Commit 56a352f3 authored by Robert Menzel's avatar Robert Menzel

added KHR_debug emulator with basic DebugMessageControl

parent 21b3492b
......@@ -14,6 +14,7 @@
#include <ACGL/ACGL.hh>
#include <ACGL/OpenGL/GL.hh>
#include <ACGL/OpenGL/Debug.hh>
namespace ACGL{
namespace OpenGL{
......@@ -222,6 +223,8 @@ inline bool openGLErrorOccuredDummy() { return false; }
# define openGLRareErrorOccured() ACGL::OpenGL::openGLErrorOccuredDummy()
#endif
} // OpenGL
} // ACGL
......
......@@ -67,8 +67,11 @@ void ACGLRegisterDefaultDebugCallback()
//! place a brakepoint in here to find the source of a problem!
void ACGL_KHR_default_debug_callback( GLenum _source, GLenum _type, GLuint _id, GLenum _severity, GLsizei _length, const GLchar *_message, const void *_userParam)
{
debug() << "<" << _id << "> " << debugSeverityName(_severity) << ": " << debugSourceName(_source) << " " << debugTypeName(_type) << " " << _message << endl;
if (_type == GL_DEBUG_TYPE_ERROR) {
error() << "<" << _id << "> severity: " << debugSeverityName(_severity) << " source: " << debugSourceName(_source) << ": " << _message << endl;
} else {
debug() << "<" << _id << "> severity: " << debugSeverityName(_severity) << " source: " << debugSourceName(_source) << ": " << _message << endl;
}
// delete all errors to not create another error log for the same problem:
while ( glGetError() != GL_NO_ERROR ) {}
}
......
......@@ -148,7 +148,7 @@ void TextureBase::generateMipmaps(void)
#if (!defined ACGL_OPENGL_PROFILE_CORE)
// on some ATI systems texturing has to be enabled to generate MipMaps
// this is not needed by the spec an deprecated on core profiles (generates
// this is not needed by the spec and deprecated on core profiles (generates
// an error on MacOS X Lion)
glEnable(mTarget);
openGLRareError();
......
#include <ACGL/OpenGL/GL.hh>
//#include <iostream>
//using namespace std;
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
// global variables:
GLenum KHR_DEBUG_EMULATOR_lastGLError = GL_NO_ERROR;
GLDEBUGPROC KHR_DEBUG_EMULATOR_callback = NULL;
const void *KHR_DEBUG_EMULATOR_userParam = NULL;
int KHR_DEBUG_EMULATOR_isEnabled = 1;
int KHR_DEBUG_EMULATOR_isSynchronous = 0;
// implementation dependent limits:
int KHR_DEBUG_EMULATOR_MAX_DEBUG_MESSAGE_LENGTH = 256;
int KHR_DEBUG_EMULATOR_MAX_DEBUG_LOGGED_MESSAGES = 1;
int KHR_DEBUG_EMULATOR_MAX_DEBUG_GROUP_STACK_DEPTH = 64;
int KHR_DEBUG_EMULATOR_MAX_LABEL_LENGTH = 256;
// extern declarations of internal original OpenGL calls (unwrapped)
extern GLenum (CODEGEN_FUNCPTR *_original_glGetError)();
extern GLvoid (CODEGEN_FUNCPTR *_original_glEnable)( GLenum );
extern GLvoid (CODEGEN_FUNCPTR *_original_glDisable)( GLenum );
extern GLboolean (CODEGEN_FUNCPTR *_original_glIsEnabled)( GLenum );
extern GLvoid (CODEGEN_FUNCPTR *_original_glGetIntegerv)( GLenum, GLint * );
extern GLvoid (CODEGEN_FUNCPTR *_original_glGetPointerv)( GLenum, GLvoid ** );
// helper:
#define KHR_DEBUG_EMULATOR_insertMissingFeatureError( m ) KHR_DEBUG_EMULATOR_DebugMessageInsert_internal( GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_MEDIUM, -1, m )
#define INSERT_API_ERROR( e, m ) KHR_DEBUG_EMULATOR_lastGLError = e; KHR_DEBUG_EMULATOR_DebugMessageInsert_internal(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_ERROR, e, GL_DEBUG_SEVERITY_HIGH, -1, m );
struct DebugMessage {
GLenum source;
GLenum type;
GLuint id;
GLenum severity;
GLsizei length;
const GLchar *buf;
};
DebugMessage g_LastDebugMessage;
GLboolean g_LastDebugMessageEmpty = true;
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
///
/// Replacements of the extension functions:
///
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
GLboolean isValidSeverity( GLenum e );
GLboolean isValidType( GLenum e );
GLboolean isValidSource( GLenum e );
void KHR_DEBUG_EMULATOR_DebugMessageInsert_internal(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar * buf);
GLboolean shouldMessageGetProcessed( GLenum source, GLenum type, GLuint id, GLenum severity );
//
// glDebugMessageInsert
//
void KHR_DEBUG_EMULATOR_DebugMessageInsert(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar * buf)
{
if (KHR_DEBUG_EMULATOR_isEnabled == 0) return;
// calls from the application are a bit more restricted in the types of errors they are allowed to generate:
if ((source != GL_DEBUG_SOURCE_APPLICATION) && (source != GL_DEBUG_SOURCE_THIRD_PARTY)) {
INSERT_API_ERROR( GL_INVALID_ENUM, "invalid enum in glDebugMessageInsert: source has to be GL_DEBUG_SOURCE_APPLICATION or GL_DEBUG_SOURCE_THIRD_PARTY" );
return;
}
KHR_DEBUG_EMULATOR_DebugMessageInsert_internal( source, type, id, severity, length, buf );
}
void KHR_DEBUG_EMULATOR_DebugMessageInsert_internal(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar * buf)
{
if (KHR_DEBUG_EMULATOR_isEnabled == 0) return;
if ( !isValidSeverity( severity ) ) {
INSERT_API_ERROR( GL_INVALID_ENUM , "invalid enum in glDebugMessageInsert: severity is invalid" );
return;
}
if ( !isValidType( type ) ) {
INSERT_API_ERROR( GL_INVALID_ENUM , "invalid enum in glDebugMessageInsert: type is invalid" );
return;
}
// length can be -1 which means that buf is 0 terminated.
// however, the messages created should always set length to the number of chars in the message (excluding the trailing 0)
if (length < 0) {
length = strlen( buf );
}
if (length > KHR_DEBUG_EMULATOR_MAX_DEBUG_MESSAGE_LENGTH) {
INSERT_API_ERROR( GL_INVALID_VALUE , "invalid value in glDebugMessageInsert: message is too long" );
return;
}
// there might be rules inserted by glDebugMessageControl to mute this message:
if ( !shouldMessageGetProcessed(source, type, id, severity) ) {
return;
}
if (KHR_DEBUG_EMULATOR_callback) {
KHR_DEBUG_EMULATOR_callback( source, type, id, severity, length, buf, KHR_DEBUG_EMULATOR_userParam );
} else {
g_LastDebugMessageEmpty = false;
g_LastDebugMessage.source = source;
g_LastDebugMessage.type = type;
g_LastDebugMessage.id = id;
g_LastDebugMessage.severity = severity;
g_LastDebugMessage.length = length;
g_LastDebugMessage.buf = buf;
}
}
//
// glDebugMessageCallback
//
void KHR_DEBUG_EMULATOR_DebugMessageCallback(GLDEBUGPROC callback, const void * userParam)
{
KHR_DEBUG_EMULATOR_callback = callback;
KHR_DEBUG_EMULATOR_userParam = userParam;
}
typedef struct DebugMessageControlRule
{
GLenum source;
GLenum type;
GLenum severity;
GLuint id;
GLboolean allIDs; // if set, ignore id
GLboolean enabled;
DebugMessageControlRule *previousRule;
DebugMessageControlRule *nextRule;
} DebugMessageControlRule;
// yes, I'm aware that this will never get cleaned up :-(
DebugMessageControlRule *g_FirstDebugMessageControlRule = NULL;
DebugMessageControlRule *g_LastDebugMessageControlRule = NULL;
GLboolean ruleApplies( DebugMessageControlRule *rule, GLenum source, GLenum type, GLuint id, GLenum severity )
{
if ( ( rule->allIDs != true ) && ( rule->id != id ) ) return false; // ID mismatch
if ( ( rule->source != GL_DONT_CARE ) && ( rule->source != source ) ) return false; // source mismatch
if ( ( rule->type != GL_DONT_CARE ) && ( rule->type != type ) ) return false; // type mismatch
if ( ( rule->severity != GL_DONT_CARE ) && ( rule->severity != severity ) ) return false; // severity mismatch
return true;
}
GLboolean shouldMessageGetProcessed( GLenum source, GLenum type, GLuint id, GLenum severity )
{
// check from the newest to the oldest rule,
// first one to be applyable to this message defines if it gets processed:
DebugMessageControlRule *ruleToCheck = g_LastDebugMessageControlRule;
while (ruleToCheck != NULL) {
if ( ruleApplies( ruleToCheck, source, type, id, severity) ) {
return ruleToCheck->enabled;
}
ruleToCheck = ruleToCheck->previousRule;
}
// no matching rule found, apply default behavior:
if (severity == GL_DEBUG_SEVERITY_LOW) {
return false;
}
return true;
}
void addRule( DebugMessageControlRule *newRule )
{
newRule->nextRule = NULL;
newRule->previousRule = NULL;
if (g_FirstDebugMessageControlRule == NULL) {
// first rule to insert:
g_FirstDebugMessageControlRule = newRule;
g_LastDebugMessageControlRule = newRule;
return;
}
g_LastDebugMessageControlRule->nextRule = newRule;
newRule->previousRule = g_LastDebugMessageControlRule;
g_LastDebugMessageControlRule = newRule;
}
//
// glDebugMessageControl TODO
//
void KHR_DEBUG_EMULATOR_DebugMessageControl(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint * ids, GLboolean enabled)
{
DebugMessageControlRule *rule = (DebugMessageControlRule*) malloc( sizeof(DebugMessageControlRule) );
rule->source = source;
rule->type = type;
rule->severity = severity;
rule->enabled = enabled;
if (count == 0) {
// ID-agnostic rule
rule->allIDs = true;
rule->id = 0;
addRule( rule );
} else {
// rules for specific IDs
rule->allIDs = false;
for (int i = 0; i < count; ++i) {
rule->id = ids[i];
addRule( rule );
}
}
}
//
// glGetDebugMessageLog
//
GLuint KHR_DEBUG_EMULATOR_GetDebugMessageLog(GLuint count, GLsizei bufsize, GLenum * sources, GLenum * types, GLuint * ids, GLenum * severities, GLsizei * lengths, GLchar * messageLog)
{
if (bufsize < 0 && messageLog != NULL) {
INSERT_API_ERROR( GL_INVALID_VALUE , "invalid value in glGetDebugMessageLog: bufsize < 0 and messageLog != NULL" );
return 0;
}
if (g_LastDebugMessageEmpty || count == 0) return 0;
if (types) types[0] = g_LastDebugMessage.type;
if (sources) sources[0] = g_LastDebugMessage.source;
if (ids) ids[0] = g_LastDebugMessage.id;
if (severities) severities[0] = g_LastDebugMessage.severity;
if (lengths) lengths[0] = g_LastDebugMessage.length;
// length is without the 0-termination
if (bufsize <= g_LastDebugMessage.length) {
// won't fit, don't return the error :-(
// 6.1.15 of KHR_debug
return 0;
}
strncpy( messageLog, g_LastDebugMessage.buf, bufsize );
messageLog[bufsize-1] = 0;
g_LastDebugMessageEmpty = true;
return 1;
}
void KHR_DEBUG_EMULATOR_GetObjectLabel(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei * length, GLchar * label)
{
KHR_DEBUG_EMULATOR_insertMissingFeatureError( "function not simulated yet - ignored" );
}
void KHR_DEBUG_EMULATOR_GetObjectPtrLabel(const void * ptr, GLsizei bufSize, GLsizei * length, GLchar * label)
{
KHR_DEBUG_EMULATOR_insertMissingFeatureError( "function not simulated yet - ignored" );
}
void KHR_DEBUG_EMULATOR_ObjectLabel(GLenum identifier, GLuint name, GLsizei length, const GLchar * label)
{
KHR_DEBUG_EMULATOR_insertMissingFeatureError( "function not simulated yet - ignored" );
}
void KHR_DEBUG_EMULATOR_ObjectPtrLabel(const void * ptr, GLsizei length, const GLchar * label)
{
KHR_DEBUG_EMULATOR_insertMissingFeatureError( "function not simulated yet - ignored" );
}
void KHR_DEBUG_EMULATOR_PopDebugGroup()
{
KHR_DEBUG_EMULATOR_insertMissingFeatureError( "function not simulated yet - ignored" );
}
void KHR_DEBUG_EMULATOR_PushDebugGroup(GLenum source, GLuint id, GLsizei length, const GLchar * message)
{
KHR_DEBUG_EMULATOR_insertMissingFeatureError( "function not simulated yet - ignored" );
}
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
///
/// Other helping functions:
///
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
GLboolean isValidSeverity( GLenum e )
{
if ((e == GL_DEBUG_SEVERITY_HIGH) || (e == GL_DEBUG_SEVERITY_MEDIUM) || (e == GL_DEBUG_SEVERITY_LOW) || (e == GL_DEBUG_SEVERITY_NOTIFICATION)) return true;
return false;
}
GLboolean isValidType( GLenum e )
{
if ( (e == GL_DEBUG_TYPE_ERROR) || (e == GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR) || (e == GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR)
|| (e == GL_DEBUG_TYPE_PERFORMANCE) || (e == GL_DEBUG_TYPE_PORTABILITY) || (e == GL_DEBUG_TYPE_OTHER)
|| (e == GL_DEBUG_TYPE_MARKER) || (e == GL_DEBUG_TYPE_PUSH_GROUP) || (e == GL_DEBUG_TYPE_POP_GROUP)) return true;
return false;
}
GLboolean isValidSource( GLenum e )
{
if ( (e == GL_DEBUG_SOURCE_API) || (e == GL_DEBUG_SOURCE_SHADER_COMPILER) || (e == GL_DEBUG_SOURCE_WINDOW_SYSTEM)
|| (e == GL_DEBUG_SOURCE_THIRD_PARTY) || (e == GL_DEBUG_SOURCE_APPLICATION) || (e == GL_DEBUG_SOURCE_OTHER)) return true;
return false;
}
const char* KHR_DEBUG_EMULATOR_GET_ERROR_STRING( GLenum _errorCode )
{
if (_errorCode == GL_INVALID_ENUM) { return (char*) "OpenGL error: GL_INVALID_ENUM"; }
else if (_errorCode == GL_INVALID_VALUE) { return (char*) "OpenGL error: GL_INVALID_VALUE"; }
else if (_errorCode == GL_INVALID_OPERATION) { return (char*) "OpenGL error: GL_INVALID_OPERATION"; }
else if (_errorCode == GL_INVALID_FRAMEBUFFER_OPERATION) { return (char*) "OpenGL error: GL_INVALID_FRAMEBUFFER_OPERATION"; }
else if (_errorCode == GL_OUT_OF_MEMORY) { return (char*) "OpenGL error: GL_OUT_OF_MEMORY"; }
else if (_errorCode == GL_NO_ERROR) { return (char*) "OpenGL error: GL_NO_ERROR"; }
else if (_errorCode == GL_STACK_UNDERFLOW) { return (char*) "OpenGL error: GL_STACK_UNDERFLOW"; }
else if (_errorCode == GL_STACK_OVERFLOW) { return (char*) "OpenGL error: GL_STACK_OVERFLOW"; }
else {
return (char*) "Unknown OpenGL error";
}
}
// internal error check that gets triggered after every GL call
// * check for errors, if there was one, trigger a debug message but store the error code to fake the original glGetError behavior
void KHR_DEBUG_EMULATOR_CHECK_GL_ERROR() {
//printf("check error\n");
GLenum currentError = _original_glGetError();
if ( currentError != GL_NO_ERROR ) {
KHR_DEBUG_EMULATOR_lastGLError = currentError;
KHR_DEBUG_EMULATOR_DebugMessageInsert_internal( GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_ERROR, currentError, GL_DEBUG_SEVERITY_HIGH, -1, KHR_DEBUG_EMULATOR_GET_ERROR_STRING( currentError ) );
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
///
/// Replacements for normal GL functions that have to behave a bit differently when KHR_debug
/// is present:
///
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//
// glGetError
// needs to return the last error even if this KHR_debug emulator already got the error code
//
GLenum KHR_DEBUG_EMULATOR_GetError() {
// if there was an error, report it. if not report the last global error
// which might got set by the automatic error checks
GLenum currentError = _original_glGetError();
if ( currentError == GL_NO_ERROR ) {
currentError = KHR_DEBUG_EMULATOR_lastGLError;
}
KHR_DEBUG_EMULATOR_lastGLError = GL_NO_ERROR;
return currentError;
}
//
// glDisable | glEnable | glIsEnabled
// need to recognize GL_DEBUG_OUTPUT & GL_DEBUG_OUTPUT_SYNCHRONOUS
//
void KHR_DEBUG_EMULATOR_Disable(GLenum cap){
if (cap == GL_DEBUG_OUTPUT) {
KHR_DEBUG_EMULATOR_isEnabled = 0;
return;
}
if (cap == GL_DEBUG_OUTPUT_SYNCHRONOUS) {
KHR_DEBUG_EMULATOR_isSynchronous = 0;
return;
}
_original_glDisable(cap);
KHR_DEBUG_EMULATOR_CHECK_GL_ERROR();
}
void KHR_DEBUG_EMULATOR_Enable(GLenum cap){
if (cap == GL_DEBUG_OUTPUT) {
KHR_DEBUG_EMULATOR_isEnabled = 1;
return;
}
if (cap == GL_DEBUG_OUTPUT_SYNCHRONOUS) {
KHR_DEBUG_EMULATOR_isSynchronous = 1;
return;
}
_original_glEnable(cap);
KHR_DEBUG_EMULATOR_CHECK_GL_ERROR();
}
GLboolean KHR_DEBUG_EMULATOR_IsEnabled(GLenum cap){
if (cap == GL_DEBUG_OUTPUT) {
return (KHR_DEBUG_EMULATOR_isEnabled == 1);
}
if (cap == GL_DEBUG_OUTPUT_SYNCHRONOUS) {
return (KHR_DEBUG_EMULATOR_isSynchronous == 1);
}
GLboolean returnValue = _original_glIsEnabled(cap);
KHR_DEBUG_EMULATOR_CHECK_GL_ERROR();
return returnValue;
}
//
// glGetIntegerv
// needs to recognize a few new tokens
//
void KHR_DEBUG_EMULATOR_GetIntegerv(GLenum pname, GLint * params){
if (pname == GL_CONTEXT_FLAGS) {
_original_glGetIntegerv(pname, params);
*params |= GL_CONTEXT_FLAG_DEBUG_BIT; // we make this a debug context ;-)
} else if (pname == GL_MAX_DEBUG_MESSAGE_LENGTH) {
*params = KHR_DEBUG_EMULATOR_MAX_DEBUG_MESSAGE_LENGTH;
} else if (pname == GL_MAX_DEBUG_LOGGED_MESSAGES) {
*params = KHR_DEBUG_EMULATOR_MAX_DEBUG_LOGGED_MESSAGES;
} else if (pname == GL_MAX_DEBUG_GROUP_STACK_DEPTH) {
*params = KHR_DEBUG_EMULATOR_MAX_DEBUG_GROUP_STACK_DEPTH;
} else if (pname == GL_MAX_LABEL_LENGTH) {
*params = KHR_DEBUG_EMULATOR_MAX_LABEL_LENGTH;
} else {
_original_glGetIntegerv(pname, params);
}
KHR_DEBUG_EMULATOR_CHECK_GL_ERROR();
}
//
// glGetPointerv
// needs to recognize GL_DEBUG_CALLBACK_FUNCTION & GL_DEBUG_CALLBACK_USER_PARAM
//
void KHR_DEBUG_EMULATOR_GetPointerv( GLenum pname, GLvoid ** params ){
if (pname == GL_DEBUG_CALLBACK_FUNCTION) {
*params = (GLvoid*) KHR_DEBUG_EMULATOR_callback;
} else if (pname == GL_DEBUG_CALLBACK_USER_PARAM) {
*params = (GLvoid*) KHR_DEBUG_EMULATOR_userParam;
} else {
_original_glGetPointerv( pname, params );
KHR_DEBUG_EMULATOR_CHECK_GL_ERROR();
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment