GS: Unify D3D device creation paths

Also makes Vulkan the device for Intel Arc GPUs.
This commit is contained in:
Stenzek 2023-04-01 15:47:02 +10:00 committed by refractionpcsx2
parent 8989b69ce8
commit fcbc027abc
11 changed files with 408 additions and 539 deletions

View File

@ -125,7 +125,7 @@ bool Context::SupportsTextureFormat(DXGI_FORMAT format)
(support.Support1 & required) == required;
}
bool Context::Create(IDXGIFactory* dxgi_factory, u32 adapter_index, bool enable_debug_layer)
bool Context::Create(IDXGIFactory5* dxgi_factory, IDXGIAdapter1* adapter, bool enable_debug_layer)
{
pxAssertRel(!g_d3d12_context, "No context exists");
@ -133,7 +133,7 @@ bool Context::Create(IDXGIFactory* dxgi_factory, u32 adapter_index, bool enable_
return false;
g_d3d12_context.reset(new Context());
if (!g_d3d12_context->CreateDevice(dxgi_factory, adapter_index, enable_debug_layer) ||
if (!g_d3d12_context->CreateDevice(dxgi_factory, adapter, enable_debug_layer) ||
!g_d3d12_context->CreateCommandQueue() || !g_d3d12_context->CreateAllocator() ||
!g_d3d12_context->CreateFence() || !g_d3d12_context->CreateDescriptorHeaps() ||
!g_d3d12_context->CreateCommandLists() || !g_d3d12_context->CreateTimestampQuery() ||
@ -166,31 +166,9 @@ u32 Context::GetAdapterVendorID() const
return desc.VendorId;
}
bool Context::CreateDevice(IDXGIFactory* dxgi_factory, u32 adapter_index, bool enable_debug_layer)
bool Context::CreateDevice(IDXGIFactory5* dxgi_factory, IDXGIAdapter1* adapter, bool enable_debug_layer)
{
ComPtr<IDXGIAdapter> adapter;
HRESULT hr = dxgi_factory->EnumAdapters(adapter_index, &adapter);
if (FAILED(hr))
{
Console.Error("Adapter %u not found, using default", adapter_index);
adapter = nullptr;
}
else
{
DXGI_ADAPTER_DESC adapter_desc;
if (SUCCEEDED(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_buffer[name_length] = 0;
Console.WriteLn("D3D Adapter: %s", adapter_name_buffer);
}
}
}
HRESULT hr;
// Enabling the debug layer will fail if the Graphics Tools feature is not installed.
if (enable_debug_layer)
@ -208,18 +186,14 @@ bool Context::CreateDevice(IDXGIFactory* dxgi_factory, u32 adapter_index, bool e
}
// Create the actual device.
hr = s_d3d12_create_device(adapter.get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device));
hr = s_d3d12_create_device(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device));
if (FAILED(hr))
return false;
// get adapter
ComPtr<IDXGIFactory4> dxgi_factory4;
if (SUCCEEDED(dxgi_factory->QueryInterface<IDXGIFactory4>(dxgi_factory4.put())))
{
const LUID luid(m_device->GetAdapterLuid());
if (FAILED(dxgi_factory4->EnumAdapterByLuid(luid, IID_PPV_ARGS(m_adapter.put()))))
Console.Error("Failed to get lookup adapter by device LUID");
}
const LUID luid(m_device->GetAdapterLuid());
if (FAILED(dxgi_factory->EnumAdapterByLuid(luid, IID_PPV_ARGS(m_adapter.put()))))
Console.Error("Failed to get lookup adapter by device LUID");
if (enable_debug_layer)
{

View File

@ -22,6 +22,7 @@
#include <array>
#include <d3d12.h>
#include <dxgi1_5.h>
#include <memory>
#include <vector>
#include <wil/com.h>
@ -60,7 +61,7 @@ namespace D3D12
~Context();
/// Creates new device and context.
static bool Create(IDXGIFactory* dxgi_factory, u32 adapter_index, bool enable_debug_layer);
static bool Create(IDXGIFactory5* dxgi_factory, IDXGIAdapter1* adapter, bool enable_debug_layer);
/// Destroys active context.
static void Destroy();
@ -167,7 +168,7 @@ namespace D3D12
Context();
bool CreateDevice(IDXGIFactory* dxgi_factory, u32 adapter_index, bool enable_debug_layer);
bool CreateDevice(IDXGIFactory5* dxgi_factory, IDXGIAdapter1* adapter, bool enable_debug_layer);
bool CreateCommandQueue();
bool CreateAllocator();
bool CreateFence();

View File

@ -36,8 +36,8 @@
class D3D11HostDisplayTexture : public HostDisplayTexture
{
public:
D3D11HostDisplayTexture(wil::com_ptr_nothrow<ID3D11Texture2D> texture,
wil::com_ptr_nothrow<ID3D11ShaderResourceView> srv, u32 width, u32 height, bool dynamic)
D3D11HostDisplayTexture(wil::com_ptr_nothrow<ID3D11Texture2D> texture, wil::com_ptr_nothrow<ID3D11ShaderResourceView> srv, u32 width,
u32 height, bool dynamic)
: m_texture(std::move(texture))
, m_srv(std::move(srv))
, m_width(width)
@ -103,19 +103,18 @@ bool D3D11HostDisplay::HasSurface() const
return static_cast<bool>(m_swap_chain);
}
std::unique_ptr<HostDisplayTexture> D3D11HostDisplay::CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, bool dynamic /* = false */)
std::unique_ptr<HostDisplayTexture> D3D11HostDisplay::CreateTexture(
u32 width, u32 height, const void* data, u32 data_stride, bool dynamic /* = false */)
{
const CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_R8G8B8A8_UNORM, width, height, 1u, 1u,
D3D11_BIND_SHADER_RESOURCE, dynamic ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT,
dynamic ? D3D11_CPU_ACCESS_WRITE : 0, 1, 0, 0);
const CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_R8G8B8A8_UNORM, width, height, 1u, 1u, D3D11_BIND_SHADER_RESOURCE,
dynamic ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT, dynamic ? D3D11_CPU_ACCESS_WRITE : 0, 1, 0, 0);
const D3D11_SUBRESOURCE_DATA srd{data, data_stride, data_stride * height};
ComPtr<ID3D11Texture2D> texture;
HRESULT hr = m_device->CreateTexture2D(&desc, data ? &srd : nullptr, texture.addressof());
if (FAILED(hr))
return {};
const CD3D11_SHADER_RESOURCE_VIEW_DESC srv_desc(D3D11_SRV_DIMENSION_TEXTURE2D, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 1, 0,
1);
const CD3D11_SHADER_RESOURCE_VIEW_DESC srv_desc(D3D11_SRV_DIMENSION_TEXTURE2D, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 1, 0, 1);
ComPtr<ID3D11ShaderResourceView> srv;
hr = m_device->CreateShaderResourceView(texture.get(), &srv_desc, srv.addressof());
if (FAILED(hr))
@ -124,14 +123,15 @@ std::unique_ptr<HostDisplayTexture> D3D11HostDisplay::CreateTexture(u32 width, u
return std::make_unique<D3D11HostDisplayTexture>(std::move(texture), std::move(srv), width, height, dynamic);
}
void D3D11HostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* texture_data, u32 texture_data_stride)
void D3D11HostDisplay::UpdateTexture(
HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* texture_data, u32 texture_data_stride)
{
D3D11HostDisplayTexture* d3d11_texture = static_cast<D3D11HostDisplayTexture*>(texture);
if (!d3d11_texture->IsDynamic())
{
const CD3D11_BOX dst_box(x, y, 0, x + width, y + height, 1);
m_context->UpdateSubresource(d3d11_texture->GetD3DTexture(), 0, &dst_box, texture_data, texture_data_stride,
texture_data_stride * height);
m_context->UpdateSubresource(
d3d11_texture->GetD3DTexture(), 0, &dst_box, texture_data, texture_data_stride, texture_data_stride * height);
}
else
{
@ -167,10 +167,9 @@ bool D3D11HostDisplay::GetHostRefreshRate(float* refresh_rate)
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);
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;
}
}
@ -189,52 +188,21 @@ bool D3D11HostDisplay::CreateDevice(const WindowInfo& wi, VsyncMode vsync)
if (EmuConfig.GS.UseDebugDevice)
create_flags |= D3D11_CREATE_DEVICE_DEBUG;
ComPtr<IDXGIFactory> temp_dxgi_factory;
HRESULT hr = CreateDXGIFactory(IID_PPV_ARGS(temp_dxgi_factory.put()));
if (FAILED(hr))
{
Console.Error("Failed to create DXGI factory: 0x%08X", hr);
m_dxgi_factory = D3D::CreateFactory(EmuConfig.GS.UseDebugDevice);
if (!m_dxgi_factory)
return false;
}
u32 adapter_index;
if (!EmuConfig.GS.Adapter.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 (EmuConfig.GS.Adapter == 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)", EmuConfig.GS.Adapter.c_str(),
adapter_info.adapter_names[0].c_str());
adapter_index = 0;
}
}
else
{
Console.WriteLn("No adapter selected, using first.");
adapter_index = 0;
}
ComPtr<IDXGIAdapter> dxgi_adapter;
hr = temp_dxgi_factory->EnumAdapters(adapter_index, dxgi_adapter.put());
if (FAILED(hr))
Console.Warning("Failed to enumerate adapter %u, using default", adapter_index);
ComPtr<IDXGIAdapter1> dxgi_adapter = D3D::GetAdapterByName(m_dxgi_factory.get(), EmuConfig.GS.Adapter);
static constexpr std::array<D3D_FEATURE_LEVEL, 3> requested_feature_levels = {
{D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0}};
hr =
D3D11CreateDevice(dxgi_adapter.get(), dxgi_adapter ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE, nullptr,
create_flags, requested_feature_levels.data(), static_cast<UINT>(requested_feature_levels.size()),
D3D11_SDK_VERSION, m_device.put(), nullptr, m_context.put());
HRESULT hr = D3D11CreateDevice(dxgi_adapter.get(), dxgi_adapter ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE, nullptr,
create_flags, requested_feature_levels.data(), static_cast<UINT>(requested_feature_levels.size()), D3D11_SDK_VERSION,
m_device.put(), nullptr, m_context.put());
// we re-grab these later, see below
dxgi_adapter.reset();
temp_dxgi_factory.reset();
if (FAILED(hr))
{
@ -252,39 +220,15 @@ bool D3D11HostDisplay::CreateDevice(const WindowInfo& wi, VsyncMode vsync)
}
}
// we need the specific factory for the device, otherwise MakeWindowAssociation() is flaky.
ComPtr<IDXGIDevice> dxgi_device;
if (!m_device.try_query_to(&dxgi_device) || FAILED(dxgi_device->GetParent(IID_PPV_ARGS(dxgi_adapter.put()))) ||
FAILED(dxgi_adapter->GetParent(IID_PPV_ARGS(m_dxgi_factory.put()))))
{
Console.Warning("Failed to get parent adapter/device/factory");
return false;
}
if (m_device.try_query_to(&dxgi_device) && SUCCEEDED(dxgi_device->GetParent(IID_PPV_ARGS(dxgi_adapter.put()))))
Console.WriteLn(fmt::format("D3D Adapter: {}", D3D::GetAdapterName(dxgi_adapter.get())));
else
Console.Error("Failed to obtain D3D adapter name.");
DXGI_ADAPTER_DESC adapter_desc;
if (SUCCEEDED(dxgi_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, sizeof(adapter_name_buffer), 0, nullptr);
if (name_length >= 0)
{
adapter_name_buffer[name_length] = 0;
Console.WriteLn("D3D Adapter: %s", adapter_name_buffer);
}
}
m_allow_tearing_supported = false;
ComPtr<IDXGIFactory5> dxgi_factory5;
if (m_dxgi_factory.try_query_to(&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);
}
BOOL allow_tearing_supported = false;
hr = m_dxgi_factory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing_supported, sizeof(allow_tearing_supported));
m_allow_tearing_supported = (SUCCEEDED(hr) && allow_tearing_supported == TRUE);
m_window_info = wi;
m_vsync_mode = vsync;
@ -323,33 +267,36 @@ bool D3D11HostDisplay::CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode)
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;
DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {};
swap_chain_desc.Width = width;
swap_chain_desc.Height = 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.OutputWindow = window_hwnd;
swap_chain_desc.Windowed = TRUE;
swap_chain_desc.SwapEffect = m_using_flip_model_swap_chain ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD;
m_using_allow_tearing = (m_allow_tearing_supported && m_using_flip_model_swap_chain && !fullscreen_mode);
if (m_using_allow_tearing)
swap_chain_desc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
DXGI_SWAP_CHAIN_FULLSCREEN_DESC fs_desc = {};
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;
swap_chain_desc.Width = fullscreen_mode->Width;
swap_chain_desc.Height = fullscreen_mode->Height;
fs_desc.RefreshRate = fullscreen_mode->RefreshRate;
fs_desc.ScanlineOrdering = fullscreen_mode->ScanlineOrdering;
fs_desc.Scaling = fullscreen_mode->Scaling;
fs_desc.Windowed = FALSE;
}
Console.WriteLn("Creating a %dx%d %s %s swap chain", swap_chain_desc.BufferDesc.Width,
swap_chain_desc.BufferDesc.Height, m_using_flip_model_swap_chain ? "flip-discard" : "discard",
swap_chain_desc.Windowed ? "windowed" : "full-screen");
Console.WriteLn("Creating a %dx%d %s %s swap chain", swap_chain_desc.Width, swap_chain_desc.Height,
m_using_flip_model_swap_chain ? "flip-discard" : "discard", fullscreen_mode ? "full-screen" : "windowed");
HRESULT hr = m_dxgi_factory->CreateSwapChain(m_device.get(), &swap_chain_desc, m_swap_chain.put());
HRESULT hr = m_dxgi_factory->CreateSwapChainForHwnd(
m_device.get(), window_hwnd, &swap_chain_desc, fullscreen_mode ? &fs_desc : nullptr, nullptr, m_swap_chain.put());
if (FAILED(hr) && m_using_flip_model_swap_chain)
{
Console.Warning("Failed to create a flip-discard swap chain, trying discard.");
@ -358,22 +305,18 @@ bool D3D11HostDisplay::CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode)
m_using_flip_model_swap_chain = false;
m_using_allow_tearing = false;
hr = m_dxgi_factory->CreateSwapChain(m_device.get(), &swap_chain_desc, m_swap_chain.put());
hr = m_dxgi_factory->CreateSwapChainForHwnd(
m_device.get(), window_hwnd, &swap_chain_desc, fullscreen_mode ? &fs_desc : nullptr, nullptr, m_swap_chain.put());
if (FAILED(hr))
{
Console.Error("CreateSwapChain failed: 0x%08X", hr);
Console.Error("CreateSwapChainForHwnd failed: 0x%08X", hr);
return false;
}
}
ComPtr<IDXGIFactory> dxgi_factory;
hr = m_swap_chain->GetParent(IID_PPV_ARGS(dxgi_factory.put()));
if (SUCCEEDED(hr))
{
hr = dxgi_factory->MakeWindowAssociation(swap_chain_desc.OutputWindow, DXGI_MWA_NO_WINDOW_CHANGES);
if (FAILED(hr))
Console.Warning("MakeWindowAssociation() to disable ALT+ENTER failed");
}
hr = m_dxgi_factory->MakeWindowAssociation(window_hwnd, DXGI_MWA_NO_WINDOW_CHANGES);
if (FAILED(hr))
Console.Warning("MakeWindowAssociation() to disable ALT+ENTER failed");
return CreateSwapChainRTV();
}
@ -391,8 +334,7 @@ bool D3D11HostDisplay::CreateSwapChainRTV()
D3D11_TEXTURE2D_DESC backbuffer_desc;
backbuffer->GetDesc(&backbuffer_desc);
CD3D11_RENDER_TARGET_VIEW_DESC rtv_desc(D3D11_RTV_DIMENSION_TEXTURE2D, backbuffer_desc.Format, 0, 0,
backbuffer_desc.ArraySize);
CD3D11_RENDER_TARGET_VIEW_DESC rtv_desc(D3D11_RTV_DIMENSION_TEXTURE2D, backbuffer_desc.Format, 0, 0, backbuffer_desc.ArraySize);
hr = m_device->CreateRenderTargetView(backbuffer.get(), &rtv_desc, m_swap_chain_rtv.put());
if (FAILED(hr))
{
@ -408,11 +350,10 @@ bool D3D11HostDisplay::CreateSwapChainRTV()
{
BOOL fullscreen = FALSE;
DXGI_SWAP_CHAIN_DESC desc;
if (SUCCEEDED(m_swap_chain->GetFullscreenState(&fullscreen, nullptr)) && fullscreen &&
SUCCEEDED(m_swap_chain->GetDesc(&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);
m_window_info.surface_refresh_rate =
static_cast<float>(desc.BufferDesc.RefreshRate.Numerator) / static_cast<float>(desc.BufferDesc.RefreshRate.Denominator);
}
else
{
@ -501,8 +442,7 @@ void D3D11HostDisplay::ResizeWindow(s32 new_window_width, s32 new_window_height,
m_swap_chain_rtv.reset();
HRESULT hr = m_swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN,
m_using_allow_tearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0);
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);
@ -681,7 +621,8 @@ void D3D11HostDisplay::PopTimestampQuery()
while (m_waiting_timestamp_queries > 0)
{
D3D11_QUERY_DATA_TIMESTAMP_DISJOINT disjoint;
const HRESULT disjoint_hr = m_context->GetData(m_timestamp_queries[m_read_timestamp_query][0].get(), &disjoint, sizeof(disjoint), D3D11_ASYNC_GETDATA_DONOTFLUSH);
const HRESULT disjoint_hr = m_context->GetData(
m_timestamp_queries[m_read_timestamp_query][0].get(), &disjoint, sizeof(disjoint), D3D11_ASYNC_GETDATA_DONOTFLUSH);
if (disjoint_hr != S_OK)
break;
@ -696,11 +637,14 @@ void D3D11HostDisplay::PopTimestampQuery()
else
{
u64 start = 0, end = 0;
const HRESULT start_hr = m_context->GetData(m_timestamp_queries[m_read_timestamp_query][1].get(), &start, sizeof(start), D3D11_ASYNC_GETDATA_DONOTFLUSH);
const HRESULT end_hr = m_context->GetData(m_timestamp_queries[m_read_timestamp_query][2].get(), &end, sizeof(end), D3D11_ASYNC_GETDATA_DONOTFLUSH);
const HRESULT start_hr = m_context->GetData(
m_timestamp_queries[m_read_timestamp_query][1].get(), &start, sizeof(start), D3D11_ASYNC_GETDATA_DONOTFLUSH);
const HRESULT end_hr =
m_context->GetData(m_timestamp_queries[m_read_timestamp_query][2].get(), &end, sizeof(end), D3D11_ASYNC_GETDATA_DONOTFLUSH);
if (start_hr == S_OK && end_hr == S_OK)
{
m_accumulated_gpu_time += static_cast<float>(static_cast<double>(end - start) / (static_cast<double>(disjoint.Frequency) / 1000.0));
m_accumulated_gpu_time +=
static_cast<float>(static_cast<double>(end - start) / (static_cast<double>(disjoint.Frequency) / 1000.0));
m_read_timestamp_query = (m_read_timestamp_query + 1) % NUM_TIMESTAMP_QUERIES;
m_waiting_timestamp_queries--;
}
@ -753,77 +697,38 @@ float D3D11HostDisplay::GetAndResetAccumulatedGPUTime()
HostDisplay::AdapterAndModeList D3D11HostDisplay::StaticGetAdapterAndModeList()
{
ComPtr<IDXGIFactory> dxgi_factory;
const HRESULT hr = CreateDXGIFactory(IID_PPV_ARGS(dxgi_factory.put()));
if (FAILED(hr))
auto factory = D3D::CreateFactory(false);
if (!factory)
return {};
return GetAdapterAndModeList(dxgi_factory.get());
return GetAdapterAndModeList(factory.get());
}
HostDisplay::AdapterAndModeList D3D11HostDisplay::GetAdapterAndModeList(IDXGIFactory* dxgi_factory)
HostDisplay::AdapterAndModeList D3D11HostDisplay::GetAdapterAndModeList(IDXGIFactory5* 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, sizeof(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)");
}
adapter_info.adapter_names = D3D::GetAdapterNames(dxgi_factory);
if (adapter_info.fullscreen_modes.empty())
auto adapter = D3D::GetChosenOrFirstAdapter(dxgi_factory, EmuConfig.GS.Adapter);
if (adapter)
{
ComPtr<IDXGIOutput> output;
if (SUCCEEDED(adapter->EnumOutputs(0, &output)))
{
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)))
{
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())))
{
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)
{
for (const DXGI_MODE_DESC& mode : modes)
{
adapter_info.fullscreen_modes.push_back(GetFullscreenModeString(
mode.Width, mode.Height,
static_cast<float>(mode.RefreshRate.Numerator) / static_cast<float>(mode.RefreshRate.Denominator)));
}
adapter_info.fullscreen_modes.push_back(GetFullscreenModeString(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;
@ -833,4 +738,3 @@ HostDisplay::AdapterAndModeList D3D11HostDisplay::GetAdapterAndModeList()
{
return GetAdapterAndModeList(m_dxgi_factory.get());
}

View File

@ -20,7 +20,7 @@
#include "common/WindowInfo.h"
#include <array>
#include <d3d11.h>
#include <dxgi.h>
#include <dxgi1_5.h>
#include <memory>
#include <string>
#include <string_view>
@ -77,7 +77,7 @@ protected:
static constexpr u32 DISPLAY_CONSTANT_BUFFER_SIZE = 16;
static constexpr u8 NUM_TIMESTAMP_QUERIES = 5;
static AdapterAndModeList GetAdapterAndModeList(IDXGIFactory* dxgi_factory);
static AdapterAndModeList GetAdapterAndModeList(IDXGIFactory5* dxgi_factory);
bool CreateImGuiContext() override;
void DestroyImGuiContext() override;
@ -94,8 +94,8 @@ protected:
ComPtr<ID3D11Device> m_device;
ComPtr<ID3D11DeviceContext> m_context;
ComPtr<IDXGIFactory> m_dxgi_factory;
ComPtr<IDXGISwapChain> m_swap_chain;
ComPtr<IDXGIFactory5> m_dxgi_factory;
ComPtr<IDXGISwapChain1> m_swap_chain;
ComPtr<ID3D11RenderTargetView> m_swap_chain_rtv;
bool m_allow_tearing_supported = false;

View File

@ -122,10 +122,9 @@ bool D3D12HostDisplay::GetHostRefreshRate(float* refresh_rate)
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);
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;
}
}
@ -140,56 +139,19 @@ void D3D12HostDisplay::SetVSync(VsyncMode mode)
bool D3D12HostDisplay::CreateDevice(const WindowInfo& wi, VsyncMode vsync)
{
ComPtr<IDXGIFactory> temp_dxgi_factory;
HRESULT hr = CreateDXGIFactory(IID_PPV_ARGS(temp_dxgi_factory.put()));
if (FAILED(hr))
{
Console.Error("Failed to create DXGI factory: 0x%08X", hr);
return false;
}
u32 adapter_index;
if (!EmuConfig.GS.Adapter.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 (EmuConfig.GS.Adapter == 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)", EmuConfig.GS.Adapter.c_str(), 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, EmuConfig.GS.UseDebugDevice))
m_dxgi_factory = D3D::CreateFactory(EmuConfig.GS.UseDebugDevice);
if (!m_dxgi_factory)
return false;
if (FAILED(hr))
{
Console.Error("Failed to create D3D device: 0x%08X", hr);
ComPtr<IDXGIAdapter1> dxgi_adapter = D3D::GetAdapterByName(m_dxgi_factory.get(), EmuConfig.GS.Adapter);
if (!D3D12::Context::Create(m_dxgi_factory.get(), dxgi_adapter.get(), EmuConfig.GS.UseDebugDevice))
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);
}
BOOL allow_tearing_supported = false;
HRESULT hr =
m_dxgi_factory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing_supported, sizeof(allow_tearing_supported));
m_allow_tearing_supported = (SUCCEEDED(hr) && allow_tearing_supported == TRUE);
m_window_info = wi;
m_vsync_mode = vsync;
@ -226,40 +188,43 @@ bool D3D12HostDisplay::CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode)
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;
DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {};
swap_chain_desc.Width = width;
swap_chain_desc.Height = 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.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;
DXGI_SWAP_CHAIN_FULLSCREEN_DESC fs_desc = {};
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;
swap_chain_desc.Width = fullscreen_mode->Width;
swap_chain_desc.Height = fullscreen_mode->Height;
fs_desc.RefreshRate = fullscreen_mode->RefreshRate;
fs_desc.ScanlineOrdering = fullscreen_mode->ScanlineOrdering;
fs_desc.Scaling = fullscreen_mode->Scaling;
fs_desc.Windowed = FALSE;
}
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");
DevCon.WriteLn(
"Creating a %dx%d %s swap chain", swap_chain_desc.Width, swap_chain_desc.Height, fullscreen_mode ? "full-screen" : "windowed");
HRESULT hr =
m_dxgi_factory->CreateSwapChain(g_d3d12_context->GetCommandQueue(), &swap_chain_desc, m_swap_chain.put());
HRESULT hr = m_dxgi_factory->CreateSwapChainForHwnd(g_d3d12_context->GetCommandQueue(), window_hwnd, &swap_chain_desc,
fullscreen_mode ? &fs_desc : nullptr, nullptr, m_swap_chain.put());
if (FAILED(hr))
{
Console.Error("CreateSwapChain failed: 0x%08X", hr);
Console.Error("CreateSwapChainForHwnd failed: 0x%08X", hr);
return false;
}
hr = m_dxgi_factory->MakeWindowAssociation(swap_chain_desc.OutputWindow, DXGI_MWA_NO_WINDOW_CHANGES);
hr = m_dxgi_factory->MakeWindowAssociation(window_hwnd, DXGI_MWA_NO_WINDOW_CHANGES);
if (FAILED(hr))
Console.Warning("MakeWindowAssociation() to disable ALT+ENTER failed");
@ -301,11 +266,10 @@ bool D3D12HostDisplay::CreateSwapChainRTV()
{
BOOL fullscreen = FALSE;
DXGI_SWAP_CHAIN_DESC desc;
if (SUCCEEDED(m_swap_chain->GetFullscreenState(&fullscreen, nullptr)) && fullscreen &&
SUCCEEDED(m_swap_chain->GetDesc(&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);
m_window_info.surface_refresh_rate =
static_cast<float>(desc.BufferDesc.RefreshRate.Numerator) / static_cast<float>(desc.BufferDesc.RefreshRate.Denominator);
}
else
{
@ -402,8 +366,7 @@ void D3D12HostDisplay::ResizeWindow(s32 new_window_width, s32 new_window_height,
DestroySwapChainRTVs();
HRESULT hr = m_swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN,
m_using_allow_tearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0);
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);
@ -529,7 +492,8 @@ HostDisplay::PresentResult D3D12HostDisplay::BeginPresent(bool frame_skip)
cmdlist->ClearRenderTargetView(swap_chain_buf.GetWriteDescriptor(), clear_color.data(), 0, nullptr);
cmdlist->OMSetRenderTargets(1, &swap_chain_buf.GetWriteDescriptor().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_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);
@ -571,77 +535,38 @@ float D3D12HostDisplay::GetAndResetAccumulatedGPUTime()
HostDisplay::AdapterAndModeList D3D12HostDisplay::StaticGetAdapterAndModeList()
{
ComPtr<IDXGIFactory> dxgi_factory;
const HRESULT hr = CreateDXGIFactory(IID_PPV_ARGS(dxgi_factory.put()));
if (FAILED(hr))
auto factory = D3D::CreateFactory(false);
if (!factory)
return {};
return GetAdapterAndModeList(dxgi_factory.get());
return GetAdapterAndModeList(factory.get());
}
HostDisplay::AdapterAndModeList D3D12HostDisplay::GetAdapterAndModeList(IDXGIFactory* dxgi_factory)
HostDisplay::AdapterAndModeList D3D12HostDisplay::GetAdapterAndModeList(IDXGIFactory5* 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)");
}
adapter_info.adapter_names = D3D::GetAdapterNames(dxgi_factory);
if (adapter_info.fullscreen_modes.empty())
auto adapter = D3D::GetChosenOrFirstAdapter(dxgi_factory, EmuConfig.GS.Adapter);
if (adapter)
{
ComPtr<IDXGIOutput> output;
if (SUCCEEDED(adapter->EnumOutputs(0, &output)))
{
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)))
{
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())))
{
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)
{
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)));
}
adapter_info.fullscreen_modes.push_back(GetFullscreenModeString(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;

View File

@ -25,7 +25,7 @@
#include "HostDisplay.h"
#include <d3d12.h>
#include <dxgi.h>
#include <dxgi1_5.h>
#include <memory>
#include <string>
#include <string_view>
@ -80,7 +80,7 @@ public:
static AdapterAndModeList StaticGetAdapterAndModeList();
protected:
static AdapterAndModeList GetAdapterAndModeList(IDXGIFactory* dxgi_factory);
static AdapterAndModeList GetAdapterAndModeList(IDXGIFactory5* dxgi_factory);
bool CreateImGuiContext() override;
void DestroyImGuiContext() override;
@ -90,8 +90,8 @@ protected:
bool CreateSwapChainRTV();
void DestroySwapChainRTVs();
ComPtr<IDXGIFactory> m_dxgi_factory;
ComPtr<IDXGISwapChain> m_swap_chain;
ComPtr<IDXGIFactory5> m_dxgi_factory;
ComPtr<IDXGISwapChain1> m_swap_chain;
std::vector<D3D12::Texture> m_swap_chain_buffers;
u32 m_current_swap_chain_buffer = 0;

View File

@ -129,24 +129,6 @@ void GSclose()
static RenderAPI GetAPIForRenderer(GSRendererType renderer)
{
#if defined(_WIN32)
// On Windows, we use DX11 for software, since it's always available.
constexpr RenderAPI default_api = RenderAPI::D3D11;
#elif defined(__APPLE__)
// For Macs, default to Metal.
constexpr RenderAPI default_api = RenderAPI::Metal;
#else
// For Linux, default to OpenGL (because of hardware compatibility), if we
// have it, otherwise Vulkan (if we have it).
#if defined(ENABLE_OPENGL)
constexpr RenderAPI default_api = RenderAPI::OpenGL;
#elif defined(ENABLE_VULKAN)
constexpr RenderAPI default_api = RenderAPI::Vulkan;
#else
constexpr RenderAPI default_api = RenderAPI::None;
#endif
#endif
switch (renderer)
{
case GSRendererType::OGL:
@ -169,7 +151,7 @@ static RenderAPI GetAPIForRenderer(GSRendererType renderer)
#endif
default:
return default_api;
return GetAPIForRenderer(GSUtil::GetPreferredRenderer());
}
}

View File

@ -203,25 +203,14 @@ GSRendererType GSUtil::GetPreferredRenderer()
// Mac: Prefer Metal hardware.
return GSRendererType::Metal;
#elif defined(_WIN32)
const u8 preferred = D3D::ShouldPreferRenderer();
#if defined(ENABLE_VULKAN)
if (preferred == D3D::Renderer::Vulkan)
return GSRendererType::VK;
#endif
#if defined(ENABLE_OPENGL)
if (preferred == D3D::Renderer::OpenGL)
return GSRendererType::OGL;
#endif
if (preferred == D3D::Renderer::Direct3D12)
return GSRendererType::DX12;
return GSRendererType::DX11;
// Use D3D device info to select renderer.
return D3D::GetPreferredRenderer();
#else
// Linux: Prefer GL/Vulkan, whatever is available.
#if defined(ENABLE_OPENGL)
return GSRendererType::OGL;
#elif defined(ENABLE_VULKAN)
return GSRendererType::Vulkan;
return GSRendererType::VK;
#else
return GSRendererType::SW;
#endif

View File

@ -17,194 +17,278 @@
#include "GS/Renderers/DX11/D3D.h"
#include "GS/GSExtra.h"
#include "common/Console.h"
#include "common/StringUtil.h"
#include <d3d11.h>
#include "fmt/format.h"
namespace D3D
wil::com_ptr_nothrow<IDXGIFactory5> D3D::CreateFactory(bool debug)
{
wil::com_ptr_nothrow<IDXGIFactory2> CreateFactory(bool debug)
UINT flags = 0;
if (debug)
flags |= DXGI_CREATE_FACTORY_DEBUG;
wil::com_ptr_nothrow<IDXGIFactory5> factory;
const HRESULT hr = CreateDXGIFactory2(flags, IID_PPV_ARGS(factory.put()));
if (FAILED(hr))
Console.Error("D3D: Failed to create DXGI factory: %08X", hr);
return factory;
}
static std::string FixupDuplicateAdapterNames(const std::vector<std::string>& adapter_names, std::string adapter_name)
{
if (std::any_of(adapter_names.begin(), adapter_names.end(),
[&adapter_name](const std::string& other) { return (adapter_name == other); }))
{
UINT flags = 0;
if (debug)
flags |= DXGI_CREATE_FACTORY_DEBUG;
std::string original_adapter_name = std::move(adapter_name);
// we use CreateDXGIFactory2 because we assume at least windows 8.1 anyway
wil::com_ptr_nothrow<IDXGIFactory2> factory;
HRESULT hr = CreateDXGIFactory2(flags, IID_PPV_ARGS(factory.put()));
// if we failed to create a factory with debug support
// try one without
if (FAILED(hr) && debug)
u32 current_extra = 2;
do
{
fprintf(stderr, "D3D: failed to create debug dxgi factory, trying without debugging\n");
hr = CreateDXGIFactory2(0, IID_PPV_ARGS(factory.put()));;
}
adapter_name = StringUtil::StdStringFromFormat("%s (%u)", original_adapter_name.c_str(), current_extra);
current_extra++;
} while (std::any_of(adapter_names.begin(), adapter_names.end(),
[&adapter_name](const std::string& other) { return (adapter_name == other); }));
}
return adapter_name;
}
std::vector<std::string> D3D::GetAdapterNames(IDXGIFactory5* factory)
{
std::vector<std::string> adapter_names;
wil::com_ptr_nothrow<IDXGIAdapter1> adapter;
for (u32 index = 0;; index++)
{
const HRESULT hr = factory->EnumAdapters1(index, adapter.put());
if (hr == DXGI_ERROR_NOT_FOUND)
break;
if (FAILED(hr))
{
fprintf(stderr, "D3D: failed to create dxgi factory\n"
"check that your system meets our minimum requirements:\n"
"https://github.com/PCSX2/pcsx2#system-requirements\n");
Console.Error(fmt::format("IDXGIFactory2::EnumAdapters() returned %08X", hr));
continue;
}
return factory;
adapter_names.push_back(FixupDuplicateAdapterNames(adapter_names, GetAdapterName(adapter.get())));
}
wil::com_ptr_nothrow<IDXGIAdapter1> GetAdapterFromIndex(IDXGIFactory2* factory, int index)
{
ASSERT(factory);
return adapter_names;
}
wil::com_ptr_nothrow<IDXGIAdapter1> adapter;
if (index < 0 || factory->EnumAdapters1(index, adapter.put()) == DXGI_ERROR_NOT_FOUND)
wil::com_ptr_nothrow<IDXGIAdapter1> D3D::GetAdapterByName(IDXGIFactory5* factory, const std::string_view& name)
{
if (name.empty())
return {};
// This might seem a bit odd to cache the names.. but there's a method to the madness.
// We might have two GPUs with the same name... :)
std::vector<std::string> adapter_names;
wil::com_ptr_nothrow<IDXGIAdapter1> adapter;
for (u32 index = 0;; index++)
{
const HRESULT hr = factory->EnumAdapters1(index, adapter.put());
if (hr == DXGI_ERROR_NOT_FOUND)
break;
if (FAILED(hr))
{
// try index 0 (default adapter)
if (index >= 0)
fprintf(stderr, "D3D: adapter not found, falling back to the default\n");
if (FAILED(factory->EnumAdapters1(0, adapter.put())))
{
// either there are no adapters connected or something major is wrong with the system
fprintf(stderr, "D3D: failed to EnumAdapters\n");
}
Console.Error(fmt::format("IDXGIFactory2::EnumAdapters() returned %08X", hr));
continue;
}
return adapter;
std::string adapter_name = FixupDuplicateAdapterNames(adapter_names, GetAdapterName(adapter.get()));
if (adapter_name == name)
{
Console.WriteLn(fmt::format("D3D: Found adapter '{}'", adapter_name));
return adapter;
}
adapter_names.push_back(std::move(adapter_name));
}
std::string GetDriverVersionFromLUID(const LUID& luid)
{
std::string ret;
Console.Warning(fmt::format("Adapter '{}' not found.", name));
return {};
}
HKEY hKey;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\DirectX", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
wil::com_ptr_nothrow<IDXGIAdapter1> D3D::GetFirstAdapter(IDXGIFactory5* factory)
{
wil::com_ptr_nothrow<IDXGIAdapter1> adapter;
HRESULT hr = factory->EnumAdapters1(0, adapter.put());
if (FAILED(hr))
Console.Error(fmt::format("IDXGIFactory2::EnumAdapters() for first adapter returned %08X", hr));
return adapter;
}
wil::com_ptr_nothrow<IDXGIAdapter1> D3D::GetChosenOrFirstAdapter(IDXGIFactory5* factory, const std::string_view& name)
{
wil::com_ptr_nothrow<IDXGIAdapter1> adapter = GetAdapterByName(factory, name);
if (!adapter)
adapter = GetFirstAdapter(factory);
return adapter;
}
std::string D3D::GetAdapterName(IDXGIAdapter1* adapter)
{
std::string ret;
DXGI_ADAPTER_DESC1 desc;
HRESULT hr = adapter->GetDesc1(&desc);
if (SUCCEEDED(hr))
{
ret = StringUtil::WideStringToUTF8String(desc.Description);
}
else
{
Console.Error(fmt::format("IDXGIAdapter1::GetDesc() returned {:08X}", hr));
}
if (ret.empty())
ret = "(Unknown)";
return ret;
}
std::string D3D::GetDriverVersionFromLUID(const LUID& luid)
{
std::string ret;
HKEY hKey;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\DirectX", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
{
DWORD max_key_len = 0, adapter_count = 0;
if (RegQueryInfoKeyW(hKey, nullptr, nullptr, nullptr, &adapter_count, &max_key_len, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr) == ERROR_SUCCESS)
{
DWORD max_key_len = 0, adapter_count = 0;
if (RegQueryInfoKeyW(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)
{
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 (RegEnumKeyExW(hKey, i, current_name.data(), &subKeyLength, nullptr, nullptr, nullptr, nullptr) ==
ERROR_SUCCESS)
{
DWORD subKeyLength = static_cast<DWORD>(current_name.size());
if (RegEnumKeyExW(hKey, i, current_name.data(), &subKeyLength, nullptr, nullptr, nullptr,
nullptr) == ERROR_SUCCESS)
LUID current_luid = {};
DWORD current_luid_size = sizeof(uint64_t);
if (RegGetValueW(hKey, current_name.data(), L"AdapterLuid", RRF_RT_QWORD, nullptr, &current_luid,
&current_luid_size) == ERROR_SUCCESS &&
current_luid.HighPart == luid.HighPart && current_luid.LowPart == luid.LowPart)
{
LUID current_luid = {};
DWORD current_luid_size = sizeof(uint64_t);
if (RegGetValueW(hKey, current_name.data(), L"AdapterLuid", RRF_RT_QWORD, nullptr,
&current_luid, &current_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 (RegGetValueW(hKey, current_name.data(), L"DriverVersion", RRF_RT_QWORD, nullptr,
&driver_version, &driver_version_size) == ERROR_SUCCESS)
{
LARGE_INTEGER driver_version = {};
DWORD driver_version_size = sizeof(driver_version);
if (RegGetValueW(hKey, current_name.data(), L"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 = fmt::format("{}.{}.{}.{}", nProduct, nVersion, nSubVersion, nBuild);
}
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 = fmt::format("{}.{}.{}.{}", nProduct, nVersion, nSubVersion, nBuild);
}
}
}
}
RegCloseKey(hKey);
}
return ret;
RegCloseKey(hKey);
}
u8 Vendor()
return ret;
}
D3D::VendorID D3D::GetVendorID(IDXGIAdapter1* adapter)
{
DXGI_ADAPTER_DESC1 desc;
const HRESULT hr = adapter->GetDesc1(&desc);
if (FAILED(hr))
{
auto factory = CreateFactory(false);
auto adapter = GetAdapterFromIndex(factory.get(), 0);
Console.Error(fmt::format("IDXGIAdapter1::GetDesc() returned {:08X}", hr));
return VendorID::Unknown;
}
ASSERT(adapter);
DXGI_ADAPTER_DESC1 desc = {};
if (FAILED(adapter->GetDesc1(&desc)))
{
fprintf(stderr, "D3D: failed to get the adapter description\n");
switch (desc.VendorId)
{
case 0x10DE:
return VendorID::Nvidia;
case 0x1002:
case 0x1022:
return VendorID::AMD;
case 0x163C:
case 0x8086:
case 0x8087:
return VendorID::Intel;
default:
return VendorID::Unknown;
}
}
}
switch (desc.VendorId)
{
case 0x10DE:
return VendorID::Nvidia;
case 0x1002:
case 0x1022:
return VendorID::AMD;
case 0x163C:
case 0x8086:
case 0x8087:
return VendorID::Intel;
default:
return VendorID::Unknown;
}
GSRendererType D3D::GetPreferredRenderer()
{
auto factory = CreateFactory(false);
auto adapter = GetChosenOrFirstAdapter(factory.get(), GSConfig.Adapter);
// If we somehow can't get a D3D11 device, it's unlikely any of the renderers are going to work.
if (!adapter)
return GSRendererType::DX11;
D3D_FEATURE_LEVEL feature_level;
static const D3D_FEATURE_LEVEL check[] = {
D3D_FEATURE_LEVEL_12_0,
D3D_FEATURE_LEVEL_11_0,
};
const HRESULT hr = D3D11CreateDevice(adapter.get(), D3D_DRIVER_TYPE_UNKNOWN, nullptr, 0, std::data(check),
std::size(check), D3D11_SDK_VERSION, nullptr, &feature_level, nullptr);
if (FAILED(hr))
{
// See note above.
return GSRendererType::DX11;
}
u8 ShouldPreferRenderer()
switch (GetVendorID(adapter.get()))
{
auto factory = CreateFactory(false);
auto adapter = GetAdapterFromIndex(factory.get(), 0);
ASSERT(adapter);
D3D_FEATURE_LEVEL feature_level;
static const D3D_FEATURE_LEVEL check[] = {
D3D_FEATURE_LEVEL_12_0,
D3D_FEATURE_LEVEL_11_0,
};
const HRESULT hr = D3D11CreateDevice(
adapter.get(), D3D_DRIVER_TYPE_UNKNOWN, nullptr, 0,
std::data(check), std::size(check), D3D11_SDK_VERSION, nullptr, &feature_level, nullptr
);
if (FAILED(hr))
return Renderer::Default;
switch (Vendor())
case VendorID::Nvidia:
{
case VendorID::Nvidia:
{
if (feature_level == D3D_FEATURE_LEVEL_12_0)
return Renderer::Vulkan;
else if (feature_level == D3D_FEATURE_LEVEL_11_0)
return Renderer::OpenGL;
else
return Renderer::Direct3D11;
}
if (feature_level == D3D_FEATURE_LEVEL_12_0)
return GSRendererType::VK;
else if (feature_level == D3D_FEATURE_LEVEL_11_0)
return GSRendererType::OGL;
else
return GSRendererType::DX11;
}
case VendorID::AMD:
{
if (feature_level == D3D_FEATURE_LEVEL_12_0)
return Renderer::Vulkan;
else
return Renderer::Direct3D11;
}
case VendorID::AMD:
{
if (feature_level == D3D_FEATURE_LEVEL_12_0)
return GSRendererType::VK;
else
return GSRendererType::DX11;
}
case VendorID::Intel:
{
// Older Intel GPUs prior to Xe seem to have broken OpenGL drivers which choke
// on some of our shaders, causing what appears to be GPU timeouts+device removals.
// Vulkan has broken barriers, also prior to Xe. So just fall back to DX11 everywhere,
// unless we can find some way of differentiating Xe.
return Renderer::Direct3D11;
}
case VendorID::Intel:
{
// Older Intel GPUs prior to Xe seem to have broken OpenGL drivers which choke
// on some of our shaders, causing what appears to be GPU timeouts+device removals.
// Vulkan has broken barriers, also prior to Xe. So just fall back to DX11 everywhere,
// unless we have Arc, which is easy to identify.
if (StringUtil::StartsWith(GetAdapterName(adapter.get()), "Intel(R) Arc(TM) "))
return GSRendererType::VK;
else
return GSRendererType::DX11;
}
default:
{
// Default is D3D11
return Renderer::Direct3D11;
}
default:
{
// Default is D3D11
return GSRendererType::DX11;
}
}
}

View File

@ -18,26 +18,39 @@
#include "common/RedtapeWindows.h"
#include "common/RedtapeWilCom.h"
#include <dxgi1_3.h>
#include <vector>
#include "pcsx2/Config.h"
#include <dxgi1_5.h>
#include <string>
#include <string_view>
#include <vector>
namespace D3D
{
// create a dxgi factory
wil::com_ptr_nothrow<IDXGIFactory2> CreateFactory(bool debug);
wil::com_ptr_nothrow<IDXGIFactory5> CreateFactory(bool debug);
// get an adapter based on position
// assuming no one removes/moves it, it should always have the same id
// however in the event that the adapter is not found due to the above, use the default
wil::com_ptr_nothrow<IDXGIAdapter1> GetAdapterFromIndex(IDXGIFactory2* factory, int index);
// returns a list of all adapter names
std::vector<std::string> GetAdapterNames(IDXGIFactory5* factory);
// get an adapter based on name
wil::com_ptr_nothrow<IDXGIAdapter1> GetAdapterByName(IDXGIFactory5* factory, const std::string_view& name);
// returns the first adapter in the system
wil::com_ptr_nothrow<IDXGIAdapter1> GetFirstAdapter(IDXGIFactory5* factory);
// returns the adapter specified in the configuration, or the default
wil::com_ptr_nothrow<IDXGIAdapter1> GetChosenOrFirstAdapter(IDXGIFactory5* factory, const std::string_view& name);
// returns a utf-8 string of the specified adapter's name
std::string GetAdapterName(IDXGIAdapter1* adapter);
// returns the driver version from the registry as a string
std::string GetDriverVersionFromLUID(const LUID& luid);
// this is sort of a legacy thing that doesn't have much to do with d3d (just the easiest way)
// checks to see if the adapter at 0 is NV and thus we should prefer OpenGL
enum VendorID
enum class VendorID
{
Unknown,
Nvidia,
@ -45,15 +58,6 @@ namespace D3D
Intel
};
enum Renderer
{
Default,
OpenGL,
Vulkan,
Direct3D11,
Direct3D12
};
u8 Vendor();
u8 ShouldPreferRenderer();
};
VendorID GetVendorID(IDXGIAdapter1* adapter);
GSRendererType GetPreferredRenderer();
}; // namespace D3D

View File

@ -119,7 +119,13 @@ bool GSDevice11::Create()
{
// HACK: check AMD
// Broken point sampler should be enabled only on AMD.
m_features.broken_point_sampler = (D3D::Vendor() == D3D::VendorID::AMD);
wil::com_ptr_nothrow<IDXGIDevice> dxgi_device;
wil::com_ptr_nothrow<IDXGIAdapter1> dxgi_adapter;
if (SUCCEEDED(m_dev->QueryInterface(dxgi_device.put())) &&
SUCCEEDED(dxgi_device->GetParent(IID_PPV_ARGS(dxgi_adapter.put()))))
{
m_features.broken_point_sampler = (D3D::GetVendorID(dxgi_adapter.get()) == D3D::VendorID::AMD);
}
}
SetFeatures();