SpriteBatch SpriteSortMode

Vor längerer Zeit hatte ich bereits einen Artikel über den SpriteBatch des XNA-Framework geschrieben und einige grundlegende Dinge erklärt. Damals hatte ich versprochen, daß ich dieses Thema wieder aufgreifen möchte und noch weitere Grundlagen vermitteln möchte. Dies möchte ich hiermit nachholen, da mich ein Foreneintrag im XNA.mag wieder an dieses Thema erinnert hat.

Wichtig ist zu wissen, wie der SpriteBatch arbeitet und vor allem wann der SpriteBatch was exakt macht. Diese Information ist wichtig um einige nette Dinge mit dem SpriteBatch machen zu können, wie z.B. eigene Pixel Shader zu verwenden. Glücklicherweise hat sich dabei praktisch nichts zwischen XNA 3.1 und 4.0 verändert und die Informationen sind nach wie vor gültig.

Erst einmal benötigen wir wieder ein paar Grundlagen zum SpriteBatch. Der SpriteBatch stellt Texturen im 2D-Raum dar und nennt diese Sprites (da es keine echten Sprites mehr gibt). Dazu werden ganz einfach ein Rechteck (bestehend aus zwei Dreiecken) mit einer Textur versehen und mit einer orthographischen Projektion (weiter hinten liegende Objekte werden also nicht kleiner wie bei einer perspektivischen Projektion) auf dem Bildschirm dargestellt.

Um dies möglichst schnell zu machen, nimmt der SpriteBatch einige Optimierungen vor, insbesondere eine Minimierung der RenderState-Changes. Dazu speichert der SpriteBatch soviele Sprite-Draw-Aufrufe wie möglich und rendert diese dann in einem Rutsch viel schneller, als dies einzeln möglich wäre. Unter Umständen werden dabei die Sprites in einer anderen Reihenfolge sortiert, dazu aber später mehr.

Um einen definierten Rahmen für diese Aufgabe zu stecken, werden alle Draw-Aufrufe in eine .Begin / .End – Klammer eingeschlossen, so daß eine typische Verwendung von SpriteBatch wie folgt aussieht (Pseudo-Code):

SpriteBatch.Begin();

SpriteBatch.Draw(); // für jedes Sprite ein Aufruf

SpriteBatch.End();

Interessant ist dabei die Tatsache, daß bei jedem dieser Aufrufe, also bei Begin, bei End und auch bei Draw bestimmte Aktionen durchgeführt werden, die aber maßgeblich durch die Verwendung eines bestimmten SpriteSortMode verändert werden. Genau um diese geht es in diesem Artikel und daher werde ich diese nun im Detail erklären.

Texture

Dies ist die Standardeinstellung des SpriteBatch ohne Parameter und besagt, daß die Sprites nach Texturen sortiert werden sollen.

Die gesamte Arbeit des SpriteBatch erfolgt im .End-Aufruf und zwar in folgender Reihenfolge:

1. Sprites aller Draw-Aufrufe seit dem letzten Begin nach verwendeter Textur sortieren
2. benötigte Render States setzen
3. benötigte Textur setzen
4. alle Sprites mit der aktuellen Textur zeichnen
5. bei Punkt 3. fortfahren, solange bis alle Sprites gezeichnet wurden.

Man sieht also, daß man keinerlei Einfluss auf die internen Einstellungen des SpriteBatch hat. In dieser Einstellung kann man weder Effekte ändern, noch die Reihenfolge der Draw-Aufrufe selbst beeinflussen, da die gesamte Arbeit sich im .End-Aufruf versteckt. Dafür ist diese Einstellung jedoch meistens am schnellsten. Der einzige negative Overhead der hier auftritt, entsteht durch die Sortierung nach Textur. Das sortieren ist dabei aber in der Regel schneller, als die Nachteile die durch unsortierte Draw-Aufrufe entstehen würden.

FrontToBack und BackToFront

Diese beiden Modi funktionieren exakt so, wie auch die Sortierung nach Textur, allerdings wird hier nicht nach Textur sortiert, sondern nach der Tiefe, die über den LayerDepth-Parameter eingestellt wird.

Bei FrontToBack wird von vorne nach hinten sortiert und bei BackToFront genau umgekehrt.

Dies ist in der Regel langsamer als die Sortierung nach Texturen, allerdings teilweise sehr interessant und dabei unterscheiden sich die beiden Modi dann doch ein wenig.

BackToFront wird verwendet, um transparente Objekte zu zeichnen. Um eine korrekte Darstellung zu erreichen, benötigt man für transparente, insbesondere halbtransparente (z.B. farbiges Glas) Objekte eine Sortierung von hinten nach vorne. Das entfernteste Objekt wird also zuerst gezeichnet und man arbeitet sich nach vorne durch.

