Add getUniformMatX support for desktop and mobile (#182117)

Adds getUniformMatX and getUniformMatXArray functions to desktop and
mobile.

Also fixes a padding issue with mat3s on Vulkan.
diff --git a/engine/src/flutter/impeller/compiler/reflector.cc b/engine/src/flutter/impeller/compiler/reflector.cc
index a506adf..eacc264 100644
--- a/engine/src/flutter/impeller/compiler/reflector.cc
+++ b/engine/src/flutter/impeller/compiler/reflector.cc
@@ -936,6 +936,29 @@
       continue;
     }
 
+    if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
+        member.width == 32 && member.columns == 3 && member.vecsize == 3) {
+      // Mat3s are packed as three vec3s with one float of padding after each.
+      // {val, val, val, padding, val, val, val, padding, val, val, val,
+      // padding}.
+      uint32_t count = array_elements.value_or(1) * 3;
+      uint32_t stride = 16;
+      uint32_t total_length = stride * count;
+
+      result.emplace_back(StructMember{
+          /*p_type=*/"Mat3",
+          /*p_base_type=*/spirv_cross::SPIRType::BaseType::Float,
+          /*p_name=*/GetMemberNameAtIndex(struct_type, i),
+          /*p_offset=*/struct_member_offset,
+          /*p_size=*/12,
+          /*p_byte_length=*/total_length,
+          /*p_array_elements=*/count,
+          /*p_element_padding=*/4,
+      });
+      current_byte_offset += total_length;
+      continue;
+    }
+
     // Tightly packed 4x4 Matrix is special cased as we know how to work with
     // those.
     if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&  //
diff --git a/engine/src/flutter/impeller/runtime_stage/runtime_stage_unittests.cc b/engine/src/flutter/impeller/runtime_stage/runtime_stage_unittests.cc
index ea47b44..36e7ad4 100644
--- a/engine/src/flutter/impeller/runtime_stage/runtime_stage_unittests.cc
+++ b/engine/src/flutter/impeller/runtime_stage/runtime_stage_unittests.cc
@@ -242,7 +242,7 @@
           stage->GetUniform(RuntimeStage::kVulkanUBOName);
       ASSERT_TRUE(uni);
       EXPECT_EQ(uni->type, RuntimeUniformType::kStruct);
-      EXPECT_EQ(uni->struct_float_count, 35u);
+      EXPECT_EQ(uni->struct_float_count, 26u);
 
       EXPECT_EQ(uni->GetGPUSize(), 640u);
       std::vector<RuntimePaddingType> layout(uni->GetGPUSize() / sizeof(float),
@@ -257,9 +257,9 @@
       layout[15] = RuntimePaddingType::kPadding;
       layout[18] = RuntimePaddingType::kPadding;
       layout[19] = RuntimePaddingType::kPadding;
-      // uMat3 is packed as 3 vec4s, with the last 3 bytes being padding
-      layout[29] = RuntimePaddingType::kPadding;
-      layout[30] = RuntimePaddingType::kPadding;
+      // uMat3 is packed as 3 vec4s, with the last byte of each being padding
+      layout[23] = RuntimePaddingType::kPadding;
+      layout[27] = RuntimePaddingType::kPadding;
       layout[31] = RuntimePaddingType::kPadding;
       // uFloatArray is packed as 2 vec4s, with the last 3 bytes of each
       // being padding.
@@ -280,7 +280,7 @@
       layout[71] = RuntimePaddingType::kPadding;
       // uVec4Array has no padding.
       // uMat2Array[2] is packed as 4 vec4s, With the last 2 bytes of each being
-      // padding. padding.
+      // padding.
       layout[82] = RuntimePaddingType::kPadding;
       layout[83] = RuntimePaddingType::kPadding;
       layout[86] = RuntimePaddingType::kPadding;
@@ -289,13 +289,13 @@
       layout[91] = RuntimePaddingType::kPadding;
       layout[94] = RuntimePaddingType::kPadding;
       layout[95] = RuntimePaddingType::kPadding;
-      // uMat3Array[2] is packed as 6 vec4s, with the last 3 bytes of the 3rd
-      // and 6th being padding.
-      layout[105] = RuntimePaddingType::kPadding;
-      layout[106] = RuntimePaddingType::kPadding;
+      // uMat3Array[2] is packed as 6 vec4s, with the last byte of each being
+      // padding.
+      layout[99] = RuntimePaddingType::kPadding;
+      layout[103] = RuntimePaddingType::kPadding;
       layout[107] = RuntimePaddingType::kPadding;
-      layout[117] = RuntimePaddingType::kPadding;
-      layout[118] = RuntimePaddingType::kPadding;
+      layout[111] = RuntimePaddingType::kPadding;
+      layout[115] = RuntimePaddingType::kPadding;
       layout[119] = RuntimePaddingType::kPadding;
       // uMat4Array[2] is packed as 8 vec4s with no padding.
       layout[152] = RuntimePaddingType::kPadding;
diff --git a/engine/src/flutter/lib/ui/fixtures/shaders/uniforms/BUILD.gn b/engine/src/flutter/lib/ui/fixtures/shaders/uniforms/BUILD.gn
index d75abef..0268468 100644
--- a/engine/src/flutter/lib/ui/fixtures/shaders/uniforms/BUILD.gn
+++ b/engine/src/flutter/lib/ui/fixtures/shaders/uniforms/BUILD.gn
@@ -17,6 +17,12 @@
     "vec3_array_uniform.frag",
     "vec4_uniform.frag",
     "vec4_array_uniform.frag",
+    "mat2_uniform.frag",
+    "mat2_array_uniform.frag",
+    "mat3_uniform.frag",
+    "mat3_array_uniform.frag",
+    "mat4_uniform.frag",
+    "mat4_array_uniform.frag",
   ]
 
   group("uniforms") {
diff --git a/engine/src/flutter/lib/ui/fixtures/shaders/uniforms/all_uniforms.frag b/engine/src/flutter/lib/ui/fixtures/shaders/uniforms/all_uniforms.frag
index 2df139c..e5d3bcc 100644
--- a/engine/src/flutter/lib/ui/fixtures/shaders/uniforms/all_uniforms.frag
+++ b/engine/src/flutter/lib/ui/fixtures/shaders/uniforms/all_uniforms.frag
@@ -7,7 +7,7 @@
 out vec4 fragColor;
 
 // One for each base type, 40 for the arrays
-float N_COLOR_VALUES = 44;
+float N_COLOR_VALUES = 143;
 
 uniform float uFloat;
 
@@ -15,6 +15,10 @@
 uniform vec3 uVec3;
 uniform vec4 uVec4;
 
+uniform mat2 uMat2;
+uniform mat3 uMat3;
+uniform mat4 uMat4;
+
 const int ARRAY_SIZE = 10;
 
 uniform float[ARRAY_SIZE] uFloatArray;
@@ -22,6 +26,10 @@
 uniform vec3[ARRAY_SIZE] uVec3Array;
 uniform vec4[ARRAY_SIZE] uVec4Array;
 
+uniform mat2[ARRAY_SIZE] uMat2Array;
+uniform mat3[ARRAY_SIZE] uMat3Array;
+uniform mat4[ARRAY_SIZE] uMat4Array;
+
 void main() {
   float u = FlutterFragCoord().x / N_COLOR_VALUES;
 
@@ -50,6 +58,30 @@
   }
   offset += increment;
 
+  for (int i = 0; i < 2; ++i) {
+    if (u < offset) {
+      fragColor = vec4(uMat2[i], 0, 1);
+      return;
+    }
+    offset += increment;
+  }
+
+  for (int i = 0; i < 3; ++i) {
+    if (u < offset) {
+      fragColor = vec4(uMat3[i], 1);
+      return;
+    }
+    offset += increment;
+  }
+
+  for (int i = 0; i < 4; ++i) {
+    if (u < offset) {
+      fragColor = uMat4[i];
+      return;
+    }
+    offset += increment;
+  }
+
   for (int i = 0; i < ARRAY_SIZE; ++i) {
     if (u < offset) {
       fragColor = vec4(uFloatArray[i], 0, 0, 1);
@@ -81,4 +113,34 @@
     }
     offset += increment;
   }
+
+  for (int i = 0; i < ARRAY_SIZE; ++i) {
+    for (int j = 0; j < 2; ++j) {
+      if (u < offset) {
+        fragColor = vec4(uMat2Array[i][j], 0, 1);
+        return;
+      }
+      offset += increment;
+    }
+  }
+
+  for (int i = 0; i < ARRAY_SIZE; ++i) {
+    for (int j = 0; j < 3; ++j) {
+      if (u < offset) {
+        fragColor = vec4(uMat3Array[i][j], 1);
+        return;
+      }
+      offset += increment;
+    }
+  }
+
+  for (int i = 0; i < ARRAY_SIZE; ++i) {
+    for (int j = 0; j < 4; ++j) {
+      if (u < offset) {
+        fragColor = uMat4Array[i][j];
+        return;
+      }
+      offset += increment;
+    }
+  }
 }
