@flighthq/effects-wgpu 0.5.1-next.650.5d3df0b → 0.5.1-next.658.9cb44d7
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 +2 -2
- package/package.json +9 -9
- package/src/wgpuBevelEffect.test.ts +67 -55
- package/src/wgpuColorLutPass.test.ts +15 -12
- package/src/wgpuColorMatrixPass.test.ts +29 -25
- package/src/wgpuContactShadowsEffect.test.ts +18 -9
- package/src/wgpuDropShadowEffect.test.ts +40 -40
- package/src/wgpuEffectBlitShader.test.ts +32 -20
- package/src/wgpuEffectBoxBlur.test.ts +36 -26
- package/src/wgpuEffectPass.test.ts +15 -13
- package/src/wgpuEffectProgramCache.test.ts +23 -18
- package/src/wgpuEffectTintShader.test.ts +31 -11
- package/src/wgpuGodRaysEffect.test.ts +33 -29
- package/src/wgpuGradientBevelEffect.test.ts +73 -58
- package/src/wgpuHalftoneEffect.test.ts +35 -25
- package/src/wgpuInnerGlowEffect.test.ts +40 -42
- package/src/wgpuInnerShadowEffect.test.ts +41 -43
- package/src/wgpuLensDirtEffect.test.ts +65 -46
- package/src/wgpuLogicalEffectResolution.test.ts +35 -33
- package/src/wgpuOuterGlowEffect.test.ts +43 -39
- package/src/wgpuRadialBlurEffect.test.ts +35 -25
package/README.md
CHANGED
|
@@ -13,5 +13,5 @@ Import the supported application-facing API from `@flighthq/effects-wgpu`. The `
|
|
|
13
13
|
This package is part of the locked-version Flight SDK graph. Applications may instead install and import `@flighthq/sdk` when package-level tree shaking is sufficient.
|
|
14
14
|
|
|
15
15
|
- [Flight project](https://github.com/flighthq/flight)
|
|
16
|
-
- [Source for @flighthq/effects-wgpu@0.5.1-next.
|
|
17
|
-
- [License](https://github.com/flighthq/flight/blob/
|
|
16
|
+
- [Source for @flighthq/effects-wgpu@0.5.1-next.658.9cb44d7](https://github.com/flighthq/flight/tree/9cb44d76b981adad2503d5e62207be99adb35c55/packages/effects-wgpu)
|
|
17
|
+
- [License](https://github.com/flighthq/flight/blob/9cb44d76b981adad2503d5e62207be99adb35c55/LICENSE.md)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flighthq/effects-wgpu",
|
|
3
|
-
"version": "0.5.1-next.
|
|
3
|
+
"version": "0.5.1-next.658.9cb44d7",
|
|
4
4
|
"author": "Joshua Granick and other contributors",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -38,14 +38,14 @@
|
|
|
38
38
|
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@flighthq/adjustments": "0.5.1-next.
|
|
42
|
-
"@flighthq/color": "0.5.1-next.
|
|
43
|
-
"@flighthq/effects": "0.5.1-next.
|
|
44
|
-
"@flighthq/geometry": "0.5.1-next.
|
|
45
|
-
"@flighthq/log": "0.5.1-next.
|
|
46
|
-
"@flighthq/render-wgpu": "0.5.1-next.
|
|
47
|
-
"@flighthq/registry": "0.5.1-next.
|
|
48
|
-
"@flighthq/types": "0.5.1-next.
|
|
41
|
+
"@flighthq/adjustments": "0.5.1-next.658.9cb44d7",
|
|
42
|
+
"@flighthq/color": "0.5.1-next.658.9cb44d7",
|
|
43
|
+
"@flighthq/effects": "0.5.1-next.658.9cb44d7",
|
|
44
|
+
"@flighthq/geometry": "0.5.1-next.658.9cb44d7",
|
|
45
|
+
"@flighthq/log": "0.5.1-next.658.9cb44d7",
|
|
46
|
+
"@flighthq/render-wgpu": "0.5.1-next.658.9cb44d7",
|
|
47
|
+
"@flighthq/registry": "0.5.1-next.658.9cb44d7",
|
|
48
|
+
"@flighthq/types": "0.5.1-next.658.9cb44d7"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
51
|
"@webgpu/types": "^0.1.70"
|
|
@@ -1,71 +1,83 @@
|
|
|
1
|
+
import * as renderWgpuContractModule from '@flighthq/render-wgpu/contract';
|
|
1
2
|
import { createWgpuRenderStateForTest, installWgpuMock } from '@flighthq/render-wgpu/contract';
|
|
2
3
|
import type { BevelEffect, WgpuRenderState, WgpuRenderTarget, WgpuRenderTargetPool } from '@flighthq/types/contract';
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
_dest: unknown,
|
|
13
|
-
_pipeline: unknown,
|
|
14
|
-
setUniforms: (f32: Float32Array) => void,
|
|
15
|
-
) => {
|
|
16
|
-
const f32 = new Float32Array(12);
|
|
17
|
-
setUniforms(f32);
|
|
18
|
-
recorded.composites.push([...f32]);
|
|
19
|
-
},
|
|
20
|
-
),
|
|
21
|
-
}));
|
|
22
|
-
const blitMock = vi.hoisted(() => ({
|
|
23
|
-
applyWgpuEffectBlitPass: vi.fn(() => recorded.calls.push('blit')),
|
|
24
|
-
applyWgpuEffectErasePass: vi.fn(() => recorded.calls.push('erase')),
|
|
25
|
-
}));
|
|
26
|
-
const blurMock = vi.hoisted(() => ({
|
|
27
|
-
applyWgpuEffectBoxBlur: vi.fn((_state: unknown, _s: unknown, _d: unknown, _t: unknown, options: unknown) => {
|
|
28
|
-
recorded.blurs.push(options);
|
|
29
|
-
}),
|
|
30
|
-
}));
|
|
31
|
-
const tintMock = vi.hoisted(() => ({
|
|
32
|
-
applyWgpuEffectTintPass: vi.fn(
|
|
33
|
-
(_state: unknown, _source: unknown, _dest: unknown, color: number, alpha: number, strength: number) => {
|
|
34
|
-
recorded.tints.push([color, alpha, strength]);
|
|
35
|
-
},
|
|
36
|
-
),
|
|
37
|
-
}));
|
|
38
|
-
const targetMock = vi.hoisted(() => ({
|
|
39
|
-
acquireWgpuRenderTarget: vi.fn((_state: unknown, _pool: unknown, descriptor: unknown) => {
|
|
40
|
-
const target = { ...(descriptor as object), id: `scratch-${recorded.acquired.length}`, view: {} };
|
|
41
|
-
recorded.acquired.push(target);
|
|
42
|
-
return target;
|
|
43
|
-
}),
|
|
44
|
-
releaseWgpuRenderTarget: vi.fn((_pool: unknown, target: unknown) => recorded.released.push(target)),
|
|
45
|
-
}));
|
|
46
|
-
const recorded = vi.hoisted(() => ({
|
|
5
|
+
import { applyBevelEffectToWgpu, defaultWgpuBevelEffectRunner, registerWgpuBevelEffect } from './wgpuBevelEffect';
|
|
6
|
+
import * as wgpuEffectBlitShaderModule from './wgpuEffectBlitShader';
|
|
7
|
+
import * as wgpuEffectBoxBlurModule from './wgpuEffectBoxBlur';
|
|
8
|
+
import * as wgpuEffectPassModule from './wgpuEffectPass';
|
|
9
|
+
import * as wgpuEffectTintShaderModule from './wgpuEffectTintShader';
|
|
10
|
+
import { getWgpuRenderEffectRunner } from './wgpuRenderEffectRegistry';
|
|
11
|
+
|
|
12
|
+
const recorded = {
|
|
47
13
|
acquired: [] as unknown[],
|
|
48
14
|
blurs: [] as unknown[],
|
|
49
15
|
calls: [] as string[],
|
|
50
16
|
composites: [] as number[][],
|
|
51
17
|
released: [] as unknown[],
|
|
52
18
|
tints: [] as (number | undefined)[][],
|
|
53
|
-
}
|
|
19
|
+
};
|
|
54
20
|
|
|
55
|
-
|
|
56
|
-
vi.mock('./wgpuEffectBoxBlur', () => blurMock);
|
|
57
|
-
vi.mock('./wgpuEffectPass', () => passMock);
|
|
58
|
-
vi.mock('./wgpuEffectTintShader', () => tintMock);
|
|
21
|
+
beforeAll(() => installWgpuMock());
|
|
59
22
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
vi.spyOn(wgpuEffectPassModule, 'clearWgpuEffectTarget').mockImplementation((() => {}) as never);
|
|
25
|
+
vi.spyOn(wgpuEffectPassModule, 'createWgpuDualSourceEffectPipeline').mockImplementation((() => ({
|
|
26
|
+
pipeline: {},
|
|
27
|
+
})) as never);
|
|
28
|
+
vi.spyOn(wgpuEffectPassModule, 'drawWgpuDualSourceEffectPass').mockImplementation(((
|
|
29
|
+
_state: unknown,
|
|
30
|
+
_field: unknown,
|
|
31
|
+
_source: unknown,
|
|
32
|
+
_dest: unknown,
|
|
33
|
+
_pipeline: unknown,
|
|
34
|
+
setUniforms: (f32: Float32Array) => void,
|
|
35
|
+
) => {
|
|
36
|
+
const f32 = new Float32Array(12);
|
|
37
|
+
setUniforms(f32);
|
|
38
|
+
recorded.composites.push([...f32]);
|
|
39
|
+
}) as never);
|
|
40
|
+
|
|
41
|
+
vi.spyOn(wgpuEffectBlitShaderModule, 'applyWgpuEffectBlitPass').mockImplementation((() =>
|
|
42
|
+
recorded.calls.push('blit')) as never);
|
|
43
|
+
vi.spyOn(wgpuEffectBlitShaderModule, 'applyWgpuEffectErasePass').mockImplementation((() =>
|
|
44
|
+
recorded.calls.push('erase')) as never);
|
|
45
|
+
|
|
46
|
+
vi.spyOn(wgpuEffectBoxBlurModule, 'applyWgpuEffectBoxBlur').mockImplementation(((
|
|
47
|
+
_state: unknown,
|
|
48
|
+
_s: unknown,
|
|
49
|
+
_d: unknown,
|
|
50
|
+
_t: unknown,
|
|
51
|
+
options: unknown,
|
|
52
|
+
) => {
|
|
53
|
+
recorded.blurs.push(options);
|
|
54
|
+
}) as never);
|
|
55
|
+
|
|
56
|
+
vi.spyOn(wgpuEffectTintShaderModule, 'applyWgpuEffectTintPass').mockImplementation(((
|
|
57
|
+
_state: unknown,
|
|
58
|
+
_source: unknown,
|
|
59
|
+
_dest: unknown,
|
|
60
|
+
color: number,
|
|
61
|
+
alpha: number,
|
|
62
|
+
strength: number,
|
|
63
|
+
) => {
|
|
64
|
+
recorded.tints.push([color, alpha, strength]);
|
|
65
|
+
}) as never);
|
|
66
|
+
|
|
67
|
+
vi.spyOn(renderWgpuContractModule, 'acquireWgpuRenderTarget').mockImplementation(((
|
|
68
|
+
_state: unknown,
|
|
69
|
+
_pool: unknown,
|
|
70
|
+
descriptor: unknown,
|
|
71
|
+
) => {
|
|
72
|
+
const target = { ...(descriptor as object), id: `scratch-${recorded.acquired.length}`, view: {} };
|
|
73
|
+
recorded.acquired.push(target);
|
|
74
|
+
return target;
|
|
75
|
+
}) as never);
|
|
76
|
+
vi.spyOn(renderWgpuContractModule, 'releaseWgpuRenderTarget').mockImplementation(((_pool: unknown, target: unknown) =>
|
|
77
|
+
recorded.released.push(target)) as never);
|
|
63
78
|
});
|
|
64
79
|
|
|
65
|
-
|
|
66
|
-
import { getWgpuRenderEffectRunner } from './wgpuRenderEffectRegistry';
|
|
67
|
-
|
|
68
|
-
beforeAll(() => installWgpuMock());
|
|
80
|
+
afterEach(() => vi.restoreAllMocks());
|
|
69
81
|
|
|
70
82
|
const SOURCE_WIDTH = 100;
|
|
71
83
|
const SOURCE_HEIGHT = 50;
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { installWgpuMock } from '@flighthq/render-wgpu/contract';
|
|
2
2
|
import type { ColorLut, WgpuColorLutTextureCache, WgpuRenderState, WgpuRenderTarget } from '@flighthq/types/contract';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const passState = vi.hoisted(() => ({
|
|
4
|
+
import { applyColorLutPassToWgpu } from './wgpuColorLutPass';
|
|
5
|
+
import * as wgpuEffectPassModule from './wgpuEffectPass';
|
|
6
|
+
|
|
7
|
+
const passState = {
|
|
9
8
|
acquireSlot: vi.fn(() => 256),
|
|
10
9
|
beginPass: vi.fn((_dest: unknown, loadOp: string) => {
|
|
11
10
|
recorded.loadOps.push(loadOp);
|
|
@@ -25,24 +24,28 @@ const passState = vi.hoisted(() => ({
|
|
|
25
24
|
write(f32);
|
|
26
25
|
recorded.uniforms.push([...f32]);
|
|
27
26
|
}),
|
|
28
|
-
}
|
|
29
|
-
const recorded =
|
|
27
|
+
};
|
|
28
|
+
const recorded = {
|
|
30
29
|
bindGroups: [] as { group: unknown; index: number }[],
|
|
31
30
|
draws: [] as number[],
|
|
32
31
|
loadOps: [] as string[],
|
|
33
32
|
pipelines: [] as unknown[],
|
|
34
33
|
shaderCode: [] as string[],
|
|
35
34
|
uniforms: [] as number[][],
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
vi.mock('./wgpuEffectPass', () => passStateMock);
|
|
39
|
-
|
|
40
|
-
import { applyColorLutPassToWgpu } from './wgpuColorLutPass';
|
|
35
|
+
};
|
|
41
36
|
|
|
42
37
|
// The WebGPU flag enums are type-level only in @webgpu/types, so jsdom has no runtime values for the
|
|
43
38
|
// usage and visibility bits this module ORs together.
|
|
44
39
|
beforeAll(() => installWgpuMock());
|
|
45
40
|
|
|
41
|
+
beforeEach(() => {
|
|
42
|
+
vi.spyOn(wgpuEffectPassModule, 'getWgpuEffectPassState').mockReturnValue(passState as never);
|
|
43
|
+
// eslint-disable-next-line no-import-assign -- replacing a non-function export for test; works on Vite SSR namespaces
|
|
44
|
+
Object.defineProperty(wgpuEffectPassModule, 'EFFECT_VERTEX_WGSL', { value: 'VERTEX_WGSL\n', configurable: true });
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
afterEach(() => vi.restoreAllMocks());
|
|
48
|
+
|
|
46
49
|
function createHarness(): {
|
|
47
50
|
createTexture: ReturnType<typeof vi.fn>;
|
|
48
51
|
destroyed: string[];
|
|
@@ -1,33 +1,37 @@
|
|
|
1
1
|
import type { WgpuRenderState, WgpuRenderTarget } from '@flighthq/types/contract';
|
|
2
2
|
|
|
3
|
-
const passMock = vi.hoisted(() => ({
|
|
4
|
-
drawWgpuEffectPass: vi.fn(
|
|
5
|
-
(
|
|
6
|
-
_state: unknown,
|
|
7
|
-
_source: unknown,
|
|
8
|
-
_dest: unknown,
|
|
9
|
-
_pipeline: unknown,
|
|
10
|
-
setUniforms: (f32: Float32Array) => void,
|
|
11
|
-
) => {
|
|
12
|
-
const f32 = new Float32Array(20);
|
|
13
|
-
setUniforms(f32);
|
|
14
|
-
uniformState.uploads.push([...f32]);
|
|
15
|
-
},
|
|
16
|
-
),
|
|
17
|
-
}));
|
|
18
|
-
const pipelineMock = vi.hoisted(() => ({
|
|
19
|
-
getWgpuEffectPipeline: vi.fn((_state: unknown, _key: string, _wgsl: string, _blend: string) => ({ pipeline: {} })),
|
|
20
|
-
}));
|
|
21
|
-
const uniformState = vi.hoisted(() => ({ uploads: [] as number[][] }));
|
|
22
|
-
|
|
23
|
-
vi.mock('./wgpuEffectPass', () => passMock);
|
|
24
|
-
vi.mock('./wgpuEffectProgramCache', () => pipelineMock);
|
|
25
|
-
|
|
26
3
|
import { applyColorMatrixPassToWgpu } from './wgpuColorMatrixPass';
|
|
4
|
+
import * as wgpuEffectPassModule from './wgpuEffectPass';
|
|
5
|
+
import * as wgpuEffectProgramCacheModule from './wgpuEffectProgramCache';
|
|
6
|
+
|
|
7
|
+
const uniformState = { uploads: [] as number[][] };
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
vi.spyOn(wgpuEffectPassModule, 'drawWgpuEffectPass').mockImplementation(((
|
|
11
|
+
_state: unknown,
|
|
12
|
+
_source: unknown,
|
|
13
|
+
_dest: unknown,
|
|
14
|
+
_pipeline: unknown,
|
|
15
|
+
setUniforms: (f32: Float32Array) => void,
|
|
16
|
+
) => {
|
|
17
|
+
const f32 = new Float32Array(20);
|
|
18
|
+
setUniforms(f32);
|
|
19
|
+
uniformState.uploads.push([...f32]);
|
|
20
|
+
}) as never);
|
|
21
|
+
|
|
22
|
+
vi.spyOn(wgpuEffectProgramCacheModule, 'getWgpuEffectPipeline').mockImplementation(((
|
|
23
|
+
_state: unknown,
|
|
24
|
+
_key: string,
|
|
25
|
+
_wgsl: string,
|
|
26
|
+
_blend: string,
|
|
27
|
+
) => ({ pipeline: {} })) as never);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
afterEach(() => vi.restoreAllMocks());
|
|
27
31
|
|
|
28
32
|
function packed(matrix: ReadonlyArray<number>): readonly number[] {
|
|
29
33
|
uniformState.uploads.length = 0;
|
|
30
|
-
|
|
34
|
+
vi.mocked(wgpuEffectProgramCacheModule.getWgpuEffectPipeline).mockClear();
|
|
31
35
|
const target = {} as unknown as WgpuRenderTarget;
|
|
32
36
|
applyColorMatrixPassToWgpu({} as unknown as WgpuRenderState, target, target, matrix);
|
|
33
37
|
return uniformState.uploads[0]!;
|
|
@@ -96,7 +100,7 @@ describe('applyColorMatrixPassToWgpu', () => {
|
|
|
96
100
|
it('compiles one pipeline for every matrix, in replace blend', () => {
|
|
97
101
|
packed(IDENTITY);
|
|
98
102
|
|
|
99
|
-
const call =
|
|
103
|
+
const call = vi.mocked(wgpuEffectProgramCacheModule.getWgpuEffectPipeline).mock.calls[0]!;
|
|
100
104
|
expect(call[1]).toBe('adjustment.colorMatrix');
|
|
101
105
|
expect(call[3]).toBe('replace');
|
|
102
106
|
});
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
vi.mock('./wgpuSsaoEffect', () => ({ applySsaoEffectToWgpu: vi.fn() }));
|
|
2
|
-
|
|
3
1
|
import { createWgpuRenderStateForTest, installWgpuMock } from '@flighthq/render-wgpu/contract';
|
|
4
2
|
|
|
5
3
|
import {
|
|
@@ -8,12 +6,18 @@ import {
|
|
|
8
6
|
registerWgpuContactShadowsEffect,
|
|
9
7
|
} from './wgpuContactShadowsEffect';
|
|
10
8
|
import { getWgpuRenderEffectRunner } from './wgpuRenderEffectRegistry';
|
|
11
|
-
import
|
|
9
|
+
import * as wgpuSsaoEffectMod from './wgpuSsaoEffect';
|
|
12
10
|
|
|
13
11
|
beforeAll(() => {
|
|
14
12
|
installWgpuMock();
|
|
15
13
|
});
|
|
16
14
|
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
vi.spyOn(wgpuSsaoEffectMod, 'applySsaoEffectToWgpu').mockImplementation((() => {}) as never);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterEach(() => vi.restoreAllMocks());
|
|
20
|
+
|
|
17
21
|
describe('applyContactShadowsEffectToWgpu', () => {
|
|
18
22
|
it('maps the contact descriptor into the shared local-occlusion realization', () => {
|
|
19
23
|
applyContactShadowsEffectToWgpu({} as never, {} as never, {} as never, {
|
|
@@ -23,12 +27,17 @@ describe('applyContactShadowsEffectToWgpu', () => {
|
|
|
23
27
|
samples: 24,
|
|
24
28
|
});
|
|
25
29
|
|
|
26
|
-
expect(applySsaoEffectToWgpu).toHaveBeenCalledWith(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
expect(wgpuSsaoEffectMod.applySsaoEffectToWgpu).toHaveBeenCalledWith(
|
|
31
|
+
expect.anything(),
|
|
32
|
+
expect.anything(),
|
|
33
|
+
expect.anything(),
|
|
34
|
+
{
|
|
35
|
+
intensity: 0.75,
|
|
36
|
+
kind: 'SsaoEffect',
|
|
37
|
+
radius: 3,
|
|
38
|
+
samples: 24,
|
|
39
|
+
},
|
|
40
|
+
);
|
|
32
41
|
});
|
|
33
42
|
});
|
|
34
43
|
|
|
@@ -1,39 +1,43 @@
|
|
|
1
|
-
|
|
2
|
-
let nextTargetId = 0;
|
|
3
|
-
return {
|
|
4
|
-
acquireWgpuRenderTarget: vi.fn((_state, _pool, descriptor) => ({
|
|
5
|
-
...descriptor,
|
|
6
|
-
id: `scratch-${nextTargetId++}`,
|
|
7
|
-
texture: {},
|
|
8
|
-
})),
|
|
9
|
-
releaseWgpuRenderTarget: vi.fn(),
|
|
10
|
-
};
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
vi.mock('./wgpuEffectBlitShader', () => ({
|
|
14
|
-
applyWgpuEffectBlitOffsetPass: vi.fn(),
|
|
15
|
-
applyWgpuEffectBlitPass: vi.fn(),
|
|
16
|
-
applyWgpuEffectErasePass: vi.fn(),
|
|
17
|
-
}));
|
|
18
|
-
|
|
19
|
-
vi.mock('./wgpuEffectBoxBlur', () => ({
|
|
20
|
-
applyWgpuEffectBoxBlur: vi.fn(),
|
|
21
|
-
}));
|
|
22
|
-
|
|
23
|
-
vi.mock('./wgpuEffectPass', () => ({
|
|
24
|
-
clearWgpuEffectTarget: vi.fn(),
|
|
25
|
-
}));
|
|
26
|
-
|
|
27
|
-
vi.mock('./wgpuEffectTintShader', () => ({
|
|
28
|
-
applyWgpuEffectTintPass: vi.fn(),
|
|
29
|
-
}));
|
|
1
|
+
import * as renderWgpuContract from '@flighthq/render-wgpu/contract';
|
|
30
2
|
|
|
31
3
|
import {
|
|
32
4
|
applyDropShadowEffectToWgpu,
|
|
33
5
|
defaultWgpuDropShadowEffectRunner,
|
|
34
6
|
registerWgpuDropShadowEffect,
|
|
35
7
|
} from './wgpuDropShadowEffect';
|
|
36
|
-
import
|
|
8
|
+
import * as wgpuEffectBlitShaderMod from './wgpuEffectBlitShader';
|
|
9
|
+
import * as wgpuEffectBoxBlurMod from './wgpuEffectBoxBlur';
|
|
10
|
+
import * as wgpuEffectPassMod from './wgpuEffectPass';
|
|
11
|
+
import * as wgpuEffectTintShaderMod from './wgpuEffectTintShader';
|
|
12
|
+
|
|
13
|
+
let nextTargetId = 0;
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
nextTargetId = 0;
|
|
17
|
+
|
|
18
|
+
vi.spyOn(renderWgpuContract, 'acquireWgpuRenderTarget').mockImplementation(((
|
|
19
|
+
_state: unknown,
|
|
20
|
+
_pool: unknown,
|
|
21
|
+
descriptor: Record<string, unknown>,
|
|
22
|
+
) => ({
|
|
23
|
+
...descriptor,
|
|
24
|
+
id: `scratch-${nextTargetId++}`,
|
|
25
|
+
texture: {},
|
|
26
|
+
})) as never);
|
|
27
|
+
vi.spyOn(renderWgpuContract, 'releaseWgpuRenderTarget').mockImplementation((() => {}) as never);
|
|
28
|
+
|
|
29
|
+
vi.spyOn(wgpuEffectBlitShaderMod, 'applyWgpuEffectBlitOffsetPass').mockImplementation((() => {}) as never);
|
|
30
|
+
vi.spyOn(wgpuEffectBlitShaderMod, 'applyWgpuEffectBlitPass').mockImplementation((() => {}) as never);
|
|
31
|
+
vi.spyOn(wgpuEffectBlitShaderMod, 'applyWgpuEffectErasePass').mockImplementation((() => {}) as never);
|
|
32
|
+
|
|
33
|
+
vi.spyOn(wgpuEffectBoxBlurMod, 'applyWgpuEffectBoxBlur').mockImplementation((() => {}) as never);
|
|
34
|
+
|
|
35
|
+
vi.spyOn(wgpuEffectPassMod, 'clearWgpuEffectTarget').mockImplementation((() => {}) as never);
|
|
36
|
+
|
|
37
|
+
vi.spyOn(wgpuEffectTintShaderMod, 'applyWgpuEffectTintPass').mockImplementation((() => {}) as never);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
afterEach(() => vi.restoreAllMocks());
|
|
37
41
|
|
|
38
42
|
describe('applyDropShadowEffectToWgpu', () => {
|
|
39
43
|
it('is a function', () => {
|
|
@@ -46,8 +50,8 @@ describe('applyDropShadowEffectToWgpu', () => {
|
|
|
46
50
|
|
|
47
51
|
applyDropShadowEffectToWgpu(createState(), source, dest, createPool(), { kind: 'DropShadowEffect' });
|
|
48
52
|
|
|
49
|
-
expect(applyWgpuEffectBlitPass).toHaveBeenCalledWith(expect.anything(), source, dest);
|
|
50
|
-
expect(applyWgpuEffectErasePass).not.toHaveBeenCalled();
|
|
53
|
+
expect(wgpuEffectBlitShaderMod.applyWgpuEffectBlitPass).toHaveBeenCalledWith(expect.anything(), source, dest);
|
|
54
|
+
expect(wgpuEffectBlitShaderMod.applyWgpuEffectErasePass).not.toHaveBeenCalled();
|
|
51
55
|
});
|
|
52
56
|
|
|
53
57
|
it('hides the source when sourceMode is hide', () => {
|
|
@@ -59,8 +63,8 @@ describe('applyDropShadowEffectToWgpu', () => {
|
|
|
59
63
|
sourceMode: 'hide',
|
|
60
64
|
});
|
|
61
65
|
|
|
62
|
-
expect(applyWgpuEffectBlitPass).not.toHaveBeenCalledWith(expect.anything(), source, dest);
|
|
63
|
-
expect(applyWgpuEffectErasePass).not.toHaveBeenCalled();
|
|
66
|
+
expect(wgpuEffectBlitShaderMod.applyWgpuEffectBlitPass).not.toHaveBeenCalledWith(expect.anything(), source, dest);
|
|
67
|
+
expect(wgpuEffectBlitShaderMod.applyWgpuEffectErasePass).not.toHaveBeenCalled();
|
|
64
68
|
});
|
|
65
69
|
|
|
66
70
|
it('erases the source silhouette when sourceMode is knockout', () => {
|
|
@@ -72,8 +76,8 @@ describe('applyDropShadowEffectToWgpu', () => {
|
|
|
72
76
|
sourceMode: 'knockout',
|
|
73
77
|
});
|
|
74
78
|
|
|
75
|
-
expect(applyWgpuEffectBlitPass).not.toHaveBeenCalledWith(expect.anything(), source, dest);
|
|
76
|
-
expect(applyWgpuEffectErasePass).toHaveBeenCalledWith(expect.anything(), source, dest);
|
|
79
|
+
expect(wgpuEffectBlitShaderMod.applyWgpuEffectBlitPass).not.toHaveBeenCalledWith(expect.anything(), source, dest);
|
|
80
|
+
expect(wgpuEffectBlitShaderMod.applyWgpuEffectErasePass).toHaveBeenCalledWith(expect.anything(), source, dest);
|
|
77
81
|
});
|
|
78
82
|
});
|
|
79
83
|
|
|
@@ -83,10 +87,6 @@ describe('defaultWgpuDropShadowEffectRunner', () => {
|
|
|
83
87
|
});
|
|
84
88
|
});
|
|
85
89
|
|
|
86
|
-
beforeEach(() => {
|
|
87
|
-
vi.clearAllMocks();
|
|
88
|
-
});
|
|
89
|
-
|
|
90
90
|
function createState(): never {
|
|
91
91
|
return {} as never;
|
|
92
92
|
}
|
|
@@ -1,30 +1,42 @@
|
|
|
1
1
|
import type { WgpuRenderState, WgpuRenderTarget } from '@flighthq/types/contract';
|
|
2
2
|
|
|
3
|
-
const passMock = vi.hoisted(() => ({
|
|
4
|
-
createWgpuEffectPipeline: vi.fn((_state: unknown, wgsl: string, blend?: string) => ({ blend, wgsl })),
|
|
5
|
-
drawWgpuEffectPass: vi.fn(
|
|
6
|
-
(_state: unknown, source: unknown, dest: unknown, pipeline: unknown, setUniforms: (f32: Float32Array) => void) => {
|
|
7
|
-
const f32 = new Float32Array(4);
|
|
8
|
-
setUniforms(f32);
|
|
9
|
-
recorded.draws.push({ dest, pipeline, source, uniforms: [...f32] });
|
|
10
|
-
},
|
|
11
|
-
),
|
|
12
|
-
}));
|
|
13
|
-
const recorded = vi.hoisted(() => ({
|
|
14
|
-
draws: [] as { dest: unknown; pipeline: unknown; source: unknown; uniforms: number[] }[],
|
|
15
|
-
}));
|
|
16
|
-
|
|
17
|
-
vi.mock('./wgpuEffectPass', () => passMock);
|
|
18
|
-
|
|
19
3
|
import {
|
|
20
4
|
applyWgpuEffectBlitOffsetPass,
|
|
21
5
|
applyWgpuEffectBlitPass,
|
|
22
6
|
applyWgpuEffectErasePass,
|
|
23
7
|
} from './wgpuEffectBlitShader';
|
|
8
|
+
import * as wgpuEffectPassMod from './wgpuEffectPass';
|
|
9
|
+
|
|
10
|
+
const recorded = {
|
|
11
|
+
draws: [] as { dest: unknown; pipeline: unknown; source: unknown; uniforms: number[] }[],
|
|
12
|
+
};
|
|
24
13
|
|
|
25
14
|
const SOURCE_WIDTH = 64;
|
|
26
15
|
const SOURCE_HEIGHT = 32;
|
|
27
16
|
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
recorded.draws.length = 0;
|
|
19
|
+
|
|
20
|
+
vi.spyOn(wgpuEffectPassMod, 'createWgpuEffectPipeline').mockImplementation(((
|
|
21
|
+
_state: unknown,
|
|
22
|
+
wgsl: string,
|
|
23
|
+
blend?: string,
|
|
24
|
+
) => ({ blend, wgsl })) as never);
|
|
25
|
+
vi.spyOn(wgpuEffectPassMod, 'drawWgpuEffectPass').mockImplementation(((
|
|
26
|
+
_state: unknown,
|
|
27
|
+
source: unknown,
|
|
28
|
+
dest: unknown,
|
|
29
|
+
pipeline: unknown,
|
|
30
|
+
setUniforms: (f32: Float32Array) => void,
|
|
31
|
+
) => {
|
|
32
|
+
const f32 = new Float32Array(4);
|
|
33
|
+
setUniforms(f32);
|
|
34
|
+
recorded.draws.push({ dest, pipeline, source, uniforms: [...f32] });
|
|
35
|
+
}) as never);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
afterEach(() => vi.restoreAllMocks());
|
|
39
|
+
|
|
28
40
|
function createState(): WgpuRenderState {
|
|
29
41
|
return {} as unknown as WgpuRenderState;
|
|
30
42
|
}
|
|
@@ -35,7 +47,7 @@ function createTarget(id: string): WgpuRenderTarget {
|
|
|
35
47
|
|
|
36
48
|
function reset(): void {
|
|
37
49
|
recorded.draws.length = 0;
|
|
38
|
-
|
|
50
|
+
vi.mocked(wgpuEffectPassMod.createWgpuEffectPipeline).mockClear();
|
|
39
51
|
}
|
|
40
52
|
|
|
41
53
|
function offsetFor(dx: number, dy: number): readonly number[] {
|
|
@@ -99,7 +111,7 @@ describe('applyWgpuEffectBlitOffsetPass', () => {
|
|
|
99
111
|
applyWgpuEffectBlitOffsetPass(state, source, dest, 1, 1);
|
|
100
112
|
applyWgpuEffectBlitOffsetPass(state, source, dest, 2, 2);
|
|
101
113
|
|
|
102
|
-
expect(
|
|
114
|
+
expect(wgpuEffectPassMod.createWgpuEffectPipeline).toHaveBeenCalledTimes(1);
|
|
103
115
|
});
|
|
104
116
|
});
|
|
105
117
|
|
|
@@ -127,7 +139,7 @@ describe('applyWgpuEffectBlitPass', () => {
|
|
|
127
139
|
applyWgpuEffectBlitOffsetPass(state, source, dest, 1, 1);
|
|
128
140
|
|
|
129
141
|
expect(new Set(recorded.draws.map((draw) => draw.pipeline)).size).toBe(3);
|
|
130
|
-
expect(
|
|
142
|
+
expect(wgpuEffectPassMod.createWgpuEffectPipeline).toHaveBeenCalledTimes(3);
|
|
131
143
|
});
|
|
132
144
|
});
|
|
133
145
|
|
|
@@ -140,7 +152,7 @@ describe('applyWgpuEffectErasePass', () => {
|
|
|
140
152
|
|
|
141
153
|
applyWgpuEffectErasePass(createState(), createTarget('source'), createTarget('dest'));
|
|
142
154
|
|
|
143
|
-
expect(
|
|
155
|
+
expect(vi.mocked(wgpuEffectPassMod.createWgpuEffectPipeline).mock.calls[0]![2]).toBe('erase');
|
|
144
156
|
});
|
|
145
157
|
|
|
146
158
|
it('binds the mask as its source', () => {
|
|
@@ -1,19 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { applyWgpuEffectBoxBlur } from './wgpuEffectBoxBlur';
|
|
2
|
+
import * as wgpuEffectPassMod from './wgpuEffectPass';
|
|
3
|
+
|
|
4
|
+
const uniformSnapshots: number[][] = [];
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
uniformSnapshots.length = 0;
|
|
8
|
+
|
|
9
|
+
vi.spyOn(wgpuEffectPassMod, 'createWgpuEffectPipeline').mockReturnValue({
|
|
10
|
+
blendMode: 'replace',
|
|
11
|
+
pipeline: {},
|
|
12
|
+
} as never);
|
|
13
|
+
vi.spyOn(wgpuEffectPassMod, 'drawWgpuEffectPass').mockImplementation(((
|
|
14
|
+
_state: unknown,
|
|
15
|
+
_source: unknown,
|
|
16
|
+
_dest: unknown,
|
|
17
|
+
_pipeline: unknown,
|
|
18
|
+
setUniforms: Function,
|
|
19
|
+
) => {
|
|
8
20
|
const f32 = new Float32Array(16);
|
|
9
21
|
const i32 = new Int32Array(f32.buffer);
|
|
10
22
|
setUniforms(f32, i32);
|
|
11
|
-
|
|
12
|
-
})
|
|
13
|
-
})
|
|
23
|
+
uniformSnapshots.push(Array.from(f32));
|
|
24
|
+
}) as never);
|
|
25
|
+
});
|
|
14
26
|
|
|
15
|
-
|
|
16
|
-
import { createWgpuEffectPipeline, drawWgpuEffectPass } from './wgpuEffectPass';
|
|
27
|
+
afterEach(() => vi.restoreAllMocks());
|
|
17
28
|
|
|
18
29
|
describe('applyWgpuEffectBoxBlur', () => {
|
|
19
30
|
it('is a function', () => {
|
|
@@ -28,12 +39,16 @@ describe('applyWgpuEffectBoxBlur', () => {
|
|
|
28
39
|
passes: 1,
|
|
29
40
|
});
|
|
30
41
|
|
|
31
|
-
expect(createWgpuEffectPipeline).toHaveBeenCalledWith(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
expect(
|
|
42
|
+
expect(wgpuEffectPassMod.createWgpuEffectPipeline).toHaveBeenCalledWith(
|
|
43
|
+
expect.anything(),
|
|
44
|
+
expect.any(String),
|
|
45
|
+
'replace',
|
|
46
|
+
);
|
|
47
|
+
expect(uniformSnapshots[0]![4]).toBeCloseTo(0.1);
|
|
48
|
+
expect(uniformSnapshots[0]![5]).toBeCloseTo(0.2);
|
|
49
|
+
expect(uniformSnapshots[0]![6]).toBeCloseTo(0.3);
|
|
50
|
+
expect(uniformSnapshots[0]![7]).toBeCloseTo(0.4);
|
|
51
|
+
expect(uniformSnapshots[0]![9]).toBe(1);
|
|
37
52
|
});
|
|
38
53
|
|
|
39
54
|
it('uses the replacement blur pipeline for zero-radius copies', () => {
|
|
@@ -45,18 +60,13 @@ describe('applyWgpuEffectBoxBlur', () => {
|
|
|
45
60
|
passes: 1,
|
|
46
61
|
});
|
|
47
62
|
|
|
48
|
-
expect(drawWgpuEffectPass).toHaveBeenCalledTimes(1);
|
|
49
|
-
expect(vi.mocked(drawWgpuEffectPass).mock.calls[0]![2]).toBe(dest);
|
|
50
|
-
expect(
|
|
51
|
-
expect(
|
|
63
|
+
expect(wgpuEffectPassMod.drawWgpuEffectPass).toHaveBeenCalledTimes(1);
|
|
64
|
+
expect(vi.mocked(wgpuEffectPassMod.drawWgpuEffectPass).mock.calls[0]![2]).toBe(dest);
|
|
65
|
+
expect(uniformSnapshots[0]![8]).toBe(0);
|
|
66
|
+
expect(uniformSnapshots[0]![9]).toBe(0);
|
|
52
67
|
});
|
|
53
68
|
});
|
|
54
69
|
|
|
55
|
-
beforeEach(() => {
|
|
56
|
-
vi.clearAllMocks();
|
|
57
|
-
mockState.uniformSnapshots.length = 0;
|
|
58
|
-
});
|
|
59
|
-
|
|
60
70
|
function createState(): never {
|
|
61
71
|
return {} as never;
|
|
62
72
|
}
|