@forgeax/engine-runtime 0.1.19 → 0.1.21

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 (91) hide show
  1. package/README.md +102 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/__tests__/create-renderer-gpu-pass-timing.unit.test.d.ts +2 -0
  4. package/dist/__tests__/create-renderer-gpu-pass-timing.unit.test.d.ts.map +1 -0
  5. package/dist/__tests__/helpers/tilemap-assets.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__/material-texture-dimension-mismatch.unit.test.d.ts +2 -0
  9. package/dist/__tests__/material-texture-dimension-mismatch.unit.test.d.ts.map +1 -0
  10. package/dist/__tests__/render-error-exhaustive.test-d.d.ts.map +1 -1
  11. package/dist/__tests__/texture-dimensions.browser.test.d.ts +2 -0
  12. package/dist/__tests__/texture-dimensions.browser.test.d.ts.map +1 -0
  13. package/dist/__tests__/volumetric-fog-record-recovery.integration.test.d.ts +2 -0
  14. package/dist/__tests__/volumetric-fog-record-recovery.integration.test.d.ts.map +1 -0
  15. package/dist/createRenderer.d.ts.map +1 -1
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.mjs +3 -1
  18. package/dist/index.mjs.map +1 -1
  19. package/package.json +30 -30
  20. package/src/__tests__/active-camera.unit.test.ts +6 -8
  21. package/src/__tests__/asset-registry.recursive.spec.ts +13 -19
  22. package/src/__tests__/asset.unit.test.ts +28 -25
  23. package/src/__tests__/basis-catalog-dispatch.integration.test.ts +10 -9
  24. package/src/__tests__/clamp-to-last.e2e.browser.test.ts +2 -2
  25. package/src/__tests__/components.test-d.ts +10 -3
  26. package/src/__tests__/components.unit.test.ts +13 -7
  27. package/src/__tests__/compressed-texture-physical-extent.integration.test.ts +12 -10
  28. package/src/__tests__/create-renderer-assembly.integration.test.ts +24 -0
  29. package/src/__tests__/create-renderer-gpu-pass-timing.unit.test.ts +32 -0
  30. package/src/__tests__/createRenderer.browser-no-webgpu.test.ts +66 -0
  31. package/src/__tests__/dawn/hello-sprite-nineslice-section.dawn.test.ts +11 -4
  32. package/src/__tests__/dawn/instances-shadow.dawn.test.ts +15 -2
  33. package/src/__tests__/dawn/material-alpha.dawn.test.ts +2 -3
  34. package/src/__tests__/dawn/mipmap-srgb-linear.dawn.test.ts +2 -3
  35. package/src/__tests__/dawn/per-entity-material-texture-isolation.dawn.test.ts +2 -3
  36. package/src/__tests__/dawn/sprite-nineslice-mesh-bind.dawn.test.ts +2 -3
  37. package/src/__tests__/dawn/upload-texture.dawn.test.ts +4 -6
  38. package/src/__tests__/derive-bgl-integration.test.ts +11 -0
  39. package/src/__tests__/equirect-bc6h.integration.test.ts +6 -4
  40. package/src/__tests__/errors.unit.test.ts +71 -20
  41. package/src/__tests__/extract-frames-merge.test.ts +48 -40
  42. package/src/__tests__/extract-skin-mismatch.unit.test.ts +6 -2
  43. package/src/__tests__/four-path-compression.integration.test.ts +2 -2
  44. package/src/__tests__/frame-targets-shadow-regression.unit.test.ts +13 -0
  45. package/src/__tests__/geometry.unit.test.ts +7 -10
  46. package/src/__tests__/gpu-resource-cow-survivor.test.ts +2 -3
  47. package/src/__tests__/helpers/tilemap-assets.ts +3 -4
  48. package/src/__tests__/light-gpu-byte-neutral.unit.test.ts +6 -3
  49. package/src/__tests__/light-snapshot.test-d.ts +8 -0
  50. package/src/__tests__/lights-preservation.fixup.test.ts +127 -0
  51. package/src/__tests__/lights.unit.test.ts +75 -2214
  52. package/src/__tests__/loadbyguid-texture-intern.unit.test.ts +3 -4
  53. package/src/__tests__/loader-name-survival.test.ts +2 -3
  54. package/src/__tests__/material-texture-dimension-mismatch.unit.test.ts +18 -0
  55. package/src/__tests__/material-texture-uv-scale.unit.test.ts +10 -2
  56. package/src/__tests__/mip-gate.unit.test.ts +11 -8
  57. package/src/__tests__/msaa-sprite-pixel-diff.dawn.test.ts +2 -3
  58. package/src/__tests__/new-kind-refs-contract.test.ts +2 -3
  59. package/src/__tests__/pbr-pipeline.unit.test.ts +29 -5
  60. package/src/__tests__/pipeline.unit.test.ts +66 -221
  61. package/src/__tests__/point-light-shadow.browser.test.ts +208 -83
  62. package/src/__tests__/point-light-shadow.unit.test.ts +67 -9
  63. package/src/__tests__/render-error-exhaustive.test-d.ts +92 -9
  64. package/src/__tests__/render-system-extract.test.ts +4 -6
  65. package/src/__tests__/render-system-mega.test.ts +30 -41
  66. package/src/__tests__/render-system-record-multi-material-textureview.test.ts +8 -7
  67. package/src/__tests__/render-system-record-per-submesh-transparency.test.ts +6 -4
  68. package/src/__tests__/render-system-skin-bg.unit.test.ts +14 -9
  69. package/src/__tests__/renderer-recover.unit.test.ts +8 -0
  70. package/src/__tests__/renderer-surface.unit.test.ts +229 -3
  71. package/src/__tests__/shadow-csm-atlas.test.ts +41 -0
  72. package/src/__tests__/shadow-csm-component.test.ts +31 -24
  73. package/src/__tests__/shadow-csm-extract.test.ts +156 -47
  74. package/src/__tests__/shadow-csm-runtime-vary.dawn.test.ts +72 -0
  75. package/src/__tests__/shadow-csm-shader.dawn.test.ts +19 -0
  76. package/src/__tests__/shadow-csm-ubo.test.ts +41 -45
  77. package/src/__tests__/shadow-csm-viewport.browser.test.ts +29 -0
  78. package/src/__tests__/shadow-fields-observable.dawn.test.ts +140 -58
  79. package/src/__tests__/skinned-shadow-mixed.dawn.test.ts +6 -2
  80. package/src/__tests__/skylight-lazy-projection.test.ts +8 -5
  81. package/src/__tests__/sprite-cullmode-flip.dawn.test.ts +4 -6
  82. package/src/__tests__/sprite-lit-bgl-byte-identical.test.ts +4 -2
  83. package/src/__tests__/ssao-bgl.test.ts +10 -16
  84. package/src/__tests__/ssao-passes.test.ts +19 -2
  85. package/src/__tests__/systems.unit.test.ts +59 -33
  86. package/src/__tests__/texture-dimensions.browser.test.ts +127 -0
  87. package/src/__tests__/transcode-fallback.unit.test.ts +5 -1
  88. package/src/__tests__/view-ubo-layout.browser.test.ts +20 -10
  89. package/src/__tests__/volumetric-fog-record-recovery.integration.test.ts +180 -0
  90. package/src/createRenderer.ts +4 -0
  91. package/src/index.ts +1 -2
