Add "sample locations" feature to GrProcessor

Adds a "sample locations" feature to GrProcessor. When enabled, this
allows a processor to know inside the shader where all the samples are
located. Also adds various infastructure to query, cache, and identify
multisample data.

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1717393002

Review URL: https://codereview.chromium.org/1717393002
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
index 4e0464a..c593f31 100644
--- a/src/gpu/GrGpu.cpp
+++ b/src/gpu/GrGpu.cpp
@@ -47,6 +47,7 @@
 GrGpu::GrGpu(GrContext* context)
     : fResetTimestamp(kExpiredTimestamp+1)
     , fResetBits(kAll_GrBackendState)
+    , fMultisampleSpecsAllocator(1)
     , fContext(context) {
 }
 
@@ -444,6 +445,61 @@
     this->onResolveRenderTarget(target);
 }
 
+inline static uint8_t multisample_specs_id(uint8_t numSamples, GrSurfaceOrigin origin,
+                                           const GrCaps& caps) {
+    if (!caps.sampleLocationsSupport()) {
+        return numSamples;
+    }
+
+    SkASSERT(numSamples < 128);
+    SkASSERT(kTopLeft_GrSurfaceOrigin == origin || kBottomLeft_GrSurfaceOrigin == origin);
+    return (numSamples << 1) | (origin - 1);
+
+    GR_STATIC_ASSERT(1 == kTopLeft_GrSurfaceOrigin);
+    GR_STATIC_ASSERT(2 == kBottomLeft_GrSurfaceOrigin);
+}
+
+const GrGpu::MultisampleSpecs& GrGpu::getMultisampleSpecs(GrRenderTarget* rt,
+                                                          const GrStencilSettings& stencil) {
+    const GrSurfaceDesc& desc = rt->desc();
+    uint8_t surfDescKey = multisample_specs_id(desc.fSampleCnt, desc.fOrigin, *this->caps());
+    if (fMultisampleSpecsMap.count() > surfDescKey && fMultisampleSpecsMap[surfDescKey]) {
+#if !defined(SK_DEBUG)
+        // In debug mode we query the multisample info every time and verify the caching is correct.
+        return *fMultisampleSpecsMap[surfDescKey];
+#endif
+    }
+    int effectiveSampleCnt;
+    SkAutoTDeleteArray<SkPoint> locations(nullptr);
+    this->onGetMultisampleSpecs(rt, stencil, &effectiveSampleCnt, &locations);
+    SkASSERT(effectiveSampleCnt && effectiveSampleCnt >= desc.fSampleCnt);
+    uint8_t effectiveKey = multisample_specs_id(effectiveSampleCnt, desc.fOrigin, *this->caps());
+    if (fMultisampleSpecsMap.count() > effectiveKey && fMultisampleSpecsMap[effectiveKey]) {
+        const MultisampleSpecs& specs = *fMultisampleSpecsMap[effectiveKey];
+        SkASSERT(effectiveKey == specs.fUniqueID);
+        SkASSERT(effectiveSampleCnt == specs.fEffectiveSampleCnt);
+        SkASSERT(!this->caps()->sampleLocationsSupport() ||
+                 !memcmp(locations.get(), specs.fSampleLocations.get(),
+                         effectiveSampleCnt * sizeof(SkPoint)));
+        SkASSERT(surfDescKey <= effectiveKey);
+        SkASSERT(!fMultisampleSpecsMap[surfDescKey] || fMultisampleSpecsMap[surfDescKey] == &specs);
+        fMultisampleSpecsMap[surfDescKey] = &specs;
+        return specs;
+    }
+    const MultisampleSpecs& specs = *new (&fMultisampleSpecsAllocator)
+        MultisampleSpecs{effectiveKey, effectiveSampleCnt, locations.detach()};
+    if (fMultisampleSpecsMap.count() <= effectiveKey) {
+        int n = 1 + effectiveKey - fMultisampleSpecsMap.count();
+        fMultisampleSpecsMap.push_back_n(n, (const MultisampleSpecs*) nullptr);
+    }
+    fMultisampleSpecsMap[effectiveKey] = &specs;
+    if (effectiveSampleCnt != desc.fSampleCnt) {
+        SkASSERT(surfDescKey < effectiveKey);
+        fMultisampleSpecsMap[surfDescKey] = &specs;
+    }
+    return specs;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 void GrGpu::draw(const DrawArgs& args, const GrVertices& vertices) {
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index 5d35fcf..ebe4116 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -12,6 +12,7 @@
 #include "GrProgramDesc.h"
 #include "GrStencil.h"
 #include "GrSwizzle.h"
+#include "GrAllocator.h"
 #include "GrTextureParamsAdjuster.h"
 #include "GrTypes.h"
 #include "GrXferProcessor.h"
@@ -365,6 +366,22 @@
                      const SkIRect& srcRect,
                      const SkIPoint& dstPoint);
 
+    struct MultisampleSpecs {
+        // Nonzero ID that uniquely identifies these multisample specs.
+        uint8_t                            fUniqueID;
+        // The actual number of samples the GPU will run. NOTE: this value can be greater than the
+        // the render target's sample count.
+        int                                fEffectiveSampleCnt;
+        // If sample locations are supported, contains the subpixel locations at which the GPU will
+        // sample. Pixel center is at (.5, .5) and (0, 0) indicates the top left corner.
+        SkAutoTDeleteArray<const SkPoint>  fSampleLocations;
+    };
+
+    // Finds a render target's multisample specs. The stencil settings are only needed to flush the
+    // draw state prior to querying multisample information; they should not have any effect on the
+    // multisample information itself.
+    const MultisampleSpecs& getMultisampleSpecs(GrRenderTarget*, const GrStencilSettings&);
+
     struct DrawArgs {
         DrawArgs(const GrPrimitiveProcessor* primProc,
                  const GrPipeline* pipeline,
@@ -601,6 +618,12 @@
                                const SkIRect& srcRect,
                                const SkIPoint& dstPoint) = 0;
 
+    // overridden by backend specific derived class to perform the multisample queries
+    virtual void onGetMultisampleSpecs(GrRenderTarget*,
+                                       const GrStencilSettings&,
+                                       int* effectiveSampleCnt,
+                                       SkAutoTDeleteArray<SkPoint>* sampleLocations) = 0;
+
     void resetContext() {
         this->onResetContext(fResetBits);
         fResetBits = 0;
@@ -609,6 +632,8 @@
 
     ResetTimestamp                                                      fResetTimestamp;
     uint32_t                                                            fResetBits;
+    SkTArray<const MultisampleSpecs*, true>                             fMultisampleSpecsMap;
+    GrTAllocator<MultisampleSpecs>                                      fMultisampleSpecsAllocator;
     // The context owns us, not vice-versa, so this ptr is not ref'ed by Gpu.
     GrContext*                                                          fContext;
 
diff --git a/src/gpu/GrProgramDesc.h b/src/gpu/GrProgramDesc.h
index 63e060e..ec6447d 100644
--- a/src/gpu/GrProgramDesc.h
+++ b/src/gpu/GrProgramDesc.h
@@ -70,9 +70,11 @@
     }
 
     struct KeyHeader {
-        // Set by GrGLShaderBuilder if there are effects that read the fragment position. Otherwise,
-        // 0.
-        uint8_t                     fFragPosKey;
+        // Set to uniquely identify the rt's origin, or 0 if the shader does not require this info.
+        uint8_t                     fSurfaceOriginKey;
+        // Set to uniquely identify the sample pattern, or 0 if the shader doesn't use sample
+        // locations.
+        uint8_t                     fSamplePatternKey;
         // Set to uniquely idenitify any swizzling of the shader's output color(s).
         uint8_t                     fOutputSwizzle;
         uint8_t                     fSnapVerticesToPixelCenters;
diff --git a/src/gpu/GrRenderTarget.cpp b/src/gpu/GrRenderTarget.cpp
index e547021..41c7f10 100644
--- a/src/gpu/GrRenderTarget.cpp
+++ b/src/gpu/GrRenderTarget.cpp
@@ -105,3 +105,8 @@
     } 
     return true;
 }
+
+const GrGpu::MultisampleSpecs&
+GrRenderTargetPriv::getMultisampleSpecs(const GrStencilSettings& stencil) const {
+    return fRenderTarget->getGpu()->getMultisampleSpecs(fRenderTarget, stencil);
+}
diff --git a/src/gpu/GrRenderTargetPriv.h b/src/gpu/GrRenderTargetPriv.h
index f4931db..c6c8933 100644
--- a/src/gpu/GrRenderTargetPriv.h
+++ b/src/gpu/GrRenderTargetPriv.h
@@ -9,6 +9,9 @@
 #define GrRenderTargetPriv_DEFINED
 
 #include "GrRenderTarget.h"
+#include "GrGpu.h"
+
+class GrStencilSettings;
 
 /** Class that adds methods to GrRenderTarget that are only intended for use internal to Skia.
     This class is purely a privileged window into GrRenderTarget. It should never have additional
@@ -27,6 +30,8 @@
      */
     bool attachStencilAttachment(GrStencilAttachment* stencil);
 
+    const GrGpu::MultisampleSpecs& getMultisampleSpecs(const GrStencilSettings& stencil) const;
+
 private:
     explicit GrRenderTargetPriv(GrRenderTarget* renderTarget) : fRenderTarget(renderTarget) {}
     GrRenderTargetPriv(const GrRenderTargetPriv&) {} // unimpl
diff --git a/src/gpu/GrTest.cpp b/src/gpu/GrTest.cpp
index 6f2b4bf..f25455a 100644
--- a/src/gpu/GrTest.cpp
+++ b/src/gpu/GrTest.cpp
@@ -316,6 +316,13 @@
                        const SkIRect& srcRect,
                        const SkIPoint& dstPoint) override { return false; };
 
