Verwenden eines Schaltbefehls in einem Shader gegen mehrere Shader

7

Wenn Sie mehrere Shader haben, können Sie, anstatt ständig zwischen Shadern zu wechseln, nur einen Shader mit einem Schalter haben und jeder Fall wird Sie zu einer anderen Shader-Funktion weiterleiten, die Sie für dieses bestimmte Objekt benötigen? (Und Sie würden einen Aufzählungswert durch Ihren konstanten Puffer für dieses bestimmte Objekt senden.)

Ich denke die eigentliche Frage ist. Was würde die meiste Leistung in Anspruch nehmen, das switchoder das Umschalten zwischen Shadern?

Beispiel:

BigShader(){
   switch(a){
      case 0 :
        { return transPSShader();}
      case 1:
        { return nonTransPSShader();}
ect.
}
Fredrik Boston Westman
quelle
4
Denken Sie darüber nach: Das switch()muss jedes Mal ausgewertet werden, wenn der Shader ausgeführt wird, dh für jedes gezeichnete Pixel. Wenn Sie Ihre Shader getrennt halten, gibt es keine zusätzliche Arbeit pro Pixel. Aber nehmen Sie nicht mein Wort dafür ... warum nicht beide Versionen codieren und die Leistung messen?
Nathan Reed
hmm du hast da einen Punkt, habe nicht darüber nachgedacht, dass es jedes Pixel machen muss. Aber vergessen wir dann den Pixel-Shader, wie wäre es mit dem Vertex-Shader (und ich glaube, es gibt einen Geometrie-Shader? Srry im kinda new). Werden sie pro Pixel oder pro Objekt aufgerufen?
Fredrik Boston Westman
2
Der Vertex-Shader wird pro Vertex ausgeführt. Nehmen Sie ein Objekt mit 1.000 Scheitelpunkten - das bedeutet, dass Ihr Switch 1.000 Mal aufgerufen werden muss - gegenüber nur einer Shader-Änderung.
Maximus Minimus
Das stimmt nicht genau. GPU ist keine CPU. Die GPU arbeitet parallel. Viele Threads (ich denke 16-32) arbeiten mit derselben Anweisung zur selben Zeit. Und wenn es den gleichen Wert auswertet, geht es auch auf die gleiche Weise weiter (in Ihrem Schalter). Wenn Sie jedoch viele "Fall" -Zweige haben, ist dies immer noch langsam - viele Bedingungen müssen bewertet werden.
Zacharmarz
Die Technik, einen Shader mit vielen Verhaltensweisen zu erstellen, wird allgemein als "Uber-Shader" bezeichnet. Dies kann Ihnen helfen, zusätzliche Informationen über Google zu finden.
Sean Middleditch

Antworten:

7

In der Regel werden Verzweigungen jeglicher Art (Schalter, if-Anweisungen, Schleifen mit nicht konstanten Iterationen) am besten vermieden. Dies trifft auf den PC nur geringfügig zu (was nicht ausreicht, um sich außerhalb sehr enger innerer Schleifen Sorgen zu machen), insbesondere auf einige Allzweck-CPUs wie den Xenon des 360 (gängige Hardware, die indirekte Verweise auf Zweige in modernen Super-Umgebungen tolerierbar macht) - Skalare Deep-Pipeline-CPUs außerhalb der Reihenfolge wurden vom Xenon weggelassen, um Kosten zu sparen. Dies gilt insbesondere für GPUs.

GPUs sind ganz besondere Tiere. Sie funktionieren nicht wie eine Allzweck-CPU. Sie führen möglicherweise Tausende von Kopien eines Shaders gleichzeitig aus, und die Hardware unterliegt Einschränkungen, um dies zu ermöglichen. Eine dieser Einschränkungen besteht darin, dass mehrere Ausführungskerne Ressourcen gemeinsam nutzen. Nehmen wir zum Beispiel an, 4 Kerne sind auf unserer hypothetischen GPU so miteinander verbunden.

Zu jeder Zeit führen vier "Core" einen Shader im Lock-Step aus. Sie teilen sich einen Anweisungszeiger. Sie teilen eine Art Registerdatei. Das SIMD-Verhalten Ihrer Shader entspricht nicht dem SIMD-Verhalten auf der CPU, das normalerweise in Spielen verwendet wird. Jeder Shader führt keine Vier-Wege-Vektoroperationen gleichzeitig aus, sondern alle vier Kerne arbeiten mit einer einzelnen Komponente aus den vier verschiedenen Datenströmen gleichzeitig. Diese vier Kerne sind eng miteinander verbunden.

Der gemeinsame Anweisungszeiger ist der Schlüssel. Wenn zwei Ihrer Shader in dieser Gruppe Switch-Fall 1 und die anderen beiden Switch-Fall 2 ausführen, müssen alle vier Kerne beide Switch-Fälle ausführen! Prädizierte Anweisungen werden verwendet, um sicherzustellen, dass die Ergebnisse der Anweisungen im "Aus" -Fall für einen bestimmten Kern ignoriert werden. Es dauert jedoch immer noch einige Zeit, die Anweisung auszuführen und Speicher- / Register- / Texturzugriffe durchzuführen (weshalb Sie dies tun sollten) Führen Sie Textur-Lookups nur in einheitlichen Codepfaden durch.

Daher sind Zweige "langsam" in der Tatsache, dass Ihre Hardware wirklich nicht ausgelastet ist. Ein möglicherweise sehr großer Teil davon verbringt Zeit damit, Anweisungen zu bewerten, die keine Wirkung haben. Dies unterscheidet sich von dem CPU-Fall, in dem Zweige aufgrund von Pipeline-Verzögerungen und falschen Vorhersagen verletzt werden. Die GPU verfügt ohnehin oft über sehr eingeschränkte Verzweigungsfunktionen.

Ist das "langsamer" als das Austauschen von Shadern? Kommt darauf an. Wenn Sie Ihre Zeichenbefehle so stapeln, dass Sie alle Zeichenvorgänge mit einem bestimmten Shader hintereinander ausführen (Sie wechseln also nicht von Shader A zu Shader B und dann zurück zu Shader A, sondern zeichnen alle mit Shader A und erst dann braucht das Zeichnen Shader B) ... es kommt immer noch darauf an, aber es wird wahrscheinlich schneller mit dieser Stapelverarbeitung. Wie alles, was mit der Leistung zu tun hat, müssen Sie diese speziell für Ihre Anwendung und Zielhardware testen und herausfinden. Wenn Ihre switch-Anweisungen einfach genug sind, stellen Sie möglicherweise fest, dass sie tatsächlich schneller verwendet werden können.

Oft ist es ohnehin besser , Objekte mit identischen Materialeigenschaften (gleiche Shader, Texturen, Puffer für Materialkonstanten usw.) zu stapeln, um zu vermeiden, dass aktive Ressourcen geändert werden, selbst wenn ein Uber-Shader verwendet wird. Am unteren Ende der Grafikskala ist es oft nicht schwer, die Shader aufzubrechen, um damit gut zu spielen. Bei mehreren Materialtypen in einem verzögerten Schattierungskontext kann es etwas schwieriger werden, und hier wird normalerweise ein Semi-Uber-Shader-Ansatz gewählt (häufig nur für BRDF-Berechnungen und dergleichen).

Beachten Sie, dass Engines wie CryTek den Uber-Shader-Ansatz gewählt haben (unsicher, ob die neuesten Inkarnationen dies noch tun), sodass sie sicherlich für sehr High-End-Spiele in der realen Welt verwendet werden können.

Sean Middleditch
quelle
Super Antwort :) Mit Uber Shader beziehen Sie sich auf das, was ich Big Shader nenne?
Fredrik Boston Westman
Aktuelle GPUs haben eher eine 32-breite oder 64-breite SIMD, nicht nur eine 4-breite. :)
Nathan Reed
1
@FredrikBostonWestman: Ja. Sie sind besser dran, wenn Sie die gebräuchlichere Terminologie verwenden, wenn die Google-Suche aus keinem anderen Grund fruchtbarer ist.
Sean Middleditch
Was ist mit einem Schalter an einer Uniform, die immer den gleichen Wert hat? Wie ein Nebelmodusparameter, der sich nur sehr selten ändern würde.
jjxtra