Skip to content

STALKER 2 fix: Slate: find the viewport when UE5.5 passes all windows as one array - #443

Open
Remleo wants to merge 1 commit into
praydog:masterfrom
Remleo:slate-windows-array
Open

Remleo wants to merge 1 commit into
praydog:masterfrom
Remleo:slate-windows-array

Conversation

@Remleo

@Remleo Remleo commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

This is a quick fix I made to get STALKER 2 (UE 5.5.4) working. You probably know a better way to solve this; I hope this PR at least helps point at the problem.

Depends on praydog/UESDK#3, which adds sdk::slate::locate_windows_array_offsets. This PR does
not bump the submodule, since the commit is not in your repository yet. It builds once that one is merged
and the submodule is updated.

On Stalker 2 (UE 5.5.4) the hooked draw function takes a TConstArrayView of per-window args in a3, not
FViewportInfo&. a4 does not match the other 5.5 variant either (a4[0] == renderer). So neither path
found the viewport, the UI was never moved to its own layer, and nothing showed in the headset:

[SlateRHIRenderer::DrawWindow_RenderThread] a4 is not UE 5.5 variant!
Failed to find FViewportInfo::GetRenderTargetProvider offset!
No viewport RT provider, skipping!

The change. When a4 is not the 5.5 variant, read a3 as {data, num} and take the viewport of the
first window, using the offsets UESDK reads from the code. After that it is the existing
slate_viewport->GetViewportRenderTargetTexture() path, nothing new.

Each step checks for a live object and falls back to the old path if anything is off:

  • the array data is in the heap, not inside a module
  • the window has a vtable inside a module
  • the viewport has a vtable inside a module

The heap check keeps builds that pass FViewportInfo& in a3 on their old path. There the first field is a
vtable inside the image, so the check rejects it before anything is dereferenced.

Beware of num. There is no capacity after it, since this is an array view and not a TArray. My first
version bounded num by that word, measured it at 0 on some frames and dropped those frames. It is now
bounded by a constant (64 windows).

Tested

Release build, Quest 3 over Virtual Desktop, OpenXR, D3D12.

game UE a3 result
Stalker 2 5.5.4 windows array UI shows, no rejected frames over a session
The Outer Worlds 2 5.4 FViewportInfo& unchanged, FViewportInfo offset 0xe8 as before

On Stalker 2 (UE 5.5.4) the hooked draw function takes a TConstArrayView
of per-window args in a3, not FViewportInfo&, and a4 is not the other 5.5
variant either. The viewport was never found, so the UI was not moved to
its own layer and did not show in the headset.

When a4 is not the 5.5 variant, read a3 as {data, num} and take the
viewport of the first window, using the offsets UESDK reads from the code.
Every step checks for a live object: array data in the heap, window and
viewport with a vtable inside a module. The heap check keeps builds that
pass FViewportInfo& here on their old path, since its first field is a
vtable inside the image. There is no capacity after num; that word
measured 0 and must not be used as a bound.

Needs the UESDK change that adds sdk::slate::locate_windows_array_offsets.

Measured: UI shows on Stalker 2, no rejected frames over a session; The
Outer Worlds 2 (UE 5.4) keeps its FViewportInfo path.
@Remleo
Remleo force-pushed the slate-windows-array branch from c068241 to 9ec72e5 Compare September 22, 2026 12:05
@Remleo Remleo changed the title Slate: find the viewport when UE5.5 passes all windows as one array STALKER 2 fix: Slate: find the viewport when UE5.5 passes all windows as one array Sep 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant