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.
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?