Debugging OpenGL Part II

In the previous tutorial we saw how hard and painful it is to work with glGetError. Luckily in OpenGL 4.3 a new feature was introduced called Debug Output that makes OpenGL error checking much more easier. Prior to version OpenGL 4.3 this feature was just an extension that can be found under the following names:

  •  AMD_debug_output (was first developed at AMD in 2010)
  •  ARB_debug_output (beside AMD other companies and vendors started to develop this feature)
  •   KHR_debug (I believe that KHR stands for Khronos)

OpenGL can notify us by generating a human readable message when various events occur in our program. This can be useful during application development, debugging and profiling. These messages will come from a variety of sources, but they all have in common following data (from Wikipedia):

  • The source that produced the message, by GLenum​.
  • The type of message, by GLenum​.
  • The message severity (how important it is), by GLenum​.
  • An ID, as a GLuint​.
  • A null-terminated string describing the message.

Now let’s see how we can integrate this in our architecture that we created in Chapter I. First we have to create an OpenGL debug context otherwise we might not receive any message. To do this open Init_GLUT.cpp and add the following line of code just after glutCreateWindow method:

void Init_GLUT::Init(const Core::WindowInfo& windowInfo,
                     const Core::ContextInfo& contextInfo,
                     const Core::FramebufferInfo& framebufferInfo)
{
....
glutCreateWindow(windowInfo.name.c_str());
std::cout << "GLUT:initialized" << std::endl;
glEnable(GL_DEBUG_OUTPUT);
....

Note!!! If this doesn’t work for you try calling glutInitContextFlags(GLUT_DEBUG); just before glutCreateWindow(windowInfo.name.c_str());

Now we need to create a callback method which is going to be called by OpenGL API when an event occurs. Because we also need to process some information based on the input from the callback, I decided to create a new class called DebugOutput in Init folder just to keep our code clean.

Your folder structure should look like this:

Debug Output class

Debug Output class

Let’s create the class and the callback:

#pragma once
#include <string>
#include <iostream>
#include "../../Dependencies/glew/glew.h"
#include "../../Dependencies/freeglut/freeglut.h"
#include <assert.h>

using namespace std;
namespace Core
{

 class DebugOutput
 {
  public:
    DebugOutput(){};
   ~DebugOutput(){};
    //if you want you can rename myCallback; This is just an example
   static void CALLBACK myCallback(GLenum source,
                                   GLenum type,
                                   GLuint id,
                                   GLenum severity,
                                   GLsizei length,
                                   const GLchar *msg,
                                   const void *data)
   {
      //display warnings/errors however you like
     cout << "\n**********Debug Output**************"                 << endl;
     cout << "source: "     << getStringForSource(source).c_str()     << endl;
     cout << "type: "       << getStringForType(type).c_str()         << endl;
     cout << "severity: "   << getStringForSeverity(severity).c_str() << endl;
     cout << "debug call: " << msg                                    << endl;
     cout << "\n************************" << endl;
   }

  private:
     //Parsing code from OpenGL Shader Language CookBook SE
     //https://github.com/daw42/glslcookbook
   static std::string getStringForType(GLenum type)
   {
     switch (type)
     {
        case GL_DEBUG_TYPE_ERROR:
              return"Error";
        case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
              return "Deprecated behavior";
        case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
              return "Undefined behavior";
        case GL_DEBUG_TYPE_PORTABILITY:
              return "Portability issue";
        case GL_DEBUG_TYPE_PERFORMANCE:
              return "Performance issue";
        case GL_DEBUG_TYPE_MARKER:
              return "Stream annotation";
        case GL_DEBUG_TYPE_OTHER_ARB:
              return "Other";
        default:
              assert(false);
              return "";
     }
   }

   static std::string getStringForSource(GLenum source)
   {
     switch (source)
     {
       case GL_DEBUG_SOURCE_API:
            return "API";
       case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
            return "Window system";
       case GL_DEBUG_SOURCE_SHADER_COMPILER:
            return "Shader compiler";
       case GL_DEBUG_SOURCE_THIRD_PARTY:
            return "Third party";
       case GL_DEBUG_SOURCE_APPLICATION:
            return "Application";
       case GL_DEBUG_SOURCE_OTHER:
            return "Other";
       default:
            assert(false);
            return "";
     }
   }

   static std::string getStringForSeverity(GLenum severity)
   {
     switch (severity)
     {
      case GL_DEBUG_SEVERITY_HIGH:
           return "High";
      case GL_DEBUG_SEVERITY_MEDIUM:
           return "Medium";
      case GL_DEBUG_SEVERITY_LOW:
           return "Low";
      case GL_DEBUG_SEVERITY_NOTIFICATION:
           return "Notification";
      default:
           assert(false);
           return("");
      }
   }

 };
}

We are not done yet because we didn’t register this callback. First let’s go in Init_GLUT.h and include our new DebugOutput.h.

...
#include "IListener.h"
#include "DebugOutput.h"
...

And now finally register the callback in Init method, just after we enable the debug context:

void Init_GLUT::Init(const Core::WindowInfo& windowInfo,
                     const Core::ContextInfo& contextInfo,
                     const Core::FramebufferInfo& framebufferInfo)
{
....
glutCreateWindow(windowInfo.name.c_str());
std::cout << "GLUT:initialized" << std::endl;
glEnable(GL_DEBUG_OUTPUT);

//these methods require some glew functionality, so we have to
//move Core::Init::Init_GLEW::Init(); just before calling thse methods
Core::Init::Init_GLEW::Init();
glDebugMessageCallback(DebugOutput::myCallback, NULL);
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE,
                      GL_DONT_CARE, 0, NULL, GL_TRUE);
....

The cool part that you can filter this messages with glDebugMessageControl method  based on your needs. You can read more about it here. In the code example above, I enable everything.

Another cool feature is that you can write your own debug messages, but for the moment we don’t really need that. Read more about it here.

On nVidia cards you might get this message from OpenGL:

source: API type: Other severity: Notification debug call:buffer detailed info: Buffer object <bound to GL_ARRAY_BUFFER_ARB usage hint is  GL_STATIC_DRAW> will use VIDEO memory as the source for buffer object operations.

This message is just a notification that a new buffer was created and ready to be used. You can filter this message using some ifs in the callback method or with  glDebugMessageControl if you don’t want to display this notification anymore.

Because I create 2 models, I will get two notifications:

Debug Output for two models on nVidia Card

Debug Output for two models on nVidia Card

Next we will see how to draw and rotate a cube in 3D :).

 

blog comments powered by Disqus