diff --git a/src/xenia/app/xenia_main.cc b/src/xenia/app/xenia_main.cc index 60b682122..e0f2aaa63 100644 --- a/src/xenia/app/xenia_main.cc +++ b/src/xenia/app/xenia_main.cc @@ -92,6 +92,13 @@ std::unique_ptr CreateGraphicsSystem() { // Create best available. std::unique_ptr best; +#if XE_PLATFORM_WIN32 + best = std::unique_ptr( + new xe::gpu::d3d12::D3D12GraphicsSystem()); + if (best) { + return best; + } +#endif // XE_PLATFORM_WIN32 best = std::unique_ptr( new xe::gpu::vulkan::VulkanGraphicsSystem()); if (best) { diff --git a/src/xenia/ui/d3d12/d3d12_context.cc b/src/xenia/ui/d3d12/d3d12_context.cc index 774bac01b..6ff4d7eed 100644 --- a/src/xenia/ui/d3d12/d3d12_context.cc +++ b/src/xenia/ui/d3d12/d3d12_context.cc @@ -13,6 +13,7 @@ #include "xenia/base/logging.h" #include "xenia/base/math.h" +#include "xenia/ui/d3d12/d3d12_immediate_drawer.h" #include "xenia/ui/d3d12/d3d12_provider.h" #include "xenia/ui/window.h" @@ -30,18 +31,22 @@ D3D12Context::D3D12Context(D3D12Provider* provider, Window* target_window) D3D12Context::~D3D12Context() { Shutdown(); } bool D3D12Context::Initialize() { - auto provider = static_cast(provider_); + auto provider = GetD3D12Provider(); auto dxgi_factory = provider->GetDXGIFactory(); auto device = provider->GetDevice(); auto direct_queue = provider->GetDirectQueue(); context_lost_ = false; - current_queue_frame_ = 0; + 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 < kFrameQueueLength; ++i) { + for (uint32_t i = 0; i < kQueuedFrames; ++i) { fences_[i] = CPUFence::Create(device, direct_queue); if (fences_[i] == nullptr) { Shutdown(); @@ -94,6 +99,8 @@ bool D3D12Context::Initialize() { Shutdown(); return false; } + swap_chain_rtv_heap_start_ = + swap_chain_rtv_heap_->GetCPUDescriptorHandleForHeapStart(); // Get the buffers and create their RTV descriptors. if (!InitializeSwapChainBuffers()) { @@ -102,7 +109,7 @@ bool D3D12Context::Initialize() { } // Create command lists for swap chain back buffer state transitions. - for (uint32_t i = 0; i < kFrameQueueLength; ++i) { + for (uint32_t i = 0; i < kQueuedFrames; ++i) { swap_command_lists_begin_[i] = CommandList::Create( device, direct_queue, D3D12_COMMAND_LIST_TYPE_DIRECT); swap_command_lists_end_[i] = CommandList::Create( @@ -113,9 +120,17 @@ bool D3D12Context::Initialize() { return false; } } + + // Initialize the immediate mode drawer if not offscreen. + immediate_drawer_ = std::make_unique(this); + if (!immediate_drawer_->Initialize()) { + Shutdown(); + return false; + } } initialized_fully_ = true; + return true; } bool D3D12Context::InitializeSwapChainBuffers() { @@ -132,7 +147,7 @@ bool D3D12Context::InitializeSwapChainBuffers() { swap_chain_back_buffer_index_ = swap_chain_->GetCurrentBackBufferIndex(); // Create RTV descriptors for the swap chain buffers. - auto device = static_cast(provider_)->GetDevice(); + auto device = GetD3D12Provider()->GetDevice(); D3D12_RENDER_TARGET_VIEW_DESC rtv_desc; rtv_desc.Format = kSwapChainFormat; rtv_desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; @@ -142,6 +157,8 @@ bool D3D12Context::InitializeSwapChainBuffers() { device->CreateRenderTargetView(swap_chain_buffers_[i], &rtv_desc, GetSwapChainBufferRTV(i)); } + + return true; } void D3D12Context::Shutdown() { @@ -154,7 +171,7 @@ void D3D12Context::Shutdown() { immediate_drawer_.reset(); if (swap_chain_ != nullptr) { - for (uint32_t i = 0; i < kFrameQueueLength; ++i) { + for (uint32_t i = 0; i < kQueuedFrames; ++i) { swap_command_lists_end_[i].reset(); swap_command_lists_begin_[i].reset(); } @@ -176,7 +193,7 @@ void D3D12Context::Shutdown() { swap_chain_->Release(); } - for (uint32_t i = 0; i < kFrameQueueLength; ++i) { + for (uint32_t i = 0; i < kQueuedFrames; ++i) { fences_[i].reset(); } } @@ -197,10 +214,14 @@ void D3D12Context::BeginSwap() { } // Await the availability of transient objects for the new frame. - // The frame number is incremented in EndSwap so they can be treated the same + // 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. @@ -282,9 +303,10 @@ void D3D12Context::EndSwap() { // Go to the next transient object frame. fences_[current_queue_frame_]->Enqueue(); ++current_queue_frame_; - if (current_queue_frame_ >= kFrameQueueLength) { - current_queue_frame_ -= kFrameQueueLength; + if (current_queue_frame_ >= kQueuedFrames) { + current_queue_frame_ -= kQueuedFrames; } + ++current_frame_; } std::unique_ptr D3D12Context::Capture() { @@ -294,19 +316,18 @@ std::unique_ptr D3D12Context::Capture() { void D3D12Context::AwaitAllFramesCompletion() { // Await the last frame since previous frames must be completed before it. - uint32_t await_frame = current_queue_frame_ + (kFrameQueueLength - 1); - if (await_frame >= kFrameQueueLength) { - await_frame -= kFrameQueueLength; + 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 { D3D12_CPU_DESCRIPTOR_HANDLE handle = swap_chain_rtv_heap_start_; - handle.ptr += - buffer_index * - static_cast(provider_)->GetDescriptorSizeRTV(); + handle.ptr += buffer_index * GetD3D12Provider()->GetDescriptorSizeRTV(); return handle; } diff --git a/src/xenia/ui/d3d12/d3d12_context.h b/src/xenia/ui/d3d12/d3d12_context.h index 5e8e91961..62cd3ea81 100644 --- a/src/xenia/ui/d3d12/d3d12_context.h +++ b/src/xenia/ui/d3d12/d3d12_context.h @@ -10,15 +10,20 @@ #ifndef XENIA_UI_D3D12_D3D12_CONTEXT_H_ #define XENIA_UI_D3D12_D3D12_CONTEXT_H_ +#include + #include "xenia/ui/d3d12/command_list.h" #include "xenia/ui/d3d12/cpu_fence.h" #include "xenia/ui/d3d12/d3d12_api.h" +#include "xenia/ui/d3d12/d3d12_provider.h" #include "xenia/ui/graphics_context.h" namespace xe { namespace ui { namespace d3d12 { +class D3D12ImmediateDrawer; + class D3D12Context : public GraphicsContext { public: ~D3D12Context() override; @@ -36,9 +41,17 @@ class D3D12Context : public GraphicsContext { std::unique_ptr Capture() override; + D3D12Provider* GetD3D12Provider() const { + return static_cast(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 kFrameQueueLength = 3; + 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(); @@ -51,6 +64,9 @@ class D3D12Context : public GraphicsContext { } D3D12_CPU_DESCRIPTOR_HANDLE GetSwapChainBufferRTV( uint32_t buffer_index) const; + D3D12_CPU_DESCRIPTOR_HANDLE GetSwapChainBackBufferRTV() const { + return GetSwapChainBufferRTV(GetSwapChainBackBufferIndex()); + } private: friend class D3D12Provider; @@ -66,8 +82,10 @@ class D3D12Context : public GraphicsContext { bool context_lost_ = false; - uint32_t current_queue_frame_ = 0; - std::unique_ptr fences_[kFrameQueueLength] = {}; + uint64_t current_frame_ = 1; + uint64_t last_completed_frame_ = 0; + uint32_t current_queue_frame_ = 1; + std::unique_ptr fences_[kQueuedFrames] = {}; static constexpr uint32_t kSwapChainBufferCount = 3; IDXGISwapChain3* swap_chain_ = nullptr; @@ -76,9 +94,8 @@ 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 swap_command_lists_begin_[kFrameQueueLength] = - {}; - std::unique_ptr swap_command_lists_end_[kFrameQueueLength] = {}; + std::unique_ptr swap_command_lists_begin_[kQueuedFrames] = {}; + std::unique_ptr swap_command_lists_end_[kQueuedFrames] = {}; std::unique_ptr immediate_drawer_ = nullptr; }; diff --git a/src/xenia/ui/d3d12/d3d12_immediate_drawer.cc b/src/xenia/ui/d3d12/d3d12_immediate_drawer.cc new file mode 100644 index 000000000..e13c03b70 --- /dev/null +++ b/src/xenia/ui/d3d12/d3d12_immediate_drawer.cc @@ -0,0 +1,171 @@ +/** + ****************************************************************************** + * 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/d3d12_immediate_drawer.h" + +#include "xenia/base/logging.h" + +namespace xe { +namespace ui { +namespace d3d12 { + +class D3D12ImmediateTexture : public ImmediateTexture { + public: + D3D12ImmediateTexture(uint32_t width, uint32_t height) + : ImmediateTexture(width, height) {} +}; + +D3D12ImmediateDrawer::D3D12ImmediateDrawer(D3D12Context* graphics_context) + : ImmediateDrawer(graphics_context), context_(graphics_context) {} + +D3D12ImmediateDrawer::~D3D12ImmediateDrawer() { Shutdown(); } + +bool D3D12ImmediateDrawer::Initialize() { + auto provider = context_->GetD3D12Provider(); + auto device = provider->GetDevice(); + + // Create the samplers. + D3D12_DESCRIPTOR_HEAP_DESC sampler_heap_desc; + sampler_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER; + sampler_heap_desc.NumDescriptors = uint32_t(SamplerIndex::kSamplerCount); + sampler_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; + sampler_heap_desc.NodeMask = 0; + if (FAILED(device->CreateDescriptorHeap(&sampler_heap_desc, + IID_PPV_ARGS(&sampler_heap_)))) { + XELOGE("Failed to create immediate drawer sampler descriptor heap"); + Shutdown(); + return false; + } + sampler_heap_cpu_start_ = sampler_heap_->GetCPUDescriptorHandleForHeapStart(); + sampler_heap_gpu_start_ = sampler_heap_->GetGPUDescriptorHandleForHeapStart(); + uint32_t sampler_size = provider->GetDescriptorSizeSampler(); + // Nearest neighbor, clamp. + D3D12_SAMPLER_DESC sampler_desc; + sampler_desc.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT; + sampler_desc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; + sampler_desc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; + sampler_desc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; + sampler_desc.MipLODBias = 0.0f; + sampler_desc.MaxAnisotropy = 1; + sampler_desc.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER; + sampler_desc.BorderColor[0] = 0.0f; + sampler_desc.BorderColor[1] = 0.0f; + sampler_desc.BorderColor[2] = 0.0f; + sampler_desc.BorderColor[3] = 0.0f; + sampler_desc.MinLOD = 0.0f; + sampler_desc.MaxLOD = 0.0f; + D3D12_CPU_DESCRIPTOR_HANDLE sampler_handle; + sampler_handle.ptr = sampler_heap_cpu_start_.ptr + + uint32_t(SamplerIndex::kNearestClamp) * sampler_size; + device->CreateSampler(&sampler_desc, sampler_handle); + // Bilinear, clamp. + sampler_desc.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; + sampler_handle.ptr = sampler_heap_cpu_start_.ptr + + uint32_t(SamplerIndex::kLinearClamp) * sampler_size; + device->CreateSampler(&sampler_desc, sampler_handle); + // Nearest neighbor, repeat. + sampler_desc.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT; + sampler_desc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + sampler_desc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + sampler_desc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + sampler_handle.ptr = sampler_heap_cpu_start_.ptr + + uint32_t(SamplerIndex::kNearestRepeat) * sampler_size; + device->CreateSampler(&sampler_desc, sampler_handle); + // Bilinear, repeat. + sampler_desc.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; + sampler_handle.ptr = sampler_heap_cpu_start_.ptr + + uint32_t(SamplerIndex::kLinearRepeat) * sampler_size; + device->CreateSampler(&sampler_desc, sampler_handle); + + // Create the command lists. + ID3D12CommandQueue* direct_queue = provider->GetDirectQueue(); + for (uint32_t i = 0; i < D3D12Context::kQueuedFrames; ++i) { + command_lists_[i] = CommandList::Create(device, direct_queue, + D3D12_COMMAND_LIST_TYPE_DIRECT); + if (command_lists_[i] == nullptr) { + Shutdown(); + return false; + } + } + + return true; +} + +void D3D12ImmediateDrawer::Shutdown() { + for (auto& texture_upload : texture_uploads_submitted_) { + texture_upload.data_resource->Release(); + } + texture_uploads_submitted_.clear(); + + for (uint32_t i = 0; i < D3D12Context::kQueuedFrames; ++i) { + command_lists_[i].reset(); + } + + if (sampler_heap_ != nullptr) { + sampler_heap_->Release(); + sampler_heap_ = nullptr; + } +} + +std::unique_ptr D3D12ImmediateDrawer::CreateTexture( + uint32_t width, uint32_t height, ImmediateTextureFilter filter, bool repeat, + const uint8_t* data) { + // TODO(Triang3l): Implement CreateTexture. + auto texture = std::make_unique(width, height); + return std::unique_ptr(texture.release()); +} + +void D3D12ImmediateDrawer::UpdateTexture(ImmediateTexture* texture, + const uint8_t* data) { + // TODO(Triang3l): Implement UpdateTexture. +} + +void D3D12ImmediateDrawer::Begin(int render_target_width, + int render_target_height) { + uint32_t queue_frame = context_->GetCurrentQueueFrame(); + uint64_t last_completed_frame = context_->GetLastCompletedFrame(); + + // 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) { + ++erase_uploads_end; + break; + } + erase_uploads_end->data_resource->Release(); + ++erase_uploads_end; + } + texture_uploads_submitted_.erase(texture_uploads_submitted_.begin(), + erase_uploads_end); + + // Start a command list recording. + ID3D12GraphicsCommandList* command_list = + command_lists_[queue_frame]->BeginRecording(); +} + +void D3D12ImmediateDrawer::BeginDrawBatch(const ImmediateDrawBatch& batch) { + // TODO(Triang3l): Implement BeginDrawBatch. +} + +void D3D12ImmediateDrawer::Draw(const ImmediateDraw& draw) { + // TODO(Triang3l): Implement Draw. +} + +void D3D12ImmediateDrawer::EndDrawBatch() { + // TODO(Triang3l): Implement EndDrawBatch. +} + +void D3D12ImmediateDrawer::End() { + command_lists_[context_->GetCurrentQueueFrame()]->Execute(); +} + +} // namespace d3d12 +} // namespace ui +} // namespace xe diff --git a/src/xenia/ui/d3d12/d3d12_immediate_drawer.h b/src/xenia/ui/d3d12/d3d12_immediate_drawer.h new file mode 100644 index 000000000..165aaa27a --- /dev/null +++ b/src/xenia/ui/d3d12/d3d12_immediate_drawer.h @@ -0,0 +1,72 @@ +/** + ****************************************************************************** + * 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_D3D12_IMMEDIATE_DRAWER_H_ +#define XENIA_UI_D3D12_D3D12_IMMEDIATE_DRAWER_H_ + +#include +#include + +#include "xenia/ui/d3d12/command_list.h" +#include "xenia/ui/d3d12/d3d12_api.h" +#include "xenia/ui/d3d12/d3d12_context.h" +#include "xenia/ui/immediate_drawer.h" + +namespace xe { +namespace ui { +namespace d3d12 { + +class D3D12ImmediateDrawer : public ImmediateDrawer { + public: + D3D12ImmediateDrawer(D3D12Context* graphics_context); + ~D3D12ImmediateDrawer() override; + + bool Initialize(); + void Shutdown(); + + std::unique_ptr CreateTexture( + uint32_t width, uint32_t height, ImmediateTextureFilter filter, + bool repeat, const uint8_t* data) override; + void UpdateTexture(ImmediateTexture* texture, const uint8_t* data) override; + + void Begin(int render_target_width, int render_target_height) override; + void BeginDrawBatch(const ImmediateDrawBatch& batch) override; + void Draw(const ImmediateDraw& draw) override; + void EndDrawBatch() override; + void End(); + + private: + D3D12Context* context_ = nullptr; + + enum class SamplerIndex { + kNearestClamp, + kLinearClamp, + kNearestRepeat, + kLinearRepeat, + + kSamplerCount + }; + ID3D12DescriptorHeap* sampler_heap_ = nullptr; + D3D12_CPU_DESCRIPTOR_HANDLE sampler_heap_cpu_start_; + D3D12_GPU_DESCRIPTOR_HANDLE sampler_heap_gpu_start_; + + std::unique_ptr command_lists_[D3D12Context::kQueuedFrames] = {}; + + struct SubmittedTextureUpload { + ID3D12Resource* data_resource; + uint64_t frame; + }; + std::deque texture_uploads_submitted_; +}; + +} // namespace d3d12 +} // namespace ui +} // namespace xe + +#endif // XENIA_UI_D3D12_D3D12_IMMEDIATE_DRAWER_H_ diff --git a/src/xenia/ui/d3d12/d3d12_provider.cc b/src/xenia/ui/d3d12/d3d12_provider.cc index 2c0786621..a895c46b2 100644 --- a/src/xenia/ui/d3d12/d3d12_provider.cc +++ b/src/xenia/ui/d3d12/d3d12_provider.cc @@ -14,7 +14,7 @@ #include "xenia/base/logging.h" #include "xenia/ui/d3d12/d3d12_context.h" -DEFINE_bool(d3d12_debug, false, "Enable Direct3D 12 and DXGI debug layer."); +DEFINE_bool(d3d12_debug, true, "Enable Direct3D 12 and DXGI debug layer."); DEFINE_int32(d3d12_adapter_index, -1, "Index of the DXGI adapter to use. " "-1 for any physical adapter, -2 for WARP software rendering.");