\ No newline at end of file
diff --git a/engine/src/flutter/lib/ui/fixtures/shaders/uniforms/mat2_array_uniform.frag b/engine/src/flutter/lib/ui/fixtures/shaders/uniforms/mat2_array_uniform.frag
new file mode 100644
index 0000000..25ba80c
--- /dev/null
+++ b/engine/src/flutter/lib/ui/fixtures/shaders/uniforms/mat2_array_uniform.frag
@@ -0,0 +1,23 @@
+#version 320 es
+
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <flutter/runtime_effect.glsl>
+
+precision highp float;
+
+uniform mat2[2] colors;
+
+out vec4 fragColor;
+
+void main() {
+  vec2 uv = FlutterFragCoord().xy;
+
+  if (uv.x < 1) {
+    fragColor = vec4(colors[0][0], colors[0][1]);
+  } else {
+    fragColor = vec4(colors[1][0], colors[1][1]);
+  }
+}
diff --git a/engine/src/flutter/lib/ui/fixtures/shaders/uniforms/mat2_uniform.frag b/engine/src/flutter/lib/ui/fixtures/shaders/uniforms/mat2_uniform.frag
new file mode 100644
index 0000000..9c1e879
--- /dev/null
+++ b/engine/src/flutter/lib/ui/fixtures/shaders/uniforms/mat2_uniform.frag
@@ -0,0 +1,15 @@
+#version 320 es
+
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+precision highp float;
+
+uniform mat2 color_rgba;
+
+out vec4 fragColor;
+
+void main() {
+  fragColor = vec4(color_rgba[0], color_rgba[1]);
+}
diff --git a/engine/src/flutter/lib/ui/fixtures/shaders/uniforms/mat3_array_uniform.frag b/engine/src/flutter/lib/ui/fixtures/shaders/uniforms/mat3_array_uniform.frag
new file mode 100644
index 0000000..01c5d25
--- /dev/null
+++ b/engine/src/flutter/lib/ui/fixtures/shaders/uniforms/mat3_array_uniform.frag
@@ -0,0 +1,34 @@
+#version 320 es
+
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// If updating this file, also update
+// engine/src/flutter/lib/web_ui/test/ui/fragment_shader_test.dart
+
+#include <flutter/runtime_effect.glsl>
+
+precision highp float;
+
+uniform mat3[] colors;
+
+out vec4 fragColor;
+
+void main() {
+  vec2 uv = FlutterFragCoord().xy;
+
+  if (uv.x < 1) {
+    fragColor = vec4(colors[0][0], 1);
+  } else if (uv.x < 2) {
+    fragColor = vec4(colors[0][1], 1);
+  } else if (uv.x < 3) {
+    fragColor = vec4(colors[0][2], 1);
+  } else if (uv.x < 4) {
+    fragColor = vec4(colors[1][0], 1);
+  } else if (uv.x < 5) {
+    fragColor = vec4(colors[1][1], 1);
+  } else if (uv.x < 6) {
+    fragColor = vec4(colors[1][2], 1);
+  }
+}
diff --git a/engine/src/flutter/lib/ui/fixtures/shaders/uniforms/mat3_uniform.frag b/engine/src/flutter/lib/ui/fixtures/shaders/uniforms/mat3_uniform.frag
new file mode 100644
index 0000000..aeb77ab
--- /dev/null
+++ b/engine/src/flutter/lib/ui/fixtures/shaders/uniforms/mat3_uniform.frag
@@ -0,0 +1,28 @@
+#version 320 es
+
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// If updating this file, also update
+// engine/src/flutter/lib/web_ui/test/ui/fragment_shader_test.dart
+
+#include <flutter/runtime_effect.glsl>
+
+precision highp float;
+
+uniform mat3 colors;
+
+out vec4 fragColor;
+
+void main() {
+  vec2 uv = FlutterFragCoord().xy / 3.0;
+
+  if (uv.x < 0.3) {
+    fragColor = vec4(colors[0], 1);
+  } else if (uv.x < 0.6) {
+    fragColor = vec4(colors[1], 1);
+  } else {
+    fragColor = vec4(colors[2], 1);
+  }
+}
diff --git a/engine/src/flutter/lib/ui/fixtures/shaders/uniforms/mat4_array_uniform.frag b/engine/src/flutter/lib/ui/fixtures/shaders/uniforms/mat4_array_uniform.frag
new file mode 100644
index 0000000..2de0560
--- /dev/null
+++ b/engine/src/flutter/lib/ui/fixtures/shaders/uniforms/mat4_array_uniform.frag
@@ -0,0 +1,38 @@
+#version 320 es
+
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// If updating this file, also update
+// engine/src/flutter/lib/web_ui/test/ui/fragment_shader_test.dart
+
+#include <flutter/runtime_effect.glsl>
+
+precision highp float;
+
+uniform mat4[] colors;
+
+out vec4 fragColor;
+
+void main() {
+  vec2 uv = FlutterFragCoord().xy;
+
+  if (uv.x < 1) {
+    fragColor = colors[0][0];
+  } else if (uv.x < 2) {
+    fragColor = colors[0][1];
+  } else if (uv.x < 3) {
+    fragColor = colors[0][2];
+  } else if (uv.x < 4) {
+    fragColor = colors[0][3];
+  } else if (uv.x < 5) {
+    fragColor = colors[1][0];
+  } else if (uv.x < 6) {
+    fragColor = colors[1][1];
+  } else if (uv.x < 7) {
+    fragColor = colors[1][2];
+  } else if (uv.x < 8) {
+    fragColor = colors[1][3];
+  }
+}
diff --git a/engine/src/flutter/lib/ui/fixtures/shaders/uniforms/mat4_uniform.frag b/engine/src/flutter/lib/ui/fixtures/shaders/uniforms/mat4_uniform.frag
new file mode 100644
index 0000000..2bff25c
--- /dev/null
+++ b/engine/src/flutter/lib/ui/fixtures/shaders/uniforms/mat4_uniform.frag
@@ -0,0 +1,30 @@
+#version 320 es
+
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// If updating this file, also update
+// engine/src/flutter/lib/web_ui/test/ui/fragment_shader_test.dart
+
+#include <flutter/runtime_effect.glsl>
+
+precision highp float;
+
+uniform mat4 colors;
+
+out vec4 fragColor;
+
+void main() {
+  vec2 uv = FlutterFragCoord().xy / 4.0;
+
+  if (uv.x < 0.25) {
+    fragColor = colors[0];
+  } else if (uv.x < 0.5) {
+    fragColor = colors[1];
+  } else if (uv.x < 0.75) {
+    fragColor = colors[2];
+  } else {
+    fragColor = colors[3];
+  }
+}
diff --git a/engine/src/flutter/lib/ui/painting.dart b/engine/src/flutter/lib/ui/painting.dart
index 6976efd..bfee6f9 100644
--- a/engine/src/flutter/lib/ui/painting.dart
+++ b/engine/src/flutter/lib/ui/painting.dart
@@ -5724,6 +5724,189 @@
   final UniformFloatSlot _xSlot, _ySlot, _zSlot, _wSlot;
 }
 
+/// A binding to a uniform of type mat2. Calling [set] on this object updates
+/// the uniform's value.
+///
+/// Example:
+///
+/// ```dart
+/// void updateShader(ui.FragmentShader shader) {
+///   shader.getUniformMat2('uIdentity').set(
+///     1.0, 0.0,
+///     0.0, 1.0
+///   );
+/// }
+/// ```
+///
+/// See also:
+///   [FragmentShader.getUniformMat2] - How [UniformMat2Slot] instances are acquired.
+///
+base class UniformMat2Slot extends UniformType {
+  UniformMat2Slot._(this._m00, this._m01, this._m10, this._m11);
+
+  /// Set the float value of the matrix in row-major order.
+  void set(double m00, double m01, double m10, double m11) {
+    _m00.set(m00);
+    _m01.set(m01);
+    _m10.set(m10);
+    _m11.set(m11);
+  }
+
+  final UniformFloatSlot _m00, _m01, _m10, _m11;
+}
+
+/// A binding to a uniform of type mat3. Calling [set] on this object updates
+/// the uniform's value.
+///
+/// Example:
+///
+/// ```dart
+/// void updateShader(ui.FragmentShader shader) {
+///   shader.getUniformMat3('uIdentity').set(
+///     1.0, 0.0, 0.0,
+///     0.0, 1.0, 0.0,
+///     0.0, 0.0, 1.0
+///   );
+/// }
+/// ```
+///
+/// See also:
+///   [FragmentShader.getUniformMat3] - How [UniformMat3Slot] instances are acquired.
+///
+base class UniformMat3Slot extends UniformType {
+  UniformMat3Slot._(
+    this._m00,
+    this._m01,
+    this._m02,
+    this._m10,
+    this._m11,
+    this._m12,
+    this._m20,
+    this._m21,
+    this._m22,
+  );
+
+  /// Set the float value of the matrix in row-major order.
+  void set(
+    double m00,
+    double m01,
+    double m02,
+    double m10,
+    double m11,
+    double m12,
+    double m20,
+    double m21,
+    double m22,
+  ) {
+    _m00.set(m00);
+    _m01.set(m01);
+    _m02.set(m02);
+    _m10.set(m10);
+    _m11.set(m11);
+    _m12.set(m12);
+    _m20.set(m20);
+    _m21.set(m21);
+    _m22.set(m22);
+  }
+
+  final UniformFloatSlot _m00, _m01, _m02, _m10, _m11, _m12, _m20, _m21, _m22;
+}
+
+/// A binding to a uniform of type mat4. Calling [set] on this object updates
+/// the uniform's value.
+///
+/// Example:
+///
+/// ```dart
+/// void updateShader(ui.FragmentShader shader) {
+///   shader.getUniformMat4('uIdentity').set(
+///     1.0, 0.0, 0.0, 0.0,
+///     0.0, 1.0, 0.0, 0.0,
+///     0.0, 0.0, 1.0, 0.0,
+///     0.0, 0.0, 0.0, 1.0
+///   );
+/// }
+/// ```
+///
+/// See also:
+///   [FragmentShader.getUniformMat4] - How [UniformMat4Slot] instances are acquired.
+///
+
+base class UniformMat4Slot extends UniformType {
+  UniformMat4Slot._(
+    this._m00,
+    this._m01,
+    this._m02,
+    this._m03,
+    this._m10,
+    this._m11,
+    this._m12,
+    this._m13,
+    this._m20,
+    this._m21,
+    this._m22,
+    this._m23,
+    this._m30,
+    this._m31,
+    this._m32,
+    this._m33,
+  );
+
+  /// Set the float value of the matrix in row-major order.
+  void set(
+    double m00,
+    double m01,
+    double m02,
+    double m03,
+    double m10,
+    double m11,
+    double m12,
+    double m13,
+    double m20,
+    double m21,
+    double m22,
+    double m23,
+    double m30,
+    double m31,
+    double m32,
+    double m33,
+  ) {
+    _m00.set(m00);
+    _m01.set(m01);
+    _m02.set(m02);
+    _m03.set(m03);
+    _m10.set(m10);
+    _m11.set(m11);
+    _m12.set(m12);
+    _m13.set(m13);
+    _m20.set(m20);
+    _m21.set(m21);
+    _m22.set(m22);
+    _m23.set(m23);
+    _m30.set(m30);
+    _m31.set(m31);
+    _m32.set(m32);
+    _m33.set(m33);
+  }
+
+  final UniformFloatSlot _m00,
+      _m01,
+      _m02,
+      _m03,
+      _m10,
+      _m11,
+      _m12,
+      _m13,
+      _m20,
+      _m21,
+      _m22,
+      _m23,
+      _m30,
+      _m31,
+      _m32,
+      _m33;
+}
+
 /// An array of bindings to uniforms of the same type T. Access elements via [] and
 /// set them individually.
 /// Example:
@@ -5964,6 +6147,87 @@
     return UniformVec4Slot._(slots[0], slots[1], slots[2], slots[3]);
   }
 
+  /// Access the float binding for a mat2 uniform named [name].
+  ///
+  /// Example:
+  ///
+  /// ```glsl
+  /// uniform mat2 uIdentity;
+  /// ```
+  ///
+  /// ```dart
+  /// void updateShader(ui.FragmentShader shader) {
+  ///   shader.getUniformMat2('uIdentity');
+  /// }
+  /// ```
+  UniformMat2Slot getUniformMat2(String name) {
+    final List<UniformFloatSlot> slots = _getSlotsForUniform(name, 4);
+    return UniformMat2Slot._(slots[0], slots[1], slots[2], slots[3]);
+  }
+
+  /// Access the float binding for a mat3 uniform named [name].
+  ///
+  /// Example:
+  ///
+  /// ```glsl
+  /// uniform mat3 uIdentity;
+  /// ```
+  ///
+  /// ```dart
+  /// void updateShader(ui.FragmentShader shader) {
+  ///   shader.getUniformMat3('uIdentity');
+  /// }
+  /// ```
+  UniformMat3Slot getUniformMat3(String name) {
+    final List<UniformFloatSlot> slots = _getSlotsForUniform(name, 9);
+    return UniformMat3Slot._(
+      slots[0],
+      slots[1],
+      slots[2],
+      slots[3],
+      slots[4],
+      slots[5],
+      slots[6],
+      slots[7],
+      slots[8],
+    );
+  }
+
+  /// Access the float binding for a mat4 uniform named [name].
+  ///
+  /// Example:
+  ///
+  /// ```glsl
+  /// uniform mat4 uIdentity;
+  /// ```
+  ///
+  /// ```dart
+  /// void updateShader(ui.FragmentShader shader) {
+  ///   shader.getUniformMat4('uIdentity');
+  /// }
+  /// ```
+  UniformMat4Slot getUniformMat4(String name) {
+    final List<UniformFloatSlot> slots = _getSlotsForUniform(name, 16);
+    return UniformMat4Slot._(
+      slots[0],
+      slots[1],
+      slots[2],
+      slots[3],
+      slots[4],
+      slots[5],
+      slots[6],
+      slots[7],
+      slots[8],
+      slots[9],
+      slots[10],
+      slots[11],
+      slots[12],
+      slots[13],
+      slots[14],
+      slots[15],
+    );
+  }
+
   UniformArray<T> _getUniformArray<T extends UniformType>(
     String name,
     int elementSize,
@@ -6084,6 +6348,111 @@
     );
   }
 
