Early Z Culling, UAVs und Tiefentests

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

Early Z Culling, UAVs und Tiefentests

Beitrag von eXile »

Warum gibt es eigentlich keinen post-Pixel-Shader Z-Reject für UAVs?
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Jammer-Thread

Beitrag von CodingCat »

Weil dann längst alles zu spät ist. Wer weiß, was in der Zwischenzeit reingeschrieben wurde.

Nebenbei: \cite, \ref, \emph, there, their, then, than, alles verschwimmt ... aber die Müdigkeit hilft, einfachere Sätze zu konstruieren.
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
eXile
Establishment
Beiträge: 1136
Registriert: 28.02.2009, 13:27

Re: Jammer-Thread

Beitrag von eXile »

CodingCat hat geschrieben:Weil dann längst alles zu spät ist. Wer weiß, was in der Zwischenzeit reingeschrieben wurde.
UAVs in Zukunft ein Flag geben (write-through on z-pass; im Pixel-Shader selbst ist die UAV nur read-only), und wenn der Pixel-Shader den z-Test passiert, den write-through durchführen. Natürlich sind das Einschränkungen; aber dennoch würde das die Verwendbarkeit von UAVs wohl doch einigermaßen erhöhen.
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Jammer-Thread

Beitrag von CodingCat »

Kostet aber vermutlich Zusatz-Hardware, weil der ganze Kram dann zwischengespeichert werden muss?
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
eXile
Establishment
Beiträge: 1136
Registriert: 28.02.2009, 13:27

Re: Jammer-Thread

Beitrag von eXile »

CodingCat hat geschrieben:Kostet aber vermutlich Zusatz-Hardware, weil der ganze Kram dann zwischengespeichert werden muss?
Ja natürlich kosten Features Zusatz-Hardware; wenn ich die flickernde Scheiße hier betrachte wäre mir das das aber wert. Der einzige Ausweg ist wohl, wie es mir aktuell erscheint, ein z-Prepass.
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Jammer-Thread

Beitrag von CodingCat »

Du hast keine Möglichkeit, konservative Tiefen mit early-z rauszuschreiben?
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
dot
Establishment
Beiträge: 1746
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Jammer-Thread

Beitrag von dot »

Ist early-z in D3D11 denn garantiert? Das wäre mir sehr neu, normalerweise kann die GPU early-z Optimierungen nur unter ganz bestimmten Vorraussetzungen durchführen und auch dann nicht unbedingt auf per Fragment level. Es ist eben nur eine Optimierung, ich würde mich nie auf sowas als integralen Bestandteil meiner Programmlogik verlassen. Möglicherweise ist die GPU sogar gezwungen, early-z zu deaktivieren, wenn ein PixelShader in ein UAV schreibt!?
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Jammer-Thread

Beitrag von CodingCat »

dot: Ja, du kannst es mit [earlydepthstencil] vor dem Pixel Shader erzwingen. Und nein, Early-Z funktioniert auch bei UAV-Access. Wieso auch nicht, UAVs garantieren eh so gut wie nichts. Sogar eigene Tiefen rausschreiben geht mit Early-Z, solange du mit SV_Depth[Less|LessEqual|GreaterEqual|Greater] garantierst, dass deine Tiefen die für korrektes Early-Z notwendige Monotonie einhalten. (Über die von dir angesprochene Hi-Z-Optimierung sagt [earlydepthstencil] btw. nichts aus.)

eXile: Alternativ kannst du natürlich eigene Tiefenpufferung per UAV implementieren, z.B. so:

Code: Alles auswählen

int lastDepthOrLocked = 0;

do
{
   // Optional early-out, if beneficial
   if (depth >= abs(DepthBuffer[pos]))
      break;

   // Update depth buffer
   InterlockedMin(DepthBuffer[pos], depth, lastDepthOrLocked);

   if (depth < lastDepthOrLocked)
   {
      // Lock frame buffer via depth buffer
      int lastDepth = 0;
      InterlockedCompareExchange(DepthBuffer[pos], depth, -depth, lastDepth);

      if (lastDepth == depth)
      {
         // Update UAVs

         // Unlock frame buffer via depth buffer
         InterlockedExchange(DepthBuffer[pos], depth);
      }
   }

} while(lastDepthOrLocked < 0);
Das ist mit Sicherheit wesentlich langsamer als ROPs auf Render Targets, erlaubt dir aber atomares Update beliebig vieler Größen in Abhängigkeit des Tiefentests.
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
eXile
Establishment
Beiträge: 1136
Registriert: 28.02.2009, 13:27

Re: Jammer-Thread

Beitrag von eXile »

