d3d10, d3d12: the window is kept across a restart

The window is left up across a video driver restart, and taken back
by the driver that comes next, for Vulkan, GDI and Direct3D 11. These
are the other two Direct3D drivers anyone uses. They join by the same
means as d3d11: a tag on the window, so that only a driver of the
same kind takes it; win32_window_keep() in place of destroying it;
and, should a swap chain not be had on a window that was taken, a new
window and a second attempt (win32_window_remake()).

d3d10 needs no more than that. Its swap chain is released with its
device, and is of the kind - not the flip model - that a window may
have another of.

d3d12 needs its swap chain gone, and it never went: the Release() in
d3d12_gfx_free() has been compiled out for years under a note that it
crashes eventually. DXGI makes no second flip-model swap chain on a
window that still has one, so as it stood every restart on a kept
window would have fallen back to a new one. When the window is kept
the swap chain is now released, in the order that should be safe: the
queue is drained at the top of the function, the back buffers are let
go of just before, the swap chain is told it is windowed (releasing
one that is not is an error), and it is released while the queue it
was made on is still there. When the window is not kept -
RETROARCH_WINDOW_KEEP=0, or the program is closing - none of that
runs and the driver frees as it always did.

Why the old Release() crashed is not known, so whether this one is
safe is not known either until it has been run: none of it could be
here. Wine starts neither driver. Both compile for Windows. If d3d12
misbehaves, RETROARCH_WINDOW_KEEP=0 is the way back.
This commit is contained in:
LibretroAdmin authored and libretroadmin committed 2026-10-03 12:08:12 +02:00
1 parent afff14a917
commit c66a7ce8f4
2 files changed
+78 -7

No files matched your search

