Move creation of GrProgramDesc to GrCaps-derived classes

We need a means of creating GrProgramDescs pre-flush w/o needing to know the specific backend in play.

Bug: skia:9455
Change-Id: Iac14ff8eda262ee6d2ad666c5d2e27e7a375c210
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/256698
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
diff --git a/gm/fpcoordinateoverride.cpp b/gm/fpcoordinateoverride.cpp
index 36913cc..dff429f 100644
--- a/gm/fpcoordinateoverride.cpp
+++ b/gm/fpcoordinateoverride.cpp
@@ -26,6 +26,7 @@
 #include "src/gpu/GrRenderTargetContextPriv.h"
 #include "src/gpu/effects/GrRRectEffect.h"
 #include "src/gpu/effects/GrSkSLFP.h"
+#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
 #include "src/gpu/ops/GrFillRectOp.h"
 #include "tools/Resources.h"
 #include "tools/ToolUtils.h"
diff --git a/samplecode/SampleCCPRGeometry.cpp b/samplecode/SampleCCPRGeometry.cpp
index e8c6f2e..dff8ed9 100644
--- a/samplecode/SampleCCPRGeometry.cpp
+++ b/samplecode/SampleCCPRGeometry.cpp
@@ -28,7 +28,7 @@
 #include "src/gpu/ccpr/GrVSCoverageProcessor.h"
 #include "src/gpu/geometry/GrPathUtils.h"
 #include "src/gpu/gl/GrGLGpu.h"
-#include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
+#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
 #include "src/gpu/ops/GrDrawOp.h"
 
 using TriPointInstance = GrCCCoverageProcessor::TriPointInstance;
diff --git a/src/gpu/GrCaps.h b/src/gpu/GrCaps.h
index d501edc..3c5501d 100644
--- a/src/gpu/GrCaps.h
+++ b/src/gpu/GrCaps.h
@@ -22,6 +22,8 @@
 class GrBackendTexture;
 struct GrContextOptions;
 class GrProcessorKeyBuilder;
+class GrProgramDesc;
+class GrProgramInfo;
 class GrRenderTargetProxy;
 class GrSamplerState;
 class GrSurface;
@@ -443,6 +445,8 @@
                                     const GrSamplerState&,
                                     const GrBackendFormat&) const {}
 
+    virtual GrProgramDesc makeDesc(const GrRenderTarget*, const GrProgramInfo&) const = 0;
+
 #ifdef SK_DEBUG
     // This is just a debugging entry point until we're weaned off of GrPixelConfig. It
     // should be used to verify that the pixel config from user-level code (the genericConfig)
diff --git a/src/gpu/GrDrawOpAtlas.cpp b/src/gpu/GrDrawOpAtlas.cpp
index b41d34b..e91f223b 100644
--- a/src/gpu/GrDrawOpAtlas.cpp
+++ b/src/gpu/GrDrawOpAtlas.cpp
@@ -9,6 +9,7 @@
 
 #include "include/gpu/GrContext.h"
 #include "include/gpu/GrTexture.h"
+#include "src/core/SkOpts.h"
 #include "src/gpu/GrContextPriv.h"
 #include "src/gpu/GrGpu.h"
 #include "src/gpu/GrOnFlushResourceProvider.h"
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index 377181d..5fd9ea0 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -15,7 +15,6 @@
 #include "src/gpu/GrAllocator.h"
 #include "src/gpu/GrCaps.h"
 #include "src/gpu/GrOpsRenderPass.h"
-#include "src/gpu/GrProgramDesc.h"
 #include "src/gpu/GrSamplePatternDictionary.h"
 #include "src/gpu/GrSwizzle.h"
 #include "src/gpu/GrTextureProducer.h"
diff --git a/src/gpu/GrPipeline.h b/src/gpu/GrPipeline.h
index fc41dfa..f2a7f81 100644
--- a/src/gpu/GrPipeline.h
+++ b/src/gpu/GrPipeline.h
@@ -14,7 +14,6 @@
 #include "src/gpu/GrFragmentProcessor.h"
 #include "src/gpu/GrNonAtomicRef.h"
 #include "src/gpu/GrProcessorSet.h"
-#include "src/gpu/GrProgramDesc.h"
 #include "src/gpu/GrScissorState.h"
 #include "src/gpu/GrSurfaceProxyView.h"
 #include "src/gpu/GrUserStencilSettings.h"
diff --git a/src/gpu/GrProgramDesc.h b/src/gpu/GrProgramDesc.h
index 775db62..c0cfaa8 100644
--- a/src/gpu/GrProgramDesc.h
+++ b/src/gpu/GrProgramDesc.h
@@ -11,11 +11,10 @@
 #include "include/private/GrTypesPriv.h"
 #include "include/private/SkTArray.h"
 #include "include/private/SkTo.h"
-#include "src/core/SkOpts.h"
-#include "src/gpu/GrColor.h"
-#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
 
+class GrCaps;
 class GrProgramInfo;
