Search code examples
c#c++opengl-espinvoke

glBufferData keeps failing with GL_INVALID_ENUM even when it shouldn't


Can anyone see what is wrong with this code?

My call to glBufferData keeps failing and the error code is GL_INVALID_ENUM. But, my values are correct - I even tried hardcoding the values in directly (just in case). According to the docs this should work.

EDIT

I created a minimal - or as minimal as ANGLE + XAML + UWP can get - sample on GitHub. There are 5 main files where all the work happens:

If you download and run the code, it should throw an exception after the glBufferData on line 61

To toggle between a buffer-based array and the direct array, just change the line: SimpleRenderer.cs#L31

ORIGINAL

For the life of me it appears to match the C++ code - which runs fine. I am using ANGLE on Windows UWP. (I have the full code here if you need more context)

GLfloat[] vertexPositions = new[] {
     0.0f,  0.5f, 0.0f,
    -0.5f, -0.5f, 0.0f,
     0.5f, -0.5f, 0.0f
};

Gles.glGenBuffers(1, out GLuint mVertexPositionBuffer);
Gles.glBindBuffer(0x8892 /*Gles.GL_ARRAY_BUFFER*/, mVertexPositionBuffer);
var gcData = GCHandle.Alloc(vertexPositions, GCHandleType.Pinned);
try {
    var size = vertexPositions.Length * Marshal.SizeOf<GLfloat>();
    var data = gcData.AddrOfPinnedObject();
    Gles.glBufferData(0x8892 /*Gles.GL_ARRAY_BUFFER*/, size, data, 0x88E4 /*Gles.GL_STATIC_DRAW*/);
    // Gles.glGetError() == Gles.GL_INVALID_ENUM
} finally {
    gcData.Free();
}

My p/invoke layer Gles looks like this:

[DllImport(libGLESv2)]
public static extern void glGenBuffers(GLsizei n, out GLuint buffers);
[DllImport(libGLESv2)]
public static extern void glBindBuffer(GLenum target, GLuint buffer);
[DllImport(libGLESv2)]
public static extern void glBufferData(GLenum target, GLsizeiptr size, IntPtr data, GLenum usage);

This is what my C++ code looks like:

GLfloat vertexPositions[] = { ... };
glGenBuffers(1, &mVertexPositionBuffer);
glBindBuffer(GL_ARRAY_BUFFER, mVertexPositionBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions), vertexPositions, GL_STATIC_DRAW);

I believe the rest of my setup/drawing code is correct because this draws just fine:

GLfloat[] vertexPositions = new[] {
     0.0f,  0.5f, 0.0f,
    -0.5f, -0.5f, 0.0f,
     0.5f, -0.5f, 0.0f
};
var gcData = GCHandle.Alloc(vertexPositions, GCHandleType.Pinned);
try {
    Gles.glVertexAttribPointer((GLuint)mPositionAttribLocation, 3, Gles.GL_FLOAT, Gles.GL_FALSE, 0, gcData.AddrOfPinnedObject());
} finally {
    gcData.Free();
}
Gles.glEnableVertexAttribArray((GLuint)mPositionAttribLocation);
Gles.glDrawArrays(Gles.GL_TRIANGLES, 0, 3);

Solution

  • If you use a 32 bit architecture e.g. x86, then GLsizeiptr has to be a 32 bit datatype.

    To solve the issue the type of the alias GLsizeiptr has to be System.Int32:

    using GLsizeiptr = System.Int32;
    

    If the type would be System.Int64, then the last paramters of

    public static extern void glBufferData(
        GLenum target, GLsizeiptr size, IntPtr data, GLenum usage);
    

    would be misaligned.

    This causes the GL_INVALID_ENUM error in glBufferData, because the value of the last parameter (usage) is not GL_STREAM_DRAW, GL_STREAM_READ... .