@@ -49,6 +49,22 @@ using namespace nbl::asset;
4949
5050namespace
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+
5268std::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+
88194std::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
190317std::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