github elliotttate/OFXR-Bridge 0.2.13.22-dlssg-V439
OFXR Bridge V439 release 22 - DLSS vectors fixed for UEVR Synchronized Sequential

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_composer checks 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
    in docs/BUILDING.md.
  • Tests. xrfg_layer_promise_pose_lag checks 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 in docs/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_S and XRFG_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's app_locate_views records 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=0 whatever 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 its ofxr-ngx-capture-pid*.log file 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_mesh is 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=0 keeps 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 in docs/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=2 erred 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=2 timed 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=2 also 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 for nvngx_dlssg.dll only 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

  1. Extract the whole archive, and keep the ofxr folder beside
    OFXRBridgeTray.exe.
  2. Run OFXRBridgeTray.exe.
  3. To try native generation, right-click the tray icon and select NVIDIA
    DLSS Frame Generation (experimental)
    before the game starts its OpenXR
    session.
  4. Optionally choose another DLSS Frame Generation resolution (67% is
    the default). It takes effect the next time the game starts.
  5. 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.
  6. Benchmark this PC... labels each option with what it costs on your
    GPU.
  7. 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
    own nvngx_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.

Don't miss a new OFXR-Bridge release

NewReleases is sending notifications on new releases.