Recycle small uniform buffers.

BUG=skia:5031
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2159333002

Review-Url: https://codereview.chromium.org/2159333002
diff --git a/src/gpu/vk/GrVkBuffer.cpp b/src/gpu/vk/GrVkBuffer.cpp
index 3b9cbc7..75f3901 100644
--- a/src/gpu/vk/GrVkBuffer.cpp
+++ b/src/gpu/vk/GrVkBuffer.cpp
@@ -108,7 +108,7 @@
 
 void GrVkBuffer::vkRelease(const GrVkGpu* gpu) {
     VALIDATE();
-    fResource->unref(gpu);
+    fResource->recycle(const_cast<GrVkGpu*>(gpu));
     fResource = nullptr;
     fMapPtr = nullptr;
     VALIDATE();
diff --git a/src/gpu/vk/GrVkBuffer.h b/src/gpu/vk/GrVkBuffer.h
index 232de17..b400e09 100644
--- a/src/gpu/vk/GrVkBuffer.h
+++ b/src/gpu/vk/GrVkBuffer.h
@@ -53,7 +53,7 @@
         bool        fDynamic;
     };
 
-    class Resource : public GrVkResource {
+    class Resource : public GrVkRecycledResource {
     public:
         Resource(VkBuffer buf, const GrVkAlloc& alloc, Type type)
             : INHERITED(), fBuffer(buf), fAlloc(alloc), fType(type) {}
@@ -70,7 +70,9 @@
     private:
         void freeGPUData(const GrVkGpu* gpu) const override;
 
-        typedef GrVkResource INHERITED;
+        void onRecycle(GrVkGpu* gpu) const override { this->unref(gpu); }
+
+        typedef GrVkRecycledResource INHERITED;
     };
 
     // convenience routine for raw buffer creation
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index a09a805..7fddd4b 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -151,7 +151,7 @@
     fHeaps[kSmallOptimalImage_Heap].reset(new GrVkHeap(this, GrVkHeap::kSubAlloc_Strategy, 2*1024*1024));
     fHeaps[kVertexBuffer_Heap].reset(new GrVkHeap(this, GrVkHeap::kSingleAlloc_Strategy, 0));
     fHeaps[kIndexBuffer_Heap].reset(new GrVkHeap(this, GrVkHeap::kSingleAlloc_Strategy, 0));
-    fHeaps[kUniformBuffer_Heap].reset(new GrVkHeap(this, GrVkHeap::kSubAlloc_Strategy, 64*1024));
+    fHeaps[kUniformBuffer_Heap].reset(new GrVkHeap(this, GrVkHeap::kSubAlloc_Strategy, 256*1024));
     fHeaps[kCopyReadBuffer_Heap].reset(new GrVkHeap(this, GrVkHeap::kSingleAlloc_Strategy, 0));
     fHeaps[kCopyWriteBuffer_Heap].reset(new GrVkHeap(this, GrVkHeap::kSubAlloc_Strategy, 16*1024*1024));
 }
diff --git a/src/gpu/vk/GrVkPipelineState.cpp b/src/gpu/vk/GrVkPipelineState.cpp
index b9aa826..ab9cff8 100644
--- a/src/gpu/vk/GrVkPipelineState.cpp
+++ b/src/gpu/vk/GrVkPipelineState.cpp
@@ -68,8 +68,8 @@
         fStartDS = SkTMin(fStartDS, (int)GrVkUniformHandler::kSamplerDescSet);
     }
 
-    fVertexUniformBuffer.reset(GrVkUniformBuffer::Create(gpu, vertexUniformSize, true));
-    fFragmentUniformBuffer.reset(GrVkUniformBuffer::Create(gpu, fragmentUniformSize, true));
+    fVertexUniformBuffer.reset(GrVkUniformBuffer::Create(gpu, vertexUniformSize));
+    fFragmentUniformBuffer.reset(GrVkUniformBuffer::Create(gpu, fragmentUniformSize));
 
     fNumSamplers = numSamplers;
 }
diff --git a/src/gpu/vk/GrVkResourceProvider.cpp b/src/gpu/vk/GrVkResourceProvider.cpp
index ccf4716..abe07bd 100644
--- a/src/gpu/vk/GrVkResourceProvider.cpp
+++ b/src/gpu/vk/GrVkResourceProvider.cpp
@@ -12,6 +12,7 @@
 #include "GrVkPipeline.h"
 #include "GrVkRenderTarget.h"
 #include "GrVkSampler.h"
+#include "GrVkUniformBuffer.h"
 #include "GrVkUtil.h"
 
 #ifdef SK_TRACE_VK_RESOURCES
@@ -267,6 +268,22 @@
     fAvailableSecondaryCommandBuffers.push_back(cb);
 }
 
