Reland image storage with fixes.

Revert "Revert "Initial OpenGL Image support.""

This reverts commit 59dc41175d99d0a31c046aec0c26c4d82a3a3574.

BUG=skia:

Change-Id: Ibe3c87ce7f746f065fdbcc5a518388cc291112f5
Reviewed-on: https://skia-review.googlesource.com/5131
Reviewed-by: Ethan Nicholas <ethannicholas@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/GrFragmentProcessor.cpp b/src/gpu/GrFragmentProcessor.cpp
index 820b428..de4ab9e 100644
--- a/src/gpu/GrFragmentProcessor.cpp
+++ b/src/gpu/GrFragmentProcessor.cpp
@@ -27,7 +27,7 @@
 
 bool GrFragmentProcessor::isEqual(const GrFragmentProcessor& that) const {
     if (this->classID() != that.classID() ||
-        !this->hasSameSamplers(that)) {
+        !this->hasSameSamplersAndAccesses(that)) {
         return false;
     }
     if (!this->hasSameTransforms(that)) {
diff --git a/src/gpu/GrProcessor.cpp b/src/gpu/GrProcessor.cpp
index 1e100e4..f4f7ccb 100644
--- a/src/gpu/GrProcessor.cpp
+++ b/src/gpu/GrProcessor.cpp
@@ -122,7 +122,12 @@
 
 void GrProcessor::addBufferAccess(const BufferAccess* access) {
     fBufferAccesses.push_back(access);
-    this->addGpuResource(access->getProgramBuffer());
+    this->addGpuResource(access->programBuffer());
+}
+
+void GrProcessor::addImageStorageAccess(const ImageStorageAccess* access) {
+    fImageStorageAccesses.push_back(access);
+    this->addGpuResource(access->programTexture());
 }
 
 void* GrProcessor::operator new(size_t size) {
@@ -133,9 +138,10 @@
     return MemoryPoolAccessor().pool()->release(target);
 }
 
-bool GrProcessor::hasSameSamplers(const GrProcessor& that) const {
+bool GrProcessor::hasSameSamplersAndAccesses(const GrProcessor &that) const {
     if (this->numTextureSamplers() != that.numTextureSamplers() ||
-        this->numBuffers() != that.numBuffers()) {
+        this->numBuffers() != that.numBuffers() ||
+        this->numImageStorages() != that.numImageStorages()) {
         return false;
     }
     for (int i = 0; i < this->numTextureSamplers(); ++i) {
@@ -148,6 +154,11 @@
             return false;
         }
     }
+    for (int i = 0; i < this->numImageStorages(); ++i) {
+        if (this->imageStorageAccess(i) != that.imageStorageAccess(i)) {
+            return false;
+        }
+    }
     return true;
 }
 
@@ -189,6 +200,37 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
+GrProcessor::ImageStorageAccess::ImageStorageAccess(sk_sp<GrTexture> texture, GrIOType ioType,
+                                                    GrSLMemoryModel memoryModel,
+                                                    GrSLRestrict restrict,
+                                                    GrShaderFlags visibility) {
+    SkASSERT(texture);
+    fTexture.set(texture.release(), ioType);
+    fMemoryModel = memoryModel;
+    fRestrict = restrict;
+    fVisibility = visibility;
+    // We currently infer this from the config. However, we could allow the client to specify
+    // a format that is different but compatible with the config.
+    switch (fTexture.get()->config()) {
+        case kRGBA_8888_GrPixelConfig:
+            fFormat = GrImageStorageFormat::kRGBA8;
+            break;
+        case kRGBA_8888_sint_GrPixelConfig:
+            fFormat = GrImageStorageFormat::kRGBA8i;
+            break;
+        case kRGBA_half_GrPixelConfig:
+            fFormat = GrImageStorageFormat::kRGBA16f;
+            break;
+        case kRGBA_float_GrPixelConfig:
+            fFormat = GrImageStorageFormat::kRGBA32f;
+            break;
+        default:
+            SkFAIL("Config is not (yet) supported as image storage.");
+            break;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
 // Initial static variable from GrXPFactory
-int32_t GrXPFactory::gCurrXPFClassID =
-        GrXPFactory::kIllegalXPFClassID;
+int32_t GrXPFactory::gCurrXPFClassID = GrXPFactory::kIllegalXPFClassID;
diff --git a/src/gpu/GrProgramDesc.cpp b/src/gpu/GrProgramDesc.cpp
index cdbbc23..9f3278c 100644
--- a/src/gpu/GrProgramDesc.cpp
+++ b/src/gpu/GrProgramDesc.cpp
@@ -15,48 +15,88 @@
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "glsl/GrGLSLCaps.h"
 
-static uint16_t sampler_key(GrSLType samplerType, GrPixelConfig config, GrShaderFlags visibility,
-                            const GrGLSLCaps& caps) {
-    enum {
-        kFirstSamplerType = kTexture2DSampler_GrSLType,
-        kLastSamplerType = kBufferSampler_GrSLType,
-        kSamplerTypeKeyBits = 4
-    };
-    GR_STATIC_ASSERT(kLastSamplerType - kFirstSamplerType < (1 << kSamplerTypeKeyBits));
+enum {
+    kSamplerOrImageTypeKeyBits = 4
+};
 
-    SkASSERT((int)samplerType >= kFirstSamplerType && (int)samplerType <= kLastSamplerType);
-    int samplerTypeKey = samplerType - kFirstSamplerType;
+static inline uint16_t image_storage_or_sampler_uniform_type_key(GrSLType type ) {
+    int value = UINT16_MAX;
+    switch (type) {
+        case kTexture2DSampler_GrSLType:
+            value = 0;
+            break;
+        case kITexture2DSampler_GrSLType:
+            value = 1;
+            break;
+        case kTextureExternalSampler_GrSLType:
+            value = 2;
+            break;
+        case kTexture2DRectSampler_GrSLType:
+            value = 3;
+            break;
+        case kBufferSampler_GrSLType:
+            value = 4;
+            break;
+        case kImageStorage2D_GrSLType:
+            value = 5;
+            break;
+        case kIImageStorage2D_GrSLType:
+            value = 6;
+            break;
 
-    return SkToU16(caps.configTextureSwizzle(config).asKey() |
-                   (samplerTypeKey << 8) |
-                   (caps.samplerPrecision(config, visibility) << (8 + kSamplerTypeKeyBits)));
+        default:
+            break;
+    }
+    SkASSERT((value & ((1 << kSamplerOrImageTypeKeyBits) - 1)) == value);
+    return value;
 }
 
-static void add_sampler_keys(GrProcessorKeyBuilder* b, const GrProcessor& proc,
-                             const GrGLSLCaps& caps) {
+static uint16_t sampler_key(GrSLType samplerType, GrPixelConfig config, GrShaderFlags visibility,
+                            const GrGLSLCaps& caps) {
+    int samplerTypeKey = image_storage_or_sampler_uniform_type_key(samplerType);
+
+    GR_STATIC_ASSERT(1 == sizeof(caps.configTextureSwizzle(config).asKey()));
+    return SkToU16(samplerTypeKey |
+                   caps.configTextureSwizzle(config).asKey() << kSamplerOrImageTypeKeyBits |
+                   (caps.samplerPrecision(config, visibility) << (8 + kSamplerOrImageTypeKeyBits)));
+}
+
+static uint16_t storage_image_key(const GrProcessor::ImageStorageAccess& imageAccess) {
+    GrSLType type = imageAccess.texture()->texturePriv().imageStorageType();
+    return image_storage_or_sampler_uniform_type_key(type) |
+           (int)imageAccess.format() << kSamplerOrImageTypeKeyBits;
+}
+
+static void add_sampler_and_image_keys(GrProcessorKeyBuilder* b, const GrProcessor& proc,
+                                       const GrGLSLCaps& caps) {
     int numTextureSamplers = proc.numTextureSamplers();
-    int numSamplers = numTextureSamplers + proc.numBuffers();
-    // Need two bytes per key (swizzle, sampler type, and precision).
-    int word32Count = (numSamplers + 1) / 2;
+    int numBuffers = proc.numBuffers();
+    int numImageStorages = proc.numImageStorages();
+    int numUniforms = numTextureSamplers + numBuffers + numImageStorages;
+    // Need two bytes per key.
+    int word32Count = (numUniforms + 1) / 2;
     if (0 == word32Count) {
         return;
     }
     uint16_t* k16 = SkTCast<uint16_t*>(b->add32n(word32Count));
-    int i = 0;
-    for (; i < numTextureSamplers; ++i) {
-        const GrProcessor::TextureSampler& textureSampler = proc.textureSampler(i);
-        const GrTexture* tex = textureSampler.texture();
-        k16[i] = sampler_key(tex->texturePriv().samplerType(), tex->config(),
-                             textureSampler.visibility(), caps);
+    int j = 0;
+    for (int i = 0; i < numTextureSamplers; ++i, ++j) {
+        const GrProcessor::TextureSampler& sampler = proc.textureSampler(i);
+        const GrTexture* tex = sampler.texture();
+        k16[j] = sampler_key(tex->texturePriv().samplerType(), tex->config(), sampler.visibility(),
+                             caps);
     }
-    for (; i < numSamplers; ++i) {
-        const GrProcessor::BufferAccess& access = proc.bufferAccess(i - numTextureSamplers);
-        k16[i] = sampler_key(kBufferSampler_GrSLType, access.texelConfig(),
-                             access.visibility(), caps);
+    for (int i = 0; i < numBuffers; ++i, ++j) {
+        const GrProcessor::BufferAccess& access = proc.bufferAccess(i);
+        k16[j] = sampler_key(kBufferSampler_GrSLType, access.texelConfig(), access.visibility(),
+                             caps);
     }
-    // zero the last 16 bits if the number of samplers is odd.
-    if (numSamplers & 0x1) {
-        k16[numSamplers] = 0;
+    for (int i = 0; i < numImageStorages; ++i, ++j) {
+        k16[j] = storage_image_key(proc.imageStorageAccess(i));
+    }
+    // zero the last 16 bits if the number of uniforms for samplers and image storages is odd.
+    if (numUniforms & 0x1) {
+        k16[numUniforms] = 0;
     }
 }
 
@@ -82,7 +122,7 @@
         return false;
     }
 
-    add_sampler_keys(b, proc, glslCaps);
+    add_sampler_and_image_keys(b, proc, glslCaps);
 
     uint32_t* key = b->add32n(2);
     key[0] = (classID << 16) | SkToU32(processorKeySize);
diff --git a/src/gpu/GrShaderVar.cpp b/src/gpu/GrShaderVar.cpp
index ef305cb..75142ca 100644
--- a/src/gpu/GrShaderVar.cpp
+++ b/src/gpu/GrShaderVar.cpp
@@ -9,14 +9,86 @@
 #include "GrShaderVar.h"
 #include "glsl/GrGLSLCaps.h"
 
+static const char* type_modifier_string(GrShaderVar::TypeModifier t) {
+    switch (t) {
+        case GrShaderVar::kNone_TypeModifier: return "";
+        case GrShaderVar::kIn_TypeModifier: return "in";
+        case GrShaderVar::kInOut_TypeModifier: return "inout";
+        case GrShaderVar::kOut_TypeModifier: return "out";
+        case GrShaderVar::kUniform_TypeModifier: return "uniform";
+    }
+    SkFAIL("Unknown shader variable type modifier.");
+    return "";
+}
+
+void GrShaderVar::setImageStorageFormat(GrImageStorageFormat format) {
+    const char* formatStr = nullptr;
+    switch (format) {
+        case GrImageStorageFormat::kRGBA8:
+            formatStr = "rgba8";
+            break;
+        case GrImageStorageFormat::kRGBA8i:
+            formatStr = "rgba8i";
+            break;
+        case GrImageStorageFormat::kRGBA16f:
+            formatStr = "rgba16f";
+            break;
+        case GrImageStorageFormat::kRGBA32f:
+            formatStr = "rgba32f";
+            break;
+    }
+    this->addLayoutQualifier(formatStr);
+    SkASSERT(formatStr);
+}
+
+void GrShaderVar::setMemoryModel(GrSLMemoryModel model) {
+    switch (model) {
+        case GrSLMemoryModel::kNone:
+            return;
+        case GrSLMemoryModel::kCoherent:
+            this->addModifier("coherent");
+            return;
+        case GrSLMemoryModel::kVolatile:
+            this->addModifier("volatile");
+            return;
+    }
+    SkFAIL("Unknown memory model.");
+}
+
+void GrShaderVar::setRestrict(GrSLRestrict restrict) {
+    switch (restrict) {
+        case GrSLRestrict::kNo:
+            return;
+        case GrSLRestrict::kYes:
+            this->addModifier("restrict");
+            return;
+    }
+    SkFAIL("Unknown restrict.");
+}
+
+void GrShaderVar::setIOType(GrIOType ioType) {
+    switch (ioType) {
+        case kRW_GrIOType:
+            return;
+        case kRead_GrIOType:
+            this->addModifier("readonly");
+            return;
+        case kWrite_GrIOType:
+            this->addModifier("writeonly");
+            return;
+    }
+    SkFAIL("Unknown io type.");
+}
+
 void GrShaderVar::appendDecl(const GrGLSLCaps* glslCaps, SkString* out) const {
     SkASSERT(kDefault_GrSLPrecision == fPrecision || GrSLTypeAcceptsPrecision(fType));
+    SkString layout = fLayoutQualifier;
     if (!fLayoutQualifier.isEmpty()) {
         out->appendf("layout(%s) ", fLayoutQualifier.c_str());
     }
     out->append(fExtraModifiers);
     if (this->getTypeModifier() != kNone_TypeModifier) {
-        out->append(TypeModifierString(this->getTypeModifier()));
+        out->append(type_modifier_string(this->getTypeModifier()));
         out->append(" ");
     }
     GrSLType effectiveType = this->getType();
diff --git a/src/gpu/GrTexturePriv.h b/src/gpu/GrTexturePriv.h
index d68eb79..0420611 100644
--- a/src/gpu/GrTexturePriv.h
+++ b/src/gpu/GrTexturePriv.h
@@ -49,6 +49,14 @@
         return fTexture->fMaxMipMapLevel;
     }
 
+    GrSLType imageStorageType() const {
+        if (GrPixelConfigIsSint(fTexture->config())) {
+            return kIImageStorage2D_GrSLType;
+        } else {
+            return kImageStorage2D_GrSLType;
+        }
+    }
+
     GrSLType samplerType() const { return fTexture->fSamplerType; }
 
     /** The filter used is clamped to this value in GrProcessor::TextureSampler. */
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index 3e9f82a..c4f7560 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -324,16 +324,29 @@
         static constexpr int kMaxSaneImages = 4;
         GrGLint maxUnits;
         GR_GL_GetIntegerv(gli, GR_GL_MAX_IMAGE_UNITS, &maxUnits);
-        GR_GL_GetIntegerv(gli, GR_GL_MAX_VERTEX_IMAGE_UNIFORMS, &glslCaps->fMaxVertexImages);
-        GR_GL_GetIntegerv(gli, GR_GL_MAX_GEOMETRY_IMAGE_UNIFORMS, &glslCaps->fMaxGeometryImages);
-        GR_GL_GetIntegerv(gli, GR_GL_MAX_FRAGMENT_IMAGE_UNIFORMS, &glslCaps->fMaxFragmentImages);
-        GR_GL_GetIntegerv(gli, GR_GL_MAX_COMBINED_IMAGE_UNIFORMS, &glslCaps->fMaxCombinedImages);
+        GR_GL_GetIntegerv(gli, GR_GL_MAX_VERTEX_IMAGE_UNIFORMS,
+                          &glslCaps->fMaxVertexImageStorages);
+        if (glslCaps->fGeometryShaderSupport) {
+            GR_GL_GetIntegerv(gli, GR_GL_MAX_GEOMETRY_IMAGE_UNIFORMS,
+                              &glslCaps->fMaxGeometryImageStorages);
+        }
+        GR_GL_GetIntegerv(gli, GR_GL_MAX_FRAGMENT_IMAGE_UNIFORMS,
+                          &glslCaps->fMaxFragmentImageStorages);
+        GR_GL_GetIntegerv(gli, GR_GL_MAX_COMBINED_IMAGE_UNIFORMS,
+                          &glslCaps->fMaxCombinedImageStorages);
         // We use one unit for every image uniform
-        glslCaps->fMaxCombinedImages = SkTMin(SkTMin(glslCaps->fMaxCombinedImages, maxUnits),
-                                                     kMaxSaneImages);
-        glslCaps->fMaxVertexImages = SkTMin(maxUnits, glslCaps->fMaxVertexImages);
-        glslCaps->fMaxGeometryImages = SkTMin(maxUnits, glslCaps->fMaxGeometryImages);
-        glslCaps->fMaxFragmentImages =  SkTMin(maxUnits, glslCaps->fMaxFragmentImages);
+        glslCaps->fMaxCombinedImageStorages = SkTMin(SkTMin(glslCaps->fMaxCombinedImageStorages,
+                                                            maxUnits), kMaxSaneImages);
+        glslCaps->fMaxVertexImageStorages = SkTMin(maxUnits, glslCaps->fMaxVertexImageStorages);
+        glslCaps->fMaxGeometryImageStorages = SkTMin(maxUnits, glslCaps->fMaxGeometryImageStorages);
+        glslCaps->fMaxFragmentImageStorages =  SkTMin(maxUnits,
+                                                      glslCaps->fMaxFragmentImageStorages);
+        // HACK: Currently we only use images in a unit test in the fragment shader. The individual
+        // stage image limits aren't exposed through GrShaderCaps. Soon GrShaderCaps and GrGLSLCaps
+        // will merge and the test can look for fragment support.
+        if (!glslCaps->fMaxFragmentImageStorages) {
+            glslCaps->fImageLoadStoreSupport = false;
+        }
     }
 
     /**************************************************************************
@@ -2023,6 +2036,24 @@
         }
     }
 
+    // We currently only support images on rgba textures formats. We could add additional formats
+    // if desired. The shader builder would have to be updated to add swizzles where appropriate
+    // (e.g. where we use GL_RED textures to implement alpha configs).
+    if (this->shaderCaps()->imageLoadStoreSupport()) {
+        fConfigTable[kRGBA_8888_sint_GrPixelConfig].fFlags |=
+                ConfigInfo::kCanUseAsImageStorage_Flag;
+        // In OpenGL ES a texture may only be used with BindImageTexture if it has been made
+        // immutable via TexStorage. We create non-integer textures as mutable textures using
+        // TexImage because we may lazily add MIP levels. Thus, on ES we currently disable image
+        // storage support for non-integer textures.
+        if (kGL_GrGLStandard == ctxInfo.standard()) {
+            fConfigTable[kRGBA_8888_GrPixelConfig].fFlags |= ConfigInfo::kCanUseAsImageStorage_Flag;
+            fConfigTable[kRGBA_float_GrPixelConfig].fFlags |=
+                    ConfigInfo::kCanUseAsImageStorage_Flag;
+            fConfigTable[kRGBA_half_GrPixelConfig].fFlags |= ConfigInfo::kCanUseAsImageStorage_Flag;
+        }
+    }
+
 #ifdef SK_DEBUG
     // Make sure we initialized everything.
     ConfigInfo defaultEntry;
diff --git a/src/gpu/gl/GrGLCaps.h b/src/gpu/gl/GrGLCaps.h
index 496635d..63843a5 100644
--- a/src/gpu/gl/GrGLCaps.h
+++ b/src/gpu/gl/GrGLCaps.h
@@ -126,7 +126,9 @@
             return SkToBool(fConfigTable[config].fFlags & ConfigInfo::kRenderable_Flag);
         }
     }
-
+    bool canConfigBeImageStorage(GrPixelConfig config) const override {
+        return SkToBool(fConfigTable[config].fFlags & ConfigInfo::kCanUseAsImageStorage_Flag);
+    }
     bool canConfigBeFBOColorAttachment(GrPixelConfig config) const {
         return SkToBool(fConfigTable[config].fFlags & ConfigInfo::kFBOColorAttachment_Flag);
     }
@@ -159,6 +161,11 @@
 
     bool getRenderbufferFormat(GrPixelConfig config, GrGLenum* internalFormat) const;
 
+    /** The format to use read/write a texture as an image in a shader */
+    GrGLenum getImageFormat(GrPixelConfig config) const {
+        return fConfigTable[config].fFormats.fSizedInternalFormat;
+    }
+
     /**
     * Gets an array of legal stencil formats. These formats are not guaranteed
     * to be supported by the driver but are legal GLenum names given the GL
@@ -451,7 +458,6 @@
         GrGLenum fExternalFormat[kExternalFormatUsageCnt];
         GrGLenum fExternalType;
 
-
         // Either the base or sized internal format depending on the GL and config.
         GrGLenum fInternalFormatTexImage;
         GrGLenum fInternalFormatRenderbuffer;
@@ -489,6 +495,7 @@
             kFBOColorAttachment_Flag      = 0x10,
             kCanUseTexStorage_Flag        = 0x20,
             kCanUseWithTexelBuffer_Flag   = 0x40,
+            kCanUseAsImageStorage_Flag    = 0x80,
         };
         uint32_t fFlags;
 
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index 9485778..af44af4 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -217,6 +217,7 @@
     fCaps.reset(SkRef(ctx->caps()));
 
     fHWBoundTextureUniqueIDs.reset(this->glCaps().glslCaps()->maxCombinedSamplers());
+    fHWBoundImageStorages.reset(this->glCaps().glslCaps()->maxCombinedImageStorages());
 
     fHWBufferState[kVertex_GrBufferType].fGLTarget = GR_GL_ARRAY_BUFFER;
     fHWBufferState[kIndex_GrBufferType].fGLTarget = GR_GL_ELEMENT_ARRAY_BUFFER;
@@ -557,6 +558,10 @@
             SkASSERT(this->caps()->shaderCaps()->texelBufferSupport());
             fHWBufferTextures[b].fKnownBound = false;
         }
+        for (int i = 0; i < fHWBoundImageStorages.count(); ++i) {
+            SkASSERT(this->caps()->shaderCaps()->imageLoadStoreSupport());
+            fHWBoundImageStorages[i].fTextureUniqueID.makeInvalid();
+        }
     }
 
     if (resetBits & kBlend_GrGLBackendState) {
@@ -3342,6 +3347,27 @@
     }
 }
 
+void GrGLGpu::bindImageStorage(int unitIdx, GrIOType ioType, GrGLTexture *texture) {
+    SkASSERT(texture);
+    if (texture->uniqueID() != fHWBoundImageStorages[unitIdx].fTextureUniqueID ||
+        ioType != fHWBoundImageStorages[unitIdx].fIOType) {
+        GrGLenum access;
+        switch (ioType) {
+            case kRead_GrIOType:
+                access = GR_GL_READ_ONLY;
+                break;
+            case kWrite_GrIOType:
+                access = GR_GL_WRITE_ONLY;
+                break;
+            case kRW_GrIOType:
+                access = GR_GL_READ_WRITE;
+                break;
+        }
+        GrGLenum format = this->glCaps().getImageFormat(texture->config());
+        GL_CALL(BindImageTexture(unitIdx, texture->textureID(), 0, GR_GL_FALSE, 0, access, format));
+    }
+}
+
 void GrGLGpu::generateMipmaps(const GrSamplerParams& params, bool allowSRGBInputs,
                               GrGLTexture* texture) {
     SkASSERT(texture);
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index dff342d..c6e7935 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -62,6 +62,8 @@
 
     void bindTexelBuffer(int unitIdx, GrPixelConfig, GrGLBuffer*);
 
+    void bindImageStorage(int unitIdx, GrIOType, GrGLTexture *);
+
     void generateMipmaps(const GrSamplerParams& params, bool allowSRGBInputs, GrGLTexture* texture);
 
     bool onGetReadPixelsInfo(GrSurface* srcSurface, int readWidth, int readHeight, size_t rowBytes,
@@ -569,6 +571,12 @@
     TriState                                fHWSRGBFramebuffer;
     SkTArray<GrGpuResource::UniqueID, true> fHWBoundTextureUniqueIDs;
 
+    struct Image {
+        GrGpuResource::UniqueID fTextureUniqueID;
+        GrIOType                fIOType;
+    };
+    SkTArray<Image, true>                   fHWBoundImageStorages;
+
     struct BufferTexture {
         BufferTexture() : fTextureID(0), fKnownBound(false),
                           fAttachedBufferUniqueID(SK_InvalidUniqueID),
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index 820b55c..f09b668 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -31,6 +31,7 @@
                          GrGLuint programID,
                          const UniformInfoArray& uniforms,
                          const UniformInfoArray& samplers,
+                         const UniformInfoArray& imageStorages,
                          const VaryingInfoArray& pathProcVaryings,
                          GrGLSLPrimitiveProcessor* geometryProcessor,
                          GrGLSLXferProcessor* xferProcessor,
@@ -46,6 +47,7 @@
     // Assign texture units to sampler uniforms one time up front.
     GL_CALL(UseProgram(fProgramID));
     fProgramDataManager.setSamplers(samplers);
+    fProgramDataManager.setImageStorages(imageStorages);
 }
 
 GrGLProgram::~GrGLProgram() {
@@ -161,6 +163,11 @@
         fGpu->bindTexelBuffer((*nextSamplerIdx)++, access.texelConfig(),
                               static_cast<GrGLBuffer*>(access.buffer()));
     }
+    for (int i = 0; i < processor.numImageStorages(); ++i) {
+        const GrProcessor::ImageStorageAccess& access = processor.imageStorageAccess(i);
+        fGpu->bindImageStorage((*nextSamplerIdx)++, access.ioType(),
+                               static_cast<GrGLTexture *>(access.texture()));
+    }
 }
 
 void GrGLProgram::generateMipmaps(const GrProcessor& processor,
diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h
index d129dda..9c42978 100644
--- a/src/gpu/gl/GrGLProgram.h
+++ b/src/gpu/gl/GrGLProgram.h
@@ -102,9 +102,9 @@
     void generateMipmaps(const GrPrimitiveProcessor&, const GrPipeline&);
 
 protected:
-    typedef GrGLSLProgramDataManager::UniformHandle UniformHandle;
-    typedef GrGLProgramDataManager::UniformInfoArray UniformInfoArray;
-    typedef GrGLProgramDataManager::VaryingInfoArray VaryingInfoArray;
+    using UniformHandle    = GrGLSLProgramDataManager::UniformHandle ;
+    using UniformInfoArray = GrGLProgramDataManager::UniformInfoArray;
+    using VaryingInfoArray = GrGLProgramDataManager::VaryingInfoArray;
 
     GrGLProgram(GrGLGpu*,
                 const GrProgramDesc&,
@@ -112,6 +112,7 @@
                 GrGLuint programID,
                 const UniformInfoArray& uniforms,
                 const UniformInfoArray& samplers,
+                const UniformInfoArray& imageStorages,
                 const VaryingInfoArray&, // used for NVPR only currently
                 GrGLSLPrimitiveProcessor* geometryProcessor,
                 GrGLSLXferProcessor* xferProcessor,
diff --git a/src/gpu/gl/GrGLProgramDataManager.cpp b/src/gpu/gl/GrGLProgramDataManager.cpp
index 3245bd7..863eab7 100644
--- a/src/gpu/gl/GrGLProgramDataManager.cpp
+++ b/src/gpu/gl/GrGLProgramDataManager.cpp
@@ -65,6 +65,16 @@
     }
 }
 
+void GrGLProgramDataManager::setImageStorages(const UniformInfoArray& images) const {
+    for (int i = 0; i < images.count(); ++i) {
+        const UniformInfo& image = images[i];
+        SkASSERT(image.fVisibility);
+        if (kUnusedUniform != image.fLocation) {
+            GR_GL_CALL(fGpu->glInterface(), Uniform1i(image.fLocation, i));
+        }
+    }
+}
+
 void GrGLProgramDataManager::set1i(UniformHandle u, int32_t i) const {
     const Uniform& uni = fUniforms[u.toIndex()];
     SkASSERT(uni.fType == kInt_GrSLType);
diff --git a/src/gpu/gl/GrGLProgramDataManager.h b/src/gpu/gl/GrGLProgramDataManager.h
index 0ef902c..62af4b8 100644
--- a/src/gpu/gl/GrGLProgramDataManager.h
+++ b/src/gpu/gl/GrGLProgramDataManager.h
@@ -47,6 +47,7 @@
 
 
     void setSamplers(const UniformInfoArray& samplers) const;
+    void setImageStorages(const UniformInfoArray &images) const;
 
     /** Functions for uploading uniform values. The varities ending in v can be used to upload to an
     *  array of uniforms. arrayCount must be <= the array count of the uniform.
diff --git a/src/gpu/gl/GrGLUniformHandler.cpp b/src/gpu/gl/GrGLUniformHandler.cpp
index 2def001..81755d5 100644
--- a/src/gpu/gl/GrGLUniformHandler.cpp
+++ b/src/gpu/gl/GrGLUniformHandler.cpp
@@ -80,11 +80,38 @@
     return GrGLSLUniformHandler::SamplerHandle(fSamplers.count() - 1);
 }
 
+GrGLSLUniformHandler::ImageStorageHandle GrGLUniformHandler::addImageStorage(
+        uint32_t visibility, GrSLType type, GrImageStorageFormat format, GrSLMemoryModel model,
+        GrSLRestrict restrict, GrIOType ioType, const char* name) {
+    SkASSERT(name && strlen(name));
+    SkDEBUGCODE(static const uint32_t kVisMask = kVertex_GrShaderFlag | kFragment_GrShaderFlag);
+    SkASSERT(0 == (~kVisMask & visibility));
+    SkASSERT(0 != visibility);
+    SkString mangleName;
+    char prefix = 'u';
+    fProgramBuilder->nameVariable(&mangleName, prefix, name, true);
+
+    UniformInfo& imageStorage = fImageStorages.push_back();
+    imageStorage.fVariable.setName(mangleName);
+
+    SkASSERT(GrSLTypeIsImageStorage(type));
+    imageStorage.fVariable.setType(type);
+    imageStorage.fVariable.setTypeModifier(GrShaderVar::kUniform_TypeModifier);
+    imageStorage.fVariable.setImageStorageFormat(format);
+    imageStorage.fVariable.setMemoryModel(model);
+    imageStorage.fVariable.setRestrict(restrict);
+    imageStorage.fVariable.setIOType(ioType);
+    imageStorage.fVariable.setPrecision(kHigh_GrSLPrecision);
+    imageStorage.fLocation = -1;
+    imageStorage.fVisibility = visibility;
+    return GrGLSLUniformHandler::ImageStorageHandle(fImageStorages.count() - 1);
+}
+
 void GrGLUniformHandler::appendUniformDecls(GrShaderFlags visibility, SkString* out) const {
     for (int i = 0; i < fUniforms.count(); ++i) {
         if (fUniforms[i].fVisibility & visibility) {
             fUniforms[i].fVariable.appendDecl(fProgramBuilder->glslCaps(), out);
-            out->append(";\n");
+            out->append(";");
         }
     }
     for (int i = 0; i < fSamplers.count(); ++i) {
@@ -93,19 +120,29 @@
             out->append(";\n");
         }
     }
+    for (int i = 0; i < fImageStorages.count(); ++i) {
+        if (fImageStorages[i].fVisibility & visibility) {
+            fImageStorages[i].fVariable.appendDecl(fProgramBuilder->glslCaps(), out);
+            out->append(";");
+        }
+    }
 }
 
 void GrGLUniformHandler::bindUniformLocations(GrGLuint programID, const GrGLCaps& caps) {
     if (caps.bindUniformLocationSupport()) {
-        int uniformCnt = fUniforms.count();
-        for (int i = 0; i < uniformCnt; ++i) {
-            GL_CALL(BindUniformLocation(programID, i, fUniforms[i].fVariable.c_str()));
-            fUniforms[i].fLocation = i;
+        int currUniform = 0;
+        for (int i = 0; i < fUniforms.count(); ++i, ++currUniform) {
+            GL_CALL(BindUniformLocation(programID, currUniform, fUniforms[i].fVariable.c_str()));
+            fUniforms[i].fLocation = currUniform;
         }
-        for (int i = 0; i < fSamplers.count(); ++i) {
-            GrGLint location = i + uniformCnt;
-            GL_CALL(BindUniformLocation(programID, location, fSamplers[i].fVariable.c_str()));
-            fSamplers[i].fLocation = location;
+        for (int i = 0; i < fSamplers.count(); ++i, ++currUniform) {
+            GL_CALL(BindUniformLocation(programID, currUniform, fSamplers[i].fVariable.c_str()));
+            fSamplers[i].fLocation = currUniform;
+        }
+        for (int i = 0; i < fImageStorages.count(); ++i) {
+            GL_CALL(BindUniformLocation(programID, currUniform,
+                                        fImageStorages[i].fVariable.c_str()));
+            fImageStorages[i].fLocation = currUniform;
         }
     }
 }
@@ -123,6 +160,12 @@
             GL_CALL_RET(location, GetUniformLocation(programID, fSamplers[i].fVariable.c_str()));
             fSamplers[i].fLocation = location;
         }
+        for (int i = 0; i < fImageStorages.count(); ++i) {
+            GrGLint location;
+            GL_CALL_RET(location, GetUniformLocation(programID,
+                                                     fImageStorages[i].fVariable.c_str()));
+            fImageStorages[i].fLocation = location;
+        }
     }
 }
 
diff --git a/src/gpu/gl/GrGLUniformHandler.h b/src/gpu/gl/GrGLUniformHandler.h
index d3aa2f8..da7b13c 100644
--- a/src/gpu/gl/GrGLUniformHandler.h
+++ b/src/gpu/gl/GrGLUniformHandler.h
@@ -29,7 +29,8 @@
     explicit GrGLUniformHandler(GrGLSLProgramBuilder* program)
         : INHERITED(program)
         , fUniforms(kUniformsPerBlock)
-        , fSamplers(kUniformsPerBlock) {}
+        , fSamplers(kUniformsPerBlock)
+        , fImageStorages(kUniformsPerBlock) {}
 
     UniformHandle internalAddUniformArray(uint32_t visibility,
                                           GrSLType type,
@@ -46,10 +47,18 @@
         return fSamplers[handle.toIndex()].fVariable;
     }
 
+    ImageStorageHandle addImageStorage(uint32_t visibility, GrSLType, GrImageStorageFormat,
+                                       GrSLMemoryModel, GrSLRestrict, GrIOType,
+                                       const char* name) override;
+
     GrSwizzle samplerSwizzle(SamplerHandle handle) const override {
         return fSamplerSwizzles[handle.toIndex()];
     }
 
+    const GrShaderVar& imageStorageVariable(ImageStorageHandle handle) const override {
+        return fImageStorages[handle.toIndex()].fVariable;
+    }
+
     void appendUniformDecls(GrShaderFlags visibility, SkString*) const override;
 
     // Manually set uniform locations for all our uniforms.
@@ -66,6 +75,7 @@
     UniformInfoArray    fUniforms;
     UniformInfoArray    fSamplers;
     SkTArray<GrSwizzle> fSamplerSwizzles;
+    UniformInfoArray    fImageStorages;
 
     friend class GrGLProgramBuilder;
 
diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.cpp b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
index a1ad572..a774570 100644
--- a/src/gpu/gl/builders/GrGLProgramBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
@@ -237,6 +237,7 @@
                            programID,
                            fUniformHandler.fUniforms,
                            fUniformHandler.fSamplers,
+                           fUniformHandler.fImageStorages,
                            fVaryingHandler.fPathProcVaryingInfos,
                            fGeometryProcessor,
                            fXferProcessor,
diff --git a/src/gpu/glsl/GrGLSL.h b/src/gpu/glsl/GrGLSL.h
index d27b25f..417f6d7 100644
--- a/src/gpu/glsl/GrGLSL.h
+++ b/src/gpu/glsl/GrGLSL.h
@@ -121,6 +121,10 @@
             return "texture2D";
         case kSampler_GrSLType:
             return "sampler";
+        case kImageStorage2D_GrSLType:
+            return "image2D";
+        case kIImageStorage2D_GrSLType:
+            return "iimage2D";
     }
     SkFAIL("Unknown shader var type.");
     return ""; // suppress warning
diff --git a/src/gpu/glsl/GrGLSLCaps.cpp b/src/gpu/glsl/GrGLSLCaps.cpp
index 5bb22b3..57e5a0c 100644
--- a/src/gpu/glsl/GrGLSLCaps.cpp
+++ b/src/gpu/glsl/GrGLSLCaps.cpp
@@ -47,10 +47,10 @@
     fMaxGeometrySamplers = 0;
     fMaxFragmentSamplers = 0;
     fMaxCombinedSamplers = 0;
-    fMaxVertexImages = 0;
-    fMaxGeometryImages = 0;
-    fMaxFragmentImages = 0;
-    fMaxCombinedImages   = 0;
+    fMaxVertexImageStorages = 0;
+    fMaxGeometryImageStorages = 0;
+    fMaxFragmentImageStorages = 0;
+    fMaxCombinedImageStorages   = 0;
     fAdvBlendEqInteraction = kNotSupported_AdvBlendEqInteraction;
 }
 
@@ -95,10 +95,10 @@
     r.appendf("Max GS Samplers: %d\n", fMaxGeometrySamplers);
     r.appendf("Max FS Samplers: %d\n", fMaxFragmentSamplers);
     r.appendf("Max Combined Samplers: %d\n", fMaxFragmentSamplers);
-    r.appendf("Max VS Images: %d\n", fMaxVertexImages);
-    r.appendf("Max GS Images: %d\n", fMaxGeometryImages);
-    r.appendf("Max FS Images: %d\n", fMaxFragmentImages);
-    r.appendf("Max Combined Images: %d\n", fMaxFragmentImages);
+    r.appendf("Max VS Image Storages: %d\n", fMaxVertexImageStorages);
+    r.appendf("Max GS Image Storages: %d\n", fMaxGeometryImageStorages);
+    r.appendf("Max FS Image Storages: %d\n", fMaxFragmentImageStorages);
+    r.appendf("Max Combined Image Storages: %d\n", fMaxFragmentImageStorages);
     r.appendf("Advanced blend equation interaction: %s\n",
               kAdvBlendEqInteractionStr[fAdvBlendEqInteraction]);
     return r;
diff --git a/src/gpu/glsl/GrGLSLCaps.h b/src/gpu/glsl/GrGLSLCaps.h
index 4c53ad0..11ce754 100644
--- a/src/gpu/glsl/GrGLSLCaps.h
+++ b/src/gpu/glsl/GrGLSLCaps.h
@@ -157,13 +157,13 @@
 
     int maxCombinedSamplers() const { return fMaxCombinedSamplers; }
 
-    int maxVertexImages() const { return fMaxVertexImages; }
+    int maxVertexImageStorages() const { return fMaxVertexImageStorages; }
 
-    int maxGeometryImages() const { return fMaxGeometryImages; }
+    int maxGeometryImageStorages() const { return fMaxGeometryImageStorages; }
 
-    int maxFragmentImages() const { return fMaxFragmentImages; }
+    int maxFragmentImageStorages() const { return fMaxFragmentImageStorages; }
 
-    int maxCombinedImages() const { return fMaxCombinedImages; }
+    int maxCombinedImageStorages() const { return fMaxCombinedImageStorages; }
 
     /**
      * Given a texture's config, this determines what swizzle must be appended to accesses to the
@@ -238,10 +238,10 @@
     int fMaxFragmentSamplers;
     int fMaxCombinedSamplers;
 
-    int fMaxVertexImages;
-    int fMaxGeometryImages;
-    int fMaxFragmentImages;
-    int fMaxCombinedImages;
+    int fMaxVertexImageStorages;
+    int fMaxGeometryImageStorages;
+    int fMaxFragmentImageStorages;
+    int fMaxCombinedImageStorages;
 
     AdvBlendEqInteraction fAdvBlendEqInteraction;
 
diff --git a/src/gpu/glsl/GrGLSLFragmentProcessor.cpp b/src/gpu/glsl/GrGLSLFragmentProcessor.cpp
index 5ae7fee..8c32482 100644
--- a/src/gpu/glsl/GrGLSLFragmentProcessor.cpp
+++ b/src/gpu/glsl/GrGLSLFragmentProcessor.cpp
@@ -49,6 +49,7 @@
     TransformedCoordVars coordVars = args.fTransformedCoords.childInputs(childIndex);
     TextureSamplers textureSamplers = args.fTexSamplers.childInputs(childIndex);
     BufferSamplers bufferSamplers = args.fBufferSamplers.childInputs(childIndex);
+    ImageStorages imageStorages = args.fImageStorages.childInputs(childIndex);
     EmitArgs childArgs(fragBuilder,
                        args.fUniformHandler,
                        args.fGLSLCaps,
@@ -58,6 +59,7 @@
                        coordVars,
                        textureSamplers,
                        bufferSamplers,
+                       imageStorages,
                        args.fGpImplementsDistanceVector);
     this->childProcessor(childIndex)->emitCode(childArgs);
     fragBuilder->codeAppend("}\n");
diff --git a/src/gpu/glsl/GrGLSLFragmentProcessor.h b/src/gpu/glsl/GrGLSLFragmentProcessor.h
index aba68b7..c815423 100644
--- a/src/gpu/glsl/GrGLSLFragmentProcessor.h
+++ b/src/gpu/glsl/GrGLSLFragmentProcessor.h
@@ -11,13 +11,13 @@
 #include "GrFragmentProcessor.h"
 #include "GrShaderVar.h"
 #include "glsl/GrGLSLProgramDataManager.h"
+#include "glsl/GrGLSLUniformHandler.h"
 
 class GrProcessor;
 class GrProcessorKeyBuilder;
 class GrGLSLCaps;
 class GrGLSLFPBuilder;
 class GrGLSLFPFragmentBuilder;
-class GrGLSLUniformHandler;
 
 class GrGLSLFragmentProcessor {
 public:
@@ -29,8 +29,9 @@
         }
     }
 
-    typedef GrGLSLProgramDataManager::UniformHandle UniformHandle;
-    typedef GrGLSLProgramDataManager::UniformHandle SamplerHandle;
+    using UniformHandle      = GrGLSLUniformHandler::UniformHandle;
+    using SamplerHandle      = GrGLSLUniformHandler::SamplerHandle;
+    using ImageStorageHandle = GrGLSLUniformHandler::ImageStorageHandle;
 
 private:
     /**
@@ -74,6 +75,8 @@
                                                  &GrProcessor::numTextureSamplers>;
     using BufferSamplers = BuilderInputProvider<SamplerHandle, GrProcessor,
                                                 &GrProcessor::numBuffers>;
+    using ImageStorages = BuilderInputProvider<ImageStorageHandle, GrProcessor,
+                                               &GrProcessor::numImageStorages>;
 
     /** Called when the program stage should insert its code into the shaders. The code in each
         shader will be in its own block ({}) and so locally scoped names will not collide across
@@ -99,6 +102,12 @@
         @param bufferSamplers    Contains one entry for each BufferAccess of the GrProcessor. These
                                  can be passed to the builder to emit buffer reads in the generated
                                  code.
+        @param imageStorages     Contains one entry for each ImageStorageAccess of the GrProcessor.
+                                 These can be passed to the builder to emit image loads and stores
+                                 in the generated code.
+        @param gpImplementsDistanceVector
+                                 Does the GrGeometryProcessor implement the feature where it
+                                 provides a vector to the nearest edge of the shape being rendered.
      */
     struct EmitArgs {
         EmitArgs(GrGLSLFPFragmentBuilder* fragBuilder,
@@ -110,6 +119,7 @@
                  const TransformedCoordVars& transformedCoordVars,
                  const TextureSamplers& textureSamplers,
                  const BufferSamplers& bufferSamplers,
+                 const ImageStorages& imageStorages,
                  bool gpImplementsDistanceVector)
             : fFragBuilder(fragBuilder)
             , fUniformHandler(uniformHandler)
@@ -120,6 +130,7 @@
             , fTransformedCoords(transformedCoordVars)
             , fTexSamplers(textureSamplers)
             , fBufferSamplers(bufferSamplers)
+            , fImageStorages(imageStorages)
             , fGpImplementsDistanceVector(gpImplementsDistanceVector) {}
         GrGLSLFPFragmentBuilder* fFragBuilder;
         GrGLSLUniformHandler* fUniformHandler;
@@ -130,6 +141,7 @@
         const TransformedCoordVars& fTransformedCoords;
         const TextureSamplers& fTexSamplers;
         const BufferSamplers& fBufferSamplers;
+        const ImageStorages& fImageStorages;
         bool fGpImplementsDistanceVector;
     };
 
diff --git a/src/gpu/glsl/GrGLSLPrimitiveProcessor.h b/src/gpu/glsl/GrGLSLPrimitiveProcessor.h
index 1991639..b398cfd 100644
--- a/src/gpu/glsl/GrGLSLPrimitiveProcessor.h
+++ b/src/gpu/glsl/GrGLSLPrimitiveProcessor.h
@@ -11,6 +11,7 @@
 #include "GrFragmentProcessor.h"
 #include "GrPrimitiveProcessor.h"
 #include "glsl/GrGLSLProgramDataManager.h"
+#include "glsl/GrGLSLUniformHandler.h"
 
 class GrBatchTracker;
 class GrPrimitiveProcessor;
@@ -18,7 +19,6 @@
 class GrGLSLPPFragmentBuilder;
 class GrGLSLGeometryBuilder;
 class GrGLSLGPBuilder;
-class GrGLSLUniformHandler;
 class GrGLSLVaryingHandler;
 class GrGLSLVertexBuilder;
 
@@ -28,8 +28,9 @@
 
     virtual ~GrGLSLPrimitiveProcessor() {}
 
-    typedef GrGLSLProgramDataManager::UniformHandle UniformHandle;
-    typedef GrGLSLProgramDataManager::UniformHandle SamplerHandle;
+    using UniformHandle      = GrGLSLProgramDataManager::UniformHandle;
+    using SamplerHandle      = GrGLSLUniformHandler::SamplerHandle;
+    using ImageStorageHandle = GrGLSLUniformHandler::ImageStorageHandle;
 
     /**
      * This class provides access to the GrCoordTransforms across all GrFragmentProcessors in a
@@ -77,6 +78,7 @@
                  const char* distanceVectorName,
                  const SamplerHandle* texSamplers,
                  const SamplerHandle* bufferSamplers,
+                 const ImageStorageHandle* imageStorages,
                  FPCoordTransformHandler* transformHandler)
             : fVertBuilder(vertBuilder)
             , fGeomBuilder(geomBuilder)
@@ -90,6 +92,7 @@
             , fDistanceVectorName(distanceVectorName)
             , fTexSamplers(texSamplers)
             , fBufferSamplers(bufferSamplers)
+            , fImageStorages(imageStorages)
             , fFPCoordTransformHandler(transformHandler) {}
         GrGLSLVertexBuilder* fVertBuilder;
         GrGLSLGeometryBuilder* fGeomBuilder;
@@ -103,6 +106,7 @@
         const char* fDistanceVectorName;
         const SamplerHandle* fTexSamplers;
         const SamplerHandle* fBufferSamplers;
+        const ImageStorageHandle* fImageStorages;
         FPCoordTransformHandler* fFPCoordTransformHandler;
     };
 
diff --git a/src/gpu/glsl/GrGLSLProgramBuilder.cpp b/src/gpu/glsl/GrGLSLProgramBuilder.cpp
index e8097c7..0c1661d 100644
--- a/src/gpu/glsl/GrGLSLProgramBuilder.cpp
+++ b/src/gpu/glsl/GrGLSLProgramBuilder.cpp
@@ -31,7 +31,10 @@
     , fXferProcessor(nullptr)
     , fNumVertexSamplers(0)
     , fNumGeometrySamplers(0)
-    , fNumFragmentSamplers(0) {
+    , fNumFragmentSamplers(0)
+    , fNumVertexImageStorages(0)
+    , fNumGeometryImageStorages(0)
+    , fNumFragmentImageStorages(0) {
 }
 
 void GrGLSLProgramBuilder::addFeature(GrShaderFlags shaders,
@@ -66,7 +69,7 @@
         this->emitFSOutputSwizzle(this->pipeline().getXferProcessor().hasSecondaryOutput());
     }
 
-    return this->checkSamplerCounts();
+    return this->checkSamplerCounts() && this->checkImageStorageCounts();
 }
 
 void GrGLSLProgramBuilder::emitAndInstallPrimProc(const GrPrimitiveProcessor& proc,
@@ -97,9 +100,10 @@
     SkASSERT(!fGeometryProcessor);
     fGeometryProcessor = proc.createGLSLInstance(*this->glslCaps());
 
-    SkSTArray<4, SamplerHandle> texSamplers(proc.numTextureSamplers());
-    SkSTArray<2, SamplerHandle> bufferSamplers(proc.numBuffers());
-    this->emitSamplers(proc, &texSamplers, &bufferSamplers);
+    SkSTArray<4, SamplerHandle>      texSamplers(proc.numTextureSamplers());
+    SkSTArray<2, SamplerHandle>      bufferSamplers(proc.numBuffers());
+    SkSTArray<2, ImageStorageHandle> imageStorages(proc.numImageStorages());
+    this->emitSamplersAndImageStorages(proc, &texSamplers, &bufferSamplers, &imageStorages);
 
     GrGLSLPrimitiveProcessor::FPCoordTransformHandler transformHandler(fPipeline,
                                                                        &fTransformedCoordVars);
@@ -115,6 +119,7 @@
                                            distanceVectorName,
                                            texSamplers.begin(),
                                            bufferSamplers.begin(),
+                                           imageStorages.begin(),
                                            &transformHandler);
     fGeometryProcessor->emitCode(args);
 
@@ -163,15 +168,18 @@
 
     SkSTArray<4, SamplerHandle> textureSamplerArray(fp.numTextureSamplers());
     SkSTArray<2, SamplerHandle> bufferSamplerArray(fp.numBuffers());
+    SkSTArray<2, ImageStorageHandle> imageStorageArray(fp.numImageStorages());
     GrFragmentProcessor::Iter iter(&fp);
     while (const GrFragmentProcessor* subFP = iter.next()) {
-        this->emitSamplers(*subFP, &textureSamplerArray, &bufferSamplerArray);
+        this->emitSamplersAndImageStorages(*subFP, &textureSamplerArray, &bufferSamplerArray,
+                                           &imageStorageArray);
     }
 
     const GrShaderVar* coordVars = fTransformedCoordVars.begin() + transformedCoordVarsIdx;
     GrGLSLFragmentProcessor::TransformedCoordVars coords(&fp, coordVars);
     GrGLSLFragmentProcessor::TextureSamplers textureSamplers(&fp, textureSamplerArray.begin());
     GrGLSLFragmentProcessor::BufferSamplers bufferSamplers(&fp, bufferSamplerArray.begin());
+    GrGLSLFragmentProcessor::ImageStorages imageStorages(&fp, imageStorageArray.begin());
     GrGLSLFragmentProcessor::EmitArgs args(&fFS,
                                            this->uniformHandler(),
                                            this->glslCaps(),
@@ -181,6 +189,7 @@
                                            coords,
                                            textureSamplers,
                                            bufferSamplers,
+                                           imageStorages,
                                            this->primitiveProcessor().implementsDistanceVector());
 
     fragProc->emitCode(args);
@@ -217,9 +226,10 @@
     openBrace.printf("{ // Xfer Processor: %s\n", xp.name());
     fFS.codeAppend(openBrace.c_str());
 
-    SkSTArray<4, SamplerHandle> texSamplers(xp.numTextureSamplers());
-    SkSTArray<2, SamplerHandle> bufferSamplers(xp.numBuffers());
-    this->emitSamplers(xp, &texSamplers, &bufferSamplers);
+    SkSTArray<4, SamplerHandle>      texSamplers(xp.numTextureSamplers());
+    SkSTArray<2, SamplerHandle>      bufferSamplers(xp.numBuffers());
+    SkSTArray<2, ImageStorageHandle> imageStorageArray(xp.numImageStorages());
+    this->emitSamplersAndImageStorages(xp, &texSamplers, &bufferSamplers, &imageStorageArray);
 
     bool usePLSDstRead = (plsState == GrPixelLocalStorageState::kFinish_GrPixelLocalStorageState);
     GrGLSLXferProcessor::EmitArgs args(&fFS,
@@ -231,6 +241,7 @@
                                        fFS.getSecondaryColorOutputName(),
                                        texSamplers.begin(),
                                        bufferSamplers.begin(),
+                                       imageStorageArray.begin(),
                                        usePLSDstRead);
     fXferProcessor->emitCode(args);
 
@@ -240,13 +251,16 @@
     fFS.codeAppend("}");
 }
 
-void GrGLSLProgramBuilder::emitSamplers(const GrProcessor& processor,
-                                        SkTArray<SamplerHandle>* outTexSamplers,
-                                        SkTArray<SamplerHandle>* outBufferSamplers) {
+void GrGLSLProgramBuilder::emitSamplersAndImageStorages(
+        const GrProcessor& processor,
+        SkTArray<SamplerHandle>* outTexSamplerHandles,
+        SkTArray<SamplerHandle>* outBufferSamplerHandles,
+        SkTArray<ImageStorageHandle>* outImageStorageHandles) {
     SkString name;
     int numTextureSamplers = processor.numTextureSamplers();
     for (int t = 0; t < numTextureSamplers; ++t) {
         const GrProcessor::TextureSampler& sampler = processor.textureSampler(t);
+        name.printf("TextureSampler_%d", outTexSamplerHandles->count());
         GrSLType samplerType = sampler.texture()->texturePriv().samplerType();
         if (kTextureExternalSampler_GrSLType == samplerType) {
             const char* externalFeatureString = this->glslCaps()->externalTextureExtensionString();
@@ -256,9 +270,9 @@
                              1 << GrGLSLShaderBuilder::kExternalTexture_GLSLPrivateFeature,
                              externalFeatureString);
         }
-        name.printf("TextureSampler_%d", outTexSamplers->count());
-        this->emitSampler(samplerType, sampler.texture()->config(),
-                          name.c_str(), sampler.visibility(), outTexSamplers);
+        this->emitSampler(samplerType, sampler.texture()->config(), name.c_str(),
+                          sampler.visibility(), outTexSamplerHandles);
+
     }
 
     if (int numBuffers = processor.numBuffers()) {
@@ -267,9 +281,9 @@
 
         for (int b = 0; b < numBuffers; ++b) {
             const GrProcessor::BufferAccess& access = processor.bufferAccess(b);
-            name.printf("BufferSampler_%d", outBufferSamplers->count());
+            name.printf("BufferSampler_%d", outBufferSamplerHandles->count());
             this->emitSampler(kBufferSampler_GrSLType, access.texelConfig(), name.c_str(),
-                              access.visibility(), outBufferSamplers);
+                              access.visibility(), outBufferSamplerHandles);
             texelBufferVisibility |= access.visibility();
         }
 
@@ -279,13 +293,19 @@
                              extension);
         }
     }
+    int numImageStorages = processor.numImageStorages();
+    for (int i = 0; i < numImageStorages; ++i) {
+        const GrProcessor::ImageStorageAccess& imageStorageAccess = processor.imageStorageAccess(i);
+        name.printf("Image_%d", outImageStorageHandles->count());
+        this->emitImageStorage(imageStorageAccess, name.c_str(), outImageStorageHandles);
+    }
 }
 
 void GrGLSLProgramBuilder::emitSampler(GrSLType samplerType,
                                        GrPixelConfig config,
                                        const char* name,
                                        GrShaderFlags visibility,
-                                       SkTArray<SamplerHandle>* outSamplers) {
+                                       SkTArray<SamplerHandle>* outSamplerHandles) {
     if (visibility & kVertex_GrShaderFlag) {
         ++fNumVertexSamplers;
     }
@@ -298,12 +318,31 @@
     }
     GrSLPrecision precision = this->glslCaps()->samplerPrecision(config, visibility);
     GrSwizzle swizzle = this->glslCaps()->configTextureSwizzle(config);
-    SamplerHandle handle = this->uniformHandler()->addSampler(visibility,
-                                                              swizzle,
-                                                              samplerType,
-                                                              precision,
-                                                              name);
-    outSamplers->emplace_back(handle);
+    outSamplerHandles->emplace_back(this->uniformHandler()->addSampler(visibility,
+                                                                       swizzle,
+                                                                       samplerType,
+                                                                       precision,
+                                                                       name));
+}
+
+void GrGLSLProgramBuilder::emitImageStorage(const GrProcessor::ImageStorageAccess& access,
+                                            const char* name,
+                                            SkTArray<ImageStorageHandle>* outImageStorageHandles) {
+    if (access.visibility() & kVertex_GrShaderFlag) {
+        ++fNumVertexImageStorages;
+    }
+    if (access.visibility() & kGeometry_GrShaderFlag) {
+        SkASSERT(this->primitiveProcessor().willUseGeoShader());
+        ++fNumGeometryImageStorages;
+    }
+    if (access.visibility() & kFragment_GrShaderFlag) {
+        ++fNumFragmentImageStorages;
+    }
+    GrSLType uniformType = access.texture()->texturePriv().imageStorageType();
+    ImageStorageHandle handle = this->uniformHandler()->addImageStorage(access.visibility(),
+         uniformType, access.format(), access.memoryModel(), access.restrict(), access.ioType(),
+         name);
+    outImageStorageHandles->emplace_back(handle);
 }
 
 void GrGLSLProgramBuilder::emitFSOutputSwizzle(bool hasSecondaryOutput) {
@@ -345,6 +384,30 @@
     return true;
 }
 
+bool GrGLSLProgramBuilder::checkImageStorageCounts() {
+    const GrGLSLCaps& glslCaps = *this->glslCaps();
+    if (fNumVertexImageStorages > glslCaps.maxVertexImageStorages()) {
+        GrCapsDebugf(this->caps(), "Program would use too many vertex images\n");
+        return false;
+    }
+    if (fNumGeometryImageStorages > glslCaps.maxGeometryImageStorages()) {
+        GrCapsDebugf(this->caps(), "Program would use too many geometry images\n");
+        return false;
+    }
+    if (fNumFragmentImageStorages > glslCaps.maxFragmentImageStorages()) {
+        GrCapsDebugf(this->caps(), "Program would use too many fragment images\n");
+        return false;
+    }
+    // If the same image is used in two different shaders, it counts as two combined images.
+    int numCombinedImages = fNumVertexImageStorages + fNumGeometryImageStorages +
+        fNumFragmentImageStorages;
+    if (numCombinedImages > glslCaps.maxCombinedImageStorages()) {
+        GrCapsDebugf(this->caps(), "Program would use too many combined images\n");
+        return false;
+    }
+    return true;
+}
+
 #ifdef SK_DEBUG
 void GrGLSLProgramBuilder::verify(const GrPrimitiveProcessor& gp) {
     SkASSERT(fFS.usedProcessorFeatures() == gp.requiredFeatures());
@@ -393,7 +456,6 @@
     this->uniformHandler()->appendUniformDecls(visibility, out);
 }
 
-
 void GrGLSLProgramBuilder::addRTAdjustmentUniform(GrSLPrecision precision,
                                                   const char* name,
                                                   const char** outName) {
diff --git a/src/gpu/glsl/GrGLSLProgramBuilder.h b/src/gpu/glsl/GrGLSLProgramBuilder.h
index 6c1eb3b..5543537 100644
--- a/src/gpu/glsl/GrGLSLProgramBuilder.h
+++ b/src/gpu/glsl/GrGLSLProgramBuilder.h
@@ -28,7 +28,9 @@
 
 class GrGLSLProgramBuilder {
 public:
-    typedef GrGLSLUniformHandler::UniformHandle UniformHandle;
+    using UniformHandle      = GrGLSLUniformHandler::UniformHandle;
+    using SamplerHandle      = GrGLSLUniformHandler::SamplerHandle;
+    using ImageStorageHandle = GrGLSLUniformHandler::ImageStorageHandle;
 
     virtual ~GrGLSLProgramBuilder() {}
 
@@ -42,8 +44,6 @@
 
     void appendUniformDecls(GrShaderFlags visibility, SkString*) const;
 
-    typedef GrGLSLUniformHandler::SamplerHandle SamplerHandle;
-
     const GrShaderVar& samplerVariable(SamplerHandle handle) const {
         return this->uniformHandler()->samplerVariable(handle);
     }
@@ -52,6 +52,10 @@
         return this->uniformHandler()->samplerSwizzle(handle);
     }
 
+    const GrShaderVar& imageStorageVariable(ImageStorageHandle handle) const {
+        return this->uniformHandler()->imageStorageVariable(handle);
+    }
+
     // Handles for program uniforms (other than per-effect uniforms)
     struct BuiltinUniformHandles {
         UniformHandle       fRTAdjustmentUni;
@@ -156,17 +160,18 @@
                                 const GrGLSLExpr4& coverageIn,
                                 bool ignoresCoverage,
                                 GrPixelLocalStorageState plsState);
-
-    void emitSamplers(const GrProcessor& processor,
-                      SkTArray<SamplerHandle>* outTexSamplers,
-                      SkTArray<SamplerHandle>* outBufferSamplers);
-    void emitSampler(GrSLType samplerType,
-                     GrPixelConfig,
-                     const char* name,
-                     GrShaderFlags visibility,
-                     SkTArray<SamplerHandle>* outSamplers);
+    void emitSamplersAndImageStorages(const GrProcessor& processor,
+                                      SkTArray<SamplerHandle>* outTexSamplerHandles,
+                                      SkTArray<SamplerHandle>* outBufferSamplerHandles,
+                                      SkTArray<ImageStorageHandle>* outImageStorageHandles);
+    void emitSampler(GrSLType samplerType, GrPixelConfig, const char* name,
+                     GrShaderFlags visibility, SkTArray<SamplerHandle >* outSamplerHandles);
+    void emitImageStorage(const GrProcessor::ImageStorageAccess&,
+                          const char* name,
+                          SkTArray<ImageStorageHandle>* outImageStorageHandles);
     void emitFSOutputSwizzle(bool hasSecondaryOutput);
     bool checkSamplerCounts();
+    bool checkImageStorageCounts();
 
 #ifdef SK_DEBUG
     void verify(const GrPrimitiveProcessor&);
@@ -177,6 +182,9 @@
     int                         fNumVertexSamplers;
     int                         fNumGeometrySamplers;
     int                         fNumFragmentSamplers;
+    int                         fNumVertexImageStorages;
+    int                         fNumGeometryImageStorages;
+    int                         fNumFragmentImageStorages;
     SkSTArray<4, GrShaderVar>   fTransformedCoordVars;
 };
 
diff --git a/src/gpu/glsl/GrGLSLShaderBuilder.cpp b/src/gpu/glsl/GrGLSLShaderBuilder.cpp
index b6e7ce9..79e977c 100644
--- a/src/gpu/glsl/GrGLSLShaderBuilder.cpp
+++ b/src/gpu/glsl/GrGLSLShaderBuilder.cpp
@@ -162,6 +162,16 @@
     this->appendTexelFetch(&this->code(), samplerHandle, coordExpr);
 }
 
+void GrGLSLShaderBuilder::appendImageStorageLoad(SkString* out, ImageStorageHandle handle,
+                                                 const char* coordExpr) {
+    const GrShaderVar& imageStorage = fProgramBuilder->imageStorageVariable(handle);
+    out->appendf("imageLoad(%s, %s)", imageStorage.c_str(), coordExpr);
+}
+
+void GrGLSLShaderBuilder::appendImageStorageLoad(ImageStorageHandle handle, const char* coordExpr) {
+    this->appendImageStorageLoad(&this->code(), handle, coordExpr);
+}
+
 bool GrGLSLShaderBuilder::addFeature(uint32_t featureBit, const char* extensionName) {
     if (featureBit & fFeaturesAddedMask) {
         return false;
diff --git a/src/gpu/glsl/GrGLSLShaderBuilder.h b/src/gpu/glsl/GrGLSLShaderBuilder.h
index b568369..983d50b 100644
--- a/src/gpu/glsl/GrGLSLShaderBuilder.h
+++ b/src/gpu/glsl/GrGLSLShaderBuilder.h
@@ -25,7 +25,8 @@
     GrGLSLShaderBuilder(GrGLSLProgramBuilder* program);
     virtual ~GrGLSLShaderBuilder() {}
 
-    typedef GrGLSLUniformHandler::SamplerHandle SamplerHandle;
+    using SamplerHandle      = GrGLSLUniformHandler::SamplerHandle;
+    using ImageStorageHandle = GrGLSLUniformHandler::ImageStorageHandle;
 
     /** Appends a 2D texture sample with projection if necessary. coordType must either be Vec2f or
         Vec3f. The latter is interpreted as projective texture coords. The vec length and swizzle
@@ -72,6 +73,11 @@
     /** Version of above that appends the result to the shader code instead.*/
     void appendTexelFetch(SamplerHandle, const char* coordExpr);
 
+    /** Creates a string of shader code that performs an image load. */
+    void appendImageStorageLoad(SkString* out, ImageStorageHandle, const char* coordExpr);
+    /** Version of above that appends the result to the shader code instead. */
+    void appendImageStorageLoad(ImageStorageHandle, const char* coordExpr);
+
     /**
     * Adds a constant declaration to the top of the shader.
     */
diff --git a/src/gpu/glsl/GrGLSLUniformHandler.h b/src/gpu/glsl/GrGLSLUniformHandler.h
index d49fbd4..3d21c1c 100644
--- a/src/gpu/glsl/GrGLSLUniformHandler.h
+++ b/src/gpu/glsl/GrGLSLUniformHandler.h
@@ -18,8 +18,9 @@
 public:
     virtual ~GrGLSLUniformHandler() {}
 
-    typedef GrGLSLProgramDataManager::UniformHandle UniformHandle;
-    typedef GrGLSLProgramDataManager::UniformHandle SamplerHandle;
+    using UniformHandle = GrGLSLProgramDataManager::UniformHandle;
+    GR_DEFINE_RESOURCE_HANDLE_CLASS(SamplerHandle);
+    GR_DEFINE_RESOURCE_HANDLE_CLASS(ImageStorageHandle);
 
     /** Add a uniform variable to the current program, that has visibility in one or more shaders.
         visibility is a bitfield of GrShaderFlag values indicating from which shaders the uniform
@@ -67,6 +68,11 @@
     virtual SamplerHandle addSampler(uint32_t visibility, GrSwizzle, GrSLType, GrSLPrecision,
                                      const char* name) = 0;
 
+    virtual const GrShaderVar& imageStorageVariable(ImageStorageHandle) const = 0;
+    virtual ImageStorageHandle addImageStorage(uint32_t visibility, GrSLType type,
+                                               GrImageStorageFormat, GrSLMemoryModel, GrSLRestrict,
+                                               GrIOType, const char* name) = 0;
+
     virtual UniformHandle internalAddUniformArray(uint32_t visibility,
                                                   GrSLType type,
                                                   GrSLPrecision precision,
diff --git a/src/gpu/glsl/GrGLSLXferProcessor.h b/src/gpu/glsl/GrGLSLXferProcessor.h
index bf6ee64..69e0072 100644
--- a/src/gpu/glsl/GrGLSLXferProcessor.h
+++ b/src/gpu/glsl/GrGLSLXferProcessor.h
@@ -9,10 +9,10 @@
 #define GrGLSLXferProcessor_DEFINED
 
 #include "glsl/GrGLSLProgramDataManager.h"
+#include "glsl/GrGLSLUniformHandler.h"
 
 class GrXferProcessor;
 class GrGLSLCaps;
-class GrGLSLUniformHandler;
 class GrGLSLXPBuilder;
 class GrGLSLXPFragmentBuilder;
 
@@ -21,7 +21,8 @@
     GrGLSLXferProcessor() {}
     virtual ~GrGLSLXferProcessor() {}
 
-    typedef GrGLSLProgramDataManager::UniformHandle SamplerHandle;
+    using SamplerHandle        = GrGLSLUniformHandler::SamplerHandle;
+    using ImageStorageHandle   = GrGLSLUniformHandler::ImageStorageHandle;
 
     struct EmitArgs {
         EmitArgs(GrGLSLXPFragmentBuilder* fragBuilder,
@@ -34,6 +35,7 @@
                  const char* outputSecondary,
                  const SamplerHandle* texSamplers,
                  const SamplerHandle* bufferSamplers,
+                 const ImageStorageHandle* imageStorages,
                  const bool usePLSDstRead)
             : fXPFragBuilder(fragBuilder)
             , fUniformHandler(uniformHandler)
@@ -45,6 +47,7 @@
             , fOutputSecondary(outputSecondary)
             , fTexSamplers(texSamplers)
             , fBufferSamplers(bufferSamplers)
+            , fImageStorages(imageStorages)
             , fUsePLSDstRead(usePLSDstRead) {}
 
         GrGLSLXPFragmentBuilder* fXPFragBuilder;
@@ -57,6 +60,7 @@
         const char* fOutputSecondary;
         const SamplerHandle* fTexSamplers;
         const SamplerHandle* fBufferSamplers;
+        const ImageStorageHandle* fImageStorages;
         bool fUsePLSDstRead;
     };
     /**
diff --git a/src/gpu/vk/GrVkCaps.h b/src/gpu/vk/GrVkCaps.h
index abc0dc9..7b40e0d 100644
--- a/src/gpu/vk/GrVkCaps.h
+++ b/src/gpu/vk/GrVkCaps.h
@@ -37,6 +37,8 @@
         return SkToBool(ConfigInfo::kRenderable_Flag & fConfigTable[config].fOptimalFlags);
     }
 
+    bool canConfigBeImageStorage(GrPixelConfig) const override { return false; }
+
     bool isConfigTexturableLinearly(GrPixelConfig config) const {
         return SkToBool(ConfigInfo::kTextureable_Flag & fConfigTable[config].fLinearFlags);
     }
diff --git a/src/gpu/vk/GrVkPipelineState.cpp b/src/gpu/vk/GrVkPipelineState.cpp
index 2d53cd8..75b8baa 100644
--- a/src/gpu/vk/GrVkPipelineState.cpp
+++ b/src/gpu/vk/GrVkPipelineState.cpp
@@ -174,6 +174,8 @@
 
 static void append_texture_bindings(const GrProcessor& processor,
                                     SkTArray<const GrProcessor::TextureSampler*>* textureBindings) {
+    // We don't support image storages in VK.
+    SkASSERT(!processor.numImageStorages());
     if (int numTextureSamplers = processor.numTextureSamplers()) {
         const GrProcessor::TextureSampler** bindings =
                 textureBindings->push_back_n(numTextureSamplers);
diff --git a/src/gpu/vk/GrVkUniformHandler.cpp b/src/gpu/vk/GrVkUniformHandler.cpp
index 3998c0f..8c4f851 100644
--- a/src/gpu/vk/GrVkUniformHandler.cpp
+++ b/src/gpu/vk/GrVkUniformHandler.cpp
@@ -45,6 +45,8 @@
         case kBufferSampler_GrSLType:
         case kTexture2D_GrSLType:
         case kSampler_GrSLType:
+        case kImageStorage2D_GrSLType:
+        case kIImageStorage2D_GrSLType:
             break;
     }
     SkFAIL("Unexpected type");
@@ -86,6 +88,8 @@
         case kBufferSampler_GrSLType:
         case kTexture2D_GrSLType:
         case kSampler_GrSLType:
+        case kImageStorage2D_GrSLType:
+        case kIImageStorage2D_GrSLType:
             break;
     }
     SkFAIL("Unexpected type");
@@ -158,7 +162,7 @@
     uni.fVariable.setTypeModifier(GrShaderVar::kNone_TypeModifier);
 
     uint32_t* currentOffset = kVertex_GrShaderFlag == visibility ? &fCurrentVertexUBOOffset
-                                                                   : &fCurrentFragmentUBOOffset;
+                                                                 : &fCurrentFragmentUBOOffset;
     get_ubo_aligned_offset(&uni.fUBOffset, currentOffset, type, arrayCount);
 
     if (outName) {
diff --git a/src/gpu/vk/GrVkUniformHandler.h b/src/gpu/vk/GrVkUniformHandler.h
index e5d4a4c..808eed7 100644
--- a/src/gpu/vk/GrVkUniformHandler.h
+++ b/src/gpu/vk/GrVkUniformHandler.h
@@ -76,6 +76,19 @@
         return fSamplers[handle.toIndex()].fVisibility;
     }
 
+    ImageStorageHandle addImageStorage(uint32_t visibility, GrSLType,  GrImageStorageFormat,
+                                       GrSLMemoryModel, GrSLRestrict, GrIOType,
+                                       const char* name) override {
+        SkFAIL("Image storages not implemented for Vulkan.");
+        return 0;
+    }
+
+    const GrShaderVar& imageStorageVariable(ImageStorageHandle handle) const override {
+        SkFAIL("Image storages not implemented for Vulkan.");
+        static const GrShaderVar* gVar = nullptr;
+        return *gVar;
+    }
+
     void appendUniformDecls(GrShaderFlags, SkString*) const override;
 
     bool hasVertexUniforms() const { return fCurrentVertexUBOOffset > 0; }
diff --git a/src/gpu/vk/GrVkVaryingHandler.cpp b/src/gpu/vk/GrVkVaryingHandler.cpp
index 9bab292..d7d08c9 100644
--- a/src/gpu/vk/GrVkVaryingHandler.cpp
+++ b/src/gpu/vk/GrVkVaryingHandler.cpp
@@ -47,6 +47,10 @@
              return 0;
         case kSampler_GrSLType:
              return 0;
+        case kImageStorage2D_GrSLType:
+            return 0;
+        case kIImageStorage2D_GrSLType:
+            return 0;
     }
     SkFAIL("Unexpected type");
     return -1;
diff --git a/src/sksl/SkSLGLSLCodeGenerator.cpp b/src/sksl/SkSLGLSLCodeGenerator.cpp
index 7c26287..5b74724 100644
--- a/src/sksl/SkSLGLSLCodeGenerator.cpp
+++ b/src/sksl/SkSLGLSLCodeGenerator.cpp
@@ -455,11 +455,26 @@
 
 void GLSLCodeGenerator::writeModifiers(const Modifiers& modifiers,
                                        bool globalContext) {
+    if (modifiers.fFlags & Modifiers::kFlat_Flag) {
+        this->write("flat ");
+    }
     if (modifiers.fFlags & Modifiers::kNoPerspective_Flag) {
         this->write("noperspective ");
     }
-    if (modifiers.fFlags & Modifiers::kFlat_Flag) {
-        this->write("flat ");
+    if (modifiers.fFlags & Modifiers::kReadOnly_Flag) {
+        this->write("readonly ");
+    }
+    if (modifiers.fFlags & Modifiers::kWriteOnly_Flag) {
+        this->write("writeonly ");
+    }
+    if (modifiers.fFlags & Modifiers::kCoherent_Flag) {
+        this->write("coherent ");
+    }
+    if (modifiers.fFlags & Modifiers::kVolatile_Flag) {
+        this->write("volatile ");
+    }
+    if (modifiers.fFlags & Modifiers::kRestrict_Flag) {
+        this->write("restrict ");
     }
     SkString layout = modifiers.fLayout.description();
     if (layout.size()) {
diff --git a/src/sksl/SkSLParser.cpp b/src/sksl/SkSLParser.cpp
index 3920b00..cd0ae6a 100644
--- a/src/sksl/SkSLParser.cpp
+++ b/src/sksl/SkSLParser.cpp
@@ -601,7 +601,8 @@
                   pushConstant);
 }
 
-/* layout? (UNIFORM | CONST | IN | OUT | INOUT | LOWP | MEDIUMP | HIGHP | FLAT | NOPERSPECTIVE)* */
+/* layout? (UNIFORM | CONST | IN | OUT | INOUT | LOWP | MEDIUMP | HIGHP | FLAT | NOPERSPECTIVE |
+            READONLY | WRITEONLY | COHERENT | VOLATILE | RESTRICT)* */
 Modifiers Parser::modifiers() {
     Layout layout = this->layout();
     int flags = 0;
@@ -649,6 +650,26 @@
                 this->nextToken();
                 flags |= Modifiers::kNoPerspective_Flag;
                 break;
+            case Token::READONLY:
+                this->nextToken();
+                flags |= Modifiers::kReadOnly_Flag;
+                break;
+            case Token::WRITEONLY:
+                this->nextToken();
+                flags |= Modifiers::kWriteOnly_Flag;
+                break;
+            case Token::COHERENT:
+                this->nextToken();
+                flags |= Modifiers::kCoherent_Flag;
+                break;
+            case Token::VOLATILE:
+                this->nextToken();
+                flags |= Modifiers::kVolatile_Flag;
+                break;
+            case Token::RESTRICT:
+                this->nextToken();
+                flags |= Modifiers::kRestrict_Flag;
+                break;
             default:
                 return Modifiers(layout, flags);
         }
diff --git a/src/sksl/SkSLSPIRVCodeGenerator.cpp b/src/sksl/SkSLSPIRVCodeGenerator.cpp
index 62c0c50..b2857f4 100644
--- a/src/sksl/SkSLSPIRVCodeGenerator.cpp
+++ b/src/sksl/SkSLSPIRVCodeGenerator.cpp
@@ -2460,6 +2460,13 @@
     for (size_t i = 0; i < decl.fVars.size(); i++) {
         const VarDeclaration& varDecl = decl.fVars[i];
         const Variable* var = varDecl.fVar;
+        // These haven't been implemented in our SPIR-V generator yet and we only currently use them
+        // in the OpenGL backend.
+        ASSERT(!(var->fModifiers.fFlags & (Modifiers::kReadOnly_Flag |
+                                           Modifiers::kWriteOnly_Flag |
+                                           Modifiers::kCoherent_Flag |
+                                           Modifiers::kVolatile_Flag |
+                                           Modifiers::kRestrict_Flag)));
         if (var->fModifiers.fLayout.fBuiltin == BUILTIN_IGNORE) {
             continue;
         }
@@ -2514,6 +2521,13 @@
 void SPIRVCodeGenerator::writeVarDeclarations(const VarDeclarations& decl, SkWStream& out) {
     for (const auto& varDecl : decl.fVars) {
         const Variable* var = varDecl.fVar;
+        // These haven't been implemented in our SPIR-V generator yet and we only currently use them
+        // in the OpenGL backend.
+        ASSERT(!(var->fModifiers.fFlags & (Modifiers::kReadOnly_Flag |
+                                           Modifiers::kWriteOnly_Flag |
+                                           Modifiers::kCoherent_Flag |
+                                           Modifiers::kVolatile_Flag |
+                                           Modifiers::kRestrict_Flag)));
         SpvId id = this->nextId();
         fVariableMap[var] = id;
         SpvId type = this->getPointerType(var->fType, SpvStorageClassFunction);
diff --git a/src/sksl/SkSLToken.h b/src/sksl/SkSLToken.h
index 6fe130f..5c8c2bd 100644
--- a/src/sksl/SkSLToken.h
+++ b/src/sksl/SkSLToken.h
@@ -96,6 +96,11 @@
         UNIFORM,
         FLAT,
         NOPERSPECTIVE,
+        READONLY,
+        WRITEONLY,
+        COHERENT,
+        VOLATILE,
+        RESTRICT,
         STRUCT,
         LAYOUT,
         DIRECTIVE,
diff --git a/src/sksl/ir/SkSLModifiers.h b/src/sksl/ir/SkSLModifiers.h
index dbb9b6b..c7a5639 100644
--- a/src/sksl/ir/SkSLModifiers.h
+++ b/src/sksl/ir/SkSLModifiers.h
@@ -17,16 +17,21 @@
  */
 struct Modifiers {
     enum Flag {
-        kNo_Flag            =   0,
-        kConst_Flag         =   1,
-        kIn_Flag            =   2,
-        kOut_Flag           =   4,
-        kLowp_Flag          =   8,
-        kMediump_Flag       =  16,
-        kHighp_Flag         =  32,
-        kUniform_Flag       =  64,
-        kFlat_Flag          = 128,
-        kNoPerspective_Flag = 256
+        kNo_Flag            =    0,
+        kConst_Flag         =    1,
+        kIn_Flag            =    2,
+        kOut_Flag           =    4,
+        kLowp_Flag          =    8,
+        kMediump_Flag       =   16,
+        kHighp_Flag         =   32,
+        kUniform_Flag       =   64,
+        kFlat_Flag          =  128,
+        kNoPerspective_Flag =  256,
+        kReadOnly_Flag      =  512,
+        kWriteOnly_Flag     = 1024,
+        kCoherent_Flag      = 2048,
+        kVolatile_Flag      = 4096,
+        kRestrict_Flag      = 8192
     };
 
     Modifiers()
@@ -60,6 +65,21 @@
         if (fFlags & kNoPerspective_Flag) {
             result += "noperspective ";
         }
+        if (fFlags & kReadOnly_Flag) {
+            result += "readonly ";
+        }
+        if (fFlags & kWriteOnly_Flag) {
+            result += "writeonly ";
+        }
+        if (fFlags & kCoherent_Flag) {
+            result += "coherent ";
+        }
+        if (fFlags & kVolatile_Flag) {
+            result += "volatile ";
+        }
+        if (fFlags & kRestrict_Flag) {
+            result += "restrict ";
+        }
 
         if ((fFlags & kIn_Flag) && (fFlags & kOut_Flag)) {
             result += "inout ";
diff --git a/src/sksl/lex.sksl.c b/src/sksl/lex.sksl.c
index 710b67b..4cff376 100644
--- a/src/sksl/lex.sksl.c
+++ b/src/sksl/lex.sksl.c
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #line 3 "lex.sksl.c"
 
 #define  YY_INT_ALIGNED short int
@@ -13,8 +13,8 @@
 
 #define FLEX_SCANNER
 #define YY_FLEX_MAJOR_VERSION 2
-#define YY_FLEX_MINOR_VERSION 5
-#define YY_FLEX_SUBMINOR_VERSION 35
+#define YY_FLEX_MINOR_VERSION 6
+#define YY_FLEX_SUBMINOR_VERSION 0
 #if YY_FLEX_SUBMINOR_VERSION > 0
 #define FLEX_BETA
 #endif
@@ -52,7 +52,6 @@
 typedef uint16_t flex_uint16_t;
 typedef int32_t flex_int32_t;
 typedef uint32_t flex_uint32_t;
-typedef uint64_t flex_uint64_t;
 #else
 typedef signed char flex_int8_t;
 typedef short int flex_int16_t;
@@ -60,7 +59,6 @@
 typedef unsigned char flex_uint8_t; 
 typedef unsigned short int flex_uint16_t;
 typedef unsigned int flex_uint32_t;
-#endif /* ! C99 */
 
 /* Limits of integral types. */
 #ifndef INT8_MIN
@@ -91,6 +89,8 @@
 #define UINT32_MAX             (4294967295U)
 #endif
 
+#endif /* ! C99 */
+
 #endif /* ! FLEXINT_H */
 
 #ifdef __cplusplus
@@ -164,7 +164,15 @@
 
 /* Size of default input buffer. */
 #ifndef YY_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k.
+ * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
+ * Ditto for the __ia64__ case accordingly.
+ */
+#define YY_BUF_SIZE 32768
+#else
 #define YY_BUF_SIZE 16384
+#endif /* __ia64__ */
 #endif
 
 /* The state buf must be large enough to hold one state per character in the main buffer.
@@ -194,11 +202,18 @@
      */
     #define  YY_LESS_LINENO(n) \
             do { \
-                yy_size_t yyl;\
+                int yyl;\
                 for ( yyl = n; yyl < yyleng; ++yyl )\
                     if ( yytext[yyl] == '\n' )\
                         --yylineno;\
             }while(0)
+    #define YY_LINENO_REWIND_TO(dst) \
+            do {\
+                const char *p;\
+                for ( p = yy_cp-1; p >= (dst); --p)\
+                    if ( *p == '\n' )\
+                        --yylineno;\
+            }while(0)
     
 /* Return all but the first "n" matched characters back to the input stream. */
 #define yyless(n) \
@@ -233,7 +248,7 @@
 	/* Number of characters read into yy_ch_buf, not including EOB
 	 * characters.
 	 */
-	yy_size_t yy_n_chars;
+	int yy_n_chars;
 
 	/* Whether we "own" the buffer - i.e., we know we created it,
 	 * and can realloc() it to grow it, and should free() it to
@@ -353,6 +368,9 @@
 static yy_state_type yy_get_previous_state (yyscan_t yyscanner );
 static yy_state_type yy_try_NUL_trans (yy_state_type current_state  ,yyscan_t yyscanner);
 static int yy_get_next_buffer (yyscan_t yyscanner );
+#if defined(__GNUC__) && __GNUC__ >= 3
+__attribute__((__noreturn__))
+#endif
 static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner );
 
 /* Done after the current pattern has been matched and before the
@@ -360,13 +378,13 @@
  */
 #define YY_DO_BEFORE_ACTION \
 	yyg->yytext_ptr = yy_bp; \
-	yyleng = (yy_size_t) (yy_cp - yy_bp); \
+	yyleng = (size_t) (yy_cp - yy_bp); \
 	yyg->yy_hold_char = *yy_cp; \
 	*yy_cp = '\0'; \
 	yyg->yy_c_buf_p = yy_cp;
 
-#define YY_NUM_RULES 84
-#define YY_END_OF_BUFFER 85
+#define YY_NUM_RULES 89
+#define YY_END_OF_BUFFER 90
 /* This struct is not used in this scanner,
    but its presence is necessary. */
 struct yy_trans_info
@@ -374,34 +392,37 @@
 	flex_int32_t yy_verify;
 	flex_int32_t yy_nxt;
 	};
-static yyconst flex_int16_t yy_accept[205] =
+static yyconst flex_int16_t yy_accept[239] =
     {   0,
-        0,    0,   85,   83,   82,   82,   56,   83,   30,   46,
-       51,   32,   33,   44,   42,   39,   43,   38,   45,    4,
-        4,   58,   79,   63,   59,   62,   57,   36,   37,   50,
-       30,   30,   30,   30,   30,   30,   30,   30,   30,   30,
-       30,   30,   30,   30,   30,   30,   30,   34,   49,   35,
-       52,   82,   61,   31,   30,   70,   55,   75,   68,   40,
-       66,   41,   67,    1,    0,   80,   69,    2,    4,    0,
-        0,   47,   65,   60,   64,   48,   74,   54,   30,   30,
-       30,   12,   30,   30,   30,   30,   30,    8,   17,   30,
-       30,   30,   30,   30,   30,   30,   30,   30,   30,   30,
+        0,    0,   90,   88,   87,   87,   61,   88,   35,   51,
+       56,   37,   38,   49,   47,   44,   48,   43,   50,    4,
+        4,   63,   84,   68,   64,   67,   62,   41,   42,   55,
+       35,   35,   35,   35,   35,   35,   35,   35,   35,   35,
+       35,   35,   35,   35,   35,   35,   35,   35,   39,   54,
+       40,   57,   87,   66,   36,   35,   75,   60,   80,   73,
+       45,   71,   46,   72,    1,    0,   85,   74,    2,    4,
+        0,    0,   52,   70,   65,   69,   53,   79,   59,   35,
+       35,   35,   12,   35,   35,   35,   35,   35,    8,   17,
+       35,   35,   35,   35,   35,   35,   35,   35,   35,   35,
 
-       73,   53,   31,   78,    0,    0,    0,   80,    1,    0,
-        0,    3,    5,   71,   72,   77,   30,   30,   30,   30,
-       30,   30,   10,   30,   30,   30,   30,   30,   30,   18,
-       30,   30,   30,   30,   30,   30,   76,    0,    1,   81,
-        0,    0,    2,   30,   30,   30,   30,    9,   30,   25,
-       30,   30,   30,   22,   30,   30,   30,   30,   30,    6,
-       30,   30,    0,    1,   13,   21,   30,   30,    7,   24,
-       19,   30,   30,   30,   30,   30,   30,   30,   11,   30,
-       30,   28,   30,   30,   30,   16,   27,   30,   30,   15,
-       23,   30,   30,   20,   14,   30,   30,   30,   29,   30,
+       35,   35,   35,   78,   58,   36,   83,    0,    0,    0,
+       85,    1,    0,    0,    3,    5,   76,   77,   82,   35,
+       35,   35,   35,   35,   35,   35,   10,   35,   35,   35,
+       35,   35,   35,   18,   35,   35,   35,   35,   35,   35,
+       35,   35,   35,   35,   81,    0,    1,   86,    0,    0,
+        2,   35,   35,   35,   35,   35,    9,   35,   25,   35,
+       35,   35,   22,   35,   35,   35,   35,   35,   35,   35,
+        6,   35,   35,   35,   35,    0,    1,   13,   35,   21,
+       35,   35,    7,   24,   19,   35,   35,   35,   35,   35,
+       35,   35,   35,   35,   35,   11,   35,   35,   35,   35,
 
-       30,   30,   26,    0
+       33,   35,   35,   35,   35,   35,   16,   32,   35,   35,
+       35,   35,   35,   15,   23,   35,   35,   35,   35,   20,
+       35,   35,   29,   14,   35,   35,   27,   31,   30,   35,
+       35,   34,   28,   35,   35,   35,   26,    0
     } ;
 
-static yyconst flex_int32_t yy_ec[256] =
+static yyconst YY_CHAR yy_ec[256] =
     {   0,
         1,    1,    1,    1,    1,    1,    1,    1,    2,    3,
         1,    1,    2,    1,    1,    1,    1,    1,    1,    1,
@@ -433,7 +454,7 @@
         1,    1,    1,    1,    1
     } ;
 
-static yyconst flex_int32_t yy_meta[57] =
+static yyconst YY_CHAR yy_meta[57] =
     {   0,
         1,    1,    2,    1,    1,    3,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    4,    4,    1,    1,
@@ -443,101 +464,113 @@
         3,    3,    1,    1,    1,    1
     } ;
 
-static yyconst flex_int16_t yy_base[211] =
+static yyconst flex_uint16_t yy_base[245] =
     {   0,
-        0,    0,  273,  274,   55,   57,  250,    0,    0,  249,
-       53,  274,  274,  248,   50,  274,   49,   47,   57,   52,
-       59,  274,  274,   59,  247,   60,  274,  274,  274,   62,
-      223,  224,   49,  226,   58,  227,   54,   64,  230,  220,
-      214,  216,  226,  212,  213,  215,  219,  274,   68,  274,
-      274,   97,  274,    0,    0,  274,  233,  274,  274,  274,
-      274,  274,  274,   87,  243,    0,  274,   91,   97,  112,
-        0,  231,  274,  274,  274,  230,  274,  229,  216,  207,
-      202,    0,  201,  206,  215,  199,  207,    0,  199,  189,
-      190,  206,  194,  190,  202,  188,  189,  185,  194,  193,
+        0,    0,  306,  307,   55,   57,  283,    0,    0,  282,
+       53,  307,  307,  281,   50,  307,   49,   47,   57,   52,
+       59,  307,  307,   59,  280,   60,  307,  307,  307,   62,
+      256,  257,   49,  259,   58,  260,   54,   64,  263,  253,
+      247,  249,  259,  245,  246,  248,  246,   60,  307,   68,
+      307,  307,   97,  307,    0,    0,  307,  266,  307,  307,
+      307,  307,  307,  307,   91,  276,    0,  307,   93,   97,
+      112,    0,  264,  307,  307,  307,  263,  307,  262,  249,
+       76,  236,    0,  235,  240,  249,  233,  241,    0,  233,
+      223,  224,  240,  228,  224,  236,   74,  224,  220,  229,
 
-      274,  208,    0,  274,  120,  218,  212,    0,   93,  127,
-      118,  125,    0,  274,  274,  274,  197,  100,  194,  191,
-      178,  176,    0,  185,  173,  177,  175,  180,  183,    0,
-      184,  167,  166,  179,  177,  171,  274,  131,  133,  274,
-      140,  138,  142,  171,  162,  170,  177,    0,  172,    0,
-      161,  157,  155,    0,  154,  156,  162,  154,  166,    0,
-      154,  162,  144,  146,    0,    0,  153,  149,    0,    0,
-        0,  146,  151,  145,  144,  147,  141,  142,    0,  138,
-      147,    0,  122,  109,  102,    0,    0,   92,   94,    0,
-        0,   86,   75,    0,    0,   84,   64,   55,    0,   59,
+      226,  227,  226,  307,  241,    0,  307,  120,  251,  245,
+        0,  118,  128,  130,  132,    0,  307,  307,  307,  230,
+      225,  107,  226,  223,  210,  208,    0,  217,  205,  209,
+      207,  212,  215,    0,  216,  214,  199,  197,  196,  209,
+      207,  211,  200,  192,  307,  138,  140,  307,  147,  145,
+      149,  199,  192,  189,  197,  204,    0,  199,    0,  188,
+      184,  182,    0,  181,  183,  189,  183,  180,  179,  191,
+        0,  179,  174,  186,  185,  151,  153,    0,  184,    0,
+      175,  171,    0,    0,    0,  168,  173,  167,  166,  169,
+      172,  167,  161,  162,  168,    0,  162,  162,  155,  169,
 
-       46,   32,    0,  274,  162,  165,  168,  173,  178,  180
+        0,  157,  156,  161,  158,  165,    0,    0,  155,  155,
+      152,  146,  153,    0,    0,  139,  117,   99,   96,    0,
+      107,   99,    0,    0,  101,   86,    0,    0,    0,   64,
+       59,    0,    0,   64,   46,   32,    0,  307,  169,  172,
+      175,  180,  185,  187
     } ;
 
-static yyconst flex_int16_t yy_def[211] =
+static yyconst flex_int16_t yy_def[245] =
     {   0,
-      204,    1,  204,  204,  204,  204,  204,  205,  206,  204,
-      204,  204,  204,  204,  204,  204,  204,  204,  204,  204,
-      204,  204,  204,  204,  204,  204,  204,  204,  204,  204,
-      206,  206,  206,  206,  206,  206,  206,  206,  206,  206,
-      206,  206,  206,  206,  206,  206,  206,  204,  204,  204,
-      204,  204,  204,  207,  206,  204,  204,  204,  204,  204,
-      204,  204,  204,  204,  208,  209,  204,  204,  204,  204,
-      210,  204,  204,  204,  204,  204,  204,  204,  206,  206,
-      206,  206,  206,  206,  206,  206,  206,  206,  206,  206,
-      206,  206,  206,  206,  206,  206,  206,  206,  206,  206,
+      238,    1,  238,  238,  238,  238,  238,  239,  240,  238,
+      238,  238,  238,  238,  238,  238,  238,  238,  238,  238,
+      238,  238,  238,  238,  238,  238,  238,  238,  238,  238,
+      240,  240,  240,  240,  240,  240,  240,  240,  240,  240,
+      240,  240,  240,  240,  240,  240,  240,  240,  238,  238,
+      238,  238,  238,  238,  241,  240,  238,  238,  238,  238,
+      238,  238,  238,  238,  238,  242,  243,  238,  238,  238,
+      238,  244,  238,  238,  238,  238,  238,  238,  238,  240,
+      240,  240,  240,  240,  240,  240,  240,  240,  240,  240,
+      240,  240,  240,  240,  240,  240,  240,  240,  240,  240,
 
-      204,  204,  207,  204,  204,  208,  208,  209,  204,  204,
-      204,  204,  210,  204,  204,  204,  206,  206,  206,  206,
-      206,  206,  206,  206,  206,  206,  206,  206,  206,  206,
-      206,  206,  206,  206,  206,  206,  204,  204,  204,  204,
-      204,  204,  204,  206,  206,  206,  206,  206,  206,  206,
-      206,  206,  206,  206,  206,  206,  206,  206,  206,  206,
-      206,  206,  204,  204,  206,  206,  206,  206,  206,  206,
-      206,  206,  206,  206,  206,  206,  206,  206,  206,  206,
-      206,  206,  206,  206,  206,  206,  206,  206,  206,  206,
-      206,  206,  206,  206,  206,  206,  206,  206,  206,  206,
+      240,  240,  240,  238,  238,  241,  238,  238,  242,  242,
+      243,  238,  238,  238,  238,  244,  238,  238,  238,  240,
+      240,  240,  240,  240,  240,  240,  240,  240,  240,  240,
+      240,  240,  240,  240,  240,  240,  240,  240,  240,  240,
+      240,  240,  240,  240,  238,  238,  238,  238,  238,  238,
+      238,  240,  240,  240,  240,  240,  240,  240,  240,  240,
+      240,  240,  240,  240,  240,  240,  240,  240,  240,  240,
+      240,  240,  240,  240,  240,  238,  238,  240,  240,  240,
+      240,  240,  240,  240,  240,  240,  240,  240,  240,  240,
+      240,  240,  240,  240,  240,  240,  240,  240,  240,  240,
 
-      206,  206,  206,    0,  204,  204,  204,  204,  204,  204
+      240,  240,  240,  240,  240,  240,  240,  240,  240,  240,
+      240,  240,  240,  240,  240,  240,  240,  240,  240,  240,
+      240,  240,  240,  240,  240,  240,  240,  240,  240,  240,
+      240,  240,  240,  240,  240,  240,  240,    0,  238,  238,
+      238,  238,  238,  238
     } ;
 
-static yyconst flex_int16_t yy_nxt[331] =
+static yyconst flex_uint16_t yy_nxt[364] =
     {   0,
         4,    5,    6,    7,    8,    9,   10,   11,   12,   13,
        14,   15,   16,   17,   18,   19,   20,   21,   22,   23,
        24,   25,   26,   27,    9,    9,   28,   29,   30,    9,
        31,   32,   33,   34,   35,    9,   36,   37,    9,   38,
-       39,   40,   41,   42,   43,   44,   45,   46,    9,   47,
-        9,    9,   48,   49,   50,   51,   52,   52,   52,   52,
-       57,   60,   62,   64,   64,  203,   68,   65,   69,   69,
-       63,   61,   66,   68,   58,   69,   69,   70,   67,   72,
-       73,   75,   76,   77,   70,   70,   81,   84,   88,  101,
-       78,   82,   70,   90,  202,   89,  201,   85,   52,   52,
+       39,   40,   41,   42,   43,   44,   45,   46,   47,   48,
+        9,    9,   49,   50,   51,   52,   53,   53,   53,   53,
+       58,   61,   63,   65,   65,  237,   69,   66,   70,   70,
+       64,   62,   67,   69,   59,   70,   70,   71,   68,   73,
+       74,   76,   77,   78,   71,   71,   82,   85,   89,  104,
+       79,   83,   71,   91,  236,   90,  102,   86,   53,   53,
 
-       86,  200,   71,   64,   64,  199,   91,  109,  109,  109,
-      109,   68,  105,   69,   69,  198,  110,  197,  141,  196,
-      105,  102,   70,  111,  110,  111,  141,  195,  112,  112,
-       70,  138,  194,  138,  112,  112,  139,  139,  142,  193,
-      142,  112,  112,  143,  143,  145,  146,  139,  139,  139,
-      139,  163,  192,  163,  143,  143,  164,  164,  143,  143,
-      164,  164,  164,  164,   54,  191,   54,   55,   55,   55,
-      103,  103,  103,  106,  106,  106,  106,  106,  108,  190,
-      108,  108,  108,  113,  113,  189,  188,  187,  186,  185,
-      184,  183,  182,  181,  180,  179,  178,  177,  176,  175,
+       87,  235,   72,  136,  103,  234,   92,   65,   65,  112,
+      112,   69,  121,   70,   70,  233,  108,  122,  113,  137,
+      138,  105,   71,  114,  108,  114,  113,  232,  115,  115,
+       71,  146,  231,  146,  112,  112,  147,  147,  230,  150,
+      229,  150,  228,  149,  151,  151,  115,  115,  115,  115,
+      227,  149,  154,  155,  147,  147,  147,  147,  176,  226,
+      176,  151,  151,  177,  177,  151,  151,  177,  177,  177,
+      177,   55,  225,   55,   56,   56,   56,  106,  106,  106,
+      109,  109,  109,  109,  109,  111,  224,  111,  111,  111,
+      116,  116,  223,  222,  221,  220,  219,  218,  217,  216,
 
-      174,  173,  172,  171,  170,  169,  168,  167,  166,  165,
-      162,  161,  160,  159,  158,  157,  156,  155,  154,  153,
-      152,  151,  150,  149,  148,  147,  144,  140,  107,  137,
-      136,  135,  134,  133,  132,  131,  130,  129,  128,  127,
-      126,  125,  124,  123,  122,  121,  120,  119,  118,  117,
-      116,  115,  114,  107,  104,  100,   99,   98,   97,   96,
-       95,   94,   93,   92,   87,   83,   80,   79,   74,   59,
-       56,   53,  204,    3,  204,  204,  204,  204,  204,  204,
-      204,  204,  204,  204,  204,  204,  204,  204,  204,  204,
-      204,  204,  204,  204,  204,  204,  204,  204,  204,  204,
+      215,  214,  213,  212,  211,  210,  209,  208,  207,  206,
+      205,  204,  203,  202,  201,  200,  199,  198,  197,  196,
+      195,  194,  193,  192,  191,  190,  189,  188,  187,  186,
+      185,  184,  183,  182,  181,  180,  179,  178,  175,  174,
+      173,  172,  171,  170,  169,  168,  167,  166,  165,  164,
+      163,  162,  161,  160,  159,  158,  157,  156,  153,  152,
+      148,  110,  145,  144,  143,  142,  141,  140,  139,  135,
+      134,  133,  132,  131,  130,  129,  128,  127,  126,  125,
+      124,  123,  120,  119,  118,  117,  110,  107,  101,  100,
+       99,   98,   97,   96,   95,   94,   93,   88,   84,   81,
 
-      204,  204,  204,  204,  204,  204,  204,  204,  204,  204,
-      204,  204,  204,  204,  204,  204,  204,  204,  204,  204,
-      204,  204,  204,  204,  204,  204,  204,  204,  204,  204
+       80,   75,   60,   57,   54,  238,    3,  238,  238,  238,
+      238,  238,  238,  238,  238,  238,  238,  238,  238,  238,
+      238,  238,  238,  238,  238,  238,  238,  238,  238,  238,
+      238,  238,  238,  238,  238,  238,  238,  238,  238,  238,
+      238,  238,  238,  238,  238,  238,  238,  238,  238,  238,
+      238,  238,  238,  238,  238,  238,  238,  238,  238,  238,
+      238,  238,  238
     } ;
 
-static yyconst flex_int16_t yy_chk[331] =
+static yyconst flex_int16_t yy_chk[364] =
     {   0,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
@@ -545,46 +578,50 @@
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    5,    5,    6,    6,
-       11,   15,   17,   18,   18,  202,   20,   19,   20,   20,
+       11,   15,   17,   18,   18,  236,   20,   19,   20,   20,
        17,   15,   19,   21,   11,   21,   21,   20,   19,   24,
-       24,   26,   26,   30,   21,   20,   33,   35,   37,   49,
-       30,   33,   21,   38,  201,   37,  200,   35,   52,   52,
+       24,   26,   26,   30,   21,   20,   33,   35,   37,   50,
+       30,   33,   21,   38,  235,   37,   48,   35,   53,   53,
 
-       35,  198,   20,   64,   64,  197,   38,   68,   68,  109,
-      109,   69,   64,   69,   69,  196,   68,  193,  109,  192,
-       64,   49,   69,   70,   68,   70,  109,  189,   70,   70,
-       69,  105,  188,  105,  111,  111,  105,  105,  110,  185,
-      110,  112,  112,  110,  110,  118,  118,  138,  138,  139,
-      139,  141,  184,  141,  142,  142,  141,  141,  143,  143,
-      163,  163,  164,  164,  205,  183,  205,  206,  206,  206,
-      207,  207,  207,  208,  208,  208,  208,  208,  209,  181,
-      209,  209,  209,  210,  210,  180,  178,  177,  176,  175,
-      174,  173,  172,  168,  167,  162,  161,  159,  158,  157,
+       35,  234,   20,   97,   48,  231,   38,   65,   65,   69,
+       69,   70,   81,   70,   70,  230,   65,   81,   69,   97,
+       97,   50,   70,   71,   65,   71,   69,  226,   71,   71,
+       70,  108,  225,  108,  112,  112,  108,  108,  222,  113,
+      221,  113,  219,  112,  113,  113,  114,  114,  115,  115,
+      218,  112,  122,  122,  146,  146,  147,  147,  149,  217,
+      149,  150,  150,  149,  149,  151,  151,  176,  176,  177,
+      177,  239,  216,  239,  240,  240,  240,  241,  241,  241,
+      242,  242,  242,  242,  242,  243,  213,  243,  243,  243,
+      244,  244,  212,  211,  210,  209,  206,  205,  204,  203,
 
-      156,  155,  153,  152,  151,  149,  147,  146,  145,  144,
-      136,  135,  134,  133,  132,  131,  129,  128,  127,  126,
-      125,  124,  122,  121,  120,  119,  117,  107,  106,  102,
-      100,   99,   98,   97,   96,   95,   94,   93,   92,   91,
-       90,   89,   87,   86,   85,   84,   83,   81,   80,   79,
-       78,   76,   72,   65,   57,   47,   46,   45,   44,   43,
-       42,   41,   40,   39,   36,   34,   32,   31,   25,   14,
-       10,    7,    3,  204,  204,  204,  204,  204,  204,  204,
-      204,  204,  204,  204,  204,  204,  204,  204,  204,  204,
-      204,  204,  204,  204,  204,  204,  204,  204,  204,  204,
+      202,  200,  199,  198,  197,  195,  194,  193,  192,  191,
+      190,  189,  188,  187,  186,  182,  181,  179,  175,  174,
+      173,  172,  170,  169,  168,  167,  166,  165,  164,  162,
+      161,  160,  158,  156,  155,  154,  153,  152,  144,  143,
+      142,  141,  140,  139,  138,  137,  136,  135,  133,  132,
+      131,  130,  129,  128,  126,  125,  124,  123,  121,  120,
+      110,  109,  105,  103,  102,  101,  100,   99,   98,   96,
+       95,   94,   93,   92,   91,   90,   88,   87,   86,   85,
+       84,   82,   80,   79,   77,   73,   66,   58,   47,   46,
+       45,   44,   43,   42,   41,   40,   39,   36,   34,   32,
 
-      204,  204,  204,  204,  204,  204,  204,  204,  204,  204,
-      204,  204,  204,  204,  204,  204,  204,  204,  204,  204,
-      204,  204,  204,  204,  204,  204,  204,  204,  204,  204
+       31,   25,   14,   10,    7,    3,  238,  238,  238,  238,
+      238,  238,  238,  238,  238,  238,  238,  238,  238,  238,
+      238,  238,  238,  238,  238,  238,  238,  238,  238,  238,
+      238,  238,  238,  238,  238,  238,  238,  238,  238,  238,
+      238,  238,  238,  238,  238,  238,  238,  238,  238,  238,
+      238,  238,  238,  238,  238,  238,  238,  238,  238,  238,
+      238,  238,  238
     } ;
 
 /* Table of booleans, true if rule could match eol. */
-static yyconst flex_int32_t yy_rule_can_match_eol[85] =
+static yyconst flex_int32_t yy_rule_can_match_eol[90] =
     {   0,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
-    0, 1, 1, 0, 0,     };
+    0, 0, 0, 0, 0, 0, 1, 1, 0, 0,     };
 
 /* The intent behind this definition is that it'll catch
  * any uses of REJECT which flex missed.
@@ -607,7 +644,7 @@
     
 */
 #define YY_NO_UNISTD_H 1
-#line 605 "lex.sksl.c"
+#line 642 "lex.sksl.c"
 
 #define INITIAL 0
 
@@ -636,7 +673,7 @@
     size_t yy_buffer_stack_max; /**< capacity of stack. */
     YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
     char yy_hold_char;
-    yy_size_t yy_n_chars;
+    int yy_n_chars;
     yy_size_t yyleng_r;
     char *yy_c_buf_p;
     int yy_init;
@@ -678,11 +715,11 @@
 
 FILE *skslget_in (yyscan_t yyscanner );
 
-void skslset_in  (FILE * in_str ,yyscan_t yyscanner );
+void skslset_in  (FILE * _in_str ,yyscan_t yyscanner );
 
 FILE *skslget_out (yyscan_t yyscanner );
 
-void skslset_out  (FILE * out_str ,yyscan_t yyscanner );
+void skslset_out  (FILE * _out_str ,yyscan_t yyscanner );
 
 yy_size_t skslget_leng (yyscan_t yyscanner );
 
@@ -690,7 +727,11 @@
 
 int skslget_lineno (yyscan_t yyscanner );
 
-void skslset_lineno (int line_number ,yyscan_t yyscanner );
+void skslset_lineno (int _line_number ,yyscan_t yyscanner );
+
+int skslget_column  (yyscan_t yyscanner );
+
+void skslset_column (int _column_no ,yyscan_t yyscanner );
 
 /* Macros after this point can all be overridden by user definitions in
  * section 1.
@@ -704,8 +745,12 @@
 #endif
 #endif
 
+#ifndef YY_NO_UNPUT
+    
     static void yyunput (int c,char *buf_ptr  ,yyscan_t yyscanner);
     
+#endif
+
 #ifndef yytext_ptr
 static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
 #endif
@@ -726,7 +771,12 @@
 
 /* Amount of stuff to slurp up with each read. */
 #ifndef YY_READ_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k */
+#define YY_READ_BUF_SIZE 16384
+#else
 #define YY_READ_BUF_SIZE 8192
+#endif /* __ia64__ */
 #endif
 
 /* Copy whatever the last rule matched to the standard output. */
@@ -734,7 +784,7 @@
 /* This used to be an fputs(), but since the string might contain NUL's,
  * we now use fwrite().
  */
-#define ECHO fwrite( yytext, yyleng, 1, yyout )
+#define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0)
 #endif
 
 /* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
@@ -745,7 +795,7 @@
 	if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
 		{ \
 		int c = '*'; \
-		yy_size_t n; \
+		size_t n; \
 		for ( n = 0; n < max_size && \
 			     (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
 			buf[n] = (char) c; \
@@ -813,7 +863,7 @@
 
 /* Code executed at the end of each rule. */
 #ifndef YY_BREAK
-#define YY_BREAK break;
+#define YY_BREAK /*LINTED*/break;
 #endif
 
 #define YY_RULE_SETUP \
@@ -823,16 +873,11 @@
  */
 YY_DECL
 {
-	register yy_state_type yy_current_state;
-	register char *yy_cp, *yy_bp;
-	register int yy_act;
+	yy_state_type yy_current_state;
+	char *yy_cp, *yy_bp;
+	int yy_act;
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
 
-#line 23 "sksl.flex"
-
-
-#line 829 "lex.sksl.c"
-
 	if ( !yyg->yy_init )
 		{
 		yyg->yy_init = 1;
@@ -859,7 +904,13 @@
 		sksl_load_buffer_state(yyscanner );
 		}
 
-	while ( 1 )		/* loops until end-of-file is reached */
+	{
+#line 23 "sksl.flex"
+
+
+#line 906 "lex.sksl.c"
+
+	while ( /*CONSTCOND*/1 )		/* loops until end-of-file is reached */
 		{
 		yy_cp = yyg->yy_c_buf_p;
 
@@ -875,7 +926,7 @@
 yy_match:
 		do
 			{
-			register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+			YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ;
 			if ( yy_accept[yy_current_state] )
 				{
 				yyg->yy_last_accepting_state = yy_current_state;
@@ -884,13 +935,13 @@
 			while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 				{
 				yy_current_state = (int) yy_def[yy_current_state];
-				if ( yy_current_state >= 205 )
+				if ( yy_current_state >= 239 )
 					yy_c = yy_meta[(unsigned int) yy_c];
 				}
 			yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
 			++yy_cp;
 			}
-		while ( yy_current_state != 204 );
+		while ( yy_current_state != 238 );
 		yy_cp = yyg->yy_last_accepting_cpos;
 		yy_current_state = yyg->yy_last_accepting_state;
 
@@ -1055,296 +1106,321 @@
 case 27:
 YY_RULE_SETUP
 #line 77 "sksl.flex"
-{ return SkSL::Token::STRUCT; }
+{ return SkSL::Token::READONLY; }
 	YY_BREAK
 case 28:
 YY_RULE_SETUP
 #line 79 "sksl.flex"
-{ return SkSL::Token::LAYOUT; }
+{ return SkSL::Token::WRITEONLY; }
 	YY_BREAK
 case 29:
 YY_RULE_SETUP
 #line 81 "sksl.flex"
-{ return SkSL::Token::PRECISION; }
+{ return SkSL::Token::COHERENT; }
 	YY_BREAK
 case 30:
 YY_RULE_SETUP
 #line 83 "sksl.flex"
-{ return SkSL::Token::IDENTIFIER; }
+{ return SkSL::Token::VOLATILE; }
 	YY_BREAK
 case 31:
 YY_RULE_SETUP
 #line 85 "sksl.flex"
-{ return SkSL::Token::DIRECTIVE; }
+{ return SkSL::Token::RESTRICT; }
 	YY_BREAK
 case 32:
 YY_RULE_SETUP
 #line 87 "sksl.flex"
-{ return SkSL::Token::LPAREN; }
+{ return SkSL::Token::STRUCT; }
 	YY_BREAK
 case 33:
 YY_RULE_SETUP
 #line 89 "sksl.flex"
-{ return SkSL::Token::RPAREN; }
+{ return SkSL::Token::LAYOUT; }
 	YY_BREAK
 case 34:
 YY_RULE_SETUP
 #line 91 "sksl.flex"
-{ return SkSL::Token::LBRACE; }
+{ return SkSL::Token::PRECISION; }
 	YY_BREAK
 case 35:
 YY_RULE_SETUP
 #line 93 "sksl.flex"
-{ return SkSL::Token::RBRACE; }
+{ return SkSL::Token::IDENTIFIER; }
 	YY_BREAK
 case 36:
 YY_RULE_SETUP
 #line 95 "sksl.flex"
-{ return SkSL::Token::LBRACKET; }
+{ return SkSL::Token::DIRECTIVE; }
 	YY_BREAK
 case 37:
 YY_RULE_SETUP
 #line 97 "sksl.flex"
-{ return SkSL::Token::RBRACKET; }
+{ return SkSL::Token::LPAREN; }
 	YY_BREAK
 case 38:
 YY_RULE_SETUP
 #line 99 "sksl.flex"
-{ return SkSL::Token::DOT; }
+{ return SkSL::Token::RPAREN; }
 	YY_BREAK
 case 39:
 YY_RULE_SETUP
 #line 101 "sksl.flex"
-{ return SkSL::Token::COMMA; }
+{ return SkSL::Token::LBRACE; }
 	YY_BREAK
 case 40:
 YY_RULE_SETUP
 #line 103 "sksl.flex"
-{ return SkSL::Token::PLUSPLUS; }
+{ return SkSL::Token::RBRACE; }
 	YY_BREAK
 case 41:
 YY_RULE_SETUP
 #line 105 "sksl.flex"
-{ return SkSL::Token::MINUSMINUS; }
+{ return SkSL::Token::LBRACKET; }
 	YY_BREAK
 case 42:
 YY_RULE_SETUP
 #line 107 "sksl.flex"
-{ return SkSL::Token::PLUS; }
+{ return SkSL::Token::RBRACKET; }
 	YY_BREAK
 case 43:
 YY_RULE_SETUP
 #line 109 "sksl.flex"
-{ return SkSL::Token::MINUS; }
+{ return SkSL::Token::DOT; }
 	YY_BREAK
 case 44:
 YY_RULE_SETUP
 #line 111 "sksl.flex"
-{ return SkSL::Token::STAR; }
+{ return SkSL::Token::COMMA; }
 	YY_BREAK
 case 45:
 YY_RULE_SETUP
 #line 113 "sksl.flex"
-{ return SkSL::Token::SLASH; }
+{ return SkSL::Token::PLUSPLUS; }
 	YY_BREAK
 case 46:
 YY_RULE_SETUP
 #line 115 "sksl.flex"
-{ return SkSL::Token::PERCENT; }
+{ return SkSL::Token::MINUSMINUS; }
 	YY_BREAK
 case 47:
 YY_RULE_SETUP
 #line 117 "sksl.flex"
-{ return SkSL::Token::SHL; }
+{ return SkSL::Token::PLUS; }
 	YY_BREAK
 case 48:
 YY_RULE_SETUP
 #line 119 "sksl.flex"
-{ return SkSL::Token::SHR; }
+{ return SkSL::Token::MINUS; }
 	YY_BREAK
 case 49:
 YY_RULE_SETUP
 #line 121 "sksl.flex"
-{ return SkSL::Token::BITWISEOR; }
+{ return SkSL::Token::STAR; }
 	YY_BREAK
 case 50:
 YY_RULE_SETUP
 #line 123 "sksl.flex"
-{ return SkSL::Token::BITWISEXOR; }
+{ return SkSL::Token::SLASH; }
 	YY_BREAK
 case 51:
 YY_RULE_SETUP
 #line 125 "sksl.flex"
-{ return SkSL::Token::BITWISEAND; }
+{ return SkSL::Token::PERCENT; }
 	YY_BREAK
 case 52:
 YY_RULE_SETUP
 #line 127 "sksl.flex"
-{ return SkSL::Token::BITWISENOT; }
+{ return SkSL::Token::SHL; }
 	YY_BREAK
 case 53:
 YY_RULE_SETUP
 #line 129 "sksl.flex"
-{ return SkSL::Token::LOGICALOR; }
+{ return SkSL::Token::SHR; }
 	YY_BREAK
 case 54:
 YY_RULE_SETUP
 #line 131 "sksl.flex"
-{ return SkSL::Token::LOGICALXOR; }
+{ return SkSL::Token::BITWISEOR; }
 	YY_BREAK
 case 55:
 YY_RULE_SETUP
 #line 133 "sksl.flex"
-{ return SkSL::Token::LOGICALAND; }
+{ return SkSL::Token::BITWISEXOR; }
 	YY_BREAK
 case 56:
 YY_RULE_SETUP
 #line 135 "sksl.flex"
-{ return SkSL::Token::LOGICALNOT; }
+{ return SkSL::Token::BITWISEAND; }
 	YY_BREAK
 case 57:
 YY_RULE_SETUP
 #line 137 "sksl.flex"
-{ return SkSL::Token::QUESTION; }
+{ return SkSL::Token::BITWISENOT; }
 	YY_BREAK
 case 58:
 YY_RULE_SETUP
 #line 139 "sksl.flex"
-{ return SkSL::Token::COLON; }
+{ return SkSL::Token::LOGICALOR; }
 	YY_BREAK
 case 59:
 YY_RULE_SETUP
 #line 141 "sksl.flex"
-{ return SkSL::Token::EQ; }
+{ return SkSL::Token::LOGICALXOR; }
 	YY_BREAK
 case 60:
 YY_RULE_SETUP
 #line 143 "sksl.flex"
-{ return SkSL::Token::EQEQ; }
+{ return SkSL::Token::LOGICALAND; }
 	YY_BREAK
 case 61:
 YY_RULE_SETUP
 #line 145 "sksl.flex"
-{ return SkSL::Token::NEQ; }
+{ return SkSL::Token::LOGICALNOT; }
 	YY_BREAK
 case 62:
 YY_RULE_SETUP
 #line 147 "sksl.flex"
-{ return SkSL::Token::GT; }
+{ return SkSL::Token::QUESTION; }
 	YY_BREAK
 case 63:
 YY_RULE_SETUP
 #line 149 "sksl.flex"
-{ return SkSL::Token::LT; }
+{ return SkSL::Token::COLON; }
 	YY_BREAK
 case 64:
 YY_RULE_SETUP
 #line 151 "sksl.flex"
-{ return SkSL::Token::GTEQ; }
+{ return SkSL::Token::EQ; }
 	YY_BREAK
 case 65:
 YY_RULE_SETUP
 #line 153 "sksl.flex"
-{ return SkSL::Token::LTEQ; }
+{ return SkSL::Token::EQEQ; }
 	YY_BREAK
 case 66:
 YY_RULE_SETUP
 #line 155 "sksl.flex"
-{ return SkSL::Token::PLUSEQ; }
+{ return SkSL::Token::NEQ; }
 	YY_BREAK
 case 67:
 YY_RULE_SETUP
 #line 157 "sksl.flex"
-{ return SkSL::Token::MINUSEQ; }
+{ return SkSL::Token::GT; }
 	YY_BREAK
 case 68:
 YY_RULE_SETUP
 #line 159 "sksl.flex"
-{ return SkSL::Token::STAREQ; }
+{ return SkSL::Token::LT; }
 	YY_BREAK
 case 69:
 YY_RULE_SETUP
 #line 161 "sksl.flex"
-{ return SkSL::Token::SLASHEQ; }
+{ return SkSL::Token::GTEQ; }
 	YY_BREAK
 case 70:
 YY_RULE_SETUP
 #line 163 "sksl.flex"
-{ return SkSL::Token::PERCENTEQ; }
+{ return SkSL::Token::LTEQ; }
 	YY_BREAK
 case 71:
 YY_RULE_SETUP
 #line 165 "sksl.flex"
-{ return SkSL::Token::SHLEQ; }
+{ return SkSL::Token::PLUSEQ; }
 	YY_BREAK
 case 72:
 YY_RULE_SETUP
 #line 167 "sksl.flex"
-{ return SkSL::Token::SHREQ; }
+{ return SkSL::Token::MINUSEQ; }
 	YY_BREAK
 case 73:
 YY_RULE_SETUP
 #line 169 "sksl.flex"
-{ return SkSL::Token::BITWISEOREQ; }
+{ return SkSL::Token::STAREQ; }
 	YY_BREAK
 case 74:
 YY_RULE_SETUP
 #line 171 "sksl.flex"
-{ return SkSL::Token::BITWISEXOREQ; }
+{ return SkSL::Token::SLASHEQ; }
 	YY_BREAK
 case 75:
 YY_RULE_SETUP
 #line 173 "sksl.flex"
-{ return SkSL::Token::BITWISEANDEQ; }
+{ return SkSL::Token::PERCENTEQ; }
 	YY_BREAK
 case 76:
 YY_RULE_SETUP
 #line 175 "sksl.flex"
-{ return SkSL::Token::LOGICALOREQ; }
+{ return SkSL::Token::SHLEQ; }
 	YY_BREAK
 case 77:
 YY_RULE_SETUP
 #line 177 "sksl.flex"
-{ return SkSL::Token::LOGICALXOREQ; }
+{ return SkSL::Token::SHREQ; }
 	YY_BREAK
 case 78:
 YY_RULE_SETUP
 #line 179 "sksl.flex"
-{ return SkSL::Token::LOGICALANDEQ; }
+{ return SkSL::Token::BITWISEOREQ; }
 	YY_BREAK
 case 79:
 YY_RULE_SETUP
 #line 181 "sksl.flex"
-{ return SkSL::Token::SEMICOLON; }
+{ return SkSL::Token::BITWISEXOREQ; }
 	YY_BREAK
 case 80:
 YY_RULE_SETUP
 #line 183 "sksl.flex"
-/* line comment */
+{ return SkSL::Token::BITWISEANDEQ; }
 	YY_BREAK
 case 81:
-/* rule 81 can match eol */
 YY_RULE_SETUP
 #line 185 "sksl.flex"
-/* block comment */
+{ return SkSL::Token::LOGICALOREQ; }
 	YY_BREAK
 case 82:
-/* rule 82 can match eol */
 YY_RULE_SETUP
 #line 187 "sksl.flex"
-/* whitespace */
+{ return SkSL::Token::LOGICALXOREQ; }
 	YY_BREAK
 case 83:
 YY_RULE_SETUP
 #line 189 "sksl.flex"
-{ return SkSL::Token::INVALID_TOKEN; }
+{ return SkSL::Token::LOGICALANDEQ; }
 	YY_BREAK
 case 84:
 YY_RULE_SETUP
 #line 191 "sksl.flex"
+{ return SkSL::Token::SEMICOLON; }
+	YY_BREAK
+case 85:
+YY_RULE_SETUP
+#line 193 "sksl.flex"
+/* line comment */
+	YY_BREAK
+case 86:
+/* rule 86 can match eol */
+YY_RULE_SETUP
+#line 195 "sksl.flex"
+/* block comment */
+	YY_BREAK
+case 87:
+/* rule 87 can match eol */
+YY_RULE_SETUP
+#line 197 "sksl.flex"
+/* whitespace */
+	YY_BREAK
+case 88:
+YY_RULE_SETUP
+#line 199 "sksl.flex"
+{ return SkSL::Token::INVALID_TOKEN; }
+	YY_BREAK
+case 89:
+YY_RULE_SETUP
+#line 201 "sksl.flex"
 ECHO;
 	YY_BREAK
-#line 1342 "lex.sksl.c"
+#line 1418 "lex.sksl.c"
 case YY_STATE_EOF(INITIAL):
 	yyterminate();
 
@@ -1476,6 +1552,7 @@
 			"fatal flex scanner internal error--no action found" );
 	} /* end of action switch */
 		} /* end of scanning one token */
+	} /* end of user's declarations */
 } /* end of sksllex */
 
 /* yy_get_next_buffer - try to read in a new buffer
@@ -1488,9 +1565,9 @@
 static int yy_get_next_buffer (yyscan_t yyscanner)
 {
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-	register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
-	register char *source = yyg->yytext_ptr;
-	register int number_to_move, i;
+	char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+	char *source = yyg->yytext_ptr;
+	yy_size_t number_to_move, i;
 	int ret_val;
 
 	if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] )
@@ -1519,7 +1596,7 @@
 	/* Try to read more data. */
 
 	/* First move last chars to start of buffer. */
-	number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1;
+	number_to_move = (yy_size_t) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1;
 
 	for ( i = 0; i < number_to_move; ++i )
 		*(dest++) = *(source++);
@@ -1539,7 +1616,7 @@
 			{ /* Not enough room in the buffer - grow it. */
 
 			/* just a shorter name for the current buffer */
-			YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+			YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE;
 
 			int yy_c_buf_p_offset =
 				(int) (yyg->yy_c_buf_p - b->yy_ch_buf);
@@ -1601,9 +1678,9 @@
 	else
 		ret_val = EOB_ACT_CONTINUE_SCAN;
 
-	if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+	if ((int) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
 		/* Extend the array by 50%, plus the number we really need. */
-		yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
+		int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
 		YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) skslrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner );
 		if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
 			YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
@@ -1622,15 +1699,15 @@
 
     static yy_state_type yy_get_previous_state (yyscan_t yyscanner)
 {
-	register yy_state_type yy_current_state;
-	register char *yy_cp;
+	yy_state_type yy_current_state;
+	char *yy_cp;
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
 
 	yy_current_state = yyg->yy_start;
 
 	for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp )
 		{
-		register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+		YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
 		if ( yy_accept[yy_current_state] )
 			{
 			yyg->yy_last_accepting_state = yy_current_state;
@@ -1639,7 +1716,7 @@
 		while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 			{
 			yy_current_state = (int) yy_def[yy_current_state];
-			if ( yy_current_state >= 205 )
+			if ( yy_current_state >= 239 )
 				yy_c = yy_meta[(unsigned int) yy_c];
 			}
 		yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
@@ -1655,11 +1732,11 @@
  */
     static yy_state_type yy_try_NUL_trans  (yy_state_type yy_current_state , yyscan_t yyscanner)
 {
-	register int yy_is_jam;
+	int yy_is_jam;
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */
-	register char *yy_cp = yyg->yy_c_buf_p;
+	char *yy_cp = yyg->yy_c_buf_p;
 
-	register YY_CHAR yy_c = 1;
+	YY_CHAR yy_c = 1;
 	if ( yy_accept[yy_current_state] )
 		{
 		yyg->yy_last_accepting_state = yy_current_state;
@@ -1668,18 +1745,21 @@
 	while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 		{
 		yy_current_state = (int) yy_def[yy_current_state];
-		if ( yy_current_state >= 205 )
+		if ( yy_current_state >= 239 )
 			yy_c = yy_meta[(unsigned int) yy_c];
 		}
 	yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
-	yy_is_jam = (yy_current_state == 204);
+	yy_is_jam = (yy_current_state == 238);
 
+	(void)yyg;
 	return yy_is_jam ? 0 : yy_current_state;
 }
 
-    static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner)
+#ifndef YY_NO_UNPUT
+
+    static void yyunput (int c, char * yy_bp , yyscan_t yyscanner)
 {
-	register char *yy_cp;
+	char *yy_cp;
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
 
     yy_cp = yyg->yy_c_buf_p;
@@ -1690,10 +1770,10 @@
 	if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
 		{ /* need to shift things up to make room */
 		/* +2 for EOB chars. */
-		register yy_size_t number_to_move = yyg->yy_n_chars + 2;
-		register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+		yy_size_t number_to_move = yyg->yy_n_chars + 2;
+		char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
 					YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
-		register char *source =
+		char *source =
 				&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
 
 		while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
@@ -1719,6 +1799,8 @@
 	yyg->yy_c_buf_p = yy_cp;
 }
 
+#endif
+
 #ifndef YY_NO_INPUT
 #ifdef __cplusplus
     static int yyinput (yyscan_t yyscanner)
@@ -1768,7 +1850,7 @@
 				case EOB_ACT_END_OF_FILE:
 					{
 					if ( skslwrap(yyscanner ) )
-						return 0;
+						return EOF;
 
 					if ( ! yyg->yy_did_buffer_switch_on_eof )
 						YY_NEW_FILE;
@@ -1879,7 +1961,7 @@
 	if ( ! b )
 		YY_FATAL_ERROR( "out of dynamic memory in sksl_create_buffer()" );
 
-	b->yy_buf_size = size;
+	b->yy_buf_size = (yy_size_t)size;
 
 	/* yy_ch_buf has to be 2 characters longer than the size given because
 	 * we need to put in 2 end-of-buffer characters.
@@ -2040,7 +2122,7 @@
 		 * scanner will even need a stack. We use 2 instead of 1 to avoid an
 		 * immediate realloc on the next call.
          */
-		num_to_alloc = 1;
+		num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */
 		yyg->yy_buffer_stack = (struct yy_buffer_state**)skslalloc
 								(num_to_alloc * sizeof(struct yy_buffer_state*)
 								, yyscanner);
@@ -2057,7 +2139,7 @@
 	if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){
 
 		/* Increase the buffer to prepare for a possible push. */
-		int grow_size = 8 /* arbitrary grow size */;
+		yy_size_t grow_size = 8 /* arbitrary grow size */;
 
 		num_to_alloc = yyg->yy_buffer_stack_max + grow_size;
 		yyg->yy_buffer_stack = (struct yy_buffer_state**)skslrealloc
@@ -2124,8 +2206,8 @@
 
 /** Setup the input buffer state to scan the given bytes. The next call to sksllex() will
  * scan from a @e copy of @a bytes.
- * @param bytes the byte buffer to scan
- * @param len the number of bytes in the buffer pointed to by @a bytes.
+ * @param yybytes the byte buffer to scan
+ * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
  * @param yyscanner The scanner object.
  * @return the newly allocated buffer state object.
  */
@@ -2133,7 +2215,8 @@
 {
 	YY_BUFFER_STATE b;
 	char *buf;
-	yy_size_t n, i;
+	yy_size_t n;
+	yy_size_t i;
     
 	/* Get memory for full buffer, including space for trailing EOB's. */
 	n = _yybytes_len + 2;
@@ -2164,7 +2247,9 @@
 
 static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner)
 {
-    	(void) fprintf( stderr, "%s\n", msg );
+	struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	(void)yyg;
+	(void) fprintf( stderr, "%s\n", msg );
 	exit( YY_EXIT_FAILURE );
 }
 
@@ -2270,51 +2355,51 @@
 }
 
 /** Set the current line number.
- * @param line_number
+ * @param _line_number line number
  * @param yyscanner The scanner object.
  */
-void skslset_lineno (int  line_number , yyscan_t yyscanner)
+void skslset_lineno (int  _line_number , yyscan_t yyscanner)
 {
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
 
         /* lineno is only valid if an input buffer exists. */
         if (! YY_CURRENT_BUFFER )
-           yy_fatal_error( "skslset_lineno called with no buffer" , yyscanner); 
+           YY_FATAL_ERROR( "skslset_lineno called with no buffer" );
     
-    yylineno = line_number;
+    yylineno = _line_number;
 }
 
 /** Set the current column.
- * @param line_number
+ * @param _column_no column number
  * @param yyscanner The scanner object.
  */
-void skslset_column (int  column_no , yyscan_t yyscanner)
+void skslset_column (int  _column_no , yyscan_t yyscanner)
 {
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
 
         /* column is only valid if an input buffer exists. */
         if (! YY_CURRENT_BUFFER )
-           yy_fatal_error( "skslset_column called with no buffer" , yyscanner); 
+           YY_FATAL_ERROR( "skslset_column called with no buffer" );
     
-    yycolumn = column_no;
+    yycolumn = _column_no;
 }
 
 /** Set the input stream. This does not discard the current
  * input buffer.
- * @param in_str A readable stream.
+ * @param _in_str A readable stream.
  * @param yyscanner The scanner object.
  * @see sksl_switch_to_buffer
  */
-void skslset_in (FILE *  in_str , yyscan_t yyscanner)
+void skslset_in (FILE *  _in_str , yyscan_t yyscanner)
 {
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-    yyin = in_str ;
+    yyin = _in_str ;
 }
 
-void skslset_out (FILE *  out_str , yyscan_t yyscanner)
+void skslset_out (FILE *  _out_str , yyscan_t yyscanner)
 {
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-    yyout = out_str ;
+    yyout = _out_str ;
 }
 
 int skslget_debug  (yyscan_t yyscanner)
@@ -2323,10 +2408,10 @@
     return yy_flex_debug;
 }
 
-void skslset_debug (int  bdebug , yyscan_t yyscanner)
+void skslset_debug (int  _bdebug , yyscan_t yyscanner)
 {
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-    yy_flex_debug = bdebug ;
+    yy_flex_debug = _bdebug ;
 }
 
 /* Accessor methods for yylval and yylloc */
@@ -2465,7 +2550,10 @@
 #ifndef yytext_ptr
 static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner)
 {
-	register int i;
+	struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	(void)yyg;
+
+	int i;
 	for ( i = 0; i < n; ++i )
 		s1[i] = s2[i];
 }
@@ -2474,7 +2562,7 @@
 #ifdef YY_NEED_STRLEN
 static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner)
 {
-	register int n;
+	int n;
 	for ( n = 0; s[n]; ++n )
 		;
 
@@ -2484,11 +2572,16 @@
 
 void *skslalloc (yy_size_t  size , yyscan_t yyscanner)
 {
+	struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	(void)yyg;
 	return (void *) malloc( size );
 }
 
 void *skslrealloc  (void * ptr, yy_size_t  size , yyscan_t yyscanner)
 {
+	struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	(void)yyg;
+
 	/* The cast to (char *) in the following accommodates both
 	 * implementations that use char* generic pointers, and those
 	 * that use void* generic pointers.  It works with the latter
@@ -2501,12 +2594,14 @@
 
 void skslfree (void * ptr , yyscan_t yyscanner)
 {
+	struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	(void)yyg;
 	free( (char *) ptr );	/* see skslrealloc() for (char *) cast */
 }
 
 #define YYTABLES_NAME "yytables"
 
-#line 191 "sksl.flex"
+#line 201 "sksl.flex"
 
 
 
diff --git a/src/sksl/sksl.flex b/src/sksl/sksl.flex
index 4455f50..25216bd 100644
--- a/src/sksl/sksl.flex
+++ b/src/sksl/sksl.flex
@@ -74,6 +74,16 @@
 
 noperspective { return SkSL::Token::NOPERSPECTIVE; }
 
+readonly { return SkSL::Token::READONLY; }
+
+writeonly { return SkSL::Token::WRITEONLY; }
+
+coherent { return SkSL::Token::COHERENT; }
+
+volatile { return SkSL::Token::VOLATILE; }
+
+restrict { return SkSL::Token::RESTRICT; }
+
 struct { return SkSL::Token::STRUCT; }
 
 layout { return SkSL::Token::LAYOUT; }