(gelöst) Glare-Algorithmus

Für Fragen zu Grafik APIs wie DirectX und OpenGL sowie Shaderprogrammierung.
Benutzeravatar
eXile
Establishment
Beiträge: 1136
Registriert: 28.02.2009, 13:27

Re: Glare-Algorithmus

Beitrag von eXile »

Ich hab jetzt überhaupt erst verstanden, was zum Teufel die da gemacht haben:
\($$\begin{align}f_2 &= (f_1 \otimes \mathrm{PSF}_\text{aperture}) \otimes \mathrm{PSF}_\text{eye} \\
&= f_1 \otimes (\mathrm{PSF}_\text{aperture} \otimes \mathrm{PSF}_\text{eye}) \\
&= f_1 \otimes \big((...\!) \ F(\mathrm{PSF}_\text{eye} \cdot E(...\!))\big) \\
&= F^{-1}\big(F(f_1) \cdot F\big((...\!) \ F(\mathrm{PSF}_\text{eye} \cdot E(...\!))\big)\big) \end{align}$$\)
Die Formel zeigt übrigens die komplette Pipeline. Dieser Post wird in den nächsten Stunden noch aktualisiert, ich muss erst einmal feiern, dass ich das rausbekommen habe.
Zuletzt geändert von eXile am 20.04.2012, 04:19, insgesamt 2-mal geändert.
Benutzeravatar
Krishty
Establishment
Beiträge: 8246
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Glare-Algorithmus

Beitrag von Krishty »

Ich unterbreche deine Edits ja nur ungern, aber ich wollte mal eben die ganze Welt wissen lassen, dass die Sum of Scaled Copies alles ruiniert. 32 Kopien übereinander blenden braucht – im Compute-Shader, wohlgemerkt – genau so viel Zeit wie die 20 FFTs und iFFTs, die ich über Szene und Linse jage! Das ist echt der Punkt, wo ich den Glauben verliere.

Zumal der GPU Shader Analyzer ausspuckt, die SSC würde 40 Takte brauchen und die FFT 2000. Aber das Ding ist sowieso total scheiße, weil z.B. ein manueller int-to-float-Cast an einer Stelle, wo der Compiler auch automatisch castet, ausreicht, um die Statistiken mit N/A zu füllen.

Mal ein wenig was zur Performance: Ich kann für die Werte keine Richtigkeit garantieren; ich glaube sogar, dass die Messungenauigkeit zu hoch ist, um überhaupt irgendwas herausdeuten zu können. Das liegt daran, dass ich keine Methode gesehen habe, CS-Performance zuverlässig zu messen. Ich habe einfach Schritt für Schritt ausgeklammert und die FPS gemessen.
  • Erster Schritt, vier FFTs (Linse horizontal, Szene R/G/B horizontal): 1,1 ms (911 fps)
  • Zweiter Schritt, vier FFTs (Linse vertikal, Szene R/G/B vertikal): 2,19 ms (460 fps)
  • Dritter Schritt, PSF (Sum of Scaled Copies): Abgeschaltet, bis der Performance-Einbruch geklärt ist
  • Vierter Schritt, sechs FFTs (PSF horizontal und vertikal, Multiplikation mit Szene): 9,86 ms (100 fps)
  • Fünfter Schritt, sechs iFFTs (Szene R/G/B je horizontal und vertikal): 7,14 ms (140 fps)
Insgesamt also 20,28 ms oder 49 fps. Wäre noch anzumerken, dass die 512er-Version nur ein Viertel des verfügbaren Gruppenspeichers nutzt, die iFFTs im suboptimalen Radix-2 sind und dass die ALU-Last noch sinken wird, wenn ich die FFTs von Radix-8 auf Radix-16 umgestellt habe. Aber ALU-Last ist eh meine geringste Sorge. Was die Geschwindigkeit der ersten beiden Schritte angeht: es ist gut möglich, dass der Treiber die Arbeit schlicht und einfach verworfen hat, als er sah, dass das Ergebnis nicht weiterverwendet wird. Mit Sonne, Mond, Sternen und Tonemapping läuft meine Testanwendung momentan bei 40 fps.

Und hier habe ich eine PSF, mit der man den Fehlerwert der FFT einschätzen kann:
error.png
Der helle Strahler in der Mitte ist die Sonne mit ca. einer Milliarde cd÷m², der Sternenhimmel liegt bei 0,001 bis 1 cd÷m2. Der relative Fehler ist also (zum Glück) relativ gering geblieben.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8246
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Glare-Algorithmus

Beitrag von Krishty »

Nachdem Cat so lieb nach Screenshots gefragt hat habe ich mir gedacht, „Was soll’s“ (ohne Satzzeichen, weil Instant Messaging meine Betonung so hat verkommen lassen, dass ich auch in Gedanken schon Alt+S drücke statt zu interpunktieren … oder mein Leben meine Sprache so geprägt hat, dass ich auch meine Sätze schon ins Leere laufen lasse … obwohl dann eher eine Ellipse kommen müsste. Nein; ich denke, meine Gedanken sind zusammenhanglos wie eine Aufzählung – ja, das wird es sein) und direkt ein Video gemacht (H.264):

[Die Dateierweiterung mkv wurde deaktiviert und kann nicht länger angezeigt werden.]

  • Um der Prestige Willen ist der helle Stern am Anfang starr fixiert bis er schwach genug ist, beim Schwenk keinen merklichen Wrap-Around hervorzurufen – könnte man glatt für Lense Flare halten
  • Am Ende schalte ich den Glare mal kurz ab, damit die Sterne wieder nur ein paar ausdruckslose Pixel sind und man mal sieht, was für ein gutes Gefühl für Überhelligkeit Glare vermittelt
  • Die Ringing-Artefakte kriege ich nicht weg so lange ich nicht die Fresnel-Funktion kapiere
  • Die Sternhelligkeiten sind, wie ich feststellen musste, von vorn bis hinten verkackt. In dem Video nicht, da habe ich mir eine schöne Ecke rausgesucht. Aber ich habe z.B. einen fetten gelben Stern, der so hell scheint wie der komplette Mond … da gibt es also noch viel zu tun
Fun fact: Ich hatte die letzten Tage versehentlich doppelte Auflösung allokiert. Den Fehler habe ich nun behoben, aber weil ich irgendwo irgendwas anderes kaputt gemacht habe, ist es nur noch halb so schnell wie vorher (also vier Mal langsamer als man es vermuten würde). Oder das ist weil der Rechner langsam schlapp macht, denn der ist schon vier Tage am arbeiten und ich habe keine Möglichkeit, ihn herunterzufahren. Genau wie mein Kopf. Boah, kurz vor Sonnenaufgang bin ich noch tausendmal verballerter als sonst.
Zuletzt geändert von Krishty am 07.01.2011, 15:58, insgesamt 2-mal geändert.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Glare-Algorithmus

Beitrag von CodingCat »

Wow, wunderhübsch - da macht selbst ein ganz normaler Sternenhimmel was her. :D
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
Krishty
Establishment
Beiträge: 8246
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Glare-Algorithmus

Beitrag von Krishty »

Soo, die 1024×1024-Version läuft vorerst (nun auch mit Tonemapping). Performance ist unterirdisch – gestern 4 fps; mittlerweile habe ich sie verdoppelt indem ich mehrere Schübe mit einfachen Shadern rechne statt weniger mit Komplexeren. Offenbar gilt für Compute Shader die Regel: Nur von einer Stelle lesen und das Ergebnis möglichst an dieselbe Stelle zurückschreiben – selbst, wenn der Gesamtrechenaufwand dadurch steigt …
1024.png
Ich muss unbedingt die Ringe glattkriegen; verdammter Fresnel. Wer genau hinschaut sieht außerdem, dass die PSF 2×2 Pixel groß ist und der Blur deshalb einen halben Pixel gegenüber den Sternen verschoben ist. Way to go … und ich muss selbstverständlich gucken, was ich an der Performance drehen kann (Zeit für Benchmarks), damit 2048² nicht auf immer Illusion bleibt.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Aramis
Moderator
Beiträge: 1458
Registriert: 25.02.2009, 19:50
Echter Name: Alexander Gessler
Wohnort: 2016
Kontaktdaten:

Re: Glare-Algorithmus

Beitrag von Aramis »

Sieht klasse aus.Ich faende es toll, die Technik auch mal auf was anderes als Sterne angewandt zu sehen - schlussendlich ist es ja ein general-purpose Glare-Modell, nicht wahr? :-)
Alexander Kornrumpf
Moderator
Beiträge: 2114
Registriert: 25.02.2009, 13:37

Re: Glare-Algorithmus

Beitrag von Alexander Kornrumpf »

Nur von einer Stelle lesen und das Ergebnis möglichst an dieselbe Stelle zurückschreiben
Wie doof ist dass denn? Da lobe ich mir ja dass die coalescing rules es für CUDA transparent machen wann lesen und schreiben schnell sind und wann nicht.
Benutzeravatar
Krishty
Establishment
Beiträge: 8246
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Glare-Algorithmus

Beitrag von Krishty »

Aramis hat geschrieben:Ich faende es toll, die Technik auch mal auf was anderes als Sterne angewandt zu sehen - schlussendlich ist es ja ein general-purpose Glare-Modell, nicht wahr? :-)
Ich auch, aber beim derzeitigen Tempo müssen wir da noch bis 2016 warten.
Alexander Kornrumpf hat geschrieben:
Nur von einer Stelle lesen und das Ergebnis möglichst an dieselbe Stelle zurückschreiben
Wie doof ist dass denn? Da lobe ich mir ja dass die coalescing rules es für CUDA transparent machen wann lesen und schreiben schnell sind und wann nicht.
So wie ich das sehe, gelten die Coalescing Rules für Nvidia-Hardware, nicht speziell für CUDA. Per Compute Shader kann ich die Lese- und Schreibzugriffe genau so gruppieren und der Treiber wird ähnlichen Text erzeugen wie unter CUDA … und unter AMD-Karten mit anderer Speicheranbindung funktioniert dann beides gleich schlecht. Im übrigen bedeutet es ja dasselbe – dass man eine kontinuierliche Speicherregion bearbeiten und dabei jeder Thread „seinen“ Offset lesen soll – und ist ganz einfach ein Resultat der Evolution aus Pixel-Shadern.

Achja – falls ihr gerade aus dem Fenster blicken und den Mond sehen könnt, könnt ihr gut sehen, wie weit ich noch von einem akzeptablen Ergebnis weg bin: Der Glare ist zu stark, seitlich gestreckt (weil ich fälschlicherweise annehme dass man bei solchen Helligkeiten schon die Augen zusammenkneift) und die Sternhelligkeit ist ein paar Dekaden zu hoch. Aber immerhin ist die Leistung eben nochmal von acht auf zehn Hertz verbessert worden.
rl.png
Zuletzt geändert von Krishty am 09.01.2011, 19:17, insgesamt 1-mal geändert.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4260
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: Glare-Algorithmus

