Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

1

22.10.2014, 13:49

[Optimierung] Rendern Isometrischer Map

Hi, in den letzten Wochen habe ich mit SFML und C++ ein kleines RPG mit isometrischer Perspektive gebaut. Dabei hatte ich auf meinem Laptop gearbeitet (FPS bei 60 gedeckelt, keine Performanceprobleme). Als ich vor einigen Tagen dann auf's Netbook migrierte stelle ich fest, dass ich teilweise auf 35-50 FPS sinke. Ich weiß, ohne expliziten Code ist es so gut wie unmöglich Performance-Hinweise zu bekommen .. aber ich denke ich habe konzeptionell noch Optimierungsbedarf. Daher versuche ich die internen Mechanismen verbal darzustellen - vielleicht sieht jemand den Fehler im Modell.

Ich verwende einen std::vector<Cell*>als Repräsentant des zweidimensionalen Feldes, indem ich die Koordinaten auf Indizes umrechne (index = x + y * width). Dabei prüfe ich vor einem at()-call, ob die Position innerhalb der Map liegt. Damit spare ich mir schonmal das catchen der std::out_of_range. Da ich diese getCell()-Methode beim Rendern verwende, macht sich das für mich schonmal bemerkmar - vorher hatte ich die Exceptions ungültiger Positionen abgefangen und alles deutlich ausgebremst. Die Methode liefert btw einen Pointer auf die Zelle (struct mit weiteren Daten) - ggf. einen nullptr, falls die Zelle nicht existiert.

Beim Rendern verfolge ich den "Diamond"-Ansatz, d.h. (0,0) ist oben in der Mitte. Weitehrin kenne ich den Viewport und kann mir damit berechnen, welche Kachel "topleft" ist, wie viele Kacheln auf der horizontalen maximal sichtbar sind und analoges auch für die Vertikale. Die Position der "topleft"-Kachel speichere ich mir als line_start. Dann gehe ich von der "topleft"-Kachel aus Sicht des Fensters) nach Rechts, indem ich x inkrementiere und y dekrementiere - so dass ich exakt die Nachbarkachel erreiche. Dann sammle ich die Renderdaten dieser Zeile (mehr dazu später). Sobald eine Zeile fertig ist (d.h. ich so viele Kacheln behandelt habe, wie ich sichtbare ausgerechnet habe), gehe ich in die nächste Zeile. Ist y gerade, gehe ich (bei line_start) in x-Richtung +1 - ansonsten in y-Richtung +1, so dass ich "zick-zack" je eine halbe Zeile runtergehe. In jeder neuen Zeile hangel ich mich erneut von links nach rechts usw.

