From 86ae996dcddff26845d9f2da38e26adac43c91bd Mon Sep 17 00:00:00 2001 From: Stenzek Date: Mon, 9 Sep 2024 00:23:30 +1000 Subject: [PATCH] D3D12Device: Use DXC and 12.0 feature level --- src/duckstation-qt/CMakeLists.txt | 5 +- src/util/d3d12_device.cpp | 2 +- src/util/d3d_common.cpp | 143 +++++++++++++++++++++++++- src/util/postprocessing_shader_fx.cpp | 16 ++- src/util/util.props | 2 + 5 files changed, 159 insertions(+), 9 deletions(-) diff --git a/src/duckstation-qt/CMakeLists.txt b/src/duckstation-qt/CMakeLists.txt index c1e504069..2ef7ef404 100644 --- a/src/duckstation-qt/CMakeLists.txt +++ b/src/duckstation-qt/CMakeLists.txt @@ -206,8 +206,9 @@ if(WIN32) ) #set_source_files_properties(${TS_FILES} PROPERTIES OUTPUT_LOCATION "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/translations") - set(DEPS_TO_COPY cpuinfo.dll discord-rpc.dll freetype.dll harfbuzz.dll libjpeg.dll libpng16.dll libsharpyuv.dll libwebp.dll - SDL2.dll shaderc_shared.dll soundtouch.dll spirv-cross-c-shared.dll zlib1.dll zstd.dll) + set(DEPS_TO_COPY cpuinfo.dll discord-rpc.dll dxcompiler.dll dxil.dll freetype.dll harfbuzz.dll jpeg62.dll libpng16.dll + libsharpyuv.dll libwebp.dll lunasvg.dll SDL2.dll shaderc_shared.dll soundtouch.dll spirv-cross-c-shared.dll + zlib1.dll zstd.dll) foreach(DEP ${DEPS_TO_COPY}) list(APPEND DEP_BINS "${CMAKE_PREFIX_PATH}/bin/${DEP}") endforeach() diff --git a/src/util/d3d12_device.cpp b/src/util/d3d12_device.cpp index ea12891e9..58e6f41d6 100644 --- a/src/util/d3d12_device.cpp +++ b/src/util/d3d12_device.cpp @@ -148,7 +148,7 @@ bool D3D12Device::CreateDevice(std::string_view adapter, std::optional exc // Create the actual device. D3D_FEATURE_LEVEL feature_level = D3D_FEATURE_LEVEL_1_0_CORE; - for (D3D_FEATURE_LEVEL try_feature_level : {D3D_FEATURE_LEVEL_11_0}) + for (D3D_FEATURE_LEVEL try_feature_level : {D3D_FEATURE_LEVEL_12_0, D3D_FEATURE_LEVEL_11_0}) { hr = D3D12CreateDevice(m_adapter.Get(), try_feature_level, IID_PPV_ARGS(&m_device)); if (SUCCEEDED(hr)) diff --git a/src/util/d3d_common.cpp b/src/util/d3d_common.cpp index 3e815051d..e9b21a3fb 100644 --- a/src/util/d3d_common.cpp +++ b/src/util/d3d_common.cpp @@ -5,6 +5,7 @@ #include "gpu_device.h" #include "common/assert.h" +#include "common/dynamic_library.h" #include "common/error.h" #include "common/file_system.h" #include "common/gsvector.h" @@ -15,6 +16,7 @@ #include #include +#include #include Log_SetChannel(D3DCommon); @@ -30,6 +32,17 @@ struct FeatureLevelTableEntry }; } // namespace +static std::optional> CompileShaderWithFXC(u32 shader_model, bool debug_device, + GPUShaderStage stage, std::string_view source, + const char* entry_point, Error* error); +static std::optional> CompileShaderWithDXC(u32 shader_model, bool debug_device, + GPUShaderStage stage, std::string_view source, + const char* entry_point, Error* error); +static bool LoadDXCompilerLibrary(Error* error); + +static DynamicLibrary s_dxcompiler_library; +static DxcCreateInstanceProc s_DxcCreateInstance; + static constexpr std::array s_feature_levels = {{ {D3D_FEATURE_LEVEL_1_0_CORE, 100, 40, "D3D_FEATURE_LEVEL_1_0_CORE"}, {D3D_FEATURE_LEVEL_9_1, 910, 40, "D3D_FEATURE_LEVEL_9_1"}, @@ -39,9 +52,9 @@ static constexpr std::array s_feature_levels = {{ {D3D_FEATURE_LEVEL_10_1, 1010, 41, "D3D_FEATURE_LEVEL_10_1"}, {D3D_FEATURE_LEVEL_11_0, 1100, 50, "D3D_FEATURE_LEVEL_11_0"}, {D3D_FEATURE_LEVEL_11_1, 1110, 50, "D3D_FEATURE_LEVEL_11_1"}, - {D3D_FEATURE_LEVEL_12_0, 1200, 50, "D3D_FEATURE_LEVEL_12_0"}, - {D3D_FEATURE_LEVEL_12_1, 1210, 50, "D3D_FEATURE_LEVEL_12_1"}, - {D3D_FEATURE_LEVEL_12_2, 1220, 50, "D3D_FEATURE_LEVEL_12_2"}, + {D3D_FEATURE_LEVEL_12_0, 1200, 60, "D3D_FEATURE_LEVEL_12_0"}, + {D3D_FEATURE_LEVEL_12_1, 1210, 60, "D3D_FEATURE_LEVEL_12_1"}, + {D3D_FEATURE_LEVEL_12_2, 1220, 60, "D3D_FEATURE_LEVEL_12_2"}, }}; } // namespace D3DCommon @@ -400,6 +413,16 @@ std::string D3DCommon::GetDriverVersionFromLUID(const LUID& luid) std::optional> D3DCommon::CompileShader(u32 shader_model, bool debug_device, GPUShaderStage stage, std::string_view source, const char* entry_point, Error* error) +{ + if (shader_model >= 60) + return CompileShaderWithDXC(shader_model, debug_device, stage, source, entry_point, error); + else + return CompileShaderWithFXC(shader_model, debug_device, stage, source, entry_point, error); +} + +std::optional> D3DCommon::CompileShaderWithFXC(u32 shader_model, bool debug_device, + GPUShaderStage stage, std::string_view source, + const char* entry_point, Error* error) { const char* target; switch (shader_model) @@ -465,6 +488,120 @@ std::optional> D3DCommon::CompileShader(u32 shader_model, b return DynamicHeapArray(static_cast(blob->GetBufferPointer()), blob->GetBufferSize()); } +std::optional> D3DCommon::CompileShaderWithDXC(u32 shader_model, bool debug_device, + GPUShaderStage stage, std::string_view source, + const char* entry_point, Error* error) +{ + if (!LoadDXCompilerLibrary(error)) + return {}; + + HRESULT hr; + Microsoft::WRL::ComPtr utils; + if (FAILED(hr = s_DxcCreateInstance(CLSID_DxcUtils, IID_PPV_ARGS(utils.GetAddressOf())))) [[unlikely]] + { + Error::SetHResult(error, "DxcCreateInstance(CLSID_DxcUtils) failed: ", hr); + return {}; + } + + Microsoft::WRL::ComPtr source_blob; + if (FAILED(hr = utils->CreateBlob(source.data(), static_cast(source.size()), DXC_CP_UTF8, + source_blob.GetAddressOf()))) [[unlikely]] + { + Error::SetHResult(error, "CreateBlob() failed: ", hr); + return {}; + } + + Microsoft::WRL::ComPtr compiler; + if (FAILED(hr = s_DxcCreateInstance(CLSID_DxcCompiler, IID_PPV_ARGS(compiler.GetAddressOf())))) [[unlikely]] + { + Error::SetHResult(error, "DxcCreateInstance(CLSID_DxcCompiler) failed: ", hr); + return {}; + } + + const wchar_t* target; + switch (shader_model) + { + case 60: + { + static constexpr std::array(GPUShaderStage::MaxCount)> targets = { + {L"vs_6_0", L"ps_6_0", L"gs_6_0", L"cs_6_0"}}; + target = targets[static_cast(stage)]; + } + break; + + default: + Error::SetStringFmt(error, "Unknown shader model: {}", shader_model); + return {}; + } + + static constexpr const wchar_t* nondebug_arguments[] = { + L"-Qstrip_reflect", + L"-Qstrip_debug", + DXC_ARG_OPTIMIZATION_LEVEL3, + }; + static constexpr const wchar_t* debug_arguments[] = { + L"-Qstrip_reflect", + DXC_ARG_DEBUG, + L"-Qembed_debug", + DXC_ARG_SKIP_OPTIMIZATIONS, + }; + const wchar_t* const* arguments = debug_device ? debug_arguments : nondebug_arguments; + const size_t arguments_size = debug_device ? std::size(debug_arguments) : std::size(nondebug_arguments); + + Microsoft::WRL::ComPtr result; + Microsoft::WRL::ComPtr compile_result; + Microsoft::WRL::ComPtr error_output; + std::string_view error_output_sv; + + hr = compiler->Compile(source_blob.Get(), L"source", StringUtil::UTF8StringToWideString(entry_point).c_str(), target, + const_cast(arguments), static_cast(arguments_size), nullptr, 0, nullptr, + result.GetAddressOf()); + + if (SUCCEEDED(result.As(&compile_result)) && compile_result->HasOutput(DXC_OUT_ERRORS) && + SUCCEEDED(compile_result->GetOutput(DXC_OUT_ERRORS, IID_PPV_ARGS(error_output.GetAddressOf()), nullptr))) + { + error_output_sv = + std::string_view(static_cast(error_output->GetBufferPointer()), error_output->GetBufferSize()); + } + + if (FAILED(hr) || (FAILED(result->GetStatus(&hr)) || FAILED(hr))) + { + Error::SetHResult(error, "Compile() failed: ", hr); + + ERROR_LOG("Failed to compile {} {}:\n{}", GPUShader::GetStageName(stage), shader_model, error_output_sv); + GPUDevice::DumpBadShader(source, error_output_sv); + return {}; + } + + if (!error_output_sv.empty()) + WARNING_LOG("{} {} compiled with warnings:\n{}", GPUShader::GetStageName(stage), shader_model, error_output_sv); + + Microsoft::WRL::ComPtr object_blob; + if (!compile_result->HasOutput(DXC_OUT_OBJECT) || + FAILED(hr = compile_result->GetOutput(DXC_OUT_OBJECT, IID_PPV_ARGS(object_blob.GetAddressOf()), nullptr))) + { + Error::SetHResult(error, "GetOutput(DXC_OUT_OBJECT) failed: ", hr); + return {}; + } + + return DynamicHeapArray(static_cast(object_blob->GetBufferPointer()), object_blob->GetBufferSize()); +} + +bool D3DCommon::LoadDXCompilerLibrary(Error* error) +{ + if (s_dxcompiler_library.IsOpen()) + return true; + + if (!s_dxcompiler_library.Open("dxcompiler.dll", error) || + !s_dxcompiler_library.GetSymbol("DxcCreateInstance", &s_DxcCreateInstance)) + { + s_dxcompiler_library.Close(); + return false; + } + + return true; +} + static constexpr std::array(GPUTexture::Format::MaxCount)> s_format_mapping = {{ // clang-format off diff --git a/src/util/postprocessing_shader_fx.cpp b/src/util/postprocessing_shader_fx.cpp index a42de5ec1..fa6f6d639 100644 --- a/src/util/postprocessing_shader_fx.cpp +++ b/src/util/postprocessing_shader_fx.cpp @@ -82,9 +82,19 @@ static std::tuple, GPUShaderLanguage> Create case RenderAPI::D3D11: case RenderAPI::D3D12: { - return std::make_tuple(std::unique_ptr(reshadefx::create_codegen_hlsl( - (rapi_version < 1100) ? 40 : 50, debug_info, uniforms_to_spec_constants)), - GPUShaderLanguage::HLSL); + // Use SPIR-V -> HLSL -> DXIL for D3D12. DXC can't handle texture parameters, which reshade generates. + if (rapi == RenderAPI::D3D12 && rapi_version >= 1200) + { + return std::make_tuple(std::unique_ptr(reshadefx::create_codegen_spirv( + true, debug_info, uniforms_to_spec_constants, false, false)), + GPUShaderLanguage::SPV); + } + else + { + return std::make_tuple(std::unique_ptr(reshadefx::create_codegen_hlsl( + (rapi_version < 1100) ? 40 : 50, debug_info, uniforms_to_spec_constants)), + GPUShaderLanguage::HLSL); + } } break; #endif diff --git a/src/util/util.props b/src/util/util.props index 0e355a7ad..4f027f045 100644 --- a/src/util/util.props +++ b/src/util/util.props @@ -32,6 +32,8 @@ + +