added global variable and multi-texture support to metal sksl backend

Bug: skia:
Change-Id: If676774ec0a30c5b536ccffbff2220d180b7fa59
Reviewed-on: https://skia-review.googlesource.com/129187
Commit-Queue: Timothy Liang <timliang@google.com>
Reviewed-by: Ethan Nicholas <ethannicholas@google.com>
diff --git a/src/sksl/SkSLMetalCodeGenerator.cpp b/src/sksl/SkSLMetalCodeGenerator.cpp
index b8fdc1c..2f801a8 100644
--- a/src/sksl/SkSLMetalCodeGenerator.cpp
+++ b/src/sksl/SkSLMetalCodeGenerator.cpp
@@ -17,7 +17,7 @@
 
 namespace SkSL {
 
-void MetalCodeGenerator::setupIntrinsics(){
+void MetalCodeGenerator::setupIntrinsics() {
 #define SPECIAL(x) std::make_tuple(kSpecial_IntrinsicKind, k ## x ## _SpecialIntrinsic, \
                                 k ## x ## _SpecialIntrinsic, k ## x ## _SpecialIntrinsic, \
                                 k ## x ## _SpecialIntrinsic)
@@ -25,6 +25,10 @@
     fIntrinsicMap[String("texture")]     = SPECIAL(Texture);
 }
 
+MetalCodeGenerator::TextureId MetalCodeGenerator::nextTextureId() {
+    return fCurrentTextureId++;
+}
+
 void MetalCodeGenerator::write(const char* s) {
     if (!s[0]) {
         return;
@@ -86,6 +90,9 @@
             this->writeType(type.componentType());
             this->write(to_string(type.columns()));
             break;
+        case Type::kSampler_Kind:
+            this->write("texture2d<half> "); //FIXME - support other texture types;
+            break;
         default:
             this->write(type.name());
     }
@@ -186,6 +193,11 @@
         this->write("_uniforms");
         separator = ", ";
     }
+    if (this->requirements(c.fFunction) & kGlobals_Requirement) {
+        this->write(separator);
+        this->write("_globals");
+        separator = ", ";
+    }
     for (size_t i = 0; i < c.fArguments.size(); ++i) {
         const Expression& arg = *c.fArguments[i];
         this->write(separator);
@@ -201,9 +213,15 @@
 void MetalCodeGenerator::writeSpecialIntrinsic(const FunctionCall & c, SpecialIntrinsic kind) {
     switch (kind) {
         case kTexture_SpecialIntrinsic:
-            this->write("_colorMap.sample($colorSampler, ");
+            this->writeExpression(*c.fArguments[0], kSequence_Precedence);
+            this->write(".sample(_globals->colorSampler, ");
             this->writeExpression(*c.fArguments[1], kSequence_Precedence);
-            this->write(".xy)"); // FIXME - dimension checking
+            if (c.fArguments[1]->fType == *fContext.fFloat3_Type) {
+                this->write(".xy)"); // FIXME - add projection functionality
+            } else {
+                ASSERT(c.fArguments[1]->fType == *fContext.fFloat2_Type);
+                this->write(")");
+            }
             break;
         default:
             ABORT("unsupported special intrinsic kind");
@@ -254,12 +272,11 @@
                 if (ref.fVariable.fModifiers.fFlags & Modifiers::kIn_Flag) {
                     this->write("_in.");
                 } else if (ref.fVariable.fModifiers.fFlags & Modifiers::kOut_Flag) {
-                    this->write("_out.");
+                    this->write("_out->");
                 } else if (ref.fVariable.fModifiers.fFlags & Modifiers::kUniform_Flag) {
                     this->write("_uniforms.");
                 } else {
-                    fErrors.error(ref.fVariable.fOffset, "Metal backend does not support global "
-                                  "variables");
+                    this->write("_globals->");
                 }
             }
             this->write(ref.fVariable.fName);
@@ -283,7 +300,7 @@
             this->write("gl_ClipDistance");
             break;
         case SK_POSITION_BUILTIN:
-            this->write("_out.position");
+            this->write("_out->position");
             break;
         default:
             this->write(f.fBase->fType.fields()[f.fFieldIndex].fName);
@@ -437,7 +454,6 @@
 }
 
 void MetalCodeGenerator::writeFunction(const FunctionDefinition& f) {
-    bool needColorSampler = false;
     const char* separator = "";
     if ("main" == f.fDeclaration.fName) {
         switch (fProgram.fKind) {
@@ -461,12 +477,15 @@
                 if (!decls.fVars.size()) {
                     continue;
                 }
-                for(const auto& stmt: decls.fVars){
+                for (const auto& stmt: decls.fVars) {
                     VarDeclaration& var = (VarDeclaration&) *stmt;
-                    if(var.fVar->fType == *fContext.fSampler2D_Type){
-                        needColorSampler = true;
-                        this->write(", texture2d<half> _colorMap [[texture(TextureIndexColor)]]");
-                    } // FIXME may require textureindexcolor field, hardcoded for now
+                    if (var.fVar->fType.kind() == Type::kSampler_Kind) {
+                        this->write(", texture2d<half> "); // FIXME - support other texture types
+                        this->write(var.fVar->fName);
+                        this->write("[[texture(");
+                        this->write(to_string(fTextureMap[var.fVar->fName]));
+                        this->write(")]]");
+                    }
                 }
             }
         }
@@ -480,7 +499,7 @@
         }
         if (this->requirements(f.fDeclaration) & kOutputs_Requirement) {
             this->write(separator);
-            this->write("thread Outputs& _out");
+            this->write("thread Outputs* _out");
             separator = ", ";
         }
         if (this->requirements(f.fDeclaration) & kUniforms_Requirement) {
@@ -488,6 +507,11 @@
             this->write("Uniforms _uniforms");
             separator = ", ";
         }
+        if (this->requirements(f.fDeclaration) & kGlobals_Requirement) {
+            this->write(separator);
+            this->write("thread Globals* _globals");
+            separator = ", ";
+        }
     }
     for (const auto& param : f.fDeclaration.fParameters) {
         this->write(separator);
@@ -517,9 +541,27 @@
     ASSERT(!fProgram.fSettings.fFragColorIsInOut);
 
     if ("main" == f.fDeclaration.fName) {
-        if (needColorSampler) {
-            this->writeLine("    constexpr sampler "
-                "$colorSampler(mip_filter::linear, mag_filter::linear, min_filter::linear);");
+        if (fNeedsGlobalStructInit) {
+            this->writeLine("    Globals globalStruct;");
+            this->writeLine("    thread Globals* _globals = &globalStruct;");
+            for (const auto var: fInitNonConstGlobalVars) {
+                this->write("    _globals->");
+                this->write(var->fVar->fName);
+                this->write(" = ");
+                this->writeVarInitializer(*var->fVar, *var->fValue);
+                this->writeLine(";");
+            }
+        }
+        if (!fTextureMap.empty()) {
+            this->writeLine("    _globals->colorSampler = sampler(mip_filter::linear, "
+                "mag_filter::linear, min_filter::linear);"); // FIXME - support other samplers
+            for (const auto& texture: fTextureMap) {
+                this->write("    _globals->");
+                this->write(texture.first);
+                this->write(" = ");
+                this->write(texture.first);
+                this->write(";\n");
+            }
         }
         switch (fProgram.fKind) {
             case Program::kFragment_Kind:
@@ -564,7 +606,7 @@
         this->write("thread ");
     }
     if (modifiers.fFlags & Modifiers::kConst_Flag) {
-        this->write("const ");
+        this->write("constant ");
     }
 }
 
@@ -609,14 +651,9 @@
     bool wroteType = false;
     for (const auto& stmt : decl.fVars) {
         VarDeclaration& var = (VarDeclaration&) *stmt;
-        if (var.fVar->fModifiers.fFlags & (Modifiers::kIn_Flag | Modifiers::kOut_Flag |
-                                           Modifiers::kUniform_Flag)) {
-            ASSERT(global);
+        if (global && !(var.fVar->fModifiers.fFlags & Modifiers::kConst_Flag)) {
             continue;
         }
-        if (var.fVar->fType == *fContext.fSampler2D_Type){
-            continue; // FIXME - temporarily ignoring global sampler2Ds
-        }
         if (wroteType) {
             this->write(", ");
         } else {
@@ -892,6 +929,46 @@
     }    this->write("};\n");
 }
 
+void MetalCodeGenerator::writeGlobalStruct() {
+    bool wroteStructDecl = false;
+    for (const auto& e : fProgram) {
+        if (ProgramElement::kVar_Kind == e.fKind) {
+            VarDeclarations& decls = (VarDeclarations&) e;
+            if (!decls.fVars.size()) {
+                continue;
+            }
+            const Variable& first = *((VarDeclaration&) *decls.fVars[0]).fVar;
+            if (!first.fModifiers.fFlags && -1 == first.fModifiers.fLayout.fBuiltin) {
+                if (!wroteStructDecl) {
+                    this->write("struct Globals {\n");
+                    wroteStructDecl = true;
+                }
+                fNeedsGlobalStructInit = true;
+                this->write("    ");
+                this->writeType(first.fType);
+                this->write(" ");
+                for (const auto& stmt : decls.fVars) {
+                    VarDeclaration& var = (VarDeclaration&) *stmt;
+                    if (var.fVar->fType.kind() == Type::kSampler_Kind) {
+                        fTextureMap[var.fVar->fName] = this->nextTextureId();
+                    }
+                    this->write(var.fVar->fName);
+                    if (var.fValue) {
+                        fInitNonConstGlobalVars.push_back(&var);
+                    }
+                }
+                this->write(";\n");
+            }
+        }
+    }
+    if (!fTextureMap.empty()) {
+        this->writeLine("    sampler colorSampler;");
+    }
+    if (wroteStructDecl) {
+        this->write("};\n");
+    }
+}
+
 void MetalCodeGenerator::writeProgramElement(const ProgramElement& e) {
     switch (e.fKind) {
         case ProgramElement::kExtension_Kind:
@@ -977,6 +1054,8 @@
                     result = kOutputs_Requirement;
                 } else if (v.fVariable.fModifiers.fFlags & Modifiers::kUniform_Flag) {
                     result = kUniforms_Requirement;
+                } else {
+                    result = kGlobals_Requirement;
                 }
             }
             return result;
@@ -1072,6 +1151,7 @@
     if (Program::kVertex_Kind == fProgram.fKind) {
         this->writeOutputStruct();
     }
+    this->writeGlobalStruct();
     StringStream body;
     fOut = &body;
     for (const auto& e : fProgram) {