feat: Using a glDebugMessageCallback instead of glGetError on devices that support it

This commit is contained in:
Chris Cameron
2019-09-14 09:33:33 -04:00
committed by Paul Chote
parent 460f5bbb30
commit 36c48e1785
2 changed files with 193 additions and 27 deletions

View File

@@ -41,8 +41,9 @@ Also thanks to:
* Brendan Gluth (Mechanical_Man)
* Bryan Wilbur
* Bugra Cuhadaroglu (BugraC)
* Christer Ulfsparre (Holloweye)
* Chris Cameron (Vesuvian)
* Chris Grant (Unit158)
* Christer Ulfsparre (Holloweye)
* clem
* Cody Brittain (Generalcamo)
* Constantin Helmig (CH4Code)

View File

@@ -10,6 +10,7 @@
#endregion
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
@@ -30,12 +31,15 @@ namespace OpenRA.Platforms.Default
None = 0,
Core = 1,
GLES = 2,
DebugMessagesCallback = 4
}
public static GLFeatures Features { get; private set; }
public static string Version { get; private set; }
#region Constants
public const int GL_FALSE = 0;
// ClearBufferMask
@@ -49,7 +53,29 @@ namespace OpenRA.Platforms.Default
// Errors
public const int GL_NO_ERROR = 0;
public const int GL_OUT_OF_MEMORY = 0x505;
public const int GL_INVALID_ENUM = 0x0500;
public const int GL_INVALID_VALUE = 0x0501;
public const int GL_INVALID_OPERATION = 0x0502;
public const int GL_STACK_OVERFLOW = 0x0503;
public const int GL_STACK_UNDERFLOW = 0x0504;
public const int GL_OUT_OF_MEMORY = 0x0505;
public const int GL_INVALID_FRAMEBUFFER_OPERATION = 0x0506;
public const int GL_CONTEXT_LOST = 0x0507;
public const int GL_TABLE_TOO_LARGE = 0x8031;
static readonly Dictionary<int, string> ErrorToText = new Dictionary<int, string>
{
{ GL_NO_ERROR, "No Error" },
{ GL_INVALID_ENUM, "Invalid Enum" },
{ GL_INVALID_VALUE, "Invalid Value" },
{ GL_INVALID_OPERATION, "Invalid Operation" },
{ GL_STACK_OVERFLOW, "Stack Overflow" },
{ GL_STACK_UNDERFLOW, "Stack Underflow" },
{ GL_OUT_OF_MEMORY, "Out Of Memory" },
{ GL_INVALID_FRAMEBUFFER_OPERATION, "Invalid Framebuffer Operation" },
{ GL_CONTEXT_LOST, "Context Lost" },
{ GL_TABLE_TOO_LARGE, "Table Too Large" },
};
// BeginMode
public const int GL_POINTS = 0;
@@ -117,6 +143,63 @@ namespace OpenRA.Platforms.Default
public const int GL_INFO_LOG_LENGTH = 0x8B84;
public const int GL_ACTIVE_UNIFORMS = 0x8B86;
// OpenGL 4.3
public const int GL_DEBUG_OUTPUT = 0x92E0;
public const int GL_DEBUG_OUTPUT_SYNCHRONOUS = 0x8242;
public const int GL_DEBUG_SOURCE_API = 0x8246;
public const int GL_DEBUG_SOURCE_WINDOW_SYSTEM = 0x8247;
public const int GL_DEBUG_SOURCE_SHADER_COMPILER = 0x8248;
public const int GL_DEBUG_SOURCE_THIRD_PARTY = 0x8249;
public const int GL_DEBUG_SOURCE_APPLICATION = 0x824A;
public const int GL_DEBUG_SOURCE_OTHER = 0x824B;
static readonly Dictionary<int, string> DebugSourceToText = new Dictionary<int, string>
{
{ GL_DEBUG_SOURCE_API, "API" },
{ GL_DEBUG_SOURCE_WINDOW_SYSTEM, "Window System" },
{ GL_DEBUG_SOURCE_SHADER_COMPILER, "Shader Compiler" },
{ GL_DEBUG_SOURCE_THIRD_PARTY, "Third Party" },
{ GL_DEBUG_SOURCE_APPLICATION, "Application" },
{ GL_DEBUG_SOURCE_OTHER, "Other" }
};
public const int GL_DEBUG_TYPE_ERROR = 0x824C;
public const int GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR = 0x824D;
public const int GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR = 0x824E;
public const int GL_DEBUG_TYPE_PORTABILITY = 0x824F;
public const int GL_DEBUG_TYPE_PERFORMANCE = 0x8250;
public const int GL_DEBUG_TYPE_MARKER = 0x8268;
public const int GL_DEBUG_TYPE_PUSH_GROUP = 0x8269;
public const int GL_DEBUG_TYPE_POP_GROUP = 0x826A;
public const int GL_DEBUG_TYPE_OTHER = 0x8251;
static readonly Dictionary<int, string> DebugTypeToText = new Dictionary<int, string>
{
{ GL_DEBUG_TYPE_ERROR, "Error" },
{ GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR, "Deprecated Behaviour" },
{ GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR, "Undefined Behaviour" },
{ GL_DEBUG_TYPE_PORTABILITY, "Portability" },
{ GL_DEBUG_TYPE_PERFORMANCE, "Performance" },
{ GL_DEBUG_TYPE_MARKER, "Marker" },
{ GL_DEBUG_TYPE_PUSH_GROUP, "Push Group" },
{ GL_DEBUG_TYPE_POP_GROUP, "Pop Group" },
{ GL_DEBUG_TYPE_OTHER, "Other" }
};
public const int GL_DEBUG_SEVERITY_HIGH = 0x9146;
public const int GL_DEBUG_SEVERITY_MEDIUM = 0x9147;
public const int GL_DEBUG_SEVERITY_LOW = 0x9148;
public const int GL_DEBUG_SEVERITY_NOTIFICATION = 0x826B;
static readonly Dictionary<int, string> DebugSeverityToText = new Dictionary<int, string>
{
{ GL_DEBUG_SEVERITY_HIGH, "High" },
{ GL_DEBUG_SEVERITY_MEDIUM, "Medium" },
{ GL_DEBUG_SEVERITY_LOW, "Low" },
{ GL_DEBUG_SEVERITY_NOTIFICATION, "Notification" }
};
// Pixel Mode / Transfer
public const int GL_PACK_ROW_LENGTH = 0x0D02;
public const int GL_PACK_ALIGNMENT = 0x0D05;
@@ -141,6 +224,20 @@ namespace OpenRA.Platforms.Default
public const int GL_FRAMEBUFFER_COMPLETE = 0x8CD5;
public const int GL_FRAMEBUFFER_BINDING = 0x8CA6;
#endregion
#region GL Delegates
public delegate void DebugProc(int source, int type, uint id, int severity, int length, StringBuilder message,
IntPtr userParam);
static DebugProc DebugMessageHandle { get; set; }
public delegate void DebugMessageCallback(DebugProc callback, IntPtr userParam);
public static DebugMessageCallback glDebugMessageCallback { get; private set; }
public delegate void DebugMessageInsert(int source, int type, uint id, int severity, int length, string message);
public static DebugMessageInsert glDebugMessageInsert { get; private set; }
public delegate void Flush();
public static Flush glFlush { get; private set; }
@@ -378,29 +475,54 @@ namespace OpenRA.Platforms.Default
public delegate int CheckFramebufferStatus(int target);
public static CheckFramebufferStatus glCheckFramebufferStatus { get; private set; }
#endregion
public static void Initialize()
{
// glGetError and glGetString are used in our error handlers
// so we want these to be available early.
try
{
// First set up the bindings we need for error handling
glEnable = Bind<Enable>("glEnable");
glDisable = Bind<Disable>("glDisable");
glGetError = Bind<GetError>("glGetError");
glGetStringInternal = Bind<GetString>("glGetString");
glGetStringiInternal = Bind<GetStringi>("glGetStringi");
}
catch (Exception)
catch (Exception e)
{
throw new InvalidProgramException("Failed to initialize low-level OpenGL bindings. GPU information is not available");
throw new InvalidProgramException("Failed to initialize low-level OpenGL bindings. GPU information is not available.", e);
}
DetectGLFeatures();
if (!Features.HasFlag(GLFeatures.Core))
{
WriteGraphicsLog("Unsupported OpenGL version: " + glGetString(GL_VERSION));
throw new InvalidProgramException("OpenGL Version Error: See graphics.log for details.");
}
else
Console.WriteLine("OpenGL version: " + glGetString(GL_VERSION));
// Setup the debug message callback handler
if (Features.HasFlag(GLFeatures.DebugMessagesCallback))
{
try
{
glDebugMessageCallback = Bind<DebugMessageCallback>("glDebugMessageCallback");
glDebugMessageInsert = Bind<DebugMessageInsert>("glDebugMessageInsert");
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
// Need to keep a reference to the callback so it doesn't get garbage collected
DebugMessageHandle = DebugMessageHandler;
glDebugMessageCallback(DebugMessageHandle, IntPtr.Zero);
}
catch (Exception e)
{
throw new InvalidProgramException("Failed to initialize an OpenGL debug message callback.", e);
}
}
Console.WriteLine("OpenGL version: " + glGetString(GL_VERSION));
try
{
@@ -445,8 +567,6 @@ namespace OpenRA.Platforms.Default
glEnableVertexAttribArray = Bind<EnableVertexAttribArray>("glEnableVertexAttribArray");
glDisableVertexAttribArray = Bind<DisableVertexAttribArray>("glDisableVertexAttribArray");
glDrawArrays = Bind<DrawArrays>("glDrawArrays");
glEnable = Bind<Enable>("glEnable");
glDisable = Bind<Disable>("glDisable");
glBlendEquation = Bind<BlendEquation>("glBlendEquation");
glBlendEquationSeparate = Bind<BlendEquationSeparate>("glBlendEquationSeparate");
glBlendFunc = Bind<BlendFunc>("glBlendFunc");
@@ -476,7 +596,7 @@ namespace OpenRA.Platforms.Default
catch (Exception e)
{
WriteGraphicsLog("Failed to initialize OpenGL bindings.\nInner exception was: {0}".F(e));
throw new InvalidProgramException("Failed to initialize OpenGL. See graphics.log for details.");
throw new InvalidProgramException("Failed to initialize OpenGL. See graphics.log for details.", e);
}
}
@@ -508,26 +628,15 @@ namespace OpenRA.Platforms.Default
Features = GLFeatures.Core | GLFeatures.GLES;
else if (major > 3 || (major == 3 && minor >= 2))
Features = GLFeatures.Core;
// Debug callbacks were introduced in GL 4.3
var hasDebugMessagesCallback = SDL.SDL_GL_ExtensionSupported("GL_KHR_debug") == SDL.SDL_bool.SDL_TRUE;
if (hasDebugMessagesCallback)
Features |= GLFeatures.DebugMessagesCallback;
}
catch (Exception) { }
}
public static void CheckGLError()
{
var n = glGetError();
if (n != GL_NO_ERROR)
{
var errorText = n == GL_OUT_OF_MEMORY ? "Out Of Memory" : n.ToString();
var error = "GL Error: {0}\n{1}".F(errorText, new StackTrace());
WriteGraphicsLog(error);
const string ExceptionMessage = "OpenGL Error: See graphics.log for details.";
if (n == GL_OUT_OF_MEMORY)
throw new OutOfMemoryException(ExceptionMessage);
else
throw new InvalidOperationException(ExceptionMessage);
}
}
public static void WriteGraphicsLog(string message)
{
Log.Write("graphics", message);
@@ -553,5 +662,61 @@ namespace OpenRA.Platforms.Default
for (var i = 0; i < extensionCount; i++)
Log.Write("graphics", glGetStringi(GL_EXTENSIONS, (uint)i));
}
public static void CheckGLError()
{
// Let the debug message handler log the errors instead.
if (Features.HasFlag(GLFeatures.DebugMessagesCallback))
return;
var type = glGetError();
if (type == GL_NO_ERROR)
return;
string errorText;
errorText = ErrorToText.TryGetValue(type, out errorText) ? errorText : type.ToString("X");
var error = "GL Error: {0}\n{1}".F(errorText, new StackTrace());
WriteGraphicsLog(error);
const string exceptionMessage = "OpenGL Error: See graphics.log for details.";
if (type == GL_OUT_OF_MEMORY)
throw new OutOfMemoryException(exceptionMessage);
throw new InvalidOperationException(exceptionMessage);
}
static void DebugMessageHandler(int source, int type, uint id, int severity, int length, StringBuilder message, IntPtr userparam)
{
string error;
switch (severity)
{
case GL_DEBUG_SEVERITY_HIGH:
error = BuildErrorText(source, type, severity, message);
WriteGraphicsLog(error);
throw new InvalidOperationException("OpenGL Error: See graphics.log for details.");
case GL_DEBUG_SEVERITY_MEDIUM:
error = BuildErrorText(source, type, severity, message);
Console.WriteLine(error);
break;
}
}
static string BuildErrorText(int source, int type, int severity, StringBuilder message)
{
string sourceText;
string typeText;
string severityText;
sourceText = DebugSourceToText.TryGetValue(source, out sourceText) ? sourceText : source.ToString("X");
typeText = DebugTypeToText.TryGetValue(type, out typeText) ? typeText : type.ToString("X");
severityText = DebugSeverityToText.TryGetValue(severity, out severityText) ? severityText : severity.ToString("X");
var messageText = message.ToString();
return "{0} - GL Debug {1} Output: {2} - {3}\n{4}".F(severityText, sourceText, typeText, messageText, new StackTrace());
}
}
}