From 649b94338efc6190490d542c0b63e298216f0a55 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Wed, 17 Feb 2016 14:57:57 +1000 Subject: [PATCH] D3D12: Cleanup/refactoring of teardown process --- Source/Core/VideoBackends/D3D12/D3DBase.cpp | 10 +-- .../D3D12/D3DCommandListManager.cpp | 87 ++++++++----------- .../D3D12/D3DCommandListManager.h | 9 +- .../D3D12/D3DQueuedCommandList.cpp | 23 ++--- .../D3D12/D3DQueuedCommandList.h | 5 +- 5 files changed, 57 insertions(+), 77 deletions(-) diff --git a/Source/Core/VideoBackends/D3D12/D3DBase.cpp b/Source/Core/VideoBackends/D3D12/D3DBase.cpp index eebbdcbba8..01d5d9a989 100644 --- a/Source/Core/VideoBackends/D3D12/D3DBase.cpp +++ b/Source/Core/VideoBackends/D3D12/D3DBase.cpp @@ -715,7 +715,7 @@ void CreateRootSignatures() void WaitForOutstandingRenderingToComplete() { - command_list_mgr->ClearQueueAndWaitForCompletionOfInflightWork(); + command_list_mgr->ExecuteQueuedWork(true); } void Close() @@ -731,8 +731,6 @@ void Close() D3D::CleanupPersistentD3DTextureResources(); - command_list_mgr->ImmediatelyDestroyAllResourcesScheduledForDestruction(); - SAFE_RELEASE(s_swap_chain); command_list_mgr.reset(); @@ -816,15 +814,15 @@ unsigned int GetMaxTextureSize() void Reset() { - command_list_mgr->ExecuteQueuedWork(true); - // release all back buffer references for (UINT i = 0; i < ARRAYSIZE(s_backbuf); i++) { SAFE_RELEASE(s_backbuf[i]); } - D3D::command_list_mgr->ImmediatelyDestroyAllResourcesScheduledForDestruction(); + // Block until all commands have finished. + // This will also final-release all pending resources (including the backbuffer above) + command_list_mgr->ExecuteQueuedWork(true); // resize swapchain buffers RECT client; diff --git a/Source/Core/VideoBackends/D3D12/D3DCommandListManager.cpp b/Source/Core/VideoBackends/D3D12/D3DCommandListManager.cpp index 4fe6875f13..07c9b65eab 100644 --- a/Source/Core/VideoBackends/D3D12/D3DCommandListManager.cpp +++ b/Source/Core/VideoBackends/D3D12/D3DCommandListManager.cpp @@ -113,12 +113,10 @@ void D3DCommandListManager::ExecuteQueuedWork(bool wait_for_gpu_completion) m_queue_fence_value++; #ifdef USE_D3D12_QUEUED_COMMAND_LISTS - CheckHR(m_queued_command_list->Close()); + m_queued_command_list->Close(); m_queued_command_list->QueueExecute(); - m_queued_command_list->QueueFenceGpuSignal(m_queue_fence, m_queue_fence_value); - - m_queued_command_list->ProcessQueuedItems(wait_for_gpu_completion); + m_queued_command_list->ProcessQueuedItems(wait_for_gpu_completion, wait_for_gpu_completion); #else CheckHR(m_backing_command_list->Close()); @@ -145,7 +143,7 @@ void D3DCommandListManager::ExecuteQueuedWorkAndPresent(IDXGISwapChain* swap_cha m_queue_fence_value++; #ifdef USE_D3D12_QUEUED_COMMAND_LISTS - CheckHR(m_queued_command_list->Close()); + m_queued_command_list->Close(); m_queued_command_list->QueueExecute(); m_queued_command_list->QueuePresent(swap_chain, sync_interval, flags); m_queued_command_list->QueueFenceGpuSignal(m_queue_fence, m_queue_fence_value); @@ -170,6 +168,31 @@ void D3DCommandListManager::ExecuteQueuedWorkAndPresent(IDXGISwapChain* swap_cha SetInitialCommandListState(); } +void D3DCommandListManager::DestroyAllPendingResources() +{ + for (auto& destruction_list : m_deferred_destruction_lists) + { + for (auto& resource : destruction_list) + resource->Release(); + + destruction_list.clear(); + } +} + +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::WaitForGPUCompletion() { // Wait for GPU to finish all outstanding work. @@ -186,13 +209,13 @@ void D3DCommandListManager::WaitForGPUCompletion() 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(); + DestroyAllPendingResources(); // Command allocators are also up-to-date, so reset these. ResetAllCommandAllocators(); } -void D3DCommandListManager::PerformGpuRolloverChecks() +void D3DCommandListManager::PerformGPURolloverChecks() { m_queue_frame_fence_value++; @@ -243,7 +266,7 @@ void D3DCommandListManager::MoveToNextCommandAllocator() // Did we wrap around? Move to the next set of allocators. if (m_current_command_allocator == 0) - PerformGpuRolloverChecks(); + PerformGPURolloverChecks(); } void D3DCommandListManager::ResetCommandList() @@ -264,52 +287,18 @@ void D3DCommandListManager::DestroyResourceAfterCurrentCommandListExecuted(ID3D1 m_deferred_destruction_lists[m_current_deferred_destruction_list].push_back(resource); } -void D3DCommandListManager::ImmediatelyDestroyAllResourcesScheduledForDestruction() -{ - for (auto& destruction_list : m_deferred_destruction_lists) - { - for (auto& resource : destruction_list) - resource->Release(); - - destruction_list.clear(); - } -} - -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() -{ - // Wait for GPU to finish all outstanding work. - m_queue_fence_value++; -#ifdef USE_D3D12_QUEUED_COMMAND_LISTS - m_queued_command_list->ClearQueue(); // Waits for currently-processing work to finish, then clears queue. - m_queued_command_list->QueueFenceGpuSignal(m_queue_fence, m_queue_fence_value); - m_queued_command_list->ProcessQueuedItems(true); -#else - CheckHR(m_command_queue->Signal(m_queue_fence, m_queue_fence_value)); -#endif - WaitOnCPUForFence(m_queue_fence, m_queue_fence_value); -} - D3DCommandListManager::~D3DCommandListManager() { - ImmediatelyDestroyAllResourcesScheduledForDestruction(); - #ifdef USE_D3D12_QUEUED_COMMAND_LISTS + // Wait for background thread to exit. m_queued_command_list->Release(); #endif + + // The command list will still be open, close it before destroying. + m_backing_command_list->Close(); + + DestroyAllPendingResources(); + m_backing_command_list->Release(); for (auto& allocator_list : m_command_allocator_lists) diff --git a/Source/Core/VideoBackends/D3D12/D3DCommandListManager.h b/Source/Core/VideoBackends/D3D12/D3DCommandListManager.h index 3f932dfc0e..f404a8a0ac 100644 --- a/Source/Core/VideoBackends/D3D12/D3DCommandListManager.h +++ b/Source/Core/VideoBackends/D3D12/D3DCommandListManager.h @@ -38,10 +38,7 @@ public: void ExecuteQueuedWork(bool wait_for_gpu_completion = false); void ExecuteQueuedWorkAndPresent(IDXGISwapChain* swap_chain, UINT sync_interval, UINT flags); - void ClearQueueAndWaitForCompletionOfInflightWork(); void DestroyResourceAfterCurrentCommandListExecuted(ID3D12Resource* resource); - void ImmediatelyDestroyAllResourcesScheduledForDestruction(); - void ResetAllCommandAllocators(); void SetCommandListDirtyState(unsigned int command_list_state, bool dirty); bool GetCommandListDirtyState(COMMAND_LIST_STATE command_list_state) const; @@ -63,9 +60,11 @@ public: void WaitOnCPUForFence(ID3D12Fence* fence, UINT64 fence_value); private: - + void DestroyAllPendingResources(); + void ResetAllCommandAllocators(); void WaitForGPUCompletion(); - void PerformGpuRolloverChecks(); + + void PerformGPURolloverChecks(); void MoveToNextCommandAllocator(); void ResetCommandList(); diff --git a/Source/Core/VideoBackends/D3D12/D3DQueuedCommandList.cpp b/Source/Core/VideoBackends/D3D12/D3DQueuedCommandList.cpp index 16be35d021..4bfb58735a 100644 --- a/Source/Core/VideoBackends/D3D12/D3DQueuedCommandList.cpp +++ b/Source/Core/VideoBackends/D3D12/D3DQueuedCommandList.cpp @@ -25,8 +25,6 @@ void ID3D12QueuedCommandList::BackgroundThreadFunction(ID3D12QueuedCommandList* while (true) { WaitForSingleObject(parent_queued_command_list->m_begin_execution_event, INFINITE); - if (parent_queued_command_list->m_background_thread_exit.load()) - break; byte* item = &queue_array[queue_array_front]; @@ -341,6 +339,7 @@ void ID3D12QueuedCommandList::BackgroundThreadFunction(ID3D12QueuedCommandList* bool eligible_to_move_to_front_of_queue = reinterpret_cast(item)->Stop.eligible_to_move_to_front_of_queue; bool signal_stop_event = reinterpret_cast(item)->Stop.signal_stop_event; + bool terminate_worker_thread = reinterpret_cast(item)->Stop.terminate_worker_thread; item += BufferOffsetForQueueItemType(); @@ -354,6 +353,9 @@ void ID3D12QueuedCommandList::BackgroundThreadFunction(ID3D12QueuedCommandList* SetEvent(parent_queued_command_list->m_stop_execution_event); } + if (terminate_worker_thread) + return; + goto exitLoop; } } @@ -380,8 +382,8 @@ ID3D12QueuedCommandList::ID3D12QueuedCommandList(ID3D12GraphicsCommandList* back ID3D12QueuedCommandList::~ID3D12QueuedCommandList() { - m_background_thread_exit.store(true); - ReleaseSemaphore(m_begin_execution_event, 1, nullptr); + // Kick worker thread, and tell it to exit. + ProcessQueuedItems(true, true, true); m_background_thread.join(); CloseHandle(m_begin_execution_event); @@ -463,22 +465,14 @@ void ID3D12QueuedCommandList::QueuePresent(IDXGISwapChain* swap_chain, UINT sync CheckForOverflow(); } -void ID3D12QueuedCommandList::ClearQueue() -{ - // Drain semaphore to ensure no new previously queued work executes (though inflight work may continue). - while (WaitForSingleObject(m_begin_execution_event, 0) != WAIT_TIMEOUT) { } - - // Assume that any inflight queued work will complete within 100ms. This is a safe assumption. - Sleep(100); -} - -void ID3D12QueuedCommandList::ProcessQueuedItems(bool eligible_to_move_to_front_of_queue, bool wait_for_stop) +void ID3D12QueuedCommandList::ProcessQueuedItems(bool eligible_to_move_to_front_of_queue, bool wait_for_stop, bool terminate_worker_thread) { D3DQueueItem item = {}; item.Type = D3DQueueItemType::Stop; item.Stop.eligible_to_move_to_front_of_queue = eligible_to_move_to_front_of_queue; item.Stop.signal_stop_event = wait_for_stop; + item.Stop.terminate_worker_thread = terminate_worker_thread; *reinterpret_cast(m_queue_array_back) = item; @@ -502,6 +496,7 @@ void ID3D12QueuedCommandList::ProcessQueuedItems(bool eligible_to_move_to_front_ if (wait_for_stop) { WaitForSingleObject(m_stop_execution_event, INFINITE); + ResetEvent(m_stop_execution_event); } } diff --git a/Source/Core/VideoBackends/D3D12/D3DQueuedCommandList.h b/Source/Core/VideoBackends/D3D12/D3DQueuedCommandList.h index 6b1ca4306c..1ca0334dba 100644 --- a/Source/Core/VideoBackends/D3D12/D3DQueuedCommandList.h +++ b/Source/Core/VideoBackends/D3D12/D3DQueuedCommandList.h @@ -211,6 +211,7 @@ struct StopArguments { bool eligible_to_move_to_front_of_queue; bool signal_stop_event; + bool terminate_worker_thread; }; struct D3DQueueItem @@ -255,13 +256,12 @@ public: ID3D12QueuedCommandList(ID3D12GraphicsCommandList* backing_command_list, ID3D12CommandQueue* backing_command_queue); - void ProcessQueuedItems(bool eligible_to_move_to_front_of_queue = false, bool wait_for_stop = false); + void ProcessQueuedItems(bool eligible_to_move_to_front_of_queue = false, bool wait_for_stop = false, bool terminate_worker_thread = false); void QueueExecute(); void QueueFenceGpuSignal(ID3D12Fence* fence_to_signal, UINT64 fence_value); void QueueFenceCpuSignal(ID3D12Fence* fence_to_signal, UINT64 fence_value); void QueuePresent(IDXGISwapChain* swap_chain, UINT sync_interval, UINT flags); - void ClearQueue(); // IUnknown methods @@ -621,7 +621,6 @@ private: byte* m_queue_array_back_at_start_of_frame = m_queue_array_back; std::thread m_background_thread; - std::atomic_bool m_background_thread_exit; HANDLE m_begin_execution_event; HANDLE m_stop_execution_event;