blob: 905d9ff30f9c211655c8e1976aae252d477f0aa9 [file] [log] [blame]
export const description = `
Execution Tests for shadowing
`;
import { makeTestGroup } from '../../../common/framework/test_group.js';
import { iterRange } from '../../../common/util/util.js';
import { AllFeaturesMaxLimitsGPUTest, GPUTest } from '../../gpu_test.js';
export const g = makeTestGroup(AllFeaturesMaxLimitsGPUTest);
/**
* Run a shader and check that the buffer output matches expectations.
*
* @param t The test object
* @param wgsl The shader source
* @param expected The array of expected values after running the shader
*/
function runShaderTest(t: GPUTest, wgsl: string, expected: Uint32Array): void {
const pipeline = t.device.createComputePipeline({
layout: 'auto',
compute: {
module: t.device.createShaderModule({ code: wgsl }),
entryPoint: 'main',
},
});
// Allocate a buffer and fill it with 0xdeadbeef words.
const outputBuffer = t.makeBufferWithContents(
new Uint32Array([...iterRange(expected.length, _i => 0xdeadbeef)]),
GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC
);
const bindGroup = t.device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [{ binding: 0, 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()]);
// Check that only the non-padding bytes were modified.
t.expectGPUBufferValuesEqual(outputBuffer, expected);
}
g.test('declaration')
.desc(`Test that shadowing is handled correctly`)
.fn(t => {
const wgsl = `
struct S {
my_var_start: u32,
my_var_block_shadow: u32,
my_var_unshadow: u32,
my_var_param_shadow: u32,
my_var_param_reshadow: u32,
my_var_after_func: u32,
my_const_start: u32,
my_const_block_shadow: u32,
my_const_unshadow: u32,
my_const_param_shadow: u32,
my_const_param_reshadow: u32,
my_const_after_func: u32,
my_let_block_shadow: u32,
my_let_param_reshadow: u32,
my_let_after_func: u32,
my_func_param_shadow: u32,
my_func_shadow: u32,
}
@group(0) @binding(0) var<storage, read_write> buffer : S;
var<private> my_var: u32 = 1;
const my_const: u32 = 100;
@compute @workgroup_size(1)
fn main() {
let my_let = 200u;
buffer.my_var_start = my_var; // 1
buffer.my_const_start = my_const; // 100
{
var my_var: u32 = 10;
const my_const: u32 = 110;
buffer.my_var_block_shadow = my_var; // 10
buffer.my_const_block_shadow = my_const; // 110
let my_let = 210u;
buffer.my_let_block_shadow = my_let; // 210
}
buffer.my_var_unshadow = my_var; // 1
buffer.my_const_unshadow = my_const; // 100
my_func(20, 120, my_let, 300);
buffer.my_var_after_func = my_var; // 1
buffer.my_const_after_func = my_const; // 100
buffer.my_let_after_func = my_let; // 200;
};
// Note, defined after |main|
fn my_func(my_var: u32, my_const: u32, my_let: u32, my_func: u32) {
buffer.my_var_param_shadow = my_var; // 20
buffer.my_const_param_shadow = my_const; // 120
buffer.my_func_param_shadow = my_func; // 300
// Need block here because of scoping rules for parameters
{
var my_var = 30u;
const my_const = 130u;
buffer.my_var_param_reshadow = my_var; // 30
buffer.my_const_param_reshadow = my_const; // 130
let my_let = 220u;
buffer.my_let_param_reshadow = my_let; // 220
let my_func: u32 = 310;
buffer.my_func_shadow = my_func; // 310
}
}
`;
runShaderTest(
t,
wgsl,
new Uint32Array([
// my_var
1, // my_var_start
10, // my_var_block_shadow
1, // my_var_unshadow
20, // my_var_param_shadow
30, // my_var_param_reshadow
1, // my_var_after_func
// my_const
100, // my_const_start
110, // my_const_block_shadow
100, // my_const_unshadow
120, // my_const_param_shadow
130, // my_const_param_reshadow
100, // my_const_after_func
// my_let
210, // my_let_block_shadow
220, // my_let_param_reshadow
200, // my_let_after_func
// my_func
300, // my_func_param_shadow
310, // my_func_shadow
])
);
});
g.test('builtin')
.desc(`Test that shadowing a builtin name is handled correctly`)
.fn(t => {
const wgsl = `
struct S {
my_max_shadow: u32,
max_call: u32,
}
@group(0) @binding(0) var<storage, read_write> buffer : S;
@compute @workgroup_size(1)
fn main() {
let max = 400u;
buffer.my_max_shadow = max;
my_func();
};
fn my_func() {
buffer.max_call = max(310u, 410u);
}
`;
runShaderTest(
t,
wgsl,
new Uint32Array([
// my_max
400, // my_max_shadow
410, // max_call
])
);
});
g.test('for_loop')
.desc(`Test that shadowing is handled correctly with for loops`)
.fn(t => {
const wgsl = `
struct S {
my_idx_before: u32,
my_idx_loop: array<u32, 2>,
my_idx_after: u32,
}
@group(0) @binding(0) var<storage, read_write> buffer : S;
@compute @workgroup_size(1)
fn main() {
var my_idx = 500u;
buffer.my_idx_before = my_idx; // 500;
for (var my_idx = 0u; my_idx < 2u; my_idx++) {
let pos = my_idx;
var my_idx = 501u + my_idx;
buffer.my_idx_loop[pos] = my_idx; // 501, 502
}
buffer.my_idx_after = my_idx; // 500;
};
`;
runShaderTest(
t,
wgsl,
new Uint32Array([
500, // my_idx_before
501, // my_idx_loop[0]
502, // my_idx_loop[1]
500, // my_idx_after
])
);
});
g.test('while')
.desc(`Test that shadowing is handled correctly with while loops`)
.fn(t => {
const wgsl = `
struct S {
my_idx_before: u32,
my_idx_loop: array<u32, 2>,
my_idx_after: u32,
}
@group(0) @binding(0) var<storage, read_write> buffer : S;
@compute @workgroup_size(1)
fn main() {
var my_idx = 0u;
buffer.my_idx_before = my_idx; // 0;
var counter = 0u;
while (counter < 2) {
var my_idx = 500u + counter;
buffer.my_idx_loop[counter] = my_idx; // 500, 501
counter += 1;
}
buffer.my_idx_after = my_idx; // 1;
};
`;
runShaderTest(
t,
wgsl,
new Uint32Array([
0, // my_idx_before
500, // my_idx_loop[0]
501, // my_idx_loop[1]
0, // my_idx_after
])
);
});
g.test('loop')
.desc(`Test that shadowing is handled correctly with loops`)
.fn(t => {
const wgsl = `
struct S {
my_idx_before: u32,
my_idx_loop: array<u32, 2>,
my_idx_continuing: array<u32, 2>,
my_idx_after: u32,
}
@group(0) @binding(0) var<storage, read_write> buffer : S;
@compute @workgroup_size(1)
fn main() {
var my_idx = 0u;
buffer.my_idx_before = my_idx; // 0;
var counter = 0u;
loop {
var my_idx = 500u + counter;
buffer.my_idx_loop[counter] = my_idx; // 500, 501
continuing {
var my_idx = 600u + counter;
buffer.my_idx_continuing[counter] = my_idx; // 600, 601
counter += 1;
break if counter == 2;
}
}
buffer.my_idx_after = my_idx; // 1;
};
`;
runShaderTest(
t,
wgsl,
new Uint32Array([
0, // my_idx_before
500, // my_idx_loop[0]
501, // my_idx_loop[1]
600, // my_idx_continuing[0]
601, // my_idx_continuing[1]
0, // my_idx_after
])
);
});
g.test('switch')
.desc(`Test that shadowing is handled correctly with a switch`)
.fn(t => {
const wgsl = `
struct S {
my_idx_before: u32,
my_idx_case: u32,
my_idx_default: u32,
my_idx_after: u32,
}
@group(0) @binding(0) var<storage, read_write> buffer : S;
@compute @workgroup_size(1)
fn main() {
var my_idx = 0u;
buffer.my_idx_before = my_idx; // 0;
for (var i = 0; i < 2; i++) {
switch (i) {
case 0: {
var my_idx = 10u;
buffer.my_idx_case = my_idx; // 10
}
default: {
var my_idx = 20u;
buffer.my_idx_default = my_idx; // 20
}
}
}
buffer.my_idx_after = my_idx; // 1;
};
`;
runShaderTest(
t,
wgsl,
new Uint32Array([
0, // my_idx_before
10, // my_idx_case
20, // my_idx_default
0, // my_idx_after
])
);
});
g.test('if')
.desc(`Test that shadowing is handled correctly with a switch`)
.fn(t => {
const wgsl = `
struct S {
my_idx_before: u32,
my_idx_if: u32,
my_idx_elseif: u32,
my_idx_else: u32,
my_idx_after: u32,
}
@group(0) @binding(0) var<storage, read_write> buffer : S;
@compute @workgroup_size(1)
fn main() {
var my_idx = 0u;
buffer.my_idx_before = my_idx; // 0;
for (var i = 0; i < 3; i++) {
if i == 0 {
var my_idx = 10u;
buffer.my_idx_if = my_idx; // 10
} else if i == 1 {
var my_idx = 20u;
buffer.my_idx_elseif = my_idx; // 20
} else {
var my_idx = 30u;
buffer.my_idx_else = my_idx; // 30
}
}
buffer.my_idx_after = my_idx; // 1;
};
`;
runShaderTest(
t,
wgsl,
new Uint32Array([
0, // my_idx_before
10, // my_idx_if
20, // my_idx_elseif
30, // my_idx_else
0, // my_idx_after
])
);
});