@woosh/meep-engine 3.59.0 → 3.60.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (326) hide show
  1. package/package.json +1 -1
  2. package/src/shade/playground/probe_clipmap/index.html +12 -0
  3. package/src/shade/playground/probe_clipmap/main.d.ts.map +1 -1
  4. package/src/shade/playground/probe_clipmap/main.js +5 -0
  5. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  6. package/src/shade/renderer/Renderer.js +4 -6
  7. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts +63 -1
  8. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts.map +1 -1
  9. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.js +228 -16
  10. package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.d.ts +27 -4
  11. package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.d.ts.map +1 -1
  12. package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.js +526 -23
  13. package/src/shade/renderer/geometry/sdf/cards/MeshCardCaptureMode.d.ts +11 -0
  14. package/src/shade/renderer/geometry/sdf/cards/MeshCardCaptureMode.d.ts.map +1 -0
  15. package/src/shade/renderer/geometry/sdf/cards/MeshCardCaptureMode.js +21 -0
  16. package/src/shade/renderer/geometry/sdf/cards/mesh_cards_raster_pass_descriptor.d.ts +65 -0
  17. package/src/shade/renderer/geometry/sdf/cards/mesh_cards_raster_pass_descriptor.d.ts.map +1 -0
  18. package/src/shade/renderer/geometry/sdf/cards/mesh_cards_raster_pass_descriptor.js +376 -0
  19. package/src/shade/renderer/geometry/sdf/cards/shader_mesh_cards_capture.d.ts +7 -3
  20. package/src/shade/renderer/geometry/sdf/cards/shader_mesh_cards_capture.d.ts.map +1 -1
  21. package/src/shade/renderer/geometry/sdf/cards/shader_mesh_cards_capture.js +70 -6
  22. package/src/shade/renderer/geometry/sdf/cards/shader_mesh_cards_raster_args.d.ts +20 -0
  23. package/src/shade/renderer/geometry/sdf/cards/shader_mesh_cards_raster_args.d.ts.map +1 -0
  24. package/src/shade/renderer/geometry/sdf/cards/shader_mesh_cards_raster_args.js +80 -0
  25. package/src/shade/renderer/geometry/sdf/cards/shader_mesh_cards_raster_scatter.d.ts +18 -0
  26. package/src/shade/renderer/geometry/sdf/cards/shader_mesh_cards_raster_scatter.d.ts.map +1 -0
  27. package/src/shade/renderer/geometry/sdf/cards/shader_mesh_cards_raster_scatter.js +113 -0
  28. package/src/shade/renderer/geometry/virtual/sdf/VGEO_SDF.d.ts +47 -0
  29. package/src/shade/renderer/geometry/virtual/sdf/VGEO_SDF.d.ts.map +1 -0
  30. package/src/shade/renderer/geometry/virtual/sdf/VGEO_SDF.js +80 -0
  31. package/src/shade/renderer/geometry/virtual/sdf/VGeoSDFProxies.d.ts +91 -0
  32. package/src/shade/renderer/geometry/virtual/sdf/VGeoSDFProxies.d.ts.map +1 -0
  33. package/src/shade/renderer/geometry/virtual/sdf/VGeoSDFProxies.js +902 -0
  34. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_collect.d.ts +16 -0
  35. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_collect.d.ts.map +1 -0
  36. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_collect.js +67 -0
  37. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_decode.d.ts +17 -0
  38. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_decode.d.ts.map +1 -0
  39. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_decode.js +145 -0
  40. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_traverse.d.ts +12 -0
  41. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_traverse.d.ts.map +1 -0
  42. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_traverse.js +105 -0
  43. package/src/shade/renderer/global_illumination/probe/octahedral/shader_lpv_build_gbuffer.d.ts.map +1 -1
  44. package/src/shade/renderer/global_illumination/probe/octahedral/shader_lpv_build_gbuffer.js +2 -1
  45. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts +3 -9
  46. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts.map +1 -1
  47. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.js +13 -31
  48. package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_CLIPMAP_PROPOSAL_2026_10_27.md +764 -48
  49. package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_RAYS_VARIABLE_AND_SORTED_SCOPE_2026_10_30.md +410 -0
  50. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.d.ts +3 -2
  51. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.d.ts.map +1 -1
  52. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.js +4 -3
  53. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.d.ts +41 -17
  54. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.d.ts.map +1 -1
  55. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.js +57 -33
  56. package/src/shade/renderer/global_illumination/probe_clipmap/brick/BRICK_CLIPMAP.d.ts +11 -3
  57. package/src/shade/renderer/global_illumination/probe_clipmap/brick/BRICK_CLIPMAP.d.ts.map +1 -1
  58. package/src/shade/renderer/global_illumination/probe_clipmap/brick/BRICK_CLIPMAP.js +13 -4
  59. package/src/shade/renderer/global_illumination/probe_clipmap/brick/GPUBrickClipmap.d.ts +9 -18
  60. package/src/shade/renderer/global_illumination/probe_clipmap/brick/GPUBrickClipmap.d.ts.map +1 -1
  61. package/src/shade/renderer/global_illumination/probe_clipmap/brick/GPUBrickClipmap.js +8 -10
  62. package/src/shade/renderer/global_illumination/probe_clipmap/brick/chunk_brick_clipmap.d.ts +12 -0
  63. package/src/shade/renderer/global_illumination/probe_clipmap/brick/chunk_brick_clipmap.d.ts.map +1 -1
  64. package/src/shade/renderer/global_illumination/probe_clipmap/brick/chunk_brick_clipmap.js +28 -0
  65. package/src/shade/renderer/global_illumination/probe_clipmap/brick/chunk_brick_clipmap_trace.d.ts +1 -29
  66. package/src/shade/renderer/global_illumination/probe_clipmap/brick/chunk_brick_clipmap_trace.d.ts.map +1 -1
  67. package/src/shade/renderer/global_illumination/probe_clipmap/brick/chunk_brick_clipmap_trace.js +1 -74
  68. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/MESH_BRICK.d.ts +161 -0
  69. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/MESH_BRICK.d.ts.map +1 -0
  70. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/MESH_BRICK.js +219 -0
  71. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/MESH_BRICK_INSTANCE_STRUCT.d.ts +1 -8
  72. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/MESH_BRICK_INSTANCE_STRUCT.d.ts.map +1 -1
  73. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/MESH_BRICK_INSTANCE_STRUCT.js +1 -9
  74. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/MeshSDFBrickProvider.d.ts +40 -12
  75. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/MeshSDFBrickProvider.d.ts.map +1 -1
  76. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/MeshSDFBrickProvider.js +373 -31
  77. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/chunk_mesh_brick_instance.d.ts +2 -1
  78. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/chunk_mesh_brick_instance.d.ts.map +1 -1
  79. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/chunk_mesh_brick_instance.js +2 -1
  80. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/chunk_mesh_brick_texel.d.ts +15 -8
  81. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/chunk_mesh_brick_texel.d.ts.map +1 -1
  82. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/chunk_mesh_brick_texel.js +28 -14
  83. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/chunk_mesh_brick_walk.d.ts +23 -0
  84. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/chunk_mesh_brick_walk.d.ts.map +1 -0
  85. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/chunk_mesh_brick_walk.js +66 -0
  86. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/shader_mesh_brick_batch.d.ts +29 -0
  87. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/shader_mesh_brick_batch.d.ts.map +1 -0
  88. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/shader_mesh_brick_batch.js +210 -0
  89. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/shader_mesh_brick_command.d.ts +29 -0
  90. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/shader_mesh_brick_command.d.ts.map +1 -0
  91. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/shader_mesh_brick_command.js +86 -0
  92. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/shader_mesh_brick_gather.d.ts +41 -0
  93. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/shader_mesh_brick_gather.d.ts.map +1 -0
  94. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/shader_mesh_brick_gather.js +439 -0
  95. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/shader_mesh_brick_resolve.d.ts +23 -0
  96. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/shader_mesh_brick_resolve.d.ts.map +1 -0
  97. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/shader_mesh_brick_resolve.js +135 -0
  98. package/src/shade/renderer/global_illumination/probe_clipmap/brick/shader_brick_clipmap_free.d.ts +3 -1
  99. package/src/shade/renderer/global_illumination/probe_clipmap/brick/shader_brick_clipmap_free.d.ts.map +1 -1
  100. package/src/shade/renderer/global_illumination/probe_clipmap/brick/shader_brick_clipmap_free.js +12 -3
  101. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts +21 -18
  102. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts.map +1 -1
  103. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.js +254 -73
  104. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.d.ts +149 -8
  105. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.d.ts.map +1 -1
  106. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.js +181 -13
  107. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.d.ts +179 -7
  108. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.d.ts.map +1 -1
  109. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.js +412 -31
  110. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_pick.js +4 -4
  111. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.d.ts.map +1 -1
  112. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.js +4 -3
  113. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.d.ts +36 -10
  114. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.d.ts.map +1 -1
  115. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.js +218 -29
  116. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_admit.d.ts +5 -1
  117. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_admit.d.ts.map +1 -1
  118. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_admit.js +24 -14
  119. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_due.d.ts +15 -6
  120. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_due.d.ts.map +1 -1
  121. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_due.js +21 -22
  122. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_guide.d.ts +36 -0
  123. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_guide.d.ts.map +1 -0
  124. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_guide.js +257 -0
  125. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_light.d.ts +18 -7
  126. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_light.d.ts.map +1 -1
  127. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_light.js +157 -51
  128. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_order.d.ts +23 -0
  129. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_order.d.ts.map +1 -0
  130. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_order.js +110 -0
  131. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_project.d.ts +24 -2
  132. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_project.d.ts.map +1 -1
  133. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_project.js +90 -10
  134. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_ray_layout.d.ts +45 -0
  135. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_ray_layout.d.ts.map +1 -0
  136. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_ray_layout.js +205 -0
  137. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_relocate.js +1 -1
  138. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_retire.d.ts +16 -3
  139. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_retire.d.ts.map +1 -1
  140. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_retire.js +55 -10
  141. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_schedule.d.ts +19 -4
  142. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_schedule.d.ts.map +1 -1
  143. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_schedule.js +60 -6
  144. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.d.ts +4 -8
  145. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.d.ts.map +1 -1
  146. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.js +21 -36
  147. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_trace.d.ts +21 -14
  148. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_trace.d.ts.map +1 -1
  149. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_trace.js +100 -30
  150. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_trace_command.d.ts +6 -2
  151. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_trace_command.d.ts.map +1 -1
  152. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_trace_command.js +26 -10
  153. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want_hits.d.ts +3 -2
  154. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want_hits.d.ts.map +1 -1
  155. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want_hits.js +12 -12
  156. package/src/shade/renderer/gpu_primitive/sort/SORT_DIGIT_SETTINGS_STRUCT.d.ts +10 -0
  157. package/src/shade/renderer/gpu_primitive/sort/SORT_DIGIT_SETTINGS_STRUCT.d.ts.map +1 -0
  158. package/src/shade/renderer/gpu_primitive/sort/SORT_DIGIT_SETTINGS_STRUCT.js +21 -0
  159. package/src/shade/renderer/gpu_primitive/sort/SORT_PASS_SETTINGS_STRUCT.d.ts +5 -2
  160. package/src/shade/renderer/gpu_primitive/sort/SORT_PASS_SETTINGS_STRUCT.d.ts.map +1 -1
  161. package/src/shade/renderer/gpu_primitive/sort/SORT_PASS_SETTINGS_STRUCT.js +31 -23
  162. package/src/shade/renderer/gpu_primitive/sort/SORT_SIZE_STRUCT.d.ts +10 -0
  163. package/src/shade/renderer/gpu_primitive/sort/SORT_SIZE_STRUCT.d.ts.map +1 -0
  164. package/src/shade/renderer/gpu_primitive/sort/SORT_SIZE_STRUCT.js +22 -0
  165. package/src/shade/renderer/gpu_primitive/sort/chunk_sort_constants.d.ts +9 -0
  166. package/src/shade/renderer/gpu_primitive/sort/chunk_sort_constants.d.ts.map +1 -0
  167. package/src/shade/renderer/gpu_primitive/sort/chunk_sort_constants.js +18 -0
  168. package/src/shade/renderer/gpu_primitive/sort/chunk_sort_digit.d.ts +8 -3
  169. package/src/shade/renderer/gpu_primitive/sort/chunk_sort_digit.d.ts.map +1 -1
  170. package/src/shade/renderer/gpu_primitive/sort/chunk_sort_digit.js +41 -32
  171. package/src/shade/renderer/gpu_primitive/sort/chunk_sort_histogram_tile.d.ts +31 -0
  172. package/src/shade/renderer/gpu_primitive/sort/chunk_sort_histogram_tile.d.ts.map +1 -0
  173. package/src/shade/renderer/gpu_primitive/sort/chunk_sort_histogram_tile.js +71 -0
  174. package/src/shade/renderer/gpu_primitive/sort/chunk_sort_scatter_tile.d.ts +47 -0
  175. package/src/shade/renderer/gpu_primitive/sort/chunk_sort_scatter_tile.d.ts.map +1 -0
  176. package/src/shade/renderer/gpu_primitive/sort/chunk_sort_scatter_tile.js +210 -0
  177. package/src/shade/renderer/gpu_primitive/sort/defs.d.ts +5 -4
  178. package/src/shade/renderer/gpu_primitive/sort/defs.d.ts.map +1 -1
  179. package/src/shade/renderer/gpu_primitive/sort/defs.js +5 -4
  180. package/src/shade/renderer/gpu_primitive/sort/graph_radix_sort.d.ts +56 -15
  181. package/src/shade/renderer/gpu_primitive/sort/graph_radix_sort.d.ts.map +1 -1
  182. package/src/shade/renderer/gpu_primitive/sort/graph_radix_sort.js +410 -61
  183. package/src/shade/renderer/gpu_primitive/sort/shader_radix_sort_histogram.d.ts +4 -1
  184. package/src/shade/renderer/gpu_primitive/sort/shader_radix_sort_histogram.d.ts.map +1 -1
  185. package/src/shade/renderer/gpu_primitive/sort/shader_radix_sort_histogram.js +16 -42
  186. package/src/shade/renderer/gpu_primitive/sort/shader_radix_sort_histogram_indirect.d.ts +15 -0
  187. package/src/shade/renderer/gpu_primitive/sort/shader_radix_sort_histogram_indirect.d.ts.map +1 -0
  188. package/src/shade/renderer/gpu_primitive/sort/shader_radix_sort_histogram_indirect.js +60 -0
  189. package/src/shade/renderer/gpu_primitive/sort/shader_radix_sort_prepare.d.ts +25 -0
  190. package/src/shade/renderer/gpu_primitive/sort/shader_radix_sort_prepare.d.ts.map +1 -0
  191. package/src/shade/renderer/gpu_primitive/sort/shader_radix_sort_prepare.js +89 -0
  192. package/src/shade/renderer/gpu_primitive/sort/shader_radix_sort_scatter.d.ts +3 -8
  193. package/src/shade/renderer/gpu_primitive/sort/shader_radix_sort_scatter.d.ts.map +1 -1
  194. package/src/shade/renderer/gpu_primitive/sort/shader_radix_sort_scatter.js +28 -160
  195. package/src/shade/renderer/gpu_primitive/sort/shader_radix_sort_scatter_indirect.d.ts +12 -0
  196. package/src/shade/renderer/gpu_primitive/sort/shader_radix_sort_scatter_indirect.d.ts.map +1 -0
  197. package/src/shade/renderer/gpu_primitive/sort/shader_radix_sort_scatter_indirect.js +74 -0
  198. package/src/shade/renderer/gpu_primitive/sort/verify_radix_sort.d.ts +3 -1
  199. package/src/shade/renderer/gpu_primitive/sort/verify_radix_sort.d.ts.map +1 -1
  200. package/src/shade/renderer/gpu_primitive/sort/verify_radix_sort.js +387 -188
  201. package/src/shade/renderer/lightmap/bake/shader_lightmap_viz_to_irradiance_flat.js +1 -1
  202. package/src/shade/renderer/lightmap/bake/shader_lightmap_viz_to_sh.js +1 -1
  203. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts.map +1 -1
  204. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.js +90 -72
  205. package/src/shade/renderer/material/resident/RESIDENT_MATERIAL_TEXTURE_LAYOUT.d.ts +5 -0
  206. package/src/shade/renderer/material/resident/RESIDENT_MATERIAL_TEXTURE_LAYOUT.d.ts.map +1 -1
  207. package/src/shade/renderer/material/resident/RESIDENT_MATERIAL_TEXTURE_LAYOUT.js +9 -0
  208. package/src/shade/renderer/path_tracer/shader/INDIRECT_TEXTURE_FILTER.d.ts +13 -0
  209. package/src/shade/renderer/path_tracer/shader/INDIRECT_TEXTURE_FILTER.d.ts.map +1 -0
  210. package/src/shade/renderer/path_tracer/shader/INDIRECT_TEXTURE_FILTER.js +14 -0
  211. package/src/shade/renderer/path_tracer/shader/IndirectTextureFilter.d.ts +10 -0
  212. package/src/shade/renderer/path_tracer/shader/IndirectTextureFilter.d.ts.map +1 -0
  213. package/src/shade/renderer/path_tracer/shader/IndirectTextureFilter.js +15 -0
  214. package/src/shade/renderer/path_tracer/shader/chunk_indirect_sample_tangent_normal.d.ts +4 -3
  215. package/src/shade/renderer/path_tracer/shader/chunk_indirect_sample_tangent_normal.d.ts.map +1 -1
  216. package/src/shade/renderer/path_tracer/shader/chunk_indirect_sample_tangent_normal.js +6 -5
  217. package/src/shade/renderer/path_tracer/shader/chunk_indirect_sample_texture.d.ts +17 -2
  218. package/src/shade/renderer/path_tracer/shader/chunk_indirect_sample_texture.d.ts.map +1 -1
  219. package/src/shade/renderer/path_tracer/shader/chunk_indirect_sample_texture.js +25 -57
  220. package/src/shade/renderer/path_tracer/shader/chunk_indirect_sample_texture_exact.d.ts +13 -0
  221. package/src/shade/renderer/path_tracer/shader/chunk_indirect_sample_texture_exact.d.ts.map +1 -0
  222. package/src/shade/renderer/path_tracer/shader/chunk_indirect_sample_texture_exact.js +70 -0
  223. package/src/shade/renderer/path_tracer/shader/chunk_indirect_sample_texture_stochastic.d.ts +10 -0
  224. package/src/shade/renderer/path_tracer/shader/chunk_indirect_sample_texture_stochastic.d.ts.map +1 -0
  225. package/src/shade/renderer/path_tracer/shader/chunk_indirect_sample_texture_stochastic.js +59 -0
  226. package/src/shade/renderer/path_tracer/shader/chunk_indirect_texture_load.d.ts +12 -0
  227. package/src/shade/renderer/path_tracer/shader/chunk_indirect_texture_load.d.ts.map +1 -0
  228. package/src/shade/renderer/path_tracer/shader/chunk_indirect_texture_load.js +49 -0
  229. package/src/shade/renderer/path_tracer/shader/chunk_ray_hit_to_opacity.d.ts.map +1 -1
  230. package/src/shade/renderer/path_tracer/shader/chunk_ray_hit_to_opacity.js +1 -0
  231. package/src/shade/renderer/path_tracer/shader/chunk_render_trace_path.d.ts.map +1 -1
  232. package/src/shade/renderer/path_tracer/shader/chunk_render_trace_path.js +1 -0
  233. package/src/shade/renderer/path_tracer/shader/chunk_sample_material.d.ts.map +1 -1
  234. package/src/shade/renderer/path_tracer/shader/chunk_sample_material.js +2 -1
  235. package/src/shade/renderer/path_tracer/shader/chunk_sample_material_data.d.ts +7 -0
  236. package/src/shade/renderer/path_tracer/shader/chunk_sample_material_data.d.ts.map +1 -1
  237. package/src/shade/renderer/path_tracer/shader/chunk_sample_material_data.js +15 -6
  238. package/src/shade/renderer/scene/GPUSceneContext.d.ts +7 -1
  239. package/src/shade/renderer/scene/GPUSceneContext.d.ts.map +1 -1
  240. package/src/shade/renderer/scene/GPUSceneContext.js +26 -1
  241. package/src/shade/renderer/shader/chunk/bvh/chunk_bvh_query_aabb_cooperative.d.ts +7 -7
  242. package/src/shade/renderer/shader/chunk/bvh/chunk_bvh_query_aabb_cooperative.d.ts.map +1 -1
  243. package/src/shade/renderer/shader/chunk/bvh/chunk_bvh_query_aabb_cooperative.js +262 -294
  244. package/src/shade/renderer/shader/chunk/filter/MULTISCALE_MEAN_STRUCT.d.ts +9 -0
  245. package/src/shade/renderer/shader/chunk/filter/MULTISCALE_MEAN_STRUCT.d.ts.map +1 -0
  246. package/src/shade/renderer/shader/chunk/filter/MULTISCALE_MEAN_STRUCT.js +21 -0
  247. package/src/shade/renderer/shader/chunk/filter/chunk_multiscale_mean.d.ts +43 -0
  248. package/src/shade/renderer/shader/chunk/filter/chunk_multiscale_mean.d.ts.map +1 -0
  249. package/src/shade/renderer/shader/chunk/filter/chunk_multiscale_mean.js +87 -0
  250. package/src/shade/renderer/shader/chunk/noise/r/chunk_r2_canonical.d.ts +7 -1
  251. package/src/shade/renderer/shader/chunk/noise/r/chunk_r2_canonical.d.ts.map +1 -1
  252. package/src/shade/renderer/shader/chunk/noise/r/chunk_r2_canonical.js +1 -1
  253. package/src/shade/renderer/shader/chunk/subgroup/chunk_subgroup_reserve_workgroup.d.ts +21 -0
  254. package/src/shade/renderer/shader/chunk/subgroup/chunk_subgroup_reserve_workgroup.d.ts.map +1 -0
  255. package/src/shade/renderer/shader/chunk/subgroup/chunk_subgroup_reserve_workgroup.js +42 -0
  256. package/src/shade/renderer/wind/GPUSceneWind.d.ts +1 -1
  257. package/src/shade/wgsl/emulator/CPUBitmapData.d.ts +19 -0
  258. package/src/shade/wgsl/emulator/CPUBitmapData.d.ts.map +1 -1
  259. package/src/shade/wgsl/emulator/CPUBitmapData.js +27 -0
  260. package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
  261. package/src/shade/wgsl/emulator/WGSLLanguageCore.js +39 -18
  262. package/src/view/elements/progress/segmented/GhostTrail.d.ts +56 -0
  263. package/src/view/elements/progress/segmented/GhostTrail.d.ts.map +1 -0
  264. package/src/view/elements/progress/segmented/GhostTrail.js +98 -0
  265. package/src/view/elements/progress/segmented/RESOURCE_BAR_SEGMENTS.d.ts +3 -1
  266. package/src/view/elements/progress/segmented/RESOURCE_BAR_SEGMENTS.d.ts.map +1 -1
  267. package/src/view/elements/progress/segmented/RESOURCE_BAR_SEGMENTS.js +3 -1
  268. package/src/view/elements/progress/segmented/SEGMENT_TIERS_DEFAULT.d.ts +11 -0
  269. package/src/view/elements/progress/segmented/SEGMENT_TIERS_DEFAULT.d.ts.map +1 -0
  270. package/src/view/elements/progress/segmented/SEGMENT_TIERS_DEFAULT.js +15 -0
  271. package/src/view/elements/progress/segmented/SegmentDefinition.d.ts +3 -0
  272. package/src/view/elements/progress/segmented/SegmentDefinition.d.ts.map +1 -1
  273. package/src/view/elements/progress/segmented/SegmentDefinition.js +3 -0
  274. package/src/view/elements/progress/segmented/SegmentTier.d.ts +38 -0
  275. package/src/view/elements/progress/segmented/SegmentTier.d.ts.map +1 -0
  276. package/src/view/elements/progress/segmented/SegmentTier.js +50 -0
  277. package/src/view/elements/progress/segmented/SegmentedResourceBarView.d.ts +155 -10
  278. package/src/view/elements/progress/segmented/SegmentedResourceBarView.d.ts.map +1 -1
  279. package/src/view/elements/progress/segmented/SegmentedResourceBarView.js +427 -101
  280. package/src/view/elements/progress/segmented/playground/index.html +78 -0
  281. package/src/view/elements/progress/segmented/playground/main.d.ts +2 -0
  282. package/src/view/elements/progress/segmented/playground/main.d.ts.map +1 -0
  283. package/src/view/elements/progress/segmented/playground/main.js +173 -0
  284. package/src/view/elements/progress/segmented/segment_cell_layout.d.ts +48 -0
  285. package/src/view/elements/progress/segmented/segment_cell_layout.d.ts.map +1 -0
  286. package/src/view/elements/progress/segmented/segment_cell_layout.js +75 -0
  287. package/src/view/elements/progress/segmented/segment_notch_place.d.ts +18 -0
  288. package/src/view/elements/progress/segmented/segment_notch_place.d.ts.map +1 -0
  289. package/src/view/elements/progress/segmented/segment_notch_place.js +28 -0
  290. package/src/view/elements/progress/segmented/segment_tick_positions.d.ts +32 -0
  291. package/src/view/elements/progress/segmented/segment_tick_positions.d.ts.map +1 -0
  292. package/src/view/elements/progress/segmented/segment_tick_positions.js +103 -0
  293. package/src/view/elements/progress/segmented/segmented-resource-bar.css +142 -0
  294. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/shader_mesh_brick_provider.d.ts +0 -35
  295. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/shader_mesh_brick_provider.d.ts.map +0 -1
  296. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/shader_mesh_brick_provider.js +0 -238
  297. package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/VGEO_BRICK.d.ts +0 -99
  298. package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/VGEO_BRICK.d.ts.map +0 -1
  299. package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/VGEO_BRICK.js +0 -139
  300. package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/VGeoBrickProvider.d.ts +0 -65
  301. package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/VGeoBrickProvider.d.ts.map +0 -1
  302. package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/VGeoBrickProvider.js +0 -393
  303. package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/chunk_vgeo_brick_box.d.ts +0 -17
  304. package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/chunk_vgeo_brick_box.d.ts.map +0 -1
  305. package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/chunk_vgeo_brick_box.js +0 -36
  306. package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/chunk_vgeo_brick_pair.d.ts +0 -29
  307. package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/chunk_vgeo_brick_pair.d.ts.map +0 -1
  308. package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/chunk_vgeo_brick_pair.js +0 -108
  309. package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/chunk_vgeo_brick_texel.d.ts +0 -31
  310. package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/chunk_vgeo_brick_texel.d.ts.map +0 -1
  311. package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/chunk_vgeo_brick_texel.js +0 -113
  312. package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/chunk_vgeo_brick_triangle.d.ts +0 -36
  313. package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/chunk_vgeo_brick_triangle.d.ts.map +0 -1
  314. package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/chunk_vgeo_brick_triangle.js +0 -85
  315. package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/shader_vgeo_brick_distance.d.ts +0 -23
  316. package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/shader_vgeo_brick_distance.d.ts.map +0 -1
  317. package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/shader_vgeo_brick_distance.js +0 -273
  318. package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/shader_vgeo_brick_gather.d.ts +0 -13
  319. package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/shader_vgeo_brick_gather.d.ts.map +0 -1
  320. package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/shader_vgeo_brick_gather.js +0 -151
  321. package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/shader_vgeo_brick_traverse.d.ts +0 -13
  322. package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/shader_vgeo_brick_traverse.d.ts.map +0 -1
  323. package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/shader_vgeo_brick_traverse.js +0 -133
  324. package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/shader_vgeo_brick_wait.d.ts +0 -22
  325. package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/shader_vgeo_brick_wait.d.ts.map +0 -1
  326. package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/shader_vgeo_brick_wait.js +0 -135
