Monday, January 30, 2017

OpenGL 4 with OpenTK in C# Part 4: Refactoring and adding error handling


In this post we will look at cleaning up the game window code a little and introduce Shader compile error logging as well as Program linking error logging.

This is part 4 of my series on OpenGL4 with OpenTK.
For other posts in this series:
OpenGL 4 with OpenTK in C# Part 1: Initialize the GameWindow
OpenGL 4 with OpenTK in C# Part 2: Compiling shaders and linking them
OpenGL 4 with OpenTK in C# Part 3: Passing data to shaders
OpenGL 4 with OpenTK in C# Part 4: Refactoring and adding error handling
OpenGL 4 with OpenTK in C# Part 5: Buffers and Triangle
OpenGL 4 with OpenTK in C# Part 6: Rotations and Movement of objects
OpenGL 4 with OpenTK in C# Part 7: Vectors and Matrices
OpenGL 4 with OpenTK in C# Part 8: Drawing multiple objects
OpenGL 4 with OpenTK in C# Part 9: Texturing
OpenGL 4 with OpenTK in C# Part 10: Asteroid Invaders
Basic bullet movement patterns in Asteroid Invaders
OpenGL 4 with OpenTK in C# Part 11: Mipmap
OpenGL 4 with OpenTK in C# Part 12: Basic Moveable Camera
OpenGL 4 with OpenTK in C# Part 13: IcoSphere
OpenGL 4 with OpenTK in C# Part 14: Basic Text
OpenGL 4 with OpenTK in C# Part 15: Object picking by mouse

As stated in the previous post, I am in no way an expert in OpenGL. I write these posts as a way to learn and if someone else finds these posts useful then all the better :)
The code in this post is from me trying to figure out why my shaders do no work.
This part will build upon the game window and shaders from the previous post.

Refactoring the code

So lets clean up the code a little. First off lets break out each shader compile to a separate method from our CompileShaders method.

private int CompileShader(ShaderType type, string path)
{
    var shader = GL.CreateShader(type);
    var src = File.ReadAllText(path);
    GL.ShaderSource(shader, src);
    GL.CompileShader(shader);
    return shader;
}
This leaves us with the following CreateProgram method, previously known as CompileShaders. Here we store the shaders in a list that we can iterate over instead of duplicating the code for each shader
private int CreateProgram()
{
    var program = GL.CreateProgram();
    var shaders = new List<int>();
    shaders.Add(CompileShader(ShaderType.VertexShader, @"Components\Shaders\1Vert\vertexShaderTriangle.c"));
    shaders.Add(CompileShader(ShaderType.FragmentShader, @"Components\Shaders\5Frag\fragmentShader.c"));
            
    foreach(var shader in shaders)
        GL.AttachShader(program, shader);
    GL.LinkProgram(program);
    foreach (var shader in shaders)
    {
        GL.DetachShader(program, shader);
        GL.DeleteShader(shader);
    }
    return program;
}

Adding shader compile output and program linking output

Edit 2017-02-05: Evidently some drivers write info log even when things are OK. So removed the try catch and changed to debug logging instead of throwing exceptions.

Next step is to add some kind of feedback from OpenGL when we try to compile a shader or link a program that has errors in it.
First off the in the CompileShader method. Here we check with OpenGL if there was anything written to the info log during the compilation of this specific shader with the help of the GL.GetShaderInfoLog. If anything was written, we write it to Debug log.
private int CompileShader(ShaderType type, string path)
{
    var shader = GL.CreateShader(type);
    var src = File.ReadAllText(path);
    GL.ShaderSource(shader, src);
    GL.CompileShader(shader);
    var info = GL.GetShaderInfoLog(shader);
    if (!string.IsNullOrWhiteSpace(info))
        Debug.WriteLine($"GL.CompileShader [{type}] had info log: {info}");
    return shader;
}

In the CreateProgram method we also add a check to see if there was anything written to the info log regarding this specific program with the help of the GL.GetProgramInfoLog method. And same as above, if anything was we write it to Debug log.

private int CreateProgram()
{
    var program = GL.CreateProgram();
    var shaders = new List<int>();
    shaders.Add(CompileShader(ShaderType.VertexShader, @"Components\Shaders\1Vert\simplePipeVert.c"));
    shaders.Add(CompileShader(ShaderType.FragmentShader, @"Components\Shaders\5Frag\simplePipeFrag.c"));

    foreach (var shader in shaders)
        GL.AttachShader(program, shader);
    GL.LinkProgram(program);
    var info = GL.GetProgramInfoLog(program);
    if (!string.IsNullOrWhiteSpace(info))
        Debug.WriteLine($"GL.LinkProgram had info log: {info}");

    foreach (var shader in shaders)
    {
        GL.DetachShader(program, shader);
        GL.DeleteShader(shader);
    }
    return program;
}


Example output in our visual studio debug output view

I've started to compile a list of stuff that I've encountered and how I solved those issues.

For the full source at the end of part 4, go to: https://github.com/eowind/dreamstatecoding

Hope this helps someone out there :)
Thanks for reading. Here's another GIF of 2 of our cats fighting to lighten up your day.

Until next time: Work to Live, Don’t Live to Work

9 comments:

  1. Still no luck. A blank blue window but no errors reported.

    ReplyDelete
    Replies
    1. Hi there!
      Thank you for your comment, I've created a repository over at GitHub for this site for hosting source code for the articles. So please head over there.
      https://github.com/eowind/dreamstatecoding
      If you find something crucial that I've missed to put into the article, please let me know :)

      Delete
  2. i always get a System.AccessViolationException in OpenTK.dll while creating the first buffer.
    any ideas how to fix that?

    ReplyDelete
    Replies
    1. Hi there. Have you tried the version that is on github? https://github.com/eowind/dreamstatecoding

      Delete
  3. Hi. You skipped how you created the "simplePipeVert.c" and "simplePipeFrag.c" files. Also those are stored in subfolders. Just wanted to point that out because it may confuse people that are just copying and pasting.

    Thanks so much for the tutorial. Really cool stuff. Great job!

    ReplyDelete
    Replies
    1. It's always good to make copy/pasters think a little 😜 Glad you liked it and thanks for commenting.

      Delete
  4. Hi, great tut, buut what is IsNullOrWhiteSpace? it just gives me an error

    ReplyDelete
    Replies
    1. https://docs.microsoft.com/en-us/dotnet/api/system.string.isnullorwhitespace?view=netcore-3.1

      Delete