Add support for image load to SkSL

GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=4865

Change-Id: I4647e6b255946ced2b1b8cb05e62f0f5a8ad28b6
Reviewed-on: https://skia-review.googlesource.com/4865
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Ethan Nicholas <ethannicholas@google.com>
diff --git a/src/sksl/SkSLCompiler.cpp b/src/sksl/SkSLCompiler.cpp
index 4794759..510d610 100644
--- a/src/sksl/SkSLCompiler.cpp
+++ b/src/sksl/SkSLCompiler.cpp
@@ -113,6 +113,9 @@
 
     ADD_TYPE(ISampler2D);
 
+    ADD_TYPE(Image2D);
+    ADD_TYPE(IImage2D);
+
     ADD_TYPE(GSampler1D);
     ADD_TYPE(GSampler2D);
     ADD_TYPE(GSampler3D);
diff --git a/src/sksl/SkSLContext.h b/src/sksl/SkSLContext.h
index 80ad7fb..a42a4cc 100644
--- a/src/sksl/SkSLContext.h
+++ b/src/sksl/SkSLContext.h
@@ -78,8 +78,14 @@
     , fSampler1DArrayShadow_Type(new Type("sampler1DArrayShadow"))
     , fSampler2DArrayShadow_Type(new Type("sampler2DArrayShadow"))
     , fSamplerCubeArrayShadow_Type(new Type("samplerCubeArrayShadow"))
+
     // Related to below FIXME, gsampler*s don't currently expand to cover integer case.
     , fISampler2D_Type(new Type("isampler2D", SpvDim2D, false, false, false, true))
+
+    // FIXME express these as "gimage2D" that expand to image2D, iimage2D, and uimage2D.
+    , fImage2D_Type(new Type("image2D", SpvDim2D, false, false, false, true))
+    , fIImage2D_Type(new Type("iimage2D", SpvDim2D, false, false, false, true))
+
     // FIXME figure out what we're supposed to do with the gsampler et al. types)
     , fGSampler1D_Type(new Type("$gsampler1D", static_type(*fSampler1D_Type)))
     , fGSampler2D_Type(new Type("$gsampler2D", static_type(*fSampler2D_Type)))
@@ -195,8 +201,12 @@
     const std::unique_ptr<Type> fSampler2DArrayShadow_Type;
     const std::unique_ptr<Type> fSamplerCubeArrayShadow_Type;
 
+
     const std::unique_ptr<Type> fISampler2D_Type;
 
+    const std::unique_ptr<Type> fImage2D_Type;
+    const std::unique_ptr<Type> fIImage2D_Type;
+
     const std::unique_ptr<Type> fGSampler1D_Type;
     const std::unique_ptr<Type> fGSampler2D_Type;
     const std::unique_ptr<Type> fGSampler3D_Type;
diff --git a/src/sksl/SkSLGLSLCodeGenerator.cpp b/src/sksl/SkSLGLSLCodeGenerator.cpp
index c6d2e6e..1252f86 100644
--- a/src/sksl/SkSLGLSLCodeGenerator.cpp
+++ b/src/sksl/SkSLGLSLCodeGenerator.cpp
@@ -474,6 +474,12 @@
             this->write(" = ");
             this->writeExpression(*var.fValue, kTopLevel_Precedence);
         }
+        if (!fFoundImageDecl && var.fVar->fType == *fContext.fImage2D_Type) {
+            if (fCaps.imageLoadStoreExtensionString()) {
+                fHeader << "#extension " << fCaps.imageLoadStoreExtensionString() << " : require\n";
+            }
+            fFoundImageDecl = true;
+        }
     }
     this->write(";");
 }
diff --git a/src/sksl/SkSLGLSLCodeGenerator.h b/src/sksl/SkSLGLSLCodeGenerator.h
index 5ed6104..16d6192 100644
--- a/src/sksl/SkSLGLSLCodeGenerator.h
+++ b/src/sksl/SkSLGLSLCodeGenerator.h
@@ -169,6 +169,7 @@
     std::vector<const Type*> fWrittenStructs;
     // true if we have run into usages of dFdx / dFdy
     bool fFoundDerivatives = false;
+    bool fFoundImageDecl = false;
 };
 
 }
