1 // Copyright 2016 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4 
5 #pragma once
6 
7 #include <array>
8 #include <cstddef>
9 #include <deque>
10 #include <functional>
11 #include <map>
12 #include <memory>
13 #include <mutex>
14 #include <thread>
15 #include <utility>
16 #include <vector>
17 
18 #include "Common/BlockingLoop.h"
19 #include "Common/Flag.h"
20 #include "Common/Semaphore.h"
21 
22 #include "VideoBackends/Vulkan/Constants.h"
23 
24 namespace Vulkan
25 {
26 class CommandBufferManager
27 {
28 public:
29   explicit CommandBufferManager(bool use_threaded_submission);
30   ~CommandBufferManager();
31 
32   bool Initialize();
33 
34   // These command buffers are allocated per-frame. They are valid until the command buffer
35   // is submitted, after that you should call these functions again.
GetCurrentInitCommandBuffer()36   VkCommandBuffer GetCurrentInitCommandBuffer()
37   {
38     m_frame_resources[m_current_frame].init_command_buffer_used = true;
39     return m_frame_resources[m_current_frame].command_buffers[0];
40   }
GetCurrentCommandBuffer()41   VkCommandBuffer GetCurrentCommandBuffer() const
42   {
43     return m_frame_resources[m_current_frame].command_buffers[1];
44   }
GetCurrentDescriptorPool()45   VkDescriptorPool GetCurrentDescriptorPool() const
46   {
47     return m_frame_resources[m_current_frame].descriptor_pool;
48   }
49   // Allocates a descriptors set from the pool reserved for the current frame.
50   VkDescriptorSet AllocateDescriptorSet(VkDescriptorSetLayout set_layout);
51 
52   // Fence "counters" are used to track which commands have been completed by the GPU.
53   // If the last completed fence counter is greater or equal to N, it means that the work
54   // associated counter N has been completed by the GPU. The value of N to associate with
55   // commands can be retreived by calling GetCurrentFenceCounter().
GetCompletedFenceCounter()56   u64 GetCompletedFenceCounter() const { return m_completed_fence_counter; }
57 
58   // Gets the fence that will be signaled when the currently executing command buffer is
59   // queued and executed. Do not wait for this fence before the buffer is executed.
GetCurrentFenceCounter()60   u64 GetCurrentFenceCounter() const { return m_frame_resources[m_current_frame].fence_counter; }
61 
62   // Returns the semaphore for the current command buffer, which can be used to ensure the
63   // swap chain image is ready before the command buffer executes.
GetCurrentCommandBufferSemaphore()64   VkSemaphore GetCurrentCommandBufferSemaphore()
65   {
66     m_frame_resources[m_current_frame].semaphore_used = true;
67     return m_frame_resources[m_current_frame].semaphore;
68   }
69 
70   // Ensure that the worker thread has submitted any previous command buffers and is idle.
71   void WaitForWorkerThreadIdle();
72 
73   // Wait for a fence to be completed.
74   // Also invokes callbacks for completion.
75   void WaitForFenceCounter(u64 fence_counter);
76 
77   void SubmitCommandBuffer(bool submit_on_worker_thread, bool wait_for_completion,
78                            VkSwapchainKHR present_swap_chain = VK_NULL_HANDLE,
79                            uint32_t present_image_index = 0xFFFFFFFF);
80 
81   // Was the last present submitted to the queue a failure? If so, we must recreate our swapchain.
CheckLastPresentFail()82   bool CheckLastPresentFail() { return m_last_present_failed.TestAndClear(); }
GetLastPresentResult()83   VkResult GetLastPresentResult() const { return m_last_present_result; }
84 
85   // Schedule a vulkan resource for destruction later on. This will occur when the command buffer
86   // is next re-used, and the GPU has finished working with the specified resource.
87   void DeferBufferDestruction(VkBuffer object);
88   void DeferBufferViewDestruction(VkBufferView object);
89   void DeferDeviceMemoryDestruction(VkDeviceMemory object);
90   void DeferFramebufferDestruction(VkFramebuffer object);
91   void DeferImageDestruction(VkImage object);
92   void DeferImageViewDestruction(VkImageView object);
93 
94 private:
95   bool CreateCommandBuffers();
96   void DestroyCommandBuffers();
97 
98   bool CreateSubmitThread();
99 
100   void WaitForCommandBufferCompletion(u32 command_buffer_index);
101   void SubmitCommandBuffer(u32 command_buffer_index, VkSwapchainKHR present_swap_chain,
102                            u32 present_image_index);
103   void BeginCommandBuffer();
104 
105   struct FrameResources
106   {
107     // [0] - Init (upload) command buffer, [1] - draw command buffer
108     VkCommandPool command_pool = VK_NULL_HANDLE;
109     std::array<VkCommandBuffer, 2> command_buffers = {};
110     VkDescriptorPool descriptor_pool = VK_NULL_HANDLE;
111     VkFence fence = VK_NULL_HANDLE;
112     VkSemaphore semaphore = VK_NULL_HANDLE;
113     u64 fence_counter = 0;
114     bool init_command_buffer_used = false;
115     bool semaphore_used = false;
116 
117     std::vector<std::function<void()>> cleanup_resources;
118   };
119 
120   u64 m_next_fence_counter = 1;
121   u64 m_completed_fence_counter = 0;
122 
123   std::array<FrameResources, NUM_COMMAND_BUFFERS> m_frame_resources;
124   u32 m_current_frame;
125 
126   // Threaded command buffer execution
127   // Semaphore determines when a command buffer can be queued
128   Common::Semaphore m_submit_semaphore;
129   std::thread m_submit_thread;
130   std::unique_ptr<Common::BlockingLoop> m_submit_loop;
131   struct PendingCommandBufferSubmit
132   {
133     VkSwapchainKHR present_swap_chain;
134     u32 present_image_index;
135     u32 command_buffer_index;
136   };
137   VkSemaphore m_present_semaphore = VK_NULL_HANDLE;
138   std::deque<PendingCommandBufferSubmit> m_pending_submits;
139   std::mutex m_pending_submit_lock;
140   Common::Flag m_last_present_failed;
141   VkResult m_last_present_result = VK_SUCCESS;
142   bool m_use_threaded_submission = false;
143 };
144 
145 extern std::unique_ptr<CommandBufferManager> g_command_buffer_mgr;
146 
147 }  // namespace Vulkan
148