feat: Using a glDebugMessageCallback instead of glGetError on devices that support it
This commit is contained in:
committed by
Paul Chote
parent
460f5bbb30
commit
36c48e1785
3
AUTHORS
3
AUTHORS
@@ -41,8 +41,9 @@ Also thanks to:
|
|||||||
* Brendan Gluth (Mechanical_Man)
|
* Brendan Gluth (Mechanical_Man)
|
||||||
* Bryan Wilbur
|
* Bryan Wilbur
|
||||||
* Bugra Cuhadaroglu (BugraC)
|
* Bugra Cuhadaroglu (BugraC)
|
||||||
* Christer Ulfsparre (Holloweye)
|
* Chris Cameron (Vesuvian)
|
||||||
* Chris Grant (Unit158)
|
* Chris Grant (Unit158)
|
||||||
|
* Christer Ulfsparre (Holloweye)
|
||||||
* clem
|
* clem
|
||||||
* Cody Brittain (Generalcamo)
|
* Cody Brittain (Generalcamo)
|
||||||
* Constantin Helmig (CH4Code)
|
* Constantin Helmig (CH4Code)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
@@ -30,12 +31,15 @@ namespace OpenRA.Platforms.Default
|
|||||||
None = 0,
|
None = 0,
|
||||||
Core = 1,
|
Core = 1,
|
||||||
GLES = 2,
|
GLES = 2,
|
||||||
|
DebugMessagesCallback = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GLFeatures Features { get; private set; }
|
public static GLFeatures Features { get; private set; }
|
||||||
|
|
||||||
public static string Version { get; private set; }
|
public static string Version { get; private set; }
|
||||||
|
|
||||||
|
#region Constants
|
||||||
|
|
||||||
public const int GL_FALSE = 0;
|
public const int GL_FALSE = 0;
|
||||||
|
|
||||||
// ClearBufferMask
|
// ClearBufferMask
|
||||||
@@ -49,7 +53,29 @@ namespace OpenRA.Platforms.Default
|
|||||||
|
|
||||||
// Errors
|
// Errors
|
||||||
public const int GL_NO_ERROR = 0;
|
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
|
// BeginMode
|
||||||
public const int GL_POINTS = 0;
|
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_INFO_LOG_LENGTH = 0x8B84;
|
||||||
public const int GL_ACTIVE_UNIFORMS = 0x8B86;
|
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
|
// Pixel Mode / Transfer
|
||||||
public const int GL_PACK_ROW_LENGTH = 0x0D02;
|
public const int GL_PACK_ROW_LENGTH = 0x0D02;
|
||||||
public const int GL_PACK_ALIGNMENT = 0x0D05;
|
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_COMPLETE = 0x8CD5;
|
||||||
public const int GL_FRAMEBUFFER_BINDING = 0x8CA6;
|
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 delegate void Flush();
|
||||||
public static Flush glFlush { get; private set; }
|
public static Flush glFlush { get; private set; }
|
||||||
|
|
||||||
@@ -378,29 +475,54 @@ namespace OpenRA.Platforms.Default
|
|||||||
public delegate int CheckFramebufferStatus(int target);
|
public delegate int CheckFramebufferStatus(int target);
|
||||||
public static CheckFramebufferStatus glCheckFramebufferStatus { get; private set; }
|
public static CheckFramebufferStatus glCheckFramebufferStatus { get; private set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
public static void Initialize()
|
public static void Initialize()
|
||||||
{
|
{
|
||||||
// glGetError and glGetString are used in our error handlers
|
|
||||||
// so we want these to be available early.
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// First set up the bindings we need for error handling
|
||||||
|
glEnable = Bind<Enable>("glEnable");
|
||||||
|
glDisable = Bind<Disable>("glDisable");
|
||||||
glGetError = Bind<GetError>("glGetError");
|
glGetError = Bind<GetError>("glGetError");
|
||||||
glGetStringInternal = Bind<GetString>("glGetString");
|
glGetStringInternal = Bind<GetString>("glGetString");
|
||||||
glGetStringiInternal = Bind<GetStringi>("glGetStringi");
|
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();
|
DetectGLFeatures();
|
||||||
|
|
||||||
if (!Features.HasFlag(GLFeatures.Core))
|
if (!Features.HasFlag(GLFeatures.Core))
|
||||||
{
|
{
|
||||||
WriteGraphicsLog("Unsupported OpenGL version: " + glGetString(GL_VERSION));
|
WriteGraphicsLog("Unsupported OpenGL version: " + glGetString(GL_VERSION));
|
||||||
throw new InvalidProgramException("OpenGL Version Error: See graphics.log for details.");
|
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
|
try
|
||||||
{
|
{
|
||||||
@@ -445,8 +567,6 @@ namespace OpenRA.Platforms.Default
|
|||||||
glEnableVertexAttribArray = Bind<EnableVertexAttribArray>("glEnableVertexAttribArray");
|
glEnableVertexAttribArray = Bind<EnableVertexAttribArray>("glEnableVertexAttribArray");
|
||||||
glDisableVertexAttribArray = Bind<DisableVertexAttribArray>("glDisableVertexAttribArray");
|
glDisableVertexAttribArray = Bind<DisableVertexAttribArray>("glDisableVertexAttribArray");
|
||||||
glDrawArrays = Bind<DrawArrays>("glDrawArrays");
|
glDrawArrays = Bind<DrawArrays>("glDrawArrays");
|
||||||
glEnable = Bind<Enable>("glEnable");
|
|
||||||
glDisable = Bind<Disable>("glDisable");
|
|
||||||
glBlendEquation = Bind<BlendEquation>("glBlendEquation");
|
glBlendEquation = Bind<BlendEquation>("glBlendEquation");
|
||||||
glBlendEquationSeparate = Bind<BlendEquationSeparate>("glBlendEquationSeparate");
|
glBlendEquationSeparate = Bind<BlendEquationSeparate>("glBlendEquationSeparate");
|
||||||
glBlendFunc = Bind<BlendFunc>("glBlendFunc");
|
glBlendFunc = Bind<BlendFunc>("glBlendFunc");
|
||||||
@@ -476,7 +596,7 @@ namespace OpenRA.Platforms.Default
|
|||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
WriteGraphicsLog("Failed to initialize OpenGL bindings.\nInner exception was: {0}".F(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;
|
Features = GLFeatures.Core | GLFeatures.GLES;
|
||||||
else if (major > 3 || (major == 3 && minor >= 2))
|
else if (major > 3 || (major == 3 && minor >= 2))
|
||||||
Features = GLFeatures.Core;
|
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) { }
|
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)
|
public static void WriteGraphicsLog(string message)
|
||||||
{
|
{
|
||||||
Log.Write("graphics", message);
|
Log.Write("graphics", message);
|
||||||
@@ -553,5 +662,61 @@ namespace OpenRA.Platforms.Default
|
|||||||
for (var i = 0; i < extensionCount; i++)
|
for (var i = 0; i < extensionCount; i++)
|
||||||
Log.Write("graphics", glGetStringi(GL_EXTENSIONS, (uint)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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user