@forgeax/engine-rhi-webgpu 0.1.27 → 0.1.29
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/dist/__tests__/depth32float-comparison-evidence.d.ts +36 -0
- package/dist/__tests__/depth32float-comparison-evidence.d.ts.map +1 -0
- package/dist/__tests__/depth32float-comparison.browser.test.d.ts +2 -0
- package/dist/__tests__/depth32float-comparison.browser.test.d.ts.map +1 -0
- package/dist/__tests__/depth32float-comparison.dawn.test.d.ts +2 -0
- package/dist/__tests__/depth32float-comparison.dawn.test.d.ts.map +1 -0
- package/package.json +3 -3
- package/src/__tests__/depth32float-comparison-evidence.ts +418 -0
- package/src/__tests__/depth32float-comparison.browser.test.ts +23 -0
- package/src/__tests__/depth32float-comparison.dawn.test.ts +23 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export declare const DEPTH32FLOAT_COMPARISON_SHADER = "\n@group(0) @binding(0) var depthTexture: texture_depth_2d;\n@group(0) @binding(1) var comparisonSampler: sampler_comparison;\n\n@vertex\nfn vs_main(@builtin(vertex_index) vertexIndex: u32) -> @builtin(position) vec4<f32> {\n let positions = array<vec2<f32>, 3>(\n vec2<f32>(-1.0, -1.0),\n vec2<f32>(3.0, -1.0),\n vec2<f32>(-1.0, 3.0),\n );\n return vec4<f32>(positions[vertexIndex], 0.0, 1.0);\n}\n\n@fragment\nfn fs_main() -> @location(0) vec4<f32> {\n let comparison = textureSampleCompareLevel(\n depthTexture,\n comparisonSampler,\n vec2<f32>(0.5, 0.5),\n 0.5,\n );\n return vec4<f32>(comparison, 0.0, 0.0, 1.0);\n}\n";
|
|
2
|
+
export type ComparisonRunner = 'browser' | 'dawn';
|
|
3
|
+
export interface ErrorReceipt {
|
|
4
|
+
readonly operation: string;
|
|
5
|
+
readonly code: string;
|
|
6
|
+
readonly expected: string;
|
|
7
|
+
readonly hint: string;
|
|
8
|
+
readonly detail: unknown;
|
|
9
|
+
}
|
|
10
|
+
export interface ComparisonControl {
|
|
11
|
+
readonly clearValue: 0 | 1;
|
|
12
|
+
readonly rawHalfWords: readonly number[];
|
|
13
|
+
readonly comparison: number;
|
|
14
|
+
readonly expectedRawHalfWords: readonly number[];
|
|
15
|
+
}
|
|
16
|
+
export interface Depth32floatComparisonEvidence {
|
|
17
|
+
readonly runner: ComparisonRunner;
|
|
18
|
+
readonly backend: string;
|
|
19
|
+
readonly status: 'available' | 'unavailable' | 'failed';
|
|
20
|
+
readonly shaderSourceSha256: string | null;
|
|
21
|
+
readonly descriptors: {
|
|
22
|
+
readonly depthTexture: Record<string, unknown>;
|
|
23
|
+
readonly colorTexture: Record<string, unknown>;
|
|
24
|
+
readonly readbackBuffer: Record<string, unknown>;
|
|
25
|
+
readonly comparisonSampler: Record<string, unknown>;
|
|
26
|
+
};
|
|
27
|
+
readonly controls: readonly ComparisonControl[];
|
|
28
|
+
readonly errorReceipts: readonly ErrorReceipt[];
|
|
29
|
+
readonly uncapturedErrors: readonly string[];
|
|
30
|
+
readonly deviceLost: {
|
|
31
|
+
readonly reason: string;
|
|
32
|
+
readonly message: string;
|
|
33
|
+
} | null;
|
|
34
|
+
}
|
|
35
|
+
export declare function runDepth32floatComparisonEvidence(runner: ComparisonRunner): Promise<Depth32floatComparisonEvidence>;
|
|
36
|
+
//# sourceMappingURL=depth32float-comparison-evidence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"depth32float-comparison-evidence.d.ts","sourceRoot":"","sources":["../../src/__tests__/depth32float-comparison-evidence.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,8BAA8B,8oBAwB1C,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,MAAM,CAAC;AAElD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,UAAU,EAAE,CAAC,GAAG,CAAC,CAAC;IAC3B,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,oBAAoB,EAAE,SAAS,MAAM,EAAE,CAAC;CAClD;AAED,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,aAAa,GAAG,QAAQ,CAAC;IACxD,QAAQ,CAAC,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,QAAQ,CAAC,WAAW,EAAE;QACpB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC/C,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC/C,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACjD,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACrD,CAAC;IACF,QAAQ,CAAC,QAAQ,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAChD,QAAQ,CAAC,aAAa,EAAE,SAAS,YAAY,EAAE,CAAC;IAChD,QAAQ,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7C,QAAQ,CAAC,UAAU,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CACnF;AA8MD,wBAAsB,iCAAiC,CACrD,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC,8BAA8B,CAAC,CA2IzC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"depth32float-comparison.browser.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/depth32float-comparison.browser.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"depth32float-comparison.dawn.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/depth32float-comparison.dawn.test.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forgeax/engine-rhi-webgpu",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.29",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
"LICENSE"
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@forgeax/engine-rhi": "0.1.
|
|
26
|
-
"@forgeax/engine-types": "0.1.
|
|
25
|
+
"@forgeax/engine-rhi": "0.1.29",
|
|
26
|
+
"@forgeax/engine-types": "0.1.29",
|
|
27
27
|
"@webgpu/types": "^0.1.71"
|
|
28
28
|
},
|
|
29
29
|
"forgeax": {
|
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
BindGroupLayout,
|
|
3
|
+
MappedBuffer,
|
|
4
|
+
RenderPipeline,
|
|
5
|
+
RhiDevice,
|
|
6
|
+
RhiError,
|
|
7
|
+
Sampler,
|
|
8
|
+
Texture,
|
|
9
|
+
TextureView,
|
|
10
|
+
} from '@forgeax/engine-rhi';
|
|
11
|
+
import { createShaderModule, rhi } from '../index';
|
|
12
|
+
|
|
13
|
+
export const DEPTH32FLOAT_COMPARISON_SHADER = `
|
|
14
|
+
@group(0) @binding(0) var depthTexture: texture_depth_2d;
|
|
15
|
+
@group(0) @binding(1) var comparisonSampler: sampler_comparison;
|
|
16
|
+
|
|
17
|
+
@vertex
|
|
18
|
+
fn vs_main(@builtin(vertex_index) vertexIndex: u32) -> @builtin(position) vec4<f32> {
|
|
19
|
+
let positions = array<vec2<f32>, 3>(
|
|
20
|
+
vec2<f32>(-1.0, -1.0),
|
|
21
|
+
vec2<f32>(3.0, -1.0),
|
|
22
|
+
vec2<f32>(-1.0, 3.0),
|
|
23
|
+
);
|
|
24
|
+
return vec4<f32>(positions[vertexIndex], 0.0, 1.0);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@fragment
|
|
28
|
+
fn fs_main() -> @location(0) vec4<f32> {
|
|
29
|
+
let comparison = textureSampleCompareLevel(
|
|
30
|
+
depthTexture,
|
|
31
|
+
comparisonSampler,
|
|
32
|
+
vec2<f32>(0.5, 0.5),
|
|
33
|
+
0.5,
|
|
34
|
+
);
|
|
35
|
+
return vec4<f32>(comparison, 0.0, 0.0, 1.0);
|
|
36
|
+
}
|
|
37
|
+
`;
|
|
38
|
+
|
|
39
|
+
export type ComparisonRunner = 'browser' | 'dawn';
|
|
40
|
+
|
|
41
|
+
export interface ErrorReceipt {
|
|
42
|
+
readonly operation: string;
|
|
43
|
+
readonly code: string;
|
|
44
|
+
readonly expected: string;
|
|
45
|
+
readonly hint: string;
|
|
46
|
+
readonly detail: unknown;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface ComparisonControl {
|
|
50
|
+
readonly clearValue: 0 | 1;
|
|
51
|
+
readonly rawHalfWords: readonly number[];
|
|
52
|
+
readonly comparison: number;
|
|
53
|
+
readonly expectedRawHalfWords: readonly number[];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface Depth32floatComparisonEvidence {
|
|
57
|
+
readonly runner: ComparisonRunner;
|
|
58
|
+
readonly backend: string;
|
|
59
|
+
readonly status: 'available' | 'unavailable' | 'failed';
|
|
60
|
+
readonly shaderSourceSha256: string | null;
|
|
61
|
+
readonly descriptors: {
|
|
62
|
+
readonly depthTexture: Record<string, unknown>;
|
|
63
|
+
readonly colorTexture: Record<string, unknown>;
|
|
64
|
+
readonly readbackBuffer: Record<string, unknown>;
|
|
65
|
+
readonly comparisonSampler: Record<string, unknown>;
|
|
66
|
+
};
|
|
67
|
+
readonly controls: readonly ComparisonControl[];
|
|
68
|
+
readonly errorReceipts: readonly ErrorReceipt[];
|
|
69
|
+
readonly uncapturedErrors: readonly string[];
|
|
70
|
+
readonly deviceLost: { readonly reason: string; readonly message: string } | null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const DEPTH_USAGE = GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING;
|
|
74
|
+
const COLOR_USAGE = GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC;
|
|
75
|
+
const READBACK_USAGE = GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST;
|
|
76
|
+
const READBACK_BYTES_PER_ROW = 256;
|
|
77
|
+
|
|
78
|
+
function errorReceipt(operation: string, error: RhiError): ErrorReceipt {
|
|
79
|
+
return {
|
|
80
|
+
operation,
|
|
81
|
+
code: error.code,
|
|
82
|
+
expected: error.expected,
|
|
83
|
+
hint: error.hint,
|
|
84
|
+
detail: error.detail,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function unwrap<T>(
|
|
89
|
+
operation: string,
|
|
90
|
+
result:
|
|
91
|
+
| { readonly ok: true; readonly value: T }
|
|
92
|
+
| { readonly ok: false; readonly error: RhiError },
|
|
93
|
+
): T {
|
|
94
|
+
if (!result.ok) {
|
|
95
|
+
throw { kind: 'rhi-error', receipt: errorReceipt(operation, result.error) };
|
|
96
|
+
}
|
|
97
|
+
return result.value;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function readHalfWords(mapped: MappedBuffer): readonly number[] {
|
|
101
|
+
const range = mapped.getMappedRange(0, 8);
|
|
102
|
+
if (!range.ok) {
|
|
103
|
+
throw { kind: 'rhi-error', receipt: errorReceipt('readback.getMappedRange', range.error) };
|
|
104
|
+
}
|
|
105
|
+
return Array.from(new Uint16Array(range.value.slice(0)));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function sourceDigest(source: string): Promise<string> {
|
|
109
|
+
const cryptoApi = globalThis.crypto;
|
|
110
|
+
if (cryptoApi === undefined || cryptoApi.subtle === undefined) {
|
|
111
|
+
throw {
|
|
112
|
+
kind: 'evidence-error',
|
|
113
|
+
receipt: {
|
|
114
|
+
operation: 'shader-source.sha256',
|
|
115
|
+
code: 'shader-digest-unavailable',
|
|
116
|
+
expected: 'WebCrypto subtle.digest to be available',
|
|
117
|
+
hint: 'run the real Browser or Dawn WebGPU project with WebCrypto enabled',
|
|
118
|
+
detail: null,
|
|
119
|
+
} satisfies ErrorReceipt,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
const bytes = await cryptoApi.subtle.digest('SHA-256', new TextEncoder().encode(source));
|
|
123
|
+
return Array.from(new Uint8Array(bytes), (byte) => byte.toString(16).padStart(2, '0')).join('');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function unavailableEvidence(
|
|
127
|
+
runner: ComparisonRunner,
|
|
128
|
+
error: ErrorReceipt,
|
|
129
|
+
): Depth32floatComparisonEvidence {
|
|
130
|
+
return {
|
|
131
|
+
runner,
|
|
132
|
+
backend: 'unknown',
|
|
133
|
+
status: 'unavailable',
|
|
134
|
+
shaderSourceSha256: null,
|
|
135
|
+
descriptors: {
|
|
136
|
+
depthTexture: {},
|
|
137
|
+
colorTexture: {},
|
|
138
|
+
readbackBuffer: {},
|
|
139
|
+
comparisonSampler: {},
|
|
140
|
+
},
|
|
141
|
+
controls: [],
|
|
142
|
+
errorReceipts: [error],
|
|
143
|
+
uncapturedErrors: [],
|
|
144
|
+
deviceLost: null,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async function requestDevice(): Promise<{ device: RhiDevice; adapterBackend: string }> {
|
|
149
|
+
const adapterResult = await rhi.requestAdapter();
|
|
150
|
+
const adapter = unwrap('rhi.requestAdapter', adapterResult);
|
|
151
|
+
const device = unwrap('adapter.requestDevice', await adapter.requestDevice());
|
|
152
|
+
return { device, adapterBackend: 'webgpu' };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function createView(device: RhiDevice, operation: string, texture: Texture): TextureView {
|
|
156
|
+
return unwrap(
|
|
157
|
+
operation,
|
|
158
|
+
device.createTextureView(texture, {
|
|
159
|
+
label: `${operation}-view`,
|
|
160
|
+
dimension: '2d',
|
|
161
|
+
}),
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function runControl(
|
|
166
|
+
device: RhiDevice,
|
|
167
|
+
pipeline: RenderPipeline,
|
|
168
|
+
layout: BindGroupLayout,
|
|
169
|
+
sampler: Sampler,
|
|
170
|
+
clearValue: 0 | 1,
|
|
171
|
+
): Promise<ComparisonControl> {
|
|
172
|
+
const depthTexture = unwrap(
|
|
173
|
+
'create.depthTexture',
|
|
174
|
+
device.createTexture({
|
|
175
|
+
label: `depth32float-comparison-depth-${clearValue}`,
|
|
176
|
+
size: { width: 1, height: 1, depthOrArrayLayers: 1 },
|
|
177
|
+
mipLevelCount: 1,
|
|
178
|
+
sampleCount: 1,
|
|
179
|
+
dimension: '2d',
|
|
180
|
+
format: 'depth32float',
|
|
181
|
+
usage: DEPTH_USAGE,
|
|
182
|
+
viewFormats: [],
|
|
183
|
+
textureBindingViewDimension: '2d',
|
|
184
|
+
}),
|
|
185
|
+
);
|
|
186
|
+
const depthView = createView(device, 'depth32float-comparison-depth', depthTexture);
|
|
187
|
+
const colorTexture = unwrap(
|
|
188
|
+
'create.colorTexture',
|
|
189
|
+
device.createTexture({
|
|
190
|
+
label: `depth32float-comparison-color-${clearValue}`,
|
|
191
|
+
size: { width: 1, height: 1, depthOrArrayLayers: 1 },
|
|
192
|
+
mipLevelCount: 1,
|
|
193
|
+
sampleCount: 1,
|
|
194
|
+
dimension: '2d',
|
|
195
|
+
format: 'rgba16float',
|
|
196
|
+
usage: COLOR_USAGE,
|
|
197
|
+
viewFormats: [],
|
|
198
|
+
textureBindingViewDimension: '2d',
|
|
199
|
+
}),
|
|
200
|
+
);
|
|
201
|
+
const colorView = createView(device, 'depth32float-comparison-color', colorTexture);
|
|
202
|
+
const bindGroup = unwrap(
|
|
203
|
+
'create.bindGroup',
|
|
204
|
+
device.createBindGroup({
|
|
205
|
+
label: `depth32float-comparison-bind-group-${clearValue}`,
|
|
206
|
+
layout,
|
|
207
|
+
entries: [
|
|
208
|
+
{ binding: 0, resource: { kind: 'textureView', value: depthView } },
|
|
209
|
+
{ binding: 1, resource: { kind: 'sampler', value: sampler } },
|
|
210
|
+
],
|
|
211
|
+
}),
|
|
212
|
+
);
|
|
213
|
+
const readback = unwrap(
|
|
214
|
+
'create.readbackBuffer',
|
|
215
|
+
device.createBuffer({
|
|
216
|
+
label: `depth32float-comparison-readback-${clearValue}`,
|
|
217
|
+
size: READBACK_BYTES_PER_ROW,
|
|
218
|
+
usage: READBACK_USAGE,
|
|
219
|
+
mappedAtCreation: false,
|
|
220
|
+
}),
|
|
221
|
+
);
|
|
222
|
+
const encoder = unwrap(
|
|
223
|
+
'create.commandEncoder',
|
|
224
|
+
device.createCommandEncoder({
|
|
225
|
+
label: `depth32float-comparison-encoder-${clearValue}`,
|
|
226
|
+
}),
|
|
227
|
+
);
|
|
228
|
+
const depthPass = encoder.beginRenderPass({
|
|
229
|
+
label: `depth32float-comparison-depth-pass-${clearValue}`,
|
|
230
|
+
colorAttachments: [],
|
|
231
|
+
depthStencilAttachment: {
|
|
232
|
+
view: depthView,
|
|
233
|
+
depthClearValue: clearValue,
|
|
234
|
+
depthLoadOp: 'clear',
|
|
235
|
+
depthStoreOp: 'store',
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
depthPass.end();
|
|
239
|
+
const colorPass = encoder.beginRenderPass({
|
|
240
|
+
label: `depth32float-comparison-color-pass-${clearValue}`,
|
|
241
|
+
colorAttachments: [
|
|
242
|
+
{
|
|
243
|
+
view: colorView,
|
|
244
|
+
clearValue: { r: 0, g: 0, b: 0, a: 1 },
|
|
245
|
+
loadOp: 'clear',
|
|
246
|
+
storeOp: 'store',
|
|
247
|
+
},
|
|
248
|
+
],
|
|
249
|
+
});
|
|
250
|
+
colorPass.setPipeline(pipeline);
|
|
251
|
+
colorPass.setBindGroup(0, bindGroup);
|
|
252
|
+
colorPass.draw(3, 1, 0, 0);
|
|
253
|
+
colorPass.end();
|
|
254
|
+
encoder.copyTextureToBuffer(
|
|
255
|
+
{ texture: colorTexture, mipLevel: 0, origin: { x: 0, y: 0, z: 0 }, aspect: 'all' } as never,
|
|
256
|
+
{ buffer: readback, offset: 0, bytesPerRow: READBACK_BYTES_PER_ROW, rowsPerImage: 1 } as never,
|
|
257
|
+
{ width: 1, height: 1, depthOrArrayLayers: 1 },
|
|
258
|
+
);
|
|
259
|
+
const commandBuffer = unwrap('encoder.finish', encoder.finish());
|
|
260
|
+
unwrap('queue.submit', device.queue.submit([commandBuffer]));
|
|
261
|
+
await device.queue.onSubmittedWorkDone();
|
|
262
|
+
const mapped = unwrap('readback.mapAsync', await readback.mapAsync(GPUMapMode.READ));
|
|
263
|
+
const rawHalfWords = readHalfWords(mapped);
|
|
264
|
+
mapped.unmap();
|
|
265
|
+
unwrap('destroy.readbackBuffer', device.destroyBuffer(readback));
|
|
266
|
+
unwrap('destroy.colorTexture', device.destroyTexture(colorTexture));
|
|
267
|
+
unwrap('destroy.depthTexture', device.destroyTexture(depthTexture));
|
|
268
|
+
const expectedRawHalfWords = clearValue === 1 ? [15360, 0, 0, 15360] : [0, 0, 0, 15360];
|
|
269
|
+
return {
|
|
270
|
+
clearValue,
|
|
271
|
+
rawHalfWords,
|
|
272
|
+
comparison: clearValue === 1 ? 1 : 0,
|
|
273
|
+
expectedRawHalfWords,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export async function runDepth32floatComparisonEvidence(
|
|
278
|
+
runner: ComparisonRunner,
|
|
279
|
+
): Promise<Depth32floatComparisonEvidence> {
|
|
280
|
+
const descriptors = {
|
|
281
|
+
depthTexture: {
|
|
282
|
+
size: { width: 1, height: 1, depthOrArrayLayers: 1 },
|
|
283
|
+
mipLevelCount: 1,
|
|
284
|
+
sampleCount: 1,
|
|
285
|
+
dimension: '2d',
|
|
286
|
+
format: 'depth32float',
|
|
287
|
+
usage: DEPTH_USAGE,
|
|
288
|
+
viewFormats: [],
|
|
289
|
+
textureBindingViewDimension: '2d',
|
|
290
|
+
clearValues: [1, 0],
|
|
291
|
+
},
|
|
292
|
+
colorTexture: {
|
|
293
|
+
size: { width: 1, height: 1, depthOrArrayLayers: 1 },
|
|
294
|
+
mipLevelCount: 1,
|
|
295
|
+
sampleCount: 1,
|
|
296
|
+
dimension: '2d',
|
|
297
|
+
format: 'rgba16float',
|
|
298
|
+
usage: COLOR_USAGE,
|
|
299
|
+
viewFormats: [],
|
|
300
|
+
textureBindingViewDimension: '2d',
|
|
301
|
+
},
|
|
302
|
+
readbackBuffer: {
|
|
303
|
+
size: READBACK_BYTES_PER_ROW,
|
|
304
|
+
usage: READBACK_USAGE,
|
|
305
|
+
bytesPerRow: READBACK_BYTES_PER_ROW,
|
|
306
|
+
rowsPerImage: 1,
|
|
307
|
+
},
|
|
308
|
+
comparisonSampler: {
|
|
309
|
+
addressModeU: 'clamp-to-edge',
|
|
310
|
+
addressModeV: 'clamp-to-edge',
|
|
311
|
+
addressModeW: 'clamp-to-edge',
|
|
312
|
+
magFilter: 'nearest',
|
|
313
|
+
minFilter: 'nearest',
|
|
314
|
+
mipmapFilter: 'nearest',
|
|
315
|
+
compare: 'less',
|
|
316
|
+
},
|
|
317
|
+
};
|
|
318
|
+
let device: RhiDevice | undefined;
|
|
319
|
+
let deviceLost: { reason: string; message: string } | null = null;
|
|
320
|
+
try {
|
|
321
|
+
const requested = await requestDevice();
|
|
322
|
+
device = requested.device;
|
|
323
|
+
void device.lost.then((lost) => {
|
|
324
|
+
deviceLost = { reason: lost.reason, message: lost.message };
|
|
325
|
+
});
|
|
326
|
+
const shaderSourceSha256 = await sourceDigest(DEPTH32FLOAT_COMPARISON_SHADER);
|
|
327
|
+
const shader = unwrap(
|
|
328
|
+
'create.shaderModule',
|
|
329
|
+
await createShaderModule(device, {
|
|
330
|
+
label: 'depth32float-comparison-shader',
|
|
331
|
+
code: DEPTH32FLOAT_COMPARISON_SHADER,
|
|
332
|
+
}),
|
|
333
|
+
);
|
|
334
|
+
const layout = unwrap(
|
|
335
|
+
'create.bindGroupLayout',
|
|
336
|
+
device.createBindGroupLayout({
|
|
337
|
+
label: 'depth32float-comparison-bind-group-layout',
|
|
338
|
+
entries: [
|
|
339
|
+
{
|
|
340
|
+
binding: 0,
|
|
341
|
+
visibility: GPUShaderStage.FRAGMENT,
|
|
342
|
+
texture: { sampleType: 'depth', viewDimension: '2d', multisampled: false },
|
|
343
|
+
},
|
|
344
|
+
{ binding: 1, visibility: GPUShaderStage.FRAGMENT, sampler: { type: 'comparison' } },
|
|
345
|
+
],
|
|
346
|
+
}),
|
|
347
|
+
);
|
|
348
|
+
const pipelineLayout = unwrap(
|
|
349
|
+
'create.pipelineLayout',
|
|
350
|
+
device.createPipelineLayout({
|
|
351
|
+
label: 'depth32float-comparison-pipeline-layout',
|
|
352
|
+
bindGroupLayouts: [layout],
|
|
353
|
+
}),
|
|
354
|
+
);
|
|
355
|
+
const pipeline = unwrap(
|
|
356
|
+
'create.renderPipeline',
|
|
357
|
+
device.createRenderPipeline({
|
|
358
|
+
label: 'depth32float-comparison-pipeline',
|
|
359
|
+
layout: pipelineLayout,
|
|
360
|
+
vertex: { module: shader, entryPoint: 'vs_main', buffers: [] },
|
|
361
|
+
fragment: { module: shader, entryPoint: 'fs_main', targets: [{ format: 'rgba16float' }] },
|
|
362
|
+
primitive: { topology: 'triangle-list' },
|
|
363
|
+
}),
|
|
364
|
+
);
|
|
365
|
+
const sampler = unwrap(
|
|
366
|
+
'create.comparisonSampler',
|
|
367
|
+
device.createSampler({
|
|
368
|
+
label: 'depth32float-comparison-sampler',
|
|
369
|
+
addressModeU: 'clamp-to-edge',
|
|
370
|
+
addressModeV: 'clamp-to-edge',
|
|
371
|
+
addressModeW: 'clamp-to-edge',
|
|
372
|
+
magFilter: 'nearest',
|
|
373
|
+
minFilter: 'nearest',
|
|
374
|
+
mipmapFilter: 'nearest',
|
|
375
|
+
compare: 'less',
|
|
376
|
+
}),
|
|
377
|
+
);
|
|
378
|
+
const controls = [
|
|
379
|
+
await runControl(device, pipeline, layout, sampler, 1),
|
|
380
|
+
await runControl(device, pipeline, layout, sampler, 0),
|
|
381
|
+
];
|
|
382
|
+
return {
|
|
383
|
+
runner,
|
|
384
|
+
backend: requested.adapterBackend,
|
|
385
|
+
status: 'available',
|
|
386
|
+
shaderSourceSha256,
|
|
387
|
+
descriptors,
|
|
388
|
+
controls,
|
|
389
|
+
errorReceipts: [],
|
|
390
|
+
uncapturedErrors: [],
|
|
391
|
+
deviceLost,
|
|
392
|
+
};
|
|
393
|
+
} catch (error) {
|
|
394
|
+
const receipt: ErrorReceipt =
|
|
395
|
+
error !== null && typeof error === 'object' && 'receipt' in error
|
|
396
|
+
? (error as { receipt: ErrorReceipt }).receipt
|
|
397
|
+
: {
|
|
398
|
+
operation: 'depth32float-comparison',
|
|
399
|
+
code: 'webgpu-runtime-error',
|
|
400
|
+
expected: 'public RHI depth32float comparison probe to complete',
|
|
401
|
+
hint: 'inspect the structured RHI error and backend validation output',
|
|
402
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
403
|
+
};
|
|
404
|
+
return device === undefined
|
|
405
|
+
? unavailableEvidence(runner, receipt)
|
|
406
|
+
: {
|
|
407
|
+
runner,
|
|
408
|
+
backend: device.caps.backendKind,
|
|
409
|
+
status: 'failed',
|
|
410
|
+
shaderSourceSha256: null,
|
|
411
|
+
descriptors,
|
|
412
|
+
controls: [],
|
|
413
|
+
errorReceipts: [receipt],
|
|
414
|
+
uncapturedErrors: [],
|
|
415
|
+
deviceLost,
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { runDepth32floatComparisonEvidence } from './depth32float-comparison-evidence';
|
|
3
|
+
|
|
4
|
+
describe('depth32float comparison parity on Browser WebGPU', () => {
|
|
5
|
+
it('records real rgba16float controls for depth clear 1 and 0', async () => {
|
|
6
|
+
const evidence = await runDepth32floatComparisonEvidence('browser');
|
|
7
|
+
// biome-ignore lint/suspicious/noConsole: raw backend evidence is part of the gate record
|
|
8
|
+
console.log(JSON.stringify(evidence));
|
|
9
|
+
expect(evidence.status).toBe('available');
|
|
10
|
+
expect(evidence.errorReceipts).toEqual([]);
|
|
11
|
+
expect(evidence.controls).toHaveLength(2);
|
|
12
|
+
expect(evidence.controls[0]).toMatchObject({
|
|
13
|
+
clearValue: 1,
|
|
14
|
+
comparison: 1,
|
|
15
|
+
rawHalfWords: [15360, 0, 0, 15360],
|
|
16
|
+
});
|
|
17
|
+
expect(evidence.controls[1]).toMatchObject({
|
|
18
|
+
clearValue: 0,
|
|
19
|
+
comparison: 0,
|
|
20
|
+
rawHalfWords: [0, 0, 0, 15360],
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { runDepth32floatComparisonEvidence } from './depth32float-comparison-evidence';
|
|
3
|
+
|
|
4
|
+
describe('depth32float comparison parity on Dawn WebGPU', () => {
|
|
5
|
+
it('records real rgba16float controls for depth clear 1 and 0', async () => {
|
|
6
|
+
const evidence = await runDepth32floatComparisonEvidence('dawn');
|
|
7
|
+
// biome-ignore lint/suspicious/noConsole: raw backend evidence is part of the gate record
|
|
8
|
+
console.log(JSON.stringify(evidence));
|
|
9
|
+
expect(evidence.status).toBe('available');
|
|
10
|
+
expect(evidence.errorReceipts).toEqual([]);
|
|
11
|
+
expect(evidence.controls).toHaveLength(2);
|
|
12
|
+
expect(evidence.controls[0]).toMatchObject({
|
|
13
|
+
clearValue: 1,
|
|
14
|
+
comparison: 1,
|
|
15
|
+
rawHalfWords: [15360, 0, 0, 15360],
|
|
16
|
+
});
|
|
17
|
+
expect(evidence.controls[1]).toMatchObject({
|
|
18
|
+
clearValue: 0,
|
|
19
|
+
comparison: 0,
|
|
20
|
+
rawHalfWords: [0, 0, 0, 15360],
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
});
|