export const description = `
This test dedicatedly tests validation of GPUDepthStencilState of createRenderPipeline.
`;
import { makeTestGroup } from '../../../../common/framework/test_group.js';
import { unreachable } from '../../../../common/util/util.js';
import {
kCompareFunctions,
kPrimitiveTopology,
kStencilOperations,
} from '../../../capability_info.js';
import {
kAllTextureFormats,
kDepthStencilFormats,
isDepthOrStencilTextureFormat,
isDepthTextureFormat,
isStencilTextureFormat,
} from '../../../format_info.js';
import { getFragmentShaderCodeWithOutput } from '../../../util/shader.js';
import * as vtu from '../validation_test_utils.js';
import { CreateRenderPipelineValidationTest } from './common.js';
export const g = makeTestGroup(CreateRenderPipelineValidationTest);
g.test('format')
.desc(`The texture format in depthStencilState must be a depth/stencil format.`)
.params(u =>
u
.combine('isAsync', [false, true])
.combine('format', kAllTextureFormats)
)
.fn(t => {
const { isAsync, format } = t.params;
t.skipIfTextureFormatNotSupported(format);
const descriptor = t.getDescriptor({
depthStencil: { format, depthWriteEnabled: false, depthCompare: 'always' },
});
vtu.doCreateRenderPipelineTest(t, isAsync, isDepthOrStencilTextureFormat(format), descriptor);
});
g.test('depthCompare_optional')
.desc(
`The depthCompare in depthStencilState is optional for stencil-only formats but
required for formats with a depth if depthCompare is not used for anything.`
)
.params(u =>
u
.combine('isAsync', [false, true])
.combine('format', kDepthStencilFormats)
.beginSubcases()
.combine('depthCompare', ['always', undefined] as const)
.combine('depthWriteEnabled', [false, true, undefined] as const)
.combine('stencilFrontDepthFailOp', ['keep', 'zero'] as const)
.combine('stencilBackDepthFailOp', ['keep', 'zero'] as const)
)
.fn(t => {
const {
isAsync,
format,
depthCompare,
depthWriteEnabled,
stencilFrontDepthFailOp,
stencilBackDepthFailOp,
} = t.params;
t.skipIfTextureFormatNotSupported(format);
const descriptor = t.getDescriptor({
depthStencil: {
format,
depthCompare,
depthWriteEnabled,
stencilFront: { depthFailOp: stencilFrontDepthFailOp },
stencilBack: { depthFailOp: stencilBackDepthFailOp },
},
});
const depthFailOpsAreKeep =
stencilFrontDepthFailOp === 'keep' && stencilBackDepthFailOp === 'keep';
const stencilStateIsDefault = depthFailOpsAreKeep;
let success = true;
if (depthWriteEnabled || (depthCompare && depthCompare !== 'always')) {
if (!isDepthTextureFormat(format)) success = false;
}
if (!stencilStateIsDefault) {
if (!isStencilTextureFormat(format)) success = false;
}
if (isDepthTextureFormat(format)) {
if (depthWriteEnabled === undefined) success = false;
if (depthWriteEnabled || !depthFailOpsAreKeep) {
if (depthCompare === undefined) success = false;
}
}
vtu.doCreateRenderPipelineTest(t, isAsync, success, descriptor);
});
g.test('depthWriteEnabled_optional')
.desc(
`The depthWriteEnabled in depthStencilState is optional for stencil-only formats but required for formats with a depth.`
)
.params(u => u.combine('isAsync', [false, true]).combine('format', kDepthStencilFormats))
.fn(t => {
const { isAsync, format } = t.params;
t.skipIfTextureFormatNotSupported(format);
const descriptor = t.getDescriptor({
depthStencil: { format, depthCompare: 'always', depthWriteEnabled: undefined },
});
vtu.doCreateRenderPipelineTest(t, isAsync, !isDepthTextureFormat(format), descriptor);
});
g.test('depth_test')
.desc(
`Depth aspect must be contained in the format if depth test is enabled in depthStencilState.`
)
.params(u =>
u
.combine('isAsync', [false, true])
.combine('format', kDepthStencilFormats)
.combine('depthCompare', kCompareFunctions)
)
.fn(t => {
const { isAsync, format, depthCompare } = t.params;
t.skipIfTextureFormatNotSupported(format);
const descriptor = t.getDescriptor({
depthStencil: { format, depthCompare, depthWriteEnabled: false },
});
const depthTestEnabled = depthCompare !== undefined && depthCompare !== 'always';
vtu.doCreateRenderPipelineTest(
t,
isAsync,
!depthTestEnabled || isDepthTextureFormat(format),
descriptor
);
});
g.test('depth_write')
.desc(
`Depth aspect must be contained in the format if depth write is enabled in depthStencilState.`
)
.params(u =>
u
.combine('isAsync', [false, true])
.combine('format', kDepthStencilFormats)
.combine('depthWriteEnabled', [false, true])
)
.fn(t => {
const { isAsync, format, depthWriteEnabled } = t.params;
t.skipIfTextureFormatNotSupported(format);
const descriptor = t.getDescriptor({
depthStencil: { format, depthWriteEnabled, depthCompare: 'always' },
});
vtu.doCreateRenderPipelineTest(
t,
isAsync,
!depthWriteEnabled || isDepthTextureFormat(format),
descriptor
);
});
g.test('depth_write,frag_depth')
.desc(`Depth aspect must be contained in the format if frag_depth is written in fragment stage.`)
.params(u =>
u.combine('isAsync', [false, true]).combine('format', [undefined, ...kDepthStencilFormats])
)
.fn(t => {
const { isAsync, format } = t.params;
t.skipIfTextureFormatNotSupported(format);
const descriptor = t.getDescriptor({
targets: [{ format: 'rgba8unorm' }],
depthStencil: format
? { format, depthWriteEnabled: true, depthCompare: 'always' }
: undefined,
fragmentShaderCode: getFragmentShaderCodeWithOutput(
[{ values: [1, 1, 1, 1], plainType: 'f32', componentCount: 4 }],
{ value: 0.5 }
),
});
const hasDepth = format ? isDepthTextureFormat(format) : false;
vtu.doCreateRenderPipelineTest(t, isAsync, hasDepth, descriptor);
});
g.test('depth_bias')
.desc(`Depth bias parameters are only valid with triangle topologies.`)
.params(u =>
u
.combine('isAsync', [false, true])
.combine('topology', kPrimitiveTopology)
.beginSubcases()
.combineWithParams([
{},
{ depthBias: -1 },
{ depthBias: 0 },
{ depthBias: 1 },
{ depthBiasSlopeScale: -1 },
{ depthBiasSlopeScale: 0 },
{ depthBiasSlopeScale: 1 },
{ depthBiasClamp: -1 },
{ depthBiasClamp: 0 },
{ depthBiasClamp: 1 },
])
)
.fn(t => {
const { isAsync, topology, depthBias, depthBiasSlopeScale, depthBiasClamp } = t.params;
if (t.isCompatibility && !!depthBiasClamp) {
t.skip('depthBiasClamp must be 0 on compatibility mode');
}
const isTriangleTopology = topology === 'triangle-list' || topology === 'triangle-strip';
const hasDepthBias = !!depthBias || !!depthBiasSlopeScale || !!depthBiasClamp;
const shouldSucceed = !hasDepthBias || isTriangleTopology;
const descriptor = t.getDescriptor({
primitive: { topology },
depthStencil: {
format: 'depth24plus',
depthWriteEnabled: true,
depthCompare: 'less-equal',
depthBias,
depthBiasSlopeScale,
depthBiasClamp,
},
});
vtu.doCreateRenderPipelineTest(t, isAsync, shouldSucceed, descriptor);
});
g.test('stencil_test')
.desc(
`Stencil aspect must be contained in the format if stencil test is enabled in depthStencilState.`
)
.params(u =>
u
.combine('isAsync', [false, true])
.combine('format', kDepthStencilFormats)
.combine('face', ['front', 'back'] as const)
.combine('compare', [undefined, ...kCompareFunctions])
)
.fn(t => {
const { isAsync, format, face, compare } = t.params;
t.skipIfTextureFormatNotSupported(format);
let descriptor: GPURenderPipelineDescriptor;
if (face === 'front') {
descriptor = t.getDescriptor({
depthStencil: {
format,
depthWriteEnabled: false,
depthCompare: 'always',
stencilFront: { compare },
},
});
} else {
descriptor = t.getDescriptor({
depthStencil: {
format,
depthWriteEnabled: false,
depthCompare: 'always',
stencilBack: { compare },
},
});
}
const stencilTestEnabled = compare !== undefined && compare !== 'always';
vtu.doCreateRenderPipelineTest(
t,
isAsync,
!stencilTestEnabled || isStencilTextureFormat(format),
descriptor
);
});
g.test('stencil_write')
.desc(
`Stencil aspect must be contained in the format if stencil write is enabled in depthStencilState.`
)
.params(u =>
u
.combine('isAsync', [false, true])
.combine('format', kDepthStencilFormats)
.combine('faceAndOpType', [
'frontFailOp',
'frontDepthFailOp',
'frontPassOp',
'backFailOp',
'backDepthFailOp',
'backPassOp',
] as const)
.combine('op', [undefined, ...kStencilOperations])
)
.fn(t => {
const { isAsync, format, faceAndOpType, op } = t.params;
t.skipIfTextureFormatNotSupported(format);
const common = {
format,
depthWriteEnabled: false,
depthCompare: 'always' as GPUCompareFunction,
};
let depthStencil: GPUDepthStencilState;
switch (faceAndOpType) {
case 'frontFailOp':
depthStencil = { ...common, stencilFront: { failOp: op } };
break;
case 'frontDepthFailOp':
depthStencil = { ...common, stencilFront: { depthFailOp: op } };
break;
case 'frontPassOp':
depthStencil = { ...common, stencilFront: { passOp: op } };
break;
case 'backFailOp':
depthStencil = { ...common, stencilBack: { failOp: op } };
break;
case 'backDepthFailOp':
depthStencil = { ...common, stencilBack: { depthFailOp: op } };
break;
case 'backPassOp':
depthStencil = { ...common, stencilBack: { passOp: op } };
break;
default:
unreachable();
}
const descriptor = t.getDescriptor({ depthStencil });
const stencilWriteEnabled = op !== undefined && op !== 'keep';
vtu.doCreateRenderPipelineTest(
t,
isAsync,
!stencilWriteEnabled || isStencilTextureFormat(format),
descriptor
);
});