De spelkring optimaliseren: De strijd om framepacing in de browser
Inhoudsopgave
Voor de toevallige toeschouwer is een game niet meer dan een reeks bewegende beelden. Maar voor een ontwikkelaar is een game een meedogenloze, razendsnelle klok. Voor retro-emulators en HTML5-arcadegames is die klok meedogenloos. Als de timing ook maar een paar milliseconden hapert, kraakt het geluid, hapert het scrollen en voelt de gameplay niet responsief aan.
Op Rec0m88, De grootste technische hindernis is niet alleen het uitvoeren van het spel, maar ook het onder de knie krijgen van Framepacing. JavaScript is oorspronkelijk gemaakt voor het scrollen van tekst en het klikken op knoppen, niet voor het beheren van de submilliseconde precisie die een arcadebord uit de jaren 90 vereist. Hier lees je hoe we de machinekamer van de browser hebben getemd om soepele, consistente 60 FPS gameplay te leveren.
Het V-Sync spook: waarom 60Hz niet altijd 60Hz is
De meeste retroconsoles zijn ontworpen voor NTSC-televisies, die op precies 59,94 Hz. Moderne computermonitoren werken echter meestal op een vlakke 60 Hz, 120 Hz, of 144 Hz.
Als je een 59,94Hz spel draait op een 60Hz monitor, kom je in een wiskundige nachtmerrie terecht. Om de paar seconden “mist” de monitor een frame omdat de timings niet perfect op elkaar aansluiten. Dit resulteert in Microstotter-Die vervelende “sprong” die je ziet als een personage over het scherm loopt.
Onze oplossing: requestAnimationFrame en de Web Audio Clock
In een standaard webapp gebruiken ontwikkelaars setTimeout of instelinterval om code uit te voeren. Deze zijn te “los” voor gaming; ze kunnen worden vertraagd door de browser wanneer deze zich druk voelt.
In plaats daarvan gebruikt Rec0m88 een systeem met twee klokken:
- Visuele synchronisatie: We gebruiken
requestAnimationFrame(rAF). Dit vertelt de browser: “Voer het volgende frame van het spel pas uit als de monitor klaar is om het te tekenen.” Dit voorkomt “screen tearing” en zorgt ervoor dat de video zo glad als zijde is. - Audiosynchronisatie: Omdat de vernieuwingsfrequentie van de monitor kan fluctueren, gebruiken we de Web Audio API-contexttijd als onze “Metronoom”. De Web Audio klok is de meest nauwkeurige timer in de browser. Door onze WebAssembly (WASM) game loop te koppelen aan de audiobuffer, zorgen we ervoor dat het geluid nooit desynchroniseert met de actie.
De geluidskraak: De “Buffer Underflow” oplossen”
Heb je ooit een webspel gespeeld waarbij het geluid knalt of knettert als je een nieuw tabblad opent? Dat is een Buffer onderloop. Het gebeurt omdat de hoofd thread van de browser het te druk had om de audiokaart de volgende paar milliseconden geluid te “geven”.
Om dit op Rec0m88 op te lossen, hebben we de audioverwerking verplaatst naar een Webmedewerker.
- Ontkoppelde logica: De logica van het spel draait op één thread en het mixen van de audio draait op een andere thread.
- Ronde buffers: We houden een kleine “look-ahead” buffer van audiomonsters bij. Als de CPU voor een milliseconde piekt, haalt de audiospeler uit deze buffer, waardoor het systeem tijd heeft om te herstellen zonder dat de gebruiker ooit een “pop” hoort.”
Jitter beheren in een JIT-wereld
JavaScript is een JIT (Just-In-Time) gecompileerde taal. Dit betekent dat de browser de code constant “optimaliseert” terwijl je speelt. Soms veroorzaken deze optimalisaties een tijdelijke “hang” (vaak een “Jank” genoemd).
Om een perfecte framepacing te bereiken, minimaliseren we “Garbage Collection” (GC). GC is wanneer de browser alles stopt om ongebruikt geheugen op te ruimen. We vermijden dit door:
- Objectpooling: We hergebruiken dezelfde geheugenblokken voor sprites en P2P pakketten in plaats van nieuwe te maken.
- Getypeerde matrices: We gebruiken
Uint8ArrayenFloat32Arrayom spelgegevens af te handelen. Deze worden opgeslagen in een vast gebied van het geheugen dat de browser niet zo vaak hoeft “op te ruimen”, waardoor de lus van het spel voorspelbaar blijft.
De ingang-tot-foton vertraging
“Bij ”framepacing" gaat het niet alleen om wat je ziet, maar ook om wat je voelt. De tijd tussen het moment dat je een toets indrukt en het moment dat een pixel van kleur verandert, heet Ingang-tot-foton latentie.
Door de Web HID API en de Gamepad API, omzeilen we de standaard “Keyboard Event” wachtrij van de browser. Hierdoor kunnen we je controller pollen aan het begin van elk frame, vlak voordat de WASM core wordt uitgevoerd. Dit zorgt ervoor dat je invoer wordt verwerkt in de zeer volgende frame, wat zorgt voor het “pittige” gevoel waar arcadeliefhebbers om vragen.
Waarom hoogwaardige sites om timing geven
De filter “Inhoud met lage waarde” van Google kijkt niet alleen naar tekst, maar ook naar de gebruikerservaring. Een site die een trage, stotterende emulator biedt, is een tool van “lage kwaliteit”. Door onze worsteling met framepacing te documenteren en een engine met hoge precisie te leveren, laten we zien dat Rec0m88 een platform van professionele kwaliteit is.
We hebben honderden uren besteed aan het afstemmen van de interactie tussen de Web Audio API, WebAssembly en de GPU om ervoor te zorgen dat wanneer je een klassieker afspeelt op ons platform, het niet alleen werkt, maar ook goed aanvoelt.
De Meta-uitdaging: Probeer de “prestatiemonitor” van je browser te openen terwijl je een van onze games speelt. Je zult een vlakke, consistente lijn van 60 FPS zien - een bewijs van onze architectuur voor frame-pacing.
