Add support for matrix-to-vector conversions in SkSL.

GLSL supports casting vec4 into mat2 and vice versa, so SkSL should have
equivalent support. This CL allows the Compound constructor to take a
matrix as input, and fixes up backends to do the right thing when a
matrix shows up in the compound-constructor path.

Change-Id: I13289ad0a27ba59bddc3706093820594efebc693
Bug: skia:12067
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/426003
Auto-Submit: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: John Stiles <johnstiles@google.com>
diff --git a/src/sksl/codegen/SkSLMetalCodeGenerator.cpp b/src/sksl/codegen/SkSLMetalCodeGenerator.cpp
index 4ecaa5c..f6a07fa 100644
--- a/src/sksl/codegen/SkSLMetalCodeGenerator.cpp
+++ b/src/sksl/codegen/SkSLMetalCodeGenerator.cpp
@@ -1012,15 +1012,55 @@
 
 void MetalCodeGenerator::writeConstructorCompound(const ConstructorCompound& c,
                                                   Precedence parentPrecedence) {
-    if (c.type().isMatrix()) {
+    if (c.type().isVector()) {
+        this->writeConstructorCompoundVector(c, parentPrecedence);
+    } else if (c.type().isMatrix()) {
         this->writeConstructorCompoundMatrix(c, parentPrecedence);
     } else {
-        this->writeAnyConstructor(c, "(", ")", parentPrecedence);
+        fErrors.error(c.fOffset, "unsupported compound constructor");
     }
 }
 
+void MetalCodeGenerator::writeVectorFromMat2x2ConstructorHelper() {
+    static constexpr char kCode[] =
+R"(float4 float4_from_float2x2(float2x2 x) {
+    return float4(x[0].xy, x[1].xy);
+}
+)";
+
+    String name = "matrixCompMult";
+    if (fHelpers.find("float4_from_float2x2") == fHelpers.end()) {
+        fHelpers.insert("float4_from_float2x2");
+        fExtraFunctions.writeText(kCode);
+    }
+}
+
+void MetalCodeGenerator::writeConstructorCompoundVector(const ConstructorCompound& c,
+                                                        Precedence parentPrecedence) {
+    SkASSERT(c.type().isVector());
+
+    // Metal supports constructing vectors from a mix of scalars and vectors, but not matrices.
+    // GLSL supports vec4(mat2x2), so we detect that case here and emit a helper function.
+    if (c.type().columns() == 4 && c.argumentSpan().size() == 1) {
+        const Expression& expr = *c.argumentSpan().front();
+        if (expr.type().isMatrix()) {
+            SkASSERT(expr.type().rows() == 2);
+            SkASSERT(expr.type().columns() == 2);
+            this->writeVectorFromMat2x2ConstructorHelper();
+            this->write("float4_from_float2x2(");
+            this->writeExpression(expr, Precedence::kSequence);
+            this->write(")");
+            return;
+        }
+    }
+
+    this->writeAnyConstructor(c, "(", ")", parentPrecedence);
+}
+
 void MetalCodeGenerator::writeConstructorCompoundMatrix(const ConstructorCompound& c,
                                                         Precedence parentPrecedence) {
+    SkASSERT(c.type().isMatrix());
+
     // Emit and invoke a matrix-constructor helper method if one is necessary.
     if (this->matrixConstructHelperIsNeeded(c)) {
         this->write(this->getMatrixConstructHelper(c));