Blender V4.5
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 int src_line_index = 0;
147 while ((src_line_end = strchr(src_line, '\n'))) {
148 if (src_line_index >= log_item.cursor.row) {
149 found_line_id = true;
150 break;
151 }
152 if (src_line_index >= log_item.cursor.row - DEBUG_CONTEXT_LINES) {
153 BLI_dynstr_appendf(dynstr, "%5d | ", src_line_index);
154 BLI_dynstr_nappend(dynstr, src_line, (src_line_end + 1) - src_line);
155 }
156 /* Continue to next line. */
157 src_line = src_line_end + 1;
158 src_line_index++;
159 }
160 /* Print error source. */
161 if (found_line_id) {
162 if (log_item.cursor.row != previous_location.row) {
163 BLI_dynstr_appendf(dynstr, "%5d | ", src_line_index);
164 }
165 else {
166 BLI_dynstr_appendf(dynstr, line_prefix);
167 }
168 BLI_dynstr_nappend(dynstr, src_line, (src_line_end + 1) - src_line);
169 /* Print char offset. */
170 BLI_dynstr_appendf(dynstr, line_prefix);
171 if (log_item.cursor.column != -1) {
172 for (int i = 0; i < log_item.cursor.column; i++) {
173 BLI_dynstr_appendf(dynstr, " ");
174 }
175 BLI_dynstr_appendf(dynstr, "^");
176 }
177 BLI_dynstr_appendf(dynstr, "\n");
178
179 /* Skip the error line. */
180 src_line = src_line_end + 1;
181 src_line_index++;
182 while ((src_line_end = strchr(src_line, '\n'))) {
183 if (src_line_index > log_item.cursor.row + DEBUG_CONTEXT_LINES) {
184 break;
185 }
186 BLI_dynstr_appendf(dynstr, "%5d | ", src_line_index);
187 BLI_dynstr_nappend(dynstr, src_line, (src_line_end + 1) - src_line);
188 /* Continue to next line. */
189 src_line = src_line_end + 1;
190 src_line_index++;
191 }
192 }
193 }
194 BLI_dynstr_appendf(dynstr, line_prefix);
195
196 /* Search the correct source index. */
197 int row_in_file = log_item.cursor.row;
198 int source_index = log_item.cursor.source;
199 if (source_index <= 0) {
200 for (auto i : sources_end_line.index_range()) {
201 if (log_item.cursor.row <= sources_end_line[i]) {
202 source_index = i;
203 break;
204 }
205 }
206 }
207 if (source_index > 0) {
208 row_in_file -= sources_end_line[source_index - 1];
209 }
210 /* Print the filename the error line is coming from. */
211 if (!log_item.cursor.file_name_and_error_line.is_empty()) {
212 char name_buf[256];
213 log_item.cursor.file_name_and_error_line.substr(0, sizeof(name_buf) - 1)
214 .copy_utf8_truncated(name_buf);
215 BLI_dynstr_appendf(dynstr, "%s%s: %s", info_col, name_buf, reset_col);
216 }
217 else if (source_index > 0) {
219 sources[source_index]);
220 if (!filename.is_empty()) {
221 BLI_dynstr_appendf(dynstr,
222 "%s%s:%d:%d: %s",
223 info_col,
224 filename.c_str(),
225 row_in_file,
226 log_item.cursor.column + 1,
227 reset_col);
228 }
229 }
230
231 if (log_item.severity == Severity::Error) {
232 BLI_dynstr_appendf(dynstr, "%s%s%s: ", err_col, "Error", info_col);
233 }
234 else if (log_item.severity == Severity::Warning) {
235 BLI_dynstr_appendf(dynstr, "%s%s%s: ", warn_col, "Warning", info_col);
236 }
237 else if (log_item.severity == Severity::Note) {
238 BLI_dynstr_appendf(dynstr, "%s%s%s: ", warn_col, "Note", info_col);
239 }
240 /* Print the error itself. */
241 BLI_dynstr_append(dynstr, info_col);
242 BLI_dynstr_nappend(dynstr, log_line, (line_end + 1) - log_line);
243 BLI_dynstr_append(dynstr, reset_col);
244 /* Continue to next line. */
245 log_line = line_end + 1;
246 previous_location = log_item.cursor;
247 }
248
250
251 if (((LOG.type->flag & CLG_FLAG_USE) && (LOG.type->level >= 0)) ||
252 (severity >= CLG_SEVERITY_WARN))
253 {
255 CLG_log_str(LOG.type, severity, this->name, stage, sources_combined.c_str());
256 }
257 const char *_str = BLI_dynstr_get_cstring(dynstr);
258 CLG_log_str(LOG.type, severity, 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
322
323/* -------------------------------------------------------------------- */
326
328{
329 if (ctx == nullptr) {
330 return;
331 }
333 return;
334 }
335 GPUStorageBuf *printf_buf = GPU_storagebuf_create(GPU_SHADER_PRINTF_MAX_CAPACITY *
336 sizeof(uint32_t));
338 ctx->printf_buf.append(printf_buf);
339}
340
342{
343 if (ctx == nullptr) {
344 return;
345 }
346 if (ctx->printf_buf.is_empty()) {
347 return;
348 }
349 GPUStorageBuf *printf_buf = ctx->printf_buf.pop_last();
350
352 GPU_storagebuf_read(printf_buf, data.data());
353 GPU_storagebuf_free(printf_buf);
354
355 uint32_t data_len = data[0];
356 if (data_len == 0) {
357 return;
358 }
359
360 int cursor = 1;
361 while (cursor < data_len + 1) {
362 uint32_t format_hash = data[cursor++];
363
365 format_hash);
366
367 if (cursor + format.format_blocks.size() >= GPU_SHADER_PRINTF_MAX_CAPACITY) {
368 printf("Printf buffer overflow.\n");
369 break;
370 }
371
372 for (const shader::PrintfFormat::Block &block : format.format_blocks) {
373 switch (block.type) {
375 printf("%s", block.fmt.c_str());
376 break;
378 printf(block.fmt.c_str(), *reinterpret_cast<uint32_t *>(&data[cursor++]));
379 break;
381 printf(block.fmt.c_str(), *reinterpret_cast<int32_t *>(&data[cursor++]));
382 break;
384 printf(block.fmt.c_str(), *reinterpret_cast<float *>(&data[cursor++]));
385 break;
386 default:
388 break;
389 }
390 }
391 }
392}
393
395
396} // 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:74
DynStr * BLI_dynstr_new(void) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
Definition BLI_dynstr.cc:36
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:55
#define STREQLEN(a, b, n)
#define ELEM(...)
void CLG_log_str(const CLG_LogType *lg, enum CLG_Severity severity, const char *file_line, const char *fn, const char *message) _CLOG_ATTR_NONNULL(1
CLG_Severity
Definition CLG_log.h:87
@ CLG_SEVERITY_WARN
Definition CLG_log.h:89
@ CLG_SEVERITY_ERROR
Definition CLG_log.h:90
@ CLG_FLAG_USE
Definition CLG_log.h:84
int CLG_color_support_get(CLG_LogRef *clg_ref)
Definition clog.c:788
void GPU_storagebuf_clear_to_zero(GPUStorageBuf *ssbo)
void GPU_storagebuf_free(GPUStorageBuf *ssbo)
void GPU_storagebuf_read(GPUStorageBuf *ssbo, void *data)
#define GPU_storagebuf_create(size)
Read Guarded memory(de)allocation.
BMesh const char void * data
long long int int64_t
T pop_last()
void append(const T &value)
bool is_empty() const
constexpr int64_t size() const
Definition BLI_span.hh:252
static constexpr int64_t not_found
constexpr int64_t find(char c, int64_t pos=0) const
constexpr bool is_empty() const
constexpr StringRef substr(int64_t start, int64_t size) const
constexpr bool endswith(StringRef suffix) 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< GPUStorageBuf * > printf_buf
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
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)
#define log
#define this
#define printf(...)
#define GPU_SHADER_PRINTF_MAX_CAPACITY
#define DEBUG_CONTEXT_LINES
#define DEBUG_LOG_SHADER_SRC_ON_ERROR
format
#define LOG(severity)
Definition log.h:32
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