diff --git a/src/sksl/SkSLParser.cpp b/src/sksl/SkSLParser.cpp
index 154989f..a3a88b0 100644
--- a/src/sksl/SkSLParser.cpp
+++ b/src/sksl/SkSLParser.cpp
@@ -537,11 +537,12 @@
     bool overrideCoverage = false;
     bool blendSupportAllEquations = false;
     bool pushConstant = false;
+    ASTLayout::Format format = ASTLayout::Format::kUnspecified;
     if (this->peek().fKind == Token::LAYOUT) {
         this->nextToken();
         if (!this->expect(Token::LPAREN, "'('")) {
             return ASTLayout(location, binding, index, set, builtin, originUpperLeft,
-                             overrideCoverage, blendSupportAllEquations, pushConstant);
+                             overrideCoverage, blendSupportAllEquations, pushConstant, format);
         }
         for (;;) {
             Token t = this->nextToken();
@@ -563,6 +564,8 @@
                 blendSupportAllEquations = true;
             } else if (t.fText == "push_constant") {
                 pushConstant = true;
+            } else if (ASTLayout::ReadFormat(t.fText, &format)) {
+               // AST::ReadFormat stored the result in 'format'.
             } else {
                 this->error(t.fPosition, ("'" + t.fText + 
                                           "' is not a valid layout qualifier").c_str());
@@ -577,7 +580,7 @@
         }
     }
     return ASTLayout(location, binding, index, set, builtin, originUpperLeft, overrideCoverage,
-                     blendSupportAllEquations, pushConstant);
+                     blendSupportAllEquations, pushConstant, format);
 }
 
 /* layout? (UNIFORM | CONST | IN | OUT | INOUT | LOWP | MEDIUMP | HIGHP | FLAT | NOPERSPECTIVE)* */
diff --git a/src/sksl/ast/SkSLASTLayout.h b/src/sksl/ast/SkSLASTLayout.h
index 2aec31e..e7bfd12 100644
--- a/src/sksl/ast/SkSLASTLayout.h
+++ b/src/sksl/ast/SkSLASTLayout.h
@@ -19,9 +19,68 @@
  * layout (location = 0) int x;
  */
 struct ASTLayout : public ASTNode {
+    // These are used by images in GLSL. We only support a subset of what GL supports.
+    enum class Format {
+        kUnspecified = -1,
+        kRGBA32F,
+        kR32F,
+        kRGBA16F,
+        kR16F,
+        kRGBA8,
+        kR8,
+        kRGBA8I,
+        kR8I,
+    };
+
+    static const char* FormatToStr(Format format) {
+        switch (format) {
+            case Format::kUnspecified:  return "";
+            case Format::kRGBA32F:      return "rgba32f";
+            case Format::kR32F:         return "r32f";
+            case Format::kRGBA16F:      return "rgba16f";
+            case Format::kR16F:         return "r16f";
+            case Format::kRGBA8:        return "rgba8";
+            case Format::kR8:           return "r8";
+            case Format::kRGBA8I:       return "rgba8i";
+            case Format::kR8I:          return "r8i";
+        }
+        SkFAIL("Unexpected format");
+        return "";
+    }
+
+    static bool ReadFormat(std::string str, Format* format) {
+        if (str == "rgba32f") {
+            *format = Format::kRGBA32F;
+            return true;
+        } else if (str == "r32f") {
+            *format = Format::kR32F;
+            return true;
+        } else if (str == "rgba16f") {
+            *format = Format::kRGBA16F;
+            return true;
+        } else if (str == "r16f") {
+            *format = Format::kR16F;
+            return true;
+        } else if (str == "rgba8") {
+            *format = Format::kRGBA8;
+            return true;
+        } else if (str == "r8") {
+            *format = Format::kR8;
+            return true;
+        } else if (str == "rgba8i") {
+            *format = Format::kRGBA8I;
+            return true;
+        } else if (str == "r8i") {
+            *format = Format::kR8I;
+            return true;
+        }
+        return false;
+    }
+
     // For int parameters, a -1 means no value
     ASTLayout(int location, int binding, int index, int set, int builtin, bool originUpperLeft,
-              bool overrideCoverage, bool blendSupportAllEquations, bool pushConstant)
+              bool overrideCoverage, bool blendSupportAllEquations, bool pushConstant,
+              Format format)
     : fLocation(location)
     , fBinding(binding)
     , fIndex(index)
@@ -30,7 +89,8 @@
     , fOriginUpperLeft(originUpperLeft)
     , fOverrideCoverage(overrideCoverage)
     , fBlendSupportAllEquations(blendSupportAllEquations)
