Generalize WGSL polyfilling logic to include arrays.

We now support polyfilling arrays, matrices and arrays of matrices
when native WGSL alignment mismatches std140 alignment.

Change-Id: I06721bb8f9c2e89acc772dd1baaa0861be827033
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/741116
Commit-Queue: John Stiles <johnstiles@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/gn/sksl_tests.gni b/gn/sksl_tests.gni
index 622fc83..a3d08bf 100644
--- a/gn/sksl_tests.gni
+++ b/gn/sksl_tests.gni
@@ -408,6 +408,7 @@
   "wgsl/OutParams.sksl",
   "wgsl/Sample.sksl",
   "wgsl/TernaryThenShortCircuit.sksl",
+  "wgsl/UniformArrays.sksl",
   "wgsl/UniformMatrices.sksl",
   "wgsl/UserDefinedPipelineIO.sksl",
   "wgsl/VertexPositionOutputIsAlwaysDeclared.vert",
diff --git a/resources/sksl/BUILD.bazel b/resources/sksl/BUILD.bazel
index 2ed5acb..e88880d 100644
--- a/resources/sksl/BUILD.bazel
+++ b/resources/sksl/BUILD.bazel
@@ -1085,6 +1085,7 @@
         "wgsl/OutParams.sksl",
         "wgsl/Sample.sksl",
         "wgsl/TernaryThenShortCircuit.sksl",
+        "wgsl/UniformArrays.sksl",
         "wgsl/UniformMatrices.sksl",
         "wgsl/UserDefinedPipelineIO.sksl",
         "wgsl/VertexPositionOutputIsAlwaysDeclared.vert",
diff --git a/resources/sksl/shared/InterfaceBlockNamedArray.sksl b/resources/sksl/shared/InterfaceBlockNamedArray.sksl
index fc797be..69f1343 100644
--- a/resources/sksl/shared/InterfaceBlockNamedArray.sksl
+++ b/resources/sksl/shared/InterfaceBlockNamedArray.sksl
@@ -1,8 +1,10 @@
 layout(binding=123) uniform testBlock {
-    layout (offset=0)  float x;
+    layout (offset=0)  float s;
     layout (offset=16) float2x2 m;
+    layout (offset=48) float a[2];
+    layout (offset=80) float2x2 am[2];
 } test[2];
 
 void main() {
-    sk_FragColor = half4(test[0].x, test[0].m[0][1], test[1].m[1]);
+    sk_FragColor = half4(test[0].s, test[1].m[1][0], test[0].a[1], test[1].am[1][0][1]);
 }
diff --git a/resources/sksl/wgsl/UniformArrays.sksl b/resources/sksl/wgsl/UniformArrays.sksl
new file mode 100644
index 0000000..7edbb11
--- /dev/null
+++ b/resources/sksl/wgsl/UniformArrays.sksl
@@ -0,0 +1,59 @@
+// Our buffers are in std140 layout, so the generated code will need to compensate.
+
+layout(set=0, binding=1) uniform UniformBuffer {
+    float     uf[3];
+    float2    uf2[3];
+    float3    uf3[3];
+    float4    uf4[3];
+    half      uh[3];
+    half2     uh2[3];
+    half3     uh3[3];
+    half4     uh4[3];
+    int       ui[3];
+    int2      ui2[3];
+    int3      ui3[3];
+    int4      ui4[3];
+};
+
+layout(set=0, binding=2) buffer StorageBuffer {
+    float     sf[4];
+    float2    sf2[4];
+    float3    sf3[4];
+    float4    sf4[4];
+    half      sh[4];
+    half2     sh2[4];
+    half3     sh3[4];
+    half4     sh4[4];
+    int       si[4];
+    int2      si2[4];
+    int3      si3[4];
+    int4      si4[4];
+};
+
+half4 main() {
+    float value = float(uf [1]  ) +
+                  float(uf2[1].x) +
+                  float(uf3[1].x) +
+                  float(uf4[1].x) +
+                  float(uh [1]  ) +
+                  float(uh2[1].x) +
+                  float(uh3[1].x) +
+                  float(uh4[1].x) +
+                  float(ui [1]  ) +
+                  float(ui2[1].x) +
+                  float(ui3[1].x) +
+                  float(ui4[1].x) +
+                  float(sf [1]  ) +
+                  float(sf2[1].x) +
+                  float(sf3[1].x) +
+                  float(sf4[1].x) +
+                  float(sh [1]  ) +
+                  float(sh2[1].x) +
+                  float(sh3[1].x) +
+                  float(sh4[1].x) +
+                  float(si [1]  ) +
+                  float(si2[1].x) +
+                  float(si3[1].x) +
+                  float(si4[1].x);
+    return half4(value);
+}
diff --git a/resources/sksl/wgsl/UniformMatrices.sksl b/resources/sksl/wgsl/UniformMatrices.sksl
index e66f764..dde4984 100644
--- a/resources/sksl/wgsl/UniformMatrices.sksl
+++ b/resources/sksl/wgsl/UniformMatrices.sksl
@@ -10,6 +10,15 @@
     float4x2 u42;
     float4x3 u43;
     float4x4 u44;
+    float2x2 au22[3];
+    float2x3 au23[3];
+    float2x4 au24[3];
+    float3x2 au32[3];
+    float3x3 au33[3];
+    float3x4 au34[3];
+    float4x2 au42[3];
+    float4x3 au43[3];
+    float4x4 au44[3];
 };
 
 layout(set=0, binding=2) buffer StorageBuffer {
@@ -22,8 +31,53 @@
     float4x2 s42;
     float4x3 s43;
     float4x4 s44;
+    float2x2 as22[3];
+    float2x3 as23[3];
+    float2x4 as24[3];
+    float3x2 as32[3];
+    float3x3 as33[3];
+    float3x4 as34[3];
+    float4x2 as42[3];
+    float4x3 as43[3];
+    float4x4 as44[3];
 };
 
 half4 main() {
-    return half4(0);
+    float value = u22[0][0] +
+                  u23[0][0] +
+                  u24[0][0] +
+                  u32[0][0] +
+                  u33[0][0] +
+                  u34[0][0] +
+                  u42[0][0] +
+                  u43[0][0] +
+                 au44[0][0][0] +
+                 au22[0][0][0] +
+                 au23[0][0][0] +
+                 au24[0][0][0] +
+                 au32[0][0][0] +
+                 au33[0][0][0] +
+                 au34[0][0][0] +
+                 au42[0][0][0] +
+                 au43[0][0][0] +
+                 au44[0][0][0] +
+                  s22[0][0] +
+                  s23[0][0] +
+                  s24[0][0] +
+                  s32[0][0] +
+                  s33[0][0] +
+                  s34[0][0] +
+                  s42[0][0] +
+                  s43[0][0] +
+                 as44[0][0][0] +
+                 as22[0][0][0] +
+                 as23[0][0][0] +
+                 as24[0][0][0] +
+                 as32[0][0][0] +
+                 as33[0][0][0] +
+                 as34[0][0][0] +
+                 as42[0][0][0] +
+                 as43[0][0][0] +
+                 as44[0][0][0];
+    return half4(value);
 }
