d3d: abstract some dxgi functionality

- adapters are now referenced by index instead of string matching, hopefully this will make a few things easier
- add a setting to enable dxgi/device debugging (instead of hiding it behind a compiler macro)
- improve logging
- remove some d3d cruft from gsutils
This commit is contained in:
kojin 2021-08-11 11:15:15 -04:00 committed by tellowkrinkle
parent 241a762d04
commit be3253bf87
10 changed files with 250 additions and 162 deletions

View File

@ -789,6 +789,7 @@ if(WIN32)
)
list(APPEND pcsx2GSSources
GS/Renderers/DX11/D3D.cpp
GS/Renderers/DX11/GSDevice11.cpp
GS/Renderers/DX11/GSRendererDX11.cpp
GS/Renderers/DX11/GSTexture11.cpp
@ -799,6 +800,7 @@ if(WIN32)
GS/Window/GSSettingsDlg.cpp
)
list(APPEND pcsx2GSHeaders
GS/Renderers/DX11/D3D.h
GS/Renderers/DX11/GSDevice11.h
GS/Renderers/DX11/GSRendererDX11.h
GS/Renderers/DX11/GSTexture11.h

View File

@ -31,6 +31,7 @@
#include "Renderers/DX11/GSRendererDX11.h"
#include "Renderers/DX11/GSDevice11.h"
#include "Window/GSSettingsDlg.h"
#include "GS/Renderers/DX11/D3D.h"
static HRESULT s_hr = E_FAIL;
@ -151,7 +152,12 @@ int _GSopen(const WindowInfo& wi, const char* title, GSRendererType renderer, in
renderer = static_cast<GSRendererType>(theApp.GetConfigI("Renderer"));
#ifdef _WIN32
if (renderer == GSRendererType::Default)
renderer = GSUtil::GetBestRenderer();
{
if (D3D::ShouldPreferD3D())
renderer = GSRendererType::DX1011_HW;
else
renderer = GSRendererType::OGL_HW;
}
#endif
}
@ -301,7 +307,12 @@ int GSopen2(const WindowInfo& wi, uint32 flags)
const auto config_renderer = static_cast<GSRendererType>(theApp.GetConfigI("Renderer"));
if (current_renderer == config_renderer)
current_renderer = GSUtil::GetBestRenderer();
{
if (D3D::ShouldPreferD3D())
current_renderer = GSRendererType::DX1011_HW;
else
current_renderer = GSRendererType::OGL_HW;
}
else
current_renderer = config_renderer;
}
@ -1185,9 +1196,10 @@ void GSApp::Init()
// Avoid to clutter the ini file with useless options
#ifdef _WIN32
// Per OS option.
m_default_configuration["Adapter"] = "default";
m_default_configuration["adapter_index"] = "0";
m_default_configuration["CaptureFileName"] = "";
m_default_configuration["CaptureVideoCodecDisplayName"] = "";
m_default_configuration["debug_d3d"] = "0";
m_default_configuration["dx_break_on_severity"] = "0";
// D3D Blending option
m_default_configuration["accurate_blending_unit_d3d11"] = "1";

View File

