diff --git a/src/xenia/gpu/d3d12/d3d12_command_processor.h b/src/xenia/gpu/d3d12/d3d12_command_processor.h index 736c4a999..22e843cf8 100644 --- a/src/xenia/gpu/d3d12/d3d12_command_processor.h +++ b/src/xenia/gpu/d3d12/d3d12_command_processor.h @@ -21,8 +21,8 @@ namespace d3d12 { class D3D12CommandProcessor : public CommandProcessor { public: - D3D12CommandProcessor(D3D12GraphicsSystem* graphics_system, - kernel::KernelState* kernel_state); + explicit D3D12CommandProcessor(D3D12GraphicsSystem* graphics_system, + kernel::KernelState* kernel_state); ~D3D12CommandProcessor(); private: diff --git a/src/xenia/ui/d3d12/d3d12_api.h b/src/xenia/ui/d3d12/d3d12_api.h index 4f81bd99e..3add074ad 100644 --- a/src/xenia/ui/d3d12/d3d12_api.h +++ b/src/xenia/ui/d3d12/d3d12_api.h @@ -10,6 +10,9 @@ #ifndef XENIA_UI_D3D12_D3D12_API_H_ #define XENIA_UI_D3D12_D3D12_API_H_ +// This must be included before D3D and DXGI for things like NOMINMAX. +#include "xenia/base/platform_win.h" + #include #include diff --git a/src/xenia/ui/d3d12/d3d12_context.cc b/src/xenia/ui/d3d12/d3d12_context.cc new file mode 100644 index 000000000..eaf817bfa --- /dev/null +++ b/src/xenia/ui/d3d12/d3d12_context.cc @@ -0,0 +1,146 @@ +/** + ****************************************************************************** + * 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_context.h" + +#include "xenia/base/logging.h" +#include "xenia/base/math.h" +#include "xenia/ui/d3d12/d3d12_provider.h" +#include "xenia/ui/window.h" + +namespace xe { +namespace ui { +namespace d3d12 { + +D3D12Context::D3D12Context(D3D12Provider* provider, Window* target_window) + : GraphicsContext(provider, target_window) {} + +D3D12Context::~D3D12Context() { Shutdown(); } + +bool D3D12Context::Initialize() { + auto provider = static_cast(provider_); + auto dxgi_factory = provider->get_dxgi_factory(); + auto device = provider->get_device(); + + 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"); + 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_) { + DXGI_SWAP_CHAIN_DESC1 swap_chain_desc; + swap_chain_desc.Width = 1280; + swap_chain_desc.Height = 720; + swap_chain_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swap_chain_desc.Stereo = FALSE; + swap_chain_desc.SampleDesc.Count = 1; + swap_chain_desc.SampleDesc.Quality = 0; + swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swap_chain_desc.BufferCount = kSwapChainBufferCount; + swap_chain_desc.Scaling = DXGI_SCALING_STRETCH; + swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + swap_chain_desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; + swap_chain_desc.Flags = 0; + IDXGISwapChain1* swap_chain_1; + if (FAILED(dxgi_factory->CreateSwapChainForHwnd( + provider->get_direct_queue(), + static_cast(target_window_->native_handle()), &swap_chain_desc, + nullptr, nullptr, &swap_chain_1))) { + XELOGE("Failed to create a DXGI swap chain"); + Shutdown(); + return false; + } + if (FAILED(swap_chain_1->QueryInterface(IID_PPV_ARGS(&swap_chain_)))) { + XELOGE("Failed to get version 3 of the DXGI swap chain interface"); + swap_chain_1->Release(); + Shutdown(); + return false; + } + swap_chain_1->Release(); + for (uint32_t i = 0; i < kSwapChainBufferCount; ++i) { + if (FAILED(swap_chain_->GetBuffer(i, + IID_PPV_ARGS(&swap_chain_buffers_[i])))) { + XELOGE("Failed to get buffer %u of the swap chain", i); + Shutdown(); + return false; + } + } + } + + initialized_fully_ = true; +} + +void D3D12Context::Shutdown() { + if (initialized_fully_) { + // TODO(Triang3l): Wait for submitted frame completion. + } + + initialized_fully_ = false; + + immediate_drawer_.reset(); + + if (swap_chain_ != nullptr) { + for (uint32_t i = 0; i < kSwapChainBufferCount; ++i) { + auto& buffer = swap_chain_buffers_[i]; + if (buffer == nullptr) { + break; + } + buffer->Release(); + buffer = nullptr; + } + swap_chain_->Release(); + } + + 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; + } + } +} + +ImmediateDrawer* D3D12Context::immediate_drawer() { + return immediate_drawer_.get(); +} + +bool D3D12Context::is_current() { return false; } + +bool D3D12Context::MakeCurrent() { return true; } + +void D3D12Context::ClearCurrent() {} + +std::unique_ptr D3D12Context::Capture() { + // TODO(Triang3l): Read back swap chain front buffer. + return nullptr; +} + +} // namespace d3d12 +} // namespace ui +} // namespace xe diff --git a/src/xenia/ui/d3d12/d3d12_context.h b/src/xenia/ui/d3d12/d3d12_context.h new file mode 100644 index 000000000..032f5418d --- /dev/null +++ b/src/xenia/ui/d3d12/d3d12_context.h @@ -0,0 +1,70 @@ +/** + ****************************************************************************** + * 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_CONTEXT_H_ +#define XENIA_UI_D3D12_D3D12_CONTEXT_H_ + +#include "xenia/ui/d3d12/d3d12_api.h" +#include "xenia/ui/graphics_context.h" + +namespace xe { +namespace ui { +namespace d3d12 { + +class D3D12Context : public GraphicsContext { + public: + ~D3D12Context() override; + + ImmediateDrawer* immediate_drawer() override; + + bool is_current() override; + bool MakeCurrent() override; + void ClearCurrent() override; + + bool WasLost() override { return context_lost_; } + + std::unique_ptr Capture() override; + + // 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; + + private: + friend class D3D12Provider; + + explicit D3D12Context(D3D12Provider* provider, Window* target_window); + + private: + bool Initialize(); + void Shutdown(); + + bool initialized_fully_ = false; + + 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]; + + static constexpr uint32_t kSwapChainBufferCount = 3; + IDXGISwapChain3* swap_chain_ = nullptr; + ID3D12Resource* swap_chain_buffers_[kSwapChainBufferCount] = {}; + + std::unique_ptr immediate_drawer_ = nullptr; +}; + +} // namespace d3d12 +} // namespace ui +} // namespace xe + +#endif // XENIA_UI_D3D12_D3D12_CONTEXT_H_ diff --git a/src/xenia/ui/d3d12/d3d12_provider.cc b/src/xenia/ui/d3d12/d3d12_provider.cc index 2ac285370..2c0786621 100644 --- a/src/xenia/ui/d3d12/d3d12_provider.cc +++ b/src/xenia/ui/d3d12/d3d12_provider.cc @@ -11,9 +11,8 @@ #include -#include - #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_int32(d3d12_adapter_index, -1, "Index of the DXGI adapter to use. " @@ -160,6 +159,25 @@ bool D3D12Provider::IsDeviceSupported(ID3D12Device* device) { return !!options.TypedUAVLoadAdditionalFormats; } +std::unique_ptr D3D12Provider::CreateContext( + Window* target_window) { + auto new_context = + std::unique_ptr(new D3D12Context(this, target_window)); + if (!new_context->Initialize()) { + return nullptr; + } + return std::unique_ptr(new_context.release()); +} + +std::unique_ptr D3D12Provider::CreateOffscreenContext() { + auto new_context = + std::unique_ptr(new D3D12Context(this, nullptr)); + if (!new_context->Initialize()) { + return nullptr; + } + return std::unique_ptr(new_context.release()); +} + } // namespace d3d12 } // namespace ui } // namespace xe diff --git a/src/xenia/ui/d3d12/d3d12_provider.h b/src/xenia/ui/d3d12/d3d12_provider.h index d3fa03618..38b5514e3 100644 --- a/src/xenia/ui/d3d12/d3d12_provider.h +++ b/src/xenia/ui/d3d12/d3d12_provider.h @@ -25,6 +25,10 @@ class D3D12Provider : public GraphicsProvider { static std::unique_ptr Create(Window* main_window); + std::unique_ptr CreateContext( + Window* target_window) override; + std::unique_ptr CreateOffscreenContext() override; + IDXGIFactory2* get_dxgi_factory() const { return dxgi_factory_; } ID3D12Device* get_device() const { return device_; } ID3D12CommandQueue* get_direct_queue() const { return direct_queue_; }