Implement matrix casting in Metal.

Unlike GLSL/SkSL, Metal does not natively support casting an array from
one size to another; we need to synthesize a helper function which will
assemble a new matrix from the values in the old matrix and the
identity.

Previously, our matrix-conversion helpers understood how to glom
together an arbitrary collection of scalars/vectors/matrices into a
matrix containing a matching number of scalars, but it would fail when
given a matrix of unequal size.

Change-Id: I35eb161ed7c17b982b00ecceb7b525cbfb8f3bcb
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/308190
Commit-Queue: John Stiles <johnstiles@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
diff --git a/tests/SkSLMetalTest.cpp b/tests/SkSLMetalTest.cpp
index 41c440f..94d2ef0 100644
--- a/tests/SkSLMetalTest.cpp
+++ b/tests/SkSLMetalTest.cpp
@@ -114,6 +114,122 @@
 )__MSL__");
 }
 
+DEF_TEST(SkSLMetalCastMat2x2ToMat3x3, r) {
+    test(r, *SkSL::ShaderCapsFactory::Default(),
+R"__SkSL__(
+void main() {
+float3x3 a = float3x3(1);
+float3x3 b = float3x3(float2x2(1));
+sk_FragColor.x = (a[0] == b[0]) ? 0 : 1;
+}
+)__SkSL__",
+R"__MSL__(#include <metal_stdlib>
+#include <simd/simd.h>
+using namespace metal;
+struct Inputs {
+};
+struct Outputs {
+    float4 sk_FragColor [[color(0)]];
+};
+float3x3 float3x3_from_float2x2(float2x2 x0) {
+    return float3x3(float3(x0[0].xy, 0.0), float3(x0[1].xy, 0.0), float3(0.0, 0.0, 1.0));
+}
+fragment Outputs fragmentMain(Inputs _in [[stage_in]], bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]) {
+    Outputs _outputStruct;
+    thread Outputs* _out = &_outputStruct;
+    _out->sk_FragColor.x = float(all(float3x3(1.0)[0] == float3x3_from_float2x2(float2x2(1.0))[0]) ? 0 : 1);
+    return *_out;
+}
+)__MSL__");
+}
+
+DEF_TEST(SkSLMetalCastMat2x3ToMat4x4, r) {
+    test(r, *SkSL::ShaderCapsFactory::Default(),
+R"__SkSL__(
+void main() {
+float4x4 a = float4x4(6);
+float4x4 b = float4x4(float2x3(7));
+sk_FragColor.x = (a[1] == b[1]) ? 0 : 1;
+}
+)__SkSL__",
+R"__MSL__(#include <metal_stdlib>
+#include <simd/simd.h>
+using namespace metal;
+struct Inputs {
+};
+struct Outputs {
+    float4 sk_FragColor [[color(0)]];
+};
+float4x4 float4x4_from_float2x3(float2x3 x0) {
+    return float4x4(float4(x0[0].xyz, 0.0), float4(x0[1].xyz, 0.0), float4(0.0, 0.0, 1.0, 0.0), float4(0.0, 0.0, 0.0, 1.0));
+}
+fragment Outputs fragmentMain(Inputs _in [[stage_in]], bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]) {
+    Outputs _outputStruct;
+    thread Outputs* _out = &_outputStruct;
+    _out->sk_FragColor.x = float(all(float4x4(6.0)[1] == float4x4_from_float2x3(float2x3(7.0))[1]) ? 0 : 1);
+    return *_out;
+}
+)__MSL__");
+}
+
+DEF_TEST(SkSLMetalCastMat4x4ToMat3x4, r) {
+    test(r, *SkSL::ShaderCapsFactory::Default(),
+R"__SkSL__(
+void main() {
+float3x4 a = float3x4(1);
+float3x4 b = float3x4(float4x4(1));
+sk_FragColor.x = (a[0] == b[0]) ? 0 : 1;
+}
+)__SkSL__",
+R"__MSL__(#include <metal_stdlib>
+#include <simd/simd.h>
+using namespace metal;
+struct Inputs {
+};
+struct Outputs {
+    float4 sk_FragColor [[color(0)]];
+};
+float3x4 float3x4_from_float4x4(float4x4 x0) {
+    return float3x4(float4(x0[0].xyzw), float4(x0[1].xyzw), float4(x0[2].xyzw));
+}
+fragment Outputs fragmentMain(Inputs _in [[stage_in]], bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]) {
+    Outputs _outputStruct;
+    thread Outputs* _out = &_outputStruct;
+    _out->sk_FragColor.x = float(all(float3x4(1.0)[0] == float3x4_from_float4x4(float4x4(1.0))[0]) ? 0 : 1);
+    return *_out;
+}
+)__MSL__");
+}
+
+DEF_TEST(SkSLMetalCastMat4x4ToMat4x3, r) {
+    test(r, *SkSL::ShaderCapsFactory::Default(),
+R"__SkSL__(
+void main() {
+float4x3 a = float4x3(1);
+float4x3 b = float4x3(float4x4(1));
+sk_FragColor.x = (a[0] == b[0]) ? 0 : 1;
+}
+)__SkSL__",
+R"__MSL__(#include <metal_stdlib>
+#include <simd/simd.h>
+using namespace metal;
+struct Inputs {
+};
+struct Outputs {
+    float4 sk_FragColor [[color(0)]];
+};
+float4x3 float4x3_from_float4x4(float4x4 x0) {
+    return float4x3(float3(x0[0].xyz), float3(x0[1].xyz), float3(x0[2].xyz), float3(x0[3].xyz));
+}
+fragment Outputs fragmentMain(Inputs _in [[stage_in]], bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]) {
+    Outputs _outputStruct;
+    thread Outputs* _out = &_outputStruct;
+    _out->sk_FragColor.x = float(all(float4x3(1.0)[0] == float4x3_from_float4x4(float4x4(1.0))[0]) ? 0 : 1);
+    return *_out;
+}
+)__MSL__");
+}
+
 DEF_TEST(SkSLMetalMatrices, r) {
     test(r, *SkSL::ShaderCapsFactory::Default(),
 R"__SkSL__(