+const GrVkResource* GrVkResourceProvider::findOrCreateStandardUniformBufferResource() {
+    const GrVkResource* resource = nullptr;
+    int count = fAvailableUniformBufferResources.count();
+    if (count > 0) {
+        resource = fAvailableUniformBufferResources[count - 1];
+        fAvailableUniformBufferResources.removeShuffle(count - 1);
+    } else {
+        resource = GrVkUniformBuffer::CreateResource(fGpu, GrVkUniformBuffer::kStandardSize);
+    }
+    return resource;
+}
+
+void GrVkResourceProvider::recycleStandardUniformBufferResource(const GrVkResource* resource) {
+    fAvailableUniformBufferResources.push_back(resource);
+}
+
 void GrVkResourceProvider::destroyResources() {
     // release our active command buffers
     for (int i = 0; i < fActiveCommandBuffers.count(); ++i) {
@@ -322,6 +339,13 @@
         fDescriptorSetManagers[i].release(fGpu);
     }
     fDescriptorSetManagers.reset();
+
+    // release our uniform buffers
+    for (int i = 0; i < fAvailableUniformBufferResources.count(); ++i) {
+        SkASSERT(fAvailableUniformBufferResources[i]->unique());
+        fAvailableUniformBufferResources[i]->unref(fGpu);
+    }
+    fAvailableUniformBufferResources.reset();
 }
 
 void GrVkResourceProvider::abandonResources() {
@@ -371,6 +395,12 @@
     }
     fDescriptorSetManagers.reset();
 
+    // release our uniform buffers
+    for (int i = 0; i < fAvailableUniformBufferResources.count(); ++i) {
+        SkASSERT(fAvailableUniformBufferResources[i]->unique());
+        fAvailableUniformBufferResources[i]->unrefAndAbandon();
+    }
+    fAvailableUniformBufferResources.reset();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/vk/GrVkResourceProvider.h b/src/gpu/vk/GrVkResourceProvider.h
index cd0411b..0660e91 100644
--- a/src/gpu/vk/GrVkResourceProvider.h
+++ b/src/gpu/vk/GrVkResourceProvider.h
@@ -128,6 +128,14 @@
     void recycleDescriptorSet(const GrVkDescriptorSet* descSet,
                               const GrVkDescriptorSetManager::Handle&);
 
+    // Creates or finds free uniform buffer resources of size GrVkUniformBuffer::kStandardSize.
+    // Anything larger will need to be created and released by the client.
+    const GrVkResource* findOrCreateStandardUniformBufferResource();
+
+    // Signals that the resource passed to it (which should be a uniform buffer resource)
+    // can be reused by the next uniform buffer resource request.
+    void recycleStandardUniformBufferResource(const GrVkResource*);
+
     // Destroy any cached resources. To be called before destroying the VkDevice.
     // The assumption is that all queues are idle and all command buffers are finished.
     // For resource tracing to work properly, this should be called after unrefing all other
@@ -226,6 +234,9 @@
     // Array of available secondary command buffers
     SkSTArray<16, GrVkSecondaryCommandBuffer*> fAvailableSecondaryCommandBuffers;
 
+    // Array of available uniform buffer resources
+    SkSTArray<16, const GrVkResource*> fAvailableUniformBufferResources;
+
     // Stores GrVkSampler objects that we've already created so we can reuse them across multiple
     // GrVkPipelineStates
     SkTDynamicHash<GrVkSampler, uint16_t> fSamplers;
diff --git a/src/gpu/vk/GrVkUniformBuffer.cpp b/src/gpu/vk/GrVkUniformBuffer.cpp
index 022e2e3..c3899b0 100644
--- a/src/gpu/vk/GrVkUniformBuffer.cpp
+++ b/src/gpu/vk/GrVkUniformBuffer.cpp
@@ -8,24 +8,84 @@
 #include "GrVkUniformBuffer.h"
 #include "GrVkGpu.h"
 
+#define VK_CALL(GPU, X) GR_VK_CALL(GPU->vkInterface(), X)
 
