2016-08-13 12:57:50 +00:00
|
|
|
// Copyright 2016 Dolphin Emulator Project
|
2021-07-05 01:22:19 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2016-08-13 12:57:50 +00:00
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <array>
|
2016-10-01 03:07:50 +00:00
|
|
|
#include <cstddef>
|
2016-08-13 12:57:50 +00:00
|
|
|
#include <deque>
|
|
|
|
#include <functional>
|
|
|
|
#include <map>
|
|
|
|
#include <memory>
|
|
|
|
#include <mutex>
|
|
|
|
#include <thread>
|
|
|
|
#include <utility>
|
|
|
|
#include <vector>
|
|
|
|
|
2023-01-27 14:06:35 +00:00
|
|
|
#include <Common/WorkQueueThread.h>
|
2016-08-13 12:57:50 +00:00
|
|
|
#include "Common/BlockingLoop.h"
|
2017-09-16 05:53:08 +00:00
|
|
|
#include "Common/Flag.h"
|
2016-08-13 12:57:50 +00:00
|
|
|
#include "Common/Semaphore.h"
|
|
|
|
|
|
|
|
#include "VideoBackends/Vulkan/Constants.h"
|
|
|
|
|
|
|
|
namespace Vulkan
|
|
|
|
{
|
|
|
|
class CommandBufferManager
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
explicit CommandBufferManager(bool use_threaded_submission);
|
|
|
|
~CommandBufferManager();
|
|
|
|
|
|
|
|
bool Initialize();
|
|
|
|
|
|
|
|
// These command buffers are allocated per-frame. They are valid until the command buffer
|
|
|
|
// is submitted, after that you should call these functions again.
|
2016-09-28 13:04:02 +00:00
|
|
|
VkCommandBuffer GetCurrentInitCommandBuffer()
|
2016-08-13 12:57:50 +00:00
|
|
|
{
|
2022-09-30 23:17:38 +00:00
|
|
|
CmdBufferResources& cmd_buffer_resources = GetCurrentCmdBufferResources();
|
|
|
|
cmd_buffer_resources.init_command_buffer_used = true;
|
|
|
|
return cmd_buffer_resources.command_buffers[0];
|
2016-08-13 12:57:50 +00:00
|
|
|
}
|
|
|
|
VkCommandBuffer GetCurrentCommandBuffer() const
|
|
|
|
{
|
2022-09-30 23:17:38 +00:00
|
|
|
const CmdBufferResources& cmd_buffer_resources = m_command_buffers[m_current_cmd_buffer];
|
|
|
|
return cmd_buffer_resources.command_buffers[1];
|
2016-08-13 12:57:50 +00:00
|
|
|
}
|
|
|
|
// Allocates a descriptors set from the pool reserved for the current frame.
|
|
|
|
VkDescriptorSet AllocateDescriptorSet(VkDescriptorSetLayout set_layout);
|
|
|
|
|
2019-03-17 05:59:22 +00:00
|
|
|
// Fence "counters" are used to track which commands have been completed by the GPU.
|
|
|
|
// If the last completed fence counter is greater or equal to N, it means that the work
|
|
|
|
// associated counter N has been completed by the GPU. The value of N to associate with
|
|
|
|
// commands can be retreived by calling GetCurrentFenceCounter().
|
|
|
|
u64 GetCompletedFenceCounter() const { return m_completed_fence_counter; }
|
|
|
|
|
2016-08-13 12:57:50 +00:00
|
|
|
// Gets the fence that will be signaled when the currently executing command buffer is
|
|
|
|
// queued and executed. Do not wait for this fence before the buffer is executed.
|
2022-09-30 23:17:38 +00:00
|
|
|
u64 GetCurrentFenceCounter() const
|
|
|
|
{
|
|
|
|
auto& resources = m_command_buffers[m_current_cmd_buffer];
|
|
|
|
return resources.fence_counter;
|
|
|
|
}
|
2019-02-15 01:59:50 +00:00
|
|
|
|
|
|
|
// Returns the semaphore for the current command buffer, which can be used to ensure the
|
|
|
|
// swap chain image is ready before the command buffer executes.
|
2024-10-01 03:00:41 +00:00
|
|
|
// Once you've confirmed that the semaphore will be signalled this frame, call
|
|
|
|
// `MarkCurrentCommandBufferSemaphoreUsed`.
|
2019-02-15 01:59:50 +00:00
|
|
|
VkSemaphore GetCurrentCommandBufferSemaphore()
|
|
|
|
{
|
2024-10-01 03:00:41 +00:00
|
|
|
return m_command_buffers[m_current_cmd_buffer].semaphore;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Marks the current command buffer's semaphore as used, so we'll wait on it in the next
|
|
|
|
// command buffer submission.
|
|
|
|
void MarkCurrentCommandBufferSemaphoreUsed()
|
|
|
|
{
|
|
|
|
m_command_buffers[m_current_cmd_buffer].semaphore_used = true;
|
2019-02-15 01:59:50 +00:00
|
|
|
}
|
2016-08-13 12:57:50 +00:00
|
|
|
|
|
|
|
// Ensure that the worker thread has submitted any previous command buffers and is idle.
|
|
|
|
void WaitForWorkerThreadIdle();
|
|
|
|
|
|
|
|
// Wait for a fence to be completed.
|
|
|
|
// Also invokes callbacks for completion.
|
2019-03-17 05:59:22 +00:00
|
|
|
void WaitForFenceCounter(u64 fence_counter);
|
2016-08-13 12:57:50 +00:00
|
|
|
|
2019-03-17 05:59:22 +00:00
|
|
|
void SubmitCommandBuffer(bool submit_on_worker_thread, bool wait_for_completion,
|
2024-09-30 23:29:38 +00:00
|
|
|
bool advance_to_next_frame = false,
|
2016-08-13 12:57:50 +00:00
|
|
|
VkSwapchainKHR present_swap_chain = VK_NULL_HANDLE,
|
|
|
|
uint32_t present_image_index = 0xFFFFFFFF);
|
|
|
|
|
2017-09-16 05:53:08 +00:00
|
|
|
// Was the last present submitted to the queue a failure? If so, we must recreate our swapchain.
|
2019-09-30 15:10:08 +00:00
|
|
|
bool CheckLastPresentFail() { return m_last_present_failed.TestAndClear(); }
|
|
|
|
VkResult GetLastPresentResult() const { return m_last_present_result; }
|
2022-11-07 01:20:22 +00:00
|
|
|
bool CheckLastPresentDone() { return m_last_present_done.TestAndClear(); }
|
2019-02-15 01:59:50 +00:00
|
|
|
|
2016-08-13 12:57:50 +00:00
|
|
|
// Schedule a vulkan resource for destruction later on. This will occur when the command buffer
|
|
|
|
// is next re-used, and the GPU has finished working with the specified resource.
|
2016-10-01 00:40:44 +00:00
|
|
|
void DeferBufferViewDestruction(VkBufferView object);
|
2022-10-07 21:37:16 +00:00
|
|
|
void DeferBufferDestruction(VkBuffer buffer, VmaAllocation alloc);
|
2016-10-01 00:40:44 +00:00
|
|
|
void DeferFramebufferDestruction(VkFramebuffer object);
|
2022-10-07 21:48:11 +00:00
|
|
|
void DeferImageDestruction(VkImage object, VmaAllocation alloc);
|
2016-10-01 00:40:44 +00:00
|
|
|
void DeferImageViewDestruction(VkImageView object);
|
2016-08-13 12:57:50 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
bool CreateCommandBuffers();
|
|
|
|
void DestroyCommandBuffers();
|
|
|
|
|
|
|
|
bool CreateSubmitThread();
|
|
|
|
|
2019-03-17 05:59:22 +00:00
|
|
|
void WaitForCommandBufferCompletion(u32 command_buffer_index);
|
2019-02-15 01:59:50 +00:00
|
|
|
void SubmitCommandBuffer(u32 command_buffer_index, VkSwapchainKHR present_swap_chain,
|
|
|
|
u32 present_image_index);
|
|
|
|
void BeginCommandBuffer();
|
2016-08-13 12:57:50 +00:00
|
|
|
|
2022-10-05 23:35:17 +00:00
|
|
|
VkDescriptorPool CreateDescriptorPool(u32 descriptor_sizes);
|
|
|
|
|
|
|
|
const u32 DESCRIPTOR_SETS_PER_POOL = 1024;
|
|
|
|
|
2022-09-30 23:17:38 +00:00
|
|
|
struct CmdBufferResources
|
2016-08-13 12:57:50 +00:00
|
|
|
{
|
|
|
|
// [0] - Init (upload) command buffer, [1] - draw command buffer
|
2019-02-15 01:59:50 +00:00
|
|
|
VkCommandPool command_pool = VK_NULL_HANDLE;
|
|
|
|
std::array<VkCommandBuffer, 2> command_buffers = {};
|
|
|
|
VkFence fence = VK_NULL_HANDLE;
|
|
|
|
VkSemaphore semaphore = VK_NULL_HANDLE;
|
2019-03-17 05:59:22 +00:00
|
|
|
u64 fence_counter = 0;
|
2019-02-15 01:59:50 +00:00
|
|
|
bool init_command_buffer_used = false;
|
|
|
|
bool semaphore_used = false;
|
2022-08-11 04:46:36 +00:00
|
|
|
std::atomic<bool> waiting_for_submit{false};
|
2022-09-30 23:17:38 +00:00
|
|
|
u32 frame_index = 0;
|
2016-08-13 12:57:50 +00:00
|
|
|
|
2016-10-01 00:40:44 +00:00
|
|
|
std::vector<std::function<void()>> cleanup_resources;
|
2016-08-13 12:57:50 +00:00
|
|
|
};
|
|
|
|
|
2022-10-05 23:35:17 +00:00
|
|
|
struct FrameResources
|
|
|
|
{
|
|
|
|
std::vector<VkDescriptorPool> descriptor_pools;
|
|
|
|
u32 current_descriptor_pool_index = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
FrameResources& GetCurrentFrameResources() { return m_frame_resources[m_current_frame]; }
|
|
|
|
|
2022-09-30 23:17:38 +00:00
|
|
|
CmdBufferResources& GetCurrentCmdBufferResources()
|
|
|
|
{
|
|
|
|
return m_command_buffers[m_current_cmd_buffer];
|
|
|
|
}
|
|
|
|
|
2019-03-17 05:59:22 +00:00
|
|
|
u64 m_next_fence_counter = 1;
|
|
|
|
u64 m_completed_fence_counter = 0;
|
|
|
|
|
2022-10-05 23:35:17 +00:00
|
|
|
std::array<FrameResources, NUM_FRAMES_IN_FLIGHT> m_frame_resources;
|
2022-09-30 23:17:38 +00:00
|
|
|
std::array<CmdBufferResources, NUM_COMMAND_BUFFERS> m_command_buffers;
|
2021-09-04 04:43:19 +00:00
|
|
|
u32 m_current_frame = 0;
|
2022-09-30 23:17:38 +00:00
|
|
|
u32 m_current_cmd_buffer = 0;
|
2016-08-13 12:57:50 +00:00
|
|
|
|
|
|
|
// Threaded command buffer execution
|
|
|
|
struct PendingCommandBufferSubmit
|
|
|
|
{
|
|
|
|
VkSwapchainKHR present_swap_chain;
|
2019-02-15 01:59:50 +00:00
|
|
|
u32 present_image_index;
|
|
|
|
u32 command_buffer_index;
|
2016-08-13 12:57:50 +00:00
|
|
|
};
|
2023-01-27 14:06:35 +00:00
|
|
|
Common::WorkQueueThread<PendingCommandBufferSubmit> m_submit_thread;
|
2019-02-15 01:59:50 +00:00
|
|
|
VkSemaphore m_present_semaphore = VK_NULL_HANDLE;
|
2019-09-30 15:10:08 +00:00
|
|
|
Common::Flag m_last_present_failed;
|
2022-11-07 01:20:22 +00:00
|
|
|
Common::Flag m_last_present_done;
|
2019-09-30 15:10:08 +00:00
|
|
|
VkResult m_last_present_result = VK_SUCCESS;
|
2016-08-13 12:57:50 +00:00
|
|
|
bool m_use_threaded_submission = false;
|
2022-10-31 20:36:15 +00:00
|
|
|
u32 m_descriptor_set_count = DESCRIPTOR_SETS_PER_POOL;
|
2016-08-13 12:57:50 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
extern std::unique_ptr<CommandBufferManager> g_command_buffer_mgr;
|
|
|
|
|
|
|
|
} // namespace Vulkan
|