Add support for input attachments in SkSL spirv

BUG=skia:

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

Change-Id: I3e03a465a10c9aff62491d0f6e71105d1b650dab
Reviewed-on: https://skia-review.googlesource.com/5115
Reviewed-by: Ethan Nicholas <ethannicholas@google.com>
Commit-Queue: Greg Daniel <egdaniel@google.com>
diff --git a/src/sksl/SkSLCompiler.cpp b/src/sksl/SkSLCompiler.cpp
index 62e5fa7..003f154 100644
--- a/src/sksl/SkSLCompiler.cpp
+++ b/src/sksl/SkSLCompiler.cpp
@@ -113,6 +113,9 @@
     ADD_TYPE(Image2D);
     ADD_TYPE(IImage2D);
 
+    ADD_TYPE(SubpassInput);
+    ADD_TYPE(SubpassInputMS);
+
     ADD_TYPE(GSampler1D);
     ADD_TYPE(GSampler2D);
     ADD_TYPE(GSampler3D);
diff --git a/src/sksl/SkSLContext.h b/src/sksl/SkSLContext.h
index 9289ede..6284c21 100644
--- a/src/sksl/SkSLContext.h
+++ b/src/sksl/SkSLContext.h
@@ -88,6 +88,13 @@
     , fImage2D_Type(new Type(SkString("image2D"), SpvDim2D, false, false, false, true))
     , fIImage2D_Type(new Type(SkString("iimage2D"), SpvDim2D, false, false, false, true))
 
+    // FIXME express these as "gsubpassInput" that expand to subpassInput, isubpassInput,
+    // and usubpassInput.
+    , fSubpassInput_Type(new Type(SkString("subpassInput"), SpvDimSubpassData, false, false,
+                                           false, false))
+    , fSubpassInputMS_Type(new Type(SkString("subpassInputMS"), SpvDimSubpassData, false, false,
+                                             true, false))
+
     // FIXME figure out what we're supposed to do with the gsampler et al. types)
     , fGSampler1D_Type(new Type(SkString("$gsampler1D"), static_type(*fSampler1D_Type)))
     , fGSampler2D_Type(new Type(SkString("$gsampler2D"), static_type(*fSampler2D_Type)))
@@ -137,7 +144,7 @@
     , fDefined_Expression(new Defined(*fInvalid_Type)) {}
 
     static std::vector<const Type*> static_type(const Type& t) {
-        return { &t, &t, &t, &t };   
+        return { &t, &t, &t, &t };
     }
 
     const std::unique_ptr<Type> fInvalid_Type;
@@ -214,6 +221,9 @@
     const std::unique_ptr<Type> fImage2D_Type;
     const std::unique_ptr<Type> fIImage2D_Type;
 
+    const std::unique_ptr<Type> fSubpassInput_Type;
+    const std::unique_ptr<Type> fSubpassInputMS_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 5536261..e4aec92 100644
--- a/src/sksl/SkSLGLSLCodeGenerator.cpp
+++ b/src/sksl/SkSLGLSLCodeGenerator.cpp
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #include "SkSLGLSLCodeGenerator.h"
 
 #include "string.h"
@@ -392,7 +392,7 @@
     this->write(SkString((const char*) data->data(), data->size()));
 }
 
-void GLSLCodeGenerator::writeModifiers(const Modifiers& modifiers, 
+void GLSLCodeGenerator::writeModifiers(const Modifiers& modifiers,
                                        bool globalContext) {
     if (modifiers.fFlags & Modifiers::kNoPerspective_Flag) {
         this->write("noperspective ");
@@ -404,7 +404,7 @@
     if (layout.size()) {
         this->write(layout + " ");
     }
-    if ((modifiers.fFlags & Modifiers::kIn_Flag) && 
+    if ((modifiers.fFlags & Modifiers::kIn_Flag) &&
         (modifiers.fFlags & Modifiers::kOut_Flag)) {
         this->write("inout ");
     } else if (modifiers.fFlags & Modifiers::kIn_Flag) {
diff --git a/src/sksl/SkSLParser.cpp b/src/sksl/SkSLParser.cpp
index 03d0d33..58ec750 100644
--- a/src/sksl/SkSLParser.cpp
+++ b/src/sksl/SkSLParser.cpp
@@ -535,7 +535,7 @@
     return -1;
 }
 
-/* LAYOUT LPAREN IDENTIFIER EQ INT_LITERAL (COMMA IDENTIFIER EQ INT_LITERAL)* 
+/* LAYOUT LPAREN IDENTIFIER EQ INT_LITERAL (COMMA IDENTIFIER EQ INT_LITERAL)*
    RPAREN */
 ASTLayout Parser::layout() {
     int location = -1;
@@ -543,6 +543,7 @@
     int index = -1;
     int set = -1;
     int builtin = -1;
+    int inputAttachmentIndex = -1;
     bool originUpperLeft = false;
     bool overrideCoverage = false;
     bool blendSupportAllEquations = false;
@@ -550,8 +551,8 @@
     if (this->peek().fKind == Token::LAYOUT) {
         this->nextToken();
         if (!this->expect(Token::LPAREN, "'('")) {
-            return ASTLayout(location, binding, index, set, builtin, originUpperLeft,
-                             overrideCoverage, blendSupportAllEquations, format);
+            return ASTLayout(location, binding, index, set, builtin, inputAttachmentIndex,
+                             originUpperLeft, overrideCoverage, blendSupportAllEquations, format);
         }
         for (;;) {
             Token t = this->nextToken();
@@ -565,6 +566,8 @@
                 set = this->layoutInt();
             } else if (t.fText == "builtin") {
                 builtin = this->layoutInt();
+            } else if (t.fText == "input_attachment_index") {
+                 inputAttachmentIndex = this->layoutInt();
             } else if (t.fText == "origin_upper_left") {
                 originUpperLeft = true;
             } else if (t.fText == "override_coverage") {
@@ -574,7 +577,7 @@
             } else if (ASTLayout::ReadFormat(t.fText, &format)) {
                // AST::ReadFormat stored the result in 'format'.
             } else {
-                this->error(t.fPosition, ("'" + t.fText + 
+                this->error(t.fPosition, ("'" + t.fText +
                                           "' is not a valid layout qualifier").c_str());
             }
             if (this->peek().fKind == Token::RPAREN) {
@@ -586,8 +589,8 @@
             }
         }
     }
-    return ASTLayout(location, binding, index, set, builtin, originUpperLeft, overrideCoverage,
-                     blendSupportAllEquations, format);
+    return ASTLayout(location, binding, index, set, builtin, inputAttachmentIndex, originUpperLeft,
+                     overrideCoverage, blendSupportAllEquations, format);
 }
 
 /* layout? (UNIFORM | CONST | IN | OUT | INOUT | LOWP | MEDIUMP | HIGHP | FLAT | NOPERSPECTIVE)* */
diff --git a/src/sksl/SkSLSPIRVCodeGenerator.cpp b/src/sksl/SkSLSPIRVCodeGenerator.cpp
index b5cd54a..300d3c5 100644
--- a/src/sksl/SkSLSPIRVCodeGenerator.cpp
+++ b/src/sksl/SkSLSPIRVCodeGenerator.cpp
@@ -106,9 +106,11 @@
     fIntrinsicMap[SkString("texture2D")]   = SPECIAL(Texture2D);
     fIntrinsicMap[SkString("textureProj")] = SPECIAL(TextureProj);
 
-    fIntrinsicMap[SkString("any")]              = std::make_tuple(kSPIRV_IntrinsicKind, SpvOpUndef, 
+    fIntrinsicMap[SkString("subpassLoad")] = SPECIAL(SubpassLoad);
+
+    fIntrinsicMap[SkString("any")]              = std::make_tuple(kSPIRV_IntrinsicKind, SpvOpUndef,
                                                                   SpvOpUndef, SpvOpUndef, SpvOpAny);
-    fIntrinsicMap[SkString("all")]              = std::make_tuple(kSPIRV_IntrinsicKind, SpvOpUndef, 
+    fIntrinsicMap[SkString("all")]              = std::make_tuple(kSPIRV_IntrinsicKind, SpvOpUndef,
                                                                   SpvOpUndef, SpvOpUndef, SpvOpAll);
     fIntrinsicMap[SkString("equal")]            = std::make_tuple(kSPIRV_IntrinsicKind,
                                                                   SpvOpFOrdEqual, SpvOpIEqual,
@@ -121,18 +123,18 @@
                                                                   SpvOpSLessThan, SpvOpULessThan,
                                                                   SpvOpFOrdLessThan, SpvOpUndef);
     fIntrinsicMap[SkString("lessThanEqual")]    = std::make_tuple(kSPIRV_IntrinsicKind,
-                                                                  SpvOpSLessThanEqual, 
+                                                                  SpvOpSLessThanEqual,
                                                                   SpvOpULessThanEqual,
                                                                   SpvOpFOrdLessThanEqual,
                                                                   SpvOpUndef);
     fIntrinsicMap[SkString("greaterThan")]      = std::make_tuple(kSPIRV_IntrinsicKind,
-                                                                  SpvOpSGreaterThan, 
+                                                                  SpvOpSGreaterThan,
                                                                   SpvOpUGreaterThan,
-                                                                  SpvOpFOrdGreaterThan, 
+                                                                  SpvOpFOrdGreaterThan,
                                                                   SpvOpUndef);
-    fIntrinsicMap[SkString("greaterThanEqual")] = std::make_tuple(kSPIRV_IntrinsicKind, 
-                                                                  SpvOpSGreaterThanEqual, 
-                                                                  SpvOpUGreaterThanEqual, 
+    fIntrinsicMap[SkString("greaterThanEqual")] = std::make_tuple(kSPIRV_IntrinsicKind,
+                                                                  SpvOpSGreaterThanEqual,
+                                                                  SpvOpUGreaterThanEqual,
                                                                   SpvOpFOrdGreaterThanEqual,
                                                                   SpvOpUndef);
 
@@ -869,7 +871,7 @@
 }
 
 
-void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, const char* string, 
+void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, const char* string,
                                           SkWStream& out) {
     int32_t length = (int32_t) strlen(string);
     this->writeOpCode(opCode, 2 + (length + 4) / 4, out);
@@ -877,7 +879,7 @@
     this->writeString(string, out);
 }
 
-void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, 
+void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
                                           const char* string, SkWStream& out) {
     int32_t length = (int32_t) strlen(string);
     this->writeOpCode(opCode, 3 + (length + 4) / 4, out);
@@ -886,14 +888,14 @@
     this->writeString(string, out);
 }
 
-void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, 
+void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
                                           SkWStream& out) {
     this->writeOpCode(opCode, 3, out);
     this->writeWord(word1, out);
     this->writeWord(word2, out);
 }
 
-void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, 
+void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
                                           int32_t word3, SkWStream& out) {
     this->writeOpCode(opCode, 4, out);
     this->writeWord(word1, out);
@@ -901,7 +903,7 @@
     this->writeWord(word3, out);
 }
 
-void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, 
+void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
                                           int32_t word3, int32_t word4, SkWStream& out) {
     this->writeOpCode(opCode, 5, out);
     this->writeWord(word1, out);
@@ -910,8 +912,8 @@
     this->writeWord(word4, out);
 }
 
-void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, 
-                                          int32_t word3, int32_t word4, int32_t word5, 
+void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
+                                          int32_t word3, int32_t word4, int32_t word5,
                                           SkWStream& out) {
     this->writeOpCode(opCode, 6, out);
     this->writeWord(word1, out);
@@ -921,7 +923,7 @@
     this->writeWord(word5, out);
 }
 
-void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, 
+void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
                                           int32_t word3, int32_t word4, int32_t word5,
                                           int32_t word6, SkWStream& out) {
     this->writeOpCode(opCode, 7, out);
@@ -933,7 +935,7 @@
     this->writeWord(word6, out);
 }
 
