Blender V5.0
gl_debug.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2005 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
10
11#include "BLI_compiler_attrs.h"
12#include "BLI_string.h"
13#include "BLI_system.h"
14#include "BLI_utildefines.h"
15
16#include "BKE_global.hh"
17
18#include "GPU_debug.hh"
19#include "GPU_platform.hh"
20#include "gpu_profile_report.hh"
21
22#include "CLG_log.h"
23
24#include "gl_backend.hh"
25#include "gl_context.hh"
26#include "gl_uniform_buffer.hh"
27
28#include "gl_debug.hh"
29
30static CLG_LogRef LOG = {"gpu.debug"};
31
32/* Avoid too much NVidia buffer info in the output log. */
33#define TRIM_NVIDIA_BUFFER_INFO 1
34/* Avoid unneeded shader statistics. */
35#define TRIM_SHADER_STATS_INFO 1
36
37namespace blender::gpu::debug {
38
39/* -------------------------------------------------------------------- */
45
46/* Debug callbacks need the same calling convention as OpenGL functions. */
47#if defined(_WIN32)
48# define APIENTRY __stdcall
49#else
50# define APIENTRY
51#endif
52
53static void APIENTRY debug_callback(GLenum /*source*/,
54 GLenum type,
55 GLuint /*id*/,
56 GLenum severity,
57 GLsizei /*length*/,
58 const GLchar *message,
59 const GLvoid * /*userParm*/)
60{
61 if (ELEM(type, GL_DEBUG_TYPE_PUSH_GROUP, GL_DEBUG_TYPE_POP_GROUP)) {
62 /* The debug layer will emit a message each time a debug group is pushed or popped.
63 * We use that for easy command grouping inside frame analyzer tools. */
64 return;
65 }
66
67 /* NOTE: callback function can be triggered during before the platform is initialized.
68 * In this case invoking `GPU_type_matches` would fail and
69 * therefore the message is checked before the platform matching. */
70 if (TRIM_NVIDIA_BUFFER_INFO && STRPREFIX(message, "Buffer detailed info") &&
72 {
73 /* Suppress buffer information flooding the output. */
74 return;
75 }
76
77 if (TRIM_SHADER_STATS_INFO && STRPREFIX(message, "Shader Stats")) {
78 /* Suppress buffer information flooding the output. */
79 return;
80 }
81
82 const bool use_color = CLG_color_support_get(&LOG);
83
84 if (ELEM(severity, GL_DEBUG_SEVERITY_LOW, GL_DEBUG_SEVERITY_NOTIFICATION)) {
86 const char *format = use_color ? "\033[2m%s\033[0m" : "%s";
87 CLG_logf(LOG.type, CLG_LEVEL_INFO, "Notification", "", format, message);
88 }
89 }
90 else {
91 char debug_groups[512] = "";
92 GPU_debug_get_groups_names(sizeof(debug_groups), debug_groups);
93 CLG_Level clog_level;
94
97 {
98 /* Do not duplicate shader compilation error/warnings. */
99 return;
100 }
101
102 switch (type) {
103 case GL_DEBUG_TYPE_ERROR:
104 case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
105 case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
106 clog_level = CLG_LEVEL_ERROR;
107 break;
108 case GL_DEBUG_TYPE_PORTABILITY:
109 case GL_DEBUG_TYPE_PERFORMANCE:
110 case GL_DEBUG_TYPE_OTHER:
111 case GL_DEBUG_TYPE_MARKER: /* KHR has this, ARB does not */
112 default:
113 clog_level = CLG_LEVEL_WARN;
114 break;
115 }
116
117 if (CLOG_CHECK(&LOG, clog_level)) {
118 CLG_logf(LOG.type, clog_level, debug_groups, "", "%s", message);
119 if (severity == GL_DEBUG_SEVERITY_HIGH) {
120 /* Focus on error message. */
121 if (use_color) {
122 fprintf(stderr, "\033[2m");
123 }
124 BLI_system_backtrace(stderr);
125 if (use_color) {
126 fprintf(stderr, "\033[0m\n");
127 }
128 fflush(stderr);
129 }
130 }
131 }
132}
133
134#undef APIENTRY
135
137{
138 glEnable(GL_DEBUG_OUTPUT);
139 glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
140 glDebugMessageCallback((GLDEBUGPROC)debug_callback, nullptr);
141 glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE);
142 glDebugMessageInsert(GL_DEBUG_SOURCE_APPLICATION,
143 GL_DEBUG_TYPE_MARKER,
144 0,
145 GL_DEBUG_SEVERITY_NOTIFICATION,
146 -1,
147 "Successfully hooked OpenGL debug callback");
148}
149
151
152/* -------------------------------------------------------------------- */
158
159void check_gl_error(const char *info)
160{
161 if (!(G.debug & G_DEBUG_GPU)) {
162 return;
163 }
164 GLenum error = glGetError();
165
166#define ERROR_CASE(err) \
167 case err: { \
168 char msg[256]; \
169 SNPRINTF(msg, "%s : %s", #err, info); \
170 debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, nullptr); \
171 break; \
172 }
173
174 switch (error) {
175 ERROR_CASE(GL_INVALID_ENUM)
176 ERROR_CASE(GL_INVALID_VALUE)
177 ERROR_CASE(GL_INVALID_OPERATION)
178 ERROR_CASE(GL_INVALID_FRAMEBUFFER_OPERATION)
179 ERROR_CASE(GL_OUT_OF_MEMORY)
180 ERROR_CASE(GL_STACK_UNDERFLOW)
181 ERROR_CASE(GL_STACK_OVERFLOW)
182 case GL_NO_ERROR:
183 break;
184 default:
185 char msg[256];
186 SNPRINTF(msg, "Unknown GL error: %x : %s", error, info);
187 debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, nullptr);
188 break;
189 }
190}
191
192void check_gl_resources(const char *info)
193{
194 if (!(G.debug & G_DEBUG_GPU)) {
195 return;
196 }
197
198 GLContext *ctx = GLContext::get();
199 ShaderInterface *interface = ctx->shader->interface;
200 /* NOTE: This only check binding. To be valid, the bound ubo needs to
201 * be big enough to feed the data range the shader awaits. */
202 uint16_t ubo_needed = interface->enabled_ubo_mask_;
203 ubo_needed &= ~ctx->bound_ubo_slots;
204 /* NOTE: This only check binding. To be valid, the bound ssbo needs to
205 * be big enough to feed the data range the shader awaits. */
206 uint16_t ssbo_needed = interface->enabled_ssbo_mask_;
207 ssbo_needed &= ~ctx->bound_ssbo_slots;
208 /* NOTE: This only check binding. To be valid, the bound texture needs to
209 * be the same format/target the shader expects. */
210 uint64_t tex_needed = interface->enabled_tex_mask_;
211 tex_needed &= ~GLContext::state_manager_active_get()->bound_texture_slots();
212 /* NOTE: This only check binding. To be valid, the bound image needs to
213 * be the same format/target the shader expects. */
214 uint8_t ima_needed = interface->enabled_ima_mask_;
215 ima_needed &= ~GLContext::state_manager_active_get()->bound_image_slots();
216
217 if (ubo_needed == 0 && tex_needed == 0 && ima_needed == 0 && ssbo_needed == 0) {
218 return;
219 }
220
221 for (int i = 0; ubo_needed != 0; i++, ubo_needed >>= 1) {
222 if ((ubo_needed & 1) != 0) {
223 const ShaderInput *ubo_input = interface->ubo_get(i);
224 const char *ubo_name = interface->input_name_get(ubo_input);
225 const StringRefNull sh_name = ctx->shader->name_get();
226 char msg[256];
227 SNPRINTF(
228 msg, "Missing UBO bind at slot %d : %s > %s : %s", i, sh_name.c_str(), ubo_name, info);
229 debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, nullptr);
230 }
231 }
232
233 for (int i = 0; ssbo_needed != 0; i++, ssbo_needed >>= 1) {
234 if ((ssbo_needed & 1) != 0) {
235 const ShaderInput *ssbo_input = interface->ssbo_get(i);
236 const char *ssbo_name = interface->input_name_get(ssbo_input);
237 const StringRefNull sh_name = ctx->shader->name_get();
238 char msg[256];
239 SNPRINTF(
240 msg, "Missing SSBO bind at slot %d : %s > %s : %s", i, sh_name.c_str(), ssbo_name, info);
241 debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, nullptr);
242 }
243 }
244
245 for (int i = 0; tex_needed != 0; i++, tex_needed >>= 1) {
246 if ((tex_needed & 1) != 0) {
247 /* FIXME: texture_get might return an image input instead. */
248 const ShaderInput *tex_input = interface->texture_get(i);
249 const char *tex_name = interface->input_name_get(tex_input);
250 const StringRefNull sh_name = ctx->shader->name_get();
251 char msg[256];
252 SNPRINTF(msg,
253 "Missing Texture bind at slot %d : %s > %s : %s",
254 i,
255 sh_name.c_str(),
256 tex_name,
257 info);
258 debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, nullptr);
259 }
260 }
261
262 for (int i = 0; ima_needed != 0; i++, ima_needed >>= 1) {
263 if ((ima_needed & 1) != 0) {
264 /* FIXME: texture_get might return a texture input instead. */
265 const ShaderInput *tex_input = interface->texture_get(i);
266 const char *tex_name = interface->input_name_get(tex_input);
267 const StringRefNull sh_name = ctx->shader->name_get();
268 char msg[256];
269 SNPRINTF(
270 msg, "Missing Image bind at slot %d : %s > %s : %s", i, sh_name.c_str(), tex_name, info);
271 debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, nullptr);
272 }
273 }
274}
275
276void raise_gl_error(const char *info)
277{
278 debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, info, nullptr);
279}
280
282
283/* -------------------------------------------------------------------- */
289
290static const char *to_str_prefix(GLenum type)
291{
292 switch (type) {
293 case GL_FRAGMENT_SHADER:
294 case GL_GEOMETRY_SHADER:
295 case GL_VERTEX_SHADER:
296 case GL_SHADER:
297 case GL_PROGRAM:
298 return "SHD-";
299 case GL_SAMPLER:
300 return "SAM-";
301 case GL_TEXTURE:
302 return "TEX-";
303 case GL_FRAMEBUFFER:
304 return "FBO-";
305 case GL_VERTEX_ARRAY:
306 return "VAO-";
307 case GL_UNIFORM_BUFFER:
308 return "UBO-";
309 case GL_BUFFER:
310 return "BUF-";
311 default:
312 return "";
313 }
314}
315static const char *to_str_suffix(GLenum type)
316{
317 switch (type) {
318 case GL_FRAGMENT_SHADER:
319 return "-Frag";
320 case GL_GEOMETRY_SHADER:
321 return "-Geom";
322 case GL_VERTEX_SHADER:
323 return "-Vert";
324 default:
325 return "";
326 }
327}
328
329void object_label(GLenum type, GLuint object, const char *name)
330{
331 if ((G.debug & G_DEBUG_GPU) &&
332 (epoxy_gl_version() >= 43 || epoxy_has_gl_extension("GL_KHR_debug")))
333 {
334 char label[64];
335 SNPRINTF(label, "%s%s%s", to_str_prefix(type), name, to_str_suffix(type));
336 /* Small convenience for caller. */
337 switch (type) {
338 case GL_FRAGMENT_SHADER:
339 case GL_GEOMETRY_SHADER:
340 case GL_VERTEX_SHADER:
341 case GL_COMPUTE_SHADER:
342 type = GL_SHADER;
343 break;
344 case GL_UNIFORM_BUFFER:
345 case GL_SHADER_STORAGE_BUFFER:
346 case GL_ARRAY_BUFFER:
347 case GL_ELEMENT_ARRAY_BUFFER:
348 type = GL_BUFFER;
349 break;
350 default:
351 break;
352 }
353 glObjectLabel(type, object, -1, label);
354 }
355}
356
358
359} // namespace blender::gpu::debug
360
361namespace blender::gpu {
362
363/* -------------------------------------------------------------------- */
368
369void GLContext::debug_group_begin(const char *name, int index)
370{
371 if ((G.debug & G_DEBUG_GPU) &&
372 (epoxy_gl_version() >= 43 || epoxy_has_gl_extension("GL_KHR_debug")))
373 {
374 /* Add 10 to avoid collision with other indices from other possible callback layers. */
375 index += 10;
376 glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, index, -1, name);
377 }
378
379 if (!G.profile_gpu) {
380 return;
381 }
382
383 TimeQuery query = {};
384 query.name = name;
385 query.finished = false;
386
387 glGetInteger64v(GL_TIMESTAMP, &query.cpu_start);
388 /* Use GL_TIMESTAMP instead of GL_ELAPSED_TIME to support nested debug groups */
389 glGenQueries(2, query.handles);
390 glQueryCounter(query.handle_start, GL_TIMESTAMP);
391
392 if (frame_timings.is_empty()) {
393 frame_timings.append({});
394 }
395 frame_timings.last().queries.append(query);
396}
397
399{
400 if ((G.debug & G_DEBUG_GPU) &&
401 (epoxy_gl_version() >= 43 || epoxy_has_gl_extension("GL_KHR_debug")))
402 {
403 glPopDebugGroup();
404 }
405
406 if (!G.profile_gpu) {
407 return;
408 }
409
410 Vector<TimeQuery> &queries = frame_timings.last().queries;
411 for (int i = queries.size() - 1; i >= 0; i--) {
412 TimeQuery &query = queries[i];
413 if (!query.finished) {
414 query.finished = true;
415 glQueryCounter(query.handle_end, GL_TIMESTAMP);
416 glGetInteger64v(GL_TIMESTAMP, &query.cpu_end);
417 break;
418 }
419 if (i == 0) {
420 CLOG_ERROR(&LOG, "Profile GPU error: Extra GPU_debug_group_end() call.");
421 }
422 }
423}
424
425void GLContext::process_frame_timings()
426{
427 if (!G.profile_gpu) {
428 return;
429 }
430
431 for (int frame_i = 0; frame_i < frame_timings.size(); frame_i++) {
432 Vector<TimeQuery> &queries = frame_timings[frame_i].queries;
433
434 GLint frame_is_ready = 0;
435 bool frame_is_valid = !queries.is_empty();
436
437 for (int i = queries.size() - 1; i >= 0; i--) {
438 if (!queries[i].finished) {
439 frame_is_valid = false;
440 CLOG_ERROR(&LOG, "Profile GPU error: Missing GPU_debug_group_end() call");
441 }
442 else {
443 glGetQueryObjectiv(queries.last().handle_end, GL_QUERY_RESULT_AVAILABLE, &frame_is_ready);
444 }
445 break;
446 }
447
448 if (!frame_is_valid) {
449 /* Cleanup. */
450 for (TimeQuery &query : queries) {
451 glDeleteQueries(2, query.handles);
452 }
453 frame_timings.remove(frame_i--);
454 continue;
455 }
456
457 if (!frame_is_ready) {
458 break;
459 }
460
461 for (TimeQuery &query : queries) {
462 GLuint64 gpu_start = 0;
463 GLuint64 gpu_end = 0;
464 glGetQueryObjectui64v(query.handle_start, GL_QUERY_RESULT, &gpu_start);
465 glGetQueryObjectui64v(query.handle_end, GL_QUERY_RESULT, &gpu_end);
466 glDeleteQueries(2, query.handles);
467
469 query.name, gpu_start, gpu_end, query.cpu_start, query.cpu_end);
470 }
471
472 frame_timings.remove(frame_i--);
473 }
474
475 frame_timings.append({});
476}
477
478bool GLContext::debug_capture_begin(const char *title)
479{
480 return GLBackend::get()->debug_capture_begin(title);
481}
482
483bool GLBackend::debug_capture_begin(const char *title)
484{
485#ifdef WITH_RENDERDOC
486 if (G.debug & G_DEBUG_GPU_RENDERDOC) {
487 bool result = renderdoc_.start_frame_capture(nullptr, nullptr);
488 if (result && title) {
489 renderdoc_.set_frame_capture_title(title);
490 }
491 return result;
492 }
493#endif
494 UNUSED_VARS(title);
495 return false;
496}
497
502
504{
505#ifdef WITH_RENDERDOC
506 if (G.debug & G_DEBUG_GPU_RENDERDOC) {
507 renderdoc_.end_frame_capture(nullptr, nullptr);
508 }
509#endif
510}
511
513{
514 return (void *)name;
515}
516
518{
519#ifdef WITH_RENDERDOC
520 const char *title = (const char *)scope;
521 if (StringRefNull(title) != StringRefNull(G.gpu_debug_scope_name)) {
522 return false;
523 }
525#else
526 UNUSED_VARS(scope);
527#endif
528 return false;
529}
530
532{
533#ifdef WITH_RENDERDOC
534 const char *title = (const char *)scope;
535 if (StringRefNull(title) == StringRefNull(G.gpu_debug_scope_name)) {
537 }
538#else
539 UNUSED_VARS(scope);
540#endif
541}
542
544{
545 this->bound_ubo_slots = 0u;
546}
547
549{
550 this->bound_ssbo_slots = 0u;
551}
552
554
555} // namespace blender::gpu
@ G_DEBUG_GPU
@ G_DEBUG_GPU_RENDERDOC
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:604
void BLI_system_backtrace(FILE *fp)
Definition system.cc:103
#define STRPREFIX(a, b)
#define UNUSED_VARS(...)
#define ELEM(...)
#define CLOG_ERROR(clg_ref,...)
Definition CLG_log.h:188
void void CLG_logf(const CLG_LogType *lg, enum CLG_Level level, const char *file_line, const char *fn, const char *format,...) _CLOG_ATTR_NONNULL(1
#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_INFO
Definition CLG_log.h:60
@ CLG_LEVEL_WARN
Definition CLG_log.h:58
int CLG_color_support_get(CLG_LogRef *clg_ref)
Definition clog.cc:1006
bool GPU_debug_group_match(const char *ref)
Definition gpu_debug.cc:61
#define GPU_DEBUG_SHADER_SPECIALIZATION_GROUP
Definition GPU_debug.hh:65
void GPU_debug_get_groups_names(int name_buf_len, char *r_name_buf)
Definition gpu_debug.cc:43
#define GPU_DEBUG_SHADER_COMPILATION_GROUP
Definition GPU_debug.hh:64
@ GPU_DEVICE_NVIDIA
bool GPU_type_matches(GPUDeviceType device, GPUOSType os, GPUDriverType driver)
@ GPU_DRIVER_OFFICIAL
@ GPU_OS_ANY
unsigned long long int uint64_t
int64_t size() const
constexpr const char * c_str() const
int64_t size() const
const T & last(const int64_t n=0) const
bool is_empty() const
static GLBackend * get()
Definition gl_backend.hh:79
bool debug_capture_begin(const char *title)
Definition gl_debug.cc:483
void * debug_capture_scope_create(const char *name) override
Definition gl_debug.cc:512
void debug_unbind_all_ubo() override
Definition gl_debug.cc:543
void debug_capture_end() override
Definition gl_debug.cc:498
bool debug_capture_begin(const char *title) override
Definition gl_debug.cc:478
void debug_group_begin(const char *name, int index) override
Definition gl_debug.cc:369
void debug_capture_scope_end(void *scope) override
Definition gl_debug.cc:531
void debug_group_end() override
Definition gl_debug.cc:398
static GLContext * get()
bool debug_capture_scope_begin(void *scope) override
Definition gl_debug.cc:517
void debug_unbind_all_ssbo() override
Definition gl_debug.cc:548
static ProfileReport & get()
void add_group(StringRefNull name, uint64_t gpu_start, uint64_t gpu_end, uint64_t cpu_start, uint64_t cpu_end)
StringRefNull name_get() const
#define ERROR_CASE(err)
#define APIENTRY
Definition gl_debug.cc:50
#define TRIM_NVIDIA_BUFFER_INFO
Definition gl_debug.cc:33
#define TRIM_SHADER_STATS_INFO
Definition gl_debug.cc:35
format
#define LOG(level)
Definition log.h:97
#define G(x, y, z)
static void error(const char *str)
void check_gl_resources(const char *info)
Definition gl_debug.cc:192
static void APIENTRY debug_callback(GLenum, GLenum type, GLuint, GLenum severity, GLsizei, const GLchar *message, const GLvoid *)
Definition gl_debug.cc:53
void raise_gl_error(const char *info)
Definition gl_debug.cc:276
static const char * to_str_prefix(GLenum type)
Definition gl_debug.cc:290
void check_gl_error(const char *info)
Definition gl_debug.cc:159
static const char * to_str_suffix(GLenum type)
Definition gl_debug.cc:315
void object_label(GLenum type, GLuint object, const char *name)
Definition gl_debug.cc:329
static CLG_LogRef LOG
static void tex_input(float *out, int num, bNodeStack *in, TexParams *params, short thread)
const char * name
i
Definition text_draw.cc:230