Zur Visualisierung habe ich hier noch eine kleine Grafik. (btw Ursprungsgrafik ist http://i.stack.imgur.com/B5kDe.png)
Grün: topleft
Rosa: 1. Zeile
Cyan: Schritt in 2. Zeile
Blau: 2. Zeile
Gelb: Schritt in 3. Zeile

Nun zum "Sammeln der Renderdaten": Im Moment habe ich eine std::map<int, RenderData*>, wobei RenderData folgendes Strukt ist:

C-/C++-Quelltext

1
2
3
4
struct RenderData {
    sf::VertexArray walls;         // SFML-VertexArray für die Wände dieser Zeile
    std::set<GameObject*> objects; // Zeiger auf zu zeichnende GameObjects dieser Zeile
};

Gleichzeitig existiert ein zusätzliches VertexArray für den gesamten sichtbaren Boden, was zusätzlich erzeugt wird.

Diese Daten werden für jede Zelle manipuliert, d.h. neue Vertices für wall und neue GameObject-Zeiger hinzugefügt, welche von der aktuellen Kachel kommen. Nachdem eine Zeile vollständig iteriert wurde (d.h. bevor in die nächste Zeile gegangen wird), schreibe ich den RenderData-Zeiger für die aktuelle Line (wobei 0 die oberste relativ zum Bildschirm ist) in meine Map. Für die nächste Zeile wir dann ein neues RenderData-Objekt erzeugt.
Damit iteriere ich einmal komplett über den sichtbaren Bereich des Viewports.

Im Anschluss zeichne ich das VertexArray für den Boden mit einem Aufruf. Da der Boden keine Wände oder Objekte verdeckt (ich arbeite ohne verschiedene Kachelhöhen), kann ich das problemlos tun ohne Fehler in der Zeichenreihenfolge zu bekommen. Danach iteriere ich über jedes Paar in dieser map (d.h. ein int-RenderData*-Paar) und iteriere für jedes Paar über die GameObjects und calle für jedes die render()-Methode. Sie kümmert sich dann darum, dass der Sprite mit dem richtigen Animationsframe gezeichnet wird. Danach wird (für das selbe RenderData Objekt) die Wände gezeichnet, d.h. ein draw-call für das VertexArray dieser Zeile. Nun gebe ich den RenderData-Pointer mit delete frei und gehe zur nächsten Zeile usw.

Habt ihr eine Idee was ich falsch mache - oder anders formuliert: wo ich optimieren kann? Wie eingangs gesagt: ich möchte hier keinen Code posten... Er ist noch ziemlich verwinkelt (daran arbeite ich sobald ich die Zeit dafür habe).. und den Code auf die üblichen Minimalbeispiele herunterzubrechen ist mir gerade zu viel :S

Vielleicht noch ein paar Angaben zum System: Das Netbook hat einen Singlecore mit 1.6 GHz, 2 GB Ram und einen (afaik No-Name) Intel-onBoard-Grafikchip.. man könnte das Teil also also lowtech bezeichnen ^^ Allerdings ist es mir dennoch wichtig, vor allem auf diesem Gerät ein flüssiges Spielerlebnis zu erreichen.

Falls noch Fragen bestehen (ich etwas konfus oder zu knapp beschrieben habe) kann ich gerne nochmal auf was eingehen.
Danke für's Lesen - ich hoffe ihr habt ein paar Ideen für mich :)

LG Glocke

PS: ich bin gerade am überlegen, ob ein std::set pro RenderData gut ist... beim Einfügen habe ich immerhin O(logn) - bei einem std::vector aber (bei push_back nur O(1) .. andererseits stehen auf einer Kachel nur wenige Objekte - meistens sogar gar keine.
PS: ich habe mit <chrono> auch mal die Renderdauer gemessen und Debug-Ausgaben gemacht, wenn sie einen gewissen Wert überschritten hatte. Die Überschreitung trat auf dem Laptop nie und auf dem Netbook sehr oft auf... Ja, das ist kein Ersatz für wirkliches Profiling .. aber mit dem Thema habe ich mich noch nicht weiter beschäftigt.
»Glocke« hat folgendes Bild angehängt:
  • isorender.png

Beiträge: 1 223

Wohnort: Deutschland Bayern

Beruf: Schüler

  • Private Nachricht senden

2

22.10.2014, 15:49

Also die zentrale Frage die ich mir stellen würde wäre, wieviel zeichnest du wirklich? Wieviele Draw Calls und wieviele Polygone?
Wichtig wäre auch die Frage ob die Software wirklich an der CPU Performance in deinem Programm gebunden ist. Wenn du zuviele DrawCalls hast oder die Grafikkarte sonst überforderst, kannst du an den Datenstrukturen so viel optimieren wie du willst und es wird keinen Effekt auf die FPS in der Praxis haben, da hilft nur "weniger " zeichnen(Culling) oder eine effizientere API. (Also zum Beispiel "sf::VertexArray" anstatt einzellner Calls. Für besondere Methoden oder Anforderungen bietet sich auch an OpenGL direkt zu verwenden) Profiling wäre auf jeden Fall eine Methode die dieses und mögliche andere Probleme aufgedeckt werden sollte.


Ehrlich gesagt halte ich es für wahrscheinlich, dass das dein Problem ist. Trotzdem ein paar Anmerkungen zu dem was du geschrieben hast:

Zitat von »Glocke«

Ich verwende einen std::vector<Cell*>als Repräsentant des zweidimensionalen Feldes

Ein "std::vector<Cell>" wäre aus Gründen des Caches in der Praxis meistens effizienter, weil Zugriffe direkt erfolgen können und sequentiell sind. Außerdem entfällt Cacheverschwendung durch den Pointer.

Zitat von »Glocke«

Nun gebe ich den RenderData-Pointer mit delete frei und gehe zur nächsten Zeile usw.

Warum nicht auf dem Stack? (Nebenbei wenn dann Smart-Pointer...)

Zitat von »Glocke«

Nun zum "Sammeln der Renderdaten": Im Moment habe ich eine std::map<int, RenderData*>, wobei RenderData folgendes Strukt ist:

Eine "std::map" ist in den meisten Fällen nicht die effizienteste Wahl. In der Regel wird empfohlen, einen "std::vector" und dann zu sortieren.

Zitat von »Glocke«

PS: ich bin gerade am überlegen, ob ein std::set pro RenderData gut ist...

"std::set" krankt an einem ähnlichen Problem wie die "std::map", also nein, mit sehr hoher Wahrscheinlichkeit eher nicht.

Bei den Datenstrukturen möchte ich ganz klar darauf hinweisen, dass man nicht zu viel auf das theoretische Laufzeitverhalten schauen schauen muss. Klar ist dass gut zum ersten Abschätzen und ein guter Anhaltspunkt. Gerade bei Datenstrukturen und Performance ist Cache mindestens genauso entscheidend.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Spiele Programmierer« (22.10.2014, 17:41)


3

22.10.2014, 20:06

SFML + Debug ist nicht das schnellste, checke mal was die FPS in der release version so machen.

Sollten du da auch diese werte haben, würde ich nochmal über optimierung nachdenken, ansonsten mach erstmal weiter.

Gruß Koschi
Wer aufhört besser werden zu wollen hört auf gut zu sein!

aktuelles Projekt:Rickety Racquet

4

23.10.2014, 09:45

Also die zentrale Frage die ich mir stellen würde wäre, wieviel zeichnest du wirklich? Wieviele Draw Calls und wieviele Polygone?

Ich bin bei ca. 910 Triangles für das Terrain. Die Objekte hab ich hier mal weggelassen - da sind es im Moment 3-4 Quads.
Die 910 scheinen realistisch wenn man von 12x42 Kacheln pro Bildschirm ausgeht (auf der y-Achse passen ja durch die verkürzte Höhe mehr Kacheln drauf - dazu kommt, dass pro halbe Kachelhöhe wegen der Versetzung der Kacheln eine neue Zeile beginnt). Dann sind wir bei bis zu 504 Bodenkacheln.. dazu kommen dann weitere für Wände usw.

Von daher zeichne ich afaik schonmal nur das, was wirklich sichtbar ist (bzw. ein kleines bisschen mehr was sehr knapp am Rand liegt).

Wichtig wäre auch die Frage ob die Software wirklich an der CPU Performance in deinem Programm gebunden ist. Wenn du zuviele DrawCalls hast oder die Grafikkarte sonst überforderst, kannst du an den Datenstrukturen so viel optimieren wie du willst und es wird keinen Effekt auf die FPS in der Praxis haben, da hilft nur "weniger " zeichnen(Culling) oder eine effizientere API. (Also zum Beispiel "sf::VertexArray" anstatt einzellner Calls. Für besondere Methoden oder Anforderungen bietet sich auch an OpenGL direkt zu verwenden)

Auf jeden Fall ist der Unterschied zwischen beiden Systemen spürbar: 2x 2 GHZ vs. 1x 1.6 GHz.

Profiling wäre auf jeden Fall eine Methode die dieses und mögliche andere Probleme aufgedeckt werden sollte.

Deshalb habe ich mich gestern in den GNU Profiler etwas eingelesen und experimentiert. Ergebnis: Ich baue meine Kachel-Vertices jedesmal mit einer getTiles()-Methode zusammen, und in der Steckt mein Spiel laut Profiling Analyse um die 13% der Gesamtzeit .. deutlich zu viel.

Da das Aufbauen der Kacheln nur von der Kachelposition (nicht vom Viewport) abhängig ist, werde ich mal versuchen alle Kachel-Vertices vorher erzeugen zu lassen und mir beim Rendern nur die Vertices schnappen. In ihr Bildschirmposition gehen keinerlei Kamera-spezifische Sachen ein, da sf::View die entsprechende Translation er Szene übernimmt.. Und da ich das Culling durchführe (indem ich die rendering range berechne und nur darüber iteriere), brauchen die Vertices nicht mehr.
D.h. die Berechnung "TilePos to ScreenPos" (um die Vertexpositionen zu bestimmen), werde ich einmal vorgerechnet ablegen. Ich könnte mir vorstellen, dass das einiges bringt.

Ein "std::vector<Cell>" wäre aus Gründen des Caches in der Praxis meistens effizienter, weil Zugriffe direkt erfolgen können und sequentiell sind. Außerdem entfällt Cacheverschwendung durch den Pointer.

Interessanter Ansatz! Das werde ich beim Ablegen meiner vorberechneten Vertices berücksichtigen :)

Warum nicht auf dem Stack? (Nebenbei wenn dann Smart-Pointer...)

  1. Was meinst du mit "auf dem Stack?" Naja, ich weiß was ein Stack ist - nur gerade nicht was du genau meinst :)
  2. Von Smart-Pointern habe ich bisher Abstand gehalten. Ich versuche möglichst viel mit Referenzen zu arbeiten - da wo es geht.. einige Codestellen sind da nicht ganz konsequent geschrieben ^^ Aber wenn ich mit Pointern arbeite überlege ich mir wer ihn wo und wann erzeugen müsste und wer ihn wo und wann zerstören soll. Damit fahre ich bisher ganz gut.
    Sicherlich spare ich mir das bei Smart-Pointern. Aber da ich im Moment über Optimierungen nachdenke frage ich mich, was mir da Smart-Pointer bringen könnten? Afaik leichten Overhead .. oder übersehe ich was?

Eine "std::map" ist in den meisten Fällen nicht die effizienteste Wahl. In der Regel wird empfohlen, einen "std::vector" und dann zu sortieren.
[...]
"std::set" krankt an einem ähnlichen Problem wie die "std::map", also nein, mit sehr hoher Wahrscheinlichkeit eher nicht.

Vermutlich arbeiten die mit balancierten Binärbäumen (was die Laufzeiten erklären würde). Also wirklich nicht die richtige Wahl für mich ^^
Ich überlege gerade, ob std::vector oder direkt std::forward_list. Letzte müsste beim Einfügen am Listenende und beim Iterieren (in eine Richtung) ziemlich gut sein..

Bei den Datenstrukturen möchte ich ganz klar darauf hinweisen, dass man nicht zu viel auf das theoretische Laufzeitverhalten schauen schauen muss. Klar ist dass gut zum ersten Abschätzen und ein guter Anhaltspunkt. Gerade bei Datenstrukturen und Performance ist Cache mindestens genauso entscheidend.

Joa man merkt sicher, dass meine Komplexitätsvorlesung noch nicht lange her ist :D

Joa erstmal danke für deine Bemerkungen und Anregungen :)

SFML + Debug ist nicht das schnellste, checke mal was die FPS in der release version so machen.

Meinst du mit "Debug version" und "Release version" die SFML - oder meinen Code?

