summaryrefslogtreecommitdiffstats
path: root/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/render_pipeline
diff options
context:
space:
mode:
Diffstat (limited to 'dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/render_pipeline')
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/render_pipeline/common.ts11
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/render_pipeline/depth_stencil_state.spec.ts12
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/render_pipeline/fragment_state.spec.ts77
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/render_pipeline/inter_stage.spec.ts72
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/render_pipeline/misc.spec.ts34
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/render_pipeline/resource_compatibility.spec.ts95
6 files changed, 252 insertions, 49 deletions
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/render_pipeline/common.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/render_pipeline/common.ts
index 93b0932042..e0316a5517 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/render_pipeline/common.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/render_pipeline/common.ts
@@ -1,4 +1,4 @@
-import { kTextureFormatInfo } from '../../../format_info.js';
+import { ColorTextureFormat, kTextureFormatInfo } from '../../../format_info.js';
import {
getFragmentShaderCodeWithOutput,
getPlainTypeInfo,
@@ -6,12 +6,14 @@ import {
} from '../../../util/shader.js';
import { ValidationTest } from '../validation_test.js';
+type ColorTargetState = GPUColorTargetState & { format: ColorTextureFormat };
+
const values = [0, 1, 0, 1];
export class CreateRenderPipelineValidationTest extends ValidationTest {
getDescriptor(
options: {
primitive?: GPUPrimitiveState;
- targets?: GPUColorTargetState[];
+ targets?: ColorTargetState[];
multisample?: GPUMultisampleState;
depthStencil?: GPUDepthStencilState;
fragmentShaderCode?: string;
@@ -19,17 +21,16 @@ export class CreateRenderPipelineValidationTest extends ValidationTest {
fragmentConstants?: Record<string, GPUPipelineConstantValue>;
} = {}
): GPURenderPipelineDescriptor {
- const defaultTargets: GPUColorTargetState[] = [{ format: 'rgba8unorm' }];
const {
primitive = {},
- targets = defaultTargets,
+ targets = [{ format: 'rgba8unorm' }] as const,
multisample = {},
depthStencil,
fragmentShaderCode = getFragmentShaderCodeWithOutput([
{
values,
plainType: getPlainTypeInfo(
- kTextureFormatInfo[targets[0] ? targets[0].format : 'rgba8unorm'].sampleType
+ kTextureFormatInfo[targets[0] ? targets[0].format : 'rgba8unorm'].color.type
),
componentCount: 4,
},
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/render_pipeline/depth_stencil_state.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/render_pipeline/depth_stencil_state.spec.ts
index eaaf78af66..403f463943 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/render_pipeline/depth_stencil_state.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/render_pipeline/depth_stencil_state.spec.ts
@@ -5,7 +5,11 @@ This test dedicatedly tests validation of GPUDepthStencilState of createRenderPi
import { makeTestGroup } from '../../../../common/framework/test_group.js';
import { unreachable } from '../../../../common/util/util.js';
import { kCompareFunctions, kStencilOperations } from '../../../capability_info.js';
-import { kTextureFormats, kTextureFormatInfo, kDepthStencilFormats } from '../../../format_info.js';
+import {
+ kAllTextureFormats,
+ kTextureFormatInfo,
+ kDepthStencilFormats,
+} from '../../../format_info.js';
import { getFragmentShaderCodeWithOutput } from '../../../util/shader.js';
import { CreateRenderPipelineValidationTest } from './common.js';
@@ -14,7 +18,11 @@ 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', kTextureFormats))
+ .params(u =>
+ u //
+ .combine('isAsync', [false, true])
+ .combine('format', kAllTextureFormats)
+ )
.beforeAllSubcases(t => {
const { format } = t.params;
const info = kTextureFormatInfo[format];
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/render_pipeline/fragment_state.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/render_pipeline/fragment_state.spec.ts
index 0206431eee..c01c2ba9ef 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/render_pipeline/fragment_state.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/render_pipeline/fragment_state.spec.ts
@@ -10,15 +10,17 @@ import {
kMaxColorAttachmentsToTest,
} from '../../../capability_info.js';
import {
- kTextureFormats,
+ kAllTextureFormats,
kRenderableColorTextureFormats,
kTextureFormatInfo,
computeBytesPerSampleFromFormats,
+ kColorTextureFormats,
} from '../../../format_info.js';
import {
getFragmentShaderCodeWithOutput,
getPlainTypeInfo,
kDefaultFragmentShaderCode,
+ kDefaultVertexShaderCode,
} from '../../../util/shader.js';
import { kTexelRepresentationInfo } from '../../../util/texture/texel_data.js';
@@ -49,9 +51,60 @@ g.test('color_target_exists')
t.doCreateRenderPipelineTest(isAsync, false, badDescriptor);
});
+g.test('targets_format_is_color_format')
+ .desc(
+ `Tests that color target state format must be a color format, regardless of how the
+ fragment shader writes to it.`
+ )
+ .params(u =>
+ u
+ // Test all non-color texture formats, plus 'rgba8unorm' as a control case.
+ .combine('format', kAllTextureFormats)
+ .filter(({ format }) => {
+ return format === 'rgba8unorm' || !kTextureFormatInfo[format].color;
+ })
+ .combine('isAsync', [false, true])
+ .beginSubcases()
+ .combine('fragOutType', ['f32', 'u32', 'i32'] as const)
+ )
+ .beforeAllSubcases(t => {
+ const { format } = t.params;
+ const info = kTextureFormatInfo[format];
+ t.skipIfTextureFormatNotSupported(t.params.format);
+ t.selectDeviceOrSkipTestCase(info.feature);
+ })
+ .fn(t => {
+ const { isAsync, format, fragOutType } = t.params;
+
+ const fragmentShaderCode = getFragmentShaderCodeWithOutput([
+ { values, plainType: fragOutType, componentCount: 4 },
+ ]);
+
+ const success = format === 'rgba8unorm' && fragOutType === 'f32';
+ t.doCreateRenderPipelineTest(isAsync, success, {
+ vertex: {
+ module: t.device.createShaderModule({ code: kDefaultVertexShaderCode }),
+ entryPoint: 'main',
+ },
+ fragment: {
+ module: t.device.createShaderModule({ code: fragmentShaderCode }),
+ entryPoint: 'main',
+ targets: [{ format }],
+ },
+ layout: 'auto',
+ });
+ });
+
g.test('targets_format_renderable')
- .desc(`Tests that color target state format must have RENDER_ATTACHMENT capability.`)
- .params(u => u.combine('isAsync', [false, true]).combine('format', kTextureFormats))
+ .desc(
+ `Tests that color target state format must have RENDER_ATTACHMENT capability
+ (tests only color formats).`
+ )
+ .params(u =>
+ u //
+ .combine('isAsync', [false, true])
+ .combine('format', kColorTextureFormats)
+ )
.beforeAllSubcases(t => {
const { format } = t.params;
const info = kTextureFormatInfo[format];
@@ -158,24 +211,12 @@ g.test('limits,maxColorAttachmentBytesPerSample,unaligned')
// become 4 and 4+4+8+16+1 > 32. Re-ordering this so the R8Unorm's are at the end, however
// is allowed: 4+8+16+1+1 < 32.
{
- formats: [
- 'r8unorm',
- 'r32float',
- 'rgba8unorm',
- 'rgba32float',
- 'r8unorm',
- ] as GPUTextureFormat[],
+ formats: ['r8unorm', 'r32float', 'rgba8unorm', 'rgba32float', 'r8unorm'],
},
{
- formats: [
- 'r32float',
- 'rgba8unorm',
- 'rgba32float',
- 'r8unorm',
- 'r8unorm',
- ] as GPUTextureFormat[],
+ formats: ['r32float', 'rgba8unorm', 'rgba32float', 'r8unorm', 'r8unorm'],
},
- ])
+ ] as const)
.beginSubcases()
.combine('isAsync', [false, true])
)
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/render_pipeline/inter_stage.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/render_pipeline/inter_stage.spec.ts
index 91aabb0ab8..db65ba8bf4 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/render_pipeline/inter_stage.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/render_pipeline/inter_stage.spec.ts
@@ -3,7 +3,7 @@ Interface matching between vertex and fragment shader validation for createRende
`;
import { makeTestGroup } from '../../../../common/framework/test_group.js';
-import { assert, range } from '../../../../common/util/util.js';
+import { range } from '../../../../common/util/util.js';
import { CreateRenderPipelineValidationTest } from './common.js';
@@ -97,8 +97,18 @@ g.test('location,mismatch')
});
g.test('location,superset')
- .desc(`TODO: implement after spec is settled: https://github.com/gpuweb/gpuweb/issues/2038`)
- .unimplemented();
+ .desc(`Tests that validation should succeed when vertex output is superset of fragment input`)
+ .params(u => u.combine('isAsync', [false, true]))
+ .fn(t => {
+ const { isAsync } = t.params;
+
+ const descriptor = t.getDescriptorWithStates(
+ t.getVertexStateWithOutputs(['@location(0) vout0: f32', '@location(1) vout1: f32']),
+ t.getFragmentStateWithInputs(['@location(1) fin1: f32'])
+ );
+
+ t.doCreateRenderPipelineTest(isAsync, true, descriptor);
+ });
g.test('location,subset')
.desc(`Tests that validation should fail when vertex output is a subset of fragment input.`)
@@ -159,20 +169,27 @@ g.test('interpolation_type')
{ output: '@interpolate(linear)', input: '@interpolate(perspective)' },
{ output: '@interpolate(flat)', input: '@interpolate(perspective)' },
{ output: '@interpolate(linear)', input: '@interpolate(flat)' },
- { output: '@interpolate(linear, center)', input: '@interpolate(linear, center)' },
+ {
+ output: '@interpolate(linear, center)',
+ input: '@interpolate(linear, center)',
+ _compat_success: false,
+ },
])
)
.fn(t => {
- const { isAsync, output, input, _success } = t.params;
+ const { isAsync, output, input, _success, _compat_success } = t.params;
const descriptor = t.getDescriptorWithStates(
t.getVertexStateWithOutputs([`@location(0) ${output} vout0: f32`]),
t.getFragmentStateWithInputs([`@location(0) ${input} fin0: f32`])
);
- t.doCreateRenderPipelineTest(isAsync, _success ?? output === input, descriptor);
- });
+ const shouldSucceed =
+ (_success ?? output === input) && (!t.isCompatibility || _compat_success !== false);
+ t.doCreateRenderPipelineTest(isAsync, shouldSucceed, descriptor);
+ });
+1;
g.test('interpolation_sampling')
.desc(
`Tests that validation should fail when interpolation sampling of vertex output and fragment input at the same location doesn't match.`
@@ -186,7 +203,12 @@ g.test('interpolation_sampling')
input: '@interpolate(perspective, center)',
_success: true,
},
- { output: '@interpolate(linear, center)', input: '@interpolate(linear)', _success: true },
+ {
+ output: '@interpolate(linear, center)',
+ input: '@interpolate(linear)',
+ _success: true,
+ _compat_success: false,
+ },
{ output: '@interpolate(flat)', input: '@interpolate(flat)' },
{ output: '@interpolate(perspective)', input: '@interpolate(perspective, sample)' },
{ output: '@interpolate(perspective, center)', input: '@interpolate(perspective, sample)' },
@@ -198,14 +220,17 @@ g.test('interpolation_sampling')
])
)
.fn(t => {
- const { isAsync, output, input, _success } = t.params;
+ const { isAsync, output, input, _success, _compat_success } = t.params;
const descriptor = t.getDescriptorWithStates(
t.getVertexStateWithOutputs([`@location(0) ${output} vout0: f32`]),
t.getFragmentStateWithInputs([`@location(0) ${input} fin0: f32`])
);
- t.doCreateRenderPipelineTest(isAsync, _success ?? output === input, descriptor);
+ const shouldSucceed =
+ (_success ?? output === input) && (!t.isCompatibility || _compat_success !== false);
+
+ t.doCreateRenderPipelineTest(isAsync, shouldSucceed, descriptor);
});
g.test('max_shader_variable_location')
@@ -251,9 +276,6 @@ g.test('max_components_count,output')
const numVec4 = Math.floor(numScalarComponents / 4);
const numTrailingScalars = numScalarComponents % 4;
- const numUserDefinedInterStageVariables = numTrailingScalars > 0 ? numVec4 + 1 : numVec4;
-
- assert(numUserDefinedInterStageVariables <= t.device.limits.maxInterStageShaderVariables);
const outputs = range(numVec4, i => `@location(${i}) vout${i}: vec4<f32>`);
const inputs = range(numVec4, i => `@location(${i}) fin${i}: vec4<f32>`);
@@ -280,23 +302,23 @@ g.test('max_components_count,input')
.params(u =>
u.combine('isAsync', [false, true]).combineWithParams([
// Number of user-defined input scalar components in test shader = device.limits.maxInterStageShaderComponents + numScalarDelta.
- { numScalarDelta: 0, useExtraBuiltinInputs: false, _success: true },
- { numScalarDelta: 1, useExtraBuiltinInputs: false, _success: false },
- { numScalarDelta: 0, useExtraBuiltinInputs: true, _success: false },
- { numScalarDelta: -3, useExtraBuiltinInputs: true, _success: true },
- { numScalarDelta: -2, useExtraBuiltinInputs: true, _success: false },
+ { numScalarDelta: 0, useExtraBuiltinInputs: false },
+ { numScalarDelta: 1, useExtraBuiltinInputs: false },
+ { numScalarDelta: 0, useExtraBuiltinInputs: true },
+ { numScalarDelta: -3, useExtraBuiltinInputs: true },
+ { numScalarDelta: -2, useExtraBuiltinInputs: true },
] as const)
)
.fn(t => {
- const { isAsync, numScalarDelta, useExtraBuiltinInputs, _success } = t.params;
+ const { isAsync, numScalarDelta, useExtraBuiltinInputs } = t.params;
const numScalarComponents = t.device.limits.maxInterStageShaderComponents + numScalarDelta;
+ const numExtraComponents = useExtraBuiltinInputs ? (t.isCompatibility ? 2 : 3) : 0;
+ const numUsedComponents = numScalarComponents + numExtraComponents;
+ const success = numUsedComponents <= t.device.limits.maxInterStageShaderComponents;
const numVec4 = Math.floor(numScalarComponents / 4);
const numTrailingScalars = numScalarComponents % 4;
- const numUserDefinedInterStageVariables = numTrailingScalars > 0 ? numVec4 + 1 : numVec4;
-
- assert(numUserDefinedInterStageVariables <= t.device.limits.maxInterStageShaderVariables);
const outputs = range(numVec4, i => `@location(${i}) vout${i}: vec4<f32>`);
const inputs = range(numVec4, i => `@location(${i}) fin${i}: vec4<f32>`);
@@ -310,9 +332,11 @@ g.test('max_components_count,input')
if (useExtraBuiltinInputs) {
inputs.push(
'@builtin(front_facing) front_facing_in: bool',
- '@builtin(sample_index) sample_index_in: u32',
'@builtin(sample_mask) sample_mask_in: u32'
);
+ if (!t.isCompatibility) {
+ inputs.push('@builtin(sample_index) sample_index_in: u32');
+ }
}
const descriptor = t.getDescriptorWithStates(
@@ -320,5 +344,5 @@ g.test('max_components_count,input')
t.getFragmentStateWithInputs(inputs, true)
);
- t.doCreateRenderPipelineTest(isAsync, _success, descriptor);
+ t.doCreateRenderPipelineTest(isAsync, success, descriptor);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/render_pipeline/misc.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/render_pipeline/misc.spec.ts
index 1e3ccf5637..adb091a236 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/render_pipeline/misc.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/render_pipeline/misc.spec.ts
@@ -96,3 +96,37 @@ g.test('pipeline_layout,device_mismatch')
t.doCreateRenderPipelineTest(isAsync, !mismatched, descriptor);
});
+
+g.test('external_texture')
+ .desc('Tests createRenderPipeline() with an external_texture')
+ .fn(t => {
+ const shader = t.device.createShaderModule({
+ code: `
+ @vertex
+ fn vertexMain() -> @builtin(position) vec4f {
+ return vec4f(1);
+ }
+
+ @group(0) @binding(0) var myTexture: texture_external;
+
+ @fragment
+ fn fragmentMain() -> @location(0) vec4f {
+ let result = textureLoad(myTexture, vec2u(1, 1));
+ return vec4f(1);
+ }
+ `,
+ });
+
+ const descriptor: GPURenderPipelineDescriptor = {
+ layout: 'auto',
+ vertex: {
+ module: shader,
+ },
+ fragment: {
+ module: shader,
+ targets: [{ format: 'rgba8unorm' }],
+ },
+ };
+
+ t.doCreateRenderPipelineTest(false, true, descriptor);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/render_pipeline/resource_compatibility.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/render_pipeline/resource_compatibility.spec.ts
new file mode 100644
index 0000000000..5d6bc8d125
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/render_pipeline/resource_compatibility.spec.ts
@@ -0,0 +1,95 @@
+export const description = `
+Tests for resource compatibilty between pipeline layout and shader modules
+ `;
+
+import { makeTestGroup } from '../../../../common/framework/test_group.js';
+import { keysOf } from '../../../../common/util/data_tables.js';
+import {
+ kAPIResources,
+ getWGSLShaderForResource,
+ getAPIBindGroupLayoutForResource,
+ doResourcesMatch,
+} from '../utils.js';
+
+import { CreateRenderPipelineValidationTest } from './common.js';
+
+export const g = makeTestGroup(CreateRenderPipelineValidationTest);
+
+g.test('resource_compatibility')
+ .desc(
+ 'Tests validation of resource (bind group) compatibility between pipeline layout and WGSL shader'
+ )
+ .params(u =>
+ u //
+ .combine('stage', ['vertex', 'fragment'] as const)
+ .combine('apiResource', keysOf(kAPIResources))
+ .filter(t => {
+ const res = kAPIResources[t.apiResource];
+ if (t.stage === 'vertex') {
+ if (res.buffer && res.buffer.type === 'storage') {
+ return false;
+ }
+ if (res.storageTexture && res.storageTexture.access !== 'read-only') {
+ return false;
+ }
+ }
+ return true;
+ })
+ .beginSubcases()
+ .combine('isAsync', [true, false] as const)
+ .combine('wgslResource', keysOf(kAPIResources))
+ )
+ .fn(t => {
+ const apiResource = kAPIResources[t.params.apiResource];
+ const wgslResource = kAPIResources[t.params.wgslResource];
+ t.skipIf(
+ wgslResource.storageTexture !== undefined &&
+ wgslResource.storageTexture.access !== 'write-only' &&
+ !t.hasLanguageFeature('readonly_and_readwrite_storage_textures'),
+ 'Storage textures require language feature'
+ );
+ const emptyVS = `
+@vertex
+fn main() -> @builtin(position) vec4f {
+ return vec4f();
+}
+`;
+ const emptyFS = `
+@fragment
+fn main() -> @location(0) vec4f {
+ return vec4f();
+}
+`;
+
+ const code = getWGSLShaderForResource(t.params.stage, wgslResource);
+ const vsCode = t.params.stage === 'vertex' ? code : emptyVS;
+ const fsCode = t.params.stage === 'fragment' ? code : emptyFS;
+ const gpuStage: GPUShaderStageFlags =
+ t.params.stage === 'vertex' ? GPUShaderStage.VERTEX : GPUShaderStage.FRAGMENT;
+ const layout = t.device.createPipelineLayout({
+ bindGroupLayouts: [getAPIBindGroupLayoutForResource(t.device, gpuStage, apiResource)],
+ });
+
+ const descriptor = {
+ layout,
+ vertex: {
+ module: t.device.createShaderModule({
+ code: vsCode,
+ }),
+ entryPoint: 'main',
+ },
+ fragment: {
+ module: t.device.createShaderModule({
+ code: fsCode,
+ }),
+ entryPoint: 'main',
+ targets: [{ format: 'rgba8unorm' }] as const,
+ },
+ };
+
+ t.doCreateRenderPipelineTest(
+ t.params.isAsync,
+ doResourcesMatch(apiResource, wgslResource),
+ descriptor
+ );
+ });