Refactor creation of GrVkRenderPasses to make them move general

This change is a refactorization to open up more flexable use for render passes
in the future. Once we start bundling draw calls into a single render pass we
will need to start using specific load and store ops instead of the default
currently of load and store everything.

We still will need to simply get a compatible render pass for creation of framebuffers
and pipeline objects. These render passes don't need to know load and store ops. Thus
in this change, I'm defaulting the "compatible" render pass to always be the one
which both loads and stores. All other load/store combinations will be created lazily
when requested for a specific draw (future change).

The CompatibleRPHandle is a way for us to avoid analysing the RenderTarget every time
we want to get a new GrVkRenderPass.

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

Review-Url: https://codereview.chromium.org/1977403002
diff --git a/src/gpu/vk/GrVkRenderPass.cpp b/src/gpu/vk/GrVkRenderPass.cpp
index 1aedab4..c56bafa 100644
--- a/src/gpu/vk/GrVkRenderPass.cpp
+++ b/src/gpu/vk/GrVkRenderPass.cpp
@@ -13,17 +13,31 @@
 #include "GrVkRenderTarget.h"
 #include "GrVkUtil.h"
 
-void setup_simple_vk_attachment_description(VkAttachmentDescription* attachment,
-                                            VkFormat format,
-                                            uint32_t samples,
-                                            VkImageLayout layout) {
+typedef GrVkRenderPass::AttachmentsDescriptor::AttachmentDesc AttachmentDesc;
+
+void setup_vk_attachment_description(VkAttachmentDescription* attachment,
+                                     const AttachmentDesc& desc,
+                                     VkImageLayout layout) {
     attachment->flags = 0;
-    attachment->format = format;
-    SkAssertResult(GrSampleCountToVkSampleCount(samples, &attachment->samples));
-    attachment->loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
-    attachment->storeOp = VK_ATTACHMENT_STORE_OP_STORE;
-    attachment->stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
-    attachment->stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
+    attachment->format = desc.fFormat;
+    SkAssertResult(GrSampleCountToVkSampleCount(desc.fSamples, &attachment->samples));
+    switch (layout) {
+        case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
+            attachment->loadOp = desc.fLoadOp;
+            attachment->storeOp = desc.fStoreOp;
+            attachment->stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+            attachment->stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+            break;
+        case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
+            attachment->loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+            attachment->storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+            attachment->stencilLoadOp = desc.fLoadOp;
+            attachment->stencilStoreOp = desc.fStoreOp;
+            break;
+        default:
+            SkFAIL("Unexpected attachment layout");
+    }
+
     attachment->initialLayout = layout;
     attachment->finalLayout = layout;
 }
@@ -56,10 +70,9 @@
     subpassDesc.pInputAttachments = nullptr;
     if (fAttachmentFlags & kColor_AttachmentFlag) {
         // set up color attachment
-        setup_simple_vk_attachment_description(&attachments[currentAttachment],
-                                               fAttachmentsDescriptor.fColor.fFormat,
-                                               fAttachmentsDescriptor.fColor.fSamples,
-                                               VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
+        setup_vk_attachment_description(&attachments[currentAttachment],
+                                        fAttachmentsDescriptor.fColor,
+                                        VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
         // setup subpass use of attachment
         colorRef.attachment = currentAttachment++;
         colorRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
@@ -75,10 +88,9 @@
 
     if (fAttachmentFlags & kResolve_AttachmentFlag) {
         // set up resolve attachment
-        setup_simple_vk_attachment_description(&attachments[currentAttachment],
-                                               fAttachmentsDescriptor.fResolve.fFormat,
-                                               fAttachmentsDescriptor.fResolve.fSamples,
-                                               VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
+        setup_vk_attachment_description(&attachments[currentAttachment],
+                                        fAttachmentsDescriptor.fResolve,
+                                        VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
         // setup subpass use of attachment
         resolveRef.attachment = currentAttachment++;
         // I'm really not sure what the layout should be for the resolve textures.
@@ -90,10 +102,9 @@
 
     if (fAttachmentFlags & kStencil_AttachmentFlag) {
         // set up stencil attachment
-        setup_simple_vk_attachment_description(&attachments[currentAttachment],
-                                               fAttachmentsDescriptor.fStencil.fFormat,
-                                               fAttachmentsDescriptor.fStencil.fSamples,
-                                               VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
+        setup_vk_attachment_description(&attachments[currentAttachment],
+                                        fAttachmentsDescriptor.fStencil,
+                                        VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
         // setup subpass use of attachment
         stencilRef.attachment = currentAttachment++;
         stencilRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
@@ -202,17 +213,17 @@
     }
 
     if (fAttachmentFlags & kColor_AttachmentFlag) {
-        if (fAttachmentsDescriptor.fColor != desc.fColor) {
+        if (!fAttachmentsDescriptor.fColor.isCompatible(desc.fColor)) {
             return false;
         }
     }
     if (fAttachmentFlags & kResolve_AttachmentFlag) {
-        if (fAttachmentsDescriptor.fResolve != desc.fResolve) {
+        if (!fAttachmentsDescriptor.fResolve.isCompatible(desc.fResolve)) {
             return false;
         }
     }
     if (fAttachmentFlags & kStencil_AttachmentFlag) {
-        if (fAttachmentsDescriptor.fStencil != desc.fStencil) {
+        if (!fAttachmentsDescriptor.fStencil.isCompatible(desc.fStencil)) {
             return false;
         }
     }
diff --git a/src/gpu/vk/GrVkRenderPass.h b/src/gpu/vk/GrVkRenderPass.h
index d38848f..082cccd 100644
--- a/src/gpu/vk/GrVkRenderPass.h
+++ b/src/gpu/vk/GrVkRenderPass.h
@@ -27,13 +27,26 @@
         struct AttachmentDesc {
             VkFormat fFormat;
             int fSamples;
-            AttachmentDesc() : fFormat(VK_FORMAT_UNDEFINED), fSamples(0) {}
+            VkAttachmentLoadOp fLoadOp;
+            VkAttachmentStoreOp fStoreOp;
+
+            AttachmentDesc()
+                : fFormat(VK_FORMAT_UNDEFINED)
+                , fSamples(0)
+                , fLoadOp(VK_ATTACHMENT_LOAD_OP_LOAD)
+                , fStoreOp(VK_ATTACHMENT_STORE_OP_STORE) {}
             bool operator==(const AttachmentDesc& right) const {
-                return (fFormat == right.fFormat && fSamples == right.fSamples);
+                return (fFormat == right.fFormat &&
+                        fSamples == right.fSamples &&
+                        fLoadOp == right.fLoadOp &&
+                        fStoreOp == right.fStoreOp);
             }
             bool operator!=(const AttachmentDesc& right) const {
                 return !(*this == right);
             }
+            bool isCompatible(const AttachmentDesc& desc) const {
+                return (fFormat == desc.fFormat && fSamples == desc.fSamples);
+            }
         };
         AttachmentDesc fColor;
         AttachmentDesc fResolve;
@@ -77,7 +90,6 @@
 
 private:
     GrVkRenderPass(const GrVkRenderPass&);
-    GrVkRenderPass& operator=(const GrVkRenderPass&);
 
     void freeGPUData(const GrVkGpu* gpu) const override;
 
diff --git a/src/gpu/vk/GrVkRenderTarget.cpp b/src/gpu/vk/GrVkRenderTarget.cpp
index dd3bca5..708af79 100644
--- a/src/gpu/vk/GrVkRenderTarget.cpp
+++ b/src/gpu/vk/GrVkRenderTarget.cpp
@@ -231,7 +231,8 @@
 
     // Vulkan requires us to create a compatible renderpass before we can create our framebuffer,
     // so we use this to get a (cached) basic renderpass, only for creation.
-    fCachedSimpleRenderPass = gpu->resourceProvider().findOrCreateCompatibleRenderPass(*this);
+    fCachedSimpleRenderPass =
+        gpu->resourceProvider().findCompatibleRenderPass(*this, &fCompatibleRPHandle);
 
     // Stencil attachment view is stored in the base RT stencil attachment
     const GrVkImageView* stencilView = this->stencilAttachmentView();
diff --git a/src/gpu/vk/GrVkRenderTarget.h b/src/gpu/vk/GrVkRenderTarget.h
index accc677..0467371 100644
--- a/src/gpu/vk/GrVkRenderTarget.h
+++ b/src/gpu/vk/GrVkRenderTarget.h
@@ -13,6 +13,7 @@
 #include "GrRenderTarget.h"
 
 #include "GrVkRenderPass.h"
+#include "GrVkResourceProvider.h"
 
 class GrVkCommandBuffer;
 class GrVkFramebuffer;
@@ -135,6 +136,8 @@
     // This is a cached pointer to a simple render pass. The render target should unref it
     // once it is done with it.
     const GrVkRenderPass*      fCachedSimpleRenderPass;
+    // This is a handle to be used to quickly get compatible GrVkRenderPasses for this render target
+    GrVkResourceProvider::CompatibleRPHandle fCompatibleRPHandle;
 };
 
 #endif
diff --git a/src/gpu/vk/GrVkResourceProvider.cpp b/src/gpu/vk/GrVkResourceProvider.cpp
index bf283fc..fdc9e90 100644
--- a/src/gpu/vk/GrVkResourceProvider.cpp
+++ b/src/gpu/vk/GrVkResourceProvider.cpp
@@ -28,7 +28,7 @@
 }
 
 GrVkResourceProvider::~GrVkResourceProvider() {
-    SkASSERT(0 == fSimpleRenderPasses.count());
+    SkASSERT(0 == fRenderPassArray.count());
     SkASSERT(VK_NULL_HANDLE == fPipelineCache);
     delete fPipelineStateCache;
 }
@@ -102,18 +102,34 @@
 // only used for framebuffer creation. When we actually render we will create
 // RenderPasses as needed that are compatible with the framebuffer.
 const GrVkRenderPass*
-GrVkResourceProvider::findOrCreateCompatibleRenderPass(const GrVkRenderTarget& target) {
-    for (int i = 0; i < fSimpleRenderPasses.count(); ++i) {
-        GrVkRenderPass* renderPass = fSimpleRenderPasses[i];
-        if (renderPass->isCompatible(target)) {
+GrVkResourceProvider::findCompatibleRenderPass(const GrVkRenderTarget& target,
+                                               CompatibleRPHandle* compatibleHandle) {
+    for (int i = 0; i < fRenderPassArray.count(); ++i) {
+        if (fRenderPassArray[i].isCompatible(target)) {
+            const GrVkRenderPass* renderPass = fRenderPassArray[i].getCompatibleRenderPass();
             renderPass->ref();
+            if (compatibleHandle) {
+                *compatibleHandle = CompatibleRPHandle(i);
+            }
             return renderPass;
         }
     }
 
-    GrVkRenderPass* renderPass = new GrVkRenderPass();
-    renderPass->initSimple(fGpu, target);
-    fSimpleRenderPasses.push_back(renderPass);
+    const GrVkRenderPass* renderPass =
+        fRenderPassArray.emplace_back(fGpu, target).getCompatibleRenderPass();
+    renderPass->ref();
+
+    if (compatibleHandle) {
+        *compatibleHandle = CompatibleRPHandle(fRenderPassArray.count() - 1);
+    }
+    return renderPass;
+}
+
+const GrVkRenderPass*
+GrVkResourceProvider::findCompatibleRenderPass(const CompatibleRPHandle& compatibleHandle) {
+    SkASSERT(compatibleHandle.isValid() && compatibleHandle.toIndex() < fRenderPassArray.count());
+    int index = compatibleHandle.toIndex();
+    const GrVkRenderPass* renderPass = fRenderPassArray[index].getCompatibleRenderPass();
     renderPass->ref();
     return renderPass;
 }
@@ -198,11 +214,11 @@
     }
     fActiveCommandBuffers.reset();
 
-    // loop over all render passes to make sure we destroy all the internal VkRenderPasses
-    for (int i = 0; i < fSimpleRenderPasses.count(); ++i) {
-        fSimpleRenderPasses[i]->unref(fGpu);
+    // loop over all render pass sets to make sure we destroy all the internal VkRenderPasses
+    for (int i = 0; i < fRenderPassArray.count(); ++i) {
+        fRenderPassArray[i].releaseResources(fGpu);
     }
-    fSimpleRenderPasses.reset();
+    fRenderPassArray.reset();
 
     // Iterate through all store GrVkSamplers and unref them before resetting the hash.
     SkTDynamicHash<GrVkSampler, uint16_t>::Iter iter(&fSamplers);
@@ -237,10 +253,11 @@
     }
     fActiveCommandBuffers.reset();
 
-    for (int i = 0; i < fSimpleRenderPasses.count(); ++i) {
-        fSimpleRenderPasses[i]->unrefAndAbandon();
+    // loop over all render pass sets to make sure we destroy all the internal VkRenderPasses
+    for (int i = 0; i < fRenderPassArray.count(); ++i) {
+        fRenderPassArray[i].abandonResources();
     }
-    fSimpleRenderPasses.reset();
+    fRenderPassArray.reset();
 
     // Iterate through all store GrVkSamplers and unrefAndAbandon them before resetting the hash.
     SkTDynamicHash<GrVkSampler, uint16_t>::Iter iter(&fSamplers);
@@ -259,3 +276,39 @@
     fUniformDescLayout = VK_NULL_HANDLE;
     fUniformDescPool->unrefAndAbandon();
 }
+
+////////////////////////////////////////////////////////////////////////////////
+
+GrVkResourceProvider::CompatibleRenderPassSet::CompatibleRenderPassSet(
+                                                                     const GrVkGpu* gpu,
+                                                                     const GrVkRenderTarget& target)
+    : fLastReturnedIndex(0) {
+    fRenderPasses.emplace_back(new GrVkRenderPass());
+    fRenderPasses[0]->initSimple(gpu, target);
+}
+
+bool GrVkResourceProvider::CompatibleRenderPassSet::isCompatible(
+                                                             const GrVkRenderTarget& target) const {
+    // The first GrVkRenderpass should always exists since we create the basic load store
+    // render pass on create
+    SkASSERT(fRenderPasses[0]);
+    return fRenderPasses[0]->isCompatible(target);
+}
+
+void GrVkResourceProvider::CompatibleRenderPassSet::releaseResources(const GrVkGpu* gpu) {
+    for (int i = 0; i < fRenderPasses.count(); ++i) {
+        if (fRenderPasses[i]) {
+            fRenderPasses[i]->unref(gpu);
+            fRenderPasses[i] = nullptr;
+        }
+    }
+}
+
+void GrVkResourceProvider::CompatibleRenderPassSet::abandonResources() {
+    for (int i = 0; i < fRenderPasses.count(); ++i) {
+        if (fRenderPasses[i]) {
+            fRenderPasses[i]->unrefAndAbandon();
+            fRenderPasses[i] = nullptr;
+        }
+    }
+}
diff --git a/src/gpu/vk/GrVkResourceProvider.h b/src/gpu/vk/GrVkResourceProvider.h
index 37ec6fd..5368a21 100644
--- a/src/gpu/vk/GrVkResourceProvider.h
+++ b/src/gpu/vk/GrVkResourceProvider.h
@@ -9,6 +9,7 @@
 #define GrVkResourceProvider_DEFINED
 
 #include "GrGpu.h"
+#include "GrResourceHandle.h"
 #include "GrVkDescriptorPool.h"
 #include "GrVkPipelineState.h"
 #include "GrVkResource.h"
@@ -46,9 +47,33 @@
                                  const GrVkRenderPass& renderPass,
                                  VkPipelineLayout layout);
 
+    GR_DEFINE_RESOURCE_HANDLE_CLASS(CompatibleRPHandle);
+
     // Finds or creates a simple render pass that matches the target, increments the refcount,
-    // and returns.
-    const GrVkRenderPass* findOrCreateCompatibleRenderPass(const GrVkRenderTarget& target);
+    // and returns. The caller can optionally pass in a pointer to a CompatibleRPHandle. If this is
+    // non null it will be set to a handle that can be used in the furutre to quickly return a
+    // compatible GrVkRenderPasses without the need inspecting a GrVkRenderTarget.
+    const GrVkRenderPass* findCompatibleRenderPass(const GrVkRenderTarget& target,
+                                                   CompatibleRPHandle* compatibleHandle = nullptr);
+    // The CompatibleRPHandle must be a valid handle previously set by a call to
+    // findCompatibleRenderPass(GrVkRenderTarget&, CompatibleRPHandle*).
+    const GrVkRenderPass* findCompatibleRenderPass(const CompatibleRPHandle& compatibleHandle);
+
+#if 0
+    // TODO:
+    const GrVkRenderPass* findRenderPass(const GrVkRenderTarget& target,
+                                         VkAttachmentLoadOp colorLoad,
+                                         VkAttachmentStoreOp colorStore,
+                                         VkAttachmentLoadOp stencilLoad,
+                                         VkAttachmentStoreOp stencilStore,
+                                         CompatibleRPHandle* compatibleHandle = nullptr);
+
+    const GrVkRenderPass* findRenderPass(const CompatibleRPHandle& compatibleHandle,
+                                         VkAttachmentLoadOp colorLoad,
+                                         VkAttachmentStoreOp colorStore,
+                                         VkAttachmentLoadOp stencilLoad,
+                                         VkAttachmentStoreOp stencilStore);
+#endif
 
     GrVkCommandBuffer* createCommandBuffer();
     void checkCommandBuffers();
@@ -94,7 +119,6 @@
     void abandonResources();
 
 private:
-
 #ifdef SK_DEBUG
 #define GR_PIPELINE_STATE_CACHE_STATS
 #endif
@@ -134,6 +158,31 @@
 #endif
     };
 
+
+    class CompatibleRenderPassSet {
+    public:
+        // This will always construct the basic load store render pass (all attachments load and
+        // store their data) so that there is at least one compatible VkRenderPass that can be used
+        // with this set.
+        CompatibleRenderPassSet(const GrVkGpu* gpu, const GrVkRenderTarget& target);
+
+        bool isCompatible(const GrVkRenderTarget& target) const;
+
+        GrVkRenderPass* getCompatibleRenderPass() const {
+            // The first GrVkRenderpass should always exist since we create the basic load store
+            // render pass on create
+            SkASSERT(fRenderPasses[0]);
+            return fRenderPasses[0];
+        }
+
+        void releaseResources(const GrVkGpu* gpu);
+        void abandonResources();
+
+    private:
+        SkSTArray<4, GrVkRenderPass*> fRenderPasses;
+        int                           fLastReturnedIndex;
+    };
+
     // Initialiaze the vkDescriptorSetLayout used for allocating new uniform buffer descritpor sets.
     void initUniformDescObjects();
 
@@ -142,9 +191,7 @@
     // Central cache for creating pipelines
     VkPipelineCache fPipelineCache;
 
-    // Array of RenderPasses that only have a single color attachment, optional stencil attachment,
-    // optional resolve attachment, and only one subpass
-    SkSTArray<4, GrVkRenderPass*> fSimpleRenderPasses;
+    SkSTArray<4, CompatibleRenderPassSet> fRenderPassArray;
 
     // Array of CommandBuffers that are currently in flight
     SkSTArray<4, GrVkCommandBuffer*> fActiveCommandBuffers;