@forgeax/engine-assets-runtime 0.1.7 → 0.1.19

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.
@@ -1,4 +1,4 @@
1
- import { type Result, RhiError } from '@forgeax/engine-rhi';
1
+ import { type Result, RhiError, type RhiRenderPassEncoder } from '@forgeax/engine-rhi';
2
2
  /**
3
3
  * Subset of `RhiDevice` consumed by the mipmap utility (charter F2: only
4
4
  * the methods the algorithm actually invokes; the dawn integration tests
@@ -129,4 +129,6 @@ export declare function blitMipmapsSync(device: MipmapBlitDevice, texture: any,
129
129
  readonly height: number;
130
130
  readonly levels?: number;
131
131
  }): Result<void, RhiError>;
132
+ /** Encode one prewarmed mip level into a caller-owned graph render pass. */
133
+ export declare function encodeMipmapLevel(device: MipmapBlitDevice, pass: RhiRenderPassEncoder, sourceView: unknown, format: GPUTextureFormat): Result<void, RhiError>;
132
134
  //# sourceMappingURL=mipmap-generator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mipmap-generator.d.ts","sourceRoot":"","sources":["../src/mipmap-generator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mipmap-generator.d.ts","sourceRoot":"","sources":["../src/mipmap-generator.ts"],"names":[],"mappings":"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"}
@@ -12,6 +12,8 @@ import { type ScenePublicationFence, type ScenePublicationFenceError } from './s
12
12
  export interface SkinJointResolver {
13
13
  resolveSkinAsset(skeletonHandleRaw: number): SkinAsset | undefined;
14
14
  }
15
+ /** Derive a publication fence from the registry's unified live projections. */
16
+ export declare function scenePublicationFenceFromRegistry(registry: AssetRegistry, outputGuid: string): Result<ScenePublicationFence, ScenePublicationFenceError>;
15
17
  /**
16
18
  * Post-spawn hook contract (D-1). A hook runs after instantiate spawns the
17
19
  * scene subtree; the shipped implementation is runtime's `postSpawnResolveJoints`
@@ -1 +1 @@
1
- {"version":3,"file":"instantiate.d.ts","sourceRoot":"","sources":["../../src/registry/instantiate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"instantiate.d.ts","sourceRoot":"","sources":["../../src/registry/instantiate.ts"],"names":[],"mappings":"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"}
@@ -15,6 +15,7 @@ import type { AssetRegistry } from '../asset-registry';
15
15
  * @returns AssetError on failure, null on success
16
16
  */
17
17
  export declare function validateMaterialPasses(registry: AssetRegistry, asset: MaterialAsset): AssetError | null;
