From 5a6f51a322f09c96b98adb5ea3355b78d606398f Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 21 Jun 2023 16:30:27 +0200 Subject: [PATCH] rhi: d3d12: Use IDxcCompiler for SM 6.0+ Only when available at build time (dxcapi.h) and at run time (dxcompiler.dll, ideally with dxil.dll). The catch is that the latter will not be true in a typical Windows system. Unlike the legacy d3dcompiler_47.dll the dxc stuff is just not there. It is unclear what we can do about it. In the meantime one needs to go to https://github.com/microsoft/DirectXShaderCompiler/releases and get the DLLs. Fixes: QTBUG-114773 Change-Id: I6e68fdd1e47505187036b47d6f3e7fe9cc4ee8dc Reviewed-by: Andy Nichols Reviewed-by: Qt CI Bot --- src/gui/rhi/qrhid3d12.cpp | 149 ++++++++++++++++++++++++++------- src/gui/rhi/qrhid3dhelpers_p.h | 41 ++++++++- 2 files changed, 160 insertions(+), 30 deletions(-) diff --git a/src/gui/rhi/qrhid3d12.cpp b/src/gui/rhi/qrhid3d12.cpp index 857297b79c5..c1d25cecd12 100644 --- a/src/gui/rhi/qrhid3d12.cpp +++ b/src/gui/rhi/qrhid3d12.cpp @@ -4828,9 +4828,14 @@ QD3D12ObjectHandle QD3D12ShaderResourceBindings::createRootSignature(const QD3D1 return QD3D12RootSignature::addToPool(&rhiD->rootSignaturePool, rootSig); } -// For now we mirror exactly what's done in the D3D11 backend, meaning we use -// the old shader compiler (so like fxc, not dxc) to generate shader model 5.0 -// output. Some day this should be moved to the new compiler and DXIL. +// For shader model < 6.0 we do the same as the D3D11 backend: use the old +// compiler (D3DCompile) to generate DXBC, just as qsb does (when -c is passed) +// by invoking fxc, not dxc. For SM >= 6.0 we have to use the new compiler and +// work with DXIL. And that involves IDxcCompiler and needs the presence of +// dxcompiler.dll and dxil.dll at runtime. Plus there's a chance we have +// ancient SDK headers when not using MSVC. So this is heavily optional, +// meaning support for dxc can be disabled both at build time (no dxcapi.h) and +// at run time (no DLLs). static inline void makeHlslTargetString(char target[7], const char stage[3], int version) { @@ -4845,6 +4850,108 @@ static inline void makeHlslTargetString(char target[7], const char stage[3], int target[6] = '\0'; } +static QByteArray legacyCompile(const QShaderCode &hlslSource, const char *target, UINT flags, QString *error) +{ + static const pD3DCompile d3dCompile = QRhiD3D::resolveD3DCompile(); + if (!d3dCompile) { + qWarning("Unable to resolve function D3DCompile()"); + return QByteArray(); + } + + ID3DBlob *bytecode = nullptr; + ID3DBlob *errors = nullptr; + HRESULT hr = d3dCompile(hlslSource.shader().constData(), SIZE_T(hlslSource.shader().size()), + nullptr, nullptr, nullptr, + hlslSource.entryPoint().constData(), target, flags, 0, &bytecode, &errors); + if (FAILED(hr) || !bytecode) { + qWarning("HLSL shader compilation failed: 0x%x", uint(hr)); + if (errors) { + *error = QString::fromUtf8(static_cast(errors->GetBufferPointer()), + int(errors->GetBufferSize())); + errors->Release(); + } + return QByteArray(); + } + + QByteArray result; + result.resize(int(bytecode->GetBufferSize())); + memcpy(result.data(), bytecode->GetBufferPointer(), size_t(result.size())); + bytecode->Release(); + return result; +} + +#ifdef QRHI_D3D12_HAS_DXC +static QByteArray dxcCompile(const QShaderCode &hlslSource, const char *target, UINT flags, QString *error) +{ + static std::pair dxc = QRhiD3D::createDxcCompiler(); + IDxcCompiler *compiler = dxc.first; + if (!compiler) { + qWarning("Unable to instantiate IDxcCompiler. Likely no dxcompiler.dll and dxil.dll present. " + "Bundling these are out of scope for Qt. Try https://github.com/microsoft/DirectXShaderCompiler/releases"); + return QByteArray(); + } + IDxcLibrary *library = dxc.second; + if (!library) + return QByteArray(); + + IDxcBlobEncoding *sourceBlob = nullptr; + HRESULT hr = library->CreateBlobWithEncodingOnHeapCopy(hlslSource.shader().constData(), + UINT32(hlslSource.shader().size()), + DXC_CP_UTF8, + &sourceBlob); + if (FAILED(hr)) { + qWarning("Failed to create source blob for dxc: 0x%x (%s)", + uint(hr), + qPrintable(QSystemError::windowsComString(hr))); + return QByteArray(); + } + + const QString entryPointStr = QString::fromLatin1(hlslSource.entryPoint()); + const QString targetStr = QString::fromLatin1(target); + + IDxcOperationResult *result = nullptr; + hr = compiler->Compile(sourceBlob, + nullptr, + reinterpret_cast(entryPointStr.utf16()), + reinterpret_cast(targetStr.utf16()), + nullptr, 0, nullptr, 0, nullptr, + &result); + sourceBlob->Release(); + if (SUCCEEDED(hr)) + result->GetStatus(&hr); + if (FAILED(hr)) { + qWarning("HLSL shader compilation failed: 0x%x (%s)", + uint(hr), + qPrintable(QSystemError::windowsComString(hr))); + if (result) { + IDxcBlobEncoding *errorsBlob = nullptr; + if (SUCCEEDED(result->GetErrorBuffer(&errorsBlob))) { + if (errorsBlob) { + *error = QString::fromUtf8(static_cast(errorsBlob->GetBufferPointer()), + int(errorsBlob->GetBufferSize())); + errorsBlob->Release(); + } + } + } + return QByteArray(); + } + + IDxcBlob *bytecode = nullptr; + if FAILED(result->GetResult(&bytecode)) { + qWarning("No result from IDxcCompiler: 0x%x (%s)", + uint(hr), + qPrintable(QSystemError::windowsComString(hr))); + return QByteArray(); + } + + QByteArray ba; + ba.resize(int(bytecode->GetBufferSize())); + memcpy(ba.data(), bytecode->GetBufferPointer(), size_t(ba.size())); + bytecode->Release(); + return ba; +} +#endif + static QByteArray compileHlslShaderSource(const QShader &shader, QShader::Variant shaderVariant, UINT flags, @@ -4904,33 +5011,17 @@ static QByteArray compileHlslShaderSource(const QShader &shader, break; } - static const pD3DCompile d3dCompile = QRhiD3D::resolveD3DCompile(); - if (!d3dCompile) { - qWarning("Unable to resolve function D3DCompile()"); - return QByteArray(); + if (key.sourceVersion().version() >= 60) { +#ifdef QRHI_D3D12_HAS_DXC + return dxcCompile(hlslSource, target, flags, error); +#else + qWarning("Attempted to runtime-compile HLSL source code for shader model >= 6.0 " + "but the Qt build has no support for DXC. " + "Rebuild Qt with a recent Windows SDK or switch to an MSVC build."); +#endif } - ID3DBlob *bytecode = nullptr; - ID3DBlob *errors = nullptr; - HRESULT hr = d3dCompile(hlslSource.shader().constData(), SIZE_T(hlslSource.shader().size()), - nullptr, nullptr, nullptr, - hlslSource.entryPoint().constData(), target, flags, 0, &bytecode, &errors); - if (FAILED(hr) || !bytecode) { - qWarning("HLSL shader compilation failed: 0x%x", uint(hr)); - if (errors) { - *error = QString::fromUtf8(static_cast(errors->GetBufferPointer()), - int(errors->GetBufferSize())); - errors->Release(); - } - return QByteArray(); - } - - QByteArray result; - result.resize(int(bytecode->GetBufferSize())); - memcpy(result.data(), bytecode->GetBufferPointer(), size_t(result.size())); - bytecode->Release(); - - return result; + return legacyCompile(hlslSource, target, flags, error); } static inline UINT8 toD3DColorWriteMask(QRhiGraphicsPipeline::ColorMask c) @@ -5229,7 +5320,7 @@ bool QD3D12GraphicsPipeline::create() &error, &shaderKey); if (bytecode.isEmpty()) { - qWarning("HLSL compute shader compilation failed: %s", qPrintable(error)); + qWarning("HLSL graphics shader compilation failed: %s", qPrintable(error)); return false; } diff --git a/src/gui/rhi/qrhid3dhelpers_p.h b/src/gui/rhi/qrhid3dhelpers_p.h index f20c042860c..07fcddb7f75 100644 --- a/src/gui/rhi/qrhid3dhelpers_p.h +++ b/src/gui/rhi/qrhid3dhelpers_p.h @@ -21,6 +21,11 @@ #include #include +#if __has_include() +#include +#define QRHI_D3D12_HAS_DXC +#endif + QT_BEGIN_NAMESPACE namespace QRhiD3D { @@ -32,6 +37,8 @@ inline pD3DCompile resolveD3DCompile() if (library.load()) { if (auto symbol = library.resolve("D3DCompile")) return reinterpret_cast(symbol); + } else { + qWarning("Failed to load D3DCompiler_47/43.dll"); } } return nullptr; @@ -53,13 +60,45 @@ inline IDCompositionDevice *createDirectCompositionDevice() IDCompositionDevice *device = nullptr; HRESULT hr = func(nullptr, __uuidof(IDCompositionDevice), reinterpret_cast(&device)); if (FAILED(hr)) { - qWarning("Failed to Direct Composition device: %s", + qWarning("Failed to create Direct Composition device: %s", qPrintable(QSystemError::windowsComString(hr))); return nullptr; } return device; } +#ifdef QRHI_D3D12_HAS_DXC +inline std::pair createDxcCompiler() +{ + QSystemLibrary dxclib(QStringLiteral("dxcompiler")); + // this will not be in the system library location, hence onlySystemDirectory==false + if (!dxclib.load(false)) { + qWarning("Failed to load dxcompiler.dll"); + return {}; + } + DxcCreateInstanceProc func = reinterpret_cast(dxclib.resolve("DxcCreateInstance")); + if (!func) { + qWarning("Unable to resolve DxcCreateInstance"); + return {}; + } + IDxcCompiler *compiler = nullptr; + HRESULT hr = func(CLSID_DxcCompiler, __uuidof(IDxcCompiler), reinterpret_cast(&compiler)); + if (FAILED(hr)) { + qWarning("Failed to create dxc compiler instance: %s", + qPrintable(QSystemError::windowsComString(hr))); + return {}; + } + IDxcLibrary *library = nullptr; + hr = func(CLSID_DxcLibrary, __uuidof(IDxcLibrary), reinterpret_cast(&library)); + if (FAILED(hr)) { + qWarning("Failed to create dxc library instance: %s", + qPrintable(QSystemError::windowsComString(hr))); + return {}; + } + return { compiler, library }; +} +#endif + } // namespace QT_END_NAMESPACE