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/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;
     };
     /**