diff --git a/src/sksl/codegen/SkSLWGSLCodeGenerator.cpp b/src/sksl/codegen/SkSLWGSLCodeGenerator.cpp
index a54753f..4987533 100644
--- a/src/sksl/codegen/SkSLWGSLCodeGenerator.cpp
+++ b/src/sksl/codegen/SkSLWGSLCodeGenerator.cpp
@@ -635,61 +635,89 @@
 
 void WGSLCodeGenerator::writeUniformPolyfills() {
     // If we didn't encounter any uniforms that need polyfilling, there is nothing to do.
-    if (fMatrixPolyfillFields.empty()) {
+    if (fFieldPolyfillMap.empty()) {
         return;
     }
 
     // We store the list of polyfilled fields as pointers in a hash-map, so the order can be
     // inconsistent across runs. For determinism, we sort the polyfilled objects by name here.
-    TArray<const MatrixPolyfillFieldMap::Pair*> matrixPolyfillOrderedFields;
-    matrixPolyfillOrderedFields.reserve_exact(fMatrixPolyfillFields.count());
+    TArray<const FieldPolyfillMap::Pair*> orderedFields;
+    orderedFields.reserve_exact(fFieldPolyfillMap.count());
 
-    fMatrixPolyfillFields.foreach([&](const MatrixPolyfillFieldMap::Pair& pair) {
-        matrixPolyfillOrderedFields.push_back(&pair);
+    fFieldPolyfillMap.foreach([&](const FieldPolyfillMap::Pair& pair) {
+        orderedFields.push_back(&pair);
     });
 
-    std::sort(matrixPolyfillOrderedFields.begin(),
-              matrixPolyfillOrderedFields.end(),
-              [](const MatrixPolyfillFieldMap::Pair* a, const MatrixPolyfillFieldMap::Pair* b) {
+    std::sort(orderedFields.begin(),
+              orderedFields.end(),
+              [](const FieldPolyfillMap::Pair* a, const FieldPolyfillMap::Pair* b) {
                   return a->second.fReplacementName < b->second.fReplacementName;
               });
 
+    THashSet<const Type*> writtenArrayElementPolyfill;
     bool writtenUniformMatrixPolyfill[5][5] = {};  // m[column][row] for each matrix type
     bool writtenUniformRowPolyfill[5] = {};        // for each matrix row-size
     bool anyFieldAccessed = false;
-    for (const MatrixPolyfillFieldMap::Pair* pair : matrixPolyfillOrderedFields) {
-        // Create structs representing the matrix as an array of vectors, whether or not the matrix
-        // is ever accessed by the SkSL. (The struct itself is mentioned in the list of uniforms.)
+    for (const FieldPolyfillMap::Pair* pair : orderedFields) {
         const auto& [field, info] = *pair;
-        int c = field->fType->columns();
-        int r = field->fType->rows();
-        if (!writtenUniformRowPolyfill[r]) {
-            writtenUniformRowPolyfill[r] = true;
+        const Type* type = field->fType;
 
-            this->write("struct _skRow");
-            this->write(std::to_string(r));
-            this->writeLine(" {");
-            this->write("  @size(16) r : vec");
-            this->write(std::to_string(r));
-            this->write("<");
-            this->write(to_wgsl_type(field->fType->componentType()));
-            this->writeLine(">");
-            this->writeLine("};");
+        if (info.fIsArray) {
+            type = &type->componentType();
+            if (!writtenArrayElementPolyfill.contains(type)) {
+                writtenArrayElementPolyfill.add(type);
+                this->write("struct _skArrayElement_");
+                this->write(type->abbreviatedName());
+                this->writeLine(" {");
+
+                if (info.fIsMatrix) {
+                    // Create a struct representing the array containing std140-padded matrices.
+                    this->write("  e : _skMatrix");
+                    this->write(std::to_string(type->columns()));
+                    this->writeLine(std::to_string(type->rows()));
+                } else {
+                    // Create a struct representing the array with extra padding between elements.
+                    this->write("  @size(16) e : ");
+                    this->writeLine(to_wgsl_type(*type));
+                }
+                this->writeLine("};");
+            }
         }
 
-        if (!writtenUniformMatrixPolyfill[c][r]) {
-            writtenUniformMatrixPolyfill[c][r] = true;
+        if (info.fIsMatrix) {
+            // Create structs representing the matrix as an array of vectors, whether or not the
+            // matrix is ever accessed by the SkSL. (The struct itself is mentioned in the list of
+            // uniforms.)
+            int c = type->columns();
+            int r = type->rows();
+            if (!writtenUniformRowPolyfill[r]) {
+                writtenUniformRowPolyfill[r] = true;
 
-            this->write("struct _skMatrix");
-            this->write(std::to_string(c));
-            this->write(std::to_string(r));
-            this->writeLine(" {");
-            this->write("  c : array<_skRow");
-            this->write(std::to_string(r));
-            this->write(", ");
-            this->write(std::to_string(c));
-            this->writeLine(">");
-            this->writeLine("};");
+                this->write("struct _skRow");
+                this->write(std::to_string(r));
+                this->writeLine(" {");
+                this->write("  @size(16) r : vec");
+                this->write(std::to_string(r));
+                this->write("<");
+                this->write(to_wgsl_type(type->componentType()));
+                this->writeLine(">");
+                this->writeLine("};");
+            }
+
+            if (!writtenUniformMatrixPolyfill[c][r]) {
+                writtenUniformMatrixPolyfill[c][r] = true;
+
+                this->write("struct _skMatrix");
+                this->write(std::to_string(c));
+                this->write(std::to_string(r));
+                this->writeLine(" {");
+                this->write("  c : array<_skRow");
+                this->write(std::to_string(r));
+                this->write(", ");
+                this->write(std::to_string(c));
+                this->writeLine(">");
+                this->writeLine("};");
+            }
         }
 
         // We create a polyfill variable only if the uniform was actually accessed.
@@ -723,7 +751,7 @@
     this->writeLine("fn _skInitializePolyfilledUniforms() {");
     ++fIndentation;
 
-    for (const MatrixPolyfillFieldMap::Pair* pair : matrixPolyfillOrderedFields) {
+    for (const FieldPolyfillMap::Pair* pair : orderedFields) {
         // Only initialize a polyfill global if the uniform was actually accessed.
         const auto& [field, info] = *pair;
         if (!info.fWasAccessed) {
@@ -739,34 +767,69 @@
 
         // Initialize the global variable associated with this uniform.
         // If the interface block is arrayed, the associated global will be arrayed as well.
-        int numElements = interfaceBlockType.isArray() ? interfaceBlockType.columns() : 1;
-        for (int arrayIdx = 0; arrayIdx < numElements; ++arrayIdx) {
+        int numIBElements = interfaceBlockType.isArray() ? interfaceBlockType.columns() : 1;
+        for (int ibIdx = 0; ibIdx < numIBElements; ++ibIdx) {
             this->write(info.fReplacementName);
             if (interfaceBlockType.isArray()) {
                 this->write("[");
-                this->write(std::to_string(arrayIdx));
+                this->write(std::to_string(ibIdx));
                 this->write("]");
             }
             this->write(" = ");
-            this->write(to_wgsl_type(*field->fType));
-            this->write("(");
-            auto separator = String::Separator();
-            int numColumns = field->fType->columns();
-            for (int column = 0; column < numColumns; column++) {
-                this->write(separator());
-                this->write(instanceName);
-                if (interfaceBlockType.isArray()) {
-                    this->write("[");
-                    this->write(std::to_string(arrayIdx));
-                    this->write("]");
-                }
-                this->write(".");
-                this->write(this->assembleName(field->fName));
-                this->write(".c[");
-                this->write(std::to_string(column));
-                this->write("].r");
+
+            const Type* type = field->fType;
+            int numArrayElements;
+            if (info.fIsArray) {
+                this->write(to_wgsl_type(*type));
+                this->write("(");
+                numArrayElements = type->columns();
+                type = &type->componentType();
+            }  else{
+                numArrayElements = 1;
             }
-            this->writeLine(");");
+
+            auto arraySeparator = String::Separator();
+            for (int arrayIdx = 0; arrayIdx < numArrayElements; arrayIdx++) {
+                this->write(arraySeparator());
+
+                std::string fieldName{instanceName};
+                if (interfaceBlockType.isArray()) {
+                    fieldName += '[';
+                    fieldName += std::to_string(ibIdx);
+                    fieldName += ']';
+                }
+                fieldName += '.';
+                fieldName += this->assembleName(field->fName);
+
+                if (info.fIsArray) {
+                    fieldName += '[';
+                    fieldName += std::to_string(arrayIdx);
+                    fieldName += "].e";
+                }
+
+                if (info.fIsMatrix) {
+                    this->write(to_wgsl_type(*type));
+                    this->write("(");
+                    int numColumns = type->columns();
+                    auto matrixSeparator = String::Separator();
+                    for (int column = 0; column < numColumns; column++) {
+                        this->write(matrixSeparator());
+                        this->write(fieldName);
+                        this->write(".c[");
+                        this->write(std::to_string(column));
+                        this->write("].r");
+                    }
+                    this->write(")");
+                } else {
+                    this->write(fieldName);
+                }
+            }
+
+            if (info.fIsArray) {
+                this->write(")");
+            }
+
+            this->writeLine(";");
         }
     }
 
@@ -1021,7 +1084,7 @@
 
     // Initialize polyfilled matrix uniforms if any were used.
     fIndentation++;
-    for (const auto& [field, info] : fMatrixPolyfillFields) {
+    for (const auto& [field, info] : fFieldPolyfillMap) {
         if (info.fWasAccessed) {
             this->writeLine("_skInitializePolyfilledUniforms();");
             break;
@@ -1890,15 +1953,15 @@
     const Field* field = &f.base()->type().fields()[f.fieldIndex()];
     std::string expr;
 
-    if (MatrixPolyfillInfo* polyfillInfo = fMatrixPolyfillFields.find(field)) {
+    if (FieldPolyfillInfo* polyfillInfo = fFieldPolyfillMap.find(field)) {
         // We found a matrix uniform. We are required to pass some matrix uniforms as array vectors,
         // since the std140 layout for a matrix assumes 4-column vectors for each row, and WGSL
         // tightly packs 2-column matrices. When emitting code, we replace the field-access
         // expression with a global variable which holds an unpacked version of the uniform.
         polyfillInfo->fWasAccessed = true;
 
-        // The matrix polyfill can either be based directly onto a uniform in an interface block, or
-        // it might be based on an index-expression onto a uniform if the block is arrayed.
+        // The polyfill can either be based directly onto a uniform in an interface block, or it
+        // might be based on an index-expression onto a uniform if the interface block is arrayed.
         const Expression* base = f.base().get();
         const IndexExpression* indexExpr = nullptr;
         if (base->is<IndexExpression>()) {
@@ -3186,10 +3249,22 @@
 
         this->write(this->assembleName(field.fName));
         this->write(": ");
-        if (fMatrixPolyfillFields.find(&field)) {
-            this->write("_skMatrix");
-            this->write(std::to_string(field.fType->columns()));
-            this->write(std::to_string(field.fType->rows()));
+        if (const FieldPolyfillInfo* info = fFieldPolyfillMap.find(&field)) {
+            if (info->fIsArray) {
+                // This properly handles arrays of matrices, as well as arrays of other primitives.
+                SkASSERT(field.fType->isArray());
+                this->write("array<_skArrayElement_");
+                this->write(field.fType->abbreviatedName());
+                this->write(", ");
+                this->write(std::to_string(field.fType->columns()));
+                this->write(">");
+            } else if (info->fIsMatrix) {
+                this->write("_skMatrix");
+                this->write(std::to_string(field.fType->columns()));
+                this->write(std::to_string(field.fType->rows()));
+            } else {
+                SkDEBUGFAILF("need polyfill for %s", info->fReplacementName.c_str());
+            }
         } else {
             this->write(to_wgsl_type(*field.fType));
         }
@@ -3332,21 +3407,41 @@
 
     const Type& structType = interfaceBlock->var()->type().componentType();
     for (const Field& field : structType.fields()) {
-        // Matrices will be represented as 16-byte aligned arrays in std140, and reconstituted into
-        // proper matrices as they are later accessed. We need to synthesize helpers for this.
-        if (field.fType->isMatrix()) {
-            // A polyfill is only necessary if the std140 layout (what Skia provides) actually
-            // differs from native layout (what WGSL expects). Otherwise the matrix is used as-is.
-            if (std140.stride(*field.fType) == native.stride(*field.fType)) {
-                continue;
-            }
+        const Type* type = field.fType;
+        bool needsArrayPolyfill = false;
+        bool needsMatrixPolyfill = false;
 
+        auto isPolyfillableMatrixType = [&](const Type* type) {
+            return type->isMatrix() && std140.stride(*type) != native.stride(*type);
+        };
+
+        if (isPolyfillableMatrixType(type)) {
+            // Matrices will be represented as 16-byte aligned arrays in std140, and reconstituted
+            // into proper matrices as they are later accessed. We need to synthesize polyfill.
+            needsMatrixPolyfill = true;
+        } else if (type->isArray() && !type->isUnsizedArray() &&
+                   !type->componentType().isOpaque()) {
+            const Type* innerType = &type->componentType();
+            if (isPolyfillableMatrixType(innerType)) {
+                // Use a polyfill when the array contains a matrix that requires polyfill.
+                needsArrayPolyfill = true;
+                needsMatrixPolyfill = true;
+            } else if (native.size(*innerType) < 16) {
+                // Use a polyfill when the array elements are smaller than 16 bytes, since std140
+                // will pad elements to a 16-byte stride.
+                needsArrayPolyfill = true;
+            }
+        }
+
+        if (needsArrayPolyfill || needsMatrixPolyfill) {
             // Add a polyfill for this matrix type.
-            MatrixPolyfillInfo info;
+            FieldPolyfillInfo info;
             info.fInterfaceBlock = interfaceBlock;
-            info.fReplacementName = "_skUnpacked_" + std::string(instanceName) + '_'
-                                    + this->assembleName(field.fName);
-            fMatrixPolyfillFields.set(&field, info);
+            info.fReplacementName = "_skUnpacked_" + std::string(instanceName) + '_' +
+                                    this->assembleName(field.fName);
+            info.fIsArray = needsArrayPolyfill;
+            info.fIsMatrix = needsMatrixPolyfill;
+            fFieldPolyfillMap.set(&field, info);
         }
     }
 }
diff --git a/src/sksl/codegen/SkSLWGSLCodeGenerator.h b/src/sksl/codegen/SkSLWGSLCodeGenerator.h
index 79166e3..3f08602 100644
--- a/src/sksl/codegen/SkSLWGSLCodeGenerator.h
+++ b/src/sksl/codegen/SkSLWGSLCodeGenerator.h
@@ -357,16 +357,20 @@
     bool fWrittenInverse3 = false;
     bool fWrittenInverse4 = false;
 
-    // These fields control uniform-matrix polyfill support. Because our uniform data is provided in
-    // std140 layout, matrices need to be represented as arrays of @size(16)-aligned vectors, and
-    // are unpacked into globals at the shader entrypoint.
-    struct MatrixPolyfillInfo {
+    // These fields control uniform polyfill support in cases where WGSL and std140 disagree.
+    // In std140 layout, matrices need to be represented as arrays of @size(16)-aligned vectors, and
+    // array elements are wrapped in a struct containing a single @size(16)-aligned element. Arrays
+    // of matrices combine both wrappers. These wrapper structs are unpacked into natively-typed
+    // globals at the shader entrypoint.
+    struct FieldPolyfillInfo {
         const InterfaceBlock* fInterfaceBlock;
         std::string fReplacementName;
+        bool fIsArray = false;
+        bool fIsMatrix = false;
         bool fWasAccessed = false;
     };
-    using MatrixPolyfillFieldMap = skia_private::THashMap<const Field*, MatrixPolyfillInfo>;
-    MatrixPolyfillFieldMap fMatrixPolyfillFields;
+    using FieldPolyfillMap = skia_private::THashMap<const Field*, FieldPolyfillInfo>;
+    FieldPolyfillMap fFieldPolyfillMap;
 
     // Output processing state.
     int fIndentation = 0;
diff --git a/tests/sksl/shared/InterfaceBlockNamedArray.asm.frag b/tests/sksl/shared/InterfaceBlockNamedArray.asm.frag
index b3daa42..632ae52 100644
--- a/tests/sksl/shared/InterfaceBlockNamedArray.asm.frag
+++ b/tests/sksl/shared/InterfaceBlockNamedArray.asm.frag
@@ -1,6 +1,6 @@
 ### Compilation failed:
 
-error: SPIR-V validation error: Block decoration on target <id> '10[%_arr_testBlock_int_2]' must be a structure type
+error: SPIR-V validation error: Block decoration on target <id> '12[%_arr_testBlock_int_2]' must be a structure type
   OpDecorate %_arr_testBlock_int_2 Block
 
                OpCapability Shader
@@ -9,16 +9,22 @@
                OpEntryPoint Fragment %main "main" %sk_Clockwise %sk_FragColor
                OpExecutionMode %main OriginUpperLeft
                OpName %testBlock "testBlock"
-               OpMemberName %testBlock 0 "x"
+               OpMemberName %testBlock 0 "s"
                OpMemberName %testBlock 1 "m"
+               OpMemberName %testBlock 2 "a"
+               OpMemberName %testBlock 3 "am"
                OpName %sk_Clockwise "sk_Clockwise"
                OpName %sk_FragColor "sk_FragColor"
                OpName %main "main"
+               OpDecorate %_arr_float_int_2 ArrayStride 16
+               OpDecorate %_arr_mat2v2float_int_2 ArrayStride 32
                OpMemberDecorate %testBlock 0 Offset 0
                OpMemberDecorate %testBlock 1 Offset 16
                OpMemberDecorate %testBlock 1 ColMajor
                OpMemberDecorate %testBlock 1 MatrixStride 16
-               OpDecorate %_arr_testBlock_int_2 ArrayStride 48
+               OpMemberDecorate %testBlock 2 Offset 48
+               OpMemberDecorate %testBlock 3 Offset 80
+               OpDecorate %_arr_testBlock_int_2 ArrayStride 144
                OpDecorate %_arr_testBlock_int_2 Block
                OpDecorate %3 Binding 123
                OpDecorate %3 DescriptorSet 0
@@ -26,15 +32,15 @@
                OpDecorate %sk_FragColor RelaxedPrecision
                OpDecorate %sk_FragColor Location 0
                OpDecorate %sk_FragColor Index 0
-               OpDecorate %32 RelaxedPrecision
-               OpDecorate %33 RelaxedPrecision
-               OpDecorate %34 RelaxedPrecision
+               OpDecorate %38 RelaxedPrecision
       %float = OpTypeFloat 32
     %v2float = OpTypeVector %float 2
 %mat2v2float = OpTypeMatrix %v2float 2
-  %testBlock = OpTypeStruct %float %mat2v2float
         %int = OpTypeInt 32 1
       %int_2 = OpConstant %int 2
+%_arr_float_int_2 = OpTypeArray %float %int_2
+%_arr_mat2v2float_int_2 = OpTypeArray %mat2v2float %int_2
+  %testBlock = OpTypeStruct %float %mat2v2float %_arr_float_int_2 %_arr_mat2v2float_int_2
 %_arr_testBlock_int_2 = OpTypeArray %testBlock %int_2
 %_ptr_Uniform__arr_testBlock_int_2 = OpTypePointer Uniform %_arr_testBlock_int_2
           %3 = OpVariable %_ptr_Uniform__arr_testBlock_int_2 Uniform
@@ -45,24 +51,26 @@
 %_ptr_Output_v4float = OpTypePointer Output %v4float
 %sk_FragColor = OpVariable %_ptr_Output_v4float Output
        %void = OpTypeVoid
-         %19 = OpTypeFunction %void
+         %21 = OpTypeFunction %void
       %int_0 = OpConstant %int 0
 %_ptr_Uniform_float = OpTypePointer Uniform %float
       %int_1 = OpConstant %int 1
 %_ptr_Uniform_v2float = OpTypePointer Uniform %v2float
-       %main = OpFunction %void None %19
-         %20 = OpLabel
-         %22 = OpAccessChain %_ptr_Uniform_float %3 %int_0 %int_0
-         %24 = OpLoad %float %22
-         %26 = OpAccessChain %_ptr_Uniform_v2float %3 %int_0 %int_1 %int_0
-         %28 = OpLoad %v2float %26
-         %29 = OpCompositeExtract %float %28 1
-         %30 = OpAccessChain %_ptr_Uniform_v2float %3 %int_1 %int_1 %int_1
-         %31 = OpLoad %v2float %30
-         %32 = OpCompositeExtract %float %31 0
-         %33 = OpCompositeExtract %float %31 1
-         %34 = OpCompositeConstruct %v4float %24 %29 %32 %33
-               OpStore %sk_FragColor %34
+      %int_3 = OpConstant %int 3
+       %main = OpFunction %void None %21
+         %22 = OpLabel
+         %24 = OpAccessChain %_ptr_Uniform_float %3 %int_0 %int_0
+         %26 = OpLoad %float %24
+         %28 = OpAccessChain %_ptr_Uniform_v2float %3 %int_1 %int_1 %int_1
+         %30 = OpLoad %v2float %28
+         %31 = OpCompositeExtract %float %30 0
+         %32 = OpAccessChain %_ptr_Uniform_float %3 %int_0 %int_2 %int_1
+         %33 = OpLoad %float %32
+         %35 = OpAccessChain %_ptr_Uniform_v2float %3 %int_1 %int_3 %int_1 %int_0
+         %36 = OpLoad %v2float %35
+         %37 = OpCompositeExtract %float %36 1
+         %38 = OpCompositeConstruct %v4float %26 %31 %33 %37
+               OpStore %sk_FragColor %38
                OpReturn
                OpFunctionEnd
 
diff --git a/tests/sksl/shared/InterfaceBlockNamedArray.glsl b/tests/sksl/shared/InterfaceBlockNamedArray.glsl
index 0d89aed..49ee265 100644
--- a/tests/sksl/shared/InterfaceBlockNamedArray.glsl
+++ b/tests/sksl/shared/InterfaceBlockNamedArray.glsl
@@ -1,9 +1,11 @@
 
 out vec4 sk_FragColor;
 layout (binding = 123) uniform testBlock {
-    layout (offset = 0) float x;
+    layout (offset = 0) float s;
     layout (offset = 16) mat2 m;
+    layout (offset = 48) float[2] a;
+    layout (offset = 80) mat2[2] am;
 } test[2];
 void main() {
-    sk_FragColor = vec4(test[0].x, test[0].m[0].y, test[1].m[1]);
+    sk_FragColor = vec4(test[0].s, test[1].m[1].x, test[0].a[1], test[1].am[1][0].y);
 }
diff --git a/tests/sksl/shared/InterfaceBlockNamedArray.hlsl b/tests/sksl/shared/InterfaceBlockNamedArray.hlsl
index b3daa42..632ae52 100644
--- a/tests/sksl/shared/InterfaceBlockNamedArray.hlsl
+++ b/tests/sksl/shared/InterfaceBlockNamedArray.hlsl
@@ -1,6 +1,6 @@
 ### Compilation failed:
 
-error: SPIR-V validation error: Block decoration on target <id> '10[%_arr_testBlock_int_2]' must be a structure type
+error: SPIR-V validation error: Block decoration on target <id> '12[%_arr_testBlock_int_2]' must be a structure type
   OpDecorate %_arr_testBlock_int_2 Block
 
                OpCapability Shader
@@ -9,16 +9,22 @@
                OpEntryPoint Fragment %main "main" %sk_Clockwise %sk_FragColor
                OpExecutionMode %main OriginUpperLeft
                OpName %testBlock "testBlock"
-               OpMemberName %testBlock 0 "x"
+               OpMemberName %testBlock 0 "s"
                OpMemberName %testBlock 1 "m"
+               OpMemberName %testBlock 2 "a"
+               OpMemberName %testBlock 3 "am"
                OpName %sk_Clockwise "sk_Clockwise"
                OpName %sk_FragColor "sk_FragColor"
                OpName %main "main"
+               OpDecorate %_arr_float_int_2 ArrayStride 16
+               OpDecorate %_arr_mat2v2float_int_2 ArrayStride 32
                OpMemberDecorate %testBlock 0 Offset 0
                OpMemberDecorate %testBlock 1 Offset 16
                OpMemberDecorate %testBlock 1 ColMajor
                OpMemberDecorate %testBlock 1 MatrixStride 16
-               OpDecorate %_arr_testBlock_int_2 ArrayStride 48
+               OpMemberDecorate %testBlock 2 Offset 48
+               OpMemberDecorate %testBlock 3 Offset 80
+               OpDecorate %_arr_testBlock_int_2 ArrayStride 144
                OpDecorate %_arr_testBlock_int_2 Block
                OpDecorate %3 Binding 123
                OpDecorate %3 DescriptorSet 0
@@ -26,15 +32,15 @@
                OpDecorate %sk_FragColor RelaxedPrecision
                OpDecorate %sk_FragColor Location 0
                OpDecorate %sk_FragColor Index 0
-               OpDecorate %32 RelaxedPrecision
-               OpDecorate %33 RelaxedPrecision
-               OpDecorate %34 RelaxedPrecision
+               OpDecorate %38 RelaxedPrecision
       %float = OpTypeFloat 32
     %v2float = OpTypeVector %float 2
 %mat2v2float = OpTypeMatrix %v2float 2
-  %testBlock = OpTypeStruct %float %mat2v2float
         %int = OpTypeInt 32 1
       %int_2 = OpConstant %int 2
+%_arr_float_int_2 = OpTypeArray %float %int_2
+%_arr_mat2v2float_int_2 = OpTypeArray %mat2v2float %int_2
+  %testBlock = OpTypeStruct %float %mat2v2float %_arr_float_int_2 %_arr_mat2v2float_int_2
 %_arr_testBlock_int_2 = OpTypeArray %testBlock %int_2
 %_ptr_Uniform__arr_testBlock_int_2 = OpTypePointer Uniform %_arr_testBlock_int_2
           %3 = OpVariable %_ptr_Uniform__arr_testBlock_int_2 Uniform
@@ -45,24 +51,26 @@
 %_ptr_Output_v4float = OpTypePointer Output %v4float
 %sk_FragColor = OpVariable %_ptr_Output_v4float Output
        %void = OpTypeVoid
-         %19 = OpTypeFunction %void
+         %21 = OpTypeFunction %void
       %int_0 = OpConstant %int 0
 %_ptr_Uniform_float = OpTypePointer Uniform %float
       %int_1 = OpConstant %int 1
 %_ptr_Uniform_v2float = OpTypePointer Uniform %v2float
-       %main = OpFunction %void None %19
-         %20 = OpLabel
-         %22 = OpAccessChain %_ptr_Uniform_float %3 %int_0 %int_0
-         %24 = OpLoad %float %22
-         %26 = OpAccessChain %_ptr_Uniform_v2float %3 %int_0 %int_1 %int_0
-         %28 = OpLoad %v2float %26
-         %29 = OpCompositeExtract %float %28 1
-         %30 = OpAccessChain %_ptr_Uniform_v2float %3 %int_1 %int_1 %int_1
-         %31 = OpLoad %v2float %30
-         %32 = OpCompositeExtract %float %31 0
-         %33 = OpCompositeExtract %float %31 1
-         %34 = OpCompositeConstruct %v4float %24 %29 %32 %33
-               OpStore %sk_FragColor %34
+      %int_3 = OpConstant %int 3
+       %main = OpFunction %void None %21
+         %22 = OpLabel
+         %24 = OpAccessChain %_ptr_Uniform_float %3 %int_0 %int_0
+         %26 = OpLoad %float %24
+         %28 = OpAccessChain %_ptr_Uniform_v2float %3 %int_1 %int_1 %int_1
+         %30 = OpLoad %v2float %28
+         %31 = OpCompositeExtract %float %30 0
+         %32 = OpAccessChain %_ptr_Uniform_float %3 %int_0 %int_2 %int_1
+         %33 = OpLoad %float %32
+         %35 = OpAccessChain %_ptr_Uniform_v2float %3 %int_1 %int_3 %int_1 %int_0
+         %36 = OpLoad %v2float %35
+         %37 = OpCompositeExtract %float %36 1
+         %38 = OpCompositeConstruct %v4float %26 %31 %33 %37
+               OpStore %sk_FragColor %38
                OpReturn
                OpFunctionEnd
 
diff --git a/tests/sksl/shared/InterfaceBlockNamedArray.metal b/tests/sksl/shared/InterfaceBlockNamedArray.metal
index a2ec37b..66baadb 100644
--- a/tests/sksl/shared/InterfaceBlockNamedArray.metal
+++ b/tests/sksl/shared/InterfaceBlockNamedArray.metal
@@ -7,9 +7,13 @@
     half4 sk_FragColor [[color(0)]];
 };
 struct testBlock {
-    float x;
+    float s;
     char pad0[12];
     float2x2 m;
+    char pad1[16];
+    array<float, 2> a;
+    char pad2[24];
+    array<float2x2, 2> am;
 } test[2];
 struct Globals {
     constant testBlock* test;
@@ -19,6 +23,6 @@
     (void)_globals;
     Outputs _out;
     (void)_out;
-    _out.sk_FragColor = half4(half(_uniforms.test[0].x), half(_uniforms.test[0].m[0].y), half2(_uniforms.test[1].m[1]));
+    _out.sk_FragColor = half4(half(_uniforms.test[0].s), half(_uniforms.test[1].m[1].x), half(_uniforms.test[0].a[1]), half(_uniforms.test[1].am[1][0].y));
     return _out;
 }
diff --git a/tests/sksl/shared/InterfaceBlockNamedArray.skrp b/tests/sksl/shared/InterfaceBlockNamedArray.skrp
index d630e33..2387e92 100644
--- a/tests/sksl/shared/InterfaceBlockNamedArray.skrp
+++ b/tests/sksl/shared/InterfaceBlockNamedArray.skrp
@@ -3,19 +3,22 @@
 error: 1: interface blocks are not allowed in this kind of program
 layout(binding=123) uniform testBlock {
                             ^^^^^^^^^
-error: 6: 'main' must return: 'vec4', 'float4', or 'half4'
+error: 8: 'main' must return: 'vec4', 'float4', or 'half4'
 void main() {
 ^^^^^^^^^^^
-error: 7: unknown identifier 'sk_FragColor'
-    sk_FragColor = half4(test[0].x, test[0].m[0][1], test[1].m[1]);
+error: 9: unknown identifier 'sk_FragColor'
+    sk_FragColor = half4(test[0].s, test[1].m[1][0], test[0].a[1], test[1].am[1][0][1]);
     ^^^^^^^^^^^^
-error: 7: unknown identifier 'test'
-    sk_FragColor = half4(test[0].x, test[0].m[0][1], test[1].m[1]);
+error: 9: unknown identifier 'test'
+    sk_FragColor = half4(test[0].s, test[1].m[1][0], test[0].a[1], test[1].am[1][0][1]);
                          ^^^^
-error: 7: unknown identifier 'test'
-    sk_FragColor = half4(test[0].x, test[0].m[0][1], test[1].m[1]);
+error: 9: unknown identifier 'test'
+    sk_FragColor = half4(test[0].s, test[1].m[1][0], test[0].a[1], test[1].am[1][0][1]);
                                     ^^^^
-error: 7: unknown identifier 'test'
-    sk_FragColor = half4(test[0].x, test[0].m[0][1], test[1].m[1]);
+error: 9: unknown identifier 'test'
+    sk_FragColor = half4(test[0].s, test[1].m[1][0], test[0].a[1], test[1].am[1][0][1]);
                                                      ^^^^
-6 errors
+error: 9: unknown identifier 'test'
+    sk_FragColor = half4(test[0].s, test[1].m[1][0], test[0].a[1], test[1].am[1][0][1]);
+                                                                   ^^^^
+7 errors
diff --git a/tests/sksl/shared/InterfaceBlockNamedArray.wgsl b/tests/sksl/shared/InterfaceBlockNamedArray.wgsl
index 9c7c248..4d59535 100644
--- a/tests/sksl/shared/InterfaceBlockNamedArray.wgsl
+++ b/tests/sksl/shared/InterfaceBlockNamedArray.wgsl
@@ -6,13 +6,15 @@
   @location(0) sk_FragColor: vec4<f32>,
 };
 struct testBlock {
-  @size(16) x: f32,
-  m: _skMatrix22,
+  @size(16) s: f32,
+  @size(32) m: _skMatrix22,
+  @size(32) a: array<_skArrayElement_f, 2>,
+  am: array<_skArrayElement_f22, 2>,
 };
 @group(0) @binding(123) var<uniform> test : array<testBlock, 2>;
 fn main(_stageOut: ptr<function, FSOut>) {
   {
-    (*_stageOut).sk_FragColor = vec4<f32>(f32(test[0].x), f32(_skUnpacked_test_m[0][0].y), vec2<f32>(_skUnpacked_test_m[1][1]));
+    (*_stageOut).sk_FragColor = vec4<f32>(f32(test[0].s), f32(_skUnpacked_test_m[1][1].x), f32(_skUnpacked_test_a[0][1]), f32(_skUnpacked_test_am[1][1][0].y));
   }
 }
 @fragment fn fragmentMain(_stageIn: FSIn) -> FSOut {
@@ -21,14 +23,26 @@
   main(&_stageOut);
   return _stageOut;
 }
+struct _skArrayElement_f {
+  @size(16) e : f32
+};
+var<private> _skUnpacked_test_a: array<array<f32, 2>, 2>;
+struct _skArrayElement_f22 {
+  e : _skMatrix22
+};
 struct _skRow2 {
   @size(16) r : vec2<f32>
 };
 struct _skMatrix22 {
   c : array<_skRow2, 2>
 };
+var<private> _skUnpacked_test_am: array<array<mat2x2<f32>, 2>, 2>;
 var<private> _skUnpacked_test_m: array<mat2x2<f32>, 2>;
 fn _skInitializePolyfilledUniforms() {
+  _skUnpacked_test_a[0] = array<f32, 2>(test[0].a[0].e, test[0].a[1].e);
+  _skUnpacked_test_a[1] = array<f32, 2>(test[1].a[0].e, test[1].a[1].e);
+  _skUnpacked_test_am[0] = array<mat2x2<f32>, 2>(mat2x2<f32>(test[0].am[0].e.c[0].r, test[0].am[0].e.c[1].r), mat2x2<f32>(test[0].am[1].e.c[0].r, test[0].am[1].e.c[1].r));
+  _skUnpacked_test_am[1] = array<mat2x2<f32>, 2>(mat2x2<f32>(test[1].am[0].e.c[0].r, test[1].am[0].e.c[1].r), mat2x2<f32>(test[1].am[1].e.c[0].r, test[1].am[1].e.c[1].r));
   _skUnpacked_test_m[0] = mat2x2<f32>(test[0].m.c[0].r, test[0].m.c[1].r);
   _skUnpacked_test_m[1] = mat2x2<f32>(test[1].m.c[0].r, test[1].m.c[1].r);
 }
diff --git a/tests/sksl/shared/UniformBuffers.wgsl b/tests/sksl/shared/UniformBuffers.wgsl
index dd50029..81ce1f0 100644
--- a/tests/sksl/shared/UniformBuffers.wgsl
+++ b/tests/sksl/shared/UniformBuffers.wgsl
@@ -1,24 +1,3 @@
-### Compilation failed:
-
-error: :11:16 error: uniform storage requires that array elements are aligned to 16 bytes, but array element of type 'f32' has a stride of 4 bytes. Consider using a vector or struct as the element type instead.
-  @size(32) y: array<f32, 2>,
-               ^^^^^^^^^^^^^
-
-:8:1 note: see layout of struct:
-/*            align(16) size(96) */ struct testBlock {
-/* offset( 0) align( 4) size( 4) */   x : f32;
-/* offset( 4) align( 4) size(12) */   w : i32;
-/* offset(16) align( 4) size(32) */   y : array<f32, 2>;
-/* offset(48) align(16) size(48) */   z : mat3x3<f32>;
-/*                               */ };
-struct testBlock {
-^^^^^^
-
-:14:36 note: 'testBlock' used in address space 'uniform' here
-@group(0) @binding(0) var<uniform> _uniform0 : testBlock;
-                                   ^^^^^^^^^
-
-
 diagnostic(off, derivative_uniformity);
 struct FSIn {
   @builtin(front_facing) sk_Clockwise: bool,
@@ -29,19 +8,25 @@
 struct testBlock {
   @size(4) x: f32,
   @size(12) w: i32,
-  @size(32) y: array<f32, 2>,
+  @size(32) y: array<_skArrayElement_h, 2>,
   z: mat3x3<f32>,
 };
 @group(0) @binding(0) var<uniform> _uniform0 : testBlock;
 fn main(_stageOut: ptr<function, FSOut>) {
   {
-    (*_stageOut).sk_FragColor = vec4<f32>(_uniform0.x, _uniform0.y[0], _uniform0.y[1], 0.0);
+    (*_stageOut).sk_FragColor = vec4<f32>(_uniform0.x, _skUnpacked__uniform0_y[0], _skUnpacked__uniform0_y[1], 0.0);
   }
 }
 @fragment fn fragmentMain(_stageIn: FSIn) -> FSOut {
+  _skInitializePolyfilledUniforms();
   var _stageOut: FSOut;
   main(&_stageOut);
   return _stageOut;
 }
-
-1 error
+struct _skArrayElement_h {
+  @size(16) e : f32
+};
+var<private> _skUnpacked__uniform0_y: array<f32, 2>;
+fn _skInitializePolyfilledUniforms() {
+  _skUnpacked__uniform0_y = array<f32, 2>(_uniform0.y[0].e, _uniform0.y[1].e);
+}
diff --git a/tests/sksl/wgsl/UniformArrays.wgsl b/tests/sksl/wgsl/UniformArrays.wgsl
new file mode 100644
index 0000000..c11c2ed
--- /dev/null
+++ b/tests/sksl/wgsl/UniformArrays.wgsl
@@ -0,0 +1,114 @@
+diagnostic(off, derivative_uniformity);
+struct FSIn {
+  @builtin(front_facing) sk_Clockwise: bool,
+};
+struct FSOut {
+  @location(0) sk_FragColor: vec4<f32>,
+};
+struct UniformBuffer {
+  uf: array<_skArrayElement_f, 3>,
+  uf2: array<_skArrayElement_f2, 3>,
+  uf3: array<_skArrayElement_f3, 3>,
+  uf4: array<vec4<f32>, 3>,
+  uh: array<_skArrayElement_h, 3>,
+  uh2: array<_skArrayElement_h2, 3>,
+  uh3: array<_skArrayElement_h3, 3>,
+  uh4: array<vec4<f32>, 3>,
+  ui: array<_skArrayElement_i, 3>,
+  ui2: array<_skArrayElement_i2, 3>,
+  ui3: array<_skArrayElement_i3, 3>,
+  ui4: array<vec4<i32>, 3>,
+};
+@group(0) @binding(1) var<uniform> _uniform0 : UniformBuffer;
+struct StorageBuffer {
+  sf: array<_skArrayElement_f, 4>,
+  sf2: array<_skArrayElement_f2, 4>,
+  sf3: array<_skArrayElement_f3, 4>,
+  sf4: array<vec4<f32>, 4>,
+  sh: array<_skArrayElement_h, 4>,
+  sh2: array<_skArrayElement_h2, 4>,
+  sh3: array<_skArrayElement_h3, 4>,
+  sh4: array<vec4<f32>, 4>,
+  si: array<_skArrayElement_i, 4>,
+  si2: array<_skArrayElement_i2, 4>,
+  si3: array<_skArrayElement_i3, 4>,
+  si4: array<vec4<i32>, 4>,
+};
+@group(0) @binding(2) var<storage, read_write> _storage1 : StorageBuffer;
+fn main() -> vec4<f32> {
+  {
+    var value: f32 = ((((((((((((((((((((((_skUnpacked__uniform0_uf[1] + _skUnpacked__uniform0_uf2[1].x) + _skUnpacked__uniform0_uf3[1].x) + _uniform0.uf4[1].x) + f32(_skUnpacked__uniform0_uh[1])) + f32(_skUnpacked__uniform0_uh2[1].x)) + f32(_skUnpacked__uniform0_uh3[1].x)) + f32(_uniform0.uh4[1].x)) + f32(_skUnpacked__uniform0_ui[1])) + f32(_skUnpacked__uniform0_ui2[1].x)) + f32(_skUnpacked__uniform0_ui3[1].x)) + f32(_uniform0.ui4[1].x)) + _skUnpacked__storage1_sf[1]) + _skUnpacked__storage1_sf2[1].x) + _skUnpacked__storage1_sf3[1].x) + _storage1.sf4[1].x) + f32(_skUnpacked__storage1_sh[1])) + f32(_skUnpacked__storage1_sh2[1].x)) + f32(_skUnpacked__storage1_sh3[1].x)) + f32(_storage1.sh4[1].x)) + f32(_skUnpacked__storage1_si[1])) + f32(_skUnpacked__storage1_si2[1].x)) + f32(_skUnpacked__storage1_si3[1].x)) + f32(_storage1.si4[1].x);
+    return vec4<f32>(f32(value));
+  }
+}
+@fragment fn fragmentMain(_stageIn: FSIn) -> FSOut {
+  _skInitializePolyfilledUniforms();
+  var _stageOut: FSOut;
+  _stageOut.sk_FragColor = main();
+  return _stageOut;
+}
+struct _skArrayElement_f {
+  @size(16) e : f32
+};
+var<private> _skUnpacked__storage1_sf: array<f32, 4>;
+struct _skArrayElement_f2 {
+  @size(16) e : vec2<f32>
+};
+var<private> _skUnpacked__storage1_sf2: array<vec2<f32>, 4>;
+struct _skArrayElement_f3 {
+  @size(16) e : vec3<f32>
+};
+var<private> _skUnpacked__storage1_sf3: array<vec3<f32>, 4>;
+struct _skArrayElement_h {
+  @size(16) e : f32
+};
+var<private> _skUnpacked__storage1_sh: array<f32, 4>;
+struct _skArrayElement_h2 {
+  @size(16) e : vec2<f32>
+};
+var<private> _skUnpacked__storage1_sh2: array<vec2<f32>, 4>;
+struct _skArrayElement_h3 {
+  @size(16) e : vec3<f32>
+};
+var<private> _skUnpacked__storage1_sh3: array<vec3<f32>, 4>;
+struct _skArrayElement_i {
+  @size(16) e : i32
+};
+var<private> _skUnpacked__storage1_si: array<i32, 4>;
+struct _skArrayElement_i2 {
+  @size(16) e : vec2<i32>
+};
+var<private> _skUnpacked__storage1_si2: array<vec2<i32>, 4>;
+struct _skArrayElement_i3 {
+  @size(16) e : vec3<i32>
+};
+var<private> _skUnpacked__storage1_si3: array<vec3<i32>, 4>;
+var<private> _skUnpacked__uniform0_uf: array<f32, 3>;
+var<private> _skUnpacked__uniform0_uf2: array<vec2<f32>, 3>;
+var<private> _skUnpacked__uniform0_uf3: array<vec3<f32>, 3>;
+var<private> _skUnpacked__uniform0_uh: array<f32, 3>;
+var<private> _skUnpacked__uniform0_uh2: array<vec2<f32>, 3>;
+var<private> _skUnpacked__uniform0_uh3: array<vec3<f32>, 3>;
+var<private> _skUnpacked__uniform0_ui: array<i32, 3>;
+var<private> _skUnpacked__uniform0_ui2: array<vec2<i32>, 3>;
+var<private> _skUnpacked__uniform0_ui3: array<vec3<i32>, 3>;
+fn _skInitializePolyfilledUniforms() {
+  _skUnpacked__storage1_sf = array<f32, 4>(_storage1.sf[0].e, _storage1.sf[1].e, _storage1.sf[2].e, _storage1.sf[3].e);
+  _skUnpacked__storage1_sf2 = array<vec2<f32>, 4>(_storage1.sf2[0].e, _storage1.sf2[1].e, _storage1.sf2[2].e, _storage1.sf2[3].e);
+  _skUnpacked__storage1_sf3 = array<vec3<f32>, 4>(_storage1.sf3[0].e, _storage1.sf3[1].e, _storage1.sf3[2].e, _storage1.sf3[3].e);
+  _skUnpacked__storage1_sh = array<f32, 4>(_storage1.sh[0].e, _storage1.sh[1].e, _storage1.sh[2].e, _storage1.sh[3].e);
+  _skUnpacked__storage1_sh2 = array<vec2<f32>, 4>(_storage1.sh2[0].e, _storage1.sh2[1].e, _storage1.sh2[2].e, _storage1.sh2[3].e);
+  _skUnpacked__storage1_sh3 = array<vec3<f32>, 4>(_storage1.sh3[0].e, _storage1.sh3[1].e, _storage1.sh3[2].e, _storage1.sh3[3].e);
+  _skUnpacked__storage1_si = array<i32, 4>(_storage1.si[0].e, _storage1.si[1].e, _storage1.si[2].e, _storage1.si[3].e);
+  _skUnpacked__storage1_si2 = array<vec2<i32>, 4>(_storage1.si2[0].e, _storage1.si2[1].e, _storage1.si2[2].e, _storage1.si2[3].e);
+  _skUnpacked__storage1_si3 = array<vec3<i32>, 4>(_storage1.si3[0].e, _storage1.si3[1].e, _storage1.si3[2].e, _storage1.si3[3].e);
+  _skUnpacked__uniform0_uf = array<f32, 3>(_uniform0.uf[0].e, _uniform0.uf[1].e, _uniform0.uf[2].e);
+  _skUnpacked__uniform0_uf2 = array<vec2<f32>, 3>(_uniform0.uf2[0].e, _uniform0.uf2[1].e, _uniform0.uf2[2].e);
+  _skUnpacked__uniform0_uf3 = array<vec3<f32>, 3>(_uniform0.uf3[0].e, _uniform0.uf3[1].e, _uniform0.uf3[2].e);
+  _skUnpacked__uniform0_uh = array<f32, 3>(_uniform0.uh[0].e, _uniform0.uh[1].e, _uniform0.uh[2].e);
+  _skUnpacked__uniform0_uh2 = array<vec2<f32>, 3>(_uniform0.uh2[0].e, _uniform0.uh2[1].e, _uniform0.uh2[2].e);
+  _skUnpacked__uniform0_uh3 = array<vec3<f32>, 3>(_uniform0.uh3[0].e, _uniform0.uh3[1].e, _uniform0.uh3[2].e);
+  _skUnpacked__uniform0_ui = array<i32, 3>(_uniform0.ui[0].e, _uniform0.ui[1].e, _uniform0.ui[2].e);
+  _skUnpacked__uniform0_ui2 = array<vec2<i32>, 3>(_uniform0.ui2[0].e, _uniform0.ui2[1].e, _uniform0.ui2[2].e);
+  _skUnpacked__uniform0_ui3 = array<vec3<i32>, 3>(_uniform0.ui3[0].e, _uniform0.ui3[1].e, _uniform0.ui3[2].e);
+}
diff --git a/tests/sksl/wgsl/UniformMatrices.wgsl b/tests/sksl/wgsl/UniformMatrices.wgsl
index 367453d..83894cc 100644
--- a/tests/sksl/wgsl/UniformMatrices.wgsl
+++ b/tests/sksl/wgsl/UniformMatrices.wgsl
@@ -15,6 +15,15 @@
   u42: _skMatrix42,
   u43: mat4x3<f32>,
   u44: mat4x4<f32>,
+  au22: array<_skArrayElement_f22, 3>,
+  au23: array<mat2x3<f32>, 3>,
+  au24: array<mat2x4<f32>, 3>,
+  au32: array<_skArrayElement_f32, 3>,
+  au33: array<mat3x3<f32>, 3>,
+  au34: array<mat3x4<f32>, 3>,
+  au42: array<_skArrayElement_f42, 3>,
+  au43: array<mat4x3<f32>, 3>,
+  au44: array<mat4x4<f32>, 3>,
 };
 @group(0) @binding(1) var<uniform> _uniform0 : UniformBuffer;
 struct StorageBuffer {
@@ -27,27 +36,73 @@
   s42: _skMatrix42,
   s43: mat4x3<f32>,
   s44: mat4x4<f32>,
+  as22: array<_skArrayElement_f22, 3>,
+  as23: array<mat2x3<f32>, 3>,
+  as24: array<mat2x4<f32>, 3>,
+  as32: array<_skArrayElement_f32, 3>,
+  as33: array<mat3x3<f32>, 3>,
+  as34: array<mat3x4<f32>, 3>,
+  as42: array<_skArrayElement_f42, 3>,
+  as43: array<mat4x3<f32>, 3>,
+  as44: array<mat4x4<f32>, 3>,
 };
 @group(0) @binding(2) var<storage, read_write> _storage1 : StorageBuffer;
 fn main() -> vec4<f32> {
   {
-    return vec4<f32>(0.0);
+    var value: f32 = ((((((((((((((((((((((((((((((((((_skUnpacked__uniform0_u22[0].x + _uniform0.u23[0].x) + _uniform0.u24[0].x) + _skUnpacked__uniform0_u32[0].x) + _uniform0.u33[0].x) + _uniform0.u34[0].x) + _skUnpacked__uniform0_u42[0].x) + _uniform0.u43[0].x) + _uniform0.au44[0][0].x) + _skUnpacked__uniform0_au22[0][0].x) + _uniform0.au23[0][0].x) + _uniform0.au24[0][0].x) + _skUnpacked__uniform0_au32[0][0].x) + _uniform0.au33[0][0].x) + _uniform0.au34[0][0].x) + _skUnpacked__uniform0_au42[0][0].x) + _uniform0.au43[0][0].x) + _uniform0.au44[0][0].x) + _skUnpacked__storage1_s22[0].x) + _storage1.s23[0].x) + _storage1.s24[0].x) + _skUnpacked__storage1_s32[0].x) + _storage1.s33[0].x) + _storage1.s34[0].x) + _skUnpacked__storage1_s42[0].x) + _storage1.s43[0].x) + _storage1.as44[0][0].x) + _skUnpacked__storage1_as22[0][0].x) + _storage1.as23[0][0].x) + _storage1.as24[0][0].x) + _skUnpacked__storage1_as32[0][0].x) + _storage1.as33[0][0].x) + _storage1.as34[0][0].x) + _skUnpacked__storage1_as42[0][0].x) + _storage1.as43[0][0].x) + _storage1.as44[0][0].x;
+    return vec4<f32>(f32(value));
   }
 }
 @fragment fn fragmentMain(_stageIn: FSIn) -> FSOut {
+  _skInitializePolyfilledUniforms();
   var _stageOut: FSOut;
   _stageOut.sk_FragColor = main();
   return _stageOut;
 }
+struct _skArrayElement_f22 {
+  e : _skMatrix22
+};
 struct _skRow2 {
   @size(16) r : vec2<f32>
 };
 struct _skMatrix22 {
   c : array<_skRow2, 2>
 };
+var<private> _skUnpacked__storage1_as22: array<mat2x2<f32>, 3>;
+struct _skArrayElement_f32 {
+  e : _skMatrix32
+};
 struct _skMatrix32 {
   c : array<_skRow2, 3>
 };
+var<private> _skUnpacked__storage1_as32: array<mat3x2<f32>, 3>;
+struct _skArrayElement_f42 {
+  e : _skMatrix42
+};
 struct _skMatrix42 {
   c : array<_skRow2, 4>
 };
+var<private> _skUnpacked__storage1_as42: array<mat4x2<f32>, 3>;
+var<private> _skUnpacked__storage1_s22: mat2x2<f32>;
+var<private> _skUnpacked__storage1_s32: mat3x2<f32>;
+var<private> _skUnpacked__storage1_s42: mat4x2<f32>;
+var<private> _skUnpacked__uniform0_au22: array<mat2x2<f32>, 3>;
+var<private> _skUnpacked__uniform0_au32: array<mat3x2<f32>, 3>;
+var<private> _skUnpacked__uniform0_au42: array<mat4x2<f32>, 3>;
+var<private> _skUnpacked__uniform0_u22: mat2x2<f32>;
+var<private> _skUnpacked__uniform0_u32: mat3x2<f32>;
+var<private> _skUnpacked__uniform0_u42: mat4x2<f32>;
+fn _skInitializePolyfilledUniforms() {
+  _skUnpacked__storage1_as22 = array<mat2x2<f32>, 3>(mat2x2<f32>(_storage1.as22[0].e.c[0].r, _storage1.as22[0].e.c[1].r), mat2x2<f32>(_storage1.as22[1].e.c[0].r, _storage1.as22[1].e.c[1].r), mat2x2<f32>(_storage1.as22[2].e.c[0].r, _storage1.as22[2].e.c[1].r));
+  _skUnpacked__storage1_as32 = array<mat3x2<f32>, 3>(mat3x2<f32>(_storage1.as32[0].e.c[0].r, _storage1.as32[0].e.c[1].r, _storage1.as32[0].e.c[2].r), mat3x2<f32>(_storage1.as32[1].e.c[0].r, _storage1.as32[1].e.c[1].r, _storage1.as32[1].e.c[2].r), mat3x2<f32>(_storage1.as32[2].e.c[0].r, _storage1.as32[2].e.c[1].r, _storage1.as32[2].e.c[2].r));
+  _skUnpacked__storage1_as42 = array<mat4x2<f32>, 3>(mat4x2<f32>(_storage1.as42[0].e.c[0].r, _storage1.as42[0].e.c[1].r, _storage1.as42[0].e.c[2].r, _storage1.as42[0].e.c[3].r), mat4x2<f32>(_storage1.as42[1].e.c[0].r, _storage1.as42[1].e.c[1].r, _storage1.as42[1].e.c[2].r, _storage1.as42[1].e.c[3].r), mat4x2<f32>(_storage1.as42[2].e.c[0].r, _storage1.as42[2].e.c[1].r, _storage1.as42[2].e.c[2].r, _storage1.as42[2].e.c[3].r));
+  _skUnpacked__storage1_s22 = mat2x2<f32>(_storage1.s22.c[0].r, _storage1.s22.c[1].r);
+  _skUnpacked__storage1_s32 = mat3x2<f32>(_storage1.s32.c[0].r, _storage1.s32.c[1].r, _storage1.s32.c[2].r);
+  _skUnpacked__storage1_s42 = mat4x2<f32>(_storage1.s42.c[0].r, _storage1.s42.c[1].r, _storage1.s42.c[2].r, _storage1.s42.c[3].r);
+  _skUnpacked__uniform0_au22 = array<mat2x2<f32>, 3>(mat2x2<f32>(_uniform0.au22[0].e.c[0].r, _uniform0.au22[0].e.c[1].r), mat2x2<f32>(_uniform0.au22[1].e.c[0].r, _uniform0.au22[1].e.c[1].r), mat2x2<f32>(_uniform0.au22[2].e.c[0].r, _uniform0.au22[2].e.c[1].r));
+  _skUnpacked__uniform0_au32 = array<mat3x2<f32>, 3>(mat3x2<f32>(_uniform0.au32[0].e.c[0].r, _uniform0.au32[0].e.c[1].r, _uniform0.au32[0].e.c[2].r), mat3x2<f32>(_uniform0.au32[1].e.c[0].r, _uniform0.au32[1].e.c[1].r, _uniform0.au32[1].e.c[2].r), mat3x2<f32>(_uniform0.au32[2].e.c[0].r, _uniform0.au32[2].e.c[1].r, _uniform0.au32[2].e.c[2].r));
+  _skUnpacked__uniform0_au42 = array<mat4x2<f32>, 3>(mat4x2<f32>(_uniform0.au42[0].e.c[0].r, _uniform0.au42[0].e.c[1].r, _uniform0.au42[0].e.c[2].r, _uniform0.au42[0].e.c[3].r), mat4x2<f32>(_uniform0.au42[1].e.c[0].r, _uniform0.au42[1].e.c[1].r, _uniform0.au42[1].e.c[2].r, _uniform0.au42[1].e.c[3].r), mat4x2<f32>(_uniform0.au42[2].e.c[0].r, _uniform0.au42[2].e.c[1].r, _uniform0.au42[2].e.c[2].r, _uniform0.au42[2].e.c[3].r));
+  _skUnpacked__uniform0_u22 = mat2x2<f32>(_uniform0.u22.c[0].r, _uniform0.u22.c[1].r);
+  _skUnpacked__uniform0_u32 = mat3x2<f32>(_uniform0.u32.c[0].r, _uniform0.u32.c[1].r, _uniform0.u32.c[2].r);
+  _skUnpacked__uniform0_u42 = mat4x2<f32>(_uniform0.u42.c[0].r, _uniform0.u42.c[1].r, _uniform0.u42.c[2].r, _uniform0.u42.c[3].r);
+}