+  /// Access the binding for a mat2[] uniform named [name].
+  ///
+  /// Example:
+  ///
+  /// ```glsl
+  /// uniform mat2[10] uMatricies;
+  /// ```
+  ///
+  /// ```dart
+  /// void updateShader(ui.FragmentShader shader) {
+  ///   final ui.UniformArray<ui.UniformMat2Slot> mats = shader.getUniformMat2Array('uMatricies');
+  ///   mats[0].set(
+  ///     1.0, 0.0,
+  ///     1.0, 0.5
+  ///   );
+  /// }
+  /// ```
+  UniformArray<UniformMat2Slot> getUniformMat2Array(String name) {
+    return _getUniformArray<UniformMat2Slot>(
+      name,
+      4,
+      (components) => UniformMat2Slot._(components[0], components[1], components[2], components[3]),
+    );
+  }
+
+  /// Access the binding for a mat3[] uniform named [name].
+  ///
+  /// Example:
+  ///
+  /// ```glsl
+  /// uniform mat3[10] uMatricies;
+  /// ```
+  ///
+  /// ```dart
+  /// void updateShader(ui.FragmentShader shader) {
+  ///   final ui.UniformArray<ui.UniformMat3Slot> mats = shader.getUniformMat3Array('uMatricies');
+  ///   mats[0].set(
+  ///     1.0, 0.0, 0.0,
+  ///     1.0, 0.5, 0.0,
+  ///     1.0, 0.3, 1.2
+  ///   );
+  /// }
+  /// ```
+  UniformArray<UniformMat3Slot> getUniformMat3Array(String name) {
+    return _getUniformArray<UniformMat3Slot>(
+      name,
+      9,
+      (components) => UniformMat3Slot._(
+        components[0],
+        components[1],
+        components[2],
+        components[3],
+        components[4],
+        components[5],
+        components[6],
+        components[7],
+        components[8],
+      ),
+    );
+  }
+
+  /// Access the binding for a mat4[] uniform named [name].
+  ///
+  /// Example:
+  ///
+  /// ```glsl
+  /// uniform mat4[10] uMatricies;
+  /// ```
+  ///
+  /// ```dart
+  /// void updateShader(ui.FragmentShader shader) {
+  ///   final ui.UniformArray<ui.UniformMat4Slot> mats = shader.getUniformMat4Array('uMatricies');
+  ///   mats[0].set(
+  ///     1.0, 0.0, 0.0, 1.0,
+  ///     1.0, 0.5, 0.0, 0.4,
+  ///     1.0, 0.3, 1.2, 0.2,
+  ///     0.0, 0.0, 1.0, 0.3,
+  ///   );
+  /// }
+  /// ```
+  UniformArray<UniformMat4Slot> getUniformMat4Array(String name) {
+    return _getUniformArray<UniformMat4Slot>(
+      name,
+      16, // 4 floats per element
+      (components) => UniformMat4Slot._(
+        components[0],
+        components[1],
+        components[2],
+        components[3],
+        components[4],
+        components[5],
+        components[6],
+        components[7],
+        components[8],
+        components[9],
+        components[10],
+        components[11],
+        components[12],
+        components[13],
+        components[14],
+        components[15],
+      ),
+    );
+  }
+
   /// Access the [ImageSamplerSlot] binding associated with the sampler named
   /// [name].
   ///
diff --git a/engine/src/flutter/lib/web_ui/lib/painting.dart b/engine/src/flutter/lib/web_ui/lib/painting.dart
index 6f1572e..4770584 100644
--- a/engine/src/flutter/lib/web_ui/lib/painting.dart
+++ b/engine/src/flutter/lib/web_ui/lib/painting.dart
@@ -1127,6 +1127,45 @@
   void set(double x, double y, double z, double w);
 }
 
+abstract class UniformMat2Slot extends UniformType {
+  void set(double m00, double m01, double m10, double m11);
+}
+
+abstract class UniformMat3Slot extends UniformType {
+  void set(
+    double m00,
+    double m01,
+    double m02,
+    double m10,
+    double m11,
+    double m12,
+    double m20,
+    double m21,
+    double m22,
+  );
+}
+
+abstract class UniformMat4Slot extends UniformType {
+  void set(
+    double m00,
+    double m01,
+    double m02,
+    double m03,
+    double m10,
+    double m11,
+    double m12,
+    double m13,
+    double m20,
+    double m21,
+    double m22,
+    double m23,
+    double m30,
+    double m31,
+    double m32,
+    double m33,
+  );
+}
+
 abstract class UniformArray<T extends UniformType> {
   T operator [](int index);
   int get length;
@@ -1157,6 +1196,12 @@
 
   UniformVec4Slot getUniformVec4(String name);
 
+  UniformMat2Slot getUniformMat2(String name);
+
+  UniformMat3Slot getUniformMat3(String name);
+
+  UniformMat4Slot getUniformMat4(String name);
+
   UniformArray<UniformFloatSlot> getUniformFloatArray(String name);
 
   UniformArray<UniformVec2Slot> getUniformVec2Array(String name);
@@ -1165,5 +1210,11 @@
 
   UniformArray<UniformVec4Slot> getUniformVec4Array(String name);
 
+  UniformArray<UniformMat2Slot> getUniformMat2Array(String name);
+
+  UniformArray<UniformMat3Slot> getUniformMat3Array(String name);
+
+  UniformArray<UniformMat4Slot> getUniformMat4Array(String name);
+
   ImageSamplerSlot getImageSampler(String name);
 }
diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/painting.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/painting.dart
index f93f996..f0a5726 100644
--- a/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/painting.dart
+++ b/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/painting.dart
@@ -511,6 +511,36 @@
   }
 
   @override
+  ui.UniformMat2Slot getUniformMat2(String name) {
+    throw UnsupportedError('getUniformMat2 is not supported on the web.');
+  }
+
+  @override
+  ui.UniformMat3Slot getUniformMat3(String name) {
+    throw UnsupportedError('getUniformMat3 is not supported on the web.');
+  }
+
+  @override
+  ui.UniformMat4Slot getUniformMat4(String name) {
+    throw UnsupportedError('getUniformMat4 is not supported on the web.');
+  }
+
+  @override
+  ui.UniformArray<ui.UniformMat2Slot> getUniformMat2Array(String name) {
+    throw UnsupportedError('getUniformMat2Array is not supported on the web.');
+  }
+
+  @override
+  ui.UniformArray<ui.UniformMat3Slot> getUniformMat3Array(String name) {
+    throw UnsupportedError('getUniformMat3Array is not supported on the web.');
+  }
+
+  @override
+  ui.UniformArray<ui.UniformMat4Slot> getUniformMat4Array(String name) {
+    throw UnsupportedError('getUniformMat4Array is not supported on the web.');
+  }
+
+  @override
   ui.ImageSamplerSlot getImageSampler(String name) {
     throw UnsupportedError('getImageSampler is not supported on the web.');
   }
diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/shaders.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/shaders.dart
index 1efb091..81e5cd54 100644
--- a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/shaders.dart
+++ b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/shaders.dart
@@ -398,6 +398,36 @@
   }
 
   @override
