LucasS
LucasS

Reputation: 729

VBO Rendering Slow

I have been learning VBOs for a couple weeks now, and I have been told here that VBOs can render "~1 million vertices at several hundred fps". However, my current VBO test program only gets around 50 FPS with a little of 1 million vertices to render. Are there ways to optimize VBO efficiency? Or, more likely, am I doing something incorrectly? My test program is here:

EDIT: Improved code based on feedback.

#include <windows.h>
#include <SFML/Graphics.hpp>
#include <iostream>

#include <glew.h>
#include <gl/gl.h>
#include <gl/glu.h>

using namespace std;

float cube_vertices[] = {-1, -1, 1,
                         1, -1, 1,
                         1, 1, 1,
                         -1, 1, 1,

                         -1, -1, -1,
                         -1, 1, -1,
                         1, 1, -1,
                         1, -1, -1,

                         -1, 1, -1,
                         -1, 1, 1,
                         1, 1, 1,
                         1, 1, -1,

                         -1, -1, -1,
                         1, -1, -1,
                         1, -1, 1,
                         -1, -1, 1,

                         1, -1, -1,
                         1, 1, -1,
                         1, 1, 1,
                         1, -1, 1,

                         -1, -1, -1,
                         -1, -1, 1,
                         -1, 1, 1,
                         -1, 1, -1};

float cube_normals[] = {0, 0, 1,
                        0, 0, 1,
                        0, 0, 1,
                        0, 0, 1,

                        0, 0, -1,
                        0, 0, -1,
                        0, 0, -1,
                        0, 0, -1,

                        0, 1, 0,
                        0, 1, 0,
                        0, 1, 0,
                        0, 1, 0,

                        0, -1, 0,
                        0, -1, 0,
                        0, -1, 0,
                        0, -1, 0,

                        1, 0, 0,
                        1, 0, 0,
                        1, 0, 0,
                        1, 0, 0,

                        -1, 0, 0,
                        -1, 0, 0,
                        -1, 0, 0,
                        -1, 0, 0};
class Scene {
public:
    void setup_projection( int w, int h ) {
        glViewport( 0, 0, w, h );
        glMatrixMode( GL_PROJECTION );
        glLoadIdentity();
        gluPerspective( 50, (GLdouble)w/(GLdouble)h, 1, 5000.0 );
        glMatrixMode( GL_MODELVIEW );
    }
};
int main() {
    ///Number of models to render
    int NumberOfCubes = 0;
    cout << "Enter number of cubes to render: ";
    cin >> NumberOfCubes;
    system("cls");

    ///Create vectors for mesh data
    //6 faces * 4 verts * x, y, z * number of cubes
    std::vector<float> vertices; vertices.resize(6*4*3*NumberOfCubes);
    std::vector<float> normals; normals.resize(6*4*3*NumberOfCubes);
    for(int i = 0; i < NumberOfCubes; i++)
    {
        for(int j = 0; j < 6*4*3; j++)
        {
            vertices[(i*6*4*3) + j] = cube_vertices[j] + i;
            normals[(i*6*4*3) + j] = cube_normals[j];
        }
    }

    ///Store size of the vectors
    int SizeOfVertices = vertices.size() * sizeof(float);
    int SizeOfNormals = normals.size() * sizeof(float);

    ///Window setup, lighting setup
    sf::RenderWindow window(sf::VideoMode(800, 600, 32), "Test");
    Scene scene;
    scene.setup_projection(window.getSize().x,window.getSize().y);
    glewInit();
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_LIGHTING);
    glShadeModel(GL_SMOOTH);
    glEnable(GL_LIGHT0);
    float XL = .5, YL = .1, ZL = 1;
    GLfloat ambientLight[] = { 0.2f, 0.2f, 0.2f, 1.0f };
    GLfloat diffuseLight[] = { 0.8f, 0.8f, 0.8, 1.0f };
    GLfloat specularLight[] = { 0.5f, 0.5f, 0.5f, 1.0f };
    GLfloat lightpos[] = {XL, YL, ZL, 0.};
    glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
    glLightfv(GL_LIGHT0, GL_SPECULAR, specularLight);
    glLightfv(GL_LIGHT0, GL_POSITION, lightpos);

    ///Generate the VBO
    GLuint VBOID;
    glGenBuffers(1, &VBOID);
    glBindBuffer(GL_ARRAY_BUFFER, VBOID);
    glBufferData(GL_ARRAY_BUFFER, SizeOfVertices + SizeOfNormals, 0, GL_STATIC_DRAW);
    glBufferSubData(GL_ARRAY_BUFFER, 0, SizeOfVertices, &vertices[0]);
    glBufferSubData(GL_ARRAY_BUFFER, SizeOfVertices, SizeOfNormals + SizeOfVertices, &normals[0]);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    ///FPS Stuff
    sf::Clock FPS;
    sf::Clock ShowFPS;
    float fps;

    ///Start loop
    cout << "Rendering " << NumberOfCubes * 8 << " vertices." << endl;
    cout << "Using graphics card: " << glGetString(GL_RENDERER) << endl;

    while( window.isOpen() ) {
        sf::Event event;
        while( window.pollEvent( event ) ) {
            if( event.type == sf::Event::Closed )
                window.close();
        }
        fps = FPS.getElapsedTime().asSeconds();
        fps = 1 / fps;
        FPS.restart();
        if(ShowFPS.getElapsedTime().asSeconds() > 1)
        {
            cout << "FPS: " << fps << "\t FrameTime: " << 1000 / fps << endl;
            ShowFPS.restart();
        }

        glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        scene.setup_projection(window.getSize().x,window.getSize().y);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        gluLookAt(-25, -25, 150, 50, 50, 50, 0, 1, 0);

        glBindBuffer(GL_ARRAY_BUFFER, VBOID);
        glEnableClientState(GL_NORMAL_ARRAY);
        glEnableClientState(GL_VERTEX_ARRAY);
        glColor3f(1, 0, 0);

        glNormalPointer(GL_FLOAT, 0, 0);
        glVertexPointer(3, GL_FLOAT, 0, 0);

        glDrawArrays(GL_QUADS, 0, 6*4*NumberOfCubes);

        glDisableClientState(GL_VERTEX_ARRAY);
        glDisableClientState(GL_NORMAL_ARRAY);
        glBindBuffer(GL_ARRAY_BUFFER, 0);

        window.display();
    }
    return 1;
}