@ -19,7 +19,6 @@
#include <codecvt>
#ifdef _WIN32
#include "Renderers/DX11/GSDevice11.h"
#include <VersionHelpers.h>
#include "svnrev.h"
#include <wil/com.h>
@ -186,82 +185,6 @@ CRCHackLevel GSUtil::GetRecommendedCRCHackLevel(GSRendererType type)
return type == GSRendererType::OGL_HW ? CRCHackLevel::Partial : CRCHackLevel::Full;
}
#ifdef _WIN32
// ---------------------------------------------------------------------------------
// DX11 Detection (includes DXGI detection and dynamic library method bindings)
// ---------------------------------------------------------------------------------
// Code 'Borrowed' from Microsoft's DXGI sources -- Modified to suit our needs. --air
// Stripped down because of unnecessary complexity and false positives
// e.g. (d3d11_beta.dll would fail at device creation time) --pseudonym
static int s_DXGI;
static int s_D3D11;
bool GSUtil::CheckDXGI()
{
if (0 == s_DXGI)
{
HMODULE hmod = LoadLibrary(L"dxgi.dll");
s_DXGI = hmod ? 1 : -1;
if (hmod)
FreeLibrary(hmod);
}
return s_DXGI > 0;
}
bool GSUtil::CheckD3D11()
{
if (!CheckDXGI())
return false;
if (0 == s_D3D11)
{
HMODULE hmod = LoadLibrary(L"d3d11.dll");
s_D3D11 = hmod ? 1 : -1;
if (hmod)
FreeLibrary(hmod);
}
return s_D3D11 > 0;
}
D3D_FEATURE_LEVEL GSUtil::CheckDirect3D11Level(IDXGIAdapter* adapter, D3D_DRIVER_TYPE type)
{
HRESULT hr;
D3D_FEATURE_LEVEL level;
if (!CheckD3D11())
return (D3D_FEATURE_LEVEL)0;
hr = D3D11CreateDevice(adapter, type, NULL, 0, NULL, 0, D3D11_SDK_VERSION, NULL, &level, NULL);
return SUCCEEDED(hr) ? level : (D3D_FEATURE_LEVEL)0;
}
GSRendererType GSUtil::GetBestRenderer()
{
wil::com_ptr_nothrow<IDXGIFactory1> dxgi_factory;
if (SUCCEEDED(CreateDXGIFactory1(IID_PPV_ARGS(dxgi_factory.put()))))
{
wil::com_ptr_nothrow<IDXGIAdapter1> adapter;
if (SUCCEEDED(dxgi_factory->EnumAdapters1(0, adapter.put())))
{
DXGI_ADAPTER_DESC1 desc;
if (SUCCEEDED(adapter->GetDesc1(&desc)))
{
D3D_FEATURE_LEVEL level = GSUtil::CheckDirect3D11Level();
// Check for Nvidia VendorID. Latest OpenGL features need at least DX11 level GPU
if (desc.VendorId == 0x10DE && level >= D3D_FEATURE_LEVEL_11_0)
return GSRendererType::OGL_HW;
}
}
}
return GSRendererType::DX1011_HW;
}
#endif
#ifdef _WIN32
void GSmkdir(const wchar_t* dir)
{

View File

@ -36,13 +36,6 @@ public:
static bool CheckSSE();
static CRCHackLevel GetRecommendedCRCHackLevel(GSRendererType type);
#ifdef _WIN32
static bool CheckDXGI();
static bool CheckD3D11();
static GSRendererType GetBestRenderer();
static D3D_FEATURE_LEVEL CheckDirect3D11Level(IDXGIAdapter* adapter = NULL, D3D_DRIVER_TYPE type = D3D_DRIVER_TYPE_HARDWARE);
#endif
};
#ifdef _WIN32

View File

@ -0,0 +1,111 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2021 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 "GS/Renderers/DX11/D3D.h"
#include "GS/GS.h"
#include <d3d11.h>
namespace D3D
{
wil::com_ptr_nothrow<IDXGIFactory2> CreateFactory(bool debug)
{
UINT flags = 0;
if (debug)
flags |= DXGI_CREATE_FACTORY_DEBUG;
// 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)
{
fprintf(stderr, "D3D: failed to create debug dxgi factory, trying without debugging\n");
hr = CreateDXGIFactory2(0, IID_PPV_ARGS(factory.put()));;
}
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");
}
return factory;
}
wil::com_ptr_nothrow<IDXGIAdapter1> GetAdapterFromIndex(IDXGIFactory2* factory, int index)
{
ASSERT(factory);
wil::com_ptr_nothrow<IDXGIAdapter1> adapter;
if (factory->EnumAdapters1(index, adapter.put()) == DXGI_ERROR_NOT_FOUND)
{
// try index 0 (default adapter)
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");
}
}
return adapter;
}
bool IsNvidia(IDXGIAdapter1* adapter)
{
ASSERT(adapter);
DXGI_ADAPTER_DESC1 desc = {};
if (FAILED(adapter->GetDesc1(&desc)))
{
fprintf(stderr, "D3D: failed to get the adapter description\n");
return false;
}
// NV magic number
return desc.VendorId == 0x10DE;
}
bool SupportsFeatureLevel11(IDXGIAdapter1* adapter)
{
ASSERT(adapter);
D3D_FEATURE_LEVEL feature_level;
static const D3D_FEATURE_LEVEL check[] = { D3D_FEATURE_LEVEL_11_0 };
const HRESULT hr = D3D11CreateDevice(
adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, 0,
check, std::size(check), D3D11_SDK_VERSION, nullptr, &feature_level, nullptr
);
if (FAILED(hr))
return false;
return feature_level == D3D_FEATURE_LEVEL_11_0;
}
bool ShouldPreferD3D()
{
auto factory = CreateFactory(false);
auto adapter = GetAdapterFromIndex(factory.get(), 0);
return !(IsNvidia(adapter.get()) && SupportsFeatureLevel11(adapter.get()));
}
}

View File

@ -0,0 +1,37 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2021 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 <dxgi1_3.h>
#include <vector>
#include <string>
#include <wil/com.h>
namespace D3D
{
// create a dxgi factory
wil::com_ptr_nothrow<IDXGIFactory2> 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);
// 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
bool IsNvidia(IDXGIAdapter1* adapter);
bool ShouldPreferD3D();
};

View File

