From: Zhiyi Zhang <zzhang@codeweavers.com> Titan Quest Anniversary Edition (SteamID: 475150) tries to create a fullscreen swapchain with an invalid scanline order and scaling and gets a windowed swapchain instead on Windows. Wine creates a fullscreen swapchain in this case and thus causes some unexpected window management bugs such as the taskbar showing up on top of the game window. On Windows 11, DXGI_STATUS_OCCLUDED is returned when creating a fullscreen swapchain in such cases and S_OK is returned on Windows 10 <= 22H2. Choose S_OK here because I worry that some applications might not handle DXGI_STATUS_OCCLUDED gracefully. --- dlls/dxgi/dxgi_private.h | 2 +- dlls/dxgi/factory.c | 11 ++++++++++- dlls/dxgi/swapchain.c | 32 +++++++++++++++++++++++++------- dlls/dxgi/tests/dxgi.c | 12 ------------ 4 files changed, 36 insertions(+), 21 deletions(-) diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h index c74a80f5c22..8eba24de24d 100644 --- a/dlls/dxgi/dxgi_private.h +++ b/dlls/dxgi/dxgi_private.h @@ -193,7 +193,7 @@ HRESULT d3d12_swapchain_create(IWineDXGIFactory *factory, ID3D12CommandQueue *qu const DXGI_SWAP_CHAIN_DESC1 *swapchain_desc, const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, IDXGISwapChain1 **swapchain); -BOOL dxgi_validate_swapchain_desc(const DXGI_SWAP_CHAIN_DESC1 *desc); +HRESULT dxgi_validate_swapchain_desc(const DXGI_SWAP_CHAIN_DESC1 *desc, const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc); /* IDXGISurface/IDXGIResource */ struct dxgi_resource diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c index 43594d14d4e..e3fa6d41160 100644 --- a/dlls/dxgi/factory.c +++ b/dlls/dxgi/factory.c @@ -299,8 +299,17 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa fullscreen_desc = &windowed_fullscreen_desc; } - if (!dxgi_validate_swapchain_desc(desc)) + if (FAILED(hr = dxgi_validate_swapchain_desc(desc, fullscreen_desc))) + { return DXGI_ERROR_INVALID_CALL; + } + else if (hr == DXGI_STATUS_OCCLUDED) + { + /* Create a windowed swapchain instead */ + windowed_fullscreen_desc = *fullscreen_desc; + windowed_fullscreen_desc.Windowed = TRUE; + fullscreen_desc = &windowed_fullscreen_desc; + } if (output) FIXME("Ignoring output %p.\n", output); diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c index 206fcd3683b..c33a2daf359 100644 --- a/dlls/dxgi/swapchain.c +++ b/dlls/dxgi/swapchain.c @@ -60,7 +60,8 @@ static BOOL dxgi_validate_flip_swap_effect_format(DXGI_FORMAT format) } } -BOOL dxgi_validate_swapchain_desc(const DXGI_SWAP_CHAIN_DESC1 *desc) +HRESULT dxgi_validate_swapchain_desc(const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc) { unsigned int min_buffer_count; @@ -76,28 +77,45 @@ BOOL dxgi_validate_swapchain_desc(const DXGI_SWAP_CHAIN_DESC1 *desc) min_buffer_count = 2; if (desc->Format && !dxgi_validate_flip_swap_effect_format(desc->Format)) - return FALSE; + return DXGI_ERROR_INVALID_CALL; if (desc->SampleDesc.Count != 1 || desc->SampleDesc.Quality) { WARN("Invalid sample desc %u, %u for swap effect %#x.\n", desc->SampleDesc.Count, desc->SampleDesc.Quality, desc->SwapEffect); - return FALSE; + return DXGI_ERROR_INVALID_CALL; } break; default: WARN("Invalid swap effect %u used.\n", desc->SwapEffect); - return FALSE; + return DXGI_ERROR_INVALID_CALL; } if (desc->BufferCount < min_buffer_count || desc->BufferCount > DXGI_MAX_SWAP_CHAIN_BUFFERS) { WARN("BufferCount is %u.\n", desc->BufferCount); - return FALSE; + return DXGI_ERROR_INVALID_CALL; } - return TRUE; + if (fullscreen_desc && !fullscreen_desc->Windowed) + { + if (!(fullscreen_desc->ScanlineOrdering >= DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED + && fullscreen_desc->ScanlineOrdering <= DXGI_MODE_SCANLINE_ORDER_LOWER_FIELD_FIRST)) + { + WARN("Invalid scaline order %#x.\n", fullscreen_desc->ScanlineOrdering); + return DXGI_STATUS_OCCLUDED; + } + + if (!(fullscreen_desc->Scaling >= DXGI_MODE_SCALING_UNSPECIFIED + && fullscreen_desc->Scaling <= DXGI_MODE_SCALING_STRETCHED)) + { + WARN("Invalid scaling %#x.\n", fullscreen_desc->Scaling); + return DXGI_STATUS_OCCLUDED; + } + } + + return S_OK; } HRESULT dxgi_get_output_from_window(IWineDXGIFactory *factory, HWND window, IDXGIOutput **dxgi_output) @@ -2586,7 +2604,7 @@ static HRESULT d3d12_swapchain_resize_buffers(struct d3d12_swapchain *swapchain, if (format) new_desc.Format = format; - if (!dxgi_validate_swapchain_desc(&new_desc)) + if (FAILED(dxgi_validate_swapchain_desc(&new_desc, NULL))) return DXGI_ERROR_INVALID_CALL; if (desc->Width == new_desc.Width && desc->Height == new_desc.Height diff --git a/dlls/dxgi/tests/dxgi.c b/dlls/dxgi/tests/dxgi.c index 96dcf7a38cd..7fa3d7ab18a 100644 --- a/dlls/dxgi/tests/dxgi.c +++ b/dlls/dxgi/tests/dxgi.c @@ -2254,18 +2254,15 @@ static void test_create_swapchain(IUnknown *device, BOOL is_d3d12) "Got unexpected scanline ordering %d.\n", fullscreen_desc.ScanlineOrdering); ok(fullscreen_desc.Scaling == creation_desc.BufferDesc.Scaling, "Got unexpected scanline ordering %d.\n", fullscreen_desc.ScanlineOrdering); - todo_wine ok(fullscreen_desc.Windowed != creation_desc.Windowed, "Got unexpected windowed %#x.\n", fullscreen_desc.Windowed); IDXGISwapChain1_Release(swapchain1); } hr = IDXGISwapChain_GetDesc(swapchain, &result_desc); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - todo_wine ok(result_desc.Windowed, "Got unexpected fullscreen state.\n"); hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - todo_wine ok(!fullscreen, "Got unexpected fullscreen state.\n"); hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL); todo_wine @@ -2294,18 +2291,15 @@ static void test_create_swapchain(IUnknown *device, BOOL is_d3d12) "Got unexpected scanline ordering %d.\n", fullscreen_desc.ScanlineOrdering); ok(fullscreen_desc.Scaling == creation_desc.BufferDesc.Scaling, "Got unexpected scanline ordering %d.\n", fullscreen_desc.ScanlineOrdering); - todo_wine ok(fullscreen_desc.Windowed != creation_desc.Windowed, "Got unexpected windowed %#x.\n", fullscreen_desc.Windowed); IDXGISwapChain1_Release(swapchain1); } hr = IDXGISwapChain_GetDesc(swapchain, &result_desc); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - todo_wine ok(result_desc.Windowed, "Got unexpected fullscreen state.\n"); hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - todo_wine ok(!fullscreen, "Got unexpected fullscreen state.\n"); hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL); todo_wine @@ -2336,18 +2330,15 @@ static void test_create_swapchain(IUnknown *device, BOOL is_d3d12) "Got unexpected scanline ordering %d.\n", fullscreen_desc.ScanlineOrdering); ok(fullscreen_desc.Scaling == creation_desc.BufferDesc.Scaling, "Got unexpected scanline ordering %d.\n", fullscreen_desc.ScanlineOrdering); - todo_wine ok(fullscreen_desc.Windowed != creation_desc.Windowed, "Got unexpected windowed %#x.\n", fullscreen_desc.Windowed); IDXGISwapChain1_Release(swapchain1); } hr = IDXGISwapChain_GetDesc(swapchain, &result_desc); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - todo_wine ok(result_desc.Windowed, "Got unexpected fullscreen state.\n"); hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - todo_wine ok(!fullscreen, "Got unexpected fullscreen state.\n"); hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL); todo_wine @@ -2376,18 +2367,15 @@ static void test_create_swapchain(IUnknown *device, BOOL is_d3d12) "Got unexpected scanline ordering %d.\n", fullscreen_desc.ScanlineOrdering); ok(fullscreen_desc.Scaling == creation_desc.BufferDesc.Scaling, "Got unexpected scanline ordering %d.\n", fullscreen_desc.ScanlineOrdering); - todo_wine ok(fullscreen_desc.Windowed != creation_desc.Windowed, "Got unexpected windowed %#x.\n", fullscreen_desc.Windowed); IDXGISwapChain1_Release(swapchain1); } hr = IDXGISwapChain_GetDesc(swapchain, &result_desc); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - todo_wine ok(result_desc.Windowed, "Got unexpected fullscreen state.\n"); hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - todo_wine ok(!fullscreen, "Got unexpected fullscreen state.\n"); hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL); todo_wine -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11231