-    , fPushConstant(pushConstant) {}
+    , fPushConstant(pushConstant)
+    , fFormat(format) {}
 
     std::string description() const {
         std::string result;
@@ -71,6 +131,10 @@
             result += separator + "push_constant";
             separator = ", ";
         }
+        if (fFormat != Format::kUnspecified) {
+            result += separator + FormatToStr(fFormat);
+            separator = ", ";
+        }
         if (result.length() > 0) {
             result = "layout (" + result + ")";
         }
@@ -86,6 +150,7 @@
     const bool fOverrideCoverage;
     const bool fBlendSupportAllEquations;
     const bool fPushConstant;
+    const Format fFormat;
 };
 
 } // namespace
diff --git a/src/sksl/ir/SkSLLayout.h b/src/sksl/ir/SkSLLayout.h
index dfa3967..ef2f8c2 100644
--- a/src/sksl/ir/SkSLLayout.h
+++ b/src/sksl/ir/SkSLLayout.h
@@ -25,10 +25,12 @@
     , fOriginUpperLeft(layout.fOriginUpperLeft)
     , fOverrideCoverage(layout.fOverrideCoverage)
     , fBlendSupportAllEquations(layout.fBlendSupportAllEquations)
-    , fPushConstant(layout.fPushConstant) {}
+    , fPushConstant(layout.fPushConstant)
+    , fFormat(layout.fFormat) {}
 
     Layout(int location, int binding, int index, int set, int builtin, bool originUpperLeft,
-           bool overrideCoverage, bool blendSupportAllEquations, bool pushconstant)
+           bool overrideCoverage, bool blendSupportAllEquations, bool pushconstant,
+           ASTLayout::Format format)
     : fLocation(location)
     , fBinding(binding)
     , fIndex(index)
@@ -37,7 +39,8 @@
     , fOriginUpperLeft(originUpperLeft)
     , fOverrideCoverage(overrideCoverage)
     , fBlendSupportAllEquations(blendSupportAllEquations)
-    , fPushConstant(pushconstant) {}
+    , fPushConstant(pushconstant)
+    , fFormat(format) {}
 
     Layout() 
     : fLocation(-1)
@@ -48,7 +51,8 @@
     , fOriginUpperLeft(false)
     , fOverrideCoverage(false)
     , fBlendSupportAllEquations(false)
-    , fPushConstant(false) {}
+    , fPushConstant(false)
+    , fFormat(ASTLayout::Format::kUnspecified) {}
 
     std::string description() const {
         std::string result;
@@ -89,6 +93,10 @@
             result += separator + "push_constant";
             separator = ", ";
         }
+        if (ASTLayout::Format::kUnspecified != fFormat) {
+            result += separator + ASTLayout::FormatToStr(fFormat);
+            separator = ", ";
+        }
         if (result.length() > 0) {
             result = "layout (" + result + ")";
         }
@@ -103,7 +111,8 @@
                fBuiltin                  == other.fBuiltin &&
                fOriginUpperLeft          == other.fOriginUpperLeft &&
                fOverrideCoverage         == other.fOverrideCoverage &&
-               fBlendSupportAllEquations == other.fBlendSupportAllEquations;
+               fBlendSupportAllEquations == other.fBlendSupportAllEquations &&
+               fFormat                   == other.fFormat;
     }
 
     bool operator!=(const Layout& other) const {
@@ -120,6 +129,7 @@
     bool fOverrideCoverage;
     bool fBlendSupportAllEquations;
     bool fPushConstant;
+    ASTLayout::Format fFormat;
 };
 
 } // namespace
diff --git a/src/sksl/sksl.include b/src/sksl/sksl.include
index 6458a15..83c6aed 100644
--- a/src/sksl/sksl.include
+++ b/src/sksl/sksl.include
@@ -537,8 +537,10 @@
 uint atomicCompSwap(inout uint mem, uint compare, uint data);
 int atomicCompSwap(inout int mem, int compare, int data);
 */
-// section 8.12 Image Functions will go here if and when we add support for them
-
+// section 8.12 Additional Image Functions will go here if and when we add
+// support for them
+vec4 imageLoad(image2D image, ivec2 P);
+ivec4 imageLoad(iimage2D image, ivec2 P);
 $genType dFdx($genType p);
 $genType dFdy($genType p);
 float interpolateAtSample(float interpolant, int sample);