From ae6c052ed9f7e3baed13e1e88e759a3a11d2c928 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 14 Feb 2019 13:39:12 -0300
Subject: [PATCH] vk_resource_manager: Implement a command buffer pool with
 VKFencedPool

---
 .../renderer_vulkan/vk_resource_manager.cpp   | 52 +++++++++++++++++++
 .../renderer_vulkan/vk_resource_manager.h     |  8 ++-
 2 files changed, 59 insertions(+), 1 deletion(-)

diff --git a/src/video_core/renderer_vulkan/vk_resource_manager.cpp b/src/video_core/renderer_vulkan/vk_resource_manager.cpp
index e98ddba58a..1678463c7a 100644
--- a/src/video_core/renderer_vulkan/vk_resource_manager.cpp
+++ b/src/video_core/renderer_vulkan/vk_resource_manager.cpp
@@ -13,8 +13,55 @@
 namespace Vulkan {
 
 // TODO(Rodrigo): Fine tune these numbers.
+constexpr std::size_t COMMAND_BUFFER_POOL_SIZE = 0x1000;
 constexpr std::size_t FENCES_GROW_STEP = 0x40;
 
+class CommandBufferPool final : public VKFencedPool {
+public:
+    CommandBufferPool(const VKDevice& device)
+        : VKFencedPool(COMMAND_BUFFER_POOL_SIZE), device{device} {}
+
+    void Allocate(std::size_t begin, std::size_t end) {
+        const auto dev = device.GetLogical();
+        const auto& dld = device.GetDispatchLoader();
+        const u32 graphics_family = device.GetGraphicsFamily();
+
+        auto pool = std::make_unique<Pool>();
+
+        // Command buffers are going to be commited, recorded, executed every single usage cycle.
+        // They are also going to be reseted when commited.
+        const auto pool_flags = vk::CommandPoolCreateFlagBits::eTransient |
+                                vk::CommandPoolCreateFlagBits::eResetCommandBuffer;
+        const vk::CommandPoolCreateInfo cmdbuf_pool_ci(pool_flags, graphics_family);
+        pool->handle = dev.createCommandPoolUnique(cmdbuf_pool_ci, nullptr, dld);
+
+        const vk::CommandBufferAllocateInfo cmdbuf_ai(*pool->handle,
+                                                      vk::CommandBufferLevel::ePrimary,
+                                                      static_cast<u32>(COMMAND_BUFFER_POOL_SIZE));
+        pool->cmdbufs =
+            dev.allocateCommandBuffersUnique<std::allocator<UniqueCommandBuffer>>(cmdbuf_ai, dld);
+
+        pools.push_back(std::move(pool));
+    }
+
+    vk::CommandBuffer Commit(VKFence& fence) {
+        const std::size_t index = CommitResource(fence);
+        const auto pool_index = index / COMMAND_BUFFER_POOL_SIZE;
+        const auto sub_index = index % COMMAND_BUFFER_POOL_SIZE;
+        return *pools[pool_index]->cmdbufs[sub_index];
+    }
+
+private:
+    struct Pool {
+        UniqueCommandPool handle;
+        std::vector<UniqueCommandBuffer> cmdbufs;
+    };
+
+    const VKDevice& device;
+
+    std::vector<std::unique_ptr<Pool>> pools;
+};
+
 VKResource::VKResource() = default;
 
 VKResource::~VKResource() = default;
@@ -174,6 +221,7 @@ void VKFencedPool::Grow() {
 
 VKResourceManager::VKResourceManager(const VKDevice& device) : device{device} {
     GrowFences(FENCES_GROW_STEP);
+    command_buffer_pool = std::make_unique<CommandBufferPool>(device);
 }
 
 VKResourceManager::~VKResourceManager() = default;
@@ -217,6 +265,10 @@ VKFence& VKResourceManager::CommitFence() {
     return *found_fence;
 }
 
+vk::CommandBuffer VKResourceManager::CommitCommandBuffer(VKFence& fence) {
+    return command_buffer_pool->Commit(fence);
+}
+
 void VKResourceManager::GrowFences(std::size_t new_fences_count) {
     const auto dev = device.GetLogical();
     const auto& dld = device.GetDispatchLoader();
diff --git a/src/video_core/renderer_vulkan/vk_resource_manager.h b/src/video_core/renderer_vulkan/vk_resource_manager.h
index 1fd68bb4c8..5018dfa449 100644
--- a/src/video_core/renderer_vulkan/vk_resource_manager.h
+++ b/src/video_core/renderer_vulkan/vk_resource_manager.h
@@ -15,6 +15,8 @@ class VKDevice;
 class VKFence;
 class VKResourceManager;
 
+class CommandBufferPool;
+
 /// Interface for a Vulkan resource
 class VKResource {
 public:
@@ -162,13 +164,17 @@ public:
     /// Commits a fence. It has to be sent to a queue and released.
     VKFence& CommitFence();
 
+    /// Commits an unused command buffer and protects it with a fence.
+    vk::CommandBuffer CommitCommandBuffer(VKFence& fence);
+
 private:
     /// Allocates new fences.
     void GrowFences(std::size_t new_fences_count);
 
     const VKDevice& device;          ///< Device handler.
     std::size_t fences_iterator = 0; ///< Index where a free fence is likely to be found.
-    std::vector<std::unique_ptr<VKFence>> fences; ///< Pool of fences.
+    std::vector<std::unique_ptr<VKFence>> fences;           ///< Pool of fences.
+    std::unique_ptr<CommandBufferPool> command_buffer_pool; ///< Pool of command buffers.
 };
 
 } // namespace Vulkan