Upvotes: 0

Views: 718

Answers (3)

LucasS
LucasS

Reputation: 729

After doing further research, I found out about VBO Indexing and was able to use that to get the several hundred FPS with a million vertices.

Upvotes: 0

datenwolf
datenwolf

Reputation: 162367

A few remarks on your code:

void Scene::resize( int w, int h ) {
    glViewport( 0, 0, w, h );
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    gluPerspective( 50, (GLdouble)w/(GLdouble)h, 1, 5000.0 );
    glMatrixMode( GL_MODELVIEW );
}

Please do understand that setting the viewport and the projection are not some sort "resizing" operation. They're part of the drawing process and hence should be treated like that. The good thing is, that you call this function with every drawing iteration. But I'd not call it resize. A better name was setup_projection or similar, to make it clear, what this function does, and not, what it does react upon. Always call a function by what it does!

This

cout << endl << endl << "Close program when finished." << endl;

bool ProgramRunning(true);
while(ProgramRunning == true) {}

probably does not work as you might expect. What you close is the console window/terminal; this makes your program to loose its standrd input and process leader thereby terminating it. None of the code after the while loop is going to be executed at all. You could install signal handlers that would set the – so far functionless – ProgrammRunning flag to false.

However the canonical way to deal with this is simply waiting for the user to pause until the user hits the enter key:

cout << "Program execution finished, hit the ENTER key to terminate" << endl;
cin.get();

Now about why you get only 50 FPS: The most likely reason is, that you got V-Sync enabled and your display has a refresh frequency of 50Hz. 50Hz is unusual, but not unheared of. Also likely is, that your display is running at 60Hz, but for some reason you're not making the refresh deadline for each retrace, effectively making your code miss in average every 6th frame.

Another reason may be, that you're not running on the GeForce, but maybe on chipset GPU of your laptop. If you have a Hybrid graphics system, make sure you got all the drivers properly installed and that you switch to the GeForce GPU before executing your program.

Print the output of glGetString(GL_RENDERER); to make sure. After opening the window, creating the OpenGL context add a

cout << glGetString(GL_RENDERER) << endl;

Upvotes: 1

v154c1
v154c1

Reputation: 1698

Are you using double buffering? Is yes, then you may have sync to vblank enabled in your drivers. This would mean, that EVERY OpengGL application using double buffering will render at most at the refresh rate of your monitor (usually around 50 - 60Hz).

You can try the same code with (significantly) smaller model to see if your FPS ever goes above this value.

Upvotes: 0

Related Questions