@woosh/meep-engine 3.31.0 → 3.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/build/bundle-worker-terrain.js +1 -1
  2. package/package.json +1 -1
  3. package/src/engine/graphics3/DebugDrawSystem.d.ts.map +1 -1
  4. package/src/engine/graphics3/DebugDrawSystem.js +6 -2
  5. package/src/engine/graphics3/PathDisplaySystem.d.ts.map +1 -1
  6. package/src/engine/graphics3/PathDisplaySystem.js +311 -307
  7. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  8. package/src/shade/RENDERER_CONTRACT.md +15 -2
  9. package/src/shade/device/IMMEDIATE_DATA_PLAN_2026_08_28.md +5 -0
  10. package/src/shade/device/ShadeGPUCommandContext.d.ts +15 -2
  11. package/src/shade/device/ShadeGPUCommandContext.d.ts.map +1 -1
  12. package/src/shade/device/ShadeGPUCommandContext.js +154 -25
  13. package/src/shade/renderer/GraphicsContext.d.ts +9 -1
  14. package/src/shade/renderer/GraphicsContext.d.ts.map +1 -1
  15. package/src/shade/renderer/GraphicsContext.js +54 -15
  16. package/src/shade/renderer/Renderer.d.ts +19 -4
  17. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  18. package/src/shade/renderer/Renderer.js +113 -2
  19. package/src/shade/renderer/animation/GPUAnimationManager.d.ts +10 -7
  20. package/src/shade/renderer/animation/GPUAnimationManager.d.ts.map +1 -1
  21. package/src/shade/renderer/animation/GPUAnimationManager.js +147 -45
  22. package/src/shade/renderer/buffer/GPUBufferStagingAllocator.d.ts +11 -2
  23. package/src/shade/renderer/buffer/GPUBufferStagingAllocator.d.ts.map +1 -1
  24. package/src/shade/renderer/buffer/GPUBufferStagingAllocator.js +95 -13
  25. package/src/shade/renderer/buffer/GPUBufferUploadAllocator.d.ts +60 -0
  26. package/src/shade/renderer/buffer/GPUBufferUploadAllocator.d.ts.map +1 -0
  27. package/src/shade/renderer/buffer/GPUBufferUploadAllocator.js +202 -0
  28. package/src/shade/renderer/buffer/GPUUploadBlock.d.ts +68 -0
  29. package/src/shade/renderer/buffer/GPUUploadBlock.d.ts.map +1 -0
  30. package/src/shade/renderer/buffer/GPUUploadBlock.js +97 -0
  31. package/src/shade/renderer/dynamic/GPUDynamicMeshRenderer.d.ts +20 -7
  32. package/src/shade/renderer/dynamic/GPUDynamicMeshRenderer.d.ts.map +1 -1
  33. package/src/shade/renderer/dynamic/GPUDynamicMeshRenderer.js +20 -7
  34. package/src/shade/renderer/geometry/meshlet/build/meshoptimizer/geometry_to_meshlets_meshoptimizer.d.ts +5 -0
  35. package/src/shade/renderer/geometry/meshlet/build/meshoptimizer/geometry_to_meshlets_meshoptimizer.d.ts.map +1 -1
  36. package/src/shade/renderer/geometry/meshlet/build/meshoptimizer/geometry_to_meshlets_meshoptimizer.js +19 -0
  37. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  38. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  39. package/src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.d.ts +3 -1
  40. package/src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.d.ts.map +1 -1
  41. package/src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.js +3 -1
  42. package/src/shade/renderer/rasterize/expand/bucket/mesh/shader_instances_rasterization_bucket_sort.d.ts.map +1 -1
  43. package/src/shade/renderer/rasterize/expand/bucket/mesh/shader_instances_rasterization_bucket_sort.js +134 -88
  44. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_rasterization_bucket_sort.d.ts.map +1 -1
  45. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_rasterization_bucket_sort.js +57 -26
  46. package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.d.ts.map +1 -1
  47. package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.js +28 -20
  48. package/src/shade/renderer/rasterize/graph_clear_raster_targets.d.ts +38 -0
  49. package/src/shade/renderer/rasterize/graph_clear_raster_targets.d.ts.map +1 -0
  50. package/src/shade/renderer/rasterize/graph_clear_raster_targets.js +105 -0
  51. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.d.ts +1 -1
  52. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.d.ts.map +1 -1
  53. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.js +21 -1
  54. package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.d.ts.map +1 -1
  55. package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.js +12 -20
  56. package/src/shade/renderer/shader/graph/graph_import_typed_buffer.d.ts +2 -1
  57. package/src/shade/renderer/shader/graph/graph_import_typed_buffer.d.ts.map +1 -1
  58. package/src/shade/renderer/shader/graph/graph_import_typed_buffer.js +15 -28
  59. package/src/core/geom/3d/atlas/atlas_bench_lib.d.ts +0 -73
  60. package/src/core/geom/3d/atlas/atlas_bench_lib.d.ts.map +0 -1
  61. package/src/engine/graphics3/terrain/GPUTerrainSplatRenderer.d.ts +0 -59
  62. package/src/engine/graphics3/terrain/GPUTerrainSplatRenderer.d.ts.map +0 -1
  63. package/src/engine/graphics3/terrain/pack_terrain_row_table.d.ts +0 -22
  64. package/src/engine/graphics3/terrain/pack_terrain_row_table.d.ts.map +0 -1
  65. package/src/engine/graphics3/terrain/shader_terrain_splat.d.ts +0 -26
  66. package/src/engine/graphics3/terrain/shader_terrain_splat.d.ts.map +0 -1
  67. package/src/shade/renderer/animation/ANIMATION_BOUND_TRACK_GROUP_STRUCT.d.ts +0 -25
  68. package/src/shade/renderer/animation/ANIMATION_BOUND_TRACK_GROUP_STRUCT.d.ts.map +0 -1
  69. package/src/shade/renderer/animation/ANIMATION_KEYFRAME_BLOCK_STRUCT.d.ts +0 -26
  70. package/src/shade/renderer/animation/ANIMATION_KEYFRAME_BLOCK_STRUCT.d.ts.map +0 -1
  71. package/src/shade/renderer/animation/ANIMATION_SKIN_JOINT_BLOCK_STRUCT.d.ts +0 -29
  72. package/src/shade/renderer/animation/ANIMATION_SKIN_JOINT_BLOCK_STRUCT.d.ts.map +0 -1
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Clear a rasterization's targets to what its first draw would have cleared them to, for a
3
+ * rasterization that has no first draw.
4
+ *
5
+ * A rasterization clears its targets through the `loadOp` of its first draw, which is its first opaque
6
+ * bucket's; everything after that loads. A scene with no opaque bucket has no such draw, and a scene
7
+ * with nothing in it has no draw at all — and no target starts a frame empty, so the frame then reads
8
+ * whatever it last held. The renderer's own targets outlive the frame: a mesh taken out of the scene
9
+ * stays in them, and so does the depth an extension wrote over the scene after the rasterizer was done.
10
+ * A graph transient comes out of a pool holding whatever its last user left, which is what a point
11
+ * light's cube faces are. This takes that draw's place.
12
+ *
13
+ * The clear values are the pass descriptor's own, so what the targets hold afterwards is exactly what
14
+ * the draw's clear would have left. A viewport short of the whole target fills that rectangle alone —
15
+ * shadow maps share an atlas, and a clear load op would erase every other map in it.
16
+ *
17
+ * @param {object} options
18
+ * @param {FrameGraph} options.graph
19
+ * @param {number} options.depth depth texture resource ID
20
+ * @param {number[]} options.color_targets colour texture resource IDs, in the order of the pass
21
+ * descriptor's colour attachments
22
+ * @param {RenderPassDescriptor} options.pass_descriptor the pass whose first draw this stands in for;
23
+ * the clear values are read off it
24
+ * @param {number[]} [options.viewport] [x, y, width, height]; a width or height of -1 runs to the edge of
25
+ * the target, and [0, 0, -1, -1] is the whole target
26
+ * @returns {{depth: number, color_targets: number[]}} the depth and colour handles, cleared
27
+ */
28
+ export function graph_clear_raster_targets({ graph, depth, color_targets, pass_descriptor, viewport, }: {
29
+ graph: FrameGraph;
30
+ depth: number;
31
+ color_targets: number[];
32
+ pass_descriptor: RenderPassDescriptor;
33
+ viewport?: number[];
34
+ }): {
35
+ depth: number;
36
+ color_targets: number[];
37
+ };
38
+ //# sourceMappingURL=graph_clear_raster_targets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph_clear_raster_targets.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/rasterize/graph_clear_raster_targets.js"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH;IAV+B,KAAK;IACT,KAAK,EAArB,MAAM;IACY,aAAa,EAA/B,MAAM,EAAE;IAEsB,eAAe;IAE1B,QAAQ,GAA3B,MAAM,EAAE;IAEN;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,EAAE,CAAA;CAAC,CA4EpD"}
@@ -0,0 +1,105 @@
1
+ import { assert } from "../../../core/assert.js";
2
+ import { texture_fill_rectangle } from "../texture/util/texture_fill_rectangle.js";
3
+
4
+ /**
5
+ * Clear a rasterization's targets to what its first draw would have cleared them to, for a
6
+ * rasterization that has no first draw.
7
+ *
8
+ * A rasterization clears its targets through the `loadOp` of its first draw, which is its first opaque
9
+ * bucket's; everything after that loads. A scene with no opaque bucket has no such draw, and a scene
10
+ * with nothing in it has no draw at all — and no target starts a frame empty, so the frame then reads
11
+ * whatever it last held. The renderer's own targets outlive the frame: a mesh taken out of the scene
12
+ * stays in them, and so does the depth an extension wrote over the scene after the rasterizer was done.
13
+ * A graph transient comes out of a pool holding whatever its last user left, which is what a point
14
+ * light's cube faces are. This takes that draw's place.
15
+ *
16
+ * The clear values are the pass descriptor's own, so what the targets hold afterwards is exactly what
17
+ * the draw's clear would have left. A viewport short of the whole target fills that rectangle alone —
18
+ * shadow maps share an atlas, and a clear load op would erase every other map in it.
19
+ *
20
+ * @param {object} options
21
+ * @param {FrameGraph} options.graph
22
+ * @param {number} options.depth depth texture resource ID
23
+ * @param {number[]} options.color_targets colour texture resource IDs, in the order of the pass
24
+ * descriptor's colour attachments
25
+ * @param {RenderPassDescriptor} options.pass_descriptor the pass whose first draw this stands in for;
26
+ * the clear values are read off it
27
+ * @param {number[]} [options.viewport] [x, y, width, height]; a width or height of -1 runs to the edge of
28
+ * the target, and [0, 0, -1, -1] is the whole target
29
+ * @returns {{depth: number, color_targets: number[]}} the depth and colour handles, cleared
30
+ */
31
+ export function graph_clear_raster_targets({
32
+ graph,
33
+ depth,
34
+ color_targets,
35
+ pass_descriptor,
36
+ viewport = [0, 0, -1, -1],
37
+ }) {
38
+ assert.defined(graph, 'graph');
39
+ assert.isNonNegativeInteger(depth, 'depth');
40
+ assert.isArray(color_targets, 'color_targets');
41
+ assert.equal(pass_descriptor.isRenderPassDescriptor, true, "pass_descriptor must be a RenderPassDescriptor");
42
+ assert.equal(color_targets.length, pass_descriptor.colorAttachments.length, 'one colour target per colour attachment of the pass');
43
+
44
+ /**
45
+ * @param data
46
+ * @param {FramePassResources} resources
47
+ * @param {GraphicsExecutionContext} context
48
+ */
49
+ function execute(data, resources, context) {
50
+ const encoder = context.encoder;
51
+
52
+ /**
53
+ * @type {GPUTextureContext}
54
+ */
55
+ const depth_texture = resources.get(data.depth);
56
+
57
+ /**
58
+ * @type {GPUTextureContext[]}
59
+ */
60
+ const color_textures = data.color_targets.map(id => resources.get(id));
61
+
62
+ const x = viewport[0];
63
+ const y = viewport[1];
64
+ const width = viewport[2] >= 0 ? viewport[2] : depth_texture.width - x;
65
+ const height = viewport[3] >= 0 ? viewport[3] : depth_texture.height - y;
66
+
67
+ const is_whole_target = x === 0 && y === 0 && width === depth_texture.width && height === depth_texture.height;
68
+
69
+ if (is_whole_target) {
70
+ const gpu_descriptor = pass_descriptor.toGPU({
71
+ colorTargets: color_textures,
72
+ depthTarget: depth_texture,
73
+ loadOp: "clear",
74
+ storeOp: "store",
75
+ });
76
+
77
+ // not the draw's pipeline label: nothing is drawn
78
+ gpu_descriptor.label = "Clear rasterization targets";
79
+
80
+ encoder.beginRenderPass(gpu_descriptor).end();
81
+
82
+ return;
83
+ }
84
+
85
+ const rectangle = [x, y, width, height];
86
+
87
+ for (let i = 0; i < color_textures.length; i++) {
88
+ texture_fill_rectangle(encoder, color_textures[i], rectangle, pass_descriptor.colorAttachments[i].clearValue);
89
+ }
90
+
91
+ texture_fill_rectangle(encoder, depth_texture, rectangle, [pass_descriptor.depthStencilAttachment.depthClearValue]);
92
+ }
93
+
94
+ const data = {};
95
+
96
+ const pass = graph.add("clear raster targets", data, execute);
97
+
98
+ data.depth = pass.write(depth);
99
+ data.color_targets = color_targets.map(pass.write, pass);
100
+
101
+ return {
102
+ depth: data.depth,
103
+ color_targets: data.color_targets,
104
+ };
105
+ }
@@ -12,7 +12,7 @@
12
12
  * @param {Set<number>} active_buckets IDs of buckets that have any materials that are active.
