0
\$\begingroup\$

trying to improve draw performance on my c++, OpenGL, SDL game. Back in school we mostly learned immediate mode, so thats 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 clearly 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();
}
\$\endgroup\$
  • 1
    \$\begingroup\$ Just for your edification, the legacy API you're talking about is called immediate mode, not intermediate mode. As in, the old functions didn't buffer anything and had an immediate result, as opposed to vertex buffers that defer the rendering output until the actual draw call. \$\endgroup\$ – Sean Middleditch Feb 22 '16 at 16:39
  • \$\begingroup\$ oh man, ha thanks. yeah my brain was processing that word as immediate but for some reason wrote down intermediate haha. thanks for the catch \$\endgroup\$ – Dylan Feb 22 '16 at 18:38
1
\$\begingroup\$

A lot of people think, that every aspect of the VBOs are fast, that's not really true. Binding a VBO is very slow. Always make sure, you bind VBOs one time, then render the same model multiple times with different position, rotation, etc.

\$\endgroup\$
0
\$\begingroup\$

glBegin(GL_TRIANGLES);

You ar still using old school glBegin/glEnd for drawing, this is not going to be faster.

You need to bind the buffer, push data to the VBO after changing it, and then draw with said buffer. This will most likely decrease your drawtime. Also, try and find geometry that does not change and create a static VBO for all that data, push it to the gpu. Now you only need to bind the buffer and draw it.

[1] - https://en.wikipedia.org/wiki/Vertex_Buffer_Object

\$\endgroup\$
  • \$\begingroup\$ I think you misread the post. You are reading from the part of my code that was replaced \$\endgroup\$ – Dylan Feb 21 '16 at 20:39
  • \$\begingroup\$ I only posted the old code before implementing VBOs for comparison "and heres what the draw method for Tesselator used to look like before adding VBOs... and in Drawing.cpp:" \$\endgroup\$ – Dylan Feb 21 '16 at 20:44
  • 1
    \$\begingroup\$ That is correct, I totally missed the text in between explaning before and after - sorry! Going from glBegin/glEnd to glBindBuffer et al and not decreasing the time it takes to render is wierd. It is really hard to say what might cause this. \$\endgroup\$ – Gosso Feb 21 '16 at 22:03
  • 1
    \$\begingroup\$ Those functions are all deprecated since 3+ core as can be read in the Wikipedia article about OpenGL. I guess adding an color to your vertices could help, or just for debugging - change clear color to non-black; this should give you black lines when rendering. \$\endgroup\$ – Gosso Feb 22 '16 at 6:55
  • 1
    \$\begingroup\$ I recommend downloading a OpenGL debugger such as CodeXL by AMD. It's hard to give any further advice without knowing more about the code of your program in general. There might be something that is slowing things down that is not directly related to VBOs. \$\endgroup\$ – Gosso Feb 22 '16 at 7:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.