Molti quad, non molti fps nonostante VBO
-
27-10-2019 - |
Domanda
Ho un po 'di tempo e ho letto di VBO ed è quello che ho ottenuto:
http://img64.imageshack.us/img64/5733/fps8.jpg
Ok, è molto meglio di prima. È compilato in rilascio. Uso VBO (probabilmente, se va tutto bene) e glDrawArrays per disegnare.
Ecco il codice del disegno. Per favore dammi consigli su come ottimizzarlo. Volevo con il terreno ... uh poche migliaia di FPS, è reale?
void DrawVBO (void)
{
int i;
CVert* m_pVertices;
CTexCoord* m_pTexCoords;
unsigned int m_nTextureId;
m_pVertices = NULL;
m_pTexCoords = NULL;
m_nVertexCount = 0;
m_nVBOVertices = m_nVBOTexCoords = m_nTextureId = 0;
if( IsExtensionSupported( "GL_ARB_vertex_buffer_object" ) )
{
// Pobierz wskaźniki na funkcje OpenGL
glGenBuffersARB = (PFNGLGENBUFFERSARBPROC) wglGetProcAddress("glGenBuffersARB");
glBindBufferARB = (PFNGLBINDBUFFERARBPROC) wglGetProcAddress("glBindBufferARB");
glBufferDataARB = (PFNGLBUFFERDATAARBPROC) wglGetProcAddress("glBufferDataARB");
glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC)
wglGetProcAddress("glDeleteBuffersARB");
}
todrawquads=0;
nIndex=0;
// my function counting how many quads I will draw
for (i=0;i<MAX_CHUNKS_LOADED;i++)
{
if (chunks_loaded[i].created==1)
{
countquads(i);
}
}
m_nVertexCount=4*todrawquads;
m_pVertices = new CVec[m_nVertexCount];
m_pTexCoords = new CTexCoord[m_nVertexCount];
// another my function adding every quad which i'm going to draw (its verticles) to array
for (i=0;i<MAX_CHUNKS_LOADED;i++)
{
if (chunks_loaded[i].created==1)
{
addchunktodraw(i,m_pVertices,m_pTexCoords);
}
}
glClearColor (1,1,1, 0.0);
glColor3f(1,1,1);
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear Screen And Depth Buffer
glLoadIdentity (); // Reset The Modelview Matrix
fps++;
// Camera settings.
//gluLookAt (zom, zom, zom, 0.0, 0.0, 0.0, 0, 0, 1);
gluLookAt (zoom, zoom, zoom, 0.0, 0.0, 0.0, 0, 0, 1);
glRotatef((rot_x / 180 * 3.141592654f),1,0,0);
glRotatef((rot_y / 180 * 3.141592654f),0,1,0);
glRotatef((rot_z / 180 * 3.141592654f),0,0,1);
//m_nTextureId = t_terrain;
// Generate And Bind The Vertex Buffer
glGenBuffersARB( 1, &m_nVBOVertices ); // Get A Valid Name
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOVertices ); // Bind The Buffer
// Load The Data
glBufferDataARB( GL_ARRAY_BUFFER_ARB, m_nVertexCount*3*sizeof(float), m_pVertices, GL_STATIC_DRAW_ARB );
// Generate And Bind The Texture Coordinate Buffer
glGenBuffersARB( 1, &m_nVBOTexCoords ); // Get A Valid Name
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOTexCoords ); // Bind The Buffer
// Load The Data
glBufferDataARB( GL_ARRAY_BUFFER_ARB, m_nVertexCount*2*sizeof(float), m_pTexCoords, GL_STATIC_DRAW_ARB );
// Enable Pointers
glEnableClientState( GL_VERTEX_ARRAY ); // Enable Vertex Arrays
glEnableClientState( GL_TEXTURE_COORD_ARRAY ); // Enable Texture Coord Arrays
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOVertices );
glVertexPointer( 3, GL_FLOAT, 0, (char *) NULL ); // Set The Vertex Pointer To The Vertex Buffer
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOTexCoords );
glTexCoordPointer( 2, GL_FLOAT, 0, (char *) NULL ); // Set The TexCoord Pointer To The TexCoord Buffe
glDrawArrays( GL_QUADS, 0, m_nVertexCount); // Draw All Of The Triangles At Once
glDisableClientState( GL_VERTEX_ARRAY ); // Disable Vertex Arrays
glDisableClientState( GL_TEXTURE_COORD_ARRAY ); // Disable Texture Coord Arrays
liniergb();
glutSwapBuffers();
delete [] m_pVertices; m_pVertices = NULL;
delete [] m_pTexCoords; m_pTexCoords = NULL;
}
Allora cosa posso farci? (Il codice sopra è la funzione di disegno principale)
modifica
Ho spostato questo:
if( IsExtensionSupported( "GL_ARB_vertex_buffer_object" ) )
{
// Pobierz wskaźniki na funkcje OpenGL
glGenBuffersARB = (PFNGLGENBUFFERSARBPROC) wglGetProcAddress("glGenBuffersARB");
glBindBufferARB = (PFNGLBINDBUFFERARBPROC) wglGetProcAddress("glBindBufferARB");
glBufferDataARB = (PFNGLBUFFERDATAARBPROC) wglGetProcAddress("glBufferDataARB");
glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC)
wglGetProcAddress("glDeleteBuffersARB");
}
alla mia funzione principale. Probabilmente nessun miglioramento.
modifica2
Ora, posso anche vedere che sta mangiando la memoria. Ogni pochi secondi l'utilizzo della memoria del programma aumenta sempre di più ... Cosa c'è che non va? Cosa non sto eliminando?
modifica3
Ok, grazie mille. Ho spostato del codice fuori dalla funzione di disegno e ... molti più fps! Grazie mille!
http://img197.imageshack.us/img197/5193/fpsfinal.jpg
È una mappa di 640x640 blocchi (quindi 40 volte più grande) con 650'000 quad (circa 70 volte di più) e ancora ~ 170 fps. Grande ! E nessuna perdita di memoria. Grazie ancora!
Soluzione
La tua funzione DrawVBO dovrebbe contenere solo:
glClearColor (1,1,1, 0.0);
glColor3f(1,1,1);
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear Screen And Depth Buffer
glLoadIdentity (); // Reset The Modelview Matrix
fps++;
// Camera settings.
//gluLookAt (zom, zom, zom, 0.0, 0.0, 0.0, 0, 0, 1);
gluLookAt (zoom, zoom, zoom, 0.0, 0.0, 0.0, 0, 0, 1);
glRotatef((rot_x / 180 * 3.141592654f),1,0,0);
glRotatef((rot_y / 180 * 3.141592654f),0,1,0);
glRotatef((rot_z / 180 * 3.141592654f),0,0,1);
// Enable Pointers
glEnableClientState( GL_VERTEX_ARRAY ); // Enable Vertex Arrays
glEnableClientState( GL_TEXTURE_COORD_ARRAY ); // Enable Texture Coord Arrays
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOVertices );
glVertexPointer( 3, GL_FLOAT, 0, (char *) NULL ); // Set The Vertex Pointer To The Vertex Buffer
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOTexCoords );
glTexCoordPointer( 2, GL_FLOAT, 0, (char *) NULL ); // Set The TexCoord Pointer To The TexCoord Buffe
glDrawArrays( GL_QUADS, 0, m_nVertexCount); // Draw All Of The Triangles At Once
glDisableClientState( GL_VERTEX_ARRAY ); // Disable Vertex Arrays
glDisableClientState( GL_TEXTURE_COORD_ARRAY ); // Disable Texture Coord Arrays
liniergb();
glutSwapBuffers();
È necessario spostare il resto in una funzione separata chiamata solo una volta all'avvio (o quando il terreno cambia).
Altri suggerimenti
Stai ricaricando tutti i tuoi buffer e liberandoli di nuovo in ogni singolo frame?Smetti di farlo e il tuo frame rate aumenterà.
Tieni presente che il tuo codice corrente finirà per esaurire gli identificatori VBO, poiché non elimini mai i VBO che crei.
Anche il collegamento di funzioni di estensione sicuramente non deve essere eseguito a ogni frame.
Alcune cose risaltano:
Nella tua funzione di disegno stai allocando la memoria per i tuoi dati geometrici
m_pVertices = new CVec[m_nVertexCount];
m_pTexCoords = new CTexCoord[m_nVertexCount];
L'allocazione della memoria è un'operazione estremamente costosa, questa è una di quelle cose che dovrebbero essere fatte solo una volta. OpenGL non è pensato per essere "inizializzato", ma le strutture dati che gli passerai lo sono!
Qui stai copiando i buffer appena allocati in OpenGL, ancora e ancora con ogni frame. Questo è esattamente l'opposto di cosa fare.
// Generate And Bind The Vertex Buffer
glGenBuffersARB( 1, &m_nVBOVertices ); // Get A Valid Name
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOVertices ); // Bind The Buffer
// Load The Data
glBufferDataARB( GL_ARRAY_BUFFER_ARB, m_nVertexCount*3*sizeof(float), m_pVertices, GL_STATIC_DRAW_ARB );
// Generate And Bind The Texture Coordinate Buffer
glGenBuffersARB( 1, &m_nVBOTexCoords ); // Get A Valid Name
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOTexCoords ); // Bind The Buffer
// Load The Data
glBufferDataARB( GL_ARRAY_BUFFER_ARB, m_nVertexCount*2*sizeof(float), m_pTexCoords, GL_STATIC_DRAW_ARB );
L'intera idea dei VBO è, caricare i dati una sola volta, copiarli in OpenGL e poi non riallocarli mai più. Nota che questa non è un'inizializzazione OpenGL, è un'inizializzazione dei dati, qualcosa di assolutamente ragionevole. Vedo che hai chiamato le tue variabili m_pVertices
e m_pTexCoords
indicando che quelle sono variabili membro di classe. Quindi la soluzione è semplice: sposta l'intero codice di inizializzazione in una funzione di caricamento. Inoltre, invece degli array C ++ nudi, suggerisco caldamente di utilizzare std::vector
.
Quindi risolviamo questo problema:
// Load Extensions only once. Well, once per context actually, however
// why don't you just use an extension wrapper and forget about those
// gritty details? Google GLEW or GLee
void init_extensions()
{
if( IsExtensionSupported( "GL_ARB_vertex_buffer_object" ) )
{
// Pobierz wska\u017aniki na funkcje OpenGL
glGenBuffersARB = (PFNGLGENBUFFERSARBPROC) wglGetProcAddress("glGenBuffersARB");
glBindBufferARB = (PFNGLBINDBUFFERARBPROC) wglGetProcAddress("glBindBufferARB");
glBufferDataARB = (PFNGLBUFFERDATAARBPROC) wglGetProcAddress("glBufferDataARB");
glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC) wglGetProcAddress("glDeleteBuffersARB");
}
}
class Block0r
{
protected:
GLuint m_nTextureId;
GLuint m_nVBOVertices;
GLuint m_nVBOTexCoords;
GLuint m_nVertexCount;
// Call this one time to load the data
void LoadVBO()
{
std::vector<CVert> vertices;
std::vector<CTexCoord> texCoords;
// my function counting how many quads I will draw
todrawquads = 0;
for(int i=0; i < MAX_CHUNKS_LOADED; i++) {
if( chunks_loaded[i].created == 1 ) {
countquads(i);
}
}
m_nVertexCount = 4*todrawquads;
vertices.resize(vertexcount);
texcoords.resize(vertexcount);
for (i=0;i<MAX_CHUNKS_LOADED;i++) {
if (chunks_loaded[i].created==1) {
addchunktodraw(i, &vertices[0], &texcoords[0]);
}
}
glGenBuffersARB( 1, &m_nVBOVertices );
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOVertices );
glBufferDataARB( GL_ARRAY_BUFFER_ARB, vertices.size()*sizeof(CVert), &vertices[0], GL_STATIC_DRAW_ARB );
glGenBuffersARB( 1, &m_nVBOTexCoords );
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOTexCoords );
glBufferDataARB( GL_ARRAY_BUFFER_ARB, texCoords.size()*sizeof(CTexCoord), &texCoords[0], GL_STATIC_DRAW_ARB );
}
void DrawVBO()
{
glClearColor (1,1,1, 0.0);
glColor3f(1,1,1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
setup_projection(); // this should really be done in the drawing handler
glMatrixMode(GL_MODELVIEW); // don't asssume a certain matrix being active!
glLoadIdentity();
fps++;
gluLookAt (zoom, zoom, zoom, 0.0, 0.0, 0.0, 0, 0, 1);
glRotatef((rot_x / 180 * 3.141592654f),1,0,0);
glRotatef((rot_y / 180 * 3.141592654f),0,1,0);
glRotatef((rot_z / 180 * 3.141592654f),0,0,1);
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOVertices );
glVertexPointer( 3, GL_FLOAT, 0, (GLvoid *) 0 );
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOTexCoords );
glTexCoordPointer( 2, GL_FLOAT, 0, (GLvoid *) 0 );
glDrawArrays( GL_QUADS, 0, m_nVertexCount);
glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
liniergb();
glutSwapBuffers();
}
}
Nota a margine: commenti come // Generate And Bind The Vertex Buffer
o // Set The Vertex Pointer To The Vertex Buffer
non sono molto utili. Quei commenti dicono solo in modo ridondante, cosa si può leggere comunque dal codice.
I commenti dovrebbero essere aggiunti al codice, il cui funzionamento interno non è immediatamente comprensibile, o se dovessi fare un qualche tipo di hack per aggiustare qualcosa e questo hack potrebbe sconcertare qualcun altro, o te stesso nel giro di pochi mesi.