Registriert: Di Aug 09, 2011 07:33 Beiträge: 163
Programmiersprache: C/C++
Ich wollte auch keinen genauen Wert sondern einfach mal so übern Daumen geschätzt Es sollte doch eig. kein Problem darstellen, mehrere Dinge zu rendern die insgesamt 1Mio Polys/Vertices beinhalten... und da kann ich doch nicht bei 50.000 schon am Limit sein
Wie meinst du das denn genau mit, in der richtigen Rheinfolge in den Speicher packen und dann noch die Displaylist drüberpacken?!
Registriert: Di Mai 18, 2004 16:45 Beiträge: 2623 Wohnort: Berlin
Programmiersprache: Go, C/C++
Das geht in Optimierung, da muss ich ein bisschen ausholen.
Beim rendern einer Szene spielen viele Faktoren rein, von einfachen wie anzahl der Bindings von OpenGL Objekten bis komplexen wie caching misses. Das erste kannst du nur durch gutes sortieren, komplexere buffer, zusammenpacken von ressourcen und einigen tricky techniken verbessern. Jeder Binding Call kostet sehr viel Zeit auf CPU und GPU seite, da beide mit einander Synchronisiert werden müssen aufgrund unterschiedlicher Frequenzen von Grafikkarte und CPU. Jedes Binding bedeutet auch lookups im arbeitsspeicher und lookups im treiber und im vram, um die vram addresse der ressource zu bekommen. Da dies recht teuer ist, geht der Trend mit OpenGL4.2 und höher gegen bindings zu objekt pointer. Die teuren Bindings sind der grund, wieso man gerne texturen zusammen packt, texture-array eingeführt hat und versucht so häufig wie möglich interleaved vbo's zu nutzten.
Das Umfangreichste Problem sind cache misses. Man kann sie nie ganz weg bekommen und egal was man tut, es hat auswirkungen auf die anzahl der Cache misses. Ein Cache miss ist nix anderes als ein zugriff auf Daten, die nicht im Cache vorliegen und deswegen von vram/ram/hdd geladen werden müssen. Es gibt 2 Caches bei GPU und 3 bei CPU Level 1 bis Level 3 welche sich in der Größe und zugriffszeit unterscheiden. Wenn die Render Pipeline sich durch die gegebenen Daten wühlt und die Vertice verarbeitet, dann gibt es für Entwickler 2 Punkte wo ein richtiges Datenmanagment zwischen 100 und 1000FPS entscheidet. Als erstes müssen die Vertice in entsprechende Modelview gebracht werden und alles unnötige aus der Pipeline gekickt. Ein Element dieses Schrittes ist der Vertex Shader. Früher hat man die Vertice, TexCoord, Color und so weiter Informationen getrennt in VBO gelegt, damit der Vertex Shader möglichst viele Vertice aus dem Buffer in den Cache werfen kann und somit die Möglichkeit von misses reduziert. Dann hatte man einfach unmengen an Vertex Prozessor in die gpu gepackt und die caches wurden auch größer, damit wurden misses wesentlich unkritischer. Der 2. wichtige Part ist der Fragment Shader, hier wird der Pixel letztendlich zusammen gerechnet und dafür braucht man ne menge Daten, zum einen die Vertice, Color und TexCoords. Das wird eng im Cache und die Gefahr, das Daten nach geladen werden müssen, um den nächsten Pixel zu berechnen ist hoch. Also versucht man die Daten möglichst so zu packen, dass alles für ein durchlauf, eines Fragment Shaders aneinander liegt. Interleaved VBO ist die Antwort, dabei werden z.B. 3x Float(Vertex), 3x Float (Color) und 2x Float(TexCoord) als Blöcke definiert. Nun hat man 24 Floats, die ein Triangle definieren direkt aneinander und damit keine Probleme, dass das Triangle nicht in den Cache passt. Nun ein Schritt weiter Abstand nehmen. 8 Float bestimmen nun ein Vertex und 24 ein Triangle, schön und gut aber was ist mit ein nächsten Triangle, wenn das seperat daneben gelegt wird haben und dann für das ganze Model machen, dann haben wir schöne für sich selbst stehende Modeldaten, die cach freundlich sind aber ne menge Platz verschwenden, da viele sich Vertice teilen. Also nochmal Caching optimierung durch Indices, hierbei sagt ein weiterer Buffer, mit welchen Offset im Interleave Buffer er das Vertex dieses Triangle findet. Durch die verwendung eines Indice Buffer wird der Speicherverbraucht reduziert, je komplexer das Model wird. Weniger Speicherverbrauch bedeutet automatisch weniger Cache wechsel und das bedeutet mehr Power. Um die höchst mögliche Caching optimierung zu haben, tut man nun die Indices sortieren, möglichst so, dass die Nummern so häufig wie möglich nebeneinander liegen. AMD und Nvidia bieten tools dafür an. Dies sorgt dafür, dass nun viele Fragment Shader durchläufe passieren können, ohne das man auf VRAM, RAM oder SWAP zugreifen muss, ganz gechilt im Cache arbeiten und damit super Performance. Texturen sind das gleiche Spiel, je öfter auf ein Texel zurück gegriffen werden kann, des so schneller, weil das im Cache liegt, also kleine Texturen sind Cachefreundlicher. Das beste was man hier machen kann, ohne auf hohe Texturauflösung zu verzichten sind Texturkompressionen die von der GPU suported werden, also DXT1-5 und die neuen DX11 Formate, die mit OpenGL 4.2 nun auch supported werden. Diese können im Idealfall 8 bzw. in worst case 4x häufiger auf den Cache greifen, da diese eine 8:1 oder 4:1 kompression haben, die Blockartig ist
Caches wo man hin guckt und mit OpenGL3 und dem Debug Context soll es nun möglich sein diese misses zu tracken, ich hab es aber nie selber gemacht, da ich nie so tief optimieren musste.
_________________ "Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren" Benjamin Franklin
Registriert: Di Aug 09, 2011 07:33 Beiträge: 163
Programmiersprache: C/C++
Wow Das nenn ich mal eine gute Erklärung
Hab nun auch mein Problem entdeckt, warum meine Displaylist so langsam war... habe das Texturbinding pro Vertices neu durchgeführt und das sowas schnell Performance frisst ist wohl klar. Aber naja, wenigstens einfach zu fixxen Außerdem sind sowieso VBO's im Einsatz... wollte eher mal was Testen mit der Displaylist.
Jup, gut geschrieben TAK, das könnte eigentlich ins Wiki falls es da nicht schon ist. Eine Frage aber: was meinst du mit SWAP (beim Zugriff auf RAM und VRAM)?
Merkwürdig das dein Treiber das nicht optimiert.
Dachte der würde sowas erkennen wo Befehle überflüssig sind und diese wegoptimieren, schließlich sind die Parameter in Listen unauswechselbar.
Registriert: Di Mai 18, 2004 16:45 Beiträge: 2623 Wohnort: Berlin
Programmiersprache: Go, C/C++
Nehmen wir mal an, du hast 512MB VRAM, 4GB RAM und 4GB "Swap Speicher"/Pagefile/"Virtueller Arbeitsspeicher". Dein Programm startet, deine Graka hält schon einige MB an Speicher für sich, Framebuffer, Backbuffer, Driver Code, der Arbeitsspeicher hat knapp 3GB in nutzung und nun kommt deine super Coole App. Diese verbraucht erstmal 999MB Arbeitsspeicher, lädt nun noch ne menge sachen in den VRam(Texturen, VBO, FBO, ...) und dann kommt noch die Verwaltungsdaten für deine OpenGL Daten oben drauf also sagen wir alles in allem 400MB. Nun lädst du noch eine Textur und dein System sagt, hey VRam voll. Was nun passiert ist in etwa folgendes. 1.)der Speichermanager auf der Graka kickt ein oder mehrere Speicherblöck raus und hinterlegt sie im Arbeitsspeicher 2.)der Speichermanager vom OS merkt, dass er auch voll ist und schiebt ein oder mehrere Speicherblöcke in den SWAP Speicher 3.)der Speichermanager vom OS schiebt die Speicherblöcke von der Graka nun in den Arbeitsspeicher 4.)der Speichermanager von der Graka packt die neue Textur in den VRAM Nun malst du dein Frame und im nächsten Frame brauchst du noch eine andere Textur und das gleiche Spiel passiert nochmal. Diesmal aber brauchst du zum rendern eine Textur, die gerade ausgelagert wurde also passiert folgendes zusätzlich. 1.) bis 3.) von oben 4.)die Textur wird vom SWAP Speicher in den VRAM geschrieben
Das also die benötigten Daten zwischen den verschiedenen Speichern wandern und damit ewig viel Zeit brauchen wird wohl jeder erahnen können. SWAP Speicher ist ein Speicherbereich mapping auf eine Datei auf der Festplatte. Dieser wird nicht im Arbeitsspeicher gecached, wie bei jeder anderen File, weil sonnst der auslagerungsspeicher garnicht notwendig wäre, wenn man diesen im Speicher unterbringen könnte xD Ein mechanischer Festplatten zugriff(SSD hat dieses Problem nicht) ist das teuerste, was man überhaupt an einem PC machen kann, jegliche andere Operation ist schneller, selbst Ping/Pong über Netzwerk ist schneller(okey CD/DVD kann noch langsamer sein). Also SWAP zugriffe machen aus jeder Echtzeit Anwendung eine Slide Show.
_________________ "Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren" Benjamin Franklin
Registriert: Di Aug 09, 2011 07:33 Beiträge: 163
Programmiersprache: C/C++
Muss mal noch was fragen
Ich stelle ja meine ganzen Objekte per glDrawElements da, doch ich habe da etwas merkwürdiges... Wenn ich z.B. 12000 Vertices auf einmal darstellen will per glDrawElements, ist das Objekt entweder total unsichtbar oder es wird nur ein Teil gerendert, lass ich nun aber 12x glDrawElements per for Schleife aufrufen und dann immer nur 1000 abarbeiten, dann wird alles ohne Probleme dargestellt... Ich kann auch keinen Faktor finden, der dafür verantwortlich sein kann... zufällig jemand eine spontane Idee?
Registriert: Di Mai 18, 2004 16:45 Beiträge: 2623 Wohnort: Berlin
Programmiersprache: Go, C/C++
glDrawElements könnte auch falsch aufgerufen worden sein. So dass er mehr Vertice rendert, als in den Buffer liegen, also out of bound, denn geprüft wird das nicht. Was dann passiert ist erst die Vertice zu rendern und dann Speichermüll.
_________________ "Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren" Benjamin Franklin
Mitglieder in diesem Forum: Google [Bot] und 13 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.