18
+ export declare function validateMaterialTransmissionContract(asset: MaterialAsset): MaterialError | null;
18
19
  export interface MaterialEffectiveRootIdentity {
19
20
  readonly guid: string;
20
21
  readonly layoutIdentity: string;
@@ -1 +1 @@
1
- {"version":3,"file":"validate-material.d.ts","sourceRoot":"","sources":["../../src/registry/validate-material.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EACL,UAAU,EAEV,KAAK,aAAa,EAClB,KAAK,aAAa,EAEnB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,aAAa,EACvB,KAAK,EAAE,aAAa,GACnB,UAAU,GAAG,IAAI,CAoFnB;AAED,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,MAAM,GAAG,SAAS,CAAC,EACtD,IAAI,EAAE,6BAA6B,GAClC,aAAa,GAAG,IAAI,CAiBtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,aAAa,EACxB,KAAK,EAAE,aAAa,GACnB,UAAU,GAAG,IAAI,CA8GnB;AAED,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,aAAa,EACxB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,OAAO,GACb,OAAO,CAgCT;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,aAAa,EAAE,KAAK,EAAE,aAAa,GAAG,IAAI,CA8BhG;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,+BAA+B,CAC7C,QAAQ,EAAE,aAAa,EACvB,QAAQ,EAAE,MAAM,GACf,WAAW,CAAC,MAAM,CAAC,GAAG,SAAS,CAIjC"}
1
+ {"version":3,"file":"validate-material.d.ts","sourceRoot":"","sources":["../../src/registry/validate-material.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EACL,UAAU,EAEV,KAAK,aAAa,EAClB,KAAK,aAAa,EAEnB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,aAAa,EACvB,KAAK,EAAE,aAAa,GACnB,UAAU,GAAG,IAAI,CAoFnB;AA4CD,wBAAgB,oCAAoC,CAAC,KAAK,EAAE,aAAa,GAAG,aAAa,GAAG,IAAI,CAkD/F;AAED,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,MAAM,GAAG,SAAS,CAAC,EACtD,IAAI,EAAE,6BAA6B,GAClC,aAAa,GAAG,IAAI,CAiBtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,aAAa,EACxB,KAAK,EAAE,aAAa,GACnB,UAAU,GAAG,IAAI,CA8GnB;AAED,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,aAAa,EACxB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,OAAO,GACb,OAAO,CAgCT;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,aAAa,EAAE,KAAK,EAAE,aAAa,GAAG,IAAI,CA8BhG;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,+BAA+B,CAC7C,QAAQ,EAAE,aAAa,EACvB,QAAQ,EAAE,MAAM,GACf,WAAW,CAAC,MAAM,CAAC,GAAG,SAAS,CAIjC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgeax/engine-assets-runtime",
3
- "version": "0.1.7",
3
+ "version": "0.1.19",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -26,22 +26,22 @@
26
26
  "LICENSE"
27
27
  ],
28
28
  "dependencies": {
29
- "@forgeax/engine-codec": "0.1.7",
30
- "@forgeax/engine-ecs": "0.1.7",
31
- "@forgeax/engine-geometry": "0.1.7",
32
- "@forgeax/engine-graphics-extras": "0.1.7",
33
- "@forgeax/engine-image": "0.1.7",
34
- "@forgeax/engine-pack": "0.1.7",
35
- "@forgeax/engine-plugin": "0.1.7",
36
- "@forgeax/engine-rhi": "0.1.7",
37
- "@forgeax/engine-scene": "0.1.7",
38
- "@forgeax/engine-shader": "0.1.7",
39
- "@forgeax/engine-types": "0.1.7",
40
- "@forgeax/engine-ui": "0.1.7",
29
+ "@forgeax/engine-codec": "0.1.19",
30
+ "@forgeax/engine-ecs": "0.1.19",
31
+ "@forgeax/engine-geometry": "0.1.19",
32
+ "@forgeax/engine-graphics-extras": "0.1.19",
33
+ "@forgeax/engine-image": "0.1.19",
34
+ "@forgeax/engine-pack": "0.1.19",
35
+ "@forgeax/engine-plugin": "0.1.19",
36
+ "@forgeax/engine-rhi": "0.1.19",
37
+ "@forgeax/engine-scene": "0.1.19",
38
+ "@forgeax/engine-shader": "0.1.19",
39
+ "@forgeax/engine-types": "0.1.19",
40
+ "@forgeax/engine-ui": "0.1.19",
41
41
  "@webgpu/types": "^0.1.71"
42
42
  },
43
43
  "devDependencies": {
44
- "@forgeax/engine-skinning": "0.1.7",
44
+ "@forgeax/engine-skinning": "0.1.19",
45
45
  "@webgpu/types": "^0.1.71"
46
46
  },
47
47
  "forgeax": {
@@ -193,4 +193,40 @@ describe('CatalogReplica', () => {
193
193
  diagnostics: [{ code: 'catalog-revision-conflict', severity: 'blocking' }],
194
194
  });
195
195
  });
196
+
197
+ it('applies a failed lifecycle projection when the producer advances its observation revision', async () => {
198
+ const fixture = source();
199
+ const replica = new CatalogReplica(fixture.source as never);
200
+ await replica.start();
201
+
202
+ fixture.emit({
203
+ added: [],
204
+ changed: [
205
+ {
206
+ ...base,
207
+ lifecycle: 'failed',
208
+ revision: {
209
+ digest: 'failure:source-package-ddc-failed:sha256:old',
210
+ observedAt: 2,
211
+ rootId: base.revision.rootId,
212
+ },
213
+ diagnostics: [
214
+ {
215
+ code: 'source-package-ddc-failed',
216
+ severity: 'blocking',
217
+ hint: 'repair and retry',
218
+ },
219
+ ],
220
+ },
221
+ ],
222
+ removed: [],
223
+ });
224
+
225
+ expect(replica.snapshot().entries[0]).toMatchObject({
226
+ guid: base.guid,
227
+ lifecycle: 'failed',
228
+ revision: { observedAt: 2 },
229
+ });
230
+ expect(replica.snapshot().stale).toBe(false);
231
+ });
196
232
  });
