@forgeax/engine-runtime 0.1.20 → 0.1.23

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 (86) hide show
  1. package/README.md +105 -3
  2. package/dist/__tests__/create-renderer-gpu-pass-timing.unit.test.d.ts +2 -0
  3. package/dist/__tests__/create-renderer-gpu-pass-timing.unit.test.d.ts.map +1 -0
  4. package/dist/__tests__/helpers/register-runtime-components.d.ts.map +1 -1
  5. package/dist/__tests__/helpers/register-scene-components.d.ts.map +1 -1
  6. package/dist/__tests__/lights-preservation.fixup.test.d.ts +2 -0
  7. package/dist/__tests__/lights-preservation.fixup.test.d.ts.map +1 -0
  8. package/dist/__tests__/render-error-exhaustive.test-d.d.ts.map +1 -1
  9. package/dist/createRenderer.d.ts.map +1 -1
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.mjs +4 -2
  12. package/dist/index.mjs.map +1 -1
  13. package/dist/sprite-param-values.d.ts +1 -1
  14. package/package.json +30 -30
  15. package/src/__tests__/active-camera.unit.test.ts +6 -8
  16. package/src/__tests__/animation-graph-asset-union.test-d.ts +2 -0
  17. package/src/__tests__/asset-brand-and-name-retirement.test.ts +4 -3
  18. package/src/__tests__/asset-registry-scene.test-d.ts +1 -0
  19. package/src/__tests__/asset-registry.recursive.spec.ts +1 -1
  20. package/src/__tests__/asset.unit.test.ts +5 -2
  21. package/src/__tests__/clamp-to-last.e2e.browser.test.ts +2 -2
  22. package/src/__tests__/collect-transient-roundtrip.test.ts +14 -3
  23. package/src/__tests__/components.test-d.ts +19 -9
  24. package/src/__tests__/components.unit.test.ts +36 -26
  25. package/src/__tests__/create-renderer-assembly.integration.test.ts +24 -0
  26. package/src/__tests__/create-renderer-gpu-pass-timing.unit.test.ts +32 -0
  27. package/src/__tests__/createRenderer.browser-no-webgpu.test.ts +66 -0
  28. package/src/__tests__/dawn/hello-sprite-nineslice-section.dawn.test.ts +9 -1
  29. package/src/__tests__/dawn/instances-shadow.dawn.test.ts +15 -2
  30. package/src/__tests__/errors.unit.test.ts +54 -20
  31. package/src/__tests__/extract-frames-merge.test.ts +51 -40
  32. package/src/__tests__/extract-skin-mismatch.unit.test.ts +6 -2
  33. package/src/__tests__/frame-targets-shadow-regression.unit.test.ts +13 -0
  34. package/src/__tests__/geometry.unit.test.ts +7 -10
  35. package/src/__tests__/helpers/register-runtime-components.ts +9 -1
  36. package/src/__tests__/helpers/register-scene-components.ts +9 -2
  37. package/src/__tests__/light-gpu-byte-neutral.unit.test.ts +10 -11
  38. package/src/__tests__/light-snapshot.test-d.ts +35 -1
  39. package/src/__tests__/lights-preservation.fixup.test.ts +125 -0
  40. package/src/__tests__/lights.unit.test.ts +223 -308
  41. package/src/__tests__/loader-name-survival.test.ts +8 -9
  42. package/src/__tests__/package-promotion.test.ts +7 -11
  43. package/src/__tests__/pbr-pipeline.unit.test.ts +22 -17
  44. package/src/__tests__/pipeline.unit.test.ts +68 -226
  45. package/src/__tests__/point-light-shadow.browser.test.ts +209 -83
  46. package/src/__tests__/point-light-shadow.unit.test.ts +24 -47
  47. package/src/__tests__/propagate-transforms.dawn.test.ts +15 -16
  48. package/src/__tests__/record-worldid-isolation.test.ts +16 -32
  49. package/src/__tests__/render-error-exhaustive.test-d.ts +73 -9
  50. package/src/__tests__/render-system-extract.test.ts +5 -5
  51. package/src/__tests__/render-system-mega.test.ts +30 -41
  52. package/src/__tests__/render-system-record-multi-material-textureview.test.ts +5 -1
  53. package/src/__tests__/render-system-record-per-submesh-transparency.test.ts +5 -1
  54. package/src/__tests__/render-system-skin-bg.unit.test.ts +14 -9
  55. package/src/__tests__/renderer-recover.unit.test.ts +8 -0
  56. package/src/__tests__/renderer-surface.unit.test.ts +229 -5
  57. package/src/__tests__/resolveName-unified-entry.test.ts +6 -6
  58. package/src/__tests__/resolveName.test.ts +14 -2
  59. package/src/__tests__/shadow-csm-atlas.test.ts +41 -0
  60. package/src/__tests__/shadow-csm-cascade-loadop.test.ts +1 -0
  61. package/src/__tests__/shadow-csm-component.test.ts +31 -24
  62. package/src/__tests__/shadow-csm-extract.test.ts +156 -47
  63. package/src/__tests__/shadow-csm-runtime-vary.dawn.test.ts +72 -0
  64. package/src/__tests__/shadow-csm-shader.dawn.test.ts +19 -0
  65. package/src/__tests__/shadow-csm-tile-consistency.test.ts +1 -0
  66. package/src/__tests__/shadow-csm-ubo.test.ts +43 -45
  67. package/src/__tests__/shadow-csm-viewport.browser.test.ts +29 -0
  68. package/src/__tests__/shadow-fields-observable.dawn.test.ts +100 -24
  69. package/src/__tests__/skin-extract-getarrayview-count.unit.test.ts +1 -1
  70. package/src/__tests__/skin-parented-double-transform.test.ts +5 -5
  71. package/src/__tests__/skinned-shadow-mixed.dawn.test.ts +4 -7
  72. package/src/__tests__/skylight-lazy-projection.test.ts +8 -5
  73. package/src/__tests__/sprite-lit-bgl-byte-identical.test.ts +11 -13
  74. package/src/__tests__/ssao-bgl.test.ts +12 -17
  75. package/src/__tests__/ssao-passes.test.ts +19 -2
  76. package/src/__tests__/systems.unit.test.ts +69 -205
  77. package/src/__tests__/template-game-default-api-contract.test.ts +45 -23
  78. package/src/__tests__/tilemap-chunk-extract.propagate-order.test.ts +10 -4
  79. package/src/__tests__/transform-hierarchy-pixel-diff.dawn.test.ts +2 -2
  80. package/src/__tests__/view-ubo-layout.browser.test.ts +20 -10
  81. package/src/collect-scene-asset.ts +1 -1
  82. package/src/components/__tests__/transform-vec-schema.test.ts +2 -2
  83. package/src/createRenderer.ts +2 -0
  84. package/src/index.ts +1 -2
  85. package/src/sprite-param-values.ts +1 -1
  86. package/dist/.tsbuildinfo +0 -1
