Summary
With Instanced Stereo enabled and TSR as the primary upscaler, any r.ScreenPercentage below 100 makes each eye’s final image contain a second, smaller copy of the scene. The copy is opaque and hard-edged, and it is the full view shrunk to the internal (pre-upscale) eye size. It sits at the position of the secondary view’s internal-resolution ViewRect. At 50% with 2376x2376 eyes, that is a 1188x1188 copy at (1192, 0) in each eye’s output: the top-right quadrant. It is drawn into the eye images themselves, so it shows both in the headset and in the desktop spectator window.
What Type of Bug are you experiencing?
Rendering (Graphics / Niagara)
Steps to Reproduce
- Project settings: Instanced Stereo on (
vr.InstancedStereo=True), deferred shading, Anti-Aliasing Method = TSR (r.AntiAliasingMethod=4), OpenXR plugin enabled. - Play in VR Preview on any level.
- Console:
r.ScreenPercentage 50. - Look at the top right of either eye, in the headset or the spectator window.
- Console:
r.Test.PrimaryScreenPercentageMethodOverride 1(spatial upscale): the copy disappears immediately.r.ScreenPercentage 100also hides it.
Observed in our project. Not yet reduced to a stock template, but nothing project-specific is involved.
Expected Result
Each eye shows only its own view, upscaled to the full eye resolution.
Observed Result
Each eye also shows its view shrunk to the internal resolution and placed at the secondary view’s internal ViewRect: the top-right quadrant at 50%.
DumpGPU (r.DumpGPU.Root *PostProcessing*, images attached):
- View0
TSR UpdateHistoryoutput (TSR.History.Colorslice 3, 2384x2376) is clean (image 2). - View0
Tonemap 2376x2376output (Tonemap, 2384x2376) already contains the copy in [1192..2380]x[0..1188] (image 1). Its inputs are the clean TSR output and BloomY, and its parameters are normal:Color/Outputviewport (0,0)-(2376,2376),ColorToBloom= (1,1,0,0). - The copy is carried through
CompositeDebugPrimitivesintoViewFamilyTexture(4752x2376) for both eyes (image 3).
Affects Versions
5.8
Platform(s)
Linux
Upload an image
Video
N/A
For crash reports, include your callstack
N/A
Additional Notes
Likely cause, from 5.8.1 source:
PreVisibilityFrameSetup(SceneVisibility.cpp) setsInstanceFactor = 2for both eye views when Instanced Stereo is enabled.- Screen passes draw
View.GetStereoPassInstanceFactor()instances:FScreenPassViewInfo(ScreenPass.cpp), and the TonemapDrawRectangle(..., View.GetStereoPassInstanceFactor())in PostProcessTonemap.cpp. ScreenPassStereoOutputSetupVS(InstancedStereo.ush) routesInstanceIdtoSV_ViewportArrayIndex(“Screen passes are only drawn twice so the instance id corresponds to the eye index”).- After TSR, post-processing runs per view at output resolution into per-view textures and sets a single viewport (e.g.
RHICmdList.SetViewport(0, 0, 0, TargetSize.X, TargetSize.Y, 1)in the Tonemap pass). Instance 1 therefore rasterises into viewport slot 1 as last set by the instanced-stereo scene passes, which is the secondary view’s internal ViewRect. (Inferred from the copy’s exact position and size; the dump does not record viewport state.) - At 100% that rect lies outside the per-view texture (x >= 2376 in a 2384-wide target), so the second instance is clipped. Below 100% it lands inside.
Suggested fix direction: per-view screen passes that run after a temporal upscale should draw one instance, or set viewport slot 1 to the view’s own output rect.
Workarounds: vr.InstancedStereo=False; keep r.ScreenPercentage at 100; or use a spatial primary upscaler.
Possibly related: How do you make upscaling work in VR? (UE 5.2, OpenXR): with TSR or FSR, screen percentage “shrinks the viewport” and part of the right eye appears in the left.