@@ -64,13 +64,12 @@ function registerQuadMesh(world: World): Handle<'MeshAsset', 'shared'> {
64
64
  function textureAsset(red = 255): TextureAsset {
65
65
  return {
66
66
  kind: 'texture',
67
- width: 1,
68
- height: 1,
67
+ shape: { viewDimension: '2d', extent: { width: 1, height: 1 } },
69
68
  format: 'rgba8unorm',
70
69
  data: new Uint8Array([red, 255, 255, 255]),
71
70
  colorSpace: 'linear',
72
- mipmap: false,
73
- } as unknown as TextureAsset;
71
+ mips: { kind: 'none' },
72
+ };
74
73
  }
75
74
 
76
75
  // Build a loadByGuid-shaped scene: the material's baseColorTexture paramValue is
@@ -77,11 +77,10 @@ function makeSharedPack(): unknown {
77
77
 
78
78
  const TEX_METADATA = {
79
79
  kind: 'texture',
80
- width: 1,
81
- height: 1,
80
+ shape: { viewDimension: '2d', extent: { width: 1, height: 1 } },
82
81
  format: 'rgba8unorm',
83
82
  colorSpace: 'srgb',
84
- mipmap: false,
83
+ mips: { kind: 'none' },
85
84
  };
86
85
 
87
86
  // `includeNames=false` is the FALSIFY variant (entry.name stripped).
@@ -0,0 +1,18 @@
1
+ import { derive } from '@forgeax/engine-types';
2
+ import { describe, expect, it } from 'vitest';
3
+
4
+ describe('material texture dimension mismatch contract', () => {
5
+ it('carries the authored array view dimension into the material binding interface', () => {
6
+ const derived = derive([{ name: 'layers', type: 'texture2d_array' }]);
7
+
8
+ expect(derived.coordinateRecords[0]?.parameter).toBe('layers');
9
+ expect(derived.bglEntries[2]?.texture?.viewDimension).toBe('2d-array');
10
+ });
11
+
12
+ it('carries the authored volume view dimension without changing the submit boundary', () => {
13
+ const derived = derive([{ name: 'volume', type: 'texture3d' }]);
14
+
15
+ expect(derived.bglEntries[2]?.texture?.viewDimension).toBe('3d');
16
+ expect(derived.resourceBindings).toHaveLength(2);
17
+ });
18
+ });
@@ -10,7 +10,10 @@ import {
10
10
 
11
11
  describe('material texture UV scale [w37]', () => {
12
12
  it('maps a non-aligned BC7 logical edge below padded physical storage', () => {
13
- const scale = materialTextureUvScale({ width: 2085, height: 1573, format: 'bc7-rgba-unorm' });
13
+ const scale = materialTextureUvScale({
14
+ shape: { viewDimension: '2d', extent: { width: 2085, height: 1573 } },
15
+ format: 'bc7-rgba-unorm',
16
+ });
14
17
  expect(scale).toEqual([2085 / 2088, 1573 / 1576]);
15
18
  expect(scale[0]).toBeLessThan(1);
16
19
  expect(scale[1]).toBeLessThan(1);
@@ -18,7 +21,12 @@ describe('material texture UV scale [w37]', () => {
18
21
 
19
22
  it('uses identity scale for uncompressed and fallback texture bindings', () => {
20
23
  expect(materialTextureUvScale(undefined)).toEqual([1, 1]);
21
- expect(materialTextureUvScale({ width: 17, height: 9, format: 'rgba8unorm' })).toEqual([1, 1]);
24
+ expect(
25
+ materialTextureUvScale({
26
+ shape: { viewDimension: '2d', extent: { width: 17, height: 9 } },
27
+ format: 'rgba8unorm',
28
+ }),
29
+ ).toEqual([1, 1]);
22
30
  });
23
31
 
24
32
  it('keeps compatibility param schemas compact when they have no textures', () => {
@@ -27,19 +27,20 @@ import { deriveRenderDataTexture } from '../../../render/src/render-data';
27
27
  function tex(overrides: Partial<TextureAsset>): TextureAsset {
28
28
  return {
29
29
  kind: 'texture',
30
- width: 8,
31
- height: 8,
30
+ shape: { viewDimension: '2d', extent: { width: 8, height: 8 } },
32
31
  format: 'rgba8unorm',
33
32
  data: new Uint8Array(8 * 8 * 4),
34
33
  colorSpace: 'linear',
35
- mipmap: false,
34
+ mips: { kind: 'none' },
36
35
  ...overrides,
37
36
  };
38
37
  }
39
38
 
40
39
  describe('deriveRenderDataTexture -- mip gate on compressed formats (w30, AC-09)', () => {
41
40
  it('compressed format + mipmap:true (runtime mip-gen requested) -> error', () => {
42
- const res = deriveRenderDataTexture(tex({ format: 'bc7-rgba-unorm', mipmap: true }));
41
+ const res = deriveRenderDataTexture(
42
+ tex({ format: 'bc7-rgba-unorm', mips: { kind: 'generate' } }),
43
+ );
43
44
  expect(res.ok).toBe(false);
44
45
  if (!res.ok) {
45
46
  expect(res.error.code).toBe('mipgen-unsupported-compressed-format');
@@ -50,7 +51,9 @@ describe('deriveRenderDataTexture -- mip gate on compressed formats (w30, AC-09)
50
51
  });
51
52
 
52
53
  it('mip-gate error fires for an ETC2 compressed format too', () => {
53
- const res = deriveRenderDataTexture(tex({ format: 'etc2-rgba8unorm', mipmap: true }));
54
+ const res = deriveRenderDataTexture(
55
+ tex({ format: 'etc2-rgba8unorm', mips: { kind: 'generate' } }),
56
+ );
54
57
  expect(res.ok).toBe(false);
55
58
  if (!res.ok) expect(res.error.code).toBe('mipgen-unsupported-compressed-format');
56
59
  });
@@ -59,20 +62,20 @@ describe('deriveRenderDataTexture -- mip gate on compressed formats (w30, AC-09)
59
62
  // The KTX2 loader baked the mip chain offline; this is the normal
60
63
  // compressed-upload path and must NOT trip the runtime-mip-gen gate.
61
64
  const res = deriveRenderDataTexture(
62
- tex({ format: 'bc7-rgba-unorm', mipmap: true, mipLevelCount: 4 }),
65
+ tex({ format: 'bc7-rgba-unorm', mips: { kind: 'packed', levelCount: 4 } }),
63
66
  );
64
67
  expect(res.ok).toBe(true);
65
68
  if (res.ok) expect(res.value.mipLevelCount).toBe(4);
66
69
  });
67
70
 
68
71
  it('compressed format + mipmap:false -> ok (single level)', () => {
69
- const res = deriveRenderDataTexture(tex({ format: 'bc7-rgba-unorm', mipmap: false }));
72
+ const res = deriveRenderDataTexture(tex({ format: 'bc7-rgba-unorm', mips: { kind: 'none' } }));
70
73
  expect(res.ok).toBe(true);
71
74
  if (res.ok) expect(res.value.mipLevelCount).toBe(1);
72
75
  });
73
76
 
74
77
  it('uncompressed format + mipmap:true -> ok (existing runtime mip-gen path unchanged)', () => {
75
- const res = deriveRenderDataTexture(tex({ format: 'rgba8unorm', mipmap: true }));
78
+ const res = deriveRenderDataTexture(tex({ format: 'rgba8unorm', mips: { kind: 'generate' } }));
76
79
  expect(res.ok).toBe(true);
77
80
  if (res.ok) expect(res.value.mipLevelCount).toBeGreaterThan(1);
78
81
  });
@@ -260,12 +260,11 @@ describe('feat-20260604-msaa M2 w9 [F-1]: LDR sprite + MSAA split sub-pass cover
260
260
  const synth = buildSyntheticRgba();
261
261
  const synthPod = {
262
262
  kind: 'texture' as const,
263
- width: synth.width,
264
- height: synth.height,
263
+ shape: { viewDimension: '2d' as const, extent: { width: synth.width, height: synth.height } },
265
264
  format: 'rgba8unorm-srgb' as const,
266
265
  data: synth.data,
267
266
  colorSpace: 'srgb' as const,
268
- mipmap: false,
267
+ mips: { kind: 'none' as const },
269
268
  };
270
269
  const TEX_GUID = '00000000-0000-7000-8000-0000000005a1';
271
270
  const SAMPLER_GUID = '00000000-0000-7000-8000-0000000005a2';
@@ -62,12 +62,11 @@ describe('AC-07 — a new reference appears in the load graph via envelope.refs'
62
62
  // new reference field whose GUID it dropped into refs[]".
63
63
  reg.catalog(parseGuid(EXTRA_REF_GUID), {
64
64
  kind: 'texture',
65
- width: 1,
66
- height: 1,
65
+ shape: { viewDimension: '2d', extent: { width: 1, height: 1 } },
67
66
  format: 'rgba8unorm',
68
67
  data: new Uint8Array(4),
69
68
  colorSpace: 'srgb',
70
- mipmap: false,
69
+ mips: { kind: 'none' },
71
70
  });
72
71
 
73
72
  const packIndex = [
@@ -44,7 +44,7 @@ function makeSpec(): PipelineSpec {
44
44
 
45
45
  describe('buildBindGroupLayoutDescriptor — pbr-pipeline 6 sites byte-equiv', () => {
46
46
  describe('pbr-view', () => {
47
- it('storageBuffer=true: 10 entries with read-only-storage on bindings 1+2', () => {
47
+ it('storageBuffer=true: 12 entries with read-only-storage on bindings 1+2 and projector bindings 11+12', () => {
48
48
  const spec = makeSpec();
49
49
  const out = buildBindGroupLayoutDescriptor(spec, {
50
50
  kind: 'pbr-view',
@@ -60,11 +60,15 @@ describe('buildBindGroupLayoutDescriptor — pbr-pipeline 6 sites byte-equiv', (
60
60
  // Points/Lines viewport UBO occupies binding 10, while w25 (scope-amend
61
61
  // webkit-fallback) folded the former binding 9 spotLightViewProj matrices
62
62
  // UBO into the View UBO (`view.spotLightViewProj`), so the BGL stays at 9
63
- // entries (bindings 0..8 plus binding 10) keeping the WebGL2 fallback fragment uniform-
64
- // buffer count <= 11 (GLES 3.0).
65
- expect(out.entries.length).toBe(10);
63
+ // entries (bindings 0..8 plus optional projector 11+12 and binding 10)
64
+ // — keeping the WebGL2 fallback fragment uniform-buffer count <= 11
65
+ // (GLES 3.0), while the projector remains available on the default
66
+ // WebGPU path.
67
+ expect(out.entries.length).toBe(12);
66
68
  expect(out.entries.find((entry) => entry.binding === 9)).toBeUndefined();
67
69
  expect(out.entries.find((entry) => entry.binding === 10)?.buffer?.type).toBe('uniform');
70
+ expect(out.entries.find((entry) => entry.binding === 11)?.texture?.viewDimension).toBe('2d');
71
+ expect(out.entries.find((entry) => entry.binding === 12)?.sampler?.type).toBe('filtering');
68
72
  });
69
73
 
70
74
  it('storageBuffer=false: bindings 1+2 fall back to uniform', () => {
@@ -79,6 +83,21 @@ describe('buildBindGroupLayoutDescriptor — pbr-pipeline 6 sites byte-equiv', (
79
83
  };
80
84
  expect(out).toEqual(expected);
81
85
  });
86
+
87
+ it('projectorAvailable=false: omits optional projector bindings for the 16-texture limit', () => {
88
+ const spec = makeSpec();
89
+ const out = buildBindGroupLayoutDescriptor(spec, {
90
+ kind: 'pbr-view',
91
+ caps: { storageBuffer: true, projectorAvailable: false },
92
+ });
93
+ expect(out).toEqual({
94
+ label: 'pbr-view-bgl',
95
+ entries: buildPbrViewBglEntries({ storageBuffer: true, projectorAvailable: false }),
96
+ });
97
+ expect(out.entries.find((entry) => entry.binding === 11)).toBeUndefined();
98
+ expect(out.entries.find((entry) => entry.binding === 12)).toBeUndefined();
99
+ expect(out.entries.find((entry) => entry.binding === 10)?.buffer?.type).toBe('uniform');
100
+ });
82
101
  });
83
102
 
84
103
  describe('pbr-material-merged', () => {
@@ -160,7 +179,7 @@ describe('buildBindGroupLayoutDescriptor — pbr-pipeline 6 sites byte-equiv', (
160
179
  });
161
180
 
162
181
  describe('pbr-skin-mesh-array', () => {
163
- it('storageBuffer=true: 2 entries, both dynamic offset', () => {
182
+ it('storageBuffer=true: 3 entries, all dynamic offset', () => {
164
183
  const spec = makeSpec();
165
184
  const out = buildBindGroupLayoutDescriptor(spec, {
166
185
  kind: 'pbr-skin-mesh-array',
@@ -179,6 +198,11 @@ describe('buildBindGroupLayoutDescriptor — pbr-pipeline 6 sites byte-equiv', (
179
198
  visibility: 0x1,
180
199
  buffer: { type: 'read-only-storage', hasDynamicOffset: true },
181
200
  },
201
+ {
202
+ binding: 2,
203
+ visibility: 0x1,
204
+ buffer: { type: 'read-only-storage', hasDynamicOffset: true },
205
+ },
182
206
  ],
183
207
  });
184
208
  });