@forgeax/engine-render 0.1.34 → 0.1.35

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 (243) hide show
  1. package/README.md +52 -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/authoring.mjs +2 -4
  12. package/dist/authoring.mjs.map +1 -1
  13. package/dist/chunk-3XUBWRY2.mjs +1842 -0
  14. package/dist/chunk-3XUBWRY2.mjs.map +1 -0
  15. package/dist/{chunk-UI5SFODX.mjs → chunk-G6HDNGY7.mjs} +2329 -1776
  16. package/dist/chunk-G6HDNGY7.mjs.map +1 -0
  17. package/dist/{chunk-HEB564WF.mjs → chunk-LM5IE7GF.mjs} +4 -4
  18. package/dist/{chunk-HEB564WF.mjs.map → chunk-LM5IE7GF.mjs.map} +1 -1
  19. package/dist/{chunk-MKDIT2EM.mjs → chunk-LNIUIKQ7.mjs} +143 -246
  20. package/dist/chunk-LNIUIKQ7.mjs.map +1 -0
  21. package/dist/chunk-MXTWKZO4.mjs +3268 -0
  22. package/dist/chunk-MXTWKZO4.mjs.map +1 -0
  23. package/dist/{chunk-ODZWR6JQ.mjs → chunk-MZ4C7EHF.mjs} +2042 -27
  24. package/dist/chunk-MZ4C7EHF.mjs.map +1 -0
  25. package/dist/chunk-RIPAEGI6.mjs +48 -0
  26. package/dist/chunk-RIPAEGI6.mjs.map +1 -0
  27. package/dist/{chunk-OCDLEI6I.mjs → chunk-SGPUJW3F.mjs} +117 -5
  28. package/dist/chunk-SGPUJW3F.mjs.map +1 -0
  29. package/dist/{chunk-ELJFJ2BU.mjs → chunk-X2KA6WHM.mjs} +29 -4
  30. package/dist/chunk-X2KA6WHM.mjs.map +1 -0
  31. package/dist/{chunk-BLXGONR2.mjs → chunk-XSFB7XXQ.mjs} +51 -4
  32. package/dist/chunk-XSFB7XXQ.mjs.map +1 -0
  33. package/dist/cloud/frame-context.d.ts +2 -2
  34. package/dist/cloud/frame-context.d.ts.map +1 -1
  35. package/dist/construct-renderer.mjs +810 -2073
  36. package/dist/construct-renderer.mjs.map +1 -1
  37. package/dist/device/gpu-residency.d.ts +19 -19
  38. package/dist/device/gpu-residency.d.ts.map +1 -1
  39. package/dist/errors/render.d.ts +3 -2
  40. package/dist/errors/render.d.ts.map +1 -1
  41. package/dist/extract/material-context.d.ts +7 -0
  42. package/dist/extract/material-context.d.ts.map +1 -0
  43. package/dist/features/host.d.ts +5 -0
  44. package/dist/features/host.d.ts.map +1 -1
  45. package/dist/features/plan.d.ts +2 -0
  46. package/dist/features/plan.d.ts.map +1 -1
  47. package/dist/features/types.d.ts +4 -0
  48. package/dist/features/types.d.ts.map +1 -1
  49. package/dist/glyph-text-layout-system.d.ts +1 -1
  50. package/dist/glyph-text-layout-system.d.ts.map +1 -1
  51. package/dist/gpu-driven/shadow-ownership.d.ts +3 -3
  52. package/dist/gpu-driven/shadow-ownership.d.ts.map +1 -1
  53. package/dist/ibl/frame-resources.d.ts.map +1 -1
  54. package/dist/ibl/skylight-bind-group.d.ts +4 -4
  55. package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
  56. package/dist/index.d.ts +3 -0
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.mjs +530 -9
  59. package/dist/index.mjs.map +1 -1
  60. package/dist/instances.d.ts +3 -4
  61. package/dist/instances.d.ts.map +1 -1
  62. package/dist/internal.mjs +7 -8
  63. package/dist/internal.mjs.map +1 -1
  64. package/dist/pbr-pipeline.d.ts +3 -3
  65. package/dist/pbr-pipeline.d.ts.map +1 -1
  66. package/dist/pipeline/standard-output/lut-gpu.d.ts +2 -2
  67. package/dist/pipeline/standard-output/lut-gpu.d.ts.map +1 -1
  68. package/dist/pipeline/standard-output/resources.d.ts +2 -2
  69. package/dist/pipeline/standard-output/resources.d.ts.map +1 -1
  70. package/dist/publication/contract.d.ts +77 -0
  71. package/dist/publication/contract.d.ts.map +1 -0
  72. package/dist/publication/dependencies.d.ts +7 -0
  73. package/dist/publication/dependencies.d.ts.map +1 -0
  74. package/dist/publication/prepare-geometry.d.ts +9 -0
  75. package/dist/publication/prepare-geometry.d.ts.map +1 -0
  76. package/dist/publication/programs.d.ts +5 -0
  77. package/dist/publication/programs.d.ts.map +1 -0
  78. package/dist/publication/publisher.d.ts +33 -0
  79. package/dist/publication/publisher.d.ts.map +1 -0
  80. package/dist/publication/receiver.d.ts +29 -0
  81. package/dist/publication/receiver.d.ts.map +1 -0
  82. package/dist/publication/resource-scope.d.ts +18 -0
  83. package/dist/publication/resource-scope.d.ts.map +1 -0
  84. package/dist/publication/targets.d.ts +33 -0
  85. package/dist/publication/targets.d.ts.map +1 -0
  86. package/dist/publication/video.d.ts +5 -0
  87. package/dist/publication/video.d.ts.map +1 -0
  88. package/dist/record/fold-buckets.d.ts +3 -3
  89. package/dist/record/fold-buckets.d.ts.map +1 -1
  90. package/dist/record/frame-snapshot.d.ts +6 -6
  91. package/dist/record/frame-snapshot.d.ts.map +1 -1
  92. package/dist/record/frame.d.ts +5 -4
  93. package/dist/record/frame.d.ts.map +1 -1
  94. package/dist/record/helpers.d.ts +2 -2
  95. package/dist/record/helpers.d.ts.map +1 -1
  96. package/dist/record/main-pass-geometry.d.ts +2 -2
  97. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  98. package/dist/record/main-pass-material.d.ts +7 -9
  99. package/dist/record/main-pass-material.d.ts.map +1 -1
  100. package/dist/record/main-pass-sprite-draws.d.ts +3 -16
  101. package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
  102. package/dist/record/main-pass.d.ts.map +1 -1
  103. package/dist/record/prepared-material-bindings.d.ts +3 -3
  104. package/dist/record/prepared-material-bindings.d.ts.map +1 -1
  105. package/dist/record/render-context.d.ts +2 -2
  106. package/dist/record/render-context.d.ts.map +1 -1
  107. package/dist/record/sprite-instance-buffer.d.ts +31 -0
  108. package/dist/record/sprite-instance-buffer.d.ts.map +1 -0
  109. package/dist/record/target-capture-graph.d.ts +19 -0
  110. package/dist/record/target-capture-graph.d.ts.map +1 -0
  111. package/dist/record/typed-frame-graph.d.ts +2 -13
  112. package/dist/record/typed-frame-graph.d.ts.map +1 -1
  113. package/dist/record/view-ubo.d.ts.map +1 -1
  114. package/dist/record/volume-params.d.ts +2 -2
  115. package/dist/record/volume-params.d.ts.map +1 -1
  116. package/dist/recovery/render-system-candidate.d.ts +5 -5
  117. package/dist/recovery/render-system-candidate.d.ts.map +1 -1
  118. package/dist/render-contract.d.ts +4 -1
  119. package/dist/render-contract.d.ts.map +1 -1
  120. package/dist/render-system-extract-tail.d.ts.map +1 -1
  121. package/dist/render-system-extract.d.ts +2 -0
  122. package/dist/render-system-extract.d.ts.map +1 -1
  123. package/dist/render-system-presentation.d.ts +2 -2
  124. package/dist/render-system-presentation.d.ts.map +1 -1
  125. package/dist/render-system-projections.d.ts +3 -2
  126. package/dist/render-system-projections.d.ts.map +1 -1
  127. package/dist/render-system-residency.d.ts +2 -2
  128. package/dist/render-system-residency.d.ts.map +1 -1
  129. package/dist/render-system.d.ts +3 -7
  130. package/dist/render-system.d.ts.map +1 -1
  131. package/dist/scene/render-scene.d.ts +8 -4
  132. package/dist/scene/render-scene.d.ts.map +1 -1
  133. package/dist/scene/render-source.d.ts +42 -0
  134. package/dist/scene/render-source.d.ts.map +1 -0
  135. package/dist/scene/source-systems.d.ts +6 -0
  136. package/dist/scene/source-systems.d.ts.map +1 -0
  137. package/dist/systems/skin-palette-allocator.d.ts.map +1 -1
  138. package/dist/systems/skin-palette-types.d.ts +10 -0
  139. package/dist/systems/skin-palette-types.d.ts.map +1 -1
  140. package/dist/systems/transparent-dispatch.d.ts +2 -2
  141. package/dist/systems/transparent-dispatch.d.ts.map +1 -1
  142. package/dist/systems/transparent-sort-config.d.ts +2 -1
  143. package/dist/systems/transparent-sort-config.d.ts.map +1 -1
  144. package/dist/temporal/index.mjs +6 -7
  145. package/package.json +21 -21
  146. package/src/__tests__/auto-exposure-transaction.integration.test.ts +4 -9
  147. package/src/__tests__/device-feature-admission.unit.test.ts +3 -2
  148. package/src/__tests__/gpu-driven-pbr.dawn.test.ts +3 -4
  149. package/src/__tests__/ktx2-basis-gpu-consumer.dawn.test.ts +4 -1
  150. package/src/__tests__/main-pass-material-medium-selection.unit.test.ts +0 -1
  151. package/src/__tests__/materials-standard-contract.unit.test.ts +0 -1
  152. package/src/__tests__/physical-clearcoat.integration.test.ts +23 -23
  153. package/src/__tests__/physical-material-budget.unit.test.ts +40 -0
  154. package/src/__tests__/render-error-code-owner.test-d.ts +1 -0
  155. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +1 -1
  156. package/src/assembly/device-feature-admission.ts +2 -2
  157. package/src/assembly/host-contract.ts +5 -2
  158. package/src/assembly/recovery/renderer-recover.ts +9 -0
  159. package/src/assembly/render-target-host.ts +57 -47
  160. package/src/assembly/renderer-facade.ts +3 -1
  161. package/src/assembly/webgpu-ready.ts +1 -1
  162. package/src/assembly/webgpu-renderer.ts +89 -61
  163. package/src/cloud/frame-context.ts +2 -2
  164. package/src/device/gpu-residency.ts +26 -25
  165. package/src/errors/render.ts +3 -1
  166. package/src/extract/material-context.ts +21 -0
  167. package/src/features/host.ts +13 -5
  168. package/src/features/plan.ts +2 -0
  169. package/src/features/types.ts +4 -0
  170. package/src/glyph-text-layout-system.ts +2 -2
  171. package/src/gpu-driven/shadow-ownership.ts +3 -3
  172. package/src/ibl/frame-resources.ts +0 -1
  173. package/src/ibl/skylight-bind-group.ts +13 -25
  174. package/src/index.ts +9 -0
  175. package/src/instances.ts +9 -6
  176. package/src/pbr-pipeline.ts +11 -17
  177. package/src/pipeline/standard-output/lut-gpu.ts +3 -3
  178. package/src/pipeline/standard-output/resources.ts +2 -2
  179. package/src/publication/__tests__/derived-geometry.integration.test.ts +177 -0
  180. package/src/publication/__tests__/features.integration.test.ts +122 -0
  181. package/src/publication/__tests__/programs.unit.test.ts +25 -0
  182. package/src/publication/__tests__/publication.integration.test.ts +370 -0
  183. package/src/publication/__tests__/targets.integration.test.ts +120 -0
  184. package/src/publication/__tests__/video.browser.test.ts +50 -0
  185. package/src/publication/contract.ts +104 -0
  186. package/src/publication/dependencies.ts +54 -0
  187. package/src/publication/prepare-geometry.ts +94 -0
  188. package/src/publication/programs.ts +45 -0
  189. package/src/publication/publisher.ts +618 -0
  190. package/src/publication/receiver.ts +297 -0
  191. package/src/publication/resource-scope.ts +31 -0
  192. package/src/publication/targets.ts +182 -0
  193. package/src/publication/video.ts +33 -0
  194. package/src/record/__tests__/material-bg-cache.unit.test.ts +15 -1
  195. package/src/record/__tests__/sprite-material-abi.unit.test.ts +8 -10
  196. package/src/record/__tests__/video-texture-view.unit.test.ts +9 -9
  197. package/src/record/fold-buckets.ts +8 -5
  198. package/src/record/frame-snapshot.ts +6 -6
  199. package/src/record/frame.ts +42 -22
  200. package/src/record/helpers.ts +2 -2
  201. package/src/record/main-pass-geometry.ts +21 -2
  202. package/src/record/main-pass-material.ts +37 -33
  203. package/src/record/main-pass-sprite-draws.ts +6 -152
  204. package/src/record/main-pass.ts +8 -7
  205. package/src/record/prepared-material-bindings.ts +11 -6
  206. package/src/record/render-context.ts +2 -2
  207. package/src/record/shadow-pass.ts +0 -3
  208. package/src/record/sprite-instance-buffer.ts +155 -0
  209. package/src/record/target-capture-graph.ts +258 -0
  210. package/src/record/typed-frame-graph.ts +6 -206
  211. package/src/record/view-ubo.ts +1 -1
  212. package/src/record/volume-params.ts +6 -6
  213. package/src/recovery/render-system-candidate.ts +10 -10
  214. package/src/render-contract.ts +6 -1
  215. package/src/render-system-extract-tail.ts +33 -17
  216. package/src/render-system-extract.ts +2 -0
  217. package/src/render-system-presentation.ts +2 -2
  218. package/src/render-system-projections.ts +3 -2
  219. package/src/render-system-residency.ts +2 -2
  220. package/src/render-system.ts +82 -53
  221. package/src/scene/render-scene.ts +110 -100
  222. package/src/scene/render-source.ts +90 -0
  223. package/src/scene/source-systems.ts +33 -0
  224. package/src/systems/skin-palette-allocator.ts +1 -0
  225. package/src/systems/skin-palette-types.ts +11 -0
  226. package/src/systems/transparent-dispatch.ts +2 -2
  227. package/src/systems/transparent-sort-config.ts +3 -1
  228. package/dist/chunk-BLXGONR2.mjs.map +0 -1
  229. package/dist/chunk-ELJFJ2BU.mjs.map +0 -1
  230. package/dist/chunk-ILQ5MEZF.mjs +0 -32
  231. package/dist/chunk-ILQ5MEZF.mjs.map +0 -1
  232. package/dist/chunk-MKDIT2EM.mjs.map +0 -1
  233. package/dist/chunk-OCDLEI6I.mjs.map +0 -1
  234. package/dist/chunk-ODZWR6JQ.mjs.map +0 -1
  235. package/dist/chunk-RXZ3RGOU.mjs +0 -53
  236. package/dist/chunk-RXZ3RGOU.mjs.map +0 -1
  237. package/dist/chunk-SHFUFJ5N.mjs +0 -1781
  238. package/dist/chunk-SHFUFJ5N.mjs.map +0 -1
  239. package/dist/chunk-UI5SFODX.mjs.map +0 -1
  240. package/dist/chunk-UINHLCZA.mjs +0 -2389
  241. package/dist/chunk-UINHLCZA.mjs.map +0 -1
  242. package/dist/chunk-WEX45HKH.mjs +0 -1524
  243. package/dist/chunk-WEX45HKH.mjs.map +0 -1