@@ -279,6 +279,43 @@ describe('materialLoader', () => {
279
279
  });
280
280
  });
281
281
 
282
+ it('resolves transmission and thickness texture refs through authored contract', () => {
283
+ const out = materialLoader.load(
284
+ {
285
+ passes: [{ name: 'Forward', program: { module: 'forgeax::default-standard-pbr' } }],
286
+ parameters: [
287
+ { name: 'transmissionTexture', type: 'texture' },
288
+ { name: 'thicknessTexture', type: 'texture' },
289
+ ],
290
+ values: {
291
+ transmission: 0.8,
292
+ ior: 1.45,
293
+ thickness: 0.25,
294
+ transmissionTexture: { texture: 0, sampler: 1, coordinates: { set: 2 } },
295
+ thicknessTexture: { texture: 2, sampler: 3, coordinates: { set: 3 } },
296
+ },
297
+ },
298
+ ['texture-transmission', 'sampler-linear', 'texture-thickness', 'sampler-nearest'],
299
+ emptyCtx,
300
+ ) as MaterialAsset;
301
+
302
+ expect(out.values).toMatchObject({
303
+ transmission: 0.8,
304
+ ior: 1.45,
305
+ thickness: 0.25,
306
+ transmissionTexture: {
307
+ texture: 'texture-transmission',
308
+ sampler: 'sampler-linear',
309
+ coordinates: { set: 2 },
310
+ },
311
+ thicknessTexture: {
312
+ texture: 'texture-thickness',
313
+ sampler: 'sampler-nearest',
314
+ coordinates: { set: 3 },
315
+ },
316
+ });
317
+ });
318
+
282
319
  it('resolves nested texture values when the shader schema is present but empty', () => {
283
320
  const out = materialLoader.load(
284
321
  {
@@ -6,7 +6,7 @@ import {
6
6
  } from '@forgeax/engine-types';
7
7
  import { describe, expect, it } from 'vitest';
8
8
  import type { AssetRegistry } from '../asset-registry';
9
- import { resolveMountsRec } from '../registry/instantiate';
9
+ import { resolveMountsRec, scenePublicationFenceFromRegistry } from '../registry/instantiate';
10
10
  import { createScenePublicationFence } from '../registry/scene-publication-fence';
11
11
 
12
12
  const SCENE_GUID = '019ffdb4-1000-7000-8000-000000000008';
@@ -155,4 +155,62 @@ describe('resolveMountsRec publication fence identity', () => {
155
155
  );
156
156
  expect(result).toMatchObject({ ok: true, value: [{ source: 6 }] });
157
157
  });
158
+
159
+ it('prefers the complete pack-index tuple during a CatalogReplica handoff', () => {
160
+ const world = new World();
161
+ const currentSourcePath = 'assets/current.glb';
162
+ const currentPublication = {
163
+ ...publication(),
164
+ sourcePath: currentSourcePath,
165
+ receipt: { ...publication().receipt, sourcePath: currentSourcePath },
166
+ };
167
+ const fence = createScenePublicationFence(currentPublication);
168
+ expect(fence.ok).toBe(true);
169
+ if (!fence.ok) return;
170
+ const registry = {
171
+ assetCatalog: new Map(),
172
+ // The replica may still expose the prior complete tuple while the
173
+ // latest pack-index has already been hydrated after a source write.
174
+ catalogSnapshot: () => ({
175
+ entries: [
176
+ {
177
+ guid: SCENE_GUID,
178
+ kind: 'scene',
179
+ sourcePath: publication().sourcePath,
180
+ packageUrl: '/assets/scene.pack.json',
181
+ publication: publication(),
182
+ },
183
+ ],
184
+ }),
185
+ packIndexCache: new Map([
186
+ [
187
+ SCENE_GUID,
188
+ {
189
+ guid: SCENE_GUID,
190
+ kind: 'scene',
191
+ sourcePath: currentSourcePath,
192
+ packageUrl: '/assets/current.pack.json',
193
+ publication: currentPublication,
194
+ },
195
+ ],
196
+ ]),
197
+ _guidForAsset: () => undefined,
198
+ } as unknown as AssetRegistry;
199
+ const result = resolveMountsRec(
200
+ registry,
201
+ [
202
+ {
203
+ localId: 0 as never,
204
+ source: 6,
205
+ memberFirst: 0 as never,
206
+ memberCount: 0,
207
+ publicationFence: fence.value,
208
+ },
209
+ ],
210
+ world,
211
+ new Set(),
212
+ );
213
+ expect(result).toMatchObject({ ok: true, value: [{ source: 6 }] });
214
+ expect(scenePublicationFenceFromRegistry(registry, SCENE_GUID)).toEqual(fence);
215
+ });
158
216
  });
@@ -5,6 +5,7 @@ import {
5
5
  } from '@forgeax/engine-pack';
6
6
  import { AssetGuid } from '@forgeax/engine-pack/guid';
7
7
  import { ShaderRegistry } from '@forgeax/engine-shader';
8
+ import type { MaterialParameter } from '@forgeax/engine-types';
8
9
  import { describe, expect, it, vi } from 'vitest';
9
10
  import { AssetRegistry } from '../asset-registry';
10
11
  import { MaterialGenerationCache } from '../material/generation-cache';
@@ -103,6 +104,70 @@ describe('material runtime readiness', () => {
103
104
  ]);
104
105
  });
