Verminderung der Auffälligkeit eines Frame-Abfalls in XNA

7

Ich hatte einige kleinere Frame-Drop-Probleme in einem 2D-XNA-Spiel.

Zunächst bin ich ein vsync'ed, gemischter Zeitschritt. Das ist:

IsFixedTimestep = false;
graphicsDeviceManager.SynchronizeWithVerticalRetrace = true;

Dies wird mit einem festen Physik-Zeitschritt von 120 Hz kombiniert, der mit dem in Fix Your Timestep! um nervöse Physikbewegungen zu vermeiden. Das Update wird also mit einem variablen Zeitschritt ausgeführt, und ein weiterer Tick wird mit einem festen Zeitschritt ausgeführt (die Idee stammt von Unitys Update / FixedUpdate).

Wenn sich ein Sprite mit gleichmäßiger Geschwindigkeit über den Bildschirm bewegt, fällt alle paar Sekunden ein Jitter / Schluckauf auf. Ein bisschen googeln hat mich vor einigen Jahren zu diesem Forumsbeitrag geführt, in dem ich über das gleiche Thema sprach.

Um dies zu testen, habe ich diesen Code einer Update-Methode hinzugefügt:

if(gameTime.ElapsedGameTime.TotalMilliseconds > 22)
{
    Logger.Log(gameTime.ElapsedGameTime.TotalMilliseconds.ToString());
}

Sicher genug, alle paar Sekunden zeigte das Protokoll eine Zeit über 30 Millisekunden, normalerweise um 32. Da ein Frame mit 60 fps 16,6 Millisekunden entspricht, scheint ein Frame einfach verloren zu gehen. Ich habe zusätzlichen Code hinzugefügt, um den Bildschirm in diesem Fall rot zu blinken. Wiederum stelle ich sicher fest, dass die Kugel schluckt, wenn der Bildschirm rot blinkt. Wenn ein Frame einfach fallen gelassen wird, bedeutet dies, dass die Kugel stillsteht. Wenn der Frame schließlich ausgeführt wird, wird die Physik viermal ausgeführt (da sie doppelt so schnell wie der Monitor läuft, über zwei Frames) und die Kugel a bewegt Menge.

Jetzt bin ich mir nicht sicher, ob ich das beheben kann. Wenn dies tatsächlich eine Eigenart von XNA (und Gamedev im Allgemeinen?) Ist, kann der Frame-Drop unvermeidbar sein. Ich würde also entweder wissen, wie man den Frame-Drop entweder beseitigt oder wie sichtbar er ist. Noch ein paar Punkte:

1. Es ist wahrscheinlich nicht der GC.

In diesem Forumsbeitrag schienen sie das Problem bis zu einem Punkt zu verbessern, an dem der GC niemals / kaum laufen würde, und sahen das Problem immer noch. Um mich selbst zu überprüfen, habe ich einen Code hinzugefügt, der GC.GetTotalMemory(false)jeden Frame überprüft. Wenn er aus dem letzten Frame entfernt wurde, protokollieren Sie "GC". Ich denke, ein Rückgang würde eine GC-Sammlung anzeigen, aber ich könnte mich irren. "GC" wurde unabhängig vom anderen Protokoll protokolliert, als ein Frame gelöscht wurde.

2. Ich kann keinen variablen Zeitschritt verwenden

Ich brauche die Physik, um über ein Netzwerk reproduzierbar zu sein. Dies bedeutet, dass es so deterministisch wie möglich sein muss (es spielt eigentlich keine 100% ige Rolle, aber es verursacht wahrnehmbare Artefakte, je weniger deterministisch es ist). Ich kann also nicht einfach die Geschwindigkeit von Objekten mit multiplizieren gameTime.ElapsedGameTime.

3. Ich möchte IsFixedTimeStep nicht verwenden

