@woosh/meep-engine 3.11.2 → 3.12.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 (551) hide show
  1. package/README.md +14 -18
  2. package/build/bundle-worker-terrain.js +1 -1
  3. package/editor/SelectionVisualizer.js +127 -127
  4. package/editor/entity_world_bounds.js +27 -27
  5. package/editor/particles/rebuildParticleEmitter.js +31 -31
  6. package/editor/process/SymbolicDisplayProcess.js +1 -1
  7. package/editor/process/symbolic/CameraSymbolicDisplay.js +146 -146
  8. package/editor/process/symbolic/LightSymbolicDisplay.js +172 -172
  9. package/editor/prototypeEditorShell.js +156 -156
  10. package/editor/selection/editor_pick.js +130 -130
  11. package/editor/selection/screen_rect_frustum.js +119 -119
  12. package/editor/tools/v2/TransformControls.js +971 -971
  13. package/package.json +1 -1
  14. package/src/engine/Engine.d.ts +3 -3
  15. package/src/engine/Engine.d.ts.map +1 -1
  16. package/src/engine/Engine.js +726 -726
  17. package/src/engine/EngineHarness.d.ts +1 -1
  18. package/src/engine/EngineHarness.js +590 -590
  19. package/src/engine/ecs/HIERARCHY_REVIEW.md +717 -717
  20. package/src/engine/ecs/terrain/ecs/BuildLightTexture.d.ts +1 -1
  21. package/src/engine/ecs/terrain/ecs/BuildLightTexture.js +27 -27
  22. package/src/engine/ecs/terrain/ecs/Terrain.d.ts +1 -5
  23. package/src/engine/ecs/terrain/ecs/Terrain.d.ts.map +1 -1
  24. package/src/engine/ecs/terrain/ecs/Terrain.js +790 -790
  25. package/src/engine/ecs/terrain/util/loadVisibleTerrainTiles.js +129 -129
  26. package/src/engine/graphics/ecs/camera/camera_find_active.d.ts +1 -1
  27. package/src/engine/graphics/ecs/camera/camera_find_active.js +28 -28
  28. package/src/engine/graphics/ecs/mesh-v2/ShadedGeometry.d.ts +1 -1
  29. package/src/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +428 -428
  30. package/src/engine/graphics/ecs/water/Water.d.ts +1 -1
  31. package/src/engine/graphics/ecs/water/Water.js +104 -104
  32. package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.d.ts +1 -1
  33. package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.js +1 -1
  34. package/src/engine/graphics3/AnimationGraphSystem.d.ts +65 -0
  35. package/src/engine/graphics3/AnimationGraphSystem.d.ts.map +1 -0
  36. package/src/engine/graphics3/AnimationGraphSystem.js +329 -0
  37. package/src/engine/graphics3/AnimationGraphSystem3.d.ts +6 -61
  38. package/src/engine/graphics3/AnimationGraphSystem3.d.ts.map +1 -1
  39. package/src/engine/graphics3/AnimationGraphSystem3.js +6 -325
  40. package/src/engine/graphics3/AnimationSystem.d.ts +76 -0
  41. package/src/engine/graphics3/AnimationSystem.d.ts.map +1 -0
  42. package/src/engine/graphics3/AnimationSystem.js +352 -0
  43. package/src/engine/graphics3/AnimationSystem3.d.ts +6 -72
  44. package/src/engine/graphics3/AnimationSystem3.d.ts.map +1 -1
  45. package/src/engine/graphics3/AnimationSystem3.js +6 -348
  46. package/src/engine/graphics3/CameraSystem.d.ts +49 -0
  47. package/src/engine/graphics3/CameraSystem.d.ts.map +1 -0
  48. package/src/engine/graphics3/CameraSystem.js +170 -0
  49. package/src/engine/graphics3/CameraSystem3.d.ts +6 -45
  50. package/src/engine/graphics3/CameraSystem3.d.ts.map +1 -1
  51. package/src/engine/graphics3/CameraSystem3.js +6 -166
  52. package/src/engine/graphics3/DebugDrawSystem.d.ts +72 -0
  53. package/src/engine/graphics3/DebugDrawSystem.d.ts.map +1 -0
  54. package/src/engine/graphics3/DebugDrawSystem.js +205 -0
  55. package/src/engine/graphics3/DebugDrawSystem3.d.ts +6 -68
  56. package/src/engine/graphics3/DebugDrawSystem3.d.ts.map +1 -1
  57. package/src/engine/graphics3/DebugDrawSystem3.js +6 -201
  58. package/src/engine/graphics3/DecalSystem.d.ts +81 -0
  59. package/src/engine/graphics3/DecalSystem.d.ts.map +1 -0
  60. package/src/engine/graphics3/DecalSystem.js +441 -0
  61. package/src/engine/graphics3/DecalSystem3.d.ts +6 -77
  62. package/src/engine/graphics3/DecalSystem3.d.ts.map +1 -1
  63. package/src/engine/graphics3/DecalSystem3.js +6 -437
  64. package/src/engine/graphics3/FogOfWarSystem.d.ts +74 -0
  65. package/src/engine/graphics3/FogOfWarSystem.d.ts.map +1 -0
  66. package/src/engine/graphics3/FogOfWarSystem.js +265 -0
  67. package/src/engine/graphics3/FogOfWarSystem3.d.ts +6 -70
  68. package/src/engine/graphics3/FogOfWarSystem3.d.ts.map +1 -1
  69. package/src/engine/graphics3/FogOfWarSystem3.js +6 -261
  70. package/src/engine/graphics3/GraphicsEngine.d.ts +340 -0
  71. package/src/engine/graphics3/GraphicsEngine.d.ts.map +1 -0
  72. package/src/engine/graphics3/GraphicsEngine.js +790 -0
  73. package/src/engine/graphics3/GraphicsEngine3.d.ts +6 -336
  74. package/src/engine/graphics3/GraphicsEngine3.d.ts.map +1 -1
  75. package/src/engine/graphics3/GraphicsEngine3.js +6 -786
  76. package/src/engine/graphics3/HighlightSystem.d.ts +82 -0
  77. package/src/engine/graphics3/HighlightSystem.d.ts.map +1 -0
  78. package/src/engine/graphics3/HighlightSystem.js +241 -0
  79. package/src/engine/graphics3/HighlightSystem3.d.ts +6 -78
  80. package/src/engine/graphics3/HighlightSystem3.d.ts.map +1 -1
  81. package/src/engine/graphics3/HighlightSystem3.js +6 -237
  82. package/src/engine/graphics3/LightSystem.d.ts +40 -0
  83. package/src/engine/graphics3/LightSystem.d.ts.map +1 -0
  84. package/src/engine/graphics3/LightSystem.js +211 -0
  85. package/src/engine/graphics3/LightSystem3.d.ts +6 -36
  86. package/src/engine/graphics3/LightSystem3.d.ts.map +1 -1
  87. package/src/engine/graphics3/LightSystem3.js +6 -207
  88. package/src/engine/graphics3/MeshSystem.d.ts +106 -0
  89. package/src/engine/graphics3/MeshSystem.d.ts.map +1 -0
  90. package/src/engine/graphics3/MeshSystem.js +647 -0
  91. package/src/engine/graphics3/MeshSystem3.d.ts +6 -102
  92. package/src/engine/graphics3/MeshSystem3.d.ts.map +1 -1
  93. package/src/engine/graphics3/MeshSystem3.js +6 -642
  94. package/src/engine/graphics3/ParticipatingMedia.d.ts +2 -2
  95. package/src/engine/graphics3/ParticipatingMedia.js +203 -203
  96. package/src/engine/graphics3/ParticipatingMediaSystem.d.ts +87 -0
  97. package/src/engine/graphics3/ParticipatingMediaSystem.d.ts.map +1 -0
  98. package/src/engine/graphics3/ParticipatingMediaSystem.js +184 -0
  99. package/src/engine/graphics3/ParticipatingMediaSystem3.d.ts +6 -83
  100. package/src/engine/graphics3/ParticipatingMediaSystem3.d.ts.map +1 -1
  101. package/src/engine/graphics3/ParticipatingMediaSystem3.js +6 -180
  102. package/src/engine/graphics3/ParticleEmitterSystem.d.ts +76 -0
  103. package/src/engine/graphics3/ParticleEmitterSystem.d.ts.map +1 -0
  104. package/src/engine/graphics3/ParticleEmitterSystem.js +289 -0
  105. package/src/engine/graphics3/ParticleEmitterSystem3.d.ts +6 -72
  106. package/src/engine/graphics3/ParticleEmitterSystem3.d.ts.map +1 -1
  107. package/src/engine/graphics3/ParticleEmitterSystem3.js +6 -285
  108. package/src/engine/graphics3/PathDisplaySystem.d.ts +70 -0
  109. package/src/engine/graphics3/PathDisplaySystem.d.ts.map +1 -0
  110. package/src/engine/graphics3/PathDisplaySystem.js +309 -0
  111. package/src/engine/graphics3/PathDisplaySystem3.d.ts +6 -66
  112. package/src/engine/graphics3/PathDisplaySystem3.d.ts.map +1 -1
  113. package/src/engine/graphics3/PathDisplaySystem3.js +6 -305
  114. package/src/engine/graphics3/Pickable.d.ts +1 -1
  115. package/src/engine/graphics3/Pickable.js +65 -65
  116. package/src/engine/graphics3/PickingQuery.d.ts +1 -1
  117. package/src/engine/graphics3/PickingQuery.js +48 -48
  118. package/src/engine/graphics3/PickingSystem.d.ts +67 -0
  119. package/src/engine/graphics3/PickingSystem.d.ts.map +1 -0
  120. package/src/engine/graphics3/PickingSystem.js +273 -0
  121. package/src/engine/graphics3/PickingSystem3.d.ts +6 -63
  122. package/src/engine/graphics3/PickingSystem3.d.ts.map +1 -1
  123. package/src/engine/graphics3/PickingSystem3.js +6 -269
  124. package/src/engine/graphics3/ShadedGeometrySystem.d.ts +47 -0
  125. package/src/engine/graphics3/ShadedGeometrySystem.d.ts.map +1 -0
  126. package/src/engine/graphics3/ShadedGeometrySystem.js +169 -0
  127. package/src/engine/graphics3/ShadedGeometrySystem3.d.ts +6 -43
  128. package/src/engine/graphics3/ShadedGeometrySystem3.d.ts.map +1 -1
  129. package/src/engine/graphics3/ShadedGeometrySystem3.js +6 -165
  130. package/src/engine/graphics3/TerrainSystem.d.ts +80 -0
  131. package/src/engine/graphics3/TerrainSystem.d.ts.map +1 -0
  132. package/src/engine/graphics3/TerrainSystem.js +566 -0
  133. package/src/engine/graphics3/TerrainSystem3.d.ts +6 -76
  134. package/src/engine/graphics3/TerrainSystem3.d.ts.map +1 -1
  135. package/src/engine/graphics3/TerrainSystem3.js +6 -562
  136. package/src/engine/graphics3/TooltipComponentSystem.d.ts +65 -0
  137. package/src/engine/graphics3/TooltipComponentSystem.d.ts.map +1 -0
  138. package/src/engine/graphics3/TooltipComponentSystem.js +328 -0
  139. package/src/engine/graphics3/TooltipComponentSystem3.d.ts +6 -61
  140. package/src/engine/graphics3/TooltipComponentSystem3.d.ts.map +1 -1
  141. package/src/engine/graphics3/TooltipComponentSystem3.js +6 -324
  142. package/src/engine/graphics3/Trail3DSystem.d.ts +73 -0
  143. package/src/engine/graphics3/Trail3DSystem.d.ts.map +1 -0
  144. package/src/engine/graphics3/Trail3DSystem.js +221 -0
  145. package/src/engine/graphics3/Trail3DSystem3.d.ts +6 -69
  146. package/src/engine/graphics3/Trail3DSystem3.d.ts.map +1 -1
  147. package/src/engine/graphics3/Trail3DSystem3.js +6 -217
  148. package/src/engine/graphics3/VolumetricLightMap.d.ts +2 -2
  149. package/src/engine/graphics3/VolumetricLightMap.js +2 -2
  150. package/src/engine/graphics3/VolumetricLightMapSystem.d.ts +98 -0
  151. package/src/engine/graphics3/VolumetricLightMapSystem.d.ts.map +1 -0
  152. package/src/engine/graphics3/VolumetricLightMapSystem.js +261 -0
  153. package/src/engine/graphics3/VolumetricLightMapSystem3.d.ts +6 -94
  154. package/src/engine/graphics3/VolumetricLightMapSystem3.d.ts.map +1 -1
  155. package/src/engine/graphics3/VolumetricLightMapSystem3.js +10 -261
  156. package/src/engine/graphics3/WaterSystem.d.ts +59 -0
  157. package/src/engine/graphics3/WaterSystem.d.ts.map +1 -0
  158. package/src/engine/graphics3/WaterSystem.js +293 -0
  159. package/src/engine/graphics3/WaterSystem3.d.ts +6 -55
  160. package/src/engine/graphics3/WaterSystem3.d.ts.map +1 -1
  161. package/src/engine/graphics3/WaterSystem3.js +6 -289
  162. package/src/engine/graphics3/animation/AnimationGraphController.d.ts +1 -1
  163. package/src/engine/graphics3/animation/AnimationGraphController.js +518 -518
  164. package/src/engine/graphics3/animation/ClipListPlayer.d.ts +1 -1
  165. package/src/engine/graphics3/animation/ClipListPlayer.js +263 -263
  166. package/src/engine/graphics3/animation/ClipPlayback.d.ts +2 -2
  167. package/src/engine/graphics3/animation/ClipPlayback.js +84 -84
  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/highlight/pack_highlight_table.d.ts +2 -2
  177. package/src/engine/graphics3/highlight/pack_highlight_table.d.ts.map +1 -1
  178. package/src/engine/graphics3/highlight/pack_highlight_table.js +74 -74
  179. package/src/engine/graphics3/pose/query_entity_node_world_pose.d.ts +1 -1
  180. package/src/engine/graphics3/pose/query_entity_node_world_pose.js +101 -101
  181. package/src/engine/graphics3/preview/make_model_thumbnail.d.ts +3 -3
  182. package/src/engine/graphics3/preview/make_model_thumbnail.d.ts.map +1 -1
  183. package/src/engine/graphics3/preview/make_model_thumbnail.js +48 -48
  184. package/src/engine/graphics3/shade_camera_projection_ray.d.ts +1 -1
  185. package/src/engine/graphics3/shade_camera_projection_ray.js +77 -77
  186. package/src/engine/graphics3/shade_node_to_entity_composition.d.ts +1 -1
  187. package/src/engine/graphics3/shade_node_to_entity_composition.js +84 -84
  188. package/src/shade/descriptor/pipeline/PipelineLayoutDescriptor.d.ts +16 -2
  189. package/src/shade/descriptor/pipeline/PipelineLayoutDescriptor.d.ts.map +1 -1
  190. package/src/shade/descriptor/pipeline/PipelineLayoutDescriptor.js +28 -4
  191. package/src/shade/descriptor/pipeline/compute/ComputePipelineDescriptor.d.ts +1 -1
  192. package/src/shade/descriptor/pipeline/compute/ComputePipelineDescriptor.d.ts.map +1 -1
  193. package/src/shade/descriptor/pipeline/compute/ComputePipelineDescriptor.js +5 -2
  194. package/src/shade/device/IMMEDIATE_DATA_PLAN_2026_08_28.md +564 -451
  195. package/src/shade/device/ShadeGPUCommandContext.d.ts +18 -2
  196. package/src/shade/device/ShadeGPUCommandContext.d.ts.map +1 -1
  197. package/src/shade/device/ShadeGPUCommandContext.js +994 -940
  198. package/src/shade/device/immediate_data_supported.d.ts +33 -0
  199. package/src/shade/device/immediate_data_supported.d.ts.map +1 -0
  200. package/src/shade/device/immediate_data_supported.js +53 -0
  201. package/src/shade/device/mock/SoftwareGPUCommandEncoder.d.ts +18 -0
  202. package/src/shade/device/mock/SoftwareGPUCommandEncoder.d.ts.map +1 -1
  203. package/src/shade/device/mock/SoftwareGPUCommandEncoder.js +24 -0
  204. package/src/shade/device/mock/SoftwareGPUComputePassEncoder.d.ts +7 -0
  205. package/src/shade/device/mock/SoftwareGPUComputePassEncoder.d.ts.map +1 -1
  206. package/src/shade/device/mock/SoftwareGPUComputePassEncoder.js +49 -1
  207. package/src/shade/device/mock/SoftwareGPUDevice.d.ts.map +1 -1
  208. package/src/shade/device/mock/SoftwareGPUDevice.js +20 -2
  209. package/src/shade/device/mock/SoftwareGPUImmediateData.d.ts +65 -0
  210. package/src/shade/device/mock/SoftwareGPUImmediateData.d.ts.map +1 -0
  211. package/src/shade/device/mock/SoftwareGPUImmediateData.js +160 -0
  212. package/src/shade/device/mock/SoftwareGPURenderPassEncoder.d.ts +7 -0
  213. package/src/shade/device/mock/SoftwareGPURenderPassEncoder.d.ts.map +1 -1
  214. package/src/shade/device/mock/SoftwareGPURenderPassEncoder.js +50 -0
  215. package/src/shade/device/mock/pipeline_immediate_size.d.ts +11 -0
  216. package/src/shade/device/mock/pipeline_immediate_size.d.ts.map +1 -0
  217. package/src/shade/device/mock/pipeline_immediate_size.js +18 -0
  218. package/src/shade/device/pipeline/PipelineLayoutManager.d.ts.map +1 -1
  219. package/src/shade/device/pipeline/PipelineLayoutManager.js +1 -0
  220. package/src/shade/playground/skinned_blas_refit/shader_blas_audit_leaf_triangles.d.ts.map +1 -1
  221. package/src/shade/playground/skinned_blas_refit/shader_blas_audit_leaf_triangles.js +0 -1
  222. package/src/shade/playground/skinned_blas_refit/shader_blas_audit_points.d.ts.map +1 -1
  223. package/src/shade/playground/skinned_blas_refit/shader_blas_audit_points.js +0 -1
  224. package/src/shade/playground/skinned_blas_refit/shader_blas_audit_visibility.d.ts.map +1 -1
  225. package/src/shade/playground/skinned_blas_refit/shader_blas_audit_visibility.js +133 -134
  226. package/src/shade/playground/skinned_blas_refit/verify_leaf_triangles.js +202 -202
  227. package/src/shade/playground/skinned_blas_refit/verify_traversal_reachability.js +249 -249
  228. package/src/shade/renderer/GraphicsContext.d.ts +9 -2
  229. package/src/shade/renderer/GraphicsContext.d.ts.map +1 -1
  230. package/src/shade/renderer/GraphicsContext.js +13 -1
  231. package/src/shade/renderer/STATIC_GRAPHICS_ENGINE_ASSETS.js +1 -1
  232. package/src/shade/renderer/animation/pose/PosePlayback.d.ts +2 -1
  233. package/src/shade/renderer/animation/pose/PosePlayback.d.ts.map +1 -1
  234. package/src/shade/renderer/animation/pose/PosePlayback.js +2 -1
  235. package/src/shade/renderer/animation/shader_animation_apply.d.ts.map +1 -1
  236. package/src/shade/renderer/animation/shader_animation_apply.js +128 -129
  237. package/src/shade/renderer/animation/shader_animation_pose_resolve.d.ts.map +1 -1
  238. package/src/shade/renderer/animation/shader_animation_pose_resolve.js +176 -177
  239. package/src/shade/renderer/animation/shader_animation_tick.d.ts.map +1 -1
  240. package/src/shade/renderer/animation/shader_animation_tick.js +125 -126
  241. package/src/shade/renderer/animation/skinning/bounds_refresh/shader_geometry_sphere_derive.d.ts.map +1 -1
  242. package/src/shade/renderer/animation/skinning/bounds_refresh/shader_geometry_sphere_derive.js +98 -99
  243. package/src/shade/renderer/animation/skinning/bounds_refresh/shader_meshlet_bounds_reduce.d.ts.map +1 -1
  244. package/src/shade/renderer/animation/skinning/bounds_refresh/shader_meshlet_bounds_reduce.js +212 -213
  245. package/src/shade/renderer/animation/skinning/bounds_refresh/shader_meshlet_bounds_scatter.d.ts.map +1 -1
  246. package/src/shade/renderer/animation/skinning/bounds_refresh/shader_meshlet_bounds_scatter.js +120 -121
  247. package/src/shade/renderer/animation/skinning/bounds_refresh/shader_skinned_mesh_bounds_refresh.d.ts.map +1 -1
  248. package/src/shade/renderer/animation/skinning/bounds_refresh/shader_skinned_mesh_bounds_refresh.js +75 -76
  249. package/src/shade/renderer/animation/skinning/shader_meshlet_apply_skinning.d.ts.map +1 -1
  250. package/src/shade/renderer/animation/skinning/shader_meshlet_apply_skinning.js +371 -372
  251. package/src/shade/renderer/animation/skinning/shader_skin_matrix_prep.d.ts.map +1 -1
  252. package/src/shade/renderer/animation/skinning/shader_skin_matrix_prep.js +104 -105
  253. package/src/shade/renderer/atmosphere/GPUSky.d.ts.map +1 -1
  254. package/src/shade/renderer/atmosphere/GPUSky.js +145 -142
  255. package/src/shade/renderer/atmosphere/MULTISCATTER_LUT_CONFIG.d.ts +34 -0
  256. package/src/shade/renderer/atmosphere/MULTISCATTER_LUT_CONFIG.d.ts.map +1 -0
  257. package/src/shade/renderer/atmosphere/MULTISCATTER_LUT_CONFIG.js +36 -0
  258. package/src/shade/renderer/atmosphere/SAMPLER_SKY_LUT.d.ts +20 -0
  259. package/src/shade/renderer/atmosphere/SAMPLER_SKY_LUT.d.ts.map +1 -0
  260. package/src/shade/renderer/atmosphere/SAMPLER_SKY_LUT.js +25 -0
  261. package/src/shade/renderer/atmosphere/chunk_get_multiscatter_values.d.ts +14 -1
  262. package/src/shade/renderer/atmosphere/chunk_get_multiscatter_values.d.ts.map +1 -1
  263. package/src/shade/renderer/atmosphere/chunk_get_multiscatter_values.js +163 -177
  264. package/src/shade/renderer/atmosphere/chunk_multiscatter_sample_direction.d.ts +20 -0
  265. package/src/shade/renderer/atmosphere/chunk_multiscatter_sample_direction.d.ts.map +1 -0
  266. package/src/shade/renderer/atmosphere/chunk_multiscatter_sample_direction.js +46 -0
  267. package/src/shade/renderer/atmosphere/chunk_sample_multiscatter_lut.d.ts +11 -1
  268. package/src/shade/renderer/atmosphere/chunk_sample_multiscatter_lut.d.ts.map +1 -1
  269. package/src/shade/renderer/atmosphere/chunk_sample_multiscatter_lut.js +57 -27
  270. package/src/shade/renderer/atmosphere/chunk_sample_transmittance_lut.d.ts +19 -1
  271. package/src/shade/renderer/atmosphere/chunk_sample_transmittance_lut.d.ts.map +1 -1
  272. package/src/shade/renderer/atmosphere/chunk_sample_transmittance_lut.js +69 -33
  273. package/src/shade/renderer/atmosphere/chunk_sky_lut_compute_uv.d.ts +13 -0
  274. package/src/shade/renderer/atmosphere/chunk_sky_lut_compute_uv.d.ts.map +1 -1
  275. package/src/shade/renderer/atmosphere/chunk_sky_lut_compute_uv.js +35 -35
  276. package/src/shade/renderer/atmosphere/chunk_sky_lut_params.d.ts +13 -0
  277. package/src/shade/renderer/atmosphere/chunk_sky_lut_params.d.ts.map +1 -0
  278. package/src/shade/renderer/atmosphere/chunk_sky_lut_params.js +38 -0
  279. package/src/shade/renderer/atmosphere/chunk_sky_lut_texel_to_unit.d.ts +17 -0
  280. package/src/shade/renderer/atmosphere/chunk_sky_lut_texel_to_unit.d.ts.map +1 -0
  281. package/src/shade/renderer/atmosphere/chunk_sky_lut_texel_to_unit.js +29 -0
  282. package/src/shade/renderer/atmosphere/chunk_sky_lut_uv.d.ts +21 -0
  283. package/src/shade/renderer/atmosphere/chunk_sky_lut_uv.d.ts.map +1 -0
  284. package/src/shade/renderer/atmosphere/chunk_sky_lut_uv.js +42 -0
  285. package/src/shade/renderer/atmosphere/shader_multiscatter_lut.d.ts +2 -2
  286. package/src/shade/renderer/atmosphere/shader_multiscatter_lut.d.ts.map +1 -1
  287. package/src/shade/renderer/atmosphere/shader_multiscatter_lut.js +164 -84
  288. package/src/shade/renderer/atmosphere/shader_sky_irradiance_lut.d.ts.map +1 -1
  289. package/src/shade/renderer/atmosphere/shader_sky_irradiance_lut.js +215 -204
  290. package/src/shade/renderer/atmosphere/shader_transmittance_lut.d.ts.map +1 -1
  291. package/src/shade/renderer/atmosphere/shader_transmittance_lut.js +79 -67
  292. package/src/shade/renderer/buffer/graph_inspect_gpu_buffer.js +136 -136
  293. package/src/shade/renderer/buffer/table/GPUDatabase.d.ts.map +1 -1
  294. package/src/shade/renderer/buffer/table/GPUDatabase.js +2 -3
  295. package/src/shade/renderer/buffer/table/single/GPUSingleTypeTable.js +319 -319
  296. package/src/shade/renderer/buffer/util/shader_buffer_copy_storage_to_storage.d.ts.map +1 -1
  297. package/src/shade/renderer/buffer/util/shader_buffer_copy_storage_to_storage.js +41 -42
  298. package/src/shade/renderer/buffer/util/shader_buffer_intra_copy_regions.d.ts.map +1 -1
  299. package/src/shade/renderer/buffer/util/shader_buffer_intra_copy_regions.js +83 -84
  300. package/src/shade/renderer/dynamic/shader_dynamic_mesh.d.ts.map +1 -1
  301. package/src/shade/renderer/dynamic/shader_dynamic_mesh.js +177 -170
  302. package/src/shade/renderer/extension/RENDER_EXTENSION_DESIGN.md +918 -918
  303. package/src/shade/renderer/geometry/bvh/record_blas_refit.js +4 -4
  304. package/src/shade/renderer/geometry/bvh/shader_blas_refit_leaf_bounds.d.ts.map +1 -1
  305. package/src/shade/renderer/geometry/bvh/shader_blas_refit_leaf_bounds.js +121 -122
  306. package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.js +774 -774
  307. package/src/shade/renderer/geometry/meshlet/shader_meshlet_metadata_address_patch.d.ts +1 -1
  308. package/src/shade/renderer/geometry/meshlet/shader_meshlet_metadata_address_patch.d.ts.map +1 -1
  309. package/src/shade/renderer/geometry/meshlet/shader_meshlet_metadata_address_patch.js +69 -70
  310. package/src/shade/renderer/global_illumination/lpv/placement/graph_generate_probe_locations.js +367 -367
  311. package/src/shade/renderer/global_illumination/probe/octahedral/convolve/graph_lvp_atlas_to_sh.js +184 -184
  312. package/src/shade/renderer/gpu_primitive/bvh/GPU_BVH_BUILD_PLAN.md +1115 -1115
  313. package/src/shade/renderer/gpu_primitive/bvh/graph_bvh_build_lbvh.js +217 -217
  314. package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_emit_hierarchy.d.ts.map +1 -1
  315. package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_emit_hierarchy.js +142 -143
  316. package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_morton.d.ts.map +1 -1
  317. package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_morton.js +0 -1
  318. package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_refit.d.ts.map +1 -1
  319. package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_refit.js +140 -141
  320. package/src/shade/renderer/gpu_primitive/sort/shader_radix_sort_histogram.d.ts.map +1 -1
  321. package/src/shade/renderer/gpu_primitive/sort/shader_radix_sort_histogram.js +0 -1
  322. package/src/shade/renderer/gpu_primitive/sort/shader_radix_sort_scatter.d.ts.map +1 -1
  323. package/src/shade/renderer/gpu_primitive/sort/shader_radix_sort_scatter.js +0 -1
  324. package/src/shade/renderer/light/cluster/cull/graph_cull_lights.d.ts.map +1 -1
  325. package/src/shade/renderer/light/cluster/cull/graph_cull_lights.js +191 -198
  326. package/src/shade/renderer/light/cluster/cull/shader_cull_lights_frustum.d.ts +0 -6
  327. package/src/shade/renderer/light/cluster/cull/shader_cull_lights_frustum.d.ts.map +1 -1
  328. package/src/shade/renderer/light/cluster/cull/shader_cull_lights_frustum.js +120 -120
  329. package/src/shade/renderer/light/cluster/cull/shader_cull_lights_hzb.d.ts +0 -4
  330. package/src/shade/renderer/light/cluster/cull/shader_cull_lights_hzb.d.ts.map +1 -1
  331. package/src/shade/renderer/light/cluster/cull/shader_cull_lights_hzb.js +112 -112
  332. package/src/shade/renderer/light/cluster/graph_assign_clsuters.d.ts.map +1 -1
  333. package/src/shade/renderer/light/cluster/graph_assign_clsuters.js +10 -9
  334. package/src/shade/renderer/light/cluster/shader_cluster_assign_lights.d.ts +0 -6
  335. package/src/shade/renderer/light/cluster/shader_cluster_assign_lights.d.ts.map +1 -1
  336. package/src/shade/renderer/light/cluster/shader_cluster_assign_lights.js +1 -1
  337. package/src/shade/renderer/lightmap/bake/raster/shader_lightmap_to_viz_buffer.js +222 -222
  338. package/src/shade/renderer/lightmap/bake/tile/shader_lightmap_tile_composit.js +81 -81
  339. package/src/shade/renderer/material/GPUMaterialContext.js +380 -380
  340. package/src/shade/renderer/material/standard/fragment_gbuffer_vt.js +169 -169
  341. package/src/shade/renderer/object_property/shader_object_property_process.d.ts.map +1 -1
  342. package/src/shade/renderer/object_property/shader_object_property_process.js +83 -84
  343. package/src/shade/renderer/object_property/shader_refresh_all_mesh_bounds.d.ts.map +1 -1
  344. package/src/shade/renderer/object_property/shader_refresh_all_mesh_bounds.js +57 -58
  345. package/src/shade/renderer/particles/prototypeParticleSystem.js +2 -3
  346. package/src/shade/renderer/particles/shaders/shader_particle_emit.d.ts.map +1 -1
  347. package/src/shade/renderer/particles/shaders/shader_particle_emit.js +117 -118
  348. package/src/shade/renderer/particles/shaders/shader_particle_finalize.d.ts.map +1 -1
  349. package/src/shade/renderer/particles/shaders/shader_particle_finalize.js +77 -79
  350. package/src/shade/renderer/particles/shaders/shader_particle_render.d.ts.map +1 -1
  351. package/src/shade/renderer/particles/shaders/shader_particle_render.js +190 -183
  352. package/src/shade/renderer/particles/shaders/shader_particle_simulate.d.ts.map +1 -1
  353. package/src/shade/renderer/particles/shaders/shader_particle_simulate.js +99 -100
  354. package/src/shade/renderer/particles/sort/shader_particle_sort.d.ts.map +1 -1
  355. package/src/shade/renderer/particles/sort/shader_particle_sort.js +158 -161
  356. package/src/shade/renderer/path_tracer/graph_path_trace_scene.js +184 -184
  357. package/src/shade/renderer/postprocess/denoise/graph_atorus_denoise_luma.js +119 -119
  358. package/src/shade/renderer/postprocess/dof/raymarch/graph_postprocess_dof.js +3 -4
  359. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_raymarch_compute.d.ts.map +1 -1
  360. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_raymarch_compute.js +266 -267
  361. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_slice_compute.d.ts.map +1 -1
  362. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_slice_compute.js +270 -271
  363. package/src/shade/renderer/postprocess/gtao/GTAO.d.ts.map +1 -1
  364. package/src/shade/renderer/postprocess/gtao/GTAO.js +4 -0
  365. package/src/shade/renderer/postprocess/nss/shader_nss_concat.d.ts.map +1 -1
  366. package/src/shade/renderer/postprocess/nss/shader_nss_concat.js +68 -69
  367. package/src/shade/renderer/postprocess/nss/shader_nss_conv_layer.d.ts.map +1 -1
  368. package/src/shade/renderer/postprocess/nss/shader_nss_conv_layer.js +225 -226
  369. package/src/shade/renderer/postprocess/nss/shader_nss_extract_feedback.d.ts.map +1 -1
  370. package/src/shade/renderer/postprocess/nss/shader_nss_extract_feedback.js +49 -50
  371. package/src/shade/renderer/postprocess/nss/shader_nss_postprocess.d.ts.map +1 -1
  372. package/src/shade/renderer/postprocess/nss/shader_nss_postprocess.js +291 -292
  373. package/src/shade/renderer/postprocess/nss/shader_nss_preprocess.d.ts.map +1 -1
  374. package/src/shade/renderer/postprocess/nss/shader_nss_preprocess.js +230 -231
  375. package/src/shade/renderer/postprocess/ssr/reproject/shader_ffx_denoiser_reflections_reproject.d.ts +16 -2
  376. package/src/shade/renderer/postprocess/ssr/reproject/shader_ffx_denoiser_reflections_reproject.d.ts.map +1 -1
  377. package/src/shade/renderer/postprocess/ssr/reproject/shader_ffx_denoiser_reflections_reproject.js +12 -12
  378. package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.d.ts.map +1 -1
  379. package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.js +341 -345
  380. package/src/shade/renderer/rasterize/bucket/shader_count_meshlets_by_material.d.ts.map +1 -1
  381. package/src/shade/renderer/rasterize/bucket/shader_count_meshlets_by_material.js +68 -69
  382. package/src/shade/renderer/rasterize/bucket/shader_make_meshlet_indirect_draw_commands_by_material.d.ts.map +1 -1
  383. package/src/shade/renderer/rasterize/bucket/shader_make_meshlet_indirect_draw_commands_by_material.js +59 -60
  384. package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.d.ts +0 -2
  385. package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.d.ts.map +1 -1
  386. package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.js +57 -57
  387. package/src/shade/renderer/rasterize/bucket/shader_sort_meshlets_by_material.d.ts.map +1 -1
  388. package/src/shade/renderer/rasterize/bucket/shader_sort_meshlets_by_material.js +64 -65
  389. package/src/shade/renderer/rasterize/compute/graph_mesh_frustum_cull.d.ts.map +1 -1
  390. package/src/shade/renderer/rasterize/compute/graph_mesh_frustum_cull.js +168 -169
  391. package/src/shade/renderer/rasterize/compute/graph_mesh_sphere_cull.d.ts.map +1 -1
  392. package/src/shade/renderer/rasterize/compute/graph_mesh_sphere_cull.js +160 -161
  393. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_mesh_filter_2way.d.ts.map +1 -1
  394. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_mesh_filter_2way.js +109 -110
  395. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_meshlet_filter_1way_full.d.ts.map +1 -1
  396. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_meshlet_filter_1way_full.js +115 -116
  397. package/src/shade/renderer/rasterize/expand/bucket/mesh/shader_instances_classify_bucket.d.ts.map +1 -1
  398. package/src/shade/renderer/rasterize/expand/bucket/mesh/shader_instances_classify_bucket.js +139 -140
  399. package/src/shade/renderer/rasterize/expand/bucket/mesh/shader_instances_count_by_rasterization_bucket.d.ts.map +1 -1
  400. package/src/shade/renderer/rasterize/expand/bucket/mesh/shader_instances_count_by_rasterization_bucket.js +91 -92
  401. package/src/shade/renderer/rasterize/expand/bucket/mesh/shader_instances_rasterization_bucket_sort.d.ts.map +1 -1
  402. package/src/shade/renderer/rasterize/expand/bucket/mesh/shader_instances_rasterization_bucket_sort.js +88 -89
  403. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_count_by_rasterization_bucket.d.ts.map +1 -1
  404. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_count_by_rasterization_bucket.js +90 -91
  405. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_extract_bucket.d.ts.map +1 -1
  406. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_extract_bucket.js +65 -66
  407. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_rasterization_bucket_sort.d.ts.map +1 -1
  408. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_rasterization_bucket_sort.js +89 -90
  409. package/src/shade/renderer/rasterize/expand/graph_mesh_filter_one_way.d.ts.map +1 -1
  410. package/src/shade/renderer/rasterize/expand/graph_mesh_filter_one_way.js +169 -170
  411. package/src/shade/renderer/rasterize/expand/mesh/graph_expand_meshes_to_meshlets.d.ts.map +1 -1
  412. package/src/shade/renderer/rasterize/expand/mesh/graph_expand_meshes_to_meshlets.js +7 -3
  413. package/src/shade/renderer/rasterize/expand/mesh/shader_mesh_expand_to_meshlets_prefix.d.ts.map +1 -1
  414. package/src/shade/renderer/rasterize/expand/mesh/shader_mesh_expand_to_meshlets_prefix.js +89 -90
  415. package/src/shade/renderer/rasterize/expand/mesh/shader_meshes_to_meshlet_counts.d.ts.map +1 -1
  416. package/src/shade/renderer/rasterize/expand/mesh/shader_meshes_to_meshlet_counts.js +61 -62
  417. package/src/shade/renderer/rasterize/expand/pass1/filter_meshlets.2-way.compute.d.ts.map +1 -1
  418. package/src/shade/renderer/rasterize/expand/pass1/filter_meshlets.2-way.compute.js +153 -154
  419. package/src/shade/renderer/rasterize/fast/graph_extract_meshes_from_bucket.d.ts.map +1 -1
  420. package/src/shade/renderer/rasterize/fast/graph_extract_meshes_from_bucket.js +77 -81
  421. package/src/shade/renderer/rasterize/native/depth/shadow_rasterization_alpha_tested_pass_descriptor.js +144 -144
  422. package/src/shade/renderer/rasterize/native/depth/shadow_rasterization_pass_descriptor.js +88 -88
  423. package/src/shade/renderer/rasterize/native/oit/shader_oit_generate_moments.js +217 -217
  424. package/src/shade/renderer/rasterize/native/oit/shader_oit_resolve_moments.js +179 -179
  425. package/src/shade/renderer/rasterize/native/oit/shader_oit_resolve_moments_ibl.js +178 -178
  426. package/src/shade/renderer/rasterize/native/viz/viz_rasterization_alpha_tested_pass_descriptor.js +194 -194
  427. package/src/shade/renderer/rasterize/native/viz/viz_rasterization_opaque_pass_descriptor.js +76 -76
  428. package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.d.ts.map +1 -1
  429. package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.js +5 -1
  430. package/src/shade/renderer/restir/di/shader_restir_di_denoise.d.ts.map +1 -1
  431. package/src/shade/renderer/restir/di/shader_restir_di_denoise.js +136 -137
  432. package/src/shade/renderer/restir/di/shader_restir_di_resample.d.ts.map +1 -1
  433. package/src/shade/renderer/restir/di/shader_restir_di_resample.js +129 -130
  434. package/src/shade/renderer/restir/di/shader_restir_di_resolve.d.ts.map +1 -1
  435. package/src/shade/renderer/restir/di/shader_restir_di_resolve.js +99 -100
  436. package/src/shade/renderer/restir/di/shader_restir_di_spatial.d.ts.map +1 -1
  437. package/src/shade/renderer/restir/di/shader_restir_di_spatial.js +116 -117
  438. package/src/shade/renderer/scene/GPUInstancesAccelerationStructure.js +383 -383
  439. package/src/shade/renderer/scene/hierarchy/graph_scene_update_transform_hierarchy_naive.js +123 -123
  440. package/src/shade/renderer/scene/hierarchy/shader_nodes_record_input_positions.d.ts.map +1 -1
  441. package/src/shade/renderer/scene/hierarchy/shader_nodes_record_input_positions.js +64 -65
  442. package/src/shade/renderer/scene/hierarchy/shader_nodes_update_transform_hierarchy.d.ts.map +1 -1
  443. package/src/shade/renderer/scene/hierarchy/shader_nodes_update_transform_hierarchy.js +372 -373
  444. package/src/shade/renderer/scene/hierarchy/shader_nodes_update_transform_hierarchy_naive.d.ts.map +1 -1
  445. package/src/shade/renderer/scene/hierarchy/shader_nodes_update_transform_hierarchy_naive.js +111 -112
  446. package/src/shade/renderer/scene/shader_bvh_extract_instance_leaves.d.ts.map +1 -1
  447. package/src/shade/renderer/scene/shader_bvh_extract_instance_leaves.js +0 -1
  448. package/src/shade/renderer/shader/ComputeShader.d.ts +24 -3
  449. package/src/shade/renderer/shader/ComputeShader.d.ts.map +1 -1
  450. package/src/shade/renderer/shader/ComputeShader.js +350 -292
  451. package/src/shade/renderer/shader/ImageShader.d.ts.map +1 -1
  452. package/src/shade/renderer/shader/ImageShader.js +217 -214
  453. package/src/shade/renderer/shader/ShaderDescriptor.d.ts +5 -0
  454. package/src/shade/renderer/shader/ShaderDescriptor.d.ts.map +1 -1
  455. package/src/shade/renderer/shader/ShaderDescriptor.js +46 -0
  456. package/src/shade/renderer/shader/chunk/atmosphere/ATMOSPHERE_RADII_DECLARATION.d.ts +18 -0
  457. package/src/shade/renderer/shader/chunk/atmosphere/ATMOSPHERE_RADII_DECLARATION.d.ts.map +1 -0
  458. package/src/shade/renderer/shader/chunk/atmosphere/ATMOSPHERE_RADII_DECLARATION.js +32 -0
  459. package/src/shade/renderer/shader/chunk/atmosphere/PLANET_CENTER_DECLARATION.d.ts +7 -0
  460. package/src/shade/renderer/shader/chunk/atmosphere/PLANET_CENTER_DECLARATION.d.ts.map +1 -1
  461. package/src/shade/renderer/shader/chunk/atmosphere/PLANET_CENTER_DECLARATION.js +17 -6
  462. package/src/shade/renderer/shader/chunk/atmosphere/chunk_atmosphere_ray_bounds.d.ts +21 -0
  463. package/src/shade/renderer/shader/chunk/atmosphere/chunk_atmosphere_ray_bounds.d.ts.map +1 -0
  464. package/src/shade/renderer/shader/chunk/atmosphere/chunk_atmosphere_ray_bounds.js +69 -0
  465. package/src/shade/renderer/shader/chunk/geometry/aabb3/chunk_aabb3_contains_aabb3.d.ts +9 -0
  466. package/src/shade/renderer/shader/chunk/geometry/aabb3/chunk_aabb3_contains_aabb3.d.ts.map +1 -0
  467. package/src/shade/renderer/shader/chunk/geometry/aabb3/chunk_aabb3_contains_aabb3.js +22 -0
  468. package/src/shade/renderer/shader/chunk/geometry/aabb3/chunk_aabb3_from_transformed_corners.d.ts +16 -0
  469. package/src/shade/renderer/shader/chunk/geometry/aabb3/chunk_aabb3_from_transformed_corners.d.ts.map +1 -0
  470. package/src/shade/renderer/shader/chunk/geometry/aabb3/chunk_aabb3_from_transformed_corners.js +41 -0
  471. package/src/shade/renderer/shader/chunk/geometry/frustum/chunk_frustum_transform_m4.d.ts +11 -0
  472. package/src/shade/renderer/shader/chunk/geometry/frustum/chunk_frustum_transform_m4.d.ts.map +1 -1
  473. package/src/shade/renderer/shader/chunk/geometry/frustum/chunk_frustum_transform_m4.js +15 -19
  474. package/src/shade/renderer/shader/chunk/geometry/frustum/chunk_frustum_transform_plane_matrix.d.ts +23 -0
  475. package/src/shade/renderer/shader/chunk/geometry/frustum/chunk_frustum_transform_plane_matrix.d.ts.map +1 -0
  476. package/src/shade/renderer/shader/chunk/geometry/frustum/chunk_frustum_transform_plane_matrix.js +44 -0
  477. package/src/shade/renderer/shader/chunk/geometry/octahedron/chunk_uv_octahedral_unit_decode.d.ts.map +1 -1
  478. package/src/shade/renderer/shader/chunk/geometry/octahedron/chunk_uv_octahedral_unit_decode.js +1 -4
  479. package/src/shade/renderer/shader/compiler/CodeChunk.d.ts +7 -1
  480. package/src/shade/renderer/shader/compiler/CodeChunk.d.ts.map +1 -1
  481. package/src/shade/renderer/shader/compiler/CodeChunk.js +37 -2
  482. package/src/shade/renderer/shader/graph/compute/compute_binding_read_names.d.ts.map +1 -1
  483. package/src/shade/renderer/shader/graph/compute/compute_binding_read_names.js +6 -0
  484. package/src/shade/renderer/shader/graph/compute/graph_compute_indirect_pass.js +1 -1
  485. package/src/shade/renderer/shader/graph/compute/graph_compute_pass.js +1 -1
  486. package/src/shade/renderer/shader/graph/graph_image_pass.js +1 -1
  487. package/src/shade/renderer/shader/graph/graph_prepare_bindings.d.ts.map +1 -1
  488. package/src/shade/renderer/shader/graph/graph_prepare_bindings.js +7 -0
  489. package/src/shade/renderer/shader/graph/graph_prepare_pass_input_data.d.ts +3 -1
  490. package/src/shade/renderer/shader/graph/graph_prepare_pass_input_data.d.ts.map +1 -1
  491. package/src/shade/renderer/shader/graph/graph_prepare_pass_input_data.js +12 -1
  492. package/src/shade/renderer/shader/pass/RenderPassDescriptor.d.ts.map +1 -1
  493. package/src/shade/renderer/shader/pass/RenderPassDescriptor.js +1 -0
  494. package/src/shade/renderer/shader/resource/IMMEDIATE_DATA.d.ts +21 -0
  495. package/src/shade/renderer/shader/resource/IMMEDIATE_DATA.d.ts.map +1 -0
  496. package/src/shade/renderer/shader/resource/IMMEDIATE_DATA.js +21 -0
  497. package/src/shade/renderer/shader/resource/ShaderResourceSetDescriptor.d.ts +59 -1
  498. package/src/shade/renderer/shader/resource/ShaderResourceSetDescriptor.d.ts.map +1 -1
  499. package/src/shade/renderer/shader/resource/ShaderResourceSetDescriptor.js +195 -2
  500. package/src/shade/renderer/shader/type/type_has_arrays.d.ts +14 -0
  501. package/src/shade/renderer/shader/type/type_has_arrays.d.ts.map +1 -0
  502. package/src/shade/renderer/shader/type/type_has_arrays.js +33 -0
  503. package/src/shade/renderer/shader/util/compute_infer_workgroup_size_from_source.d.ts +47 -0
  504. package/src/shade/renderer/shader/util/compute_infer_workgroup_size_from_source.d.ts.map +1 -0
  505. package/src/shade/renderer/shader/util/compute_infer_workgroup_size_from_source.js +448 -0
  506. package/src/shade/renderer/shadow/map/csm/graph_csm_setup_cascades.js +3 -3
  507. package/src/shade/renderer/shadow/ray/staged/gen/graph_gen_rays.js +3 -3
  508. package/src/shade/renderer/shadow/ray/staged/trace/graph_trace_rays.js +2 -2
  509. package/src/shade/renderer/shadow/sdf/cascade/CascadedSceneSDF.js +4 -4
  510. package/src/shade/renderer/texture/virtual/build_shader_vt_feedback.d.ts.map +1 -1
  511. package/src/shade/renderer/texture/virtual/build_shader_vt_feedback.js +231 -232
  512. package/src/shade/renderer/view/GPUViewSkyContext.d.ts.map +1 -1
  513. package/src/shade/renderer/view/GPUViewSkyContext.js +169 -173
  514. package/src/shade/renderer/volumetrics/GPUViewVolumetrics.js +1 -1
  515. package/src/shade/renderer/volumetrics/NOTES.md +238 -0
  516. package/src/shade/renderer/volumetrics/chunk_volumetrics_local_light_shadow.js +145 -145
  517. package/src/shade/renderer/volumetrics/chunk_volumetrics_sun_optical_depth.d.ts.map +1 -1
  518. package/src/shade/renderer/volumetrics/chunk_volumetrics_sun_optical_depth.js +38 -15
  519. package/src/shade/renderer/volumetrics/graph_build_volumetrics.d.ts +14 -2
  520. package/src/shade/renderer/volumetrics/graph_build_volumetrics.d.ts.map +1 -1
  521. package/src/shade/renderer/volumetrics/graph_build_volumetrics.js +591 -577
  522. package/src/shade/renderer/volumetrics/shader_volumetrics_build_lighting.d.ts +17 -0
  523. package/src/shade/renderer/volumetrics/shader_volumetrics_build_lighting.d.ts.map +1 -1
  524. package/src/shade/renderer/volumetrics/shader_volumetrics_build_lighting.js +20 -2
  525. package/src/shade/renderer/volumetrics/shader_volumetrics_build_participating_media.d.ts.map +1 -1
  526. package/src/shade/renderer/volumetrics/shader_volumetrics_build_participating_media.js +29 -18
  527. package/src/shade/renderer/volumetrics/shader_volumetrics_build_scattering_lut.js +260 -260
  528. package/src/shade/renderer/volumetrics/shader_volumetrics_build_sun_shadow_volume.d.ts +53 -3
  529. package/src/shade/renderer/volumetrics/shader_volumetrics_build_sun_shadow_volume.d.ts.map +1 -1
  530. package/src/shade/renderer/volumetrics/shader_volumetrics_build_sun_shadow_volume.js +175 -59
  531. package/src/shade/renderer/volumetrics/taa/shader_volumetrics_taa.d.ts +0 -2
  532. package/src/shade/renderer/volumetrics/taa/shader_volumetrics_taa.d.ts.map +1 -1
  533. package/src/shade/renderer/volumetrics/taa/shader_volumetrics_taa.js +17 -6
  534. package/src/shade/renderer/water/shader_water.js +246 -246
  535. package/src/shade/vitest.setup.mjs +1 -1
  536. package/src/shade/wgsl/emulator/ComputeShaderEmulator.js +291 -291
  537. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +8 -1
  538. package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
  539. package/src/shade/wgsl/emulator/WGSLLanguageCore.js +1325 -1297
  540. package/src/view/minimap/Minimap.d.ts +2 -2
  541. package/src/view/minimap/Minimap.d.ts.map +1 -1
  542. package/src/view/minimap/Minimap.js +225 -225
  543. package/src/view/minimap/dom/MinimapCameraView.d.ts +3 -3
  544. package/src/view/minimap/dom/MinimapCameraView.d.ts.map +1 -1
  545. package/src/view/minimap/dom/MinimapCameraView.js +206 -206
  546. package/src/shade/renderer/shadow/map/csm/shader/shader_setup_cascades.d.ts +0 -3
  547. package/src/shade/renderer/shadow/map/csm/shader/shader_setup_cascades.d.ts.map +0 -1
  548. package/src/shade/renderer/shadow/map/csm/shader/shader_setup_cascades.js +0 -20
  549. package/src/shade/renderer/volumetrics/VOLUMETRICS_WORKGROUP_RESOLUTION.d.ts +0 -2
  550. package/src/shade/renderer/volumetrics/VOLUMETRICS_WORKGROUP_RESOLUTION.d.ts.map +0 -1
  551. package/src/shade/renderer/volumetrics/VOLUMETRICS_WORKGROUP_RESOLUTION.js +0 -1
