OFXR Bridge V439, release 18: the DLSS vectors + FidelityFX hybrid by default
This release comes from the native-dlss-fg branch of
elliotttate/OFXR-Bridge. That
fork builds on djules75/OFXR-Bridge
v0.2.13.1 (V438), which is itself a fork of
tig3rmast3r/OFXR-Bridge.
It adds native NVIDIA DLSS Frame Generation (NGX DLSS-G) as a VR
frame-generation backend beside OFXR's own methods, two OFXR modes of its
own (a DLSS vectors + optical flow hybrid, and SpaceWarp-style
extrapolation), and a benchmark of every mode on your own PC. Native
generation and the new modes are off by default; OFXR's own algorithm
stays the default selection.
New in this release
- "Interpolate, DLSS vectors + FidelityFX flow" is the default. In a game
with DLSS, OFXR runs FidelityFX's optical flow beside the game's motion
vectors and keeps, per pixel, whichever explains both frames better: the
least error of every method measured, and the mode that looked right in the
games where the vectors alone showed double images. It costs about a
millisecond more a pair than the vectors alone (1.0-1.4 ms in Lies of P,
1.43 ms in Galactic Racer at 2316x2316 per eye). For the cheapest mode,
choose Interpolate, from the game's DLSS vectors in the tray. - Games without DLSS keep the optical flow chosen in the tray. The
hybrid now waits for the game's first DLSS vectors and switches over then
(0.06-0.2 s after the game first ran DLSS in Lies of P and Galactic Racer,
before generation began). Before, choosing the hybrid also ran FidelityFX's
flow alone, at a quarter resolution, in games without DLSS; the tray no
longer says the hybrid uses FidelityFX in every game. - Existing settings move to the hybrid once. Earlier trays saved
dlss_flow_hybrid=0whatever was chosen, so the tray keeps the choice under
a new key,dlss_hybrid, and ignores the old one. Extrapolation, if
chosen, is kept. - The DLSS vector statistics no longer count each hybrid pair as a temporal
rejection.
From release 17
- Extrapolation ("Extrapolate, SpaceWarp-style") follows the game's own
step. A predicted frame should show the scene one display period of game
time on. The game's DLSS vectors span its step from the previous frame,
which it reports to DLSS, and that step swings around the frame rate (12-20
ms in one Galactic Racer race) and back the next frame. Extrapolating by
that step instead of a fixed share cut the prediction's timing error about
in half against the game's own timing (0.060 against 0.110 on average in
that race, 0.018 against 0.027 in a steadier one). Games that do not report
a frame time to DLSS are unchanged.
From release 16
- UEVR games in Alternating/AFR now generate on every launch. At the
start of a session the layer prepared frame generation for every image the
game had made, not only the ones it shows in the headset. Under UEVR that
spent SteamVR's limit of sixteen swapchains on spectator and UI images, and
in Alternating/AFR - where each eye has its own image - the eyes were then
refused and nothing was generated, on some launches and not others. Only
images actually shown in the headset are prepared now. Lies of P (AFR)
generated with both eyes' DLSS vectors on two launches in a row. Native
Stereo is still the better UEVR setting for frame generation where a game
allows it. - Also tested: Subnautica 2 under UEVR Native Stereo, every method including
native DLSS FG (it ships DLSS-G): OFXR + DLSS vectors 0.38 ms a pair,
FidelityFX 1.02, native 67% 1.36, hybrid 1.47, native 100% 1.77, NVIDIA
medium 8.5.
From release 15
- Per-eye DLSS vectors, done properly. Release 14 gave each eye of a game
with a swapchain per eye its own DLSS evaluation, but for a game that runs
one DLSS feature for both eyes it guessed which evaluation was the left
eye's from their count. Tested in Lies of P under UEVR - exactly such a
game - a launch whose count began on the right eye gave the left eye the
right eye's vectors and the right eye none. Each eye now takes the newest
DLSS evaluation made before its own image was released, as games evaluate
an eye and then release it; live in Lies of P both eyes' vectors are used
from the first seconds. The flight log now records every DLSS evaluation. - The promise follows games whose frames go down in two groups. The
display time a game is promised followed its real frames only when nine
in ten of a window agreed. Kayak VR: Mirage's frames go down in two groups
a frame apart, so it never moved, and with FidelityFX flow 71% of its
frames went down a period after their promise. Following the median, 73%
now go down at their promise in every method. - Tested this round on Kayak VR: Mirage, RoboQuest VR, Lies of P (UEVR) and
Hubris besides Galactic Racer. Generated-frame placement at the fixed share
beat the game's stamps in every one (Kayak 0.0011 against 0.0253).
From release 14
- Each eye gets its own DLSS vectors in games with a swapchain per eye.
Games that submit each eye from its own image (MSFS 2024 and CONTROL
Resonant were reported) had one eye's generated frames follow the other
eye's motion: a double image of the generated frames with every OFXR
method in a DLSS game and with native DLSS FG, though not with "DLSS
vectors + FidelityFX", which falls back to optical flow where the vectors
do not fit. The game's DLSS evaluations were matched to each eye's image
by size alone, and both eyes took the one DLSS ran last. Each eye's image
now takes its own eye's evaluation. Games that submit both eyes in one
image, such as Galactic Racer under UEVR, are unchanged. If a game still
shows double images, "DLSS vectors + FidelityFX" avoids them; please
report the game with itsofxr-ngx-capture-pid*.logfile from
%LOCALAPPDATA%/OFXR Bridge/RuntimeLayer/v439.
From release 13
- Each generated frame goes at its share of the frame period. OFXR's own
algorithm placed each generated frame by the display times the game
stamped its frames with. But frames do not go down at their stamps: a pair
takes its two display slots whatever the stamps say, and a frame that
misses its slot repeats the frame before. Against where every generated
frame was actually shown between its real frames, live in Galactic Racer
on the Steam Frame, the fixed share (the midpoint; thirds at 3X; 1.5 spans
extrapolating) erred about half as much as the stamps: 0.0080 against
0.0159 on average with "Prefer FPS over latency", 0.0046 against 0.0089
without it, 0.0211 against 0.0311 at 3X, 0.0126 against 0.0205
extrapolating, and 0.0010 against 0.0124 in Hubris, whose engine stamps
one frame in ten with the time of the frame before.
From release 12
- A slipped phase is moved after eight late pairs in a row instead of
twelve. The length was chosen on the natural runs of a race with no
correction at all, counting a needless move as costing its run again to
undo: over three races eight left about 392 pairs slow against 503 for
twelve. Live in Galactic Racer: 1.0% of a race's full-rate pairs slow (1.3%
in release 11), no run longer than 9, three moves.
From release 11
- A slipped phase is moved in a fifth of a second. Release 10 noticed
when the game's slack settled before synthesis (each real frame then a
period older) by judging fixed windows of 32 pairs, so a slip lasted 20-59
pairs. It now moves the game after twelve late pairs in a row, which the
isolated late pairs of normal running never reach, and not while SteamVR
runs at half the rate. Live in Galactic Racer: 1.3% of a race's full-rate
pairs slow, none longer than 13, every full-rate round at 62 ms. - Every interpolating method compared live at two resolutions. At
2316x2316 per eye: OFXR + DLSS vectors 0.37 ms a pair, FidelityFX 0.84-1.03,
the hybrid 1.32-1.44, native 67% 1.38-1.69, native 100% 2.52, NVIDIA medium
4.7-5.6 (the one that cost frames). At 2886x2886, with the GPU saturated:
vectors 0.58, FidelityFX 1.24, native 67% 1.55, the hybrid 1.91; beside a
game that fills the GPU, FidelityFX's passes stretch to twice their offline
time and native's do not. Offline, all 37 cases ran with drift 0.999. - The hybrid is taken up from the INI at each live control change, as the
extrapolation warp is.
From release 10
- A display period less latency with "Prefer FPS over latency" on. With
the deeper pipeline a real frame went down 62 ms after the game's
xrWaitFrame in some stretches and 70 ms in others, at the same 119.7
frames a second. The game's slack could settle in the wait before
synthesis rather than the hold after it, so each finished frame waited out
a period, rendered for an older head pose; in Galactic Racer that was 57%
of a race. The layer now notices it and moves the game back: 1.7-6.8% of a
race, each stretch over within a second, and every full-rate round at 62
ms, with no frame lost. - The promised display time holds while SteamVR halves the rate. The
measurement behind it pauses there instead of swinging between one period
and three. - Mesh and gather extrapolation, live at 3004x3004 per eye, interleaved
in one race: from the game's vectors 170 us a pair against 432-692 us, from
FidelityFX's flow 780 us against 1541-1714 us, and 118.5 frames a second
for both meshes against 113.4 and 103.5 for the gathers. The mesh and the
quarter-resolution flow also held on 17 recorded triplets no tuning used. - "Prefer FPS over latency" stays on by default, now measured against an
automatic depth as well as against off: over whole races the option
showed 102.6 and 104.3 frames a second, an automatic depth 99.2 and 90.1. [ofxr] extrapolate_meshis taken up again at each settings change from
the tray.
From release 9
-
Extrapolation draws Meta's mesh warps. Instead of OFXR's per-pixel
gather, a grid over each view (a vertex every 16 pixels) is moved along the
game's DLSS vectors and depth-tested, each vertex taking the motion and
depth of the nearest of four taps, as the Meta XR Simulator draws
Application SpaceWarp; without the game's vectors, FidelityFX's optical
flow drives the same grid as Meta's Asynchronous SpaceWarp does. On 42
recorded Galactic Racer triplets, predicting a whole frame ahead:Extrapolation Error SSIM GPU a pair Gather, DLSS vectors (release 8) 12.63 0.684 0.76 ms Mesh, DLSS vectors 12.00 0.683 0.17 ms Gather, FidelityFX flow (release 8) 12.41 0.660 1.22 ms Mesh, FidelityFX flow 11.90 0.673 0.47 ms Gather, both ( extrapolate=2, release 8)11.88 0.688 1.49 ms Mesh, both 11.56 0.682 0.86 ms Live in Galactic Racer at 3004x3004 per eye it cost 0.17 ms a pair from
the vectors and 0.78 ms from the flow, against 1.59-1.63 ms for native
generation at 67%. It is a little softer where triangles stretch.
[ofxr] extrapolate_mesh=0keeps the gather. -
The hybrid's flow runs at a quarter resolution. Where the flow only
patches what the vectors miss, a quarter per axis measured the same as half
(7.19 against 7.18) for 11% less time: the DLSS vectors + FidelityFX flow
hybrid now costs 1.03 ms a pair at 3004x3004 against 1.20 ms. -
"Prefer FPS over latency", measured. In Galactic Racer at the limit of
half the display rate it showed about 7% more frames (110.6 against 103.3
a second) for 17-24 ms more latency; it stays on by default. -
How the Steam Frame does it, read off the headset: streamed frames get
a rotation-only timewarp at display rate; its own frame generation (Motion
Smoothing) is off by default for the Frame. Notes indocs/BUILDING.md. -
A test that failed about one run in eight under load now checks what it
meant to (xrfg_layer_dcs_pipelined_wait).
From release 8
- Extrapolation searches five candidates, not nine. Near a motion edge
each pixel solved from its own motion and its neighbours' at 12 and 40
pixels. On 42 recorded Galactic Racer triplets the outer ring erred more,
and a headset predicts half as far ahead as that test. With the pixel and
its four nearest neighbours, extrapolation from the game's vectors erred
12.63 where the scene moved with SSIM 0.684, against 12.68 and 0.679, for
0.43 ms a pair at 3004x3004 per eye against 0.46 (0.76 ms against 1.13 on
the recorded frames).extrapolate=2erred 11.88 with SSIM 0.688, for
1.49 ms against 1.93.
From release 7
- Extrapolation from the game's vectors reads its depth again. Release 6
shipped a bug: the combined mode's constants layout reached the
vectors-only shader, which then read the depth rectangle from the wrong
constants and scored every pixel for a choice it never made. On 42 recorded
Galactic Racer triplets it erred 13.83 where the scene moved instead of
12.68. Fixed, and the unused scoring is gone (0.46 ms a pair at 3004x3004
per eye, from 0.48). - The game is promised the time its frame is actually shown. The display
time handed to the game at xrWaitFrame assumed its frame would go down
within a display period; measured in Galactic Racer on a Steam Frame, real
frames went down one period later than promised extrapolating and two
later interpolating with "Prefer FPS over latency" on, so the game rendered
for a head pose that much early and SteamVR's reprojection made up the
difference. The layer now measures how many whole periods late real frames
go down and moves the promise to match ([ofxr] promise_shown_time=1, the
default). Live, real frames then went down at their promised time in every
mode. When frames are shown is unchanged. - Latency, measured. From the game's xrWaitFrame returning to its real
frame going down to the display, live in Galactic Racer on a Steam Frame at
120 Hz: 45.5-47.0 ms extrapolating, 54.1-55.3 ms for native generation
interpolating with "Prefer FPS over latency" off, and 62.5 ms for any
interpolating mode with it on (the default). SteamVR's own lead from its
wait to the display is 35.3 ms there. The flight log now records what every
submission shows (presenter_content) against a clock origin, so this is
read from a log rather than inferred. - Extrapolation from FidelityFX's flow is a third cheaper and slightly
better. A 6-pixel motion-edge threshold for the flow, which is measured in
blocks, and its four nearest candidates rather than nine: 1.44 ms a pair at
3004x3004 against 2.30, 1.11-1.59 ms live in Galactic Racer, and on the
recorded frames 12.41 error with SSIM 0.660 against 12.46 and 0.652. extrapolate=2timed warm. Vectors and flow together erred least
(11.87, SSIM 0.684) and cost 1.93 ms a pair on the recorded frames, against
1.13 ms from the vectors and 1.22 ms from the flow; 1.27-1.91 ms live at
2316x2316 per eye. It stays an INI option.
From release 6
- A reworked tray menu, and Benchmark this PC. The menu is grouped by
decision: the frame-generation method, how OFXR makes frames, quality and
performance, then diagnostics and advanced settings, with what is running
shown at the top and options that do not apply greyed out. Benchmark this
PC... times every mode at your headset's resolution through the real
synthesizer (ofxr\OFXRBenchmark.exe, about 20 seconds on an RTX 5090),
and each option then shows its GPU cost and the most it can raise your
frame rate, with its measured quality and latency in the results window. - DLSS vectors + FidelityFX flow, a new OFXR mode. In a game with DLSS,
OFXR runs FidelityFX's optical flow beside the game's motion vectors and
composes each pixel from whichever explains both frames better. Content
the vectors do not describe - a shadow cast onto ground rushing past, a
reflection, an object that writes no velocity - moves as the image does,
which the flow follows. On 42 recorded Galactic Racer triplets it had the
least error of every method where the scene moved (7.18, against 7.67 for
NVIDIA flow, 7.96 for native generation at 67% and 8.40 for the vectors
alone), with the best SSIM. It costs about as much as native generation at
67%: 1.58-1.71 ms a stereo pair live on a Steam Frame at 3004x3004 per eye. - Extrapolation, SpaceWarp-style, a new OFXR mode. Every interpolating
method holds each real frame back a display period, to show a frame between
it and the one before first. Extrapolation shows each real frame as soon as
it is ready and then predicts the next display period from it, as Meta's
Application SpaceWarp does: a display period less latency, for the quality
of a prediction. It follows the game's DLSS vectors and depth, ordering
surfaces by depth near motion edges and stretching background over what
moving content uncovers, or FidelityFX's optical flow in games without
them, as Asynchronous SpaceWarp does. Predicting a whole frame ahead from
recorded frames, a harder test than the half frame a headset needs, it
erred 12.7 where the scene moved against 20.6 for showing the last frame
again. Live it cost a median 1.1-1.25 ms a pair in Galactic Racer, and
0.29 ms in Hubris.[ofxr] extrapolate=2also runs the flow beside the
vectors for the least error (11.87), at 2.1-4.6 ms live. - Native generation at 67% by default. NVIDIA's full-resolution frame is
the softest of the three: on the 42 triplets 67% erred less (7.96 against
8.04) with better SSIM and sharper detail, for about a quarter less GPU
time. 100% and 50% stay in the tray. - Native generation's depth scaled for NGX, as NVIDIA suggests for
compressed depth: 0.09-0.12 less error, at every resolution. - Optical flow no longer falls back to a double image. Where the flow's
two samples disagreed, the optical-flow methods blended the two frames
after only the head's turn. Each pixel now takes whichever explains both
frames better, the flow or the camera alone: FidelityFX flow's error fell
from 8.21 to 7.86, NVIDIA medium's from 8.17 to 7.67 and NVIDIA fast's from
9.01 to 7.94, and a pure head turn is more exact than before. - OFXR + DLSS vectors' occlusions take B's sample wherever the motion is
not uniform, not only at sharp motion edges: 8.49 to 8.40.
From release 5
- DLSS guides matched to the right eye. When each eye has its own DLSS
feature, as under UEVR, each eye could be given the other eye's motion and
depth: the eyes were matched in the order their DLSS first ran, and in
Galactic Racer the right eye's ran first every time. Native generation and
OFXR + DLSS vectors both suffered. Replaying recorded frames with the eyes
put right, the error where the scene moved fell from 9.6 to 3.5 for OFXR +
DLSS vectors and from 4.8 to 3.6 for native generation. The eyes are now
matched by where their inputs sit in the game's render targets. - 50% native resolution is back in the tray's DLSS Frame Generation
resolution submenu, beside 100% and 67%.
From release 4
- Fades and flashes fixed in OFXR + DLSS vectors. Release 3 treated any
disagreement between the two frames as an occlusion, so a fade or a flash
stepped at half the rate in generated frames. Away from moving edges,
generated frames now blend the two frames along the game's motion, as a
fade needs. On a moving scene that darkens, the error is 0.08, against
22.5 in release 3 and 45.7 before that.
From release 3
-
Native generation in games that ship no DLSS Frame Generation. NGX
looks fornvngx_dlssg.dllonly along the search paths of the process's
first NGX initialisation, which is normally the game's own DLSS. In a game
without its own copy, native generation therefore reported "unavailable".
The layer now adds its folder to those search paths when the game's OpenXR
instance is created with a DLSS method selected, before the game's DLSS
starts. Tested in Hubris, a native Unreal Engine 4 VR game with DLSS
but no frame generation of its own. -
Depth for games with no camera metadata. A game that submits no OpenXR
depth, and is not run through UEVR, still states its depth direction in its
DLSS flags. That direction is all NVIDIA's output depends on, so capture
now uses it. Hubris needed this as well. -
Much better OFXR + DLSS vectors. Where the game's motion reveals or
covers background, the generated frame used to show both frames' edges at
once. It now takes uncovered background from the new frame and covered
background from the previous one, and finds the surface in front near
moving edges. Content that stays put on screen although its vectors say it
moved, such as a HUD drawn after DLSS, is kept.On a test scene of a striped square sliding over a detailed background, the
error fell from 3.95 to 1.74 overall, and from 25.9 to 7.55 at the square's
edges. Native generation measures 4.25 and 8.7 on the same scene, so with
exact vectors OFXR + DLSS vectors is the more accurate method, at about a
sixth of the GPU time. At 3X its frames err 1.38 and 1.37 (native: 3.38 and
2.99). On recorded game frames, where the game's vectors are not exact,
native generation held up better on average. -
Diagnostics. With the environment variable
XRFG_TEST_NATIVE_DLSSG_VERBOSE=1, the layer writes NVIDIA's answers -
initialisation result, frame-generation availability, feature creation
failures - to%LOCALAPPDATA%\OFXR Bridge\NGX\ofxr-native-dlssg.log.
From release 2
- Optional reduced resolution for native generation. The tray's DLSS
Frame Generation resolution submenu offered 100% (the default) and
67%, which is faster with softer moving detail. - A GPU fault on resolution changes fixed. NGX now stays initialised, and
swapchain teardown no longer frees hand-over copies the GPU has not
finished.
Install
- Extract the whole archive, and keep the
ofxrfolder beside
OFXRBridgeTray.exe. - Run
OFXRBridgeTray.exe. - To try native generation, right-click the tray icon and select NVIDIA
DLSS Frame Generation (experimental) before the game starts its OpenXR
session. - Optionally choose another DLSS Frame Generation resolution (67% is
the default). It takes effect the next time the game starts. - For OFXR's own algorithm, How OFXR makes frames chooses interpolation,
the DLSS vectors + FidelityFX flow hybrid, or extrapolation. They apply
from the next game start. - Benchmark this PC... labels each option with what it costs on your
GPU. - 3X Frame Gen asks NGX for two generated frames, where the adapter
supports it.
OpenXR games only, and never run the game as administrator. See README.md for
the full guide.
Requirements for native DLSS FG
- An NVIDIA RTX GPU and driver for which NGX reports DLSS Frame Generation
available. Tested on an RTX 5090 with driver 616.56. - A game that uses a D3D12 DLSS or Ray Reconstruction upscaler. The bridge
captures its motion vectors and depth automatically. - The layer loading before the game's DLSS starts, or a game that ships its
ownnvngx_dlssg.dll. Native OpenXR games load the layer first. A game
that UEVR injects after it has started, and that ships no
nvngx_dlssg.dll, cannot run native generation; OFXR + DLSS vectors still
works there.
Without usable guides, the headset shows the current frame instead of a
generated one.
Measured cost
All figures are from one RTX 5090, and are median GPU time per stereo pair.
Offline benchmark, turning head:
| Method | 1440x1584 | 2004x2004 | 3004x3004 | 3600x3600 |
|---|---|---|---|---|
| OFXR + DLSS vectors | 0.13 ms | 0.23 ms | 0.52 ms | 0.72 ms |
| OFXR FidelityFX, half-res flow | 0.20 ms | 0.31 ms | 0.58 ms | 0.78 ms |
| Native DLSS FG 2X | 1.14 ms | 1.49 ms | 2.38 ms | 3.42 ms |
| OFXR NVIDIA medium flow | 1.30 ms | 1.61 ms | 2.89 ms | 3.80 ms |
| Native DLSS FG 3X | 1.66 ms | 2.13 ms | 3.73 ms | 5.02 ms |
At 3004x3004 and 120 Hz, OFXRBenchmark (every case, drift 1.002) measured
OFXR + DLSS vectors at 0.50 ms, FidelityFX flow at 0.59 ms, the DLSS vectors
- FidelityFX flow hybrid at 1.03 ms, extrapolation from the vectors at 0.16
ms and from FidelityFX's flow at 0.48 ms, native generation at 67% at 1.81 ms
and at 100% at 2.58 ms, and NVIDIA medium flow at 2.89 ms.
Live in Hubris through SteamVR, at 2568x2568 per eye in its menu scene:
| Method | GPU per stereo pair (median) |
|---|---|
| OFXR + DLSS vectors | 0.37 ms |
| OFXR FidelityFX flow | 0.67 ms |
| Native DLSS FG 2X at 67% resolution | 1.39 ms |
| Native DLSS FG 2X | 1.87 ms |
| OFXR NVIDIA medium flow | 3.15 ms |
Live in Galactic Racer on a Steam Frame, racing (3004x3004 per eye):
| Method | GPU per stereo pair (median) |
|---|---|
| OFXR + DLSS vectors | 0.59-0.62 ms |
| OFXR FidelityFX flow | 0.90 ms |
| OFXR extrapolation, DLSS vectors (mesh) | 0.17 ms |
| OFXR extrapolation, FidelityFX flow (mesh) | 0.78 ms |
| OFXR DLSS vectors + FidelityFX flow (new) | 1.58-1.71 ms |
| Native DLSS FG 2X at 67% resolution | 1.60-1.82 ms |
| Native DLSS FG 2X | 2.2-4.9 ms |
| OFXR NVIDIA medium flow | 4.5 ms |
Full measurements are in docs/BUILDING.md.
Caveats
- Experimental. Live testing so far covers Galactic Racer under UEVR, on
a Steam Frame through SteamVR and on the Meta XR Simulator, and Hubris
through SteamVR. The hybrid and extrapolation were tested live in Galactic
Racer on a Steam Frame, and extrapolation in Hubris as well. - Extrapolation predicts. Content its source does not describe - for the
game's vectors, a shadow on moving ground - is placed where the vectors say,
with no later frame to correct it; uncovered background is stretched. - Fixed cadence. Native mode uses NGX's fixed 1/2, or 1/3 and 2/3,
fractions. It suits games that hold half or a third of the display rate.
Licences
This package bundles NVIDIA's unmodified production nvngx_dlssg.dll under
the NVIDIA DLSS SDK license (licenses/NVIDIA-DLSS.txt). See THIRD_PARTY.md
and licenses/ for every third-party notice. This fork is not sponsored or
endorsed by NVIDIA.
SHA256SUMS.txt lists the checksum of every file in the archive.