-GrVkUniformBuffer* GrVkUniformBuffer::Create(GrVkGpu* gpu, size_t size, bool dynamic) {
+GrVkUniformBuffer* GrVkUniformBuffer::Create(GrVkGpu* gpu, size_t size) {
     if (0 == size) {
         return nullptr;
     }
-    GrVkBuffer::Desc desc;
-    desc.fDynamic = dynamic;
-    desc.fType = GrVkBuffer::kUniform_Type;
-    desc.fSizeInBytes = size;
-
-    const GrVkBuffer::Resource* bufferResource = GrVkBuffer::Create(gpu, desc);
-    if (!bufferResource) {
+    const GrVkResource* resource = nullptr;
+    if (size <= GrVkUniformBuffer::kStandardSize) {
+        resource = gpu->resourceProvider().findOrCreateStandardUniformBufferResource();
+    } else {
+        resource = CreateResource(gpu, size);
+    }
+    if (!resource) {
         return nullptr;
     }
 
-    GrVkUniformBuffer* buffer = new GrVkUniformBuffer(desc, bufferResource);
+    GrVkBuffer::Desc desc;
+    desc.fDynamic = true;
+    desc.fType = GrVkBuffer::kUniform_Type;
+    desc.fSizeInBytes = size;
+    GrVkUniformBuffer* buffer = new GrVkUniformBuffer(gpu, desc,
+                                                      (const GrVkUniformBuffer::Resource*) resource);
     if (!buffer) {
-        bufferResource->unref(gpu);
+        // this will destroy anything we got from the resource provider,
+        // but this avoids a conditional
+        resource->unref(gpu);
     }
     return buffer;
-}
\ No newline at end of file
+}
+
+// We implement our own creation function for special buffer resource type
+const GrVkResource* GrVkUniformBuffer::CreateResource(GrVkGpu* gpu, size_t size) {
+    if (0 == size) {
+        return nullptr;
+    }
+
+    VkBuffer       buffer;
+    GrVkAlloc      alloc;
+
+    // create the buffer object
+    VkBufferCreateInfo bufInfo;
+    memset(&bufInfo, 0, sizeof(VkBufferCreateInfo));
+    bufInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+    bufInfo.flags = 0;
+    bufInfo.size = size;
+    bufInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
+    bufInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+    bufInfo.queueFamilyIndexCount = 0;
+    bufInfo.pQueueFamilyIndices = nullptr;
+
+    VkResult err;
+    err = VK_CALL(gpu, CreateBuffer(gpu->device(), &bufInfo, nullptr, &buffer));
+    if (err) {
+        return nullptr;
+    }
+
+    if (!GrVkMemory::AllocAndBindBufferMemory(gpu,
+                                              buffer,
+                                              kUniform_Type,
+                                              true,  // dynamic
+                                              &alloc)) {
+        return nullptr;
+    }
+
+    const GrVkResource* resource = new GrVkUniformBuffer::Resource(buffer, alloc);
+    if (!resource) {
+        VK_CALL(gpu, DestroyBuffer(gpu->device(), buffer, nullptr));
+        GrVkMemory::FreeBufferMemory(gpu, kUniform_Type, alloc);
+        return nullptr;
+    }
+
+    return resource;
+}
+
+void GrVkUniformBuffer::Resource::onRecycle(GrVkGpu* gpu) const {
+    if (fAlloc.fSize <= GrVkUniformBuffer::kStandardSize) {
+        gpu->resourceProvider().recycleStandardUniformBufferResource(this);
+    } else {
+        this->unref(gpu);
+    }
+}
diff --git a/src/gpu/vk/GrVkUniformBuffer.h b/src/gpu/vk/GrVkUniformBuffer.h
index ed6c8a0..7aa3af6 100644
--- a/src/gpu/vk/GrVkUniformBuffer.h
+++ b/src/gpu/vk/GrVkUniformBuffer.h
@@ -15,7 +15,9 @@
 class GrVkUniformBuffer : public GrVkBuffer {
 
 public:
-    static GrVkUniformBuffer* Create(GrVkGpu* gpu, size_t size, bool dynamic);
+    static GrVkUniformBuffer* Create(GrVkGpu* gpu, size_t size);
+    static const GrVkResource* CreateResource(GrVkGpu* gpu, size_t size);
+    static const size_t kStandardSize = 256;
 
     void* map(const GrVkGpu* gpu) {
         return this->vkMap(gpu);
@@ -29,18 +31,27 @@
                     bool* createdNewBuffer) {
         return this->vkUpdateData(gpu, src, srcSizeInBytes, createdNewBuffer);
     }
-    void release(const GrVkGpu* gpu) {
-        this->vkRelease(gpu);
-    }
-    void abandon() {
-        this->vkAbandon();
-    }
+    void release(const GrVkGpu* gpu) { this->vkRelease(gpu); }
+    void abandon() { this->vkAbandon(); }
 
 private:
-    GrVkUniformBuffer(const GrVkBuffer::Desc& desc, const GrVkBuffer::Resource* resource)
-        : INHERITED(desc, resource) {
+    class Resource : public GrVkBuffer::Resource {
+    public:
+        Resource(VkBuffer buf, const GrVkAlloc& alloc)
+            : INHERITED(buf, alloc, kUniform_Type) {}
+
+        void onRecycle(GrVkGpu* gpu) const override;
+
+        typedef GrVkBuffer::Resource INHERITED;
     };
 
+    GrVkUniformBuffer(GrVkGpu* gpu, const GrVkBuffer::Desc& desc,
+                      const GrVkUniformBuffer::Resource* resource)
+        : INHERITED(desc, resource)
+        , fGpu(gpu) {}
+
+    GrVkGpu* fGpu;
+
     typedef GrVkBuffer INHERITED;
 };