@woosh/meep-engine 3.60.4 → 3.60.5

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 (379) hide show
  1. package/CHANGELOG.md +2602 -0
  2. package/CONTRIBUTING.md +7 -0
  3. package/MIGRATION_2_TO_3.md +136 -0
  4. package/SECURITY.md +4 -0
  5. package/editor/EditorCameraNavigation.d.ts +2 -1
  6. package/editor/EditorCameraNavigation.d.ts.map +1 -1
  7. package/editor/EditorCameraNavigation.js +51 -57
  8. package/package.json +6 -2
  9. package/src/engine/EngineHarness.d.ts +3 -9
  10. package/src/engine/EngineHarness.d.ts.map +1 -1
  11. package/src/engine/EngineHarness.js +6 -8
  12. package/src/engine/graphics/camera/makeOrbitalCameraController.d.ts +9 -6
  13. package/src/engine/graphics/camera/makeOrbitalCameraController.d.ts.map +1 -1
  14. package/src/engine/graphics/camera/makeOrbitalCameraController.js +44 -71
  15. package/src/engine/graphics/camera/orbital/MomentumAxis.d.ts +64 -0
  16. package/src/engine/graphics/camera/orbital/MomentumAxis.d.ts.map +1 -0
  17. package/src/engine/graphics/camera/orbital/MomentumAxis.js +187 -0
  18. package/src/engine/graphics/camera/orbital/OrbitalCameraRig.d.ts +86 -0
  19. package/src/engine/graphics/camera/orbital/OrbitalCameraRig.d.ts.map +1 -0
  20. package/src/engine/graphics/camera/orbital/OrbitalCameraRig.js +319 -0
  21. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.d.ts +34 -10
  22. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.d.ts.map +1 -1
  23. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.js +116 -30
  24. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.d.ts +1 -7
  25. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.d.ts.map +1 -1
  26. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.js +1 -37
  27. package/src/engine/graphics/ecs/water/Water.d.ts +8 -0
  28. package/src/engine/graphics/ecs/water/Water.d.ts.map +1 -1
  29. package/src/engine/graphics/ecs/water/Water.js +8 -0
  30. package/src/engine/graphics3/WaterSystem.d.ts.map +1 -1
  31. package/src/engine/graphics3/WaterSystem.js +731 -734
  32. package/src/engine/graphics3/decal/DecalTileShader.d.ts +61 -0
  33. package/src/engine/graphics3/decal/DecalTileShader.d.ts.map +1 -0
  34. package/src/engine/graphics3/decal/DecalTileShader.js +138 -0
  35. package/src/engine/graphics3/decal/GPUDecalRenderer.d.ts +10 -4
  36. package/src/engine/graphics3/decal/GPUDecalRenderer.d.ts.map +1 -1
  37. package/src/engine/graphics3/decal/GPUDecalRenderer.js +149 -44
  38. package/src/engine/graphics3/decal/chunk_decal_tile_vertex.d.ts +25 -0
  39. package/src/engine/graphics3/decal/chunk_decal_tile_vertex.d.ts.map +1 -0
  40. package/src/engine/graphics3/decal/chunk_decal_tile_vertex.js +55 -0
  41. package/src/engine/graphics3/decal/graph_gather_decal_tiles.d.ts +22 -0
  42. package/src/engine/graphics3/decal/graph_gather_decal_tiles.d.ts.map +1 -0
  43. package/src/engine/graphics3/decal/graph_gather_decal_tiles.js +70 -0
  44. package/src/engine/graphics3/decal/shader_decal_composite.d.ts +7 -5
  45. package/src/engine/graphics3/decal/shader_decal_composite.d.ts.map +1 -1
  46. package/src/engine/graphics3/decal/shader_decal_composite.js +48 -44
  47. package/src/engine/graphics3/decal/shader_decal_tile_copy.d.ts +12 -0
  48. package/src/engine/graphics3/decal/shader_decal_tile_copy.d.ts.map +1 -0
  49. package/src/engine/graphics3/decal/shader_decal_tile_copy.js +78 -0
  50. package/src/engine/graphics3/decal/shader_gather_decal_tiles.d.ts +11 -0
  51. package/src/engine/graphics3/decal/shader_gather_decal_tiles.d.ts.map +1 -0
  52. package/src/engine/graphics3/decal/shader_gather_decal_tiles.js +80 -0
  53. package/src/engine/input/devices/mouse/MouseEventButtons.d.ts +13 -0
  54. package/src/engine/input/devices/mouse/MouseEventButtons.d.ts.map +1 -0
  55. package/src/engine/input/devices/mouse/MouseEventButtons.js +23 -0
  56. package/src/engine/input/ecs/controllers/KeyboardCameraController.d.ts +4 -0
  57. package/src/engine/input/ecs/controllers/KeyboardCameraController.d.ts.map +1 -1
  58. package/src/engine/input/ecs/controllers/KeyboardCameraController.js +9 -45
  59. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  60. package/src/shade/playground/water_refraction/README.md +5 -6
  61. package/src/shade/playground/water_refraction/index.html +1 -1
  62. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  63. package/src/shade/renderer/Renderer.js +7 -5
  64. package/src/shade/renderer/camera/orbital/OrbitalCameraController.d.ts +40 -80
  65. package/src/shade/renderer/camera/orbital/OrbitalCameraController.d.ts.map +1 -1
  66. package/src/shade/renderer/camera/orbital/OrbitalCameraController.js +197 -147
  67. package/src/shade/renderer/fluid/GPUFluidCascade.d.ts +3 -3
  68. package/src/shade/renderer/fluid/GPUFluidCascadeStack.d.ts +1 -1
  69. package/src/shade/renderer/fluid/forcing/GPUFluidForcing.d.ts +6 -6
  70. package/src/shade/renderer/fluid/pressure/GPUFluidPressureSolve.d.ts +1 -1
  71. package/src/shade/renderer/fluid/query/GPUFluidQueries.d.ts +1 -1
  72. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts +2 -2
  73. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.js +2 -2
  74. package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.d.ts +2 -1
  75. package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.d.ts.map +1 -1
  76. package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.js +18 -1
  77. package/src/shade/renderer/geometry/virtual/proxy/vgeo_to_proxy_mesh.d.ts +31 -0
  78. package/src/shade/renderer/geometry/virtual/proxy/vgeo_to_proxy_mesh.d.ts.map +1 -0
  79. package/src/shade/renderer/geometry/virtual/proxy/vgeo_to_proxy_mesh.js +343 -0
  80. package/src/shade/renderer/geometry/virtual/runtime/vgeo_meshlet_metadata_write.d.ts +17 -0
  81. package/src/shade/renderer/geometry/virtual/runtime/vgeo_meshlet_metadata_write.d.ts.map +1 -0
  82. package/src/shade/renderer/geometry/virtual/runtime/vgeo_meshlet_metadata_write.js +29 -0
  83. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.d.ts.map +1 -1
  84. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.js +7 -13
  85. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts.map +1 -1
  86. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.js +1 -8
  87. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.d.ts +5 -5
  88. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.d.ts.map +1 -1
  89. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.js +10 -10
  90. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts +1 -4
  91. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts.map +1 -1
  92. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.js +1 -10
  93. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_surface.d.ts +5 -15
  94. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_surface.d.ts.map +1 -1
  95. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_surface.js +6 -34
  96. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.d.ts.map +1 -1
  97. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.js +1 -7
  98. package/src/shade/renderer/hiz/HZBFootprint.d.ts +14 -0
  99. package/src/shade/renderer/hiz/HZBFootprint.d.ts.map +1 -0
  100. package/src/shade/renderer/hiz/HZBFootprint.js +16 -0
  101. package/src/shade/renderer/hiz/HZB_FOOTPRINT_X.d.ts +10 -0
  102. package/src/shade/renderer/hiz/HZB_FOOTPRINT_X.d.ts.map +1 -0
  103. package/src/shade/renderer/hiz/HZB_FOOTPRINT_X.js +11 -0
  104. package/src/shade/renderer/hiz/HZB_FOOTPRINT_Y.d.ts +10 -0
  105. package/src/shade/renderer/hiz/HZB_FOOTPRINT_Y.d.ts.map +1 -0
  106. package/src/shade/renderer/hiz/HZB_FOOTPRINT_Y.js +11 -0
  107. package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts +0 -7
  108. package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts.map +1 -1
  109. package/src/shade/renderer/hiz/HierarchicalZBuffer.js +405 -350
  110. package/src/shade/renderer/hiz/chunk_hzb_footprint_x.d.ts +12 -0
  111. package/src/shade/renderer/hiz/chunk_hzb_footprint_x.d.ts.map +1 -0
  112. package/src/shade/renderer/hiz/chunk_hzb_footprint_x.js +27 -0
  113. package/src/shade/renderer/hiz/chunk_hzb_footprint_y.d.ts +12 -0
  114. package/src/shade/renderer/hiz/chunk_hzb_footprint_y.d.ts.map +1 -0
  115. package/src/shade/renderer/hiz/chunk_hzb_footprint_y.js +27 -0
  116. package/src/shade/renderer/hiz/hzb_footprint_texels.d.ts +14 -0
  117. package/src/shade/renderer/hiz/hzb_footprint_texels.d.ts.map +1 -0
  118. package/src/shade/renderer/hiz/hzb_footprint_texels.js +34 -0
  119. package/src/shade/renderer/hiz/shader_hzb_reduce_any.d.ts +0 -5
  120. package/src/shade/renderer/hiz/shader_hzb_reduce_any.d.ts.map +1 -1
  121. package/src/shade/renderer/hiz/shader_hzb_reduce_any.js +37 -57
  122. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0.d.ts +11 -0
  123. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0.d.ts.map +1 -0
  124. package/src/shade/renderer/hiz/{shader_hzb_reduce_mip0_clip.js → shader_hzb_reduce_mip0.js} +120 -111
  125. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  126. package/src/shade/renderer/material/GPUMaterialManager.d.ts +7 -0
  127. package/src/shade/renderer/material/GPUMaterialManager.d.ts.map +1 -1
  128. package/src/shade/renderer/material/GPUMaterialManager.js +11 -0
  129. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts +12 -12
  130. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts.map +1 -1
  131. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.js +17 -118
  132. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.d.ts.map +1 -1
  133. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.js +7 -2
  134. package/src/shade/renderer/water/GPUWaterRenderer.d.ts +0 -6
  135. package/src/shade/renderer/water/GPUWaterRenderer.d.ts.map +1 -1
  136. package/src/shade/renderer/water/GPUWaterRenderer.js +12 -17
  137. package/src/shade/renderer/water/medium/graph_water_meshlets.d.ts +35 -0
  138. package/src/shade/renderer/water/medium/graph_water_meshlets.d.ts.map +1 -0
  139. package/src/shade/renderer/water/medium/graph_water_meshlets.js +89 -0
  140. package/src/shade/renderer/water/query/GPUWaterQueries.d.ts +1 -1
  141. package/src/shade/renderer/water/shader_water.d.ts.map +1 -1
  142. package/src/shade/renderer/water/shader_water.js +22 -13
  143. package/src/shade/renderer/water/shader_water_avboit.d.ts.map +1 -1
  144. package/src/shade/renderer/water/shader_water_avboit.js +7 -7
  145. package/src/shade/renderer/water/surface/graph_water_tessellation.d.ts +1 -1
  146. package/src/shade/renderer/water/surface/graph_water_tessellation.js +1 -1
  147. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +9 -1
  148. package/editor/REWORK_PLAN_2026_08.md +0 -692
  149. package/src/CODEC_LAYOUT_VERIFICATION.md +0 -419
  150. package/src/CORRECTNESS_HUNT_2026_07_24.md +0 -142
  151. package/src/DUPLICATION_AUDIT_2026_08_07/00_core_reuse_surface.md +0 -738
  152. package/src/DUPLICATION_AUDIT_2026_08_07/A_core_math_geom.md +0 -79
  153. package/src/DUPLICATION_AUDIT_2026_08_07/B_core_data.md +0 -93
  154. package/src/DUPLICATION_AUDIT_2026_08_07/C_gfx_render.md +0 -80
  155. package/src/DUPLICATION_AUDIT_2026_08_07/D_gfx_features.md +0 -164
  156. package/src/DUPLICATION_AUDIT_2026_08_07/E_physics.md +0 -69
  157. package/src/DUPLICATION_AUDIT_2026_08_07/F_sound.md +0 -110
  158. package/src/DUPLICATION_AUDIT_2026_08_07/G_net_save.md +0 -129
  159. package/src/DUPLICATION_AUDIT_2026_08_07/H_engine_misc.md +0 -72
  160. package/src/DUPLICATION_AUDIT_2026_08_07/I_generation_view.md +0 -74
  161. package/src/DUPLICATION_AUDIT_2026_08_07/K_crosscutting.md +0 -169
  162. package/src/DUPLICATION_AUDIT_2026_08_07.md +0 -254
  163. package/src/DUPLICATION_AUDIT_REMEDIATION.md +0 -250
  164. package/src/DUPLICATION_REVIEW_2026_10_24.md +0 -1072
  165. package/src/REVIEW_2026_08_06/adv-core.md +0 -511
  166. package/src/REVIEW_2026_08_06/adv-gfx.md +0 -480
  167. package/src/REVIEW_2026_08_06/adv-view.md +0 -446
  168. package/src/REVIEW_2026_08_06/api-core.md +0 -296
  169. package/src/REVIEW_2026_08_06/api-engine.md +0 -195
  170. package/src/REVIEW_2026_08_06/bvh-phys-sound-net.md +0 -507
  171. package/src/REVIEW_2026_08_06/core-binary.md +0 -348
  172. package/src/REVIEW_2026_08_06/core-collection.md +0 -376
  173. package/src/REVIEW_2026_08_06/core-math.md +0 -221
  174. package/src/REVIEW_2026_08_06/core-process.md +0 -266
  175. package/src/REVIEW_2026_08_06/core-science.md +0 -223
  176. package/src/REVIEW_2026_08_06/engine-ai-nav.md +0 -279
  177. package/src/REVIEW_2026_08_06/engine-animation.md +0 -167
  178. package/src/REVIEW_2026_08_06/engine-asset.md +0 -166
  179. package/src/REVIEW_2026_08_06/engine-ecs.md +0 -920
  180. package/src/REVIEW_2026_08_06/engine-input.md +0 -197
  181. package/src/REVIEW_2026_08_06/engine-misc.md +0 -248
  182. package/src/REVIEW_2026_08_06/engine-terrain.md +0 -52
  183. package/src/REVIEW_2026_08_06/generation.md +0 -176
  184. package/src/REVIEW_2026_08_06/geom-2d.md +0 -434
  185. package/src/REVIEW_2026_08_06/geom-3d.md +0 -117
  186. package/src/REVIEW_2026_08_06/gfx-gi.md +0 -143
  187. package/src/REVIEW_2026_08_06/gfx-render.md +0 -740
  188. package/src/REVIEW_2026_08_06/gfx-texture.md +0 -690
  189. package/src/REVIEW_2026_08_06/prior-criticals.md +0 -49
  190. package/src/REVIEW_2026_08_06/prior-highs.md +0 -138
  191. package/src/REVIEW_2026_08_06/test-gaps.md +0 -364
  192. package/src/REVIEW_2026_08_06/view.md +0 -667
  193. package/src/REVIEW_2026_08_06.md +0 -610
  194. package/src/core/binary/lz4/LZ4_HIGH_COMPRESSION_PLAN_2026_09_14.md +0 -542
  195. package/src/core/bvh2/bvh3/NOTES.md +0 -2
  196. package/src/core/bvh8/NOTES.md +0 -63
  197. package/src/core/cache/wtinylfu/benchmark/CacheWTinylfu.bench.md +0 -243
  198. package/src/core/color/COLOR_FIX_PLAN_2026_08_28.md +0 -417
  199. package/src/core/color/COLOR_REVIEW_2026_08_28.md +0 -779
  200. package/src/core/geom/2d/polygon/TRIANGULATION_DESIGN.md +0 -433
  201. package/src/core/geom/3d/atlas/ATLAS_PORT_PLAN.md +0 -486
  202. package/src/core/geom/3d/atlas/BFF_PLAN.md +0 -174
  203. package/src/core/geom/3d/atlas/REVIEW_02.md +0 -392
  204. package/src/core/geom/3d/gjk/GJK_REVIEW_NOTES.md +0 -146
  205. package/src/core/geom/3d/gjk/NOTES.md +0 -8
  206. package/src/core/geom/3d/topology/TOPO_MESH_RETIREMENT_PLAN.md +0 -135
  207. package/src/core/geom/GEOMETRY_REVIEW_002_API_ISSUES.md +0 -381
  208. package/src/core/geom/GEOMETRY_REVIEW_002_NAMING.md +0 -684
  209. package/src/core/geom/MANIFOLD_COMPARISON_2026_08_01.md +0 -554
  210. package/src/core/model/reactive/evaluation/PREDICATE_APPLICABILITY_PROPOSAL.md +0 -446
  211. package/src/engine/REVIEW_2026_07_03.md +0 -211
  212. package/src/engine/asset/loaders/image/IMAGE_CODEC_REVIEW_2026_08_28.md +0 -815
  213. package/src/engine/control/first-person/DESIGN.md +0 -658
  214. package/src/engine/control/first-person/DESIGN_COLLISION.md +0 -382
  215. package/src/engine/control/first-person/DESIGN_EXTENSIONS.md +0 -563
  216. package/src/engine/control/first-person/TODO.md +0 -160
  217. package/src/engine/ecs/ASYNC_SYSTEMS_PROPOSAL.md +0 -531
  218. package/src/engine/ecs/HIERARCHY_REVIEW.md +0 -717
  219. package/src/engine/ecs/async/WORKER_SYSTEM_PLAN.md +0 -262
  220. package/src/engine/ecs/ik/PLAN.md +0 -179
  221. package/src/engine/ecs/storage/binary/SERIALIZATION_COVERAGE.md +0 -272
  222. package/src/engine/ecs/storage/binary/object-v2/DESIGN.md +0 -199
  223. package/src/engine/ecs/transform/TRANSFORM64_MIGRATION_PLAN_2026_09_04.md +0 -521
  224. package/src/engine/graphics/CONTEXT_LOSS_RECOVERY_PLAN.md +0 -463
  225. package/src/engine/graphics/ecs/camera/pp/NOTES.md +0 -48
  226. package/src/engine/graphics/render/frame_graph/CORRECTNESS_FIXES_2026_09_04.md +0 -659
  227. package/src/engine/graphics/render/frame_graph/FRAME_GRAPH_RECOMMENDATIONS.md +0 -235
  228. package/src/engine/graphics/sh3/path_tracer/REVIEW_001_ACTION_PLAN.md +0 -490
  229. package/src/engine/graphics/texture/sampler/compare/FLIP_IMPLEMENTATION_PLAN.md +0 -466
  230. package/src/engine/input/GAMEPAD_API_PROPOSAL.md +0 -717
  231. package/src/engine/input/ecs/ism/ISM_DESIGN.md +0 -1523
  232. package/src/engine/input/ecs/ism/ISM_IMPLEMENTATION_PLAN.md +0 -627
  233. package/src/engine/navigation/mesh/OVERLAPPING_SURFACES_2026_08_25.md +0 -661
  234. package/src/engine/navigation/mesh/PATHFINDING_PLAN.md +0 -229
  235. package/src/engine/network/CONGESTION_CONTROL.md +0 -87
  236. package/src/engine/network/MESSAGE_TRANSPORT.md +0 -364
  237. package/src/engine/network/SMOOTHNESS_DESIGN.md +0 -400
  238. package/src/engine/physics/cloth/MEASUREMENTS.md +0 -5304
  239. package/src/engine/physics/cloth/PLAN.md +0 -3056
  240. package/src/engine/physics/fluid/GPU_FLUID_CASCADES_PROPOSAL_2026_10_16.md +0 -1382
  241. package/src/engine/physics/fluid/REVIEW_02_PLAN.md +0 -243
  242. package/src/engine/physics/fluid/solver/NOTES.md +0 -46
  243. package/src/engine/sound/SOUND_EMITTER_RETIREMENT_PLAN.md +0 -536
  244. package/src/engine/sound/simulation/IMPLEMENTATION_PLAN.md +0 -432
  245. package/src/engine/sound/simulation/PROBE_PLACEMENT.md +0 -250
  246. package/src/engine/sound/simulation/PROBE_TRANSFER_RESEARCH.md +0 -322
  247. package/src/engine/sound/simulation/REFLECTION_IR_PLAN.md +0 -261
  248. package/src/engine/sound/simulation/VISIBILITY_GRAPH_PLAN.md +0 -121
  249. package/src/engine/sound/sopra/IMPLEMENTATION_PLAN.md +0 -999
  250. package/src/format/image/avif/DECISIONS.md +0 -545
  251. package/src/format/image/jpeg/NOTES.md +0 -3
  252. package/src/format/scene/gltf/MESHOPT_COMPRESSION_PLAN.md +0 -814
  253. package/src/generation/COORDINATES.md +0 -54
  254. package/src/generation/REVIEW_01_ACTION_PLAN.md +0 -628
  255. package/src/generation/foliage/ANNUAL_SHOOTS_BRIEF_2026_10_25.md +0 -220
  256. package/src/generation/foliage/CROWN_FILLING_BRIEF_2026_10_26.md +0 -172
  257. package/src/generation/foliage/CROWN_FULLNESS_BRIEF_2026_10_26.md +0 -86
  258. package/src/generation/foliage/FOLIAGE_DESIGN.md +0 -547
  259. package/src/generation/foliage/FOLIAGE_PLAN.md +0 -198
  260. package/src/generation/foliage/LEAF_ARCHITECTURE_BRIEF_2026_10_26.md +0 -158
  261. package/src/generation/foliage/LOD_REWORK_BRIEF_2026_10_27.md +0 -521
  262. package/src/generation/foliage/REAL_TIME_TREES_BRIEF_2026_10_27.md +0 -86
  263. package/src/generation/foliage/SELF_ORGANIZING_FRAMEWORK_BRIEF_2026_10_26.md +0 -347
  264. package/src/generation/foliage/SHOOT_SYSTEMS_ALL_SPECIES_BRIEF_2026_10_26.md +0 -248
  265. package/src/generation/foliage/SPEEDTREE_COMPARISON_2026_10_24.md +0 -694
  266. package/src/generation/foliage/TWO_FRONT_ENDS_BRIEF_2026_10_28.md +0 -552
  267. package/src/shade/RENDERER_CONTRACT.md +0 -697
  268. package/src/shade/SHADER_F16_PROPOSAL_2026_10_24.md +0 -263
  269. package/src/shade/device/IMMEDIATE_DATA_PLAN_2026_08_28.md +0 -569
  270. package/src/shade/device/PASS_ENCODER_PROPOSAL_2026_08_31.md +0 -755
  271. package/src/shade/device/mock/EXTENSION_PROPOSAL_2026_08_22.md +0 -458
  272. package/src/shade/device/mock/IMPLEMENTATION_PLAN.md +0 -255
  273. package/src/shade/device/mock/REVIEW_2026_08_22.md +0 -527
  274. package/src/shade/device/timing/GPU_PROFILER_PROPOSAL_2026_08_28.md +0 -894
  275. package/src/shade/device/timing/profile/SGPT_FORMAT.md +0 -345
  276. package/src/shade/renderer/NOTES.md +0 -78
  277. package/src/shade/renderer/PLAN.md +0 -8
  278. package/src/shade/renderer/animation/ADDITIVE_BLENDING_PLAN.md +0 -145
  279. package/src/shade/renderer/animation/SKINNING_FRAME_PLAN.md +0 -357
  280. package/src/shade/renderer/atmosphere/NOTES.md +0 -6
  281. package/src/shade/renderer/camera/orbital/MomentumAxis.d.ts +0 -76
  282. package/src/shade/renderer/camera/orbital/MomentumAxis.d.ts.map +0 -1
  283. package/src/shade/renderer/camera/orbital/MomentumAxis.js +0 -164
  284. package/src/shade/renderer/camera/orbital/OrbitalCameraConfig.d.ts +0 -45
  285. package/src/shade/renderer/camera/orbital/OrbitalCameraConfig.d.ts.map +0 -1
  286. package/src/shade/renderer/camera/orbital/OrbitalCameraConfig.js +0 -66
  287. package/src/shade/renderer/camera/orbital/OrbitalCameraInput.d.ts +0 -98
  288. package/src/shade/renderer/camera/orbital/OrbitalCameraInput.d.ts.map +0 -1
  289. package/src/shade/renderer/camera/orbital/OrbitalCameraInput.js +0 -184
  290. package/src/shade/renderer/camera/orbital/OrbitalCameraRig.d.ts +0 -175
  291. package/src/shade/renderer/camera/orbital/OrbitalCameraRig.d.ts.map +0 -1
  292. package/src/shade/renderer/camera/orbital/OrbitalCameraRig.js +0 -411
  293. package/src/shade/renderer/camera/orbital/README.md +0 -81
  294. package/src/shade/renderer/camera/orbital/index.d.ts +0 -6
  295. package/src/shade/renderer/camera/orbital/index.d.ts.map +0 -1
  296. package/src/shade/renderer/camera/orbital/index.js +0 -9
  297. package/src/shade/renderer/extension/RENDER_EXTENSION_DESIGN.md +0 -964
  298. package/src/shade/renderer/fluid/GPU_FLUID_OCCUPANCY_AND_SOTA_REVIEW_2026_10_26.md +0 -361
  299. package/src/shade/renderer/geometry/bvh/BLAS_REFIT_BATCH_PLAN.md +0 -619
  300. package/src/shade/renderer/geometry/meshlet/COMPACTION_PLAN_2026_09_16.md +0 -674
  301. package/src/shade/renderer/geometry/sdf/NOTES.md +0 -6
  302. package/src/shade/renderer/geometry/virtual/ATTRIBUTE_PLAN_2026_09_18.md +0 -540
  303. package/src/shade/renderer/geometry/virtual/MICRON_COMPARISON_2026_08_20.md +0 -305
  304. package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +0 -913
  305. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md +0 -787
  306. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_PLAN.md +0 -481
  307. package/src/shade/renderer/geometry/virtual/VK_LOD_CLUSTERS_COMPARISON_2026_08_20.md +0 -301
  308. package/src/shade/renderer/geometry/virtual/WEDGE_PLAN_2026_09_18.md +0 -448
  309. package/src/shade/renderer/geometry/virtual/runtime/CULL_PLAN_2026_09_16.md +0 -385
  310. package/src/shade/renderer/geometry/virtual/runtime/CUT_PLAN_2026_09_14.md +0 -656
  311. package/src/shade/renderer/geometry/virtual/runtime/REVIEW_LEDGER.md +0 -308
  312. package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN.md +0 -657
  313. package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN_2026_09_14.md +0 -551
  314. package/src/shade/renderer/geometry/virtual/runtime/SHADOW_PLAN_2026_09_28.md +0 -529
  315. package/src/shade/renderer/global_illumination/lpv/NOTES.md +0 -3
  316. package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_CLIPMAP_PROPOSAL_2026_10_27.md +0 -4306
  317. package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_RAYS_VARIABLE_AND_SORTED_SCOPE_2026_10_30.md +0 -410
  318. package/src/shade/renderer/global_illumination/sharc/NOTES.md +0 -8
  319. package/src/shade/renderer/gpu_primitive/bvh/GPU_BVH_BUILD_PLAN.md +0 -1369
  320. package/src/shade/renderer/gpu_primitive/bvh/PHASE1_HANDOVER.md +0 -202
  321. package/src/shade/renderer/gpu_primitive/indirect/NOTES.md +0 -94
  322. package/src/shade/renderer/hiz/NOTES.md +0 -3
  323. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.d.ts +0 -8
  324. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.d.ts.map +0 -1
  325. package/src/shade/renderer/light/cluster/NOTES.md +0 -11
  326. package/src/shade/renderer/light/cluster/PROPOSAL_ALGORITHM_2026_10_16.md +0 -524
  327. package/src/shade/renderer/light/cluster/PROPOSAL_HIERARCHICAL_2026_10_16.md +0 -1101
  328. package/src/shade/renderer/material/resident/shader_resident_texture_means.d.ts +0 -24
  329. package/src/shade/renderer/material/resident/shader_resident_texture_means.d.ts.map +0 -1
  330. package/src/shade/renderer/material/resident/shader_resident_texture_means.js +0 -120
  331. package/src/shade/renderer/particles/DESIGN.md +0 -1011
  332. package/src/shade/renderer/particles/EMITTER_GLOBAL_STATE_PLAN_2026_10_29.md +0 -280
  333. package/src/shade/renderer/particles/ORIENTATION_PLAN_2026_10_15.md +0 -453
  334. package/src/shade/renderer/path_tracer/PATH_TRACED_MEDIA_PLAN_2026_10_02.md +0 -520
  335. package/src/shade/renderer/postprocess/bloom/NOTES.md +0 -6
  336. package/src/shade/renderer/postprocess/fsr/NOTES.md +0 -4
  337. package/src/shade/renderer/postprocess/gtao/NOTES.md +0 -6
  338. package/src/shade/renderer/postprocess/nss/NOTES.md +0 -332
  339. package/src/shade/renderer/postprocess/perspective/NOTES.md +0 -3
  340. package/src/shade/renderer/postprocess/sscs/NOTES.md +0 -5
  341. package/src/shade/renderer/postprocess/ssr/NOTES.md +0 -14
  342. package/src/shade/renderer/postprocess/ssr/REPROJECTION_PROPOSAL.md +0 -286
  343. package/src/shade/renderer/postprocess/ssr/prefilter/NOTES.md +0 -39
  344. package/src/shade/renderer/postprocess/taa/NOTES.md +0 -11
  345. package/src/shade/renderer/postprocess/upscale-dither/NOTES.md +0 -78
  346. package/src/shade/renderer/postprocess/vrs/NOTES.md +0 -4
  347. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_LEDGER.md +0 -1218
  348. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_PLAN_2026_09_03.md +0 -600
  349. package/src/shade/renderer/restir/reservoir/NOTES.md +0 -6
  350. package/src/shade/renderer/shader/chunk/atmosphere/NOTES.md +0 -6
  351. package/src/shade/renderer/shader/chunk/brdf/FRESNEL_BRIEF_2026_10_16.md +0 -391
  352. package/src/shade/renderer/shader/chunk/color/tonemap/ACES_TONE_MAPPING_BRIEF_2026_10_17.md +0 -153
  353. package/src/shade/renderer/shader/chunk/color/tonemap/HDR_TONE_MAPPING_FORMS_BRIEF_2026_10_16.md +0 -322
  354. package/src/shade/renderer/shader/chunk/color/tonemap/SDR_TONE_MAPPING_BRIEF_2026_10_16.md +0 -247
  355. package/src/shade/renderer/shader/chunk/fsr/NOTES.md +0 -3
  356. package/src/shade/renderer/shader/chunk/fsr/spd/NOTES.md +0 -4
  357. package/src/shade/renderer/shader/chunk/geometry/ray/NOTES.md +0 -4
  358. package/src/shade/renderer/shader/chunk/material/MULTISCATTER.md +0 -159
  359. package/src/shade/renderer/shader/chunk/mboit/NOTES.md +0 -38
  360. package/src/shade/renderer/shader/chunk/spherical_harmonics/l1i/NOTES.md +0 -3
  361. package/src/shade/renderer/shader/chunk/spherical_harmonics/sampling/NOTES.md +0 -8
  362. package/src/shade/renderer/shader/compiler/SHADER_SWITCH_PROPOSAL_2026_10_16.md +0 -927
  363. package/src/shade/renderer/shadow/contact/FINDINGS.md +0 -413
  364. package/src/shade/renderer/shadow/map/csm/NOTES.md +0 -5
  365. package/src/shade/renderer/shadow/ray/NOTES.md +0 -31
  366. package/src/shade/renderer/shadow/sdf/NOTES.md +0 -9
  367. package/src/shade/renderer/shadow/sdf/brixelizer/NOTES.md +0 -5
  368. package/src/shade/renderer/shadow/sdf/cascade/NOTES.md +0 -9
  369. package/src/shade/renderer/shadow/vsm/NOTES.md +0 -12
  370. package/src/shade/renderer/shadow/vsm/PLAN.md +0 -442
  371. package/src/shade/renderer/shadow/vsm/REVIEW_LEDGER.md +0 -43
  372. package/src/shade/renderer/texture/COMPRESSED_TEXTURE_PLAN_2026_09_01.md +0 -2444
  373. package/src/shade/renderer/texture/mipmaps/NOTES.md +0 -3
  374. package/src/shade/renderer/volumetrics/NOTES.md +0 -1329
  375. package/src/shade/renderer/volumetrics/VOLUMETRIC_NOISE_PLAN_2026_10_15.md +0 -336
  376. package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts +0 -23
  377. package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts.map +0 -1
  378. package/src/shade/renderer/water/medium/GPUWaterBodies.js +0 -75
  379. package/src/shade/wgsl/NOTES.md +0 -4
