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

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.
Supports Markdown
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