Skip to content

Commit 47d19f5

Browse files
authored
op: Introduce depth_bias.spec.ts file with a 'depth_bias' test (#2075)
This PR introduces a new test to check if depth bias works as expected by drawing a square with different depth bias values like 'positive', 'negative', 'infinity', 'slope', 'clamp', etc. Issue: #2023
1 parent 7a679a3 commit 47d19f5

File tree

2 files changed

+263
-6
lines changed

2 files changed

+263
-6
lines changed

src/webgpu/api/operation/rendering/depth.spec.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -148,12 +148,6 @@ class DepthTest extends GPUTest {
148148

149149
export const g = makeTestGroup(DepthTest);
150150

151-
g.test('depth_bias')
152-
.desc(
153-
`Tests render results with different depth bias values: positive, negative, infinity, slope, clamp, etc.`
154-
)
155-
.unimplemented();
156-
157151
g.test('depth_disabled')
158152
.desc('Tests render results with depth test disabled.')
159153
.fn(async t => {
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
export const description = `
2+
Tests render results with different depth bias values like 'positive', 'negative', 'infinity',
3+
'slope', 'clamp', etc.
4+
`;
5+
6+
import { makeTestGroup } from '../../../../common/framework/test_group.js';
7+
import { unreachable } from '../../../../common/util/util.js';
8+
import { DepthStencilFormat, EncodableTextureFormat } from '../../../capability_info.js';
9+
import { GPUTest } from '../../../gpu_test.js';
10+
import { kValue } from '../../../util/constants.js';
11+
import { TexelView } from '../../../util/texture/texel_view.js';
12+
import { textureContentIsOKByT2B } from '../../../util/texture/texture_ok.js';
13+
14+
enum QuadAngle {
15+
Flat,
16+
TiltedX,
17+
}
18+
19+
// Floating point depth buffers use the following formula to calculate bias
20+
// bias = depthBias * 2 ** (exponent(max z of primitive) - number of bits in mantissa) +
21+
// slopeScale * maxSlope
22+
// https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-output-merger-stage-depth-bias
23+
// https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkCmdSetDepthBias.html
24+
// https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1516269-setdepthbias
25+
//
26+
// To get a final bias of 0.25 for primitives with z = 0.25, we can use
27+
// depthBias = 0.25 / (2 ** (-2 - 23)) = 8388608.
28+
const kPointTwoFiveBiasForPointTwoFiveZOnFloat = 8388608;
29+
30+
class DepthBiasTest extends GPUTest {
31+
runDepthBiasTest(
32+
depthFormat: EncodableTextureFormat & DepthStencilFormat,
33+
{
34+
quadAngle,
35+
bias,
36+
biasSlopeScale,
37+
biasClamp,
38+
expectedDepth,
39+
}: {
40+
quadAngle: QuadAngle;
41+
bias: number;
42+
biasSlopeScale: number;
43+
biasClamp: number;
44+
expectedDepth: number;
45+
}
46+
) {
47+
const renderTargetFormat = 'rgba8unorm';
48+
let vertexShaderCode: string;
49+
switch (quadAngle) {
50+
case QuadAngle.Flat:
51+
// Draw a square at z = 0.25.
52+
vertexShaderCode = `
53+
@vertex
54+
fn main(@builtin(vertex_index) VertexIndex : u32) -> @builtin(position) vec4<f32> {
55+
var pos = array<vec2<f32>, 6>(
56+
vec2<f32>(-1.0, -1.0),
57+
vec2<f32>( 1.0, -1.0),
58+
vec2<f32>(-1.0, 1.0),
59+
vec2<f32>(-1.0, 1.0),
60+
vec2<f32>( 1.0, -1.0),
61+
vec2<f32>( 1.0, 1.0));
62+
return vec4<f32>(pos[VertexIndex], 0.25, 1.0);
63+
}
64+
`;
65+
break;
66+
case QuadAngle.TiltedX:
67+
// Draw a square ranging from 0 to 0.5, bottom to top.
68+
vertexShaderCode = `
69+
@vertex
70+
fn main(@builtin(vertex_index) VertexIndex : u32) -> @builtin(position) vec4<f32> {
71+
var pos = array<vec3<f32>, 6>(
72+
vec3<f32>(-1.0, -1.0, 0.0),
73+
vec3<f32>( 1.0, -1.0, 0.0),
74+
vec3<f32>(-1.0, 1.0, 0.5),
75+
vec3<f32>(-1.0, 1.0, 0.5),
76+
vec3<f32>( 1.0, -1.0, 0.0),
77+
vec3<f32>( 1.0, 1.0, 0.5));
78+
return vec4<f32>(pos[VertexIndex], 1.0);
79+
}
80+
`;
81+
break;
82+
default:
83+
unreachable();
84+
}
85+
86+
const renderTarget = this.device.createTexture({
87+
format: renderTargetFormat,
88+
size: { width: 1, height: 1, depthOrArrayLayers: 1 },
89+
usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.RENDER_ATTACHMENT,
90+
});
91+
92+
const depthTexture = this.device.createTexture({
93+
size: { width: 1, height: 1, depthOrArrayLayers: 1 },
94+
format: depthFormat,
95+
sampleCount: 1,
96+
mipLevelCount: 1,
97+
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC,
98+
});
99+
100+
const depthStencilAttachment: GPURenderPassDepthStencilAttachment = {
101+
view: depthTexture.createView(),
102+
depthLoadOp: 'load',
103+
depthStoreOp: 'store',
104+
stencilLoadOp: 'load',
105+
stencilStoreOp: 'store',
106+
};
107+
108+
const encoder = this.device.createCommandEncoder();
109+
const pass = encoder.beginRenderPass({
110+
colorAttachments: [
111+
{
112+
view: renderTarget.createView(),
113+
storeOp: 'store',
114+
loadOp: 'load',
115+
},
116+
],
117+
depthStencilAttachment,
118+
});
119+
120+
let depthCompare: GPUCompareFunction = 'always';
121+
if (depthFormat !== 'depth32float') {
122+
depthCompare = 'greater';
123+
}
124+
125+
const testState = {
126+
format: depthFormat,
127+
depthCompare,
128+
depthWriteEnabled: true,
129+
depthBias: bias,
130+
depthBiasSlopeScale: biasSlopeScale,
131+
depthBiasClamp: biasClamp,
132+
} as const;
133+
134+
// Draw a square with the given depth state and bias values.
135+
const testPipeline = this.createRenderPipelineForTest(vertexShaderCode, testState);
136+
pass.setPipeline(testPipeline);
137+
pass.draw(6);
138+
pass.end();
139+
this.device.queue.submit([encoder.finish()]);
140+
141+
const expColor = { Depth: expectedDepth };
142+
const expTexelView = TexelView.fromTexelsAsColors(depthFormat, coords => expColor);
143+
144+
const result = textureContentIsOKByT2B(
145+
this,
146+
{ texture: depthTexture },
147+
[1, 1],
148+
{ expTexelView },
149+
{ maxDiffULPsForFloatFormat: 1 }
150+
);
151+
this.eventualExpectOK(result);
152+
this.trackForCleanup(renderTarget);
153+
}
154+
155+
createRenderPipelineForTest(
156+
vertex: string,
157+
depthStencil: GPUDepthStencilState
158+
): GPURenderPipeline {
159+
return this.device.createRenderPipeline({
160+
layout: 'auto',
161+
vertex: {
162+
module: this.device.createShaderModule({
163+
code: vertex,
164+
}),
165+
entryPoint: 'main',
166+
},
167+
fragment: {
168+
targets: [{ format: 'rgba8unorm' }],
169+
module: this.device.createShaderModule({
170+
code: `
171+
@fragment fn main() -> @location(0) vec4<f32> {
172+
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
173+
}`,
174+
}),
175+
entryPoint: 'main',
176+
},
177+
depthStencil,
178+
});
179+
}
180+
}
181+
182+
export const g = makeTestGroup(DepthBiasTest);
183+
184+
g.test('depth_bias')
185+
.desc(
186+
`
187+
Tests that a square with different depth bias values like 'positive', 'negative', 'infinity',
188+
'slope', 'clamp', etc. is drawn as expected.
189+
190+
TODO: Need to test 'depth24plus-stencil8' format?
191+
`
192+
)
193+
.params(u =>
194+
u //
195+
.combineWithParams([
196+
{
197+
quadAngle: QuadAngle.Flat,
198+
bias: kPointTwoFiveBiasForPointTwoFiveZOnFloat,
199+
biasSlopeScale: 0,
200+
biasClamp: 0,
201+
expectedDepth: 0.5,
202+
},
203+
{
204+
quadAngle: QuadAngle.Flat,
205+
bias: kPointTwoFiveBiasForPointTwoFiveZOnFloat,
206+
biasSlopeScale: 0,
207+
biasClamp: 0.125,
208+
expectedDepth: 0.375,
209+
},
210+
{
211+
quadAngle: QuadAngle.Flat,
212+
bias: -kPointTwoFiveBiasForPointTwoFiveZOnFloat,
213+
biasSlopeScale: 0,
214+
biasClamp: 0.125,
215+
expectedDepth: 0,
216+
},
217+
{
218+
quadAngle: QuadAngle.Flat,
219+
bias: -kPointTwoFiveBiasForPointTwoFiveZOnFloat,
220+
biasSlopeScale: 0,
221+
biasClamp: -0.125,
222+
expectedDepth: 0.125,
223+
},
224+
{
225+
quadAngle: QuadAngle.TiltedX,
226+
bias: 0,
227+
biasSlopeScale: 0,
228+
biasClamp: 0,
229+
expectedDepth: 0.25,
230+
},
231+
{
232+
quadAngle: QuadAngle.TiltedX,
233+
bias: 0,
234+
biasSlopeScale: 1,
235+
biasClamp: 0,
236+
expectedDepth: 0.75,
237+
},
238+
{
239+
quadAngle: QuadAngle.TiltedX,
240+
bias: 0,
241+
biasSlopeScale: -0.5,
242+
biasClamp: 0,
243+
expectedDepth: 0,
244+
},
245+
{
246+
quadAngle: QuadAngle.TiltedX,
247+
bias: 0,
248+
biasSlopeScale: kValue.f32.infinity.positive,
249+
biasClamp: 0,
250+
expectedDepth: 1,
251+
},
252+
{
253+
quadAngle: QuadAngle.TiltedX,
254+
bias: 0,
255+
biasSlopeScale: kValue.f32.infinity.negative,
256+
biasClamp: 0,
257+
expectedDepth: 0,
258+
},
259+
] as const)
260+
)
261+
.fn(async t => {
262+
t.runDepthBiasTest('depth32float', t.params);
263+
});

0 commit comments

Comments
 (0)