@woosh/meep-engine 3.60.4 → 3.60.6

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 (419) hide show
  1. package/CHANGELOG.md +2621 -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 +11 -0
  63. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  64. package/src/shade/renderer/Renderer.js +50 -6
  65. package/src/shade/renderer/camera/orbital/OrbitalCameraController.d.ts +40 -80
  66. package/src/shade/renderer/camera/orbital/OrbitalCameraController.d.ts.map +1 -1
  67. package/src/shade/renderer/camera/orbital/OrbitalCameraController.js +197 -147
  68. package/src/shade/renderer/fluid/GPUFluidCascade.d.ts +3 -3
  69. package/src/shade/renderer/fluid/GPUFluidCascadeStack.d.ts +1 -1
  70. package/src/shade/renderer/fluid/forcing/GPUFluidForcing.d.ts +6 -6
  71. package/src/shade/renderer/fluid/pressure/GPUFluidPressureSolve.d.ts +1 -1
  72. package/src/shade/renderer/fluid/query/GPUFluidQueries.d.ts +1 -1
  73. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts +28 -45
  74. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts.map +1 -1
  75. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.js +111 -119
  76. package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.d.ts +22 -3
  77. package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.d.ts.map +1 -1
  78. package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.js +84 -3
  79. package/src/shade/renderer/geometry/virtual/proxy/vgeo_proxy_triangle_limit.d.ts +19 -0
  80. package/src/shade/renderer/geometry/virtual/proxy/vgeo_proxy_triangle_limit.d.ts.map +1 -0
  81. package/src/shade/renderer/geometry/virtual/proxy/vgeo_proxy_triangle_limit.js +25 -0
  82. package/src/shade/renderer/geometry/virtual/proxy/vgeo_to_proxy_mesh.d.ts +31 -0
  83. package/src/shade/renderer/geometry/virtual/proxy/vgeo_to_proxy_mesh.d.ts.map +1 -0
  84. package/src/shade/renderer/geometry/virtual/proxy/vgeo_to_proxy_mesh.js +343 -0
  85. package/src/shade/renderer/geometry/virtual/runtime/VirtualGeometry.d.ts +15 -0
  86. package/src/shade/renderer/geometry/virtual/runtime/VirtualGeometry.d.ts.map +1 -1
  87. package/src/shade/renderer/geometry/virtual/runtime/VirtualGeometry.js +38 -0
  88. package/src/shade/renderer/geometry/virtual/runtime/vgeo_meshlet_metadata_write.d.ts +17 -0
  89. package/src/shade/renderer/geometry/virtual/runtime/vgeo_meshlet_metadata_write.d.ts.map +1 -0
  90. package/src/shade/renderer/geometry/virtual/runtime/vgeo_meshlet_metadata_write.js +29 -0
  91. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.d.ts.map +1 -1
  92. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.js +7 -13
  93. package/src/shade/renderer/geometry/virtual/sdf/VGeoSDFProxies.d.ts +28 -59
  94. package/src/shade/renderer/geometry/virtual/sdf/VGeoSDFProxies.d.ts.map +1 -1
  95. package/src/shade/renderer/geometry/virtual/sdf/VGeoSDFProxies.js +84 -789
  96. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts.map +1 -1
  97. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.js +1 -8
  98. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.d.ts +7 -6
  99. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.d.ts.map +1 -1
  100. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.js +12 -11
  101. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/chunk_mesh_brick_instance.d.ts +2 -2
  102. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/chunk_mesh_brick_instance.js +2 -2
  103. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts +1 -4
  104. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts.map +1 -1
  105. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.js +1 -10
  106. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_surface.d.ts +5 -15
  107. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_surface.d.ts.map +1 -1
  108. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_surface.js +6 -34
  109. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.d.ts.map +1 -1
  110. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.js +1 -7
  111. package/src/shade/renderer/hiz/HZBFootprint.d.ts +14 -0
  112. package/src/shade/renderer/hiz/HZBFootprint.d.ts.map +1 -0
  113. package/src/shade/renderer/hiz/HZBFootprint.js +16 -0
  114. package/src/shade/renderer/hiz/HZB_FOOTPRINT_X.d.ts +10 -0
  115. package/src/shade/renderer/hiz/HZB_FOOTPRINT_X.d.ts.map +1 -0
  116. package/src/shade/renderer/hiz/HZB_FOOTPRINT_X.js +11 -0
  117. package/src/shade/renderer/hiz/HZB_FOOTPRINT_Y.d.ts +10 -0
  118. package/src/shade/renderer/hiz/HZB_FOOTPRINT_Y.d.ts.map +1 -0
  119. package/src/shade/renderer/hiz/HZB_FOOTPRINT_Y.js +11 -0
  120. package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts +0 -7
  121. package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts.map +1 -1
  122. package/src/shade/renderer/hiz/HierarchicalZBuffer.js +405 -350
  123. package/src/shade/renderer/hiz/chunk_hzb_footprint_x.d.ts +12 -0
  124. package/src/shade/renderer/hiz/chunk_hzb_footprint_x.d.ts.map +1 -0
  125. package/src/shade/renderer/hiz/chunk_hzb_footprint_x.js +27 -0
  126. package/src/shade/renderer/hiz/chunk_hzb_footprint_y.d.ts +12 -0
  127. package/src/shade/renderer/hiz/chunk_hzb_footprint_y.d.ts.map +1 -0
  128. package/src/shade/renderer/hiz/chunk_hzb_footprint_y.js +27 -0
  129. package/src/shade/renderer/hiz/hzb_footprint_texels.d.ts +14 -0
  130. package/src/shade/renderer/hiz/hzb_footprint_texels.d.ts.map +1 -0
  131. package/src/shade/renderer/hiz/hzb_footprint_texels.js +34 -0
  132. package/src/shade/renderer/hiz/shader_hzb_reduce_any.d.ts +0 -5
  133. package/src/shade/renderer/hiz/shader_hzb_reduce_any.d.ts.map +1 -1
  134. package/src/shade/renderer/hiz/shader_hzb_reduce_any.js +37 -57
  135. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0.d.ts +11 -0
  136. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0.d.ts.map +1 -0
  137. package/src/shade/renderer/hiz/{shader_hzb_reduce_mip0_clip.js → shader_hzb_reduce_mip0.js} +120 -111
  138. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  139. package/src/shade/renderer/material/GPUMaterialManager.d.ts +11 -1
  140. package/src/shade/renderer/material/GPUMaterialManager.d.ts.map +1 -1
  141. package/src/shade/renderer/material/GPUMaterialManager.js +16 -8
  142. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts +12 -12
  143. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts.map +1 -1
  144. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.js +17 -118
  145. package/src/shade/renderer/postprocess/gtao/GTAO.d.ts +7 -3
  146. package/src/shade/renderer/postprocess/gtao/GTAO.d.ts.map +1 -1
  147. package/src/shade/renderer/postprocess/gtao/GTAO.js +26 -13
  148. package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.d.ts +14 -0
  149. package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.d.ts.map +1 -1
  150. package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.js +196 -68
  151. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao.d.ts.map +1 -1
  152. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao.js +42 -28
  153. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao_with_irradiance.d.ts.map +1 -1
  154. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao_with_irradiance.js +429 -414
  155. package/src/shade/renderer/postprocess/gtao/shader_gtao_spatial_denoise.d.ts +16 -0
  156. package/src/shade/renderer/postprocess/gtao/shader_gtao_spatial_denoise.d.ts.map +1 -0
  157. package/src/shade/renderer/postprocess/gtao/shader_gtao_spatial_denoise.js +156 -0
  158. package/src/shade/renderer/scene/GPUSceneContext.d.ts +2 -2
  159. package/src/shade/renderer/scene/GPUSceneContext.js +5 -5
  160. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.d.ts.map +1 -1
  161. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.js +7 -2
  162. package/src/shade/renderer/water/GPUWaterRenderer.d.ts +0 -6
  163. package/src/shade/renderer/water/GPUWaterRenderer.d.ts.map +1 -1
  164. package/src/shade/renderer/water/GPUWaterRenderer.js +12 -17
  165. package/src/shade/renderer/water/medium/graph_water_meshlets.d.ts +35 -0
  166. package/src/shade/renderer/water/medium/graph_water_meshlets.d.ts.map +1 -0
  167. package/src/shade/renderer/water/medium/graph_water_meshlets.js +89 -0
  168. package/src/shade/renderer/water/query/GPUWaterQueries.d.ts +1 -1
  169. package/src/shade/renderer/water/shader_water.d.ts.map +1 -1
  170. package/src/shade/renderer/water/shader_water.js +22 -13
  171. package/src/shade/renderer/water/shader_water_avboit.d.ts.map +1 -1
  172. package/src/shade/renderer/water/shader_water_avboit.js +7 -7
  173. package/src/shade/renderer/water/surface/graph_water_tessellation.d.ts +1 -1
  174. package/src/shade/renderer/water/surface/graph_water_tessellation.js +1 -1
  175. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +9 -1
  176. package/editor/REWORK_PLAN_2026_08.md +0 -692
  177. package/src/CODEC_LAYOUT_VERIFICATION.md +0 -419
  178. package/src/CORRECTNESS_HUNT_2026_07_24.md +0 -142
  179. package/src/DUPLICATION_AUDIT_2026_08_07/00_core_reuse_surface.md +0 -738
  180. package/src/DUPLICATION_AUDIT_2026_08_07/A_core_math_geom.md +0 -79
  181. package/src/DUPLICATION_AUDIT_2026_08_07/B_core_data.md +0 -93
  182. package/src/DUPLICATION_AUDIT_2026_08_07/C_gfx_render.md +0 -80
  183. package/src/DUPLICATION_AUDIT_2026_08_07/D_gfx_features.md +0 -164
  184. package/src/DUPLICATION_AUDIT_2026_08_07/E_physics.md +0 -69
  185. package/src/DUPLICATION_AUDIT_2026_08_07/F_sound.md +0 -110
  186. package/src/DUPLICATION_AUDIT_2026_08_07/G_net_save.md +0 -129
  187. package/src/DUPLICATION_AUDIT_2026_08_07/H_engine_misc.md +0 -72
  188. package/src/DUPLICATION_AUDIT_2026_08_07/I_generation_view.md +0 -74
  189. package/src/DUPLICATION_AUDIT_2026_08_07/K_crosscutting.md +0 -169
  190. package/src/DUPLICATION_AUDIT_2026_08_07.md +0 -254
  191. package/src/DUPLICATION_AUDIT_REMEDIATION.md +0 -250
  192. package/src/DUPLICATION_REVIEW_2026_10_24.md +0 -1072
  193. package/src/REVIEW_2026_08_06/adv-core.md +0 -511
  194. package/src/REVIEW_2026_08_06/adv-gfx.md +0 -480
  195. package/src/REVIEW_2026_08_06/adv-view.md +0 -446
  196. package/src/REVIEW_2026_08_06/api-core.md +0 -296
  197. package/src/REVIEW_2026_08_06/api-engine.md +0 -195
  198. package/src/REVIEW_2026_08_06/bvh-phys-sound-net.md +0 -507
  199. package/src/REVIEW_2026_08_06/core-binary.md +0 -348
  200. package/src/REVIEW_2026_08_06/core-collection.md +0 -376
  201. package/src/REVIEW_2026_08_06/core-math.md +0 -221
  202. package/src/REVIEW_2026_08_06/core-process.md +0 -266
  203. package/src/REVIEW_2026_08_06/core-science.md +0 -223
  204. package/src/REVIEW_2026_08_06/engine-ai-nav.md +0 -279
  205. package/src/REVIEW_2026_08_06/engine-animation.md +0 -167
  206. package/src/REVIEW_2026_08_06/engine-asset.md +0 -166
  207. package/src/REVIEW_2026_08_06/engine-ecs.md +0 -920
  208. package/src/REVIEW_2026_08_06/engine-input.md +0 -197
  209. package/src/REVIEW_2026_08_06/engine-misc.md +0 -248
  210. package/src/REVIEW_2026_08_06/engine-terrain.md +0 -52
  211. package/src/REVIEW_2026_08_06/generation.md +0 -176
  212. package/src/REVIEW_2026_08_06/geom-2d.md +0 -434
  213. package/src/REVIEW_2026_08_06/geom-3d.md +0 -117
  214. package/src/REVIEW_2026_08_06/gfx-gi.md +0 -143
  215. package/src/REVIEW_2026_08_06/gfx-render.md +0 -740
  216. package/src/REVIEW_2026_08_06/gfx-texture.md +0 -690
  217. package/src/REVIEW_2026_08_06/prior-criticals.md +0 -49
  218. package/src/REVIEW_2026_08_06/prior-highs.md +0 -138
  219. package/src/REVIEW_2026_08_06/test-gaps.md +0 -364
  220. package/src/REVIEW_2026_08_06/view.md +0 -667
  221. package/src/REVIEW_2026_08_06.md +0 -610
  222. package/src/core/binary/lz4/LZ4_HIGH_COMPRESSION_PLAN_2026_09_14.md +0 -542
  223. package/src/core/bvh2/bvh3/NOTES.md +0 -2
  224. package/src/core/bvh8/NOTES.md +0 -63
  225. package/src/core/cache/wtinylfu/benchmark/CacheWTinylfu.bench.md +0 -243
  226. package/src/core/color/COLOR_FIX_PLAN_2026_08_28.md +0 -417
  227. package/src/core/color/COLOR_REVIEW_2026_08_28.md +0 -779
  228. package/src/core/geom/2d/polygon/TRIANGULATION_DESIGN.md +0 -433
  229. package/src/core/geom/3d/atlas/ATLAS_PORT_PLAN.md +0 -486
  230. package/src/core/geom/3d/atlas/BFF_PLAN.md +0 -174
  231. package/src/core/geom/3d/atlas/REVIEW_02.md +0 -392
  232. package/src/core/geom/3d/gjk/GJK_REVIEW_NOTES.md +0 -146
  233. package/src/core/geom/3d/gjk/NOTES.md +0 -8
  234. package/src/core/geom/3d/topology/TOPO_MESH_RETIREMENT_PLAN.md +0 -135
  235. package/src/core/geom/GEOMETRY_REVIEW_002_API_ISSUES.md +0 -381
  236. package/src/core/geom/GEOMETRY_REVIEW_002_NAMING.md +0 -684
  237. package/src/core/geom/MANIFOLD_COMPARISON_2026_08_01.md +0 -554
  238. package/src/core/model/reactive/evaluation/PREDICATE_APPLICABILITY_PROPOSAL.md +0 -446
  239. package/src/engine/REVIEW_2026_07_03.md +0 -211
  240. package/src/engine/asset/loaders/image/IMAGE_CODEC_REVIEW_2026_08_28.md +0 -815
  241. package/src/engine/control/first-person/DESIGN.md +0 -658
  242. package/src/engine/control/first-person/DESIGN_COLLISION.md +0 -382
  243. package/src/engine/control/first-person/DESIGN_EXTENSIONS.md +0 -563
  244. package/src/engine/control/first-person/TODO.md +0 -160
  245. package/src/engine/ecs/ASYNC_SYSTEMS_PROPOSAL.md +0 -531
  246. package/src/engine/ecs/HIERARCHY_REVIEW.md +0 -717
  247. package/src/engine/ecs/async/WORKER_SYSTEM_PLAN.md +0 -262
  248. package/src/engine/ecs/ik/PLAN.md +0 -179
  249. package/src/engine/ecs/storage/binary/SERIALIZATION_COVERAGE.md +0 -272
  250. package/src/engine/ecs/storage/binary/object-v2/DESIGN.md +0 -199
  251. package/src/engine/ecs/transform/TRANSFORM64_MIGRATION_PLAN_2026_09_04.md +0 -521
  252. package/src/engine/graphics/CONTEXT_LOSS_RECOVERY_PLAN.md +0 -463
  253. package/src/engine/graphics/ecs/camera/pp/NOTES.md +0 -48
  254. package/src/engine/graphics/render/frame_graph/CORRECTNESS_FIXES_2026_09_04.md +0 -659
  255. package/src/engine/graphics/render/frame_graph/FRAME_GRAPH_RECOMMENDATIONS.md +0 -235
  256. package/src/engine/graphics/sh3/path_tracer/REVIEW_001_ACTION_PLAN.md +0 -490
  257. package/src/engine/graphics/texture/sampler/compare/FLIP_IMPLEMENTATION_PLAN.md +0 -466
  258. package/src/engine/input/GAMEPAD_API_PROPOSAL.md +0 -717
  259. package/src/engine/input/ecs/ism/ISM_DESIGN.md +0 -1523
  260. package/src/engine/input/ecs/ism/ISM_IMPLEMENTATION_PLAN.md +0 -627
  261. package/src/engine/navigation/mesh/OVERLAPPING_SURFACES_2026_08_25.md +0 -661
  262. package/src/engine/navigation/mesh/PATHFINDING_PLAN.md +0 -229
  263. package/src/engine/network/CONGESTION_CONTROL.md +0 -87
  264. package/src/engine/network/MESSAGE_TRANSPORT.md +0 -364
  265. package/src/engine/network/SMOOTHNESS_DESIGN.md +0 -400
  266. package/src/engine/physics/cloth/MEASUREMENTS.md +0 -5304
  267. package/src/engine/physics/cloth/PLAN.md +0 -3056
  268. package/src/engine/physics/fluid/GPU_FLUID_CASCADES_PROPOSAL_2026_10_16.md +0 -1382
  269. package/src/engine/physics/fluid/REVIEW_02_PLAN.md +0 -243
  270. package/src/engine/physics/fluid/solver/NOTES.md +0 -46
  271. package/src/engine/sound/SOUND_EMITTER_RETIREMENT_PLAN.md +0 -536
  272. package/src/engine/sound/simulation/IMPLEMENTATION_PLAN.md +0 -432
  273. package/src/engine/sound/simulation/PROBE_PLACEMENT.md +0 -250
  274. package/src/engine/sound/simulation/PROBE_TRANSFER_RESEARCH.md +0 -322
  275. package/src/engine/sound/simulation/REFLECTION_IR_PLAN.md +0 -261
  276. package/src/engine/sound/simulation/VISIBILITY_GRAPH_PLAN.md +0 -121
  277. package/src/engine/sound/sopra/IMPLEMENTATION_PLAN.md +0 -999
  278. package/src/format/image/avif/DECISIONS.md +0 -545
  279. package/src/format/image/jpeg/NOTES.md +0 -3
  280. package/src/format/scene/gltf/MESHOPT_COMPRESSION_PLAN.md +0 -814
  281. package/src/generation/COORDINATES.md +0 -54
  282. package/src/generation/REVIEW_01_ACTION_PLAN.md +0 -628
  283. package/src/generation/foliage/ANNUAL_SHOOTS_BRIEF_2026_10_25.md +0 -220
  284. package/src/generation/foliage/CROWN_FILLING_BRIEF_2026_10_26.md +0 -172
  285. package/src/generation/foliage/CROWN_FULLNESS_BRIEF_2026_10_26.md +0 -86
  286. package/src/generation/foliage/FOLIAGE_DESIGN.md +0 -547
  287. package/src/generation/foliage/FOLIAGE_PLAN.md +0 -198
  288. package/src/generation/foliage/LEAF_ARCHITECTURE_BRIEF_2026_10_26.md +0 -158
  289. package/src/generation/foliage/LOD_REWORK_BRIEF_2026_10_27.md +0 -521
  290. package/src/generation/foliage/REAL_TIME_TREES_BRIEF_2026_10_27.md +0 -86
  291. package/src/generation/foliage/SELF_ORGANIZING_FRAMEWORK_BRIEF_2026_10_26.md +0 -347
  292. package/src/generation/foliage/SHOOT_SYSTEMS_ALL_SPECIES_BRIEF_2026_10_26.md +0 -248
  293. package/src/generation/foliage/SPEEDTREE_COMPARISON_2026_10_24.md +0 -694
  294. package/src/generation/foliage/TWO_FRONT_ENDS_BRIEF_2026_10_28.md +0 -552
  295. package/src/shade/RENDERER_CONTRACT.md +0 -697
  296. package/src/shade/SHADER_F16_PROPOSAL_2026_10_24.md +0 -263
  297. package/src/shade/device/IMMEDIATE_DATA_PLAN_2026_08_28.md +0 -569
  298. package/src/shade/device/PASS_ENCODER_PROPOSAL_2026_08_31.md +0 -755
  299. package/src/shade/device/mock/EXTENSION_PROPOSAL_2026_08_22.md +0 -458
  300. package/src/shade/device/mock/IMPLEMENTATION_PLAN.md +0 -255
  301. package/src/shade/device/mock/REVIEW_2026_08_22.md +0 -527
  302. package/src/shade/device/timing/GPU_PROFILER_PROPOSAL_2026_08_28.md +0 -894
  303. package/src/shade/device/timing/profile/SGPT_FORMAT.md +0 -345
  304. package/src/shade/renderer/NOTES.md +0 -78
  305. package/src/shade/renderer/PLAN.md +0 -8
  306. package/src/shade/renderer/animation/ADDITIVE_BLENDING_PLAN.md +0 -145
  307. package/src/shade/renderer/animation/SKINNING_FRAME_PLAN.md +0 -357
  308. package/src/shade/renderer/atmosphere/NOTES.md +0 -6
  309. package/src/shade/renderer/camera/orbital/MomentumAxis.d.ts +0 -76
  310. package/src/shade/renderer/camera/orbital/MomentumAxis.d.ts.map +0 -1
  311. package/src/shade/renderer/camera/orbital/MomentumAxis.js +0 -164
  312. package/src/shade/renderer/camera/orbital/OrbitalCameraConfig.d.ts +0 -45
  313. package/src/shade/renderer/camera/orbital/OrbitalCameraConfig.d.ts.map +0 -1
  314. package/src/shade/renderer/camera/orbital/OrbitalCameraConfig.js +0 -66
  315. package/src/shade/renderer/camera/orbital/OrbitalCameraInput.d.ts +0 -98
  316. package/src/shade/renderer/camera/orbital/OrbitalCameraInput.d.ts.map +0 -1
  317. package/src/shade/renderer/camera/orbital/OrbitalCameraInput.js +0 -184
  318. package/src/shade/renderer/camera/orbital/OrbitalCameraRig.d.ts +0 -175
  319. package/src/shade/renderer/camera/orbital/OrbitalCameraRig.d.ts.map +0 -1
  320. package/src/shade/renderer/camera/orbital/OrbitalCameraRig.js +0 -411
  321. package/src/shade/renderer/camera/orbital/README.md +0 -81
  322. package/src/shade/renderer/camera/orbital/index.d.ts +0 -6
  323. package/src/shade/renderer/camera/orbital/index.d.ts.map +0 -1
  324. package/src/shade/renderer/camera/orbital/index.js +0 -9
  325. package/src/shade/renderer/extension/RENDER_EXTENSION_DESIGN.md +0 -964
  326. package/src/shade/renderer/fluid/GPU_FLUID_OCCUPANCY_AND_SOTA_REVIEW_2026_10_26.md +0 -361
  327. package/src/shade/renderer/geometry/bvh/BLAS_REFIT_BATCH_PLAN.md +0 -619
  328. package/src/shade/renderer/geometry/meshlet/COMPACTION_PLAN_2026_09_16.md +0 -674
  329. package/src/shade/renderer/geometry/sdf/NOTES.md +0 -6
  330. package/src/shade/renderer/geometry/virtual/ATTRIBUTE_PLAN_2026_09_18.md +0 -540
  331. package/src/shade/renderer/geometry/virtual/MICRON_COMPARISON_2026_08_20.md +0 -305
  332. package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +0 -913
  333. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md +0 -787
  334. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_PLAN.md +0 -481
  335. package/src/shade/renderer/geometry/virtual/VK_LOD_CLUSTERS_COMPARISON_2026_08_20.md +0 -301
  336. package/src/shade/renderer/geometry/virtual/WEDGE_PLAN_2026_09_18.md +0 -448
  337. package/src/shade/renderer/geometry/virtual/runtime/CULL_PLAN_2026_09_16.md +0 -385
  338. package/src/shade/renderer/geometry/virtual/runtime/CUT_PLAN_2026_09_14.md +0 -656
  339. package/src/shade/renderer/geometry/virtual/runtime/REVIEW_LEDGER.md +0 -308
  340. package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN.md +0 -657
  341. package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN_2026_09_14.md +0 -551
  342. package/src/shade/renderer/geometry/virtual/runtime/SHADOW_PLAN_2026_09_28.md +0 -529
  343. package/src/shade/renderer/geometry/virtual/sdf/VGEO_SDF.d.ts +0 -47
  344. package/src/shade/renderer/geometry/virtual/sdf/VGEO_SDF.d.ts.map +0 -1
  345. package/src/shade/renderer/geometry/virtual/sdf/VGEO_SDF.js +0 -80
  346. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_collect.d.ts +0 -16
  347. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_collect.d.ts.map +0 -1
  348. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_collect.js +0 -67
  349. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_decode.d.ts +0 -17
  350. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_decode.d.ts.map +0 -1
  351. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_decode.js +0 -145
  352. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_traverse.d.ts +0 -12
  353. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_traverse.d.ts.map +0 -1
  354. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_traverse.js +0 -105
  355. package/src/shade/renderer/global_illumination/lpv/NOTES.md +0 -3
  356. package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_CLIPMAP_PROPOSAL_2026_10_27.md +0 -4306
  357. package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_RAYS_VARIABLE_AND_SORTED_SCOPE_2026_10_30.md +0 -410
  358. package/src/shade/renderer/global_illumination/sharc/NOTES.md +0 -8
  359. package/src/shade/renderer/gpu_primitive/bvh/GPU_BVH_BUILD_PLAN.md +0 -1369
  360. package/src/shade/renderer/gpu_primitive/bvh/PHASE1_HANDOVER.md +0 -202
  361. package/src/shade/renderer/gpu_primitive/indirect/NOTES.md +0 -94
  362. package/src/shade/renderer/hiz/NOTES.md +0 -3
  363. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.d.ts +0 -8
  364. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.d.ts.map +0 -1
  365. package/src/shade/renderer/light/cluster/NOTES.md +0 -11
  366. package/src/shade/renderer/light/cluster/PROPOSAL_ALGORITHM_2026_10_16.md +0 -524
  367. package/src/shade/renderer/light/cluster/PROPOSAL_HIERARCHICAL_2026_10_16.md +0 -1101
  368. package/src/shade/renderer/material/resident/shader_resident_texture_means.d.ts +0 -24
  369. package/src/shade/renderer/material/resident/shader_resident_texture_means.d.ts.map +0 -1
  370. package/src/shade/renderer/material/resident/shader_resident_texture_means.js +0 -120
  371. package/src/shade/renderer/particles/DESIGN.md +0 -1011
  372. package/src/shade/renderer/particles/EMITTER_GLOBAL_STATE_PLAN_2026_10_29.md +0 -280
  373. package/src/shade/renderer/particles/ORIENTATION_PLAN_2026_10_15.md +0 -453
  374. package/src/shade/renderer/path_tracer/PATH_TRACED_MEDIA_PLAN_2026_10_02.md +0 -520
  375. package/src/shade/renderer/postprocess/bloom/NOTES.md +0 -6
  376. package/src/shade/renderer/postprocess/fsr/NOTES.md +0 -4
  377. package/src/shade/renderer/postprocess/gtao/NOTES.md +0 -6
  378. package/src/shade/renderer/postprocess/nss/NOTES.md +0 -332
  379. package/src/shade/renderer/postprocess/perspective/NOTES.md +0 -3
  380. package/src/shade/renderer/postprocess/sscs/NOTES.md +0 -5
  381. package/src/shade/renderer/postprocess/ssr/NOTES.md +0 -14
  382. package/src/shade/renderer/postprocess/ssr/REPROJECTION_PROPOSAL.md +0 -286
  383. package/src/shade/renderer/postprocess/ssr/prefilter/NOTES.md +0 -39
  384. package/src/shade/renderer/postprocess/taa/NOTES.md +0 -11
  385. package/src/shade/renderer/postprocess/upscale-dither/NOTES.md +0 -78
  386. package/src/shade/renderer/postprocess/vrs/NOTES.md +0 -4
  387. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_LEDGER.md +0 -1218
  388. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_PLAN_2026_09_03.md +0 -600
  389. package/src/shade/renderer/restir/reservoir/NOTES.md +0 -6
  390. package/src/shade/renderer/shader/chunk/atmosphere/NOTES.md +0 -6
  391. package/src/shade/renderer/shader/chunk/brdf/FRESNEL_BRIEF_2026_10_16.md +0 -391
  392. package/src/shade/renderer/shader/chunk/color/tonemap/ACES_TONE_MAPPING_BRIEF_2026_10_17.md +0 -153
  393. package/src/shade/renderer/shader/chunk/color/tonemap/HDR_TONE_MAPPING_FORMS_BRIEF_2026_10_16.md +0 -322
  394. package/src/shade/renderer/shader/chunk/color/tonemap/SDR_TONE_MAPPING_BRIEF_2026_10_16.md +0 -247
  395. package/src/shade/renderer/shader/chunk/fsr/NOTES.md +0 -3
  396. package/src/shade/renderer/shader/chunk/fsr/spd/NOTES.md +0 -4
  397. package/src/shade/renderer/shader/chunk/geometry/ray/NOTES.md +0 -4
  398. package/src/shade/renderer/shader/chunk/material/MULTISCATTER.md +0 -159
  399. package/src/shade/renderer/shader/chunk/mboit/NOTES.md +0 -38
  400. package/src/shade/renderer/shader/chunk/spherical_harmonics/l1i/NOTES.md +0 -3
  401. package/src/shade/renderer/shader/chunk/spherical_harmonics/sampling/NOTES.md +0 -8
  402. package/src/shade/renderer/shader/compiler/SHADER_SWITCH_PROPOSAL_2026_10_16.md +0 -927
  403. package/src/shade/renderer/shadow/contact/FINDINGS.md +0 -413
  404. package/src/shade/renderer/shadow/map/csm/NOTES.md +0 -5
  405. package/src/shade/renderer/shadow/ray/NOTES.md +0 -31
  406. package/src/shade/renderer/shadow/sdf/NOTES.md +0 -9
  407. package/src/shade/renderer/shadow/sdf/brixelizer/NOTES.md +0 -5
  408. package/src/shade/renderer/shadow/sdf/cascade/NOTES.md +0 -9
  409. package/src/shade/renderer/shadow/vsm/NOTES.md +0 -12
  410. package/src/shade/renderer/shadow/vsm/PLAN.md +0 -442
  411. package/src/shade/renderer/shadow/vsm/REVIEW_LEDGER.md +0 -43
  412. package/src/shade/renderer/texture/COMPRESSED_TEXTURE_PLAN_2026_09_01.md +0 -2444
  413. package/src/shade/renderer/texture/mipmaps/NOTES.md +0 -3
  414. package/src/shade/renderer/volumetrics/NOTES.md +0 -1329
  415. package/src/shade/renderer/volumetrics/VOLUMETRIC_NOISE_PLAN_2026_10_15.md +0 -336
  416. package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts +0 -23
  417. package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts.map +0 -1
  418. package/src/shade/renderer/water/medium/GPUWaterBodies.js +0 -75
  419. package/src/shade/wgsl/NOTES.md +0 -4
