Files |  Tutorials |  Articles |  Links |  Home |  Team |  Forum |  Wiki |  Impressum

Aktuelle Zeit: Sa Jun 08, 2024 14:27

Foren-Übersicht » Programmierung » Einsteiger-Fragen
Unbeantwortete Themen | Aktive Themen



Ein neues Thema erstellen Auf das Thema antworten  [ 22 Beiträge ]  Gehe zu Seite 1, 2  Nächste
Autor Nachricht
 Betreff des Beitrags: Zeichnen von Texturen + Performance
BeitragVerfasst: So Nov 10, 2013 12:56 
Offline
DGL Member

Registriert: Di Dez 13, 2011 19:14
Beiträge: 166
Wohnort: Hamburg / Mölln
Programmiersprache: D
Hi. Ich zeichne momentan diverse Texturen mittels glDrawArrays und habe (bei 9 - 10 Texturen und einigen anderen Dingen wie Shapes [was wiederum Vertex Arrays sind und ebenso mit glDrawArrays gezeichnet werden]) eine Framerate von 1200.
Ohne das zeichnen liegt sie bei 1600 - 1700. Jede dieser Grafiken ist 128x128px groß. Ist es also normal, das es 400 - 500 FPS "kostet" um 9 - 10 Texturen zu zeichnen?
Und kann man das nicht irgendwie beschleunigen? Ich dachte an einen VBO, aber für jede Textur wäre das ziemlicher Overkill würde ich vermuten. Danach dachte ich an _einen_ gemeinsamen VBO, den alle Texturen teilen. Aber das stelle ich mir irgendwie kompliziert vor.
Ich hatte auch schon an eine statische Szene gedacht, und den GL Buffer nur noch geswappt, wenn wirklich etwas gezeichnet werden muss. Das brachte mir fast 6000 FPS ein, mit dem Nachteil, dass kein dynamisches Verhalten mehr durchgängig möglich war.

Wie verfährt man am besten/performantesten, um in jedem Frame nur wirklich die Texturen über den Bus zur Graka zu transportieren, die es auch müssen? Wie macht ihr das?


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: So Nov 10, 2013 13:11 
Offline
DGL Member
Benutzeravatar

Registriert: So Sep 26, 2010 12:54
Beiträge: 238
Wohnort: wieder in Berlin
Programmiersprache: Englisch
Du könntest Deine 9-10 Texturen auf einen Atlas packen, dann ist nur noch eine Textur gebunden, ein bissel Mathe für die TexCoords noch dazu...
Alles was nicht sichtbar ist wird cullst Du weg, dann sortierst Du deine Meshes nach Material (Shader) und malst diese dann.

Und so wichtig sind die FPS nicht, eher die Milli/Mikrosekunden um einen draw call durchzuführen.


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: So Nov 10, 2013 13:16 
Offline
DGL Member

Registriert: Di Dez 13, 2011 19:14
Beiträge: 166
Wohnort: Hamburg / Mölln
Programmiersprache: D
Das ganze soll eine Art Framework werden, was bei uns an der Uni mitverwendet wird. Deswegen sollte das irgendwie automatisch geschehen. Wäre so etwas möglich?
Z.B. einen großen VBO für, sagen wir, 2000 Sprites und dort immer reinrendern und diesen in jedem Frame zeichnen. Ich wüsste nur nicht, wie ich "geänderte" Daten dort ersetze...

Das mit den Zeiten ist gut zu wissen, sollte ich auch mal messen. :)


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: So Nov 10, 2013 13:17 
Offline
DGL Member

Registriert: Do Dez 29, 2011 19:40
Beiträge: 421
Wohnort: Deutschland, Bayern
Programmiersprache: C++, C, D, C# VB.Net
Was verstehst du unter Texturenzeichnen?
Auf modernen Grafikkarten ist vorallendingend der Fragmenshader und dia Anzahl Drawcalls ausschlaggebend für die Performance. Wenn du es beschleunigen willst, mach einen Textureatlas oder ein Texturearray für deine Texturen auf, packe alle Geometriedaten in ein VBO und vereinfache den Fragmentshader.

Deine Rechnung mit den FPS ist übrigens falsch.
Du musst bedenken, dass die Frames pro Sekunde der Reziprok der Sekunden pro Frame ist.
Wenn du die Performance annähernd abschätzen willst, musst du natürlich mit den Sekunden pro Frame rechnen. Bei 1200 FPS kostet ein Frame ~0.00083 Sekunden und bei 1600 FPS ~0.000625 Sekunden. In der Darstellung sieht der Unterschied schonmal weit weniger schwerwiegend aus. Bei den "magischen" 60 FPS die man für eine wirklich flüssige Darstellung auf handelsüblichen Monitoren benötigt, beträgt der Framezeitabstand ~0,0167 Sekunden. 0.0167 geteilt durch 0.00083 ergibt, dass du mindestens 20 mal so lange für ein Frame brauchen darfst, bis es in der "kritische Bereich" erreicht wird. In der Praxis darfst du sogar noch mehr als die 20 mal soviel Zeichnen, weil der Bufferswap oder der Bufferclear auch Zeit kostet aber den auch weiterhin nur einmal ausführen wirst. Des Weiteren werden bei steigender Objektezahl logischerweise die einzelnen Objekte kleiner oder außerhalb des Bildschirms liegen was das Fragmentprocessing entlastet und so durch die Geometrie kaum zusätzliche Belastung bringt.


Zuletzt geändert von OpenglerF am So Nov 10, 2013 13:33, insgesamt 1-mal geändert.

Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: So Nov 10, 2013 13:31 
Offline
DGL Member

Registriert: Di Dez 13, 2011 19:14
Beiträge: 166
Wohnort: Hamburg / Mölln
Programmiersprache: D
Gut zu wissen. :)
Wäre dann mein Beispiel mit dem einen großen VBO nicht die Lösung? Ich hätte einen Draw Call für mehrere oder gar alle Objekte. Ich weiß bloß nicht, wie ich geänderte Daten dort ersetze.
Z.B. eben wollte ich nur Teil A einer Textur zeichnen, z.B. {10, 10, 0, 20, 10, 0, 20, 20, 0, 10, 20, 0} und im nächsten Frame will ich (wegen einer gedrückten Taste oder sonst was) auf einmal Teil B zeigen, welcher dann {20, 20, 0, 40, 20, 0, 40, 40, 0, 20, 40, 0} wäre. Wie ersetze ich nun? Oder leere ich den Buffer nach jedem zeichnen und "schreibe" im nächsten erneut hinein?


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: So Nov 10, 2013 13:44 
Offline
DGL Member

Registriert: Do Dez 29, 2011 19:40
Beiträge: 421
Wohnort: Deutschland, Bayern
Programmiersprache: C++, C, D, C# VB.Net
Doch, ein gemeinsames VBO könnte die Sache beschleunigen. Aber das kann eigentlich nicht funktionieren, weil du ja im Moment mit verschiedenen Texturen arbeitest. Dieso müsstest du eben erstmal in einen Atlas oder Array verbinden.

Um die Daten zu ersetzen, mappst du einfach den Buffer und änderst den entsprechenden Bereich.
Wenn du Geometrie zur Laufzeit hinzufügen oder entfernen willst, wird das etwas komplizierter.
Wenn es darauf ankommt, könntest du beispielsweise überlegen den Weg über Transformfeedback und Geometryshader zu gehen.

Bei der Anzahl Vertices bei der annehme das es bei dir geht, kannst du auch einfach den Buffer jeden Frame erneut zusammenbauen. In meiner CPU Partikelengine sind so bis zu 1 000 000 VBO-Vertices "Punkte" möglich.


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: So Nov 10, 2013 13:48 
Offline
DGL Member

Registriert: Di Dez 13, 2011 19:14
Beiträge: 166
Wohnort: Hamburg / Mölln
Programmiersprache: D
Wie gesagt, das ganze soll eine Art Framework sein, also kann ich mich nicht auf die Klugheit der Allgemeinheit verlassen, dass die alle Textur Atlanten bauen. Ist ohne Textur Atlanten keine Verbesserung möglich?

Auch ist mir eben aufgefallen, dass meine Shapes _jeweils_ einen VBO haben. Ist das nicht vllt. auch ein wenig Overkill?


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: So Nov 10, 2013 14:01 
Offline
DGL Member

Registriert: Do Dez 29, 2011 19:40
Beiträge: 421
Wohnort: Deutschland, Bayern
Programmiersprache: C++, C, D, C# VB.Net
Wenn ein Framework werden soll, ist doch ein Textureatlas optimal. Du wirst ja eh ein Texturemanagament haben da kannst du auch gleich Texturatlase zusammenbauen. Genauso mache ich es auch. Ich verwende dafür übrigens eine verbesserte native C++ Portierung dieser Rechteckpacker.

Am Schnellsten wird es einfach mit einer einzigen Textur gehen. Wenn es mehrere Texturen sein müssen wird es viel schwieriger. Es bleibt noch die Möglichkeit die Objekte nach ihrer Textur zu sortieren und in ein gemeinsamen Buffer zu packen und gemeinsam zu rendern. Das ist aber mit möglicherweise transparenten Objekten nicht mehr möglich. Es gibt eine neuere Nvidea-Erweiterung die in die Richtung geht: NV_bindless_texture Damit kann man beliebig viele Texturen mit einen Drawcall zeichnen. Das gibt es aber nur auf neuer Nvidea Hardware und man kann es deshalb höchstens zum Optimieren verwenden aber nicht als Standardmethode für alle Grafikkarten.

Jedes Shape(?) ein eigenes VBO ist, wenn es um Performance geht, kein gute Idee. Wenn die Anzahl Objekte unter 1 000 - 10 000 bleibt, sich das eher nicht entscheidend sein. Wenn du wirklich Performance auch bei vielen Objekten willst, musst du die Objekte zusammenpacken. Möglicherweise kannst du zur Laufzeit abschätzen welche Objekte weitgehend statisch sein könnten und die dann in ein statisches VBO packen, dass du nur selten aktualisiern musst. Bei mir in der GUI sind alle Vertexdaten Größen und Positonsunabhänig, dass heißt, ich muss beim Verschieben und Skalieren keine Vertexbuffer neu zusammenbauen.


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: So Nov 10, 2013 14:13 
Offline
DGL Member
Benutzeravatar

Registriert: So Sep 26, 2010 12:54
Beiträge: 238
Wohnort: wieder in Berlin
Programmiersprache: Englisch
Es gibt auch noch UBOs und Instancing. Für die paar Popeldreiecke, von denen Du immer redest, kann man auch das nutzen, dann wärs nur 1 draw call für alles.

Auf fixe Erweiterungen würde ich mich auch nicht verlassen, entweder Du legst deinen Renderpfad auf NVidia aus, oder auf AMD oder auf beides, oder auf keines und lässt nur ARBs zu.

@Opengler:

Transparente Texturen gehen nach wie vor, genauso wie transparentes Aluminium. Den Atlas einfach mit Alphakanal entsprechend versehen. Ansonsten gibt es eben mehere Atlanten, Atlasse, Atlassi, Atlantissen, Atlantasse :)


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: So Nov 10, 2013 16:41 
Offline
DGL Member

Registriert: Di Dez 13, 2011 19:14
Beiträge: 166
Wohnort: Hamburg / Mölln
Programmiersprache: D
So ich hab mal die VBO's aus jedem Shape ausgebaut.
Jedes Shape hat ein Array aus Pixeln. Ein Pixel sieht wie folgt aus:
Code:
  1. struct Pixel {
  2.     float x, y, z;
  3.     float r, g, b, a;
  4. }

Wie könnte ich nun alle Shape in einem VBO zusammenfassen? Und was viel wichtiger ist: was mache ich wenn eines oder mehrere dieser Shapes sich verändern? Z.B. ich habe zwei Rechtecke und speicher die Daten dieser Shapes. Aber dann verlänger ich von einem von beiden die Koordinaten um aus diesem ein Rechteck zu machen. Oder ich füge zwei Punkt hinzu, um ein Sechseck daraus zu machen. Was dann? Ich kann ja schlecht bestimmen, an welcher Stelle das eine Shape anfängt und das andere aufhört.


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: So Nov 10, 2013 17:12 
Offline
DGL Member

