@woosh/meep-engine 3.2.0 → 3.4.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/editor/process/SymbolicDisplayProcess.js +106 -106
- package/editor/process/symbolic/{SoundEmitterSymbolicDisplay.js → AudioEmitterSymbolicDisplay.js} +67 -57
- package/package.json +94 -94
- package/src/DUPLICATION_AUDIT_2026_08_07/F_sound.md +110 -102
- package/src/avif/encoder_worker.js +5 -18
- package/src/core/bvh2/bvh3/ebvh_build_hierarchy_radix.d.ts +54 -0
- package/src/core/bvh2/bvh3/ebvh_build_hierarchy_radix.d.ts.map +1 -1
- package/src/core/bvh2/bvh3/ebvh_build_hierarchy_radix.js +10 -3
- package/src/core/color/PQ/PQ_constants.d.ts +43 -0
- package/src/core/color/PQ/PQ_constants.d.ts.map +1 -0
- package/src/core/color/PQ/PQ_constants.js +50 -0
- package/src/core/color/PQ/PQ_to_linear.d.ts +24 -0
- package/src/core/color/PQ/PQ_to_linear.d.ts.map +1 -0
- package/src/core/color/PQ/PQ_to_linear.js +35 -0
- package/src/core/color/PQ/linear_to_PQ.d.ts +27 -0
- package/src/core/color/PQ/linear_to_PQ.d.ts.map +1 -0
- package/src/core/color/PQ/linear_to_PQ.js +32 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_other_face.d.ts +32 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_other_face.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_other_face.js +59 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_face_third_vertex.d.ts +26 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_face_third_vertex.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_face_third_vertex.js +55 -0
- package/src/engine/EntityCreator.d.ts +19 -11
- package/src/engine/EntityCreator.d.ts.map +1 -1
- package/src/engine/EntityCreator.js +116 -94
- package/src/engine/achievements/AchievementManager.d.ts.map +1 -1
- package/src/engine/achievements/AchievementManager.js +437 -433
- package/src/engine/animation/AnimatedActions.d.ts +6 -1
- package/src/engine/animation/AnimatedActions.d.ts.map +1 -1
- package/src/engine/animation/AnimatedActions.js +111 -81
- package/src/engine/animation/AnimationUtils.d.ts.map +1 -1
- package/src/engine/animation/AnimationUtils.js +234 -252
- package/src/engine/ecs/storage/binary/SERIALIZATION_COVERAGE.md +275 -269
- package/src/engine/ecs/storage/populateEngineSerializationRegistry.d.ts.map +1 -1
- package/src/engine/ecs/storage/populateEngineSerializationRegistry.js +290 -265
- package/src/engine/ecs/util/hideEntityGracefully.d.ts.map +1 -1
- package/src/engine/ecs/util/hideEntityGracefully.js +196 -208
- package/src/engine/graphics3/VolumetricLightMap.d.ts +90 -0
- package/src/engine/graphics3/VolumetricLightMap.d.ts.map +1 -0
- package/src/engine/graphics3/VolumetricLightMap.js +160 -0
- package/src/engine/graphics3/VolumetricLightMapSerializationAdapter.d.ts +32 -0
- package/src/engine/graphics3/VolumetricLightMapSerializationAdapter.d.ts.map +1 -0
- package/src/engine/graphics3/VolumetricLightMapSerializationAdapter.js +67 -0
- package/src/engine/graphics3/VolumetricLightMapSystem3.d.ts +98 -0
- package/src/engine/graphics3/VolumetricLightMapSystem3.d.ts.map +1 -0
- package/src/engine/graphics3/VolumetricLightMapSystem3.js +261 -0
- package/src/engine/intelligence/behavior/primitive/ActionBehavior.d.ts +3 -1
- package/src/engine/intelligence/behavior/primitive/ActionBehavior.d.ts.map +1 -1
- package/src/engine/intelligence/behavior/primitive/ActionBehavior.js +4 -2
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
- package/src/engine/sound/SOUND_EMITTER_RETIREMENT_PLAN.md +536 -0
- package/src/engine/sound/SoundEngine.d.ts +2 -2
- package/src/engine/sound/SoundEngine.js +124 -124
- package/src/engine/sound/ecs/audio/AudioEmitter.d.ts +20 -0
- package/src/engine/sound/ecs/audio/AudioEmitter.d.ts.map +1 -1
- package/src/engine/sound/ecs/audio/AudioEmitter.js +25 -2
- package/src/engine/sound/ecs/audio/AudioEmitterSystem.d.ts +6 -6
- package/src/engine/sound/ecs/audio/AudioEmitterSystem.d.ts.map +1 -1
- package/src/engine/sound/ecs/audio/AudioEmitterSystem.js +338 -314
- package/src/engine/sound/ecs/trigger/AudioEventTrigger.d.ts +143 -0
- package/src/engine/sound/ecs/trigger/AudioEventTrigger.d.ts.map +1 -0
- package/src/engine/sound/ecs/trigger/AudioEventTrigger.js +191 -0
- package/src/engine/sound/ecs/trigger/AudioEventTriggerSerializationAdapter.d.ts +33 -0
- package/src/engine/sound/ecs/trigger/AudioEventTriggerSerializationAdapter.d.ts.map +1 -0
- package/src/engine/sound/ecs/trigger/AudioEventTriggerSerializationAdapter.js +81 -0
- package/src/engine/sound/ecs/trigger/AudioEventTriggerSystem.d.ts +65 -0
- package/src/engine/sound/ecs/trigger/AudioEventTriggerSystem.d.ts.map +1 -0
- package/src/engine/sound/ecs/trigger/AudioEventTriggerSystem.js +184 -0
- package/src/engine/sound/sopra/IMPLEMENTATION_PLAN.md +999 -993
- package/src/engine/sound/sopra/README.md +644 -643
- package/src/engine/sound/sopra/SopraEngine.d.ts +2 -2
- package/src/engine/sound/sopra/SopraEngine.js +432 -432
- package/src/engine/sound/sopra/legacy/SoundAttenuationFunction.d.ts.map +1 -0
- package/src/engine/sound/{ecs → sopra/legacy}/SoundController.d.ts +3 -3
- package/src/engine/sound/sopra/legacy/SoundController.d.ts.map +1 -0
- package/src/engine/sound/{ecs → sopra/legacy}/SoundController.js +176 -176
- package/src/engine/sound/{ecs/emitter → sopra/legacy}/SoundEmitter.d.ts +11 -27
- package/src/engine/sound/sopra/legacy/SoundEmitter.d.ts.map +1 -0
- package/src/engine/sound/{ecs/emitter → sopra/legacy}/SoundEmitter.js +352 -382
- package/src/engine/sound/sopra/legacy/SoundEmitterFlags.d.ts.map +1 -0
- package/src/engine/sound/sopra/legacy/SoundEmitterSerializationAdapter.d.ts.map +1 -0
- package/src/engine/sound/{ecs/emitter → sopra/legacy}/SoundEmitterSerializationUpgrader_0_1.d.ts.map +1 -1
- package/src/engine/sound/{ecs/emitter → sopra/legacy}/SoundEmitterSerializationUpgrader_1_2.d.ts.map +1 -1
- package/src/engine/sound/sopra/legacy/SoundPanningModelType.d.ts.map +1 -0
- package/src/engine/sound/{ecs/emitter → sopra/legacy}/SoundTrack.d.ts +11 -55
- package/src/engine/sound/sopra/legacy/SoundTrack.d.ts.map +1 -0
- package/src/engine/sound/{ecs/emitter → sopra/legacy}/SoundTrack.js +278 -357
- package/src/engine/sound/sopra/legacy/SoundTrackFlags.d.ts.map +1 -0
- package/src/engine/sound/sopra/legacy/convertLegacySoundComponents.d.ts +34 -0
- package/src/engine/sound/sopra/legacy/convertLegacySoundComponents.d.ts.map +1 -0
- package/src/engine/sound/sopra/legacy/convertLegacySoundComponents.js +279 -0
- package/src/engine/sound/sopra/legacy/soundEmitterToEventDescription.d.ts +3 -3
- package/src/engine/sound/sopra/legacy/soundEmitterToEventDescription.d.ts.map +1 -1
- package/src/engine/sound/sopra/legacy/soundEmitterToEventDescription.js +122 -122
- package/src/shade/device/mock/SoftwareGPUDevice.d.ts.map +1 -1
- package/src/shade/device/mock/SoftwareGPUDevice.js +7 -1
- package/src/shade/device/timing/GPUTimerArray.d.ts.map +1 -1
- package/src/shade/device/timing/GPUTimerArray.js +10 -0
- package/src/shade/playground/add_random_meshes.d.ts +19 -3
- package/src/shade/playground/add_random_meshes.d.ts.map +1 -1
- package/src/shade/playground/add_random_meshes.js +132 -84
- package/src/shade/playground/bvh_repro/README.md +57 -0
- package/src/shade/playground/bvh_repro/bvh_repro_device.d.ts +33 -0
- package/src/shade/playground/bvh_repro/bvh_repro_device.d.ts.map +1 -0
- package/src/shade/playground/bvh_repro/bvh_repro_device.js +90 -0
- package/src/shade/playground/bvh_repro/index.html +124 -0
- package/src/shade/playground/bvh_repro/main.d.ts.map +1 -0
- package/src/shade/playground/bvh_repro/main.js +254 -0
- package/src/shade/playground/load_default_environment.d.ts +10 -1
- package/src/shade/playground/load_default_environment.d.ts.map +1 -1
- package/src/shade/playground/load_default_environment.js +44 -20
- package/src/shade/playground/make_playground_asset_manager.d.ts +16 -0
- package/src/shade/playground/make_playground_asset_manager.d.ts.map +1 -0
- package/src/shade/playground/make_playground_asset_manager.js +33 -0
- package/src/shade/playground/skinned_blas_refit/README.md +103 -0
- package/src/shade/playground/skinned_blas_refit/add_floor.d.ts +47 -0
- package/src/shade/playground/skinned_blas_refit/add_floor.d.ts.map +1 -0
- package/src/shade/playground/skinned_blas_refit/add_floor.js +71 -0
- package/src/shade/playground/skinned_blas_refit/diff_traced_against_rasterized.d.ts +28 -0
- package/src/shade/playground/skinned_blas_refit/diff_traced_against_rasterized.d.ts.map +1 -0
- package/src/shade/playground/skinned_blas_refit/diff_traced_against_rasterized.js +745 -0
- package/src/shade/playground/skinned_blas_refit/flat_environment.d.ts +30 -0
- package/src/shade/playground/skinned_blas_refit/flat_environment.d.ts.map +1 -0
- package/src/shade/playground/skinned_blas_refit/flat_environment.js +67 -0
- package/src/shade/playground/skinned_blas_refit/index.html +168 -0
- package/src/shade/playground/skinned_blas_refit/main.d.ts +8 -0
- package/src/shade/playground/skinned_blas_refit/main.d.ts.map +1 -0
- package/src/shade/playground/skinned_blas_refit/main.js +502 -0
- package/src/shade/playground/skinned_blas_refit/place_character.d.ts +81 -0
- package/src/shade/playground/skinned_blas_refit/place_character.d.ts.map +1 -0
- package/src/shade/playground/skinned_blas_refit/place_character.js +116 -0
- package/src/shade/playground/skinned_blas_refit/shader_blas_audit_leaf_triangles.d.ts +21 -0
- package/src/shade/playground/skinned_blas_refit/shader_blas_audit_leaf_triangles.d.ts.map +1 -0
- package/src/shade/playground/skinned_blas_refit/shader_blas_audit_leaf_triangles.js +104 -0
- package/src/shade/playground/skinned_blas_refit/shader_blas_audit_points.d.ts +23 -0
- package/src/shade/playground/skinned_blas_refit/shader_blas_audit_points.d.ts.map +1 -0
- package/src/shade/playground/skinned_blas_refit/shader_blas_audit_points.js +94 -0
- package/src/shade/playground/skinned_blas_refit/shader_blas_audit_visibility.d.ts +26 -0
- package/src/shade/playground/skinned_blas_refit/shader_blas_audit_visibility.d.ts.map +1 -0
- package/src/shade/playground/skinned_blas_refit/shader_blas_audit_visibility.js +134 -0
- package/src/shade/playground/skinned_blas_refit/sweep_views.d.ts +40 -0
- package/src/shade/playground/skinned_blas_refit/sweep_views.d.ts.map +1 -0
- package/src/shade/playground/skinned_blas_refit/sweep_views.js +135 -0
- package/src/shade/playground/skinned_blas_refit/verify_instance_bounds.d.ts +31 -0
- package/src/shade/playground/skinned_blas_refit/verify_instance_bounds.d.ts.map +1 -0
- package/src/shade/playground/skinned_blas_refit/verify_instance_bounds.js +240 -0
- package/src/shade/playground/skinned_blas_refit/verify_leaf_triangles.d.ts +20 -0
- package/src/shade/playground/skinned_blas_refit/verify_leaf_triangles.d.ts.map +1 -0
- package/src/shade/playground/skinned_blas_refit/verify_leaf_triangles.js +202 -0
- package/src/shade/playground/skinned_blas_refit/verify_skinned_blas_refit.d.ts +50 -0
- package/src/shade/playground/skinned_blas_refit/verify_skinned_blas_refit.d.ts.map +1 -0
- package/src/shade/playground/skinned_blas_refit/verify_skinned_blas_refit.js +366 -0
- package/src/shade/playground/skinned_blas_refit/verify_traversal_reachability.d.ts +17 -0
- package/src/shade/playground/skinned_blas_refit/verify_traversal_reachability.d.ts.map +1 -0
- package/src/shade/playground/skinned_blas_refit/verify_traversal_reachability.js +249 -0
- package/src/shade/playground/sponza_path_trace/README.md +57 -0
- package/src/shade/playground/sponza_path_trace/index.html +164 -0
- package/src/shade/playground/sponza_path_trace/main.d.ts +8 -0
- package/src/shade/playground/sponza_path_trace/main.d.ts.map +1 -0
- package/src/shade/playground/sponza_path_trace/main.js +363 -0
- package/src/shade/renderer/Renderer.d.ts +16 -0
- package/src/shade/renderer/Renderer.d.ts.map +1 -1
- package/src/shade/renderer/Renderer.js +23 -4
- package/src/shade/renderer/animation/GPUAnimationManager.d.ts +5 -1
- package/src/shade/renderer/animation/GPUAnimationManager.d.ts.map +1 -1
- package/src/shade/renderer/animation/GPUAnimationManager.js +28 -3
- package/src/shade/renderer/animation/Skin.d.ts +9 -7
- package/src/shade/renderer/animation/Skin.d.ts.map +1 -1
- package/src/shade/renderer/animation/Skin.js +9 -7
- package/src/shade/renderer/animation/compute_skin_world_bounds.d.ts +35 -0
- package/src/shade/renderer/animation/compute_skin_world_bounds.d.ts.map +1 -0
- package/src/shade/renderer/animation/compute_skin_world_bounds.js +146 -0
- package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.d.ts.map +1 -1
- package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.js +41 -0
- package/src/shade/renderer/geometry/GPUGeometryManager.d.ts.map +1 -1
- package/src/shade/renderer/geometry/GPUGeometryManager.js +65 -15
- package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.d.ts +78 -7
- package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.d.ts.map +1 -1
- package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.js +302 -77
- package/src/shade/renderer/geometry/bvh/GPUGeometryMeshletsBVH.d.ts +18 -6
- package/src/shade/renderer/geometry/bvh/GPUGeometryMeshletsBVH.d.ts.map +1 -1
- package/src/shade/renderer/geometry/bvh/GPUGeometryMeshletsBVH.js +19 -6
- package/src/shade/renderer/geometry/bvh/bvh2_check_bounds.d.ts +51 -0
- package/src/shade/renderer/geometry/bvh/bvh2_check_bounds.d.ts.map +1 -0
- package/src/shade/renderer/geometry/bvh/bvh2_check_bounds.js +260 -0
- package/src/shade/renderer/geometry/bvh/bvh2_derive_topology.d.ts +23 -0
- package/src/shade/renderer/geometry/bvh/bvh2_derive_topology.d.ts.map +1 -0
- package/src/shade/renderer/geometry/bvh/bvh2_derive_topology.js +82 -0
- package/src/shade/renderer/geometry/bvh/record_blas_refit.d.ts +25 -0
- package/src/shade/renderer/geometry/bvh/record_blas_refit.d.ts.map +1 -0
- package/src/shade/renderer/geometry/bvh/record_blas_refit.js +102 -0
- package/src/shade/renderer/geometry/bvh/shader_blas_refit_leaf_bounds.d.ts +30 -0
- package/src/shade/renderer/geometry/bvh/shader_blas_refit_leaf_bounds.d.ts.map +1 -0
- package/src/shade/renderer/geometry/bvh/shader_blas_refit_leaf_bounds.js +122 -0
- package/src/shade/renderer/global_illumination/brick4/cpu/brick4_empty_gpu_structure.d.ts +23 -0
- package/src/shade/renderer/global_illumination/brick4/cpu/brick4_empty_gpu_structure.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/brick4/cpu/brick4_empty_gpu_structure.js +34 -0
- package/src/shade/renderer/gpu_primitive/bvh/BVH_BUFFER_LAYOUT.d.ts +55 -0
- package/src/shade/renderer/gpu_primitive/bvh/BVH_BUFFER_LAYOUT.d.ts.map +1 -0
- package/src/shade/renderer/gpu_primitive/bvh/BVH_BUFFER_LAYOUT.js +70 -0
- package/src/shade/renderer/gpu_primitive/bvh/BVH_BUILD_SETTINGS_STRUCT.d.ts +37 -0
- package/src/shade/renderer/gpu_primitive/bvh/BVH_BUILD_SETTINGS_STRUCT.d.ts.map +1 -0
- package/src/shade/renderer/gpu_primitive/bvh/BVH_BUILD_SETTINGS_STRUCT.js +45 -0
- package/src/shade/renderer/gpu_primitive/bvh/GPU_BVH_BUILD_PLAN.md +1115 -0
- package/src/shade/renderer/gpu_primitive/bvh/PHASE1_HANDOVER.md +201 -0
- package/src/shade/renderer/gpu_primitive/bvh/check_bvh_structure.d.ts +88 -0
- package/src/shade/renderer/gpu_primitive/bvh/check_bvh_structure.d.ts.map +1 -0
- package/src/shade/renderer/gpu_primitive/bvh/check_bvh_structure.js +233 -0
- package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_delta.d.ts +18 -0
- package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_delta.d.ts.map +1 -0
- package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_delta.js +38 -0
- package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_determine_range.d.ts +16 -0
- package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_determine_range.d.ts.map +1 -0
- package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_determine_range.js +79 -0
- package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_find_split.d.ts +16 -0
- package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_find_split.d.ts.map +1 -0
- package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_find_split.js +49 -0
- package/src/shade/renderer/gpu_primitive/bvh/graph_bvh_build_lbvh.d.ts +43 -0
- package/src/shade/renderer/gpu_primitive/bvh/graph_bvh_build_lbvh.d.ts.map +1 -0
- package/src/shade/renderer/gpu_primitive/bvh/graph_bvh_build_lbvh.js +217 -0
- package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_emit_hierarchy.d.ts +24 -0
- package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_emit_hierarchy.d.ts.map +1 -0
- package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_emit_hierarchy.js +143 -0
- package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_morton.d.ts +17 -0
- package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_morton.d.ts.map +1 -0
- package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_morton.js +83 -0
- package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_refit.d.ts +24 -0
- package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_refit.d.ts.map +1 -0
- package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_refit.js +141 -0
- package/src/shade/renderer/gpu_primitive/bvh/verify_gpu_bvh_build.d.ts +31 -0
- package/src/shade/renderer/gpu_primitive/bvh/verify_gpu_bvh_build.d.ts.map +1 -0
- package/src/shade/renderer/gpu_primitive/bvh/verify_gpu_bvh_build.js +422 -0
- package/src/shade/renderer/gpu_primitive/prefix_sum/v1/test_prefix_sum.d.ts.map +1 -1
- package/src/shade/renderer/gpu_primitive/prefix_sum/v1/test_prefix_sum.js +1 -2
- package/src/shade/renderer/gpu_primitive/sort/SORT_PASS_SETTINGS_STRUCT.d.ts +9 -0
- package/src/shade/renderer/gpu_primitive/sort/SORT_PASS_SETTINGS_STRUCT.d.ts.map +1 -0
- package/src/shade/renderer/gpu_primitive/sort/SORT_PASS_SETTINGS_STRUCT.js +23 -0
- package/src/shade/renderer/gpu_primitive/sort/chunk_sort_digit.d.ts +18 -0
- package/src/shade/renderer/gpu_primitive/sort/chunk_sort_digit.d.ts.map +1 -0
- package/src/shade/renderer/gpu_primitive/sort/chunk_sort_digit.js +32 -0
- package/src/shade/renderer/gpu_primitive/sort/chunk_sort_rank.d.ts +27 -0
- package/src/shade/renderer/gpu_primitive/sort/chunk_sort_rank.d.ts.map +1 -0
- package/src/shade/renderer/gpu_primitive/sort/chunk_sort_rank.js +41 -0
- package/src/shade/renderer/gpu_primitive/sort/chunk_sort_workgroup_scan.d.ts +25 -0
- package/src/shade/renderer/gpu_primitive/sort/chunk_sort_workgroup_scan.d.ts.map +1 -0
- package/src/shade/renderer/gpu_primitive/sort/chunk_sort_workgroup_scan.js +74 -0
- package/src/shade/renderer/gpu_primitive/sort/defs.d.ts +71 -0
- package/src/shade/renderer/gpu_primitive/sort/defs.d.ts.map +1 -0
- package/src/shade/renderer/gpu_primitive/sort/defs.js +79 -0
- package/src/shade/renderer/gpu_primitive/sort/graph_radix_sort.d.ts +37 -0
- package/src/shade/renderer/gpu_primitive/sort/graph_radix_sort.d.ts.map +1 -0
- package/src/shade/renderer/gpu_primitive/sort/graph_radix_sort.js +197 -0
- package/src/shade/renderer/gpu_primitive/sort/shader_radix_sort_histogram.d.ts +16 -0
- package/src/shade/renderer/gpu_primitive/sort/shader_radix_sort_histogram.d.ts.map +1 -0
- package/src/shade/renderer/gpu_primitive/sort/shader_radix_sort_histogram.js +85 -0
- package/src/shade/renderer/gpu_primitive/sort/shader_radix_sort_scatter.d.ts +15 -0
- package/src/shade/renderer/gpu_primitive/sort/shader_radix_sort_scatter.d.ts.map +1 -0
- package/src/shade/renderer/gpu_primitive/sort/shader_radix_sort_scatter.js +192 -0
- package/src/shade/renderer/gpu_primitive/sort/verify_radix_sort.d.ts +12 -0
- package/src/shade/renderer/gpu_primitive/sort/verify_radix_sort.d.ts.map +1 -0
- package/src/shade/renderer/gpu_primitive/sort/verify_radix_sort.js +188 -0
- package/src/shade/renderer/scene/GPUInstancesAccelerationStructure.d.ts +16 -4
- package/src/shade/renderer/scene/GPUInstancesAccelerationStructure.d.ts.map +1 -1
- package/src/shade/renderer/scene/GPUInstancesAccelerationStructure.js +235 -24
- package/src/shade/renderer/scene/GPUSceneContext.d.ts.map +1 -1
- package/src/shade/renderer/scene/GPUSceneContext.js +15 -3
- package/src/shade/renderer/scene/SkinnedMesh.d.ts +57 -11
- package/src/shade/renderer/scene/SkinnedMesh.d.ts.map +1 -1
- package/src/shade/renderer/scene/SkinnedMesh.js +111 -11
- package/src/shade/renderer/scene/optimization/geometry/internal/anchor_grow_topological.d.ts.map +1 -1
- package/src/shade/renderer/scene/optimization/geometry/internal/anchor_grow_topological.js +12 -3
- package/src/shade/renderer/scene/optimization/geometry/internal/match_validate_topology.js +3 -3
- package/src/shade/renderer/scene/optimization/geometry/internal/seed_correspondence_from_window.js +3 -3
- package/src/shade/renderer/scene/serialization/deserialize_scene.d.ts.map +1 -1
- package/src/shade/renderer/scene/serialization/deserialize_scene.js +0 -2
- package/src/shade/renderer/scene/shader_bvh_extract_instance_leaves.d.ts +22 -0
- package/src/shade/renderer/scene/shader_bvh_extract_instance_leaves.d.ts.map +1 -0
- package/src/shade/renderer/scene/shader_bvh_extract_instance_leaves.js +145 -0
- package/src/shade/renderer/scene/verify_gpu_tlas_build.d.ts +18 -0
- package/src/shade/renderer/scene/verify_gpu_tlas_build.d.ts.map +1 -0
- package/src/shade/renderer/scene/verify_gpu_tlas_build.js +364 -0
- package/src/shade/renderer/shader/chunk/binary/chunk_split_by_3.d.ts +9 -1
- package/src/shade/renderer/shader/chunk/binary/chunk_split_by_3.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/binary/chunk_split_by_3.js +29 -21
- package/src/shade/renderer/shader/chunk/bvh/morton/chunk_v3_morton_encode.d.ts +22 -0
- package/src/shade/renderer/shader/chunk/bvh/morton/chunk_v3_morton_encode.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/bvh/morton/chunk_v3_morton_encode.js +31 -0
- package/src/shade/renderer/shader/chunk/geometry/aabb3/chunk_aabb3_intersects_ray.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/geometry/aabb3/chunk_aabb3_intersects_ray.js +87 -39
- package/src/shade/renderer/shader/chunk/geometry/format/chunk_geometry_read_triangle_bounds.d.ts +17 -0
- package/src/shade/renderer/shader/chunk/geometry/format/chunk_geometry_read_triangle_bounds.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/geometry/format/chunk_geometry_read_triangle_bounds.js +40 -0
- package/src/shade/renderer/shader/chunk/geometry/meshlet/chunk_read_meshlet_triangle_bounds.d.ts +15 -0
- package/src/shade/renderer/shader/chunk/geometry/meshlet/chunk_read_meshlet_triangle_bounds.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/geometry/meshlet/chunk_read_meshlet_triangle_bounds.js +52 -0
- package/src/shade/renderer/shader/chunk/geometry/ray/RAY_STRUCT.d.ts +9 -0
- package/src/shade/renderer/shader/chunk/geometry/ray/RAY_STRUCT.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/geometry/ray/RAY_STRUCT.js +9 -0
- package/src/shade/renderer/shader/chunk/geometry/ray/chunk_ray_transform_m4.d.ts +20 -0
- package/src/shade/renderer/shader/chunk/geometry/ray/chunk_ray_transform_m4.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/geometry/ray/chunk_ray_transform_m4.js +52 -34
- package/src/shade/renderer/texture/virtual/README.md +255 -276
- package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.d.ts.map +1 -1
- package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +66 -2
- package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
- package/src/shade/wgsl/emulator/WGSLLanguageCore.js +83 -0
- package/src/view/interaction/InterfaceCommand.d.ts +8 -8
- package/src/view/interaction/InterfaceCommand.d.ts.map +1 -1
- package/src/view/interaction/InterfaceCommand.js +146 -146
- package/src/view/interaction/createInterfaceCommandButton.d.ts.map +1 -1
- package/src/view/interaction/createInterfaceCommandButton.js +115 -124
- package/src/engine/ecs/components/Timer.d.ts +0 -50
- package/src/engine/ecs/components/Timer.d.ts.map +0 -1
- package/src/engine/ecs/components/Timer.js +0 -80
- package/src/engine/ecs/evaluation/PointFitnessBinary.d.ts +0 -22
- package/src/engine/ecs/evaluation/PointFitnessBinary.d.ts.map +0 -1
- package/src/engine/ecs/evaluation/PointFitnessBinary.js +0 -33
- package/src/engine/ecs/evaluation/PointFitnessConstant.d.ts +0 -12
- package/src/engine/ecs/evaluation/PointFitnessConstant.d.ts.map +0 -1
- package/src/engine/ecs/evaluation/PointFitnessConstant.js +0 -27
- package/src/engine/ecs/evaluation/PointFitnessFunction.d.ts +0 -17
- package/src/engine/ecs/evaluation/PointFitnessFunction.d.ts.map +0 -1
- package/src/engine/ecs/evaluation/PointFitnessFunction.js +0 -20
- package/src/engine/ecs/evaluation/arithmetic/PointFitnessAdd.d.ts +0 -12
- package/src/engine/ecs/evaluation/arithmetic/PointFitnessAdd.d.ts.map +0 -1
- package/src/engine/ecs/evaluation/arithmetic/PointFitnessAdd.js +0 -27
- package/src/engine/ecs/evaluation/arithmetic/PointFitnessMultiply.d.ts +0 -12
- package/src/engine/ecs/evaluation/arithmetic/PointFitnessMultiply.d.ts.map +0 -1
- package/src/engine/ecs/evaluation/arithmetic/PointFitnessMultiply.js +0 -28
- package/src/engine/ecs/evaluation/complex/PointFitnessGaussianBlur.d.ts +0 -50
- package/src/engine/ecs/evaluation/complex/PointFitnessGaussianBlur.d.ts.map +0 -1
- package/src/engine/ecs/evaluation/complex/PointFitnessGaussianBlur.js +0 -176
- package/src/engine/ecs/evaluation/json/deserializeFromJSON_PointFitnessFunction.d.ts +0 -7
- package/src/engine/ecs/evaluation/json/deserializeFromJSON_PointFitnessFunction.d.ts.map +0 -1
- package/src/engine/ecs/evaluation/json/deserializeFromJSON_PointFitnessFunction.js +0 -68
- package/src/engine/ecs/evaluation/json/serializeToJSON_PointFitnessFunction.d.ts +0 -7
- package/src/engine/ecs/evaluation/json/serializeToJSON_PointFitnessFunction.d.ts.map +0 -1
- package/src/engine/ecs/evaluation/json/serializeToJSON_PointFitnessFunction.js +0 -89
- package/src/engine/ecs/evaluation/world/ReadTerrainLayerWeightPointFitness.d.ts +0 -17
- package/src/engine/ecs/evaluation/world/ReadTerrainLayerWeightPointFitness.d.ts.map +0 -1
- package/src/engine/ecs/evaluation/world/ReadTerrainLayerWeightPointFitness.js +0 -60
- package/src/engine/ecs/systems/TimerSystem.d.ts +0 -10
- package/src/engine/ecs/systems/TimerSystem.d.ts.map +0 -1
- package/src/engine/ecs/systems/TimerSystem.js +0 -86
- package/src/engine/graphics/FULL_SCREEN_QUAD_VERTEX_SHADER.d.ts +0 -13
- package/src/engine/graphics/FULL_SCREEN_QUAD_VERTEX_SHADER.d.ts.map +0 -1
- package/src/engine/graphics/FULL_SCREEN_QUAD_VERTEX_SHADER.js +0 -14
- package/src/engine/graphics/StandardFrameBuffers.d.ts +0 -7
- package/src/engine/graphics/StandardFrameBuffers.d.ts.map +0 -1
- package/src/engine/graphics/StandardFrameBuffers.js +0 -9
- package/src/engine/graphics/copy_transform_to_threejs_object.d.ts +0 -7
- package/src/engine/graphics/copy_transform_to_threejs_object.d.ts.map +0 -1
- package/src/engine/graphics/copy_transform_to_threejs_object.js +0 -12
- package/src/engine/graphics/ecs/light/binding/LightBinding.d.ts +0 -42
- package/src/engine/graphics/ecs/light/binding/LightBinding.d.ts.map +0 -1
- package/src/engine/graphics/ecs/light/binding/LightBinding.js +0 -133
- package/src/engine/graphics/ecs/light/binding/fp/FPLightBinding.d.ts +0 -25
- package/src/engine/graphics/ecs/light/binding/fp/FPLightBinding.d.ts.map +0 -1
- package/src/engine/graphics/ecs/light/binding/fp/FPLightBinding.js +0 -90
- package/src/engine/graphics/ecs/light/binding/three/applyRotation.d.ts +0 -7
- package/src/engine/graphics/ecs/light/binding/three/applyRotation.d.ts.map +0 -1
- package/src/engine/graphics/ecs/light/binding/three/applyRotation.js +0 -24
- package/src/engine/graphics/ecs/light/binding/three/threeEnsureLightObject.d.ts +0 -7
- package/src/engine/graphics/ecs/light/binding/three/threeEnsureLightObject.d.ts.map +0 -1
- package/src/engine/graphics/ecs/light/binding/three/threeEnsureLightObject.js +0 -12
- package/src/engine/graphics/ecs/light/binding/three/threeMakeLight.d.ts +0 -8
- package/src/engine/graphics/ecs/light/binding/three/threeMakeLight.d.ts.map +0 -1
- package/src/engine/graphics/ecs/light/binding/three/threeMakeLight.js +0 -41
- package/src/engine/graphics/ecs/light/shadow/LightShadow.d.ts +0 -4
- package/src/engine/graphics/ecs/light/shadow/LightShadow.d.ts.map +0 -1
- package/src/engine/graphics/ecs/light/shadow/LightShadow.js +0 -4
- package/src/engine/graphics/ecs/light/shadow/ShadowManager.d.ts +0 -27
- package/src/engine/graphics/ecs/light/shadow/ShadowManager.d.ts.map +0 -1
- package/src/engine/graphics/ecs/light/shadow/ShadowManager.js +0 -53
- package/src/engine/graphics/ecs/light/shadow/ShadowMap.d.ts +0 -8
- package/src/engine/graphics/ecs/light/shadow/ShadowMap.d.ts.map +0 -1
- package/src/engine/graphics/ecs/light/shadow/ShadowMap.js +0 -7
- package/src/engine/graphics/ecs/light/shadow/compute_view_frustum_aabb_in_space.d.ts +0 -14
- package/src/engine/graphics/ecs/light/shadow/compute_view_frustum_aabb_in_space.d.ts.map +0 -1
- package/src/engine/graphics/ecs/light/shadow/compute_view_frustum_aabb_in_space.js +0 -62
- package/src/engine/graphics/ecs/light/shadow/extend_shadow_camera_near_for_casters.d.ts +0 -30
- package/src/engine/graphics/ecs/light/shadow/extend_shadow_camera_near_for_casters.d.ts.map +0 -1
- package/src/engine/graphics/ecs/light/shadow/extend_shadow_camera_near_for_casters.js +0 -77
- package/src/engine/graphics/ecs/light/shadow/setShadowCameraDimensionsDiscrete.d.ts +0 -13
- package/src/engine/graphics/ecs/light/shadow/setShadowCameraDimensionsDiscrete.d.ts.map +0 -1
- package/src/engine/graphics/ecs/light/shadow/setShadowCameraDimensionsDiscrete.js +0 -43
- package/src/engine/graphics/ecs/light/three/light2type.d.ts +0 -8
- package/src/engine/graphics/ecs/light/three/light2type.d.ts.map +0 -1
- package/src/engine/graphics/ecs/light/three/light2type.js +0 -20
- package/src/engine/graphics/ecs/mesh/applyTransformToThreeObject.d.ts +0 -7
- package/src/engine/graphics/ecs/mesh/applyTransformToThreeObject.d.ts.map +0 -1
- package/src/engine/graphics/ecs/mesh/applyTransformToThreeObject.js +0 -13
- package/src/engine/graphics/particles/node-based/codegen/CodeContext.d.ts +0 -20
- package/src/engine/graphics/particles/node-based/codegen/CodeContext.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/codegen/CodeContext.js +0 -47
- package/src/engine/graphics/particles/node-based/codegen/CodeGenerator.d.ts +0 -12
- package/src/engine/graphics/particles/node-based/codegen/CodeGenerator.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/codegen/CodeGenerator.js +0 -19
- package/src/engine/graphics/particles/node-based/codegen/glsl/GLSLCodeGenerator.d.ts +0 -18
- package/src/engine/graphics/particles/node-based/codegen/glsl/GLSLCodeGenerator.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/codegen/glsl/GLSLCodeGenerator.js +0 -249
- package/src/engine/graphics/particles/node-based/codegen/glsl/genAttributeInputName.d.ts +0 -7
- package/src/engine/graphics/particles/node-based/codegen/glsl/genAttributeInputName.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/codegen/glsl/genAttributeInputName.js +0 -8
- package/src/engine/graphics/particles/node-based/codegen/glsl/genAttributeOutputName.d.ts +0 -7
- package/src/engine/graphics/particles/node-based/codegen/glsl/genAttributeOutputName.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/codegen/glsl/genAttributeOutputName.js +0 -8
- package/src/engine/graphics/particles/node-based/codegen/glsl/getTypeByteSize.d.ts +0 -14
- package/src/engine/graphics/particles/node-based/codegen/glsl/getTypeByteSize.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/codegen/glsl/getTypeByteSize.js +0 -20
- package/src/engine/graphics/particles/node-based/codegen/glsl/modules/noise/curl/glslm_curlNoise.d.ts +0 -3
- package/src/engine/graphics/particles/node-based/codegen/glsl/modules/noise/curl/glslm_curlNoise.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/codegen/glsl/modules/noise/curl/glslm_curlNoise.js +0 -58
- package/src/engine/graphics/particles/node-based/codegen/glsl/modules/noise/curl/glslm_snoiseVector3.d.ts +0 -3
- package/src/engine/graphics/particles/node-based/codegen/glsl/modules/noise/curl/glslm_snoiseVector3.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/codegen/glsl/modules/noise/curl/glslm_snoiseVector3.js +0 -44
- package/src/engine/graphics/particles/node-based/codegen/glsl/modules/noise/simplex/3d/glslm_mod289_v3.d.ts +0 -3
- package/src/engine/graphics/particles/node-based/codegen/glsl/modules/noise/simplex/3d/glslm_mod289_v3.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/codegen/glsl/modules/noise/simplex/3d/glslm_mod289_v3.js +0 -28
- package/src/engine/graphics/particles/node-based/codegen/glsl/modules/noise/simplex/3d/glslm_mod289_v4.d.ts +0 -3
- package/src/engine/graphics/particles/node-based/codegen/glsl/modules/noise/simplex/3d/glslm_mod289_v4.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/codegen/glsl/modules/noise/simplex/3d/glslm_mod289_v4.js +0 -28
- package/src/engine/graphics/particles/node-based/codegen/glsl/modules/noise/simplex/3d/glslm_permute.d.ts +0 -3
- package/src/engine/graphics/particles/node-based/codegen/glsl/modules/noise/simplex/3d/glslm_permute.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/codegen/glsl/modules/noise/simplex/3d/glslm_permute.js +0 -39
- package/src/engine/graphics/particles/node-based/codegen/glsl/modules/noise/simplex/3d/glslm_snoise.d.ts +0 -3
- package/src/engine/graphics/particles/node-based/codegen/glsl/modules/noise/simplex/3d/glslm_snoise.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/codegen/glsl/modules/noise/simplex/3d/glslm_snoise.js +0 -124
- package/src/engine/graphics/particles/node-based/codegen/glsl/modules/noise/simplex/3d/glslm_tylorInvSqrt.d.ts +0 -3
- package/src/engine/graphics/particles/node-based/codegen/glsl/modules/noise/simplex/3d/glslm_tylorInvSqrt.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/codegen/glsl/modules/noise/simplex/3d/glslm_tylorInvSqrt.js +0 -30
- package/src/engine/graphics/particles/node-based/codegen/modules/FunctionModule.d.ts +0 -64
- package/src/engine/graphics/particles/node-based/codegen/modules/FunctionModule.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/codegen/modules/FunctionModule.js +0 -131
- package/src/engine/graphics/particles/node-based/codegen/modules/FunctionModuleReference.d.ts +0 -32
- package/src/engine/graphics/particles/node-based/codegen/modules/FunctionModuleReference.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/codegen/modules/FunctionModuleReference.js +0 -62
- package/src/engine/graphics/particles/node-based/codegen/modules/FunctionModuleRegistry.d.ts +0 -46
- package/src/engine/graphics/particles/node-based/codegen/modules/FunctionModuleRegistry.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/codegen/modules/FunctionModuleRegistry.js +0 -272
- package/src/engine/graphics/particles/node-based/codegen/modules/FunctionParameterSpecification.d.ts +0 -28
- package/src/engine/graphics/particles/node-based/codegen/modules/FunctionParameterSpecification.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/codegen/modules/FunctionParameterSpecification.js +0 -57
- package/src/engine/graphics/particles/node-based/codegen/modules/FunctionSignature.d.ts +0 -32
- package/src/engine/graphics/particles/node-based/codegen/modules/FunctionSignature.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/codegen/modules/FunctionSignature.js +0 -55
- package/src/engine/graphics/particles/node-based/editor/ParticleSpecificationEditorView.d.ts +0 -34
- package/src/engine/graphics/particles/node-based/editor/ParticleSpecificationEditorView.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/editor/ParticleSpecificationEditorView.js +0 -178
- package/src/engine/graphics/particles/node-based/nodes/FloatConstant.d.ts +0 -7
- package/src/engine/graphics/particles/node-based/nodes/FloatConstant.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/nodes/FloatConstant.js +0 -31
- package/src/engine/graphics/particles/node-based/nodes/ParticleDataTypes.d.ts +0 -13
- package/src/engine/graphics/particles/node-based/nodes/ParticleDataTypes.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/nodes/ParticleDataTypes.js +0 -13
- package/src/engine/graphics/particles/node-based/nodes/ReadFloatUniform.d.ts +0 -7
- package/src/engine/graphics/particles/node-based/nodes/ReadFloatUniform.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/nodes/ReadFloatUniform.js +0 -26
- package/src/engine/graphics/particles/node-based/nodes/ReadPosition.d.ts +0 -4
- package/src/engine/graphics/particles/node-based/nodes/ReadPosition.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/nodes/ReadPosition.js +0 -5
- package/src/engine/graphics/particles/node-based/nodes/ReadVelocity.d.ts +0 -6
- package/src/engine/graphics/particles/node-based/nodes/ReadVelocity.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/nodes/ReadVelocity.js +0 -14
- package/src/engine/graphics/particles/node-based/nodes/ShaderNode.d.ts +0 -37
- package/src/engine/graphics/particles/node-based/nodes/ShaderNode.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/nodes/ShaderNode.js +0 -73
- package/src/engine/graphics/particles/node-based/nodes/Vector3Constant.d.ts +0 -7
- package/src/engine/graphics/particles/node-based/nodes/Vector3Constant.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/nodes/Vector3Constant.js +0 -30
- package/src/engine/graphics/particles/node-based/nodes/Vector3Merge.d.ts +0 -7
- package/src/engine/graphics/particles/node-based/nodes/Vector3Merge.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/nodes/Vector3Merge.js +0 -30
- package/src/engine/graphics/particles/node-based/nodes/Vector3Split.d.ts +0 -7
- package/src/engine/graphics/particles/node-based/nodes/Vector3Split.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/nodes/Vector3Split.js +0 -32
- package/src/engine/graphics/particles/node-based/nodes/WriteVelocity.d.ts +0 -6
- package/src/engine/graphics/particles/node-based/nodes/WriteVelocity.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/nodes/WriteVelocity.js +0 -15
- package/src/engine/graphics/particles/node-based/nodes/attribute/ReadVector3Attribute.d.ts +0 -7
- package/src/engine/graphics/particles/node-based/nodes/attribute/ReadVector3Attribute.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/nodes/attribute/ReadVector3Attribute.js +0 -27
- package/src/engine/graphics/particles/node-based/nodes/attribute/WriteVector3Attribute.d.ts +0 -7
- package/src/engine/graphics/particles/node-based/nodes/attribute/WriteVector3Attribute.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/nodes/attribute/WriteVector3Attribute.js +0 -25
- package/src/engine/graphics/particles/node-based/nodes/math/AddFloatNode.d.ts +0 -7
- package/src/engine/graphics/particles/node-based/nodes/math/AddFloatNode.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/nodes/math/AddFloatNode.js +0 -26
- package/src/engine/graphics/particles/node-based/nodes/math/Vector3Add.d.ts +0 -7
- package/src/engine/graphics/particles/node-based/nodes/math/Vector3Add.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/nodes/math/Vector3Add.js +0 -25
- package/src/engine/graphics/particles/node-based/nodes/math/Vector3Divide.d.ts +0 -7
- package/src/engine/graphics/particles/node-based/nodes/math/Vector3Divide.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/nodes/math/Vector3Divide.js +0 -25
- package/src/engine/graphics/particles/node-based/nodes/math/Vector3Multiply.d.ts +0 -7
- package/src/engine/graphics/particles/node-based/nodes/math/Vector3Multiply.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/nodes/math/Vector3Multiply.js +0 -25
- package/src/engine/graphics/particles/node-based/nodes/math/Vector3Subtract.d.ts +0 -7
- package/src/engine/graphics/particles/node-based/nodes/math/Vector3Subtract.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/nodes/math/Vector3Subtract.js +0 -25
- package/src/engine/graphics/particles/node-based/nodes/noise/CurlNoiseNode.d.ts +0 -7
- package/src/engine/graphics/particles/node-based/nodes/noise/CurlNoiseNode.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/nodes/noise/CurlNoiseNode.js +0 -39
- package/src/engine/graphics/particles/node-based/nodes/particle_data_type_component_count.d.ts +0 -39
- package/src/engine/graphics/particles/node-based/nodes/particle_data_type_component_count.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/nodes/particle_data_type_component_count.js +0 -45
- package/src/engine/graphics/particles/node-based/particle/GeometryAttributes.d.ts +0 -9
- package/src/engine/graphics/particles/node-based/particle/GeometryAttributes.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/particle/GeometryAttributes.js +0 -10
- package/src/engine/graphics/particles/node-based/particle/ParticleAttributeBinding.d.ts +0 -13
- package/src/engine/graphics/particles/node-based/particle/ParticleAttributeBinding.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/particle/ParticleAttributeBinding.js +0 -14
- package/src/engine/graphics/particles/node-based/particle/ParticleAttributeSpecification.d.ts +0 -50
- package/src/engine/graphics/particles/node-based/particle/ParticleAttributeSpecification.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/particle/ParticleAttributeSpecification.js +0 -47
- package/src/engine/graphics/particles/node-based/particle/ParticleSpecification.d.ts +0 -51
- package/src/engine/graphics/particles/node-based/particle/ParticleSpecification.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/particle/ParticleSpecification.js +0 -93
- package/src/engine/graphics/particles/node-based/populateFunctionModuleRegistry.d.ts +0 -6
- package/src/engine/graphics/particles/node-based/populateFunctionModuleRegistry.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/populateFunctionModuleRegistry.js +0 -23
- package/src/engine/graphics/particles/node-based/populateNodeRegistry.d.ts +0 -7
- package/src/engine/graphics/particles/node-based/populateNodeRegistry.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/populateNodeRegistry.js +0 -48
- package/src/engine/graphics/particles/node-based/rendering/AbstractParticleRenderer.d.ts +0 -21
- package/src/engine/graphics/particles/node-based/rendering/AbstractParticleRenderer.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/rendering/AbstractParticleRenderer.js +0 -35
- package/src/engine/graphics/particles/node-based/rendering/ParticleRenderCommand.d.ts +0 -20
- package/src/engine/graphics/particles/node-based/rendering/ParticleRenderCommand.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/rendering/ParticleRenderCommand.js +0 -35
- package/src/engine/graphics/particles/node-based/rendering/ParticleRenderSpecification.d.ts +0 -14
- package/src/engine/graphics/particles/node-based/rendering/ParticleRenderSpecification.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/rendering/ParticleRenderSpecification.js +0 -17
- package/src/engine/graphics/particles/node-based/rendering/billboard/ParticleBillboardShader.d.ts +0 -5
- package/src/engine/graphics/particles/node-based/rendering/billboard/ParticleBillboardShader.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/rendering/billboard/ParticleBillboardShader.js +0 -150
- package/src/engine/graphics/particles/node-based/simulation/EmitterAttributeData.d.ts +0 -55
- package/src/engine/graphics/particles/node-based/simulation/EmitterAttributeData.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/simulation/EmitterAttributeData.js +0 -105
- package/src/engine/graphics/particles/node-based/simulation/GLDataBuffer.d.ts +0 -49
- package/src/engine/graphics/particles/node-based/simulation/GLDataBuffer.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/simulation/GLDataBuffer.js +0 -103
- package/src/engine/graphics/particles/node-based/simulation/GLSLParticleSimulator.d.ts +0 -4
- package/src/engine/graphics/particles/node-based/simulation/GLSLParticleSimulator.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/simulation/GLSLParticleSimulator.js +0 -6
- package/src/engine/graphics/particles/node-based/simulation/GLSLSimulationShader.d.ts +0 -83
- package/src/engine/graphics/particles/node-based/simulation/GLSLSimulationShader.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/simulation/GLSLSimulationShader.js +0 -188
- package/src/engine/graphics/particles/node-based/simulation/TransformFeedback.d.ts +0 -123
- package/src/engine/graphics/particles/node-based/simulation/TransformFeedback.d.ts.map +0 -1
- package/src/engine/graphics/particles/node-based/simulation/TransformFeedback.js +0 -415
- package/src/engine/graphics/render/buffer/FrameBufferManager.d.ts +0 -71
- package/src/engine/graphics/render/buffer/FrameBufferManager.d.ts.map +0 -1
- package/src/engine/graphics/render/buffer/FrameBufferManager.js +0 -229
- package/src/engine/graphics/render/buffer/simple-fx/taa/TemporalSupersamplingRenderPlugin.d.ts +0 -22
- package/src/engine/graphics/render/buffer/simple-fx/taa/TemporalSupersamplingRenderPlugin.d.ts.map +0 -1
- package/src/engine/graphics/render/buffer/simple-fx/taa/TemporalSupersamplingRenderPlugin.js +0 -84
- package/src/engine/graphics/render/forward_plus/LightRenderMetadata.d.ts +0 -33
- package/src/engine/graphics/render/forward_plus/LightRenderMetadata.d.ts.map +0 -1
- package/src/engine/graphics/render/forward_plus/LightRenderMetadata.js +0 -61
- package/src/engine/graphics/render/forward_plus/SPECIFICATION.md +0 -155
- package/src/engine/graphics/render/forward_plus/assign_cluster.d.ts +0 -29
- package/src/engine/graphics/render/forward_plus/assign_cluster.d.ts.map +0 -1
- package/src/engine/graphics/render/forward_plus/assign_cluster.js +0 -124
- package/src/engine/graphics/render/forward_plus/cluster/compute_light_data_hash.d.ts +0 -7
- package/src/engine/graphics/render/forward_plus/cluster/compute_light_data_hash.d.ts.map +0 -1
- package/src/engine/graphics/render/forward_plus/cluster/compute_light_data_hash.js +0 -17
- package/src/engine/graphics/render/forward_plus/cluster/compute_light_data_hash_0.d.ts +0 -7
- package/src/engine/graphics/render/forward_plus/cluster/compute_light_data_hash_0.d.ts.map +0 -1
- package/src/engine/graphics/render/forward_plus/cluster/compute_light_data_hash_0.js +0 -17
- package/src/engine/graphics/render/forward_plus/cluster/read_plane_pair.d.ts +0 -9
- package/src/engine/graphics/render/forward_plus/cluster/read_plane_pair.d.ts.map +0 -1
- package/src/engine/graphics/render/forward_plus/cluster/read_plane_pair.js +0 -29
- package/src/engine/graphics/render/forward_plus/cluster/write_cluster_planes.d.ts +0 -12
- package/src/engine/graphics/render/forward_plus/cluster/write_cluster_planes.d.ts.map +0 -1
- package/src/engine/graphics/render/forward_plus/cluster/write_cluster_planes.js +0 -43
- package/src/engine/graphics/render/forward_plus/computeFrustumCorners.d.ts +0 -8
- package/src/engine/graphics/render/forward_plus/computeFrustumCorners.d.ts.map +0 -1
- package/src/engine/graphics/render/forward_plus/computeFrustumCorners.js +0 -68
- package/src/engine/graphics/render/forward_plus/debug/createLayerSwitcher.d.ts +0 -4
- package/src/engine/graphics/render/forward_plus/debug/createLayerSwitcher.d.ts.map +0 -1
- package/src/engine/graphics/render/forward_plus/debug/createLayerSwitcher.js +0 -46
- package/src/engine/graphics/render/forward_plus/debug/createScreenGrid.d.ts +0 -14
- package/src/engine/graphics/render/forward_plus/debug/createScreenGrid.d.ts.map +0 -1
- package/src/engine/graphics/render/forward_plus/debug/createScreenGrid.js +0 -327
- package/src/engine/graphics/render/forward_plus/materials/FP_INJECTION_POINT_ACCUMULATION.d.ts +0 -2
- package/src/engine/graphics/render/forward_plus/materials/FP_INJECTION_POINT_ACCUMULATION.d.ts.map +0 -1
- package/src/engine/graphics/render/forward_plus/materials/FP_INJECTION_POINT_ACCUMULATION.js +0 -1
- package/src/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_ACCUMULATION.d.ts +0 -2
- package/src/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_ACCUMULATION.d.ts.map +0 -1
- package/src/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_ACCUMULATION.js +0 -40
- package/src/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_APPLY_DECALS.d.ts +0 -2
- package/src/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_APPLY_DECALS.d.ts.map +0 -1
- package/src/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_APPLY_DECALS.js +0 -74
- package/src/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_DECODE_PARS.d.ts +0 -2
- package/src/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_DECODE_PARS.d.ts.map +0 -1
- package/src/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_DECODE_PARS.js +0 -55
- package/src/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_LOAD_METADATA.d.ts +0 -2
- package/src/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_LOAD_METADATA.d.ts.map +0 -1
- package/src/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_LOAD_METADATA.js +0 -11
- package/src/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_PREAMBLE.d.ts +0 -2
- package/src/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_PREAMBLE.d.ts.map +0 -1
- package/src/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_PREAMBLE.js +0 -34
- package/src/engine/graphics/render/forward_plus/materials/fp_build_fragment_shader.d.ts +0 -7
- package/src/engine/graphics/render/forward_plus/materials/fp_build_fragment_shader.d.ts.map +0 -1
- package/src/engine/graphics/render/forward_plus/materials/fp_build_fragment_shader.js +0 -60
- package/src/engine/graphics/render/forward_plus/materials/fp_build_vertex_lighting_shared.d.ts +0 -7
- package/src/engine/graphics/render/forward_plus/materials/fp_build_vertex_lighting_shared.d.ts.map +0 -1
- package/src/engine/graphics/render/forward_plus/materials/fp_build_vertex_lighting_shared.js +0 -38
- package/src/engine/graphics/render/forward_plus/materials/fp_build_vertex_shader.d.ts +0 -7
- package/src/engine/graphics/render/forward_plus/materials/fp_build_vertex_shader.d.ts.map +0 -1
- package/src/engine/graphics/render/forward_plus/materials/fp_build_vertex_shader.js +0 -30
- package/src/engine/graphics/render/forward_plus/model/Decal.d.ts +0 -51
- package/src/engine/graphics/render/forward_plus/model/Decal.d.ts.map +0 -1
- package/src/engine/graphics/render/forward_plus/model/Decal.js +0 -133
- package/src/engine/graphics/render/forward_plus/plugin/forwardPlusMaterialMarker.d.ts +0 -16
- package/src/engine/graphics/render/forward_plus/plugin/forwardPlusMaterialMarker.d.ts.map +0 -1
- package/src/engine/graphics/render/forward_plus/plugin/forwardPlusMaterialMarker.js +0 -18
- package/src/engine/graphics/render/forward_plus/plugin/isLitMaterial.d.ts +0 -7
- package/src/engine/graphics/render/forward_plus/plugin/isLitMaterial.d.ts.map +0 -1
- package/src/engine/graphics/render/forward_plus/plugin/isLitMaterial.js +0 -8
- package/src/engine/graphics/render/forward_plus/query/analytical_biplanar_corner_mapping.d.ts +0 -10
- package/src/engine/graphics/render/forward_plus/query/analytical_biplanar_corner_mapping.d.ts.map +0 -1
- package/src/engine/graphics/render/forward_plus/query/analytical_biplanar_corner_mapping.js +0 -65
- package/src/engine/graphics/render/forward_plus/query/analytical_triplanar_corner_mapping.d.ts +0 -7
- package/src/engine/graphics/render/forward_plus/query/analytical_triplanar_corner_mapping.d.ts.map +0 -1
- package/src/engine/graphics/render/forward_plus/query/analytical_triplanar_corner_mapping.js +0 -8
- package/src/engine/graphics/render/forward_plus/query/cube_mapping.d.ts +0 -11
- package/src/engine/graphics/render/forward_plus/query/cube_mapping.d.ts.map +0 -1
- package/src/engine/graphics/render/forward_plus/query/cube_mapping.js +0 -38
- package/src/engine/graphics/render/forward_plus/query/detailed_sphere_frustum_intersection_test.d.ts +0 -21
- package/src/engine/graphics/render/forward_plus/query/detailed_sphere_frustum_intersection_test.d.ts.map +0 -1
- package/src/engine/graphics/render/forward_plus/query/detailed_sphere_frustum_intersection_test.js +0 -96
- package/src/engine/graphics/render/forward_plus/query/point_light_inside_volume.d.ts +0 -22
- package/src/engine/graphics/render/forward_plus/query/point_light_inside_volume.d.ts.map +0 -1
- package/src/engine/graphics/render/forward_plus/query/point_light_inside_volume.js +0 -44
- package/src/engine/graphics/render/forward_plus/query/query_bvh_frustum_from_texture.d.ts +0 -11
- package/src/engine/graphics/render/forward_plus/query/query_bvh_frustum_from_texture.d.ts.map +0 -1
- package/src/engine/graphics/render/forward_plus/query/query_bvh_frustum_from_texture.js +0 -169
- package/src/engine/graphics/render/forward_plus/query/spot_light_inside_volume.d.ts +0 -9
- package/src/engine/graphics/render/forward_plus/query/spot_light_inside_volume.d.ts.map +0 -1
- package/src/engine/graphics/render/forward_plus/query/spot_light_inside_volume.js +0 -33
- package/src/engine/graphics/render/forward_plus/read_frustum_corner.d.ts +0 -2
- package/src/engine/graphics/render/forward_plus/read_frustum_corner.d.ts.map +0 -1
- package/src/engine/graphics/render/forward_plus/read_frustum_corner.js +0 -2
- package/src/engine/graphics/render/forward_plus/sort_decal_data.d.ts +0 -9
- package/src/engine/graphics/render/forward_plus/sort_decal_data.d.ts.map +0 -1
- package/src/engine/graphics/render/forward_plus/sort_decal_data.js +0 -102
- package/src/engine/graphics/render/forward_plus/testClusterEquality.d.ts +0 -10
- package/src/engine/graphics/render/forward_plus/testClusterEquality.d.ts.map +0 -1
- package/src/engine/graphics/render/forward_plus/testClusterEquality.js +0 -22
- package/src/engine/graphics/render/frame_graph/webgl/WebGLRenderContext.d.ts +0 -19
- package/src/engine/graphics/render/frame_graph/webgl/WebGLRenderContext.d.ts.map +0 -1
- package/src/engine/graphics/render/frame_graph/webgl/WebGLRenderContext.js +0 -62
- package/src/engine/graphics/shaders/AlphaBlendShader.d.ts +0 -18
- package/src/engine/graphics/shaders/AlphaBlendShader.d.ts.map +0 -1
- package/src/engine/graphics/shaders/AlphaBlendShader.js +0 -42
- package/src/engine/graphics/shaders/ClearShader.d.ts +0 -8
- package/src/engine/graphics/shaders/ClearShader.d.ts.map +0 -1
- package/src/engine/graphics/shaders/ClearShader.js +0 -28
- package/src/engine/graphics/shaders/ScreenSpaceQuadShader.d.ts +0 -13
- package/src/engine/graphics/shaders/ScreenSpaceQuadShader.d.ts.map +0 -1
- package/src/engine/graphics/shaders/ScreenSpaceQuadShader.js +0 -38
- package/src/engine/graphics/shaders/ScreenSpaceShader.d.ts +0 -7
- package/src/engine/graphics/shaders/ScreenSpaceShader.d.ts.map +0 -1
- package/src/engine/graphics/shaders/ScreenSpaceShader.js +0 -102
- package/src/engine/graphics/shaders/chunks/glsl_octahedral_impostor.d.ts +0 -102
- package/src/engine/graphics/shaders/chunks/glsl_octahedral_impostor.d.ts.map +0 -1
- package/src/engine/graphics/shaders/chunks/glsl_octahedral_impostor.js +0 -277
- package/src/engine/graphics/shaders/chunks/glsl_voxel_impostor_splat.d.ts +0 -40
- package/src/engine/graphics/shaders/chunks/glsl_voxel_impostor_splat.d.ts.map +0 -1
- package/src/engine/graphics/shaders/chunks/glsl_voxel_impostor_splat.js +0 -57
- package/src/engine/graphics/shaders/glsl_gen_swizzled_read.d.ts +0 -8
- package/src/engine/graphics/shaders/glsl_gen_swizzled_read.d.ts.map +0 -1
- package/src/engine/graphics/shaders/glsl_gen_swizzled_read.js +0 -39
- package/src/engine/graphics/shadows/README.md +0 -6
- package/src/engine/graphics/shadows/vsm.glsl.d.ts +0 -3
- package/src/engine/graphics/shadows/vsm.glsl.d.ts.map +0 -1
- package/src/engine/graphics/shadows/vsm.glsl.js +0 -43
- package/src/engine/graphics/util/composeMatrix4.d.ts +0 -9
- package/src/engine/graphics/util/composeMatrix4.d.ts.map +0 -1
- package/src/engine/graphics/util/composeMatrix4.js +0 -13
- package/src/engine/graphics/util/threeUpdateMatrix.d.ts +0 -6
- package/src/engine/graphics/util/threeUpdateMatrix.d.ts.map +0 -1
- package/src/engine/graphics/util/threeUpdateMatrix.js +0 -14
- package/src/engine/graphics/util/threeUpdateTransform.d.ts +0 -6
- package/src/engine/graphics/util/threeUpdateTransform.d.ts.map +0 -1
- package/src/engine/graphics/util/threeUpdateTransform.js +0 -16
- package/src/engine/sound/ecs/SoundController.d.ts.map +0 -1
- package/src/engine/sound/ecs/SoundControllerSystem.d.ts +0 -23
- package/src/engine/sound/ecs/SoundControllerSystem.d.ts.map +0 -1
- package/src/engine/sound/ecs/SoundControllerSystem.js +0 -103
- package/src/engine/sound/ecs/emitter/SoundAttenuationFunction.d.ts.map +0 -1
- package/src/engine/sound/ecs/emitter/SoundEmitter.d.ts.map +0 -1
- package/src/engine/sound/ecs/emitter/SoundEmitterComponentContext.d.ts +0 -55
- package/src/engine/sound/ecs/emitter/SoundEmitterComponentContext.d.ts.map +0 -1
- package/src/engine/sound/ecs/emitter/SoundEmitterComponentContext.js +0 -265
- package/src/engine/sound/ecs/emitter/SoundEmitterFlags.d.ts.map +0 -1
- package/src/engine/sound/ecs/emitter/SoundEmitterSerializationAdapter.d.ts.map +0 -1
- package/src/engine/sound/ecs/emitter/SoundEmitterSystem.d.ts +0 -88
- package/src/engine/sound/ecs/emitter/SoundEmitterSystem.d.ts.map +0 -1
- package/src/engine/sound/ecs/emitter/SoundEmitterSystem.js +0 -159
- package/src/engine/sound/ecs/emitter/SoundPanningModelType.d.ts.map +0 -1
- package/src/engine/sound/ecs/emitter/SoundTrack.d.ts.map +0 -1
- package/src/engine/sound/ecs/emitter/SoundTrackFlags.d.ts.map +0 -1
- package/src/engine/sound/ecs/emitter/loadSoundTrackAsset.d.ts +0 -8
- package/src/engine/sound/ecs/emitter/loadSoundTrackAsset.d.ts.map +0 -1
- package/src/engine/sound/ecs/emitter/loadSoundTrackAsset.js +0 -41
- package/src/shade/playground/FramePerformanceMonitor.d.ts +0 -114
- package/src/shade/playground/FramePerformanceMonitor.d.ts.map +0 -1
- package/src/shade/playground/FramePerformanceMonitor.js +0 -468
- package/src/shade/playground/add_random_lights.d.ts +0 -8
- package/src/shade/playground/add_random_lights.d.ts.map +0 -1
- package/src/shade/playground/add_random_lights.js +0 -50
- package/src/shade/playground/animation_prototype.d.ts +0 -24
- package/src/shade/playground/animation_prototype.d.ts.map +0 -1
- package/src/shade/playground/animation_prototype.js +0 -508
- package/src/shade/playground/animation_prototype_skinning.d.ts +0 -31
- package/src/shade/playground/animation_prototype_skinning.d.ts.map +0 -1
- package/src/shade/playground/animation_prototype_skinning.js +0 -647
- package/src/shade/playground/animation_prototype_stress.d.ts +0 -35
- package/src/shade/playground/animation_prototype_stress.d.ts.map +0 -1
- package/src/shade/playground/animation_prototype_stress.js +0 -765
- package/src/shade/playground/enable_picker.d.ts +0 -8
- package/src/shade/playground/enable_picker.d.ts.map +0 -1
- package/src/shade/playground/enable_picker.js +0 -60
- package/src/shade/playground/gltf_skinned_prototype.d.ts +0 -27
- package/src/shade/playground/gltf_skinned_prototype.d.ts.map +0 -1
- package/src/shade/playground/gltf_skinned_prototype.js +0 -560
- package/src/shade/playground/gltf_skinned_stress_prototype.d.ts +0 -24
- package/src/shade/playground/gltf_skinned_stress_prototype.d.ts.map +0 -1
- package/src/shade/playground/gltf_skinned_stress_prototype.js +0 -387
- package/src/shade/playground/gltf_viewer.d.ts +0 -2
- package/src/shade/playground/gltf_viewer.d.ts.map +0 -1
- package/src/shade/playground/gltf_viewer.js +0 -283
- package/src/shade/playground/main.d.ts.map +0 -1
- package/src/shade/playground/main.js +0 -2727
- package/src/shade/playground/make_animated_instances.d.ts +0 -9
- package/src/shade/playground/make_animated_instances.d.ts.map +0 -1
- package/src/shade/playground/make_animated_instances.js +0 -118
- package/src/shade/playground/prototype_virtual_texture.d.ts +0 -2
- package/src/shade/playground/prototype_virtual_texture.d.ts.map +0 -1
- package/src/shade/playground/prototype_virtual_texture.js +0 -274
- package/src/shade/playground/prototype_volumetrics_01.d.ts +0 -2
- package/src/shade/playground/prototype_volumetrics_01.d.ts.map +0 -1
- package/src/shade/playground/prototype_volumetrics_01.js +0 -206
- package/src/shade/playground/sample.d.ts +0 -2
- package/src/shade/playground/sample.d.ts.map +0 -1
- package/src/shade/playground/sample.js +0 -234
- package/src/shade/playground/sample_load_gltf.d.ts +0 -9
- package/src/shade/playground/sample_load_gltf.d.ts.map +0 -1
- package/src/shade/playground/sample_load_gltf.js +0 -103
- package/src/shade/playground/spread_node3d_grid.d.ts +0 -7
- package/src/shade/playground/spread_node3d_grid.d.ts.map +0 -1
- package/src/shade/playground/spread_node3d_grid.js +0 -20
- package/src/shade/renderer/geometry/bvh/GPUGeometryBVH.d.ts +0 -24
- package/src/shade/renderer/geometry/bvh/GPUGeometryBVH.d.ts.map +0 -1
- package/src/shade/renderer/geometry/bvh/GPUGeometryBVH.js +0 -88
- package/src/shade/renderer/gpu_primitive/prefix_sum/v0/shader_exclusive_prefix_scan_add.d.ts +0 -3
- package/src/shade/renderer/gpu_primitive/prefix_sum/v0/shader_exclusive_prefix_scan_add.d.ts.map +0 -1
- package/src/shade/renderer/gpu_primitive/prefix_sum/v0/shader_exclusive_prefix_scan_add.js +0 -49
- package/src/shade/renderer/gpu_primitive/prefix_sum/v0/shader_exclusive_prefix_sum_scan.d.ts +0 -3
- package/src/shade/renderer/gpu_primitive/prefix_sum/v0/shader_exclusive_prefix_sum_scan.d.ts.map +0 -1
- package/src/shade/renderer/gpu_primitive/prefix_sum/v0/shader_exclusive_prefix_sum_scan.js +0 -93
- package/src/shade/renderer/gpu_primitive/prefix_sum/v1/graph_prefix_scan_csdl.d.ts +0 -24
- package/src/shade/renderer/gpu_primitive/prefix_sum/v1/graph_prefix_scan_csdl.d.ts.map +0 -1
- package/src/shade/renderer/gpu_primitive/prefix_sum/v1/graph_prefix_scan_csdl.js +0 -119
- package/src/shade/renderer/gpu_primitive/prefix_sum/v1/shader_one_sweep_prefix_scan.d.ts +0 -7
- package/src/shade/renderer/gpu_primitive/prefix_sum/v1/shader_one_sweep_prefix_scan.d.ts.map +0 -1
- package/src/shade/renderer/gpu_primitive/prefix_sum/v1/shader_one_sweep_prefix_scan.js +0 -235
- package/src/shade/renderer/gpu_primitive/sort/NOTES.md +0 -5
- package/src/shade/renderer/gpu_primitive/sort/chunk_sort_common.d.ts +0 -7
- package/src/shade/renderer/gpu_primitive/sort/chunk_sort_common.d.ts.map +0 -1
- package/src/shade/renderer/gpu_primitive/sort/chunk_sort_common.js +0 -136
- package/src/shade/renderer/gpu_primitive/sort/ffx/chunk_safe_load_uint4_umax.d.ts +0 -18
- package/src/shade/renderer/gpu_primitive/sort/ffx/chunk_safe_load_uint4_umax.d.ts.map +0 -1
- package/src/shade/renderer/gpu_primitive/sort/ffx/chunk_safe_load_uint4_umax.js +0 -108
- package/src/shade/renderer/gpu_primitive/sort/ffx/defs.d.ts +0 -35
- package/src/shade/renderer/gpu_primitive/sort/ffx/defs.d.ts.map +0 -1
- package/src/shade/renderer/gpu_primitive/sort/ffx/defs.js +0 -42
- package/src/shade/renderer/gpu_primitive/sort/ffx/graph_radix_sort.d.ts +0 -13
- package/src/shade/renderer/gpu_primitive/sort/ffx/graph_radix_sort.d.ts.map +0 -1
- package/src/shade/renderer/gpu_primitive/sort/ffx/graph_radix_sort.js +0 -116
- package/src/shade/renderer/gpu_primitive/sort/ffx/shader_bit_histogram.d.ts +0 -3
- package/src/shade/renderer/gpu_primitive/sort/ffx/shader_bit_histogram.d.ts.map +0 -1
- package/src/shade/renderer/gpu_primitive/sort/ffx/shader_bit_histogram.js +0 -120
- package/src/shade/renderer/gpu_primitive/sort/ffx/shader_distribute_part_sum_int4.d.ts +0 -3
- package/src/shade/renderer/gpu_primitive/sort/ffx/shader_distribute_part_sum_int4.d.ts.map +0 -1
- package/src/shade/renderer/gpu_primitive/sort/ffx/shader_distribute_part_sum_int4.js +0 -76
- package/src/shade/renderer/gpu_primitive/sort/ffx/shader_scan_exclusive_partial.d.ts +0 -3
- package/src/shade/renderer/gpu_primitive/sort/ffx/shader_scan_exclusive_partial.d.ts.map +0 -1
- package/src/shade/renderer/gpu_primitive/sort/ffx/shader_scan_exclusive_partial.js +0 -125
- package/src/shade/renderer/gpu_primitive/sort/ffx/shader_scan_exclusive_uint4.d.ts +0 -3
- package/src/shade/renderer/gpu_primitive/sort/ffx/shader_scan_exclusive_uint4.d.ts.map +0 -1
- package/src/shade/renderer/gpu_primitive/sort/ffx/shader_scan_exclusive_uint4.js +0 -140
- package/src/shade/renderer/gpu_primitive/sort/ffx/shader_scatter_keys_and_values.d.ts +0 -3
- package/src/shade/renderer/gpu_primitive/sort/ffx/shader_scatter_keys_and_values.d.ts.map +0 -1
- package/src/shade/renderer/gpu_primitive/sort/ffx/shader_scatter_keys_and_values.js +0 -338
- package/src/shade/renderer/gpu_primitive/sort/gaussian_splatting/x.d.ts +0 -7
- package/src/shade/renderer/gpu_primitive/sort/gaussian_splatting/x.d.ts.map +0 -1
- package/src/shade/renderer/gpu_primitive/sort/gaussian_splatting/x.js +0 -261
- package/src/shade/renderer/gpu_primitive/sort/one_sweep/chunk_extract_digit.d.ts +0 -3
- package/src/shade/renderer/gpu_primitive/sort/one_sweep/chunk_extract_digit.d.ts.map +0 -1
- package/src/shade/renderer/gpu_primitive/sort/one_sweep/chunk_extract_digit.js +0 -14
- package/src/shade/renderer/gpu_primitive/sort/one_sweep/defs.d.ts +0 -25
- package/src/shade/renderer/gpu_primitive/sort/one_sweep/defs.d.ts.map +0 -1
- package/src/shade/renderer/gpu_primitive/sort/one_sweep/defs.js +0 -25
- package/src/shade/renderer/gpu_primitive/sort/one_sweep/graph_radix_sort.d.ts +0 -15
- package/src/shade/renderer/gpu_primitive/sort/one_sweep/graph_radix_sort.d.ts.map +0 -1
- package/src/shade/renderer/gpu_primitive/sort/one_sweep/graph_radix_sort.js +0 -192
- package/src/shade/renderer/gpu_primitive/sort/one_sweep/shader_digit_binning_pass_pairs.d.ts +0 -5
- package/src/shade/renderer/gpu_primitive/sort/one_sweep/shader_digit_binning_pass_pairs.d.ts.map +0 -1
- package/src/shade/renderer/gpu_primitive/sort/one_sweep/shader_digit_binning_pass_pairs.js +0 -990
- package/src/shade/renderer/gpu_primitive/sort/one_sweep/shader_global_histogram.d.ts +0 -5
- package/src/shade/renderer/gpu_primitive/sort/one_sweep/shader_global_histogram.d.ts.map +0 -1
- package/src/shade/renderer/gpu_primitive/sort/one_sweep/shader_global_histogram.js +0 -82
- package/src/shade/renderer/gpu_primitive/sort/one_sweep/shader_init_sweep.d.ts +0 -3
- package/src/shade/renderer/gpu_primitive/sort/one_sweep/shader_init_sweep.d.ts.map +0 -1
- package/src/shade/renderer/gpu_primitive/sort/one_sweep/shader_init_sweep.js +0 -44
- package/src/shade/renderer/gpu_primitive/sort/one_sweep/shader_scan.d.ts +0 -5
- package/src/shade/renderer/gpu_primitive/sort/one_sweep/shader_scan.d.ts.map +0 -1
- package/src/shade/renderer/gpu_primitive/sort/one_sweep/shader_scan.js +0 -156
- package/src/shade/renderer/gpu_primitive/sort/one_sweep/test_radix_sort.d.ts +0 -6
- package/src/shade/renderer/gpu_primitive/sort/one_sweep/test_radix_sort.d.ts.map +0 -1
- package/src/shade/renderer/gpu_primitive/sort/one_sweep/test_radix_sort.js +0 -55
- package/src/shade/renderer/gpu_primitive/sort/one_sweep/verify_radix_sort_one_sweep.d.ts +0 -14
- package/src/shade/renderer/gpu_primitive/sort/one_sweep/verify_radix_sort_one_sweep.d.ts.map +0 -1
- package/src/shade/renderer/gpu_primitive/sort/one_sweep/verify_radix_sort_one_sweep.js +0 -110
- package/src/shade/renderer/gpu_primitive/sort/v4/graph_radix_sort.d.ts +0 -12
- package/src/shade/renderer/gpu_primitive/sort/v4/graph_radix_sort.d.ts.map +0 -1
- package/src/shade/renderer/gpu_primitive/sort/v4/graph_radix_sort.js +0 -74
- package/src/shade/renderer/gpu_primitive/sort/v4/shader_radix_sort.d.ts +0 -3
- package/src/shade/renderer/gpu_primitive/sort/v4/shader_radix_sort.d.ts.map +0 -1
- package/src/shade/renderer/gpu_primitive/sort/v4/shader_radix_sort.js +0 -125
- package/src/shade/renderer/scene/optimization/geometry/internal/bt_face_loop_helpers.d.ts +0 -32
- package/src/shade/renderer/scene/optimization/geometry/internal/bt_face_loop_helpers.d.ts.map +0 -1
- package/src/shade/renderer/scene/optimization/geometry/internal/bt_face_loop_helpers.js +0 -77
- package/src/shade/renderer/scene/optimization/geometry/internal/bt_find_edge_between.d.ts +0 -12
- package/src/shade/renderer/scene/optimization/geometry/internal/bt_find_edge_between.d.ts.map +0 -1
- package/src/shade/renderer/scene/optimization/geometry/internal/bt_find_edge_between.js +0 -35
- package/src/shade/renderer/scene/serialization/linear_to_pq.d.ts +0 -2
- package/src/shade/renderer/scene/serialization/linear_to_pq.d.ts.map +0 -1
- package/src/shade/renderer/scene/serialization/linear_to_pq.js +0 -14
- package/src/shade/renderer/scene/serialization/pq_to_linear.d.ts +0 -9
- package/src/shade/renderer/scene/serialization/pq_to_linear.d.ts.map +0 -1
- package/src/shade/renderer/scene/serialization/pq_to_linear.js +0 -30
- package/src/shade/renderer/shader/chunk/bvh/morton/chunk_calculate_morton_code.d.ts +0 -7
- package/src/shade/renderer/shader/chunk/bvh/morton/chunk_calculate_morton_code.d.ts.map +0 -1
- package/src/shade/renderer/shader/chunk/bvh/morton/chunk_calculate_morton_code.js +0 -36
- package/src/shade/renderer/shader/chunk/bvh/morton/chunk_get_morton_codes_from_unit_coord.d.ts +0 -7
- package/src/shade/renderer/shader/chunk/bvh/morton/chunk_get_morton_codes_from_unit_coord.d.ts.map +0 -1
- package/src/shade/renderer/shader/chunk/bvh/morton/chunk_get_morton_codes_from_unit_coord.js +0 -54
- package/src/shade/renderer/shader/chunk/bvh/packed/NOTES.md +0 -19
- package/src/shade/renderer/shader/chunk/bvh/packed/PACKED_BVH_NODE_STRUCT.d.ts +0 -3
- package/src/shade/renderer/shader/chunk/bvh/packed/PACKED_BVH_NODE_STRUCT.d.ts.map +0 -1
- package/src/shade/renderer/shader/chunk/bvh/packed/PACKED_BVH_NODE_STRUCT.js +0 -9
- package/src/shade/renderer/shader/chunk/bvh/packed/PACKED_BVH_NODE_WORD_COUNT.d.ts +0 -6
- package/src/shade/renderer/shader/chunk/bvh/packed/PACKED_BVH_NODE_WORD_COUNT.d.ts.map +0 -1
- package/src/shade/renderer/shader/chunk/bvh/packed/PACKED_BVH_NODE_WORD_COUNT.js +0 -5
- package/src/shade/renderer/shader/chunk/bvh/packed/chunk_make_f32_interval_f16_safe.d.ts +0 -8
- package/src/shade/renderer/shader/chunk/bvh/packed/chunk_make_f32_interval_f16_safe.d.ts.map +0 -1
- package/src/shade/renderer/shader/chunk/bvh/packed/chunk_make_f32_interval_f16_safe.js +0 -17
- package/src/shade/renderer/shader/chunk/bvh/packed/chunk_pack_f32_interval_to_u32.d.ts +0 -3
- package/src/shade/renderer/shader/chunk/bvh/packed/chunk_pack_f32_interval_to_u32.d.ts.map +0 -1
- package/src/shade/renderer/shader/chunk/bvh/packed/chunk_pack_f32_interval_to_u32.js +0 -14
- package/src/shade/renderer/shader/chunk/bvh/packed/chunk_packed_bvh_from_unpacked_node.d.ts +0 -3
- package/src/shade/renderer/shader/chunk/bvh/packed/chunk_packed_bvh_from_unpacked_node.d.ts.map +0 -1
- package/src/shade/renderer/shader/chunk/bvh/packed/chunk_packed_bvh_from_unpacked_node.js +0 -27
- package/src/shade/renderer/shader/chunk/bvh/packed/chunk_packed_bvh_read_node.d.ts +0 -3
- package/src/shade/renderer/shader/chunk/bvh/packed/chunk_packed_bvh_read_node.d.ts.map +0 -1
- package/src/shade/renderer/shader/chunk/bvh/packed/chunk_packed_bvh_read_node.js +0 -31
- package/src/shade/renderer/shader/chunk/bvh/packed/chunk_packed_bvh_write_node.d.ts +0 -3
- package/src/shade/renderer/shader/chunk/bvh/packed/chunk_packed_bvh_write_node.d.ts.map +0 -1
- package/src/shade/renderer/shader/chunk/bvh/packed/chunk_packed_bvh_write_node.js +0 -25
- package/src/shade/renderer/shader/chunk/bvh/packed/shader_pack_blas.d.ts +0 -3
- package/src/shade/renderer/shader/chunk/bvh/packed/shader_pack_blas.d.ts.map +0 -1
- package/src/shade/renderer/shader/chunk/bvh/packed/shader_pack_blas.js +0 -42
- /package/src/engine/sound/{ecs/emitter → sopra/legacy}/SoundAttenuationFunction.d.ts +0 -0
- /package/src/engine/sound/{ecs/emitter → sopra/legacy}/SoundAttenuationFunction.js +0 -0
- /package/src/engine/sound/{ecs/emitter → sopra/legacy}/SoundEmitterFlags.d.ts +0 -0
- /package/src/engine/sound/{ecs/emitter → sopra/legacy}/SoundEmitterFlags.js +0 -0
- /package/src/engine/sound/{ecs/emitter → sopra/legacy}/SoundEmitterSerializationAdapter.d.ts +0 -0
- /package/src/engine/sound/{ecs/emitter → sopra/legacy}/SoundEmitterSerializationAdapter.js +0 -0
- /package/src/engine/sound/{ecs/emitter → sopra/legacy}/SoundEmitterSerializationUpgrader_0_1.d.ts +0 -0
- /package/src/engine/sound/{ecs/emitter → sopra/legacy}/SoundEmitterSerializationUpgrader_0_1.js +0 -0
- /package/src/engine/sound/{ecs/emitter → sopra/legacy}/SoundEmitterSerializationUpgrader_1_2.d.ts +0 -0
- /package/src/engine/sound/{ecs/emitter → sopra/legacy}/SoundEmitterSerializationUpgrader_1_2.js +0 -0
- /package/src/engine/sound/{ecs/emitter → sopra/legacy}/SoundPanningModelType.d.ts +0 -0
- /package/src/engine/sound/{ecs/emitter → sopra/legacy}/SoundPanningModelType.js +0 -0
- /package/src/engine/sound/{ecs/emitter → sopra/legacy}/SoundTrackFlags.d.ts +0 -0
- /package/src/engine/sound/{ecs/emitter → sopra/legacy}/SoundTrackFlags.js +0 -0
- /package/src/shade/playground/{main.d.ts → bvh_repro/main.d.ts} +0 -0
|
@@ -0,0 +1,1115 @@
|
|
|
1
|
+
# GPU BVH Builder — research findings and implementation plan
|
|
2
|
+
|
|
3
|
+
> Status: **PHASE 1 LANDED (device-free tiers)** — 2026-08-26. The CSDLDF key-value radix sort and
|
|
4
|
+
> the GPU TLAS build are in the tree, the GPU path is the default, and the three device-free
|
|
5
|
+
> validation tiers are green (`npm test`: 13,590 passing, nothing skipped that was not already).
|
|
6
|
+
> **Phase 1 is complete and gated.** The harnesses are green, and the consumer A/B has been run
|
|
7
|
+
> through `src/shade/playground/sponza_path_trace/`: Sponza traced against a CPU-built and a
|
|
8
|
+
> GPU-built tree, images verified equivalent by inspection. §4.6 carries the numbers.
|
|
9
|
+
>
|
|
10
|
+
> The A/B produced one result that changes what phase 2 is for, and §5.1 has been rewritten around
|
|
11
|
+
> it: **at instance scale the GPU LBVH already beats the CPU pipeline on every axis** — SAH, depth,
|
|
12
|
+
> traced frame time and build time — so phase 2's stated premise, that phase 1 ships a quality
|
|
13
|
+
> regression, does not survive measurement. Deviations from this plan are recorded in place below,
|
|
14
|
+
> each marked **[deviation]**.
|
|
15
|
+
>
|
|
16
|
+
> Landed as: `db960d9cb` (sort), `31599c3b9` (morton + emission chunks), `61727d3b8` (builder
|
|
17
|
+
> shaders + recorder), `6b062dbcc` (TLAS integration), `7222f1b35` (format spec + verify harness).
|
|
18
|
+
>
|
|
19
|
+
> Original status: PLANNED — research complete, no code landed. 2026-08-26.
|
|
20
|
+
> Revised same day after review: the sort is a deliverable built around **CSDLDF** (the
|
|
21
|
+
> fallback-less OneSweep port was removed in `b6049214c`; history serves as reference);
|
|
22
|
+
> **subgroups are a baseline engine requirement** and are used wherever they pay; validation
|
|
23
|
+
> explicitly stands on all four device-free/on-device tiers, `SoftwareGPUDevice` and the WGSL
|
|
24
|
+
> emulator included.
|
|
25
|
+
>
|
|
26
|
+
> Goal: move BVH construction to the GPU. First deliverable replaces the CPU-side build that
|
|
27
|
+
> `GPUInstancesAccelerationStructure` (the TLAS) performs today; then treelet optimisation on the
|
|
28
|
+
> GPU; then the geometry side (BLAS); and only after all of that, BVH8. Every phase leaves the
|
|
29
|
+
> existing RT and culling consumers fully working.
|
|
30
|
+
>
|
|
31
|
+
> References studied: Microsoft's D3D12 Raytracing Fallback Layer and AMD's GPURT, plus the
|
|
32
|
+
> construction literature (Karras 2012, Karras & Aila 2013, Apetrei 2014, PLOC/PLOC++/H-PLOC).
|
|
33
|
+
> Both references and the literature survey are summarised in §3; meep's own inventory — which
|
|
34
|
+
> turns out to already contain most of the algorithm — is §2.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 1. Why, and why now
|
|
39
|
+
|
|
40
|
+
`GPUInstancesAccelerationStructure.push_to_gpu()` currently does, on the CPU, on **every frame in
|
|
41
|
+
which any instance moved**:
|
|
42
|
+
|
|
43
|
+
1. `ebvh_optimize_treelet(tree)` — a whole-tree treelet restructuring pass (Karras & Aila 2013,
|
|
44
|
+
treelet size 7, exhaustive 2^7-subset SAH DP per treelet), mutating the dynamic tree;
|
|
45
|
+
2. `bvh_to_gpu_buffer(...)` — flatten `node_capacity` nodes from the 40-byte CPU format to the
|
|
46
|
+
32-byte GPU format into a fresh mapped staging buffer;
|
|
47
|
+
3. a full-buffer `copyBufferToBuffer` upload.
|
|
48
|
+
|
|
49
|
+
This is a measured, known frame-budget problem. `TerrainSystem3.spec.js` documents the incident
|
|
50
|
+
(E19): *"the TLAS rebuilds — a whole-tree treelet optimisation and a fresh GPU buffer — whenever
|
|
51
|
+
any instance it holds is told its bounds changed. A terrain standing still was doing all of that
|
|
52
|
+
once a frame, which was a quarter of the frame."* The fix that spec pins (don't bump versions for
|
|
53
|
+
unmoved nodes) treats the trigger; this project treats the cost. In any scene with continuous
|
|
54
|
+
motion — animation, physics, particles-as-instances — the rebuild fires every frame by design, and
|
|
55
|
+
the CPU pays O(N) treelet DP plus an O(capacity) upload each time.
|
|
56
|
+
|
|
57
|
+
There is a second, structural motivation: **the CPU cannot see GPU-authority transforms.** Rows
|
|
58
|
+
under `TransformAuthority.GPU` (GPU-side animation / hierarchy propagation) move on the GPU;
|
|
59
|
+
`shader_refresh_all_mesh_bounds` recomputes their world bounds into the scene database — and the
|
|
60
|
+
CPU-built TLAS never learns about any of it. A GPU-side build that reads instance bounds from the
|
|
61
|
+
scene database gets correct bounds for both authorities from a single source of truth. The GPU
|
|
62
|
+
build is not just faster; for GPU-animated content it is the only correct option.
|
|
63
|
+
|
|
64
|
+
Context worth stating: the survey (§3.3) found that essentially every public WebGPU ray tracer
|
|
65
|
+
still builds its BVH on the CPU and only traverses on the GPU — published CPU build times run to
|
|
66
|
+
hundreds of milliseconds per million triangles, while driver-class GPU builders do the same work
|
|
67
|
+
in low single-digit milliseconds. A WGSL GPU builder is near-unoccupied territory; meep has an
|
|
68
|
+
unusually short path to it because the algorithms and the substrate primitives are already in the
|
|
69
|
+
tree (§2).
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## 2. Research: what meep already has
|
|
74
|
+
|
|
75
|
+
The striking finding of this research: **meep already contains a complete, tested CPU
|
|
76
|
+
implementation of exactly the algorithm family that both external references run on the GPU**, plus
|
|
77
|
+
almost every GPU primitive the port needs. The builder is a port, not an invention.
|
|
78
|
+
|
|
79
|
+
### 2.1 The wire format — the compatibility contract
|
|
80
|
+
|
|
81
|
+
Everything below is what "existing RT code stays compatible" means, verified against every consumer
|
|
82
|
+
in the tree.
|
|
83
|
+
|
|
84
|
+
**Node** — `src/shade/renderer/shader/chunk/bvh/BVH2_NODE_STRUCT.js`, 32 bytes, align 4:
|
|
85
|
+
|
|
86
|
+
| bytes | field | notes |
|
|
87
|
+
|---|---|---|
|
|
88
|
+
| 0–23 | `bounds: array<f32, 6>` | `[min.x, min.y, min.z, max.x, max.y, max.z]` — block layout, not interleaved (`chunk_aabb3_intersects_ray` reads `bounds[0..2]` / `bounds[3..5]`) |
|
|
89
|
+
| 24–27 | `child_1: u32` | `== 0xFFFFFFFF` (`BVH_NULL_NODE`) ⇔ **leaf** |
|
|
90
|
+
| 28–31 | `child_2: u32` | internal: right-child node index; leaf: **payload** (user data) |
|
|
91
|
+
|
|
92
|
+
There are no tag bits, no counts, no ranges. One primitive per leaf. Internal nodes always have two
|
|
93
|
+
valid children — and the distance-ordered point queries (`chunk_point_query_blas_nearest`,
|
|
94
|
+
`chunk_scene_point_query_nearest`) dereference **both** children's bounds before descending, so
|
|
95
|
+
both must be real node indices with meaningful AABBs.
|
|
96
|
+
|
|
97
|
+
**Buffer** — TLAS and the LPV tetrahedral lookup use `BVH_STRUCT`
|
|
98
|
+
(`{root: u32, nodes: array<BVH2_NODE_STRUCT>}`): a `u32` root index at byte 0, nodes from byte 4.
|
|
99
|
+
That is the entire meaning of the `+ 4` in every sizing expression. **BLAS has no header**: root is
|
|
100
|
+
implicitly node 0 (asserted twice in `meshlet_batch_to_bvh.js`, relied on by
|
|
101
|
+
`chunk_ray_query_blas_*` and by `shader_build_sdf.js`, which reads `blas_data[blas_address].bounds`
|
|
102
|
+
as the geometry's bounds). BLAS trees live packed in one arena
|
|
103
|
+
(`GPUGeometryBVHManager`); `blas_lookup[geometry] = address` is a **node index, not a byte
|
|
104
|
+
offset**.
|
|
105
|
+
|
|
106
|
+
**Leaf payloads**: TLAS leaf `child_2` = row in the scene database `meshes` table, consumed via
|
|
107
|
+
`scene_read_mesh`. BLAS leaf `child_2` = `(meshlet_index << 8) | (triangle_index & 0xFF)` — ≤256
|
|
108
|
+
triangles per meshlet, and this encoding is **serialized to disk** with geometry
|
|
109
|
+
(`MeshletGeometrySerializationAdapter`), so it is a content-format contract, not just a runtime
|
|
110
|
+
one.
|
|
111
|
+
|
|
112
|
+
**Traversal** (`chunk_ray_query_nearest`, `chunk_ray_query_occluded`, BLAS variants, LPV lookup):
|
|
113
|
+
stack-based, `BVH_TRAVERSAL_STACK_SIZE = 32`, descends `child_1`, pushes `child_2`, no ordered
|
|
114
|
+
descent in the ray paths. Two facts with teeth:
|
|
115
|
+
|
|
116
|
+
- The 32-entry stack is **shared between TLAS and BLAS** in one query — effective combined depth
|
|
117
|
+
budget ≈ 31. The occlusion variant's `(pointer & 31) != 0` loop condition silently *truncates*
|
|
118
|
+
traversal at stack overflow rather than erroring. Tree depth is therefore a real output-quality
|
|
119
|
+
constraint on the builder, not a nicety (§7, R4).
|
|
120
|
+
- `tlas.root` is read from the header word — the builder must write it (it will be constant 0, see
|
|
121
|
+
§4.3, but it must be written).
|
|
122
|
+
|
|
123
|
+
**Who consumes what** (all verified):
|
|
124
|
+
|
|
125
|
+
- GPU, TLAS: path tracer (`graph_path_trace_scene`, `AccumulatingPathTracer`, `graph_restir_di`),
|
|
126
|
+
lightmap baker (`GPULightMap`), LPV/probes (`graph_lpv_shade`, `GPUProbeAtlas`,
|
|
127
|
+
`GPULightProbeVolumeRenderer`, `graph_generate_probe_locations`), brick4 GI bake, RTX shadows
|
|
128
|
+
(`RTXShadows`, `graph_trace_rays`), SDF passes (`CascadedSceneSDF`, `graph_draw_sdf_shadows`,
|
|
129
|
+
`shader_sdf_visualize`). All grab `scene_ctx.tlas.buffer` **at pass-execute time** as an external
|
|
130
|
+
buffer — none of them track it as a frame-graph resource. A build that writes the same
|
|
131
|
+
(or a replacement) buffer earlier in queue order is invisible to them.
|
|
132
|
+
- CPU: `DirectionalLightShadowmap` runs `compute_tight_near_far_clipping_planes` against
|
|
133
|
+
**`scene_ctx.tlas.bvh` — the CPU-side tree object** — per cascade per frame. The CPU dynamic tree
|
|
134
|
+
must therefore stay alive regardless of where the GPU buffer comes from.
|
|
135
|
+
- Culling: **none**. `graph_mesh_frustum_cull`, HZB filtering, and bucket classification are flat
|
|
136
|
+
sweeps over the `meshes` table; `grep bvh src/shade/renderer/rasterize` is empty. "Culling stays
|
|
137
|
+
compatible" is satisfied vacuously — and equally, no raster-side test will catch a TLAS
|
|
138
|
+
regression; RT consumers are the only canaries.
|
|
139
|
+
|
|
140
|
+
### 2.2 CPU algorithms already in the tree (the semantic reference implementations)
|
|
141
|
+
|
|
142
|
+
`src/core/bvh2/bvh3/`:
|
|
143
|
+
|
|
144
|
+
- **`ebvh_build_hierarchy_radix.js` — Karras 2012 binary radix tree, complete and spec-tested.**
|
|
145
|
+
`delta()` with the index tiebreak for duplicate morton codes
|
|
146
|
+
(`ci === cj ? 32 + clz32(i ^ j) : clz32(ci ^ cj)`), `determineRange` (exponential probe + binary
|
|
147
|
+
descent), `find_split` (binary search). The in-source comment pins a real trap: do **not**
|
|
148
|
+
midpoint-split duplicate-code runs — it desynchronises from `determineRange` and produces a
|
|
149
|
+
shared-node non-tree. This file is, line for line, the hierarchy-emission kernel of the GPU
|
|
150
|
+
build; the GPU version is a transliteration with an index-layout change (§4.3).
|
|
151
|
+
- **`ebvh_optimize_treelet.js` — Karras & Aila 2013 treelet restructuring**, treelet size 7,
|
|
152
|
+
the exhaustive subset-DP with the paper's constants (`C_int = 1.2`, `C_prim = 1.0`), greedy
|
|
153
|
+
largest-surface-area treelet formation. This is the same algorithm — same constants — that the
|
|
154
|
+
DXR Fallback runs as its GPU `TreeletReorder` pass. Phase 2 is a port of semantics that already
|
|
155
|
+
have a CPU oracle and a `.spec.js`.
|
|
156
|
+
- `build_triangle_morton_codes.js` + `v3_morton_encode` (magic-bits): 30-bit morton, 10 bits/axis,
|
|
157
|
+
`x | y<<1 | z<<2`. The WGSL side already has the matching expander:
|
|
158
|
+
`shader/chunk/binary/chunk_split_by_3.js`.
|
|
159
|
+
- `ebvh_update_hierarchy_bounds.js` / `refit_radix_tree` (inside the radix builder): sequential
|
|
160
|
+
forms of the bottom-up refit.
|
|
161
|
+
- `BVH.js`: 10-word AoS nodes (bounds ×6, parent, child_1, child_2/user_data, height),
|
|
162
|
+
free-list allocation, `allocate_linear` bulk path, SAH-guided `insert_leaf` with
|
|
163
|
+
`balance_rotate` (Kensler/Box2D-style SAH rotations) — the **dynamic** tree that
|
|
164
|
+
`GPUInstancesAccelerationStructure` maintains incrementally and that CPU queries walk. It stays.
|
|
165
|
+
|
|
166
|
+
The CPU→GPU flattening (`bvh2_compress.js` + `bvh_to_gpu_buffer.js`) is a pure 40→32-byte gather
|
|
167
|
+
in slot order — CPU node index i **is** GPU node index i, free-pool holes are uploaded as garbage
|
|
168
|
+
and never referenced, and the buffer is sized by `node_capacity`, not live count. Nothing anywhere
|
|
169
|
+
depends on any particular node *ordering* — only on the format and the root convention. The GPU
|
|
170
|
+
builder is free to choose its own layout (§4.3).
|
|
171
|
+
|
|
172
|
+
### 2.3 GPU compute infrastructure already in the tree
|
|
173
|
+
|
|
174
|
+
- **Frame graph** (`engine/graphics/render/frame_graph/FrameGraph.js` + shade's
|
|
175
|
+
`graph_*` recorder convention): one-shot record → `compile()` → `execute()`; passes run in
|
|
176
|
+
insertion order; each `ComputeShader.dispatch` opens its own compute pass, so WebGPU's implicit
|
|
177
|
+
inter-dispatch barriers do the synchronisation. `ShadeGPUCommandContext.create(graphics)` +
|
|
178
|
+
`cmd.encodeGraph(graph)` + `cmd.finish()` is the standalone idiom
|
|
179
|
+
(`test_radix_sort.js` is an 8-line template). Transient uniforms via
|
|
180
|
+
`encoder.allocateTransientValueBuffer(STRUCT, value)`; pooled transient buffers via
|
|
181
|
+
`BufferResourceDescriptor` (fresh buffers arrive zeroed; reused pool buffers honour the
|
|
182
|
+
descriptor's clear range).
|
|
183
|
+
- **Sort: none (deliberately).** Commit `b6049214c` ("deleted partial and dead code") removed all
|
|
184
|
+
four sort variants — the OneSweep port included, because it had **no fallback path** (bare
|
|
185
|
+
decoupled lookback assumes forward progress). The engine's sorting direction is now: **build
|
|
186
|
+
around CSDLDF** — chained scan, decoupled lookback, *decoupled fallback*. The key-value radix
|
|
187
|
+
sort is therefore a deliverable of this project (§4.2.1), not a reuse. The deleted OneSweep port
|
|
188
|
+
remains reference material in history
|
|
189
|
+
(`git show 370542c3f:src/shade/renderer/gpu_primitive/sort/one_sweep/...`) — in particular its
|
|
190
|
+
digit-binning workgroup machinery and its two hard-won WebGPU lessons (the `subgroup_size`
|
|
191
|
+
private-var propagation fix; manual unrolling for Tint's uniformity analysis).
|
|
192
|
+
- **Prefix scan — the surviving, blessed pattern**
|
|
193
|
+
(`gpu_primitive/prefix_sum/v1/graph_prefix_scan_csdldf.js` + `shader_prefix_scan_csdldf.js`,
|
|
194
|
+
from the b0nes164 Decoupled-Fallback paper artifact): subgroup-first (shuffle/ballot,
|
|
195
|
+
`subgroup_size` builtin, `MIN_SUBGROUP_SIZE = 4` handling, the
|
|
196
|
+
`@diagnostic(off, subgroup_uniformity)` idiom), per-partition **split flag words** (two
|
|
197
|
+
`atomic<u32>`s carrying READY/INCLUSIVE flags plus 16-bit value halves), bounded spin
|
|
198
|
+
(`MAX_SPIN_COUNT = 4`, `LOCKED/UNLOCKED`) and then **fallback: the blocked workgroup reduces the
|
|
199
|
+
missing predecessor tile itself** — progress guaranteed with no scheduling-fairness assumption.
|
|
200
|
+
Inclusive-only, `{count: u32, elements: array<vec4u>}` layout (16-byte aligned), partition size
|
|
201
|
+
4096, indirect-dispatched. This shader is the in-repo template the sort's spine and protocol are
|
|
202
|
+
built from.
|
|
203
|
+
- **Indirect dispatch** (`gpu_primitive/indirect/IndirectComputeCommandBuildPass.js`):
|
|
204
|
+
count-buffer → `STRUCT_DISPATCH_COMPUTE_COMMAND`, with `graph_build_indirect_dispatch_command`
|
|
205
|
+
and `ComputeShader.dispatchIndirect`. Available; phase 1 does not need it (the instance count is
|
|
206
|
+
CPU-known at encode time), the BLAS phase does.
|
|
207
|
+
- **Atomic f32 AABB merge** (`animation/skinning/bounds_refresh/chunk_aabb_atomic_f32.js`):
|
|
208
|
+
CAS-spin `aabb_atomic_min_f32` / `aabb_atomic_max_f32` over `atomic<u32>` cells holding plain
|
|
209
|
+
f32 bit patterns, plus `shader_meshlet_bounds_reduce.js` as the proven
|
|
210
|
+
reduce-then-atomic-merge pattern — this is the scene-AABB pass, essentially verbatim, with one
|
|
211
|
+
upgrade: that shader predates the subgroups baseline and reduces through workgroup memory only
|
|
212
|
+
(its own comment concedes subgroup ops would be faster); the builder's variant puts
|
|
213
|
+
`subgroupMin/Max` in front, workgroup memory only across subgroup boundaries. The same file
|
|
214
|
+
also demonstrates the "same buffer bound as `array<atomic<u32>>` in one pass and as typed
|
|
215
|
+
structs in another" idiom the refit pass uses.
|
|
216
|
+
- **Scene database** (`GPUSceneContext` / `SCENE_DATABASE_SPEC`): the `meshes` table row already
|
|
217
|
+
carries `bounding_box: array<f32,6>` (world-space) and is refreshed GPU-side by
|
|
218
|
+
`shader_refresh_all_mesh_bounds` for GPU-authority content — the build's input, already resident.
|
|
219
|
+
- **Shader authoring**: `CodeChunk` dependency DAG, `ShaderResourceSetDescriptor`,
|
|
220
|
+
`WebGPUStruct/Array` with correct WGSL layout math, `ComputeShader.from({...}).dispatch(...)`.
|
|
221
|
+
- **Validation tiers** (the standing rule: a vitest spec may not stand up a real GPU device):
|
|
222
|
+
(a) tree-sitter WGSL parse + `validate_wgsl_source` (`src/shade/wgsl/`) in vitest;
|
|
223
|
+
(b) **`ComputeShaderEmulator`** (`src/shade/wgsl/emulator/`) — compiles WGSL to JS and executes
|
|
224
|
+
it point-wise; not a full implementation (no subgroups, no real barrier scheduling), but exactly
|
|
225
|
+
right for executing *specific functions and chunks* against CPU oracles, and whole kernels when
|
|
226
|
+
they are pure per-invocation functions;
|
|
227
|
+
(c) **`SoftwareGPUDevice`** (`src/shade/device/mock/`) — a recording software `GPUDevice`
|
|
228
|
+
(encoders, bind groups, buffers, mapping, timestamps, validation) that runs **none** of the
|
|
229
|
+
shaders: dispatches land on `SoftwareGPUComputePassEncoder.dispatches` for assertion. The tier
|
|
230
|
+
for host-side orchestration: encode the real frame graph against it and assert the dispatch
|
|
231
|
+
sequence, bindings, buffer sizes/usages, and host-written bytes;
|
|
232
|
+
(d) browser playground verify functions (FrameGraph + `graph_import_typed_buffer` +
|
|
233
|
+
`graph_inspect_gpu_buffer` readback + CPU assertions, registered as lil-gui "review checks");
|
|
234
|
+
`gpu_test.js` offers a standalone device bootstrap. GPU timing via `GPUTimer` +
|
|
235
|
+
`encoder.enable_debug_timers` (note: the `timer` argument `graph_compute_pass` forwards is
|
|
236
|
+
silently dropped by `ComputeShader.dispatch` — use the debug-timers path).
|
|
237
|
+
- **Morton chunks** (`shader/chunk/bvh/morton/`): the fixed 10-bit `chunk_v3_morton_encode`,
|
|
238
|
+
spec'd bit-exact against `v3_morton_encode_magicbits`, is what the build uses (§4.2) for exact
|
|
239
|
+
CPU parity, alongside the 2D encode/decode pairs. A WGSL port of the DXR Fallback's
|
|
240
|
+
*adaptive-bits* `CalculateMortonCodes.hlsli` also lived here (the repo has been absorbing
|
|
241
|
+
reference #1 for a while) — `chunk_calculate_morton_code` plus its
|
|
242
|
+
`chunk_get_morton_codes_from_unit_coord` dependency, which never passed WGSL validation
|
|
243
|
+
(`if (bit)` on a `u32`; an f32/u32 `min` mix upstream). Both were unused and are now deleted (§8).
|
|
244
|
+
|
|
245
|
+
### 2.4 What does NOT exist yet (the actual gaps)
|
|
246
|
+
|
|
247
|
+
1. A **key-value radix sort** built around CSDLDF (§4.2.1) — the largest single new piece since
|
|
248
|
+
commit `b6049214c` removed the fallback-less OneSweep port. A first-class `gpu_primitive/sort`
|
|
249
|
+
deliverable in its own right, with the builder as its first consumer.
|
|
250
|
+
2. A WGSL **3D morton encode** chunk composing `chunk_split_by_3` (2D variants exist; 3D does not).
|
|
251
|
+
3. A **scene/leaf AABB reduction** pass (pattern exists in `shader_meshlet_bounds_reduce`; the
|
|
252
|
+
general primitive does not).
|
|
253
|
+
4. The **hierarchy emission** kernel (CPU version exists; WGSL does not).
|
|
254
|
+
5. The **bottom-up refit** kernel (no GPU refit exists anywhere in the tree).
|
|
255
|
+
6. Orchestration: a `graph_bvh_build_*` recorder and the `GPUInstancesAccelerationStructure`
|
|
256
|
+
integration, including a live-instance-rows input (§4.1).
|
|
257
|
+
7. The **treelet optimisation** kernel (phase 2; CPU version exists).
|
|
258
|
+
8. A BVH2→BVH8 **collapse** pass (phase 4; traversal-side `BVH8_INODE_STRUCT` +
|
|
259
|
+
`chunk_bvh8_node_intersect` + `chunk_bvh8_ray_query_blas_occlusion` already exist as an
|
|
260
|
+
unwired experiment).
|
|
261
|
+
|
|
262
|
+
Scan (with the fallback discipline), reduction idiom, atomic-f32 min/max, indirect dispatch,
|
|
263
|
+
struct/layout codegen, graph plumbing, readback, four validation tiers, CPU oracles: all exist.
|
|
264
|
+
Phase 1's slice is the sort plus five small builder shaders (items 2–5 and an init pass) and one
|
|
265
|
+
integration; items 7–8 belong to later phases.
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## 3. Research: the references
|
|
270
|
+
|
|
271
|
+
### 3.1 Microsoft D3D12 Raytracing Fallback Layer (`D3D12RaytracingFallback/src`)
|
|
272
|
+
|
|
273
|
+
The most directly relevant reference — same output shape (binary BVH, 32-byte nodes, software
|
|
274
|
+
stack traversal with `TRAVERSAL_MAX_STACK_DEPTH 32`), and, remarkably, the same two core
|
|
275
|
+
algorithms meep already has on CPU. ~5,400 LOC total for the GPU build path (~26 compute PSOs);
|
|
276
|
+
the meep port is far smaller because sort, ingestion, and orchestration already exist.
|
|
277
|
+
|
|
278
|
+
Pipeline (`GpuBvh2Builder::BuildBVH`), identical skeleton for TLAS and BLAS:
|
|
279
|
+
|
|
280
|
+
1. **Load elements** — per-geometry dispatches decoding VB/IB into a flat `Primitive[]` scratch
|
|
281
|
+
(BLAS), or one dispatch loading instance descs, dereferencing each BLAS root box, transforming
|
|
282
|
+
its 8 corners to world space, and pre-inverting the transform (TLAS).
|
|
283
|
+
2. **Scene AABB** — iterative 8:1 ping-pong reduction, O(log₈ n) dispatches.
|
|
284
|
+
3. **Morton codes** — 30-bit, 10 bits/axis, centroid quantised into the scene AABB (an
|
|
285
|
+
adaptive-bits `SCALED_MORTON_CODES` variant exists but is dead code — meep carried an unused
|
|
286
|
+
WGSL transcription of that same variant until §8 deleted it).
|
|
287
|
+
4. **Sort** — MiniEngine **bitonic** sort (indirect-dispatched), key=morton, payload=index. Not
|
|
288
|
+
radix — O(n log²n); the CSDLDF radix sort this plan builds (§4.2.1) is strictly better here.
|
|
289
|
+
5. **Rearrange** — gather elements into sorted order (so leaves need no indirection), optionally
|
|
290
|
+
persisting the inverse permutation in the AS for later refit-only updates.
|
|
291
|
+
6. **Hierarchy emission** — **Karras 2012**, cited by name in `BuildBVHSplits.hlsli`; one thread
|
|
292
|
+
per internal node; duplicate keys broken by index (same tiebreak as meep's CPU builder). Node
|
|
293
|
+
layout: internal nodes `[0, n-2]` (root = 0), leaves `[n-1, 2n-2]`. Children/parents written to
|
|
294
|
+
a separate 12-byte `HierarchyNode` scratch, not the final format.
|
|
295
|
+
7. **Treelet reordering** (BLAS only, 0/1/3 passes by build flag) — **Karras & Aila 2013**, cited
|
|
296
|
+
by name; treelet size 7, 128-subset SAH DP in groupshared memory, **one 32-thread group per
|
|
297
|
+
treelet**, constants `Cbox = 1.2` / `Ctri = 1.0` (the very constants in
|
|
298
|
+
`ebvh_optimize_treelet.js`). Three dispatches per pass: clear; find-treelets (bottom-up climb
|
|
299
|
+
with `InterlockedAdd` arrival counters, emitting base-treelet roots once a subtree holds ≥
|
|
300
|
+
`7·2^pass` primitives); reorder (form treelet by repeatedly expanding the largest-area leaf →
|
|
301
|
+
subset DP → rewrite links → climb to the parent treelet within the same dispatch).
|
|
302
|
+
8. **AABB fitting** — single-dispatch bottom-up refit: one thread per leaf climbs parent links;
|
|
303
|
+
`InterlockedAdd` on a per-node arrival counter; first arrival terminates, second proceeds with
|
|
304
|
+
both children guaranteed written. Buffers declared `globallycoherent`. Also the entire
|
|
305
|
+
refit-only update path (`PERFORM_UPDATE`): topology frozen, leaves written through the cached
|
|
306
|
+
sort permutation, only this pass re-runs.
|
|
307
|
+
|
|
308
|
+
Portability notes that shaped this plan: the builder uses **no wave intrinsics at all**
|
|
309
|
+
(threadgroup size 64 chosen as "safest across vendors") — everything except the sort is portable
|
|
310
|
+
compute; the load-bearing hazard is `globallycoherent` + relaxed-atomic handshakes for the refit
|
|
311
|
+
and treelet climbs, which WGSL can only approximate (§7, R1); the leaf-collapse half of their
|
|
312
|
+
treelet pass (multi-triangle leaves) was never finished (`MAX_TRIS_IN_LEAF 1`), so their shipped
|
|
313
|
+
trees are 1-primitive-per-leaf — same as meep's format; their per-node layout differs (center/
|
|
314
|
+
halfDim, leaf flag in the high bit) but nothing in their pipeline depends on it — the algorithms
|
|
315
|
+
transfer to meep's min/max + `child_1`-sentinel format unchanged.
|
|
316
|
+
|
|
317
|
+
### 3.2 AMD GPURT (`GPUOpen-Drivers/gpurt`, dev branch, 2026-08 snapshot)
|
|
318
|
+
|
|
319
|
+
The production reference — the shared BVH library under AMD's D3D12 and Vulkan RT drivers.
|
|
320
|
+
Verified against source (a full snapshot sits in the session scratchpad). What it teaches, sorted
|
|
321
|
+
by relevance to meep:
|
|
322
|
+
|
|
323
|
+
**Build-mode architecture.** One shared 64-byte scratch-BVH2 node representation; three builders
|
|
324
|
+
over it as a quality dial: `Linear` = **`BuildFastAgglomerativeLbvh.hlsl`** — an *Apetrei-style*
|
|
325
|
+
single-pass LBVH (bottom-up walk from leaves; parent chosen by comparing adjacent-code deltas;
|
|
326
|
+
first arrival does `InterlockedExchange` on a propagation flag and dies, second arrival owns the
|
|
327
|
+
parent and fits its AABB inline — no separate refit dispatch), with **the identical duplicate-key
|
|
328
|
+
index tiebreak meep's CPU builder uses** (`32 + clz(i ^ j)`); `PLOC` (TG = 256, radius ≤ 10,
|
|
329
|
+
LDS-windowed nearest-neighbour merging, phased task queue); `HPLOC` (TG = 32 = one wave, the
|
|
330
|
+
HPG 2024 algorithm of §3.3). Selection: fast-build and small builds → Linear; fast-trace → PLOC;
|
|
331
|
+
HPLOC as the single-dispatch compromise. **No treelet restructuring exists in GPURT, and the
|
|
332
|
+
older top-down/triangle-splitting builders have been removed from current branches** — the
|
|
333
|
+
quality story is entirely "better sort order + PLOC-family clustering + wide-node encode".
|
|
334
|
+
|
|
335
|
+
**Morton codes.** Default is **variable-bit** morton (axes sorted by extent; leading bits granted
|
|
336
|
+
to dominant axes before regular interleave — the productionised form of the adaptive scheme the
|
|
337
|
+
DXR Fallback left as dead code, and which meep's since-deleted `chunk_calculate_morton_code`
|
|
338
|
+
transcribed), with
|
|
339
|
+
optional surface-area "size bits" interleaved into 64-bit keys. 30-bit codes remain a supported
|
|
340
|
+
end-to-end path. Degenerate/inactive primitives get sentinel codes sorting to the end; identical
|
|
341
|
+
centroids quantise to 0.5.
|
|
342
|
+
|
|
343
|
+
**Sorting.** 4-bit-digit LSD radix (8 passes for 30-bit keys) with a decoupled-lookback scan *or*
|
|
344
|
+
barrier-based hierarchical scans as the portable fallback — and, notably, batched builds always
|
|
345
|
+
use an LDS **merge sort** instead ("merge sort outperforms radix sort in most cases in the
|
|
346
|
+
batched builder"; radix only wins in their parallel path above 50k primitives). Context for meep:
|
|
347
|
+
their radix is a weaker configuration than the OneSweep/CSDLDF family (the successors to exactly
|
|
348
|
+
this lookback-scan lineage); the datapoint says sort choice is not the quality lever — sort
|
|
349
|
+
*order* (morton scheme) is.
|
|
350
|
+
|
|
351
|
+
**Orchestration.** Two maintained execution strategies: a **batched multi-dispatch path** (one
|
|
352
|
+
dispatch per phase, real barriers between phases) and **BuildParallel** (the whole build in one
|
|
353
|
+
persistent-threads dispatch, phase-fenced by spin-loop "task loop" barriers on
|
|
354
|
+
`globallycoherent` buffers). Their own structure is the portability verdict: everything
|
|
355
|
+
single-dispatch leans on device-scope coherence plus inter-workgroup forward progress — neither
|
|
356
|
+
guaranteed in WebGPU — while **the multi-dispatch path is the portable template**, which is
|
|
357
|
+
precisely the shape meep's frame-graph pipeline takes. Small-build specialisations exist
|
|
358
|
+
(whole-build-in-one-workgroup for ≤1024 primitives with an in-LDS sort; a 16-thread trivial
|
|
359
|
+
builder for ≤16) — relevant later for per-geometry BLAS batching.
|
|
360
|
+
|
|
361
|
+
**TLAS specifics.** Instance encode fetches the BLAS root bounds (RDNA4: a tighter KDOP-derived
|
|
362
|
+
bound), transforms by the instance transform, writes scratch leaves, and atomically grows scene
|
|
363
|
+
bounds using the monotonic sign-flip float→u32 mapping + `InterlockedMin/Max` (the cheaper
|
|
364
|
+
alternative to meep's CAS chunk; both are portable WGSL). **Rebraid** runs before morton coding:
|
|
365
|
+
instances are opened into up to `rebraidFactor` (4) leaves pointing at BLAS root children, budget
|
|
366
|
+
distributed by a surface-area-difference priority — the production answer to the
|
|
367
|
+
"huge overlapping instance" quality failure LBVH-family TLAS builds share (§3.3). Updates are
|
|
368
|
+
pure refit via an update stack; topology is never touched — confirming the phase-3a shape.
|
|
369
|
+
|
|
370
|
+
**Wide-node encode.** BVH2 → BVH4/BVH8 collapse by repeatedly opening the child with the largest
|
|
371
|
+
surface area (`ApplyHeuristic`; 8-lane lane-groups cooperate per BVH8 node on RDNA4), producing
|
|
372
|
+
128-byte quantised 8-wide nodes (12-bit child bounds against a shared per-axis 8-bit exponent,
|
|
373
|
+
children implicitly contiguous). Different final format from CWBVH but the same collapse
|
|
374
|
+
philosophy and the same "BVH2 scratch is canonical, wide is an encode" architecture this plan
|
|
375
|
+
adopts.
|
|
376
|
+
|
|
377
|
+
**Float16/f32 box nodes, triangle pairing, OBBs, DGF** — BLAS-leaf compression machinery, out of
|
|
378
|
+
scope until phase 3b+, noted as the direction wide-format work eventually goes.
|
|
379
|
+
|
|
380
|
+
### 3.3 Construction literature and WebGPU state of the art
|
|
381
|
+
|
|
382
|
+
The field has converged, and the convergence is recent enough to matter:
|
|
383
|
+
|
|
384
|
+
- **Karras 2012** (binary radix tree from sorted morton codes; one thread per internal node,
|
|
385
|
+
range/split by longest-common-prefix searches) is the canonical GPU emission kernel — what the
|
|
386
|
+
DXR Fallback ships and what meep's CPU builder implements. The duplicate-key index tiebreak is
|
|
387
|
+
mandatory on real content (identical instance positions collide long before 2^30 cells fill).
|
|
388
|
+
- **Apetrei 2014** ("Fast and Simple Agglomerative LBVH Construction") inverts it: one thread per
|
|
389
|
+
leaf walks *upward*, finding each node's parent in O(1) from adjacent-code deltas; an
|
|
390
|
+
`atomicExchange` handshake lets the second-arriving thread continue and fit the parent's AABB on
|
|
391
|
+
the way — **hierarchy emission and refit fuse into one kernel**, no binary searches, same
|
|
392
|
+
topology as Karras (different internal-node index assignment; arXiv:2402.00665 shows how to
|
|
393
|
+
recover Karras indexing inside it if wanted). No spinning anywhere → no forward-progress
|
|
394
|
+
assumption. This is the "speed of light" binary builder in AMD's terminology and the substrate
|
|
395
|
+
H-PLOC is built on.
|
|
396
|
+
- **PLOC (2018) → PLOC++ (2022, Intel) → H-PLOC (2024, AMD)**: parallel locally-ordered
|
|
397
|
+
clustering — merge mutually-nearest neighbours within a search window of the morton-sorted
|
|
398
|
+
cluster list, giving near-agglomerative SAH quality. PLOC++ fuses each iteration into one kernel
|
|
399
|
+
(chunked shared-memory NN search, encoded-atomicMin pair propagation). **H-PLOC** composes
|
|
400
|
+
Apetrei's upward walk with wave-cooperative PLOC merging of per-subtree cluster lists (threshold
|
|
401
|
+
T = wave_size/2, search radius 8), emitting the whole BVH2 in a single kernel: PLOC-class
|
|
402
|
+
quality at ~15% over raw-LBVH build cost. It is **the production default in AMD's stack** —
|
|
403
|
+
GPURT, and RADV as of Mesa 26.0 (Jan 2026; measured ~1 ms/frame TLAS savings, 2–5% total fps in
|
|
404
|
+
shipped titles). Measured TLAS scale: a BVH4 over **1M randomly-sized instances in 2.21 ms** on
|
|
405
|
+
an RX 7900 XT (LBVH: 1.96 ms); ~0.25 ms extrapolated at 100k. GPU TLAS builds at meep's scale
|
|
406
|
+
are cheap in absolute terms regardless of algorithm.
|
|
407
|
+
- **Treelet restructuring (TRBVH 2013 / ATRBVH 2015) has been retired as a primary quality
|
|
408
|
+
mechanism by the field.** The H-PLOC paper's head-to-head: ATRBVH's BVH2 phase is **8.2–13×
|
|
409
|
+
slower than H-PLOC at indistinguishable SAH quality**, with structural waste (overlapping
|
|
410
|
+
treelets repeatedly rebuilt during the ascent). Where more quality is wanted, the literature
|
|
411
|
+
reaches for parallel reinsertion (PRBVH) offline, not treelets. This bears directly on phase 2
|
|
412
|
+
(§5.1, AR2).
|
|
413
|
+
- **How much quality is actually at stake** (Meister & Bittner, JCGT 2022, GPU, secondary/
|
|
414
|
+
incoherent rays, normalized to LBVH = 1.00): PLOC 1.25, ATRBVH 1.39, binned SAH 1.43, SBVH 1.86
|
|
415
|
+
— with per-scene extremes near 2× on mixed-primitive-size content. Coherent primary rays show
|
|
416
|
+
much smaller spreads, and **widening the BVH compresses the gap** (at width 8: PLOC 1.21, SBVH
|
|
417
|
+
1.54 vs LBVH 0.98) — i.e. the eventual BVH8 phase partially launders binary-topology quality
|
|
418
|
+
differences. LBVH's specific failure mode is *highly varying primitive sizes* (morton codes see
|
|
419
|
+
centroids only); for heavily-overlapping or long-thin instances no bottom-up TLAS builder saves
|
|
420
|
+
you — production drivers attack that with **rebraiding** (opening a BLAS root so one instance
|
|
421
|
+
contributes several TLAS entries), a later, orthogonal lever worth remembering.
|
|
422
|
+
- **Refit vs rebuild for TLAS**: NVIDIA's published best practice is to *rebuild* the TLAS per
|
|
423
|
+
frame rather than refit ("the cost savings to refit likely aren't worth sacrificing quality of
|
|
424
|
+
TLAS"); refit is for BLAS geometry that "bends but doesn't break". Matches this plan (§4, AR8,
|
|
425
|
+
phase 3a).
|
|
426
|
+
- **Wide BVH**: everyone builds binary and converts; nobody constructs BVH8 directly. The
|
|
427
|
+
reference conversion is Ylitie/Karras/Laine 2017 — a bottom-up cost DP deciding each node's role
|
|
428
|
+
(allowing SAH-decided multi-primitive leaves), then top-down emission of **CWBVH**: 80-byte
|
|
429
|
+
8-wide nodes with child boxes quantised to 8 bits/axis against a per-node origin/scale, 1.9–2.1×
|
|
430
|
+
faster incoherent traversal at 35–60% of the memory. meep's dormant `BVH8_INODE_STRUCT` is
|
|
431
|
+
80 bytes — the experiment already targets this format, and CWBVH traversal in WGSL is proven in
|
|
432
|
+
the wild (albedo / tinybvh-rs). H-PLOC's single-kernel converter carries a genuine
|
|
433
|
+
forward-progress hazard (documented in the paper — resident consumers spin on resident
|
|
434
|
+
producers); the boring per-level-dispatch collapse (~log₈N ≈ 5–7 launches) is formally safe and
|
|
435
|
+
the right WebGPU answer.
|
|
436
|
+
- **Sorting**: OneSweep (Adinets & Merrill 2022) is the single-pass state of the art and what
|
|
437
|
+
H-PLOC's own pipeline uses — but its bare decoupled lookback spins across workgroups; b0nes164
|
|
438
|
+
(whose port meep briefly carried before `b6049214c` removed it for exactly this) states it
|
|
439
|
+
"tends to run on anything that is not mobile, a software rasterizer, or Apple", and the
|
|
440
|
+
Linebender GPU-sorting survey reaches the same conclusion. The safe formulations are
|
|
441
|
+
reduce-then-scan (`DeviceRadixSort`) and — the one meep has standardised on — **CSDLDF**, the
|
|
442
|
+
decoupled-fallback protocol from b0nes164's Decoupled-Fallback paper, whose scan artifact is
|
|
443
|
+
already ported in-tree. The engine direction is to build the sort around that protocol
|
|
444
|
+
(§4.2.1). 32-bit keys are sufficient for TLAS at ≤1M instances; 64-bit keys (for dense BLAS
|
|
445
|
+
quality) cost ~3× in sort time and, in WGSL (no u64), mean chained two-word sorts — deferred
|
|
446
|
+
until phase 3b demands it.
|
|
447
|
+
- **WebGPU state of the art: essentially every public WebGPU ray tracer builds its BVH on the CPU**
|
|
448
|
+
(three-mesh-bvh — GPU traversal only; WebRTX; strahl — whose published *CPU* build times,
|
|
449
|
+
327–383 ms for 1M triangles, are the cautionary tale; gnikoloff/re-ovo path tracers; tinybvh's
|
|
450
|
+
WGSL consumers). A WGSL GPU *builder* is near-unoccupied territory. Best porting bases found:
|
|
451
|
+
MircoWerner/VkLBVH (GLSL Karras LBVH, transliterates to WGSL almost mechanically), NexusBVH
|
|
452
|
+
(clean CUDA H-PLOC), and Nate Morrical's H-PLOC in Slang (Slang can emit WGSL), which notably
|
|
453
|
+
expects an external radix sort and recommends OneSweep.
|
|
454
|
+
|
|
455
|
+
### 3.4 Synthesis — the choice
|
|
456
|
+
|
|
457
|
+
**Phase 1 builds LBVH: morton → CSDLDF radix sort → Karras emission → arrival-counter refit.
|
|
458
|
+
Phase 2 adds Karras & Aila treelet restructuring on top.** Reasons, in order of weight:
|
|
459
|
+
|
|
460
|
+
1. **Zero algorithm risk.** Both algorithms exist in-tree as tested CPU implementations
|
|
461
|
+
(`ebvh_build_hierarchy_radix`, `ebvh_optimize_treelet`) whose semantics — including the
|
|
462
|
+
duplicate-key tiebreak that is the classic LBVH correctness trap — the GPU kernels
|
|
463
|
+
transliterate. The DXR Fallback is a complete worked example of the same two algorithms as GPU
|
|
464
|
+
compute, in the same 32-byte-node, stack-traversal setting.
|
|
465
|
+
2. **Maximum reuse, one deliberate build.** Reduce idiom, atomic AABB merge, graph plumbing,
|
|
466
|
+
scan-with-fallback, validation tiers: existing meep facilities. The one sizeable new primitive
|
|
467
|
+
is the key-value radix sort — built around CSDLDF per the engine's sorting direction
|
|
468
|
+
(§4.2.1), as a first-class `gpu_primitive` whose first consumer is the builder and whose
|
|
469
|
+
spine/protocol come straight from the in-repo `shader_prefix_scan_csdldf`.
|
|
470
|
+
3. **Subgroups-first; portability through the fallback discipline, not through avoidance.**
|
|
471
|
+
Subgroups are a baseline requirement of the engine — every kernel here uses them where they
|
|
472
|
+
pay (wave pre-reduction before atomics in the bounds pass, ballot/prefix machinery in the
|
|
473
|
+
sort's ranking, wave-aggregated allocation and subset evaluation in the treelet DP), following
|
|
474
|
+
the csdldf shader's established idioms (`subgroup_size` builtin handling down to
|
|
475
|
+
`MIN_SUBGROUP_SIZE = 4`, `@diagnostic(off, subgroup_uniformity)` where the analysis
|
|
476
|
+
over-approximates). What keeps the pipeline safe on weakly-scheduled hardware is the CSDLDF
|
|
477
|
+
fallback protocol — never spin without a bounded fallback — not renouncing wave ops. (The DXR
|
|
478
|
+
Fallback's zero-wave-intrinsics posture was *its* portability answer for 2018 D3D12; it is not
|
|
479
|
+
this engine's.)
|
|
480
|
+
4. **Karras emission + separate refit over the fused Apetrei kernel — for phase 1.** Apetrei's
|
|
481
|
+
single fused kernel is objectively the leaner LBVH (§3.3) and the same topology. Karras wins
|
|
482
|
+
the *first* deliverable on de-risking grounds: the emission kernel is a pure, atomic-free
|
|
483
|
+
function of the sorted keys, which makes it (a) a line-for-line transliteration of the spec'd
|
|
484
|
+
in-repo oracle and (b) executable on the WGSL emulator in vitest — the strongest device-free
|
|
485
|
+
test this project can have, covering the classic LBVH correctness trap (duplicate-key
|
|
486
|
+
handling). The refit kernel it requires is not overhead: refit-only rebuilds (DXR's update
|
|
487
|
+
mode, phase 3a's skinned BLAS) need it as a standalone pass anyway. Apetrei is recorded as the
|
|
488
|
+
recognised follow-up optimisation (AR11) and the substrate H-PLOC builds on.
|
|
489
|
+
5. **PLOC/H-PLOC deferred, seam kept swappable — with eyes open.** The PLOC family (which AMD
|
|
490
|
+
ships; §3.2, §3.3) produces better trees than raw LBVH and H-PLOC does it at near-LBVH cost.
|
|
491
|
+
It is deferred, not dismissed: it is a genuinely new algorithm with no in-repo oracle, a
|
|
492
|
+
wave-*width*-dependent structure (its cluster threshold is defined in terms of the runtime
|
|
493
|
+
subgroup size — a different thing from merely using wave ops, which are baseline here), and
|
|
494
|
+
real WGSL-uniformity porting friction — while LBVH+treelets reaches today's CPU quality bar
|
|
495
|
+
with two oracles in hand. The pipeline's seam — "sorted leaf
|
|
496
|
+
array in, BVH2 topology out" — is exactly where an H-PLOC stage would replace emission + refit
|
|
497
|
+
+ treelets wholesale if the phase-2 gate argues for it (§6, AR2 carries the full argument and
|
|
498
|
+
the numbers).
|
|
499
|
+
6. **BVH2 stays the canonical intermediate.** All references converge on this: build a binary
|
|
500
|
+
scratch BVH, then encode wide/compressed formats from it. Phases 3–4 consume the same
|
|
501
|
+
intermediate this phase produces.
|
|
502
|
+
|
|
503
|
+
---
|
|
504
|
+
|
|
505
|
+
## 4. Phase 1 — GPU TLAS build
|
|
506
|
+
|
|
507
|
+
Replace steps 1–3 of `push_to_gpu()` with a GPU pipeline; keep the CPU dynamic tree for CPU
|
|
508
|
+
queries; keep the CPU upload path as a fallback flag until phase 2 retires it.
|
|
509
|
+
|
|
510
|
+
### 4.1 Inputs
|
|
511
|
+
|
|
512
|
+
- **`meshes` table** (scene database buffer) — world AABB per row, already GPU-resident and
|
|
513
|
+
CPU-written each frame for CPU-authority rows (`GPUSceneContext.build()` runs, and submits,
|
|
514
|
+
before `tlas.update()` — queue order guarantees visibility), GPU-written for GPU-authority rows.
|
|
515
|
+
- **Live rows list** — `array<u32>` of occupied `meshes` rows. `GPUInstancesAccelerationStructure`
|
|
516
|
+
already learns exactly this from `instance_add`/`instance_remove`; it keeps a compact
|
|
517
|
+
`Uint32Array` mirror and re-uploads it (small: 4·N bytes) only when membership churns
|
|
518
|
+
(`#membership_version`). Dead/reused rows never enter the build — no GPU-side liveness scheme is
|
|
519
|
+
invented.
|
|
520
|
+
- Uniforms: `element_count = N`.
|
|
521
|
+
|
|
522
|
+
Input AABBs come from the scene database rather than from the CPU tree's leaves deliberately: it
|
|
523
|
+
is the single source of truth, it is correct for GPU-authority instances (the CPU is not), and it
|
|
524
|
+
removes the per-frame dependency of the GPU build on CPU-side bounds bookkeeping. One-frame
|
|
525
|
+
staleness for GPU-authority bounds (refreshed later in the frame than `tlas.update()` runs) is
|
|
526
|
+
accepted in phase 1 — today's CPU build has *unbounded* staleness for those rows, so this is
|
|
527
|
+
strictly an improvement; the follow-up that moves TLAS encoding after the bounds-refresh point in
|
|
528
|
+
the frame is noted in §8.
|
|
529
|
+
|
|
530
|
+
### 4.2 Passes
|
|
531
|
+
|
|
532
|
+
New directory `src/shade/renderer/gpu_primitive/bvh/`, following the `graph_*`/`shader_*`/
|
|
533
|
+
`chunk_*` conventions. The pipeline is one `graph_bvh_build_lbvh({graph, leaf_bounds,
|
|
534
|
+
leaf_payload, element_count, nodes, root})` recorder plus a TLAS-specific extraction pass, so the
|
|
535
|
+
BLAS phase reuses everything downstream of extraction.
|
|
536
|
+
|
|
537
|
+
| # | pass | threads | what it does |
|
|
538
|
+
|---|---|---|---|
|
|
539
|
+
| 0 | `shader_bvh_extract_instance_leaves` | N | `row = rows[i]`; read `meshes[row].bounding_box`; write `leaf_bounds[i]` (6×f32) + `leaf_payload[i] = row`; merge into the 6-word scene-bounds cell via `subgroupMin/Max` pre-reduction → workgroup reduce → `chunk_aabb_atomic_f32` (the `shader_meshlet_bounds_reduce` pattern, subgroup-accelerated — GPURT's sign-flip monotonic `atomicMin/Max` encoding is the known cheaper alternative if the CAS ever shows up in a profile). Scene bounds seeded ±inf by an init clear. |
|
|
540
|
+
| 1 | `shader_bvh_morton` | N | centroid of `leaf_bounds[i]`, quantised 10 bits/axis into scene bounds, `morton = split_by_3(x) \| split_by_3(y)<<1 \| split_by_3(z)<<2` via a new `chunk_v3_morton_encode` mirroring `v3_morton_encode_magicbits` exactly; `keys[i] = morton`, `values[i] = i`. |
|
|
541
|
+
| 2 | `graph_radix_sort` | — | the rebuilt CSDLDF-based sort (§4.2.1). Keys 32-bit (30 used), values = leaf indices. Stable, ascending. |
|
|
542
|
+
| 3 | `shader_bvh_emit_hierarchy` | N | Transliteration of `ebvh_build_hierarchy_radix` with the DXR index layout: internal node k at node k (k ∈ [0, N-2], root = 0), leaf j at node (N-1)+j. Thread i writes leaf node (N-1)+i (`bounds = leaf_bounds[values[i]]`, `child_1 = BVH_NULL_NODE`, `child_2 = leaf_payload[values[i]]`); threads i < N-1 additionally run `determineRange`/`find_split` (`delta` with the `32 + countLeadingZeros(i^j)` duplicate tiebreak — `clz32` ⇔ `countLeadingZeros`) and write internal node i's children plus `parents[child] = i` into scratch. Pure per-thread function of the sorted keys: no barriers, no atomics, no subgroups. |
|
|
543
|
+
| 4 | `shader_bvh_refit` | N | One thread per leaf climbs `parents[]`; `atomicAdd` on a per-internal-node arrival counter (transient buffer, arrives zeroed); first arrival terminates, second recomputes the parent's AABB from its children and continues. All cross-workgroup-communicated words — node bounds and counters — are accessed through `array<atomic<u32>>` bindings (atomicLoad/atomicStore, f32 via bitcast), per the `shader_meshlet_bounds_reduce` idiom, never as plain loads/stores. **[deviation]** the `root = 0` header word is written by pass 3 instead — emission already writes plain node words and precedes the refit, and nothing reads the header in between. |
|
|
544
|
+
|
|
545
|
+
Edge cases handled at encode time on the CPU: `N == 0` → skip the graph, write `root =
|
|
546
|
+
BVH_NULL_NODE`. Consumers already handle `root == BVH_NULL_NODE` (empty CPU tree uploads it today).
|
|
547
|
+
|
|
548
|
+
**[deviation]** `N == 1` gets no special case at all. It falls out of the same four passes: the
|
|
549
|
+
sort has nothing to reorder and returns its input, emission's tail `if` skips the internal node and
|
|
550
|
+
writes leaf 0 at node 0 with `parents[0] = BVH_NULL_NODE`, and the refit's single thread reads that
|
|
551
|
+
terminator and stops. The host-written 36 bytes the plan called for would have had to take the
|
|
552
|
+
bounds from the CPU tree, whose copy for a GPU-authority row can be arbitrarily stale — the whole
|
|
553
|
+
reason this build reads the `meshes` table. The one thing `N == 1` actually needed was a counters
|
|
554
|
+
buffer WebGPU will accept, so it is sized `max(1, N-1)`.
|
|
555
|
+
|
|
556
|
+
### 4.2.1 The sort — `gpu_primitive/sort/`, rebuilt around CSDLDF
|
|
557
|
+
|
|
558
|
+
Commit `b6049214c` cleared the ground; this project re-founds the directory with one blessed
|
|
559
|
+
implementation. It keeps the deleted port's interface for continuity —
|
|
560
|
+
`graph_radix_sort({graph, keys, values, element_count}) → {keys, values}`, u32 keys + u32 values,
|
|
561
|
+
ascending, stable, result landing back in the caller's buffers — and is a first-class primitive
|
|
562
|
+
with its own spec and verify harness (the deleted `one_sweep.spec.js` /
|
|
563
|
+
`verify_radix_sort_one_sweep.js` in history are the direct templates for both).
|
|
564
|
+
|
|
565
|
+
**v1 — reduce-then-scan on the csdldf spine** (the shipping shape). LSD radix, 8-bit digits × 4
|
|
566
|
+
passes; per digit pass three dispatches: (a) per-tile 256-bin histogram (subgroup-accelerated,
|
|
567
|
+
workgroup atomics); (b) spine scan of the `tile × bin` counts, flattened column-major so one scan
|
|
568
|
+
yields global digit offsets and per-tile offsets together — run through
|
|
569
|
+
**`graph_prefix_scan_csdldf` as-is** (its inclusive-only output consumed exclusively by
|
|
570
|
+
subtracting own count; its `{count, elements: vec4u}` 16-byte-aligned layout adopted for the
|
|
571
|
+
spine); (c) rank-and-scatter, with the workgroup ranking machinery adapted from the deleted
|
|
572
|
+
binning shader in history (subgroup multi-split, and its two pinned WebGPU lessons carried over).
|
|
573
|
+
Every dispatch is bounded; the only spin anywhere is csdldf's own bounded-spin-then-fallback.
|
|
574
|
+
|
|
575
|
+
**[deviation]** the scatter's ranking is not the deleted port's per-wave-histogram multi-split. That
|
|
576
|
+
formulation needs `workgroup_size / subgroup_size * 256` words of workgroup memory, which at the
|
|
577
|
+
minimum subgroup size of 4 is 16 KB — the entire budget, with nothing left to stage keys in — which
|
|
578
|
+
is precisely why the deleted shader carried a whole second `WLT16` code path with bit-packed
|
|
579
|
+
half-radix histograms. What ships instead sorts the tile in workgroup memory with eight stable
|
|
580
|
+
binary splits (256 threads × 4 keys = a 1024-key tile, ~10.3 KB of workgroup memory at any subgroup
|
|
581
|
+
size). That makes a key's rank its sorted position, gets contiguous per-bin device writes for free,
|
|
582
|
+
and lets the tail tile's padding be identified by position alone. Subgroups carry the wide part of
|
|
583
|
+
each split's scan; the barrier-carrying loop over per-subgroup partials runs a *constant* trip count
|
|
584
|
+
rather than one derived from `subgroup_size`, so no barrier sits under a bound a uniformity analysis
|
|
585
|
+
has to reason about. Ballot-based peer aggregation in the histogram pass is the recorded upgrade
|
|
586
|
+
there; v1 uses workgroup atomics.
|
|
587
|
+
Roughly twelve smallish dispatches per sort against the removed port's seven — at TLAS scale
|
|
588
|
+
(≤1M keys) this is tens of microseconds of difference, and it is correct on every scheduler.
|
|
589
|
+
|
|
590
|
+
**v2 — single-pass digit binning with the fallback protocol** (recorded upgrade, not scheduled).
|
|
591
|
+
OneSweep's chained per-(tile, bin) lookback with the Decoupled-Fallback protocol generalised to
|
|
592
|
+
histogram vectors: split flag words per bin following the csdldf READY/INCLUSIVE rules, fallback
|
|
593
|
+
re-reducing the stuck predecessor tile's histogram from its keys. Pursued only if a measured
|
|
594
|
+
consumer (realistically: BLAS-scale triangle sorts, phase 3b) shows the spine passes matter.
|
|
595
|
+
|
|
596
|
+
The sort ships before the builder consumes it — its own verify harness green in the playground,
|
|
597
|
+
its shaders through all four validation tiers — so builder bring-up debugging never has to ask
|
|
598
|
+
"is it the sort?".
|
|
599
|
+
|
|
600
|
+
### 4.3 Output layout
|
|
601
|
+
|
|
602
|
+
`[u32 root = 0][internal 0 .. N-2][leaf N-1 .. 2N-2]`, `2N-1` nodes exactly. This differs from the
|
|
603
|
+
CPU path's slot-order-with-holes layout — legal, because (§2.1) nothing consumes ordering, only
|
|
604
|
+
format + root word. Root is node 0 by Karras's construction (internal node 0 spans the whole
|
|
605
|
+
range), which also natively satisfies the BLAS "root must be node 0, no header" convention when the
|
|
606
|
+
same pipeline is pointed at geometry in phase 3.
|
|
607
|
+
|
|
608
|
+
Buffers: the persistent output buffer keeps its `STORAGE | COPY_DST | COPY_SRC` usage and grows
|
|
609
|
+
via `ensure_buffer_capacity` to `4 + 32·(2N-1)`; the staging copy disappears (the refit writes
|
|
610
|
+
final bytes in place). Transients (graph-pooled): `leaf_bounds` 24N, `leaf_payload` 4N, keys/values
|
|
611
|
+
4N each (+ the sort's own internals), `parents` 4·(2N-1), `counters` 4·(N-1), scene bounds 24 B.
|
|
612
|
+
For N = 100k: ~5–6 MB transient, pooled and reused across frames — versus today's freshly
|
|
613
|
+
allocated ~6.4 MB staging buffer plus its full-buffer copy on every rebuild, and versus the CPU
|
|
614
|
+
milliseconds that dominate the current cost.
|
|
615
|
+
|
|
616
|
+
### 4.4 Integration in `GPUInstancesAccelerationStructure`
|
|
617
|
+
|
|
618
|
+
- `instance_add/remove/update` unchanged (CPU tree maintenance stays — `DirectionalLightShadowmap`
|
|
619
|
+
and any future CPU query walk it).
|
|
620
|
+
- `push_to_gpu()` becomes: ensure output capacity → refresh rows buffer if membership churned →
|
|
621
|
+
`new FrameGraph()` → import rows + scene database + output → record extraction +
|
|
622
|
+
`graph_bvh_build_lbvh` → `ShadeGPUCommandContext.create` / `encodeGraph` / `finish`. Same frame
|
|
623
|
+
position (inside `GPUSceneContext.update`, after `build()`), same submit-per-change semantics,
|
|
624
|
+
same version guard. **`ebvh_optimize_treelet` no longer runs on the CPU tree** — the tree stays
|
|
625
|
+
a plain SAH-insertion/rotation tree, which is what its CPU consumers actually need.
|
|
626
|
+
- ~~A module-level flag selects GPU vs legacy CPU upload~~ — **the legacy path is gone.** It
|
|
627
|
+
existed for one A/B; that A/B has been run (§4.6), the GPU build won on every axis at instance
|
|
628
|
+
scale, and the flag, the treelet call, the flatten and the whole-buffer upload went with it. It
|
|
629
|
+
was `TLAS_BUILD.use_gpu` — a settable field rather than the `const USE_GPU_TLAS_BUILD` boolean
|
|
630
|
+
the plan named, so the comparison could be a toggle in front of a running scene rather than an
|
|
631
|
+
edit and a reload. Anyone needing the comparison again recovers ~40 lines from history at
|
|
632
|
+
`23ab7bcd2`, the commit before the deletion. Subgroups are a baseline engine requirement, so there
|
|
633
|
+
is no "no-subgroups device" tier to serve: the legacy path (~40 lines) is deleted outright at
|
|
634
|
+
the phase-2 gate, flag and all.
|
|
635
|
+
|
|
636
|
+
**[deviation]** `GPUSceneContext` needs two lines. The TLAS is constructed with `graphics`
|
|
637
|
+
rather than `device` (the build has a frame graph to encode, which needs the buffer allocators), and
|
|
638
|
+
`update()` is handed `scene_database_buffer` — the table the world AABBs live in, which the TLAS has
|
|
639
|
+
no other way to reach. `GPUSceneContext`'s own API is unchanged, and consumers — every reader of
|
|
640
|
+
`tlas.buffer`, and `DirectionalLightShadowmap`'s reader of `tlas.bvh` — need no changes.
|
|
641
|
+
|
|
642
|
+
### 4.5 Validation
|
|
643
|
+
|
|
644
|
+
Four tiers, each doing the job only it can do:
|
|
645
|
+
|
|
646
|
+
1. **WGSL validity, vitest** (`bvh_lbvh.spec.js`, sort specs alongside): every new shader parses
|
|
647
|
+
(tree-sitter) and passes `validate_wgsl_source` as assembled — subgroup-using shaders
|
|
648
|
+
included; this is the tier that catches uniformity-adjacent assembly mistakes before a device
|
|
649
|
+
ever sees them.
|
|
650
|
+
2. **`ComputeShaderEmulator`, vitest — functions and chunks first, whole kernels where pure.**
|
|
651
|
+
The emulator is not a full device, and that is its value: it executes *specific WGSL
|
|
652
|
+
functions* against CPU oracles. Chunk-level: `chunk_v3_morton_encode` vs `v3_morton_encode`;
|
|
653
|
+
the emission kernel's `delta` / `determine_range` / `find_split` chunks vs their counterparts
|
|
654
|
+
inside `ebvh_build_hierarchy_radix` — each as a directly-invoked function over randomized and
|
|
655
|
+
adversarial inputs (duplicate-key runs — the historical failure mode — first among them).
|
|
656
|
+
Kernel-level: `shader_bvh_morton` and `shader_bvh_emit_hierarchy` executed point-wise
|
|
657
|
+
end-to-end (both are barrier- and atomic-free pure functions of their inputs, which the
|
|
658
|
+
emulator schedules faithfully); full-tree topology compared against the CPU builder. The
|
|
659
|
+
refit and sort-scatter kernels are atomics/subgroups-bound and are *not* forced through the
|
|
660
|
+
emulator — their chunks that are pure (bounds merge math, digit extraction, rank arithmetic)
|
|
661
|
+
still get chunk-level emulator coverage.
|
|
662
|
+
3. **`SoftwareGPUDevice`, vitest — the orchestration tier.** Encode the real thing against the
|
|
663
|
+
recording device: `GPUInstancesAccelerationStructure.push_to_gpu()` and
|
|
664
|
+
`graph_bvh_build_lbvh` run their actual FrameGraph against a `SoftwareGPUDevice`, and the spec
|
|
665
|
+
asserts what was encoded — dispatch sequence and group counts per pass, bind groups resolving
|
|
666
|
+
to the intended buffers, transient sizes/usages/clears, the rows-list upload happening only on
|
|
667
|
+
membership churn, and the `N == 0` / `N == 1` short-circuits (graph skipped, root word written
|
|
668
|
+
host-side — bytes the host wrote are assertable here; shader-written bytes are not, by the
|
|
669
|
+
mock's own contract). This tier pins the integration and the graph wiring in CI, where neither
|
|
670
|
+
the emulator (no host path) nor the browser (no CI) can.
|
|
671
|
+
4. **Browser playground + end-to-end** — **[not run]** this tier is the human's; meep is a
|
|
672
|
+
library and the playground mounts in the host repo. Both harnesses are in place and registered.
|
|
673
|
+
(`verify_gpu_bvh_build.js` and `verify_radix_sort.js`, registered under "review checks"): randomized instance sets (duplicate positions, degenerate
|
|
674
|
+
AABBs, N ∈ {0, 1, 2, 3, powers of two ± 1, ~100k}); readback via `graph_inspect_gpu_buffer`;
|
|
675
|
+
assert structural validity (every leaf reachable exactly once; both children valid on every
|
|
676
|
+
internal node; parent AABB ⊇ children; root word correct), payload permutation correctness,
|
|
677
|
+
**depth histogram** (§7, R4), and SAH cost vs the CPU oracle on the same input — same sorted
|
|
678
|
+
order fed to both ⇒ topology must match node-for-node modulo the index mapping. Then the real
|
|
679
|
+
consumers: path tracer + RTX shadows on existing playground scenes, CPU-built vs GPU-built A/B
|
|
680
|
+
(the flag), image diff + `GPUTimer` numbers for build and trace.
|
|
681
|
+
|
|
682
|
+
A `bvh_format.spec.js` locking the byte contract of §2.1 (today enforced only by the shaders
|
|
683
|
+
themselves) is added alongside — it pins both the legacy compressor and the GPU layout to the same
|
|
684
|
+
golden bytes.
|
|
685
|
+
|
|
686
|
+
### 4.6 Acceptance
|
|
687
|
+
|
|
688
|
+
- All four validation tiers green (sort and builder both); traversal-consumer A/B images
|
|
689
|
+
identical-or-better.
|
|
690
|
+
- Depth histogram within the traversal stack budget on benchmark scenes — no silent traversal
|
|
691
|
+
truncation (R4).
|
|
692
|
+
- Build time for 100k instances well under the CPU path's. Calibration from the literature
|
|
693
|
+
(§3.3): AMD measures a full quality-build TLAS over 1M instances at 2.2 ms on an RX 7900 XT,
|
|
694
|
+
~0.25 ms extrapolated at 100k; raw LBVH ~15% cheaper still. WebGPU overheads on top are exactly
|
|
695
|
+
the novel data point this phase produces — measure with `GPUTimer`, don't assume.
|
|
696
|
+
|
|
697
|
+
**Measured, 2026-08-26** — `verify_gpu_bvh_build` and `verify_gpu_tlas_build` on one machine, all
|
|
698
|
+
58 builder cases and all 8 TLAS scenes PASS. One machine, one browser, and a harness that makes a
|
|
699
|
+
separate submission per case: treat these as an order-of-magnitude reading, not a benchmark.
|
|
700
|
+
|
|
701
|
+
- **Topology parity is exact.** Every case's SAH matches the CPU oracle's to the last reported
|
|
702
|
+
decimal, at every size and every shape — which is the node-for-node claim, since the SAH is
|
|
703
|
+
summed over the same internal nodes. Including 100k coincident instances.
|
|
704
|
+
- **Build time, 100k scattered instances: ~0.15 ms** of GPU time across all 19 dispatches (morton
|
|
705
|
+
0.003, sort ~0.082 over four digit passes, emission 0.026, refit 0.040). That sits where §3.3's
|
|
706
|
+
extrapolation put a driver-class builder (~0.2 ms), which is the answer to "what do WebGPU
|
|
707
|
+
overheads cost" — apparently not much. Two outliers in the log (a 0.24 ms first scan and a
|
|
708
|
+
0.58 ms readback copy in one case) do not recur across the other three digit passes of the same
|
|
709
|
+
build and look like queue bubbles between the harness's per-case submissions; a real number for
|
|
710
|
+
the frame budget has to come from the in-frame path, not from here.
|
|
711
|
+
- **The duplicate-key tiebreak works at scale.** 100,003 instances at *identical* positions — one
|
|
712
|
+
Morton code for the entire scene — build to depth 18, not depth 100,003. The depth histogram
|
|
713
|
+
(`8:1 9:2 12:32 13:128 14:1536 18:98304`, summing to exactly 100,003) is the binary decomposition
|
|
714
|
+
of the index range showing through, which is what `delta`'s `32 + clz32(i ^ j)` produces when no
|
|
715
|
+
code carries information. This is the failure mode the whole emission kernel is careful about,
|
|
716
|
+
now observed on a device.
|
|
717
|
+
- **The ±inf seeding is confirmed.** The `far from origin` TLAS scene (700 instances in a 10-unit
|
|
718
|
+
cube offset ~9000 units from the origin) builds to a normal depth spread of 8..16. A zeroed
|
|
719
|
+
scene-bounds cell would have anchored the bounds at the origin, crushed every centroid into one
|
|
720
|
+
Morton cell, and produced the flat single-depth histogram the coincident case shows.
|
|
721
|
+
- **Depth.** 100k scattered instances build to **depth 22 of the 32-entry stack**, and 20k to
|
|
722
|
+
depth 20. The stack is shared with the BLAS within one query and the occlusion path truncates
|
|
723
|
+
silently past it, so this is a real output constraint — but see the A/B below, where the legacy
|
|
724
|
+
CPU tree turns out to be the *deeper* of the two.
|
|
725
|
+
|
|
726
|
+
**Consumer A/B, 2026-08-26** — `sponza_path_trace`, path tracer, same camera, same frame, toggling
|
|
727
|
+
`TLAS_BUILD.use_gpu` under the running scene. Images verified equivalent by inspection. Frame times
|
|
728
|
+
are medians of three runs; SAH is the sum of internal-node surface areas, which over a fixed leaf
|
|
729
|
+
set is the term that differs between two trees.
|
|
730
|
+
|
|
731
|
+
Sponza as it ships — **103 instances**:
|
|
732
|
+
|
|
733
|
+
| | GPU LBVH | CPU pipeline (insertion + rotations + treelets) |
|
|
734
|
+
|---|---|---|
|
|
735
|
+
| SAH | 43,409 | 22,414 |
|
|
736
|
+
| max depth | 13 | 20 |
|
|
737
|
+
| traced frame | 69.5 ms | 70.6 ms |
|
|
738
|
+
|
|
739
|
+
Sponza plus **20,000 scattered boxes** at sizes spanning two orders of magnitude — the shape that
|
|
740
|
+
troubles a Morton order most, and enough instances that traversal cost is in the TLAS rather than
|
|
741
|
+
in the per-mesh BLAS:
|
|
742
|
+
|
|
743
|
+
| | GPU LBVH | CPU pipeline |
|
|
744
|
+
|---|---|---|
|
|
745
|
+
| SAH | 713,765 | 941,015 |
|
|
746
|
+
| max depth | 20 | 27 |
|
|
747
|
+
| traced frame | **132.6 ms** | 176.1 ms |
|
|
748
|
+
| rebuild, wall clock | **21.7 ms** | 161.2 ms |
|
|
749
|
+
|
|
750
|
+
Read together these say something the plan did not anticipate. At ~100 instances the GPU tree's SAH
|
|
751
|
+
is 1.9× the CPU tree's and the traced frame times are identical — the regression AR1 predicted,
|
|
752
|
+
and invisible, because a TLAS that small is not what traversal spends its time in. At 20k the
|
|
753
|
+
ordering **reverses on every axis**: the GPU tree is 24% better by SAH, seven levels shallower, and
|
|
754
|
+
25% faster to trace.
|
|
755
|
+
|
|
756
|
+
Why: the CPU tree is built by incremental SAH-guided insertion with local rotations. At a hundred
|
|
757
|
+
instances that is close to optimal. At twenty thousand it is a greedy, order-dependent construction
|
|
758
|
+
whose mistakes rotations only locally repair, while the LBVH's global Morton ordering does not
|
|
759
|
+
degrade. The treelet pass on top buys 2.4% (22,954 → 22,414 over two passes on the 103-instance
|
|
760
|
+
scene) — it repairs neighbourhoods, not a bad global shape.
|
|
761
|
+
|
|
762
|
+
Note also the depth column: at 20k the *legacy* tree reaches depth 27 of a 32-entry stack shared
|
|
763
|
+
with the BLAS, leaving five. R4's cliff is real, and the GPU build moves away from it rather than
|
|
764
|
+
toward it.
|
|
765
|
+
- Known, documented, temporary quality dip vs the treelet-optimised CPU tree (raw LBVH SAH cost is
|
|
766
|
+
worse; phase 2 exists to close exactly this). The flag stays available until phase 2's gate.
|
|
767
|
+
|
|
768
|
+
---
|
|
769
|
+
|
|
770
|
+
## 5. Phases 2–4
|
|
771
|
+
|
|
772
|
+
### 5.1 Phase 2 — tree quality optimisation on the GPU
|
|
773
|
+
|
|
774
|
+
> **Checkpoint, 2026-08-26 — the premise did not survive measurement.** This phase was committed to
|
|
775
|
+
> on the expectation that phase 1 ships a quality regression which treelet restructuring then closes
|
|
776
|
+
> (AR1, AR2, R3). §4.6's A/B says otherwise: at instance scale the GPU LBVH is *better* than the
|
|
777
|
+
> CPU pipeline it replaced — 24% better SAH, seven levels shallower, 25% faster to trace — and at
|
|
778
|
+
> the small end where it is worse by SAH, the difference does not reach the frame. The gate this
|
|
779
|
+
> phase was to be judged against ("SAH cost within noise of the current CPU pipeline, and
|
|
780
|
+
> traversal-heavy consumers at parity or better frame time") is **already met, without treelets**.
|
|
781
|
+
>
|
|
782
|
+
> What follows is therefore no longer a committed plan. It is what phase 2 *would* be, kept because
|
|
783
|
+
> the reasoning is still sound where a case for it exists, and the decision belongs to whoever picks
|
|
784
|
+
> the project up next. The three ways forward, in the order the evidence supports them:
|
|
785
|
+
>
|
|
786
|
+
> 1. **Do nothing here and delete the legacy path.** — **taken.** The legacy CPU upload and
|
|
787
|
+
> `TLAS_BUILD` are gone; phase 3 is the next work.
|
|
788
|
+
> 2. **Take the quality work to the BLAS instead.** Everything above is a statement about a TLAS —
|
|
789
|
+
> thousands of leaves, and leaf *boxes* rather than leaf shape dominating. A BLAS is millions of
|
|
790
|
+
> triangles, where the literature's LBVH-vs-PLOC gap (§3.3) was measured and where it should
|
|
791
|
+
> actually bite. If PLOC/H-PLOC is worth building, that is where its case is.
|
|
792
|
+
> 3. **Build it anyway, for a workload not yet measured.** Content with far more instances, or with
|
|
793
|
+
> heavy overlap, could still find the LBVH's weak spot. That case has to be produced and measured
|
|
794
|
+
> first; `sponza_path_trace`'s box-scatter control is the tool for it.
|
|
795
|
+
>
|
|
796
|
+
> The seam holds whichever of the rest is picked up later: "sorted leaf array in, BVH2 topology out" is where any of this plugs
|
|
797
|
+
> in, and nothing about extract/morton/sort or the output contract has to move.
|
|
798
|
+
|
|
799
|
+
The treelet port, in the DXR Fallback's GPU formulation (§3.1, step 7), appended to the phase-1
|
|
800
|
+
graph after refit:
|
|
801
|
+
|
|
802
|
+
- **Pass A — clear**: zero treelet counters/list.
|
|
803
|
+
- **Pass B — find base treelets**: one thread per leaf climbs `parents[]` with arrival counters
|
|
804
|
+
(the refit trick again), accumulating subtree primitive counts; on crossing the
|
|
805
|
+
`min_prims_per_treelet` threshold, appends the node to the base-treelet list via an atomic
|
|
806
|
+
cursor and stops.
|
|
807
|
+
- **Pass C — reorder**: one **32-thread workgroup per base treelet**: form the 7-leaf treelet by
|
|
808
|
+
greedy largest-area expansion; evaluate all 128 subset AABBs/areas cooperatively; run the
|
|
809
|
+
subset-partition DP (`partition = (partition - delta) & s` enumeration — identical to the CPU
|
|
810
|
+
code); rewrite the ≤6 internal nodes' links and bounds; climb to the parent treelet via arrival
|
|
811
|
+
counters and repeat. Workgroup memory is the correctness layer (the DP must not assume a wave
|
|
812
|
+
width — subgroup size ranges 4–128 at runtime); subgroup reductions/broadcasts accelerate the
|
|
813
|
+
subset-area evaluation and argmin steps per the csdldf idioms. All cross-group traffic through
|
|
814
|
+
`array<atomic<u32>>`, as in refit.
|
|
815
|
+
- Iterations: quality knob, default 1 per build.
|
|
816
|
+
|
|
817
|
+
Treelets were also the answer to the depth risk (§7, R4). That reading has changed too: the
|
|
818
|
+
measured depths put the GPU build *below* the legacy tree, so shortening chains is no longer a
|
|
819
|
+
reason to reach for them.
|
|
820
|
+
|
|
821
|
+
### 5.2 Phase 3 — BLAS on the GPU
|
|
822
|
+
|
|
823
|
+
Two sub-phases, in value order:
|
|
824
|
+
|
|
825
|
+
- **3a — skinned-clone BLAS refit (per frame). DONE.** Built as planned, refit-only, with the
|
|
826
|
+
phase-1 refit pass unchanged. Four deviations, all recorded here because the code is what won:
|
|
827
|
+
|
|
828
|
+
- **The extraction pass writes node bounds directly, not a `leaf_bounds` array.** A refit has no
|
|
829
|
+
sort and no emission, so nothing consumes a separate array — `shader_blas_refit_leaf_bounds`
|
|
830
|
+
puts each triangle's box straight into the leaf node and the climb reads it there. It gets the
|
|
831
|
+
triangle id by reading the leaf's own `child_2` payload back, so no side table has to agree
|
|
832
|
+
with the tree about which leaf is which.
|
|
833
|
+
- **A clone gets its own *nodes*, but shares its source's *shape*.** Parents and leaf-node
|
|
834
|
+
indices are identical across every copy of a geometry — only bounds are per-instance — so
|
|
835
|
+
`GPUGeometryBVHManager` derives them once per source into two arenas of their own
|
|
836
|
+
(`bvh2_derive_topology`, which walks the compressed bytes from the root so the free list's
|
|
837
|
+
holes are never mistaken for leaves) and every copy points at that.
|
|
838
|
+
- **The BLAS map is keyed by geometry *id*, not by geometry.** A clone and its source share a
|
|
839
|
+
`MeshletGeometry` and collided on it. `obtain(owner, source)` now separates the identity a tree
|
|
840
|
+
is looked up by from the geometry it takes its shape from. This also fixed a live bug in the
|
|
841
|
+
`blas_lookup` table (`blas_lookup_indexing.spec.js`, commit `f1f247438`).
|
|
842
|
+
- **Both settings structs carry arena offsets.** `parents_offset` / `leaf_nodes_offset` join
|
|
843
|
+
`node_word_offset` for the same reason it exists: a build owns its arrays and passes 0, a refit
|
|
844
|
+
is a tenant in shared ones. Counters are sized by *node*, not by leaf — they are indexed by
|
|
845
|
+
node id, and a tree read off an arena numbers its nodes with holes in.
|
|
846
|
+
|
|
847
|
+
Verified on an NVIDIA Lovelace device by `src/shade/playground/skinned_blas_refit/`, which holds
|
|
848
|
+
the refit's root box against the bounds-refresh chain's independently computed box over the same
|
|
849
|
+
deformed vertices — two paths sharing no code, and `min`/`max` do not round, so the bar is bit
|
|
850
|
+
equality. Result: bit-identical at every sample, moving 0.39–0.80 of its own span between poses,
|
|
851
|
+
and all 28 105 internal nodes exactly their own children's union, which is what says the interior
|
|
852
|
+
holds and not merely the root. Four clones, four distinct arena regions on a uniform stride.
|
|
853
|
+
|
|
854
|
+
**Still open:** `shader_build_sdf` reads `blas_lookup[slot_index]` with an SDF atlas slot — a
|
|
855
|
+
third index space over a table that is now unambiguously indexed by geometry id.
|
|
856
|
+
- **3b — full BLAS build (import/procedural).** Same `graph_bvh_build_lbvh` with a triangle
|
|
857
|
+
extraction pass (`leaf_payload = (meshlet << 8) | tri`, computed at extraction). Root lands at
|
|
858
|
+
node 0 with no header — the exact BLAS convention — because Karras emission puts it there. The
|
|
859
|
+
current CPU builder shapes the tree as meshlet-subtrees-under-a-top-tree; **no consumer depends
|
|
860
|
+
on that internal shape** (only on root-at-0 + the leaf payload encoding), so the GPU build's
|
|
861
|
+
global-morton tree is format-legal and expected to trace better. Verify `shader_build_sdf`'s
|
|
862
|
+
root-bounds read and the serialization adapter round-trip (GPU-built BLAS read back once at
|
|
863
|
+
import time for the on-disk cache) before flipping. Batch small geometries through one graph;
|
|
864
|
+
use `graph_build_indirect_dispatch_command` where counts live GPU-side.
|
|
865
|
+
|
|
866
|
+
### 5.3 Phase 4 — BVH8
|
|
867
|
+
|
|
868
|
+
Traversal-side artifacts already exist (`bvh8/webgpu/BVH8_INODE_STRUCT.js` — 80-byte compressed
|
|
869
|
+
wide node — and `chunk_bvh8_node_intersect`, `chunk_bvh8_ray_query_blas_occlusion`,
|
|
870
|
+
`chunk_ray_get_octant_inv4`, `chunk_sign_extend_s8x4`), currently unwired. The build side follows
|
|
871
|
+
the references' shared design: **BVH2 stays the construction intermediate; BVH8 is an encode.**
|
|
872
|
+
|
|
873
|
+
- **Collapse pass(es)**: bottom-up cost DP over the BVH2 (arrival-counter climb again) deciding
|
|
874
|
+
each node's role in the wide tree per Ylitie et al. 2017 (SAH-driven collapse to ≤8 children,
|
|
875
|
+
which is also where SAH-decided multi-primitive leaves enter, if the leaf format ever grows
|
|
876
|
+
them), then emission of the 80-byte compressed nodes — meep's dormant `BVH8_INODE_STRUCT` is
|
|
877
|
+
already CWBVH-shaped, and CWBVH traversal in WGSL is proven in the wild (§3.3). Conversion runs
|
|
878
|
+
as **per-level dispatches** (~log₈N launches, formally-safe inter-dispatch ordering) rather than
|
|
879
|
+
H-PLOC's single-kernel polling converter, which the paper itself flags for its forward-progress
|
|
880
|
+
requirement — the one place this plan deliberately declines a single-kernel formulation.
|
|
881
|
+
Quantised-box packing needs no `shader-f16` device feature (`pack`-family builtins suffice).
|
|
882
|
+
GPURT's encoder (§3.2) confirms the collapse heuristic — repeatedly open the child with the
|
|
883
|
+
largest surface area until the slots fill — and demonstrates the cooperative refinement
|
|
884
|
+
(8-lane groups per wide node) available if the per-level formulation ever needs the speed.
|
|
885
|
+
- The JCGT data (§3.3) is the planning caveat worth repeating here: widening compresses the
|
|
886
|
+
quality differences between binary builders — so re-run the phase-2 quality debate's numbers on
|
|
887
|
+
BVH8 before spending further on binary-topology quality.
|
|
888
|
+
- Ships **additively**: BVH8 buffers alongside BVH2, consumers migrate per-shader (occlusion /
|
|
889
|
+
RTX shadows first — the existing chunk is the occlusion variant), format compatibility preserved
|
|
890
|
+
throughout; BVH2 bindings retire only where no consumer remains.
|
|
891
|
+
|
|
892
|
+
---
|
|
893
|
+
|
|
894
|
+
## 6. Adversarial review
|
|
895
|
+
|
|
896
|
+
The plan above already absorbed several rounds of self-challenge; the surviving objections and
|
|
897
|
+
their dispositions, kept honest:
|
|
898
|
+
|
|
899
|
+
**AR1 — "You're building LBVH in 2026; raw LBVH trees are noticeably worse than SAH trees, and
|
|
900
|
+
phase 1 ships a quality regression."** **Measured, and largely wrong** — see §4.6's A/B: the
|
|
901
|
+
regression appears only at ~100 instances, where it does not reach the frame, and reverses at 20k,
|
|
902
|
+
where the GPU tree beats the CPU pipeline on SAH, depth and traced frame time alike. The objection
|
|
903
|
+
was right that raw LBVH is worse than a *good* SAH tree; what it missed is that incremental
|
|
904
|
+
insertion at scale does not produce one. Original disposition, kept for the record:
|
|
905
|
+
the regression is temporary by design (phase 2 is the same PR series), bounded (TLAS-over-
|
|
906
|
+
instances is the most LBVH-tolerant tier — leaf count is small and leaf AABBs, not leaf shape,
|
|
907
|
+
dominate), measured (SAH + frame-time gates, not vibes), and reversible (flag). The alternative —
|
|
908
|
+
shipping treelets in the same deliverable — was rejected for review size, not difficulty; the
|
|
909
|
+
phases share every buffer.
|
|
910
|
+
|
|
911
|
+
**AR2 — "The field retired treelets. AMD ships H-PLOC. You researched this and still plan a TRBVH
|
|
912
|
+
port — that's sentimentality about in-repo code, not engineering."** The strongest objection in
|
|
913
|
+
this review, and it gets the full numbers. Against the treelet plan: H-PLOC delivers ATRBVH-class
|
|
914
|
+
quality with its BVH2 phase **8.2–13× faster** than the treelet approach (same codebase, same
|
|
915
|
+
GPU, AMD's own measurements); RADV replaced PLOC with H-PLOC as the shipping default in Mesa 26.0;
|
|
916
|
+
treelets have a documented GPU-stability history (the DXR pass carries "TDRs on AMD" workaround
|
|
917
|
+
flags); overlapping treelets are rebuilt repeatedly during the ascent by construction. For the
|
|
918
|
+
treelet plan: the transliteration-with-oracle argument is not sentiment — `ebvh_optimize_treelet`
|
|
919
|
+
has a spec suite pinning the exact DP, the DXR Fallback is a complete worked GPU example of the
|
|
920
|
+
same pass, the DP's cooperation pattern is workgroup-shaped and indifferent to wave width
|
|
921
|
+
(whereas H-PLOC's cluster-list threshold is defined *in terms of* the wave size, which in WGSL is
|
|
922
|
+
a runtime value ranging 4–128 — dynamic `T = subgroup_size/2` handling, degenerate at the low
|
|
923
|
+
end, plus known uniformity-analysis friction; subgroups being the engine baseline makes wave ops
|
|
924
|
+
free to *use*, but it does not make wave-*width-dependent algorithm structure* free to port), and
|
|
925
|
+
at TLAS scale the 8–13× speed difference is ~0.5 ms vs ~0.06 ms — real but not decision-grade,
|
|
926
|
+
while implementation risk is. Both paths clear
|
|
927
|
+
the actual gate (SAH parity with today's CPU trees — the acceptance test is SAH-based either way,
|
|
928
|
+
so the oracle advantage matters for *debugging*, not for *acceptance*). Disposition: treelet port
|
|
929
|
+
stays the committed phase-2 path per the stated goal sequencing; **phase 2 opens with an explicit
|
|
930
|
+
decision checkpoint** re-weighing H-PLOC (porting bases exist: NexusBVH in CUDA, Morrical's Slang
|
|
931
|
+
implementation that can emit WGSL) now that phase 1 will have produced our own SAH/depth/frame
|
|
932
|
+
numbers on our own scenes; and the seam is pre-committed so that whichever loses can be replaced
|
|
933
|
+
without touching extract/morton/sort or the output contract. If meep had no in-repo treelet
|
|
934
|
+
implementation, H-PLOC would be the phase-2 recommendation outright.
|
|
935
|
+
|
|
936
|
+
**AR3 — "Cross-workgroup relaxed-atomic handshakes (refit, treelet climbs) are formally racy
|
|
937
|
+
under the WGSL memory model — WGSL atomics are relaxed, there is no `globallycoherent`, no
|
|
938
|
+
device-scope barrier."** Correct, and this is the plan's most serious technical risk (§7, R1).
|
|
939
|
+
Disposition: (a) every production GPU builder ships this pattern (DXR: `InterlockedAdd` +
|
|
940
|
+
`globallycoherent`; GPURT likewise), and D3D's guarantees here are not materially stronger than
|
|
941
|
+
WGSL's; (b) the engine's line, drawn when `b6049214c` removed OneSweep, is precise and this
|
|
942
|
+
design respects it: what was rejected was **unbounded waiting on another workgroup's progress**,
|
|
943
|
+
not cross-workgroup communication through relaxed atomic words — csdldf itself communicates
|
|
944
|
+
exactly that way, with bounded spin and a fallback. The refit/treelet climbs sit on the accepted
|
|
945
|
+
side: arrival-counter handshakes have no spin at all (the losing thread exits; the winner's
|
|
946
|
+
inputs are already written), so they assume atomic-word *visibility*, never scheduling fairness;
|
|
947
|
+
(c) discipline: all cross-group-communicated state (bounds, counters, links during climbs) goes
|
|
948
|
+
through `array<atomic<u32>>` accesses only, never plain loads/stores gated by an atomic — the
|
|
949
|
+
strictest formulation available in WGSL; (d) the browser verify harness asserts structural
|
|
950
|
+
validity per platform, and a formally-clean fallback (iterative per-level dispatches —
|
|
951
|
+
inter-dispatch ordering is guaranteed by WebGPU) is sketched and cheap to add if a platform ever
|
|
952
|
+
misbehaves. Not hand-waved: named, bounded, fallback designed.
|
|
953
|
+
|
|
954
|
+
**AR4 — "The 32-slot traversal stack is shared TLAS+BLAS and your LBVH can exceed the depth the
|
|
955
|
+
current balanced trees keep. Silent geometry drop-out is the failure mode."** The sharpest finding
|
|
956
|
+
of the consumer audit (§2.1). Worst-case Karras depth over 30-bit keys with index tiebreaks is
|
|
957
|
+
~30 + log₂(duplicate-run), far beyond 31 in adversarial clustering; today's rotation-balanced
|
|
958
|
+
dynamic tree keeps ~1.44·log₂N. Disposition: depth histogram is a first-class assertion in the
|
|
959
|
+
verify harness and an A/B metric on real scenes (phase 1 gate includes "no traversal truncation on
|
|
960
|
+
benchmark scenes"); treelet restructuring (phase 2) actively shortens chains; and if real content
|
|
961
|
+
ever approaches the cliff, `BVH_TRAVERSAL_STACK_SIZE` is a single constant whose 32→64 cost will
|
|
962
|
+
be measured rather than feared. Note the cliff is *pre-existing* (deep TLAS+BLAS combinations can
|
|
963
|
+
overflow today); the builder makes it measurable for the first time.
|
|
964
|
+
|
|
965
|
+
**AR5 — "Reading instance AABBs from the scene database couples the TLAS to renderer-internal
|
|
966
|
+
tables; the CPU tree's leaves were a clean input."** The coupling already exists — TLAS leaf
|
|
967
|
+
payloads *are* `meshes`-table rows, consumed by `scene_read_mesh` in every traversal; bounds from
|
|
968
|
+
the same row is less coupling, not more (one source of truth instead of two). And CPU-side bounds
|
|
969
|
+
are simply wrong for GPU-authority rows. The builder core stays decoupled regardless: extraction
|
|
970
|
+
is the only scene-aware pass; `graph_bvh_build_lbvh` itself takes flat `leaf_bounds`/
|
|
971
|
+
`leaf_payload` and serves BLAS unchanged.
|
|
972
|
+
|
|
973
|
+
**AR6 — "Keeping the CPU BVH is duplicated state — kill it."** Rejected: `DirectionalLightShadowmap`
|
|
974
|
+
queries it per cascade per frame on the CPU, and the acoustic/probe systems use the same `BVH`
|
|
975
|
+
class independently. What *does* die: the per-frame CPU treelet pass over it (phase 1), the
|
|
976
|
+
full-tree flatten+upload (phase 1), and the legacy upload path plus its flag (phase 2). The CPU
|
|
977
|
+
tree reverts to what it structurally is — a cheap incremental index for CPU queries.
|
|
978
|
+
|
|
979
|
+
**AR7 — "The pipeline's backbone is a sort with a forward-progress assumption (tilers, Apple)."**
|
|
980
|
+
This objection **won**, pre-emptively: the engine removed the fallback-less OneSweep port
|
|
981
|
+
(`b6049214c`) and set the direction — build around CSDLDF. The sort is now a deliverable of this
|
|
982
|
+
plan (§4.2.1) whose v1 has no unbounded spin anywhere except inside csdldf's own
|
|
983
|
+
bounded-spin-then-fallback protocol, making it correct on every scheduler b0nes164's warning
|
|
984
|
+
covers ("tends to run on anything that is not mobile, a software rasterizer, or Apple" — §3.3,
|
|
985
|
+
about the removed formulation). The cost is honest: three dispatches per digit instead of one,
|
|
986
|
+
tens of microseconds at TLAS scale, with the single-pass v2 recorded for the day a measurement
|
|
987
|
+
demands it. What remains of the original objection is only schedule risk — the sort is new
|
|
988
|
+
engineering on the critical path — carried as R2.
|
|
989
|
+
|
|
990
|
+
**AR8 — "Per-frame full rebuild is wasteful; you should refit-update the GPU tree."** Rebuild-
|
|
991
|
+
always is the references' default for TLAS (cheap at instance counts; immune to the
|
|
992
|
+
quality decay that refit-only accumulates under motion) and is dramatically cheaper than what
|
|
993
|
+
ships today. Refit-only (topology frozen, extraction + refit passes alone — both already exist in
|
|
994
|
+
phase 1) is a knob this architecture gets almost for free, and phase 3a *is* that knob applied to
|
|
995
|
+
skinned BLAS, where it matters. Deliberately not a phase-1 deliverable: no measurement yet says
|
|
996
|
+
the rebuild is the bottleneck.
|
|
997
|
+
|
|
998
|
+
**AR9 — "The emission kernel writes leaves and internals from one dispatch with divergent work
|
|
999
|
+
per thread (thread i does leaf i and maybe internal i) — split it for clarity?"** No: one dispatch
|
|
1000
|
+
of N threads with a tail `if` is the canonical LBVH formulation (DXR ships it), avoids a second
|
|
1001
|
+
pass over the sorted arrays, and the emulator tier tests it as a pure function either way.
|
|
1002
|
+
|
|
1003
|
+
**AR10 — "New directory `gpu_primitive/bvh/` — is that the right home, or is this a scene
|
|
1004
|
+
feature?"** `gpu_primitive/` is where engine-level, consumer-agnostic GPU algorithms live (sort,
|
|
1005
|
+
prefix_sum, indirect, hash_table); the builder is exactly that (TLAS and BLAS both consume it).
|
|
1006
|
+
Scene-specific extraction lives with the scene code. This mirrors how `graph_radix_sort` is a
|
|
1007
|
+
primitive while its callers own their data semantics — the "first-class member, not tack-on"
|
|
1008
|
+
requirement made structural.
|
|
1009
|
+
|
|
1010
|
+
**AR11 — "Apetrei 2014 fuses emission and refit into one kernel with no searches and no spinning —
|
|
1011
|
+
you're knowingly shipping the two-kernel 2012 formulation."** Yes, knowingly (§3.4-4). What the
|
|
1012
|
+
fused kernel saves at TLAS scale is one small dispatch and some binary searches; what the split
|
|
1013
|
+
formulation buys is the project's best test story — the emission kernel is a pure function, so the
|
|
1014
|
+
vitest emulator tier executes the actual WGSL against the actual in-repo oracle, duplicate-key
|
|
1015
|
+
runs and all — plus a standalone refit pass that phase 3a and refit-only updates require to exist
|
|
1016
|
+
regardless. Apetrei changes neither topology nor the output contract, and is the natural
|
|
1017
|
+
optimisation to fold in during phase-2 work — note that GPURT's shipping `Linear` builder *is*
|
|
1018
|
+
the Apetrei formulation (§3.2), and it is H-PLOC's substrate, so effort spent there compounds
|
|
1019
|
+
toward AR2's alternative rather than against it.
|
|
1020
|
+
|
|
1021
|
+
---
|
|
1022
|
+
|
|
1023
|
+
## 7. Risk register
|
|
1024
|
+
|
|
1025
|
+
| # | risk | mitigation |
|
|
1026
|
+
|---|---|---|
|
|
1027
|
+
| R1 | WGSL relaxed-atomic cross-workgroup visibility (refit/treelet climbs) is beyond formal guarantees | All-atomic access discipline; no unbounded waiting anywhere (arrival-counter losers exit); per-platform structural verification in the harness; per-level-dispatch fallback designed (inter-dispatch ordering is spec-guaranteed); precedent: csdldf ships on the same visibility assumption. See AR3. |
|
|
1028
|
+
| R2 | The sort is new engineering on phase 1's critical path (the fallback-less port was removed; nothing shippable exists today) | v1 deliberately assembled from proven parts — `graph_prefix_scan_csdldf` reused as-is for the spine, ranking/scatter machinery adapted from the deleted port in history, interface kept; ships and verifies before the builder consumes it; single-pass v2 deferred behind a measurement. See AR7, §4.2.1. |
|
|
1029
|
+
| R3 | ~~Phase-1 quality dip (raw LBVH vs treelet-optimised dynamic tree)~~ | **Retired.** Measured (§4.6): a dip at ~100 instances that does not reach the frame, and an improvement on every axis at 20k. See AR1. |
|
|
1030
|
+
| R4 | Tree depth vs the shared 32-slot traversal stack; occlusion path truncates silently | Depth histogram in verify harness + phase gates; treelets shorten chains; `BVH_TRAVERSAL_STACK_SIZE` bump is a measured last resort. Pre-existing cliff, now instrumented — and **measured**: 22/32 at 100k scattered instances, 20/32 at 20k (§4.6). Not a hypothetical any more. See AR4. |
|
|
1031
|
+
| R5 | GPU-authority bounds one frame stale at `tlas.update()` encode position | Accepted for phase 1 (strict improvement over CPU build's unbounded staleness); follow-up moves TLAS encoding after the bounds-refresh point in the frame. |
|
|
1032
|
+
| R6 | Degenerate inputs: N ∈ {0, 1}, all-identical centroids, zero-extent scene bounds | Explicit encode-time special cases; duplicate-run handling is the transliterated tiebreak, tested at emulator tier; zero-extent axes quantise to 0 like the CPU builder (scale = 0). |
|
|
1033
|
+
| R7 | `TerrainSystem3.spec.js` and similar pin current TLAS *trigger* behaviour in comments/asserts | Trigger semantics (`#version` guard) are preserved verbatim in phase 1; only the work done per trigger changes. Spec expectations re-checked at phase-1 PR. |
|
|
1034
|
+
| R8 | BLAS serialization round-trip (phase 3b) — GPU-built trees must reach the on-disk cache | Import-time readback path; serialized-BVH adoption is already format-first (bytes are adopted verbatim), so a format-identical GPU build slots in. |
|
|
1035
|
+
| R9 | Subgroup size is a runtime value (4–128) across devices; wave-width-baked assumptions break silently | Subgroups are used everywhere they pay, but algorithm *structure* stays workgroup-shaped unless width-guarded — the csdldf idioms (`subgroup_size` builtin, `MIN_SUBGROUP_SIZE` handling, `@diagnostic(off, subgroup_uniformity)` only where the analysis over-approximates) are the house style; wave-width-dependent designs (H-PLOC's `T = wave/2`) get this called out at their decision checkpoint (AR2). |
|
|
1036
|
+
|
|
1037
|
+
---
|
|
1038
|
+
|
|
1039
|
+
## 8. Cleanup and hardening backlog (folded into the phases)
|
|
1040
|
+
|
|
1041
|
+
Doing this as a first-class citizen also means leaving the neighbourhood cleaner:
|
|
1042
|
+
|
|
1043
|
+
- ~~Delete the dead/fallback-less sort variants~~ — **done** (`b6049214c` removed all four,
|
|
1044
|
+
OneSweep included). The re-founded sort (§4.2.1) carries a `warn_limited` element-count guard
|
|
1045
|
+
from day one, like the scan.
|
|
1046
|
+
- ~~Retire the deprecated pre-fallback scan pair once nothing references it:
|
|
1047
|
+
`prefix_sum/v1/graph_prefix_scan_csdl.js` + `shader_one_sweep_prefix_scan.js`~~ — **done**; both
|
|
1048
|
+
deleted. Nothing referenced either: the graph was the shader's only importer, and none of the
|
|
1049
|
+
symbols the pair exported (`graph_prefix_scan_csdl`, `shader_one_sweep_prefix_scan`,
|
|
1050
|
+
`ONE_SWEEP_UNIFORMS_STRUCT`, `PREFIX_SCAN_NUM_GROUP_THREADS` / `_NUM_ELEMENTS_PER_GROUP`) was
|
|
1051
|
+
named anywhere outside the pair. Same no-fallback class the sort purge removed in `b6049214c` —
|
|
1052
|
+
bare decoupled lookback, which assumes cross-workgroup forward progress. `graph_prefix_scan_csdldf`
|
|
1053
|
+
/ `shader_prefix_scan_csdldf` are the survivors and are what the sort's spine (§4.2.1) dispatches.
|
|
1054
|
+
- ~~Supersede the buggy, unused adaptive-morton chunks with the validated fixed-bit
|
|
1055
|
+
`chunk_v3_morton_encode`~~ — **done**: the fixed-bit chunk is spec'd bit-exact against
|
|
1056
|
+
`v3_morton_encode_magicbits`, and `chunk_calculate_morton_code` /
|
|
1057
|
+
`chunk_get_morton_codes_from_unit_coord` are deleted. Neither had an importer — the pair only
|
|
1058
|
+
imported each other — and the one textual reference outside it was a sentence in
|
|
1059
|
+
`chunk_v3_morton_encode`'s doc comment, reworded with the deletion. Note that `chunk_split_by_3`
|
|
1060
|
+
turned out to be invalid WGSL too (`x | x << 16` does not parse — WGSL gives `<<` and `|` no
|
|
1061
|
+
relative precedence) and was fixed, since the Morton chunk composes it.
|
|
1062
|
+
- ~~Delete `GPUGeometryBVH.js`~~ — **done**. Its header claimed "Currently used"; nothing in the
|
|
1063
|
+
tree named the class. It built a CPU per-geometry BVH over raw triangle indices
|
|
1064
|
+
(`ebvh_build_for_geometry_morton`, then an optional treelet pass) with both of its tuning
|
|
1065
|
+
constants pinned to `0` — so the treelet loop it carried was dead even to itself. The live BLAS
|
|
1066
|
+
path is `meshlet_batch_to_bvh.js` building into `GPUGeometryBVHManager`'s arena, with
|
|
1067
|
+
`GPUGeometryMeshletsBVH` as the per-geometry record.
|
|
1068
|
+
- ~~Decide the fate of `shader/chunk/bvh/packed/`~~ — **deleted**, all nine files; nothing imported
|
|
1069
|
+
any of them. *Superseded*: it is a second, competing compaction of BVH2 — a 20-byte node (3 words
|
|
1070
|
+
of packed bounds + 2 child words) beside the live 32-byte one — while §5.3 takes compaction in a
|
|
1071
|
+
different direction, keeping BVH2 as the construction intermediate and making BVH8 an encode into
|
|
1072
|
+
the 80-byte `BVH8_INODE_STRUCT` that already exists with traversal chunks to match. *Broken*:
|
|
1073
|
+
`NOTES.md`'s leaf convention ("MSB is set to 0 in `child_1`") is not merely unimplemented, it is
|
|
1074
|
+
*inverted* relative to §2.1, where a leaf is `child_1 == 0xFFFFFFFF` — MSB set — while an internal
|
|
1075
|
+
node's child index has it clear; `packed_bvh_from_unpacked_node` copies `child_1` through
|
|
1076
|
+
untouched, so nothing reconciles the two. *Correction* to the note this replaces: `shader-f16` was
|
|
1077
|
+
a red herring — the pair is `pack2x16float`/`unpack2x16float`, core builtins needing no device
|
|
1078
|
+
feature (§5.3 says the same of the BVH8 quantised boxes), and both halves validated clean against
|
|
1079
|
+
`validate_wgsl_source`, extension list empty, before deletion. **Carry forward into §5.3**: the
|
|
1080
|
+
one piece of real logic there, `make_f32_interval_f16_safe`, was wrong and carried its own
|
|
1081
|
+
`FIXME` — it widens an interval by an *absolute* `FLT_EPSILON_16` (2⁻¹⁰, the f16 ULP at 1.0), but
|
|
1082
|
+
f16 spacing scales with magnitude, so near 1024 (ULP 1.0) the nudge rounds away entirely and the
|
|
1083
|
+
bound clips *inward*, dropping geometry from traversal. BVH8's quantised boxes need the same
|
|
1084
|
+
conservative outward rounding, and it has to be directed rounding per magnitude — which is what
|
|
1085
|
+
the routine's own TODO was pointing at in the DXR Fallback's `CpuBVH2Builder.cpp`.
|
|
1086
|
+
- ~~`prefix_sum/v1/test_prefix_sum.js` passes `element_count`/`timer` args that
|
|
1087
|
+
`graph_prefix_scan_csdldf` does not accept~~ — **done** in the sort commit.
|
|
1088
|
+
- Sort v2 (single-pass digit binning with the per-bin fallback protocol, §4.2.1) — recorded
|
|
1089
|
+
upgrade, gated on a measurement from a real consumer.
|
|
1090
|
+
- ~~Add `bvh_format.spec.js` locking the §2.1 byte contract~~ — **done**; it holds both producers
|
|
1091
|
+
to the same golden bytes.
|
|
1092
|
+
- Extend `ComputeShaderEmulator` with sequential atomics (optional; would lift the refit kernel
|
|
1093
|
+
into the emulator tier). Its bit-counting builtins (`countLeadingZeros` and siblings) were added
|
|
1094
|
+
in phase 1 — `delta` is written in terms of `countLeadingZeros`, so the emission chunks could not
|
|
1095
|
+
be executed at all without them.
|
|
1096
|
+
- `SoftwareGPUDevice` now accepts a `subgroups` requirement. It runs no WGSL, so the feature changes
|
|
1097
|
+
nothing it emulates; refusing it left the orchestration of every subgroup-using pass untestable at
|
|
1098
|
+
tier 3. The corollary for future specs: one that stands up a scene and calls
|
|
1099
|
+
`GPUSceneContext.update()` must construct its device with `requiredFeatures: ['subgroups']`.
|
|
1100
|
+
- `Renderer` requests `Subgroups` as *optional*, so a device without it starts and then throws at
|
|
1101
|
+
the first subgroup dispatch. That predates this work (the csdldf scan has the same property) and
|
|
1102
|
+
is consistent with subgroups being a stated baseline, but it is the one place where the baseline
|
|
1103
|
+
is not enforced where the device is created.
|
|
1104
|
+
|
|
1105
|
+
---
|
|
1106
|
+
|
|
1107
|
+
## 9. Deliverable sequence (summary)
|
|
1108
|
+
|
|
1109
|
+
| phase | delivers | new shaders | gate |
|
|
1110
|
+
|---|---|---|---|
|
|
1111
|
+
| 1 | CSDLDF key-value radix sort (`gpu_primitive/sort` re-founded) + GPU TLAS build (LBVH); CPU treelet+flatten+upload retired from the hot path | sort: histogram, spine (csdldf reused), scatter; builder: extract, morton, emit, refit (+ init) | 4-tier validation green for sort and builder; A/B images clean; build-time win measured; depth instrumented |
|
|
1112
|
+
| 2 | GPU treelet optimisation; GPU path becomes default | clear, find-treelets, reorder | SAH + frame-time parity with today's CPU pipeline; legacy CPU upload path deleted |
|
|
1113
|
+
| 3a | Skinned-clone BLAS refit (per-frame correct animated BLAS) | triangle-leaf extract (refit reused) | deformed-mesh RT correctness on animation scenes |
|
|
1114
|
+
| 3b | Full GPU BLAS build (import + procedural) | (extraction variant only) | format + serialization round-trip; trace parity vs CPU-built BLAS |
|
|
1115
|
+
| 4 | BVH8 encode + consumer migration, additive | cost/collapse, encode | traversal win measured per consumer before migration |
|