@@ -1,529 +0,0 @@
1
- # Virtual geometry casts shadows — the plan
2
-
3
- **Closed 2026-09-29.** Everything below is done, withdrawn or measured moot, and §5 is what it
4
- deliberately leaves out.
5
-
6
- **2026-09-28, revision 3**, after an adversarial pass over revision 2 (three reviewers against the
7
- code; §6 is the ledger, and the sections it changed say so). **S1–S5 are done, S6 is withdrawn, and shadow
8
- views cut at twice the camera's τ; §7 is what each step measured.** It changes no decision in
9
- `VIRTUAL_GEOMETRY_DESIGN.md` — §4 already pins one cut per view and withdrew the claim that shadow
10
- views could reuse the camera's — and it takes `RUNTIME_PLAN.md` §9's "no shadow-view cuts" out of
11
- the list of things v1 does not do.
12
-
13
- ## Decided
14
-
15
- 1. **One cut per view, and a shadow view is a view.** Cascades, spots and a point light's cube faces
16
- go through the same seam, the same per-view state and the same rules as the main view. Nothing in
17
- the VG runtime knows which kind of view it is cutting for.
18
- 2. **The engine owns the VG frame.** It is a core feature; a host does not open, read or close it.
19
- 3. **VG shadows are not optional.** How is open; whether is not.
20
- 4. **Proxies are not built now, and the architecture leaves room for them.** Nanite-style proxies
21
- drawn in place of a cut for some views, behind a switch, may come later; §2.6 is where they would
22
- go and what this work must not close off.
23
- 5. **The order is §4's.**
24
- 6. **Shadow views cut at twice the camera's τ** *(after S4)*. The shadow context sets
25
- `tau_scale` to 2 on every view it owns (`SHADOWMAP_VGEO_TAU_SCALE`); §7.2 is the price.
26
- 7. **No batched cascade cut** *(after S5)*. Cascades are boxes fitted to separate slices of the view
27
- frustum — misaligned, overlapping only at the blend margins — so one descent over all three
28
- shares no traversal work, only dispatches. §3 item 2 and S6 are withdrawn.
29
-
30
- Revision 1 argued from a measured match between a cascade's texel and the screen pixel at the far end
31
- of its slice. That argument is withdrawn: what a view's cut needs is a function of what projects into
32
- *that* view, and §1 says why the camera's view says nothing about it.
33
-
34
- ## 0. What is true today — measured
35
-
36
- ### 0.1 Virtual geometry receives shadows and never casts them
37
-
38
- Measured on `playground/vgeo_scene` with the textured Zorah rebuild (1,012 containers, 12,597
39
- instances, the scene's own sun), headless Chrome on the 4090, 1556×592. Each condition is a
40
- 32-frame average after TAA has settled; the column is the fraction of pixels that go more than 16
41
- luma darker when shadows are switched on.
42
-
43
- | view | only VG in view | plus two ordinary `BoxGeometry` casters |
44
- |---|---:|---:|
45
- | authored | 0 | 0.110% |
46
- | oblique | 0 | 0.076% |
47
- | plan | 0 | 0.106% |
48
-
49
- Two identical captures differ by the same 0 at that threshold, so the VG column is noise. The boxes'
50
- shadows land on VG ground and are the only thing in the on/off difference; the dome and the
51
- greenhouse, under a sun 52° above the horizon, leave nothing on it.
52
-
53
- Before the far-plane fix of 2026-09-28 the page hid every shadow, not only VG's: `look()` set
54
- `camera.far = distance × 2000`, and `DirectionalLightShadowmap.update` takes `camera.far` as its
55
- shadow distance, so from the authored viewpoint the nearest cascade spanned 0–5.8 km. The page now
56
- fits the far plane to the world's bounding sphere, capped at twenty times the orbit distance, and
57
- the numbers above are from that version.
58
-
59
- ### 0.2 Why, in the code
60
-
61
- Three things each stop it, in the order a frame meets them:
62
-
63
- - **The frame is not open yet.** `Renderer.render` calls `sm.select_for_draw` / `sm.draw` before it
64
- creates the `Shading` graph, and the VG frame is opened by the host's `RenderExtension` at
65
- `FramePhase.FrameStart` — on that graph. Every shadow view is recorded while
66
- `runtime.feedback === -1`, and `graph_vgeo_expand_if_enabled` returns at its first check.
67
- - **The graph is not the frame's.** Each shadow map records on a `new FrameGraph("shadows")` of its
68
- own, and the feedback buffer is a transient of the `Shading` graph. Were the frame open, the
69
- second check (`runtime.frame_graph !== graph`, REVIEW_LEDGER M13) would skip anyway, and must:
70
- the handle names a resource that graph has never heard of.
71
- - **An orthographic view has no projection the cut can take.** `VGeoViewCut.set_projection` reads
72
- `view.camera.camera.fov`; a cascade's `OrthographicCamera` has none, and `vg_project` divides by a
73
- distance that means nothing to a parallel projection.
74
-
75
- Nothing else stops it. The seam already sits in both rasterization paths shadow views use —
76
- `graph_rasterize_scene` for cascades and spots, `graph_rasterize_scene_fast` for a point light's cube faces — and
77
- every `GPUViewContext`, shadow views included, already owns a `VGeoViewCut`. The cascade fit's
78
- caster range (`shader_csm_caster_range`) already reads every `meshes` row's world bounds, VG rows
79
- among them, so the near planes already reach VG casters.
80
-
81
- ### 0.3 What a cut costs now
82
-
83
- `GPUProfileSession` at `TIMING`, 90 frames, medians, the same page, `max_iterations` 16 (escalated),
84
- `descent_steps` 24:
85
-
86
- | | authored | oblique |
87
- |---|---:|---:|
88
- | GPU frame, sum of spans | 2,903 µs | 2,931 µs |
89
- | `VGeo cut traverse`, 48 dispatches | 984 µs | 814 µs |
90
- | `VGeo cut queue prepare`, 48 dispatches | 60 µs | 60 µs |
91
- | collect + seed, 3 each | 29 µs | 35 µs |
92
- | per site: traverse | 815 / 120 / 46 µs | 630 / 98 / 70 µs |
93
-
94
- The main view records **three cut sites** — the per-opaque-bucket seam of
95
- `graph_rasterize_partial_opaque` plus the one in `graph_rasterize_remaining_opaque` — and the work
96
- is almost all in the first. A site with nothing to do still pays its ladder: sixteen traverse
97
- dispatches at ~3 µs, sixteen prepares, collect, seed, the scan and the remainder, about **80 µs**.
98
- The cut is already the largest line in the frame, over a third of it.
99
-
100
- ## 1. Why a shadow view needs its own cut
101
-
102
- **A cut is a function of what projects into the view it is drawn into, and a caster's projection into
103
- a shadow map has nothing to do with its projection onto the screen.** A small object high above the
104
- observer is a speck to the camera — or outside its frustum altogether — and throws a large shadow on
105
- the ground in front of it. That shadow needs the caster's fine levels; the camera's cut holds its
106
- coarse ones where the camera sees it at all, and nothing where the camera does not. No property of
107
- the camera's view bounds what a shadow view needs, so a shadow view gets its own cut exactly as the
108
- main view does, with its own projection and its own threshold in the units of its own render target.
109
-
110
- It follows that **a shadow view is a streaming client in its own right.** Its demand is not a subset
111
- of the camera's — the speck in the sky wants pages the camera will never ask for — so its wants reach
112
- residency on the same terms as the main view's (§2.4), and residency, which is already scene-wide,
113
- arbitrates between them.
114
-
115
- **This is what the references do.** Unreal's Nanite renders every virtual-shadow-map view with its
116
- own culling and LOD, against that view's resolution; Bevy's meshlet renderer runs a full cull and
117
- raster per shadow view, and its LOD test reads the bound view's own projection and viewport. Neither
118
- reuses the camera's cut. The one reference that does, NVIDIA's `vk_lod_clusters`, traces its shadow
119
- rays against the camera's cut and documents the cost: off-screen instances cast from a coarser level,
120
- and their shadows change when they come into view.
121
-
122
- **Self-shadowing is measured, not argued.** A surface that is both receiver and caster is drawn at
123
- the camera's level in the G-buffer and at the shadow view's level in the map. The receiver bias in
124
- `chunk_sample_shadowmap_atlas_for_light` is a normal offset and a slope-scaled depth offset in texels,
125
- over a 5×5 filter; whether that absorbs the mismatch is S3's measurement (§4), and a new bias term is
126
- added only if it fails.
127
-
128
- ## 2. What changes
129
-
130
- ### 2.1 The engine owns the VG frame *(rev 3)*
131
-
132
- The renderer opens the frame for a scene with virtual geometry enabled, **before the shadow block and
133
- outside its `if`** — so a frame with shadows off still opens and closes — and schedules the readback
134
- and closes the frame inside the `Shading` graph's recording, after the last phase and before
135
- `encodeGraph`, in a `finally` so a throwing phase cannot leave it open. `VGeoFrameStart` and
136
- `VGeoFrameEnd` are deleted from **all three** hosts — `vgeo_scene`, `vgeo_runtime` and `caldera`. That
137
- closes REVIEW_LEDGER U1: `GPUSceneContext` constructs the runtime, residency and `VGeoFeedbackLoop`
138
- today, and nothing in the engine drives them. The frame is opened *for* the view being rendered, which
139
- takes slot 0 (§2.4); that is the frame's own view, not a kind of view.
140
-
141
- The feedback buffer stops being a graph transient. It becomes a buffer the runtime owns
142
- (`STORAGE | COPY_SRC | COPY_DST`), re-made when its layout grows, and **imported** into each graph that
143
- cuts via `FrameGraph.import_resource` — a write to an imported resource marks the pass side-effecting,
144
- so nothing is culled. The seam asks the runtime for the graph's handle (`feedback_in(graph)`), and the
145
- first graph of a frame to import it records a pass that **clears the counter and touch words in-stream**
146
- (`clearBuffer`, no upload; the want records need no clearing). M13's hazard — a handle from one graph
147
- named in another — then cannot be written. Ordering between graphs needs nothing: `encodeGraph`
148
- compiles and executes each graph into the frame's one encoder as it is handed over, and there is one
149
- submit per render.
150
-
151
- `GraphicsEngine.render_to_sampler` renders eight times per call and each is a frame, which is the
152
- right semantics — each is a render of a view — and it advances the residency's protect clock eight
153
- times; noted, not changed.
154
-
155
- ### 2.2 The cut takes an orthographic projection *(rev 3)*
156
-
157
- **One derivation, from the projection matrix the view actually draws with** — no camera-type branch in
158
- the seam, and no widths the old setter could not take:
159
-
160
- k = ½ · max(width · |P[0]|, height · |P[5]|) w = −P[11]
161
-
162
- For this engine's infinite reverse-Z perspective that is `height / (2 tan(fov/2))` and `w = 1`; for its
163
- reverse-Z orthographic, `height / (top − bottom)` on the finer axis and `w = 0`. Anything else asserts.
164
- `vg_project` divides by `w · d + (1 − w)` and tests **that** for `≤ 0`, so an orthographic camera whose
165
- transform sits inside a root sphere — a cascade's centre usually does — is not taken as an eye inside
166
- the sphere.
167
-
168
- The weight lives **in the instance record** (`VGEO_INSTANCE_OFFSET.DISTANCE_WEIGHT`), not only in the
169
- params, so the traversal never reads a per-view uniform for it. The collect's scale term is
170
- `k · σmax / (w · σmin + 1 − w)`: σmax/σmin in perspective, as today, and σmax alone orthographically,
171
- where no distance cancels a scale. `vgeo_select_cut` gets the same term.
172
-
173
- **The proof has to discriminate the scale term, not just the formula** *(rev 3)*: the oracle has no
174
- instance transform, so agreement alone would check the GPU against the same formula it was given. S1
175
- adds a direct collect check at a uniform scale of 7 (σmax = 7 against σmax/σmin = 1) and a metamorphic
176
- one — an instance scaled by s in an orthographic view cuts exactly like an unscaled one at s times the
177
- extent — plus the orthographic frustum held against `construct_frustum_planes`.
178
-
179
- Bevy's `lod_error_is_imperceptible` has this shape, and its orthographic arm is where its bug was: PR
180
- #19318 fixed a metric that had left shadows visibly faceted.
181
-
182
- ### 2.3 τ is the runtime's, scaled per view *(rev 3)*
183
-
184
- Revision 2 made τ an absolute per-view value. That splits the one quality lever — the playgrounds'
185
- slider sets `runtime.tau`, and an absolute shadow τ of 2 becomes *finer* than the main view the moment
186
- the slider goes to 4 — and the reference it cited is relative. So a view carries **`tau_scale`**,
187
- default 1, and cuts at `runtime.tau × tau_scale`. Whoever owns a view may set it; nothing in the runtime
188
- knows what kind of view it is. S4 measures before anything but 1 is set.
189
-
190
- **Shadow views default to 2** *(after S4)*: the shadow context sets it on every view it owns
191
- (`SHADOWMAP_VGEO_TAU_SCALE`), and the runtime still knows nothing of what kind of view it cuts for.
192
- At 2 the cascades draw 39% of the clusters they do at 1, and false light against a fine reference
193
- rises 2.3–4.7× (§7.2).
194
-
195
- ### 2.4 Feedback is per view *(rev 3)*
196
-
197
- Each view that cuts in a frame takes a **slot**. Per slot, the layout carries a header — the want
198
- counter and the stats block — and a want slice; the touch bits after the headers stay shared, since a
199
- page any view draws from is a page in use. The view's params hold its offsets (`stats_base`,
200
- `want_count_offset`, `want_base`, `want_capacity`), so this is parameters, not bindings: every kernel
201
- that writes feedback already binds `params`, and the traverse is at its tenth storage buffer with no
202
- eleventh. `want_capacity` keeps its meaning — pages one frame may ask for across every view — and is
203
- divided evenly among the slots, so the readback does not grow with the view count.
204
-
205
- - **Slots.** The frame's own view is slot 0. Other views take the next slot the first time they cut
206
- in the frame. The layout is sized to a **high-water mark** of slots used, like `max_iterations`: a
207
- view beyond it shares the last slot for that frame, and the next frame has room. The mark is capped,
208
- so a frame that draws many point lights for the first time — first draws bypass the shadow budget —
209
- cannot grow the buffer without bound.
210
- - **Wants: one stamp tag per (frame, slot).** The stamp was taken *before* the capacity check, so a
211
- full shadow slice burned the stamp of every page it could not write and the main view, recording
212
- later, could not ask for it that frame; and the recorded priority was the first writer's. With a
213
- tag per view, each view dedups its own wants and records them in its own slice; a page two views want
214
- appears twice, and `VGeoResidency` already collapses the second (the page is pending or resident by
215
- then).
216
- - **Priority is error over the view's own τ**, so a want at twice its view's threshold ranks the same
217
- whether that view counts screen pixels or texels. Eviction stays LRU over touches; refusals per slot
218
- are what S4 measures.
219
- - **Escalation is per view.** `max_iterations` moves from the runtime onto `VGeoViewCut`, starting
220
- from the runtime's and raised from that view's own slot. One far cascade or one cube face of a point light that
221
- needed more wavefronts must not double every site's ladder for the life of the scene. The readback
222
- closes over the slot → view table with the layout, since it lands frames later and the set of views
223
- changes.
224
- - **Stats** are per slot; `VGeoFeedbackLoop.stats` is slot 0's, which is what the panels show, and each
225
- view's last reading is on its `VGeoViewCut`.
226
-
227
- ### 2.5 What does not change
228
-
229
- The seam's call sites, the record type, the HZB meshlet filter, the draw, the shadow atlas, the
230
- cascade fit and the caster range.
231
-
232
- **Cached local maps** *(rev 3)*. A spot or point map drawn once is kept until `shadowmap_update_score`
233
- re-selects it. Its first draw is forced at creation, when little but the roots is resident, and between
234
- redraws it touches nothing, so the LRU evicts exactly the pages only it needs. Left alone it would keep
235
- a root-level cut indefinitely. So a map whose own views' last reading was **clamped**, and under which
236
- pages have installed since, is re-selected like an invalid one. That keys on the map's own incomplete
237
- cut rather than on every LOD change, which keeps it clear of an invalidation storm when pages stream in.
238
-
239
- ### 2.6 Room for proxies — not built
240
-
241
- A proxy is a fixed, cheap stand-in for a whole geometry — Nanite's fallback mesh — drawn **instead of**
242
- a cut for a view that does not justify one. If it comes, it comes as a per-view choice, and this work
243
- keeps the two places it would need open:
244
-
245
- - **The choice is made per (instance, view).** Today that is the seam, which already receives the view:
246
- it would either cut or append the instances' proxy meshlets.
247
- - **A proxy's meshlets would hang off the VG geometry row, never off `VirtualGeometry.meshlets`.**
248
- `meshlets.count` is zero for a virtual geometry, and that is what keeps static expansion from
249
- drawing a VG instance in every view; filling it with a proxy would draw the proxy *and* the cut
250
- everywhere.
251
-
252
- That room is for **raster** views *(rev 3)*: ray-traced shadows read `geometries.blas`, built from
253
- static meshlets, so a proxy for RT needs the BLAS builder to take an input from the VG row as well.
254
-
255
- ## 3. Cost *(rev 3)*
256
-
257
- **Sites** *(before S5)*. A view cuts once per active opaque bucket in phase one plus once in phase two (standard
258
- path: cascades and spots, ~3 sites each), and once per active bucket in the fast path (a point light's cube faces:
259
- two active buckets make 12 sites for a cube light and 8 for a tetrahedral one). S5 made it two per
260
- standard view and one per face (§7.3). An empty site is ~80 µs at
261
- `max_iterations` 16 and ~225 µs at the ceiling of 64.
262
-
263
- **Work.** The three cascades are 1740² + 2 × 1440² ≈ 7.3 Mpx against the 0.92 Mpx main view that §0.3
264
- measured, and a cut is a function of pixels, so "a cascade costs what the camera's cut does" is a floor
265
- rather than an estimate. S4 measures it, with the raster cost of the extra clusters beside it.
266
-
267
- 1. **One cut per view** — CUT_PLAN §7 6b, decided and not done, and **extended to the fast path**:
268
- filter instances once, cut once, sort the meshlets by bucket, then loop the buckets for raster
269
- alone. That is two sites per standard view and one per cube face.
270
- 2. **One ladder for the cascades — withdrawn.** It would have run one descent over all three
271
- cascades. They are not one frustum and not nested ones: each is a box fitted to its own slice of
272
- the view frustum, misaligned with the others and overlapping only at the blend margins, so a shared
273
- descent shares no traversal work. What it would share is dispatch overhead — 0.2–0.3 ms after S5
274
- (§7.4) — for a change inside the traverse.
275
-
276
- ## 4. Order, and what each step leaves proved *(rev 3)*
277
-
278
- **Correctness first, on by default, then cost.** VG shadows are not optional, so they land for every
279
- scene with virtual geometry with no switch to turn them off. Each step is red-then-green against a spec
280
- or a scripted headless measurement.
281
-
282
- - **S1 — orthographic projection (§2.2).** Agreement spec: orthographic views against the oracle; the
283
- σmax collect check; the metamorphic scale check; the orthographic frustum. `VGeoViewCut` derives k and
284
- w from the projection matrix, with its own spec. The seam takes it from the view's camera.
285
- - **S2 — the engine owns the frame; feedback is per view (§2.1, §2.3, §2.4).** This is the step that
286
- turns shadow cuts on. Emulator tier: two views in one run write their own stats and want slices, a
287
- full slice does not stop another view asking for the same page, and priority is over τ. Recording
288
- tier: the seam on a second graph within an open frame records its cut against the same imported
289
- buffer, and only the first graph clears it. Headless: all three playgrounds still stream.
290
- - **S3 — the shadows are right.** Proved against the tree before S2, where every one of these is red.
291
- TAA off and single frames, the shadow term measured against a reference with shadow views at a
292
- quarter of the τ: false-shadow and false-lit pixels on light-facing receivers.
293
- - a caster outside the camera frustum (behind the camera) casts into view;
294
- - spot, cube and tetrahedral point lights over VG, including a cached map converging (§2.5);
295
- - a yaw sweep over a still scene: frame-to-frame XOR of the shadow mask, VG against an ordinary
296
- mesh of the same size, since unstabilized cascades refit — and change `k` — every frame;
297
- - per slot: `QUEUE_DROPPED`, `INSTANCES_DROPPED`, `MESHLETS_DROPPED` all zero, `QUEUE_REMAINING`
298
- zero after escalation.
299
- - **S4 — measure.** Per view (the cut's scope and the `VGeoViewCut` label carry the view's name): cut
300
- time, drawn clusters, wants, refusals; spot, cube and tetra lights; a frame spawning N point lights;
301
- shadow views at `tau_scale` 1, 2, 4 — a default other than 1 only if S3's checks pass at it.
302
- - **S5 — one cut per view (6b), standard and fast paths.**
303
- - **S6 — one ladder for the cascades: withdrawn** (§3 item 2).
304
-
305
- ## 5. Not in this
306
-
307
- - **Proxies** (§2.6), beyond keeping the room.
308
- - **Non-opaque VG casters.** An alpha-tested VG instance draws nothing in the main view either
309
- (design §9); the shadow path's alpha-tested bucket inherits that.
310
- - **Ray-traced and SDF shadows** (`shadow/ray`, `shadow/sdf`). VG × RT is design §9's future work.
311
- - **Screen-space contact shadows** already see VG — they trace the depth buffer.
312
- - **Releasing VG on scene teardown** was a defect of its own. It was fixed separately:
313
- `GPUSceneContext.destroy()` releases what `enable_virtual_geometry` built (7a9e14fea, and
314
- REVIEW_LEDGER U1).
315
-
316
- ## 6. Adversarial review of revision 2 — the ledger
317
-
318
- Three reviewers, one each on the frame and feedback, the kernel, and the shadow path and cost; each
319
- finding checked against the code before it was taken.
320
-
321
- | # | finding | disposition |
322
- |---|---|---|
323
- | 1 | want stamp taken before the capacity check: a full shadow slice silences the main view's want for that page; first writer's priority wins (all three) | §2.4: stamp tag per (frame, slot); priority over the view's τ |
324
- | 2 | absolute per-view τ splits the one lever and inverts at a raised slider | §2.3: `tau_scale` |
325
- | 3 | one `max_iterations` for the frame lets one hungry view double every site forever | §2.4: per view |
326
- | 4 | S2 already turns shadows on, so S3 cannot be red after it; the software device cannot prove a readback | §4: S3 proved against the pre-S2 tree; slot writes proved at the emulator tier |
327
- | 5 | Caldera is a third host; a second `begin_frame` mid-render would split `frame_tag` | §2.1 |
328
- | 6 | the oracle has no instance transform, so agreement cannot catch a wrong scale term; the seam still reads `fov` | §2.2, S1: σmax and metamorphic checks; projection from the matrix |
329
- | 7 | `vg_project`'s eye-inside test must be on the denominator — a cascade's centre sits inside root spheres | §2.2 |
330
- | 8 | "no change inside the traverse" for a batched cut is false; cascades and local maps are in separate graphs; the multi-view collect moves the proxy choice | §3.2 scoped to cascades and called a traverse change; §2.6 restated per (instance, view) |
331
- | 9 | a point light's cube faces cut per bucket in the fast path, first draws bypass the budget, cascades are 7.3 Mpx | §3 |
332
- | 10 | 6b as written covers only the standard path | §3.1: and the fast path |
333
- | 11 | a cached local map keeps its first, root-level cut | §2.5: re-select a map whose cut was clamped once pages have installed |
334
- | 12 | the S3 probe cannot see speckle through TAA, never looks for leaks, sweeps the wrong τ, and misses off-frustum casters, local lights and stability | §4 S3 |
335
- | 13 | clearing by `writeBuffer` uploads the whole buffer, and the usage lacks `COPY_DST` | §2.1: in-stream clear of the counter and touch words |
336
- | 14 | `end_frame` needs a `finally`; the open has to sit outside the shadows-enabled `if` | §2.1 |
337
- | 15 | per-view attribution impossible: identical scope names, `VGeoViewCut` labelled before its view is | §4 S4 |
338
- | 16 | proxies as described are raster-only | §2.6 |
339
- | 17 | unstabilized cascades change `k` every frame, so shadow LOD may pop under camera motion | **measured, no action (§7.6)**: `k` moves and the cut follows, but the picture does not flicker beyond what a frozen-`k` cut — an ordinary mesh, to the map — does. `k` is not quantized |
340
- | 18 | a point light's cube faces render at a fixed 512² each and are then remapped into the light's octahedral atlas slot, 128² by default and sized by the resolution policy — so each face cuts for texels the slot does not keep | **withdrawn, not a VG issue.** It is the remap's resolution match between cube faces and the octahedral encoding, and a VG knob would only paper over it |
341
- | 19 | the orthographic far plane is finite and skipped: casters behind a slice's receivers refine and want pages | **withdrawn, measured (§7.5)**: a cascade's far plane is the far corner of its view slice in light space, and testing it culls nothing worth having |
342
- | 20 | the exact orthographic factor is the largest singular value of the projected 2×3 | declined: σmax is a conservative bound and exact for uniform scale |
343
-
344
- ## 7. What the steps found
345
-
346
- All of it is Zorah (§0.1's page, scene and viewpoints) on the 4090 in headless Chrome, at 1556×592.
347
- Costs come from `GPUProfileSession` at `TIMING`, as medians over 48 frames. **This GPU runs in two
348
- modes about 2× apart.** It switches between launches and now and then mid-run, so absolute numbers do
349
- not compare across runs, or even across the rows of one table. Every cost claimed here is a
350
- difference within a pair taken in one Chrome, and only pairs taken in one mode are quoted.
351
-
352
- ### 7.1 Shadows are cast (S3)
353
-
354
- The table is §0.1's: the fraction of pixels that go more than 16 luma darker when shadows are switched
355
- on, only VG in view, against the tree before S2.
356
-
357
- | view | before | after |
358
- |---|---:|---:|
359
- | authored | 0 | 3.99% |
360
- | oblique | 0 | 0.31% |
361
- | plan | 0 | 1.95% |
362
-
363
- - **A caster behind the camera casts into view.** From inside the throne room, 39% of the pixels
364
- darken.
365
- - **Local lights, with the sun off.** Pixels darkened: 4.5% under a cube point light, 4.1% under a
366
- tetrahedral one, 0.22% under a spot. In every slot `QUEUE_DROPPED`, `INSTANCES_DROPPED`,
367
- `MESHLETS_DROPPED` and `QUEUE_REMAINING` are zero.
368
- - **Cached maps (§2.5).** Twelve point lights spawned in one frame reach zero clamped maps within
369
- 14 s, with nothing else fetching.
370
- - **At rest, nothing moves.** Across consecutive settled frames, no pixel changes in the authored view.
371
- In the plan view, fewer than one pixel in 100,000 changes by more than 4 luma, and that holds with
372
- shadows on and off alike.
373
- - **Under camera motion, VG shadows flicker no more than ordinary geometry's** (§7.6).
374
- - **Self-shadowing (§1) needs no new bias.** The main view's `tau_scale` was swept 1 → 8, which makes
375
- the G-buffer up to 8× coarser than the maps.
376
- - Authored: the fraction of darker pixels holds at 6.98–6.99%, with no isolated acne.
377
- - Oblique: 0.79% at 1 and 0.89% at 2, then 1.17% at 4 and 2.29% at 8.
378
- - Isolated dark pixels do not grow anywhere in the sweep.
379
-
380
- So the existing bias absorbs about a 2× mismatch, and the default adds none beyond what the two
381
- projections make. Measured the other way round, against shadow views at τ × ¼, shadow views at τ × 1
382
- leave 0.003–0.012% false shadow and 0.006–0.154% false light.
383
-
384
- ### 7.2 What shadows cost, and τ (S4)
385
-
386
- **The first S4 run found a defect in S2**, fixed in 3f9cce3cc. The first frames were laid out for one
387
- slot, so the cascades shared slot 0 with the frame's view. The sum of their remainders escalated the
388
- main view to 32 wavefronts, for good. Now the floor is two slots, a view past the layout never shares
389
- slot 0, and a shared slot escalates nobody.
390
-
391
- **Where the time goes**, read within single frames so the modes cannot move it. The frame is the
392
- authored view with shadows on, after S5:
393
-
394
- - The cut is half of it: 2.67 of 5.27 ms.
395
- - Of that cut, the three cascades take 1.88 ms and the camera 0.74 ms.
396
- - Cascade 0 alone is 0.82 ms and draws 113k clusters, against the camera's 32k.
397
-
398
- Per-site ladders are the smaller part (§7.4).
399
-
400
- **`tau_scale` on the cascades** (`shadow_tau`, TAA and auto exposure off; the reference is every
401
- cascade at τ × ¼):
402
-
403
- | view | false light ×1 | ×2 | ×4 |
404
- |---|---:|---:|---:|
405
- | authored | 0.006% | 0.028% | 0.040% |
406
- | oblique | 0.154% | 0.349% | 0.454% |
407
- | plan | 0.153% | 0.400% | 0.530% |
408
-
409
- ×2 draws 36% of ×1's clusters in cascade 0 and cuts the depth raster by about 60%. Across the three
410
- views it also multiplies the false light by 2.3–4.7×, and false light is sunlit ground under a caster.
411
- **Shadow views default to 2** — Alex's call on these numbers. The clusters and raster are the saving,
412
- and the false light is the price.
413
-
414
- ### 7.3 One cut per view (S5)
415
-
416
- - **Standard path, phase one.** The HZB filter appends every opaque bucket to one positive set, as it
417
- already did the maybe set. That set is expanded, cut and meshlet-filtered once, then sorted by bucket
418
- and drawn a bucket at a time (`graph_meshlets_rasterize_by_bucket`).
419
- - **Fast path.** The opaque buckets are not neighbours in its bucket-sorted meshes, so it gathers them
420
- into one collection first (`shader_gather_bucket`). Each bucket lands after the gathered buckets
421
- below it, read off their headers, with no atomics.
422
- - **Both paths, and phase two,** sort only when there is more than one bucket to separate.
423
-
424
- Paired, before (3f9cce3cc) and after in one Chrome, ABBA twice. The ranges are over the pairs taken in
425
- the fast mode, and the slow mode's pairs save proportionally the same.
426
-
427
- | | cut sites | frame saved | cut saved |
428
- |---|---:|---:|---:|
429
- | authored, shadows on | 12 → 8 | 1.0–1.1 ms of 6.2 | 0.9–1.2 ms |
430
- | oblique, shadows on | 12 → 8 | 0.6–1.6 ms of 10.2 | 1.2–1.7 ms |
431
- | authored, shadows off | 3 → 2 | 0.3–1.0 ms | 0.2–0.7 ms |
432
- | oblique, shadows off | 3 → 2 | 0.02–0.13 ms | 0.09–0.11 ms |
433
-
434
- **Nothing else changed:**
435
-
436
- - The two trees' pictures differ by nothing at any threshold, in all four conditions.
437
- - Every view draws the same number of clusters.
438
- - Every counter of every face of a cube and a tetrahedral point light is the same, from drawn to
439
- culled-by-LOD.
440
-
441
- The second bucket's site disappears and the first barely grows, because nearly all of what the second
442
- site cost was its ladder. Cascade 0 goes from [807, 328, 71] µs to [820, 69] µs.
443
-
444
- ### 7.4 S6 is withdrawn
445
-
446
- After S5 each view is left with **one ladder per phase**, and an empty ladder costs 40–75 µs. That
447
- ladder is all a batched cascade cut could have shared: the cascades are disjoint boxes over separate
448
- slices of the view frustum (§3 item 2), so the descent's own work — where the cascades' cost is —
449
- would be the same.
450
-
451
- What would reduce that work is fewer clusters per cascade. Two levers exist:
452
-
453
- - `tau_scale` — now 2 on shadow views (§7.2).
454
- - Starting each view's descent from its previous frontier. `VGeoViewCut` already keeps a frontier
455
- (CUT_PLAN 6c), and the seam does not read it.
456
-
457
- Neither is proposed here.
458
-
459
- ### 7.5 A cascade's far plane culls nothing worth having
460
-
461
- Ledger 19 said the untested orthographic far plane lets casters behind a cascade's receivers refine.
462
- That was argued, not measured. A cascade's far plane is `extents.z1`, the far corner of its view
463
- slice in light space, so anything behind it sits below everything the slice covers.
464
-
465
- Measured with shadow views at `tau_scale` 2. The collect's fifth plane was swapped for orthographic
466
- views only, one Chrome launch per arm: the near plane as shipped, no plane, the far plane, then the
467
- near plane again.
468
-
469
- - **The near plane culls nothing either.** The caster range pushes it behind every caster by
470
- construction, so "no plane" matches "near" on every counter.
471
- - **So the far arm is the far plane's whole effect.** Across the three viewpoints and three cascades,
472
- every counter — drawn, items, pages, clusters tested, frustum culls — is identical in all four arms,
473
- but one: oblique cascade 0, which draws 65 clusters, draws 14.
474
- - **The pictures are identical to the pixel** in every arm.
475
-
476
- ### 7.6 Cascade refits do not make VG shadows flicker (ledger 17)
477
-
478
- Cascades are not stabilized, so every camera move refits them and changes each cascade's `k`. The
479
- question was whether VG's shadow cuts re-selecting on every refit make the shadows flicker more than
480
- an ordinary caster's.
481
-
482
- **Method.** A moving view camera would put its own motion into every frame-to-frame difference, so
483
- the view stands still and only the camera the cascades are fitted from moves. It yaws
484
- A·sin(2πk/48), one step per rendered frame, so every change in the picture comes from the refit.
485
-
486
- - **Two arms over the identical sequence.** In one, VG's shadow cuts are live. In the other, every
487
- shadow view's `k` is frozen at the unwobbled fit: `set_projection` becomes a no-op. A cut with
488
- `k` frozen is an ordinary mesh as far as the map is concerned, so live minus frozen is VG's own
489
- flicker. Freezing `k` is also exactly the remedy this ledger row named.
490
- - **Measurement conditions.** TAA and auto exposure are off, and shadow views are at `tau_scale` 2.
491
- Each frame is captured inside the render wrapper, so the two arms' phases line up. Metrics cover the
492
- central 80% of the screen. A second live arm reproduced the first to the pixel.
493
-
494
- Per frame, the fraction of pixels that change by more than 8 luma:
495
-
496
- | view | ±3°: live | ±3°: frozen | ±10°: live | ±10°: frozen |
497
- |---|---:|---:|---:|---:|
498
- | authored | 0 | 0 | 0.0002% | 0 |
499
- | oblique | 0.033% | 0.033% | — | — |
500
- | plan | 0.009% | 0.006% | 0.018% | 0.013% |
501
-
502
- Over the ±3° sweep, cascade 0's `k` runs 67–78 and it draws 35k–47k clusters, so the cut does follow
503
- the refit — and the picture barely notices. At ±10°, about 1.3° a frame and roughly a fast turn, VG's
504
- own share is 0.005% of the screen per frame.
505
-
506
- **The metric can see VG flicker when there is some.** As a positive control, with no wobble, every
507
- shadow view's `tau_scale` alternated between 2 and 16 each frame: cascade 0 drew 3.7k and 42k clusters
508
- in turn. That flickers 0.17% of the screen per frame in the authored view and 0.33% in the plan view.
509
-
510
- ## References
511
-
512
- Read 2026-09-28.
513
-
514
- - Epic, *Virtual Shadow Maps in Unreal Engine* — per-view Nanite rendering, one pass per light
515
- class, 17 directional clipmap levels, page caching and invalidation.
516
- https://dev.epicgames.com/documentation/en-us/unreal-engine/virtual-shadow-maps-in-unreal-engine
517
- - Epic, *Hardware Ray Tracing* — ray-traced shadows trace the Nanite fallback mesh.
518
- https://dev.epicgames.com/documentation/en-us/unreal-engine/hardware-ray-tracing-in-unreal-engine
519
- - Bevy, `crates/bevy_pbr/src/meshlet/cull_shared.wesl` (`lod_error_is_imperceptible`) and
520
- `visibility_buffer_raster_node.rs` (one full cull and raster per shadow view).
521
- https://github.com/bevyengine/bevy/tree/main/crates/bevy_pbr/src/meshlet
522
- - Bevy PR #19318, the orthographic LOD metric fix. https://github.com/bevyengine/bevy/pull/19318
523
- - jms55, *Virtual Geometry in Bevy 0.15* — "render all views at once" listed as shadow performance
524
- work. https://jms55.github.io/posts/2024-11-14-virtual-geometry-bevy-0-15/
525
- - NVIDIA `vk_lod_clusters` — one traversal from the camera, shadow rays traced against it; and
526
- `vk_tessellated_clusters`' README on off-screen shadows changing when instances become visible.
527
- https://github.com/nvpro-samples/vk_lod_clusters · https://github.com/nvpro-samples/vk_tessellated_clusters
528
- - **Not found in any primary source:** whether and how Nanite's shadow views issue streaming
529
- requests. §2.4 does not rest on it.
@@ -1,47 +0,0 @@
1
- /**
2
- * Words of the payload's header for `job_count` jobs: their counters, then the cut's own counters from its feedback
3
- * slot (`VGEO_CUT_STAT`), which say whether the cut left a subtree undescended.
4
- *
5
- * @param {number} job_count
6
- * @returns {number}
7
- */
8
- export function vgeo_sdf_header_words(job_count: number): number;
9
- /**
10
- * The extraction's settings, an immediate of each of its passes.
11
- * @type {WebGPUStruct}
12
- */
13
- export const VGEO_SDF_SETTINGS: WebGPUStruct;
14
- /**
15
- * A job the frame cuts, as the host writes it: the vgeo geometry row it cuts and the projection constant that puts its
16
- * error against a τ of 1, `1 / (½ voxel)` in the geometry's own units. A job the frame writes out (`decode` 1) has its
17
- * room in the payload: `vertex_count` vertices from `vertex_base` and `triangle_count` triangles from `triangle_base`,
18
- * what its last complete cut held.
19
- * @type {WebGPUStruct}
20
- */
21
- export const VGEO_SDF_JOB_STRUCT: WebGPUStruct;
22
- /**
23
- * A job's counters, atomics the frame's passes add into: the clusters its cut took, the stand-ins among them (a
24
- * cluster taken for a page not resident or a group held), those dropped past the cluster list, the vertices and
25
- * triangles of its listed clusters, and of those, what the decode wrote into its room.
26
- */
27
- export const VGEO_SDF_COUNTER: Readonly<{
28
- TAKEN: 0;
29
- STAND_INS: 1;
30
- DROPPED: 2;
31
- VERTICES: 3;
32
- TRIANGLES: 4;
33
- VERTICES_WRITTEN: 5;
34
- TRIANGLES_WRITTEN: 6;
35
- }>;
36
- /**
37
- * Words of a job's counters.
38
- * @type {number}
39
- */
40
- export const VGEO_SDF_COUNTER_WORDS: number;
41
- /**
42
- * Words of a cluster list entry: its job, then its meshlet row.
43
- * @type {number}
44
- */
45
- export const VGEO_SDF_CLUSTER_WORDS: number;
46
- import { WebGPUStruct } from "../../../shader/type/WebGPUStruct.js";
47
- //# sourceMappingURL=VGEO_SDF.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"VGEO_SDF.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/geometry/virtual/sdf/VGEO_SDF.js"],"names":[],"mappings":"AAsEA;;;;;;GAMG;AACH,iDAHW,MAAM,GACJ,MAAM,CAIlB;AArED;;;GAGG;AACH,gCAFU,YAAY,CAWA;AAEtB;;;;;;GAMG;AACH,kCAFU,YAAY,CAWL;AAEjB;;;;GAIG;AACH;;;;;;;;GAQG;AAEH;;;GAGG;AACH,qCAFU,MAAM,CAEwB;AAExC;;;GAGG;AACH,qCAFU,MAAM,CAEwB;6BAnEX,sCAAsC"}
@@ -1,80 +0,0 @@
1
- import { f32, u32 } from "../../../shader/type/WEBGPU_WGSL_PRIMITIVE_TYPES.js";
2
- import { WebGPUStruct } from "../../../shader/type/WebGPUStruct.js";
3
- import { VGEO_CUT_STAT_COUNT } from "../runtime/layout/VGEO_CUT_STAT_COUNT.js";
4
-
5
- /*
6
- The layouts of a vgeo geometry's proxy extraction (PROBE_CLIPMAP_PROPOSAL_2026_10_27.md §7.8, as S8 built it): each geometry
7
- whose cache is asked for is cut whole at half its cache's level-0 voxel, its cut's clusters listed, and, once a cut
8
- stands nothing in, its vertices and triangles written out and read back for the proxy the mesh bake bakes.
9
- */
10
-
11
- /**
12
- * The extraction's settings, an immediate of each of its passes.
13
- * @type {WebGPUStruct}
14
- */
15
- export const VGEO_SDF_SETTINGS = WebGPUStruct.from({
16
- /** jobs the frame cuts */
17
- job_count: u32,
18
- /** entries the cluster list holds */
19
- cluster_capacity: u32,
20
- /** the word of the payload its vertices start at, 3 floats a vertex */
21
- vertex_offset: u32,
22
- /** the word of the payload its triangles start at, 3 indices a triangle */
23
- index_offset: u32,
24
- }, "VGeoSDFSettings");
25
-
26
- /**
27
- * A job the frame cuts, as the host writes it: the vgeo geometry row it cuts and the projection constant that puts its
28
- * error against a τ of 1, `1 / (½ voxel)` in the geometry's own units. A job the frame writes out (`decode` 1) has its
29
- * room in the payload: `vertex_count` vertices from `vertex_base` and `triangle_count` triangles from `triangle_base`,
30
- * what its last complete cut held.
31
- * @type {WebGPUStruct}
32
- */
33
- export const VGEO_SDF_JOB_STRUCT = WebGPUStruct.from({
34
- vg_row: u32,
35
- projection_k: f32,
36
- decode: u32,
37
- vertex_base: u32,
38
- triangle_base: u32,
39
- vertex_count: u32,
40
- triangle_count: u32,
41
- pad: u32,
42
- }, "VGeoSDFJob");
43
-
44
- /**
45
- * A job's counters, atomics the frame's passes add into: the clusters its cut took, the stand-ins among them (a
46
- * cluster taken for a page not resident or a group held), those dropped past the cluster list, the vertices and
47
- * triangles of its listed clusters, and of those, what the decode wrote into its room.
48
- */
49
- export const VGEO_SDF_COUNTER = Object.freeze({
50
- TAKEN: 0,
51
- STAND_INS: 1,
52
- DROPPED: 2,
53
- VERTICES: 3,
54
- TRIANGLES: 4,
55
- VERTICES_WRITTEN: 5,
56
- TRIANGLES_WRITTEN: 6,
57
- });
58
-
59
- /**
60
- * Words of a job's counters.
61
- * @type {number}
62
- */
63
- export const VGEO_SDF_COUNTER_WORDS = 8;
64
-
65
- /**
66
- * Words of a cluster list entry: its job, then its meshlet row.
67
- * @type {number}
68
- */
69
- export const VGEO_SDF_CLUSTER_WORDS = 2;
70
-
71
- /**
72
- * Words of the payload's header for `job_count` jobs: their counters, then the cut's own counters from its feedback
73
- * slot (`VGEO_CUT_STAT`), which say whether the cut left a subtree undescended.
74
- *
75
- * @param {number} job_count
76
- * @returns {number}
77
- */
78
- export function vgeo_sdf_header_words(job_count) {
79
- return job_count * VGEO_SDF_COUNTER_WORDS + VGEO_CUT_STAT_COUNT;
80
- }