fix texture projection and fmod on metal

Change-Id: I95d9020f003592915cad3b9aa29236ea3bdac3db
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/264019
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
diff --git a/src/sksl/SkSLMetalCodeGenerator.cpp b/src/sksl/SkSLMetalCodeGenerator.cpp
index b4f75e2..c42061f 100644
--- a/src/sksl/SkSLMetalCodeGenerator.cpp
+++ b/src/sksl/SkSLMetalCodeGenerator.cpp
@@ -69,50 +69,49 @@
     this->writeLine("#extension " + ext.fName + " : enable");
 }
 
-void MetalCodeGenerator::writeType(const Type& type) {
+String MetalCodeGenerator::typeName(const Type& type) {
     switch (type.kind()) {
-        case Type::kStruct_Kind:
-            for (const Type* search : fWrittenStructs) {
-                if (*search == type) {
-                    // already written
-                    this->write(type.name());
-                    return;
-                }
-            }
-            fWrittenStructs.push_back(&type);
-            this->writeLine("struct " + type.name() + " {");
-            fIndentation++;
-            this->writeFields(type.fields(), type.fOffset);
-            fIndentation--;
-            this->write("}");
-            break;
         case Type::kVector_Kind:
-            this->writeType(type.componentType());
-            this->write(to_string(type.columns()));
-            break;
+            return this->typeName(type.componentType()) + to_string(type.columns());
         case Type::kMatrix_Kind:
-            this->writeType(type.componentType());
-            this->write(to_string(type.columns()));
-            this->write("x");
-            this->write(to_string(type.rows()));
-            break;
+            return this->typeName(type.componentType()) + to_string(type.columns()) + "x" +
+                                  to_string(type.rows());
         case Type::kSampler_Kind:
-            this->write("texture2d<float> "); // FIXME - support other texture types;
-            break;
+            return "texture2d<float>"; // FIXME - support other texture types;
         default:
             if (type == *fContext.fHalf_Type) {
                 // FIXME - Currently only supporting floats in MSL to avoid type coercion issues.
-                this->write(fContext.fFloat_Type->name());
+                return fContext.fFloat_Type->name();
             } else if (type == *fContext.fByte_Type) {
-                this->write("char");
+                return "char";
             } else if (type == *fContext.fUByte_Type) {
-                this->write("uchar");
+                return "uchar";
             } else {
-                this->write(type.name());
+                return type.name();
             }
     }
 }
 
+void MetalCodeGenerator::writeType(const Type& type) {
+    if (type.kind() == Type::kStruct_Kind) {
+        for (const Type* search : fWrittenStructs) {
+            if (*search == type) {
+                // already written
+                this->write(type.name());
+                return;
+            }
+        }
+        fWrittenStructs.push_back(&type);
+        this->writeLine("struct " + type.name() + " {");
+        fIndentation++;
+        this->writeFields(type.fields(), type.fOffset);
+        fIndentation--;
+        this->write("}");
+    } else {
+        this->write(this->typeName(type));
+    }
+}
+
 void MetalCodeGenerator::writeExpression(const Expression& expr, Precedence parentPrecedence) {
     switch (expr.fKind) {
         case Expression::kBinary_Kind:
@@ -352,26 +351,33 @@
             this->writeExpression(*c.fArguments[0], kSequence_Precedence);
             this->write(SAMPLER_SUFFIX);
             this->write(", ");
-            this->writeExpression(*c.fArguments[1], kSequence_Precedence);
             if (c.fArguments[1]->fType == *fContext.fFloat3_Type) {
-                this->write(".xy)"); // FIXME - add projection functionality
+                // have to store the vector in a temp variable to avoid double evaluating it
+                String tmpVar = "tmpCoord" + to_string(fVarCount++);
+                this->fFunctionHeader += "    " + this->typeName(c.fArguments[1]->fType) + " " +
+                                         tmpVar + ";\n";
+                this->write("(" + tmpVar + " = ");
+                this->writeExpression(*c.fArguments[1], kSequence_Precedence);
+                this->write(", " + tmpVar + ".xy / " + tmpVar + ".z))");
             } else {
                 SkASSERT(c.fArguments[1]->fType == *fContext.fFloat2_Type);
+                this->writeExpression(*c.fArguments[1], kSequence_Precedence);
                 this->write(")");
             }
             break;
-        case kMod_SpecialIntrinsic:
+        case kMod_SpecialIntrinsic: {
             // fmod(x, y) in metal calculates x - y * trunc(x / y) instead of x - y * floor(x / y)
-            this->write("((");
+            String tmpX = "tmpX" + to_string(fVarCount++);
+            String tmpY = "tmpY" + to_string(fVarCount++);
+            this->fFunctionHeader += "    " + this->typeName(c.fArguments[0]->fType) + " " + tmpX +
+                                     ", " + tmpY + ";\n";
+            this->write("(" + tmpX + " = ");
             this->writeExpression(*c.fArguments[0], kSequence_Precedence);
-            this->write(") - (");
+            this->write(", " + tmpY + " = ");
             this->writeExpression(*c.fArguments[1], kSequence_Precedence);
-            this->write(") * floor((");
-            this->writeExpression(*c.fArguments[0], kSequence_Precedence);
-            this->write(") / (");
-            this->writeExpression(*c.fArguments[1], kSequence_Precedence);
-            this->write(")))");
+            this->write(", " + tmpX + " - " + tmpY + " * floor(" + tmpX + " / " + tmpY + "))");
             break;
+        }
         default:
             ABORT("unsupported special intrinsic kind");
     }