105
106
 
107
+ it('keeps the Standard transmission contract in the published snapshot', async () => {
108
+ const parameters: readonly MaterialParameter[] = [
109
+ { name: 'transmission', type: 'f32', default: 0 },
110
+ { name: 'ior', type: 'f32', default: 1.5 },
111
+ { name: 'thickness', type: 'f32', default: 0 },
112
+ { name: 'attenuationColor', type: 'vec3', default: [1, 1, 1] },
113
+ { name: 'attenuationDistance', type: 'f32' },
114
+ { name: 'transmissionTexture', type: 'texture' },
115
+ { name: 'thicknessTexture', type: 'texture' },
116
+ ];
117
+ expect(materialParametersToParamSchema(parameters, 'published::standard')).toEqual([
118
+ { name: 'transmission', type: 'f32', default: 0 },
119
+ { name: 'ior', type: 'f32', default: 1.5 },
120
+ { name: 'thickness', type: 'f32', default: 0 },
121
+ { name: 'attenuationColor', type: 'vec3', default: [1, 1, 1] },
122
+ { name: 'attenuationDistance', type: 'f32' },
123
+ { name: 'transmissionTexture', type: 'texture2d' },
124
+ { name: 'thicknessTexture', type: 'texture2d' },
125
+ ]);
126
+ const baseRecord = cookedRecord(PRODUCTION_GUID);
127
+ const record: CookedMaterialRecord = {
128
+ ...baseRecord,
129
+ parameterContract: {
130
+ parameters,
131
+ values: {
132
+ transmission: 0.8,
133
+ ior: 1.45,
134
+ thickness: 0.25,
135
+ attenuationColor: [0.9, 0.95, 1],
136
+ attenuationDistance: 4,
137
+ transmissionTexture: { texture: 'texture/transmission' },
138
+ thicknessTexture: { texture: 'texture/thickness' },
139
+ },
140
+ },
141
+ resolved: {
142
+ ...baseRecord.resolved,
143
+ parameters,
144
+ values: {
145
+ transmission: 0.8,
146
+ ior: 1.45,
147
+ thickness: 0.25,
148
+ attenuationColor: [0.9, 0.95, 1],
149
+ attenuationDistance: 4,
150
+ transmissionTexture: { texture: 'texture/transmission' },
151
+ thicknessTexture: { texture: 'texture/thickness' },
152
+ },
153
+ },
154
+ };
155
+ const loader = createMaterialLoader({
156
+ loadPublication: async () => ({ ...publication(), guid: PRODUCTION_GUID, record }),
157
+ loadReference: async () => true,
158
+ });
159
+
160
+ const result = await loader.load({
161
+ guid: PRODUCTION_GUID,
162
+ specializationKey: 'material-specialization/7/ready',
163
+ });
164
+
165
+ expect(result.status).toBe('Ready');
166
+ if (result.status !== 'Ready') return;
167
+ expect(result.parameterContract).toEqual(record.parameterContract);
168
+ expect(inspectMaterialRuntime(result).parameterContract).toEqual(record.parameterContract);
169
+ });
170
+
106
171
  it('loads only a cooked record with its artifact and complete references', async () => {
107
172
  const loader = createLoader(async () => publication());
108
173
 
@@ -18,6 +18,7 @@ import {
18
18
  materialShaderTextureFieldNames,
19
19
  validateMaterialCookIdentity,
20
20
  validateMaterialPasses,
21
+ validateMaterialTransmissionContract,
21
22
  validateParamType,
22
23
  validateSpriteSlices,
23
24
  } from '../registry/validate-material';
@@ -180,6 +181,50 @@ describe('validateMaterialPasses', () => {
180
181
  });
181
182
  });
