export const description = `
Compound statement execution.
`;
import { makeTestGroup } from '../../../../common/framework/test_group.js';
import { keysOf } from '../../../../common/util/data_tables.js';
import { TypedArrayBufferView } from '../../../../common/util/util.js';
import { AllFeaturesMaxLimitsGPUTest, GPUTest } from '../../../gpu_test.js';
import { runFlowControlTest } from '../flow_control/harness.js';
export const g = makeTestGroup(AllFeaturesMaxLimitsGPUTest);
* Builds, runs then checks the output of a statement shader test.
*
* @param t The test object
* @param ty The WGSL scalar type to be written
* @param values The expected output values of type ty
* @param wgsl_main The body of the WGSL entry point.
*/
export function runStatementTest(
t: GPUTest,
ty: string,
values: TypedArrayBufferView,
wgsl_main: string
) {
const wgsl = `
struct Outputs {
data : array<${ty}>,
};
var<private> count: u32 = 0;
@group(0) @binding(1) var<storage, read_write> outputs : Outputs;
fn put(value : ${ty}) {
outputs.data[count] = value;
count += 1;
}
@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 } }],
});
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);
}
const kTests = {
uses: {
src: `let x = 1;
put(x);
{
put(x);
let x = x+1; // The declaration in question
put(x);
{
put(x);
}
put(x);
}
put(x);`,
values: [1, 1, 2, 2, 2, 1],
},
shadowed: {
src: `let x = 1;
put(x);
{
put(x);
let x = x+1; // The declaration in question
put(x);
{
let x = x+1; // A shadow
put(x);
}
put(x);
}
put(x);`,
values: [1, 1, 2, 3, 2, 1],
},
gone: {
src: `{
let x = 2; // The declaration in question
put(x);
}
let x = 1;
put(x);`,
values: [2, 1],
},
} as const;
g.test('decl')
.desc('Tests the value of a declared value in a compound statment.')
.params(u => u.combine('case', keysOf(kTests)))
.fn(t => {
runStatementTest(
t,
'i32',
new Int32Array(kTests[t.params.case].values),
kTests[t.params.case].src
);
});
g.test('eval_order')
.desc('Tests evaluation order of compound assignment, lhs is evaluated before rhs')
.fn(t => {
runFlowControlTest(t, f => ({
entrypoint: `
arr[0] = 41;
${f.expect_order(0)}
arr[idx()] += foo();
${f.expect_order(3)}
if (arr[0] == 42) {
${f.expect_order(4)}
} else {
${f.expect_not_reached()}
}
`,
extra: `
var<private> arr : array<u32, 1>;
fn idx() -> u32 {
${f.expect_order(1)}
return 0;
}
fn foo() -> u32 {
${f.expect_order(2)}
arr[0] = 10;
return 1;
}
`,
}));
});