Ich hatte gestern Abend fast das gleiche implementiert; auch wenn ich eine eigene Spin-Lock-UAV benutzt hatte (weil ich die Tiefen gerade noch als floats speichere). Irgendwo steckt hier aber noch der Wurm drin: Treiber-Resets und sogar zwei Neustarts (wegen eingefrorenem Systems trotz Watchdog) habe ich schon hinter mir.

Oh die wundervolle, mutige neue Welt der UAVs voller Treiberbugs. Vielen Dank für die Antwort, ich melde mich wieder wenn ich Benchmark-Zahlen habe.
Zuletzt geändert von eXile am 09.09.2012, 11:50, insgesamt 1-mal geändert.
Benutzeravatar
dot
Establishment
Beiträge: 1746
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Jammer-Thread

Beitrag von dot »

CodingCat hat geschrieben:dot: Ja, du kannst es mit [earlydepthstencil] vor dem Pixel Shader erzwingen. Und nein, Early-Z funktioniert auch bei UAV-Access. Wieso auch nicht, UAVs garantieren eh so gut wie nichts. Sogar eigene Tiefen rausschreiben geht mit Early-Z, solange du mit SV_Depth[Less|LessEqual|GreaterEqual|Greater] garantierst, dass deine Tiefen die für korrektes Early-Z notwendige Monotonie einhalten. (Über die von dir angesprochene Hi-Z-Optimierung sagt [earlydepthstencil] btw. nichts aus.)
Naja, es wohl offenbar genau wie ich vermutet hab:
MSDN hat geschrieben:An application can force early depth-stencil testing by supplying the attribute or the hardware may execute early depth-stencil testing provided no depth values are written and no unordered access operations are performed in a shader as an optimization.
Ist imo auch logisch, da eine Optimierung nicht einfach so das beobachtbare Verhalten des Programms verändern sollte. Allerdings kannst du es wohl mit [earlydepthstencil] erzwingen, das is inzwischen wohl irgendwie wieder aus meinem Bewusstsein verschwunden. Ich frag mich allerdings, wie das bei [earlydepthstencil] bezüglich der ganzen klassischen Einschränkungen von wegen Fragment discard, depth compare mode etc. ist...
Zuletzt geändert von dot am 09.09.2012, 12:00, insgesamt 3-mal geändert.
Benutzeravatar
eXile
Establishment
Beiträge: 1136
Registriert: 28.02.2009, 13:27

Re: Jammer-Thread

Beitrag von eXile »

Um es noch einmal klar zu machen: [earlydepthstencil] benutze ich natürlich schon; dennoch können Dreiecke bei konkaven Objekten aufpoppen.
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Jammer-Thread

Beitrag von CodingCat »

Klar, die Idee hinter [earlydepthstencil] ist ja gerade, dass die Anwendung erklären kann, dass sie mit Early-Z-Culling umgehen kann.
eXile hat geschrieben:Ich hatte gestern Abend fast das gleiche implementiert; auch wenn ich eine eigene Spin-Lock-UAV benutzt hatte (weil ich die Tiefen gerade noch als floats speichere). Irgendwo steckt hier aber noch der Wurm drin: Treiber-Resets und sogar zwei Neustarts (wegen eingefrorenem Systems trotz Watchdog) habe ich schon hinter mir.
Vorsicht mit Spin Locks: Immer im Locked-Fall Instruktionen überspringen und im Unlocked-Fall "spinnen" (umgekehrt zu typischen Spin Locks auf CPU, siehe Beispiel), andernfalls haut dich das SIMT-Modell direkt in den Deadlock. (Im Locked-Fall spinnende Threads würden sämtliche Unlocked-Threads in derselben Thread-Gruppe blockieren.)
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
dot
Establishment
Beiträge: 1746
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Jammer-Thread

Beitrag von dot »

Wenn ihr eigenes Depth Buffering implementieren wollt, ist ein gängiger Trick, einfach atomicMin() bzw. atomicMax() zu benutzen. Ja, das gibts nicht für floats, aber IEEE floats haben die nette Eigenschaft, dass das relative Ordering ihrer Bitpattern mit dem Ordering der float Werte übereinstimmt... ;)
Zuletzt geändert von dot am 09.09.2012, 12:16, insgesamt 1-mal geändert.
Benutzeravatar
eXile
Establishment
Beiträge: 1136
Registriert: 28.02.2009, 13:27

Re: Jammer-Thread

Beitrag von eXile »

Ja, das wäre schon. Geht aber nicht auf floats:

Code: Alles auswählen

error X3681: ps_5_0 only supports interlocked operations on scalar [cn]int[/cn] or 
[cn]uint[/cn] data
Nachtrag: Ach herrje das sagst du ja selbst schon. Ich hab's auch nicht so mit dem Lesen.