Ich mag es, die Framerate ein bisschen unter meiner Kontrolle zu halten. Es gibt mehr Kontrolle über das Zeichnen und ermöglicht mehrere etwas unabhängige Frameraten. In dem Forumsbeitrag wurde auch angegeben, dass das Setzen von IsFixedTimeStep auf false das Problem behoben hat. Zugegeben, ich habe IsFixedTimeStep im Grunde selbst neu implementiert!

Außerdem IsFixedTimeStep = true;hilft die Einstellung nicht beim Stottern.

4. Einige Ideen?

Wenn ich das Problem nicht beheben kann, kann ich es vielleicht irgendwie abmildern. Einige Ideen, die ich hatte, sind:

  • Bewegungsunschärfe
  • Geben Sie den Kugeln Spuren
  • Erhöhen Sie das "Rauschen" auf dem Bildschirm, sodass ein Bildverlust ein kleines Problem darstellt
  • Separates Update und Zeichnen in separate Threads?
  • Mit Reißen und Unsynchronisieren von der vertikalen Rückverfolgung umgehen? Dies löst das Problem fast vollständig, aber die GPU-Nutzung geht zu 100%, da sie so schnell wie möglich aktualisiert und gezeichnet wird.

Vielleicht ist seit dem Forumsbeitrag 2008 auch etwas mit XNA passiert, das ich nutzen kann. Ansonsten bin ich mir nicht sicher, was ich gegen das Problem tun soll. Ist es ein wirklich kleines Problem? Ja! Ich würde aber gerne ein seidenweiches Spiel haben. Ich würde alle Ideen lieben!

Snea
quelle
Ich bin kein Experte, aber ich glaube, da Sie mit vsync arbeiten und Monitore nicht genau auf 60 Hz eingestellt sind, haben Sie irgendwann einen übrig gebliebenen Frame oder einen Frame dahinter, da der Monitor Hz dies nicht tut. Nicht mit dem Spiel FPS übereinstimmen. BEARBEITEN : Beachten Sie, dass das Deaktivieren von vsync und das Beibehalten von IsFixedTimeStep die GPU davon abhält, verrückt zu werden.
William Mariager
Ich dachte, so etwas wäre der Schuldige. Leider macht es ein sichtbares Artefakt, und ich suche nach einer Möglichkeit, um zu mildern, wie auffällig das ist. Wenn ich mich mit Physik beschäftige, kann ich diese kleine verbleibende Zeit tatsächlich nutzen, um die Positionen der Objekte sauber zu mischen. Für Monitoraktualisierungen ist dies jedoch viel schwieriger. Was Ihre Bearbeitung betrifft, würde ich davon ausgehen, dass dies immer noch zu einem Schluckauf führen würde, da der festgelegte Zeitschritt von 60 Hz mit der Aktualisierung des ~ 60-Hz-Monitors außer Phase wäre.
Snea
Können Sie das Problem in einem leeren Projekt reproduzieren?
Andrew Russell
Hallo @Snea, haben Sie jemals eine Lösung für dieses Problem gefunden? Ich habe das gleiche Problem immer wieder bekämpft, um diesen Schluckauf zu lindern, aber ohne Erfolg. Bei jedem Draw-Aufruf wurde festgestellt, wie weit ich im Frame durch den Zeitschritt geteilt bin, den ich zum Interpolieren zwischen den letzten Positionen und den aktuellen Positionen verwende. Dies hat dazu beigetragen, das Problem zu glätten. Bei einer festen Geschwindigkeit von 60 fps wird jedoch ein Frame gelöscht, der dies nicht tut Hilfe.
Joshjje
@ Joshjje Ich fürchte nicht. Es ist möglich, dass sich MonoGame anders und besser verhält, ich habe es nicht ausprobiert.
Snea

Antworten:

1

Zu Beginn gibt es hier einen guten Beitrag von Shawn Hargreaves auf GameTime (genauer gesagt, fester und nicht festgelegter Zeitschritt) . Vielleicht gibt es Ihnen eine Idee oder einige Dinge zu versuchen.

Ich bin mir jedoch nicht ganz sicher, ob Sie den Garbage Collector ausschließen können.

Für Müll:

