Skip to content

Commit

Permalink
Compat: Test textureBindingViewDimension validation (#3098)
Browse files Browse the repository at this point in the history
Compat: Test textureBindingViewDimension validation

Tests that if textureBindingViewDimension is incompatible with
a texture's dimension in createTexture a validation error is
generated

Tests that if a texture view's dimension does not match the
texture's textureBindingViewDimension when passed to createBindGroup
a validation error is generated.
  • Loading branch information
greggman authored Nov 7, 2023
1 parent 1c407e0 commit 149e02a
Show file tree
Hide file tree
Showing 4 changed files with 246 additions and 7 deletions.
176 changes: 176 additions & 0 deletions src/webgpu/compat/api/validation/createBindGroup.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
export const description = `
Tests that, in compat mode, the dimension of a view is compatible with a texture's textureBindingViewDimension.
`;

import { makeTestGroup } from '../../../../common/framework/test_group.js';
import { kTextureDimensions, kTextureViewDimensions } from '../../../capability_info.js';
import {
effectiveViewDimensionForTexture,
getTextureDimensionFromView,
} from '../../../util/texture/base.js';
import { CompatibilityTest } from '../../compatibility_test.js';

export const g = makeTestGroup(CompatibilityTest);

function isTextureBindingViewDimensionCompatibleWithDimension(
dimension: GPUTextureDimension = '2d',
textureBindingViewDimension: GPUTextureViewDimension = '2d'
) {
return getTextureDimensionFromView(textureBindingViewDimension) === dimension;
}

function isValidViewDimensionForDimension(
dimension: GPUTextureDimension | undefined,
depthOrArrayLayers: number,
viewDimension: GPUTextureViewDimension | undefined
) {
if (viewDimension === undefined) {
return true;
}

switch (dimension) {
case '1d':
return viewDimension === '1d';
case '2d':
case undefined:
switch (viewDimension) {
case undefined:
case '2d':
case '2d-array':
return true;
case 'cube':
return depthOrArrayLayers === 6;
case 'cube-array':
return depthOrArrayLayers % 6 === 0;
default:
return false;
}
break;
case '3d':
return viewDimension === '3d';
}
}

function isValidDimensionForDepthOrArrayLayers(
dimension: GPUTextureDimension | undefined,
depthOrArrayLayers: number
) {
switch (dimension) {
case '1d':
return depthOrArrayLayers === 1;
default:
return true;
}
}

function isValidViewDimensionForDepthOrArrayLayers(
viewDimension: GPUTextureViewDimension | undefined,
depthOrArrayLayers: number
) {
switch (viewDimension) {
case 'cube':
return depthOrArrayLayers === 6;
case 'cube-array':
return depthOrArrayLayers % 6 === 0;
default:
return true;
}
return viewDimension === 'cube';
}

function getEffectiveTextureBindingViewDimension(
dimension: GPUTextureDimension | undefined,
depthOrArrayLayers: number,
textureBindingViewDimension: GPUTextureViewDimension | undefined
) {
if (textureBindingViewDimension) {
return textureBindingViewDimension;
}

switch (dimension) {
case '1d':
return '1d';
case '2d':
case undefined:
return depthOrArrayLayers > 1 ? '2d-array' : '2d';
break;
case '3d':
return '3d';
}
}

g.test('viewDimension_matches_textureBindingViewDimension')
.desc(
`
Tests that, in compat mode, the dimension of a view is compatible with a texture's textureBindingViewDimension
when used as a TEXTURE_BINDING.
`
)
.params(u =>
u //
.combine('dimension', [...kTextureDimensions, undefined])
.combine('textureBindingViewDimension', [...kTextureViewDimensions, undefined])
.combine('viewDimension', [...kTextureViewDimensions, undefined])
.combine('depthOrArrayLayers', [1, 2, 6])
.filter(
({ dimension, textureBindingViewDimension, depthOrArrayLayers, viewDimension }) =>
textureBindingViewDimension !== 'cube-array' &&
viewDimension !== 'cube-array' &&
isTextureBindingViewDimensionCompatibleWithDimension(
dimension,
textureBindingViewDimension
) &&
isValidViewDimensionForDimension(dimension, depthOrArrayLayers, viewDimension) &&
isValidViewDimensionForDepthOrArrayLayers(
textureBindingViewDimension,
depthOrArrayLayers
) &&
isValidDimensionForDepthOrArrayLayers(dimension, depthOrArrayLayers)
)
)
.fn(t => {
const { dimension, textureBindingViewDimension, viewDimension, depthOrArrayLayers } = t.params;

const texture = t.device.createTexture({
size: [1, 1, depthOrArrayLayers],
format: 'rgba8unorm',
usage: GPUTextureUsage.TEXTURE_BINDING,
...(dimension && { dimension }),
...(textureBindingViewDimension && { textureBindingViewDimension }),
} as GPUTextureDescriptor); // MAINTENANCE_TODO: remove cast once textureBindingViewDimension is added to IDL
t.trackForCleanup(texture);

const effectiveTextureBindingViewDimension = getEffectiveTextureBindingViewDimension(
dimension,
texture.depthOrArrayLayers,
textureBindingViewDimension
);

const effectiveViewDimension = getEffectiveTextureBindingViewDimension(
dimension,
texture.depthOrArrayLayers,
viewDimension
);

const layout = t.device.createBindGroupLayout({
entries: [
{
binding: 0,
visibility: GPUShaderStage.COMPUTE,
texture: {
viewDimension: effectiveViewDimensionForTexture(texture, viewDimension),
},
},
],
});

const resource = texture.createView({ dimension: viewDimension });
const shouldError = effectiveTextureBindingViewDimension !== effectiveViewDimension;

t.expectValidationError(() => {
t.device.createBindGroup({
layout,
entries: [{ binding: 0, resource }],
});
}, shouldError);
});
31 changes: 31 additions & 0 deletions src/webgpu/compat/api/validation/texture/createTexture.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
export const description = `
Tests that you can not use bgra8unorm-srgb in compat mode.
Tests that textureBindingViewDimension must compatible with texture dimension
`;

import { makeTestGroup } from '../../../../../common/framework/test_group.js';
import { kTextureDimensions, kTextureViewDimensions } from '../../../../capability_info.js';
import { getTextureDimensionFromView } from '../../../../util/texture/base.js';
import { CompatibilityTest } from '../../../compatibility_test.js';

export const g = makeTestGroup(CompatibilityTest);
Expand Down Expand Up @@ -39,3 +42,31 @@ g.test('unsupportedTextureViewFormats')
true
);
});