@@ -1,5 +1,5 @@
1
1
  // template-game-default-api-contract.test - the surface APIs that the
2
- // `templates/game-default/main.ts` `instantiateScenePack` helper relies on
2
+ // `apps/game-capability-lab/main.ts` scene instantiation relies on
3
3
  // must continue to exist and compose. Catches the API drift that produced
4
4
  // `TypeError: Cannot read properties of undefined (reading
5
5
  // 'setSceneAssetResolver')` at runtime when an earlier
@@ -30,7 +30,7 @@ import { fileURLToPath } from 'node:url';
30
30
  import { AssetRegistry } from '@forgeax/engine-assets-runtime';
31
31
  import { type EntityHandle, World } from '@forgeax/engine-ecs';
32
32
  import { componentDefinition } from '@forgeax/engine-ecs/internal';
33
- import { AssetGuid } from '@forgeax/engine-pack/guid';
33
+ import { AssetGuid, PackageId } from '@forgeax/engine-pack/guid';
34
34
  import { MeshRenderer, SceneInstance } from '@forgeax/engine-render';
35
35
  // Importing the runtime components barrel populates the global component
36
36
  // table consulted by `World._buildSceneEntityComponentDatas` — without
@@ -47,7 +47,7 @@ function lid(n: number): LocalEntityId {
47
47
  return n as LocalEntityId;
48
48
  }