@@ -9,15 +9,19 @@ import { f32, u32 } from "../../../shader/type/WEBGPU_WGSL_PRIMITIVE_TYPES.js";
9
9
  import { WebGPUStruct } from "../../../shader/type/WebGPUStruct.js";
10
10
  import {
11
11
  chunk_probe_clipmap,
12
+ chunk_probe_clipmap_guide,
13
+ PROBE_GUIDE_READ_RESOURCE,
12
14
  PROBE_MAPS_READ_RESOURCE,
13
15
  PROBE_PROJECTION_READ_RESOURCE,
14
16
  PROBE_PROJECTION_ROW_STRUCT,
15
17
  PROBE_PROJECTION_WRITE_RESOURCE,
16
18
  PROBE_RECORDS_STORE_RESOURCE,
19
+ PROBE_REQUEST_STRUCT,
20
+ PROBE_REQUESTS_WRITE_RESOURCE,
17
21
  PROBE_STATES_STORE_RESOURCE,
18
22
  PROBE_TRACE_READ_RESOURCE
19
23
  } from "./chunk_probe_clipmap.js";
20
- import { PROBE_DEAD_BACK_FACE_SHARE, PROBE_THREAD_WORKGROUP_SIZE } from "./PROBE_CLIPMAP.js";
24
+ import { PROBE_CHANGE_SIGNIFICANCE, PROBE_DEAD_BACK_FACE_SHARE, PROBE_RADIANCE_SAMPLES_LIMIT, PROBE_THREAD_WORKGROUP_SIZE } from "./PROBE_CLIPMAP.js";
21
25
 
