Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Win32 DX11 Getting error DXGI_ERROR_DEVICE_REMOVED in backend CreateWindow when using RDP #8418

Open
lailoken opened this issue Feb 20, 2025 · 1 comment
Labels

Comments

@lailoken
Copy link

Version/Branch of Dear ImGui:

1.91.9 dockking

Back-ends:

imgui_impl_dx11 + imgui_impl_win32

Compiler, OS:

MSVC

Full config/build information:

Dear ImGui 1.91.9 WIP (19184)

sizeof(size_t): 8, sizeof(ImDrawIdx): 4, sizeof(ImDrawVert): 20
define: __cplusplus=199711
define: IMGUI_DISABLE_OBSOLETE_FUNCTIONS
define: _WIN32
define: _WIN64
define: _MSC_VER=1940
define: _MSVC_LANG=202002
define: IMGUI_HAS_VIEWPORT
define: IMGUI_HAS_DOCK

io.BackendPlatformName: imgui_impl_win32
io.BackendRendererName: imgui_impl_dx11
io.ConfigFlags: 0x0000C481
NavEnableKeyboard
DockingEnable
ViewportsEnable
DpiEnableScaleViewports
DpiEnableScaleFonts
io.ConfigViewportsNoAutoMerge
io.ConfigViewportsNoDefaultParent
io.ConfigDockingWithShift
io.ConfigNavCaptureKeyboard
io.ConfigInputTextCursorBlink
io.ConfigWindowsResizeFromEdges
io.ConfigMemoryCompactTimer = 60.0
io.BackendFlags: 0x00001C0E
HasMouseCursors
HasSetMousePos
PlatformHasViewports
HasMouseHoveredViewport
RendererHasVtxOffset
RendererHasViewports

io.Fonts: 11 fonts, Flags: 0x00000000, TexSize: 1024,1024
io.DisplaySize: 1795.00,1161.00
io.DisplayFramebufferScale: 1.00,1.00

style.WindowPadding: 8.00,8.00
style.WindowBorderSize: 1.00
style.FramePadding: 4.00,3.00
style.FrameRounding: 0.00
style.FrameBorderSize: 0.00
style.ItemSpacing: 8.00,4.00
style.ItemInnerSpacing: 4.00,4.00

Details:

In the DX11 backend binding, inside ImGui_ImplDX11_CreateWindow,
The CreateSwapChain call returns an error while using RDP (for some users only).

I've tracked it down to the following steps to reproduce:

While on a host with an NVidia RTX and Intel graphics, inside the NVidia Control Panel I set it to always use NVidia (default is Auto, which uses Intel I assume)

Then I start the ImGui application, causing it to use NVidia (I assume)

Then when I RDP in, I suspect that RDP forces it to use Intel, and this switch then crashes ImGui on the next window/viewport creation.
I can then restart the ImGui application inside RDP without a problem after this.

Switching NVidia Control panel back to "Auto" prevents this. I also suspect that disabling Intel integrated gfx in the BIOS may prevent this, but have not tested.

Ideally ImGui needs to detect this change and select the new device for use.

Screenshots/Video:

No response

Minimal, Complete and Verifiable Example code:

No response

@PathogenDavid
Copy link
Contributor

The fact that things fail in ImGui_ImplDX11_CreateWindow is potentially a red herring. Device removal if generally only ever reported when you interact with DXGI, but the actual cause of the removal can happen well before then.

Are you able to update the graphics drivers on these systems? By default RDP will present the Microsoft Basic Render Driver (a software device) as the default to D3D applications, so I'm wondering if NVIDIA's automatic graphics switching hacks are activating incorrectly when they shouldn't.


Then I start the ImGui application, causing it to use NVidia (I assume)

As mentioned earlier, in theory it actually shouldn't use either.

Ignoring RDP, if these are laptops with dual graphics and your application is not marked with NvOptimusEnablement, it'd actually prefer the Intel GPU.

To avoid any doubt, you can query which adapter was used to create the device:

IDXGIDevice* dxgiDevice;
HRESULT result;
result = g_pd3dDevice->QueryInterface(&dxgiDevice);
assert(SUCCEEDED(result));

IDXGIAdapter* dxgiAdapter;
result = dxgiDevice->GetAdapter(&dxgiAdapter);
assert(SUCCEEDED(result));

DXGI_ADAPTER_DESC desc;
result = dxgiAdapter->GetDesc(&desc);
assert(SUCCEEDED(result));
wprintf(L"Adapter name: %s\n", desc.Description);

(If your app has logic to explicitly select the adapter passed to D3D11CreateDevice/D3D11CreateDeviceAndSwapChain, you can just query it directly.)


Ideally ImGui needs to detect this change and select the new device for use.

The problem is the backend doesn't select the device in the first place, it just uses the one you provide to it. Recovering from device removal is also non-trivial and many apps don't even support it, once this has happened it's too late for the backend to do anything about it because your application is (probably) dead.

I believe it's possible to use NVAPI to query that setting, but that'd be a workaround more than anything.

Your app can also be more explicit about which adapter you use. IDXGIFactory6::EnumAdapterByGpuPreference is the modern method for selecting the appropriate adapter, it should bypass NVIDIA's automatic adapter selection. (Here's an example of manual enumeration, you'll have to replace the D3D12 device creation with D3D11.)


If the graphics drivers don't fix it, we need to investigate why exactly the device is getting removed.

The debug layer can likely provide more details. You can enable it by passing D3D11_CREATE_DEVICE_DEBUG to D3D11CreateDevice. The debug layer technically requires the Windows SDK to be installed in order to work, but you can get away with copying the three d3d11_*sdklayers.dll files and DXGIDebug.dll from C:\Windows\System32\ from your development machine to your app's folder next to your executable on the problematic machine. (Obviously don't include these in your standard install as they aren't meant for redistribution.)

Debug layer messages are logged to the debug output. It's typically viewed in the output window of Visual Studio, but you can view it using this standalone tool.

If you want to avoid mucking with debug layer stuff, you can get a rough idea of why device removal happened by calling GetDeviceRemovedReason after the removal error, but it's usually too vague to be helpful. (The removal reason should be logged with the debug layer turned on. The meanings of the different reasons is better explained here.)

Technically there are system-wide settings that control which messages are logged. These are almost always not configured, but if you have reason to suspect they maybe are, you can clear the filters by calling IDXGIInfoQueue::PushEmptyStorageFilter(DXGI_DEBUG_ALL) before you create your device.


While looking at some of my own code around this sort of thing, I was reminded of this article, and noticed this bit in particular:

If a computer's display driver is not functioning or is disabled, the computer's primary (NULL) adapter might also be called "Microsoft Basic Render Driver." But this adapter has outputs and doesn't have the DXGI_ADAPTER_FLAG_SOFTWARE value set. The operating system and apps use this adapter by default. If a display driver is installed or enabled, apps can receive DXGI_ERROR_DEVICE_REMOVED for this adapter and then must re-enumerate adapters again.

(Emphasis mine)

I am not sure if this applies to the RDP software driver (I thought it did at first, but it turns out to have a different name), but it might explain why your app starts working after the first crash. It may be that something (like that NVIDIA setting or a bug in its implementation within the driver) is causing the NVIDIA GPU to become available within the RDP session. It would be interesting to see what the adapters list looks like before and after the crash.

(You can either enumerate them yourself as mentioned above, or just run dxdiag and look at the display/renderer tab(s). Technically your app can receive a different list though.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants