CXO2's picture

Make a Simple Error Checker

Well I will begin with introduction first ;)
My (nickname) is CXO2, lot People call me Chrono Cross (X referring to Cross lol)
I started this things since i was on 3rd junior high school and now i am 2nd high school.

Oh anyway this is my first blog tho, Now lets get started!

We are going to create a simple class to handle any OpenGL call (well actually any callback) with error checking.
So every time we call OpenGL, we don't need to check error manually. let's say the class is "Renderer" with "Call()" and "CheckError()" function.

We will create error checking function first.
Here the code:

        /// <summary>
        /// Check for the OpenGL Error
        /// </summary>
        public static void CheckError()
        {
            ErrorCode errorCode = GL.GetError();
 
            if (errorCode == ErrorCode.NoError)
                return;
 
            LastError = errorCode;
            string error = "Unknown Error";
            string description = "No Description";
 
            // Decode the error code
            switch (errorCode)
            {
                case ErrorCode.InvalidEnum:
                    {
                        error = "GL_INVALID_ENUM";
                        description = "An unacceptable value has been specified for an enumerated argument";
                        break;
                    }
 
                case ErrorCode.InvalidValue:
                    {
                        error = "GL_INVALID_VALUE";
                        description = "A numeric argument is out of range";
                        break;
                    }
 
                case ErrorCode.InvalidOperation:
                    {
                        error = "GL_INVALID_OPERATION";
                        description = "The specified operation is not allowed in the current state";
                        break;
                    }
 
                case ErrorCode.StackOverflow:
                    {
                        error = "GL_STACK_OVERFLOW";
                        description = "This command would cause a stack overflow";
                        break;
                    }
 
                case ErrorCode.StackUnderflow:
                    {
                        error = "GL_STACK_UNDERFLOW";
                        description = "This command would cause a stack underflow";
                        break;
                    }
 
                case ErrorCode.OutOfMemory:
                    {
                        error = "GL_OUT_OF_MEMORY";
                        description = "there is not enough memory left to execute the command";
                        break;
                    }
 
                case ErrorCode.InvalidFramebufferOperationExt:
                    {
                        error = "GL_INVALID_FRAMEBUFFER_OPERATION_EXT";
                        description = "The object bound to FRAMEBUFFER_BINDING_EXT is not \"framebuffer complete\"";
                        break;
                    }
                default:
                    {
                        error = errorCode.ToString();
                        break;
                    }
            }
 
            // Log the error
            Console.WriteLine("An internal OpenGL call failed: " + error + " (" + description + ")", "Fatal Error");
        }

(My Opinion) this function is really neat.
the checking process include the basic detailed information on description (Change any description if you think i am wrong lol).

Now, we are going to create Call() function.
This will handle OpenGL call (again, actually it could be any call that passed on this parameter function)

/// <summary>
/// Call OpenGL function and check for the error
/// </summary>
/// <param name="callback">OpenGL Function to be called</param>
public static void Call(Action callback)
{
    callback();
    CheckError();
}
 
/// <summary>
/// OpenGL function and check for the error
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="func">OpenGL Function to be called</param>
/// <param name="parameter">Parameters of OpenGL Function</param>
public static void Call<T>(Action<T> func, T parameter)
{
    func(parameter);
    CheckError();
}

As you can see, simply it execute the Callback and Check for the error.
The explanation about the usage will covered in further reading.

You may experiencing compatibility problem which Action and Func<> is not available because you are using .NET 2.0.
I will explain solution about it now ;)

Compatibility Issue
As you may know the Action and Func<> Delegate is only available on .NET 3.0 (or .NET 3.5?)
To workaround against this limitation on .NET 2.0, simply create the a delegate named Action and Func

I can guarantee that the delegate will act as same as Action and Func delegate on .NET 3.0.

here the code:

using System;
using System.Collections.Generic;
using System.Text;
 
namespace Tutorial
{
    public delegate void Action();
    public delegate void Action<T1, T2>(T1 arg1, T2 arg2);
    public delegate void Action<T1, T2, T3>(T1 arg1, T2 arg2, T3 arg3);
    public delegate void Action<T1, T2, T3, T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
    public delegate TResult Func<TResult>();
    public delegate TResult Func<T, TResult>(T arg);
    public delegate TResult Func<T1, T2, TResult>(T1 arg1, T2 arg2);
    public delegate TResult Func<T1, T2, T3, TResult>(T1 arg1, T2 arg2, T3 arg3);
    public delegate TResult Func<T1, T2, T3, T4, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
}

And that's it!
We are finished with our custom error checking! This saving a lot time in most cases, very useful for debugging process.

Here the full code:

using System;
using System.Collections.Generic;
using System.Text;
 
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
 
namespace Tutorial
{
    // Include this if you are at .NET 2.0
    public delegate void Action();
    public delegate void Action<T1, T2>(T1 arg1, T2 arg2);
    public delegate void Action<T1, T2, T3>(T1 arg1, T2 arg2, T3 arg3);
    public delegate void Action<T1, T2, T3, T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
    public delegate TResult Func<TResult>();
    public delegate TResult Func<T, TResult>(T arg);
    public delegate TResult Func<T1, T2, TResult>(T1 arg1, T2 arg2);
    public delegate TResult Func<T1, T2, T3, TResult>(T1 arg1, T2 arg2, T3 arg3);
    public delegate TResult Func<T1, T2, T3, T4, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
 
    // Well you can name anything as you like Lol.
    public static class Renderer
    {
        /// <summary>
        /// Lastest Error Code that Occurred.
        /// </summary>
        public static ErrorCode LastError { get; private set; }
 
        /// <summary>
        /// Call OpenGL function and check for the error
        /// </summary>
        /// <param name="callback">OpenGL Function to be called</param>
        public static void Call(Action callback)
        {
            callback();
            CheckError();
        }
 
        /// <summary>
        /// OpenGL function and check for the error
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="func">OpenGL Function to be called</param>
        /// <param name="parameter">Parameters of OpenGL Function</param>
        public static void Call<T>(Action<T> func, T parameter)
        {
            func(parameter);
            CheckError();
        }
 
        /// <summary>
        /// Check for the OpenGL Error
        /// </summary>
        public static void CheckError()
        {
            ErrorCode errorCode = GL.GetError();
 
            if (errorCode == ErrorCode.NoError)
                return;
 
            LastError = errorCode;
            string error = "Unknown Error";
            string description = "No Description";
 
            // Decode the error code
            switch (errorCode)
            {
                case ErrorCode.InvalidEnum:
                    {
                        error = "GL_INVALID_ENUM";
                        description = "An unacceptable value has been specified for an enumerated argument";
                        break;
                    }
 
                case ErrorCode.InvalidValue:
                    {
                        error = "GL_INVALID_VALUE";
                        description = "A numeric argument is out of range";
                        break;
                    }
 
                case ErrorCode.InvalidOperation:
                    {
                        error = "GL_INVALID_OPERATION";
                        description = "The specified operation is not allowed in the current state";
                        break;
                    }
 
                case ErrorCode.StackOverflow:
                    {
                        error = "GL_STACK_OVERFLOW";
                        description = "This command would cause a stack overflow";
                        break;
                    }
 
                case ErrorCode.StackUnderflow:
                    {
                        error = "GL_STACK_UNDERFLOW";
                        description = "This command would cause a stack underflow";
                        break;
                    }
 
                case ErrorCode.OutOfMemory:
                    {
                        error = "GL_OUT_OF_MEMORY";
                        description = "there is not enough memory left to execute the command";
                        break;
                    }
 
                case ErrorCode.InvalidFramebufferOperationExt:
                    {
                        error = "GL_INVALID_FRAMEBUFFER_OPERATION_EXT";
                        description = "The object bound to FRAMEBUFFER_BINDING_EXT is not \"framebuffer complete\"";
                        break;
                    }
                default:
                    {
                        error = errorCode.ToString();
                        break;
                    }
            }
 
            // Log the error
            Console.WriteLine("An internal OpenGL call failed: " + error + " (" + description + ")", "Fatal Error");
        }
    }
}

Okay, so here is it, the usage is quiet simple:

// Using lambda expression:
Renderer.Call(() => GL.xxxx(parameter));
 
// Example:
Renderer.Call(() => GL.PushMatrix());
 
// or with parameter
Renderer.Call(() => GL.Viewport(0, 0, Width, Height));
 
// you could also call it like this
// notice there are no () at the end
Renderer.Call(GL.PushMatrix);
 
// or like this
Renderer.Call(GL.ClearColor, Color.White);

That few things that you need to note:

  • Do not use this on Immediate Mode rendering code (GL.Begin() and GL.End() block)

    // Regarding to OpenGL API Documentation:
    // glGetError() is cannot called between glBegin() and glEnd() block. Otherwise, GL_INVALID_OPERATION will occur.
     
    Renderer.Call(() => GL.Begin(BeginMode.Quads)); // ERROR
     
    // This is wrong too
    GL.Begin(BeginMode.Quads);
       for (int i = 0; i < Vertices.Count; i++)
       {
              GL.TexCoord2(Vertices[i].TexCoord.X, Vertices[i].TexCoord.Y);
              Renderer.Call(() => GL.Vertex2(Vertices[i].Position.X, Vertices[i].Position.Y)); // ERROR! Calling any function with Renderer.Call(() will trigger GL_INVALID_OPERATION
              GL.Color4(Vertices[i].Color);
       }
    GL.End();
     
    // The another wrong example
    GL.Begin(BeginMode.Quads);
       for (int i = 0; i < Vertices.Count; i++)
       {
              GL.TexCoord2(Vertices[i].TexCoord.X, Vertices[i].TexCoord.Y);
              GL.Vertex2(Vertices[i].Position.X, Vertices[i].Position.Y));
              GL.Color4(Vertices[i].Color);
       }
    Renderer.Call(() => GL.End()); // ERROR! No GL.GetError() until there new call after GL.End(); otherwise GL_INVALID_OPERATION will occur.
     
    // Correct way
    GL.Begin(BeginMode.Quads);
       for (int i = 0; i < Vertices.Count; i++)
       {
              GL.TexCoord2(Vertices[i].TexCoord.X, Vertices[i].TexCoord.Y);
              GL.Vertex2(Vertices[i].Position.X, Vertices[i].Position.Y));
              GL.Color4(Vertices[i].Color);
       }
    GL.End(); // No Renderer.Call() -> No GL.GetError -> No Error

    There is no workaround against limitation, unless moving to Vertex Buffer Object.
    Any developer is encouraged to move the drawing implementation into Vertex Buffer Object.

    There's too much performance trap on Immediate Mode.

  • This method won't work with ref or out
    so you couldn't use it like Renderer.Call(() => GL.LoadMatrix(ref matrix)); // Compiler wont compile the code, can't use ref on lambda function

    To workaround against this limitation, simply call Renderer.CheckError(); manually.
    For example:

    // Renderer.Cal() actually is shortcut of this execution
    GL.LoadMatrix(ref matrix);
    Renderer.CheckError();
  • You can get the latest Error code by calling LastError

    Renderer.Call(() => GL.Flush()); // for example, this method is throwing an ErrorCode
    ErrorCode lastError = Renderer.LastError;
     
    // do something with lastError
  • You could use StackFrame to extract more error details.
    here some example usage (that you can replace Console.WriteLine() at CheckError() function):

    StackTrace stackTrace = new StackTrace(true);
    StackFrame sf = stackTrace.GetFrame(3);
    Console.WriteLine("[" + Path.GetFileName(sf.GetFileName()) + "(" + sf.GetMethod().Name + ":" + sf.GetFileLineNumber() + ")] " + "An internal OpenGL call failed: " + error + " (" + description + ")", "Fatal Error");

    With this, the error message will show you where the error came (filename and line of code).
    The error line somehow got wrong, for example it show the error come from Example.cs line: 110, but actually the error come from 109 (you need to subtract line number with 1 (so 110 - 1 = 109), if there any extra 1 new line, you need subtract 2 the line number and so on);

    The reason StrackFrame is 3 because its like a Frame Function
    if the value is 0, the GetMethod().Name will return "Renderer.CheckError();"
    if the value is 1, the GetMethod().Name will return "Renderer.Call();"
    if the value is 2, the GetMethod().Name will return the method name that the source of error << this is what we want.
    if the value is 3, the GetMethod().Name will return GL function that we call << its too deep bro.. too deep lol

    Note that the *.pdb file on your program working directory to make StackTrace working, otherwise, the filename and the line number won't show up.

  • Instead writing error message to the console, you may interested to write it into output window.
    Simply you can replace Console.WriteLine with Debug.WriteLine or Trace.WriteLine depending Configuration that you are using right now.

    StackTrace stackTrace = new StackTrace(true);
    StackFrame sf = stackTrace.GetFrame(3);
    string msg = "[" + Path.GetFileName(sf.GetFileName()) + "(" + sf.GetMethod().Name + ":" + sf.GetFileLineNumber() + ")] " + "An internal OpenGL call failed: " + error + " (" + description + ")";
     
    #if DEBUG
                Debug.WriteLine(msg, "Fatal Error"); 
    #else
                Trace.WriteLine(msg, "Fatal Error");
    #endif
  • You may throw the exception instead rather than Writing the error into Console / Output Window.
    But this will reduce the performance a lot (especially if the exception thrown so frequently, like on drawing error, the exception thrown every Render loop).

    adding variable IsError maybe the best solution in this case (you need sprite class or similar class like that).
    IsError will became true when exception being thrown, and the render will skip the drawing part if the IsError is true (unless you make a change on your sprite).
    example:

    // Somewhere in sprite class
    public bool IsError { get; private set; }
    public void Draw()
    {
        if (IsError)
            return;
     
        try {
        // your rendering code here
        catch (Exception ex)
        {
            Console.WriteLine("Error: " + ex.Message);
            IsError = true;
        }
    }

Note that this have negative effect, calling GL.GetError() frequently may cause framerate hiccups .
Here some few possibilities improvement (Thanks to Fiddler!):

the Fiddler wrote:
  • According to the GL.GetError documentation, "glGetError should always be called in a loop, until it returns GL_NO_ERROR".
  • GL.GetError may cause a pipeline stall, which reduces performance. This is fine for debug builds, but it might be a good idea to disable error checking in release builds.
  • Lambdas () => ... allocate memory and will cause the GC to run from time to time, which might result in framerate hiccups. This goes back to #2 above (disable error checking for release builds.)

You can use #if DEBUG at Call() function to disable error checking in release build.
Alternatively, you can use The Fiddler method:

the Fiddler wrote:

OpenTK actually comes with a similar feature built-in. This is not very well known, but debug versions of OpenTK.dll automatically insert GL.GetError calls and raise exceptions whenever they discover an error.

What I usually do in my projects is modify the .csproj file to point to a debug dll when building in debug mode and a release dll when building in release. This gives me rigorous error checking without adding explicit checks to my code - which is nice, because I am rather lazy.

My .csproj looks like this:

    <Reference Include="OpenTK" Condition="$(Configuration) == 'Debug'">
      <HintPath>lib\Debug\OpenTK.dll</HintPath>
    </Reference>
    <Reference Include="OpenTK" Condition="$(Configuration) != 'Debug'">
      <HintPath>lib\Release\OpenTK.dll</HintPath>
    </Reference>

That's it for now.
I hope this piece of code will help you debugging your error problem (and make your headache feel better lololol)
if I had enough time, I will write post about Simple Shader Implementation so stay tuned! ;)
Thanks to the Fiddler for the tips (he always save my day!)

