From 3b2ebe6001247e1feb48b584e42d641d157e1e69 Mon Sep 17 00:00:00 2001 From: Corwin Date: Sat, 6 Apr 2024 03:26:06 +0100 Subject: [PATCH] dynamically choose frameskip or timeout --- website/app/src/mgba.tsx | 27 +++++++++++++++ website/app/src/useSmoothedFramerate.hook.ts | 35 ++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 website/app/src/useSmoothedFramerate.hook.ts diff --git a/website/app/src/mgba.tsx b/website/app/src/mgba.tsx index e2b14a49..29b17681 100644 --- a/website/app/src/mgba.tsx +++ b/website/app/src/mgba.tsx @@ -8,6 +8,7 @@ import { import mGBA from "./vendor/mgba"; import { GbaKey, KeyBindings } from "./bindings"; import { styled } from "styled-components"; +import { useSmoothedFramerate } from "./useSmoothedFramerate.hook"; type Module = any; @@ -40,6 +41,14 @@ export interface MgbaHandle { buttonRelease: (key: GbaKey) => void; } +const whichFrameSkip = (frameRate: number): number | undefined => { + if ((frameRate + 5) % 60 <= 10) { + // framerate close to multiple of 60 + // use frameskip + return Math.round(frameRate / 60); + } +}; + export const Mgba = forwardRef( ({ gameUrl, volume, controls, paused }, ref) => { const canvas = useRef(null); @@ -92,6 +101,24 @@ export const Mgba = forwardRef( }; }, [state]); + const frameRate = useSmoothedFramerate(); + const frameSkipToUse = whichFrameSkip(frameRate); + + useEffect(() => { + if (!gameLoaded) return; + + if (frameSkipToUse) { + // framerate close to multiple of 60 + // use frameskip + console.log("Using frameskip"); + mgbaModule.current.setMainLoopTiming(1, frameSkipToUse); + } else { + // frame rate not close to multiple of 60, use timeout + console.log("Using timeout"); + mgbaModule.current.setMainLoopTiming(0, 1000 / 59.727500569606); + } + }, [frameSkipToUse, gameLoaded]); + useEffect(() => { if (!gameLoaded) return; diff --git a/website/app/src/useSmoothedFramerate.hook.ts b/website/app/src/useSmoothedFramerate.hook.ts new file mode 100644 index 00000000..80666e8f --- /dev/null +++ b/website/app/src/useSmoothedFramerate.hook.ts @@ -0,0 +1,35 @@ +import { useEffect, useState } from "react"; + + +export const useSmoothedFramerate = (): number => { + + const [smoothedFrameTime, setSmoothedFrameTime] = useState(60); + + useEffect(() => { + + let previous: number | undefined = undefined; + let stopped = false; + + const raf = (time: DOMHighResTimeStamp) => { + if (previous) { + let delta = time - previous; + + setSmoothedFrameTime((time) => (time * 3 + delta) / 4); + } + previous = time; + + + if (!stopped) { + window.requestAnimationFrame(raf); + } + } + + window.requestAnimationFrame(raf); + + return () => { stopped = true; } + + }, []); + + + return Math.round(1 / (smoothedFrameTime / 1000)); +} \ No newline at end of file