Mad God Overhaul 4.0 Beta RC3 · Skyrim VR · CSX 3.18-VR

MGO RC3 Performance vs Quality Guide

GPU
RTX 5080 · 16 GB
Headset
Quest 3 · Virtual Desktop
Metric
GPUBusy p50
Budget
120 Hz = 8.33 ms

Scope — read this before using a number

Every millisecond here describes one modlist on one rig: Mad God Overhaul 4.0 Beta RC3 with CSX 3.18-VR, on the hardware above, at Riverwood with the world frozen, DLSS off at native resolution. On any other modlist — including an MGO update — these numbers are void, not approximate: RC3 replaced the renderer outright and moved every level measured before it. The settings, the directions and the method carry over to another setup; the numbers do not.

Metric
View

Settings
loading full-resolution frames…
Read this first — what these numbers are, and are not

These are levels for one modlist, not for Skyrim VR. Everything here was measured on Mad God Overhaul 4.0 Beta RC3 with the CSX 3.19-PR16 renderer on an RTX 5080, in one overnight session (2026-08-13). Move to a different modlist and the method transfers; the levels do not.

Everything here was measured with the upscaler OFF — native 2608×2680 per eye, no DLSS, no FSR, no TAA, no dynamic resolution (all three CSX presets ship DLSS on; it was staged off before every launch). That isolates what each feature itself costs, at ~2.25× the pixels of the DLSS-quality configuration you probably play at. Do not compare these numbers to any DLSS-on measurement — the ratios survive, the milliseconds do not. Varying the upscaler is a separate, unmeasured exercise.

The metric is GPUBusy p50 from PresentMon — never FPS, because per-frame FPS under a VR compositor quantises to refresh divisors and averaging it produces an artifact. Times are quoted to 0.01 ms; anything finer is noise.

The bar is measured, not assumed, and it differs by design. Live CS features were chopped — toggled ~1 Hz through CS's own A/B harness and demodulated — so their bar is the 3σ of eight interleaved null runs: 0.06 ms. Restart-gated levers and mod toggles need one relaunch per state, so their bar is the worst drift between adjacent identical baseline launches: 0.25 ms. Each setting shows its run's bar.

All arms ran on a virtual HMD — a SteamVR null driver at the Godlike 2608×2680/eye — with the world frozen (sgtm 0), the clock pinned at noon, clear weather, and actors censused before and after every window (an arm is voided, not fudged, if the crowd changed). No video encode and no transport in the path: treat deltas as a clean ranking of renderer cost, and confirm anything you ship by feel in the headset.

What a screenshot can and cannot prove

Every image here is the whole blended stereo frame, uncropped, at the capture's 2560×1440 — Community Shaders' own FramedStereo capture, taken after the post-process chain, loaded straight from the original lossless PNG. There is no re-encoding anywhere in this page. The 1:1 button paints one image pixel onto one physical pixel of your monitor. Every scored frame is a settled frame: the capture repeats until two consecutive shots agree, so a renderer mid-rebuild can never be scored as an effect.

The instrument has two very different floors, and each run uses its own. Within one launch, frozen, with no temporal AA in the pipeline, the scene rasterises deterministically: two identical captures differ by 0.0076% of pixels (SSIM 0.9992) — the sharpest visual instrument this project has had, and why "no visible change" here is a sensitive verdict, not a blind one. Across two launches the same comparison is ~42% of pixels (SSIM 0.84): per-launch HMD yaw inside the view gate shifts every razor-sharp native edge. So the restart/mod-toggle run's frames are look-only — its visual metric is deliberately blind, and its verdicts rest on frametime.

Several genuinely expensive levers land at "no visible change" at this bench point — Volumetric Shadows costs a real 0.54 ms and moved 0.05% of pixels at clear noon. That is a fact about one street view at one hour, not about the lever: cost and visibility are independent answers, and the guide keeps both.

And no still frame can show a temporal failure: a slower skylighting cadence lagging as you turn, or per-eye sky desync (Sky Sync moved 52% of pixels between states yet is frametime-free — the one-frame view understates what losing it feels like). Frames are frozen by construction.

Where you start — the native baseline

The whole page is anchored at one configuration: frozen Riverwood, native 2608×2680 per eye, no upscaler, no AA, on the CSX Quality preset. There, the baseline is GPUBusy p50 ≈ 22.5–22.8 ms across ten launches (busiest-thread CPU ≈ 6.2–6.4 ms/frame — the GPU binds by a wide margin). For scale: the same scene with the shipped DLSS-quality upscaling renders ~44% of the pixels and measured ~17.1 ms the same night.