@@ -79,6 +79,11 @@ interface ReadbackRecord {
79
79
  export interface RenderTargetHost {
80
80
  /** The logical owner for every target lifecycle operation. */
81
81
  readonly owner: 'renderer';
82
+ /** Logical descriptions for the source publication boundary; no physical state escapes. */
83
+ descriptions(): readonly {
84
+ readonly target: RenderTarget;
85
+ readonly descriptor: RenderTargetDescriptor;
86
+ }[];
82
87
  createRenderTarget(descriptor: RenderTargetDescriptor): RenderResult<RenderTarget, RenderError>;
83
88
  resizeRenderTarget(
84
89
  target: RenderTarget,
@@ -231,53 +236,51 @@ function makePhysical(
231
236
  const faceViews: TextureView[] = [];
232
237
  const depthTextures: Texture[] = [];
233
238
  const depthViews: TextureView[] = [];
234
- if (descriptor.shape === 'cube') {
235
- for (let face = 0; face < layers; face += 1) {
236
- const depth = device.createTexture({
237
- label: `render-target.${generation}.depth.${face}`,
238
- size: {
239
- width: descriptor.width,
240
- height: descriptor.height,
241
- depthOrArrayLayers: descriptor.sampleCount === 4 ? 1 : layers,
242
- },
243
- mipLevelCount: 1,
244
- sampleCount: descriptor.sampleCount,
245
- dimension: '2d',
246
- format: 'depth24plus-stencil8',
247
- usage: GPU_TEXTURE_USAGE_RENDER_ATTACHMENT,
248
- viewFormats: undefined,
249
- textureBindingViewDimension: undefined,
250
- });
251
- if (!depth.ok)
252
- return {
253
- ok: false,
254
- error: new RenderTargetOperationFailedError({
255
- operation: 'create',
256
- stage: 'allocation',
257
- generation,
258
- cause: depth.error,
259
- recovery: 'retry',
260
- }),
261
- };
262
- const depthView = device.createTextureView(depth.value, {
263
- dimension: '2d',
264
- baseArrayLayer: descriptor.sampleCount === 4 ? 0 : face,
265
- arrayLayerCount: 1,
266
- });
267
- if (!depthView.ok)
268
- return {
269
- ok: false,
270
- error: new RenderTargetOperationFailedError({
271
- operation: 'create',
272
- stage: 'allocation',
273
- generation,
274
- cause: depthView.error,
275
- recovery: 'retry',
276
- }),
277
- };
278
- depthTextures.push(depth.value);
279
- depthViews.push(depthView.value);
280
- }
239
+ for (let face = 0; face < layers; face += 1) {
240
+ const depth = device.createTexture({
241
+ label: `render-target.${generation}.depth.${face}`,
242
+ size: {
243
+ width: descriptor.width,
244
+ height: descriptor.height,
245
+ depthOrArrayLayers: descriptor.sampleCount === 4 ? 1 : layers,
246
+ },
247
+ mipLevelCount: 1,
248
+ sampleCount: descriptor.sampleCount,
249
+ dimension: '2d',
250
+ format: 'depth24plus-stencil8',
251
+ usage: GPU_TEXTURE_USAGE_RENDER_ATTACHMENT,
252
+ viewFormats: undefined,
253
+ textureBindingViewDimension: undefined,
254
+ });
255
+ if (!depth.ok)
256
+ return {
257
+ ok: false,
258
+ error: new RenderTargetOperationFailedError({
259
+ operation: 'create',
260
+ stage: 'allocation',
261
+ generation,
262
+ cause: depth.error,
263
+ recovery: 'retry',
264
+ }),
265
+ };
266
+ const depthView = device.createTextureView(depth.value, {
267
+ dimension: '2d',
268
+ baseArrayLayer: descriptor.sampleCount === 4 ? 0 : face,
269
+ arrayLayerCount: 1,
270
+ });
271
+ if (!depthView.ok)
272
+ return {
273
+ ok: false,
274
+ error: new RenderTargetOperationFailedError({
275
+ operation: 'create',
276
+ stage: 'allocation',
277
+ generation,
278
+ cause: depthView.error,
279
+ recovery: 'retry',
280
+ }),
281
+ };
282
+ depthTextures.push(depth.value);
283
+ depthViews.push(depthView.value);
281
284
  }
282
285
  if (descriptor.sampleCount === 4) {
283
286
  for (let face = 0; face < layers; face += 1) {
@@ -405,6 +408,13 @@ export function createRenderTargetHost(options: RenderTargetHostOptions = {}): R
405
408
 
406
409
  return Object.freeze({
407
410
  owner: 'renderer' as const,
411
+ descriptions() {
412
+ return [...targets].map((target) => {
413
+ const state = owner.inspect(target);
414
+ if (!state.ok) throw state.error;
415
+ return { target, descriptor: state.value.candidate?.descriptor ?? state.value.descriptor };
416
+ });
417
+ },
408
418
  createRenderTarget(
409
419
  descriptor: RenderTargetDescriptor,
410
420
  ): RenderResult<RenderTarget, RenderError> {
@@ -1,3 +1,4 @@
1
+ import { RenderPublicationError } from '../publication/contract';
1
2
  // Public renderer facade and profile validation.
2
3
  // This module narrows the lifetime-heavy host implementation to the stable
3
4
  // lease/receipt contract and owns the profile/error projection helpers.
@@ -64,7 +65,8 @@ export function exposeRenderer(renderer: RendererHostImplementation): Renderer {
64
65
  cause: structuredRendererCause(cause, 'renderer-event'),
65
66
  });
66
67
  const drawError = (cause: RhiError | RenderError): RenderError => {
67
- if (cause instanceof MotionBlurValidationError) return cause;
68
+ if (cause instanceof MotionBlurValidationError || cause instanceof RenderPublicationError)
69
+ return cause;
68
70
  if (cause instanceof RendererOperationError) return cause;
69
71
  if (cause instanceof RendererContractFailureError) {
70
72
  return new RendererOperationError('device-operation-failed', {
@@ -1439,7 +1439,7 @@ export async function buildReadyWebGPU(
1439
1439
  // instances) and the 4-slot pipeline layout in one call, throwing on each
1440
1440
  // device.createBindGroupLayout / device.createPipelineLayout failure.
1441
1441
  // material BGL now carries 24 entries (canonical material 0..14 + Skylight
1442
- // 15..21 + transmission 22..23); authored physical maps append after the
1442
+ // 15..20 + transmission 21..22); authored physical maps append after the
1443
1443
  // engine-owned injections. The pipeline layout itself stays at 4 slots, no
1444
1444
  // @group(4) allocated (D-5 round-4 fix for the round-2 maxBindGroups=4
1445
1445
  // BLOCKER). The standalone unlit BGL is the 15-entry user region exposed by
@@ -6,7 +6,7 @@ import {
6
6
  DynamicTextureStore,
7
7
  } from '@forgeax/engine-assets-runtime';
8
8
  import { audioLoader } from '@forgeax/engine-audio-webaudio';
9
- import { Update, type World } from '@forgeax/engine-ecs';
9
+ import type { World } from '@forgeax/engine-ecs';
10
10
  import { createRenderReadLease, type RenderReadLease } from '@forgeax/engine-ecs/projection';
11
11
  import {
12
12
  DEFAULT_VERTEX_ATTRIBUTE_MAP,
@@ -21,7 +21,6 @@ import type {
21
21
  RhiDevice,
22
22
  } from '@forgeax/engine-rhi';
23
23
  import { err, ok, RhiError, validateDrawArgs } from '@forgeax/engine-rhi';
24
- import { registerPropagateTransforms } from '@forgeax/engine-scene';
25
24
  import {
26
25
  findVariantByKey,
27
26
  type MaterialShaderEntry,
@@ -39,6 +38,11 @@ import type {
39
38
  PrimitiveTopology,
40
39
  VertexAttributeMap,
41
40
  } from '@forgeax/engine-types';
41
+ import { type PublishedRenderFrameInput, RenderPublicationError } from '../publication/contract';
42
+ import { installPublicationPrograms } from '../publication/programs';
43
+ import { type PreparedRenderPublication, RenderPublicationReceiver } from '../publication/receiver';
44
+ import { RenderPublicationTargetReceiver } from '../publication/targets';
45
+ import { registerRenderSourceSystems } from '../scene/source-systems';
42
46
  import { transmissionBackdropAvailable } from './device-feature-admission';
43
47
 
44
48
  export type { MaterialShaderManifestEntry } from '@forgeax/engine-shader';
@@ -71,7 +75,6 @@ import {
71
75
  type PostProcessShaderEntry,
72
76
  postProcessShaderModuleLabel,
73
77
  } from '../fullscreen-post-process-pass';
74
- import { glyphTextLayoutSystem } from '../glyph-text-layout-system';
75
78
  import { GPU_SHADER_STAGE_FRAGMENT, GPU_SHADER_STAGE_VERTEX } from '../gpu-stage';
76
79
  import { type BloomInspection, emptyBloomInspection } from '../inspection-types';
77
80
  import { DeviceScope } from '../lifecycle';
@@ -155,7 +158,6 @@ import {
155
158
  import { createGpuPassTimingObservationStore, observeGpuPassTimingDisabled } from '../renderer.js';
156
159
  import { postSpawnResolveJoints } from '../scene-instances/post-spawn-resolve-joints';
157
160
  import type { RenderTarget } from '../targets/contracts';
158
- import { tilemapChunkExtractSystem } from '../tilemap-chunk-extract-system';
159
161
  import type { TypedFrameObservationCapture } from '../typed-render-graph-primitives';
160
162
  import { registerAnalyticFogPostProcess } from './analytic-fog-registration';
161
163
  import type { RhiBackendPack } from './backend-contract';
@@ -303,7 +305,6 @@ export type { WebGPUOutcome, WebGPURendererInternals } from './webgpu-renderer-c
303
305
  export { disposeObservationCaptureSet } from './webgpu-renderer-observation';
304
306
 
305
307
  /** Bundler injection and the WebGPU renderer construction entry points. */
306
- let nextDerivedSystemId = 0;
307
308
  export async function tryCreateWebGPURenderer(
308
309
  canvas: HTMLCanvasElement | OffscreenCanvas,
309
310
  options: RendererOptions | undefined,
@@ -315,7 +316,12 @@ export async function tryCreateWebGPURenderer(
315
316
  async function makeWebGPURenderer(
316
317
  internals: WebGPURendererInternals,
317
318
  ): Promise<RendererAssemblyImplementation> {
319
+ let publicationTargets: RenderPublicationTargetReceiver | undefined;
318
320
  let disposed = false;
321
+ const publicationReceiver =
322
+ internals.options?.publicationSource === undefined
323
+ ? undefined
324
+ : new RenderPublicationReceiver(internals.options.publicationSource);
319
325
  const observeTiming = observeGpuPassTimingDisabled;
320
326
  const frameContinuations = new Set<ContinuationTerminator>();
321
327
  type RecoveryInspection = RenderInspection['recovery'];
@@ -584,27 +590,7 @@ async function makeWebGPURenderer(
584
590
  }
585
591
  return state.shaderInstance;
586
592
  };
587
- // D-S9: AssetRegistry instance shared with RenderSystem. RenderSystem
588
- // looks up MeshFilter.assetHandle here per frame; buildReadyWebGPU step 3
589
- // uploads the registry's builtin mesh geometry to GPU buffers so the
590
- // RenderSystem can route MeshFilter.assetHandle to a real (vbo, ibo) pair.
591
- // feat-20260527 M1 / w1: ShaderCatalog is now eager-constructed and
592
- // constructor-injected into AssetRegistry (plan-strategy D-1). The
593
- // ShaderCatalogDevice adapter (shared between the catalog and per-MaterialShader
594
- // pipeline cache) is already available at this point.
595
593
  const shaderCatalog = getShader();
596
- // bug-20260601-hello-tonemap-material-register M1 (plan-strategy D-1):
597
- // All material-shader entries are now registered from the manifest
598
- // BEFORE the renderer is returned, so `register<MaterialAsset>` referencing
599
- // an engine shader succeeds without waiting for `renderer.initialization`.
600
- // Host-injected ImportTransport remains a constructor-only dependency.
601
- // feat-20260705-runtime-tier2-decomposition M1 / w10 (D-1 / D-2): the sole
602
- // production assembly point injects (a) the post-spawn hook
603
- // `postSpawnResolveJoints` (auto-wire Skin.joints; D-1) and the audio
604
- // catalog-entry loader. The latter stays at this assembly boundary so
605
- // assets-runtime remains independent of the concrete Web Audio backend.
606
- // videoLoader is wired internally from graphics-extras; the full registry has
607
- // 11 engine kinds. This is the only `new AssetRegistry(...)` call in the repo.
608
594
  const assets = new AssetRegistry(
609
595
  shaderCatalog,
610
596
  internals.importTransport,
@@ -735,7 +721,9 @@ async function makeWebGPURenderer(
735
721
  gpuStore.configureGpuDevice(
736
722
  internals.device,
737
723
  packShaderFactory,
738
- (world, pod) => {
724
+ (world, pod, source) => {
725
+ // The accepted equirect identity already owns this renderer-local GPU projection.
726
+ if ('resolveAsset' in world) return ok(source);
739
727
  const handle = world.allocSharedRef('EquirectAsset', pod);
740
728
  return ok(handle);
741
729
  },
@@ -2562,7 +2550,6 @@ async function makeWebGPURenderer(
2562
2550
  currentGeneration: () => activeDeviceScope.generation,
2563
2551
  renderSystem,
2564
2552
  });
2565
- const derivedSystemNames = new Map<World, string>();
2566
2553
  const transformReleases = new Map<World, () => void>();
2567
2554
  const attachmentOwner = {};
2568
2555
  let frameId = 0;
@@ -2599,6 +2586,13 @@ async function makeWebGPURenderer(
2599
2586
  renderSystem.setSurfaceDynamicInput(frame);
2600
2587
  },
2601
2588
  attach(world: World): RenderResult<RenderWorldLease, RenderError> {
2589
+ if (publicationReceiver !== undefined)
2590
+ return err(
2591
+ new RenderPublicationError({
2592
+ reason: 'identity',
2593
+ subject: 'publication Renderer has no World attachment',
2594
+ }),
2595
+ );
2602
2596
  const attached = this.attachScene(world);
2603
2597
  if (!attached.ok) {
2604
2598
  return err(new RendererContractFailureError('attach', attached.error.hint));
@@ -2850,23 +2844,7 @@ async function makeWebGPURenderer(
2850
2844
  }
2851
2845
  if (attachedWorlds.has(world)) return ok(undefined);
2852
2846
  try {
2853
- const releaseTransforms = registerPropagateTransforms(world);
2854
- const derivedSystemName = `renderDerived:${++nextDerivedSystemId}`;
2855
- const derived = world.addSystem(Update, {
2856
- name: derivedSystemName,
2857
- queries: [],
2858
- fn: (world) => {
2859
- tilemapChunkExtractSystem(world, (guid) => assets.lookup(guid));
2860
- const glyphResult = glyphTextLayoutSystem(world, gpuStore);
2861
- if (!glyphResult.ok) throw glyphResult.error;
2862
- },
2863
- });
2864
- if (!derived.ok) {
2865
- releaseTransforms();
2866
- throw new Error(`failed to register ${derivedSystemName}: ${derived.error.code}`);
2867
- }
2868
- transformReleases.set(world, releaseTransforms);
2869
- derivedSystemNames.set(world, derivedSystemName);
2847
+ transformReleases.set(world, registerRenderSourceSystems(world, assets, gpuStore));
2870
2848
  attachedWorlds.add(world);
2871
2849
  return ok(undefined);
2872
2850
  } catch (cause) {
@@ -2897,11 +2875,6 @@ async function makeWebGPURenderer(
2897
2875
  }
2898
2876
  }
2899
2877
  renderSystem.detachScene(world);
2900
- const derivedSystemName = derivedSystemNames.get(world);
2901
- if (derivedSystemName !== undefined) {
2902
- world.removeSystem(Update, derivedSystemName);
2903
- derivedSystemNames.delete(world);
2904
- }
2905
2878
  transformReleases.get(world)?.();
2906
2879
  transformReleases.delete(world);
2907
2880
  },
@@ -3105,17 +3078,43 @@ async function makeWebGPURenderer(
3105
3078
  : ok(result.value);
3106
3079
  },
3107
3080
  draw(
3108
- worldsOrRequest: readonly World[] | RenderFrameInput,
3081
+ worldsOrRequest: readonly World[] | RenderFrameInput | PublishedRenderFrameInput,
3109
3082
  options?: DrawOwnerOptions,
3110
3083
  ): Result<void | FrameReceipt, RhiError | RenderError> {
3111
3084
  const isFrameRequest = !Array.isArray(worldsOrRequest);
3112
- const frameRequest = isFrameRequest ? (worldsOrRequest as RenderFrameInput) : undefined;
3085
+ const publicationRequest =
3086
+ isFrameRequest && 'publication' in worldsOrRequest
3087
+ ? (worldsOrRequest as PublishedRenderFrameInput)
3088
+ : undefined;
3089
+ const frameRequest =
3090
+ isFrameRequest && publicationRequest === undefined
3091
+ ? (worldsOrRequest as RenderFrameInput)
3092
+ : undefined;
3093
+ let publication: PreparedRenderPublication | undefined;
3094
+ if (publicationRequest !== undefined) {
3095
+ if (publicationReceiver === undefined)
3096
+ return err(
3097
+ new RenderPublicationError({
3098
+ reason: 'identity',
3099
+ subject: 'RendererOptions.publicationSource',
3100
+ }),
3101
+ );
3102
+ } else if (publicationReceiver !== undefined) {
3103
+ return err(
3104
+ new RenderPublicationError({
3105
+ reason: 'identity',
3106
+ subject: 'a publication-bound renderer cannot consume local World leases',
3107
+ }),
3108
+ );
3109
+ }
3113
3110
  const worlds: readonly World[] =
3114
3111
  frameRequest !== undefined
3115
3112
  ? frameRequest.leases
3116
3113
  .map((lease) => attachedLeases.get(lease))
3117
3114
  .filter((world): world is World => world !== undefined)
3118
- : (worldsOrRequest as readonly World[]);
3115
+ : publicationRequest === undefined
3116
+ ? (worldsOrRequest as readonly World[])
3117
+ : [];
3119
3118
  const readLeases: readonly RenderReadLease[] | undefined =
3120
3119
  frameRequest !== undefined
3121
3120
  ? frameRequest.leases
@@ -3170,7 +3169,14 @@ async function makeWebGPURenderer(
3170
3169
  ? {}
3171
3170
  : { geometryLane: frameRequest.geometryLane }),
3172
3171
  }
3173
- : (options ?? { cameraOwner: 0, resourceOwner: 0 });
3172
+ : publicationRequest === undefined
3173
+ ? (options ?? { cameraOwner: 0, resourceOwner: 0 })
3174
+ : {
3175
+ cameraOwner: 0,
3176
+ resourceOwner: 0,
3177
+ sampleTimeSeconds: publicationRequest.publication.sampleTimeSeconds,
3178
+ temporalReset: publicationRequest.publication.temporalReset,
3179
+ };
3174
3180
  // feat-20260612-rhi-destroy-renderer-dispose-gpu-lifecycle / M5 / w21
3175
3181
  // (plan-strategy D-1, D-8): post-dispose the renderer is dead. AI
3176
3182
  // users observing `result.ok === false && err.code === 'rhi-not-
@@ -3246,7 +3252,8 @@ async function makeWebGPURenderer(
3246
3252
  // are non-exclusive: an empty array short-circuits to empty-worlds. The
3247
3253
  // check is defensive against JS callers passing a non-array despite the
3248
3254
  // compile-time World[] type (red-window migration safety).
3249
- const worldCount = Array.isArray(worlds) ? worlds.length : 0;
3255
+ const worldCount =
3256
+ publicationRequest === undefined ? (Array.isArray(worlds) ? worlds.length : 0) : 1;
3250
3257
  if (worldCount === 0 && internals.device.caps.backendKind === 'null') {
3251
3258
  return ok(undefined);
3252
3259
  }
@@ -3268,12 +3275,27 @@ async function makeWebGPURenderer(
3268
3275
  internals.errorRegistry,
3269
3276
  );
3270
3277
  if (!contextConfigured.ok) return contextConfigured;
3271
- renderTargetHost.beginFrame();
3272
3278
  // w24 — facade-level try/catch produces Result.err on unexpected throw
3273
3279
  // (D-P6 dual-channel preserved: per-stage RhiError continues to fan out
3274
3280
  // through onError separately; the facade Result is the synchronous
3275
3281
  // summary AI users can ignore or branch on).
3276
3282
  try {
3283
+ if (publicationRequest !== undefined && publicationReceiver !== undefined) {
3284
+ const accepted = publicationReceiver.accept(publicationRequest.publication);
3285
+ if (!accepted.ok) return accepted;
3286
+ publicationTargets ??= new RenderPublicationTargetReceiver(renderTargetHost);
3287
+ publication = {
3288
+ ...publicationTargets.apply(accepted.value),
3289
+ ...(publicationRequest.onFeatureSourceSubmitted === undefined
3290
+ ? {}
3291
+ : {
3292
+ onFeatureSourceSubmitted: publicationRequest.onFeatureSourceSubmitted,
3293
+ }),
3294
+ };
3295
+ installPublicationPrograms(assets, publicationRequest.publication.programs);
3296
+ }
3297
+ renderTargetHost.beginFrame();
3298
+
3277
3299
  const timingHost = internals as WebGPURendererInternals & {
3278
3300
  gpuPassTimingFrameIdentity?:
3279
3301
  | {
@@ -3311,7 +3333,13 @@ async function makeWebGPURenderer(
3311
3333
  graphGeneration: 0,
3312
3334
  };
3313
3335
  }
3314
- const submitted = renderSystem.draw(worlds, drawOptions, readLeases, volumeTimingCapture);
3336
+ const submitted = renderSystem.draw(
3337
+ worlds,
3338
+ drawOptions,
3339
+ readLeases,
3340
+ volumeTimingCapture,
3341
+ publication,
3342
+ );
3315
3343
  timingHost.observationFrameId = undefined;
3316
3344
  timingHost.observationCaptureDomains = undefined;
3317
3345
  timingHost.gpuPassTimingFrameIdentity = undefined;
@@ -3510,6 +3538,10 @@ async function makeWebGPURenderer(
3510
3538
  });
3511
3539
  internals.errorRegistry.fire(e);
3512
3540
  return err(e);
3541
+ } finally {
3542
+ // Native video pixels have been consumed by synchronous queue copy calls.
3543
+ // Release them on every success/failure path, including direct publication users.
3544
+ publicationReceiver?.releaseVideoFrames();
3513
3545
  }
3514
3546
  },
3515
3547
  async observe(
@@ -3752,6 +3784,7 @@ async function makeWebGPURenderer(
3752
3784
  disposed = true;
3753
3785
  for (const world of attachedWorlds) dynamicGeometryHost.invalidateDynamicGeometryWorld(world);
3754
3786
  dynamicGeometry.dispose();
3787
+ publicationReceiver?.dispose();
3755
3788
  for (const continuation of frameContinuations) {
3756
3789
  continuation.terminate({ code: 'disposed' });
3757
3790
  }
@@ -3762,11 +3795,6 @@ async function makeWebGPURenderer(
3762
3795
  }
3763
3796
  for (const world of attachedWorlds) {
3764
3797
  try {
3765
- const derivedSystemName = derivedSystemNames.get(world);
3766
- if (derivedSystemName !== undefined) {
3767
- world.removeSystem(Update, derivedSystemName);
3768
- derivedSystemNames.delete(world);
3769
- }
3770
3798
  transformReleases.get(world)?.();
3771
3799
  transformReleases.delete(world);
3772
3800
  } catch (cause) {
@@ -1,12 +1,12 @@
1
- import type { World } from '@forgeax/engine-ecs';
2
1
  import type { RenderFeatureFrameContext, RenderFeatureViewContext } from '../features/types';
2
+ import type { RenderResourceScope } from '../publication/resource-scope';
3
3
  import type { CameraSnapshot } from '../render-contract';
4
4
  import type { ExtractedCloudLayer } from './extract';
5
5
 
6
6
  /** Project the stable camera/world facts consumed by the cloud feature owner. */
7
7
  export function createCloudFeatureView(
8
8
  camera: CameraSnapshot | undefined,
9
- worlds: readonly World[],
9
+ worlds: readonly RenderResourceScope[],
10
10
  cameraOwner: number,
11
11
  width: number,
12
12
  height: number,
@@ -1,3 +1,4 @@
1
+ import type { RenderResourceScope } from '../publication/resource-scope';
1
2
  import { type MeshResidencyLease, MeshResidencyLifetime } from './mesh-residency-lifetime';
2
3
  // @forgeax/engine-runtime - GpuResidencyCache
3
4
  // (feat-20260601-device/gpu-residency-extraction M1; M2 added the deriveRenderData
@@ -37,7 +38,6 @@ import {
37
38
  type MipmapShaderModuleFactory,
38
39
  prepareMipmaps,
39
40
  } from '@forgeax/engine-assets-runtime';
40
- import type { World } from '@forgeax/engine-ecs';
41
41
  import { deriveVertexCount, type VertexLayoutProjection } from '@forgeax/engine-geometry';
42
42
  import { halfFloat } from '@forgeax/engine-math';
43
43
  import {
@@ -288,8 +288,9 @@ function makeImageError<C extends ImageErrorCode>(
288
288
 
289
289
  /** Cube-POD register-relay injected by the wire layer (D-3). */
290
290
  type RegisterCube = (
291
- world: World,
291
+ world: RenderResourceScope,
292
292
  pod: EquirectAsset,
293
+ source: Handle<'EquirectAsset', 'shared'>,
293
294
  ) => Result<Handle<'EquirectAsset', 'shared'>, AssetError>;
294
295
 
295
296
  // feat-20260612-rhi-destroy-renderer-dispose-gpu-lifecycle / M-3 / w11:
@@ -607,16 +608,16 @@ export class GpuResidencyCache {
607
608
  }
608
609
 
609
610
  /**
610
- * Compose a cache key from the raw handle slot and its owning World. The
611
+ * Compose a cache key from the raw handle slot and its owning RenderResourceScope. The
611
612
  * draw-time world index is only a position in one frame's `worlds[]`; it is
612
- * not an asset identity and can be reused by a later World. World objects
613
+ * not an asset identity and can be reused by a later RenderResourceScope. RenderResourceScope objects
613
614
  * therefore receive a renderer-local namespace, while numeric callers keep
614
615
  * the legacy namespace for low-level tests and built-in upload paths.
615
616
  */
616
- private readonly worldNamespaces = new WeakMap<World, number>();
617
+ private readonly worldNamespaces = new WeakMap<RenderResourceScope, number>();
617
618
  private nextWorldNamespace = 1;
618
619
 
619
- private worldKey(slot: number, world: World | number): string | number {
620
+ private worldKey(slot: number, world: RenderResourceScope | number): string | number {
620
621
  if (typeof world === 'number') {
621
622
  return world === 0 ? slot : world * 0x1000000 + slot;
622
623
  }
@@ -809,7 +810,7 @@ export class GpuResidencyCache {
809
810
  */
810
811
  evictTexture(
811
812
  handle: Handle<'TextureAsset', 'shared'>,
812
- worldId: World | number = 0,
813
+ worldId: RenderResourceScope | number = 0,
813
814
  ): { freed: number; errors: RhiError[] } {
814
815
  const id = this.worldKey(handleSlot(handle), worldId);
815
816
  const entry = this.textureGpuHandles.get(id);
@@ -835,7 +836,7 @@ export class GpuResidencyCache {
835
836
 
836
837
  evictMesh(
837
838
  handle: Handle<'MeshAsset', 'shared'>,
838
- worldId: World | number = 0,
839
+ worldId: RenderResourceScope | number = 0,
839
840
  ): { freed: number; errors: RhiError[] } {
840
841
  const id = this.worldKey(Number(handle), worldId);
841
842
  const entry = this.meshGpuHandles.get(id);
@@ -1000,7 +1001,7 @@ export class GpuResidencyCache {
1000
1001
  */
1001
1002
  _getTextureGpuTexture(
1002
1003
  handle: Handle<'TextureAsset', 'shared'>,
1003
- worldId: World | number = 0,
1004
+ worldId: RenderResourceScope | number = 0,
1004
1005
  ): GpuTexture | undefined {
1005
1006
  return this.textureGpuHandles.get(this.worldKey(handleSlot(handle), worldId))?.texture;
1006
1007
  }
@@ -1011,7 +1012,7 @@ export class GpuResidencyCache {
1011
1012
  */
1012
1013
  getTextureGpuView(
1013
1014
  handle: Handle<'TextureAsset', 'shared'>,
1014
- worldId: World | number = 0,
1015
+ worldId: RenderResourceScope | number = 0,
1015
1016
  ): TextureView | undefined {
1016
1017
  return this.textureGpuHandles.get(this.worldKey(handleSlot(handle), worldId))?.view;
1017
1018
  }
@@ -1024,7 +1025,7 @@ export class GpuResidencyCache {
1024
1025
  ensureSamplerResident(
1025
1026
  handle: Handle<'SamplerAsset', 'shared'>,
1026
1027
  pod: SamplerAsset,
1027
- worldId: World | number = 0,
1028
+ worldId: RenderResourceScope | number = 0,
1028
1029
  ): Result<Sampler, RhiError> {
1029
1030
  const key = this.worldKey(handleSlot(handle), worldId);
1030
1031
  const existing = this.samplerGpuHandles.get(key);
@@ -1093,7 +1094,7 @@ export class GpuResidencyCache {
1093
1094
  */
1094
1095
  getMeshGpuHandles(
1095
1096
  handle: Handle<'MeshAsset', 'shared'>,
1096
- worldId: World | number = 0,
1097
+ worldId: RenderResourceScope | number = 0,
1097
1098
  ): MeshGpuHandles | undefined {
1098
1099
  return this.meshGpuHandles.get(this.worldKey(Number(handle), worldId));
1099
1100
  }
@@ -1101,7 +1102,7 @@ export class GpuResidencyCache {
1101
1102
  /** Candidate leases retain the concrete allocation even after cache invalidation. */
1102
1103
  retainMeshResidency(
1103
1104
  handle: Handle<'MeshAsset', 'shared'>,
1104
- worldId: World | number = 0,
1105
+ worldId: RenderResourceScope | number = 0,
1105
1106
  ): MeshResidencyLease | undefined {
1106
1107
  const key = this.worldKey(Number(handle), worldId);
1107
1108
  const entry = this.meshGpuHandles.get(key);
@@ -1114,7 +1115,7 @@ export class GpuResidencyCache {
1114
1115
  }
1115
1116
 
1116
1117
  /** In-place author changes immediately miss the cache; old submissions keep their allocation. */
1117
- invalidateMesh(handle: number, worldId: World | number = 0): void {
1118
+ invalidateMesh(handle: number, worldId: RenderResourceScope | number = 0): void {
1118
1119
  const key = this.worldKey(handle, worldId);
1119
1120
  const entry = this.meshGpuHandles.get(key);
1120
1121
  if (entry !== undefined) this.retireMeshEntry(key, entry);
@@ -1137,18 +1138,18 @@ export class GpuResidencyCache {
1137
1138
  ensureResident(
1138
1139
  handle: Handle<'MeshAsset', 'shared'>,
1139
1140
  pod: TypesMeshAsset,
1140
- worldId?: World | number,
1141
+ worldId?: RenderResourceScope | number,
1141
1142
  lodMeshes?: readonly TypesMeshAsset[],
1142
1143
  ): Result<MeshGpuHandles, RhiError | AssetError | ImageError>;
1143
1144
  ensureResident(
1144
1145
  handle: Handle<'TextureAsset', 'shared'>,
1145
1146
  pod: TextureAsset,
1146
- worldId?: World | number,
1147
+ worldId?: RenderResourceScope | number,
1147
1148
  ): Result<TextureGpuEntry, RhiError | AssetError | ImageError>;
1148
1149
  ensureResident(
1149
1150
  handle: Handle<'MeshAsset', 'shared'> | Handle<'TextureAsset', 'shared'>,
1150
1151
  pod: TypesMeshAsset | TextureAsset,
1151
- worldId: World | number = 0,
1152
+ worldId: RenderResourceScope | number = 0,
1152
1153
  lodMeshes: readonly TypesMeshAsset[] = [],
1153
1154
  ): Result<TextureGpuEntry | MeshGpuHandles, RhiError | AssetError | ImageError> {
1154
1155
  const id = handleSlot(handle);
@@ -1218,17 +1219,17 @@ export class GpuResidencyCache {
1218
1219
  prepareResidentForRecovery(
1219
1220
  handle: Handle<'MeshAsset', 'shared'>,
1220
1221
  pod: TypesMeshAsset,
1221
- worldId?: World | number,
1222
+ worldId?: RenderResourceScope | number,
1222
1223
  ): Result<RecoveryResidencyPreparation, RhiError | AssetError | ImageError>;
1223
1224
  prepareResidentForRecovery(
1224
1225
  handle: Handle<'TextureAsset', 'shared'>,
1225
1226
  pod: TextureAsset,
1226
- worldId?: World | number,
1227
+ worldId?: RenderResourceScope | number,
1227
1228
  ): Result<RecoveryResidencyPreparation, RhiError | AssetError | ImageError>;
1228
1229
  prepareResidentForRecovery(
1229
1230
  handle: Handle<'MeshAsset', 'shared'> | Handle<'TextureAsset', 'shared'>,
1230
1231
  pod: TypesMeshAsset | TextureAsset,
1231
- worldId: World | number = 0,
1232
+ worldId: RenderResourceScope | number = 0,
1232
1233
  ): Result<RecoveryResidencyPreparation, RhiError | AssetError | ImageError> {
1233
1234
  const id = handleSlot(handle);
1234
1235
  if (pod.kind === 'mesh') {
@@ -1316,7 +1317,7 @@ export class GpuResidencyCache {
1316
1317
  handle: Handle<'TextureAsset', 'shared'>,
1317
1318
  pod: TextureAsset,
1318
1319
  decoded: DecodedImage,
1319
- worldId: World | number = 0,
1320
+ worldId: RenderResourceScope | number = 0,
1320
1321
  ): Promise<Result<void, AssetError | ImageError | RhiError>> {
1321
1322
  const device = this.gpuDevice;
1322
1323
  const projected = deriveRenderDataTexture(pod);
@@ -1392,7 +1393,7 @@ export class GpuResidencyCache {
1392
1393
  pod: TextureAsset,
1393
1394
  decoded: DecodedImage,
1394
1395
  renderData: TextureRenderData,
1395
- worldId: World | number = 0,
1396
+ worldId: RenderResourceScope | number = 0,
1396
1397
  ): Result<TextureGpuEntry, AssetError | ImageError | RhiError> {
1397
1398
  const device = this.gpuDevice;
1398
1399
  const prepared = this.prepareTextureUpload(handle, pod, decoded, renderData);
@@ -1654,7 +1655,7 @@ export class GpuResidencyCache {
1654
1655
  * retries by inferring "no entry => try again".
1655
1656
  */
1656
1657
  async _uploadCubemapFromEquirect(
1657
- world: World,
1658
+ world: RenderResourceScope,
1658
1659
  sourceHandle: Handle<'EquirectAsset', 'shared'>,
1659
1660
  sourcePod: EquirectAsset,
1660
1661
  ): Promise<Result<Handle<'EquirectAsset', 'shared'>, AssetError | RhiError>> {
@@ -1999,7 +2000,7 @@ export class GpuResidencyCache {
1999
2000
  view: cubeView,
2000
2001
  faceViews,
2001
2002
  });
2002
- const regResult = registerCube(world, cubeAsset);
2003
+ const regResult = registerCube(world, cubeAsset, sourceHandle);
2003
2004
  if (!regResult.ok) return recordFailed(regResult.error);
2004
2005
  const cubeHandle = regResult.value;
2005
2006
  const cubeId = handleSlot(cubeHandle);
@@ -2252,7 +2253,7 @@ export class GpuResidencyCache {
2252
2253
  handle: Handle<'MeshAsset', 'shared'>,
2253
2254
  newVertices: Float32Array,
2254
2255
  newIndices: Uint16Array,
2255
- worldId: World | number = 0,
2256
+ worldId: RenderResourceScope | number = 0,
2256
2257
  submeshes?: readonly Submesh[],
2257
2258
  ): void {
2258
2259
  const id = this.worldKey(handleSlot(handle), worldId);