blob: 14b5541e9a8b269b5804fe1a07ebed5fd470d608 [file] [log] [blame]
export const description = `
Tests that object attributes which reflect the object's creation properties are properly set.
`;
import { makeTestGroup } from '../../../common/framework/test_group.js';
import { GPUConst } from '../../constants.js';
import { AllFeaturesMaxLimitsGPUTest, GPUTest } from '../../gpu_test.js';
import { reifyExtent3D } from '../../util/unions.js';
export const g = makeTestGroup(AllFeaturesMaxLimitsGPUTest);
function* extractValuePropertyKeys(obj: { [k: string]: unknown }) {
for (const key in obj) {
if (typeof obj[key] !== 'function') {
yield key;
}
}
}
const kBufferSubcases: readonly {
size: number;
usage: GPUBufferUsageFlags;
label?: string;
invalid?: boolean;
}[] = [
{ size: 4, usage: GPUConst.BufferUsage.VERTEX },
{
size: 16,
usage:
GPUConst.BufferUsage.STORAGE | GPUConst.BufferUsage.COPY_SRC | GPUConst.BufferUsage.UNIFORM,
},
{ size: 32, usage: GPUConst.BufferUsage.MAP_READ | GPUConst.BufferUsage.COPY_DST },
{ size: 40, usage: GPUConst.BufferUsage.INDEX, label: 'some label' },
{
size: 32,
usage: GPUConst.BufferUsage.MAP_READ | GPUConst.BufferUsage.MAP_WRITE,
invalid: true,
},
] as const;
g.test('buffer_reflection_attributes')
.desc(`For every buffer attribute, the corresponding descriptor value is carried over.`)
.paramsSubcasesOnly(u => u.combine('descriptor', kBufferSubcases))
.fn(t => {
const { descriptor } = t.params;
t.expectValidationError(() => {
const buffer = t.createBufferTracked(descriptor);
t.expect(buffer.size === descriptor.size);
t.expect(buffer.usage === descriptor.usage);
}, descriptor.invalid === true);
});
g.test('buffer_creation_from_reflection')
.desc(
`
Check that you can create a buffer from a buffer's reflection.
This check is to insure that as WebGPU develops this path doesn't
suddenly break because of new reflection.
`
)
.paramsSubcasesOnly(u =>
u.combine('descriptor', kBufferSubcases).filter(p => !p.descriptor.invalid)
)
.fn(t => {
const { descriptor } = t.params;
const buffer = t.createBufferTracked(descriptor);
const buffer2 = t.createBufferTracked(buffer);
const bufferAsObject = buffer as unknown as { [k: string]: unknown };
const buffer2AsObject = buffer2 as unknown as { [k: string]: unknown };
const keys = [...extractValuePropertyKeys(bufferAsObject)];
// Sanity check
t.expect(keys.includes('size'));
t.expect(keys.includes('usage'));
t.expect(keys.includes('label'));
for (const key of keys) {
t.expect(bufferAsObject[key] === buffer2AsObject[key], key);
}
});
const kTextureSubcases: readonly {
size: GPUExtent3D;
format: GPUTextureFormat;
usage: GPUTextureUsageFlags;
mipLevelCount?: number;
label?: string;
dimension?: GPUTextureDimension;
textureBindingViewDimension?: GPUTextureViewDimension;
sampleCount?: number;
invalid?: boolean;
}[] = [
{
size: { width: 4, height: 4 },
format: 'rgba8unorm',
usage: GPUConst.TextureUsage.TEXTURE_BINDING,
},
{
size: { width: 4, height: 4 },
format: 'rgba8unorm',
usage: GPUConst.TextureUsage.TEXTURE_BINDING,
label: 'some label',
},
{
size: { width: 8, height: 8, depthOrArrayLayers: 8 },
format: 'bgra8unorm',
usage: GPUConst.TextureUsage.RENDER_ATTACHMENT | GPUConst.TextureUsage.COPY_SRC,
},
{
size: [4, 4],
format: 'rgba8unorm',
usage: GPUConst.TextureUsage.TEXTURE_BINDING,
mipLevelCount: 2,
},
{
size: [4, 4],
format: 'rgba8unorm',
usage: GPUConst.TextureUsage.TEXTURE_BINDING,
mipLevelCount: 2,
textureBindingViewDimension: '2d',
},
{
size: [4, 4],
format: 'rgba8unorm',
usage: GPUConst.TextureUsage.TEXTURE_BINDING,
mipLevelCount: 2,
textureBindingViewDimension: '2d-array',
},
{
size: [4, 4, 4],
format: 'rgba8unorm',
usage: GPUConst.TextureUsage.TEXTURE_BINDING,
mipLevelCount: 2,
},
{
size: [16, 16, 16],
format: 'rgba8unorm',
usage: GPUConst.TextureUsage.TEXTURE_BINDING,
dimension: '3d',
},
{
size: [16, 16, 16],
format: 'rgba8unorm',
usage: GPUConst.TextureUsage.TEXTURE_BINDING,
dimension: '3d',
textureBindingViewDimension: '3d',
},
{
size: [16, 16, 6],
format: 'rgba8unorm',
usage: GPUConst.TextureUsage.TEXTURE_BINDING,
dimension: '2d',
textureBindingViewDimension: 'cube',
},
{
size: [32],
format: 'rgba8unorm',
usage: GPUConst.TextureUsage.TEXTURE_BINDING,
dimension: '1d',
},
{
size: [32],
format: 'rgba8unorm',
usage: GPUConst.TextureUsage.TEXTURE_BINDING,
dimension: '1d',
textureBindingViewDimension: '1d',
},
{
size: { width: 4, height: 4 },
format: 'rgba8unorm',
usage: GPUConst.TextureUsage.RENDER_ATTACHMENT,
sampleCount: 4,
},
{
size: { width: 4, height: 4 },
format: 'rgba8unorm',
usage: GPUConst.TextureUsage.TEXTURE_BINDING,
sampleCount: 4,
invalid: true,
},
] as const;
function getExpectedTextureBindingViewDimension(
t: GPUTest,
descriptor: GPUTextureDescriptor
): GPUTextureViewDimension | undefined {
if (t.isCompatibility) {
if (descriptor.textureBindingViewDimension) {
return descriptor.textureBindingViewDimension;
}
switch (descriptor.dimension) {
case '1d':
return '1d';
case '2d':
case undefined:
return reifyExtent3D(descriptor.size).depthOrArrayLayers > 1 ? '2d-array' : '2d';
case '3d':
return '3d';
}
} else {
return undefined;
}
}
g.test('texture_reflection_attributes')
.desc(`For every texture attribute, the corresponding descriptor value is carried over.`)
.paramsSubcasesOnly(u => u.combine('descriptor', kTextureSubcases))
.fn(t => {
const { descriptor } = t.params;
let width: number;
let height: number;
let depthOrArrayLayers: number;
if (Array.isArray(descriptor.size)) {
width = descriptor.size[0];
height = descriptor.size[1] || 1;
depthOrArrayLayers = descriptor.size[2] || 1;
} else {
width = (descriptor.size as GPUExtent3DDict).width;
height = (descriptor.size as GPUExtent3DDict).height || 1;
depthOrArrayLayers = (descriptor.size as GPUExtent3DDict).depthOrArrayLayers || 1;
}
t.expectValidationError(() => {
const texture = t.createTextureTracked(descriptor);
t.expect(texture.width === width);
t.expect(texture.height === height);
t.expect(texture.depthOrArrayLayers === depthOrArrayLayers);
t.expect(texture.format === descriptor.format);
t.expect(texture.usage === descriptor.usage);
t.expect(texture.dimension === (descriptor.dimension || '2d'));
t.expect(texture.mipLevelCount === (descriptor.mipLevelCount || 1));
t.expect(texture.sampleCount === (descriptor.sampleCount || 1));
t.expect(
texture.textureBindingViewDimension ===
getExpectedTextureBindingViewDimension(t, descriptor)
);
}, descriptor.invalid === true);
});
interface TextureWithSize extends GPUTexture {
size: GPUExtent3D;
}
g.test('texture_creation_from_reflection')
.desc(
`
Check that you can create a texture from a texture's reflection.
This check is to insure that as WebGPU develops this path doesn't
suddenly break because of new reflection.
`
)
.paramsSubcasesOnly(u =>
u.combine('descriptor', kTextureSubcases).filter(p => !p.descriptor.invalid)
)
.fn(t => {
const { descriptor } = t.params;
const texture = t.createTextureTracked(descriptor);
const textureWithSize = texture as TextureWithSize;
textureWithSize.size = [texture.width, texture.height, texture.depthOrArrayLayers];
const texture2 = t.createTextureTracked(textureWithSize);
const textureAsObject = texture as unknown as { [k: string]: unknown };
const texture2AsObject = texture2 as unknown as { [k: string]: unknown };
const keys = [...extractValuePropertyKeys(textureAsObject)].filter(k => k !== 'size');
// Sanity check
t.expect(keys.includes('format'));
t.expect(keys.includes('usage'));
t.expect(keys.includes('label'));
for (const key of keys) {
t.expect(textureAsObject[key] === texture2AsObject[key], key);
}
// MAINTENANCE_TODO: Check this if it is made possible by a spec change.
//
// texture3 = t.createTextureTracked({
// ...texture,
// size: [texture.width, texture.height, texture.depthOrArrayLayers],
// });
//
// and this
//
// texture3 = t.createTextureTracked({
// size: [texture.width, texture.height, texture.depthOrArrayLayers],
// ...texture,
// });
});
const kQuerySetSubcases: readonly {
type: GPUQueryType;
count: number;
label?: string;
invalid?: boolean;
}[] = [
{ type: 'occlusion', count: 4 },
{ type: 'occlusion', count: 16 },
{ type: 'occlusion', count: 32, label: 'some label' },
{ type: 'occlusion', count: 8193, invalid: true },
] as const;
g.test('query_set_reflection_attributes')
.desc(`For every queue attribute, the corresponding descriptor value is carried over.`)
.paramsSubcasesOnly(u => u.combine('descriptor', kQuerySetSubcases))
.fn(t => {
const { descriptor } = t.params;
t.expectValidationError(() => {
const querySet = t.createQuerySetTracked(descriptor);
t.expect(querySet.type === descriptor.type);
t.expect(querySet.count === descriptor.count);
}, descriptor.invalid === true);
});
g.test('query_set_creation_from_reflection')
.desc(
`
Check that you can create a queryset from a queryset's reflection.
This check is to insure that as WebGPU develops this path doesn't
suddenly break because of new reflection.
`
)
.paramsSubcasesOnly(u =>
u.combine('descriptor', kQuerySetSubcases).filter(p => !p.descriptor.invalid)
)
.fn(t => {
const { descriptor } = t.params;
const querySet = t.createQuerySetTracked(descriptor);
const querySet2 = t.createQuerySetTracked(querySet);
const querySetAsObject = querySet as unknown as { [k: string]: unknown };
const querySet2AsObject = querySet2 as unknown as { [k: string]: unknown };
const keys = [...extractValuePropertyKeys(querySetAsObject)];
// Sanity check
t.expect(keys.includes('type'));
t.expect(keys.includes('count'));
t.expect(keys.includes('label'));
for (const key of keys) {
t.expect(querySetAsObject[key] === querySet2AsObject[key], key);
}
});