@woosh/meep-engine 3.11.3 → 3.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (596) hide show
  1. package/README.md +14 -18
  2. package/editor/SelectionVisualizer.js +127 -127
  3. package/editor/entity_world_bounds.js +27 -27
  4. package/editor/particles/rebuildParticleEmitter.js +31 -31
  5. package/editor/process/SymbolicDisplayProcess.js +1 -1
  6. package/editor/process/symbolic/CameraSymbolicDisplay.js +146 -146
  7. package/editor/process/symbolic/LightSymbolicDisplay.js +172 -172
  8. package/editor/prototypeEditorShell.js +156 -156
  9. package/editor/selection/editor_pick.js +130 -130
  10. package/editor/selection/screen_rect_frustum.js +119 -119
  11. package/editor/tools/v2/TransformControls.js +971 -971
  12. package/package.json +1 -1
  13. package/src/engine/Engine.d.ts +3 -3
  14. package/src/engine/Engine.d.ts.map +1 -1
  15. package/src/engine/Engine.js +726 -726
  16. package/src/engine/EngineHarness.d.ts +1 -1
  17. package/src/engine/EngineHarness.js +590 -590
  18. package/src/engine/ecs/HIERARCHY_REVIEW.md +717 -717
  19. package/src/engine/ecs/terrain/ecs/BuildLightTexture.d.ts +1 -1
  20. package/src/engine/ecs/terrain/ecs/BuildLightTexture.js +27 -27
  21. package/src/engine/ecs/terrain/ecs/Terrain.d.ts +1 -5
  22. package/src/engine/ecs/terrain/ecs/Terrain.d.ts.map +1 -1
  23. package/src/engine/ecs/terrain/ecs/Terrain.js +790 -790
  24. package/src/engine/ecs/terrain/util/loadVisibleTerrainTiles.js +129 -129
  25. package/src/engine/graphics/ecs/camera/camera_find_active.d.ts +1 -1
  26. package/src/engine/graphics/ecs/camera/camera_find_active.js +28 -28
  27. package/src/engine/graphics/ecs/mesh-v2/ShadedGeometry.d.ts +1 -1
  28. package/src/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +428 -428
  29. package/src/engine/graphics/ecs/water/Water.d.ts +1 -1
  30. package/src/engine/graphics/ecs/water/Water.js +104 -104
  31. package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.d.ts +1 -1
  32. package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.js +1 -1
  33. package/src/engine/graphics3/AnimationGraphSystem.d.ts +65 -0
  34. package/src/engine/graphics3/AnimationGraphSystem.d.ts.map +1 -0
  35. package/src/engine/graphics3/AnimationGraphSystem.js +329 -0
  36. package/src/engine/graphics3/AnimationGraphSystem3.d.ts +6 -61
  37. package/src/engine/graphics3/AnimationGraphSystem3.d.ts.map +1 -1
  38. package/src/engine/graphics3/AnimationGraphSystem3.js +6 -325
  39. package/src/engine/graphics3/AnimationSystem.d.ts +76 -0
  40. package/src/engine/graphics3/AnimationSystem.d.ts.map +1 -0
  41. package/src/engine/graphics3/AnimationSystem.js +352 -0
  42. package/src/engine/graphics3/AnimationSystem3.d.ts +6 -72
  43. package/src/engine/graphics3/AnimationSystem3.d.ts.map +1 -1
  44. package/src/engine/graphics3/AnimationSystem3.js +6 -348
  45. package/src/engine/graphics3/CameraSystem.d.ts +49 -0
  46. package/src/engine/graphics3/CameraSystem.d.ts.map +1 -0
  47. package/src/engine/graphics3/CameraSystem.js +170 -0
  48. package/src/engine/graphics3/CameraSystem3.d.ts +6 -45
  49. package/src/engine/graphics3/CameraSystem3.d.ts.map +1 -1
  50. package/src/engine/graphics3/CameraSystem3.js +6 -166
  51. package/src/engine/graphics3/DebugDrawSystem.d.ts +72 -0
  52. package/src/engine/graphics3/DebugDrawSystem.d.ts.map +1 -0
  53. package/src/engine/graphics3/DebugDrawSystem.js +205 -0
  54. package/src/engine/graphics3/DebugDrawSystem3.d.ts +6 -68
  55. package/src/engine/graphics3/DebugDrawSystem3.d.ts.map +1 -1
  56. package/src/engine/graphics3/DebugDrawSystem3.js +6 -201
  57. package/src/engine/graphics3/DecalSystem.d.ts +81 -0
  58. package/src/engine/graphics3/DecalSystem.d.ts.map +1 -0
  59. package/src/engine/graphics3/DecalSystem.js +441 -0
  60. package/src/engine/graphics3/DecalSystem3.d.ts +6 -77
  61. package/src/engine/graphics3/DecalSystem3.d.ts.map +1 -1
  62. package/src/engine/graphics3/DecalSystem3.js +6 -437
  63. package/src/engine/graphics3/FogOfWarSystem.d.ts +74 -0
  64. package/src/engine/graphics3/FogOfWarSystem.d.ts.map +1 -0
  65. package/src/engine/graphics3/FogOfWarSystem.js +265 -0
  66. package/src/engine/graphics3/FogOfWarSystem3.d.ts +6 -70
  67. package/src/engine/graphics3/FogOfWarSystem3.d.ts.map +1 -1
  68. package/src/engine/graphics3/FogOfWarSystem3.js +6 -261
  69. package/src/engine/graphics3/GraphicsEngine.d.ts +340 -0
  70. package/src/engine/graphics3/GraphicsEngine.d.ts.map +1 -0
  71. package/src/engine/graphics3/GraphicsEngine.js +790 -0
  72. package/src/engine/graphics3/GraphicsEngine3.d.ts +6 -336
  73. package/src/engine/graphics3/GraphicsEngine3.d.ts.map +1 -1
  74. package/src/engine/graphics3/GraphicsEngine3.js +6 -786
  75. package/src/engine/graphics3/HighlightSystem.d.ts +82 -0
  76. package/src/engine/graphics3/HighlightSystem.d.ts.map +1 -0
  77. package/src/engine/graphics3/HighlightSystem.js +241 -0
  78. package/src/engine/graphics3/HighlightSystem3.d.ts +6 -78
  79. package/src/engine/graphics3/HighlightSystem3.d.ts.map +1 -1
  80. package/src/engine/graphics3/HighlightSystem3.js +6 -237
  81. package/src/engine/graphics3/LightSystem.d.ts +40 -0
  82. package/src/engine/graphics3/LightSystem.d.ts.map +1 -0
  83. package/src/engine/graphics3/LightSystem.js +211 -0
  84. package/src/engine/graphics3/LightSystem3.d.ts +6 -36
  85. package/src/engine/graphics3/LightSystem3.d.ts.map +1 -1
  86. package/src/engine/graphics3/LightSystem3.js +6 -207
  87. package/src/engine/graphics3/MeshSystem.d.ts +106 -0
  88. package/src/engine/graphics3/MeshSystem.d.ts.map +1 -0
  89. package/src/engine/graphics3/MeshSystem.js +647 -0
  90. package/src/engine/graphics3/MeshSystem3.d.ts +6 -102
  91. package/src/engine/graphics3/MeshSystem3.d.ts.map +1 -1
  92. package/src/engine/graphics3/MeshSystem3.js +6 -642
  93. package/src/engine/graphics3/ParticipatingMedia.d.ts +2 -2
  94. package/src/engine/graphics3/ParticipatingMedia.js +203 -203
  95. package/src/engine/graphics3/ParticipatingMediaSystem.d.ts +87 -0
  96. package/src/engine/graphics3/ParticipatingMediaSystem.d.ts.map +1 -0
  97. package/src/engine/graphics3/ParticipatingMediaSystem.js +184 -0
  98. package/src/engine/graphics3/ParticipatingMediaSystem3.d.ts +6 -83
  99. package/src/engine/graphics3/ParticipatingMediaSystem3.d.ts.map +1 -1
  100. package/src/engine/graphics3/ParticipatingMediaSystem3.js +6 -180
  101. package/src/engine/graphics3/ParticleEmitterSystem.d.ts +76 -0
  102. package/src/engine/graphics3/ParticleEmitterSystem.d.ts.map +1 -0
  103. package/src/engine/graphics3/ParticleEmitterSystem.js +289 -0
  104. package/src/engine/graphics3/ParticleEmitterSystem3.d.ts +6 -72
  105. package/src/engine/graphics3/ParticleEmitterSystem3.d.ts.map +1 -1
  106. package/src/engine/graphics3/ParticleEmitterSystem3.js +6 -285
  107. package/src/engine/graphics3/PathDisplaySystem.d.ts +70 -0
  108. package/src/engine/graphics3/PathDisplaySystem.d.ts.map +1 -0
  109. package/src/engine/graphics3/PathDisplaySystem.js +309 -0
  110. package/src/engine/graphics3/PathDisplaySystem3.d.ts +6 -66
  111. package/src/engine/graphics3/PathDisplaySystem3.d.ts.map +1 -1
  112. package/src/engine/graphics3/PathDisplaySystem3.js +6 -305
  113. package/src/engine/graphics3/Pickable.d.ts +1 -1
  114. package/src/engine/graphics3/Pickable.js +65 -65
  115. package/src/engine/graphics3/PickingQuery.d.ts +1 -1
  116. package/src/engine/graphics3/PickingQuery.js +48 -48
  117. package/src/engine/graphics3/PickingSystem.d.ts +67 -0
  118. package/src/engine/graphics3/PickingSystem.d.ts.map +1 -0
  119. package/src/engine/graphics3/PickingSystem.js +273 -0
  120. package/src/engine/graphics3/PickingSystem3.d.ts +6 -63
  121. package/src/engine/graphics3/PickingSystem3.d.ts.map +1 -1
  122. package/src/engine/graphics3/PickingSystem3.js +6 -269
  123. package/src/engine/graphics3/ShadedGeometrySystem.d.ts +47 -0
  124. package/src/engine/graphics3/ShadedGeometrySystem.d.ts.map +1 -0
  125. package/src/engine/graphics3/ShadedGeometrySystem.js +169 -0
  126. package/src/engine/graphics3/ShadedGeometrySystem3.d.ts +6 -43
  127. package/src/engine/graphics3/ShadedGeometrySystem3.d.ts.map +1 -1
  128. package/src/engine/graphics3/ShadedGeometrySystem3.js +6 -165
  129. package/src/engine/graphics3/TerrainSystem.d.ts +80 -0
  130. package/src/engine/graphics3/TerrainSystem.d.ts.map +1 -0
  131. package/src/engine/graphics3/TerrainSystem.js +566 -0
  132. package/src/engine/graphics3/TerrainSystem3.d.ts +6 -76
  133. package/src/engine/graphics3/TerrainSystem3.d.ts.map +1 -1
  134. package/src/engine/graphics3/TerrainSystem3.js +6 -562
  135. package/src/engine/graphics3/TooltipComponentSystem.d.ts +65 -0
  136. package/src/engine/graphics3/TooltipComponentSystem.d.ts.map +1 -0
  137. package/src/engine/graphics3/TooltipComponentSystem.js +328 -0
  138. package/src/engine/graphics3/TooltipComponentSystem3.d.ts +6 -61
  139. package/src/engine/graphics3/TooltipComponentSystem3.d.ts.map +1 -1
  140. package/src/engine/graphics3/TooltipComponentSystem3.js +6 -324
  141. package/src/engine/graphics3/Trail3DSystem.d.ts +73 -0
  142. package/src/engine/graphics3/Trail3DSystem.d.ts.map +1 -0
  143. package/src/engine/graphics3/Trail3DSystem.js +221 -0
  144. package/src/engine/graphics3/Trail3DSystem3.d.ts +6 -69
  145. package/src/engine/graphics3/Trail3DSystem3.d.ts.map +1 -1
  146. package/src/engine/graphics3/Trail3DSystem3.js +6 -217
  147. package/src/engine/graphics3/VolumetricLightMap.d.ts +2 -2
  148. package/src/engine/graphics3/VolumetricLightMap.js +2 -2
  149. package/src/engine/graphics3/VolumetricLightMapSystem.d.ts +98 -0
  150. package/src/engine/graphics3/VolumetricLightMapSystem.d.ts.map +1 -0
  151. package/src/engine/graphics3/VolumetricLightMapSystem.js +261 -0
  152. package/src/engine/graphics3/VolumetricLightMapSystem3.d.ts +6 -94
  153. package/src/engine/graphics3/VolumetricLightMapSystem3.d.ts.map +1 -1
  154. package/src/engine/graphics3/VolumetricLightMapSystem3.js +10 -261
  155. package/src/engine/graphics3/WaterSystem.d.ts +59 -0
  156. package/src/engine/graphics3/WaterSystem.d.ts.map +1 -0
  157. package/src/engine/graphics3/WaterSystem.js +293 -0
  158. package/src/engine/graphics3/WaterSystem3.d.ts +6 -55
  159. package/src/engine/graphics3/WaterSystem3.d.ts.map +1 -1
  160. package/src/engine/graphics3/WaterSystem3.js +6 -289
  161. package/src/engine/graphics3/animation/AnimationGraphController.d.ts +1 -1
  162. package/src/engine/graphics3/animation/AnimationGraphController.js +518 -518
  163. package/src/engine/graphics3/animation/ClipListPlayer.d.ts +1 -1
  164. package/src/engine/graphics3/animation/ClipListPlayer.js +263 -263
  165. package/src/engine/graphics3/animation/ClipPlayback.d.ts +2 -2
  166. package/src/engine/graphics3/animation/ClipPlayback.js +84 -84
  167. package/src/engine/graphics3/decal/GPUDecalRenderer.js +3 -3
  168. package/src/engine/graphics3/decal/graph_build_decal_clusters.d.ts.map +1 -1
  169. package/src/engine/graphics3/decal/graph_build_decal_clusters.js +6 -8
  170. package/src/engine/graphics3/decal/shader_cluster_assign_decals.d.ts +0 -7
  171. package/src/engine/graphics3/decal/shader_cluster_assign_decals.d.ts.map +1 -1
  172. package/src/engine/graphics3/decal/shader_cluster_assign_decals.js +1 -1
  173. package/src/engine/graphics3/decal/shader_cull_decals_frustum.d.ts +0 -4
  174. package/src/engine/graphics3/decal/shader_cull_decals_frustum.d.ts.map +1 -1
  175. package/src/engine/graphics3/decal/shader_cull_decals_frustum.js +1 -1
  176. package/src/engine/graphics3/decal/shader_decal_composite.d.ts.map +1 -1
  177. package/src/engine/graphics3/decal/shader_decal_composite.js +4 -2
  178. package/src/engine/graphics3/highlight/GPUHighlightRenderer.d.ts.map +1 -1
  179. package/src/engine/graphics3/highlight/GPUHighlightRenderer.js +2 -3
  180. package/src/engine/graphics3/highlight/pack_highlight_table.d.ts +2 -2
  181. package/src/engine/graphics3/highlight/pack_highlight_table.d.ts.map +1 -1
  182. package/src/engine/graphics3/highlight/pack_highlight_table.js +74 -74
  183. package/src/engine/graphics3/highlight/shader_highlight_outline.js +150 -150
  184. package/src/engine/graphics3/pose/query_entity_node_world_pose.d.ts +1 -1
  185. package/src/engine/graphics3/pose/query_entity_node_world_pose.js +101 -101
  186. package/src/engine/graphics3/preview/make_model_thumbnail.d.ts +3 -3
  187. package/src/engine/graphics3/preview/make_model_thumbnail.d.ts.map +1 -1
  188. package/src/engine/graphics3/preview/make_model_thumbnail.js +48 -48
  189. package/src/engine/graphics3/shade_camera_projection_ray.d.ts +1 -1
  190. package/src/engine/graphics3/shade_camera_projection_ray.js +77 -77
  191. package/src/engine/graphics3/shade_node_to_entity_composition.d.ts +1 -1
  192. package/src/engine/graphics3/shade_node_to_entity_composition.js +84 -84
  193. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  194. package/src/shade/RENDERER_CONTRACT.md +2 -1
  195. package/src/shade/descriptor/pipeline/compute/ComputePipelineDescriptor.d.ts +18 -0
  196. package/src/shade/descriptor/pipeline/compute/ComputePipelineDescriptor.d.ts.map +1 -1
  197. package/src/shade/descriptor/pipeline/compute/ComputePipelineDescriptor.js +73 -0
  198. package/src/shade/device/PASS_ENCODER_PROPOSAL_2026_08_31.md +750 -0
  199. package/src/shade/device/ShadeGPUCommandContext.d.ts +10 -16
  200. package/src/shade/device/ShadeGPUCommandContext.d.ts.map +1 -1
  201. package/src/shade/device/ShadeGPUCommandContext.js +801 -994
  202. package/src/shade/device/pass/ShadeGPUComputePassEncoder.d.ts +107 -0
  203. package/src/shade/device/pass/ShadeGPUComputePassEncoder.d.ts.map +1 -0
  204. package/src/shade/device/pass/ShadeGPUComputePassEncoder.js +222 -0
  205. package/src/shade/device/pass/ShadeGPURenderPassEncoder.d.ts +168 -0
  206. package/src/shade/device/pass/ShadeGPURenderPassEncoder.d.ts.map +1 -0
  207. package/src/shade/device/pass/ShadeGPURenderPassEncoder.js +317 -0
  208. package/src/shade/device/timing/GPUTimerArray.d.ts +4 -9
  209. package/src/shade/device/timing/GPUTimerArray.d.ts.map +1 -1
  210. package/src/shade/device/timing/GPUTimerArray.js +261 -289
  211. package/src/shade/device/timing/GPU_PROFILER_PROPOSAL_2026_08_28.md +20 -2
  212. package/src/shade/device/timing/ShadeGPUContextProfiler.d.ts +59 -0
  213. package/src/shade/device/timing/ShadeGPUContextProfiler.d.ts.map +1 -0
  214. package/src/shade/device/timing/ShadeGPUContextProfiler.js +182 -0
  215. package/src/shade/device/timing/profile/GPUProfileSession.d.ts +4 -4
  216. package/src/shade/device/timing/profile/GPUProfileSession.js +4 -4
  217. package/src/shade/playground/skinned_blas_refit/shader_blas_audit_leaf_triangles.d.ts.map +1 -1
  218. package/src/shade/playground/skinned_blas_refit/shader_blas_audit_leaf_triangles.js +3 -2
  219. package/src/shade/playground/skinned_blas_refit/shader_blas_audit_points.d.ts.map +1 -1
  220. package/src/shade/playground/skinned_blas_refit/shader_blas_audit_points.js +3 -2
  221. package/src/shade/playground/skinned_blas_refit/shader_blas_audit_visibility.d.ts.map +1 -1
  222. package/src/shade/playground/skinned_blas_refit/shader_blas_audit_visibility.js +135 -134
  223. package/src/shade/playground/skinned_blas_refit/verify_leaf_triangles.js +202 -202
  224. package/src/shade/playground/skinned_blas_refit/verify_traversal_reachability.js +249 -249
  225. package/src/shade/renderer/Renderer.d.ts +0 -6
  226. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  227. package/src/shade/renderer/Renderer.js +0 -29
  228. package/src/shade/renderer/STATIC_GRAPHICS_ENGINE_ASSETS.js +1 -1
  229. package/src/shade/renderer/animation/GPUAnimationManager.d.ts.map +1 -1
  230. package/src/shade/renderer/animation/GPUAnimationManager.js +1 -3
  231. package/src/shade/renderer/animation/pose/PosePlayback.d.ts +2 -1
  232. package/src/shade/renderer/animation/pose/PosePlayback.d.ts.map +1 -1
  233. package/src/shade/renderer/animation/pose/PosePlayback.js +2 -1
  234. package/src/shade/renderer/animation/shader_animation_apply.d.ts.map +1 -1
  235. package/src/shade/renderer/animation/shader_animation_apply.js +128 -129
  236. package/src/shade/renderer/animation/shader_animation_pose_resolve.d.ts.map +1 -1
  237. package/src/shade/renderer/animation/shader_animation_pose_resolve.js +176 -177
  238. package/src/shade/renderer/animation/shader_animation_tick.d.ts.map +1 -1
  239. package/src/shade/renderer/animation/shader_animation_tick.js +4 -2
  240. package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.d.ts.map +1 -1
  241. package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.js +8 -18
  242. package/src/shade/renderer/animation/skinning/bounds_refresh/shader_geometry_sphere_derive.d.ts.map +1 -1
  243. package/src/shade/renderer/animation/skinning/bounds_refresh/shader_geometry_sphere_derive.js +3 -2
  244. package/src/shade/renderer/animation/skinning/bounds_refresh/shader_meshlet_bounds_reduce.d.ts.map +1 -1
  245. package/src/shade/renderer/animation/skinning/bounds_refresh/shader_meshlet_bounds_reduce.js +4 -2
  246. package/src/shade/renderer/animation/skinning/bounds_refresh/shader_meshlet_bounds_scatter.d.ts +1 -1
  247. package/src/shade/renderer/animation/skinning/bounds_refresh/shader_meshlet_bounds_scatter.d.ts.map +1 -1
  248. package/src/shade/renderer/animation/skinning/bounds_refresh/shader_meshlet_bounds_scatter.js +24 -10
  249. package/src/shade/renderer/animation/skinning/bounds_refresh/shader_skinned_mesh_bounds_refresh.d.ts.map +1 -1
  250. package/src/shade/renderer/animation/skinning/bounds_refresh/shader_skinned_mesh_bounds_refresh.js +3 -2
  251. package/src/shade/renderer/animation/skinning/shader_meshlet_apply_skinning.d.ts.map +1 -1
  252. package/src/shade/renderer/animation/skinning/shader_meshlet_apply_skinning.js +5 -2
  253. package/src/shade/renderer/animation/skinning/shader_skin_matrix_prep.d.ts.map +1 -1
  254. package/src/shade/renderer/animation/skinning/shader_skin_matrix_prep.js +104 -105
  255. package/src/shade/renderer/atmosphere/shader_multiscatter_lut.d.ts.map +1 -1
  256. package/src/shade/renderer/atmosphere/shader_multiscatter_lut.js +0 -1
  257. package/src/shade/renderer/atmosphere/shader_transmittance_lut.js +3 -3
  258. package/src/shade/renderer/buffer/graph_inspect_gpu_buffer.js +136 -136
  259. package/src/shade/renderer/buffer/table/GPUDatabase.d.ts.map +1 -1
  260. package/src/shade/renderer/buffer/table/GPUDatabase.js +16 -22
  261. package/src/shade/renderer/buffer/table/gpu_table_element_upload_shader.d.ts.map +1 -1
  262. package/src/shade/renderer/buffer/table/gpu_table_element_upload_shader.js +7 -2
  263. package/src/shade/renderer/buffer/table/single/GPUSingleTypeTable.d.ts.map +1 -1
  264. package/src/shade/renderer/buffer/table/single/GPUSingleTypeTable.js +11 -5
  265. package/src/shade/renderer/buffer/util/shader_buffer_copy_storage_to_storage.d.ts.map +1 -1
  266. package/src/shade/renderer/buffer/util/shader_buffer_copy_storage_to_storage.js +43 -42
  267. package/src/shade/renderer/buffer/util/shader_buffer_intra_copy_regions.d.ts.map +1 -1
  268. package/src/shade/renderer/buffer/util/shader_buffer_intra_copy_regions.js +83 -84
  269. package/src/shade/renderer/deferred/shader_tonemap_HDR.d.ts.map +1 -1
  270. package/src/shade/renderer/deferred/shader_tonemap_HDR.js +4 -1
  271. package/src/shade/renderer/dynamic/GPUDynamicMeshResidency.d.ts +2 -2
  272. package/src/shade/renderer/dynamic/GPUDynamicMeshResidency.d.ts.map +1 -1
  273. package/src/shade/renderer/dynamic/GPUDynamicMeshResidency.js +167 -167
  274. package/src/shade/renderer/dynamic/shader_dynamic_mesh.d.ts.map +1 -1
  275. package/src/shade/renderer/dynamic/shader_dynamic_mesh.js +177 -170
  276. package/src/shade/renderer/extension/RENDER_EXTENSION_DESIGN.md +918 -918
  277. package/src/shade/renderer/fow/GPUFogOfWarRenderer.js +3 -3
  278. package/src/shade/renderer/fow/shader_fog_of_war.d.ts.map +1 -1
  279. package/src/shade/renderer/fow/shader_fog_of_war.js +3 -1
  280. package/src/shade/renderer/geometry/bvh/record_blas_refit.js +102 -102
  281. package/src/shade/renderer/geometry/bvh/shader_blas_refit_leaf_bounds.d.ts.map +1 -1
  282. package/src/shade/renderer/geometry/bvh/shader_blas_refit_leaf_bounds.js +5 -2
  283. package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.d.ts.map +1 -1
  284. package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.js +3 -11
  285. package/src/shade/renderer/geometry/meshlet/shader_meshlet_metadata_address_patch.d.ts +1 -1
  286. package/src/shade/renderer/geometry/meshlet/shader_meshlet_metadata_address_patch.d.ts.map +1 -1
  287. package/src/shade/renderer/geometry/meshlet/shader_meshlet_metadata_address_patch.js +6 -4
  288. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts.map +1 -1
  289. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.js +5 -10
  290. package/src/shade/renderer/geometry/sdf/shader_build_sdf.js +129 -129
  291. package/src/shade/renderer/geometry/sdf/shader_sdf_visualize.d.ts.map +1 -1
  292. package/src/shade/renderer/geometry/sdf/shader_sdf_visualize.js +5 -3
  293. package/src/shade/renderer/global_illumination/brick4/gpu/bake/shader_brick4_bake_probes.d.ts.map +1 -1
  294. package/src/shade/renderer/global_illumination/brick4/gpu/bake/shader_brick4_bake_probes.js +4 -1
  295. package/src/shade/renderer/global_illumination/brick4/gpu/bake/shader_brick4_bake_resolve_probes.d.ts.map +1 -1
  296. package/src/shade/renderer/global_illumination/brick4/gpu/bake/shader_brick4_bake_resolve_probes.js +3 -1
  297. package/src/shade/renderer/global_illumination/clipmap/shader_update_clipmap_cascade.d.ts.map +1 -1
  298. package/src/shade/renderer/global_illumination/clipmap/shader_update_clipmap_cascade.js +114 -112
  299. package/src/shade/renderer/global_illumination/lpv/placement/graph_generate_probe_locations.js +367 -367
  300. package/src/shade/renderer/global_illumination/probe/octahedral/convolve/graph_lpv_store_sh.d.ts.map +1 -1
  301. package/src/shade/renderer/global_illumination/probe/octahedral/convolve/graph_lpv_store_sh.js +4 -1
  302. package/src/shade/renderer/global_illumination/probe/octahedral/convolve/graph_lvp_atlas_to_sh.d.ts.map +1 -1
  303. package/src/shade/renderer/global_illumination/probe/octahedral/convolve/graph_lvp_atlas_to_sh.js +9 -6
  304. package/src/shade/renderer/global_illumination/probe/octahedral/convolve/graph_reduce_atlas_to_sh.d.ts.map +1 -1
  305. package/src/shade/renderer/global_illumination/probe/octahedral/convolve/graph_reduce_atlas_to_sh.js +4 -1
  306. package/src/shade/renderer/global_illumination/sharc/SpatialHashRadianceCache.d.ts.map +1 -1
  307. package/src/shade/renderer/global_illumination/sharc/SpatialHashRadianceCache.js +6 -8
  308. package/src/shade/renderer/global_illumination/sharc/shader/shader_sharc_evict.d.ts.map +1 -1
  309. package/src/shade/renderer/global_illumination/sharc/shader/shader_sharc_evict.js +3 -1
  310. package/src/shade/renderer/global_illumination/sharc/shader/shader_sharc_update.d.ts.map +1 -1
  311. package/src/shade/renderer/global_illumination/sharc/shader/shader_sharc_update.js +3 -1
  312. package/src/shade/renderer/gpu_primitive/bvh/GPU_BVH_BUILD_PLAN.md +1115 -1115
  313. package/src/shade/renderer/gpu_primitive/bvh/PHASE1_HANDOVER.md +3 -3
  314. package/src/shade/renderer/gpu_primitive/bvh/graph_bvh_build_lbvh.js +10 -10
  315. package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_emit_hierarchy.d.ts.map +1 -1
  316. package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_emit_hierarchy.js +4 -2
  317. package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_morton.d.ts.map +1 -1
  318. package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_morton.js +87 -83
  319. package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_refit.d.ts.map +1 -1
  320. package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_refit.js +4 -2
  321. package/src/shade/renderer/gpu_primitive/bvh/verify_gpu_bvh_build.d.ts +2 -2
  322. package/src/shade/renderer/gpu_primitive/bvh/verify_gpu_bvh_build.d.ts.map +1 -1
  323. package/src/shade/renderer/gpu_primitive/bvh/verify_gpu_bvh_build.js +43 -8
  324. package/src/shade/renderer/gpu_primitive/sort/graph_radix_sort.d.ts.map +1 -1
  325. package/src/shade/renderer/gpu_primitive/sort/graph_radix_sort.js +4 -5
  326. package/src/shade/renderer/gpu_primitive/sort/shader_radix_sort_histogram.d.ts.map +1 -1
  327. package/src/shade/renderer/gpu_primitive/sort/shader_radix_sort_histogram.js +88 -85
  328. package/src/shade/renderer/gpu_primitive/sort/shader_radix_sort_scatter.d.ts.map +1 -1
  329. package/src/shade/renderer/gpu_primitive/sort/shader_radix_sort_scatter.js +194 -192
  330. package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts.map +1 -1
  331. package/src/shade/renderer/hiz/HierarchicalZBuffer.js +33 -69
  332. package/src/shade/renderer/hiz/shader_hzb_reduce_any.d.ts.map +1 -1
  333. package/src/shade/renderer/hiz/shader_hzb_reduce_any.js +6 -1
  334. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.d.ts.map +1 -1
  335. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.js +23 -7
  336. package/src/shade/renderer/light/cluster/cull/graph_cull_lights.d.ts.map +1 -1
  337. package/src/shade/renderer/light/cluster/cull/graph_cull_lights.js +191 -198
  338. package/src/shade/renderer/light/cluster/cull/shader_cull_lights_frustum.d.ts +0 -6
  339. package/src/shade/renderer/light/cluster/cull/shader_cull_lights_frustum.d.ts.map +1 -1
  340. package/src/shade/renderer/light/cluster/cull/shader_cull_lights_frustum.js +120 -120
  341. package/src/shade/renderer/light/cluster/cull/shader_cull_lights_hzb.d.ts +0 -4
  342. package/src/shade/renderer/light/cluster/cull/shader_cull_lights_hzb.d.ts.map +1 -1
  343. package/src/shade/renderer/light/cluster/cull/shader_cull_lights_hzb.js +112 -112
  344. package/src/shade/renderer/light/cluster/graph_assign_clsuters.d.ts.map +1 -1
  345. package/src/shade/renderer/light/cluster/graph_assign_clsuters.js +4 -7
  346. package/src/shade/renderer/light/cluster/shader_cluster_assign_lights.d.ts +0 -6
  347. package/src/shade/renderer/light/cluster/shader_cluster_assign_lights.d.ts.map +1 -1
  348. package/src/shade/renderer/light/cluster/shader_cluster_assign_lights.js +1 -1
  349. package/src/shade/renderer/light/environment/execute_filter_environment_map.d.ts.map +1 -1
  350. package/src/shade/renderer/light/environment/execute_filter_environment_map.js +1 -2
  351. package/src/shade/renderer/light/environment/shader_convolve_reflection_map.d.ts.map +1 -1
  352. package/src/shade/renderer/light/environment/shader_convolve_reflection_map.js +6 -1
  353. package/src/shade/renderer/lightmap/bake/raster/graph_draw_lightmap_viz_buffer.d.ts.map +1 -1
  354. package/src/shade/renderer/lightmap/bake/raster/graph_draw_lightmap_viz_buffer.js +234 -219
  355. package/src/shade/renderer/lightmap/bake/raster/shader_lightmap_to_viz_buffer.js +222 -222
  356. package/src/shade/renderer/lightmap/bake/shader_lightmap_denoise_sh.js +191 -191
  357. package/src/shade/renderer/lightmap/bake/tile/shader_lightmap_accummulate_tile.d.ts.map +1 -1
  358. package/src/shade/renderer/lightmap/bake/tile/shader_lightmap_accummulate_tile.js +85 -82
  359. package/src/shade/renderer/lightmap/bake/tile/shader_lightmap_tile_composit.js +81 -81
  360. package/src/shade/renderer/material/GPUMaterialContext.js +380 -380
  361. package/src/shade/renderer/material/standard/fragment_gbuffer_vt.js +169 -169
  362. package/src/shade/renderer/object_property/shader_object_property_process.d.ts.map +1 -1
  363. package/src/shade/renderer/object_property/shader_object_property_process.js +83 -84
  364. package/src/shade/renderer/object_property/shader_refresh_all_mesh_bounds.d.ts.map +1 -1
  365. package/src/shade/renderer/object_property/shader_refresh_all_mesh_bounds.js +57 -58
  366. package/src/shade/renderer/particles/graph_particles.d.ts.map +1 -1
  367. package/src/shade/renderer/particles/graph_particles.js +5 -3
  368. package/src/shade/renderer/particles/prototypeParticleSystem.js +5 -7
  369. package/src/shade/renderer/particles/shaders/shader_particle_emit.d.ts.map +1 -1
  370. package/src/shade/renderer/particles/shaders/shader_particle_emit.js +27 -13
  371. package/src/shade/renderer/particles/shaders/shader_particle_finalize.d.ts.map +1 -1
  372. package/src/shade/renderer/particles/shaders/shader_particle_finalize.js +4 -4
  373. package/src/shade/renderer/particles/shaders/shader_particle_render.d.ts.map +1 -1
  374. package/src/shade/renderer/particles/shaders/shader_particle_render.js +190 -183
  375. package/src/shade/renderer/particles/shaders/shader_particle_simulate.d.ts.map +1 -1
  376. package/src/shade/renderer/particles/shaders/shader_particle_simulate.js +18 -7
  377. package/src/shade/renderer/particles/sort/shader_particle_sort.d.ts.map +1 -1
  378. package/src/shade/renderer/particles/sort/shader_particle_sort.js +47 -21
  379. package/src/shade/renderer/path_tracer/accumulating/AccumulatingPathTracer.d.ts.map +1 -1
  380. package/src/shade/renderer/path_tracer/accumulating/AccumulatingPathTracer.js +7 -10
  381. package/src/shade/renderer/path_tracer/accumulating/shader_accumulating_path_tracer.d.ts.map +1 -1
  382. package/src/shade/renderer/path_tracer/accumulating/shader_accumulating_path_tracer.js +4 -1
  383. package/src/shade/renderer/path_tracer/graph_path_trace_scene.js +184 -184
  384. package/src/shade/renderer/postprocess/bloom/shader_bloom_mix.d.ts.map +1 -1
  385. package/src/shade/renderer/postprocess/bloom/shader_bloom_mix.js +3 -1
  386. package/src/shade/renderer/postprocess/cas/shader_ffx_rcas.d.ts.map +1 -1
  387. package/src/shade/renderer/postprocess/cas/shader_ffx_rcas.js +4 -2
  388. package/src/shade/renderer/postprocess/denoise/fragment_shader_denoise_ao.js +147 -147
  389. package/src/shade/renderer/postprocess/denoise/graph_atorus_denoise_luma.d.ts.map +1 -1
  390. package/src/shade/renderer/postprocess/denoise/graph_atorus_denoise_luma.js +19 -17
  391. package/src/shade/renderer/postprocess/dof/gather/graph_postprocess_dof2.d.ts.map +1 -1
  392. package/src/shade/renderer/postprocess/dof/gather/graph_postprocess_dof2.js +1 -2
  393. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_gather_far.d.ts.map +1 -1
  394. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_gather_far.js +4 -1
  395. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_gather_near.d.ts.map +1 -1
  396. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_gather_near.js +4 -1
  397. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_recombine.d.ts.map +1 -1
  398. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_recombine.js +4 -1
  399. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_setup.d.ts.map +1 -1
  400. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_setup.js +4 -1
  401. package/src/shade/renderer/postprocess/dof/raymarch/graph_postprocess_dof.d.ts.map +1 -1
  402. package/src/shade/renderer/postprocess/dof/raymarch/graph_postprocess_dof.js +5 -8
  403. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_composite.d.ts.map +1 -1
  404. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_composite.js +4 -1
  405. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_prefilter.d.ts.map +1 -1
  406. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_prefilter.js +4 -1
  407. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_raymarch.d.ts.map +1 -1
  408. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_raymarch.js +4 -1
  409. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_raymarch_compute.d.ts.map +1 -1
  410. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_raymarch_compute.js +4 -2
  411. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_slice_compute.d.ts.map +1 -1
  412. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_slice_compute.js +4 -2
  413. package/src/shade/renderer/postprocess/dof/shader_dof_autofocus.d.ts.map +1 -1
  414. package/src/shade/renderer/postprocess/dof/shader_dof_autofocus.js +4 -1
  415. package/src/shade/renderer/postprocess/eye/GPUCameraExposureManager.d.ts.map +1 -1
  416. package/src/shade/renderer/postprocess/eye/GPUCameraExposureManager.js +8 -11
  417. package/src/shade/renderer/postprocess/eye/shader_autoexposure_adapt.d.ts.map +1 -1
  418. package/src/shade/renderer/postprocess/eye/shader_autoexposure_adapt.js +5 -1
  419. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao.d.ts.map +1 -1
  420. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao.js +11 -8
  421. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao_with_irradiance.d.ts.map +1 -1
  422. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao_with_irradiance.js +7 -8
  423. package/src/shade/renderer/postprocess/motion_blur/shader_motion_blur_reconstruct.d.ts.map +1 -1
  424. package/src/shade/renderer/postprocess/motion_blur/shader_motion_blur_reconstruct.js +3 -1
  425. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  426. package/src/shade/renderer/postprocess/nss/shader_nss_concat.d.ts.map +1 -1
  427. package/src/shade/renderer/postprocess/nss/shader_nss_concat.js +3 -2
  428. package/src/shade/renderer/postprocess/nss/shader_nss_conv_layer.d.ts.map +1 -1
  429. package/src/shade/renderer/postprocess/nss/shader_nss_conv_layer.js +225 -226
  430. package/src/shade/renderer/postprocess/nss/shader_nss_extract_feedback.d.ts.map +1 -1
  431. package/src/shade/renderer/postprocess/nss/shader_nss_extract_feedback.js +49 -50
  432. package/src/shade/renderer/postprocess/nss/shader_nss_postprocess.d.ts.map +1 -1
  433. package/src/shade/renderer/postprocess/nss/shader_nss_postprocess.js +291 -292
  434. package/src/shade/renderer/postprocess/nss/shader_nss_preprocess.d.ts.map +1 -1
  435. package/src/shade/renderer/postprocess/nss/shader_nss_preprocess.js +230 -231
  436. package/src/shade/renderer/postprocess/ssr/depth_max_shader.d.ts.map +1 -1
  437. package/src/shade/renderer/postprocess/ssr/depth_max_shader.js +70 -68
  438. package/src/shade/renderer/postprocess/ssr/reproject/shader_ffx_denoiser_reflections_reproject.d.ts +16 -2
  439. package/src/shade/renderer/postprocess/ssr/reproject/shader_ffx_denoiser_reflections_reproject.d.ts.map +1 -1
  440. package/src/shade/renderer/postprocess/ssr/reproject/shader_ffx_denoiser_reflections_reproject.js +12 -12
  441. package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader_IBL.d.ts.map +1 -1
  442. package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader_IBL.js +3 -1
  443. package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader_LPV.d.ts.map +1 -1
  444. package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader_LPV.js +4 -1
  445. package/src/shade/renderer/postprocess/ssr/shader_downsample_encoded_normal.d.ts.map +1 -1
  446. package/src/shade/renderer/postprocess/ssr/shader_downsample_encoded_normal.js +103 -101
  447. package/src/shade/renderer/postprocess/ssr/ssr_spatial_denoise_shader.d.ts.map +1 -1
  448. package/src/shade/renderer/postprocess/ssr/ssr_spatial_denoise_shader.js +4 -2
  449. package/src/shade/renderer/postprocess/ssr/ssr_trace_shader.d.ts.map +1 -1
  450. package/src/shade/renderer/postprocess/ssr/ssr_trace_shader.js +3 -1
  451. package/src/shade/renderer/postprocess/taa/shader_taa.d.ts.map +1 -1
  452. package/src/shade/renderer/postprocess/taa/shader_taa.js +4 -1
  453. package/src/shade/renderer/postprocess/upscale-dither/fragment_shader_upscale_dither.js +5 -5
  454. package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.d.ts.map +1 -1
  455. package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.js +339 -341
  456. package/src/shade/renderer/rasterize/bucket/shader_count_meshlets_by_material.d.ts.map +1 -1
  457. package/src/shade/renderer/rasterize/bucket/shader_count_meshlets_by_material.js +68 -69
  458. package/src/shade/renderer/rasterize/bucket/shader_make_meshlet_indirect_draw_commands_by_material.d.ts.map +1 -1
  459. package/src/shade/renderer/rasterize/bucket/shader_make_meshlet_indirect_draw_commands_by_material.js +59 -60
  460. package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.d.ts.map +1 -1
  461. package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.js +0 -1
  462. package/src/shade/renderer/rasterize/bucket/shader_sort_meshlets_by_material.d.ts.map +1 -1
  463. package/src/shade/renderer/rasterize/bucket/shader_sort_meshlets_by_material.js +64 -65
  464. package/src/shade/renderer/rasterize/compute/graph_mesh_frustum_cull.d.ts.map +1 -1
  465. package/src/shade/renderer/rasterize/compute/graph_mesh_frustum_cull.js +168 -169
  466. package/src/shade/renderer/rasterize/compute/graph_mesh_sphere_cull.d.ts.map +1 -1
  467. package/src/shade/renderer/rasterize/compute/graph_mesh_sphere_cull.js +160 -161
  468. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_mesh_filter_2way.d.ts.map +1 -1
  469. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_mesh_filter_2way.js +6 -2
  470. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_meshlet_filter_1way_full.d.ts.map +1 -1
  471. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_meshlet_filter_1way_full.js +4 -2
  472. package/src/shade/renderer/rasterize/expand/bucket/mesh/shader_instances_classify_bucket.d.ts.map +1 -1
  473. package/src/shade/renderer/rasterize/expand/bucket/mesh/shader_instances_classify_bucket.js +139 -140
  474. package/src/shade/renderer/rasterize/expand/bucket/mesh/shader_instances_count_by_rasterization_bucket.d.ts.map +1 -1
  475. package/src/shade/renderer/rasterize/expand/bucket/mesh/shader_instances_count_by_rasterization_bucket.js +91 -92
  476. package/src/shade/renderer/rasterize/expand/bucket/mesh/shader_instances_rasterization_bucket_sort.d.ts.map +1 -1
  477. package/src/shade/renderer/rasterize/expand/bucket/mesh/shader_instances_rasterization_bucket_sort.js +88 -89
  478. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_count_by_rasterization_bucket.d.ts.map +1 -1
  479. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_count_by_rasterization_bucket.js +90 -91
  480. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_extract_bucket.d.ts.map +1 -1
  481. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_extract_bucket.js +5 -2
  482. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_rasterization_bucket_sort.d.ts.map +1 -1
  483. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_rasterization_bucket_sort.js +89 -90
  484. package/src/shade/renderer/rasterize/expand/graph_mesh_filter_one_way.d.ts.map +1 -1
  485. package/src/shade/renderer/rasterize/expand/graph_mesh_filter_one_way.js +169 -170
  486. package/src/shade/renderer/rasterize/expand/mesh/shader_mesh_expand_to_meshlets_prefix.d.ts.map +1 -1
  487. package/src/shade/renderer/rasterize/expand/mesh/shader_mesh_expand_to_meshlets_prefix.js +89 -90
  488. package/src/shade/renderer/rasterize/expand/mesh/shader_meshes_to_meshlet_counts.d.ts.map +1 -1
  489. package/src/shade/renderer/rasterize/expand/mesh/shader_meshes_to_meshlet_counts.js +61 -62
  490. package/src/shade/renderer/rasterize/expand/pass1/filter_meshlets.2-way.compute.d.ts.map +1 -1
  491. package/src/shade/renderer/rasterize/expand/pass1/filter_meshlets.2-way.compute.js +153 -154
  492. package/src/shade/renderer/rasterize/native/depth/shadow_rasterization_alpha_tested_pass_descriptor.js +144 -144
  493. package/src/shade/renderer/rasterize/native/depth/shadow_rasterization_pass_descriptor.js +88 -88
  494. package/src/shade/renderer/rasterize/native/oit/graph_rasterize_meshes_transparent_oit.js +722 -722
  495. package/src/shade/renderer/rasterize/native/oit/shader_oit_generate_moments.js +217 -217
  496. package/src/shade/renderer/rasterize/native/oit/shader_oit_resolve_moments.js +179 -179
  497. package/src/shade/renderer/rasterize/native/oit/shader_oit_resolve_moments_ibl.js +178 -178
  498. package/src/shade/renderer/rasterize/native/viz/viz_rasterization_alpha_tested_pass_descriptor.js +194 -194
  499. package/src/shade/renderer/rasterize/native/viz/viz_rasterization_opaque_pass_descriptor.js +76 -76
  500. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.d.ts.map +1 -1
  501. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.js +1 -9
  502. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.d.ts.map +1 -1
  503. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.js +1 -6
  504. package/src/shade/renderer/restir/di/graph_restir_di.d.ts.map +1 -1
  505. package/src/shade/renderer/restir/di/graph_restir_di.js +2 -3
  506. package/src/shade/renderer/restir/di/graph_restir_di_denoise.d.ts.map +1 -1
  507. package/src/shade/renderer/restir/di/graph_restir_di_denoise.js +1 -2
  508. package/src/shade/renderer/restir/di/shader_restir_di_denoise.d.ts.map +1 -1
  509. package/src/shade/renderer/restir/di/shader_restir_di_denoise.js +5 -3
  510. package/src/shade/renderer/restir/di/shader_restir_di_resample.d.ts.map +1 -1
  511. package/src/shade/renderer/restir/di/shader_restir_di_resample.js +4 -3
  512. package/src/shade/renderer/restir/di/shader_restir_di_resolve.d.ts.map +1 -1
  513. package/src/shade/renderer/restir/di/shader_restir_di_resolve.js +4 -3
  514. package/src/shade/renderer/restir/di/shader_restir_di_spatial.d.ts.map +1 -1
  515. package/src/shade/renderer/restir/di/shader_restir_di_spatial.js +116 -117
  516. package/src/shade/renderer/scene/GPUInstancesAccelerationStructure.d.ts.map +1 -1
  517. package/src/shade/renderer/scene/GPUInstancesAccelerationStructure.js +3 -7
  518. package/src/shade/renderer/scene/hierarchy/graph_scene_update_transform_hierarchy_naive.js +123 -123
  519. package/src/shade/renderer/scene/hierarchy/shader_nodes_record_input_positions.d.ts.map +1 -1
  520. package/src/shade/renderer/scene/hierarchy/shader_nodes_record_input_positions.js +64 -65
  521. package/src/shade/renderer/scene/hierarchy/shader_nodes_update_transform_hierarchy.d.ts.map +1 -1
  522. package/src/shade/renderer/scene/hierarchy/shader_nodes_update_transform_hierarchy.js +372 -373
  523. package/src/shade/renderer/scene/hierarchy/shader_nodes_update_transform_hierarchy_naive.d.ts.map +1 -1
  524. package/src/shade/renderer/scene/hierarchy/shader_nodes_update_transform_hierarchy_naive.js +111 -112
  525. package/src/shade/renderer/scene/shader_bvh_extract_instance_leaves.d.ts.map +1 -1
  526. package/src/shade/renderer/scene/shader_bvh_extract_instance_leaves.js +147 -145
  527. package/src/shade/renderer/shader/ComputeShader.d.ts +26 -5
  528. package/src/shade/renderer/shader/ComputeShader.d.ts.map +1 -1
  529. package/src/shade/renderer/shader/ComputeShader.js +347 -296
  530. package/src/shade/renderer/shader/ImageShader.js +217 -217
  531. package/src/shade/renderer/shader/ShaderDescriptor.d.ts +5 -0
  532. package/src/shade/renderer/shader/ShaderDescriptor.d.ts.map +1 -1
  533. package/src/shade/renderer/shader/ShaderDescriptor.js +20 -0
  534. package/src/shade/renderer/shader/pass/RenderPassDescriptor.d.ts +2 -2
  535. package/src/shade/renderer/shader/pass/RenderPassDescriptor.d.ts.map +1 -1
  536. package/src/shade/renderer/shader/pass/RenderPassDescriptor.js +189 -189
  537. package/src/shade/renderer/shader/resource/ShaderResourceSetDescriptor.d.ts.map +1 -1
  538. package/src/shade/renderer/shader/resource/ShaderResourceSetDescriptor.js +3 -1
  539. package/src/shade/renderer/shader/util/compute_infer_workgroup_size_from_source.d.ts +47 -0
  540. package/src/shade/renderer/shader/util/compute_infer_workgroup_size_from_source.d.ts.map +1 -0
  541. package/src/shade/renderer/shader/util/compute_infer_workgroup_size_from_source.js +448 -0
  542. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.d.ts.map +1 -1
  543. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.js +14 -18
  544. package/src/shade/renderer/shadow/map/csm/graph_csm_setup_cascades.js +3 -3
  545. package/src/shade/renderer/shadow/map/shader/shader_shadowmap_remap_cube_to_octahedral.d.ts.map +1 -1
  546. package/src/shade/renderer/shadow/map/shader/shader_shadowmap_remap_cube_to_octahedral.js +5 -1
  547. package/src/shade/renderer/shadow/ray/shader_shadow_upscale.d.ts.map +1 -1
  548. package/src/shade/renderer/shadow/ray/shader_shadow_upscale.js +3 -1
  549. package/src/shade/renderer/shadow/ray/staged/gen/graph_gen_rays.js +73 -73
  550. package/src/shade/renderer/shadow/ray/staged/gen/shader_generate_rays.d.ts.map +1 -1
  551. package/src/shade/renderer/shadow/ray/staged/gen/shader_generate_rays.js +151 -148
  552. package/src/shade/renderer/shadow/ray/staged/trace/graph_trace_rays.d.ts.map +1 -1
  553. package/src/shade/renderer/shadow/ray/staged/trace/graph_trace_rays.js +6 -4
  554. package/src/shade/renderer/shadow/ray/staged/trace/shader_trace_rays.d.ts.map +1 -1
  555. package/src/shade/renderer/shadow/ray/staged/trace/shader_trace_rays.js +18 -4
  556. package/src/shade/renderer/shadow/sdf/cascade/CascadedSceneSDF.d.ts.map +1 -1
  557. package/src/shade/renderer/shadow/sdf/cascade/CascadedSceneSDF.js +11 -12
  558. package/src/shade/renderer/shadow/sdf/cascade/shader_build_scene_sdf_f32.d.ts.map +1 -1
  559. package/src/shade/renderer/shadow/sdf/cascade/shader_build_scene_sdf_f32.js +6 -2
  560. package/src/shade/renderer/shadow/sdf/cascade/shader_scene_sdf_debug_viz.d.ts.map +1 -1
  561. package/src/shade/renderer/shadow/sdf/cascade/shader_scene_sdf_debug_viz.js +129 -127
  562. package/src/shade/renderer/shadow/sdf/graph_draw_sdf_shadows.d.ts.map +1 -1
  563. package/src/shade/renderer/shadow/sdf/graph_draw_sdf_shadows.js +5 -3
  564. package/src/shade/renderer/texture/virtual/VirtualTextureManager.d.ts.map +1 -1
  565. package/src/shade/renderer/texture/virtual/VirtualTextureManager.js +2 -5
  566. package/src/shade/renderer/texture/virtual/build_shader_vt_feedback.d.ts.map +1 -1
  567. package/src/shade/renderer/texture/virtual/build_shader_vt_feedback.js +3 -2
  568. package/src/shade/renderer/volumetrics/GPUViewVolumetrics.js +1 -1
  569. package/src/shade/renderer/volumetrics/graph_build_volumetrics.d.ts.map +1 -1
  570. package/src/shade/renderer/volumetrics/graph_build_volumetrics.js +14 -19
  571. package/src/shade/renderer/volumetrics/shader_volumetrics_build_lighting.d.ts +3 -2
  572. package/src/shade/renderer/volumetrics/shader_volumetrics_build_lighting.d.ts.map +1 -1
  573. package/src/shade/renderer/volumetrics/shader_volumetrics_build_lighting.js +3 -2
  574. package/src/shade/renderer/volumetrics/taa/shader_volumetrics_taa.d.ts +0 -2
  575. package/src/shade/renderer/volumetrics/taa/shader_volumetrics_taa.d.ts.map +1 -1
  576. package/src/shade/renderer/volumetrics/taa/shader_volumetrics_taa.js +17 -6
  577. package/src/shade/renderer/water/shader_water.js +246 -246
  578. package/src/shade/vitest.setup.mjs +1 -1
  579. package/src/shade/wgsl/emulator/ComputeShaderEmulator.js +291 -291
  580. package/src/shade/wgsl/validate_wgsl_source.d.ts.map +1 -1
  581. package/src/shade/wgsl/validate_wgsl_source.js +8 -3
  582. package/src/view/minimap/Minimap.d.ts +2 -2
  583. package/src/view/minimap/Minimap.d.ts.map +1 -1
  584. package/src/view/minimap/Minimap.js +225 -225
  585. package/src/view/minimap/dom/MinimapCameraView.d.ts +3 -3
  586. package/src/view/minimap/dom/MinimapCameraView.d.ts.map +1 -1
  587. package/src/view/minimap/dom/MinimapCameraView.js +206 -206
  588. package/src/shade/device/timing/profile/make_profiling_pass_encoder.d.ts +0 -21
  589. package/src/shade/device/timing/profile/make_profiling_pass_encoder.d.ts.map +0 -1
  590. package/src/shade/device/timing/profile/make_profiling_pass_encoder.js +0 -114
  591. package/src/shade/device/timing/profile/parse_workgroup_size.d.ts +0 -20
  592. package/src/shade/device/timing/profile/parse_workgroup_size.d.ts.map +0 -1
  593. package/src/shade/device/timing/profile/parse_workgroup_size.js +0 -43
  594. package/src/shade/renderer/shadow/map/csm/shader/shader_setup_cascades.d.ts +0 -3
  595. package/src/shade/renderer/shadow/map/csm/shader/shader_setup_cascades.d.ts.map +0 -1
  596. package/src/shade/renderer/shadow/map/csm/shader/shader_setup_cascades.js +0 -20