13
13
  * @param {number[]} [viewport] optional viewport definition, if not provided, will use the entire render target. Useful for drawing multiple views into the same render target.
14
14
  * @param {Readonly<Record<number, GPUCullMode>>} [cull_policy] side→cullMode mapping; defaults to {@link CullPolicy.Standard}
15
- * @returns {{instances_maybe:number, meshlets_maybe:number, depth:number, color_targets:number[]}} the "maybe visible" sets, and the depth and colour handles produced by the rasterization
15
+ * @returns {{instances_maybe:number, meshlets_maybe:number, depth:number, color_targets:number[]}} the "maybe visible" sets, and the depth and colour handles produced by the rasterization — cleared whether or not any bucket drew
16
16
  */
17
17
  export function graph_rasterize_partial_opaque({ graph, buckets, view, render_pass_descriptor, limits, depth, raster_color_targets, hzb, viewport, active_buckets, cull_policy, }: FrameGraph): {
18
18
  instances_maybe: number;
@@ -1 +1 @@
1
- {"version":3,"file":"graph_rasterize_partial_opaque.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.js"],"names":[],"mappings":"AAwBA;;;;;;;;;;;;;;;GAeG;AACH,gMAFa;IAAC,eAAe,EAAC,MAAM,CAAC;IAAC,cAAc,EAAC,MAAM,CAAC;IAAC,KAAK,EAAC,MAAM,CAAC;IAAC,aAAa,EAAC,MAAM,EAAE,CAAA;CAAC,CAgMjG"}
1
+ {"version":3,"file":"graph_rasterize_partial_opaque.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.js"],"names":[],"mappings":"AAyBA;;;;;;;;;;;;;;;GAeG;AACH,gMAFa;IAAC,eAAe,EAAC,MAAM,CAAC;IAAC,cAAc,EAAC,MAAM,CAAC;IAAC,KAAK,EAAC,MAAM,CAAC;IAAC,aAAa,EAAC,MAAM,EAAE,CAAA;CAAC,CAmNjG"}
@@ -21,6 +21,7 @@ import { MeshletFilter2WayPassData, pass_filter_meshlets_2way } from "../expand/
21
21
  import { RasterizationCollectionType } from "../expand/RasterizationCollectionType.js";