+  ui.UniformMat2Slot getUniformMat2(String name) {
+    throw UnsupportedError('getUniformMat2 is not supported on the web.');
+  }
+
+  @override
+  ui.UniformMat3Slot getUniformMat3(String name) {
+    throw UnsupportedError('getUniformMat3 is not supported on the web.');
+  }
+
+  @override
+  ui.UniformMat4Slot getUniformMat4(String name) {
+    throw UnsupportedError('getUniformMat4 is not supported on the web.');
+  }
+
+  @override
+  ui.UniformArray<ui.UniformMat2Slot> getUniformMat2Array(String name) {
+    throw UnsupportedError('getUniformMat2Array is not supported on the web.');
+  }
+
+  @override
+  ui.UniformArray<ui.UniformMat3Slot> getUniformMat3Array(String name) {
+    throw UnsupportedError('getUniformMat3Array is not supported on the web.');
+  }
+
+  @override
+  ui.UniformArray<ui.UniformMat4Slot> getUniformMat4Array(String name) {
+    throw UnsupportedError('getUniformMat4Array is not supported on the web.');
+  }
+
+  @override
   ui.ImageSamplerSlot getImageSampler(String name) {
     throw UnsupportedError('getImageSampler is not supported on the web.');
   }
diff --git a/engine/src/flutter/testing/dart/fragment_shader_test.dart b/engine/src/flutter/testing/dart/fragment_shader_test.dart
index 8c85b2e..12550ab 100644
--- a/engine/src/flutter/testing/dart/fragment_shader_test.dart
+++ b/engine/src/flutter/testing/dart/fragment_shader_test.dart
@@ -94,6 +94,15 @@
         UniformVec4Slot: (await FragmentProgram.fromAsset(
           'vec4_uniform.frag.iplr',
         )).fragmentShader(),
+        UniformMat2Slot: (await FragmentProgram.fromAsset(
+          'mat2_uniform.frag.iplr',
+        )).fragmentShader(),
+        UniformMat3Slot: (await FragmentProgram.fromAsset(
+          'mat3_uniform.frag.iplr',
+        )).fragmentShader(),
+        UniformMat4Slot: (await FragmentProgram.fromAsset(
+          'mat4_uniform.frag.iplr',
+        )).fragmentShader(),
         UniformArray<UniformFloatSlot>: (await FragmentProgram.fromAsset(
           'float_array_uniform.frag.iplr',
         )).fragmentShader(),
@@ -106,6 +115,15 @@
         UniformArray<UniformVec4Slot>: (await FragmentProgram.fromAsset(
           'vec4_array_uniform.frag.iplr',
         )).fragmentShader(),
+        UniformArray<UniformMat2Slot>: (await FragmentProgram.fromAsset(
+          'mat2_array_uniform.frag.iplr',
+        )).fragmentShader(),
+        UniformArray<UniformMat3Slot>: (await FragmentProgram.fromAsset(
+          'mat3_array_uniform.frag.iplr',
+        )).fragmentShader(),
+        UniformArray<UniformMat4Slot>: (await FragmentProgram.fromAsset(
+          'mat4_array_uniform.frag.iplr',
+        )).fragmentShader(),
       };
     });
 
@@ -250,6 +268,177 @@
       });
     });
 
+    group('mat2', () {
+      test('set using setFloat', () async {
+        const color = Color.fromARGB(255, 67, 42, 12);
+        final FragmentShader shader = shaderMap[UniformMat2Slot]!;
+        shader.setFloat(0, color.r);
+        shader.setFloat(1, color.g);
+        shader.setFloat(2, color.b);
+        shader.setFloat(3, color.a);
+        _expectShaderRendersColor(shader, color);
+      });
+
+      test('set using getUniformMat2', () async {
+        const color = Color.fromARGB(255, 12, 37, 27);
+        final FragmentShader shader = shaderMap[UniformMat2Slot]!;
+        shader.getUniformMat2('color_rgba').set(color.r, color.g, color.b, color.a);
+        _expectShaderRendersColor(shader, color);
+      });
+
+      test('wrong datatype', () async {
+        final FragmentShader shader = shaderMap[UniformVec3Slot]!;
+        expect(
+          () => shader.getUniformMat2('color_rgb'),
+          throwsA(
+            isA<ArgumentError>().having(
+              (e) => e.message,
+              'message',
+              contains('`color_rgb` has size 3, not size 4.'),
+            ),
+          ),
+        );
+      });
+    });
+
+    group('mat3', () {
+      test('set using setFloat', () async {
+        const cpuColors = [
+          Color.fromARGB(255, 67, 42, 12),
+          Color.fromARGB(255, 11, 22, 96),
+          Color.fromARGB(255, 8, 16, 67),
+        ];
+        final FragmentShader shader = shaderMap[UniformMat3Slot]!;
+        shader.setFloat(0, cpuColors[0].r);
+        shader.setFloat(1, cpuColors[0].g);
+        shader.setFloat(2, cpuColors[0].b);
+        shader.setFloat(3, cpuColors[1].r);
+        shader.setFloat(4, cpuColors[1].g);
+        shader.setFloat(5, cpuColors[1].b);
+        shader.setFloat(6, cpuColors[2].r);
+        shader.setFloat(7, cpuColors[2].g);
+        shader.setFloat(8, cpuColors[2].b);
+        _expectShaderRendersBarcode(shader, cpuColors);
+      });
+
+      test('set using getUniformMat3', () async {
+        const cpuColors = [
+          Color.fromARGB(255, 11, 22, 96),
+          Color.fromARGB(255, 8, 16, 67),
+          Color.fromARGB(255, 67, 42, 12),
+        ];
+        final FragmentShader shader = shaderMap[UniformMat3Slot]!;
+        final UniformMat3Slot gpuColors = shader.getUniformMat3('colors');
+        gpuColors.set(
+          cpuColors[0].r,
+          cpuColors[0].g,
+          cpuColors[0].b,
+
+          cpuColors[1].r,
+          cpuColors[1].g,
+          cpuColors[1].b,
+
+          cpuColors[2].r,
+          cpuColors[2].g,
+          cpuColors[2].b,
+        );
+        _expectShaderRendersBarcode(shader, cpuColors);
+      });
+
+      test('wrong datatype', () async {
+        final FragmentShader shader = shaderMap[UniformVec3Slot]!;
+        expect(
+          () => shader.getUniformMat3('color_rgb'),
+          throwsA(
+            isA<ArgumentError>().having(
+              (e) => e.message,
+              'message',
+              contains('Uniform `color_rgb` has size 3, not size 9.'),
+            ),
+          ),
+        );
+      });
+    });
+
+    group('mat4', () {
+      test('set using setFloat', () async {
+        const cpuColors = [
+          Color.fromARGB(6, 67, 42, 12),
+          Color.fromARGB(33, 11, 22, 96),
+          Color.fromARGB(99, 8, 16, 67),
+          Color.fromARGB(120, 11, 22, 96),
+        ];
+        final FragmentShader shader = shaderMap[UniformMat4Slot]!;
+        shader.setFloat(0, cpuColors[0].r);
+        shader.setFloat(1, cpuColors[0].g);
+        shader.setFloat(2, cpuColors[0].b);
+        shader.setFloat(3, cpuColors[0].a);
+
+        shader.setFloat(4, cpuColors[1].r);
+        shader.setFloat(5, cpuColors[1].g);
+        shader.setFloat(6, cpuColors[1].b);
+        shader.setFloat(7, cpuColors[1].a);
+
+        shader.setFloat(8, cpuColors[2].r);
+        shader.setFloat(9, cpuColors[2].g);
+        shader.setFloat(10, cpuColors[2].b);
+        shader.setFloat(11, cpuColors[2].a);
+
+        shader.setFloat(12, cpuColors[3].r);
+        shader.setFloat(13, cpuColors[3].g);
+        shader.setFloat(14, cpuColors[3].b);
+        shader.setFloat(15, cpuColors[3].a);
+        _expectShaderRendersBarcode(shader, cpuColors);
+      });
+
+      test('set using getUniformMat4', () async {
+        const cpuColors = [
+          Color.fromARGB(78, 11, 22, 96),
+          Color.fromARGB(255, 8, 16, 67),
+          Color.fromARGB(99, 11, 22, 96),
+          Color.fromARGB(46, 67, 42, 12),
+        ];
+        final FragmentShader shader = shaderMap[UniformMat4Slot]!;
+        final UniformMat4Slot gpuColors = shader.getUniformMat4('colors');
+        gpuColors.set(
+          cpuColors[0].r,
+          cpuColors[0].g,
+          cpuColors[0].b,
+          cpuColors[0].a,
+
+          cpuColors[1].r,
+          cpuColors[1].g,
+          cpuColors[1].b,
+          cpuColors[1].a,
+
+          cpuColors[2].r,
+          cpuColors[2].g,
+          cpuColors[2].b,
+          cpuColors[2].a,
+
+          cpuColors[3].r,
+          cpuColors[3].g,
+          cpuColors[3].b,
+          cpuColors[3].a,
+        );
+        _expectShaderRendersBarcode(shader, cpuColors);
+      });
+
+      test('wrong datatype', () async {
+        final FragmentShader shader = shaderMap[UniformVec3Slot]!;
+        expect(
+          () => shader.getUniformMat4('color_rgb'),
+          throwsA(
+            isA<ArgumentError>().having(
+              (e) => e.message,
+              'message',
+              contains('Uniform `color_rgb` has size 3, not size 16.'),
+            ),
+          ),
+        );
+      });
+    });
+
     group('float array', () {
       test('set using setFloat', () {
         const color = Color.fromARGB(255, 11, 22, 96);
@@ -391,6 +580,265 @@
       });
     });
 