Registriert: Do Dez 29, 2011 19:40
Beiträge: 421
Wohnort: Deutschland, Bayern
Programmiersprache: C++, C, D, C# VB.Net
Zitat:
Transparente Texturen gehen nach wie vor, genauso wie transparentes Aluminium.

Bei transparenter Geometrie ist es bei nicht kommutativen Blending(Wie das normalerweise eingesetze Alphablending) nötig, die Geometry in der Reihenfolge von vorne nach hinten sortiert zu zeichnen. Deshalb ist das Sortieren nach Textur außer in Spezialfällen nicht möglich.

Zitat:
Wie könnte ich nun alle Shape in einem VBO zusammenfassen?

Du machst ein großes VBO das mindesten groß genug ist, um alle Vertices der Teilobjekte zu beinhalten und dann schreibst du die Vertexdaten der Teilobjekte einfach der Reihe nach da rein.

Zitat:
was mache ich wenn eines oder mehrere dieser Shapes sich verändern?

Geometrie in VBOs nur zu ändern ist relativ einfach.
Da musst du den Buffer halt mit "glMapBuffer" mappen und dann die alten Vertices mit den Neuen überschreiben.
Wenn du auch Vertices hinzufügen und entfernen willst, ist es wahrscheinlich am Sinnvollsten, den gesamten Buffer neu zusammenzubauen. Du rufst also "glBufferData" erneut auf gibst dabei auch die neue Größe an. Dann mappst du den Buffer wieder mit "glMapBuffer" und schreibst du Daten aller Shapes rein, die in das VBO rein sollen.

Zitat:
Ich kann ja schlecht bestimmen, an welcher Stelle das eine Shape anfängt und das andere aufhört.

Das musst du selber speichern. Wenn du den Buffer zum Ersten mal zusammenbaust, siehst du ja selber, an welchen Index die Vertices des Shapes beginnen und wieviele du brauchst. Diese Informationen musst du natürlich speichern, für den Fall, dass irgendwann nochmal etwas an dem Buffer verändert werden muss.


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: So Nov 10, 2013 23:02 
Offline
DGL Member
Benutzeravatar

Registriert: So Sep 26, 2010 12:54
Beiträge: 238
Wohnort: wieder in Berlin
Programmiersprache: Englisch
Hmm?

wir reden hier von 4-Ecken und nicht von hochkomplexen meshes.


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Mo Nov 11, 2013 02:20 
Offline
DGL Member

Registriert: Di Dez 13, 2011 19:14
Beiträge: 166
Wohnort: Hamburg / Mölln
Programmiersprache: D
Eine andere Frage die sich mir gerade aufdrängt. Sind Texture Pools möglich? Ich habe z.B. ein Dutzend Texturen und weiß deren Koordinaten (und möglicherweise Ausschnitte). Kann ich irgendwie alle Koordinaten hintereinander wegspeichern, dann (ich weiß nicht wie) alle Texturen binden und dann mit einem einzigen Draw Call alles zeichnen?
Ich wüsste nur nicht, wie man mehrere Texturen auf einmal binden kann. Oder gibt es noch einen anderen Weg? Oder ist so etwas nicht möglich und man müsste alles in eine Texture mappen?


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Mo Nov 11, 2013 09:54 
Offline
DGL Member
Benutzeravatar

