[UI/D3D12] Small refactoring, allow BeginSwap to return false if no surface

This commit is contained in:
Triang3l 2020-09-14 23:27:19 +03:00
parent acb1fc059f
commit dfbe36a8aa
9 changed files with 149 additions and 132 deletions

View File

@ -9,9 +9,6 @@
#include "xenia/ui/d3d12/d3d12_context.h"
#include <cstdlib>
#include "xenia/base/cvar.h"
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/ui/d3d12/d3d12_immediate_drawer.h"
@ -19,9 +16,6 @@
#include "xenia/ui/d3d12/d3d12_util.h"
#include "xenia/ui/window.h"
DEFINE_bool(d3d12_random_clear_color, false,
"Randomize presentation back buffer clear color.", "D3D12");
namespace xe {
namespace ui {
namespace d3d12 {
@ -32,110 +26,112 @@ D3D12Context::D3D12Context(D3D12Provider* provider, Window* target_window)
D3D12Context::~D3D12Context() { Shutdown(); }
bool D3D12Context::Initialize() {
context_lost_ = false;
if (!target_window_) {
return true;
}
auto& provider = GetD3D12Provider();
auto dxgi_factory = provider.GetDXGIFactory();
auto device = provider.GetDevice();
auto direct_queue = provider.GetDirectQueue();
context_lost_ = false;
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_) {
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;
}
// 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;
swap_chain_desc.Width = swap_chain_width_;
swap_chain_desc.Height = swap_chain_height_;
swap_chain_desc.Format = kSwapChainFormat;
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.GetDirectQueue(),
static_cast<HWND>(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;
}
// 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;
swap_chain_desc.Width = swap_chain_width_;
swap_chain_desc.Height = swap_chain_height_;
swap_chain_desc.Format = kSwapChainFormat;
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.GetDirectQueue(),
reinterpret_cast<HWND>(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();
// Create a heap for RTV descriptors of swap chain buffers.
D3D12_DESCRIPTOR_HEAP_DESC rtv_heap_desc;
rtv_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
rtv_heap_desc.NumDescriptors = kSwapChainBufferCount;
rtv_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
rtv_heap_desc.NodeMask = 0;
if (FAILED(device->CreateDescriptorHeap(
&rtv_heap_desc, IID_PPV_ARGS(&swap_chain_rtv_heap_)))) {
XELOGE("Failed to create swap chain RTV descriptor heap");
// Create a heap for RTV descriptors of swap chain buffers.
D3D12_DESCRIPTOR_HEAP_DESC rtv_heap_desc;
rtv_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
rtv_heap_desc.NumDescriptors = kSwapChainBufferCount;
rtv_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
rtv_heap_desc.NodeMask = 0;
if (FAILED(device->CreateDescriptorHeap(
&rtv_heap_desc, IID_PPV_ARGS(&swap_chain_rtv_heap_)))) {
XELOGE("Failed to create swap chain RTV descriptor heap");
Shutdown();
return false;
}
swap_chain_rtv_heap_start_ =
swap_chain_rtv_heap_->GetCPUDescriptorHandleForHeapStart();
// Get the buffers and create their RTV descriptors.
if (!InitializeSwapChainBuffers()) {
Shutdown();
return false;
}
// Create the command list for compositing.
for (uint32_t i = 0; i < kSwapCommandAllocatorCount; ++i) {
if (FAILED(device->CreateCommandAllocator(
D3D12_COMMAND_LIST_TYPE_DIRECT,
IID_PPV_ARGS(&swap_command_allocators_[i])))) {
XELOGE("Failed to create a composition command allocator");
Shutdown();
return false;
}
swap_chain_rtv_heap_start_ =
swap_chain_rtv_heap_->GetCPUDescriptorHandleForHeapStart();
}
if (FAILED(device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT,
swap_command_allocators_[0], nullptr,
IID_PPV_ARGS(&swap_command_list_)))) {
XELOGE("Failed to create the composition graphics command list");
Shutdown();
return false;
}
// Initially in open state, wait until BeginSwap.
swap_command_list_->Close();
// Get the buffers and create their RTV descriptors.
if (!InitializeSwapChainBuffers()) {
Shutdown();
return false;
}
// Create the command list for compositing.
for (uint32_t i = 0; i < kSwapCommandAllocatorCount; ++i) {
if (FAILED(device->CreateCommandAllocator(
D3D12_COMMAND_LIST_TYPE_DIRECT,
IID_PPV_ARGS(&swap_command_allocators_[i])))) {
XELOGE("Failed to create a composition command allocator");
Shutdown();
return false;
}
}
if (FAILED(device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT,
swap_command_allocators_[0], nullptr,
IID_PPV_ARGS(&swap_command_list_)))) {
XELOGE("Failed to create the composition graphics command list");
Shutdown();
return false;
}
// Initially in open state, wait until BeginSwap.
swap_command_list_->Close();
// Initialize the immediate mode drawer if not offscreen.
immediate_drawer_ = std::make_unique<D3D12ImmediateDrawer>(*this);
if (!immediate_drawer_->Initialize()) {
Shutdown();
return false;
}
// Initialize the immediate mode drawer if not offscreen.
immediate_drawer_ = std::make_unique<D3D12ImmediateDrawer>(*this);
if (!immediate_drawer_->Initialize()) {
Shutdown();
return false;
}
return true;
@ -223,9 +219,11 @@ ImmediateDrawer* D3D12Context::immediate_drawer() {
return immediate_drawer_.get();
}
void D3D12Context::BeginSwap() {
bool D3D12Context::WasLost() { return context_lost_; }
bool D3D12Context::BeginSwap() {
if (!target_window_ || context_lost_) {
return;
return false;
}
// Resize the swap chain if the window is resized.
@ -252,13 +250,13 @@ void D3D12Context::BeginSwap() {
kSwapChainBufferCount, target_window_width, target_window_height,
kSwapChainFormat, 0))) {
context_lost_ = true;
return;
return false;
}
swap_chain_width_ = target_window_width;
swap_chain_height_ = target_window_height;
if (!InitializeSwapChainBuffers()) {
context_lost_ = true;
return;
return false;
}
}
@ -295,18 +293,11 @@ void D3D12Context::BeginSwap() {
D3D12_CPU_DESCRIPTOR_HANDLE back_buffer_rtv = GetSwapChainBackBufferRTV();
swap_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] = 0.0f;
clear_color[1] = 0.0f;
clear_color[2] = 0.0f;
}
clear_color[3] = 1.0f;
GetClearColor(clear_color);
swap_command_list_->ClearRenderTargetView(back_buffer_rtv, clear_color, 0,
nullptr);
return true;
}
void D3D12Context::EndSwap() {

View File

@ -28,9 +28,9 @@ class D3D12Context : public GraphicsContext {
ImmediateDrawer* immediate_drawer() override;
bool WasLost() override { return context_lost_; }
bool WasLost() override;
void BeginSwap() override;
bool BeginSwap() override;
void EndSwap() override;
std::unique_ptr<RawImage> Capture() override;
@ -69,11 +69,10 @@ class D3D12Context : public GraphicsContext {
private:
friend class D3D12Provider;
explicit D3D12Context(D3D12Provider* provider, Window* target_window);
bool Initialize();
private:
bool Initialize();
bool InitializeSwapChainBuffers();
void Shutdown();

View File

@ -383,6 +383,14 @@ bool D3D12Provider::Initialize() {
device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
// Check if optional features are supported.
// D3D12_HEAP_FLAG_CREATE_NOT_ZEROED requires Windows 10 2004 (indicated by
// the availability of ID3D12Device8 or D3D12_FEATURE_D3D12_OPTIONS7).
heap_flag_create_not_zeroed_ = D3D12_HEAP_FLAG_NONE;
D3D12_FEATURE_DATA_D3D12_OPTIONS7 options7;
if (SUCCEEDED(device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS7,
&options7, sizeof(options7)))) {
heap_flag_create_not_zeroed_ = D3D12_HEAP_FLAG_CREATE_NOT_ZEROED;
}
rasterizer_ordered_views_supported_ = false;
resource_binding_tier_ = D3D12_RESOURCE_BINDING_TIER_1;
tiled_resources_tier_ = D3D12_TILED_RESOURCES_TIER_NOT_SUPPORTED;
@ -409,14 +417,6 @@ bool D3D12Provider::Initialize() {
virtual_address_bits_per_resource_ =
virtual_address_support.MaxGPUVirtualAddressBitsPerResource;
}
// D3D12_HEAP_FLAG_CREATE_NOT_ZEROED requires Windows 10 2004 (indicated by
// the availability of ID3D12Device8 or D3D12_FEATURE_D3D12_OPTIONS7).
heap_flag_create_not_zeroed_ = D3D12_HEAP_FLAG_NONE;
D3D12_FEATURE_DATA_D3D12_OPTIONS7 options7;
if (SUCCEEDED(device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS7,
&options7, sizeof(options7)))) {
heap_flag_create_not_zeroed_ = D3D12_HEAP_FLAG_CREATE_NOT_ZEROED;
}
XELOGD3D(
"Direct3D 12 device and OS features:\n"
"* Max GPU virtual address bits per resource: {}\n"

View File

@ -68,6 +68,9 @@ class D3D12Provider : public GraphicsProvider {
uint32_t GetAdapterVendorID() const { return adapter_vendor_id_; }
// Device features.
D3D12_HEAP_FLAGS GetHeapFlagCreateNotZeroed() const {
return heap_flag_create_not_zeroed_;
}
D3D12_PROGRAMMABLE_SAMPLE_POSITIONS_TIER
GetProgrammableSamplePositionsTier() const {
return programmable_sample_positions_tier_;
@ -84,9 +87,6 @@ class D3D12Provider : public GraphicsProvider {
uint32_t GetVirtualAddressBitsPerResource() const {
return virtual_address_bits_per_resource_;
}
D3D12_HEAP_FLAGS GetHeapFlagCreateNotZeroed() const {
return heap_flag_create_not_zeroed_;
}
// Proxies for Direct3D 12 functions since they are loaded dynamically.
inline HRESULT SerializeRootSignature(const D3D12_ROOT_SIGNATURE_DESC* desc,
@ -162,12 +162,12 @@ class D3D12Provider : public GraphicsProvider {
uint32_t adapter_vendor_id_;
D3D12_HEAP_FLAGS heap_flag_create_not_zeroed_;
D3D12_PROGRAMMABLE_SAMPLE_POSITIONS_TIER programmable_sample_positions_tier_;
bool rasterizer_ordered_views_supported_;
D3D12_RESOURCE_BINDING_TIER resource_binding_tier_;
D3D12_TILED_RESOURCES_TIER tiled_resources_tier_;
uint32_t virtual_address_bits_per_resource_;
D3D12_HEAP_FLAGS heap_flag_create_not_zeroed_;
};
} // namespace d3d12

View File

@ -9,8 +9,13 @@
#include "xenia/ui/graphics_context.h"
#include <cstdlib>
#include "xenia/base/cvar.h"
#include "xenia/ui/graphics_provider.h"
DEFINE_bool(random_clear_color, false, "Randomize window clear color.", "UI");
namespace xe {
namespace ui {
@ -26,5 +31,18 @@ bool GraphicsContext::MakeCurrent() { return true; }
void GraphicsContext::ClearCurrent() {}
void GraphicsContext::GetClearColor(float* rgba) {
if (cvars::random_clear_color) {
rgba[0] = rand() / float(RAND_MAX); // NOLINT(runtime/threadsafe_fn)
rgba[1] = 1.0f;
rgba[2] = 0.0f;
} else {
rgba[0] = 0.0f;
rgba[1] = 0.0f;
rgba[2] = 0.0f;
}
rgba[3] = 1.0f;
}
} // namespace ui
} // namespace xe

View File

@ -51,7 +51,8 @@ class GraphicsContext {
// This context must be made current in order for this call to work properly.
virtual bool WasLost() = 0;
virtual void BeginSwap() = 0;
// Returns true if able to draw now (the target surface is available).
virtual bool BeginSwap() = 0;
virtual void EndSwap() = 0;
virtual std::unique_ptr<RawImage> Capture() = 0;
@ -59,6 +60,8 @@ class GraphicsContext {
protected:
explicit GraphicsContext(GraphicsProvider* provider, Window* target_window);
static void GetClearColor(float* rgba);
GraphicsProvider* provider_ = nullptr;
Window* target_window_ = nullptr;
};

View File

@ -141,7 +141,7 @@ bool VulkanContext::MakeCurrent() {
void VulkanContext::ClearCurrent() {}
void VulkanContext::BeginSwap() {
bool VulkanContext::BeginSwap() {
SCOPE_profile_cpu_f("gpu");
auto provider = static_cast<VulkanProvider*>(provider_);
auto device = provider->device();
@ -170,6 +170,8 @@ void VulkanContext::BeginSwap() {
// TODO(benvanik): use a fence instead? May not be possible with target image.
std::lock_guard<std::mutex> queue_lock(device->primary_queue_mutex());
status = vkQueueWaitIdle(device->primary_queue());
return true;
}
void VulkanContext::EndSwap() {

View File

@ -40,7 +40,7 @@ class VulkanContext : public GraphicsContext {
bool WasLost() override { return context_lost_; }
void BeginSwap() override;
bool BeginSwap() override;
void EndSwap() override;
std::unique_ptr<RawImage> Capture() override;

View File

@ -200,11 +200,15 @@ void Window::OnPaint(UIEvent* e) {
io.DisplaySize = ImVec2(static_cast<float>(scaled_width()),
static_cast<float>(scaled_height()));
context_->BeginSwap();
bool can_swap = context_->BeginSwap();
if (context_->WasLost()) {
on_context_lost(e);
return;
}
if (!can_swap) {
// Surface not available.
return;
}
ImGui::NewFrame();