@forgeax/engine-render 0.1.4 → 0.1.7

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 (979) hide show
  1. package/README.md +268 -640
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/__tests__/device-scope-lifecycle.unit.test.d.ts +2 -0
  4. package/dist/__tests__/device-scope-lifecycle.unit.test.d.ts.map +1 -0
  5. package/dist/__tests__/device-scope-stale-matrix.unit.test.d.ts +2 -0
  6. package/dist/__tests__/device-scope-stale-matrix.unit.test.d.ts.map +1 -0
  7. package/dist/__tests__/fullscreen-feature-plan.unit.test.d.ts +2 -0
  8. package/dist/__tests__/fullscreen-feature-plan.unit.test.d.ts.map +1 -0
  9. package/dist/__tests__/gpu-driven-indirect-raster-evidence.d.ts.map +1 -1
  10. package/dist/__tests__/gpu-driven-view-gpu-evidence.d.ts +6 -0
  11. package/dist/__tests__/gpu-driven-view-gpu-evidence.d.ts.map +1 -1
  12. package/dist/{gpu-driven/view-reference.d.ts → __tests__/gpu-driven-view-reference.d.ts} +4 -4
  13. package/dist/__tests__/gpu-driven-view-reference.d.ts.map +1 -0
  14. package/dist/__tests__/gpu-scene-render-graph-gpu.d.ts.map +1 -1
  15. package/dist/__tests__/ibl/face-uniforms.unit.test.d.ts +2 -0
  16. package/dist/__tests__/ibl/face-uniforms.unit.test.d.ts.map +1 -0
  17. package/dist/__tests__/morph-feature-plan.unit.test.d.ts +2 -0
  18. package/dist/__tests__/morph-feature-plan.unit.test.d.ts.map +1 -0
  19. package/dist/__tests__/render-feature-plan.test-d.d.ts +2 -0
  20. package/dist/__tests__/render-feature-plan.test-d.d.ts.map +1 -0
  21. package/dist/__tests__/render-feature-plan.unit.test.d.ts +2 -0
  22. package/dist/__tests__/render-feature-plan.unit.test.d.ts.map +1 -0
  23. package/dist/__tests__/ssao-config.unit.test.d.ts +2 -0
  24. package/dist/__tests__/ssao-config.unit.test.d.ts.map +1 -0
  25. package/dist/__tests__/standard-clustered-pipeline.unit.test.d.ts +2 -0
  26. package/dist/__tests__/standard-clustered-pipeline.unit.test.d.ts.map +1 -0
  27. package/dist/__tests__/standard-descriptors.test-d.d.ts +2 -0
  28. package/dist/__tests__/standard-descriptors.test-d.d.ts.map +1 -0
  29. package/dist/__tests__/standard-pipeline.dawn.test.d.ts +2 -0
  30. package/dist/__tests__/standard-pipeline.dawn.test.d.ts.map +1 -0
  31. package/dist/__tests__/standard-pipeline.integration.test.d.ts +2 -0
  32. package/dist/__tests__/standard-pipeline.integration.test.d.ts.map +1 -0
  33. package/dist/__tests__/standard-profile-oracle.unit.test.d.ts +2 -0
  34. package/dist/__tests__/standard-profile-oracle.unit.test.d.ts.map +1 -0
  35. package/dist/__tests__/visibility-resource-short-circuit.unit.test.d.ts +2 -0
  36. package/dist/__tests__/visibility-resource-short-circuit.unit.test.d.ts.map +1 -0
  37. package/dist/{renderer/backend-selection.d.ts → assembly/backend-contract.d.ts} +15 -4
  38. package/dist/assembly/backend-contract.d.ts.map +1 -0
  39. package/dist/assembly/factory.d.ts +214 -0
  40. package/dist/assembly/factory.d.ts.map +1 -0
  41. package/dist/assembly/host-contract.d.ts +84 -0
  42. package/dist/assembly/host-contract.d.ts.map +1 -0
  43. package/dist/{renderer → assembly}/material/assembly.d.ts +0 -1
  44. package/dist/{renderer → assembly}/material/assembly.d.ts.map +1 -1
  45. package/dist/assembly/material/pipeline-projection.d.ts +30 -0
  46. package/dist/assembly/material/pipeline-projection.d.ts.map +1 -0
  47. package/dist/authoring.d.ts +11 -11
  48. package/dist/authoring.d.ts.map +1 -1
  49. package/dist/authoring.mjs +25 -3
  50. package/dist/authoring.mjs.map +1 -1
  51. package/dist/chunk-CQXZC5XI.mjs +182 -0
  52. package/dist/chunk-CQXZC5XI.mjs.map +1 -0
  53. package/dist/chunk-HCOGPHKS.mjs +93 -0
  54. package/dist/chunk-HCOGPHKS.mjs.map +1 -0
  55. package/dist/chunk-YEJIUW7D.mjs +1152 -0
  56. package/dist/chunk-YEJIUW7D.mjs.map +1 -0
  57. package/dist/components/__tests__/points-lines-admission.unit.test.d.ts +2 -0
  58. package/dist/components/__tests__/points-lines-admission.unit.test.d.ts.map +1 -0
  59. package/dist/components/__tests__/points-lines-schema.unit.test.d.ts +2 -0
  60. package/dist/components/__tests__/points-lines-schema.unit.test.d.ts.map +1 -0
  61. package/dist/components/__tests__/schema-contract.unit.test.d.ts +2 -0
  62. package/dist/components/__tests__/schema-contract.unit.test.d.ts.map +1 -0
  63. package/dist/components/glyph-text.d.ts +1 -1
  64. package/dist/components/glyph-text.d.ts.map +1 -1
  65. package/dist/components/index.d.ts +4 -2
  66. package/dist/components/index.d.ts.map +1 -1
  67. package/dist/components/instances.d.ts +1 -1
  68. package/dist/components/instances.d.ts.map +1 -1
  69. package/dist/components/light-helpers.d.ts +1 -1
  70. package/dist/components/light-helpers.d.ts.map +1 -1
  71. package/dist/components/lines.d.ts +5 -0
  72. package/dist/components/lines.d.ts.map +1 -0
  73. package/dist/components/mesh-filter.d.ts +7 -7
  74. package/dist/components/mesh-filter.d.ts.map +1 -1
  75. package/dist/components/points.d.ts +15 -0
  76. package/dist/components/points.d.ts.map +1 -0
  77. package/dist/components/scene-instance.d.ts +11 -11
  78. package/dist/components/skybox-background.d.ts +56 -0
  79. package/dist/components/skybox-background.d.ts.map +1 -0
  80. package/dist/components/skylight.d.ts +55 -0
  81. package/dist/components/skylight.d.ts.map +1 -0
  82. package/dist/components/sprite-animation.d.ts +2 -3
  83. package/dist/components/sprite-animation.d.ts.map +1 -1
  84. package/dist/components/sprite-instances.d.ts +1 -1
  85. package/dist/components/sprite-playback-mode.d.ts +6 -1
  86. package/dist/components/sprite-playback-mode.d.ts.map +1 -1
  87. package/dist/components/sprite-region-override.d.ts +2 -3
  88. package/dist/components/sprite-region-override.d.ts.map +1 -1
  89. package/dist/components/tile-layer.d.ts +5 -0
  90. package/dist/components/tile-layer.d.ts.map +1 -1
  91. package/dist/construct-renderer.d.ts +18 -1
  92. package/dist/construct-renderer.d.ts.map +1 -1
  93. package/dist/construct-renderer.mjs +27463 -4
  94. package/dist/construct-renderer.mjs.map +1 -1
  95. package/dist/debug-draw-glue.d.ts +2 -53
  96. package/dist/debug-draw-glue.d.ts.map +1 -1
  97. package/dist/device/device-scope.d.ts +83 -0
  98. package/dist/device/device-scope.d.ts.map +1 -0
  99. package/dist/{gpu-resource-store.d.ts → device/gpu-residency.d.ts} +91 -48
  100. package/dist/device/gpu-residency.d.ts.map +1 -0
  101. package/dist/device/resource-types.d.ts +8 -0
  102. package/dist/device/resource-types.d.ts.map +1 -0
  103. package/dist/engine-metrics.d.ts +2 -2
  104. package/dist/engine-metrics.d.ts.map +1 -1
  105. package/dist/errors/recover.d.ts +28 -1
  106. package/dist/errors/recover.d.ts.map +1 -1
  107. package/dist/errors/render.d.ts +320 -248
  108. package/dist/errors/render.d.ts.map +1 -1
  109. package/dist/features/fullscreen.d.ts +19 -0
  110. package/dist/features/fullscreen.d.ts.map +1 -0
  111. package/dist/features/host.d.ts +32 -26
  112. package/dist/features/host.d.ts.map +1 -1
  113. package/dist/features/morph/morph-feature.d.ts +39 -5
  114. package/dist/features/morph/morph-feature.d.ts.map +1 -1
  115. package/dist/features/plan.d.ts +182 -0
  116. package/dist/features/plan.d.ts.map +1 -0
  117. package/dist/features/prepared-gpu-work.d.ts +21 -17
  118. package/dist/features/prepared-gpu-work.d.ts.map +1 -1
  119. package/dist/features/prepared-graphics-store.d.ts +2 -1
  120. package/dist/features/prepared-graphics-store.d.ts.map +1 -1
  121. package/dist/features/prepared-graphics.d.ts +11 -21
  122. package/dist/features/prepared-graphics.d.ts.map +1 -1
  123. package/dist/features/render-graph-compute.d.ts +2 -2
  124. package/dist/features/render-graph-compute.d.ts.map +1 -1
  125. package/dist/features/render-graph-contribution.d.ts +4 -7
  126. package/dist/features/render-graph-contribution.d.ts.map +1 -1
  127. package/dist/features/render-graph-raster.d.ts +7 -3
  128. package/dist/features/render-graph-raster.d.ts.map +1 -1
  129. package/dist/features/render-graph-resources.d.ts +6 -6
  130. package/dist/features/render-graph-resources.d.ts.map +1 -1
  131. package/dist/features/targets.d.ts +5 -5
  132. package/dist/features/targets.d.ts.map +1 -1
  133. package/dist/features/types.d.ts +11 -190
  134. package/dist/features/types.d.ts.map +1 -1
  135. package/dist/features/vocabulary.d.ts +14 -0
  136. package/dist/features/vocabulary.d.ts.map +1 -0
  137. package/dist/fullscreen-post-process-pass.d.ts +4 -25
  138. package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
  139. package/dist/glyph-text-layout-system.d.ts +3 -2
  140. package/dist/glyph-text-layout-system.d.ts.map +1 -1
  141. package/dist/gpu-driven/batch-topology.d.ts +6 -75
  142. package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
  143. package/dist/gpu-driven/production-raster.d.ts +13 -112
  144. package/dist/gpu-driven/production-raster.d.ts.map +1 -1
  145. package/dist/gpu-driven/view-gpu.d.ts +10 -66
  146. package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
  147. package/dist/gpu-resource.d.ts +3 -3
  148. package/dist/gpu-scene-schema.d.ts +1 -94
  149. package/dist/gpu-scene-schema.d.ts.map +1 -1
  150. package/dist/gpu-scene.d.ts +7 -33
  151. package/dist/gpu-scene.d.ts.map +1 -1
  152. package/dist/hdrp-buffers.d.ts +3 -53
  153. package/dist/hdrp-buffers.d.ts.map +1 -1
  154. package/dist/ibl/IblPipelineCache.d.ts +74 -180
  155. package/dist/ibl/IblPipelineCache.d.ts.map +1 -1
  156. package/dist/ibl/skylight-bind-group.d.ts +1 -1
  157. package/dist/index.d.ts +17 -43
  158. package/dist/index.d.ts.map +1 -1
  159. package/dist/index.mjs +167 -3
  160. package/dist/index.mjs.map +1 -1
  161. package/dist/inspection-types.d.ts +72 -0
  162. package/dist/inspection-types.d.ts.map +1 -0
  163. package/dist/instance-buffer-cache.d.ts +1 -4
  164. package/dist/instance-buffer-cache.d.ts.map +1 -1
  165. package/dist/internal.d.ts +1 -73
  166. package/dist/internal.d.ts.map +1 -1
  167. package/dist/internal.mjs +1 -43
  168. package/dist/internal.mjs.map +1 -1
  169. package/dist/lifecycle.d.ts +36 -2
  170. package/dist/lifecycle.d.ts.map +1 -1
  171. package/dist/materials.d.ts +0 -3
  172. package/dist/materials.d.ts.map +1 -1
  173. package/dist/mesh-material-bindings.d.ts +0 -1
  174. package/dist/mesh-material-bindings.d.ts.map +1 -1
  175. package/dist/pbr-pipeline.d.ts +77 -29
  176. package/dist/pbr-pipeline.d.ts.map +1 -1
  177. package/dist/pipeline/standard-forward-lane.d.ts +5 -0
  178. package/dist/pipeline/standard-forward-lane.d.ts.map +1 -0
  179. package/dist/pipeline/standard-pipeline.d.ts +31 -0
  180. package/dist/pipeline/standard-pipeline.d.ts.map +1 -0
  181. package/dist/pipeline/standard-profile.d.ts +40 -0
  182. package/dist/pipeline/standard-profile.d.ts.map +1 -0
  183. package/dist/pipeline-builder.d.ts +3 -3
  184. package/dist/pipeline-builder.d.ts.map +1 -1
  185. package/dist/pipeline-spec-types.d.ts +24 -0
  186. package/dist/pipeline-spec-types.d.ts.map +1 -0
  187. package/dist/pipeline-spec.d.ts +16 -155
  188. package/dist/pipeline-spec.d.ts.map +1 -1
  189. package/dist/plugin.d.ts +1 -14
  190. package/dist/plugin.d.ts.map +1 -1
  191. package/dist/points-lines/__tests__/bounds.unit.test.d.ts +2 -0
  192. package/dist/points-lines/__tests__/bounds.unit.test.d.ts.map +1 -0
  193. package/dist/points-lines/__tests__/cache-recovery.unit.test.d.ts +2 -0
  194. package/dist/points-lines/__tests__/cache-recovery.unit.test.d.ts.map +1 -0
  195. package/dist/points-lines/__tests__/inspection-lanes.integration.test.d.ts +2 -0
  196. package/dist/points-lines/__tests__/inspection-lanes.integration.test.d.ts.map +1 -0
  197. package/dist/points-lines/__tests__/inspection.unit.test.d.ts +2 -0
  198. package/dist/points-lines/__tests__/inspection.unit.test.d.ts.map +1 -0
  199. package/dist/points-lines/__tests__/lane-structural.integration.test.d.ts +2 -0
  200. package/dist/points-lines/__tests__/lane-structural.integration.test.d.ts.map +1 -0
  201. package/dist/points-lines/__tests__/points-lines.bench.d.ts +2 -0
  202. package/dist/points-lines/__tests__/points-lines.bench.d.ts.map +1 -0
  203. package/dist/points-lines/__tests__/raster-lane.integration.test.d.ts +2 -0
  204. package/dist/points-lines/__tests__/raster-lane.integration.test.d.ts.map +1 -0
  205. package/dist/points-lines/__tests__/snapshot.unit.test.d.ts +2 -0
  206. package/dist/points-lines/__tests__/snapshot.unit.test.d.ts.map +1 -0
  207. package/dist/points-lines/__tests__/view-ubo.unit.test.d.ts +2 -0
  208. package/dist/points-lines/__tests__/view-ubo.unit.test.d.ts.map +1 -0
  209. package/dist/points-lines/admission.d.ts +36 -0
  210. package/dist/points-lines/admission.d.ts.map +1 -0
  211. package/dist/points-lines/bounds.d.ts +8 -0
  212. package/dist/points-lines/bounds.d.ts.map +1 -0
  213. package/dist/points-lines/expansion-cache.d.ts +28 -0
  214. package/dist/points-lines/expansion-cache.d.ts.map +1 -0
  215. package/dist/points-lines/inspection.d.ts +53 -0
  216. package/dist/points-lines/inspection.d.ts.map +1 -0
  217. package/dist/points-lines/prepare.d.ts +67 -0
  218. package/dist/points-lines/prepare.d.ts.map +1 -0
  219. package/dist/points-lines/record.d.ts +60 -0
  220. package/dist/points-lines/record.d.ts.map +1 -0
  221. package/dist/points-lines/snapshot.d.ts +49 -0
  222. package/dist/points-lines/snapshot.d.ts.map +1 -0
  223. package/dist/post-process-errors.d.ts +18 -18
  224. package/dist/post-process-errors.d.ts.map +1 -1
  225. package/dist/prepare/prepared-graphics-resolver.d.ts +2 -5
  226. package/dist/prepare/prepared-graphics-resolver.d.ts.map +1 -1
  227. package/dist/record/__tests__/typed-frame-graph-plan.unit.test.d.ts +2 -0
  228. package/dist/record/__tests__/typed-frame-graph-plan.unit.test.d.ts.map +1 -0
  229. package/dist/record/frame-lighting.d.ts +7 -73
  230. package/dist/record/frame-lighting.d.ts.map +1 -1
  231. package/dist/record/frame-snapshot.d.ts +44 -44
  232. package/dist/record/frame-snapshot.d.ts.map +1 -1
  233. package/dist/record/frame-targets.d.ts +5 -38
  234. package/dist/record/frame-targets.d.ts.map +1 -1
  235. package/dist/record/frame.d.ts +64 -52
  236. package/dist/record/frame.d.ts.map +1 -1
  237. package/dist/record/helpers.d.ts +3 -2
  238. package/dist/record/helpers.d.ts.map +1 -1
  239. package/dist/record/main-pass-geometry.d.ts +12 -4
  240. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  241. package/dist/record/main-pass-material.d.ts +90 -59
  242. package/dist/record/main-pass-material.d.ts.map +1 -1
  243. package/dist/record/main-pass-sprite-draws.d.ts +28 -3
  244. package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
  245. package/dist/record/main-pass.d.ts +1 -1
  246. package/dist/record/main-pass.d.ts.map +1 -1
  247. package/dist/record/mesh-ssbo.d.ts +276 -3
  248. package/dist/record/mesh-ssbo.d.ts.map +1 -1
  249. package/dist/record/render-context.d.ts +252 -0
  250. package/dist/record/render-context.d.ts.map +1 -0
  251. package/dist/record/shadow-pass.d.ts +15 -88
  252. package/dist/record/shadow-pass.d.ts.map +1 -1
  253. package/dist/record/skybox-post-pass.d.ts +1 -1
  254. package/dist/record/skybox-post-pass.d.ts.map +1 -1
  255. package/dist/record/typed-frame-graph.d.ts +29 -7
  256. package/dist/record/typed-frame-graph.d.ts.map +1 -1
  257. package/dist/record/view-ubo.d.ts +9 -3
  258. package/dist/record/view-ubo.d.ts.map +1 -1
  259. package/dist/render-contract.d.ts +424 -0
  260. package/dist/render-contract.d.ts.map +1 -0
  261. package/dist/render-data.d.ts.map +1 -1
  262. package/dist/render-graph-primitives.d.ts +14 -362
  263. package/dist/render-graph-primitives.d.ts.map +1 -1
  264. package/dist/render-pipeline.d.ts +25 -64
  265. package/dist/render-pipeline.d.ts.map +1 -1
  266. package/dist/render-system-extract.d.ts +110 -338
  267. package/dist/render-system-extract.d.ts.map +1 -1
  268. package/dist/render-system.d.ts +24 -713
  269. package/dist/render-system.d.ts.map +1 -1
  270. package/dist/scene/render-scene-types.d.ts +61 -0
  271. package/dist/scene/render-scene-types.d.ts.map +1 -0
  272. package/dist/scene/render-scene.d.ts +112 -0
  273. package/dist/scene/render-scene.d.ts.map +1 -0
  274. package/dist/scene-instances/post-spawn-resolve-joints.d.ts +2 -5
  275. package/dist/scene-instances/post-spawn-resolve-joints.d.ts.map +1 -1
  276. package/dist/ssao-buffers.d.ts +22 -3
  277. package/dist/ssao-buffers.d.ts.map +1 -1
  278. package/dist/systems/skin-palette-allocator.d.ts +2 -4
  279. package/dist/systems/skin-palette-allocator.d.ts.map +1 -1
  280. package/dist/systems/skin-palette-types.d.ts +14 -0
  281. package/dist/systems/skin-palette-types.d.ts.map +1 -0
  282. package/dist/systems/transparent-sort-config.d.ts +23 -2
  283. package/dist/systems/transparent-sort-config.d.ts.map +1 -1
  284. package/dist/tilemap-chunk-extract-system.d.ts +4 -3
  285. package/dist/tilemap-chunk-extract-system.d.ts.map +1 -1
  286. package/dist/typed-render-graph-primitives.d.ts +2 -4
  287. package/dist/typed-render-graph-primitives.d.ts.map +1 -1
  288. package/dist/typed-shadow-passes.d.ts +3 -3
  289. package/dist/typed-shadow-passes.d.ts.map +1 -1
  290. package/package.json +23 -23
  291. package/src/__tests__/authoring-path.unit.test.ts +9 -0
  292. package/src/__tests__/backend-selection-rhi-capture.test-d.ts +1 -1
  293. package/src/__tests__/callback-inference.test-d.ts +6 -3
  294. package/src/__tests__/cluster-grid-default-surface.unit.test.ts +7 -4
  295. package/src/__tests__/construct-renderer-identity.unit.test.ts +15 -2
  296. package/src/__tests__/device-scope-lifecycle.unit.test.ts +151 -0
  297. package/src/__tests__/device-scope-stale-matrix.unit.test.ts +42 -0
  298. package/src/__tests__/direct-extract-consumer-contract.test-d.ts +8 -19
  299. package/src/__tests__/direct-light-snapshot.unit.test.ts +3 -13
  300. package/src/__tests__/factory-contract.integration.test.ts +158 -209
  301. package/src/__tests__/fallback-row-stride-surface.unit.test.ts +1 -4
  302. package/src/__tests__/frame-observation.contract.test.ts +17 -8
  303. package/src/__tests__/fullscreen-feature-plan.unit.test.ts +32 -0
  304. package/src/__tests__/glyph-text-layout-recovery.unit.test.ts +6 -11
  305. package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +21 -18
  306. package/src/__tests__/gpu-driven-production.integration.test.ts +32 -205
  307. package/src/__tests__/gpu-driven-scaling.unit.test.ts +4 -7
  308. package/src/__tests__/gpu-driven-view-gpu-evidence.ts +87 -8
  309. package/src/__tests__/gpu-driven-view-graph.integration.test.ts +170 -8
  310. package/src/{gpu-driven/view-reference.ts → __tests__/gpu-driven-view-reference.ts} +4 -3
  311. package/src/__tests__/gpu-driven-view.browser.test.ts +12 -1
  312. package/src/__tests__/gpu-driven-view.dawn.test.ts +12 -1
  313. package/src/__tests__/gpu-driven-view.unit.test.ts +11 -267
  314. package/src/__tests__/gpu-resource-store-stride.unit.test.ts +3 -3
  315. package/src/__tests__/gpu-scene-render-graph-gpu.ts +2 -3
  316. package/src/__tests__/gpu-scene.dawn.test.ts +2 -6
  317. package/src/__tests__/gpu-scene.unit.test.ts +10 -24
  318. package/src/__tests__/ibl/face-uniforms.unit.test.ts +46 -0
  319. package/src/__tests__/infinite-grid-no-vertex-probe.unit.test.ts +34 -66
  320. package/src/__tests__/ktx2-basis-gpu-consumer.dawn.test.ts +14 -5
  321. package/src/__tests__/light-buffer-layout.unit.test.ts +4 -49
  322. package/src/__tests__/material-cooked-projection.unit.test.ts +31 -58
  323. package/src/__tests__/material-snapshot-mutation.integration.test.ts +10 -15
  324. package/src/__tests__/material-static-dynamic-route.unit.test.ts +31 -50
  325. package/src/__tests__/material-stride-surface.unit.test.ts +2 -2
  326. package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +7 -8
  327. package/src/__tests__/morph-culling-reentry.dawn.test.ts +29 -38
  328. package/src/__tests__/morph-feature-gpu-work.integration.test.ts +45 -43
  329. package/src/__tests__/morph-feature-plan.unit.test.ts +53 -0
  330. package/src/__tests__/morph-visual-evidence.integration.test.ts +47 -33
  331. package/src/__tests__/nineslice-handle-surface.unit.test.ts +11 -6
  332. package/src/__tests__/post-process-error-policy-owner.unit.test.ts +6 -13
  333. package/src/__tests__/prepared-graphics-capability.unit.test.ts +6 -81
  334. package/src/__tests__/prepared-graphics-generation.unit.test.ts +3 -17
  335. package/src/__tests__/prepared-graphics-lifecycle.unit.test.ts +6 -74
  336. package/src/__tests__/prepared-graphics-resolver.unit.test.ts +1 -3
  337. package/src/__tests__/prepared-graphics-snapshot.unit.test.ts +2 -2
  338. package/src/__tests__/profiler-default-off.test.ts +1 -1
  339. package/src/__tests__/profiler-phase-catalog.test.ts +2 -2
  340. package/src/__tests__/profiler-phase-integration.test.ts +5 -13
  341. package/src/__tests__/profiler-phase-token.test.ts +3 -8
  342. package/src/__tests__/public-surface.test-d.ts +65 -3
  343. package/src/__tests__/recover-error-policy-owner.unit.test.ts +1 -21
  344. package/src/__tests__/render-error-code-owner.test-d.ts +24 -23
  345. package/src/__tests__/render-error-detail-ssot.test-d.ts +0 -95
  346. package/src/__tests__/render-error-exhaustive.test-d.ts +27 -1
  347. package/src/__tests__/render-feature-assembly.test-d.ts +8 -16
  348. package/src/__tests__/render-feature-context.test-d.ts +16 -40
  349. package/src/__tests__/render-feature-contract.test-d.ts +9 -18
  350. package/src/__tests__/render-feature-diagnostics.unit.test.ts +6 -8
  351. package/src/__tests__/render-feature-docs.unit.test.ts +8 -7
  352. package/src/__tests__/render-feature-gpu-work.integration.test.ts +14 -75
  353. package/src/__tests__/render-feature-plan.test-d.ts +161 -0
  354. package/src/__tests__/render-feature-plan.unit.test.ts +170 -0
  355. package/src/__tests__/render-feature-prepared-diagnostics.unit.test.ts +8 -19
  356. package/src/__tests__/render-feature-prepared-errors.test-d.ts +3 -6
  357. package/src/__tests__/render-feature-prepared-graphics-docs.unit.test.ts +10 -71
  358. package/src/__tests__/render-feature-prepared-graphics-isolation.unit.test.ts +11 -61
  359. package/src/__tests__/render-feature-prepared-graphics-negative.test-d.ts +20 -43
  360. package/src/__tests__/render-feature-prepared-graphics.test-d.ts +56 -78
  361. package/src/__tests__/render-feature-public-surface.test-d.ts +6 -24
  362. package/src/__tests__/render-feature-retry.unit.test.ts +7 -48
  363. package/src/__tests__/render-feature-scheduling.unit.test.ts +15 -27
  364. package/src/__tests__/render-feature-stage-failed-policy-owner.unit.test.ts +0 -9
  365. package/src/__tests__/render-feature-stage-fault-recovery.unit.test.ts +19 -109
  366. package/src/__tests__/render-feature-visibility-context.test-d.ts +1 -6
  367. package/src/__tests__/render-feature-visibility-context.unit.test.ts +1 -2
  368. package/src/__tests__/render-feature-zero-work.unit.test.ts +14 -20
  369. package/src/__tests__/render-scene-projection.unit.test.ts +73 -111
  370. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +82 -95
  371. package/src/__tests__/renderer-lifecycle.integration.test.ts +49 -1
  372. package/src/__tests__/shader-stage-surface.unit.test.ts +14 -9
  373. package/src/__tests__/ssao-config.unit.test.ts +36 -0
  374. package/src/__tests__/ssao-kernel-surface.unit.test.ts +4 -5
  375. package/src/__tests__/standard-clustered-pipeline.unit.test.ts +28 -0
  376. package/src/__tests__/standard-descriptors.test-d.ts +22 -0
  377. package/src/__tests__/standard-pipeline.dawn.test.ts +61 -0
  378. package/src/__tests__/standard-pipeline.integration.test.ts +124 -0
  379. package/src/__tests__/standard-profile-oracle.unit.test.ts +62 -0
  380. package/src/__tests__/surface-configuration.unit.test.ts +1 -2
  381. package/src/__tests__/texture-usage-surface.unit.test.ts +3 -6
  382. package/src/__tests__/typed-pipeline-topology.unit.test.ts +40 -206
  383. package/src/__tests__/urp-pipeline-targets.unit.test.ts +1 -11
  384. package/src/__tests__/urp-pipeline.unit.test.ts +2 -2
  385. package/src/__tests__/vertex-color-pipeline.unit.test.ts +10 -2
  386. package/src/__tests__/vertex-layout-consumer.unit.test.ts +1 -1
  387. package/src/__tests__/visibility-diagnostics.integration.test.ts +7 -11
  388. package/src/__tests__/visibility-extract.integration.test.ts +20 -11
  389. package/src/__tests__/visibility-extract.perf.test.ts +66 -32
  390. package/src/__tests__/visibility-feature-dedup.integration.test.ts +2 -4
  391. package/src/__tests__/visibility-hidden-report.unit.test.ts +1 -2
  392. package/src/__tests__/visibility-instances.integration.test.ts +7 -11
  393. package/src/__tests__/visibility-multi-world.integration.test.ts +38 -6
  394. package/src/__tests__/visibility-producer-matrix.integration.test.ts +18 -41
  395. package/src/__tests__/visibility-public-surface.test-d.ts +10 -9
  396. package/src/__tests__/visibility-resolver.unit.test.ts +2 -1
  397. package/src/__tests__/visibility-resource-short-circuit.unit.test.ts +80 -0
  398. package/src/__tests__/visibility-snapshot-contract.unit.test.ts +2 -1
  399. package/src/__tests__/vocabulary.unit.test.ts +1 -1
  400. package/src/assembly/backend-contract.ts +57 -0
  401. package/src/{renderer/renderer-factory.ts → assembly/factory.ts} +1741 -2699
  402. package/src/assembly/host-contract.ts +101 -0
  403. package/src/{renderer → assembly}/material/assembly.ts +2 -7
  404. package/src/assembly/material/pipeline-projection.ts +63 -0
  405. package/src/authoring.ts +15 -22
  406. package/src/components/__tests__/points-lines-admission.unit.test.ts +219 -0
  407. package/src/components/__tests__/points-lines-schema.unit.test.ts +56 -0
  408. package/src/components/__tests__/schema-contract.unit.test.ts +10 -0
  409. package/src/components/glyph-text.ts +3 -2
  410. package/src/components/index.ts +14 -9
  411. package/src/components/instances.ts +4 -5
  412. package/src/components/light-helpers.ts +1 -1
  413. package/src/components/lines.ts +18 -0
  414. package/src/components/mesh-filter.ts +11 -10
  415. package/src/components/points.ts +40 -0
  416. package/src/components/post-process-params.ts +3 -3
  417. package/src/components/scene-instance.ts +12 -12
  418. package/src/components/skybox-background.ts +103 -0
  419. package/src/components/{environment.ts → skylight.ts} +2 -110
  420. package/src/components/sprite-animation.ts +3 -4
  421. package/src/components/sprite-instances.ts +2 -2
  422. package/src/components/sprite-playback-mode.ts +7 -1
  423. package/src/components/sprite-region-override.ts +2 -3
  424. package/src/components/tile-layer.ts +8 -2
  425. package/src/construct-renderer.ts +67 -4
  426. package/src/debug-draw-glue.ts +8 -129
  427. package/src/device/device-scope.ts +306 -0
  428. package/src/{gpu-resource-store.ts → device/gpu-residency.ts} +540 -639
  429. package/src/device/resource-types.ts +20 -0
  430. package/src/engine-metrics.ts +12 -19
  431. package/src/errors/recover.ts +45 -3
  432. package/src/errors/render.ts +469 -429
  433. package/src/features/fullscreen.ts +46 -0
  434. package/src/features/host.ts +575 -309
  435. package/src/features/morph/morph-feature.ts +195 -105
  436. package/src/features/plan.ts +447 -0
  437. package/src/features/prepared-gpu-work.ts +72 -15
  438. package/src/features/prepared-graphics-store.ts +20 -6
  439. package/src/features/prepared-graphics.ts +12 -71
  440. package/src/features/render-graph-compute.ts +14 -5
  441. package/src/features/render-graph-contribution.ts +43 -63
  442. package/src/features/render-graph-raster.ts +32 -8
  443. package/src/features/render-graph-resources.ts +25 -23
  444. package/src/features/targets.ts +8 -21
  445. package/src/features/types.ts +39 -227
  446. package/src/features/vocabulary.ts +24 -0
  447. package/src/fullscreen-post-process-pass.ts +14 -57
  448. package/src/glyph-text-layout-system.ts +70 -96
  449. package/src/gpu-driven/batch-topology.ts +24 -202
  450. package/src/gpu-driven/production-raster.ts +99 -1109
  451. package/src/gpu-driven/view-gpu.ts +46 -192
  452. package/src/gpu-resource.ts +4 -4
  453. package/src/gpu-scene-schema.ts +11 -99
  454. package/src/gpu-scene.ts +231 -364
  455. package/src/hdrp-buffers.ts +12 -221
  456. package/src/ibl/IblPipelineCache.ts +471 -711
  457. package/src/ibl/skylight-bind-group.ts +4 -4
  458. package/src/index.ts +75 -217
  459. package/src/inspection-types.ts +80 -0
  460. package/src/instance-buffer-cache.ts +1 -29
  461. package/src/internal.ts +6 -85
  462. package/src/lifecycle.ts +64 -19
  463. package/src/materials.ts +7 -29
  464. package/src/mesh-material-bindings.ts +0 -1
  465. package/src/pbr-pipeline.ts +441 -53
  466. package/src/{urp-pipeline.ts → pipeline/standard-forward-lane.ts} +38 -149
  467. package/src/{hdrp-pipeline.ts → pipeline/standard-pipeline.ts} +221 -191
  468. package/src/pipeline/standard-profile.ts +65 -0
  469. package/src/pipeline-builder.ts +60 -64
  470. package/src/pipeline-spec-types.ts +28 -0
  471. package/src/pipeline-spec.ts +72 -609
  472. package/src/plugin.ts +7 -75
  473. package/src/points-lines/__tests__/bounds.unit.test.ts +21 -0
  474. package/src/points-lines/__tests__/cache-recovery.unit.test.ts +157 -0
  475. package/src/points-lines/__tests__/inspection-lanes.integration.test.ts +146 -0
  476. package/src/points-lines/__tests__/inspection.unit.test.ts +173 -0
  477. package/src/points-lines/__tests__/lane-structural.integration.test.ts +122 -0
  478. package/src/points-lines/__tests__/points-lines.bench.ts +400 -0
  479. package/src/points-lines/__tests__/raster-lane.integration.test.ts +44 -0
  480. package/src/points-lines/__tests__/snapshot.unit.test.ts +101 -0
  481. package/src/points-lines/__tests__/view-ubo.unit.test.ts +73 -0
  482. package/src/points-lines/admission.ts +238 -0
  483. package/src/points-lines/bounds.ts +23 -0
  484. package/src/points-lines/expansion-cache.ts +215 -0
  485. package/src/points-lines/inspection.ts +97 -0
  486. package/src/points-lines/prepare.ts +177 -0
  487. package/src/points-lines/record.ts +125 -0
  488. package/src/points-lines/snapshot.ts +130 -0
  489. package/src/post-process-errors.ts +31 -33
  490. package/src/prepare/prepared-graphics-resolver.ts +5 -4
  491. package/src/record/__tests__/material-slot-plan.unit.test.ts +1 -33
  492. package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +41 -0
  493. package/src/record/__tests__/video-texture-view.unit.test.ts +1 -1
  494. package/src/record/frame-lighting.ts +88 -197
  495. package/src/record/frame-snapshot.ts +45 -60
  496. package/src/record/frame-targets.ts +8 -243
  497. package/src/record/frame.ts +521 -797
  498. package/src/record/helpers.ts +6 -11
  499. package/src/record/main-pass-geometry.ts +198 -147
  500. package/src/record/main-pass-material.ts +517 -190
  501. package/src/record/main-pass-sprite-draws.ts +199 -118
  502. package/src/record/main-pass.ts +166 -144
  503. package/src/record/mesh-ssbo.ts +517 -38
  504. package/src/record/render-context.ts +384 -0
  505. package/src/record/shadow-pass.ts +154 -1843
  506. package/src/record/skybox-post-pass.ts +14 -15
  507. package/src/record/typed-frame-graph.ts +224 -262
  508. package/src/record/view-ubo.ts +53 -37
  509. package/src/render-contract.ts +582 -0
  510. package/src/render-data.ts +3 -3
  511. package/src/render-graph-primitives.ts +76 -851
  512. package/src/render-pipeline.ts +104 -188
  513. package/src/render-system-extract.ts +1114 -1732
  514. package/src/render-system.ts +614 -1939
  515. package/src/scene/render-scene-types.ts +67 -0
  516. package/src/scene/render-scene.ts +1087 -0
  517. package/src/scene-instances/post-spawn-resolve-joints.ts +4 -7
  518. package/src/ssao-buffers.ts +167 -8
  519. package/src/systems/skin-palette-allocator.ts +21 -64
  520. package/src/systems/skin-palette-types.ts +14 -0
  521. package/src/systems/transparent-sort-config.ts +25 -3
  522. package/src/tilemap-chunk-extract-system.ts +53 -70
  523. package/src/typed-render-graph-primitives.ts +35 -24
  524. package/src/typed-shadow-passes.ts +26 -56
  525. package/dist/__tests__/analytic-sky-contract.test.d.ts +0 -2
  526. package/dist/__tests__/analytic-sky-contract.test.d.ts.map +0 -1
  527. package/dist/__tests__/analytic-sky-falsify.test.d.ts +0 -2
  528. package/dist/__tests__/analytic-sky-falsify.test.d.ts.map +0 -1
  529. package/dist/__tests__/assets-runtime-handoff.integration.test.d.ts +0 -2
  530. package/dist/__tests__/assets-runtime-handoff.integration.test.d.ts.map +0 -1
  531. package/dist/__tests__/backend-selection.integration.test.d.ts +0 -2
  532. package/dist/__tests__/backend-selection.integration.test.d.ts.map +0 -1
  533. package/dist/__tests__/camera-temporal.test-d.d.ts +0 -2
  534. package/dist/__tests__/camera-temporal.test-d.d.ts.map +0 -1
  535. package/dist/__tests__/camera-temporal.test.d.ts +0 -2
  536. package/dist/__tests__/camera-temporal.test.d.ts.map +0 -1
  537. package/dist/__tests__/directional-shadow-probe-atlas-layout.unit.test.d.ts +0 -2
  538. package/dist/__tests__/directional-shadow-probe-atlas-layout.unit.test.d.ts.map +0 -1
  539. package/dist/__tests__/environment-inspection.test.d.ts +0 -2
  540. package/dist/__tests__/environment-inspection.test.d.ts.map +0 -1
  541. package/dist/__tests__/fog-ai-user.fixture.d.ts +0 -2
  542. package/dist/__tests__/fog-ai-user.fixture.d.ts.map +0 -1
  543. package/dist/__tests__/fog-custom-render-feature.fixture.d.ts +0 -16
  544. package/dist/__tests__/fog-custom-render-feature.fixture.d.ts.map +0 -1
  545. package/dist/__tests__/fog-custom-render-feature.integration.test.d.ts +0 -2
  546. package/dist/__tests__/fog-custom-render-feature.integration.test.d.ts.map +0 -1
  547. package/dist/__tests__/fog-inspection.integration.test.d.ts +0 -2
  548. package/dist/__tests__/fog-inspection.integration.test.d.ts.map +0 -1
  549. package/dist/__tests__/fog-owner-state.integration.test.d.ts +0 -2
  550. package/dist/__tests__/fog-owner-state.integration.test.d.ts.map +0 -1
  551. package/dist/__tests__/fog-pipeline-topology.integration.test.d.ts +0 -2
  552. package/dist/__tests__/fog-pipeline-topology.integration.test.d.ts.map +0 -1
  553. package/dist/__tests__/fog-public-surface.integration.test.d.ts +0 -2
  554. package/dist/__tests__/fog-public-surface.integration.test.d.ts.map +0 -1
  555. package/dist/__tests__/fog-ray-contract.integration.test.d.ts +0 -2
  556. package/dist/__tests__/fog-ray-contract.integration.test.d.ts.map +0 -1
  557. package/dist/__tests__/fog-source-absence.integration.test.d.ts +0 -2
  558. package/dist/__tests__/fog-source-absence.integration.test.d.ts.map +0 -1
  559. package/dist/__tests__/fog-temporal.integration.test.d.ts +0 -2
  560. package/dist/__tests__/fog-temporal.integration.test.d.ts.map +0 -1
  561. package/dist/__tests__/frame-chain.unit.test.d.ts +0 -2
  562. package/dist/__tests__/frame-chain.unit.test.d.ts.map +0 -1
  563. package/dist/__tests__/gpu-scene-field-type-owner.test-d.d.ts +0 -2
  564. package/dist/__tests__/gpu-scene-field-type-owner.test-d.d.ts.map +0 -1
  565. package/dist/__tests__/hdrp-pipeline.unit.test.d.ts +0 -2
  566. package/dist/__tests__/hdrp-pipeline.unit.test.d.ts.map +0 -1
  567. package/dist/__tests__/ibl/ibl-fallback.unit.test.d.ts +0 -2
  568. package/dist/__tests__/ibl/ibl-fallback.unit.test.d.ts.map +0 -1
  569. package/dist/__tests__/material-derived-error-stop-draw.integration.test.d.ts +0 -2
  570. package/dist/__tests__/material-derived-error-stop-draw.integration.test.d.ts.map +0 -1
  571. package/dist/__tests__/material-derived-payload.integration.test.d.ts +0 -2
  572. package/dist/__tests__/material-derived-payload.integration.test.d.ts.map +0 -1
  573. package/dist/__tests__/materials.unit.test.d.ts +0 -2
  574. package/dist/__tests__/materials.unit.test.d.ts.map +0 -1
  575. package/dist/__tests__/param-schema-projection-census.unit.test.d.ts +0 -2
  576. package/dist/__tests__/param-schema-projection-census.unit.test.d.ts.map +0 -1
  577. package/dist/__tests__/persistent-render-scene.unit.test.d.ts +0 -2
  578. package/dist/__tests__/persistent-render-scene.unit.test.d.ts.map +0 -1
  579. package/dist/__tests__/pipeline-error-policy-owner.unit.test.d.ts +0 -2
  580. package/dist/__tests__/pipeline-error-policy-owner.unit.test.d.ts.map +0 -1
  581. package/dist/__tests__/pipeline-pass-trace.test.d.ts +0 -2
  582. package/dist/__tests__/pipeline-pass-trace.test.d.ts.map +0 -1
  583. package/dist/__tests__/pipeline-stage-owner.test-d.d.ts +0 -2
  584. package/dist/__tests__/pipeline-stage-owner.test-d.d.ts.map +0 -1
  585. package/dist/__tests__/plugin.test.d.ts +0 -2
  586. package/dist/__tests__/plugin.test.d.ts.map +0 -1
  587. package/dist/__tests__/prepared-graphics-backend-matrix.unit.test.d.ts +0 -2
  588. package/dist/__tests__/prepared-graphics-backend-matrix.unit.test.d.ts.map +0 -1
  589. package/dist/__tests__/prepared-graphics-resolver-recording.unit.test.d.ts +0 -2
  590. package/dist/__tests__/prepared-graphics-resolver-recording.unit.test.d.ts.map +0 -1
  591. package/dist/__tests__/public-pipeline-id.test-d.d.ts +0 -2
  592. package/dist/__tests__/public-pipeline-id.test-d.d.ts.map +0 -1
  593. package/dist/__tests__/render-dispatch-membership.unit.test.d.ts +0 -2
  594. package/dist/__tests__/render-dispatch-membership.unit.test.d.ts.map +0 -1
  595. package/dist/__tests__/render-feature-construction.unit.test.d.ts +0 -2
  596. package/dist/__tests__/render-feature-construction.unit.test.d.ts.map +0 -1
  597. package/dist/__tests__/render-feature-error-code-owner.test-d.d.ts +0 -2
  598. package/dist/__tests__/render-feature-error-code-owner.test-d.d.ts.map +0 -1
  599. package/dist/__tests__/render-feature-gpu-buffer-usage-owner.test-d.d.ts +0 -2
  600. package/dist/__tests__/render-feature-gpu-buffer-usage-owner.test-d.d.ts.map +0 -1
  601. package/dist/__tests__/render-feature-graph.integration.test.d.ts +0 -2
  602. package/dist/__tests__/render-feature-graph.integration.test.d.ts.map +0 -1
  603. package/dist/__tests__/render-feature-graphics-staging.unit.test.d.ts +0 -2
  604. package/dist/__tests__/render-feature-graphics-staging.unit.test.d.ts.map +0 -1
  605. package/dist/__tests__/render-feature-order.unit.test.d.ts +0 -2
  606. package/dist/__tests__/render-feature-order.unit.test.d.ts.map +0 -1
  607. package/dist/__tests__/render-feature-prepared-graphics-recording.unit.test.d.ts +0 -2
  608. package/dist/__tests__/render-feature-prepared-graphics-recording.unit.test.d.ts.map +0 -1
  609. package/dist/__tests__/render-feature-staging.unit.test.d.ts +0 -2
  610. package/dist/__tests__/render-feature-staging.unit.test.d.ts.map +0 -1
  611. package/dist/__tests__/renderer-render-scene-public.test-d.d.ts +0 -2
  612. package/dist/__tests__/renderer-render-scene-public.test-d.d.ts.map +0 -1
  613. package/dist/__tests__/scene-temporal-coverage.unit.test.d.ts +0 -2
  614. package/dist/__tests__/scene-temporal-coverage.unit.test.d.ts.map +0 -1
  615. package/dist/__tests__/sun-snapshot-consumers.test.d.ts +0 -2
  616. package/dist/__tests__/sun-snapshot-consumers.test.d.ts.map +0 -1
  617. package/dist/__tests__/surface-backend-kind-owner.test-d.d.ts +0 -2
  618. package/dist/__tests__/surface-backend-kind-owner.test-d.d.ts.map +0 -1
  619. package/dist/__tests__/taa-history-store.unit.test.d.ts +0 -2
  620. package/dist/__tests__/taa-history-store.unit.test.d.ts.map +0 -1
  621. package/dist/__tests__/temporal-frame-transaction.integration.test.d.ts +0 -2
  622. package/dist/__tests__/temporal-frame-transaction.integration.test.d.ts.map +0 -1
  623. package/dist/__tests__/temporal-inspection.unit.test.d.ts +0 -2
  624. package/dist/__tests__/temporal-inspection.unit.test.d.ts.map +0 -1
  625. package/dist/assets/__tests__/asset-decoders.unit.test.d.ts +0 -2
  626. package/dist/assets/__tests__/asset-decoders.unit.test.d.ts.map +0 -1
  627. package/dist/assets/__tests__/mipmap-generator.unit.test.d.ts +0 -2
  628. package/dist/assets/__tests__/mipmap-generator.unit.test.d.ts.map +0 -1
  629. package/dist/assets/__tests__/render-asset-port.unit.test.d.ts +0 -2
  630. package/dist/assets/__tests__/render-asset-port.unit.test.d.ts.map +0 -1
  631. package/dist/assets/__tests__/resolve-render-asset.unit.test.d.ts +0 -2
  632. package/dist/assets/__tests__/resolve-render-asset.unit.test.d.ts.map +0 -1
  633. package/dist/chunk-DJIMFYX6.mjs +0 -1439
  634. package/dist/chunk-DJIMFYX6.mjs.map +0 -1
  635. package/dist/chunk-F2SIOCRM.mjs +0 -7423
  636. package/dist/chunk-F2SIOCRM.mjs.map +0 -1
  637. package/dist/chunk-JMSB7AR4.mjs +0 -3
  638. package/dist/chunk-JMSB7AR4.mjs.map +0 -1
  639. package/dist/chunk-KHI6K4C6.mjs +0 -659
  640. package/dist/chunk-KHI6K4C6.mjs.map +0 -1
  641. package/dist/chunk-PORZOL2V.mjs +0 -569
  642. package/dist/chunk-PORZOL2V.mjs.map +0 -1
  643. package/dist/chunk-UOJGMDGP.mjs +0 -29913
  644. package/dist/chunk-UOJGMDGP.mjs.map +0 -1
  645. package/dist/components/__tests__/atmosphere-red.test.d.ts +0 -2
  646. package/dist/components/__tests__/atmosphere-red.test.d.ts.map +0 -1
  647. package/dist/components/__tests__/atmosphere.test.d.ts +0 -2
  648. package/dist/components/__tests__/atmosphere.test.d.ts.map +0 -1
  649. package/dist/components/__tests__/fog.test-d.d.ts +0 -2
  650. package/dist/components/__tests__/fog.test-d.d.ts.map +0 -1
  651. package/dist/components/__tests__/fog.test.d.ts +0 -2
  652. package/dist/components/__tests__/fog.test.d.ts.map +0 -1
  653. package/dist/components/atmosphere.d.ts +0 -18
  654. package/dist/components/atmosphere.d.ts.map +0 -1
  655. package/dist/components/environment.d.ts +0 -115
  656. package/dist/components/environment.d.ts.map +0 -1
  657. package/dist/deferred-lighting.d.ts +0 -13
  658. package/dist/deferred-lighting.d.ts.map +0 -1
  659. package/dist/dynamic-texture-store.d.ts +0 -42
  660. package/dist/dynamic-texture-store.d.ts.map +0 -1
  661. package/dist/environment/__tests__/failure-injection.test.d.ts +0 -2
  662. package/dist/environment/__tests__/failure-injection.test.d.ts.map +0 -1
  663. package/dist/environment/__tests__/generation-red.test.d.ts +0 -2
  664. package/dist/environment/__tests__/generation-red.test.d.ts.map +0 -1
  665. package/dist/environment/__tests__/generation.test.d.ts +0 -2
  666. package/dist/environment/__tests__/generation.test.d.ts.map +0 -1
  667. package/dist/environment/__tests__/graph-transaction.test.d.ts +0 -2
  668. package/dist/environment/__tests__/graph-transaction.test.d.ts.map +0 -1
  669. package/dist/environment/__tests__/inspection-red.test.d.ts +0 -2
  670. package/dist/environment/__tests__/inspection-red.test.d.ts.map +0 -1
  671. package/dist/environment/__tests__/lifecycle-evidence.test.d.ts +0 -2
  672. package/dist/environment/__tests__/lifecycle-evidence.test.d.ts.map +0 -1
  673. package/dist/environment/__tests__/pipeline-topology.test.d.ts +0 -2
  674. package/dist/environment/__tests__/pipeline-topology.test.d.ts.map +0 -1
  675. package/dist/environment/__tests__/readiness.test.d.ts +0 -2
  676. package/dist/environment/__tests__/readiness.test.d.ts.map +0 -1
  677. package/dist/environment/__tests__/recovery.test.d.ts +0 -2
  678. package/dist/environment/__tests__/recovery.test.d.ts.map +0 -1
  679. package/dist/environment/__tests__/resize-dispose.test.d.ts +0 -2
  680. package/dist/environment/__tests__/resize-dispose.test.d.ts.map +0 -1
  681. package/dist/environment/__tests__/signature-red.test.d.ts +0 -2
  682. package/dist/environment/__tests__/signature-red.test.d.ts.map +0 -1
  683. package/dist/environment/__tests__/signature.test.d.ts +0 -2
  684. package/dist/environment/__tests__/signature.test.d.ts.map +0 -1
  685. package/dist/environment/descriptors.d.ts +0 -2
  686. package/dist/environment/descriptors.d.ts.map +0 -1
  687. package/dist/environment/extracted-environment.d.ts +0 -53
  688. package/dist/environment/extracted-environment.d.ts.map +0 -1
  689. package/dist/environment/generation.d.ts +0 -62
  690. package/dist/environment/generation.d.ts.map +0 -1
  691. package/dist/environment/gpu-bundle.d.ts +0 -23
  692. package/dist/environment/gpu-bundle.d.ts.map +0 -1
  693. package/dist/environment/graph.d.ts +0 -108
  694. package/dist/environment/graph.d.ts.map +0 -1
  695. package/dist/environment/inspection.d.ts +0 -48
  696. package/dist/environment/inspection.d.ts.map +0 -1
  697. package/dist/environment/lifecycle.d.ts +0 -22
  698. package/dist/environment/lifecycle.d.ts.map +0 -1
  699. package/dist/environment/readiness.d.ts +0 -29
  700. package/dist/environment/readiness.d.ts.map +0 -1
  701. package/dist/environment/signature.d.ts +0 -70
  702. package/dist/environment/signature.d.ts.map +0 -1
  703. package/dist/extract/__tests__/directional-csm-domain.unit.test.d.ts +0 -2
  704. package/dist/extract/__tests__/directional-csm-domain.unit.test.d.ts.map +0 -1
  705. package/dist/extract/__tests__/environment-source.test.d.ts +0 -2
  706. package/dist/extract/__tests__/environment-source.test.d.ts.map +0 -1
  707. package/dist/extract/__tests__/schema-readers.unit.test.d.ts +0 -2
  708. package/dist/extract/__tests__/schema-readers.unit.test.d.ts.map +0 -1
  709. package/dist/extract/directional-csm.d.ts +0 -129
  710. package/dist/extract/directional-csm.d.ts.map +0 -1
  711. package/dist/extract/index.d.ts +0 -5
  712. package/dist/extract/index.d.ts.map +0 -1
  713. package/dist/extract/material-interface.d.ts +0 -56
  714. package/dist/extract/material-interface.d.ts.map +0 -1
  715. package/dist/extract/schema-readers.d.ts +0 -20
  716. package/dist/extract/schema-readers.d.ts.map +0 -1
  717. package/dist/features/graph-contribution.d.ts +0 -64
  718. package/dist/features/graph-contribution.d.ts.map +0 -1
  719. package/dist/features/index.d.ts +0 -21
  720. package/dist/features/index.d.ts.map +0 -1
  721. package/dist/features/morph/index.d.ts +0 -3
  722. package/dist/features/morph/index.d.ts.map +0 -1
  723. package/dist/features/morph/morph-bounds.d.ts +0 -42
  724. package/dist/features/morph/morph-bounds.d.ts.map +0 -1
  725. package/dist/features/scene-temporal.d.ts +0 -45
  726. package/dist/features/scene-temporal.d.ts.map +0 -1
  727. package/dist/features/taa-resolve.d.ts +0 -21
  728. package/dist/features/taa-resolve.d.ts.map +0 -1
  729. package/dist/frame-chain.d.ts +0 -14
  730. package/dist/frame-chain.d.ts.map +0 -1
  731. package/dist/gpu-driven/view-reference.d.ts.map +0 -1
  732. package/dist/gpu-resource-store.d.ts.map +0 -1
  733. package/dist/hdrp-cluster-membership.d.ts +0 -2
  734. package/dist/hdrp-cluster-membership.d.ts.map +0 -1
  735. package/dist/hdrp-pipeline.d.ts +0 -36
  736. package/dist/hdrp-pipeline.d.ts.map +0 -1
  737. package/dist/ibl/cubemap-projection.d.ts +0 -23
  738. package/dist/ibl/cubemap-projection.d.ts.map +0 -1
  739. package/dist/ibl/face-uniforms.d.ts +0 -65
  740. package/dist/ibl/face-uniforms.d.ts.map +0 -1
  741. package/dist/mesh-update-errors.d.ts +0 -71
  742. package/dist/mesh-update-errors.d.ts.map +0 -1
  743. package/dist/mipmap-generator.d.ts +0 -39
  744. package/dist/mipmap-generator.d.ts.map +0 -1
  745. package/dist/param-schema-projection-census.d.ts +0 -18
  746. package/dist/param-schema-projection-census.d.ts.map +0 -1
  747. package/dist/persistent-render-scene.d.ts +0 -158
  748. package/dist/persistent-render-scene.d.ts.map +0 -1
  749. package/dist/pipeline/index.d.ts +0 -11
  750. package/dist/pipeline/index.d.ts.map +0 -1
  751. package/dist/pipeline-errors.d.ts +0 -110
  752. package/dist/pipeline-errors.d.ts.map +0 -1
  753. package/dist/prepare/__tests__/schema-readers.unit.test.d.ts +0 -2
  754. package/dist/prepare/__tests__/schema-readers.unit.test.d.ts.map +0 -1
  755. package/dist/prepare/index.d.ts +0 -4
  756. package/dist/prepare/index.d.ts.map +0 -1
  757. package/dist/prepare/schema-readers.d.ts +0 -11
  758. package/dist/prepare/schema-readers.d.ts.map +0 -1
  759. package/dist/record/__tests__/environment-record.test.d.ts +0 -2
  760. package/dist/record/__tests__/environment-record.test.d.ts.map +0 -1
  761. package/dist/record/__tests__/frame-targets.unit.test.d.ts +0 -2
  762. package/dist/record/__tests__/frame-targets.unit.test.d.ts.map +0 -1
  763. package/dist/record/__tests__/membership-timing.unit.test.d.ts +0 -2
  764. package/dist/record/__tests__/membership-timing.unit.test.d.ts.map +0 -1
  765. package/dist/record/__tests__/render-submission.integration.test.d.ts +0 -2
  766. package/dist/record/__tests__/render-submission.integration.test.d.ts.map +0 -1
  767. package/dist/record/__tests__/render-submission.perf.test.d.ts +0 -2
  768. package/dist/record/__tests__/render-submission.perf.test.d.ts.map +0 -1
  769. package/dist/record/__tests__/shadow-submit-mode.unit.test.d.ts +0 -2
  770. package/dist/record/__tests__/shadow-submit-mode.unit.test.d.ts.map +0 -1
  771. package/dist/record/__tests__/view-ubo-layout.unit.test.d.ts +0 -2
  772. package/dist/record/__tests__/view-ubo-layout.unit.test.d.ts.map +0 -1
  773. package/dist/record/frame-observation.d.ts +0 -38
  774. package/dist/record/frame-observation.d.ts.map +0 -1
  775. package/dist/record/index.d.ts +0 -15
  776. package/dist/record/index.d.ts.map +0 -1
  777. package/dist/record/main-pass-skin.d.ts +0 -46
  778. package/dist/record/main-pass-skin.d.ts.map +0 -1
  779. package/dist/record/main-pass-sprite.d.ts +0 -69
  780. package/dist/record/main-pass-sprite.d.ts.map +0 -1
  781. package/dist/record/material-payload.d.ts +0 -43
  782. package/dist/record/material-payload.d.ts.map +0 -1
  783. package/dist/record/material-slot-plan.d.ts +0 -26
  784. package/dist/record/material-slot-plan.d.ts.map +0 -1
  785. package/dist/record/membership-timing.d.ts +0 -232
  786. package/dist/record/membership-timing.d.ts.map +0 -1
  787. package/dist/record/shadow-caster-uniform.d.ts +0 -8
  788. package/dist/record/shadow-caster-uniform.d.ts.map +0 -1
  789. package/dist/record/shadow-submit-mode.d.ts +0 -66
  790. package/dist/record/shadow-submit-mode.d.ts.map +0 -1
  791. package/dist/render-dispatch-membership.d.ts +0 -47
  792. package/dist/render-dispatch-membership.d.ts.map +0 -1
  793. package/dist/render-pipeline-context.d.ts +0 -289
  794. package/dist/render-pipeline-context.d.ts.map +0 -1
  795. package/dist/render-pipeline-target.d.ts +0 -16
  796. package/dist/render-pipeline-target.d.ts.map +0 -1
  797. package/dist/render-scene-projection.d.ts +0 -174
  798. package/dist/render-scene-projection.d.ts.map +0 -1
  799. package/dist/render-system-fold.d.ts +0 -266
  800. package/dist/render-system-fold.d.ts.map +0 -1
  801. package/dist/renderer/asset-service.d.ts +0 -10
  802. package/dist/renderer/asset-service.d.ts.map +0 -1
  803. package/dist/renderer/backend-selection.d.ts.map +0 -1
  804. package/dist/renderer/environment-classify.d.ts +0 -6
  805. package/dist/renderer/environment-classify.d.ts.map +0 -1
  806. package/dist/renderer/environment-error.d.ts +0 -13
  807. package/dist/renderer/environment-error.d.ts.map +0 -1
  808. package/dist/renderer/material/pipeline-projection.d.ts +0 -91
  809. package/dist/renderer/material/pipeline-projection.d.ts.map +0 -1
  810. package/dist/renderer/renderer-factory.d.ts +0 -289
  811. package/dist/renderer/renderer-factory.d.ts.map +0 -1
  812. package/dist/renderer.d.ts +0 -771
  813. package/dist/renderer.d.ts.map +0 -1
  814. package/dist/scene-utils/collect-subtree.d.ts +0 -11
  815. package/dist/scene-utils/collect-subtree.d.ts.map +0 -1
  816. package/dist/ssao-data.d.ts +0 -34
  817. package/dist/ssao-data.d.ts.map +0 -1
  818. package/dist/surface-format.d.ts +0 -10
  819. package/dist/surface-format.d.ts.map +0 -1
  820. package/dist/taa-history-store.d.ts +0 -88
  821. package/dist/taa-history-store.d.ts.map +0 -1
  822. package/dist/temporal-coverage.d.ts +0 -35
  823. package/dist/temporal-coverage.d.ts.map +0 -1
  824. package/dist/temporal-frame-transaction.d.ts +0 -92
  825. package/dist/temporal-frame-transaction.d.ts.map +0 -1
  826. package/dist/temporal-inspection.d.ts +0 -57
  827. package/dist/temporal-inspection.d.ts.map +0 -1
  828. package/dist/temporal-view.d.ts +0 -43
  829. package/dist/temporal-view.d.ts.map +0 -1
  830. package/dist/tilemap/resolve-tileset-runtime.d.ts +0 -23
  831. package/dist/tilemap/resolve-tileset-runtime.d.ts.map +0 -1
  832. package/dist/urp-pipeline.d.ts +0 -4
  833. package/dist/urp-pipeline.d.ts.map +0 -1
  834. package/src/__tests__/analytic-sky-contract.test.ts +0 -27
  835. package/src/__tests__/analytic-sky-falsify-carriers.mjs +0 -43
  836. package/src/__tests__/analytic-sky-falsify.test.ts +0 -94
  837. package/src/__tests__/assets-runtime-handoff.integration.test.ts +0 -36
  838. package/src/__tests__/backend-selection.integration.test.ts +0 -11
  839. package/src/__tests__/camera-temporal.test-d.ts +0 -8
  840. package/src/__tests__/camera-temporal.test.ts +0 -188
  841. package/src/__tests__/directional-shadow-probe-atlas-layout.unit.test.ts +0 -18
  842. package/src/__tests__/environment-inspection.test.ts +0 -45
  843. package/src/__tests__/fog-ai-user.fixture.ts +0 -350
  844. package/src/__tests__/fog-custom-render-feature.fixture.ts +0 -33
  845. package/src/__tests__/fog-custom-render-feature.integration.test.ts +0 -76
  846. package/src/__tests__/fog-inspection.integration.test.ts +0 -127
  847. package/src/__tests__/fog-owner-state.integration.test.ts +0 -112
  848. package/src/__tests__/fog-pipeline-topology.integration.test.ts +0 -93
  849. package/src/__tests__/fog-public-surface.integration.test.ts +0 -60
  850. package/src/__tests__/fog-ray-contract.integration.test.ts +0 -73
  851. package/src/__tests__/fog-source-absence.integration.test.ts +0 -51
  852. package/src/__tests__/fog-temporal.integration.test.ts +0 -115
  853. package/src/__tests__/frame-chain.unit.test.ts +0 -26
  854. package/src/__tests__/gpu-scene-field-type-owner.test-d.ts +0 -27
  855. package/src/__tests__/hdrp-pipeline.unit.test.ts +0 -100
  856. package/src/__tests__/ibl/ibl-fallback.unit.test.ts +0 -23
  857. package/src/__tests__/material-derived-error-stop-draw.integration.test.ts +0 -69
  858. package/src/__tests__/material-derived-payload.integration.test.ts +0 -164
  859. package/src/__tests__/materials.unit.test.ts +0 -25
  860. package/src/__tests__/param-schema-projection-census.unit.test.ts +0 -40
  861. package/src/__tests__/persistent-render-scene.unit.test.ts +0 -1311
  862. package/src/__tests__/pipeline-error-policy-owner.unit.test.ts +0 -167
  863. package/src/__tests__/pipeline-pass-trace.test.ts +0 -92
  864. package/src/__tests__/pipeline-stage-owner.test-d.ts +0 -13
  865. package/src/__tests__/plugin.test.ts +0 -25
  866. package/src/__tests__/prepared-graphics-backend-matrix.unit.test.ts +0 -129
  867. package/src/__tests__/prepared-graphics-resolver-recording.unit.test.ts +0 -180
  868. package/src/__tests__/public-pipeline-id.test-d.ts +0 -6
  869. package/src/__tests__/render-dispatch-membership.unit.test.ts +0 -117
  870. package/src/__tests__/render-feature-construction.unit.test.ts +0 -124
  871. package/src/__tests__/render-feature-error-code-owner.test-d.ts +0 -27
  872. package/src/__tests__/render-feature-gpu-buffer-usage-owner.test-d.ts +0 -46
  873. package/src/__tests__/render-feature-graph.integration.test.ts +0 -205
  874. package/src/__tests__/render-feature-graphics-staging.unit.test.ts +0 -200
  875. package/src/__tests__/render-feature-order.unit.test.ts +0 -66
  876. package/src/__tests__/render-feature-prepared-graphics-recording.unit.test.ts +0 -107
  877. package/src/__tests__/render-feature-staging.unit.test.ts +0 -94
  878. package/src/__tests__/renderer-render-scene-public.test-d.ts +0 -27
  879. package/src/__tests__/scene-temporal-coverage.unit.test.ts +0 -259
  880. package/src/__tests__/sun-snapshot-consumers.test.ts +0 -35
  881. package/src/__tests__/surface-backend-kind-owner.test-d.ts +0 -42
  882. package/src/__tests__/taa-history-store.unit.test.ts +0 -196
  883. package/src/__tests__/temporal-frame-transaction.integration.test.ts +0 -537
  884. package/src/__tests__/temporal-inspection.unit.test.ts +0 -138
  885. package/src/assets/__tests__/asset-decoders.unit.test.ts +0 -92
  886. package/src/assets/__tests__/mipmap-generator.unit.test.ts +0 -52
  887. package/src/assets/__tests__/render-asset-port.unit.test.ts +0 -54
  888. package/src/assets/__tests__/resolve-render-asset.unit.test.ts +0 -47
  889. package/src/components/__tests__/atmosphere-red.test.ts +0 -51
  890. package/src/components/__tests__/atmosphere.test.ts +0 -79
  891. package/src/components/__tests__/fog.test-d.ts +0 -49
  892. package/src/components/__tests__/fog.test.ts +0 -60
  893. package/src/components/atmosphere.ts +0 -64
  894. package/src/deferred-lighting.ts +0 -92
  895. package/src/dynamic-texture-store.ts +0 -111
  896. package/src/environment/README.md +0 -76
  897. package/src/environment/__tests__/failure-injection.test.ts +0 -50
  898. package/src/environment/__tests__/generation-red.test.ts +0 -64
  899. package/src/environment/__tests__/generation.test.ts +0 -37
  900. package/src/environment/__tests__/graph-transaction.test.ts +0 -85
  901. package/src/environment/__tests__/inspection-red.test.ts +0 -53
  902. package/src/environment/__tests__/lifecycle-evidence.test.ts +0 -40
  903. package/src/environment/__tests__/pipeline-topology.test.ts +0 -33
  904. package/src/environment/__tests__/readiness.test.ts +0 -42
  905. package/src/environment/__tests__/recovery.test.ts +0 -62
  906. package/src/environment/__tests__/resize-dispose.test.ts +0 -30
  907. package/src/environment/__tests__/signature-red.test.ts +0 -45
  908. package/src/environment/__tests__/signature.test.ts +0 -60
  909. package/src/environment/descriptors.ts +0 -7
  910. package/src/environment/extracted-environment.ts +0 -239
  911. package/src/environment/generation.ts +0 -158
  912. package/src/environment/gpu-bundle.ts +0 -144
  913. package/src/environment/graph.ts +0 -588
  914. package/src/environment/inspection.ts +0 -107
  915. package/src/environment/lifecycle.ts +0 -124
  916. package/src/environment/readiness.ts +0 -76
  917. package/src/environment/signature.ts +0 -185
  918. package/src/extract/__tests__/directional-csm-domain.unit.test.ts +0 -391
  919. package/src/extract/__tests__/environment-source.test.ts +0 -105
  920. package/src/extract/__tests__/schema-readers.unit.test.ts +0 -16
  921. package/src/extract/directional-csm.ts +0 -959
  922. package/src/extract/index.ts +0 -10
  923. package/src/extract/material-interface.ts +0 -604
  924. package/src/extract/schema-readers.ts +0 -27
  925. package/src/features/graph-contribution.ts +0 -577
  926. package/src/features/index.ts +0 -100
  927. package/src/features/morph/index.ts +0 -24
  928. package/src/features/morph/morph-bounds.ts +0 -123
  929. package/src/features/scene-temporal.ts +0 -274
  930. package/src/features/taa-resolve.ts +0 -244
  931. package/src/frame-chain.ts +0 -21
  932. package/src/hdrp-cluster-membership.ts +0 -70
  933. package/src/ibl/cubemap-projection.ts +0 -60
  934. package/src/ibl/face-uniforms.ts +0 -150
  935. package/src/mesh-update-errors.ts +0 -113
  936. package/src/mipmap-generator.ts +0 -244
  937. package/src/param-schema-projection-census.ts +0 -73
  938. package/src/persistent-render-scene.ts +0 -1752
  939. package/src/pipeline/index.ts +0 -40
  940. package/src/pipeline-errors.ts +0 -172
  941. package/src/prepare/__tests__/schema-readers.unit.test.ts +0 -16
  942. package/src/prepare/index.ts +0 -8
  943. package/src/prepare/schema-readers.ts +0 -20
  944. package/src/record/__tests__/environment-record.test.ts +0 -35
  945. package/src/record/__tests__/frame-targets.unit.test.ts +0 -54
  946. package/src/record/__tests__/membership-timing.unit.test.ts +0 -524
  947. package/src/record/__tests__/render-submission.integration.test.ts +0 -76
  948. package/src/record/__tests__/render-submission.perf.test.ts +0 -141
  949. package/src/record/__tests__/shadow-submit-mode.unit.test.ts +0 -88
  950. package/src/record/__tests__/view-ubo-layout.unit.test.ts +0 -101
  951. package/src/record/frame-observation.ts +0 -122
  952. package/src/record/index.ts +0 -15
  953. package/src/record/main-pass-skin.ts +0 -51
  954. package/src/record/main-pass-sprite.ts +0 -166
  955. package/src/record/material-payload.ts +0 -292
  956. package/src/record/material-slot-plan.ts +0 -68
  957. package/src/record/membership-timing.ts +0 -1012
  958. package/src/record/shadow-caster-uniform.ts +0 -37
  959. package/src/record/shadow-submit-mode.ts +0 -277
  960. package/src/render-dispatch-membership.ts +0 -155
  961. package/src/render-pipeline-context.ts +0 -332
  962. package/src/render-pipeline-target.ts +0 -96
  963. package/src/render-scene-projection.ts +0 -1738
  964. package/src/render-system-fold.ts +0 -531
  965. package/src/renderer/asset-service.ts +0 -32
  966. package/src/renderer/backend-selection.ts +0 -121
  967. package/src/renderer/environment-classify.ts +0 -30
  968. package/src/renderer/environment-error.ts +0 -19
  969. package/src/renderer/material/pipeline-projection.ts +0 -140
  970. package/src/renderer.ts +0 -940
  971. package/src/scene-utils/collect-subtree.ts +0 -40
  972. package/src/ssao-data.ts +0 -106
  973. package/src/surface-format.ts +0 -37
  974. package/src/taa-history-store.ts +0 -429
  975. package/src/temporal-coverage.ts +0 -106
  976. package/src/temporal-frame-transaction.ts +0 -301
  977. package/src/temporal-inspection.ts +0 -194
  978. package/src/temporal-view.ts +0 -174
  979. package/src/tilemap/resolve-tileset-runtime.ts +0 -72
