VBO Buffering

I’ve got a map I want to scroll, a 2d map to be precise. I’ve uploaded my vertexes into a VBO and as it scrolls I intend to update the VBOs contents with the new tiles for the area the player is about to scroll to.

My vertex array uses quads with textures and colours and I only render 20x20 of them. Doesnt seem like much to me.

I’ve tried using GLBufferData to wipe the VBO and update when a new area comes up but it seems to create a small delay when it updates.

I’ve also tried using GLSubBufferData but for some reason it only seems to work when I wipe the VBO and still exhibits the small stutter when updating.

My question is, is there any way to get around this stutter?

Here’s a snippet of my code(it’s in C# using OpenTK)

Rendering Loop(btw, I’m rendering to a texture object which is then getting rendered to my screen)

            GL.BindFramebuffer(FramebufferTarget.FramebufferExt, VBO);
            GL.PushAttrib(AttribMask.ViewportBit);
            GL.Viewport(0, 0, internalrendersize.X, internalrendersize.Y);

            if (GPUUpdated)
            {
                UpdateVBO<Vertex>(Vertexes, test);
                GPUUpdated = false;
            }

            GL.Clear(ClearBufferMask.ColorBufferBit);

            
           GL.PushMatrix();
           GL.Ortho(0, internalrendersize.X, 0, internalrendersize.Y, 0, 1);
           GL.Translate(-translate, 0, 0);
           GL.BindTexture(TextureTarget.Texture2D, texture);


           GL.EnableClientState(ArrayCap.ColorArray);
           GL.EnableClientState(ArrayCap.VertexArray);
           GL.EnableClientState(ArrayCap.TextureCoordArray);

           GL.BindBuffer(BufferTarget.ArrayBuffer, currentBuffer);


           GL.TexCoordPointer(2,TexCoordPointerType.Float, sizeof(float) * 7, new IntPtr(0));
           
           GL.ColorPointer(3, ColorPointerType.Float, sizeof(float) * 7, new IntPtr(sizeof(float) * 2));
           GL.VertexPointer(2, VertexPointerType.Float, sizeof(float) * 7, new IntPtr(sizeof(float) * 5));
           GL.DrawArrays(BeginMode.Quads, 0, 20*20*4);

           GL.DisableClientState(ArrayCap.ColorArray);
           GL.DisableClientState(ArrayCap.TextureCoordArray);
           GL.DisableClientState(ArrayCap.VertexArray);
            

            GL.PopMatrix();
           
            GL.LoadIdentity();

            GL.PopAttrib();
            GL.BindFramebuffer(FramebufferTarget.FramebufferExt, 0);


            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
            
            GL.MatrixMode(MatrixMode.Modelview);
            
            GL.BindTexture(TextureTarget.Texture2D, img);
                    GL.TexParameter(
          TextureTarget.Texture2D,
          TextureParameterName.TextureWrapS,
          (int)TextureWrapMode.Clamp);

            GL.TexParameter(TextureTarget.Texture2D,
              TextureParameterName.TextureWrapT,
              (int)TextureWrapMode.Clamp);

            GL.TexParameter(
              TextureTarget.Texture2D,
              TextureParameterName.TextureMagFilter,
              (int)TextureMagFilter.Linear);

            GL.TexParameter(
              TextureTarget.Texture2D,
              TextureParameterName.TextureMinFilter,
              (int)TextureMinFilter.Linear);

            GL.Begin(BeginMode.Quads);

            GL.Vertex2(0.0, -1.0);
            GL.TexCoord2(0.0, 0.0);
            GL.Vertex2(-1.0, -1.0);
            GL.TexCoord2(0.0, 1.0);
            GL.Vertex2(-1.0, 0.0);
            GL.TexCoord2(1.0, 1.0);
            GL.Vertex2(0.0, 0.0);
            GL.TexCoord2(1.0, 0.0);
            GL.End();
            currentwindow.SwapBuffers();

When I do update I have a vertex array the same size as the first vertex array I uploaded which changed verts and texture coordinates.

Here’s the code when I update the VBO.

            GL.BindBuffer(BufferTarget.ArrayBuffer, test2.VboID);

            GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vertices.Length * BlittableValueType.StrideOf(vertices)), IntPtr.Zero, BufferUsageHint.StreamDraw);
            GL.BufferSubData(BufferTarget.ArrayBuffer, (IntPtr)0, (IntPtr)(vertices.Length * BlittableValueType.StrideOf(vertices)), Vertexes);

A couple of things you could try:

GL_DYANMIC_DRAW as the hint instead of STREAM_DRAW. This is only a hint and probably won’t do much.

During the update, you don’t need to call both glBufferData and glBufferSubData, only the latter. This should fix the problem.

I’d suggest using two VBOs and alternate between them every other frame (ping-pong). Now you can decouple updates from rendering…

What platform is this? Java? What video card?
I know for sure that calling glBufferData with NULL and then glBufferSubData is more expensive than simply calling glBufferData. Actally, in contrast to what BionicBytes said, if you use glBufferSubData instead of glBufferData you may stall the driver, because it will have to wait for pending draw calls (see http://developer.nvidia.com/attach/6427).
Are you sure it’s a draw call problem and not unpacking/CPU related?
And yes, DYNAMIC_DRAW is better for your case. I also vote for a ping-pong implementation though I would expect that it should work with just one buffer. But anyway, do anything that works in your case.

If I remove BufferData then GL.BufferSubData seems to do nothing at all. I do have the entire vertex array in Vertexes but I dont know if that’d be slowing it down that much.

I did give having two VBOs a go but it seemed like the stutter was still there(i’ll try again tomorrow.

Also, the platform is Windows, C#, and a ATI 4870.

I have tried doing just GLBufferData and the stutter is there but I have not had time to investigate.

By the way, Vertexes is a reference to an instance of a struct that has all the Vertexes, Texture Coordinates and Colours.
There’s roughly 100x100 tiles, so there will be roughly 100000 floats that need to be uploaded. This doesnt seem like the most efficient way to me…

I will try again tomorrow and let you guys know how I go, I was working on another implementation using client side vertex arrays.

No, do one BufferData to force-allocate the buffer. You can provide a NULL data pointer for this. Then for subsequent updates, only call BufferSubData.

Semantically:

BufferData = Allocate and fill (latter only if non-NULL pointer provided
BufferSubData = Fill only