@@ -21,6 +21,8 @@ using namespace nbl;
2121using namespace nbl ::asset;
2222using Microsoft::WRL::ComPtr;
2323
24+ static constexpr const wchar_t * SHADER_MODEL_PROFILE = L" XX_6_6" ;
25+
2426namespace nbl ::asset::hlsl::impl
2527{
2628 struct DXC {
@@ -52,43 +54,61 @@ CHLSLCompiler::~CHLSLCompiler()
5254}
5355
5456static tcpp::IInputStream* getInputStreamInclude (
55- const IShaderCompiler::CIncludeFinder* _inclFinder,
56- const system::ISystem* _fs,
57- uint32_t _maxInclCnt,
58- const char * _requesting_source,
59- const char * _requested_source,
60- bool _type // true for #include "string"; false for #include <string>
57+ const IShaderCompiler::CIncludeFinder* inclFinder,
58+ const system::ISystem* fs,
59+ uint32_t maxInclCnt,
60+ const char * requestingSource,
61+ const char * requestedSource,
62+ bool isRelative, // true for #include "string"; false for #include <string>
63+ uint32_t lexerLineIndex,
64+ uint32_t leadingLinesImports,
65+ std::vector<std::pair<uint32_t , std::string>>& includeStack
6166)
6267{
6368 std::string res_str;
6469
6570 std::filesystem::path relDir;
66- const bool reqFromBuiltin = builtin::hasPathPrefix (_requesting_source );
67- const bool reqBuiltin = builtin::hasPathPrefix (_requested_source );
71+ const bool reqFromBuiltin = builtin::hasPathPrefix (requestingSource );
72+ const bool reqBuiltin = builtin::hasPathPrefix (requestedSource );
6873 if (!reqFromBuiltin && !reqBuiltin)
6974 {
7075 // While #includ'ing a builtin, one must specify its full path (starting with "nbl/builtin" or "/nbl/builtin").
7176 // This rule applies also while a builtin is #includ`ing another builtin.
7277 // While including a filesystem file it must be either absolute path (or relative to any search dir added to asset::iIncludeHandler; <>-type),
7378 // or path relative to executable's working directory (""-type).
74- relDir = std::filesystem::path (_requesting_source ).parent_path ();
79+ relDir = std::filesystem::path (requestingSource ).parent_path ();
7580 }
76- std::filesystem::path name = _type ? (relDir / _requested_source ) : (_requested_source );
81+ std::filesystem::path name = isRelative ? (relDir / requestedSource ) : (requestedSource );
7782
7883 if (std::filesystem::exists (name) && !reqBuiltin)
7984 name = std::filesystem::absolute (name);
8085
81- if (_type )
82- res_str = _inclFinder ->getIncludeRelative (relDir, _requested_source );
86+ if (isRelative )
87+ res_str = inclFinder ->getIncludeRelative (relDir, requestedSource );
8388 else // shaderc_include_type_standard
84- res_str = _inclFinder ->getIncludeStandard (relDir, _requested_source );
89+ res_str = inclFinder ->getIncludeStandard (relDir, requestedSource );
8590
8691 if (!res_str.size ()) {
8792 return new tcpp::StringInputStream (" #error File not found" );
8893 }
8994
95+ // Figure out what line in the current file this #include was
96+ // That would be the current lexer line, minus the line where the current file was included
97+ uint32_t lineInCurrentFileWithInclude = lexerLineIndex -
98+ // if this is 2 includes deep (include within include), subtract leading import lines
99+ // from the previous include
100+ (includeStack.size () > 1 ? leadingLinesImports : 0 );
101+ auto lastItemInIncludeStack = includeStack.back ();
102+
90103 IShaderCompiler::disableAllDirectivesExceptIncludes (res_str);
91- res_str = IShaderCompiler::encloseWithinExtraInclGuards (std::move (res_str), _maxInclCnt, name.string ().c_str ());
104+ res_str = IShaderCompiler::encloseWithinExtraInclGuards (std::move (res_str), maxInclCnt, name.string ().c_str ());
105+ res_str = res_str + " \n " +
106+ IShaderCompiler::PREPROC_DIRECTIVE_DISABLER + " line " + std::to_string (lineInCurrentFileWithInclude - lastItemInIncludeStack.first - 1 ).c_str () + " \" " + lastItemInIncludeStack.second .c_str () + " \"\n " ;
107+
108+ // Offset the lines this include takes up for subsequent includes
109+ includeStack.back ().first += std::count (res_str.begin (), res_str.end (), ' \n ' );
110+
111+ includeStack.push_back (std::pair<uint32_t , std::string>(lineInCurrentFileWithInclude, IShaderCompiler::escapeFilename (name.string ())));
92112
93113 return new tcpp::StringInputStream (std::move (res_str));
94114}
@@ -100,9 +120,9 @@ class DxcCompilationResult
100120 ComPtr<IDxcBlob> objectBlob;
101121 ComPtr<IDxcResult> compileResult;
102122
103- char * GetErrorMessagesString ()
123+ std::string GetErrorMessagesString ()
104124 {
105- return reinterpret_cast <char *>(errorMessages->GetBufferPointer ());
125+ return std::string ( reinterpret_cast <char *>(errorMessages->GetBufferPointer ()), errorMessages-> GetBufferSize ());
106126 }
107127};
108128
@@ -151,9 +171,17 @@ DxcCompilationResult dxcCompile(const CHLSLCompiler* compiler, nbl::asset::hlsl:
151171 result.compileResult = compileResult;
152172 result.objectBlob = nullptr ;
153173
154- if (!SUCCEEDED (compilationStatus))
174+ auto errorMessagesString = result.GetErrorMessagesString ();
175+ if (SUCCEEDED (compilationStatus))
176+ {
177+ if (errorMessagesString.length () > 0 )
178+ {
179+ options.preprocessorOptions .logger .log (" DXC Compilation Warnings:\n %s" , system::ILogger::ELL_WARNING, errorMessagesString.c_str ());
180+ }
181+ }
182+ else
155183 {
156- options.preprocessorOptions .logger .log (result. GetErrorMessagesString () , system::ILogger::ELL_ERROR);
184+ options.preprocessorOptions .logger .log (" DXC Compilation Failed: \n %s " , system::ILogger::ELL_ERROR, errorMessagesString. c_str () );
157185 return result;
158186 }
159187
@@ -169,13 +197,22 @@ DxcCompilationResult dxcCompile(const CHLSLCompiler* compiler, nbl::asset::hlsl:
169197
170198std::string CHLSLCompiler::preprocessShader (std::string&& code, IShader::E_SHADER_STAGE& stage, const SPreprocessorOptions& preprocessOptions) const
171199{
200+ // Line 1 comes before all the extra defines in the main shader
201+ insertIntoStart (code, std::ostringstream (std::string (IShaderCompiler::PREPROC_DIRECTIVE_ENABLER) + " line 1\n " ));
202+
203+ uint32_t defineLeadingLinesMain = 1 ;
204+ uint32_t leadingLinesImports = IShaderCompiler::encloseWithinExtraInclGuardsLeadingLines (preprocessOptions.maxSelfInclusionCount + 1u );
172205 if (preprocessOptions.extraDefines .size ())
173206 {
174207 insertExtraDefines (code, preprocessOptions.extraDefines );
208+ defineLeadingLinesMain += preprocessOptions.extraDefines .size ();
175209 }
176210
177211 IShaderCompiler::disableAllDirectivesExceptIncludes (code);
178212
213+ // Keep track of the line in the original file where each #include was on each level of the include stack
214+ std::vector<std::pair<uint32_t , std::string>> lineOffsetStack = { std::pair<uint32_t , std::string>(defineLeadingLinesMain, preprocessOptions.sourceIdentifier ) };
215+
179216 tcpp::StringInputStream codeIs = tcpp::StringInputStream (code);
180217 tcpp::Lexer lexer (codeIs);
181218 tcpp::Preprocessor proc (
@@ -188,13 +225,17 @@ std::string CHLSLCompiler::preprocessShader(std::string&& code, IShader::E_SHADE
188225 {
189226 return getInputStreamInclude (
190227 preprocessOptions.includeFinder , m_system.get (), preprocessOptions.maxSelfInclusionCount + 1u ,
191- preprocessOptions.sourceIdentifier .data (), path.c_str (), !isSystemPath
228+ preprocessOptions.sourceIdentifier .data (), path.c_str (), !isSystemPath,
229+ lexer.GetCurrLineIndex (), leadingLinesImports, lineOffsetStack
192230 );
193231 }
194232 else
195233 {
196234 return static_cast <tcpp::IInputStream*>(new tcpp::StringInputStream (std::string (" #error No include handler" )));
197235 }
236+ },
237+ [&]() {
238+ lineOffsetStack.pop_back ();
198239 }
199240 );
200241
@@ -239,6 +280,7 @@ std::string CHLSLCompiler::preprocessShader(std::string&& code, IShader::E_SHADE
239280
240281 auto resolvedString = proc.Process ();
241282 IShaderCompiler::reenableDirectives (resolvedString);
283+
242284 return resolvedString;
243285}
244286
@@ -256,7 +298,16 @@ core::smart_refctd_ptr<ICPUShader> CHLSLCompiler::compileToSPIRV(const char* cod
256298 auto newCode = preprocessShader (code, stage, hlslOptions.preprocessorOptions );
257299
258300 // Suffix is the shader model version
259- std::wstring targetProfile (L" XX_6_2" );
301+ // TODO: Figure out a way to get the shader model version automatically
302+ //
303+ // We can't get it from the DXC library itself, as the different versions and the parsing
304+ // use a weird lexer based system that resolves to a hash, and all of that is in a scoped variable
305+ // (lib/DXIL/DxilShaderModel.cpp:83)
306+ //
307+ // Another option is trying to fetch it from the commandline tool, either from parsing the help message
308+ // or from brute forcing every -T option until one isn't accepted
309+ //
310+ std::wstring targetProfile (SHADER_MODEL_PROFILE);
260311
261312 // Set profile two letter prefix based on stage
262313 switch (stage) {
@@ -289,29 +340,40 @@ core::smart_refctd_ptr<ICPUShader> CHLSLCompiler::compileToSPIRV(const char* cod
289340 return nullptr ;
290341 };
291342
292- LPCWSTR arguments[] = {
293- // These will always be present
343+ std::vector<LPCWSTR> arguments = {
294344 L" -spirv" ,
295345 L" -HV" , L" 2021" ,
296346 L" -T" , targetProfile.c_str (),
297-
298- // These are debug only
299- DXC_ARG_DEBUG,
300- L" -Qembed_debug" ,
301- L" -fspv-debug=vulkan-with-source" ,
302- L" -fspv-debug=file"
303347 };
304348
305- const uint32_t nonDebugArgs = 5 ;
306- const uint32_t allArgs = nonDebugArgs + 4 ;
349+ // If a custom SPIR-V optimizer is specified, use that instead of DXC's spirv-opt.
350+ // This is how we can get more optimizer options.
351+ //
352+ // Optimization is also delegated to SPIRV-Tools. Right now there are no difference between
353+ // optimization levels greater than zero; they will all invoke the same optimization recipe.
354+ // https://github.com/Microsoft/DirectXShaderCompiler/blob/main/docs/SPIR-V.rst#optimization
355+ if (hlslOptions.spirvOptimizer )
356+ {
357+ arguments.push_back (L" -O0" );
358+ }
359+
360+ // Debug only values
361+ if (hlslOptions.genDebugInfo )
362+ {
363+ arguments.insert (arguments.end (), {
364+ DXC_ARG_DEBUG,
365+ L" -Qembed_debug" ,
366+ L" -fspv-debug=vulkan-with-source" ,
367+ L" -fspv-debug=file"
368+ });
369+ }
307370
308- // const CHLSLCompiler* compiler, nbl::asset::hlsl::impl::DXC* compilerTypes, std::string& source, LPCWSTR* args, uint32_t argCount, const CHLSLCompiler::SOptions& options
309371 auto compileResult = dxcCompile (
310372 this ,
311373 m_dxcCompilerTypes,
312374 newCode,
313- &arguments[0 ],
314- hlslOptions. genDebugInfo ? allArgs : nonDebugArgs,
375+ &arguments[0 ],
376+ arguments. size (),
315377 hlslOptions
316378 );
317379
@@ -322,6 +384,11 @@ core::smart_refctd_ptr<ICPUShader> CHLSLCompiler::compileToSPIRV(const char* cod
322384
323385 auto outSpirv = core::make_smart_refctd_ptr<ICPUBuffer>(compileResult.objectBlob ->GetBufferSize ());
324386 memcpy (outSpirv->getPointer (), compileResult.objectBlob ->GetBufferPointer (), compileResult.objectBlob ->GetBufferSize ());
387+
388+ // Optimizer step
389+ if (hlslOptions.spirvOptimizer )
390+ outSpirv = hlslOptions.spirvOptimizer ->optimize (outSpirv.get (), hlslOptions.preprocessorOptions .logger );
391+
325392
326393 return core::make_smart_refctd_ptr<asset::ICPUShader>(std::move (outSpirv), stage, IShader::E_CONTENT_TYPE::ECT_SPIRV, hlslOptions.preprocessorOptions .sourceIdentifier .data ());
327394}
0 commit comments