g.test('invalidTextureBindingViewDimension')
.desc(
`Tests that you can not specify a textureBindingViewDimension that is incompatible with the texture's dimension.`
)
.params(u =>
u //
.combine('dimension', kTextureDimensions)
.combine('textureBindingViewDimension', kTextureViewDimensions)
)
.fn(t => {
const { dimension, textureBindingViewDimension } = t.params;
const shouldError = getTextureDimensionFromView(textureBindingViewDimension) !== dimension;
t.expectGPUError(
'validation',
() => {
const texture = t.device.createTexture({
size: [1, 1, dimension === '1d' ? 1 : 6],
format: 'rgba8unorm',
usage: GPUTextureUsage.TEXTURE_BINDING,
dimension,
textureBindingViewDimension,
} as GPUTextureDescriptor); // MAINTENANCE_TODO: remove cast once textureBindingViewDimension is added to IDL
t.trackForCleanup(texture);
},
shouldError
);
});
2 changes: 2 additions & 0 deletions src/webgpu/listing_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -829,6 +829,7 @@
"webgpu:api,validation,texture,rg11b10ufloat_renderable:begin_render_pass_single_sampled:*": { "subcaseMS": 1.200 },
"webgpu:api,validation,texture,rg11b10ufloat_renderable:create_render_pipeline:*": { "subcaseMS": 2.400 },
"webgpu:api,validation,texture,rg11b10ufloat_renderable:create_texture:*": { "subcaseMS": 12.700 },
"webgpu:compat,api,validation,createBindGroup:viewDimension_matches_textureBindingViewDimension:*": { "subcaseMS": 6.523 },
"webgpu:compat,api,validation,encoding,cmds,copyTextureToBuffer:compressed:*": { "subcaseMS": 202.929 },
"webgpu:compat,api,validation,encoding,programmable,pipeline_bind_group_compat:twoDifferentTextureViews,compute_pass,unused:*": { "subcaseMS": 1.501 },
"webgpu:compat,api,validation,encoding,programmable,pipeline_bind_group_compat:twoDifferentTextureViews,compute_pass,used:*": { "subcaseMS": 49.405 },
Expand All @@ -837,6 +838,7 @@
"webgpu:compat,api,validation,render_pipeline,fragment_state:colorState:*": { "subcaseMS": 32.604 },
"webgpu:compat,api,validation,render_pipeline,shader_module:sample_mask:*": { "subcaseMS": 14.801 },
"webgpu:compat,api,validation,render_pipeline,vertex_state:maxVertexAttributesVertexIndexInstanceIndex:*": { "subcaseMS": 3.700 },
"webgpu:compat,api,validation,texture,createTexture:invalidTextureBindingViewDimension:*": { "subcaseMS": 6.022 },
"webgpu:compat,api,validation,texture,createTexture:unsupportedTextureFormats:*": { "subcaseMS": 0.700 },
"webgpu:compat,api,validation,texture,createTexture:unsupportedTextureViewFormats:*": { "subcaseMS": 0.601 },
"webgpu:compat,api,validation,texture,cubeArray:cube_array:*": { "subcaseMS": 13.701 },
Expand Down
44 changes: 37 additions & 7 deletions src/webgpu/util/texture/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,22 +157,52 @@ export function viewDimensionsForTextureDimension(textureDimension: GPUTextureDi
}
}

