VBO problems: nothing gets drawn

Hey. I'm having some issues using VBOs. This is the code I have right now:

       public void Draw(Point position)
        {
            int[] vertexData = {
                                                     position.X, position.Y,
                                                     position.X + Image.Width, position.Y,
                                                     position.X, position.Y + Image.Height,
                                                     position.X + Image.Width, position.Y + Image.Height
                                               };

            uint glVertexBuffer;

            GL.GenBuffers(1, out glVertexBuffer);
            GL.BindBuffer(BufferTarget.ArrayBuffer, glVertexBuffer);
            GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vertexData.Length * sizeof(int)), vertexData, BufferUsageHint.StreamDraw);

            int size; // This is 32, like it should be so I assumed the buffer got uploaded correctly.
            GL.GetBufferParameter(BufferTarget.ArrayBuffer, BufferParameterName.BufferMapped, out size);

            GL.EnableClientState(EnableCap.VertexArray);

            GL.BindBuffer(BufferTarget.ArrayBuffer, glVertexBuffer);
            GL.VertexPointer(2, VertexPointerType.Int, 0, 0);

            //GL.BindTexture(TextureTarget.Texture2D, glTexture);
            GL.Disable(EnableCap.Texture2D);

            GL.Color3(1.0f, 1.0f, 1.0f);

            GL.BindBuffer(BufferTarget.ArrayBuffer, glVertexBuffer);
            GL.DrawArrays(BeginMode.TriangleStrip, 0, vertexData.Length);

            GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
            GL.DisableClientState(EnableCap.VertexArray);

            GL.DeleteBuffers(1, ref glVertexBuffer);

            /*GL.Begin(BeginMode.TriangleStrip);
            GL.TexCoord2(0, 0);
            GL.Vertex2(position.X, position.Y);

            GL.TexCoord2(1, 0);
            GL.Vertex2(position.X + Image.Width, position.Y);

            GL.TexCoord2(0, 1);
            GL.Vertex2(position.X, position.Y + Image.Height);
           
            GL.TexCoord2(1, 1);
            GL.Vertex2(position.X + Image.Width, position.Y + Image.Height);
            GL.End();*/

        }

I call SwapBuffer later on in another function. The commented out immediate mode section is what I want to replace, and that works fine, but using VBOs, nothing get's drawn. I've checked using GL.GetError after every function call and no errors are reported. Anyone got a clue on what I'm doing wrong?


Comments

One thing that strikes me as odd is that you construct/destroy the VBO in the "Draw" function. The "usage pattern" for VBOs is:

1. Construct a VBO eg. when loading a level of the game, known by an 'uint name'
2. Use 'name' when rendering each frame to draw the VBO
3. When you don't need the VBO anymore, destroy it (eg. when loading another level)

This pattern is very similar to texture objects actually.

Are you able to draw using basic GL.Begin()-primitives?

Yes, [b]like I said in my original post[/b], the immediate mode sections works. I was creating the VBO on the fly just for testing.

Anyway, I fixed the problem, turns out I needed to cast the last parameter of GL.VertexPointer to IntPtr.

@yuriks
Oh, sorry didn't see the end of your first post.

@Fiddler
Maybe this could be fixed? I mean accepting 0 as a valid IntPtr.Zero substitute?

Re: VBO problems: nothing gets drawn
posted by the Fiddler

I'm afraid no, as this would mean adding an int overload to every IntPtr parameter.

We'll just have to use IntPtr.Zero or null as the compiler writers intended.

@yuriks, Fiddler

But why did yurik NOT get a compiler warning/error then..? I just assumed he did...

Yeah, didn't get any errors or warnings. I assume that 0 (Int32) is being treated as an object, instead of a null pointer.

Re: VBO problems: nothing gets drawn
posted by the Fiddler

It's a bit ugly, but I can't think of any way to work around that, aside from adding inline documentation.

I'm still not clear on this; is 0 automagically converted to some kind of IntPtr? If so, to which IntPtr?

0 isn't converted to an IntPtr, it's taken as an object of type Int32, that's why I explicitly need to cast it.

