Search code examples
c#openglcrashglfwopentk

OpenTK OpenGL GL.DrawElements Crash


I'm using OpenTK's GLFW wrapper and OpenGL wrapper. I've run it with both x86 and x64 builds just in case. Installed nuget packages are OpenTK.Graphics and OpenTK.Windowing.GraphicsLibraryFramework and whatever dependencies Visual Studio installs with them.

When I run the code a window opens but is unresponsive. The window crashes a few seconds after opening. OpenGL outputs no debug information to the console nor does C#.

If I comment out GL.DrawElements the window doesn't crash and will properly clear to a black screen and respond to input, but nothing draws to the screen.

This issue persists whether I set the profile to OpenGlProfile.Core or OpenGlProfile.Compat.

GL.DrawArrays does work (in comapat only).

Code

using Engine2D;

namespace Sandbox;

public static class Program
{
    public static void Main()
    {
        var app = new Application();
        app.Run();
    }
}

using OpenTK.Windowing.GraphicsLibraryFramework;
using OpenTK.Graphics.OpenGL;

namespace Engine2D;

public unsafe sealed class Application
{
    private readonly Window* _window;

    private static readonly float[] _vertices =
    {
        -0.5f, -0.5f,
        -0.5f,  0.5f,
         0.5f,  0.5f,
         0.5f, -0.5f
    };

    private static readonly int[] _indices =
    {
        0, 1, 2,
        3, 0, 2
    };

    public Application()
    {
        GLFW.Init();

        GLFW.WindowHint(WindowHintOpenGlProfile.OpenGlProfile, OpenGlProfile.Core);
        GLFW.WindowHint(WindowHintInt.ContextVersionMajor, 4);
        GLFW.WindowHint(WindowHintInt.ContextVersionMinor, 3);
        _window = GLFW.CreateWindow(1200, 800, "Engine2D", null, null);
        GLFW.MakeContextCurrent(_window);

        GL.LoadBindings(new GLFWBindingsContext());

        GL.Enable(EnableCap.DebugOutput);
        GL.DebugMessageCallback((source, type, id, severity, length, message, userParam) =>
        {
            if (severity != DebugSeverity.DebugSeverityMedium || severity != DebugSeverity.DebugSeverityHigh)
            {
                return;
            }
            
            string msg = System.Text.Encoding.Default.GetString((byte*)message.ToPointer(), length);
            Console.WriteLine(msg);
        }, IntPtr.Zero);
    }

    public void Run()
    {
        var vao = GL.GenVertexArray();
        GL.BindVertexArray(vao);

        GL.EnableVertexAttribArray(0);
        GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, sizeof(float) * 2, 0);

        var vbo = GL.GenBuffer();
        GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
        GL.BufferData(BufferTarget.ArrayBuffer, sizeof(float) * _vertices.Length, _vertices, BufferUsageHint.StaticDraw);

        var ibo = GL.GenBuffer();
        GL.BindBuffer(BufferTarget.ElementArrayBuffer, ibo);
        GL.BufferData(BufferTarget.ElementArrayBuffer, sizeof(int) * _indices.Length, _indices, BufferUsageHint.StaticDraw);

        var vertShader = GL.CreateShader(ShaderType.VertexShader);
        GL.ShaderSource(vertShader, File.ReadAllText(@"C:\Dev\Engine2D\Sandbox\Standard.vert"));
        GL.CompileShader(vertShader);

        GL.GetShaderInfoLog(vertShader, out var info);
        if (!string.IsNullOrWhiteSpace(info))
        {
            Console.WriteLine(info);
            Console.Read();
        }

        var fragShader = GL.CreateShader(ShaderType.FragmentShader);
        GL.ShaderSource(fragShader, File.ReadAllText(@"C:\Dev\Engine2D\Sandbox\Standard.frag"));
        GL.CompileShader(fragShader);

        GL.GetShaderInfoLog(fragShader, out info);
        if (!string.IsNullOrWhiteSpace(info))
        {
            Console.WriteLine(info);
            Console.Read();
        }

        var program = GL.CreateProgram();
        GL.AttachShader(program, vertShader);
        GL.AttachShader(program, fragShader);
        GL.LinkProgram(program);
        GL.ValidateProgram(program);

        GL.GetProgramInfoLog(program, out info);
        if (!string.IsNullOrWhiteSpace(info))
        {
            Console.WriteLine(info);
            Console.Read();
        }

        GL.DeleteShader(vertShader);
        GL.DeleteShader(fragShader);

        while (!GLFW.WindowShouldClose(_window))
        {
            if (GLFW.GetKey(_window, Keys.Escape) == InputAction.Press)
            {
                GLFW.SetWindowShouldClose(_window, true);
            }

            GL.ClearColor(0f, 0f, 0f, 1f);
            GL.Clear(ClearBufferMask.ColorBufferBit);

            GL.BindVertexArray(vao);
            GL.UseProgram(program);

            GL.DrawElements(PrimitiveType.Triangles, _indices.Length, DrawElementsType.UnsignedInt, 0);

            GLFW.SwapBuffers(_window);
            GLFW.PollEvents();
        }

        GLFW.Terminate();

        GL.DeleteVertexArray(vao);
        GL.DeleteBuffer(vbo);
        GL.DeleteBuffer(ibo);
        GL.DeleteProgram(program);
    }
}

Shaders

#version 330 core

layout (location = 0) in vec2 aPos;

void main()
{
    gl_Position = vec4(aPos, 0.0, 1.0);
}

#version 330 core

out vec4 oColor;

void main()
{
    oColor = vec4(1.0, 1.0, 0.0, 1.0);
}

I've been stuck on this for 2 days now. Some help, even a lead, would be greatly appreciated.

Edit I needed to set the vertex attributes after generating the buffers. For some reason I thought the order didn't matter.


Solution

  • You invoked VertexAttribPointer before creating buffers.

    From the document,

    If pointer is not NULL, a non-zero named buffer object must be bound to the GL_ARRAY_BUFFER target

    Just change the order.

    GL.BindVertexArray(vao);
    
    var vbo = GL.GenBuffer();
    GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
    GL.BufferData(BufferTarget.ArrayBuffer, sizeof(float) * _vertices.Length, _vertices, BufferUsageHint.StaticDraw);
    
    var ibo = GL.GenBuffer();
    GL.BindBuffer(BufferTarget.ElementArrayBuffer, ibo);
    GL.BufferData(BufferTarget.ElementArrayBuffer, sizeof(int) * _indices.Length, _indices, BufferUsageHint.StaticDraw);
    
    GL.EnableVertexAttribArray(0);
    GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, sizeof(float) * 2, 0);