[D3D12] Don't use D3D12Context for command processor fence

This commit is contained in:
Triang3l
2019-10-28 10:49:32 +03:00
parent b4af63fe31
commit d3b6f71ae1
16 changed files with 337 additions and 424 deletions

View File

@@ -16,6 +16,7 @@
#include "xenia/base/math.h"
#include "xenia/ui/d3d12/d3d12_immediate_drawer.h"
#include "xenia/ui/d3d12/d3d12_provider.h"
#include "xenia/ui/d3d12/d3d12_util.h"
#include "xenia/ui/window.h"
DEFINE_bool(d3d12_random_clear_color, false,
@@ -25,6 +26,9 @@ namespace xe {
namespace ui {
namespace d3d12 {
constexpr uint32_t D3D12Context::kSwapCommandListCount;
constexpr uint32_t D3D12Context::kSwapChainBufferCount;
D3D12Context::D3D12Context(D3D12Provider* provider, Window* target_window)
: GraphicsContext(provider, target_window) {}
@@ -38,23 +42,24 @@ bool D3D12Context::Initialize() {
context_lost_ = false;
current_frame_ = 1;
// No frames have been completed yet.
last_completed_frame_ = 0;
// Keep in sync with the modulo because why not.
current_queue_frame_ = 1;
// Create fences for synchronization of reuse and destruction of transient
// objects (like command lists) and for global shutdown.
for (uint32_t i = 0; i < kQueuedFrames; ++i) {
fences_[i] = CPUFence::Create(device, direct_queue);
if (fences_[i] == nullptr) {
if (target_window_) {
swap_fence_current_value_ = 1;
swap_fence_completed_value_ = 0;
swap_fence_completion_event_ = CreateEvent(nullptr, false, false, nullptr);
if (swap_fence_completion_event_ == nullptr) {
XELOGE("Failed to create the composition fence completion event");
Shutdown();
return false;
}
// Create a fence for transient resources of compositing.
if (FAILED(device->CreateFence(0, D3D12_FENCE_FLAG_NONE,
IID_PPV_ARGS(&swap_fence_)))) {
XELOGE("Failed to create the composition fence");
Shutdown();
return false;
}
}
if (target_window_) {
// Create the swap chain.
swap_chain_width_ = target_window_->scaled_width();
swap_chain_height_ = target_window_->scaled_height();
DXGI_SWAP_CHAIN_DESC1 swap_chain_desc;
@@ -109,7 +114,7 @@ bool D3D12Context::Initialize() {
}
// Create command lists for compositing.
for (uint32_t i = 0; i < kQueuedFrames; ++i) {
for (uint32_t i = 0; i < kSwapCommandListCount; ++i) {
swap_command_lists_[i] = CommandList::Create(
device, direct_queue, D3D12_COMMAND_LIST_TYPE_DIRECT);
if (swap_command_lists_[i] == nullptr) {
@@ -126,7 +131,6 @@ bool D3D12Context::Initialize() {
}
}
initialized_fully_ = true;
return true;
}
@@ -159,29 +163,30 @@ bool D3D12Context::InitializeSwapChainBuffers() {
}
void D3D12Context::Shutdown() {
if (initialized_fully_ && !context_lost_) {
AwaitAllFramesCompletion();
if (!context_lost_ && swap_fence_ &&
swap_fence_->GetCompletedValue() + 1 < swap_fence_current_value_) {
swap_fence_->SetEventOnCompletion(swap_fence_current_value_ - 1,
swap_fence_completion_event_);
WaitForSingleObject(swap_fence_completion_event_, INFINITE);
}
initialized_fully_ = false;
immediate_drawer_.reset();
if (swap_chain_ != nullptr) {
for (uint32_t i = 0; i < kQueuedFrames; ++i) {
swap_command_lists_[i].reset();
}
for (uint32_t i = 0; i < kSwapCommandListCount; ++i) {
swap_command_lists_[i].reset();
}
if (swap_chain_) {
for (uint32_t i = 0; i < kSwapChainBufferCount; ++i) {
auto& buffer = swap_chain_buffers_[i];
if (buffer == nullptr) {
auto& swap_chain_buffer = swap_chain_buffers_[i];
if (!swap_chain_buffer) {
break;
}
buffer->Release();
buffer = nullptr;
swap_chain_buffer->Release();
swap_chain_buffer = nullptr;
}
if (swap_chain_rtv_heap_ != nullptr) {
if (swap_chain_rtv_heap_) {
swap_chain_rtv_heap_->Release();
swap_chain_rtv_heap_ = nullptr;
}
@@ -189,9 +194,14 @@ void D3D12Context::Shutdown() {
swap_chain_->Release();
}
for (uint32_t i = 0; i < kQueuedFrames; ++i) {
fences_[i].reset();
// First release the fence since it may reference the event.
util::ReleaseAndNull(swap_fence_);
if (swap_fence_completion_event_) {
CloseHandle(swap_fence_completion_event_);
swap_fence_completion_event_ = nullptr;
}
swap_fence_current_value_ = 1;
swap_fence_completed_value_ = 0;
}
ImmediateDrawer* D3D12Context::immediate_drawer() {
@@ -205,119 +215,125 @@ bool D3D12Context::MakeCurrent() { return true; }
void D3D12Context::ClearCurrent() {}
void D3D12Context::BeginSwap() {
if (context_lost_) {
if (!target_window_ || context_lost_) {
return;
}
// Await the availability of transient objects for the new frame.
// The frame number is incremented in EndSwap so it can be treated the same
// way both when inside a frame and when outside of it (it's tied to actual
// submissions).
fences_[current_queue_frame_]->Await();
// Update the completed frame if didn't explicitly await all queued frames.
if (last_completed_frame_ + kQueuedFrames < current_frame_) {
last_completed_frame_ = current_frame_ - kQueuedFrames;
}
if (target_window_ != nullptr) {
// Resize the swap chain if the window is resized.
uint32_t target_window_width = target_window_->scaled_width();
uint32_t target_window_height = target_window_->scaled_height();
if (swap_chain_width_ != target_window_width ||
swap_chain_height_ != target_window_height) {
// Await the completion of swap chain use.
// Context loss is also faked if resizing fails. In this case, before the
// context is shut down to be recreated, frame completion must be awaited
// (this isn't done if the context is truly lost).
AwaitAllFramesCompletion();
// All buffer references must be released before resizing.
for (uint32_t i = 0; i < kSwapChainBufferCount; ++i) {
swap_chain_buffers_[i]->Release();
swap_chain_buffers_[i] = nullptr;
}
if (FAILED(swap_chain_->ResizeBuffers(
kSwapChainBufferCount, target_window_width, target_window_height,
kSwapChainFormat, 0))) {
context_lost_ = true;
return;
}
swap_chain_width_ = target_window_width;
swap_chain_height_ = target_window_height;
if (!InitializeSwapChainBuffers()) {
context_lost_ = true;
return;
}
// Resize the swap chain if the window is resized.
uint32_t target_window_width = target_window_->scaled_width();
uint32_t target_window_height = target_window_->scaled_height();
if (swap_chain_width_ != target_window_width ||
swap_chain_height_ != target_window_height) {
// Await the completion of swap chain use.
// Context loss is also faked if resizing fails. In this case, before the
// context is shut down to be recreated, frame completion must be awaited
// (this isn't done if the context is truly lost).
if (swap_fence_completed_value_ + 1 < swap_fence_current_value_) {
swap_fence_->SetEventOnCompletion(swap_fence_current_value_ - 1,
swap_fence_completion_event_);
WaitForSingleObject(swap_fence_completion_event_, INFINITE);
swap_fence_completed_value_ = swap_fence_current_value_ - 1;
}
// Bind the back buffer as a render target and clear it.
auto command_list = swap_command_lists_[current_queue_frame_].get();
auto graphics_command_list = command_list->BeginRecording();
D3D12_RESOURCE_BARRIER barrier;
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barrier.Transition.pResource =
swap_chain_buffers_[swap_chain_back_buffer_index_];
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
graphics_command_list->ResourceBarrier(1, &barrier);
D3D12_CPU_DESCRIPTOR_HANDLE back_buffer_rtv = GetSwapChainBackBufferRTV();
graphics_command_list->OMSetRenderTargets(1, &back_buffer_rtv, TRUE,
nullptr);
float clear_color[4];
if (cvars::d3d12_random_clear_color) {
clear_color[0] =
rand() / float(RAND_MAX); // NOLINT(runtime/threadsafe_fn)
clear_color[1] = 1.0f;
clear_color[2] = 0.0f;
} else {
clear_color[0] = 238.0f / 255.0f;
clear_color[1] = 238.0f / 255.0f;
clear_color[2] = 238.0f / 255.0f;
// All buffer references must be released before resizing.
for (uint32_t i = 0; i < kSwapChainBufferCount; ++i) {
swap_chain_buffers_[i]->Release();
swap_chain_buffers_[i] = nullptr;
}
clear_color[3] = 1.0f;
graphics_command_list->ClearRenderTargetView(back_buffer_rtv, clear_color,
0, nullptr);
}
}
void D3D12Context::EndSwap() {
if (context_lost_) {
return;
}
if (target_window_ != nullptr) {
// Switch the back buffer to presentation state.
auto command_list = swap_command_lists_[current_queue_frame_].get();
auto graphics_command_list = command_list->GetCommandList();
D3D12_RESOURCE_BARRIER barrier;
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barrier.Transition.pResource =
swap_chain_buffers_[swap_chain_back_buffer_index_];
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
graphics_command_list->ResourceBarrier(1, &barrier);
command_list->Execute();
// Present and check if the context was lost.
HRESULT result = swap_chain_->Present(0, 0);
if (result == DXGI_ERROR_DEVICE_RESET ||
result == DXGI_ERROR_DEVICE_REMOVED) {
if (FAILED(swap_chain_->ResizeBuffers(
kSwapChainBufferCount, target_window_width, target_window_height,
kSwapChainFormat, 0))) {
context_lost_ = true;
return;
}
swap_chain_width_ = target_window_width;
swap_chain_height_ = target_window_height;
if (!InitializeSwapChainBuffers()) {
context_lost_ = true;
return;
}
// Get the back buffer index for the next frame.
swap_chain_back_buffer_index_ = swap_chain_->GetCurrentBackBufferIndex();
}
// Go to the next transient object frame.
fences_[current_queue_frame_]->Enqueue();
++current_queue_frame_;
if (current_queue_frame_ >= kQueuedFrames) {
current_queue_frame_ -= kQueuedFrames;
// Wait for a swap command list to become free.
// Command list 0 is used when swap_fence_current_value_ is 1, 4, 7...
swap_fence_completed_value_ = swap_fence_->GetCompletedValue();
if (swap_fence_completed_value_ + kSwapCommandListCount <
swap_fence_current_value_) {
swap_fence_->SetEventOnCompletion(
swap_fence_current_value_ - kSwapCommandListCount,
swap_fence_completion_event_);
WaitForSingleObject(swap_fence_completion_event_, INFINITE);
swap_fence_completed_value_ = swap_fence_->GetCompletedValue();
}
++current_frame_;
// Bind the back buffer as a render target and clear it.
uint32_t command_list_index =
uint32_t((swap_fence_current_value_ + (kSwapCommandListCount - 1)) %
kSwapCommandListCount);
auto command_list = swap_command_lists_[command_list_index].get();
auto graphics_command_list = command_list->BeginRecording();
D3D12_RESOURCE_BARRIER barrier;
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barrier.Transition.pResource =
swap_chain_buffers_[swap_chain_back_buffer_index_];
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
graphics_command_list->ResourceBarrier(1, &barrier);
D3D12_CPU_DESCRIPTOR_HANDLE back_buffer_rtv = GetSwapChainBackBufferRTV();
graphics_command_list->OMSetRenderTargets(1, &back_buffer_rtv, TRUE, nullptr);
float clear_color[4];
if (cvars::d3d12_random_clear_color) {
clear_color[0] = rand() / float(RAND_MAX); // NOLINT(runtime/threadsafe_fn)
clear_color[1] = 1.0f;
clear_color[2] = 0.0f;
} else {
clear_color[0] = 238.0f / 255.0f;
clear_color[1] = 238.0f / 255.0f;
clear_color[2] = 238.0f / 255.0f;
}
clear_color[3] = 1.0f;
graphics_command_list->ClearRenderTargetView(back_buffer_rtv, clear_color, 0,
nullptr);
}
void D3D12Context::EndSwap() {
if (!target_window_ || context_lost_) {
return;
}
// Switch the back buffer to presentation state.
uint32_t command_list_index =
uint32_t((swap_fence_current_value_ + (kSwapCommandListCount - 1)) %
kSwapCommandListCount);
auto command_list = swap_command_lists_[command_list_index].get();
auto graphics_command_list = command_list->GetCommandList();
D3D12_RESOURCE_BARRIER barrier;
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barrier.Transition.pResource =
swap_chain_buffers_[swap_chain_back_buffer_index_];
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
graphics_command_list->ResourceBarrier(1, &barrier);
command_list->Execute();
// Present and check if the context was lost.
HRESULT result = swap_chain_->Present(0, 0);
if (result == DXGI_ERROR_DEVICE_RESET ||
result == DXGI_ERROR_DEVICE_REMOVED) {
context_lost_ = true;
return;
}
// Signal the fence to wait for frame resources to become free again.
GetD3D12Provider()->GetDirectQueue()->Signal(swap_fence_,
swap_fence_current_value_++);
// Get the back buffer index for the next frame.
swap_chain_back_buffer_index_ = swap_chain_->GetCurrentBackBufferIndex();
}
std::unique_ptr<RawImage> D3D12Context::Capture() {
@@ -325,19 +341,6 @@ std::unique_ptr<RawImage> D3D12Context::Capture() {
return nullptr;
}
void D3D12Context::AwaitAllFramesCompletion() {
// Await the last frame since previous frames must be completed before it.
if (context_lost_) {
return;
}
uint32_t await_frame = current_queue_frame_ + (kQueuedFrames - 1);
if (await_frame >= kQueuedFrames) {
await_frame -= kQueuedFrames;
}
fences_[await_frame]->Await();
last_completed_frame_ = current_frame_ - 1;
}
D3D12_CPU_DESCRIPTOR_HANDLE D3D12Context::GetSwapChainBufferRTV(
uint32_t buffer_index) const {
return GetD3D12Provider()->OffsetRTVDescriptor(swap_chain_rtv_heap_start_,