Add fields to fragmentProcessors in sksl code.

Getters for GrFragmentProcessor's optimization flags and state are
exposed as constant fields on the fragmentProcessor variable in sksl.
The CPP code generation then emits extra instructions to invoke the
particular getter when building the final sksl string at runtime.

Bug: skia:
Change-Id: If6bf4f4df6c446fb6331484d36effb1386172918
Reviewed-on: https://skia-review.googlesource.com/152381
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Ethan Nicholas <ethannicholas@google.com>
diff --git a/src/sksl/SkSLCPPCodeGenerator.cpp b/src/sksl/SkSLCPPCodeGenerator.cpp
index 88c7ed3..1a14cba 100644
--- a/src/sksl/SkSLCPPCodeGenerator.cpp
+++ b/src/sksl/SkSLCPPCodeGenerator.cpp
@@ -323,6 +323,53 @@
     INHERITED::writeSwitchStatement(s);
 }
 
+void CPPCodeGenerator::writeFieldAccess(const FieldAccess& access) {
+    if (access.fBase->fType.name() == "fragmentProcessor") {
+        // Special field access on fragment processors are converted into function calls on
+        // GrFragmentProcessor's getters.
+        if (access.fBase->fKind != Expression::kVariableReference_Kind) {
+            fErrors.error(access.fBase->fOffset, "fragmentProcessor must be a reference\n");
+            return;
+        }
+
+        const Type::Field& field = fContext.fFragmentProcessor_Type->fields()[access.fFieldIndex];
+        int index = getChildFPIndex((const VariableReference&) *access.fBase);
+        String cppAccess = String::printf("_outer.childProcessor(%s).%s()",
+                                          to_string(index).c_str(), String(field.fName).c_str());
+
+        if (fCPPMode) {
+            this->write(cppAccess.c_str());
+        } else {
+            writeRuntimeValue(*field.fType, Layout(), cppAccess);
+        }
+        return;
+    }
+    INHERITED::writeFieldAccess(access);
+}
+
+int CPPCodeGenerator::getChildFPIndex(const VariableReference& reference) const {
+    int index = 0;
+    bool found = false;
+    for (const auto& p : fProgram) {
+        if (ProgramElement::kVar_Kind == p.fKind) {
+            const VarDeclarations& decls = (const VarDeclarations&) p;
+            for (const auto& raw : decls.fVars) {
+                const VarDeclaration& decl = (VarDeclaration&) *raw;
+                if (decl.fVar == &reference.fVariable) {
+                    found = true;
+                } else if (decl.fVar->fType == *fContext.fFragmentProcessor_Type) {
+                    ++index;
+                }
+            }
+        }
+        if (found) {
+            break;
+        }
+    }
+    SkASSERT(found);
+    return index;
+}
+
 void CPPCodeGenerator::writeFunctionCall(const FunctionCall& c) {
     if (c.fFunction.fBuiltin && c.fFunction.fName == "process") {
         // Sanity checks that are detected by function definition in sksl_fp.inc
@@ -341,25 +388,7 @@
             // Second argument must also be a half4 expression
             SkASSERT("half4" == c.fArguments[1]->fType.name());
         }
-        int index = 0;
-        bool found = false;
-        for (const auto& p : fProgram) {
-            if (ProgramElement::kVar_Kind == p.fKind) {
-                const VarDeclarations& decls = (const VarDeclarations&) p;
-                for (const auto& raw : decls.fVars) {
-                    VarDeclaration& decl = (VarDeclaration&) *raw;
-                    if (decl.fVar == &((VariableReference&) *c.fArguments[0]).fVariable) {
-                        found = true;
-                    } else if (decl.fVar->fType == *fContext.fFragmentProcessor_Type) {
-                        ++index;
-                    }
-                }
-            }
-            if (found) {
-                break;
-            }
-        }
-        SkASSERT(found);
+        int index = getChildFPIndex((const VariableReference&) *c.fArguments[0]);
 
         // Start a new extra emit code section so that the emitted child processor can depend on
         // sksl variables defined in earlier sksl code.
diff --git a/src/sksl/SkSLCPPCodeGenerator.h b/src/sksl/SkSLCPPCodeGenerator.h
index 4b62fc6..08d88a9 100644
--- a/src/sksl/SkSLCPPCodeGenerator.h
+++ b/src/sksl/SkSLCPPCodeGenerator.h
@@ -43,6 +43,8 @@
 
     void writeSwizzle(const Swizzle& swizzle) override;
 
+    void writeFieldAccess(const FieldAccess& access) override;
+
     void writeVariableReference(const VariableReference& ref) override;
 
     String getSamplerHandle(const Variable& var);
@@ -113,6 +115,8 @@
     // Append CPP code to the current extra emit code block.
     void addExtraEmitCodeLine(const String& toAppend);
 
+    int getChildFPIndex(const VariableReference& reference) const;
+
     String fName;
     String fFullName;
     SectionAndParameterHelper fSectionAndParameterHelper;
diff --git a/src/sksl/SkSLContext.h b/src/sksl/SkSLContext.h
index 58ce3a4..225f65d 100644
--- a/src/sksl/SkSLContext.h
+++ b/src/sksl/SkSLContext.h
@@ -196,7 +196,7 @@
                                      fBool3_Type.get(), fBool4_Type.get() }))
     , fSkCaps_Type(new Type("$sk_Caps"))
     , fSkArgs_Type(new Type("$sk_Args"))