LG Glocke

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Glocke« (23.10.2014, 10:05)


5

23.10.2014, 12:14

Meinst du mit "Debug version" und "Release version" die SFML - oder meinen Code?


Vorrangig die sfml, aber schaden tut es auch nichts alles im Release zu testen.

Gruß Koschi
Wer aufhört besser werden zu wollen hört auf gut zu sein!

aktuelles Projekt:Rickety Racquet

Beiträge: 1 223

Wohnort: Deutschland Bayern

Beruf: Schüler

  • Private Nachricht senden

6

23.10.2014, 13:13

Wenn ich das richtig verstanden habe, renderst du momentan wirklich nur genau diese zwei Dinge: Tiles und Objekte.
Ich würde testweise einfach mal das eine und das andere deaktivieren und die Performance messen. Ich kann dir, wenn du nicht mehr Code von der Problemstelle zeigst, dir leider kaum mehr empfehlen als einfach profilen. Ausgehend von deiner Beschreibung dürfte es keine Probleme geben. Es sollten auch einige Größenordnungen mehr Polygone kein Problem sein.

Zitat von »Glocke«

Was meinst du mit "auf dem Stack?" Naja, ich weiß was ein Stack ist - nur gerade nicht was du genau meinst.

Ich meine halt das du die Struktur einfach direkt ablegst und nicht auf dem Heap. Quasi einfach den Pointer entfernen und direkt verwenden.
Also zum Beispiel...

