mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-03-12 06:39:14 +01:00
D3D12: Improve robustness of command allocator and fence tracking
This commit is contained in:
parent
2f7870b046
commit
efbb85da43
@ -2,6 +2,7 @@
|
|||||||
// Licensed under GPLv2+
|
// Licensed under GPLv2+
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -69,6 +70,9 @@ D3DCommandListManager::D3DCommandListManager(
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_current_deferred_destruction_list = 0;
|
m_current_deferred_destruction_list = 0;
|
||||||
|
|
||||||
|
std::fill(m_command_allocator_list_fences.begin(), m_command_allocator_list_fences.end(), 0);
|
||||||
|
std::fill(m_deferred_destruction_list_fences.begin(), m_deferred_destruction_list_fences.end(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3DCommandListManager::SetInitialCommandListState()
|
void D3DCommandListManager::SetInitialCommandListState()
|
||||||
@ -114,37 +118,26 @@ void D3DCommandListManager::ExecuteQueuedWork(bool wait_for_gpu_completion)
|
|||||||
|
|
||||||
m_queued_command_list->QueueFenceGpuSignal(m_queue_fence, m_queue_fence_value);
|
m_queued_command_list->QueueFenceGpuSignal(m_queue_fence, m_queue_fence_value);
|
||||||
|
|
||||||
ResetCommandListWithIdleCommandAllocator();
|
m_queued_command_list->ProcessQueuedItems(wait_for_gpu_completion);
|
||||||
|
|
||||||
m_queued_command_list->ProcessQueuedItems();
|
|
||||||
#else
|
#else
|
||||||
CheckHR(m_backing_command_list->Close());
|
CheckHR(m_backing_command_list->Close());
|
||||||
|
|
||||||
ID3D12CommandList* const commandListsToExecute[1] = { m_backing_command_list };
|
ID3D12CommandList* const execute_list[1] = { m_backing_command_list };
|
||||||
m_command_queue->ExecuteCommandLists(1, commandListsToExecute);
|
m_command_queue->ExecuteCommandLists(1, execute_list);
|
||||||
|
|
||||||
if (wait_for_gpu_completion)
|
|
||||||
{
|
|
||||||
CheckHR(m_command_queue->Signal(m_queue_fence, m_queue_fence_value));
|
CheckHR(m_command_queue->Signal(m_queue_fence, m_queue_fence_value));
|
||||||
}
|
|
||||||
|
|
||||||
if (m_current_command_allocator == 0)
|
|
||||||
{
|
|
||||||
PerformGpuRolloverChecks();
|
|
||||||
}
|
|
||||||
|
|
||||||
ResetCommandListWithIdleCommandAllocator();
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Notify observers of the fence value for the current work to finish.
|
||||||
for (auto it : m_queue_fence_callbacks)
|
for (auto it : m_queue_fence_callbacks)
|
||||||
it.second(it.first, m_queue_fence_value);
|
it.second(it.first, m_queue_fence_value);
|
||||||
|
|
||||||
SetInitialCommandListState();
|
|
||||||
|
|
||||||
if (wait_for_gpu_completion)
|
if (wait_for_gpu_completion)
|
||||||
{
|
WaitForGPUCompletion();
|
||||||
WaitOnCPUForFence(m_queue_fence, m_queue_fence_value);
|
|
||||||
}
|
// Re-open the command list, using the current allocator.
|
||||||
|
ResetCommandList();
|
||||||
|
SetInitialCommandListState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3DCommandListManager::ExecuteQueuedWorkAndPresent(IDXGISwapChain* swap_chain, UINT sync_interval, UINT flags)
|
void D3DCommandListManager::ExecuteQueuedWorkAndPresent(IDXGISwapChain* swap_chain, UINT sync_interval, UINT flags)
|
||||||
@ -154,60 +147,67 @@ void D3DCommandListManager::ExecuteQueuedWorkAndPresent(IDXGISwapChain* swap_cha
|
|||||||
#ifdef USE_D3D12_QUEUED_COMMAND_LISTS
|
#ifdef USE_D3D12_QUEUED_COMMAND_LISTS
|
||||||
CheckHR(m_queued_command_list->Close());
|
CheckHR(m_queued_command_list->Close());
|
||||||
m_queued_command_list->QueueExecute();
|
m_queued_command_list->QueueExecute();
|
||||||
m_queued_command_list->QueueFenceGpuSignal(m_queue_fence, m_queue_fence_value);
|
|
||||||
m_queued_command_list->QueuePresent(swap_chain, sync_interval, flags);
|
m_queued_command_list->QueuePresent(swap_chain, sync_interval, flags);
|
||||||
m_queued_command_list->ProcessQueuedItems(true);
|
|
||||||
|
|
||||||
if (m_current_command_allocator == 0)
|
|
||||||
{
|
|
||||||
PerformGpuRolloverChecks();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_current_command_allocator = (m_current_command_allocator + 1) % m_command_allocator_lists[m_current_command_allocator_list].size();
|
|
||||||
|
|
||||||
ResetCommandListWithIdleCommandAllocator();
|
|
||||||
|
|
||||||
SetInitialCommandListState();
|
|
||||||
#else
|
|
||||||
ExecuteQueuedWork();
|
|
||||||
m_command_queue->Signal(m_queue_fence, m_queue_fence_value);
|
|
||||||
CheckHR(swap_chain->Present(sync_interval, flags));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (auto it : m_queue_fence_callbacks)
|
|
||||||
it.second(it.first, m_queue_fence_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void D3DCommandListManager::WaitForQueuedWorkToBeExecutedOnGPU()
|
|
||||||
{
|
|
||||||
// Wait for GPU to finish all outstanding work.
|
|
||||||
m_queue_fence_value++;
|
|
||||||
|
|
||||||
#ifdef USE_D3D12_QUEUED_COMMAND_LISTS
|
|
||||||
m_queued_command_list->QueueExecute();
|
|
||||||
m_queued_command_list->QueueFenceGpuSignal(m_queue_fence, m_queue_fence_value);
|
m_queued_command_list->QueueFenceGpuSignal(m_queue_fence, m_queue_fence_value);
|
||||||
|
|
||||||
m_queued_command_list->ProcessQueuedItems(true);
|
m_queued_command_list->ProcessQueuedItems(true);
|
||||||
#else
|
#else
|
||||||
|
CheckHR(m_backing_command_list->Close());
|
||||||
|
|
||||||
|
ID3D12CommandList* const execute_list[1] = { m_backing_command_list };
|
||||||
|
m_command_queue->ExecuteCommandLists(1, execute_list);
|
||||||
|
|
||||||
|
CheckHR(swap_chain->Present(sync_interval, flags));
|
||||||
CheckHR(m_command_queue->Signal(m_queue_fence, m_queue_fence_value));
|
CheckHR(m_command_queue->Signal(m_queue_fence, m_queue_fence_value));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
WaitOnCPUForFence(m_queue_fence, m_queue_fence_value);
|
// Notify observers of the fence value for the current work to finish.
|
||||||
|
for (auto it : m_queue_fence_callbacks)
|
||||||
|
it.second(it.first, m_queue_fence_value);
|
||||||
|
|
||||||
|
// Move to the next command allocator, this may mean switching allocator lists.
|
||||||
|
MoveToNextCommandAllocator();
|
||||||
|
ResetCommandList();
|
||||||
|
SetInitialCommandListState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3DCommandListManager::WaitForGPUCompletion()
|
||||||
|
{
|
||||||
|
// Wait for GPU to finish all outstanding work.
|
||||||
|
// This method assumes that no command lists are open.
|
||||||
|
m_queue_frame_fence_value++;
|
||||||
|
|
||||||
|
#ifdef USE_D3D12_QUEUED_COMMAND_LISTS
|
||||||
|
m_queued_command_list->QueueFenceGpuSignal(m_queue_frame_fence, m_queue_frame_fence_value);
|
||||||
|
m_queued_command_list->ProcessQueuedItems(true);
|
||||||
|
#else
|
||||||
|
CheckHR(m_command_queue->Signal(m_queue_frame_fence, m_queue_frame_fence_value));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
WaitOnCPUForFence(m_queue_frame_fence, m_queue_frame_fence_value);
|
||||||
|
|
||||||
|
// GPU is up to date with us. Therefore, it has finished with any pending resources.
|
||||||
|
ImmediatelyDestroyAllResourcesScheduledForDestruction();
|
||||||
|
|
||||||
|
// Command allocators are also up-to-date, so reset these.
|
||||||
|
ResetAllCommandAllocators();
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3DCommandListManager::PerformGpuRolloverChecks()
|
void D3DCommandListManager::PerformGpuRolloverChecks()
|
||||||
{
|
{
|
||||||
// Insert fence to measure GPU progress, ensure we aren't using in-use command allocators.
|
m_queue_frame_fence_value++;
|
||||||
if (m_queue_frame_fence->GetCompletedValue() < m_queue_frame_fence_value)
|
|
||||||
{
|
#ifdef USE_D3D12_QUEUED_COMMAND_LISTS
|
||||||
WaitOnCPUForFence(m_queue_frame_fence, m_queue_frame_fence_value);
|
m_queued_command_list->QueueFenceGpuSignal(m_queue_frame_fence, m_queue_frame_fence_value);
|
||||||
}
|
#else
|
||||||
|
CheckHR(m_command_queue->Signal(m_queue_frame_fence, m_queue_frame_fence_value));
|
||||||
|
#endif
|
||||||
|
|
||||||
// We now know that the previous 'set' of command lists has completed on GPU, and it is safe to
|
// We now know that the previous 'set' of command lists has completed on GPU, and it is safe to
|
||||||
// release resources / start back at beginning of command allocator list.
|
// release resources / start back at beginning of command allocator list.
|
||||||
|
|
||||||
// Begin Deferred Resource Destruction
|
// Begin Deferred Resource Destruction
|
||||||
UINT safe_to_delete_deferred_destruction_list = (m_current_deferred_destruction_list - 1) % m_deferred_destruction_lists.size();
|
UINT safe_to_delete_deferred_destruction_list = (m_current_deferred_destruction_list - 1) % m_deferred_destruction_lists.size();
|
||||||
|
WaitOnCPUForFence(m_queue_frame_fence, m_deferred_destruction_list_fences[safe_to_delete_deferred_destruction_list]);
|
||||||
|
|
||||||
for (UINT i = 0; i < m_deferred_destruction_lists[safe_to_delete_deferred_destruction_list].size(); i++)
|
for (UINT i = 0; i < m_deferred_destruction_lists[safe_to_delete_deferred_destruction_list].size(); i++)
|
||||||
{
|
{
|
||||||
@ -216,30 +216,37 @@ void D3DCommandListManager::PerformGpuRolloverChecks()
|
|||||||
|
|
||||||
m_deferred_destruction_lists[safe_to_delete_deferred_destruction_list].clear();
|
m_deferred_destruction_lists[safe_to_delete_deferred_destruction_list].clear();
|
||||||
|
|
||||||
|
m_deferred_destruction_list_fences[m_current_deferred_destruction_list] = m_queue_frame_fence_value;
|
||||||
m_current_deferred_destruction_list = (m_current_deferred_destruction_list + 1) % m_deferred_destruction_lists.size();
|
m_current_deferred_destruction_list = (m_current_deferred_destruction_list + 1) % m_deferred_destruction_lists.size();
|
||||||
// End Deferred Resource Destruction
|
// End Deferred Resource Destruction
|
||||||
|
|
||||||
|
|
||||||
// Begin Command Allocator Resets
|
// Begin Command Allocator Resets
|
||||||
UINT safe_to_reset_command_allocator_list = (m_current_command_allocator_list - 1) % m_command_allocator_lists.size();
|
UINT safe_to_reset_command_allocator_list = (m_current_command_allocator_list - 1) % m_command_allocator_lists.size();
|
||||||
|
WaitOnCPUForFence(m_queue_frame_fence, m_command_allocator_list_fences[safe_to_reset_command_allocator_list]);
|
||||||
|
|
||||||
for (UINT i = 0; i < m_command_allocator_lists[safe_to_reset_command_allocator_list].size(); i++)
|
for (UINT i = 0; i < m_command_allocator_lists[safe_to_reset_command_allocator_list].size(); i++)
|
||||||
{
|
{
|
||||||
CheckHR(m_command_allocator_lists[safe_to_reset_command_allocator_list][i]->Reset());
|
CheckHR(m_command_allocator_lists[safe_to_reset_command_allocator_list][i]->Reset());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_command_allocator_list_fences[m_current_command_allocator_list] = m_queue_frame_fence_value;
|
||||||
m_current_command_allocator_list = (m_current_command_allocator_list + 1) % m_command_allocator_lists.size();
|
m_current_command_allocator_list = (m_current_command_allocator_list + 1) % m_command_allocator_lists.size();
|
||||||
|
m_current_command_allocator = 0;
|
||||||
// End Command Allocator Resets
|
// End Command Allocator Resets
|
||||||
|
|
||||||
m_queue_frame_fence_value++;
|
|
||||||
#ifdef USE_D3D12_QUEUED_COMMAND_LISTS
|
|
||||||
m_queued_command_list->QueueFenceGpuSignal(m_queue_frame_fence, m_queue_frame_fence_value);
|
|
||||||
#else
|
|
||||||
CheckHR(m_command_queue->Signal(m_queue_frame_fence, m_queue_frame_fence_value));
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3DCommandListManager::ResetCommandListWithIdleCommandAllocator()
|
void D3DCommandListManager::MoveToNextCommandAllocator()
|
||||||
|
{
|
||||||
|
// Move to the next allocator in the current allocator list.
|
||||||
|
m_current_command_allocator = (m_current_command_allocator + 1) % m_command_allocator_lists[m_current_command_allocator_list].size();
|
||||||
|
|
||||||
|
// Did we wrap around? Move to the next set of allocators.
|
||||||
|
if (m_current_command_allocator == 0)
|
||||||
|
PerformGpuRolloverChecks();
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3DCommandListManager::ResetCommandList()
|
||||||
{
|
{
|
||||||
#ifdef USE_D3D12_QUEUED_COMMAND_LISTS
|
#ifdef USE_D3D12_QUEUED_COMMAND_LISTS
|
||||||
ID3D12QueuedCommandList* command_list = m_queued_command_list;
|
ID3D12QueuedCommandList* command_list = m_queued_command_list;
|
||||||
@ -268,6 +275,20 @@ void D3DCommandListManager::ImmediatelyDestroyAllResourcesScheduledForDestructio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void D3DCommandListManager::ResetAllCommandAllocators()
|
||||||
|
{
|
||||||
|
for (auto& allocator_list : m_command_allocator_lists)
|
||||||
|
{
|
||||||
|
for (auto& allocator : allocator_list)
|
||||||
|
allocator->Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move back to the start, using the first allocator of first list.
|
||||||
|
m_current_command_allocator = 0;
|
||||||
|
m_current_command_allocator_list = 0;
|
||||||
|
m_current_deferred_destruction_list = 0;
|
||||||
|
}
|
||||||
|
|
||||||
void D3DCommandListManager::ClearQueueAndWaitForCompletionOfInflightWork()
|
void D3DCommandListManager::ClearQueueAndWaitForCompletionOfInflightWork()
|
||||||
{
|
{
|
||||||
// Wait for GPU to finish all outstanding work.
|
// Wait for GPU to finish all outstanding work.
|
||||||
@ -305,8 +326,10 @@ D3DCommandListManager::~D3DCommandListManager()
|
|||||||
|
|
||||||
void D3DCommandListManager::WaitOnCPUForFence(ID3D12Fence* fence, UINT64 fence_value)
|
void D3DCommandListManager::WaitOnCPUForFence(ID3D12Fence* fence, UINT64 fence_value)
|
||||||
{
|
{
|
||||||
CheckHR(fence->SetEventOnCompletion(fence_value, m_wait_on_cpu_fence_event));
|
if (fence->GetCompletedValue() >= fence_value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
CheckHR(fence->SetEventOnCompletion(fence_value, m_wait_on_cpu_fence_event));
|
||||||
WaitForSingleObject(m_wait_on_cpu_fence_event, INFINITE);
|
WaitForSingleObject(m_wait_on_cpu_fence_event, INFINITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,11 +38,10 @@ public:
|
|||||||
void ExecuteQueuedWork(bool wait_for_gpu_completion = false);
|
void ExecuteQueuedWork(bool wait_for_gpu_completion = false);
|
||||||
void ExecuteQueuedWorkAndPresent(IDXGISwapChain* swap_chain, UINT sync_interval, UINT flags);
|
void ExecuteQueuedWorkAndPresent(IDXGISwapChain* swap_chain, UINT sync_interval, UINT flags);
|
||||||
|
|
||||||
void WaitForQueuedWorkToBeExecutedOnGPU();
|
|
||||||
|
|
||||||
void ClearQueueAndWaitForCompletionOfInflightWork();
|
void ClearQueueAndWaitForCompletionOfInflightWork();
|
||||||
void DestroyResourceAfterCurrentCommandListExecuted(ID3D12Resource* resource);
|
void DestroyResourceAfterCurrentCommandListExecuted(ID3D12Resource* resource);
|
||||||
void ImmediatelyDestroyAllResourcesScheduledForDestruction();
|
void ImmediatelyDestroyAllResourcesScheduledForDestruction();
|
||||||
|
void ResetAllCommandAllocators();
|
||||||
|
|
||||||
void SetCommandListDirtyState(unsigned int command_list_state, bool dirty);
|
void SetCommandListDirtyState(unsigned int command_list_state, bool dirty);
|
||||||
bool GetCommandListDirtyState(COMMAND_LIST_STATE command_list_state) const;
|
bool GetCommandListDirtyState(COMMAND_LIST_STATE command_list_state) const;
|
||||||
@ -65,8 +64,10 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
void WaitForGPUCompletion();
|
||||||
void PerformGpuRolloverChecks();
|
void PerformGpuRolloverChecks();
|
||||||
void ResetCommandListWithIdleCommandAllocator();
|
void MoveToNextCommandAllocator();
|
||||||
|
void ResetCommandList();
|
||||||
|
|
||||||
unsigned int m_command_list_dirty_state = UINT_MAX;
|
unsigned int m_command_list_dirty_state = UINT_MAX;
|
||||||
D3D_PRIMITIVE_TOPOLOGY m_command_list_current_topology = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED;
|
D3D_PRIMITIVE_TOPOLOGY m_command_list_current_topology = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED;
|
||||||
@ -85,6 +86,7 @@ private:
|
|||||||
UINT m_current_command_allocator;
|
UINT m_current_command_allocator;
|
||||||
UINT m_current_command_allocator_list;
|
UINT m_current_command_allocator_list;
|
||||||
std::array<std::vector<ID3D12CommandAllocator*>, 2> m_command_allocator_lists;
|
std::array<std::vector<ID3D12CommandAllocator*>, 2> m_command_allocator_lists;
|
||||||
|
std::array<UINT64, 2> m_command_allocator_list_fences;
|
||||||
|
|
||||||
ID3D12GraphicsCommandList* m_backing_command_list;
|
ID3D12GraphicsCommandList* m_backing_command_list;
|
||||||
ID3D12QueuedCommandList* m_queued_command_list;
|
ID3D12QueuedCommandList* m_queued_command_list;
|
||||||
@ -93,6 +95,7 @@ private:
|
|||||||
|
|
||||||
UINT m_current_deferred_destruction_list;
|
UINT m_current_deferred_destruction_list;
|
||||||
std::array<std::vector<ID3D12Resource*>, 2> m_deferred_destruction_lists;
|
std::array<std::vector<ID3D12Resource*>, 2> m_deferred_destruction_lists;
|
||||||
|
std::array<UINT64, 2> m_deferred_destruction_list_fences;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
Loading…
x
Reference in New Issue
Block a user