Absolute levels are not portable — deltas are the product here. This project has measured the identical Riverwood config at 17.8, 21.4 and 18.4 ms across sessions on the old stack; the cause is unidentified and older than this page. Within-run comparisons (what every row shows) are tight to 0.05 ms; the 22.7 headline is context, not a promise.

What tonight's numbers say about the budget: at native Godlike with everything on, the frame misses even 72 Hz (13.9 ms). The two biggest renderer levers combined — Skylighting off (−2.87) and Parallax off (−1.25) — buy ~4 ms and visibly flatten the image. The pixel count is the lever with the arithmetic that matters, which is why the upscaler exists; measuring DLSS's own quality ladder against these native levels is the flagged next exercise, not part of this page.

What to actually change, in order
#ChangeGainWhat it costs you
1Never touch the free wins: depth-buffer culling, fast probe sampling, reduced probe update stay ON(+1.56 if you break them)Nothing — all three are visually null or better ON. They ship correct; this row exists because turning things off indiscriminately costs 1.5 ms here.
2Exterior volumetric lighting off (restart)−1.03Nothing a still frame can find at clear noon; judge god-rays at dawn before committing.
3Volumetric Shadows off−0.54Invisible at this bench point (0.05% of pixels). The best cost-per-visible-damage cut on the page.
4Skylighting off, only if you need real milliseconds−2.87You will see this one — 66.8% of the frame; exterior ambient shadowing flattens everywhere. The single biggest lever in CS, priced for the eyes-open buyer.
5Parallax off, same caveat−1.25Visible (19% of pixels): ground and rock go flat. Pairs with #4 for a ~4.1 ms "performance look".
6Do not switch presets chasing frametime±0.1With the upscaler held equal the CSX ladder is flat (Balanced −0.05, Performance −0.15, inside the bar). Preset choice is a look choice and an upscaler-setting choice.

The cheap-and-invisible stack (#2 + #3) is worth ~1.6 ms at no still-frame cost. The visible stack (#4 + #5) buys ~4.1 ms and looks like it. Enabling extras runs the other way: IBL +1.47, Linear Lighting +0.40, Contact Shadows +0.19, SSGI +0.42, shadow maps at 4096 +0.57 — a fully-loaded frame is ~3 ms over this baseline before the first mod toggle.

The traps — why some nulls are not negatives

A live A/B on a load-time key is a VOID TEST, not a negative. fGrassStartFadeDistance was measured twice as "+0.01 ms, not a result" — meaningless, because the key applies on load and the arms toggled it live. Driven properly with a relaunch per state it is real — −0.39 ms here (and a cautionary tale in the other direction: the old stack's famous −6.70 for this key does not reproduce on this configuration, because with fGrassFadeRange still at 8000 the grass mostly persists; a historical number is not a target). Before explaining a null, check whether the test was even valid.

The terrain, tree and LOD nulls are not safe to read as "no performance impact." DynDOLOD is installed and takes over object LOD; if it owns the distances then the vanilla keys are not the ones in control, which is a fact about who owns LOD rather than about what LOD costs. The decisive test is visual, not another arm: set the key to its minimum and see whether distant terrain changes at all. Note fBlockMaximumDistance did measure −0.28 ms, so the mechanism is not "all terrain keys are inert".

HIGGS silently restores iShadowUpdateFrameDelay around every Havok simulation-island deactivation — within seconds in a busy world — so any live arm on that key is stomped back to the session-start value. Ship it in the INI or the test is void.

Restart-gated CS settings cannot be chopped (SSGI, exterior volumetrics, the upscaling group) — a live toggle measures the write, not the setting, and reports a beautiful null. Their rows here came from one-relaunch-per-state arms against bracketed baselines. And SSGI has TWO gates: Enabled alone runs AO-only resources; the +0.42 row set Enabled AND EnableGI together.

A preset can void a valid harness. Balanced and Performance oscillate GPUBusy on their slower Skylighting probe cadence (update interval 13/16 vs Quality's 9) — enough to trip a 30 s window's shape gate repeatedly. Their rows are 45 s windows, which integrate whole cadence cycles. If you harness-test a preset yourself and it keeps "failing", this is probably why.

Outstanding work — what this page does not yet know

The upscaler itself is the flagged next exercise. This entire page holds DLSS off to isolate feature costs; the DLSS quality-mode ladder (and DLAA at qualityMode=0) against these native levels is deliberately unmeasured here. It is the largest single lever on the rig by construction (~5.6 ms between native and DLSS-quality the same night).

Levers with a path but no number (each also has an "unmeasured" row in the tree): VR/EnableDynamicCubemapVisibilityThrottle (file edit + relaunch; heir to the old −0.65 water-throttle win), Unified Water (boot-time kill only — needs a staged-boot A/B), interior volumetrics and depth-buffer culling interior (both need an interior bench point), the Skylighting numeric quality knobs (the presets differ mainly through them and skylighting is the #1 lever — a value sweep is the highest-leverage open item), and the Wetterness family in actual rain (the pinned clear-noon protocol structurally cannot price rain effects).

Scene generality is one-deep. Every number is one bench point (frozen Riverwood street). The old stack showed which side binds is scene-dependent — Whiterun exterior was CPU-bound. A second point (grass-heavy tundra, an interior, a city) would test which of these levers scale and would exercise Grass Lighting/Collision properly, both under-tested by a frozen street view.

SSGI and volumetrics engagement is frametime-inferred. Their cross-launch shots cannot corroborate visually (the 42% launch-to-launch floor); a within-launch settled capture pair at a restart-staged config would close that loop if it ever needs closing.

Mod-level candidates untouched tonight: Shadow Boost / VR FPS Stabilizer (closed-loop controllers — never enabled during a sweep; pick one, not both), eFPS / Project Optimization (CPU-side occlusion; low priority while the GPU binds). VRAM remains the untracked cost class — its failure mode is hitching, scored on p99 and spikes, which no median on this page shows.

How this was measured

Each gate below exists because a measurement without it was wrong in a way a median hid. They are enforced in code, and a failing gate voids the arm rather than returning a number.

GateWhat it doesThe bad arm that bought it
chop + nullLive levers toggle ~1 Hz through CS's in-memory A/B harness and the states are differenced; every session runs NULLS (both variants identical) and the bar is their 3σA null run through the naive file-write path once reported −0.118 ms as SIGNIFICANT — the write cost, not the setting; and slow drift lands entirely in one arm of any block design
world freezesgtm 0 asserted immediately after the clock pin, re-asserted before every windowtai alone stops decisions, not animation — an NPC was watched walking during a "frozen" arm
actor censusEvery actor's position recorded before and after the window; any arrival or >12-unit drift voidsOne straggler ref (0x00013491) voided five arms in this very session — each void was a different crowd, not a different setting
viewRecords camera yaw and pitch; voids if the view movedAn A/B/A read 16.36 ms against a 12.43 ms baseline purely because the headset faced a wall
shapeBuckets the frame CSV per second; voids on spread or a >0.30 ms head-vs-tail stepA window that was 16.8 ms for 14 s then 12.5 reported a median belonging to neither half; tonight it caught the Balanced preset's probe-cadence oscillation
upscaling stateReads CS's latched upscaler state and render/display resolution from its log; recorded with every armThe renderer boot-latches DLSS, so an "upscaling off" arm can silently come back on — every arm here took the native no-CS-line branch
clock & weathertimescale 1, gamehour 12, clear weather, pinned BEFORE the freeze, every armThe in-game clock runs ~20× real time; uncontrolled, a control arm drifted 0.81 ms purely from the sun moving — and moving the clock while frozen soft-locks the engine
rig healthCrash dialogs, the VR stack, DevBench liveness and an fps floor checked before and after every windowA compositor crash mid-suite once left six clean levers and a seventh reading +4.6 ms that would have shipped as a regression — a broken rig is still a steady rig

Freezing the actors is worth a 3.2× tighter bar. Two identical unfrozen arms drifted 0.28 ms, and lengthening the window from 3×10 s to 6×15 s made it worse, because a longer window samples more actor variance. Frozen: 0.09 ms. It costs almost nothing of what is being measured — the actor update root is 0.0003 ms per frame.

Unfrozen actor variance is ~0.70 ms and it is not drift. Three identical unfrozen arms read 8.15 / 8.84 / 8.92, which looks exactly like a session drifting. Twenty windows under a fixed workload gave an OLS slope of −0.0055 ms/window at t = −0.74 — not significant — with the CPU pinned at 4700 MHz and the GPU flat at 60–62 °C. The range within one launch was 0.70 ms with its maximum in the middle. The amplitude is real; the direction is not.

Score GPU-bound arms on GPUBusy, not CPU. At both bench points the GPU binds, and there the busiest-thread CPU metric carries ~1.8 ms of spin and wait variance against a workload GPUBusy shows is flat to 0.20–0.35 ms.

Cross-launch variance is the second, bigger bar. Within one launch, identical arms agree to 0.05 ms; across launches, seven identical baseline launches spread 22.49–22.78 with adjacent drifts up to 0.25 ms — which is why every restart-gated state is scored against baseline launches bracketing it in time, and why anything under 0.25 ms in that run reads "not resolved" rather than "zero".