22
26
  /**
23
27
  * @type {WebGPUStruct}
@@ -25,12 +29,22 @@ import { PROBE_DEAD_BACK_FACE_SHARE, PROBE_THREAD_WORKGROUP_SIZE } from "./PROBE
25
29
  export const PROBE_PROJECT_SETTINGS = WebGPUStruct.from({
26
30
  /** probes a frame updates at most: the trace list's bound */
27
31
  trace_capacity: u32,
32
+ /** rays a uniform update casts, R */
33
+ rays_per_probe: u32,
28
34
  /** octahedral radiance texels a side, n_r */
29
35
  radiance_resolution: u32,
30
36
  /** octahedral depth texels a side, n_d */
31
37
  depth_resolution: u32,
32
- /** the frame's time on the records' clock, seconds */
33
- time: f32,
38
+ /** the frame, of the frames the clipmap has recorded */
39
+ frame: u32,
40
+ /** updates between a probe's uniform ones, `PROBE_UNIFORM_PERIOD` */
41
+ uniform_period: u32,
42
+ /** the fewest rays an update casts, `PROBE_RAYS_PER_TEXEL_MIN` of the texels */
43
+ rays_min: u32,
44
+ /** the most rays an update casts, `PROBE_RAYS_PER_TEXEL_MAX` of the texels */
45
+ rays_max: u32,
46
+ /** the radiance's short mean's response, τ, in frames: the change score's statistics follow it */
47
+ response_frames: f32,
34
48
  }, "ProbeProjectSettings");
35
49
 
36
50
  const row_resources = new ShaderResourceSetDescriptor();
@@ -46,8 +60,9 @@ row_resources.createGroup()
46
60
  * Projection, rows (PROBE_CLIPMAP_PROPOSAL_2026_10_27.md §8.8): a thread a row of each listed probe's
47
61
  * radiance map, dispatched through `PROBE_DISPATCH_ROWS`. It sums the row's texels, new and kept, as
48
62
  * irradiance SH, `convert_to_irradiance_sh(L, ω)·dω/(2π)` over each texel's solid angle, so that the
49
- * resolve gives E/π; and the row's rays this update, resolved and back faces. One row of the probe's
50
- * projection each, which the probe's pass sums.
63
+ * resolve gives E/π; the row's rays this update, resolved and back faces; and how far the update's
64
+ * samples fell from their means, with that sum's variance. One row of the probe's projection each, which
65
+ * the probe's pass sums.
51
66
  *
52
67
  * @type {ComputeShader}
53
68
  */
@@ -84,11 +99,14 @@ fn main(
84
99
  let weight = octahedral_solid_angle(uv, inverse) * ${(0.5 / Math.PI).toPrecision(17)};
85
100
  let sh = convert_to_irradiance_sh(light, uv_octahedral_unit_decode(uv));
86
101
  let rays = probe_maps[maps + 2u * texels + i];
102
+ let change = unpack2x16float(probe_maps[maps + 3u * texels + settings.depth_resolution * settings.depth_resolution + i]);
87
103
 
88
104
  row_sum.sh.CoCg += sh.CoCg * weight;
89
105
  row_sum.sh.Y += sh.Y * weight;
90
106
  row_sum.resolved += rays & 0xFFFFu;
91
107
  row_sum.back_faces += rays >> 16u;
108
+ row_sum.change += change.x;
109
+ row_sum.blended += change.y;
92
110
  }
93
111
 
94
112
  probe_projection[thread] = row_sum;
@@ -110,8 +128,10 @@ probe_resources.setImmediate("settings", PROBE_PROJECT_SETTINGS);
110
128
  probe_resources.createGroup()
111
129
  .add(PROBE_TRACE_READ_RESOURCE)
112
130
  .add(PROBE_PROJECTION_READ_RESOURCE)
131
+ .add(PROBE_GUIDE_READ_RESOURCE)
113
132
  .add(PROBE_STATES_STORE_RESOURCE)
114
- .add(PROBE_RECORDS_STORE_RESOURCE);
133
+ .add(PROBE_RECORDS_STORE_RESOURCE)
134
+ .add(PROBE_REQUESTS_WRITE_RESOURCE);
115
135
 
116
136
  /**
117
137
  * Projection, probes (PROBE_CLIPMAP_PROPOSAL_2026_10_27.md §8.4, §8.8): a thread a listed probe,
@@ -119,6 +139,27 @@ probe_resources.createGroup()
119
139
  * by the update's resolved rays: dead past a quarter of back faces, else alive, its update counted and
120
140
  * its time the frame's. An update with no resolved ray keeps the probe's kind.
121
141
  *
142
+ * Only uniform rays classify. Those are a uniform update's, and a guided update's whose guide weighs
143
+ * nothing (a dark history), which spreads its rays evenly. A guided update's rays shun back faces, whose
144
+ * radiance is nothing, so their share would read a probe inside a solid as alive. Such an update keeps
145
+ * the probe's kind and its back-face share.
146
+ *
147
+ * **The probe's ray request** (§8.6): the rays its next guided update asks for, in `probe_requests`.
148
+ * - After a probe's first update, the most: Enshrouded's new probe that asks for all it can have.
149
+ * - When its light has moved, the most again. The update's change score sums its blended texels' falls
150
+ * from their means, luminance, each in its own deviation and held to `PROBE_CHANGE_SIGNIFICANCE` by
151
+ * accumulation, over the root of their count. A test texel by texel sees no change under a one-ray
152
+ * texel's own noise (measured: the estimator's inconsistency barely moves on a doubling); a sum of the
153
+ * falls unbounded is one bright sample's.
154
+ * - The score is held to the probe's own: moved means past `PROBE_CHANGE_SIGNIFICANCE` of its running
155
+ * deviation, at least one, from its running mean, both over the estimator's variance window (2τ). A
156
+ * probe whose means sit off its samples (the estimator's own bias on texels lit by few rays) scores
157
+ * steadily off zero.
158
+ * - Measured, Sponza on the 4090, the camera and the light still, 3,600 frames settled: of the probes
159
+ * past 64 updates, 4.2% asked for the most against a sum of the raw falls held to zero, 3.2% with each
160
+ * texel's fall bounded, and 1.1–1.3% held to their own statistics.
161
+ * - Otherwise half what it asked for last, down to the fewest: a converged probe sips.
162
+ *
122
163
  * @type {ComputeShader}
123
164
  */
