Blender V4.3
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
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
21#include "CLG_log.h"
22
23#include "gl_backend.hh"
24#include "gl_context.hh"
25#include "gl_uniform_buffer.hh"
26
27#include "gl_debug.hh"
28
29#include <cstdio>
30
31static CLG_LogRef LOG = {"gpu.debug"};
32
33/* Avoid too much NVidia buffer info in the output log. */
34#define TRIM_NVIDIA_BUFFER_INFO 1
35/* Avoid unneeded shader statistics. */
36#define TRIM_SHADER_STATS_INFO 1
37
38namespace blender::gpu::debug {
39
40/* -------------------------------------------------------------------- */
47/* Debug callbacks need the same calling convention as OpenGL functions. */
48#if defined(_WIN32)
49# define APIENTRY __stdcall
50#else
51# define APIENTRY
52#endif
53
54static void APIENTRY debug_callback(GLenum /*source*/,
55 GLenum type,
56 GLuint /*id*/,
57 GLenum severity,
58 GLsizei /*length*/,
59 const GLchar *message,
60 const GLvoid * /*userParm*/)
61{
62 if (ELEM(type, GL_DEBUG_TYPE_PUSH_GROUP, GL_DEBUG_TYPE_POP_GROUP)) {
63 /* The debug layer will emit a message each time a debug group is pushed or popped.
64 * We use that for easy command grouping inside frame analyzer tools. */
65 return;
66 }
67
68 /* NOTE: callback function can be triggered during before the platform is initialized.
69 * In this case invoking `GPU_type_matches` would fail and
70 * therefore the message is checked before the platform matching. */
71 if (TRIM_NVIDIA_BUFFER_INFO && STRPREFIX(message, "Buffer detailed info") &&
73 {
74 /* Suppress buffer infos flooding the output. */
75 return;
76 }
77
78 if (TRIM_SHADER_STATS_INFO && STRPREFIX(message, "Shader Stats")) {
79 /* Suppress buffer infos flooding the output. */
80 return;
81 }
82
83 const bool use_color = CLG_color_support_get(&LOG);
84
85 if (ELEM(severity, GL_DEBUG_SEVERITY_LOW, GL_DEBUG_SEVERITY_NOTIFICATION)) {
87 const char *format = use_color ? "\033[2m%s\033[0m" : "%s";
88 CLG_logf(LOG.type, CLG_SEVERITY_INFO, "Notification", "", format, message);
89 }
90 }
91 else {
92 char debug_groups[512] = "";
93 GPU_debug_get_groups_names(sizeof(debug_groups), debug_groups);
94 CLG_Severity clog_severity;
95
97 /* Do not duplicate shader compilation error/warnings. */
98 return;
99 }
100
101 switch (type) {
102 case GL_DEBUG_TYPE_ERROR:
103 case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
104 case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
105 clog_severity = CLG_SEVERITY_ERROR;
106 break;
107 case GL_DEBUG_TYPE_PORTABILITY:
108 case GL_DEBUG_TYPE_PERFORMANCE:
109 case GL_DEBUG_TYPE_OTHER:
110 case GL_DEBUG_TYPE_MARKER: /* KHR has this, ARB does not */
111 default:
112 clog_severity = CLG_SEVERITY_WARN;
113 break;
114 }
115
116 if ((LOG.type->flag & CLG_FLAG_USE) && (LOG.type->level <= clog_severity)) {
117 CLG_logf(LOG.type, clog_severity, debug_groups, "", "%s", message);
118 if (severity == GL_DEBUG_SEVERITY_HIGH) {
119 /* Focus on error message. */
120 if (use_color) {
121 fprintf(stderr, "\033[2m");
122 }
123 BLI_system_backtrace(stderr);
124 if (use_color) {
125 fprintf(stderr, "\033[0m\n");
126 }
127 fflush(stderr);
128 }
129 }
130 }
131}
132
133#undef APIENTRY
134
136{
138
139 char msg[256] = "";
140 const char format[] = "Successfully hooked OpenGL debug callback using %s";
141
142 if (epoxy_gl_version() >= 43 || epoxy_has_gl_extension("GL_KHR_debug")) {
143 SNPRINTF(msg, format, epoxy_gl_version() >= 43 ? "OpenGL 4.3" : "KHR_debug extension");
144 glEnable(GL_DEBUG_OUTPUT);
145 glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
146 glDebugMessageCallback((GLDEBUGPROC)debug_callback, nullptr);
147 glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE);
148 glDebugMessageInsert(GL_DEBUG_SOURCE_APPLICATION,
149 GL_DEBUG_TYPE_MARKER,
150 0,
151 GL_DEBUG_SEVERITY_NOTIFICATION,
152 -1,
153 msg);
154 }
155 else if (epoxy_has_gl_extension("GL_ARB_debug_output")) {
156 SNPRINTF(msg, format, "ARB_debug_output");
157 glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
158 glDebugMessageCallbackARB((GLDEBUGPROCARB)debug_callback, nullptr);
159 glDebugMessageControlARB(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE);
160 glDebugMessageInsertARB(GL_DEBUG_SOURCE_APPLICATION_ARB,
161 GL_DEBUG_TYPE_OTHER_ARB,
162 0,
163 GL_DEBUG_SEVERITY_LOW_ARB,
164 -1,
165 msg);
166 }
167 else {
168 CLOG_STR_WARN(&LOG, "Failed to hook OpenGL debug callback. Use fallback debug layer.");
170 }
171}
172
175/* -------------------------------------------------------------------- */
182void check_gl_error(const char *info)
183{
184 if (!(G.debug & G_DEBUG_GPU)) {
185 return;
186 }
187 GLenum error = glGetError();
188
189#define ERROR_CASE(err) \
190 case err: { \
191 char msg[256]; \
192 SNPRINTF(msg, "%s : %s", #err, info); \
193 debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, nullptr); \
194 break; \
195 }
196
197 switch (error) {
198 ERROR_CASE(GL_INVALID_ENUM)
199 ERROR_CASE(GL_INVALID_VALUE)
200 ERROR_CASE(GL_INVALID_OPERATION)
201 ERROR_CASE(GL_INVALID_FRAMEBUFFER_OPERATION)
202 ERROR_CASE(GL_OUT_OF_MEMORY)
203 ERROR_CASE(GL_STACK_UNDERFLOW)
204 ERROR_CASE(GL_STACK_OVERFLOW)
205 case GL_NO_ERROR:
206 break;
207 default:
208 char msg[256];
209 SNPRINTF(msg, "Unknown GL error: %x : %s", error, info);
210 debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, nullptr);
211 break;
212 }
213}
214
215void check_gl_resources(const char *info)
216{
217 if (!(G.debug & G_DEBUG_GPU) || GPU_bgl_get()) {
218 return;
219 }
220
221 GLContext *ctx = GLContext::get();
222 ShaderInterface *interface = ctx->shader->interface;
223 /* NOTE: This only check binding. To be valid, the bound ubo needs to
224 * be big enough to feed the data range the shader awaits. */
225 uint16_t ubo_needed = interface->enabled_ubo_mask_;
226 ubo_needed &= ~ctx->bound_ubo_slots;
227 /* NOTE: This only check binding. To be valid, the bound ssbo needs to
228 * be big enough to feed the data range the shader awaits. */
229 uint16_t ssbo_needed = interface->enabled_ssbo_mask_;
230 ssbo_needed &= ~ctx->bound_ssbo_slots;
231 /* NOTE: This only check binding. To be valid, the bound texture needs to
232 * be the same format/target the shader expects. */
233 uint64_t tex_needed = interface->enabled_tex_mask_;
234 tex_needed &= ~GLContext::state_manager_active_get()->bound_texture_slots();
235 /* NOTE: This only check binding. To be valid, the bound image needs to
236 * be the same format/target the shader expects. */
237 uint8_t ima_needed = interface->enabled_ima_mask_;
238 ima_needed &= ~GLContext::state_manager_active_get()->bound_image_slots();
239
240 if (ubo_needed == 0 && tex_needed == 0 && ima_needed == 0 && ssbo_needed == 0) {
241 return;
242 }
243
244 for (int i = 0; ubo_needed != 0; i++, ubo_needed >>= 1) {
245 if ((ubo_needed & 1) != 0) {
246 const ShaderInput *ubo_input = interface->ubo_get(i);
247 const char *ubo_name = interface->input_name_get(ubo_input);
248 const char *sh_name = ctx->shader->name_get();
249 char msg[256];
250 SNPRINTF(msg, "Missing UBO bind at slot %d : %s > %s : %s", i, sh_name, ubo_name, info);
251 debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, nullptr);
252 }
253 }
254
255 for (int i = 0; ssbo_needed != 0; i++, ssbo_needed >>= 1) {
256 if ((ssbo_needed & 1) != 0) {
257 const ShaderInput *ssbo_input = interface->ssbo_get(i);
258 const char *ssbo_name = interface->input_name_get(ssbo_input);
259 const char *sh_name = ctx->shader->name_get();
260 char msg[256];
261 SNPRINTF(msg, "Missing SSBO bind at slot %d : %s > %s : %s", i, sh_name, ssbo_name, info);
262 debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, nullptr);
263 }
264 }
265
266 for (int i = 0; tex_needed != 0; i++, tex_needed >>= 1) {
267 if ((tex_needed & 1) != 0) {
268 /* FIXME: texture_get might return an image input instead. */
269 const ShaderInput *tex_input = interface->texture_get(i);
270 const char *tex_name = interface->input_name_get(tex_input);
271 const char *sh_name = ctx->shader->name_get();
272 char msg[256];
273 SNPRINTF(msg, "Missing Texture bind at slot %d : %s > %s : %s", i, sh_name, tex_name, info);
274 debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, nullptr);
275 }
276 }
277
278 for (int i = 0; ima_needed != 0; i++, ima_needed >>= 1) {
279 if ((ima_needed & 1) != 0) {
280 /* FIXME: texture_get might return a texture input instead. */
281 const ShaderInput *tex_input = interface->texture_get(i);
282 const char *tex_name = interface->input_name_get(tex_input);
283 const char *sh_name = ctx->shader->name_get();
284 char msg[256];
285 SNPRINTF(msg, "Missing Image bind at slot %d : %s > %s : %s", i, sh_name, tex_name, info);
286 debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, nullptr);
287 }
288 }
289}
290
291void raise_gl_error(const char *info)
292{
293 debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, info, nullptr);
294}
295
298/* -------------------------------------------------------------------- */
305static const char *to_str_prefix(GLenum type)
306{
307 switch (type) {
308 case GL_FRAGMENT_SHADER:
309 case GL_GEOMETRY_SHADER:
310 case GL_VERTEX_SHADER:
311 case GL_SHADER:
312 case GL_PROGRAM:
313 return "SHD-";
314 case GL_SAMPLER:
315 return "SAM-";
316 case GL_TEXTURE:
317 return "TEX-";
318 case GL_FRAMEBUFFER:
319 return "FBO-";
320 case GL_VERTEX_ARRAY:
321 return "VAO-";
322 case GL_UNIFORM_BUFFER:
323 return "UBO-";
324 case GL_BUFFER:
325 return "BUF-";
326 default:
327 return "";
328 }
329}
330static const char *to_str_suffix(GLenum type)
331{
332 switch (type) {
333 case GL_FRAGMENT_SHADER:
334 return "-Frag";
335 case GL_GEOMETRY_SHADER:
336 return "-Geom";
337 case GL_VERTEX_SHADER:
338 return "-Vert";
339 default:
340 return "";
341 }
342}
343
344void object_label(GLenum type, GLuint object, const char *name)
345{
346 if ((G.debug & G_DEBUG_GPU) &&
347 (epoxy_gl_version() >= 43 || epoxy_has_gl_extension("GL_KHR_debug")))
348 {
349 char label[64];
350 SNPRINTF(label, "%s%s%s", to_str_prefix(type), name, to_str_suffix(type));
351 /* Small convenience for caller. */
352 switch (type) {
353 case GL_FRAGMENT_SHADER:
354 case GL_GEOMETRY_SHADER:
355 case GL_VERTEX_SHADER:
356 case GL_COMPUTE_SHADER:
357 type = GL_SHADER;
358 break;
359 case GL_UNIFORM_BUFFER:
360 case GL_SHADER_STORAGE_BUFFER:
361 case GL_ARRAY_BUFFER:
362 case GL_ELEMENT_ARRAY_BUFFER:
363 type = GL_BUFFER;
364 break;
365 default:
366 break;
367 }
368 glObjectLabel(type, object, -1, label);
369 }
370}
371
374} // namespace blender::gpu::debug
375
376namespace blender::gpu {
377
378/* -------------------------------------------------------------------- */
384void GLContext::debug_group_begin(const char *name, int index)
385{
386 if ((G.debug & G_DEBUG_GPU) &&
387 (epoxy_gl_version() >= 43 || epoxy_has_gl_extension("GL_KHR_debug")))
388 {
389 /* Add 10 to avoid collision with other indices from other possible callback layers. */
390 index += 10;
391 glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, index, -1, name);
392 }
393}
394
396{
397 if ((G.debug & G_DEBUG_GPU) &&
398 (epoxy_gl_version() >= 43 || epoxy_has_gl_extension("GL_KHR_debug")))
399 {
400 glPopDebugGroup();
401 }
402}
403
404bool GLContext::debug_capture_begin(const char *title)
405{
406 return GLBackend::get()->debug_capture_begin(title);
407}
408
409bool GLBackend::debug_capture_begin(const char *title)
410{
411#ifdef WITH_RENDERDOC
412 if (G.debug & G_DEBUG_GPU_RENDERDOC) {
413 bool result = renderdoc_.start_frame_capture(nullptr, nullptr);
414 if (result && title) {
415 renderdoc_.set_frame_capture_title(title);
416 }
417 return result;
418 }
419#endif
420 UNUSED_VARS(title);
421 return false;
422}
423
428
430{
431#ifdef WITH_RENDERDOC
432 if (G.debug & G_DEBUG_GPU_RENDERDOC) {
433 renderdoc_.end_frame_capture(nullptr, nullptr);
434 }
435#endif
436}
437
439{
440 return (void *)name;
441}
442
444{
445#ifdef WITH_RENDERDOC
446 const char *title = (const char *)scope;
447 if (StringRefNull(title) != StringRefNull(G.gpu_debug_scope_name)) {
448 return false;
449 }
451#else
452 UNUSED_VARS(scope);
453#endif
454 return false;
455}
456
458{
459#ifdef WITH_RENDERDOC
460 const char *title = (const char *)scope;
461 if (StringRefNull(title) == StringRefNull(G.gpu_debug_scope_name)) {
463 }
464#else
465 UNUSED_VARS(scope);
466#endif
467}
468
470{
471 this->bound_ubo_slots = 0u;
472}
473
475{
476 this->bound_ssbo_slots = 0u;
477}
478
481} // namespace blender::gpu
@ G_DEBUG_GPU
@ G_DEBUG_GPU_RENDERDOC
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:597
void BLI_system_backtrace(FILE *fp)
Definition system.c:63
#define STRPREFIX(a, b)
#define UNUSED_VARS(...)
#define ELEM(...)
CLG_Severity
Definition CLG_log.h:87
@ CLG_SEVERITY_INFO
Definition CLG_log.h:88
@ CLG_SEVERITY_WARN
Definition CLG_log.h:89
@ CLG_SEVERITY_ERROR
Definition CLG_log.h:90
@ CLG_FLAG_USE
Definition CLG_log.h:84
void void CLG_logf(const CLG_LogType *lg, enum CLG_Severity severity, const char *file_line, const char *fn, const char *format,...) _CLOG_ATTR_NONNULL(1
#define CLOG_ENSURE(clg_ref)
Definition CLG_log.h:150
#define CLOG_STR_WARN(clg_ref, str)
Definition CLG_log.h:187
int CLG_color_support_get(CLG_LogRef *clg_ref)
Definition clog.c:790
bool GPU_debug_group_match(const char *ref)
Definition gpu_debug.cc:61
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:66
@ GPU_DRIVER_OFFICIAL
@ GPU_OS_ANY
@ GPU_DEVICE_NVIDIA
bool GPU_type_matches(eGPUDeviceType device, eGPUOSType os, eGPUDriverType driver)
bool GPU_bgl_get()
Definition gpu_state.cc:363
static GLBackend * get()
Definition gl_backend.hh:65
bool debug_capture_begin(const char *title)
Definition gl_debug.cc:409
void * debug_capture_scope_create(const char *name) override
Definition gl_debug.cc:438
void debug_unbind_all_ubo() override
Definition gl_debug.cc:469
void debug_capture_end() override
Definition gl_debug.cc:424
bool debug_capture_begin(const char *title) override
Definition gl_debug.cc:404
void debug_group_begin(const char *name, int index) override
Definition gl_debug.cc:384
void debug_capture_scope_end(void *scope) override
Definition gl_debug.cc:457
void debug_group_end() override
Definition gl_debug.cc:395
static GLContext * get()
bool debug_capture_scope_begin(void *scope) override
Definition gl_debug.cc:443
void debug_unbind_all_ssbo() override
Definition gl_debug.cc:474
const char *const name_get() const
const char * label
#define ERROR_CASE(err)
#define APIENTRY
Definition gl_debug.cc:51
#define TRIM_NVIDIA_BUFFER_INFO
Definition gl_debug.cc:34
#define TRIM_SHADER_STATS_INFO
Definition gl_debug.cc:36
static CLG_LogRef LOG
Definition gl_debug.cc:31
format
#define G(x, y, z)
static void error(const char *str)
void check_gl_resources(const char *info)
Definition gl_debug.cc:215
static void APIENTRY debug_callback(GLenum, GLenum type, GLuint, GLenum severity, GLsizei, const GLchar *message, const GLvoid *)
Definition gl_debug.cc:54
void raise_gl_error(const char *info)
Definition gl_debug.cc:291
static const char * to_str_prefix(GLenum type)
Definition gl_debug.cc:305
void check_gl_error(const char *info)
Definition gl_debug.cc:182
static const char * to_str_suffix(GLenum type)
Definition gl_debug.cc:330
void object_label(GLenum type, GLuint object, const char *name)
Definition gl_debug.cc:344
static void tex_input(float *out, int num, bNodeStack *in, TexParams *params, short thread)
unsigned short uint16_t
Definition stdint.h:79
unsigned char uint8_t
Definition stdint.h:78
unsigned __int64 uint64_t
Definition stdint.h:90
CLG_LogType * type
Definition CLG_log.h:108
enum CLG_LogFlag flag
Definition CLG_log.h:103