C-/C++-Quelltext

1
2
3
4
void Something()
{
    RenderData MyData; //oder genauso in einer "struct". Jetzt ist es einfach auf dem Programmstack. Das geht schneller.
}


Zitat von »Glocke«

D.h. die Berechnung "TilePos to ScreenPos" (um die Vertexpositionen zu bestimmen), werde ich einmal vorgerechnet ablegen.

Wie berechnest du das? Ich glaube nicht, dass das der Flaschenhals ist. Berechnungen an sich sind in der Regel vergleichsweise schnell. Was Zeit kostet sind eher selten die blanken Berechnungen, sondern zum Beispiel die Speicherzugriffe oder (indirekte) Sprünge.

Zitat von »Glocke«

Von Smart-Pointern habe ich bisher Abstand gehalten. Ich versuche möglichst viel mit Referenzen zu arbeiten - da wo es geht [...] Aber da ich

im Moment über Optimierungen nachdenke frage ich mich, was mir da Smart-Pointer bringen könnten? Afaik leichten Overhead .. oder übersehe ich was?

Smart-Pointer lösen Referenzen nicht ab. Sie lösen auch nicht normale Pointer ab: Sie sind einfach eine Möglichkeit Objekte automatisch freizugeben und damit "delete" zu sparen. Im Falle von Ausnahmen hat man ohne sie in einem komplexen Programm sonst wenig Changen. Außerdem drücken sie die Besitzverhältnise aus und verbessern so die Aussagekraft des Codes und des Designs. Smart-Pointer sind konzeptionell overhead-frei. Zumindest bei "std::unique_ptr", der in der Regel sowieso empfohlen ist. Einfach gesagt fügt dieser das notwendige Aufräumen einfach automatisch ein, ohne dass du manuell "delete" aufrufen musst. Das hat wie schon gesagt ein paar nette positive Nebeneffekte für deinen Code.

Zitat von »Glocke«

VIch überlege gerade, ob std::vector oder direkt std::forward_list.

"std::vector". Mit einem "std::vector" fährst du normalerweise immer am Besten. Bei "std::forward_list" hast du wieder eine verkettete Liste. Das heißt tausende Allokationen beim Erstellen und beim Durchgehen gibt es keine feste Reihenfolge im Speicher und eine Menge Pointer. Um diese Datenstrukturen würde ich in 99,9% der Fälle vermeiden. Speziell auf diese Gruppe der Datenstrukturen bezieht sich auch meine Aussage, dass das theoretische Laufzeitverhalten in der Praxis nicht überbewertet werden sollte.
Ich verweise dazu auch mal darauf:
http://isocpp.org/blog/2014/06/stroustrup-lists
http://www.youtube.com/watch?v=fHNmRkzxH…utu.be&t=34m41s
http://baptiste-wicht.com/posts/2012/12/…list-deque.html

Zitat von »Glocke«