+27 -3
View File
@@ -2442,12 +2442,20 @@ static void d3d10_gfx_free(void* data)
}
}
/* Left up for the next D3D10 driver, where it can be. The swap
* chain went with the device above, and it is of the kind (not the
* flip model) that a window can have another of in any case. */
#if defined(HAVE_WINDOW) && defined(HAVE_MONITOR)
if (!win32_window_keep())
#endif
{
#ifdef HAVE_MONITOR
win32_monitor_from_window();
win32_monitor_from_window();
#endif
#ifdef HAVE_WINDOW
win32_destroy_window();
win32_destroy_window();
#endif
}
free(d3d10);
}
@@ -2509,6 +2517,9 @@ static void *d3d10_gfx_init(const video_info_t* video,
wndclass.lpfnWndProc = win32_window_proc;
win32_window_proc_setup(WIN32_WINDOW_D3D);
#ifdef HAVE_WINDOW
/* the window may be left up for the next D3D10 driver, and taken
* from the last (win32_window_keep()) */
win32_window_tag("d3d10");
win32_window_init(&wndclass, true, NULL);
#endif
@@ -2546,7 +2557,20 @@ static void *d3d10_gfx_init(const video_info_t* video,
NULL
#endif
))
goto error;
{
#if defined(HAVE_WINDOW) && defined(HAVE_MONITOR)
/* On a window the last driver left up: if a device and swap
* chain cannot be made on it, a new window, and once more. */
if ( !win32_window_was_taken()
|| !win32_window_remake(d3d10, d3d10->vp.full_dims,
video->fullscreen)
|| !d3d10_init_swapchain(d3d10,
VIDEO_SCALE_W(d3d10->vp.full_dims),
VIDEO_SCALE_H(d3d10->vp.full_dims),
main_window.hwnd))
#endif
goto error;
}
{
D3D10Texture2D backBuffer;
+51 -4
View File
@@ -4313,6 +4313,7 @@ static void d3d12_hw_ring_free(d3d12_video_t *d3d12);
static void d3d12_gfx_free(void* data)
{
unsigned i;
bool window_kept = false;
d3d12_video_t* d3d12 = (d3d12_video_t*)data;
if (!d3d12)
@@ -4426,6 +4427,29 @@ static void d3d12_gfx_free(void* data)
Release(d3d12->chain.renderTargets[0]);
Release(d3d12->chain.renderTargets[1]);
#if defined(HAVE_WINDOW) && defined(HAVE_MONITOR)
/* The window is left up for the next D3D12 driver, where it can be
* (win32_window_keep()). DXGI makes no second flip-model swap chain
* on a window that still has one, so then this one has to go - and
* it never did: the Release() further down has been compiled out
* for years with a note that it crashes eventually. It is done
* here instead, in the order that should be safe: the queue was
* drained at the top, the back buffers were let go of just above,
* the swap chain is told it is windowed (it always is; releasing
* one that is not is an error), and it is released while the
* queue it was made on is still there. Only when the window is
* kept: with RETROARCH_WINDOW_KEEP=0, or on the way out of the
* program, nothing here runs and the driver frees as it did. */
window_kept = win32_window_keep();
if (window_kept && d3d12->chain.handle)
{
d3d12->chain.handle->lpVtbl->SetFullscreenState(
d3d12->chain.handle, FALSE, NULL);
Release(d3d12->chain.handle);
d3d12->chain.handle = NULL;
}
#endif
Release(d3d12->queue.fence);
Release(d3d12->queue.cmd);
Release(d3d12->queue.allocator);
@@ -4435,7 +4459,9 @@ static void d3d12_gfx_free(void* data)
CloseHandle(d3d12->queue.fenceEvent);
#if 0
/* Releasing this will crash eventually (?!) */
/* Releasing this will crash eventually (?!)
* (It is released above, before the queue, when the window is
* kept for the next driver - which needs it gone.) */
Release(d3d12->chain.handle);
#endif
@@ -4460,12 +4486,15 @@ static void d3d12_gfx_free(void* data)
video_driver_modify_disp_flags(0, VIDEO_FLAG_HDR_SUPPORT | VIDEO_FLAG_HDR10_SUPPORT | VIDEO_FLAG_SCRGB_SUPPORT);
#endif
if (!window_kept)
{
#ifdef HAVE_MONITOR
win32_monitor_from_window();
win32_monitor_from_window();
#endif
#ifdef HAVE_WINDOW
win32_destroy_window();
win32_destroy_window();
#endif
}
free(d3d12);
}
@@ -5226,6 +5255,10 @@ static void *d3d12_gfx_init(const video_info_t* video,
wndclass.lpfnWndProc = win32_window_proc;
win32_window_proc_setup(WIN32_WINDOW_D3D);
#ifdef HAVE_WINDOW
/* the window may be left up for the next D3D12 driver, and taken
* from the last: d3d12_gfx_free() and the swap chain's creation
* below do what that needs */
win32_window_tag("d3d12");
win32_window_init(&wndclass, true, NULL);
#endif
@@ -5289,7 +5322,21 @@ static void *d3d12_gfx_init(const video_info_t* video,
goto error;
#else
if (!d3d12_init_swapchain(d3d12, VIDEO_SCALE_W(d3d12->vp.full_dims), VIDEO_SCALE_H(d3d12->vp.full_dims), main_window.hwnd))
goto error;
{
#if defined(HAVE_WINDOW) && defined(HAVE_MONITOR)
/* On a window the last driver left up, DXGI may refuse a swap
* chain - something of the old one is still alive on it. Then
* the window is not worth having: a new one, and once more. */
if ( !win32_window_was_taken()
|| !win32_window_remake(d3d12, d3d12->vp.full_dims,
video->fullscreen)
|| !d3d12_init_swapchain(d3d12,
VIDEO_SCALE_W(d3d12->vp.full_dims),
VIDEO_SCALE_H(d3d12->vp.full_dims),
main_window.hwnd))
#endif
goto error;
}
#endif
#ifdef HAVE_DXGI_HDR