From 72a29eeabd8c9be280d8782ed575d68537ee892a Mon Sep 17 00:00:00 2001 From: TheLastRar Date: Tue, 15 Sep 2026 19:12:14 +0100 Subject: [PATCH] GS/DX12: Support SM6.5 --- common/vsprops/LinkPCSX2Deps.props | 1 + common/vsprops/common.props | 2 +- pcsx2/CMakeLists.txt | 3 +- pcsx2/GS/Renderers/DX11/D3D.cpp | 133 ++++++++++++++++++- pcsx2/GS/Renderers/DX11/D3D.h | 11 ++ pcsx2/GS/Renderers/DX12/D3D12ShaderCache.cpp | 1 - pcsx2/GS/Renderers/DX12/GSDevice12.cpp | 28 +++- pcsx2/GS/Renderers/DX12/GSDevice12.h | 3 + 8 files changed, 175 insertions(+), 7 deletions(-) diff --git a/common/vsprops/LinkPCSX2Deps.props b/common/vsprops/LinkPCSX2Deps.props index 75825177ac..66210e4a5c 100644 --- a/common/vsprops/LinkPCSX2Deps.props +++ b/common/vsprops/LinkPCSX2Deps.props @@ -33,6 +33,7 @@ + diff --git a/common/vsprops/common.props b/common/vsprops/common.props index e605d17109..8bbda572f9 100644 --- a/common/vsprops/common.props +++ b/common/vsprops/common.props @@ -63,7 +63,7 @@ comctl32.lib;ws2_32.lib;shlwapi.lib;winmm.lib;rpcrt4.lib;iphlpapi.lib;%(AdditionalDependencies) - dxguid.lib;dinput8.lib;hid.lib;PowrProf.lib;d3dcompiler.lib;d3d11.lib;d3d12.lib;dxgi.lib;strmiids.lib;comsuppw.lib;OneCore.lib;dwmapi.lib;%(AdditionalDependencies) + dxguid.lib;dinput8.lib;hid.lib;PowrProf.lib;d3dcompiler.lib;dxcompiler.lib;d3d11.lib;d3d12.lib;dxgi.lib;strmiids.lib;comsuppw.lib;OneCore.lib;dwmapi.lib;%(AdditionalDependencies) opengl32.lib;%(AdditionalDependencies) true Windows diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt index 16efd3045a..c100efb296 100644 --- a/pcsx2/CMakeLists.txt +++ b/pcsx2/CMakeLists.txt @@ -1186,6 +1186,7 @@ if(WIN32) hid.lib PowrProf.lib d3dcompiler.lib + dxcompiler.lib d3d11.lib d3d12.lib dxgi.lib @@ -1310,7 +1311,7 @@ function(setup_main_executable target) # Copy dependency libraries. set(DEPS_BINDIR "${CMAKE_SOURCE_DIR}/deps/bin") - set(DEPS_TO_COPY freetype.dll harfbuzz.dll jpeg62.dll libpng16.dll libsharpyuv.dll libwebp.dll libwebpdemux.dll libwebpmux.dll lz4.dll SDL3.dll shaderc_shared.dll z.dll zstd.dll plutovg.dll plutosvg.dll ryml.dll) + set(DEPS_TO_COPY freetype.dll harfbuzz.dll jpeg62.dll libpng16.dll libsharpyuv.dll libwebp.dll libwebpdemux.dll libwebpmux.dll lz4.dll SDL3.dll shaderc_shared.dll z.dll zstd.dll plutovg.dll plutosvg.dll ryml.dll dxcompiler.dll) set(DEPS_TO_COPY $,kddockwidgets-qt6d.dll,kddockwidgets-qt6.dll> ${DEPS_TO_COPY} diff --git a/pcsx2/GS/Renderers/DX11/D3D.cpp b/pcsx2/GS/Renderers/DX11/D3D.cpp index 758e4196ae..66b29a445c 100644 --- a/pcsx2/GS/Renderers/DX11/D3D.cpp +++ b/pcsx2/GS/Renderers/DX11/D3D.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include "fmt/format.h" @@ -492,18 +493,23 @@ const char* D3D::ShaderModelToCacheString(D3D::ShaderModel shader_model) case ShaderModel::SM50: return "sm50"; case ShaderModel::SM51: + case ShaderModel::SM60: + case ShaderModel::SM61: + case ShaderModel::SM62: + case ShaderModel::SM63: + case ShaderModel::SM64: return "sm51"; + case ShaderModel::SM65: + return "sm65"; default: return "unk"; } } -wil::com_ptr_nothrow D3D::CompileShader(D3D::ShaderType type, D3D::ShaderModel shader_model, bool debug, +wil::com_ptr_nothrow D3D::CompileShaderDXBC(D3D::ShaderType type, D3D::ShaderModel shader_model, bool debug, const std::string_view code, const D3D_SHADER_MACRO* macros /* = nullptr */, const char* entry_point /* = "main" */) { - const GSShaderCompileIndicator::CompileTimer compile_timer; - const char* target; switch (shader_model) { @@ -580,3 +586,124 @@ wil::com_ptr_nothrow D3D::CompileShader(D3D::ShaderType type, D3D::Sha return blob; } + +wil::com_ptr_nothrow D3D::CompileShaderDXIL(D3D::ShaderType type, D3D::ShaderModel shader_model, bool debug, + const std::string_view code, const D3D_SHADER_MACRO* macros /* = nullptr */, + const char* entry_point /* = "main" */) +{ + const wchar_t* target; + switch (shader_model) + { + case ShaderModel::SM60: + case ShaderModel::SM61: + case ShaderModel::SM62: + case ShaderModel::SM63: + case ShaderModel::SM64: + pxAssert(false); + break; + case ShaderModel::SM65: + default: + { + static constexpr std::array targets = {{L"vs_6_5", L"ps_6_5", L"cs_6_5"}}; + target = targets[static_cast(type)]; + } + break; + } + + // Build Args. + std::vector args; + if (macros) + { + for (const D3D_SHADER_MACRO* macro = macros; macro->Name != nullptr; macro++) + args.push_back(StringUtil::UTF8StringToWideString(fmt::format("-D{}={}", macro->Name, macro->Definition))); + } + + if (entry_point) + { + args.push_back(L"-E"); + args.push_back(StringUtil::UTF8StringToWideString(entry_point)); + } + + args.push_back(L"-T"); + args.push_back(target); + + if (debug) + { + args.push_back(L"-Od"); + args.push_back(L"-Zi"); + args.push_back(L"-Zss"); + args.push_back(L"-Qembed_debug"); + } + else + { + args.push_back(L"-O3"); + args.push_back(L"-Qstrip_reflect"); + } + + std::vector arg_ptrs; + arg_ptrs.reserve(args.size()); + for (const std::wstring& arg : args) + arg_ptrs.push_back(arg.c_str()); + + // Compile Shader. + wil::com_ptr_nothrow compiler; + DxcCreateInstance(CLSID_DxcCompiler, IID_PPV_ARGS(compiler.put())); + + const DxcBuffer source{code.data(), code.length(), DXC_CP_UTF8}; + wil::com_ptr_nothrow results; + HRESULT hr = compiler->Compile(&source, arg_ptrs.data(), arg_ptrs.size(), nullptr, IID_PPV_ARGS(results.put())); + + if (FAILED(hr)) + { + Console.WriteLn("Compiler Failed"); + return {}; + } + + wil::com_ptr_nothrow error_string = nullptr; + results->GetOutput(DXC_OUT_ERRORS, IID_PPV_ARGS(error_string.put()), nullptr); + + results->GetStatus(&hr); + if (FAILED(hr)) + { + std::string target_utf8 = StringUtil::WideStringToUTF8String(target); + Console.WriteLn("D3D: Failed to compile '%s':\n%s", target_utf8.c_str(), error_string->GetStringPointer()); + + std::ofstream ofs(Path::Combine(EmuFolders::Logs, fmt::format("pcsx2_bad_shader_{}.txt", s_next_bad_shader_id++)), + std::ofstream::out | std::ofstream::binary); + if (ofs.is_open()) + { + ofs << code; + ofs << "\n\nCompile as " << target_utf8.c_str() << " failed: " << hr << "\n"; + ofs.write(error_string->GetStringPointer(), error_string->GetStringLength()); + ofs << "\n"; + if (macros) + { + for (const D3D_SHADER_MACRO* macro = macros; macro->Name != nullptr; macro++) + ofs << "#define " << macro->Name << " " << macro->Definition << "\n"; + } + ofs.close(); + } + + return {}; + } + + if (error_string->GetStringLength() != 0) + Console.Warning("'%s' compiled with warnings:\n%s", StringUtil::WideStringToUTF8String(target).c_str(), error_string->GetStringPointer()); + + wil::com_ptr_nothrow blob = nullptr; + results->GetOutput(DXC_OUT_OBJECT, IID_PPV_ARGS(blob.put()), nullptr); + + return blob; +} + +wil::com_ptr_nothrow D3D::CompileShader(D3D::ShaderType type, D3D::ShaderModel shader_model, bool debug, + const std::string_view code, const D3D_SHADER_MACRO* macros /* = nullptr */, + const char* entry_point /* = "main" */) +{ + const GSShaderCompileIndicator::CompileTimer compile_timer; + + if (static_cast(shader_model) < 0x65) + return CompileShaderDXBC(type, shader_model, debug, code, macros, entry_point); + else + return CompileShaderDXIL(type, shader_model, debug, code, macros, entry_point); +} diff --git a/pcsx2/GS/Renderers/DX11/D3D.h b/pcsx2/GS/Renderers/DX11/D3D.h index 61c07715fa..c6037bfe61 100644 --- a/pcsx2/GS/Renderers/DX11/D3D.h +++ b/pcsx2/GS/Renderers/DX11/D3D.h @@ -69,10 +69,21 @@ namespace D3D SM41 = 0x41, // DX11 FL 10_1 SM50 = 0x50, // DX11 FL 11_0 SM51 = 0x51, // DX12 + SM60 = 0x60, + SM61 = 0x61, + SM62 = 0x62, + SM63 = 0x63, + SM64 = 0x64, + SM65 = 0x65, }; const char* ShaderModelToCacheString(ShaderModel shader_model); wil::com_ptr_nothrow CompileShader(ShaderType type, ShaderModel shader_model, bool debug, const std::string_view code, const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main"); + + wil::com_ptr_nothrow CompileShaderDXBC(ShaderType type, ShaderModel shader_model, bool debug, + const std::string_view code, const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main"); + wil::com_ptr_nothrow CompileShaderDXIL(ShaderType type, ShaderModel shader_model, bool debug, + const std::string_view code, const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main"); }; // namespace D3D diff --git a/pcsx2/GS/Renderers/DX12/D3D12ShaderCache.cpp b/pcsx2/GS/Renderers/DX12/D3D12ShaderCache.cpp index 39d004f028..fc3d379265 100644 --- a/pcsx2/GS/Renderers/DX12/D3D12ShaderCache.cpp +++ b/pcsx2/GS/Renderers/DX12/D3D12ShaderCache.cpp @@ -56,7 +56,6 @@ bool D3D12ShaderCache::CacheIndexKey::operator!=(const CacheIndexKey& key) const bool D3D12ShaderCache::Open(D3D::ShaderModel shader_model, bool debug) { - // Only support SM5.1 for now, which is the minimum for D3D12. pxAssert(shader_model >= D3D::ShaderModel::SM51); m_shader_model = shader_model; m_debug = debug; diff --git a/pcsx2/GS/Renderers/DX12/GSDevice12.cpp b/pcsx2/GS/Renderers/DX12/GSDevice12.cpp index 205c3674c4..c1deb00361 100644 --- a/pcsx2/GS/Renderers/DX12/GSDevice12.cpp +++ b/pcsx2/GS/Renderers/DX12/GSDevice12.cpp @@ -152,6 +152,29 @@ bool GSDevice12::SupportsProgrammableSamplePositions() return false; } +D3D_SHADER_MODEL GSDevice12::DetectShaderModelSupport() { + // CheckFeatureSupport will fail if the runtime dosen't support a requested shader model. + // Loop though ranges of valid shader models until the check succeeds. + constexpr std::array, 2> shader_model_ranges{{ + {D3D_SHADER_MODEL_6_5, D3D_SHADER_MODEL_6_0}, + {D3D_SHADER_MODEL_5_1, D3D_SHADER_MODEL_5_1}, + }}; + + for (const std::array& range : shader_model_ranges) + { + for (int i = range[0]; i >= range[1]; i--) + { + D3D12_FEATURE_DATA_SHADER_MODEL shader_model_device = {static_cast(i)}; + const HRESULT hr = m_device->CheckFeatureSupport(D3D12_FEATURE_SHADER_MODEL, &shader_model_device, sizeof(shader_model_device)); + if (SUCCEEDED(hr)) + return shader_model_device.HighestShaderModel; + } + } + + // If the above somehow fails. + return D3D_SHADER_MODEL_5_1; +} + u32 GSDevice12::GetAdapterVendorID() const { if (!m_adapter) @@ -992,7 +1015,7 @@ bool GSDevice12::Create(GSVSyncMode vsync_mode, bool allow_present_throttle) m_tfx_source = std::move(*shader); } - if (!m_shader_cache.Open(D3D::ShaderModel::SM51, GSConfig.UseDebugDevice)) + if (!m_shader_cache.Open(static_cast(m_shader_model), GSConfig.UseDebugDevice)) Console.Warning("D3D12: Shader cache failed to open."); if (!CreateRootSignatures()) @@ -1538,6 +1561,9 @@ bool GSDevice12::CheckFeatures(const u32& vendor_id) DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing_supported, sizeof(allow_tearing_supported)); m_allow_tearing_supported = (SUCCEEDED(hr) && allow_tearing_supported == TRUE); + m_shader_model = DetectShaderModelSupport(); + Console.WriteLnFmt("D3D12: Shader Model: {}.{}", (m_shader_model & 0xF0) >> 4, (m_shader_model & 0xF)); + D3D12_FEATURE_DATA_ARCHITECTURE1 device_architecture1 = {}; hr = m_device->CheckFeatureSupport(D3D12_FEATURE_ARCHITECTURE1, &device_architecture1, sizeof(device_architecture1)); m_uma = SUCCEEDED(hr) && device_architecture1.UMA; diff --git a/pcsx2/GS/Renderers/DX12/GSDevice12.h b/pcsx2/GS/Renderers/DX12/GSDevice12.h index 5a9b545853..98f6eb05a2 100644 --- a/pcsx2/GS/Renderers/DX12/GSDevice12.h +++ b/pcsx2/GS/Renderers/DX12/GSDevice12.h @@ -158,6 +158,8 @@ public: // Partial depth copies require ProgrammableSamplePositions tier 1. bool SupportsProgrammableSamplePositions(); + D3D_SHADER_MODEL DetectShaderModelSupport(); + enum class WaitType { None, ///< Don't wait (async) @@ -380,6 +382,7 @@ private: bool m_allow_tearing_supported = false; bool m_using_allow_tearing = false; bool m_is_exclusive_fullscreen = false; + D3D_SHADER_MODEL m_shader_model = D3D_SHADER_MODEL_5_1; bool m_uma = false; bool m_typed_casting_supported = false; bool m_enhanced_barriers = false;