@@ -0,0 +1,750 @@
1
+ # Pass encoder decorators, and the end of the debug-timer path
2
+
3
+ **Status:** implemented — revision 3
4
+ **Date:** 2026-08-31
5
+ **Scope:** `src/shade/device/ShadeGPUCommandContext.js`, `src/shade/device/timing/`, `src/shade/descriptor/pipeline/compute/`, `src/shade/renderer/Renderer.js`
6
+ **Supersedes:** [GPU_PROFILER_PROPOSAL_2026_08_28.md](timing/GPU_PROFILER_PROPOSAL_2026_08_28.md) §6.1 (last paragraph) and §6.2 hook 3
7
+
8
+ > **Revision 2** carried the workgroup size rather than re-parsing it (§3.5), dropped the pretence
9
+ > of WebGPU overload compatibility (§3.2), removed the scratch-object side channel revision 1
10
+ > introduced (§3.4), and added an adversarial pass (§8) that changed four decisions.
11
+ >
12
+ > **Revision 3** records what shipped. The §9 decisions were taken (§9), the error-carrying
13
+ > `workgroup_size_error` of revision 2 was rejected in favour of throwing at construction (§3.5),
14
+ > and the code landed in three commits (§10).
15
+
16
+ ---
17
+
18
+ ## 1. What is there now
19
+
20
+ Profiling reaches a GPU pass through four private members of `ShadeGPUCommandContext` and one `Proxy`.
21
+
22
+ | Piece | Where | What it does |
23
+ |---|---|---|
24
+ | `#with_timestamp_writes(descriptor, kind)` | [ShadeGPUCommandContext.js:461](ShadeGPUCommandContext.js:461) | Branches on the string `'compute'`/`'render'`, asks `GPUTimerArray` for a slot, shallow-copies the descriptor to add `timestampWrites`, and **as a side effect** writes `#pending_work`. |
25
+ | `#pending_work` | [:128](ShadeGPUCommandContext.js:128) | A one-slot channel between `#with_timestamp_writes` and `#instrument_pass`. |
26
+ | `#instrument_pass(encoder)` | [:136](ShadeGPUCommandContext.js:136) | Reads `#pending_work`, clears it, writes `#last_work`, and returns either the raw encoder or a `Proxy`. |
27
+ | `#last_work` | [:577](ShadeGPUCommandContext.js:577) | A second one-slot channel, this time between `begin*Pass` and `#note_pipeline`. |
28
+ | `make_profiling_pass_encoder` | [timing/profile/make_profiling_pass_encoder.js](timing/profile/make_profiling_pass_encoder.js) | A `Proxy` whose `get` trap switches on the method **name as a string**, wraps six of them, memoises bound closures in a `Map`, and forwards the rest by `Reflect`. |
29
+ | `parse_workgroup_size` | [timing/profile/parse_workgroup_size.js](timing/profile/parse_workgroup_size.js) | Re-parses WGSL per pass to recover `@workgroup_size`. Already marked `@deprecated`: it duplicates `compute_infer_workgroup_size_from_source`, badly. |
30
+
31
+ So `beginComputePass` reads:
32
+
33
+ ```js
34
+ const _descriptor = this.#with_timestamp_writes(descriptor, 'compute');
35
+
36
+ return this.#instrument_pass(encoder.beginComputePass(_descriptor))
37
+ ```
38
+
39
+ Two calls, neither of which names what it produced, coupled through two private fields that neither
40
+ signature mentions.
41
+
42
+ Alongside it, a second entry point into the same machinery:
43
+
44
+ | Piece | Where | What it does |
45
+ |---|---|---|
46
+ | `enable_debug_timers(cb)` | [:93](ShadeGPUCommandContext.js:93) | Allocates the `GPUTimerArray`, and at `finish()` hands `results_to_console_table()` to a callback. |
47
+ | `Renderer.add_debug_frame(n)` / `Renderer.onFrameDebug` | [Renderer.js:671](../renderer/Renderer.js:671), [:431](../renderer/Renderer.js:431) | Arms N frames, fires the table. |
48
+
49
+ ## 2. What is wrong with it
50
+
51
+ **The two hidden channels.** `#pending_work` and `#last_work` are the whole objection. Neither
52
+ appears in a signature. `#with_timestamp_writes` is a *getter-shaped* method that mutates; the only
53
+ thing keeping it correct is that the very next statement consumes what it left behind. Insert a line
54
+ between them, reach `beginComputePass` from inside its call tree, or throw part way, and the next
55
+ pass silently inherits the previous pass's work record. Nothing would catch it.
56
+
57
+ **`kind` as a string.** `#with_timestamp_writes(descriptor, 'compute')` re-derives, by string
58
+ comparison, a fact the call site already knew statically — it is *literally inside*
59
+ `beginComputePass`.
60
+
61
+ **Reflection where a method call belongs.** `make_profiling_pass_encoder` switches on
62
+ `'dispatchWorkgroups'`, `'draw'`, `'drawIndexed'` as strings, in a `Proxy` `get` trap, on the hot
63
+ encoding path. `work.dispatch_count++` is three characters of real logic wrapped in a dispatch
64
+ mechanism.
65
+
66
+ **The Proxy's own justification does not survive contact with the fix.** The file argues a
67
+ hand-written forwarder is dangerous because a missed method "does not fail loudly, it silently drops
68
+ a call … and only while profiling". That is true *only because the wrapper is conditional*. Make the
69
+ decorator the one and only pass type Shade hands out, and the failure mode inverts: a missing method
70
+ is `pass.setScissorRect is not a function`, on the first frame, in every build, profiling or not. The
71
+ argument for the Proxy is an argument against *conditional* wrapping, not against hand-writing the
72
+ forwarder.
73
+
74
+ **Three silent returns.** `#with_timestamp_writes` returns the descriptor unchanged for three
75
+ different reasons (timers off, device lacks `timestamp-query`, array full) and the caller cannot tell
76
+ which — nor that the pass ended up untimed.
77
+
78
+ **A second, worse WGSL parser, run per pass.** `parse_workgroup_size` reads a bare decimal literal
79
+ and returns `[0,0,0]` for anything else — while `compute_infer_workgroup_size_from_source`, twenty
80
+ metres away, resolves `const` references, strips comments, handles hex and `i`/`u` suffixes, and
81
+ throws with a specific message when it genuinely cannot tell. `ComputeShader` already calls the good
82
+ one **once, at construction**, and keeps the answer. The profiler re-derives a worse answer at encode
83
+ time, for a value that has not changed since the pipeline was created.
84
+
85
+ **`enable_debug_timers` is a redundant second face.** It shares the `GPUTimerArray` with
86
+ `enable_profiling`, either may allocate it, and `finish()` runs both drains. It produces a
87
+ slot-shaped console table with no graph-pass attribution, no workload, no pipeline identity — a
88
+ strictly worse view than the profiler's, reached through a separate API. Its consumers:
89
+
90
+ - `Renderer.add_debug_frame` / `Renderer.onFrameDebug` — **zero subscribers anywhere in the tree**
91
+ (grep across all packages; the 2026-08-28 proposal noted the same and left them in).
92
+ - [verify_gpu_bvh_build.js:316](../renderer/gpu_primitive/bvh/verify_gpu_bvh_build.js:316) — one
93
+ developer verification tool that wants a console table.
94
+
95
+ That is the entire public surface. It is the cheapest moment this will ever be to remove it.
96
+
97
+ ---
98
+
99
+ ## 3. Proposed design
100
+
101
+ ### 3.1 `ShadeGPUComputePassEncoder` / `ShadeGPURenderPassEncoder`
102
+
103
+ Two concrete decorator classes, in `src/shade/device/pass/`. **Every** pass Shade opens is one of
104
+ these; there is no unwrapped path and no conditional wrapping. They hold a nullable
105
+ `GPUProfileWork` and tally into it.
106
+
107
+ They import **nothing** from `timing/profile/`. `work` is a bag of counters, `null` is the ordinary
108
+ case, and the classes do not know what a capture is — which preserves (in fact improves) the
109
+ tree-shaking property `GPUProfileSession`'s header promises.
110
+
111
+ ### 3.2 Forward exactly the arity you received
112
+
113
+ These are not a compatibility shim. Following WebGPU's method names removes friction for anyone who
114
+ already knows the API; it is not a promise of substitutability.
115
+
116
+ **The rule, learnt the hard way (§8.9): a forwarder passes exactly the arity it was given, and never
117
+ reasons about what a binding layer will do with a trailing `undefined`.** Three shapes satisfy it,
118
+ and every method in both classes uses one of them:
119
+
120
+ ```js
121
+ // 1. Only forwards, and has trailing optionals → rest arguments. Arity is preserved by
122
+ // construction, which is the property that matters and the one a reader should not have to
123
+ // verify branch by branch. `size` here means "the rest of the buffer" and has no default to
124
+ // stand in for at all, so an absent argument and an `undefined` one are different requests.
125
+ setVertexBuffer(...args) {
126
+ this.#pass.setVertexBuffer(...args);
127
+ }
128
+
129
+ // 2. One trailing optional → branch on it. Cheap, and it also means this form can never select
130
+ // the five-argument overload by accident, which is what makes `setBindGroupRange` an honest
131
+ // name rather than only a convention.
132
+ setBindGroup(index, bind_group, dynamic_offsets) {
133
+ if (dynamic_offsets === undefined) {
134
+ this.#pass.setBindGroup(index, bind_group);
135
+ } else {
136
+ this.#pass.setBindGroup(index, bind_group, dynamic_offsets);
137
+ }
138
+ }
139
+
140
+ // 3. Has to read its arguments in order to count them → named parameters, with WebGPU's own
141
+ // defaults spelled out. Those defaults are 1 and 0, so materialising them passes four defined
142
+ // values rather than a trailing `undefined`, and is equivalent to omitting them.
143
+ draw(vertex_count, instance_count = 1, first_vertex = 0, first_instance = 0) { /* ... */ }
144
+ ```
145
+
146
+ Where WebGPU resolves an overload by arity, Shade gives each form its own name: `setBindGroup` for
147
+ the `sequence` form and `setBindGroupRange` for the `Uint32Array`-slice form. Nothing in the
148
+ renderer uses the slice form today; it exists because `SoftwareGPUComputePassEncoder` models it.
149
+
150
+ > Revision 2 got this exactly half right. It identified `setBindGroup` as the one forward that could
151
+ > be *wrong* rather than merely incomplete, then guarded the wrong end of it — see §8.9.
152
+
153
+ ### 3.3 The compute decorator in full
154
+
155
+ ```js
156
+ export class ShadeGPUComputePassEncoder {
157
+
158
+ /** @type {GPUComputePassEncoder} */
159
+ #pass;
160
+
161
+ /**
162
+ * Where this pass's encoded work is tallied, or null when nothing is profiling.
163
+ * @type {GPUProfileWork|null}
164
+ */
165
+ #work;
166
+
167
+ #ended = false;
168
+
169
+ /**
170
+ * @param {GPUComputePassEncoder} pass
171
+ * @param {GPUProfileWork|null} work
172
+ */
173
+ constructor(pass, work) {
174
+ assert.defined(pass, 'pass');
175
+
176
+ this.#pass = pass;
177
+ this.#work = work;
178
+ }
179
+
180
+ /** @returns {string} */
181
+ get label() { return this.#pass.label; }
182
+
183
+ /** @param {string} value */
184
+ set label(value) { this.#pass.label = value; }
185
+
186
+ // --- GPUComputePassEncoder ------------------------------------------------
187
+
188
+ setPipeline(pipeline) {
189
+ this.#pass.setPipeline(pipeline);
190
+ }
191
+
192
+ dispatchWorkgroups(x, y = 1, z = 1) {
193
+ const work = this.#work;
194
+
195
+ if (work !== null) {
196
+ work.dispatch_count++;
197
+ work.workgroups[0] += x;
198
+ work.workgroups[1] += y;
199
+ work.workgroups[2] += z;
200
+ }
201
+
202
+ this.#pass.dispatchWorkgroups(x, y, z);
203
+ }
204
+
205
+ dispatchWorkgroupsIndirect(indirect_buffer, indirect_offset) {
206
+ const work = this.#work;
207
+
208
+ if (work !== null) {
209
+ // Size lives in a GPU buffer, so there is nothing to add to `workgroups`. Counted
210
+ // apart so the totals are not read as covering this.
211
+ work.indirect_dispatch_count++;
212
+ }
213
+
214
+ this.#pass.dispatchWorkgroupsIndirect(indirect_buffer, indirect_offset);
215
+ }
216
+
217
+ end() {
218
+ assert.notOk(this.#ended, 'pass already ended');
219
+
220
+ this.#ended = true;
221
+
222
+ this.#pass.end();
223
+ }
224
+
225
+ // --- GPUBindingCommandsMixin ----------------------------------------------
226
+
227
+ setBindGroup(index, bind_group, dynamic_offsets) { /* §3.2 */ }
228
+
229
+ setBindGroupRange(index, bind_group, data, data_start, data_length) { /* §3.2 */ }
230
+
231
+ setImmediates(range_offset, data, data_offset, data_size) {
232
+ this.#pass.setImmediates(range_offset, data, data_offset, data_size);
233
+ }
234
+
235
+ // --- GPUDebugCommandsMixin ------------------------------------------------
236
+
237
+ pushDebugGroup(label) { this.#pass.pushDebugGroup(label); }
238
+
239
+ popDebugGroup() { this.#pass.popDebugGroup(); }
240
+
241
+ insertDebugMarker(label) { this.#pass.insertDebugMarker(label); }
242
+
243
+ // --- Shade ----------------------------------------------------------------
244
+
245
+ /**
246
+ * Record which pipeline descriptor this pass ran.
247
+ *
248
+ * Separate from `setPipeline` because `setPipeline` receives the compiled
249
+ * {@link GPUComputePipeline}, and neither the label the descriptor was given nor its workgroup
250
+ * size is on it. Only `constructComputePass` holds the descriptor, so a pass begun the long way
251
+ * records its counts but not what ran them — which the model represents as an unknown rather
252
+ * than a zero.
253
+ *
254
+ * @param {ComputePipelineDescriptor} descriptor
255
+ * @returns {void}
256
+ */
257
+ record_pipeline(descriptor) {
258
+ const work = this.#work;
259
+
260
+ if (work === null) {
261
+ return;
262
+ }
263
+
264
+ work.pipeline = descriptor.label ?? "";
265
+ work.workgroup_size = descriptor.workgroup_size;
266
+ }
267
+ }
268
+ ```
269
+
270
+ `ShadeGPURenderPassEncoder` is the mirror image over the render surface: `setViewport`,
271
+ `setScissorRect`, `setBlendConstant`, `setStencilReference`, `setIndexBuffer`, `setVertexBuffer`,
272
+ `beginOcclusionQuery`, `endOcclusionQuery`, `executeBundles`, `draw`, `drawIndexed`, `drawIndirect`,
273
+ `drawIndexedIndirect`, plus the same two mixins, `end` and `record_pipeline`. Only the four `draw*`
274
+ methods carry a tally; the rest forward.
275
+
276
+ **Only six methods branch** — four `draw*` and two `dispatch*`. Everything else is a one-line
277
+ forward. Both classes come to roughly 130 lines and read top to bottom as a complete statement of the
278
+ interface they decorate, which is the entire point.
279
+
280
+ > **Known blind spot, inherited:** draws inside a `GPURenderBundle` passed to `executeBundles` are
281
+ > not counted — the bundle was encoded elsewhere. The Proxy had the same gap and nothing in Shade
282
+ > uses bundles today. It belongs in `GPUProfileWork`'s documentation rather than being left implicit.
283
+
284
+ ### 3.4 The context's profiling state, in one object
285
+
286
+ `ShadeGPUCommandContext` currently carries six fields for a feature that is off by default:
287
+ `#debug_timers`, `#debug_timers_callback`, `#profile_recorder`, `#profile_query_set_id`,
288
+ `#pending_work`, `#last_work`, plus `#profiling_absorbed` and its resolver. Collapse them into one
289
+ nullable `#profiler`.
290
+
291
+ The profiler **opens the pass itself**. Two values are needed at two different instants —
292
+ `timestampWrites` before the encoder exists, the `GPUProfileWork` after — and any design that returns
293
+ them to the caller has to carry them across that gap somehow. Handing the profiler the encoder
294
+ collapses the gap: both values are produced and consumed inside one method, and there is nothing to
295
+ carry. (Revision 1 returned a reused `{writes, work}` struct, which was `#pending_work` again in
296
+ better clothes. See §8.3.)
297
+
298
+ ```js
299
+ /**
300
+ * Everything one command context needs in order to be profiled: its timer array, the recorder the
301
+ * timings go to, and which query set it is within the frame.
302
+ */
303
+ export class ShadeGPUContextProfiler {
304
+
305
+ #timers;
306
+ #recorder;
307
+ #query_set_id;
308
+
309
+ #absorbed;
310
+ #absorbed_resolve;
311
+
312
+ constructor(device, recorder, query_set_id) {
313
+ this.#timers = new GPUTimerArray(device);
314
+ this.#recorder = recorder;
315
+ this.#query_set_id = query_set_id;
316
+
317
+ this.#absorbed = new Promise(resolve => { this.#absorbed_resolve = resolve; });
318
+ }
319
+
320
+ get absorbed() { return this.#absorbed; }
321
+
322
+ /**
323
+ * @param {GPUCommandEncoder} encoder
324
+ * @param {GPUComputePassDescriptor} [descriptor]
325
+ * @returns {ShadeGPUComputePassEncoder}
326
+ */
327
+ begin_compute_pass(encoder, descriptor) {
328
+ const writes = this.#timers.getComputeWrites(descriptor?.label);
329
+
330
+ if (writes === undefined) {
331
+ /*
332
+ No slot left, or the device withheld `timestamp-query`. An untimed pass is the correct
333
+ degradation in both; a pass carrying query indices the set does not have is a
334
+ validation error blamed on a pass that did nothing wrong.
335
+ */
336
+ return new ShadeGPUComputePassEncoder(encoder.beginComputePass(descriptor), null);
337
+ }
338
+
339
+ /*
340
+ Copied, never written through. Several call sites reuse one descriptor across passes, and
341
+ stamping `timestampWrites` onto it left the next pass carrying the previous pass's query
342
+ indices. `{...undefined}` is `{}`, which is also how the no-descriptor form stays legal.
343
+ */
344
+ const gpu_pass = encoder.beginComputePass({ ...descriptor, timestampWrites: writes });
345
+
346
+ return new ShadeGPUComputePassEncoder(gpu_pass, this.#note_slot(writes));
347
+ }
348
+
349
+ /** The render mirror of {@link begin_compute_pass}. */
350
+ begin_render_pass(encoder, descriptor) { /* getRenderWrites, ShadeGPURenderPassEncoder */ }
351
+
352
+ /**
353
+ * @param {GPUComputePassTimestampWrites|GPURenderPassTimestampWrites} writes
354
+ * @returns {GPUProfileWork|null}
355
+ */
356
+ #note_slot(writes) {
357
+ /*
358
+ The one moment the join can be made. The recorder knows which frame graph pass is
359
+ executing right now; the slot is what the timings will come back keyed by. Neither fact is
360
+ recoverable later — see GPUFrameRecorder.
361
+ */
362
+ return this.#recorder.note_pass_slot(
363
+ this.#query_set_id,
364
+ writes.beginningOfPassWriteIndex / 2
365
+ );
366
+ }
367
+
368
+ /** @param {GPUCommandEncoder} encoder */
369
+ resolve(encoder) { this.#timers.resolve(encoder); }
370
+
371
+ /**
372
+ * Read the timings back and hand them to the recorder. Resolves {@link absorbed} either way.
373
+ * @returns {void}
374
+ */
375
+ absorb() {
376
+ const timers = this.#timers;
377
+
378
+ timers.download_results()
379
+ .then(() => { this.#recorder.absorb(timers, this.#query_set_id); })
380
+ .finally(() => {
381
+ timers.destroy();
382
+
383
+ // In `finally` so a failed readback still releases whoever is waiting on the frame.
384
+ // A profile session that hangs because one download rejected is worse than one
385
+ // missing a frame.
386
+ this.#absorbed_resolve();
387
+ });
388
+ }
389
+ }
390
+ ```
391
+
392
+ `beginComputePass` on the context becomes five lines with two named outcomes:
393
+
394
+ ```js
395
+ /**
396
+ * @param {GPUComputePassDescriptor} [descriptor]
397
+ * @returns {ShadeGPUComputePassEncoder}
398
+ */
399
+ beginComputePass(descriptor) {
400
+ const profiler = this.#profiler;
401
+
402
+ if (profiler === null) {
403
+ return new ShadeGPUComputePassEncoder(this.#gpu_encoder.beginComputePass(descriptor), null);
404
+ }
405
+
406
+ return profiler.begin_compute_pass(this.#gpu_encoder, descriptor);
407
+ }
408
+ ```
409
+
410
+ No `kind` string. No hidden field. `beginRenderPass` is the same five lines.
411
+
412
+ `constructComputePass` loses `#note_pipeline` and `#last_work` entirely:
413
+
414
+ ```js
415
+ const pass = this.beginComputePass(pass_descriptor);
416
+
417
+ pass.setPipeline(gpu_pipeline);
418
+ pass.record_pipeline(pipeline);
419
+
420
+ graphics.setPipelineBindings(pass, pipeline, bindings, immediate);
421
+ ```
422
+
423
+ ### 3.5 The workgroup size is carried, not re-parsed
424
+
425
+ `ComputePipelineDescriptor` reads `@workgroup_size` out of its code **as the code is set**, in
426
+ `from`, `simplified` and `fromJSON`, and becomes the single owner of the answer:
427
+
428
+ ```js
429
+ /**
430
+ * `[x, y, z]` of this pipeline's `@workgroup_size`.
431
+ *
432
+ * Read once, when the code is set, and kept — the source cannot change under a descriptor without
433
+ * going back through one of the factories, each of which reads it again.
434
+ *
435
+ * Frozen, because it is handed out by reference to per-pass profile records and a descriptor that
436
+ * outlives a whole capture must not be mutable through one of them.
437
+ *
438
+ * Takes no part in `equals`/`hash`: it is derived from `compute.module`, which both already cover,
439
+ * and a cache key computed two ways is a cache that can disagree with itself.
440
+ *
441
+ * @returns {readonly number[]}
442
+ */
443
+ get workgroup_size()
444
+ ```
445
+
446
+ Backed by `compute_infer_workgroup_size_from_source`. **Source it cannot read throws, at
447
+ construction.** Revision 2 proposed a `workgroup_size_error` companion so the descriptor could
448
+ report rather than throw, on the grounds that a capture must not take a frame down. That was
449
+ rejected, and rightly:
450
+
451
+ > The user can't construct a ComputeShader because their source is messed up — that's preferred to
452
+ > them constructing an invalid shader that we then have to recognize and deal with.
453
+
454
+ Every consumer of this number divides a thread count by it. A descriptor that cannot state its own
455
+ workgroup size is one nobody downstream can use correctly, so `[0, 0, 0]` would only defer the
456
+ failure to somewhere with less context to explain it. The only `[0, 0, 0]` left is a bare
457
+ `new ComputePipelineDescriptor()` that has no code yet — not a fallback, a state `new` alone
458
+ produces.
459
+
460
+ `ComputeShader` reads the descriptor instead of parsing separately. Its message keeps the shader's
461
+ label because the pipeline is given the same one, so nothing is wrapped and nothing is lost.
462
+
463
+ This is strictly better than what either side did before:
464
+
465
+ - **One parse**, at pipeline-descriptor construction, by the parser that actually handles WGSL.
466
+ - The nine `ComputePipelineDescriptor` sites that do **not** go through `ComputeShader` — VSM
467
+ residence, tile allocation, coarse bounds, probe dering, indirect command build ×2, irradiance
468
+ lookup, debug reprojection, virtual shadow residence — get correct workgroup sizes in profiles
469
+ for the first time. `parse_workgroup_size` returned `[0,0,0]` for any of them stating the size
470
+ through a `const`.
471
+ - `parse_workgroup_size.js` is deleted, resolving the `@deprecated` note already on it.
472
+ - `timing/profile/` loses its last unconditionally-linked module, so the whole profiler directory
473
+ is now unreachable from any engine entry point — which is what `GPUProfileSession`'s header
474
+ claims and, before this, was not quite true.
475
+
476
+ **`fromJSON` re-reads it.** `ComputePipelineDescriptor.fromJSON` replaces `compute` in place, so a
477
+ size read for whatever was there before would be the wrong shader's — and wrong here means a wrong
478
+ *divisor*. Covered by a spec.
479
+
480
+ Verified before landing that eager reading breaks nothing: every compute pipeline reachable in the
481
+ tree parses. Two modules would not, and both are unreferenced — `GPUVirtualShadowTextureResidence`
482
+ (which already throws on import today, for an unrelated missing default export) and
483
+ `graph_pass_debug_reprojection`.
484
+
485
+ ### 3.6 A check the device already does, dropped
486
+
487
+ `#with_timestamp_writes` guards on `this.#graphics.device.features.has('timestamp-query')`. This is
488
+ redundant: `GPUTimerArray`'s constructor returns early without the feature, leaving `#query_set`
489
+ undefined, and `#get_writes` already returns `undefined` in that case
490
+ ([GPUTimerArray.js:97](timing/GPUTimerArray.js:97)). One fewer place that has to know the feature
491
+ exists.
492
+
493
+ ---
494
+
495
+ ## 4. Removing the debug-timer path
496
+
497
+ | File | Change |
498
+ |---|---|
499
+ | `ShadeGPUCommandContext.js` | Delete `enable_debug_timers` (:93), `#debug_timers_callback` (:87), and the `results_to_console_table` branch in `finish()` (:975). The `noop` import becomes unused — its only two uses are `#debug_timers_callback` and `#profiling_absorbed_resolve`, and both leave. |
500
+ | `renderer/Renderer.js` | Delete `onFrameDebug` (:431), `#debug_frame_count` (:606), `add_debug_frame` (:671), and the arming block (:1736–1744). **Zero subscribers repo-wide.** |
501
+ | `renderer/gpu_primitive/bvh/verify_gpu_bvh_build.js` | Move onto the profiler (below). |
502
+ | `RENDERER_CONTRACT.md:544` | Drop `add_debug_frame(n)` from the surface table. |
503
+ | `timing/GPU_PROFILER_PROPOSAL_2026_08_28.md` | §6.1 last ¶, §6.2 hook 3, and table rows 57/59/60 describe things that no longer exist. |
504
+ | `gpu_primitive/bvh/GPU_BVH_BUILD_PLAN.md:235`, `PHASE1_HANDOVER.md:148` | Same. |
505
+
506
+ The replacement for the one real consumer is *better* than what it replaces — it gains dispatch and
507
+ invocation counts, which is what a BVH build verification actually wants:
508
+
509
+ ```js
510
+ let recorder = null;
511
+
512
+ if (test_case.count >= 100000) {
513
+ recorder = new GPUFrameRecorder(0, performance.now());
514
+ recorder.enable_work_capture();
515
+
516
+ cmd.enable_profiling(recorder, 0);
517
+ }
518
+
519
+ // ... build the graph ...
520
+
521
+ if (recorder !== null) {
522
+ recorder.attach_graph(graph);
523
+ }
524
+
525
+ cmd.encodeGraph(graph);
526
+ cmd.finish();
527
+
528
+ if (recorder !== null) {
529
+ const captured = recorder;
530
+
531
+ cmd.profiling_absorbed.then(() => {
532
+ const frame = captured.close(performance.now());
533
+
534
+ console.log(`[BVH] GPU timings — ${test_case.name}`);
535
+ console.table(frame.spans.map(span => ({
536
+ label: span.label,
537
+ duration_ms: span.duration_ns * 1e-6,
538
+ dispatches: span.work?.dispatch_count ?? 0,
539
+ invocations: span.work?.invocation_count ?? 0
540
+ })));
541
+ });
542
+ }
543
+ ```
544
+
545
+ "Profile the next N frames" — what `add_debug_frame` was for — is already
546
+ `new GPUProfileSession({ frame_limit: N })`.
547
+
548
+ ---
549
+
550
+ ## 5. One migration hazard, worth stating plainly
551
+
552
+ **Do not null `#profiler` inside `finish()`.** The renderer reads `profiling_absorbed` *after*
553
+ `finish()` ([Renderer.js:2520, then :2531](../renderer/Renderer.js:2531)). If `profiling_absorbed`
554
+ becomes a getter that returns `Promise.resolve()` when `#profiler === null`, nulling the field in
555
+ `finish()` hands the renderer an already-resolved promise and it commits an empty frame — every
556
+ frame, silently, with no error anywhere. The current code survives this only because
557
+ `#profiling_absorbed` is a separate cached field.
558
+
559
+ The profiler holds no GPU resources after `absorb()` — it destroys the timer array itself — so
560
+ leaving the reference in place is correct and costs nothing.
561
+
562
+ ---
563
+
564
+ ## 6. Testing
565
+
566
+ | Tier | What |
567
+ |---|---|
568
+ | **Unit, node** | Port `sgpt_workload.spec.js` off `make_profiling_pass_encoder` onto the two classes: counting *and* forwarding, which it already tests. Its "a method the proxy has never heard of still works" test is deleted — that behaviour is gone deliberately. |
569
+ | **Unit, node** | `ComputePipelineDescriptor.workgroup_size`: the size for a literal, for a `const`, `[0,0,0]` plus a populated `workgroup_size_error` for an override expression, the memo invalidated by `fromJSON`/`copy`, and the returned array frozen. |
570
+ | **Unit, node** | `ComputeShader` still throws the same message for an unreadable size — a spec that pins the wrapping, since §3.5 rewrites how it gets there. |
571
+ | **Unit, node** | Drift guard: every method name on `SoftwareGPUComputePassEncoder.prototype` / `SoftwareGPURenderPassEncoder.prototype` has a counterpart on the decorator, modulo the §3.2 rename. This catches the decorator falling behind **the mock**, which is what breaks node-tier specs. It is *not* a completeness check against WebGPU — see §8.2. |
572
+ | **`SoftwareGPUDevice`** | `ShadeGPUCommandContextFrame.spec.js` already drives `setViewport`/`draw`/`dispatchWorkgroups`/`end` through the context and asserts on the underlying mock encoder — it passes unchanged if the forwarding is right, which makes it the integration check for free. `GPUFrameRecorder.spec.js` and `GPUProfileSession.spec.js` cover the join end to end. |
573
+ | **Browser playground** | The **only** real completeness check: diff each decorator against the live `GPUComputePassEncoder.prototype` / `GPURenderPassEncoder.prototype` and **throw**, not warn. A console warning in a playground page is a warning nobody reads. |
574
+ | **Measurement** | `shade/playground/volumetrics_froxel/measure.js`, before and after, profiling **off**. |
575
+
576
+ ---
577
+
578
+ ## 7. Cost, honestly
579
+
580
+ **Not profiling** — per pass: one object allocation, one `assert.defined`, one `assert.notOk`; per
581
+ encoder method: one call frame. Asserts in this codebase are **not** stripped in production
582
+ ([core/assert.js](../../core/assert.js) has no build-time gate), so those two are real work on every
583
+ pass, not a debug-build cost.
584
+
585
+ How many passes per frame is **not known** — the 2026-08-28 proposal lists it as an open question
586
+ (§734 there) and `GPUTimerArray`'s 1024-slot default is a ceiling, not a measurement. So the total is
587
+ unquantified by construction and §6's measurement is the answer, not this paragraph.
588
+
589
+ **Profiling** — strictly faster. A `Proxy` `get` trap plus a `Map` lookup on every property access
590
+ becomes a direct method call. Plus one WGSL parse removed per profiled compute pass (§3.5).
591
+
592
+ **Correctness** — the two hidden fields go, and with them any defect that could only manifest while
593
+ profiling.
594
+
595
+ ---
596
+
597
+ ## 8. Adversarial pass
598
+
599
+ Attacking the above. Four findings changed the design; the rest are recorded because they are the
600
+ things most likely to be wrong.
601
+
602
+ ### 8.1 `label` was write-only-broken *(fixed above)*
603
+
604
+ Revision 1 gave the decorators a `label` getter and no setter. `GPUObjectBase.label` is a read-write
605
+ attribute, ES modules are always strict mode, and assigning to an accessor with no setter throws
606
+ `TypeError`. Nothing writes `pass.label` today (checked), so this would have sat dormant until
607
+ someone tried — and the §6 drift guard compares *method* names and would never have caught a missing
608
+ accessor. Fixed: `get`/`set` pair. The general lesson is that the drift guard's scope is narrower
609
+ than it looks, which is §8.2.
610
+
611
+ ### 8.2 The node conformance test was oversold *(claim corrected above)*
612
+
613
+ Revision 1 called the node-tier test "the completeness guarantee … which the Proxy could not do".
614
+ That is wrong twice over. It compares meep's decorator against meep's own hand-written mock, so it
615
+ cannot possibly catch a method Chrome adds — both sides would be missing it. And `toHaveProperty`
616
+ walks the prototype chain, so `Object.prototype` members pass vacuously. It is a **drift guard**
617
+ between two files in this repo, which is worth having and is not the same claim. Only the browser
618
+ check tests completeness, and it has to *throw*. §6 now says so.
619
+
620
+ This weakens — but does not overturn — the §2 argument. The Proxy's real advantage was forwarding
621
+ methods **nobody had heard of yet**; that advantage is real and this proposal gives it up on purpose,
622
+ in exchange for a failure that is loud and immediate instead of silent and capture-only. Worth being
623
+ explicit that it is a trade, not a free win.
624
+
625
+ ### 8.3 Revision 1 reintroduced the bug it was removing *(design changed above)*
626
+
627
+ Revision 1 replaced `#pending_work` with a reused `ShadeGPUPassProfile` scratch object returned from
628
+ `open_compute_pass`, "valid until the next call". That is the same coupling — a mutable value whose
629
+ correctness depends on the caller consuming it before anything else runs — moved one class down and
630
+ given a name. Having just written two pages about why that pattern is bad, shipping it would have
631
+ been indefensible. §3.4 now has the profiler open the pass, so both values are produced and consumed
632
+ inside one method and there is nothing to carry.
633
+
634
+ ### 8.4 The commit sequence was not actually independently green *(fixed above, see §9)*
635
+
636
+ Revision 1 ordered "decorators first, profiler-state consolidation second". But the decorators need
637
+ the work record to come from *somewhere*, so step 1 would have had to build `begin*Pass` against the
638
+ old `#pending_work` channel and then immediately rewrite it. Reordered.
639
+
640
+ ### 8.5 Sharing the workgroup-size array is a real aliasing hazard *(mitigated above)*
641
+
642
+ `work.workgroup_size = descriptor.workgroup_size` hands a per-pass profile record a reference to an
643
+ array owned by a descriptor that outlives the whole capture. Nothing mutates it today —
644
+ `GPUProfileWork` assigns it and never writes through it, unlike `workgroups`, which is mutated in
645
+ place three lines away in the same class. That is close enough to be worth removing as a possibility
646
+ rather than as a bug: §3.5 freezes it.
647
+
648
+ ### 8.6 The memo has two invalidation paths and I nearly missed both *(covered above)*
649
+
650
+ `ComputePipelineDescriptor.fromJSON` mutates `this.compute` in place and `PipelineDescriptorBase` has
651
+ a `copy`. A memoised `workgroup_size` read before either would be silently stale afterwards — and
652
+ stale in the worst way, since the number is used as a *divisor* for thread counts. §3.5 and §6 both
653
+ call it out. This is the single most likely place for this change to introduce a real bug.
654
+
655
+ ### 8.7 Claims I softened rather than defended
656
+
657
+ - **"Perfectly predicted branch."** The `#work !== null` check is uniform within a frame but flips
658
+ when a capture starts or stops. Predictable, not free, and not worth a sentence of advocacy.
659
+ - **"~200 passes/frame, ~1200 calls/frame."** Invented. The number is unknown and is listed as an
660
+ open question in the profiler proposal itself. §7 now says so instead of quoting a fabricated
661
+ figure at two decimal places of confidence.
662
+ - **"Both classes ~120 lines."** Now ~130 with the `label` setter and the split `setBindGroup`.
663
+ Immaterial, but the earlier number was a guess presented as a measurement.
664
+
665
+ ### 8.9 It shipped broken anyway — the arity bug *(found in a browser, after landing)*
666
+
667
+ `setBindGroup(0, bind_group)` forwarded `setBindGroup(0, bind_group, undefined)`, and the renderer
668
+ died on its first bind group with "the provided value cannot be converted to a sequence".
669
+
670
+ The failure is worth recording in full, because every safeguard in this document was pointed at it
671
+ and it got through anyway.
672
+
673
+ **§3.2 named the right method and guarded the wrong end of it.** It identified `setBindGroup` as the
674
+ one forward that could be actively wrong, correctly diagnosed *why* — an overload resolved by
675
+ counting arguments — and then wrote a three-argument forwarder whose whole purpose was to avoid the
676
+ five-argument one. The hazard was never the arity it might reach; it was the `undefined` it would
677
+ hand over on the way. The proposal even asserted the opposite in as many words: "Every other method
678
+ has all-optional trailing parameters with WebIDL defaults, for which forwarding `undefined` is
679
+ correct." That sentence was reasoning about a specification, presented as a fact about a browser,
680
+ and it was load-bearing for seven methods.
681
+
682
+ **Six more had the same defect waiting.** `setImmediates`, `setIndexBuffer`, `setVertexBuffer` and
683
+ the render-side `setBindGroup`. `setVertexBuffer(0, buffer)` — from `prototypeParticleSystem` — was
684
+ next in line, and worse: `size` there has no default to stand in for, it means "the rest of the
685
+ buffer", so an absent argument and an `undefined` one are genuinely different requests.
686
+
687
+ **The node tier could not have caught it.** The mock encoders are plain JS functions and take a
688
+ trailing `undefined` happily, so the mock and the browser disagree in exactly the place the design
689
+ depended on. §6's drift guard compared method *names*, which was never going to help — §8.2 already
690
+ observed the node tier was weaker than it looked, and still put a name-comparison in the plan.
691
+
692
+ **What replaced it:** specs that assert arity preservation against a recording fake, which is
693
+ mock-independent and would have failed on the first run; and the fix verified against real WebGPU
694
+ in a browser rather than against the mock.
695
+
696
+ **Not fully explained.** The in-app browser (Chrome 148) *accepts* the explicit `undefined`; the
697
+ build that crashed does not. So the fix is verified correct on both, but the exact behavioural
698
+ difference was not reproduced, and the code comment says "some builds accept it, others do not"
699
+ rather than claiming a mechanism it cannot demonstrate.
700
+
701
+ **The standing lesson.** The browser-tier check §9 leaves open is not a nice-to-have. Two of the
702
+ three tiers in §6 are blind to the mock/browser boundary by construction, and this change has now
703
+ been wrong at that boundary twice.
704
+
705
+ ### 8.8 Residual risks, accepted
706
+
707
+ - **A WebGPU method Chrome ships that Shade has not modelled** is now a hard failure rather than a
708
+ silent forward. That is the intended trade (§8.2), but it means the browser check is load-bearing
709
+ and needs to actually run in CI or a pre-release checklist, not just exist.
710
+ - **`executeBundles` work is uncounted** (§3.3). Pre-existing, documented, nothing uses bundles.
711
+ - **99 call sites change type.** No reflection over pass objects exists anywhere in the tree
712
+ (`typeof pass.x`, `Object.keys(pass)`, spread, `instanceof` — all checked, all absent), so the
713
+ surface is method calls only. If one is missed it throws at that call; it cannot silently misbehave.
714
+
715
+ ---
716
+
717
+ ## 9. Decisions taken
718
+
719
+ | # | Question | Decision |
720
+ |---|---|---|
721
+ | 1 | Location | **`device/pass/`**, with `ShadeGPUContextProfiler` in `device/timing/` beside the timer array it owns. |
722
+ | 2 | `GPUTimerArray.results_to_console_table` | **Deleted** — "a crude tool, no longer required". Its two specs moved onto `traverse_results`. |
723
+ | 3 | `setBindGroupRange` | **Kept.** The mock models the slice form, and naming it is the whole point of §3.2. |
724
+ | 4 | A `gpu_pass` escape hatch | **Omitted.** Nothing needs one and it invites bypass. |
725
+ | 5 | `end()` double-end assert | **In.** Tracking open passes on the context stays a separate change. |
726
+ | 6 | `workgroup_size_error` | **Rejected — throw instead.** See §3.5. |
727
+
728
+ Still open, and deliberately not done here:
729
+
730
+ - **The browser-tier completeness check** against the live `GPUComputePassEncoder.prototype` /
731
+ `GPURenderPassEncoder.prototype`. It is the only real completeness check (§8.2) and it is now
732
+ load-bearing, since a method Chrome ships that Shade has not modelled throws rather than being
733
+ forwarded blind.
734
+ - **The measurement.** `shade/playground/volumetrics_froxel/measure.js`, profiling off, before and
735
+ after. §7 says why the numbers are not guessed at here.
736
+ - **Two unreferenced modules** found on the way: `GPUVirtualShadowTextureResidence.js` imports a
737
+ default export that does not exist and throws on import; `graph_pass_debug_reprojection.js` is
738
+ dead. Both predate this change and neither was touched.
739
+
740
+ ---
741
+
742
+ ## 10. Commits
743
+
744
+ 1. `refactor(shade): pipeline descriptors carry their workgroup size` — §3.5.
745
+ 2. `refactor(shade): pass encoders become explicit decorator classes` — §§3.1–3.4 and 3.6, plus the
746
+ BVH verification tool moved onto a `GPUFrameRecorder`.
747
+ 3. `refactor(shade): remove the debug-timer path, the profiler is the only one` — §4.
748
+
749
+ Revision 2 ordered these decorators-first; that was wrong (§8.4) and the workgroup size went first
750
+ instead. Full suite green after each: 15,012 tests.