Commits

Alex Szpakowski committed 0eacc7f

OpenGL debug output, take two. Set the LOVE_DEBUG_OUTPUT environment variable to 1 prior to launching love.

AMD_debug_output isn't supported because the latest AMD drivers support KHR_debug.

Comments (0)

Files changed (5)

src/modules/graphics/Graphics.h

 	 * Sets the current graphics display viewport and initializes the renderer.
 	 * @param width The viewport width.
 	 * @param height The viewport height.
+	 * @param debug Whether debug output should be enabled, if supported.
 	 **/
-	virtual bool setMode(int width, int height) = 0;
+	virtual bool setMode(int width, int height, bool debug) = 0;
 
 	/**
 	 * Un-sets the current graphics display mode (uninitializing objects if

src/modules/graphics/opengl/Graphics.cpp

 	currentWindow = love::window::sdl::Window::createSingleton();
 
 	if (currentWindow->isCreated())
-		setMode(currentWindow->getWidth(), currentWindow->getHeight());
+		setMode(currentWindow->getWidth(), currentWindow->getHeight(), false);
 }
 
 Graphics::~Graphics()
 	glMatrixMode(GL_MODELVIEW);
 }
 
-bool Graphics::setMode(int width, int height)
+bool Graphics::setMode(int width, int height, bool debug)
 {
 	this->width = width;
 	this->height = height;
 	// Set pixel row alignment
 	glPixelStorei(GL_UNPACK_ALIGNMENT, 2);
 
+	setDebugOutput(debug);
+
 	// Reload all volatile objects.
 	if (!Volatile::loadAll())
 		std::cerr << "Could not reload all volatile objects." << std::endl;
 	pixel_size_stack.push_back(1);
 }
 
+static const char *getDebugSeverityStr(GLenum severity)
+{
+	switch (severity)
+	{
+	case GL_DEBUG_SEVERITY_HIGH:
+		return "high";
+	case GL_DEBUG_SEVERITY_MEDIUM:
+		return "medium";
+	case GL_DEBUG_SEVERITY_LOW:
+		return "low";
+	default:
+		break;
+	}
+
+	return "unknown";
+}
+
+static const char *getDebugSourceStr(GLenum source)
+{
+	switch (source)
+	{
+	case GL_DEBUG_SOURCE_API:
+		return "API";
+	case GL_DEBUG_SOURCE_SHADER_COMPILER:
+		return "shader";
+	case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
+		return "window";
+	case GL_DEBUG_SOURCE_THIRD_PARTY:
+		return "external";
+	case GL_DEBUG_SOURCE_APPLICATION:
+		return "LOVE";
+	case GL_DEBUG_SOURCE_OTHER:
+	default:
+		break;
+	}
+
+	return "unknown";
+}
+
+static const char *getDebugTypeStr(GLenum type)
+{
+	switch (type)
+	{
+	case GL_DEBUG_TYPE_ERROR:
+		return "error";
+	case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
+		return "deprecated behaviour";
+	case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
+		return "undefined behaviour";
+	case GL_DEBUG_TYPE_PERFORMANCE:
+		return "performance";
+	case GL_DEBUG_TYPE_PORTABILITY:
+		return "portability";
+	case GL_DEBUG_TYPE_OTHER:
+		return "other";
+	default:
+		break;
+	}
+
+	return "unknown";
+}
+
+static void APIENTRY debugCB(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei /*len*/, const GLchar *msg, GLvoid* /*usr*/)
+{
+	// Human-readable strings for the debug info.
+	const char *source_str = getDebugSourceStr(source);
+	const char *type_str = getDebugTypeStr(type);
+	const char *severity_str = getDebugSeverityStr(severity);
+
+	const char *format = "OpenGL: %s [source=%s, type=%s, severity=%s, id=%d]\n";
+	printf(format, msg, source_str, type_str, severity_str, id);
+}
+
+void Graphics::setDebugOutput(bool enable)
+{
+	// Make sure debug output is supported. The AMD variant is a bit different
+	// so we don't check for it, since AMD drivers now support KHR_debug.
+	if (!(GLEE_VERSION_4_3 || GLEE_KHR_debug || GLEE_ARB_debug_output))
+		return;
+
+	// Ugly hack to reduce code duplication.
+	if (GLEE_VERSION_4_3 || GLEE_KHR_debug)
+	{
+		glDebugMessageCallbackARB = (GLEEPFNGLDEBUGMESSAGECALLBACKARBPROC) glDebugMessageCallback;
+		glDebugMessageControlARB = (GLEEPFNGLDEBUGMESSAGECONTROLARBPROC) glDebugMessageControl;
+	}
+
+	if (!enable)
+	{
+		// Disable all debug messages and synchronous callbacks.
+		glDebugMessageCallbackARB(NULL, NULL);
+		glDisable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
+
+		// We can disable debug output entirely with GL_KHR_debug.
+		if (GLEE_VERSION_4_3 || GLEE_KHR_debug)
+			glDisable(GL_DEBUG_OUTPUT);
+
+		return;
+	}
+
+	// Synchronous output because the callback function isn't thread-safe.
+	glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
+
+	glDebugMessageCallbackARB(debugCB, NULL);
+
+	// Initially, enable everything.
+	glDebugMessageControlARB(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, 0, GL_TRUE);
+
+	// Disable messages about deprecated OpenGL functionality.
+	glDebugMessageControlARB(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR, GL_DONT_CARE, 0, 0, GL_FALSE);
+	glDebugMessageControlARB(GL_DEBUG_SOURCE_SHADER_COMPILER, GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR, GL_DONT_CARE, 0, 0, GL_FALSE);
+}
+
 void Graphics::clear()
 {
 	glClear(GL_COLOR_BUFFER_BIT);

src/modules/graphics/opengl/Graphics.h

 	void restoreState(const DisplayState &s);
 
 	virtual void setViewportSize(int width, int height);
-	virtual bool setMode(int width, int height);
+	virtual bool setMode(int width, int height, bool debug);
 	virtual void unSetMode();
 
 	/**
 
 private:
 
+	void setDebugOutput(bool enable);
+
 	Font *currentFont;
 	love::window::Window *currentWindow;
 

src/modules/window/sdl/Window.cpp

 #include <vector>
 #include <algorithm>
 
+// C
+#include <cstdlib>
+#include <cstring>
+
 namespace love
 {
 namespace window
 
 	SDL_RaiseWindow(window);
 
-	if (!setContext(f.fsaa, f.vsync))
+	// Has debug output been requested?
+	const char *envvar = getenv("LOVE_DEBUG_OUTPUT");
+	bool debug_output = (envvar && strcmp(envvar, "1") == 0);
+
+	if (!setContext(f.fsaa, f.vsync, debug_output))
 		return false;
 
 	created = true;
 	updateWindowFlags(f);
 
 	if (gfx)
-		gfx->setMode(curMode.width, curMode.height);
+		gfx->setMode(curMode.width, curMode.height, debug_output);
 
 	// Make sure the mouse keeps its previous grab setting.
 	setMouseGrab(mouseGrabbed);
 	return true;
 }
 
-bool Window::setContext(int fsaa, bool vsync)
+bool Window::setContext(int fsaa, bool vsync, bool debug)
 {
 	// We would normally only need to recreate the context if FSAA changes or
 	// SDL_GL_MakeCurrent is unsuccessful, but in Windows MakeCurrent can
 	// Make sure the proper attributes are set.
 	setWindowGLAttributes(fsaa);
 
+	if (debug)
+	{
+		SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
+		SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
+	}
+	else
+	{
+		SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
+		SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0);
+	}
+
 	context = SDL_GL_CreateContext(window);
 
 	if (!context && fsaa > 0)
 		context = SDL_GL_CreateContext(window);
 	}
 
+	if (!context && debug)
+	{
+		// Debug output might not be supported, disable it and try again.
+		SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
+		SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0);
+		context = SDL_GL_CreateContext(window);
+	}
+
 	if (!context)
 	{
 		std::cerr << "Could not set video mode: " << SDL_GetError() << std::endl;

src/modules/window/sdl/Window.h

 
 private:
 
-	bool setContext(int fsaa, bool vsync);
+	bool setContext(int fsaa, bool vsync, bool debug);
 	void setWindowGLAttributes(int fsaa) const;
 
 	// Update the window flags based on the window's actual state.