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()
{
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;

View File

@ -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)

View File

@ -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();

View File

@ -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<D3DQueueItem*>(item)->Stop.eligible_to_move_to_front_of_queue;
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>();
@ -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<D3DQueueItem*>(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);
}
}

View File

@ -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;