Good Luck! :)


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
the Fiddler's picture

This is a nice trick to make debugging easier.

A couple of possible improvements:

  1. According to the GL.GetError documentation, "glGetError should always be called in a loop, until it returns GL_NO_ERROR".
  2. GL.GetError may cause a pipeline stall, which reduces performance. This is fine for debug builds, but it might be a good idea to disable error checking in release builds.
  3. Lambdas () => ... allocate memory and will cause the GC to run from time to time, which might result in framerate hiccups. This goes back to #2 above (disable error checking for release builds.)

OpenTK actually comes with a similar feature built-in. This is not very well known, but debug versions of OpenTK.dll automatically insert GL.GetError calls and raise exceptions whenever they discover an error.

What I usually do in my projects is modify the .csproj file to point to a debug dll when building in debug mode and a release dll when building in release. This gives me rigorous error checking without adding explicit checks to my code - which is nice, because I am rather lazy.

My .csproj looks like this:

    <Reference Include="OpenTK" Condition="$(Configuration) == 'Debug'">
      <HintPath>lib\Debug\OpenTK.dll</HintPath>
    </Reference>
    <Reference Include="OpenTK" Condition="$(Configuration) != 'Debug'">
      <HintPath>lib\Release\OpenTK.dll</HintPath>
    </Reference>
CXO2's picture

Well I didn't get any framerate problem when using this method, calling GL.GetError(); isn't reducing the framerate (on my computer).
but maybe this will be a different story on another pc with lower spec than my pc.

Thank you for the precious information!
I updated my post.

the Fiddler's picture

It's true, performance depends a lot on your system specs and what the program is doing. The impact would be highest on systems with a fast GPU and a slow CPU.

Looking forward to your shader tutorial, we are really missing good tutorials for OpenTK!