Beitrag von Chromanoid »

Und hast du mal geschaut ob du das Glare auf einem verkleinerten bild berechnen kannst um es dann auf das original bild vergößert drüber zu legen?
Benutzeravatar
Krishty
Establishment
Beiträge: 8246
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Glare-Algorithmus

Beitrag von Krishty »

Ja, das werde ich sogar müssen – 1024² ist ja noch nicht einmal in annähernd echtzeitfähigen Gefilden für einen Postprocessing-Effekt und Full-HD fordert 2048². Allerdings muss ich den Glare dazu erstmal fokussiert kriegen – wenn nämlich schon in Originalauflösung alle Pixel zu 2×2 streuen, wird das in halber Auflösung richtig schlampig.

Edit: Besser?
rl2.png
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Alexander Kornrumpf
Moderator
Beiträge: 2114
Registriert: 25.02.2009, 13:37

Re: Glare-Algorithmus

Beitrag von Alexander Kornrumpf »

So wie ich das sehe, gelten die Coalescing Rules für Nvidia-Hardware, nicht speziell für CUDA.
Das eigentlich Entscheidene ist ja das Pattern das sich daraus entwickelt hat, das man wo möglich einmal coalsced in den lokalen (schnellen) Speicher liest, dort alle Berechnungen vornimmt und dann wieder coalesced in den globalen Speicher zurück schreibt. Ich weiß ja nicht inwiefern du diese Kontrolle auch im Shader hast, aber es klang deiner Beschreibung nach eher wie kompilieren und hoffen.
Benutzeravatar
Krishty
Establishment
Beiträge: 8246
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Glare-Algorithmus

Beitrag von Krishty »

Ja doch, die habe ich – globalen, lokalen und gruppenübergreifenden Speicher kann man mit erträglichem Aufwand verwalten. Ich war nur halt baff, weil diese Coalescing Rules schon seit der GeForce 8800 gelten und sich scheinbar kein bisschen verändert haben, obwohl immer von verbesserter Scattering Performance gesprochen wird. Seine Daten aus vier Texturen zusammenlesen ist einfach (noch) nicht.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8246
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Glare-Algorithmus

Beitrag von Krishty »

Mal ein kleiner Statusbericht:
  • Die 1024²er-Version läuft bei 12 fps (auf einer Radeon HD 5770). Es werden 15 sein, wenn (oder falls) ich endlich den inversen Radix-8-Butterfly zusammengekriegt habe, mit dem ich mich seit Wochen quäle. Wie schnell die 512er-Version läuft, kann ich erst sagen, wenn ich sie auf den selben technischen Stand gebracht habe (habe nun eine Woche nicht mehr mit ihr gearbeitet) – ich gehe von über 30 fps aus.
  • Ich habe die meisten generellen Optimierungen abgeschlossen – ich werde morgen noch etwas testen, was den VRAM-Bedarf um ein Siebtel senkt; werde es aber nicht übernehmen, falls es sich negativ auf die Geschwindigkeit auswirkt. Radix-16 könnte vielleicht nochmal 20 bis 30 % rausholen, aber Sprünge um Vielfache dürften unmöglich sein. (Randbemerkung: Radix wirkt sich tatsächlich noch so stark aus, weil nicht nur die Gesamtzahl der Rechenoperationen ein wenig sinkt, sondern, weil es der GPU bei doppeltem Radix auch nur noch halb so schwer fällt, die Speicherverzögerungen zu verstecken – und die sind das Hauptproblem.) Alles, was darüber hinaus noch bleibt, sind maschinenspezifische Optimierungen (Anordnung der Lese- und Schreiboperationen, Anordnung der Zwischenergebnisse entsprechend der Speicherbänke usw; also alles, was ab der nächsten GPU vergebene Liebesmüh’ sein wird).
  • Ich habe ein wenig an der Iristextur gefeilt und nun endlich „stacheligen“ Glare. Das ist eine echte Qual, weil sich tatsächlich jeder Iris-Pixel stark auf die Form des Glares auswirkt. Hier könnte ich wahrscheinlich ganze Wochen verplempern. Screenshots unten.
  • Ich werde zwei Optionen anbieten: Einmal, vorberechnetes Glare-Muster statt dynamischem zu benutzen (dürfte die Performance verdoppeln, oszilliert und knistert aber nicht mehr so schön) und, den Glare in halber Auflösung zu berechnen (dürfte die einzige Möglichkeit sein, das innerhalb der nächsten Jahre echtzeitfähig zu halten – dafür muss ich aber erstmal die PSF scharf kriegen).
  • Je nachdem, wie sehr ich meinen Welthass unter Kontrolle habe, baue ich nächste Woche u.U. eine 2048²-Version – just for fun.
shinynew2.png
shinynew.png
Nachtrag: Die 512²er-Version läuft bei 49 fps, also ziemlich genau vier Mal so schnell wie die 1024²er-Version – mit an Sicherheit grenzender Wahrscheinlichkeit wird also eine 2048²er-Version bei 3 fps arbeiten; vielleicht 4 falls man optimiert, dass von nicht genutzten Bildbereichen auch keine FFT berechnet werden muss (was bei 1920×1080 immerhin fast die Hälfte wäre).
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Glare-Algorithmus

Beitrag von CodingCat »

Unglaublich, mit den Stacheln sieht das jetzt absolut fantastisch aus. Und das bei 49 FPS, da kann man ja echt was mit anfangen... Nur schade, dass ich keine DX11-Karte haben. :-(
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
Zudomon
Establishment
Beiträge: 2254
Registriert: 25.03.2009, 07:20
Kontaktdaten:

Re: Glare-Algorithmus

Beitrag von Zudomon »

Also ich finde, der Glare ist absolut falsch! So bricht sich das Licht in meinem Auge nicht...
Aber was erwartest du denn auch? Du modellst ein Standardauge und gehst dann davon aus, es würde keinem auffallen, wenn du anderen das Scattering eines fremden Auges unterjuwelst! ;)

btw. sieht echt schon verdammt real aus!
Benutzeravatar
Krishty
Establishment
Beiträge: 8246
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Glare-Algorithmus

Beitrag von Krishty »

Ich pfeife auf die schönen Stacheln und das oszillieren. Jaa, es wirkt in Bewegung super, aber es gibt unüberwindbare Hindernisse (weil das Paper nicht nur bei den Wrap-Arounds arg geschönt ist). Und irgendwann muss ich auch mal weiterkommen. Es bleibt also bei einem statischen Blur mit einer Kernelgröße von 2048² Pixeln … enttäuschend, aber immernoch hübsch anzusehen. Ich warte noch die Klärung einiger Performance-Fragen ab, damit ich die Implementierung endlich abschließen kann … bis dahin gibt es nur ein paar Screenshots der Full-HD-Version mit halbaufgelöstem Blur:
gl.png
mondsichel.png
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Schrompf
Moderator
Beiträge: 4859
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: Glare-Algorithmus

Beitrag von Schrompf »

Das sieht absolut geil aus. Großen Glückwunsch! Bei Deinem letzten Jammer-Beitrag dachte ich noch, Du hast das Programmieren komplett hingeschmissen :-)
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
Krishty
Establishment
Beiträge: 8246
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Glare-Algorithmus

Beitrag von Krishty »

Schrompf hat geschrieben:Bei Deinem letzten Jammer-Beitrag dachte ich noch, Du hast das Programmieren komplett hingeschmissen :-)
Ich auch. Es ist auch eigentlich nichts anderes als ständige Frustration mehr:
  • Der VC-Compiler verkackt meinen Shader-Cache
  • der HLSL-Compiler ist so lahm dass jede kleine Korrektur minutenlanges Warten erfordert
  • Die Grafikkarte nutzt nur 12–25 % der zur Verfügung stehenden Bandbreite, und wenn sich das nicht ändert, kann ich den Scheiß gleich in die Tonne kloppen weil die Performance wie 2007 ist
  • Herausfinden, ob es ein Fehler im Compiler, Treiber oder by Design ist kann ich nicht, weil die HLSL-Opcode-Spezifikation Verschlusssache ist
  • Bei Microsoft und AMD antwortet mir niemand
  • Compute Shader ist so mies dokumentiert dass man alles einmal ausprobiert haben muss, egal, wie abwegig es aussieht, bevor man weiß, wie man es machen muss
  • Zum Benchen und optimieren muss ich immer alles schließen, weil Flash, Aero und andere ungeklärte Ursachen 10–70 % der GPU fressen
  • Benchen ist eh für den Popo weil sich dank der Bandbreitenlimitation alles völlig unberechenbar verhält
  • AMDs Stream Profiler stürzt ab
  • AMDs Treiber stürzt ab einer gewissen Shaderkomplexität ab
  • Der HLSL-Compiler stürzt ab, sobald der Application Verifier eingeschaltet ist
  • Der PC stürzt ab, wenn meine Engine von einem anderen Fenster verdeckt wird
  • Der PC friert ein, wenn ich versehentliche eine Shader-Endlosschleife gebaut oder einen negativen Dispatch-Parameter übergeben habe
  • ntdll stürzt manchmal ab, wenn es Abhängigkeiten meiner Engine lädt
  • Gimp benutzt zum Überblenden meiner Ebenen zu geringe Farbtiefe; kann meine Texturen nicht feinabstimmen
Ich will diese verdammte Selbstgeißelung einfach nur noch hinter mir haben. Naja … im Gegenlicht sieht alles gleich schöner aus … mal sehen, wie lange es sich noch hinzieht.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8246
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Glare-Algorithmus

Beitrag von Krishty »

Ich nutze die Gelegenheit ausgedehnter Farbübergänge einfach mal dafür, wieder Werbung für Dithering zu machen:
without dithering.png
with dithering.png
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4260
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: Glare-Algorithmus

Beitrag von Chromanoid »

Video pls :) ich weiß das oszillieren und so ist raus aber ich würde es trotzdem mal gerne sehen...
Benutzeravatar
Krishty
Establishment
Beiträge: 8246
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Glare-Algorithmus

Beitrag von Krishty »

Wenn …
  • … es fertig ist (Shader für 20 FFTs in je drei verschiedenen Auflösungen zu entwickeln, zu prüfen und zu optimieren braucht seine Zeit),
  • … ich mit der visuellen Qualität zufrieden bin,
  • … es optimiert ist (500 MiB VRAM für einen Nachbearbeitungseffekt sind eben zu viel),
  • … ATI neue Treiber rausgebracht hat (sollte sich der Leistungseinbruch als deren Fehler herausstellen),
  • … ich es auf anderen Karten als der HD 5770 getestet habe und
  • … der Shader-Cache um herstellerspezifische Optimierungen erweitert ist,
dann gibt es eine aktualisierte Sternendemo.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8246
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Glare-Algorithmus

Beitrag von Krishty »

Und ich mache nochmal auf einen weiteren Fallstrick aufmerksam: Aliasing und Flimmern.

Nehmen wir diesen Glare, der später auf das Ausgangsbild aufaddiert wird:
moongl.png
Er resultiert aus der Mondsichel. Die sieht ohne Glare so aus:
moon.png
Es lässt sich abzählen, wie wenige Pixel der Mond abdeckt – nur etwa 30 bis 40. Trotzdem sind diese 30 bis 40 Pixel für den Großteil der Helligkeit der restlichen zwei Millionen Pixel verantwortlich.

Wenn sich der Mond nun bewegt – das muss nicht durch programmierten Tagesrhythmus geschehen, sondern kann schon ganz banal durch Kamerabewegung hervorgerufen werden – bleibt die Gesamthelligkeit der Pixel nicht konstant, weil immer mal wieder ein paar Pixel überspringen. Die Gesamthelligkeit aller Mondpixel springt um gute fünf Prozent. Und damit springt auch ein Großteil der Gesamthelligkeit des fertigen Bildes von Frame zu Frame. Und alles flackert und flimmert wie Sau.

Ich habe das Glück, von Anfang an mit viel Antialiasing gerendert (und die Gestirne ja sogar auf möglichst glattes Aussehen optimiert) zu haben; das kommt mir jetzt zugute – ich brauche „nur“ mit 8×AA rendern und muss darauf achten, vor den FFTs alle Samples aufzulösen. Das kostet zwar 6 ms in Full-HD, aber die Leistung ist ja eh schon ruiniert. Das Flimmern ist zwar immer noch da, aber nicht mehr so stark, dass es direkt ins Auge fällt.

Der Punkt ist: Wenn ihr Glare implementiert, dann achtet darauf, dass kleine Lichtquellen möglichst weich sind oder lasst sie komplett weg. Sonst habt ihr nur noch Stroboskoplicht.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8246
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Glare-Algorithmus

Beitrag von Krishty »

Weil mir die ganze Sache langsam zu viel wird, fange ich schonmal mit meiner Zusammenfassung an (was ’n Schluss, nä? Höhö). Beginnen wir damit, worüber wir hier eigentlich die ganze Zeit reden und ziehen in zukünftigen Beiträgen weiter zu Fallstricken und Optimierungen.

Es geht hier um dieses Paper, das ich zu implementieren versucht habe: Temporal Glare: Real-Time Dynamic Simulation of the Scattering in the Human Eye.

Weil hier auch die Profis ihre Probleme hatten zu verstehen, was vor sich geht, ein Überriss:

• Konventionelle Filter werden bei großen Kerneln furchtbar ineffizient – man setzt auch heute noch sehr lokale Kernel (zweistellige Pixelzahl) für Blur ein. Weiterhin benutzt man einen Gaussfilter. Der hat zwar im Fall von Glare wenig damit gemein, was wir in der Realität sehen, ist dafür aber effizient berechenbar weil er sich in zwei Durchläufe über X und Y zerlegen lässt. (Das ist nur mit sehr wenigen Funktionen möglich!) Möchte man komplexere Kernel – z.B. für einen Sternfilter – macht man entweder pro Streifenrichtung einen Durchlauf oder rendert den fertigen Kernel in eine Textur, in der man die Intensität nachschlägt statt sie zu berechnen. Hier ist man nun in Regionen, die auch mit modernen Karten unmöglich in Echtzeit realisierbar sind, sobald der Kernel nicht mehr in den Cache passt.

• Weitaus intelligenter lässt sich das Problem mit der Fourier-Transformation lösen: Man berechnet die FT des Bildes und des Kernels, multipliziert beide und führt dann eine inverse Fourier-Transformation aus. Das Ergebnis ist ein Bild, bei dem der Kernel auf jeden einzelnen Pixel angewandt wurde. Da sowohl die Daten als auch der Kernel dieselbe Größe haben müssen, ist es für die Leistung egal, wie groß der Kernel ist – ein 4×4-Gaussfilter ist genau so schnell wie ein 2048² Pixel weiter Sternfilter. Die Fourier-Transformation (und auch ihre Inverse) realisiert man üblicherweise als Fast Fourier Transformation, die sich enorm gut parallelisieren lässt. Über schnelle Implementierungen gibt es jede Menge Paper; ich habe mich an diesem orientiert: High Performance Discrete Fourier Transforms on Graphics Processors.

• In der Essenz basiert nun der Glare-Algorithmus darauf, die Punktspreizfunktion (Point Spread Function) des Auges zu berechnen – also das Abbild, das ein einziger Pixel nach Streuung und Beugung auf der Netzhaut hinterlässt – und die dann als Kernel auf alle Pixel des Frames anzuwenden.

• Die PSF entsteht durch die Fokussierung der Linse sowie durch Beugung an der Iris, den Wimpern, Partikeln im Glaskörper u.v.m. Diese Beugung lässt sich durch das Beugungsintegral einer Abbildung des Auges berechnen (im Paper unter dem englischen Titel Fresnel Diffraction referenziert). Für so kurze Brennweiten wie im menschlichen Auge lässt sich das wiederum zufälligerweise als Fourier-Transformation ausdrücken, was die Sache enorm vereinfacht.

• Der Ablauf besteht nun also daraus,
  • eine Innenansicht des Auges zu rendern.
  • Daraus lässt sich nach Multiplikation mit einer Linsenformel (Fresnel Term im Paper) per Fourier-Transformation die PSF berechnen. (Normalerweise müsste das für jede Wellenlänge geschehen, aber man kann das Ergebnis annähern indem man sie nur für grünes Licht (mittlere Wellenlänge) berechnet und viele leicht skalierte und dem Lichtsprektrum entsprechend gefärbte Kopien aufaddiert).
  • PSF und Szene werden jeweils Fourier-transformiert und miteinander multipliziert.
  • Die iFT des Ergebnisses wird berechnet und ist das Ergebnis: das Eingangsbild mit angewandtem Glare.
eXile hat die entsprechende mathematische Repräsentation hier aufgeschrieben.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8246
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Glare-Algorithmus

Beitrag von Krishty »

Nun komme ich zum ersten Fallstrick, über den ich gestolpert bin: Den Aufwand zu unterschätzen.
  • Eine FFT kann am besten auf Zweierpotenzen durchgeführt werden. Das bedeutet, dass die Bildausmaße auf die nächsthöheren Zweierpotenzen aufgerundet werden müssen. (Es ist zwar möglich, FFTs auch auf die Potenzen von anderen Primzahlen durchzuführen, aber das lässt die Code-Basis explodieren.)
  • Eine FFT sieht das Signal als zyklisch an; so auch die PSF. Das bedeutet konkret, dass ein helles Licht am einen Bildrand am gegenüberliegenden Bildrand ebenfalls Glare produziert. Wenn man Glück hat, ist der Kernel kleiner als der Verschnitt, den man durch das Aufrunden auf die nächste Zweierpotenz produziert, und wird von diesem verschluckt. Da die PSF über das gesamte Auge geht und Glare kaum lokal eingrenzbar ist, hat man hier Pech und muss die Bildausmaße verdoppeln.
  • Multisampling lässt sich nicht transformieren. Glare muss im linearen Farbraum berechnet werden; Multisampling darf erst im gammakorrigierten Farbraum aufgelöst werden. Darum ist der Algorithmus grundsätzlich nicht kompatibel zu Multisampling – was umso schwerer wiegt, weil Aliasing durch Glare äußerst stark hervorgehoben wird. Man kann das nur umgehen, indem man die Mitte der PSF schwärzt (damit sie den Originalwert auslöscht) und das entstehende Bild auf die ursprüngliche, immernoch gemultisamplete, Szene aufaddiert, die man vorher mit dem ursprünglichen Wert in der Mitte der PSF multipliziert.
  • Man muss alle Berechnungen in 32-Bit-Gleitkommapräzision durchführen; auf kleinere Formate lässt sich nicht ausweichen.
  • Spätestens beim Fresnel Term der PSF kommen in der Eingabe der FT komplexe Zahlen vor; die üblichen FFT on Real Values-Optimierungen sind hier nicht mehr anwendbar. Disregard that … wenn die PSF steht sind es ja wieder nur reelle Werte und die Szene ist ebenfalls reell … hier kann ich wohl wirklich noch was rausholen
Rechnen wir die Sache mal exemplarisch für einen Frame in Full-HD mit 8×AA durch (habe ich so nicht implementiert, mehr dazu später in den Optimierungen). Um Wrap-Arounds zu vermeiden muss die Auflösung verdoppelt werden, um FFT zu nutzen muss sie zur nächsthöheren Zweierpotenz gerundet werden – für die sichtbaren 1920×1080 Pixel muss man also intern in 4096×4096 Pixeln rechnen:
  • Für die PSF muss eine Innenansicht des Auges berechnet werden. Das braucht man nur für einen Kanal zu machen; die Sum of Scaled Copies wird später die Farben hinzufügen. Da man hier bereits den Fresnel Term einbeziehen muss, muss alles in komplexen Zahlen geschehen. Speicherverbrauch: 4096×4096×2×4 B = 128 MiB; Bandbreite (angenommen, man kann die Ansicht im Shader aus relativ kleinen Texturen zusammenkomponieren): 4096×4096×2×4 B = 128 MiB schreibend.
  • Per FFT – horizontal und vertikal – muss die PSF berechnet werden. Wir nehmen an, dass sich die FFT komplett im Gruppenspeicher des Shaders realisieren lässt, also keine Zwischenergebnisse in globalen Speicher wandern – mit den 32 KiB Gruppenspeicher von Compute Shader 5.0 ist das möglich, mit den 16 KiB von Cuda nicht mehr. Man könnte dann auch das Ergebnis in dieselbe Textur schreiben, aus der gelesen wurde, aber Compute Shader haben damit Probleme und der gesparte Speicher resultiert in einem 4× geringeren Speicherdurchsatz (Erklärung nach der Liste). Darum legen wir hier eine Kopie des Puffers an und schreiben in einem Ping-Pong-Muster. Da wir von der PSF nur den Realteil benötigen, schreiben wir auch nur die realen Werte. Speicherverbrauch: 4096×4096×2×4 B = 128 MiB; Bandbreite: 2×4096×4096×2×4 B = 256 MiB lesend, 4096×4096×2×4 + 4096×4096×4 B = 192 MiB schreibend; FFTs: 2×4096.
  • Die Sum of Scaled Copies macht aus der monotonen PSF eine farbige PSF mit Brechungen. Für ein befriedigendes Ergebnis braucht man 16 übereinandergelegte Kopien, die bei mir zu drei Vierteln im Cache lagen. Wir lesen einkanalig und schreiben in drei Kanäle (RGB) mit Reellwerten. Speicherverbrauch: 3×4096×4096×4 B = 112 MiB; Bandbreite: 16/4×4096×4096×4 B = 256 MiB lesend, 3×4096×4096×4 B = 448 MiB schreibend.
  • Die FFT der PSF muss berechnet werden. Bisher haben wir für die PSF nur eine reellwertige RGB-Textur, also brauchen wir wieder zwei (Ping-Pong!) komplexe RGB-Texturen. Speicherverbrauch: 2×3×4096×4096×2×4 B = 768 MiB; Bandbreite: 2×3×4096×4096×2×4 B = 768 MiB lesend; 2×3×4096×4096×2×4 B = 768 MiB schreibend; FFTs: 2×4096.
  • 1920×1080 Pixel 12-Byte-RGB mit den Pixeln der Szene (die man aber auch ohne Glare hätte) müssen mit je acht Samples in einen als FFT-Eingabe lesbaren Puffer aufgelöst werden. (GPUs können nur das 128-Bit-RGBA-Format verarbeiten, aber der Alphakanal ist hier vernachlässigbar.) Speicherverbrauch: 1920×1080×12 B = 24300 KiB; Bandbreite: 1920×1080×12×8 B = 194400 KiB lesend, 1920×1080×12 B = 24300 KiB schreibend.
  • Die FFT der Szene muss in RGB berechnet werden. Für das Ping-Pong können wir einen Puffer der PSF-FFT-Berechnung wiederverwenden, brauchen aber noch einen für das Ergebnis. Wir lesen ausschließlich reelle Werte, schreiben aber komplexe. Speicherverbrauch: 3×4096×4096×2×4 B = 384 MiB; Bandbreite: 3×4096×4096×4 + 3×4096×4096×2×4 B = 576 MiB lesend; 2×3×4096×4096×2×4 B = 768 MiB schreibend; FFTs: 2×4096.
  • Die Ergebnisse der Fourier-Transformationen müssen miteinander multipliziert werden. Dann wird die inverse FFT ausgeführt. (Diese Schritte können miteinander kombiniert werden. Theoretisch könnte man die vertikale Fourier-Transformation des vorherigen Schritts auch mit der dieses Schritts zusammenlegen und sich das Schreiben und Lesen eines Zwischenergebnisses sparen – praktisch wird die Shader-Komplexität dann aber so groß, dass der HLSL-Compiler mir bei Seitenlängen über 1024² keine gültigen Programme mehr generiert und die Leistung durch hunderte Scratch-Register eh im Keller wäre.) Wir benutzen die Puffer aus den vorherigen Schritten für Zwischenergebnisse und schreiben am Ende nur reelle Zahlen, weil uns der komplexe Teil der Szene nicht interessiert. Bandbreite: 3×4096×4096×4 + 3×4096×4096×2×4 B = 576 MiB lesend; 2×3×4096×4096×2×4 B = 768 MiB schreibend; FFTs: 2×4096
