Skip to content

IMGUI: draw the overlay into the NGX output image on Vulkan (DLSS / DLSS-G) - #596

Open
thierbig wants to merge 1 commit into
Norbyte:mainfrom
thierbig:upscaler-clean
Open

IMGUI: draw the overlay into the NGX output image on Vulkan (DLSS / DLSS-G)#596
thierbig wants to merge 1 commit into
Norbyte:mainfrom
thierbig:upscaler-clean

Conversation

@thierbig

@thierbig thierbig commented Aug 13, 2026

Copy link
Copy Markdown

Problem

When DLSS / DLSS-G is active, the game presents the NGX-upscaled image rather than the swapchain image that the ImGui overlay was rendered into. The result is that the extender UI is completely invisible while upscaling is enabled — the overlay is drawn, but into an image that never reaches the screen.

Approach

Hook NVSDK_NGX_VULKAN_EvaluateFeature_C, probing sl.interposer.dll, nvngx_dlss.dll and nvngx.dll in that order. The hook is installed lazily from NewFrame(), since the NGX module is generally not loaded yet at backend-init time.

After NGX has produced its output, the hook fetches the output image view out of the NGX parameter block via NVSDK_NGX_Parameter_GetVoidPointer and renders the ImGui draw data into it inside a LOAD_OP_LOAD render pass, so the upscaled image is preserved and the overlay composites on top. The image is transitioned GENERALCOLOR_ATTACHMENT_OPTIMAL for the pass and back to GENERAL afterwards, so downstream consumers (frame generation in particular) see the layout they expect. Render passes are cached per VkFormat and framebuffers per VkImageView.

vkQueuePresentKHR and vkCreateSwapchainKHR are also chained through sl.interposer.dll when Streamline is loaded, so the present hook sits on top of the interposer rather than underneath it.

Everything is gated on the NGX module actually being present — with no upscaler loaded, tryInstallNgxEvaluateFeatureHook() finds nothing and the existing swapchain path is unchanged.

Drive-by fix

hookDevice() was casting three device function pointers to the wrong types:

auto createSwapchainKHR  = (PFN_vkCreatePipelineCache*)vkGetDeviceProcAddr(*pDevice, "vkCreateSwapchainKHR");
auto destroySwapchainKHR = (PFN_vkCreatePipelineCache*)vkGetDeviceProcAddr(*pDevice, "vkDestroySwapchainKHR");
auto queuePresentKHR     = (PFN_vkQueuePresentKHR*)vkGetDeviceProcAddr(*pDevice, "vkQueuePresentKHR");

These are pointer-to-function-pointer types, and the first two name the wrong function entirely. It works today because the value is passed straight through to ResolveFunctionTrampoline, but it defeats type checking. Corrected to the declared PFN_ types with explicit reinterpret_cast.

Notes

Single file, Vulkan.inl, +276/-6. No changes to build configuration or third-party dependencies — the NGX types the hook needs are declared locally rather than pulling in the NGX SDK as a dependency.

This supersedes #595, which was opened as a full fork sync and so carried the fork.s vendored External/ tree and local build configuration alongside this change. This PR isolates the feature.

When DLSS / DLSS-G is active the game presents the NGX-upscaled image
rather than the swapchain image the ImGui overlay was rendered into, so
the extender UI is invisible while upscaling is on.

Hook NVSDK_NGX_VULKAN_EvaluateFeature_C (probing sl.interposer.dll,
nvngx_dlss.dll and nvngx.dll) and, after NGX has produced its output,
fetch the output image view from the NGX parameter block and render the
ImGui draw data into it inside a LOAD_OP_LOAD render pass, restoring the
image layout afterwards. Render passes are cached per format and
framebuffers per image view.

Also chain vkQueuePresentKHR / vkCreateSwapchainKHR through
sl.interposer.dll when Streamline is loaded, so the present hook sits on
the interposer rather than under it.

Fix the function pointer types in hookDevice(): vkCreateSwapchainKHR,
vkDestroySwapchainKHR and vkQueuePresentKHR were being cast to
PFN_vkCreatePipelineCache* / PFN_vkQueuePresentKHR* (pointer-to-function-
pointer), which happened to work but is not the declared type.
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