@ -16,6 +16,7 @@
#include "PrecompiledHeader.h"
#include "GS.h"
#include "GSDevice11.h"
#include "GS/Renderers/DX11/D3D.h"
#include "GS/GSUtil.h"
#include "GS/resource.h"
#include <fstream>
@ -76,8 +77,6 @@ bool GSDevice11::SetFeatureLevel(D3D_FEATURE_LEVEL level, bool compat_mode)
bool GSDevice11::Create(const WindowInfo& wi)
{
bool nvidia_vendor = false;
if (!__super::Create(wi))
{
return false;
@ -89,57 +88,35 @@ bool GSDevice11::Create(const WindowInfo& wi)
D3D11_RASTERIZER_DESC rd;
D3D11_BLEND_DESC bsd;
// create factory
wil::com_ptr_nothrow<IDXGIFactory2> factory;
const bool enable_debugging = theApp.GetConfigB("debug_d3d");
auto factory = D3D::CreateFactory(enable_debugging);
if (!factory)
return false;
// select adapter
auto adapter = D3D::GetAdapterFromIndex(
factory.get(), theApp.GetConfigI("adapter_index")
);
DXGI_ADAPTER_DESC1 adapter_desc = {};
if (SUCCEEDED(adapter->GetDesc1(&adapter_desc)))
{
const HRESULT result = CreateDXGIFactory2(0, IID_PPV_ARGS(factory.put()));
if (FAILED(result))
{
fprintf(stderr, "D3D11: Unable to create DXGIFactory2 (reason: %x)\n", result);
return false;
}
std::string adapter_name = convert_utf16_to_utf8(
adapter_desc.Description
);
fprintf(stderr, "Selected DXGI Adapter\n"
"\tName: %s\n"
"\tVendor: %x\n", adapter_name.c_str(), adapter_desc.VendorId);
}
// enumerate adapters
wil::com_ptr_nothrow<IDXGIAdapter1> adapter;
D3D_DRIVER_TYPE driver_type = D3D_DRIVER_TYPE_HARDWARE;
{
std::string adapter_id = theApp.GetConfigS("Adapter");
if (adapter_id == "ref")
driver_type = D3D_DRIVER_TYPE_REFERENCE;
else
{
for (int i = 0;; i++)
{
wil::com_ptr_nothrow<IDXGIAdapter1> enum_adapter;
if (FAILED(factory->EnumAdapters1(i, enum_adapter.put())))
break;
DXGI_ADAPTER_DESC1 desc;
const HRESULT hr = enum_adapter->GetDesc1(&desc);
if (SUCCEEDED(hr) && (GSAdapter(desc) == adapter_id || adapter_id == "default"))
{
if (desc.VendorId == 0x10DE)
nvidia_vendor = true;
adapter = std::move(enum_adapter);
driver_type = D3D_DRIVER_TYPE_UNKNOWN;
break;
}
}
}
}
D3D_FEATURE_LEVEL level;
// device creation
{
uint32 flags = D3D11_CREATE_DEVICE_SINGLETHREADED;
#ifdef _DEBUG
flags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
if(enable_debugging)
flags |= D3D11_CREATE_DEVICE_DEBUG;
constexpr std::array<D3D_FEATURE_LEVEL, 3> supported_levels = {
D3D_FEATURE_LEVEL_11_0,
@ -147,16 +124,60 @@ bool GSDevice11::Create(const WindowInfo& wi)
D3D_FEATURE_LEVEL_10_0,
};
const HRESULT result = D3D11CreateDevice(
adapter.get(), driver_type, nullptr, flags,
D3D_FEATURE_LEVEL feature_level;
HRESULT result = D3D11CreateDevice(
adapter.get(), D3D_DRIVER_TYPE_UNKNOWN, nullptr, flags,
supported_levels.data(), supported_levels.size(),
D3D11_SDK_VERSION, m_dev.put(), &level, m_ctx.put());
D3D11_SDK_VERSION, m_dev.put(), &feature_level, m_ctx.put()
);
// if a debug device is requested but not supported, fallback to non-debug device
if (FAILED(result) && enable_debugging)
{
fprintf(stderr, "D3D: failed to create debug device, trying without debugging\n");
// clear the debug flag
flags = D3D11_CREATE_DEVICE_SINGLETHREADED;
result = D3D11CreateDevice(
adapter.get(), D3D_DRIVER_TYPE_UNKNOWN, nullptr, flags,
supported_levels.data(), supported_levels.size(),
D3D11_SDK_VERSION, m_dev.put(), &feature_level, m_ctx.put()
);
}
if (FAILED(result))
{
fprintf(stderr, "D3D11: Unable to create D3D11 device (reason %x)\n", result);
fprintf(stderr, "D3D: unable to create D3D11 device (reason %x)\n"
"ensure that your gpu supports our minimum requirements:\n"
"https://github.com/PCSX2/pcsx2#system-requirements\n", result);
return false;
}
if (enable_debugging)
{
if (auto info_queue = m_dev.try_query<ID3D11InfoQueue>())
{
const int break_on = theApp.GetConfigI("dx_break_on_severity");
info_queue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_CORRUPTION, break_on & (1 << 0));
info_queue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR, break_on & (1 << 1));
info_queue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_WARNING, break_on & (1 << 2));
info_queue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_INFO, break_on & (1 << 3));
}
fprintf(stderr, "D3D: debugging enabled\n");
}
if (!SetFeatureLevel(feature_level, true))
{
fprintf(stderr, "D3D: adapter doesn't have a sufficient feature level\n");
return false;
}
// Set maximum texture size limit based on supported feature level.
if (feature_level >= D3D_FEATURE_LEVEL_11_0)
m_d3d_texsize = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
else
m_d3d_texsize = D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION;
}
// swapchain creation
@ -182,40 +203,18 @@ bool GSDevice11::Create(const WindowInfo& wi)
if (FAILED(result))
{
fprintf(stderr, "D3D11: Failed to create swapchain (reason: %x)\n", result);
fprintf(stderr, "D3D: Failed to create swapchain (reason: %x)\n", result);
return false;
}
}
if (!SetFeatureLevel(level, true))
return false;
// Set maximum texture size limit based on supported feature level.
if (level >= D3D_FEATURE_LEVEL_11_0)
m_d3d_texsize = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
else
m_d3d_texsize = D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION;
{
// HACK: check nVIDIA
// Note: It can cause issues on several games such as SOTC, Fatal Frame, plus it adds border offset.
const bool disable_safe_features = theApp.GetConfigB("UserHacks") && theApp.GetConfigB("UserHacks_Disable_Safe_Features");
m_hack_topleft_offset = (m_upscale_multiplier != 1 && nvidia_vendor && !disable_safe_features) ? -0.01f : 0.0f;
bool disable_safe_features = theApp.GetConfigB("UserHacks") && theApp.GetConfigB("UserHacks_Disable_Safe_Features");
m_hack_topleft_offset = (m_upscale_multiplier != 1 && D3D::IsNvidia(adapter.get()) && !disable_safe_features) ? -0.01f : 0.0f;
}
// debug
#ifdef _DEBUG
if (auto info_queue = m_dev.try_query<ID3D11InfoQueue>())
{
int break_on = theApp.GetConfigI("dx_break_on_severity");
info_queue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_CORRUPTION, break_on & (1 << 0));
info_queue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR, break_on & (1 << 1));
info_queue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_WARNING, break_on & (1 << 2));
info_queue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_INFO, break_on & (1 << 3));
}
#endif
// convert
D3D11_INPUT_ELEMENT_DESC il_convert[] =