+    group('mat2 array', () {
+      test('set using setFloat', () async {
+        const cpuColors = [Color.fromARGB(77, 67, 42, 12), Color.fromARGB(51, 11, 22, 96)];
+        final FragmentShader shader = shaderMap[UniformArray<UniformMat2Slot>]!;
+        shader.setFloat(0, cpuColors[0].r);
+        shader.setFloat(1, cpuColors[0].g);
+        shader.setFloat(2, cpuColors[0].b);
+        shader.setFloat(3, cpuColors[0].a);
+        shader.setFloat(4, cpuColors[1].r);
+        shader.setFloat(5, cpuColors[1].g);
+        shader.setFloat(6, cpuColors[1].b);
+        shader.setFloat(7, cpuColors[1].a);
+        _expectShaderRendersBarcode(shader, cpuColors);
+      });
+
+      test('set using getUniformMat2', () async {
+        const cpuColors = [Color.fromARGB(51, 11, 22, 96), Color.fromARGB(77, 67, 42, 12)];
+        final FragmentShader shader = shaderMap[UniformArray<UniformMat2Slot>]!;
+        final UniformArray<UniformMat2Slot> colors = shader.getUniformMat2Array('colors');
+        colors[0].set(cpuColors[0].r, cpuColors[0].g, cpuColors[0].b, cpuColors[0].a);
+        colors[1].set(cpuColors[1].r, cpuColors[1].g, cpuColors[1].b, cpuColors[1].a);
+        _expectShaderRendersBarcode(shader, cpuColors);
+      });
+
+      test('wrong datatype', () async {
+        final FragmentShader shader = shaderMap[UniformVec3Slot]!;
+        expect(
+          () => shader.getUniformMat2Array('color_rgb'),
+          throwsA(
+            isA<ArgumentError>().having(
+              (e) => e.message,
+              'message',
+              contains('Uniform size (3) for "color_rgb" is not a multiple of 4.'),
+            ),
+          ),
+        );
+      });
+    });
+
+    group('mat4 array', () {
+      test('set using setFloat', () async {
+        const cpuColors = [
+          Color.fromARGB(31, 8, 16, 67),
+          Color.fromARGB(29, 11, 22, 96),
+          Color.fromARGB(43, 32, 34, 36),
+          Color.fromARGB(41, 26, 28, 30),
+          Color.fromARGB(39, 20, 22, 24),
+          Color.fromARGB(37, 14, 16, 18),
+          Color.fromARGB(35, 8, 10, 12),
+          Color.fromARGB(33, 2, 4, 6),
+        ];
+        final FragmentShader shader = shaderMap[UniformArray<UniformMat4Slot>]!;
+        shader.setFloat(0, cpuColors[0].r);
+        shader.setFloat(1, cpuColors[0].g);
+        shader.setFloat(2, cpuColors[0].b);
+        shader.setFloat(3, cpuColors[0].a);
+
+        shader.setFloat(4, cpuColors[1].r);
+        shader.setFloat(5, cpuColors[1].g);
+        shader.setFloat(6, cpuColors[1].b);
+        shader.setFloat(7, cpuColors[1].a);
+
+        shader.setFloat(8, cpuColors[2].r);
+        shader.setFloat(9, cpuColors[2].g);
+        shader.setFloat(10, cpuColors[2].b);
+        shader.setFloat(11, cpuColors[2].a);
+
+        shader.setFloat(12, cpuColors[3].r);
+        shader.setFloat(13, cpuColors[3].g);
+        shader.setFloat(14, cpuColors[3].b);
+        shader.setFloat(15, cpuColors[3].a);
+
+        shader.setFloat(16, cpuColors[4].r);
+        shader.setFloat(17, cpuColors[4].g);
+        shader.setFloat(18, cpuColors[4].b);
+        shader.setFloat(19, cpuColors[4].a);
+
+        shader.setFloat(20, cpuColors[5].r);
+        shader.setFloat(21, cpuColors[5].g);
+        shader.setFloat(22, cpuColors[5].b);
+        shader.setFloat(23, cpuColors[5].a);
+
+        shader.setFloat(24, cpuColors[6].r);
+        shader.setFloat(25, cpuColors[6].g);
+        shader.setFloat(26, cpuColors[6].b);
+        shader.setFloat(27, cpuColors[6].a);
+
+        shader.setFloat(28, cpuColors[7].r);
+        shader.setFloat(29, cpuColors[7].g);
+        shader.setFloat(30, cpuColors[7].b);
+        shader.setFloat(31, cpuColors[7].a);
+
+        await _expectShaderRendersBarcode(shader, cpuColors);
+      });
+
+      test('set using getUniformMat4Array', () async {
+        const cpuColors = [
+          Color.fromARGB(29, 11, 22, 96),
+          Color.fromARGB(31, 8, 16, 67),
+          Color.fromARGB(33, 2, 4, 6),
+          Color.fromARGB(35, 8, 10, 12),
+          Color.fromARGB(37, 14, 16, 18),
+          Color.fromARGB(39, 20, 22, 24),
+          Color.fromARGB(41, 26, 28, 30),
+          Color.fromARGB(43, 32, 34, 36),
+        ];
+        final FragmentShader shader = shaderMap[UniformArray<UniformMat4Slot>]!;
+        final UniformArray<UniformMat4Slot> colors = shader.getUniformMat4Array('colors');
+        colors[0].set(
+          cpuColors[0].r,
+          cpuColors[0].g,
+          cpuColors[0].b,
+          cpuColors[0].a,
+
+          cpuColors[1].r,
+          cpuColors[1].g,
+          cpuColors[1].b,
+          cpuColors[1].a,
+
+          cpuColors[2].r,
+          cpuColors[2].g,
+          cpuColors[2].b,
+          cpuColors[2].a,
+
+          cpuColors[3].r,
+          cpuColors[3].g,
+          cpuColors[3].b,
+          cpuColors[3].a,
+        );
+
+        colors[1].set(
+          cpuColors[4].r,
+          cpuColors[4].g,
+          cpuColors[4].b,
+          cpuColors[4].a,
+
+          cpuColors[5].r,
+          cpuColors[5].g,
+          cpuColors[5].b,
+          cpuColors[5].a,
+
+          cpuColors[6].r,
+          cpuColors[6].g,
+          cpuColors[6].b,
+          cpuColors[6].a,
+
+          cpuColors[7].r,
+          cpuColors[7].g,
+          cpuColors[7].b,
+          cpuColors[7].a,
+        );
+        await _expectShaderRendersBarcode(shader, cpuColors);
+      });
+
+      test('wrong datatype', () async {
+        final FragmentShader shader = shaderMap[UniformVec3Slot]!;
+        expect(
+          () => shader.getUniformMat4Array('color_rgb'),
+          throwsA(
+            isA<ArgumentError>().having(
+              (e) => e.message,
+              'message',
+              contains('Uniform size (3) for "color_rgb" is not a multiple of 16.'),
+            ),
+          ),
+        );
+      });
+    });
+
+    group('mat3 array', () {
+      test('set using setFloat', () async {
+        const cpuColors = [
+          Color.fromARGB(255, 67, 42, 12),
+          Color.fromARGB(255, 11, 22, 96),
+          Color.fromARGB(255, 2, 4, 6),
+          Color.fromARGB(255, 8, 10, 12),
+          Color.fromARGB(255, 14, 16, 18),
+          Color.fromARGB(255, 20, 22, 24),
+        ];
+        final FragmentShader shader = shaderMap[UniformArray<UniformMat3Slot>]!;
+        shader.setFloat(0, cpuColors[0].r);
+        shader.setFloat(1, cpuColors[0].g);
+        shader.setFloat(2, cpuColors[0].b);
+
+        shader.setFloat(3, cpuColors[1].r);
+        shader.setFloat(4, cpuColors[1].g);
+        shader.setFloat(5, cpuColors[1].b);
+
+        shader.setFloat(6, cpuColors[2].r);
+        shader.setFloat(7, cpuColors[2].g);
+        shader.setFloat(8, cpuColors[2].b);
+
+        shader.setFloat(9, cpuColors[3].r);
+        shader.setFloat(10, cpuColors[3].g);
+        shader.setFloat(11, cpuColors[3].b);
+
+        shader.setFloat(12, cpuColors[4].r);
+        shader.setFloat(13, cpuColors[4].g);
+        shader.setFloat(14, cpuColors[4].b);
+
+        shader.setFloat(15, cpuColors[5].r);
+        shader.setFloat(16, cpuColors[5].g);
+        shader.setFloat(17, cpuColors[5].b);
+
+        await _expectShaderRendersBarcode(shader, cpuColors);
+      });
+
+      test('set using getUniformMat3Array', () async {
+        const cpuColors = [
+          Color.fromARGB(255, 67, 42, 12),
+          Color.fromARGB(255, 11, 22, 96),
+          Color.fromARGB(255, 2, 4, 6),
+          Color.fromARGB(255, 8, 10, 12),
+          Color.fromARGB(255, 14, 16, 18),
+          Color.fromARGB(255, 20, 22, 24),
+        ];
+        final FragmentShader shader = shaderMap[UniformArray<UniformMat3Slot>]!;
+        final UniformArray<UniformMat3Slot> colors = shader.getUniformMat3Array('colors');
+        colors[0].set(
+          cpuColors[0].r,
+          cpuColors[0].g,
+          cpuColors[0].b,
+          cpuColors[1].r,
+          cpuColors[1].g,
+          cpuColors[1].b,
+          cpuColors[2].r,
+          cpuColors[2].g,
+          cpuColors[2].b,
+        );
+
+        colors[1].set(
+          cpuColors[3].r,
+          cpuColors[3].g,
+          cpuColors[3].b,
+          cpuColors[4].r,
+          cpuColors[4].g,
+          cpuColors[4].b,
+          cpuColors[5].r,
+          cpuColors[5].g,
+          cpuColors[5].b,
+        );
+        await _expectShaderRendersBarcode(shader, cpuColors);
+      });
+
+      test('wrong datatype', () async {
+        final FragmentShader shader = shaderMap[UniformVec3Slot]!;
+        expect(
+          () => shader.getUniformMat3Array('color_rgb'),
+          throwsA(
+            isA<ArgumentError>().having(
+              (e) => e.message,
+              'message',
+              contains('Uniform size (3) for "color_rgb" is not a multiple of 9.'),
+            ),
+          ),
+        );
+      });
+    });
+
     group('all uniforms', () {
       late FragmentProgram program;
       late List<Color> cpuColors;
@@ -418,19 +866,44 @@
             random.nextInt(255),
           ),
         );