I cannot see the cast in your example:

           GL.VertexPointer(2, VertexPointerType.Int, 0, 0);

Re: VBO problems: nothing gets drawn
posted by the Fiddler

As he said, he fixed the problem in his code (not the sample posted here) :-)

So why is there any problem with the VertexPointer method? I cannot see a problem if there is an error message from the compiler .. just add an IntelliSense comment "Use IntPtr.Zero instead of 0"

Re: VBO problems: nothing gets drawn
posted by the Fiddler

There is no error message: "0 isn't converted to an IntPtr, it's taken as an object of type Int32, that's why I explicitly need to cast it. When you pass 0, the object overload is used (which passes a reference to the local variable that contains 0, instead of 0 itself).

In other words: the compiler does what it's told, which is not what you expect.

I've spoken with the guy behind PyOpenGL, who explained how they build the PyOpenGL docs. I hope to add these docs to intellisense, along with custom annotations, which will hopefully help avoid such issues in the future.

When you pass 0, the object overload is used (which passes a reference to the local variable that contains 0, instead of 0 itself).

I guess the natural question then is: why do we need an object overload for VertexPointer?

Re: VBO problems: nothing gets drawn
posted by the Fiddler

Let me twist your question around: do you know, off-hand, how to obtain a void* to a managed object? :-)

That's the main reason. The other is backwards compatibility between Tao 2.0 and Tao 1.3.

Another question that gets asked from time to time is why not use a generic method. The main reason is that DllImports can't be generics and, besides, generics would needlessly increase memory requirements and complicate pinning.

Let me twist your question around: do you know, off-hand, how to obtain a void* to a managed object? :-)

Nope :)

But then again: the IntPtr in VertexPointer is supposed to be targeted at an unmanaged resource, right? Pointing it towards a managed object wouldn't be correct.

Re: VBO problems: nothing gets drawn
posted by the Fiddler

But then again: the IntPtr in VertexPointer is supposed to be targeted at an unmanaged resource, right? Pointing it towards a managed object wouldn't be correct.
Not necesserily. As long as the managed resource has the correct format and you know it won't move, you are perfectly free to pass a pointer to that.

Of course, this only holds true for functions that don't retain the resource after the function returns. Fortunately for us, almost everything in OpenGL works like this - and everything that doesn't, has been deprecated in GL3.0. This means, you are free to create VBOs, textures, shaders from managed objects (say, an array of floats or Vector3 structs).

The only thing you need to avoid are legacy, client-side vertex arrays (VAs).

But isn't VertexPointer the API to specify data for a VA?

And therefore the object-overload should be unnecessary?

Why not put in an overload for VertexPointer for the specific case of a managed float-array? Having a parameter of type "object" is less-than-elegant in C#, and as we have seen in this thread confusing..

Re: VBO problems: nothing gets drawn
posted by the Fiddler

The answer is simple actually: the last parameter is not constrained to float arrays.

Why don't you check yourself, if you don't believe me? :-)

Well yeah, it is a void* in c. So what? :)

If we can make it better, why should we not? OpenTK is not a 1-1 mapping of the OpenGL API.

Re: VBO problems: nothing gets drawn
posted by the Fiddler

Actually, this has been tried before: Tao 1.3 universally replaced void* with array types (float[], float[,], float[,,], byte[], byte[,], ...), IIRC ~20 overloads for each void* (bloated), when only five or six were valid at any time (incorrect). And what if you actually needed a float[][]? (inflexible)

Sometimes the simplest solution is the best one.

What we could do is detect 0 and replace it with IntPtr.Zero (adding overhead). Or we could document that you should use IntPtr.Zero instead of 0.

Right now, I'm leaning towards the latter - but I'm always open to ideas.

Just to clarify this, the following ideas are impossible:

  1. We cannot detect the correct overloads automatically (no such info in the specs).
  2. We cannot add the correct overloads by hand (too many, too error prone).

I can see the (practical) problem of "X number of overloads", yes. So I rest my case :)