summaryrefslogtreecommitdiffstats
path: root/dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/rendering/depth_bias.spec.ts
diff options
context:
space:
mode:
Diffstat (limited to 'dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/rendering/depth_bias.spec.ts')
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/rendering/depth_bias.spec.ts369
1 files changed, 369 insertions, 0 deletions
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/rendering/depth_bias.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/rendering/depth_bias.spec.ts
new file mode 100644
index 0000000000..01e2dc904d
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/rendering/depth_bias.spec.ts
@@ -0,0 +1,369 @@
+export const description = `
+Tests render results with different depth bias values like 'positive', 'negative',
+'slope', 'clamp', etc.
+`;
+
+import { makeTestGroup } from '../../../../common/framework/test_group.js';
+import { unreachable } from '../../../../common/util/util.js';
+import {
+ DepthStencilFormat,
+ EncodableTextureFormat,
+ kTextureFormatInfo,
+} from '../../../capability_info.js';
+import { GPUTest } from '../../../gpu_test.js';
+import { TexelView } from '../../../util/texture/texel_view.js';
+import { textureContentIsOKByT2B } from '../../../util/texture/texture_ok.js';
+
+enum QuadAngle {
+ Flat,
+ TiltedX,
+}
+
+// Floating point depth buffers use the following formula to calculate bias
+// bias = depthBias * 2 ** (exponent(max z of primitive) - number of bits in mantissa) +
+// slopeScale * maxSlope
+// https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-output-merger-stage-depth-bias
+// https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkCmdSetDepthBias.html
+// https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1516269-setdepthbias
+//
+// To get a final bias of 0.25 for primitives with z = 0.25, we can use
+// depthBias = 0.25 / (2 ** (-2 - 23)) = 8388608.
+const kPointTwoFiveBiasForPointTwoFiveZOnFloat = 8388608;
+
+class DepthBiasTest extends GPUTest {
+ runDepthBiasTestInternal(
+ depthFormat: DepthStencilFormat,
+ {
+ quadAngle,
+ bias,
+ biasSlopeScale,
+ biasClamp,
+ initialDepth,
+ }: {
+ quadAngle: QuadAngle;
+ bias: number;
+ biasSlopeScale: number;
+ biasClamp: number;
+ initialDepth: number;
+ }
+ ): { renderTarget: GPUTexture; depthTexture: GPUTexture } {
+ const renderTargetFormat = 'rgba8unorm';
+ const depthFormatInfo = kTextureFormatInfo[depthFormat];
+
+ let vertexShaderCode: string;
+ switch (quadAngle) {
+ case QuadAngle.Flat:
+ // Draw a square at z = 0.25.
+ vertexShaderCode = `
+ @vertex
+ fn main(@builtin(vertex_index) VertexIndex : u32) -> @builtin(position) vec4<f32> {
+ var pos = array<vec2<f32>, 6>(
+ vec2<f32>(-1.0, -1.0),
+ vec2<f32>( 1.0, -1.0),
+ vec2<f32>(-1.0, 1.0),
+ vec2<f32>(-1.0, 1.0),
+ vec2<f32>( 1.0, -1.0),
+ vec2<f32>( 1.0, 1.0));
+ return vec4<f32>(pos[VertexIndex], 0.25, 1.0);
+ }
+ `;
+ break;
+ case QuadAngle.TiltedX:
+ // Draw a square ranging from 0 to 0.5, bottom to top.
+ vertexShaderCode = `
+ @vertex
+ fn main(@builtin(vertex_index) VertexIndex : u32) -> @builtin(position) vec4<f32> {
+ var pos = array<vec3<f32>, 6>(
+ vec3<f32>(-1.0, -1.0, 0.0),
+ vec3<f32>( 1.0, -1.0, 0.0),
+ vec3<f32>(-1.0, 1.0, 0.5),
+ vec3<f32>(-1.0, 1.0, 0.5),
+ vec3<f32>( 1.0, -1.0, 0.0),
+ vec3<f32>( 1.0, 1.0, 0.5));
+ return vec4<f32>(pos[VertexIndex], 1.0);
+ }
+ `;
+ break;
+ default:
+ unreachable();
+ }
+
+ const renderTarget = this.device.createTexture({
+ format: renderTargetFormat,
+ size: { width: 1, height: 1, depthOrArrayLayers: 1 },
+ usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.RENDER_ATTACHMENT,
+ });
+
+ const depthTexture = this.device.createTexture({
+ size: { width: 1, height: 1, depthOrArrayLayers: 1 },
+ format: depthFormat,
+ sampleCount: 1,
+ mipLevelCount: 1,
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC,
+ });
+
+ const depthStencilAttachment: GPURenderPassDepthStencilAttachment = {
+ view: depthTexture.createView(),
+ depthLoadOp: depthFormatInfo.depth ? 'clear' : undefined,
+ depthStoreOp: depthFormatInfo.depth ? 'store' : undefined,
+ stencilLoadOp: depthFormatInfo.stencil ? 'clear' : undefined,
+ stencilStoreOp: depthFormatInfo.stencil ? 'store' : undefined,
+ depthClearValue: initialDepth,
+ };
+
+ const encoder = this.device.createCommandEncoder();
+ const pass = encoder.beginRenderPass({
+ colorAttachments: [
+ {
+ view: renderTarget.createView(),
+ storeOp: 'store',
+ loadOp: 'load',
+ },
+ ],
+ depthStencilAttachment,
+ });
+
+ let depthCompare: GPUCompareFunction = 'always';
+ if (depthFormat !== 'depth32float') {
+ depthCompare = 'greater';
+ }
+
+ const testState = {
+ format: depthFormat,
+ depthCompare,
+ depthWriteEnabled: true,
+ depthBias: bias,
+ depthBiasSlopeScale: biasSlopeScale,
+ depthBiasClamp: biasClamp,
+ } as const;
+
+ // Draw a square with the given depth state and bias values.
+ const testPipeline = this.createRenderPipelineForTest(vertexShaderCode, testState);
+ pass.setPipeline(testPipeline);
+ pass.draw(6);
+ pass.end();
+ this.device.queue.submit([encoder.finish()]);
+
+ return { renderTarget, depthTexture };
+ }
+
+ runDepthBiasTest(
+ depthFormat: EncodableTextureFormat & DepthStencilFormat,
+ {
+ quadAngle,
+ bias,
+ biasSlopeScale,
+ biasClamp,
+ _expectedDepth,
+ }: {
+ quadAngle: QuadAngle;
+ bias: number;
+ biasSlopeScale: number;
+ biasClamp: number;
+ _expectedDepth: number;
+ }
+ ) {
+ const { renderTarget, depthTexture } = this.runDepthBiasTestInternal(depthFormat, {
+ quadAngle,
+ bias,
+ biasSlopeScale,
+ biasClamp,
+ initialDepth: 0,
+ });
+
+ const expColor = { Depth: _expectedDepth };
+ const expTexelView = TexelView.fromTexelsAsColors(depthFormat, coords => expColor);
+
+ const result = textureContentIsOKByT2B(
+ this,
+ { texture: depthTexture },
+ [1, 1],
+ { expTexelView },
+ { maxDiffULPsForFloatFormat: 1 }
+ );
+ this.eventualExpectOK(result);
+ this.trackForCleanup(renderTarget);
+ this.trackForCleanup(depthTexture);
+ }
+
+ runDepthBiasTestFor24BitFormat(
+ depthFormat: DepthStencilFormat,
+ {
+ quadAngle,
+ bias,
+ biasSlopeScale,
+ biasClamp,
+ _expectedColor,
+ }: {
+ quadAngle: QuadAngle;
+ bias: number;
+ biasSlopeScale: number;
+ biasClamp: number;
+ _expectedColor: Float32Array;
+ }
+ ) {
+ const { renderTarget, depthTexture } = this.runDepthBiasTestInternal(depthFormat, {
+ quadAngle,
+ bias,
+ biasSlopeScale,
+ biasClamp,
+ initialDepth: 0.4,
+ });
+
+ const renderTargetFormat = 'rgba8unorm';
+ const expColor = {
+ R: _expectedColor[0],
+ G: _expectedColor[1],
+ B: _expectedColor[2],
+ A: _expectedColor[3],
+ };
+ const expTexelView = TexelView.fromTexelsAsColors(renderTargetFormat, coords => expColor);
+
+ const result = textureContentIsOKByT2B(
+ this,
+ { texture: renderTarget },
+ [1, 1],
+ { expTexelView },
+ { maxDiffULPsForNormFormat: 1 }
+ );
+ this.eventualExpectOK(result);
+ this.trackForCleanup(renderTarget);
+ this.trackForCleanup(depthTexture);
+ }
+
+ createRenderPipelineForTest(
+ vertex: string,
+ depthStencil: GPUDepthStencilState
+ ): GPURenderPipeline {
+ return this.device.createRenderPipeline({
+ layout: 'auto',
+ vertex: {
+ module: this.device.createShaderModule({
+ code: vertex,
+ }),
+ entryPoint: 'main',
+ },
+ fragment: {
+ targets: [{ format: 'rgba8unorm' }],
+ module: this.device.createShaderModule({
+ code: `
+ @fragment fn main() -> @location(0) vec4<f32> {
+ return vec4<f32>(1.0, 0.0, 0.0, 1.0);
+ }`,
+ }),
+ entryPoint: 'main',
+ },
+ depthStencil,
+ });
+ }
+}
+
+export const g = makeTestGroup(DepthBiasTest);
+
+g.test('depth_bias')
+ .desc(
+ `
+ Tests that a square with different depth bias values like 'positive', 'negative',
+ 'slope', 'clamp', etc. is drawn as expected.
+ `
+ )
+ .params(u =>
+ u //
+ .combineWithParams([
+ {
+ quadAngle: QuadAngle.Flat,
+ bias: kPointTwoFiveBiasForPointTwoFiveZOnFloat,
+ biasSlopeScale: 0,
+ biasClamp: 0,
+ _expectedDepth: 0.5,
+ },
+ {
+ quadAngle: QuadAngle.Flat,
+ bias: kPointTwoFiveBiasForPointTwoFiveZOnFloat,
+ biasSlopeScale: 0,
+ biasClamp: 0.125,
+ _expectedDepth: 0.375,
+ },
+ {
+ quadAngle: QuadAngle.Flat,
+ bias: -kPointTwoFiveBiasForPointTwoFiveZOnFloat,
+ biasSlopeScale: 0,
+ biasClamp: 0.125,
+ _expectedDepth: 0,
+ },
+ {
+ quadAngle: QuadAngle.Flat,
+ bias: -kPointTwoFiveBiasForPointTwoFiveZOnFloat,
+ biasSlopeScale: 0,
+ biasClamp: -0.125,
+ _expectedDepth: 0.125,
+ },
+ {
+ quadAngle: QuadAngle.TiltedX,
+ bias: 0,
+ biasSlopeScale: 0,
+ biasClamp: 0,
+ _expectedDepth: 0.25,
+ },
+ {
+ quadAngle: QuadAngle.TiltedX,
+ bias: 0,
+ biasSlopeScale: 1,
+ biasClamp: 0,
+ _expectedDepth: 0.75,
+ },
+ {
+ quadAngle: QuadAngle.TiltedX,
+ bias: 0,
+ biasSlopeScale: -0.5,
+ biasClamp: 0,
+ _expectedDepth: 0,
+ },
+ ] as const)
+ )
+ .fn(async t => {
+ t.runDepthBiasTest('depth32float', t.params);
+ });
+
+g.test('depth_bias_24bit_format')
+ .desc(
+ `
+ Tests that a square with different depth bias values like 'positive', 'negative',
+ 'slope', 'clamp', etc. is drawn as expected with 24 bit depth format.
+
+ TODO: Enhance these tests by reading back the depth (emulating the copy using texture sampling)
+ and checking the result directly, like the non-24-bit depth tests, instead of just relying on
+ whether the depth test passes or fails.
+ `
+ )
+ .params(u =>
+ u //
+ .combine('format', ['depth24plus', 'depth24plus-stencil8'] as const)
+ .combineWithParams([
+ {
+ quadAngle: QuadAngle.Flat,
+ bias: 0.25 * (1 << 25),
+ biasSlopeScale: 0,
+ biasClamp: 0,
+ _expectedColor: new Float32Array([1.0, 0.0, 0.0, 1.0]),
+ },
+ {
+ quadAngle: QuadAngle.TiltedX,
+ bias: 0.25 * (1 << 25),
+ biasSlopeScale: 1,
+ biasClamp: 0,
+ _expectedColor: new Float32Array([1.0, 0.0, 0.0, 1.0]),
+ },
+ {
+ quadAngle: QuadAngle.Flat,
+ bias: 0.25 * (1 << 25),
+ biasSlopeScale: 0,
+ biasClamp: 0.1,
+ _expectedColor: new Float32Array([0.0, 0.0, 0.0, 0.0]),
+ },
+ ] as const)
+ )
+ .fn(async t => {
+ const { format } = t.params;
+ t.runDepthBiasTestFor24BitFormat(format, t.params);
+ });