Der Encoder sitzt jetzt im Display-Treiber. Windows-Streams haben halb so viel Lag.
Der Windows-Host von punktfunk hat jedes Bild aus unserem Display-Treiber kopiert, umgewandelt und in einem zweiten Prozess encodiert. Seit 0.35 encodiert der Treiber jedes Bild in dem Moment, in dem Windows es fertig gezeichnet hat. Auf unserer RTX-4090-Testkiste sank die Latenz bei 4K120 HDR von 9–17 ms auf unter 5.
Jede Millisekunde in einem Game-Stream gehört jemandem. Dem Spiel, dem Encoder, dem Netzwerk, dem Decoder, dem Bildschirm. Lange Zeit steckte ein überraschend großer Teil unserer Millisekunden an einer Stelle, an die man nie denken würde: bei einer Übergabe zwischen zwei Programmen auf demselben PC.
Wo wir angefangen haben
Vor einer Weile haben wir IDD-push erklärt. punktfunk streamt auf sein eigenes virtuelles Display, und weil der Treiber uns gehört, schiebt er jedes fertige Bild an den Host. Kein Desktop Duplication, keine Capture-API.
Das war ein großer Schritt. Aber das Bild nahm trotzdem die Panoramaroute:
- der Treiber kopierte es in einen gemeinsamen Ring aus GPU-Texturen,
- der Host-Prozess holte es ab und wandelte es in das Format um, das der Encoder will,
- dann encodierte der Host es.
Zwei komplette Durchgänge pro Bild auf der GPU, ein Sprung zwischen zwei Prozessen, und jeder Schritt wartete, bis der vorige fertig war. Bei 4K und 120 fps kostete allein der Anteil des Hosts 6 bis 9 ms.
Warum es die Kopie überhaupt gab
Das war kein Versehen. Windows lässt Display-Treiber wie unseren in einem Systemprozess laufen. Hängt dieser Prozess, beendet Windows ihn nach etwa zwei Sekunden, und dein Display verschwindet gleich mit. Mit dem Encoder in einem eigenen Prozess konnte ein hängender Encoder das Display nicht mitreißen.
Du musst also wählen. Ein Prozess bringt Tempo. Zwei Prozesse bringen ein Sicherheitsnetz. Wir hatten uns fürs Netz entschieden.
Dann haben wir uns noch einmal angeschaut, wie das Modell für virtuelle Displays unter Windows gedacht ist. Es erwartet, dass der Treiber Bilder genau dort weitergibt, wo sie gezeichnet werden. Rohe Bilder über eine Prozessgrenze zu schicken, war der ungewöhnliche Teil, und das waren wir.
Was sich mit 0.35 geändert hat
Der Encoder wohnt jetzt im Treiber. Sobald Windows ein Bild fertig hat, macht der Treiber einen einzigen Durchgang direkt in einen kleinen Pool aus drei Bildern, und ein Encoder-Thread übernimmt von dort. NVENC von NVIDIA, AMF von AMD, Quick Sync von Intel und unser eigenes PyroWave laufen alle dort.
Der Host fasst keine Pixel mehr an. Er liest fertig komprimierte Bilder aus gemeinsamem Speicher und schickt sie übers Netzwerk.
Und das Sicherheitsnetz? Das ist jetzt der Pool. Fällt der Encoder zurück, wirft der Pool das älteste Bild weg und behält das neueste. Windows wartet nie auf uns, also kann ein langsamer Encoder dein Display nicht einfrieren.
Was wir zuerst falsch gemacht haben
Die erste Version der neuen Schleife hat den Encoder per Timer abgefragt: Fertig? Und jetzt? Im Schnitt war das okay, ein paar Millisekunden. Aber in einem Testfenster saß ein Bild 48 ms im Treiber, bevor es rausging. Bei 120 fps sind das fast sechs Bilder Verzögerung, aus dem Nichts.
Die Lösung: nicht mehr fragen, sondern zuhören. Encoder können ein Signal geben, wenn ein Bild fertig ist, und die Schleife schläft jetzt, bis genau dieses Signal kommt. Der schlimmste Fall fiel auf 6,2 ms, der Schnitt auf 0,7–3,1 ms.
Da versteckte sich noch eine zweite Timer-Falle. Der Standard-Timer von Windows tickt nur alle 15,6 ms, und das hat die Mauszeiger-Updates heimlich auf etwa 64 pro Sekunde begrenzt. Ein hochauflösender Timer hat auch das behoben.
Die Zahlen
Gemessen auf einer RTX-4090-Kiste, mit dem Client auf demselben Rechner, damit das Netzwerk das Bild nicht verwischt:
- 4K, 120 fps, HDR: vorher 9–17 ms, nachher 4,7 ms im Median (5,7 ms im 95. Perzentil).
- 1080p, 60 fps: vorher 12–17 ms, nachher 4,1–5,8 ms.
- Der Anteil des Hosts: vorher 6–9 ms, nachher praktisch null.
Ehrlicherweise: Die Bildrate hat sich nicht geändert. Der alte Weg hat bei 4K120 schon jedes Bild geliefert. Es geht darum, wie alt jedes Bild ist, wenn es bei dir ankommt, und genau das spürst du, wenn du die Maus schnell rumreißt.
Ein Abstecher: RivaTuner in Session null
Unser Treiber läuft in einer Windows-Session ohne Desktop und ohne Benutzer. PyroWave dorthin zu holen hieß, Vulkan in dieser Session zu starten, und auf einer unserer Testkisten hing es einfach.
Der Übeltäter war RivaTuner. Seine Vulkan-Overlay-Schicht lädt sich in jedes Vulkan-Programm auf dem Rechner, auch in unseres, und kam mit einer Session ohne Desktop nicht klar. Der übliche Schalter „alle impliziten Schichten aus“ reichte bei sechs installierten Schichten nicht, also legt der Treiber jetzt zusätzlich den eigenen Aus-Schalter jeder Schicht um. Läuft auf deinem Streaming-PC ein Overlay-Tool, bist du abgesichert.
Was du tun musst
Aktualisiere auf 0.35. Der Installer aktualisiert Host und Treiber zusammen, mit einem kurzen schwarzen Flackern, während der Treiber neu lädt. Du brauchst Windows 11 22H2 oder neuer. Eine Einstellung zum Einschalten gibt es nicht.
Wenn du von Windows streamst: Update. Und wenn du deine Latenz schon mal gemessen hast: Miss nochmal.
Den Rest der Latenz-Geschichte findest du in unserem Beitrag darüber, wie wir den Encoder schnell halten, wenn ein Spiel die GPU voll auslastet.