View File

@ -486,6 +486,9 @@ DebugTab::DebugTab(wxWindow* parent)
auto* debug_check_box = new wxWrapSizer(wxHORIZONTAL);
m_ui.addCheckBox(debug_check_box, "GLSL compilation", "debug_glsl_shader");
m_ui.addCheckBox(debug_check_box, "Print GL error", "debug_opengl");
#ifdef _WIN32
m_ui.addCheckBox(debug_check_box, "D3D Debug Layer", "debug_d3d");
#endif
m_ui.addCheckBox(debug_check_box, "Dump GS data", "dump");
auto* debug_save_check_box = new wxWrapSizer(wxHORIZONTAL);

View File

@ -304,6 +304,7 @@
<ClCompile Include="GameDatabase.cpp" />
<ClCompile Include="Gif_Logger.cpp" />
<ClCompile Include="Gif_Unit.cpp" />
<ClCompile Include="GS\Renderers\DX11\D3D.cpp" />
<ClCompile Include="gui\AppGameDatabase.cpp" />
<ClCompile Include="gui\AppUserMode.cpp" />
<ClCompile Include="gui\CheckedStaticBox.cpp" />
@ -742,6 +743,7 @@
<ClInclude Include="DEV9\Win32\tap.h" />
<ClInclude Include="GameDatabase.h" />
<ClInclude Include="Gif_Unit.h" />
<ClInclude Include="GS\Renderers\DX11\D3D.h" />
<ClInclude Include="gui\AppGameDatabase.h" />
<ClInclude Include="gui\CheckedStaticBox.h" />
<ClInclude Include="gui\i18n.h" />

View File

@ -1601,6 +1601,9 @@
<ClCompile Include="GS\Window\GSSettingsDlg.cpp">
<Filter>System\Ps2\GS\Window</Filter>
</ClCompile>
<ClCompile Include="GS\Renderers\DX11\D3D.cpp">
<Filter>System\Ps2\GS\Renderers\Direct3D</Filter>
</ClCompile>
<ClCompile Include="gui\wxAppWithHelpers.cpp">
<Filter>AppHost</Filter>
</ClCompile>
@ -2697,6 +2700,9 @@
<ClInclude Include="GS\Window\GSSettingsDlg.h">
<Filter>System\Ps2\GS\Window</Filter>
</ClInclude>
<ClInclude Include="GS\Renderers\DX11\D3D.h">
<Filter>System\Ps2\GS\Renderers\Direct3D</Filter>
</ClInclude>
<ClInclude Include="gui\wxAppWithHelpers.h">
<Filter>AppHost</Filter>
</ClInclude>