Registriert: Mi Nov 30, 2011 21:41
Beiträge: 136
Wohnort: Bad Vilbel
Programmiersprache: Delphi 7
Also mehrere Texturen kann man mit Hilfe von glActiveTexture binden, aber das würde nicht zu dem von dir gewünschten Ergebnis führen, weil damit dann jedes Objekt mit allen Texturen gerendert würde, Da müsstest du dir dann einen komplexen Shader zusammenbasteln damit das doch irgendwie geht, aber viel hättest du da eh nicht von, wenns nicht sogar langsamer ist. (Hinweis: auf vielen Grakas kann man bisher nur maximal 4 Textures gleichzeitig binden)


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Mo Nov 11, 2013 11:49 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2622
Wohnort: Berlin
Programmiersprache: Go, C/C++
Auf modernen Karten ist das synchronisieren von CPU und GPU recht teuer und Texturfetches.
Um texturfetches zu reduzieren muss man die Caches sinnvoll nutzen, damit man aus dem Cache liest, statt aus dem VRAM.
Das kann man durch Texturkompression schaffen, DXT1-5 sind hier gute Lösungen.
Um die Texturbindings zu reduzieren macht es sinn auf Texture-Array(OpenGL 3-4) oder auf ein Textur-Atlas zu setzen.
Um das syncen niedrig zu halten sollte man die Vertexdaten und shader versuchen in wenige großere zu packen und mit neueren Grakas kann man auf OpenGL 3 Extension oder OGL 4 Core Features arbeiten, die erlauben die Pointer von Objekten in einem neuen Objekt zu halten.

Du arbeitest mit so wenig Last, dass so ziemlich alles ein großen Einfluss auf die FPS hat.
So kann z.B. die CPU die GPU aufhalten, da ja glSwapBuffer von der CPU kommt und aud der CPU auch noch die Eventloop und einiges andere Läuft.
Dafür kann man mit einem Software Tripplebuffer abhilfe schaffen, man hat eine Kommando Queue, in den Logik-Threads, ein zwischen Logik-Thread und dem Render Thread und ein weiteren im Render Thread.
Der Render Thread wechselt die 2 Queues, wenn ein Änderung passiert ist und die Logik Threads können den eigenen und die Render Thread Queues swappen.

Logik Thread Render Thread Render Thread
Logik-Render-Queue -> 1Backbuffer-Render-Queue -> 2Backbuffer-Render-Queue(wird solange gerendert, bis 1Backbuffer-Render-Queue sich ändert)

Der Render Thread hat die "2Backbuffer-Render-Queue" abgearbeitet und fest gestellt, dass "1Backbuffer-Render-Queue" sich geändert hat und swapped die beiden.

Logik Thread Render Thread Render Thread
Logik-Render-Queue -> 2Backbuffer-Render-Queue -> 1Backbuffer-Render-Queue(wird solange rendert, bis 2Backbuffer-Render-Queue sich ändert)

Also ein Logik Thread erstellt eine Kommando Queue und tut dann per atomic operation die pointer von dem öffentlichen Render Buffer swappen und entsorgt anschliessend den alten.
Wenn die GPU nicht hinter her kommt, dann wird dies noch mal gemacht, bevor der Render Thread den Backbuffer mit dem Öffentlichen Buffer getauscht hat und rendern konnte. Ein Frameskip ist passiert, statt das warten von CPU auf GPU.
Wenn die GPU schneller ist, dann rendert diese einfach den Backbuffer nochmal, bis der Öffentliche Buffer sich verändert hat und swapped dann diesen. Die GPU muss nicht auf die CPU warten und damit sinkt die FPS nicht.

_________________
"Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren"
Benjamin Franklin

Projekte: https://github.com/tak2004


Nach oben
 Profil  
Mit Zitat antworten  
Beiträge der letzten Zeit anzeigen:  Sortiere nach  
Ein neues Thema erstellen Auf das Thema antworten  [ 22 Beiträge ]  Gehe zu Seite 1, 2  Nächste
Foren-Übersicht » Programmierung » Einsteiger-Fragen


Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 25 Gäste


Du darfst keine neuen Themen in diesem Forum erstellen.
Du darfst keine Antworten zu Themen in diesem Forum erstellen.
Du darfst deine Beiträge in diesem Forum nicht ändern.
Du darfst deine Beiträge in diesem Forum nicht löschen.
Du darfst keine Dateianhänge in diesem Forum erstellen.

Suche nach:
Gehe zu:  
cron
  Powered by phpBB® Forum Software © phpBB Group
Deutsche Übersetzung durch phpBB.de
[ Time : 0.011s | 14 Queries | GZIP : On ]