Skip to content

Commit

Permalink
fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
kainino0x committed Sep 23, 2021
1 parent 6a24a08 commit 1d6a59c
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 32 deletions.
88 changes: 64 additions & 24 deletions src/webgpu/api/operation/rendering/depth_range.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,22 @@ import { makeTestGroup } from '../../../../common/framework/test_group.js';
import { kDepthStencilFormats, kTextureFormatInfo } from '../../../capability_info.js';
import { GPUTest } from '../../../gpu_test.js';
import {
checkElementsBetween,
checkElementsPassPredicate,
CheckElementsSupplementalTableRows,
} from '../../../util/check_contents.js';

export const g = makeTestGroup(GPUTest);

const kNumDepthValues = 6;
/** Number of depth values to test for both vertex output and frag_depth output. */
const kNumDepthValues = 8;
/** Test every combination of vertex output and frag_depth output. */
const kNumTestPoints = kNumDepthValues * kNumDepthValues;
const kViewportMinDepth = 0.25;
const kViewportMaxDepth = 0.75;

g.test('depth_clip')
.desc(`If clipping, first 4 and last 4 should be clipped.`)
.desc(`FIXME`)
.params(u =>
u //
.combine('format', kDepthStencilFormats)
Expand All @@ -36,61 +39,86 @@ g.test('depth_clip')
const info = kTextureFormatInfo[format];

const kShaderSource = `
// Test depths, with viewport range corresponding to [0,1].
var<private> kDepths: array<f32, ${kNumDepthValues}> = array<f32, ${kNumDepthValues}>(
-0.1, 0.0, ${kViewportMinDepth}, ${kViewportMaxDepth}, 1.0, 1.1);
-1.0, -0.5, 0.0, 0.25, 0.75, 1.0, 1.5, 2.0);
struct VF {
[[builtin(position)]] pos: vec4<f32>;
[[location(0)]] z: f32;
[[location(1)]] writeDepth: f32;
[[location(2)]] vertexIndex: u32;
};
let vpMin: f32 = ${kViewportMinDepth};
let vpMax: f32 = ${kViewportMaxDepth};
// Draw the points in a straight horizontal row, one per pixel.
fn vertexX(idx: u32) -> f32 {
return (f32(idx) + 0.5) * 2.0 / ${kNumTestPoints}.0 - 1.0;
}
// Test vertex shader's position.z output.
// The viewport range corresponds to position.z in [0,1].
fn vertexZ(idx: u32) -> f32 {
return kDepths[idx / ${kNumDepthValues}u];
}
// Test fragment shader's expected position.z input.
// The viewport range corresponds to position.z in [vpMin,vpMax]; values extend beyond that.
fn expectedFragPosZ(idx: u32) -> f32 {
return vpMin + vertexZ(idx) * (vpMax - vpMin);
}
//////// "Test" entry points
struct VF {
[[builtin(position)]] pos: vec4<f32>;
[[location(0)]] vertexIndex: u32;
};
[[stage(vertex)]]
fn vmain([[builtin(vertex_index)]] idx: u32) -> VF {
var vf: VF;
vf.z = kDepths[idx / ${kNumDepthValues}u];
vf.pos = vec4<f32>(vertexX(idx), 0.0, vf.z, 1.0);
vf.writeDepth = kDepths[idx % ${kNumDepthValues}u];
vf.pos = vec4<f32>(vertexX(idx), 0.0, vertexZ(idx), 1.0);
vf.vertexIndex = idx;
return vf;
}
[[block]] struct Output { fragInputZDiff: array<f32, ${kNumTestPoints}>; };
[[block]] struct Output {
// Each fragment (that didn't get clipped) writes into one element of this output.
// (Anything that doesn't get written is zero.)
fragInputZDiff: array<f32, ${kNumTestPoints}>;
};
[[group(0), binding(0)]] var <storage, read_write> output: Output;
fn checkZ(vf: VF) {
output.fragInputZDiff[vf.vertexIndex] = vf.pos.z - vf.z;
output.fragInputZDiff[vf.vertexIndex] = vf.pos.z - expectedFragPosZ(vf.vertexIndex);
}
[[stage(fragment)]]
fn fmain_WriteDepth(vf: VF) -> [[builtin(frag_depth)]] f32 {
checkZ(vf);
return vf.writeDepth;
return kDepths[vf.vertexIndex % ${kNumDepthValues}u];
}
[[stage(fragment)]]
fn fmain_NoWriteDepth(vf: VF) {
checkZ(vf);
}
//////// "Check" entry points
[[stage(vertex)]]
fn vcheck([[builtin(vertex_index)]] idx: u32) -> [[builtin(position)]] vec4<f32> {
let rasterDepth = kDepths[idx / ${kNumDepthValues}u];
let outOfRange = rasterDepth < 0.0 || rasterDepth > 1.0;
let vertZ = vertexZ(idx);
let outOfRange = vertZ < 0.0 || vertZ > 1.0;
let expFragPosZ = expectedFragPosZ(idx);
let writtenDepth = kDepths[idx % ${kNumDepthValues}u];
var expectedZ = clamp(${writeDepth ? 'writtenDepth' : 'rasterDepth'},
${kViewportMinDepth}, ${kViewportMaxDepth});
let expectedDepthWriteInput = ${writeDepth ? 'writtenDepth' : 'expFragPosZ'};
var expectedDepthBufferValue = clamp(expectedDepthWriteInput, vpMin, vpMax);
if (${!clampDepth} && outOfRange) {
// Test fragment should have been clipped; expect the depth attachment to
// have its clear value (0.5).
expectedZ = 0.5;
expectedDepthBufferValue = 0.5;
}
return vec4<f32>(vertexX(idx), 0.0, expectedZ, 1.0);
return vec4<f32>(vertexX(idx), 0.0, expectedDepthBufferValue, 1.0);
}
[[stage(fragment)]]
Expand All @@ -101,7 +129,12 @@ g.test('depth_clip')
const module = t.device.createShaderModule({ code: kShaderSource });
const testPipeline = t.device.createRenderPipeline({
vertex: { module, entryPoint: 'vmain' },
primitive: { topology: 'point-list', clampDepth: clampDepth || undefined },
primitive: {
topology: 'point-list',
// `|| undefined` is a workaround for Chromium not allowing `false` here
// when the feature is unavailable.
clampDepth: clampDepth || undefined,
},
depthStencil: { format, depthWriteEnabled: true },
multisample: multisampled ? { count: 4 } : undefined,
fragment: {
Expand All @@ -115,7 +148,9 @@ g.test('depth_clip')
primitive: { topology: 'point-list' },
depthStencil: {
format,
depthCompare: 'not-equal',
// TODO: This check is probably very susceptible to floating point error.
// Replace it with two checks (less + greater) with an epsilon applied in the check shader?
depthCompare: 'not-equal', // Expect every depth value to be exactly equal.
depthWriteEnabled: true, // If the check failed, overwrite with the expected result
},
multisample: multisampled ? { count: 4 } : undefined,
Expand Down Expand Up @@ -216,6 +251,7 @@ g.test('depth_clip')
},
});
pass.setPipeline(checkPipeline);
pass.setViewport(0, 0, kNumTestPoints, 1, 0.0, 1.0);
pass.draw(kNumTestPoints);
pass.endPass();
}
Expand All @@ -226,7 +262,11 @@ g.test('depth_clip')
const cb = enc.finish();
t.device.queue.submit([cb]);

t.expectGPUBufferValuesEqual(fragInputZFailedBuffer, new Float32Array(kNumTestPoints));
t.expectGPUBufferValuesPassCheck(
fragInputZFailedBuffer,
a => checkElementsBetween(a, [() => -1e-7, () => 1e-7]),
{ type: Float32Array, typedLength: kNumTestPoints }
);

const kCheckPassedValue = 0;
const predicatePrinter: CheckElementsSupplementalTableRows = [
Expand Down
2 changes: 1 addition & 1 deletion src/webgpu/gpu_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -583,7 +583,7 @@ export class GPUTest extends Fixture {
slice = 0,
layout,
generateWarningOnly = false,
checkElementsBetweenFn = checkElementsBetween,
checkElementsBetweenFn = (act, [a, b]) => checkElementsBetween(act, [i => a[i], i => b[i]]),
}: {
exp: [TypedArrayBufferView, TypedArrayBufferView];
slice?: number;
Expand Down
14 changes: 7 additions & 7 deletions src/webgpu/util/check_contents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export type CheckElementsSupplementalTableRows = Array<{
/**
* Get the value for a cell in the table with element index `index`.
* May be a string or a number; a number will be formatted according to the TypedArray type used.
*/
*/
getValueForCell: (index: number) => number | string;
}>;

Expand All @@ -42,21 +42,21 @@ export function checkElementsEqual(

/**
* Check whether each value in a `TypedArray` is between the two corresponding "expected" values
* (either `a[i] <= actual[i] <= b[i]` or `a[i] >= actual[i] => b[i]`).
* (either `a(i) <= actual[i] <= b(i)` or `a(i) >= actual[i] => b(i)`).
*/
export function checkElementsBetween(
actual: TypedArrayBufferView,
expected: readonly [TypedArrayBufferView, TypedArrayBufferView]
expected: readonly [CheckElementsGenerator, CheckElementsGenerator]
): ErrorWithExtra | undefined {
const error = checkElementsPassPredicate(
actual,
(index, value) =>
value >= Math.min(expected[0][index], expected[1][index]) &&
value <= Math.max(expected[0][index], expected[1][index]),
value >= Math.min(expected[0](index), expected[1](index)) &&
value <= Math.max(expected[0](index), expected[1](index)),
{
predicatePrinter: [
{ leftHeader: 'between', getValueForCell: index => expected[0][index] },
{ leftHeader: 'and', getValueForCell: index => expected[1][index] },
{ leftHeader: 'between', getValueForCell: index => expected[0](index) },
{ leftHeader: 'and', getValueForCell: index => expected[1](index) },
],
}
);
Expand Down

0 comments on commit 1d6a59c

Please sign in to comment.