Ich muss gleich ganz schnell weg; heute Abend geht's weiter. Thread darf abgetrennt werden.
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Jammer-Thread

Beitrag von CodingCat »

dot hat geschrieben:Wenn ihr eigenes Depth Buffering implementieren wollt, ist ein gängiger Trick, einfach atomicMin() bzw. atomicMax() zu benutzen. Ja, das gibts nicht für floats, aber IEEE floats haben die nette Eigenschaft, dass das relative Ordering ihrer Bitpattern mit dem Ordering der float Werte übereinstimmt... ;)
Das tut mein Depth Buffering Code oben bereits. Das Problem dabei ist, den ganzen Rest in Abhängigkeit des Tiefentests atomar zu aktualisieren, siehe Code-Snippet.
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
dot
Establishment
Beiträge: 1746
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Jammer-Thread

Beitrag von dot »

Ja, mir ging es vor allem um das Problem mit atomicMin() auf float. Allerdings funktioniert das auch nur unter gewissen Einschränkungen, da das Ordering nur für eine Interpretation als signed Magnitude Integer ganz stimmt, nicht fürs Zweierkomplement. Aber unter Umständen gut genug...
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Early Z Culling, UAVs und Tiefentests

Beitrag von CodingCat »

Ja, für Tiefentest ist das eigentlich immer gut genug, weil Tiefen selten negative sind. Ansonsten Hilfsfunktionen:

Code: Alles auswählen

/// Converts the given float into an integer bit representation maintaining relative order.
int asint_ordered(float f)
{
	int i = asint(f);
	if (i < 0)
		i = (1 << 31) - i;
	return i;
}
int2 asint_ordered(float2 f) { return int2(asint_ordered(f.x), asint_ordered(f.y)); }
int3 asint_ordered(float3 f) { return int3(asint_ordered(f.x), asint_ordered(f.y), asint_ordered(f.z)); }
int4 asint_ordered(float4 f) { return int4(asint_ordered(f.x), asint_ordered(f.y), asint_ordered(f.z), asint_ordered(f.w)); }

/// Converts the given ordered integer bit representation into the corresponding float.
float asfloat_ordered(int i)
{
	if (i < 0)
		i = (1 << 31) - i;
	return asfloat(i);
}
float2 asfloat_ordered(int2 i) { return float2(asfloat_ordered(i.x), asfloat_ordered(i.y)); }
float3 asfloat_ordered(int3 i) { return float3(asfloat_ordered(i.x), asfloat_ordered(i.y), asfloat_ordered(i.z)); }
float4 asfloat_ordered(int4 i) { return float4(asfloat_ordered(i.x), asfloat_ordered(i.y), asfloat_ordered(i.z), asfloat_ordered(i.w)); }
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Jammer-Thread

Beitrag von CodingCat »

CodingCat hat geschrieben:eXile: Alternativ kannst du natürlich eigene Tiefenpufferung per UAV implementieren, z.B. so:

Code: Alles auswählen

int lastDepthOrLocked = 0;

do
{
   // Optional early-out, if beneficial
   if (depth >= abs(DepthBuffer[pos]))
      break;

   // Update depth buffer
   InterlockedMin(DepthBuffer[pos], depth, lastDepthOrLocked);

   if (depth < lastDepthOrLocked)
   {
      // Lock frame buffer via depth buffer
      int lastDepth = 0;
      InterlockedCompareExchange(DepthBuffer[pos], depth, -depth, lastDepth);

      if (lastDepth == depth)
      {
         // Update UAVs

         // Unlock frame buffer via depth buffer
         InterlockedExchange(DepthBuffer[pos], depth);
      }
   }

} while(lastDepthOrLocked < 0);
Das ist mit Sicherheit wesentlich langsamer als ROPs auf Render Targets, erlaubt dir aber atomares Update beliebig vieler Größen in Abhängigkeit des Tiefentests.
Ich habe gerade eben testweise sämtliche Synchronisation aus diesem Code genommen:

Code: Alles auswählen

// Optional early-out, if beneficial
if (depth >= abs(DepthBuffer[pos]))
   break;

// Update depth buffer
int lastDistBits = 0;
InterlockedMin(DepthBuffer[pos], depth, lastDistBits);

if (depth < lastDistBits)
{
   // Update UAVs
}
Das Erschreckende? Er funktioniert scheinbar genauso zuverlässig, in einem 10tel der Zeit! Ich habe keine Ahnung, wie zuverlässig, es gab schließlich nie Garantien zu Ausführungs- und Schreibreihenfolge. Für den Moment sieht es jedoch so aus, als ob Schreibaufträge weitestgehend in der Reihenfolge abgearbeitet würden in der sie aufgegeben wurden.
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Antworten