export const description = `Test memory layout requirements`;

import { makeTestGroup } from '../../../common/framework/test_group.js';
import { keysOf } from '../../../common/util/data_tables.js';
import { iterRange } from '../../../common/util/util.js';
import { AllFeaturesMaxLimitsGPUTest } from '../../gpu_test.js';

export const g = makeTestGroup(AllFeaturesMaxLimitsGPUTest);

interface LayoutCase {
  type: string;
  decl?: string;
  read_assign: string;
  write_assign: string;
  offset: number;
  f16?: boolean;
  f32?: boolean;
  skip_uniform?: boolean;
}

const kLayoutCases: Record<string, LayoutCase> = {
  vec2u_align8: {
    type: `S_vec2u_align`,
    decl: `struct S_vec2u_align {
      x : u32,
      y : vec2u,
    }`,
    read_assign: `out = in.y[1]`,
    write_assign: `out.y[1] = in`,
    offset: 12,
  },
  vec3u_align16: {
    type: `S_vec3u_align`,
    decl: `struct S_vec3u_align {
      x : u32,
      y : vec3u,
    }`,
    read_assign: `out = in.y[2]`,
    write_assign: `out.y[2] = in`,
    offset: 24,
  },
  vec4u_align16: {
    type: `S_vec4u_align`,
    decl: `struct S_vec4u_align {
      x : u32,
      y : vec4u,
    }`,
    read_assign: `out = in.y[0]`,
    write_assign: `out.y[0] = in`,
    offset: 16,
  },
  struct_align32: {
    type: `S_align32`,
    decl: `struct S_align32 {
      x : u32,
      @align(32) y : u32,
    }`,
    read_assign: `out = in.y;`,
    write_assign: `out.y = in`,
    offset: 32,
  },
  vec2h_align4: {
    type: `S_vec2h_align`,
    decl: `struct S_vec2h_align {
      x : f16,
      y : vec2h,
    }`,
    read_assign: `out = u32(in.y[0])`,
    write_assign: `out.y[0] = f16(in)`,
    offset: 4,
    f16: true,
  },
  vec3h_align8: {
    type: `S_vec3h_align`,
    decl: `struct S_vec3h_align {
      x : f16,
      y : vec3h,
    }`,
    read_assign: `out = u32(in.y[2])`,
    write_assign: `out.y[2] = f16(in)`,
    offset: 12,
    f16: true,
  },
  vec4h_align8: {
    type: `S_vec4h_align`,
    decl: `struct S_vec4h_align {
      x : f16,
      y : vec4h,
    }`,
    read_assign: `out = u32(in.y[2])`,
    write_assign: `out.y[2] = f16(in)`,
    offset: 12,
    f16: true,
  },
  vec2f_align8: {
    type: `S_vec2f_align`,
    decl: `struct S_vec2f_align {
      x : u32,
      y : vec2f,
    }`,
    read_assign: `out = u32(in.y[1])`,
    write_assign: `out.y[1] = f32(in)`,
    offset: 12,
    f32: true,
  },
  vec3f_align16: {
    type: `S_vec3f_align`,
    decl: `struct S_vec3f_align {
      x : u32,
      y : vec3f,
    }`,
    read_assign: `out = u32(in.y[2])`,
    write_assign: `out.y[2] = f32(in)`,
    offset: 24,
    f32: true,
  },
  vec4f_align16: {
    type: `S_vec4f_align`,
    decl: `struct S_vec4f_align {
      x : u32,
      y : vec4f,
    }`,
    read_assign: `out = u32(in.y[0])`,
    write_assign: `out.y[0] = f32(in)`,
    offset: 16,
    f32: true,
  },
  vec3i_size12: {
    type: `S_vec3i_size`,
    decl: `struct S_vec3i_size {
      x : vec3i,
      y : u32,
    }`,
    read_assign: `out = in.y`,
    write_assign: `out.y = in`,
    offset: 12,
  },
  vec3h_size6: {
    type: `S_vec3h_size`,
    decl: `struct S_vec3h_size {
      x : vec3h,
      y : f16,
      z : f16,
    }`,
    read_assign: `out = u32(in.z)`,
    write_assign: `out.z = f16(in)`,
    offset: 8,
    f16: true,
  },
  size80: {
    type: `S_size80`,
    decl: `struct S_size80 {
      @size(80) x : u32,
      y : u32,
    }`,
    read_assign: `out = in.y`,
    write_assign: `out.y = in`,
    offset: 80,
  },
  atomic_align4: {
    type: `S_atomic_align`,
    decl: `struct S_atomic_align {
      x : u32,
      y : atomic<u32>,
    }`,
    read_assign: `out = atomicLoad(&in.y)`,
    write_assign: `atomicStore(&out.y, in)`,
    offset: 4,
  },
  atomic_size4: {
    type: `S_atomic_size`,
    decl: `struct S_atomic_size {
      x : atomic<u32>,
      y : u32,
    }`,
    read_assign: `out = in.y`,
    write_assign: `out.y = in`,
    offset: 4,
  },
  mat2x2f_align8: {
    type: `S_mat2x2f_align`,
    decl: `struct S_mat2x2f_align {
      x : u32,
      y : mat2x2f,
    }`,
    read_assign: `out = u32(in.y[0][0])`,
    write_assign: `out.y[0][0] = f32(in)`,
    offset: 8,
    f32: true,
  },
  mat3x2f_align8: {
    type: `S_mat3x2f_align`,
    decl: `struct S_mat3x2f_align {
      x : u32,
      y : mat3x2f,
    }`,
    read_assign: `out = u32(in.y[0][0])`,
    write_assign: `out.y[0][0] = f32(in)`,
    offset: 8,
    f32: true,
  },
  mat4x2f_align8: {
    type: `S_mat4x2f_align`,
    decl: `struct S_mat4x2f_align {
      x : u32,
      y : mat4x2f,
    }`,
    read_assign: `out = u32(in.y[0][0])`,
    write_assign: `out.y[0][0] = f32(in)`,
    offset: 8,
    f32: true,
  },
  mat2x3f_align16: {
    type: `S_mat2x3f_align`,
    decl: `struct S_mat2x3f_align {
      x : u32,
      y : mat2x3f,
    }`,
    read_assign: `out = u32(in.y[0][0])`,
    write_assign: `out.y[0][0] = f32(in)`,
    offset: 16,
    f32: true,
  },
  mat3x3f_align16: {
    type: `S_mat3x3f_align`,
    decl: `struct S_mat3x3f_align {
      x : u32,
      y : mat3x3f,
    }`,
    read_assign: `out = u32(in.y[0][0])`,
    write_assign: `out.y[0][0] = f32(in)`,
    offset: 16,
    f32: true,
  },
  mat4x3f_align16: {
    type: `S_mat4x3f_align`,
    decl: `struct S_mat4x3f_align {
      x : u32,
      y : mat4x3f,
    }`,
    read_assign: `out = u32(in.y[0][0])`,
    write_assign: `out.y[0][0] = f32(in)`,
    offset: 16,
    f32: true,
  },
  mat2x4f_align16: {
    type: `S_mat2x4f_align`,
    decl: `struct S_mat2x4f_align {
      x : u32,
      y : mat2x4f,
    }`,
    read_assign: `out = u32(in.y[0][0])`,
    write_assign: `out.y[0][0] = f32(in)`,
    offset: 16,
    f32: true,
  },
  mat3x4f_align16: {
    type: `S_mat3x4f_align`,
    decl: `struct S_mat3x4f_align {
      x : u32,
      y : mat3x4f,
    }`,
    read_assign: `out = u32(in.y[0][0])`,
    write_assign: `out.y[0][0] = f32(in)`,
    offset: 16,
    f32: true,
  },
  mat4x4f_align16: {
    type: `S_mat4x4f_align`,
    decl: `struct S_mat4x4f_align {
      x : u32,
      y : mat4x4f,
    }`,
    read_assign: `out = u32(in.y[0][0])`,
    write_assign: `out.y[0][0] = f32(in)`,
    offset: 16,
    f32: true,
  },
  mat2x2h_align4: {
    type: `S_mat2x2h_align`,
    decl: `struct S_mat2x2h_align {
      x : u32,
      y : mat2x2h,
    }`,
    read_assign: `out = u32(in.y[0][0])`,
    write_assign: `out.y[0][0] = f16(in)`,
    offset: 4,
    f16: true,
  },
  mat3x2h_align4: {
    type: `S_mat3x2h_align`,
    decl: `struct S_mat3x2h_align {
      x : u32,
      y : mat3x2h,
    }`,
    read_assign: `out = u32(in.y[0][0])`,
    write_assign: `out.y[0][0] = f16(in)`,
    offset: 4,
    f16: true,
  },
  mat4x2h_align4: {
    type: `S_mat4x2h_align`,
    decl: `struct S_mat4x2h_align {
      x : u32,
      y : mat4x2h,
    }`,
    read_assign: `out = u32(in.y[0][0])`,
    write_assign: `out.y[0][0] = f16(in)`,
    offset: 4,
    f16: true,
  },
  mat2x3h_align8: {
    type: `S_mat2x3h_align`,
    decl: `struct S_mat2x3h_align {
      x : u32,
      y : mat2x3h,
    }`,
    read_assign: `out = u32(in.y[0][0])`,
    write_assign: `out.y[0][0] = f16(in)`,
    offset: 8,
    f16: true,
  },
  mat3x3h_align8: {
    type: `S_mat3x3h_align`,
    decl: `struct S_mat3x3h_align {
      x : u32,
      y : mat2x3h,
    }`,
    read_assign: `out = u32(in.y[0][0])`,
    write_assign: `out.y[0][0] = f16(in)`,
    offset: 8,
    f16: true,
  },
  mat4x3h_align8: {
    type: `S_mat4x3h_align`,
    decl: `struct S_mat4x3h_align {
      x : u32,
      y : mat4x3h,
    }`,
    read_assign: `out = u32(in.y[0][0])`,
    write_assign: `out.y[0][0] = f16(in)`,
    offset: 8,
    f16: true,
  },
  mat2x4h_align8: {
    type: `S_mat2x4h_align`,
    decl: `struct S_mat2x4h_align {
      x : u32,
      y : mat2x4h,
    }`,
    read_assign: `out = u32(in.y[0][0])`,
    write_assign: `out.y[0][0] = f16(in)`,
    offset: 8,
    f16: true,
  },
  mat3x4h_align8: {
    type: `S_mat3x4h_align`,
    decl: `struct S_mat3x4h_align {
      x : u32,
      y : mat3x4h,
    }`,
    read_assign: `out = u32(in.y[0][0])`,
    write_assign: `out.y[0][0] = f16(in)`,
    offset: 8,
    f16: true,
  },
  mat4x4h_align8: {
    type: `S_mat4x4h_align`,
    decl: `struct S_mat4x4h_align {
      x : u32,
      y : mat4x4h,
    }`,
    read_assign: `out = u32(in.y[0][0])`,
    write_assign: `out.y[0][0] = f16(in)`,
    offset: 8,
    f16: true,
  },
  mat2x2f_size: {
    type: `S_mat2x2f_size`,
    decl: `struct S_mat2x2f_size {
      x : mat2x2f,
      y : u32,
    }`,
    read_assign: `out = in.y`,
    write_assign: `out.y = in`,
    offset: 16,
  },
  mat3x2f_size: {
    type: `S_mat3x2f_size`,
    decl: `struct S_mat3x2f_size {
      x : mat3x2f,
      y : u32,
    }`,
    read_assign: `out = in.y`,
    write_assign: `out.y = in`,
    offset: 24,
  },
  mat4x2f_size: {
    type: `S_mat4x2f_size`,
    decl: `struct S_mat4x2f_size {
      x : mat4x2f,
      y : u32,
    }`,
    read_assign: `out = in.y`,
    write_assign: `out.y = in`,
    offset: 32,
  },
  mat2x3f_size: {
    type: `S_mat2x3f_size`,
    decl: `struct S_mat2x3f_size {
      x : mat2x3f,
      y : u32,
    }`,
    read_assign: `out = in.y`,
    write_assign: `out.y = in`,
    offset: 32,
  },
  mat3x3f_size: {
    type: `S_mat3x3f_size`,
    decl: `struct S_mat3x3f_size {
      x : mat3x3f,
      y : u32,
    }`,
    read_assign: `out = in.y`,
    write_assign: `out.y = in`,
    offset: 48,
  },
  mat4x3f_size: {
    type: `S_mat4x3f_size`,
    decl: `struct S_mat4x3f_size {
      x : mat4x3f,
      y : u32,
    }`,
    read_assign: `out = in.y`,
    write_assign: `out.y = in`,
    offset: 64,
  },
  mat2x4f_size: {
    type: `S_mat2x4f_size`,
    decl: `struct S_mat2x4f_size {
      x : mat2x4f,
      y : u32,
    }`,
    read_assign: `out = in.y`,
    write_assign: `out.y = in`,
    offset: 32,
  },
  mat3x4f_size: {
    type: `S_mat3x4f_size`,
    decl: `struct S_mat3x4f_size {
      x : mat3x4f,
      y : u32,
    }`,
    read_assign: `out = in.y`,
    write_assign: `out.y = in`,
    offset: 48,
  },
  mat4x4f_size: {
    type: `S_mat4x4f_size`,
    decl: `struct S_mat4x4f_size {
      x : mat4x4f,
      y : u32,
    }`,
    read_assign: `out = in.y`,
    write_assign: `out.y = in`,
    offset: 64,
  },
  mat2x2h_size: {
    type: `S_mat2x2h_size`,
    decl: `struct S_mat2x2h_size {
      x : mat2x2h,
      y : f16,
    }`,
    read_assign: `out = u32(in.y)`,
    write_assign: `out.y = f16(in)`,
    offset: 8,
    f16: true,
  },
  mat3x2h_size: {
    type: `S_mat3x2h_size`,
    decl: `struct S_mat3x2h_size {
      x : mat3x2h,
      y : f16,
    }`,
    read_assign: `out = u32(in.y)`,
    write_assign: `out.y = f16(in)`,
    offset: 12,
    f16: true,
  },
  mat4x2h_size: {
    type: `S_mat4x2h_size`,
    decl: `struct S_mat4x2h_size {
      x : mat4x2h,
      y : f16,
    }`,
    read_assign: `out = u32(in.y)`,
    write_assign: `out.y = f16(in)`,
    offset: 16,
    f16: true,
  },
  mat2x3h_size: {
    type: `S_mat2x3h_size`,
    decl: `struct S_mat2x3h_size {
      x : mat2x3h,
      y : f16,
    }`,
    read_assign: `out = u32(in.y)`,
    write_assign: `out.y = f16(in)`,
    offset: 16,
    f16: true,
  },
  mat3x3h_size: {
    type: `S_mat3x3h_size`,
    decl: `struct S_mat3x3h_size {
      x : mat3x3h,
      y : f16,
    }`,
    read_assign: `out = u32(in.y)`,
    write_assign: `out.y = f16(in)`,
    offset: 24,
    f16: true,
  },
  mat4x3h_size: {
    type: `S_mat4x3h_size`,
    decl: `struct S_mat4x3h_size {
      x : mat4x3h,
      y : f16,
    }`,
    read_assign: `out = u32(in.y)`,
    write_assign: `out.y = f16(in)`,
    offset: 32,
    f16: true,
  },
  mat2x4h_size: {
    type: `S_mat2x4h_size`,
    decl: `struct S_mat2x4h_size {
      x : mat2x4h,
      y : f16,
    }`,
    read_assign: `out = u32(in.y)`,
    write_assign: `out.y = f16(in)`,
    offset: 16,
    f16: true,
  },
  mat3x4h_size: {
    type: `S_mat3x4h_size`,
    decl: `struct S_mat3x4h_size {
      x : mat3x4h,
      y : f16,
    }`,
    read_assign: `out = u32(in.y)`,
    write_assign: `out.y = f16(in)`,
    offset: 24,
    f16: true,
  },
  mat4x4h_size: {
    type: `S_mat4x4h_size`,
    decl: `struct S_mat4x4h_size {
      x : mat4x4h,
      y : f16,
    }`,
    read_assign: `out = u32(in.y)`,
    write_assign: `out.y = f16(in)`,
    offset: 32,
    f16: true,
  },
  struct_align_vec2i: {
    type: `S_struct_align_vec2i`,
    decl: `struct Inner {
      x : u32,
      y : vec2i,
    }
    struct S_struct_align_vec2i {
      x : u32,
      y : Inner,
    }`,
    read_assign: `out = in.y.x`,
    write_assign: `out.y.x = in`,
    offset: 8,
    skip_uniform: true,
  },
  struct_align_vec3i: {
    type: `S_struct_align_vec3i`,
    decl: `struct Inner {
      x : u32,
      y : vec3i,
    }
    struct S_struct_align_vec3i {
      x : u32,
      y : Inner,
    }`,
    read_assign: `out = in.y.x`,
    write_assign: `out.y.x = in`,
    offset: 16,
  },
  struct_align_vec4i: {
    type: `S_struct_align_vec4i`,
    decl: `struct Inner {
      x : u32,
      y : vec4i,
    }
    struct S_struct_align_vec4i {
      x : u32,
      y : Inner,
    }`,
    read_assign: `out = in.y.x`,
    write_assign: `out.y.x = in`,
    offset: 16,
  },
  struct_align_vec2h: {
    type: `S_struct_align_vec2h`,
    decl: `struct Inner {
      x : f16,
      y : vec2h,
    }
    struct S_struct_align_vec2h {
      x : f16,
      y : Inner,
    }`,
    read_assign: `out = u32(in.y.x)`,
    write_assign: `out.y.x = f16(in)`,
    offset: 4,
    f16: true,
    skip_uniform: true,
  },
  struct_align_vec3h: {
    type: `S_struct_align_vec3h`,
    decl: `struct Inner {
      x : f16,
      y : vec3h,
    }
    struct S_struct_align_vec3h {
      x : f16,
      y : Inner,
    }`,
    read_assign: `out = u32(in.y.x)`,
    write_assign: `out.y.x = f16(in)`,
    offset: 8,
    f16: true,
    skip_uniform: true,
  },
  struct_align_vec4h: {
    type: `S_struct_align_vec4h`,
    decl: `struct Inner {
      x : f16,
      y : vec4h,
    }
    struct S_struct_align_vec4h {
      x : f16,
      y : Inner,
    }`,
    read_assign: `out = u32(in.y.x)`,
    write_assign: `out.y.x = f16(in)`,
    offset: 8,
    f16: true,
    skip_uniform: true,
  },
  struct_size_roundup: {
    type: `S_struct_size_roundup`,
    decl: `struct Inner {
      x : vec3u,
    }
    struct S_struct_size_roundup {
      x : Inner,
      y : u32,
    }`,
    read_assign: `out = in.y`,
    write_assign: `out.y = in`,
    offset: 16,
  },
  struct_inner_size: {
    type: `S_struct_inner_size`,
    decl: `struct Inner {
      @size(112) x : u32,
    }
    struct S_struct_inner_size {
      x : Inner,
      y : u32,
    }`,
    read_assign: `out = in.y`,
    write_assign: `out.y = in`,
    offset: 112,
  },
  struct_inner_align: {
    type: `S_struct_inner_align`,
    decl: `struct Inner {
      @align(64) x : u32,
    }
    struct S_struct_inner_align {
      x : u32,
      y : Inner,
    }`,
    read_assign: `out = in.y.x`,
    write_assign: `out.y.x = in`,
    offset: 64,
  },
  struct_inner_size_and_align: {
    type: `S_struct_inner_size_and_align`,
    decl: `struct Inner {
      @align(32) @size(33) x : u32,
    }
    struct S_struct_inner_size_and_align {
      x : Inner,
      y : Inner,
    }`,
    read_assign: `out = in.y.x`,
    write_assign: `out.y.x = in`,
    offset: 64,
  },
  struct_override_size: {
    type: `S_struct_override_size`,
    decl: `struct Inner {
      @size(32) x : u32,
    }
    struct S_struct_override_size {
      @size(64) x : Inner,
      y : u32,
    }`,
    read_assign: `out = in.y`,
    write_assign: `out.y = in`,
    offset: 64,
  },
  struct_double_align: {
    type: `S_struct_double_align`,
    decl: `struct Inner {
      x : u32,
      @align(32) y : u32,
    }
    struct S_struct_double_align {
      x : u32,
      @align(64) y : Inner,
    }`,
    read_assign: `out = in.y.y`,
    write_assign: `out.y.y = in`,
    offset: 96,
  },
  array_vec3u_align: {
    type: `S_array_vec3u_align`,
    decl: `struct S_array_vec3u_align {
      x : u32,
      y : array<vec3u, 2>,
    }`,
    read_assign: `out = in.y[0][0]`,
    write_assign: `out.y[0][0] = in`,
    offset: 16,
  },
  array_vec3h_align: {
    type: `S_array_vec3h_align`,
    decl: `struct S_array_vec3h_align {
      x : f16,
      y : array<vec3h, 2>,
    }`,
    read_assign: `out = u32(in.y[0][0])`,
    write_assign: `out.y[0][0] = f16(in)`,
    offset: 8,
    f16: true,
    skip_uniform: true,
  },
  array_vec3u_stride: {
    type: `S_array_vec3u_stride`,
    decl: `struct S_array_vec3u_stride {
      x : array<vec3u, 4>,
    }`,
    read_assign: `out = in.x[1][0]`,
    write_assign: `out.x[1][0] = in`,
    offset: 16,
  },
  array_vec3h_stride: {
    type: `S_array_vec3h_stride`,
    decl: `struct S_array_vec3h_stride {
      x : array<vec3h, 4>,
    }`,
    read_assign: `out = u32(in.x[1][0])`,
    write_assign: `out.x[1][0] = f16(in)`,
    offset: 8,
    f16: true,
    skip_uniform: true,
  },
  array_stride_size: {
    type: `array<S_stride, 4>`,
    decl: `struct S_stride {
      @size(16) x : u32,
    }`,
    read_assign: `out = in[2].x`,
    write_assign: `out[2].x = in`,
    offset: 32,
  },
  array_mat2x2f_stride: {
    type: `array<mat2x2f, 4>`,
    read_assign: `out = u32(in[1][0][0])`,
    write_assign: `out[1][0][0] = f32(in)`,
    offset: 16,
    f32: true,
  },
  array_mat2x2h_stride: {
    type: `array<mat2x2h, 2>`,
    read_assign: `out = u32(in[1][0][0])`,
    write_assign: `out[1][0][0] = f16(in)`,
    offset: 8,
    f16: true,
    skip_uniform: true,
  },
  array_mat3x2f_stride: {
    type: `array<mat3x2f, 3>`,
    read_assign: `out = u32(in[2][0][0])`,
    write_assign: `out[2][0][0] = f32(in)`,
    offset: 48,
    f32: true,
    skip_uniform: true,
  },
  array_mat3x2h_stride: {
    type: `array<mat3x2h, 2>`,
    read_assign: `out = u32(in[1][0][0])`,
    write_assign: `out[1][0][0] = f16(in)`,
    offset: 12,
    f16: true,
    skip_uniform: true,
  },
  array_mat4x2f_stride: {
    type: `array<mat4x2f, 4>`,
    read_assign: `out = u32(in[1][0][0])`,
    write_assign: `out[1][0][0] = f32(in)`,
    offset: 32,
    f32: true,
  },
  array_mat4x2h_stride: {
    type: `array<mat4x2h, 2>`,
    read_assign: `out = u32(in[1][0][0])`,
    write_assign: `out[1][0][0] = f16(in)`,
    offset: 16,
    f16: true,
  },
  array_mat2x3f_stride: {
    type: `array<mat2x3f, 4>`,
    read_assign: `out = u32(in[1][0][0])`,
    write_assign: `out[1][0][0] = f32(in)`,
    offset: 32,
    f32: true,
  },
  array_mat2x3h_stride: {
    type: `array<mat2x3h, 2>`,
    read_assign: `out = u32(in[1][0][0])`,
    write_assign: `out[1][0][0] = f16(in)`,
    offset: 16,
    f16: true,
  },
  array_mat3x3f_stride: {
    type: `array<mat3x3f, 3>`,
    read_assign: `out = u32(in[2][0][0])`,
    write_assign: `out[2][0][0] = f32(in)`,
    offset: 96,
    f32: true,
  },
  array_mat3x3h_stride: {
    type: `array<mat3x3h, 2>`,
    read_assign: `out = u32(in[1][0][0])`,
    write_assign: `out[1][0][0] = f16(in)`,
    offset: 24,
    f16: true,
    skip_uniform: true,
  },
  array_mat4x3f_stride: {
    type: `array<mat4x3f, 4>`,
    read_assign: `out = u32(in[1][0][0])`,
    write_assign: `out[1][0][0] = f32(in)`,
    offset: 64,
    f32: true,
  },
  array_mat4x3h_stride: {
    type: `array<mat4x3h, 2>`,
    read_assign: `out = u32(in[1][0][0])`,
    write_assign: `out[1][0][0] = f16(in)`,
    offset: 32,
    f16: true,
  },
  array_mat2x4f_stride: {
    type: `array<mat2x4f, 4>`,
    read_assign: `out = u32(in[1][0][0])`,
    write_assign: `out[1][0][0] = f32(in)`,
    offset: 32,
    f32: true,
  },
  array_mat2x4h_stride: {
    type: `array<mat2x4h, 2>`,
    read_assign: `out = u32(in[1][0][0])`,
    write_assign: `out[1][0][0] = f16(in)`,
    offset: 16,
    f16: true,
  },
  array_mat3x4f_stride: {
    type: `array<mat3x4f, 3>`,
    read_assign: `out = u32(in[2][0][0])`,
    write_assign: `out[2][0][0] = f32(in)`,
    offset: 96,
    f32: true,
  },
  array_mat3x4h_stride: {
    type: `array<mat3x4h, 2>`,
    read_assign: `out = u32(in[1][0][0])`,
    write_assign: `out[1][0][0] = f16(in)`,
    offset: 24,
    f16: true,
    skip_uniform: true,
  },
  array_mat4x4f_stride: {
    type: `array<mat4x4f, 4>`,
    read_assign: `out = u32(in[1][0][0])`,
    write_assign: `out[1][0][0] = f32(in)`,
    offset: 64,
    f32: true,
  },
  array_mat4x4h_stride: {
    type: `array<mat4x4h, 2>`,
    read_assign: `out = u32(in[1][0][0])`,
    write_assign: `out[1][0][0] = f16(in)`,
    offset: 32,
    f16: true,
  },
};

