Add OFFLINEMOD_FPS_CAP render-loop throttle#5
Open
Seltraeh wants to merge 1 commit into
Open
Conversation
The shipped Brave Frontier Windows 8.1 client drives its render loop
from the XAML CompositionTarget::Rendering event, which fires once per
display vblank with no internal gating. On displays above 60 Hz the
game ticks faster than designed: animations play too fast and short
mouse clicks register as held-down.
Two coordinated Detours hooks restore the intended 60 Hz cadence
without ghosting or input lag:
1. ?Render@CCEGLView@cocos2d@@QAEXXZ (libcocos2d)
Replaced with a mimic of the original prologue (ready-flag checks
at offsets +0x138/+0x139) that always calls ProcessEvents to drain
queued pointer events, then gates the game tick (CCDirector::
mainLoop via vtable[11]) by a QPC timer. Sets g_didDraw=1 only
when the tick actually ran.
2. eglSwapBuffers (libEGL)
Returns EGL_TRUE without swapping when g_didDraw is 0. Skipping
the swap on a vblank where we didn't redraw avoids presenting
stale back-buffer content, which otherwise shows up as ghosting
/ double-image on high-refresh displays.
The cap value is fetched once at startup from the offline server via
GET http://127.0.0.1:9960/offline_mod/fps_cap (transparently redirected
through the existing InternetConnect hook). The first valid MyRender
call probes the server with a 1 sec timeout; the response body is a
bare integer in [0, 1000] that we apply to g_minFrameTicks. If the
probe fails (server down, unreachable) the compile-time OFFLINEMOD_FPS_CAP
default remains in effect, so the cap is never un-capped by a missing
server.
Configurable knobs:
-DOFFLINEMOD_FPS_CAP=N compile-time fallback (60; 0 disables)
-DOFFLINEMOD_FPS_CAP_DIAG=1 compile in heartbeat logging to
%TEMP%\offlinemod_fps.log + OutputDebugString
To change the cap without rebuilding, edit fps_cap in the server's
deploy/config.json under plugins[0].config.server and relaunch the game.
The mimic-then-gate approach hard-codes offsets specific to
libcocos2d_v2.2.5_Windows_8.1.dll. Different cocos2d builds would
need the offsets re-derived from the binary.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The shipped Brave Frontier Windows 8.1 client drives its render loop from the XAML CompositionTarget::Rendering event, which fires once per display vblank with no internal gating. On displays above 60 Hz the game ticks faster than designed: animations play too fast and short mouse clicks register as held-down.
Two coordinated Detours hooks restore the intended 60 Hz cadence without ghosting or input lag:
?Render@CCEGLView@cocos2d@@QAEXXZ (libcocos2d) Replaced with a mimic of the original prologue (ready-flag checks at offsets +0x138/+0x139) that always calls ProcessEvents to drain queued pointer events, then gates the game tick (CCDirector:: mainLoop via vtable[11]) by a QPC timer. Sets g_didDraw=1 only when the tick actually ran.
eglSwapBuffers (libEGL) Returns EGL_TRUE without swapping when g_didDraw is 0. Skipping the swap on a vblank where we didn't redraw avoids presenting stale back-buffer content, which otherwise shows up as ghosting / double-image on high-refresh displays.
The cap value is fetched once at startup from the offline server via GET http://127.0.0.1:9960/offline_mod/fps_cap (transparently redirected through the existing InternetConnect hook). The first valid MyRender call probes the server with a 1 sec timeout; the response body is a bare integer in [0, 1000] that we apply to g_minFrameTicks. If the probe fails (server down, unreachable) the compile-time OFFLINEMOD_FPS_CAP default remains in effect, so the cap is never un-capped by a missing server.
Configurable knobs:
-DOFFLINEMOD_FPS_CAP=N compile-time fallback (60; 0 disables)
-DOFFLINEMOD_FPS_CAP_DIAG=1 compile in heartbeat logging to
%TEMP%\offlinemod_fps.log + OutputDebugString
To change the cap without rebuilding, edit fps_cap in the server's deploy/config.json under plugins[0].config.server and relaunch the game.
The mimic-then-gate approach hard-codes offsets specific to libcocos2d_v2.2.5_Windows_8.1.dll. Different cocos2d builds would need the offsets re-derived from the binary.