diff --git a/src/xenia/gpu/d3d12/d3d12_graphics_system.cc b/src/xenia/gpu/d3d12/d3d12_graphics_system.cc index e0223ba64..258f5867e 100644 --- a/src/xenia/gpu/d3d12/d3d12_graphics_system.cc +++ b/src/xenia/gpu/d3d12/d3d12_graphics_system.cc @@ -10,7 +10,7 @@ #include "xenia/gpu/d3d12/d3d12_graphics_system.h" #include "xenia/gpu/d3d12/d3d12_command_processor.h" -#include "xenia/ui/vulkan/vulkan_provider.h" +#include "xenia/ui/d3d12/d3d12_provider.h" #include "xenia/xbox.h" namespace xe { @@ -24,9 +24,7 @@ D3D12GraphicsSystem::~D3D12GraphicsSystem() {} X_STATUS D3D12GraphicsSystem::Setup(cpu::Processor* processor, kernel::KernelState* kernel_state, ui::Window* target_window) { - // This is a null graphics system, but we still setup vulkan because UI needs - // it through us :| - provider_ = xe::ui::vulkan::VulkanProvider::Create(target_window); + provider_ = xe::ui::d3d12::D3D12Provider::Create(target_window); return GraphicsSystem::Setup(processor, kernel_state, target_window); } diff --git a/src/xenia/ui/d3d12/cpu_fence.cc b/src/xenia/ui/d3d12/cpu_fence.cc new file mode 100644 index 000000000..e01363915 --- /dev/null +++ b/src/xenia/ui/d3d12/cpu_fence.cc @@ -0,0 +1,75 @@ +/** + ****************************************************************************** + * 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::Create(ID3D12Device* device, ID3D12CommandQueue* queue) { + std::unique_ptr 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 Direct3D 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 diff --git a/src/xenia/ui/d3d12/cpu_fence.h b/src/xenia/ui/d3d12/cpu_fence.h new file mode 100644 index 000000000..4f77dee65 --- /dev/null +++ b/src/xenia/ui/d3d12/cpu_fence.h @@ -0,0 +1,52 @@ +/** + ****************************************************************************** + * 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 + +#include "xenia/ui/d3d12/d3d12_api.h" + +namespace xe { +namespace ui { +namespace d3d12 { + +class CPUFence { + public: + ~CPUFence(); + + static std::unique_ptr 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::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_ diff --git a/src/xenia/ui/d3d12/d3d12_context.cc b/src/xenia/ui/d3d12/d3d12_context.cc index eaf817bfa..4b79c635b 100644 --- a/src/xenia/ui/d3d12/d3d12_context.cc +++ b/src/xenia/ui/d3d12/d3d12_context.cc @@ -27,26 +27,18 @@ bool D3D12Context::Initialize() { auto provider = static_cast(provider_); auto dxgi_factory = provider->get_dxgi_factory(); auto device = provider->get_device(); + auto direct_queue = provider->get_direct_queue(); context_lost_ = false; // Create fences for synchronization of reuse and destruction of transient // objects (like command lists) and for global shutdown. for (uint32_t i = 0; i < kFrameQueueLength; ++i) { - auto& fence = fences_[i]; - if (FAILED(device->CreateFence(0, D3D12_FENCE_FLAG_NONE, - IID_PPV_ARGS(&fence.fence)))) { - XELOGE("Failed to create a frame completion fence"); + fences_[i] = CPUFence::Create(device, direct_queue); + if (fences_[i] == nullptr) { Shutdown(); return false; } - fence.completion_event = CreateEvent(nullptr, false, false, nullptr); - if (fence.completion_event == nullptr) { - XELOGE("Failed to create a frame completion event"); - Shutdown(); - return false; - } - fence.queued_value = 0; } if (target_window_) { @@ -114,15 +106,7 @@ void D3D12Context::Shutdown() { } for (uint32_t i = 0; i < kFrameQueueLength; ++i) { - auto& fence = fences_[i]; - if (fence.fence != nullptr) { - fence.fence->Release(); - fence.fence = nullptr; - } - if (fence.completion_event != nullptr) { - CloseHandle(fence.completion_event); - fence.completion_event = nullptr; - } + fences_[i].reset(); } } diff --git a/src/xenia/ui/d3d12/d3d12_context.h b/src/xenia/ui/d3d12/d3d12_context.h index 032f5418d..6ab74a52a 100644 --- a/src/xenia/ui/d3d12/d3d12_context.h +++ b/src/xenia/ui/d3d12/d3d12_context.h @@ -10,6 +10,7 @@ #ifndef XENIA_UI_D3D12_D3D12_CONTEXT_H_ #define XENIA_UI_D3D12_D3D12_CONTEXT_H_ +#include "xenia/ui/d3d12/cpu_fence.h" #include "xenia/ui/d3d12/d3d12_api.h" #include "xenia/ui/graphics_context.h" @@ -48,13 +49,7 @@ class D3D12Context : public GraphicsContext { bool context_lost_ = false; - struct Fence { - ID3D12Fence* fence = nullptr; - HANDLE completion_event = nullptr; - // 0 means the fence signal command hasn't been submitted at all yet. - uint64_t queued_value = 0; - }; - Fence fences_[kFrameQueueLength]; + std::unique_ptr fences_[kFrameQueueLength] = {}; static constexpr uint32_t kSwapChainBufferCount = 3; IDXGISwapChain3* swap_chain_ = nullptr;