D3D12: Cleanup/refactoring of teardown process

This commit is contained in:
Stenzek 2016-02-17 14:57:57 +10:00
parent 759b77474d
commit 649b94338e
5 changed files with 57 additions and 77 deletions

View File

@ -715,7 +715,7 @@ void CreateRootSignatures()
void WaitForOutstandingRenderingToComplete() void WaitForOutstandingRenderingToComplete()
{ {
command_list_mgr->ClearQueueAndWaitForCompletionOfInflightWork(); command_list_mgr->ExecuteQueuedWork(true);
} }
void Close() void Close()
@ -731,8 +731,6 @@ void Close()
D3D::CleanupPersistentD3DTextureResources(); D3D::CleanupPersistentD3DTextureResources();
command_list_mgr->ImmediatelyDestroyAllResourcesScheduledForDestruction();
SAFE_RELEASE(s_swap_chain); SAFE_RELEASE(s_swap_chain);
command_list_mgr.reset(); command_list_mgr.reset();
@ -816,15 +814,15 @@ unsigned int GetMaxTextureSize()
void Reset() void Reset()
{ {
command_list_mgr->ExecuteQueuedWork(true);
// release all back buffer references // release all back buffer references
for (UINT i = 0; i < ARRAYSIZE(s_backbuf); i++) for (UINT i = 0; i < ARRAYSIZE(s_backbuf); i++)
{ {
SAFE_RELEASE(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 // resize swapchain buffers
RECT client; RECT client;

View File

@ -113,12 +113,10 @@ void D3DCommandListManager::ExecuteQueuedWork(bool wait_for_gpu_completion)
m_queue_fence_value++; m_queue_fence_value++;
#ifdef USE_D3D12_QUEUED_COMMAND_LISTS #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->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(wait_for_gpu_completion, wait_for_gpu_completion);
m_queued_command_list->ProcessQueuedItems(wait_for_gpu_completion);
#else #else
CheckHR(m_backing_command_list->Close()); CheckHR(m_backing_command_list->Close());
@ -145,7 +143,7 @@ void D3DCommandListManager::ExecuteQueuedWorkAndPresent(IDXGISwapChain* swap_cha
m_queue_fence_value++; m_queue_fence_value++;
#ifdef USE_D3D12_QUEUED_COMMAND_LISTS #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->QueueExecute();
m_queued_command_list->QueuePresent(swap_chain, sync_interval, flags); m_queued_command_list->QueuePresent(swap_chain, sync_interval, flags);
m_queued_command_list->QueueFenceGpuSignal(m_queue_fence, m_queue_fence_value); m_queued_command_list->QueueFenceGpuSignal(m_queue_fence, m_queue_fence_value);
@ -170,6 +168,31 @@ void D3DCommandListManager::ExecuteQueuedWorkAndPresent(IDXGISwapChain* swap_cha
SetInitialCommandListState(); 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() void D3DCommandListManager::WaitForGPUCompletion()
{ {
// Wait for GPU to finish all outstanding work. // 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); 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. // 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. // Command allocators are also up-to-date, so reset these.
ResetAllCommandAllocators(); ResetAllCommandAllocators();
} }
void D3DCommandListManager::PerformGpuRolloverChecks() void D3DCommandListManager::PerformGPURolloverChecks()
{ {
m_queue_frame_fence_value++; m_queue_frame_fence_value++;
@ -243,7 +266,7 @@ void D3DCommandListManager::MoveToNextCommandAllocator()
// Did we wrap around? Move to the next set of allocators. // Did we wrap around? Move to the next set of allocators.
if (m_current_command_allocator == 0) if (m_current_command_allocator == 0)
PerformGpuRolloverChecks(); PerformGPURolloverChecks();
} }
void D3DCommandListManager::ResetCommandList() void D3DCommandListManager::ResetCommandList()
@ -264,52 +287,18 @@ void D3DCommandListManager::DestroyResourceAfterCurrentCommandListExecuted(ID3D1
m_deferred_destruction_lists[m_current_deferred_destruction_list].push_back(resource); 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() D3DCommandListManager::~D3DCommandListManager()
{ {
ImmediatelyDestroyAllResourcesScheduledForDestruction();
#ifdef USE_D3D12_QUEUED_COMMAND_LISTS #ifdef USE_D3D12_QUEUED_COMMAND_LISTS
// Wait for background thread to exit.
m_queued_command_list->Release(); m_queued_command_list->Release();
#endif #endif
// The command list will still be open, close it before destroying.
m_backing_command_list->Close();
DestroyAllPendingResources();
m_backing_command_list->Release(); m_backing_command_list->Release();
for (auto& allocator_list : m_command_allocator_lists) for (auto& allocator_list : m_command_allocator_lists)

View File

@ -38,10 +38,7 @@ 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 ClearQueueAndWaitForCompletionOfInflightWork();
void DestroyResourceAfterCurrentCommandListExecuted(ID3D12Resource* resource); void DestroyResourceAfterCurrentCommandListExecuted(ID3D12Resource* resource);
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;
@ -63,9 +60,11 @@ public:
void WaitOnCPUForFence(ID3D12Fence* fence, UINT64 fence_value); void WaitOnCPUForFence(ID3D12Fence* fence, UINT64 fence_value);
private: private:
void DestroyAllPendingResources();
void ResetAllCommandAllocators();
void WaitForGPUCompletion(); void WaitForGPUCompletion();
void PerformGpuRolloverChecks();
void PerformGPURolloverChecks();
void MoveToNextCommandAllocator(); void MoveToNextCommandAllocator();
void ResetCommandList(); void ResetCommandList();

View File

@ -25,8 +25,6 @@ void ID3D12QueuedCommandList::BackgroundThreadFunction(ID3D12QueuedCommandList*
while (true) while (true)
{ {
WaitForSingleObject(parent_queued_command_list->m_begin_execution_event, INFINITE); 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]; 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<D3DQueueItem*>(item)->Stop.eligible_to_move_to_front_of_queue; bool eligible_to_move_to_front_of_queue = reinterpret_cast<D3DQueueItem*>(item)->Stop.eligible_to_move_to_front_of_queue;
bool signal_stop_event = reinterpret_cast<D3DQueueItem*>(item)->Stop.signal_stop_event; bool signal_stop_event = reinterpret_cast<D3DQueueItem*>(item)->Stop.signal_stop_event;
bool terminate_worker_thread = reinterpret_cast<D3DQueueItem*>(item)->Stop.terminate_worker_thread;
item += BufferOffsetForQueueItemType<StopArguments>(); item += BufferOffsetForQueueItemType<StopArguments>();
@ -354,6 +353,9 @@ void ID3D12QueuedCommandList::BackgroundThreadFunction(ID3D12QueuedCommandList*
SetEvent(parent_queued_command_list->m_stop_execution_event); SetEvent(parent_queued_command_list->m_stop_execution_event);
} }
if (terminate_worker_thread)
return;
goto exitLoop; goto exitLoop;
} }
} }
@ -380,8 +382,8 @@ ID3D12QueuedCommandList::ID3D12QueuedCommandList(ID3D12GraphicsCommandList* back
ID3D12QueuedCommandList::~ID3D12QueuedCommandList() ID3D12QueuedCommandList::~ID3D12QueuedCommandList()
{ {
m_background_thread_exit.store(true); // Kick worker thread, and tell it to exit.
ReleaseSemaphore(m_begin_execution_event, 1, nullptr); ProcessQueuedItems(true, true, true);
m_background_thread.join(); m_background_thread.join();
CloseHandle(m_begin_execution_event); CloseHandle(m_begin_execution_event);
@ -463,22 +465,14 @@ void ID3D12QueuedCommandList::QueuePresent(IDXGISwapChain* swap_chain, UINT sync
CheckForOverflow(); CheckForOverflow();
} }
void ID3D12QueuedCommandList::ClearQueue() void ID3D12QueuedCommandList::ProcessQueuedItems(bool eligible_to_move_to_front_of_queue, bool wait_for_stop, bool terminate_worker_thread)
{
// 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)
{ {
D3DQueueItem item = {}; D3DQueueItem item = {};
item.Type = D3DQueueItemType::Stop; item.Type = D3DQueueItemType::Stop;
item.Stop.eligible_to_move_to_front_of_queue = eligible_to_move_to_front_of_queue; 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.signal_stop_event = wait_for_stop;
item.Stop.terminate_worker_thread = terminate_worker_thread;
*reinterpret_cast<D3DQueueItem*>(m_queue_array_back) = item; *reinterpret_cast<D3DQueueItem*>(m_queue_array_back) = item;
@ -502,6 +496,7 @@ void ID3D12QueuedCommandList::ProcessQueuedItems(bool eligible_to_move_to_front_
if (wait_for_stop) if (wait_for_stop)
{ {
WaitForSingleObject(m_stop_execution_event, INFINITE); WaitForSingleObject(m_stop_execution_event, INFINITE);
ResetEvent(m_stop_execution_event);
} }
} }

View File

@ -211,6 +211,7 @@ struct StopArguments
{ {
bool eligible_to_move_to_front_of_queue; bool eligible_to_move_to_front_of_queue;
bool signal_stop_event; bool signal_stop_event;
bool terminate_worker_thread;
}; };
struct D3DQueueItem struct D3DQueueItem
@ -255,13 +256,12 @@ public:
ID3D12QueuedCommandList(ID3D12GraphicsCommandList* backing_command_list, ID3D12CommandQueue* backing_command_queue); 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 QueueExecute();
void QueueFenceGpuSignal(ID3D12Fence* fence_to_signal, UINT64 fence_value); void QueueFenceGpuSignal(ID3D12Fence* fence_to_signal, UINT64 fence_value);
void QueueFenceCpuSignal(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 QueuePresent(IDXGISwapChain* swap_chain, UINT sync_interval, UINT flags);
void ClearQueue();
// IUnknown methods // IUnknown methods
@ -621,7 +621,6 @@ private:
byte* m_queue_array_back_at_start_of_frame = m_queue_array_back; byte* m_queue_array_back_at_start_of_frame = m_queue_array_back;
std::thread m_background_thread; std::thread m_background_thread;
std::atomic_bool m_background_thread_exit;
HANDLE m_begin_execution_event; HANDLE m_begin_execution_event;
HANDLE m_stop_execution_event; HANDLE m_stop_execution_event;