Make GrCaps and GrShaderCaps private.

Moves getCaps() from GrContext to GrContextPriv and removes unused refCaps().

Change-Id: Ic6a8951b656c0d1b2773eae73bff8e88af819866
Reviewed-on: https://skia-review.googlesource.com/127389
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
diff --git a/src/atlastext/SkAtlasTextTarget.cpp b/src/atlastext/SkAtlasTextTarget.cpp
index c1a21b0..39f0c1b 100644
--- a/src/atlastext/SkAtlasTextTarget.cpp
+++ b/src/atlastext/SkAtlasTextTarget.cpp
@@ -148,7 +148,7 @@
     if (op->maskType() != GrAtlasTextOp::kGrayscaleDistanceField_MaskType) {
         return;
     }
-    const GrCaps& caps = *this->context()->internal().grContext()->caps();
+    const GrCaps& caps = *this->context()->internal().grContext()->contextPriv().caps();
     op->finalizeForTextTarget(fColor, caps);
     int n = SkTMin(kMaxBatchLookBack, fOps.count());
     for (int i = 0; i < n; ++i) {
diff --git a/src/core/SkBlurMF.cpp b/src/core/SkBlurMF.cpp
index b75fd16..d716c33 100644
--- a/src/core/SkBlurMF.cpp
+++ b/src/core/SkBlurMF.cpp
@@ -21,6 +21,7 @@
 #if SK_SUPPORT_GPU
 #include "GrClip.h"
 #include "GrContext.h"
+#include "GrContextPriv.h"
 #include "GrFragmentProcessor.h"
 #include "GrRenderTargetContext.h"
 #include "GrResourceProvider.h"
@@ -758,8 +759,8 @@
         SkScalar pad = 3.0f * xformedSigma;
         rect.outset(pad, pad);
 
-        fp = GrRectBlurEffect::Make(proxyProvider, *context->caps()->shaderCaps(), rect,
-                                    xformedSigma);
+        fp = GrRectBlurEffect::Make(proxyProvider, *context->contextPriv().caps()->shaderCaps(),
+                                    rect, xformedSigma);
     } else if (path.isOval(&rect) && SkScalarNearlyEqual(rect.width(), rect.height())) {
         fp = GrCircleBlurFragmentProcessor::Make(proxyProvider, rect, xformedSigma);
 
@@ -812,7 +813,7 @@
             SkScalar pad = 3.0f * xformedSigma;
             const SkRect dstCoverageRect = devRRect.rect().makeOutset(pad, pad);
 
-            fp = GrRectBlurEffect::Make(proxyProvider, *context->caps()->shaderCaps(),
+            fp = GrRectBlurEffect::Make(proxyProvider, *context->contextPriv().caps()->shaderCaps(),
                                         dstCoverageRect, xformedSigma);
         } else {
             fp = GrCircleBlurFragmentProcessor::Make(proxyProvider,
diff --git a/src/core/SkDeferredDisplayListRecorder.cpp b/src/core/SkDeferredDisplayListRecorder.cpp
index d390228..d13fd39 100644
--- a/src/core/SkDeferredDisplayListRecorder.cpp
+++ b/src/core/SkDeferredDisplayListRecorder.cpp
@@ -102,11 +102,11 @@
     // DDL is being replayed into.
 
     GrInternalSurfaceFlags surfaceFlags = GrInternalSurfaceFlags::kNone;
-    if (fContext->caps()->usesMixedSamples() && desc.fSampleCnt > 1 && !usesGLFBO0) {
+    if (fContext->contextPriv().caps()->usesMixedSamples() && desc.fSampleCnt > 1 && !usesGLFBO0) {
         // In GL, FBO 0 never supports mixed samples
         surfaceFlags |= GrInternalSurfaceFlags::kMixedSampled;
     }
-    if (fContext->caps()->maxWindowRectangles() > 0 && !usesGLFBO0) {
+    if (fContext->contextPriv().caps()->maxWindowRectangles() > 0 && !usesGLFBO0) {
         // In GL, FBO 0 never supports window rectangles
         surfaceFlags |= GrInternalSurfaceFlags::kWindowRectsSupport;
     }
diff --git a/src/core/SkGpuBlurUtils.cpp b/src/core/SkGpuBlurUtils.cpp
index c75ff3e..a2f95ad 100644
--- a/src/core/SkGpuBlurUtils.cpp
+++ b/src/core/SkGpuBlurUtils.cpp
@@ -455,7 +455,7 @@
 
     int scaleFactorX, radiusX;
     int scaleFactorY, radiusY;
-    int maxTextureSize = context->caps()->maxTextureSize();
+    int maxTextureSize = context->contextPriv().caps()->maxTextureSize();
     sigmaX = adjust_sigma(sigmaX, maxTextureSize, &scaleFactorX, &radiusX);
     sigmaY = adjust_sigma(sigmaY, maxTextureSize, &scaleFactorY, &radiusY);
     SkASSERT(sigmaX || sigmaY);
diff --git a/src/core/SkSurfaceCharacterization.cpp b/src/core/SkSurfaceCharacterization.cpp
index 74160d2..d71559f 100644
--- a/src/core/SkSurfaceCharacterization.cpp
+++ b/src/core/SkSurfaceCharacterization.cpp
@@ -8,6 +8,7 @@
 #include "SkSurfaceCharacterization.h"
 
 #if SK_SUPPORT_GPU
+#include "GrCaps.h"
 #include "GrContextThreadSafeProxyPriv.h"
 
 bool SkSurfaceCharacterization::operator==(const SkSurfaceCharacterization& other) const {
diff --git a/src/effects/SkTableColorFilter.cpp b/src/effects/SkTableColorFilter.cpp
index 8cf3728..47d6ae2 100644
--- a/src/effects/SkTableColorFilter.cpp
+++ b/src/effects/SkTableColorFilter.cpp
@@ -443,7 +443,7 @@
     desc.fWidth  = bitmap.width();
     desc.fHeight = 128;
     desc.fRowHeight = bitmap.height();
-    desc.fConfig = SkImageInfo2GrPixelConfig(bitmap.info(), *context->caps());
+    desc.fConfig = SkImageInfo2GrPixelConfig(bitmap.info(), *context->contextPriv().caps());
 
     if (kUnknown_GrPixelConfig == desc.fConfig) {
         return nullptr;
diff --git a/src/gpu/GrBackendTextureImageGenerator.cpp b/src/gpu/GrBackendTextureImageGenerator.cpp
index 3835bfa..3ec773d 100644
--- a/src/gpu/GrBackendTextureImageGenerator.cpp
+++ b/src/gpu/GrBackendTextureImageGenerator.cpp
@@ -44,8 +44,8 @@
     context->contextPriv().getResourceCache()->insertCrossContextGpuResource(texture.get());
 
     GrBackendTexture backendTexture = texture->getBackendTexture();
-    if (!context->caps()->validateBackendTexture(backendTexture, colorType,
-                                                 &backendTexture.fConfig)) {
+    if (!context->contextPriv().caps()->validateBackendTexture(backendTexture, colorType,
+                                                               &backendTexture.fConfig)) {
         return nullptr;
     }
 
diff --git a/src/gpu/GrCaps.h b/src/gpu/GrCaps.h
new file mode 100644
index 0000000..5858afa
--- /dev/null
+++ b/src/gpu/GrCaps.h
@@ -0,0 +1,349 @@
+
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef GrCaps_DEFINED
+#define GrCaps_DEFINED
+
+#include "../private/GrTypesPriv.h"
+#include "GrBlend.h"
+#include "GrShaderCaps.h"
+#include "SkImageInfo.h"
+#include "SkRefCnt.h"
+#include "SkString.h"
+
+class GrBackendFormat;
+class GrBackendRenderTarget;
+class GrBackendTexture;
+struct GrContextOptions;
+class GrRenderTargetProxy;
+class GrSurface;
+class GrSurfaceProxy;
+class SkJSONWriter;
+
+/**
+ * Represents the capabilities of a GrContext.
+ */
+class GrCaps : public SkRefCnt {
+public:
+    GrCaps(const GrContextOptions&);
+
+    void dumpJSON(SkJSONWriter*) const;
+
+    const GrShaderCaps* shaderCaps() const { return fShaderCaps.get(); }
+
+    bool npotTextureTileSupport() const { return fNPOTTextureTileSupport; }
+    /** To avoid as-yet-unnecessary complexity we don't allow any partial support of MIP Maps (e.g.
+        only for POT textures) */
+    bool mipMapSupport() const { return fMipMapSupport; }
+
+    /**
+     * Skia convention is that a device only has sRGB support if it supports sRGB formats for both
+     * textures and framebuffers. In addition:
+     *   Decoding to linear of an sRGB texture can be disabled.
+     */
+    bool srgbSupport() const { return fSRGBSupport; }
+    /**
+     * Is there support for enabling/disabling sRGB writes for sRGB-capable color buffers?
+     */
+    bool srgbWriteControl() const { return fSRGBWriteControl; }
+    bool srgbDecodeDisableSupport() const { return fSRGBDecodeDisableSupport; }
+    bool discardRenderTargetSupport() const { return fDiscardRenderTargetSupport; }
+    bool gpuTracingSupport() const { return fGpuTracingSupport; }
+    bool oversizedStencilSupport() const { return fOversizedStencilSupport; }
+    bool textureBarrierSupport() const { return fTextureBarrierSupport; }
+    bool sampleLocationsSupport() const { return fSampleLocationsSupport; }
+    bool multisampleDisableSupport() const { return fMultisampleDisableSupport; }
+    bool instanceAttribSupport() const { return fInstanceAttribSupport; }
+    bool usesMixedSamples() const { return fUsesMixedSamples; }
+
+    // Primitive restart functionality is core in ES 3.0, but using it will cause slowdowns on some
+    // systems. This cap is only set if primitive restart will improve performance.
+    bool usePrimitiveRestart() const { return fUsePrimitiveRestart; }
+
+    bool preferClientSideDynamicBuffers() const { return fPreferClientSideDynamicBuffers; }
+
+    // On tilers, an initial fullscreen clear is an OPTIMIZATION. It allows the hardware to
+    // initialize each tile with a constant value rather than loading each pixel from memory.
+    bool preferFullscreenClears() const { return fPreferFullscreenClears; }
+
+    bool preferVRAMUseOverFlushes() const { return fPreferVRAMUseOverFlushes; }
+
+    bool blacklistCoverageCounting() const { return fBlacklistCoverageCounting; }
+
+    bool avoidStencilBuffers() const { return fAvoidStencilBuffers; }
+
+    /**
+     * Indicates the capabilities of the fixed function blend unit.
+     */
+    enum BlendEquationSupport {
+        kBasic_BlendEquationSupport,             //<! Support to select the operator that
+                                                 //   combines src and dst terms.
+        kAdvanced_BlendEquationSupport,          //<! Additional fixed function support for specific
+                                                 //   SVG/PDF blend modes. Requires blend barriers.
+        kAdvancedCoherent_BlendEquationSupport,  //<! Advanced blend equation support that does not
+                                                 //   require blend barriers, and permits overlap.
+
+        kLast_BlendEquationSupport = kAdvancedCoherent_BlendEquationSupport
+    };
+
+    BlendEquationSupport blendEquationSupport() const { return fBlendEquationSupport; }
+
+    bool advancedBlendEquationSupport() const {
+        return fBlendEquationSupport >= kAdvanced_BlendEquationSupport;
+    }
+
+    bool advancedCoherentBlendEquationSupport() const {
+        return kAdvancedCoherent_BlendEquationSupport == fBlendEquationSupport;
+    }
+
+    bool canUseAdvancedBlendEquation(GrBlendEquation equation) const {
+        SkASSERT(GrBlendEquationIsAdvanced(equation));
+        return SkToBool(fAdvBlendEqBlacklist & (1 << equation));
+    }
+
+    /**
+     * Indicates whether GPU->CPU memory mapping for GPU resources such as vertex buffers and
+     * textures allows partial mappings or full mappings.
+     */
+    enum MapFlags {
+        kNone_MapFlags   = 0x0,       //<! Cannot map the resource.
+
+        kCanMap_MapFlag  = 0x1,       //<! The resource can be mapped. Must be set for any of
+                                      //   the other flags to have meaning.
+        kSubset_MapFlag  = 0x2,       //<! The resource can be partially mapped.
+    };
+
+    uint32_t mapBufferFlags() const { return fMapBufferFlags; }
+
+    // Scratch textures not being reused means that those scratch textures
+    // that we upload to (i.e., don't have a render target) will not be
+    // recycled in the texture cache. This is to prevent ghosting by drivers
+    // (in particular for deferred architectures).
+    bool reuseScratchTextures() const { return fReuseScratchTextures; }
+    bool reuseScratchBuffers() const { return fReuseScratchBuffers; }
+
+    /// maximum number of attribute values per vertex
+    int maxVertexAttributes() const { return fMaxVertexAttributes; }
+
+    int maxRenderTargetSize() const { return fMaxRenderTargetSize; }
+
+    /** This is the largest render target size that can be used without incurring extra perfomance
+        cost. It is usually the max RT size, unless larger render targets are known to be slower. */
+    int maxPreferredRenderTargetSize() const { return fMaxPreferredRenderTargetSize; }
+
+    int maxTextureSize() const { return fMaxTextureSize; }
+
+    /** This is the maximum tile size to use by GPU devices for rendering sw-backed images/bitmaps.
+        It is usually the max texture size, unless we're overriding it for testing. */
+    int maxTileSize() const {
+        SkASSERT(fMaxTileSize <= fMaxTextureSize);
+        return fMaxTileSize;
+    }
+
+    int maxRasterSamples() const { return fMaxRasterSamples; }
+
+    int maxWindowRectangles() const { return fMaxWindowRectangles; }
+
+    // Returns whether mixed samples is supported for the given backend render target.
+    bool isWindowRectanglesSupportedForRT(const GrBackendRenderTarget& rt) const {
+        return this->maxWindowRectangles() > 0 && this->onIsWindowRectanglesSupportedForRT(rt);
+    }
+
+    // A tuned, platform-specific value for the maximum number of analytic fragment processors we
+    // should use to implement a clip, before falling back on a mask.
+    int maxClipAnalyticFPs() const { return fMaxClipAnalyticFPs; }
+
+    virtual bool isConfigTexturable(GrPixelConfig) const = 0;
+
+    // Returns whether a texture of the given config can be copied to a texture of the same config.
+    virtual bool isConfigCopyable(GrPixelConfig) const = 0;
+
+    // Returns the maximum supported sample count for a config. 0 means the config is not renderable
+    // 1 means the config is renderable but doesn't support MSAA.
+    virtual int maxRenderTargetSampleCount(GrPixelConfig) const = 0;
+
+    bool isConfigRenderable(GrPixelConfig config) const {
+        return this->maxRenderTargetSampleCount(config) > 0;
+    }
+
+    // TODO: Remove this after Flutter updated to no longer use it.
+    bool isConfigRenderable(GrPixelConfig config, bool withMSAA) const {
+        return this->maxRenderTargetSampleCount(config) > (withMSAA ? 1 : 0);
+    }
+
+    // Find a sample count greater than or equal to the requested count which is supported for a
+    // color buffer of the given config or 0 if no such sample count is supported. If the requested
+    // sample count is 1 then 1 will be returned if non-MSAA rendering is supported, otherwise 0.
+    // For historical reasons requestedCount==0 is handled identically to requestedCount==1.
+    virtual int getRenderTargetSampleCount(int requestedCount, GrPixelConfig) const = 0;
+    // TODO: Remove. Legacy name used by Chrome.
+    int getSampleCount(int requestedCount, GrPixelConfig config) const {
+        return this->getRenderTargetSampleCount(requestedCount, config);
+    }
+
+    /**
+     * Backends may have restrictions on what types of surfaces support GrGpu::writePixels().
+     * If this returns false then the caller should implement a fallback where a temporary texture
+     * is created, pixels are written to it, and then that is copied or drawn into the the surface.
+     */
+    virtual bool surfaceSupportsWritePixels(const GrSurface*) const = 0;
+
+    /**
+     * Backends may have restrictions on what types of surfaces support GrGpu::readPixels().
+     * If this returns false then the caller should implement a fallback where a temporary texture
+     * is created, the surface is drawn or copied into the temporary, and pixels are read from the
+     * temporary.
+     */
+    virtual bool surfaceSupportsReadPixels(const GrSurface*) const = 0;
+
+    /**
+     * Given a dst pixel config and a src color type what color type must the caller coax the
+     * the data into in order to use GrGpu::writePixels().
+     */
+    virtual GrColorType supportedWritePixelsColorType(GrPixelConfig config,
+                                                      GrColorType /*srcColorType*/) const {
+        return GrPixelConfigToColorType(config);
+    }
+
+    /**
+     * Given a src pixel config and a dst color type what color type must the caller read to using
+     * GrGpu::readPixels() and then coax into dstColorType.
+     */
+    virtual GrColorType supportedReadPixelsColorType(GrPixelConfig config,
+                                                     GrColorType /*dstColorType*/) const {
+        return GrPixelConfigToColorType(config);
+    }
+
+    bool suppressPrints() const { return fSuppressPrints; }
+
+    size_t bufferMapThreshold() const {
+        SkASSERT(fBufferMapThreshold >= 0);
+        return fBufferMapThreshold;
+    }
+
+    /** True in environments that will issue errors if memory uploaded to buffers
+        is not initialized (even if not read by draw calls). */
+    bool mustClearUploadedBufferData() const { return fMustClearUploadedBufferData; }
+
+    bool wireframeMode() const { return fWireframeMode; }
+
+    bool sampleShadingSupport() const { return fSampleShadingSupport; }
+
+    bool fenceSyncSupport() const { return fFenceSyncSupport; }
+    bool crossContextTextureSupport() const { return fCrossContextTextureSupport; }
+
+    /**
+     * Returns whether or not we will be able to do a copy given the passed in params
+     */
+    virtual bool canCopySurface(const GrSurfaceProxy* dst, const GrSurfaceProxy* src,
+                                const SkIRect& srcRect, const SkIPoint& dstPoint) const = 0;
+
+    /**
+     * This is can be called before allocating a texture to be a dst for copySurface. This is only
+     * used for doing dst copies needed in blends, thus the src is always a GrRenderTargetProxy. It
+     * will populate config and flags fields of the desc such that copySurface can efficiently
+     * succeed as well as the proxy origin. rectsMustMatch will be set to true if the copy operation
+     * must ensure that the src and dest rects are identical. disallowSubrect will be set to true if
+     * copy rect must equal src's bounds.
+     */
+    virtual bool initDescForDstCopy(const GrRenderTargetProxy* src, GrSurfaceDesc* desc,
+                                    GrSurfaceOrigin* origin, bool* rectsMustMatch,
+                                    bool* disallowSubrect) const = 0;
+
+    bool validateSurfaceDesc(const GrSurfaceDesc&, GrMipMapped) const;
+
+    /**
+     * Returns true if the GrBackendTexture can be used with the supplied SkColorType. If it is
+     * compatible, the passed in GrPixelConfig will be set to a config that matches the backend
+     * format and requested SkColorType.
+     */
+    virtual bool validateBackendTexture(const GrBackendTexture& tex, SkColorType ct,
+                                        GrPixelConfig*) const = 0;
+    virtual bool validateBackendRenderTarget(const GrBackendRenderTarget&, SkColorType,
+                                             GrPixelConfig*) const = 0;
+
+    // TODO: replace validateBackendTexture and validateBackendRenderTarget with calls to
+    // getConfigFromBackendFormat?
+    // TODO: it seems like we could pass the full SkImageInfo and validate its colorSpace too
+    virtual bool getConfigFromBackendFormat(const GrBackendFormat& format, SkColorType ct,
+                                            GrPixelConfig*) const = 0;
+
+protected:
+    /** Subclasses must call this at the end of their constructors in order to apply caps
+        overrides requested by the client. Note that overrides will only reduce the caps never
+        expand them. */
+    void applyOptionsOverrides(const GrContextOptions& options);
+
+    sk_sp<GrShaderCaps> fShaderCaps;
+
+    bool fNPOTTextureTileSupport                     : 1;
+    bool fMipMapSupport                              : 1;
+    bool fSRGBSupport                                : 1;
+    bool fSRGBWriteControl                           : 1;
+    bool fSRGBDecodeDisableSupport                   : 1;
+    bool fDiscardRenderTargetSupport                 : 1;
+    bool fReuseScratchTextures                       : 1;
+    bool fReuseScratchBuffers                        : 1;
+    bool fGpuTracingSupport                          : 1;
+    bool fOversizedStencilSupport                    : 1;
+    bool fTextureBarrierSupport                      : 1;
+    bool fSampleLocationsSupport                     : 1;
+    bool fMultisampleDisableSupport                  : 1;
+    bool fInstanceAttribSupport                      : 1;
+    bool fUsesMixedSamples                           : 1;
+    bool fUsePrimitiveRestart                        : 1;
+    bool fPreferClientSideDynamicBuffers             : 1;
+    bool fPreferFullscreenClears                     : 1;
+    bool fMustClearUploadedBufferData                : 1;
+
+    // Driver workaround
+    bool fBlacklistCoverageCounting                  : 1;
+    bool fAvoidStencilBuffers                        : 1;
+
+    // ANGLE performance workaround
+    bool fPreferVRAMUseOverFlushes                   : 1;
+
+    bool fSampleShadingSupport                       : 1;
+    // TODO: this may need to be an enum to support different fence types
+    bool fFenceSyncSupport                           : 1;
+
+    // Vulkan doesn't support this (yet) and some drivers have issues, too
+    bool fCrossContextTextureSupport                 : 1;
+
+    BlendEquationSupport fBlendEquationSupport;
+    uint32_t fAdvBlendEqBlacklist;
+    GR_STATIC_ASSERT(kLast_GrBlendEquation < 32);
+
+    uint32_t fMapBufferFlags;
+    int fBufferMapThreshold;
+
+    int fMaxRenderTargetSize;
+    int fMaxPreferredRenderTargetSize;
+    int fMaxVertexAttributes;
+    int fMaxTextureSize;
+    int fMaxTileSize;
+    int fMaxRasterSamples;
+    int fMaxWindowRectangles;
+    int fMaxClipAnalyticFPs;
+
+private:
+    virtual void onApplyOptionsOverrides(const GrContextOptions&) {}
+    virtual void onDumpJSON(SkJSONWriter*) const {}
+
+    // Backends should implement this if they have any extra requirements for use of window
+    // rectangles for a specific GrBackendRenderTarget outside of basic support.
+    virtual bool onIsWindowRectanglesSupportedForRT(const GrBackendRenderTarget&) const {
+        return true;
+    }
+
+    bool fSuppressPrints : 1;
+    bool fWireframeMode  : 1;
+
+    typedef SkRefCnt INHERITED;
+};
+
+#endif
diff --git a/src/gpu/GrClipStackClip.cpp b/src/gpu/GrClipStackClip.cpp
index f5c08f0..92a6d04 100644
--- a/src/gpu/GrClipStackClip.cpp
+++ b/src/gpu/GrClipStackClip.cpp
@@ -117,14 +117,14 @@
 
         GrShape shape(path, GrStyle::SimpleFill());
         GrPathRenderer::CanDrawPathArgs canDrawArgs;
-        canDrawArgs.fCaps = context->caps();
+        canDrawArgs.fCaps = context->contextPriv().caps();
         canDrawArgs.fClipConservativeBounds = &scissorRect;
         canDrawArgs.fViewMatrix = &viewMatrix;
         canDrawArgs.fShape = &shape;
         canDrawArgs.fAAType = GrChooseAAType(GrAA(element->isAA()),
                                              renderTargetContext->fsaaType(),
                                              GrAllowMixedSamples::kYes,
-                                             *context->caps());
+                                             *context->contextPriv().caps());
         canDrawArgs.fHasUserStencilSettings = hasUserStencilSettings;
 
         // the 'false' parameter disallows use of the SW path renderer
@@ -151,8 +151,9 @@
     // of whether it would invoke the GrSoftwarePathRenderer.
 
     // If we're avoiding stencils, always use SW:
-    if (context->caps()->avoidStencilBuffers())
+    if (context->contextPriv().caps()->avoidStencilBuffers()) {
         return true;
+    }
 
     // Set the matrix so that rendered clip elements are transformed to mask space from clip
     // space.
@@ -191,9 +192,9 @@
     }
 
     GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
-    const auto* caps = context->caps()->shaderCaps();
+    const auto* caps = context->contextPriv().caps()->shaderCaps();
     int maxWindowRectangles = renderTargetContext->priv().maxWindowRectangles();
-    int maxAnalyticFPs = context->caps()->maxClipAnalyticFPs();
+    int maxAnalyticFPs = context->contextPriv().caps()->maxClipAnalyticFPs();
     if (GrFSAAType::kNone != renderTargetContext->fsaaType()) {
         // With mixed samples (non-msaa color buffer), any coverage info is lost from color once it
         // hits the color buffer anyway, so we may as well use coverage AA if nothing else in the
@@ -201,7 +202,8 @@
         if (renderTargetContext->numColorSamples() > 1 || useHWAA || hasUserStencilSettings) {
             maxAnalyticFPs = 0;
         }
-        SkASSERT(!context->caps()->avoidStencilBuffers()); // We disable MSAA when avoiding stencil.
+        // We disable MSAA when avoiding stencil.
+        SkASSERT(!context->contextPriv().caps()->avoidStencilBuffers());
     }
     auto* ccpr = context->contextPriv().drawingManager()->getCoverageCountingPathRenderer();
 
@@ -253,7 +255,7 @@
 
     // If the stencil buffer is multisampled we can use it to do everything.
     if ((GrFSAAType::kNone == renderTargetContext->fsaaType() && reducedClip.maskRequiresAA()) ||
-        context->caps()->avoidStencilBuffers()) {
+        context->contextPriv().caps()->avoidStencilBuffers()) {
         sk_sp<GrTextureProxy> result;
         if (UseSWOnlyPath(context, hasUserStencilSettings, renderTargetContext, reducedClip)) {
             // The clip geometry is complex enough that it will be more efficient to create it
@@ -272,7 +274,7 @@
 
         // If alpha or software clip mask creation fails, fall through to the stencil code paths,
         // unless stencils are disallowed.
-        if (context->caps()->avoidStencilBuffers()) {
+        if (context->contextPriv().caps()->avoidStencilBuffers()) {
             SkDebugf("WARNING: Clip mask requires stencil, but stencil unavailable. "
                      "Clip will be ignored.\n");
             return false;
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 499c711..3bb5b3a 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -163,6 +163,16 @@
     delete fGlyphCache;
 }
 
+//////////////////////////////////////////////////////////////////////////////
+
+GrContextThreadSafeProxy::GrContextThreadSafeProxy(sk_sp<const GrCaps> caps, uint32_t uniqueID,
+                                                   GrBackend backend,
+                                                   const GrContextOptions& options)
+        : fCaps(std::move(caps))
+        , fContextUniqueID(uniqueID)
+        , fBackend(backend)
+        , fOptions(options) {}
+
 sk_sp<GrContextThreadSafeProxy> GrContext::threadSafeProxy() {
     return fThreadSafeProxy;
 }
@@ -314,18 +324,18 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-int GrContext::maxTextureSize() const { return this->caps()->maxTextureSize(); }
+int GrContext::maxTextureSize() const { return fCaps->maxTextureSize(); }
 
-int GrContext::maxRenderTargetSize() const { return this->caps()->maxRenderTargetSize(); }
+int GrContext::maxRenderTargetSize() const { return fCaps->maxRenderTargetSize(); }
 
 bool GrContext::colorTypeSupportedAsImage(SkColorType colorType) const {
-    GrPixelConfig config = SkImageInfo2GrPixelConfig(colorType, nullptr, *this->caps());
-    return this->caps()->isConfigTexturable(config);
+    GrPixelConfig config = SkImageInfo2GrPixelConfig(colorType, nullptr, *fCaps);
+    return fCaps->isConfigTexturable(config);
 }
 
 int GrContext::maxSurfaceSampleCountForColorType(SkColorType colorType) const {
-    GrPixelConfig config = SkImageInfo2GrPixelConfig(colorType, nullptr, *this->caps());
-    return this->caps()->maxRenderTargetSampleCount(config);
+    GrPixelConfig config = SkImageInfo2GrPixelConfig(colorType, nullptr, *fCaps);
+    return fCaps->maxRenderTargetSampleCount(config);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -574,7 +584,7 @@
     GrGpu::WritePixelTempDrawInfo tempDrawInfo;
     GrSRGBConversion srgbConversion = determine_write_pixels_srgb_conversion(
             srcColorType, srcColorSpace, GrPixelConfigIsSRGBEncoded(dstProxy->config()),
-            dst->colorSpaceInfo().colorSpace(), *fContext->caps());
+            dst->colorSpaceInfo().colorSpace(), *fContext->contextPriv().caps());
     if (!fContext->fGpu->getWritePixelsInfo(dstSurface, dstProxy->origin(), width, height,
                                             srcColorType, srgbConversion, &drawPreference,
                                             &tempDrawInfo)) {
@@ -743,7 +753,7 @@
     GrGpu::ReadPixelTempDrawInfo tempDrawInfo;
     GrSRGBConversion srgbConversion = determine_read_pixels_srgb_conversion(
             GrPixelConfigIsSRGBEncoded(srcProxy->config()), src->colorSpaceInfo().colorSpace(),
-            dstColorType, dstColorSpace, *fContext->caps());
+            dstColorType, dstColorSpace, *fContext->contextPriv().caps());
 
     if (!fContext->fGpu->getReadPixelsInfo(srcSurface, srcProxy->origin(), width, height, rowBytes,
                                            dstColorType, srgbConversion, &drawPreference,
@@ -895,10 +905,11 @@
             (dstProxy->config() == kRGBA_8888_GrPixelConfig ||
              dstProxy->config() == kBGRA_8888_GrPixelConfig) &&
             !(pixelOpsFlags & kDontFlush_PixelOpsFlag) &&
-            fContext->caps()->isConfigTexturable(kRGBA_8888_GrPixelConfig) &&
+            fContext->contextPriv().caps()->isConfigTexturable(kRGBA_8888_GrPixelConfig) &&
             fContext->validPMUPMConversionExists();
 
-    if (!fContext->caps()->surfaceSupportsWritePixels(dstSurface) || canvas2DFastPath) {
+    if (!fContext->contextPriv().caps()->surfaceSupportsWritePixels(dstSurface) ||
+        canvas2DFastPath) {
         // We don't expect callers that are skipping flushes to require an intermediate draw.
         SkASSERT(!(pixelOpsFlags & kDontFlush_PixelOpsFlag));
         if (pixelOpsFlags & kDontFlush_PixelOpsFlag) {
@@ -957,8 +968,8 @@
         return false;
     }
 
-    GrColorType allowedColorType =
-            fContext->caps()->supportedWritePixelsColorType(dstProxy->config(), srcColorType);
+    GrColorType allowedColorType = fContext->contextPriv().caps()->supportedWritePixelsColorType(
+            dstProxy->config(), srcColorType);
     convert = convert || (srcColorType != allowedColorType);
 
     if (!dst->colorSpaceInfo().colorSpace()) {
@@ -1071,10 +1082,11 @@
             SkToBool(srcProxy->asTextureProxy()) &&
             (srcProxy->config() == kRGBA_8888_GrPixelConfig ||
              srcProxy->config() == kBGRA_8888_GrPixelConfig) &&
-            fContext->caps()->isConfigRenderable(kRGBA_8888_GrPixelConfig) &&
+            fContext->contextPriv().caps()->isConfigRenderable(kRGBA_8888_GrPixelConfig) &&
             fContext->validPMUPMConversionExists();
 
-    if (!fContext->caps()->surfaceSupportsReadPixels(srcSurface) || canvas2DFastPath) {
+    if (!fContext->contextPriv().caps()->surfaceSupportsReadPixels(srcSurface) ||
+        canvas2DFastPath) {
         GrSurfaceDesc desc;
         desc.fFlags = canvas2DFastPath ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags;
         desc.fConfig = canvas2DFastPath ? kRGBA_8888_GrPixelConfig : srcProxy->config();
@@ -1132,8 +1144,8 @@
         top = srcSurface->height() - top - height;
     }
 
-    GrColorType allowedColorType =
-            fContext->caps()->supportedReadPixelsColorType(srcProxy->config(), dstColorType);
+    GrColorType allowedColorType = fContext->contextPriv().caps()->supportedReadPixelsColorType(
+            srcProxy->config(), dstColorType);
     convert = convert || (dstColorType != allowedColorType);
 
     if (!src->colorSpaceInfo().colorSpace()) {
@@ -1392,7 +1404,7 @@
                                                                  const SkSurfaceProps* surfaceProps,
                                                                  SkBudgeted budgeted) {
     SkASSERT(sampleCnt > 0);
-    if (0 == fContext->caps()->getRenderTargetSampleCount(sampleCnt, config)) {
+    if (0 == fContext->contextPriv().caps()->getRenderTargetSampleCount(sampleCnt, config)) {
         config = GrPixelConfigFallback(config);
     }
 
diff --git a/src/gpu/GrContextPriv.h b/src/gpu/GrContextPriv.h
index 61ff3c1..7e6ecb8 100644
--- a/src/gpu/GrContextPriv.h
+++ b/src/gpu/GrContextPriv.h
@@ -30,6 +30,8 @@
      */
     static sk_sp<GrContext> MakeDDL(const sk_sp<GrContextThreadSafeProxy>&);
 
+    const GrCaps* caps() const { return fContext->fCaps.get(); }
+
     GrDrawingManager* drawingManager() { return fContext->fDrawingManager.get(); }
 
     sk_sp<GrSurfaceContext> makeWrappedSurfaceContext(sk_sp<GrSurfaceProxy>,
diff --git a/src/gpu/GrDDLContext.cpp b/src/gpu/GrDDLContext.cpp
index 5ed65ea..1ae640c 100644
--- a/src/gpu/GrDDLContext.cpp
+++ b/src/gpu/GrDDLContext.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "GrContext.h"
+#include "GrCaps.h"
 #include "GrContextPriv.h"
 #include "GrContextThreadSafeProxyPriv.h"
 
diff --git a/src/gpu/GrDrawOpTest.cpp b/src/gpu/GrDrawOpTest.cpp
index 7aba424..d14e5b5 100644
--- a/src/gpu/GrDrawOpTest.cpp
+++ b/src/gpu/GrDrawOpTest.cpp
@@ -8,6 +8,7 @@
 #include "GrDrawOpTest.h"
 #include "GrCaps.h"
 #include "GrContext.h"
+#include "GrContextPriv.h"
 #include "GrUserStencilSettings.h"
 #include "SkRandom.h"
 #include "SkTypes.h"
@@ -15,7 +16,7 @@
 #if GR_TEST_UTILS
 
 const GrUserStencilSettings* GrGetRandomStencil(SkRandom* random, GrContext* context) {
-    if (context->caps()->avoidStencilBuffers()) {
+    if (context->contextPriv().caps()->avoidStencilBuffers()) {
         return &GrUserStencilSettings::kUnused;
     }
     static constexpr GrUserStencilSettings kReads(
diff --git a/src/gpu/GrDrawingManager.cpp b/src/gpu/GrDrawingManager.cpp
index ffacf55..de414ae 100644
--- a/src/gpu/GrDrawingManager.cpp
+++ b/src/gpu/GrDrawingManager.cpp
@@ -6,7 +6,6 @@
  */
 
 #include "GrDrawingManager.h"
-
 #include "GrBackendSemaphore.h"
 #include "GrContext.h"
 #include "GrContextPriv.h"
@@ -23,11 +22,9 @@
 #include "GrTextureOpList.h"
 #include "GrTextureProxy.h"
 #include "GrTextureProxyPriv.h"
-
 #include "SkDeferredDisplayList.h"
 #include "SkSurface_Gpu.h"
 #include "SkTTopoSort.h"
-
 #include "GrTracing.h"
 #include "text/GrAtlasTextContext.h"
 
@@ -65,7 +62,7 @@
 void GrDrawingManager::cleanup() {
     for (int i = 0; i < fOpLists.count(); ++i) {
         // no opList should receive a new command after this
-        fOpLists[i]->makeClosed(*fContext->caps());
+        fOpLists[i]->makeClosed(*fContext->contextPriv().caps());
 
         // We shouldn't need to do this, but it turns out some clients still hold onto opLists
         // after a cleanup.
@@ -130,7 +127,7 @@
         // needs to flush mid-draw. In that case, the SkGpuDevice's GrOpLists won't be closed
         // but need to be flushed anyway. Closing such GrOpLists here will mean new
         // GrOpLists will be created to replace them if the SkGpuDevice(s) write to them again.
-        fOpLists[i]->makeClosed(*fContext->caps());
+        fOpLists[i]->makeClosed(*fContext->contextPriv().caps());
     }
 
 #ifdef SK_DEBUG
@@ -186,7 +183,7 @@
                     SkASSERT(GrSurfaceProxy::LazyState::kNot == p->lazyInstantiationState());
                 });
 #endif
-                onFlushOpList->makeClosed(*fContext->caps());
+                onFlushOpList->makeClosed(*fContext->contextPriv().caps());
                 onFlushOpList->prepare(&flushState);
                 fOnFlushCBOpLists.push_back(std::move(onFlushOpList));
             }
@@ -380,7 +377,7 @@
 void GrDrawingManager::moveOpListsToDDL(SkDeferredDisplayList* ddl) {
     for (int i = 0; i < fOpLists.count(); ++i) {
         // no opList should receive a new command after this
-        fOpLists[i]->makeClosed(*fContext->caps());
+        fOpLists[i]->makeClosed(*fContext->contextPriv().caps());
     }
 
     ddl->fOpLists = std::move(fOpLists);
@@ -402,7 +399,7 @@
     // so ops that (in the single opList world) would've just glommed onto the end of the single
     // opList but referred to a far earlier RT need to appear in their own opList.
     if (!fOpLists.empty()) {
-        fOpLists.back()->makeClosed(*fContext->caps());
+        fOpLists.back()->makeClosed(*fContext->contextPriv().caps());
     }
 
     auto resourceProvider = fContext->contextPriv().resourceProvider();
@@ -427,7 +424,7 @@
     // so ops that (in the single opList world) would've just glommed onto the end of the single
     // opList but referred to a far earlier RT need to appear in their own opList.
     if (!fOpLists.empty()) {
-        fOpLists.back()->makeClosed(*fContext->caps());
+        fOpLists.back()->makeClosed(*fContext->contextPriv().caps());
     }
 
     sk_sp<GrTextureOpList> opList(new GrTextureOpList(fContext->contextPriv().resourceProvider(),
@@ -502,7 +499,7 @@
 
     // SkSurface catches bad color space usage at creation. This check handles anything that slips
     // by, including internal usage.
-    if (!SkSurface_Gpu::Valid(fContext->caps(), sProxy->config(), colorSpace.get())) {
+    if (!SkSurface_Gpu::Valid(fContext->contextPriv().caps(), sProxy->config(), colorSpace.get())) {
         SkDEBUGFAIL("Invalid config and colorspace combination");
         return nullptr;
     }
@@ -525,7 +522,7 @@
 
     // SkSurface catches bad color space usage at creation. This check handles anything that slips
     // by, including internal usage.
-    if (!SkSurface_Gpu::Valid(fContext->caps(), sProxy->config(), colorSpace.get())) {
+    if (!SkSurface_Gpu::Valid(fContext->contextPriv().caps(), sProxy->config(), colorSpace.get())) {
         SkDEBUGFAIL("Invalid config and colorspace combination");
         return nullptr;
     }
diff --git a/src/gpu/GrImageTextureMaker.cpp b/src/gpu/GrImageTextureMaker.cpp
index 5bf81d2..920415f 100644
--- a/src/gpu/GrImageTextureMaker.cpp
+++ b/src/gpu/GrImageTextureMaker.cpp
@@ -6,12 +6,12 @@
  */
 
 #include "GrImageTextureMaker.h"
-
 #include "GrContext.h"
+#include "GrContextPriv.h"
 #include "GrGpuResourcePriv.h"
 #include "SkGr.h"
-#include "SkImage_Base.h"
 #include "SkImageCacherator.h"
+#include "SkImage_Base.h"
 #include "SkPixelRef.h"
 
 GrImageTextureMaker::GrImageTextureMaker(GrContext* context, const SkImage* client,
@@ -36,7 +36,7 @@
                                       SkColorSpace* dstColorSpace) {
     if (fOriginalKey.isValid() && SkImage::kAllow_CachingHint == fCachingHint) {
         SkImageCacherator::CachedFormat cacheFormat =
-            fCacher->chooseCacheFormat(dstColorSpace, this->context()->caps());
+                fCacher->chooseCacheFormat(dstColorSpace, this->context()->contextPriv().caps());
         GrUniqueKey cacheKey;
         fCacher->makeCacheKeyFromOrigKey(fOriginalKey, cacheFormat, &cacheKey);
         MakeCopyKeyFromOrigKey(cacheKey, stretch, paramsCopyKey);
diff --git a/src/gpu/GrOnFlushResourceProvider.cpp b/src/gpu/GrOnFlushResourceProvider.cpp
index d892dee..9e2e1fe 100644
--- a/src/gpu/GrOnFlushResourceProvider.cpp
+++ b/src/gpu/GrOnFlushResourceProvider.cpp
@@ -115,5 +115,5 @@
 }
 
 const GrCaps* GrOnFlushResourceProvider::caps() const {
-    return fDrawingMgr->getContext()->caps();
+    return fDrawingMgr->getContext()->contextPriv().caps();
 }
diff --git a/src/gpu/GrPathRenderer.h b/src/gpu/GrPathRenderer.h
index c5a1f63..39208bc 100644
--- a/src/gpu/GrPathRenderer.h
+++ b/src/gpu/GrPathRenderer.h
@@ -9,18 +9,17 @@
 #define GrPathRenderer_DEFINED
 
 #include "GrCaps.h"
-#include "GrRenderTargetContext.h"
+#include "GrContextPriv.h"
 #include "GrPaint.h"
+#include "GrRenderTargetContext.h"
 #include "GrShape.h"
 #include "GrUserStencilSettings.h"
-
 #include "SkDrawProcs.h"
 #include "SkTArray.h"
 
 class SkPath;
 class GrFixedClip;
 class GrHardClip;
-struct GrPoint;
 
 /**
  *  Base class for drawing paths into a GrOpList.
@@ -138,7 +137,7 @@
         SkDEBUGCODE(args.validate();)
 #ifdef SK_DEBUG
         CanDrawPathArgs canArgs;
-        canArgs.fCaps = args.fContext->caps();
+        canArgs.fCaps = args.fContext->contextPriv().caps();
         canArgs.fClipConservativeBounds = args.fClipConservativeBounds;
         canArgs.fViewMatrix = args.fViewMatrix;
         canArgs.fShape = args.fShape;
diff --git a/src/gpu/GrPathRendererChain.cpp b/src/gpu/GrPathRendererChain.cpp
index bc77634..4dc814e 100644
--- a/src/gpu/GrPathRendererChain.cpp
+++ b/src/gpu/GrPathRendererChain.cpp
@@ -7,15 +7,12 @@
 
 
 #include "GrPathRendererChain.h"
-
 #include "GrCaps.h"
 #include "GrShaderCaps.h"
 #include "GrContext.h"
 #include "GrContextPriv.h"
 #include "GrGpu.h"
-
 #include "ccpr/GrCoverageCountingPathRenderer.h"
-
 #include "ops/GrAAConvexPathRenderer.h"
 #include "ops/GrAAHairLinePathRenderer.h"
 #include "ops/GrAALinearizingConvexPathRenderer.h"
@@ -26,7 +23,7 @@
 #include "ops/GrTessellatingPathRenderer.h"
 
 GrPathRendererChain::GrPathRendererChain(GrContext* context, const Options& options) {
-    const GrCaps& caps = *context->caps();
+    const GrCaps& caps = *context->contextPriv().caps();
     if (options.fGpuPathRenderers & GpuPathRenderers::kDashLine) {
         fChain.push_back(sk_make_sp<GrDashLinePathRenderer>());
     }
@@ -43,8 +40,8 @@
 
     if (options.fGpuPathRenderers & GpuPathRenderers::kCoverageCounting) {
         bool drawCachablePaths = !options.fAllowPathMaskCaching;
-        if (auto ccpr = GrCoverageCountingPathRenderer::CreateIfSupported(*context->caps(),
-                                                                          drawCachablePaths)) {
+        if (auto ccpr =
+                    GrCoverageCountingPathRenderer::CreateIfSupported(caps, drawCachablePaths)) {
             fCoverageCountingPathRenderer = ccpr.get();
             context->contextPriv().addOnFlushCallbackObject(fCoverageCountingPathRenderer);
             fChain.push_back(std::move(ccpr));
diff --git a/src/gpu/GrProcessor.cpp b/src/gpu/GrProcessor.cpp
index 2b02b8d..561fd4f 100644
--- a/src/gpu/GrProcessor.cpp
+++ b/src/gpu/GrProcessor.cpp
@@ -25,9 +25,7 @@
     return fContext->contextPriv().proxyProvider();
 }
 
-const GrCaps* GrProcessorTestData::caps() {
-    return fContext->caps();
-}
+const GrCaps* GrProcessorTestData::caps() { return fContext->contextPriv().caps(); }
 
 #if SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
 class GrFragmentProcessor;
diff --git a/src/gpu/GrReducedClip.cpp b/src/gpu/GrReducedClip.cpp
index 1c5150c..e6cd6b3 100644
--- a/src/gpu/GrReducedClip.cpp
+++ b/src/gpu/GrReducedClip.cpp
@@ -847,7 +847,7 @@
 
             GrShape shape(clipPath, GrStyle::SimpleFill());
             GrPathRenderer::CanDrawPathArgs canDrawArgs;
-            canDrawArgs.fCaps = context->caps();
+            canDrawArgs.fCaps = context->contextPriv().caps();
             canDrawArgs.fClipConservativeBounds = &stencilClip.fixedClip().scissorRect();
             canDrawArgs.fViewMatrix = &SkMatrix::I();
             canDrawArgs.fShape = &shape;
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index 6d8f6cd..c447a5c 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -340,7 +340,7 @@
     if (!clip.hasWindowRectangles()) {
         isFull = !clip.scissorEnabled() ||
                  (CanClearFullscreen::kYes == canClearFullscreen &&
-                  fContext->caps()->preferFullscreenClears()) ||
+                  this->caps()->preferFullscreenClears()) ||
                  clip.scissorRect().contains(SkIRect::MakeWH(this->width(), this->height()));
     }
 
@@ -589,7 +589,7 @@
 
 int GrRenderTargetContextPriv::maxWindowRectangles() const {
     return fRenderTargetContext->fRenderTargetProxy->maxWindowRectangles(
-                                                    *fRenderTargetContext->fContext->caps());
+            *fRenderTargetContext->caps());
 }
 
 void GrRenderTargetContextPriv::clearStencilClip(const GrFixedClip& clip, bool insideStencilMask) {
@@ -917,7 +917,7 @@
 
     GrAAType aaType = this->chooseAAType(aa, GrAllowMixedSamples::kNo);
     if (GrAAType::kCoverage == aaType) {
-        const GrShaderCaps* shaderCaps = fContext->caps()->shaderCaps();
+        const GrShaderCaps* shaderCaps = this->caps()->shaderCaps();
         std::unique_ptr<GrDrawOp> op = GrOvalOpFactory::MakeRRectOp(std::move(paint),
                                                                     viewMatrix,
                                                                     rrect,
@@ -1306,7 +1306,7 @@
 
     GrAAType aaType = this->chooseAAType(aa, GrAllowMixedSamples::kNo);
     if (GrAAType::kCoverage == aaType) {
-        const GrShaderCaps* shaderCaps = fContext->caps()->shaderCaps();
+        const GrShaderCaps* shaderCaps = this->caps()->shaderCaps();
         if (auto op = GrOvalOpFactory::MakeOvalOp(std::move(paint), viewMatrix, oval, style,
                                                   shaderCaps)) {
             this->addDrawOp(clip, std::move(op));
@@ -1337,7 +1337,7 @@
 
     GrAAType aaType = this->chooseAAType(aa, GrAllowMixedSamples::kNo);
     if (GrAAType::kCoverage == aaType) {
-        const GrShaderCaps* shaderCaps = fContext->caps()->shaderCaps();
+        const GrShaderCaps* shaderCaps = this->caps()->shaderCaps();
         std::unique_ptr<GrDrawOp> op = GrOvalOpFactory::MakeArcOp(std::move(paint),
                                                                   viewMatrix,
                                                                   oval,
@@ -1563,7 +1563,7 @@
 
     GrShape shape(path, GrStyle::SimpleFill());
     GrPathRenderer::CanDrawPathArgs canDrawArgs;
-    canDrawArgs.fCaps = fRenderTargetContext->drawingManager()->getContext()->caps();
+    canDrawArgs.fCaps = fRenderTargetContext->caps();
     canDrawArgs.fViewMatrix = &viewMatrix;
     canDrawArgs.fShape = &shape;
     canDrawArgs.fClipConservativeBounds = &clipConservativeBounds;
@@ -1627,7 +1627,7 @@
                                                     : GrAllowMixedSamples::kYes;
     GrAAType aaType = this->chooseAAType(aa, allowMixedSamples);
     GrPathRenderer::CanDrawPathArgs canDrawArgs;
-    canDrawArgs.fCaps = this->drawingManager()->getContext()->caps();
+    canDrawArgs.fCaps = this->caps();
     canDrawArgs.fViewMatrix = &viewMatrix;
     canDrawArgs.fShape = &originalShape;
     canDrawArgs.fClipConservativeBounds = &clipConservativeBounds;
diff --git a/src/gpu/GrRenderTargetContext.h b/src/gpu/GrRenderTargetContext.h
index 5865da9..8d677ae 100644
--- a/src/gpu/GrRenderTargetContext.h
+++ b/src/gpu/GrRenderTargetContext.h
@@ -333,7 +333,7 @@
     void insertEventMarker(const SkString&);
 
     GrFSAAType fsaaType() const { return fRenderTargetProxy->fsaaType(); }
-    const GrCaps* caps() const { return fContext->caps(); }
+    const GrCaps* caps() const { return fContext->contextPriv().caps(); }
     int width() const { return fRenderTargetProxy->width(); }
     int height() const { return fRenderTargetProxy->height(); }
     int numColorSamples() const { return fRenderTargetProxy->numColorSamples(); }
diff --git a/src/gpu/GrShaderCaps.h b/src/gpu/GrShaderCaps.h
new file mode 100644
index 0000000..2f2d277
--- /dev/null
+++ b/src/gpu/GrShaderCaps.h
@@ -0,0 +1,315 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrShaderCaps_DEFINED
+#define GrShaderCaps_DEFINED
+
+#include "../private/GrGLSL.h"
+#include "../private/GrSwizzle.h"
+
+namespace SkSL {
+class ShaderCapsFactory;
+}
+
+struct GrContextOptions;
+class SkJSONWriter;
+
+class GrShaderCaps : public SkRefCnt {
+public:
+    /**
+     * Indicates how GLSL must interact with advanced blend equations. The KHR extension requires
+     * special layout qualifiers in the fragment shader.
+     */
+    enum AdvBlendEqInteraction {
+        kNotSupported_AdvBlendEqInteraction,     //<! No _blend_equation_advanced extension
+        kAutomatic_AdvBlendEqInteraction,        //<! No interaction required
+        kGeneralEnable_AdvBlendEqInteraction,    //<! layout(blend_support_all_equations) out
+        kSpecificEnables_AdvBlendEqInteraction,  //<! Specific layout qualifiers per equation
+
+        kLast_AdvBlendEqInteraction = kSpecificEnables_AdvBlendEqInteraction
+    };
+
+    GrShaderCaps(const GrContextOptions&);
+
+    void dumpJSON(SkJSONWriter*) const;
+
+    bool shaderDerivativeSupport() const { return fShaderDerivativeSupport; }
+    bool geometryShaderSupport() const { return fGeometryShaderSupport; }
+    bool gsInvocationsSupport() const { return fGSInvocationsSupport; }
+    bool pathRenderingSupport() const { return fPathRenderingSupport; }
+    bool dstReadInShaderSupport() const { return fDstReadInShaderSupport; }
+    bool dualSourceBlendingSupport() const { return fDualSourceBlendingSupport; }
+    bool integerSupport() const { return fIntegerSupport; }
+    bool texelBufferSupport() const { return fTexelBufferSupport; }
+    int imageLoadStoreSupport() const { return fImageLoadStoreSupport; }
+
+    /**
+     * Some helper functions for encapsulating various extensions to read FB Buffer on openglES
+     *
+     * TODO(joshualitt) On desktop opengl 4.2+ we can achieve something similar to this effect
+     */
+    bool fbFetchSupport() const { return fFBFetchSupport; }
+
+    bool fbFetchNeedsCustomOutput() const { return fFBFetchNeedsCustomOutput; }
+
+    const char* versionDeclString() const { return fVersionDeclString; }
+
+    const char* fbFetchColorName() const { return fFBFetchColorName; }
+
+    const char* fbFetchExtensionString() const { return fFBFetchExtensionString; }
+
+    bool dropsTileOnZeroDivide() const { return fDropsTileOnZeroDivide; }
+
+    bool flatInterpolationSupport() const { return fFlatInterpolationSupport; }
+
+    bool preferFlatInterpolation() const { return fPreferFlatInterpolation; }
+
+    bool noperspectiveInterpolationSupport() const { return fNoPerspectiveInterpolationSupport; }
+
+    bool externalTextureSupport() const { return fExternalTextureSupport; }
+
+    bool texelFetchSupport() const { return fTexelFetchSupport; }
+
+    bool vertexIDSupport() const { return fVertexIDSupport; }
+
+    // frexp, ldexp, etc.
+    bool fpManipulationSupport() const { return fFPManipulationSupport; }
+
+    bool floatIs32Bits() const { return fFloatIs32Bits; }
+
+    bool halfIs32Bits() const { return fHalfIs32Bits; }
+
+    AdvBlendEqInteraction advBlendEqInteraction() const { return fAdvBlendEqInteraction; }
+
+    bool mustEnableAdvBlendEqs() const {
+        return fAdvBlendEqInteraction >= kGeneralEnable_AdvBlendEqInteraction;
+    }
+
+    bool mustEnableSpecificAdvBlendEqs() const {
+        return fAdvBlendEqInteraction == kSpecificEnables_AdvBlendEqInteraction;
+    }
+
+    bool mustDeclareFragmentShaderOutput() const { return fGLSLGeneration > k110_GrGLSLGeneration; }
+
+    bool usesPrecisionModifiers() const { return fUsesPrecisionModifiers; }
+
+    // Returns whether we can use the glsl function any() in our shader code.
+    bool canUseAnyFunctionInShader() const { return fCanUseAnyFunctionInShader; }
+
+    bool canUseMinAndAbsTogether() const { return fCanUseMinAndAbsTogether; }
+
+    bool canUseFractForNegativeValues() const { return fCanUseFractForNegativeValues; }
+
+    bool mustForceNegatedAtanParamToFloat() const { return fMustForceNegatedAtanParamToFloat; }
+
+    // Returns whether a device incorrectly implements atan(y,x) as atan(y/x)
+    bool atan2ImplementedAsAtanYOverX() const { return fAtan2ImplementedAsAtanYOverX; }
+
+    // If this returns true some operation (could be a no op) must be called between floor and abs
+    // to make sure the driver compiler doesn't inline them together which can cause a driver bug in
+    // the shader.
+    bool mustDoOpBetweenFloorAndAbs() const { return fMustDoOpBetweenFloorAndAbs; }
+
+    // If false, SkSL uses a workaround so that sk_FragCoord doesn't actually query gl_FragCoord
+    bool canUseFragCoord() const { return fCanUseFragCoord; }
+
+    // If true interpolated vertex shader outputs are inaccurate.
+    bool interpolantsAreInaccurate() const { return fInterpolantsAreInaccurate; }
+
+    // If true, short ints can't represent every integer in the 16-bit two's complement range as
+    // required by the spec. SKSL will always emit full ints.
+    bool incompleteShortIntPrecision() const { return fIncompleteShortIntPrecision; }
+
+    bool requiresLocalOutputColorForFBFetch() const { return fRequiresLocalOutputColorForFBFetch; }
+
+    bool mustObfuscateUniformColor() const { return fMustObfuscateUniformColor; }
+
+    // The D3D shader compiler, when targeting PS 3.0 (ie within ANGLE) fails to compile certain
+    // constructs. See detailed comments in GrGLCaps.cpp.
+    bool mustGuardDivisionEvenAfterExplicitZeroCheck() const {
+        return fMustGuardDivisionEvenAfterExplicitZeroCheck;
+    }
+
+    // Returns the string of an extension that must be enabled in the shader to support
+    // derivatives. If nullptr is returned then no extension needs to be enabled. Before calling
+    // this function, the caller should check that shaderDerivativeSupport exists.
+    const char* shaderDerivativeExtensionString() const {
+        SkASSERT(this->shaderDerivativeSupport());
+        return fShaderDerivativeExtensionString;
+    }
+
+    // Returns the string of an extension that must be enabled in the shader to support geometry
+    // shaders. If nullptr is returned then no extension needs to be enabled. Before calling this
+    // function, the caller must verify that geometryShaderSupport exists.
+    const char* geometryShaderExtensionString() const {
+        SkASSERT(this->geometryShaderSupport());
+        return fGeometryShaderExtensionString;
+    }
+
+    // Returns the string of an extension that must be enabled in the shader to support
+    // geometry shader invocations. If nullptr is returned then no extension needs to be enabled.
+    // Before calling this function, the caller must verify that gsInvocationsSupport exists.
+    const char* gsInvocationsExtensionString() const {
+        SkASSERT(this->gsInvocationsSupport());
+        return fGSInvocationsExtensionString;
+    }
+
+    // Returns the string of an extension that will do all necessary coord transfomations needed
+    // when reading the fragment position. If such an extension does not exisits, this function
+    // returns a nullptr, and all transforms of the frag position must be done manually in the
+    // shader.
+    const char* fragCoordConventionsExtensionString() const {
+        return fFragCoordConventionsExtensionString;
+    }
+
+    // This returns the name of an extension that must be enabled in the shader, if such a thing is
+    // required in order to use a secondary output in the shader. This returns a nullptr if no such
+    // extension is required. However, the return value of this function does not say whether dual
+    // source blending is supported.
+    const char* secondaryOutputExtensionString() const { return fSecondaryOutputExtensionString; }
+
+    // This returns the name of an extension that must be enabled in the shader to support external
+    // textures. In some cases, two extensions must be enabled - the second extension is returned
+    // by secondExternalTextureExtensionString(). If that function returns nullptr, then only one
+    // extension is required.
+    const char* externalTextureExtensionString() const {
+        SkASSERT(this->externalTextureSupport());
+        return fExternalTextureExtensionString;
+    }
+
+    const char* secondExternalTextureExtensionString() const {
+        SkASSERT(this->externalTextureSupport());
+        return fSecondExternalTextureExtensionString;
+    }
+
+    const char* texelBufferExtensionString() const {
+        SkASSERT(this->texelBufferSupport());
+        return fTexelBufferExtensionString;
+    }
+
+    const char* noperspectiveInterpolationExtensionString() const {
+        SkASSERT(this->noperspectiveInterpolationSupport());
+        return fNoPerspectiveInterpolationExtensionString;
+    }
+
+    const char* imageLoadStoreExtensionString() const {
+        SkASSERT(this->imageLoadStoreSupport());
+        return fImageLoadStoreExtensionString;
+    }
+
+    int maxVertexSamplers() const { return fMaxVertexSamplers; }
+
+    int maxGeometrySamplers() const { return fMaxGeometrySamplers; }
+
+    int maxFragmentSamplers() const { return fMaxFragmentSamplers; }
+
+    int maxCombinedSamplers() const { return fMaxCombinedSamplers; }
+
+    /**
+     * In general using multiple texture units for image rendering seems to be a win at smaller
+     * sizes of dst rects and a loss at larger sizes. Dst rects above this pixel area threshold will
+     * not use multitexturing.
+     */
+    size_t disableImageMultitexturingDstRectAreaThreshold() const {
+        return fDisableImageMultitexturingDstRectAreaThreshold;
+    }
+
+    /**
+     * Given a texture's config, this determines what swizzle must be appended to accesses to the
+     * texture in generated shader code. Swizzling may be implemented in texture parameters or a
+     * sampler rather than in the shader. In this case the returned swizzle will always be "rgba".
+     */
+    const GrSwizzle& configTextureSwizzle(GrPixelConfig config) const {
+        return fConfigTextureSwizzle[config];
+    }
+
+    /** Swizzle that should occur on the fragment shader outputs for a given config. */
+    const GrSwizzle& configOutputSwizzle(GrPixelConfig config) const {
+        return fConfigOutputSwizzle[config];
+    }
+
+    GrGLSLGeneration generation() const { return fGLSLGeneration; }
+
+private:
+    void applyOptionsOverrides(const GrContextOptions& options);
+
+    GrGLSLGeneration fGLSLGeneration;
+
+    bool fShaderDerivativeSupport           : 1;
+    bool fGeometryShaderSupport             : 1;
+    bool fGSInvocationsSupport              : 1;
+    bool fPathRenderingSupport              : 1;
+    bool fDstReadInShaderSupport            : 1;
+    bool fDualSourceBlendingSupport         : 1;
+    bool fIntegerSupport                    : 1;
+    bool fTexelBufferSupport                : 1;
+    bool fImageLoadStoreSupport             : 1;
+    bool fDropsTileOnZeroDivide             : 1;
+    bool fFBFetchSupport                    : 1;
+    bool fFBFetchNeedsCustomOutput          : 1;
+    bool fUsesPrecisionModifiers            : 1;
+    bool fFlatInterpolationSupport          : 1;
+    bool fPreferFlatInterpolation           : 1;
+    bool fNoPerspectiveInterpolationSupport : 1;
+    bool fExternalTextureSupport            : 1;
+    bool fTexelFetchSupport                 : 1;
+    bool fVertexIDSupport                   : 1;
+    bool fFPManipulationSupport             : 1;
+    bool fFloatIs32Bits                     : 1;
+    bool fHalfIs32Bits                      : 1;
+
+    // Used for specific driver bug work arounds
+    bool fCanUseAnyFunctionInShader                   : 1;
+    bool fCanUseMinAndAbsTogether                     : 1;
+    bool fCanUseFractForNegativeValues                : 1;
+    bool fMustForceNegatedAtanParamToFloat            : 1;
+    bool fAtan2ImplementedAsAtanYOverX                : 1;
+    bool fMustDoOpBetweenFloorAndAbs                  : 1;
+    bool fRequiresLocalOutputColorForFBFetch          : 1;
+    bool fMustObfuscateUniformColor                   : 1;
+    bool fMustGuardDivisionEvenAfterExplicitZeroCheck : 1;
+    bool fCanUseFragCoord                             : 1;
+    bool fInterpolantsAreInaccurate                   : 1;
+    bool fIncompleteShortIntPrecision                 : 1;
+
+    const char* fVersionDeclString;
+
+    const char* fShaderDerivativeExtensionString;
+    const char* fGeometryShaderExtensionString;
+    const char* fGSInvocationsExtensionString;
+    const char* fFragCoordConventionsExtensionString;
+    const char* fSecondaryOutputExtensionString;
+    const char* fExternalTextureExtensionString;
+    const char* fSecondExternalTextureExtensionString;
+    const char* fTexelBufferExtensionString;
+    const char* fNoPerspectiveInterpolationExtensionString;
+    const char* fImageLoadStoreExtensionString;
+
+    const char* fFBFetchColorName;
+    const char* fFBFetchExtensionString;
+
+    int fMaxVertexSamplers;
+    int fMaxGeometrySamplers;
+    int fMaxFragmentSamplers;
+    int fMaxCombinedSamplers;
+
+    size_t fDisableImageMultitexturingDstRectAreaThreshold;
+
+    AdvBlendEqInteraction fAdvBlendEqInteraction;
+
+    GrSwizzle fConfigTextureSwizzle[kGrPixelConfigCnt];
+    GrSwizzle fConfigOutputSwizzle[kGrPixelConfigCnt];
+
+    friend class GrCaps;  // For initialization.
+    friend class GrGLCaps;
+    friend class GrMockCaps;
+    friend class GrMtlCaps;
+    friend class GrVkCaps;
+    friend class SkSL::ShaderCapsFactory;
+};
+
+#endif
diff --git a/src/gpu/GrSurfaceContext.cpp b/src/gpu/GrSurfaceContext.cpp
index 56d6d7d..25093cb 100644
--- a/src/gpu/GrSurfaceContext.cpp
+++ b/src/gpu/GrSurfaceContext.cpp
@@ -6,12 +6,10 @@
  */
 
 #include "GrSurfaceContext.h"
-
 #include "GrContextPriv.h"
 #include "GrDrawingManager.h"
 #include "GrOpList.h"
 #include "SkGr.h"
-
 #include "../private/GrAuditTrail.h"
 
 #define ASSERT_SINGLE_OWNER \
@@ -87,10 +85,11 @@
     SkDEBUGCODE(this->validate();)
     GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrSurfaceContext::copy");
 
-    if (!fContext->caps()->canCopySurface(this->asSurfaceProxy(), src, srcRect, dstPoint)) {
+    if (!fContext->contextPriv().caps()->canCopySurface(this->asSurfaceProxy(), src, srcRect,
+                                                        dstPoint)) {
         return false;
     }
 
-    return this->getOpList()->copySurface(*fContext->caps(),
-                                          this->asSurfaceProxy(), src, srcRect, dstPoint);
+    return this->getOpList()->copySurface(*fContext->contextPriv().caps(), this->asSurfaceProxy(),
+                                          src, srcRect, dstPoint);
 }
diff --git a/src/gpu/GrTextureAdjuster.cpp b/src/gpu/GrTextureAdjuster.cpp
index e39937d..cc61533 100644
--- a/src/gpu/GrTextureAdjuster.cpp
+++ b/src/gpu/GrTextureAdjuster.cpp
@@ -6,7 +6,6 @@
  */
 
 #include "GrTextureAdjuster.h"
-
 #include "GrColorSpaceXform.h"
 #include "GrContext.h"
 #include "GrContextPriv.h"
@@ -81,12 +80,12 @@
     if (texColorSpace) {
         *texColorSpace = sk_ref_sp(fColorSpace);
     }
-    SkASSERT(this->width() <= fContext->caps()->maxTextureSize() &&
-             this->height() <= fContext->caps()->maxTextureSize());
+    SkASSERT(this->width() <= fContext->contextPriv().caps()->maxTextureSize() &&
+             this->height() <= fContext->contextPriv().caps()->maxTextureSize());
 
-    if (!GrGpu::IsACopyNeededForTextureParams(fContext->caps(),
-                                              proxy.get(), proxy->width(), proxy->height(),
-                                              params, &copyParams, scaleAdjust)) {
+    if (!GrGpu::IsACopyNeededForTextureParams(fContext->contextPriv().caps(), proxy.get(),
+                                              proxy->width(), proxy->height(), params, &copyParams,
+                                              scaleAdjust)) {
         return proxy;
     }
 
diff --git a/src/gpu/GrTextureMaker.cpp b/src/gpu/GrTextureMaker.cpp
index b464998..58ab38d 100644
--- a/src/gpu/GrTextureMaker.cpp
+++ b/src/gpu/GrTextureMaker.cpp
@@ -17,15 +17,15 @@
                                                                  SkColorSpace* dstColorSpace,
                                                                  sk_sp<SkColorSpace>* texColorSpace,
                                                                  SkScalar scaleAdjust[2]) {
-    if (this->width() > fContext->caps()->maxTextureSize() ||
-        this->height() > fContext->caps()->maxTextureSize()) {
+    if (this->width() > fContext->contextPriv().caps()->maxTextureSize() ||
+        this->height() > fContext->contextPriv().caps()->maxTextureSize()) {
         return nullptr;
     }
 
     CopyParams copyParams;
     bool willBeMipped = params.filter() == GrSamplerState::Filter::kMipMap;
 
-    if (!fContext->caps()->mipMapSupport()) {
+    if (!fContext->contextPriv().caps()->mipMapSupport()) {
         willBeMipped = false;
     }
 
@@ -36,15 +36,15 @@
     sk_sp<GrTextureProxy> original(this->refOriginalTextureProxy(willBeMipped, dstColorSpace,
                                                                  AllowedTexGenType::kCheap));
     if (original) {
-        if (!GrGpu::IsACopyNeededForTextureParams(fContext->caps(), original.get(),
-                                                  original->width(), original->height(),
-                                                  params, &copyParams, scaleAdjust)) {
+        if (!GrGpu::IsACopyNeededForTextureParams(fContext->contextPriv().caps(), original.get(),
+                                                  original->width(), original->height(), params,
+                                                  &copyParams, scaleAdjust)) {
             return original;
         }
     } else {
-        if (!GrGpu::IsACopyNeededForTextureParams(fContext->caps(), nullptr,
-                                                  this->width(), this->height(),
-                                                  params, &copyParams, scaleAdjust)) {
+        if (!GrGpu::IsACopyNeededForTextureParams(fContext->contextPriv().caps(), nullptr,
+                                                  this->width(), this->height(), params,
+                                                  &copyParams, scaleAdjust)) {
             return this->refOriginalTextureProxy(willBeMipped, dstColorSpace,
                                                  AllowedTexGenType::kAny);
         }
diff --git a/src/gpu/GrYUVProvider.cpp b/src/gpu/GrYUVProvider.cpp
index b79d4a7..bb85aab 100644
--- a/src/gpu/GrYUVProvider.cpp
+++ b/src/gpu/GrYUVProvider.cpp
@@ -138,7 +138,7 @@
     // Otherwise, we do our shader math to go from YUV -> sRGB, manually convert sRGB -> Linear,
     // then let the HW convert Linear -> sRGB.
     if (GrPixelConfigIsSRGB(desc.fConfig)) {
-        if (ctx->caps()->srgbWriteControl()) {
+        if (ctx->contextPriv().caps()->srgbWriteControl()) {
             paint.setDisableOutputConversionToSRGB(true);
         } else {
             paint.addColorFragmentProcessor(GrSRGBEffect::Make(GrSRGBEffect::Mode::kSRGBToLinear,
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index f22fbcf..a08c0e5 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -157,7 +157,7 @@
         return nullptr;
     }
 
-    GrPixelConfig config = SkImageInfo2GrPixelConfig(origInfo, *context->caps());
+    GrPixelConfig config = SkImageInfo2GrPixelConfig(origInfo, *context->contextPriv().caps());
     if (kUnknown_GrPixelConfig == config) {
         return nullptr;
     }
@@ -769,7 +769,7 @@
     }
     samplerState.setFilterMode(textureFilterMode);
 
-    int maxTileSize = fContext->caps()->maxTileSize() - 2 * tileFilterPad;
+    int maxTileSize = this->caps()->maxTileSize() - 2 * tileFilterPad;
 
     // these are output, which we safely ignore, as we just want to know the predicate
     int outTileSize;
@@ -789,7 +789,7 @@
     SkMatrix viewMatrix;
     viewMatrix.setConcat(this->ctm(), m);
 
-    int maxTileSize = fContext->caps()->maxTileSize();
+    int maxTileSize = this->caps()->maxTileSize();
 
     // The tile code path doesn't currently support AA, so if the paint asked for aa and we could
     // draw untiled, then we bypass checking for tiling purely for optimization reasons.
@@ -821,7 +821,7 @@
         }
         samplerState.setFilterMode(textureFilterMode);
 
-        int maxTileSizeForFilter = fContext->caps()->maxTileSize() - 2 * tileFilterPad;
+        int maxTileSizeForFilter = this->caps()->maxTileSize() - 2 * tileFilterPad;
         if (this->shouldTileImageID(bitmap.getGenerationID(), bitmap.getSubset(), viewMatrix,
                                     SkMatrix::I(), samplerState, &srcRect, maxTileSizeForFilter,
                                     &tileSize, &clippedSrcRect)) {
@@ -968,11 +968,11 @@
                                  bool bicubic,
                                  bool needsTextureDomain) {
     // We should have already handled bitmaps larger than the max texture size.
-    SkASSERT(bitmap.width() <= fContext->caps()->maxTextureSize() &&
-             bitmap.height() <= fContext->caps()->maxTextureSize());
+    SkASSERT(bitmap.width() <= this->caps()->maxTextureSize() &&
+             bitmap.height() <= this->caps()->maxTextureSize());
     // We should be respecting the max tile size by the time we get here.
-    SkASSERT(bitmap.width() <= fContext->caps()->maxTileSize() &&
-             bitmap.height() <= fContext->caps()->maxTileSize());
+    SkASSERT(bitmap.width() <= this->caps()->maxTileSize() &&
+             bitmap.height() <= this->caps()->maxTileSize());
     SkASSERT(!samplerState.isRepeated());
 
     SkScalar scales[2] = {1.f, 1.f};
@@ -1148,7 +1148,7 @@
         }
     }
 
-    int maxTileSize = fContext->caps()->maxTileSize();
+    int maxTileSize = this->caps()->maxTileSize();
 
     // The tile code path doesn't currently support AA, so if the paint asked for aa and we could
     // draw untiled, then we bypass checking for tiling purely for optimization reasons.
@@ -1179,7 +1179,7 @@
         }
         sampleState.setFilterMode(textureFilterMode);
 
-        int maxTileSizeForFilter = fContext->caps()->maxTileSize() - 2 * tileFilterPad;
+        int maxTileSizeForFilter = this->caps()->maxTileSize() - 2 * tileFilterPad;
         if (this->shouldTileImageID(bitmap.getGenerationID(), bitmap.getSubset(), this->ctm(),
                                     srcToDstMatrix, sampleState, src, maxTileSizeForFilter,
                                     &tileSize, &clippedSrcRect)) {
diff --git a/src/gpu/SkGpuDevice.h b/src/gpu/SkGpuDevice.h
index f415f3d..d98dbff 100644
--- a/src/gpu/SkGpuDevice.h
+++ b/src/gpu/SkGpuDevice.h
@@ -8,16 +8,17 @@
 #ifndef SkGpuDevice_DEFINED
 #define SkGpuDevice_DEFINED
 
-#include "SkGr.h"
+#include "GrClipStackClip.h"
+#include "GrContext.h"
+#include "GrContextPriv.h"
+#include "GrRenderTargetContext.h"
+#include "GrTypes.h"
 #include "SkBitmap.h"
 #include "SkClipStackDevice.h"
+#include "SkGr.h"
 #include "SkPicture.h"
 #include "SkRegion.h"
 #include "SkSurface.h"
-#include "GrClipStackClip.h"
-#include "GrRenderTargetContext.h"
-#include "GrContext.h"
-#include "GrTypes.h"
 
 class GrAccelData;
 class GrTextureMaker;
@@ -157,6 +158,8 @@
 
     GrClipStackClip clip() const { return GrClipStackClip(&this->cs()); }
 
+    const GrCaps* caps() const { return fContext->contextPriv().caps(); }
+
     /**
      * Helper functions called by drawBitmapCommon. By the time these are called the SkDraw's
      * matrix, clip, and the device's render target has already been set on GrContext.
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index 60586ea..a37523f 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -110,7 +110,7 @@
 sk_sp<GrTextureProxy> GrCopyBaseMipMapToTextureProxy(GrContext* ctx, GrTextureProxy* baseProxy) {
     SkASSERT(baseProxy);
 
-    if (!ctx->caps()->isConfigCopyable(baseProxy->config())) {
+    if (!ctx->contextPriv().caps()->isConfigCopyable(baseProxy->config())) {
         return nullptr;
     }
 
diff --git a/src/gpu/gl/GrGLRenderTarget.cpp b/src/gpu/gl/GrGLRenderTarget.cpp
index 382457d..e996588 100644
--- a/src/gpu/gl/GrGLRenderTarget.cpp
+++ b/src/gpu/gl/GrGLRenderTarget.cpp
@@ -6,8 +6,8 @@
  */
 
 #include "GrGLRenderTarget.h"
-
 #include "GrContext.h"
+#include "GrContextPriv.h"
 #include "GrGLGpu.h"
 #include "GrGLUtil.h"
 #include "GrGpuResourcePriv.h"
@@ -187,7 +187,7 @@
 }
 
 bool GrGLRenderTarget::canAttemptStencilAttachment() const {
-    if (this->getGpu()->getContext()->caps()->avoidStencilBuffers()) {
+    if (this->getGpu()->getContext()->contextPriv().caps()->avoidStencilBuffers()) {
         return false;
     }
 
diff --git a/src/gpu/gl/GrGLTextureRenderTarget.cpp b/src/gpu/gl/GrGLTextureRenderTarget.cpp
index 049cb31..a9d7f14 100644
--- a/src/gpu/gl/GrGLTextureRenderTarget.cpp
+++ b/src/gpu/gl/GrGLTextureRenderTarget.cpp
@@ -6,8 +6,8 @@
  */
 
 #include "GrGLTextureRenderTarget.h"
-
 #include "GrContext.h"
+#include "GrContextPriv.h"
 #include "GrGLGpu.h"
 #include "GrTexturePriv.h"
 #include "SkTraceMemoryDump.h"
@@ -52,7 +52,7 @@
 bool GrGLTextureRenderTarget::canAttemptStencilAttachment() const {
     // The RT FBO of GrGLTextureRenderTarget is never created from a
     // wrapped FBO, so we only care about the flag.
-    return !this->getGpu()->getContext()->caps()->avoidStencilBuffers();
+    return !this->getGpu()->getContext()->contextPriv().caps()->avoidStencilBuffers();
 }
 
 sk_sp<GrGLTextureRenderTarget> GrGLTextureRenderTarget::MakeWrapped(
diff --git a/src/gpu/ops/GrDrawVerticesOp.cpp b/src/gpu/ops/GrDrawVerticesOp.cpp
index 329fa81..dded70a 100644
--- a/src/gpu/ops/GrDrawVerticesOp.cpp
+++ b/src/gpu/ops/GrDrawVerticesOp.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "GrDrawVerticesOp.h"
+#include "GrCaps.h"
 #include "GrDefaultGeoProcFactory.h"
 #include "GrOpFlushState.h"
 #include "SkGr.h"
@@ -374,7 +375,7 @@
     do {
        type = GrPrimitiveType(random->nextULessThan(kNumGrPrimitiveTypes));
     } while (GrPrimTypeRequiresGeometryShaderSupport(type) &&
-             !context->caps()->shaderCaps()->geometryShaderSupport());
+             !context->contextPriv().caps()->shaderCaps()->geometryShaderSupport());
 
     uint32_t primitiveCount = random->nextRangeU(1, 100);
 
diff --git a/src/gpu/text/GrAtlasTextContext.cpp b/src/gpu/text/GrAtlasTextContext.cpp
index a2e6243..a7700cb 100644
--- a/src/gpu/text/GrAtlasTextContext.cpp
+++ b/src/gpu/text/GrAtlasTextContext.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 #include "GrAtlasTextContext.h"
+#include "GrCaps.h"
 #include "GrContext.h"
 #include "GrContextPriv.h"
 #include "GrSDFMaskFilter.h"
@@ -154,8 +155,8 @@
             textBlobCache->remove(cacheBlob.get());
             cacheBlob = textBlobCache->makeCachedBlob(blob, key, blurRec, skPaint);
             this->regenerateTextBlob(cacheBlob.get(), glyphCache,
-                                     *context->caps()->shaderCaps(), paint, scalerContextFlags,
-                                     viewMatrix, props, blob, x, y, drawFilter);
+                                     *context->contextPriv().caps()->shaderCaps(), paint,
+                                     scalerContextFlags, viewMatrix, props, blob, x, y, drawFilter);
         } else {
             textBlobCache->makeMRU(cacheBlob.get());
 
@@ -165,9 +166,9 @@
                 GrTextBlobCache::BlobGlyphCount(&glyphCount, &runCount, blob);
                 sk_sp<GrAtlasTextBlob> sanityBlob(textBlobCache->makeBlob(glyphCount, runCount));
                 sanityBlob->setupKey(key, blurRec, skPaint);
-                this->regenerateTextBlob(sanityBlob.get(), glyphCache,
-                                         *context->caps()->shaderCaps(), paint, scalerContextFlags,
-                                         viewMatrix, props, blob, x, y, drawFilter);
+                this->regenerateTextBlob(
+                        sanityBlob.get(), glyphCache, *context->contextPriv().caps()->shaderCaps(),
+                        paint, scalerContextFlags, viewMatrix, props, blob, x, y, drawFilter);
                 GrAtlasTextBlob::AssertEqual(*sanityBlob, *cacheBlob);
             }
         }
@@ -178,8 +179,8 @@
             cacheBlob = textBlobCache->makeBlob(blob);
         }
         this->regenerateTextBlob(cacheBlob.get(), glyphCache,
-                                 *context->caps()->shaderCaps(), paint, scalerContextFlags,
-                                 viewMatrix, props, blob, x, y, drawFilter);
+                                 *context->contextPriv().caps()->shaderCaps(), paint,
+                                 scalerContextFlags, viewMatrix, props, blob, x, y, drawFilter);
     }
 
     cacheBlob->flush(target, props, fDistanceAdjustTable.get(), paint,
@@ -326,11 +327,10 @@
     auto textBlobCache = context->contextPriv().getTextBlobCache();
 
     GrTextUtils::Paint paint(&skPaint, &target->colorSpaceInfo());
-    sk_sp<GrAtlasTextBlob> blob(
-            this->makeDrawTextBlob(textBlobCache, glyphCache,
-                                    *context->caps()->shaderCaps(), paint,
-                                    ComputeScalerContextFlags(target->colorSpaceInfo()),
-                                    viewMatrix, props, text, byteLength, x, y));
+    sk_sp<GrAtlasTextBlob> blob(this->makeDrawTextBlob(
+            textBlobCache, glyphCache, *context->contextPriv().caps()->shaderCaps(), paint,
+            ComputeScalerContextFlags(target->colorSpaceInfo()), viewMatrix, props, text,
+            byteLength, x, y));
     if (blob) {
         blob->flush(target, props, fDistanceAdjustTable.get(), paint,
                     clip, viewMatrix, regionClipBounds, x, y);
@@ -352,8 +352,7 @@
     auto textBlobCache = context->contextPriv().getTextBlobCache();
 
     sk_sp<GrAtlasTextBlob> blob(this->makeDrawPosTextBlob(
-            textBlobCache, glyphCache,
-            *context->caps()->shaderCaps(), paint,
+            textBlobCache, glyphCache, *context->contextPriv().caps()->shaderCaps(), paint,
             ComputeScalerContextFlags(target->colorSpaceInfo()), viewMatrix, props, text,
             byteLength, pos, scalarsPerPosition, offset));
     if (blob) {
@@ -951,12 +950,10 @@
     // right now we don't handle textblobs, nor do we handle drawPosText. Since we only intend to
     // test the text op with this unit test, that is okay.
     sk_sp<GrAtlasTextBlob> blob(textContext->makeDrawTextBlob(
-                                            context->contextPriv().getTextBlobCache(), glyphCache,
-                                            *context->caps()->shaderCaps(), utilsPaint,
-                                            GrAtlasTextContext::kTextBlobOpScalerContextFlags,
-                                            viewMatrix, surfaceProps, text,
-                                            static_cast<size_t>(textLen),
-                                            SkIntToScalar(x), SkIntToScalar(y)));
+            context->contextPriv().getTextBlobCache(), glyphCache,
+            *context->contextPriv().caps()->shaderCaps(), utilsPaint,
+            GrAtlasTextContext::kTextBlobOpScalerContextFlags, viewMatrix, surfaceProps, text,
+            static_cast<size_t>(textLen), SkIntToScalar(x), SkIntToScalar(y)));
 
     return blob->test_makeOp(textLen, 0, 0, viewMatrix, x, y, utilsPaint, surfaceProps,
                              textContext->dfAdjustTable(), rtc->textTarget());
diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp
index 17a0a25..62b97ae 100644
--- a/src/image/SkImage_Gpu.cpp
+++ b/src/image/SkImage_Gpu.cpp
@@ -374,7 +374,7 @@
         return false;
     }
 
-    return ctx->caps()->validateBackendTexture(tex, ct, config);
+    return ctx->contextPriv().caps()->validateBackendTexture(tex, ct, config);
 }
 
 sk_sp<SkImage> SkImage::MakeFromTexture(GrContext* ctx,
@@ -694,7 +694,8 @@
         return nullptr;
     }
     GrPixelConfig config = kUnknown_GrPixelConfig;
-    if (!context->caps()->getConfigFromBackendFormat(backendFormat, colorType, &config)) {
+    if (!context->contextPriv().caps()->getConfigFromBackendFormat(backendFormat, colorType,
+                                                                   &config)) {
         return nullptr;
     }
 
@@ -748,12 +749,13 @@
     }
 
     // Some backends or drivers don't support (safely) moving resources between contexts
-    if (!context || !context->caps()->crossContextTextureSupport()) {
+    if (!context || !context->contextPriv().caps()->crossContextTextureSupport()) {
         return codecImage;
     }
 
-    if (limitToMaxTextureSize && (codecImage->width() > context->caps()->maxTextureSize() ||
-                                  codecImage->height() > context->caps()->maxTextureSize())) {
+    auto maxTextureSize = context->contextPriv().caps()->maxTextureSize();
+    if (limitToMaxTextureSize &&
+        (codecImage->width() > maxTextureSize || codecImage->height() > maxTextureSize)) {
         SkAutoPixmapStorage pmap;
         SkImageInfo info = as_IB(codecImage)->onImageInfo();
         if (!dstColorSpace) {
@@ -801,7 +803,7 @@
                                                    SkColorSpace* dstColorSpace,
                                                    bool limitToMaxTextureSize) {
     // Some backends or drivers don't support (safely) moving resources between contexts
-    if (!context || !context->caps()->crossContextTextureSupport()) {
+    if (!context || !context->contextPriv().caps()->crossContextTextureSupport()) {
         return SkImage::MakeRasterCopy(originalPixmap);
     }
 
@@ -814,7 +816,7 @@
 
     const SkPixmap* pixmap = &originalPixmap;
     SkAutoPixmapStorage resized;
-    int maxTextureSize = context->caps()->maxTextureSize();
+    int maxTextureSize = context->contextPriv().caps()->maxTextureSize();
     int maxDim = SkTMax(originalPixmap.width(), originalPixmap.height());
     if (limitToMaxTextureSize && maxDim > maxTextureSize) {
         float scale = static_cast<float>(maxTextureSize) / maxDim;
diff --git a/src/image/SkImage_Lazy.cpp b/src/image/SkImage_Lazy.cpp
index 035ddcc..9ca9d0d 100644
--- a/src/image/SkImage_Lazy.cpp
+++ b/src/image/SkImage_Lazy.cpp
@@ -724,7 +724,7 @@
         // may want to know what space the image data is in, so return it.
         return fInfo.refColorSpace();
     } else {
-        CachedFormat format = this->chooseCacheFormat(dstColorSpace, ctx->caps());
+        CachedFormat format = this->chooseCacheFormat(dstColorSpace, ctx->contextPriv().caps());
         SkImageInfo cacheInfo = this->buildCacheInfo(format);
         return cacheInfo.refColorSpace();
     }
@@ -759,7 +759,7 @@
 
     // Determine which cached format we're going to use (which may involve decoding to a different
     // info than the generator provides).
-    CachedFormat format = this->chooseCacheFormat(dstColorSpace, ctx->caps());
+    CachedFormat format = this->chooseCacheFormat(dstColorSpace, ctx->contextPriv().caps());
 
     // Fold the cache format into our texture key
     GrUniqueKey key;
@@ -808,7 +808,7 @@
     // 3. Ask the generator to return YUV planes, which the GPU can convert. If we will be mipping
     //    the texture we fall through here and have the CPU generate the mip maps for us.
     if (!proxy && !willBeMipped && !ctx->contextPriv().disableGpuYUVConversion()) {
-        const GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(cacheInfo, *ctx->caps());
+        const GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(cacheInfo, *ctx->contextPriv().caps());
         ScopedGenerator generator(fSharedGenerator);
         Generator_GrYUVProvider provider(generator);
 
diff --git a/src/image/SkSurface_Gpu.cpp b/src/image/SkSurface_Gpu.cpp
index c81fc28..b4e6acf 100644
--- a/src/image/SkSurface_Gpu.cpp
+++ b/src/image/SkSurface_Gpu.cpp
@@ -6,22 +6,21 @@
  */
 
 #include "SkSurface_Gpu.h"
-
 #include "GrBackendSurface.h"
+#include "GrCaps.h"
 #include "GrContextPriv.h"
 #include "GrRenderTarget.h"
 #include "GrRenderTargetContextPriv.h"
 #include "GrRenderTargetProxyPriv.h"
 #include "GrTexture.h"
-
 #include "SkCanvas.h"
 #include "SkDeferredDisplayList.h"
 #include "SkGpuDevice.h"
+#include "SkImagePriv.h"
 #include "SkImage_Base.h"
 #include "SkImage_Gpu.h"
-#include "SkImagePriv.h"
-#include "SkSurface_Base.h"
 #include "SkSurfaceCharacterization.h"
+#include "SkSurface_Base.h"
 
 #if SK_SUPPORT_GPU
 
@@ -334,7 +333,7 @@
         return nullptr;
     }
 
-    if (!SkSurface_Gpu::Valid(context->caps(), c.config(), c.colorSpace())) {
+    if (!SkSurface_Gpu::Valid(context->contextPriv().caps(), c.config(), c.colorSpace())) {
         return nullptr;
     }
 
@@ -389,7 +388,7 @@
     sampleCount = SkTMax(1, sampleCount);
     GrMipMapped mipMapped = shouldCreateWithMips ? GrMipMapped::kYes : GrMipMapped::kNo;
 
-    if (!ctx->caps()->mipMapSupport()) {
+    if (!ctx->contextPriv().caps()->mipMapSupport()) {
         mipMapped = GrMipMapped::kNo;
     }
 
@@ -433,17 +432,17 @@
         return false;
     }
 
-    if (!ctx->caps()->validateBackendTexture(tex, ct, config)) {
+    if (!ctx->contextPriv().caps()->validateBackendTexture(tex, ct, config)) {
         return false;
     }
 
     // We don't require that the client gave us an exact valid sample cnt. However, it must be
     // less than the max supported sample count and 1 if MSAA is unsupported for the color type.
-    if (!ctx->caps()->getRenderTargetSampleCount(sampleCnt, *config)) {
+    if (!ctx->contextPriv().caps()->getRenderTargetSampleCount(sampleCnt, *config)) {
         return false;
     }
 
-    if (texturable && !ctx->caps()->isConfigTexturable(*config)) {
+    if (texturable && !ctx->contextPriv().caps()->isConfigTexturable(*config)) {
         return false;
     }
     return true;
@@ -467,7 +466,7 @@
     if (!context) {
         return nullptr;
     }
-    if (!SkSurface_Gpu::Valid(context->caps(), texCopy.config(), colorSpace.get())) {
+    if (!SkSurface_Gpu::Valid(context->contextPriv().caps(), texCopy.config(), colorSpace.get())) {
         return nullptr;
     }
     sampleCnt = SkTMax(1, sampleCnt);
@@ -501,15 +500,15 @@
         return false;
     }
 
-    if (!ctx->caps()->validateBackendRenderTarget(rt, ct, config)) {
+    if (!ctx->contextPriv().caps()->validateBackendRenderTarget(rt, ct, config)) {
         return false;
     }
 
     if (rt.sampleCnt() > 1) {
-        if (ctx->caps()->maxRenderTargetSampleCount(*config) <= 1) {
+        if (ctx->contextPriv().caps()->maxRenderTargetSampleCount(*config) <= 1) {
             return false;
         }
-    } else if (!ctx->caps()->isConfigRenderable(*config)) {
+    } else if (!ctx->contextPriv().caps()->isConfigRenderable(*config)) {
         return false;
     }
 
@@ -530,7 +529,7 @@
     if (!validate_backend_render_target(context, rtCopy, &rtCopy.fConfig, colorType, colorSpace)) {
         return nullptr;
     }
-    if (!SkSurface_Gpu::Valid(context->caps(), rtCopy.config(), colorSpace.get())) {
+    if (!SkSurface_Gpu::Valid(context->contextPriv().caps(), rtCopy.config(), colorSpace.get())) {
         return nullptr;
     }
 
@@ -565,7 +564,8 @@
     if (!context) {
         return nullptr;
     }
-    if (!tex.isValid() || !SkSurface_Gpu::Valid(context->caps(), tex.config(), colorSpace.get())) {
+    if (!tex.isValid() ||
+        !SkSurface_Gpu::Valid(context->contextPriv().caps(), tex.config(), colorSpace.get())) {
         return nullptr;
     }
     sampleCnt = SkTMax(1, sampleCnt);
diff --git a/src/shaders/SkPictureShader.cpp b/src/shaders/SkPictureShader.cpp
index 42f5c3e..f958814 100644
--- a/src/shaders/SkPictureShader.cpp
+++ b/src/shaders/SkPictureShader.cpp
@@ -24,6 +24,7 @@
 #include "GrCaps.h"
 #include "GrColorSpaceInfo.h"
 #include "GrContext.h"
+#include "GrContextPriv.h"
 #include "GrFragmentProcessor.h"
 #endif
 
@@ -358,7 +359,7 @@
         const GrFPArgs& args) const {
     int maxTextureSize = 0;
     if (args.fContext) {
-        maxTextureSize = args.fContext->caps()->maxTextureSize();
+        maxTextureSize = args.fContext->contextPriv().caps()->maxTextureSize();
     }
 
     auto lm = this->totalLocalMatrix(args.fPreLocalMatrix, args.fPostLocalMatrix);
diff --git a/src/shaders/gradients/SkGradientShader.cpp b/src/shaders/gradients/SkGradientShader.cpp
index 7f597cb..60e9f2a 100644
--- a/src/shaders/gradients/SkGradientShader.cpp
+++ b/src/shaders/gradients/SkGradientShader.cpp
@@ -1268,11 +1268,12 @@
     } else {
         SkGradientShaderBase::GradientBitmapType bitmapType =
             SkGradientShaderBase::GradientBitmapType::kLegacy;
+        auto caps = args.fContext->contextPriv().caps();
         if (args.fDstColorSpace) {
             // Try to use F16 if we can
-            if (args.fContext->caps()->isConfigTexturable(kRGBA_half_GrPixelConfig)) {
+            if (caps->isConfigTexturable(kRGBA_half_GrPixelConfig)) {
                 bitmapType = SkGradientShaderBase::GradientBitmapType::kHalfFloat;
-            } else if (args.fContext->caps()->isConfigTexturable(kSRGBA_8888_GrPixelConfig)) {
+            } else if (caps->isConfigTexturable(kSRGBA_8888_GrPixelConfig)) {
                 bitmapType = SkGradientShaderBase::GradientBitmapType::kSRGB;
             } else {
                 // This can happen, but only if someone explicitly creates an unsupported
@@ -1290,7 +1291,7 @@
         desc.fWidth  = bitmap.width();
         desc.fHeight = 32;
         desc.fRowHeight = bitmap.height(); // always 1 here
-        desc.fConfig = SkImageInfo2GrPixelConfig(bitmap.info(), *args.fContext->caps());
+        desc.fConfig = SkImageInfo2GrPixelConfig(bitmap.info(), *caps);
         fAtlas = atlasManager->refAtlas(desc);
         SkASSERT(fAtlas);
 
diff --git a/src/sksl/SkSLUtil.h b/src/sksl/SkSLUtil.h
index 40a5a47..eb4fa51 100644
--- a/src/sksl/SkSLUtil.h
+++ b/src/sksl/SkSLUtil.h
@@ -16,7 +16,7 @@
 #include "SkSLString.h"
 #include "SkSLStringStream.h"
 
-#ifndef SKSL_STANDALONE
+#if !defined(SKSL_STANDALONE) && SK_SUPPORT_GPU
 #include "GrContextOptions.h"
 #include "GrShaderCaps.h"
 #endif
@@ -31,9 +31,11 @@
 #endif // SK_BUILD_FOR_WIN
 #endif // SKSL_STANDALONE
 
+class GrShaderCaps;
+
 namespace SkSL {
 
-#ifdef SKSL_STANDALONE
+#if defined(SKSL_STANDALONE) || !SK_SUPPORT_GPU
 
 // we're being compiled standalone, so we don't have access to caps...
 enum GrGLSLGeneration {