blob: a7e05993b5e3227d8a52ecb77b5dae176487a8f1 [file] [log] [blame]
export const description = `
Increment and decrement statement tests.
`;
import { makeTestGroup } from '../../../../common/framework/test_group.js';
import { TypedArrayBufferView } from '../../../../common/util/util.js';
import { AllFeaturesMaxLimitsGPUTest, GPUTest } from '../../../gpu_test.js';
import { kValue } from '../../../util/constants.js';
export const g = makeTestGroup(AllFeaturesMaxLimitsGPUTest);
/**
* Builds, runs then checks the output of a statement shader test.
*
* @param t The test object
* @param builder The shader builder function that takes a
* StatementTestBuilder as the single argument, and returns either a WGSL
* string which is embedded into the WGSL entrypoint function, or a structure
* with entrypoint-scoped WGSL code and extra module-scope WGSL code.
*/
export function runStatementTest(
t: GPUTest,
fmt: string,
values: TypedArrayBufferView,
wgsl_main: string,
extras: { global_decl?: string } = {}
) {
const wgsl = `
struct Outputs {
data : array<${fmt}>,
};
var<private> count: u32 = 0;
@group(0) @binding(1) var<storage, read_write> outputs : Outputs;
fn push_output(value : ${fmt}) {
outputs.data[count] = value;
count += 1;
}
${extras.global_decl ?? ''}
@compute @workgroup_size(1)
fn main() {
_ = &outputs;
${wgsl_main}
}
`;
const pipeline = t.device.createComputePipeline({
layout: 'auto',
compute: {
module: t.device.createShaderModule({ code: wgsl }),
entryPoint: 'main',
},
});
const maxOutputValues = 1000;
const outputBuffer = t.createBufferTracked({
size: 4 * (1 + maxOutputValues),
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
});
const bindGroup = t.device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [{ binding: 1, resource: { buffer: outputBuffer } }],
});
// Run the shader.
const encoder = t.device.createCommandEncoder();
const pass = encoder.beginComputePass();
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup);
pass.dispatchWorkgroups(1);
pass.end();
t.queue.submit([encoder.finish()]);
t.expectGPUBufferValuesEqual(outputBuffer, values);
}
g.test('scalar_i32_increment')
.desc('Tests increment of scalar i32 values')
.fn(t => {
runStatementTest(
t,
'i32',
new Int32Array([-9, 11, kValue.i32.negative.min + 1, kValue.i32.positive.max, 1]),
`
var a: i32 = -10;
var b: i32 = 10;
var c: i32 = ${kValue.i32.negative.min};
var d: i32 = ${kValue.i32.positive.max - 1};
var e: i32 = 0;
a++;
b++;
c++;
d++;
e++;
push_output(a);
push_output(b);
push_output(c);
push_output(d);
push_output(e);
`
);
});
g.test('scalar_i32_increment_overflow')
.desc('Tests increment of scalar i32 values which overflows')
.fn(t => {
runStatementTest(
t,
'i32',
new Int32Array([kValue.i32.negative.min]),
`
var a: i32 = ${kValue.i32.positive.max};
a++;
push_output(a);
`
);
});
g.test('scalar_u32_increment')
.desc('Tests increment of scalar u32 values')
.fn(t => {
runStatementTest(
t,
'u32',
new Uint32Array([1, 11, kValue.u32.max]),
`
var a: u32 = 0;
var b: u32 = 10;
var c: u32 = ${kValue.u32.max - 1};
a++;
b++;
c++;
push_output(a);
push_output(b);
push_output(c);
`
);
});
g.test('scalar_u32_increment_overflow')
.desc('Tests increment of scalar u32 values which overflows')
.fn(t => {
runStatementTest(
t,
'u32',
new Uint32Array([0]),
`
var a: u32 = ${kValue.u32.max};
a++;
push_output(a);
`
);
});
g.test('scalar_i32_decrement')
.desc('Tests decrement of scalar i32 values')
.fn(t => {
runStatementTest(
t,
'i32',
new Int32Array([-11, 9, kValue.i32.negative.min, kValue.i32.positive.max - 1, -1]),
`
var a: i32 = -10;
var b: i32 = 10;
var c: i32 = ${kValue.i32.negative.min + 1};
var d: i32 = ${kValue.i32.positive.max};
var e: i32 = 0;
a--;
b--;
c--;
d--;
e--;
push_output(a);
push_output(b);
push_output(c);
push_output(d);
push_output(e);
`
);
});
g.test('scalar_i32_decrement_underflow')
.desc('Tests decrement of scalar i32 values which underflow')
.fn(t => {
runStatementTest(
t,
'i32',
new Int32Array([kValue.i32.positive.max]),
`
var a: i32 = ${kValue.i32.negative.min};
a--;
push_output(a);
`
);
});
g.test('scalar_u32_decrement')
.desc('Tests decrement of scalar u32 values')
.fn(t => {
runStatementTest(
t,
'u32',
new Uint32Array([0, 9, kValue.u32.max - 1]),
`
var a: u32 = 1;
var b: u32 = 10;
var c: u32 = ${kValue.u32.max};
a--;
b--;
c--;
push_output(a);
push_output(b);
push_output(c);
`
);
});
g.test('scalar_u32_decrement_underflow')
.desc('Tests decrement of scalar u32 values which underflow')
.fn(t => {
runStatementTest(
t,
'u32',
new Uint32Array([kValue.u32.max]),
`
var a: u32 = 0;
a--;
push_output(a);
`
);
});
g.test('vec2_element_increment')
.desc('Tests increment of ve2 values')
.fn(t => {
runStatementTest(
t,
'i32',
new Int32Array([-9, 11]),
`
var a = vec2(-10, 10);
a.x++;
a.g++;
push_output(a.x);
push_output(a.y);
`
);
});
g.test('vec3_element_increment')
.desc('Tests increment of vec3 values')
.fn(t => {
runStatementTest(
t,
'i32',
new Int32Array([-9, 11, kValue.i32.negative.min + 1]),
`
var a = vec3(-10, 10, ${kValue.i32.negative.min});
a.x++;
a.g++;
a.z++;
push_output(a.x);
push_output(a.y);
push_output(a.z);
`
);
});
g.test('vec4_element_increment')
.desc('Tests increment of vec4 values')
.fn(t => {
runStatementTest(
t,
'i32',
new Int32Array([-9, 11, kValue.i32.negative.min + 1, kValue.i32.positive.max]),
`
var a: vec4<i32> = vec4(-10, 10, ${kValue.i32.negative.min}, ${kValue.i32.positive.max - 1});
a.x++;
a.g++;
a.z++;
a.a++;
push_output(a.x);
push_output(a.y);
push_output(a.z);
push_output(a.w);
`
);
});
g.test('vec2_element_decrement')
.desc('Tests decrement of vec2 values')
.fn(t => {
runStatementTest(
t,
'i32',
new Int32Array([-11, 9]),
`
var a = vec2(-10, 10);
a.x--;
a.g--;
push_output(a.x);
push_output(a.y);
`
);
});
g.test('vec3_element_decrement')
.desc('Tests decrement of vec3 values')
.fn(t => {
runStatementTest(
t,
'i32',
new Int32Array([-11, 9, kValue.i32.negative.min]),
`
var a = vec3(-10, 10, ${kValue.i32.negative.min + 1});
a.x--;
a.g--;
a.z--;
push_output(a.x);
push_output(a.y);
push_output(a.z);
`
);
});
g.test('vec4_element_decrement')
.desc('Tests decrement of vec4 values')
.fn(t => {
runStatementTest(
t,
'i32',
new Int32Array([-11, 9, kValue.i32.negative.min, kValue.i32.positive.max - 1]),
`
var a: vec4<i32> = vec4(-10, 10, ${kValue.i32.negative.min + 1}, ${kValue.i32.positive.max});
a.x--;
a.g--;
a.z--;
a.a--;
push_output(a.x);
push_output(a.y);
push_output(a.z);
push_output(a.w);
`
);
});
g.test('frexp_exp_increment')
.desc('Tests increment can be used on a frexp field')
.fn(t => {
runStatementTest(
t,
'i32',
new Int32Array([2]),
`
var a = frexp(1.23);
a.exp++;
push_output(a.exp);
`
);
});
g.test('single_eval_increment')
.desc('Tests the left-hand-side reference of an increment is computed only once.')
.fn(t => {
runStatementTest(
t,
'i32',
new Int32Array([999, 0, 1, 2, 11, 21, 31]),
`
var a: array<i32,3> = array(10, 20, 30);
push_output(999);
a[bump()]++;
a[bump()]++;
a[bump()]++;
push_output(a[0]);
push_output(a[1]);
push_output(a[2]);
`,
{
global_decl: `
var<private> index_counter: i32 = 0;
fn bump() -> i32 {
let result = index_counter;
push_output(result);
index_counter = index_counter + 1;
return result;
}
`,
}
);
});
g.test('single_eval_decrement')
.desc('Tests the left-hand-side reference of a decrement is computed only once.')
.fn(t => {
runStatementTest(
t,
'i32',
new Int32Array([999, 0, 1, 2, 9, 19, 29]),
`
var a: array<i32,3> = array(10, 20, 30);
push_output(999);
a[bump()]--;
a[bump()]--;
a[bump()]--;
push_output(a[0]);
push_output(a[1]);
push_output(a[2]);
`,
{
global_decl: `
var<private> index_counter: i32 = 0;
fn bump() -> i32 {
let result = index_counter;
push_output(result);
index_counter = index_counter + 1;
return result;
}
`,
}
);
});