Skip to content

Commit

Permalink
Test textureSample WGSL validation
Browse files Browse the repository at this point in the history
  • Loading branch information
greggman committed Mar 12, 2024
1 parent 02a12ed commit 1ff2b05
Show file tree
Hide file tree
Showing 2 changed files with 258 additions and 0 deletions.
4 changes: 4 additions & 0 deletions src/webgpu/listing_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -1928,6 +1928,10 @@
"webgpu:shader,validation,expression,call,builtin,sqrt:values:*": { "subcaseMS": 0.302 },
"webgpu:shader,validation,expression,call,builtin,tan:integer_argument:*": { "subcaseMS": 1.734 },
"webgpu:shader,validation,expression,call,builtin,tan:values:*": { "subcaseMS": 0.350 },
"webgpu:shader,validation,expression,call,builtin,textureSample:array_index_argument:*": { "subcaseMS": 1.888 },
"webgpu:shader,validation,expression,call,builtin,textureSample:coords_argument:*": { "subcaseMS": 1.342 },
"webgpu:shader,validation,expression,call,builtin,textureSample:offset_argument,non_const:*": { "subcaseMS": 1.604 },
"webgpu:shader,validation,expression,call,builtin,textureSample:offset_argument:*": { "subcaseMS": 1.401 },
"webgpu:shader,validation,expression,call,builtin,unpack4xI8:bad_args:*": { "subcaseMS": 121.263 },
"webgpu:shader,validation,expression,call,builtin,unpack4xI8:must_use:*": { "subcaseMS": 35.200 },
"webgpu:shader,validation,expression,call,builtin,unpack4xI8:supported:*": { "subcaseMS": 24.150 },
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
const builtin = 'textureSample';
export const description = `
Validation tests for the ${builtin}() builtin.
* test textureSample coords parameter must be correct type
* test textureSample array_index parameter must be correct type
* test textureSample coords parameter must be correct type
* test textureSample offset parameter must be correct type
* test textureSample offset parameter must be a const-expression
* test textureSample offset parameter must be between -8 and +7 inclusive
`;

import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js';
import {
kConcreteIntegerScalarsAndVectors,
kConvertableToFloatScalarsAndVectors,
} from '../../../../../util/conversion.js';
import { ShaderValidationTest } from '../../../shader_validation_test.js';

type TextureSampleParams = {
coords: string;
array_index?: boolean;
offset?: string;
};

const kValidTextureSampleParameterTypes: { [n: string]: TextureSampleParams } = {
'texture_1d<f32>': { coords: 'f32' },
'texture_2d<f32>': { coords: 'vec2<f32>', offset: 'vec2<i32>' },
'texture_2d_array<f32>': { coords: 'vec2<f32>', array_index: true, offset: 'vec2<i32>' },
'texture_3d<f32>': { coords: 'vec3<f32>', offset: 'vec3<i32>' },
'texture_cube<f32>': { coords: 'vec3<f32>' },
'texture_cube_array<f32>': { coords: 'vec3<f32>', array_index: true },
texture_depth_2d: { coords: 'vec2<f32>', offset: 'vec2<i32>' },
texture_depth_2d_array: { coords: 'vec2<f32>', array_index: true },
texture_depth_cube: { coords: 'vec3<f32>' },
texture_depth_cube_array: { coords: 'vec3<f32>', array_index: true },
} as const;

const kTextureTypes = keysOf(kValidTextureSampleParameterTypes);
const kValuesTypes = objectsToRecord([
...kConvertableToFloatScalarsAndVectors,
...kConcreteIntegerScalarsAndVectors,
] as const);

function innerType(type: string) {
const angleNdx = type.indexOf('<');
return type.substring(angleNdx + 1, type.length - (angleNdx >= 0 ? 1 : 0));
}

// Replace with non-hacky version
function isConvertible(src: string, dst: string) {
if (src === dst) {
return true;
}
const angleNdx = src.indexOf('<');
if (src.substring(0, angleNdx) !== dst.substring(0, angleNdx)) {
return false;
}

src = src.substring(angleNdx + 1, src.length - (angleNdx >= 0 ? 1 : 0));
dst = dst.substring(angleNdx + 1, dst.length - (angleNdx >= 0 ? 1 : 0));

switch (src) {
case 'abstract-float':
switch (dst) {
case 'abstract-float':
case 'f16':
case 'f32':
case 'f64':
return true;
default:
return false;
}
case 'abstract-int':
switch (dst) {
case 'abstract-int':
case 'abstract-float':
case 'f16':
case 'f32':
case 'f64':
case 'u16':
case 'u32':
case 'u8':
case 'i16':
case 'i32':
case 'i8':
return true;
default:
return false;
}
default:
return false;
}
}

export const g = makeTestGroup(ShaderValidationTest);

