mirror of
https://github.com/thejerf/baijiSteamVR.git
synced 2026-10-23 06:01:54 +02:00
253 lines
9.1 KiB
C++
253 lines
9.1 KiB
C++
// Copyright 2019 Dolphin Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#pragma once
|
|
|
|
#include <array>
|
|
#include <memory>
|
|
#include <vector>
|
|
|
|
#include "Common/CommonTypes.h"
|
|
#include "Common/HRWrap.h"
|
|
|
|
#include "VideoBackends/D3D12/Common.h"
|
|
#include "VideoBackends/D3D12/D3D12StreamBuffer.h"
|
|
#include "VideoBackends/D3D12/DescriptorAllocator.h"
|
|
#include "VideoBackends/D3D12/DescriptorHeapManager.h"
|
|
|
|
struct IDXGIFactory;
|
|
|
|
namespace DX12
|
|
{
|
|
// Vertex/Pixel shader root parameters
|
|
enum ROOT_PARAMETER
|
|
{
|
|
ROOT_PARAMETER_PS_CBV,
|
|
ROOT_PARAMETER_PS_SRV,
|
|
ROOT_PARAMETER_PS_SAMPLERS,
|
|
ROOT_PARAMETER_VS_CBV,
|
|
ROOT_PARAMETER_VS_CBV2,
|
|
ROOT_PARAMETER_VS_CUS_CBV,
|
|
ROOT_PARAMETER_GS_CBV,
|
|
ROOT_PARAMETER_VS_SRV,
|
|
ROOT_PARAMETER_BASE_VERTEX_CONSTANT,
|
|
ROOT_PARAMETER_PS_CUS_CBV,
|
|
ROOT_PARAMETER_PS_UAV_OR_CBV2,
|
|
ROOT_PARAMETER_PS_CBV2, // ROOT_PARAMETER_PS_UAV_OR_CBV2 if bbox is not enabled
|
|
NUM_ROOT_PARAMETERS
|
|
};
|
|
// Compute shader root parameters
|
|
enum CS_ROOT_PARAMETERS
|
|
{
|
|
CS_ROOT_PARAMETER_CBV,
|
|
CS_ROOT_PARAMETER_SRV,
|
|
CS_ROOT_PARAMETER_SAMPLERS,
|
|
CS_ROOT_PARAMETER_UAV,
|
|
NUM_CS_ROOT_PARAMETERS,
|
|
};
|
|
|
|
class DXContext
|
|
{
|
|
public:
|
|
~DXContext();
|
|
|
|
// Returns a list of AA modes.
|
|
static std::vector<u32> GetAAModes(u32 adapter_index);
|
|
|
|
// Creates new device and context.
|
|
static bool Create(u32 adapter_index, bool enable_debug_layer);
|
|
|
|
// Destroys active context.
|
|
static void Destroy();
|
|
|
|
IDXGIFactory* GetDXGIFactory() const { return m_dxgi_factory.Get(); }
|
|
ID3D12Device* GetDevice() const { return m_device.Get(); }
|
|
ID3D12CommandQueue* GetCommandQueue() const { return m_command_queue.Get(); }
|
|
|
|
// Returns the current command list, commands can be recorded directly.
|
|
ID3D12GraphicsCommandList* GetCommandList() const
|
|
{
|
|
return m_command_lists[m_current_command_list].command_list.Get();
|
|
}
|
|
DescriptorAllocator* GetDescriptorAllocator()
|
|
{
|
|
return &m_command_lists[m_current_command_list].descriptor_allocator;
|
|
}
|
|
SamplerAllocator* GetSamplerAllocator()
|
|
{
|
|
return &m_command_lists[m_current_command_list].sampler_allocator;
|
|
}
|
|
|
|
// Descriptor manager access.
|
|
DescriptorHeapManager& GetDescriptorHeapManager() { return m_descriptor_heap_manager; }
|
|
DescriptorHeapManager& GetRTVHeapManager() { return m_rtv_heap_manager; }
|
|
DescriptorHeapManager& GetDSVHeapManager() { return m_dsv_heap_manager; }
|
|
SamplerHeapManager& GetSamplerHeapManager() { return m_sampler_heap_manager; }
|
|
ID3D12DescriptorHeap* const* GetGPUDescriptorHeaps() const
|
|
{
|
|
return m_gpu_descriptor_heaps.data();
|
|
}
|
|
u32 GetGPUDescriptorHeapCount() const { return static_cast<u32>(m_gpu_descriptor_heaps.size()); }
|
|
const DescriptorHandle& GetNullSRVDescriptor() const { return m_null_srv_descriptor; }
|
|
|
|
// Root signature access.
|
|
ID3D12RootSignature* GetGXRootSignature() const { return m_gx_root_signature.Get(); }
|
|
ID3D12RootSignature* GetUtilityRootSignature() const { return m_utility_root_signature.Get(); }
|
|
ID3D12RootSignature* GetComputeRootSignature() const { return m_compute_root_signature.Get(); }
|
|
|
|
// Fence value for current command list.
|
|
u64 GetCurrentFenceValue() const { return m_current_fence_value; }
|
|
|
|
// Last "completed" fence.
|
|
u64 GetCompletedFenceValue() const { return m_completed_fence_value; }
|
|
|
|
// Texture streaming buffer for uploads.
|
|
StreamBuffer& GetTextureUploadBuffer() { return m_texture_upload_buffer; }
|
|
|
|
// Feature level to use when compiling shaders.
|
|
D3D_FEATURE_LEVEL GetFeatureLevel() const { return m_feature_level; }
|
|
|
|
// Test for support for the specified texture format.
|
|
bool SupportsTextureFormat(DXGI_FORMAT format);
|
|
|
|
// Creates command lists, global buffers and descriptor heaps.
|
|
bool CreateGlobalResources();
|
|
|
|
// Executes the current command list.
|
|
void ExecuteCommandList(bool wait_for_completion);
|
|
|
|
// Waits for a specific fence.
|
|
void WaitForFence(u64 fence);
|
|
|
|
// Defers destruction of a D3D resource (associates it with the current list).
|
|
void DeferResourceDestruction(ID3D12Resource* resource);
|
|
|
|
// Defers destruction of any other device object, e.g. a pipeline state.
|
|
void DeferObjectDestruction(IUnknown* object);
|
|
|
|
// Defers destruction of a descriptor handle (associates it with the current list).
|
|
void DeferDescriptorDestruction(DescriptorHeapManager& manager, u32 index);
|
|
|
|
// Clears all samplers from the per-frame allocators.
|
|
void ResetSamplerAllocators();
|
|
|
|
// Re-creates the root signature. Call when the host config changes (e.g. bbox/per-pixel shading).
|
|
void RecreateGXRootSignature();
|
|
|
|
public:
|
|
// How many command lists this device was created with. Bounded by
|
|
// Config::GFX_COMMAND_BUFFERS_IN_FLIGHT_MIN/MAX.
|
|
//
|
|
// Command lists are the ring Dolphin builds into. One is being built while the others are
|
|
// executed, and every ExecuteCommandList rotates the ring and blocks until the slot being
|
|
// reused has finished on the GPU. The ring therefore has to span more wall time than the
|
|
// GPU's completion latency, or that wait becomes the dominant cost. Workloads submitting
|
|
// many times per frame reach that point easily: CPU EFB access is the big one (each peek
|
|
// batch is a submit), and VR adds the eye-swapchain release on top — measured at ~13
|
|
// submits/frame in Mario Galaxy. At the old fixed value of 3 the ring wrapped into
|
|
// unfinished work constantly (40-54% of wall spent stalled on PSVR2/SteamVR); 16 drops that
|
|
// to zero. Vulkan's command buffer ring is sized from the same setting.
|
|
//
|
|
// Each slot costs one shader-visible descriptor heap of TEMPORARY_SLOTS entries (~2MB) plus a
|
|
// command allocator. The 1M descriptor cap is per heap rather than aggregate, so that memory
|
|
// is the only cost of a deeper ring — with the caveat that a deeper ring lets the CPU run
|
|
// further ahead of the GPU, which is latency if you are actually GPU-bound.
|
|
u32 GetCommandListCount() const { return m_num_command_lists; }
|
|
|
|
private:
|
|
|
|
// Textures that don't fit into this buffer will be uploaded with a staging buffer.
|
|
static const u32 TEXTURE_UPLOAD_BUFFER_SIZE = 32 * 1024 * 1024;
|
|
|
|
struct CommandListResources
|
|
{
|
|
ComPtr<ID3D12CommandAllocator> command_allocator;
|
|
ComPtr<ID3D12GraphicsCommandList> command_list;
|
|
DescriptorAllocator descriptor_allocator;
|
|
SamplerAllocator sampler_allocator;
|
|
std::vector<IUnknown*> pending_resources;
|
|
std::vector<std::pair<DescriptorHeapManager&, u32>> pending_descriptors;
|
|
u64 ready_fence_value = 0;
|
|
};
|
|
|
|
DXContext();
|
|
|
|
bool CreateDXGIFactory(bool enable_debug_layer);
|
|
bool CreateDevice(u32 adapter_index, bool enable_debug_layer);
|
|
bool CreateCommandQueue();
|
|
bool CreateFence();
|
|
bool CreateDescriptorHeaps();
|
|
bool CreateRootSignatures();
|
|
bool CreateGXRootSignature();
|
|
bool CreateUtilityRootSignature();
|
|
bool CreateComputeRootSignature();
|
|
bool CreateTextureUploadBuffer();
|
|
bool CreateCommandLists();
|
|
void MoveToNextCommandList();
|
|
void DestroyPendingResources(CommandListResources& cmdlist);
|
|
|
|
ComPtr<IDXGIFactory> m_dxgi_factory;
|
|
ComPtr<ID3D12Debug> m_debug_interface;
|
|
ComPtr<ID3D12Device> m_device;
|
|
ComPtr<ID3D12CommandQueue> m_command_queue;
|
|
|
|
ComPtr<ID3D12Fence> m_fence = nullptr;
|
|
HANDLE m_fence_event = {};
|
|
u32 m_current_fence_value = 0;
|
|
u64 m_completed_fence_value = 0;
|
|
|
|
// Sized from Config::GFX_COMMAND_BUFFERS_IN_FLIGHT at device init; fixed for the device's life,
|
|
// so changing the setting takes effect on the next emulation start.
|
|
std::vector<CommandListResources> m_command_lists;
|
|
u32 m_num_command_lists = 0;
|
|
u32 m_current_command_list = 0;
|
|
|
|
DescriptorHeapManager m_descriptor_heap_manager;
|
|
DescriptorHeapManager m_rtv_heap_manager;
|
|
DescriptorHeapManager m_dsv_heap_manager;
|
|
SamplerHeapManager m_sampler_heap_manager;
|
|
std::array<ID3D12DescriptorHeap*, 2> m_gpu_descriptor_heaps = {};
|
|
DescriptorHandle m_null_srv_descriptor;
|
|
D3D_FEATURE_LEVEL m_feature_level = D3D_FEATURE_LEVEL_11_0;
|
|
|
|
ComPtr<ID3D12RootSignature> m_gx_root_signature;
|
|
ComPtr<ID3D12RootSignature> m_utility_root_signature;
|
|
ComPtr<ID3D12RootSignature> m_compute_root_signature;
|
|
|
|
StreamBuffer m_texture_upload_buffer;
|
|
};
|
|
|
|
extern std::unique_ptr<DXContext> g_dx_context;
|
|
|
|
// Wrapper for HRESULT to be used with fmt. Note that we can't create a fmt::formatter directly
|
|
// for HRESULT as HRESULT is simply a typedef on long and not a distinct type.
|
|
// Unlike the version in Common, this variant also knows to call GetDeviceRemovedReason if needed.
|
|
struct DX12HRWrap
|
|
{
|
|
constexpr explicit DX12HRWrap(HRESULT hr) : m_hr(hr) {}
|
|
const HRESULT m_hr;
|
|
};
|
|
|
|
} // namespace DX12
|
|
|
|
template <>
|
|
struct fmt::formatter<DX12::DX12HRWrap>
|
|
{
|
|
constexpr auto parse(fmt::format_parse_context& ctx) { return ctx.begin(); }
|
|
template <typename FormatContext>
|
|
auto format(const DX12::DX12HRWrap& hr, FormatContext& ctx) const
|
|
{
|
|
if (hr.m_hr == DXGI_ERROR_DEVICE_REMOVED && DX12::g_dx_context != nullptr &&
|
|
DX12::g_dx_context->GetDevice() != nullptr)
|
|
{
|
|
return fmt::format_to(
|
|
ctx.out(), "{}\nDevice removal reason: {}", Common::HRWrap(hr.m_hr),
|
|
Common::HRWrap(DX12::g_dx_context->GetDevice()->GetDeviceRemovedReason()));
|
|
}
|
|
else
|
|
{
|
|
return fmt::format_to(ctx.out(), "{}", Common::HRWrap(hr.m_hr));
|
|
}
|
|
}
|
|
};
|