Ich überlege gerade, warum viele Spiele so träge werden und wie ich die Latenz von Benutzereingaben minimieren könnte:
- Asynchrone CPU und GPU – dadurch bevorzugt man eindeutig Durchsatz gegenüber Latenz.
Man lässt z.B. die GPU drei Frames vorausrendern, um sie besser auszulasten und dadurch eine höhere Frame-Rate zu erzielen. Allerdings beträgt dabei auch die Latenz zwischen Eingabe und Ausgabe mindestens drei Frames. Es sollte offensichtlich sein, dass man die Auslastung schon zumindest verdreifachen müsste, um die Latenz unberührt zu lassen, was völlig illusorisch ist.
Das produziert dann den typischen Effekt, dass man die Maus schnell einmal im Kreis bewegt und das auf dem Bildschirm auch erkennen kann; allerdings fängt der Bildschirm erst an, wenn die Maus schon wieder stillsteht.
Gegenmittel ist ab Vista SP2 mit Direct3D 10.1 IDXGIDevice1::SetMaximumFrameLatency().
Es wäre toll, wenn jemand bei einem eigenen, GPU-limitierten, bei 20 fps kriechenden Spiel den Latenz- und Leistungsunterschied messen könnte.
- Vertikale Synchronisation – dadurch opfert man Latenz für Stabilität.
Rendert man mit theoretischen 55 Hz auf einen 60-Hz-Monitor, ist man echt angeschissen, weil man auf 30 Hz limitiert wird, und die Latenz damit fast verdoppelt.
VSync abzuschalten ist inho keine Option, weil die Bildqualität zu sehr leidet und langsam über den Bildschirm kriechende Balken eine zu starke Ablenkung sind.
Triple Buffering macht Kopfschmerzen, weil das Programm dann mit unregelmäßiger Latenz zu antworten scheint – dieses temporale Aliasing nehmen wir dann als Haken wahr. Dabei sind 30 Hz, 60 Hz, 30 Hz-Muster längst nicht das Schlimmste – katastrophal wird es im Bereich nahe der Bildschirmaktualisierungsrate, wenn das Programm mit 30 Hz, 30 Hz, 30 Hz, 30 Hz, 60 Hz zu antworten scheint. Dadurch kann alles unspielbar werden.
Imho sollte man in so einem Fall das Warten nicht ans Ende des Frames legen, sondern an den Anfang. Das heißt: Wenn ich im letzten Frame 7 ms auf den Bildschirm warten musste, warte ich im nächsten direkt am Anfang 7 ms (rein exemplarisch!) und berechne erst danach den kompletten Frame inklusive Physik und Logik. So liegt der Zeitpunkt, zu dem ich meine Physik berechne, näher an dem Zeitpunkt, zu dem das Bild auf dem Monitor auftaucht. Damit bleibt die maximale Latenz gleich (wenn die Eingabe zu Anfang der Wartezeit entsteht), aber die minimale Latenz kann sich fast halbieren (wenn die Eingabe zu Ende der Wartezeit entsteht).
- Polling – damit lässt man das Zeitfenster, in dem Eingaben entstehen, zu einem Zeitpunkt kollabieren.
Drücke ich dreimal pro Frame die Feuertaste, wird ein naives Polling das nur als einen einzelnen Klick interpretieren. Oder als drei Klicks zwar, aber zum selben Zeitpunkt. Dadurch geht jede Menge Information verloren.
Leider ist Polling die einfachste Möglichkeit, Eingaben in einem Echtzeitspiel zu verarbeiten. Wenn ich es bei mir anders machen wollte, würde mir nur vorschweben, die Eingabeverarbeitung in einen eigenen Thread zu verschieben, der die ganze Zeit wartet. Danach würde jede Eingabe mit einem präzisen Zeitstempel versehen und in den Haupt-Thread zum Verarbeiten geschoben. Hat man dann eine Physik, die mit höherer Frequenz arbeitet als die Bildausgabe (so wie die meisten Simulationen), kann man die Eingaben sogar einzelnen Logikschritten zuweisen.
Das Ganze birgt großes Problempotential (das Fenster, das die Eingaben verarbeitet, müsste ein anderes sein als das Ausgabefenster, weil ein Thread nur die Message Queue des Fensters abarbeiten kann, das auch in diesem Thread erzeugt wurde) und wäre viel, viel einfacher wenn GetMessageTime() eine höhere Auflösung hätte –aber 60 Hz reichen nicht aus um Eingaben sauber auseinander zu halten (eben getestet)damit kann man aber zumindest die minimale Latenz von Spielen, die eh schon auf 20 fps laufen, wieder auf 60 Hz anheben.
P.S.: Bei einigen Spielen könnte man die bewegten Objekte zuletzt zeichnen, und vorher einen zusätzlichen Logikdurchlauf tätigen. Das geht aber natürlich nicht bei Spielen, in denen der Spieler bestimmt, wo die Kamera hinsieht.