g.test('coords_argument')
.desc(
`
Validates that only incorrect coords arguments are rejected by ${builtin}
`
)
.params(u =>
u
.combine('textureType', kTextureTypes)
.combine('coordType', keysOf(kValuesTypes))
.beginSubcases()
.combine('value', [-1, 0, 1] as const)
.expand('offset', ({ textureType }) => {
const offset = kValidTextureSampleParameterTypes[textureType].offset;
return offset ? ['', offset] : [''];
})
)
.fn(t => {
const { textureType, coordType, value, offset } = t.params;
const args = [kValuesTypes[coordType].create(value)];
const { array_index, coords } = kValidTextureSampleParameterTypes[textureType];

const coordWGSL = args.map(arg => arg.wgsl()).join(', ');
const arrayWGSL = array_index ? ', 0' : '';
const offsetWGSL = offset ? `, ${offset}(0)` : '';

const code = `
@group(0) @binding(0) var s: sampler;
@group(0) @binding(1) var t: ${textureType};
@fragment fn fs() -> @location(0) vec4f {
let v = textureSample(t, s, ${coordWGSL}${arrayWGSL}${offsetWGSL});
return vec4f(0);
}
`;

const expectSuccess = isConvertible(coordType, coords);

t.expectCompileResult(expectSuccess, code);
});

g.test('array_index_argument')
.desc(
`
Validates that only incorrect array_index arguments are rejected by ${builtin}
`
)
.params(u =>
u
.combine('textureType', kTextureTypes)
// filter out types with no array_index
.filter(({ textureType }) => !!kValidTextureSampleParameterTypes[textureType].array_index)
.combine('arrayIndexType', keysOf(kValuesTypes))
.beginSubcases()
.combine('value', [-9, -8, 0, 7, 8])
)
.fn(t => {
const { textureType, arrayIndexType, value } = t.params;
const args = [kValuesTypes[arrayIndexType].create(value)];
const { coords, offset = '' } = kValidTextureSampleParameterTypes[textureType];

const coordWGSL = `${coords}(0)`;
const arrayWGSL = args.map(arg => arg.wgsl()).join(', ');
const offsetWGSL = offset ? `, ${offset}(0)` : '';

const code = `
@group(0) @binding(0) var s: sampler;
@group(0) @binding(1) var t: ${textureType};
@fragment fn fs() -> @location(0) vec4f {
let v = textureSample(t, s, ${coordWGSL}, ${arrayWGSL}${offsetWGSL});
return vec4f(0);
}
`;

const expectSuccess =
isConvertible(arrayIndexType, 'i32') || isConvertible(arrayIndexType, 'u32');
t.expectCompileResult(expectSuccess, code);
});

g.test('offset_argument')
.desc(
`
Validates that only incorrect offset arguments are rejected by ${builtin}
`
)
.params(u =>
u
.combine('textureType', kTextureTypes)
// filter out types with no offset
.filter(
({ textureType }) => kValidTextureSampleParameterTypes[textureType].offset !== undefined
)
.combine('offsetType', keysOf(kValuesTypes))
.beginSubcases()
.combine('value', [-9, -8, 0, 7, 8])
)
.fn(t => {
const { textureType, offsetType, value } = t.params;
const args = [kValuesTypes[offsetType].create(value)];
const { coords, array_index, offset = '' } = kValidTextureSampleParameterTypes[textureType];

const coordWGSL = `${coords}(0)`;
const arrayWGSL = array_index ? ', 0' : '';
const offsetWGSL = args.map(arg => arg.wgsl()).join(', ');

const code = `
@group(0) @binding(0) var s: sampler;
@group(0) @binding(1) var t: ${textureType};
@fragment fn fs() -> @location(0) vec4f {
let v = textureSample(t, s, ${coordWGSL}${arrayWGSL}, ${offsetWGSL});
return vec4f(0);
}
`;

const expectSuccess = isConvertible(offsetType, offset) && value >= -8 && value <= 7;
t.expectCompileResult(expectSuccess, code);
});

g.test('offset_argument,non_const')
.desc(
`
Validates that only non-const offset arguments are rejected by ${builtin}
`
)
.params(u =>
u
.combine('textureType', kTextureTypes)
.combine('varType', ['c', 'u', 'l'])
// filter out types with no offset
.filter(
({ textureType }) => kValidTextureSampleParameterTypes[textureType].offset !== undefined
)
)
.fn(t => {
const { textureType, varType } = t.params;
const { coords, array_index, offset = '' } = kValidTextureSampleParameterTypes[textureType];

const coordWGSL = `${coords}(0)`;
const arrayWGSL = array_index ? ', 0' : '';
const offsetWGSL = `${offset}(${varType})`;
const castWGSL = innerType(offset);

const code = `
@group(0) @binding(0) var s: sampler;
@group(0) @binding(1) var t: ${textureType};
@group(0) @binding(2) var<uniform> u: ${offset};
@fragment fn fs(@builtin(position) p: vec4f) -> @location(0) vec4f {
const c = 1;
let l = ${offset}(${castWGSL}(p.x));
let v = textureSample(t, s, ${coordWGSL}${arrayWGSL}, ${offsetWGSL});
return vec4f(0);
}
`;

const expectSuccess = varType === 'c';
t.expectCompileResult(expectSuccess, code);
});

0 comments on commit 1ff2b05

Please sign in to comment.