diff --git a/src/renderer_d3d12.cpp b/src/renderer_d3d12.cpp index 3123716a9..cc3ad198f 100644 --- a/src/renderer_d3d12.cpp +++ b/src/renderer_d3d12.cpp @@ -3136,7 +3136,7 @@ namespace bgfx { namespace d3d12 bx::Error err; read(&rd, dxbc, &err); - bool patchShader = !dxbc.shader.aon9; + bool patchShader = !dxbc.shader.aon9 && !dxbc.shader.dxil; if (BX_ENABLED(BGFX_CONFIG_DEBUG) && patchShader) { diff --git a/src/shader_dxbc.cpp b/src/shader_dxbc.cpp index 33cf7eb26..4e0693c98 100644 --- a/src/shader_dxbc.cpp +++ b/src/shader_dxbc.cpp @@ -1970,6 +1970,7 @@ namespace bgfx size += bx::read(_reader, _dxbc.header, _err); _dxbc.shader.shex = false; _dxbc.shader.aon9 = false; + _dxbc.shader.dxil = false; for (uint32_t ii = 0; ii < _dxbc.header.numChunks; ++ii) { @@ -2041,6 +2042,14 @@ namespace bgfx size += chunkSize; break; + // Detect if it's DXIL + case BX_MAKEFOURCC('H', 'A', 'S', 'H'): + case BX_MAKEFOURCC('P', 'S', 'V', '0'): + case BX_MAKEFOURCC('D', 'X', 'I', 'L'): + _dxbc.shader.dxil = true; + size += chunkSize; + break; + default: size += chunkSize; BX_ASSERT(false, "UNKNOWN FOURCC %c%c%c%c %d" diff --git a/src/shader_dxbc.h b/src/shader_dxbc.h index 5bd16641c..bd99f3125 100644 --- a/src/shader_dxbc.h +++ b/src/shader_dxbc.h @@ -703,6 +703,7 @@ namespace bgfx stl::vector byteCode; bool shex; bool aon9; + bool dxil; }; struct DxbcSFI0 diff --git a/tools/shaderc/shaderc.cpp b/tools/shaderc/shaderc.cpp index 39e95a03f..00e9b096c 100644 --- a/tools/shaderc/shaderc.cpp +++ b/tools/shaderc/shaderc.cpp @@ -103,6 +103,7 @@ namespace bgfx { ShadingLang::HLSL, 300, "s_3_0" }, { ShadingLang::HLSL, 400, "s_4_0" }, { ShadingLang::HLSL, 500, "s_5_0" }, + { ShadingLang::HLSL, 610, "s_6_1" }, // This is a part of an ugly hack to enable SM6.1 barycentrics { ShadingLang::Metal, 1000, "metal" }, { ShadingLang::PSSL, 1000, "pssl" }, { ShadingLang::SpirV, 1010, "spirv" }, diff --git a/tools/shaderc/shaderc_hlsl.cpp b/tools/shaderc/shaderc_hlsl.cpp index 82a924490..27f96ff6c 100644 --- a/tools/shaderc/shaderc_hlsl.cpp +++ b/tools/shaderc/shaderc_hlsl.cpp @@ -16,6 +16,12 @@ #define COM_NO_WINDOWS_H #include #include + +#include +#include +#include +#include + #include #ifndef D3D_SVF_USED @@ -56,11 +62,19 @@ namespace bgfx { namespace hlsl , _Out_ ID3DBlob** ppStrippedBlob ); + typedef HRESULT(WINAPI* PFN_DXC_CREATE_INSTANCE)( + _In_ REFCLSID rclsid, + _In_ REFIID riid, + _Out_ LPVOID* ppv + ); + PFN_D3D_COMPILE D3DCompile; PFN_D3D_DISASSEMBLE D3DDisassemble; PFN_D3D_REFLECT D3DReflect; PFN_D3D_STRIP_SHADER D3DStripShader; + PFN_DXC_CREATE_INSTANCE DxcCreateInstance; + struct D3DCompiler { const char* fileName; @@ -70,6 +84,7 @@ namespace bgfx { namespace hlsl static const D3DCompiler s_d3dcompiler[] = { // BK - the only different method in interface is GetRequiresFlags at the end // of IID_ID3D11ShaderReflection47 (which is not used anyway). + { "dxcompiler.dll", { 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0 } } }, { "D3DCompiler_47.dll", { 0x8d536ca1, 0x0cca, 0x4956, { 0xa8, 0x37, 0x78, 0x69, 0x63, 0x75, 0x55, 0x84 } } }, { "D3DCompiler_46.dll", { 0x0a233719, 0x3960, 0x4578, { 0x9d, 0x7c, 0x20, 0x3b, 0x8b, 0x1d, 0x9c, 0xc1 } } }, { "D3DCompiler_45.dll", { 0x0a233719, 0x3960, 0x4578, { 0x9d, 0x7c, 0x20, 0x3b, 0x8b, 0x1d, 0x9c, 0xc1 } } }, @@ -84,7 +99,7 @@ namespace bgfx { namespace hlsl { bx::Error messageErr; - for (uint32_t ii = 0; ii < BX_COUNTOF(s_d3dcompiler); ++ii) + for (uint32_t ii = 1; ii < BX_COUNTOF(s_d3dcompiler); ++ii) { const D3DCompiler* compiler = &s_d3dcompiler[ii]; s_d3dcompilerdll = bx::dlopen(compiler->fileName); @@ -121,6 +136,38 @@ namespace bgfx { namespace hlsl return NULL; } + const D3DCompiler* loadD3D12(bx::WriterI* _messageWriter) + { + bx::Error messageErr; + + const D3DCompiler* compiler = &s_d3dcompiler[0]; + s_d3dcompilerdll = bx::dlopen(compiler->fileName); + if (NULL == s_d3dcompilerdll) + { + goto fail; + } + + DxcCreateInstance = (PFN_DXC_CREATE_INSTANCE)bx::dlsym(s_d3dcompilerdll, "DxcCreateInstance"); + + if (NULL == DxcCreateInstance) + { + bx::dlclose(s_d3dcompilerdll); + goto fail; + } + + if (g_verbose) + { + char filePath[bx::kMaxFilePath]; + GetModuleFileNameA( (HMODULE)s_d3dcompilerdll, filePath, sizeof(filePath) ); + BX_TRACE("Loaded %s compiler (%s).", compiler->fileName, filePath); + } + + return compiler; +fail: + bx::write(_messageWriter, &messageErr, "Error: Unable to open dxcompiler.dll shader compiler.\n"); + return NULL; + } + void unload() { bx::dlclose(s_d3dcompilerdll); @@ -250,6 +297,31 @@ namespace bgfx { namespace hlsl return UniformType::Count; } + UniformType::Enum findUniformType(const D3D12_SHADER_TYPE_DESC& constDesc) + { + for (uint32_t ii = 0; ii < BX_COUNTOF(s_uniformRemap); ++ii) + { + const UniformRemap& remap = s_uniformRemap[ii]; + + if (remap.paramClass == constDesc.Class + && remap.paramType == constDesc.Type) + { + if (D3D_SVC_MATRIX_COLUMNS != constDesc.Class) + { + return remap.id; + } + + if (remap.columns == constDesc.Columns + && remap.rows == constDesc.Rows) + { + return remap.id; + } + } + } + + return UniformType::Count; + } + static uint32_t s_optimizationLevelD3D11[4] = { D3DCOMPILE_OPTIMIZATION_LEVEL0, @@ -258,6 +330,14 @@ namespace bgfx { namespace hlsl D3DCOMPILE_OPTIMIZATION_LEVEL3, }; + static const wchar_t* s_optimizationLevelD3D12[4] = + { + L"-O0", + L"-O1", + L"-O2", + L"-O3", + }; + typedef std::vector UniformNameList; static bool isSampler(D3D_SHADER_VARIABLE_TYPE _svt) @@ -559,6 +639,204 @@ namespace bgfx { namespace hlsl return true; } + bool getReflectionDataD3D12(IDxcBlob* _code, bool _vshader, UniformArray& _uniforms, uint8_t& _numAttrs, uint16_t* _attrs, uint16_t& _size, UniformNameList& unusedUniforms, bx::WriterI* _messageWriter) + { + bx::Error messageErr; + + CComPtr reflection; + HRESULT hr = DxcCreateInstance(CLSID_DxcContainerReflection, IID_PPV_ARGS(&reflection)); + if (FAILED(hr)) + { + bx::write(_messageWriter, &messageErr, "Error: Unable to create D3D12 compiler container reflection.\n"); + return false; + } + + hr = reflection->Load(_code); + if (FAILED(hr)) + { + bx::write(_messageWriter, &messageErr, "Error: Unable to load D3D12 compiler container reflection.\n"); + return false; + } + + UINT32 shaderIdx; + hr = reflection->FindFirstPartKind(BX_MAKEFOURCC('D', 'X', 'I', 'L'), &shaderIdx); + if (FAILED(hr)) + { + bx::write(_messageWriter, &messageErr, "Error: Unable to find D3D12 compiler container reflection.\n"); + return false; + } + + CComPtr reflect; + hr = reflection->GetPartReflection(shaderIdx, IID_PPV_ARGS(&reflect)); + if (FAILED(hr)) + { + bx::write(_messageWriter, &messageErr, "Error: Unable to get D3D12 compiler container reflection.\n"); + return false; + } + + D3D12_SHADER_DESC desc; + hr = reflect->GetDesc(&desc); + if (FAILED(hr) ) + { + bx::write(_messageWriter, &messageErr, "Error: ID3D12ShaderReflection::GetDesc failed 0x%08x\n", (uint32_t)hr); + return false; + } + + BX_TRACE("Creator: %s 0x%08x", desc.Creator, desc.Version); + BX_TRACE("Num constant buffers: %d", desc.ConstantBuffers); + + BX_TRACE("Input:"); + + if (_vshader) // Only care about input semantic on vertex shaders + { + for (uint32_t ii = 0; ii < desc.InputParameters; ++ii) + { + D3D12_SIGNATURE_PARAMETER_DESC spd; + reflect->GetInputParameterDesc(ii, &spd); + BX_TRACE("\t%2d: %s%d, vt %d, ct %d, mask %x, reg %d" + , ii + , spd.SemanticName + , spd.SemanticIndex + , spd.SystemValueType + , spd.ComponentType + , spd.Mask + , spd.Register + ); + + const RemapInputSemantic& ris = findInputSemantic(spd.SemanticName, uint8_t(spd.SemanticIndex) ); + if (ris.m_attr != bgfx::Attrib::Count) + { + _attrs[_numAttrs] = bgfx::attribToId(ris.m_attr); + ++_numAttrs; + } + } + } + + BX_TRACE("Output:"); + for (uint32_t ii = 0; ii < desc.OutputParameters; ++ii) + { + D3D12_SIGNATURE_PARAMETER_DESC spd; + reflect->GetOutputParameterDesc(ii, &spd); + BX_TRACE("\t%2d: %s%d, %d, %d", ii, spd.SemanticName, spd.SemanticIndex, spd.SystemValueType, spd.ComponentType); + } + + for (uint32_t ii = 0, num = bx::uint32_min(1, desc.ConstantBuffers); ii < num; ++ii) + { + ID3D12ShaderReflectionConstantBuffer* cbuffer = reflect->GetConstantBufferByIndex(ii); + D3D12_SHADER_BUFFER_DESC bufferDesc; + hr = cbuffer->GetDesc(&bufferDesc); + + _size = (uint16_t)bufferDesc.Size; + + if (SUCCEEDED(hr) ) + { + BX_TRACE("%s, %d, vars %d, size %d" + , bufferDesc.Name + , bufferDesc.Type + , bufferDesc.Variables + , bufferDesc.Size + ); + + for (uint32_t jj = 0; jj < bufferDesc.Variables; ++jj) + { + ID3D12ShaderReflectionVariable* var = cbuffer->GetVariableByIndex(jj); + ID3D12ShaderReflectionType* type = var->GetType(); + D3D12_SHADER_VARIABLE_DESC varDesc; + hr = var->GetDesc(&varDesc); + if (SUCCEEDED(hr) ) + { + D3D12_SHADER_TYPE_DESC constDesc; + hr = type->GetDesc(&constDesc); + if (SUCCEEDED(hr) ) + { + UniformType::Enum uniformType = findUniformType(constDesc); + + if (UniformType::Count != uniformType + && 0 != (varDesc.uFlags & D3D_SVF_USED) ) + { + Uniform un; + un.name = varDesc.Name; + un.type = uniformType; + un.num = uint8_t(constDesc.Elements); + un.regIndex = uint16_t(varDesc.StartOffset); + un.regCount = uint16_t(bx::alignUp(varDesc.Size, 16) / 16); + _uniforms.push_back(un); + + BX_TRACE("\t%s, %d, size %d, flags 0x%08x, %d (used)" + , varDesc.Name + , varDesc.StartOffset + , varDesc.Size + , varDesc.uFlags + , uniformType + ); + } + else + { + if (0 == (varDesc.uFlags & D3D_SVF_USED) ) + { + unusedUniforms.push_back(varDesc.Name); + } + + BX_TRACE("\t%s, unknown type", varDesc.Name); + } + } + } + } + } + } + + BX_TRACE("Bound:"); + for (uint32_t ii = 0; ii < desc.BoundResources; ++ii) + { + D3D12_SHADER_INPUT_BIND_DESC bindDesc; + + hr = reflect->GetResourceBindingDesc(ii, &bindDesc); + if (SUCCEEDED(hr) ) + { + if (D3D_SIT_SAMPLER == bindDesc.Type || D3D_SIT_TEXTURE == bindDesc.Type) + { + BX_TRACE("\t%s, %d, %d, %d" + , bindDesc.Name + , bindDesc.Type + , bindDesc.BindPoint + , bindDesc.BindCount + ); + + bx::StringView end = bx::strFind(bindDesc.Name, "Sampler"); + if (end.isEmpty()) + end = bx::strFind(bindDesc.Name, "Texture"); + + if (!end.isEmpty() ) + { + Uniform un; + un.name.assign(bindDesc.Name, (end.getPtr() - bindDesc.Name) ); + un.type = UniformType::Enum(kUniformSamplerBit | UniformType::Sampler); + un.num = 1; + un.regIndex = uint16_t(bindDesc.BindPoint); + un.regCount = uint16_t(bindDesc.BindCount); + _uniforms.push_back(un); + } + } + else + { + BX_TRACE("\t%s, unknown bind data", bindDesc.Name); + } + } + } + + if (NULL != reflection) + { + reflection.Release(); + } + + if (NULL != reflect) + { + reflect.Release(); + } + + return true; + } + static bool compile(const Options& _options, uint32_t _version, const std::string& _code, bx::WriterI* _shaderWriter, bx::WriterI* _messageWriter, bool _firstPass) { bx::Error messageErr; @@ -838,10 +1116,309 @@ namespace bgfx { namespace hlsl return result; } + static bool compileD3D12(const Options& _options, uint32_t _version, const std::string& _code, bx::WriterI* _shaderWriter, bx::WriterI* _messageWriter, bool _firstPass) + { + bx::Error messageErr; + + const char* profile = _options.profile.c_str(); + + if (profile[0] == '\0') + { + bx::write(_messageWriter, &messageErr, "Error: Shader profile must be specified.\n"); + return false; + } + + char profileAndType[8] = {}; + profileAndType[0] = (_options.shaderType == 'f') ? 'p' : _options.shaderType; + bx::strCat(profileAndType, BX_COUNTOF(profileAndType), profile); + + s_compiler = loadD3D12(_messageWriter); + + std::vector arguments; + + bool result = false; + bool debug = _options.debugInformation; + + debug ? arguments.emplace_back(L"-Zi") : 0; + //arguments.emplace_back(L"-Gec"); // Deprecated + _options.avoidFlowControl ? arguments.emplace_back(L"-Gfa") : 0; + _options.preferFlowControl ? arguments.emplace_back(L"-Gfp") : 0; + + bool werror = _options.warningsAreErrors; + + werror ? arguments.emplace_back(L"-WX") : 0; + + if (_options.optimize ) + { + uint32_t optimization = bx::uint32_min(_options.optimizationLevel, BX_COUNTOF(s_optimizationLevelD3D12) - 1); + arguments.emplace_back(s_optimizationLevelD3D12[optimization]); + } + else + { + arguments.emplace_back(L"-Od"); + } + + BX_TRACE("Profile: %s", profile); + + CComPtr code; + CComPtr errorMsg; + + // Output preprocessed shader so that HLSL can be debugged via GPA + // or PIX. Compiling through memory won't embed preprocessed shader + // file path. + std::string hlslfp; + + if (debug) + { + hlslfp = _options.outputFilePath + ".hlsl"; + writeFile(hlslfp.c_str(), _code.c_str(), (int32_t)_code.size() ); + } + + bx::ErrorAssert err; + + CComPtr library; + HRESULT hr = DxcCreateInstance(CLSID_DxcLibrary, IID_PPV_ARGS(&library)); + if (FAILED(hr)) + { + bx::write(_messageWriter, &messageErr, "Error: Unable to create D3D12 compiler library.\n"); + return false; + } + + CComPtr compiler; + hr = DxcCreateInstance(CLSID_DxcCompiler, IID_PPV_ARGS(&compiler)); + if (FAILED(hr)) + { + bx::write(_messageWriter, &messageErr, "Error: Unable to create D3D12 compiler.\n"); + return false; + } + + CComPtr sourceBlob; + hr = library->CreateBlobWithEncodingFromPinned(_code.c_str(), _code.size(), CP_UTF8, &sourceBlob); + if (FAILED(hr)) + { + bx::write(_messageWriter, &messageErr, "Error: Unable to create D3D12 compiler source blob.\n"); + return false; + } + + // DXC compiler only accepts wide strings, so we convert! + USES_CONVERSION; + LPCWSTR whlslfp = A2W(hlslfp.c_str()); + LPCWSTR wprofileAndType = A2W(profileAndType); + + CComPtr compileResult; + hr = compiler->Compile( + sourceBlob, + whlslfp, + L"main", + wprofileAndType, + arguments.data(), arguments.size(), + NULL, 0, + NULL, + &compileResult + ); + + if(SUCCEEDED(hr)) + { + compileResult->GetStatus(&hr); + } + if(FAILED(hr)) + { + if(compileResult) + { + hr = compileResult->GetErrorBuffer(&errorMsg); + if(SUCCEEDED(hr) && errorMsg) + { + const char* log = (char*)errorMsg->GetBufferPointer(); + + bx::write(_messageWriter, &messageErr, "Error: D3D12 Compile failed:\n%s\n", log); + return false; + } + } + } + + compileResult->GetResult(&code); + + UniformArray uniforms; + UniformNameList unusedUniforms; + uint8_t numAttrs = 0; + uint16_t attrs[bgfx::Attrib::Count]; + uint16_t size = 0; + + if (!getReflectionDataD3D12(code, profileAndType[0] == 'v', uniforms, numAttrs, attrs, size, unusedUniforms, _messageWriter) ) + { + bx::write(_messageWriter, &messageErr, "Error: Unable to get D3D12 reflection data.\n"); + goto error; + } + + if (_firstPass + && unusedUniforms.size() > 0) + { + // first time through, we just find unused uniforms and get rid of them + std::string output; + bx::LineReader reader(_code.c_str() ); + while (!reader.isDone() ) + { + bx::StringView strLine = reader.next(); + bool found = false; + + for (UniformNameList::iterator it = unusedUniforms.begin(), itEnd = unusedUniforms.end(); it != itEnd; ++it) + { + bx::StringView str = strFind(strLine, "uniform "); + if (str.isEmpty() ) + { + continue; + } + + // matching lines like: uniform u_name; + // we want to replace "uniform" with "static" so that it's no longer + // included in the uniform blob that the application must upload + // we can't just remove them, because unused functions might still reference + // them and cause a compile error when they're gone + if (!bx::findIdentifierMatch(strLine, it->c_str() ).isEmpty() ) + { + output.append(strLine.getPtr(), str.getPtr() ); + output += "static "; + output.append(str.getTerm(), strLine.getTerm() ); + output += "\n"; + found = true; + + unusedUniforms.erase(it); + break; + } + } + + if (!found) + { + output.append(strLine.getPtr(), strLine.getTerm() ); + output += "\n"; + } + } + + // recompile with the unused uniforms converted to statics + return compileD3D12(_options, _version, output.c_str(), _shaderWriter, _messageWriter, false); + } + + { + uint16_t count = (uint16_t)uniforms.size(); + bx::write(_shaderWriter, count, &err); + + uint32_t fragmentBit = profileAndType[0] == 'p' ? kUniformFragmentBit : 0; + for (UniformArray::const_iterator it = uniforms.begin(); it != uniforms.end(); ++it) + { + const Uniform& un = *it; + uint8_t nameSize = (uint8_t)un.name.size(); + bx::write(_shaderWriter, nameSize, &err); + bx::write(_shaderWriter, un.name.c_str(), nameSize, &err); + uint8_t type = uint8_t(un.type | fragmentBit); + bx::write(_shaderWriter, type, &err); + bx::write(_shaderWriter, un.num, &err); + bx::write(_shaderWriter, un.regIndex, &err); + bx::write(_shaderWriter, un.regCount, &err); + bx::write(_shaderWriter, un.texComponent, &err); + bx::write(_shaderWriter, un.texDimension, &err); + bx::write(_shaderWriter, un.texFormat, &err); + + BX_TRACE("%s, %s, %d, %d, %d" + , un.name.c_str() + , getUniformTypeName(UniformType::Enum(un.type & ~kUniformMask)) + , un.num + , un.regIndex + , un.regCount + ); + } + } + + { + arguments.emplace_back(L"-Qstrip_debug"); + arguments.emplace_back(L"-Qstrip_reflect"); + + compileResult.Release(); + + CComPtr stripped; + hr = compiler->Compile( + sourceBlob, + whlslfp, + L"main", + wprofileAndType, + arguments.data(), arguments.size(), + NULL, 0, + NULL, + &compileResult + ); + + if (SUCCEEDED(hr) ) + { + compileResult->GetResult(&stripped); + code.Release(); + code = stripped; + } + + sourceBlob.Release(); + compileResult.Release(); + } + + { + uint32_t shaderSize = uint32_t(code->GetBufferSize() ); + bx::write(_shaderWriter, shaderSize, &err); + bx::write(_shaderWriter, code->GetBufferPointer(), shaderSize, &err); + uint8_t nul = 0; + bx::write(_shaderWriter, nul, &err); + } + + if (_version >= 400) + { + bx::write(_shaderWriter, numAttrs, &err); + bx::write(_shaderWriter, attrs, numAttrs*sizeof(uint16_t), &err); + + bx::write(_shaderWriter, size, &err); + } + + if (_options.disasm ) + { + CComPtr disasm; + compiler->Disassemble(code, &disasm); + + if (NULL != disasm) + { + std::string disasmfp = _options.outputFilePath + ".disasm"; + + writeFile(disasmfp.c_str(), disasm->GetBufferPointer(), (uint32_t)disasm->GetBufferSize() ); + disasm.Release(); + } + } + + if (NULL != library) + { + library.Release(); + } + + if (NULL != compiler) + { + compiler.Release(); + } + + if (NULL != errorMsg) + { + errorMsg.Release(); + } + + result = true; + + error: + code.Release(); + unload(); + return result; + } + } // namespace hlsl bool compileHLSLShader(const Options& _options, uint32_t _version, const std::string& _code, bx::WriterI* _shaderWriter, bx::WriterI* _messageWriter) { + if (_version >= 600) + { + return hlsl::compileD3D12(_options, _version, _code, _shaderWriter, _messageWriter, true); + } + return hlsl::compile(_options, _version, _code, _shaderWriter, _messageWriter, true); }