mirror of https://github.com/PCSX2/pcsx2.git
Frontend: Add D3D12HostDisplay
This commit is contained in:
parent
e767fb8d35
commit
3c18cdcb1f
|
@ -276,7 +276,7 @@ void Texture::TransitionToState(ID3D12GraphicsCommandList* cmdlist, D3D12_RESOUR
|
||||||
}
|
}
|
||||||
|
|
||||||
void Texture::TransitionSubresourceToState(ID3D12GraphicsCommandList* cmdlist, u32 level,
|
void Texture::TransitionSubresourceToState(ID3D12GraphicsCommandList* cmdlist, u32 level,
|
||||||
D3D12_RESOURCE_STATES before_state, D3D12_RESOURCE_STATES after_state)
|
D3D12_RESOURCE_STATES before_state, D3D12_RESOURCE_STATES after_state) const
|
||||||
{
|
{
|
||||||
const D3D12_RESOURCE_BARRIER barrier = {D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
|
const D3D12_RESOURCE_BARRIER barrier = {D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
|
||||||
D3D12_RESOURCE_BARRIER_FLAG_NONE,
|
D3D12_RESOURCE_BARRIER_FLAG_NONE,
|
||||||
|
|
|
@ -68,7 +68,7 @@ namespace D3D12
|
||||||
|
|
||||||
void TransitionToState(ID3D12GraphicsCommandList* cmdlist, D3D12_RESOURCE_STATES state);
|
void TransitionToState(ID3D12GraphicsCommandList* cmdlist, D3D12_RESOURCE_STATES state);
|
||||||
void TransitionSubresourceToState(ID3D12GraphicsCommandList* cmdlist, u32 level,
|
void TransitionSubresourceToState(ID3D12GraphicsCommandList* cmdlist, u32 level,
|
||||||
D3D12_RESOURCE_STATES before_state, D3D12_RESOURCE_STATES after_state);
|
D3D12_RESOURCE_STATES before_state, D3D12_RESOURCE_STATES after_state) const;
|
||||||
|
|
||||||
Texture& operator=(const Texture&) = delete;
|
Texture& operator=(const Texture&) = delete;
|
||||||
Texture& operator=(Texture&& texture);
|
Texture& operator=(Texture&& texture);
|
||||||
|
|
|
@ -1022,9 +1022,11 @@ endif()
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
list(APPEND pcsx2FrontendSources
|
list(APPEND pcsx2FrontendSources
|
||||||
Frontend/D3D11HostDisplay.cpp
|
Frontend/D3D11HostDisplay.cpp
|
||||||
|
Frontend/D3D12HostDisplay.cpp
|
||||||
)
|
)
|
||||||
list(APPEND pcsx2FrontendHeaders
|
list(APPEND pcsx2FrontendHeaders
|
||||||
Frontend/D3D11HostDisplay.h
|
Frontend/D3D11HostDisplay.h
|
||||||
|
Frontend/D3D12HostDisplay.h
|
||||||
)
|
)
|
||||||
elseif(APPLE)
|
elseif(APPLE)
|
||||||
list(APPEND pcsx2GSSources
|
list(APPEND pcsx2GSSources
|
||||||
|
@ -1621,6 +1623,7 @@ if(WIN32)
|
||||||
baseclasses
|
baseclasses
|
||||||
pthreads4w
|
pthreads4w
|
||||||
WIL::WIL
|
WIL::WIL
|
||||||
|
D3D12MemAlloc
|
||||||
setupapi.lib
|
setupapi.lib
|
||||||
ws2_32.lib
|
ws2_32.lib
|
||||||
shlwapi.lib
|
shlwapi.lib
|
||||||
|
|
|
@ -0,0 +1,759 @@
|
||||||
|
/* PCSX2 - PS2 Emulator for PCs
|
||||||
|
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||||
|
*
|
||||||
|
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||||
|
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||||
|
* ation, either version 3 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||||
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE. See the GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||||
|
* If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "PrecompiledHeader.h"
|
||||||
|
|
||||||
|
#include "Frontend/D3D12HostDisplay.h"
|
||||||
|
#include "common/Assertions.h"
|
||||||
|
#include "common/Console.h"
|
||||||
|
#include "common/D3D12/Context.h"
|
||||||
|
#include "common/D3D12/Util.h"
|
||||||
|
#include "common/StringUtil.h"
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
#include "backends/imgui_impl_dx12.h"
|
||||||
|
#include <array>
|
||||||
|
#include <dxgi1_5.h>
|
||||||
|
|
||||||
|
class D3D12HostDisplayTexture : public HostDisplayTexture
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
D3D12HostDisplayTexture(D3D12::Texture texture)
|
||||||
|
: m_texture(std::move(texture))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
~D3D12HostDisplayTexture() override = default;
|
||||||
|
|
||||||
|
void* GetHandle() const override { return const_cast<D3D12::Texture*>(&m_texture); }
|
||||||
|
u32 GetWidth() const override { return m_texture.GetWidth(); }
|
||||||
|
u32 GetHeight() const override { return m_texture.GetHeight(); }
|
||||||
|
|
||||||
|
const D3D12::Texture& GetTexture() const { return m_texture; }
|
||||||
|
D3D12::Texture& GetTexture() { return m_texture; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
D3D12::Texture m_texture;
|
||||||
|
};
|
||||||
|
|
||||||
|
D3D12HostDisplay::D3D12HostDisplay() = default;
|
||||||
|
|
||||||
|
D3D12HostDisplay::~D3D12HostDisplay()
|
||||||
|
{
|
||||||
|
pxAssertMsg(!g_d3d12_context, "Context should have been destroyed by now");
|
||||||
|
pxAssertMsg(!m_swap_chain, "Swap chain should have been destroyed by now");
|
||||||
|
}
|
||||||
|
|
||||||
|
HostDisplay::RenderAPI D3D12HostDisplay::GetRenderAPI() const
|
||||||
|
{
|
||||||
|
return HostDisplay::RenderAPI::D3D12;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* D3D12HostDisplay::GetRenderDevice() const
|
||||||
|
{
|
||||||
|
return g_d3d12_context->GetDevice();
|
||||||
|
}
|
||||||
|
|
||||||
|
void* D3D12HostDisplay::GetRenderContext() const
|
||||||
|
{
|
||||||
|
return g_d3d12_context.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void* D3D12HostDisplay::GetRenderSurface() const
|
||||||
|
{
|
||||||
|
return m_swap_chain.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D12HostDisplay::HasRenderDevice() const
|
||||||
|
{
|
||||||
|
return static_cast<bool>(g_d3d12_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D12HostDisplay::HasRenderSurface() const
|
||||||
|
{
|
||||||
|
return static_cast<bool>(m_swap_chain);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<HostDisplayTexture> D3D12HostDisplay::CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, bool dynamic /* = false */)
|
||||||
|
{
|
||||||
|
D3D12::Texture tex;
|
||||||
|
if (!tex.Create(width, height, 1, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN,
|
||||||
|
D3D12_RESOURCE_FLAG_NONE))
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data && !tex.LoadData(0, 0, 0, width, height, data, data_stride))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return std::make_unique<D3D12HostDisplayTexture>(std::move(tex));
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D12HostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height,
|
||||||
|
const void* texture_data, u32 texture_data_stride)
|
||||||
|
{
|
||||||
|
static_cast<D3D12HostDisplayTexture*>(texture)->GetTexture().LoadData(0, x, y, width, height, texture_data,
|
||||||
|
texture_data_stride);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D12HostDisplay::GetHostRefreshRate(float* refresh_rate)
|
||||||
|
{
|
||||||
|
if (m_swap_chain && IsFullscreen())
|
||||||
|
{
|
||||||
|
DXGI_SWAP_CHAIN_DESC desc;
|
||||||
|
if (SUCCEEDED(m_swap_chain->GetDesc(&desc)) && desc.BufferDesc.RefreshRate.Numerator > 0 &&
|
||||||
|
desc.BufferDesc.RefreshRate.Denominator > 0)
|
||||||
|
{
|
||||||
|
DevCon.WriteLn("using fs rr: %u %u", desc.BufferDesc.RefreshRate.Numerator,
|
||||||
|
desc.BufferDesc.RefreshRate.Denominator);
|
||||||
|
*refresh_rate = static_cast<float>(desc.BufferDesc.RefreshRate.Numerator) /
|
||||||
|
static_cast<float>(desc.BufferDesc.RefreshRate.Denominator);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return HostDisplay::GetHostRefreshRate(refresh_rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D12HostDisplay::SetVSync(VsyncMode mode)
|
||||||
|
{
|
||||||
|
m_vsync_mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D12HostDisplay::CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, VsyncMode vsync, bool threaded_presentation, bool debug_device)
|
||||||
|
{
|
||||||
|
ComPtr<IDXGIFactory> temp_dxgi_factory;
|
||||||
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||||
|
HRESULT hr = CreateDXGIFactory(IID_PPV_ARGS(temp_dxgi_factory.put()));
|
||||||
|
#else
|
||||||
|
HRESULT hr = CreateDXGIFactory2(0, IID_PPV_ARGS(temp_dxgi_factory.put()));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
Console.Error("Failed to create DXGI factory: 0x%08X", hr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 adapter_index;
|
||||||
|
if (!adapter_name.empty())
|
||||||
|
{
|
||||||
|
AdapterAndModeList adapter_info(GetAdapterAndModeList(temp_dxgi_factory.get()));
|
||||||
|
for (adapter_index = 0; adapter_index < static_cast<u32>(adapter_info.adapter_names.size()); adapter_index++)
|
||||||
|
{
|
||||||
|
if (adapter_name == adapter_info.adapter_names[adapter_index])
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (adapter_index == static_cast<u32>(adapter_info.adapter_names.size()))
|
||||||
|
{
|
||||||
|
Console.Warning("Could not find adapter '%*s', using first (%s)", static_cast<int>(adapter_name.size()),
|
||||||
|
adapter_name.data(), adapter_info.adapter_names[0].c_str());
|
||||||
|
adapter_index = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLn("No adapter selected, using first.");
|
||||||
|
adapter_index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!D3D12::Context::Create(temp_dxgi_factory.get(), adapter_index, debug_device))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
Console.Error("Failed to create D3D device: 0x%08X", hr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dxgi_factory = std::move(temp_dxgi_factory);
|
||||||
|
|
||||||
|
m_allow_tearing_supported = false;
|
||||||
|
ComPtr<IDXGIFactory5> dxgi_factory5(m_dxgi_factory.try_query<IDXGIFactory5>());
|
||||||
|
if (dxgi_factory5)
|
||||||
|
{
|
||||||
|
BOOL allow_tearing_supported = false;
|
||||||
|
hr = dxgi_factory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing_supported,
|
||||||
|
sizeof(allow_tearing_supported));
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
m_allow_tearing_supported = (allow_tearing_supported == TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_window_info = wi;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D12HostDisplay::InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device)
|
||||||
|
{
|
||||||
|
if (m_window_info.type != WindowInfo::Type::Surfaceless && !CreateSwapChain(nullptr))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D12HostDisplay::DestroyRenderDevice()
|
||||||
|
{
|
||||||
|
g_d3d12_context->ExecuteCommandList(true);
|
||||||
|
|
||||||
|
DestroyRenderSurface();
|
||||||
|
if (g_d3d12_context)
|
||||||
|
g_d3d12_context->Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D12HostDisplay::MakeRenderContextCurrent()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D12HostDisplay::DoneRenderContextCurrent()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D12HostDisplay::CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode)
|
||||||
|
{
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||||
|
if (m_window_info.type != WindowInfo::Type::Win32)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const HWND window_hwnd = reinterpret_cast<HWND>(m_window_info.window_handle);
|
||||||
|
RECT client_rc{};
|
||||||
|
GetClientRect(window_hwnd, &client_rc);
|
||||||
|
const u32 width = static_cast<u32>(client_rc.right - client_rc.left);
|
||||||
|
const u32 height = static_cast<u32>(client_rc.bottom - client_rc.top);
|
||||||
|
|
||||||
|
DXGI_SWAP_CHAIN_DESC swap_chain_desc = {};
|
||||||
|
swap_chain_desc.BufferDesc.Width = width;
|
||||||
|
swap_chain_desc.BufferDesc.Height = height;
|
||||||
|
swap_chain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||||
|
swap_chain_desc.SampleDesc.Count = 1;
|
||||||
|
swap_chain_desc.BufferCount = 3;
|
||||||
|
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||||
|
swap_chain_desc.OutputWindow = window_hwnd;
|
||||||
|
swap_chain_desc.Windowed = TRUE;
|
||||||
|
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
|
||||||
|
|
||||||
|
m_using_allow_tearing = (m_allow_tearing_supported && !fullscreen_mode);
|
||||||
|
if (m_using_allow_tearing)
|
||||||
|
swap_chain_desc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
|
||||||
|
|
||||||
|
if (fullscreen_mode)
|
||||||
|
{
|
||||||
|
swap_chain_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
|
||||||
|
swap_chain_desc.Windowed = FALSE;
|
||||||
|
swap_chain_desc.BufferDesc = *fullscreen_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
DevCon.WriteLn("Creating a %dx%d %s swap chain", swap_chain_desc.BufferDesc.Width, swap_chain_desc.BufferDesc.Height,
|
||||||
|
swap_chain_desc.Windowed ? "windowed" : "full-screen");
|
||||||
|
|
||||||
|
hr =
|
||||||
|
m_dxgi_factory->CreateSwapChain(g_d3d12_context->GetCommandQueue(), &swap_chain_desc, m_swap_chain.put());
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
Console.Error("CreateSwapChain failed: 0x%08X", hr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = m_dxgi_factory->MakeWindowAssociation(swap_chain_desc.OutputWindow, DXGI_MWA_NO_WINDOW_CHANGES);
|
||||||
|
if (FAILED(hr))
|
||||||
|
Console.Warning("MakeWindowAssociation() to disable ALT+ENTER failed");
|
||||||
|
#else
|
||||||
|
if (m_window_info.type != WindowInfo::Type::WinRT)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ComPtr<IDXGIFactory2> factory2;
|
||||||
|
hr = m_dxgi_factory.As(&factory2);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
Console.Error("Failed to get DXGI factory: %08X", hr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {};
|
||||||
|
swap_chain_desc.Width = m_window_info.surface_width;
|
||||||
|
swap_chain_desc.Height = m_window_info.surface_height;
|
||||||
|
swap_chain_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||||
|
swap_chain_desc.SampleDesc.Count = 1;
|
||||||
|
swap_chain_desc.BufferCount = 3;
|
||||||
|
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||||
|
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
|
||||||
|
|
||||||
|
m_using_allow_tearing = (m_allow_tearing_supported && !fullscreen_mode);
|
||||||
|
if (m_using_allow_tearing)
|
||||||
|
swap_chain_desc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
|
||||||
|
|
||||||
|
ComPtr<IDXGISwapChain1> swap_chain1;
|
||||||
|
hr = factory2->CreateSwapChainForCoreWindow(g_d3d12_context->GetCommandQueue(),
|
||||||
|
static_cast<IUnknown*>(m_window_info.window_handle), &swap_chain_desc,
|
||||||
|
nullptr, swap_chain1.GetAddressOf());
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
Console.Error("CreateSwapChainForCoreWindow failed: 0x%08X", hr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_swap_chain = swap_chain1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return CreateSwapChainRTV();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D12HostDisplay::CreateSwapChainRTV()
|
||||||
|
{
|
||||||
|
DXGI_SWAP_CHAIN_DESC swap_chain_desc;
|
||||||
|
HRESULT hr = m_swap_chain->GetDesc(&swap_chain_desc);
|
||||||
|
if (FAILED(hr))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (u32 i = 0; i < swap_chain_desc.BufferCount; i++)
|
||||||
|
{
|
||||||
|
ComPtr<ID3D12Resource> backbuffer;
|
||||||
|
hr = m_swap_chain->GetBuffer(i, IID_PPV_ARGS(backbuffer.put()));
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
Console.Error("GetBuffer for RTV failed: 0x%08X", hr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
D3D12::Texture tex;
|
||||||
|
if (!tex.Adopt(std::move(backbuffer), DXGI_FORMAT_UNKNOWN, swap_chain_desc.BufferDesc.Format, DXGI_FORMAT_UNKNOWN,
|
||||||
|
D3D12_RESOURCE_STATE_PRESENT))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_swap_chain_buffers.push_back(std::move(tex));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_window_info.surface_width = swap_chain_desc.BufferDesc.Width;
|
||||||
|
m_window_info.surface_height = swap_chain_desc.BufferDesc.Height;
|
||||||
|
DevCon.WriteLn("Swap chain buffer size: %ux%u", m_window_info.surface_width, m_window_info.surface_height);
|
||||||
|
|
||||||
|
if (m_window_info.type == WindowInfo::Type::Win32)
|
||||||
|
{
|
||||||
|
BOOL fullscreen = FALSE;
|
||||||
|
DXGI_SWAP_CHAIN_DESC desc;
|
||||||
|
if (SUCCEEDED(m_swap_chain->GetFullscreenState(&fullscreen, nullptr)) && fullscreen &&
|
||||||
|
SUCCEEDED(m_swap_chain->GetDesc(&desc)))
|
||||||
|
{
|
||||||
|
m_window_info.surface_refresh_rate = static_cast<float>(desc.BufferDesc.RefreshRate.Numerator) /
|
||||||
|
static_cast<float>(desc.BufferDesc.RefreshRate.Denominator);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_window_info.surface_refresh_rate = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_current_swap_chain_buffer = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D12HostDisplay::DestroySwapChainRTVs()
|
||||||
|
{
|
||||||
|
for (D3D12::Texture& buffer : m_swap_chain_buffers)
|
||||||
|
buffer.Destroy(false);
|
||||||
|
m_swap_chain_buffers.clear();
|
||||||
|
m_current_swap_chain_buffer = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D12HostDisplay::ChangeRenderWindow(const WindowInfo& new_wi)
|
||||||
|
{
|
||||||
|
DestroyRenderSurface();
|
||||||
|
|
||||||
|
m_window_info = new_wi;
|
||||||
|
return CreateSwapChain(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D12HostDisplay::DestroyRenderSurface()
|
||||||
|
{
|
||||||
|
// For some reason if we don't execute the command list here, the swap chain is in use.. not sure where.
|
||||||
|
g_d3d12_context->ExecuteCommandList(true);
|
||||||
|
|
||||||
|
if (IsFullscreen())
|
||||||
|
SetFullscreen(false, 0, 0, 0.0f);
|
||||||
|
|
||||||
|
DestroySwapChainRTVs();
|
||||||
|
m_swap_chain.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string GetDriverVersionFromLUID(const LUID& luid)
|
||||||
|
{
|
||||||
|
std::string ret;
|
||||||
|
|
||||||
|
HKEY hKey;
|
||||||
|
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\DirectX"), 0, KEY_READ, &hKey) == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
DWORD max_key_len = 0, adapter_count = 0;
|
||||||
|
if (RegQueryInfoKey(hKey, nullptr, nullptr, nullptr, &adapter_count, &max_key_len,
|
||||||
|
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr) == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
std::vector<TCHAR> current_name(max_key_len + 1);
|
||||||
|
for (DWORD i = 0; i < adapter_count; ++i)
|
||||||
|
{
|
||||||
|
DWORD subKeyLength = static_cast<DWORD>(current_name.size());
|
||||||
|
if (RegEnumKeyEx(hKey, i, current_name.data(), &subKeyLength, nullptr, nullptr, nullptr, nullptr) == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
LUID current_luid = {};
|
||||||
|
DWORD current_luid_size = sizeof(uint64_t);
|
||||||
|
if (RegGetValue(hKey, current_name.data(), _T("AdapterLuid"), RRF_RT_QWORD, nullptr, ¤t_luid, ¤t_luid_size) == ERROR_SUCCESS &&
|
||||||
|
current_luid.HighPart == luid.HighPart && current_luid.LowPart == luid.LowPart)
|
||||||
|
{
|
||||||
|
LARGE_INTEGER driver_version = {};
|
||||||
|
DWORD driver_version_size = sizeof(driver_version);
|
||||||
|
if (RegGetValue(hKey, current_name.data(), _T("DriverVersion"), RRF_RT_QWORD, nullptr, &driver_version, &driver_version_size) == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
WORD nProduct = HIWORD(driver_version.HighPart);
|
||||||
|
WORD nVersion = LOWORD(driver_version.HighPart);
|
||||||
|
WORD nSubVersion = HIWORD(driver_version.LowPart);
|
||||||
|
WORD nBuild = LOWORD(driver_version.LowPart);
|
||||||
|
ret = StringUtil::StdStringFromFormat("%u.%u.%u.%u", nProduct, nVersion, nSubVersion, nBuild);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RegCloseKey(hKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string D3D12HostDisplay::GetDriverInfo() const
|
||||||
|
{
|
||||||
|
std::string ret = "Unknown Feature Level";
|
||||||
|
|
||||||
|
static constexpr std::array<std::tuple<D3D_FEATURE_LEVEL, const char*>, 4> feature_level_names = {{
|
||||||
|
{D3D_FEATURE_LEVEL_10_0, "D3D_FEATURE_LEVEL_10_0"},
|
||||||
|
{D3D_FEATURE_LEVEL_10_0, "D3D_FEATURE_LEVEL_10_1"},
|
||||||
|
{D3D_FEATURE_LEVEL_11_0, "D3D_FEATURE_LEVEL_11_0"},
|
||||||
|
{D3D_FEATURE_LEVEL_11_1, "D3D_FEATURE_LEVEL_11_1"},
|
||||||
|
}};
|
||||||
|
|
||||||
|
const D3D_FEATURE_LEVEL fl = g_d3d12_context->GetFeatureLevel();
|
||||||
|
for (size_t i = 0; i < std::size(feature_level_names); i++)
|
||||||
|
{
|
||||||
|
if (fl == std::get<0>(feature_level_names[i]))
|
||||||
|
{
|
||||||
|
ret = std::get<1>(feature_level_names[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret += "\n";
|
||||||
|
|
||||||
|
IDXGIAdapter* adapter = g_d3d12_context->GetAdapter();
|
||||||
|
DXGI_ADAPTER_DESC desc;
|
||||||
|
if (adapter && SUCCEEDED(adapter->GetDesc(&desc)))
|
||||||
|
{
|
||||||
|
ret += StringUtil::StdStringFromFormat("VID: 0x%04X PID: 0x%04X\n", desc.VendorId, desc.DeviceId);
|
||||||
|
ret += StringUtil::WideStringToUTF8String(desc.Description);
|
||||||
|
ret += "\n";
|
||||||
|
|
||||||
|
const std::string driver_version(GetDriverVersionFromLUID(desc.AdapterLuid));
|
||||||
|
if (!driver_version.empty())
|
||||||
|
{
|
||||||
|
ret += "Driver Version: ";
|
||||||
|
ret += driver_version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D12HostDisplay::ResizeRenderWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
|
||||||
|
{
|
||||||
|
if (!m_swap_chain)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_window_info.surface_scale = new_window_scale;
|
||||||
|
|
||||||
|
if (m_window_info.surface_width == new_window_width && m_window_info.surface_height == new_window_height)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// For some reason if we don't execute the command list here, the swap chain is in use.. not sure where.
|
||||||
|
g_d3d12_context->ExecuteCommandList(true);
|
||||||
|
|
||||||
|
DestroySwapChainRTVs();
|
||||||
|
|
||||||
|
HRESULT hr = m_swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN,
|
||||||
|
m_using_allow_tearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0);
|
||||||
|
if (FAILED(hr))
|
||||||
|
Console.Error("ResizeBuffers() failed: 0x%08X", hr);
|
||||||
|
|
||||||
|
if (!CreateSwapChainRTV())
|
||||||
|
pxFailRel("Failed to recreate swap chain RTV after resize");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D12HostDisplay::SupportsFullscreen() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D12HostDisplay::IsFullscreen()
|
||||||
|
{
|
||||||
|
BOOL is_fullscreen = FALSE;
|
||||||
|
return (m_swap_chain && SUCCEEDED(m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr)) && is_fullscreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D12HostDisplay::SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate)
|
||||||
|
{
|
||||||
|
if (!m_swap_chain)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
BOOL is_fullscreen = FALSE;
|
||||||
|
HRESULT hr = m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr);
|
||||||
|
if (!fullscreen)
|
||||||
|
{
|
||||||
|
// leaving fullscreen
|
||||||
|
if (is_fullscreen)
|
||||||
|
return SUCCEEDED(m_swap_chain->SetFullscreenState(FALSE, nullptr));
|
||||||
|
else
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
IDXGIOutput* output;
|
||||||
|
if (FAILED(hr = m_swap_chain->GetContainingOutput(&output)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
DXGI_SWAP_CHAIN_DESC current_desc;
|
||||||
|
hr = m_swap_chain->GetDesc(¤t_desc);
|
||||||
|
if (FAILED(hr))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
DXGI_MODE_DESC new_mode = current_desc.BufferDesc;
|
||||||
|
new_mode.Width = width;
|
||||||
|
new_mode.Height = height;
|
||||||
|
new_mode.RefreshRate.Numerator = static_cast<UINT>(std::floor(refresh_rate * 1000.0f));
|
||||||
|
new_mode.RefreshRate.Denominator = 1000u;
|
||||||
|
|
||||||
|
DXGI_MODE_DESC closest_mode;
|
||||||
|
if (FAILED(hr = output->FindClosestMatchingMode(&new_mode, &closest_mode, nullptr)) ||
|
||||||
|
new_mode.Format != current_desc.BufferDesc.Format)
|
||||||
|
{
|
||||||
|
Console.Error("Failed to find closest matching mode, hr=%08X", hr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_mode.Width == current_desc.BufferDesc.Width && new_mode.Height == current_desc.BufferDesc.Width &&
|
||||||
|
new_mode.RefreshRate.Numerator == current_desc.BufferDesc.RefreshRate.Numerator &&
|
||||||
|
new_mode.RefreshRate.Denominator == current_desc.BufferDesc.RefreshRate.Denominator)
|
||||||
|
{
|
||||||
|
DevCon.WriteLn("Fullscreen mode already set");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_d3d12_context->ExecuteCommandList(true);
|
||||||
|
DestroySwapChainRTVs();
|
||||||
|
m_swap_chain.reset();
|
||||||
|
|
||||||
|
if (!CreateSwapChain(&closest_mode))
|
||||||
|
{
|
||||||
|
Console.Error("Failed to create a fullscreen swap chain");
|
||||||
|
if (!CreateSwapChain(nullptr))
|
||||||
|
pxFailRel("Failed to recreate windowed swap chain");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
HostDisplay::AdapterAndModeList D3D12HostDisplay::GetAdapterAndModeList()
|
||||||
|
{
|
||||||
|
return GetAdapterAndModeList(m_dxgi_factory.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D12HostDisplay::CreateImGuiContext()
|
||||||
|
{
|
||||||
|
ImGui::GetIO().DisplaySize.x = static_cast<float>(m_window_info.surface_width);
|
||||||
|
ImGui::GetIO().DisplaySize.y = static_cast<float>(m_window_info.surface_height);
|
||||||
|
|
||||||
|
if (!m_imgui_descriptor_heap.Create(g_d3d12_context->GetDevice(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1, true))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!m_imgui_descriptor_heap.Allocate(&m_imgui_descriptor_handle))
|
||||||
|
{
|
||||||
|
m_imgui_descriptor_heap.Destroy();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ImGui_ImplDX12_Init(g_d3d12_context->GetDevice(), D3D12::Context::NUM_COMMAND_LISTS,
|
||||||
|
DXGI_FORMAT_R8G8B8A8_UNORM, m_imgui_descriptor_heap.GetDescriptorHeap(),
|
||||||
|
m_imgui_descriptor_handle, m_imgui_descriptor_handle))
|
||||||
|
{
|
||||||
|
m_imgui_descriptor_heap.Free(m_imgui_descriptor_handle);
|
||||||
|
m_imgui_descriptor_heap.Destroy();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D12HostDisplay::DestroyImGuiContext()
|
||||||
|
{
|
||||||
|
g_d3d12_context->WaitForGPUIdle();
|
||||||
|
|
||||||
|
ImGui_ImplDX12_Shutdown();
|
||||||
|
|
||||||
|
m_imgui_descriptor_heap.Free(&m_imgui_descriptor_handle);
|
||||||
|
m_imgui_descriptor_heap.Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D12HostDisplay::UpdateImGuiFontTexture()
|
||||||
|
{
|
||||||
|
ImGui_ImplDX12_InvalidateDeviceObjects();
|
||||||
|
return ImGui_ImplDX12_CreateDeviceObjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D12HostDisplay::BeginPresent(bool frame_skip)
|
||||||
|
{
|
||||||
|
if (frame_skip || !m_swap_chain)
|
||||||
|
{
|
||||||
|
ImGui::EndFrame();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr std::array<float, 4> clear_color = {};
|
||||||
|
D3D12::Texture& swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer];
|
||||||
|
|
||||||
|
ID3D12GraphicsCommandList* cmdlist = g_d3d12_context->GetCommandList();
|
||||||
|
swap_chain_buf.TransitionToState(cmdlist, D3D12_RESOURCE_STATE_RENDER_TARGET);
|
||||||
|
cmdlist->ClearRenderTargetView(swap_chain_buf.GetRTVOrDSVDescriptor(), clear_color.data(), 0, nullptr);
|
||||||
|
cmdlist->OMSetRenderTargets(1, &swap_chain_buf.GetRTVOrDSVDescriptor().cpu_handle, FALSE, nullptr);
|
||||||
|
|
||||||
|
const D3D12_VIEWPORT vp{0.0f, 0.0f, static_cast<float>(m_window_info.surface_width), static_cast<float>(m_window_info.surface_height), 0.0f, 1.0f};
|
||||||
|
const D3D12_RECT scissor{0, 0, static_cast<LONG>(m_window_info.surface_width), static_cast<LONG>(m_window_info.surface_height)};
|
||||||
|
cmdlist->RSSetViewports(1, &vp);
|
||||||
|
cmdlist->RSSetScissorRects(1, &scissor);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D12HostDisplay::EndPresent()
|
||||||
|
{
|
||||||
|
ID3D12GraphicsCommandList* cmdlist = g_d3d12_context->GetCommandList();
|
||||||
|
ID3D12DescriptorHeap* heaps[] = {m_imgui_descriptor_heap.GetDescriptorHeap()};
|
||||||
|
cmdlist->SetDescriptorHeaps(std::size(heaps), heaps);
|
||||||
|
|
||||||
|
ImGui::Render();
|
||||||
|
ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), cmdlist);
|
||||||
|
|
||||||
|
D3D12::Texture& swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer];
|
||||||
|
m_current_swap_chain_buffer = ((m_current_swap_chain_buffer + 1) % static_cast<u32>(m_swap_chain_buffers.size()));
|
||||||
|
|
||||||
|
swap_chain_buf.TransitionToState(cmdlist, D3D12_RESOURCE_STATE_PRESENT);
|
||||||
|
g_d3d12_context->ExecuteCommandList(false);
|
||||||
|
|
||||||
|
const UINT vsync_rate = static_cast<UINT>(m_vsync_mode != VsyncMode::Off);
|
||||||
|
if (!m_vsync && m_using_allow_tearing)
|
||||||
|
m_swap_chain->Present(0, DXGI_PRESENT_ALLOW_TEARING);
|
||||||
|
else
|
||||||
|
m_swap_chain->Present(vsync_rate, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D12HostDisplay::SetGPUTimingEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
g_d3d12_context->SetEnableGPUTiming(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
float D3D12HostDisplay::GetAndResetAccumulatedGPUTime()
|
||||||
|
{
|
||||||
|
return g_d3d12_context->GetAndResetAccumulatedGPUTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
HostDisplay::AdapterAndModeList D3D12HostDisplay::StaticGetAdapterAndModeList()
|
||||||
|
{
|
||||||
|
ComPtr<IDXGIFactory> dxgi_factory;
|
||||||
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||||
|
HRESULT hr = CreateDXGIFactory(IID_PPV_ARGS(dxgi_factory.put()));
|
||||||
|
#else
|
||||||
|
HRESULT hr = CreateDXGIFactory2(0, IID_PPV_ARGS(dxgi_factory.put()));
|
||||||
|
#endif
|
||||||
|
if (FAILED(hr))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return GetAdapterAndModeList(dxgi_factory.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
HostDisplay::AdapterAndModeList D3D12HostDisplay::GetAdapterAndModeList(IDXGIFactory* dxgi_factory)
|
||||||
|
{
|
||||||
|
AdapterAndModeList adapter_info;
|
||||||
|
ComPtr<IDXGIAdapter> current_adapter;
|
||||||
|
while (SUCCEEDED(dxgi_factory->EnumAdapters(static_cast<UINT>(adapter_info.adapter_names.size()),
|
||||||
|
current_adapter.put())))
|
||||||
|
{
|
||||||
|
DXGI_ADAPTER_DESC adapter_desc;
|
||||||
|
std::string adapter_name;
|
||||||
|
if (SUCCEEDED(current_adapter->GetDesc(&adapter_desc)))
|
||||||
|
{
|
||||||
|
char adapter_name_buffer[128];
|
||||||
|
const int name_length = WideCharToMultiByte(CP_UTF8, 0, adapter_desc.Description,
|
||||||
|
static_cast<int>(std::wcslen(adapter_desc.Description)),
|
||||||
|
adapter_name_buffer, std::size(adapter_name_buffer), 0, nullptr);
|
||||||
|
if (name_length >= 0)
|
||||||
|
adapter_name.assign(adapter_name_buffer, static_cast<size_t>(name_length));
|
||||||
|
else
|
||||||
|
adapter_name.assign("(Unknown)");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
adapter_name.assign("(Unknown)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (adapter_info.fullscreen_modes.empty())
|
||||||
|
{
|
||||||
|
ComPtr<IDXGIOutput> output;
|
||||||
|
if (SUCCEEDED(current_adapter->EnumOutputs(0, &output)))
|
||||||
|
{
|
||||||
|
UINT num_modes = 0;
|
||||||
|
if (SUCCEEDED(output->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &num_modes, nullptr)))
|
||||||
|
{
|
||||||
|
std::vector<DXGI_MODE_DESC> modes(num_modes);
|
||||||
|
if (SUCCEEDED(output->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &num_modes, modes.data())))
|
||||||
|
{
|
||||||
|
for (const DXGI_MODE_DESC& mode : modes)
|
||||||
|
{
|
||||||
|
adapter_info.fullscreen_modes.push_back(StringUtil::StdStringFromFormat(
|
||||||
|
"%u x %u @ %f hz", mode.Width, mode.Height,
|
||||||
|
static_cast<float>(mode.RefreshRate.Numerator) / static_cast<float>(mode.RefreshRate.Denominator)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle duplicate adapter names
|
||||||
|
if (std::any_of(adapter_info.adapter_names.begin(), adapter_info.adapter_names.end(),
|
||||||
|
[&adapter_name](const std::string& other) { return (adapter_name == other); }))
|
||||||
|
{
|
||||||
|
std::string original_adapter_name = std::move(adapter_name);
|
||||||
|
|
||||||
|
u32 current_extra = 2;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
adapter_name = StringUtil::StdStringFromFormat("%s (%u)", original_adapter_name.c_str(), current_extra);
|
||||||
|
current_extra++;
|
||||||
|
} while (std::any_of(adapter_info.adapter_names.begin(), adapter_info.adapter_names.end(),
|
||||||
|
[&adapter_name](const std::string& other) { return (adapter_name == other); }));
|
||||||
|
}
|
||||||
|
|
||||||
|
adapter_info.adapter_names.push_back(std::move(adapter_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
return adapter_info;
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
/* PCSX2 - PS2 Emulator for PCs
|
||||||
|
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||||
|
*
|
||||||
|
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||||
|
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||||
|
* ation, either version 3 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||||
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE. See the GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||||
|
* If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/Pcsx2Defs.h"
|
||||||
|
#include "common/RedtapeWindows.h"
|
||||||
|
|
||||||
|
#include "common/D3D12/DescriptorHeapManager.h"
|
||||||
|
#include "common/D3D12/Texture.h"
|
||||||
|
#include "common/WindowInfo.h"
|
||||||
|
|
||||||
|
#include "HostDisplay.h"
|
||||||
|
|
||||||
|
#include <d3d12.h>
|
||||||
|
#include <dxgi.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
#include <wil/com.h>
|
||||||
|
|
||||||
|
class D3D12HostDisplay : public HostDisplay
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template <typename T>
|
||||||
|
using ComPtr = wil::com_ptr_nothrow<T>;
|
||||||
|
|
||||||
|
D3D12HostDisplay();
|
||||||
|
~D3D12HostDisplay();
|
||||||
|
|
||||||
|
RenderAPI GetRenderAPI() const override;
|
||||||
|
void* GetRenderDevice() const override;
|
||||||
|
void* GetRenderContext() const override;
|
||||||
|
void* GetRenderSurface() const override;
|
||||||
|
|
||||||
|
bool HasRenderDevice() const override;
|
||||||
|
bool HasRenderSurface() const override;
|
||||||
|
|
||||||
|
bool CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, VsyncMode vsync, bool threaded_presentation, bool debug_device) override;
|
||||||
|
bool InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device) override;
|
||||||
|
void DestroyRenderDevice() override;
|
||||||
|
|
||||||
|
bool MakeRenderContextCurrent() override;
|
||||||
|
bool DoneRenderContextCurrent() override;
|
||||||
|
|
||||||
|
bool ChangeRenderWindow(const WindowInfo& new_wi) override;
|
||||||
|
void ResizeRenderWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
|
||||||
|
bool SupportsFullscreen() const override;
|
||||||
|
bool IsFullscreen() override;
|
||||||
|
bool SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
|
||||||
|
AdapterAndModeList GetAdapterAndModeList() override;
|
||||||
|
void DestroyRenderSurface() override;
|
||||||
|
std::string GetDriverInfo() const override;
|
||||||
|
|
||||||
|
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, bool dynamic = false) override;
|
||||||
|
void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* texture_data, u32 texture_data_stride) override;
|
||||||
|
|
||||||
|
bool GetHostRefreshRate(float* refresh_rate) override;
|
||||||
|
|
||||||
|
void SetVSync(VsyncMode mode) override;
|
||||||
|
|
||||||
|
bool BeginPresent(bool frame_skip) override;
|
||||||
|
void EndPresent() override;
|
||||||
|
|
||||||
|
void SetGPUTimingEnabled(bool enabled) override;
|
||||||
|
float GetAndResetAccumulatedGPUTime() override;
|
||||||
|
|
||||||
|
static AdapterAndModeList StaticGetAdapterAndModeList();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static AdapterAndModeList GetAdapterAndModeList(IDXGIFactory* dxgi_factory);
|
||||||
|
|
||||||
|
bool CreateImGuiContext() override;
|
||||||
|
void DestroyImGuiContext() override;
|
||||||
|
bool UpdateImGuiFontTexture() override;
|
||||||
|
|
||||||
|
bool CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode);
|
||||||
|
bool CreateSwapChainRTV();
|
||||||
|
void DestroySwapChainRTVs();
|
||||||
|
|
||||||
|
ComPtr<IDXGIFactory> m_dxgi_factory;
|
||||||
|
ComPtr<IDXGISwapChain> m_swap_chain;
|
||||||
|
std::vector<D3D12::Texture> m_swap_chain_buffers;
|
||||||
|
u32 m_current_swap_chain_buffer = 0;
|
||||||
|
|
||||||
|
D3D12::DescriptorHeapManager m_imgui_descriptor_heap;
|
||||||
|
D3D12::DescriptorHandle m_imgui_descriptor_handle;
|
||||||
|
|
||||||
|
bool m_allow_tearing_supported = false;
|
||||||
|
bool m_using_allow_tearing = false;
|
||||||
|
bool m_vsync = true;
|
||||||
|
};
|
|
@ -37,6 +37,7 @@ const char* HostDisplay::RenderAPIToString(RenderAPI api)
|
||||||
#define CASE(x) case RenderAPI::x: return #x
|
#define CASE(x) case RenderAPI::x: return #x
|
||||||
CASE(None);
|
CASE(None);
|
||||||
CASE(D3D11);
|
CASE(D3D11);
|
||||||
|
CASE(D3D12);
|
||||||
CASE(Metal);
|
CASE(Metal);
|
||||||
CASE(Vulkan);
|
CASE(Vulkan);
|
||||||
CASE(OpenGL);
|
CASE(OpenGL);
|
||||||
|
@ -134,6 +135,7 @@ std::string HostDisplay::GetFullscreenModeString(u32 width, u32 height, float re
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include "Frontend/D3D11HostDisplay.h"
|
#include "Frontend/D3D11HostDisplay.h"
|
||||||
|
#include "Frontend/D3D12HostDisplay.h"
|
||||||
#endif
|
#endif
|
||||||
#include "GS/Renderers/Metal/GSMetalCPPAccessible.h"
|
#include "GS/Renderers/Metal/GSMetalCPPAccessible.h"
|
||||||
|
|
||||||
|
@ -144,6 +146,8 @@ std::unique_ptr<HostDisplay> HostDisplay::CreateDisplayForAPI(RenderAPI api)
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
case RenderAPI::D3D11:
|
case RenderAPI::D3D11:
|
||||||
return std::make_unique<D3D11HostDisplay>();
|
return std::make_unique<D3D11HostDisplay>();
|
||||||
|
case RenderAPI::D3D12:
|
||||||
|
return std::make_unique<D3D12HostDisplay>();
|
||||||
#endif
|
#endif
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
case HostDisplay::RenderAPI::Metal:
|
case HostDisplay::RenderAPI::Metal:
|
||||||
|
|
|
@ -47,6 +47,7 @@ public:
|
||||||
None,
|
None,
|
||||||
D3D11,
|
D3D11,
|
||||||
Metal,
|
Metal,
|
||||||
|
D3D12,
|
||||||
Vulkan,
|
Vulkan,
|
||||||
OpenGL,
|
OpenGL,
|
||||||
OpenGLES
|
OpenGLES
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\cubeb\cubeb\include;$(SolutionDir)3rdparty\cubeb\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\cubeb\cubeb\include;$(SolutionDir)3rdparty\cubeb\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\imgui\imgui;$(SolutionDir)3rdparty\imgui\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\imgui\imgui;$(SolutionDir)3rdparty\imgui\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\libzip;$(SolutionDir)3rdparty\libzip\libzip\lib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\libzip;$(SolutionDir)3rdparty\libzip\libzip\lib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\d3d12memalloc\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<ExceptionHandling>Async</ExceptionHandling>
|
<ExceptionHandling>Async</ExceptionHandling>
|
||||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||||
<PrecompiledHeaderFile>PrecompiledHeader.h</PrecompiledHeaderFile>
|
<PrecompiledHeaderFile>PrecompiledHeader.h</PrecompiledHeaderFile>
|
||||||
|
@ -321,6 +322,7 @@
|
||||||
<ClCompile Include="DEV9\net.cpp" />
|
<ClCompile Include="DEV9\net.cpp" />
|
||||||
<ClCompile Include="DEV9\Win32\tap-win32.cpp" />
|
<ClCompile Include="DEV9\Win32\tap-win32.cpp" />
|
||||||
<ClCompile Include="Frontend\D3D11HostDisplay.cpp" />
|
<ClCompile Include="Frontend\D3D11HostDisplay.cpp" />
|
||||||
|
<ClCompile Include="Frontend\D3D12HostDisplay.cpp" />
|
||||||
<ClCompile Include="Frontend\ImGuiManager.cpp" />
|
<ClCompile Include="Frontend\ImGuiManager.cpp" />
|
||||||
<ClCompile Include="Frontend\imgui_impl_vulkan.cpp" />
|
<ClCompile Include="Frontend\imgui_impl_vulkan.cpp" />
|
||||||
<ClCompile Include="Frontend\OpenGLHostDisplay.cpp" />
|
<ClCompile Include="Frontend\OpenGLHostDisplay.cpp" />
|
||||||
|
@ -783,6 +785,7 @@
|
||||||
<ClInclude Include="DEV9\Win32\pcap_io_win32_funcs.h" />
|
<ClInclude Include="DEV9\Win32\pcap_io_win32_funcs.h" />
|
||||||
<ClInclude Include="DEV9\Win32\tap.h" />
|
<ClInclude Include="DEV9\Win32\tap.h" />
|
||||||
<ClInclude Include="Frontend\D3D11HostDisplay.h" />
|
<ClInclude Include="Frontend\D3D11HostDisplay.h" />
|
||||||
|
<ClInclude Include="Frontend\D3D12HostDisplay.h" />
|
||||||
<ClInclude Include="Frontend\ImGuiManager.h" />
|
<ClInclude Include="Frontend\ImGuiManager.h" />
|
||||||
<ClInclude Include="Frontend\OpenGLHostDisplay.h" />
|
<ClInclude Include="Frontend\OpenGLHostDisplay.h" />
|
||||||
<ClInclude Include="Frontend\VulkanHostDisplay.h" />
|
<ClInclude Include="Frontend\VulkanHostDisplay.h" />
|
||||||
|
|
|
@ -1766,6 +1766,9 @@
|
||||||
<ClCompile Include="gui\IniInterface.cpp">
|
<ClCompile Include="gui\IniInterface.cpp">
|
||||||
<Filter>AppHost</Filter>
|
<Filter>AppHost</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="Frontend\D3D12HostDisplay.cpp">
|
||||||
|
<Filter>Host</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="Patch.h">
|
<ClInclude Include="Patch.h">
|
||||||
|
@ -2936,6 +2939,9 @@
|
||||||
<ClInclude Include="gui\IniInterface.h">
|
<ClInclude Include="gui\IniInterface.h">
|
||||||
<Filter>AppHost</Filter>
|
<Filter>AppHost</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="Frontend\D3D12HostDisplay.h">
|
||||||
|
<Filter>Host</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="windows\wxResources.rc">
|
<ResourceCompile Include="windows\wxResources.rc">
|
||||||
|
|
|
@ -47,6 +47,7 @@
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\simpleini\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\simpleini\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\sdl2\include;$(SolutionDir)3rdparty\sdl2\SDL\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\sdl2\include;$(SolutionDir)3rdparty\sdl2\SDL\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\libzip;$(SolutionDir)3rdparty\libzip\libzip\lib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\libzip;$(SolutionDir)3rdparty\libzip\libzip\lib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\d3d12memalloc\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<ExceptionHandling>Async</ExceptionHandling>
|
<ExceptionHandling>Async</ExceptionHandling>
|
||||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||||
<PrecompiledHeaderFile>PrecompiledHeader.h</PrecompiledHeaderFile>
|
<PrecompiledHeaderFile>PrecompiledHeader.h</PrecompiledHeaderFile>
|
||||||
|
@ -184,6 +185,7 @@
|
||||||
<ClCompile Include="DEV9\net.cpp" />
|
<ClCompile Include="DEV9\net.cpp" />
|
||||||
<ClCompile Include="DEV9\Win32\tap-win32.cpp" />
|
<ClCompile Include="DEV9\Win32\tap-win32.cpp" />
|
||||||
<ClCompile Include="Frontend\D3D11HostDisplay.cpp" />
|
<ClCompile Include="Frontend\D3D11HostDisplay.cpp" />
|
||||||
|
<ClCompile Include="Frontend\D3D12HostDisplay.cpp" />
|
||||||
<ClCompile Include="Frontend\GameList.cpp" />
|
<ClCompile Include="Frontend\GameList.cpp" />
|
||||||
<ClCompile Include="Frontend\ImGuiManager.cpp" />
|
<ClCompile Include="Frontend\ImGuiManager.cpp" />
|
||||||
<ClCompile Include="Frontend\imgui_impl_vulkan.cpp" />
|
<ClCompile Include="Frontend\imgui_impl_vulkan.cpp" />
|
||||||
|
@ -499,6 +501,7 @@
|
||||||
<ClInclude Include="DEV9\Win32\pcap_io_win32_funcs.h" />
|
<ClInclude Include="DEV9\Win32\pcap_io_win32_funcs.h" />
|
||||||
<ClInclude Include="DEV9\Win32\tap.h" />
|
<ClInclude Include="DEV9\Win32\tap.h" />
|
||||||
<ClInclude Include="Frontend\D3D11HostDisplay.h" />
|
<ClInclude Include="Frontend\D3D11HostDisplay.h" />
|
||||||
|
<ClInclude Include="Frontend\D3D12HostDisplay.h" />
|
||||||
<ClInclude Include="Frontend\GameList.h" />
|
<ClInclude Include="Frontend\GameList.h" />
|
||||||
<ClInclude Include="Frontend\ImGuiManager.h" />
|
<ClInclude Include="Frontend\ImGuiManager.h" />
|
||||||
<ClInclude Include="Frontend\INISettingsInterface.h" />
|
<ClInclude Include="Frontend\INISettingsInterface.h" />
|
||||||
|
|
|
@ -1251,6 +1251,9 @@
|
||||||
<Filter>System\Ps2\EmotionEngine\EE\Dynarec</Filter>
|
<Filter>System\Ps2\EmotionEngine\EE\Dynarec</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="GSDumpReplayer.cpp" />
|
<ClCompile Include="GSDumpReplayer.cpp" />
|
||||||
|
<ClCompile Include="Frontend\D3D12HostDisplay.cpp">
|
||||||
|
<Filter>Host</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="Patch.h">
|
<ClInclude Include="Patch.h">
|
||||||
|
@ -2065,6 +2068,9 @@
|
||||||
<Filter>System\Ps2\EmotionEngine\EE\Dynarec</Filter>
|
<Filter>System\Ps2\EmotionEngine\EE\Dynarec</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="GSDumpReplayer.h" />
|
<ClInclude Include="GSDumpReplayer.h" />
|
||||||
|
<ClInclude Include="Frontend\D3D12HostDisplay.h">
|
||||||
|
<Filter>Host</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="GS\GS.rc">
|
<ResourceCompile Include="GS\GS.rc">
|
||||||
|
|
Loading…
Reference in New Issue