@forgeax/engine-render 0.1.34 → 0.1.36

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 (273) hide show
  1. package/README.md +69 -3
  2. package/dist/assembly/device-feature-admission.d.ts +2 -2
  3. package/dist/assembly/host-contract.d.ts +3 -2
  4. package/dist/assembly/host-contract.d.ts.map +1 -1
  5. package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -1
  6. package/dist/assembly/render-target-host.d.ts +5 -0
  7. package/dist/assembly/render-target-host.d.ts.map +1 -1
  8. package/dist/assembly/renderer-facade.d.ts.map +1 -1
  9. package/dist/assembly/webgpu-renderer.d.ts +1 -0
  10. package/dist/assembly/webgpu-renderer.d.ts.map +1 -1
  11. package/dist/assembly/webgpu-vertex-layouts.d.ts.map +1 -1
  12. package/dist/authoring.mjs +2 -4
  13. package/dist/authoring.mjs.map +1 -1
  14. package/dist/chunk-2OZAS3WU.mjs +3489 -0
  15. package/dist/chunk-2OZAS3WU.mjs.map +1 -0
  16. package/dist/{chunk-UI5SFODX.mjs → chunk-BZQ4CDRF.mjs} +2338 -1991
  17. package/dist/chunk-BZQ4CDRF.mjs.map +1 -0
  18. package/dist/{chunk-MKDIT2EM.mjs → chunk-CJOV2XLO.mjs} +216 -261
  19. package/dist/chunk-CJOV2XLO.mjs.map +1 -0
  20. package/dist/chunk-D6WSBXJ2.mjs +1842 -0
  21. package/dist/chunk-D6WSBXJ2.mjs.map +1 -0
  22. package/dist/{chunk-HEB564WF.mjs → chunk-DRF6OULX.mjs} +4 -4
  23. package/dist/{chunk-HEB564WF.mjs.map → chunk-DRF6OULX.mjs.map} +1 -1
  24. package/dist/{chunk-OCDLEI6I.mjs → chunk-OB5WAFYH.mjs} +117 -5
  25. package/dist/chunk-OB5WAFYH.mjs.map +1 -0
  26. package/dist/{chunk-ODZWR6JQ.mjs → chunk-OEYKKMXG.mjs} +2042 -27
  27. package/dist/chunk-OEYKKMXG.mjs.map +1 -0
  28. package/dist/chunk-RIPAEGI6.mjs +48 -0
  29. package/dist/chunk-RIPAEGI6.mjs.map +1 -0
  30. package/dist/{chunk-ELJFJ2BU.mjs → chunk-X2KA6WHM.mjs} +29 -4
  31. package/dist/chunk-X2KA6WHM.mjs.map +1 -0
  32. package/dist/{chunk-BLXGONR2.mjs → chunk-XSFB7XXQ.mjs} +51 -4
  33. package/dist/chunk-XSFB7XXQ.mjs.map +1 -0
  34. package/dist/cloud/frame-context.d.ts +2 -2
  35. package/dist/cloud/frame-context.d.ts.map +1 -1
  36. package/dist/construct-renderer.mjs +936 -2140
  37. package/dist/construct-renderer.mjs.map +1 -1
  38. package/dist/device/device-scope.d.ts.map +1 -1
  39. package/dist/device/gpu-residency.d.ts +19 -19
  40. package/dist/device/gpu-residency.d.ts.map +1 -1
  41. package/dist/errors/render.d.ts +3 -2
  42. package/dist/errors/render.d.ts.map +1 -1
  43. package/dist/extract/material-context.d.ts +7 -0
  44. package/dist/extract/material-context.d.ts.map +1 -0
  45. package/dist/features/host.d.ts +5 -0
  46. package/dist/features/host.d.ts.map +1 -1
  47. package/dist/features/particle-mesh-layout.d.ts +19 -0
  48. package/dist/features/particle-mesh-layout.d.ts.map +1 -0
  49. package/dist/features/plan.d.ts +2 -0
  50. package/dist/features/plan.d.ts.map +1 -1
  51. package/dist/features/types.d.ts +8 -0
  52. package/dist/features/types.d.ts.map +1 -1
  53. package/dist/glyph-text-layout-system.d.ts +1 -1
  54. package/dist/glyph-text-layout-system.d.ts.map +1 -1
  55. package/dist/gpu-driven/production-raster.d.ts +1 -1
  56. package/dist/gpu-driven/production-raster.d.ts.map +1 -1
  57. package/dist/gpu-driven/shadow-ownership.d.ts +3 -3
  58. package/dist/gpu-driven/shadow-ownership.d.ts.map +1 -1
  59. package/dist/gpu-driven/shadow-views.d.ts +1 -1
  60. package/dist/gpu-driven/shadow-views.d.ts.map +1 -1
  61. package/dist/gpu-driven/view-gpu.d.ts +1 -1
  62. package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
  63. package/dist/ibl/frame-resources.d.ts.map +1 -1
  64. package/dist/ibl/skylight-bind-group.d.ts +4 -4
  65. package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
  66. package/dist/index.d.ts +3 -0
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.mjs +542 -9
  69. package/dist/index.mjs.map +1 -1
  70. package/dist/instances.d.ts +3 -4
  71. package/dist/instances.d.ts.map +1 -1
  72. package/dist/internal.mjs +7 -8
  73. package/dist/internal.mjs.map +1 -1
  74. package/dist/pbr-pipeline.d.ts +3 -3
  75. package/dist/pbr-pipeline.d.ts.map +1 -1
  76. package/dist/pipeline/standard-output/lut-gpu.d.ts +2 -2
  77. package/dist/pipeline/standard-output/lut-gpu.d.ts.map +1 -1
  78. package/dist/pipeline/standard-output/resources.d.ts +2 -2
  79. package/dist/pipeline/standard-output/resources.d.ts.map +1 -1
  80. package/dist/publication/contract.d.ts +77 -0
  81. package/dist/publication/contract.d.ts.map +1 -0
  82. package/dist/publication/dependencies.d.ts +7 -0
  83. package/dist/publication/dependencies.d.ts.map +1 -0
  84. package/dist/publication/prepare-geometry.d.ts +9 -0
  85. package/dist/publication/prepare-geometry.d.ts.map +1 -0
  86. package/dist/publication/programs.d.ts +5 -0
  87. package/dist/publication/programs.d.ts.map +1 -0
  88. package/dist/publication/publisher.d.ts +34 -0
  89. package/dist/publication/publisher.d.ts.map +1 -0
  90. package/dist/publication/receiver.d.ts +29 -0
  91. package/dist/publication/receiver.d.ts.map +1 -0
  92. package/dist/publication/resource-scope.d.ts +18 -0
  93. package/dist/publication/resource-scope.d.ts.map +1 -0
  94. package/dist/publication/targets.d.ts +33 -0
  95. package/dist/publication/targets.d.ts.map +1 -0
  96. package/dist/publication/video.d.ts +5 -0
  97. package/dist/publication/video.d.ts.map +1 -0
  98. package/dist/record/fold-buckets.d.ts +3 -3
  99. package/dist/record/fold-buckets.d.ts.map +1 -1
  100. package/dist/record/frame-snapshot.d.ts +6 -6
  101. package/dist/record/frame-snapshot.d.ts.map +1 -1
  102. package/dist/record/frame.d.ts +5 -4
  103. package/dist/record/frame.d.ts.map +1 -1
  104. package/dist/record/helpers.d.ts +2 -2
  105. package/dist/record/helpers.d.ts.map +1 -1
  106. package/dist/record/main-pass-geometry.d.ts +2 -2
  107. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  108. package/dist/record/main-pass-material.d.ts +7 -9
  109. package/dist/record/main-pass-material.d.ts.map +1 -1
  110. package/dist/record/main-pass-sprite-draws.d.ts +3 -16
  111. package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
  112. package/dist/record/main-pass.d.ts.map +1 -1
  113. package/dist/record/prepared-material-bindings.d.ts +2 -3
  114. package/dist/record/prepared-material-bindings.d.ts.map +1 -1
  115. package/dist/record/render-context.d.ts +4 -2
  116. package/dist/record/render-context.d.ts.map +1 -1
  117. package/dist/record/shadow-pass.d.ts +2 -2
  118. package/dist/record/shadow-pass.d.ts.map +1 -1
  119. package/dist/record/sprite-instance-buffer.d.ts +31 -0
  120. package/dist/record/sprite-instance-buffer.d.ts.map +1 -0
  121. package/dist/record/target-capture-graph.d.ts +19 -0
  122. package/dist/record/target-capture-graph.d.ts.map +1 -0
  123. package/dist/record/typed-frame-graph.d.ts +2 -13
  124. package/dist/record/typed-frame-graph.d.ts.map +1 -1
  125. package/dist/record/view-ubo.d.ts.map +1 -1
  126. package/dist/record/volume-params.d.ts +2 -2
  127. package/dist/record/volume-params.d.ts.map +1 -1
  128. package/dist/recovery/render-system-candidate.d.ts +5 -12
  129. package/dist/recovery/render-system-candidate.d.ts.map +1 -1
  130. package/dist/render-contract.d.ts +4 -1
  131. package/dist/render-contract.d.ts.map +1 -1
  132. package/dist/render-system-extract-tail.d.ts.map +1 -1
  133. package/dist/render-system-extract.d.ts +2 -0
  134. package/dist/render-system-extract.d.ts.map +1 -1
  135. package/dist/render-system-presentation.d.ts +2 -2
  136. package/dist/render-system-presentation.d.ts.map +1 -1
  137. package/dist/render-system-projections.d.ts +3 -2
  138. package/dist/render-system-projections.d.ts.map +1 -1
  139. package/dist/render-system-residency.d.ts +2 -2
  140. package/dist/render-system-residency.d.ts.map +1 -1
  141. package/dist/render-system.d.ts +3 -7
  142. package/dist/render-system.d.ts.map +1 -1
  143. package/dist/scene/render-scene.d.ts +8 -4
  144. package/dist/scene/render-scene.d.ts.map +1 -1
  145. package/dist/scene/render-source.d.ts +42 -0
  146. package/dist/scene/render-source.d.ts.map +1 -0
  147. package/dist/scene/source-systems.d.ts +6 -0
  148. package/dist/scene/source-systems.d.ts.map +1 -0
  149. package/dist/systems/skin-palette-allocator.d.ts.map +1 -1
  150. package/dist/systems/skin-palette-types.d.ts +10 -0
  151. package/dist/systems/skin-palette-types.d.ts.map +1 -1
  152. package/dist/systems/transparent-dispatch.d.ts +2 -2
  153. package/dist/systems/transparent-dispatch.d.ts.map +1 -1
  154. package/dist/systems/transparent-sort-config.d.ts +2 -1
  155. package/dist/systems/transparent-sort-config.d.ts.map +1 -1
  156. package/dist/temporal/index.mjs +6 -7
  157. package/dist/typed-shadow-passes.d.ts.map +1 -1
  158. package/package.json +21 -21
  159. package/src/__tests__/auto-exposure-transaction.integration.test.ts +4 -9
  160. package/src/__tests__/device-feature-admission.unit.test.ts +3 -2
  161. package/src/__tests__/device-scope-lifecycle.unit.test.ts +26 -0
  162. package/src/__tests__/gpu-driven-pbr.dawn.test.ts +3 -4
  163. package/src/__tests__/gpu-driven-production.integration.test.ts +18 -1
  164. package/src/__tests__/gpu-driven-shadow-views.unit.test.ts +23 -0
  165. package/src/__tests__/ktx2-basis-gpu-consumer.dawn.test.ts +4 -1
  166. package/src/__tests__/main-pass-material-medium-selection.unit.test.ts +0 -1
  167. package/src/__tests__/material-vertex-texture.browser.test.ts +7 -0
  168. package/src/__tests__/material-vertex-texture.dawn.test.ts +7 -0
  169. package/src/__tests__/material-vertex-texture.fixture.ts +31 -0
  170. package/src/__tests__/materials-standard-contract.unit.test.ts +0 -1
  171. package/src/__tests__/physical-clearcoat.integration.test.ts +23 -23
  172. package/src/__tests__/physical-material-budget.unit.test.ts +40 -0
  173. package/src/__tests__/recovery-candidate-prepare.contract.test.ts +5 -0
  174. package/src/__tests__/render-error-code-owner.test-d.ts +1 -0
  175. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +107 -5
  176. package/src/__tests__/runtime-material-program.integration.test.ts +79 -0
  177. package/src/__tests__/vertex-layouts-ssot.unit.test.ts +23 -3
  178. package/src/assembly/device-feature-admission.ts +2 -2
  179. package/src/assembly/host-contract.ts +5 -2
  180. package/src/assembly/recovery/renderer-recover.ts +9 -0
  181. package/src/assembly/render-target-host.ts +57 -47
  182. package/src/assembly/renderer-facade.ts +3 -1
  183. package/src/assembly/webgpu-ready.ts +1 -1
  184. package/src/assembly/webgpu-renderer.ts +101 -70
  185. package/src/assembly/webgpu-vertex-layouts.ts +2 -9
  186. package/src/cloud/frame-context.ts +2 -2
  187. package/src/device/device-scope.ts +3 -0
  188. package/src/device/gpu-residency.ts +26 -25
  189. package/src/errors/render.ts +3 -1
  190. package/src/extract/material-context.ts +21 -0
  191. package/src/features/host.ts +13 -5
  192. package/src/features/particle-mesh-layout.ts +25 -0
  193. package/src/features/plan.ts +2 -0
  194. package/src/features/types.ts +8 -0
  195. package/src/glyph-text-layout-system.ts +2 -2
  196. package/src/gpu-driven/production-raster.ts +9 -3
  197. package/src/gpu-driven/shadow-ownership.ts +3 -3
  198. package/src/gpu-driven/shadow-views.ts +27 -2
  199. package/src/gpu-driven/view-gpu.ts +5 -0
  200. package/src/ibl/frame-resources.ts +0 -1
  201. package/src/ibl/skylight-bind-group.ts +13 -25
  202. package/src/index.ts +9 -0
  203. package/src/instances.ts +9 -6
  204. package/src/pbr-pipeline.ts +15 -18
  205. package/src/pipeline/standard-output/lut-gpu.ts +3 -3
  206. package/src/pipeline/standard-output/resources.ts +2 -2
  207. package/src/publication/__tests__/derived-geometry.integration.test.ts +177 -0
  208. package/src/publication/__tests__/features.integration.test.ts +173 -0
  209. package/src/publication/__tests__/programs.unit.test.ts +25 -0
  210. package/src/publication/__tests__/publication.integration.test.ts +415 -0
  211. package/src/publication/__tests__/targets.integration.test.ts +120 -0
  212. package/src/publication/__tests__/video.browser.test.ts +50 -0
  213. package/src/publication/contract.ts +104 -0
  214. package/src/publication/dependencies.ts +54 -0
  215. package/src/publication/prepare-geometry.ts +94 -0
  216. package/src/publication/programs.ts +45 -0
  217. package/src/publication/publisher.ts +645 -0
  218. package/src/publication/receiver.ts +297 -0
  219. package/src/publication/resource-scope.ts +31 -0
  220. package/src/publication/targets.ts +182 -0
  221. package/src/publication/video.ts +33 -0
  222. package/src/record/__tests__/material-bg-cache.unit.test.ts +15 -1
  223. package/src/record/__tests__/prepared-runtime-material.integration.test.ts +134 -0
  224. package/src/record/__tests__/sprite-material-abi.unit.test.ts +8 -10
  225. package/src/record/__tests__/video-texture-view.unit.test.ts +9 -9
  226. package/src/record/fold-buckets.ts +8 -5
  227. package/src/record/frame-snapshot.ts +6 -6
  228. package/src/record/frame.ts +87 -28
  229. package/src/record/helpers.ts +2 -2
  230. package/src/record/main-pass-geometry.ts +21 -2
  231. package/src/record/main-pass-material.ts +37 -33
  232. package/src/record/main-pass-sprite-draws.ts +6 -152
  233. package/src/record/main-pass.ts +8 -7
  234. package/src/record/prepared-material-bindings.ts +16 -37
  235. package/src/record/render-context.ts +4 -2
  236. package/src/record/shadow-pass.ts +65 -7
  237. package/src/record/sprite-instance-buffer.ts +155 -0
  238. package/src/record/target-capture-graph.ts +258 -0
  239. package/src/record/typed-frame-graph.ts +11 -207
  240. package/src/record/view-ubo.ts +1 -1
  241. package/src/record/volume-params.ts +6 -6
  242. package/src/recovery/render-system-candidate.ts +10 -18
  243. package/src/render-contract.ts +6 -1
  244. package/src/render-system-extract-tail.ts +48 -23
  245. package/src/render-system-extract.ts +11 -2
  246. package/src/render-system-presentation.ts +2 -2
  247. package/src/render-system-projections.ts +3 -2
  248. package/src/render-system-residency.ts +2 -2
  249. package/src/render-system.ts +82 -54
  250. package/src/scene/render-scene.ts +110 -100
  251. package/src/scene/render-source.ts +90 -0
  252. package/src/scene/source-systems.ts +33 -0
  253. package/src/systems/skin-palette-allocator.ts +1 -0
  254. package/src/systems/skin-palette-types.ts +11 -0
  255. package/src/systems/transparent-dispatch.ts +2 -2
  256. package/src/systems/transparent-sort-config.ts +3 -1
  257. package/src/typed-shadow-passes.ts +36 -5
  258. package/dist/chunk-BLXGONR2.mjs.map +0 -1
  259. package/dist/chunk-ELJFJ2BU.mjs.map +0 -1
  260. package/dist/chunk-ILQ5MEZF.mjs +0 -32
  261. package/dist/chunk-ILQ5MEZF.mjs.map +0 -1
  262. package/dist/chunk-MKDIT2EM.mjs.map +0 -1
  263. package/dist/chunk-OCDLEI6I.mjs.map +0 -1
  264. package/dist/chunk-ODZWR6JQ.mjs.map +0 -1
  265. package/dist/chunk-RXZ3RGOU.mjs +0 -53
  266. package/dist/chunk-RXZ3RGOU.mjs.map +0 -1
  267. package/dist/chunk-SHFUFJ5N.mjs +0 -1781
  268. package/dist/chunk-SHFUFJ5N.mjs.map +0 -1
  269. package/dist/chunk-UI5SFODX.mjs.map +0 -1
  270. package/dist/chunk-UINHLCZA.mjs +0 -2389
  271. package/dist/chunk-UINHLCZA.mjs.map +0 -1
  272. package/dist/chunk-WEX45HKH.mjs +0 -1524
  273. package/dist/chunk-WEX45HKH.mjs.map +0 -1
