Reputation: 17
I'm new to openGL and I'm working on an application where I need to draw several linestrips each with several vertices. All-together, we're talking about 300,000 vertices.
In order to attempt to do this efficiently, I am using a VBO to store the vertex data.
I am using a single VBO for storing all the vertices for all the lines. I then use GLDrawElements
to draw the entire VBO, passing it an index array which specifies which indices to draw, and leveraging primitiveRestart to designate where Linestrips start/end (see code below).
This does not execute as quickly I would've hoped. I can only render at about 10Hz. I'm thinking perhaps it's that I'm passing a large array of indices which must get copied to the GPU each render.
I'm having trouble determining the right direction to attempt to improve performance. Would shaders be the right way to go? Is there some way to NOT write the index array to the GPU during rendering (needed by DrawElements).
Any help determinig the right direction to explore would be appreciated.
I'm writing this in C# using openTK.
//Enabled Primitive Restart
GL.Enable(EnableCap.PrimitiveRestart);
GL.PrimitiveRestartIndex(PrimitiveRestartIndex);
//Generate Linestrip data to buffer (array of vertices)
int[] FrameData = ScanBuffer.getBufferData();
//Create the buffer on the GPU
ScanBuffer.vbo_id = new int[1];
GL.GenBuffers(1, ScanBuffer.vbo_id);
//Buffer the data
GL.BindBuffer(BufferTarget.ArrayBuffer, ScanBuffer.vbo_id[0]);
GL.BufferData(BufferTarget.ArrayBuffer, new IntPtr(sizeof(int) * FrameData.Length), FrameData, BufferUsageHint.StaticDraw);
GL.VertexPointer(2, VertexPointerType.Int, 0, 0);
GL.EnableClientState(ArrayCap.VertexArray);
//Generate the vertex index array
uint[] IndexData = ScanBuffer.getIndexData();
//Draw the VBO
GL.BindBuffer(BufferTarget.ArrayBuffer, ScanBuffer.vbo_id[0]);
GL.VertexPointer(2, VertexPointerType.Int, 0, 0);
GL.DrawElements(BeginMode.LineStrip, IndexData.Length, DrawElementsType.UnsignedInt, IndexData);
I was able to find a viable solution using array element buffer to store my indicies. I get about 10X performance improvement. The buffer and render commands look as follows.
vertexData = generateVertexData();
indexData = generateIndexData();
//Bind to the VAO
GL.BindVertexArray(vaoID[0]);
//Bind + Write Index Data to the Buffer
GL.BindBuffer(BufferTarget.ElementArrayBuffer, veoID[0]);
GL.BufferData(BufferTarget.ElementArrayBuffer, new IntPtr(sizeof(uint) * IndexData.Count()), IndexData.ToArray(), BufferUsageHint.StaticDraw);
//Bind + Write Scan Vertex Data to the Buffer
GL.BindBuffer(BufferTarget.ArrayBuffer, vboID[0]);
GL.BufferData(BufferTarget.ArrayBuffer, new IntPtr(sizeof(int) * VertexData.Count()), VertexData.ToArray(), BufferUsageHint.StaticDraw);
GL.VertexPointer(2, VertexPointerType.Int, 0, 0);
GL.EnableClientState(ArrayCap.VertexArray);
//Remove Binding
GL.BindVertexArray(0);
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
//Enabled Primitive Restart
GL.Enable(EnableCap.PrimitiveRestart);
GL.PrimitiveRestartIndex(PrimitiveRestartIndex);
GL.LineWidth(1);
GL.Color3(Color.Red);
GL.BindBuffer(BufferTarget.ArrayBuffer, vboID[0]);
GL.VertexPointer(2, VertexPointerType.Int, 0, 0);
//-Draw using a VEO-
GL.BindBuffer(BufferTarget.ElementArrayBuffer, veoID[0]);
GL.DrawElements(BeginMode.LineStrip, scanVertexBufferLength, DrawElementsType.UnsignedInt, 0);
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
GL.BindVertexArray(0);
GL.Disable(EnableCap.PrimitiveRestart);
Upvotes: 1
Views: 2005
Reputation: 10390
Shaders might eventually help your performance, but they are definitely not the bottleneck at this point. Your rendering workload is very light and should be blazingly fast if done properly.
First of all, some of your usage of OpenGL is very outdated. Make sure you are following up to date documentation and tutorials to avoid this. I know it can be hard when entering in a new field to tell what is outdated or not. I suggest starting with a recently published OpenGL book if you can.
Now on to your code.
GL.BufferData(BufferTarget.ArrayBuffer, new IntPtr(sizeof(int) * FrameData.Length), FrameData, BufferUsageHint.StaticDraw);
This tells OpenGL to allocate a new buffer for your vertex data and this is done each time you render your geometry. This is obviously pretty bad for performance. What you want to do is allocate the buffer only once or at least only if it needs to grow. This call also uploads your 300000 vertex positions to the GPU each frame which is also costly. If the vertex positions do not change, upload them once and then reuse the buffer for the next frames. If the positions do change, use glBufferSubData()
to upload the new data without reallocating your vertex buffer object's memory.
GL.DrawElements(BeginMode.LineStrip, IndexData.Length, DrawElementsType.UnsignedInt, IndexData);
This is the second big culprit of poor performance. In OpenGL "compatibility mode" which you must be using, glDrawElements()
reads the vertex indices from the last parameter (IndexData
) when no buffer (id 0) is bound to GL_ELEMENT_ARRAY_BUFFER
(your case obviously). In essence, this does the same thing as described in the previous paragraph: allocate a new buffer and upload data each frame. You should create another buffer holding the indices, bind that to GL_ELEMENT_ARRAY_BUFFER
and draw. This will avoid memory allocations and useless data transfers for reasons aforementioned.
Lastly, OpenGL is a state machine. Avoid redundant calls like GL.VertexPointer(2, VertexPointerType.Int, 0, 0);
. To get peak performance, one usually needs to reduce the number of OpenGL function calls.
Upvotes: 1