-        for (var i = 0; i < 10; ++i) {
-          cpuColors.add(Color.fromARGB(255, random.nextInt(255), 0, 0));
-        }
-
-        for (var i = 0; i < 10; ++i) {
+        // uMat2
+        for (var i = 0; i < 2; ++i) {
           cpuColors.add(Color.fromARGB(255, random.nextInt(255), random.nextInt(255), 0));
         }
 
+        // uMat3
+        for (var i = 0; i < 3; ++i) {
+          cpuColors.add(
+            Color.fromARGB(255, random.nextInt(255), random.nextInt(255), random.nextInt(255)),
+          );
+        }
+
+        // uMat4
+        for (var i = 0; i < 4; ++i) {
+          cpuColors.add(
+            Color.fromARGB(
+              random.nextInt(255),
+              random.nextInt(255),
+              random.nextInt(255),
+              random.nextInt(255),
+            ),
+          );
+        }
+        // uFloatArray
+        for (var i = 0; i < 10; ++i) {
+          cpuColors.add(Color.fromARGB(255, random.nextInt(255), 0, 0));
+        }
+        // uVec2Array
+        for (var i = 0; i < 10; ++i) {
+          cpuColors.add(Color.fromARGB(255, random.nextInt(255), random.nextInt(255), 0));
+        }
+        // uVec3Array
         for (var i = 0; i < 10; ++i) {
           cpuColors.add(
             Color.fromARGB(255, random.nextInt(255), random.nextInt(255), random.nextInt(255)),
           );
         }
+        // uVec4Array
         for (var i = 0; i < 10; ++i) {
           cpuColors.add(
             Color.fromARGB(
@@ -441,6 +914,30 @@
             ),
           );
         }
+
+        // uMat2Array
+        for (var i = 0; i < 20; ++i) {
+          cpuColors.add(Color.fromARGB(255, random.nextInt(255), random.nextInt(255), 0));
+        }
+
+        // uMat3Array
+        for (var i = 0; i < 30; ++i) {
+          cpuColors.add(
+            Color.fromARGB(255, random.nextInt(255), random.nextInt(255), random.nextInt(255)),
+          );
+        }
+
+        // uMat4Array
+        for (var i = 0; i < 40; ++i) {
+          cpuColors.add(
+            Color.fromARGB(
+              random.nextInt(255),
+              random.nextInt(255),
+              random.nextInt(255),
+              random.nextInt(255),
+            ),
+          );
+        }
       });
 
       test('set using setFloat', () async {
@@ -460,8 +957,49 @@
         shader.setFloat(8, cpuColors[3].b);
         shader.setFloat(9, cpuColors[3].a);
 
-        var shaderOffset = 10;
-        var colorOffset = 4;
+        //uMat2
+        shader.setFloat(10, cpuColors[4].r);
+        shader.setFloat(11, cpuColors[4].g);
+
+        shader.setFloat(12, cpuColors[5].r);
+        shader.setFloat(13, cpuColors[5].g);
+
+        //uMat3
+        shader.setFloat(14, cpuColors[6].r);
+        shader.setFloat(15, cpuColors[6].g);
+        shader.setFloat(16, cpuColors[6].b);
+
+        shader.setFloat(17, cpuColors[7].r);
+        shader.setFloat(18, cpuColors[7].g);
+        shader.setFloat(19, cpuColors[7].b);
+
+        shader.setFloat(20, cpuColors[8].r);
+        shader.setFloat(21, cpuColors[8].g);
+        shader.setFloat(22, cpuColors[8].b);
+
+        //uMat4
+        shader.setFloat(23, cpuColors[9].r);
+        shader.setFloat(24, cpuColors[9].g);
+        shader.setFloat(25, cpuColors[9].b);
+        shader.setFloat(26, cpuColors[9].a);
+
+        shader.setFloat(27, cpuColors[10].r);
+        shader.setFloat(28, cpuColors[10].g);
+        shader.setFloat(29, cpuColors[10].b);
+        shader.setFloat(30, cpuColors[10].a);
+
+        shader.setFloat(31, cpuColors[11].r);
+        shader.setFloat(32, cpuColors[11].g);
+        shader.setFloat(33, cpuColors[11].b);
+        shader.setFloat(34, cpuColors[11].a);
+
+        shader.setFloat(35, cpuColors[12].r);
+        shader.setFloat(36, cpuColors[12].g);
+        shader.setFloat(37, cpuColors[12].b);
+        shader.setFloat(38, cpuColors[12].a);
+
+        var shaderOffset = 39;
+        var colorOffset = 13;
 
         for (var i = 0; i < 10; ++i) {
           shader.setFloat(shaderOffset++, cpuColors[colorOffset++].r);
@@ -481,6 +1019,22 @@
           shader.setFloat(shaderOffset++, cpuColors[colorOffset].b);
           shader.setFloat(shaderOffset++, cpuColors[colorOffset++].a);
         }
+        for (var i = 0; i < 20; ++i) {
+          shader.setFloat(shaderOffset++, cpuColors[colorOffset].r);
+          shader.setFloat(shaderOffset++, cpuColors[colorOffset++].g);
+        }
+        for (var i = 0; i < 30; ++i) {
+          shader.setFloat(shaderOffset++, cpuColors[colorOffset].r);
+          shader.setFloat(shaderOffset++, cpuColors[colorOffset].g);
+          shader.setFloat(shaderOffset++, cpuColors[colorOffset++].b);
+        }
+        for (var i = 0; i < 40; ++i) {
+          shader.setFloat(shaderOffset++, cpuColors[colorOffset].r);
+          shader.setFloat(shaderOffset++, cpuColors[colorOffset].g);
+          shader.setFloat(shaderOffset++, cpuColors[colorOffset].b);
+          shader.setFloat(shaderOffset++, cpuColors[colorOffset++].a);
+        }
+
         _expectShaderRendersBarcode(shader, cpuColors);
       });
 
