From be3253bf87da4ba14adf33addb14355d1025c14d Mon Sep 17 00:00:00 2001 From: kojin Date: Wed, 11 Aug 2021 11:15:15 -0400 Subject: [PATCH] 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 --- pcsx2/CMakeLists.txt | 2 + pcsx2/GS/GS.cpp | 18 ++- pcsx2/GS/GSUtil.cpp | 77 ------------- pcsx2/GS/GSUtil.h | 7 -- pcsx2/GS/Renderers/DX11/D3D.cpp | 111 ++++++++++++++++++ pcsx2/GS/Renderers/DX11/D3D.h | 37 ++++++ pcsx2/GS/Renderers/DX11/GSDevice11.cpp | 149 ++++++++++++------------- pcsx2/GS/Window/GSwxDialog.cpp | 3 + pcsx2/pcsx2.vcxproj | 2 + pcsx2/pcsx2.vcxproj.filters | 6 + 10 files changed, 250 insertions(+), 162 deletions(-) create mode 100644 pcsx2/GS/Renderers/DX11/D3D.cpp create mode 100644 pcsx2/GS/Renderers/DX11/D3D.h diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt index a3e7eedfa4..e11523a619 100644 --- a/pcsx2/CMakeLists.txt +++ b/pcsx2/CMakeLists.txt @@ -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 diff --git a/pcsx2/GS/GS.cpp b/pcsx2/GS/GS.cpp index 6e092648e3..c5f0382cc7 100644 --- a/pcsx2/GS/GS.cpp +++ b/pcsx2/GS/GS.cpp @@ -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(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(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"; diff --git a/pcsx2/GS/GSUtil.cpp b/pcsx2/GS/GSUtil.cpp index 560f2b5b4e..4e119b914b 100644 --- a/pcsx2/GS/GSUtil.cpp +++ b/pcsx2/GS/GSUtil.cpp @@ -19,7 +19,6 @@ #include #ifdef _WIN32 -#include "Renderers/DX11/GSDevice11.h" #include #include "svnrev.h" #include @@ -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 dxgi_factory; - if (SUCCEEDED(CreateDXGIFactory1(IID_PPV_ARGS(dxgi_factory.put())))) - { - wil::com_ptr_nothrow 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) { diff --git a/pcsx2/GS/GSUtil.h b/pcsx2/GS/GSUtil.h index 6820e21284..721295e635 100644 --- a/pcsx2/GS/GSUtil.h +++ b/pcsx2/GS/GSUtil.h @@ -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 diff --git a/pcsx2/GS/Renderers/DX11/D3D.cpp b/pcsx2/GS/Renderers/DX11/D3D.cpp new file mode 100644 index 0000000000..bdd8d1de24 --- /dev/null +++ b/pcsx2/GS/Renderers/DX11/D3D.cpp @@ -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 . + */ + +#include "PrecompiledHeader.h" +#include "GS/Renderers/DX11/D3D.h" +#include "GS/GS.h" + +#include + +namespace D3D +{ + wil::com_ptr_nothrow 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 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 GetAdapterFromIndex(IDXGIFactory2* factory, int index) + { + ASSERT(factory); + + wil::com_ptr_nothrow 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())); + } +} diff --git a/pcsx2/GS/Renderers/DX11/D3D.h b/pcsx2/GS/Renderers/DX11/D3D.h new file mode 100644 index 0000000000..dee2e177ff --- /dev/null +++ b/pcsx2/GS/Renderers/DX11/D3D.h @@ -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 . + */ + +#pragma once + +#include +#include +#include +#include + +namespace D3D +{ + // create a dxgi factory + wil::com_ptr_nothrow 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 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(); +}; diff --git a/pcsx2/GS/Renderers/DX11/GSDevice11.cpp b/pcsx2/GS/Renderers/DX11/GSDevice11.cpp index bc4419c569..18332cec04 100644 --- a/pcsx2/GS/Renderers/DX11/GSDevice11.cpp +++ b/pcsx2/GS/Renderers/DX11/GSDevice11.cpp @@ -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 @@ -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 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 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 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 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()) + { + 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()) - { - 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[] = diff --git a/pcsx2/GS/Window/GSwxDialog.cpp b/pcsx2/GS/Window/GSwxDialog.cpp index 5fe1bd9a40..8d2604d99c 100644 --- a/pcsx2/GS/Window/GSwxDialog.cpp +++ b/pcsx2/GS/Window/GSwxDialog.cpp @@ -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); diff --git a/pcsx2/pcsx2.vcxproj b/pcsx2/pcsx2.vcxproj index 9060d4caef..6031e4fcdc 100644 --- a/pcsx2/pcsx2.vcxproj +++ b/pcsx2/pcsx2.vcxproj @@ -304,6 +304,7 @@ + @@ -742,6 +743,7 @@ + diff --git a/pcsx2/pcsx2.vcxproj.filters b/pcsx2/pcsx2.vcxproj.filters index a6efc7b40d..de0ded8fb7 100644 --- a/pcsx2/pcsx2.vcxproj.filters +++ b/pcsx2/pcsx2.vcxproj.filters @@ -1601,6 +1601,9 @@ System\Ps2\GS\Window + + System\Ps2\GS\Renderers\Direct3D + AppHost @@ -2697,6 +2700,9 @@ System\Ps2\GS\Window + + System\Ps2\GS\Renderers\Direct3D + AppHost