OFXR Bridge V439, release 22: DLSS vectors fixed for UEVR Synchronized Sequential
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
- DLSS vectors are right under UEVR's Synchronized Sequential. Reported
in the Stellar Blade demo: "dlss-g is quite broken, mouse rotation has
issues". Stellar Blade's right eye renders wrongly in UEVR's Native Stereo,
so it is played in Synchronized Sequential, where one Unreal view renders
both eyes in turn (Alternating/AFR is the same without UEVR's ghosting
fix). Unreal writes a view's vectors from its previous frame, which is then
the other eye's, so each eye's vectors held the shift of a distant point
from one eye's image to the other's (402 pixels at 1680x1760 on the Meta XR
Simulator) plus part of the motion, and every method took them as that
eye's own. Turning the camera, OFXR + DLSS vectors erred 23.7 (as badly as
blending the two frames), the hybrid 13.2 and native DLSS FG 11.8 at 67%.
An eye whose DLSS stream also evaluates the other eye now keeps the other
eye's evaluation between its own two, and the vectors are followed through
it to the eye's own previous image; the GPU decides each pair whether the
vectors point into the other eye, and leaves them alone where each eye has
a view of its own (Lies of P with the ghosting fix). Turning the camera
again: OFXR + DLSS vectors 10.2, the hybrid 9.7, native DLSS FG 9.3 (100%)
and 10.0 (67%), beside NVIDIA's optical flow at 9.4. Native Stereo is
unchanged and still the better mode where a game allows it. Details are in
docs/BUILDING.md. - Tests.
test_guide_composerchecks vectors into the other eye,
vectors into the eye's own image and an eye without a bridge; the per-eye
test checks each eye's bridge. 110 of 110 tests pass.
From release 21
- UEVR games get head poses for the time they are shown. UEVR locates a
frame's head pose on its game thread for the time its previous frame was
promised, so with frame generation every frame carried the head pose from
a whole game frame (22 ms at 2X on 90 Hz) before it was shown, and the
runtime turned it back by the head's motion since: the "fighting the
camera" users reported in head turns, and black at the edges of the view.
In Galactic Racer on the Meta XR Simulator, with the head made to turn up
to 42 degrees a second, every frame needed a median 0.65 degrees of
turning, and the offset that fit was -22.2 ms. The promise, on the
presenter and inline, is now measured against the time each frame's poses
were located for (the xrLocateViews whose pose the frame carries) rather
than the time it is stamped with; it settled one game frame later, and
real frames then needed 0 degrees of turning (median) and generated frames
0.004. Games that render for the time they stamp are unchanged. Details are
indocs/BUILDING.md. - Tests.
xrfg_layer_promise_pose_lagchecks a game that submits the
poses it located for its previous frame's time, as UEVR does. The promise
and pacing tests, which time frames against the wall clock and failed now
and then beside a parallel run's other tests, now run one at a time; full
parallel runs pass 110 of 110.
From release 20
- Inline games are promised the time their real frames go down. Where
the bridge runs without its presenter thread (single-threaded D3D11 games,
DCS, many titles on Virtual Desktop and Pimax), a pair goes down as the
generated frame in the game's own frame and the real frame a display
period later. The game was promised the earlier time, so every real frame
was shown a period after the head pose it was rendered for, and the runtime
turned it back: black at the edge in head turns, and a period more
reprojection error. The promise is now measured inline as on the presenter
([ofxr] inline_promise=1, the default; 0 keeps the earlier promise). On
the Meta XR Simulator, with a perfectly predicted head turn of up to 42
degrees a second, real frames needed a median 0.32 degrees of turning
before and none after, the correction following within 64 frames.
Generated frames then lie halfway between the real frames' poses and
times. - The status panel says what native DLSS FG is really doing. When NGX
skips a pair for want of usable guides, a copy of the real frame goes out
where the generated frame would be. The panel counted that copy as a
generated frame, so a Star Wars Jedi: Survivor session on Virtual Desktop
read 90 shown, 45 generated and "no repeats" while nothing was generated;
such copies now count as repeats (45 shown, none generated, 45 repeats a
second). The Why line names the actual reason - the guides did not match
the frames, no DLSS output is the eye images' size, or DLSS runs on another
queue - instead of "Waiting for the game's DLSS guides", and the Latency
line counts the frames held while skipped pairs go out. An eye image
holding one eye alone is no longer taken for two eyes side by side (UEVR
gave Lies of P's left eye one twice an eye's width on some launches, and
its guides were refused). A supersampled game's panel names the flow scale
actually run, after the per-eye cap, rather than the one chosen. If native
DLSS FG says its guides were refused under UEVR, try UEVR's Native Stereo
rendering method. - Under head turns the default hybrid is best or level. With the head
made to sway 20 degrees (up to 42 degrees a second) in Galactic Racer on
the Meta XR Simulator, and the frames replayed fairly, the error where the
scene moved was 2.15 in the menu and 4.70 in a race for the hybrid, against
2.20 and 4.59 for NVIDIA medium flow, which costs several times as much
live, and 3.04-3.32 and 6.77-6.93 for native DLSS FG, which trails every
OFXR method (repeating the frame: 24.4 and 14.3). A first replay had put
optical flow ahead of the game's vectors under head turns; it had
stretched the vectors by the game's frame times while a head turn follows
display time, and the vectors in fact carry the turn to within 0.2 pixels.
Half-resolution flow in the hybrid gained 2% in the turning race, nearly
all of it in one of ten runs, for 7% more GPU time, so quarter stays;
native DLSS FG given its frames unaligned erred 7.04 in the menu against
3.04, so the alignment stays. Details are indocs/BUILDING.md. - Test tooling.
XRFG_TEST_HEAD_SWAY_DEG(and_PERIOD_S) turns every
head pose a game is given, so it renders head turns with no one in the
headset.XRFG_TEST_RUNTIME_FLIP_SandXRFG_TEST_RUNTIME_HEAD_SWAY_DEG
move the runtime's own devices where it offers
XR_EXT_conformance_automation: on the simulator the status panel showed
0.29 s after the controller turned over and hid 0.51 s after it came
upright, every cycle. The flight recorder'sapp_locate_viewsrecords the
display time each of the game's xrLocateViews asks for; under UEVR on the
simulator those frames were shown at exactly the time they were rendered
for. - Tested in 17 more games on the Meta XR Simulator; see the README's table
(Metro Awakening, Deep Rock Galactic: Rogue Core, Reach, Deadzone: Rogue,
Stellar Blade and its demo, which now runs with a fresh UEVR profile,
Alien: Rogue Incursion, Into the Radius 2, Hellblade II, Black Myth: Wukong
and others).
From release 19
- A status panel in VR. Turn either controller upside down for a quarter
second and a panel appears above it: whether OFXR is generating, falling
back or off; frames shown a second, the game's own, generated, and the
headset's refresh; what the tray asked for against what is actually
running (the hybrid waiting for a game's first DLSS vectors, native DLSS FG
held at 2X on cards without multi-frame generation, NVIDIA flow
unavailable, a deeper pipeline given up, and so on); how many pairs used
the game's DLSS vectors; the pipeline and estimated added latency. Tray >
FPS overlay sets it to the gesture (default), always (low in the view), or
off;[overlay] panel=gesture|always|off. The gesture applies from the
next game start. - No black bars at the edge of the view in head turns. Each generated
frame was handed to SteamVR with the next real frame's head pose but shown
a display period earlier, so SteamVR turned it back and left black at the
trailing edge. Each generated frame is now made in, and submitted with, the
head's pose at the moment it is shown, for every OFXR method, 3X and native
DLSS FG (about 12 microseconds more a frame).[ofxr] synthetic_pose=real
restores the old behaviour. - Games whose Agility SDK exports point nowhere now generate. Beat
Saber carries D3D12 SDK exports without the folder they name, so every
D3D12 device in the process was refused, the D3D11 bridge's included, and
OFXR did nothing. The bridges now make their device on the system's D3D12. - Supersampled games stay affordable. The flow's per-eye input is held to
2304 pixels whatever scale is chosen: Trombone Champ at 6514x6514 per eye
went from 19-21 ms a pair, with half its generated frames missing their
slot, to 3.8-4.7 ms with every frame generated. - No second-long gaps when a game makes a swapchain. Riven lost a second
of generated frames to a swapchain it made for its UI. - The tray says native 3X needs an RTX 50 card. Elsewhere native DLSS FG
stays at 2X, which needs the game at half the refresh; OFXR's own 3X is the
better choice there. - Tested in 17 more games; see the README's table (Riven, Firmament, Halo:
Campaign Evolved, Surreal 98, The Midnight Walk, Moss: Book II, FlatOut 4
VR, Beat Saber, Trombone Champ and others).
From release 18
- "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.