@woosh/meep-engine 3.29.0 → 3.30.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.
- package/build/bundle-worker-image-decoder.js +1 -1
- package/build/bundle-worker-terrain.js +1 -1
- package/package.json +1 -1
- package/src/core/binary/BinaryBuffer.d.ts.map +1 -1
- package/src/core/binary/BinaryBuffer.js +14 -2
- package/src/core/bvh2/bvh3/BVH.d.ts.map +1 -1
- package/src/core/bvh2/bvh3/BVH.js +14 -1
- package/src/core/cache/FrequencySketch.d.ts +3 -1
- package/src/core/cache/FrequencySketch.d.ts.map +1 -1
- package/src/core/cache/FrequencySketch.js +10 -4
- package/src/core/cache/wtinylfu/CacheWTinylfu.d.ts.map +1 -1
- package/src/core/cache/wtinylfu/CacheWTinylfu.js +24 -2
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadrics.d.ts +20 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadrics.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadrics.js +69 -4
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.d.ts +48 -18
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.js +262 -70
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_fold_limit.d.ts +25 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_fold_limit.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_fold_limit.js +228 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_would_fold.d.ts +13 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_would_fold.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_would_fold.js +10 -128
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_build_face_bvh.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_build_face_bvh.js +11 -1
- package/src/core/graph/mn_graph_collapse_weighted_edge.js +6 -6
- package/src/core/math/lookup/ParameterLookupTable.d.ts.map +1 -1
- package/src/core/math/lookup/ParameterLookupTable.js +10 -5
- package/src/core/math/noise/sdnoise.d.ts.map +1 -1
- package/src/core/math/noise/sdnoise.js +16 -10
- package/src/core/model/object/ImmutableObjectPool.d.ts.map +1 -1
- package/src/core/model/object/ImmutableObjectPool.js +17 -7
- package/src/core/process/executor/ConcurrentExecutor.d.ts +1 -5
- package/src/core/process/executor/ConcurrentExecutor.d.ts.map +1 -1
- package/src/core/process/executor/ConcurrentExecutor.js +95 -1
- package/src/core/process/task/task_find_dependency_cycle.d.ts +17 -0
- package/src/core/process/task/task_find_dependency_cycle.d.ts.map +1 -0
- package/src/core/process/task/task_find_dependency_cycle.js +81 -0
- package/src/core/wfc/WFCCellChoice.d.ts +17 -0
- package/src/core/wfc/WFCCellChoice.d.ts.map +1 -0
- package/src/core/wfc/WFCCellChoice.js +36 -0
- package/src/core/wfc/WFCLattice.d.ts +105 -0
- package/src/core/wfc/WFCLattice.d.ts.map +1 -0
- package/src/core/wfc/WFCLattice.js +235 -0
- package/src/core/wfc/WFCRuleTable.d.ts +172 -0
- package/src/core/wfc/WFCRuleTable.d.ts.map +1 -0
- package/src/core/wfc/WFCRuleTable.js +334 -0
- package/src/core/wfc/WFCState.d.ts +273 -0
- package/src/core/wfc/WFCState.d.ts.map +1 -0
- package/src/core/wfc/WFCState.js +552 -0
- package/src/core/wfc/WFCStatus.d.ts +11 -0
- package/src/core/wfc/WFCStatus.d.ts.map +1 -0
- package/src/core/wfc/WFCStatus.js +28 -0
- package/src/core/wfc/WFCTileWeights.d.ts +48 -0
- package/src/core/wfc/WFCTileWeights.d.ts.map +1 -0
- package/src/core/wfc/WFCTileWeights.js +86 -0
- package/src/core/wfc/WFC_UNRESOLVED.d.ts +12 -0
- package/src/core/wfc/WFC_UNRESOLVED.d.ts.map +1 -0
- package/src/core/wfc/WFC_UNRESOLVED.js +11 -0
- package/src/core/wfc/wfc_estimate_bytes.d.ts +24 -0
- package/src/core/wfc/wfc_estimate_bytes.d.ts.map +1 -0
- package/src/core/wfc/wfc_estimate_bytes.js +47 -0
- package/src/core/wfc/wfc_rule_table_tile_components.d.ts +27 -0
- package/src/core/wfc/wfc_rule_table_tile_components.d.ts.map +1 -0
- package/src/core/wfc/wfc_rule_table_tile_components.js +80 -0
- package/src/core/wfc/wfc_solve.d.ts +21 -0
- package/src/core/wfc/wfc_solve.d.ts.map +1 -0
- package/src/core/wfc/wfc_solve.js +154 -0
- package/src/core/wfc/wfc_state_apply_boundary.d.ts +18 -0
- package/src/core/wfc/wfc_state_apply_boundary.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_apply_boundary.js +61 -0
- package/src/core/wfc/wfc_state_assign.d.ts +11 -0
- package/src/core/wfc/wfc_state_assign.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_assign.js +36 -0
- package/src/core/wfc/wfc_state_ban_bits.d.ts +17 -0
- package/src/core/wfc/wfc_state_ban_bits.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_ban_bits.js +81 -0
- package/src/core/wfc/wfc_state_collapse.d.ts +12 -0
- package/src/core/wfc/wfc_state_collapse.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_collapse.js +73 -0
- package/src/core/wfc/wfc_state_flush_dirty.d.ts +12 -0
- package/src/core/wfc/wfc_state_flush_dirty.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_flush_dirty.js +39 -0
- package/src/core/wfc/wfc_state_propagate.d.ts +14 -0
- package/src/core/wfc/wfc_state_propagate.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_propagate.js +155 -0
- package/src/core/wfc/wfc_state_read.d.ts +11 -0
- package/src/core/wfc/wfc_state_read.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_read.js +38 -0
- package/src/core/wfc/wfc_state_restrict.d.ts +18 -0
- package/src/core/wfc/wfc_state_restrict.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_restrict.js +39 -0
- package/src/core/wfc/wfc_state_set_eligible.d.ts +24 -0
- package/src/core/wfc/wfc_state_set_eligible.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_set_eligible.js +49 -0
- package/src/core/wfc/wfc_state_undo_to.d.ts +13 -0
- package/src/core/wfc/wfc_state_undo_to.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_undo_to.js +84 -0
- package/src/core/wfc/wfc_words_per_cell.d.ts +11 -0
- package/src/core/wfc/wfc_words_per_cell.d.ts.map +1 -0
- package/src/core/wfc/wfc_words_per_cell.js +16 -0
- package/src/engine/ecs/EntityComponentDataset.d.ts +8 -0
- package/src/engine/ecs/EntityComponentDataset.d.ts.map +1 -1
- package/src/engine/ecs/EntityComponentDataset.js +37 -14
- package/src/engine/ecs/fow/FogOfWarRevealerSystem.d.ts +8 -1
- package/src/engine/ecs/fow/FogOfWarRevealerSystem.d.ts.map +1 -1
- package/src/engine/ecs/fow/FogOfWarRevealerSystem.js +37 -2
- package/src/engine/ecs/guid/UUID.d.ts.map +1 -1
- package/src/engine/ecs/guid/UUID.js +45 -17
- package/src/engine/ecs/ik/OneBoneSurfaceAlignmentSolver.d.ts.map +1 -1
- package/src/engine/ecs/ik/OneBoneSurfaceAlignmentSolver.js +3 -1
- package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.d.ts.map +1 -1
- package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.js +366 -360
- package/src/engine/graphics/texture/sampler/resize/sampler2d_scale_down_linear.d.ts.map +1 -1
- package/src/engine/graphics/texture/sampler/resize/sampler2d_scale_down_linear.js +24 -10
- package/src/engine/grid/grid2transform/GridPosition2TransformSystem.d.ts.map +1 -1
- package/src/engine/grid/grid2transform/GridPosition2TransformSystem.js +24 -5
- package/src/engine/interpolation/Interpoland.d.ts +7 -1
- package/src/engine/interpolation/Interpoland.d.ts.map +1 -1
- package/src/engine/interpolation/Interpoland.js +8 -1
- package/src/engine/interpolation/InterpolationSystem.d.ts.map +1 -1
- package/src/engine/interpolation/InterpolationSystem.js +3 -0
- package/src/engine/interpolation/TransformPoseSerializationAdapter.d.ts +5 -3
- package/src/engine/interpolation/TransformPoseSerializationAdapter.d.ts.map +1 -1
- package/src/engine/interpolation/TransformPoseSerializationAdapter.js +14 -3
- package/src/engine/interpolation/pose_interpoland.d.ts.map +1 -1
- package/src/engine/interpolation/pose_interpoland.js +2 -0
- package/src/engine/network/orchestrator/NetworkPeer.d.ts.map +1 -1
- package/src/engine/network/orchestrator/NetworkPeer.js +15 -10
- package/src/engine/physics/cloth/ecs/ClothWorld.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/ClothWorld.js +17 -0
- package/src/engine/physics/mls-mpm/MLS_MPM.d.ts.map +1 -1
- package/src/engine/physics/mls-mpm/MLS_MPM.js +14 -0
- package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.d.ts +2 -1
- package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.js +1 -1
- package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClip.d.ts +1 -0
- package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.d.ts +1 -0
- package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/clip/SampleAudioClip.d.ts +2 -1
- package/src/engine/sound/sopra/definition/clip/SampleAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/clip/SampleAudioClip.js +12 -3
- package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClip.d.ts +1 -0
- package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.d.ts +1 -0
- package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/runtime/EventInstance.d.ts.map +1 -1
- package/src/engine/sound/sopra/runtime/EventInstance.js +4 -1
- package/src/format/image/avif/av1/decode/decode_coefficients.d.ts.map +1 -1
- package/src/format/image/avif/av1/decode/decode_coefficients.js +248 -259
- package/src/format/image/avif/av1/decode/read_golomb.d.ts +21 -0
- package/src/format/image/avif/av1/decode/read_golomb.d.ts.map +1 -0
- package/src/format/image/avif/av1/decode/read_golomb.js +49 -0
- package/src/format/image/avif/av1/filter/loop_restoration.js +461 -451
- package/src/format/image/avif/av1/util/round2.d.ts +19 -2
- package/src/format/image/avif/av1/util/round2.d.ts.map +1 -1
- package/src/format/image/avif/av1/util/round2.js +51 -27
- package/src/format/image/avif/heif/parse_avif_file.js +24 -0
- package/src/shade/renderer/buffer/table/GPUTypedTable.d.ts.map +1 -1
- package/src/shade/renderer/buffer/table/GPUTypedTable.js +14 -0
- package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md +46 -3
- package/src/shade/renderer/geometry/virtual/WEDGE_PLAN_2026_09_18.md +91 -1
- package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts +0 -13
- package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.js +0 -15
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_group_step.js +5 -5
- package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_DEFAULT_ATTRIBUTE_WEIGHT_RATIOS.d.ts +11 -1
- package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_DEFAULT_ATTRIBUTE_WEIGHT_RATIOS.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_DEFAULT_ATTRIBUTE_WEIGHT_RATIOS.js +11 -1
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.d.ts +18 -9
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.js +20 -26
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_normalize_source.d.ts +1 -3
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_normalize_source.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_normalize_source.js +2 -5
- package/src/shade/renderer/geometry/virtual/build/vgeo_build.js +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.js +14 -1
- package/src/shade/renderer/global_illumination/probe/octahedral/GPUProbeAtlas.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe/octahedral/GPUProbeAtlas.js +12 -3
- package/src/shade/renderer/light/environment/rgbe/RGBE_ReadPixels_RLE.d.ts.map +1 -1
- package/src/shade/renderer/light/environment/rgbe/RGBE_ReadPixels_RLE.js +7 -1
- package/src/shade/renderer/lightmap/GPULightMap.d.ts.map +1 -1
- package/src/shade/renderer/lightmap/GPULightMap.js +349 -336
- package/src/shade/renderer/loader/gltf/load_gltf.d.ts.map +1 -1
- package/src/shade/renderer/loader/gltf/load_gltf.js +1137 -1128
- package/src/shade/renderer/particles/DESIGN.md +14 -5
- package/src/shade/renderer/particles/GPUParticleSystem.d.ts +25 -10
- package/src/shade/renderer/particles/GPUParticleSystem.d.ts.map +1 -1
- package/src/shade/renderer/particles/GPUParticleSystem.js +182 -38
- package/src/shade/renderer/particles/graph/groups/simplex_reference.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/groups/simplex_reference.js +16 -10
- package/src/shade/renderer/particles/graph_particles.d.ts +4 -2
- package/src/shade/renderer/particles/graph_particles.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph_particles.js +8 -2
- package/src/shade/renderer/particles/optimizer/particle_vm_semantics.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/particle_vm_semantics.js +8 -2
- package/src/shade/renderer/particles/optimizer/pass_fold_constants.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/pass_fold_constants.js +10 -4
- package/src/shade/renderer/particles/shaders/shader_particle_init_free_list.d.ts +31 -0
- package/src/shade/renderer/particles/shaders/shader_particle_init_free_list.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/shader_particle_init_free_list.js +62 -0
- package/src/shade/renderer/particles/vm/ParticleVMReference.d.ts +13 -0
- package/src/shade/renderer/particles/vm/ParticleVMReference.d.ts.map +1 -1
- package/src/shade/renderer/particles/vm/ParticleVMReference.js +95 -17
- package/src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.d.ts +21 -0
- package/src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.js +28 -0
- package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.js +19 -2
- package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.js +19 -2
- package/src/shade/renderer/shader/type/WebGPUStruct.d.ts.map +1 -1
- package/src/shade/renderer/shader/type/WebGPUStruct.js +12 -0
- package/src/shade/renderer/texture/mipmaps/WebGPUMipmapGenerator.d.ts +8 -2
- package/src/shade/renderer/texture/mipmaps/WebGPUMipmapGenerator.d.ts.map +1 -1
- package/src/shade/renderer/texture/mipmaps/WebGPUMipmapGenerator.js +41 -6
- package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +2383 -2356
- package/src/view/tooltip/DomTooltipManager.d.ts +7 -0
- package/src/view/tooltip/DomTooltipManager.d.ts.map +1 -1
- package/src/view/tooltip/DomTooltipManager.js +22 -0
- package/src/view/tooltip/DomTooltipObserver.d.ts.map +1 -1
- package/src/view/tooltip/DomTooltipObserver.js +13 -3
- package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_CREASE_ANGLE.js +0 -45
|
@@ -284,7 +284,12 @@ export function evaluate_particle_operation(op, a, b, c) {
|
|
|
284
284
|
// the whole of what the option trades; anything an executor could disagree about by more
|
|
285
285
|
// than a rounding is refused below rather than folded.
|
|
286
286
|
case VM_OP.MAD: return Math.fround(a * b + c);
|
|
287
|
-
case VM_OP.MIX:
|
|
287
|
+
case VM_OP.MIX: {
|
|
288
|
+
const from = Math.fround(a * Math.fround(1 - c));
|
|
289
|
+
const to = Math.fround(b * c);
|
|
290
|
+
|
|
291
|
+
return Math.fround(from + to);
|
|
292
|
+
}
|
|
288
293
|
case VM_OP.SIN: return Math.fround(Math.sin(a));
|
|
289
294
|
case VM_OP.COS: return Math.fround(Math.cos(a));
|
|
290
295
|
case VM_OP.TAN: return Math.fround(Math.tan(a));
|
|
@@ -294,7 +299,7 @@ export function evaluate_particle_operation(op, a, b, c) {
|
|
|
294
299
|
case VM_OP.LOG: return Math.fround(Math.log(a));
|
|
295
300
|
// `1 / sqrt(a)` for `a <= 0` is an infinity or a `NaN`, and `finite` drops both — the same
|
|
296
301
|
// rule that keeps `SQRT` next door out of trouble, so there is nothing to say here.
|
|
297
|
-
case VM_OP.RSQRT: return Math.fround(1 / Math.sqrt(a));
|
|
302
|
+
case VM_OP.RSQRT: return Math.fround(1 / Math.fround(Math.sqrt(a)));
|
|
298
303
|
// WGSL's `pow` is `exp2(e2 * log2(e1))`, which is a `NaN` for every base at or below zero
|
|
299
304
|
// — including `pow(0, 0)`, where JS says 1. A fold that turned the hardware's `NaN` into a
|
|
300
305
|
// number would not be a rounding, so the non-positive bases are not folded at all.
|
|
@@ -306,9 +311,10 @@ export function evaluate_particle_operation(op, a, b, c) {
|
|
|
306
311
|
return NaN;
|
|
307
312
|
}
|
|
308
313
|
|
|
309
|
-
const
|
|
314
|
+
const span = Math.fround(b - a);
|
|
315
|
+
const t = Math.min(Math.max(Math.fround(Math.fround(c - a) / span), 0), 1);
|
|
310
316
|
|
|
311
|
-
return Math.fround(t * t * (3 - 2 * t));
|
|
317
|
+
return Math.fround(Math.fround(t * t) * Math.fround(3 - Math.fround(2 * t)));
|
|
312
318
|
}
|
|
313
319
|
// `atan2(0, 0)` is 0 in JS and left to the implementation in WGSL. Every other pair is an
|
|
314
320
|
// angle both agree on to a rounding.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The free list as a fresh pool leaves it: every slot free, `dead[i] = i`, bottom to top.
|
|
3
|
+
*
|
|
4
|
+
* It is a stack of slot indices — the emit pass pops `dead[--DEAD]` — so the order within it is
|
|
5
|
+
* arbitrary and identity is simply the cheapest arrangement to write. What matters is that every
|
|
6
|
+
* slot appears exactly once: a repeat hands two particles one record, and a gap strands a slot for
|
|
7
|
+
* the lifetime of the system.
|
|
8
|
+
*
|
|
9
|
+
* ## Why this is a pass and not a host write
|
|
10
|
+
*
|
|
11
|
+
* Zero is not a valid free list — `dead[i] = 0` for every `i` is the same slot half a million times
|
|
12
|
+
* over — so the buffer WebGPU hands back has to be written before the first emit. Writing it from
|
|
13
|
+
* the host means a typed array the size of the list (2 MiB at the default capacity), a loop over
|
|
14
|
+
* every element of it, and a staged upload, all on the frame the system is constructed. Here it is
|
|
15
|
+
* one store per slot with no host memory involved at all, and it leaves the free list `STORAGE`
|
|
16
|
+
* only: nothing else ever writes it from the host, so it needs no `COPY_DST` and stays off the
|
|
17
|
+
* driver's upload-friendly — and slower — path.
|
|
18
|
+
*
|
|
19
|
+
* ## Bounded launch, strided loop
|
|
20
|
+
*
|
|
21
|
+
* The pool capacity belongs to whoever constructs the system, and `capacity / workgroup_size` can
|
|
22
|
+
* pass `maxComputeWorkgroupsPerDimension` — a validation error for a direct dispatch, and worse
|
|
23
|
+
* than that for the indirect ones elsewhere in the engine, which simply do not run. So the caller
|
|
24
|
+
* clamps the launch and every lane strides through the list by the whole grid: the fill covers any
|
|
25
|
+
* capacity, and a grid that happens to cover it in one pass costs one comparison to find out.
|
|
26
|
+
*/
|
|
27
|
+
/** Lanes per workgroup. One store each, so this is sized for occupancy and nothing else. */
|
|
28
|
+
export const PARTICLE_INIT_FREE_LIST_WORKGROUP_SIZE: 256;
|
|
29
|
+
export const shader_particle_init_free_list: ComputeShader;
|
|
30
|
+
import { ComputeShader } from "../../shader/ComputeShader.js";
|
|
31
|
+
//# sourceMappingURL=shader_particle_init_free_list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shader_particle_init_free_list.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/particles/shaders/shader_particle_init_free_list.js"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,4FAA4F;AAC5F,yDAA0D;AAQ1D,2DAmBG;8BA3D2B,+BAA+B"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { u32 } from "../../shader/type/WEBGPU_WGSL_PRIMITIVE_TYPES.js";
|
|
2
|
+
import { WebGPUArray } from "../../shader/type/WebGPUArray.js";
|
|
3
|
+
import { ComputeShader } from "../../shader/ComputeShader.js";
|
|
4
|
+
import { CodeChunk } from "../../shader/compiler/CodeChunk.js";
|
|
5
|
+
import { ShaderResourceSetDescriptor } from "../../shader/resource/ShaderResourceSetDescriptor.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The free list as a fresh pool leaves it: every slot free, `dead[i] = i`, bottom to top.
|
|
9
|
+
*
|
|
10
|
+
* It is a stack of slot indices — the emit pass pops `dead[--DEAD]` — so the order within it is
|
|
11
|
+
* arbitrary and identity is simply the cheapest arrangement to write. What matters is that every
|
|
12
|
+
* slot appears exactly once: a repeat hands two particles one record, and a gap strands a slot for
|
|
13
|
+
* the lifetime of the system.
|
|
14
|
+
*
|
|
15
|
+
* ## Why this is a pass and not a host write
|
|
16
|
+
*
|
|
17
|
+
* Zero is not a valid free list — `dead[i] = 0` for every `i` is the same slot half a million times
|
|
18
|
+
* over — so the buffer WebGPU hands back has to be written before the first emit. Writing it from
|
|
19
|
+
* the host means a typed array the size of the list (2 MiB at the default capacity), a loop over
|
|
20
|
+
* every element of it, and a staged upload, all on the frame the system is constructed. Here it is
|
|
21
|
+
* one store per slot with no host memory involved at all, and it leaves the free list `STORAGE`
|
|
22
|
+
* only: nothing else ever writes it from the host, so it needs no `COPY_DST` and stays off the
|
|
23
|
+
* driver's upload-friendly — and slower — path.
|
|
24
|
+
*
|
|
25
|
+
* ## Bounded launch, strided loop
|
|
26
|
+
*
|
|
27
|
+
* The pool capacity belongs to whoever constructs the system, and `capacity / workgroup_size` can
|
|
28
|
+
* pass `maxComputeWorkgroupsPerDimension` — a validation error for a direct dispatch, and worse
|
|
29
|
+
* than that for the indirect ones elsewhere in the engine, which simply do not run. So the caller
|
|
30
|
+
* clamps the launch and every lane strides through the list by the whole grid: the fill covers any
|
|
31
|
+
* capacity, and a grid that happens to cover it in one pass costs one comparison to find out.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/** Lanes per workgroup. One store each, so this is sized for occupancy and nothing else. */
|
|
35
|
+
export const PARTICLE_INIT_FREE_LIST_WORKGROUP_SIZE = 256;
|
|
36
|
+
|
|
37
|
+
const resources = new ShaderResourceSetDescriptor();
|
|
38
|
+
|
|
39
|
+
// per-dispatch, four bytes, read-only: immediate data rather than a uniform buffer holding one u32
|
|
40
|
+
resources.setImmediate("u_capacity", u32);
|
|
41
|
+
resources.createGroup().addStorageBuffer("dead", WebGPUArray.u32, true);
|
|
42
|
+
|
|
43
|
+
export const shader_particle_init_free_list = ComputeShader.from({
|
|
44
|
+
label: "Particle Init Free List",
|
|
45
|
+
resources,
|
|
46
|
+
body: CodeChunk.from(
|
|
47
|
+
/* language=wgsl */
|
|
48
|
+
`
|
|
49
|
+
@compute @workgroup_size(${PARTICLE_INIT_FREE_LIST_WORKGROUP_SIZE}, 1, 1)
|
|
50
|
+
fn main(
|
|
51
|
+
@builtin(global_invocation_id) global_id : vec3<u32>,
|
|
52
|
+
@builtin(num_workgroups) group_count : vec3<u32>,
|
|
53
|
+
) {
|
|
54
|
+
// The grid may be smaller than the list; every lane keeps taking the next slot a whole grid on.
|
|
55
|
+
let stride = group_count.x * ${PARTICLE_INIT_FREE_LIST_WORKGROUP_SIZE}u;
|
|
56
|
+
|
|
57
|
+
for (var slot = global_id.x; slot < u_capacity; slot += stride) {
|
|
58
|
+
dead[slot] = slot;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
`),
|
|
62
|
+
});
|
|
@@ -29,6 +29,19 @@ export function u32_to_unit_f32(u: number): number;
|
|
|
29
29
|
* so a source may overlap the destination in any way and still mean what it held before the
|
|
30
30
|
* instruction.
|
|
31
31
|
*
|
|
32
|
+
* ## Every step is an f32 step
|
|
33
|
+
*
|
|
34
|
+
* The register file is `f32`, and so is the shader's. An opcode that takes more than one arithmetic
|
|
35
|
+
* step to reach its result therefore rounds at every one of them, through `Math.fround`, rather
|
|
36
|
+
* than computing wide and rounding on the way out. Rounding only at the end gives f32 RESULTS
|
|
37
|
+
* without f32 RANGE, and the range is where the two executors part company: a product that is
|
|
38
|
+
* `inf` on the device is an ordinary number in `f64`, a difference that saturates there is finite
|
|
39
|
+
* here, and neither is a rounding apart. `CROSS` answered 0 where the device answers NaN, and
|
|
40
|
+
* `SMOOTHSTEP` answered the midpoint where the device answers the near edge.
|
|
41
|
+
*
|
|
42
|
+
* Two opcodes are deliberately outside that rule, and each says why where it is written: `MAD`,
|
|
43
|
+
* which models the fused product, and `FRACT`, whose subtraction cannot round twice.
|
|
44
|
+
*
|
|
32
45
|
* @param {object} params
|
|
33
46
|
* @param {Uint32Array} params.words instruction words for the section (SPAWN or UPDATE)
|
|
34
47
|
* @param {Float32Array} params.constants constant pool, one float per word
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ParticleVMReference.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/particles/vm/ParticleVMReference.js"],"names":[],"mappings":"AA0BA;;;;;;;GAOG;AACH,gCAHW,MAAM,GACJ;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAW5C;AAED;;;;;GAKG;AACH,mCAHW,MAAM,GACJ,MAAM,CAIlB;AA8DD
|
|
1
|
+
{"version":3,"file":"ParticleVMReference.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/particles/vm/ParticleVMReference.js"],"names":[],"mappings":"AA0BA;;;;;;;GAOG;AACH,gCAHW,MAAM,GACJ;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAW5C;AAED;;;;;GAKG;AACH,mCAHW,MAAM,GACJ,MAAM,CAIlB;AA8DD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH;IAR+B,KAAK,EAAzB,WAAW;IACU,SAAS,EAA9B,YAAY;IACG,cAAc,EAA7B,MAAM;IACc,MAAM,EAA1B,WAAW;IACS,QAAQ,GAA5B,MAAM,EAAE,EAAE;IACM,SAAS,GAAzB,MAAM;IACJ;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAyUlD"}
|
|
@@ -125,6 +125,19 @@ const LITERAL_U32 = new Uint32Array(LITERAL_F32.buffer);
|
|
|
125
125
|
* so a source may overlap the destination in any way and still mean what it held before the
|
|
126
126
|
* instruction.
|
|
127
127
|
*
|
|
128
|
+
* ## Every step is an f32 step
|
|
129
|
+
*
|
|
130
|
+
* The register file is `f32`, and so is the shader's. An opcode that takes more than one arithmetic
|
|
131
|
+
* step to reach its result therefore rounds at every one of them, through `Math.fround`, rather
|
|
132
|
+
* than computing wide and rounding on the way out. Rounding only at the end gives f32 RESULTS
|
|
133
|
+
* without f32 RANGE, and the range is where the two executors part company: a product that is
|
|
134
|
+
* `inf` on the device is an ordinary number in `f64`, a difference that saturates there is finite
|
|
135
|
+
* here, and neither is a rounding apart. `CROSS` answered 0 where the device answers NaN, and
|
|
136
|
+
* `SMOOTHSTEP` answered the midpoint where the device answers the near edge.
|
|
137
|
+
*
|
|
138
|
+
* Two opcodes are deliberately outside that rule, and each says why where it is written: `MAD`,
|
|
139
|
+
* which models the fused product, and `FRACT`, whose subtraction cannot round twice.
|
|
140
|
+
*
|
|
128
141
|
* @param {object} params
|
|
129
142
|
* @param {Uint32Array} params.words instruction words for the section (SPAWN or UPDATE)
|
|
130
143
|
* @param {Float32Array} params.constants constant pool, one float per word
|
|
@@ -263,6 +276,18 @@ export function execute_particle_vm({
|
|
|
263
276
|
case DIV:
|
|
264
277
|
for (let i = 0; i < w; i++) d[i] = a[i] / b[i];
|
|
265
278
|
break;
|
|
279
|
+
// The exception to the rule above, and the only one that is a choice. `MAD` models the
|
|
280
|
+
// FUSED form: the product reaches the addition unrounded, which is what makes it more
|
|
281
|
+
// than `MUL` then `ADD`. {@link ../optimizer/pass_peephole.js} refuses to fuse that
|
|
282
|
+
// pair into a `MAD` by default for exactly this reason, and
|
|
283
|
+
// {@link ../optimizer/particle_integer_bounds.js} exists only to prove the cases where
|
|
284
|
+
// the product needs no rounding and the two forms therefore agree; rounding it here
|
|
285
|
+
// would make both of those dead weight.
|
|
286
|
+
//
|
|
287
|
+
// The shader spells this `a * b + c`, which a driver is free to contract into an FMA
|
|
288
|
+
// or not, so it is a reading rather than a certainty — `VM_OP.MAD` is INEXACT in
|
|
289
|
+
// {@link ../optimizer/particle_vm_semantics.js} to say so. Spelling the shader
|
|
290
|
+
// `fma(a, b, c)` would settle it in this reading's favour on every device.
|
|
266
291
|
case MAD:
|
|
267
292
|
for (let i = 0; i < w; i++) d[i] = a[i] * b[i] + c[i];
|
|
268
293
|
break;
|
|
@@ -272,8 +297,21 @@ export function execute_particle_vm({
|
|
|
272
297
|
case MAX:
|
|
273
298
|
for (let i = 0; i < w; i++) d[i] = Math.max(a[i], b[i]);
|
|
274
299
|
break;
|
|
300
|
+
// WGSL defines `mix(e1, e2, e3)` as `e1 * (1 - e3) + e2 * e3`, and this used to spell
|
|
301
|
+
// it as the lerp `a + (b - a) * c`. The two are the same number only where the span
|
|
302
|
+
// `b - a` loses nothing, and at the ends of the f32 range it loses everything:
|
|
303
|
+
// `mix(1e20, 1, 1)` is 1 on the device and was 0 here, because `1 - 1e20` IS `-1e20`
|
|
304
|
+
// in f32. Rounding the lerp's steps without changing its shape would have made it
|
|
305
|
+
// worse — the span of (3e38, -3e38) is not an f32 at all, so a blend both of whose
|
|
306
|
+
// ends are finite would have come out infinite. Scaling each end on its own is what
|
|
307
|
+
// the shader does and it cannot overflow where the result does not.
|
|
275
308
|
case MIX:
|
|
276
|
-
for (let i = 0; i < w; i++)
|
|
309
|
+
for (let i = 0; i < w; i++) {
|
|
310
|
+
const from = Math.fround(a[i] * Math.fround(1 - c[i]));
|
|
311
|
+
const to = Math.fround(b[i] * c[i]);
|
|
312
|
+
|
|
313
|
+
d[i] = Math.fround(from + to);
|
|
314
|
+
}
|
|
277
315
|
break;
|
|
278
316
|
case CLAMP:
|
|
279
317
|
for (let i = 0; i < w; i++) d[i] = Math.min(Math.max(a[i], b[i]), c[i]);
|
|
@@ -288,6 +326,12 @@ export function execute_particle_vm({
|
|
|
288
326
|
case FLOOR:
|
|
289
327
|
for (let i = 0; i < w; i++) d[i] = Math.floor(a[i]);
|
|
290
328
|
break;
|
|
329
|
+
// The other exception, and this one is free: `a - floor(a)` cannot round twice. For
|
|
330
|
+
// |a| >= 1 the fractional part is a run of at most 23 of a's own mantissa bits and is
|
|
331
|
+
// an f32 exactly; for |a| < 1 it is a itself, or `1 + a`, which only becomes inexact
|
|
332
|
+
// in f64 once |a| falls below about 2^-29 — by which point it is far inside the last
|
|
333
|
+
// f32 step below 1 and rounds to 1.0 either way. The two conditions a double rounding
|
|
334
|
+
// would need cannot hold at once, which the spec checks rather than assumes.
|
|
291
335
|
case FRACT:
|
|
292
336
|
for (let i = 0; i < w; i++) d[i] = a[i] - Math.floor(a[i]);
|
|
293
337
|
break;
|
|
@@ -316,9 +360,13 @@ export function execute_particle_vm({
|
|
|
316
360
|
for (let i = 0; i < w; i++) d[i] = smoothstep(a[i], b[i], c[i]);
|
|
317
361
|
break;
|
|
318
362
|
// WGSL's `inverseSqrt`. Spelled as the reciprocal of a root rather than approximated,
|
|
319
|
-
// because the reference is the definition the hardware's estimate is measured against
|
|
363
|
+
// because the reference is the definition the hardware's estimate is measured against
|
|
364
|
+
// — which is also why the root is rounded before the division reads it. A definition
|
|
365
|
+
// carried out in f64 is not the thing `inverseSqrt` is allowed to be 2 ULP away from:
|
|
366
|
+
// 1 / sqrt(0.0009) is 33.33333206176758 taken wide and 33.333335876464844 taken the
|
|
367
|
+
// way the device takes it, and the slack is measured from the second.
|
|
320
368
|
case RSQRT:
|
|
321
|
-
for (let i = 0; i < w; i++) d[i] = 1 / Math.sqrt(a[i]);
|
|
369
|
+
for (let i = 0; i < w; i++) d[i] = Math.fround(1 / Math.fround(Math.sqrt(a[i])));
|
|
322
370
|
break;
|
|
323
371
|
case TAN:
|
|
324
372
|
for (let i = 0; i < w; i++) d[i] = Math.tan(a[i]);
|
|
@@ -336,40 +384,55 @@ export function execute_particle_vm({
|
|
|
336
384
|
|
|
337
385
|
// The reductions write a single lane; whatever wants the result wider reads it through
|
|
338
386
|
// an `.xxxx` swizzle.
|
|
387
|
+
//
|
|
388
|
+
// Every step goes through `Math.fround`, because a plain `let sum = 0` is an f64
|
|
389
|
+
// accumulator and only the store into the register file rounds — which gives f32
|
|
390
|
+
// RESULTS without f32 RANGE, and the range is not recovered at the end. The shader
|
|
391
|
+
// computes `dot(a, a)` entirely in f32, where 1e20 squared is already `inf` and
|
|
392
|
+
// 1e-25 squared is already zero, so the two sides answered `inf` against 1.00000002e20
|
|
393
|
+
// and, through NORMALIZE's `len > 0`, the zero vector against a unit one.
|
|
339
394
|
case DOT: {
|
|
340
395
|
let dp = 0;
|
|
341
|
-
for (let i = 0; i < w; i++) dp
|
|
396
|
+
for (let i = 0; i < w; i++) dp = Math.fround(dp + Math.fround(a[i] * b[i]));
|
|
342
397
|
d[0] = dp;
|
|
343
398
|
break;
|
|
344
399
|
}
|
|
400
|
+
// Six products and three differences, every one of them in f32. Taken wide, a lane
|
|
401
|
+
// whose two products both overflow cancelled to exactly 0 — the mathematically right
|
|
402
|
+
// answer for a vector crossed with itself — where the device subtracts `inf - inf` and
|
|
403
|
+
// gets NaN. a = b = (0, 1e20, 1e20) is the shortest witness, and a NaN that exists
|
|
404
|
+
// only on the GPU is the worst shape this takes: invisible in the editor preview, and
|
|
405
|
+
// loose in a position attribute from the first frame it appears.
|
|
345
406
|
case CROSS:
|
|
346
|
-
d[0] = a[1] * b[2] - a[2] * b[1];
|
|
347
|
-
d[1] = a[2] * b[0] - a[0] * b[2];
|
|
348
|
-
d[2] = a[0] * b[1] - a[1] * b[0];
|
|
407
|
+
d[0] = Math.fround(Math.fround(a[1] * b[2]) - Math.fround(a[2] * b[1]));
|
|
408
|
+
d[1] = Math.fround(Math.fround(a[2] * b[0]) - Math.fround(a[0] * b[2]));
|
|
409
|
+
d[2] = Math.fround(Math.fround(a[0] * b[1]) - Math.fround(a[1] * b[0]));
|
|
349
410
|
// A fourth lane, if asked for, is the zero the opcode used to append.
|
|
350
411
|
d[3] = 0;
|
|
351
412
|
break;
|
|
352
413
|
case LENGTH: {
|
|
353
414
|
let sum = 0;
|
|
354
|
-
for (let i = 0; i < w; i++) sum
|
|
355
|
-
d[0] = Math.sqrt(sum);
|
|
415
|
+
for (let i = 0; i < w; i++) sum = Math.fround(sum + Math.fround(a[i] * a[i]));
|
|
416
|
+
d[0] = Math.fround(Math.sqrt(sum));
|
|
356
417
|
break;
|
|
357
418
|
}
|
|
358
419
|
case NORMALIZE: {
|
|
359
420
|
let sum = 0;
|
|
360
|
-
for (let i = 0; i < w; i++) sum
|
|
361
|
-
const len = Math.sqrt(sum);
|
|
362
|
-
|
|
421
|
+
for (let i = 0; i < w; i++) sum = Math.fround(sum + Math.fround(a[i] * a[i]));
|
|
422
|
+
const len = Math.fround(Math.sqrt(sum));
|
|
423
|
+
// the shader's `select(0.0, 1.0 / len, len > 0.0)` — the same test, over a length
|
|
424
|
+
// that has now underflowed on the same inputs
|
|
425
|
+
const inv = len > 0 ? Math.fround(1 / len) : 0;
|
|
363
426
|
for (let i = 0; i < w; i++) d[i] = a[i] * inv;
|
|
364
427
|
break;
|
|
365
428
|
}
|
|
366
429
|
case DISTANCE: {
|
|
367
430
|
let sum = 0;
|
|
368
431
|
for (let i = 0; i < w; i++) {
|
|
369
|
-
const e = a[i] - b[i];
|
|
370
|
-
sum
|
|
432
|
+
const e = Math.fround(a[i] - b[i]);
|
|
433
|
+
sum = Math.fround(sum + Math.fround(e * e));
|
|
371
434
|
}
|
|
372
|
-
d[0] = Math.sqrt(sum);
|
|
435
|
+
d[0] = Math.fround(Math.sqrt(sum));
|
|
373
436
|
break;
|
|
374
437
|
}
|
|
375
438
|
|
|
@@ -414,6 +477,18 @@ export function execute_particle_vm({
|
|
|
414
477
|
}
|
|
415
478
|
|
|
416
479
|
/**
|
|
480
|
+
* WGSL's `smoothstep`, which is `t * t * (3 - 2t)` over `t = clamp((x - low) / (high - low), 0, 1)`.
|
|
481
|
+
*
|
|
482
|
+
* Every step is an f32 step, for the reason the executor's docblock gives, and here it is the SPAN
|
|
483
|
+
* that carries it: with the edges at ±3e38 the divisor is 6e38, which is `inf` in f32, so the
|
|
484
|
+
* device divides by an infinity, gets 0, and returns the near edge. Taken wide the divisor is
|
|
485
|
+
* finite and the answer was the midpoint — a ramp authored across a range wider than f32 reaches
|
|
486
|
+
* read as half-way in the editor preview and as not started at all in the game.
|
|
487
|
+
*
|
|
488
|
+
* The equal-edges guard stands in for a division by zero the device would do instead. It is the one
|
|
489
|
+
* place this function is not the shader's arithmetic, and it only differs from it at `x === edge0`,
|
|
490
|
+
* where the device's `0 / 0` is a NaN that WGSL does not define the clamp of.
|
|
491
|
+
*
|
|
417
492
|
* @param {number} edge0
|
|
418
493
|
* @param {number} edge1
|
|
419
494
|
* @param {number} x
|
|
@@ -421,6 +496,9 @@ export function execute_particle_vm({
|
|
|
421
496
|
*/
|
|
422
497
|
function smoothstep(edge0, edge1, x) {
|
|
423
498
|
if (edge0 === edge1) return x < edge0 ? 0 : 1;
|
|
424
|
-
|
|
425
|
-
|
|
499
|
+
|
|
500
|
+
const span = Math.fround(edge1 - edge0);
|
|
501
|
+
const t = Math.min(Math.max(Math.fround(Math.fround(x - edge0) / span), 0), 1);
|
|
502
|
+
|
|
503
|
+
return Math.fround(Math.fround(t * t) * Math.fround(3 - Math.fround(2 * t)));
|
|
426
504
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether the scene has anything in an opaque rasterization bucket.
|
|
3
|
+
*
|
|
4
|
+
* The opaque path clears the visibility-buffer colour attachments and the depth attachment on its
|
|
5
|
+
* first draw, and it is the only thing that does — every later pass is recorded with `load`. So a
|
|
6
|
+
* scene whose materials are all alpha-tested or transparent, which is an ordinary thing (a
|
|
7
|
+
* foliage-only vegetation view, a glass preview, a point light whose only casters are billboards),
|
|
8
|
+
* used to draw over the previous frame's contents: geometry depth-tested against stale occluders,
|
|
9
|
+
* and every pixel the alpha-tested pass did not cover keeping last frame's mesh and triangle ids for
|
|
10
|
+
* the deferred shading to resolve as real surfaces.
|
|
11
|
+
*
|
|
12
|
+
* This is what lets the composer hand the clear to the first pass that will actually run.
|
|
13
|
+
*
|
|
14
|
+
* @param {Set<number>} active_buckets bucket ids any material in the scene asks for
|
|
15
|
+
* @returns {boolean}
|
|
16
|
+
*
|
|
17
|
+
* @author Alex Goldring
|
|
18
|
+
* @copyright Company Named Limited (c) 2026
|
|
19
|
+
*/
|
|
20
|
+
export function rasterization_has_opaque_bucket(active_buckets: Set<number>): boolean;
|
|
21
|
+
//# sourceMappingURL=rasterization_has_opaque_bucket.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rasterization_has_opaque_bucket.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;GAkBG;AACH,gEANW,IAAI,MAAM,CAAC,GACT,OAAO,CAWnB"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { rasterization_opaque_buckets } from "./rasterization_opaque_buckets.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Whether the scene has anything in an opaque rasterization bucket.
|
|
5
|
+
*
|
|
6
|
+
* The opaque path clears the visibility-buffer colour attachments and the depth attachment on its
|
|
7
|
+
* first draw, and it is the only thing that does — every later pass is recorded with `load`. So a
|
|
8
|
+
* scene whose materials are all alpha-tested or transparent, which is an ordinary thing (a
|
|
9
|
+
* foliage-only vegetation view, a glass preview, a point light whose only casters are billboards),
|
|
10
|
+
* used to draw over the previous frame's contents: geometry depth-tested against stale occluders,
|
|
11
|
+
* and every pixel the alpha-tested pass did not cover keeping last frame's mesh and triangle ids for
|
|
12
|
+
* the deferred shading to resolve as real surfaces.
|
|
13
|
+
*
|
|
14
|
+
* This is what lets the composer hand the clear to the first pass that will actually run.
|
|
15
|
+
*
|
|
16
|
+
* @param {Set<number>} active_buckets bucket ids any material in the scene asks for
|
|
17
|
+
* @returns {boolean}
|
|
18
|
+
*
|
|
19
|
+
* @author Alex Goldring
|
|
20
|
+
* @copyright Company Named Limited (c) 2026
|
|
21
|
+
*/
|
|
22
|
+
export function rasterization_has_opaque_bucket(active_buckets) {
|
|
23
|
+
for (const _bucket of rasterization_opaque_buckets(active_buckets)) {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph_rasterize_scene_fast.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"graph_rasterize_scene_fast.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.js"],"names":[],"mappings":"AAoBA;;;;;;;;;;GAUG;AACH;;;;;;;IAFa;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,EAAE,CAAA;CAAC,CA0J3D;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH;IAJ+B,KAAK;IACC,GAAG;IACA,MAAM;SA8C7C"}
|
|
@@ -13,6 +13,7 @@ import { graph_vgeo_expand_if_enabled } from "../../geometry/virtual/runtime/gra
|
|
|
13
13
|
import { RasterizationCollectionType } from "../expand/RasterizationCollectionType.js";
|
|
14
14
|
import { construct_primitive_state } from "../bucket/construct_primitive_state.js";
|
|
15
15
|
import { rasterization_opaque_buckets } from "../bucket/rasterization_opaque_buckets.js";
|
|
16
|
+
import { rasterization_has_opaque_bucket } from "../bucket/rasterization_has_opaque_bucket.js";
|
|
16
17
|
import { graph_mesh_frustum_cull } from "../compute/graph_mesh_frustum_cull.js";
|
|
17
18
|
import { graph_rasterize_meshes_alpha_tested } from "../native/graph_rasterize_meshes_alpha_tested.js";
|
|
18
19
|
import { graph_extract_meshes_from_bucket } from "./graph_extract_meshes_from_bucket.js";
|
|
@@ -123,13 +124,27 @@ export function graph_rasterize_scene_fast_raw({
|
|
|
123
124
|
|
|
124
125
|
// alpha-tested: reuse the existing native pass but skip HZB filtering by passing hzb=-1
|
|
125
126
|
if (job.alpha_tested_pass !== undefined) {
|
|
127
|
+
/**
|
|
128
|
+
* The opaque path's first draw is the only thing that clears the visibility-buffer colour
|
|
129
|
+
* attachments and the depth attachment — the clear values live on the opaque pass descriptor
|
|
130
|
+
* and reach the targets through its `loadOp`, and every pass after it is recorded with
|
|
131
|
+
* `load`. When the scene has no opaque bucket that draw never happens, so the first pass that
|
|
132
|
+
* does run owns the clear. The alpha-tested descriptor carries the same clear values
|
|
133
|
+
* (`[0,0,0,0]`, VIZ_BUFFER_INVALID_MESH_VALUE, DEPTH_BACKGROUND_VALUE), so handing it over
|
|
134
|
+
* changes nothing about what the targets are cleared to.
|
|
135
|
+
*
|
|
136
|
+
* Wherever an opaque bucket exists — nearly every scene — this starts true and every call
|
|
137
|
+
* below asks for `false`, exactly as before.
|
|
138
|
+
*/
|
|
139
|
+
let targets_cleared = rasterization_has_opaque_bucket(active_buckets);
|
|
140
|
+
|
|
126
141
|
|
|
127
142
|
const alpha_tested_front = graph_rasterize_meshes_alpha_tested({
|
|
128
143
|
graph,
|
|
129
144
|
buckets: gr_render_buckets,
|
|
130
145
|
view_ctx: view_ctx,
|
|
131
146
|
render_pass_descriptor: job.alpha_tested_pass,
|
|
132
|
-
clear_target:
|
|
147
|
+
clear_target: !targets_cleared,
|
|
133
148
|
side: ShadeDrawSide.Front,
|
|
134
149
|
cull_policy: job.cull_policy,
|
|
135
150
|
limits,
|
|
@@ -142,13 +157,14 @@ export function graph_rasterize_scene_fast_raw({
|
|
|
142
157
|
|
|
143
158
|
target_depth = alpha_tested_front.depth;
|
|
144
159
|
targets_color = alpha_tested_front.color_targets;
|
|
160
|
+
targets_cleared = targets_cleared || alpha_tested_front.executed;
|
|
145
161
|
|
|
146
162
|
const alpha_tested_double = graph_rasterize_meshes_alpha_tested({
|
|
147
163
|
graph,
|
|
148
164
|
buckets: gr_render_buckets,
|
|
149
165
|
view_ctx: view_ctx,
|
|
150
166
|
render_pass_descriptor: job.alpha_tested_pass,
|
|
151
|
-
clear_target:
|
|
167
|
+
clear_target: !targets_cleared,
|
|
152
168
|
side: ShadeDrawSide.Double,
|
|
153
169
|
cull_policy: job.cull_policy,
|
|
154
170
|
limits,
|
|
@@ -161,6 +177,7 @@ export function graph_rasterize_scene_fast_raw({
|
|
|
161
177
|
|
|
162
178
|
target_depth = alpha_tested_double.depth;
|
|
163
179
|
targets_color = alpha_tested_double.color_targets;
|
|
180
|
+
targets_cleared = targets_cleared || alpha_tested_double.executed;
|
|
164
181
|
}
|
|
165
182
|
|
|
166
183
|
return {target_depth, targets_color};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph_rasterize_scene.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/rasterize/standard/graph_rasterize_scene.js"],"names":[],"mappings":"
|
|
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"}
|
|
@@ -11,6 +11,7 @@ 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";
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* TODO:
|
|
@@ -155,13 +156,27 @@ export function graph_rasterize_scene({
|
|
|
155
156
|
|
|
156
157
|
|
|
157
158
|
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
|
+
|
|
158
173
|
|
|
159
174
|
const alpha_tested_front = graph_rasterize_meshes_alpha_tested({
|
|
160
175
|
graph,
|
|
161
176
|
buckets: gr_render_buckets,
|
|
162
177
|
view_ctx,
|
|
163
178
|
render_pass_descriptor: job.alpha_tested_pass,
|
|
164
|
-
clear_target:
|
|
179
|
+
clear_target: !targets_cleared,
|
|
165
180
|
side: ShadeDrawSide.Front,
|
|
166
181
|
cull_policy: job.cull_policy,
|
|
167
182
|
limits,
|
|
@@ -174,13 +189,14 @@ export function graph_rasterize_scene({
|
|
|
174
189
|
|
|
175
190
|
gr_texture_depth = alpha_tested_front.depth;
|
|
176
191
|
raster_color_targets = alpha_tested_front.color_targets;
|
|
192
|
+
targets_cleared = targets_cleared || alpha_tested_front.executed;
|
|
177
193
|
|
|
178
194
|
const alpha_tested_double = graph_rasterize_meshes_alpha_tested({
|
|
179
195
|
graph,
|
|
180
196
|
buckets: gr_render_buckets,
|
|
181
197
|
view_ctx,
|
|
182
198
|
render_pass_descriptor: job.alpha_tested_pass,
|
|
183
|
-
clear_target:
|
|
199
|
+
clear_target: !targets_cleared,
|
|
184
200
|
side: ShadeDrawSide.Double,
|
|
185
201
|
cull_policy: job.cull_policy,
|
|
186
202
|
limits,
|
|
@@ -193,6 +209,7 @@ export function graph_rasterize_scene({
|
|
|
193
209
|
|
|
194
210
|
gr_texture_depth = alpha_tested_double.depth;
|
|
195
211
|
raster_color_targets = alpha_tested_double.color_targets;
|
|
212
|
+
targets_cleared = targets_cleared || alpha_tested_double.executed;
|
|
196
213
|
|
|
197
214
|
if (alpha_tested_front.executed || alpha_tested_double.executed) {
|
|
198
215
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebGPUStruct.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/shader/type/WebGPUStruct.js"],"names":[],"mappings":"AA0BA;;;;GAIG;AACH;
|
|
1
|
+
{"version":3,"file":"WebGPUStruct.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/shader/type/WebGPUStruct.js"],"names":[],"mappings":"AA0BA;;;;GAIG;AACH;IAiNI;;;;;;;;;;;;;;OAcG;IACH,8BAHW,MAAM,GACL,YAAY,CAiCvB;IAED;;;;OAIG;IACH,6BAHc,eAAe,KACjB,YAAY,CAiBvB;IA1QD;;;OAGG;IACH,uBAEC;IAVD,kBAEC;IAUD;;;OAGG;IACH,iBAFU,eAAe,EAAE,CAEf;IAmBZ;;;;OAIG;IACH,YAHW,YAAY,GACV,IAAI,CAwBhB;IAED;;;OAGG;IACH,SAFY,YAAY,CAUvB;IAED;;;;;;;;OAQG;IACH,kBAJa,MAAM,CA+BlB;IAED;;;;;;OAMG;IACH,QAFa,IAAI,CAoChB;IAED,4BAYC;IAqGD;;;;OAIG;IACH,gBAHW,MAAM,GACJ,OAAO,CAKnB;IAGD;;;;;OAKG;IACH,gBAJW,MAAM,GACL,eAAe,CAa1B;IA8GL;;;OAGG;IACH,mBAFU,OAAO,CAEc;;CAN9B;2BArb0B,iBAAiB;gCAEZ,sBAAsB"}
|
|
@@ -83,6 +83,18 @@ export class WebGPUStruct extends WebGPUType {
|
|
|
83
83
|
this.#size = other.#size;
|
|
84
84
|
this.#align = other.#align;
|
|
85
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Carried with the layout it describes. Without it a clone claimed a size and per-field
|
|
88
|
+
* offsets while reading as unbuilt, and `pack()` — which reorders the fields and then
|
|
89
|
+
* rebuilds only `if (this.#is_built)` — left the declaration in the packed order and the
|
|
90
|
+
* offsets in the unpacked one. WGSL would lay a field out at one address and
|
|
91
|
+
* `write_struct_value` seek to another, with no error anywhere.
|
|
92
|
+
*/
|
|
93
|
+
this.#is_built = other.#is_built;
|
|
94
|
+
|
|
95
|
+
//the cached WGSL text belongs to the struct that built it
|
|
96
|
+
this.#declaration_chunk = undefined;
|
|
97
|
+
|
|
86
98
|
this.fields = other.fields.map(f => f.clone());
|
|
87
99
|
|
|
88
100
|
return this;
|
|
@@ -47,10 +47,16 @@ export class WebGPUMipmapGenerator {
|
|
|
47
47
|
* @param {GPUTexture} texture - Texture to generate mipmaps for.
|
|
48
48
|
* @param {GPUTextureDescriptor} texture_descriptor - GPUTextureDescriptor the texture was created with.
|
|
49
49
|
* @param {TextureFilterType} [filter]
|
|
50
|
-
* @param {GPUCommandEncoder} [commandEncoder]
|
|
50
|
+
* @param {GPUCommandEncoder} [commandEncoder] when supplied, the commands are recorded rather
|
|
51
|
+
* than submitted, so the scratch resources outlive this call and `release_after_submit` is
|
|
52
|
+
* required
|
|
53
|
+
* @param {{destroy: function():void}[]} [release_after_submit] where the scratch buffer and
|
|
54
|
+
* textures go when the encoder is the caller's; destroy them once it has submitted
|
|
51
55
|
* @returns {GPUTexture} - The originally passed texture
|
|
52
56
|
*/
|
|
53
|
-
generateMipmap(texture: GPUTexture, texture_descriptor: GPUTextureDescriptor, filter?: TextureFilterType, commandEncoder?: GPUCommandEncoder
|
|
57
|
+
generateMipmap(texture: GPUTexture, texture_descriptor: GPUTextureDescriptor, filter?: TextureFilterType, commandEncoder?: GPUCommandEncoder, release_after_submit?: {
|
|
58
|
+
destroy: () => void;
|
|
59
|
+
}[]): GPUTexture;
|
|
54
60
|
#private;
|
|
55
61
|
}
|
|
56
62
|
import { HashMap } from "../../../../core/collection/map/HashMap.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebGPUMipmapGenerator.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/texture/mipmaps/WebGPUMipmapGenerator.js"],"names":[],"mappings":"AAkMA;;;GAGG;AACH;IAqDI;;;OAGG;IACH,+BAWC;IAnED;;OAEG;IACH,kBAAM;IAEN;;;;OAIG;IACH,oBAFU,QAAQ,eAAe,oBAAoB,CAE3B;IAE1B;;OAEG;IACH,oBAAQ;IAgCR,gBAGC;IA6DD;;;;;OAKG;IACH,yEAFW,iBAAiB,QAY3B;IAED;;;OAGG;IACH,wBAFW,MAAM,QAyBhB;IAED;;OAEG;IACH,cAEC;IAmGG,qBAiBE;IAEF,oBAGE;IAGN
|
|
1
|
+
{"version":3,"file":"WebGPUMipmapGenerator.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/texture/mipmaps/WebGPUMipmapGenerator.js"],"names":[],"mappings":"AAkMA;;;GAGG;AACH;IAqDI;;;OAGG;IACH,+BAWC;IAnED;;OAEG;IACH,kBAAM;IAEN;;;;OAIG;IACH,oBAFU,QAAQ,eAAe,oBAAoB,CAE3B;IAE1B;;OAEG;IACH,oBAAQ;IAgCR,gBAGC;IA6DD;;;;;OAKG;IACH,yEAFW,iBAAiB,QAY3B;IAED;;;OAGG;IACH,wBAFW,MAAM,QAyBhB;IAED;;OAEG;IACH,cAEC;IAmGG,qBAiBE;IAEF,oBAGE;IAGN;;;;;;;;;;;;OAYG;IACH,uFARW,iBAAiB,6DAIjB;QAAC,OAAO,QAAa,IAAI,CAAA;KAAC,EAAE,cAyZtC;;CACJ;wBAr4BuB,4CAA4C;AA2CpE;IAEI;;OAEG;IACH,yBAAO;IAEP;;OAEG;IACH;;;;QA1BA;;WAEG;;QAEH;;WAEG;;QAEH;;WAEG;;;;MAgBI;IAEP;;;;OAIG;IACH,cAHW,eAAe,GACd,OAAO,CAMlB;IAED;;;OAGG;IACH,QAFY,MAAM,CAOjB;CAEJ;kCAjEiC,yBAAyB"}
|