Add implementation of outerProduct() intrinsic to Metal.

Metal doesn't natively offer this intrinsic at all, but we now write an
equivalent template function whenever the intrinsic is encountered.
Proper testing will be added in a followup CL (when outerProduct is made
available in public SkSL).

Change-Id: Ie8d6bf8d735d0ab45b7559be68036b08c5802365
Bug: skia:12202
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/447296
Commit-Queue: John Stiles <johnstiles@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/src/sksl/codegen/SkSLMetalCodeGenerator.cpp b/src/sksl/codegen/SkSLMetalCodeGenerator.cpp
index 22a24a7..02e8b14 100644
--- a/src/sksl/codegen/SkSLMetalCodeGenerator.cpp
+++ b/src/sksl/codegen/SkSLMetalCodeGenerator.cpp
@@ -467,7 +467,8 @@
     return "inverse";
 }
 
-static constexpr char kMatrixCompMult[] = R"(
+void MetalCodeGenerator::writeMatrixCompMult() {
+    static constexpr char kMatrixCompMult[] = R"(
 template <int C, int R>
 matrix<float, C, R> matrixCompMult(matrix<float, C, R> a, matrix<float, C, R> b) {
     matrix<float, C, R> result;
@@ -478,7 +479,6 @@
 }
 )";
 
-void MetalCodeGenerator::writeMatrixCompMult() {
     String name = "matrixCompMult";
     if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) {
         fWrittenIntrinsics.insert(name);
@@ -486,6 +486,25 @@
     }
 }
 
+void MetalCodeGenerator::writeOuterProduct() {
+    static constexpr char kOuterProduct[] = R"(
+template <int C, int R>
+matrix<float, C, R> outerProduct(const vec<float, R> a, const vec<float, C> b) {
+    matrix<float, C, R> result;
+    for (int c = 0; c < C; ++c) {
+        result[c] = a * b[c];
+    }
+    return result;
+}
+)";
+
+    String name = "outerProduct";
+    if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) {
+        fWrittenIntrinsics.insert(name);
+        fExtraFunctions.writeText(kOuterProduct);
+    }
+}
+
 String MetalCodeGenerator::getTempVariable(const Type& type) {
     String tempVar = "_skTemp" + to_string(fVarCount++);
     this->fFunctionHeader += "    " + this->typeName(type) + " " + tempVar + ";\n";
@@ -827,6 +846,11 @@
             this->writeSimpleIntrinsic(c);
             return true;
         }
+        case k_outerProduct_IntrinsicKind: {
+            this->writeOuterProduct();
+            this->writeSimpleIntrinsic(c);
+            return true;
+        }
         case k_mix_IntrinsicKind: {
             SkASSERT(c.arguments().size() == 3);
             if (arguments[2]->type().componentType().isBoolean()) {