1 /*
2  *   Copyright (c) 2013 Arthur Huillet
3  *
4  *
5  *  This file is part of Freedroid
6  *
7  *  Freedroid is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  Freedroid is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with Freedroid; see the file COPYING. If not, write to the
19  *  Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
20  *  MA  02111-1307  USA
21  *
22  */
23 
24 /**
25  * This file contains OpenGL debug-related code.
26  * It makes use of KHR_debug if available.
27  */
28 
29 #define _open_gl_debug_c
30 
31 #include "system.h"
32 #include "defs.h"
33 #include "struct.h"
34 #include "global.h"
35 #include "proto.h"
36 
37 // For a not yet known reason, init_opengl_debug() crashes on some Win10
38 // computers (observed on 64b systems with NVidia GPU).
39 // Further test is needed to solve that issue.
40 
41 #if defined(HAVE_LIBGL) && (!defined __WIN32__)
42 
43 // Copy-paste of glext.h because SDL's is outdated
44 #ifndef GL_KHR_debug
45 #define GL_DEBUG_OUTPUT_SYNCHRONOUS       0x8242
46 #define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243
47 #define GL_DEBUG_CALLBACK_FUNCTION        0x8244
48 #define GL_DEBUG_CALLBACK_USER_PARAM      0x8245
49 #define GL_DEBUG_SOURCE_API               0x8246
50 #define GL_DEBUG_SOURCE_WINDOW_SYSTEM     0x8247
51 #define GL_DEBUG_SOURCE_SHADER_COMPILER   0x8248
52 #define GL_DEBUG_SOURCE_THIRD_PARTY       0x8249
53 #define GL_DEBUG_SOURCE_APPLICATION       0x824A
54 #define GL_DEBUG_SOURCE_OTHER             0x824B
55 #define GL_DEBUG_TYPE_ERROR               0x824C
56 #define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D
57 #define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR  0x824E
58 #define GL_DEBUG_TYPE_PORTABILITY         0x824F
59 #define GL_DEBUG_TYPE_PERFORMANCE         0x8250
60 #define GL_DEBUG_TYPE_OTHER               0x8251
61 #define GL_DEBUG_TYPE_MARKER              0x8268
62 #define GL_DEBUG_TYPE_PUSH_GROUP          0x8269
63 #define GL_DEBUG_TYPE_POP_GROUP           0x826A
64 #define GL_DEBUG_SEVERITY_NOTIFICATION    0x826B
65 #define GL_MAX_DEBUG_GROUP_STACK_DEPTH    0x826C
66 #define GL_DEBUG_GROUP_STACK_DEPTH        0x826D
67 #define GL_BUFFER                         0x82E0
68 #define GL_SHADER                         0x82E1
69 #define GL_PROGRAM                        0x82E2
70 #define GL_QUERY                          0x82E3
71 #define GL_PROGRAM_PIPELINE               0x82E4
72 #define GL_SAMPLER                        0x82E6
73 #define GL_DISPLAY_LIST                   0x82E7
74 /* DISPLAY_LIST used in compatibility profile only */
75 #define GL_MAX_LABEL_LENGTH               0x82E8
76 #define GL_MAX_DEBUG_MESSAGE_LENGTH       0x9143
77 #define GL_MAX_DEBUG_LOGGED_MESSAGES      0x9144
78 #define GL_DEBUG_LOGGED_MESSAGES          0x9145
79 #define GL_DEBUG_SEVERITY_HIGH            0x9146
80 #define GL_DEBUG_SEVERITY_MEDIUM          0x9147
81 #define GL_DEBUG_SEVERITY_LOW             0x9148
82 #define GL_DEBUG_OUTPUT                   0x92E0
83 #define GL_CONTEXT_FLAG_DEBUG_BIT         0x00000002
84 /* reuse GL_STACK_UNDERFLOW */
85 /* reuse GL_STACK_OVERFLOW */
86 typedef void (APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,GLvoid *userParam);
87 typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLPROC) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled);
88 typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTPROC) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf);
89 typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKPROC) (GLDEBUGPROC callback, const void *userParam);
90 typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGPROC) (GLuint count, GLsizei bufsize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog);
91 typedef void (APIENTRYP PFNGLPUSHDEBUGGROUPPROC) (GLenum source, GLuint id, GLsizei length, const GLchar *message);
92 typedef void (APIENTRYP PFNGLPOPDEBUGGROUPPROC) (void);
93 typedef void (APIENTRYP PFNGLOBJECTLABELPROC) (GLenum identifier, GLuint name, GLsizei length, const GLchar *label);
94 typedef void (APIENTRYP PFNGLGETOBJECTLABELPROC) (GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label);
95 typedef void (APIENTRYP PFNGLOBJECTPTRLABELPROC) (const void *ptr, GLsizei length, const GLchar *label);
96 typedef void (APIENTRYP PFNGLGETOBJECTPTRLABELPROC) (const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label);
97 #endif
98 
99 PFNGLDEBUGMESSAGECONTROLPROC glDebugMessageControl;
100 PFNGLDEBUGMESSAGECALLBACKPROC glDebugMessageCallback;
101 
102 #define DBG_FLAG(f) { f, #f }
103 struct debug_flag {
104 	GLenum flag_value;
105 	char *flag_descr;
106 } debug_flags[] = {
107 	DBG_FLAG(GL_DEBUG_OUTPUT_SYNCHRONOUS),
108 	DBG_FLAG(GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH),
109 	DBG_FLAG(GL_DEBUG_CALLBACK_FUNCTION),
110 	DBG_FLAG(GL_DEBUG_CALLBACK_USER_PARAM),
111 	DBG_FLAG(GL_DEBUG_SOURCE_API),
112 	DBG_FLAG(GL_DEBUG_SOURCE_WINDOW_SYSTEM),
113 	DBG_FLAG(GL_DEBUG_SOURCE_SHADER_COMPILER),
114 	DBG_FLAG(GL_DEBUG_SOURCE_THIRD_PARTY),
115 	DBG_FLAG(GL_DEBUG_SOURCE_APPLICATION),
116 	DBG_FLAG(GL_DEBUG_SOURCE_OTHER),
117 	DBG_FLAG(GL_DEBUG_TYPE_ERROR),
118 	DBG_FLAG(GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR),
119 	DBG_FLAG(GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR),
120 	DBG_FLAG(GL_DEBUG_TYPE_PORTABILITY),
121 	DBG_FLAG(GL_DEBUG_TYPE_PERFORMANCE),
122 	DBG_FLAG(GL_DEBUG_TYPE_OTHER),
123 	DBG_FLAG(GL_DEBUG_TYPE_MARKER),
124 	DBG_FLAG(GL_DEBUG_TYPE_PUSH_GROUP),
125 	DBG_FLAG(GL_DEBUG_TYPE_POP_GROUP),
126 	DBG_FLAG(GL_DEBUG_SEVERITY_NOTIFICATION),
127 	DBG_FLAG(GL_MAX_DEBUG_GROUP_STACK_DEPTH),
128 	DBG_FLAG(GL_DEBUG_GROUP_STACK_DEPTH),
129 	DBG_FLAG(GL_BUFFER),
130 	DBG_FLAG(GL_SHADER),
131 	DBG_FLAG(GL_PROGRAM),
132 	DBG_FLAG(GL_QUERY),
133 	DBG_FLAG(GL_PROGRAM_PIPELINE),
134 	DBG_FLAG(GL_SAMPLER),
135 	DBG_FLAG(GL_DISPLAY_LIST),
136 	DBG_FLAG(GL_MAX_LABEL_LENGTH),
137 	DBG_FLAG(GL_MAX_DEBUG_MESSAGE_LENGTH),
138 	DBG_FLAG(GL_MAX_DEBUG_LOGGED_MESSAGES),
139 	DBG_FLAG(GL_DEBUG_LOGGED_MESSAGES),
140 	DBG_FLAG(GL_DEBUG_SEVERITY_HIGH),
141 	DBG_FLAG(GL_DEBUG_SEVERITY_MEDIUM),
142 	DBG_FLAG(GL_DEBUG_SEVERITY_LOW),
143 	DBG_FLAG(GL_DEBUG_OUTPUT),
144 	{ 0x0503, "GL_STACK_OVERFLOW" },
145 	{ 0x0504, "GL_STACK_UNDERFLOW" }
146 };
147 #undef DBG_FLAG
148 
find_debug_flag(GLenum value)149 static struct debug_flag *find_debug_flag(GLenum value)
150 {
151 	unsigned int i;
152 	for (i=0; i < sizeof(debug_flags)/sizeof(debug_flags[0]); i++) {
153 		if (debug_flags[i].flag_value == value)
154 			return &debug_flags[i];
155 	}
156 	return NULL;
157 }
158 
gl_debug_callback(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar * message,GLvoid * userParam)159 static void gl_debug_callback(GLenum source, GLenum type, GLuint id,
160 							GLenum severity, GLsizei length, const GLchar* message,
161 							GLvoid* userParam)
162 {
163 	// Ignore certain message IDs
164 
165 	if (id == 131204) {
166 		// "Waste of memory: Texture 0 has mipmaps, while it's min filter is inconsistent with mipmaps."
167 		// Possible nvidia bug in version 313.30, ignore it for now
168 		return;
169 	}
170 	if (severity == GL_DEBUG_SEVERITY_NOTIFICATION) {
171 		// Do not display notifications
172 		return;
173 	}
174 
175 	// Report a good looking error message
176 
177 	struct auto_string *msg = alloc_autostr(256);
178 	struct debug_flag *data = NULL;
179 
180 	data = find_debug_flag(source);
181 	if (data) {
182 		autostr_append(msg, "Source = %s,", data->flag_descr);
183 	} else {
184 		autostr_append(msg, "Source = 0x%x,", source);
185 	}
186 
187 	data = find_debug_flag(type);
188 	if (data) {
189 		autostr_append(msg, " type = %s,", data->flag_descr);
190 	} else {
191 		autostr_append(msg, " type = 0x%x,", type);
192 	}
193 
194 	autostr_append(msg, " id = %d,", id);
195 
196 	data = find_debug_flag(severity);
197 	if (data) {
198 		autostr_append(msg, " severity = %s,", data->flag_descr);
199 	} else {
200 		autostr_append(msg, " severity = 0x%x,", severity);
201 	}
202 
203 	error_message(__FUNCTION__, "%s: %s", NO_REPORT, msg->value, message);
204 
205 	free_autostr(msg);
206 }
207 
208 /**
209  * Initialize and enabled the OpenGL debug features.
210  * @return 0 if OK, 1 if debug could not be enabled
211  */
init_opengl_debug(void)212 int init_opengl_debug(void)
213 {
214 	/* Check if KHR_debug is available */
215 	const char *extensions = (const char*)glGetString(GL_EXTENSIONS);
216 	if (!strstr(extensions, "GL_KHR_debug")) {
217 		// no debug extension available
218 		// We cannot use ARB_debug_output because it doesn't allow glEnable(GL_DEBUG_OUTPUT)
219 		return 1;
220 	}
221 
222 	glDebugMessageControl = SDL_GL_GetProcAddress("glDebugMessageControl");
223 	glDebugMessageCallback = SDL_GL_GetProcAddress("glDebugMessageCallback");
224 
225 	if (!glDebugMessageCallback || !glDebugMessageControl) {
226 		error_message(__FUNCTION__, "Unable to retrieve function pointers for glDebugMessageCallback and glDebugMessageControl, but debug extension is present.", PLEASE_INFORM);
227 		return 1;
228 	}
229 
230 	glEnable(GL_DEBUG_OUTPUT);
231 	glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
232 	glDebugMessageCallback(&gl_debug_callback, NULL);
233 	glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE);
234 
235 	return 0;
236 }
237 
238 #else // defined(HAVE_LIBGL) && (!defined __WIN32__)
239 
init_opengl_debug(void)240 int init_opengl_debug(void)
241 {
242 	return 1;
243 }
244 
245 #endif // defined(HAVE_LIBGL) && (!defined __WIN32__)
246 
247 /**
248  * This function checks the error status of the OpenGL driver.  An error
249  * will produce at least a warning message, maybe even program termination
250  * if the errors are really severe.
251  */
open_gl_check_error_status(const char * name_of_calling_function)252 void open_gl_check_error_status(const char *name_of_calling_function)
253 {
254 #ifdef HAVE_LIBGL
255 	char *enum_str = "UNKNOWN";
256 	int error_type = PLEASE_INFORM;
257 
258 	switch (glGetError()) {
259 		case GL_NO_ERROR:
260 			return;
261 		case GL_INVALID_ENUM:
262 			enum_str = "GL_INVALID_ENUM";
263 			break;
264 		case GL_INVALID_VALUE:
265 			enum_str = "GL_INVALID_VALUE";
266 			break;
267 		case GL_INVALID_OPERATION:
268 			enum_str = "GL_INVALID_OPERATION";
269 			break;
270 		case GL_STACK_OVERFLOW:
271 			enum_str = "GL_STACK_OVERFLOW";
272 			error_type |= IS_FATAL;
273 			break;
274 		case GL_STACK_UNDERFLOW:
275 			enum_str = "GL_STACK_UNDERFLOW";
276 			error_type |= IS_FATAL;
277 			break;
278 		case GL_OUT_OF_MEMORY:
279 			enum_str = "GL_OUT_OF_MEMORY";
280 			error_type |= IS_FATAL;
281 			break;
282 	}
283 
284 	error_message(__FUNCTION__, "Error code %s received, called by %s.", error_type, enum_str, name_of_calling_function);
285 #endif
286 }
287 
288 
289 #undef _open_gl_debug_c
290