@@ -1,8 +1,9 @@
1
+ import type { RenderResourceScope } from '../publication/resource-scope';
2
+ import { resolveSpriteInstancesBuffer } from './sprite-instance-buffer';
1
3
  // @forgeax/engine-runtime - RenderSystem record stage: main-pass geometry draws.
2
4
  // feat-20260704 M5/w31: further-split from main-pass.ts (AC-05 <=1500 lines/file).
3
5
  // recordGeometryDraws + resolveGeometryInstanceBuffer, moved verbatim.
4
6
 
5
- import type { World } from '@forgeax/engine-ecs';
6
7
  import {
7
8
  type BindGroup,
8
9
  type Buffer,
@@ -495,7 +496,7 @@ export function recordGeometryDraws(
495
496
  materialSlot: number,
496
497
  submeshMaterial: MaterialSnapshot,
497
498
  entityKey: number,
498
- materialWorld: World,
499
+ materialWorld: RenderResourceScope,
499
500
  materialShaderId?: string,
500
501
  ) => BindGroup,
501
502
  passKind: PassKind = 'forward',
@@ -1916,6 +1917,24 @@ export function resolveGeometryInstanceBuffer(
1916
1917
  ): readonly GeometryInstanceDraw[] | null {
1917
1918
  const { runtime, pipelineState, frameState } = c;
1918
1919
  const inst = entry.source.instances;
1920
+ if (entry.source.spriteInstances !== undefined) {
1921
+ const { buffer, count } = resolveSpriteInstancesBuffer(
1922
+ c,
1923
+ entry,
1924
+ pipelineState.identityInstanceBuffer,
1925
+ 0,
1926
+ );
1927
+ return count === 0
1928
+ ? []
1929
+ : [
1930
+ {
1931
+ instanceBuffer: buffer,
1932
+ instanceBindGroup: resolveGeometryInstancesBindGroup(c, buffer, undefined),
1933
+ probeOffset: -1,
1934
+ instanceCount: count,
1935
+ },
1936
+ ];
1937
+ }
1919
1938
  const probeBinding =
1920
1939
  !useProbeBlend ||
1921
1940
  !runtime.device.caps.storageBuffer ||
@@ -1,14 +1,16 @@
1
1
  import { transmissionBackdropAvailable } from '../assembly/device-feature-admission';
2
2
  import { getOrCreateIblCache } from '../ibl/IblPipelineCache';
3
+ import type { RenderResourceScope } from '../publication/resource-scope';
3
4
  import type { _InternalRenderPipelineContext } from './render-context';
4
5
  // @forgeax/engine-runtime - RenderSystem record stage: main-pass-material.
5
6
  // Extracted from render-system-record.ts (feat-20260704 M3/w17, pure move).
6
7
 
7
8
  import { resolveAssetHandle } from '@forgeax/engine-assets-runtime';
8
- import type { EntityHandle, World } from '@forgeax/engine-ecs';
9
+ import type { EntityHandle } from '@forgeax/engine-ecs';
9
10
  import {
10
- VIDEO_ELEMENT_PROVIDER_KEY,
11
- type VideoElementProvider,
11
+ VIDEO_SOURCE_PROVIDER_KEY,
12
+ type VideoSourceProvider,
13
+ videoSourceExtent,
12
14
  } from '@forgeax/engine-graphics-extras';
13
15
  import {
14
16
  type BindGroup,
@@ -261,11 +263,11 @@ function materialDiagnosticsEnabled(): boolean {
261
263
  }
262
264
 
263
265
  export function residentTextureView(
264
- world: World,
266
+ world: RenderResourceScope,
265
267
  store: GpuResidencyCache,
266
268
  runtime: RenderSystemRuntime,
267
269
  handle: Handle<'TextureAsset', 'shared'>,
268
- worldId: World | number = world,
270
+ worldId: RenderResourceScope | number = world,
269
271
  ): TextureView | undefined {
270
272
  const podRes = resolveAssetHandle<TextureAsset>(world, handle);
271
273
  const diagnosticsEnabled = materialDiagnosticsEnabled();
@@ -323,11 +325,11 @@ export function residentTextureView(
323
325
  }
324
326
 
325
327
  function residentSampler(
326
- world: World,
328
+ world: RenderResourceScope,
327
329
  store: GpuResidencyCache,
328
330
  runtime: RenderSystemRuntime,
329
331
  handle: Handle<'SamplerAsset', 'shared'>,
330
- worldId: World | number = world,
332
+ worldId: RenderResourceScope | number = world,
331
333
  ): Sampler | undefined {
332
334
  const podRes = resolveAssetHandle<SamplerAsset>(world, handle);
333
335
  if (!podRes.ok) return undefined;
@@ -344,7 +346,7 @@ function residentSampler(
344
346
  // transient DynamicTextureStore, NOT the static `residentTextureView` /
345
347
  // `ensureResident` cache (video never enters that switch; AC-08).
346
348
  //
347
- // Per frame: ask the host-registered VideoElementProvider (World Resource,
349
+ // Per frame: ask the host-registered VideoSourceProvider (RenderResourceScope Resource,
348
350
  // D-1) for this entity's HTMLVideoElement, upload its current frame via
349
351
  // `store.uploadFrame` (copyExternalImageToTexture), and return the resulting
350
352
  // view. When the provider is absent / returns no element / the element has no
@@ -404,7 +406,7 @@ function clearVideoUploadFailureEpisode(
404
406
  }
405
407
 
406
408
  export function videoTextureView(
407
- world: World,
409
+ world: RenderResourceScope,
408
410
  store: DynamicTextureStore | undefined,
409
411
  runtime: RenderSystemRuntime,
410
412
  entityKey: number,
@@ -412,10 +414,14 @@ export function videoTextureView(
412
414
  highPerfAvailable: boolean,
413
415
  ): TextureView | undefined {
414
416
  if (store === undefined) return undefined;
415
- const provider = world.hasResource(VIDEO_ELEMENT_PROVIDER_KEY)
416
- ? world.getResource<VideoElementProvider>(VIDEO_ELEMENT_PROVIDER_KEY)
417
- : undefined;
418
- const element = provider?.getElement(entityKey as EntityHandle, clip);
417
+ const provider =
418
+ !('resolveAsset' in world) && world.hasResource(VIDEO_SOURCE_PROVIDER_KEY)
419
+ ? world.getResource<VideoSourceProvider>(VIDEO_SOURCE_PROVIDER_KEY)
420
+ : undefined;
421
+ const element =
422
+ 'resolveAsset' in world
423
+ ? world.videoFrame(entityKey, clip)
424
+ : provider?.getSource(entityKey as EntityHandle, clip);
419
425
  // AC-10 double-miss: a VideoPlayer entity can reach NEITHER upload path this
420
426
  // frame — no host HTMLVideoElement (general copyExternalImageToTexture path)
421
427
  // AND no high-perf GPUExternalTexture path. This is the genuine "this backend
@@ -441,13 +447,9 @@ export function videoTextureView(
441
447
  clearVideoUploadFailureEpisode(store, entityKey, clip);
442
448
  return store.getView(clip);
443
449
  }
444
- const width = element.videoWidth;
445
- const height = element.videoHeight;
446
- // Metadata dimensions can be non-zero before the decoder has produced a
447
- // current frame. WebGPU rejects copyExternalImageToTexture in that window
448
- // because the video element has no back resource yet; keep the previous
449
- // frame/default view until HAVE_CURRENT_DATA (2) is reached.
450
- if (width <= 0 || height <= 0 || element.readyState < 2) return store.getView(clip);
450
+ const extent = videoSourceExtent(element);
451
+ if (extent === undefined) return store.getView(clip);
452
+ const { width, height } = extent;
451
453
  const uploaded = store.uploadFrame(clip, element, width, height);
452
454
  if (uploaded === undefined) return store.getView(clip);
453
455
  if (!uploaded.ok) {
@@ -592,7 +594,7 @@ function writeNumericParamValue(
592
594
  export function applyMaterialTextureUvScales(
593
595
  payload: MaterialUboPayload,
594
596
  material: MaterialSnapshot,
595
- world: World,
597
+ world: RenderResourceScope,
596
598
  ): void {
597
599
  const f32 = materialUboFloatView(payload);
598
600
  const coordinateSchema = materialCoordinateSchema(material);
@@ -940,14 +942,14 @@ export function applyParamSchemaDefaultsToUbo(
940
942
  export interface PerSubmeshMaterialBgDeps {
941
943
  readonly runtime: RenderSystemRuntime;
942
944
  readonly pipelineState: PipelineState;
943
- readonly world: World;
945
+ readonly world: RenderResourceScope;
944
946
  readonly store: GpuResidencyCache;
945
947
  readonly materialSlice: number;
946
948
  readonly videoHighPerfAvailable: boolean;
947
949
  readonly skylightResources: SkylightBindGroupResources;
948
950
  readonly resolveRenderTargetTextureSource?: RenderSystemRuntime['resolveRenderTargetTextureSource'];
949
951
  readonly resolveReflectionProbeResources?: (
950
- materialWorld: World,
952
+ materialWorld: RenderResourceScope,
951
953
  entityKey: number,
952
954
  ) => { readonly resources: SkylightBindGroupResources; readonly probeIndex: number } | undefined;
953
955
  readonly materialBgShared: Map<string, WeakMap<object, unknown>>;
@@ -979,7 +981,7 @@ function recordIblMaterialBinding(
979
981
  if (receipt === undefined) return;
980
982
  const skylightBindingStart = entries.findIndex((entry) => entry.binding === iblStart);
981
983
  if (skylightBindingStart < 0) return;
982
- const iblEntries = entries.slice(skylightBindingStart, skylightBindingStart + 7);
984
+ const iblEntries = entries.slice(skylightBindingStart, skylightBindingStart + 6);
983
985
  const projected: IblBindingEntryInspection[] = [];
984
986
  for (const entry of iblEntries) {
985
987
  if (entry.resource.kind === 'externalTexture') continue;
@@ -1018,7 +1020,15 @@ export function isMaterialBgAssemblyCacheHit(
1018
1020
  sceneMaterialBuffer?: Buffer,
1019
1021
  surfaceRawDepthView?: TextureView | null,
1020
1022
  ): cached is MaterialBgAssemblyCacheEntry {
1021
- if (transmissionBackdropView != null || surfaceRawDepthView != null) return false;
1023
+ // Logical target sources keep their identity when a resize promotes a new
1024
+ // physical view. Resolve those views each frame; the lower binding cache is
1025
+ // keyed by the actual GPU resources and still reuses unchanged bindings.
1026
+ if (
1027
+ transmissionBackdropView != null ||
1028
+ surfaceRawDepthView != null ||
1029
+ (material.textureSources?.size ?? 0) > 0
1030
+ )
1031
+ return false;
1022
1032
  return (
1023
1033
  cached?.material === material &&
1024
1034
  cached.materialResourceEpoch === materialResourceEpoch &&
@@ -1031,7 +1041,6 @@ export function isMaterialBgAssemblyCacheHit(
1031
1041
  cached.skylightResources.skylightPrefilterView === skylightResources.skylightPrefilterView &&
1032
1042
  cached.skylightResources.prefilterSampler === skylightResources.prefilterSampler &&
1033
1043
  cached.skylightResources.brdfLutView === skylightResources.brdfLutView &&
1034
- cached.skylightResources.brdfLutSampler === skylightResources.brdfLutSampler &&
1035
1044
  cached.skylightResources.intensityBuffer === skylightResources.intensityBuffer
1036
1045
  );
1037
1046
  }
@@ -1040,7 +1049,7 @@ export function buildPerSubmeshMaterialBg(
1040
1049
  deps: PerSubmeshMaterialBgDeps,
1041
1050
  submeshMaterial: MaterialSnapshot,
1042
1051
  entityKey: number,
1043
- materialWorld: World = deps.world,
1052
+ materialWorld: RenderResourceScope = deps.world,
1044
1053
  materialShaderId: string | undefined = submeshMaterial.materialShaderId,
1045
1054
  ): BindGroup {
1046
1055
  const {
@@ -1175,10 +1184,6 @@ export function buildPerSubmeshMaterialBg(
1175
1184
  },
1176
1185
  {
1177
1186
  binding: 6 + smUserRegionFields.length * 2,
1178
- resource: { kind: 'sampler', value: effectiveSkylightResources.brdfLutSampler },
1179
- },
1180
- {
1181
- binding: 7 + smUserRegionFields.length * 2,
1182
1187
  resource: {
1183
1188
  kind: 'buffer',
1184
1189
  value: { buffer: effectiveSkylightResources.intensityBuffer },
@@ -1429,6 +1434,7 @@ export function buildPerSubmeshMaterialBg(
1429
1434
  if (
1430
1435
  smMaterialHandle !== undefined &&
1431
1436
  !smHasVideoFields &&
1437
+ (submeshMaterial.textureSources?.size ?? 0) === 0 &&
1432
1438
  materialResourcesResident &&
1433
1439
  !hasActiveTransmissionBackdrop
1434
1440
  ) {
@@ -1532,7 +1538,6 @@ export function resolveMaterialSkylight(
1532
1538
  prefilterView: activeViews.pref,
1533
1539
  prefilterSampler: skylightFallback.sampler,
1534
1540
  brdfLutView: activeViews.brdf,
1535
- brdfLutSampler: skylightFallback.sampler,
1536
1541
  intensityBuffer: skylightFallback.intensityBuffer,
1537
1542
  }
1538
1543
  : {
@@ -1541,7 +1546,6 @@ export function resolveMaterialSkylight(
1541
1546
  prefilterView: skylightFallback.prefilterView,
1542
1547
  prefilterSampler: skylightFallback.sampler,
1543
1548
  brdfLutView: skylightFallback.brdfLutView,
1544
- brdfLutSampler: skylightFallback.sampler,
1545
1549
  intensityBuffer: skylightFallback.intensityBuffer,
1546
1550
  };
1547
1551
  return { skylightResources, activeViews };
@@ -1,8 +1,9 @@
1
+ import type { RenderResourceScope } from '../publication/resource-scope';
2
+ import { resolveSpriteInstancesBuffer } from './sprite-instance-buffer';
1
3
  // @forgeax/engine-runtime - RenderSystem record stage: main-pass sprite draws.
2
4
  // feat-20260704 M5/w31: further-split from main-pass.ts (AC-05 <=1500 lines/file).
3
5
  // recordSpritePass + sprite entity/transparent/instance-buffer helpers, moved verbatim.
4
6
 
5
- import type { World } from '@forgeax/engine-ecs';
6
7
  import {
7
8
  type BindGroup,
8
9
  type Buffer,
@@ -21,11 +22,7 @@ import type { InstanceBufferCacheEntry } from '../instance-buffer-cache';
21
22
  import { SPRITE_PREMULTIPLIED_ALPHA_BLEND } from '../materials';
22
23
  import { SPRITE_PASS_PER_INSTANCE_REGION_VARIANT_SET } from '../pbr-pipeline';
23
24
  import { buildBeginRenderPassDescriptor, standardTopologyVariantSet } from '../pipeline-spec';
24
- import type {
25
- DispatchEntry,
26
- MaterialSnapshot,
27
- SpriteInstancesSnapshot,
28
- } from '../render-system-extract';
25
+ import type { DispatchEntry, MaterialSnapshot } from '../render-system-extract';
29
26
  import { resolveFoldInstanceBuffer } from './fold-instance-buffer';
30
27
  import { worldEntityKey } from './frame-snapshot';
31
28
  import { recordGeometryDraws, resolveGeometryInstancesBindGroup } from './main-pass-geometry';
@@ -37,55 +34,6 @@ import {
37
34
  import type { _InternalRenderPipelineContext } from './render-context';
38
35
  import { MATERIAL_PER_ENTITY_STRIDE } from './render-context';
39
36
 
40
- export type { SpriteInstancesSnapshot };
41
-
42
- /**
43
- * Build the interleaved sprite instance payload consumed by this record owner.
44
- * The extract stage validates the two packed arrays before they reach this
45
- * function, so the record path has one source of count-mismatch errors.
46
- */
47
- export function interleaveSpriteInstanceBuffer(
48
- transforms: Float32Array,
49
- regions: Float32Array,
50
- includePrevious = false,
51
- ): Float32Array {
52
- const count = transforms.length / 16;
53
- const stride = includePrevious ? 36 : 20;
54
- const regionOffset = includePrevious ? 32 : 16;
55
- const out = new Float32Array(count * stride);
56
- for (let i = 0; i < count; i++) {
57
- const dstBase = i * stride;
58
- const transformBase = i * 16;
59
- const regionBase = i * 4;
60
- for (let k = 0; k < 16; k++) out[dstBase + k] = transforms[transformBase + k] ?? 0;
61
- if (includePrevious) {
62
- for (let k = 0; k < 16; k++) {
63
- out[dstBase + 16 + k] = transforms[transformBase + k] ?? 0;
64
- }
65
- }
66
- for (let k = 0; k < 4; k++) {
67
- out[dstBase + regionOffset + k] = regions[regionBase + k] ?? 0;
68
- }
69
- }
70
- return out;
71
- }
72
-
73
- /**
74
- * Check whether the existing per-entity sprite buffer still matches the
75
- * extract snapshot and interleaved byte count.
76
- */
77
- export function spriteInstancesCacheHit(
78
- entry: InstanceBufferCacheEntry | undefined,
79
- snapshot: SpriteInstancesSnapshot,
80
- requestedBytes: number,
81
- ): boolean {
82
- return (
83
- entry !== undefined &&
84
- entry.uploadedArchVersion === snapshot.archVersion &&
85
- entry.uploadedByteLength === requestedBytes
86
- );
87
- }
88
-
89
37
  /**
90
38
  * Decide whether the LDR record needs the transparent sprite sub-pass. This
91
39
  * remains beside the sprite record owner so the frame stage and draw stage
@@ -144,7 +92,7 @@ export function recordSpritePass(
144
92
  materialSlot: number,
145
93
  submeshMaterial: MaterialSnapshot,
146
94
  entityKey: number,
147
- materialWorld: World,
95
+ materialWorld: RenderResourceScope,
148
96
  materialShaderId?: string,
149
97
  ) => BindGroup,
150
98
  graphPass?: RhiRenderPassEncoder,
@@ -382,7 +330,7 @@ function recordSpriteTransparentPbrDraws(
382
330
  slot: number,
383
331
  material: MaterialSnapshot,
384
332
  entityKey: number,
385
- world: World,
333
+ world: RenderResourceScope,
386
334
  shader?: string,
387
335
  ) => BindGroup,
388
336
  meshGroup2: BindGroup | null,
@@ -455,7 +403,7 @@ function recordSpriteEntityDraws(
455
403
  materialSlot: number,
456
404
  material: MaterialSnapshot,
457
405
  entityKey: number,
458
- materialWorld: World,
406
+ materialWorld: RenderResourceScope,
459
407
  ) => BindGroup,
460
408
  spritePH: RenderPipeline | null,
461
409
  spritePH_withRegion: RenderPipeline | null,
@@ -760,100 +708,6 @@ function recordSpriteEntityDraws(
760
708
  }
761
709
  }
762
710
 
763
- /**
764
- * feat-20260704 M3/w19: resolve the interleaved SpriteInstances (@group(3))
765
- * buffer + instanceCount for a sprite entity in the LDR blend sub-pass,
766
- * extracted verbatim from recordSpriteEntityDraws. Uploads the interleaved
767
- * mat4 + per-instance UV region transforms (cache-keyed on the snapshot); on
768
- * over-cap fires the structured limit-exceeded error and leaves the passed-in
769
- * fallback buffer/count. Returns the resolved (or unchanged) buffer + count.
770
- *
771
- * @internal
772
- */
773
- function resolveSpriteInstancesBuffer(
774
- c: _InternalRenderPipelineContext,
775
- spriteEntry: _InternalRenderPipelineContext['validatedOrdered'][number],
776
- fallbackBuffer: Buffer,
777
- fallbackCount: number,
778
- ): { buffer: Buffer; count: number } {
779
- const { runtime, frameState } = c;
780
- let buffer = fallbackBuffer;
781
- let count = fallbackCount;
782
- const spriteInstancesSnap: SpriteInstancesSnapshot | undefined =
783
- spriteEntry.source.spriteInstances;
784
- if (spriteInstancesSnap !== undefined) {
785
- const uniformFallback = runtime.device.caps.storageBuffer === false;
786
- const interleaved = interleaveSpriteInstanceBuffer(
787
- spriteInstancesSnap.transforms,
788
- spriteInstancesSnap.regions,
789
- !uniformFallback,
790
- );
791
- const requestedBytes = interleaved.byteLength;
792
- const cap = runtime.device.limits.maxStorageBufferBindingSize;
793
- if (typeof cap === 'number' && requestedBytes > cap) {
794
- runtime.errorRegistry.fire(
795
- new RhiError({
796
- code: 'limit-exceeded',
797
- expected: `requestedBytes (${requestedBytes}) <= maxStorageBufferBindingSize (${cap})`,
798
- hint: 'reduce SpriteInstances instance count to fit within device.limits.maxStorageBufferBindingSize (144 bytes per instance: current mat4 64B + previous mat4 64B + region 16B)',
799
- detail: {
800
- maxStorageBufferBindingSize: cap,
801
- requestedBytes,
802
- },
803
- }),
804
- );
805
- } else {
806
- const cachedSpriteInst = frameState.instanceBuffers.get(
807
- worldEntityKey(spriteEntry.source.worldId, spriteInstancesSnap.cacheKey),
808
- );
809
- let activeSpriteInst: InstanceBufferCacheEntry | null = null;
810
- if (spriteInstancesCacheHit(cachedSpriteInst, spriteInstancesSnap, requestedBytes)) {
811
- activeSpriteInst = cachedSpriteInst ?? null;
812
- } else if (requestedBytes > 0) {
813
- const bufRes = runtime.device.createBuffer({
814
- size: requestedBytes,
815
- usage:
816
- (uniformFallback ? GPU_BUFFER_USAGE_UNIFORM : GPU_BUFFER_USAGE_STORAGE) |
817
- GPU_BUFFER_USAGE_COPY_DST,
818
- mappedAtCreation: false,
819
- });
820
- if (!bufRes.ok) {
821
- runtime.errorRegistry.fire(bufRes.error);
822
- } else {
823
- if (cachedSpriteInst !== undefined && !cachedSpriteInst.buffer.isDestroyed) {
824
- const r = cachedSpriteInst.buffer.destroy();
825
- if (!r.ok) runtime.errorRegistry.fire(r.error);
826
- }
827
- const newBuf = new GpuBuffer(runtime.device, bufRes.value);
828
- activeSpriteInst = {
829
- buffer: newBuf,
830
- uploadedArchVersion: spriteInstancesSnap.archVersion,
831
- uploadedByteLength: requestedBytes,
832
- };
833
- frameState.instanceBuffers.set(
834
- worldEntityKey(spriteEntry.source.worldId, spriteInstancesSnap.cacheKey),
835
- activeSpriteInst,
836
- );
837
- }
838
- }
839
- if (activeSpriteInst !== null && requestedBytes > 0) {
840
- const writeRes = runtime.device.queue.writeBuffer(
841
- activeSpriteInst.buffer.handle,
842
- 0,
843
- interleaved,
844
- );
845
- if (!writeRes.ok) {
846
- runtime.errorRegistry.fire(writeRes.error);
847
- } else {
848
- buffer = activeSpriteInst.buffer.handle;
849
- count = spriteInstancesSnap.instanceCount;
850
- }
851
- }
852
- }
853
- }
854
- return { buffer, count };
855
- }
856
-
857
711
  function transparentPassColorFormat(
858
712
  c: _InternalRenderPipelineContext,
859
713
  pipelineState: _InternalRenderPipelineContext['pipelineState'] = c.pipelineState,
@@ -1,4 +1,3 @@
1
- import type { World } from '@forgeax/engine-ecs';
2
1
  import { probeVideoHighPerfUpload } from '@forgeax/engine-graphics-extras';
3
2
  import type { GraphResourceResolver } from '@forgeax/engine-render-graph';
4
3
  import {
@@ -19,6 +18,7 @@ import { getOrCreateIblCache } from '../ibl/IblPipelineCache';
19
18
  import type { SkylightBindGroupResources } from '../ibl/skylight-bind-group';
20
19
  import { buildBeginRenderPassDescriptor, standardTopologyBindGroupReady } from '../pipeline-spec';
21
20
  import { POINTS_LINES_MATERIAL_SHADER_ID } from '../points-lines/record';
21
+ import type { RenderResourceScope } from '../publication/resource-scope';
22
22
  import type { ReflectionProbeSelectionResult } from '../reflection/projection';
23
23
  import type { RenderRecordPhase } from '../render-contract';
24
24
  import type {
@@ -529,7 +529,7 @@ export function recordMainPass(
529
529
  identities: [
530
530
  getOpaqueResourceIdentity(skylightResources.irradianceSampler as object),
531
531
  getOpaqueResourceIdentity(skylightResources.prefilterSampler as object),
532
- getOpaqueResourceIdentity(skylightResources.brdfLutSampler as object),
532
+ getOpaqueResourceIdentity(skylightResources.irradianceSampler as object),
533
533
  ],
534
534
  },
535
535
  resources: {
@@ -549,7 +549,9 @@ export function recordMainPass(
549
549
  },
550
550
  brdfLut: {
551
551
  viewIdentity: getOpaqueResourceIdentity(skylightResources.brdfLutView as object),
552
- samplerIdentity: getOpaqueResourceIdentity(skylightResources.brdfLutSampler as object),
552
+ samplerIdentity: getOpaqueResourceIdentity(
553
+ skylightResources.irradianceSampler as object,
554
+ ),
553
555
  deviceGeneration: runtime.deviceScope.generation,
554
556
  },
555
557
  intensityBufferIdentity: getOpaqueResourceIdentity(
@@ -590,7 +592,7 @@ export function recordMainPass(
590
592
  // which cannot render a PBR decal). Captures only frame-stable closure
591
593
  // state; the caller passes the per-submesh material snapshot + entityKey
592
594
  // (for video texture routing) and sets the dynamic UBO offset itself.
593
- // Selection keys use the extracted frame index, never World.identity.
595
+ // Selection keys use the extracted frame index, never RenderResourceScope.identity.
594
596
  const hasProbeBindings = (c.reflectionProbes?.table.rows.length ?? 0) > 0;
595
597
  const worldIndices = hasProbeBindings
596
598
  ? new Map(
@@ -632,7 +634,6 @@ export function recordMainPass(
632
634
  skylightPrefilterView: skylightResources.prefilterView,
633
635
  prefilterSampler: row.sampler,
634
636
  brdfLutView: skylightResources.brdfLutView,
635
- brdfLutSampler: skylightResources.brdfLutSampler,
636
637
  intensityBuffer: row.uniformBuffer,
637
638
  };
638
639
  return { resources: probeResources, probeIndex: row.index };
@@ -657,7 +658,7 @@ export function recordMainPass(
657
658
  const buildPerSubmeshMaterialBg = (
658
659
  submeshMaterial: MaterialSnapshot,
659
660
  entityKey: number,
660
- materialWorld: World = world,
661
+ materialWorld: RenderResourceScope = world,
661
662
  materialShaderId: string | undefined = submeshMaterial.materialShaderId,
662
663
  ): BindGroup =>
663
664
  buildPerSubmeshMaterialBgImpl(
@@ -748,7 +749,7 @@ export function recordMainPass(
748
749
  materialSlot: number,
749
750
  material: MaterialSnapshot,
750
751
  entityKey: number,
751
- materialWorld: World = world,
752
+ materialWorld: RenderResourceScope = world,
752
753
  materialShaderId: string | undefined = material.materialShaderId,
753
754
  ): BindGroup =>
754
755
  !hasProbeBindings &&
@@ -1,5 +1,4 @@
1
1
  import { resolveAssetHandle } from '@forgeax/engine-assets-runtime';
2
- import type { World } from '@forgeax/engine-ecs';
3
2
  import type { BindGroupEntry, BindGroupLayout, Buffer } from '@forgeax/engine-rhi';
4
3
  import { err, ok } from '@forgeax/engine-rhi';
5
4
  import {
@@ -14,6 +13,8 @@ import { transmissionBackdropAvailable } from '../assembly/device-feature-admiss
14
13
  import { GPU_BUFFER_USAGE_COPY_DST, GPU_BUFFER_USAGE_UNIFORM } from '../gpu-usage';
15
14
  import type { SkylightBindGroupResources } from '../ibl/skylight-bind-group';
16
15
  import { assembleMaterialWithSkylightEntries } from '../ibl/skylight-bind-group';
16
+ import type { RenderResourceScope } from '../publication/resource-scope';
17
+ import { internSharedRefFromGuid } from '../render-system-extract';
17
18
  import {
18
19
  applyParamSchemaDefaultsToUbo,
19
20
  applyParamSnapshotToUbo,
@@ -28,7 +29,6 @@ export type PreparedResolverCaches = {
28
29
  readonly preparedGroup0Pipelines: WeakSet<object>;
29
30
  readonly preparedViewOnlyPipelines: WeakSet<object>;
30
31
  readonly preparedRenderMaterialPipelines: WeakSet<object>;
31
- readonly preparedAssetHandles: WeakMap<World, Map<string, number>>;
32
32
  };
33
33
 
34
34
  export function createPreparedResolverCaches(): PreparedResolverCaches {
@@ -38,36 +38,23 @@ export function createPreparedResolverCaches(): PreparedResolverCaches {
38
38
  preparedGroup0Pipelines: new WeakSet(),
39
39
  preparedViewOnlyPipelines: new WeakSet(),
40
40
  preparedRenderMaterialPipelines: new WeakSet(),
41
- preparedAssetHandles: new WeakMap(),
42
41
  };
43
42
  }
44
43
 
45
44
  function preparedHandle<Brand extends string>(
46
45
  runtime: RenderSystemInternals,
47
- caches: PreparedResolverCaches,
48
- world: World,
46
+ world: RenderResourceScope,
49
47
  guid: string,
50
48
  brand: Brand,
51
49
  ): Handle<Brand, 'shared'> | undefined {
52
- let handles = caches.preparedAssetHandles.get(world);
53
- if (handles === undefined) {
54
- handles = new Map();
55
- caches.preparedAssetHandles.set(world, handles);
56
- }
57
- const key = `${brand}:${guid.toLowerCase()}`;
58
- const cached = handles.get(key);
59
- if (cached !== undefined) return cached as Handle<Brand, 'shared'>;
60
- const asset = runtime.assets.lookup(guid);
61
- if (asset === undefined) return undefined;
62
- const handle = world.allocSharedRef(brand, asset);
63
- handles.set(key, handle as number);
64
- return handle;
50
+ if ('resolveAsset' in world)
51
+ return world.handleForGuid(guid) as Handle<Brand, 'shared'> | undefined;
52
+ return internSharedRefFromGuid(world, runtime.assets, guid, brand);
65
53
  }
66
54
 
67
55
  export function preparedMaterialBindings(
68
56
  runtime: RenderSystemInternals,
69
- caches: PreparedResolverCaches,
70
- worlds: readonly World[],
57
+ worlds: readonly RenderResourceScope[],
71
58
  materialShaderId: string,
72
59
  worldIndex: number,
73
60
  materialGuid: string,
@@ -75,11 +62,16 @@ export function preparedMaterialBindings(
75
62
  skylightResources?: SkylightBindGroupResources,
76
63
  ) {
77
64
  const world = worlds[worldIndex];
78
- const material = runtime.assets.lookup(materialGuid) as MaterialAsset | undefined;
65
+ const handle =
66
+ world === undefined ? undefined : preparedHandle(runtime, world, materialGuid, 'MaterialAsset');
79
67
  const pipelineState = runtime.getPipelineState();
80
- if (world === undefined || material?.kind !== 'material' || pipelineState === null) {
68
+ if (world === undefined || handle === undefined || pipelineState === null) {
81
69
  return err(new Error('prepared material asset is unavailable'));
82
70
  }
71
+ const resolved = resolveAssetHandle<MaterialAsset>(world, handle);
72
+ if (!resolved.ok) return resolved;
73
+ const material = resolved.value;
74
+ if (material.kind !== 'material') return err(new Error('prepared material asset is unavailable'));
83
75
  const schema = runtime.getParamSchema?.(materialShaderId) ?? [];
84
76
  // Feature materials share the ordinary linear parameter domain and semantic texture defaults.
85
77
  const values = materialValuesToLinearRuntime(
@@ -126,13 +118,7 @@ export function preparedMaterialBindings(
126
118
  : undefined;
127
119
  let sampler = pipelineState.defaultSampler;
128
120
  if (textureValue?.sampler !== undefined) {
129
- const handle = preparedHandle(
130
- runtime,
131
- caches,
132
- world,
133
- String(textureValue.sampler),
134
- 'SamplerAsset',
135
- );
121
+ const handle = preparedHandle(runtime, world, String(textureValue.sampler), 'SamplerAsset');
136
122
  if (handle !== undefined) {
137
123
  const asset = resolveAssetHandle<SamplerAsset>(world, handle);
138
124
  if (asset.ok) {
@@ -143,13 +129,7 @@ export function preparedMaterialBindings(
143
129
  }
144
130
  let view = defaultViewForUserRegionField(field, pipelineState, schema);
145
131
  if (textureValue !== undefined) {
146
- const handle = preparedHandle(
147
- runtime,
148
- caches,
149
- world,
150
- String(textureValue.texture),
151
- 'TextureAsset',
152
- );
132
+ const handle = preparedHandle(runtime, world, String(textureValue.texture), 'TextureAsset');
153
133
  if (handle !== undefined) {
154
134
  view = residentTextureView(world, runtime.gpuStore, runtime, handle) ?? view;
155
135
  }
@@ -203,7 +183,6 @@ export function preparedMaterialBindings(
203
183
  prefilterView: fallback.prefilterView,
204
184
  prefilterSampler: fallback.sampler,
205
185
  brdfLutView: fallback.brdfLutView,
206
- brdfLutSampler: fallback.sampler,
207
186
  intensityBuffer: fallback.intensityBuffer,
208
187
  },
209
188
  transmissionBackdropAvailable(runtime.device.limits.maxSampledTexturesPerShaderStage)
@@ -1,5 +1,4 @@
1
1
  import type { AssetRegistry, DynamicTextureStore } from '@forgeax/engine-assets-runtime';
2
- import type { World } from '@forgeax/engine-ecs';
3
2
  import type { VertexLayoutProjection } from '@forgeax/engine-geometry';
4
3
  import type { Profiler } from '@forgeax/engine-profiler';
5
4
  import type { CompiledRenderGraphInfo } from '@forgeax/engine-render-graph';
@@ -58,6 +57,7 @@ import type { StandardProfile } from '../pipeline/standard-profile';
58
57
  import type { PipelineBuilderShaderModuleFactory } from '../pipeline-builder';
59
58
  import type { PointsLinesInspection } from '../points-lines/inspection';
60
59
  import type { PointsLinesRecordPlan } from '../points-lines/record';
60
+ import type { RenderResourceScope } from '../publication/resource-scope';
61
61
  import type { ReflectionProbeTable } from '../reflection/gpu-table';
62
62
  import type { ReflectionProbeSelectionResult } from '../reflection/projection';
63
63
  import type { FrameObservationDomain } from '../render-contract';
@@ -582,7 +582,7 @@ export interface _StandardForwardSceneView {
582
582
  readonly skybox: SkyboxSnapshot | undefined;
583
583
  readonly profilePhase?: <T>(phase: RenderRecordPhase, action: () => T) => T;
584
584
  readonly directionalShadowCacheReuse: boolean;
585
- readonly world: World;
585
+ readonly world: RenderResourceScope;
586
586
  readonly tonemapActive: boolean;
587
587
  readonly geometryColorView: TextureView | null;
588
588
  readonly geometryDepthView: TextureView | null;
@@ -620,6 +620,8 @@ export interface _StandardForwardSceneView {
620
620
  readonly gpuDrivenShadowDrawKeys?: ReadonlySet<string>;
621
621
  /** View-scoped ShadowCaster claims for exact CPU residual routing. */
622
622
  readonly gpuDrivenShadowDrawKeysByView?: ReadonlyMap<string, ReadonlySet<string>>;
623
+ /** Per-shadow-view frusta shared by GPU compaction and CPU residual routing. */
624
+ readonly shadowViewPlanesByView?: ReadonlyMap<string, Float32Array>;
623
625
  /** Extracted structured ShadowCaster ownership facts. */
624
626
  readonly shadowCasterMembership?: readonly ShadowCasterMembership[];
625
627
  /** Complete retained ShadowCaster rows, including camera-culled casters. */