Meinst du mit "Debug version" und "Release version" die SFML - oder meinen Code?

Beides. Die Debug Version solltest du wirklich nur zum debuggen verwenden. Die Geschwindigkeit ist nicht bloß niedrig sondern möglicherweise auch anders verteilt als im Release.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Spiele Programmierer« (23.10.2014, 13:32)


BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

7

23.10.2014, 13:25

Nicht nur möglicherweise ;)
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

8

23.10.2014, 14:28

Ich würde testweise einfach mal das eine und das andere deaktivieren und die Performance messen

Ich habe z.B. das Rendern der Wände mal auskommentiert - dann läuft schonmal alles extrem flüssig. Daher habe ich - weil ich beim Rendern JEDER Kachel die Nachbarn prüfe, das ganze in den Konstruktor ausgelagert. Die Map ändert sich nachträglich nicht mehr - daher passt das.
Warum ich jeden Nachbarn anschaue: in meiner Map steht nur, ob es eine Boden- oder Wand-Kachel ist. Je nach Umfeld, wird aber eine andere Grafik für die Wandkachel verwendet. Da sich das wie gesagt nicht ändert, ist das ganze nun im Konstruktor besser aufgehoben .. kp warum ich das vorher in jedem Frame habe machen lassen :dash:

Das ganze beschleunigte jetzt schon einmal das Rendering!

Wie berechnest du das? Ich glaube nicht, dass das der Flaschenhals ist. Berechnungen an sich sind in der Regel vergleichsweise schnell. Was Zeit kostet sind eher selten die blanken Berechnungen, sondern zum Beispiel die Speicherzugriffe oder (indirekte) Sprünge.

Konkret:

C-/C++-Quelltext

1
2
3
4
5
6
sf::Vector2f WorldRenderer::toScreen(sf::Vector2f const & world_pos) const {
    return {
        (world_pos.x - world_pos.y) * this->tile_size.x / 2.f,
        (world_pos.x + world_pos.y) * this->tile_size.y / 2.f
    };
}

Ich wüsste da jetzt auch nicht was ich noch verbessern könnte - außer die Multiplikation durch einen entsprechenden Bitshift zu ersetzen.

"std::vector". Mit einem "std::vector" fährst du normalerweise immer am Besten. Bei "std::forward_list" hast du wieder eine verkettete Liste. Das heißt tausende Allokationen beim Erstellen und beim Durchgehen gibt es keine feste Reihenfolge im Speicher und eine Menge Pointer.

Da hast du allerdings vollkommen recht! :) Für das Sammeln der zu zeichnenden Daten habe ich jetzt (lokal in der render()-Methode std::vector<RenderData> lines; - sollte dem also genüge tun). Analog habe ich in meinem WorldRenderer ein std::vector<Tile> tiles; der die Vertices enthält.


Danke, führe ich mir demnächst zu Gemüte - habe dafür gerade keine Ruhe.

Beides. Die Debug Version solltest du wirklich nur zum debuggen verwenden. Die Geschwindigkeit ist nicht bloß niedrig sondern möglicherweise auch anders verteilt als im Release.

Was die SFML-Version angeht, verwende ich jene aus den Ubuntu 14.04 Quellen. Da das *-dev Paket Header enthält und die SharedObjects aus dem "normalen" Paket kommen vermute ich mal, dass ich nicht mit der Debug-Version von SFML arbeite. ^^
Meinen Code baue ich im Moment alles mit den Debug- und Profiling-Flags von GCC. Ich habe es mal ohne (und dazu -O2) getestet: Da läuft es (inzwischen, hab jetzt den Vergleich mit vorher nicht mehr) optimal. Allerdings merke ich selbst bei meiner Debug-Version (also -g -pg), dass ich einige FPS gewonnen habe. Da es im Release-Modus (also ohne -g -pg, mit -O2) scheinbar flüssig läuft, werde ich zum Arbeiten auf dem Netbook (ich will da echt nicht aus dem Debug-Modus raus ^^) einfach die Fensterauflösung und das FPS-Limit runtersetzen - dann erziele ich einen ähnlichen Effekt wie im Release-Mode. Ich stell ich einfach auf den Standpunkt, dass wenn etwas nicht läuft, es an meinem Code und nicht an SFML liegt :D (wird in über 99,9% der Fälle sicher auch so sein^^)

