Blender V5.0
gpu_shader_log.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2021 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "MEM_guardedalloc.h"
10
11#include "BLI_dynstr.h"
12#include "BLI_vector.hh"
13
14#include "GPU_storage_buffer.hh"
15
18#include "gpu_shader_private.hh"
19
20#include "CLG_log.h"
21
22#include "fmt/format.h"
23
24static CLG_LogRef LOG = {"gpu.shader"};
25
26namespace blender::gpu {
27
28/* -------------------------------------------------------------------- */
31
32/* Number of lines before and after the error line to print for compilation errors. */
33#define DEBUG_CONTEXT_LINES 0
38#define DEBUG_DEPENDENCIES 0
39
41 const char *log,
42 const char *stage,
43 const bool error,
44 GPULogParser *parser)
45{
46 const char line_prefix[] = " | ";
47 char err_col[] = "\033[31;1m";
48 char warn_col[] = "\033[33;1m";
49 char info_col[] = "\033[0;2m";
50 char reset_col[] = "\033[0;0m";
51 std::string sources_combined = fmt::to_string(fmt::join(sources, ""));
52 DynStr *dynstr = BLI_dynstr_new();
53
55 err_col[0] = warn_col[0] = info_col[0] = reset_col[0] = '\0';
56 }
57
58 BLI_dynstr_appendf(dynstr, "\n");
59
60#if DEBUG_DEPENDENCIES
62 dynstr, "%s%sIncluded files (in order):%s\n", info_col, line_prefix, reset_col);
63#endif
64
65 Vector<int64_t> sources_end_line;
66 for (StringRef src : sources) {
67 int64_t cursor = 0, line_count = 0;
68 while ((cursor = src.find('\n', cursor) + 1)) {
69 line_count++;
70 }
71 if (sources_end_line.is_empty() == false) {
72 line_count += sources_end_line.last();
73 }
74 sources_end_line.append(line_count);
75#if DEBUG_DEPENDENCIES
77 if (!filename.is_empty()) {
79 dynstr, "%s%s %s%s\n", info_col, line_prefix, filename.c_str(), reset_col);
80 }
81#endif
82 }
83 if (sources_end_line.is_empty()) {
84 sources_end_line.append(0);
85 }
86
87 const char *log_line = log, *line_end;
88
89 LogCursor previous_location;
90
91 bool found_line_id = false;
92 while ((line_end = strchr(log_line, '\n'))) {
93 /* Skip empty lines. */
94 if (line_end == log_line) {
95 log_line++;
96 continue;
97 }
98
99 /* Silence not useful lines. */
100 StringRef logref = StringRefNull(log_line).substr(0, size_t(line_end) - size_t(log_line));
101 if (logref.endswith(" shader failed to compile with the following errors:") ||
102 logref.endswith(" No code generated"))
103 {
104 log_line += size_t(line_end) - size_t(log_line);
105 continue;
106 }
107
108 GPULogItem log_item;
109 log_line = parser->parse_line(sources_combined.c_str(), log_line, log_item);
110
111 /* Empty line, skip. */
112 if ((log_item.cursor.row == -1) && ELEM(log_line[0], '\n', '\0')) {
113 continue;
114 }
115
116 /* Sanitize output. Really bad values can happen when the error line is buggy. */
117 if (log_item.cursor.source >= sources.size()) {
118 log_item.cursor.source = -1;
119 }
120 if (log_item.cursor.row >= sources_end_line.last()) {
121 log_item.cursor.source = -1;
122 log_item.cursor.row = -1;
123 }
124
125 if (log_item.cursor.row == -1) {
126 found_line_id = false;
127 }
128
129 const char *src_line = sources_combined.c_str();
130
131 /* Separate from previous block. */
132 if (previous_location.source != log_item.cursor.source ||
133 previous_location.row != log_item.cursor.row)
134 {
135 BLI_dynstr_appendf(dynstr, "%s%s%s\n", info_col, line_prefix, reset_col);
136 }
137 else if (log_item.cursor.column != previous_location.column) {
138 BLI_dynstr_appendf(dynstr, "%s\n", line_prefix);
139 }
140 /* Print line from the source file that is producing the error. */
141 if ((log_item.cursor.row != -1) && (log_item.cursor.row != previous_location.row ||
142 log_item.cursor.column != previous_location.column))
143 {
144 const char *src_line_end;
145 found_line_id = false;
146 /* Lines are 1 based. */
147 int src_line_index = 1;
148 while ((src_line_end = strchr(src_line, '\n'))) {
149 if (src_line_index >= log_item.cursor.row) {
150 found_line_id = true;
151 break;
152 }
153 if (src_line_index >= log_item.cursor.row - DEBUG_CONTEXT_LINES) {
154 BLI_dynstr_appendf(dynstr, "%5d | ", src_line_index);
155 BLI_dynstr_nappend(dynstr, src_line, (src_line_end + 1) - src_line);
156 }
157 /* Continue to next line. */
158 src_line = src_line_end + 1;
159 src_line_index++;
160 }
161 /* Print error source. */
162 if (found_line_id) {
163 if (log_item.cursor.row != previous_location.row) {
164 BLI_dynstr_appendf(dynstr, "%5d | ", src_line_index);
165 }
166 else {
167 BLI_dynstr_appendf(dynstr, line_prefix);
168 }
169 BLI_dynstr_nappend(dynstr, src_line, (src_line_end + 1) - src_line);
170 /* Print char offset. */
171 BLI_dynstr_appendf(dynstr, line_prefix);
172 if (log_item.cursor.column != -1) {
173 for (int i = 0; i < log_item.cursor.column; i++) {
174 BLI_dynstr_appendf(dynstr, " ");
175 }
176 BLI_dynstr_appendf(dynstr, "^");
177 }
178 BLI_dynstr_appendf(dynstr, "\n");
179
180 /* Skip the error line. */
181 src_line = src_line_end + 1;
182 src_line_index++;
183 while ((src_line_end = strchr(src_line, '\n'))) {
184 if (src_line_index > log_item.cursor.row + DEBUG_CONTEXT_LINES) {
185 break;
186 }
187 BLI_dynstr_appendf(dynstr, "%5d | ", src_line_index);
188 BLI_dynstr_nappend(dynstr, src_line, (src_line_end + 1) - src_line);
189 /* Continue to next line. */
190 src_line = src_line_end + 1;
191 src_line_index++;
192 }
193 }
194 }
195 BLI_dynstr_appendf(dynstr, line_prefix);
196
197 /* Search the correct source index. */
198 int row_in_file = log_item.cursor.row;
199 int source_index = log_item.cursor.source;
200 if (source_index <= 0) {
201 for (auto i : sources_end_line.index_range()) {
202 if (log_item.cursor.row <= sources_end_line[i]) {
203 source_index = i;
204 break;
205 }
206 }
207 }
208 if (source_index > 0) {
209 row_in_file -= sources_end_line[source_index - 1];
210 }
211 /* Print the filename the error line is coming from. */
212 if (!log_item.cursor.file_name_and_error_line.empty()) {
213 char name_buf[256];
214 std::string string = log_item.cursor.file_name_and_error_line.substr(0,
215 sizeof(name_buf) - 1);
216 StringRefNull(string).copy_utf8_truncated(name_buf);
217 BLI_dynstr_appendf(dynstr, "%s%s: %s", info_col, name_buf, reset_col);
218 }
219 else if (source_index > 0) {
221 sources[source_index]);
222 if (!filename.is_empty()) {
223 BLI_dynstr_appendf(dynstr,
224 "%s%s:%d:%d: %s",
225 info_col,
226 filename.c_str(),
227 row_in_file,
228 log_item.cursor.column + 1,
229 reset_col);
230 }
231 }
232
233 if (log_item.severity == Severity::Error) {
234 BLI_dynstr_appendf(dynstr, "%s%s%s: ", err_col, "Error", info_col);
235 }
236 else if (log_item.severity == Severity::Warning) {
237 BLI_dynstr_appendf(dynstr, "%s%s%s: ", warn_col, "Warning", info_col);
238 }
239 else if (log_item.severity == Severity::Note) {
240 BLI_dynstr_appendf(dynstr, "%s%s%s: ", warn_col, "Note", info_col);
241 }
242 /* Print the error itself. */
243 BLI_dynstr_append(dynstr, info_col);
244 BLI_dynstr_nappend(dynstr, log_line, (line_end + 1) - log_line);
245 BLI_dynstr_append(dynstr, reset_col);
246 /* Continue to next line. */
247 log_line = line_end + 1;
248 previous_location = log_item.cursor;
249 }
250
252
253 if (CLOG_CHECK(&LOG, level)) {
255 CLG_log_raw(LOG.type, sources_combined.c_str());
256 }
257 const char *_str = BLI_dynstr_get_cstring(dynstr);
258 CLOG_AT_LEVEL(&LOG, level, "%s %s: %s", this->name, stage, _str);
259 MEM_freeN(_str);
260 }
261
262 BLI_dynstr_free(dynstr);
263}
264
265const char *GPULogParser::skip_severity(const char *log_line,
266 GPULogItem &log_item,
267 const char *error_msg,
268 const char *warning_msg,
269 const char *note_msg) const
270{
271 if (STREQLEN(log_line, error_msg, strlen(error_msg))) {
272 log_line += strlen(error_msg);
273 log_item.severity = Severity::Error;
274 }
275 else if (STREQLEN(log_line, warning_msg, strlen(warning_msg))) {
276 log_line += strlen(warning_msg);
277 log_item.severity = Severity::Warning;
278 }
279 else if (STREQLEN(log_line, note_msg, strlen(note_msg))) {
280 log_line += strlen(note_msg);
281 log_item.severity = Severity::Note;
282 }
283 return log_line;
284}
285
286const char *GPULogParser::skip_separators(const char *log_line, const StringRef separators) const
287{
288 while (at_any(log_line, separators)) {
289 log_line++;
290 }
291 return log_line;
292}
293
294const char *GPULogParser::skip_until(const char *log_line, char stop_char) const
295{
296 const char *cursor = log_line;
297 while (!ELEM(cursor[0], '\n', '\0')) {
298 if (cursor[0] == stop_char) {
299 return cursor;
300 }
301 cursor++;
302 }
303 return log_line;
304}
305
306bool GPULogParser::at_number(const char *log_line) const
307{
308 return log_line[0] >= '0' && log_line[0] <= '9';
309}
310
311bool GPULogParser::at_any(const char *log_line, const StringRef chars) const
312{
313 return chars.find(log_line[0]) != StringRef::not_found;
314}
315
316int GPULogParser::parse_number(const char *log_line, const char **r_new_position) const
317{
318 return int(strtol(log_line, const_cast<char **>(r_new_position), 10));
319}
320
321size_t GPULogParser::line_start_get(StringRefNull source_combined, size_t target_line)
322{
323 size_t cursor = 0;
324 size_t current_line = 1;
325 for (char c : source_combined) {
326 if (current_line >= target_line) {
327 return cursor + 1;
328 }
329 if (c == '\n') {
330 current_line++;
331 }
332 cursor++;
333 }
334 return -1;
335}
336
338{
339 StringRef sub_str = source_combined.substr(0, pos);
340 StringRefNull directive = "#line 1 \"";
341 size_t nearest_line_directive = sub_str.rfind(directive);
342 if (nearest_line_directive != std::string::npos) {
343 size_t start_of_file_name = nearest_line_directive + directive.size();
344 size_t end_of_file_name = sub_str.find('\"', start_of_file_name);
345 if (end_of_file_name != std::string::npos) {
346 return sub_str.substr(start_of_file_name, end_of_file_name - start_of_file_name);
347 }
348 }
349 return {};
350}
351
352/* Original source file line. Found by looking up #line directives. */
353size_t GPULogParser::source_line_get(StringRefNull source_combined, size_t pos)
354{
355 StringRef sub_str = source_combined.substr(0, pos);
356 StringRefNull directive = "#line ";
357 size_t nearest_line_directive = sub_str.rfind(directive);
358 size_t line_count = 1;
359 if (nearest_line_directive != std::string::npos) {
360 sub_str = sub_str.substr(nearest_line_directive + directive.size());
361 line_count = std::stoll(sub_str) - 1;
362 }
363 return line_count + std::count(sub_str.begin(), sub_str.end(), '\n');
364}
365
366
367/* -------------------------------------------------------------------- */
370
372{
373 if (ctx == nullptr) {
374 return;
375 }
377 return;
378 }
380 sizeof(uint32_t));
382 ctx->printf_buf.append(printf_buf);
383}
384
386{
387 if (ctx == nullptr) {
388 return;
389 }
390 if (ctx->printf_buf.is_empty()) {
391 return;
392 }
393 StorageBuf *printf_buf = ctx->printf_buf.pop_last();
394
396 GPU_storagebuf_read(printf_buf, data.data());
397 GPU_storagebuf_free(printf_buf);
398
399 uint32_t data_len = data[0];
400 if (data_len == 0) {
401 return;
402 }
403
404 int cursor = 1;
405 while (cursor < data_len + 1) {
406 uint32_t format_hash = data[cursor++];
407
409 format_hash);
410
411 if (cursor + format.format_blocks.size() >= GPU_SHADER_PRINTF_MAX_CAPACITY) {
412 printf("Printf buffer overflow.\n");
413 break;
414 }
415
416 for (const shader::PrintfFormat::Block &block : format.format_blocks) {
417 switch (block.type) {
419 printf("%s", block.fmt.c_str());
420 break;
422 printf(block.fmt.c_str(), *reinterpret_cast<uint32_t *>(&data[cursor++]));
423 break;
425 printf(block.fmt.c_str(), *reinterpret_cast<int32_t *>(&data[cursor++]));
426 break;
428 printf(block.fmt.c_str(), *reinterpret_cast<float *>(&data[cursor++]));
429 break;
430 default:
432 break;
433 }
434 }
435 }
436}
437
439
440} // namespace blender::gpu
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
A dynamically sized string ADT.
char * BLI_dynstr_get_cstring(const DynStr *ds) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
void BLI_dynstr_nappend(DynStr *__restrict ds, const char *cstr, int len) ATTR_NONNULL()
Definition BLI_dynstr.cc:75
DynStr * BLI_dynstr_new(void) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
Definition BLI_dynstr.cc:37
void BLI_dynstr_free(DynStr *ds) ATTR_NONNULL()
void BLI_dynstr_appendf(DynStr *__restrict ds, const char *__restrict format,...) ATTR_PRINTF_FORMAT(2
void BLI_dynstr_append(DynStr *__restrict ds, const char *cstr) ATTR_NONNULL()
Definition BLI_dynstr.cc:56
#define STREQLEN(a, b, n)
#define ELEM(...)
void CLG_log_raw(const CLG_LogType *lg, const char *message)
Definition clog.cc:700
#define CLOG_CHECK(clg_ref, verbose_level,...)
Definition CLG_log.h:147
CLG_Level
Definition CLG_log.h:52
@ CLG_LEVEL_ERROR
Definition CLG_log.h:56
@ CLG_LEVEL_WARN
Definition CLG_log.h:58
#define CLOG_AT_LEVEL(clg_ref, verbose_level,...)
Definition CLG_log.h:150
int CLG_color_support_get(CLG_LogRef *clg_ref)
Definition clog.cc:1006
void GPU_storagebuf_free(blender::gpu::StorageBuf *ssbo)
void GPU_storagebuf_clear_to_zero(blender::gpu::StorageBuf *ssbo)
void GPU_storagebuf_read(blender::gpu::StorageBuf *ssbo, void *data)
#define GPU_storagebuf_create(size)
Read Guarded memory(de)allocation.
BMesh const char void * data
long long int int64_t
constexpr int64_t size() const
Definition BLI_span.hh:252
static constexpr int64_t not_found
constexpr int64_t rfind(char c, int64_t pos=INT64_MAX) const
constexpr int64_t find(char c, int64_t pos=0) const
constexpr const char * begin() const
constexpr const char * end() const
constexpr bool is_empty() const
constexpr StringRef substr(int64_t start, int64_t size) const
constexpr bool endswith(StringRef suffix) const
constexpr int64_t size() const
void copy_utf8_truncated(char *dst, int64_t dst_size) const
Definition string_ref.cc:28
constexpr const char * c_str() const
void append(const T &value)
const T & last(const int64_t n=0) const
bool is_empty() const
IndexRange index_range() const
Vector< StorageBuf * > printf_buf
static size_t line_start_get(StringRefNull source_combined, size_t target_line)
static StringRef filename_get(StringRefNull source_combined, size_t pos)
int parse_number(const char *log_line, const char **r_new_position) const
const char * skip_separators(const char *log_line, const StringRef separators) const
bool at_number(const char *log_line) const
static size_t source_line_get(StringRefNull source_combined, size_t pos)
bool at_any(const char *log_line, const StringRef chars) const
virtual const char * parse_line(const char *source_combined, const char *log_line, GPULogItem &log_item)=0
const char * skip_until(const char *log_line, char stop_char) const
const char * skip_severity(const char *log_line, GPULogItem &log_item, const char *error_msg, const char *warning_msg, const char *note_msg) const
void print_log(Span< StringRefNull > sources, const char *log, const char *stage, bool error, GPULogParser *parser)
uint pos
#define printf(...)
#define GPU_SHADER_PRINTF_MAX_CAPACITY
#define log
#define DEBUG_CONTEXT_LINES
#define DEBUG_LOG_SHADER_SRC_ON_ERROR
format
#define LOG(level)
Definition log.h:97
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static void error(const char *str)
const PrintfFormat & gpu_shader_dependency_get_printf_format(uint32_t format_hash)
StringRefNull gpu_shader_dependency_get_filename_from_source_string(const StringRef source_string)
Find the name of the file from which the given string was generated.
static CLG_LogRef LOG
void printf_begin(Context *ctx)
void printf_end(Context *ctx)
enum blender::gpu::shader::PrintfFormat::Block::ArgumentType type
i
Definition text_draw.cc:230