FrontToBack kann sinnvoll sein, wenn man viel Overdraw hat. Nähere Informationen dazu in meinem Artikel Early Z Culling.

Deferred

Dieser Modus ist auch sehr leicht zu erklären. Die gesamte Arbeit wird wieder im Aufruf von .End vorgenommen, allerdings mit einem Unterschied: Die Sprites werden nicht sortiert, d.h. alle Sprites werden in der Reihenfolge gezeichnet, wie die Draw-Aufrufe erfolgt sind.

Was hat dies für einen Sinn? Ist dies nicht langsamer? Ja, grundsätzlich ist es langsamer, allerding nicht in jedem Fall. Wenn z.B. die Sprites bereits nach Tiefe und/oder Textur sortiert sind, dann spart man sich durch die Verwendung von Deferred die Sortierung. Bei also bereits vorsortierten Sprites ist Deferred ein klein wenig schneller als die zuvor besprochenen Modi.

Immediate

Dies ist ein sehr interessanter Modus, der uns die meiste Kontroller überlässt und aus diesem Grund auch etwas anders funktioniert als die anderen Modi.

Der erste Unterschied ist in Begin zu finden. Bereits hier werden alle notwendigen Render States gesetzt, sowie der Effekt, der verwendet werden soll. Dies versetzt uns in die Lage, daß wir die RenderStates und sogar den verwendeten Effekt beliebig abändern können, da diese nicht erst beim Aufruf von .End gemeinsam mit dem Zeichnen gesetzt werden.

Der nächste Unterschied ist, daß bei einem Aufruf von Draw ein Sprite sofort mit den aktuellen Einstellungen gezeichnet wird. Dies versetzt uns also in die Lage die RenderStates und den verwendeten Effekt nach jedem Sprite zu ändern. Wir sind also sehr flexibel.

Der Aufruf von End räumt hier lediglich auf und spielt nur eine untergeordnete Rolle.

Diese zusätzliche Kontrolle versetzt uns zwar in die Lage, daß wir vieles selbst bestimmen können, nimmt uns aber auch in die Pflicht: Um eine annehmbare Performance zu erreichen, müssen wir selbst die Sprites sortieren, um möglichst wenig Änderungen in der Begin/End-Klammer vornehmen zu müssen. Draw arbeitet jetzt deutlich weniger effektiv, was auch der große Nachteil von SpriteSortMode.Immediate ist. Unter Umständen macht es Sinn, den SpriteBatch mit unterschiedlichen Einstellungen mehrfach zu starten, wenn nur ein kleiner Teil der Sprites einen eigenen Effekt benötigt.

Fazit

In diesem Artikel habe ich versucht, ein wenig Licht auf die Betriebsmodi des SpriteBatch zu werfen und zu erklären, wann welcher Modus am besten Anwendung findet. Ich hoffe, daß dies gelungen ist und stehe in den Diskussionen natürlich wie immer für Fragen, Anregungen und Kritiken gerne zur Verfügung.

Advertisements

Veröffentlicht am 21.02.2011 in Grundlagen, XNA, XNA 3.1, XNA 4.0 und mit , , , getaggt. Setze ein Lesezeichen auf den Permalink. 7 Kommentare.

  1. Danke Glatzemann! Der Beitrag hat mir grad echt weiter geholfen.
    LG

  2. Ist das auch nicht für 3D Objekte nötig? Das Batching meine ich.

    • Ich habe die Frage nicht ganz verstanden… Ich versuche trotzdem eine Antwort: Batching ist grundsätzlich für alle Arten von Ausgaben über die Grafikkarte wichtig, da nur so eine optimale Geschwindigkeit erreicht werden kann. Die „Sprites“ die der SpriteBatch rendert sind im Grunde genommen auch nur 3D-Objekte (zwei Dreiecke), die ein Rechteck bilden, dass parallel zur Kamera gehalten wird.

      Ich hoffe, dass ich damit deine Frage beantworten konnte, ansonsten einfach die Frage nochmal konkretisieren.

      • Stimmt, du hast recht 🙂 auch Sprites sind nur 3D Objekte. Deine Antwort hilft mir auch schon etwas weiter.

        Auch andere 3D Objekte müssen ->

        1. RenderStates setzten
        2. Texturen setzen
        3. Objekte mit der aktuellen Textur rendern
        4. Weiter bei 2 bis alle Objekte gerendert sind

        Sollte man noch etwas beachten? Was ist z.B. mit Shadern. Wo würden die reinpassen?

        Danke für die schnelle Antwort.

  3. TheoTechnic

    Danke,
    hat mir sehr weitergeholfen 🙂

  1. Pingback: SpriteBatch Magic: Grundlagen « "Mit ohne Haare"

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s

%d Bloggern gefällt das: