OnTogetherFrameCare
More frame rate with the same picture: outlines drawn in a pass of their own in dense views, less main-thread work on busy servers, a 600 fps cap. F2 saves a performance report; Shift+F2 tests what would help.Changelog
1.0.0
The first public version. The test builds before it were never published; their detailed history is in the source repository.
FrameCare gives On-Together more frame rate where the island gets dense and on busy servers, with the same picture, and measures where your frame rate goes. Everything it changes happens on your computer only: other players see nothing different, nothing about the network changes, and nothing is saved to the game's own settings.
Every measured number below comes from one test machine: a desktop with an RTX 5080 graphics card and a Ryzen 9 9950X3D processor, at 3440x1440 with the game's High quality on Direct3D 11, without Day and Night or any other mod that changes rendering. Read them as an example, not a promise: another machine or view gives other numbers.
Boosts: on by default, same picture
-
Outline pass (
Boost / OutlinePass). The game's Toon shader draws most objects twice, once for their colour and once for their black outline, and the engine alternates the two object by object, so it switches shaders for almost every draw and can batch almost nothing. FrameCare draws all the colour passes first and all the outline passes straight after, in a pass of its own. Every outline is still drawn. In an experiment run standing still at the edge of the map looking over the island, the frame rate went from about 64 to about 244 fps; draw calls and triangles stayed the same (4,737 and 4,734 draw calls, 948 K triangles both times) and shader switches fell from about 4,000 to about 340. On a 49-player server it took the same machine from 31 to about 110 fps. What can look different: the moved passes come after all solid objects, which can matter for masks, blending among solid objects and surfaces lying exactly on top of each other (an F2 report lists those materials), and a few solid props whose only colour pass is unlit are drawn a moment later. See-through objects are not affected. If URP is set up differently from what FrameCare checked, the boost changes nothing and says why. -
Five boosts for busy servers, for the other players' avatars and for work the game repeats every frame. Your own avatar is never touched.
RemoteControllerToggleOff: the network code stops switching each other player's CharacterController off and on around every move. One small physics difference: when you walk into a moving player, their collision capsule catches up with their avatar at the next physics step (at most 0.02 s) instead of every frame.HiddenTailSoftBoneOff: a tail's soft-bone physics pauses while that player wears no tail and comes back as soon as a tail is shown.RemoteGroundCheckKinematic: other players' ground-check physics bodies stop colliding with the ground; the game only reads your own.MenuLookupCache: on the island the game's sound manager searches the whole scene for the main menu every frame. FrameCare answers from the game's own cache and searches for real after every scene load, whenever the island is not loaded, and at least once a second.AmbientSoundLookupCache: on the island the game places the shore and river sounds at the nearest point of two curves every frame. FrameCare reuses the last position while you move less than 2 cm, for at most 0.1 s, and looks it up for real otherwise, after every scene load and whenever a curve changes.
FrameCare puts back every value it changed when you switch a setting off, change scene, quit or start an experiment run, and leaves a value alone if something else changed it. If the game's code is not what FrameCare checked, a lookup cache says "not available" once in the log and patches nothing. The F2 report's Boost section shows what each boost holds.
Measured in two players runs:
- On a 13-player server (12 other players), a steady run (drift 0.03 ms), with a test build that had the first four of these boosts: the menu lookup cache alone saved about 1.15 ms of main thread per frame (177 to 222 fps, +25%), and those four together about 1.35 ms (232 fps, +31%). On their own the controller toggle saved about 0.12 ms, the tail about 0.09 ms and the ground check about 0.01 ms.
- On a 23-player server, with this version's five boosts: 5.84 ms of main thread per frame (171 fps) with the boosts paused; the controller toggle saved about 0.18 ms, the tail about 0.15 ms and the ground check about 0.08 ms (no bigger than the run's drift). A 24th player joined during this run, before the menu cache step, so the report marks the later steps as not trustworthy; they still read about 1.14 ms for the menu lookup cache, about 0.07 ms for the ambient sound cache and about 1.60 ms for all five together (171 to 235 fps).
Optional settings: off by default, they change what you see
- Lower LOD bias (
Boost / LodBiasFactor, default 1.0, 0.25-1.0). Multiplies the game's LOD bias, so buildings, props and trees switch to simpler models closer to you. FrameCare applies it right after the game sets its own value, never multiplies twice, and puts the game's value back at 1.0. At 0.5, measured without the outline pass, about 13 fps more in the test view (64 to 76 fps). - Hide small objects far away (
Boost / HideSmallFarObjects, withSmallRadius0.75 m andFarDistance35 m). Small scenery is not drawn beyond that distance and appears again as you come closer (2 m past the distance, so it does not flicker). Players, your desk, customization and highlighted objects, 3D text and objects moved by physics are never hidden. Measured without the outline pass, about 38 fps more in the test view (64 to 101 fps). - With the outline pass on, as it is by default, the two together took the same view from about 244 to about 290 fps: far less time saved per frame, so on a machine running at 60 fps expect a few fps, not tens.
Frame-rate cap
Boost / FrameRateCap, 600 by default. Saves power and heat when the game runs far above what a monitor shows. It only applies while vSync is off, and a lower limit chosen in the game's own settings always stays. 0 turns it off; values from 1 to 29 count as 30. The game's own limit comes back at 0, while an experiment run is going and when the game closes.
Reports and experiments
- F2 saves a numbered report (
.txtand.json) of the view in front of you toBepInEx/FrameCare/reports: frame rate over the last 5 and 30 seconds, where the frame goes (main thread, render thread, GPU), how busy the game's CPU threads are, the main thread broken down by part of the frame (with the time between frames and between phases, frames with and without mouse movement, and input events per second), every running script timed for about two seconds (other mods' too, only timed, never changed), scene searches with the script that asked, the graphics settings in force, what each boost is doing, engine counters, what is being drawn, shaders, player avatars, particles, lights, UI, loaded mods, the session's hitches and garbage collection. Numbers continue across launches; FrameCare never deletes a report. Taking one is a visible hitch of a fraction of a second, left out of its own figures. - Shift+F2 starts an experiment run (thirteen steps, about 70 s; keep the mouse and keyboard still). It switches one thing at a time (render scale, shadows, LOD bias, small far objects, grass, outlines, the outline pass and combinations), measures it against baselines at the start and the end, and puts it back. Boosts are paused so the baselines show the game's own rendering. Steps something interfered with are marked.
- Ctrl+Shift+F2 starts a players run (thirteen steps, about 72 s): what the other players' avatars cost on the main thread, each busy-server boost on its own and all five together, plus diagnostics such as animation or avatar shadows off. It needs another player in view, pauses the frame-rate cap and the five busy-server boosts, and does not start while BlueSage QoL Tweaks' Player Render Saver is on.
- Ctrl+F2 switches the outline pass for the rest of the game session, to compare the picture. It never changes the config.
- Keys need exactly the modifiers they list, do nothing while you are typing, and can be changed in the config. Alt+F2 is left to the NVIDIA overlay.
Diagnostics files
- Session log,
BepInEx/FrameCare/sessions/session_<date>.csv: one line per second with frame rate, frame times, camera position, scene, players, the report taken, main thread parts, garbage collections, mouse movement and input events. - Hitch log,
sessions/hitches_<date>.csv: every frame slower thanDiagnostics / HitchMs(50 ms), counting the time between frames before it, with what the frame spent its time on, how much of it the main thread was on the CPU, garbage collections and what happened just before (another player's avatar appearing, an outfit applied, a scene load, a report, focus lost). Written at most once a second, at most 20,000 lines per launch. SessionLog / Enabled = falsestops both files. F2 reports still count and list hitches.- Only the newest 20 launches' session and hitch logs are kept (
SessionLog / KeepLaunches, 0 keeps all). Nothing else in the folder is deleted. available-playerloop.txtandavailable-counters.txtlist what the engine offers, overwritten when FrameCare starts its timing or saves the first report of a launch.- Main thread timestamps (
Diagnostics / MainThreadProbes, on by default) feed the breakdown and the hitch log. On the test machine on a 23-player server they cost about 0.035 ms per frame (about 132 timestamps per frame of about 265 ns each, under 1% of the main thread); every F2 report states the measured figure. Off: those figures read n/a and the hitch log uses the frame time alone.
Built against On-Together v1.1.8 and BepInEx 5.4.23.5.
