summaryrefslogtreecommitdiffstats
path: root/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/padding.spec.ts
diff options
context:
space:
mode:
Diffstat (limited to 'dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/padding.spec.ts')
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/padding.spec.ts423
1 files changed, 423 insertions, 0 deletions
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/padding.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/padding.spec.ts
new file mode 100644
index 0000000000..7bc31a7712
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/padding.spec.ts
@@ -0,0 +1,423 @@
+export const description = `
+Execution Tests for preservation of padding bytes in structures and arrays.
+`;
+
+import { makeTestGroup } from '../../../common/framework/test_group.js';
+import { iterRange } from '../../../common/util/util.js';
+import { GPUTest } from '../../gpu_test.js';
+
+export const g = makeTestGroup(GPUTest);
+
+/**
+ * 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, x => 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('struct_implicit')
+ .desc(
+ `Test that padding bytes in between structure members are preserved.
+
+ This test defines a structure that has implicit padding and creates a read-write storage
+ buffer with that structure type. The shader assigns the whole variable at once, and we
+ then test that data in the padding bytes was preserved.
+ `
+ )
+ .fn(async t => {
+ const wgsl = `
+ struct S {
+ a : u32,
+ // 12 bytes of padding
+ b : vec3<u32>,
+ // 4 bytes of padding
+ c : vec2<u32>,
+ // 8 bytes of padding
+ }
+ @group(0) @binding(0) var<storage, read_write> buffer : S;
+
+ @compute @workgroup_size(1)
+ fn main() {
+ buffer = S(0x12345678, vec3(0xabcdef01), vec2(0x98765432));
+ }
+ `;
+ runShaderTest(
+ t,
+ wgsl,
+ new Uint32Array([
+ // a : u32
+ 0x12345678,
+ 0xdeadbeef,
+ 0xdeadbeef,
+ 0xdeadbeef,
+ // b : vec3<u32>
+ 0xabcdef01,
+ 0xabcdef01,
+ 0xabcdef01,
+ 0xdeadbeef,
+ // c : vec2<u32>
+ 0x98765432,
+ 0x98765432,
+ 0xdeadbeef,
+ 0xdeadbeef,
+ ])
+ );
+ });
+
+g.test('struct_explicit')
+ .desc(
+ `Test that padding bytes in between structure members are preserved.
+
+ This test defines a structure with explicit padding attributes and creates a read-write storage
+ buffer with that structure type. The shader assigns the whole variable at once, and we
+ then test that data in the padding bytes was preserved.
+ `
+ )
+ .fn(async t => {
+ const wgsl = `
+ struct S {
+ a : u32,
+ // 12 bytes of padding
+ @align(16) @size(20) b : u32,
+ // 16 bytes of padding
+ @size(12) c : u32,
+ // 8 bytes of padding
+ }
+ @group(0) @binding(0) var<storage, read_write> buffer : S;
+
+ @compute @workgroup_size(1)
+ fn main() {
+ buffer = S(0x12345678, 0xabcdef01, 0x98765432);
+ }
+ `;
+ runShaderTest(
+ t,
+ wgsl,
+ new Uint32Array([
+ // a : u32
+ 0x12345678,
+ 0xdeadbeef,
+ 0xdeadbeef,
+ 0xdeadbeef,
+ // @align(16) @size(20) b : u32
+ 0xabcdef01,
+ 0xdeadbeef,
+ 0xdeadbeef,
+ 0xdeadbeef,
+ 0xdeadbeef,
+ // @size(12) c : u32
+ 0x98765432,
+ 0xdeadbeef,
+ 0xdeadbeef,
+ ])
+ );
+ });
+
+g.test('struct_nested')
+ .desc(
+ `Test that padding bytes in nested structures are preserved.
+
+ This test defines a set of nested structures that have padding and creates a read-write storage
+ buffer with the root structure type. The shader assigns the whole variable at once, and we
+ then test that data in the padding bytes was preserved.
+ `
+ )
+ .fn(async t => {
+ const wgsl = `
+ // Size of S1 is 48 bytes.
+ // Alignment of S1 is 16 bytes.
+ struct S1 {
+ a : u32,
+ // 12 bytes of padding
+ b : vec3<u32>,
+ // 4 bytes of padding
+ c : vec2<u32>,
+ // 8 bytes of padding
+ }
+
+ // Size of S2 is 112 bytes.
+ // Alignment of S2 is 48 bytes.
+ struct S2 {
+ a2 : u32,
+ // 12 bytes of padding
+ b2 : S1,
+ c2 : S1,
+ }
+
+ // Size of S3 is 144 bytes.
+ // Alignment of S3 is 48 bytes.
+ struct S3 {
+ a3 : S1,
+ b3 : S2,
+ c3 : S2,
+ }
+
+ @group(0) @binding(0) var<storage, read_write> buffer : S3;
+
+ @compute @workgroup_size(1)
+ fn main() {
+ buffer = S3();
+ }
+ `;
+ runShaderTest(
+ t,
+ wgsl,
+ new Uint32Array([
+ // a3 : S1
+ // a3.a1 : u32
+ 0x00000000,
+ 0xdeadbeef,
+ 0xdeadbeef,
+ 0xdeadbeef,
+ // a3.b1 : vec3<u32>
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0xdeadbeef,
+ // a3.c1 : vec2<u32>
+ 0x00000000,
+ 0x00000000,
+ 0xdeadbeef,
+ 0xdeadbeef,
+
+ // b3 : S2
+ // b3.a2 : u32
+ 0x00000000,
+ 0xdeadbeef,
+ 0xdeadbeef,
+ 0xdeadbeef,
+ // b3.b2 : S1
+ // b3.b2.a1 : u32
+ 0x00000000,
+ 0xdeadbeef,
+ 0xdeadbeef,
+ 0xdeadbeef,
+ // b3.b2.b1 : vec3<u32>
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0xdeadbeef,
+ // b3.b2.c1 : vec2<u32>
+ 0x00000000,
+ 0x00000000,
+ 0xdeadbeef,
+ 0xdeadbeef,
+ // b3.c2 : S1
+ // b3.c2.a1 : u32
+ 0x00000000,
+ 0xdeadbeef,
+ 0xdeadbeef,
+ 0xdeadbeef,
+ // b3.c2.b1 : vec3<u32>
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0xdeadbeef,
+ // b3.c2.c1 : vec2<u32>
+ 0x00000000,
+ 0x00000000,
+ 0xdeadbeef,
+ 0xdeadbeef,
+
+ // c3 : S2
+ // c3.a2 : u32
+ 0x00000000,
+ 0xdeadbeef,
+ 0xdeadbeef,
+ 0xdeadbeef,
+ // c3.b2 : S1
+ // c3.b2.a1 : u32
+ 0x00000000,
+ 0xdeadbeef,
+ 0xdeadbeef,
+ 0xdeadbeef,
+ // c3.b2.b1 : vec3<u32>
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0xdeadbeef,
+ // c3.b2.c1 : vec2<u32>
+ 0x00000000,
+ 0x00000000,
+ 0xdeadbeef,
+ 0xdeadbeef,
+ // c3.c2 : S1
+ // c3.c2.a1 : u32
+ 0x00000000,
+ 0xdeadbeef,
+ 0xdeadbeef,
+ 0xdeadbeef,
+ // c3.c2.b1 : vec3<u32>
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0xdeadbeef,
+ // c3.c2.c1 : vec2<u32>
+ 0x00000000,
+ 0x00000000,
+ 0xdeadbeef,
+ 0xdeadbeef,
+ ])
+ );
+ });
+
+g.test('array_of_vec3')
+ .desc(
+ `Test that padding bytes in between array elements are preserved.
+
+ This test defines creates a read-write storage buffer with type array<vec3, 4>. The shader
+ assigns the whole variable at once, and we then test that data in the padding bytes was
+ preserved.
+ `
+ )
+ .fn(async t => {
+ const wgsl = `
+ @group(0) @binding(0) var<storage, read_write> buffer : array<vec3<u32>, 4>;
+
+ @compute @workgroup_size(1)
+ fn main() {
+ buffer = array<vec3<u32>, 4>(
+ vec3(0x12345678),
+ vec3(0xabcdef01),
+ vec3(0x98765432),
+ vec3(0x0f0f0f0f),
+ );
+ }
+ `;
+ runShaderTest(
+ t,
+ wgsl,
+ new Uint32Array([
+ // buffer[0]
+ 0x12345678,
+ 0x12345678,
+ 0x12345678,
+ 0xdeadbeef,
+ // buffer[1]
+ 0xabcdef01,
+ 0xabcdef01,
+ 0xabcdef01,
+ 0xdeadbeef,
+ // buffer[2]
+ 0x98765432,
+ 0x98765432,
+ 0x98765432,
+ 0xdeadbeef,
+ // buffer[2]
+ 0x0f0f0f0f,
+ 0x0f0f0f0f,
+ 0x0f0f0f0f,
+ 0xdeadbeef,
+ ])
+ );
+ });
+
+g.test('array_of_struct')
+ .desc(
+ `Test that padding bytes in between array elements are preserved.
+
+ This test defines creates a read-write storage buffer with type array<S, 4>, where S is a
+ structure that contains padding bytes. The shader assigns the whole variable at once, and we
+ then test that data in the padding bytes was preserved.
+ `
+ )
+ .fn(async t => {
+ const wgsl = `
+ struct S {
+ a : u32,
+ b : vec3<u32>,
+ }
+ @group(0) @binding(0) var<storage, read_write> buffer : array<S, 3>;
+
+ @compute @workgroup_size(1)
+ fn main() {
+ buffer = array<S, 3>(
+ S(0x12345678, vec3(0x0f0f0f0f)),
+ S(0xabcdef01, vec3(0x7c7c7c7c)),
+ S(0x98765432, vec3(0x18181818)),
+ );
+ }
+ `;
+ runShaderTest(
+ t,
+ wgsl,
+ new Uint32Array([
+ // buffer[0]
+ 0x12345678,
+ 0xdeadbeef,
+ 0xdeadbeef,
+ 0xdeadbeef,
+ 0x0f0f0f0f,
+ 0x0f0f0f0f,
+ 0x0f0f0f0f,
+ 0xdeadbeef,
+ // buffer[1]
+ 0xabcdef01,
+ 0xdeadbeef,
+ 0xdeadbeef,
+ 0xdeadbeef,
+ 0x7c7c7c7c,
+ 0x7c7c7c7c,
+ 0x7c7c7c7c,
+ 0xdeadbeef,
+ // buffer[2]
+ 0x98765432,
+ 0xdeadbeef,
+ 0xdeadbeef,
+ 0xdeadbeef,
+ 0x18181818,
+ 0x18181818,
+ 0x18181818,
+ 0xdeadbeef,
+ ])
+ );
+ });
+
+g.test('vec3')
+ .desc(
+ `Test padding bytes are preserved when assigning to a variable of type vec3 (without a struct).
+ `
+ )
+ .fn(async t => {
+ const wgsl = `
+ @group(0) @binding(0) var<storage, read_write> buffer : vec3<u32>;
+
+ @compute @workgroup_size(1)
+ fn main() {
+ buffer = vec3<u32>(0x12345678, 0xabcdef01, 0x98765432);
+ }
+ `;
+ runShaderTest(t, wgsl, new Uint32Array([0x12345678, 0xabcdef01, 0x98765432, 0xdeadbeef]));
+ });