sksl: Add support for 2x2 matrix inversions in Metal

Bug: skia:
Change-Id: I524319ede491be4884c42d89f485ed98cff0cd29
Reviewed-on: https://skia-review.googlesource.com/c/171221
Reviewed-by: Ethan Nicholas <ethannicholas@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/src/sksl/SkSLMetalCodeGenerator.cpp b/src/sksl/SkSLMetalCodeGenerator.cpp
index 22e5f1d..e4bcb5e 100644
--- a/src/sksl/SkSLMetalCodeGenerator.cpp
+++ b/src/sksl/SkSLMetalCodeGenerator.cpp
@@ -204,6 +204,9 @@
         this->write("atan2");
     } else if (c.fFunction.fBuiltin && "inversesqrt" == c.fFunction.fName) {
         this->write("rsqrt");
+    } else if (c.fFunction.fBuiltin && "inverse" == c.fFunction.fName) {
+        SkASSERT(c.fArguments.size() == 1);
+        this->writeInverseHack(*c.fArguments[0]);
     } else if (c.fFunction.fBuiltin && "dFdx" == c.fFunction.fName) {
         this->write("dfdx");
     } else if (c.fFunction.fBuiltin && "dFdy" == c.fFunction.fName) {
@@ -244,6 +247,22 @@
     this->write(")");
 }
 
+void MetalCodeGenerator::writeInverseHack(const Expression& mat) {
+    String name = "ERROR_MatrixInverseNotImplementedFor_" + mat.fType.name();
+    if (mat.fType == *fContext.fFloat2x2_Type) {
+        name = "_inverse2";
+        if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) {
+            fWrittenIntrinsics.insert(name);
+            fExtraFunctions.writeText((
+                "float2x2 " + name + "(float2x2 m) {"
+                "    return float2x2(m[1][1], -m[0][1], -m[1][0], m[0][0]) * (1/determinant(m));"
+                "}"
+            ).c_str());
+        }
+    }
+    this->write(name);
+}
+
 void MetalCodeGenerator::writeSpecialIntrinsic(const FunctionCall & c, SpecialIntrinsic kind) {
     switch (kind) {
         case kTexture_SpecialIntrinsic:
@@ -1444,6 +1463,7 @@
     fOut = rawOut;
 
     write_stringstream(fHeader, *rawOut);
+    write_stringstream(fExtraFunctions, *rawOut);
     write_stringstream(body, *rawOut);
 #ifdef SK_MOLTENVK
     this->write("\0");
diff --git a/src/sksl/SkSLMetalCodeGenerator.h b/src/sksl/SkSLMetalCodeGenerator.h
index bbf930b..1a3a790 100644
--- a/src/sksl/SkSLMetalCodeGenerator.h
+++ b/src/sksl/SkSLMetalCodeGenerator.h
@@ -182,6 +182,8 @@
 
     void writeFunctionCall(const FunctionCall& c);
 
+    void writeInverseHack(const Expression& mat);
+
     void writeSpecialIntrinsic(const FunctionCall& c, SpecialIntrinsic kind);
 
     void writeConstructor(const Constructor& c);
@@ -250,6 +252,7 @@
     const Context& fContext;
     StringStream fHeader;
     String fFunctionHeader;
+    StringStream fExtraFunctions;
     Program::Kind fProgramKind;
     int fVarCount = 0;
     int fIndentation = 0;
@@ -258,6 +261,7 @@
     // more than one or two structs per shader, a simple linear search will be faster than anything
     // fancier.
     std::vector<const Type*> fWrittenStructs;
+    std::set<String> fWrittenIntrinsics;
     // true if we have run into usages of dFdx / dFdy
     bool fFoundDerivatives = false;
     bool fFoundImageDecl = false;