g.test('read_layout')
  .desc('Test reading memory layouts')
  .params(u =>
    u
      .combine('case', keysOf(kLayoutCases))
      .combine('aspace', ['storage', 'uniform', 'workgroup', 'function', 'private'] as const)
      .beginSubcases()
  )
  .beforeAllSubcases(t => {
    const testcase = kLayoutCases[t.params.case];
    // Don't test atomics in workgroup due to initialization boilerplate.
    t.skipIf(
      testcase.type.includes('atomic') && t.params.aspace !== 'storage',
      `Skipping atomic test for non-storage address space`
    );

    // If the `uniform_buffer_standard_layout` feature is supported, the `uniform` address space has
    // the same layout constraints as `storage`.
    const ubo_std_layout = t.hasLanguageFeature('uniform_buffer_standard_layout');

    t.skipIf(
      !ubo_std_layout && testcase.skip_uniform === true && t.params.aspace === 'uniform',
      `Uniform requires 16 byte alignment`
    );
  })
  .fn(t => {
    const testcase = kLayoutCases[t.params.case];
    if (testcase.f16) {
      t.skipIfDeviceDoesNotHaveFeature('shader-f16');
    }

    let code = `
${testcase.f16 ? 'enable f16;' : ''}
${testcase.decl ?? ''}

@group(0) @binding(1)
var<storage, read_write> out : u32;
`;

    if (t.params.aspace === 'uniform') {
      code += `@group(0) @binding(0)
      var<${t.params.aspace}> in : ${testcase.type};`;
    } else if (t.params.aspace === 'storage') {
      // Use read_write for input data to support atomics.
      code += `@group(0) @binding(0)
      var<${t.params.aspace}, read_write> in : ${testcase.type};`;
    } else {
      code += `@group(0) @binding(0)
      var<storage> pre_in : ${testcase.type};`;
      if (t.params.aspace === 'workgroup') {
        code += `
        var<workgroup> in : ${testcase.type};`;
      } else if (t.params.aspace === 'private') {
        code += `
        var<private> in : ${testcase.type};`;
      }
    }

    code += `
@compute @workgroup_size(1,1,1)
fn main() {
`;

    if (
      t.params.aspace === 'workgroup' ||
      t.params.aspace === 'function' ||
      t.params.aspace === 'private'
    ) {
      if (t.params.aspace === 'function') {
        code += `var in : ${testcase.type};\n`;
      }
      code += `in = pre_in;`;
      if (t.params.aspace === 'workgroup') {
        code += `workgroupBarrier();\n`;
      }
    }

    code += `\n${testcase.read_assign};\n}`;

    let usage = GPUBufferUsage.COPY_SRC;
    if (t.params.aspace === 'uniform') {
      usage |= GPUBufferUsage.UNIFORM;
    } else {
      usage |= GPUBufferUsage.STORAGE;
    }

    // Magic number is 42 in various representations.
    const inMagicNumber = testcase.f16 ? 0x5140 : testcase.f32 ? 0x42280000 : 42;
    const in_buffer = t.makeBufferWithContents(
      new Uint32Array([
        ...iterRange(128, x => {
          if (x * 4 === testcase.offset) {
            return inMagicNumber;
          } else {
            return 0;
          }
        }),
      ]),
      usage
    );

    const out_buffer = t.makeBufferWithContents(
      new Uint32Array([...iterRange(1, x => 0)]),
      GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST
    );

    const pipeline = t.device.createComputePipeline({
      layout: 'auto',
      compute: {
        module: t.device.createShaderModule({
          code,
        }),
        entryPoint: 'main',
      },
    });

    const bg = t.device.createBindGroup({
      layout: pipeline.getBindGroupLayout(0),
      entries: [
        {
          binding: 0,
          resource: {
            buffer: in_buffer,
          },
        },
        {
          binding: 1,
          resource: {
            buffer: out_buffer,
          },
        },
      ],
    });

    const encoder = t.device.createCommandEncoder();
    const pass = encoder.beginComputePass();
    pass.setPipeline(pipeline);
    pass.setBindGroup(0, bg);
    pass.dispatchWorkgroups(1, 1, 1);
    pass.end();
    t.queue.submit([encoder.finish()]);

    t.expectGPUBufferValuesEqual(out_buffer, new Uint32Array([42]));
  });

