For Assassin's Creed Syndicate. When switching from fullscreen to a smaller windowed resolution, it calls `SetWindowPos()` centering the window, then calls `ResizeTarget()` and finally `SetFullscreenState()`, and expects the window to preserve the position.
Window style handling is a bit iffy because of the manipulation we do when entering fullscreen, but that's not really new.
-- v3: dxgi/tests: Test window states tracking and restoration in fullscreen mode.
From: Matteo Bruni mbruni@codeweavers.com
--- dlls/wined3d/swapchain.c | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-)
diff --git a/dlls/wined3d/swapchain.c b/dlls/wined3d/swapchain.c index 1a010958a21..14c34b57c89 100644 --- a/dlls/wined3d/swapchain.c +++ b/dlls/wined3d/swapchain.c @@ -2101,6 +2101,25 @@ HRESULT CDECL wined3d_swapchain_state_resize_target(struct wined3d_swapchain_sta } else { + if (FAILED(hr = wined3d_output_get_desc(state->desc.output, &output_desc))) + { + ERR("Failed to get output description, hr %#lx.\n", hr); + wined3d_mutex_unlock(); + return hr; + } + width = output_desc.desktop_rect.right - output_desc.desktop_rect.left; + height = output_desc.desktop_rect.bottom - output_desc.desktop_rect.top; + + GetWindowRect(window, &window_rect); + if (width != window_rect.right - window_rect.left || height != window_rect.bottom - window_rect.top) + { + TRACE("Update saved window state.\n"); + /* Undo changes made by fullscreen_style(). */ + state->style = GetWindowLongW(window, GWL_STYLE) & ~(WS_POPUP | WS_SYSMENU); + state->exstyle = GetWindowLongW(window, GWL_EXSTYLE); + state->original_window_rect = window_rect; + } + if (state->desc.flags & WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH) { actual_mode = *mode; @@ -2111,19 +2130,18 @@ HRESULT CDECL wined3d_swapchain_state_resize_target(struct wined3d_swapchain_sta wined3d_mutex_unlock(); return hr; } - } + if (FAILED(hr = wined3d_output_get_desc(state->desc.output, &output_desc))) + { + ERR("Failed to get output description, hr %#lx.\n", hr); + wined3d_mutex_unlock(); + return hr; + }
- if (FAILED(hr = wined3d_output_get_desc(state->desc.output, &output_desc))) - { - ERR("Failed to get output description, hr %#lx.\n", hr); - wined3d_mutex_unlock(); - return hr; + width = output_desc.desktop_rect.right - output_desc.desktop_rect.left; + height = output_desc.desktop_rect.bottom - output_desc.desktop_rect.top; } - x = output_desc.desktop_rect.left; y = output_desc.desktop_rect.top; - width = output_desc.desktop_rect.right - output_desc.desktop_rect.left; - height = output_desc.desktop_rect.bottom - output_desc.desktop_rect.top; }
wined3d_mutex_unlock();
From: Matteo Bruni mbruni@codeweavers.com
--- dlls/dxgi/tests/dxgi.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-)
diff --git a/dlls/dxgi/tests/dxgi.c b/dlls/dxgi/tests/dxgi.c index 03bccc50a37..f223ab96c2d 100644 --- a/dlls/dxgi/tests/dxgi.c +++ b/dlls/dxgi/tests/dxgi.c @@ -3257,14 +3257,18 @@ static void test_resize_target(IUnknown *device, BOOL is_d3d12) for (adapter_idx = 0; SUCCEEDED(IDXGIFactory_EnumAdapters(factory, adapter_idx, &adapter)); ++adapter_idx) { + winetest_push_context("Adapter %u", adapter_idx); for (output_idx = 0; SUCCEEDED(IDXGIAdapter_EnumOutputs(adapter, output_idx, &output)); ++output_idx) { + winetest_push_context("output %u", adapter_idx); hr = IDXGIOutput_GetDesc(output, &output_desc); - ok(hr == S_OK, "Adapter %u output %u: Got unexpected hr %#lx.\n", adapter_idx, output_idx, hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
for (test_idx = 0; test_idx < ARRAY_SIZE(tests); ++test_idx) { + winetest_push_context("test %u", test_idx); + swapchain_desc.Flags = tests[test_idx].flags; swapchain_desc.OutputWindow = CreateWindowA("static", "dxgi_test", 0, output_desc.DesktopCoordinates.left + tests[test_idx].origin.x, @@ -3282,8 +3286,7 @@ static void test_resize_target(IUnknown *device, BOOL is_d3d12) capture_fullscreen_state(&initial_state.fullscreen_state, swapchain_desc.OutputWindow);
hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain); - ok(hr == S_OK, "Adapter %u output %u test %u: Got unexpected hr %#lx.\n", - adapter_idx, output_idx, test_idx, hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); check_swapchain_fullscreen_state(swapchain, &initial_state);
expected_state = initial_state; @@ -3294,36 +3297,33 @@ static void test_resize_target(IUnknown *device, BOOL is_d3d12) &swapchain_desc, &initial_state.fullscreen_state.monitor_rect, 800, 600, NULL); hr = IDXGISwapChain_GetContainingOutput(swapchain, &expected_state.target); ok(hr == S_OK || broken(hr == DXGI_ERROR_UNSUPPORTED) /* Win 7 testbot */, - "Adapter %u output %u test %u: Got unexpected hr %#lx.\n", - adapter_idx, output_idx, test_idx, hr); + "Unexpected hr %#lx.\n", hr); if (hr == DXGI_ERROR_UNSUPPORTED) { - win_skip("Adapter %u output %u test %u: GetContainingOutput() not supported.\n", - adapter_idx, output_idx, test_idx); + win_skip("GetContainingOutput() not supported.\n"); IDXGISwapChain_Release(swapchain); DestroyWindow(swapchain_desc.OutputWindow); + winetest_pop_context(); continue; }
hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL); ok(hr == S_OK || hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE, - "Adapter %u output %u test %u: Got unexpected hr %#lx.\n", - adapter_idx, output_idx, test_idx, hr); + "Unexpected hr %#lx.\n", hr); if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE) { - skip("Adapter %u output %u test %u: Could not change fullscreen state.\n", - adapter_idx, output_idx, test_idx); + skip("Could not change fullscreen state.\n"); IDXGIOutput_Release(expected_state.target); IDXGISwapChain_Release(swapchain); DestroyWindow(swapchain_desc.OutputWindow); + winetest_pop_context(); continue; } } check_swapchain_fullscreen_state(swapchain, &expected_state);
hr = IDXGISwapChain_ResizeTarget(swapchain, NULL); - ok(hr == DXGI_ERROR_INVALID_CALL, "Adapter %u output %u test %u: Got unexpected hr %#lx.\n", - adapter_idx, output_idx, test_idx, hr); + ok(hr == DXGI_ERROR_INVALID_CALL, "Unexpected hr %#lx.\n", hr); check_swapchain_fullscreen_state(swapchain, &expected_state);
if (tests[test_idx].fullscreen) @@ -3331,8 +3331,7 @@ static void test_resize_target(IUnknown *device, BOOL is_d3d12) test_fullscreen_resize_target(swapchain, &expected_state);
hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL); - ok(hr == S_OK, "Adapter %u output %u test %u: Got unexpected hr %#lx.\n", - adapter_idx, output_idx, test_idx, hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); check_swapchain_fullscreen_state(swapchain, &initial_state); IDXGIOutput_Release(expected_state.target); check_swapchain_fullscreen_state(swapchain, &initial_state); @@ -3346,14 +3345,16 @@ static void test_resize_target(IUnknown *device, BOOL is_d3d12) }
refcount = IDXGISwapChain_Release(swapchain); - ok(!refcount, "Adapter %u output %u test %u: IDXGISwapChain has %lu references left.\n", - adapter_idx, output_idx, test_idx, refcount); + ok(!refcount, "IDXGISwapChain has %lu references left.\n", refcount); check_window_fullscreen_state(swapchain_desc.OutputWindow, &expected_state.fullscreen_state); DestroyWindow(swapchain_desc.OutputWindow); + winetest_pop_context(); } IDXGIOutput_Release(output); + winetest_pop_context(); } IDXGIAdapter_Release(adapter); + winetest_pop_context(); } refcount = IDXGIFactory_Release(factory); ok(refcount == !is_d3d12, "Got unexpected refcount %lu.\n", refcount);
From: Matteo Bruni mbruni@codeweavers.com
--- dlls/dxgi/tests/dxgi.c | 180 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+)
diff --git a/dlls/dxgi/tests/dxgi.c b/dlls/dxgi/tests/dxgi.c index f223ab96c2d..5f55d7a75b0 100644 --- a/dlls/dxgi/tests/dxgi.c +++ b/dlls/dxgi/tests/dxgi.c @@ -3056,6 +3056,184 @@ static void test_default_fullscreen_target_output(IUnknown *device, BOOL is_d3d1 ok(refcount == !is_d3d12, "IDXGIFactory has %lu references left.\n", refcount); }
+static void test_resize_fullscreen_window(IUnknown *device, BOOL is_d3d12, BOOL change_style) +{ + struct swapchain_fullscreen_state initial_state, expected_state; + unsigned int desktop_width, desktop_height; + DXGI_SWAP_CHAIN_DESC swapchain_desc; + IDXGIAdapter *adapter = NULL; + DXGI_OUTPUT_DESC output_desc; + IDXGIOutput *output = NULL; + IDXGISwapChain *swapchain; + IDXGIFactory *factory; + BOOL fullscreen, ret; + DXGI_MODE_DESC mode; + ULONG refcount; + HRESULT hr; + RECT r; + + winetest_push_context(change_style ? "Style change" : "No style change"); + + get_factory(device, is_d3d12, &factory); + + swapchain_desc.BufferDesc.Width = 800; + swapchain_desc.BufferDesc.Height = 600; + swapchain_desc.BufferDesc.RefreshRate.Numerator = 60; + swapchain_desc.BufferDesc.RefreshRate.Denominator = 60; + swapchain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swapchain_desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; + swapchain_desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; + swapchain_desc.SampleDesc.Count = 1; + swapchain_desc.SampleDesc.Quality = 0; + swapchain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapchain_desc.BufferCount = is_d3d12 ? 2 : 1; + swapchain_desc.OutputWindow = CreateWindowA("static", "dxgi_test", 0, 0, 0, 400, 200, 0, 0, 0, 0); + swapchain_desc.Windowed = TRUE; + swapchain_desc.SwapEffect = is_d3d12 ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD; + swapchain_desc.Flags = 0; + + memset(&initial_state, 0, sizeof(initial_state)); + capture_fullscreen_state(&initial_state.fullscreen_state, swapchain_desc.OutputWindow); + hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IDXGISwapChain_GetContainingOutput(swapchain, &output); + ok(hr == S_OK || broken(hr == DXGI_ERROR_UNSUPPORTED), /* Win 7 testbot */ + "Unexpected hr %#lx.\n", hr); + if (FAILED(hr)) + { + skip("Could not get output.\n"); + goto done; + } + hr = IDXGIOutput_GetParent(output, &IID_IDXGIAdapter, (void **)&adapter); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + check_swapchain_fullscreen_state(swapchain, &initial_state); + flush_events(); + hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL); + ok(hr == S_OK || hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE + || broken(hr == DXGI_ERROR_UNSUPPORTED), /* Win 7 testbot */ + "Unexpected hr %#lx.\n", hr); + if (FAILED(hr)) + { + skip("Could not change fullscreen state.\n"); + goto done; + } + hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + refcount = IDXGISwapChain_Release(swapchain); + ok(!refcount, "IDXGISwapChain has %lu references left.\n", refcount); + + DestroyWindow(swapchain_desc.OutputWindow); + swapchain_desc.OutputWindow = CreateWindowA("static", "dxgi_test", 0, 0, 0, 400, 200, 0, 0, 0, 0); + flush_events(); + check_window_fullscreen_state(swapchain_desc.OutputWindow, &initial_state.fullscreen_state); + hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!fullscreen, "Unexpected fullscreen %#x.\n", fullscreen); + + hr = IDXGIOutput_GetDesc(output, &output_desc); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + if (change_style) + SetWindowLongPtrA(swapchain_desc.OutputWindow, GWL_STYLE, 0x10ca0000); + + expected_state = initial_state; + SetRect(&expected_state.fullscreen_state.client_rect, 0, 0, 640, 480); + r = expected_state.fullscreen_state.client_rect; + ret = AdjustWindowRectEx(&r, GetWindowLongW(swapchain_desc.OutputWindow, GWL_STYLE), + FALSE, GetWindowLongW(swapchain_desc.OutputWindow, GWL_EXSTYLE)); + ok(ret, "Unexpected ret %#x.\n", ret); + desktop_width = output_desc.DesktopCoordinates.right - output_desc.DesktopCoordinates.left; + desktop_height = output_desc.DesktopCoordinates.bottom - output_desc.DesktopCoordinates.top; + OffsetRect(&r, (desktop_width - 640) / 2, (desktop_height - 480) / 2); + expected_state.fullscreen_state.window_rect = r; + + /* Center the window on the screen. */ + ret = SetWindowPos(swapchain_desc.OutputWindow, HWND_NOTOPMOST, r.left, + r.top, r.right - r.left, r.bottom - r.top, SWP_FRAMECHANGED); + ok(ret, "Unexpected ret %#x.\n", ret); + hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!fullscreen, "Unexpected fullscreen %#x.\n", fullscreen); + expected_state.fullscreen = TRUE; + expected_state.target = output; + if (change_style) + { + expected_state.fullscreen_state.style = 0x4ca0000; + } + else + { + expected_state.fullscreen_state.style = 0x4000000; + expected_state.fullscreen_state.exstyle = 0; + } + check_swapchain_fullscreen_state(swapchain, &expected_state); + + /* Update DXGI swapchain. */ + memset(&mode, 0, sizeof(mode)); + mode.Width = 640; + mode.Height = 480; + hr = IDXGISwapChain_ResizeTarget(swapchain, &mode); + ok(!hr, "Unexpected hr %#lx.\n", hr); + hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!fullscreen, "Unexpected fullscreen %#x.\n", fullscreen); + SetRect(&expected_state.fullscreen_state.window_rect, 0, 0, desktop_width, desktop_height); + expected_state.fullscreen_state.client_rect = expected_state.fullscreen_state.window_rect; + if (change_style) + { + expected_state.fullscreen_state.style = 0x4000000; + expected_state.fullscreen_state.exstyle = 0; + } + check_swapchain_fullscreen_state(swapchain, &expected_state); + hr = IDXGISwapChain_GetDesc(swapchain, &swapchain_desc); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(swapchain_desc.BufferDesc.Width == 800, "Unexpected width %u.\n", swapchain_desc.BufferDesc.Width); + ok(swapchain_desc.BufferDesc.Height == 600, "Unexpected height %u.\n", swapchain_desc.BufferDesc.Height); + + /* Now actually leave fullscreen mode. */ + hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!fullscreen, "Unexpected fullscreen %#x.\n", fullscreen); + expected_state.fullscreen_state.window_rect = r; + SetRect(&expected_state.fullscreen_state.client_rect, 0, 0, 640, 480); + expected_state.fullscreen = FALSE; + expected_state.target = NULL; + if (change_style) + { + expected_state.fullscreen_state.style = 0x4ca0000; + expected_state.fullscreen_state.exstyle = 0x100; + } + check_swapchain_fullscreen_state(swapchain, &expected_state); + +done: + if (output) + IDXGIOutput_Release(output); + if (adapter) + IDXGIAdapter_Release(adapter); + refcount = IDXGISwapChain_Release(swapchain); + ok(!refcount, "IDXGISwapChain has %lu references left.\n", refcount); + DestroyWindow(swapchain_desc.OutputWindow); + + refcount = IDXGIFactory_Release(factory); + ok(refcount == !is_d3d12, "Unexpected refcount %lu.\n", refcount); + + winetest_pop_context(); +} + +static void test_resize_fullscreen(IUnknown *device, BOOL is_d3d12) +{ + test_resize_fullscreen_window(device, is_d3d12, FALSE); + /* Skip the test on Wine, it's very tricky to make it pass. */ + if (!winetest_platform_is_wine) + test_resize_fullscreen_window(device, is_d3d12, TRUE); +} + static void test_windowed_resize_target(IDXGISwapChain *swapchain, HWND window, struct swapchain_fullscreen_state *state) { @@ -8336,6 +8514,7 @@ START_TEST(dxgi) test_swapchain_window_styles(); run_on_d3d10(test_set_fullscreen); run_on_d3d10(test_resize_target); + run_on_d3d10(test_resize_fullscreen); run_on_d3d10(test_swapchain_resize); run_on_d3d10(test_swapchain_present); run_on_d3d10(test_swapchain_backbuffer_index); @@ -8368,6 +8547,7 @@ START_TEST(dxgi)
run_on_d3d12(test_set_fullscreen); run_on_d3d12(test_resize_target); + run_on_d3d10(test_resize_fullscreen); run_on_d3d12(test_swapchain_resize); run_on_d3d12(test_swapchain_present); run_on_d3d12(test_swapchain_backbuffer_index);
Can we please use symbolic constants for window style?
Why are we bothering with WS_VISIBLE in the tests? It can't prove anything since we strip it out anyway when checking. (I'm not sure why we do that, but.) This also makes the tests confusing to read...
The style management bothers me, because it doesn't seem very declarative. We believe the style the application sets here is the one they want to restore, except we don't believe it about WS_POPUP | WS_SYSMENU? Why not?
Can we please use symbolic constants for window style?
I forgot to do that :/
Why are we bothering with WS_VISIBLE in the tests? It can't prove anything since we strip it out anyway when checking. (I'm not sure why we do that, but.) This also makes the tests confusing to read...
I don't think we care about WS_VISIBLE, I just picked the style used by the game without paying particular attention to it.
The style management bothers me, because it doesn't seem very declarative. We believe the style the application sets here is the one they want to restore, except we don't believe it about WS_POPUP | WS_SYSMENU? Why not?
This is about the implementation patch, right? The idea behind masking those style flags is to make sure we don't introduce unintended window style changes by picking up the flags we override in wined3d_swapchain_state_setup_fullscreen() -> fullscreen_style() (e.g. if the application didn't touch style). I don't know that's any better than just saving the current state as is. I'll check if it matters for the game, but I suspect the answer is no. The game didn't seem to be picky about window states, but it does expect to keep the window position it set while still in fullscreen.
I guess a possible idea would be to flag window style (or even size) changes in our wndproc and only update the relevant saved window state. Not sure if that would be a change in the right direction though.
The style management bothers me, because it doesn't seem very declarative. We believe the style the application sets here is the one they want to restore, except we don't believe it about WS_POPUP | WS_SYSMENU? Why not?
This is about the implementation patch, right? The idea behind masking those style flags is to make sure we don't introduce unintended window style changes by picking up the flags we override in wined3d_swapchain_state_setup_fullscreen() -> fullscreen_style() (e.g. if the application didn't touch style). I don't know that's any better than just saving the current state as is. I'll check if it matters for the game, but I suspect the answer is no. The game didn't seem to be picky about window states, but it does expect to keep the window position it set while still in fullscreen.
I would be inclined to just leave it alone until we find something that cares. I'd rather keep the code as simple as possible in cases like this.
Nicholas Ansell (@roadrunnerto) commented about dlls/dxgi/tests/dxgi.c:
run_on_d3d12(test_set_fullscreen); run_on_d3d12(test_resize_target);
- run_on_d3d10(test_resize_fullscreen);
```suggestion:-0+0 run_on_d3d12(test_resize_fullscreen); ```