[UI] Image post-processing and full presentation/window rework
[GPU] Add FXAA post-processing [UI] Add FidelityFX FSR and CAS post-processing [UI] Add blue noise dithering from 10bpc to 8bpc [GPU] Apply the DC PWL gamma ramp closer to the spec, supporting fully white color [UI] Allow the GPU CP thread to present on the host directly, bypassing the UI thread OS paint event [UI] Allow variable refresh rate (or tearing) [UI] Present the newest frame (restart) on DXGI [UI] Replace GraphicsContext with a far more advanced Presenter with more coherent surface connection and UI overlay state management [UI] Connect presentation to windows via the Surface class, not native window handles [Vulkan] Switch to simpler Vulkan setup with no instance/device separation due to interdependencies and to pass fewer objects around [Vulkan] Lower the minimum required Vulkan version to 1.0 [UI/GPU] Various cleanup, mainly ComPtr usage [UI] Support per-monitor DPI awareness v2 on Windows [UI] DPI-scale Dear ImGui [UI] Replace the remaining non-detachable window delegates with unified window event and input listeners [UI] Allow listeners to safely destroy or close the window, and to register/unregister listeners without use-after-free and the ABA problem [UI] Explicit Z ordering of input listeners and UI overlays, top-down for input, bottom-up for drawing [UI] Add explicit window lifecycle phases [UI] Replace Window virtual functions with explicit desired state, its application, actual state, its feedback [UI] GTK: Apply the initial size to the drawing area [UI] Limit internal UI frame rate to that of the monitor [UI] Hide the cursor using a timer instead of polling due to no repeated UI thread paints with GPU CP thread presentation, and only within the window
This commit is contained in:
@@ -10,15 +10,14 @@
|
||||
#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.
|
||||
// Must be included before D3D and DXGI for things like NOMINMAX.
|
||||
#include "xenia/base/platform_win.h"
|
||||
|
||||
#include <DXProgrammableCapture.h>
|
||||
#include <d3d12.h>
|
||||
#include <d3d12sdklayers.h>
|
||||
#include <d3dcompiler.h>
|
||||
#include <dcomp.h>
|
||||
#include <dxgi1_4.h>
|
||||
#include <dxgi1_5.h>
|
||||
#include <dxgidebug.h>
|
||||
// For Microsoft::WRL::ComPtr.
|
||||
#include <wrl/client.h>
|
||||
|
||||
@@ -1,379 +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/d3d12_context.h"
|
||||
|
||||
#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/d3d12/d3d12_util.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() {
|
||||
context_lost_ = false;
|
||||
|
||||
if (!target_window_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const D3D12Provider& provider = GetD3D12Provider();
|
||||
IDXGIFactory2* dxgi_factory = provider.GetDXGIFactory();
|
||||
ID3D12Device* device = provider.GetDevice();
|
||||
ID3D12CommandQueue* direct_queue = provider.GetDirectQueue();
|
||||
|
||||
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->CreateSwapChainForComposition(
|
||||
provider.GetDirectQueue(), &swap_chain_desc, nullptr,
|
||||
&swap_chain_1))) {
|
||||
XELOGE("Failed to create a DXGI swap chain for composition");
|
||||
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");
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (FAILED(device->CreateCommandList(
|
||||
0, D3D12_COMMAND_LIST_TYPE_DIRECT, swap_command_allocators_[0].Get(),
|
||||
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();
|
||||
|
||||
// Associate the swap chain with the window via DirectComposition.
|
||||
if (FAILED(provider.CreateDCompositionDevice(nullptr,
|
||||
IID_PPV_ARGS(&dcomp_device_)))) {
|
||||
XELOGE("Failed to create a DirectComposition device");
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
if (FAILED(dcomp_device_->CreateTargetForHwnd(
|
||||
reinterpret_cast<HWND>(target_window_->native_handle()), TRUE,
|
||||
&dcomp_target_))) {
|
||||
XELOGE("Failed to create a DirectComposition target for the window");
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
if (FAILED(dcomp_device_->CreateVisual(&dcomp_visual_))) {
|
||||
XELOGE("Failed to create a DirectComposition visual");
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
if (FAILED(dcomp_visual_->SetContent(swap_chain_.Get()))) {
|
||||
XELOGE(
|
||||
"Failed to set the content of the DirectComposition visual to the swap "
|
||||
"chain");
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
if (FAILED(dcomp_target_->SetRoot(dcomp_visual_.Get()))) {
|
||||
XELOGE(
|
||||
"Failed to set the root of the DirectComposition target to the swap "
|
||||
"chain visual");
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
if (FAILED(dcomp_device_->Commit())) {
|
||||
XELOGE("Failed to commit DirectComposition commands");
|
||||
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;
|
||||
}
|
||||
|
||||
bool D3D12Context::InitializeSwapChainBuffers() {
|
||||
// Get references to the buffers.
|
||||
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 {} of the swap chain", i);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the back buffer index for the first draw.
|
||||
swap_chain_back_buffer_index_ = swap_chain_->GetCurrentBackBufferIndex();
|
||||
|
||||
// Create RTV descriptors for the swap chain buffers.
|
||||
ID3D12Device* device = GetD3D12Provider().GetDevice();
|
||||
D3D12_RENDER_TARGET_VIEW_DESC rtv_desc;
|
||||
rtv_desc.Format = kSwapChainFormat;
|
||||
rtv_desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
|
||||
rtv_desc.Texture2D.MipSlice = 0;
|
||||
rtv_desc.Texture2D.PlaneSlice = 0;
|
||||
for (uint32_t i = 0; i < kSwapChainBufferCount; ++i) {
|
||||
device->CreateRenderTargetView(swap_chain_buffers_[i].Get(), &rtv_desc,
|
||||
GetSwapChainBufferRTV(i));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void D3D12Context::Shutdown() {
|
||||
if (!target_window_) {
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
immediate_drawer_.reset();
|
||||
|
||||
dcomp_visual_.Reset();
|
||||
dcomp_target_.Reset();
|
||||
dcomp_device_.Reset();
|
||||
|
||||
swap_command_list_.Reset();
|
||||
for (uint32_t i = 0; i < kSwapCommandAllocatorCount; ++i) {
|
||||
swap_command_allocators_[i].Reset();
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < kSwapChainBufferCount; ++i) {
|
||||
swap_chain_buffers_[i].Reset();
|
||||
}
|
||||
swap_chain_rtv_heap_.Reset();
|
||||
swap_chain_.Reset();
|
||||
|
||||
// First release the fence since it may reference the event.
|
||||
swap_fence_.Reset();
|
||||
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() {
|
||||
return immediate_drawer_.get();
|
||||
}
|
||||
|
||||
bool D3D12Context::WasLost() { return context_lost_; }
|
||||
|
||||
bool D3D12Context::BeginSwap() {
|
||||
if (!target_window_ || context_lost_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
// All buffer references must be released before resizing.
|
||||
for (uint32_t i = 0; i < kSwapChainBufferCount; ++i) {
|
||||
swap_chain_buffers_[i].Reset();
|
||||
}
|
||||
if (FAILED(swap_chain_->ResizeBuffers(
|
||||
kSwapChainBufferCount, target_window_width, target_window_height,
|
||||
kSwapChainFormat, 0))) {
|
||||
context_lost_ = true;
|
||||
return false;
|
||||
}
|
||||
swap_chain_width_ = target_window_width;
|
||||
swap_chain_height_ = target_window_height;
|
||||
if (!InitializeSwapChainBuffers()) {
|
||||
context_lost_ = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for a swap command allocator to become free.
|
||||
// Command allocator 0 is used when swap_fence_current_value_ is 1, 4, 7...
|
||||
swap_fence_completed_value_ = swap_fence_->GetCompletedValue();
|
||||
if (swap_fence_completed_value_ + kSwapCommandAllocatorCount <
|
||||
swap_fence_current_value_) {
|
||||
swap_fence_->SetEventOnCompletion(
|
||||
swap_fence_current_value_ - kSwapCommandAllocatorCount,
|
||||
swap_fence_completion_event_);
|
||||
WaitForSingleObject(swap_fence_completion_event_, INFINITE);
|
||||
swap_fence_completed_value_ = swap_fence_->GetCompletedValue();
|
||||
}
|
||||
|
||||
// Start the command list.
|
||||
uint32_t command_allocator_index =
|
||||
uint32_t((swap_fence_current_value_ + (kSwapCommandAllocatorCount - 1)) %
|
||||
kSwapCommandAllocatorCount);
|
||||
ID3D12CommandAllocator* command_allocator =
|
||||
swap_command_allocators_[command_allocator_index].Get();
|
||||
command_allocator->Reset();
|
||||
swap_command_list_->Reset(command_allocator, nullptr);
|
||||
|
||||
// Bind the back buffer as a render target and clear it.
|
||||
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_].Get();
|
||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||
swap_command_list_->ResourceBarrier(1, &barrier);
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE back_buffer_rtv = GetSwapChainBackBufferRTV();
|
||||
swap_command_list_->OMSetRenderTargets(1, &back_buffer_rtv, TRUE, nullptr);
|
||||
float clear_color[4];
|
||||
GetClearColor(clear_color);
|
||||
swap_command_list_->ClearRenderTargetView(back_buffer_rtv, clear_color, 0,
|
||||
nullptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void D3D12Context::EndSwap() {
|
||||
if (!target_window_ || context_lost_) {
|
||||
return;
|
||||
}
|
||||
|
||||
ID3D12CommandQueue* direct_queue = GetD3D12Provider().GetDirectQueue();
|
||||
|
||||
// Switch the back buffer to presentation state.
|
||||
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_].Get();
|
||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
|
||||
swap_command_list_->ResourceBarrier(1, &barrier);
|
||||
|
||||
// Submit the command list.
|
||||
swap_command_list_->Close();
|
||||
ID3D12CommandList* execute_command_lists[] = {swap_command_list_.Get()};
|
||||
direct_queue->ExecuteCommandLists(1, execute_command_lists);
|
||||
|
||||
// Present and check if the context was lost.
|
||||
if (FAILED(swap_chain_->Present(0, 0))) {
|
||||
context_lost_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Signal the fence to wait for frame resources to become free again.
|
||||
direct_queue->Signal(swap_fence_.Get(), 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() {
|
||||
// TODO(Triang3l): Read back swap chain front buffer.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE D3D12Context::GetSwapChainBufferRTV(
|
||||
uint32_t buffer_index) const {
|
||||
return GetD3D12Provider().OffsetRTVDescriptor(swap_chain_rtv_heap_start_,
|
||||
buffer_index);
|
||||
}
|
||||
|
||||
} // namespace d3d12
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
@@ -1,112 +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_D3D12_CONTEXT_H_
|
||||
#define XENIA_UI_D3D12_D3D12_CONTEXT_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "xenia/ui/d3d12/d3d12_immediate_drawer.h"
|
||||
#include "xenia/ui/d3d12/d3d12_provider.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 WasLost() override;
|
||||
|
||||
bool BeginSwap() override;
|
||||
void EndSwap() override;
|
||||
|
||||
std::unique_ptr<RawImage> Capture() override;
|
||||
|
||||
D3D12Provider& GetD3D12Provider() const {
|
||||
return static_cast<D3D12Provider&>(*provider_);
|
||||
}
|
||||
|
||||
// The format used by DWM.
|
||||
static constexpr DXGI_FORMAT kSwapChainFormat = DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
ID3D12Resource* GetSwapChainBuffer(uint32_t buffer_index) const {
|
||||
return swap_chain_buffers_[buffer_index].Get();
|
||||
}
|
||||
uint32_t GetSwapChainBackBufferIndex() const {
|
||||
return swap_chain_back_buffer_index_;
|
||||
}
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE GetSwapChainBufferRTV(
|
||||
uint32_t buffer_index) const;
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE GetSwapChainBackBufferRTV() const {
|
||||
return GetSwapChainBufferRTV(GetSwapChainBackBufferIndex());
|
||||
}
|
||||
void GetSwapChainSize(uint32_t& width, uint32_t& height) const {
|
||||
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_list_.Get();
|
||||
}
|
||||
|
||||
private:
|
||||
friend class D3D12Provider;
|
||||
explicit D3D12Context(D3D12Provider* provider, Window* target_window);
|
||||
bool Initialize();
|
||||
|
||||
private:
|
||||
bool InitializeSwapChainBuffers();
|
||||
void Shutdown();
|
||||
|
||||
bool context_lost_ = false;
|
||||
|
||||
static constexpr uint32_t kSwapChainBufferCount = 3;
|
||||
Microsoft::WRL::ComPtr<IDXGISwapChain3> swap_chain_;
|
||||
uint32_t swap_chain_width_ = 0, swap_chain_height_ = 0;
|
||||
Microsoft::WRL::ComPtr<ID3D12Resource>
|
||||
swap_chain_buffers_[kSwapChainBufferCount];
|
||||
uint32_t swap_chain_back_buffer_index_ = 0;
|
||||
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> swap_chain_rtv_heap_;
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE swap_chain_rtv_heap_start_;
|
||||
|
||||
uint64_t swap_fence_current_value_ = 1;
|
||||
uint64_t swap_fence_completed_value_ = 0;
|
||||
HANDLE swap_fence_completion_event_ = nullptr;
|
||||
Microsoft::WRL::ComPtr<ID3D12Fence> swap_fence_;
|
||||
|
||||
static constexpr uint32_t kSwapCommandAllocatorCount = 3;
|
||||
Microsoft::WRL::ComPtr<ID3D12CommandAllocator>
|
||||
swap_command_allocators_[kSwapCommandAllocatorCount];
|
||||
// Current command allocator is:
|
||||
// ((swap_fence_current_value_ + (kSwapCommandAllocatorCount - 1))) %
|
||||
// kSwapCommandAllocatorCount.
|
||||
Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList> swap_command_list_;
|
||||
|
||||
Microsoft::WRL::ComPtr<IDCompositionDevice> dcomp_device_;
|
||||
Microsoft::WRL::ComPtr<IDCompositionTarget> dcomp_target_;
|
||||
Microsoft::WRL::ComPtr<IDCompositionVisual> dcomp_visual_;
|
||||
|
||||
std::unique_ptr<D3D12ImmediateDrawer> immediate_drawer_;
|
||||
};
|
||||
|
||||
} // namespace d3d12
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_UI_D3D12_D3D12_CONTEXT_H_
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -41,6 +41,25 @@ void D3D12DescriptorHeapPool::Reclaim(uint64_t completed_submission_index) {
|
||||
}
|
||||
}
|
||||
|
||||
void D3D12DescriptorHeapPool::ChangeSubmissionTimeline() {
|
||||
// Reclaim all submitted pages.
|
||||
if (writable_last_) {
|
||||
writable_last_->next = submitted_first_;
|
||||
} else {
|
||||
writable_first_ = submitted_first_;
|
||||
}
|
||||
writable_last_ = submitted_last_;
|
||||
submitted_first_ = nullptr;
|
||||
submitted_last_ = nullptr;
|
||||
|
||||
// Mark all pages as never used yet in the new timeline.
|
||||
Page* page = writable_first_;
|
||||
while (page) {
|
||||
page->last_submission_index = 0;
|
||||
page = page->next;
|
||||
}
|
||||
}
|
||||
|
||||
void D3D12DescriptorHeapPool::ClearCache() {
|
||||
// Not checking current_page_used_ != 0 because asking for 0 descriptors
|
||||
// returns a valid heap also - but actually the new heap will be different now
|
||||
@@ -49,14 +68,12 @@ void D3D12DescriptorHeapPool::ClearCache() {
|
||||
current_page_used_ = 0;
|
||||
while (submitted_first_) {
|
||||
auto next = submitted_first_->next;
|
||||
submitted_first_->heap->Release();
|
||||
delete submitted_first_;
|
||||
submitted_first_ = next;
|
||||
}
|
||||
submitted_last_ = nullptr;
|
||||
while (writable_first_) {
|
||||
auto next = writable_first_->next;
|
||||
writable_first_->heap->Release();
|
||||
delete writable_first_;
|
||||
writable_first_ = next;
|
||||
}
|
||||
@@ -110,7 +127,7 @@ uint64_t D3D12DescriptorHeapPool::Request(uint64_t submission_index,
|
||||
new_heap_desc.NumDescriptors = page_size_;
|
||||
new_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
|
||||
new_heap_desc.NodeMask = 0;
|
||||
ID3D12DescriptorHeap* new_heap;
|
||||
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> new_heap;
|
||||
if (FAILED(device_->CreateDescriptorHeap(&new_heap_desc,
|
||||
IID_PPV_ARGS(&new_heap)))) {
|
||||
XELOGE("Failed to create a heap for {} shader-visible descriptors",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -30,6 +30,7 @@ class D3D12DescriptorHeapPool {
|
||||
~D3D12DescriptorHeapPool();
|
||||
|
||||
void Reclaim(uint64_t completed_submission_index);
|
||||
void ChangeSubmissionTimeline();
|
||||
void ClearCache();
|
||||
|
||||
// Because all descriptors for a single draw call must be in the same heap,
|
||||
@@ -65,7 +66,7 @@ class D3D12DescriptorHeapPool {
|
||||
// after a successful request because before a request, the heap may not exist
|
||||
// yet.
|
||||
ID3D12DescriptorHeap* GetLastRequestHeap() const {
|
||||
return writable_first_->heap;
|
||||
return writable_first_->heap.Get();
|
||||
}
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE GetLastRequestHeapCPUStart() const {
|
||||
return writable_first_->cpu_start;
|
||||
@@ -80,7 +81,7 @@ class D3D12DescriptorHeapPool {
|
||||
uint32_t page_size_;
|
||||
|
||||
struct Page {
|
||||
ID3D12DescriptorHeap* heap;
|
||||
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> heap;
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE cpu_start;
|
||||
D3D12_GPU_DESCRIPTOR_HANDLE gpu_start;
|
||||
uint64_t last_submission_index;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2018 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -17,7 +17,7 @@
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/ui/d3d12/d3d12_context.h"
|
||||
#include "xenia/ui/d3d12/d3d12_presenter.h"
|
||||
#include "xenia/ui/d3d12/d3d12_util.h"
|
||||
|
||||
namespace xe {
|
||||
@@ -38,35 +38,40 @@ D3D12ImmediateDrawer::D3D12ImmediateTexture::D3D12ImmediateTexture(
|
||||
resource_(resource),
|
||||
sampler_index_(sampler_index),
|
||||
immediate_drawer_(immediate_drawer),
|
||||
immediate_drawer_index_(immediate_drawer_index) {
|
||||
if (resource_) {
|
||||
resource_->AddRef();
|
||||
}
|
||||
}
|
||||
immediate_drawer_index_(immediate_drawer_index) {}
|
||||
|
||||
D3D12ImmediateDrawer::D3D12ImmediateTexture::~D3D12ImmediateTexture() {
|
||||
if (immediate_drawer_) {
|
||||
immediate_drawer_->OnImmediateTextureDestroyed(*this);
|
||||
}
|
||||
if (resource_) {
|
||||
resource_->Release();
|
||||
}
|
||||
}
|
||||
|
||||
void D3D12ImmediateDrawer::D3D12ImmediateTexture::OnImmediateDrawerShutdown() {
|
||||
void D3D12ImmediateDrawer::D3D12ImmediateTexture::OnImmediateDrawerDestroyed() {
|
||||
immediate_drawer_ = nullptr;
|
||||
// Lifetime is not managed anymore, so don't keep the resource either.
|
||||
util::ReleaseAndNull(resource_);
|
||||
resource_.Reset();
|
||||
}
|
||||
|
||||
D3D12ImmediateDrawer::D3D12ImmediateDrawer(D3D12Context& graphics_context)
|
||||
: ImmediateDrawer(&graphics_context), context_(graphics_context) {}
|
||||
D3D12ImmediateDrawer::~D3D12ImmediateDrawer() {
|
||||
// Await GPU usage completion of all draws and texture uploads (which happen
|
||||
// before draws).
|
||||
auto d3d12_presenter = static_cast<D3D12Presenter*>(presenter());
|
||||
if (d3d12_presenter) {
|
||||
d3d12_presenter->AwaitUISubmissionCompletionFromUIThread(
|
||||
last_paint_submission_index_);
|
||||
}
|
||||
|
||||
D3D12ImmediateDrawer::~D3D12ImmediateDrawer() { Shutdown(); }
|
||||
// Texture resources and descriptors are owned and tracked by the immediate
|
||||
// drawer. Zombie texture objects are supported, but are meaningless.
|
||||
assert_true(textures_.empty());
|
||||
for (D3D12ImmediateTexture* texture : textures_) {
|
||||
texture->OnImmediateDrawerDestroyed();
|
||||
}
|
||||
textures_.clear();
|
||||
}
|
||||
|
||||
bool D3D12ImmediateDrawer::Initialize() {
|
||||
const D3D12Provider& provider = context_.GetD3D12Provider();
|
||||
ID3D12Device* device = provider.GetDevice();
|
||||
ID3D12Device* device = provider_.GetDevice();
|
||||
|
||||
// Create the root signature.
|
||||
D3D12_ROOT_PARAMETER root_parameters[size_t(RootParameter::kCount)];
|
||||
@@ -99,7 +104,7 @@ bool D3D12ImmediateDrawer::Initialize() {
|
||||
}
|
||||
{
|
||||
auto& root_parameter =
|
||||
root_parameters[size_t(RootParameter::kViewportSizeInv)];
|
||||
root_parameters[size_t(RootParameter::kCoordinateSpaceSizeInv)];
|
||||
root_parameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
|
||||
root_parameter.Constants.ShaderRegister = 0;
|
||||
root_parameter.Constants.RegisterSpace = 0;
|
||||
@@ -113,16 +118,16 @@ bool D3D12ImmediateDrawer::Initialize() {
|
||||
root_signature_desc.pStaticSamplers = nullptr;
|
||||
root_signature_desc.Flags =
|
||||
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
|
||||
root_signature_ = util::CreateRootSignature(provider, root_signature_desc);
|
||||
if (root_signature_ == nullptr) {
|
||||
XELOGE("Failed to create the Direct3D 12 immediate drawer root signature");
|
||||
Shutdown();
|
||||
*(root_signature_.ReleaseAndGetAddressOf()) =
|
||||
util::CreateRootSignature(provider_, root_signature_desc);
|
||||
if (!root_signature_) {
|
||||
XELOGE("D3D12ImmediateDrawer: Failed to create the root signature");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create the pipelines.
|
||||
D3D12_GRAPHICS_PIPELINE_STATE_DESC pipeline_desc = {};
|
||||
pipeline_desc.pRootSignature = root_signature_;
|
||||
pipeline_desc.pRootSignature = root_signature_.Get();
|
||||
pipeline_desc.VS.pShaderBytecode = shaders::immediate_vs;
|
||||
pipeline_desc.VS.BytecodeLength = sizeof(shaders::immediate_vs);
|
||||
pipeline_desc.PS.pShaderBytecode = shaders::immediate_ps;
|
||||
@@ -133,13 +138,10 @@ bool D3D12ImmediateDrawer::Initialize() {
|
||||
pipeline_blend_desc.SrcBlend = D3D12_BLEND_SRC_ALPHA;
|
||||
pipeline_blend_desc.DestBlend = D3D12_BLEND_INV_SRC_ALPHA;
|
||||
pipeline_blend_desc.BlendOp = D3D12_BLEND_OP_ADD;
|
||||
// Don't change alpha (always 1).
|
||||
pipeline_blend_desc.SrcBlendAlpha = D3D12_BLEND_ZERO;
|
||||
pipeline_blend_desc.SrcBlendAlpha = D3D12_BLEND_ONE;
|
||||
pipeline_blend_desc.DestBlendAlpha = D3D12_BLEND_ONE;
|
||||
pipeline_blend_desc.BlendOpAlpha = D3D12_BLEND_OP_ADD;
|
||||
pipeline_blend_desc.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_RED |
|
||||
D3D12_COLOR_WRITE_ENABLE_GREEN |
|
||||
D3D12_COLOR_WRITE_ENABLE_BLUE;
|
||||
pipeline_blend_desc.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
|
||||
pipeline_desc.SampleMask = UINT_MAX;
|
||||
pipeline_desc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID;
|
||||
pipeline_desc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
|
||||
@@ -161,23 +163,17 @@ bool D3D12ImmediateDrawer::Initialize() {
|
||||
UINT(xe::countof(pipeline_input_elements));
|
||||
pipeline_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
|
||||
pipeline_desc.NumRenderTargets = 1;
|
||||
pipeline_desc.RTVFormats[0] = D3D12Context::kSwapChainFormat;
|
||||
pipeline_desc.RTVFormats[0] = D3D12Presenter::kSwapChainFormat;
|
||||
pipeline_desc.SampleDesc.Count = 1;
|
||||
if (FAILED(device->CreateGraphicsPipelineState(
|
||||
&pipeline_desc, IID_PPV_ARGS(&pipeline_triangle_)))) {
|
||||
XELOGE(
|
||||
"Failed to create the Direct3D 12 immediate drawer triangle pipeline "
|
||||
"state");
|
||||
Shutdown();
|
||||
XELOGE("D3D12ImmediateDrawer: Failed to create the triangle pipeline");
|
||||
return false;
|
||||
}
|
||||
pipeline_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE;
|
||||
if (FAILED(device->CreateGraphicsPipelineState(
|
||||
&pipeline_desc, IID_PPV_ARGS(&pipeline_line_)))) {
|
||||
XELOGE(
|
||||
"Failed to create the Direct3D 12 immediate drawer line pipeline "
|
||||
"state");
|
||||
Shutdown();
|
||||
XELOGE("D3D12ImmediateDrawer: Failed to create the line pipeline");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -190,14 +186,12 @@ bool D3D12ImmediateDrawer::Initialize() {
|
||||
if (FAILED(device->CreateDescriptorHeap(&sampler_heap_desc,
|
||||
IID_PPV_ARGS(&sampler_heap_)))) {
|
||||
XELOGE(
|
||||
"Failed to create the Direct3D 12 immediate drawer sampler descriptor "
|
||||
"heap");
|
||||
Shutdown();
|
||||
"D3D12ImmediateDrawer: Failed to create the sampler descriptor heap");
|
||||
return false;
|
||||
}
|
||||
sampler_heap_cpu_start_ = sampler_heap_->GetCPUDescriptorHandleForHeapStart();
|
||||
sampler_heap_gpu_start_ = sampler_heap_->GetGPUDescriptorHandleForHeapStart();
|
||||
uint32_t sampler_size = provider.GetSamplerDescriptorSize();
|
||||
uint32_t sampler_size = provider_.GetSamplerDescriptorSize();
|
||||
// Nearest neighbor, clamp.
|
||||
D3D12_SAMPLER_DESC sampler_desc = {};
|
||||
sampler_desc.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
|
||||
@@ -228,58 +222,22 @@ bool D3D12ImmediateDrawer::Initialize() {
|
||||
device->CreateSampler(&sampler_desc, sampler_handle);
|
||||
|
||||
// Create pools for draws.
|
||||
vertex_buffer_pool_ = std::make_unique<D3D12UploadBufferPool>(provider);
|
||||
vertex_buffer_pool_ = std::make_unique<D3D12UploadBufferPool>(provider_);
|
||||
texture_descriptor_pool_ = std::make_unique<D3D12DescriptorHeapPool>(
|
||||
device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 2048);
|
||||
|
||||
// Reset the current state.
|
||||
current_command_list_ = nullptr;
|
||||
batch_open_ = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void D3D12ImmediateDrawer::Shutdown() {
|
||||
for (auto& deleted_texture : textures_deleted_) {
|
||||
deleted_texture.first->Release();
|
||||
}
|
||||
textures_deleted_.clear();
|
||||
|
||||
for (auto& texture_upload : texture_uploads_submitted_) {
|
||||
texture_upload.buffer->Release();
|
||||
texture_upload.texture->Release();
|
||||
}
|
||||
texture_uploads_submitted_.clear();
|
||||
|
||||
for (auto& texture_upload : texture_uploads_pending_) {
|
||||
texture_upload.buffer->Release();
|
||||
texture_upload.texture->Release();
|
||||
}
|
||||
texture_uploads_pending_.clear();
|
||||
|
||||
for (D3D12ImmediateTexture* texture : textures_) {
|
||||
texture->OnImmediateDrawerShutdown();
|
||||
}
|
||||
textures_.clear();
|
||||
|
||||
texture_descriptor_pool_.reset();
|
||||
vertex_buffer_pool_.reset();
|
||||
|
||||
util::ReleaseAndNull(sampler_heap_);
|
||||
|
||||
util::ReleaseAndNull(pipeline_line_);
|
||||
util::ReleaseAndNull(pipeline_triangle_);
|
||||
|
||||
util::ReleaseAndNull(root_signature_);
|
||||
}
|
||||
|
||||
std::unique_ptr<ImmediateTexture> D3D12ImmediateDrawer::CreateTexture(
|
||||
uint32_t width, uint32_t height, ImmediateTextureFilter filter,
|
||||
bool is_repeated, const uint8_t* data) {
|
||||
const D3D12Provider& provider = context_.GetD3D12Provider();
|
||||
ID3D12Device* device = provider.GetDevice();
|
||||
ID3D12Device* device = provider_.GetDevice();
|
||||
D3D12_HEAP_FLAGS heap_flag_create_not_zeroed =
|
||||
provider.GetHeapFlagCreateNotZeroed();
|
||||
provider_.GetHeapFlagCreateNotZeroed();
|
||||
|
||||
D3D12_RESOURCE_DESC resource_desc;
|
||||
resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
|
||||
@@ -293,8 +251,8 @@ std::unique_ptr<ImmediateTexture> D3D12ImmediateDrawer::CreateTexture(
|
||||
resource_desc.SampleDesc.Quality = 0;
|
||||
resource_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
|
||||
resource_desc.Flags = D3D12_RESOURCE_FLAG_NONE;
|
||||
ID3D12Resource* resource;
|
||||
if (SUCCEEDED(provider.GetDevice()->CreateCommittedResource(
|
||||
Microsoft::WRL::ComPtr<ID3D12Resource> resource;
|
||||
if (SUCCEEDED(device->CreateCommittedResource(
|
||||
&util::kHeapPropertiesDefault, heap_flag_create_not_zeroed,
|
||||
&resource_desc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr,
|
||||
IID_PPV_ARGS(&resource)))) {
|
||||
@@ -306,7 +264,7 @@ std::unique_ptr<ImmediateTexture> D3D12ImmediateDrawer::CreateTexture(
|
||||
D3D12_RESOURCE_DESC upload_buffer_desc;
|
||||
util::FillBufferResourceDesc(upload_buffer_desc, upload_size,
|
||||
D3D12_RESOURCE_FLAG_NONE);
|
||||
ID3D12Resource* upload_buffer;
|
||||
Microsoft::WRL::ComPtr<ID3D12Resource> upload_buffer;
|
||||
if (SUCCEEDED(device->CreateCommittedResource(
|
||||
&util::kHeapPropertiesUpload, heap_flag_create_not_zeroed,
|
||||
&upload_buffer_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr,
|
||||
@@ -333,35 +291,30 @@ std::unique_ptr<ImmediateTexture> D3D12ImmediateDrawer::CreateTexture(
|
||||
}
|
||||
upload_buffer->Unmap(0, nullptr);
|
||||
// Defer uploading and transition to the next draw.
|
||||
PendingTextureUpload& pending_upload =
|
||||
texture_uploads_pending_.emplace_back();
|
||||
// While the upload has not been yet completed, keep a reference to the
|
||||
// resource because its lifetime is not tied to that of the
|
||||
// ImmediateTexture (and thus to context's submissions) now.
|
||||
resource->AddRef();
|
||||
pending_upload.texture = resource;
|
||||
pending_upload.buffer = upload_buffer;
|
||||
PendingTextureUpload& pending_upload =
|
||||
texture_uploads_pending_.emplace_back(resource.Get(),
|
||||
upload_buffer.Get());
|
||||
} else {
|
||||
XELOGE(
|
||||
"Failed to map a Direct3D 12 upload buffer for a {}x{} texture for "
|
||||
"immediate drawing",
|
||||
"D3D12ImmediateDrawer: Failed to map an upload buffer for a {}x{} "
|
||||
"texture",
|
||||
width, height);
|
||||
upload_buffer->Release();
|
||||
resource->Release();
|
||||
resource = nullptr;
|
||||
upload_buffer.Reset();
|
||||
resource.Reset();
|
||||
}
|
||||
} else {
|
||||
XELOGE(
|
||||
"Failed to create a Direct3D 12 upload buffer for a {}x{} texture "
|
||||
"for immediate drawing",
|
||||
"D3D12ImmediateDrawer: Failed to create an upload buffer for a {}x{} "
|
||||
"texture",
|
||||
width, height);
|
||||
resource->Release();
|
||||
resource = nullptr;
|
||||
resource.Reset();
|
||||
}
|
||||
} else {
|
||||
XELOGE("Failed to create a {}x{} Direct3D 12 texture for immediate drawing",
|
||||
width, height);
|
||||
resource = nullptr;
|
||||
XELOGE("D3D12ImmediateDrawer: Failed to create a {}x{} texture", width,
|
||||
height);
|
||||
}
|
||||
|
||||
SamplerIndex sampler_index;
|
||||
@@ -376,35 +329,38 @@ std::unique_ptr<ImmediateTexture> D3D12ImmediateDrawer::CreateTexture(
|
||||
// Manage by this immediate drawer if successfully created a resource.
|
||||
std::unique_ptr<D3D12ImmediateTexture> texture =
|
||||
std::make_unique<D3D12ImmediateTexture>(
|
||||
width, height, resource, sampler_index, resource ? this : nullptr,
|
||||
textures_.size());
|
||||
width, height, resource.Get(), sampler_index,
|
||||
resource ? this : nullptr, textures_.size());
|
||||
if (resource) {
|
||||
textures_.push_back(texture.get());
|
||||
// D3D12ImmediateTexture now holds a reference.
|
||||
resource->Release();
|
||||
}
|
||||
return std::move(texture);
|
||||
}
|
||||
|
||||
void D3D12ImmediateDrawer::Begin(int render_target_width,
|
||||
int render_target_height) {
|
||||
assert_null(current_command_list_);
|
||||
void D3D12ImmediateDrawer::Begin(UIDrawContext& ui_draw_context,
|
||||
float coordinate_space_width,
|
||||
float coordinate_space_height) {
|
||||
ImmediateDrawer::Begin(ui_draw_context, coordinate_space_width,
|
||||
coordinate_space_height);
|
||||
|
||||
assert_false(batch_open_);
|
||||
|
||||
ID3D12Device* device = context_.GetD3D12Provider().GetDevice();
|
||||
const D3D12UIDrawContext& d3d12_ui_draw_context =
|
||||
static_cast<const D3D12UIDrawContext&>(ui_draw_context);
|
||||
|
||||
// Use the compositing command list.
|
||||
current_command_list_ = context_.GetSwapCommandList();
|
||||
|
||||
uint64_t completed_fence_value = context_.GetSwapCompletedFenceValue();
|
||||
// Update the submission index to be used throughout the current immediate
|
||||
// drawer paint.
|
||||
last_paint_submission_index_ =
|
||||
d3d12_ui_draw_context.submission_index_current();
|
||||
last_completed_submission_index_ =
|
||||
d3d12_ui_draw_context.submission_index_completed();
|
||||
|
||||
// Release deleted textures.
|
||||
for (auto it = textures_deleted_.begin(); it != textures_deleted_.end();) {
|
||||
if (it->second > completed_fence_value) {
|
||||
if (it->second > last_completed_submission_index_) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
it->first->Release();
|
||||
if (std::next(it) != textures_deleted_.end()) {
|
||||
*it = textures_deleted_.back();
|
||||
}
|
||||
@@ -414,37 +370,45 @@ void D3D12ImmediateDrawer::Begin(int render_target_width,
|
||||
// Release upload buffers for completed texture uploads.
|
||||
auto erase_uploads_end = texture_uploads_submitted_.begin();
|
||||
while (erase_uploads_end != texture_uploads_submitted_.end()) {
|
||||
if (erase_uploads_end->fence_value > completed_fence_value) {
|
||||
if (erase_uploads_end->submission_index >
|
||||
last_completed_submission_index_) {
|
||||
break;
|
||||
}
|
||||
erase_uploads_end->buffer->Release();
|
||||
// Release the texture reference held for uploading.
|
||||
erase_uploads_end->texture->Release();
|
||||
++erase_uploads_end;
|
||||
}
|
||||
texture_uploads_submitted_.erase(texture_uploads_submitted_.begin(),
|
||||
erase_uploads_end);
|
||||
|
||||
vertex_buffer_pool_->Reclaim(completed_fence_value);
|
||||
texture_descriptor_pool_->Reclaim(completed_fence_value);
|
||||
// Make sure textures created before the current frame are uploaded, even if
|
||||
// nothing was drawn in the previous frames or nothing will be drawn in the
|
||||
// current or subsequent ones, as that would result in upload buffers kept
|
||||
// forever.
|
||||
UploadTextures();
|
||||
|
||||
texture_descriptor_pool_->Reclaim(last_completed_submission_index_);
|
||||
vertex_buffer_pool_->Reclaim(last_completed_submission_index_);
|
||||
|
||||
// Begin drawing.
|
||||
|
||||
ID3D12GraphicsCommandList* command_list =
|
||||
d3d12_ui_draw_context.command_list();
|
||||
|
||||
current_render_target_width_ = render_target_width;
|
||||
current_render_target_height_ = render_target_height;
|
||||
D3D12_VIEWPORT viewport;
|
||||
viewport.TopLeftX = 0.0f;
|
||||
viewport.TopLeftY = 0.0f;
|
||||
viewport.Width = float(render_target_width);
|
||||
viewport.Height = float(render_target_height);
|
||||
viewport.Width = float(d3d12_ui_draw_context.render_target_width());
|
||||
viewport.Height = float(d3d12_ui_draw_context.render_target_height());
|
||||
viewport.MinDepth = 0.0f;
|
||||
viewport.MaxDepth = 1.0f;
|
||||
current_command_list_->RSSetViewports(1, &viewport);
|
||||
command_list->RSSetViewports(1, &viewport);
|
||||
|
||||
current_command_list_->SetGraphicsRootSignature(root_signature_);
|
||||
float viewport_inv_size[2];
|
||||
viewport_inv_size[0] = 1.0f / viewport.Width;
|
||||
viewport_inv_size[1] = 1.0f / viewport.Height;
|
||||
current_command_list_->SetGraphicsRoot32BitConstants(
|
||||
UINT(RootParameter::kViewportSizeInv), 2, viewport_inv_size, 0);
|
||||
command_list->SetGraphicsRootSignature(root_signature_.Get());
|
||||
float coordinate_space_size_inv[2];
|
||||
coordinate_space_size_inv[0] = 1.0f / coordinate_space_width;
|
||||
coordinate_space_size_inv[1] = 1.0f / coordinate_space_height;
|
||||
command_list->SetGraphicsRoot32BitConstants(
|
||||
UINT(RootParameter::kCoordinateSpaceSizeInv), 2,
|
||||
coordinate_space_size_inv, 0);
|
||||
|
||||
current_scissor_.left = 0;
|
||||
current_scissor_.top = 0;
|
||||
@@ -460,9 +424,12 @@ void D3D12ImmediateDrawer::Begin(int render_target_width,
|
||||
|
||||
void D3D12ImmediateDrawer::BeginDrawBatch(const ImmediateDrawBatch& batch) {
|
||||
assert_false(batch_open_);
|
||||
assert_not_null(current_command_list_);
|
||||
|
||||
uint64_t current_fence_value = context_.GetSwapCurrentFenceValue();
|
||||
const D3D12UIDrawContext& d3d12_ui_draw_context =
|
||||
*static_cast<const D3D12UIDrawContext*>(ui_draw_context());
|
||||
|
||||
ID3D12GraphicsCommandList* command_list =
|
||||
d3d12_ui_draw_context.command_list();
|
||||
|
||||
// Bind the vertices.
|
||||
D3D12_VERTEX_BUFFER_VIEW vertex_buffer_view;
|
||||
@@ -470,16 +437,16 @@ void D3D12ImmediateDrawer::BeginDrawBatch(const ImmediateDrawBatch& batch) {
|
||||
vertex_buffer_view.SizeInBytes =
|
||||
UINT(sizeof(ImmediateVertex)) * batch.vertex_count;
|
||||
void* vertex_buffer_mapping = vertex_buffer_pool_->Request(
|
||||
current_fence_value, vertex_buffer_view.SizeInBytes, sizeof(float),
|
||||
nullptr, nullptr, &vertex_buffer_view.BufferLocation);
|
||||
last_paint_submission_index_, vertex_buffer_view.SizeInBytes,
|
||||
sizeof(float), nullptr, nullptr, &vertex_buffer_view.BufferLocation);
|
||||
if (vertex_buffer_mapping == nullptr) {
|
||||
XELOGE("Failed to get a buffer for {} vertices in the immediate drawer",
|
||||
XELOGE("D3D12ImmediateDrawer: Failed to get a buffer for {} vertices",
|
||||
batch.vertex_count);
|
||||
return;
|
||||
}
|
||||
std::memcpy(vertex_buffer_mapping, batch.vertices,
|
||||
vertex_buffer_view.SizeInBytes);
|
||||
current_command_list_->IASetVertexBuffers(0, 1, &vertex_buffer_view);
|
||||
command_list->IASetVertexBuffers(0, 1, &vertex_buffer_view);
|
||||
|
||||
// Bind the indices.
|
||||
batch_has_index_buffer_ = batch.indices != nullptr;
|
||||
@@ -488,16 +455,16 @@ void D3D12ImmediateDrawer::BeginDrawBatch(const ImmediateDrawBatch& batch) {
|
||||
index_buffer_view.SizeInBytes = UINT(sizeof(uint16_t)) * batch.index_count;
|
||||
index_buffer_view.Format = DXGI_FORMAT_R16_UINT;
|
||||
void* index_buffer_mapping = vertex_buffer_pool_->Request(
|
||||
current_fence_value, index_buffer_view.SizeInBytes, sizeof(uint16_t),
|
||||
nullptr, nullptr, &index_buffer_view.BufferLocation);
|
||||
last_paint_submission_index_, index_buffer_view.SizeInBytes,
|
||||
sizeof(uint16_t), nullptr, nullptr, &index_buffer_view.BufferLocation);
|
||||
if (index_buffer_mapping == nullptr) {
|
||||
XELOGE("Failed to get a buffer for {} indices in the immediate drawer",
|
||||
XELOGE("D3D12ImmediateDrawer: Failed to get a buffer for {} indices",
|
||||
batch.index_count);
|
||||
return;
|
||||
}
|
||||
std::memcpy(index_buffer_mapping, batch.indices,
|
||||
index_buffer_view.SizeInBytes);
|
||||
current_command_list_->IASetIndexBuffer(&index_buffer_view);
|
||||
command_list->IASetIndexBuffer(&index_buffer_view);
|
||||
}
|
||||
|
||||
batch_open_ = true;
|
||||
@@ -509,30 +476,30 @@ void D3D12ImmediateDrawer::Draw(const ImmediateDraw& draw) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the scissor rectangle if enabled.
|
||||
D3D12_RECT scissor;
|
||||
if (draw.scissor) {
|
||||
scissor.left = draw.scissor_rect[0];
|
||||
scissor.top = current_render_target_height_ -
|
||||
(draw.scissor_rect[1] + draw.scissor_rect[3]);
|
||||
scissor.right = scissor.left + draw.scissor_rect[2];
|
||||
scissor.bottom = scissor.top + draw.scissor_rect[3];
|
||||
} else {
|
||||
scissor.left = 0;
|
||||
scissor.top = 0;
|
||||
scissor.right = current_render_target_width_;
|
||||
scissor.bottom = current_render_target_height_;
|
||||
}
|
||||
if (scissor.right <= scissor.left || scissor.bottom <= scissor.top) {
|
||||
// Nothing is visible (used as the default current_scissor_ value also).
|
||||
const D3D12UIDrawContext& d3d12_ui_draw_context =
|
||||
*static_cast<const D3D12UIDrawContext*>(ui_draw_context());
|
||||
ID3D12GraphicsCommandList* command_list =
|
||||
d3d12_ui_draw_context.command_list();
|
||||
|
||||
// Set the scissor rectangle.
|
||||
uint32_t scissor_left, scissor_top, scissor_width, scissor_height;
|
||||
if (!ScissorToRenderTarget(draw, scissor_left, scissor_top, scissor_width,
|
||||
scissor_height)) {
|
||||
// Nothing is visible (zero area is used as the default current_scissor_
|
||||
// value also).
|
||||
return;
|
||||
}
|
||||
D3D12_RECT scissor;
|
||||
scissor.left = LONG(scissor_left);
|
||||
scissor.top = LONG(scissor_top);
|
||||
scissor.right = LONG(scissor_left + scissor_width);
|
||||
scissor.bottom = LONG(scissor_top + scissor_height);
|
||||
if (current_scissor_.left != scissor.left ||
|
||||
current_scissor_.top != scissor.top ||
|
||||
current_scissor_.right != scissor.right ||
|
||||
current_scissor_.bottom != scissor.bottom) {
|
||||
current_scissor_ = scissor;
|
||||
current_command_list_->RSSetScissorRects(1, &scissor);
|
||||
command_list->RSSetScissorRects(1, &scissor);
|
||||
}
|
||||
|
||||
// Ensure texture data is available if any texture is loaded, upload all in a
|
||||
@@ -542,30 +509,27 @@ void D3D12ImmediateDrawer::Draw(const ImmediateDraw& draw) {
|
||||
// Bind the texture. If this is the first draw in a frame, the descriptor heap
|
||||
// index will be invalid initially, and the texture will be bound regardless
|
||||
// of what's in current_texture_.
|
||||
uint64_t current_fence_value = context_.GetSwapCurrentFenceValue();
|
||||
auto texture = static_cast<D3D12ImmediateTexture*>(draw.texture);
|
||||
ID3D12Resource* texture_resource = texture ? texture->resource() : nullptr;
|
||||
bool bind_texture = current_texture_ != texture_resource;
|
||||
uint32_t texture_descriptor_index;
|
||||
uint64_t texture_heap_index = texture_descriptor_pool_->Request(
|
||||
current_fence_value, current_texture_descriptor_heap_index_,
|
||||
last_paint_submission_index_, current_texture_descriptor_heap_index_,
|
||||
bind_texture ? 1 : 0, 1, texture_descriptor_index);
|
||||
if (texture_heap_index == D3D12DescriptorHeapPool::kHeapIndexInvalid) {
|
||||
return;
|
||||
}
|
||||
if (texture_resource) {
|
||||
texture->SetLastUsageFenceValue(current_fence_value);
|
||||
texture->SetLastUsageSubmissionIndex(last_paint_submission_index_);
|
||||
}
|
||||
if (current_texture_descriptor_heap_index_ != texture_heap_index) {
|
||||
current_texture_descriptor_heap_index_ = texture_heap_index;
|
||||
bind_texture = true;
|
||||
ID3D12DescriptorHeap* descriptor_heaps[] = {
|
||||
texture_descriptor_pool_->GetLastRequestHeap(), sampler_heap_};
|
||||
current_command_list_->SetDescriptorHeaps(2, descriptor_heaps);
|
||||
texture_descriptor_pool_->GetLastRequestHeap(), sampler_heap_.Get()};
|
||||
command_list->SetDescriptorHeaps(2, descriptor_heaps);
|
||||
}
|
||||
|
||||
const D3D12Provider& provider = context_.GetD3D12Provider();
|
||||
|
||||
if (bind_texture) {
|
||||
current_texture_ = texture_resource;
|
||||
D3D12_SHADER_RESOURCE_VIEW_DESC texture_view_desc;
|
||||
@@ -587,14 +551,14 @@ void D3D12ImmediateDrawer::Draw(const ImmediateDraw& draw) {
|
||||
texture_view_desc.Texture2D.MipLevels = 1;
|
||||
texture_view_desc.Texture2D.PlaneSlice = 0;
|
||||
texture_view_desc.Texture2D.ResourceMinLODClamp = 0.0f;
|
||||
provider.GetDevice()->CreateShaderResourceView(
|
||||
provider_.GetDevice()->CreateShaderResourceView(
|
||||
texture_resource, &texture_view_desc,
|
||||
provider.OffsetViewDescriptor(
|
||||
provider_.OffsetViewDescriptor(
|
||||
texture_descriptor_pool_->GetLastRequestHeapCPUStart(),
|
||||
texture_descriptor_index));
|
||||
current_command_list_->SetGraphicsRootDescriptorTable(
|
||||
command_list->SetGraphicsRootDescriptorTable(
|
||||
UINT(RootParameter::kTexture),
|
||||
provider.OffsetViewDescriptor(
|
||||
provider_.OffsetViewDescriptor(
|
||||
texture_descriptor_pool_->GetLastRequestHeapGPUStart(),
|
||||
texture_descriptor_index));
|
||||
}
|
||||
@@ -605,10 +569,10 @@ void D3D12ImmediateDrawer::Draw(const ImmediateDraw& draw) {
|
||||
texture_resource ? texture->sampler_index() : SamplerIndex::kNearestClamp;
|
||||
if (current_sampler_index_ != sampler_index) {
|
||||
current_sampler_index_ = sampler_index;
|
||||
current_command_list_->SetGraphicsRootDescriptorTable(
|
||||
command_list->SetGraphicsRootDescriptorTable(
|
||||
UINT(RootParameter::kSampler),
|
||||
provider.OffsetSamplerDescriptor(sampler_heap_gpu_start_,
|
||||
uint32_t(sampler_index)));
|
||||
provider_.OffsetSamplerDescriptor(sampler_heap_gpu_start_,
|
||||
uint32_t(sampler_index)));
|
||||
}
|
||||
|
||||
// Set the primitive type and the pipeline for it.
|
||||
@@ -617,11 +581,11 @@ void D3D12ImmediateDrawer::Draw(const ImmediateDraw& draw) {
|
||||
switch (draw.primitive_type) {
|
||||
case ImmediatePrimitiveType::kLines:
|
||||
primitive_topology = D3D_PRIMITIVE_TOPOLOGY_LINELIST;
|
||||
pipeline = pipeline_line_;
|
||||
pipeline = pipeline_line_.Get();
|
||||
break;
|
||||
case ImmediatePrimitiveType::kTriangles:
|
||||
primitive_topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
|
||||
pipeline = pipeline_triangle_;
|
||||
pipeline = pipeline_triangle_.Get();
|
||||
break;
|
||||
default:
|
||||
assert_unhandled_case(draw.primitive_type);
|
||||
@@ -629,16 +593,16 @@ void D3D12ImmediateDrawer::Draw(const ImmediateDraw& draw) {
|
||||
}
|
||||
if (current_primitive_topology_ != primitive_topology) {
|
||||
current_primitive_topology_ = primitive_topology;
|
||||
current_command_list_->IASetPrimitiveTopology(primitive_topology);
|
||||
current_command_list_->SetPipelineState(pipeline);
|
||||
command_list->IASetPrimitiveTopology(primitive_topology);
|
||||
command_list->SetPipelineState(pipeline);
|
||||
}
|
||||
|
||||
// Draw.
|
||||
if (batch_has_index_buffer_) {
|
||||
current_command_list_->DrawIndexedInstanced(
|
||||
draw.count, 1, draw.index_offset, draw.base_vertex, 0);
|
||||
command_list->DrawIndexedInstanced(draw.count, 1, draw.index_offset,
|
||||
draw.base_vertex, 0);
|
||||
} else {
|
||||
current_command_list_->DrawInstanced(draw.count, 1, draw.base_vertex, 0);
|
||||
command_list->DrawInstanced(draw.count, 1, draw.base_vertex, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -646,11 +610,29 @@ void D3D12ImmediateDrawer::EndDrawBatch() { batch_open_ = false; }
|
||||
|
||||
void D3D12ImmediateDrawer::End() {
|
||||
assert_false(batch_open_);
|
||||
if (current_command_list_) {
|
||||
// Don't keep upload buffers forever if nothing was drawn in this frame.
|
||||
UploadTextures();
|
||||
current_command_list_ = nullptr;
|
||||
|
||||
ImmediateDrawer::End();
|
||||
}
|
||||
|
||||
void D3D12ImmediateDrawer::OnLeavePresenter() {
|
||||
// Leaving the presenter's submission timeline - await GPU usage completion of
|
||||
// all draws and texture uploads (which happen before draws) and reset
|
||||
// submission indices.
|
||||
D3D12Presenter& d3d12_presenter = *static_cast<D3D12Presenter*>(presenter());
|
||||
d3d12_presenter.AwaitUISubmissionCompletionFromUIThread(
|
||||
last_paint_submission_index_);
|
||||
|
||||
for (D3D12ImmediateTexture* texture : textures_) {
|
||||
texture->SetLastUsageSubmissionIndex(0);
|
||||
}
|
||||
|
||||
texture_uploads_submitted_.clear();
|
||||
|
||||
vertex_buffer_pool_->ChangeSubmissionTimeline();
|
||||
texture_descriptor_pool_->ChangeSubmissionTimeline();
|
||||
|
||||
last_paint_submission_index_ = 0;
|
||||
last_completed_submission_index_ = 0;
|
||||
}
|
||||
|
||||
void D3D12ImmediateDrawer::OnImmediateTextureDestroyed(
|
||||
@@ -665,35 +647,35 @@ void D3D12ImmediateDrawer::OnImmediateTextureDestroyed(
|
||||
|
||||
// Queue for delayed release.
|
||||
ID3D12Resource* resource = texture.resource();
|
||||
uint64_t last_usage_fence_value = texture.last_usage_fence_value();
|
||||
UINT64 last_usage_submission_index = texture.last_usage_submission_index();
|
||||
if (resource &&
|
||||
last_usage_fence_value > context_.GetSwapCompletedFenceValue()) {
|
||||
resource->AddRef();
|
||||
textures_deleted_.push_back(
|
||||
std::make_pair(resource, last_usage_fence_value));
|
||||
last_usage_submission_index > last_completed_submission_index_) {
|
||||
textures_deleted_.emplace_back(resource, last_usage_submission_index);
|
||||
}
|
||||
}
|
||||
|
||||
void D3D12ImmediateDrawer::UploadTextures() {
|
||||
assert_not_null(current_command_list_);
|
||||
if (texture_uploads_pending_.empty()) {
|
||||
// Called often - don't initialize anything.
|
||||
return;
|
||||
}
|
||||
|
||||
ID3D12Device* device = context_.GetD3D12Provider().GetDevice();
|
||||
uint64_t current_fence_value = context_.GetSwapCurrentFenceValue();
|
||||
ID3D12Device* device = provider_.GetDevice();
|
||||
const D3D12UIDrawContext& d3d12_ui_draw_context =
|
||||
*static_cast<const D3D12UIDrawContext*>(ui_draw_context());
|
||||
ID3D12GraphicsCommandList* command_list =
|
||||
d3d12_ui_draw_context.command_list();
|
||||
|
||||
// Copy all at once, then transition all at once (not interleaving copying and
|
||||
// pipeline barriers).
|
||||
std::vector<D3D12_RESOURCE_BARRIER> barriers;
|
||||
barriers.reserve(texture_uploads_pending_.size());
|
||||
for (const PendingTextureUpload& pending_upload : texture_uploads_pending_) {
|
||||
ID3D12Resource* texture = pending_upload.texture;
|
||||
ID3D12Resource* texture = pending_upload.texture.Get();
|
||||
|
||||
D3D12_RESOURCE_DESC texture_desc = texture->GetDesc();
|
||||
D3D12_TEXTURE_COPY_LOCATION location_source, location_dest;
|
||||
location_source.pResource = pending_upload.buffer;
|
||||
location_source.pResource = pending_upload.buffer.Get();
|
||||
location_source.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
|
||||
device->GetCopyableFootprints(&texture_desc, 0, 1, 0,
|
||||
&location_source.PlacedFootprint, nullptr,
|
||||
@@ -701,8 +683,8 @@ void D3D12ImmediateDrawer::UploadTextures() {
|
||||
location_dest.pResource = texture;
|
||||
location_dest.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
||||
location_dest.SubresourceIndex = 0;
|
||||
current_command_list_->CopyTextureRegion(&location_dest, 0, 0, 0,
|
||||
&location_source, nullptr);
|
||||
command_list->CopyTextureRegion(&location_dest, 0, 0, 0, &location_source,
|
||||
nullptr);
|
||||
|
||||
D3D12_RESOURCE_BARRIER& barrier = barriers.emplace_back();
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
@@ -712,18 +694,12 @@ void D3D12ImmediateDrawer::UploadTextures() {
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
|
||||
|
||||
SubmittedTextureUpload& submitted_upload =
|
||||
texture_uploads_submitted_.emplace_back();
|
||||
// Transfer the reference to the texture - need to keep it until the upload
|
||||
// is completed.
|
||||
submitted_upload.texture = texture;
|
||||
submitted_upload.buffer = pending_upload.buffer;
|
||||
submitted_upload.fence_value = current_fence_value;
|
||||
texture_uploads_submitted_.emplace_back(
|
||||
texture, pending_upload.buffer.Get(), last_paint_submission_index_);
|
||||
}
|
||||
texture_uploads_pending_.clear();
|
||||
assert_false(barriers.empty());
|
||||
current_command_list_->ResourceBarrier(UINT(barriers.size()),
|
||||
barriers.data());
|
||||
command_list->ResourceBarrier(UINT(barriers.size()), barriers.data());
|
||||
}
|
||||
|
||||
} // namespace d3d12
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2018 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include "xenia/ui/d3d12/d3d12_api.h"
|
||||
#include "xenia/ui/d3d12/d3d12_descriptor_heap_pool.h"
|
||||
#include "xenia/ui/d3d12/d3d12_provider.h"
|
||||
#include "xenia/ui/d3d12/d3d12_upload_buffer_pool.h"
|
||||
#include "xenia/ui/immediate_drawer.h"
|
||||
|
||||
@@ -24,15 +25,19 @@ namespace xe {
|
||||
namespace ui {
|
||||
namespace d3d12 {
|
||||
|
||||
class D3D12Context;
|
||||
|
||||
class D3D12ImmediateDrawer : public ImmediateDrawer {
|
||||
class D3D12ImmediateDrawer final : public ImmediateDrawer {
|
||||
public:
|
||||
D3D12ImmediateDrawer(D3D12Context& graphics_context);
|
||||
~D3D12ImmediateDrawer() override;
|
||||
static std::unique_ptr<D3D12ImmediateDrawer> Create(
|
||||
const D3D12Provider& provider) {
|
||||
auto immediate_drawer = std::unique_ptr<D3D12ImmediateDrawer>(
|
||||
new D3D12ImmediateDrawer(provider));
|
||||
if (!immediate_drawer->Initialize()) {
|
||||
return nullptr;
|
||||
}
|
||||
return std::move(immediate_drawer);
|
||||
}
|
||||
|
||||
bool Initialize();
|
||||
void Shutdown();
|
||||
~D3D12ImmediateDrawer();
|
||||
|
||||
std::unique_ptr<ImmediateTexture> CreateTexture(uint32_t width,
|
||||
uint32_t height,
|
||||
@@ -40,12 +45,16 @@ class D3D12ImmediateDrawer : public ImmediateDrawer {
|
||||
bool is_repeated,
|
||||
const uint8_t* data) override;
|
||||
|
||||
void Begin(int render_target_width, int render_target_height) override;
|
||||
void Begin(UIDrawContext& ui_draw_context, float coordinate_space_width,
|
||||
float coordinate_space_height) override;
|
||||
void BeginDrawBatch(const ImmediateDrawBatch& batch) override;
|
||||
void Draw(const ImmediateDraw& draw) override;
|
||||
void EndDrawBatch() override;
|
||||
void End() override;
|
||||
|
||||
protected:
|
||||
void OnLeavePresenter() override;
|
||||
|
||||
private:
|
||||
enum class SamplerIndex {
|
||||
kNearestClamp,
|
||||
@@ -57,7 +66,7 @@ class D3D12ImmediateDrawer : public ImmediateDrawer {
|
||||
kInvalid = kCount
|
||||
};
|
||||
|
||||
class D3D12ImmediateTexture : public ImmediateTexture {
|
||||
class D3D12ImmediateTexture final : public ImmediateTexture {
|
||||
public:
|
||||
static constexpr DXGI_FORMAT kFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
D3D12ImmediateTexture(uint32_t width, uint32_t height,
|
||||
@@ -66,75 +75,93 @@ class D3D12ImmediateDrawer : public ImmediateDrawer {
|
||||
size_t immediate_drawer_index);
|
||||
~D3D12ImmediateTexture() override;
|
||||
|
||||
ID3D12Resource* resource() const { return resource_; }
|
||||
ID3D12Resource* resource() const { return resource_.Get(); }
|
||||
SamplerIndex sampler_index() const { return sampler_index_; }
|
||||
|
||||
size_t immediate_drawer_index() const { return immediate_drawer_index_; }
|
||||
void SetImmediateDrawerIndex(size_t index) {
|
||||
immediate_drawer_index_ = index;
|
||||
}
|
||||
void OnImmediateDrawerShutdown();
|
||||
void OnImmediateDrawerDestroyed();
|
||||
|
||||
uint64_t last_usage_fence_value() const { return last_usage_fence_value_; }
|
||||
void SetLastUsageFenceValue(uint64_t fence_value) {
|
||||
last_usage_fence_value_ = fence_value;
|
||||
UINT64 last_usage_submission_index() const {
|
||||
return last_usage_submission_index_;
|
||||
}
|
||||
void SetLastUsageSubmissionIndex(UINT64 submission_index) {
|
||||
last_usage_submission_index_ = submission_index;
|
||||
}
|
||||
|
||||
private:
|
||||
ID3D12Resource* resource_;
|
||||
Microsoft::WRL::ComPtr<ID3D12Resource> resource_;
|
||||
SamplerIndex sampler_index_;
|
||||
|
||||
D3D12ImmediateDrawer* immediate_drawer_;
|
||||
size_t immediate_drawer_index_;
|
||||
|
||||
uint64_t last_usage_fence_value_ = 0;
|
||||
UINT64 last_usage_submission_index_ = 0;
|
||||
};
|
||||
|
||||
D3D12ImmediateDrawer(const D3D12Provider& provider) : provider_(provider) {}
|
||||
bool Initialize();
|
||||
|
||||
void OnImmediateTextureDestroyed(D3D12ImmediateTexture& texture);
|
||||
|
||||
void UploadTextures();
|
||||
|
||||
D3D12Context& context_;
|
||||
const D3D12Provider& provider_;
|
||||
|
||||
ID3D12RootSignature* root_signature_ = nullptr;
|
||||
Microsoft::WRL::ComPtr<ID3D12RootSignature> root_signature_;
|
||||
enum class RootParameter {
|
||||
kTexture,
|
||||
kSampler,
|
||||
kViewportSizeInv,
|
||||
kCoordinateSpaceSizeInv,
|
||||
|
||||
kCount
|
||||
};
|
||||
|
||||
ID3D12PipelineState* pipeline_triangle_ = nullptr;
|
||||
ID3D12PipelineState* pipeline_line_ = nullptr;
|
||||
Microsoft::WRL::ComPtr<ID3D12PipelineState> pipeline_triangle_;
|
||||
Microsoft::WRL::ComPtr<ID3D12PipelineState> pipeline_line_;
|
||||
|
||||
ID3D12DescriptorHeap* sampler_heap_ = nullptr;
|
||||
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> sampler_heap_;
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE sampler_heap_cpu_start_;
|
||||
D3D12_GPU_DESCRIPTOR_HANDLE sampler_heap_gpu_start_;
|
||||
|
||||
std::unique_ptr<D3D12UploadBufferPool> vertex_buffer_pool_;
|
||||
std::unique_ptr<D3D12DescriptorHeapPool> texture_descriptor_pool_;
|
||||
|
||||
// Only with non-null resources.
|
||||
std::vector<D3D12ImmediateTexture*> textures_;
|
||||
|
||||
struct PendingTextureUpload {
|
||||
ID3D12Resource* texture;
|
||||
ID3D12Resource* buffer;
|
||||
PendingTextureUpload(ID3D12Resource* texture, ID3D12Resource* buffer)
|
||||
: texture(texture), buffer(buffer) {}
|
||||
Microsoft::WRL::ComPtr<ID3D12Resource> texture;
|
||||
Microsoft::WRL::ComPtr<ID3D12Resource> buffer;
|
||||
};
|
||||
std::vector<PendingTextureUpload> texture_uploads_pending_;
|
||||
|
||||
struct SubmittedTextureUpload {
|
||||
ID3D12Resource* texture;
|
||||
ID3D12Resource* buffer;
|
||||
uint64_t fence_value;
|
||||
SubmittedTextureUpload(ID3D12Resource* texture, ID3D12Resource* buffer,
|
||||
UINT64 submission_index)
|
||||
: texture(texture),
|
||||
buffer(buffer),
|
||||
submission_index(submission_index) {}
|
||||
Microsoft::WRL::ComPtr<ID3D12Resource> texture;
|
||||
Microsoft::WRL::ComPtr<ID3D12Resource> buffer;
|
||||
UINT64 submission_index;
|
||||
};
|
||||
std::deque<SubmittedTextureUpload> texture_uploads_submitted_;
|
||||
|
||||
std::vector<std::pair<ID3D12Resource*, uint64_t>> textures_deleted_;
|
||||
std::deque<std::pair<Microsoft::WRL::ComPtr<ID3D12Resource>, UINT64>>
|
||||
textures_deleted_;
|
||||
|
||||
std::unique_ptr<D3D12UploadBufferPool> vertex_buffer_pool_;
|
||||
std::unique_ptr<D3D12DescriptorHeapPool> texture_descriptor_pool_;
|
||||
|
||||
// The submission index within the current Begin (or the last, if outside
|
||||
// one).
|
||||
UINT64 last_paint_submission_index_ = 0;
|
||||
// Completed submission index as of the latest Begin, to coarsely skip delayed
|
||||
// texture deletion.
|
||||
UINT64 last_completed_submission_index_ = 0;
|
||||
|
||||
ID3D12GraphicsCommandList* current_command_list_ = nullptr;
|
||||
int current_render_target_width_, current_render_target_height_;
|
||||
bool batch_open_ = false;
|
||||
bool batch_has_index_buffer_;
|
||||
D3D12_RECT current_scissor_;
|
||||
|
||||
1466
src/xenia/ui/d3d12/d3d12_presenter.cc
Normal file
1466
src/xenia/ui/d3d12/d3d12_presenter.cc
Normal file
File diff suppressed because it is too large
Load Diff
332
src/xenia/ui/d3d12/d3d12_presenter.h
Normal file
332
src/xenia/ui/d3d12/d3d12_presenter.h
Normal file
@@ -0,0 +1,332 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_UI_D3D12_D3D12_PRESENTER_H_
|
||||
#define XENIA_UI_D3D12_D3D12_PRESENTER_H_
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/ui/d3d12/d3d12_provider.h"
|
||||
#include "xenia/ui/d3d12/d3d12_submission_tracker.h"
|
||||
#include "xenia/ui/presenter.h"
|
||||
#include "xenia/ui/surface.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace d3d12 {
|
||||
|
||||
class D3D12UIDrawContext final : public UIDrawContext {
|
||||
public:
|
||||
D3D12UIDrawContext(Presenter& presenter, uint32_t render_target_width,
|
||||
uint32_t render_target_height,
|
||||
ID3D12GraphicsCommandList* command_list,
|
||||
UINT64 submission_index_current,
|
||||
UINT64 submission_index_completed)
|
||||
: UIDrawContext(presenter, render_target_width, render_target_height),
|
||||
command_list_(command_list),
|
||||
submission_index_current_(submission_index_current),
|
||||
submission_index_completed_(submission_index_completed) {}
|
||||
|
||||
ID3D12GraphicsCommandList* command_list() const {
|
||||
return command_list_.Get();
|
||||
}
|
||||
UINT64 submission_index_current() const { return submission_index_current_; }
|
||||
UINT64 submission_index_completed() const {
|
||||
return submission_index_completed_;
|
||||
}
|
||||
|
||||
private:
|
||||
Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList> command_list_;
|
||||
UINT64 submission_index_current_;
|
||||
UINT64 submission_index_completed_;
|
||||
};
|
||||
|
||||
class D3D12Presenter final : public Presenter {
|
||||
public:
|
||||
static constexpr DXGI_FORMAT kGuestOutputFormat =
|
||||
DXGI_FORMAT_R10G10B10A2_UNORM;
|
||||
static constexpr D3D12_RESOURCE_STATES kGuestOutputInternalState =
|
||||
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
|
||||
|
||||
static constexpr DXGI_FORMAT kGuestOutputIntermediateFormat =
|
||||
DXGI_FORMAT_R10G10B10A2_UNORM;
|
||||
|
||||
// The format used internally by Windows composition.
|
||||
static constexpr DXGI_FORMAT kSwapChainFormat = DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
|
||||
// The callback must use the main direct queue of the provider.
|
||||
class D3D12GuestOutputRefreshContext final
|
||||
: public GuestOutputRefreshContext {
|
||||
public:
|
||||
D3D12GuestOutputRefreshContext(bool& is_8bpc_out_ref,
|
||||
ID3D12Resource* resource)
|
||||
: GuestOutputRefreshContext(is_8bpc_out_ref), resource_(resource) {}
|
||||
|
||||
// kGuestOutputFormat, supports UAV. The initial state in the callback is
|
||||
// kGuestOutputInternalState, and the callback must also transition it back
|
||||
// to kGuestOutputInternalState before finishing.
|
||||
ID3D12Resource* resource_uav_capable() const { return resource_.Get(); }
|
||||
|
||||
private:
|
||||
Microsoft::WRL::ComPtr<ID3D12Resource> resource_;
|
||||
};
|
||||
|
||||
static std::unique_ptr<D3D12Presenter> Create(
|
||||
HostGpuLossCallback host_gpu_loss_callback,
|
||||
const D3D12Provider& provider) {
|
||||
auto presenter = std::unique_ptr<D3D12Presenter>(
|
||||
new D3D12Presenter(host_gpu_loss_callback, provider));
|
||||
if (!presenter->InitializeSurfaceIndependent()) {
|
||||
return nullptr;
|
||||
}
|
||||
return presenter;
|
||||
}
|
||||
|
||||
~D3D12Presenter();
|
||||
|
||||
const D3D12Provider& provider() const { return provider_; }
|
||||
|
||||
Surface::TypeFlags GetSupportedSurfaceTypes() const override;
|
||||
|
||||
bool CaptureGuestOutput(RawImage& image_out) override;
|
||||
|
||||
void AwaitUISubmissionCompletionFromUIThread(UINT64 submission_index) {
|
||||
ui_submission_tracker_.AwaitSubmissionCompletion(submission_index);
|
||||
}
|
||||
|
||||
protected:
|
||||
SurfacePaintConnectResult ConnectOrReconnectPaintingToSurfaceFromUIThread(
|
||||
Surface& new_surface, uint32_t new_surface_width,
|
||||
uint32_t new_surface_height, bool was_paintable,
|
||||
bool& is_vsync_implicit_out) override;
|
||||
void DisconnectPaintingFromSurfaceFromUIThreadImpl() override;
|
||||
|
||||
bool RefreshGuestOutputImpl(
|
||||
uint32_t mailbox_index, uint32_t frontbuffer_width,
|
||||
uint32_t frontbuffer_height,
|
||||
std::function<bool(GuestOutputRefreshContext& context)> refresher,
|
||||
bool& is_8bpc_out) override;
|
||||
|
||||
PaintResult PaintAndPresentImpl(bool execute_ui_drawers) override;
|
||||
|
||||
private:
|
||||
struct GuestOutputPaintRectangleConstants {
|
||||
union {
|
||||
struct {
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
float offset[2];
|
||||
};
|
||||
union {
|
||||
struct {
|
||||
float width;
|
||||
float height;
|
||||
};
|
||||
float size[2];
|
||||
};
|
||||
};
|
||||
|
||||
enum class GuestOutputPaintRootParameter : UINT {
|
||||
kSource,
|
||||
kRectangle,
|
||||
kEffectConstants,
|
||||
|
||||
kCount,
|
||||
};
|
||||
|
||||
enum GuestOutputPaintRootSignatureIndex : size_t {
|
||||
kGuestOutputPaintRootSignatureIndexBilinear,
|
||||
kGuestOutputPaintRootSignatureIndexCasSharpen,
|
||||
kGuestOutputPaintRootSignatureIndexCasResample,
|
||||
kGuestOutputPaintRootSignatureIndexFsrEasu,
|
||||
kGuestOutputPaintRootSignatureIndexFsrRcas,
|
||||
|
||||
kGuestOutputPaintRootSignatureCount,
|
||||
};
|
||||
|
||||
static constexpr GuestOutputPaintRootSignatureIndex
|
||||
GetGuestOutputPaintRootSignatureIndex(GuestOutputPaintEffect effect) {
|
||||
switch (effect) {
|
||||
case GuestOutputPaintEffect::kBilinear:
|
||||
case GuestOutputPaintEffect::kBilinearDither:
|
||||
return kGuestOutputPaintRootSignatureIndexBilinear;
|
||||
case GuestOutputPaintEffect::kCasSharpen:
|
||||
case GuestOutputPaintEffect::kCasSharpenDither:
|
||||
return kGuestOutputPaintRootSignatureIndexCasSharpen;
|
||||
case GuestOutputPaintEffect::kCasResample:
|
||||
case GuestOutputPaintEffect::kCasResampleDither:
|
||||
return kGuestOutputPaintRootSignatureIndexCasResample;
|
||||
case GuestOutputPaintEffect::kFsrEasu:
|
||||
return kGuestOutputPaintRootSignatureIndexFsrEasu;
|
||||
case GuestOutputPaintEffect::kFsrRcas:
|
||||
case GuestOutputPaintEffect::kFsrRcasDither:
|
||||
return kGuestOutputPaintRootSignatureIndexFsrRcas;
|
||||
default:
|
||||
assert_unhandled_case(effect);
|
||||
return kGuestOutputPaintRootSignatureCount;
|
||||
}
|
||||
}
|
||||
|
||||
struct PaintContext {
|
||||
explicit PaintContext() = default;
|
||||
PaintContext(const PaintContext& paint_context) = delete;
|
||||
PaintContext& operator=(const PaintContext& paint_context) = delete;
|
||||
|
||||
static constexpr uint32_t kSwapChainBufferCount = 3;
|
||||
|
||||
enum RTVIndex : UINT {
|
||||
// Swap chain buffers - updated when creating the swap chain
|
||||
// (connection-specific).
|
||||
kRTVIndexSwapChainBuffer0,
|
||||
|
||||
// Intermediate textures - the last usage is
|
||||
// guest_output_intermediate_texture_paint_last_usage_.
|
||||
kRTVIndexGuestOutputIntermediate0 =
|
||||
kRTVIndexSwapChainBuffer0 + kSwapChainBufferCount,
|
||||
|
||||
kRTVCount =
|
||||
kRTVIndexGuestOutputIntermediate0 + kGuestOutputMailboxSize - 1,
|
||||
};
|
||||
|
||||
enum ViewIndex : UINT {
|
||||
// Guest output textures - indices are the same as in
|
||||
// guest_output_resource_paint_refs, and the last usage is tied to them.
|
||||
kViewIndexGuestOutput0Srv,
|
||||
|
||||
// Intermediate textures - the last usage is
|
||||
// guest_output_intermediate_texture_paint_last_usage_.
|
||||
kViewIndexGuestOutputIntermediate0Srv =
|
||||
kViewIndexGuestOutput0Srv + kGuestOutputMailboxSize,
|
||||
|
||||
kViewCount = kViewIndexGuestOutputIntermediate0Srv +
|
||||
kMaxGuestOutputPaintEffects - 1,
|
||||
};
|
||||
|
||||
void AwaitSwapChainUsageCompletion() {
|
||||
// Presentation engine usage.
|
||||
present_submission_tracker.AwaitAllSubmissionsCompletion();
|
||||
// Paint (render target) usage. While the presentation fence is signaled
|
||||
// on the same queue, and presentation happens after painting, awaiting
|
||||
// anyway for safety just to make less assumptions in the architecture.
|
||||
paint_submission_tracker.AwaitAllSubmissionsCompletion();
|
||||
}
|
||||
|
||||
void DestroySwapChain();
|
||||
|
||||
// Connection-independent.
|
||||
|
||||
// Signaled before presenting.
|
||||
D3D12SubmissionTracker paint_submission_tracker;
|
||||
// Signaled after presenting.
|
||||
D3D12SubmissionTracker present_submission_tracker;
|
||||
|
||||
std::array<Microsoft::WRL::ComPtr<ID3D12CommandAllocator>,
|
||||
kSwapChainBufferCount>
|
||||
command_allocators;
|
||||
Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList> command_list;
|
||||
|
||||
// Descriptor heaps for views of the current resources related to the guest
|
||||
// output and to painting, updated either during painting or during
|
||||
// connection lifetime management if outdated after awaiting usage
|
||||
// completion.
|
||||
// RTV heap.
|
||||
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> rtv_heap;
|
||||
// Shader-visible CBV/SRV/UAV heap.
|
||||
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> view_heap;
|
||||
|
||||
// Refreshed and cleaned up during guest output painting. The first is the
|
||||
// paint submission index in which the guest output texture (and its
|
||||
// descriptors) was last used, the second is the reference to the texture,
|
||||
// which may be null. The indices are not mailbox indices here, rather, if
|
||||
// the reference is not in this array yet, the most outdated reference, if
|
||||
// needed, is replaced with the new one, awaiting the completion of the last
|
||||
// paint usage.
|
||||
std::array<std::pair<UINT64, Microsoft::WRL::ComPtr<ID3D12Resource>>,
|
||||
kGuestOutputMailboxSize>
|
||||
guest_output_resource_paint_refs;
|
||||
|
||||
// Current intermediate textures for guest output painting, refreshed when
|
||||
// painting guest output. While not in use, they are in
|
||||
// D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE.
|
||||
std::array<Microsoft::WRL::ComPtr<ID3D12Resource>,
|
||||
kMaxGuestOutputPaintEffects - 1>
|
||||
guest_output_intermediate_textures;
|
||||
UINT64 guest_output_intermediate_texture_last_usage = 0;
|
||||
|
||||
// Connection-specific.
|
||||
|
||||
uint32_t swap_chain_width = 0;
|
||||
uint32_t swap_chain_height = 0;
|
||||
bool swap_chain_allows_tearing = false;
|
||||
Microsoft::WRL::ComPtr<IDXGISwapChain3> swap_chain;
|
||||
std::array<Microsoft::WRL::ComPtr<ID3D12Resource>, kSwapChainBufferCount>
|
||||
swap_chain_buffers;
|
||||
};
|
||||
|
||||
explicit D3D12Presenter(HostGpuLossCallback host_gpu_loss_callback,
|
||||
const D3D12Provider& provider)
|
||||
: Presenter(host_gpu_loss_callback), provider_(provider) {}
|
||||
|
||||
bool dxgi_supports_tearing() const { return dxgi_supports_tearing_; }
|
||||
|
||||
bool InitializeSurfaceIndependent();
|
||||
|
||||
const D3D12Provider& provider_;
|
||||
|
||||
// Whether DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING is supported by DXGI (depends in
|
||||
// particular on the Windows 10 version and hardware support), primarily for
|
||||
// variable refresh rate support.
|
||||
bool dxgi_supports_tearing_ = false;
|
||||
|
||||
// Static objects for guest output presentation, used only when painting the
|
||||
// main target (can be destroyed only after awaiting main target usage
|
||||
// completion).
|
||||
std::array<Microsoft::WRL::ComPtr<ID3D12RootSignature>,
|
||||
kGuestOutputPaintRootSignatureCount>
|
||||
guest_output_paint_root_signatures_;
|
||||
std::array<Microsoft::WRL::ComPtr<ID3D12PipelineState>,
|
||||
size_t(GuestOutputPaintEffect::kCount)>
|
||||
guest_output_paint_intermediate_pipelines_;
|
||||
std::array<Microsoft::WRL::ComPtr<ID3D12PipelineState>,
|
||||
size_t(GuestOutputPaintEffect::kCount)>
|
||||
guest_output_paint_final_pipelines_;
|
||||
|
||||
// The first is the refresher submission tracker fence value at which the
|
||||
// guest output texture was last refreshed, the second is the reference to the
|
||||
// texture, which may be null. The indices are the mailbox indices.
|
||||
std::array<std::pair<UINT64, Microsoft::WRL::ComPtr<ID3D12Resource>>,
|
||||
kGuestOutputMailboxSize>
|
||||
guest_output_resources_;
|
||||
// The guest output resources are protected by two submission trackers - the
|
||||
// refresher ones (for writing to them via the guest_output_resources_
|
||||
// references) and the paint one (for presenting it via the
|
||||
// paint_context_.guest_output_resource_paint_refs references taken from
|
||||
// guest_output_resources_).
|
||||
D3D12SubmissionTracker guest_output_resource_refresher_submission_tracker_;
|
||||
|
||||
// UI submission tracker with the submission index that can be given to UI
|
||||
// drawers (accessible from the UI thread only, at any time).
|
||||
D3D12SubmissionTracker ui_submission_tracker_;
|
||||
|
||||
// Accessible only by painting and by surface connection lifetime management
|
||||
// (ConnectOrReconnectPaintingToSurfaceFromUIThread,
|
||||
// DisconnectPaintingFromSurfaceFromUIThreadImpl) by the thread doing it, as
|
||||
// well as by presenter initialization and shutdown.
|
||||
PaintContext paint_context_;
|
||||
};
|
||||
|
||||
} // namespace d3d12
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_UI_D3D12_D3D12_PRESENTER_H_
|
||||
@@ -15,7 +15,8 @@
|
||||
#include "xenia/base/cvar.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/ui/d3d12/d3d12_context.h"
|
||||
#include "xenia/ui/d3d12/d3d12_immediate_drawer.h"
|
||||
#include "xenia/ui/d3d12/d3d12_presenter.h"
|
||||
|
||||
DEFINE_bool(d3d12_debug, false, "Enable Direct3D 12 and DXGI debug layer.",
|
||||
"D3D12");
|
||||
@@ -77,6 +78,14 @@ D3D12Provider::~D3D12Provider() {
|
||||
dxgi_factory_->Release();
|
||||
}
|
||||
|
||||
if (cvars::d3d12_debug && pfn_dxgi_get_debug_interface1_) {
|
||||
Microsoft::WRL::ComPtr<IDXGIDebug> dxgi_debug;
|
||||
if (SUCCEEDED(
|
||||
pfn_dxgi_get_debug_interface1_(0, IID_PPV_ARGS(&dxgi_debug)))) {
|
||||
dxgi_debug->ReportLiveObjects(DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_ALL);
|
||||
}
|
||||
}
|
||||
|
||||
if (library_dxcompiler_ != nullptr) {
|
||||
FreeLibrary(library_dxcompiler_);
|
||||
}
|
||||
@@ -86,9 +95,6 @@ D3D12Provider::~D3D12Provider() {
|
||||
if (library_d3dcompiler_ != nullptr) {
|
||||
FreeLibrary(library_d3dcompiler_);
|
||||
}
|
||||
if (library_dcomp_ != nullptr) {
|
||||
FreeLibrary(library_dcomp_);
|
||||
}
|
||||
if (library_d3d12_ != nullptr) {
|
||||
FreeLibrary(library_d3d12_);
|
||||
}
|
||||
@@ -120,9 +126,8 @@ bool D3D12Provider::Initialize() {
|
||||
// Load the core libraries.
|
||||
library_dxgi_ = LoadLibraryW(L"dxgi.dll");
|
||||
library_d3d12_ = LoadLibraryW(L"D3D12.dll");
|
||||
library_dcomp_ = LoadLibraryW(L"dcomp.dll");
|
||||
if (!library_dxgi_ || !library_d3d12_ || !library_dcomp_) {
|
||||
XELOGE("Failed to load dxgi.dll, D3D12.dll or dcomp.dll");
|
||||
if (!library_dxgi_ || !library_d3d12_) {
|
||||
XELOGE("Failed to load dxgi.dll or D3D12.dll");
|
||||
return false;
|
||||
}
|
||||
bool libraries_loaded = true;
|
||||
@@ -143,12 +148,8 @@ bool D3D12Provider::Initialize() {
|
||||
(pfn_d3d12_serialize_root_signature_ = PFN_D3D12_SERIALIZE_ROOT_SIGNATURE(
|
||||
GetProcAddress(library_d3d12_, "D3D12SerializeRootSignature"))) !=
|
||||
nullptr;
|
||||
libraries_loaded &=
|
||||
(pfn_dcomposition_create_device_ = PFNDCompositionCreateDevice(
|
||||
GetProcAddress(library_dcomp_, "DCompositionCreateDevice"))) !=
|
||||
nullptr;
|
||||
if (!libraries_loaded) {
|
||||
XELOGE("Failed to get DXGI, Direct3D 12 or DirectComposition functions");
|
||||
XELOGE("Failed to get DXGI or Direct3D 12 functions");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -470,23 +471,13 @@ bool D3D12Provider::Initialize() {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<GraphicsContext> D3D12Provider::CreateContext(
|
||||
Window* target_window) {
|
||||
auto new_context =
|
||||
std::unique_ptr<D3D12Context>(new D3D12Context(this, target_window));
|
||||
if (!new_context->Initialize()) {
|
||||
return nullptr;
|
||||
}
|
||||
return std::unique_ptr<GraphicsContext>(new_context.release());
|
||||
std::unique_ptr<Presenter> D3D12Provider::CreatePresenter(
|
||||
Presenter::HostGpuLossCallback host_gpu_loss_callback) {
|
||||
return D3D12Presenter::Create(host_gpu_loss_callback, *this);
|
||||
}
|
||||
|
||||
std::unique_ptr<GraphicsContext> D3D12Provider::CreateOffscreenContext() {
|
||||
auto new_context =
|
||||
std::unique_ptr<D3D12Context>(new D3D12Context(this, nullptr));
|
||||
if (!new_context->Initialize()) {
|
||||
return nullptr;
|
||||
}
|
||||
return std::unique_ptr<GraphicsContext>(new_context.release());
|
||||
std::unique_ptr<ImmediateDrawer> D3D12Provider::CreateImmediateDrawer() {
|
||||
return D3D12ImmediateDrawer::Create(*this);
|
||||
}
|
||||
|
||||
} // namespace d3d12
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2018 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -23,15 +23,17 @@ namespace d3d12 {
|
||||
|
||||
class D3D12Provider : public GraphicsProvider {
|
||||
public:
|
||||
~D3D12Provider() override;
|
||||
~D3D12Provider();
|
||||
|
||||
static bool IsD3D12APIAvailable();
|
||||
|
||||
static std::unique_ptr<D3D12Provider> Create();
|
||||
|
||||
std::unique_ptr<GraphicsContext> CreateContext(
|
||||
Window* target_window) override;
|
||||
std::unique_ptr<GraphicsContext> CreateOffscreenContext() override;
|
||||
std::unique_ptr<Presenter> CreatePresenter(
|
||||
Presenter::HostGpuLossCallback host_gpu_loss_callback =
|
||||
Presenter::FatalErrorHostGpuLossCallback) override;
|
||||
|
||||
std::unique_ptr<ImmediateDrawer> CreateImmediateDrawer() override;
|
||||
|
||||
IDXGIFactory2* GetDXGIFactory() const { return dxgi_factory_; }
|
||||
// nullptr if PIX not attached.
|
||||
@@ -118,11 +120,6 @@ class D3D12Provider : public GraphicsProvider {
|
||||
return pfn_d3d12_serialize_root_signature_(desc, version, blob_out,
|
||||
error_blob_out);
|
||||
}
|
||||
HRESULT CreateDCompositionDevice(IDXGIDevice* dxgi_device, const IID& iid,
|
||||
void** dcomposition_device_out) const {
|
||||
return pfn_dcomposition_create_device_(dxgi_device, iid,
|
||||
dcomposition_device_out);
|
||||
}
|
||||
HRESULT Disassemble(const void* src_data, size_t src_data_size, UINT flags,
|
||||
const char* comments, ID3DBlob** disassembly_out) const {
|
||||
if (!pfn_d3d_disassemble_) {
|
||||
@@ -156,22 +153,17 @@ class D3D12Provider : public GraphicsProvider {
|
||||
_COM_Outptr_ void** ppFactory);
|
||||
typedef HRESULT(WINAPI* PFNDXGIGetDebugInterface1)(
|
||||
UINT Flags, REFIID riid, _COM_Outptr_ void** pDebug);
|
||||
typedef HRESULT(WINAPI* PFNDCompositionCreateDevice)(
|
||||
_In_opt_ IDXGIDevice* dxgiDevice, _In_ REFIID iid,
|
||||
_Outptr_ void** dcompositionDevice);
|
||||
|
||||
HMODULE library_dxgi_ = nullptr;
|
||||
PFNCreateDXGIFactory2 pfn_create_dxgi_factory2_;
|
||||
PFNDXGIGetDebugInterface1 pfn_dxgi_get_debug_interface1_;
|
||||
// Needed during shutdown as well to report live objects, so may be nullptr.
|
||||
PFNDXGIGetDebugInterface1 pfn_dxgi_get_debug_interface1_ = nullptr;
|
||||
|
||||
HMODULE library_d3d12_ = nullptr;
|
||||
PFN_D3D12_GET_DEBUG_INTERFACE pfn_d3d12_get_debug_interface_;
|
||||
PFN_D3D12_CREATE_DEVICE pfn_d3d12_create_device_;
|
||||
PFN_D3D12_SERIALIZE_ROOT_SIGNATURE pfn_d3d12_serialize_root_signature_;
|
||||
|
||||
HMODULE library_dcomp_ = nullptr;
|
||||
PFNDCompositionCreateDevice pfn_dcomposition_create_device_;
|
||||
|
||||
HMODULE library_d3dcompiler_ = nullptr;
|
||||
pD3DDisassemble pfn_d3d_disassemble_ = nullptr;
|
||||
|
||||
@@ -182,9 +174,9 @@ class D3D12Provider : public GraphicsProvider {
|
||||
DxcCreateInstanceProc pfn_dxcompiler_dxc_create_instance_ = nullptr;
|
||||
|
||||
IDXGIFactory2* dxgi_factory_ = nullptr;
|
||||
IDXGraphicsAnalysis* graphics_analysis_ = nullptr;
|
||||
ID3D12Device* device_ = nullptr;
|
||||
ID3D12CommandQueue* direct_queue_ = nullptr;
|
||||
IDXGraphicsAnalysis* graphics_analysis_ = nullptr;
|
||||
|
||||
uint32_t descriptor_sizes_[D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES];
|
||||
|
||||
|
||||
126
src/xenia/ui/d3d12/d3d12_submission_tracker.cc
Normal file
126
src/xenia/ui/d3d12/d3d12_submission_tracker.cc
Normal file
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2021 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/ui/d3d12/d3d12_submission_tracker.h"
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/logging.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace d3d12 {
|
||||
|
||||
bool D3D12SubmissionTracker::Initialize(ID3D12Device* device,
|
||||
ID3D12CommandQueue* queue) {
|
||||
Shutdown();
|
||||
fence_completion_event_ = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
if (!fence_completion_event_) {
|
||||
XELOGE(
|
||||
"D3D12SubmissionTracker: Failed to create the fence completion event");
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
// Continue where the tracker was left at the last shutdown.
|
||||
if (FAILED(device->CreateFence(submission_current_ - 1, D3D12_FENCE_FLAG_NONE,
|
||||
IID_PPV_ARGS(&fence_)))) {
|
||||
XELOGE("D3D12SubmissionTracker: Failed to create the fence");
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
queue_ = queue;
|
||||
submission_signal_queued_ = submission_current_ - 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
void D3D12SubmissionTracker::Shutdown() {
|
||||
AwaitAllSubmissionsCompletion();
|
||||
queue_.Reset();
|
||||
fence_.Reset();
|
||||
if (fence_completion_event_) {
|
||||
CloseHandle(fence_completion_event_);
|
||||
fence_completion_event_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool D3D12SubmissionTracker::AwaitSubmissionCompletion(
|
||||
UINT64 submission_index) {
|
||||
if (!fence_ || !fence_completion_event_) {
|
||||
// Not fully initialized yet or already shut down.
|
||||
return false;
|
||||
}
|
||||
// The tracker itself can't give a submission index for a submission that
|
||||
// hasn't even started being recorded yet, the client has provided a
|
||||
// completely invalid value or has done overly optimistic math if such an
|
||||
// index has been obtained somehow.
|
||||
assert_true(submission_index <= submission_current_);
|
||||
// Waiting for the current submission is fine if there was a refusal to
|
||||
// submit, and the submission index wasn't incremented, but still need to
|
||||
// release objects referenced in the dropped submission (while shutting down,
|
||||
// for instance - in this case, waiting for the last successful submission,
|
||||
// which could have also referenced the objects from the new submission - we
|
||||
// can't know since the client has already overwritten its last usage index,
|
||||
// would correctly ensure that GPU usage of the objects is not pending).
|
||||
// Waiting for successful submissions, but failed signals, will result in a
|
||||
// true race condition, however, but waiting for the closest successful signal
|
||||
// is the best approximation - also retrying to signal in this case.
|
||||
UINT64 fence_value = submission_index;
|
||||
if (submission_index > submission_signal_queued_) {
|
||||
TrySignalEnqueueing();
|
||||
fence_value = submission_signal_queued_;
|
||||
}
|
||||
if (fence_->GetCompletedValue() < fence_value) {
|
||||
if (FAILED(fence_->SetEventOnCompletion(fence_value,
|
||||
fence_completion_event_))) {
|
||||
return false;
|
||||
}
|
||||
if (WaitForSingleObject(fence_completion_event_, INFINITE) !=
|
||||
WAIT_OBJECT_0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return fence_value == submission_index;
|
||||
}
|
||||
|
||||
void D3D12SubmissionTracker::SetQueue(ID3D12CommandQueue* new_queue) {
|
||||
if (queue_.Get() == new_queue) {
|
||||
return;
|
||||
}
|
||||
if (queue_) {
|
||||
// Make sure the first signal on the new queue won't happen before the last
|
||||
// signal, if pending, on the old one, as that would result first in too
|
||||
// early submission completion indication, and then in rewinding.
|
||||
AwaitAllSubmissionsCompletion();
|
||||
}
|
||||
queue_ = new_queue;
|
||||
}
|
||||
|
||||
bool D3D12SubmissionTracker::NextSubmission() {
|
||||
++submission_current_;
|
||||
assert_not_null(queue_);
|
||||
assert_not_null(fence_);
|
||||
return TrySignalEnqueueing();
|
||||
}
|
||||
|
||||
bool D3D12SubmissionTracker::TrySignalEnqueueing() {
|
||||
if (submission_signal_queued_ + 1 >= submission_current_) {
|
||||
return true;
|
||||
}
|
||||
if (!queue_ || !fence_) {
|
||||
return false;
|
||||
}
|
||||
if (FAILED(queue_->Signal(fence_.Get(), submission_current_ - 1))) {
|
||||
return false;
|
||||
}
|
||||
submission_signal_queued_ = submission_current_ - 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace d3d12
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
93
src/xenia/ui/d3d12/d3d12_submission_tracker.h
Normal file
93
src/xenia/ui/d3d12/d3d12_submission_tracker.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2021 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_UI_D3D12_D3D12_SUBMISSION_TRACKER_H_
|
||||
#define XENIA_UI_D3D12_D3D12_SUBMISSION_TRACKER_H_
|
||||
|
||||
#include "xenia/ui/d3d12/d3d12_api.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace d3d12 {
|
||||
|
||||
// GPU > CPU fence wrapper, safely handling cases when the fence has not been
|
||||
// initialized yet or has already been shut down, dropped submissions, and also
|
||||
// transfers between queues so signals stay ordered.
|
||||
//
|
||||
// The current submission index can be associated with the usage of objects to
|
||||
// release them when the GPU isn't potentially referencing them anymore, and
|
||||
// should be incremented only
|
||||
//
|
||||
// 0 can be used as a "never referenced" submission index.
|
||||
//
|
||||
// The submission index timeline survives Shutdown / Initialize, so submission
|
||||
// indices can be given to clients that are not aware of the lifetime of the
|
||||
// tracker.
|
||||
class D3D12SubmissionTracker {
|
||||
public:
|
||||
D3D12SubmissionTracker() = default;
|
||||
D3D12SubmissionTracker(const D3D12SubmissionTracker& submission_tracker) =
|
||||
delete;
|
||||
D3D12SubmissionTracker& operator=(
|
||||
const D3D12SubmissionTracker& submission_tracker) = delete;
|
||||
~D3D12SubmissionTracker() { Shutdown(); }
|
||||
|
||||
// The queue may be null if it's going to be set dynamically. Will also take a
|
||||
// reference to the queue.
|
||||
bool Initialize(ID3D12Device* device, ID3D12CommandQueue* queue);
|
||||
void Shutdown();
|
||||
|
||||
// Will perform an ownership transfer if the queue is different than the
|
||||
// current one, and take a reference to the queue.
|
||||
void SetQueue(ID3D12CommandQueue* new_queue);
|
||||
|
||||
UINT64 GetCurrentSubmission() const { return submission_current_; }
|
||||
// May be lower than a value awaited by AwaitSubmissionCompletion if it
|
||||
// returned false.
|
||||
UINT64 GetCompletedSubmission() const {
|
||||
// If shut down already or haven't fully initialized yet, don't care, for
|
||||
// simplicity of external code, as any downloads are unlikely in this case,
|
||||
// but destruction can be simplified.
|
||||
return fence_ ? fence_->GetCompletedValue() : (GetCurrentSubmission() - 1);
|
||||
}
|
||||
|
||||
// Returns whether the expected GPU signal has actually been reached (rather
|
||||
// than some fallback condition) for cases when stronger completeness
|
||||
// guarantees as needed (when downloading, as opposed to just destroying).
|
||||
// If false is returned, it's also not guaranteed that GetCompletedSubmission
|
||||
// will return a value >= submission_index.
|
||||
bool AwaitSubmissionCompletion(UINT64 submission_index);
|
||||
bool AwaitAllSubmissionsCompletion() {
|
||||
return AwaitSubmissionCompletion(GetCurrentSubmission() - 1);
|
||||
}
|
||||
|
||||
// Call after a successful ExecuteCommandList. Unconditionally increments the
|
||||
// current submission index, and tries to enqueue the fence signal. Returns
|
||||
// true if enqueued successfully, but even if not, waiting for submissions
|
||||
// without a successfully enqueued signal is handled in the tracker in a way
|
||||
// that it won't be infinite, so there's no need for clients to revert updates
|
||||
// to submission indices associated with GPU usage of objects.
|
||||
bool NextSubmission();
|
||||
// If NextSubmission has failed, but it's important that the signal is
|
||||
// enqueued, can be used to retry enqueueing the signal.
|
||||
bool TrySignalEnqueueing();
|
||||
|
||||
private:
|
||||
UINT64 submission_current_ = 1;
|
||||
UINT64 submission_signal_queued_ = 0;
|
||||
HANDLE fence_completion_event_ = nullptr;
|
||||
Microsoft::WRL::ComPtr<ID3D12Fence> fence_;
|
||||
Microsoft::WRL::ComPtr<ID3D12CommandQueue> queue_;
|
||||
};
|
||||
|
||||
} // namespace d3d12
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_UI_D3D12_D3D12_SUBMISSION_TRACKER_H_
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -38,7 +38,7 @@ uint8_t* D3D12UploadBufferPool::Request(
|
||||
return nullptr;
|
||||
}
|
||||
if (buffer_out) {
|
||||
*buffer_out = page->buffer_;
|
||||
*buffer_out = page->buffer_.Get();
|
||||
}
|
||||
if (offset_out) {
|
||||
*offset_out = offset;
|
||||
@@ -61,7 +61,7 @@ uint8_t* D3D12UploadBufferPool::RequestPartial(
|
||||
return nullptr;
|
||||
}
|
||||
if (buffer_out) {
|
||||
*buffer_out = page->buffer_;
|
||||
*buffer_out = page->buffer_.Get();
|
||||
}
|
||||
if (offset_out) {
|
||||
*offset_out = offset;
|
||||
@@ -80,7 +80,7 @@ D3D12UploadBufferPool::CreatePageImplementation() {
|
||||
D3D12_RESOURCE_DESC buffer_desc;
|
||||
util::FillBufferResourceDesc(buffer_desc, page_size_,
|
||||
D3D12_RESOURCE_FLAG_NONE);
|
||||
ID3D12Resource* buffer;
|
||||
Microsoft::WRL::ComPtr<ID3D12Resource> buffer;
|
||||
if (FAILED(provider_.GetDevice()->CreateCommittedResource(
|
||||
&util::kHeapPropertiesUpload, provider_.GetHeapFlagCreateNotZeroed(),
|
||||
&buffer_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr,
|
||||
@@ -97,24 +97,16 @@ D3D12UploadBufferPool::CreatePageImplementation() {
|
||||
buffer->Release();
|
||||
return nullptr;
|
||||
}
|
||||
D3D12Page* page = new D3D12Page(buffer, mapping);
|
||||
// Owned by the page now.
|
||||
buffer->Release();
|
||||
return page;
|
||||
// Unmapping will be done implicitly when the resource is destroyed.
|
||||
return new D3D12Page(buffer.Get(), mapping);
|
||||
}
|
||||
|
||||
D3D12UploadBufferPool::D3D12Page::D3D12Page(ID3D12Resource* buffer,
|
||||
void* mapping)
|
||||
: buffer_(buffer), mapping_(mapping) {
|
||||
buffer_->AddRef();
|
||||
gpu_address_ = buffer_->GetGPUVirtualAddress();
|
||||
}
|
||||
|
||||
D3D12UploadBufferPool::D3D12Page::~D3D12Page() {
|
||||
// Unmapping is done implicitly when the buffer is destroyed.
|
||||
buffer_->Release();
|
||||
}
|
||||
|
||||
} // namespace d3d12
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -38,8 +38,7 @@ class D3D12UploadBufferPool : public GraphicsUploadBufferPool {
|
||||
// Creates a reference to the buffer. It must not be unmapped until this
|
||||
// D3D12Page is deleted.
|
||||
D3D12Page(ID3D12Resource* buffer, void* mapping);
|
||||
~D3D12Page() override;
|
||||
ID3D12Resource* buffer_;
|
||||
Microsoft::WRL::ComPtr<ID3D12Resource> buffer_;
|
||||
void* mapping_;
|
||||
D3D12_GPU_VIRTUAL_ADDRESS gpu_address_;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user