+    void onGetMultisampleSpecs(GrRenderTarget* rt,
+                               const GrStencilSettings&,
+                               int* effectiveSampleCnt,
+                               SkAutoTDeleteArray<SkPoint>*) override {
+        *effectiveSampleCnt = rt->desc().fSampleCnt;
+    }
+
     bool initCopySurfaceDstDesc(const GrSurface* src, GrSurfaceDesc* desc) const override {
         return false;
     }
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index dc50c14..95f34a9 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -4141,6 +4141,39 @@
     return true;
 }
 
+void GrGLGpu::onGetMultisampleSpecs(GrRenderTarget* rt,
+                                    const GrStencilSettings& stencil,
+                                    int* effectiveSampleCnt,
+                                    SkAutoTDeleteArray<SkPoint>* sampleLocations) {
+    SkASSERT(!rt->hasMixedSamples() || rt->renderTargetPriv().getStencilAttachment() ||
+             stencil.isDisabled());
+
+    this->flushStencil(stencil);
+    this->flushHWAAState(rt, true, !stencil.isDisabled());
+    this->flushRenderTarget(static_cast<GrGLRenderTarget*>(rt), &SkIRect::EmptyIRect());
+
+    if (0 != this->caps()->maxRasterSamples()) {
+        GR_GL_GetIntegerv(this->glInterface(), GR_GL_EFFECTIVE_RASTER_SAMPLES, effectiveSampleCnt);
+    } else {
+        GR_GL_GetIntegerv(this->glInterface(), GR_GL_SAMPLES, effectiveSampleCnt);
+    }
+
+    SkASSERT(*effectiveSampleCnt >= rt->desc().fSampleCnt);
+
+    if (this->caps()->sampleLocationsSupport()) {
+        sampleLocations->reset(new SkPoint[*effectiveSampleCnt]);
+        for (int i = 0; i < *effectiveSampleCnt; ++i) {
+            GrGLfloat pos[2];
+            GL_CALL(GetMultisamplefv(GR_GL_SAMPLE_POSITION, i, pos));
+            if (kTopLeft_GrSurfaceOrigin == rt->origin()) {
+                (*sampleLocations)[i].set(pos[0], pos[1]);
+            } else {
+                (*sampleLocations)[i].set(pos[0], 1 - pos[1]);
+            }
+        }
+    }
+}
+
 void GrGLGpu::xferBarrier(GrRenderTarget* rt, GrXferBarrierType type) {
     SkASSERT(type);
     switch (type) {
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index 56ffcb3..1ad519a 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -223,6 +223,11 @@
                        const SkIRect& srcRect,
                        const SkIPoint& dstPoint) override;
 
+    void onGetMultisampleSpecs(GrRenderTarget*,
+                               const GrStencilSettings&,
+                               int* effectiveSampleCnt,
+                               SkAutoTDeleteArray<SkPoint>* sampleLocations) override;
+
     // binds texture unit in GL
     void setTextureUnit(int unitIdx);
 
diff --git a/src/gpu/gl/GrGLProgramDesc.cpp b/src/gpu/gl/GrGLProgramDesc.cpp
index 789eb62..68020a9 100644
--- a/src/gpu/gl/GrGLProgramDesc.cpp
+++ b/src/gpu/gl/GrGLProgramDesc.cpp
@@ -8,6 +8,7 @@
 
 #include "GrProcessor.h"
 #include "GrPipeline.h"
+#include "GrRenderTargetPriv.h"
 #include "SkChecksum.h"
 #include "gl/GrGLDefines.h"
 #include "gl/GrGLTexture.h"
@@ -147,15 +148,24 @@
     // make sure any padding in the header is zeroed.
     memset(header, 0, kHeaderSize);
 
-    if (requiredFeatures & GrProcessor::kFragmentPosition_RequiredFeature) {
-        header->fFragPosKey =
-                GrGLSLFragmentShaderBuilder::KeyForFragmentPosition(pipeline.getRenderTarget());
+    GrRenderTarget* rt = pipeline.getRenderTarget();
+
+    if (requiredFeatures & (GrProcessor::kFragmentPosition_RequiredFeature |
+                            GrProcessor::kSampleLocations_RequiredFeature)) {
+        header->fSurfaceOriginKey = GrGLSLFragmentShaderBuilder::KeyForSurfaceOrigin(rt->origin());
     } else {
-        header->fFragPosKey = 0;
+        header->fSurfaceOriginKey = 0;
     }
 
-    header->fOutputSwizzle =
-        glslCaps.configOutputSwizzle(pipeline.getRenderTarget()->config()).asKey();
+    if (requiredFeatures & GrProcessor::kSampleLocations_RequiredFeature) {
+        SkASSERT(pipeline.isHWAntialiasState());
+        header->fSamplePatternKey =
+            rt->renderTargetPriv().getMultisampleSpecs(pipeline.getStencil()).fUniqueID;
+    } else {
+        header->fSamplePatternKey = 0;
+    }
+
+    header->fOutputSwizzle = glslCaps.configOutputSwizzle(rt->config()).asKey();
 
     if (pipeline.ignoresCoverage()) {
         header->fIgnoresCoverage = 1;
diff --git a/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp b/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp
index e6717a9..166e474 100644
--- a/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp
+++ b/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp
@@ -7,6 +7,7 @@
 
 #include "GrGLSLFragmentShaderBuilder.h"
 #include "GrRenderTarget.h"
+#include "GrRenderTargetPriv.h"
 #include "gl/GrGLGpu.h"
 #include "glsl/GrGLSL.h"
 #include "glsl/GrGLSLCaps.h"
@@ -16,6 +17,18 @@
 
 const char* GrGLSLFragmentShaderBuilder::kDstTextureColorName = "_dstColor";
 
+static const char* sample_offset_array_name(GrGLSLFPFragmentBuilder::Coordinates coords) {
+    static const char* kArrayNames[] = {
+        "deviceSpaceSampleOffsets",
+        "windowSpaceSampleOffsets"
+    };
+    return kArrayNames[coords];
+
+    GR_STATIC_ASSERT(0 == GrGLSLFPFragmentBuilder::kSkiaDevice_Coordinates);
+    GR_STATIC_ASSERT(1 == GrGLSLFPFragmentBuilder::kGLSLWindow_Coordinates);
+    GR_STATIC_ASSERT(SK_ARRAY_COUNT(kArrayNames) == GrGLSLFPFragmentBuilder::kLast_Coordinates + 1);
+}
+
 static const char* specific_layout_qualifier_name(GrBlendEquation equation) {
     SkASSERT(GrBlendEquationIsAdvanced(equation));
 
@@ -57,23 +70,21 @@
                      kGrBlendEquationCnt - kFirstAdvancedGrBlendEquation);
 }
 
-GrGLSLFragmentShaderBuilder::FragPosKey
-GrGLSLFragmentShaderBuilder::KeyForFragmentPosition(const GrRenderTarget* dst) {
-    if (kTopLeft_GrSurfaceOrigin == dst->origin()) {
-        return kTopLeftFragPosRead_FragPosKey;
-    } else {
-        return kBottomLeftFragPosRead_FragPosKey;
-    }
+uint8_t GrGLSLFragmentShaderBuilder::KeyForSurfaceOrigin(GrSurfaceOrigin origin) {
+    SkASSERT(kTopLeft_GrSurfaceOrigin == origin || kBottomLeft_GrSurfaceOrigin == origin);
+    return origin;
+
+    GR_STATIC_ASSERT(1 == kTopLeft_GrSurfaceOrigin);
+    GR_STATIC_ASSERT(2 == kBottomLeft_GrSurfaceOrigin);
 }
 
-GrGLSLFragmentShaderBuilder::GrGLSLFragmentShaderBuilder(GrGLSLProgramBuilder* program,
-                                                         uint8_t fragPosKey)
+GrGLSLFragmentShaderBuilder::GrGLSLFragmentShaderBuilder(GrGLSLProgramBuilder* program)
     : GrGLSLFragmentBuilder(program)
     , fSetupFragPosition(false)
-    , fTopLeftFragPosRead(kTopLeftFragPosRead_FragPosKey == fragPosKey)
     , fHasCustomColorOutput(false)
     , fCustomColorOutputIndex(-1)
     , fHasSecondaryOutput(false)
+    , fUsedSampleOffsetArrays(0)
     , fHasInitializedSampleMask(false) {
     fSubstageIndices.push_back(0);
 #ifdef SK_DEBUG
@@ -82,10 +93,6 @@
 #endif
 }
 
-bool GrGLSLFragmentShaderBuilder::hasFragmentPosition() const {
-    return 0 != fProgramBuilder->header().fFragPosKey;
-}
-
 bool GrGLSLFragmentShaderBuilder::enableFeature(GLSLFeature feature) {
     const GrGLSLCaps& glslCaps = *fProgramBuilder->glslCaps();
     switch (feature) {
@@ -135,14 +142,13 @@
 }
 
 const char* GrGLSLFragmentShaderBuilder::fragmentPosition() {
-    SkASSERT(this->hasFragmentPosition());
     SkDEBUGCODE(fUsedProcessorFeatures |= GrProcessor::kFragmentPosition_RequiredFeature;)
 
     const GrGLSLCaps* glslCaps = fProgramBuilder->glslCaps();
     // We only declare "gl_FragCoord" when we're in the case where we want to use layout qualifiers
     // to reverse y. Otherwise it isn't necessary and whether the "in" qualifier appears in the
     // declaration varies in earlier GLSL specs. So it is simpler to omit it.
-    if (fTopLeftFragPosRead) {
+    if (kTopLeft_GrSurfaceOrigin == this->getSurfaceOrigin()) {
         fSetupFragPosition = true;
         return "gl_FragCoord";
     } else if (const char* extension = glslCaps->fragCoordConventionsExtensionString()) {
@@ -183,6 +189,17 @@
     }
 }
 
+void GrGLSLFragmentShaderBuilder::appendOffsetToSample(const char* sampleIdx, Coordinates coords) {
+    SkASSERT(fProgramBuilder->header().fSamplePatternKey);
+    SkDEBUGCODE(fUsedProcessorFeatures |= GrProcessor::kSampleLocations_RequiredFeature);
+    if (kTopLeft_GrSurfaceOrigin == this->getSurfaceOrigin()) {
+        // With a top left origin, device and window space are equal, so we only use device coords.
+        coords = kSkiaDevice_Coordinates;
+    }
+    this->codeAppendf("%s[%s]", sample_offset_array_name(coords), sampleIdx);
+    fUsedSampleOffsetArrays |= (1 << coords);
+}
+
 void GrGLSLFragmentShaderBuilder::maskSampleCoverage(const char* mask, bool invert) {
     const GrGLSLCaps& glslCaps = *fProgramBuilder->glslCaps();
     if (!glslCaps.sampleVariablesSupport()) {
@@ -314,11 +331,50 @@
                                                   : "gl_SecondaryFragColorEXT";
 }
 
+GrSurfaceOrigin GrGLSLFragmentShaderBuilder::getSurfaceOrigin() const {
+    SkASSERT(fProgramBuilder->header().fSurfaceOriginKey);
+    return static_cast<GrSurfaceOrigin>(fProgramBuilder->header().fSurfaceOriginKey);
+
+    GR_STATIC_ASSERT(1 == kTopLeft_GrSurfaceOrigin);
+    GR_STATIC_ASSERT(2 == kBottomLeft_GrSurfaceOrigin);
+}
+
 void GrGLSLFragmentShaderBuilder::onFinalize() {
     fProgramBuilder->varyingHandler()->getFragDecls(&this->inputs(), &this->outputs());
     GrGLSLAppendDefaultFloatPrecisionDeclaration(kDefault_GrSLPrecision,
                                                  *fProgramBuilder->glslCaps(),
                                                  &this->precisionQualifier());
+    if (fUsedSampleOffsetArrays & (1 << kSkiaDevice_Coordinates)) {
+        this->defineSampleOffsetArray(sample_offset_array_name(kSkiaDevice_Coordinates),
+                                      SkMatrix::MakeTrans(-0.5f, -0.5f));
+    }
+    if (fUsedSampleOffsetArrays & (1 << kGLSLWindow_Coordinates)) {
+        // With a top left origin, device and window space are equal, so we only use device coords.
+        SkASSERT(kBottomLeft_GrSurfaceOrigin == this->getSurfaceOrigin());
+        SkMatrix m;
+        m.setScale(1, -1);
+        m.preTranslate(-0.5f, -0.5f);
+        this->defineSampleOffsetArray(sample_offset_array_name(kGLSLWindow_Coordinates), m);
+    }
+}
+
+void GrGLSLFragmentShaderBuilder::defineSampleOffsetArray(const char* name, const SkMatrix& m) {
+    SkASSERT(fProgramBuilder->caps()->sampleLocationsSupport());
+    const GrPipeline& pipeline = fProgramBuilder->pipeline();
+    const GrRenderTargetPriv& rtp = pipeline.getRenderTarget()->renderTargetPriv();
+    const GrGpu::MultisampleSpecs& specs = rtp.getMultisampleSpecs(pipeline.getStencil());
+    SkSTArray<16, SkPoint, true> offsets;
+    offsets.push_back_n(specs.fEffectiveSampleCnt);
+    m.mapPoints(offsets.begin(), specs.fSampleLocations.get(), specs.fEffectiveSampleCnt);
+    this->definitions().append("const ");
+    if (fProgramBuilder->glslCaps()->usesPrecisionModifiers()) {
+        this->definitions().append("highp ");
+    }
+    this->definitions().appendf("vec2 %s[] = vec2[](", name);
+    for (int i = 0; i < specs.fEffectiveSampleCnt; ++i) {
+        this->definitions().appendf("vec2(%f, %f)", offsets[i].x(), offsets[i].y());
+        this->definitions().append(i + 1 != specs.fEffectiveSampleCnt ? ", " : ");\n");
+    }
 }
 
 void GrGLSLFragmentShaderBuilder::onBeforeChildProcEmitCode() {
diff --git a/src/gpu/glsl/GrGLSLFragmentShaderBuilder.h b/src/gpu/glsl/GrGLSLFragmentShaderBuilder.h
index 57b8ee9..614b04f 100644
--- a/src/gpu/glsl/GrGLSLFragmentShaderBuilder.h
+++ b/src/gpu/glsl/GrGLSLFragmentShaderBuilder.h
@@ -68,6 +68,23 @@
     /** Appease the compiler; the derived class initializes GrGLSLFragmentBuilder. */
     GrGLSLFPFragmentBuilder() : GrGLSLFragmentBuilder(nullptr) {}
 
+    enum Coordinates {
+        kSkiaDevice_Coordinates,
+        kGLSLWindow_Coordinates,
+
+        kLast_Coordinates = kGLSLWindow_Coordinates
+    };
+
+    /**
+     * Appends the offset from the center of the pixel to a specified sample.
+     *
+     * @param sampleIdx      GLSL expression of the sample index.
+     * @param Coordinates    Coordinate space in which to emit the offset.
+     *
+     * A processor must call setWillUseSampleLocations in its constructor before using this method.
+     */
+    virtual void appendOffsetToSample(const char* sampleIdx, Coordinates) = 0;
+
     /**
      * Subtracts sample coverage from the fragment. Any sample whose corresponding bit is not found
      * in the mask will not be written out to the framebuffer.
@@ -138,14 +155,11 @@
  */
 class GrGLSLFragmentShaderBuilder : public GrGLSLPPFragmentBuilder, public GrGLSLXPFragmentBuilder {
 public:
-    typedef uint8_t FragPosKey;
+   /** Returns a nonzero key for a surface's origin. This should only be called if a processor will
+       use the fragment position and/or sample locations. */
+    static uint8_t KeyForSurfaceOrigin(GrSurfaceOrigin);
 
-    /** Returns a key for reading the fragment location. This should only be called if there is an
-       effect that will requires the fragment position. If the fragment position is not required,
-       the key is 0. */
-    static FragPosKey KeyForFragmentPosition(const GrRenderTarget* dst);
-
-    GrGLSLFragmentShaderBuilder(GrGLSLProgramBuilder* program, uint8_t fragPosKey);
+    GrGLSLFragmentShaderBuilder(GrGLSLProgramBuilder* program);
 
     // Shared GrGLSLFragmentBuilder interface.
     bool enableFeature(GLSLFeature) override;
@@ -154,6 +168,7 @@
     const char* fragmentPosition() override;
 
     // GrGLSLFPFragmentBuilder interface.
+    void appendOffsetToSample(const char* sampleIdx, Coordinates) override;
     void maskSampleCoverage(const char* mask, bool invert = false) override;
     void overrideSampleCoverage(const char* mask) override;
     const SkString& getMangleString() const override { return fMangleString; }
@@ -167,8 +182,6 @@
     void enableAdvancedBlendEquationIfNeeded(GrBlendEquation) override;
 
 private:
-    bool hasFragmentPosition() const;
-
     // Private public interface, used by GrGLProgramBuilder to build a fragment shader
     void enableCustomOutput();
     void enableSecondaryOutput();
@@ -189,19 +202,10 @@
     static const char* DeclaredColorOutputName() { return "fsColorOut"; }
     static const char* DeclaredSecondaryColorOutputName() { return "fsSecondaryColorOut"; }
 
-    /*
-     * An internal call for GrGLProgramBuilder to use to add varyings to the vertex shader
-     */
-    void addVarying(GrGLSLVarying*, GrSLPrecision);
+    GrSurfaceOrigin getSurfaceOrigin() const;
 
     void onFinalize() override;
-
-    // Interpretation of FragPosKey when generating code
-    enum {
-        kNoFragPosRead_FragPosKey           = 0,  // The fragment positition will not be needed.
-        kTopLeftFragPosRead_FragPosKey      = 0x1,// Read frag pos relative to top-left.
-        kBottomLeftFragPosRead_FragPosKey   = 0x2,// Read frag pos relative to bottom-left.
-    };
+    void defineSampleOffsetArray(const char* name, const SkMatrix&);
 
     static const char* kDstTextureColorName;
 
@@ -225,12 +229,12 @@
      */
     SkString fMangleString;
 
-    bool fSetupFragPosition;
-    bool fTopLeftFragPosRead;
-    bool fHasCustomColorOutput;
-    int  fCustomColorOutputIndex;
-    bool fHasSecondaryOutput;
-    bool fHasInitializedSampleMask;
+    bool       fSetupFragPosition;
+    bool       fHasCustomColorOutput;
+    int        fCustomColorOutputIndex;
+    bool       fHasSecondaryOutput;
+    uint8_t    fUsedSampleOffsetArrays;
+    bool       fHasInitializedSampleMask;
 
 #ifdef SK_DEBUG
     // some state to verify shaders and effects are consistent, this is reset between effects by
diff --git a/src/gpu/glsl/GrGLSLProgramBuilder.cpp b/src/gpu/glsl/GrGLSLProgramBuilder.cpp
index 417e924..b9eaca4 100644
--- a/src/gpu/glsl/GrGLSLProgramBuilder.cpp
+++ b/src/gpu/glsl/GrGLSLProgramBuilder.cpp
@@ -18,7 +18,7 @@
 GrGLSLProgramBuilder::GrGLSLProgramBuilder(const DrawArgs& args)
     : fVS(this)
     , fGS(this)
-    , fFS(this, args.fDesc->header().fFragPosKey)
+    , fFS(this)
     , fStageIndex(-1)
     , fArgs(args)
     , fGeometryProcessor(nullptr)
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index a0e6aa5..1ce78fd 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -1211,6 +1211,13 @@
     return false;
 }
 
+void GrVkGpu::onGetMultisampleSpecs(GrRenderTarget* rt, const GrStencilSettings&,
+                                    int* effectiveSampleCnt, SkAutoTDeleteArray<SkPoint>*) {
+    // TODO: stub.
+    SkASSERT(!this->caps()->sampleLocationsSupport());
+    *effectiveSampleCnt = rt->desc().fSampleCnt;
+}
+
 bool GrVkGpu::onGetReadPixelsInfo(GrSurface* srcSurface, int width, int height, size_t rowBytes,
                                   GrPixelConfig readConfig, DrawPreference* drawPreference,
                                   ReadPixelTempDrawInfo* tempDrawInfo) {
diff --git a/src/gpu/vk/GrVkGpu.h b/src/gpu/vk/GrVkGpu.h
index 344f81c..723d714 100644
--- a/src/gpu/vk/GrVkGpu.h
+++ b/src/gpu/vk/GrVkGpu.h
@@ -76,6 +76,11 @@
                        const SkIRect& srcRect,
                        const SkIPoint& dstPoint) override;
 
+    void onGetMultisampleSpecs(GrRenderTarget* rt,
+                               const GrStencilSettings&,
+                               int* effectiveSampleCnt,
+                               SkAutoTDeleteArray<SkPoint>*);
+
     bool initCopySurfaceDstDesc(const GrSurface* src, GrSurfaceDesc* desc) const override {
         SkDebugf("initCopySurfaceDstDesc not yet implemented for Vulkan\n");
         return false;
diff --git a/src/gpu/vk/GrVkProgramDesc.cpp b/src/gpu/vk/GrVkProgramDesc.cpp
index 32357cd..74e6bbb 100644
--- a/src/gpu/vk/GrVkProgramDesc.cpp
+++ b/src/gpu/vk/GrVkProgramDesc.cpp
@@ -9,6 +9,7 @@
 //#include "GrVkProcessor.h"
 #include "GrProcessor.h"
 #include "GrPipeline.h"
+#include "GrRenderTargetPriv.h"
 #include "GrVkGpu.h"
 #include "GrVkUtil.h"
 #include "SkChecksum.h"
@@ -134,15 +135,24 @@
     // make sure any padding in the header is zeroed.
     memset(header, 0, kHeaderSize);
 
-    if (requiredFeatures & GrProcessor::kFragmentPosition_RequiredFeature) {
-        header->fFragPosKey =
-            GrGLSLFragmentShaderBuilder::KeyForFragmentPosition(pipeline.getRenderTarget());
+    GrRenderTarget* rt = pipeline.getRenderTarget();
+
+    if (requiredFeatures & (GrProcessor::kFragmentPosition_RequiredFeature |
+                            GrProcessor::kSampleLocations_RequiredFeature)) {
+        header->fSurfaceOriginKey = GrGLSLFragmentShaderBuilder::KeyForSurfaceOrigin(rt->origin());
     } else {
-        header->fFragPosKey = 0;
+        header->fSurfaceOriginKey = 0;
     }
 
-    header->fOutputSwizzle =
-        glslCaps.configOutputSwizzle(pipeline.getRenderTarget()->config()).asKey();
+    if (requiredFeatures & GrProcessor::kSampleLocations_RequiredFeature) {
+        SkASSERT(pipeline.isHWAntialiasState());
+        header->fSamplePatternKey =
+            rt->renderTargetPriv().getMultisampleSpecs(pipeline.getStencil()).fUniqueID;
+    } else {
+        header->fSamplePatternKey = 0;
+    }
+
+    header->fOutputSwizzle = glslCaps.configOutputSwizzle(rt->config()).asKey();
 
     if (pipeline.ignoresCoverage()) {
         header->fIgnoresCoverage = 1;