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 FPSIch 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 ausreichenWas 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'sWer 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 xDhttp://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?