Skip to content

Commit c86f8e1

Browse files
committed
Fix Wave include paths and failure dumps
1 parent c6e5d16 commit c86f8e1

4 files changed

Lines changed: 300 additions & 63 deletions

File tree

include/nbl/asset/utils/IShaderCompiler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ class NBL_API2 IShaderCompiler : public core::IReferenceCounted
136136
E_SPIRV_VERSION targetSpirvVersion = E_SPIRV_VERSION::ESV_1_6;
137137
bool depfile = false;
138138
system::path depfilePath = {};
139+
std::string* partialOutputOnFailure = nullptr;
139140
};
140141

141142
// https://github.com/microsoft/DirectXShaderCompiler/blob/main/docs/SPIR-V.rst#debugging

src/nbl/asset/utils/CWaveStringResolver.cpp

Lines changed: 172 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,22 @@ using namespace nbl::asset;
4949

5050
namespace
5151
{
52+
constexpr size_t kWavePartialOutputTailMaxChars = 4096ull;
53+
constexpr size_t kWavePartialOutputTailMaxLines = 16ull;
54+
constexpr size_t kWaveTokenPreviewMaxChars = 160ull;
55+
56+
struct WaveRenderProgress
57+
{
58+
core::string output;
59+
std::optional<nbl::wave::context::position_type> previousPosition = std::nullopt;
60+
bool previousWasExplicitWhitespace = false;
61+
size_t emittedTokenCount = 0ull;
62+
std::string lastTokenFile;
63+
int lastTokenLine = 0;
64+
int lastTokenColumn = 0;
65+
std::string lastTokenValue;
66+
};
67+
5268
std::string getLineSnippet(std::string_view text, const int lineNo)
5369
{
5470
if (lineNo <= 0)
@@ -85,8 +101,100 @@ std::string makeCaretLine(const int columnNo)
85101
return std::string(static_cast<size_t>(columnNo - 1), ' ') + '^';
86102
}
87103

104+
size_t countLogicalLines(const std::string_view text)
105+
{
106+
if (text.empty())
107+
return 0ull;
108+
109+
size_t lines = 0ull;
110+
for (const auto ch : text)
111+
{
112+
if (ch == '\n')
113+
++lines;
114+
}
115+
116+
if (text.back() != '\n')
117+
++lines;
118+
return lines;
119+
}
120+
121+
std::string truncateEscapedPreview(std::string value, const size_t maxChars)
122+
{
123+
if (value.size() <= maxChars)
124+
return value;
125+
126+
if (maxChars <= 3ull)
127+
return value.substr(0ull, maxChars);
128+
129+
value.resize(maxChars - 3ull);
130+
value += "...";
131+
return value;
132+
}
133+
134+
std::string indentMultiline(std::string_view text, std::string_view indent)
135+
{
136+
if (text.empty())
137+
return {};
138+
139+
std::string out;
140+
out.reserve(text.size() + indent.size() * 4ull);
141+
142+
size_t lineStart = 0ull;
143+
while (lineStart < text.size())
144+
{
145+
out.append(indent.data(), indent.size());
146+
147+
const auto lineEnd = text.find('\n', lineStart);
148+
if (lineEnd == std::string_view::npos)
149+
{
150+
out.append(text.data() + lineStart, text.size() - lineStart);
151+
break;
152+
}
153+
154+
out.append(text.data() + lineStart, lineEnd - lineStart + 1ull);
155+
lineStart = lineEnd + 1ull;
156+
}
157+
158+
return out;
159+
}
160+
161+
std::string makeOutputTail(std::string_view text)
162+
{
163+
if (text.empty())
164+
return {};
165+
166+
size_t start = text.size();
167+
size_t chars = 0ull;
168+
size_t newlines = 0ull;
169+
while (start > 0ull)
170+
{
171+
--start;
172+
++chars;
173+
if (text[start] == '\n')
174+
{
175+
++newlines;
176+
if (newlines > kWavePartialOutputTailMaxLines)
177+
{
178+
++start;
179+
break;
180+
}
181+
}
182+
183+
if (chars >= kWavePartialOutputTailMaxChars)
184+
break;
185+
}
186+
187+
std::string tail;
188+
if (start > 0ull)
189+
tail = "[truncated]\n";
190+
tail.append(text.data() + start, text.size() - start);
191+
return tail;
192+
}
193+
88194
std::string makeWaveFailureContext(
89195
const nbl::asset::IShaderCompiler::SPreprocessorOptions& preprocessOptions,
196+
const nbl::wave::context& context,
197+
const WaveRenderProgress& renderProgress,
90198
const std::string_view code,
91199
const char* const phase,
92200
const std::string_view activeMacroDefinition,
@@ -97,14 +205,28 @@ std::string makeWaveFailureContext(
97205
std::ostringstream stream;
98206
stream << "Wave preprocessing context:";
99207
if (!preprocessOptions.sourceIdentifier.empty())
100-
stream << "\n source: " << preprocessOptions.sourceIdentifier;
208+
stream << "\n source: " << nbl::wave::detail::escape_control_chars(preprocessOptions.sourceIdentifier);
101209
stream << "\n phase: " << phase;
102210
stream << "\n extra_define_count: " << preprocessOptions.extraDefines.size();
211+
stream << "\n source_length_bytes: " << code.size();
103212
stream << "\n source_has_trailing_newline: " << ((!code.empty() && code.back() == '\n') ? "yes" : "no");
213+
stream << "\n include_depth: " << context.get_iteration_depth();
214+
stream << "\n current_include_spelling: " << nbl::wave::detail::escape_control_chars(context.get_current_relative_filename());
215+
stream << "\n current_directory: " << nbl::wave::detail::escape_control_chars(context.get_current_directory().string());
216+
#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
217+
stream << "\n current_include_absolute_path: " << nbl::wave::detail::escape_control_chars(context.get_current_filename());
218+
#endif
104219
if (!activeMacroDefinition.empty())
105-
stream << "\n active_macro_definition: " << activeMacroDefinition;
220+
stream << "\n active_macro_definition: " << nbl::wave::detail::escape_control_chars(activeMacroDefinition);
106221
if (fileName && fileName[0] != '\0')
107-
stream << "\n location: " << fileName << ':' << lineNo << ':' << columnNo;
222+
stream << "\n location: " << nbl::wave::detail::escape_control_chars(fileName) << ':' << lineNo << ':' << columnNo;
223+
stream << "\n emitted_output_bytes: " << renderProgress.output.size();
224+
stream << "\n emitted_output_lines: " << countLogicalLines(renderProgress.output);
225+
stream << "\n emitted_token_count: " << renderProgress.emittedTokenCount;
226+
if (!renderProgress.lastTokenFile.empty())
227+
stream << "\n last_emitted_token_location: " << nbl::wave::detail::escape_control_chars(renderProgress.lastTokenFile) << ':' << renderProgress.lastTokenLine << ':' << renderProgress.lastTokenColumn;
228+
if (!renderProgress.lastTokenValue.empty())
229+
stream << "\n last_emitted_token_value: " << truncateEscapedPreview(nbl::wave::detail::escape_control_chars(renderProgress.lastTokenValue), kWaveTokenPreviewMaxChars);
108230

109231
const auto snippet = getLineSnippet(code, lineNo);
110232
if (!snippet.empty() && fileName && preprocessOptions.sourceIdentifier == fileName)
@@ -115,6 +237,10 @@ std::string makeWaveFailureContext(
115237
stream << "\n " << caret;
116238
}
117239

240+
const auto outputTail = makeOutputTail(renderProgress.output);
241+
if (!outputTail.empty())
242+
stream << "\n partial_output_tail:\n" << indentMultiline(outputTail, " ");
243+
118244
return stream.str();
119245
}
120246

@@ -134,14 +260,11 @@ std::string tokenValueToString(const TokenT& token)
134260
return std::string(value.data(), value.size());
135261
}
136262

137-
core::string renderPreprocessedOutput(nbl::wave::context& context)
263+
void renderPreprocessedOutput(nbl::wave::context& context, WaveRenderProgress& renderProgress)
138264
{
139265
using namespace boost::wave;
140266

141-
core::string output;
142267
util::insert_whitespace_detection whitespace(true);
143-
std::optional<nbl::wave::context::position_type> previousPosition = std::nullopt;
144-
bool previousWasExplicitWhitespace = false;
145268

146269
for (auto it = context.begin(); it != context.end(); ++it)
147270
{
@@ -154,37 +277,41 @@ core::string renderPreprocessedOutput(nbl::wave::context& context)
154277
const auto& position = token.get_position();
155278
const auto value = tokenValueToString(token);
156279

157-
if (previousPosition.has_value() && !explicitWhitespace)
280+
if (renderProgress.previousPosition.has_value() && !explicitWhitespace)
158281
{
159282
const auto movedToNewLogicalLine =
160-
position.get_file() != previousPosition->get_file() ||
161-
position.get_line() > previousPosition->get_line();
283+
position.get_file() != renderProgress.previousPosition->get_file() ||
284+
position.get_line() > renderProgress.previousPosition->get_line();
162285

163286
if (movedToNewLogicalLine)
164287
{
165-
if (output.empty() || output.back() != '\n')
288+
if (renderProgress.output.empty() || renderProgress.output.back() != '\n')
166289
{
167-
output.push_back('\n');
290+
renderProgress.output.push_back('\n');
168291
whitespace.shift_tokens(T_NEWLINE);
169292
}
170293
}
171-
else if (!previousWasExplicitWhitespace && whitespace.must_insert(id, value))
294+
else if (!renderProgress.previousWasExplicitWhitespace && whitespace.must_insert(id, value))
172295
{
173-
if (output.empty() || (output.back() != ' ' && output.back() != '\n' && output.back() != '\r' && output.back() != '\t'))
296+
if (renderProgress.output.empty() || (renderProgress.output.back() != ' ' && renderProgress.output.back() != '\n' && renderProgress.output.back() != '\r' && renderProgress.output.back() != '\t'))
174297
{
175-
output.push_back(' ');
298+
renderProgress.output.push_back(' ');
176299
whitespace.shift_tokens(T_SPACE);
177300
}
178301
}
179302
}
180303

181-
output += value;
304+
renderProgress.output += value;
182305
whitespace.shift_tokens(id);
183-
previousPosition = position;
184-
previousWasExplicitWhitespace = explicitWhitespace;
306+
renderProgress.previousPosition = position;
307+
renderProgress.previousWasExplicitWhitespace = explicitWhitespace;
308+
const auto& file = position.get_file();
309+
renderProgress.lastTokenFile.assign(file.c_str(), file.size());
310+
renderProgress.lastTokenLine = position.get_line();
311+
renderProgress.lastTokenColumn = position.get_column();
312+
renderProgress.lastTokenValue = value;
313+
++renderProgress.emittedTokenCount;
185314
}
186-
187-
return output;
188315
}
189316

190317
std::string preprocessImpl(
@@ -199,9 +326,20 @@ std::string preprocessImpl(
199326
context.add_macro_definition("__SPIRV_MAJOR_VERSION__=" + std::to_string(IShaderCompiler::getSpirvMajor(preprocessOptions.targetSpirvVersion)));
200327
context.add_macro_definition("__SPIRV_MINOR_VERSION__=" + std::to_string(IShaderCompiler::getSpirvMinor(preprocessOptions.targetSpirvVersion)));
201328

202-
core::string resolvedString;
329+
WaveRenderProgress renderProgress;
330+
if (preprocessOptions.partialOutputOnFailure)
331+
preprocessOptions.partialOutputOnFailure->clear();
203332
const char* phase = "registering built-in macros";
204333
std::string activeMacroDefinition;
334+
const auto storePartialOutputOnFailure = [&]()
335+
{
336+
if (preprocessOptions.partialOutputOnFailure)
337+
*preprocessOptions.partialOutputOnFailure = renderProgress.output;
338+
};
339+
const auto makeFailureContext = [&](const char* const fileName, const int lineNo, const int columnNo)
340+
{
341+
return makeWaveFailureContext(preprocessOptions, context, renderProgress, code, phase, activeMacroDefinition, fileName, lineNo, columnNo);
342+
};
205343
try
206344
{
207345
phase = "registering extra macro definitions";
@@ -215,39 +353,45 @@ std::string preprocessImpl(
215353
activeMacroDefinition.clear();
216354

217355
phase = "expanding translation unit";
218-
resolvedString = renderPreprocessedOutput(context);
356+
renderPreprocessedOutput(context, renderProgress);
219357
}
220358
catch (boost::wave::preprocess_exception& e)
221359
{
222-
const auto failureContext = makeWaveFailureContext(preprocessOptions, code, phase, activeMacroDefinition, e.file_name(), e.line_no(), e.column_no());
223-
preprocessOptions.logger.log("%s exception caught. %s [%s:%d:%d]\n%s", system::ILogger::ELL_ERROR, e.what(), e.description(), e.file_name(), e.line_no(), e.column_no(), failureContext.c_str());
360+
storePartialOutputOnFailure();
361+
const auto escapedDescription = nbl::wave::detail::escape_control_chars(e.description());
362+
const auto escapedFileName = nbl::wave::detail::escape_control_chars(e.file_name());
363+
const auto failureContext = makeFailureContext(e.file_name(), e.line_no(), e.column_no());
364+
preprocessOptions.logger.log("%s exception caught. %s [%s:%d:%d]\n%s", system::ILogger::ELL_ERROR, e.what(), escapedDescription.c_str(), escapedFileName.c_str(), e.line_no(), e.column_no(), failureContext.c_str());
224365
preprocessOptions.logger.log("Boost diagnostic information:\n%s", system::ILogger::ELL_ERROR, boost::diagnostic_information(e).c_str());
225366
return {};
226367
}
227368
catch (const boost::exception& e)
228369
{
229-
const auto failureContext = makeWaveFailureContext(preprocessOptions, code, phase, activeMacroDefinition, preprocessOptions.sourceIdentifier.data(), 0, 0);
370+
storePartialOutputOnFailure();
371+
const auto failureContext = makeFailureContext(preprocessOptions.sourceIdentifier.data(), 0, 0);
230372
preprocessOptions.logger.log("Boost exception caught during Wave preprocessing.\n%s", system::ILogger::ELL_ERROR, failureContext.c_str());
231373
preprocessOptions.logger.log("Boost diagnostic information:\n%s", system::ILogger::ELL_ERROR, boost::diagnostic_information(e).c_str());
232374
return {};
233375
}
234376
catch (const std::exception& e)
235377
{
236-
const auto failureContext = makeWaveFailureContext(preprocessOptions, code, phase, activeMacroDefinition, preprocessOptions.sourceIdentifier.data(), 0, 0);
378+
storePartialOutputOnFailure();
379+
const auto failureContext = makeFailureContext(preprocessOptions.sourceIdentifier.data(), 0, 0);
237380
preprocessOptions.logger.log("std::exception caught during Wave preprocessing. %s\n%s", system::ILogger::ELL_ERROR, e.what(), failureContext.c_str());
238381
return {};
239382
}
240383
catch (...)
241384
{
242-
const auto failureContext = makeWaveFailureContext(preprocessOptions, code, phase, activeMacroDefinition, preprocessOptions.sourceIdentifier.data(), 0, 0);
385+
storePartialOutputOnFailure();
386+
const auto failureContext = makeFailureContext(preprocessOptions.sourceIdentifier.data(), 0, 0);
243387
preprocessOptions.logger.log("Unknown exception caught during Wave preprocessing.\n%s", system::ILogger::ELL_ERROR, failureContext.c_str());
244388
preprocessOptions.logger.log("Current exception diagnostic information:\n%s", system::ILogger::ELL_ERROR, boost::current_exception_diagnostic_information().c_str());
245389
return {};
246390
}
247391

248392
post(context);
249393

250-
return resolvedString;
394+
return std::move(renderProgress.output);
251395
}
252396
}
253397

0 commit comments

Comments
 (0)