@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.
- package/README.md +102 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/__tests__/create-renderer-gpu-pass-timing.unit.test.d.ts +2 -0
- package/dist/__tests__/create-renderer-gpu-pass-timing.unit.test.d.ts.map +1 -0
- package/dist/__tests__/helpers/tilemap-assets.d.ts.map +1 -1
- package/dist/__tests__/lights-preservation.fixup.test.d.ts +2 -0
- package/dist/__tests__/lights-preservation.fixup.test.d.ts.map +1 -0
- package/dist/__tests__/material-texture-dimension-mismatch.unit.test.d.ts +2 -0
- package/dist/__tests__/material-texture-dimension-mismatch.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-error-exhaustive.test-d.d.ts.map +1 -1
- package/dist/__tests__/texture-dimensions.browser.test.d.ts +2 -0
- package/dist/__tests__/texture-dimensions.browser.test.d.ts.map +1 -0
- package/dist/__tests__/volumetric-fog-record-recovery.integration.test.d.ts +2 -0
- package/dist/__tests__/volumetric-fog-record-recovery.integration.test.d.ts.map +1 -0
- package/dist/createRenderer.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +3 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +30 -30
- package/src/__tests__/active-camera.unit.test.ts +6 -8
- package/src/__tests__/asset-registry.recursive.spec.ts +13 -19
- package/src/__tests__/asset.unit.test.ts +28 -25
- package/src/__tests__/basis-catalog-dispatch.integration.test.ts +10 -9
- package/src/__tests__/clamp-to-last.e2e.browser.test.ts +2 -2
- package/src/__tests__/components.test-d.ts +10 -3
- package/src/__tests__/components.unit.test.ts +13 -7
- package/src/__tests__/compressed-texture-physical-extent.integration.test.ts +12 -10
- package/src/__tests__/create-renderer-assembly.integration.test.ts +24 -0
- package/src/__tests__/create-renderer-gpu-pass-timing.unit.test.ts +32 -0
- package/src/__tests__/createRenderer.browser-no-webgpu.test.ts +66 -0
- package/src/__tests__/dawn/hello-sprite-nineslice-section.dawn.test.ts +11 -4
- package/src/__tests__/dawn/instances-shadow.dawn.test.ts +15 -2
- package/src/__tests__/dawn/material-alpha.dawn.test.ts +2 -3
- package/src/__tests__/dawn/mipmap-srgb-linear.dawn.test.ts +2 -3
- package/src/__tests__/dawn/per-entity-material-texture-isolation.dawn.test.ts +2 -3
- package/src/__tests__/dawn/sprite-nineslice-mesh-bind.dawn.test.ts +2 -3
- package/src/__tests__/dawn/upload-texture.dawn.test.ts +4 -6
- package/src/__tests__/derive-bgl-integration.test.ts +11 -0
- package/src/__tests__/equirect-bc6h.integration.test.ts +6 -4
- package/src/__tests__/errors.unit.test.ts +71 -20
- package/src/__tests__/extract-frames-merge.test.ts +48 -40
- package/src/__tests__/extract-skin-mismatch.unit.test.ts +6 -2
- package/src/__tests__/four-path-compression.integration.test.ts +2 -2
- package/src/__tests__/frame-targets-shadow-regression.unit.test.ts +13 -0
- package/src/__tests__/geometry.unit.test.ts +7 -10
- package/src/__tests__/gpu-resource-cow-survivor.test.ts +2 -3
- package/src/__tests__/helpers/tilemap-assets.ts +3 -4
- package/src/__tests__/light-gpu-byte-neutral.unit.test.ts +6 -3
- package/src/__tests__/light-snapshot.test-d.ts +8 -0
- package/src/__tests__/lights-preservation.fixup.test.ts +127 -0
- package/src/__tests__/lights.unit.test.ts +75 -2214
- package/src/__tests__/loadbyguid-texture-intern.unit.test.ts +3 -4
- package/src/__tests__/loader-name-survival.test.ts +2 -3
- package/src/__tests__/material-texture-dimension-mismatch.unit.test.ts +18 -0
- package/src/__tests__/material-texture-uv-scale.unit.test.ts +10 -2
- package/src/__tests__/mip-gate.unit.test.ts +11 -8
- package/src/__tests__/msaa-sprite-pixel-diff.dawn.test.ts +2 -3
- package/src/__tests__/new-kind-refs-contract.test.ts +2 -3
- package/src/__tests__/pbr-pipeline.unit.test.ts +29 -5
- package/src/__tests__/pipeline.unit.test.ts +66 -221
- package/src/__tests__/point-light-shadow.browser.test.ts +208 -83
- package/src/__tests__/point-light-shadow.unit.test.ts +67 -9
- package/src/__tests__/render-error-exhaustive.test-d.ts +92 -9
- package/src/__tests__/render-system-extract.test.ts +4 -6
- package/src/__tests__/render-system-mega.test.ts +30 -41
- package/src/__tests__/render-system-record-multi-material-textureview.test.ts +8 -7
- package/src/__tests__/render-system-record-per-submesh-transparency.test.ts +6 -4
- package/src/__tests__/render-system-skin-bg.unit.test.ts +14 -9
- package/src/__tests__/renderer-recover.unit.test.ts +8 -0
- package/src/__tests__/renderer-surface.unit.test.ts +229 -3
- package/src/__tests__/shadow-csm-atlas.test.ts +41 -0
- package/src/__tests__/shadow-csm-component.test.ts +31 -24
- package/src/__tests__/shadow-csm-extract.test.ts +156 -47
- package/src/__tests__/shadow-csm-runtime-vary.dawn.test.ts +72 -0
- package/src/__tests__/shadow-csm-shader.dawn.test.ts +19 -0
- package/src/__tests__/shadow-csm-ubo.test.ts +41 -45
- package/src/__tests__/shadow-csm-viewport.browser.test.ts +29 -0
- package/src/__tests__/shadow-fields-observable.dawn.test.ts +140 -58
- package/src/__tests__/skinned-shadow-mixed.dawn.test.ts +6 -2
- package/src/__tests__/skylight-lazy-projection.test.ts +8 -5
- package/src/__tests__/sprite-cullmode-flip.dawn.test.ts +4 -6
- package/src/__tests__/sprite-lit-bgl-byte-identical.test.ts +4 -2
- package/src/__tests__/ssao-bgl.test.ts +10 -16
- package/src/__tests__/ssao-passes.test.ts +19 -2
- package/src/__tests__/systems.unit.test.ts +59 -33
- package/src/__tests__/texture-dimensions.browser.test.ts +127 -0
- package/src/__tests__/transcode-fallback.unit.test.ts +5 -1
- package/src/__tests__/view-ubo-layout.browser.test.ts +20 -10
- package/src/__tests__/volumetric-fog-record-recovery.integration.test.ts +180 -0
- package/src/createRenderer.ts +4 -0
- package/src/index.ts +1 -2
|
@@ -144,12 +144,11 @@ function configuredStore(probe: CowProbe): GpuResidencyCache {
|
|
|
144
144
|
function texturePod(w = 2, h = 2): TextureAsset {
|
|
145
145
|
return {
|
|
146
146
|
kind: 'texture',
|
|
147
|
-
width: w,
|
|
148
|
-
height: h,
|
|
147
|
+
shape: { viewDimension: '2d', extent: { width: w, height: h } },
|
|
149
148
|
format: 'rgba8unorm-srgb',
|
|
150
149
|
data: new Uint8Array(w * h * 4).fill(188),
|
|
151
150
|
colorSpace: 'srgb',
|
|
152
|
-
|
|
151
|
+
mips: { kind: 'none' },
|
|
153
152
|
};
|
|
154
153
|
}
|
|
155
154
|
|
|
@@ -6,12 +6,11 @@ export const TEST_ATLAS_GUID = 'test/atlas';
|
|
|
6
6
|
export function makeTestTexture(seed = 0): TextureAsset {
|
|
7
7
|
return {
|
|
8
8
|
kind: 'texture',
|
|
9
|
-
width: 32,
|
|
10
|
-
|
|
11
|
-
format: 'rgba8unorm',
|
|
9
|
+
shape: { viewDimension: '2d', extent: { width: 32, height: 32 } },
|
|
10
|
+
format: 'rgba8unorm-srgb',
|
|
12
11
|
data: new Uint8Array(32 * 32 * 4).fill(seed),
|
|
13
12
|
colorSpace: 'srgb',
|
|
14
|
-
|
|
13
|
+
mips: { kind: 'none' },
|
|
15
14
|
};
|
|
16
15
|
}
|
|
17
16
|
|
|
@@ -57,13 +57,14 @@ const emptyLights: ExtractedLights = {
|
|
|
57
57
|
directionalCount: 0,
|
|
58
58
|
point: [],
|
|
59
59
|
spot: [],
|
|
60
|
+
directionalShadowError: undefined,
|
|
60
61
|
lightViewProj: undefined,
|
|
61
62
|
splitPlanes: undefined,
|
|
62
63
|
cascadeCount: undefined,
|
|
63
64
|
cascadeBlend: undefined,
|
|
64
65
|
depthBias: undefined,
|
|
65
66
|
normalBias: undefined,
|
|
66
|
-
|
|
67
|
+
directionalShadowQuality: undefined,
|
|
67
68
|
} as unknown as ExtractedLights;
|
|
68
69
|
|
|
69
70
|
/** Capture the single writeViewUbo payload as a Float32Array. */
|
|
@@ -117,7 +118,7 @@ describe('w7 -- light GPU pack byte-neutral (AC-11)', () => {
|
|
|
117
118
|
expect(out[6]).toBeCloseTo(0.6, 6);
|
|
118
119
|
});
|
|
119
120
|
|
|
120
|
-
it('packSpotLight writes color slots 4..6 + direction slots 8..10 byte-for-byte (
|
|
121
|
+
it('packSpotLight writes color slots 4..6 + direction slots 8..10 byte-for-byte (80 B)', () => {
|
|
121
122
|
const snap: SpotLightSnapshot = {
|
|
122
123
|
kind: 'spot',
|
|
123
124
|
position: vec3.create(0, 5, 0),
|
|
@@ -135,7 +136,9 @@ describe('w7 -- light GPU pack byte-neutral (AC-11)', () => {
|
|
|
135
136
|
shadowAtlasTile: -1,
|
|
136
137
|
};
|
|
137
138
|
const out = packSpotLight(snap);
|
|
138
|
-
|
|
139
|
+
// SpotLight carries projector receiver controls and the shadow intensity
|
|
140
|
+
// tail in the current std430 payload.
|
|
141
|
+
expect(out.byteLength).toBe(80);
|
|
139
142
|
expect(out[4]).toBeCloseTo(0.3, 6);
|
|
140
143
|
expect(out[5]).toBeCloseTo(0.4, 6);
|
|
141
144
|
expect(out[6]).toBeCloseTo(0.5, 6);
|
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
// covers any .test-d.ts file in any package).
|
|
22
22
|
|
|
23
23
|
import { expectTypeOf, test } from 'vitest';
|
|
24
|
+
import type { DirectionalShadowQuality } from '../../../render/src/components/directional-shadow-filter';
|
|
25
|
+
import type { LightValidationError } from '../../../render/src/components/light-helpers';
|
|
24
26
|
import type {
|
|
25
27
|
DirectionalLightSnapshot,
|
|
26
28
|
ExtractedLights,
|
|
@@ -81,4 +83,10 @@ test('ExtractedLights three-bucket shape', () => {
|
|
|
81
83
|
expectTypeOf<ExtractedLights['directionalCount']>().toEqualTypeOf<number>();
|
|
82
84
|
expectTypeOf<ExtractedLights['point']>().toEqualTypeOf<readonly PointLightSnapshot[]>();
|
|
83
85
|
expectTypeOf<ExtractedLights['spot']>().toEqualTypeOf<readonly SpotLightSnapshot[]>();
|
|
86
|
+
expectTypeOf<ExtractedLights['directionalShadowQuality']>().toEqualTypeOf<
|
|
87
|
+
DirectionalShadowQuality | undefined
|
|
88
|
+
>();
|
|
89
|
+
expectTypeOf<ExtractedLights['directionalShadowError']>().toEqualTypeOf<
|
|
90
|
+
LightValidationError | undefined
|
|
91
|
+
>();
|
|
84
92
|
});
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { World } from '@forgeax/engine-ecs';
|
|
2
|
+
import { vec3 } from '@forgeax/engine-math';
|
|
3
|
+
import { Camera, PointLight, SpotLight } from '@forgeax/engine-render';
|
|
4
|
+
import { Transform } from '@forgeax/engine-scene';
|
|
5
|
+
import { describe, expect, it } from 'vitest';
|
|
6
|
+
import {
|
|
7
|
+
computeInvRangeSquared,
|
|
8
|
+
degToCos,
|
|
9
|
+
validatePointLightShadowData,
|
|
10
|
+
validateSpotLightData,
|
|
11
|
+
} from '../../../render/src/components/light-helpers';
|
|
12
|
+
import {
|
|
13
|
+
packLightSlot,
|
|
14
|
+
packPointLight,
|
|
15
|
+
packSpotLight,
|
|
16
|
+
} from '../../../render/src/light-buffer-layout';
|
|
17
|
+
import { buildPbrViewBglEntries } from '../../../render/src/pbr-pipeline';
|
|
18
|
+
import type {
|
|
19
|
+
PointLightSnapshot,
|
|
20
|
+
SpotLightSnapshot,
|
|
21
|
+
} from '../../../render/src/render-system-extract';
|
|
22
|
+
import { extractFrame, prepareExtractContext } from '../../../render/src/render-system-extract';
|
|
23
|
+
|
|
24
|
+
function pointSnapshot(): PointLightSnapshot {
|
|
25
|
+
return {
|
|
26
|
+
kind: 'point',
|
|
27
|
+
position: vec3.create(1, 2, 3),
|
|
28
|
+
color: vec3.create(2, 1, 0.5),
|
|
29
|
+
intensity: 2,
|
|
30
|
+
invRangeSquared: 0.25,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function spotSnapshot(): SpotLightSnapshot {
|
|
35
|
+
return {
|
|
36
|
+
kind: 'spot',
|
|
37
|
+
position: vec3.create(1, 2, 3),
|
|
38
|
+
direction: vec3.create(0, -1, 0),
|
|
39
|
+
color: vec3.create(2, 1, 0.5),
|
|
40
|
+
intensity: 2,
|
|
41
|
+
invRangeSquared: 0.25,
|
|
42
|
+
cosInner: degToCos(15),
|
|
43
|
+
cosOuter: degToCos(30),
|
|
44
|
+
castShadow: false,
|
|
45
|
+
lightViewProj: undefined,
|
|
46
|
+
mapSize: 512,
|
|
47
|
+
nearPlane: 0.1,
|
|
48
|
+
farPlane: 25,
|
|
49
|
+
shadowAtlasTile: -1,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
describe('Point/Spot and shared-light regression guard', () => {
|
|
54
|
+
it('keeps extract facts for point range and spot cone in the shared light path', () => {
|
|
55
|
+
const world = new World();
|
|
56
|
+
const pointSpawn = world.spawn(
|
|
57
|
+
{ component: Transform, data: { pos: [1, 2, 3] } },
|
|
58
|
+
{ component: PointLight, data: { range: 2 } },
|
|
59
|
+
);
|
|
60
|
+
const spotSpawn = world.spawn(
|
|
61
|
+
{ component: Transform, data: { pos: [4, 5, 6] } },
|
|
62
|
+
{
|
|
63
|
+
component: SpotLight,
|
|
64
|
+
data: { direction: [0, -1, 0], range: 4, innerConeDeg: 15, outerConeDeg: 30 },
|
|
65
|
+
},
|
|
66
|
+
);
|
|
67
|
+
world.spawn(
|
|
68
|
+
{ component: Transform, data: {} },
|
|
69
|
+
{ component: Camera, data: { fov: Math.PI / 4, aspect: 1, near: 0.1, far: 100 } },
|
|
70
|
+
);
|
|
71
|
+
expect(pointSpawn.ok, JSON.stringify(pointSpawn)).toBe(true);
|
|
72
|
+
expect(spotSpawn.ok, JSON.stringify(spotSpawn)).toBe(true);
|
|
73
|
+
|
|
74
|
+
const lights = extractFrame(world, prepareExtractContext(world)).lights;
|
|
75
|
+
expect(lights.point).toHaveLength(1);
|
|
76
|
+
expect(lights.point[0]?.invRangeSquared).toBeCloseTo(0.25, 6);
|
|
77
|
+
expect(lights.spot).toHaveLength(1);
|
|
78
|
+
expect(lights.spot[0]?.cosInner).toBeCloseTo(degToCos(15), 6);
|
|
79
|
+
expect(lights.spot[0]?.cosOuter).toBeCloseTo(degToCos(30), 6);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('keeps attenuation and cone helpers as finite, shared semantic owners', () => {
|
|
83
|
+
expect(computeInvRangeSquared(2)).toBeCloseTo(0.25, 8);
|
|
84
|
+
expect(computeInvRangeSquared(0)).toBe(1e8);
|
|
85
|
+
expect(computeInvRangeSquared(Number.POSITIVE_INFINITY)).toBe(0);
|
|
86
|
+
expect(degToCos(0)).toBeCloseTo(1, 8);
|
|
87
|
+
expect(degToCos(60)).toBeCloseTo(0.5, 8);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('keeps Point/Spot packing and the shared LightSlot discriminant intact', () => {
|
|
91
|
+
const point = pointSnapshot();
|
|
92
|
+
const spot = spotSnapshot();
|
|
93
|
+
const pointPacked = packPointLight(point);
|
|
94
|
+
const spotPacked = packSpotLight(spot);
|
|
95
|
+
const pointSlot = packLightSlot(point, { shadowAtlasLayer: 2, near: 0.1, far: 20 });
|
|
96
|
+
const spotSlot = packLightSlot(spot);
|
|
97
|
+
|
|
98
|
+
expect(pointPacked.byteLength).toBe(32);
|
|
99
|
+
expect(spotPacked.byteLength).toBe(80);
|
|
100
|
+
expect(new Uint32Array(pointSlot.buffer)[12]).toBe(0);
|
|
101
|
+
expect(new Int32Array(pointSlot.buffer)[13]).toBe(2);
|
|
102
|
+
expect(new Uint32Array(spotSlot.buffer)[12]).toBe(1);
|
|
103
|
+
expect(new Int32Array(spotSlot.buffer)[13]).toBe(-1);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('keeps shared PBR view bindings for both storage and uniform light routes', () => {
|
|
107
|
+
const storageBindings = buildPbrViewBglEntries({ storageBuffer: true });
|
|
108
|
+
const uniformBindings = buildPbrViewBglEntries({ storageBuffer: false });
|
|
109
|
+
expect(storageBindings.map((entry) => entry.binding)).toEqual(
|
|
110
|
+
uniformBindings.map((entry) => entry.binding),
|
|
111
|
+
);
|
|
112
|
+
expect(storageBindings[1]?.buffer?.type).toBe('read-only-storage');
|
|
113
|
+
expect(storageBindings[2]?.buffer?.type).toBe('read-only-storage');
|
|
114
|
+
expect(uniformBindings[1]?.buffer?.type).toBe('uniform');
|
|
115
|
+
expect(uniformBindings[2]?.buffer?.type).toBe('uniform');
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('keeps Point/Spot pcfKernelSize validation independent of Directional quality', () => {
|
|
119
|
+
expect(
|
|
120
|
+
validatePointLightShadowData({ mapSize: 512, nearPlane: 0.1, farPlane: 20, pcfKernelSize: 5 })
|
|
121
|
+
.ok,
|
|
122
|
+
).toBe(true);
|
|
123
|
+
expect(validateSpotLightData({ direction: [0, -1, 0], pcfKernelSize: 3 }).ok).toBe(true);
|
|
124
|
+
expect(validatePointLightShadowData({ pcfKernelSize: 2 }).ok).toBe(false);
|
|
125
|
+
expect(validateSpotLightData({ direction: [0, -1, 0], pcfKernelSize: 2 }).ok).toBe(false);
|
|
126
|
+
});
|
|
127
|
+
});
|