-void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, 
+void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
                                           int32_t word3, int32_t word4, int32_t word5,
                                           int32_t word6, int32_t word7, SkWStream& out) {
     this->writeOpCode(opCode, 8, out);
@@ -946,7 +948,7 @@
     this->writeWord(word7, out);
 }
 
-void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, 
+void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
                                           int32_t word3, int32_t word4, int32_t word5,
                                           int32_t word6, int32_t word7, int32_t word8,
                                           SkWStream& out) {
@@ -998,13 +1000,13 @@
                                fNameBuffer);
         this->writeLayout(type.fields()[i].fModifiers.fLayout, resultId, i);
         if (type.fields()[i].fModifiers.fLayout.fBuiltin < 0) {
-            this->writeInstruction(SpvOpMemberDecorate, resultId, (SpvId) i, SpvDecorationOffset, 
+            this->writeInstruction(SpvOpMemberDecorate, resultId, (SpvId) i, SpvDecorationOffset,
                                    (SpvId) offset, fDecorationBuffer);
         }
         if (type.fields()[i].fType->kind() == Type::kMatrix_Kind) {
-            this->writeInstruction(SpvOpMemberDecorate, resultId, i, SpvDecorationColMajor, 
+            this->writeInstruction(SpvOpMemberDecorate, resultId, i, SpvDecorationColMajor,
                                    fDecorationBuffer);
-            this->writeInstruction(SpvOpMemberDecorate, resultId, i, SpvDecorationMatrixStride, 
+            this->writeInstruction(SpvOpMemberDecorate, resultId, i, SpvDecorationMatrixStride,
                                    (SpvId) type.fields()[i].fType->stride(), fDecorationBuffer);
         }
         offset += size;
@@ -1036,13 +1038,13 @@
                 }
                 break;
             case Type::kVector_Kind:
-                this->writeInstruction(SpvOpTypeVector, result, 
+                this->writeInstruction(SpvOpTypeVector, result,
                                        this->getType(type.componentType()),
                                        type.columns(), fConstantBuffer);
                 break;
             case Type::kMatrix_Kind:
-                this->writeInstruction(SpvOpTypeMatrix, result, 
-                                       this->getType(index_type(fContext, type)), 
+                this->writeInstruction(SpvOpTypeMatrix, result,
+                                       this->getType(index_type(fContext, type)),
                                        type.columns(), fConstantBuffer);
                 break;
             case Type::kStruct_Kind:
@@ -1051,25 +1053,30 @@
             case Type::kArray_Kind: {
                 if (type.columns() > 0) {
                     IntLiteral count(fContext, Position(), type.columns());
-                    this->writeInstruction(SpvOpTypeArray, result, 
-                                           this->getType(type.componentType()), 
+                    this->writeInstruction(SpvOpTypeArray, result,
+                                           this->getType(type.componentType()),
                                            this->writeIntLiteral(count), fConstantBuffer);
-                    this->writeInstruction(SpvOpDecorate, result, SpvDecorationArrayStride, 
+                    this->writeInstruction(SpvOpDecorate, result, SpvDecorationArrayStride,
                                            (int32_t) type.stride(), fDecorationBuffer);
                 } else {
                     ABORT("runtime-sized arrays are not yet supported");
-                    this->writeInstruction(SpvOpTypeRuntimeArray, result, 
+                    this->writeInstruction(SpvOpTypeRuntimeArray, result,
                                            this->getType(type.componentType()), fConstantBuffer);
                 }
                 break;
             }
             case Type::kSampler_Kind: {
-                SpvId image = this->nextId();
-                this->writeInstruction(SpvOpTypeImage, image, this->getType(*fContext.fFloat_Type), 
+                SpvId image = result;
+                if (SpvDimSubpassData != type.dimensions()) {
+                    image = this->nextId();
+                }
+                this->writeInstruction(SpvOpTypeImage, image, this->getType(*fContext.fFloat_Type),
                                        type.dimensions(), type.isDepth(), type.isArrayed(),
-                                       type.isMultisampled(), type.isSampled(), 
+                                       type.isMultisampled(), type.isSampled() ? 1 : 2,
                                        SpvImageFormatUnknown, fConstantBuffer);
-                this->writeInstruction(SpvOpTypeSampledImage, result, image, fConstantBuffer);
+                if (SpvDimSubpassData != type.dimensions()) {
+                    this->writeInstruction(SpvOpTypeSampledImage, result, image, fConstantBuffer);
+                }
                 break;
             }
             default:
@@ -1101,26 +1108,26 @@
         SpvId returnType = this->getType(function.fReturnType);
         std::vector<SpvId> parameterTypes;
         for (size_t i = 0; i < function.fParameters.size(); i++) {
-            // glslang seems to treat all function arguments as pointers whether they need to be or 
-            // not. I  was initially puzzled by this until I ran bizarre failures with certain 
-            // patterns of function calls and control constructs, as exemplified by this minimal 
+            // glslang seems to treat all function arguments as pointers whether they need to be or
+            // not. I  was initially puzzled by this until I ran bizarre failures with certain
+            // patterns of function calls and control constructs, as exemplified by this minimal
             // failure case:
             //
             // void sphere(float x) {
             // }
-            // 
+            //
             // void map() {
             //     sphere(1.0);
             // }
-            // 
+            //
             // void main() {
             //     for (int i = 0; i < 1; i++) {
             //         map();
             //     }
             // }
             //
-            // As of this writing, compiling this in the "obvious" way (with sphere taking a float) 
-            // crashes. Making it take a float* and storing the argument in a temporary variable, 
+            // As of this writing, compiling this in the "obvious" way (with sphere taking a float)
+            // crashes. Making it take a float* and storing the argument in a temporary variable,
             // as glslang does, fixes it. It's entirely possible I simply missed whichever part of
             // the spec makes this make sense.
 //            if (is_out(function->fParameters[i])) {
@@ -1142,13 +1149,13 @@
     return entry->second;
 }
 
-SpvId SPIRVCodeGenerator::getPointerType(const Type& type, 
+SpvId SPIRVCodeGenerator::getPointerType(const Type& type,
                                          SpvStorageClass_ storageClass) {
     SkString key = type.description() + "*" + to_string(storageClass);
     auto entry = fTypeMap.find(key);
     if (entry == fTypeMap.end()) {
         SpvId result = this->nextId();
-        this->writeInstruction(SpvOpTypePointer, result, storageClass, 
+        this->writeInstruction(SpvOpTypePointer, result, storageClass,
                                this->getType(type), fConstantBuffer);
         fTypeMap[key] = result;
         return result;
@@ -1245,7 +1252,7 @@
     }
 }
 
-SpvId SPIRVCodeGenerator::writeSpecialIntrinsic(const FunctionCall& c, SpecialIntrinsic kind, 
+SpvId SPIRVCodeGenerator::writeSpecialIntrinsic(const FunctionCall& c, SpecialIntrinsic kind,
                                                 SkWStream& out) {
     SpvId result = this->nextId();
     switch (kind) {
@@ -1262,7 +1269,7 @@
             for (SpvId id : arguments) {
                 this->writeWord(id, out);
             }
-            return result;            
+            return result;
         }
         case kTexture_SpecialIntrinsic: {
             SpvId type = this->getType(c.fType);
@@ -1290,7 +1297,7 @@
                                        out);
             } else {
                 ASSERT(c.fArguments.size() == 2);
-                this->writeInstruction(SpvOpImageSampleProjImplicitLod, type, result, sampler, uv, 
+                this->writeInstruction(SpvOpImageSampleProjImplicitLod, type, result, sampler, uv,
                                        out);
             }
             break;
@@ -1300,12 +1307,40 @@
             SpvId coords = this->writeExpression(*c.fArguments[1], out);
             this->writeInstruction(SpvOpImageSampleImplicitLod,
                                    this->getType(c.fType),
-                                   result, 
+                                   result,
                                    img,
                                    coords,
                                    out);
             break;
         }
+        case kSubpassLoad_SpecialIntrinsic: {
+            SpvId img = this->writeExpression(*c.fArguments[0], out);
+            std::vector<std::unique_ptr<Expression>> args;
+            args.emplace_back(new FloatLiteral(fContext, Position(), 0.0));
+            args.emplace_back(new FloatLiteral(fContext, Position(), 0.0));
+            Constructor ctor(Position(), *fContext.fVec2_Type, std::move(args));
+            SpvId coords = this->writeConstantVector(ctor);
+            if (1 == c.fArguments.size()) {
+                this->writeInstruction(SpvOpImageRead,
+                                       this->getType(c.fType),
+                                       result,
+                                       img,
+                                       coords,
+                                       out);
+            } else {
+                SkASSERT(2 == c.fArguments.size());
+                SpvId sample = this->writeExpression(*c.fArguments[1], out);
+                this->writeInstruction(SpvOpImageRead,
+                                       this->getType(c.fType),
+                                       result,
+                                       img,
+                                       coords,
+                                       SpvImageOperandsSampleMask,
+                                       sample,
+                                       out);
+            }
+            break;
+        }
     }
     return result;
 }
@@ -1319,7 +1354,7 @@
     std::vector<std::tuple<SpvId, SpvId, std::unique_ptr<LValue>>> lvalues;
     std::vector<SpvId> arguments;
     for (size_t i = 0; i < c.fArguments.size(); i++) {
-        // id of temporary variable that we will use to hold this argument, or 0 if it is being 
+        // id of temporary variable that we will use to hold this argument, or 0 if it is being
         // passed directly
         SpvId tmpVar;
         // if we need a temporary var to store this argument, this is the value to store in the var
@@ -1344,10 +1379,10 @@
             tmpValueId = this->writeExpression(*c.fArguments[i], out);
             tmpVar = this->nextId();
         }
-        this->writeInstruction(SpvOpVariable, 
-                               this->getPointerType(c.fArguments[i]->fType, 
+        this->writeInstruction(SpvOpVariable,
+                               this->getPointerType(c.fArguments[i]->fType,
                                                     SpvStorageClassFunction),
-                               tmpVar, 
+                               tmpVar,
                                SpvStorageClassFunction,
                                fVariableBuffer);
         this->writeInstruction(SpvOpStore, tmpVar, tmpValueId, out);
@@ -1388,7 +1423,7 @@
             this->writeWord(arguments[0], fConstantBuffer);
         }
     } else {
-        this->writeOpCode(SpvOpConstantComposite, 3 + (int32_t) c.fArguments.size(), 
+        this->writeOpCode(SpvOpConstantComposite, 3 + (int32_t) c.fArguments.size(),
                           fConstantBuffer);
         this->writeWord(type, fConstantBuffer);
         this->writeWord(result, fConstantBuffer);
@@ -1406,10 +1441,10 @@
     SpvId result = this->nextId();
     SpvId parameter = this->writeExpression(*c.fArguments[0], out);
     if (c.fArguments[0]->fType == *fContext.fInt_Type) {
-        this->writeInstruction(SpvOpConvertSToF, this->getType(c.fType), result, parameter, 
+        this->writeInstruction(SpvOpConvertSToF, this->getType(c.fType), result, parameter,
                                out);
     } else if (c.fArguments[0]->fType == *fContext.fUInt_Type) {
-        this->writeInstruction(SpvOpConvertUToF, this->getType(c.fType), result, parameter, 
+        this->writeInstruction(SpvOpConvertUToF, this->getType(c.fType), result, parameter,
                                out);
     } else if (c.fArguments[0]->fType == *fContext.fFloat_Type) {
         return parameter;
@@ -1424,10 +1459,10 @@
     SpvId result = this->nextId();
     SpvId parameter = this->writeExpression(*c.fArguments[0], out);
     if (c.fArguments[0]->fType == *fContext.fFloat_Type) {
-        this->writeInstruction(SpvOpConvertFToS, this->getType(c.fType), result, parameter, 
+        this->writeInstruction(SpvOpConvertFToS, this->getType(c.fType), result, parameter,
                                out);
     } else if (c.fArguments[0]->fType == *fContext.fUInt_Type) {
-        this->writeInstruction(SpvOpSatConvertUToS, this->getType(c.fType), result, parameter, 
+        this->writeInstruction(SpvOpSatConvertUToS, this->getType(c.fType), result, parameter,
                                out);
     } else if (c.fArguments[0]->fType == *fContext.fInt_Type) {
         return parameter;
@@ -1448,16 +1483,16 @@
     int columns = c.fType.columns();
     // FIXME this won't work to create a matrix from another matrix
     if (arguments.size() == 1) {
-        // with a single argument, a matrix will have all of its diagonal entries equal to the 
+        // with a single argument, a matrix will have all of its diagonal entries equal to the
         // argument and its other values equal to zero
         // FIXME this won't work for int matrices
         FloatLiteral zero(fContext, Position(), 0);
         SpvId zeroId = this->writeFloatLiteral(zero);
         std::vector<SpvId> columnIds;
         for (int column = 0; column < columns; column++) {
-            this->writeOpCode(SpvOpCompositeConstruct, 3 + c.fType.rows(), 
+            this->writeOpCode(SpvOpCompositeConstruct, 3 + c.fType.rows(),
                               out);
-            this->writeWord(this->getType(c.fType.componentType().toCompound(fContext, rows, 1)), 
+            this->writeWord(this->getType(c.fType.componentType().toCompound(fContext, rows, 1)),
                             out);
             SpvId columnId = this->nextId();
             this->writeWord(columnId, out);
@@ -1466,7 +1501,7 @@
                 this->writeWord(row == column ? arguments[0] : zeroId, out);
             }
         }
-        this->writeOpCode(SpvOpCompositeConstruct, 3 + columns, 
+        this->writeOpCode(SpvOpCompositeConstruct, 3 + columns,
                           out);
         this->writeWord(this->getType(c.fType), out);
         this->writeWord(result, out);
@@ -1485,8 +1520,8 @@
                 ASSERT(c.fArguments[i]->fType.kind() == Type::kScalar_Kind);
                 if (currentCount == 0) {
                     this->writeOpCode(SpvOpCompositeConstruct, 3 + c.fType.rows(), out);
-                    this->writeWord(this->getType(c.fType.componentType().toCompound(fContext, rows, 
-                                                                                     1)), 
+                    this->writeWord(this->getType(c.fType.componentType().toCompound(fContext, rows,
+                                                                                     1)),
                                     out);
                     SpvId id = this->nextId();
                     this->writeWord(id, out);
@@ -1602,7 +1637,7 @@
 
 class PointerLValue : public SPIRVCodeGenerator::LValue {
 public:
-    PointerLValue(SPIRVCodeGenerator& gen, SpvId pointer, SpvId type) 
+    PointerLValue(SPIRVCodeGenerator& gen, SpvId pointer, SpvId type)
     : fGen(gen)
     , fPointer(pointer)
     , fType(type) {}
@@ -1629,7 +1664,7 @@
 
 class SwizzleLValue : public SPIRVCodeGenerator::LValue {
 public:
-    SwizzleLValue(SPIRVCodeGenerator& gen, SpvId vecPointer, const std::vector<int>& components, 
+    SwizzleLValue(SPIRVCodeGenerator& gen, SpvId vecPointer, const std::vector<int>& components,
                   const Type& baseType, const Type& swizzleType)
     : fGen(gen)
     , fVecPointer(vecPointer)
@@ -1659,12 +1694,12 @@
     virtual void store(SpvId value, SkWStream& out) override {
         // use OpVectorShuffle to mix and match the vector components. We effectively create
         // a virtual vector out of the concatenation of the left and right vectors, and then
-        // select components from this virtual vector to make the result vector. For 
+        // select components from this virtual vector to make the result vector. For
         // instance, given:
         // vec3 L = ...;
         // vec3 R = ...;
         // L.xz = R.xy;
-        // we end up with the virtual vector (L.x, L.y, L.z, R.x, R.y, R.z). Then we want 
+        // we end up with the virtual vector (L.x, L.y, L.z, R.x, R.y, R.z). Then we want
         // our result vector to look like (R.x, L.y, R.y), so we need to select indices
         // (3, 1, 4).
         SpvId base = fGen.nextId();
@@ -1682,7 +1717,7 @@
             // check to see if we are writing this component
             for (size_t j = 0; j < fComponents.size(); j++) {
                 if (fComponents[j] == i) {
-                    // we're writing to this component, so adjust the offset to pull from 
+                    // we're writing to this component, so adjust the offset to pull from
                     // the correct component of the right side instead of preserving the
                     // value from the left
                     offset = (int) (j + fBaseType.columns());
@@ -1702,7 +1737,7 @@
     const Type& fSwizzleType;
 };
 
-std::unique_ptr<SPIRVCodeGenerator::LValue> SPIRVCodeGenerator::getLValue(const Expression& expr, 
+std::unique_ptr<SPIRVCodeGenerator::LValue> SPIRVCodeGenerator::getLValue(const Expression& expr,
                                                                           SkWStream& out) {
     switch (expr.fKind) {
         case Expression::kVariableReference_Kind: {
@@ -1711,7 +1746,7 @@
             ASSERT(entry != fVariableMap.end());
             return std::unique_ptr<SPIRVCodeGenerator::LValue>(new PointerLValue(
                                                                        *this,
-                                                                       entry->second, 
+                                                                       entry->second,
                                                                        this->getType(expr.fType)));
         }
         case Expression::kIndex_Kind: // fall through
@@ -1719,14 +1754,14 @@
             std::vector<SpvId> chain = this->getAccessChain(expr, out);
             SpvId member = this->nextId();
             this->writeOpCode(SpvOpAccessChain, (SpvId) (3 + chain.size()), out);
-            this->writeWord(this->getPointerType(expr.fType, get_storage_class(expr)), out); 
+            this->writeWord(this->getPointerType(expr.fType, get_storage_class(expr)), out);
             this->writeWord(member, out);
             for (SpvId idx : chain) {
                 this->writeWord(idx, out);
             }
             return std::unique_ptr<SPIRVCodeGenerator::LValue>(new PointerLValue(
                                                                        *this,
-                                                                       member, 
+                                                                       member,
                                                                        this->getType(expr.fType)));
         }
 
@@ -1739,21 +1774,21 @@
                 IntLiteral index(fContext, Position(), swizzle.fComponents[0]);
                 SpvId member = this->nextId();
                 this->writeInstruction(SpvOpAccessChain,
-                                       this->getPointerType(swizzle.fType, 
-                                                            get_storage_class(*swizzle.fBase)), 
-                                       member, 
-                                       base, 
-                                       this->writeIntLiteral(index), 
+                                       this->getPointerType(swizzle.fType,
+                                                            get_storage_class(*swizzle.fBase)),
+                                       member,
+                                       base,
+                                       this->writeIntLiteral(index),
                                        out);
                 return std::unique_ptr<SPIRVCodeGenerator::LValue>(new PointerLValue(
                                                                        *this,
-                                                                       member, 
+                                                                       member,
                                                                        this->getType(expr.fType)));
             } else {
                 return std::unique_ptr<SPIRVCodeGenerator::LValue>(new SwizzleLValue(
-                                                                              *this, 
-                                                                              base, 
-                                                                              swizzle.fComponents, 
+                                                                              *this,
+                                                                              base,
+                                                                              swizzle.fComponents,
                                                                               swizzle.fBase->fType,
                                                                               expr.fType));
             }
@@ -1761,7 +1796,7 @@
 
         default:
             // expr isn't actually an lvalue, create a dummy variable for it. This case happens due
-            // to the need to store values in temporary variables during function calls (see 
+            // to the need to store values in temporary variables during function calls (see
             // comments in getFunctionType); erroneous uses of rvalues as lvalues should have been
             // caught by IRGenerator
             SpvId result = this->nextId();
@@ -1771,7 +1806,7 @@
             this->writeInstruction(SpvOpStore, result, this->writeExpression(expr, out), out);
             return std::unique_ptr<SPIRVCodeGenerator::LValue>(new PointerLValue(
                                                                        *this,
-                                                                       result, 
+                                                                       result,
                                                                        this->getType(expr.fType)));
     }
 }
@@ -1798,8 +1833,8 @@
     SpvId result = this->nextId();
     size_t count = swizzle.fComponents.size();
     if (count == 1) {
-        this->writeInstruction(SpvOpCompositeExtract, this->getType(swizzle.fType), result, base, 
-                               swizzle.fComponents[0], out); 
+        this->writeInstruction(SpvOpCompositeExtract, this->getType(swizzle.fType), result, base,
+                               swizzle.fComponents[0], out);
     } else {
         this->writeOpCode(SpvOpVectorShuffle, 5 + (int32_t) count, out);
         this->writeWord(this->getType(swizzle.fType), out);
@@ -1813,9 +1848,9 @@
     return result;
 }
 
-SpvId SPIRVCodeGenerator::writeBinaryOperation(const Type& resultType, 
-                                               const Type& operandType, SpvId lhs, 
-                                               SpvId rhs, SpvOp_ ifFloat, SpvOp_ ifInt, 
+SpvId SPIRVCodeGenerator::writeBinaryOperation(const Type& resultType,
+                                               const Type& operandType, SpvId lhs,
+                                               SpvId rhs, SpvOp_ ifFloat, SpvOp_ ifInt,
                                                SpvOp_ ifUInt, SpvOp_ ifBool, SkWStream& out) {
     SpvId result = this->nextId();
     if (is_float(fContext, operandType)) {
@@ -1887,7 +1922,7 @@
     // IR allows mismatched types in expressions (e.g. vec2 * float), but they need special handling
     // in SPIR-V
     if (b.fLeft->fType != b.fRight->fType) {
-        if (b.fLeft->fType.kind() == Type::kVector_Kind && 
+        if (b.fLeft->fType.kind() == Type::kVector_Kind &&
             b.fRight->fType.isNumber()) {
             // promote number to vector
             SpvId vec = this->nextId();
@@ -1899,7 +1934,7 @@
             }
             rhs = vec;
             operandType = &b.fRight->fType;
-        } else if (b.fRight->fType.kind() == Type::kVector_Kind && 
+        } else if (b.fRight->fType.kind() == Type::kVector_Kind &&
                    b.fLeft->fType.isNumber()) {
             // promote number to vector
             SpvId vec = this->nextId();
@@ -1933,11 +1968,11 @@
         } else if (b.fRight->fType.kind() == Type::kMatrix_Kind) {
             SpvId result = this->nextId();
             if (b.fLeft->fType.kind() == Type::kVector_Kind) {
-                this->writeInstruction(SpvOpVectorTimesMatrix, this->getType(b.fType), result, 
+                this->writeInstruction(SpvOpVectorTimesMatrix, this->getType(b.fType), result,
                                        lhs, rhs, out);
             } else {
                 ASSERT(b.fLeft->fType.kind() == Type::kScalar_Kind);
-                this->writeInstruction(SpvOpMatrixTimesScalar, this->getType(b.fType), result, rhs, 
+                this->writeInstruction(SpvOpMatrixTimesScalar, this->getType(b.fType), result, rhs,
                                        lhs, out);
             }
             if (b.fOperator == Token::STAREQ) {
@@ -1956,40 +1991,40 @@
     switch (b.fOperator) {
         case Token::EQEQ:
             ASSERT(resultType == *fContext.fBool_Type);
-            return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFOrdEqual, 
+            return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFOrdEqual,
                                               SpvOpIEqual, SpvOpIEqual, SpvOpLogicalEqual, out);
         case Token::NEQ:
             ASSERT(resultType == *fContext.fBool_Type);
-            return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFOrdNotEqual, 
-                                              SpvOpINotEqual, SpvOpINotEqual, SpvOpLogicalNotEqual, 
+            return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFOrdNotEqual,
+                                              SpvOpINotEqual, SpvOpINotEqual, SpvOpLogicalNotEqual,
                                               out);
         case Token::GT:
             ASSERT(resultType == *fContext.fBool_Type);
-            return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, 
-                                              SpvOpFOrdGreaterThan, SpvOpSGreaterThan, 
+            return this->writeBinaryOperation(resultType, *operandType, lhs, rhs,
+                                              SpvOpFOrdGreaterThan, SpvOpSGreaterThan,
                                               SpvOpUGreaterThan, SpvOpUndef, out);
         case Token::LT:
             ASSERT(resultType == *fContext.fBool_Type);
-            return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFOrdLessThan, 
+            return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFOrdLessThan,
                                               SpvOpSLessThan, SpvOpULessThan, SpvOpUndef, out);
         case Token::GTEQ:
             ASSERT(resultType == *fContext.fBool_Type);
-            return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, 
-                                              SpvOpFOrdGreaterThanEqual, SpvOpSGreaterThanEqual, 
+            return this->writeBinaryOperation(resultType, *operandType, lhs, rhs,
+                                              SpvOpFOrdGreaterThanEqual, SpvOpSGreaterThanEqual,
                                               SpvOpUGreaterThanEqual, SpvOpUndef, out);
         case Token::LTEQ:
             ASSERT(resultType == *fContext.fBool_Type);
-            return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, 
-                                              SpvOpFOrdLessThanEqual, SpvOpSLessThanEqual, 
+            return this->writeBinaryOperation(resultType, *operandType, lhs, rhs,
+                                              SpvOpFOrdLessThanEqual, SpvOpSLessThanEqual,
                                               SpvOpULessThanEqual, SpvOpUndef, out);
         case Token::PLUS:
-            return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFAdd, 
+            return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFAdd,
                                               SpvOpIAdd, SpvOpIAdd, SpvOpUndef, out);
         case Token::MINUS:
-            return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFSub, 
+            return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFSub,
                                               SpvOpISub, SpvOpISub, SpvOpUndef, out);
         case Token::STAR:
-            if (b.fLeft->fType.kind() == Type::kMatrix_Kind && 
+            if (b.fLeft->fType.kind() == Type::kMatrix_Kind &&
                 b.fRight->fType.kind() == Type::kMatrix_Kind) {
                 // matrix multiply
                 SpvId result = this->nextId();
@@ -1997,27 +2032,27 @@
                                        lhs, rhs, out);
                 return result;
             }
-            return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFMul, 
+            return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFMul,
                                               SpvOpIMul, SpvOpIMul, SpvOpUndef, out);
         case Token::SLASH:
-            return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFDiv, 
+            return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFDiv,
                                               SpvOpSDiv, SpvOpUDiv, SpvOpUndef, out);
         case Token::PLUSEQ: {
-            SpvId result = this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFAdd, 
+            SpvId result = this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFAdd,
                                                       SpvOpIAdd, SpvOpIAdd, SpvOpUndef, out);
             ASSERT(lvalue);
             lvalue->store(result, out);
             return result;
         }
         case Token::MINUSEQ: {
-            SpvId result = this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFSub, 
+            SpvId result = this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFSub,
                                                       SpvOpISub, SpvOpISub, SpvOpUndef, out);
             ASSERT(lvalue);
             lvalue->store(result, out);
             return result;
         }
         case Token::STAREQ: {
-            if (b.fLeft->fType.kind() == Type::kMatrix_Kind && 
+            if (b.fLeft->fType.kind() == Type::kMatrix_Kind &&
                 b.fRight->fType.kind() == Type::kMatrix_Kind) {
                 // matrix multiply
                 SpvId result = this->nextId();
@@ -2027,14 +2062,14 @@
                 lvalue->store(result, out);
                 return result;
             }
-            SpvId result = this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFMul, 
+            SpvId result = this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFMul,
                                                       SpvOpIMul, SpvOpIMul, SpvOpUndef, out);
             ASSERT(lvalue);
             lvalue->store(result, out);
             return result;
         }
         case Token::SLASHEQ: {
-            SpvId result = this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFDiv, 
+            SpvId result = this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFDiv,
                                                       SpvOpSDiv, SpvOpUDiv, SpvOpUndef, out);
             ASSERT(lvalue);
             lvalue->store(result, out);
@@ -2062,7 +2097,7 @@
     this->writeInstruction(SpvOpBranch, end, out);
     this->writeLabel(end, out);
     SpvId result = this->nextId();
-    this->writeInstruction(SpvOpPhi, this->getType(*fContext.fBool_Type), result, falseConstant, 
+    this->writeInstruction(SpvOpPhi, this->getType(*fContext.fBool_Type), result, falseConstant,
                            lhsBlock, rhs, rhsBlock, out);
     return result;
 }
@@ -2083,7 +2118,7 @@
     this->writeInstruction(SpvOpBranch, end, out);
     this->writeLabel(end, out);
     SpvId result = this->nextId();
-    this->writeInstruction(SpvOpPhi, this->getType(*fContext.fBool_Type), result, trueConstant, 
+    this->writeInstruction(SpvOpPhi, this->getType(*fContext.fBool_Type), result, trueConstant,
                            lhsBlock, rhs, rhsBlock, out);
     return result;
 }
@@ -2095,14 +2130,14 @@
         SpvId result = this->nextId();
         SpvId trueId = this->writeExpression(*t.fIfTrue, out);
         SpvId falseId = this->writeExpression(*t.fIfFalse, out);
-        this->writeInstruction(SpvOpSelect, this->getType(t.fType), result, test, trueId, falseId, 
+        this->writeInstruction(SpvOpSelect, this->getType(t.fType), result, test, trueId, falseId,
                                out);
         return result;
     }
-    // was originally using OpPhi to choose the result, but for some reason that is crashing on 
+    // was originally using OpPhi to choose the result, but for some reason that is crashing on
     // Adreno. Switched to storing the result in a temp variable as glslang does.
     SpvId var = this->nextId();
-    this->writeInstruction(SpvOpVariable, this->getPointerType(t.fType, SpvStorageClassFunction), 
+    this->writeInstruction(SpvOpVariable, this->getPointerType(t.fType, SpvStorageClassFunction),
                            var, SpvStorageClassFunction, fVariableBuffer);
     SpvId trueLabel = this->nextId();
     SpvId falseLabel = this->nextId();
@@ -2152,8 +2187,8 @@
         case Token::PLUSPLUS: {
             std::unique_ptr<LValue> lv = this->getLValue(*p.fOperand, out);
             SpvId one = this->writeExpression(*create_literal_1(fContext, p.fType), out);
-            SpvId result = this->writeBinaryOperation(p.fType, p.fType, lv->load(out), one, 
-                                                      SpvOpFAdd, SpvOpIAdd, SpvOpIAdd, SpvOpUndef, 
+            SpvId result = this->writeBinaryOperation(p.fType, p.fType, lv->load(out), one,
+                                                      SpvOpFAdd, SpvOpIAdd, SpvOpIAdd, SpvOpUndef,
                                                       out);
             lv->store(result, out);
             return result;
@@ -2161,8 +2196,8 @@
         case Token::MINUSMINUS: {
             std::unique_ptr<LValue> lv = this->getLValue(*p.fOperand, out);
             SpvId one = this->writeExpression(*create_literal_1(fContext, p.fType), out);
-            SpvId result = this->writeBinaryOperation(p.fType, p.fType, lv->load(out), one, 
-                                                      SpvOpFSub, SpvOpISub, SpvOpISub, SpvOpUndef, 
+            SpvId result = this->writeBinaryOperation(p.fType, p.fType, lv->load(out), one,
+                                                      SpvOpFSub, SpvOpISub, SpvOpISub, SpvOpUndef,
                                                       out);
             lv->store(result, out);
             return result;
@@ -2191,13 +2226,13 @@
     SpvId one = this->writeExpression(*create_literal_1(fContext, p.fType), out);
     switch (p.fOperator) {
         case Token::PLUSPLUS: {
-            SpvId temp = this->writeBinaryOperation(p.fType, p.fType, result, one, SpvOpFAdd, 
+            SpvId temp = this->writeBinaryOperation(p.fType, p.fType, result, one, SpvOpFAdd,
                                                     SpvOpIAdd, SpvOpIAdd, SpvOpUndef, out);
             lv->store(temp, out);
             return result;
         }
         case Token::MINUSMINUS: {
-            SpvId temp = this->writeBinaryOperation(p.fType, p.fType, result, one, SpvOpFSub, 
+            SpvId temp = this->writeBinaryOperation(p.fType, p.fType, result, one, SpvOpFSub,
                                                     SpvOpISub, SpvOpISub, SpvOpUndef, out);
             lv->store(temp, out);
             return result;
@@ -2211,14 +2246,14 @@
     if (b.fValue) {
         if (fBoolTrue == 0) {
             fBoolTrue = this->nextId();
-            this->writeInstruction(SpvOpConstantTrue, this->getType(b.fType), fBoolTrue, 
+            this->writeInstruction(SpvOpConstantTrue, this->getType(b.fType), fBoolTrue,
                                    fConstantBuffer);
         }
         return fBoolTrue;
     } else {
         if (fBoolFalse == 0) {
             fBoolFalse = this->nextId();
-            this->writeInstruction(SpvOpConstantFalse, this->getType(b.fType), fBoolFalse, 
+            this->writeInstruction(SpvOpConstantFalse, this->getType(b.fType), fBoolFalse,
                                    fConstantBuffer);
         }
         return fBoolFalse;
@@ -2230,7 +2265,7 @@
         auto entry = fIntConstants.find(i.fValue);
         if (entry == fIntConstants.end()) {
             SpvId result = this->nextId();
-            this->writeInstruction(SpvOpConstant, this->getType(i.fType), result, (SpvId) i.fValue, 
+            this->writeInstruction(SpvOpConstant, this->getType(i.fType), result, (SpvId) i.fValue,
                                    fConstantBuffer);
             fIntConstants[i.fValue] = result;
             return result;
@@ -2241,7 +2276,7 @@
         auto entry = fUIntConstants.find(i.fValue);
         if (entry == fUIntConstants.end()) {
             SpvId result = this->nextId();
-            this->writeInstruction(SpvOpConstant, this->getType(i.fType), result, (SpvId) i.fValue, 
+            this->writeInstruction(SpvOpConstant, this->getType(i.fType), result, (SpvId) i.fValue,
                                    fConstantBuffer);
             fUIntConstants[i.fValue] = result;
             return result;
@@ -2259,7 +2294,7 @@
             uint32_t bits;
             ASSERT(sizeof(bits) == sizeof(value));
             memcpy(&bits, &value, sizeof(bits));
-            this->writeInstruction(SpvOpConstant, this->getType(f.fType), result, bits, 
+            this->writeInstruction(SpvOpConstant, this->getType(f.fType), result, bits,
                                    fConstantBuffer);
             fFloatConstants[value] = result;
             return result;
@@ -2273,7 +2308,7 @@
             uint64_t bits;
             ASSERT(sizeof(bits) == sizeof(f.fValue));
             memcpy(&bits, &f.fValue, sizeof(bits));
-            this->writeInstruction(SpvOpConstant, this->getType(f.fType), result, 
+            this->writeInstruction(SpvOpConstant, this->getType(f.fType), result,
                                    bits & 0xffffffff, bits >> 32, fConstantBuffer);
             fDoubleConstants[f.fValue] = result;
             return result;
@@ -2284,7 +2319,7 @@
 
 SpvId SPIRVCodeGenerator::writeFunctionStart(const FunctionDeclaration& f, SkWStream& out) {
     SpvId result = fFunctionMap[&f];
-    this->writeInstruction(SpvOpFunction, this->getType(f.fReturnType), result, 
+    this->writeInstruction(SpvOpFunction, this->getType(f.fReturnType), result,
                            SpvFunctionControlMaskNone, this->getFunctionType(f), out);
     this->writeInstruction(SpvOpName, result, f.fName.c_str(), fNameBuffer);
     for (size_t i = 0; i < f.fParameters.size(); i++) {
@@ -2316,46 +2351,54 @@
 
 void SPIRVCodeGenerator::writeLayout(const Layout& layout, SpvId target) {
     if (layout.fLocation >= 0) {
-        this->writeInstruction(SpvOpDecorate, target, SpvDecorationLocation, layout.fLocation, 
+        this->writeInstruction(SpvOpDecorate, target, SpvDecorationLocation, layout.fLocation,
                                fDecorationBuffer);
     }
     if (layout.fBinding >= 0) {
-        this->writeInstruction(SpvOpDecorate, target, SpvDecorationBinding, layout.fBinding, 
+        this->writeInstruction(SpvOpDecorate, target, SpvDecorationBinding, layout.fBinding,
                                fDecorationBuffer);
     }
     if (layout.fIndex >= 0) {
-        this->writeInstruction(SpvOpDecorate, target, SpvDecorationIndex, layout.fIndex, 
+        this->writeInstruction(SpvOpDecorate, target, SpvDecorationIndex, layout.fIndex,
                                fDecorationBuffer);
     }
     if (layout.fSet >= 0) {
-        this->writeInstruction(SpvOpDecorate, target, SpvDecorationDescriptorSet, layout.fSet, 
+        this->writeInstruction(SpvOpDecorate, target, SpvDecorationDescriptorSet, layout.fSet,
                                fDecorationBuffer);
     }
+    if (layout.fInputAttachmentIndex >= 0) {
+        this->writeInstruction(SpvOpDecorate, target, SpvDecorationInputAttachmentIndex,
+                               layout.fInputAttachmentIndex, fDecorationBuffer);
+    }
     if (layout.fBuiltin >= 0 && layout.fBuiltin != SK_FRAGCOLOR_BUILTIN) {
-        this->writeInstruction(SpvOpDecorate, target, SpvDecorationBuiltIn, layout.fBuiltin, 
+        this->writeInstruction(SpvOpDecorate, target, SpvDecorationBuiltIn, layout.fBuiltin,
                                fDecorationBuffer);
     }
 }
 
 void SPIRVCodeGenerator::writeLayout(const Layout& layout, SpvId target, int member) {
     if (layout.fLocation >= 0) {
-        this->writeInstruction(SpvOpMemberDecorate, target, member, SpvDecorationLocation, 
+        this->writeInstruction(SpvOpMemberDecorate, target, member, SpvDecorationLocation,
                                layout.fLocation, fDecorationBuffer);
     }
     if (layout.fBinding >= 0) {
-        this->writeInstruction(SpvOpMemberDecorate, target, member, SpvDecorationBinding, 
+        this->writeInstruction(SpvOpMemberDecorate, target, member, SpvDecorationBinding,
                                layout.fBinding, fDecorationBuffer);
     }
     if (layout.fIndex >= 0) {
-        this->writeInstruction(SpvOpMemberDecorate, target, member, SpvDecorationIndex, 
+        this->writeInstruction(SpvOpMemberDecorate, target, member, SpvDecorationIndex,
                                layout.fIndex, fDecorationBuffer);
     }
     if (layout.fSet >= 0) {
-        this->writeInstruction(SpvOpMemberDecorate, target, member, SpvDecorationDescriptorSet, 
+        this->writeInstruction(SpvOpMemberDecorate, target, member, SpvDecorationDescriptorSet,
                                layout.fSet, fDecorationBuffer);
     }
+    if (layout.fInputAttachmentIndex >= 0) {
+        this->writeInstruction(SpvOpDecorate, target, member, SpvDecorationInputAttachmentIndex,
+                               layout.fInputAttachmentIndex, fDecorationBuffer);
+    }
     if (layout.fBuiltin >= 0) {
-        this->writeInstruction(SpvOpMemberDecorate, target, member, SpvDecorationBuiltIn, 
+        this->writeInstruction(SpvOpMemberDecorate, target, member, SpvDecorationBuiltIn,
                                layout.fBuiltin, fDecorationBuffer);
     }
 }
@@ -2374,7 +2417,7 @@
 }
 
 #define BUILTIN_IGNORE 9999
-void SPIRVCodeGenerator::writeGlobalVars(Program::Kind kind, const VarDeclarations& decl, 
+void SPIRVCodeGenerator::writeGlobalVars(Program::Kind kind, const VarDeclarations& decl,
                                          SkWStream& out) {
     for (size_t i = 0; i < decl.fVars.size(); i++) {
         const VarDeclaration& varDecl = decl.fVars[i];
@@ -2414,9 +2457,9 @@
         this->writeInstruction(SpvOpVariable, type, id, storageClass, fConstantBuffer);
         this->writeInstruction(SpvOpName, id, var->fName.c_str(), fNameBuffer);
         if (var->fType.kind() == Type::kMatrix_Kind) {
-            this->writeInstruction(SpvOpMemberDecorate, id, (SpvId) i, SpvDecorationColMajor, 
+            this->writeInstruction(SpvOpMemberDecorate, id, (SpvId) i, SpvDecorationColMajor,
                                    fDecorationBuffer);
-            this->writeInstruction(SpvOpMemberDecorate, id, (SpvId) i, SpvDecorationMatrixStride, 
+            this->writeInstruction(SpvOpMemberDecorate, id, (SpvId) i, SpvDecorationMatrixStride,
                                    (SpvId) var->fType.stride(), fDecorationBuffer);
         }
         if (varDecl.fValue) {
@@ -2453,7 +2496,7 @@
         case Statement::kExpression_Kind:
             this->writeExpression(*((ExpressionStatement&) s).fExpression, out);
             break;
-        case Statement::kReturn_Kind: 
+        case Statement::kReturn_Kind:
             this->writeReturnStatement((ReturnStatement&) s, out);
             break;
         case Statement::kVarDeclarations_Kind:
@@ -2553,7 +2596,7 @@
 
 void SPIRVCodeGenerator::writeReturnStatement(const ReturnStatement& r, SkWStream& out) {
     if (r.fExpression) {
-        this->writeInstruction(SpvOpReturnValue, this->writeExpression(*r.fExpression, out), 
+        this->writeInstruction(SpvOpReturnValue, this->writeExpression(*r.fExpression, out),
                                out);
     } else {
         this->writeInstruction(SpvOpReturn, out);
@@ -2583,7 +2626,7 @@
     }
     for (size_t i = 0; i < program.fElements.size(); i++) {
         if (program.fElements[i]->fKind == ProgramElement::kVar_Kind) {
-            this->writeGlobalVars(program.fKind, ((VarDeclarations&) *program.fElements[i]), 
+            this->writeGlobalVars(program.fKind, ((VarDeclarations&) *program.fElements[i]),
                                   body);
         }
     }
@@ -2601,7 +2644,7 @@
     ASSERT(main);
     for (auto entry : fVariableMap) {
         const Variable* var = entry.first;
-        if (var->fStorage == Variable::kGlobal_Storage && 
+        if (var->fStorage == Variable::kGlobal_Storage &&
                 ((var->fModifiers.fFlags & Modifiers::kIn_Flag) ||
                  (var->fModifiers.fFlags & Modifiers::kOut_Flag))) {
             interfaceVars.push_back(entry.second);
@@ -2610,7 +2653,7 @@
     this->writeCapabilities(out);
     this->writeInstruction(SpvOpExtInstImport, fGLSLExtendedInstructions, "GLSL.std.450", out);
     this->writeInstruction(SpvOpMemoryModel, SpvAddressingModelLogical, SpvMemoryModelGLSL450, out);
-    this->writeOpCode(SpvOpEntryPoint, (SpvId) (3 + (strlen(main->fName.c_str()) + 4) / 4) + 
+    this->writeOpCode(SpvOpEntryPoint, (SpvId) (3 + (strlen(main->fName.c_str()) + 4) / 4) +
                       (int32_t) interfaceVars.size(), out);
     switch (program.fKind) {
         case Program::kVertex_Kind:
@@ -2626,19 +2669,19 @@
         this->writeWord(var, out);
     }
     if (program.fKind == Program::kFragment_Kind) {
-        this->writeInstruction(SpvOpExecutionMode, 
-                               fFunctionMap[main], 
+        this->writeInstruction(SpvOpExecutionMode,
+                               fFunctionMap[main],
                                SpvExecutionModeOriginUpperLeft,
                                out);
     }
     for (size_t i = 0; i < program.fElements.size(); i++) {
         if (program.fElements[i]->fKind == ProgramElement::kExtension_Kind) {
-            this->writeInstruction(SpvOpSourceExtension, 
-                                   ((Extension&) *program.fElements[i]).fName.c_str(), 
+            this->writeInstruction(SpvOpSourceExtension,
+                                   ((Extension&) *program.fElements[i]).fName.c_str(),
                                    out);
         }
     }
-    
+
     write_data(*fNameBuffer.detachAsData(), out);
     write_data(*fDecorationBuffer.detachAsData(), out);
     write_data(*fConstantBuffer.detachAsData(), out);
diff --git a/src/sksl/SkSLSPIRVCodeGenerator.h b/src/sksl/SkSLSPIRVCodeGenerator.h
index 84c582e..b791945 100644
--- a/src/sksl/SkSLSPIRVCodeGenerator.h
+++ b/src/sksl/SkSLSPIRVCodeGenerator.h
@@ -51,7 +51,7 @@
     class LValue {
     public:
         virtual ~LValue() {}
-        
+
         // returns a pointer to the lvalue, if possible. If the lvalue cannot be directly referenced
         // by a pointer (e.g. vector swizzles), returns 0.
         virtual SpvId getPointer() = 0;
@@ -84,7 +84,8 @@
         kAtan_SpecialIntrinsic,
         kTexture_SpecialIntrinsic,
         kTexture2D_SpecialIntrinsic,
-        kTextureProj_SpecialIntrinsic
+        kTextureProj_SpecialIntrinsic,
+        kSubpassLoad_SpecialIntrinsic,
     };
 
     void setupIntrinsics();
diff --git a/src/sksl/ast/SkSLASTLayout.h b/src/sksl/ast/SkSLASTLayout.h
index cb7f3c1..a77e838 100644
--- a/src/sksl/ast/SkSLASTLayout.h
+++ b/src/sksl/ast/SkSLASTLayout.h
@@ -78,13 +78,15 @@
     }
 
     // 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, Format format)
+    ASTLayout(int location, int binding, int index, int set, int builtin, int inputAttachmentIndex,
+              bool originUpperLeft, bool overrideCoverage, bool blendSupportAllEquations,
+              Format format)
     : fLocation(location)
     , fBinding(binding)
     , fIndex(index)
     , fSet(set)
     , fBuiltin(builtin)
+    , fInputAttachmentIndex(inputAttachmentIndex)
     , fOriginUpperLeft(originUpperLeft)
     , fOverrideCoverage(overrideCoverage)
     , fBlendSupportAllEquations(blendSupportAllEquations)
@@ -113,6 +115,10 @@
             result += separator + "builtin = " + to_string(fBuiltin);
             separator = ", ";
         }
+        if (fInputAttachmentIndex >= 0) {
+            result += separator + "input_attachment_index = " + to_string(fBuiltin);
+            separator = ", ";
+        }
         if (fOriginUpperLeft) {
             result += separator + "origin_upper_left";
             separator = ", ";
@@ -140,6 +146,7 @@
     const int fIndex;
     const int fSet;
     const int fBuiltin;
+    const int fInputAttachmentIndex;
     const bool fOriginUpperLeft;
     const bool fOverrideCoverage;
     const bool fBlendSupportAllEquations;
diff --git a/src/sksl/ir/SkSLConstructor.h b/src/sksl/ir/SkSLConstructor.h
index 92f7580..63c692b 100644
--- a/src/sksl/ir/SkSLConstructor.h
+++ b/src/sksl/ir/SkSLConstructor.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_CONSTRUCTOR
 #define SKSL_CONSTRUCTOR
 
@@ -16,7 +16,7 @@
  * Represents the construction of a compound type, such as "vec2(x, y)".
  */
 struct Constructor : public Expression {
-    Constructor(Position position, const Type& type, 
+    Constructor(Position position, const Type& type,
                 std::vector<std::unique_ptr<Expression>> arguments)
     : INHERITED(position, kConstructor_Kind, type)
     , fArguments(std::move(arguments)) {}
diff --git a/src/sksl/ir/SkSLLayout.h b/src/sksl/ir/SkSLLayout.h
index 7359262..c5753e0 100644
--- a/src/sksl/ir/SkSLLayout.h
+++ b/src/sksl/ir/SkSLLayout.h
@@ -22,29 +22,33 @@
     , fIndex(layout.fIndex)
     , fSet(layout.fSet)
     , fBuiltin(layout.fBuiltin)
+    , fInputAttachmentIndex(layout.fInputAttachmentIndex)
     , fOriginUpperLeft(layout.fOriginUpperLeft)
     , fOverrideCoverage(layout.fOverrideCoverage)
     , fBlendSupportAllEquations(layout.fBlendSupportAllEquations)
     , fFormat(layout.fFormat) {}
 
-    Layout(int location, int binding, int index, int set, int builtin, bool originUpperLeft,
-           bool overrideCoverage, bool blendSupportAllEquations, ASTLayout::Format format)
+    Layout(int location, int binding, int index, int set, int builtin, int inputAttachmentIndex,
+           bool originUpperLeft, bool overrideCoverage, bool blendSupportAllEquations,
+           ASTLayout::Format format)
     : fLocation(location)
     , fBinding(binding)
     , fIndex(index)
     , fSet(set)
     , fBuiltin(builtin)
+    , fInputAttachmentIndex(inputAttachmentIndex)
     , fOriginUpperLeft(originUpperLeft)
     , fOverrideCoverage(overrideCoverage)
     , fBlendSupportAllEquations(blendSupportAllEquations)
     , fFormat(format) {}
 
-    Layout() 
+    Layout()
     : fLocation(-1)
     , fBinding(-1)
     , fIndex(-1)
     , fSet(-1)
     , fBuiltin(-1)
+    , fInputAttachmentIndex(-1)
     , fOriginUpperLeft(false)
     , fOverrideCoverage(false)
     , fBlendSupportAllEquations(false)
@@ -73,6 +77,10 @@
             result += separator + "builtin = " + to_string(fBuiltin);
             separator = ", ";
         }
+        if (fInputAttachmentIndex >= 0) {
+            result += separator + "input_attachment_index = " + to_string(fBuiltin);
+            separator = ", ";
+        }
         if (fOriginUpperLeft) {
             result += separator + "origin_upper_left";
             separator = ", ";
@@ -101,6 +109,7 @@
                fIndex                    == other.fIndex &&
                fSet                      == other.fSet &&
                fBuiltin                  == other.fBuiltin &&
+               fInputAttachmentIndex     == other.fInputAttachmentIndex &&
                fOriginUpperLeft          == other.fOriginUpperLeft &&
                fOverrideCoverage         == other.fOverrideCoverage &&
                fBlendSupportAllEquations == other.fBlendSupportAllEquations &&
@@ -111,13 +120,16 @@
         return !(*this == other);
     }
 
-    // everything but builtin is in the GLSL spec; builtin comes from SPIR-V and identifies which
-    // particular builtin value this object represents.
     int fLocation;
     int fBinding;
     int fIndex;
     int fSet;
+    // builtin comes from SPIR-V and identifies which particular builtin value this object
+    // represents.
     int fBuiltin;
+    // input_attachment_index comes from Vulkan/SPIR-V to connect a shader variable to the a
+    // corresponding attachment on the subpass in which the shader is being used.
+    int fInputAttachmentIndex;
     bool fOriginUpperLeft;
     bool fOverrideCoverage;
     bool fBlendSupportAllEquations;
diff --git a/src/sksl/ir/SkSLType.h b/src/sksl/ir/SkSLType.h
index 4174c72..81ec13a 100644
--- a/src/sksl/ir/SkSLType.h
+++ b/src/sksl/ir/SkSLType.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKIASL_TYPE
 #define SKIASL_TYPE
 
@@ -51,14 +51,14 @@
         kOther_Kind
     };
 
-    // Create an "other" (special) type with the given name. These types cannot be directly 
+    // Create an "other" (special) type with the given name. These types cannot be directly
     // referenced from user code.
     Type(SkString name)
     : INHERITED(Position(), kType_Kind, std::move(name))
     , fTypeKind(kOther_Kind) {}
 
-    // Create a generic type which maps to the listed types. As currently implemented, there are 
-    // always exactly four coercion targets, mapping to the scalar, vec2, vec3, and vec4 versions of 
+    // Create a generic type which maps to the listed types. As currently implemented, there are
+    // always exactly four coercion targets, mapping to the scalar, vec2, vec3, and vec4 versions of
     // a type.
     Type(SkString name, std::vector<const Type*> types)
     : INHERITED(Position(), kType_Kind, std::move(name))
@@ -100,7 +100,7 @@
     , fTypeKind(kind)
     , fComponentType(&componentType)
     , fColumns(columns)
-    , fRows(1)    
+    , fRows(1)
     , fDimensions(SpvDim1D) {}
 
     // Create a matrix type.
@@ -109,12 +109,12 @@
     , fTypeKind(kMatrix_Kind)
     , fComponentType(&componentType)
     , fColumns(columns)
-    , fRows(rows)    
+    , fRows(rows)
     , fDimensions(SpvDim1D) {}
 
     // Create a sampler type.
-    Type(SkString name, SpvDim_ dimensions, bool isDepth, bool isArrayed, bool isMultisampled, 
-         bool isSampled) 
+    Type(SkString name, SpvDim_ dimensions, bool isDepth, bool isArrayed, bool isMultisampled,
+         bool isSampled)
     : INHERITED(Position(), kType_Kind, std::move(name))
     , fTypeKind(kSampler_Kind)
     , fDimensions(dimensions)
@@ -154,7 +154,7 @@
     }
 
     /**
-     * Returns true if an instance of this type can be freely coerced (implicitly converted) to 
+     * Returns true if an instance of this type can be freely coerced (implicitly converted) to
      * another type.
      */
     bool canCoerceTo(const Type& other) const {
@@ -164,8 +164,8 @@
 
     /**
      * Determines the "cost" of coercing (implicitly converting) this type to another type. The cost
-     * is a number with no particular meaning other than that lower costs are preferable to higher 
-     * costs. Returns true if a conversion is possible, false otherwise. The value of the out 
+     * is a number with no particular meaning other than that lower costs are preferable to higher
+     * costs. Returns true if a conversion is possible, false otherwise. The value of the out
      * parameter is undefined if false is returned.
      */
     bool determineCoercionCost(const Type& other, int* outCost) const;
@@ -181,11 +181,11 @@
 
     /**
      * For matrices and vectors, returns the number of columns (e.g. both mat3 and vec3 return 3).
-     * For scalars, returns 1. For arrays, returns either the size of the array (if known) or -1. 
+     * For scalars, returns 1. For arrays, returns either the size of the array (if known) or -1.
      * For all other types, causes an assertion failure.
      */
     int columns() const {
-        ASSERT(fTypeKind == kScalar_Kind || fTypeKind == kVector_Kind || 
+        ASSERT(fTypeKind == kScalar_Kind || fTypeKind == kVector_Kind ||
                fTypeKind == kMatrix_Kind || fTypeKind == kArray_Kind);
         return fColumns;
     }
@@ -214,27 +214,27 @@
     }
 
     int dimensions() const {
-        ASSERT(fTypeKind == kSampler_Kind);
+        ASSERT(kSampler_Kind == fTypeKind);
         return fDimensions;
     }
 
     bool isDepth() const {
-        ASSERT(fTypeKind == kSampler_Kind);
+        ASSERT(kSampler_Kind == fTypeKind);
         return fIsDepth;
     }
 
     bool isArrayed() const {
-        ASSERT(fTypeKind == kSampler_Kind);
+        ASSERT(kSampler_Kind == fTypeKind);
         return fIsArrayed;
     }
 
     bool isMultisampled() const {
-        ASSERT(fTypeKind == kSampler_Kind);
+        ASSERT(kSampler_Kind == fTypeKind);
         return fIsMultisampled;
     }
 
     bool isSampled() const {
-        ASSERT(fTypeKind == kSampler_Kind);
+        ASSERT(kSampler_Kind == fTypeKind);
         return fIsSampled;
     }
 
@@ -320,7 +320,7 @@
     }
 
     /**
-     * Returns the corresponding vector or matrix type with the specified number of columns and 
+     * Returns the corresponding vector or matrix type with the specified number of columns and
      * rows.
      */
     const Type& toCompound(const Context& context, int columns, int rows) const;
diff --git a/src/sksl/sksl.include b/src/sksl/sksl.include
index 83c6aed..d7b6326 100644
--- a/src/sksl/sksl.include
+++ b/src/sksl/sksl.include
@@ -286,6 +286,15 @@
 float texture(sampler2DRectShadow sampler, vec3 P);
 float texture($gsamplerCubeArrayShadow sampler, vec4 P, float compare);
 */
+
+// Currently we do not support the generic types of loading subpassInput so we have some explicit
+// versions that we currently use
+vec4 subpassLoad(subpassInput subpass);
+vec4 subpassLoad(subpassInputMS subpass, int sample);
+/*
+$gvec4 subpassLoad(gsubpassInput subpass);
+$gvec4 subpassLoad(gsubpassInputMS subpass, int sample);
+*/
 )
 
 // split into multiple chunks, as MSVC++ complains if a single string is too long