summaryrefslogtreecommitdiffstats
path: root/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/user/ptr_params.spec.ts
diff options
context:
space:
mode:
Diffstat (limited to 'dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/user/ptr_params.spec.ts')
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/user/ptr_params.spec.ts849
1 files changed, 849 insertions, 0 deletions
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/user/ptr_params.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/user/ptr_params.spec.ts
new file mode 100644
index 0000000000..87c3b9f2e1
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/user/ptr_params.spec.ts
@@ -0,0 +1,849 @@
+export const description = `
+User function call tests for pointer parameters.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+
+export const g = makeTestGroup(GPUTest);
+
+function wgslTypeDecl(kind: 'vec4i' | 'array' | 'struct') {
+ switch (kind) {
+ case 'vec4i':
+ return `
+alias T = vec4i;
+`;
+ case 'array':
+ return `
+alias T = array<vec4f, 3>;
+`;
+ case 'struct':
+ return `
+struct S {
+a : i32,
+b : u32,
+c : i32,
+d : u32,
+}
+alias T = S;
+`;
+ }
+}
+
+function valuesForType(kind: 'vec4i' | 'array' | 'struct') {
+ switch (kind) {
+ case 'vec4i':
+ return new Uint32Array([1, 2, 3, 4]);
+ case 'array':
+ return new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
+ case 'struct':
+ return new Uint32Array([1, 2, 3, 4]);
+ }
+}
+
+function run(
+ t: GPUTest,
+ wgsl: string,
+ inputUsage: 'uniform' | 'storage',
+ input: Uint32Array | Float32Array,
+ expected: Uint32Array | Float32Array
+) {
+ const pipeline = t.device.createComputePipeline({
+ layout: 'auto',
+ compute: {
+ module: t.device.createShaderModule({ code: wgsl }),
+ entryPoint: 'main',
+ },
+ });
+
+ const inputBuffer = t.makeBufferWithContents(
+ input,
+ inputUsage === 'uniform' ? GPUBufferUsage.UNIFORM : GPUBufferUsage.STORAGE
+ );
+
+ const outputBuffer = t.device.createBuffer({
+ size: expected.buffer.byteLength,
+ usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
+ });
+
+ const bindGroup = t.device.createBindGroup({
+ layout: pipeline.getBindGroupLayout(0),
+ entries: [
+ { binding: 0, resource: { buffer: inputBuffer } },
+ { 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, expected);
+}
+
+g.test('read_full_object')
+ .desc('Test a pointer parameter can be read by a callee function')
+ .params(u =>
+ u
+ .combine('address_space', ['function', 'private', 'workgroup', 'storage', 'uniform'] as const)
+ .combine('call_indirection', [0, 1, 2] as const)
+ .combine('type', ['vec4i', 'array', 'struct'] as const)
+ )
+ .fn(t => {
+ switch (t.params.address_space) {
+ case 'workgroup':
+ case 'storage':
+ case 'uniform':
+ t.skipIfLanguageFeatureNotSupported('unrestricted_pointer_parameters');
+ }
+
+ const main: string = {
+ function: `
+@compute @workgroup_size(1)
+fn main() {
+ var F : T = input;
+ f0(&F);
+}
+`,
+ private: `
+var<private> P : T;
+@compute @workgroup_size(1)
+fn main() {
+ P = input;
+ f0(&P);
+}
+`,
+ workgroup: `
+var<workgroup> W : T;
+@compute @workgroup_size(1)
+fn main() {
+ W = input;
+ f0(&W);
+}
+`,
+ storage: `
+@compute @workgroup_size(1)
+fn main() {
+ f0(&input);
+}
+`,
+ uniform: `
+@compute @workgroup_size(1)
+fn main() {
+ f0(&input);
+}
+`,
+ }[t.params.address_space];
+
+ let call_chain = '';
+ for (let i = 0; i < t.params.call_indirection; i++) {
+ call_chain += `
+fn f${i}(p : ptr<${t.params.address_space}, T>) {
+ f${i + 1}(p);
+}
+`;
+ }
+
+ const inputVar: string =
+ t.params.address_space === 'uniform'
+ ? `@binding(0) @group(0) var<uniform> input : T;`
+ : `@binding(0) @group(0) var<storage, read> input : T;`;
+
+ const wgsl = `
+${wgslTypeDecl(t.params.type)}
+
+${inputVar}
+
+@binding(1) @group(0) var<storage, read_write> output : T;
+
+fn f${t.params.call_indirection}(p : ptr<${t.params.address_space}, T>) {
+ output = *p;
+}
+
+${call_chain}
+
+${main}
+`;
+
+ const values = valuesForType(t.params.type);
+
+ run(t, wgsl, t.params.address_space === 'uniform' ? 'uniform' : 'storage', values, values);
+ });
+
+g.test('read_ptr_to_member')
+ .desc('Test a pointer parameter to a member of a structure can be read by a callee function')
+ .params(u =>
+ u.combine('address_space', ['function', 'private', 'workgroup', 'storage', 'uniform'] as const)
+ )
+ .fn(t => {
+ t.skipIfLanguageFeatureNotSupported('unrestricted_pointer_parameters');
+
+ const main: string = {
+ function: `
+@compute @workgroup_size(1)
+fn main() {
+ var v : S = input;
+ output = f0(&v);
+}
+`,
+ private: `
+var<private> P : S;
+@compute @workgroup_size(1)
+fn main() {
+ P = input;
+ output = f0(&P);
+}
+`,
+ workgroup: `
+var<workgroup> W : S;
+@compute @workgroup_size(1)
+fn main() {
+ W = input;
+ output = f0(&W);
+}
+`,
+ storage: `
+@compute @workgroup_size(1)
+fn main() {
+ output = f0(&input);
+}
+`,
+ uniform: `
+@compute @workgroup_size(1)
+fn main() {
+ output = f0(&input);
+}
+`,
+ }[t.params.address_space];
+
+ const inputVar: string =
+ t.params.address_space === 'uniform'
+ ? `@binding(0) @group(0) var<uniform> input : S;`
+ : `@binding(0) @group(0) var<storage, read> input : S;`;
+
+ const wgsl = `
+struct S {
+ a : vec4i,
+ b : T,
+ c : vec4i,
+}
+
+struct T {
+ a : vec4i,
+ b : vec4i,
+}
+
+
+${inputVar}
+@binding(1) @group(0) var<storage, read_write> output : T;
+
+fn f2(p : ptr<${t.params.address_space}, T>) -> T {
+ return *p;
+}
+
+fn f1(p : ptr<${t.params.address_space}, S>) -> T {
+ return f2(&(*p).b);
+}
+
+fn f0(p : ptr<${t.params.address_space}, S>) -> T {
+ return f1(p);
+}
+
+${main}
+`;
+
+ // prettier-ignore
+ const input = new Uint32Array([
+ /* S.a */ 1, 2, 3, 4,
+ /* S.b.a */ 5, 6, 7, 8,
+ /* S.b.b */ 9, 10, 11, 12,
+ /* S.c */ 13, 14, 15, 16,
+ ]);
+
+ // prettier-ignore
+ const expected = new Uint32Array([
+ /* S.b.a */ 5, 6, 7, 8,
+ /* S.b.b */ 9, 10, 11, 12,
+ ]);
+
+ run(t, wgsl, t.params.address_space === 'uniform' ? 'uniform' : 'storage', input, expected);
+ });
+
+g.test('read_ptr_to_element')
+ .desc('Test a pointer parameter to an element of an array can be read by a callee function')
+ .params(u =>
+ u.combine('address_space', ['function', 'private', 'workgroup', 'storage', 'uniform'] as const)
+ )
+ .fn(t => {
+ t.skipIfLanguageFeatureNotSupported('unrestricted_pointer_parameters');
+
+ const main: string = {
+ function: `
+@compute @workgroup_size(1)
+fn main() {
+ var v : T = input;
+ output = f0(&v);
+}
+`,
+ private: `
+var<private> P : T;
+@compute @workgroup_size(1)
+fn main() {
+ P = input;
+ output = f0(&P);
+}
+`,
+ workgroup: `
+var<workgroup> W : T;
+@compute @workgroup_size(1)
+fn main() {
+ W = input;
+ output = f0(&W);
+}
+`,
+ storage: `
+@compute @workgroup_size(1)
+fn main() {
+ output = f0(&input);
+}
+`,
+ uniform: `
+@compute @workgroup_size(1)
+fn main() {
+ output = f0(&input);
+}
+`,
+ }[t.params.address_space];
+
+ const inputVar: string =
+ t.params.address_space === 'uniform'
+ ? `@binding(0) @group(0) var<uniform> input : T;`
+ : `@binding(0) @group(0) var<storage, read> input : T;`;
+
+ const wgsl = `
+alias T3 = vec4i;
+alias T2 = array<T3, 2>;
+alias T1 = array<T2, 3>;
+alias T = array<T1, 2>;
+
+${inputVar}
+@binding(1) @group(0) var<storage, read_write> output : T3;
+
+fn f2(p : ptr<${t.params.address_space}, T2>) -> T3 {
+ return (*p)[1];
+}
+
+fn f1(p : ptr<${t.params.address_space}, T1>) -> T3 {
+ return f2(&(*p)[0]) + f2(&(*p)[2]);
+}
+
+fn f0(p : ptr<${t.params.address_space}, T>) -> T3 {
+ return f1(&(*p)[0]);
+}
+
+${main}
+`;
+
+ // prettier-ignore
+ const input = new Uint32Array([
+ /* [0][0][0] */ 1, 2, 3, 4,
+ /* [0][0][1] */ 5, 6, 7, 8,
+ /* [0][1][0] */ 9, 10, 11, 12,
+ /* [0][1][1] */ 13, 14, 15, 16,
+ /* [0][2][0] */ 17, 18, 19, 20,
+ /* [0][2][1] */ 21, 22, 23, 24,
+ /* [1][0][0] */ 25, 26, 27, 28,
+ /* [1][0][1] */ 29, 30, 31, 32,
+ /* [1][1][0] */ 33, 34, 35, 36,
+ /* [1][1][1] */ 37, 38, 39, 40,
+ /* [1][2][0] */ 41, 42, 43, 44,
+ /* [1][2][1] */ 45, 46, 47, 48,
+ ]);
+ const expected = new Uint32Array([/* [0][0][1] + [0][2][1] */ 5 + 21, 6 + 22, 7 + 23, 8 + 24]);
+
+ run(t, wgsl, t.params.address_space === 'uniform' ? 'uniform' : 'storage', input, expected);
+ });
+
+g.test('write_full_object')
+ .desc('Test a pointer parameter can be written to by a callee function')
+ .params(u =>
+ u
+ .combine('address_space', ['function', 'private', 'workgroup', 'storage'] as const)
+ .combine('call_indirection', [0, 1, 2] as const)
+ .combine('type', ['vec4i', 'array', 'struct'] as const)
+ )
+ .fn(t => {
+ switch (t.params.address_space) {
+ case 'workgroup':
+ case 'storage':
+ t.skipIfLanguageFeatureNotSupported('unrestricted_pointer_parameters');
+ }
+
+ const ptr =
+ t.params.address_space === 'storage'
+ ? `ptr<storage, T, read_write>`
+ : `ptr<${t.params.address_space}, T>`;
+
+ const main: string = {
+ function: `
+@compute @workgroup_size(1)
+fn main() {
+ var F : T;
+ f0(&F);
+ output = F;
+}
+`,
+ private: `
+var<private> P : T;
+@compute @workgroup_size(1)
+fn main() {
+ f0(&P);
+ output = P;
+}
+`,
+ workgroup: `
+var<workgroup> W : T;
+@compute @workgroup_size(1)
+fn main() {
+ f0(&W);
+ output = W;
+}
+`,
+ storage: `
+@compute @workgroup_size(1)
+fn main() {
+ f0(&output);
+}
+`,
+ }[t.params.address_space];
+
+ let call_chain = '';
+ for (let i = 0; i < t.params.call_indirection; i++) {
+ call_chain += `
+fn f${i}(p : ${ptr}) {
+ f${i + 1}(p);
+}
+`;
+ }
+
+ const wgsl = `
+${wgslTypeDecl(t.params.type)}
+
+@binding(0) @group(0) var<uniform> input : T;
+@binding(1) @group(0) var<storage, read_write> output : T;
+
+fn f${t.params.call_indirection}(p : ${ptr}) {
+ *p = input;
+}
+
+${call_chain}
+
+${main}
+`;
+
+ const values = valuesForType(t.params.type);
+
+ run(t, wgsl, 'uniform', values, values);
+ });
+
+g.test('write_ptr_to_member')
+ .desc(
+ 'Test a pointer parameter to a member of a structure can be written to by a callee function'
+ )
+ .params(u => u.combine('address_space', ['function', 'private', 'workgroup', 'storage'] as const))
+ .fn(t => {
+ t.skipIfLanguageFeatureNotSupported('unrestricted_pointer_parameters');
+
+ const main: string = {
+ function: `
+@compute @workgroup_size(1)
+fn main() {
+ var v : S;
+ f0(&v);
+ output = v;
+}
+`,
+ private: `
+var<private> P : S;
+@compute @workgroup_size(1)
+fn main() {
+ f0(&P);
+ output = P;
+}
+`,
+ workgroup: `
+var<workgroup> W : S;
+@compute @workgroup_size(1)
+fn main() {
+ f0(&W);
+ output = W;
+}
+`,
+ storage: `
+@compute @workgroup_size(1)
+fn main() {
+ f1(&output);
+}
+`,
+ }[t.params.address_space];
+
+ const ptr = (ty: string) =>
+ t.params.address_space === 'storage'
+ ? `ptr<storage, ${ty}, read_write>`
+ : `ptr<${t.params.address_space}, ${ty}>`;
+
+ const wgsl = `
+struct S {
+ a : vec4i,
+ b : T,
+ c : vec4i,
+}
+
+struct T {
+ a : vec4i,
+ b : vec4i,
+}
+
+
+@binding(0) @group(0) var<storage> input : T;
+@binding(1) @group(0) var<storage, read_write> output : S;
+
+fn f2(p : ${ptr('T')}) {
+ *p = input;
+}
+
+fn f1(p : ${ptr('S')}) {
+ f2(&(*p).b);
+}
+
+fn f0(p : ${ptr('S')}) {
+ f1(p);
+}
+
+${main}
+`;
+
+ // prettier-ignore
+ const input = new Uint32Array([
+ /* S.b.a */ 5, 6, 7, 8,
+ /* S.b.b */ 9, 10, 11, 12,
+ ]);
+
+ // prettier-ignore
+ const expected = new Uint32Array([
+ /* S.a */ 0, 0, 0, 0,
+ /* S.b.a */ 5, 6, 7, 8,
+ /* S.b.b */ 9, 10, 11, 12,
+ /* S.c */ 0, 0, 0, 0,
+ ]);
+
+ run(t, wgsl, 'storage', input, expected);
+ });
+
+g.test('write_ptr_to_element')
+ .desc('Test a pointer parameter to an element of an array can be written to by a callee function')
+ .params(u => u.combine('address_space', ['function', 'private', 'workgroup', 'storage'] as const))
+ .fn(t => {
+ t.skipIfLanguageFeatureNotSupported('unrestricted_pointer_parameters');
+
+ const main: string = {
+ function: `
+@compute @workgroup_size(1)
+fn main() {
+ var v : T;
+ f0(&v);
+ output = v;
+}
+`,
+ private: `
+var<private> P : T;
+@compute @workgroup_size(1)
+fn main() {
+ f0(&P);
+ output = P;
+}
+`,
+ workgroup: `
+var<workgroup> W : T;
+@compute @workgroup_size(1)
+fn main() {
+ f0(&W);
+ output = W;
+}
+`,
+ storage: `
+@compute @workgroup_size(1)
+fn main() {
+ f0(&output);
+}
+`,
+ }[t.params.address_space];
+
+ const ptr = (ty: string) =>
+ t.params.address_space === 'storage'
+ ? `ptr<storage, ${ty}, read_write>`
+ : `ptr<${t.params.address_space}, ${ty}>`;
+
+ const wgsl = `
+alias T3 = vec4i;
+alias T2 = array<T3, 2>;
+alias T1 = array<T2, 3>;
+alias T = array<T1, 2>;
+
+@binding(0) @group(0) var<storage, read> input : T3;
+@binding(1) @group(0) var<storage, read_write> output : T;
+
+fn f2(p : ${ptr('T2')}) {
+ (*p)[1] = input;
+}
+
+fn f1(p : ${ptr('T1')}) {
+ f2(&(*p)[0]);
+ f2(&(*p)[2]);
+}
+
+fn f0(p : ${ptr('T')}) {
+ f1(&(*p)[0]);
+}
+
+${main}
+`;
+
+ const input = new Uint32Array([1, 2, 3, 4]);
+
+ // prettier-ignore
+ const expected = new Uint32Array([
+ /* [0][0][0] */ 0, 0, 0, 0,
+ /* [0][0][1] */ 1, 2, 3, 4,
+ /* [0][1][0] */ 0, 0, 0, 0,
+ /* [0][1][1] */ 0, 0, 0, 0,
+ /* [0][2][0] */ 0, 0, 0, 0,
+ /* [0][2][1] */ 1, 2, 3, 4,
+ /* [1][0][0] */ 0, 0, 0, 0,
+ /* [1][0][1] */ 0, 0, 0, 0,
+ /* [1][1][0] */ 0, 0, 0, 0,
+ /* [1][1][1] */ 0, 0, 0, 0,
+ /* [1][2][0] */ 0, 0, 0, 0,
+ /* [1][2][1] */ 0, 0, 0, 0,
+ ]);
+
+ run(t, wgsl, 'storage', input, expected);
+ });
+
+g.test('atomic_ptr_to_element')
+ .desc(
+ 'Test a pointer parameter to an atomic<i32> of an array can be read from and written to by a callee function'
+ )
+ .params(u => u.combine('address_space', ['workgroup', 'storage'] as const))
+ .fn(t => {
+ t.skipIfLanguageFeatureNotSupported('unrestricted_pointer_parameters');
+
+ const main: string = {
+ workgroup: `
+var<workgroup> W_input : T;
+var<workgroup> W_output : T;
+@compute @workgroup_size(1)
+fn main() {
+ // Copy input -> W_input
+ for (var i = 0; i < 2; i++) {
+ for (var j = 0; j < 3; j++) {
+ for (var k = 0; k < 2; k++) {
+ atomicStore(&W_input[k][j][i], atomicLoad(&input[k][j][i]));
+ }
+ }
+ }
+
+ f0(&W_input, &W_output);
+
+ // Copy W_output -> output
+ for (var i = 0; i < 2; i++) {
+ for (var j = 0; j < 3; j++) {
+ for (var k = 0; k < 2; k++) {
+ atomicStore(&output[k][j][i], atomicLoad(&W_output[k][j][i]));
+ }
+ }
+ }
+}
+`,
+ storage: `
+@compute @workgroup_size(1)
+fn main() {
+ f0(&input, &output);
+}
+`,
+ }[t.params.address_space];
+
+ const ptr = (ty: string) =>
+ t.params.address_space === 'storage'
+ ? `ptr<storage, ${ty}, read_write>`
+ : `ptr<${t.params.address_space}, ${ty}>`;
+
+ const wgsl = `
+alias T3 = atomic<i32>;
+alias T2 = array<T3, 2>;
+alias T1 = array<T2, 3>;
+alias T = array<T1, 2>;
+
+@binding(0) @group(0) var<storage, read_write> input : T;
+@binding(1) @group(0) var<storage, read_write> output : T;
+
+fn f2(in : ${ptr('T2')}, out : ${ptr('T2')}) {
+ let v = atomicLoad(&(*in)[0]);
+ atomicStore(&(*out)[1], v);
+}
+
+fn f1(in : ${ptr('T1')}, out : ${ptr('T1')}) {
+ f2(&(*in)[0], &(*out)[1]);
+ f2(&(*in)[2], &(*out)[0]);
+}
+
+fn f0(in : ${ptr('T')}, out : ${ptr('T')}) {
+ f1(&(*in)[1], &(*out)[0]);
+}
+
+${main}
+`;
+
+ // prettier-ignore
+ const input = new Uint32Array([
+ /* [0][0][0] */ 1,
+ /* [0][0][1] */ 2,
+ /* [0][1][0] */ 3,
+ /* [0][1][1] */ 4,
+ /* [0][2][0] */ 5,
+ /* [0][2][1] */ 6,
+ /* [1][0][0] */ 7, // -> [0][1][1]
+ /* [1][0][1] */ 8,
+ /* [1][1][0] */ 9,
+ /* [1][1][1] */ 10,
+ /* [1][2][0] */ 11, // -> [0][0][1]
+ /* [1][2][1] */ 12,
+ ]);
+
+ // prettier-ignore
+ const expected = new Uint32Array([
+ /* [0][0][0] */ 0,
+ /* [0][0][1] */ 11,
+ /* [0][1][0] */ 0,
+ /* [0][1][1] */ 7,
+ /* [0][2][0] */ 0,
+ /* [0][2][1] */ 0,
+ /* [1][0][0] */ 0,
+ /* [1][0][1] */ 0,
+ /* [1][1][0] */ 0,
+ /* [1][1][1] */ 0,
+ /* [1][2][0] */ 0,
+ /* [1][2][1] */ 0,
+ ]);
+
+ run(t, wgsl, 'storage', input, expected);
+ });
+
+g.test('array_length')
+ .desc(
+ 'Test a pointer parameter to a runtime sized array can be used by arrayLength() in a callee function'
+ )
+ .fn(t => {
+ t.skipIfLanguageFeatureNotSupported('unrestricted_pointer_parameters');
+
+ const wgsl = `
+@binding(0) @group(0) var<storage, read> arr : array<u32>;
+@binding(1) @group(0) var<storage, read_write> output : u32;
+
+fn f2(p : ptr<storage, array<u32>, read>) -> u32 {
+ return arrayLength(p);
+}
+
+fn f1(p : ptr<storage, array<u32>, read>) -> u32 {
+ return f2(p);
+}
+
+fn f0(p : ptr<storage, array<u32>, read>) -> u32 {
+ return f1(p);
+}
+
+@compute @workgroup_size(1)
+fn main() {
+ output = f0(&arr);
+}
+`;
+
+ const input = new Uint32Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
+ const expected = new Uint32Array([12]);
+
+ run(t, wgsl, 'storage', input, expected);
+ });
+
+g.test('mixed_ptr_parameters')
+ .desc('Test that functions can accept multiple, mixed pointer parameters')
+ .fn(t => {
+ t.skipIfLanguageFeatureNotSupported('unrestricted_pointer_parameters');
+
+ const wgsl = `
+@binding(0) @group(0) var<uniform> input : array<vec4i, 4>;
+@binding(1) @group(0) var<storage, read_write> output : array<vec4i, 4>;
+
+fn sum(f : ptr<function, i32>,
+ w : ptr<workgroup, atomic<i32>>,
+ p : ptr<private, i32>,
+ u : ptr<uniform, vec4i>) -> vec4i {
+
+ return vec4(*f + atomicLoad(w) + *p) + *u;
+}
+
+struct S {
+ i : i32,
+}
+
+var<private> P0 = S(0);
+var<private> P1 = S(10);
+var<private> P2 = 20;
+var<private> P3 = 30;
+
+struct T {
+ i : atomic<i32>,
+}
+
+var<workgroup> W0 : T;
+var<workgroup> W1 : atomic<i32>;
+var<workgroup> W2 : T;
+var<workgroup> W3 : atomic<i32>;
+
+@compute @workgroup_size(1)
+fn main() {
+ atomicStore(&W0.i, 0);
+ atomicStore(&W1, 100);
+ atomicStore(&W2.i, 200);
+ atomicStore(&W3, 300);
+
+ var F = array(0, 1000, 2000, 3000);
+
+ output[0] = sum(&F[2], &W3, &P1.i, &input[0]); // vec4(2310) + vec4(1, 2, 3, 4)
+ output[1] = sum(&F[1], &W2.i, &P0.i, &input[1]); // vec4(1200) + vec4(4, 3, 2, 1)
+ output[2] = sum(&F[3], &W0.i, &P3, &input[2]); // vec4(3030) + vec4(2, 4, 1, 3)
+ output[3] = sum(&F[2], &W1, &P2, &input[3]); // vec4(2120) + vec4(4, 1, 2, 3)
+}
+`;
+
+ // prettier-ignore
+ const input = new Uint32Array([
+ /* [0] */ 1, 2, 3, 4,
+ /* [1] */ 4, 3, 2, 1,
+ /* [2] */ 2, 4, 1, 3,
+ /* [3] */ 4, 1, 2, 3,
+ ]);
+
+ // prettier-ignore
+ const expected = new Uint32Array([
+ /* [0] */ 2311, 2312, 2313, 2314,
+ /* [1] */ 1204, 1203, 1202, 1201,
+ /* [2] */ 3032, 3034, 3031, 3033,
+ /* [3] */ 2124, 2121, 2122, 2123,
+ ]);
+
+ run(t, wgsl, 'uniform', input, expected);
+ });