Vertex Arrays mit VBO's sind langsamer ? :(



  • Hi,

    Ich habe ein Problem mit VBO's bei OpenGL. Meine Tileengine besteht aus
    Millionen unterschiedlich großen Quads, welche in einer riesigen Welt angeordnet sind. Die Welt wird dabei in Bildschirmgroße "Renderpages" aufgeteilt und es
    werden dann max. 4 solcher Pages auf einmal gerendert. Es handelt sich hierbei um ca. 5000 Quads. Da ich diese 5000 Quads dynamisch ermittele, hatte ich diese zuerst mit 5000 einzelnen GL_BEGIN / GL_END - Blöcken gerendert. Um ein wenig
    Performance zu bekommen und die CPU zu entlasten, bin ich auf Vertex-Arrays
    umgestiegen. Für die ca. 5000 Quads benutze ich dann ungefähr 50 VA's,
    da ich unterschiedliche Texturen und auch Layer mit beachten muss.
    Tatsächlich, obwohl meine Daten dynamisch sind, bringen mir die VA's guten
    Schub und ich habe so 190FPS beim Rendern der quads. Nun habe ich gelesen, dass
    Vertex-Arrays in Verbindung mit VBO's auch noch etwas bringen können, schlechtestenfalls gleichschnell sind. Das ist aber bei mir scheinbar nicht der Fall. Ich habe jetzt auf VBO's umgestellt und meine FPS sinkt von 190FPS auf
    160 FPS 😞 Ich kann einfach nicht herausfinden, wo hier mein Flaschenhals ist.
    Klar, VBO's sollten für statische Daten benutzt werden, aber ich habe gelesen, dass selbst für dynamische Daten diese trotzdem etwas mehr bringen können.

    Vielleicht wisst ihr was ich da falsch mache, sitze schon seit 2 Tagen an dem Problem 😞

    Bei der Version ohne VBO's lade ich alle zu rendernen Tiles in ein paar VA's
    und schicke diese abschließend mit glDrawArrays raus.

    Hier die wichtigen Stellen:

    void Renderer::buildWorld()
    {
    
    	this->numberOfStoragesPerLayer = 1;
    	this->vertexArray = new VertexArray[this->numberOfStoragesPerLayer];
    
    	for (int z=0; z < this->numberOfStoragesPerLayer; z++)
    	{
    		this->vertexArray[z].setSizeOfVertexArray(2000 * 12);
    		this->vertexArray[z].setSizeOfColorArray(2000 *16);
    		this->vertexArray[z].setSizeOfTexCoordArray(2000 *8);
    	}
    
    	glEnableClientState(GL_VERTEX_ARRAY);
    	glEnableClientState(GL_COLOR_ARRAY);
    	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    }
    
    void Renderer::renderWorld(int *camX, int *camY, unsigned char layer)
    {
    	if ((*camX) < 0) (*camX) = 0;
    	if ((*camY) < 0) (*camY) = 0;
    
    	if ((*camX) > (int)(this->worldWidth - this->screenWidth))
    		(*camX) = (int)(this->worldWidth - this->screenWidth);
    
    	if ((*camY) > (int)(this->worldHeight - this->screenHeight)) 
    		(*camY) = (int)(this->worldHeight - this->screenHeight);
    
    	int actScreenX = (*camX) / (int)this->screenWidth;
    	int actScreenY = (*camY) / (int)this->screenHeight;
    
    	int screenX[4];
    	int screenY[4];
    	memset(screenX, -1, sizeof(char) * 4);
    	memset(screenY, -1, sizeof(char) * 4);
    
    	screenX[0] = actScreenX;
    	screenY[0] = actScreenY;
    	screenX[2] = actScreenX;
    	screenY[1] = actScreenY;
    
    	if (actScreenX < (this->numberOfRenderPagesX-1))
    	{
    		screenX[1] = actScreenX+1;
    		screenX[3] = actScreenX+1;
    	}
    
    	if (actScreenY < (this->numberOfRenderPagesY-1))
    	{
    		screenY[2] = actScreenY+1;
    		screenY[3] = actScreenY+1;
    	}
    
    	//VertexarrayCounter zurücksetzen
    	for (int i = 0; i < this->numberOfStoragesPerLayer; i++)
    	{
    		this->vertexArray[i].v = 0;
    		this->vertexArray[i].c = 0;
    		this->vertexArray[i].t = 0;		
    	}
    
    	//Vertexarrays füllen
    	for (int i=0; i < 4;i++)
    	{
    		if (screenX[i] == -1 || screenY[i] == -1)
    			continue;
    
    		vector<Tile*> * tiles = this->renderPages[screenX[i]][screenY[i]].getTiles(layer);
    
    		for (vector<Tile*>::iterator tilesIter=tiles->begin();  tilesIter < tiles->end(); tilesIter++)
    		{
    			float x				= (*tilesIter)->x -(*camX);
    			float y				= (*tilesIter)->y -(*camY);
    			float width			= (*tilesIter)->tilePaletteEntry->width;
    			float height			= (*tilesIter)->tilePaletteEntry->height;
    
    			float rectAX1 = x;
    			float rectAX2 = x+width;
    			float rectAY1 = y;
    			float rectAY2 = y+height;
    
    			float rectBX1 = 0.0f;
    			float rectBX2 = this->screenWidth;
    			float rectBY1 = 0.0f;
    			float rectBY2 = this->screenHeight;
    
    			if (!(rectAX2 >=  rectBX1 &&
    				rectAX1 <=  rectBX2 &&
    				rectAY2 >=  rectBY1 &&
    				rectAY1 <=  rectBY2))
    				continue;
    
    			if ((*tilesIter)->sharedTile > 0 && i>0)
    			{
    				int rpX = (int)((*tilesIter)->x / this->screenWidth); 
    				int rpY = (int)((*tilesIter)->y / this->screenHeight);
    				int isOriginOnThisPage = 0;
    
    				if (screenX[i] == rpX && screenY[i] == rpY)
    					isOriginOnThisPage = 1;
    
    				int sharedWithRight			 = 1;
    				int sharedWithBottom		 = 2;
    				int sharedWithRightAndBottom = 3;
    
    				//Renderpage Rechts oben
    				if (i==1)
    				{
    					if ((*tilesIter)->sharedTile == sharedWithRight && isOriginOnThisPage == 0)
    						continue;
    
    					if ((*tilesIter)->sharedTile == sharedWithRightAndBottom && isOriginOnThisPage == 0)
    						continue;
    				}
    
    				//Renderpage Links oben
    				if (i==2)
    				{
    					if ((*tilesIter)->sharedTile == sharedWithBottom && isOriginOnThisPage == 0)
    						continue;
    
    					if ((*tilesIter)->sharedTile == sharedWithRightAndBottom && isOriginOnThisPage == 0)
    						continue;
    				}
    
    				//Renderpage Rechts unten
    				if (i==3)
    				{
    					if ((*tilesIter)->sharedTile == sharedWithRight && isOriginOnThisPage == 0)
    						continue;
    
    					if ((*tilesIter)->sharedTile == sharedWithBottom && isOriginOnThisPage == 0)
    						continue;
    
    					if ((*tilesIter)->sharedTile == sharedWithRightAndBottom && isOriginOnThisPage == 0)
    						continue;
    				}
    			}
    
    			float colorRed		= (*tilesIter)->tilePaletteEntry->rgbaColor.red;
    			float colorGreen	= (*tilesIter)->tilePaletteEntry->rgbaColor.green;
    			float colorBlue		= (*tilesIter)->tilePaletteEntry->rgbaColor.blue;
    			float colorAlpha	= (*tilesIter)->tilePaletteEntry->rgbaColor.alpha;
    			float texLeft		= (*tilesIter)->tilePaletteEntry->texLeft;
    			float texTop		= (*tilesIter)->tilePaletteEntry->texTop;
    			float texRight		= (*tilesIter)->tilePaletteEntry->texRight;
    			float texBottom		= (*tilesIter)->tilePaletteEntry->texBottom;
    			int storage			= (*tilesIter)->tilePaletteEntry->storagePos;
    			float l				= -(float)layer;
    
    			int v = this->vertexArray[storage].v;
    			int c = this->vertexArray[storage].c;
    			int t = this->vertexArray[storage].t;
    
    			//Nun Kandidaten füllen
    			this->vertexArray[storage].vertices[v] = x;
    			this->vertexArray[storage].vertices[++v] = y;
    			this->vertexArray[storage].vertices[++v] = l;
    			this->vertexArray[storage].vertices[++v] = x;
    			this->vertexArray[storage].vertices[++v] = y + height;
    			this->vertexArray[storage].vertices[++v] = l;
    			this->vertexArray[storage].vertices[++v] = x + width;
    			this->vertexArray[storage].vertices[++v] = y + height;
    			this->vertexArray[storage].vertices[++v] = l;
    			this->vertexArray[storage].vertices[++v]  = x + width;
    			this->vertexArray[storage].vertices[++v] = y;
    			this->vertexArray[storage].vertices[++v] = l;
    			this->vertexArray[storage].colors[c]   = colorRed;
    			this->vertexArray[storage].colors[++c] = colorGreen;
    			this->vertexArray[storage].colors[++c] = colorBlue;
    			this->vertexArray[storage].colors[++c] = colorAlpha;
    			this->vertexArray[storage].colors[++c] = colorRed;
    			this->vertexArray[storage].colors[++c] = colorGreen;
    			this->vertexArray[storage].colors[++c] = colorBlue;
    			this->vertexArray[storage].colors[++c] = colorAlpha;
    			this->vertexArray[storage].colors[++c]  = colorRed;
    			this->vertexArray[storage].colors[++c]  = colorGreen;
    			this->vertexArray[storage].colors[++c] = colorBlue;
    			this->vertexArray[storage].colors[++c] = colorAlpha;
    			this->vertexArray[storage].colors[++c] = colorRed;
    			this->vertexArray[storage].colors[++c] = colorGreen;
    			this->vertexArray[storage].colors[++c] = colorBlue;
    			this->vertexArray[storage].colors[++c] = colorAlpha;
    			this->vertexArray[storage].texCoords[t]   = texLeft;
    			this->vertexArray[storage].texCoords[++t] = texTop;
    			this->vertexArray[storage].texCoords[++t] = texLeft;
    			this->vertexArray[storage].texCoords[++t] = texBottom;
    			this->vertexArray[storage].texCoords[++t] = texRight;
    			this->vertexArray[storage].texCoords[++t] = texBottom;
    			this->vertexArray[storage].texCoords[++t] = texRight;
    			this->vertexArray[storage].texCoords[++t] = texTop;
    			this->vertexArray[storage].v += 12;
    			this->vertexArray[storage].c += 16;
    			this->vertexArray[storage].t += 8;
    		}
    	}
    
    	//Vertexarrays zeichnen
    	for (int i = 0; i < this->numberOfStoragesPerLayer; i++)
    	{
    		if (this->vertexArray[i].v == 0)
    			continue;
    
    		this->textureManager->activateTexture(this->vertexArray[i].storage);
    
    		glColorPointer(4,GL_FLOAT,0,this->vertexArray[i].colors);
    		glTexCoordPointer(2,GL_FLOAT,0,this->vertexArray[i].texCoords);
    		glVertexPointer(3,GL_FLOAT,0,this->vertexArray[i].vertices);
    		glDrawArrays(GL_QUADS,0, (this->vertexArray[i].v / 3));
    	}
    }
    

    Bei der VBO-Version mache ich das gleiche, schreibe aber die Daten
    direkt in den VBO und rufe dann glDrawArrays auf. Habe das ganze auch schon
    mit glBufferSubData probiert. Keinerlei Veränderung. Habe auch mal gelesen,
    dass wenn man den Speicher falsch anspricht, es zu Performanceinbußen bei
    VBO's kommen kann. Aber da finde ich keinen Fehler.. 😞
    Hier die wichtigen Stellen der VBO-Version:

    #define BUFFER_OFFSET(bytes) ((GLubyte *)NULL + (bytes))
    
    void Renderer::buildWorld()
    {
    	//Extensions initialisieren
    	GLenum glewOK = glewInit();
    
    	//Testen ob GLEW OK ist
    	if (glewOK != GLEW_OK)
    	{
    		return;
    	}
    
    	this->numberOfStoragesPerLayer = 1;
    
    	glGenBuffers(1, this->vertexBuffer);
    	this->bufferSize = sizeof(float) * 2000 * 36 * this->numberOfStoragesPerLayer;
    	glBindBuffer(GL_ARRAY_BUFFER, this->vertexBuffer[0]);
    	glBufferData(GL_ARRAY_BUFFER, bufferSize, NULL, GL_DYNAMIC_DRAW);
    
    	this->vertexArray = new VertexArray[this->numberOfStoragesPerLayer];
    
    	glEnableClientState(GL_VERTEX_ARRAY);
    	glEnableClientState(GL_COLOR_ARRAY);
    	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    }
    
    void Renderer::renderWorld(int *camX, int *camY, unsigned char layer)
    {
    	if ((*camX) < 0) (*camX) = 0;
    	if ((*camY) < 0) (*camY) = 0;
    
    	if ((*camX) > (int)(this->worldWidth - this->screenWidth))
    		(*camX) = (int)(this->worldWidth - this->screenWidth);
    
    	if ((*camY) > (int)(this->worldHeight - this->screenHeight)) 
    		(*camY) = (int)(this->worldHeight - this->screenHeight);
    
    	int actScreenX = (*camX) / (int)this->screenWidth;
    	int actScreenY = (*camY) / (int)this->screenHeight;
    
    	int screenX[4];
    	int screenY[4];
    	memset(screenX, -1, sizeof(char) * 4);
    	memset(screenY, -1, sizeof(char) * 4);
    
    	screenX[0] = actScreenX;
    	screenY[0] = actScreenY;
    	screenX[2] = actScreenX;
    	screenY[1] = actScreenY;
    
    	if (actScreenX < (this->numberOfRenderPagesX-1))
    	{
    		screenX[1] = actScreenX+1;
    		screenX[3] = actScreenX+1;
    	}
    
    	if (actScreenY < (this->numberOfRenderPagesY-1))
    	{
    		screenY[2] = actScreenY+1;
    		screenY[3] = actScreenY+1;
    	}
    
    	//VertexarrayCounter zurücksetzen
    	for (int i = 0; i < this->numberOfStoragesPerLayer; i++)
    	{
    		this->vertexArray[i].v = 0;
    	}
    
    	//VertexBuffer-Pointer holen
    	float * bufferPtr;
    	bufferPtr = (float*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
    
    	//Vertexarrays füllen
    	for (int i=0; i < 4;i++)
    	{
    		if (screenX[i] == -1 || screenY[i] == -1)
    			continue;
    
    		vector<Tile*> * tiles = this->renderPages[screenX[i]][screenY[i]].getTiles(layer);
    
    		for (vector<Tile*>::iterator tilesIter=tiles->begin();  tilesIter < tiles->end(); tilesIter++)
    		{
    			float x				= (*tilesIter)->x -(*camX);
    			float y				= (*tilesIter)->y -(*camY);
    			float width			= (*tilesIter)->tilePaletteEntry->width;
    			float height		= (*tilesIter)->tilePaletteEntry->height;
    
    			float rectAX1 = x;
    			float rectAX2 = x+width;
    			float rectAY1 = y;
    			float rectAY2 = y+height;
    
    			float rectBX1 = 0.0f;
    			float rectBX2 = this->screenWidth;
    			float rectBY1 = 0.0f;
    			float rectBY2 = this->screenHeight;
    
    			if (!(rectAX2 >=  rectBX1 &&
    				rectAX1 <=  rectBX2 &&
    				rectAY2 >=  rectBY1 &&
    				rectAY1 <=  rectBY2))
    				continue;
    
    			if ((*tilesIter)->sharedTile > 0 && i>0)
    			{
    				int rpX = (int)((*tilesIter)->x / this->screenWidth); 
    				int rpY = (int)((*tilesIter)->y / this->screenHeight);
    				int isOriginOnThisPage = 0;
    
    				if (screenX[i] == rpX && screenY[i] == rpY)
    					isOriginOnThisPage = 1;
    
    				int sharedWithRight			 = 1;
    				int sharedWithBottom		 = 2;
    				int sharedWithRightAndBottom = 3;
    
    				//Renderpage Rechts oben
    				if (i==1)
    				{
    					if ((*tilesIter)->sharedTile == sharedWithRight && isOriginOnThisPage == 0)
    						continue;
    
    					if ((*tilesIter)->sharedTile == sharedWithRightAndBottom && isOriginOnThisPage == 0)
    						continue;
    				}
    
    				//Renderpage Links oben
    				if (i==2)
    				{
    					if ((*tilesIter)->sharedTile == sharedWithBottom && isOriginOnThisPage == 0)
    						continue;
    
    					if ((*tilesIter)->sharedTile == sharedWithRightAndBottom && isOriginOnThisPage == 0)
    						continue;
    				}
    
    				//Renderpage Rechts unten
    				if (i==3)
    				{
    					if ((*tilesIter)->sharedTile == sharedWithRight && isOriginOnThisPage == 0)
    						continue;
    
    					if ((*tilesIter)->sharedTile == sharedWithBottom && isOriginOnThisPage == 0)
    						continue;
    
    					if ((*tilesIter)->sharedTile == sharedWithRightAndBottom && isOriginOnThisPage == 0)
    						continue;
    				}
    			}
    
    			float colorRed		= (*tilesIter)->tilePaletteEntry->rgbaColor.red;
    			float colorGreen	= (*tilesIter)->tilePaletteEntry->rgbaColor.green;
    			float colorBlue		= (*tilesIter)->tilePaletteEntry->rgbaColor.blue;
    			float colorAlpha	= (*tilesIter)->tilePaletteEntry->rgbaColor.alpha;
    			float texLeft		= (*tilesIter)->tilePaletteEntry->texLeft;
    			float texTop		= (*tilesIter)->tilePaletteEntry->texTop;
    			float texRight		= (*tilesIter)->tilePaletteEntry->texRight;
    			float texBottom		= (*tilesIter)->tilePaletteEntry->texBottom;
    			int storage			= (*tilesIter)->tilePaletteEntry->storagePos;
    			float l				= -(float)layer;
    
    			int storageOffset = (storage * 2000 * 36);
    			int tileIndex = storageOffset + (this->vertexArray[storage].v * 36);
    
    			//Nun Kandidaten füllen
    			//1 Vertice
    			bufferPtr[tileIndex]		= x;
    			bufferPtr[tileIndex + 1]	= y;
    			bufferPtr[tileIndex + 2]	= l;
    			bufferPtr[tileIndex + 3]	= colorRed;
    			bufferPtr[tileIndex + 4]	= colorGreen;
    			bufferPtr[tileIndex + 5]	= colorBlue;
    			bufferPtr[tileIndex + 6]	= colorAlpha;
    			bufferPtr[tileIndex + 7]    = texLeft;
    			bufferPtr[tileIndex + 8]	= texTop;
    
    			//2 Vertice
    			bufferPtr[tileIndex + 9]	= x;
    			bufferPtr[tileIndex + 10]	= y + height;
    			bufferPtr[tileIndex + 11]	= l;
    			bufferPtr[tileIndex + 12]	= colorRed;
    			bufferPtr[tileIndex + 13]	= colorGreen;
    			bufferPtr[tileIndex + 14]	= colorBlue;
    			bufferPtr[tileIndex + 15]	= colorAlpha;
    			bufferPtr[tileIndex + 16]	= texLeft;
    			bufferPtr[tileIndex + 17]	= texBottom;
    
    			//3 Vertice
    			bufferPtr[tileIndex + 18]	= x + width;
    			bufferPtr[tileIndex + 19]	= y + height;
    			bufferPtr[tileIndex + 20]	= l;
    			bufferPtr[tileIndex + 21]   = colorRed;
    			bufferPtr[tileIndex + 22]   = colorGreen;
    			bufferPtr[tileIndex + 23]	= colorBlue;
    			bufferPtr[tileIndex + 24]	= colorAlpha;
    			bufferPtr[tileIndex + 25]	= texRight;
    			bufferPtr[tileIndex + 26]	= texBottom;
    
    			//4 Vertice
    			bufferPtr[tileIndex + 27]	= x + width;
    			bufferPtr[tileIndex + 28]	= y;
    			bufferPtr[tileIndex + 29]	= l;
    			bufferPtr[tileIndex + 30]	= colorRed;
    			bufferPtr[tileIndex + 31]	= colorGreen;
    			bufferPtr[tileIndex + 32]	= colorBlue;
    			bufferPtr[tileIndex + 33]   = colorAlpha;					
    			bufferPtr[tileIndex + 34]	= texRight;
    			bufferPtr[tileIndex + 35]	= texTop;
    
    			this->vertexArray[storage].v ++;
    		}
    	}
    	glUnmapBuffer(GL_ARRAY_BUFFER);
    
    	//Vertexarrays zeichnen
    	for (int i = 0; i < this->numberOfStoragesPerLayer; i++)
    	{
    		if (this->vertexArray[i].v == 0)
    			continue;
    
    		this->textureManager->activateTexture(this->vertexArray[i].storage);
    
    		int storageOffset = (i * 2000 * 36 * sizeof(float));
    
    		glColorPointer(4,GL_FLOAT,9*sizeof(float),BUFFER_OFFSET(storageOffset+3*sizeof(float)));
    		glTexCoordPointer(2,GL_FLOAT,9*sizeof(float),BUFFER_OFFSET(storageOffset+7*sizeof(float)));
    		glVertexPointer(3,GL_FLOAT,9*sizeof(float),BUFFER_OFFSET(storageOffset));
    		glDrawArrays(GL_QUADS,0, this->vertexArray[i].v*4);
    	}
    }
    

    Wäre toll wenn mir jemand da Helfen könnte 😋

    Danke und Gruß

    fbrjogl

    ZusatzInfos:

    Verwendet:

    C++, OpenGL, GLFW, GLEW, Geforce 8600 GT, Windows Vista, Core 2 Quad,



  • Du solltest erstmal herausfinden was genau langsamer geworden ist.
    Ohne mir Deinen Code jetzt im Detail angucken zu wollen, waere der erste Vedaechtige das Beschreiben des VBOs:

    bufferPtr[tileIndex]        = x;
                bufferPtr[tileIndex + 1]    = y;
                bufferPtr[tileIndex + 2]    = l;
                bufferPtr[tileIndex + 3]    = colorRed;
                bufferPtr[tileIndex + 4]    = colorGreen;
                bufferPtr[tileIndex + 5]    = colorBlue;
                bufferPtr[tileIndex + 6]    = colorAlpha;
                bufferPtr[tileIndex + 7]    = texLeft;
                bufferPtr[tileIndex + 8]    = texTop;
               
                //2 Vertice
                bufferPtr[tileIndex + 9]    = x;
                bufferPtr[tileIndex + 10]    = y + height;
                bufferPtr[tileIndex + 11]    = l;
                bufferPtr[tileIndex + 12]    = colorRed;
                bufferPtr[tileIndex + 13]    = colorGreen;
                bufferPtr[tileIndex + 14]    = colorBlue;
                bufferPtr[tileIndex + 15]    = colorAlpha;
                bufferPtr[tileIndex + 16]    = texLeft;
                bufferPtr[tileIndex + 17]    = texBottom;
               
                //3 Vertice
                bufferPtr[tileIndex + 18]    = x + width;
                bufferPtr[tileIndex + 19]    = y + height;
                bufferPtr[tileIndex + 20]    = l;
                bufferPtr[tileIndex + 21]   = colorRed;
                bufferPtr[tileIndex + 22]   = colorGreen;
                bufferPtr[tileIndex + 23]    = colorBlue;
                bufferPtr[tileIndex + 24]    = colorAlpha;
                bufferPtr[tileIndex + 25]    = texRight;
                bufferPtr[tileIndex + 26]    = texBottom;
    
                //4 Vertice
                bufferPtr[tileIndex + 27]    = x + width;
                bufferPtr[tileIndex + 28]    = y;
                bufferPtr[tileIndex + 29]    = l;
                bufferPtr[tileIndex + 30]    = colorRed;
                bufferPtr[tileIndex + 31]    = colorGreen;
                bufferPtr[tileIndex + 32]    = colorBlue;
                bufferPtr[tileIndex + 33]   = colorAlpha;                   
                bufferPtr[tileIndex + 34]    = texRight;
                bufferPtr[tileIndex + 35]    = texTop;
    

    Der VertexBuffer sollte streng linear beschrieben werden.
    Dazu solltest Du sicherstellen, dass der Compiler die Reihenfolge der Schreibzugriffe nicht veraendert (siehe volatile) und "tileIndex" evtl vorher sortieren.

    Da Du anscheindend bei jedem Render-Vorgang alle Vertexdaten erneut in den VBO scheibst, hast Du dadurch effektiv keinen Vorteil.
    Du solltest also versuchen festzustellen, ob sich Daten nicht veraendert haben und Du den VBO somit wiederverwenden kannst.
    Ausserdem solltest Du die Datenmenge reduzieren, zb indem Du Indexbuffer benutzt und die Vertexstruktur klein haelst (muessen die Farben float sein?).
    Aendern sich immer alle Attribute eines Vertex? Falls nicht kann es vorteilhaft sein, unterschiedliche Buffer fuer Position/Farbe/Texturkoordinate anzulegen.



  • Wenn du genau wissen willst, wo der Flaschenhals ist, dann empfehle ich dir einen guten Profiler (z.B CodeAnalyst).



  • Vielen Dank für eure Tipps:

    Das es mit dem linearen Beschreiben ein Problem gibt, würde ich erstmal ausschließen, da ich selbe Ergebnisse mit glBufferSubData bekomme.
    Der Flaschenhals muss woanders liegen. Ich werde mir mal "volatile" dazu ansehen.
    Da die Vertexdaten abhängig von der aktuellen Kamerasicht sind, also voll dynamisch errechnet werden, können sich theoretisch alle Daten ändern. Das mit den floats ist eine gute Idee, 1 Byte pro Farbe würde auch ausreichen 🙂 Was mich halt nur sehr wundert, ist die Tatsache, das die FPS' mit VBO's deutlich in den Keller gehen.
    Ich verstehe, dass bei meiner Vorgehensweise VBO's nicht wirklich mehr bringen müssen, aber sie sollten doch zumindest gleich schnell sein oder ?

    Bei Vertex-array habe ich die Daten im RAM und die müssen dann auch zur Grafikkarte gepumpt werden. Bei VBO's (So habe ich es zumindest verstanden) schreibe ich direkt in den Speicher der Grafikkarte und schicke dann nur noch den glDrawArray()-Befehl rüber. Oder sind hier die Übertragungswege unterschiedlich. Vielleicht liegt es auch daran, das ich schnellen Speicher habe (DDR2 666MHZ) und meine Graka nicht die schnellste ist (Geforce 8600 GT) ?
    CodeAnalyst schaue ich mir mal an. Der Profiler vom Visualstudio zeigt an, das
    die meiste Performance bei irgendeiner nvidia-dll draufgeht..also Treiber vermute ich. Habt ihr sonst noch eine Idee..oder vielleicht sogar Ratschläge, wie man performantes Scrollen mit mehreren Layer (alles nur 2D) alternativ lösen kann, außer mit Renderpages. Eine Tileengine (mit homogenen Tiles) hatte ich auch schon, aber da bin ich nicht flexibel genug finde ich. Ich würde ja alles
    direkt in der Grafikkarte verstauen, aber die Welt soll recht groß sein und möglichst wenig dynamisch nachladen.

    Vielleicht habt ihr da noch ein paar Tipps zu meinem Code oder generelle Ratschläge 🙂

    Vielen Dank schonmal

    Gruß

    FbrJogl



  • Ich denke hier liegt ein grundsaetzliches Missverstaendnis vor.
    Du hast vermutlich irgendwo gelesen, dass VBOs toll und schnell sind und glaubst, dass es schneller sein muesste, die Daten direkt in den Grafikspeicher anstatt zunaechst in den Hauptspeicher zu schreiben.
    Effektiv macht der Treiber mit den Daten im Hauptspeicher aber nichts anderes als sie in einen (internen) Vertexbuffer zu schreiben (also das gleiche wie Du jetzt auch).
    Der Treiber kann das nun offenbar schneller als Du.
    Das liegt einerseits daran, dass der Zwischenstop im Hauptspeicher wegen ueberschaubarer Datenmenge nichts kostet und dass der Treiber die Daten cache-freundlich (Indexing etc) aufbereitet und somit weniger zu uebertragen ist *und* schneller gerendert werden kann.
    Du hast leider einfach an der falschen Stelle "optimiert"...



  • Ok, verstehe 🙂 Dann kann ich da wohl nicht mehr rausholen 😞
    Sind denn ~200 FPS für ca. 5000 Quads (Mit Alphablending) nicht trotzdem noch recht wenig ? Meine CPU ist gerade mal zu 30% ausgelastet, also kommt die Karte irgendwie schon nicht hinterher. Dachte immer es sei meist umgekehrt, das die CPU nicht mit dem schaufeln hinterherkommt ? Und 20000 Vertices sollten eine Geforce 8600 GT
    nicht allzu in Schwitzen bringen oder ?



  • Wenn Deine Quads alle bildschirmfuellend sind, kriegst Du die GPU auch schon mit 100 in die Knie 😉
    Davon abgesehen scheint mir Dein Konzept einfach nicht hardware-nah.
    Falls Du mal erklaeren moechtest, was Du ueberhaupt erreichen willst und was Du dazu genau anstellst, kann man Dir vielleicht konkretere Ratschlaege geben...



  • hi, ok ich erkläre mal kurz was ich mir so vorgestellt habe.
    Und zwar möchte ich in späterer Zukunft (denke so 2-3 Jahre Entwicklungszeit für die Engine)
    anfangen, etwas umfangreichere 2D-Spiele wie Turrican, R-Type, Probotector etc.
    zu entwickeln. Hierfür wollte ich mir erstmal eine eigene 2D-Engine basteln,
    welche von den meisten "hardware-nahen" Operationen abstrahiert und mich voll
    auf das eigentliche Spiel konzentrieren lässt. Um gute Performance und Platformunabhängigkeit
    zu bekommen habe ich mich für OpenGL, GLFW und GLEW entschieden. Für Sound wird dann
    noch FMOd dazukommen. Denke das ist eine gute Kombination. Momentan habe ich auch schon
    ein paar Klassen auf die Beine gestellt:

    TGALoader, Logger, FPSCounter, TextureManager, ConfigFileReader, Renderer

    ..Und genau bei dem Renderer haperts ein bisschen bei mir.

    Meine Engine ist momentan so aufgebaut, das ich ein Objekt von der Engine
    instanziere, dann 3 CallBackfunktionen registriere (Initialisieren, MainLoop, Exit)
    und gleich mit dem Spiel loslegen kann.

    Der TextureManager funktioniert so, das er 1024x1024 große Texturen
    in die Grafikkarte lädt und dann eine Schnittstelle bereitstellt,
    mit der auf Ausschnitte der Textur zugegriffen werden kann.
    Dadurch spare ich mir dann einiges an TexturSwitching.
    Als nächstes kann eine sogenannte TilePalette definiert werden.
    Jedes Tile der Palette besitzt dabei eine Grafik (Zeigt auf das Subtexture)
    des TextureManagers. Mithilfe der Palette können dann beliebig viele
    Tiles erstellt werden und dann in einer großen Welt statisch platziert werden.
    Bei einer 400 Bildschirmgroßen Welt und 2 Millionen darin platzierten Tiles
    verbrauche ich hier "nur" 40-50 MB an RAM. Es sind deshalb so viele Tiles,
    weil diese auch hintereinander angeordnet werden können, sprich es sind
    einige Layer möglich. Da ich nciht alle 2 Millionen Tiles auf einmal rendern
    kann, hatte ich nun die Idee, meine Welt in Renderpages aufzuteilen.
    eine Renderpage ist sogroß wie der Bildschirm und je nachdem wo sich
    der aktuelle Viewport befindet, müssen bis zu 4 Renderpages zum rendern in Betracht
    gezogen werden. Beim rendern werden dann diese 4 Seiten durchgegangen und
    per Bounding-Box verfahren geschaut, was tatsächlich sichtbar auf dem Viewport ist.
    Die ermittelten Tiles werden dann in einige VertexArrays geschrieben.
    Und zwar in x = (AnzahlLayer * Anzahl verschiedene Texturen) verschiedene
    Vertex-Arrays. Das sind bei mir momentan bei 8 Verschiedenen Texturen
    und 6 Layern 48 VertexArrays. So spare ich mir das Sortieren der Layer,
    welches trotz Z-Buffer nötig ist, weil jedes Tile Alphablending unterstützt.
    Habe auch gelesen, das es eventuell hilfreich ist, nicht alle mit Alphablending-Möglichkeiten
    zu versehen, sondern erst einige mit Alpha-Test und dann nur zum Schluss die mit
    Alphablending. Da ich Layerweise rendere, kann ich zwischen den Layern dann
    die dynamischen Objekte rendern. Also quasi verschiedene Backgrounds, Hauptlayer,
    Dynamische Objekte (Player etc.), Frontlayer.

    Das ist so momentan mein Ansatz für den Renderer, aber mittlerweile glaube ich, dass
    es bestimmt bessere Ansätze gibt. Wie sieht es mit Quadtrees aus anstatt Renderpages ?
    Würde sich das lohnen ? Kennt jemand einen Link, wo man sich über das Design
    von einfachen Renderern für Spiele schlau machen kann. Es gibt bestimmt viel
    bessere Ansätze, wie man so ein Projekt strukturieren kann. Vielleicht haben die Erfahrenen
    unter euch da ein paar gute Tipps für do's und dont's 🙂

    Wer mal meinen bisherigen Stand sehen möchte kann sich hier
    die Exe (Für Windows) herunterladen. Sie gibt auch die aktuellen FPS
    in einer Textdatei aus. (Vertex-Array ohne VBO)- Version.
    Vielleicht habt ihr ja viel mehr und mein Vista ist das ganze Prob xD

    http://rapidshare.com/files/119825927/Retro2DEngine.zip

    Gruß

    Fbrjogl



  • Wenn ich Dich richtig verstehe sind die anfangs erwaehnten 5000 Quads dann jene Objekte die aktuell gerade sichtbar auf dem Bildschirm sind?
    Warum musst Du die denn unbedingt bei jedem Frame neu erzeugen?
    Da wird sich von einem Bild zum naechsten doch vermutlich nicht viel dran aendern - und dann macht auch der VBO wieder Sinn...



  • hmm...aber wenn sich meine Kamera nur um eine Pixel verändert..dann ändern sich alle 5000 ja auch um 1 Pixel....vieleicht sollte ich mal mit translate arbeiten..also ein translate vor allen 5000 Quads..anstatt jeden Quad einzeln zu verschieben ? 🙂



  • ein translate vor allen 5000 Quads..anstatt jeden Quad einzeln zu verschieben ?

    Eine hervorragende Idee! 😉



  • wie viel performance bringen vertex arrays?


Log in to reply