22
22
  import { construct_primitive_state, CullPolicy } from "../bucket/construct_primitive_state.js";
23
23
  import { rasterization_opaque_buckets } from "../bucket/rasterization_opaque_buckets.js";
24
+ import { graph_clear_raster_targets } from "../graph_clear_raster_targets.js";
24
25
 
25
26
  /**
26
27
  *
@@ -36,7 +37,7 @@ import { rasterization_opaque_buckets } from "../bucket/rasterization_opaque_buc
36
37
  * @param {Set<number>} active_buckets IDs of buckets that have any materials that are active.
37
38
  * @param {number[]} [viewport] optional viewport definition, if not provided, will use the entire render target. Useful for drawing multiple views into the same render target.
38
39
  * @param {Readonly<Record<number, GPUCullMode>>} [cull_policy] side→cullMode mapping; defaults to {@link CullPolicy.Standard}
39
- * @returns {{instances_maybe:number, meshlets_maybe:number, depth:number, color_targets:number[]}} the "maybe visible" sets, and the depth and colour handles produced by the rasterization
40
+ * @returns {{instances_maybe:number, meshlets_maybe:number, depth:number, color_targets:number[]}} the "maybe visible" sets, and the depth and colour handles produced by the rasterization — cleared whether or not any bucket drew
40
41
  */