+class GrRenderTarget;
 class GrShaderCaps;
 
 /** This class is used to generate a generic program cache key. The Dawn, Metal and Vulkan
@@ -23,29 +22,7 @@
  */
 class GrProgramDesc {
 public:
-    // Creates an uninitialized key that must be populated by Build
-    GrProgramDesc() {}
-
-    /**
-     * Builds a program descriptor.
-     *
-     * @param desc          The built descriptor
-     * @param renderTarget  The target of the draw
-     * @param programInfo   Program information need to build the key
-     * @param caps          the caps
-     **/
-    static bool Build(GrProgramDesc*, const GrRenderTarget*, const GrProgramInfo&, const GrCaps&);
-
-    // This is strictly an OpenGL call since the other backends have additional data in their
-    // keys
-    static bool BuildFromData(GrProgramDesc* desc, const void* keyData, size_t keyLength) {
-        if (!SkTFitsIn<int>(keyLength)) {
-            return false;
-        }
-        desc->fKey.reset(SkToInt(keyLength));
-        memcpy(desc->fKey.begin(), keyData, keyLength);
-        return true;
-    }
+    bool isValid() const { return !fKey.empty(); }
 
     // Returns this as a uint32_t array to be used as a key in the program cache.
     const uint32_t* asKey() const {
@@ -89,6 +66,38 @@
     uint32_t initialKeyLength() const { return this->header().fInitialKeyLength; }
 
 protected:
+    friend class GrDawnCaps;
+    friend class GrGLCaps;
+    friend class GrMockCaps;
+    friend class GrMtlCaps;
+    friend class GrVkCaps;
+
+    friend class GrGLGpu; // for ProgramCache to access BuildFromData
+
+    // Creates an uninitialized key that must be populated by Build
+    GrProgramDesc() {}
+
+    /**
+     * Builds a program descriptor.
+     *
+     * @param desc          The built descriptor
+     * @param renderTarget  The target of the draw
+     * @param programInfo   Program information need to build the key
+     * @param caps          the caps
+     **/
+    static bool Build(GrProgramDesc*, const GrRenderTarget*, const GrProgramInfo&, const GrCaps&);
+
+    // This is strictly an OpenGL call since the other backends have additional data in their
+    // keys
+    static bool BuildFromData(GrProgramDesc* desc, const void* keyData, size_t keyLength) {
+        if (!SkTFitsIn<int>(keyLength)) {
+            return false;
+        }
+        desc->fKey.reset(SkToInt(keyLength));
+        memcpy(desc->fKey.begin(), keyData, keyLength);
+        return true;
+    }
+
     // TODO: this should be removed and converted to just data added to the key
     struct KeyHeader {
         // Set to uniquely identify any swizzling of the shader's output color(s).
diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.h b/src/gpu/ccpr/GrCCCoverageProcessor.h
index 78f8ac3..e16227b 100644
--- a/src/gpu/ccpr/GrCCCoverageProcessor.h
+++ b/src/gpu/ccpr/GrCCCoverageProcessor.h
@@ -14,6 +14,7 @@
 #include "src/gpu/GrPipeline.h"
 #include "src/gpu/GrShaderCaps.h"
 #include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
+#include "src/gpu/glsl/GrGLSLShaderBuilder.h"
 #include "src/gpu/glsl/GrGLSLVarying.h"
 
 class GrGLSLFPFragmentBuilder;
diff --git a/src/gpu/ccpr/GrGSCoverageProcessor.cpp b/src/gpu/ccpr/GrGSCoverageProcessor.cpp
index 7a4a5b1..b8b8888 100644
--- a/src/gpu/ccpr/GrGSCoverageProcessor.cpp
+++ b/src/gpu/ccpr/GrGSCoverageProcessor.cpp
@@ -8,6 +8,7 @@
 #include "src/gpu/ccpr/GrGSCoverageProcessor.h"
 
 #include "src/gpu/GrMesh.h"
+#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
 #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
 
 using InputType = GrGLSLGeometryBuilder::InputType;
diff --git a/src/gpu/ccpr/GrVSCoverageProcessor.cpp b/src/gpu/ccpr/GrVSCoverageProcessor.cpp
index 5b4f6d2..e61c76e 100644
--- a/src/gpu/ccpr/GrVSCoverageProcessor.cpp
+++ b/src/gpu/ccpr/GrVSCoverageProcessor.cpp
@@ -8,6 +8,7 @@
 #include "src/gpu/ccpr/GrVSCoverageProcessor.h"
 
 #include "src/gpu/GrMesh.h"
+#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
 #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
 
 // This class implements the coverage processor with vertex shaders.
diff --git a/src/gpu/dawn/GrDawnCaps.cpp b/src/gpu/dawn/GrDawnCaps.cpp
index a5114a7..52ff461 100644
--- a/src/gpu/dawn/GrDawnCaps.cpp
+++ b/src/gpu/dawn/GrDawnCaps.cpp
@@ -7,6 +7,12 @@
 
 #include "src/gpu/dawn/GrDawnCaps.h"
 
+#include "src/gpu/GrProgramDesc.h"
+#include "src/gpu/GrProgramInfo.h"
+#include "src/gpu/GrRenderTarget.h"
+#include "src/gpu/GrRenderTargetPriv.h"
+#include "src/gpu/GrStencilSettings.h"
+
 GrDawnCaps::GrDawnCaps(const GrContextOptions& contextOptions) : INHERITED(contextOptions) {
     fMipMapSupport = true;
     fBufferMapThreshold = SK_MaxS32;  // FIXME: get this from Dawn?
@@ -209,6 +215,53 @@
     }
 }
 
+// FIXME: taken from GrVkPipelineState; refactor.
+static uint32_t get_blend_info_key(const GrPipeline& pipeline) {
+    GrXferProcessor::BlendInfo blendInfo = pipeline.getXferProcessor().getBlendInfo();
+
+    static const uint32_t kBlendWriteShift = 1;
+    static const uint32_t kBlendCoeffShift = 5;
+    GR_STATIC_ASSERT(kLast_GrBlendCoeff < (1 << kBlendCoeffShift));
+    GR_STATIC_ASSERT(kFirstAdvancedGrBlendEquation - 1 < 4);
+
+    uint32_t key = blendInfo.fWriteColor;
+    key |= (blendInfo.fSrcBlend << kBlendWriteShift);
+    key |= (blendInfo.fDstBlend << (kBlendWriteShift + kBlendCoeffShift));
+    key |= (blendInfo.fEquation << (kBlendWriteShift + 2 * kBlendCoeffShift));
+
+    return key;
+}
+
+GrProgramDesc GrDawnCaps::makeDesc(const GrRenderTarget* rt,
+                                   const GrProgramInfo& programInfo) const {
+    GrProgramDesc desc;
+    if (!GrProgramDesc::Build(&desc, rt, programInfo, *this)) {
+        SkASSERT(!desc.isValid());
+        return desc;
+    }
+
+    wgpu::TextureFormat format;
+    if (!programInfo.backendFormat().asDawnFormat(&format)) {
+        desc.key().reset();
+        SkASSERT(!desc.isValid());
+        return desc;
+    }
+
+    GrProcessorKeyBuilder b(&desc.key());
+
+    GrStencilSettings stencil = programInfo.nonGLStencilSettings();
+    stencil.genKey(&b);
+
+    // TODO: remove this reliance on the renderTarget
+    bool hasDepthStencil = rt->renderTargetPriv().getStencilAttachment() != nullptr;
+
+    b.add32(static_cast<uint32_t>(format));
+    b.add32(static_cast<int32_t>(hasDepthStencil));
+    b.add32(get_blend_info_key(programInfo.pipeline()));
+    b.add32(static_cast<uint32_t>(programInfo.primitiveType()));
+    return desc;
+}
+
 #if GR_TEST_UTILS
 std::vector<GrCaps::TestFormatColorTypeCombination> GrDawnCaps::getTestingCombinations() const {
     std::vector<GrCaps::TestFormatColorTypeCombination> combos = {
diff --git a/src/gpu/dawn/GrDawnCaps.h b/src/gpu/dawn/GrDawnCaps.h
index 6af5d7d..7ae3cb1 100644
--- a/src/gpu/dawn/GrDawnCaps.h
+++ b/src/gpu/dawn/GrDawnCaps.h
@@ -58,6 +58,8 @@
     GrColorType getYUVAColorTypeFromBackendFormat(const GrBackendFormat&,
                                                   bool isAlphaChannel) const override;
 
+    GrProgramDesc makeDesc(const GrRenderTarget*, const GrProgramInfo&) const override;
+
 #if GR_TEST_UTILS
     std::vector<TestFormatColorTypeCombination> getTestingCombinations() const override;
 #endif
diff --git a/src/gpu/dawn/GrDawnGpu.cpp b/src/gpu/dawn/GrDawnGpu.cpp
index ba9a2ff..18aa4eb 100644
--- a/src/gpu/dawn/GrDawnGpu.cpp
+++ b/src/gpu/dawn/GrDawnGpu.cpp
@@ -66,52 +66,6 @@
 
 }
 
-// FIXME: taken from GrVkPipelineState; refactor.
-static uint32_t get_blend_info_key(const GrPipeline& pipeline) {
-    GrXferProcessor::BlendInfo blendInfo = pipeline.getXferProcessor().getBlendInfo();
-
-    static const uint32_t kBlendWriteShift = 1;
-    static const uint32_t kBlendCoeffShift = 5;
-    GR_STATIC_ASSERT(kLast_GrBlendCoeff < (1 << kBlendCoeffShift));
-    GR_STATIC_ASSERT(kFirstAdvancedGrBlendEquation - 1 < 4);
-
-    uint32_t key = blendInfo.fWriteColor;
-    key |= (blendInfo.fSrcBlend << kBlendWriteShift);
-    key |= (blendInfo.fDstBlend << (kBlendWriteShift + kBlendCoeffShift));
-    key |= (blendInfo.fEquation << (kBlendWriteShift + 2 * kBlendCoeffShift));
-
-    return key;
-}
-
-class Desc : public GrProgramDesc {
-public:
-    static bool Build(Desc* desc,
-                      GrRenderTarget* rt,
-                      const GrProgramInfo& programInfo,
-                      bool hasDepthStencil,
-                      const GrCaps& caps) {
-        if (!GrProgramDesc::Build(desc, rt, programInfo, caps)) {
-            return false;
-        }
-
-        wgpu::TextureFormat format;
-        if (!programInfo.backendFormat().asDawnFormat(&format)) {
-            return false;
-        }
-
-        GrProcessorKeyBuilder b(&desc->key());
-
-        GrStencilSettings stencil = programInfo.nonGLStencilSettings();
-        stencil.genKey(&b);
-
-        b.add32(static_cast<uint32_t>(format));
-        b.add32(static_cast<int32_t>(hasDepthStencil));
-        b.add32(get_blend_info_key(programInfo.pipeline()));
-        b.add32(static_cast<uint32_t>(programInfo.primitiveType()));
-        return true;
-    }
-};
-
 sk_sp<GrGpu> GrDawnGpu::Make(const wgpu::Device& device,
                              const GrContextOptions& options, GrContext* context) {
     if (!device) {
@@ -625,9 +579,9 @@
 sk_sp<GrDawnProgram> GrDawnGpu::getOrCreateRenderPipeline(
         GrRenderTarget* rt,
         const GrProgramInfo& programInfo) {
-    bool hasDepthStencil = rt->renderTargetPriv().getStencilAttachment() != nullptr;
-    Desc desc;
-    if (!Desc::Build(&desc, rt, programInfo, hasDepthStencil, *this->caps())) {
+
+    GrProgramDesc desc = this->caps()->makeDesc(rt, programInfo);
+    if (!desc.isValid()) {
         return nullptr;
     }
 
@@ -639,6 +593,7 @@
     SkAssertResult(programInfo.backendFormat().asDawnFormat(&colorFormat));
 
     wgpu::TextureFormat stencilFormat = wgpu::TextureFormat::Depth24PlusStencil8;
+    bool hasDepthStencil = rt->renderTargetPriv().getStencilAttachment() != nullptr;
 
     sk_sp<GrDawnProgram> program = GrDawnProgramBuilder::Build(
         this, rt, programInfo, colorFormat,
diff --git a/src/gpu/dawn/GrDawnGpu.h b/src/gpu/dawn/GrDawnGpu.h
index bd485c9..c909630 100644
--- a/src/gpu/dawn/GrDawnGpu.h
+++ b/src/gpu/dawn/GrDawnGpu.h
@@ -9,6 +9,7 @@
 #define GrDawnGpu_DEFINED
 
 #include "src/gpu/GrGpu.h"
+#include "src/gpu/GrProgramDesc.h"
 #include "dawn/dawncpp.h"
 #include "src/core/SkLRUCache.h"
 #include "src/gpu/dawn/GrDawnRingBuffer.h"
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index bf8570c..bcefe4e 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -8,6 +8,7 @@
 #include "include/gpu/GrContextOptions.h"
 #include "src/core/SkTSearch.h"
 #include "src/core/SkTSort.h"
+#include "src/gpu/GrProgramDesc.h"
 #include "src/gpu/GrRenderTargetProxyPriv.h"
 #include "src/gpu/GrShaderCaps.h"
 #include "src/gpu/GrSurfaceProxyPriv.h"
@@ -4228,6 +4229,13 @@
     return GrSwizzle::RGBA();
 }
 
+GrProgramDesc GrGLCaps::makeDesc(const GrRenderTarget* rt, const GrProgramInfo& programInfo) const {
+    GrProgramDesc desc;
+    SkDEBUGCODE(bool result =) GrProgramDesc::Build(&desc, rt, programInfo, *this);
+    SkASSERT(result == desc.isValid());
+    return desc;
+}
+
 #if GR_TEST_UTILS
 std::vector<GrCaps::TestFormatColorTypeCombination> GrGLCaps::getTestingCombinations() const {
     std::vector<GrCaps::TestFormatColorTypeCombination> combos = {
diff --git a/src/gpu/gl/GrGLCaps.h b/src/gpu/gl/GrGLCaps.h
index ca55f85..5c30c0a 100644
--- a/src/gpu/gl/GrGLCaps.h
+++ b/src/gpu/gl/GrGLCaps.h
@@ -425,6 +425,8 @@
     GrSwizzle getTextureSwizzle(const GrBackendFormat&, GrColorType) const override;
     GrSwizzle getOutputSwizzle(const GrBackendFormat&, GrColorType) const override;
 
+    GrProgramDesc makeDesc(const GrRenderTarget*, const GrProgramInfo&) const override;
+
 #if GR_TEST_UTILS
     GrGLStandard standard() const { return fStandard; }
 
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index 0ceae9c..da5e575 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -15,6 +15,7 @@
 #include "src/gpu/GrGpu.h"
 #include "src/gpu/GrMesh.h"
 #include "src/gpu/GrNativeRect.h"
+#include "src/gpu/GrProgramDesc.h"
 #include "src/gpu/GrWindowRectsState.h"
 #include "src/gpu/GrXferProcessor.h"
 #include "src/gpu/gl/GrGLContext.h"
diff --git a/src/gpu/gl/GrGLGpuProgramCache.cpp b/src/gpu/gl/GrGLGpuProgramCache.cpp
index 9eff843..c5f9f2a 100644
--- a/src/gpu/gl/GrGLGpuProgramCache.cpp
+++ b/src/gpu/gl/GrGLGpuProgramCache.cpp
@@ -50,9 +50,8 @@
                                                const GrProgramInfo& programInfo) {
     const GrCaps& caps = *gpu->caps();
 
-    GrProgramDesc desc;
-    if (!GrProgramDesc::Build(&desc, renderTarget, programInfo, caps)) {
-
+    GrProgramDesc desc = caps.makeDesc(renderTarget, programInfo);
+    if (!desc.isValid()) {
         GrCapsDebugf(gpu->caps(), "Failed to gl program descriptor!\n");
         return nullptr;
     }
diff --git a/src/gpu/glsl/GrGLSLProgramBuilder.h b/src/gpu/glsl/GrGLSLProgramBuilder.h
index 9a19cc0..1b82a0c 100644
--- a/src/gpu/glsl/GrGLSLProgramBuilder.h
+++ b/src/gpu/glsl/GrGLSLProgramBuilder.h
@@ -10,7 +10,6 @@
 
 #include "src/gpu/GrCaps.h"
 #include "src/gpu/GrGeometryProcessor.h"
-#include "src/gpu/GrProgramDesc.h"
 #include "src/gpu/GrProgramInfo.h"
 #include "src/gpu/GrRenderTarget.h"
 #include "src/gpu/GrRenderTargetPriv.h"
@@ -22,6 +21,7 @@
 #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
 #include "src/gpu/glsl/GrGLSLXferProcessor.h"
 
+class GrProgramDesc;
 class GrShaderVar;
 class GrGLSLVaryingHandler;
 class SkString;
diff --git a/src/gpu/mock/GrMockCaps.cpp b/src/gpu/mock/GrMockCaps.cpp
index 8d798fa..680e69d 100644
--- a/src/gpu/mock/GrMockCaps.cpp
+++ b/src/gpu/mock/GrMockCaps.cpp
@@ -7,6 +7,16 @@
 
 #include "src/gpu/mock/GrMockCaps.h"
 
+#include "src/gpu/GrProgramDesc.h"
+
+GrProgramDesc GrMockCaps::makeDesc(const GrRenderTarget* rt,
+                                   const GrProgramInfo& programInfo) const {
+    GrProgramDesc desc;
+    SkDEBUGCODE(bool result =) GrProgramDesc::Build(&desc, rt, programInfo, *this);
+    SkASSERT(result == desc.isValid());
+    return desc;
+}
+
 #if GR_TEST_UTILS
 std::vector<GrCaps::TestFormatColorTypeCombination> GrMockCaps::getTestingCombinations() const {
     // TODO: need to add compressed formats to this list
diff --git a/src/gpu/mock/GrMockCaps.h b/src/gpu/mock/GrMockCaps.h
index e25bc51..b386571 100644
--- a/src/gpu/mock/GrMockCaps.h
+++ b/src/gpu/mock/GrMockCaps.h
@@ -143,6 +143,8 @@
         return GrSwizzle();
     }
 
+    GrProgramDesc makeDesc(const GrRenderTarget*, const GrProgramInfo&) const override;
+
 #if GR_TEST_UTILS
     std::vector<GrCaps::TestFormatColorTypeCombination> getTestingCombinations() const override;
 #endif
diff --git a/src/gpu/mtl/GrMtlCaps.h b/src/gpu/mtl/GrMtlCaps.h
index da3d1a7..9962559 100644
--- a/src/gpu/mtl/GrMtlCaps.h
+++ b/src/gpu/mtl/GrMtlCaps.h
@@ -90,6 +90,8 @@
     GrSwizzle getTextureSwizzle(const GrBackendFormat&, GrColorType) const override;
     GrSwizzle getOutputSwizzle(const GrBackendFormat&, GrColorType) const override;
 
+    GrProgramDesc makeDesc(const GrRenderTarget*, const GrProgramInfo&) const override;
+
 #if GR_TEST_UTILS
     std::vector<TestFormatColorTypeCombination> getTestingCombinations() const override;
 #endif
diff --git a/src/gpu/mtl/GrMtlCaps.mm b/src/gpu/mtl/GrMtlCaps.mm
index c8bb936..a5fbdef 100644
--- a/src/gpu/mtl/GrMtlCaps.mm
+++ b/src/gpu/mtl/GrMtlCaps.mm
@@ -9,7 +9,11 @@
 
 #include "include/core/SkRect.h"
 #include "include/gpu/GrBackendSurface.h"
+#include "src/gpu/GrProcessor.h"
+#include "src/gpu/GrProgramDesc.h"
+#include "src/gpu/GrProgramInfo.h"
 #include "src/gpu/GrRenderTarget.h"
+#include "src/gpu/GrRenderTargetPriv.h"
 #include "src/gpu/GrRenderTargetProxy.h"
 #include "src/gpu/GrShaderCaps.h"
 #include "src/gpu/GrSurfaceProxy.h"
@@ -1095,6 +1099,52 @@
     return {GrColorType::kUnknown, 0};
 }
 
+/**
+ * For Metal we want to cache the entire pipeline for reuse of draws. The Desc here holds all
+ * the information needed to differentiate one pipeline from another.
+ *
+ * The GrProgramDesc contains all the information need to create the actual shaders for the
+ * pipeline.
+ *
+ * For Metal we need to add to the GrProgramDesc to include the rest of the state on the
+ * pipeline. This includes blending information and primitive type. The pipeline is immutable
+ * so any remaining dynamic state is set via the MtlRenderCmdEncoder.
+ */
+GrProgramDesc GrMtlCaps::makeDesc(const GrRenderTarget* rt,
+                                  const GrProgramInfo& programInfo) const {
+
+    GrProgramDesc desc;
+    if (!GrProgramDesc::Build(&desc, rt, programInfo, *this)) {
+        SkASSERT(!desc.isValid());
+        return desc;
+    }
+
+    GrProcessorKeyBuilder b(&desc.key());
+
+    b.add32(programInfo.backendFormat().asMtlFormat());
+
+    b.add32(programInfo.numRasterSamples());
+
+#ifdef SK_DEBUG
+    if (rt && programInfo.pipeline().isStencilEnabled()) {
+        SkASSERT(rt->renderTargetPriv().getStencilAttachment());
+    }
+#endif
+
+    b.add32(programInfo.pipeline().isStencilEnabled()
+                                 ? this->preferredStencilFormat().fInternalFormat
+                                 : MTLPixelFormatInvalid);
+    b.add32((uint32_t)programInfo.pipeline().isStencilEnabled());
+    // Stencil samples don't seem to be tracked in the MTLRenderPipeline
+
+    programInfo.pipeline().genKey(&b, *this);
+
+    b.add32((uint32_t)programInfo.primitiveType());
+
+    return desc;
+}
+
+
 #if GR_TEST_UTILS
 std::vector<GrCaps::TestFormatColorTypeCombination> GrMtlCaps::getTestingCombinations() const {
     std::vector<GrCaps::TestFormatColorTypeCombination> combos = {
diff --git a/src/gpu/mtl/GrMtlPipelineStateBuilder.h b/src/gpu/mtl/GrMtlPipelineStateBuilder.h
index 829f5aa..bfa0c45 100644
--- a/src/gpu/mtl/GrMtlPipelineStateBuilder.h
+++ b/src/gpu/mtl/GrMtlPipelineStateBuilder.h
@@ -9,7 +9,6 @@
 #define GrMtlPipelineStateBuilder_DEFINED
 
 #include "src/gpu/GrPipeline.h"
-#include "src/gpu/GrProgramDesc.h"
 #include "src/gpu/glsl/GrGLSLProgramBuilder.h"
 #include "src/gpu/mtl/GrMtlUniformHandler.h"
 #include "src/gpu/mtl/GrMtlVaryingHandler.h"
@@ -17,6 +16,7 @@
 
 #import <Metal/Metal.h>
 
+class GrProgramDesc;
 class GrProgramInfo;
 class GrMtlCaps;
 class GrMtlGpu;
@@ -24,24 +24,6 @@
 
 class GrMtlPipelineStateBuilder : public GrGLSLProgramBuilder {
 public:
-    /**
-     * For Metal we want to cache the entire pipeline for reuse of draws. The Desc here holds all
-     * the information needed to differentiate one pipeline from another.
-     *
-     * The GrProgramDesc contains all the information need to create the actual shaders for the
-     * pipeline.
-     *
-     * For Metal we need to add to the GrProgramDesc to include the rest of the state on the
-     * pipeline. This includes blending information and primitive type. The pipeline is immutable
-     * so any remaining dynamic state is set via the MtlRenderCmdEncoder.
-     */
-    class Desc : public GrProgramDesc {
-    public:
-        static bool Build(Desc*, GrRenderTarget*, const GrProgramInfo&, const GrMtlCaps&);
-    private:
-        typedef GrProgramDesc INHERITED;
-    };
-
     /** Generates a pipeline state.
      *
      * The GrMtlPipelineState implements what is specified in the GrPipeline and
@@ -53,12 +35,12 @@
     static GrMtlPipelineState* CreatePipelineState(GrMtlGpu*,
                                                    GrRenderTarget*,
                                                    const GrProgramInfo&,
-                                                   Desc*);
+                                                   GrProgramDesc*);
 
 private:
     GrMtlPipelineStateBuilder(GrMtlGpu*, GrRenderTarget*, const GrProgramInfo&, GrProgramDesc*);
 
-    GrMtlPipelineState* finalize(GrRenderTarget*, const GrProgramInfo&, Desc*);
+    GrMtlPipelineState* finalize(GrRenderTarget*, const GrProgramInfo&, GrProgramDesc*);
 
     const GrCaps* caps() const override;
 
diff --git a/src/gpu/mtl/GrMtlPipelineStateBuilder.mm b/src/gpu/mtl/GrMtlPipelineStateBuilder.mm
index 83eb7da..dc98db2 100644
--- a/src/gpu/mtl/GrMtlPipelineStateBuilder.mm
+++ b/src/gpu/mtl/GrMtlPipelineStateBuilder.mm
@@ -26,7 +26,7 @@
 GrMtlPipelineState* GrMtlPipelineStateBuilder::CreatePipelineState(GrMtlGpu* gpu,
                                                                    GrRenderTarget* renderTarget,
                                                                    const GrProgramInfo& programInfo,
-                                                                   Desc* desc) {
+                                                                   GrProgramDesc* desc) {
     GrAutoLocaleSetter als("C");
     GrMtlPipelineStateBuilder builder(gpu, renderTarget, programInfo, desc);
 
@@ -341,7 +341,7 @@
 
 GrMtlPipelineState* GrMtlPipelineStateBuilder::finalize(GrRenderTarget* renderTarget,
                                                         const GrProgramInfo& programInfo,
-                                                        Desc* desc) {
+                                                        GrProgramDesc* desc) {
     auto pipelineDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
 
     fVS.extensions().appendf("#extension GL_ARB_separate_shader_objects : enable\n");
@@ -450,35 +450,3 @@
 
 //////////////////////////////////////////////////////////////////////////////
 
-bool GrMtlPipelineStateBuilder::Desc::Build(Desc* desc,
-                                            GrRenderTarget* renderTarget,
-                                            const GrProgramInfo& programInfo,
-                                            const GrMtlCaps& caps) {
-    if (!GrProgramDesc::Build(desc, renderTarget, programInfo, caps)) {
-        return false;
-    }
-
-    GrProcessorKeyBuilder b(&desc->key());
-
-    b.add32(programInfo.backendFormat().asMtlFormat());
-
-    b.add32(programInfo.numRasterSamples());
-
-#ifdef SK_DEBUG
-    if (renderTarget && programInfo.pipeline().isStencilEnabled()) {
-        SkASSERT(renderTarget->renderTargetPriv().getStencilAttachment());
-    }
-#endif
-
-    b.add32(programInfo.pipeline().isStencilEnabled()
-                                 ? caps.preferredStencilFormat().fInternalFormat
-                                 : MTLPixelFormatInvalid);
-    b.add32((uint32_t)programInfo.pipeline().isStencilEnabled());
-    // Stencil samples don't seem to be tracked in the MTLRenderPipeline
-
-    programInfo.pipeline().genKey(&b, caps);
-
-    b.add32((uint32_t)programInfo.primitiveType());
-
-    return true;
-}
diff --git a/src/gpu/mtl/GrMtlResourceProvider.h b/src/gpu/mtl/GrMtlResourceProvider.h
index c542145..e568fd3 100644
--- a/src/gpu/mtl/GrMtlResourceProvider.h
+++ b/src/gpu/mtl/GrMtlResourceProvider.h
@@ -11,6 +11,7 @@
 #include "include/private/SkSpinlock.h"
 #include "include/private/SkTArray.h"
 #include "src/core/SkLRUCache.h"
+#include "src/gpu/GrProgramDesc.h"
 #include "src/gpu/mtl/GrMtlDepthStencil.h"
 #include "src/gpu/mtl/GrMtlPipelineStateBuilder.h"
 #include "src/gpu/mtl/GrMtlSampler.h"
@@ -62,9 +63,9 @@
             }
         };
 
-        SkLRUCache<const GrMtlPipelineStateBuilder::Desc, std::unique_ptr<Entry>, DescHash> fMap;
+        SkLRUCache<const GrProgramDesc, std::unique_ptr<Entry>, DescHash> fMap;
 
-        GrMtlGpu*                    fGpu;
+        GrMtlGpu*                   fGpu;
 
 #ifdef GR_PIPELINE_STATE_CACHE_STATS
         int                         fTotalRequests;
diff --git a/src/gpu/mtl/GrMtlResourceProvider.mm b/src/gpu/mtl/GrMtlResourceProvider.mm
index bb5b75f..89be009 100644
--- a/src/gpu/mtl/GrMtlResourceProvider.mm
+++ b/src/gpu/mtl/GrMtlResourceProvider.mm
@@ -141,10 +141,8 @@
 
     const GrMtlCaps& caps = fGpu->mtlCaps();
 
-    // TODO: unify GL, VK and Mtl
-    // Get GrMtlProgramDesc
-    GrMtlPipelineStateBuilder::Desc desc;
-    if (!GrMtlPipelineStateBuilder::Desc::Build(&desc, renderTarget, programInfo, caps)) {
+    GrProgramDesc desc = caps.makeDesc(renderTarget, programInfo);
+    if (!desc.isValid()) {
         GrCapsDebugf(fGpu->caps(), "Failed to build mtl program descriptor!\n");
         return nullptr;
     }
diff --git a/src/gpu/ops/GrLatticeOp.cpp b/src/gpu/ops/GrLatticeOp.cpp
index 1728149..aac4dd6 100644
--- a/src/gpu/ops/GrLatticeOp.cpp
+++ b/src/gpu/ops/GrLatticeOp.cpp
@@ -18,6 +18,7 @@
 #include "src/gpu/GrVertexWriter.h"
 #include "src/gpu/SkGr.h"
 #include "src/gpu/glsl/GrGLSLColorSpaceXformHelper.h"
+#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
 #include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
 #include "src/gpu/glsl/GrGLSLVarying.h"
 #include "src/gpu/ops/GrLatticeOp.h"
diff --git a/src/gpu/ops/GrMeshDrawOp.h b/src/gpu/ops/GrMeshDrawOp.h
index 3025150..1922266 100644
--- a/src/gpu/ops/GrMeshDrawOp.h
+++ b/src/gpu/ops/GrMeshDrawOp.h
@@ -8,6 +8,7 @@
 #ifndef GrMeshDrawOp_DEFINED
 #define GrMeshDrawOp_DEFINED
 
+#include "src/core/SkArenaAlloc.h"
 #include "src/gpu/GrAppliedClip.h"
 #include "src/gpu/GrGeometryProcessor.h"
 #include "src/gpu/GrMesh.h"
diff --git a/src/gpu/vk/GrVkCaps.cpp b/src/gpu/vk/GrVkCaps.cpp
index 7579c49..90fbd4f 100644
--- a/src/gpu/vk/GrVkCaps.cpp
+++ b/src/gpu/vk/GrVkCaps.cpp
@@ -8,13 +8,17 @@
 #include "include/gpu/GrBackendSurface.h"
 #include "include/gpu/vk/GrVkBackendContext.h"
 #include "include/gpu/vk/GrVkExtensions.h"
+#include "src/gpu/GrProgramDesc.h"
 #include "src/gpu/GrRenderTarget.h"
 #include "src/gpu/GrRenderTargetProxy.h"
 #include "src/gpu/GrShaderCaps.h"
+#include "src/gpu/GrStencilSettings.h"
 #include "src/gpu/GrUtil.h"
 #include "src/gpu/SkGr.h"
 #include "src/gpu/vk/GrVkCaps.h"
+#include "src/gpu/vk/GrVkGpu.h"
 #include "src/gpu/vk/GrVkInterface.h"
+#include "src/gpu/vk/GrVkRenderTarget.h"
 #include "src/gpu/vk/GrVkTexture.h"
 #include "src/gpu/vk/GrVkUniformHandler.h"
 #include "src/gpu/vk/GrVkUtil.h"
@@ -1745,6 +1749,58 @@
     memcpy(tmp, &key, sizeof(key));
 }
 
+/**
+ * For Vulkan we want to cache the entire VkPipeline for reuse of draws. The Desc here holds all
+ * the information needed to differentiate one pipeline from another.
+ *
+ * The GrProgramDesc contains all the information need to create the actual shaders for the
+ * pipeline.
+ *
+ * For Vulkan we need to add to the GrProgramDesc to include the rest of the state on the
+ * pipline. This includes stencil settings, blending information, render pass format, draw face
+ * information, and primitive type. Note that some state is set dynamically on the pipeline for
+ * each draw  and thus is not included in this descriptor. This includes the viewport, scissor,
+ * and blend constant.
+ */
+GrProgramDesc GrVkCaps::makeDesc(const GrRenderTarget* rt, const GrProgramInfo& programInfo) const {
+    GrProgramDesc desc;
+    if (!GrProgramDesc::Build(&desc, rt, programInfo, *this)) {
+        SkASSERT(!desc.isValid());
+        return desc;
+    }
+
+    GrProcessorKeyBuilder b(&desc.key());
+
+    // This will become part of the sheared off key used to persistently cache
+    // the SPIRV code. It needs to be added right after the base key so that,
+    // when the base-key is sheared off, the shearing code can include it in the
+    // reduced key (c.f. the +4s in the SkData::MakeWithCopy calls in
+    // GrVkPipelineStateBuilder.cpp).
+    b.add32(GrVkGpu::kShader_PersistentCacheKeyType);
+
+    GrVkRenderTarget* vkRT = (GrVkRenderTarget*) rt;
+    // TODO: support failure in getSimpleRenderPass
+    SkASSERT(vkRT->getSimpleRenderPass());
+    vkRT->getSimpleRenderPass()->genKey(&b);
+
+    GrStencilSettings stencil = programInfo.nonGLStencilSettings();
+    stencil.genKey(&b);
+
+    programInfo.pipeline().genKey(&b, *this);
+    b.add32(programInfo.numRasterSamples());
+
+    // Vulkan requires the full primitive type as part of its key
+    b.add32((uint32_t)programInfo.primitiveType());
+
+    if (this->mixedSamplesSupport()) {
+        // Add "0" to indicate that coverage modulation will not be enabled, or the (non-zero)
+        // raster sample count if it will.
+        b.add32(!programInfo.isMixedSampled() ? 0 : programInfo.numRasterSamples());
+    }
+
+    return desc;
+}
+
 #if GR_TEST_UTILS
 std::vector<GrCaps::TestFormatColorTypeCombination> GrVkCaps::getTestingCombinations() const {
     std::vector<GrCaps::TestFormatColorTypeCombination> combos = {
diff --git a/src/gpu/vk/GrVkCaps.h b/src/gpu/vk/GrVkCaps.h
index b0ced01..38fb367 100644
--- a/src/gpu/vk/GrVkCaps.h
+++ b/src/gpu/vk/GrVkCaps.h
@@ -188,6 +188,8 @@
                             const GrSamplerState&,
                             const GrBackendFormat&) const override;
 
+    GrProgramDesc makeDesc(const GrRenderTarget*, const GrProgramInfo&) const override;
+
 #if GR_TEST_UTILS
     std::vector<TestFormatColorTypeCombination> getTestingCombinations() const override;
 #endif
diff --git a/src/gpu/vk/GrVkPipelineStateBuilder.cpp b/src/gpu/vk/GrVkPipelineStateBuilder.cpp
index 736f004..41dca29 100644
--- a/src/gpu/vk/GrVkPipelineStateBuilder.cpp
+++ b/src/gpu/vk/GrVkPipelineStateBuilder.cpp
@@ -24,7 +24,7 @@
         GrVkGpu* gpu,
         GrRenderTarget* renderTarget,
         const GrProgramInfo& programInfo,
-        Desc* desc,
+        GrProgramDesc* desc,
         VkRenderPass compatibleRenderPass) {
     // ensure that we use "." as a decimal separator when creating SkSL code
     GrAutoLocaleSetter als("C");
@@ -66,7 +66,7 @@
                                                     VkShaderModule* shaderModule,
                                                     VkPipelineShaderStageCreateInfo* stageInfo,
                                                     const SkSL::Program::Settings& settings,
-                                                    Desc* desc,
+                                                    GrProgramDesc* desc,
                                                     SkSL::String* outSPIRV,
                                                     SkSL::Program::Inputs* outInputs) {
     if (!GrCompileVkShaderModule(fGpu, sksl, stage, shaderModule,
@@ -135,13 +135,14 @@
 void GrVkPipelineStateBuilder::storeShadersInCache(const SkSL::String shaders[],
                                                    const SkSL::Program::Inputs inputs[],
                                                    bool isSkSL) {
-    const Desc* desc = static_cast<const Desc*>(this->desc());
     // Here we shear off the Vk-specific portion of the Desc in order to create the
     // persistent key. This is bc Vk only caches the SPIRV code, not the fully compiled
     // program, and that only depends on the base GrProgramDesc data.
     // The +4 is to include the kShader_PersistentCacheKeyType code the Vulkan backend adds
     // to the key right after the base key.
-    sk_sp<SkData> key = SkData::MakeWithoutCopy(desc->asKey(), desc->initialKeyLength()+4);
+    sk_sp<SkData> key = SkData::MakeWithoutCopy(this->desc()->asKey(),
+                                                this->desc()->initialKeyLength()+4);
+
     sk_sp<SkData> data = GrPersistentCacheUtils::PackCachedShaders(isSkSL ? kSKSL_Tag : kSPIRV_Tag,
                                                                    shaders,
                                                                    inputs, kGrShaderTypeCount);
@@ -149,7 +150,7 @@
 }
 
 GrVkPipelineState* GrVkPipelineStateBuilder::finalize(VkRenderPass compatibleRenderPass,
-                                                      Desc* desc) {
+                                                      GrProgramDesc* desc) {
     VkDescriptorSetLayout dsLayout[2];
     VkPipelineLayout pipelineLayout;
     VkShaderModule shaderModules[kGrShaderTypeCount] = { VK_NULL_HANDLE,
@@ -328,45 +329,3 @@
                                  std::move(fFragmentProcessors),
                                  fFragmentProcessorCnt);
 }
-
-//////////////////////////////////////////////////////////////////////////////
-
-bool GrVkPipelineStateBuilder::Desc::Build(Desc* desc,
-                                           GrRenderTarget* renderTarget,
-                                           const GrProgramInfo& programInfo,
-                                           const GrCaps& caps) {
-    if (!GrProgramDesc::Build(desc, renderTarget, programInfo, caps)) {
-        return false;
-    }
-
-    GrProcessorKeyBuilder b(&desc->key());
-
-    // This will become part of the sheared off key used to persistently cache
-    // the SPIRV code. It needs to be added right after the base key so that,
-    // when the base-key is sheared off, the shearing code can include it in the
-    // reduced key (c.f. the +4s in the SkData::MakeWithCopy calls in
-    // GrVkPipelineStateBuilder.cpp).
-    b.add32(GrVkGpu::kShader_PersistentCacheKeyType);
-
-    GrVkRenderTarget* vkRT = (GrVkRenderTarget*)renderTarget;
-    // TODO: support failure in getSimpleRenderPass
-    SkASSERT(vkRT->getSimpleRenderPass());
-    vkRT->getSimpleRenderPass()->genKey(&b);
-
-    GrStencilSettings stencil = programInfo.nonGLStencilSettings();
-    stencil.genKey(&b);
-
-    programInfo.pipeline().genKey(&b, caps);
-    b.add32(programInfo.numRasterSamples());
-
-    // Vulkan requires the full primitive type as part of its key
-    b.add32((uint32_t)programInfo.primitiveType());
-
-    if (caps.mixedSamplesSupport()) {
-        // Add "0" to indicate that coverage modulation will not be enabled, or the (non-zero)
-        // raster sample count if it will.
-        b.add32(!programInfo.isMixedSampled() ? 0 : programInfo.numRasterSamples());
-    }
-
-    return true;
-}
diff --git a/src/gpu/vk/GrVkPipelineStateBuilder.h b/src/gpu/vk/GrVkPipelineStateBuilder.h
index 0932654..b09b939 100644
--- a/src/gpu/vk/GrVkPipelineStateBuilder.h
+++ b/src/gpu/vk/GrVkPipelineStateBuilder.h
@@ -10,52 +10,31 @@
 
 #include "include/gpu/vk/GrVkTypes.h"
 #include "src/gpu/GrPipeline.h"
-#include "src/gpu/GrProgramDesc.h"
 #include "src/gpu/glsl/GrGLSLProgramBuilder.h"
 #include "src/gpu/vk/GrVkPipelineState.h"
 #include "src/gpu/vk/GrVkUniformHandler.h"
 #include "src/gpu/vk/GrVkVaryingHandler.h"
 #include "src/sksl/SkSLCompiler.h"
 
+class GrProgramDesc;
 class GrVkGpu;
 class GrVkRenderPass;
 class SkReader32;
 
 class GrVkPipelineStateBuilder : public GrGLSLProgramBuilder {
 public:
-    /**
-     * For Vulkan we want to cache the entire VkPipeline for reuse of draws. The Desc here holds all
-     * the information needed to differentiate one pipeline from another.
-     *
-     * The GrProgramDesc contains all the information need to create the actual shaders for the
-     * pipeline.
-     *
-     * For Vulkan we need to add to the GrProgramDesc to include the rest of the state on the
-     * pipline. This includes stencil settings, blending information, render pass format, draw face
-     * information, and primitive type. Note that some state is set dynamically on the pipeline for
-     * each draw  and thus is not included in this descriptor. This includes the viewport, scissor,
-     * and blend constant.
-     */
-    class Desc : public GrProgramDesc {
-    public:
-        static bool Build(Desc*, GrRenderTarget*, const GrProgramInfo&, const GrCaps&);
-
-    private:
-        typedef GrProgramDesc INHERITED;
-    };
-
     /** Generates a pipeline state.
-    *
-    * The GrVkPipelineState implements what is specified in the GrPipeline and GrPrimitiveProcessor
-    * as input. After successful generation, the builder result objects are available to be used.
-    * This function may modify the program key by setting the surface origin key to 0 (unspecified)
-    * if it turns out the program does not care about the surface origin.
-    * @return true if generation was successful.
-    */
+     *
+     * The GrVkPipelineState implements what is specified in the GrPipeline and GrPrimitiveProcessor
+     * as input. After successful generation, the builder result objects are available to be used.
+     * This function may modify the program key by setting the surface origin key to 0 (unspecified)
+     * if it turns out the program does not care about the surface origin.
+     * @return true if generation was successful.
+     */
     static GrVkPipelineState* CreatePipelineState(GrVkGpu*,
                                                   GrRenderTarget*,
                                                   const GrProgramInfo&,
-                                                  Desc*,
+                                                  GrProgramDesc*,
                                                   VkRenderPass compatibleRenderPass);
 
     const GrCaps* caps() const override;
@@ -68,7 +47,7 @@
 private:
     GrVkPipelineStateBuilder(GrVkGpu*, GrRenderTarget*, const GrProgramInfo&, GrProgramDesc*);
 
-    GrVkPipelineState* finalize(VkRenderPass compatibleRenderPass, Desc*);
+    GrVkPipelineState* finalize(VkRenderPass compatibleRenderPass, GrProgramDesc*);
 
     // returns number of shader stages
     int loadShadersFromCache(SkReader32* cached, VkShaderModule outShaderModules[],
@@ -82,7 +61,7 @@
                               VkShaderModule* shaderModule,
                               VkPipelineShaderStageCreateInfo* stageInfo,
                               const SkSL::Program::Settings& settings,
-                              Desc* desc,
+                              GrProgramDesc* desc,
                               SkSL::String* outSPIRV,
                               SkSL::Program::Inputs* outInputs);
 
diff --git a/src/gpu/vk/GrVkPipelineStateCache.cpp b/src/gpu/vk/GrVkPipelineStateCache.cpp
index 4e7d3b16..6081643 100644
--- a/src/gpu/vk/GrVkPipelineStateCache.cpp
+++ b/src/gpu/vk/GrVkPipelineStateCache.cpp
@@ -91,10 +91,8 @@
     }
 #endif
 
-    // TODO: can this be unified between GL, Vk and Mtl?
-    // Get GrVkProgramDesc
-    GrVkPipelineStateBuilder::Desc desc;
-    if (!GrVkPipelineStateBuilder::Desc::Build(&desc, renderTarget, programInfo, *fGpu->caps())) {
+    GrProgramDesc desc = fGpu->caps()->makeDesc(renderTarget, programInfo);
+    if (!desc.isValid()) {
         GrCapsDebugf(fGpu->caps(), "Failed to build vk program descriptor!\n");
         return nullptr;
     }
diff --git a/src/gpu/vk/GrVkResourceProvider.h b/src/gpu/vk/GrVkResourceProvider.h
index ea919c9..6cdbda8 100644
--- a/src/gpu/vk/GrVkResourceProvider.h
+++ b/src/gpu/vk/GrVkResourceProvider.h
@@ -13,6 +13,7 @@
 #include "src/core/SkLRUCache.h"
 #include "src/core/SkTDynamicHash.h"
 #include "src/core/SkTInternalLList.h"
+#include "src/gpu/GrProgramDesc.h"
 #include "src/gpu/GrResourceHandle.h"
 #include "src/gpu/vk/GrVkDescriptorPool.h"
 #include "src/gpu/vk/GrVkDescriptorSetManager.h"
@@ -202,7 +203,7 @@
             }
         };
 
-        SkLRUCache<const GrVkPipelineStateBuilder::Desc, std::unique_ptr<Entry>, DescHash> fMap;
+        SkLRUCache<const GrProgramDesc, std::unique_ptr<Entry>, DescHash> fMap;
 
         GrVkGpu*                    fGpu;