@@ -493,14 +1047,59 @@
             .getUniformVec4('uVec4')
             .set(cpuColors[3].r, cpuColors[3].g, cpuColors[3].b, cpuColors[3].a);
 
+        shader
+            .getUniformMat2('uMat2')
+            .set(cpuColors[4].r, cpuColors[4].g, cpuColors[5].r, cpuColors[5].g);
+
+        shader
+            .getUniformMat3('uMat3')
+            .set(
+              cpuColors[6].r,
+              cpuColors[6].g,
+              cpuColors[6].b,
+              cpuColors[7].r,
+              cpuColors[7].g,
+              cpuColors[7].b,
+              cpuColors[8].r,
+              cpuColors[8].g,
+              cpuColors[8].b,
+            );
+
+        shader
+            .getUniformMat4('uMat4')
+            .set(
+              cpuColors[9].r,
+              cpuColors[9].g,
+              cpuColors[9].b,
+              cpuColors[9].a,
+
+              cpuColors[10].r,
+              cpuColors[10].g,
+              cpuColors[10].b,
+              cpuColors[10].a,
+
+              cpuColors[11].r,
+              cpuColors[11].g,
+              cpuColors[11].b,
+              cpuColors[11].a,
+
+              cpuColors[12].r,
+              cpuColors[12].g,
+              cpuColors[12].b,
+              cpuColors[12].a,
+            );
+
         final UniformArray<UniformFloatSlot> floatArray = shader.getUniformFloatArray(
           'uFloatArray',
         );
         final UniformArray<UniformVec2Slot> vec2Array = shader.getUniformVec2Array('uVec2Array');
         final UniformArray<UniformVec3Slot> vec3Array = shader.getUniformVec3Array('uVec3Array');
         final UniformArray<UniformVec4Slot> vec4Array = shader.getUniformVec4Array('uVec4Array');
+        final UniformArray<UniformMat2Slot> mat2Array = shader.getUniformMat2Array('uMat2Array');
+        final UniformArray<UniformMat3Slot> mat3Array = shader.getUniformMat3Array('uMat3Array');
+        final UniformArray<UniformMat4Slot> mat4Array = shader.getUniformMat4Array('uMat4Array');
 
-        var colorOffset = 4;
+        var colorOffset = 13;
 
         for (var i = 0; i < 10; ++i) {
           floatArray[i].set(cpuColors[colorOffset++].r);
@@ -526,6 +1125,53 @@
           );
           ++colorOffset;
         }
+        for (var i = 0; i < 10; ++i) {
+          mat2Array[i].set(
+            cpuColors[colorOffset].r,
+            cpuColors[colorOffset].g,
+            cpuColors[colorOffset + 1].r,
+            cpuColors[colorOffset + 1].g,
+          );
+          colorOffset += 2;
+        }
+        for (var i = 0; i < 10; ++i) {
+          mat3Array[i].set(
+            cpuColors[colorOffset].r,
+            cpuColors[colorOffset].g,
+            cpuColors[colorOffset].b,
+            cpuColors[colorOffset + 1].r,
+            cpuColors[colorOffset + 1].g,
+            cpuColors[colorOffset + 1].b,
+            cpuColors[colorOffset + 2].r,
+            cpuColors[colorOffset + 2].g,
+            cpuColors[colorOffset + 2].b,
+          );
+          colorOffset += 3;
+        }
+        for (var i = 0; i < 10; ++i) {
+          mat4Array[i].set(
+            cpuColors[colorOffset].r,
+            cpuColors[colorOffset].g,
+            cpuColors[colorOffset].b,
+            cpuColors[colorOffset].a,
+
+            cpuColors[colorOffset + 1].r,
+            cpuColors[colorOffset + 1].g,
+            cpuColors[colorOffset + 1].b,
+            cpuColors[colorOffset + 1].a,
+
+            cpuColors[colorOffset + 2].r,
+            cpuColors[colorOffset + 2].g,
+            cpuColors[colorOffset + 2].b,
+            cpuColors[colorOffset + 2].a,
+
+            cpuColors[colorOffset + 3].r,
+            cpuColors[colorOffset + 3].g,
+            cpuColors[colorOffset + 3].b,
+            cpuColors[colorOffset + 3].a,
+          );
+          colorOffset += 4;
+        }
         _expectShaderRendersBarcode(shader, cpuColors);
       });
     });