[PATCH v4 0/3] MR9585: win32u: Introduce a D3DKMT escape code to set fullscreen present rect. (1/2)
These calls are Wine-specific and expected to fail on Windows, as the escape codes and parameter combinations are likely invalid there. There doesn't seem to be any mechanism to do this otherwise. When a window presentation rect has been set, the window is meant to be presented to that rect on screen exactly, mapped to physical coords. Then this also has the following side effects: * In exclusive fullscreen mode, GDI drawn child windows are clipped out of the rendered screen, regardless of the normal clipping rules. * The window position and size changes should be ignored wrt its visible area, and in particular the window should not be unmapped even if its Win32 rect is moved to an invisible location. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58844 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58443 --- This only introduces the mechanism and calls it from Wine D3D side. The side effects will be implemented later in win32u and the user drivers. This is necessary both for Command & Conquer 2, which has dialog child windows covering the entire game window while using WS_CLIPCHILDREN, and for Fallout 3 which does a bogus call sequence after window restoration which moves the window outside of screen area and causes its window and client rects to be empty: ``` SetRect(&rect, 0, 0, width, height); AdjustWindowRectEx(&rect, style, FALSE, 0); SetWindowPos(hwnd, 0, rect.left, rect.bottom, rect.right - rect.left, rect.top - rect.bottom, 0); ``` We need either this kind of mechanism (which could be later changed to some D3DKMTPresent call, but it would require a lot more work and I'd like to fix those regressions before the release), or to hook the window proc like modern Windows seems to be doing as tested in !9549 (and more tests suggest that invalid window moves are also now inhibited and corrected on modern Windows). -- v4: wined3d: Use the present rather than client rect when fullscreen. wined3d: Set the window present rect when entering fullscreen mode. win32u: Introduce a D3DKMT escape code to set fullscreen present rect. https://gitlab.winehq.org/wine/wine/-/merge_requests/9585
From: Rémi Bernon <rbernon(a)codeweavers.com> These calls are Wine-specific and expected to fail on Windows, as the escape codes and parameter combinations are likely invalid there. There doesn't seem to be any mechanism to do this otherwise. When a window presentation rect has been set, the window is meant to be presented to that rect on screen exactly, mapped to physical coords. Then this also has the following side effects: * In exclusive fullscreen mode, GDI drawn child windows are clipped out of the rendered screen, regardless of the normal clipping rules. * The window position and size changes should be ignored wrt its visible area, and in particular the window should not be unmapped even if its Win32 rect is moved to an invisible location. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58844 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58443 --- dlls/win32u/d3dkmt.c | 17 +++++++++++++++++ dlls/win32u/ntuser_private.h | 1 + include/ddk/d3dkmthk.h | 1 + 3 files changed, 19 insertions(+) diff --git a/dlls/win32u/d3dkmt.c b/dlls/win32u/d3dkmt.c index 8da6886154a..baf99f69291 100644 --- a/dlls/win32u/d3dkmt.c +++ b/dlls/win32u/d3dkmt.c @@ -547,6 +547,23 @@ NTSTATUS WINAPI NtGdiDdDDIEscape( const D3DKMT_ESCAPE *desc ) return d3dkmt_object_update( &resource->obj, desc->pPrivateDriverData, desc->PrivateDriverDataSize ); } + case D3DKMT_ESCAPE_SET_PRESENT_RECT_WINE: + { + HWND hwnd = UlongToHandle( desc->hContext ); + RECT *rect = desc->pPrivateDriverData; + UINT dpi = get_dpi_for_window( hwnd ); + WND *win; + + if (desc->PrivateDriverDataSize != sizeof(*rect)) return STATUS_INVALID_PARAMETER; + + TRACE( "hwnd %p, rect %s\n", hwnd, wine_dbgstr_rect( rect ) ); + if (!(win = get_win_ptr( hwnd ))) return STATUS_INVALID_PARAMETER; + win->present_rect = map_dpi_rect( *rect, get_thread_dpi(), dpi ); + release_win_ptr( win ); + + return STATUS_SUCCESS; + } + default: FIXME( "(%p): stub\n", desc ); return STATUS_NO_MEMORY; diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h index 14dcff3ad38..697e4c74203 100644 --- a/dlls/win32u/ntuser_private.h +++ b/dlls/win32u/ntuser_private.h @@ -51,6 +51,7 @@ typedef struct tagWND HINSTANCE hInstance; /* Window hInstance (from CreateWindow) */ struct window_rects rects; /* window rects in window DPI, relative to the parent client area */ RECT normal_rect; /* Normal window rect saved when maximized/minimized */ + RECT present_rect; /* present rect for exclusive fullscreen mode */ POINT min_pos; /* Position for minimized window */ POINT max_pos; /* Position for maximized window */ WCHAR *text; /* Window text */ diff --git a/include/ddk/d3dkmthk.h b/include/ddk/d3dkmthk.h index 9f7e6d55a3d..a4da022e9aa 100644 --- a/include/ddk/d3dkmthk.h +++ b/include/ddk/d3dkmthk.h @@ -770,6 +770,7 @@ typedef enum _D3DKMT_ESCAPETYPE D3DKMT_ESCAPE_DIAGNOSTICS, /* Wine-specific escape codes */ D3DKMT_ESCAPE_UPDATE_RESOURCE_WINE = 0x80000000, + D3DKMT_ESCAPE_SET_PRESENT_RECT_WINE = 0x80000001, } D3DKMT_ESCAPETYPE; typedef struct _D3DKMT_ESCAPE -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9585
From: Rémi Bernon <rbernon(a)codeweavers.com> And unset it when exiting fullscreen. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58844 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58443 --- dlls/wined3d/swapchain.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/dlls/wined3d/swapchain.c b/dlls/wined3d/swapchain.c index a1000ec2393..f19f51d0e51 100644 --- a/dlls/wined3d/swapchain.c +++ b/dlls/wined3d/swapchain.c @@ -27,6 +27,19 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d); WINE_DECLARE_DEBUG_CHANNEL(d3d_perf); +static BOOL set_window_present_rect(HWND hwnd, UINT x, UINT y, UINT width, UINT height) +{ + RECT rect = {x, y, x + width, y + height}; + D3DKMT_ESCAPE escape = {0}; + + escape.Type = D3DKMT_ESCAPE_SET_PRESENT_RECT_WINE; + escape.hContext = HandleToULong(hwnd); + escape.pPrivateDriverData = ▭ + escape.PrivateDriverDataSize = sizeof(rect); + + return !D3DKMTEscape(&escape); +} + void wined3d_swapchain_cleanup(struct wined3d_swapchain *swapchain) { HRESULT hr; @@ -2258,6 +2271,8 @@ HRESULT wined3d_swapchain_state_setup_fullscreen(struct wined3d_swapchain_state return WINED3DERR_NOTAVAILABLE; } + set_window_present_rect(window, x, y, width, height); + if (!(s = malloc(sizeof(*s)))) return E_OUTOFMEMORY; s->window = window; @@ -2301,6 +2316,8 @@ void wined3d_swapchain_state_restore_from_fullscreen(struct wined3d_swapchain_st struct wined3d_window_state *s; LONG style, exstyle; + set_window_present_rect(window, 0, 0, 0, 0); + if (!state->style && !state->exstyle) return; @@ -2444,6 +2461,9 @@ HRESULT CDECL wined3d_swapchain_state_set_fullscreen(struct wined3d_swapchain_st HWND window = state->device_window; BOOL filter; + set_window_present_rect(state->device_window, output_desc.desktop_rect.left, + output_desc.desktop_rect.top, width, height); + /* Fullscreen -> fullscreen mode change */ filter = wined3d_filter_messages(window, TRUE); MoveWindow(window, output_desc.desktop_rect.left, output_desc.desktop_rect.top, width, -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9585
From: Rémi Bernon <rbernon(a)codeweavers.com> The client rect might be empty or invalid. Fallout 3 in particular does a bogus call sequence after restoring minimized window which causes the window and client rects to be empty: SetRect(&rect, 0, 0, width, height); AdjustWindowRectEx(&rect, style, FALSE, 0); SetWindowPos(hwnd, 0, rect.left, rect.bottom, rect.right - rect.left, rect.top - rect.bottom, 0); We either need to hook the window messages and inhibit such window size changes, or we need to avoid relying on client and window rect when in fullscreen mode. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58844 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58443 --- dlls/wined3d/context_gl.c | 17 +++++++++++++---- dlls/wined3d/swapchain.c | 32 ++++++++++++++++++++++---------- dlls/wined3d/texture.c | 13 ++++++++++--- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/dlls/wined3d/context_gl.c b/dlls/wined3d/context_gl.c index ac1b9370fc1..2b19664230e 100644 --- a/dlls/wined3d/context_gl.c +++ b/dlls/wined3d/context_gl.c @@ -2391,11 +2391,20 @@ static void wined3d_context_gl_get_rt_size(const struct wined3d_context_gl *cont if (rt->swapchain) { - RECT window_size; + const struct wined3d_swapchain_desc *desc = &rt->swapchain->state.desc; - GetClientRect(context_gl->window, &window_size); - size->cx = window_size.right - window_size.left; - size->cy = window_size.bottom - window_size.top; + if (!desc->windowed) + { + size->cx = desc->backbuffer_width; + size->cy = desc->backbuffer_height; + } + else + { + RECT client_rect; + GetClientRect(context_gl->window, &client_rect); + size->cx = client_rect.right - client_rect.left; + size->cy = client_rect.bottom - client_rect.top; + } return; } diff --git a/dlls/wined3d/swapchain.c b/dlls/wined3d/swapchain.c index f19f51d0e51..664a8001e50 100644 --- a/dlls/wined3d/swapchain.c +++ b/dlls/wined3d/swapchain.c @@ -206,6 +206,7 @@ HRESULT CDECL wined3d_swapchain_present(struct wined3d_swapchain *swapchain, const RECT *src_rect, const RECT *dst_rect, HWND dst_window_override, unsigned int swap_interval, uint32_t flags) { + const struct wined3d_swapchain_desc *desc = &swapchain->state.desc; RECT s, d; TRACE("swapchain %p, src_rect %s, dst_rect %s, dst_window_override %p, swap_interval %u, flags %#x.\n", @@ -226,14 +227,16 @@ HRESULT CDECL wined3d_swapchain_present(struct wined3d_swapchain *swapchain, if (!src_rect) { - SetRect(&s, 0, 0, swapchain->state.desc.backbuffer_width, - swapchain->state.desc.backbuffer_height); + SetRect(&s, 0, 0, desc->backbuffer_width, desc->backbuffer_height); src_rect = &s; } if (!dst_rect) { - GetClientRect(swapchain->win_handle, &d); + if (!desc->windowed) + SetRect(&d, 0, 0, desc->backbuffer_width, desc->backbuffer_height); + else + GetClientRect(swapchain->win_handle, &d); dst_rect = &d; } @@ -575,19 +578,28 @@ static void wined3d_swapchain_gl_rotate(struct wined3d_swapchain *swapchain, str static bool swapchain_present_is_partial_copy(struct wined3d_swapchain *swapchain, const RECT *dst_rect) { enum wined3d_swap_effect swap_effect = swapchain->state.desc.swap_effect; - RECT client_rect; - unsigned int t; + const struct wined3d_swapchain_desc *desc = &swapchain->state.desc; + unsigned int width, height; if (swap_effect != WINED3D_SWAP_EFFECT_COPY && swap_effect != WINED3D_SWAP_EFFECT_COPY_VSYNC) return false; - GetClientRect(swapchain->win_handle, &client_rect); + if (!desc->windowed) + { + width = desc->backbuffer_width; + height = desc->backbuffer_height; + } + else + { + RECT client_rect; + GetClientRect(swapchain->win_handle, &client_rect); + width = client_rect.right - client_rect.left; + height = client_rect.bottom - client_rect.top; + } - t = client_rect.right - client_rect.left; - if ((dst_rect->left && dst_rect->right) || abs(dst_rect->right - dst_rect->left) != t) + if ((dst_rect->left && dst_rect->right) || abs(dst_rect->right - dst_rect->left) != width) return true; - t = client_rect.bottom - client_rect.top; - if ((dst_rect->top && dst_rect->bottom) || abs(dst_rect->bottom - dst_rect->top) != t) + if ((dst_rect->top && dst_rect->bottom) || abs(dst_rect->bottom - dst_rect->top) != height) return true; return false; diff --git a/dlls/wined3d/texture.c b/dlls/wined3d/texture.c index dd61a2f0d3c..784406cf245 100644 --- a/dlls/wined3d/texture.c +++ b/dlls/wined3d/texture.c @@ -68,12 +68,13 @@ static BOOL wined3d_texture_use_pbo(const struct wined3d_texture *texture, const * origin, while D3D has a top-left origin. */ void wined3d_texture_translate_drawable_coords(const struct wined3d_texture *texture, HWND window, RECT *rect) { + const struct wined3d_swapchain_desc *desc; unsigned int drawable_height; POINT offset = {0, 0}; - RECT windowsize; if (!texture->swapchain) return; + desc = &texture->swapchain->state.desc; if (texture == texture->swapchain->front_buffer) { @@ -81,8 +82,14 @@ void wined3d_texture_translate_drawable_coords(const struct wined3d_texture *tex OffsetRect(rect, offset.x, offset.y); } - GetClientRect(window, &windowsize); - drawable_height = windowsize.bottom - windowsize.top; + if (!desc->windowed) + drawable_height = desc->backbuffer_height; + else + { + RECT client_rect; + GetClientRect(window, &client_rect); + drawable_height = client_rect.bottom - client_rect.top; + } rect->top = drawable_height - rect->top; rect->bottom = drawable_height - rect->bottom; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9585
"present_rect" is a bit of an unfortunate name, since that could refer to one or more of the rects passed to wined3d_swapchain_present(). "drawable_rect" would be more in line with existing terminology.
Well I don't really know about the wined3d terminology, and I'm fine to change it to any name on that side of things but it does seem more confusing to me at least for the win32u side: `window drawable rect` could be read as "the window rect that can be drawn to", and this is not what this is. The rect is not a rectangle inside the window, and not really something that can be drawn to from the window perspective, but rather the area of the screen the window swapchain images should be presented to. Then I would think this to be better to have consistent names across modules but I'm fine renaming `set_window_present_rect` to `set_window_drawable_rect` if that is better for you. (Also fwiw, I think `present` makes it close to D3DKMTPresent which this is fairly related to)
The upshot of this is that the whole of get_swapchain_present_rect() is pretty much unnecessary; you can just use the swapchain size directly.
Thanks, I changed the code to use swapchain backbuffer width/height, I understand this is what you meant? -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9585#note_124757
This merge request was approved by Elizabeth Figura. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9585
Then I think it is better to have consistent names across modules but I'm fine renaming `set_window_present_rect` to `set_window_drawable_rect` if that is better for you.
(Also fwiw, I think `present` makes it close to D3DKMTPresent which this is fairly related to)
It's probably fine for now; it's temporary anyway.
The upshot of this is that the whole of get_swapchain_present_rect() is pretty much unnecessary; you can just use the swapchain size directly.
Thanks, I changed the code to use swapchain backbuffer width/height, I understand this is what you meant?
Yes, thank you. Though the patch subject is now not quite correct. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9585#note_124830
participants (2)
-
Elizabeth Figura (@zfigura) -
Rémi Bernon