@@ -1,775 +1,775 @@
1
- import { assert } from "../../../../core/assert.js";
2
- import { ALLOCATOR_NO_SPACE, OffsetAllocator } from "../../../../core/binary/allocator/OffsetAllocator.js";
3
- import { BinaryBuffer } from "../../../../core/binary/BinaryBuffer.js";
4
- import { platform_compute_endianness } from "../../../../core/binary/platform_compute_endianness.js";
5
- import Signal from "../../../../core/events/signal/Signal.js";
6
- import { MEGABYTE } from "../../../../core/science/units/memory/MEGABYTE.js";
7
- import { read_wgsl_type_value } from "../../shader/type/serde/read_gpu_typed_buffer.js";
8
- import { write_wgsl_type_value } from "../../shader/type/serde/write_gpu_typed_buffer.js";
9
- import { u32 } from "../../shader/type/WEBGPU_WGSL_PRIMITIVE_TYPES.js";
10
- import { inspect_gpu_buffer_typed } from "../../buffer/inspect_gpu_buffer_typed.js";
11
- import {
12
- BUFFER_INTRA_COPY_REGION_STRUCT,
13
- shader_buffer_intra_copy_regions
14
- } from "../../buffer/util/shader_buffer_intra_copy_regions.js";
15
- import { MESHLET_METADATA_STRUCT } from "./encoding/MESHLET_METADATA_STRUCT.js";
16
- import { shader_meshlet_metadata_address_patch } from "./shader_meshlet_metadata_address_patch.js";
17
-
18
- /**
19
- * In meshlets
20
- * @type {number}
21
- */
22
- const DEFAULT_METADATA_CAPACITY = 128 * 1024;
23
-
24
- /**
25
- * In bytes
26
- * @type {number}
27
- */
28
- const DEFAULT_DATA_CAPACITY = 32 * MEGABYTE;
29
-
30
- class MeshletBatchAllocationRecord {
31
- /**
32
- * @type {Allocation}
33
- */
34
- metadata
35
- /**
36
- * @type {Allocation}
37
- */
38
- data
39
-
40
- /**
41
- * Number of words
42
- * @type {number}
43
- */
44
- data_size = 0;
45
-
46
- /**
47
- * Meshlet count
48
- * @type {number}
49
- */
50
- meshlet_count = 0;
51
-
52
- /**
53
- *
54
- * @type {ArrayBuffer}
55
- */
56
- metadata_buffer_original;
57
-
58
- /**
59
- *
60
- * @type {Signal<>}
61
- */
62
- changed = new Signal()
63
- }
64
-
65
- export class GPUMeshletManager {
66
-
67
- /**
68
- * Allocation sizes in words of 4 bytes
69
- * @type {OffsetAllocator}
70
- */
71
- #allocator_data = new OffsetAllocator(1024);
72
-
73
- /**
74
- * Allocation sizes in meshlets
75
- * @type {OffsetAllocator}
76
- */
77
- #allocator_metadata = new OffsetAllocator(1024);
78
-
79
- /**
80
- * @type {GPUBuffer}
81
- */
82
- #meshlet_data_buffer;
83
-
84
- get buffer_data() {
85
- return this.#meshlet_data_buffer;
86
- }
87
-
88
- /**
89
- * @type {GPUBuffer}
90
- */
91
- #meshlet_metadata_buffer;
92
-
93
- get buffer_metadata() {
94
- return this.#meshlet_metadata_buffer;
95
- }
96
-
97
- /**
98
- * @type {GPUDevice}
99
- */
100
- #device;
101
-
102
- /**
103
- *
104
- * @type {number}
105
- */
106
- #deallocations_since_last_rebuild = 0;
107
-
108
- /**
109
- *
110
- * @type {MeshletBatchAllocationRecord[]}
111
- */
112
- #records = [];
113
-
114
- /**
115
- * Queue of GPU-side clones that have been allocated but not yet
116
- * copied + patched. Drained by {@link update}. Each entry holds the
117
- * minimum bookkeeping needed to issue two `copyBufferToBuffer`
118
- * commands and N `(record_index, address_delta)` patch entries.
119
- *
120
- * Entries name the two records, not the addresses they held when the
121
- * clone was queued: a {@link compact} or {@link grow} in between
122
- * migrates both, and {@link update} reads where they ended up. What
123
- * an entry does require is that neither record is handed back to
124
- * {@link remove_batch} before the flush.
125
- *
126
- * @type {{
127
- * source: MeshletBatchAllocationRecord,
128
- * clone: MeshletBatchAllocationRecord,
129
- * }[]}
130
- */
131
- #pending_clones = [];
132
-
133
- /**
134
- *
135
- * @param {GPUDevice} device
136
- * @param {number} [initial_data_capacity] in bytes
137
- * @param {number} [initial_metadata_capacity] in meshlets
138
- */
139
- constructor({
140
- device,
141
- initial_data_capacity = DEFAULT_DATA_CAPACITY,
142
- initial_metadata_capacity = DEFAULT_METADATA_CAPACITY,
143
- }) {
144
- assert.defined(device, 'device');
145
-
146
- assert.isNonNegativeInteger(initial_data_capacity, 'initial_data_capacity');
147
- assert.isNonNegativeInteger(initial_metadata_capacity, 'initial_metadata_capacity');
148
-
149
- assert.greaterThan(initial_data_capacity, 0, 'initial_data_capacity');
150
- assert.greaterThan(initial_metadata_capacity, 0, 'initial_data_capacity');
151
-
152
- this.#device = device;
153
-
154
- const data_buffer_size_words = Math.ceil(initial_data_capacity / 4);
155
-
156
- // TODO manage max-allocations as well
157
- this.#allocator_data = new OffsetAllocator(data_buffer_size_words);
158
- this.#allocator_metadata = new OffsetAllocator(initial_metadata_capacity);
159
-
160
- this.#meshlet_data_buffer = device.createBuffer({
161
- label: 'GPUMeshletManager/data',
162
- size: data_buffer_size_words * 4,
163
- usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC
164
- });
165
-
166
- this.#meshlet_metadata_buffer = device.createBuffer({
167
- label: 'GPUMeshletManager/metadata',
168
- size: initial_metadata_capacity * MESHLET_METADATA_STRUCT.aligned_size,
169
- usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC
170
- });
171
-
172
- }
173
-
174
- compact() {
175
- this.#rebuild(
176
- this.#meshlet_data_buffer.size >>> 2,
177
- Math.ceil(this.#meshlet_metadata_buffer.size / MESHLET_METADATA_STRUCT.aligned_size)
178
- );
179
- }
180
-
181
- /**
182
- *
183
- * @param {number} extra_data_size in words (4 bytes)
184
- * @param {number} extra_metadata_size in metadata elements
185
- */
186
- grow(extra_data_size, extra_metadata_size) {
187
- assert.isNonNegativeInteger(extra_data_size, 'extra_data_size');
188
- assert.isNonNegativeInteger(extra_metadata_size, 'extra_metadata_size');
189
-
190
- if (extra_data_size === 0 && extra_metadata_size === 0) {
191
- // nothing to do
192
- return;
193
- }
194
-
195
- const current_data_buffer_size_bytes = this.#meshlet_data_buffer.size;
196
- const current_metadata_buffer_size = this.#meshlet_metadata_buffer.size;
197
-
198
- const current_buffer_size_words = current_data_buffer_size_bytes >> 2;
199
- const current_metadata_buffer_size_elements = Math.ceil(current_metadata_buffer_size / MESHLET_METADATA_STRUCT.aligned_size);
200
-
201
- const DATA_GROWTH_STEP = 16 * 1024;
202
- const METADATA_GROWTH_STEP = 256;
203
- const GROWTH_MULTIPLIER = 1.25;
204
-
205
- let new_data_size;
206
- if (extra_data_size === 0) {
207
- new_data_size = current_buffer_size_words;
208
- } else {
209
- // grow
210
- new_data_size = Math.max(
211
- current_buffer_size_words + extra_data_size,
212
- current_buffer_size_words + DATA_GROWTH_STEP,
213
- Math.ceil(current_buffer_size_words * GROWTH_MULTIPLIER)
214
- );
215
- }
216
-
217
- let new_metadata_size;
218
- if (extra_metadata_size === 0) {
219
- new_metadata_size = current_metadata_buffer_size_elements;
220
- } else {
221
- // grow
222
- new_metadata_size = Math.max(
223
- current_metadata_buffer_size_elements + extra_metadata_size,
224
- current_metadata_buffer_size_elements + METADATA_GROWTH_STEP,
225
- current_metadata_buffer_size_elements * GROWTH_MULTIPLIER
226
- );
227
- }
228
-
229
- this.#rebuild(
230
- new_data_size,
231
- new_metadata_size
232
- );
233
- }
234
-
235
- /**
236
- * Helper to patch pointers in metadata and upload to GPU
237
- * @param {ArrayBuffer} source_array_buffer
238
- * @param {number} meshlet_count
239
- * @param {number} data_offset_words
240
- * @param {number} metadata_offset_index
241
- * @param {GPUBuffer} target_buffer
242
- */
243
- #write_patched_metadata(
244
- source_array_buffer,
245
- meshlet_count,
246
- data_offset_words,
247
- metadata_offset_index,
248
- target_buffer
249
- ) {
250
- const temp_writer = BinaryBuffer.fromEndianness(platform_compute_endianness());
251
- temp_writer.setCapacity(meshlet_count * MESHLET_METADATA_STRUCT.size); // pre-allocate to avoid resizing
252
-
253
- const source_reader = BinaryBuffer.fromArrayBuffer(source_array_buffer);
254
- source_reader.endianness = platform_compute_endianness();
255
-
256
- // Iterate over meshlets in the batch, patch the address, and write to temp buffer
257
- for (let i = 0; i < meshlet_count; i++) {
258
- const header = read_wgsl_type_value(source_reader, MESHLET_METADATA_STRUCT);
259
-
260
- // PATCH: Add the allocation offset to the relative address
261
- header.address += data_offset_words;
262
-
263
- write_wgsl_type_value(header, temp_writer, MESHLET_METADATA_STRUCT);
264
- }
265
-
266
- // Upload
267
- this.#device.queue.writeBuffer(
268
- target_buffer,
269
- metadata_offset_index * MESHLET_METADATA_STRUCT.aligned_size,
270
- temp_writer.data,
271
- 0,
272
- meshlet_count * MESHLET_METADATA_STRUCT.aligned_size
273
- );
274
- }
275
-
276
- /**
277
- *
278
- * @param {number} new_data_size
279
- * @param {number} new_metadata_size
280
- */
281
- #rebuild(
282
- new_data_size,
283
- new_metadata_size
284
- ) {
285
-
286
- assert.isNonNegativeInteger(new_data_size, 'new_data_size');
287
- assert.isNonNegativeInteger(new_metadata_size, 'new_metadata_size');
288
-
289
- // Outstanding clone work survives this. A queued clone names its
290
- // source and destination records, both of which are migrated
291
- // below, and the flush reads their addresses when it runs — so a
292
- // rebuild moves the copy rather than invalidating it.
293
-
294
- // console.warn('GPUMeshletManager/rebuild');
295
-
296
- const device = this.#device;
297
-
298
- const records = this.#records;
299
- const record_count = records.length;
300
- const new_allocator_data = new OffsetAllocator(
301
- new_data_size,
302
- Math.max(record_count * 2, 128 * 1024)
303
- );
304
-
305
- const old_buffer_data = this.#meshlet_data_buffer;
306
- const new_buffer_data = device.createBuffer({
307
- label: old_buffer_data.label,
308
- usage: old_buffer_data.usage,
309
- size: new_data_size * 4,
310
- });
311
-
312
- const new_allocator_metadata = new OffsetAllocator(
313
- new_metadata_size,
314
- Math.max(record_count * 2, 128 * 1024)
315
- );
316
-
317
- const old_buffer_metadata = this.#meshlet_metadata_buffer;
318
- const new_buffer_metadata = device.createBuffer({
319
- label: old_buffer_metadata.label,
320
- usage: old_buffer_metadata.usage,
321
- size: new_metadata_size * MESHLET_METADATA_STRUCT.aligned_size,
322
- });
323
-
324
- const encoder = device.createCommandEncoder({
325
- label: "GPUMeshletManager/rebuild",
326
- });
327
-
328
- const moved_records = [];
329
-
330
- // move data
331
- for (let i = 0; i < record_count; i++) {
332
- const record = records[i];
333
-
334
- const new_allocation_data = new_allocator_data.allocate(record.data_size);
335
- const new_allocation_metadata = new_allocator_metadata.allocate(record.meshlet_count);
336
-
337
- assert.notEqual(new_allocation_data.offset, ALLOCATOR_NO_SPACE);
338
- assert.notEqual(new_allocation_metadata.offset, ALLOCATOR_NO_SPACE);
339
-
340
- encoder.copyBufferToBuffer(
341
- old_buffer_data, record.data.offset * 4,
342
- new_buffer_data, new_allocation_data.offset * 4,
343
- record.data_size * 4
344
- );
345
-
346
- const metadata_record_moved = record.data.offset !== new_allocation_data.offset;
347
-
348
- // B. Patch Metadata (CPU -> GPU Write)
349
- // Metadata contains `address` which points to the Data offset.
350
- // Since the Data offset changed, we must re-calculate and upload the metadata.
351
- // We reuse the helper to avoid code duplication.
352
- this.#write_patched_metadata(
353
- record.metadata_buffer_original,
354
- record.meshlet_count,
355
- new_allocation_data.offset, // The NEW data offset
356
- new_allocation_metadata.offset, // The NEW metadata location
357
- new_buffer_metadata
358
- );
359
-
360
- // update record
361
- record.data = new_allocation_data;
362
- record.metadata = new_allocation_metadata;
363
-
364
- if (metadata_record_moved) {
365
- moved_records.push(record);
366
- }
367
- }
368
-
369
- for (const record of moved_records) {
370
- // notify
371
- record.changed.send0();
372
- }
373
-
374
- const command_buffer = encoder.finish();
375
-
376
- device.queue.submit([command_buffer]);
377
-
378
- old_buffer_data.destroy();
379
- old_buffer_metadata.destroy();
380
-
381
- this.#meshlet_data_buffer = new_buffer_data;
382
- this.#meshlet_metadata_buffer = new_buffer_metadata;
383
-
384
- this.#allocator_data = new_allocator_data;
385
- this.#allocator_metadata = new_allocator_metadata;
386
-
387
- // reset count
388
- this.#deallocations_since_last_rebuild = 0;
389
- }
390
-
391
- /**
392
- * Allocate `data_size_words` in the data buffer and `meshlet_count`
393
- * in the metadata buffer, retrying with compact-then-grow on failure.
394
- * Throws if the third attempt still can't satisfy both. The two
395
- * allocations are kept in lockstep — partial successes are released
396
- * before retrying so we never leak.
397
- *
398
- * Compaction triggers a full {@link #rebuild} which migrates every
399
- * existing allocation to a new buffer pair. Queued clone work is
400
- * carried through it — {@link #pending_clones} names records, not
401
- * addresses — so an allocation made while a clone is pending is safe.
402
- *
403
- * @param {number} data_size_words
404
- * @param {number} meshlet_count
405
- * @returns {{data: Allocation, metadata: Allocation}}
406
- */
407
- #allocate_data_and_metadata(data_size_words, meshlet_count) {
408
- let data_allocation;
409
- let metadata_allocation;
410
-
411
- for (let try_index = 0; try_index < 3; try_index++) {
412
-
413
- data_allocation = this.#allocator_data.allocate(data_size_words);
414
- metadata_allocation = this.#allocator_metadata.allocate(meshlet_count);
415
-
416
- const data_ok = data_allocation.offset !== ALLOCATOR_NO_SPACE;
417
- const metadata_ok = metadata_allocation.offset !== ALLOCATOR_NO_SPACE;
418
-
419
- if (data_ok && metadata_ok) {
420
- break;
421
- }
422
-
423
- // release partial allocation
424
- if (data_ok) {
425
- this.#allocator_data.free(data_allocation);
426
-
427
- data_allocation.offset = ALLOCATOR_NO_SPACE;
428
- data_allocation.metadata = ALLOCATOR_NO_SPACE;
429
- }
430
- if (metadata_ok) {
431
- this.#allocator_metadata.free(metadata_allocation);
432
-
433
- metadata_allocation.offset = ALLOCATOR_NO_SPACE;
434
- metadata_allocation.metadata = ALLOCATOR_NO_SPACE;
435
- }
436
-
437
- if (this.#deallocations_since_last_rebuild > 0) {
438
- // try compaction
439
- this.compact();
440
- continue;
441
- }
442
-
443
- let grow_data_size = data_ok ? 0 : data_size_words;
444
- let grow_metadata_size = metadata_ok ? 0 : meshlet_count;
445
-
446
- this.grow(grow_data_size, grow_metadata_size);
447
-
448
- }
449
-
450
- if (data_allocation.offset === ALLOCATOR_NO_SPACE || metadata_allocation.offset === ALLOCATOR_NO_SPACE) {
451
- throw new Error('Failed to allocate GPU memory for meshlet batch');
452
- }
453
-
454
- return { data: data_allocation, metadata: metadata_allocation };
455
- }
456
-
457
- /**
458
- *
459
- * @param {MeshletBatch} batch
460
- * @returns {MeshletBatchAllocationRecord}
461
- */
462
- #allocate_for_batch(batch) {
463
- assert.defined(batch, 'batch');
464
-
465
- const data_buffer = batch.data_buffer;
466
-
467
- const data_size_words = Math.ceil(data_buffer.byteLength / 4);
468
-
469
- const meshlet_count = batch.count;
470
-
471
- const { data, metadata } = this.#allocate_data_and_metadata(data_size_words, meshlet_count);
472
-
473
- const record = new MeshletBatchAllocationRecord();
474
-
475
- record.data = data;
476
- record.metadata = metadata;
477
-
478
- record.meshlet_count = meshlet_count;
479
- record.data_size = data_size_words;
480
-
481
- record.metadata_buffer_original = batch.metadata_buffer;
482
-
483
- this.#records.push(record);
484
-
485
- return record;
486
- }
487
-
488
- /**
489
- * Release a batch allocation back to the data and metadata allocators. The GPU bytes are
490
- * not cleared — the freed slices are reclaimed lazily by the next {@link compact} (a
491
- * deallocation bumps the compaction counter the allocator consults under pressure).
492
- * Other batches' allocations are unaffected (the OffsetAllocator frees disjoint slices),
493
- * so callers only need to ensure nothing still references this batch's metadata rows
494
- * (the geometry table rebuild in {@link GPUGeometryManager} handles that).
495
- *
496
- * @param {MeshletBatchAllocationRecord} record a record returned by {@link add_batch} or
497
- * {@link clone_batch_allocation}
498
- * @returns {boolean} true if the record was owned and freed
499
- */
500
- remove_batch(record) {
501
- assert.defined(record, 'record');
502
-
503
- const index = this.#records.indexOf(record);
504
- if (index === -1) {
505
- return false;
506
- }
507
-
508
- this.#allocator_data.free(record.data);
509
- this.#allocator_metadata.free(record.metadata);
510
-
511
- this.#records.splice(index, 1);
512
- this.#deallocations_since_last_rebuild++;
513
-
514
- return true;
515
- }
516
-
517
- /**
518
- * Allocate a fresh (data, metadata) pair sized to mirror `source`,
519
- * and queue a GPU-side clone: the source's data and metadata are
520
- * copied into the new ranges via `copyBufferToBuffer`, then a
521
- * compute pass patches the cloned metadata records' `address`
522
- * fields to point at the new data range. Both the copies and the
523
- * patch dispatch land when the host next calls
524
- * {@link update update(cmd_ctx)}.
525
- *
526
- * The returned record can be wired into the geometry table
527
- * immediately — its `data.offset` and `metadata.offset` are valid
528
- * the moment this call returns. The GPU bytes at those addresses
529
- * just aren't populated yet; downstream passes that consume the
530
- * clone must run after the next `update`. Those addresses may still
531
- * move before the flush, which is why nothing outside the record is
532
- * allowed to remember them.
533
- *
534
- * Sharing the source's `metadata_buffer_original` lets a future
535
- * compaction patch the clone's metadata from the same un-patched
536
- * CPU bytes the source uses — the post-patch state on the GPU is
537
- * identical to the source aside from the data offset.
538
- *
539
- * @param {MeshletBatchAllocationRecord} source
540
- * @returns {MeshletBatchAllocationRecord}
541
- */
542
- clone_batch_allocation(source) {
543
- assert.defined(source, 'source');
544
- assert.greaterThan(source.meshlet_count, 0, 'source.meshlet_count');
545
-
546
- const { data, metadata } = this.#allocate_data_and_metadata(source.data_size, source.meshlet_count);
547
-
548
- const record = new MeshletBatchAllocationRecord();
549
-
550
- record.data = data;
551
- record.metadata = metadata;
552
-
553
- record.meshlet_count = source.meshlet_count;
554
- record.data_size = source.data_size;
555
-
556
- // Share the un-patched CPU bytes so a future compact rewrites
557
- // the clone's metadata correctly under its new data offset.
558
- record.metadata_buffer_original = source.metadata_buffer_original;
559
-
560
- this.#records.push(record);
561
-
562
- // The queue names the two records rather than the addresses they
563
- // hold right now. Both are in `#records`, so a rebuild between
564
- // this call and the flush moves them and the flush still reads
565
- // where they went — which is what lets a clone be queued in the
566
- // same frame as the growth another geometry's arrival forces.
567
- this.#pending_clones.push({ source, clone: record });
568
-
569
- return record;
570
- }
571
-
572
- /**
573
- * Flush any pending clone work into `cmd_ctx`. For each queued
574
- * clone, emits two `copyBufferToBuffer` commands (one for vertex
575
- * data, one for metadata records). After every queued clone's
576
- * copies are recorded, dispatches a single batched patch shader
577
- * pass that walks a flat `(record_index, address_delta)` table
578
- * — N clones → 1 patch dispatch, total threads = sum of clone
579
- * meshlet counts.
580
- *
581
- * Idempotent and cheap when nothing's queued.
582
- *
583
- * @param {ShadeGPUCommandContext} cmd_ctx
584
- */
585
- update(cmd_ctx) {
586
- const pending = this.#pending_clones;
587
- if (pending.length === 0) {
588
- return;
589
- }
590
-
591
- const data_buffer = this.#meshlet_data_buffer;
592
- const metadata_buffer = this.#meshlet_metadata_buffer;
593
- const metadata_stride = MESHLET_METADATA_STRUCT.aligned_size;
594
- const metadata_stride_words = metadata_stride / 4;
595
-
596
- // WebGPU forbids `copyBufferToBuffer` with src === dst even when
597
- // ranges don't overlap (the validation check is buffer-identity,
598
- // not range-overlap), so we relocate via a small compute-shader
599
- // memcpy bound to the buffer once as `read_write`. Two dispatches
600
- // — one over the data-buffer regions, one over the metadata-buffer
601
- // regions — handle every queued clone in a batched pass.
602
- //
603
- // Region (src, dst) ranges are non-overlapping by construction:
604
- // the OffsetAllocator hands out fresh disjoint slices, so the
605
- // memcpy shader can safely skip the per-thread overlap check.
606
- const region_size_bytes = BUFFER_INTRA_COPY_REGION_STRUCT.aligned_size;
607
-
608
- const data_regions_array = new Uint32Array(pending.length * (region_size_bytes / 4));
609
- const metadata_regions_array = new Uint32Array(pending.length * (region_size_bytes / 4));
610
-
611
- let total_records = 0;
612
- for (let i = 0; i < pending.length; i++) {
613
- const { source, clone } = pending[i];
614
-
615
- // Region struct is just three u32s — write directly. Order
616
- // matches the field declaration: src_offset, dst_offset,
617
- // word_count.
618
- const region_words = i * 3;
619
- data_regions_array[region_words] = source.data.offset;
620
- data_regions_array[region_words + 1] = clone.data.offset;
621
- data_regions_array[region_words + 2] = clone.data_size;
622
-
623
- metadata_regions_array[region_words] = source.metadata.offset * metadata_stride_words;
624
- metadata_regions_array[region_words + 1] = clone.metadata.offset * metadata_stride_words;
625
- metadata_regions_array[region_words + 2] = clone.meshlet_count * metadata_stride_words;
626
-
627
- total_records += clone.meshlet_count;
628
- }
629
-
630
- const data_regions_buffer = cmd_ctx.allocateTransientBufferAndLoad(
631
- data_regions_array.buffer,
632
- GPUBufferUsage.STORAGE,
633
- );
634
- shader_buffer_intra_copy_regions.dispatch({
635
- encoder: cmd_ctx,
636
- group_count_x: pending.length,
637
- bindings: {
638
- buffer: data_buffer,
639
- regions: data_regions_buffer,
640
- },
641
- });
642
-
643
- const metadata_regions_buffer = cmd_ctx.allocateTransientBufferAndLoad(
644
- metadata_regions_array.buffer,
645
- GPUBufferUsage.STORAGE,
646
- );
647
- shader_buffer_intra_copy_regions.dispatch({
648
- encoder: cmd_ctx,
649
- group_count_x: pending.length,
650
- bindings: {
651
- buffer: metadata_buffer,
652
- regions: metadata_regions_buffer,
653
- },
654
- });
655
-
656
- // Build the flat (record_index, address_delta) table. One pair
657
- // per cloned meshlet record. Uploaded as a transient storage
658
- // buffer the patch shader binds for one dispatch.
659
- const patches_array = new Uint32Array(total_records * 2);
660
- let p = 0;
661
- for (let i = 0; i < pending.length; i++) {
662
- const { source, clone } = pending[i];
663
-
664
- // Address delta is in u32 word units. When the clone's offset
665
- // is below the source's, the unsigned subtraction wraps — the
666
- // patch shader does the matching wrapping add, so the final
667
- // per-record `address` lands at the right absolute word offset
668
- // either way.
669
- const address_delta = (clone.data.offset - source.data.offset) >>> 0;
670
-
671
- for (let m = 0; m < clone.meshlet_count; m++) {
672
- patches_array[p++] = clone.metadata.offset + m;
673
- patches_array[p++] = address_delta;
674
- }
675
- }
676
-
677
- const patches_buffer = cmd_ctx.allocateTransientBufferAndLoad(
678
- patches_array.buffer,
679
- GPUBufferUsage.STORAGE,
680
- );
681
-
682
- // patch_count uniform is a bare u32 — see
683
- // shader_meshlet_metadata_address_patch.
684
- const patch_count_buffer = cmd_ctx.allocateTransientValueBuffer(
685
- u32,
686
- total_records,
687
- );
688
-
689
- shader_meshlet_metadata_address_patch.dispatchLinear({
690
- encoder: cmd_ctx,
691
- element_count: total_records,
692
- bindings: {
693
- meshlet_metadata: metadata_buffer,
694
- patches: patches_buffer,
695
- patch_count: patch_count_buffer,
696
- },
697
- });
698
-
699
- pending.length = 0;
700
- }
701
-
702
- destroy() {
703
- this.#meshlet_data_buffer.destroy();
704
- this.#meshlet_metadata_buffer.destroy();
705
- }
706
-
707
- /**
708
- *
709
- * @param {MeshletBatch} batch
710
- * @returns {MeshletBatchAllocationRecord}
711
- */
712
- add_batch(batch) {
713
- assert.defined(batch, 'batch');
714
- assert.notNull(batch, 'batch');
715
- assert.equal(batch.isMeshletBatch, true, 'batch.isMeshletBatch !== true');
716
-
717
- const record = this.#allocate_for_batch(batch);
718
-
719
- // plonk data
720
- const data_buffer = batch.data_buffer;
721
- const data_allocation = record.data;
722
-
723
- this.#device.queue.writeBuffer(
724
- this.#meshlet_data_buffer,
725
- data_allocation.offset << 2, // allocator works in words of 4 bytes
726
- data_buffer
727
- );
728
-
729
- // Write Metadata (Patch & Upload)
730
- this.#write_patched_metadata(
731
- record.metadata_buffer_original,
732
- record.meshlet_count,
733
- record.data.offset,
734
- record.metadata.offset,
735
- this.#meshlet_metadata_buffer
736
- );
737
-
738
- return record;
739
- }
740
-
741
- async debug_dump_metadata() {
742
- const device = this.#device;
743
-
744
- return await inspect_gpu_buffer_typed({
745
- buffer: this.#meshlet_metadata_buffer,
746
- device,
747
- type: MESHLET_METADATA_STRUCT,
748
- count: Math.floor(this.#meshlet_metadata_buffer.size / MESHLET_METADATA_STRUCT.aligned_size),
749
- });
750
- }
751
-
752
- get gpu_memory_usage_occupied() {
753
- let result = 0;
754
-
755
- for (const record of this.#records) {
756
- result += record.data_size * 4;
757
- }
758
-
759
- return result;
760
- }
761
-
762
- get gpu_memory_usage() {
763
- let result = 0;
764
-
765
- if (this.#meshlet_data_buffer) {
766
- result += this.#meshlet_data_buffer.size;
767
- }
768
-
769
- if (this.#meshlet_metadata_buffer) {
770
- result += this.#meshlet_metadata_buffer.size;
771
- }
772
-
773
- return result;
774
- }
1
+ import { assert } from "../../../../core/assert.js";
2
+ import { ALLOCATOR_NO_SPACE, OffsetAllocator } from "../../../../core/binary/allocator/OffsetAllocator.js";
3
+ import { BinaryBuffer } from "../../../../core/binary/BinaryBuffer.js";
4
+ import { platform_compute_endianness } from "../../../../core/binary/platform_compute_endianness.js";
5
+ import Signal from "../../../../core/events/signal/Signal.js";
6
+ import { MEGABYTE } from "../../../../core/science/units/memory/MEGABYTE.js";
7
+ import { read_wgsl_type_value } from "../../shader/type/serde/read_gpu_typed_buffer.js";
8
+ import { write_wgsl_type_value } from "../../shader/type/serde/write_gpu_typed_buffer.js";
9
+ import { u32 } from "../../shader/type/WEBGPU_WGSL_PRIMITIVE_TYPES.js";
10
+ import { inspect_gpu_buffer_typed } from "../../buffer/inspect_gpu_buffer_typed.js";
11
+ import {
12
+ BUFFER_INTRA_COPY_REGION_STRUCT,
13
+ shader_buffer_intra_copy_regions
14
+ } from "../../buffer/util/shader_buffer_intra_copy_regions.js";
15
+ import { MESHLET_METADATA_STRUCT } from "./encoding/MESHLET_METADATA_STRUCT.js";
16
+ import { shader_meshlet_metadata_address_patch } from "./shader_meshlet_metadata_address_patch.js";
17
+
18
+ /**
19
+ * In meshlets
20
+ * @type {number}
21
+ */
22
+ const DEFAULT_METADATA_CAPACITY = 128 * 1024;
23
+
24
+ /**
25
+ * In bytes
26
+ * @type {number}
27
+ */
28
+ const DEFAULT_DATA_CAPACITY = 32 * MEGABYTE;
29
+
30
+ class MeshletBatchAllocationRecord {
31
+ /**
32
+ * @type {Allocation}
33
+ */
34
+ metadata
35
+ /**
36
+ * @type {Allocation}
37
+ */
38
+ data
39
+
40
+ /**
41
+ * Number of words
42
+ * @type {number}
43
+ */
44
+ data_size = 0;
45
+
46
+ /**
47
+ * Meshlet count
48
+ * @type {number}
49
+ */
50
+ meshlet_count = 0;
51
+
52
+ /**
53
+ *
54
+ * @type {ArrayBuffer}
55
+ */
56
+ metadata_buffer_original;
57
+
58
+ /**
59
+ *
60
+ * @type {Signal<>}
61
+ */
62
+ changed = new Signal()
63
+ }
64
+
65
+ export class GPUMeshletManager {
66
+
67
+ /**
68
+ * Allocation sizes in words of 4 bytes
69
+ * @type {OffsetAllocator}
70
+ */
71
+ #allocator_data = new OffsetAllocator(1024);
72
+
73
+ /**
74
+ * Allocation sizes in meshlets
75
+ * @type {OffsetAllocator}
76
+ */
77
+ #allocator_metadata = new OffsetAllocator(1024);
78
+
79
+ /**
80
+ * @type {GPUBuffer}
81
+ */
82
+ #meshlet_data_buffer;
83
+
84
+ get buffer_data() {
85
+ return this.#meshlet_data_buffer;
86
+ }
87
+
88
+ /**
89
+ * @type {GPUBuffer}
90
+ */
91
+ #meshlet_metadata_buffer;
92
+
93
+ get buffer_metadata() {
94
+ return this.#meshlet_metadata_buffer;
95
+ }
96
+
97
+ /**
98
+ * @type {GPUDevice}
99
+ */
100
+ #device;
101
+
102
+ /**
103
+ *
104
+ * @type {number}
105
+ */
106
+ #deallocations_since_last_rebuild = 0;
107
+
108
+ /**
109
+ *
110
+ * @type {MeshletBatchAllocationRecord[]}
111
+ */
112
+ #records = [];
113
+
114
+ /**
115
+ * Queue of GPU-side clones that have been allocated but not yet
116
+ * copied + patched. Drained by {@link update}. Each entry holds the
117
+ * minimum bookkeeping needed to issue two `copyBufferToBuffer`
118
+ * commands and N `(record_index, address_delta)` patch entries.
119
+ *
120
+ * Entries name the two records, not the addresses they held when the
121
+ * clone was queued: a {@link compact} or {@link grow} in between
122
+ * migrates both, and {@link update} reads where they ended up. What
123
+ * an entry does require is that neither record is handed back to
124
+ * {@link remove_batch} before the flush.
125
+ *
126
+ * @type {{
127
+ * source: MeshletBatchAllocationRecord,
128
+ * clone: MeshletBatchAllocationRecord,
129
+ * }[]}
130
+ */
131
+ #pending_clones = [];
132
+
133
+ /**
134
+ *
135
+ * @param {GPUDevice} device
136
+ * @param {number} [initial_data_capacity] in bytes
137
+ * @param {number} [initial_metadata_capacity] in meshlets
138
+ */
139
+ constructor({
140
+ device,
141
+ initial_data_capacity = DEFAULT_DATA_CAPACITY,
142
+ initial_metadata_capacity = DEFAULT_METADATA_CAPACITY,
143
+ }) {
144
+ assert.defined(device, 'device');
145
+
146
+ assert.isNonNegativeInteger(initial_data_capacity, 'initial_data_capacity');
147
+ assert.isNonNegativeInteger(initial_metadata_capacity, 'initial_metadata_capacity');
148
+
149
+ assert.greaterThan(initial_data_capacity, 0, 'initial_data_capacity');
150
+ assert.greaterThan(initial_metadata_capacity, 0, 'initial_data_capacity');
151
+
152
+ this.#device = device;
153
+
154
+ const data_buffer_size_words = Math.ceil(initial_data_capacity / 4);
155
+
156
+ // TODO manage max-allocations as well
157
+ this.#allocator_data = new OffsetAllocator(data_buffer_size_words);
158
+ this.#allocator_metadata = new OffsetAllocator(initial_metadata_capacity);
159
+
160
+ this.#meshlet_data_buffer = device.createBuffer({
161
+ label: 'GPUMeshletManager/data',
162
+ size: data_buffer_size_words * 4,
163
+ usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC
164
+ });
165
+
166
+ this.#meshlet_metadata_buffer = device.createBuffer({
167
+ label: 'GPUMeshletManager/metadata',
168
+ size: initial_metadata_capacity * MESHLET_METADATA_STRUCT.aligned_size,
169
+ usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC
170
+ });
171
+
172
+ }
173
+
174
+ compact() {
175
+ this.#rebuild(
176
+ this.#meshlet_data_buffer.size >>> 2,
177
+ Math.ceil(this.#meshlet_metadata_buffer.size / MESHLET_METADATA_STRUCT.aligned_size)
178
+ );
179
+ }
180
+
181
+ /**
182
+ *
183
+ * @param {number} extra_data_size in words (4 bytes)
184
+ * @param {number} extra_metadata_size in metadata elements
185
+ */
186
+ grow(extra_data_size, extra_metadata_size) {
187
+ assert.isNonNegativeInteger(extra_data_size, 'extra_data_size');
188
+ assert.isNonNegativeInteger(extra_metadata_size, 'extra_metadata_size');
189
+
190
+ if (extra_data_size === 0 && extra_metadata_size === 0) {
191
+ // nothing to do
192
+ return;
193
+ }
194
+
195
+ const current_data_buffer_size_bytes = this.#meshlet_data_buffer.size;
196
+ const current_metadata_buffer_size = this.#meshlet_metadata_buffer.size;
197
+
198
+ const current_buffer_size_words = current_data_buffer_size_bytes >> 2;
199
+ const current_metadata_buffer_size_elements = Math.ceil(current_metadata_buffer_size / MESHLET_METADATA_STRUCT.aligned_size);
200
+
201
+ const DATA_GROWTH_STEP = 16 * 1024;
202
+ const METADATA_GROWTH_STEP = 256;
203
+ const GROWTH_MULTIPLIER = 1.25;
204
+
205
+ let new_data_size;
206
+ if (extra_data_size === 0) {
207
+ new_data_size = current_buffer_size_words;
208
+ } else {
209
+ // grow
210
+ new_data_size = Math.max(
211
+ current_buffer_size_words + extra_data_size,
212
+ current_buffer_size_words + DATA_GROWTH_STEP,
213
+ Math.ceil(current_buffer_size_words * GROWTH_MULTIPLIER)
214
+ );
215
+ }
216
+
217
+ let new_metadata_size;
218
+ if (extra_metadata_size === 0) {
219
+ new_metadata_size = current_metadata_buffer_size_elements;
220
+ } else {
221
+ // grow
222
+ new_metadata_size = Math.max(
223
+ current_metadata_buffer_size_elements + extra_metadata_size,
224
+ current_metadata_buffer_size_elements + METADATA_GROWTH_STEP,
225
+ current_metadata_buffer_size_elements * GROWTH_MULTIPLIER
226
+ );
227
+ }
228
+
229
+ this.#rebuild(
230
+ new_data_size,
231
+ new_metadata_size
232
+ );
233
+ }
234
+
235
+ /**
236
+ * Helper to patch pointers in metadata and upload to GPU
237
+ * @param {ArrayBuffer} source_array_buffer
238
+ * @param {number} meshlet_count
239
+ * @param {number} data_offset_words
240
+ * @param {number} metadata_offset_index
241
+ * @param {GPUBuffer} target_buffer
242
+ */
243
+ #write_patched_metadata(
244
+ source_array_buffer,
245
+ meshlet_count,
246
+ data_offset_words,
247
+ metadata_offset_index,
248
+ target_buffer
249
+ ) {
250
+ const temp_writer = BinaryBuffer.fromEndianness(platform_compute_endianness());
251
+ temp_writer.setCapacity(meshlet_count * MESHLET_METADATA_STRUCT.size); // pre-allocate to avoid resizing
252
+
253
+ const source_reader = BinaryBuffer.fromArrayBuffer(source_array_buffer);
254
+ source_reader.endianness = platform_compute_endianness();
255
+
256
+ // Iterate over meshlets in the batch, patch the address, and write to temp buffer
257
+ for (let i = 0; i < meshlet_count; i++) {
258
+ const header = read_wgsl_type_value(source_reader, MESHLET_METADATA_STRUCT);
259
+
260
+ // PATCH: Add the allocation offset to the relative address
261
+ header.address += data_offset_words;
262
+
263
+ write_wgsl_type_value(header, temp_writer, MESHLET_METADATA_STRUCT);
264
+ }
265
+
266
+ // Upload
267
+ this.#device.queue.writeBuffer(
268
+ target_buffer,
269
+ metadata_offset_index * MESHLET_METADATA_STRUCT.aligned_size,
270
+ temp_writer.data,
271
+ 0,
272
+ meshlet_count * MESHLET_METADATA_STRUCT.aligned_size
273
+ );
274
+ }
275
+
276
+ /**
277
+ *
278
+ * @param {number} new_data_size
279
+ * @param {number} new_metadata_size
280
+ */
281
+ #rebuild(
282
+ new_data_size,
283
+ new_metadata_size
284
+ ) {
285
+
286
+ assert.isNonNegativeInteger(new_data_size, 'new_data_size');
287
+ assert.isNonNegativeInteger(new_metadata_size, 'new_metadata_size');
288
+
289
+ // Outstanding clone work survives this. A queued clone names its
290
+ // source and destination records, both of which are migrated
291
+ // below, and the flush reads their addresses when it runs — so a
292
+ // rebuild moves the copy rather than invalidating it.
293
+
294
+ // console.warn('GPUMeshletManager/rebuild');
295
+
296
+ const device = this.#device;
297
+
298
+ const records = this.#records;
299
+ const record_count = records.length;
300
+ const new_allocator_data = new OffsetAllocator(
301
+ new_data_size,
302
+ Math.max(record_count * 2, 128 * 1024)
303
+ );
304
+
305
+ const old_buffer_data = this.#meshlet_data_buffer;
306
+ const new_buffer_data = device.createBuffer({
307
+ label: old_buffer_data.label,
308
+ usage: old_buffer_data.usage,
309
+ size: new_data_size * 4,
310
+ });
311
+
312
+ const new_allocator_metadata = new OffsetAllocator(
313
+ new_metadata_size,
314
+ Math.max(record_count * 2, 128 * 1024)
315
+ );
316
+
317
+ const old_buffer_metadata = this.#meshlet_metadata_buffer;
318
+ const new_buffer_metadata = device.createBuffer({
319
+ label: old_buffer_metadata.label,
320
+ usage: old_buffer_metadata.usage,
321
+ size: new_metadata_size * MESHLET_METADATA_STRUCT.aligned_size,
322
+ });
323
+
324
+ const encoder = device.createCommandEncoder({
325
+ label: "GPUMeshletManager/rebuild",
326
+ });
327
+
328
+ const moved_records = [];
329
+
330
+ // move data
331
+ for (let i = 0; i < record_count; i++) {
332
+ const record = records[i];
333
+
334
+ const new_allocation_data = new_allocator_data.allocate(record.data_size);
335
+ const new_allocation_metadata = new_allocator_metadata.allocate(record.meshlet_count);
336
+
337
+ assert.notEqual(new_allocation_data.offset, ALLOCATOR_NO_SPACE);
338
+ assert.notEqual(new_allocation_metadata.offset, ALLOCATOR_NO_SPACE);
339
+
340
+ encoder.copyBufferToBuffer(
341
+ old_buffer_data, record.data.offset * 4,
342
+ new_buffer_data, new_allocation_data.offset * 4,
343
+ record.data_size * 4
344
+ );
345
+
346
+ const metadata_record_moved = record.data.offset !== new_allocation_data.offset;
347
+
348
+ // B. Patch Metadata (CPU -> GPU Write)
349
+ // Metadata contains `address` which points to the Data offset.
350
+ // Since the Data offset changed, we must re-calculate and upload the metadata.
351
+ // We reuse the helper to avoid code duplication.
352
+ this.#write_patched_metadata(
353
+ record.metadata_buffer_original,
354
+ record.meshlet_count,
355
+ new_allocation_data.offset, // The NEW data offset
356
+ new_allocation_metadata.offset, // The NEW metadata location
357
+ new_buffer_metadata
358
+ );
359
+
360
+ // update record
361
+ record.data = new_allocation_data;
362
+ record.metadata = new_allocation_metadata;
363
+
364
+ if (metadata_record_moved) {
365
+ moved_records.push(record);
366
+ }
367
+ }
368
+
369
+ for (const record of moved_records) {
370
+ // notify
371
+ record.changed.send0();
372
+ }
373
+
374
+ const command_buffer = encoder.finish();
375
+
376
+ device.queue.submit([command_buffer]);
377
+
378
+ old_buffer_data.destroy();
379
+ old_buffer_metadata.destroy();
380
+
381
+ this.#meshlet_data_buffer = new_buffer_data;
382
+ this.#meshlet_metadata_buffer = new_buffer_metadata;
383
+
384
+ this.#allocator_data = new_allocator_data;
385
+ this.#allocator_metadata = new_allocator_metadata;
386
+
387
+ // reset count
388
+ this.#deallocations_since_last_rebuild = 0;
389
+ }
390
+
391
+ /**
392
+ * Allocate `data_size_words` in the data buffer and `meshlet_count`
393
+ * in the metadata buffer, retrying with compact-then-grow on failure.
394
+ * Throws if the third attempt still can't satisfy both. The two
395
+ * allocations are kept in lockstep — partial successes are released
396
+ * before retrying so we never leak.
397
+ *
398
+ * Compaction triggers a full {@link #rebuild} which migrates every
399
+ * existing allocation to a new buffer pair. Queued clone work is
400
+ * carried through it — {@link #pending_clones} names records, not
401
+ * addresses — so an allocation made while a clone is pending is safe.
402
+ *
403
+ * @param {number} data_size_words
404
+ * @param {number} meshlet_count
405
+ * @returns {{data: Allocation, metadata: Allocation}}
406
+ */
407
+ #allocate_data_and_metadata(data_size_words, meshlet_count) {
408
+ let data_allocation;
409
+ let metadata_allocation;
410
+
411
+ for (let try_index = 0; try_index < 3; try_index++) {
412
+
413
+ data_allocation = this.#allocator_data.allocate(data_size_words);
414
+ metadata_allocation = this.#allocator_metadata.allocate(meshlet_count);
415
+
416
+ const data_ok = data_allocation.offset !== ALLOCATOR_NO_SPACE;
417
+ const metadata_ok = metadata_allocation.offset !== ALLOCATOR_NO_SPACE;
418
+
419
+ if (data_ok && metadata_ok) {
420
+ break;
421
+ }
422
+
423
+ // release partial allocation
424
+ if (data_ok) {
425
+ this.#allocator_data.free(data_allocation);
426
+
427
+ data_allocation.offset = ALLOCATOR_NO_SPACE;
428
+ data_allocation.metadata = ALLOCATOR_NO_SPACE;
429
+ }
430
+ if (metadata_ok) {
431
+ this.#allocator_metadata.free(metadata_allocation);
432
+
433
+ metadata_allocation.offset = ALLOCATOR_NO_SPACE;
434
+ metadata_allocation.metadata = ALLOCATOR_NO_SPACE;
435
+ }
436
+
437
+ if (this.#deallocations_since_last_rebuild > 0) {
438
+ // try compaction
439
+ this.compact();
440
+ continue;
441
+ }
442
+
443
+ let grow_data_size = data_ok ? 0 : data_size_words;
444
+ let grow_metadata_size = metadata_ok ? 0 : meshlet_count;
445
+
446
+ this.grow(grow_data_size, grow_metadata_size);
447
+
448
+ }
449
+
450
+ if (data_allocation.offset === ALLOCATOR_NO_SPACE || metadata_allocation.offset === ALLOCATOR_NO_SPACE) {
451
+ throw new Error('Failed to allocate GPU memory for meshlet batch');
452
+ }
453
+
454
+ return { data: data_allocation, metadata: metadata_allocation };
455
+ }
456
+
457
+ /**
458
+ *
459
+ * @param {MeshletBatch} batch
460
+ * @returns {MeshletBatchAllocationRecord}
461
+ */
462
+ #allocate_for_batch(batch) {
463
+ assert.defined(batch, 'batch');
464
+
465
+ const data_buffer = batch.data_buffer;
466
+
467
+ const data_size_words = Math.ceil(data_buffer.byteLength / 4);
468
+
469
+ const meshlet_count = batch.count;
470
+
471
+ const { data, metadata } = this.#allocate_data_and_metadata(data_size_words, meshlet_count);
472
+
473
+ const record = new MeshletBatchAllocationRecord();
474
+
475
+ record.data = data;
476
+ record.metadata = metadata;
477
+
478
+ record.meshlet_count = meshlet_count;
479
+ record.data_size = data_size_words;
480
+
481
+ record.metadata_buffer_original = batch.metadata_buffer;
482
+
483
+ this.#records.push(record);
484
+
485
+ return record;
486
+ }
487
+
488
+ /**
489
+ * Release a batch allocation back to the data and metadata allocators. The GPU bytes are
490
+ * not cleared — the freed slices are reclaimed lazily by the next {@link compact} (a
491
+ * deallocation bumps the compaction counter the allocator consults under pressure).
492
+ * Other batches' allocations are unaffected (the OffsetAllocator frees disjoint slices),
493
+ * so callers only need to ensure nothing still references this batch's metadata rows
494
+ * (the geometry table rebuild in {@link GPUGeometryManager} handles that).
495
+ *
496
+ * @param {MeshletBatchAllocationRecord} record a record returned by {@link add_batch} or
497
+ * {@link clone_batch_allocation}
498
+ * @returns {boolean} true if the record was owned and freed
499
+ */
500
+ remove_batch(record) {
501
+ assert.defined(record, 'record');
502
+
503
+ const index = this.#records.indexOf(record);
504
+ if (index === -1) {
505
+ return false;
506
+ }
507
+
508
+ this.#allocator_data.free(record.data);
509
+ this.#allocator_metadata.free(record.metadata);
510
+
511
+ this.#records.splice(index, 1);
512
+ this.#deallocations_since_last_rebuild++;
513
+
514
+ return true;
515
+ }
516
+
517
+ /**
518
+ * Allocate a fresh (data, metadata) pair sized to mirror `source`,
519
+ * and queue a GPU-side clone: the source's data and metadata are
520
+ * copied into the new ranges via `copyBufferToBuffer`, then a
521
+ * compute pass patches the cloned metadata records' `address`
522
+ * fields to point at the new data range. Both the copies and the
523
+ * patch dispatch land when the host next calls
524
+ * {@link update update(cmd_ctx)}.
525
+ *
526
+ * The returned record can be wired into the geometry table
527
+ * immediately — its `data.offset` and `metadata.offset` are valid
528
+ * the moment this call returns. The GPU bytes at those addresses
529
+ * just aren't populated yet; downstream passes that consume the
530
+ * clone must run after the next `update`. Those addresses may still
531
+ * move before the flush, which is why nothing outside the record is
532
+ * allowed to remember them.
533
+ *
534
+ * Sharing the source's `metadata_buffer_original` lets a future
535
+ * compaction patch the clone's metadata from the same un-patched
536
+ * CPU bytes the source uses — the post-patch state on the GPU is
537
+ * identical to the source aside from the data offset.
538
+ *
539
+ * @param {MeshletBatchAllocationRecord} source
540
+ * @returns {MeshletBatchAllocationRecord}
541
+ */
542
+ clone_batch_allocation(source) {
543
+ assert.defined(source, 'source');
544
+ assert.greaterThan(source.meshlet_count, 0, 'source.meshlet_count');
545
+
546
+ const { data, metadata } = this.#allocate_data_and_metadata(source.data_size, source.meshlet_count);
547
+
548
+ const record = new MeshletBatchAllocationRecord();
549
+
550
+ record.data = data;
551
+ record.metadata = metadata;
552
+
553
+ record.meshlet_count = source.meshlet_count;
554
+ record.data_size = source.data_size;
555
+
556
+ // Share the un-patched CPU bytes so a future compact rewrites
557
+ // the clone's metadata correctly under its new data offset.
558
+ record.metadata_buffer_original = source.metadata_buffer_original;
559
+
560
+ this.#records.push(record);
561
+
562
+ // The queue names the two records rather than the addresses they
563
+ // hold right now. Both are in `#records`, so a rebuild between
564
+ // this call and the flush moves them and the flush still reads
565
+ // where they went — which is what lets a clone be queued in the
566
+ // same frame as the growth another geometry's arrival forces.
567
+ this.#pending_clones.push({ source, clone: record });
568
+
569
+ return record;
570
+ }
571
+
572
+ /**
573
+ * Flush any pending clone work into `cmd_ctx`. For each queued
574
+ * clone, emits two `copyBufferToBuffer` commands (one for vertex
575
+ * data, one for metadata records). After every queued clone's
576
+ * copies are recorded, dispatches a single batched patch shader
577
+ * pass that walks a flat `(record_index, address_delta)` table
578
+ * — N clones → 1 patch dispatch, total threads = sum of clone
579
+ * meshlet counts.
580
+ *
581
+ * Idempotent and cheap when nothing's queued.
582
+ *
583
+ * @param {ShadeGPUCommandContext} cmd_ctx
584
+ */
585
+ update(cmd_ctx) {
586
+ const pending = this.#pending_clones;
587
+ if (pending.length === 0) {
588
+ return;
589
+ }
590
+
591
+ const data_buffer = this.#meshlet_data_buffer;
592
+ const metadata_buffer = this.#meshlet_metadata_buffer;
593
+ const metadata_stride = MESHLET_METADATA_STRUCT.aligned_size;
594
+ const metadata_stride_words = metadata_stride / 4;
595
+
596
+ // WebGPU forbids `copyBufferToBuffer` with src === dst even when
597
+ // ranges don't overlap (the validation check is buffer-identity,
598
+ // not range-overlap), so we relocate via a small compute-shader
599
+ // memcpy bound to the buffer once as `read_write`. Two dispatches
600
+ // — one over the data-buffer regions, one over the metadata-buffer
601
+ // regions — handle every queued clone in a batched pass.
602
+ //
603
+ // Region (src, dst) ranges are non-overlapping by construction:
604
+ // the OffsetAllocator hands out fresh disjoint slices, so the
605
+ // memcpy shader can safely skip the per-thread overlap check.
606
+ const region_size_bytes = BUFFER_INTRA_COPY_REGION_STRUCT.aligned_size;
607
+
608
+ const data_regions_array = new Uint32Array(pending.length * (region_size_bytes / 4));
609
+ const metadata_regions_array = new Uint32Array(pending.length * (region_size_bytes / 4));
610
+
611
+ let total_records = 0;
612
+ for (let i = 0; i < pending.length; i++) {
613
+ const { source, clone } = pending[i];
614
+
615
+ // Region struct is just three u32s — write directly. Order
616
+ // matches the field declaration: src_offset, dst_offset,
617
+ // word_count.
618
+ const region_words = i * 3;
619
+ data_regions_array[region_words] = source.data.offset;
620
+ data_regions_array[region_words + 1] = clone.data.offset;
621
+ data_regions_array[region_words + 2] = clone.data_size;
622
+
623
+ metadata_regions_array[region_words] = source.metadata.offset * metadata_stride_words;
624
+ metadata_regions_array[region_words + 1] = clone.metadata.offset * metadata_stride_words;
625
+ metadata_regions_array[region_words + 2] = clone.meshlet_count * metadata_stride_words;
626
+
627
+ total_records += clone.meshlet_count;
628
+ }
629
+
630
+ const data_regions_buffer = cmd_ctx.allocateTransientBufferAndLoad(
631
+ data_regions_array.buffer,
632
+ GPUBufferUsage.STORAGE,
633
+ );
634
+ shader_buffer_intra_copy_regions.dispatch({
635
+ encoder: cmd_ctx,
636
+ group_count_x: pending.length,
637
+ bindings: {
638
+ buffer: data_buffer,
639
+ regions: data_regions_buffer,
640
+ },
641
+ });
642
+
643
+ const metadata_regions_buffer = cmd_ctx.allocateTransientBufferAndLoad(
644
+ metadata_regions_array.buffer,
645
+ GPUBufferUsage.STORAGE,
646
+ );
647
+ shader_buffer_intra_copy_regions.dispatch({
648
+ encoder: cmd_ctx,
649
+ group_count_x: pending.length,
650
+ bindings: {
651
+ buffer: metadata_buffer,
652
+ regions: metadata_regions_buffer,
653
+ },
654
+ });
655
+
656
+ // Build the flat (record_index, address_delta) table. One pair
657
+ // per cloned meshlet record. Uploaded as a transient storage
658
+ // buffer the patch shader binds for one dispatch.
659
+ const patches_array = new Uint32Array(total_records * 2);
660
+ let p = 0;
661
+ for (let i = 0; i < pending.length; i++) {
662
+ const { source, clone } = pending[i];
663
+
664
+ // Address delta is in u32 word units. When the clone's offset
665
+ // is below the source's, the unsigned subtraction wraps — the
666
+ // patch shader does the matching wrapping add, so the final
667
+ // per-record `address` lands at the right absolute word offset
668
+ // either way.
669
+ const address_delta = (clone.data.offset - source.data.offset) >>> 0;
670
+
671
+ for (let m = 0; m < clone.meshlet_count; m++) {
672
+ patches_array[p++] = clone.metadata.offset + m;
673
+ patches_array[p++] = address_delta;
674
+ }
675
+ }
676
+
677
+ const patches_buffer = cmd_ctx.allocateTransientBufferAndLoad(
678
+ patches_array.buffer,
679
+ GPUBufferUsage.STORAGE,
680
+ );
681
+
682
+ // patch_count uniform is a bare u32 — see
683
+ // shader_meshlet_metadata_address_patch.
684
+ const patch_count_buffer = cmd_ctx.allocateTransientValueBuffer(
685
+ u32,
686
+ total_records,
687
+ );
688
+
689
+ shader_meshlet_metadata_address_patch.dispatchThreads({
690
+ encoder: cmd_ctx,
691
+ thread_count_x: total_records,
692
+ bindings: {
693
+ meshlet_metadata: metadata_buffer,
694
+ patches: patches_buffer,
695
+ patch_count: patch_count_buffer,
696
+ },
697
+ });
698
+
699
+ pending.length = 0;
700
+ }
701
+
702
+ destroy() {
703
+ this.#meshlet_data_buffer.destroy();
704
+ this.#meshlet_metadata_buffer.destroy();
705
+ }
706
+
707
+ /**
708
+ *
709
+ * @param {MeshletBatch} batch
710
+ * @returns {MeshletBatchAllocationRecord}
711
+ */
712
+ add_batch(batch) {
713
+ assert.defined(batch, 'batch');
714
+ assert.notNull(batch, 'batch');
715
+ assert.equal(batch.isMeshletBatch, true, 'batch.isMeshletBatch !== true');
716
+
717
+ const record = this.#allocate_for_batch(batch);
718
+
719
+ // plonk data
720
+ const data_buffer = batch.data_buffer;
721
+ const data_allocation = record.data;
722
+
723
+ this.#device.queue.writeBuffer(
724
+ this.#meshlet_data_buffer,
725
+ data_allocation.offset << 2, // allocator works in words of 4 bytes
726
+ data_buffer
727
+ );
728
+
729
+ // Write Metadata (Patch & Upload)
730
+ this.#write_patched_metadata(
731
+ record.metadata_buffer_original,
732
+ record.meshlet_count,
733
+ record.data.offset,
734
+ record.metadata.offset,
735
+ this.#meshlet_metadata_buffer
736
+ );
737
+
738
+ return record;
739
+ }
740
+
741
+ async debug_dump_metadata() {
742
+ const device = this.#device;
743
+
744
+ return await inspect_gpu_buffer_typed({
745
+ buffer: this.#meshlet_metadata_buffer,
746
+ device,
747
+ type: MESHLET_METADATA_STRUCT,
748
+ count: Math.floor(this.#meshlet_metadata_buffer.size / MESHLET_METADATA_STRUCT.aligned_size),
749
+ });
750
+ }
751
+
752
+ get gpu_memory_usage_occupied() {
753
+ let result = 0;
754
+
755
+ for (const record of this.#records) {
756
+ result += record.data_size * 4;
757
+ }
758
+
759
+ return result;
760
+ }
761
+
762
+ get gpu_memory_usage() {
763
+ let result = 0;
764
+
765
+ if (this.#meshlet_data_buffer) {
766
+ result += this.#meshlet_data_buffer.size;
767
+ }
768
+
769
+ if (this.#meshlet_metadata_buffer) {
770
+ result += this.#meshlet_metadata_buffer.size;
771
+ }
772
+
773
+ return result;
774
+ }
775
775
  }