@@ -1,569 +0,0 @@
1
- # WGSL immediate data in Shade — landing plan, 2026-08-28
2
-
3
- How to bring the WGSL `immediate` address space (`var<immediate>` + `GPUBindingCommandsMixin.setImmediates`)
4
- into Shade, starting from `ShadeGPUCommandContext.allocateTransientBuffer` and the call sites that
5
- feed it.
6
-
7
- **Verdict:** worth landing. Shade already models this exact concept, under this exact name, on top of
8
- uniform buffers — nineteen of the twenty structs literally called `PUSH_CONSTANTS` fit inside the
9
- 64-byte budget unmodified. With no fallback in scope the remaining work is small and almost entirely
10
- additive.
11
-
12
- > **Scope decision (rev 2).** An earlier revision of this plan carried a dual-lowering fallback:
13
- > `var<immediate>` where supported, `@group(N) @binding(0) var<uniform>` where not, chosen by a
14
- > capability probe. That is now **out of scope**, and it should never have been in it —
15
- > [`RENDERER_CONTRACT.md` §2](../RENDERER_CONTRACT.md) already states the rule it violated: *"Shade
16
- > requires a WebGPU device that meets a fixed floor. There is no feature detection beyond it and no
17
- > branch that produces a lesser frame."* `ShadeDeviceFailureReason` says the same thing in its header
18
- > comment, citing D6/I-8: no degradation tiers, no fallbacks, no feature-detection branches.
19
- >
20
- > Immediates therefore join the floor rather than getting a compatibility path. That deletes: the
21
- > import-time capability probe and its unrecoverable failure mode, the extra bind group the fallback
22
- > lowering needed, the resulting exclusion of the 7 shaders already at `maxBindGroups: 4`, and the
23
- > `vi.resetModules()` dance every dual-lowering spec would have required. If a fallback is ever
24
- > genuinely wanted, §9 shows why it belongs in a shader variant rather than in the binding machinery.
25
- >
26
- > **Scope decision (rev 3).** Immediates are also *not* a device floor. `Renderer.initialize()` does
27
- > not check for them and the contract's §2 is unchanged; a browser without immediates starts and
28
- > renders as before. Detection is exported instead (§4), so a shader author decides between refusing
29
- > and reaching for a variant. This keeps the engine's floor where it was while the feature lands.
30
-
31
- ---
32
-
33
- ## 1. What the feature is, restricted to what matters here
34
-
35
- Immediates are WebGPU's portable spelling of Vulkan push constants / D3D12 root constants / Metal
36
- `setBytes`. The parts that constrain this plan:
37
-
38
- | | Value |
39
- |---|---|
40
- | WGSL gate | `requires immediate_address_space;` — a **language extension**, not a `GPUFeatureName` |
41
- | Detection | `navigator.gpu.wgslLanguageFeatures.has('immediate_address_space')` — synchronous, no adapter needed |
42
- | Size budget | `maxImmediateSize`, default **64 bytes**, multiple of 4, **no separate Compatibility Mode default** |
43
- | Declaration | Module scope, immutable, **no `@group` / `@binding`** |
44
- | Store type | Host-shareable constructible, **arrays and structs containing arrays are disallowed** |
45
- | Count | **At most one** immediate variable statically accessed per entry point |
46
- | Layout rules | Storage-space (natural alignment), **not** uniform-space |
47
- | API | `pass.setImmediates(rangeOffset, data, dataOffset, dataSize)`, on render pass, compute pass and bundle encoders |
48
- | Pipeline layout | `GPUPipelineLayoutDescriptor.immediateSize` (default 0, ≤ `maxImmediateSize`, multiple of 4) |
49
- | Init | Bytes start zeroed but **slots start unset**; a draw reading an unset 4-byte slot is a validation error |
50
- | Reset | Per-encoder; survives `setPipeline`, cleared by a new pass and by `executeBundles` |
51
-
52
- Because `maxImmediateSize` is a `maximum`-class limit with a default of 64 and no compat-mode
53
- reduction, **any adapter that exposes the WGSL extension reports at least 64 bytes**. There is no
54
- device where the language feature is present and the budget is not. That is what makes a floor check
55
- sufficient and a runtime branch unnecessary.
56
-
57
- Availability is a browser-version question, not a hardware one: Vulkan's `maxPushConstantsSize` is
58
- core-1.0 with a 128-byte required minimum and no feature bit, and no device in the
59
- `vulkan.gpuinfo.org` corpus reports below 128. Chrome ships immediates in 149–150.
60
-
61
- ---
62
-
63
- ## 2. The current path, traced
64
-
65
- Shade already has this pattern, three layers deep.
66
-
67
- **Declaration** — `ShaderResourceGroupDescriptor.addUniform` (`ShaderResourceGroupDescriptor.js:335`):
68
-
69
- ```js
70
- const PUSH_CONSTANTS = WebGPUStruct.from({
71
- probe_index_offset: u32,
72
- probe_update_count: u32,
73
- probe_resolution: u32,
74
- probe_count: u32
75
- });
76
-
77
- resources.createGroup()
78
- .addUniform("settings", PUSH_CONSTANTS)
79
- .addTexture("tAtlas_radiance");
80
- ```
81
-
82
- **Codegen** — `ShaderResourceSetDescriptor.generateCodeChunk` (`ShaderResourceSetDescriptor.js:305`)
83
- emits `@group(0) @binding(0) var<uniform> settings : PUSH_CONSTANTS;`.
84
-
85
- **Upload, per dispatch** — `ShadeGPUCommandContext.allocateTransientValueBuffer` (`:598`):
86
-
87
- ```js
88
- const settings = context.encoder.allocateTransientValueBuffer(PUSH_CONSTANTS, { … });
89
-
90
- shader.dispatch({ encoder: context.encoder, bindings: { settings, … } });
91
- ```
92
-
93
- which lands in `allocateTransientBuffer` (`:538`) → `GPUBufferAllocator.get` (pooled), then
94
- `writeValueBuffer` (`:620`) → staging buffer → `copyBufferToBuffer`, then a `BindGroupDescriptor` hash
95
- lookup in `GraphicsContext.setPipelineBindings` (`GraphicsContext.js:211`) and finally
96
- `pass.setBindGroup(i, bind_group)`.
97
-
98
- The FrameGraph route reaches the same place: `graph_compute_pass` → `graph_prepare_bindings` →
99
- `encoder_prepare_shader_binding` (`:34`), which turns a raw value or `ArrayBuffer` into a transient
100
- uniform buffer on the spot.
101
-
102
- **Scale of the surface:**
103
-
104
- - 67 `allocateTransientValueBuffer` call sites, 11 `allocateTransientBufferAndLoad`, 21 `graph_import_typed_buffer`
105
- - 300 `addUniform` declarations; 22 of those *call sites* name a `*PUSH_CONSTANTS*` type, across
106
- **20 distinct struct definitions** (`LPV_ACCUMULATION_PUSH_CONSTANTS` is shared by four shaders).
107
- The table in §6 is per-struct, so it has 20 rows, not 22
108
- - The single most common transient value type is a bare `u32` (12 sites)
109
-
110
- ---
111
-
112
- ## 3. What the win actually is
113
-
114
- Not bandwidth. 64 bytes at a few hundred dispatches a frame is noise on any bus.
115
-
116
- The win is the machinery each of those 20-byte structs currently drags behind it:
117
-
118
- 1. **A pooled `GPUBuffer` acquire/release per value.** `GPUBufferAllocator.get` plus the transient
119
- tracking array in `ShadeGPUCommandContext`, per dispatch, per frame.
120
- 2. **A staging buffer, a `getMappedRange`, an `unmap` and a `copyBufferToBuffer` per value** —
121
- `writeValueBuffer` does all four for what is often four bytes.
122
- 3. **A `BindGroupDescriptor` construction plus a `HashMap` lookup per value**, and a cache entry
123
- whose key changes every time the value changes — so the small-uniform bind groups are the ones
124
- least likely to hit the cache.
125
- 4. **Two JS allocations per value, in the serialiser.** `writeValueBuffer` reaches
126
- `write_gpu_typed_buffer`, which calls `scratch_buffer.fromArrayBuffer(data)`, which constructs a
127
- fresh `DataView` and `Uint8Array` over the target every single call. Per dispatch, per frame, in a
128
- codebase that advertises "zero-allocation" in its own package keywords. The immediate path writes
129
- into a persistent `BinaryBuffer` and allocates nothing (§6, L3).
130
- 5. **A `@binding` slot** in the group the settings share.
131
-
132
- Point 5 is *only* a binding, not a group. Measured across all twenty candidates: the settings uniform
133
- shares its group with textures and storage buffers in every case, at a median of about four resources
134
- per group. No bind group slot is recovered anywhere in the current codebase. (An earlier revision
135
- claimed otherwise and built an argument on it; that argument is withdrawn.)
136
-
137
- **The strongest case is `graph_import_typed_buffer`** (`renderer/shader/graph/graph_import_typed_buffer.js`,
138
- 21 call sites). It does not use the transient pool at all — per execution it calls `device.createBuffer`
139
- with `mappedAtCreation`, `getMappedRange`, `unmap`, `copyBufferToBuffer`, then `destroy` on
140
- `encoder.onFinished`. A full buffer create-and-destroy per frame per value, carrying things like
141
- `LPV_ACCUMULATION_PUSH_CONSTANTS` at 20 bytes. The source carries a standing TODO ("this is pretty
142
- inefficient, figure out a way to skip needing 2 buffers") and a 2025-09-13 note that the obvious fix —
143
- transient buffer plus `queue.writeBuffer` — produced garbage for reasons never tracked down.
144
- Immediates make the two-buffer dance unnecessary by construction for the ≤64-byte cases rather than
145
- requiring that bug to be found first.
146
-
147
- Caveat: converting a `graph_import_typed_buffer` site is *not* a drop-in. It returns a graph resource
148
- id that passes consume as an ordinary binding, so the consuming pass stops declaring a graph input.
149
- Higher payoff per site, higher cost per site.
150
-
151
- Honest counterweight: on hardware, a 64-byte immediate may well land in a driver-managed constant
152
- bank rather than in registers, so do not promise a shader-side speedup. Promise the removal of
153
- CPU-side per-draw allocation, and measure the rest.
154
-
155
- ---
156
-
157
- ## 4. Capability, not a floor
158
-
159
- **Built as optional.** `Renderer.initialize()` does not consult it and nothing was added to
160
- `RENDERER_CONTRACT.md` §2 — a browser without immediates still starts and still renders. It simply
161
- cannot run a shader that declares one, and deciding what to do about that belongs to whoever wrote
162
- the shader.
163
-
164
- `device/immediate_data_supported.js` answers the two separate questions:
165
-
166
- ```js
167
- immediate_data_supported() // does this browser's WGSL have the address space?
168
- device_supports_immediate_size(device, bytes) // does this adapter have the budget?
169
- ```
170
-
171
- The first is synchronous and needs no adapter — `wgslLanguageFeatures` hangs off `navigator.gpu` —
172
- so it can be asked at module scope, where shader descriptors are built. That is what makes the
173
- shader-variant approach in §9 workable: one `if` at the point a shader is chosen.
174
-
175
- Nothing goes in `requiredFeatures`: `immediate_address_space` is a WGSL *language* extension, not a
176
- `GPUFeatureName`, and putting it there would make `requestDevice` reject on every device. Nothing
177
- goes in `requiredLimits` either — 64 is the spec default, granted without asking.
178
-
179
- `ShaderDescriptor.validate_against_device` does check the budget, and throws naming the shader and
180
- the shortfall. That is a per-draw guard against a device that disagrees with the language extension,
181
- not a startup gate; it fires only for a shader that actually declares immediate data.
182
-
183
- ## 5. The one structural obstacle
184
-
185
- `ShaderResourceSetDescriptor` is a list of groups; a group is a list of resources; a resource has a
186
- `binding_index`. Everything downstream — `generateBindings` returning `GPUBindingResource[][]`,
187
- `generatePipelineLayoutDescriptor` producing one `BindGroupLayoutDescriptor` per group,
188
- `generateCodeChunk` prefixing every declaration with `@group(g) @binding(b)` — assumes that shape.
189
-
190
- An immediate has no group, no binding, and there is at most one per entry point. It has to live
191
- *beside* the groups, and its value needs a second route to the pass encoder that ends in
192
- `setImmediates` rather than `setBindGroup`.
193
-
194
- Nothing about that requires a device, so the whole thing stays where Shade already does its work:
195
- in descriptors built at module scope. `PipelineLayoutDescriptor` is device-free by design and stays
196
- that way.
197
-
198
- ---
199
-
200
- ## 6. The landing plan
201
-
202
- **L1 — declaration + codegen**
203
-
204
- - `ShaderResourceSetDescriptor`: an `#immediate` slot beside `#groups`, a `setImmediate(name, type)`
205
- that rejects a second call, plus validation at declaration time: `type.size <= 64`, no arrays
206
- anywhere in the type (walk it — `WebGPUArray` members and nested structs both), no atomics, size a
207
- multiple of 4.
208
- - `generateCodeChunk` (`ShaderResourceSetDescriptor.js:305`): emit `var<immediate> name : Type;`
209
- first, with no `@group`/`@binding` prefix.
210
- - `ShaderDescriptor.compile` (`ShaderDescriptor.js:53`): the existing mechanism emits
211
- `enable ${extension};`. Language extensions use **`requires`**. That needs a second channel on
212
- `CodeChunk` — `addLanguageExtension` beside `addExtension` — and a `requires` line in the prelude.
213
- Do not overload `addExtension`; its assert is against `WebGPUExtensionType`, which is the
214
- `GPUFeatureName` namespace.
215
-
216
- **L2 — pipeline layout**
217
-
218
- - `PipelineLayoutDescriptor`: an `immediateSize = 0` field, threaded through `from`, `forStage`,
219
- `fromJSON`, `hash`, `equals`, `copy`, `clone`. Missing it from `hash`/`equals` would silently alias
220
- two layouts that differ only in immediate size — cheap mistake, expensive symptom.
221
- - `PipelineLayoutManager.#create_pipeline_layout` (`PipelineLayoutManager.js:85`): pass it to
222
- `createPipelineLayout`.
223
- - `generatePipelineLayoutDescriptor` / `generatePipelineLayout`: set it from `roundUp(4, type.size)`.
224
-
225
- **L3 — binding resolution and the call**
226
-
227
- **Do not change the return shape of `generateBindings`.** Returning `{ groups, immediate }` would
228
- break all **10** external callers (`GPUParticularRenderer`, `GPUDynamicMeshResidency`,
229
- `graph_draw_lightmap_viz_buffer`, `prototypeParticleSystem`, `graph_atorus_denoise_luma`,
230
- `graph_rasterize_meshes_transparent_oit`, `ComputeShader`, `ImageShader`, `RenderPassDescriptor`,
231
- `GPUWaterRenderer`) and force the new field through the **22** external
232
- `constructRenderPass`/`constructComputePass` call sites. Purely additive instead:
233
-
234
- - `ShaderResourceSetDescriptor.writeImmediate(map, buffer)` — a *new* method that **allocates
235
- nothing**. It writes the immediate value into a caller-owned `BinaryBuffer` at that buffer's
236
- current `position`, advances `position` by the type's size, and returns `false` when the shader
237
- declares no immediate. Every existing `generateBindings` caller is untouched.
238
-
239
- Use the already-exported `write_wgsl_type_value(value, buffer, type)`
240
- (`serde/write_gpu_typed_buffer.js:114`), which takes a `BinaryBuffer` directly. **Do not** route
241
- this through `write_gpu_typed_buffer` (`:188`): that wrapper calls
242
- `scratch_buffer.fromArrayBuffer(data)`, and `fromArrayBuffer` constructs `new DataView(data)` and
243
- `new Uint8Array(data)` on every call — two allocations per write, and it resets `position` to 0.
244
- The zero-allocation entry point already exists and is public; the wrapper is the thing to avoid.
245
-
246
- - `GraphicsContext.setPipelineBindings` (`GraphicsContext.js:211`) gains an optional fourth
247
- parameter — the `BinaryBuffer`, plus the byte offset and length written. After the `setBindGroup`
248
- loop: `pass.setImmediates(0, buffer.data, byte_offset, byte_length)`. `setImmediates` takes
249
- `AllowSharedBufferSource`, and for a plain `ArrayBuffer` the offset and size arguments are in
250
- **bytes**, so the backing store is passed through with no copy on our side. All four existing
251
- callers keep working.
252
-
253
- - `ImageShader.draw` and `ComputeShader.constructPass`/`dispatch` set `position`, call
254
- `writeImmediate`, and thread the buffer plus range through as optional fields.
255
-
256
- Three properties of `BinaryBuffer` that this design has to respect:
257
-
258
- 1. **`data` is not stable.** `setCapacity` (and therefore `ensureCapacity`) reallocates the backing
259
- `ArrayBuffer` and rebinds `dataView` / `__data_uint8`. Read `buffer.data` at the point of the
260
- `setImmediates` call, never cache it — and size the buffer once at construction so it never grows
261
- mid-frame.
262
- 2. **Endianness is not the default.** `BinaryBuffer` defaults to `EndianType.LittleEndian`, but the
263
- serde module's own scratch sets `endianness = platform_compute_endianness()` (`:179`) because GPU
264
- buffer contents must match the host. A caller-supplied buffer that skips this writes correct-looking
265
- bytes that are wrong on a big-endian host and identical on the one you test on. Set it at
266
- construction, and assert it in `writeImmediate`.
267
- 3. **Ownership.** One long-lived buffer on `ShadeGPUCommandContext`, sized to `maxImmediateSize` at
268
- construction, is enough — `setImmediates` copies the bytes at call time, so the region is free for
269
- reuse immediately after. The API is still parameterised on `position` rather than hardcoding 0,
270
- because that costs nothing and leaves room for a caller that wants to stage several values or keep
271
- a frame log of them.
272
- - `encoder_prepare_shader_binding` (`:34`): in the immediate case, return the raw value rather than
273
- allocating a transient buffer.
274
-
275
- **The eight bypass paths.** `setPipelineBindings` does not cover everything. Eight files drive
276
- `pass.setBindGroup` directly and never touch it — `GPUMaterialManager`, `prototypeParticleSystem`,
277
- `graph_build_depth_mipmap`, `render_copy_texture`, `graph_rasterize_meshes_alpha_tested`,
278
- `graph_rasterize_meshes_transparent_oit`, `draw_texture_as_rgba`, `WebGPUMipmapGenerator`. A shader
279
- migrated in one of those needs its own `setImmediates` call at the right point in the loop. None of
280
- the twenty candidates lives there, so this constrains future migration rather than this landing — but
281
- it must not be assumed away.
282
-
283
- **L4 — the test tiers**
284
-
285
- Without a fallback these are not optional: they are the only way immediates get exercised outside a
286
- browser.
287
-
288
- - **WGSL validation** (`wgsl/validate_wgsl_source.js`) — teach it that `var<immediate>` is legal and
289
- `requires immediate_address_space;` is a valid directive, so the new declaration does not trip the
290
- foreign-token or undeclared-call rules.
291
- - **JS emulator** — a real trap. `collectGlobalVariable`
292
- (`wgsl/emulator/WGSLJavaScriptCompiler.js:825`) only pushes to `globalBindings` when *both* `group`
293
- and `binding` are non-null (`:862`). A `var<immediate>` has neither, is not `workgroup`, and falls
294
- through to the `privateGlobals` branch, where it is **silently default-initialised**. Every emulator
295
- spec for an immediate-using chunk would pass while reading zeros. Route
296
- `address_space === 'immediate'` into `globalBindings` with a null group/binding so
297
- `emu.bindings.settings = {…}` keeps working by name.
298
- - **SoftwareGPUDevice** — `DEFAULT_LIMITS.maxImmediateSize` (`mock/SoftwareGPUDevice.js:67`) is
299
- already present, set to `0`. Raise it to 64, add `setImmediates` to `SoftwareGPURenderPassEncoder`
300
- (`:130`) and `SoftwareGPUComputePassEncoder` (`:108`) with the spec's `[[immediate_data]]` /
301
- `[[immediate_slots_set]]` state, validate `rangeOffset % 4`, size `% 4` and the range bound, and
302
- snapshot the bytes into each recorded draw/dispatch the way `#snapshot_bindings` already snapshots
303
- bind groups. Follow the folder's stated policy in `mock/IMPLEMENTATION_PLAN.md` §2: validation
304
- errors go through `generate_error`, they do not throw. `createPipelineLayout` (`:766`) gains the
305
- `immediateSize ≤ maxImmediateSize` and `% 4` checks. Emulate the unset-slot rule too — it is the
306
- mock's chance to catch a missing `setImmediates` before a browser does.
307
- - **Playground** — one pass converted end to end and run in Chrome before any bulk migration.
308
-
309
- **L5 — migration**
310
-
311
- Smallest first. `shader_read_out_bucket` (4 bytes, one `u32`) is the ideal first patient;
312
- `shader_generate_rays` and `shader_shadow_upscale` are the same shape.
313
-
314
- **It is not one line per shader.** Only 5 of the 20 candidates supply their settings implicitly, by
315
- passing a plain object in `bindings` and letting `encoder_prepare_shader_binding` allocate. The other
316
- ~15 have an explicit `allocateTransientValueBuffer` or `graph_import_typed_buffer` call that
317
- pre-builds the buffer and passes the `GPUBuffer`. Those need two edits: the declaration, and the call
318
- site that stops pre-allocating.
319
-
320
- ---
321
-
322
- ## 7. Candidate inventory
323
-
324
- Every struct named `*PUSH_CONSTANTS*`, sized by `WebGPUStruct` (the same natural-alignment layout the
325
- `immediate` address space uses). Sizes were re-derived programmatically — field lists extracted from
326
- source and fed back through the real `WebGPUStruct.from`, not computed by hand.
327
-
328
- | Struct | Size | Verdict |
329
- |---|---:|---|
330
- | `shader_read_out_bucket` | 4 | fits |
331
- | `shader_generate_rays` | 4 | fits |
332
- | `shader_shadow_upscale` | 4 | fits |
333
- | `shader_sharc_evict` | 8 | fits |
334
- | `shader_lightmap_accummulate_tile` | 8 | fits |
335
- | `shader_lightmap_tile_composit` | 8 | fits |
336
- | `graph_lpv_store_sh` | 16 | fits |
337
- | `shader_perfect_perspective` | 16 | fits, see note |
338
- | `graph_lvp_atlas_to_sh` | 20 | fits |
339
- | `LPV_ACCUMULATION_PUSH_CONSTANTS` | 20 | fits |
340
- | `shader_lpv_accumulate_depth` | 20 | fits |
341
- | `graph_sample_light_probes` | 20 | fits |
342
- | `graph_reduce_atlas_to_sh` | 24 | fits |
343
- | `shader_lpv_build_gbuffer` | 24 | fits |
344
- | `shader_update_clipmap_cascade` | 32 | fits |
345
- | `graph_lpv_shade` | 32 | fits |
346
- | `shader_build_scene_sdf_f32` | 32 | fits |
347
- | `shader_scene_sdf_debug_viz` | 32 | fits |
348
- | `shader_scene_sdf_soft_shadows` | 48 | fits |
349
- | `LIGHT_CLUSTER_ASSIGNMENT_PUSH_CONSTANTS` | 128 | **cannot move** |
350
-
351
- `LIGHT_CLUSTER_ASSIGNMENT_PUSH_CONSTANTS` fails twice: 128 bytes against a 64-byte budget, and
352
- `frustum: WebGPUArray.from(vec4f, 6)` is an array, which the `immediate` address space disallows
353
- outright. It stays a uniform buffer. If the rest is worth moving later, the split is
354
- `{cluster_params, screen_resolution, distance_min, distance_max}` at 32 bytes as an immediate with the
355
- frustum left in a buffer — a follow-up, not part of this landing.
356
-
357
- Note on `shader_perfect_perspective`: its `use_vignette` member is typed `bool`, and Shade's `bool`
358
- primitive (`WEBGPU_WGSL_PRIMITIVE_TYPES.js:8`) is a 4-byte type. WGSL's host-shareable set is numeric
359
- scalars, vectors, matrices, atomics, arrays and structs of those — `bool` is in none of them, in *any*
360
- address space, uniform included. So this is a pre-existing condition rather than something immediates
361
- introduce, but the member needs to become `u32` before this struct can move. Worth checking whether
362
- that shader is reachable at all; grep finds no importer.
363
-
364
- Beyond the named ones, the 12 bare-`u32` `allocateTransientValueBuffer` sites are the same pattern
365
- without the naming, and are equally good candidates.
366
-
367
- ### What must not move
368
-
369
- `CAMERA_UNIFORM_STRUCT` (~640 bytes: eight `mat4x4f`, a six-element `vec4f` frustum array, and more)
370
- and `VIEW_UNIFORM_STRUCT` account for 116 of the 300 `addUniform` calls. They are per-view, written
371
- once, read by everything — over budget, containing arrays, and not per-draw data. They stay. Same for
372
- anything reached through `GPUDatabase` or `GPUSingleTypeTable`.
373
-
374
- ---
375
-
376
- ## 8. What was built
377
-
378
- Landed in one pass, all against a green suite (14,873 tests). Deviations from the plan above are
379
- noted where they occurred.
380
-
381
- **Mock device** — `SoftwareGPUImmediateData.js` holds WebGPU's `[[immediate_data]]` /
382
- `[[immediate_slots_set]]`; `setImmediates` on both pass encoders, with content-timeline failures
383
- throwing `OperationError` and device-timeline failures invalidating the encoder through a new
384
- `SoftwareGPUCommandEncoder#notify_pass_validation_error`; `maxImmediateSize` raised 0 → 64;
385
- `createPipelineLayout` validates `immediateSize`. Draws and dispatches now run WebGPU's *Validate
386
- immediate data*, so a forgotten `setImmediates` is caught here rather than in a browser.
387
-
388
- **Emulator** — `WGSLJavaScriptCompiler` routes `var<immediate>` into `globalBindings` instead of
389
- letting it fall through to `privateGlobals`, where it was silently default-initialised. Pinned by a
390
- spec that fails without the fix.
391
-
392
- **Validator** — *no change needed.* The plan assumed `validate_wgsl_source` would have to learn about
393
- `requires` and `var<immediate>`; it already accepts both. A regression test pins that so a future
394
- rule cannot start false-positiving on them.
395
-
396
- **Pipeline layout** — `PipelineLayoutDescriptor.immediateSize`, through `from`/`forStage`/`fromJSON`/
397
- `hash`/`equals`/`copy`, and passed to `createPipelineLayout` by the manager.
398
-
399
- **Codegen** — `CodeChunk#addLanguageExtension` as a separate channel from `addExtension`, emitted as
400
- `requires` rather than `enable` and deliberately not folded into `#features` (a different namespace
401
- from `GPUFeatureName`). `ShaderResourceSetDescriptor#setImmediate` validates size, arrays at any
402
- depth, atomics and runtime sizing at declaration time.
403
-
404
- **Binding** — `writeImmediate(map, buffer)` writes into a caller-owned `BinaryBuffer` at its
405
- `position` via the already-public `write_wgsl_type_value`, allocating nothing.
406
- `ShadeGPUCommandContext` owns one such buffer for the life of the context.
407
- `GraphicsContext#setPipelineBindings` took an optional fourth parameter, so all four existing callers
408
- are untouched — `generateBindings` kept its return shape and its 10 callers were not disturbed.
409
-
410
- **FrameGraph** — the plan missed this entirely. `getResourceByName` only searches groups, so the
411
- graph path threw for any immediate; and worse, `graph_prepare_pass_input_data` and
412
- `graph_prepare_bindings` read a numeric input as a graph resource id, which would have silently
413
- misread a bare `u32` immediate as a handle. `isImmediateName` now short-circuits all three helpers.
414
- Pinned by a spec that fails without it.
415
-
416
- **Detection** — `immediate_data_supported()` / `device_supports_immediate_size()`, plus a budget
417
- check in `ShaderDescriptor.validate_against_device`.
418
-
419
- ### Not done
420
-
421
- - **No production shader migrated.** The 19 candidates in §7 are unchanged; this is the machinery
422
- only. `shader_read_out_bucket` remains the right first patient.
423
- - **`shader_perfect_perspective`'s `bool` member** is still a `bool` (§7's note). Pre-existing, and
424
- it blocks only that one struct.
425
- - **The eight `setBindGroup` bypass paths** (§6, L3) are untouched. No candidate lives there.
426
-
427
- ## 9. Transition candidates, proved
428
-
429
- Every candidate below was **actually transitioned** — declaration and call site edited in the working
430
- tree — then proved, then reverted. This section records what the proof showed, not what inspection
431
- suggested. The tier-1 entries were carried all the way through; the rest were assessed against the
432
- same checks without being applied.
433
-
434
- ### What "proved" means here
435
-
436
- The six checks each applied transition had to pass:
437
-
438
- 1. **Module imports.** These shaders build at module scope, so a rejected type, a broken declaration
439
- or bad codegen throws at import.
440
- 2. **`immediateSize` is what the struct says.**
441
- 3. **Generated WGSL declares `var<immediate>` with no `@group`/`@binding`, and emits `requires`.**
442
- 4. **`validate_wgsl_source` returns no diagnostics** on the generated source.
443
- 5. **`pipeline.layout.immediateSize` matches**, so a device would accept the pipeline.
444
- 6. **A value round-trips** through `writeImmediate` into the expected words.
445
-
446
- Plus, across the whole set: the settings name must no longer resolve through `getResourceByName` or
447
- appear in any group — a stale uniform left beside a new immediate would bind twice.
448
-
449
- **What the proof does not cover, and why the scores are not higher.** *None of these shaders has any
450
- spec coverage* — `grep -rl <name> --include=*.spec.js` returns zero for every one, and for their call
451
- sites too. The full suite passing with all five applied (14,887 tests) therefore says only that
452
- nothing *else* broke. Nothing executes these passes outside a browser, so the call-site edit is
453
- verified by inspection and by the diff being a verbatim move, not by running it.
454
-
455
- **The one silent failure mode.** A misspelled *field* inside the value object is not an error:
456
- `write_gpu_typed_buffer` writes 0 for a member it cannot find. Probed and confirmed —
457
- `{bucket_ID: 11, frame: 2}` against `{bucket_id, frame}` yields words `[0, 2]`, no throw. This is
458
- pre-existing behaviour that immediates inherit rather than introduce (the same is true of
459
- `allocateTransientValueBuffer` today), but it is why every transition below moves the object literal
460
- **verbatim** rather than retyping it, and why the diffs are worth reading rather than trusting.
461
-
462
- ### Tier 1 — transitioned and proved end to end
463
-
464
- | Shader | Bytes | Call-site change | Group effect | Net lines | Confidence |
465
- |---|---:|---|---|---:|---:|
466
- | `shader_sharc_evict` | 8 | **none** — already passes a plain object | leads a group of 4; siblings shift down one | −0 | **95%** |
467
- | `shader_shadow_upscale` | 4 | **none** — already passes a plain object | leads a group of 5; siblings shift down one | −0 | **95%** |
468
- | `shader_read_out_bucket` | 4 | 2 sites, inline `{ bucket_id }` | **owns group 0 outright** — group disappears, later groups renumber | −8 | **90%** |
469
- | `shader_generate_rays` | 4 | 1 site, inline `{ frame_index }` | **last in its group** — no sibling index moves | −4 | **92%** |
470
- | `graph_lvp_atlas_to_sh` | 20 | 1 site, inline 5-field object | owns group 0 outright — group disappears | −5 | **85%** |
471
-
472
- The two at 95% need *no call-site edit at all*: they already pass `settings: { … }` as a plain
473
- object, because the immediate path accepts a value where the uniform path demands a bindable buffer.
474
- Their entire diff is one line in the declaration. The residual 5% is that no test executes them.
475
-
476
- `shader_read_out_bucket` scores lower than `shader_generate_rays` despite being smaller: removing its
477
- uniform empties group 0, so every later group renumbers. That is consistent between the WGSL and the
478
- layout because both regenerate from the same descriptor, and it is why it still scores 90 — but it is
479
- a larger blast radius than a declaration that sits last in a shared group and moves nothing.
480
-
481
- `graph_lvp_atlas_to_sh` is the lowest of the tier because its value object has five fields drawn from
482
- four different sources, which is the most surface for the silent-misspelling failure above.
483
-
484
- ### Tier 2 — same shape, not yet applied
485
-
486
- Assessed against the same checks by inspection. All are live (one importer each), all fit, none
487
- contains an array.
488
-
489
- | Shader | Bytes | Notes | Confidence |
490
- |---|---:|---|---:|
491
- | `graph_lpv_store_sh` | 16 | leads a group of 3 | 80% |
492
- | `graph_reduce_atlas_to_sh` | 24 | owns its group | 80% |
493
- | `shader_lpv_build_gbuffer` | 24 | ImageShader | 75% |
494
- | `shader_lpv_accumulate_depth` | 20 | ImageShader, group of 5 | 75% |
495
- | `graph_lpv_shade` | 32 | group of 3 | 75% |
496
- | `graph_sample_light_probes` | 20 | ImageShader, group of 7 | 70% |
497
- | `shader_build_scene_sdf_f32` | 32 | **two** resource sets (f32 and f16) share one struct; both must move together | 65% |
498
-
499
- `LPV_ACCUMULATION_PUSH_CONSTANTS` (20 bytes, 5 importers) is deliberately absent. It is shared across
500
- four shaders and supplied through `graph_import_typed_buffer`, so it is not a per-shader edit — it is
501
- the `graph_import_typed_buffer` conversion described in §3, which changes how the consuming pass
502
- declares its input. Higher payoff, different job.
503
-
504
- ### Not worth doing
505
-
506
- Six candidates have **zero importers** — nothing in the tree constructs them:
507
- `shader_lightmap_tile_composit`, `shader_lightmap_accummulate_tile`, `shader_update_clipmap_cascade`,
508
- `shader_perfect_perspective`, `shader_scene_sdf_debug_viz`, `shader_scene_sdf_soft_shadows`.
509
-
510
- They would transition trivially and change nothing that runs. `shader_perfect_perspective` also still
511
- carries the `bool` member from §7. Migrating dead shaders would inflate the count and prove nothing;
512
- they are listed so nobody spends a morning on them.
513
-
514
- `LIGHT_CLUSTER_ASSIGNMENT_PUSH_CONSTANTS` remains excluded on both counts — 128 bytes and an array.
515
-
516
- ### Recommended order
517
-
518
- `shader_sharc_evict` first, then `shader_shadow_upscale`: one line each, no call site, and they
519
- establish the pattern with the smallest possible diff. `shader_generate_rays` next, as the first with
520
- a call-site edit. Then `shader_read_out_bucket`, which is the first to renumber groups and therefore
521
- the first worth looking at in a browser before moving on.
522
-
523
- Before any of them lands in a build anyone plays: run one in Chrome 149+. Nothing in the suite
524
- executes these passes, and that gap is the whole of the residual risk.
525
-
526
- ---
527
-
528
- ## 10. If a fallback is ever wanted anyway
529
-
530
- It belongs in a shader variant, not in the binding machinery — and Shade already supports that with
531
- no new mechanism. `ShaderDescriptor.compile()` clones the body chunk and adds the resource set's
532
- generated chunk as a dependency:
533
-
534
- ```js
535
- const chunk = this.#body.clone();
536
- chunk.addDependency(this.#resources.generateCodeChunk());
537
- ```
538
-
539
- So one `CodeChunk` body pairs with two different `ShaderResourceSetDescriptor`s to produce two
540
- `ShaderDescriptor`s — immediate and uniform — from a single source of shader text, selected by
541
- whoever constructs them. The body never changes: `settings.probe_count` reads identically either way.
542
-
543
- That is a per-shader, opt-in decision made by the shader's author, which is a very different thing
544
- from a capability branch threaded through codegen, pipeline layout, binding resolution and the test
545
- harness. Keeping the door open costs nothing; walking through it preemptively cost a great deal.
546
-
547
- ---
548
-
549
- ## 11. Open questions
550
-
551
- - **Which shader migrates first, and when?** Answered in §9: `shader_sharc_evict` and
552
- `shader_shadow_upscale` need no call-site edit at all. The machinery is inert until one lands.
553
- - **Do these passes get a spec before or after they migrate?** None of the twelve live candidates has
554
- any test coverage, which is the entire residual risk in §9. A spec that drives one through the
555
- software device would convert several 75s into 90s, and is probably worth more than the next three
556
- migrations.
557
- - **Is 64 bytes worth revisiting later?** `maxImmediateSize` is raisable, but D3D12's root signature
558
- budget means no browser is likely to expose more. `IMMEDIATE_SIZE_FLOOR` refuses more than 64 at
559
- declaration; if a project ever knows its device offers more, that constant is the single place to
560
- relax.
561
- - **Should `graph_import_typed_buffer`'s two-buffer dance be fixed independently?** §3 argues its 21
562
- call sites are the strongest case for immediates, but the standing TODO there is a separate bug —
563
- the 2025-09-13 note says the obvious fix produced garbage and was never chased down. Immediates
564
- route around it for the ≤64-byte cases without explaining it.
565
- Answered 2026-09-26: fixed independently. The garbage was frame-graph aliasing — a `queue.writeBuffer`
566
- into a pooled buffer lands ahead of the whole submission, and a pass earlier in it had written that
567
- buffer for another resource. The import now writes with `writeValueBuffer`, in order, and creates no
568
- buffer per frame; `graph_import_typed_buffer.spec.js` pins both. Immediates would still drop the
569
- buffer altogether for the ≤64-byte cases.