Win32 DX11 Getting error DXGI_ERROR_DEVICE_REMOVED in backend CreateWindow when using RDP
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
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.)