GLSL Compute Shader ImageWrite synchronisieren

Für Fragen zu Grafik APIs wie DirectX und OpenGL sowie Shaderprogrammierung.
Antworten
Matthias Gubisch
Establishment
Beiträge: 470
Registriert: 01.03.2009, 19:09

GLSL Compute Shader ImageWrite synchronisieren

Beitrag von Matthias Gubisch »

Ich spiele aktuell an einer Rasterizer implementierung im ComputeShader herum.

An sich klappt das auch schon ganz gut. Jetzt will ich aber einen DepthBuffer hinzufuegen.

Nach meinem Verständnis muss das schreiben auf den DepthBuffer und das ColorTarget doch "atomar" also in einem Rutsch erfolgen damit mir da keine andere ShaderInvocation dazwischen funkt.

Atomare Image Operationen gibt es aber nur für 32bit integer Targets, was für einen Einfachen ColorBuffer schon passt, mit nem DepthBuffer zusammen klappt das nicht mehr und spätestens bei HDR Rendering wirds ganz problematisch.

Einfach eine globale MemoryBarrier setzten würde sicher funktionieren, aber ich fürchte das das wahnsinnig ineffizient wird.
Zweite Idee waeren einfach 2 Passes 1xDepth und darauf basierend dann 1x ColorTarget. Klingt auch nicht besonders effizient/elegant.

Hat jemand eine Idee oder noch besser ein Beispiel wie man das effizienter gestalten könnte?
Bevor man den Kopf schüttelt, sollte man sich vergewissern einen zu haben
Benutzeravatar
Schrompf
Moderator
Beiträge: 4838
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: GLSL Compute Shader ImageWrite synchronisieren

Beitrag von Schrompf »

Gibt's die Atomics nicht auch für float. Oder Du schreibst halt Integer. Müssten doch eh normierte Werte [0..1] sein, die da reinkommen.

Die "echten" GPUs haben den Output Merger, der Fragmente von allen Threads sammelt und solche Konflikte auflöst. Wenn Du Lust hast, kannst Du ja all Deine Fragmente nur in ne Liste schreiben und danach erst rauspumpen
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Matthias Gubisch
Establishment
Beiträge: 470
Registriert: 01.03.2009, 19:09

Re: GLSL Compute Shader ImageWrite synchronisieren

Beitrag von Matthias Gubisch »

Unter glsl gibts atomicImageWrite nur für int32 bzw uint32 Targets
rgba8 hat 32 bit, also für normales ColorTarget alles top

Jetzt kommt der DepthBuffer ins Spiel:

so könnte das Pseudocode maessig aussehen:

Code: Alles auswählen

if(atomicDepthUpdata(tiefe))
{
	schreibeColorTarget(farbe)
}
Wenn jetzt 2 Threads nacheinander die if Bedingung erfuellen, heisst Fragment 2 liegt vor Fragment 1, wie stelle ich sicher dass auch Fragment 2 sicher nach Fragment 1 geschrieben wird und somit der richtige value im ColorTarget steht

Auf der CPU würde ich einfach einen Mutex in den If-body packen und fertig gut is..
Im ComputeShader? ka!!, Mutex gibt es da nicht und eine global Barrier scheint mir auch nicht das richtige zu sein da die die Zugriffe auch nicht zwangsweise sortiert.

Oder mache ich mir zuviele Gedanken weil da eh nix passiert wenn ich den zweiten zugriff auch atomic mache?
Bevor man den Kopf schüttelt, sollte man sich vergewissern einen zu haben
Benutzeravatar
Schrompf
Moderator
Beiträge: 4838
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: GLSL Compute Shader ImageWrite synchronisieren

Beitrag von Schrompf »

Ne Barrier wäre nach meinem Verständnis tödlich, weil Du damit nicht den einen Pixel, bei dem's Konflikte gibt, exklusivierst, sondern im schlimmsten Fall global jede Schreiboperation. Da kannste auch SingleCore auf der CPU rendern :-)

Und bereits Dein Beispiel-Code enthält ne RaceCondition, zumindest mit meiner CPU-Intuition. Niemand garantiert Dir, dass zwischen atomicDepthUpdata()-Abschluss und dem schreibeColorTarget() niemand den Thread wegscheduled :-)

Wenn Du's wirklich wirklich exakt haben willst, solltest Du vielleicht wirklich ne Liste machen. Oder immer nur Dreiecke/Primitives als Ganzes rasterizen, dann ist ja garantiert, dass jedes Zielfragment nur einmal geschrieben wird.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Matthias Gubisch
Establishment
Beiträge: 470
Registriert: 01.03.2009, 19:09

Re: GLSL Compute Shader ImageWrite synchronisieren

Beitrag von Matthias Gubisch »

Ja das mit der RaceCondition ist mir klar, ich suche ja Moeglichkeiten die zu vermeiden ;)

