FFXIV depends on this. It calls SetFullscreenState when it receives WM_WINDOWPOSCHANGED. Through luck the recursion aborts after two calls. The user-visible bug is that after leaving fullscreen, the game's window is still TOPMOST because we overwrite the stored pre-fullscreen window flags.
-- v3: dxgi/tests: Test nested SetFullscreenState from the same thread. dxgi/tests: Test nested fullscreen application from different thread. dxgi: Catch nested SetFullscreenState invocations. dxgi: Unlock the wined3d mutex after storing the new target. dxgi/tests: Run test_swapchain_window_messages on d3d12. dxgi/tests: Run test_resize_target_wndproc on d3d12 too. dxgi/tests: Fix UnregisterClass call in test_resize_target_wndproc.
From: Stefan Dösinger stefan@codeweavers.com
--- dlls/dxgi/tests/dxgi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/dxgi/tests/dxgi.c b/dlls/dxgi/tests/dxgi.c index e0465b90ed0..fffca68c9c1 100644 --- a/dlls/dxgi/tests/dxgi.c +++ b/dlls/dxgi/tests/dxgi.c @@ -3418,7 +3418,7 @@ static DWORD WINAPI window_thread(void *data) DestroyWindow(thread_data->window); thread_data->window = NULL;
- UnregisterClassA("dxgi_test_wndproc_wc", GetModuleHandleA(NULL)); + UnregisterClassA("dxgi_resize_target_wndproc_wc", GetModuleHandleA(NULL));
return 0; }
From: Stefan Dösinger stefan@codeweavers.com
--- dlls/dxgi/tests/dxgi.c | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-)
diff --git a/dlls/dxgi/tests/dxgi.c b/dlls/dxgi/tests/dxgi.c index fffca68c9c1..b05b4a2e19c 100644 --- a/dlls/dxgi/tests/dxgi.c +++ b/dlls/dxgi/tests/dxgi.c @@ -3423,15 +3423,13 @@ static DWORD WINAPI window_thread(void *data) return 0; }
-static void test_resize_target_wndproc(void) +static void test_resize_target_wndproc(IUnknown *device, BOOL is_d3d12) { struct window_thread_data thread_data; DXGI_SWAP_CHAIN_DESC swapchain_desc; IDXGISwapChain *swapchain; IDXGIFactory *factory; - IDXGIAdapter *adapter; DXGI_MODE_DESC mode; - IDXGIDevice *device; unsigned int ret; ULONG refcount; LONG_PTR data; @@ -3439,11 +3437,7 @@ static void test_resize_target_wndproc(void) HRESULT hr; RECT rect;
- if (!(device = create_device(0))) - { - skip("Failed to create device.\n"); - return; - } + get_factory(device, is_d3d12, &factory);
memset(&thread_data, 0, sizeof(thread_data)); thread_data.window_created = CreateEventA(NULL, FALSE, FALSE, NULL); @@ -3456,11 +3450,6 @@ static void test_resize_target_wndproc(void) ret = WaitForSingleObject(thread_data.window_created, INFINITE); ok(ret == WAIT_OBJECT_0, "Failed to wait for thread, ret %#x, last error %#lx.\n", ret, GetLastError());
- hr = IDXGIDevice_GetAdapter(device, &adapter); - ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - hr = IDXGIAdapter_GetParent(adapter, &IID_IDXGIFactory, (void **)&factory); - ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - swapchain_desc.BufferDesc.Width = 800; swapchain_desc.BufferDesc.Height = 600; swapchain_desc.BufferDesc.RefreshRate.Numerator = 60; @@ -3471,12 +3460,12 @@ static void test_resize_target_wndproc(void) swapchain_desc.SampleDesc.Count = 1; swapchain_desc.SampleDesc.Quality = 0; swapchain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - swapchain_desc.BufferCount = 1; + swapchain_desc.BufferCount = is_d3d12 ? 2 : 1; swapchain_desc.OutputWindow = thread_data.window; swapchain_desc.Windowed = TRUE; - swapchain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + swapchain_desc.SwapEffect = is_d3d12 ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD;; swapchain_desc.Flags = 0; - hr = IDXGIFactory_CreateSwapChain(factory, (IUnknown *)device, &swapchain_desc, &swapchain); + hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
data = SetWindowLongPtrA(thread_data.window, GWLP_USERDATA, (LONG_PTR)swapchain); @@ -3503,11 +3492,8 @@ static void test_resize_target_wndproc(void) refcount = IDXGISwapChain_Release(swapchain); ok(!refcount, "IDXGISwapChain has %lu references left.\n", refcount);
- IDXGIAdapter_Release(adapter); - refcount = IDXGIDevice_Release(device); - ok(!refcount, "Device has %lu references left.\n", refcount); refcount = IDXGIFactory_Release(factory); - ok(!refcount, "Factory has %lu references left.\n", refcount); + ok(refcount == !is_d3d12, "Got unexpected refcount %lu.\n", refcount);
ret = SetEvent(thread_data.finished); ok(ret, "Failed to set event, last error %#lx.\n", GetLastError()); @@ -7673,7 +7659,6 @@ START_TEST(dxgi) queue_test(test_parents); queue_test(test_output); queue_test(test_find_closest_matching_mode); - queue_test(test_resize_target_wndproc); queue_test(test_create_factory); queue_test(test_private_data); queue_test(test_maximum_frame_latency); @@ -7705,6 +7690,7 @@ START_TEST(dxgi) run_on_d3d10(test_default_fullscreen_target_output); run_on_d3d10(test_mode_change); run_on_d3d10(test_swapchain_present_count); + run_on_d3d10(test_resize_target_wndproc);
if (!(d3d12_module = LoadLibraryA("d3d12.dll"))) { @@ -7736,6 +7722,7 @@ START_TEST(dxgi) run_on_d3d12(test_default_fullscreen_target_output); run_on_d3d12(test_mode_change); run_on_d3d12(test_swapchain_present_count); + run_on_d3d12(test_resize_target_wndproc);
FreeLibrary(d3d12_module); }
From: Stefan Dösinger stefan@codeweavers.com
--- dlls/dxgi/tests/dxgi.c | 31 +++++++++---------------------- 1 file changed, 9 insertions(+), 22 deletions(-)
diff --git a/dlls/dxgi/tests/dxgi.c b/dlls/dxgi/tests/dxgi.c index b05b4a2e19c..cbcc2801bdb 100644 --- a/dlls/dxgi/tests/dxgi.c +++ b/dlls/dxgi/tests/dxgi.c @@ -5831,14 +5831,12 @@ static LRESULT CALLBACK test_wndproc(HWND hwnd, unsigned int message, WPARAM wpa return DefWindowProcA(hwnd, message, wparam, lparam); }
-static void test_swapchain_window_messages(void) +static void test_swapchain_window_messages(IUnknown *device, BOOL is_d3d12) { DXGI_SWAP_CHAIN_DESC swapchain_desc; IDXGISwapChain *swapchain; DXGI_MODE_DESC mode_desc; IDXGIFactory *factory; - IDXGIAdapter *adapter; - IDXGIDevice *device; ULONG refcount; WNDCLASSA wc; HWND window; @@ -5900,12 +5898,6 @@ static void test_swapchain_window_messages(void) {0, FALSE, 0}, };
- if (!(device = create_device(0))) - { - skip("Failed to create device.\n"); - return; - } - memset(&wc, 0, sizeof(wc)); wc.lpfnWndProc = test_wndproc; wc.lpszClassName = "dxgi_test_wndproc_wc"; @@ -5913,11 +5905,7 @@ static void test_swapchain_window_messages(void) window = CreateWindowA("dxgi_test_wndproc_wc", "dxgi_test", 0, 0, 0, 400, 200, 0, 0, 0, 0); ok(!!window, "Failed to create window.\n");
- hr = IDXGIDevice_GetAdapter(device, &adapter); - ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - hr = IDXGIAdapter_GetParent(adapter, &IID_IDXGIFactory, (void **)&factory); - ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - IDXGIAdapter_Release(adapter); + get_factory(device, is_d3d12, &factory);
swapchain_desc.BufferDesc.Width = 800; swapchain_desc.BufferDesc.Height = 600; @@ -5929,16 +5917,16 @@ static void test_swapchain_window_messages(void) swapchain_desc.SampleDesc.Count = 1; swapchain_desc.SampleDesc.Quality = 0; swapchain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - swapchain_desc.BufferCount = 1; + swapchain_desc.BufferCount = is_d3d12 ? 2 : 1; swapchain_desc.OutputWindow = window; swapchain_desc.Windowed = TRUE; - swapchain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + swapchain_desc.SwapEffect = is_d3d12 ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD; swapchain_desc.Flags = 0;
/* create swapchain */ flush_events(); expect_no_messages = TRUE; - hr = IDXGIFactory_CreateSwapChain(factory, (IUnknown *)device, &swapchain_desc, &swapchain); + hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); flush_events(); expect_no_messages = FALSE; @@ -6003,7 +5991,7 @@ static void test_swapchain_window_messages(void)
expect_messages = enter_fullscreen_messages; expect_messages_broken = enter_fullscreen_messages_vista; - hr = IDXGIFactory_CreateSwapChain(factory, (IUnknown *)device, &swapchain_desc, &swapchain); + hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); flush_events(); todo_wine @@ -6025,10 +6013,8 @@ done: ok(!refcount, "IDXGISwapChain has %lu references left.\n", refcount); DestroyWindow(window);
- refcount = IDXGIDevice_Release(device); - ok(!refcount, "Device has %lu references left.\n", refcount); refcount = IDXGIFactory_Release(factory); - ok(!refcount, "Factory has %lu references left.\n", refcount); + ok(refcount == !is_d3d12, "Got unexpected refcount %lu.\n", refcount);
UnregisterClassA("dxgi_test_wndproc_wc", GetModuleHandleA(NULL)); } @@ -7675,7 +7661,6 @@ START_TEST(dxgi) test_gamma_control(); test_multi_adapter(); test_swapchain_parameters(); - test_swapchain_window_messages(); test_swapchain_window_styles(); run_on_d3d10(test_set_fullscreen); run_on_d3d10(test_resize_target); @@ -7691,6 +7676,7 @@ START_TEST(dxgi) run_on_d3d10(test_mode_change); run_on_d3d10(test_swapchain_present_count); run_on_d3d10(test_resize_target_wndproc); + run_on_d3d10(test_swapchain_window_messages);
if (!(d3d12_module = LoadLibraryA("d3d12.dll"))) { @@ -7723,6 +7709,7 @@ START_TEST(dxgi) run_on_d3d12(test_mode_change); run_on_d3d12(test_swapchain_present_count); run_on_d3d12(test_resize_target_wndproc); + run_on_d3d12(test_swapchain_window_messages);
FreeLibrary(d3d12_module); }
From: Stefan Dösinger stefan@codeweavers.com
Otherwise a second thread might set it to something else between the unlock and assignment. --- dlls/dxgi/swapchain.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-)
diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c index 9677142e0af..21e6db257f9 100644 --- a/dlls/dxgi/swapchain.c +++ b/dlls/dxgi/swapchain.c @@ -410,12 +410,11 @@ static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_SetFullscreen swapchain_desc.output = dxgi_output->wined3d_output; swapchain_desc.windowed = !fullscreen; hr = wined3d_swapchain_state_set_fullscreen(state, &swapchain_desc, NULL); - wined3d_mutex_unlock(); if (FAILED(hr)) { IDXGIOutput_Release(target); - - return DXGI_ERROR_NOT_CURRENTLY_AVAILABLE; + hr = DXGI_ERROR_NOT_CURRENTLY_AVAILABLE; + goto done; }
if (!fullscreen) @@ -428,7 +427,9 @@ static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_SetFullscreen IDXGIOutput_Release(swapchain->target); swapchain->target = target;
- return S_OK; +done: + wined3d_mutex_unlock(); + return hr; }
static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetFullscreenState(IDXGISwapChain1 *iface, @@ -2195,13 +2196,20 @@ static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d12_swapchain_SetFullscreen
if (FAILED(hr = wined3d_swapchain_desc_from_dxgi(&wined3d_desc, target, window, swapchain_desc, fullscreen_desc))) - goto fail; + { + IDXGIOutput_Release(target); + return hr; + } + wined3d_mutex_lock(); wined3d_desc.windowed = !fullscreen; hr = wined3d_swapchain_state_set_fullscreen(swapchain->state, &wined3d_desc, NULL); - wined3d_mutex_unlock(); if (FAILED(hr)) - goto fail; + { + IDXGIOutput_Release(target); + hr = DXGI_ERROR_NOT_CURRENTLY_AVAILABLE; + goto done; + }
fullscreen_desc->Windowed = wined3d_desc.windowed; if (!fullscreen) @@ -2214,12 +2222,9 @@ static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d12_swapchain_SetFullscreen IDXGIOutput_Release(swapchain->target); swapchain->target = target;
- return S_OK; - -fail: - IDXGIOutput_Release(target); - - return DXGI_ERROR_NOT_CURRENTLY_AVAILABLE; +done: + wined3d_mutex_unlock(); + return hr; }
static HRESULT STDMETHODCALLTYPE d3d12_swapchain_GetFullscreenState(IDXGISwapChain4 *iface,
From: Stefan Dösinger stefan@codeweavers.com
--- dlls/dxgi/dxgi_private.h | 1 + dlls/dxgi/swapchain.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+)
diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h index dd17e39eca6..fa704d28b0f 100644 --- a/dlls/dxgi/dxgi_private.h +++ b/dlls/dxgi/dxgi_private.h @@ -183,6 +183,7 @@ struct d3d11_swapchain
IDXGIOutput *target; LONG present_count; + LONG in_set_fullscreen_state; };
HRESULT d3d11_swapchain_init(struct d3d11_swapchain *swapchain, struct dxgi_device *device, diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c index 21e6db257f9..4f64978fe09 100644 --- a/dlls/dxgi/swapchain.c +++ b/dlls/dxgi/swapchain.c @@ -383,6 +383,8 @@ static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_SetFullscreen struct wined3d_swapchain_desc swapchain_desc; struct wined3d_swapchain_state *state; struct dxgi_output *dxgi_output; + LONG in_set_fullscreen_state; + BOOL old_fs; HRESULT hr;
TRACE("iface %p, fullscreen %#x, target %p.\n", iface, fullscreen, target); @@ -404,6 +406,18 @@ static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_SetFullscreen } dxgi_output = unsafe_impl_from_IDXGIOutput(target);
+ /* DXGI catches nested SetFullscreenState invocations, earlier versions of d3d + * do not. Final Fantasy XIV depends on this behavior. It tries to call SFS on + * WM_WINDOWPOSCHANGED messages. */ + in_set_fullscreen_state = InterlockedExchange(&swapchain->in_set_fullscreen_state, 1); + if (in_set_fullscreen_state) + { + WARN("Nested invocation of SetFullscreenState.\n"); + IDXGIOutput_Release(target); + IDXGISwapChain1_GetFullscreenState(iface, &old_fs, NULL); + return old_fs == fullscreen ? S_OK : DXGI_STATUS_MODE_CHANGE_IN_PROGRESS; + } + wined3d_mutex_lock(); state = wined3d_swapchain_get_state(swapchain->wined3d_swapchain); wined3d_swapchain_get_desc(swapchain->wined3d_swapchain, &swapchain_desc); @@ -429,6 +443,7 @@ static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_SetFullscreen
done: wined3d_mutex_unlock(); + InterlockedExchange(&swapchain->in_set_fullscreen_state, 0); return hr; }
@@ -1016,6 +1031,7 @@ struct d3d12_swapchain IDXGIOutput *target; DXGI_SWAP_CHAIN_DESC1 desc; DXGI_SWAP_CHAIN_FULLSCREEN_DESC fullscreen_desc; + LONG in_set_fullscreen_state;
ID3D12Fence *frame_latency_fence; HANDLE frame_latency_event; @@ -2174,6 +2190,8 @@ static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d12_swapchain_SetFullscreen const DXGI_SWAP_CHAIN_DESC1 *swapchain_desc = &swapchain->desc; struct wined3d_swapchain_desc wined3d_desc; HWND window = swapchain->window; + LONG in_set_fullscreen_state; + BOOL old_fs; HRESULT hr;
TRACE("iface %p, fullscreen %#x, target %p.\n", iface, fullscreen, target); @@ -2201,6 +2219,15 @@ static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d12_swapchain_SetFullscreen return hr; }
+ in_set_fullscreen_state = InterlockedExchange(&swapchain->in_set_fullscreen_state, 1); + if (in_set_fullscreen_state) + { + WARN("Nested invocation of SetFullscreenState.\n"); + IDXGIOutput_Release(target); + IDXGISwapChain4_GetFullscreenState(iface, &old_fs, NULL); + return old_fs == fullscreen ? S_OK : DXGI_STATUS_MODE_CHANGE_IN_PROGRESS; + } + wined3d_mutex_lock(); wined3d_desc.windowed = !fullscreen; hr = wined3d_swapchain_state_set_fullscreen(swapchain->state, &wined3d_desc, NULL); @@ -2224,6 +2251,7 @@ static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d12_swapchain_SetFullscreen
done: wined3d_mutex_unlock(); + InterlockedExchange(&swapchain->in_set_fullscreen_state, 0); return hr; }
From: Stefan Dösinger stefan@codeweavers.com
--- dlls/dxgi/tests/dxgi.c | 47 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 6 deletions(-)
diff --git a/dlls/dxgi/tests/dxgi.c b/dlls/dxgi/tests/dxgi.c index cbcc2801bdb..097f85b2ac8 100644 --- a/dlls/dxgi/tests/dxgi.c +++ b/dlls/dxgi/tests/dxgi.c @@ -3358,25 +3358,42 @@ static void test_resize_target(IUnknown *device, BOOL is_d3d12) ok(refcount == !is_d3d12, "Got unexpected refcount %lu.\n", refcount); }
+struct resize_target_data +{ + IDXGISwapChain *swapchain; + BOOL test_nested_sfs; +}; + static LRESULT CALLBACK resize_target_wndproc(HWND hwnd, unsigned int message, WPARAM wparam, LPARAM lparam) { - IDXGISwapChain *swapchain = (IDXGISwapChain *)GetWindowLongPtrA(hwnd, GWLP_USERDATA); + struct resize_target_data *data = (struct resize_target_data *)GetWindowLongPtrA(hwnd, GWLP_USERDATA); DXGI_SWAP_CHAIN_DESC desc; HRESULT hr; + BOOL fs;
switch (message) { case WM_SIZE: - ok(!!swapchain, "GWLP_USERDATA is NULL.\n"); - hr = IDXGISwapChain_GetDesc(swapchain, &desc); + ok(!!data, "GWLP_USERDATA is NULL.\n"); + hr = IDXGISwapChain_GetDesc(data->swapchain, &desc); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); ok(desc.BufferDesc.Width == 800, "Got unexpected buffer width %u.\n", desc.BufferDesc.Width); ok(desc.BufferDesc.Height == 600, "Got unexpected buffer height %u.\n", desc.BufferDesc.Height); return 0;
- default: - return DefWindowProcA(hwnd, message, wparam, lparam); + case WM_WINDOWPOSCHANGED: + if (!data->test_nested_sfs) + break; + + /* We are not supposed to deadlock if the window is owned by a different thread. + * The current fullscreen state and consequently the return value of the nested + * SetFullscreenState call are racy on Windows, do not test them. */ + hr = IDXGISwapChain_GetFullscreenState(data->swapchain, &fs, NULL); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + IDXGISwapChain_SetFullscreenState(data->swapchain, FALSE, NULL); + break; } + return DefWindowProcA(hwnd, message, wparam, lparam); }
struct window_thread_data @@ -3425,6 +3442,7 @@ static DWORD WINAPI window_thread(void *data)
static void test_resize_target_wndproc(IUnknown *device, BOOL is_d3d12) { + struct resize_target_data window_data = {0}; struct window_thread_data thread_data; DXGI_SWAP_CHAIN_DESC swapchain_desc; IDXGISwapChain *swapchain; @@ -3436,6 +3454,7 @@ static void test_resize_target_wndproc(IUnknown *device, BOOL is_d3d12) HANDLE thread; HRESULT hr; RECT rect; + BOOL fs;
get_factory(device, is_d3d12, &factory);
@@ -3468,7 +3487,8 @@ static void test_resize_target_wndproc(IUnknown *device, BOOL is_d3d12) hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
- data = SetWindowLongPtrA(thread_data.window, GWLP_USERDATA, (LONG_PTR)swapchain); + window_data.swapchain = swapchain; + data = SetWindowLongPtrA(thread_data.window, GWLP_USERDATA, (LONG_PTR)&window_data); ok(!data, "Got unexpected GWLP_USERDATA %p.\n", (void *)data);
memset(&mode, 0, sizeof(mode)); @@ -3489,6 +3509,21 @@ static void test_resize_target_wndproc(IUnknown *device, BOOL is_d3d12) ok(rect.right == mode.Width && rect.bottom == mode.Height, "Got unexpected client rect %s.\n", wine_dbgstr_rect(&rect));
+ /* Win7 testbot reports no output for the swapchain and can't switch to fullscreen. */ + window_data.test_nested_sfs = TRUE; + hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL); + ok(hr == S_OK || broken(hr == DXGI_ERROR_UNSUPPORTED), "Got unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) + { + hr = IDXGISwapChain_GetFullscreenState(swapchain, &fs, NULL); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(fs, "Got unexpected fullscreen state %x.\n", fs); + } + window_data.test_nested_sfs = FALSE; + + hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + refcount = IDXGISwapChain_Release(swapchain); ok(!refcount, "IDXGISwapChain has %lu references left.\n", refcount);
From: Stefan Dösinger stefan@codeweavers.com
I am still deliberately not testing after which message the swapchain state changes from the old state to the new until we find a game that depends on this.
ddraw, d3d8 and d3d9 do not catch recursive SetDisplayMode or Reset() calls. They will recurse for about 16 times and then return library specific weird return values like 0xd0000510 (NTSTATUS, but nonexistent facility) or 0x88760868 (non-existent HRESULT facility). I suspect it aborts when the kernel-side message processing stack is exhausted. As such, an application depending on nested calls in those libraries would have to deliberately do it, but limit the amount of nesting to a non-zero amount. --- dlls/dxgi/tests/dxgi.c | 44 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+)
diff --git a/dlls/dxgi/tests/dxgi.c b/dlls/dxgi/tests/dxgi.c index 097f85b2ac8..d9b1472df16 100644 --- a/dlls/dxgi/tests/dxgi.c +++ b/dlls/dxgi/tests/dxgi.c @@ -5847,14 +5847,54 @@ static BOOL check_message(const struct message *expected,
static LRESULT CALLBACK test_wndproc(HWND hwnd, unsigned int message, WPARAM wparam, LPARAM lparam) { + IDXGISwapChain *swapchain = (IDXGISwapChain *)GetWindowLongPtrA(hwnd, GWLP_USERDATA); + static BOOL reentry; + IDXGIOutput *target; + HRESULT hr, hr2; + BOOL fs; + flaky ok(!expect_no_messages, "Got unexpected message %#x, hwnd %p, wparam %#Ix, lparam %#Ix.\n", message, hwnd, wparam, lparam);
+ ok(!reentry, "Re-entered wndproc in nested SetFullscreenState call\n"); + if (expect_messages) { if (check_message(expect_messages, hwnd, message, wparam, lparam)) + { ++expect_messages; + + if (swapchain) + { + reentry = TRUE; + + hr = IDXGISwapChain_GetFullscreenState(swapchain, &fs, NULL); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + /* Priority of error values. */ + hr = IDXGISwapChain_GetContainingOutput(swapchain, &target); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, target); + ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#lx.\n", DXGI_ERROR_INVALID_CALL); + IDXGIOutput_Release(target); + + hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL); + hr2 = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL); + + if (fs) + { + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(hr2 == DXGI_STATUS_MODE_CHANGE_IN_PROGRESS, "Got unexpected hr %#lx.\n", hr); + } + else + { + ok(hr == DXGI_STATUS_MODE_CHANGE_IN_PROGRESS, "Got unexpected hr %#lx.\n", hr); + ok(hr2 == S_OK, "Got unexpected hr %#lx.\n", hr); + } + reentry = FALSE; + } + } }
if (expect_messages_broken) @@ -5986,12 +6026,14 @@ static void test_swapchain_window_messages(IUnknown *device, BOOL is_d3d12) ok(!expect_messages->message, "Expected message %#x.\n", expect_messages->message);
/* enter fullscreen */ + SetWindowLongPtrA(window, GWLP_USERDATA, (LONG_PTR)swapchain); expect_messages = enter_fullscreen_messages; expect_messages_broken = enter_fullscreen_messages_vista; hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL); ok(hr == S_OK || hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE || broken(hr == DXGI_ERROR_UNSUPPORTED), /* Win 7 testbot */ "Got unexpected hr %#lx.\n", hr); + SetWindowLongPtrA(window, GWLP_USERDATA, (LONG_PTR)NULL); if (FAILED(hr)) { skip("Could not change fullscreen state.\n"); @@ -6036,9 +6078,11 @@ static void test_swapchain_window_messages(IUnknown *device, BOOL is_d3d12) expect_messages_broken = NULL;
/* leave fullscreen */ + SetWindowLongPtrA(window, GWLP_USERDATA, (LONG_PTR)swapchain); expect_messages = leave_fullscreen_messages; hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + SetWindowLongPtrA(window, GWLP_USERDATA, (LONG_PTR)NULL); flush_events(); ok(!expect_messages->message, "Expected message %#x.\n", expect_messages->message); expect_messages = NULL;
On Mon Feb 27 08:45:21 2023 +0000, Stefan Dösinger wrote:
changed this line in [version 3 of the diff](/wine/wine/-/merge_requests/1964/diffs?diff_id=34680&start_sha=742dd7555698b8f3427f6a58fb4f75aca645987c#1a5ca62987511b09e4db9a7acacf45d80c135b00_186_186)
I changed that in the latest version
"dxgi: Unlock the wined3d mutex after storing the new target." should improve the locking, and setting in_set_fullscreen_state piggypacks on top of that. Now we shouldn't race re setting swapchain->target vs unlocking
weird return values like 0xd0000510 (NTSTATUS, but nonexistent facility)
That corresponds to `STATUS_ALREADY_INITIALIZED`. I'm not sure, but I remember reading somewhere that in `NTSTATUS` values, bit 28 (the low bit of the high nibble) is set to turn it into an `HRESULT`... (/me checks) It's documented in MS's `<ntstatus.h>` to be reserved, but it's definitely not part of the facility code.
or 0x88760868 (non-existent HRESULT facility)
That's `D3DERR_DEVICELOST`. Facility 0x876 is the old DirectDraw/Direct3D facility code (`_FACDD`/`_FACD3D`); 0x868 is 2152 in decimal. You won't find these in `<winerror.h>`.
This merge request was approved by Jan Sikorski.
Patch 3/7 is failing for me; I'm currently trying to determine why.
Patch 3/7 is failing for me; I'm currently trying to determine why.
It fails because creating a d3d12 swapchain initially in the fullscreen state doesn't actually work. It works with the following diff, to match d3d11. It'd probably be nicer to just call wined3d_swapchain_state_set_fullscreen() inside of wined3d_swapchain_state_init(), but it's not immediately clear to me if that's possible.
Stefan, do the tests succeed for you without this diff?
diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c index 9677142e0af..5dd6846d541 100644 --- a/dlls/dxgi/swapchain.c +++ b/dlls/dxgi/swapchain.c @@ -2889,6 +2889,7 @@ static HRESULT d3d12_swapchain_init(struct d3d12_swapchain *swapchain, IWineDXGI VkBool32 supported; VkDevice vk_device; VkFence vk_fence; + bool fullscreen; VkResult vr; HRESULT hr;
@@ -2933,6 +2934,9 @@ static HRESULT d3d12_swapchain_init(struct d3d12_swapchain *swapchain, IWineDXGI return hr; }
+ fullscreen = !wined3d_desc.windowed; + wined3d_desc.windowed = TRUE; + dxgi_factory = unsafe_impl_from_IDXGIFactory((IDXGIFactory *)factory); if (FAILED(hr = wined3d_swapchain_state_create(&wined3d_desc, window, dxgi_factory->wined3d, &swapchain->state_parent, &swapchain->state))) @@ -2941,8 +2945,9 @@ static HRESULT d3d12_swapchain_init(struct d3d12_swapchain *swapchain, IWineDXGI return hr; }
- if (!fullscreen_desc->Windowed) + if (fullscreen) { + wined3d_desc.windowed = FALSE; hr = wined3d_swapchain_state_set_fullscreen(swapchain->state, &wined3d_desc, NULL); if (FAILED(hr)) {
The dxgi tests pass here, but on closer reading I saw "dxgi.c:7762: Tests skipped: Direct3D 12 is not available.", which may indicate a problem on my end :-)
I'll include your patch in the series, but yes I agree that the situation with wined3d_swapchain_state_init(windowed = false) is awkward. It does do something for this mode, but not enough, and at very least could use more documentation.