Optimieren Sie Statusänderungen in einer Renderwarteschlange

7

Ich überarbeite einige Teile der Spiel-Engine, an der ich arbeite. Diese Engine wird in C # mit XNA hergestellt.

Der Teil, mit dem ich Probleme habe, ist Shader / Material und die Render-Warteschlange. In der neuen Version werde ich die Möglichkeit geben, unterschiedliche Renderzustände pro Durchgang innerhalb einer Technik anzugeben (Mischzustand, Rasterzustandszustand, ...). In meiner Engine haben alle Renderables ein Material und jedes Material ist einer Technik zugeordnet (also einem oder mehreren Durchgängen).

Später, wenn das Spiel ausgeführt wird, füge ich der Renderwarteschlange renderbare Objekte hinzu und sortiere sie dann nach dem von jedem Material verwendeten Durchgang. Was ich erreichen möchte, ist, Zustandsänderungen zwischen Draw Calls zu reduzieren, indem zuerst dieselben Durchgänge zusammengefasst und dann mit nahezu identischen Zuständen so geschlossen wie möglich geschlossen werden, um Änderungen zu minimieren. Diese Lösung scheint jedoch problematisch zu sein, wenn es darum geht, Techniken mit mehr als einem Durchgang mit völlig unterschiedlichen Zuständen zu verwenden. Durchläufe innerhalb derselben Technik können in der Renderwarteschlange vollständig getrennt und nicht nacheinander gerendert werden. Wenn ich sage, dass es daran liegt, dass ich immer gesehen habe, wie die Technik mit mehreren Durchgängen in einer Schleife gerendert wird und alle Durchgänge sofort so gerendert werden:

foreach(EffectPass pass in technique.Passes)
{
    /* Update parameters */

    pass.apply();
    graphicsDevice.Draw(...);
}

Ich weiß jedoch nicht, ob ich das Pass-Rendering mit derselben Technik verschieben kann, anstatt sie wie im vorherigen Beispiel nacheinander zu verwenden.

Hier ist ein Beispiel dafür, was wahrscheinlich mit der Lösung passieren wird, die ich zu erstellen versuche:

  • Shader1
    • TechnikA
      • Pass1 (Zustand A)
      • Pass2 (Zustand C)
  • Shader2
    • TechnikB
      • Pass1 (Zustand A)
  • Shader3
    • TechnikC
      • Pass1 (Zustand A)

Jetzt muss ich drei Modelle zeichnen, Modell1, Modell2 und Modell3, die jeweils eine andere Technik als die vorherige Liste verwenden. Wenn ich diese drei Modelle in die Renderwarteschlange einfüge, wird die folgende Renderwarteschlange angezeigt:

-> Status A einstellen -> Modell1 zeichnen (Pass1) -> Modell2 zeichnen (Pass1) -> Modell3 zeichnen (Pass1) -> Status C setzen -> Modell1 zeichnen (Pass2)

Wir können sehen, dass das Modell1 zweimal gezeichnet wird (zwei Durchgänge für Technik A), aber der Zeichnungsaufruf wird durch einen anderen Zeichnungsaufruf getrennt.

Ich weiß nicht, wie ich das lösen soll. Was ist eine gute Lösung für dieses Problem? Wie kann ich meine Renderwarteschlange so gestalten, dass Statusänderungen zwischen Zeichnungsaufrufen so weit wie möglich reduziert werden?

BEARBEITEN:

Nachdem ich ein bisschen mehr gesucht habe, habe ich diesen hilfreichen Artikel gefunden: http://realtimecollisiondetection.net/blog/?p=86 . Es scheint eine elegante Art zu sein, Objekte nach Tiefe und Materialien zu sortieren. Und in der Material-ID kann ich eine Pass-ID haben, um denselben Pass zusammen zu sortieren, um Statusänderungen zu reduzieren.

Derzeit zeichnet mein Renderer Objekte direkt im Backbuffer ohne Licht oder Schatten, alles ist flach. Also werde ich einfach meine Renderwarteschlange durchlaufen und Zeichenmethoden aufrufen. Aber in Zukunft werde ich wahrscheinlich das verzögerte Rendern implementieren.

Ich weiß nicht viel darüber, aber ich weiß, dass ich meine Objekte in verschiedenen Renderzielen rendern muss, um verschiedene Informationen zu sammeln (Z-Puffer, Normaler Puffer, G-Puffer, ...). Ich gehe davon aus, dass ich für Z-Puffer und normalen Puffer für alle Objekte den gleichen Shader verwenden werde, unabhängig davon, welches Material sie verwenden. Aber für den G-Puffer werde ich die Materialien jedes Objekts verwenden, was unterschiedliche Shader mit unterschiedlicher Anzahl von Durchgängen bedeutet. Wenn ich die Lösung im vorherigen Link verwende und ein Objekt mit einem Material habe, das zwei oder mehr Durchgänge ausführen muss, hat meine Renderwarteschlange mindestens das Zweifache des Objekts (einen für jeden Durchgang). Aber für Z-Puffer oder normalen Puffer ist es nicht nutzlos, dasselbe Objekt mehrmals zu haben?

