Ich stelle diese Frage, weil ich keine endgültige Antwort darauf gefunden habe.
Lassen Sie mich zunächst einige Dinge über das Spiel und das, was ich bereits getan habe, darlegen. Das Spiel wird ein RTS-Set in einer prozedural generierten Welt sein, in der Simplex-Rauschen verwendet wird. Die Welt besteht aus Blöcken mit einer Größe von 16 x 16, die aus Sprites mit einer Größe von 64 x 64 bestehen. Ich habe es geschafft, Blöcke dynamisch zu laden und zu entladen, was einwandfrei funktioniert. Die Welt wird ein bisschen wie Rimworld aussehen, also von oben nach unten mit verschiedenen Schichten von Sprites (Terrain zuerst, Übergangs-Sprites, Bäume, Abziehbilder usw.). Neu erzeugte Welten können Entitäten enthalten, die die Umwelt beeinflussen können (z. B. ein Dorf, das zu einer Stadt geworden ist) und damit den Brocken. Ich bin sicher, dass dies mit einer Funktion berechnet werden kann, aber es ist etwas zu beachten.
Das Hauptproblem, das ich habe, ist, dass beim Verkleinern immer mehr Kacheln gezeichnet werden, was die Leistung stark beeinträchtigt. Bei ungefähr 30000 Sprites dauert der Ziehabschnitt 8 ms, was der Hälfte dessen entspricht, was für den Betrieb mit 60 FPS erforderlich ist. Und das ist nur das Gelände. Ich verwende Texturatlanten, um die Anzahl der Ziehungen zu begrenzen (30000 Sprites, die in 6 Zählungen gezeichnet wurden).
Das Ziel ist es, von der Ebene von Stadt / Dorf / Stadt bis zum Sehen eines ganzen Landes herauszoomen zu können. Dies muss dynamisch erfolgen (z. B. nicht durch Klicken auf ein Minikartensymbol, sondern einfach wie in Supreme Commander zurückblättern).
Ich habe viele Artikel zu diesem Thema gelesen, aber ich habe kein klares Beispiel dafür gefunden oder gesehen, wo es funktionieren würde. Hier ist eine Liste der Techniken, die ich gefunden habe und die funktionieren sollen:
- Schmutzige Rechtecke, wie hier beschrieben , in denen Sie nur neues Material zeichnen und den Rest im Backbuffer aufbewahren. Das macht sehr viel Sinn, aber ich weiß nicht, wie ich das in Monogame implementieren soll.
- Meine bevorzugte Wahl: Verwenden Sie RenderTargets, wie hier beschrieben , in großem Umfang, um auf ein RenderTarget zu zeichnen und es dann als Textur zu speichern. In meinem Fall würde ein 16 x 16-Block, der aus 64 x 64 besteht, eine 1024 x 1024-Textur erzeugen. Ich bezweifle wirklich, dass dies in Bezug auf die Leistung funktionieren wird, aber das Ergebnis würde aus sehr detaillierten Texturen bestehen und ist auch großartig zu verwenden, wenn man bedenkt, dass das meiste davon statisch ist (Gelände / Bäume usw.) und sich nicht so stark ändert. Dies bedeutet aber auch, dass jedes Mal, wenn eine Änderung an einem Block vorgenommen wird, Texture2D mithilfe von SetData geändert werden muss, was meiner Erfahrung nach ziemlich CPU-intensiv ist. Wenn die Texturen jedoch 16 x 16 wären, könnte es tatsächlich funktionieren und es würde auch den eigenen Speicher und die Festplattennutzung reduzieren.
- Bis Texturen durch Angabe des SourceRectangle in SpriteBatch. Für riesige Graslandschaften / Ozeane ist dies ein Plus, da nur ein Sprite gezeichnet wird. Für detailliertes Gelände mit unterschiedlichen Farben und unterschiedlichen Sprites (Biomes und Biomübergänge) befürchte ich jedoch, dass dies keinen großen Unterschied macht.
- Meine eigene Lösung, die Details beeinträchtigt, bestand darin, eine weiße Standardfliese (64 x 64) zu verwenden, ihr die Farbe der umgebenden 4 Kacheln zu geben und sie dann so zu skalieren, dass sie die 4 vorherigen Kacheln abdeckt. Der Unterschied besteht hier (abgesehen von der einfarbigen Fliese) darin, dass sich die Landschaft sichtbar verändert hat. Ich sollte auch erwähnen, dass Wälder immer noch einzeln gezeichnet werden müssen, da sie nicht perfekt quadratisch sind.
Wenn jemand eine Idee hat, wie er dieses Problem angehen kann, auch wenn es bedeutet, bestimmte Dinge zu kompromittieren (z. B. Details oder die Verwendung von 16 x 16 Sprites), würde ich es gerne hören.
Antworten:
Grafikkarten sind so optimiert, dass sie ein paar Dinge oft zeichnen und nicht umgekehrt.
In Ihrem Fall haben Sie viele Dinge (verschiedene Texturen), die Sie oft zeichnen möchten (Anzahl der Kacheln).
IMO, die einfachsten Optimierungen, die Sie vornehmen können, um signifikante Leistungssteigerungen zu erzielen, sind:
Ich würde empfehlen, beide Dinge zu tun. Viel Glück.
quelle
Ich habe herausgefunden, dass SpriteBatch nicht zum Zeichnen von Kacheln geeignet ist. Ein Grund dafür ist, dass Sprites für dynamische Objekte gedacht sind und für mehrere Zwecke verwendet werden. Ein weiterer Grund ist, dass es unglaublich CPU-gebunden ist , z. Wie in der Beschreibung erläutert, kostet das Zeichnen von 30.000 Objekten 8 ms (während mein Chipsatz maximal 30% betrug). Außerdem können bis zu 3000 Sprites gezeichnet werden, bevor ein neuer Draw-Aufruf an die GPU gesendet werden muss (Batched and All).
Die Lösung bestand darin, meine eigenen Kacheln mit strukturierten Quads zu zeichnen . In Kombination mit einem VertexBuffer konnte ich in einem Zeichenaufruf bis zu 500.000 strukturierte Kacheln zeichnen, wobei die Zeichenmethode ungefähr 1/20 Millisekunde in Anspruch nahm . Natürlich muss ich wahrscheinlich nicht so viele zeichnen, aber so oder so habe ich meine GPU endlich dazu gebracht, eine Arbeitslast von 75% bei konstanten 60 fps zu haben.
quelle