I'm trying to improve the draw performance on my c++, OpenGL, SDL game.
Back in school we mostly learned immediate mode, so that's how my drawing was originally implemented. When I started reading up online about improving performance, I read about VBOs and how they are 'so much better' and use the graphics card memory, etc.
So I took a couple of nights and converted to them. When I tested the game, it actually ran slower (draw method went from ~7 to ~10 ticks).
Did I do something wrong or are VBOs only more efficient in certain situations?
Here's my class for tessellated polygons after adding VBOs (my game draws a lot of flat 2D shapes in a 3D world, sort of papermario-style)
#include "Tesselator.h" Tesselator::Tesselator(std::vector<b2Vec2> vertices, Gradient* gradient) { this->vertices = vertices; this->gradient = gradient; tesselator = gluNewTess(); gluTessCallback(tesselator, GLU_TESS_BEGIN_DATA, (void(CALLBACK*)())beginCallback); gluTessCallback(tesselator, GLU_TESS_VERTEX_DATA, (void(CALLBACK*)())vertexCallback); gluTessCallback(tesselator, GLU_TESS_END, (void(CALLBACK*)())endCallback); int vertcount = (int)vertices.size(); GLdouble vertarray[vertcount][3]; for (int i = 0; i < vertices.size(); i++) { vertarray[i][0] = vertices[i].x; vertarray[i][1] = vertices[i].y; vertarray[i][2] = 0; } gluTessBeginPolygon(tesselator, this); gluTessBeginContour(tesselator); for (int i = 0; i < vertices.size(); i++) { gluTessVertex(tesselator, vertarray[i], vertarray[i]); } gluTessEndContour(tesselator); gluTessEndPolygon(tesselator); gluDeleteTess(tesselator); for (int i = 0; i < triangles.size(); i++) { triangleVBOs.push_back(new VertexBufferObject(triangles[i])); } for (int i = 0; i < trianglefans.size(); i++) { triangleFanVBOs.push_back(new VertexBufferObject(trianglefans[i])); } for (int i = 0; i < trianglestrips.size(); i++) { triangleStripVBOs.push_back(new VertexBufferObject(trianglestrips[i])); } } Tesselator::~Tesselator() { for (int i = 0; i < triangleVBOs.size(); i++) { delete triangleVBOs[i]; } for (int i = 0; i < triangleFanVBOs.size(); i++) { delete triangleFanVBOs[i]; } for (int i = 0; i < triangleStripVBOs.size(); i++) { delete triangleStripVBOs[i]; } triangleVBOs.clear(); triangleFanVBOs.clear(); triangleStripVBOs.clear(); } void Tesselator::draw(float z) { Drawing::applyZTranslation(z); for (int i = 0; i < triangleVBOs.size(); i++) { triangleVBOs[i]->draw(GL_TRIANGLES); } for (int i = 0; i < triangleFanVBOs.size(); i++) { triangleFanVBOs[i]->draw(GL_TRIANGLE_FAN); } for (int i = 0; i < triangleStripVBOs.size(); i++) { triangleStripVBOs[i]->draw(GL_TRIANGLE_STRIP); } Drawing::applyZTranslation(-z); } void Tesselator::addTriangle() { std::vector<b2Vec2> triangleVertices; triangles.push_back(triangleVertices); } void Tesselator::addTriangleFan() { std::vector<b2Vec2> triangleFanVertices; trianglefans.push_back(triangleFanVertices); } void Tesselator::addTriangleStrip() { std::vector<b2Vec2> triangleStripVertices; trianglestrips.push_back(triangleStripVertices); } void Tesselator::addVertex(b2Vec2 vertex) { if (currentType == GL_TRIANGLES) { triangles.back().push_back(b2Vec2(vertex.x, vertex.y)); } else if (currentType == GL_TRIANGLE_FAN) { trianglefans.back().push_back(b2Vec2(vertex.x, vertex.y)); } else if (currentType == GL_TRIANGLE_STRIP) { trianglestrips.back().push_back(b2Vec2(vertex.x, vertex.y)); } } void CALLBACK Tesselator::beginCallback(GLenum type, GLvoid * tessdata) { Tesselator* tess = (Tesselator*) tessdata; tess->currentType = type; if (type == GL_TRIANGLES) { tess->addTriangle(); } else if (type == GL_TRIANGLE_FAN) { tess->addTriangleFan(); } else if (type == GL_TRIANGLE_STRIP) { tess->addTriangleStrip(); } } void CALLBACK Tesselator::vertexCallback(GLdouble* vertex, GLvoid* tessdata) { Tesselator* tess = (Tesselator*) tessdata; tess->addVertex(b2Vec2(*(vertex), *(vertex + 1))); } void CALLBACK Tesselator::endCallback() { } and here is my vertex buffer object class
#include "VertexBufferObject.h" VertexBufferObject::VertexBufferObject(std::vector<b2Vec2> shapeVertices) { numberOfCoordinateValues = 0; for (int i = 0; i < shapeVertices.size(); i++) { numberOfCoordinateValues = numberOfCoordinateValues + 2; } float coordinateValues[numberOfCoordinateValues]; int coordinateValueArrayIndex = 0; for (int i = 0; i < shapeVertices.size(); i++) { coordinateValues[coordinateValueArrayIndex] = shapeVertices[i].x; coordinateValueArrayIndex++; coordinateValues[coordinateValueArrayIndex] = shapeVertices[i].y; coordinateValueArrayIndex++; } glGenBuffers(1, &id); glBindBuffer(GL_ARRAY_BUFFER, id); glBufferData(GL_ARRAY_BUFFER, sizeof(coordinateValues), coordinateValues, GL_STATIC_DRAW); } VertexBufferObject::~VertexBufferObject() { glDeleteBuffers(1, &id); } void VertexBufferObject::draw(GLenum mode) { glBindBuffer(GL_ARRAY_BUFFER, id); glVertexPointer(2, GL_FLOAT, 0, NULL); glEnableClientState(GL_VERTEX_ARRAY); glDrawArrays(mode, 0, numberOfCoordinateValues/2); glDisableClientState(GL_VERTEX_ARRAY); glBindBuffer(GL_ARRAY_BUFFER, 0); } and heres what the draw method for Tesselator used to look like before adding VBOs
void Tesselator::draw(float z) { for (int i = 0; i < triangles.size(); i++) { Drawing::drawTriangles(triangles[i], z, gradient); } for (int i = 0; i < trianglefans.size(); i++) { Drawing::drawTriangleFan(trianglefans[i], z, gradient); } for (int i = 0; i < trianglestrips.size(); i++) { Drawing::drawTriangleStrip(trianglestrips[i], z, gradient); } } and in Drawing.cpp:
void Drawing::drawTriangles(std::vector<b2Vec2> vertices, float z, Gradient* gradient) { glBegin(GL_TRIANGLES); for (int i = 0; i < vertices.size(); i++) { addVertex(vertices[i].x, vertices[i].y, z, gradient); } glEnd(); } void Drawing::drawTriangleFan(std::vector<b2Vec2> vertices, float z, Gradient* gradient) { glBegin(GL_TRIANGLE_FAN); for (int i = 0; i < vertices.size(); i++) { addVertex(vertices[i].x, vertices[i].y, z, gradient); } glEnd(); } void Drawing::drawTriangleStrip(std::vector<b2Vec2> vertices, float z, Gradient* gradient) { glBegin(GL_TRIANGLE_STRIP); for (int i = 0; i < vertices.size(); i++) { addVertex(vertices[i].x, vertices[i].y, z, gradient); } glEnd(); }