182
183
 
184
+ describe('validateMaterialTransmissionContract', () => {
185
+ it.each([
186
+ ['transmission range', { transmission: 1.1 }],
187
+ ['ior range', { ior: 0 }],
188
+ ['thickness range', { thickness: -0.1 }],
189
+ ['attenuation distance', { attenuationDistance: Number.POSITIVE_INFINITY }],
190
+ ])('returns a structured error for %s', (_name, value) => {
191
+ const error = validateMaterialTransmissionContract(mat({ values: value }));
192
+ expect(error?.code).toBe('material-transmission-contract-invalid');
193
+ expect(error?.detail).toMatchObject({ reason: expect.any(String) });
194
+ });
195
+
196
+ it('rejects BLEND and depth writes when transmission is active', () => {
197
+ expect(
198
+ validateMaterialTransmissionContract(
199
+ mat({
200
+ values: { transmission: 0.5 },
201
+ passes: [
202
+ {
203
+ name: 'forward',
204
+ program: { module: 'forgeax_material::standard' },
205
+ renderState: { blend: {} },
206
+ },
207
+ ],
208
+ }),
209
+ )?.detail,
210
+ ).toMatchObject({ reason: 'blend' });
211
+ expect(
212
+ validateMaterialTransmissionContract(
213
+ mat({
214
+ values: { transmission: 0.5 },
215
+ passes: [
216
+ {
217
+ name: 'forward',
218
+ program: { module: 'forgeax_material::standard' },
219
+ renderState: { depthWriteEnabled: true },
220
+ },
221
+ ],
222
+ }),
223
+ )?.detail,
224
+ ).toMatchObject({ reason: 'depth-write' });
225
+ });
226
+ });
227
+
183
228
  describe('validateParamType', () => {
184
229
  const reg = makeRegistry({});
185
230
  it('numeric scalars', () => {
package/src/index.ts CHANGED
@@ -143,6 +143,7 @@ export {
143
143
  // ─── Mipmap generation helpers ──────────────────────────────────────────────
144
144
  export {
145
145
  blitMipmapsSync,
146
+ encodeMipmapLevel,
146
147
  getOrCreateMipmapPipeline,
147
148
  type MipmapBlitDevice,
148
149
  type MipmapShaderModuleFactory,
@@ -157,9 +158,13 @@ export {
157
158
  type RuntimeEvidenceSource,
158
159
  } from './registry/asset-evidence';
159
160
  export { CatalogReplica, type CatalogReplicaSnapshot } from './registry/catalog-state';
160
- export type { PostSpawnHook, SkinJointResolver } from './registry/instantiate';
161
161
  // ─── Scene instantiate collaboration contract types (D-1 injected hook) ─────
162
- export { buildSceneChildContext } from './registry/instantiate';
162
+ export {
163
+ buildSceneChildContext,
164
+ type PostSpawnHook,
165
+ type SkinJointResolver,
166
+ scenePublicationFenceFromRegistry,
167
+ } from './registry/instantiate';
163
168
  export { loadMaterialReadyByGuid } from './registry/load-by-guid';
164
169
  export {
165
170
  compareScenePublicationFences,
@@ -45,7 +45,7 @@
45
45
  // - `numMipLevels` is pure (no device argument); the WeakMap lookup only
46
46
  // happens at pipeline-create time.
47
47
 
48
- import { err, ok, type Result, RhiError } from '@forgeax/engine-rhi';
48
+ import { err, ok, type Result, RhiError, type RhiRenderPassEncoder } from '@forgeax/engine-rhi';
49
49
 
50
50
  /**
51
51
  * Subset of `RhiDevice` consumed by the mipmap utility (charter F2: only
@@ -448,3 +448,45 @@ export function blitMipmapsSync(
448
448
 
449
449
  return recordMipmapBlit<RhiError>(device, texture, levels, pipeline, cache);
450
450
  }
451
+
452
+ /** Encode one prewarmed mip level into a caller-owned graph render pass. */
453
+ export function encodeMipmapLevel(
454
+ device: MipmapBlitDevice,
455
+ pass: RhiRenderPassEncoder,
456
+ sourceView: unknown,
457
+ format: GPUTextureFormat,
458
+ ): Result<void, RhiError> {
459
+ const cache = deviceCache.get(device);
460
+ if (cache === undefined) {
461
+ return err(
462
+ new RhiError({
463
+ code: 'rhi-not-available',
464
+ expected: 'mipmap pipeline cache prewarmed for the graph device before encoding',
465
+ hint: 'call prewarmMipmapPipeline(device, formats) at renderer.ready',
466
+ }),
467
+ );
468
+ }
469
+ const pipeline = cache.pipelines.get(format);
470
+ if (pipeline === undefined) {
471
+ return err(
472
+ new RhiError({
473
+ code: 'rhi-not-available',
474
+ expected: `mipmap pipeline for format ${format} prewarmed`,
475
+ hint: `format ${format} was not prewarmed; add it to the renderer prewarm list`,
476
+ }),
477
+ );
478
+ }
479
+ const bindGroup = device.createBindGroup({
480
+ label: 'mipmap-graph-bind-group',
481
+ layout: cache.layout,
482
+ entries: [
483
+ { binding: 0, resource: { kind: 'sampler', value: cache.sampler } },
484
+ { binding: 1, resource: { kind: 'textureView', value: sourceView } },
485
+ ],
486
+ });
487
+ if (!bindGroup.ok) return bindGroup;
488
+ pass.setPipeline(pipeline);
489
+ pass.setBindGroup(0, bindGroup.value);
490
+ pass.draw(3, 1, 0, 0);
491
+ return ok(undefined);
492
+ }
@@ -78,16 +78,26 @@ function catalogEntriesForFence(registry: AssetRegistry): readonly CatalogEntry[
78
78
  if (record.sourcePath === undefined) continue;
79
79
  const candidate = { ...record, guid, sourcePath: record.sourcePath } as CatalogEntry;
80
80
  const current = byGuid.get(guid.toLowerCase());
81
- if (
82
- current === undefined ||
83
- (current.publication === undefined && candidate.publication !== undefined)
84
- ) {
81
+ if (current === undefined || candidate.publication !== undefined) {
82
+ // A persisted Scene fence is captured from the complete pack-index
83
+ // publication at save time. If the CatalogReplica is still carrying its
84
+ // previous tuple during the watcher handoff, keep the same pack-index
85
+ // authority here or the freshly reopened scene will reject its own
86
+ // mount with asset-generation-fence-mismatch.
85
87
  byGuid.set(guid.toLowerCase(), candidate);
86
88
  }
87
89
  }
88
90
  return [...byGuid.values()];
89
91
  }
90
92
 
93
+ /** Derive a publication fence from the registry's unified live projections. */
94
+ export function scenePublicationFenceFromRegistry(
95
+ registry: AssetRegistry,
96
+ outputGuid: string,
97
+ ): Result<ScenePublicationFence, ScenePublicationFenceError> {
98
+ return scenePublicationFenceFromCatalog(catalogEntriesForFence(registry), outputGuid);
99
+ }
100
+
91
101
  /**
92
102
  * Post-spawn hook contract (D-1). A hook runs after instantiate spawns the
93
103
  * scene subtree; the shipped implementation is runtime's `postSpawnResolveJoints`
@@ -112,6 +112,100 @@ export function validateMaterialPasses(
112
112
  return null;
113
113
  }
114
114
 
115
+ type TransmissionParameter =
116
+ | 'transmission'
117
+ | 'ior'
118
+ | 'thickness'
119
+ | 'attenuationColor'
120
+ | 'attenuationDistance'
121
+ | 'transmissionTexture'
122
+ | 'thicknessTexture';
123
+
124
+ function transmissionError(
125
+ material: string,
126
+ parameter: TransmissionParameter,
127
+ reason: 'non-finite' | 'range' | 'shape' | 'blend' | 'depth-write',
128
+ actual?: unknown,
129
+ ): MaterialError {
130
+ return createMaterialError('material-transmission-contract-invalid', {
131
+ code: 'material-transmission-contract-invalid',
132
+ material,
133
+ parameter,
134
+ reason,
135
+ ...(actual === undefined ? {} : { actual }),
136
+ });
137
+ }
138
+
139
+ function validateTransmissionNumber(
140
+ material: string,
141
+ values: Record<string, unknown>,
142
+ parameter: 'transmission' | 'ior' | 'thickness' | 'attenuationDistance',
143
+ minimum: number,
144
+ maximum?: number,
145
+ ): MaterialError | null {
146
+ const value = values[parameter];
147
+ if (value === undefined) return null;
148
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
149
+ return transmissionError(material, parameter, 'non-finite', value);
150
+ }
151
+ if (value < minimum || (maximum !== undefined && value > maximum)) {
152
+ return transmissionError(material, parameter, 'range', value);
153
+ }
154
+ return null;
155
+ }
156
+
157
+ export function validateMaterialTransmissionContract(asset: MaterialAsset): MaterialError | null {
158
+ const material = (asset.values?.materialName as string | undefined) ?? '<unnamed>';
159
+ const values = (asset.values ?? {}) as Record<string, unknown>;
160
+ const numericChecks: ReadonlyArray<
161
+ ['transmission' | 'ior' | 'thickness' | 'attenuationDistance', number, number | undefined]
162
+ > = [
163
+ ['transmission', 0, 1],
164
+ ['ior', 1, undefined],
165
+ ['thickness', 0, undefined],
166
+ ['attenuationDistance', Number.MIN_VALUE, undefined],
167
+ ];
168
+ for (const [parameter, minimum, maximum] of numericChecks) {
169
+ const error = validateTransmissionNumber(material, values, parameter, minimum, maximum);
170
+ if (error !== null) return error;
171
+ }
172
+ const attenuationColor = values.attenuationColor;
173
+ if (attenuationColor !== undefined) {
174
+ if (
175
+ !Array.isArray(attenuationColor) ||
176
+ attenuationColor.length !== 3 ||
177
+ attenuationColor.some(
178
+ (value) => typeof value !== 'number' || !Number.isFinite(value) || value < 0 || value > 1,
179
+ )
180
+ ) {
181
+ return transmissionError(material, 'attenuationColor', 'shape', attenuationColor);
182
+ }
183
+ }
184
+ for (const parameter of ['transmissionTexture', 'thicknessTexture'] as const) {
185
+ const value = values[parameter];
186
+ if (
187
+ value !== undefined &&
188
+ typeof value !== 'string' &&
189
+ !(typeof value === 'number' && Number.isInteger(value) && value >= 0) &&
190
+ (typeof value !== 'object' || value === null || Array.isArray(value))
191
+ ) {
192
+ return transmissionError(material, parameter, 'shape', value);
193
+ }
194
+ }
195
+ const transmission = values.transmission;
196
+ if (typeof transmission === 'number' && transmission > 0) {
197
+ for (const pass of asset.passes ?? []) {
198
+ if (pass.renderState?.blend !== undefined) {
199
+ return transmissionError(material, 'transmission', 'blend');
200
+ }
201
+ if (pass.renderState?.depthWriteEnabled === true) {
202
+ return transmissionError(material, 'transmission', 'depth-write');
203
+ }
204
+ }
205
+ }
206
+ return null;
207
+ }
208
+
115
209
  export interface MaterialEffectiveRootIdentity {
116
210
  readonly guid: string;
117
211
  readonly layoutIdentity: string;