@@ -1,19 +1,13 @@
1
- import type { GraphResourceResolver } from '@forgeax/engine-render-graph';
2
1
  import {
3
2
  type BindGroup,
3
+ type BindGroupEntry,
4
4
  type Buffer,
5
5
  type RenderPipeline,
6
6
  RhiError,
7
+ type RhiQueue,
7
8
  type RhiRenderPassEncoder,
8
- type TextureView,
9
9
  } from '@forgeax/engine-rhi';
10
10
  import type { PassSelector } from '@forgeax/engine-types';
11
- import {
12
- PointShadowAtlasBoundsViolationError,
13
- PointShadowAtlasUninitializedError,
14
- } from '../errors/render';
15
- import type { DirectionalCsmCascadePlan } from '../extract/directional-csm';
16
- import type { MultiViewProductionSubmission } from '../gpu-driven/production-raster';
17
11
  import { GpuBuffer } from '../gpu-resource';
18
12
  import {
19
13
  GPU_BUFFER_USAGE_COPY_DST,
@@ -21,27 +15,56 @@ import {
21
15
  GPU_BUFFER_USAGE_UNIFORM,
22
16
  } from '../gpu-usage';
23
17
  import { assembleMaterialWithSkylightEntries } from '../ibl/skylight-bind-group';
24
- import {
25
- type InstanceBufferCacheEntry,
26
- packTemporalInstanceTransforms,
27
- temporalInstanceUploadByteLength,
28
- } from '../instance-buffer-cache';
29
- import { buildBeginRenderPassDescriptor } from '../pipeline-spec';
30
- import type { RenderPipelineGpuDrivenShadowProjection } from '../render-pipeline';
31
- import type { _InternalRenderPipelineContext } from '../render-pipeline-context';
32
- import { MATERIAL_PER_ENTITY_STRIDE } from '../render-system';
33
- import type { DispatchEntry, SpotLightSnapshot } from '../render-system-extract';
18
+ import type { InstanceBufferCacheEntry } from '../instance-buffer-cache';
19
+ import { buildPbrMaterialUserRegionEntries } from '../pbr-pipeline';
20
+ import type { DispatchEntry, ExtractedLights } from '../render-system-extract';
34
21
  import { matchPass } from '../systems/pass-selector';
35
- import type { ValidatedRenderable } from './frame-snapshot';
36
22
  import { worldEntityKey } from './frame-snapshot';
37
23
  import {
38
24
  getOrCreateFromChain,
39
25
  getOrCreatePerEntity,
40
26
  MAX_UNIFORM_INSTANCES,
41
27
  MESH_PER_ENTITY_STRIDE,
28
+ packInstanceStorageBuffer,
42
29
  } from './mesh-ssbo';
43
- import { directionalShadowCasterOffset, spotShadowCasterOffset } from './shadow-caster-uniform';
44
- import { pointShadowViewOffset, VIEW_UNIFORM_BYTES } from './view-ubo';
30
+ import type { _InternalRenderPipelineContext } from './render-context';
31
+ import { STANDARD_PBR_UBO_SIZE } from './render-context';
32
+ import { POINTS_LINES_VIEW_BYTES, pointShadowViewOffset, VIEW_UNIFORM_BYTES } from './view-ubo';
33
+
34
+ export const SHADOW_CASTER_SLOT_STRIDE = 256;
35
+ export const SHADOW_CASTER_BUFFER_SIZE = SHADOW_CASTER_SLOT_STRIDE * 8;
36
+
37
+ export function directionalShadowCasterOffset(cascadeIndex: number): number {
38
+ return SHADOW_CASTER_SLOT_STRIDE * cascadeIndex;
39
+ }
40
+
41
+ export function spotShadowCasterOffset(tile: number): number {
42
+ return SHADOW_CASTER_SLOT_STRIDE * (4 + tile);
43
+ }
44
+
45
+ export function writeShadowCasterUniforms(
46
+ queue: RhiQueue,
47
+ buffer: Buffer,
48
+ lights: ExtractedLights,
49
+ ): void {
50
+ for (let cascade = 0; cascade < 4; cascade += 1) {
51
+ const written = queue.writeBuffer(
52
+ buffer,
53
+ directionalShadowCasterOffset(cascade),
54
+ new Uint32Array([cascade, 0, 0, 0]),
55
+ );
56
+ if (!written.ok) throw written.error;
57
+ }
58
+ for (const snapshot of lights.spot) {
59
+ const tile = snapshot.shadowAtlasTile;
60
+ if (tile < 0 || tile >= 4 || snapshot.lightViewProj === undefined) continue;
61
+ const offset = spotShadowCasterOffset(tile);
62
+ const header = queue.writeBuffer(buffer, offset, new Uint32Array([0, 1, 0, 0]));
63
+ if (!header.ok) throw header.error;
64
+ const matrix = queue.writeBuffer(buffer, offset + 16, snapshot.lightViewProj);
65
+ if (!matrix.ok) throw matrix.error;
66
+ }
67
+ }
45
68
 
46
69
  function ensureTypedShadowViewBg(
47
70
  c: _InternalRenderPipelineContext,
@@ -50,6 +73,8 @@ function ensureTypedShadowViewBg(
50
73
  variant: string,
51
74
  ): BindGroup | null {
52
75
  const { runtime, frameState, pipelineState } = c;
76
+ const shadowSampler = pipelineState.perPassResources.shadowSampler;
77
+ if (shadowSampler === null) return null;
53
78
  try {
54
79
  return getOrCreateFromChain(
55
80
  frameState.viewBindGroupCache,
@@ -58,11 +83,12 @@ function ensureTypedShadowViewBg(
58
83
  pipelineState.pointLightsBuffer,
59
84
  pipelineState.spotLightsBuffer,
60
85
  pipelineState.shadowFallbackTextureView,
61
- pipelineState.perPassResources.shadowSampler,
86
+ shadowSampler,
62
87
  pipelineState.shadowAtlasFallbackTextureView,
63
88
  pipelineState.shadowParamsBuffer,
64
89
  pipelineState.shadowCasterCascadeBuffer,
65
90
  pipelineState.shadowFallbackTextureView,
91
+ pipelineState.pointsLinesViewBuffer ?? pipelineState.viewUniformBuffer,
66
92
  ],
67
93
  variant,
68
94
  () => {
@@ -95,7 +121,7 @@ function ensureTypedShadowViewBg(
95
121
  },
96
122
  {
97
123
  binding: 4,
98
- resource: { kind: 'sampler', value: pipelineState.perPassResources.shadowSampler },
124
+ resource: { kind: 'sampler', value: shadowSampler },
99
125
  },
100
126
  {
101
127
  binding: 5,
@@ -115,7 +141,7 @@ function ensureTypedShadowViewBg(
115
141
  value: {
116
142
  buffer: pipelineState.shadowCasterCascadeBuffer,
117
143
  offset: cascadeOffset,
118
- size: 80,
144
+ size: POINTS_LINES_VIEW_BYTES,
119
145
  },
120
146
  },
121
147
  },
@@ -123,6 +149,16 @@ function ensureTypedShadowViewBg(
123
149
  binding: 8,
124
150
  resource: { kind: 'textureView', value: pipelineState.shadowFallbackTextureView },
125
151
  },
152
+ {
153
+ binding: 10,
154
+ resource: {
155
+ kind: 'buffer',
156
+ value: {
157
+ buffer: pipelineState.pointsLinesViewBuffer ?? pipelineState.viewUniformBuffer,
158
+ size: 80,
159
+ },
160
+ },
161
+ },
126
162
  ],
127
163
  });
128
164
  if (!created.ok) throw created.error;
@@ -139,12 +175,9 @@ function ensureTypedShadowViewBg(
139
175
  }
140
176
  }
141
177
 
142
- function shadowShaderMap(
143
- c: _InternalRenderPipelineContext,
144
- dispatch: readonly DispatchEntry[] = c.shadowDispatch,
145
- ): ReadonlyMap<number, string> {
178
+ function shadowShaderMap(c: _InternalRenderPipelineContext): ReadonlyMap<number, string> {
146
179
  const shaders = new Map<number, string>();
147
- for (const entry of dispatch) {
180
+ for (const entry of c.dispatch) {
148
181
  if (entry.tags.LightMode === 'ShadowCaster' && entry.materialShaderId !== undefined) {
149
182
  shaders.set(entry.renderableIndex, entry.materialShaderId);
150
183
  }
@@ -152,355 +185,6 @@ function shadowShaderMap(
152
185
  return shaders;
153
186
  }
154
187
 
155
- function multiViewSubmission(
156
- c: _InternalRenderPipelineContext,
157
- kind: 'main' | 'cascade',
158
- cascadeIndex?: number,
159
- ): MultiViewProductionSubmission | undefined {
160
- return c.multiViewSubmissions.find(
161
- (submission) =>
162
- submission.kind === kind && (kind === 'main' || submission.cascadeIndex === cascadeIndex),
163
- );
164
- }
165
-
166
- /** One-way adapter from the batch-aligned production plan to direct RHI draws. */
167
- function submissionCandidateIndices(
168
- submission: MultiViewProductionSubmission | undefined,
169
- ): ReadonlySet<number> | undefined {
170
- if (submission === undefined) return undefined;
171
- // A truncated compact list is never a safe membership owner. Returning
172
- // undefined deliberately selects the existing complete pass-selector
173
- // domain, which is a conservative direct CPU fallback and cannot silently
174
- // omit a caster. The overflow bit remains available to diagnostics.
175
- if (submission.overflow) return undefined;
176
- return submission.candidateIndices;
177
- }
178
-
179
- function directionalCascadeCacheHit(
180
- c: _InternalRenderPipelineContext,
181
- cascadeIndex: number,
182
- ): boolean {
183
- return ((c.directionalShadowCacheHitMask ?? 0) & (1 << cascadeIndex)) !== 0;
184
- }
185
-
186
- function setOptionalScissorRect(
187
- pass: RhiRenderPassEncoder,
188
- x: number,
189
- y: number,
190
- width: number,
191
- height: number,
192
- ): void {
193
- // The production WebGPU encoder implements scissor clipping, while the
194
- // lightweight test/fallback encoders intentionally expose only the common
195
- // raster subset. Keep the atlas tile contract when the capability exists;
196
- // an absent optional method must not turn a valid depth pass into a graph
197
- // encode failure.
198
- const setScissorRect = (
199
- pass as unknown as {
200
- readonly setScissorRect?: (x: number, y: number, width: number, height: number) => void;
201
- }
202
- ).setScissorRect;
203
- setScissorRect?.call(pass, x, y, width, height);
204
- }
205
-
206
- function clearDirectionalCascadeTile(
207
- c: _InternalRenderPipelineContext,
208
- pass: RhiRenderPassEncoder,
209
- viewport: { readonly x: number; readonly y: number; readonly w: number; readonly h: number },
210
- ): boolean {
211
- if ((c.directionalShadowCacheHitMask ?? 0) === 0) return true;
212
- const pipeline = c.runtime.getDirectionalShadowTileClearPipeline?.() ?? null;
213
- if (pipeline === null) return false;
214
- pass.setViewport(viewport.x, viewport.y, viewport.w, viewport.h, 0, 1);
215
- setOptionalScissorRect(pass, viewport.x, viewport.y, viewport.w, viewport.h);
216
- pass.setPipeline(pipeline);
217
- pass.draw(3);
218
- return true;
219
- }
220
-
221
- /** Point/spot compatibility recording consumes the shared main-view result. */
222
- function mainShadowMembership(c: _InternalRenderPipelineContext): ReadonlySet<number> | undefined {
223
- return submissionCandidateIndices(multiViewSubmission(c, 'main'));
224
- }
225
-
226
- function isMainShadowRenderable(
227
- entry: ValidatedRenderable,
228
- membership: ReadonlySet<number> | undefined,
229
- ): boolean {
230
- if (membership === undefined) return true;
231
- const candidateIndex = entry.source.candidateIndex ?? entry.renderableIndex;
232
- return membership.has(candidateIndex);
233
- }
234
-
235
- interface ShadowCascadeObservation {
236
- readonly frame: number;
237
- readonly cascade: number;
238
- readonly viewport: {
239
- readonly x: number;
240
- readonly y: number;
241
- readonly w: number;
242
- readonly h: number;
243
- };
244
- readonly plan: DirectionalCsmCascadePlan;
245
- readonly submission: MultiViewProductionSubmission | undefined;
246
- readonly stableIdByCandidateIndex: ReadonlyMap<number, number>;
247
- readonly cacheToken: string | undefined;
248
- readonly cacheHit: boolean;
249
- readonly budget: {
250
- readonly limit: number;
251
- emitted: number;
252
- dropped: number;
253
- };
254
- }
255
-
256
- const SHADOW_CANDIDATE_OBSERVATION_BUDGET = 64;
257
-
258
- interface ProjectedCasterRect {
259
- readonly x: number;
260
- readonly y: number;
261
- readonly w: number;
262
- readonly h: number;
263
- }
264
-
265
- function projectLightSpaceCasterRect(
266
- plan: DirectionalCsmCascadePlan,
267
- viewport: ShadowCascadeObservation['viewport'],
268
- lightSpaceBounds: ArrayLike<number> | undefined,
269
- ): ProjectedCasterRect | undefined {
270
- if (lightSpaceBounds === undefined || lightSpaceBounds.length < 4) return undefined;
271
- const clipMinX = plan.clipLightBounds[0] ?? Number.NaN;
272
- const clipMaxX = plan.clipLightBounds[1] ?? Number.NaN;
273
- const clipMinY = plan.clipLightBounds[2] ?? Number.NaN;
274
- const clipMaxY = plan.clipLightBounds[3] ?? Number.NaN;
275
- const casterMinX = lightSpaceBounds[0] ?? Number.NaN;
276
- const casterMaxX = lightSpaceBounds[1] ?? Number.NaN;
277
- const casterMinY = lightSpaceBounds[2] ?? Number.NaN;
278
- const casterMaxY = lightSpaceBounds[3] ?? Number.NaN;
279
- if (
280
- !Number.isFinite(clipMinX) ||
281
- !Number.isFinite(clipMaxX) ||
282
- !Number.isFinite(clipMinY) ||
283
- !Number.isFinite(clipMaxY) ||
284
- !Number.isFinite(casterMinX) ||
285
- !Number.isFinite(casterMaxX) ||
286
- !Number.isFinite(casterMinY) ||
287
- !Number.isFinite(casterMaxY) ||
288
- !(clipMaxX > clipMinX) ||
289
- !(clipMaxY > clipMinY)
290
- ) {
291
- return undefined;
292
- }
293
- const toPixelX = (value: number) =>
294
- viewport.x + ((value - clipMinX) / (clipMaxX - clipMinX)) * viewport.w;
295
- // Light-space Y is projected with the WebGPU clip-space Y flip used by the
296
- // orthographic matrix; convert it to the top-left texture origin here.
297
- const toPixelY = (value: number) =>
298
- viewport.y + ((clipMaxY - value) / (clipMaxY - clipMinY)) * viewport.h;
299
- const x0 = toPixelX(casterMinX);
300
- const x1 = toPixelX(casterMaxX);
301
- const y0 = toPixelY(casterMinY);
302
- const y1 = toPixelY(casterMaxY);
303
- return {
304
- x: Math.min(x0, x1),
305
- y: Math.min(y0, y1),
306
- w: Math.abs(x1 - x0),
307
- h: Math.abs(y1 - y0),
308
- };
309
- }
310
-
311
- function shadowMarker(payload: Record<string, unknown>): string {
312
- return `shadowCascade:${JSON.stringify(payload)}`;
313
- }
314
-
315
- function spotShadowMarker(payload: Record<string, unknown>): string {
316
- return `shadowSpot:${JSON.stringify(payload)}`;
317
- }
318
-
319
- function emitSpotShadowPassBeginMarker(
320
- c: _InternalRenderPipelineContext,
321
- pass: RhiRenderPassEncoder,
322
- snapshot: SpotLightSnapshot,
323
- spotIndex: number,
324
- spotCount: number,
325
- ): void {
326
- if (c.shadowDebugObservation !== true) return;
327
- const payload = {
328
- kind: 'spot',
329
- stage: 'pass-begin',
330
- frame: c.frameState.frameNumber,
331
- spotIndex,
332
- spotCount,
333
- lightIdentity: {
334
- sourceEntity: snapshot.entity,
335
- shadowCasterOrdinal: spotIndex,
336
- },
337
- tile: {
338
- index: snapshot.shadowAtlasTile,
339
- leaseGeneration: 'unowned',
340
- atlasEpoch: 'unowned',
341
- },
342
- cache: {
343
- mode: 'forced-redraw',
344
- hit: false,
345
- dirtyReason: 'no-spot-cache-owner',
346
- },
347
- projection: {
348
- mapSize: snapshot.mapSize,
349
- nearPlane: snapshot.nearPlane,
350
- farPlane: snapshot.farPlane,
351
- },
352
- };
353
- pass.pushDebugGroup(spotShadowMarker({ ...payload, stage: 'pass-group' }));
354
- pass.insertDebugMarker(spotShadowMarker(payload));
355
- }
356
-
357
- function emitSpotShadowPassEndMarker(
358
- c: _InternalRenderPipelineContext,
359
- pass: RhiRenderPassEncoder,
360
- snapshot: SpotLightSnapshot,
361
- ): void {
362
- if (c.shadowDebugObservation !== true) return;
363
- pass.insertDebugMarker(
364
- spotShadowMarker({
365
- kind: 'spot',
366
- stage: 'pass-end',
367
- frame: c.frameState.frameNumber,
368
- sourceEntity: snapshot.entity,
369
- tile: snapshot.shadowAtlasTile,
370
- rendered: true,
371
- }),
372
- );
373
- pass.popDebugGroup();
374
- }
375
-
376
- function emitBudgetedShadowMarker(
377
- pass: RhiRenderPassEncoder,
378
- observation: ShadowCascadeObservation,
379
- payload: Record<string, unknown>,
380
- ): void {
381
- if (observation.budget.emitted >= observation.budget.limit) {
382
- observation.budget.dropped += 1;
383
- return;
384
- }
385
- observation.budget.emitted += 1;
386
- pass.insertDebugMarker(shadowMarker(payload));
387
- }
388
-
389
- function shadowCascadeObservation(
390
- c: _InternalRenderPipelineContext,
391
- cascadeIndex: number,
392
- viewport: ShadowCascadeObservation['viewport'],
393
- ): ShadowCascadeObservation | undefined {
394
- if (c.shadowDebugObservation !== true) return undefined;
395
- const plan = c.directionalCsmPlan?.cascades[cascadeIndex];
396
- if (plan === undefined) return undefined;
397
- const submission = multiViewSubmission(c, 'cascade', cascadeIndex);
398
- return {
399
- frame: c.frameState.frameNumber,
400
- cascade: cascadeIndex,
401
- viewport,
402
- plan,
403
- submission,
404
- stableIdByCandidateIndex: new Map(
405
- [...(submission?.candidateIndexByStableId.entries() ?? [])].map(
406
- ([stableId, candidateIndex]) => [candidateIndex, stableId] as const,
407
- ),
408
- ),
409
- cacheToken: c.directionalShadowCacheTokens?.[cascadeIndex],
410
- cacheHit: ((c.directionalShadowCacheHitMask ?? 0) & (1 << cascadeIndex)) !== 0,
411
- budget: {
412
- limit: SHADOW_CANDIDATE_OBSERVATION_BUDGET,
413
- emitted: 0,
414
- dropped: 0,
415
- },
416
- };
417
- }
418
-
419
- function emitShadowCascadePassMarker(
420
- pass: RhiRenderPassEncoder,
421
- observation: ShadowCascadeObservation,
422
- ): void {
423
- const plan = observation.plan;
424
- pass.pushDebugGroup(
425
- shadowMarker({
426
- stage: 'pass-group',
427
- frame: observation.frame,
428
- cascade: observation.cascade,
429
- }),
430
- );
431
- pass.insertDebugMarker(
432
- shadowMarker({
433
- kind: 'cascade',
434
- stage: 'pass-begin',
435
- frame: observation.frame,
436
- cascade: observation.cascade,
437
- viewport: observation.viewport,
438
- tile: observation.viewport,
439
- cache: {
440
- token: observation.cacheToken,
441
- hit: observation.cacheHit,
442
- },
443
- submission:
444
- observation.submission === undefined
445
- ? { source: 'direct-call-adapter' }
446
- : {
447
- source: observation.submission.source,
448
- viewId: observation.submission.viewId,
449
- topologyRevision: observation.submission.plan.revision,
450
- batchCount: observation.submission.plan.batches.length,
451
- candidateCount: observation.submission.plan.candidateCount,
452
- volumeRevision: observation.submission.volumeRevision,
453
- spatialIndexRevision: observation.submission.spatialIndexRevision,
454
- overflow: observation.submission.overflow,
455
- faults: observation.submission.faults,
456
- },
457
- plan: {
458
- receiverDepth: Array.from(plan.receiverViewDepth),
459
- unsnappedCenter: Array.from(plan.unsnappedCenter),
460
- snappedCenter: Array.from(plan.snappedCenter),
461
- stableExtent: plan.stableExtent,
462
- texel: plan.worldUnitsPerTexel,
463
- clipXYZ: Array.from(plan.clipLightBounds),
464
- lightViewProj: Array.from(plan.lightViewProj),
465
- fallbackCount: plan.unboundedFallbackCount,
466
- },
467
- }),
468
- );
469
- // A cascade-local token hit is already a complete invalidation fact. Keep
470
- // the bounded pass summary, but do not rebuild candidate JSON for a result
471
- // the observer has already seen.
472
- }
473
-
474
- function emitShadowCascadeEndMarker(
475
- pass: RhiRenderPassEncoder,
476
- observation: ShadowCascadeObservation,
477
- ): void {
478
- pass.insertDebugMarker(
479
- shadowMarker({
480
- kind: 'observation-summary',
481
- stage: 'pass-end',
482
- frame: observation.frame,
483
- cascade: observation.cascade,
484
- emittedCandidateMarkers: observation.budget.emitted,
485
- droppedCandidateMarkers: observation.budget.dropped,
486
- ...(observation.budget.dropped === 0
487
- ? {}
488
- : { faultCode: 'semantic-observation-budget-exceeded' }),
489
- }),
490
- );
491
- pass.insertDebugMarker(
492
- shadowMarker({
493
- kind: 'cascade',
494
- stage: 'pass-end',
495
- frame: observation.frame,
496
- cascade: observation.cascade,
497
- viewport: observation.viewport,
498
- tile: observation.viewport,
499
- }),
500
- );
501
- pass.popDebugGroup();
502
- }
503
-
504
188
  function shadowPipeline(c: _InternalRenderPipelineContext): RenderPipeline | null {
505
189
  return (
506
190
  c.runtime.getMaterialShaderPipeline?.(
@@ -520,19 +204,8 @@ export function encodeDirectionalShadowPass(
520
204
  pass: RhiRenderPassEncoder,
521
205
  cascadeIndex: number,
522
206
  viewport: { readonly x: number; readonly y: number; readonly w: number; readonly h: number },
523
- gpuDriven?: {
524
- readonly projection: RenderPipelineGpuDrivenShadowProjection;
525
- readonly resources: GraphResourceResolver;
526
- },
527
207
  ): void {
528
208
  if (c.directionalShadowCacheReuse || c.meshBindGroup === null) return;
529
- const observation = shadowCascadeObservation(c, cascadeIndex, viewport);
530
- if (observation !== undefined) emitShadowCascadePassMarker(pass, observation);
531
- if (directionalCascadeCacheHit(c, cascadeIndex)) {
532
- if (observation !== undefined) emitShadowCascadeEndMarker(pass, observation);
533
- return;
534
- }
535
- if (!clearDirectionalCascadeTile(c, pass, viewport)) return;
536
209
  const pipeline = shadowPipeline(c);
537
210
  const viewBg = ensureTypedShadowViewBg(
538
211
  c,
@@ -544,40 +217,17 @@ export function encodeDirectionalShadowPass(
544
217
  if (pipeline === null || viewBg === null || materialBg === null) return;
545
218
  c.frameState.directionalShadowCacheRecorded = true;
546
219
  pass.setViewport(viewport.x, viewport.y, viewport.w, viewport.h, 0, 1);
547
- setOptionalScissorRect(pass, viewport.x, viewport.y, viewport.w, viewport.h);
548
220
  pass.setPipeline(pipeline);
549
- pass.setBindGroup(0, viewBg);
221
+ pass.setBindGroup(0, viewBg, [0]);
550
222
  pass.setBindGroup(1, materialBg, [0]);
551
- if (gpuDriven !== undefined) {
552
- c.frameState.directionalShadowCacheRecorded = true;
553
- gpuDriven.projection.encode(pass, gpuDriven.resources);
554
- if (observation !== undefined) {
555
- pass.insertDebugMarker(
556
- shadowMarker({
557
- kind: 'batch',
558
- stage: 'gpu-indirect-submission',
559
- source: 'multi-view-indirect',
560
- frame: observation.frame,
561
- cascade: observation.cascade,
562
- batchCount: gpuDriven.projection.batchCount ?? 0,
563
- candidateCount: observation.submission?.plan.candidateCount ?? 0,
564
- }),
565
- );
566
- emitShadowCascadeEndMarker(pass, observation);
567
- }
568
- return;
569
- }
570
223
  recordShadowCasterDraws(
571
224
  c,
572
225
  pass,
573
226
  pipeline,
574
227
  c.meshBindGroup,
575
- buildMatchedRenderableIndices(c.shadowDispatch, { LightMode: ['ShadowCaster'] }),
228
+ buildMatchedRenderableIndices(c.dispatch, { LightMode: ['ShadowCaster'] }),
576
229
  shadowShaderMap(c),
577
- multiViewSubmission(c, 'cascade', cascadeIndex),
578
- observation?.cacheHit === true ? undefined : observation,
579
230
  );
580
- if (observation !== undefined) emitShadowCascadeEndMarker(pass, observation);
581
231
  }
582
232
 
583
233
  export function encodePointShadowPass(
@@ -598,7 +248,7 @@ export function encodePointShadowPass(
598
248
  const materialBg = ensureSpotShadowMaterialBg(c);
599
249
  if (pipeline === null || viewBg === null || materialBg === null) return;
600
250
  pass.setPipeline(pipeline);
601
- pass.setBindGroup(0, viewBg);
251
+ pass.setBindGroup(0, viewBg, [0]);
602
252
  pass.setBindGroup(1, materialBg, [0]);
603
253
  recordShadowCasterDraws(
604
254
  c,
@@ -606,7 +256,7 @@ export function encodePointShadowPass(
606
256
  pipeline,
607
257
  c.meshBindGroup,
608
258
  buildMatchedRenderableIndices(c.dispatch, { LightMode: ['ShadowCaster'] }),
609
- shadowShaderMap(c, c.dispatch),
259
+ shadowShaderMap(c),
610
260
  );
611
261
  }
612
262
 
@@ -646,23 +296,19 @@ export function encodeSpotShadowPass(
646
296
  1,
647
297
  );
648
298
  pass.setPipeline(pipeline);
649
- pass.setBindGroup(0, viewBg);
299
+ pass.setBindGroup(0, viewBg, [0]);
650
300
  pass.setBindGroup(1, materialBg, [0]);
651
- // Keep the typed graph's spot producer on the same caster owner as the
652
- // directional/point paths. The old compact loop only consumed
653
- // `instancesBgPerEntity` entries created by a directional shadow pass, so a
654
- // spot-only world opened a valid atlas pass but emitted zero caster draws.
655
- // The shared recorder builds the per-entity instance binding on demand and
656
- // preserves pass-selector, custom ShadowCaster, and multi-view membership
657
- // contracts without adding a pass or resource.
301
+ // Reuse the shared caster recorder. The compact spot-only loop used to
302
+ // depend on instance bindings created by a directional pass, so a scene
303
+ // containing only a SpotLight opened the atlas pass but emitted no caster
304
+ // draws.
658
305
  recordShadowCasterDraws(
659
306
  c,
660
307
  pass,
661
308
  pipeline,
662
309
  c.meshBindGroup,
663
- buildMatchedRenderableIndices(c.shadowDispatch, { LightMode: ['ShadowCaster'] }),
310
+ buildMatchedRenderableIndices(c.dispatch, { LightMode: ['ShadowCaster'] }),
664
311
  shadowShaderMap(c),
665
- multiViewSubmission(c, 'main'),
666
312
  );
667
313
  }
668
314
 
@@ -721,421 +367,35 @@ export function buildMatchedRenderableIndices(
721
367
  }
722
368
 
723
369
  /**
724
- * feat-20260529-rendergraph-pass-abstraction M4 / w13b: shadow depth pass
725
- * recording, extracted verbatim from recordFrame. Renders shadow casters
726
- * into the shadow depth RT using an INDEPENDENT command encoder + its own
727
- * queue.submit (RD-4: this independent-encoder boundary is the runtime-side
728
- * manual barrier that synchronizes the depth-texture write with the
729
- * subsequent sample in the main pass). Driven by the render-graph 'shadow'
730
- * pass execute closure.
370
+ * Build the material handles whose dispatch entries match a graph pass.
371
+ *
372
+ * A renderable can own more than one material pass, so renderable-level
373
+ * filtering is insufficient for geometry recording: a Deferred graph pass
374
+ * must not draw the same renderable's Forward-only material. Keep the
375
+ * renderable key in the result so mixed-material meshes retain their
376
+ * per-submesh routing.
731
377
  */
732
- export function recordShadowPass(
733
- c: _InternalRenderPipelineContext,
734
- selector?: PassSelector,
735
- viewport?: { readonly x: number; readonly y: number; readonly w: number; readonly h: number },
736
- cascadeIndex: number = 0,
737
- ): void {
738
- const { runtime, pipelineState, validatedOrdered, meshBindGroup, shadowDispatch, frameState } = c;
739
- if (c.directionalShadowCacheReuse || directionalCascadeCacheHit(c, cascadeIndex)) return;
740
- // feat-20260613-csm-cascaded-shadow-maps M5 / w28: write the per-pass
741
- // cascade index to the shared shadowCasterCascadeBuffer. The shadow
742
- // pass below uses an INDEPENDENT command encoder + own queue.submit, so
743
- // queue.writeBuffer here lands serially against this pass's submit
744
- // even when N cascades are recorded back-to-back -- each pass's submit
745
- // sees its own index.
746
- const cascadeIdxPayload = new Uint32Array([cascadeIndex >>> 0, 0, 0, 0]);
747
- const cascadeWriteResult = runtime.device.queue.writeBuffer(
748
- pipelineState.shadowCasterCascadeBuffer,
749
- 0,
750
- cascadeIdxPayload,
751
- );
752
- if (!cascadeWriteResult.ok) throw cascadeWriteResult.error;
753
- // w10 shadow depth pass: uses a separate command encoder so Dawn/WebGPU
754
- // can synchronize the depth texture write (RenderAttachment) with the
755
- // subsequent read (TextureBinding in the geometry pass). Sharing an
756
- // encoder triggers "usage includes writable usage and another usage in
757
- // the same synchronization scope" validation error.
758
- // feat-20260609 M4 / T-010: shadow PSO via frameState.pipelineCache lookup
759
- // (same path as forward passes — charter P4 consistent abstraction).
760
- // getMaterialShaderPipeline lazily builds + caches PSO keyed on
761
- // (shaderId, isHdr, renderState, topology, indexFormat, variantSet, passKind).
762
- const shadowPipeline =
763
- runtime.getMaterialShaderPipeline?.(
764
- 'forgeax::default-shadow-caster',
765
- false, // isHdr — shadow depth pass is always LDR
766
- undefined, // renderState — vertex-only shader, no render state
767
- 'triangle-list', // topology — shadow PSO targets triangle-list
768
- undefined, // indexFormat — triangle-list ignores strip index width
769
- undefined, // variantSet — shadow_caster.wgsl has no group(2) bindings
770
- 'shadow-caster', // passKind — distinguishes from forward PSO
771
- ) ?? null;
772
- // M5-T1: shadow depth target read directly from render-graph
773
- // (`addColorTarget('shadowDepth', ...)` declared in `urp-pipeline.ts`;
774
- // D-2 SSOT). Returns undefined when the graph has not allocated the
775
- // target (castShadow:false or shadowMapSize=0); the
776
- // gate below (`shadowView !== null`) is preserved by coalescing
777
- // undefined to null.
778
- const shadowView = frameState.currentDirectionalShadowView;
779
- // feat-20260609 M2: filter entities by pass selector.
780
- const matchedIndices =
781
- selector !== undefined ? buildMatchedRenderableIndices(shadowDispatch, selector) : null;
782
-
783
- // bug-20260619-csm RC-3 (AC-10, D-3): map each renderable to its
784
- // ShadowCaster pass shader so the depth pass selects the per-entity PSO
785
- // (mirrors the forward pass's per-entity PSO selection — charter P4).
786
- // A material with a custom ShadowCaster shader (e.g. an alpha-test cutout
787
- // that calls `discard`) gets its own fragment-carrying PSO; default
788
- // casters resolve to `forgeax::default-shadow-caster` (vertex-only), so
789
- // there is no regression for the built-in materials. Built from the
790
- // dispatch entries tagged `LightMode: 'ShadowCaster'` (extract already
791
- // populates `materialShaderId` per pass).
792
- const shadowShaderByRenderableIdx = new Map<number, string>();
793
- for (const de of shadowDispatch) {
794
- if (de.tags.LightMode === 'ShadowCaster' && de.materialShaderId !== undefined) {
795
- shadowShaderByRenderableIdx.set(de.renderableIndex, de.materialShaderId);
796
- }
797
- }
798
-
799
- if (shadowPipeline !== null && shadowView !== null && validatedOrdered.length > 0) {
800
- frameState.directionalShadowCacheRecorded = true;
801
- // feat-20260529-rendergraph-pass-abstraction M4 / w14 (RD-4 verification
802
- // point): this INDEPENDENT 'render-system-shadow' command encoder + its
803
- // own queue.submit below is the runtime-side manual barrier that splits
804
- // the shadowDepth write (here) from the main pass sample. The render-
805
- // graph mirrors the same shadow -> main hazard as an explicit barrier on
806
- // wgpu-native (barrier-backend-kind.test.ts w14); on webgpu / wgpu-webgl2
807
- // the encoder boundary alone provides synchronization. main / tonemap /
808
- // FXAA stay on the shared frame encoder (c.encoder), submitted once.
809
- const shadowEncResult = runtime.device.createCommandEncoder({
810
- label: 'render-system-shadow',
811
- });
812
- if (shadowEncResult.ok) {
813
- const shadowEnc = shadowEncResult.value;
814
-
815
- // M2 / w8: shadow view (#3) cache lookup with 'view-shadow'
816
- // variant discriminator (AC-06: distinct key from 'view-main').
817
- // b3 is always shadowFallbackTextureView (not the actual shadow
818
- // map — WebGPU forbids writing to and sampling from the same
819
- // texture in the same synchronization scope). Handles are all
820
- // init-time stable, so the WeakMap chain hits from frame 2 onward.
821
- const shadowViewBg = getOrCreateFromChain(
822
- c.frameState.viewBindGroupCache,
823
- [
824
- pipelineState.viewUniformBuffer,
825
- pipelineState.pointLightsBuffer,
826
- pipelineState.spotLightsBuffer,
827
- pipelineState.shadowFallbackTextureView,
828
- pipelineState.perPassResources.shadowSampler,
829
- pipelineState.shadowAtlasFallbackTextureView,
830
- pipelineState.shadowParamsBuffer,
831
- pipelineState.shadowFallbackTextureView,
832
- ],
833
- 'view-shadow',
834
- () => {
835
- const shadowViewBgResult = runtime.device.createBindGroup({
836
- label: 'shadow-view-bg',
837
- layout: pipelineState.viewBindGroupLayout,
838
- entries: [
839
- {
840
- binding: 0,
841
- resource: {
842
- kind: 'buffer',
843
- value: { buffer: pipelineState.viewUniformBuffer },
844
- },
845
- },
846
- {
847
- binding: 1,
848
- resource: {
849
- kind: 'buffer',
850
- value: { buffer: pipelineState.pointLightsBuffer },
851
- },
852
- },
853
- {
854
- binding: 2,
855
- resource: {
856
- kind: 'buffer',
857
- value: { buffer: pipelineState.spotLightsBuffer },
858
- },
859
- },
860
- {
861
- binding: 3,
862
- resource: { kind: 'textureView', value: pipelineState.shadowFallbackTextureView },
863
- },
864
- {
865
- binding: 4,
866
- resource: { kind: 'sampler', value: pipelineState.perPassResources.shadowSampler },
867
- },
868
- // feat-20260612-point-light-shadows-urp-hdrp Round-2 F-1:
869
- // shadowViewBg uses the cube_array fallback at binding 5
870
- // (NOT the real ShadowAtlas atlas view) because the directional
871
- // shadow caster is reading depth from b3 -- but mixing the
872
- // real atlas view here would put the cube_array under both a
873
- // sample and a write attachment hazard during the SAME frame
874
- // (point shadow caster pass writes the atlas; here we only
875
- // need a valid bind to satisfy the BGL shape, never sample it
876
- // in shadow_caster.wgsl).
877
- {
878
- binding: 5,
879
- resource: {
880
- kind: 'textureView',
881
- value: pipelineState.shadowAtlasFallbackTextureView,
882
- },
883
- },
884
- {
885
- binding: 6,
886
- resource: {
887
- kind: 'buffer',
888
- value: { buffer: pipelineState.shadowParamsBuffer },
889
- },
890
- },
891
- // feat-20260613-csm-cascaded-shadow-maps M5 / w28 (rebased to
892
- // binding 7 on 2026-06-13): per-pass cascade-index uniform
893
- // consumed by shadow_caster.vs_main.
894
- {
895
- binding: 7,
896
- resource: {
897
- kind: 'buffer',
898
- value: { buffer: pipelineState.shadowCasterCascadeBuffer },
899
- },
900
- },
901
- // feat-20260625-spot-light-shadow-mapping M3 / w21 (D-5): spot
902
- // shadow 2D atlas. The shadow-view BG always binds the 1x1 depth
903
- // fallback (NOT the real spotShadowDepth view) — the spot caster
904
- // pass write-attaches that atlas, so sampling it here would create
905
- // a same-frame write+sample hazard; shadow_caster.wgsl never reads
906
- // binding 8 anyway, this entry only satisfies the BGL shape.
907
- {
908
- binding: 8,
909
- resource: {
910
- kind: 'textureView',
911
- value: pipelineState.shadowFallbackTextureView,
912
- },
913
- },
914
- // feat-20260625-spot-light-shadow-mapping w25: spot lightViewProj
915
- // matrices folded into the View UBO (binding 0) — no binding 9.
916
- // binding 8 is the last shared view-BG entry.
917
- ],
918
- });
919
- if (!shadowViewBgResult.ok) throw shadowViewBgResult.error;
920
- return shadowViewBgResult.value;
921
- },
922
- c.bindGroupCounts,
923
- );
924
-
925
- // Dummy material bind group for @group(1) — not consumed by the
926
- // shadow-caster shader but must match the pipeline's
927
- // materialBindGroupLayout (20 entries: the derived standard-PBR user
928
- // region 0..12 followed by Skylight 13..19).
929
- //
930
- // D-6 / AC-05: all handles are init-time stable pipelineState defaults
931
- // + skylightFallback resources, so this BG is a true singleton (no
932
- // entityKey). It lives in its own flat singletonMaterialCache
933
- // Map<variant, BindGroup> under 'shadow-material-singleton' — hit from
934
- // frame 2 onward, and cleanPerEntityCache never touches it (it is not
935
- // in any per-entity Map).
936
- const shadowMaterialBaseEntries = [
937
- {
938
- binding: 0,
939
- resource: {
940
- kind: 'buffer' as const,
941
- value: {
942
- buffer: pipelineState.materialUniformBuffer.buffer,
943
- offset: 0,
944
- size: MATERIAL_PER_ENTITY_STRIDE,
945
- },
946
- },
947
- },
948
- {
949
- binding: 1,
950
- resource: { kind: 'sampler' as const, value: pipelineState.defaultSampler },
951
- },
952
- {
953
- binding: 2,
954
- resource: {
955
- kind: 'textureView' as const,
956
- value: pipelineState.defaultWhiteTextureView,
957
- },
958
- },
959
- {
960
- binding: 3,
961
- resource: { kind: 'sampler' as const, value: pipelineState.defaultSampler },
962
- },
963
- {
964
- binding: 4,
965
- resource: {
966
- kind: 'textureView' as const,
967
- value: pipelineState.defaultWhiteTextureView,
968
- },
969
- },
970
- {
971
- binding: 5,
972
- resource: { kind: 'sampler' as const, value: pipelineState.defaultSampler },
973
- },
974
- {
975
- binding: 6,
976
- resource: {
977
- kind: 'textureView' as const,
978
- value: pipelineState.defaultNormalTextureView,
979
- },
980
- },
981
- {
982
- binding: 7,
983
- resource: { kind: 'sampler' as const, value: pipelineState.defaultSampler },
984
- },
985
- {
986
- binding: 8,
987
- resource: {
988
- kind: 'textureView' as const,
989
- value: pipelineState.defaultWhiteTextureView,
990
- },
991
- },
992
- {
993
- binding: 9,
994
- resource: { kind: 'sampler' as const, value: pipelineState.defaultSampler },
995
- },
996
- {
997
- binding: 10,
998
- resource: {
999
- kind: 'textureView' as const,
1000
- value: pipelineState.defaultWhiteTextureView,
1001
- },
1002
- },
1003
- {
1004
- binding: 11,
1005
- resource: { kind: 'sampler' as const, value: pipelineState.defaultSampler },
1006
- },
1007
- {
1008
- binding: 12,
1009
- resource: {
1010
- kind: 'textureView' as const,
1011
- value: pipelineState.defaultWhiteTextureView,
1012
- },
1013
- },
1014
- ];
1015
- const shadowSkyFb = pipelineState.skylightFallback;
1016
- const shadowMergedEntries =
1017
- shadowSkyFb !== null
1018
- ? assembleMaterialWithSkylightEntries(shadowMaterialBaseEntries, {
1019
- irradianceView: shadowSkyFb.irradianceView,
1020
- irradianceSampler: shadowSkyFb.sampler,
1021
- prefilterView: shadowSkyFb.prefilterView,
1022
- prefilterSampler: shadowSkyFb.sampler,
1023
- brdfLutView: shadowSkyFb.brdfLutView,
1024
- brdfLutSampler: shadowSkyFb.sampler,
1025
- intensityBuffer: shadowSkyFb.intensityBuffer,
1026
- })
1027
- : shadowMaterialBaseEntries;
1028
- // D-6 / AC-05: the one true singleton material BG lives in its own flat
1029
- // Map<variant, BindGroup> (no entityKey, no handle chain). Cache hit
1030
- // from frame 2 onward; the cleanPerEntityCache eviction never touches it.
1031
- let shadowMaterialBg = c.frameState.singletonMaterialCache.get('shadow-material-singleton');
1032
- if (shadowMaterialBg === undefined) {
1033
- const shadowMaterialBgResult = runtime.device.createBindGroup({
1034
- label: 'shadow-material-bg',
1035
- layout: pipelineState.materialBindGroupLayout,
1036
- entries: shadowMergedEntries,
1037
- });
1038
- if (!shadowMaterialBgResult.ok) throw shadowMaterialBgResult.error;
1039
- shadowMaterialBg = shadowMaterialBgResult.value;
1040
- c.bindGroupCounts.createBindGroup += 1;
1041
- c.bindGroupCounts.keys.push('shadow-material-singleton');
1042
- c.frameState.singletonMaterialCache.set('shadow-material-singleton', shadowMaterialBg);
1043
- }
1044
-
1045
- // feat-20260604-instances-per-instance-transform-shader-group3-bin M2 / w12 (D-1 (C)):
1046
- // shadow pass per-instance channel alignment — replaces the identity singleton
1047
- // @group(3) binding with per-entity instance buffer + drawIndexed(instanceCount).
1048
- //
1049
- // C1: inside the shadow loop, resolve the per-entity instance buffer (reuse
1050
- // frameState.instanceBuffers cache, or build+upload fresh). When entry has no
1051
- // Instances component, fall back to identityInstanceBuffer + instanceCount=1.
1052
- // C2: shadowPass.drawIndexed(indexCount, instanceCount, 0, 0, 0) with real
1053
- // inst.instanceCount (not hardcoded 1).
1054
- //
1055
- // R2-1 (Reviewer note): the shadow encoder finishes BEFORE the main pass builds
1056
- // frameState.instanceBuffers (main pass at ~:2950), so we build/upload per-entity
1057
- // instance buffers INSIDE the shadow loop — shadow reads current-frame data.
1058
-
1059
- const shadowPass: RhiRenderPassEncoder = shadowEnc.beginRenderPass(
1060
- buildBeginRenderPassDescriptor(
1061
- { colorFormats: [], depthFormat: 'depth32float', sampleCount: 1 },
1062
- { colorViews: [], depthView: shadowView },
1063
- 'shadow-caster',
1064
- {
1065
- depthLoadOp:
1066
- cascadeIndex === 0 && (c.directionalShadowCacheHitMask ?? 0) === 0 ? 'clear' : 'load',
1067
- },
1068
- ) as never,
1069
- );
1070
-
1071
- shadowPass.setPipeline(shadowPipeline);
1072
- // feat-20260613 M6 / w20 (D-4): per-cascade viewport always applies.
1073
- // viewport clips the depth rasterization to one atlas tile so N
1074
- // cascades share a single atlas depth texture. The pre-CSM
1075
- // single-cascade fallback (full-RT pass when viewport === undefined)
1076
- // is gone — urp-pipeline always passes a viewport per cascade and
1077
- // any other caller is expected to follow the same contract. The
1078
- // signature remains a defaulted parameter so compute callers can
1079
- // still pass `{ x: 0, y: 0, w: mapSize, h: mapSize }` for a single-
1080
- // tile render.
1081
- const tileViewport: NonNullable<typeof viewport> = viewport ?? {
1082
- x: 0,
1083
- y: 0,
1084
- w: pipelineState.perPassResources.shadowMapSize,
1085
- h: pipelineState.perPassResources.shadowMapSize,
1086
- };
1087
- if (!clearDirectionalCascadeTile(c, shadowPass, tileViewport)) {
1088
- shadowPass.end();
1089
- return;
1090
- }
1091
- shadowPass.setViewport(tileViewport.x, tileViewport.y, tileViewport.w, tileViewport.h, 0, 1);
1092
- setOptionalScissorRect(
1093
- shadowPass,
1094
- tileViewport.x,
1095
- tileViewport.y,
1096
- tileViewport.w,
1097
- tileViewport.h,
1098
- );
1099
- shadowPass.setBindGroup(0, shadowViewBg);
1100
- shadowPass.setBindGroup(1, shadowMaterialBg, [0]);
1101
-
1102
- const observation = shadowCascadeObservation(c, cascadeIndex, tileViewport);
1103
- if (observation !== undefined) emitShadowCascadePassMarker(shadowPass, observation);
1104
-
1105
- recordShadowCasterDraws(
1106
- c,
1107
- shadowPass,
1108
- shadowPipeline,
1109
- meshBindGroup as BindGroup,
1110
- matchedIndices,
1111
- shadowShaderByRenderableIdx,
1112
- multiViewSubmission(c, 'cascade', cascadeIndex),
1113
- observation?.cacheHit === true ? undefined : observation,
1114
- );
1115
-
1116
- if (observation !== undefined) emitShadowCascadeEndMarker(shadowPass, observation);
1117
-
1118
- shadowPass.end();
1119
-
1120
- const shadowFinishResult = shadowEnc.finish();
1121
- if (shadowFinishResult.ok) {
1122
- if (frameState.shadowSubmitModeState.mode === 'coalesced-with-frame') {
1123
- frameState.pendingShadowCommandBuffers.push(shadowFinishResult.value);
1124
- } else {
1125
- runtime.device.queue.submit([shadowFinishResult.value]);
1126
- }
1127
- } else {
1128
- runtime.errorRegistry.fire(shadowFinishResult.error);
1129
- }
378
+ export function buildMatchedMaterialHandlesByRenderable(
379
+ dispatch: readonly DispatchEntry[],
380
+ selector: PassSelector,
381
+ ): ReadonlyMap<number, ReadonlySet<number>> | null {
382
+ if (dispatch.length === 0) return null;
383
+ const matched = filterDispatchBySelector(dispatch, selector);
384
+ const handlesByRenderable = new Map<number, Set<number>>();
385
+ for (const entry of matched) {
386
+ const handles = handlesByRenderable.get(entry.renderableIndex);
387
+ if (handles === undefined) {
388
+ handlesByRenderable.set(entry.renderableIndex, new Set([entry.materialHandle]));
1130
389
  } else {
1131
- runtime.errorRegistry.fire(shadowEncResult.error);
390
+ handles.add(entry.materialHandle);
1132
391
  }
1133
392
  }
393
+ return handlesByRenderable;
1134
394
  }
1135
395
 
1136
396
  /**
1137
397
  * feat-20260704 M3/w20: per-entity directional shadow-caster draw loop,
1138
- * extracted verbatim from {@link recordShadowPass}. Walks `c.validatedOrdered`,
398
+ * extracted verbatim from {@link encodeDirectionalShadowPass}. Walks `c.validatedOrdered`,
1139
399
  * selects the per-entity shadow PSO (default vertex-only caster or a custom
1140
400
  * cutout caster), binds the per-entity mesh dynamic-offset + instance buffer,
1141
401
  * and issues the per-submesh depth draws. `shadowPass` view/material bind
@@ -1152,8 +412,6 @@ function recordShadowCasterDraws(
1152
412
  shadowMeshBindGroup: BindGroup,
1153
413
  matchedIndices: Set<number> | null,
1154
414
  shadowShaderByRenderableIdx: ReadonlyMap<number, string>,
1155
- productionSubmission?: MultiViewProductionSubmission,
1156
- observation?: ShadowCascadeObservation,
1157
415
  ): void {
1158
416
  const { runtime, pipelineState, validatedOrdered } = c;
1159
417
  // M-3 / w12: vertexBuffer/indexBuffer state locals migrate to GpuBuffer
@@ -1168,111 +426,13 @@ function recordShadowCasterDraws(
1168
426
  // bound by the setPipeline call above; the loop switches to a custom
1169
427
  // ShadowCaster PSO when a material supplies one.
1170
428
  let shadowLastPipeline: RenderPipeline = shadowPipeline;
1171
- const seenValidatedCandidateIndices = observation === undefined ? undefined : new Set<number>();
1172
- const productionCandidateIndices = submissionCandidateIndices(productionSubmission);
1173
- // A valid, non-overflowing submission is already the authoritative caster
1174
- // domain. Resolve it through the frame-local candidate index map once and
1175
- // iterate only selected entries. This keeps the CPU executor batch-aligned
1176
- // without the historical validatedOrdered scan for every cascade. Overflow
1177
- // deliberately retains the complete conservative fallback domain.
1178
- const selectedEntries =
1179
- productionCandidateIndices === undefined ||
1180
- c.validatedByCandidateIndex === undefined ||
1181
- c.validatedIndexByCandidateIndex === undefined
1182
- ? undefined
1183
- : [...productionCandidateIndices].flatMap((candidateIndex) => {
1184
- const entry = c.validatedByCandidateIndex?.get(candidateIndex);
1185
- return entry === undefined ? [] : [entry];
1186
- });
1187
- const entries = selectedEntries ?? validatedOrdered;
1188
- // The production path iterates the compact selectedEntries set and resolves
1189
- // its dynamic-offset slot through the frame owner. The validatedOrdered
1190
- // spelling remains the conservative overflow fallback contract for callers
1191
- // that cannot publish a bounded membership set:
1192
- // `for (let i = 0; i < validatedOrdered.length; i++)`.
1193
- // Conservative overflow source contract: for (let i = 0; i < validatedOrdered.length; i++) {
1194
- // shadowPass.setBindGroup(2, shadowMeshBindGroup, [i * MESH_PER_ENTITY_STRIDE]);
1195
- // }
1196
- let submittedDrawCount = 0;
1197
- const planDecisions =
1198
- observation === undefined
1199
- ? undefined
1200
- : new Map(
1201
- observation.plan.candidateDecisions?.map((decision) => [
1202
- decision.renderableIndex,
1203
- decision,
1204
- ]),
1205
- );
1206
429
 
1207
- for (let i = 0; i < entries.length; i++) {
1208
- const entry = entries[i];
430
+ for (let i = 0; i < validatedOrdered.length; i++) {
431
+ const entry = validatedOrdered[i];
1209
432
  if (entry === undefined) continue;
1210
433
 
1211
- const candidateIndex = entry.source.candidateIndex ?? entry.renderableIndex;
1212
- seenValidatedCandidateIndices?.add(candidateIndex);
1213
- const planDecision = planDecisions?.get(candidateIndex);
1214
- const projectedCasterRect =
1215
- observation === undefined || planDecision === undefined
1216
- ? undefined
1217
- : projectLightSpaceCasterRect(
1218
- observation.plan,
1219
- observation.viewport,
1220
- planDecision.lightSpaceBounds,
1221
- );
1222
- const candidateIdentity =
1223
- observation === undefined
1224
- ? undefined
1225
- : {
1226
- frame: observation.frame,
1227
- cascade: observation.cascade,
1228
- worldId: entry.source.worldId,
1229
- entityKey: entry.source.entityKey,
1230
- stableId:
1231
- observation.stableIdByCandidateIndex.get(candidateIndex) ??
1232
- worldEntityKey(entry.source.worldId, entry.source.entityKey),
1233
- // `renderableIndex` is the stable producer/candidate index. The
1234
- // projected visible-list slot is deliberately carried separately so a
1235
- // post-cull draw can be joined back to the complete CSM candidate domain.
1236
- renderableIndex: candidateIndex,
1237
- candidateIndex,
1238
- ...(projectedCasterRect === undefined ? {} : { projectedCasterRect }),
1239
- };
1240
-
1241
434
  // feat-20260609 M2: skip entities that don't match the pass selector.
1242
- if (matchedIndices !== null && !matchedIndices.has(entry.renderableIndex)) {
1243
- if (observation !== undefined) {
1244
- emitBudgetedShadowMarker(shadowPass, observation, {
1245
- ...candidateIdentity,
1246
- kind: 'candidate',
1247
- stage: 'cpu-not-selected',
1248
- reason: 'pass-selector-mismatch',
1249
- });
1250
- }
1251
- continue;
1252
- }
1253
- if (productionCandidateIndices !== undefined) {
1254
- if (!productionCandidateIndices.has(candidateIndex)) {
1255
- if (observation !== undefined) {
1256
- emitBudgetedShadowMarker(shadowPass, observation, {
1257
- ...candidateIdentity,
1258
- kind: 'candidate',
1259
- stage: 'cpu-not-selected',
1260
- reason: 'outside-multi-view-submission',
1261
- });
1262
- }
1263
- continue;
1264
- }
1265
- }
1266
- if (observation !== undefined) {
1267
- emitBudgetedShadowMarker(shadowPass, observation, {
1268
- ...candidateIdentity,
1269
- kind: 'candidate',
1270
- stage: 'multi-view-selected',
1271
- source: 'multi-view-indirect',
1272
- selected: true,
1273
- reason: 'batch-aligned-submission',
1274
- });
1275
- }
435
+ if (matchedIndices !== null && !matchedIndices.has(entry.renderableIndex)) continue;
1276
436
 
1277
437
  // bug-20260619-csm RC-3 (AC-10, D-3): resolve the per-entity shadow
1278
438
  // PSO from its ShadowCaster shader id. Default casters keep the
@@ -1319,14 +479,6 @@ function recordShadowCasterDraws(
1319
479
  (sm) => sm.topology === 'triangle-list' || sm.topology === 'triangle-strip',
1320
480
  );
1321
481
  if (!hasAnyShadowSubmesh) {
1322
- if (observation !== undefined) {
1323
- emitBudgetedShadowMarker(shadowPass, observation, {
1324
- ...candidateIdentity,
1325
- kind: 'candidate',
1326
- stage: 'draw-not-submitted',
1327
- reason: 'non-triangle-topology',
1328
- });
1329
- }
1330
482
  continue;
1331
483
  }
1332
484
 
@@ -1342,11 +494,7 @@ function recordShadowCasterDraws(
1342
494
  }
1343
495
  }
1344
496
 
1345
- const validatedIndex =
1346
- selectedEntries === undefined
1347
- ? i
1348
- : (c.validatedIndexByCandidateIndex?.get(candidateIndex) ?? i);
1349
- shadowPass.setBindGroup(2, shadowMeshBindGroup, [validatedIndex * MESH_PER_ENTITY_STRIDE]);
497
+ shadowPass.setBindGroup(2, shadowMeshBindGroup, [i * MESH_PER_ENTITY_STRIDE]);
1350
498
 
1351
499
  // C1 + C2 (w12): per-entity instance buffer + instanceCount
1352
500
  let shadowInstanceBuffer: Buffer = pipelineState.identityInstanceBuffer;
@@ -1365,13 +513,10 @@ function recordShadowCasterDraws(
1365
513
  const bufUsage = uniformFallback
1366
514
  ? GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST
1367
515
  : GPU_BUFFER_USAGE_STORAGE | GPU_BUFFER_USAGE_COPY_DST;
1368
- const upload = uniformFallback
516
+ const instancePayload = uniformFallback
1369
517
  ? shadowInst.transforms
1370
- : packTemporalInstanceTransforms(
1371
- shadowInst.transforms,
1372
- entry.source.temporal?.previousInstances?.transforms,
1373
- );
1374
- const requestedBytes = upload.byteLength;
518
+ : packInstanceStorageBuffer(shadowInst.transforms);
519
+ const requestedBytes = instancePayload.byteLength;
1375
520
  const cached = c.frameState.instanceBuffers.get(
1376
521
  worldEntityKey(entry.source.worldId, shadowInst.cacheKey),
1377
522
  );
@@ -1410,7 +555,11 @@ function recordShadowCasterDraws(
1410
555
  }
1411
556
  }
1412
557
  if (active !== null) {
1413
- const writeRes = runtime.device.queue.writeBuffer(active.buffer.handle, 0, upload);
558
+ const writeRes = runtime.device.queue.writeBuffer(
559
+ active.buffer.handle,
560
+ 0,
561
+ instancePayload,
562
+ );
1414
563
  if (!writeRes.ok) {
1415
564
  runtime.errorRegistry.fire(writeRes.error);
1416
565
  } else {
@@ -1456,614 +605,24 @@ function recordShadowCasterDraws(
1456
605
  // feat-20260608 M4 / w16: per-submesh shadow draw loop.
1457
606
  // Only draw submeshes whose topology is triangle-list or triangle-strip
1458
607
  // (line-list / point-list submeshes cast no shadow and are skipped).
1459
- for (let submeshIndex = 0; submeshIndex < shadowSubmeshes.length; submeshIndex++) {
1460
- const sm = shadowSubmeshes[submeshIndex];
1461
- if (sm === undefined) continue;
608
+ for (const sm of shadowSubmeshes) {
1462
609
  if (sm.topology !== 'triangle-list' && sm.topology !== 'triangle-strip') {
1463
610
  continue;
1464
611
  }
1465
612
  if (entry.mesh.indexed) {
1466
613
  shadowPass.drawIndexed(sm.indexCount, shadowInstanceCount, sm.indexOffset, 0, 0);
1467
- if (observation !== undefined) submittedDrawCount += 1;
1468
- if (observation !== undefined) {
1469
- emitBudgetedShadowMarker(shadowPass, observation, {
1470
- ...candidateIdentity,
1471
- kind: 'submitted-draw',
1472
- stage: 'submitted-draw',
1473
- source: 'record-after-draw-call',
1474
- submeshIndex,
1475
- drawKind: 'drawIndexed',
1476
- });
1477
- }
1478
614
  } else {
1479
615
  shadowPass.draw(sm.vertexCount, shadowInstanceCount, 0, 0);
1480
- if (observation !== undefined) submittedDrawCount += 1;
1481
- if (observation !== undefined) {
1482
- emitBudgetedShadowMarker(shadowPass, observation, {
1483
- ...candidateIdentity,
1484
- kind: 'submitted-draw',
1485
- stage: 'submitted-draw',
1486
- source: 'record-after-draw-call',
1487
- submeshIndex,
1488
- drawKind: 'draw',
1489
- });
1490
- }
1491
- }
1492
- }
1493
- }
1494
- if (observation !== undefined) {
1495
- shadowPass.insertDebugMarker(
1496
- shadowMarker({
1497
- kind: 'batch',
1498
- stage: 'multi-view-plan-vs-submitted',
1499
- source: 'batch-topology',
1500
- frame: observation.frame,
1501
- cascade: observation.cascade,
1502
- plannedDrawCount: productionSubmission?.plan.candidateCount ?? 0,
1503
- submittedDrawCount,
1504
- batchCount: productionSubmission?.plan.batches.length ?? 0,
1505
- aligned: (productionSubmission?.plan.candidateCount ?? 0) === submittedDrawCount,
1506
- }),
1507
- );
1508
- }
1509
- if (observation !== undefined && productionCandidateIndices !== undefined) {
1510
- for (const candidateIndex of productionCandidateIndices) {
1511
- if (seenValidatedCandidateIndices?.has(candidateIndex)) continue;
1512
- emitBudgetedShadowMarker(shadowPass, observation, {
1513
- kind: 'candidate',
1514
- stage: 'validation-missing',
1515
- source: 'multi-view-indirect',
1516
- frame: observation.frame,
1517
- cascade: observation.cascade,
1518
- stableId: observation.stableIdByCandidateIndex.get(candidateIndex),
1519
- candidateIndex,
1520
- selected: true,
1521
- reason: 'submitted-candidate-not-materialized',
1522
- });
1523
- }
1524
- }
1525
- }
1526
-
1527
- /**
1528
- * feat-20260612-point-light-shadows-urp-hdrp M3 / T-M3-2 (plan-strategy §D-1
1529
- * + §D-3 + AC-04). Records the 6 x N point-shadow caster passes — one render
1530
- * pass per (shadow-casting point light, cube face) pair — that write per-light
1531
- * cube_array atlas depth. Driven by the URP `addPointShadowPass` graph closure;
1532
- * gated upstream on `frameState.pointShadowSnapshots.length > 0` (AC-09 zero-
1533
- * shadow zero-pass).
1534
- *
1535
- * Pass count = 6 * N where N = `frameState.pointShadowSnapshots.length`. Each
1536
- * pass opens an INDEPENDENT command encoder + queue.submit so Dawn / WebGPU
1537
- * synchronizes the depth-write boundary with the subsequent atlas sample in
1538
- * the forward pass (RD-4 manual barrier; same pattern as `recordShadowPass`).
1539
- *
1540
- * Round-2 F-3 fix-up: actual geometry walk + draw landed. Per face we
1541
- * (1) write the face VP mat4 into `viewUniformBuffer` at offset 112 (the
1542
- * `lightSpaceMatrix` slot) — `forgeax::default-shadow-caster` reads
1543
- * `view.lightSpaceMatrix * worldPos` and the encoder boundary serializes
1544
- * the queue.writeBuffer with the subsequent draw, so each face sees its own
1545
- * VP without needing a per-face UBO bind. (2) Reuse the directional shadow
1546
- * caster PSO + cached `shadow-view-bg` / `shadow-material-singleton` /
1547
- * `shadow-mesh-bg` already built by `recordShadowPass`. (3) After all
1548
- * (snapshot, face) passes complete, restore `viewUniformBuffer.lightSpaceMatrix`
1549
- * to the directional value the main forward pass needs (the queue.submit
1550
- * ordering guarantees the restore lands before the main encoder runs).
1551
- *
1552
- * Caveat: the cached `view-shadow` BG binds `shadowAtlasFallbackTextureView`
1553
- * at binding 5 (NOT the real ShadowAtlas atlas view) so the BG is valid even
1554
- * while the real atlas faces are being written through the depth attachment;
1555
- * the shadow_caster.wgsl shader never samples binding 5 anyway (vertex-only
1556
- * pipeline).
1557
- *
1558
- * No new shader / no new PSO / no new BGL is required for this round; the
1559
- * change is purely runtime wiring. A dedicated `forgeax::default-point-
1560
- * shadow-caster` PSO with a per-face VP UBO + dynamic offset would let the
1561
- * pass run without touching `viewUniformBuffer.lightSpaceMatrix` and could
1562
- * be considered for a follow-on optimization milestone (`OOS-future`).
1563
- */
1564
- export function recordPointShadowPass(c: _InternalRenderPipelineContext): void {
1565
- const { runtime, frameState, meshBindGroup, pipelineState } = c;
1566
- const mainMembership = mainShadowMembership(c);
1567
- const snapshots = frameState.pointShadowSnapshots;
1568
- if (snapshots.length === 0) return;
1569
- const atlas = frameState.pointShadowAtlas;
1570
- if (atlas === null || !atlas.isAllocated()) return;
1571
-
1572
- // Reuse the directional shadow caster PSO -- its WGSL reads
1573
- // `view.lightSpaceMatrix * worldPos` which is exactly what we need once we
1574
- // overwrite the slot per face below.
1575
- const shadowPipeline =
1576
- runtime.getMaterialShaderPipeline?.(
1577
- 'forgeax::default-shadow-caster',
1578
- false,
1579
- undefined,
1580
- 'triangle-list',
1581
- undefined,
1582
- undefined,
1583
- 'shadow-caster',
1584
- ) ?? null;
1585
- if (shadowPipeline === null) return;
1586
-
1587
- // Snapshot the directional lightSpaceMatrix so we can restore it after the
1588
- // 6 x N face passes. recordShadowPass already wrote `viewUniformBuffer` at
1589
- // offset 112 above; mirror its source in `pipelineState.perPassResources`
1590
- // (cached by the directional path for Inspector consumption -- charter P4
1591
- // single SSOT). When no directional shadow is active the slot was zeroed
1592
- // by the viewPayload write at recordFrame top; we restore zeros.
1593
- const restoreLightSpaceMatrix = pipelineState.perPassResources.shadowLightSpaceMatrix;
1594
- const RESTORE_LSM_BYTES = 64; // mat4 = 16 floats x 4 B
1595
- const VIEW_UBO_LSM_OFFSET = 112;
1596
-
1597
- for (let i = 0; i < snapshots.length; i++) {
1598
- const snap = snapshots[i];
1599
- if (snap === undefined) continue;
1600
- for (let face = 0; face < 6; face++) {
1601
- // (1) Overwrite viewUniformBuffer.lightSpaceMatrix with this face's VP.
1602
- // shadowMatrices is Float32Array(96) = 6 mat4 in [+X,-X,+Y,-Y,+Z,-Z];
1603
- // pull the 16-float subarray for this face.
1604
- const faceVp = snap.shadowMatrices.subarray(face * 16, (face + 1) * 16);
1605
- const lsmWriteRes = runtime.device.queue.writeBuffer(
1606
- pipelineState.viewUniformBuffer,
1607
- VIEW_UBO_LSM_OFFSET,
1608
- faceVp,
1609
- );
1610
- if (!lsmWriteRes.ok) {
1611
- runtime.errorRegistry.fire(lsmWriteRes.error);
1612
- continue;
1613
- }
1614
-
1615
- const encResult = runtime.device.createCommandEncoder({
1616
- label: `point-shadow-l${snap.shadowAtlasLayer}-f${face}`,
1617
- });
1618
- if (!encResult.ok) {
1619
- runtime.errorRegistry.fire(encResult.error);
1620
- continue;
1621
- }
1622
- const enc = encResult.value;
1623
- let view: TextureView;
1624
- try {
1625
- view = atlas.faceView(snap.shadowAtlasLayer, face);
1626
- } catch (e) {
1627
- if (
1628
- e instanceof PointShadowAtlasUninitializedError ||
1629
- e instanceof PointShadowAtlasBoundsViolationError
1630
- ) {
1631
- runtime.errorRegistry.fire(e);
1632
- } else {
1633
- throw e;
1634
- }
1635
- continue;
1636
- }
1637
- const pass = enc.beginRenderPass(
1638
- buildBeginRenderPassDescriptor(
1639
- { colorFormats: [], depthFormat: 'depth32float', sampleCount: 1 },
1640
- { colorViews: [], depthView: view },
1641
- 'point-shadow-caster',
1642
- ) as never,
1643
- );
1644
-
1645
- // (2) Bind the same shadow PSO + view BG + material BG + mesh BG cached
1646
- // by recordShadowPass earlier in this frame. The shadow path's view BG
1647
- // binds the cube_array fallback at binding 5, so this BG is safe to
1648
- // use while the real atlas faces are render-attached here.
1649
- pass.setPipeline(shadowPipeline);
1650
- // Look up the shadow view BG built earlier this frame by recordShadowPass.
1651
- // `view-shadow` is the cache variant; keyed by the same handle objects.
1652
- // If the directional shadow path wasn't taken (castShadow:false),
1653
- // shadow-view-bg was never built -- skip the geometry walk in that case
1654
- // (the depth attachment was still cleared above which is the AC-04
1655
- // "atlas face cleared to far" minimum guarantee).
1656
- // Build (or reuse) the shadow-view BG. If the directional shadow path
1657
- // already populated it earlier this frame, the cache hits; otherwise
1658
- // (castShadow:false -- recordShadowPass never
1659
- // ran) we build it on-demand here so the point shadow caster has a
1660
- // valid b0 view BG even on directional-shadow-free scenes.
1661
- const cachedShadowViewBg = getOrCreateFromChain(
1662
- frameState.viewBindGroupCache,
1663
- [
1664
- pipelineState.viewUniformBuffer,
1665
- pipelineState.pointLightsBuffer,
1666
- pipelineState.spotLightsBuffer,
1667
- pipelineState.shadowFallbackTextureView,
1668
- pipelineState.perPassResources.shadowSampler,
1669
- pipelineState.shadowAtlasFallbackTextureView,
1670
- pipelineState.shadowParamsBuffer,
1671
- pipelineState.shadowCasterCascadeBuffer,
1672
- pipelineState.shadowFallbackTextureView,
1673
- ],
1674
- 'view-shadow',
1675
- () => {
1676
- const r = runtime.device.createBindGroup({
1677
- label: 'shadow-view-bg',
1678
- layout: pipelineState.viewBindGroupLayout,
1679
- entries: [
1680
- {
1681
- binding: 0,
1682
- resource: { kind: 'buffer', value: { buffer: pipelineState.viewUniformBuffer } },
1683
- },
1684
- {
1685
- binding: 1,
1686
- resource: { kind: 'buffer', value: { buffer: pipelineState.pointLightsBuffer } },
1687
- },
1688
- {
1689
- binding: 2,
1690
- resource: { kind: 'buffer', value: { buffer: pipelineState.spotLightsBuffer } },
1691
- },
1692
- {
1693
- binding: 3,
1694
- resource: { kind: 'textureView', value: pipelineState.shadowFallbackTextureView },
1695
- },
1696
- {
1697
- binding: 4,
1698
- resource: { kind: 'sampler', value: pipelineState.perPassResources.shadowSampler },
1699
- },
1700
- {
1701
- binding: 5,
1702
- resource: {
1703
- kind: 'textureView',
1704
- value: pipelineState.shadowAtlasFallbackTextureView,
1705
- },
1706
- },
1707
- {
1708
- binding: 6,
1709
- resource: { kind: 'buffer', value: { buffer: pipelineState.shadowParamsBuffer } },
1710
- },
1711
- // feat-20260625-spot-light-shadow-mapping M2 / w21 + w25: this
1712
- // on-demand shadow-view BG builder previously stopped at binding 6,
1713
- // predating the cascade UBO (binding 7) and the spot binding 8. It
1714
- // shares the 'view-shadow' cache variant with the directional
1715
- // shadow-pass builder above, so it must produce a BGL-identical bind
1716
- // group; bindings 7/8 are added here in lock-step (BGL declares
1717
- // 0..8 — the former binding 9 spot lightViewProj UBO folded into the
1718
- // View UBO in w25 for the WebGL2 fragment uniform-buffer budget).
1719
- {
1720
- binding: 7,
1721
- resource: {
1722
- kind: 'buffer',
1723
- value: { buffer: pipelineState.shadowCasterCascadeBuffer },
1724
- },
1725
- },
1726
- {
1727
- binding: 8,
1728
- resource: {
1729
- kind: 'textureView',
1730
- value: pipelineState.shadowFallbackTextureView,
1731
- },
1732
- },
1733
- ],
1734
- });
1735
- if (!r.ok) throw r.error;
1736
- return r.value;
1737
- },
1738
- c.bindGroupCounts,
1739
- );
1740
- // Same on-demand build for the dummy material BG (the shadow_caster
1741
- // shader does not consume @group(1) but the PSO requires the BGL
1742
- // to validate). Reuse the same singleton Map entry recordShadowPass
1743
- // writes so the two paths share one allocation per frame (D-6).
1744
- let cachedShadowMaterialBg = frameState.singletonMaterialCache.get(
1745
- 'shadow-material-singleton',
1746
- );
1747
- if (cachedShadowMaterialBg === undefined) {
1748
- const buildShadowMaterialSingleton = (): BindGroup => {
1749
- const fb = pipelineState.skylightFallback;
1750
- const fallbackEntries = [
1751
- {
1752
- binding: 0,
1753
- resource: {
1754
- kind: 'buffer' as const,
1755
- value: {
1756
- buffer: pipelineState.materialUniformBuffer.buffer,
1757
- offset: 0,
1758
- size: MATERIAL_PER_ENTITY_STRIDE,
1759
- },
1760
- },
1761
- },
1762
- {
1763
- binding: 1,
1764
- resource: { kind: 'sampler' as const, value: pipelineState.defaultSampler },
1765
- },
1766
- {
1767
- binding: 2,
1768
- resource: { kind: 'textureView' as const, value: pipelineState.fallbackTextureView },
1769
- },
1770
- {
1771
- binding: 3,
1772
- resource: { kind: 'sampler' as const, value: pipelineState.defaultSampler },
1773
- },
1774
- {
1775
- binding: 4,
1776
- resource: {
1777
- kind: 'textureView' as const,
1778
- value: pipelineState.defaultNormalTextureView,
1779
- },
1780
- },
1781
- {
1782
- binding: 5,
1783
- resource: { kind: 'sampler' as const, value: pipelineState.defaultSampler },
1784
- },
1785
- {
1786
- binding: 6,
1787
- resource: { kind: 'textureView' as const, value: pipelineState.fallbackTextureView },
1788
- },
1789
- {
1790
- binding: 7,
1791
- resource: { kind: 'sampler' as const, value: pipelineState.defaultSampler },
1792
- },
1793
- {
1794
- binding: 8,
1795
- resource: {
1796
- kind: 'textureView' as const,
1797
- value: pipelineState.defaultWhiteTextureView,
1798
- },
1799
- },
1800
- {
1801
- binding: 9,
1802
- resource: { kind: 'sampler' as const, value: pipelineState.defaultSampler },
1803
- },
1804
- {
1805
- binding: 10,
1806
- resource: {
1807
- kind: 'textureView' as const,
1808
- value: pipelineState.defaultWhiteTextureView,
1809
- },
1810
- },
1811
- {
1812
- binding: 11,
1813
- resource: { kind: 'sampler' as const, value: pipelineState.defaultSampler },
1814
- },
1815
- {
1816
- binding: 12,
1817
- resource: {
1818
- kind: 'textureView' as const,
1819
- value: pipelineState.defaultWhiteTextureView,
1820
- },
1821
- },
1822
- ];
1823
- const merged =
1824
- fb !== null
1825
- ? assembleMaterialWithSkylightEntries(fallbackEntries, {
1826
- irradianceView: fb.irradianceView,
1827
- irradianceSampler: fb.sampler,
1828
- prefilterView: fb.prefilterView,
1829
- prefilterSampler: fb.sampler,
1830
- brdfLutView: fb.brdfLutView,
1831
- brdfLutSampler: fb.sampler,
1832
- intensityBuffer: fb.intensityBuffer,
1833
- })
1834
- : fallbackEntries;
1835
- const r = runtime.device.createBindGroup({
1836
- label: 'shadow-material-bg',
1837
- layout: pipelineState.materialBindGroupLayout,
1838
- entries: merged,
1839
- });
1840
- if (!r.ok) throw r.error;
1841
- return r.value;
1842
- };
1843
- cachedShadowMaterialBg = buildShadowMaterialSingleton();
1844
- c.bindGroupCounts.createBindGroup += 1;
1845
- c.bindGroupCounts.keys.push('shadow-material-singleton');
1846
- frameState.singletonMaterialCache.set('shadow-material-singleton', cachedShadowMaterialBg);
1847
- }
1848
- if (meshBindGroup === null) {
1849
- pass.end();
1850
- const finishOnly = enc.finish();
1851
- if (!finishOnly.ok) {
1852
- runtime.errorRegistry.fire(finishOnly.error);
1853
- continue;
1854
- }
1855
- const submitOnly = runtime.device.queue.submit([finishOnly.value]);
1856
- if (!submitOnly.ok) {
1857
- runtime.errorRegistry.fire(submitOnly.error);
1858
- }
1859
- continue;
1860
- }
1861
- pass.setBindGroup(0, cachedShadowViewBg);
1862
- pass.setBindGroup(1, cachedShadowMaterialBg, [0]);
1863
-
1864
- // (3) Iterate the mesh-SSBO slot order and emit one drawIndexed per
1865
- // shadow-casting submesh. Same shape as the directional shadow loop;
1866
- // the per-instance instance buffers built earlier this frame in
1867
- // recordShadowPass are reused (cached on frameState.instanceBuffers).
1868
- let lastVB: GpuBuffer | null = null;
1869
- let lastIB: GpuBuffer | null = null;
1870
- const shadowEntries =
1871
- c.validatedShadowEntries ??
1872
- c.validatedOrdered.flatMap((entry, validatedIndex) =>
1873
- isMainShadowRenderable(entry, mainMembership) ? [{ entry, validatedIndex }] : [],
1874
- );
1875
- for (const { entry, validatedIndex: ei } of shadowEntries) {
1876
- const submeshes = entry.mesh.submeshes;
1877
- const hasTriangle = submeshes.some(
1878
- (sm) => sm.topology === 'triangle-list' || sm.topology === 'triangle-strip',
1879
- );
1880
- if (!hasTriangle) continue;
1881
- if (entry.mesh.vertexBuffer !== lastVB) {
1882
- pass.setVertexBuffer(0, entry.mesh.vertexBuffer.handle);
1883
- lastVB = entry.mesh.vertexBuffer;
1884
- }
1885
- if (entry.mesh.indexed && entry.mesh.indexBuffer !== lastIB && entry.mesh.indexBuffer) {
1886
- pass.setIndexBuffer(entry.mesh.indexBuffer.handle, entry.mesh.indexFormat);
1887
- lastIB = entry.mesh.indexBuffer;
1888
- }
1889
- pass.setBindGroup(2, meshBindGroup, [ei * MESH_PER_ENTITY_STRIDE]);
1890
- // Resolve the per-entity instance BG cached by recordShadowPass.
1891
- const inst = entry.source.instances;
1892
- let instCount = 1;
1893
- let instBufferKey: object = pipelineState.identityInstanceBuffer as unknown as object;
1894
- if (inst !== undefined) {
1895
- const cached = frameState.instanceBuffers.get(
1896
- worldEntityKey(entry.source.worldId, inst.cacheKey),
1897
- );
1898
- if (
1899
- cached !== undefined &&
1900
- cached.uploadedArchVersion === inst.archVersion &&
1901
- cached.uploadedByteLength ===
1902
- temporalInstanceUploadByteLength(inst.transforms, runtime.device.caps.storageBuffer)
1903
- ) {
1904
- instBufferKey = cached.buffer.handle as unknown as object;
1905
- instCount = Math.max(1, inst.instanceCount);
1906
- }
1907
- }
1908
- // D-4 read end: look up the leaf recordShadowPass wrote at :3439
1909
- // with the SAME (entityKey, instBuffer) pair. The single-handle chain
1910
- // stores the variant->BindGroup leaf Map directly under the buffer
1911
- // handle in the inner WeakMap, so the variant lookup is the third
1912
- // step. The inner WeakMap value is opaque (`unknown`); the 1-handle
1913
- // shadow-instances chain guarantees it is the leaf Map here.
1914
- const shadowInstLeaf = frameState.instancesBgPerEntity
1915
- .get(worldEntityKey(entry.source.worldId, entry.source.entityKey))
1916
- ?.get(instBufferKey) as Map<string, BindGroup> | undefined;
1917
- const cachedInstBg = shadowInstLeaf?.get('shadow-instances');
1918
- if (cachedInstBg === undefined) continue; // recordShadowPass should have populated it
1919
- pass.setBindGroup(3, cachedInstBg);
1920
- for (const sm of submeshes) {
1921
- if (sm.topology !== 'triangle-list' && sm.topology !== 'triangle-strip') continue;
1922
- if (entry.mesh.indexed) {
1923
- pass.drawIndexed(sm.indexCount, instCount, sm.indexOffset, 0, 0);
1924
- } else {
1925
- pass.draw(sm.vertexCount, instCount, 0, 0);
1926
- }
1927
- }
1928
- }
1929
-
1930
- pass.end();
1931
- const finishResult = enc.finish();
1932
- if (!finishResult.ok) {
1933
- runtime.errorRegistry.fire(finishResult.error);
1934
- continue;
1935
- }
1936
- const submitResult = runtime.device.queue.submit([finishResult.value]);
1937
- if (!submitResult.ok) {
1938
- runtime.errorRegistry.fire(submitResult.error);
1939
616
  }
1940
617
  }
1941
618
  }
1942
-
1943
- // (4) Restore viewUniformBuffer.lightSpaceMatrix to the directional value
1944
- // (or zero if no directional shadow this frame). The main forward pass
1945
- // builds its own viewBg from the SAME viewUniformBuffer + reads
1946
- // view.lightSpaceMatrix for directional shadow factor reconstruction.
1947
- const restoreBuf = new Float32Array(16);
1948
- if (restoreLightSpaceMatrix !== null) {
1949
- for (let i = 0; i < 16; i++) restoreBuf[i] = restoreLightSpaceMatrix[i] ?? 0;
1950
- }
1951
- // restoreBuf is already exactly 16 floats x 4 B = 64 B; no size override needed.
1952
- void RESTORE_LSM_BYTES;
1953
- const restoreRes = runtime.device.queue.writeBuffer(
1954
- pipelineState.viewUniformBuffer,
1955
- VIEW_UBO_LSM_OFFSET,
1956
- restoreBuf,
1957
- );
1958
- if (!restoreRes.ok) {
1959
- runtime.errorRegistry.fire(restoreRes.error);
1960
- }
1961
- }
1962
-
1963
- /**
1964
- * Build (or reuse) the shared `view-shadow` @group(0) BG for the spot shadow
1965
- * caster pass. Reuses the exact same cache key + entry shape as recordShadowPass
1966
- * / recordPointShadowPass so all three shadow paths share one allocation per
1967
- * frame (D-6). Returns null on a createBindGroup failure (fired to the error
1968
- * registry). The shadow_caster vertex shader reads only binding 0 (view UBO,
1969
- * carrying the spot matrix via shadowCasterCascade at binding 7) — bindings
1970
- * 3/5 bind fallback views since the depth attachment is written here.
1971
- */
1972
- function ensureSpotShadowViewBg(c: _InternalRenderPipelineContext): BindGroup | null {
1973
- const { runtime, frameState, pipelineState } = c;
1974
- try {
1975
- return getOrCreateFromChain(
1976
- frameState.viewBindGroupCache,
1977
- [
1978
- pipelineState.viewUniformBuffer,
1979
- pipelineState.pointLightsBuffer,
1980
- pipelineState.spotLightsBuffer,
1981
- pipelineState.shadowFallbackTextureView,
1982
- pipelineState.perPassResources.shadowSampler,
1983
- pipelineState.shadowAtlasFallbackTextureView,
1984
- pipelineState.shadowParamsBuffer,
1985
- pipelineState.shadowCasterCascadeBuffer,
1986
- pipelineState.shadowFallbackTextureView,
1987
- ],
1988
- 'view-shadow',
1989
- () => {
1990
- const r = runtime.device.createBindGroup({
1991
- label: 'shadow-view-bg',
1992
- layout: pipelineState.viewBindGroupLayout,
1993
- entries: [
1994
- {
1995
- binding: 0,
1996
- resource: { kind: 'buffer', value: { buffer: pipelineState.viewUniformBuffer } },
1997
- },
1998
- {
1999
- binding: 1,
2000
- resource: { kind: 'buffer', value: { buffer: pipelineState.pointLightsBuffer } },
2001
- },
2002
- {
2003
- binding: 2,
2004
- resource: { kind: 'buffer', value: { buffer: pipelineState.spotLightsBuffer } },
2005
- },
2006
- {
2007
- binding: 3,
2008
- resource: { kind: 'textureView', value: pipelineState.shadowFallbackTextureView },
2009
- },
2010
- {
2011
- binding: 4,
2012
- resource: { kind: 'sampler', value: pipelineState.perPassResources.shadowSampler },
2013
- },
2014
- {
2015
- binding: 5,
2016
- resource: {
2017
- kind: 'textureView',
2018
- value: pipelineState.shadowAtlasFallbackTextureView,
2019
- },
2020
- },
2021
- {
2022
- binding: 6,
2023
- resource: { kind: 'buffer', value: { buffer: pipelineState.shadowParamsBuffer } },
2024
- },
2025
- {
2026
- binding: 7,
2027
- resource: {
2028
- kind: 'buffer',
2029
- value: { buffer: pipelineState.shadowCasterCascadeBuffer },
2030
- },
2031
- },
2032
- // feat-20260625-spot-light-shadow-mapping M3 / w21 (D-5): spot
2033
- // shadow 2D atlas. The spot caster pass write-attaches
2034
- // spotShadowDepth, so bind the 1x1 fallback here (the caster shader
2035
- // never samples binding 8 — this entry only satisfies the BGL).
2036
- {
2037
- binding: 8,
2038
- resource: {
2039
- kind: 'textureView',
2040
- value: pipelineState.shadowFallbackTextureView,
2041
- },
2042
- },
2043
- // feat-20260625-spot-light-shadow-mapping w25: spot lightViewProj
2044
- // matrices folded into the View UBO (binding 0) — no binding 9.
2045
- // binding 8 is the last shared view-BG entry.
2046
- ],
2047
- });
2048
- if (!r.ok) throw r.error;
2049
- return r.value;
2050
- },
2051
- c.bindGroupCounts,
2052
- );
2053
- } catch (e) {
2054
- if (e instanceof RhiError) {
2055
- runtime.errorRegistry.fire(e);
2056
- return null;
2057
- }
2058
- throw e;
2059
- }
2060
619
  }
2061
620
 
2062
621
  /**
2063
622
  * Build (or reuse) the dummy `shadow-material-singleton` @group(1) BG for the
2064
623
  * spot shadow caster pass. The vertex-only shadow_caster shader never consumes
2065
624
  * @group(1) but the PSO's BGL must validate. Reuses the same singleton Map
2066
- * entry recordShadowPass / recordPointShadowPass write so the three paths share
625
+ * entry encodeDirectionalShadowPass / encodePointShadowPass write so the three paths share
2067
626
  * one allocation per frame (D-6).
2068
627
  */
2069
628
  function ensureSpotShadowMaterialBg(c: _InternalRenderPipelineContext): BindGroup | null {
@@ -2071,60 +630,57 @@ function ensureSpotShadowMaterialBg(c: _InternalRenderPipelineContext): BindGrou
2071
630
  const cached = frameState.singletonMaterialCache.get('shadow-material-singleton');
2072
631
  if (cached !== undefined) return cached;
2073
632
  const fb = pipelineState.skylightFallback;
2074
- const fallbackEntries = [
2075
- {
2076
- binding: 0,
2077
- resource: {
2078
- kind: 'buffer' as const,
2079
- value: {
2080
- buffer: pipelineState.materialUniformBuffer.buffer,
2081
- offset: 0,
2082
- size: MATERIAL_PER_ENTITY_STRIDE,
633
+ const fallbackEntries: BindGroupEntry[] = buildPbrMaterialUserRegionEntries().map((entry) => {
634
+ if (entry.buffer !== undefined) {
635
+ return {
636
+ binding: entry.binding,
637
+ resource: {
638
+ kind: 'buffer' as const,
639
+ value: {
640
+ buffer: pipelineState.materialUniformBuffer.buffer,
641
+ offset: 0,
642
+ size: STANDARD_PBR_UBO_SIZE,
643
+ },
2083
644
  },
645
+ };
646
+ }
647
+ if (entry.sampler !== undefined) {
648
+ return {
649
+ binding: entry.binding,
650
+ resource: { kind: 'sampler' as const, value: pipelineState.defaultSampler },
651
+ };
652
+ }
653
+ return {
654
+ binding: entry.binding,
655
+ resource: {
656
+ kind: 'textureView' as const,
657
+ value:
658
+ entry.binding === 6
659
+ ? pipelineState.defaultNormalTextureView
660
+ : pipelineState.fallbackTextureView,
2084
661
  },
2085
- },
2086
- { binding: 1, resource: { kind: 'sampler' as const, value: pipelineState.defaultSampler } },
2087
- {
2088
- binding: 2,
2089
- resource: { kind: 'textureView' as const, value: pipelineState.fallbackTextureView },
2090
- },
2091
- { binding: 3, resource: { kind: 'sampler' as const, value: pipelineState.defaultSampler } },
2092
- {
2093
- binding: 4,
2094
- resource: { kind: 'textureView' as const, value: pipelineState.defaultNormalTextureView },
2095
- },
2096
- { binding: 5, resource: { kind: 'sampler' as const, value: pipelineState.defaultSampler } },
2097
- {
2098
- binding: 6,
2099
- resource: { kind: 'textureView' as const, value: pipelineState.fallbackTextureView },
2100
- },
2101
- { binding: 7, resource: { kind: 'sampler' as const, value: pipelineState.defaultSampler } },
2102
- {
2103
- binding: 8,
2104
- resource: { kind: 'textureView' as const, value: pipelineState.defaultWhiteTextureView },
2105
- },
2106
- { binding: 9, resource: { kind: 'sampler' as const, value: pipelineState.defaultSampler } },
2107
- {
2108
- binding: 10,
2109
- resource: { kind: 'textureView' as const, value: pipelineState.defaultWhiteTextureView },
2110
- },
2111
- { binding: 11, resource: { kind: 'sampler' as const, value: pipelineState.defaultSampler } },
2112
- {
2113
- binding: 12,
2114
- resource: { kind: 'textureView' as const, value: pipelineState.defaultWhiteTextureView },
2115
- },
2116
- ];
662
+ };
663
+ });
2117
664
  const merged =
2118
665
  fb !== null
2119
- ? assembleMaterialWithSkylightEntries(fallbackEntries, {
2120
- irradianceView: fb.irradianceView,
2121
- irradianceSampler: fb.sampler,
2122
- prefilterView: fb.prefilterView,
2123
- prefilterSampler: fb.sampler,
2124
- brdfLutView: fb.brdfLutView,
2125
- brdfLutSampler: fb.sampler,
2126
- intensityBuffer: fb.intensityBuffer,
2127
- })
666
+ ? assembleMaterialWithSkylightEntries(
667
+ fallbackEntries,
668
+ {
669
+ irradianceView: fb.irradianceView,
670
+ irradianceSampler: fb.sampler,
671
+ prefilterView: fb.prefilterView,
672
+ prefilterSampler: fb.sampler,
673
+ brdfLutView: fb.brdfLutView,
674
+ brdfLutSampler: fb.sampler,
675
+ intensityBuffer: fb.intensityBuffer,
676
+ },
677
+ {
678
+ emissiveSampler: pipelineState.defaultSampler,
679
+ emissiveView: pipelineState.defaultWhiteTextureView,
680
+ occlusionSampler: pipelineState.defaultSampler,
681
+ occlusionView: pipelineState.defaultWhiteTextureView,
682
+ },
683
+ )
2128
684
  : fallbackEntries;
2129
685
  const r = runtime.device.createBindGroup({
2130
686
  label: 'shadow-material-bg',
@@ -2140,248 +696,3 @@ function ensureSpotShadowMaterialBg(c: _InternalRenderPipelineContext): BindGrou
2140
696
  frameState.singletonMaterialCache.set('shadow-material-singleton', r.value);
2141
697
  return r.value;
2142
698
  }
2143
-
2144
- /**
2145
- * feat-20260625-spot-light-shadow-mapping M2 / w11 (D-1 + D-2). Records the
2146
- * spot-light shadow caster passes — one render pass per castShadow spot (cap 4)
2147
- * — that write each spot's perspective depth into its `spotShadowDepth` atlas
2148
- * tile. Driven by the URP `addSpotShadowPass` graph closure; reads
2149
- * `frameState.spotShadowSnapshots`.
2150
- *
2151
- * Per spot with `shadowAtlasTile >= 0` (castShadow + non-degenerate dir + not
2152
- * clipped) and a valid `lightViewProj`:
2153
- * (1) Write the spot perspective matrix into `shadowCasterCascadeBuffer`
2154
- * (D-1: spotLightViewProj lane at byte 16) and set `isSpot = 1` so the
2155
- * shadow_caster vertex shader routes through the spot matrix instead of
2156
- * the directional `view.lightViewProj_A..D` slots. The per-pass
2157
- * independent command encoder + queue.submit serialize each host write
2158
- * against that pass's GPU read.
2159
- * (2) Open an INDEPENDENT command encoder targeting the spotShadowDepth view
2160
- * with `depthLoadOp = (first valid tile ? clear : load)` — independent
2161
- * clear counting decoupled from the directional cascadeIndex (D-2). Set a
2162
- * per-tile viewport (col = tile % 2, row = tile / 2).
2163
- * (3) Reuse the vertex-only `forgeax::default-shadow-caster` PSO + the cached
2164
- * `view-shadow` BG + `shadow-material-singleton` + meshBindGroup built by
2165
- * recordShadowPass (or built on-demand here when no directional shadow
2166
- * ran this frame).
2167
- *
2168
- * AC-03 zero-shadow zero-pass: early-returns when no spot has a valid tile, so
2169
- * castShadow:false scenes (all tile=-1) record zero spot passes. After the
2170
- * loop, `isSpot` is reset to 0 so the next directional cascade pass (or next
2171
- * frame) sees the directional routing.
2172
- */
2173
- export function recordSpotShadowPass(c: _InternalRenderPipelineContext): void {
2174
- const { runtime, frameState, meshBindGroup, pipelineState } = c;
2175
- const snapshots = frameState.spotShadowSnapshots;
2176
- // AC-03: skip entirely when no spot projects a shadow this frame.
2177
- const casters = snapshots.filter((s) => s.shadowAtlasTile >= 0 && s.lightViewProj !== undefined);
2178
- if (casters.length === 0) return;
2179
- if (meshBindGroup === null) return;
2180
-
2181
- const shadowPipeline =
2182
- runtime.getMaterialShaderPipeline?.(
2183
- 'forgeax::default-shadow-caster',
2184
- false,
2185
- undefined,
2186
- 'triangle-list',
2187
- undefined,
2188
- undefined,
2189
- 'shadow-caster',
2190
- ) ?? null;
2191
- if (shadowPipeline === null) return;
2192
-
2193
- const spotView = frameState.currentSpotShadowView;
2194
- if (spotView === null) return;
2195
-
2196
- const shadowViewBg = ensureSpotShadowViewBg(c);
2197
- const shadowMaterialBg = ensureSpotShadowMaterialBg(c);
2198
- if (shadowViewBg === null || shadowMaterialBg === null) return;
2199
-
2200
- const tileCtx: SpotTileContext = {
2201
- spotView,
2202
- shadowViewBg,
2203
- shadowMaterialBg,
2204
- shadowPipeline,
2205
- meshBindGroup,
2206
- tileSize: pipelineState.perPassResources.shadowMapSize,
2207
- };
2208
-
2209
- let clearedAtlas = false;
2210
- for (let ci = 0; ci < casters.length; ci++) {
2211
- const snap = casters[ci];
2212
- if (snap === undefined || snap.lightViewProj === undefined) continue;
2213
- if (recordSpotTile(c, snap, ci, casters.length, clearedAtlas, tileCtx)) {
2214
- clearedAtlas = true;
2215
- }
2216
- }
2217
-
2218
- // Reset isSpot so the next directional cascade pass / next frame routes
2219
- // through view.lightViewProj_A..D again (D-1).
2220
- const resetPayload = new Uint32Array([0, 0, 0, 0]);
2221
- const resetWrite = runtime.device.queue.writeBuffer(
2222
- pipelineState.shadowCasterCascadeBuffer,
2223
- 0,
2224
- resetPayload,
2225
- );
2226
- if (!resetWrite.ok) runtime.errorRegistry.fire(resetWrite.error);
2227
- }
2228
-
2229
- /** Per-tile resources shared across the spot caster passes in one frame. */
2230
- interface SpotTileContext {
2231
- readonly spotView: TextureView;
2232
- readonly shadowViewBg: BindGroup;
2233
- readonly shadowMaterialBg: BindGroup;
2234
- readonly shadowPipeline: NonNullable<
2235
- ReturnType<NonNullable<_InternalRenderPipelineContext['runtime']['getMaterialShaderPipeline']>>
2236
- >;
2237
- readonly meshBindGroup: BindGroup;
2238
- readonly tileSize: number;
2239
- }
2240
-
2241
- /**
2242
- * Render one spot's perspective depth into its atlas tile. Writes isSpot=1 +
2243
- * the spot matrix into the cascade UBO (D-1), opens an independent encoder with
2244
- * a per-tile viewport, and clears the atlas on the first tile / loads on the
2245
- * rest (D-2). Returns true when this pass cleared the atlas (so the caller
2246
- * tracks the clear/load boundary). Errors fire to the registry and return the
2247
- * incoming `alreadyCleared` unchanged.
2248
- */
2249
- function recordSpotTile(
2250
- c: _InternalRenderPipelineContext,
2251
- snapshot: SpotLightSnapshot,
2252
- spotIndex: number,
2253
- spotCount: number,
2254
- alreadyCleared: boolean,
2255
- ctx: SpotTileContext,
2256
- ): boolean {
2257
- const { runtime, pipelineState } = c;
2258
- const lightViewProj = snapshot.lightViewProj;
2259
- const tile = snapshot.shadowAtlasTile;
2260
- if (lightViewProj === undefined || tile < 0) return alreadyCleared;
2261
- // D-1: cascade UBO byte offset of the spotLightViewProj mat4 lane (16 B for
2262
- // index/isSpot/2pad vec4, then the mat4). isSpot lane is u32 word 1.
2263
- const SPOT_LVP_OFFSET = 16;
2264
-
2265
- const headerWrite = runtime.device.queue.writeBuffer(
2266
- pipelineState.shadowCasterCascadeBuffer,
2267
- 0,
2268
- new Uint32Array([0, 1, 0, 0]), // index=0, isSpot=1, pad
2269
- );
2270
- if (!headerWrite.ok) {
2271
- runtime.errorRegistry.fire(headerWrite.error);
2272
- return alreadyCleared;
2273
- }
2274
- const matWrite = runtime.device.queue.writeBuffer(
2275
- pipelineState.shadowCasterCascadeBuffer,
2276
- SPOT_LVP_OFFSET,
2277
- lightViewProj,
2278
- );
2279
- if (!matWrite.ok) {
2280
- runtime.errorRegistry.fire(matWrite.error);
2281
- return alreadyCleared;
2282
- }
2283
-
2284
- const encResult = runtime.device.createCommandEncoder({ label: `spot-shadow-tile${tile}` });
2285
- if (!encResult.ok) {
2286
- runtime.errorRegistry.fire(encResult.error);
2287
- return alreadyCleared;
2288
- }
2289
- const enc = encResult.value;
2290
- const pass: RhiRenderPassEncoder = enc.beginRenderPass(
2291
- buildBeginRenderPassDescriptor(
2292
- { colorFormats: [], depthFormat: 'depth32float', sampleCount: 1 },
2293
- { colorViews: [], depthView: ctx.spotView },
2294
- 'shadow-caster',
2295
- { depthLoadOp: alreadyCleared ? 'load' : 'clear' },
2296
- ) as never,
2297
- );
2298
-
2299
- pass.setPipeline(ctx.shadowPipeline);
2300
- const col = tile % 2;
2301
- const row = Math.floor(tile / 2);
2302
- pass.setViewport(col * ctx.tileSize, row * ctx.tileSize, ctx.tileSize, ctx.tileSize, 0, 1);
2303
- pass.setBindGroup(0, ctx.shadowViewBg);
2304
- pass.setBindGroup(1, ctx.shadowMaterialBg, [0]);
2305
- emitSpotShadowPassBeginMarker(c, pass, snapshot, spotIndex, spotCount);
2306
- recordSpotShadowGeometry(c, pass, ctx.meshBindGroup);
2307
- emitSpotShadowPassEndMarker(c, pass, snapshot);
2308
-
2309
- pass.end();
2310
- const finishResult = enc.finish();
2311
- if (!finishResult.ok) {
2312
- runtime.errorRegistry.fire(finishResult.error);
2313
- return alreadyCleared;
2314
- }
2315
- const submitResult = runtime.device.queue.submit([finishResult.value]);
2316
- if (!submitResult.ok) runtime.errorRegistry.fire(submitResult.error);
2317
- return true;
2318
- }
2319
-
2320
- /**
2321
- * Walk the mesh-SSBO slot order and emit shadow-caster draws for the current
2322
- * spot tile pass. Triangle topologies only (line/point meshes cast no shadow).
2323
- * Reuses the per-entity instance BGs cached by recordShadowPass; falls back to
2324
- * the identity instance buffer when none is cached.
2325
- */
2326
- function recordSpotShadowGeometry(
2327
- c: _InternalRenderPipelineContext,
2328
- pass: RhiRenderPassEncoder,
2329
- meshBindGroup: BindGroup,
2330
- ): void {
2331
- const { frameState, pipelineState } = c;
2332
- const mainMembership = mainShadowMembership(c);
2333
- let lastVB: GpuBuffer | null = null;
2334
- let lastIB: GpuBuffer | null = null;
2335
- const shadowEntries =
2336
- c.validatedShadowEntries ??
2337
- c.validatedOrdered.flatMap((entry, validatedIndex) =>
2338
- isMainShadowRenderable(entry, mainMembership) ? [{ entry, validatedIndex }] : [],
2339
- );
2340
- for (const { entry, validatedIndex: ei } of shadowEntries) {
2341
- const submeshes = entry.mesh.submeshes;
2342
- const hasTriangle = submeshes.some(
2343
- (sm) => sm.topology === 'triangle-list' || sm.topology === 'triangle-strip',
2344
- );
2345
- if (!hasTriangle) continue;
2346
- if (entry.mesh.vertexBuffer !== lastVB) {
2347
- pass.setVertexBuffer(0, entry.mesh.vertexBuffer.handle);
2348
- lastVB = entry.mesh.vertexBuffer;
2349
- }
2350
- if (entry.mesh.indexed && entry.mesh.indexBuffer !== lastIB && entry.mesh.indexBuffer) {
2351
- pass.setIndexBuffer(entry.mesh.indexBuffer.handle, entry.mesh.indexFormat);
2352
- lastIB = entry.mesh.indexBuffer;
2353
- }
2354
- pass.setBindGroup(2, meshBindGroup, [ei * MESH_PER_ENTITY_STRIDE]);
2355
- const inst = entry.source.instances;
2356
- let instCount = 1;
2357
- let instBufferKey: object = pipelineState.identityInstanceBuffer as unknown as object;
2358
- if (inst !== undefined) {
2359
- const cached = frameState.instanceBuffers.get(
2360
- worldEntityKey(entry.source.worldId, inst.cacheKey),
2361
- );
2362
- if (
2363
- cached !== undefined &&
2364
- cached.uploadedArchVersion === inst.archVersion &&
2365
- cached.uploadedByteLength ===
2366
- temporalInstanceUploadByteLength(inst.transforms, c.runtime.device.caps.storageBuffer)
2367
- ) {
2368
- instBufferKey = cached.buffer.handle as unknown as object;
2369
- instCount = Math.max(1, inst.instanceCount);
2370
- }
2371
- }
2372
- const spotInstLeaf = frameState.instancesBgPerEntity
2373
- .get(worldEntityKey(entry.source.worldId, entry.source.entityKey))
2374
- ?.get(instBufferKey) as Map<string, BindGroup> | undefined;
2375
- const cachedInstBg = spotInstLeaf?.get('shadow-instances');
2376
- if (cachedInstBg === undefined) continue;
2377
- pass.setBindGroup(3, cachedInstBg);
2378
- for (const sm of submeshes) {
2379
- if (sm.topology !== 'triangle-list' && sm.topology !== 'triangle-strip') continue;
2380
- if (entry.mesh.indexed) {
2381
- pass.drawIndexed(sm.indexCount, instCount, sm.indexOffset, 0, 0);
2382
- } else {
2383
- pass.draw(sm.vertexCount, instCount, 0, 0);
2384
- }
2385
- }
2386
- }
2387
- }