Was ich sehe, ist, dass ich mindestens zwei verschiedene Renderwarteschlangen haben muss: eine für den G-Puffer, in der sich ein Objekt je nach Anzahl der Durchgänge, die das Material benötigt, mehrmals befinden kann, und eine andere Renderwarteschlange für den Z-Puffer oder einen anderen Puffer in denen jedes Objekt nur einmal ist. Habe ich recht?

Julien Pires
quelle

Antworten:

4

Die Zeichenreihenfolge wird häufig in Tutorials impliziert, in denen Sie Folgendes tun:

for each object:
    for each pass:
        apply pass state
        draw object

ist eigentlich rückwärts von dem, wie es sinnvoll ist, es in einem "echten Spiel" -Kontext zu tun. Eher würden Sie eher etwas tun wie:

for each pass:
    apply pass state
    for each object (grouped by shader/material/mesh/etc):
        draw object

Mehrere Durchgänge pro Objekt sind fast nie das, was Sie tatsächlich wollen. Stattdessen möchten Sie alle Objekte akkumulieren, die einen bestimmten Durchgang verwenden, und dann alle diese Objekte zusammen zeichnen.

Einige häufig vorkommende Ausweise sind beispielsweise:

  • Tiefenvorab
  • Schattenkartenpass
  • Aufgeschobener Schattierungsdurchlauf
  • Aufkleberpass
  • Beleuchtungspass
  • Vorwärtsschattierungspass
  • Transluzenz-Pass
  • Nachbearbeitungspass
  • UI-Pass

Nicht jeder Shader enthält jeden dieser Durchgänge. In einem verzögerten Renderer verwendet beispielsweise ein typischer Shader für "undurchsichtige Objekte" möglicherweise nur die ersten drei Durchgänge, ein Licht-Shader verwendet nur den Beleuchtungsdurchlauf usw.

In solchen Fällen möchten Sie natürlich alle Objekte in der Tiefe vor dem Durchlauf zusammen zeichnen, sodass Sie eine Renderliste erstellen, die alle diese Objekte enthält, dann den Durchlaufstatus einmal anwenden und sie zusammen zeichnen. Anschließend wenden Sie den Passstatus der Schattenkarte an und zeichnen alle Objekte, die in der Schattenkarte enthalten sind, und so weiter.

Es gibt nur seltene Sonderfälle, in denen Sie tatsächlich Objektdurchläufe wünschen - beispielsweise Streuung unter der Oberfläche mithilfe der Texturraumdiffusion, bei der Sie die Beleuchtung des Objekts in ein Offscreen-Rendering-Ziel zeichnen, es verwischen und dann das Objekt mit der Unschärfe auf dem Bildschirm zeichnen Beleuchtung (3 Durchgänge pro Objekt). Diese Art von Dingen lässt sich nicht gut skalieren - es wird sehr teuer, wenn Sie Objekte hinzufügen -, daher versuchen Engine-Programmierer, dies so weit wie möglich zu vermeiden.

Nathan Reed
quelle
Vielen Dank für Ihre Antwort, es hat mir geholfen, über mein Problem nachzudenken. Inzwischen habe ich einen interessanten Artikel über eine Methode zum Sortieren einer Renderwarteschlange gefunden. Ich denke darüber nach, es zu implementieren, aber ich habe viel mehr Fragen, deshalb habe ich meine Frage mit mehr Details und dem Link zum Artikel bearbeitet.
Julien Pires
@Takumi Was ich hier "pass" nenne, ähnelt eher dem, was in diesem Artikel als "Ebene" bezeichnet wird. Es wäre ziemlich ungewöhnlich, wenn ein Material mehr als einen verzögerten Durchgang hätte. Unterschiedliche Objekte haben unterschiedliche Materialien, aber sie werden alle einmal im verzögerten Durchgang gezeichnet. Und wie ich in meiner Antwort erwähnt habe, werden viele Materialien in einigen Durchgängen enthalten sein, in anderen jedoch nicht. Sie können weiterhin alles in einer Master-Render-Warteschlange behalten, solange es nach Pass sortiert ist.
Nathan Reed
1
@cubrman Wenn Sie das Effekt-Framework IIRC verwenden, müssen Sie Apply jedes Mal aufrufen, wenn Sie Parameterwerte ändern.
Nathan Reed
1
@cubrman Das würde ich nicht sagen. Das Aktualisieren von Shader-Parametern ist eine der leichtesten Statusänderungen, daher würde ich mir darüber keine Sorgen machen. Sie können die teuren Statusänderungen (Shader wechseln, Renderziele wechseln) weiterhin minimieren , indem Sie Ihre Objekte nach Shader gruppieren. Und Sie können das Wechseln von Texturen, Scheitelpunktpuffern usw. (mittel teuer) vermeiden, indem Sie Ihre Objekte innerhalb der Gruppierung nach Shader weiter nach Material und Netz gruppieren.
Nathan Reed
1
@cubrman Ja, das Ändern von Techniken oder Durchläufen innerhalb einer Effektdatei ändert die Shader. Jedes Mal, wenn Sie zu einem anderen individuellen Vertex- oder Pixel-Shader-Programm wechseln, werden die Shader geändert.
Nathan Reed