124
165
  export const shader_probe_clipmap_project = ComputeShader.from({
@@ -150,6 +191,8 @@ fn main(
150
191
  sum.sh.Y += part.sh.Y;
151
192
  sum.resolved += part.resolved;
152
193
  sum.back_faces += part.back_faces;
194
+ sum.change += part.change;
195
+ sum.blended += part.blended;
153
196
  }
154
197
 
155
198
  let state = probe_states[slot].x;
@@ -157,21 +200,58 @@ fn main(
157
200
 
158
201
  var kind = probe_state_kind(state);
159
202
 
160
- if (sum.resolved > 0u) {
203
+ // the guide's total, less the even shares, is the weight its luminance gave
204
+ let header = k * probe_clipmap_guide_words(n) + 2u * n * n + 2u * n;
205
+ let weighed = probe_guide[header] != probe_guide[header + 1u] * n * n;
206
+ let uniform = !probe_clipmap_guided(state, settings.uniform_period, settings.rays_per_probe, n * n) || !weighed;
207
+
208
+ if (sum.resolved > 0u && uniform) {
161
209
  let share = f32(sum.back_faces) / f32(sum.resolved);
162
210
 
163
211
  kind = select(PROBE_STATE_ALIVE, PROBE_STATE_DEAD, share > ${PROBE_DEAD_BACK_FACE_SHARE});
164
212
 
165
- probe_records[slot].back_face = share;
213
+ probe_records[slot].flags = (probe_records[slot].flags & 0xFFFF0000u) | probe_record_flags(share, false, 0u);
166
214
  }
167
215
 
216
+ let elapsed = f32(settings.frame - probe_records[slot].traced_frame);
217
+
168
218
  probe_states[slot].x = probe_state(kind, updates + 1u);
169
- probe_records[slot].sh = sum.sh;
170
- probe_records[slot].traced_time = settings.time;
219
+ probe_records[slot].sh = probe_record_sh_encode(sum.sh);
220
+ probe_records[slot].traced_frame = settings.frame;
221
+
222
+ // the next guided update's rays: the most after the first update and whenever the light moved, else half
223
+ var request = probe_requests[slot];
224
+
225
+ if (updates == 0u) {
226
+ // its slot's last owner's statistics are not its own
227
+ request = ${PROBE_REQUEST_STRUCT.make({ rays: "settings.rays_max", change_mean: "0.0", change_variance: "0.0", changes: "0u" })};
228
+ } else if (sum.blended > 0.0) {
229
+ let score = sum.change / sqrt(sum.blended);
230
+ let reach = ${PROBE_CHANGE_SIGNIFICANCE} * max(sqrt(request.change_variance), 1.0);
231
+ let moved = abs(score - request.change_mean) > reach;
232
+
233
+ // the score's running statistics, a running mean while young, then over the estimator's variance
234
+ // window; a score past their reach counts at it, so a change in the light does not become the probe's
235
+ // own spread, while a lasting shift of its own still does within a few windows
236
+ let held = clamp(score, request.change_mean - reach, request.change_mean + reach);
237
+ let blend = max(1.0 / f32(request.changes + 1u), 1.0 - exp(-0.5 * elapsed / settings.response_frames));
238
+ let mean = mix(request.change_mean, held, blend);
239
+
240
+ request.change_variance = mix(request.change_variance, (held - request.change_mean) * (held - mean), blend);
241
+ request.change_mean = mean;
242
+ request.changes = min(request.changes + 1u, ${PROBE_RADIANCE_SAMPLES_LIMIT}u);
243
+ request.rays = select(max(settings.rays_min, request.rays >> 1u), settings.rays_max, moved);
244
+ } else {
245
+ request.rays = max(settings.rays_min, request.rays >> 1u);
246
+ }
247
+
248
+ probe_requests[slot] = request;
171
249
  }
172
250
  `, [
173
251
  chunk_probe_clipmap,
252
+ chunk_probe_clipmap_guide,
174
253
  chunk_folded_workgroup_index,
175
254
  PROBE_PROJECTION_ROW_STRUCT.declaration_chunk,
255
+ PROBE_REQUEST_STRUCT.declaration_chunk,
176
256
  ]),
177
257
  });
@@ -0,0 +1,45 @@
1
+ /**
2
+ * @type {WebGPUStruct}
3
+ */
4
+ export const PROBE_RAY_COUNTS_SETTINGS: WebGPUStruct;
5
+ /**
6
+ * The ray counts (PROBE_CLIPMAP_PROPOSAL_2026_10_27.md §8.6): a thread a listed probe, dispatched through
7
+ * `PROBE_DISPATCH_TRACED`, writes the rays its update casts, as two parts whose counts the trace command
8
+ * wrote:
9
+ * - `probe_ray_counts`, what the budget does not scale: a uniform update's R, a NEW probe's among them,
10
+ * and a DEAD probe's fewest, whose updates are all uniform. A uniform update's map of rays to texels
11
+ * takes a power of two, and U = ⌊budget / R⌋ makes room for a frame of them.
12
+ * - `probe_ray_requests`, what it does: a guided update's request, the rays the probe asked for after its
13
+ * last update (`probe_requests`), at least the fewest.
14
+ *
15
+ * Their scans, and the ray layout pass after them, lay the frame's rays out (`probe_ray_sums`).
16
+ *
17
+ * @type {ComputeShader}
18
+ */
19
+ export const shader_probe_clipmap_ray_counts: ComputeShader;
20
+ /**
21
+ * @type {WebGPUStruct}
22
+ */
23
+ export const PROBE_RAY_LAYOUT_SETTINGS: WebGPUStruct;
24
+ /**
25
+ * The ray layout (§8.6): a thread a listed probe, dispatched through `PROBE_DISPATCH_TRACED`, once the
26
+ * counts and the requests are scanned. The requests share what the budget leaves past the counts, all of
27
+ * them when they fit, in proportion when they do not (Enshrouded's requests scaled to the budget), and
28
+ * the k-th probe's rays end at its counts' sum and its requests' sum so scaled, rounded down. The rounded
29
+ * sums never fall as k rises and their last is under the budget, so the probes' rays partition the
30
+ * frame's without a second scan. The trace command wrote the sums' count.
31
+ *
32
+ * @type {ComputeShader}
33
+ */
34
+ export const shader_probe_clipmap_ray_layout: ComputeShader;
35
+ /**
36
+ * The rays' command (§8.6): one thread, once the rays are laid out, writes a thread a ray of the frame's,
37
+ * its count the last sum, at `PROBE_DISPATCH_RAYS`: trace, shade, light, and the next frame's hits'
38
+ * wants. The last workgroup's threads past the last ray are trace's to mark (`PROBE_RAY_NONE`).
39
+ *
40
+ * @type {ComputeShader}
41
+ */
42
+ export const shader_probe_clipmap_ray_command: ComputeShader;
43
+ import { WebGPUStruct } from "../../../shader/type/WebGPUStruct.js";
44
+ import { ComputeShader } from "../../../shader/ComputeShader.js";
45
+ //# sourceMappingURL=shader_probe_clipmap_ray_layout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shader_probe_clipmap_ray_layout.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_ray_layout.js"],"names":[],"mappings":"AAuBA;;GAEG;AACH,wCAFU,YAAY,CAaO;AAa7B;;;;;;;;;;;;;GAaG;AACH,8CAFU,aAAa,CAyCpB;AAEH;;GAEG;AACH,wCAFU,YAAY,CAOO;AAW7B;;;;;;;;;GASG;AACH,8CAFU,aAAa,CA+BpB;AAQH;;;;;;GAMG;AACH,+CAFU,aAAa,CA4BpB;6BAtM0B,sCAAsC;8BAHrC,kCAAkC"}
@@ -0,0 +1,205 @@
1
+ import { chunk_folded_dispatch } from "../../../gpu_primitive/indirect/chunk_folded_dispatch.js";
2
+ import { chunk_folded_workgroup_index } from "../../../shader/chunk/grid/chunk_folded_workgroup_index.js";
3
+ import { CodeChunk } from "../../../shader/compiler/CodeChunk.js";
4
+ import { ComputeShader } from "../../../shader/ComputeShader.js";
5
+ import { ShaderResourceSetDescriptor } from "../../../shader/resource/ShaderResourceSetDescriptor.js";
6
+ import { u32 } from "../../../shader/type/WEBGPU_WGSL_PRIMITIVE_TYPES.js";
7
+ import { WebGPUStruct } from "../../../shader/type/WebGPUStruct.js";
8
+ import {
9
+ chunk_probe_clipmap,
10
+ chunk_probe_clipmap_guide,
11
+ PROBE_DISPATCH_RESOURCE,
12
+ PROBE_RAY_COUNT_SUMS_READ_RESOURCE,
13
+ PROBE_RAY_COUNTS_WRITE_RESOURCE,
14
+ PROBE_RAY_REQUEST_SUMS_READ_RESOURCE,
15
+ PROBE_RAY_REQUESTS_WRITE_RESOURCE,
16
+ PROBE_RAY_SUMS_READ_RESOURCE,
17
+ PROBE_RAY_SUMS_WRITE_RESOURCE,
18
+ PROBE_REQUESTS_READ_RESOURCE,
19
+ PROBE_STATES_READ_RESOURCE,
20
+ PROBE_TRACE_READ_RESOURCE
21
+ } from "./chunk_probe_clipmap.js";
22
+ import { PROBE_THREAD_WORKGROUP_SIZE } from "./PROBE_CLIPMAP.js";
23
+
24
+ /**
25
+ * @type {WebGPUStruct}
26
+ */
27
+ export const PROBE_RAY_COUNTS_SETTINGS = WebGPUStruct.from({
28
+ /** probes a frame updates at most: the trace list's bound */
29
+ trace_capacity: u32,
30
+ /** rays a uniform update casts, R */
31
+ rays_per_probe: u32,
32
+ /** the fewest rays an update casts, a DEAD probe's, `PROBE_RAYS_PER_TEXEL_MIN` of the texels */
33
+ rays_min: u32,
34
+ /** octahedral radiance texels a side, n_r */
35
+ radiance_resolution: u32,
36
+ /** updates between a probe's uniform ones, `PROBE_UNIFORM_PERIOD` */
37
+ uniform_period: u32,
38
+ }, "ProbeRayCountsSettings");
39
+
40
+ const counts_resources = new ShaderResourceSetDescriptor();
41
+
42
+ counts_resources.setImmediate("settings", PROBE_RAY_COUNTS_SETTINGS);
43
+
44
+ counts_resources.createGroup()
45
+ .add(PROBE_TRACE_READ_RESOURCE)
46
+ .add(PROBE_STATES_READ_RESOURCE)
47
+ .add(PROBE_REQUESTS_READ_RESOURCE)
48
+ .add(PROBE_RAY_COUNTS_WRITE_RESOURCE)
49
+ .add(PROBE_RAY_REQUESTS_WRITE_RESOURCE);
50
+
51
+ /**
52
+ * The ray counts (PROBE_CLIPMAP_PROPOSAL_2026_10_27.md §8.6): a thread a listed probe, dispatched through
53
+ * `PROBE_DISPATCH_TRACED`, writes the rays its update casts, as two parts whose counts the trace command
54
+ * wrote:
55
+ * - `probe_ray_counts`, what the budget does not scale: a uniform update's R, a NEW probe's among them,
56
+ * and a DEAD probe's fewest, whose updates are all uniform. A uniform update's map of rays to texels
57
+ * takes a power of two, and U = ⌊budget / R⌋ makes room for a frame of them.
58
+ * - `probe_ray_requests`, what it does: a guided update's request, the rays the probe asked for after its
59
+ * last update (`probe_requests`), at least the fewest.
60
+ *
61
+ * Their scans, and the ray layout pass after them, lay the frame's rays out (`probe_ray_sums`).
62
+ *
63
+ * @type {ComputeShader}
64
+ */
65
+ export const shader_probe_clipmap_ray_counts = ComputeShader.from({
66
+ label: "Probe Clipmap Ray Counts",
67
+ resources: counts_resources,
68
+ body: CodeChunk.from(
69
+ //language=WGSL
70
+ `
71
+ @compute @workgroup_size(${PROBE_THREAD_WORKGROUP_SIZE})
72
+ fn main(
73
+ @builtin(workgroup_id) workgroup_id: vec3<u32>,
74
+ @builtin(local_invocation_index) lane: u32,
75
+ ) {
76
+ let k = folded_workgroup_index(workgroup_id) * PROBE_THREAD_WORKGROUP_SIZE + lane;
77
+
78
+ if (k >= min(probe_trace.count, settings.trace_capacity)) {
79
+ return;
80
+ }
81
+
82
+ let slot = probe_trace.elements[k];
83
+ let state = probe_states[slot].x;
84
+ let n = settings.radiance_resolution;
85
+
86
+ var fixed = settings.rays_per_probe;
87
+ var request = 0u;
88
+
89
+ if (probe_state_kind(state) == PROBE_STATE_DEAD) {
90
+ fixed = settings.rays_min;
91
+ } else if (probe_clipmap_guided(state, settings.uniform_period, settings.rays_per_probe, n * n)) {
92
+ fixed = 0u;
93
+ request = max(probe_requests[slot].rays, settings.rays_min);
94
+ }
95
+
96
+ probe_ray_counts.elements[k] = fixed;
97
+ probe_ray_requests.elements[k] = request;
98
+ }
99
+ `, [
100
+ chunk_probe_clipmap,
101
+ chunk_probe_clipmap_guide,
102
+ chunk_folded_workgroup_index,
103
+ ]),
104
+ });
105
+
106
+ /**
107
+ * @type {WebGPUStruct}
108
+ */
109
+ export const PROBE_RAY_LAYOUT_SETTINGS = WebGPUStruct.from({
110
+ /** probes a frame updates at most: the trace list's bound */
111
+ trace_capacity: u32,
112
+ /** rays a frame casts at most */
113
+ ray_budget: u32,
114
+ }, "ProbeRayLayoutSettings");
115
+
116
+ const layout_resources = new ShaderResourceSetDescriptor();
117
+
118
+ layout_resources.setImmediate("settings", PROBE_RAY_LAYOUT_SETTINGS);
119
+
120
+ layout_resources.createGroup()
121
+ .add(PROBE_RAY_COUNT_SUMS_READ_RESOURCE)
122
+ .add(PROBE_RAY_REQUEST_SUMS_READ_RESOURCE)
123
+ .add(PROBE_RAY_SUMS_WRITE_RESOURCE);
124
+
125
+ /**
126
+ * The ray layout (§8.6): a thread a listed probe, dispatched through `PROBE_DISPATCH_TRACED`, once the
127
+ * counts and the requests are scanned. The requests share what the budget leaves past the counts, all of
128
+ * them when they fit, in proportion when they do not (Enshrouded's requests scaled to the budget), and
129
+ * the k-th probe's rays end at its counts' sum and its requests' sum so scaled, rounded down. The rounded
130
+ * sums never fall as k rises and their last is under the budget, so the probes' rays partition the
131
+ * frame's without a second scan. The trace command wrote the sums' count.
132
+ *
133
+ * @type {ComputeShader}
134
+ */
135
+ export const shader_probe_clipmap_ray_layout = ComputeShader.from({
136
+ label: "Probe Clipmap Ray Layout",
137
+ resources: layout_resources,
138
+ body: CodeChunk.from(
139
+ //language=WGSL
140
+ `
141
+ @compute @workgroup_size(${PROBE_THREAD_WORKGROUP_SIZE})
142
+ fn main(
143
+ @builtin(workgroup_id) workgroup_id: vec3<u32>,
144
+ @builtin(local_invocation_index) lane: u32,
145
+ ) {
146
+ let k = folded_workgroup_index(workgroup_id) * PROBE_THREAD_WORKGROUP_SIZE + lane;
147
+ let listed = probe_ray_count_sums.count;
148
+
149
+ if (k >= listed) {
150
+ return;
151
+ }
152
+
153
+ let counted = probe_ray_count_sums.elements[listed - 1u];
154
+ let requested = probe_ray_request_sums.elements[listed - 1u];
155
+ let room = select(0u, settings.ray_budget - counted, settings.ray_budget > counted);
156
+ let scale = select(1.0, f32(room) / f32(requested), requested > room);
157
+
158
+ probe_ray_sums.elements[k] = probe_ray_count_sums.elements[k] + u32(floor(scale * f32(probe_ray_request_sums.elements[k])));
159
+ }
160
+ `, [
161
+ chunk_probe_clipmap,
162
+ chunk_folded_workgroup_index,
163
+ ]),
164
+ });
165
+
166
+ const command_resources = new ShaderResourceSetDescriptor();
167
+
168
+ command_resources.createGroup()
169
+ .add(PROBE_RAY_SUMS_READ_RESOURCE)
170
+ .add(PROBE_DISPATCH_RESOURCE);
171
+
172
+ /**
173
+ * The rays' command (§8.6): one thread, once the rays are laid out, writes a thread a ray of the frame's,
174
+ * its count the last sum, at `PROBE_DISPATCH_RAYS`: trace, shade, light, and the next frame's hits'
175
+ * wants. The last workgroup's threads past the last ray are trace's to mark (`PROBE_RAY_NONE`).
176
+ *
177
+ * @type {ComputeShader}
178
+ */
179
+ export const shader_probe_clipmap_ray_command = ComputeShader.from({
180
+ label: "Probe Clipmap Ray Command",
181
+ resources: command_resources,
182
+ body: CodeChunk.from(
183
+ //language=WGSL
184
+ `
185
+ @compute @workgroup_size(1)
186
+ fn main() {
187
+ let listed = probe_ray_sums.count;
188
+
189
+ var total = 0u;
190
+
191
+ if (listed > 0u) {
192
+ total = probe_ray_sums.elements[listed - 1u];
193
+ }
194
+
195
+ let rays = folded_dispatch((total + PROBE_THREAD_WORKGROUP_SIZE - 1u) / PROBE_THREAD_WORKGROUP_SIZE);
196
+
197
+ probe_dispatch[PROBE_DISPATCH_RAYS] = rays.x;
198
+ probe_dispatch[PROBE_DISPATCH_RAYS + 1u] = rays.y;
199
+ probe_dispatch[PROBE_DISPATCH_RAYS + 2u] = rays.z;
200
+ }
201
+ `, [
202
+ chunk_probe_clipmap,
203
+ chunk_folded_dispatch,
204
+ ]),
205
+ });
@@ -87,7 +87,7 @@ fn main(
87
87
  }
88
88
 
89
89
  let slot = probe_trace.elements[index];
90
- let spacing = probe_levels[probe_records[slot].cascade].cell_size;
90
+ let spacing = probe_levels[probe_record_cascade(probe_records[slot].flags)].cell_size;
91
91
  let lattice = vec3<f32>(probe_records[slot].cell) * spacing;
92
92
  let clearance = ${PROBE_RELOCATION_CLEARANCE} * spacing;
93
93
 
@@ -6,9 +6,22 @@ export const PROBE_RETIRE_SETTINGS: WebGPUStruct;
6
6
  * Retire (PROBE_CLIPMAP_PROPOSAL_2026_10_27.md §8.3): a thread a live probe, the first of the frame's
7
7
  * lifetime passes, dispatched through `PROBE_DISPATCH_ACTIVE` over the list the frame before wrote.
8
8
  *
9
- * A probe is freed when its lattice point has left its cascade's window, or when nothing, pixel or hit,
10
- * has wanted it for longer than the retention time: its slot goes back on the free list, its state to
11
- * FREE, and its indirection entry, if it still names the slot, to none. The rest go on the frame's list.
9
+ * A probe is freed when its lattice point has left its cascade's window, or when the last frame's
10
+ * schedule asked for its slot: its slot goes back on the free list, its state to FREE, and its
11
+ * indirection entry, if it still names the slot, to none. The rest go on the frame's list.
12
+ *
13
+ * Eviction follows the pressure on the slots, not a time (Enshrouded's cache): schedule asks for as many
14
+ * slots as the free ones are short of the frame's wanted points and `PROBE_FREE_SLACK` of the capacity,
15
+ * least recently wanted first, so with room to spare a probe keeps its history however long nothing
16
+ * wants it. It asks by the probes' age buckets (`probe_clipmap_age_bucket` of the frames since the probe
17
+ * was last wanted), `PROBE_EVICTION_STRUCT`: the oldest buckets whole, and a number of the bucket below
18
+ * them, which the frame's threads take while `PROBE_COUNTER_EVICTED` is under it. A probe in use is
19
+ * never evicted: one a want waits on, NEW or wanted since its last update, and one wanted the last
20
+ * frame. Where those fill the slots, an admission starves and the capacity grows (§7.10).
21
+ *
22
+ * Every probe it keeps that it could have evicted is counted into its age's bucket, from
23
+ * `PROBE_COUNTER_AGES`, for the frame's schedule to choose the next eviction by; and every one it keeps
24
+ * that waits for an update into `PROBE_COUNTER_WAITING`, for the period.
12
25
  *
13
26
  * Running before want means a window's move needs no pass of its own: the storage cells it vacated
14
27
  * are emptied before any point it exposed can claim them.
@@ -1 +1 @@
1
- {"version":3,"file":"shader_probe_clipmap_retire.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_retire.js"],"names":[],"mappings":"AAuBA;;GAEG;AACH,oCAFU,YAAY,CAWI;AAgB1B;;;;;;;;;;;;GAYG;AACH,0CAFU,aAAa,CAuDpB;6BA5G0B,sCAAsC;8BAHrC,kCAAkC"}
1
+ {"version":3,"file":"shader_probe_clipmap_retire.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_retire.js"],"names":[],"mappings":"AAyBA;;GAEG;AACH,oCAFU,YAAY,CASI;AAiB1B;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,0CAFU,aAAa,CAsFpB;6BAxJ0B,sCAAsC;8BAHrC,kCAAkC"}
@@ -3,16 +3,18 @@ import { chunk_slot_stack } from "../../../gpu_primitive/slot_stack/chunk_slot_s
3
3
  import { chunk_clipmap_contains } from "../../../shader/chunk/clipmap/chunk_clipmap_contains.js";
4
4
  import { chunk_clipmap_storage_coord } from "../../../shader/chunk/clipmap/chunk_clipmap_storage_coord.js";
5
5
  import { chunk_folded_workgroup_index } from "../../../shader/chunk/grid/chunk_folded_workgroup_index.js";
6
+ import { chunk_subgroup_atomic_add } from "../../../shader/chunk/subgroup/chunk_subgroup_atomic_add.js";
6
7
  import { CodeChunk } from "../../../shader/compiler/CodeChunk.js";
7
8
  import { ComputeShader } from "../../../shader/ComputeShader.js";
8
9
  import { ShaderResourceSetDescriptor } from "../../../shader/resource/ShaderResourceSetDescriptor.js";
9
- import { f32, u32 } from "../../../shader/type/WEBGPU_WGSL_PRIMITIVE_TYPES.js";
10
+ import { u32 } from "../../../shader/type/WEBGPU_WGSL_PRIMITIVE_TYPES.js";
10
11
  import { WebGPUStruct } from "../../../shader/type/WebGPUStruct.js";
11
12
  import {
12
13
  chunk_probe_clipmap,
13
14
  PROBE_ACTIVE_READ_RESOURCE,
14
15
  PROBE_ACTIVE_WRITE_RESOURCE,
15
16
  PROBE_COUNTERS_RESOURCE,
17
+ PROBE_EVICTION_READ_RESOURCE,
16
18
  PROBE_FREE_RESOURCE,
17
19
  PROBE_INDIRECTION_STORE_RESOURCE,
18
20
  PROBE_LEVELS_RESOURCE,
@@ -29,10 +31,8 @@ export const PROBE_RETIRE_SETTINGS = WebGPUStruct.from({
29
31
  capacity: u32,
30
32
  /** lattice points a cascade's window has a side */
31
33
  resolution: u32,
32
- /** the frame's time, seconds since the clipmap was made */
33
- time: f32,
34
- /** seconds an unwanted probe keeps its slot, `retention_time` */
35
- retention_time: f32,
34
+ /** the frame, of the frames the clipmap has recorded: a probe wanted the frame before it or since is not evicted */
35
+ frame: u32,
36
36
  }, "ProbeRetireSettings");
37
37
 
38
38
  const resources = new ShaderResourceSetDescriptor();
@@ -45,6 +45,7 @@ resources.createGroup()
45
45
  .add(PROBE_STATES_STORE_RESOURCE)
46
46
  .add(PROBE_RECORDS_STORE_RESOURCE)
47
47
  .add(PROBE_COUNTERS_RESOURCE)
48
+ .add(PROBE_EVICTION_READ_RESOURCE)
48
49
  .add(PROBE_FREE_RESOURCE)
49
50
  .add(PROBE_ACTIVE_READ_RESOURCE)
50
51
  .add(PROBE_ACTIVE_WRITE_RESOURCE);
@@ -53,9 +54,22 @@ resources.createGroup()
53
54
  * Retire (PROBE_CLIPMAP_PROPOSAL_2026_10_27.md §8.3): a thread a live probe, the first of the frame's
54
55
  * lifetime passes, dispatched through `PROBE_DISPATCH_ACTIVE` over the list the frame before wrote.
55
56
  *
56
- * A probe is freed when its lattice point has left its cascade's window, or when nothing, pixel or hit,
57
- * has wanted it for longer than the retention time: its slot goes back on the free list, its state to
58
- * FREE, and its indirection entry, if it still names the slot, to none. The rest go on the frame's list.
57
+ * A probe is freed when its lattice point has left its cascade's window, or when the last frame's
58
+ * schedule asked for its slot: its slot goes back on the free list, its state to FREE, and its
59
+ * indirection entry, if it still names the slot, to none. The rest go on the frame's list.
60
+ *
61
+ * Eviction follows the pressure on the slots, not a time (Enshrouded's cache): schedule asks for as many
62
+ * slots as the free ones are short of the frame's wanted points and `PROBE_FREE_SLACK` of the capacity,
63
+ * least recently wanted first, so with room to spare a probe keeps its history however long nothing
64
+ * wants it. It asks by the probes' age buckets (`probe_clipmap_age_bucket` of the frames since the probe
65
+ * was last wanted), `PROBE_EVICTION_STRUCT`: the oldest buckets whole, and a number of the bucket below
66
+ * them, which the frame's threads take while `PROBE_COUNTER_EVICTED` is under it. A probe in use is
67
+ * never evicted: one a want waits on, NEW or wanted since its last update, and one wanted the last
68
+ * frame. Where those fill the slots, an admission starves and the capacity grows (§7.10).
69
+ *
70
+ * Every probe it keeps that it could have evicted is counted into its age's bucket, from
71
+ * `PROBE_COUNTER_AGES`, for the frame's schedule to choose the next eviction by; and every one it keeps
72
+ * that waits for an update into `PROBE_COUNTER_WAITING`, for the period.
59
73
  *
60
74
  * Running before want means a window's move needs no pass of its own: the storage cells it vacated
61
75
  * are emptied before any point it exposed can claim them.
@@ -77,18 +91,43 @@ fn main(
77
91
 
78
92
  var slot = 0u;
79
93
  var keep = false;
94
+ var waiting = false;
95
+ var evictable = false;
96
+ var bucket = 0u;
80
97
 
81
98
  if (index < min(probe_active_read.count, settings.capacity)) {
82
99
  slot = probe_active_read.elements[index];
83
100
 
84
101
  let cell = probe_records[slot].cell;
85
- let cascade = probe_records[slot].cascade;
102
+ let cascade = probe_record_cascade(probe_records[slot].flags);
86
103
  let level = probe_levels[cascade];
104
+ let wanted_frame = probe_records[slot].wanted_frame;
87
105
 
88
106
  // the lattice's window is one point wider than the cages' on each axis
89
107
  let held = clipmap_contains(cell, level.corner, level.size + vec3<i32>(1));
90
108
 
91
- keep = held && settings.time - probe_records[slot].wanted_time <= settings.retention_time;
109
+ waiting = probe_state_kind(probe_states[slot].x) == PROBE_STATE_NEW || wanted_frame > probe_records[slot].traced_frame;
110
+ // not wanted the frame before: wanted_frame < frame − 1, which does not wrap at the first frame
111
+ evictable = !waiting && wanted_frame + 1u < settings.frame;
112
+
113
+ let age = settings.frame - wanted_frame;
114
+
115
+ bucket = probe_clipmap_age_bucket(age);
116
+
117
+ // the bucket the last frame's schedule counted it in, a frame younger: a probe that aged into an asked
118
+ // bucket since, uncounted, is not taken for it
119
+ let counted = probe_clipmap_age_bucket(age - 1u);
120
+
121
+ // the oldest buckets whole, and as many of the one below them as are asked for; one that left its
122
+ // window goes anyway and takes none of those
123
+ let whole = PROBE_AGE_BUCKETS - min(probe_eviction.buckets, PROBE_AGE_BUCKETS);
124
+ var evicted = evictable && counted >= whole;
125
+
126
+ if (held && evictable && counted + 1u == whole && probe_eviction.remaining > 0u) {
127
+ evicted = atomicAdd(&probe_counters[PROBE_COUNTER_EVICTED], 1u) < probe_eviction.remaining;
128
+ }
129
+
130
+ keep = held && !evicted;
92
131
 
93
132
  if (!keep) {
94
133
  let resolution = settings.resolution;
@@ -103,8 +142,13 @@ fn main(
103
142
 
104
143
  slot_stack_push(&probe_counters[PROBE_COUNTER_FREE], &probe_free, slot);
105
144
  }
145
+
146
+ if (keep && evictable) {
147
+ atomicAdd(&probe_counters[PROBE_COUNTER_AGES + bucket], 1u);
148
+ }
106
149
  }
107
150
 
151
+ subgroup_atomic_add_u32(&probe_counters[PROBE_COUNTER_WAITING], select(0u, 1u, keep && waiting));
108
152
  collection_u32_append(&probe_active_write, keep, slot, settings.capacity);
109
153
  }
110
154
  `, [
@@ -114,5 +158,6 @@ fn main(
114
158
  chunk_clipmap_storage_coord,
115
159
  chunk_slot_stack,
116
160
  chunk_collection_u32_append,
161
+ chunk_subgroup_atomic_add,
117
162
  ]),
118
163
  });
@@ -3,14 +3,29 @@
3
3
  */
4
4
  export const PROBE_SCHEDULE_SETTINGS: WebGPUStruct;
5
5
  /**
6
- * Schedule (PROBE_CLIPMAP_PROPOSAL_2026_10_27.md §8.5): one thread, after admit, when the frame's active
7
- * list is whole.
8
- * - The period: with P_w the points wanted this frame, `T = max(1, ⌈P_w / U⌉)` frames, so that every
9
- * wanted probe is updated once a period and the period follows the live count. It goes to
6
+ * Schedule (PROBE_CLIPMAP_PROPOSAL_2026_10_27.md §8.3, §8.5): one thread, after admit, when the frame's
7
+ * active list is whole.
8
+ * - The period: with P_w the probes waiting for an update, the NEW and those wanted since their last
9
+ * update, `T = max(1, ⌈P_w / U⌉)` frames, so that every probe a want waits on is updated within a period
10
+ * and the period follows the live count. Retire and admit counted them in
11
+ * `probe_counters[PROBE_COUNTER_WAITING]`, which it empties for the next frame's; the period goes to
10
12
  * `probe_counters[PROBE_COUNTER_PERIOD]` for the due passes.
13
+ * - The next frame's eviction, `probe_eviction`: as many slots as the free ones fall short of the frame's
14
+ * wanted points and the slack, the probes wanted longest ago first. Walking retire's age buckets from the
15
+ * oldest, it asks for each bucket whole while the shortfall takes it, then for what is left of the
16
+ * shortfall from the next; past the youngest bucket it counted anything in, it asks for nothing, so a
17
+ * shortfall the evictable probes cannot cover frees them all and no more. It empties the buckets and
18
+ * the count of a bucket's probes retire evicted.
11
19
  * - A thread an entry of the frame's active list, at `PROBE_DISPATCH_ACTIVE`: the due passes' launch, and
12
20
  * the next frame's retire's, which reads this list.
13
21
  *
22
+ * A hit wants a probe the frame after its probe's update, so a probe hits keep may be wanted on few of
23
+ * its turns. The waiting count is what due lists over a period, where this frame's wanted points would
24
+ * count the probes a pixel wants every frame and miss most of those hits want between their turns.
25
+ *
26
+ * The frame's wanted points are the most a frame can admit, so the next frame's admissions find slots
27
+ * for as many points as this one wanted, and the slack more, before an admission starves.
28
+ *
14
29
  * @type {ComputeShader}
15
30
  */
16
31
  export const shader_probe_clipmap_schedule: ComputeShader;
@@ -1 +1 @@
1
- {"version":3,"file":"shader_probe_clipmap_schedule.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_schedule.js"],"names":[],"mappings":"AAcA;;GAEG;AACH,sCAFU,YAAY,CASM;AAY5B;;;;;;;;;;GAUG;AACH,4CAFU,aAAa,CA0BpB;6BAlE0B,sCAAsC;8BAHrC,kCAAkC"}
1
+ {"version":3,"file":"shader_probe_clipmap_schedule.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_schedule.js"],"names":[],"mappings":"AAeA;;GAEG;AACH,sCAFU,YAAY,CAWM;AAa5B;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,4CAFU,aAAa,CA6DpB;6BAxH0B,sCAAsC;8BAHrC,kCAAkC"}