NVIDIA-RTX / NVIDIA-RTX/Streamline

sl.common: per-Present QueryVideoMemoryInfo on a sleeping NVIDIA adapter causes ~150 ms stalls when rendering on another GPU (laptop + eGPU)

Open
#124 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C
Stars
1.2k
Forks
159
PR merge metrics
No merged PRs in 30d

Description

Summary

presentCommon() in sl.common calls QueryVideoMemoryInfo() on every Present. It calls it on ctx.adapter, which is set on the first frame to "best NVDA adapter". There is no check that this adapter is the adapter the app renders on.

On a laptop with an NVIDIA dGPU and an AMD eGPU, the game renders on the eGPU and Windows keeps the idle dGPU in D3. Each query then blocks the render thread on the dxgkrnl adapter core lock while the GPU power arbiter wakes the dGPU. Result: stalls of about 145 ms, several per second, at any graphics settings.

Environment

  • Game: Diablo IV (Steam), ships Streamline 2.11.1 (sl.common.dll, sl.interposer.dll, sl.dlss.dll, sl.dlss_g.dll, sl.reflex.dll, sl.pcl.dll)
  • Laptop: Intel i7-11370H, Intel Iris Xe, NVIDIA RTX 3050 Ti Laptop GPU (idle, runtime D3)
  • eGPU: AMD Radeon RX 7900 XTX over Thunderbolt, display connected to the eGPU. The game renders on the AMD GPU (game log: Gpus detected (index 0 will be used): AMD Radeon RX 7900 XTX).
  • Windows 11 build 26200

Evidence

  1. PresentMon, 99 s: 1847 frames, average 18.6 FPS. 378 frames > 50 ms. On slow frames the CPU-side time is 181 ms and GPU busy is 15.7 ms, so the GPU is idle while it waits.
  2. WPR capture of 60 s, with CPU and context switch stacks. The render thread waited 174 times, 145 ms on average (25.3 s in total). WaitReason = WrResource. Wait stack:
    ntoskrnl!ExAcquireResourceSharedLite
    dxgkrnl!DXGADAPTER::AcquireCoreResourceShared
    dxgkrnl!DxgkQueryVideoMemoryInfo
    win32u!NtGdiDdDDIQueryVideoMemoryInfo
    dxgi!CDXGIAdapter::QueryVideoMemoryInfo
    sl.common.dll
    sl.interposer.dll
    
    Readying stack (the thread that released the lock):
    dxgkrnl!DXGADAPTER::ReleaseCoreResource
    dxgkrnl!DXGADAPTER::ReleaseCoreSync
    dxgkrnl!DxgkReleaseAdapterCoreSync
    dxgkrnl!DpiPowerArbiterThread
    
    The main thread and the job workers then wait on the render thread, so the whole game stalls.
  3. The RTX 3050 Ti was in D3 at idle.
  4. Workaround confirmed: after I disabled the RTX 3050 Ti in Device Manager, the stutter stopped completely.

The trace does not show the adapter LUID of each query. The link to the dGPU is inferred from points 3 and 4 and from the source below.

Relevant source

v2.11.1, and the same code is on main: source/plugins/sl.common/commonInterface.cpp

// presentCommon()
else if (ctx.adapter)
{
    //! IMPORTANT: Overhead for calling 'QueryVideoMemoryInfo' is 0.01ms
    ctx.adapter->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_LOCAL, &videoMemoryInfo);
// First run, find best NVDA adapter
if (isVendorNvidia(ctx.sysCaps.adapters[i].vendor) && ctx.sysCaps.adapters[i].architecture > hwArch)
    ctx.adapter = reinterpret_cast<IDXGIAdapter3*>(ctx.sysCaps.adapters[i].nativeInterface);

The 0.01 ms assumption holds for an active GPU. It does not hold for a laptop dGPU in runtime D3.

Suggested fix

  • Select ctx.adapter by matching the LUID of the app's device (for example ID3D12Device::GetAdapterLuid()), not only by vendor and architecture. If the app does not render on an NVIDIA adapter, skip the VRAM budget query. This is the same path that an AMD-only system already takes, because ctx.adapter stays null there.
  • Optionally, do not call QueryVideoMemoryInfo on the Present path, or call it less often.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Read source/plugins/sl.common/commonInterface.cpp, focusing on presentCommon() and the code that initializes ctx.adapter. Trace how the rendering device identity can be compared with the selected adapter, then validate the behavior on the laptop/eGPU setup described. Done means an unrelated sleeping adapter is not queried on the Present path and the reported stalls are absent.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
computer-graphics, performance
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.