-    , fFragmentProcessor_Type(new Type("fragmentProcessor"))
+    , fFragmentProcessor_Type(fp_type(fInt_Type.get(), fBool_Type.get()))
     , fSkRasterPipeline_Type(new Type("SkRasterPipeline"))
     , fDefined_Expression(new Defined(*fInvalid_Type)) {}
 
@@ -385,6 +385,22 @@
 
         typedef Expression INHERITED;
     };
+
+    static std::unique_ptr<Type> fp_type(const Type* intType, const Type* boolType) {
+        // Build fields for FragmentProcessors, which should parallel the
+        // C++ API for GrFragmentProcessor.
+        Modifiers mods(Layout(), Modifiers::kConst_Flag);
+        std::vector<Type::Field> fields = {
+            Type::Field(mods, "numTextureSamplers", intType),
+            Type::Field(mods, "numCoordTransforms", intType),
+            Type::Field(mods, "numChildProcessors", intType),
+            Type::Field(mods, "usesLocalCoords", boolType),
+            Type::Field(mods, "compatibleWithCoverageAsAlpha", boolType),
+            Type::Field(mods, "preservesOpaqueInput", boolType),
+            Type::Field(mods, "hasConstantOutputForConstantInput", boolType)
+        };
+        return std::unique_ptr<Type>(new Type("fragmentProcessor", fields));
+    }
 };
 
 } // namespace
diff --git a/src/sksl/SkSLGLSLCodeGenerator.h b/src/sksl/SkSLGLSLCodeGenerator.h
index bebd76f..906c1b0 100644
--- a/src/sksl/SkSLGLSLCodeGenerator.h
+++ b/src/sksl/SkSLGLSLCodeGenerator.h
@@ -151,7 +151,7 @@
 
     void writeConstructor(const Constructor& c, Precedence parentPrecedence);
 
-    void writeFieldAccess(const FieldAccess& f);
+    virtual void writeFieldAccess(const FieldAccess& f);
 
     virtual void writeSwizzle(const Swizzle& swizzle);
 
diff --git a/src/sksl/SkSLIRGenerator.cpp b/src/sksl/SkSLIRGenerator.cpp
index 489f5d1..f39777e 100644
--- a/src/sksl/SkSLIRGenerator.cpp
+++ b/src/sksl/SkSLIRGenerator.cpp
@@ -2184,6 +2184,7 @@
             switch (base->fType.kind()) {
                 case Type::kVector_Kind:
                     return this->convertSwizzle(std::move(base), field);
+                case Type::kOther_Kind:
                 case Type::kStruct_Kind:
                     return this->convertField(std::move(base), field);
                 default:
diff --git a/src/sksl/ir/SkSLModifiers.h b/src/sksl/ir/SkSLModifiers.h
index 80ef5d7..ecadf3e 100644
--- a/src/sksl/ir/SkSLModifiers.h
+++ b/src/sksl/ir/SkSLModifiers.h
@@ -40,7 +40,7 @@
     : fLayout(Layout())
     , fFlags(0) {}
 
-    Modifiers(Layout& layout, int flags)
+    Modifiers(const Layout& layout, int flags)
     : fLayout(layout)
     , fFlags(flags) {}
 
diff --git a/src/sksl/ir/SkSLType.h b/src/sksl/ir/SkSLType.h
index ed4b8d5..782d010 100644
--- a/src/sksl/ir/SkSLType.h
+++ b/src/sksl/ir/SkSLType.h
@@ -71,6 +71,17 @@
         fName.fLength = fNameString.size();
     }
 
+    // Create an "other" (special) type that supports field access.
+    Type(String name, std::vector<Field> fields)
+    : INHERITED(-1, kType_Kind, StringFragment())
+    , fNameString(std::move(name))
+    , fTypeKind(kOther_Kind)
+    , fNumberKind(kNonnumeric_NumberKind)
+    , fFields(std::move(fields)) {
+        fName.fChars = fNameString.c_str();
+        fName.fLength = fNameString.size();
+    }
+
     // Create a simple type.
     Type(String name, Kind kind)
     : INHERITED(-1, kType_Kind, StringFragment())
@@ -289,7 +300,7 @@
     }
 
     const std::vector<Field>& fields() const {
-        SkASSERT(fTypeKind == kStruct_Kind);
+        SkASSERT(fTypeKind == kStruct_Kind || fTypeKind == kOther_Kind);
         return fFields;
     }