@flighthq/render-wgpu 0.5.1-next.1069.554a2ac → 0.5.1-next.1201.9b49dee
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/dist/contract.d.ts +9 -6
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +9 -6
- package/dist/contract.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/wgpuDraw.js +1 -1
- package/dist/wgpuDraw.js.map +1 -1
- package/dist/wgpuFrame.d.ts +23 -0
- package/dist/wgpuFrame.d.ts.map +1 -0
- package/dist/{wgpuBackground.js → wgpuFrame.js} +67 -121
- package/dist/wgpuFrame.js.map +1 -0
- package/dist/wgpuFullscreenPass.d.ts +2 -2
- package/dist/wgpuFullscreenPass.d.ts.map +1 -1
- package/dist/wgpuFullscreenPass.js +1 -2
- package/dist/wgpuFullscreenPass.js.map +1 -1
- package/dist/wgpuHost.d.ts +6 -6
- package/dist/wgpuHost.d.ts.map +1 -1
- package/dist/wgpuHost.js +17 -3
- package/dist/wgpuHost.js.map +1 -1
- package/dist/wgpuRenderPass.d.ts +9 -0
- package/dist/wgpuRenderPass.d.ts.map +1 -0
- package/dist/wgpuRenderPass.js +222 -0
- package/dist/wgpuRenderPass.js.map +1 -0
- package/dist/wgpuRenderState.d.ts +3 -4
- package/dist/wgpuRenderState.d.ts.map +1 -1
- package/dist/wgpuRenderState.js +17 -88
- package/dist/wgpuRenderState.js.map +1 -1
- package/dist/wgpuRenderTarget.d.ts +12 -29
- package/dist/wgpuRenderTarget.d.ts.map +1 -1
- package/dist/wgpuRenderTarget.js +19 -252
- package/dist/wgpuRenderTarget.js.map +1 -1
- package/dist/wgpuRenderTargetPool.d.ts +5 -5
- package/dist/wgpuRenderTargetPool.d.ts.map +1 -1
- package/dist/wgpuRenderTargetPool.js +7 -7
- package/dist/wgpuRenderTargetPool.js.map +1 -1
- package/dist/wgpuRenderTexture.d.ts +5 -4
- package/dist/wgpuRenderTexture.d.ts.map +1 -1
- package/dist/wgpuRenderTexture.js +14 -16
- package/dist/wgpuRenderTexture.js.map +1 -1
- package/dist/wgpuRenderTexturePool.js +1 -3
- package/dist/wgpuRenderTexturePool.js.map +1 -1
- package/dist/wgpuScissor.d.ts +1 -1
- package/dist/wgpuScissor.d.ts.map +1 -1
- package/dist/wgpuScissor.js +7 -4
- package/dist/wgpuScissor.js.map +1 -1
- package/dist/wgpuScreenAntialias.d.ts +3 -0
- package/dist/wgpuScreenAntialias.d.ts.map +1 -0
- package/dist/wgpuScreenAntialias.js +119 -0
- package/dist/wgpuScreenAntialias.js.map +1 -0
- package/dist/wgpuScreenCapture.d.ts +5 -0
- package/dist/wgpuScreenCapture.d.ts.map +1 -0
- package/dist/{wgpuSurface.js → wgpuScreenCapture.js} +55 -73
- package/dist/wgpuScreenCapture.js.map +1 -0
- package/dist/wgpuScreenRenderTarget.d.ts +9 -0
- package/dist/wgpuScreenRenderTarget.d.ts.map +1 -0
- package/dist/wgpuScreenRenderTarget.js +123 -0
- package/dist/wgpuScreenRenderTarget.js.map +1 -0
- package/dist/wgpuShader.js +2 -2
- package/dist/wgpuShader.js.map +1 -1
- package/dist/wgpuTestHelper.d.ts +8 -2
- package/dist/wgpuTestHelper.d.ts.map +1 -1
- package/dist/wgpuTestHelper.js +38 -3
- package/dist/wgpuTestHelper.js.map +1 -1
- package/dist/wgpuTextureRenderTarget.d.ts +18 -0
- package/dist/wgpuTextureRenderTarget.d.ts.map +1 -0
- package/dist/wgpuTextureRenderTarget.js +122 -0
- package/dist/wgpuTextureRenderTarget.js.map +1 -0
- package/package.json +11 -11
- package/src/wgpuDraw.test.ts +8 -8
- package/src/{wgpuBackground.test.ts → wgpuFrame.test.ts} +34 -20
- package/src/wgpuFullscreenPass.test.ts +7 -7
- package/src/wgpuHost.test.ts +28 -82
- package/src/wgpuRenderPass.test.ts +258 -0
- package/src/wgpuRenderState.test.ts +34 -313
- package/src/wgpuRenderTarget.test.ts +44 -219
- package/src/wgpuRenderTargetPool.test.ts +21 -21
- package/src/wgpuRenderTexture.test.ts +17 -15
- package/src/wgpuScissor.test.ts +43 -13
- package/src/wgpuScreenAntialias.test.ts +60 -0
- package/src/wgpuScreenCapture.test.ts +156 -0
- package/src/wgpuScreenRenderTarget.test.ts +152 -0
- package/src/wgpuShader.test.ts +5 -6
- package/src/wgpuTextureRenderTarget.test.ts +129 -0
- package/src/wgpuTextureResolver.test.ts +5 -5
- package/dist/wgpuAntialias.d.ts +0 -14
- package/dist/wgpuAntialias.d.ts.map +0 -1
- package/dist/wgpuAntialias.js +0 -155
- package/dist/wgpuAntialias.js.map +0 -1
- package/dist/wgpuBackground.d.ts +0 -20
- package/dist/wgpuBackground.d.ts.map +0 -1
- package/dist/wgpuBackground.js.map +0 -1
- package/dist/wgpuElement.d.ts +0 -7
- package/dist/wgpuElement.d.ts.map +0 -1
- package/dist/wgpuElement.js +0 -20
- package/dist/wgpuElement.js.map +0 -1
- package/dist/wgpuSurface.d.ts +0 -6
- package/dist/wgpuSurface.d.ts.map +0 -1
- package/dist/wgpuSurface.js.map +0 -1
- package/src/wgpuAntialias.test.ts +0 -239
- package/src/wgpuElement.test.ts +0 -145
- package/src/wgpuSurface.test.ts +0 -141
|
@@ -1,170 +1,41 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { WgpuRenderTarget } from '@flighthq/types/contract';
|
|
1
|
+
import type { WgpuRenderTarget, WgpuScreenRenderTarget } from '@flighthq/types/contract';
|
|
3
2
|
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import { beginWgpuRenderPass, endWgpuRenderPass } from './wgpuRenderPass';
|
|
4
|
+
import { getWgpuRenderStateRuntime } from './wgpuRenderState';
|
|
5
|
+
import { declareWgpuRenderTargetColorSpace, getWgpuRenderTargetSupersampleScale } from './wgpuRenderTarget';
|
|
7
6
|
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
getWgpuRenderTargetSupersampleScale,
|
|
15
|
-
initializeWgpuRenderTarget,
|
|
16
|
-
resizeWgpuRenderTarget,
|
|
17
|
-
setWgpuRenderTransform2D,
|
|
18
|
-
} from './wgpuRenderTarget';
|
|
19
|
-
import { createWgpuRenderStateForTest, installWgpuMock } from './wgpuTestHelper';
|
|
7
|
+
beginWgpuScreenRenderPassForTest,
|
|
8
|
+
createWgpuRenderStateForTest,
|
|
9
|
+
createWgpuScreenRenderTargetForTest,
|
|
10
|
+
installWgpuMock,
|
|
11
|
+
} from './wgpuTestHelper';
|
|
12
|
+
import { createWgpuTextureRenderTarget } from './wgpuTextureRenderTarget';
|
|
20
13
|
|
|
21
14
|
beforeAll(() => {
|
|
22
15
|
installWgpuMock();
|
|
23
16
|
});
|
|
24
17
|
|
|
25
|
-
describe('beginWgpuRenderPass', () => {
|
|
26
|
-
it('sets the render target viewport', async () => {
|
|
27
|
-
const state = await createWgpuRenderStateForTest();
|
|
28
|
-
renderWgpuBackground(state);
|
|
29
|
-
const target = createWgpuRenderTarget(state, 64, 64);
|
|
30
|
-
beginWgpuRenderPass(state, target);
|
|
31
|
-
expect(getWgpuRenderStateRuntime(state).renderTargetViewport?.width).toBe(64);
|
|
32
|
-
endWgpuRenderPass(state);
|
|
33
|
-
submitWgpuRenderPass(state);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('switches the render target and restores it on end', async () => {
|
|
37
|
-
const state = await createWgpuRenderStateForTest();
|
|
38
|
-
renderWgpuBackground(state);
|
|
39
|
-
|
|
40
|
-
const target = createWgpuRenderTarget(state, 128, 128);
|
|
41
|
-
beginWgpuRenderPass(state, target);
|
|
42
|
-
|
|
43
|
-
const runtime = getWgpuRenderStateRuntime(state);
|
|
44
|
-
expect(runtime.renderTargetViewport?.width).toBe(128);
|
|
45
|
-
|
|
46
|
-
endWgpuRenderPass(state);
|
|
47
|
-
expect(runtime.renderTargetViewport).toBeNull();
|
|
48
|
-
|
|
49
|
-
submitWgpuRenderPass(state);
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
describe('createWgpuRenderTarget', () => {
|
|
54
|
-
it('returns a target with texture, view, binding cache, and depth-stencil', async () => {
|
|
55
|
-
const state = await createWgpuRenderStateForTest();
|
|
56
|
-
const target = createWgpuRenderTarget(state, 256, 256);
|
|
57
|
-
expect(target.texture).toBeDefined();
|
|
58
|
-
expect(target.view).toBeDefined();
|
|
59
|
-
expect(target.bindings).toBeDefined();
|
|
60
|
-
expect(target.depthStencilTexture).toBeDefined();
|
|
61
|
-
expect(target.width).toBe(256);
|
|
62
|
-
expect(target.height).toBe(256);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('clamps to minimum 1×1', async () => {
|
|
66
|
-
const state = await createWgpuRenderStateForTest();
|
|
67
|
-
const target = createWgpuRenderTarget(state, 0, 0);
|
|
68
|
-
expect(target.width).toBe(1);
|
|
69
|
-
expect(target.height).toBe(1);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('defaults to sRGB and accepts a linear content declaration', async () => {
|
|
73
|
-
const state = await createWgpuRenderStateForTest();
|
|
74
|
-
expect(createWgpuRenderTarget(state, 16, 16).colorSpace).toBe('srgb');
|
|
75
|
-
expect(createWgpuRenderTarget(state, 16, 16, state.format, 'linear').colorSpace).toBe('linear');
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('realizes four coverage samples as a 2x extent in each axis', async () => {
|
|
79
|
-
const state = await createWgpuRenderStateForTest();
|
|
80
|
-
const target = createWgpuRenderTarget(state, 64, 48, state.format, 'srgb', 4);
|
|
81
|
-
expect(target.width).toBe(128);
|
|
82
|
-
expect(target.height).toBe(96);
|
|
83
|
-
expect(target.sampleCount).toBe(4);
|
|
84
|
-
expect(target.texture.width).toBe(128);
|
|
85
|
-
expect(target.texture.height).toBe(96);
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
|
|
89
18
|
describe('declareWgpuRenderTargetColorSpace', () => {
|
|
90
|
-
it('
|
|
91
|
-
const state = await createWgpuRenderStateForTest();
|
|
92
|
-
renderWgpuBackground(state);
|
|
93
|
-
const runtime = getWgpuRenderStateRuntime(state);
|
|
94
|
-
const beginRenderPass = vi.spyOn(runtime.commandEncoder!, 'beginRenderPass');
|
|
95
|
-
const target = createWgpuRenderTarget(state, 32, 32, state.format, 'linear');
|
|
96
|
-
target.clearColors = [0x0a0c10ff];
|
|
97
|
-
|
|
98
|
-
beginWgpuRenderPass(state, target);
|
|
99
|
-
const attachment = Array.from(beginRenderPass.mock.calls.at(-1)![0].colorAttachments)[0]!;
|
|
100
|
-
const linearClear = attachment.clearValue as GPUColorDict;
|
|
101
|
-
expect(beginRenderPass).toHaveBeenCalledTimes(1);
|
|
102
|
-
expect(linearClear.r).toBeCloseTo(0.00303527);
|
|
103
|
-
expect(linearClear.g).toBeCloseTo(0.00367651);
|
|
104
|
-
expect(linearClear.b).toBeCloseTo(0.00518152);
|
|
105
|
-
expect(linearClear.a).toBe(1);
|
|
106
|
-
|
|
107
|
-
endWgpuRenderPass(state);
|
|
108
|
-
submitWgpuRenderPass(state);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it('stamps the current target and restores the enclosing target on end', async () => {
|
|
19
|
+
it('stamps the innermost open target and reports false outside a pass', async () => {
|
|
112
20
|
const state = await createWgpuRenderStateForTest();
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
const inner = createWgpuRenderTarget(state, 16, 16);
|
|
21
|
+
const outer = createWgpuTextureRenderTarget(state, 32, 32);
|
|
22
|
+
const inner = createWgpuTextureRenderTarget(state, 16, 16);
|
|
116
23
|
|
|
117
24
|
expect(declareWgpuRenderTargetColorSpace(state, 'linear')).toBe(false);
|
|
118
|
-
|
|
25
|
+
|
|
26
|
+
const screenPass = beginWgpuScreenRenderPassForTest(state);
|
|
27
|
+
const outerPass = beginWgpuRenderPass(state, outer);
|
|
119
28
|
expect(declareWgpuRenderTargetColorSpace(state, 'linear')).toBe(true);
|
|
120
|
-
beginWgpuRenderPass(state, inner);
|
|
29
|
+
const innerPass = beginWgpuRenderPass(state, inner);
|
|
121
30
|
expect(declareWgpuRenderTargetColorSpace(state, 'linear')).toBe(true);
|
|
122
|
-
|
|
31
|
+
|
|
32
|
+
endWgpuRenderPass(innerPass);
|
|
123
33
|
expect(getWgpuRenderStateRuntime(state).currentRenderTarget).toBe(outer);
|
|
124
|
-
endWgpuRenderPass(
|
|
125
|
-
|
|
34
|
+
endWgpuRenderPass(outerPass);
|
|
35
|
+
endWgpuRenderPass(screenPass);
|
|
36
|
+
|
|
126
37
|
expect(outer.colorSpace).toBe('linear');
|
|
127
38
|
expect(inner.colorSpace).toBe('linear');
|
|
128
|
-
submitWgpuRenderPass(state);
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
describe('destroyWgpuRenderTarget', () => {
|
|
133
|
-
it('calls destroy on texture and depth-stencil', async () => {
|
|
134
|
-
const state = await createWgpuRenderStateForTest();
|
|
135
|
-
const target = createWgpuRenderTarget(state, 64, 64);
|
|
136
|
-
const destroyTexture = vi.spyOn(target.texture, 'destroy');
|
|
137
|
-
const destroyDepth = vi.spyOn(target.depthStencilTexture, 'destroy');
|
|
138
|
-
destroyWgpuRenderTarget(state, target);
|
|
139
|
-
expect(destroyTexture).toHaveBeenCalled();
|
|
140
|
-
expect(destroyDepth).toHaveBeenCalled();
|
|
141
|
-
});
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
describe('drawWgpuRenderTargetResult', () => {
|
|
145
|
-
it('does not throw with an open render pass', async () => {
|
|
146
|
-
const state = await createWgpuRenderStateForTest();
|
|
147
|
-
renderWgpuBackground(state);
|
|
148
|
-
const target = createWgpuRenderTarget(state, 64, 64);
|
|
149
|
-
const fakeNode = {
|
|
150
|
-
alpha: 1,
|
|
151
|
-
material: null,
|
|
152
|
-
transform2D: { a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0 },
|
|
153
|
-
};
|
|
154
|
-
expect(() => drawWgpuRenderTargetResult(state, fakeNode, target, createMatrix())).not.toThrow();
|
|
155
|
-
submitWgpuRenderPass(state);
|
|
156
|
-
});
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
describe('endWgpuRenderPass', () => {
|
|
160
|
-
it('restores null renderTargetViewport', async () => {
|
|
161
|
-
const state = await createWgpuRenderStateForTest();
|
|
162
|
-
renderWgpuBackground(state);
|
|
163
|
-
const target = createWgpuRenderTarget(state, 32, 32);
|
|
164
|
-
beginWgpuRenderPass(state, target);
|
|
165
|
-
endWgpuRenderPass(state);
|
|
166
|
-
expect(getWgpuRenderStateRuntime(state).renderTargetViewport).toBeNull();
|
|
167
|
-
submitWgpuRenderPass(state);
|
|
168
39
|
});
|
|
169
40
|
});
|
|
170
41
|
|
|
@@ -173,9 +44,22 @@ describe('getWgpuRenderTargetSupersampleScale', () => {
|
|
|
173
44
|
// if the 2D projection divides by that physical width it maps logical x = 800 to NDC 0, and the scene
|
|
174
45
|
// fills a quarter of its own target. Measured on effect-sepia before the repair: the WebGPU frame
|
|
175
46
|
// equalled the WebGL frame sampled at (2x, 2y) on 12 of 12 grid points; after, 48 of 48 matched 1:1.
|
|
176
|
-
it('reports 2 for a supersampled target and 1 for a single-sample one', () => {
|
|
177
|
-
expect(getWgpuRenderTargetSupersampleScale({ sampleCount: 4 } as WgpuRenderTarget)).toBe(2);
|
|
178
|
-
expect(getWgpuRenderTargetSupersampleScale({ sampleCount: 1 } as WgpuRenderTarget)).toBe(1);
|
|
47
|
+
it('reports 2 for a supersampled texture target and 1 for a single-sample one', () => {
|
|
48
|
+
expect(getWgpuRenderTargetSupersampleScale({ context: null, sampleCount: 4 } as WgpuRenderTarget)).toBe(2);
|
|
49
|
+
expect(getWgpuRenderTargetSupersampleScale({ context: null, sampleCount: 1 } as WgpuRenderTarget)).toBe(1);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// A screen target reaches supersampling by a different route — an antialias option rather than a sample
|
|
53
|
+
// count — and the two must resolve to one number, or the scissor and the projection disagree about which
|
|
54
|
+
// coordinate space the frame is in.
|
|
55
|
+
it('reads a screen target from its antialias option, not its sample count', async () => {
|
|
56
|
+
const state = await createWgpuRenderStateForTest();
|
|
57
|
+
const plain = createWgpuScreenRenderTargetForTest(state);
|
|
58
|
+
const antialiased = createWgpuScreenRenderTargetForTest(state, { antialias: true });
|
|
59
|
+
|
|
60
|
+
expect(getWgpuRenderTargetSupersampleScale(plain)).toBe(1);
|
|
61
|
+
expect(getWgpuRenderTargetSupersampleScale(antialiased)).toBe(2);
|
|
62
|
+
expect(antialiased.sampleCount).toBe(1);
|
|
179
63
|
});
|
|
180
64
|
|
|
181
65
|
// Dividing the physical extent by the scale has to land back on the logical extent exactly, or the
|
|
@@ -189,77 +73,18 @@ describe('getWgpuRenderTargetSupersampleScale', () => {
|
|
|
189
73
|
[1, 4],
|
|
190
74
|
] as const) {
|
|
191
75
|
const physical = Math.max(1, Math.ceil(logical)) * (sampleCount === 4 ? 2 : 1);
|
|
192
|
-
const scale = getWgpuRenderTargetSupersampleScale({ sampleCount } as WgpuRenderTarget);
|
|
76
|
+
const scale = getWgpuRenderTargetSupersampleScale({ context: null, sampleCount } as WgpuRenderTarget);
|
|
193
77
|
|
|
194
78
|
expect(physical / scale).toBe(logical);
|
|
195
79
|
}
|
|
196
80
|
});
|
|
197
|
-
});
|
|
198
81
|
|
|
199
|
-
|
|
200
|
-
it('is the construction initializer of createWgpuRenderTarget', () => {
|
|
201
|
-
expect(typeof initializeWgpuRenderTarget).toBe('function');
|
|
202
|
-
});
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
describe('resizeWgpuRenderTarget', () => {
|
|
206
|
-
it('preserves the allocation when dimensions are unchanged', async () => {
|
|
207
|
-
const state = await createWgpuRenderStateForTest();
|
|
208
|
-
const target = createWgpuRenderTarget(state, 64, 64);
|
|
209
|
-
const texture = target.texture;
|
|
210
|
-
const depthStencilTexture = target.depthStencilTexture;
|
|
211
|
-
const sampler = getWgpuRenderStateDeviceResources(state).linearSampler;
|
|
212
|
-
const bindGroup = resolveWgpuSmoothingBindGroup(state, target, true);
|
|
213
|
-
|
|
214
|
-
resizeWgpuRenderTarget(state, target, 64, 64);
|
|
215
|
-
|
|
216
|
-
expect(target.texture).toBe(texture);
|
|
217
|
-
expect(target.depthStencilTexture).toBe(depthStencilTexture);
|
|
218
|
-
// The view survived, so its cached bindings must survive with it.
|
|
219
|
-
expect(target.bindings.get(sampler)).toBe(bindGroup);
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
it('updates width, height, and drops bindings that referenced the old view', async () => {
|
|
223
|
-
const state = await createWgpuRenderStateForTest();
|
|
224
|
-
const target = createWgpuRenderTarget(state, 64, 64);
|
|
225
|
-
const previousBindGroup = resolveWgpuSmoothingBindGroup(state, target, true);
|
|
226
|
-
resizeWgpuRenderTarget(state, target, 200, 150);
|
|
227
|
-
expect(target.width).toBe(200);
|
|
228
|
-
expect(target.height).toBe(150);
|
|
229
|
-
// Reallocation replaces the view, so every binding over the old one is dropped and rebuilt.
|
|
230
|
-
expect(target.bindings.size).toBe(0);
|
|
231
|
-
expect(resolveWgpuSmoothingBindGroup(state, target, true)).not.toBe(previousBindGroup);
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
it('reallocates when the effective sample count changes', async () => {
|
|
235
|
-
const state = await createWgpuRenderStateForTest();
|
|
236
|
-
const target = createWgpuRenderTarget(state, 64, 48);
|
|
237
|
-
const previousTexture = target.texture;
|
|
238
|
-
|
|
239
|
-
resizeWgpuRenderTarget(state, target, 64, 48, 4);
|
|
240
|
-
|
|
241
|
-
expect(target.width).toBe(128);
|
|
242
|
-
expect(target.height).toBe(96);
|
|
243
|
-
expect(target.sampleCount).toBe(4);
|
|
244
|
-
expect(target.texture).not.toBe(previousTexture);
|
|
245
|
-
});
|
|
246
|
-
});
|
|
247
|
-
describe('setWgpuRenderTransform2D', () => {
|
|
248
|
-
it('installs a copy of the transform, restored by the enclosing pass', async () => {
|
|
82
|
+
it('inverts the screen allocator exactly for a supersampled surface', async () => {
|
|
249
83
|
const state = await createWgpuRenderStateForTest();
|
|
250
|
-
|
|
251
|
-
const original = state.renderTransform2D;
|
|
252
|
-
const target = createWgpuRenderTarget(state, 32, 32);
|
|
253
|
-
const bake = createMatrix();
|
|
254
|
-
bake.tx = 42;
|
|
255
|
-
|
|
256
|
-
beginWgpuRenderPass(state, target);
|
|
257
|
-
setWgpuRenderTransform2D(state, bake);
|
|
258
|
-
expect(state.renderTransform2D?.tx).toBe(42);
|
|
259
|
-
expect(state.renderTransform2D).not.toBe(bake);
|
|
260
|
-
endWgpuRenderPass(state);
|
|
84
|
+
const screen: WgpuScreenRenderTarget = createWgpuScreenRenderTargetForTest(state, { antialias: true }, 801, 599);
|
|
261
85
|
|
|
262
|
-
expect(
|
|
263
|
-
|
|
86
|
+
expect(screen.width).toBe(1602);
|
|
87
|
+
expect(screen.width / getWgpuRenderTargetSupersampleScale(screen)).toBe(801);
|
|
88
|
+
expect(screen.height / getWgpuRenderTargetSupersampleScale(screen)).toBe(599);
|
|
264
89
|
});
|
|
265
90
|
});
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
acquireWgpuTextureRenderTarget,
|
|
3
3
|
createWgpuRenderTargetPool,
|
|
4
4
|
destroyWgpuRenderTargetPool,
|
|
5
5
|
initializeWgpuRenderTargetPool,
|
|
6
|
-
|
|
6
|
+
releaseWgpuTextureRenderTarget,
|
|
7
7
|
} from './wgpuRenderTargetPool';
|
|
8
8
|
import { createWgpuRenderStateForTest, installWgpuMock } from './wgpuTestHelper';
|
|
9
9
|
|
|
@@ -11,22 +11,22 @@ beforeAll(() => {
|
|
|
11
11
|
installWgpuMock();
|
|
12
12
|
});
|
|
13
13
|
|
|
14
|
-
describe('
|
|
14
|
+
describe('acquireWgpuTextureRenderTarget', () => {
|
|
15
15
|
it('reuses a released target matching width, height, and format', async () => {
|
|
16
16
|
const state = await createWgpuRenderStateForTest();
|
|
17
17
|
const pool = createWgpuRenderTargetPool();
|
|
18
|
-
const first =
|
|
19
|
-
|
|
20
|
-
const second =
|
|
18
|
+
const first = acquireWgpuTextureRenderTarget(state, pool, { width: 64, height: 64 });
|
|
19
|
+
releaseWgpuTextureRenderTarget(pool, first);
|
|
20
|
+
const second = acquireWgpuTextureRenderTarget(state, pool, { width: 64, height: 64 });
|
|
21
21
|
expect(second).toBe(first);
|
|
22
22
|
});
|
|
23
23
|
|
|
24
24
|
it('allocates a distinct target when no free target matches the format', async () => {
|
|
25
25
|
const state = await createWgpuRenderStateForTest();
|
|
26
26
|
const pool = createWgpuRenderTargetPool();
|
|
27
|
-
const eight =
|
|
28
|
-
|
|
29
|
-
const hdr =
|
|
27
|
+
const eight = acquireWgpuTextureRenderTarget(state, pool, { width: 64, height: 64 });
|
|
28
|
+
releaseWgpuTextureRenderTarget(pool, eight);
|
|
29
|
+
const hdr = acquireWgpuTextureRenderTarget(state, pool, { width: 64, height: 64, format: 'rgba16float' });
|
|
30
30
|
expect(hdr).not.toBe(eight);
|
|
31
31
|
expect(hdr.format).toBe('rgba16float');
|
|
32
32
|
});
|
|
@@ -34,26 +34,26 @@ describe('acquireWgpuRenderTarget', () => {
|
|
|
34
34
|
it('realizes and pools a four-sample request at twice the extent in each axis', async () => {
|
|
35
35
|
const state = await createWgpuRenderStateForTest();
|
|
36
36
|
const pool = createWgpuRenderTargetPool();
|
|
37
|
-
const supersampled =
|
|
37
|
+
const supersampled = acquireWgpuTextureRenderTarget(state, pool, { width: 64, height: 48, sampleCount: 4 });
|
|
38
38
|
expect(supersampled.width).toBe(128);
|
|
39
39
|
expect(supersampled.height).toBe(96);
|
|
40
40
|
expect(supersampled.sampleCount).toBe(4);
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
const singleSampled =
|
|
42
|
+
releaseWgpuTextureRenderTarget(pool, supersampled);
|
|
43
|
+
const singleSampled = acquireWgpuTextureRenderTarget(state, pool, { width: 128, height: 96 });
|
|
44
44
|
expect(singleSampled).not.toBe(supersampled);
|
|
45
45
|
|
|
46
|
-
const reused =
|
|
46
|
+
const reused = acquireWgpuTextureRenderTarget(state, pool, { width: 64, height: 48, sampleCount: 4 });
|
|
47
47
|
expect(reused).toBe(supersampled);
|
|
48
48
|
});
|
|
49
49
|
|
|
50
50
|
it('re-stamps the logical color space when reusing storage', async () => {
|
|
51
51
|
const state = await createWgpuRenderStateForTest();
|
|
52
52
|
const pool = createWgpuRenderTargetPool();
|
|
53
|
-
const first =
|
|
53
|
+
const first = acquireWgpuTextureRenderTarget(state, pool, { width: 64, height: 64, colorSpace: 'linear' });
|
|
54
54
|
expect(first.colorSpace).toBe('linear');
|
|
55
|
-
|
|
56
|
-
const reused =
|
|
55
|
+
releaseWgpuTextureRenderTarget(pool, first);
|
|
56
|
+
const reused = acquireWgpuTextureRenderTarget(state, pool, { width: 64, height: 64 });
|
|
57
57
|
expect(reused).toBe(first);
|
|
58
58
|
expect(reused.colorSpace).toBe('srgb');
|
|
59
59
|
});
|
|
@@ -69,8 +69,8 @@ describe('destroyWgpuRenderTargetPool', () => {
|
|
|
69
69
|
it('clears the free list', async () => {
|
|
70
70
|
const state = await createWgpuRenderStateForTest();
|
|
71
71
|
const pool = createWgpuRenderTargetPool();
|
|
72
|
-
|
|
73
|
-
destroyWgpuRenderTargetPool(
|
|
72
|
+
releaseWgpuTextureRenderTarget(pool, acquireWgpuTextureRenderTarget(state, pool, { width: 32, height: 32 }));
|
|
73
|
+
destroyWgpuRenderTargetPool(pool);
|
|
74
74
|
expect(pool.free.length).toBe(0);
|
|
75
75
|
});
|
|
76
76
|
});
|
|
@@ -80,12 +80,12 @@ describe('initializeWgpuRenderTargetPool', () => {
|
|
|
80
80
|
expect(typeof initializeWgpuRenderTargetPool).toBe('function');
|
|
81
81
|
});
|
|
82
82
|
});
|
|
83
|
-
describe('
|
|
83
|
+
describe('releaseWgpuTextureRenderTarget', () => {
|
|
84
84
|
it('returns the target to the free list', async () => {
|
|
85
85
|
const state = await createWgpuRenderStateForTest();
|
|
86
86
|
const pool = createWgpuRenderTargetPool();
|
|
87
|
-
const target =
|
|
88
|
-
|
|
87
|
+
const target = acquireWgpuTextureRenderTarget(state, pool, { width: 16, height: 16 });
|
|
88
|
+
releaseWgpuTextureRenderTarget(pool, target);
|
|
89
89
|
expect(pool.free).toContain(target);
|
|
90
90
|
});
|
|
91
91
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { RenderTexture } from '@flighthq/types/contract';
|
|
1
|
+
import type { RenderTargetClear, RenderTexture } from '@flighthq/types/contract';
|
|
2
2
|
import { RenderTargetTextureSourceKind } from '@flighthq/types/contract';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { submitWgpuFrame } from './wgpuFrame';
|
|
5
5
|
import { getWgpuRenderStateRuntime } from './wgpuRenderState';
|
|
6
6
|
import {
|
|
7
7
|
bindWgpuRenderTexture,
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
setWgpuRenderTextureGuard,
|
|
15
15
|
writeWgpuRenderTextureTarget,
|
|
16
16
|
} from './wgpuRenderTexture';
|
|
17
|
-
import { createWgpuRenderStateForTest, installWgpuMock } from './wgpuTestHelper';
|
|
17
|
+
import { beginWgpuScreenRenderPassForTest, createWgpuRenderStateForTest, installWgpuMock } from './wgpuTestHelper';
|
|
18
18
|
|
|
19
19
|
beforeAll(() => {
|
|
20
20
|
installWgpuMock();
|
|
@@ -48,10 +48,10 @@ describe('bindWgpuRenderTexture', () => {
|
|
|
48
48
|
const renderTexture = texture();
|
|
49
49
|
expect(bindWgpuRenderTexture(state, renderTexture)).toBeNull();
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
renderIntoWgpuRenderTexture(state, renderTexture, () => {});
|
|
51
|
+
beginWgpuScreenRenderPassForTest(state);
|
|
52
|
+
renderIntoWgpuRenderTexture(state, renderTexture, () => {}, TRANSPARENT_CLEAR);
|
|
53
53
|
expect(bindWgpuRenderTexture(state, renderTexture)).not.toBeNull();
|
|
54
|
-
|
|
54
|
+
submitWgpuFrame(state);
|
|
55
55
|
});
|
|
56
56
|
});
|
|
57
57
|
|
|
@@ -59,15 +59,15 @@ describe('destroyWgpuRenderTexture', () => {
|
|
|
59
59
|
it('destroys and removes the hidden target', async () => {
|
|
60
60
|
const state = await createWgpuRenderStateForTest();
|
|
61
61
|
const renderTexture = texture();
|
|
62
|
-
|
|
63
|
-
renderIntoWgpuRenderTexture(state, renderTexture, () => {});
|
|
62
|
+
beginWgpuScreenRenderPassForTest(state);
|
|
63
|
+
renderIntoWgpuRenderTexture(state, renderTexture, () => {}, TRANSPARENT_CLEAR);
|
|
64
64
|
const entry = getWgpuRenderStateRuntime(state).context.wgpuRenderTextureCache!.get(renderTexture)!;
|
|
65
65
|
const destroy = vi.spyOn(entry.target.texture, 'destroy');
|
|
66
66
|
|
|
67
67
|
destroyWgpuRenderTexture(state, renderTexture);
|
|
68
68
|
expect(destroy).toHaveBeenCalled();
|
|
69
69
|
expect(bindWgpuRenderTexture(state, renderTexture)).toBeNull();
|
|
70
|
-
|
|
70
|
+
submitWgpuFrame(state);
|
|
71
71
|
});
|
|
72
72
|
});
|
|
73
73
|
|
|
@@ -109,10 +109,10 @@ describe('isWgpuRenderTextureReady', () => {
|
|
|
109
109
|
const state = await createWgpuRenderStateForTest();
|
|
110
110
|
const renderTexture = texture();
|
|
111
111
|
expect(isWgpuRenderTextureReady(state, renderTexture)).toBe(false);
|
|
112
|
-
|
|
113
|
-
renderIntoWgpuRenderTexture(state, renderTexture, () => {});
|
|
112
|
+
beginWgpuScreenRenderPassForTest(state);
|
|
113
|
+
renderIntoWgpuRenderTexture(state, renderTexture, () => {}, TRANSPARENT_CLEAR);
|
|
114
114
|
expect(isWgpuRenderTextureReady(state, renderTexture)).toBe(true);
|
|
115
|
-
|
|
115
|
+
submitWgpuFrame(state);
|
|
116
116
|
});
|
|
117
117
|
});
|
|
118
118
|
|
|
@@ -120,16 +120,16 @@ describe('renderIntoWgpuRenderTexture', () => {
|
|
|
120
120
|
it('restores the enclosing pass and bumps version after success', async () => {
|
|
121
121
|
const state = await createWgpuRenderStateForTest();
|
|
122
122
|
const renderTexture = texture();
|
|
123
|
-
|
|
123
|
+
beginWgpuScreenRenderPassForTest(state);
|
|
124
124
|
const enclosingPass = getWgpuRenderStateRuntime(state).renderPass;
|
|
125
125
|
|
|
126
|
-
renderIntoWgpuRenderTexture(state, renderTexture, () => {});
|
|
126
|
+
renderIntoWgpuRenderTexture(state, renderTexture, () => {}, TRANSPARENT_CLEAR);
|
|
127
127
|
|
|
128
128
|
expect(renderTexture.version).toBe(1);
|
|
129
129
|
expect(renderTexture.colorSpace).toBe('linear');
|
|
130
130
|
expect(getWgpuRenderStateRuntime(state).renderPass).not.toBeNull();
|
|
131
131
|
expect(getWgpuRenderStateRuntime(state).renderPass).not.toBe(enclosingPass);
|
|
132
|
-
|
|
132
|
+
submitWgpuFrame(state);
|
|
133
133
|
});
|
|
134
134
|
});
|
|
135
135
|
|
|
@@ -176,3 +176,5 @@ describe('writeWgpuRenderTextureTarget', () => {
|
|
|
176
176
|
expect(target.sampleCount).toBe(4);
|
|
177
177
|
});
|
|
178
178
|
});
|
|
179
|
+
|
|
180
|
+
const TRANSPARENT_CLEAR: Readonly<RenderTargetClear> = { color: [0, 0, 0, 0], depth: 1.0, stencil: 0 };
|
package/src/wgpuScissor.test.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { beginWgpuRenderPass, endWgpuRenderPass } from './wgpuRenderPass';
|
|
2
2
|
import { getWgpuRenderStateRuntime } from './wgpuRenderState';
|
|
3
3
|
import {
|
|
4
4
|
applyWgpuScissorRect,
|
|
@@ -6,7 +6,8 @@ import {
|
|
|
6
6
|
pushWgpuScissorRect,
|
|
7
7
|
setWgpuRenderPassScissorRect,
|
|
8
8
|
} from './wgpuScissor';
|
|
9
|
-
import { createWgpuRenderStateForTest, installWgpuMock } from './wgpuTestHelper';
|
|
9
|
+
import { beginWgpuScreenRenderPassForTest, createWgpuRenderStateForTest, installWgpuMock } from './wgpuTestHelper';
|
|
10
|
+
import { createWgpuTextureRenderTarget } from './wgpuTextureRenderTarget';
|
|
10
11
|
|
|
11
12
|
beforeAll(() => {
|
|
12
13
|
installWgpuMock();
|
|
@@ -15,7 +16,7 @@ beforeAll(() => {
|
|
|
15
16
|
describe('applyWgpuScissorRect', () => {
|
|
16
17
|
it('calls setScissorRect on the pass when a rect is active', async () => {
|
|
17
18
|
const state = await createWgpuRenderStateForTest();
|
|
18
|
-
|
|
19
|
+
beginWgpuScreenRenderPassForTest(state);
|
|
19
20
|
const runtime = getWgpuRenderStateRuntime(state);
|
|
20
21
|
runtime.currentScissorRect = { x: 10, y: 20, width: 100, height: 50 };
|
|
21
22
|
const calls: number[][] = [];
|
|
@@ -27,7 +28,7 @@ describe('applyWgpuScissorRect', () => {
|
|
|
27
28
|
|
|
28
29
|
it('is a no-op when currentScissorRect is null', async () => {
|
|
29
30
|
const state = await createWgpuRenderStateForTest();
|
|
30
|
-
|
|
31
|
+
beginWgpuScreenRenderPassForTest(state);
|
|
31
32
|
const calls: number[][] = [];
|
|
32
33
|
const fakePass = { setScissorRect: (...args: number[]) => calls.push(args) } as unknown as GPURenderPassEncoder;
|
|
33
34
|
applyWgpuScissorRect(state, fakePass);
|
|
@@ -36,7 +37,7 @@ describe('applyWgpuScissorRect', () => {
|
|
|
36
37
|
|
|
37
38
|
it('clamps dimensions to at least 1×1', async () => {
|
|
38
39
|
const state = await createWgpuRenderStateForTest();
|
|
39
|
-
|
|
40
|
+
beginWgpuScreenRenderPassForTest(state);
|
|
40
41
|
const runtime = getWgpuRenderStateRuntime(state);
|
|
41
42
|
runtime.currentScissorRect = { x: 0, y: 0, width: 0, height: 0 };
|
|
42
43
|
const calls: number[][] = [];
|
|
@@ -49,14 +50,14 @@ describe('applyWgpuScissorRect', () => {
|
|
|
49
50
|
describe('popWgpuScissorRect', () => {
|
|
50
51
|
it('is a no-op when the stack is empty', async () => {
|
|
51
52
|
const state = await createWgpuRenderStateForTest();
|
|
52
|
-
|
|
53
|
+
beginWgpuScreenRenderPassForTest(state);
|
|
53
54
|
expect(() => popWgpuScissorRect(state)).not.toThrow();
|
|
54
55
|
expect(getWgpuRenderStateRuntime(state).currentScissorRect).toBeNull();
|
|
55
56
|
});
|
|
56
57
|
|
|
57
58
|
it('restores the previous rect after a push/pop pair', async () => {
|
|
58
59
|
const state = await createWgpuRenderStateForTest();
|
|
59
|
-
|
|
60
|
+
beginWgpuScreenRenderPassForTest(state);
|
|
60
61
|
const r1 = { x: 0, y: 0, width: 100, height: 100 };
|
|
61
62
|
const r2 = { x: 10, y: 10, width: 50, height: 50 };
|
|
62
63
|
pushWgpuScissorRect(state, r1);
|
|
@@ -72,7 +73,7 @@ describe('popWgpuScissorRect', () => {
|
|
|
72
73
|
describe('pushWgpuScissorRect', () => {
|
|
73
74
|
it('sets currentScissorRect on first push', async () => {
|
|
74
75
|
const state = await createWgpuRenderStateForTest();
|
|
75
|
-
|
|
76
|
+
beginWgpuScreenRenderPassForTest(state);
|
|
76
77
|
const rect = { x: 5, y: 10, width: 80, height: 40 };
|
|
77
78
|
pushWgpuScissorRect(state, rect);
|
|
78
79
|
expect(getWgpuRenderStateRuntime(state).currentScissorRect).toEqual(rect);
|
|
@@ -80,7 +81,7 @@ describe('pushWgpuScissorRect', () => {
|
|
|
80
81
|
|
|
81
82
|
it('stacks previous rect when pushing a second rect', async () => {
|
|
82
83
|
const state = await createWgpuRenderStateForTest();
|
|
83
|
-
|
|
84
|
+
beginWgpuScreenRenderPassForTest(state);
|
|
84
85
|
const r1 = { x: 0, y: 0, width: 100, height: 100 };
|
|
85
86
|
const r2 = { x: 20, y: 20, width: 60, height: 60 };
|
|
86
87
|
pushWgpuScissorRect(state, r1);
|
|
@@ -93,7 +94,7 @@ describe('pushWgpuScissorRect', () => {
|
|
|
93
94
|
|
|
94
95
|
it('copies the rect so mutation of the original does not affect the pushed value', async () => {
|
|
95
96
|
const state = await createWgpuRenderStateForTest();
|
|
96
|
-
|
|
97
|
+
beginWgpuScreenRenderPassForTest(state);
|
|
97
98
|
const rect = { x: 0, y: 0, width: 50, height: 50 };
|
|
98
99
|
pushWgpuScissorRect(state, rect);
|
|
99
100
|
rect.width = 999;
|
|
@@ -102,13 +103,42 @@ describe('pushWgpuScissorRect', () => {
|
|
|
102
103
|
});
|
|
103
104
|
|
|
104
105
|
describe('setWgpuRenderPassScissorRect', () => {
|
|
105
|
-
it('scales logical coordinates
|
|
106
|
-
const state = await createWgpuRenderStateForTest(
|
|
107
|
-
|
|
106
|
+
it('scales logical coordinates onto a supersampled screen surface', async () => {
|
|
107
|
+
const state = await createWgpuRenderStateForTest();
|
|
108
|
+
beginWgpuScreenRenderPassForTest(state, { color: [0, 0, 0, 0] }, { antialias: true });
|
|
109
|
+
const setScissorRect = vi.fn();
|
|
110
|
+
|
|
111
|
+
setWgpuRenderPassScissorRect(state, { setScissorRect } as unknown as GPURenderPassEncoder, 3, 5, 7, 11);
|
|
112
|
+
|
|
113
|
+
expect(setScissorRect).toHaveBeenCalledWith(6, 10, 14, 22);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Logical is one coordinate space, so a supersampled OFFSCREEN target scales its scissors by the same
|
|
117
|
+
// factor its storage was grown by. It previously passed them through unscaled while the screen scaled
|
|
118
|
+
// them, which left a clip inside a sampleCount-4 effect target covering a quarter of its intended area.
|
|
119
|
+
it('scales them onto a supersampled texture target by the same rule', async () => {
|
|
120
|
+
const state = await createWgpuRenderStateForTest();
|
|
121
|
+
const outer = beginWgpuScreenRenderPassForTest(state);
|
|
122
|
+
const pass = beginWgpuRenderPass(state, createWgpuTextureRenderTarget(state, 64, 64, state.format, 'srgb', 4));
|
|
108
123
|
const setScissorRect = vi.fn();
|
|
109
124
|
|
|
110
125
|
setWgpuRenderPassScissorRect(state, { setScissorRect } as unknown as GPURenderPassEncoder, 3, 5, 7, 11);
|
|
111
126
|
|
|
112
127
|
expect(setScissorRect).toHaveBeenCalledWith(6, 10, 14, 22);
|
|
128
|
+
endWgpuRenderPass(pass);
|
|
129
|
+
endWgpuRenderPass(outer);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('passes them through unscaled on a target with no supersample', async () => {
|
|
133
|
+
const state = await createWgpuRenderStateForTest();
|
|
134
|
+
const outer = beginWgpuScreenRenderPassForTest(state);
|
|
135
|
+
const pass = beginWgpuRenderPass(state, createWgpuTextureRenderTarget(state, 64, 64));
|
|
136
|
+
const setScissorRect = vi.fn();
|
|
137
|
+
|
|
138
|
+
setWgpuRenderPassScissorRect(state, { setScissorRect } as unknown as GPURenderPassEncoder, 3, 5, 7, 11);
|
|
139
|
+
|
|
140
|
+
expect(setScissorRect).toHaveBeenCalledWith(3, 5, 7, 11);
|
|
141
|
+
endWgpuRenderPass(pass);
|
|
142
|
+
endWgpuRenderPass(outer);
|
|
113
143
|
});
|
|
114
144
|
});
|