ThermalPixel's blog

home

Using GL_KHR_debug in OpenSceneGraph

06 Feb 2014

The OpenGL debug output extension is very useful to find errors and performance issues. However, OpenSceneGraph still uses glGetError for error reporting. In this post, I will show you how to enable the GL_KHR_debug extension with OpenSceneGraph without the use of external libraries. So, let's get started.

Setting up the Viewer

In order to setup an OpenGL extension, we need an active OpenGL context on the current thread. A way to do this with OpenSceneGraph is to create a subclass of osg::GraphicsOperation and register it as a realize operation on the viewer.

osg::Viewer viewer;
viewer.setRealizeOperation(new EnableGLDebugOperation());

The graphics operation is simple, it only calls extension setup routine.

class EnableGLDebugOperation : public osg::GraphicsOperation 
{
public:
    EnableGLDebugOperation()
        : osg::GraphicsOperation("EnableGLDebugOperation", false) {
    }
    virtual void operator ()(osg::GraphicsContext* gc) {
        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
        int context_id = gc->getState()->getContextID();
        enableGLDebugExtension(context_id);
    }
    OpenThreads::Mutex _mutex;
};

Setting up the Extension

We need to define the three entry points of the extensions as well as the OpenGL defines. I'll skip some of the ugly code here and leave the rest in the repository for the brave.

typedef void (GL_APIENTRY *GLDEBUGPROC)(GLenum, GLenum, GLuint, GLenum, GLsizei,
const GLchar *, const void *);
typedef void (GL_APIENTRY *GLDebugMessageControlPROC)(GLenum, GLenum, GLenum, GLsizei,
 const GLuint *, GLboolean);
typedef void (GL_APIENTRY *GLDebugMessageCallbackPROC)(GLDEBUGPROC , const void *) ;

Now, we can set up the extension. OpenSceneGraph provides with setGLExtensionFuncPtr a function that maps the address of the extension to our function instance:

void enableGLDebugExtension(int context_id)
{
    GLDebugMessageControlPROC glDebugMessageControl = nullptr;
    GLDebugMessageCallbackPROC glDebugMessageCallback = nullptr;
    //test the possible debug extensions
    if(osg::isGLExtensionSupported(context_id, "GL_KHR_debug")){
        osg::setGLExtensionFuncPtr(glDebugMessageCallback, "glDebugMessageCallback");
        osg::setGLExtensionFuncPtr(glDebugMessageControl, "glDebugMessageControl");
    }else if(osg::isGLExtensionSupported(context_id, "GL_ARB_debug_output")){
        osg::setGLExtensionFuncPtr(glDebugMessageCallback, "glDebugMessageCallbackARB");
        osg::setGLExtensionFuncPtr(glDebugMessageControl, "glDebugMessageControlARB");
    }else if(osg::isGLExtensionSupported(context_id, "GL_AMD_debug_output")){
        osg::setGLExtensionFuncPtr(glDebugMessageCallback, "glDebugMessageCallbackAMD");
        osg::setGLExtensionFuncPtr(glDebugMessageControl, "glDebugMessageControlAMD");
    }

    if(glDebugMessageCallback != nullptr && glDebugMessageControl != nullptr){
        glEnable(GL_DEBUG_OUTPUT);
        glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
        glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_MEDIUM, 0, NULL,
        GL_TRUE);
        glDebugMessageCallback(debugCallback, nullptr);
    }
}

Finally, one just need to define the callback debugCallback that will accept the messages. An example implementation, which logs the messages using osg::notify, can be found in the repository.

Some Notes

In order to guarantee a debug output based on the GL_KHR_debug extension specification, the CONTEXT_FLAG_DEBUG_BIT must be set during OpenGL context creation. However, the OpenSceneGraph API currently doesn't provide access to the context creation. Therefore, this approach is depending on how the graphics vendors implemented the debug output extension. For me, the debug output in an OpenSceneGraph application works fine on a NVIDIA graphics card. However, I don't receive any callbacks on an AMD card. I assume that the debug output is disabled when the CONTEXT_FLAG_DEBUG_BIT is not set.

Get the code here.

comments powered by Disqus