Temporal upscaling renders the 3D scene at a lower resolution than the display, then rebuilds a display resolution image from the current frame plus the frames before it. Every frame the camera is nudged by a fraction of a pixel, so over several frames the render covers positions between the pixels of the reduced render, and the upscaler accumulates those into detail the single frame did not have.
Since version 0.40 the same pipeline drives DLSS, FSR 3, FSR 4, XeSS, and FSR 3 (custom). The user-facing options, hardware support, and the reasons a mode may be missing are on Anti-aliasing and Upscaling . This page explains how the renderer is organised around the upscaler and what that means for effects, content, and mods.
The frame is split in two by the UpscaleDispatch render bin. With a temporal upscaler, everything before it rasterizes at render resolution with a jittered camera. The bin hands scene color, depth, and motion vectors to the upscaler, receives a display resolution HDR image, and everything after it runs at display resolution on that image. FXAA, SMAA, and SMAA T2x use the deferred path described below and stay at render resolution until the final stretch.
| Stage | Resolution | Passes |
|---|---|---|
| Prepass, lighting, ambient occlusion, screen space shadows, screen space reflections | Render | Jittered. Produce #prepass, #lightinfo, #ssaoMask, #VelocityBuffer. |
| Opaque and translucent color, sky, glow, fog, caustics | Render | Jittered. |
UpscaleDispatch |
Render to display | Upscaler dispatch. The scene color is temporally resolved and no longer jittered. |
Bloom, local exposure, color correction, tone mapping (AfterPostFX) |
Display / render | Operate on the upscaled HDR image. Deferred modes run these at render resolution instead. |
FXAA, SMAA (PFXAfterDiffuse) |
Display / render | Run at display resolution after a temporal upscale, or at render resolution in their deferred path. |
Motion blur (PFXBeforeBin on OverlayRender) |
Display / render | Runs at display resolution after a temporal upscale and at render resolution for deferred modes. World billboards and icons are drawn later and stay sharp. |
UpscaleOverlay, editor, UI overlays |
Display / render | Jitter removed from the camera constants. Pixel stable. Deferred modes are stretched after these bins. |
The jitter
stays in the shared camera constants until the UpscaleOverlay bin so that display resolution passes reading #prepass[Depth] or #VelocityBuffer still see the same offset those buffers were drawn with.
Render Resolution in the graphics options is a linear scale from 33% to 100% applied to both axes, so the render keeps the display aspect ratio. At 100% the upscaler still runs, but only as temporal anti-aliasing (DLAA for DLSS, native AA for the others).
The upscaler may adjust the requested size. DLSS and XeSS report the range they accept for a display size and the engine clamps the request into it. FSR uses the request directly. The result is written to beamng.log when the mode or size changes. The slider values and the vendor preset names they correspond to are listed on Anti-aliasing and Upscaling
.
Because the scale applies to both axes, the pixel count falls with its square: 67% renders 44% of the display pixels, 50% renders 25%, 33% renders 11%.
Sharpness (GraphicUpscalingSharpness, RenderView.upscaleSharpness) is 0 (off) to 1 (maximum). The default is 0.25. The options slider shows it as 0% to 100% and only for the modes that reach Upscaler::dispatch: DLSS, FSR 3, FSR 4, XeSS, and FSR 3 (custom). FXAA, SMAA, and SMAA T2x never call the dispatch, so the slider is hidden.
The filter is AMD FidelityFX RCAS, applied to the display-resolution HDR image inside UpscaleDispatch, before exposure and tone mapping. FSR 3, FSR 4, and FSR 3 (custom) run it inside their own dispatch. DLSS has ignored its sharpness input since 2.5.1, and XeSS has none, so those modes run the same RCAS shader as a separate pass on the upscaled color. A value of 0 skips the pass.
Every RenderView owns its own upscaler instance and temporal history. Split screen views and the two VR eyes each accumulate independently. When vendor FSR 3 is available, a new VR eye view selects it at 50% render scale. No other upscaler is selected automatically when FSR 3 is unavailable. See Virtual Reality
.
A temporal upscaler cannot reconstruct anything the raster never sampled. To make the raster sample different positions each frame, the projection matrix is offset by a fraction of a render pixel:
| Mode | Pattern | Sequence length |
|---|---|---|
| FSR 3, FSR 4, XeSS, FSR 3 (custom) | Halton (2, 3) in -0.5 to +0.5 render pixels |
8 x (display width / render width)^2 frames |
| DLSS | The same Halton pattern | Eight times the base sequence length |
| SMAA T2x | Two 0.25 pixel diagonal phases |
2 frames |
| Bilinear, FXAA, SMAA | None | - |
The sequence grows with the upscale ratio because there are more display pixels per render pixel to cover. For FSR and XeSS, at 67% the sequence is 18 frames, at 50% it is 32 frames, and at 33% it is 72 frames. DLSS uses an eight-times longer cycle to avoid a screen-fixed dot pattern during long accumulation on a still frame.
The offset lives in the projection matrix, so depth, normals, velocity, and color are all jittered by the same amount in the same frame. Only things that mix a fixed screen position with a jittered buffer need to compensate; the PostFX page covers how.
#VelocityBuffer[RT0] is a screen UV delta from the previous frame, rendered at render resolution for every opaque and opaque-transparent surface with a velocity permutation. Camera motion is written first for the whole frame, then dynamic objects overwrite their pixels. The upscaler reprojects its history with it. Pure translucency does not write velocity, it would smear what is behind it.
FSR 3, FSR 4, and FSR 3 (custom) additionally receive a reactive mask. #VelocityBuffer[RT1] marks vehicle pixels; the dynamicDisocclusion pass compares that mask with the previous frame’s mask after reprojection and writes #ReactiveMask where a pixel was a vehicle last frame and static world this frame. Those pixels are rebuilt from the current frame alone, which removes the car shaped smear a moving vehicle would otherwise leave on the road. The band is only one frame wide and only appears when history is genuinely wrong, so it can be fully reactive without eating the anti-aliasing. DLSS and XeSS use their own disocclusion heuristics and do not receive the mask.
Textures are sampled with a mip bias of log2(render width / display width) - 1. The first term keeps texture detail at the display resolution rather than the render resolution; the extra -1 is what the DLSS and FSR integration guides ask for, one mip sharper so the upscaler has high frequency content to reconstruct. SMAA T2x and Bilinear use no bias: neither reconstructs detail, so a sharper mip would only add back aliasing.
Detail levels are chosen against the display resolution, not the render resolution. Metrics that compare a projected pixel size against a threshold are scaled by display height / render height first, so lowering the render scale does not swap in lower detail meshes or foliage. Reflections and the reflection texture sizes follow the render resolution.
The history is what turns a 50% render into a full resolution image, and it is also where every temporal artifact comes from. It is dropped and restarted from the current frame when:
max(4 m, 4000 m/s x frame time) in one frame and the camera mode did not declare its motion continuous. Camera modes set cameraMotionContinuous on the render view to keep history through fast but legitimate moves.prevFrameData.invalidated), for example on vehicle reset or teleport.After a reset the image is soft for as many frames as the jitter sequence is long. Like every temporal upscaler, BeamNG.drive converges faster at high frame rates and at high render scales. A CPU bound frame rate or a VSync cap slows convergence just as much as a GPU bound one.
FXAA, SMAA, and SMAA T2x use deferred resolve. In code, FXAA and SMAA share the Bilinear mode. These modes render, tone map, and anti-alias entirely at render resolution, and the finished LDR image is stretched to the display as the last step of the view. UpscaleDispatch is a no-op for them and AfterPostFX runs at render resolution. This is why FXAA and SMAA at a reduced render scale look softer than a temporal upscaler at the same scale: nothing reconstructs detail, the image is simply smaller.
SMAA T2x is the exception in between. It uses the two phase jitter and #VelocityBuffer to blend two frames, so it removes shimmering that FXAA and SMAA cannot, but it does not upscale.
The upscaler produces color only. For display resolution passes and overlays that depth test against the scene, the render resolution depth is upsampled into a display resolution depth target right after the dispatch. When the render scale is 100% the scene depth is attached directly.
#prepass) go before UpscaleDispatch, at render resolution.AfterPostFX or later. Before the upscaler they are treated as noise and either smeared or amplified.#VelocityBuffer[RT0], must handle the jitter delta between frames, and must reset when UpscaleDispatch resets. The shipped depth of field temporal pass is the reference.targetSize equals the window size. Read targetSize, rtParams, and dispatchRect from the constants.See Post Process Effects for the constant names and the buffer list.
Was this article helpful?