49
49
 
50
- // Shape this fixture mirrors the `templates/game-default/scene.pack.json`
50
+ // Shape this fixture mirrors the `apps/game-capability-lab/assets/scene.pack.json`
51
51
  // SceneAsset payload after the template's `instantiateScenePack` helper has
52
52
  // rewritten ref-int → GUID strings + lifted MeshRenderer.material → materials[].
53
53
  // We register one MaterialAsset so the SceneAsset's MeshRenderer GUID slot
@@ -83,7 +83,7 @@ function buildSceneAssetWithGuidRef(): SceneAsset {
83
83
  return { kind: 'scene', entities: nodes };
84
84
  }
85
85
 
86
- describe('templates/game-default API contract (regression for `world.sceneInstances` drift)', () => {
86
+ describe('game-capability-lab API contract (regression for `world.sceneInstances` drift)', () => {
87
87
  it('(a) assets.instantiate returns the synthetic root Entity directly (no `byRef` indirection)', async () => {
88
88
  const reg = new AssetRegistry(makeMockShaderRegistry());
89
89
  const world = new World();
@@ -208,11 +208,11 @@ describe('templates/game-default API contract (regression for `world.sceneInstan
208
208
  // doesn't pass while runtime explodes (the template is a workspace
209
209
  // sibling that the engine packages don't import — typecheck never sees
210
210
  // it).
211
- it('(c2) [regression] templates/game-default/main.ts must not call `world.sceneInstances`', () => {
211
+ it('(c2) [regression] apps/game-capability-lab/main.ts must not call `world.sceneInstances`', () => {
212
212
  const here = fileURLToPath(import.meta.url);
213
213
  // here = packages/runtime/src/__tests__/<file> → 4 levels up = repo root
214
214
  const repoRoot = resolve(here, '..', '..', '..', '..', '..');
215
- const main = readFileSync(resolve(repoRoot, 'templates', 'game-default', 'main.ts'), 'utf-8');
215
+ const main = readFileSync(resolve(repoRoot, 'apps', 'game-capability-lab', 'main.ts'), 'utf-8');
216
216
  // Strip line comments so prose explaining the rename does not trip the
217
217
  // grep gate; the gate targets actual member access only.
218
218
  const noComments = main
@@ -267,17 +267,26 @@ describe('templates/game-default API contract (regression for `world.sceneInstan
267
267
 
268
268
  const here = fileURLToPath(import.meta.url);
269
269
  const repoRoot = resolve(here, '..', '..', '..', '..', '..');
270
- const packPath = resolve(repoRoot, 'templates', 'game-default', 'assets', 'scene.pack.json');
270
+ const packPath = resolve(repoRoot, 'apps', 'game-capability-lab', 'assets', 'scene.pack.json');
271
271
  const pack = JSON.parse(readFileSync(packPath, 'utf-8')) as {
272
- assets: {
273
- kind: string;
274
- payload?: { entities?: SceneEntity[]; nodes?: SceneEntity[] };
275
- }[];
272
+ assets:
273
+ | {
274
+ kind: string;
275
+ payload?: { entities?: SceneEntity[]; nodes?: SceneEntity[] };
276
+ }[]
277
+ | Record<
278
+ string,
279
+ {
280
+ kind: string;
281
+ payload?: { entities?: SceneEntity[]; nodes?: SceneEntity[] };
282
+ }
283
+ >;
276
284
  };
285
+ const assets = Array.isArray(pack.assets) ? pack.assets : Object.values(pack.assets);
277
286
 
278
287
  const offenders: string[] = [];
279
288
  let scanned = 0;
280
- for (const asset of pack.assets) {
289
+ for (const asset of assets) {
281
290
  // The envelope `kind` is the authoritative asset discriminant; the payload
282
291
  // no longer restates it (Derive, Don't Duplicate — the loader synthesises
283
292
  // Asset.kind from the envelope on load).
@@ -317,16 +326,22 @@ describe('templates/game-default API contract (regression for `world.sceneInstan
317
326
  it('(e2) [regression] every inline template material is referenced by a MeshRenderer', () => {
318
327
  const here = fileURLToPath(import.meta.url);
319
328
  const repoRoot = resolve(here, '..', '..', '..', '..', '..');
320
- const packPath = resolve(repoRoot, 'templates', 'game-default', 'assets', 'scene.pack.json');
329
+ const packPath = resolve(repoRoot, 'apps', 'game-capability-lab', 'assets', 'scene.pack.json');
330
+ type TemplatePackAsset = {
331
+ guid?: string;
332
+ kind: string;
333
+ payload?: { entities?: { components: { MeshRenderer?: { materials?: number[] } } }[] };
334
+ refs?: string[];
335
+ };
321
336
  const pack = JSON.parse(readFileSync(packPath, 'utf-8')) as {
322
- assets: {
323
- guid: string;
324
- kind: string;
325
- payload?: { entities?: { components: { MeshRenderer?: { materials?: number[] } } }[] };
326
- refs?: string[];
327
- }[];
337
+ packageId?: string;
338
+ assets: TemplatePackAsset[] | Record<string, TemplatePackAsset>;
328
339
  };
329
- const scene = pack.assets.find((asset) => asset.kind === 'scene');
340
+ const entries = Array.isArray(pack.assets)
341
+ ? pack.assets.map((asset) => ({ asset, sourceKey: undefined }))
342
+ : Object.entries(pack.assets).map(([sourceKey, asset]) => ({ asset, sourceKey }));
343
+ const sceneEntry = entries.find(({ asset }) => asset.kind === 'scene');
344
+ const scene = sceneEntry?.asset;
330
345
  expect(scene).toBeDefined();
331
346
  if (scene === undefined) return;
332
347
 
@@ -335,9 +350,16 @@ describe('templates/game-default API contract (regression for `world.sceneInstan
335
350
  (entity.components.MeshRenderer?.materials ?? []).map((index) => scene.refs?.[index]),
336
351
  ),
337
352
  );
338
- const orphanedMaterialGuids = pack.assets
339
- .filter((asset) => asset.kind === 'material')
340
- .map((asset) => asset.guid)
353
+ const packageId = pack.packageId === undefined ? undefined : PackageId.parse(pack.packageId);
354
+ const guidFor = ({ asset, sourceKey }: (typeof entries)[number]): string | undefined => {
355
+ if (asset.guid !== undefined) return asset.guid;
356
+ if (sourceKey === undefined || packageId === undefined || !packageId.ok) return undefined;
357
+ return AssetGuid.format(AssetGuid.derive(packageId.value, sourceKey));
358
+ };
359
+ const orphanedMaterialGuids = entries
360
+ .filter(({ asset }) => asset.kind === 'material')
361
+ .map(guidFor)
362
+ .filter((guid): guid is string => guid !== undefined)
341
363
  .filter((guid) => !referencedMaterialGuids.has(guid));
342
364
 
343
365
  expect(orphanedMaterialGuids).toEqual([]);
@@ -1,7 +1,13 @@
1
1
  import { type EntityHandle, Update, World } from '@forgeax/engine-ecs';
2
2
  import { MeshFilter } from '@forgeax/engine-render';
3
3
  import { TileLayer, Tilemap } from '@forgeax/engine-render/authoring';
4
- import { ChildOf, Children, registerPropagateTransforms, Transform } from '@forgeax/engine-scene';
4
+ import {
5
+ ChildOf,
6
+ Children,
7
+ GlobalTransform,
8
+ registerPropagateTransforms,
9
+ Transform,
10
+ } from '@forgeax/engine-scene';
5
11
  import { describe, expect, it } from 'vitest';
6
12
  import { tilemapChunkExtractSystem } from '../../../render/src/tilemap-chunk-extract-system';
7
13
  import { registerRuntimeComponents } from './helpers/register-runtime-components';
@@ -65,12 +71,12 @@ describe('tilemap derivation and Transform publication order', () => {
65
71
  const children = world.get(layer, Children).unwrap();
66
72
  const derived = children.entities[0] as EntityHandle | undefined;
67
73
  expect(derived).toBeDefined();
68
- const transform = derived === undefined ? undefined : world.get(derived, Transform);
74
+ const transform = derived === undefined ? undefined : world.get(derived, GlobalTransform);
69
75
  const matrix = transform?.ok ? transform.value.world : undefined;
70
76
  expect({
71
77
  derived: matrix?.[12],
72
- layer: world.get(layer, Transform).unwrap().world[12],
73
- map: world.get(map, Transform).unwrap().world[12],
78
+ layer: world.get(layer, GlobalTransform).unwrap().world[12],
79
+ map: world.get(map, GlobalTransform).unwrap().world[12],
74
80
  entityCount: world.inspect().entityCount,
75
81
  }).toEqual({ derived: 5, layer: 4, map: 4, entityCount: expect.any(Number) });
76
82
  expect(matrix?.[13]).toBeCloseTo(1);
@@ -7,7 +7,7 @@
7
7
  // through renderer.attach, spawns a non-identity parent + a ChildOf
8
8
  // child + a static reference sphere, then proves moving the PARENT moves the
9
9
  // CHILD's rendered world position (the child gets no Transform write of its
10
- // own between frames; the only change is the parent's resolved Transform.world
10
+ // own between frames; the only change is the parent's resolved GlobalTransform.world
11
11
  // mat4 propagated down the ChildOf edge). This is the vitest-dawn-project
12
12
  // counterpart to the CI
13
13
  // smoke step so `pnpm test:dawn` exercises the visual-evidence path
@@ -154,7 +154,7 @@ describe('feat-20260531 M3 w13: AC-08 parent moves -> child follows (dawn)', ()
154
154
  if (device === undefined) throw new Error('GPUDevice not captured');
155
155
 
156
156
  // Single World wires the consume path through renderer.attach so the
157
- // child's resolved Transform.world mat4 is derived each frame. feat-20260614
157
+ // child's resolved GlobalTransform.world mat4 is derived each frame. feat-20260614
158
158
  // M8: the material is a per-World column handle minted via allocSharedRef
159
159
  // (AssetRegistry has no handle concept).
160
160
  const world = new World();
@@ -1,6 +1,7 @@
1
1
  // view-ubo-layout.browser.test.ts - feat-20260518-pbr-direct-lighting-mvp
2
- // feat-20260531-skybox-env-background M2 / w4 (TDD red): VIEW_UBO_BYTES
3
- // 112 -> 176 -> 240 layout (worldViewProj 64 B + lightDir 16 B + lightColor
2
+ // feat-20260827-directional-csm-pcss-quality M1: VIEW_UBO_BYTES remains 960
3
+ // while the View slot remains 1024 B. The prefix layout is unchanged and the
4
+ // directional shadow carrier reuses the existing tail pad.
4
5
  // 16 B + cameraPos 16 B + lightSpaceMatrix 64 B + inverseViewProj 64 B;
5
6
  // std140 vec3 padded to 16-byte boundaries).
6
7
  //
@@ -44,14 +45,16 @@ const VIEW_UBO_OFFSETS = {
44
45
  lightSpaceMatrix: 112,
45
46
  inverseViewProj: 176,
46
47
  } as const;
47
- const VIEW_UBO_BYTES = 240;
48
+ const VIEW_UBO_BYTES = 960;
49
+ const DIRECTIONAL_FILTER_OFFSET = 512;
50
+ const SPOT_LIGHT_VIEW_PROJ_OFFSET = 528;
48
51
 
49
52
  describe('w4 view UBO 240 B std140 layout (AC-06, browser)', () => {
50
- it.skipIf(!browserReady)('host write payload is exactly 240 bytes (60 floats)', () => {
51
- // The host (render-system-record.ts) builds a Float32Array(60) and
53
+ it.skipIf(!browserReady)('host write payload is exactly 960 bytes (240 floats)', () => {
54
+ // The host (view-ubo.ts) builds a Float32Array(240) and
52
55
  // emits a single queue.writeBuffer. Asserting the size lock here keeps
53
56
  // the host shape and shader ABI in agreement before the GPU sees it.
54
- const payload = new Float32Array(60);
57
+ const payload = new Float32Array(240);
55
58
  expect(payload.byteLength).toBe(VIEW_UBO_BYTES);
56
59
  });
57
60
 
@@ -63,17 +66,17 @@ describe('w4 view UBO 240 B std140 layout (AC-06, browser)', () => {
63
66
  expect(VIEW_UBO_OFFSETS.lightSpaceMatrix).toBe(112);
64
67
  expect(VIEW_UBO_OFFSETS.inverseViewProj).toBe(176);
65
68
  // Buffer total = inverseViewProj offset + mat4 (64 B) = 240.
66
- expect(VIEW_UBO_OFFSETS.inverseViewProj + 64).toBe(VIEW_UBO_BYTES);
69
+ expect(VIEW_UBO_OFFSETS.inverseViewProj + 64).toBe(240);
67
70
  });
68
71
 
69
72
  it.skipIf(!browserReady)(
70
73
  'host packs lightDir / lightColor / cameraPos at canonical std140 slots',
71
74
  () => {
72
75
  // Canonical packing order documented in plan-strategy D-4: the host
73
- // builds a 60-float buffer mirroring the shader-side `View` struct
76
+ // builds a 240-float buffer mirroring the shader-side `View` struct
74
77
  // field order. AC-06: existing field offsets unchanged -- tail append
75
78
  // only.
76
- const payload = new Float32Array(60);
79
+ const payload = new Float32Array(240);
77
80
  // worldViewProj = identity for this assertion (mat4 in [0..16))
78
81
  payload[0] = 1; // m00
79
82
  payload[5] = 1; // m11
@@ -133,7 +136,7 @@ describe('w4 view UBO 240 B std140 layout (AC-06, browser)', () => {
133
136
  // calculation.
134
137
  const inverseProjF32Index = VIEW_UBO_OFFSETS.inverseViewProj / 4;
135
138
  expect(inverseProjF32Index).toBe(44);
136
- const payload = new Float32Array(60);
139
+ const payload = new Float32Array(240);
137
140
  // Place an identity mat4 at inverseViewProj slot [44..59]
138
141
  payload[44] = 1;
139
142
  payload[49] = 1;
@@ -145,6 +148,13 @@ describe('w4 view UBO 240 B std140 layout (AC-06, browser)', () => {
145
148
  expect(payload[59]).toBe(1);
146
149
  });
147
150
 
151
+ it.skipIf(!browserReady)('shadow tail keeps the directional carrier before spot matrices', () => {
152
+ expect(DIRECTIONAL_FILTER_OFFSET).toBe(126 * 4 + 8);
153
+ expect(SPOT_LIGHT_VIEW_PROJ_OFFSET).toBe(132 * 4);
154
+ expect(SPOT_LIGHT_VIEW_PROJ_OFFSET - DIRECTIONAL_FILTER_OFFSET).toBe(16);
155
+ expect(SPOT_LIGHT_VIEW_PROJ_OFFSET + 4 * 4 * 16).toBe(VIEW_UBO_BYTES - 176);
156
+ });
157
+
148
158
  it.skipIf(!browserReady)(
149
159
  'existing field offsets unchanged by tail append (AC-06 rear-append guarantee)',
150
160
  () => {
@@ -336,7 +336,7 @@ export function rootsToSceneAsset(
336
336
  // mount.parent = P's ChildOf parent, and any ref to P resolves to the mount.
337
337
  const childOfTk0 = world.components.resolve('ChildOf');
338
338
  const childrenTk0 = world.components.resolve('Children');
339
- const carrierAllowed = new Set(['Transform', 'Children', 'ChildOf', 'Entity']);
339
+ const carrierAllowed = new Set(['Transform', 'GlobalTransform', 'Children', 'ChildOf', 'Entity']);
340
340
  const carrierForAnchor = new Map<number, number>(); // anchorRaw -> carrierRaw
341
341
  const carrierToAnchor = new Map<number, number>(); // carrierRaw -> anchorRaw
342
342
  const isMountCarrier = (p: number, anchorRaw: number): boolean => {
@@ -19,7 +19,7 @@
19
19
 
20
20
  import { World } from '@forgeax/engine-ecs';
21
21
  import { componentSchema } from '@forgeax/engine-ecs/internal';
22
- import { Transform } from '@forgeax/engine-scene';
22
+ import { GlobalTransform, Transform } from '@forgeax/engine-scene';
23
23
  import { describe, expect, it } from 'vitest';
24
24
 
25
25
  describe('w4 -- Transform vec schema (AC-05)', () => {
@@ -46,7 +46,7 @@ describe('w4 -- Transform vec schema (AC-05)', () => {
46
46
  });
47
47
 
48
48
  it('(b) world column keeps array<f32, 16> (outside the M2 migration surface)', () => {
49
- const schema = componentSchema(Transform) as Record<string, string>;
49
+ const schema = componentSchema(GlobalTransform) as Record<string, string>;
50
50
  expect(schema.world).toBe('array<f32, 16>');
51
51
  });
52
52
  });
@@ -39,6 +39,8 @@ export async function createRenderer(
39
39
  ...(options.standardProfile === undefined
40
40
  ? {}
41
41
  : { standardProfile: options.standardProfile }),
42
+ ...(options.gpuPassTiming === undefined ? {} : { gpuPassTiming: options.gpuPassTiming }),
43
+ ...(options.ssrIdentity === undefined ? {} : { ssrIdentity: options.ssrIdentity }),
42
44
  };
43
45
  try {
44
46
  const constructed = await constructRuntimeRendererHost(canvas, rendererOptions, bundler);
package/src/index.ts CHANGED
@@ -340,8 +340,7 @@ export {
340
340
  // never rendered). One barrel entry => one module copy => one registry shared by
341
341
  // createDebugDrawOnReady (the writer) and attachDebugOverlayPass (the reader).
342
342
  // feat-20260704-runtime-tier1-decomposition M2 / w10 (D-3): the third error
343
- // re-export block (HdrpCapsInsufficientError / HdrpIndexListOverflowError /
344
- // HdrpLightBudgetExceededError) is folded into the render-cluster class
343
+ // re-export block (Standard Cluster error classes) is folded into the render-cluster class
345
344
  // re-export block above -- they are render-cluster members sourced from
346
345
  // ./errors/render.
347
346
  // feat-20260608-cluster-lighting M2 / w10 + verify F-1/F-2: HDRP cluster-forward
@@ -31,7 +31,7 @@
31
31
  * replaces this asset-side region.
32
32
  * - `pivotAndSize`: vec4 `[pivotX, pivotY, sizeX, sizeY]` — `.xy` is the
33
33
  * pivot (0..1 normalised); `.zw` is the DEAD SLOT post-w11 (D-6 unit-
34
- * quad: world scale flows entirely through Transform.world, no longer
34
+ * quad: world scale flows entirely through GlobalTransform.world, no longer
35
35
  * double-applied via the UBO). Default `[0.5, 0.5, 1, 1]`.
36
36
  * - `slicesAndMode`: vec4 `[left, top, right, bottom-signed]` — 9-slice
37
37
  * border widths in UV (0..1) on the source texture region. Sum of