blob: 8c4149a79cab406b84f8f29bbfed8c623be101b9 [file] [edit]
export const description = 'Test helpers for texel data produce the expected data in the shader';
import { params, poptions } from '../../../common/framework/params_builder.js';
import { makeTestGroup } from '../../../common/framework/test_group.js';
import { unreachable, assert } from '../../../common/framework/util/util.js';
import {
kEncodableTextureFormats,
kEncodableTextureFormatInfo,
EncodableTextureFormat,
} from '../../capability_info.js';
import { GPUTest } from '../../gpu_test.js';
import { getTexelDataRepresentation } from './texelData.js';
export const g = makeTestGroup(GPUTest);
let ReadbackTypedArray: Float32ArrayConstructor | Int32ArrayConstructor | Uint32ArrayConstructor;
function doTest(
t: GPUTest & {
params: {
format: EncodableTextureFormat;
R?: number;
G?: number;
B?: number;
A?: number;
};
}
) {
const { format } = t.params;
const componentData = (() => {
const { R, G, B, A } = t.params;
return { R, G, B, A };
})();
const rep = getTexelDataRepresentation(format);
const texelData = rep.packData(componentData);
const texture = t.device.createTexture({
format,
size: [1, 1, 1],
usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.SAMPLED,
});
t.device.defaultQueue.writeTexture(
{ texture },
texelData,
{
bytesPerRow: texelData.byteLength,
},
[1]
);
let shaderType: 'i32' | 'u32' | 'f32';
switch (kEncodableTextureFormatInfo[format].componentType) {
case 'sint':
ReadbackTypedArray = Int32Array;
shaderType = 'i32';
break;
case 'uint':
ReadbackTypedArray = Uint32Array;
shaderType = 'u32';
break;
case 'float':
ReadbackTypedArray = Float32Array;
shaderType = 'f32';
break;
default:
unreachable();
}
const shader = `
[[set(0), binding(0)]] var<uniform_constant> tex : texture_2d<${shaderType}>;
[[block]] struct Output {
${rep.componentOrder
.map((C, i) => `[[offset(${i * 4})]] result${C} : ${shaderType};`)
.join('\n')}
};
[[set(0), binding(1)]] var<storage_buffer> output : Output;
[[stage(compute)]]
fn main() -> void {
var texel : vec4<${shaderType}> = textureLoad(tex, vec2<i32>(0, 0), 0);
${rep.componentOrder.map(C => `output.result${C} = texel.${C.toLowerCase()};`).join('\n')}
return;
}`;
const pipeline = t.device.createComputePipeline({
computeStage: {
module: t.device.createShaderModule({
code: shader,
}),
entryPoint: 'main',
},
});
const outputBuffer = t.device.createBuffer({
size: rep.componentOrder.length * 4,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
});
const bindGroup = t.device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [
{
binding: 0,
resource: texture.createView(),
},
{
binding: 1,
resource: {
buffer: outputBuffer,
},
},
],
});
const encoder = t.device.createCommandEncoder();
const pass = encoder.beginComputePass();
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup);
pass.dispatch(1);
pass.endPass();
t.device.defaultQueue.submit([encoder.finish()]);
t.expectContents(
outputBuffer,
new ReadbackTypedArray(
rep.componentOrder.map(c => {
const value = rep.decode(componentData)[c];
assert(value !== undefined);
return value;
})
)
);
}
// Make a test parameter by mapping a format and each component to a texel component
// data value.
function makeParam(
format: EncodableTextureFormat,
fn: (bitLength: number, index: number) => number
) {
const rep = getTexelDataRepresentation(format);
return {
R: rep.componentInfo.R ? fn(rep.componentInfo.R.bitLength, 0) : undefined,
G: rep.componentInfo.G ? fn(rep.componentInfo.G.bitLength, 1) : undefined,
B: rep.componentInfo.B ? fn(rep.componentInfo.B.bitLength, 2) : undefined,
A: rep.componentInfo.A ? fn(rep.componentInfo.A.bitLength, 3) : undefined,
};
}
g.test('unorm_texel_data_in_shader')
.params(
params()
.combine(poptions('format', kEncodableTextureFormats))
.filter(({ format }) => {
return (
kEncodableTextureFormatInfo[format].copyDst &&
kEncodableTextureFormatInfo[format].color &&
kEncodableTextureFormatInfo[format].dataType === 'unorm'
);
})
.expand(({ format }) => {
const max = (bitLength: number) => Math.pow(2, bitLength) - 1;
return [
// Test extrema
makeParam(format, () => 0),
makeParam(format, bitLength => max(bitLength)),
// Test a middle value
makeParam(format, bitLength => Math.floor(max(bitLength) / 2)),
// Test mixed values
makeParam(format, (bitLength, i) => {
const offset = [0.13, 0.63, 0.42, 0.89];
return Math.floor(offset[i] * max(bitLength));
}),
];
})
)
.fn(doTest);
g.test('snorm_texel_data_in_shader')
.params(
params()
.combine(poptions('format', kEncodableTextureFormats))
.filter(({ format }) => {
return (
kEncodableTextureFormatInfo[format].copyDst &&
kEncodableTextureFormatInfo[format].color &&
kEncodableTextureFormatInfo[format].dataType === 'snorm'
);
})
.expand(({ format }) => {
const max = (bitLength: number) => Math.pow(2, bitLength - 1) - 1;
return [
// Test extrema
makeParam(format, () => 0),
makeParam(format, bitLength => max(bitLength)),
makeParam(format, bitLength => -max(bitLength)),
makeParam(format, bitLength => -max(bitLength) - 1),
// Test a middle value
makeParam(format, bitLength => Math.floor(max(bitLength) / 2)),
// Test mixed values
makeParam(format, (bitLength, i) => {
const offset = [0.13, 0.63, 0.42, 0.89];
const range = 2 * max(bitLength);
return -max(bitLength) + Math.floor(offset[i] * range);
}),
];
})
)
.fn(doTest);
g.test('uint_texel_data_in_shader')
.params(
params()
.combine(poptions('format', kEncodableTextureFormats))
.filter(({ format }) => {
return (
kEncodableTextureFormatInfo[format].copyDst &&
kEncodableTextureFormatInfo[format].color &&
kEncodableTextureFormatInfo[format].dataType === 'uint'
);
})
.expand(({ format }) => {
const max = (bitLength: number) => Math.pow(2, bitLength) - 1;
return [
// Test extrema
makeParam(format, () => 0),
makeParam(format, bitLength => max(bitLength)),
// Test a middle value
makeParam(format, bitLength => Math.floor(max(bitLength) / 2)),
// Test mixed values
makeParam(format, (bitLength, i) => {
const offset = [0.13, 0.63, 0.42, 0.89];
return Math.floor(offset[i] * max(bitLength));
}),
];
})
)
.fn(doTest);
g.test('sint_texel_data_in_shader')
.params(
params()
.combine(poptions('format', kEncodableTextureFormats))
.filter(({ format }) => {
return (
kEncodableTextureFormatInfo[format].copyDst &&
kEncodableTextureFormatInfo[format].color &&
kEncodableTextureFormatInfo[format].dataType === 'sint'
);
})
.expand(({ format }) => {
const max = (bitLength: number) => Math.pow(2, bitLength - 1) - 1;
return [
// Test extrema
makeParam(format, () => 0),
makeParam(format, bitLength => max(bitLength)),
makeParam(format, bitLength => -max(bitLength) - 1),
// Test a middle value
makeParam(format, bitLength => Math.floor(max(bitLength) / 2)),
// Test mixed values
makeParam(format, (bitLength, i) => {
const offset = [0.13, 0.63, 0.42, 0.89];
const range = 2 * max(bitLength);
return -max(bitLength) + Math.floor(offset[i] * range);
}),
];
})
)
.fn(doTest);
g.test('float_texel_data_in_shader')
.params(
params()
.combine(poptions('format', kEncodableTextureFormats))
.filter(({ format }) => {
return (
kEncodableTextureFormatInfo[format].copyDst &&
kEncodableTextureFormatInfo[format].color &&
kEncodableTextureFormatInfo[format].dataType === 'float'
);
})
.expand(({ format }) => {
return [
// Test extrema
makeParam(format, () => 0),
// TODO: Test NaN, Infinity, -Infinity
// Test some values
makeParam(format, () => 0.1199951171875),
makeParam(format, () => 1.4072265625),
makeParam(format, () => 24928),
makeParam(format, () => -0.1319580078125),
makeParam(format, () => -323.25),
makeParam(format, () => -7440),
// Test mixed values
makeParam(format, (bitLength, i) => {
return [24896, -0.1319580078125, -323.25, -234.375][i];
}),
];
})
)
.fn(doTest);
g.test('ufloat_texel_data_in_shader')
.params(
params()
.combine(poptions('format', kEncodableTextureFormats))
.filter(({ format }) => {
return (
kEncodableTextureFormatInfo[format].copyDst &&
kEncodableTextureFormatInfo[format].color &&
kEncodableTextureFormatInfo[format].dataType === 'ufloat'
);
})
.expand(({ format }) => {
return [
// Test extrema
makeParam(format, () => 0),
// TODO: Test NaN, Infinity
// Test some values
makeParam(format, () => 0.119140625),
makeParam(format, () => 1.40625),
makeParam(format, () => 24896),
// Test scattered mixed values
makeParam(format, (bitLength, i) => {
return [24896, 1.40625, 0.119140625, 0.23095703125][i];
}),
// Test mixed values that are close in magnitude.
makeParam(format, (bitLength, i) => {
return [0.1337890625, 0.17919921875, 0.119140625, 0.125][i];
}),
];
})
)
.fn(doTest);