Aktuell rastere ich halt pro GPU "Thread" 1 Dreieick.
Problemeatiasch ist halt der Overdraw-Fall wenn 2 oder mehr Threads Dreiecke auf den gleichen Pixeln haben.
Und ich gleichzeitig Multiple Target Images haben will (Depth und Color im einfachsten Fall, ein kompletter GBuffer im schlimmsten Fall)

Liste klingt kompliziert, je nach Overdraw kann die ja beliebig lang werden.
Ich hatte mal OIT mit so ner LinkedListe per Pixel implementiert, das war Performance Technisch ziemlich grausam, ist aber auch schon > 10 Jahre her..

Kann ja nicht sein dass ich der Einzige mit dem Problem bin.

Tante Edith meint:
mit atomicExchange könnte man da eventuell was machen
Quasi ein int(8) per Pixel und den Lock holen, dann schreiben dann wieder freigeben, könnte so klappen...
Bevor man den Kopf schüttelt, sollte man sich vergewissern einen zu haben
Benutzeravatar
Schrompf
Moderator
Beiträge: 4838
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: GLSL Compute Shader ImageWrite synchronisieren

Beitrag von Schrompf »

Hm, tja. Langsam gehen mir die Ideen aus, obwohl ich das Thema echt spannend finde. Ich habe im Zuge der UE5-Nanite-Präsentation mal was von versteckten 64bit-Atomics gehört. Wenn Du Depth und RGBA in einen uint64 gepackt kriegst, könnte das Dein Problem auch lösen.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Matthias Gubisch
Establishment
Beiträge: 470
Registriert: 01.03.2009, 19:09

Re: GLSL Compute Shader ImageWrite synchronisieren

Beitrag von Matthias Gubisch »

Ja die hatte ich auch schon auf dem Schirm
Mittlerweile gab ich auch die richtige shader extension für glsl gefunden
Bin jetzt leider das ganze we nicht da, aber wenn ich nächste Woche dazukommen hab ich auf dem Stammtisch evtl was zu zeigen ;)

Aber danke schonmal für den schubser in die richtige Richtung :)
Bevor man den Kopf schüttelt, sollte man sich vergewissern einen zu haben
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: GLSL Compute Shader ImageWrite synchronisieren

Beitrag von dot »

Schrompf hat geschrieben: 14.10.2022, 11:10 Wenn Du Depth und RGBA in einen uint64 gepackt kriegst, könnte das Dein Problem auch lösen.
yup, das ist der klassische Trick. Per-pixel Locks willst du ziemlich sicher nicht. Bei Locks auf der GPU ist generell sehr aufzupassen. Hast du z.B. eine Situation wo Threads das Lock acquiren wollen welches von einem anderen Thread in der selben Wave gehalten wird, hast du auf den meisten GPUs sehr leicht ein potentielles Deadlock geschaffen (die meisten GPUs können pro Wave nur eine aktive Branch ausführen, das Acquiren des Lock ist eine Branch, wenn die Branch wo das Lock nicht acquired wurde zuerst ausgeführt wird und diese Branch dann wieder versucht das Lock zu acquiren, kommt der Thread der das Lock erhalten hat nie dran, weil die Threads die versuchen das Lock zu acquiren es unmöglich machen den Thread der das Lock releasen könnte zu schedulen). Bedenke auch, dass ein Thread pro Dreieck bei ungleichmäßiger Größenverteilung der Dreiecke sehr schnell sehr ineffizient wird…
Matthias Gubisch
Establishment
Beiträge: 470
Registriert: 01.03.2009, 19:09

Re: GLSL Compute Shader ImageWrite synchronisieren

Beitrag von Matthias Gubisch »

Das mir den Locks in der gleichen Wave ist ein Punkt den ich noch gar nicht bedacht hatte, danke für den Hinweis.

Ja das mit der Effizienz ist mit bewusst
Aber erstmal will muss es überhaupt laufen. Und eigentlich strebe ich eh möglichst viele kleine dreieicke an.

Was ich bei der 64 bit Methode noch nicht verstanden habe, ich möchte den write ja nur in Abhängigkeit vom Depth value ausführen, wie schaffe ich es nur z.B. atomicimagewritemax nur die unteren 32 bit für den Vergleich zu berücksichtigen?

Wenn ich das noch gelöst bekomme bin ich gut dabei, glaube ich.
Aber egal wie ich es in meinem Kopf grad drehe und wenn ich komm immer auf 2 Operationen, die zwar für sich atomic sein können, aber dazwischen natürlich was passieren kann.

Vielleich muss ich einfach lesen, und dann ein atomicSwapCompare und das in einer schleife so lange bis es erfolgreich war, klingt aber irgendwie auch bäh...
Bevor man den Kopf schüttelt, sollte man sich vergewissern einen zu haben
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: GLSL Compute Shader ImageWrite synchronisieren

Beitrag von dot »