In der Summe müssen wir also 32.768 FFTs auf je 4096 Samples durchführen, lesen dabei 2,5 GiB, schreiben 3 GiB und benötigen 1,5 GiB zusätzlichen Grafikspeicher. Das sind globale Werte; zum Glück wird ja am meisten mit gruppenlokalen Werten gerechnet (die aber auch nicht latenzfrei sind!). Und das alles mindestens 25× pro Sekunde.

Jeder Wert an sich kratzt, obgleich recht optimistisch gerechnet, bereits an der Leistungsgrenze aktueller GPUs. Manche lassen sich durch gute Optimierung aber noch drücken. Die 1,5 GiB VRAM jedoch sind kaum zu reduzieren und sind das Totschlagargument gegen Full-HD-Glare. Man könnte sie fast halbieren, indem man statt dem Ping-Pong-Muster in dieselbe Textur schreibt, aus der man liest; dem steht allerdings etwas im Weg:

ATI-Karten haben zwei Speicherpfade – den FastPath, der nur 32-/64-/128-Bit-Werte schreibt und 80 % der theoretischen Spitzenbandbreite erreicht und den CompletePath, der jeden Speicherzugriff durch einen lokalen Puffer führt und 4–8× langsamer ist als FastPath. CompletePath ist dazu da, „ungerade“ Speichergrößen zu schreiben (z.B. einzelne Bytes), atomare Operationen durchzuführen und Speicherzugriffe sofort global sichtbar zu machen. Welchen Speicherpfad der Shader benutzt ist davon abhänhig, wie man die Daten deklariert hat:
  • RWBuffer<float>: CompletePath
  • RWStructuredBuffer<float>: FastPath
  • RWTexture?D<float>: CompletePath
  • Texture?D<float>: FastPath
Mir persönlich sieht das bereits völlig sinnlos aus, weil RWBuffer ein RWStructuredBuffer ohne Synchronisierung oder Unterstützung nicht-nativer Elemente ist, und deshalb eigentlich schneller sein sollte, nicht 8× langsamer. Wiedemauchsei: versucht man, aus einer Textur sowohl zu lesen als auch zu schreiben, muss man RWTexture2D benutzen, das ist langsam. Rendert man hingegen in einem Ping-Pong-Muster und liest die vorherige Textur als Texture2D, ist das schneller – viel schneller. Nachdem ich die Sum of Scaled Copies auf Ping-Pong umgestellt hatte, hat sich die Leistung verachtfacht (ja, der Sprung von einer HD 2xxx auf eine HD 6xxx). Die Gesamtlaufzeit des kompletten Algorithmus hat sich geviertelt.
Das war das lesen; aber schreibend führt noch immer kein Weg am langsamen RWTexture2D vorbei. RWStructuredBuffer ist keine Option, weil es durch die Auslegung im Speicher bei vertikalen FFTs und wahlfreiem Zugriff noch langsamer ist. Auf die Frage nach dem Sinn und der Schuld haben mir weder AMD noch Microsoft geantwortet, also muss ich mich vorerst einfach damit abfinden. Immerhin kann man den Schreibdurchsatz um zehn Prozent erhöhen, wenn man bei komplexen Zahlen Real- und Imaginärteil in einem Rutsch schreibt statt getrennt.

Das zu tun ist nicht selbstverständlich – wenn man nämlich getrennt schreibt, kann man einige Texturen sowohl für reelle als auch für komplexe Zahlen benutzen und damit das eine oder andere Texturobjekt sparen. Aber die Leistung ist eben schlechter.

Der Datenmenge, mit der man hier hantiert, sollte man sich halbwegs bewusst sein.

Als Nächstes geht es dann an ein paar grundsätzliche, aber unvorhergesehene Schwächen des Konzepts und dann um Optimierungen.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Lynxeye
Establishment
Beiträge: 145
Registriert: 27.02.2009, 16:50
Echter Name: Lucas
Wohnort: Hildesheim
Kontaktdaten:

Re: Glare-Algorithmus

Beitrag von Lynxeye »

Krishty hat geschrieben: [*]Eine FFT sieht das Signal als zyklisch an; so auch die PSF. Das bedeutet konkret, dass ein helles Licht am einen Bildrand am gegenüberliegenden Bildrand ebenfalls Glare produziert. Wenn man Glück hat, ist der Kernel kleiner als der Verschnitt, den man durch das Aufrunden auf die nächste Zweierpotenz produziert, und wird von diesem verschluckt. Da die PSF über das gesamte Auge geht und Glare kaum lokal eingrenzbar ist, hat man hier Pech und muss die Bildausmaße verdoppeln.
Ich weiß es ist physikalisch wahrscheinlich absolut nicht korrekt mit so einem Hammer da ran zu gehen, aber hast du mal über eine Windowfunktion vor der FFT nachgedacht? Das würde zwar den Einfluss von Lichtquellen am Bildrand ziemlich killen, würde es dir aber wahrscheinlich erlauben bei FullHD die FFT mit einer Auflösung von 2048² auszuführen. Konkret schwirrt mir da gerade so etwas wie die Tukey Funktion im Kopf herum.

Ich bin mir nicht sicher, in wie fern das die Bildqualität beeinträchtigt und mit deinem physikalisch korrekten Ansatz zu verbinden ist, aber bevor du ganz aufgibst wollte ich das noch mal schnell in die Runde werfen.
Benutzeravatar
Krishty
Establishment
Beiträge: 8246
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Glare-Algorithmus

Beitrag von Krishty »

Lynxeye hat geschrieben:Ich weiß es ist physikalisch wahrscheinlich absolut nicht korrekt mit so einem Hammer da ran zu gehen, aber hast du mal über eine Windowfunktion vor der FFT nachgedacht? Das würde zwar den Einfluss von Lichtquellen am Bildrand ziemlich killen, würde es dir aber wahrscheinlich erlauben bei FullHD die FFT mit einer Auflösung von 2048² auszuführen. Konkret schwirrt mir da gerade so etwas wie die Tukey Funktion im Kopf herum.
Oha; von Fensterfunktionen höre ich jetzt das erste Mal. Wenn ich das richtig verstehe, schwächt man das Signal (nicht den Kernel) zum Rand hin ab?

Ganz so einfach ist es nicht, weil die Helligkeit 14 Dekaden abdeckt. Wenn etwas am linken Bildrand auftaucht, was acht Dekaden heller ist als der Rest (z.B. der Mond vor den Sternen), streut das trotz winziger Randwerte noch wie Sau. Schwächerer Glare, wie der der Sterne wäre dann nur relativ weit vom Bildrand entfernt überhaupt bemerkbar (ich gehe bei dem Kernel von einer viertel Bildschirmbreite deutlichem Radius aus; also wäre der Glare nur auf einem Viertel der Bildschirmfläche (halbe Breite × halbe Höhe abgedeckt) so hell sichtbar, wie er sein sollte) … ich muss das testweise implementieren und dann abwägen, wie gut es aussieht.

(Btw rechne ich momentan alles in halber Seitenlänge aus um von 4096² auf 2048² zu kommen; der Glare ist so schön niederfrequent, dass das nichts ausmacht.)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Schrompf
Moderator
Beiträge: 4859
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: Glare-Algorithmus

Beitrag von Schrompf »

Ich muss mal sagen: es sieht echt cool aus. Und ich finde es super, dass Du Dir soviel Mühe machst, Deine Erfahrungen hier so umfangreich zu beschreiben. Ich fand es jedenfalls interessanten Lesestoff, und dank Tante Google werden auch andere noch davon profitieren. Danke!
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
hagbard
Beiträge: 66
Registriert: 05.08.2010, 23:54

Re: Glare-Algorithmus

Beitrag von hagbard »

@Krishty: Danke für deine Mühen das ganze nieder zu schreiben. Jetzt hab auch ich als Nicht-Grafikprogrammierer eine Chance das Gesamtkonzept zu verstehen. :mrgreen:
starvald
Beiträge: 31
Registriert: 16.04.2010, 17:20
Wohnort: Heppenheim

Re: Glare-Algorithmus

Beitrag von starvald »

Sehr geil von Dir hier alles mit uns zu teilen. Hut ab und weiterhin viel Spass und Erfolg beim coden!
Benutzeravatar
Krishty
Establishment
Beiträge: 8246
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Glare-Algorithmus

Beitrag von Krishty »

Dankt mir erst, wenn ich durch bin … DO habe ich wieder Zeit, hier über die Schwächen zu schwadronieren.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Antworten