Surfaces bringen das Spiel zum absemmeln

  • Win

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Surfaces bringen das Spiel zum absemmeln

    Hallo Leute,

    ich hab Heute ein kleines Licht/Schatten-Programm gecodet,
    das durch die Verwendung von vielen einzelnen Surfaces einen halbwegs realistischen Licht/Schattenwurf erzeugt.

    Im Testprojekt, quasi dem Prototypen, lief Alles fein, keine Abstürze bei 200+ Surfaces/Lichtquellen und Shadowcasters.
    Hab sogar mit Gewalt versucht, das Programm zum abstürzen zu bringen, keine Chance. Endete meist nur bei <10 fps (während Realtime-Licht/Schattenberechnung, Stepfrequency von 60.)

    Wenn ich aber diesen "Shader" in mein Projekt merge, gibt's teilweise schon bei drei Lichtquellen den Totalschaden und "...funktioniert nicht mehr".
    Der einzige Unterschied hier ist, dass die erste Surface etwa doppelt so groß ist, wie die des Prototypen. Aber das kann doch nicht zu solchen Abstürzen führen, oder?

    Laut Taskmanager füttert das Spiel keine 40MB RAM, selbst bei "Totalauslatung".
    Ich bin ein absoluter Versager was Surfaces angeht, ich weiß zwar wie sie funktionieren, aber nicht was sie mit meinem Spiel anstellen, dass es so derartig böse abstürzt.

    Ich weiß, dass das hier etwas wenig Informationen sind, aber vielleicht gibt es ja eine generelle Antwort auf dieses Problem,
    falls nicht gebe ich gern Näheres zum Verhalten der Lichtengine und des Spiels raus.

    Vielen Dank!
    Dieser Beitrag wurde bereits 8.675.628 mal editiert, zuletzt von »levarris07« (Heute, 11:33)
  • 200+ Surfaces?! 8|
    Ich glaub nich dass das gesund ist.

    Und wegen den Abstürzen. Das liegt vielleicht an der Größe der Surface. Je nach GPU kannst du nur eine bestimmte maximale Größe an Surfaces haben. Ich glaub wenn du mehr VRAM hast kann die Surface auch größer werden.
    Und wenn du versuchst eine Surface zu erstellen die größer ist dann wird sie entweder nich erstellt oder es stürzt ab.
    Weiß nicht wie es unter GM:S ist, aber bei GM 8.1 wurd sie einfach nicht erstellt.
    Ist aber auch nur so eine Vermutung die wahrscheinlich nicht stimmt.


    Ansonsten hab ich einfach zu wenig Informationen darüber wies funktioniert und so.

    Der Fehler "...funktioniert nicht mehr". tritt ja meistens auf wenn sich das Programm aufhängt, also z.b. in einer Schleife stecken bleibt oder so.
    Schonmal nachgeschaut obs da irgendwo so eine kritische Schleife gibt?
    Sorm ist Schuld

    Edit: Doch ist er
  • levarris07 schrieb:

    "...funktioniert nicht mehr"

    Habe ich bisher nur gehabt wenn ich ein Memory Loch erschaffen habe, also aus versehen Surfaces oder Variablen in einer unendlich Schleife.
    Dann konnte ich zusehen wie der RAM raufrattert bis das Programm dan schlussendlich "...funktioniert nicht mehr".

    Aber du sagst ja bei dir liegt es nicht am RAM

    levarris07 schrieb:

    Laut Taskmanager füttert das Spiel keine 40MB RAM, selbst bei "Totalauslatung".


    Pacmangamer schrieb:

    200+ Surfaces?! 8|
    Ich glaub nich dass das gesund ist.

    Kommt ganz auf die Größe an und wie sehr sie genutzt werden würde ich jetzt spontan sagen ;)

    Ich habe bei meinem aktuellen Projekt 8 Surfaces in Bildschirmauflösung (also ab und zu auch HD 1920*1080)
    und je nach Menü 30 bis 100 kleinere das ganze läuft mit einer GT630 ohne Probleme bei 60FPS
    Aber auch mit Mini ITX und Atom Prozessor mit integrierter Grafiklösung sakt das ganze zwar auf 20 bis 30 FPS ab, stürzt aber nicht ab bei ca. 100MB RAM nutzung.


    Meine Ansätze die ich ausprobieren würde:
    Lass das Programm nicht mehr benötigte Surface löschen.
    Überprüfe vor jedem Zugriff auf ein Surface ob es vorhanden ist.
    Wenn das nicht hilft, dann halt leider den zusätzlichen Code in kleinen schritten einfügen und so die Quelle des Problems eingrenzen.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Balls of Steel ()

  • Balls of Steel schrieb:

    Meine Ansätze die ich ausprobieren würde:
    Lass das Programm nicht mehr benötigte Surface löschen.
    Überprüfe vor jedem Zugriff auf ein Surface ob es vorhanden ist.
    Wenn das nicht hilft, dann halt leider den zusätzlichen Code in kleinen schritten einfügen und so die Quelle des Problems eingrenzen.


    Das habe ich alles bereits gemacht.
    Ich lösche sogar die Surfaces direkt nach der Erstellung, da sie nur für einen Draw verwendet, und nicht Realtime berechnet werden. (werden auf eine "Main Surface" gedrawt, die bleibt natürlich vorhanden)
    Habe nun den Code enorm refactored, und die Leistung wo es nur geht verbessert.

    Die Surfaces haben jetzt nun einen Cooldown von 100ms, das macht enorm viel aus, erhöht aber die Ladezeiten jeder Map.



    Eine Frage am Rande:
    Ist es klüger, ein riesiges statisches Surface für einen großen Room zu erstellen (4096x4096),
    oder ein kleines, dynamisches Surface das permanent über den derzeitigen View (1920x1080) gedrawt wird?

    Das Problem bei der dynamischen Lösung wäre, dass es eben jeden Step upgedated werden müsste.
    Ich möchte wissen, welcher dieser beiden Lösungen weniger Speicher frisst.
    Dieser Beitrag wurde bereits 8.675.628 mal editiert, zuletzt von »levarris07« (Heute, 11:33)
  • Was für ein Betriebssystem verwendest du? (Windows 8 hatte eingie performanceprobleme wenn surfaces verwendet wurden. Keine ahnung ob das mittlerweile mit einem Update behoben wurde.)

    Überprüfe vor jedem Zugriff auf ein Surface ob es vorhanden ist.

    Soweit ich weiss, muss man nicht explizit vor jedem Surface-zugriff auf dessen existenz prüfen.
    Prüfe die Surface nur 1 mal im Step event ab. Sobald dies gemacht wurde, bleibt sie bis zum abschluss des Draw-events auf jedenfall erhalten.
    > Spart performacne da du nicht jedesmall nach der Surface mit "surface_exists()" fragen musst.

    Ist es klüger, ein riesiges statisches Surface für einen großen Room zu erstellen (4096x4096),

    Bedenke dass ältere GPUs ein problem mit derartig großen Surfaces bekommen können. (Soweit ich weiss haben manche ein 2k limit.)
    Würde diese Variante deshalb nicht empfehlen.

    200+ Surfaces sind schon recht viel. Vor allem musst du bedenken dass der V-ram stark ausgelastet wird.
    Kommt der performancedrop wirklich nur bei der verwendung von Shadern zu tage?
    Wie schaut der Shadercode aus? Womöglich hast du dort irgendwo einen Bottleneck.

    Was auch sein kann:
    1. Grafikkartentreiber die ein problem haben (update versucht?)
    2. Fillrate Probleme. Falls du 200+ Surfaces verwendest, köntne es se ndass deine GPU bereits zu 99% ausgelastet ist. Sprich: Sie kommt zwar noch mit um die FPS im grünen Bereich zu erhalten, aber sie besitzt nurnoch sehr wenig freie Ressourcen.
    Kommt da also ein shader ins Spiel der etwas komplexere Rechenverfahren verwendet, könnte es sein dass du die Grenze deiner GPU überschritten hast. Wenn dies passiert, stürzen deine FPS momental abartig in die Tiefe.
    Das passiert bei mir beispielsweise wenn ich alle Effekte (SSAO, Volumetrisches Licht, AA,...) aktiviere. Die GPU wird ausgelastet. Bei 720p läuft alles schön und gut. Ab einer bestimmten Auflösung (die zwischen 720p und 1080p liegt) stürzen die FPS von 60 in den 20-30er Bereich.
    Keine Ahnung ob das bei dir zutrifft, wollte es nur mal gesagt haben.

    Ps: Sorry für Rechtschreibfehler. Arbeite mit einer neuen (gewöhnungsbedürftigen) Tastatur...


    Laut Taskmanager füttert das Spiel keine 40MB RAM, selbst bei "Totalauslatung".

    Könnte daran liegen dass die Surfaces nur im V-Ram gespeichert liegen und nicht im Ram. Dementsprechend verbrauchst du zwar keinen "normalen" Ram, dafür wird aber die GPU belastet. (welche im Taskmanager nicht abgefragt wird.)

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von LEWA ()

  • Danke für diese ausführliche Antwort, Lewa.

    Also, ich verwende nicht 200 Surfaces im Spiel, ich habe nur einmal 200 Surfaces im Prototyp erstellt, um es zu testen.
    Das hat auch wunderbar funktioniert, nur in meinem tatsächlichen Projekt waren manchmal 2 Surfaces bereits tödlich.
    Das heißt, dass das Problem ja wohl definitiv an dem Code meines Projekts liegt.

    Mir geht es auch nicht um FPS Drops, das Problem ist, dass das Spiel (vor der Überarbeitung) bei absolut jedem Tester gecrashed ist.
    Lustigerweise ist das Spiel immer nur beim zweiten Start gecrasht, der erste lief einwandfrei.

    Hier eine Zusammenfassung was ich mit den Surfaces im Spiel genau mache.
    Achtung: Pseudocode!

    Klasse surfaceMain Step:

    GML-Quellcode

    1. if (!surface_exists(surface)) {
    2. surface = surface_create(roomwidth, roomheight); //wobei roomwidth und height ca 1000-2000 Pixel betragen
    3. } else {
    4. if (update) { //führt folgenden code nur aus, wenn sich etwas verändert hat
    5. update = false;
    6. with(surfaceSub) { //es handelt sich hier um 5-20 Surface-Instanzen deren Surface eine Größe von durchschnittlich 128x128 hat
    7. mySurface = surface_create(mysize, mysize) //erstellt ein surface
    8. drawStuff() //zeichnet sachen auf das eigene surface
    9. drawPrimitives() //zeichnet ein primitive auf das eigene surface
    10. drawSurfaceToMainSurface() //zeichnet das eigene surface auf das hauptsurface von der klasse surfaceMain
    11. surface_free(mySurface) //löscht die eigene Surface wieder
    12. }
    13. }
    Alles anzeigen


    Und das Draw Event:

    GML-Quellcode

    1. drawSurface(surface);


    Das ist eigentlich alles, was passiert.
    Der view hier im room beträgt 480x270px, der auf 1920x1080 geported wird. (Ja, es ist ein pixeliges game ^^)

    Ich erstelle in diesem Spiel keine anderen surfaces und mache auch keine aufwändigen aktionen.
    Und wie gesagt, im Testprojekt/Prototyp (welcher nur aus den Surfaces selbst besteht) gibt es selbst bei 100+ keine Probleme, bei (fast) gleicher surfacegröße.

    Ich hab nun einen kleinen workaround erstellt, in dem ich alle surfaces verkleinert hab und sie nach der reihe erstellen lasse.
    Funktioniert nun zwar, habe aber lange ladezeiten und größere surfaces gehen auch nicht.
    das kann aber nicht die endgültige Lösung sein, wie ich finde.

    PS.: Sorry falls ich mal das Wort Shader verwendet hab, ich verwende keinen einzigen Shader in meinem Spiel. Mit Shader meine ich meist einfach nur Surface, bin ein verwirrter mensch
    Dieser Beitrag wurde bereits 8.675.628 mal editiert, zuletzt von »levarris07« (Heute, 11:33)
  • levarris07 schrieb:

    PS.: Sorry falls ich mal das Wort Shader verwendet hab, ich verwende keinen einzigen Shader in meinem Spiel. Mit Shader meine ich meist einfach nur Surface, bin ein verwirrter mensch

    Ok? Jetzt bin ich etwas verwirrt, da ich nicht weiss wie ich deine Aussagen deuten soll. XD (Mal sagst du dass bei einem Stress-test von vielen Surfaces alles super läuft, und dann sagst du dass der "Shader"- welcher eine Surface ist- das Spiel ab 3 Stück zum absturz bringt. Verstehe ich was falsch? ^^
    Also: Die Surfaces crashen das Spiel jedes 2te mal beim starten? Wie/wann ändert sich das verhalten?

    Zum Pseudocode:
    Also, ich bin mir nicht sicher wie du die surface da verwendest (surface_set_target() wird nirgends aufgerufen und du kannst nicht wirklich auf eine andere surface zeichnen ohne davor surface_reset_target aufzurufen.)
    Womöglich schaut es nur so aus da es nur Pseudocode ist?

    Eine große Sache fällt mir aber auf:
    Pro objekt wird eine neue Surface erstellt und wieder gelöscht.
    > Die Speicherbelegung und Speicherfreisetzung ist ein recht rechenintensiver Prozess. (egal ob es Java, C#,C++ oder GML ist.)
    Bei Algorithmen die schnell arbeiten sollen, sollte man das hochfrequente belegen und freigeben von riesigen Speichermengen vermeiden. (sei es der System-Ram oder der V-Ram.)
    > Surfaces sind nicht gerade kleine Speichereinheiten.
    Hier erstellst du pro objekt eine surface, beschreibst sie und löscht sie wieder (und das pro step n-mal, wobei n deine Anzahl an objekten ist.). Zwar mag es so erscheinen dass du wenig speicher verbrauchst (da nur gleichzeitig 1 Surface im Ram sitzt) dafür wird der Speicherzugriff zum Bottleneck.

    Stattdessen: Erstelle nur 1 Surface und verwende diese immerwieder in deinen objekten:

    GML-Quellcode

    1. if (!surface_exists(surface)) {
    2. surface = surface_create(roomwidth, roomheight); //wobei roomwidth und height ca 1000-2000 Pixel betragen
    3. } else {
    4. if (update) { //führt folgenden code nur aus, wenn sich etwas verändert hat
    5. update = false;
    6. var mySurface = surface_create(mysize, mysize) //erstellt ein surface
    7. with(surfaceSub) { //es handelt sich hier um 5-20 Surface-Instanzen deren Surface eine Größe von durchschnittlich 128x128 hat
    8. surface_set_target(mySurface);//das funktioniert, auch wenn wir uns mit "with" in einem anderen objekt operieren.
    9. drawStuff() //zeichnet sachen auf das eigene surface
    10. drawPrimitives() //zeichnet ein primitive auf das eigene surface
    11. surface_reset_target();
    12. drawSurfaceToMainSurface() //zeichnet das eigene surface auf das hauptsurface von der klasse surfaceMain
    13. }
    14. surface_free(mySurface) //löscht die eigene Surface wieder
    15. }
    Alles anzeigen

    Du wirst das eventuell anpassen müssen, da ich keine Ahnung habe wie genau der "drawSurfaceToMainSurface()" Script funktioniert.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von LEWA ()