Vielleicht schaffe ich es irgendwann (vermutlich erst während der Vorlesungsunterbrechung oder spätestens in den Semesterferien) mal intensiv daran weiterzuarbeiten .. und hier auch was zu präsentieren 8)

Danke für alle Anregungen und Kommentare. Jetzt weiß ich wenigstens, dass ich ungefähr auf dem richtigen Weg bin.. Mit einer bedachteren Wahl der ADTs und etwas Profiling, sollte ich das zukünftig besser in den Griff bekommen ^^

LG Glocke

Beiträge: 1 223

Wohnort: Deutschland Bayern

Beruf: Schüler

  • Private Nachricht senden

9

23.10.2014, 15:02

Zitat von »Glocke«

Ich wüsste da jetzt auch nicht was ich noch verbessern könnte - außer die Multiplikation durch einen entsprechenden Bitshift zu ersetzen.

Aha. Ich meine, dort zu optimieren wäre dann so ziemlich das Anpacken perfekt am anderen Ende. Eine Verzweigung die entscheidet ob erneut berechnet wird, wäre wahrscheinlich schon viel langsamer. Dein Prozessor kann Hundert Millionen bis Milliarden Floating Point Berechnungen pro Sekunde ausführen. Die paar Addition an der Stelle einmal pro Frame brauchen so extrem unverstellbar wahnsinnig wenig Zeit. (Übrigens: Bit Schiebereien gehen bei Gleitkommazahlen nicht. Das geh nur bei Integer. Dafür könnte man die Division mit einer Multiplikation mit 0.5 ersetzen)

Du kannst auch ruhig den Debug-Modus verwenden. Deshalb gibt es den ja. Aber egal welche Maschine, zum Performance beurteilen oder testen, und auch wenn du dein fertiges Spiel tatsächlich verteilst oder selbst spielst willst, solltest du unbedingt in den Release Modus kompilieren.

10

23.10.2014, 15:20

Dein Prozessor kann Hundert Millionen bis Milliarden Floating Point Berechnungen pro Sekunde ausführen. Die paar Addition an der Stelle einmal pro Frame brauchen so extrem unverstellbar wahnsinnig wenig Zeit.

Das ist auch der Grund, warum ich es mir bisher verkniffen habe, dran rumzuoptimieren

Übrigens: Bit Schiebereien gehen bei Gleitkommazahlen nicht. Das geh nur bei Integer. Dafür könnte man die Division mit einer Multiplikation mit 0.5 ersetzen

Richtig. Prinzipiell ist die Skalierung TilePos-WorldPos bei mir so, dass eine Kachelbreite einem ganzzahligen Schritt entspricht, d.h. sich alle Nachkommastellen also "innerhalb" der Kachel abspielen. Prinzipiell könnte ich [...] aber wie gesagt: bisher habe ich keinen Grund gesehen. Bitshifts sehe ich persönlich schon als "extreme" Optimierung an - da werde ich noch Tausend andere Sachen finden, die optimierungswürdiger sind.

Du kannst auch ruhig den Debug-Modus verwenden. Deshalb gibt es den ja. Aber egal welche Maschine, zum Performance beurteilen oder testen, und auch wenn du dein fertiges Spiel tatsächlich verteilst oder selbst spielst willst, solltest du unbedingt in den Release Modus kompilieren.

Danke, das werde ich beim Testen ab sofort beherzigen :) Für einen Game-Release hatte ich den Release-Modus bisher auch im Auge gehabt. Aber für Performance-Tests ist es natürlich genauso sinnvoll - da "nur" das fertige Produkt diesbezüglich wirklich zählen kann.

Werbeanzeige