An unofficial build on
tig3rmast3r's v0.2.1.
If you use a SteamVR native headset, take this build
This release is about SteamVR, and it is the one to have. Frame delivery and
frame-rate stability are substantially better across a wide range of games — in
several titles the difference is between half the generated frames reaching your
headset and effectively all of them.
The bridge's own FPS overlay now tells you the truth on SteamVR. It used to
count the frames the bridge handed over, which is not the same as the frames
your headset was shown — a session really running at 40 to 45 would read a
confident 70. It now reads SteamVR's own delivered count, the same source fpsVR
uses, so the number in the headset is the number the headset received.
It brings SteamVR up to what was already possible on a Quest 3 through VDXR.
That gap was real and it was ours: VDXR happens to pace the bridge's frames for
free, so the bridge got away with guessing where the display was. SteamVR does
not pace them, so on SteamVR the guess was the whole story — and it was wrong
often enough, and differently enough per game and per machine, to be the single
biggest thing holding SteamVR headsets back. It no longer guesses. It asks
SteamVR directly, every frame.
Nothing in this release is tuned per game, per headset or per machine, and
nothing outside SteamVR changed: VDXR, Oculus and every other runtime take the
same path they did in 0.2.2_beta6.
Please report results on other SteamVR headsets. This was developed and
measured on a Pimax Crystal Super OLED. The change is not headset-specific by
design, but Index, Vive, Bigscreen Beyond, Reverb-through-SteamVR and other
Pimax models have not been tested, and that is the most useful thing you can
tell us right now. See Known issues for the one symptom to watch for.
What was wrong, and what changed
Frame generation hands your headset two frames for every one the game draws,
and they have to land one refresh apart. Getting that right means knowing
exactly where the compositor is in its own frame when you hand a frame over.
The bridge used to work that out for itself: it ran its own clock at your
headset's nominal refresh and inferred the rest from how its own frames were
behaving. Four separate parts of it were doing that inference, and they
disagreed — one of them was overruling the others five corrections to one. The
result was a schedule that slid slowly out of step with the display and landed
wherever it landed. Where it landed decided whether the generated frame was
taken for the frame the compositor was already assembling, and thrown away, or
the next one, and shown.
That is why the symptom was so strange to chase. Hogwarts Legacy delivered about
half its frames while The Callisto Protocol on the same build delivered nearly
all of them; the same game on the same machine behaved differently between
sessions; and every counter the bridge owns read a healthy 90 the entire time,
because all of them are written before the frame is handed over.
SteamVR can simply be asked. It reports how much time is left in the frame its
compositor is currently assembling, and that number falls in step with real
time. The bridge now waits against that and places each submission at a fixed
point inside the compositor's frame. There is no longer a schedule to hold, a
phase to calibrate or a drift to cancel — the three mechanisms that existed to
do those jobs are gone, and with them the per-game and per-session differences.
Measured with SteamVR's own delivery counters, across four titles:
| frames reaching the headset | |
|---|---|
| The Callisto Protocol (UEVR) | 99.3% |
| Hogwarts Legacy (UEVR) | 96.9–97.7% |
Hogwarts Legacy is the same title that sat around half on 0.2.2_beta6.
The biggest gains are in UEVR titles. They were the worst affected before —
UEVR's frame loop tends to hand frames over with more spread than a native VR
game does, which is precisely what the old guessed schedule handled worst. If
you play UEVR games on a SteamVR headset, this is the upgrade with the most in
it for you.
It also backs off by itself on hardware that cannot afford the setting. The
bridge holds a small reserve at the end of the compositor's frame, and how large
that reserve can safely be depends on your GPU, your resolution and your SteamVR
version. If delivery stays poor for a few seconds, the bridge now walks the
reserve down on its own and keeps the smaller value for the session. You should
never need to touch it.
How to tell it is working
- The bridge's overlay counter, on SteamVR, now reads what your headset was
actually shown. It should sit at your full refresh while the game's own rate
sits at half it. On every other runtime it still counts frames handed over,
which is not the same thing. - fpsVR, or your headset's own frame-rate overlay, should agree with it.
- With
logging_enabled=1, the rendered frame carries a green marker beside
the generated frame's purple one. Both flashing means both are reaching your
headset. A steady purple square means only one of the two is, and that is
worth reporting.
Known issues
- If generation appears not to engage at all on your headset — the overlay
sits near your game's own rate instead of double it, and the headset looks
half-rate — the reserve described above is the first suspect, and this is the
report we most want. It should correct itself
within about five seconds; if it does not, please open an issue saying which
headset, which GPU and roughly what per-eye resolution you run. - Very high per-eye resolutions remain the hard limit. Above roughly
9 megapixels per eye the generated frame cannot be finished early enough to be
shown, whatever the pacing does. Drop resolution first; the bridge's cost
scales with it. - Selecting the NVIDIA backend without an NVIDIA card silently does nothing.
On AMD or Intel leave the backend on FidelityFX, which is the shipped default.
Still worth knowing, unchanged from 0.2.2_beta6
Use the NVIDIA backend if you have an NVIDIA card. The shipped default is
FidelityFX, which works on any card. On NVIDIA the optical flow runs on the
dedicated Optical Flow Accelerator, which is idle while your game renders, so
about 86% of the generation work costs you almost none of the GPU your game is
using. That is what brings the frame rate your game needs to hold a steady 90
down from roughly 57 to roughly 48. FidelityFX computes the same flow on the
shader cores your game is drawing with, so it competes with your frame rate
instead of hiding behind it.
Frame times in fpsVR read oddly, and that is expected. The bridge
deliberately holds your game to half the refresh and fills the gaps, and that
hold happens inside a call fpsVR counts as your game's CPU time — so CPU frame
time goes up with the bridge on without your game having got slower. GPU frame
time reads low for the opposite reason: on the NVIDIA backend the generation
work runs on its own queue and on the flow engine, neither of which the runtime
measures. What matters is that the game's own rate sits at half your refresh and
stays there.
Optical flow resolution applies to both backends, at a shipped default of
50%. Set nvidia_input_scale=100 in ofxr_bridge.ini to run FidelityFX at full
resolution as older builds did.
Installing
Unzip anywhere and run OFXRBridgeTray.exe, then Arm bridge. Keep
OFXRBridgeTray.exe and the ofxr folder together. If you have an earlier
version armed, disarm it first.
Tested
Extensively, over the development of this release:
- Microsoft Flight Simulator 2024
- Luke Ross mod titles, principally Cyberpunk 2077
- UEVR titles, principally The Callisto Protocol and Hogwarts Legacy
on SteamVR with a Pimax Crystal Super OLED, on both the NVIDIA OFA and
FidelityFX backends, and cross-checked on a Quest 3 with no regression to
the VDXR path. PimaxXR was tested briefly and also works — it takes the same
path it did in 0.2.2_beta6, since everything new here is gated on SteamVR.
This is an experimental beta and compatibility is not universal, and
other SteamVR headsets are untested. Please report results — working and not —
on this fork's GitHub Issues, with an OFXR flight log where you
can. Enable one by setting logging_enabled=1 under [diagnostics] in
ofxr_bridge.ini; logs are written next to the armed layer under
%LOCALAPPDATA%\OFXR Bridge\RuntimeLayer\v260\.
Upstream results are collected in the
OFXR Bridge Compatibility Chart.