g.test('write_layout')
  .desc('Test writing memory layouts')
  .params(u =>
    u
      .combine('case', keysOf(kLayoutCases))
      .combine('aspace', ['storage', 'workgroup', 'function', 'private'] as const)
      .beginSubcases()
  )
  .beforeAllSubcases(t => {
    const testcase = kLayoutCases[t.params.case];
    // Don't test atomics in workgroup due to initialization boilerplate.
    t.skipIf(
      testcase.type.includes('atomic') && t.params.aspace !== 'storage',
      `Skipping atomic test for non-storage address space`
    );
  })
  .fn(t => {
    const testcase = kLayoutCases[t.params.case];
    if (testcase.f16) {
      t.skipIfDeviceDoesNotHaveFeature('shader-f16');
    }

    let code = `
${testcase.f16 ? 'enable f16;' : ''}
${testcase.decl ?? ''}

@group(0) @binding(0)
var<storage> in : u32;
`;

    if (t.params.aspace === 'storage') {
      code += `@group(0) @binding(1)
      var<storage, read_write> out : ${testcase.type};\n`;
    } else {
      code += `@group(0) @binding(1)
      var<storage, read_write> post_out : ${testcase.type};\n`;

      if (t.params.aspace === 'workgroup') {
        code += `var<workgroup> out : ${testcase.type};\n`;
      } else if (t.params.aspace === 'private') {
        code += `var<private> out : ${testcase.type};\n`;
      }
    }

    code += `
@compute @workgroup_size(1,1,1)
fn main() {
`;

    if (t.params.aspace === 'function') {
      code += `var out : ${testcase.type};\n`;
    }

    code += `${testcase.write_assign};\n`;
    if (
      t.params.aspace === 'workgroup' ||
      t.params.aspace === 'function' ||
      t.params.aspace === 'private'
    ) {
      if (t.params.aspace === 'workgroup') {
        code += `workgroupBarrier();\n`;
      }
      code += `post_out = out;`;
    }

    code += `\n}`;

    const in_buffer = t.makeBufferWithContents(
      new Uint32Array([42]),
      GPUBufferUsage.COPY_SRC | GPUBufferUsage.STORAGE
    );

    const out_buffer = t.makeBufferWithContents(
      new Uint32Array([...iterRange(128, x => 0)]),
      GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST
    );

    const pipeline = t.device.createComputePipeline({
      layout: 'auto',
      compute: {
        module: t.device.createShaderModule({
          code,
        }),
        entryPoint: 'main',
      },
    });

    const bg = t.device.createBindGroup({
      layout: pipeline.getBindGroupLayout(0),
      entries: [
        {
          binding: 0,
          resource: {
            buffer: in_buffer,
          },
        },
        {
          binding: 1,
          resource: {
            buffer: out_buffer,
          },
        },
      ],
    });

    const encoder = t.device.createCommandEncoder();
    const pass = encoder.beginComputePass();
    pass.setPipeline(pipeline);
    pass.setBindGroup(0, bg);
    pass.dispatchWorkgroups(1, 1, 1);
    pass.end();
    t.queue.submit([encoder.finish()]);

    // Magic number is 42 in various representations.
    const outMagicNumber = testcase.f16 ? 0x5140 : testcase.f32 ? 0x42280000 : 42;
    const expect = new Uint32Array([
      ...iterRange(128, x => {
        if (x * 4 === testcase.offset) {
          return outMagicNumber;
        } else {
          return 0;
        }
      }),
    ]);

    t.expectGPUBufferValuesEqual(out_buffer, expect);
  });
