[D3D12] Don't use D3D12Context for command processor fence
This commit is contained in:
@@ -1,75 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2018 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/ui/d3d12/cpu_fence.h"
|
||||
|
||||
#include "xenia/base/logging.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace d3d12 {
|
||||
|
||||
std::unique_ptr<CPUFence> CPUFence::Create(ID3D12Device* device,
|
||||
ID3D12CommandQueue* queue) {
|
||||
std::unique_ptr<CPUFence> fence(new CPUFence(device, queue));
|
||||
if (!fence->Initialize()) {
|
||||
return nullptr;
|
||||
}
|
||||
return fence;
|
||||
}
|
||||
|
||||
CPUFence::CPUFence(ID3D12Device* device, ID3D12CommandQueue* queue)
|
||||
: device_(device), queue_(queue) {}
|
||||
|
||||
CPUFence::~CPUFence() {
|
||||
// First destroying the fence because it may reference the event.
|
||||
if (fence_ != nullptr) {
|
||||
fence_->Release();
|
||||
}
|
||||
if (completion_event_ != nullptr) {
|
||||
CloseHandle(completion_event_);
|
||||
}
|
||||
}
|
||||
|
||||
bool CPUFence::Initialize() {
|
||||
if (FAILED(device_->CreateFence(0, D3D12_FENCE_FLAG_NONE,
|
||||
IID_PPV_ARGS(&fence_)))) {
|
||||
XELOGE("Failed to create a fence");
|
||||
return false;
|
||||
}
|
||||
completion_event_ = CreateEvent(nullptr, false, false, nullptr);
|
||||
if (completion_event_ == nullptr) {
|
||||
XELOGE("Failed to create a fence completion event");
|
||||
fence_->Release();
|
||||
fence_ = nullptr;
|
||||
return false;
|
||||
}
|
||||
queued_value_ = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CPUFence::Enqueue() {
|
||||
++queued_value_;
|
||||
queue_->Signal(fence_, queued_value_);
|
||||
}
|
||||
|
||||
bool CPUFence::IsCompleted() {
|
||||
return fence_->GetCompletedValue() >= queued_value_;
|
||||
}
|
||||
|
||||
void CPUFence::Await() {
|
||||
if (fence_->GetCompletedValue() < queued_value_) {
|
||||
fence_->SetEventOnCompletion(queued_value_, completion_event_);
|
||||
WaitForSingleObject(completion_event_, INFINITE);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace d3d12
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
@@ -1,52 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2018 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_UI_D3D12_CPU_FENCE_H_
|
||||
#define XENIA_UI_D3D12_CPU_FENCE_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "xenia/ui/d3d12/d3d12_api.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace d3d12 {
|
||||
|
||||
class CPUFence {
|
||||
public:
|
||||
~CPUFence();
|
||||
|
||||
static std::unique_ptr<CPUFence> Create(ID3D12Device* device,
|
||||
ID3D12CommandQueue* queue);
|
||||
|
||||
// Submits the fence to the GPU command queue.
|
||||
void Enqueue();
|
||||
|
||||
// Immediately returns whether the GPU has reached the fence.
|
||||
bool IsCompleted();
|
||||
// Blocks until the fence has been reached.
|
||||
void Await();
|
||||
|
||||
private:
|
||||
CPUFence(ID3D12Device* device, ID3D12CommandQueue* queue);
|
||||
bool Initialize();
|
||||
|
||||
ID3D12Device* device_;
|
||||
ID3D12CommandQueue* queue_;
|
||||
|
||||
ID3D12Fence* fence_ = nullptr;
|
||||
HANDLE completion_event_ = nullptr;
|
||||
uint64_t queued_value_ = 0;
|
||||
};
|
||||
|
||||
} // namespace d3d12
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_UI_D3D12_CPU_FENCE_H_
|
||||
@@ -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_,
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include <memory>
|
||||
|
||||
#include "xenia/ui/d3d12/command_list.h"
|
||||
#include "xenia/ui/d3d12/cpu_fence.h"
|
||||
#include "xenia/ui/d3d12/d3d12_immediate_drawer.h"
|
||||
#include "xenia/ui/d3d12/d3d12_provider.h"
|
||||
#include "xenia/ui/graphics_context.h"
|
||||
@@ -45,16 +44,6 @@ class D3D12Context : public GraphicsContext {
|
||||
return static_cast<D3D12Provider*>(provider_);
|
||||
}
|
||||
|
||||
// The count of copies of transient objects (like command lists, dynamic
|
||||
// descriptor heaps) that must be kept when rendering with this context.
|
||||
static constexpr uint32_t kQueuedFrames = 3;
|
||||
// The current absolute frame number.
|
||||
uint64_t GetCurrentFrame() { return current_frame_; }
|
||||
// The last completed frame - it's fine to destroy objects used in it.
|
||||
uint64_t GetLastCompletedFrame() { return last_completed_frame_; }
|
||||
uint32_t GetCurrentQueueFrame() { return current_queue_frame_; }
|
||||
void AwaitAllFramesCompletion();
|
||||
|
||||
static constexpr DXGI_FORMAT kSwapChainFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
ID3D12Resource* GetSwapChainBuffer(uint32_t buffer_index) const {
|
||||
return swap_chain_buffers_[buffer_index];
|
||||
@@ -71,8 +60,18 @@ class D3D12Context : public GraphicsContext {
|
||||
width = swap_chain_width_;
|
||||
height = swap_chain_height_;
|
||||
}
|
||||
// Inside the current BeginSwap/EndSwap pair.
|
||||
uint64_t GetSwapCurrentFenceValue() const {
|
||||
return swap_fence_current_value_;
|
||||
}
|
||||
uint64_t GetSwapCompletedFenceValue() const {
|
||||
return swap_fence_completed_value_;
|
||||
}
|
||||
ID3D12GraphicsCommandList* GetSwapCommandList() const {
|
||||
return swap_command_lists_[current_queue_frame_]->GetCommandList();
|
||||
uint32_t command_list_index =
|
||||
uint32_t((swap_fence_current_value_ + (kSwapCommandListCount - 1)) %
|
||||
kSwapCommandListCount);
|
||||
return swap_command_lists_[command_list_index]->GetCommandList();
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -85,15 +84,8 @@ class D3D12Context : public GraphicsContext {
|
||||
bool InitializeSwapChainBuffers();
|
||||
void Shutdown();
|
||||
|
||||
bool initialized_fully_ = false;
|
||||
|
||||
bool context_lost_ = false;
|
||||
|
||||
uint64_t current_frame_ = 1;
|
||||
uint64_t last_completed_frame_ = 0;
|
||||
uint32_t current_queue_frame_ = 1;
|
||||
std::unique_ptr<CPUFence> fences_[kQueuedFrames] = {};
|
||||
|
||||
static constexpr uint32_t kSwapChainBufferCount = 3;
|
||||
IDXGISwapChain3* swap_chain_ = nullptr;
|
||||
uint32_t swap_chain_width_ = 0, swap_chain_height_ = 0;
|
||||
@@ -101,7 +93,17 @@ class D3D12Context : public GraphicsContext {
|
||||
uint32_t swap_chain_back_buffer_index_ = 0;
|
||||
ID3D12DescriptorHeap* swap_chain_rtv_heap_ = nullptr;
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE swap_chain_rtv_heap_start_;
|
||||
std::unique_ptr<CommandList> swap_command_lists_[kQueuedFrames] = {};
|
||||
|
||||
uint64_t swap_fence_current_value_ = 1;
|
||||
uint64_t swap_fence_completed_value_ = 0;
|
||||
HANDLE swap_fence_completion_event_ = nullptr;
|
||||
ID3D12Fence* swap_fence_ = nullptr;
|
||||
|
||||
static constexpr uint32_t kSwapCommandListCount = 3;
|
||||
std::unique_ptr<CommandList> swap_command_lists_[kSwapCommandListCount] = {};
|
||||
// Current is
|
||||
// ((swap_fence_current_value_ + (kSwapCommandListCount - 1))) %
|
||||
// kSwapCommandListCount.
|
||||
|
||||
std::unique_ptr<D3D12ImmediateDrawer> immediate_drawer_ = nullptr;
|
||||
};
|
||||
|
||||
@@ -399,7 +399,7 @@ void D3D12ImmediateDrawer::UpdateTexture(ImmediateTexture* texture,
|
||||
&location_source, nullptr);
|
||||
SubmittedTextureUpload submitted_upload;
|
||||
submitted_upload.buffer = buffer;
|
||||
submitted_upload.frame = context_->GetCurrentFrame();
|
||||
submitted_upload.fence_value = context_->GetSwapCurrentFenceValue();
|
||||
texture_uploads_submitted_.push_back(submitted_upload);
|
||||
} else {
|
||||
// Defer uploading to the next frame when there's a command list.
|
||||
@@ -417,14 +417,14 @@ void D3D12ImmediateDrawer::Begin(int render_target_width,
|
||||
// Use the compositing command list.
|
||||
current_command_list_ = context_->GetSwapCommandList();
|
||||
|
||||
uint64_t current_frame = context_->GetCurrentFrame();
|
||||
uint64_t last_completed_frame = context_->GetLastCompletedFrame();
|
||||
uint64_t completed_fence_value = context_->GetSwapCompletedFenceValue();
|
||||
uint64_t current_fence_value = context_->GetSwapCurrentFenceValue();
|
||||
|
||||
// Remove temporary buffers for completed texture uploads.
|
||||
auto erase_uploads_end = texture_uploads_submitted_.begin();
|
||||
while (erase_uploads_end != texture_uploads_submitted_.end()) {
|
||||
uint64_t upload_frame = erase_uploads_end->frame;
|
||||
if (upload_frame > last_completed_frame) {
|
||||
uint64_t upload_fence_value = erase_uploads_end->fence_value;
|
||||
if (upload_fence_value > completed_fence_value) {
|
||||
++erase_uploads_end;
|
||||
break;
|
||||
}
|
||||
@@ -456,13 +456,13 @@ void D3D12ImmediateDrawer::Begin(int render_target_width,
|
||||
&location_source, nullptr);
|
||||
SubmittedTextureUpload submitted_upload;
|
||||
submitted_upload.buffer = pending_upload.buffer;
|
||||
submitted_upload.frame = current_frame;
|
||||
submitted_upload.fence_value = current_fence_value;
|
||||
texture_uploads_submitted_.push_back(submitted_upload);
|
||||
texture_uploads_pending_.pop_back();
|
||||
}
|
||||
|
||||
vertex_buffer_pool_->Reclaim(last_completed_frame);
|
||||
texture_descriptor_pool_->Reclaim(last_completed_frame);
|
||||
vertex_buffer_pool_->Reclaim(completed_fence_value);
|
||||
texture_descriptor_pool_->Reclaim(completed_fence_value);
|
||||
texture_descriptor_pool_heap_index_ = DescriptorHeapPool::kHeapIndexInvalid;
|
||||
|
||||
current_render_target_width_ = render_target_width;
|
||||
@@ -493,6 +493,7 @@ void D3D12ImmediateDrawer::BeginDrawBatch(const ImmediateDrawBatch& batch) {
|
||||
if (current_command_list_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
uint64_t current_fence_value = context_->GetSwapCurrentFenceValue();
|
||||
|
||||
batch_open_ = false;
|
||||
|
||||
@@ -502,8 +503,8 @@ void D3D12ImmediateDrawer::BeginDrawBatch(const ImmediateDrawBatch& batch) {
|
||||
vertex_buffer_view.SizeInBytes =
|
||||
batch.vertex_count * uint32_t(sizeof(ImmediateVertex));
|
||||
void* vertex_buffer_mapping = vertex_buffer_pool_->Request(
|
||||
context_->GetCurrentFrame(), vertex_buffer_view.SizeInBytes, nullptr,
|
||||
nullptr, &vertex_buffer_view.BufferLocation);
|
||||
current_fence_value, vertex_buffer_view.SizeInBytes, nullptr, nullptr,
|
||||
&vertex_buffer_view.BufferLocation);
|
||||
if (vertex_buffer_mapping == nullptr) {
|
||||
XELOGE("Failed to get a buffer for %u vertices in the immediate drawer",
|
||||
batch.vertex_count);
|
||||
@@ -520,7 +521,7 @@ void D3D12ImmediateDrawer::BeginDrawBatch(const ImmediateDrawBatch& batch) {
|
||||
index_buffer_view.SizeInBytes = batch.index_count * sizeof(uint16_t);
|
||||
index_buffer_view.Format = DXGI_FORMAT_R16_UINT;
|
||||
void* index_buffer_mapping = vertex_buffer_pool_->Request(
|
||||
context_->GetCurrentFrame(),
|
||||
current_fence_value,
|
||||
xe::align(index_buffer_view.SizeInBytes, UINT(sizeof(uint32_t))),
|
||||
nullptr, nullptr, &index_buffer_view.BufferLocation);
|
||||
if (index_buffer_mapping == nullptr) {
|
||||
@@ -563,7 +564,7 @@ void D3D12ImmediateDrawer::Draw(const ImmediateDraw& draw) {
|
||||
bool bind_texture = current_texture_ != texture;
|
||||
uint32_t texture_descriptor_index;
|
||||
uint64_t texture_heap_index = texture_descriptor_pool_->Request(
|
||||
context_->GetCurrentFrame(), texture_descriptor_pool_heap_index_,
|
||||
context_->GetSwapCurrentFenceValue(), texture_descriptor_pool_heap_index_,
|
||||
bind_texture ? 1 : 0, 1, texture_descriptor_index);
|
||||
if (texture_heap_index == DescriptorHeapPool::kHeapIndexInvalid) {
|
||||
return;
|
||||
@@ -674,9 +675,7 @@ void D3D12ImmediateDrawer::Draw(const ImmediateDraw& draw) {
|
||||
|
||||
void D3D12ImmediateDrawer::EndDrawBatch() { batch_open_ = false; }
|
||||
|
||||
void D3D12ImmediateDrawer::End() {
|
||||
current_command_list_ = nullptr;
|
||||
}
|
||||
void D3D12ImmediateDrawer::End() { current_command_list_ = nullptr; }
|
||||
|
||||
} // namespace d3d12
|
||||
} // namespace ui
|
||||
|
||||
@@ -87,7 +87,7 @@ class D3D12ImmediateDrawer : public ImmediateDrawer {
|
||||
|
||||
struct SubmittedTextureUpload {
|
||||
ID3D12Resource* buffer;
|
||||
uint64_t frame;
|
||||
uint64_t fence_value;
|
||||
};
|
||||
std::deque<SubmittedTextureUpload> texture_uploads_submitted_;
|
||||
|
||||
|
||||
@@ -22,9 +22,7 @@ namespace d3d12 {
|
||||
UploadBufferPool::UploadBufferPool(ID3D12Device* device, uint32_t page_size)
|
||||
: device_(device), page_size_(page_size) {}
|
||||
|
||||
UploadBufferPool::~UploadBufferPool() {
|
||||
ClearCache();
|
||||
}
|
||||
UploadBufferPool::~UploadBufferPool() { ClearCache(); }
|
||||
|
||||
void UploadBufferPool::Reclaim(uint64_t completed_fence_value) {
|
||||
while (submitted_first_) {
|
||||
@@ -172,9 +170,7 @@ DescriptorHeapPool::DescriptorHeapPool(ID3D12Device* device,
|
||||
uint32_t page_size)
|
||||
: device_(device), type_(type), page_size_(page_size) {}
|
||||
|
||||
DescriptorHeapPool::~DescriptorHeapPool() {
|
||||
ClearCache();
|
||||
}
|
||||
DescriptorHeapPool::~DescriptorHeapPool() { ClearCache(); }
|
||||
|
||||
void DescriptorHeapPool::Reclaim(uint64_t completed_fence_value) {
|
||||
while (submitted_first_) {
|
||||
|
||||
Reference in New Issue
Block a user