Matthias Gubisch hat geschrieben: 15.10.2022, 19:17 Was ich bei der 64 bit Methode noch nicht verstanden habe, ich möchte den write ja nur in Abhängigkeit vom Depth value ausführen, wie schaffe ich es nur z.B. atomicimagewritemax nur die unteren 32 bit für den Vergleich zu berücksichtigen?
Der Trick ist, die Depth in die oberen 32 Bit zu packen und die Farbe in die unteren. IEEE 754 Floats mit dem selben Vorzeichen (natürlich außer NaNs und so) haben die nette Eigenschaft, dass ihre Bitpattern als Integer interpretiert gleich bzw. genau umgekehrt geordnet sind wie die entsprechenden Floats…
Matthias Gubisch
Establishment
Beiträge: 470
Registriert: 01.03.2009, 19:09

Re: GLSL Compute Shader ImageWrite synchronisieren

Beitrag von Matthias Gubisch »

Arg, manchmal ist die Lösung echt naheliegend.

Manchmal sieht man einfach den Wald vor lauter Bäumen nicht.

Dann lässt sich das mit atiomicMin bzw atomicMax ja echt easy Lösen...

Danke euch, damit sollte sich dad Lösen lassen, leider kann ich erst am Montag wieder an dem Projekt arbeiten. Aber ich glaub damit bekomm ich es hin
Bevor man den Kopf schüttelt, sollte man sich vergewissern einen zu haben
Matthias Gubisch
Establishment
Beiträge: 470
Registriert: 01.03.2009, 19:09

Re: GLSL Compute Shader ImageWrite synchronisieren

Beitrag von Matthias Gubisch »

Also mit den 64 Bit Atomics funktioniert es zwar, allerdings brechen mir die Dinger Performancetechnisch das Genick...

War eh schon nicht schnell weil der Ansatz mit ein Dreieck pro Thread nicht besonders schnell ist, mit den 64Bit Atomics geht es aber nochmal Faktor 3 nach unten....
Bevor man den Kopf schüttelt, sollte man sich vergewissern einen zu haben
Benutzeravatar
Schrompf
Moderator
Beiträge: 4838
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: GLSL Compute Shader ImageWrite synchronisieren

Beitrag von Schrompf »

Aua. Mist.

Wie wär's mit "alle Dreiecke auf einmal pro Tile"? Du teilst Deinen Screen in Tiles auf, irgendwas Compute-Freundliches wie 32x32. Wobei Thread Groups glaube ich ne natürliche Größe von 64 haben, also müsste 8x8 das theoretische Optimum sein? Weiß ich nicht, ich spekuliere nur. Jedenfalls hast Du in so einem Tile dann einen Thread pro Fragment, der demzufolge Tiefe und Farbe lokal in Registern halten kann. Und in jedem Thread gehst Du dann über alle Dreiecke drüber und rasterst diesen einen Pixel davon aus. Am Ende kann die Thread Group dann ihre Farb- und Tiefenwerte als ganzen Block zurückschreiben und gar nix muss synchronisiert werden.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Matthias Gubisch
Establishment
Beiträge: 470
Registriert: 01.03.2009, 19:09

Re: GLSL Compute Shader ImageWrite synchronisieren

Beitrag von Matthias Gubisch »

Thread Group Size kommt auf den GPU Vendor an, bei AMD 64, bei Nvidia 32

Ja irgend sowas in der Art, erst mal einen groben Pass der die Dreiecke anhand ihrer AABB in die Tiles schiebt und dann das Rastern per Pixel, wie in deinem Vorschlag. Das schoene war da dann auch dass ich kein 64Bit Target brauche sondern Depth und Farbe getrennt voneinander halten kann.
Fasst halt grosse Dreiecke mehrfach an, aber vielleicht kann man da noch ein Pretransform der Vertices davor setzen dann spart man sich da auch wieder was...

Es bleibt Spannend ;)
Bevor man den Kopf schüttelt, sollte man sich vergewissern einen zu haben
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: GLSL Compute Shader ImageWrite synchronisieren

Beitrag von dot »

Schrompf hat geschrieben: 18.10.2022, 09:26 Wie wär's mit "alle Dreiecke auf einmal pro Tile"? Du teilst Deinen Screen in Tiles auf, irgendwas Compute-Freundliches wie 32x32. Wobei Thread Groups glaube ich ne natürliche Größe von 64 haben, also müsste 8x8 das theoretische Optimum sein? Weiß ich nicht, ich spekuliere nur. Jedenfalls hast Du in so einem Tile dann einen Thread pro Fragment, der demzufolge Tiefe und Farbe lokal in Registern halten kann. Und in jedem Thread gehst Du dann über alle Dreiecke drüber und rasterst diesen einen Pixel davon aus. Am Ende kann die Thread Group dann ihre Farb- und Tiefenwerte als ganzen Block zurückschreiben und gar nix muss synchronisiert werden.
yup

https://research.nvidia.com/publication ... ation-gpus

https://www.tugraz.at/institute/icg/res ... ines/cure/

;)
Antworten