41
42
  export function graph_rasterize_partial_opaque({
42
43
  graph,
@@ -219,6 +220,25 @@ export function graph_rasterize_partial_opaque({
219
220
  is_first_draw = false;
220
221
  }
221
222
 
223
+ if (is_first_draw) {
224
+ /*
225
+ No opaque bucket, so no draw above cleared the targets, and they still hold what they held the
226
+ last time this view drew into them — they are the renderer's own and outlive the frame. The
227
+ HZB is built from this depth next and everything after this loads, so the clear goes here, in
228
+ the first draw's place, before anything reads.
229
+ */
230
+ const cleared = graph_clear_raster_targets({
231
+ graph,
232
+ depth: viz_depth,
233
+ color_targets: raster_color_targets,
234
+ pass_descriptor: render_pass_descriptor,
235
+ viewport,
236
+ });
237
+
238
+ viz_depth = cleared.depth;
239
+ raster_color_targets = cleared.color_targets;
240
+ }
241
+
222
242
  // at this point we have rendered all occluders, and the uncertain cases are all stored in the "maybe" set
223
243
 
224
244
  // will be appended to in further passes
@@ -1 +1 @@
1
- {"version":3,"file":"graph_rasterize_scene.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/rasterize/standard/graph_rasterize_scene.js"],"names":[],"mappings":"AAeA;;;;;;;;;;;GAWG;AACH;IAN+B,KAAK;IACC,GAAG;IACA,MAAM;IACjC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,EAAE,CAAA;CAAC,CA0NrE"}
1
+ {"version":3,"file":"graph_rasterize_scene.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/rasterize/standard/graph_rasterize_scene.js"],"names":[],"mappings":"AAcA;;;;;;;;;;;GAWG;AACH;IAN+B,KAAK;IACC,GAAG;IACA,MAAM;IACjC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,EAAE,CAAA;CAAC,CAmNrE"}
@@ -11,7 +11,6 @@ import { RasterizationCollectionType } from "../expand/RasterizationCollectionTy
11
11
  import { graph_rasterize_meshes_alpha_tested } from "../native/graph_rasterize_meshes_alpha_tested.js";
12
12
  import { graph_rasterize_partial_opaque } from "./graph_rasterize_partial_opaque.js";
13
13
  import { graph_rasterize_remaining_opaque } from "./graph_rasterize_remaining_opaque.js";
14
- import { rasterization_has_opaque_bucket } from "../bucket/rasterization_has_opaque_bucket.js";
15
14
 
16
15
  /**
17
16
  * TODO:
@@ -156,27 +155,22 @@ export function graph_rasterize_scene({
156
155
 
157
156
 
158
157
  if (job.alpha_tested_pass !== undefined) {
159
- /**
160
- * The opaque path's first draw is the only thing that clears the visibility-buffer colour
161
- * attachments and the depth attachment — the clear values live on the opaque pass descriptor
162
- * and reach the targets through its `loadOp`, and every pass after it is recorded with
163
- * `load`. When the scene has no opaque bucket that draw never happens, so the first pass that
164
- * does run owns the clear. The alpha-tested descriptor carries the same clear values
165
- * (`[0,0,0,0]`, VIZ_BUFFER_INVALID_MESH_VALUE, DEPTH_BACKGROUND_VALUE), so handing it over
166
- * changes nothing about what the targets are cleared to.
167
- *
168
- * Wherever an opaque bucket exists — nearly every scene — this starts true and every call
169
- * below asks for `false`, exactly as before.
170
- */
171
- let targets_cleared = rasterization_has_opaque_bucket(active_buckets);
172
-
173
-
158
+ /*
159
+ Both alpha-tested passes load, never clear: the targets were cleared before the HZB above was
160
+ built from them — by the first opaque draw, or in a scene with no opaque bucket by the clear
161
+ `graph_rasterize_partial_opaque` records in that draw's place.
162
+
163
+ The clear used to be handed to the first alpha-tested draw instead. That left the HZB built
164
+ from an uncleared depth whenever there was no opaque bucket, and when there was no
165
+ alpha-tested draw either — a scene with nothing in it, or only transparent surfaces — nothing
166
+ cleared at all, and the frame inherited the targets as they were left.
167
+ */
174
168
  const alpha_tested_front = graph_rasterize_meshes_alpha_tested({
175
169
  graph,
176
170
  buckets: gr_render_buckets,
177
171
  view_ctx,
178
172
  render_pass_descriptor: job.alpha_tested_pass,
179
- clear_target: !targets_cleared,
173
+ clear_target: false,
180
174
  side: ShadeDrawSide.Front,
181
175
  cull_policy: job.cull_policy,
182
176
  limits,
@@ -189,14 +183,13 @@ export function graph_rasterize_scene({
189
183
 
190
184
  gr_texture_depth = alpha_tested_front.depth;
191
185
  raster_color_targets = alpha_tested_front.color_targets;
192
- targets_cleared = targets_cleared || alpha_tested_front.executed;
193
186
 
194
187
  const alpha_tested_double = graph_rasterize_meshes_alpha_tested({
195
188
  graph,
196
189
  buckets: gr_render_buckets,
197
190
  view_ctx,
198
191
  render_pass_descriptor: job.alpha_tested_pass,
199
- clear_target: !targets_cleared,
192
+ clear_target: false,
200
193
  side: ShadeDrawSide.Double,
201
194
  cull_policy: job.cull_policy,
202
195
  limits,
@@ -209,7 +202,6 @@ export function graph_rasterize_scene({
209
202
 
210
203
  gr_texture_depth = alpha_tested_double.depth;
211
204
  raster_color_targets = alpha_tested_double.color_targets;
212
- targets_cleared = targets_cleared || alpha_tested_double.executed;
213
205
 
214
206
  if (alpha_tested_front.executed || alpha_tested_double.executed) {
215
207
 
@@ -3,7 +3,8 @@
3
3
  * @param {Object} options
4
4
  * @param {FrameGraph} options.graph
5
5
  * @param {WebGPUType} options.type type of the buffer
6
- * @param {*} options.value actual value to store in the imported buffer
6
+ * @param {*} options.value actual value to store in the imported buffer. Give every field: one left
7
+ * out is skipped, not zeroed, so it holds whatever bytes the upload memory held before
7
8
  * @param {string} [options.label] optional name for the new resource, if not specified will assume name of the type
8
9
  * @param {GPUBufferUsage|number} options.usage
9
10
  * @returns {number}
@@ -1 +1 @@
1
- {"version":3,"file":"graph_import_typed_buffer.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/shader/graph/graph_import_typed_buffer.js"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH;IArB+B,KAAK;IACL,IAAI;IACb,KAAK;IACC,KAAK,GAAtB,MAAM;IACyB,KAAK,EAApC,iBAAe,MAAM;IACnB,MAAM,CAkFlB"}
1
+ {"version":3,"file":"graph_import_typed_buffer.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/shader/graph/graph_import_typed_buffer.js"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH;IAtB+B,KAAK;IACL,IAAI;IACb,KAAK;IAEC,KAAK,GAAtB,MAAM;IACyB,KAAK,EAApC,iBAAe,MAAM;IACnB,MAAM,CAqElB"}
@@ -1,13 +1,13 @@
1
1
  import { assert } from "../../../../core/assert.js";
2
2
  import { BufferResourceDescriptor } from "../../../device/graph/BufferResourceDescriptor.js";
3
- import { write_gpu_typed_buffer } from "../type/serde/write_gpu_typed_buffer.js";
4
3
 
5
4
  /**
6
5
  *
7
6
  * @param {Object} options
8
7
  * @param {FrameGraph} options.graph
9
8
  * @param {WebGPUType} options.type type of the buffer
10
- * @param {*} options.value actual value to store in the imported buffer
9
+ * @param {*} options.value actual value to store in the imported buffer. Give every field: one left
10
+ * out is skipped, not zeroed, so it holds whatever bytes the upload memory held before
11
11
  * @param {string} [options.label] optional name for the new resource, if not specified will assume name of the type
12
12
  * @param {GPUBufferUsage|number} options.usage
13
13
  * @returns {number}
@@ -52,32 +52,19 @@ export function graph_import_typed_buffer({
52
52
  function execute(data, resources, context) {
53
53
  const gpu_buffer = resources.get(data.buffer);
54
54
 
55
- const encoder = context.encoder;
56
-
57
- // TODO this is pretty inefficient, figure out a way to skip needing 2 buffers
58
- // [AG - 2025/09/13] - Using a transient buffer and a queue.writeBuffer resulted in garbage being written.
59
- // No idea why currently, creating a new buffer works for now, but the underlying issue is not resolved
60
-
61
- const _size = type.size;
62
- const buffer = encoder.device.createBuffer({
63
- label: "Import typed buffer/ temp buffer",
64
- size: _size,
65
- usage: GPUBufferUsage.COPY_SRC,
66
- mappedAtCreation: true
67
- });
68
-
69
- const mapped = buffer.getMappedRange();
70
-
71
- write_gpu_typed_buffer(value, type, mapped);
72
- buffer.unmap();
73
- // Copy is necessary to ensure a correct device timeline
74
-
75
- encoder.copyBufferToBuffer(buffer, 0, gpu_buffer, 0, _size);
76
-
77
- encoder.onFinished.addOne(() => {
78
-
79
- buffer.destroy();
80
- })
55
+ /*
56
+ Recorded in order, where this pass runs — never `queue.writeBuffer`.
57
+
58
+ The resource is a frame graph transient, and the pool hands a transient out again as soon
59
+ as its last user in the frame has run, so this buffer may have held another resource
60
+ earlier in the same submission, written by a pass recorded before this one. A queue write
61
+ lands ahead of the whole submission, that earlier pass's write executes after it, and the
62
+ reader gets the earlier pass's bytes. That is the garbage this import wrote in 2025, twice:
63
+ queue-writing the resource itself, then queue-writing a transient from the same pool and
64
+ copying from that. A mapped buffer created for every call hid it, at a create and a destroy
65
+ per import per frame.
66
+ */
67
+ context.encoder.writeValueBuffer(gpu_buffer, 0, type, value);
81
68
  }
82
69
 
83
70
  const pass = graph.add("import_typed_buffer", data, execute);
@@ -1,73 +0,0 @@
1
- /** Flat n×n plane in z=0. Single planar region => one chart, zero stretch. */
2
- export function soup_plane(n: any): any[];
3
- /** Deterministic gently-bumpy terrain (curved, smooth, no RNG). */
4
- export function soup_terrain(n: any, amplitude: any): any[];
5
- /** Dense bumpy terrain decimated by the production simplifier -> irregular, over-segmenting asset. */
6
- export function soup_decimated_terrain(n: any, amplitude: any, error: any): number[];
7
- /** UV sphere (fanned poles, no degenerate tris). Smooth closed surface. */
8
- export function soup_uv_sphere(rings: any, sectors: any): any[];
9
- /** Upper hemisphere cap (intrinsic positive curvature -> MUST distort under any flattening). */
10
- export function soup_hemisphere(rings: any, sectors: any): any[];
11
- /** Open cylinder side (no caps): a DEVELOPABLE surface — should flatten to a flat strip with ~zero stretch. */
12
- export function soup_cylinder_open(radial: any, rings: any, height: any, radius: any): any[];
13
- /** Open cone side (no base): DEVELOPABLE — flattens to a circular sector with ~zero stretch. */
14
- export function soup_cone_open(radial: any, rings: any, height: any, radius: any): any[];
15
- /** Torus: irregular, curved charts (a packing stress fixture). */
16
- export function soup_torus(rings: any, sides: any, R: any, rr: any): any[];
17
- /** Grid of unit cubes (each cube => 6 charts at 90°; a chart-count fixture). */
18
- export function soup_cube_field(count: any): any[];
19
- export function build_mesh(soup: any): BinaryTopology;
20
- /**
21
- * Per-run chart-quality metrics. `charts` are finalized charts (each with
22
- * `surface_area`, `parametric_area`, `type`, and solved `uv`).
23
- *
24
- * @param {Chart[]} charts
25
- * @returns {object} chart_count, ortho/lscm/piecewise counts, area-weighted
26
- * mean_stretch, max_stretch, p50/p90/p99 of per-chart worst stretch,
27
- * tail_count + tail_area_frac (charts above TAIL_STRETCH), degenerate count.
28
- */
29
- export function chart_metrics(charts: Chart[]): object;
30
- /** Total 3D length of edges cut between different charts (texture seams) + fraction of interior length. */
31
- export function seam_metrics(mesh: any, charts: any): {
32
- seam_len: number;
33
- seam_pct: number;
34
- interior_len: number;
35
- };
36
- /**
37
- * Run the full pipeline once and return the metric panel. Always rebuilds the
38
- * mesh from the soup so packing-only A/Bs are independent (pack mutates uv).
39
- *
40
- * @param {number[]} soup triangle soup
41
- * @param {object} [computeOpts] atlas_compute_charts options
42
- * @param {object} [packOpts] atlas_pack_charts options
43
- * @returns {object} flattened metric panel
44
- */
45
- export function measure(soup: number[], computeOpts?: object, packOpts?: object): object;
46
- /** Median wall-clock of `fn` over a time budget, with warmup. */
47
- export function time_median(fn: any, budget_ms?: number, warmup?: number): any;
48
- /**
49
- * A/B: measure baseline vs candidate on the same fixture and return both panels
50
- * plus per-key deltas. The caller asserts the expected direction (the gate).
51
- *
52
- * @param {number[]} soup
53
- * @param {{compute?:object, pack?:object}} a baseline options
54
- * @param {{compute?:object, pack?:object}} b candidate options
55
- * @returns {{base:object, cand:object, delta:object}}
56
- */
57
- export function ab(soup: number[], a: {
58
- compute?: object;
59
- pack?: object;
60
- }, b: {
61
- compute?: object;
62
- pack?: object;
63
- }): {
64
- base: object;
65
- cand: object;
66
- delta: object;
67
- };
68
- /** Compact one-line metric panel for console output. */
69
- export function fmt(m: any): string;
70
- /** Charts whose worst (L∞) per-triangle stretch exceeds this count as the "high-distortion tail". */
71
- export const TAIL_STRETCH: 2;
72
- import { BinaryTopology } from "../topology/struct/binary/BinaryTopology.js";
73
- //# sourceMappingURL=atlas_bench_lib.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"atlas_bench_lib.d.ts","sourceRoot":"","sources":["../../../../../../src/core/geom/3d/atlas/atlas_bench_lib.js"],"names":[],"mappings":"AAoCA,8EAA8E;AAC9E,0CAKC;AAED,mEAAmE;AACnE,4DAMC;AAED,sGAAsG;AACtG,qFAIC;AAED,2EAA2E;AAC3E,gEAYC;AAED,gGAAgG;AAChG,iEAYC;AAED,+GAA+G;AAC/G,6FAQC;AAED,gGAAgG;AAChG,yFAUC;AAED,kEAAkE;AAClE,2EASC;AAED,gFAAgF;AAChF,mDAWC;AAED,sDAIC;AA8BD;;;;;;;;GAQG;AACH,sCALW,OAAO,GACL,MAAM,CA0ClB;AAED,2GAA2G;AAC3G;;;;EAsBC;AAGD;;;;;;;;GAQG;AACH,8BALW,MAAM,EAAE,gBACR,MAAM,aACN,MAAM,GACJ,MAAM,CAgBlB;AAED,iEAAiE;AACjE,+EAWC;AAED;;;;;;;;GAQG;AACH,yBALW,MAAM,EAAE,KACR;IAAC,OAAO,CAAC,EAAC,MAAM,CAAC;IAAC,IAAI,CAAC,EAAC,MAAM,CAAA;CAAC,KAC/B;IAAC,OAAO,CAAC,EAAC,MAAM,CAAC;IAAC,IAAI,CAAC,EAAC,MAAM,CAAA;CAAC,GAC7B;IAAC,IAAI,EAAC,MAAM,CAAC;IAAC,IAAI,EAAC,MAAM,CAAC;IAAC,KAAK,EAAC,MAAM,CAAA;CAAC,CAUpD;AAED,wDAAwD;AACxD,oCAMC;AApJD,qGAAqG;AACrG,6BAAgC;+BAvKa,6CAA6C"}
@@ -1,59 +0,0 @@
1
- /**
2
- * Draws one terrain's splat and grid overlay into the G-buffer's albedo.
3
- *
4
- * Two passes: this frame's tables and any changed textures are uploaded, and the composite reads
5
- * the albedo the material pass wrote and writes a replacement — publishing into
6
- * {@link GBufferTextures} (D23) is what carries it downstream. One renderer per terrain, owned by the system's
7
- * entry for it, because every texture here is the terrain's own.
8
- */
9
- export class GPUTerrainSplatRenderer {
10
- /**
11
- * Record this terrain's splat into the frame and hand the replacement albedo back through the
12
- * context.
13
- *
14
- * @param {object} args
15
- * @param {FrameContext} args.frame
16
- * @param {Uint32Array} args.rows 1 where a mesh row belongs to this terrain
17
- * @param {number} args.row_count rows in use — one past the highest row marked
18
- * @param {Float32Array} args.scales two floats per layer, the legacy `materialScalesMap`
19
- * @param {number} args.layer_count
20
- * @param {Float32Array} args.world_to_terrain inverse of the terrain entity's world matrix
21
- * @param {number[]} args.inv_world_size `1 / (size × gridScale)` per axis
22
- * @param {number[]} args.grid_transform scale xy, offset zw
23
- * @param {number[]} args.grid_resolution the terrain's size in cells
24
- * @param {{data: Uint8Array, width: number, height: number, depth: number, version: number}} args.weights
25
- * @param {{data: Uint8Array, width: number, height: number, depth: number, version: number}} args.layer_image
26
- * @param {Sampler2D} args.overlay the per-cell rgba map
27
- * @param {Sampler2D} args.sprite the cell sprite, rgba
28
- */
29
- graph_draw({ frame, rows, row_count, scales, layer_count, world_to_terrain, inv_world_size, grid_transform, grid_resolution, weights, layer_image, overlay, sprite }: {
30
- frame: FrameContext;
31
- rows: Uint32Array;
32
- row_count: number;
33
- scales: Float32Array;
34
- layer_count: number;
35
- world_to_terrain: Float32Array;
36
- inv_world_size: number[];
37
- grid_transform: number[];
38
- grid_resolution: number[];
39
- weights: {
40
- data: Uint8Array;
41
- width: number;
42
- height: number;
43
- depth: number;
44
- version: number;
45
- };
46
- layer_image: {
47
- data: Uint8Array;
48
- width: number;
49
- height: number;
50
- depth: number;
51
- version: number;
52
- };
53
- overlay: Sampler2D;
54
- sprite: Sampler2D;
55
- }): void;
56
- destroy(): void;
57
- #private;
58
- }
59
- //# sourceMappingURL=GPUTerrainSplatRenderer.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"GPUTerrainSplatRenderer.d.ts","sourceRoot":"","sources":["../../../../../src/engine/graphics3/terrain/GPUTerrainSplatRenderer.js"],"names":[],"mappings":"AA6EA;;;;;;;GAOG;AACH;IAyCI;;;;;;;;;;;;;;;;;;OAkBG;IACH;QAd8B,KAAK;QACN,IAAI,EAAtB,WAAW;QACE,SAAS,EAAtB,MAAM;QACa,MAAM,EAAzB,YAAY;QACC,WAAW,EAAxB,MAAM;QACa,gBAAgB,EAAnC,YAAY;QACG,cAAc,EAA7B,MAAM,EAAE;QACO,cAAc,EAA7B,MAAM,EAAE;QACO,eAAe,EAA9B,MAAM,EAAE;QACgF,OAAO,EAA/F;YAAC,IAAI,EAAE,UAAU,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAC;QACO,WAAW,EAAnG;YAAC,IAAI,EAAE,UAAU,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAC;QACjE,OAAO;QACP,MAAM;aAgEhC;IAyND,gBAwBC;;CACJ"}
@@ -1,22 +0,0 @@
1
- /**
2
- * The lookup the splat pass reads a visibility-buffer mesh row through: one word per row, 1 for a
3
- * row that is a tile of this terrain, 0 for anything else.
4
- *
5
- * A tile is an entity, and its row is the entity's, one on — so the table is sized by the highest
6
- * tile entity and the rows in between belong to whatever else the level holds. Whether a row has
7
- * been written yet is not this table's business: a row the scene has not placed is in no pixel of
8
- * the visibility buffer, and marking it costs nothing.
9
- *
10
- * @param {object} args
11
- * @param {Map<*, number>} args.tiles the terrain's tile entities, by whatever spawned them
12
- * @param {Uint32Array} args.table the previous frame's table, reused when it is long enough
13
- * @returns {{table: Uint32Array, size: number}} the table and how much of it is meaningful
14
- */
15
- export function pack_terrain_row_table({ tiles, table }: {
16
- tiles: Map<any, number>;
17
- table: Uint32Array;
18
- }): {
19
- table: Uint32Array;
20
- size: number;
21
- };
22
- //# sourceMappingURL=pack_terrain_row_table.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"pack_terrain_row_table.d.ts","sourceRoot":"","sources":["../../../../../src/engine/graphics3/terrain/pack_terrain_row_table.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AACH;IAJgC,KAAK,EAA1B,SAAO,MAAM,CAAC;IACI,KAAK,EAAvB,WAAW;IACT;IAAC,KAAK,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAC,CAiC9C"}
@@ -1,26 +0,0 @@
1
- export const TERRAIN_SPLAT_SETTINGS: WebGPUStruct;
2
- /**
3
- * Reads the G-buffer's albedo, paints the terrain's splat mix and grid overlay into every pixel
4
- * the visibility buffer attributes to a terrain tile, and writes a new albedo.
5
- *
6
- * **This is the decal architecture keyed on a different thing (D40).** A pass after the opaque
7
- * geometry on the in/out G-buffer D23 opened — but where a decal asks a froxel walk whether a
8
- * world position is inside an oriented box, this pass asks the visibility buffer which mesh the
9
- * pixel came from. The classification is exact and costs one load: no second geometry pass, no
10
- * depth-equal redraw, no bounds test a unit standing on the ground would fail.
11
- *
12
- * **Only the albedo is replaced.** The legacy splat material forced roughness 1 and metalness 0,
13
- * which is what the tile's own material already writes into the PBR target during the material
14
- * pass; the normals are the tile geometry's own. What the splat changes is colour, so colour is
15
- * the one target this pass touches — the other three handles pass through untouched.
16
- *
17
- * **Linear space throughout, by format rather than by arithmetic** (§U3): the layer array and both
18
- * overlay textures are uploaded sRGB, so their samplers decode before filtering, the mix and the
19
- * grid tint multiply happen on linear values, and nothing here decodes or encodes anything.
20
- *
21
- * @type {ImageShader}
22
- */
23
- export const shader_terrain_splat: ImageShader;
24
- import { WebGPUStruct } from "../../../shade/renderer/shader/type/WebGPUStruct.js";
25
- import { ImageShader } from "../../../shade/renderer/shader/ImageShader.js";
26
- //# sourceMappingURL=shader_terrain_splat.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"shader_terrain_splat.d.ts","sourceRoot":"","sources":["../../../../../src/engine/graphics3/terrain/shader_terrain_splat.js"],"names":[],"mappings":"AAuBA,kDA+BG;AAkIH;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,mCAFU,WAAW,CAWlB;6BAnM0B,qDAAqD;4BAPtD,+CAA+C"}
@@ -1,25 +0,0 @@
1
- /**
2
- * Number of bound tracks inlined into a single group node. Picked so a
3
- * node fits comfortably in a cache line or two — three u32s per binding
4
- * times eight, plus the count + next + clip header, ~104 bytes total.
5
- */
6
- export const TRACKS_PER_GROUP_NODE: 8;
7
- /**
8
- * Sentinel for the `next` pointer of a tail group node and for clips with
9
- * no bindings.
10
- */
11
- export const ANIMATION_BOUND_TRACK_GROUP_NULL: 4294967295;
12
- /**
13
- * One node in a bound clip's linked list of bound tracks. Each clip points
14
- * at a head node; nodes chain forward via {@link next} until a sentinel
15
- * (`0xFFFFFFFF`). Slots `[0, count)` of `bindings` are populated; tail
16
- * slots are zeroed and ignored.
17
- *
18
- * The dispatch model is one thread per group node, not one per binding —
19
- * each thread iterates its node's `count` bindings and applies them under
20
- * the back-referenced clip's time. The `clip` back-pointer makes that
21
- * dispatch possible without walking the chain from the clip end.
22
- */
23
- export const ANIMATION_BOUND_TRACK_GROUP_STRUCT: WebGPUStruct;
24
- import { WebGPUStruct } from "../shader/type/WebGPUStruct.js";
25
- //# sourceMappingURL=ANIMATION_BOUND_TRACK_GROUP_STRUCT.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ANIMATION_BOUND_TRACK_GROUP_STRUCT.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/animation/ANIMATION_BOUND_TRACK_GROUP_STRUCT.js"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,sCAAuC;AAEvC;;;GAGG;AACH,0DAA2D;AAE3D;;;;;;;;;;GAUG;AACH,8DA2B+B;6BAtDF,gCAAgC"}
@@ -1,26 +0,0 @@
1
- /**
2
- * Number of keyframes inlined into a single block. Eight 16-byte
3
- * keyframes plus the count + next header keeps a node at 136 bytes and
4
- * lets the curve evaluator amortize the pointer-chase across multiple
5
- * samples.
6
- */
7
- export const KEYFRAMES_PER_BLOCK: 8;
8
- /**
9
- * Sentinel for the `next` pointer of a tail keyframe block.
10
- */
11
- export const ANIMATION_KEYFRAME_BLOCK_NULL: 4294967295;
12
- /**
13
- * One node in a curve's linked list of keyframes. Curves point at a head
14
- * block; blocks chain forward via {@link next} until a sentinel
15
- * (`0xFFFFFFFF`). Slots `[0, count)` of `keyframes` are populated in
16
- * ascending time order; tail slots are zeroed and ignored.
17
- *
18
- * Replaces the previous "all keyframes for one curve are a contiguous run
19
- * in `animation_keyframes`" arrangement: that constraint forced an
20
- * append-only monotonic counter and made it impossible to grow a curve
21
- * after creation. With block chaining each curve allocates blocks
22
- * independently of every other curve.
23
- */
24
- export const ANIMATION_KEYFRAME_BLOCK_STRUCT: WebGPUStruct;
25
- import { WebGPUStruct } from "../shader/type/WebGPUStruct.js";
26
- //# sourceMappingURL=ANIMATION_KEYFRAME_BLOCK_STRUCT.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ANIMATION_KEYFRAME_BLOCK_STRUCT.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/animation/ANIMATION_KEYFRAME_BLOCK_STRUCT.js"],"names":[],"mappings":"AAKA;;;;;GAKG;AACH,oCAAqC;AAErC;;GAEG;AACH,uDAAwD;AAExD;;;;;;;;;;;GAWG;AACH,2DAmB6B;6BA/CA,gCAAgC"}