Sto lavorando a un'applicazione OpenGL basata su tile, C++. Sto aggiungendo schermata di esempio dall'applicazione, in modo che sarà più chiaro:VBO più lento del metodo obsoleto di disegno delle primitive - perché?
Ho Tile
classe che contiene un array di Object
s. Ogni riquadro può contenere fino a 15 oggetti: l'esempio è Tile
con un quadrato verde e giallo (due oggetti), quindi è 10 x 10 x 15 = 1500 Object
s disegnare (nel peggiore dei casi, perché non sto gestendo 'vuoto 'quelli). Di solito è meno, nei miei test ne uso circa 600. Object
ha il proprio grafico, che può essere disegnato. Ogni Tile
appartiene a uno Tile
alla volta, ma può essere spostato (come ad esempio i quadrati rossi nell'immagine).
Object
s gli sfondi avranno un bordo e devono essere ben scalabili, quindi sto usando un modello a 9 patch per disegnarli (sono fatti di 9 quad).
Senza il disegno Tile
s (il loro Object
s per la precisione), la mia applicazione ha circa 600 fps
.
In un primo momento, ho usato il metodo obsoleto per disegnare quei Tile
s - usando glBegin(GL_QUADS)
/glEnd()
e glDisplayList
s. Ho avuto un grande calo di prestazioni a causa di quel disegno - da 600
a 320 fps
. Questo è il modo che ho di loro disegnando:
bool Background::draw(const TPoint& pos, int width, int height)
{
if(width <= 0 || height <= 0)
return false;
//glFrontFace(GL_CW);
glPushMatrix();
glTranslatef((GLfloat)pos.x, (GLfloat)pos.y, 0.0f); // Move background to right direction
if((width != m_savedWidth) || (height != m_savedHeight)) // If size to draw is different than the one saved in display list,
// then recalculate everything and save in display list
{
// That size will be now saved in display list
m_savedWidth = width;
m_savedHeight = height;
// If this background doesn't have unique display list id specified yet,
// then let OpenGL generate one
if(m_displayListId == NO_DISPLAY_LIST_ID)
{
GLuint displayList;
displayList = glGenLists(1);
m_displayListId = displayList;
}
glNewList(m_displayListId, GL_COMPILE);
GLfloat texelCentersOffsetX = (GLfloat)1/(2*m_width);
// Instead of coordinates range 0..1 we need to specify new ones
GLfloat maxTexCoordWidth = m_bTiling ? (GLfloat)width/m_width : 1.0;
GLfloat maxTexCoordHeight = m_bTiling ? (GLfloat)height/m_height : 1.0;
GLfloat maxTexCoordBorderX = (GLfloat)m_borderWidth/m_width;
GLfloat maxTexCoordBorderY = (GLfloat)m_borderWidth/m_height;
/* 9-cell-pattern
-------------------
| 1 | 2 | 3 |
-------------------
| | | |
| 4 | 9 | 5 |
| | | |
-------------------
| 6 | 7 | 8 |
-------------------
*/
glBindTexture(GL_TEXTURE_2D, m_texture); // Select Our Texture
// Top left quad [1]
glBegin(GL_QUADS);
// Bottom left
glTexCoord2f(0.0, maxTexCoordBorderY);
glVertex2i(0, 0 + m_borderWidth);
// Top left
glTexCoord2f(0.0, 0.0);
glVertex2i(0, 0);
// Top right
glTexCoord2f(maxTexCoordBorderX, 0.0);
glVertex2i(0 + m_borderWidth, 0);
// Bottom right
glTexCoord2f(maxTexCoordBorderX, maxTexCoordBorderY);
glVertex2i(0 + m_borderWidth, 0 + m_borderWidth);
glEnd();
// Top middle quad [2]
glBegin(GL_QUADS);
// Bottom left
glTexCoord2f(maxTexCoordBorderX + texelCentersOffsetX, maxTexCoordBorderY);
glVertex2i(0 + m_borderWidth, 0 + m_borderWidth);
// Top left
glTexCoord2f(maxTexCoordBorderX + texelCentersOffsetX, 0.0);
glVertex2i(0 + m_borderWidth, 0);
// Top right
glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX - texelCentersOffsetX, 0.0);
glVertex2i(0 + width - m_borderWidth, 0);
// Bottom right
glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX - texelCentersOffsetX, maxTexCoordBorderY);
glVertex2i(0 + width - m_borderWidth, 0 + m_borderWidth);
glEnd();
// Top right quad [3]
glBegin(GL_QUADS);
// Bottom left
glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX, maxTexCoordBorderY);
glVertex2i(0 + width - m_borderWidth, 0 + m_borderWidth);
// Top left
glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX, 0.0);
glVertex2i(0 + width - m_borderWidth, 0);
// Top right
glTexCoord2f(1.0, 0.0);
glVertex2i(0 + width, 0);
// Bottom right
glTexCoord2f(1.0, maxTexCoordBorderY);
glVertex2i(0 + width, 0 + m_borderWidth);
glEnd();
// Middle left quad [4]
glBegin(GL_QUADS);
// Bottom left
glTexCoord2f(0.0, (GLfloat)1.0 - maxTexCoordBorderY);
glVertex2i(0, 0 + height - m_borderWidth);
// Top left
glTexCoord2f(0.0, maxTexCoordBorderY);
glVertex2i(0, 0 + m_borderWidth);
// Top right
glTexCoord2f(maxTexCoordBorderX, maxTexCoordBorderY);
glVertex2i(0 + m_borderWidth, 0 + m_borderWidth);
// Bottom right
glTexCoord2f(maxTexCoordBorderX, (GLfloat)1.0 - maxTexCoordBorderY);
glVertex2i(0 + m_borderWidth, 0 + height - m_borderWidth);
glEnd();
// Middle right quad [5]
glBegin(GL_QUADS);
// Bottom left
glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX, (GLfloat)1.0 - maxTexCoordBorderY);
glVertex2i(0 + width - m_borderWidth, 0 + height - m_borderWidth);
// Top left
glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX, maxTexCoordBorderY);
glVertex2i(0 + width - m_borderWidth, 0 + m_borderWidth);
// Top right
glTexCoord2f(1.0, maxTexCoordBorderY);
glVertex2i(0 + width, 0 + m_borderWidth);
// Bottom right
glTexCoord2f(1.0, (GLfloat)1.0 - maxTexCoordBorderY);
glVertex2i(0 + width, 0 + height - m_borderWidth);
glEnd();
// Bottom left quad [6]
glBegin(GL_QUADS);
// Bottom left
glTexCoord2f(0.0f, 1.0);
glVertex2i(0, 0 + height);
// Top left
glTexCoord2f(0.0f, (GLfloat)1.0 - maxTexCoordBorderY);
glVertex2i(0, 0 + height - m_borderWidth);
// Top right
glTexCoord2f(maxTexCoordBorderX, (GLfloat)1.0 - maxTexCoordBorderY);
glVertex2i(0 + m_borderWidth, 0 + height - m_borderWidth);
// Bottom right
glTexCoord2f(maxTexCoordBorderX, 1.0);
glVertex2i(0 + m_borderWidth, 0 + height);
glEnd();
// Bottom middle quad [7]
glBegin(GL_QUADS);
// Bottom left
glTexCoord2f(maxTexCoordBorderX + texelCentersOffsetX, 1.0);
glVertex2i(0 + m_borderWidth, 0 + height);
// Top left
glTexCoord2f(maxTexCoordBorderX + texelCentersOffsetX, (GLfloat)1.0 - maxTexCoordBorderY);
glVertex2i(0 + m_borderWidth, 0 + height - m_borderWidth);
// Top right
glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX - texelCentersOffsetX, (GLfloat)1.0 - maxTexCoordBorderY);
glVertex2i(0 + width - m_borderWidth, 0 + height - m_borderWidth);
// Bottom right
glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX - texelCentersOffsetX, 1.0);
glVertex2i(0 + width - m_borderWidth, 0 + height);
glEnd();
// Bottom right quad [8]
glBegin(GL_QUADS);
// Bottom left
glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX, 1.0);
glVertex2i(0 + width - m_borderWidth, 0 + height);
// Top left
glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX, (GLfloat)1.0 - maxTexCoordBorderY);
glVertex2i(0 + width - m_borderWidth, 0 + height - m_borderWidth);
// Top right
glTexCoord2f(1.0, (GLfloat)1.0 - maxTexCoordBorderY);
glVertex2i(0 + width, 0 + height - m_borderWidth);
// Bottom right
glTexCoord2f(1.0, 1.0);
glVertex2i(0 + width, 0 + height);
glEnd();
GLfloat xTexOffset;
GLfloat yTexOffset;
if(m_borderWidth > 0)
{
glBindTexture(GL_TEXTURE_2D, m_centerTexture); // If there's a border, we have to use
// second texture now for middle quad
xTexOffset = 0.0; // We are using another texture, so middle middle quad
yTexOffset = 0.0; // has to be texture with a whole texture
}
else
{
// Don't bind any texture here - we're still using the same one
xTexOffset = maxTexCoordBorderX; // But it implies using offset which equals
yTexOffset = maxTexCoordBorderY; // maximum texture coordinates
}
// Middle middle quad [9]
glBegin(GL_QUADS);
// Bottom left
glTexCoord2f(xTexOffset, maxTexCoordHeight - yTexOffset);
glVertex2i(0 + m_borderWidth, 0 + height - m_borderWidth);
// Top left
glTexCoord2f(xTexOffset, yTexOffset);
glVertex2i(0 + m_borderWidth, 0 + m_borderWidth);
// Top right
glTexCoord2f(maxTexCoordWidth - xTexOffset, yTexOffset);
glVertex2i(0 + width - m_borderWidth, 0 + m_borderWidth);
// Bottom right
glTexCoord2f(maxTexCoordWidth - xTexOffset, maxTexCoordHeight - yTexOffset);
glVertex2i(0 + width - m_borderWidth, 0 + height - m_borderWidth);
glEnd();
glEndList();
}
glCallList(m_displayListId); // Now we can call earlier or now created display list
glPopMatrix();
return true;
}
Probabilmente c'è troppo di codice di lì, ma ho voluto mostrare tutto. La cosa principale di questa versione è l'uso di liste di visualizzazione e glVertex2i
che sono deprecate.
Ho pensato che il problema di tale rallentamento fosse l'uso di questo metodo obsoleto che ho letto è piuttosto lento, quindi ho deciso di andare su VBO
. Ho usato this tutorial e in base ad esso ho cambiato metodo come questo:
bool Background::draw(const TPoint& pos, int width, int height)
{
if(width <= 0 || height <= 0)
return false;
glPushMatrix();
glTranslatef((GLfloat)pos.x, (GLfloat)pos.y, 0.0f); // Move background to right direction
if((width != m_savedWidth) || (height != m_savedHeight)) // If size to draw is different than the one saved in display list,
// then recalculate everything and save in display list
{
// That size will be now saved in display list
m_savedWidth = width;
m_savedHeight = height;
GLfloat texelCentersOffsetX = (GLfloat)1/(2*m_width);
// Instead of coordinates range 0..1 we need to specify new ones
GLfloat maxTexCoordWidth = m_bTiling ? (GLfloat)width/m_width : 1.0;
GLfloat maxTexCoordHeight = m_bTiling ? (GLfloat)height/m_height : 1.0;
GLfloat maxTexCoordBorderX = (GLfloat)m_borderWidth/m_width;
GLfloat maxTexCoordBorderY = (GLfloat)m_borderWidth/m_height;
/* 9-cell-pattern, each number represents one quad
-------------------
| 1 | 2 | 3 |
-------------------
| | | |
| 4 | 9 | 5 |
| | | |
-------------------
| 6 | 7 | 8 |
-------------------
*/
/* How vertices are distributed on one quad made of two triangles
v1 ------ v0
| /|
| / |
|/ |
v2 ------ v3
*/
GLfloat vertices[] = {
// Top left quad [1]
m_borderWidth, 0, 0, // v0
0, 0, 0, // v1
0, m_borderWidth, 0, // v2
0, m_borderWidth, 0, // v2
m_borderWidth, m_borderWidth, 0, // v3
m_borderWidth, 0, 0, // v0
// Top middle quad [2]
width-m_borderWidth, 0, 0, // v0
m_borderWidth, 0, 0, // v1
m_borderWidth, m_borderWidth, 0, // v2
m_borderWidth, m_borderWidth, 0, // v2
width-m_borderWidth, m_borderWidth, 0, // v3
width-m_borderWidth, 0, 0, // v0
// Top right quad [3]
width, 0, 0, // v0
width-m_borderWidth, 0, 0, // v1
width-m_borderWidth, m_borderWidth, 0, // v2
width-m_borderWidth, m_borderWidth, 0, // v2
width, m_borderWidth, 0, // v3
width, 0, 0, // v0
// Middle left quad [4]
m_borderWidth, m_borderWidth, 0, // v0
0, m_borderWidth, 0, // v1
0, height-m_borderWidth, 0, // v2
0, height-m_borderWidth, 0, // v2
m_borderWidth, height-m_borderWidth, 0, // v3
m_borderWidth, m_borderWidth, 0, // v0
// Middle right quad [5]
width, m_borderWidth, 0, // v0
width-m_borderWidth, m_borderWidth, 0, // v1
width-m_borderWidth, height-m_borderWidth, 0, // v2
width-m_borderWidth, height-m_borderWidth, 0, // v2
width, height-m_borderWidth, 0, // v3
width, m_borderWidth, 0, // v0
// Bottom left quad [6]
m_borderWidth, height-m_borderWidth, 0, // v0
0, height-m_borderWidth, 0, // v1
0, height, 0, // v2
0, height, 0, // v2
m_borderWidth, height, 0, // v3
m_borderWidth, height-m_borderWidth, 0, // v0
// Bottom middle quad [7]
width-m_borderWidth, height-m_borderWidth, 0, // v0
m_borderWidth, height-m_borderWidth, 0, // v1
m_borderWidth, height, 0, // v2
m_borderWidth, height, 0, // v2
width-m_borderWidth, height, 0, // v3
width-m_borderWidth, height-m_borderWidth, 0, // v0
// Bottom right quad [8]
width, height-m_borderWidth, 0, // v0
width-m_borderWidth, height-m_borderWidth, 0, // v1
width-m_borderWidth, height, 0, // v2
width-m_borderWidth, height, 0, // v2
width, height, 0, // v3
width, height-m_borderWidth, 0, // v0
// Middle middle quad [9]
width-m_borderWidth, m_borderWidth, 0, // v0
m_borderWidth, m_borderWidth, 0, // v1
m_borderWidth, height-m_borderWidth, 0, // v2
m_borderWidth, height-m_borderWidth, 0, // v2
width-m_borderWidth, height-m_borderWidth, 0, // v3
width-m_borderWidth, m_borderWidth, 0 // v0
};
copy(vertices, vertices + 162, m_vCoords); // 162, because we have 162 coordinates
int dataSize = 162 * sizeof(GLfloat);
m_vboId = createVBO(m_vCoords, dataSize);
}
// bind VBOs for vertex array
glBindBufferARB(GL_ARRAY_BUFFER_ARB, m_vboId); // for vertex coordinates
glEnableClientState(GL_VERTEX_ARRAY); // activate vertex coords array
glVertexPointer(3, GL_FLOAT, 0, 0);
glDrawArrays(GL_TRIANGLES, 0, 162);
glDisableClientState(GL_VERTEX_ARRAY); // deactivate vertex array
// bind with 0, so, switch back to normal pointer operation
glBindBufferARB(GL_ARRAY_BUFFER_ARB, NO_VBO_ID);
glPopMatrix();
return true;
}
E 'molto simile alla versione precedente, ma invece di glDisplayList
e glVertex2i()
ho usato VBO
che viene creata con dati memorizzati in un array .
ma i risultati mi ha deluso, perché ho ottenuto calo di prestazioni, invece di spinta, ho avuto a malapena ~260 fps
e devo notare che in questa versione il metodo non ho ancora implementato l'uso di texture, quindi non ci sono solo i quad per ora senza qualsiasi trama associata ad esso.
Ho letto alcuni articoli per trovare quale potrebbe essere il motivo di tale rallentamento e ho scoperto che forse è dovuto a una grande quantità di piccoli VBO
se dovrei probabilmente avere uno VBO
contenente tutti i dati di sfondi invece di separare VBO
per ogni sfondo. Ma il problema è che i Object
possono spostarsi e hanno trame diverse (e l'atlante di texture non è una buona soluzione per me), quindi sarebbe difficile per me aggiornare quelle modifiche per quelle Object
che hanno cambiato il loro stato. Per ora, quando viene modificato Object
s, ho appena ricreato è VBO
e il resto VBO
rimangono intatti.
Quindi la mia domanda è: cosa sto sbagliando?L'uso di un numero più grande (~ 600) di piccoli VBO
s è molto più lento del metodo di disegno obsoleto con glVertex2i
? E quale potrebbe essere - forse non la migliore, ma migliore - soluzione nel mio caso?
Il frame al secondo è una [terribile metrica comparativa] (http://www.mvps.org/directx/articles/fps_versus_frame_time.htm). Passa a (milli-) secondi per fotogramma. – genpfault
@genpfault Grazie, è sicuramente bello saperlo. Resta il fatto che VBO è più lento in quel caso, però. –
Alcuni driver sono in realtà molto bravi nella compilazione degli elenchi di visualizzazione. – JasonD