Führen Sie Ihr Spiel über CLR Profiler aus und sehen Sie, was es Ihnen sagt. Wenn Sie Zugriff auf eine Xbox 360 haben, können Sie mithilfe des XNA Remote Performance Monitor schneller feststellen, ob ein Problem vorliegt (im Gegensatz zu CLR Profiler wird Ihnen jedoch nicht mitgeteilt, wo das Problem liegt). Es gibt eine Reihe von Blog-Posts von Shawn über die Verwendung von XNA RPM hier und hier.

Auch Ihre Methode der Überprüfung Müll sollte funktionieren. Mein persönlicher Lieblingsweg ist jedoch ungefähr so:

 WeakReference GarbageTracker = new WeakReference(new object());
 int GarbageCollections = 0;

Dann in Ihrem Update:

if(!GarbageTracker.IsAlive)// if it's not alive.  If it's been collected, it will be set to false
{
      GarbageTracker = new WeakReference(new object());
      GarbageCollections++;
}

Hier erfahren Sie, wann eine Sammlung stattfindet, da diese WeakReference mit jeder Sammlung gesammelt wird, die ausgeführt wird. Wenn es dann gesammelt wurde, erhöhen wir unseren Zähler und setzen ihn zurück.

Allgemein:

Einige Frame-Sprünge sind unvermeidlich. Manchmal dauert ein Update oder (seltener) ein Draw länger als es sollte, und es gibt keine gute Möglichkeit, sich darauf einzustellen (genauer gesagt, keine gute einfache Möglichkeit, sich darauf einzustellen). Auf den ersten Blick möchten Sie vielleicht gameTime.IsRunningSlowly auf Verzögerungen überprüfen. Wenn XNA nicht glaubt, dass es langsam läuft, ist es höchstwahrscheinlich ein Problem mit der Aktualisierungsrate.

Hoffentlich hilft das!

Elektroflamme
quelle
Ich mag diese Art, nach einer Speicherbereinigung zu suchen. Leider hat es mir nichts mehr gesagt, da es zur gleichen Zeit wie mein anderer Garbage Collection-Tester zu passieren schien, der immer noch unabhängig von einem Frame-Skip war. Ich hatte auch sehr wenig Glück, den CLR-Profiler zum Laufen zu bringen. Es wird nur 0 für alle Speicherinformationen angezeigt. Ich muss es noch einmal versuchen. IsRunningSlowly wird auch nie auf true gesetzt, mit einem festen oder nicht festgelegten Zeitschritt, daher handelt es sich wahrscheinlich um ein Problem mit der Aktualisierungsrate. Gibt es wirklich keine gute Möglichkeit, dies anzupassen oder zu maskieren, wenn es passiert?
Snea
Wenn Sie unter Windows 7 arbeiten (und ich glaube, Windows Vista, ich bin mir nicht sicher), kann es etwas schmerzhaft sein, CLRProfiler zum Laufen zu bringen. Insbesondere möchten Sie die 32-Bit-Version und wählen "V4 Desktop CLR", sobald der Profiler ausgeführt wird. Außerdem bin ich auf diesen Blog-Beitrag gestoßen, in dem jemand das gleiche Problem hatte wie Sie (obwohl er es auch nicht gelöst hat): ( codeforcake.com/blog/?p=83 ) Ich werde meine Frage aktualisieren, wenn ich denken kann von irgendetwas anderem.
Elektroflame
0

Zum Thema Verwendung von DirectX / DXGI / XNA- und .NET-Sprachen - Wenn Ihre Anwendung den DirectX / .NET-Mustern und -Praktiken entspricht, wird die Garbage Collection Ihren Frame nicht beeinträchtigen. Sofern Ihre Anwendung in einem anderen Thread nichts Dummes tut oder Sie eine große Anzahl von Objekten in einer engen Schleife ohne ordnungsgemäße Entsorgung erstellen, ist dies 9/10 Mal ein Problem mit der Hauptschleifenlogik (Rundungsfehler sind häufig) oder mit dem Rendern Code.

ROGRat
quelle