Skip to content

Commit 3759b35

Browse files
D3D12: Switched to dxcompiler to produce DXIL shader binaries. (#1664)
- Bumped bgfx.cmake to pick up bgfx D3D12 fixes. - Removed the `BGFX_PLATFORM_SUPPORTS_DXIL=0` override. - Split ShaderCompilerD3D.cpp into ShaderCompilerDXBC.cpp (D3D11, legacy D3DCompile producing SM 5.0 DXBC) and ShaderCompilerDXIL.cpp (D3D12, IDxcCompiler3 producing SM 6.0 DXIL). - Canvas polyfill: renamed dx11/ shader blob directory to dxbc/ and added dxil/ precompiled shaders; nanovg_babylon.cpp and nanovg_filterstack.cpp include both variants. shaderc.cmake emits DXBC (s_5_0) and DXIL (s_6_0) outputs on Windows. - Dropped the half-texel workaround in fs_boxblur.sc, vs_fspass.sc and vs_nanovg_fill.sc; the NEED_HALF_TEXEL path only mattered for pre-SM4 D3D9 which is no longer a target. - Playground config: removed D3D12 exclusions for Native Canvas, "Glow layer and LODs", and Dynamic Texture context clip, which now render correctly. --------- Co-authored-by: Branimir Karadzic <branimirkaradzic@gmail.com>
1 parent 91d0ead commit 3759b35

36 files changed

Lines changed: 2597 additions & 1393 deletions

Apps/Playground/Scripts/config.json

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,7 @@
1616
{
1717
"title": "Native Canvas",
1818
"playgroundId": "#TKVFSA#7",
19-
"referenceImage": "native-canvas.png",
20-
"excludedGraphicsApis": [ "D3D12" ],
21-
"comments": {
22-
"D3D12": "2D Path not rendered"
23-
}
19+
"referenceImage": "native-canvas.png"
2420
},
2521
{
2622
"title": "EXR Loader",
@@ -139,10 +135,9 @@
139135
"title": "Glow layer and LODs",
140136
"playgroundId": "#UNS6ZV#2",
141137
"renderCount": 50,
142-
"excludedGraphicsApis": [ "D3D12", "Metal" ],
138+
"excludedGraphicsApis": [ "Metal" ],
143139
"referenceImage": "glowlayerandlods.png",
144140
"comments": {
145-
"D3D12": "Mapping a texture fails with error HRESULT 0x887A0005",
146141
"Metal": "Result doesn't match reference"
147142
}
148143
},
@@ -155,11 +150,7 @@
155150
{
156151
"title": "Dynamic Texture context clip",
157152
"playgroundId": "#FU0ES5#47",
158-
"referenceImage": "dynamicTextureClip.png",
159-
"excludedGraphicsApis": [ "D3D12" ],
160-
"comments": {
161-
"D3D12": "Incorrect rendering of clipped texture"
162-
}
153+
"referenceImage": "dynamicTextureClip.png"
163154
},
164155
{
165156
"title": "GUI3D SpherePanel",

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ FetchContent_Declare(base-n
3535
EXCLUDE_FROM_ALL)
3636
FetchContent_Declare(bgfx.cmake
3737
GIT_REPOSITORY https://github.com/BabylonJS/bgfx.cmake.git
38-
GIT_TAG 53d884a6e221f96fade72a15e0ad5f92ec7b66b5
38+
GIT_TAG be466af2a964bf2d2d28fdaf1543d89129c7fe21
3939
EXCLUDE_FROM_ALL)
4040
FetchContent_Declare(CMakeExtensions
4141
GIT_REPOSITORY https://github.com/BabylonJS/CMakeExtensions.git

Dependencies/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ target_compile_definitions(bgfx PRIVATE BGFX_CONFIG_MIN_UNIFORM_BUFFER_SIZE=4096
5252
target_compile_definitions(bgfx PRIVATE BGFX_CONFIG_UNIFORM_BUFFER_RESIZE_THRESHOLD_SIZE=256)
5353
target_compile_definitions(bgfx PRIVATE BGFX_CONFIG_UNIFORM_BUFFER_RESIZE_INCREMENT_SIZE=1024)
5454
target_compile_definitions(bgfx PUBLIC BGFX_PLATFORM_SUPPORTS_WGSL=0)
55-
target_compile_definitions(bgfx PUBLIC BGFX_PLATFORM_SUPPORTS_DXIL=0)
5655

5756
# Temporary disable uniform debug.
5857
target_compile_definitions(bgfx PRIVATE BGFX_CONFIG_DEBUG_UNIFORM=0)

Plugins/ShaderCompiler/CMakeLists.txt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ set(SOURCES
55
"Source/ShaderCompilerTraversers.cpp"
66
"Source/ShaderCompilerTraversers.h")
77

8-
if(GRAPHICS_API STREQUAL "D3D11" OR GRAPHICS_API STREQUAL "D3D12")
9-
set(SOURCES ${SOURCES} "Source/ShaderCompilerD3D.cpp")
8+
if(GRAPHICS_API STREQUAL "D3D11")
9+
set(SOURCES ${SOURCES} "Source/ShaderCompilerDXBC.cpp")
10+
elseif(GRAPHICS_API STREQUAL "D3D12")
11+
set(SOURCES ${SOURCES} "Source/ShaderCompilerDXIL.cpp")
1012
else()
1113
set(SOURCES ${SOURCES} "Source/ShaderCompiler${GRAPHICS_API}.cpp")
1214
endif()
@@ -38,7 +40,7 @@ elseif(TARGET spirv-cross-glsl)
3840
PRIVATE spirv-cross-glsl)
3941
endif()
4042

41-
if(WIN32)
43+
if(GRAPHICS_API STREQUAL "D3D11")
4244
target_link_libraries(ShaderCompiler
4345
PRIVATE "d3dcompiler.lib")
4446
endif()

Plugins/ShaderCompiler/Source/ShaderCompilerD3D.cpp renamed to Plugins/ShaderCompiler/Source/ShaderCompilerDXBC.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ namespace
4848
std::string hlsl = compiler->compile();
4949

5050
Microsoft::WRL::ComPtr<ID3DBlob> errorMsgs;
51-
const char* target = stage == EShLangVertex ? "vs_4_0" : "ps_4_0";
51+
const char* target = stage == EShLangVertex ? "vs_5_0" : "ps_5_0";
5252

5353
UINT flags = 0;
5454

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
#include <Babylon/Plugins/ShaderCompiler.h>
2+
3+
#include "ShaderCompilerCommon.h"
4+
#include "ShaderCompilerTraversers.h"
5+
#include <bgfx/bgfx.h>
6+
#include <glslang/Public/ShaderLang.h>
7+
#include <glslang/Public/ResourceLimits.h>
8+
#include <SPIRV/GlslangToSpv.h>
9+
#include <spirv_parser.hpp>
10+
#include <spirv_hlsl.hpp>
11+
#include <wrl/client.h>
12+
#include <dxcapi.h>
13+
14+
namespace
15+
{
16+
void AddShader(glslang::TProgram& program, glslang::TShader& shader, std::string_view source)
17+
{
18+
const std::array<const char*, 1> sources{source.data()};
19+
shader.setStrings(sources.data(), gsl::narrow_cast<int>(sources.size()));
20+
21+
auto defaultTBuiltInResource = GetDefaultResources();
22+
23+
if (!shader.parse(defaultTBuiltInResource, 310, EProfile::EEsProfile, true, true, EShMsgDefault))
24+
{
25+
throw std::runtime_error{shader.getInfoLog()};
26+
}
27+
28+
program.addShader(&shader);
29+
}
30+
31+
struct DxcCompilerState
32+
{
33+
// Order matters: ComPtrs must be destroyed before FreeLibrary so COM
34+
// object vtables (which live inside dxcompiler.dll) remain valid during
35+
// Release(). Members are destroyed in reverse declaration order.
36+
struct ModuleDeleter
37+
{
38+
void operator()(HMODULE m) const noexcept
39+
{
40+
if (m != nullptr)
41+
{
42+
FreeLibrary(m);
43+
}
44+
}
45+
};
46+
std::unique_ptr<std::remove_pointer_t<HMODULE>, ModuleDeleter> Module;
47+
Microsoft::WRL::ComPtr<IDxcCompiler3> Compiler;
48+
};
49+
50+
// Thread-safe lazy initialization via C++11 magic statics.
51+
DxcCompilerState& GetDxcCompiler()
52+
{
53+
static DxcCompilerState state = []() {
54+
DxcCompilerState s;
55+
56+
s.Module.reset(LoadLibraryExW(L"dxcompiler.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32 | LOAD_LIBRARY_SEARCH_APPLICATION_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS));
57+
if (!s.Module)
58+
{
59+
throw std::runtime_error{"Failed to load dxcompiler.dll"};
60+
}
61+
62+
auto createInstance = reinterpret_cast<DxcCreateInstanceProc>(
63+
GetProcAddress(s.Module.get(), "DxcCreateInstance"));
64+
if (!createInstance)
65+
{
66+
throw std::runtime_error{"Failed to find DxcCreateInstance in dxcompiler.dll"};
67+
}
68+
69+
if (FAILED(createInstance(CLSID_DxcCompiler, IID_PPV_ARGS(&s.Compiler))))
70+
{
71+
throw std::runtime_error{"Failed to create IDxcCompiler3"};
72+
}
73+
74+
return s;
75+
}();
76+
return state;
77+
}
78+
79+
std::pair<std::unique_ptr<spirv_cross::Parser>, std::unique_ptr<spirv_cross::Compiler>> CompileShader(glslang::TProgram& program, EShLanguage stage, gsl::span<const spirv_cross::HLSLVertexAttributeRemap> attributes, IDxcBlob** blob)
80+
{
81+
std::vector<uint32_t> spirv;
82+
glslang::GlslangToSpv(*program.getIntermediate(stage), spirv);
83+
84+
auto parser = std::make_unique<spirv_cross::Parser>(std::move(spirv));
85+
parser->parse();
86+
87+
auto compiler = std::make_unique<spirv_cross::CompilerHLSL>(parser->get_parsed_ir());
88+
89+
compiler->set_hlsl_options({40, true});
90+
91+
for (const auto& attribute : attributes)
92+
{
93+
compiler->add_vertex_attribute_remap(attribute);
94+
}
95+
96+
std::string hlsl = compiler->compile();
97+
98+
auto& dxc = GetDxcCompiler();
99+
100+
const wchar_t* target = stage == EShLangVertex ? L"vs_6_0" : L"ps_6_0";
101+
102+
std::vector<LPCWSTR> args = {L"-E", L"main", L"-T", target};
103+
#ifdef _DEBUG
104+
args.push_back(L"-Zi");
105+
args.push_back(L"-Od");
106+
#endif
107+
108+
DxcBuffer sourceBuffer{};
109+
sourceBuffer.Ptr = hlsl.data();
110+
sourceBuffer.Size = hlsl.size();
111+
sourceBuffer.Encoding = DXC_CP_UTF8;
112+
113+
Microsoft::WRL::ComPtr<IDxcResult> result;
114+
if (FAILED(dxc.Compiler->Compile(
115+
&sourceBuffer,
116+
args.data(),
117+
static_cast<UINT32>(args.size()),
118+
nullptr,
119+
IID_PPV_ARGS(&result))))
120+
{
121+
throw std::runtime_error{"DXC compilation call failed"};
122+
}
123+
124+
HRESULT status;
125+
result->GetStatus(&status);
126+
if (FAILED(status))
127+
{
128+
Microsoft::WRL::ComPtr<IDxcBlobUtf8> errors;
129+
result->GetOutput(DXC_OUT_ERRORS, IID_PPV_ARGS(&errors), nullptr);
130+
throw std::runtime_error{errors && errors->GetStringLength() > 0
131+
? errors->GetStringPointer()
132+
: "DXC compilation failed"};
133+
}
134+
135+
if (FAILED(result->GetOutput(DXC_OUT_OBJECT, IID_PPV_ARGS(blob), nullptr)) || *blob == nullptr)
136+
{
137+
throw std::runtime_error{"DXC did not produce a shader object"};
138+
}
139+
140+
return {std::move(parser), std::move(compiler)};
141+
}
142+
}
143+
144+
namespace Babylon::Plugins
145+
{
146+
using namespace ShaderCompilerCommon;
147+
148+
ShaderCompiler::ShaderCompiler()
149+
{
150+
glslang::InitializeProcess();
151+
}
152+
153+
ShaderCompiler::~ShaderCompiler()
154+
{
155+
glslang::FinalizeProcess();
156+
}
157+
158+
Graphics::BgfxShaderInfo ShaderCompiler::Compile(std::string_view vertexSource, std::string_view fragmentSource)
159+
{
160+
glslang::TProgram program;
161+
162+
glslang::TShader vertexShader{EShLangVertex};
163+
AddShader(program, vertexShader, ProcessSamplerFlip(ProcessShaderCoordinates(vertexSource)));
164+
165+
glslang::TShader fragmentShader{EShLangFragment};
166+
AddShader(program, fragmentShader, ProcessSamplerFlip(fragmentSource));
167+
168+
glslang::SpvVersion spv{};
169+
spv.spv = 0x10000;
170+
vertexShader.getIntermediate()->setSpv(spv);
171+
fragmentShader.getIntermediate()->setSpv(spv);
172+
173+
if (!program.link(EShMsgDefault))
174+
{
175+
throw std::runtime_error{program.getInfoLog()};
176+
}
177+
178+
ShaderCompilerTraversers::IdGenerator ids{};
179+
auto cutScope = ShaderCompilerTraversers::ChangeUniformTypes(program, ids);
180+
auto utstScope = ShaderCompilerTraversers::MoveNonSamplerUniformsIntoStruct(program, ids);
181+
std::map<std::string, std::string> vertexAttributeRenaming = {};
182+
ShaderCompilerTraversers::AssignLocationsAndNamesToVertexVaryingsD3D(program, ids, vertexAttributeRenaming);
183+
ShaderCompilerTraversers::SplitSamplersIntoSamplersAndTextures(program, ids);
184+
ShaderCompilerTraversers::InvertYDerivativeOperands(program);
185+
186+
// clang-format off
187+
static const spirv_cross::HLSLVertexAttributeRemap attributes[] = {
188+
{bgfx::Attrib::Position, "POSITION" },
189+
{bgfx::Attrib::Normal, "NORMAL" },
190+
{bgfx::Attrib::Tangent, "TANGENT" },
191+
{bgfx::Attrib::Color0, "COLOR" },
192+
{bgfx::Attrib::Indices, "BLENDINDICES"},
193+
{bgfx::Attrib::Weight, "BLENDWEIGHT" },
194+
{bgfx::Attrib::TexCoord0, "TEXCOORD0" },
195+
{bgfx::Attrib::TexCoord1, "TEXCOORD1" },
196+
{bgfx::Attrib::TexCoord2, "TEXCOORD2" },
197+
{bgfx::Attrib::TexCoord3, "TEXCOORD3" },
198+
{bgfx::Attrib::TexCoord4, "TEXCOORD4" },
199+
{bgfx::Attrib::TexCoord5, "TEXCOORD5" },
200+
{bgfx::Attrib::TexCoord6, "TEXCOORD6" },
201+
{bgfx::Attrib::TexCoord7, "TEXCOORD7" },
202+
};
203+
// clang-format on
204+
205+
Microsoft::WRL::ComPtr<IDxcBlob> vertexBlob;
206+
auto [vertexParser, vertexCompiler] = CompileShader(program, EShLangVertex, attributes, &vertexBlob);
207+
ShaderInfo vertexShaderInfo{
208+
std::move(vertexParser),
209+
std::move(vertexCompiler),
210+
gsl::make_span(static_cast<uint8_t*>(vertexBlob->GetBufferPointer()), vertexBlob->GetBufferSize()),
211+
std::move(vertexAttributeRenaming)};
212+
213+
Microsoft::WRL::ComPtr<IDxcBlob> fragmentBlob;
214+
auto [fragmentParser, fragmentCompiler] = CompileShader(program, EShLangFragment, {}, &fragmentBlob);
215+
ShaderInfo fragmentShaderInfo{
216+
std::move(fragmentParser),
217+
std::move(fragmentCompiler),
218+
gsl::make_span(static_cast<uint8_t*>(fragmentBlob->GetBufferPointer()), fragmentBlob->GetBufferSize()),
219+
{}};
220+
221+
return CreateBgfxShader(std::move(vertexShaderInfo), std::move(fragmentShaderInfo));
222+
}
223+
}

0 commit comments

Comments
 (0)