/** Returns the default view dimension for a given texture descriptor. */
export function defaultViewDimensionsForTexture(textureDescriptor: Readonly<GPUTextureDescriptor>) {
switch (textureDescriptor.dimension) {
/** Returns the effective view dimension for a given texture dimension and depthOrArrayLayers */
export function effectiveViewDimensionForDimension(
viewDimension: GPUTextureViewDimension | undefined,
dimension: GPUTextureDimension | undefined,
depthOrArrayLayers: number
) {
if (viewDimension) {
return viewDimension;
}

switch (dimension || '2d') {
case '1d':
return '1d';
case '2d': {
const sizeDict = reifyExtent3D(textureDescriptor.size);
return sizeDict.depthOrArrayLayers > 1 ? '2d-array' : '2d';
}
case '2d':
case undefined:
return depthOrArrayLayers > 1 ? '2d-array' : '2d';
break;
case '3d':
return '3d';
default:
unreachable();
}
}

/** Returns the effective view dimension for a given texture */
export function effectiveViewDimensionForTexture(
texture: GPUTexture,
viewDimension: GPUTextureViewDimension | undefined
) {
return effectiveViewDimensionForDimension(
viewDimension,
texture.dimension,
texture.depthOrArrayLayers
);
}

/** Returns the default view dimension for a given texture descriptor. */
export function defaultViewDimensionsForTexture(textureDescriptor: Readonly<GPUTextureDescriptor>) {
const sizeDict = reifyExtent3D(textureDescriptor.size);
return effectiveViewDimensionForDimension(
undefined,
textureDescriptor.dimension,
sizeDict.depthOrArrayLayers
);
}

/** Reifies the optional fields of `GPUTextureDescriptor`.
* MAINTENANCE_TODO: viewFormats should not be omitted here, but it seems likely that the
* @webgpu/types definition will have to change before we can include it again.
Expand Down

0 comments on commit 149e02a

Please sign in to comment.