[PATCH v2 0/4] MR10992: ddraw: Allow attaching flip chain surfaces surfaces (for v1 surfaces).
-- v2: ddraw: Force render target bind flags on v1 swapchain candidate surfaces. ddraw/tests: Add tests for attaching and detaching backbuffers on ddraw1. ddraw: Allow attaching flip chain surfaces. ddraw/tests: Check surface caps in BackBufferAttachmentFlipTest(). https://gitlab.winehq.org/wine/wine/-/merge_requests/10992
From: Paul Gofman <pgofman@codeweavers.com> And take backbuffer size from primary surface so it doesn't hit broken path for unrelated reasons. --- dlls/ddraw/tests/dsurface.c | 198 ++++++++++++++++++++---------------- 1 file changed, 109 insertions(+), 89 deletions(-) diff --git a/dlls/ddraw/tests/dsurface.c b/dlls/ddraw/tests/dsurface.c index e65160034f6..7f5c51fe258 100644 --- a/dlls/ddraw/tests/dsurface.c +++ b/dlls/ddraw/tests/dsurface.c @@ -72,6 +72,21 @@ static ULONG getref(IUnknown *iface) return IUnknown_Release(iface); } +#define check_surface_caps(a, b, c, d) check_surface_caps_(__LINE__, (a), (b), (c), (d)) +static void check_surface_caps_(unsigned int line, IDirectDrawSurface *surface, DWORD expected_caps, + DWORD exclude_caps, DWORD expected_buffer_count) +{ + DDSURFACEDESC surface_desc = {.dwSize = sizeof(surface_desc) }; + HRESULT hr; + + hr = IDirectDrawSurface_GetSurfaceDesc(surface, &surface_desc); + ok_(__FILE__, line)(hr == DD_OK, "got %#lx.\n", hr); + ok_(__FILE__, line)((surface_desc.ddsCaps.dwCaps & ~exclude_caps) == expected_caps, "got %#lx, expected %#lx.\n", + surface_desc.ddsCaps.dwCaps & ~exclude_caps, expected_caps); + ok_(__FILE__, line)(surface_desc.dwBackBufferCount == expected_buffer_count, "got backbuffer count %ld, expected %ld.\n", + surface_desc.dwBackBufferCount, expected_buffer_count); +} + static void GetDDInterface_1(void) { IDirectDrawSurface2 *dsurface2; @@ -1671,6 +1686,7 @@ static void BackBufferCreateSurfaceTest(void) static void BackBufferAttachmentFlipTest(void) { + const DWORD placement = DDSCAPS_LOCALVIDMEM | DDSCAPS_VIDEOMEMORY | DDSCAPS_SYSTEMMEMORY; HRESULT hr; IDirectDrawSurface *surface1, *surface2, *surface3, *surface4; DDSURFACEDESC ddsd; @@ -1680,106 +1696,110 @@ static void BackBufferAttachmentFlipTest(void) hr = IDirectDraw_SetCooperativeLevel(lpDD, window, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN); ok(hr == DD_OK, "Got hr %#lx.\n", hr); + /* Try a single primary and a two back buffers */ + memset(&ddsd, 0, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + hr = IDirectDraw_CreateSurface(lpDD, &ddsd, &surface1, NULL); + check_surface_caps(surface1, DDSCAPS_PRIMARYSURFACE | DDSCAPS_VISIBLE, placement, 0); + /* Perform attachment tests on a back-buffer */ memset(&ddsd, 0, sizeof(ddsd)); ddsd.dwSize = sizeof(ddsd); + hr = IDirectDrawSurface_GetSurfaceDesc(surface1, &ddsd); + ok(hr==DD_OK, "Got hr %#lx.\n", hr); ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER; - ddsd.dwWidth = GetSystemMetrics(SM_CXSCREEN); - ddsd.dwHeight = GetSystemMetrics(SM_CYSCREEN); hr = IDirectDraw_CreateSurface(lpDD, &ddsd, &surface2, NULL); ok(SUCCEEDED(hr), "Got hr %#lx.\n", hr); - if (surface2 != NULL) + ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; + ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER; + hr = IDirectDraw_CreateSurface(lpDD, &ddsd, &surface3, NULL); + ok(hr==DD_OK, "Got hr %#lx.\n", hr); + + /* This one has a different size */ + memset(&ddsd, 0, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; + ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER; + ddsd.dwWidth = 128; + ddsd.dwHeight = 128; + hr = IDirectDraw_CreateSurface(lpDD, &ddsd, &surface4, NULL); + ok(hr==DD_OK, "Got hr %#lx.\n", hr); + + hr = IDirectDrawSurface_AddAttachedSurface(surface1, surface2); + todo_wine ok(hr == DD_OK || broken(hr == DDERR_CANNOTATTACHSURFACE), + "Attaching a back buffer to a front buffer returned %#lx\n", hr); + if(SUCCEEDED(hr)) { - /* Try a single primary and a two back buffers */ - memset(&ddsd, 0, sizeof(ddsd)); - ddsd.dwSize = sizeof(ddsd); - ddsd.dwFlags = DDSD_CAPS; - ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; - hr = IDirectDraw_CreateSurface(lpDD, &ddsd, &surface1, NULL); - ok(hr==DD_OK, "Got hr %#lx.\n", hr); - - memset(&ddsd, 0, sizeof(ddsd)); - ddsd.dwSize = sizeof(ddsd); - ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; - ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER; - ddsd.dwWidth = GetSystemMetrics(SM_CXSCREEN); - ddsd.dwHeight = GetSystemMetrics(SM_CYSCREEN); - hr = IDirectDraw_CreateSurface(lpDD, &ddsd, &surface3, NULL); - ok(hr==DD_OK, "Got hr %#lx.\n", hr); - - /* This one has a different size */ - memset(&ddsd, 0, sizeof(ddsd)); - ddsd.dwSize = sizeof(ddsd); - ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; - ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER; - ddsd.dwWidth = 128; - ddsd.dwHeight = 128; - hr = IDirectDraw_CreateSurface(lpDD, &ddsd, &surface4, NULL); - ok(hr==DD_OK, "Got hr %#lx.\n", hr); - - hr = IDirectDrawSurface_AddAttachedSurface(surface1, surface2); - todo_wine ok(hr == DD_OK || broken(hr == DDERR_CANNOTATTACHSURFACE), - "Attaching a back buffer to a front buffer returned %#lx\n", hr); - if(SUCCEEDED(hr)) - { - /* Try flipping the surfaces */ - hr = IDirectDrawSurface_Flip(surface1, NULL, DDFLIP_WAIT); - todo_wine ok(hr == DD_OK, "Got hr %#lx.\n", hr); - hr = IDirectDrawSurface_Flip(surface2, NULL, DDFLIP_WAIT); - todo_wine ok(hr == DDERR_NOTFLIPPABLE, "Got hr %#lx.\n", hr); - - /* Try the reverse without detaching first */ - hr = IDirectDrawSurface_AddAttachedSurface(surface2, surface1); - ok(hr == DDERR_SURFACEALREADYATTACHED, "Got hr %#lx.\n", hr); - hr = IDirectDrawSurface_DeleteAttachedSurface(surface1, 0, surface2); - ok(hr == DD_OK, "Got hr %#lx.\n", hr); - } + /* Try flipping the surfaces */ + check_surface_caps(surface1, DDSCAPS_PRIMARYSURFACE | DDSCAPS_VISIBLE | DDSCAPS_FRONTBUFFER | DDSCAPS_FLIP, placement, 0); + check_surface_caps(surface2, DDSCAPS_BACKBUFFER | DDSCAPS_FLIP, placement, 0); + hr = IDirectDrawSurface_Flip(surface1, NULL, DDFLIP_WAIT); + ok(hr == DD_OK, "Got hr %#lx.\n", hr); + hr = IDirectDrawSurface_Flip(surface2, NULL, DDFLIP_WAIT); + ok(hr == DDERR_NOTFLIPPABLE, "Got hr %#lx.\n", hr); + + /* Try the reverse without detaching first */ hr = IDirectDrawSurface_AddAttachedSurface(surface2, surface1); - todo_wine ok(hr == DD_OK || broken(hr == DDERR_CANNOTATTACHSURFACE), - "Attaching a front buffer to a back buffer returned %#lx\n", hr); - if(SUCCEEDED(hr)) - { - /* Try flipping the surfaces */ - hr = IDirectDrawSurface_Flip(surface1, NULL, DDFLIP_WAIT); - todo_wine ok(hr == DD_OK, "Got hr %#lx.\n", hr); - hr = IDirectDrawSurface_Flip(surface2, NULL, DDFLIP_WAIT); - todo_wine ok(hr == DDERR_NOTFLIPPABLE, "Got hr %#lx.\n", hr); - - /* Try to detach reversed */ - hr = IDirectDrawSurface_DeleteAttachedSurface(surface1, 0, surface2); - ok(hr == DDERR_CANNOTDETACHSURFACE, "Got hr %#lx.\n", hr); - /* Now the proper detach */ - hr = IDirectDrawSurface_DeleteAttachedSurface(surface2, 0, surface1); - ok(hr == DD_OK, "Got hr %#lx.\n", hr); - } - hr = IDirectDrawSurface_AddAttachedSurface(surface2, surface3); - todo_wine ok(hr == DD_OK || broken(hr == DDERR_CANNOTATTACHSURFACE), - "Attaching a back buffer to another back buffer returned %#lx\n", hr); - if(SUCCEEDED(hr)) - { - /* Try flipping the surfaces */ - hr = IDirectDrawSurface_Flip(surface3, NULL, DDFLIP_WAIT); - todo_wine ok(hr == DD_OK, "Got hr %#lx.\n", hr); - hr = IDirectDrawSurface_Flip(surface2, NULL, DDFLIP_WAIT); - todo_wine ok(hr == DDERR_NOTFLIPPABLE, "Got hr %#lx.\n", hr); - hr = IDirectDrawSurface_Flip(surface1, NULL, DDFLIP_WAIT); - ok(hr == DDERR_NOTFLIPPABLE, "Got hr %#lx.\n", hr); - - hr = IDirectDrawSurface_DeleteAttachedSurface(surface2, 0, surface3); - ok(hr == DD_OK, "Got hr %#lx.\n", hr); - } - hr = IDirectDrawSurface_AddAttachedSurface(surface1, surface4); - ok(hr == DDERR_CANNOTATTACHSURFACE, "Got hr %#lx.\n", hr); - hr = IDirectDrawSurface_AddAttachedSurface(surface4, surface1); - ok(hr == DDERR_CANNOTATTACHSURFACE, "Got hr %#lx.\n", hr); - - IDirectDrawSurface_Release(surface4); - IDirectDrawSurface_Release(surface3); - IDirectDrawSurface_Release(surface2); - IDirectDrawSurface_Release(surface1); + ok(hr == DDERR_SURFACEALREADYATTACHED, "Got hr %#lx.\n", hr); + hr = IDirectDrawSurface_DeleteAttachedSurface(surface1, 0, surface2); + ok(hr == DD_OK, "Got hr %#lx.\n", hr); + check_surface_caps(surface1, DDSCAPS_PRIMARYSURFACE | DDSCAPS_VISIBLE, placement, 0); + check_surface_caps(surface2, DDSCAPS_BACKBUFFER, placement, 0); } + hr = IDirectDrawSurface_AddAttachedSurface(surface2, surface1); + todo_wine ok(hr == DD_OK || broken(hr == DDERR_CANNOTATTACHSURFACE), + "Attaching a front buffer to a back buffer returned %#lx\n", hr); + if(SUCCEEDED(hr)) + { + check_surface_caps(surface1, DDSCAPS_PRIMARYSURFACE | DDSCAPS_VISIBLE | DDSCAPS_FRONTBUFFER | DDSCAPS_FLIP, placement, 0); + check_surface_caps(surface2, DDSCAPS_BACKBUFFER | DDSCAPS_FLIP, placement, 0); + /* Try flipping the surfaces */ + hr = IDirectDrawSurface_Flip(surface1, NULL, DDFLIP_WAIT); + ok(hr == DD_OK, "Got hr %#lx.\n", hr); + hr = IDirectDrawSurface_Flip(surface2, NULL, DDFLIP_WAIT); + ok(hr == DDERR_NOTFLIPPABLE, "Got hr %#lx.\n", hr); + + /* Try to detach reversed */ + hr = IDirectDrawSurface_DeleteAttachedSurface(surface1, 0, surface2); + ok(hr == DDERR_CANNOTDETACHSURFACE, "Got hr %#lx.\n", hr); + /* Now the proper detach */ + hr = IDirectDrawSurface_DeleteAttachedSurface(surface2, 0, surface1); + ok(hr == DD_OK, "Got hr %#lx.\n", hr); + check_surface_caps(surface1, DDSCAPS_PRIMARYSURFACE | DDSCAPS_VISIBLE, placement, 0); + check_surface_caps(surface2, DDSCAPS_BACKBUFFER, placement, 0); + } + check_surface_caps(surface3, DDSCAPS_BACKBUFFER, placement, 0); + hr = IDirectDrawSurface_AddAttachedSurface(surface2, surface3); + todo_wine ok(hr == DD_OK || broken(hr == DDERR_CANNOTATTACHSURFACE), + "Attaching a back buffer to another back buffer returned %#lx\n", hr); + if(SUCCEEDED(hr)) + { + /* Try flipping the surfaces */ + check_surface_caps(surface2, DDSCAPS_BACKBUFFER | DDSCAPS_FLIP, placement, 0); + check_surface_caps(surface3, DDSCAPS_FRONTBUFFER | DDSCAPS_BACKBUFFER | DDSCAPS_FLIP, placement, 0); + hr = IDirectDrawSurface_Flip(surface3, NULL, DDFLIP_WAIT); + ok(hr == DD_OK, "Got hr %#lx.\n", hr); + hr = IDirectDrawSurface_Flip(surface2, NULL, DDFLIP_WAIT); + ok(hr == DDERR_NOTFLIPPABLE, "Got hr %#lx.\n", hr); + hr = IDirectDrawSurface_Flip(surface1, NULL, DDFLIP_WAIT); + ok(hr == DDERR_NOTFLIPPABLE, "Got hr %#lx.\n", hr); + + hr = IDirectDrawSurface_DeleteAttachedSurface(surface2, 0, surface3); + ok(hr == DD_OK, "Got hr %#lx.\n", hr); + } + hr = IDirectDrawSurface_AddAttachedSurface(surface1, surface4); + ok(hr == DDERR_CANNOTATTACHSURFACE, "Got hr %#lx.\n", hr); + hr = IDirectDrawSurface_AddAttachedSurface(surface4, surface1); + ok(hr == DDERR_CANNOTATTACHSURFACE, "Got hr %#lx.\n", hr); + + IDirectDrawSurface_Release(surface4); + IDirectDrawSurface_Release(surface3); + IDirectDrawSurface_Release(surface2); + IDirectDrawSurface_Release(surface1); hr =IDirectDraw_SetCooperativeLevel(lpDD, NULL, DDSCL_NORMAL); ok(hr == DD_OK, "Got hr %#lx.\n", hr); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10992
From: Paul Gofman <pgofman@codeweavers.com> --- dlls/ddraw/ddraw_private.h | 2 + dlls/ddraw/surface.c | 137 +++++++++++++++++++++++++++++++++--- dlls/ddraw/tests/d3d.c | 6 +- dlls/ddraw/tests/dsurface.c | 6 +- dlls/ddraw/tests/visual.c | 4 +- 5 files changed, 139 insertions(+), 16 deletions(-) diff --git a/dlls/ddraw/ddraw_private.h b/dlls/ddraw/ddraw_private.h index f2c99a30d57..69b8ff68366 100644 --- a/dlls/ddraw/ddraw_private.h +++ b/dlls/ddraw/ddraw_private.h @@ -191,6 +191,7 @@ struct ddraw_surface struct ddraw_surface *next_attached; struct ddraw_surface *first_attached; IUnknown *attached_iface; + DWORD detach_clear_flags; /* Complex surfaces are organized in a tree, although the tree is degenerated to a list in most cases. * In mipmap and primary surfaces each level has only one attachment, which is the next surface level. @@ -203,6 +204,7 @@ struct ddraw_surface * but no pointer to prevent temptations to traverse it in the wrong direction. */ unsigned int is_root : 1; + unsigned int is_chain_start : 1; unsigned int is_lost : 1; unsigned int sysmem_fallback : 1; diff --git a/dlls/ddraw/surface.c b/dlls/ddraw/surface.c index 829fbf699ff..c48aeb7c839 100644 --- a/dlls/ddraw/surface.c +++ b/dlls/ddraw/surface.c @@ -28,11 +28,19 @@ WINE_DECLARE_DEBUG_CHANNEL(fps); static struct ddraw_surface *unsafe_impl_from_IDirectDrawSurface2(IDirectDrawSurface2 *iface); static struct ddraw_surface *unsafe_impl_from_IDirectDrawSurface3(IDirectDrawSurface3 *iface); +static HRESULT ddraw_surface_delete_attached_surface(struct ddraw_surface *surface, + struct ddraw_surface *attachment, IUnknown *detach_iface); static const struct wined3d_parent_ops ddraw_surface_wined3d_parent_ops; static const struct wined3d_parent_ops ddraw_texture_wined3d_parent_ops; static const struct wined3d_parent_ops ddraw_view_wined3d_parent_ops; +static BOOL surface_attachable_for_flip(struct ddraw_surface *surface) +{ + return surface->version == 1 && surface->is_root + && !(surface->surface_desc.ddsCaps.dwCaps & (DDSCAPS_ZBUFFER | DDSCAPS_TEXTURE | DDSCAPS_OVERLAY)); +} + static inline struct ddraw_surface *impl_from_IDirectDrawGammaControl(IDirectDrawGammaControl *iface) { return CONTAINING_RECORD(iface, struct ddraw_surface, IDirectDrawGammaControl_iface); @@ -595,6 +603,18 @@ static void ddraw_surface_cleanup(struct ddraw_surface *surface) break; surf = surface->complex_array[i]; + while (surf && surf->is_root && !surf->is_chain_start) + { + if (FAILED(ddraw_surface_delete_attached_surface(surface, surf, surf->attached_iface))) + { + ERR("ddraw_surface_delete_attached_surface failed.\n"); + break; + } + surf = surface->complex_array[i]; + } + if (!surf) + continue; + surface->complex_array[i] = NULL; if (!surf->is_root) { @@ -1401,7 +1421,8 @@ static HRESULT WINAPI DECLSPEC_HOTPATCH ddraw_surface1_Flip(IDirectDrawSurface * TRACE("iface %p, src %p, flags %#lx.\n", iface, src, flags); - if (src == iface || !(dst_impl->surface_desc.ddsCaps.dwCaps & (DDSCAPS_FRONTBUFFER | DDSCAPS_OVERLAY))) + if (src == iface || !(dst_impl->surface_desc.ddsCaps.dwCaps & (DDSCAPS_FRONTBUFFER | DDSCAPS_OVERLAY)) + || !(dst_impl->surface_desc.ddsCaps.dwCaps & DDSCAPS_FLIP)) return DDERR_NOTFLIPPABLE; if (ddraw_surface_is_lost(dst_impl)) @@ -1981,15 +2002,15 @@ static HRESULT WINAPI DECLSPEC_HOTPATCH ddraw_surface2_Blt(IDirectDrawSurface2 * * the surface that the app requested, not the complex root. See * GetAttachedSurface for a description how surfaces are found. * + * Attachments forming flip chain can only be explicitly attached on ddraw v1. That + * is implemented by inserting those into complex_array chain so that appear + * the same way as auto created back buffers in the attachment structure. + * * This is how the current implementation works, and it was coded by looking * at the needs of the applications. * - * So far only Z-Buffer attachments are tested, and they are activated in + * So far only Z-Buffer and flip chain attachments are tested, and they are activated in * WineD3D. Mipmaps could be tricky to activate in WineD3D. - * Back buffers should work in 2D mode, but they are not tested(They can be - * attached in older iface versions). Rendering to the front buffer and - * switching between that and double buffering is not yet implemented in - * WineD3D, so for 3D it might have unexpected results. * * ddraw_surface_attach_surface is the real thing, * ddraw_surface7_AddAttachedSurface is a wrapper around it that @@ -2015,6 +2036,56 @@ static HRESULT ddraw_surface_attach_surface(struct ddraw_surface *This, struct d wined3d_mutex_lock(); + if (surface_attachable_for_flip(Surf)) + { + if (This->surface_desc.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE + && !(This->ddraw->cooperative_level & DDERR_NOEXCLUSIVEMODE)) + { + wined3d_mutex_unlock(); + return DDERR_NOEXCLUSIVEMODE; + } + if (This->complex_array[0] == Surf) + { + wined3d_mutex_unlock(); + return DDERR_SURFACEALREADYATTACHED; + } + if (Surf->complex_array[0] || Surf->surface_desc.dwWidth != This->surface_desc.dwWidth + || Surf->surface_desc.dwHeight != This->surface_desc.dwHeight + || wined3dformat_from_ddrawformat(&Surf->surface_desc.ddpfPixelFormat) + != wined3dformat_from_ddrawformat(&This->surface_desc.ddpfPixelFormat)) + { + wined3d_mutex_unlock(); + return DDERR_CANNOTATTACHSURFACE; + } + Surf->detach_clear_flags = DDSCAPS_FLIP; + Surf->surface_desc.ddsCaps.dwCaps |= DDSCAPS_FLIP; + This->surface_desc.ddsCaps.dwCaps |= DDSCAPS_FLIP; + if (!This->complex_array[0]) + { + This->is_chain_start = 1; + if (!(This->surface_desc.ddsCaps.dwCaps & (DDSCAPS_FRONTBUFFER | DDSCAPS_BACKBUFFER))) + { + This->surface_desc.ddsCaps.dwCaps |= DDSCAPS_FRONTBUFFER; + Surf->surface_desc.ddsCaps.dwCaps |= DDSCAPS_BACKBUFFER; + Surf->surface_desc.ddsCaps.dwCaps &= ~DDSCAPS_FRONTBUFFER; + } + else if (This->surface_desc.ddsCaps.dwCaps & DDSCAPS_BACKBUFFER) + { + Surf->surface_desc.ddsCaps.dwCaps |= DDSCAPS_FRONTBUFFER; + } + } + Surf->is_chain_start = 0; + Surf->complex_array[0] = This->complex_array[0] ? This->complex_array[0] : This; + This->complex_array[0] = Surf; + Surf = This; + while (!Surf->is_chain_start) + Surf = Surf->complex_array[0]; + if (Surf->surface_desc.dwFlags & DDSD_BACKBUFFERCOUNT) + ++Surf->surface_desc.dwBackBufferCount; + wined3d_mutex_unlock(); + return DD_OK; + } + /* Check if the surface is already attached somewhere */ if (Surf->next_attached || Surf->first_attached != Surf) { @@ -2095,8 +2166,11 @@ static HRESULT WINAPI ddraw_surface4_AddAttachedSurface(IDirectDrawSurface4 *ifa return DDERR_CANNOTATTACHSURFACE; } } - else if (!(surface->surface_desc.ddsCaps.dwCaps & (DDSCAPS_PRIMARYSURFACE | DDSCAPS_3DDEVICE)) - || !(attachment_impl->surface_desc.ddsCaps.dwCaps & (DDSCAPS_ZBUFFER))) + else if ((!(surface->surface_desc.ddsCaps.dwCaps & (DDSCAPS_PRIMARYSURFACE | DDSCAPS_3DDEVICE)) + || !(attachment_impl->surface_desc.ddsCaps.dwCaps & DDSCAPS_ZBUFFER)) + && !(surface->version == 1 + && !(surface->surface_desc.ddsCaps.dwCaps & ((DDSCAPS_ZBUFFER | DDSCAPS_TEXTURE | DDSCAPS_OVERLAY))) + && surface_attachable_for_flip(attachment_impl))) { WARN("Invalid attachment combination.\n"); return DDERR_CANNOTATTACHSURFACE; @@ -2189,6 +2263,52 @@ static HRESULT ddraw_surface_delete_attached_surface(struct ddraw_surface *surfa TRACE("surface %p, attachment %p, detach_iface %p.\n", surface, attachment, detach_iface); wined3d_mutex_lock(); + + if (attachment->surface_desc.ddsCaps.dwCaps & DDSCAPS_FLIP) + { + struct ddraw_surface *surf = surface; + + if (surface->version != 1 || surface->complex_array[0] != attachment) + { + WARN("backbuffer %p is not attached to %p.\n", attachment, surface); + wined3d_mutex_unlock(); + return DDERR_SURFACENOTATTACHED; + } + if (!attachment->is_root || attachment->is_chain_start) + { + wined3d_mutex_unlock(); + WARN("trying to detach non-root attachment %p.\n", attachment); + return DDERR_CANNOTDETACHSURFACE; + } + assert(attachment->complex_array[0]); + while (!surf->is_chain_start) + surf = surf->complex_array[0]; + if (surf->surface_desc.dwFlags & DDSD_BACKBUFFERCOUNT) + --surf->surface_desc.dwBackBufferCount; + surface->complex_array[0] = attachment->complex_array[0]; + if (surface->complex_array[0] == surface) + { + attachment->surface_desc.ddsCaps.dwCaps &= ~DDSCAPS_FRONTBUFFER; + surface->is_chain_start = 0; + surface->complex_array[0] = NULL; + surface->surface_desc.ddsCaps.dwCaps &= ~(DDSCAPS_FLIP | DDSCAPS_FRONTBUFFER); + } + else + { + if (attachment->surface_desc.ddsCaps.dwCaps & DDSCAPS_FRONTBUFFER) + surface->complex_array[0]->detach_clear_flags |= DDSCAPS_FRONTBUFFER; + else + surface->complex_array[0]->detach_clear_flags |= DDSCAPS_BACKBUFFER; + surface->complex_array[0]->surface_desc.ddsCaps.dwCaps |= surface->complex_array[0]->detach_clear_flags; + } + attachment->surface_desc.ddsCaps.dwCaps &= ~attachment->detach_clear_flags; + attachment->complex_array[0] = NULL; + IUnknown_Release(detach_iface); + attachment->attached_iface = NULL; + wined3d_mutex_unlock(); + return DD_OK; + } + if (!attachment || (attachment->first_attached != surface) || (attachment == surface) ) { wined3d_mutex_unlock(); @@ -6986,6 +7106,7 @@ HRESULT ddraw_surface_create(struct ddraw *ddraw, const DDSURFACEDESC2 *surface_ unsigned int count = desc->dwBackBufferCount; struct ddraw_surface *last = root; + root->is_chain_start = 1; attach = &last->complex_array[0]; for (i = 0; i < count; ++i) { diff --git a/dlls/ddraw/tests/d3d.c b/dlls/ddraw/tests/d3d.c index ec536a83729..d9e795118f7 100644 --- a/dlls/ddraw/tests/d3d.c +++ b/dlls/ddraw/tests/d3d.c @@ -1605,7 +1605,7 @@ static void BackBuffer3DAttachmentTest(void) ok(hr == DD_OK, "Got hr %#lx.\n", hr); hr = IDirectDrawSurface_AddAttachedSurface(surface1, surface2); - todo_wine ok(hr == DD_OK || broken(hr == DDERR_CANNOTATTACHSURFACE), "Got hr %#lx.\n", hr); + ok(hr == DD_OK || broken(hr == DDERR_CANNOTATTACHSURFACE), "Got hr %#lx.\n", hr); if(SUCCEEDED(hr)) { /* Try the reverse without detaching first */ @@ -1615,7 +1615,7 @@ static void BackBuffer3DAttachmentTest(void) ok(hr == DD_OK, "Got hr %#lx.\n", hr); } hr = IDirectDrawSurface_AddAttachedSurface(surface2, surface1); - todo_wine ok(hr == DD_OK || broken(hr == DDERR_CANNOTATTACHSURFACE), "Got hr %#lx.\n", hr); + ok(hr == DD_OK || broken(hr == DDERR_CANNOTATTACHSURFACE), "Got hr %#lx.\n", hr); if(SUCCEEDED(hr)) { /* Try to detach reversed */ @@ -1626,7 +1626,7 @@ static void BackBuffer3DAttachmentTest(void) ok(hr == DD_OK, "Got hr %#lx.\n", hr); } hr = IDirectDrawSurface_AddAttachedSurface(surface2, surface3); - todo_wine ok(hr == DD_OK || broken(hr == DDERR_CANNOTATTACHSURFACE), "Got hr %#lx.\n", hr); + ok(hr == DD_OK || broken(hr == DDERR_CANNOTATTACHSURFACE), "Got hr %#lx.\n", hr); if(SUCCEEDED(hr)) { hr = IDirectDrawSurface_DeleteAttachedSurface(surface2, 0, surface3); diff --git a/dlls/ddraw/tests/dsurface.c b/dlls/ddraw/tests/dsurface.c index 7f5c51fe258..a47a53a0b37 100644 --- a/dlls/ddraw/tests/dsurface.c +++ b/dlls/ddraw/tests/dsurface.c @@ -1730,7 +1730,7 @@ static void BackBufferAttachmentFlipTest(void) ok(hr==DD_OK, "Got hr %#lx.\n", hr); hr = IDirectDrawSurface_AddAttachedSurface(surface1, surface2); - todo_wine ok(hr == DD_OK || broken(hr == DDERR_CANNOTATTACHSURFACE), + ok(hr == DD_OK || broken(hr == DDERR_CANNOTATTACHSURFACE), "Attaching a back buffer to a front buffer returned %#lx\n", hr); if(SUCCEEDED(hr)) { @@ -1751,7 +1751,7 @@ static void BackBufferAttachmentFlipTest(void) check_surface_caps(surface2, DDSCAPS_BACKBUFFER, placement, 0); } hr = IDirectDrawSurface_AddAttachedSurface(surface2, surface1); - todo_wine ok(hr == DD_OK || broken(hr == DDERR_CANNOTATTACHSURFACE), + ok(hr == DD_OK || broken(hr == DDERR_CANNOTATTACHSURFACE), "Attaching a front buffer to a back buffer returned %#lx\n", hr); if(SUCCEEDED(hr)) { @@ -1774,7 +1774,7 @@ static void BackBufferAttachmentFlipTest(void) } check_surface_caps(surface3, DDSCAPS_BACKBUFFER, placement, 0); hr = IDirectDrawSurface_AddAttachedSurface(surface2, surface3); - todo_wine ok(hr == DD_OK || broken(hr == DDERR_CANNOTATTACHSURFACE), + ok(hr == DD_OK || broken(hr == DDERR_CANNOTATTACHSURFACE), "Attaching a back buffer to another back buffer returned %#lx\n", hr); if(SUCCEEDED(hr)) { diff --git a/dlls/ddraw/tests/visual.c b/dlls/ddraw/tests/visual.c index f5c6e79f832..347e0d21fb4 100644 --- a/dlls/ddraw/tests/visual.c +++ b/dlls/ddraw/tests/visual.c @@ -1540,7 +1540,7 @@ static void DX1_BackBufferFlipTest(void) if(FAILED(hr)) goto out; hr = IDirectDrawSurface_AddAttachedSurface(Primary, Backbuffer); - todo_wine ok(hr == DD_OK || broken(hr == DDERR_CANNOTATTACHSURFACE), "Got hr %#lx.\n", hr); + ok(hr == DD_OK || broken(hr == DDERR_CANNOTATTACHSURFACE), "Got hr %#lx.\n", hr); if (FAILED(hr)) goto out; attached = TRUE; @@ -1567,7 +1567,7 @@ static void DX1_BackBufferFlipTest(void) GetRValue(color), GetGValue(color), GetBValue(color)); hr = IDirectDrawSurface_Flip(Primary, NULL, DDFLIP_WAIT); - todo_wine ok(hr == DD_OK, "Got hr %#lx.\n", hr); + ok(hr == DD_OK, "Got hr %#lx.\n", hr); if (hr == DD_OK) { -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10992
From: Paul Gofman <pgofman@codeweavers.com> --- dlls/ddraw/tests/ddraw1.c | 556 +++++++++++++++++++++++++++++++++++--- 1 file changed, 522 insertions(+), 34 deletions(-) diff --git a/dlls/ddraw/tests/ddraw1.c b/dlls/ddraw/tests/ddraw1.c index 007daf29407..6ce50821590 100644 --- a/dlls/ddraw/tests/ddraw1.c +++ b/dlls/ddraw/tests/ddraw1.c @@ -452,6 +452,21 @@ static void fill_surface(IDirectDrawSurface *surface, D3DCOLOR color) ok(SUCCEEDED(hr), "Failed to unlock surface, hr %#lx.\n", hr); } +#define check_surface_caps(a, b, c, d) check_surface_caps_(__LINE__, (a), (b), (c), (d)) +static void check_surface_caps_(unsigned int line, IDirectDrawSurface *surface, DWORD expected_caps, + DWORD exclude_caps, DWORD expected_buffer_count) +{ + DDSURFACEDESC surface_desc = {.dwSize = sizeof(surface_desc) }; + HRESULT hr; + + hr = IDirectDrawSurface_GetSurfaceDesc(surface, &surface_desc); + ok_(__FILE__, line)(hr == DD_OK, "got %#lx.\n", hr); + ok_(__FILE__, line)((surface_desc.ddsCaps.dwCaps & ~exclude_caps) == expected_caps, "got %#lx, expected %#lx.\n", + surface_desc.ddsCaps.dwCaps & ~exclude_caps, expected_caps); + ok_(__FILE__, line)(surface_desc.dwBackBufferCount == expected_buffer_count, "got backbuffer count %ld, expected %ld.\n", + surface_desc.dwBackBufferCount, expected_buffer_count); +} + static void check_rect(IDirectDrawSurface *surface, RECT r) { LONG x_coords[2][2] = @@ -5406,13 +5421,16 @@ static void test_flip(void) IDirectDrawSurface *frontbuffer, *backbuffer1, *backbuffer2, *backbuffer3, *surface; DDSCAPS caps = {DDSCAPS_FLIP}; DDSURFACEDESC surface_desc; + IDirect3DDevice *device; unsigned int color, i; + DWORD expected_caps; BOOL sysmem_primary; IDirectDraw *ddraw; - DWORD expected_caps; + BOOL have_3ddevice; ULONG refcount; HWND window; HRESULT hr; + LONG ref; static const struct { @@ -5430,6 +5448,16 @@ static void test_flip(void) ddraw = create_ddraw(); ok(!!ddraw, "Failed to create a ddraw object.\n"); + if ((device = create_device(ddraw, window, DDSCL_NORMAL))) + { + have_3ddevice = TRUE; + IDirect3DDevice_Release(device); + } + else + { + have_3ddevice = FALSE; + } + hr = IDirectDraw_SetCooperativeLevel(ddraw, window, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN); ok(SUCCEEDED(hr), "Failed to set cooperative level, hr %#lx.\n", hr); @@ -5489,52 +5517,26 @@ static void test_flip(void) hr = restore_surfaces(ddraw); ok(SUCCEEDED(hr), "%s: Failed to restore surfaces, hr %#lx.\n", test_data[i].name, hr); - memset(&surface_desc, 0, sizeof(surface_desc)); - surface_desc.dwSize = sizeof(surface_desc); - hr = IDirectDrawSurface_GetSurfaceDesc(frontbuffer, &surface_desc); - ok(SUCCEEDED(hr), "%s: Failed to get surface desc, hr %#lx.\n", test_data[i].name, hr); expected_caps = DDSCAPS_FRONTBUFFER | DDSCAPS_COMPLEX | DDSCAPS_FLIP | test_data[i].caps; if (test_data[i].caps & DDSCAPS_PRIMARYSURFACE) expected_caps |= DDSCAPS_VISIBLE; - ok((surface_desc.ddsCaps.dwCaps & ~placement) == expected_caps, - "%s: Got unexpected caps %#lx.\n", test_data[i].name, surface_desc.ddsCaps.dwCaps); + check_surface_caps(frontbuffer, expected_caps, placement, 3); sysmem_primary = surface_desc.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY; hr = IDirectDrawSurface_GetAttachedSurface(frontbuffer, &caps, &backbuffer1); ok(SUCCEEDED(hr), "%s: Failed to get attached surface, hr %#lx.\n", test_data[i].name, hr); - memset(&surface_desc, 0, sizeof(surface_desc)); - surface_desc.dwSize = sizeof(surface_desc); - hr = IDirectDrawSurface_GetSurfaceDesc(backbuffer1, &surface_desc); - ok(SUCCEEDED(hr), "%s: Failed to get surface desc, hr %#lx.\n", test_data[i].name, hr); - ok(!surface_desc.dwBackBufferCount, "%s: Got unexpected back buffer count %lu.\n", - test_data[i].name, surface_desc.dwBackBufferCount); expected_caps &= ~(DDSCAPS_VISIBLE | DDSCAPS_PRIMARYSURFACE | DDSCAPS_FRONTBUFFER); expected_caps |= DDSCAPS_BACKBUFFER; - ok((surface_desc.ddsCaps.dwCaps & ~placement) == expected_caps, - "%s: Got unexpected caps %#lx.\n", test_data[i].name, surface_desc.ddsCaps.dwCaps); + check_surface_caps(backbuffer1, expected_caps, placement, 0); hr = IDirectDrawSurface_GetAttachedSurface(backbuffer1, &caps, &backbuffer2); ok(SUCCEEDED(hr), "%s: Failed to get attached surface, hr %#lx.\n", test_data[i].name, hr); - memset(&surface_desc, 0, sizeof(surface_desc)); - surface_desc.dwSize = sizeof(surface_desc); - hr = IDirectDrawSurface_GetSurfaceDesc(backbuffer2, &surface_desc); - ok(SUCCEEDED(hr), "%s: Failed to get surface desc, hr %#lx.\n", test_data[i].name, hr); - ok(!surface_desc.dwBackBufferCount, "%s: Got unexpected back buffer count %lu.\n", - test_data[i].name, surface_desc.dwBackBufferCount); expected_caps &= ~DDSCAPS_BACKBUFFER; - ok((surface_desc.ddsCaps.dwCaps & ~placement) == expected_caps, - "%s: Got unexpected caps %#lx.\n", test_data[i].name, surface_desc.ddsCaps.dwCaps); + check_surface_caps(backbuffer2, expected_caps, placement, 0); hr = IDirectDrawSurface_GetAttachedSurface(backbuffer2, &caps, &backbuffer3); ok(SUCCEEDED(hr), "%s: Failed to get attached surface, hr %#lx.\n", test_data[i].name, hr); - memset(&surface_desc, 0, sizeof(surface_desc)); - surface_desc.dwSize = sizeof(surface_desc); - hr = IDirectDrawSurface_GetSurfaceDesc(backbuffer3, &surface_desc); - ok(SUCCEEDED(hr), "%s: Failed to get surface desc, hr %#lx.\n", test_data[i].name, hr); - ok(!surface_desc.dwBackBufferCount, "%s: Got unexpected back buffer count %lu.\n", - test_data[i].name, surface_desc.dwBackBufferCount); - ok((surface_desc.ddsCaps.dwCaps & ~placement) == expected_caps, - "%s: Got unexpected caps %#lx.\n", test_data[i].name, surface_desc.ddsCaps.dwCaps); + check_surface_caps(backbuffer3, expected_caps, placement, 0); hr = IDirectDrawSurface_GetAttachedSurface(backbuffer3, &caps, &surface); ok(SUCCEEDED(hr), "%s: Failed to get attached surface, hr %#lx.\n", test_data[i].name, hr); @@ -5631,6 +5633,222 @@ static void test_flip(void) IDirectDrawSurface_Release(frontbuffer); } + /* Test adding backbuffers to primary surface in exclusive fullscreen mode. */ + memset(&surface_desc, 0, sizeof(surface_desc)); + surface_desc.dwSize = sizeof(surface_desc); + surface_desc.dwFlags = DDSD_CAPS; + surface_desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FRONTBUFFER; + hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &frontbuffer, NULL); + ok(hr == DD_OK, "got %#lx.\n", hr); + fill_surface(frontbuffer, 0xffff0000); + + hr = IDirectDrawSurface_GetSurfaceDesc(frontbuffer, &surface_desc); + ok(hr == DD_OK, "got %#lx.\n", hr); + surface_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; + surface_desc.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER | DDSCAPS_3DDEVICE; + hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &backbuffer1, NULL); + ok(hr == DD_OK, "got %#lx.\n", hr); + fill_surface(backbuffer1, 0xff00ff00); + + hr = IDirectDrawSurface_GetSurfaceDesc(frontbuffer, &surface_desc); + ok(hr == DD_OK, "got %#lx.\n", hr); + surface_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; + surface_desc.ddsCaps.dwCaps = DDSCAPS_FRONTBUFFER | DDSCAPS_3DDEVICE; + hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &backbuffer2, NULL); + ok(hr == DD_OK, "got %#lx.\n", hr); + fill_surface(backbuffer2, 0xff0000ff); + + hr = IDirectDrawSurface_Flip(frontbuffer, NULL, DDFLIP_WAIT); + ok(hr == DDERR_NOTFLIPPABLE, "Got unexpected hr %#lx.\n", hr); + + check_surface_caps(frontbuffer, DDSCAPS_PRIMARYSURFACE | DDSCAPS_VISIBLE | DDSCAPS_FRONTBUFFER, placement, 0); + check_surface_caps(backbuffer1, DDSCAPS_BACKBUFFER | DDSCAPS_3DDEVICE, placement, 0); + check_surface_caps(backbuffer2, DDSCAPS_FRONTBUFFER | DDSCAPS_3DDEVICE, placement, 0); + hr = IDirectDrawSurface_AddAttachedSurface(backbuffer1, backbuffer2); + ok(hr == DD_OK, "got %#lx.\n", hr); + check_surface_caps(frontbuffer, DDSCAPS_PRIMARYSURFACE | DDSCAPS_VISIBLE | DDSCAPS_FRONTBUFFER, placement, 0); + check_surface_caps(backbuffer1, DDSCAPS_FLIP | DDSCAPS_BACKBUFFER | DDSCAPS_3DDEVICE, placement, 0); + check_surface_caps(backbuffer2, DDSCAPS_FLIP | DDSCAPS_FRONTBUFFER | DDSCAPS_3DDEVICE, placement, 0); + + hr = IDirectDrawSurface_DeleteAttachedSurface(backbuffer2, 0, backbuffer1); + ok(hr == DDERR_CANNOTDETACHSURFACE, "Failed to delete attached surface, hr %#lx.\n", hr); + + ref = get_refcount((IUnknown *)backbuffer2); + ok(ref == 2, "got %ld.\n", ref); + ref = get_refcount((IUnknown *)backbuffer1); + ok(ref == 1, "got %ld.\n", ref); + hr = IDirectDrawSurface_DeleteAttachedSurface(backbuffer1, 0, backbuffer2); + ok(hr == DD_OK, "Failed to delete attached surface, hr %#lx.\n", hr); + ref = get_refcount((IUnknown *)backbuffer2); + ok(ref == 1, "got %ld.\n", ref); + ref = get_refcount((IUnknown *)backbuffer1); + ok(ref == 1, "got %ld.\n", ref); + + check_surface_caps(backbuffer2, DDSCAPS_3DDEVICE, placement, 0); + + ref = get_refcount((IUnknown *)backbuffer1); + ok(ref == 1, "got %ld.\n", ref); + hr = IDirectDrawSurface_AddAttachedSurface(frontbuffer, backbuffer1); + ok(hr == DD_OK, "got %#lx.\n", hr); + ref = get_refcount((IUnknown *)backbuffer1); + ok(ref == 2, "got %ld.\n", ref); + + check_surface_caps(frontbuffer, DDSCAPS_PRIMARYSURFACE | DDSCAPS_VISIBLE | DDSCAPS_FRONTBUFFER | DDSCAPS_FLIP, placement, 0); + check_surface_caps(backbuffer1, DDSCAPS_BACKBUFFER | DDSCAPS_3DDEVICE | DDSCAPS_FLIP, placement, 0); + + hr = IDirectDrawSurface_Flip(backbuffer1, NULL, DDFLIP_WAIT); + ok(hr == DDERR_NOTFLIPPABLE, "got %#lx.\n", hr); + + hr = IDirectDrawSurface_Flip(frontbuffer, NULL, DDFLIP_WAIT); + ok(hr == DD_OK, "got %#lx.\n", hr); + + if (have_3ddevice) + { + hr = IDirectDrawSurface_QueryInterface(backbuffer1, &IID_IDirect3DHALDevice, (void **)&device); + todo_wine ok(hr == DD_OK || broken(hr == E_FAIL), "got %#lx.\n", hr); + if (SUCCEEDED(hr)) + IDirect3DDevice_Release(device); + } + + color = get_surface_color(frontbuffer, 1, 1); + ok(color == 0x0000ff00, "got %#x.\n", color); + color = get_surface_color(backbuffer1, 1, 1); + ok(color == 0x00ff0000, "got %#x.\n", color); + + ref = get_refcount((IUnknown *)backbuffer2); + ok(ref == 1, "got %ld.\n", ref); + hr = IDirectDrawSurface_AddAttachedSurface(backbuffer1, backbuffer2); + ok(hr == DD_OK, "got %#lx.\n", hr); + ref = get_refcount((IUnknown *)backbuffer2); + ok(ref == 2, "got %ld.\n", ref); + check_surface_caps(backbuffer2, DDSCAPS_3DDEVICE | DDSCAPS_FLIP, placement, 0); + hr = IDirectDrawSurface_AddAttachedSurface(backbuffer1, backbuffer2); + ok(hr == DDERR_SURFACEALREADYATTACHED, "got %#lx.\n", hr); + hr = IDirectDrawSurface_AddAttachedSurface(frontbuffer, backbuffer2); + ok(hr == DDERR_CANNOTATTACHSURFACE, "got %#lx.\n", hr); + + hr = IDirectDrawSurface_Flip(backbuffer2, NULL, DDFLIP_WAIT); + ok(hr == DDERR_NOTFLIPPABLE, "got %#lx.\n", hr); + hr = IDirectDrawSurface_Flip(backbuffer1, NULL, DDFLIP_WAIT); + ok(hr == DDERR_NOTFLIPPABLE, "got %#lx.\n", hr); + + hr = IDirectDrawSurface_Flip(frontbuffer, NULL, DDFLIP_WAIT); + ok(hr == DD_OK, "got %#lx.\n", hr); + + color = get_surface_color(frontbuffer, 1, 1); + ok(color == 0x00ff0000, "got %#x.\n", color); + color = get_surface_color(backbuffer1, 1, 1); + ok(color == 0x000000ff, "got %#x.\n", color); + color = get_surface_color(backbuffer2, 1, 1); + ok(color == 0x0000ff00, "got %#x.\n", color); + + hr = IDirectDrawSurface_DeleteAttachedSurface(frontbuffer, 0, backbuffer2); + ok(hr == DDERR_SURFACENOTATTACHED, "got %#lx.\n", hr); + ref = get_refcount((IUnknown *)frontbuffer); + ok(ref == 1, "got %ld.\n", ref); + hr = IDirectDrawSurface_DeleteAttachedSurface(backbuffer1, 0, backbuffer2); + ok(hr == DD_OK, "got %#lx.\n", hr); + ref = get_refcount((IUnknown *)frontbuffer); + todo_wine ok(ref == 2, "got %ld.\n", ref); + + ref = get_refcount((IUnknown *)backbuffer1); + ok(ref == 2, "got %ld.\n", ref); + ref = get_refcount((IUnknown *)backbuffer2); + ok(ref == 1, "got %ld.\n", ref); + ref = IDirectDrawSurface_Release(frontbuffer); + todo_wine ok(ref == 1, "got %ld.\n", ref); + if (ref) + IDirectDrawSurface_Release(frontbuffer); + ref = IDirectDrawSurface_Release(backbuffer1); + ok(!ref, "got %ld.\n", ref); + ref = IDirectDrawSurface_Release(backbuffer2); + ok(!ref, "got %ld.\n", ref); + + /* Test adding backbuffers to primary surface in windowed mode. */ + hr = IDirectDraw_SetCooperativeLevel(ddraw, window, DDSCL_NORMAL); + ok(hr == DD_OK, "got %#lx.\n", hr); + + memset(&surface_desc, 0, sizeof(surface_desc)); + surface_desc.dwSize = sizeof(surface_desc); + surface_desc.dwFlags = DDSD_CAPS; + surface_desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FRONTBUFFER; + hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &frontbuffer, NULL); + ok(hr == DD_OK, "got %#lx.\n", hr); + fill_surface(frontbuffer, 0xffff0000); + + hr = IDirectDrawSurface_GetSurfaceDesc(frontbuffer, &surface_desc); + ok(hr == DD_OK, "got %#lx.\n", hr); + surface_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; + surface_desc.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER | DDSCAPS_3DDEVICE; + hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &backbuffer1, NULL); + ok(hr == DD_OK, "got %#lx.\n", hr); + fill_surface(backbuffer1, 0xff00ff00); + + hr = IDirectDrawSurface_GetSurfaceDesc(frontbuffer, &surface_desc); + ok(hr == DD_OK, "got %#lx.\n", hr); + surface_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; + surface_desc.ddsCaps.dwCaps = DDSCAPS_FRONTBUFFER | DDSCAPS_3DDEVICE; + hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &backbuffer2, NULL); + ok(hr == DD_OK, "got %#lx.\n", hr); + fill_surface(backbuffer2, 0xff0000ff); + + hr = IDirectDrawSurface_Flip(frontbuffer, NULL, DDFLIP_WAIT); + ok(hr == DDERR_NOTFLIPPABLE, "Got unexpected hr %#lx.\n", hr); + + check_surface_caps(frontbuffer, DDSCAPS_PRIMARYSURFACE | DDSCAPS_VISIBLE | DDSCAPS_FRONTBUFFER, placement, 0); + + hr = IDirectDrawSurface_AddAttachedSurface(frontbuffer, backbuffer1); + ok(hr == DDERR_NOEXCLUSIVEMODE, "got %#lx.\n", hr); + hr = IDirectDrawSurface_AddAttachedSurface(frontbuffer, backbuffer2); + ok(hr == DDERR_NOEXCLUSIVEMODE, "got %#lx.\n", hr); + + hr = IDirectDrawSurface_AddAttachedSurface(backbuffer1, backbuffer2); + ok(hr == DD_OK, "got %#lx.\n", hr); + check_surface_caps(backbuffer1, DDSCAPS_BACKBUFFER | DDSCAPS_3DDEVICE | DDSCAPS_FLIP, placement, 0); + + hr = IDirectDrawSurface_AddAttachedSurface(frontbuffer, backbuffer1); + ok(hr == DDERR_NOEXCLUSIVEMODE, "got %#lx.\n", hr); + + check_surface_caps(backbuffer2, DDSCAPS_FRONTBUFFER | DDSCAPS_3DDEVICE | DDSCAPS_FLIP, placement, 0); + + hr = IDirectDrawSurface_DeleteAttachedSurface(backbuffer2, 0, backbuffer1); + ok(hr == DDERR_CANNOTDETACHSURFACE, "Failed to delete attached surface, hr %#lx.\n", hr); + + hr = IDirectDrawSurface_AddAttachedSurface(frontbuffer, backbuffer1); + ok(hr == DDERR_NOEXCLUSIVEMODE, "got %#lx.\n", hr); + + hr = IDirectDrawSurface_Flip(backbuffer1, NULL, DDFLIP_WAIT); + ok(hr == DDERR_NOTFLIPPABLE, "got %#lx.\n", hr); + hr = IDirectDrawSurface_Flip(frontbuffer, NULL, DDFLIP_WAIT); + ok(hr == DDERR_NOTFLIPPABLE, "got %#lx.\n", hr); + hr = IDirectDrawSurface_Flip(backbuffer2, NULL, DDFLIP_WAIT); + ok(hr == DD_OK, "got %#lx.\n", hr); + + color = get_surface_color(backbuffer1, 1, 1); + ok(color == 0x000000ff, "got %#x.\n", color); + color = get_surface_color(backbuffer2, 1, 1); + ok(color == 0x0000ff00, "got %#x.\n", color); + + hr = IDirectDrawSurface_Flip(backbuffer2, NULL, DDFLIP_WAIT); + ok(hr == DD_OK, "got %#lx.\n", hr); + hr = IDirectDrawSurface_Flip(backbuffer1, NULL, DDFLIP_WAIT); + ok(hr == DDERR_NOTFLIPPABLE, "got %#lx.\n", hr); + + color = get_surface_color(backbuffer1, 1, 1); + ok(color == 0x0000ff00, "got %#x.\n", color); + color = get_surface_color(backbuffer2, 1, 1); + ok(color == 0x000000ff, "got %#x.\n", color); + + ref = IDirectDrawSurface_Release(frontbuffer); + ok(!ref, "got %ld.\n", ref); + ref = IDirectDrawSurface_Release(backbuffer1); + ok(!ref, "got %ld.\n", ref); + ref = IDirectDrawSurface_Release(backbuffer2); + ok(!ref, "got %ld.\n", ref); + + hr = IDirectDraw_SetCooperativeLevel(ddraw, NULL, DDSCL_NORMAL); + ok(hr == DD_OK, "got %#lx.\n", hr); + refcount = IDirectDraw_Release(ddraw); ok(!refcount, "Unexpected refcount %lu.\n", refcount); DestroyWindow(window); @@ -5840,14 +6058,17 @@ static HRESULT WINAPI surface_counter(IDirectDrawSurface *surface, DDSURFACEDESC static void test_surface_attachment(void) { - IDirectDrawSurface *surface1, *surface2, *surface3, *surface4; - DDSCAPS caps = {DDSCAPS_TEXTURE}; + const DWORD placement = DDSCAPS_LOCALVIDMEM | DDSCAPS_VIDEOMEMORY | DDSCAPS_SYSTEMMEMORY; + IDirectDrawSurface *surface1, *surface2, *surface3, *surface4, *surface5, *surface6, *tmp; + IDirectDrawSurface *backbuffer1, *backbuffer2; DDSURFACEDESC surface_desc; IDirectDraw *ddraw; UINT surface_count; ULONG refcount; + DDSCAPS caps; HWND window; HRESULT hr; + LONG ref; window = create_window(); ddraw = create_ddraw(); @@ -5865,6 +6086,7 @@ static void test_surface_attachment(void) hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &surface1, NULL); ok(SUCCEEDED(hr), "Failed to create surface, hr %#lx.\n", hr); + caps.dwCaps = DDSCAPS_TEXTURE; hr = IDirectDrawSurface_GetAttachedSurface(surface1, &caps, &surface2); ok(SUCCEEDED(hr), "Failed to get mip level, hr %#lx.\n", hr); hr = IDirectDrawSurface_GetAttachedSurface(surface2, &caps, &surface3); @@ -5980,8 +6202,30 @@ static void test_surface_attachment(void) hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &surface4, NULL); ok(SUCCEEDED(hr), "Failed to create surface, hr %#lx.\n", hr); + memset(&surface_desc, 0, sizeof(surface_desc)); + surface_desc.dwSize = sizeof(surface_desc); + surface_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; + surface_desc.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER; + surface_desc.dwWidth = registry_mode.dmPelsWidth; + surface_desc.dwHeight = registry_mode.dmPelsHeight; + hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &surface5, NULL); + ok(hr == DD_OK, "got %#lx.\n", hr); + + memset(&surface_desc, 0, sizeof(surface_desc)); + surface_desc.dwSize = sizeof(surface_desc); + surface_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; + surface_desc.ddsCaps.dwCaps = DDSCAPS_FRONTBUFFER; + surface_desc.dwWidth = registry_mode.dmPelsWidth; + surface_desc.dwHeight = registry_mode.dmPelsHeight; + hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &surface6, NULL); + ok(hr == DD_OK, "got %#lx.\n", hr); + + check_surface_caps(surface1, DDSCAPS_PRIMARYSURFACE | DDSCAPS_VISIBLE, + placement, 0); hr = IDirectDrawSurface_AddAttachedSurface(surface1, surface2); ok(SUCCEEDED(hr), "Failed to attach surface, hr %#lx.\n", hr); + check_surface_caps(surface1, DDSCAPS_PRIMARYSURFACE | DDSCAPS_FRONTBUFFER | DDSCAPS_VISIBLE | DDSCAPS_FLIP, + placement, 0); /* Try the reverse without detaching first. */ hr = IDirectDrawSurface_AddAttachedSurface(surface2, surface1); ok(hr == DDERR_SURFACEALREADYATTACHED, "Got unexpected hr %#lx.\n", hr); @@ -6006,10 +6250,250 @@ static void test_surface_attachment(void) hr = IDirectDrawSurface_AddAttachedSurface(surface4, surface1); ok(hr == DDERR_CANNOTATTACHSURFACE, "Got unexpected hr %#lx.\n", hr); + check_surface_caps(surface5, DDSCAPS_BACKBUFFER, placement, 0); + check_surface_caps(surface6, DDSCAPS_FRONTBUFFER, placement, 0); + hr = IDirectDrawSurface_AddAttachedSurface(surface5, surface6); + ok(hr == DD_OK, "got %#lx.\n", hr); + check_surface_caps(surface5, DDSCAPS_FLIP | DDSCAPS_BACKBUFFER, placement, 0); + check_surface_caps(surface6, DDSCAPS_FLIP | DDSCAPS_FRONTBUFFER, placement, 0); + hr = IDirectDrawSurface_AddAttachedSurface(surface1, surface6); + ok(hr == DDERR_CANNOTATTACHSURFACE, "got %#lx.\n", hr); + hr = IDirectDrawSurface_AddAttachedSurface(surface1, surface5); + ok(hr == DDERR_CANNOTATTACHSURFACE, "got %#lx.\n", hr); + hr = IDirectDrawSurface_DeleteAttachedSurface(surface5, 0, surface6); + ok(hr == DD_OK, "got %#lx.\n", hr); + check_surface_caps(surface5, DDSCAPS_BACKBUFFER, placement, 0); + check_surface_caps(surface6, 0, placement, 0); + check_surface_caps(surface1, DDSCAPS_VISIBLE | DDSCAPS_PRIMARYSURFACE, placement, 0); + hr = IDirectDrawSurface_AddAttachedSurface(surface1, surface5); + ok(hr == DD_OK, "got %#lx.\n", hr); + check_surface_caps(surface1, DDSCAPS_FLIP | DDSCAPS_FRONTBUFFER | DDSCAPS_VISIBLE | DDSCAPS_PRIMARYSURFACE, placement, 0); + check_surface_caps(surface5, DDSCAPS_FLIP | DDSCAPS_BACKBUFFER, placement, 0); + check_surface_caps(surface6, 0, placement, 0); + hr = IDirectDrawSurface_AddAttachedSurface(surface5, surface6); + ok(hr == DD_OK, "got %#lx.\n", hr); + check_surface_caps(surface1, DDSCAPS_VISIBLE | DDSCAPS_PRIMARYSURFACE | DDSCAPS_FRONTBUFFER | DDSCAPS_FLIP, placement, 0); + check_surface_caps(surface5, DDSCAPS_FLIP | DDSCAPS_BACKBUFFER, placement, 0); + check_surface_caps(surface6, DDSCAPS_FLIP, placement, 0); + + caps.dwCaps = DDSCAPS_FLIP; + hr = IDirectDrawSurface_GetAttachedSurface(surface1, &caps, &tmp); + ok(hr == DD_OK, "got %#lx.\n", hr); + ok(tmp == surface5, "got %p, %p.\n", tmp, surface5); + IDirectDrawSurface_Release(tmp); + hr = IDirectDrawSurface_GetAttachedSurface(surface5, &caps, &tmp); + ok(hr == DD_OK, "got %#lx.\n", hr); + ok(tmp == surface6, "got %p, %p.\n", tmp, surface5); + IDirectDrawSurface_Release(tmp); + hr = IDirectDrawSurface_GetAttachedSurface(surface6, &caps, &tmp); + ok(hr == DD_OK, "got %#lx.\n", hr); + ok(tmp == surface1, "got %p, %p.\n", tmp, surface5); + IDirectDrawSurface_Release(tmp); + + hr = IDirectDrawSurface_DeleteAttachedSurface(surface1, 0, surface6); + ok(hr == DDERR_SURFACENOTATTACHED, "got %#lx.\n", hr); + hr = IDirectDrawSurface_DeleteAttachedSurface(surface6, 0, surface1); + ok(hr == DDERR_CANNOTDETACHSURFACE, "got %#lx.\n", hr); + ref = get_refcount((IUnknown *)surface5); + ok(ref == 2, "got %ld,\n", ref); + ref = get_refcount((IUnknown *)surface6); + ok(ref == 2, "got %ld,\n", ref); + check_surface_caps(surface1, DDSCAPS_VISIBLE | DDSCAPS_PRIMARYSURFACE | DDSCAPS_FRONTBUFFER | DDSCAPS_FLIP, placement, 0); + check_surface_caps(surface5, DDSCAPS_FLIP | DDSCAPS_BACKBUFFER, placement, 0); + check_surface_caps(surface6, DDSCAPS_FLIP, placement, 0); + hr = IDirectDrawSurface_DeleteAttachedSurface(surface1, 0, surface5); + ok(hr == DD_OK, "got %#lx.\n", hr); + ref = get_refcount((IUnknown *)surface5); + ok(ref == 1, "got %ld,\n", ref); + ref = get_refcount((IUnknown *)surface6); + ok(ref == 2, "got %ld,\n", ref); + check_surface_caps(surface1, DDSCAPS_VISIBLE | DDSCAPS_PRIMARYSURFACE | DDSCAPS_FRONTBUFFER | DDSCAPS_FLIP, placement, 0); + check_surface_caps(surface5, DDSCAPS_BACKBUFFER, placement, 0); + check_surface_caps(surface6, DDSCAPS_FLIP | DDSCAPS_BACKBUFFER, placement, 0); + + hr = IDirectDrawSurface_GetAttachedSurface(surface1, &caps, &tmp); + ok(hr == DD_OK, "got %#lx.\n", hr); + ok(tmp == surface6, "got %p, %p.\n", tmp, surface6); + IDirectDrawSurface_Release(surface6); + + hr = IDirectDrawSurface_GetAttachedSurface(surface5, &caps, &tmp); + ok(hr == DDERR_NOTFOUND, "got %#lx.\n", hr); + + hr = IDirectDrawSurface_DeleteAttachedSurface(surface5, 0, surface6); + ok(hr == DDERR_SURFACENOTATTACHED, "got %#lx.\n", hr); + ref = get_refcount((IUnknown *)surface6); + ok(ref == 2, "got %ld,\n", ref); + + hr = IDirectDrawSurface_DeleteAttachedSurface(surface1, 0, surface6); + ok(hr == DD_OK, "got %#lx.\n", hr); + check_surface_caps(surface6, 0, placement, 0); + + hr = IDirectDrawSurface_GetAttachedSurface(surface1, &caps, &tmp); + ok(hr == DDERR_NOTFOUND, "got %#lx.\n", hr); + + ref = IDirectDrawSurface_Release(surface1); + ok(!ref, "got %ld\n", ref); + check_surface_caps(surface6, 0, placement, 0); + + ref = IDirectDrawSurface_Release(surface6); + ok(!ref, "got %ld\n", ref); + ref = IDirectDrawSurface_Release(surface5); + ok(!ref, "got %ld\n", ref); IDirectDrawSurface_Release(surface4); IDirectDrawSurface_Release(surface3); IDirectDrawSurface_Release(surface2); - IDirectDrawSurface_Release(surface1); + + /* Test adding backbuffers to complex surface with auto created backbuffers. */ + caps.dwCaps = DDSCAPS_FLIP; + memset(&surface_desc, 0, sizeof(surface_desc)); + surface_desc.dwSize = sizeof(surface_desc); + surface_desc.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT; + surface_desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_FLIP; + surface_desc.dwBackBufferCount = 2; + hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &surface1, NULL); + ok(hr == DD_OK, "got %#lx.\n", hr); + + hr = IDirectDrawSurface_GetAttachedSurface(surface1, &caps, &backbuffer1); + ok(hr == DD_OK, "got %#lx.\n", hr); + ref = get_refcount((IUnknown *)backbuffer1); + ok(ref == 2, "got %ld.\n", ref); + + hr = IDirectDrawSurface_DeleteAttachedSurface(surface1, 0, surface1); + ok(hr == DDERR_SURFACENOTATTACHED, "got %#lx.\n", hr); + hr = IDirectDrawSurface_DeleteAttachedSurface(surface1, 0, backbuffer1); + ok(hr == DDERR_CANNOTDETACHSURFACE, "got %#lx.\n", hr); + + hr = IDirectDrawSurface_GetSurfaceDesc(surface1, &surface_desc); + ok(hr == DD_OK, "got %#lx.\n", hr); + surface_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; + surface_desc.ddsCaps.dwCaps = DDSCAPS_3DDEVICE; + surface_desc.dwWidth /= 2; + hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &backbuffer2, NULL); + ok(hr == DD_OK, "got %#lx.\n", hr); + + /* size mismatch */ + hr = IDirectDrawSurface_AddAttachedSurface(surface1, backbuffer2); + ok(hr == DDERR_CANNOTATTACHSURFACE, "got %#lx.\n", hr); + IDirectDrawSurface_Release(backbuffer2); + + hr = IDirectDrawSurface_GetSurfaceDesc(surface1, &surface_desc); + ok(hr == DD_OK, "got %#lx.\n", hr); + surface_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; + surface_desc.ddsCaps.dwCaps = DDSCAPS_3DDEVICE; + hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &backbuffer2, NULL); + ok(hr == DD_OK, "got %#lx.\n", hr); + + memset(&surface_desc, 0, sizeof(surface_desc)); + surface_desc.dwSize = sizeof(surface_desc); + surface_desc.dwFlags = DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT; + surface_desc.ddsCaps.dwCaps = DDSCAPS_ZBUFFER; + surface_desc.ddpfPixelFormat.dwSize = sizeof(surface_desc.ddpfPixelFormat); + surface_desc.ddpfPixelFormat.dwFlags = DDPF_ZBUFFER; + surface_desc.ddpfPixelFormat.dwZBufferBitDepth = 16; + surface_desc.ddpfPixelFormat.dwZBitMask = 0x0000ffff; + surface_desc.dwWidth = registry_mode.dmPelsWidth; + surface_desc.dwHeight = registry_mode.dmPelsHeight; + hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &surface2, NULL); + ok(hr == D3D_OK, "Failed to create surface, hr %#lx.\n", hr); + + hr = IDirectDrawSurface_AddAttachedSurface(surface1, surface2); + ok(hr == DD_OK, "got %#lx.\n", hr); + ref = get_refcount((IUnknown *)surface1); + ok(ref == 1, "got %ld.\n", ref); + caps.dwCaps = DDSCAPS_ZBUFFER; + hr = IDirectDrawSurface_GetAttachedSurface(surface1, &caps, &tmp); + ok(hr == DD_OK, "got %#lx.\n", hr); + ok(tmp == surface2, "got %p, %p.\n", tmp, surface2); + IDirectDrawSurface_Release(tmp); + + caps.dwCaps = DDSCAPS_FLIP; + hr = IDirectDrawSurface_GetAttachedSurface(backbuffer1, &caps, &surface3); + ok(hr == DD_OK, "got %#lx.\n", hr); + + caps.dwCaps = DDSCAPS_COMPLEX; + hr = IDirectDrawSurface_GetAttachedSurface(backbuffer1, &caps, &tmp); + ok(hr == DD_OK, "got %#lx.\n", hr); + ok(tmp == surface3, "got %p, %p.\n", tmp, surface3); + IDirectDrawSurface_Release(tmp); + caps.dwCaps = DDSCAPS_ZBUFFER; + hr = IDirectDrawSurface_GetAttachedSurface(backbuffer1, &caps, &tmp); + ok(hr == DDERR_NOTFOUND, "got %#lx.\n", hr); + ref = IDirectDrawSurface_Release(surface3); + ok(ref == 1, "got %ld.\n", ref); + + ref = get_refcount((IUnknown *)backbuffer1); + ok(ref == 2, "got %ld.\n", ref); + ref = get_refcount((IUnknown *)backbuffer2); + ok(ref == 1, "got %ld.\n", ref); + hr = IDirectDrawSurface_AddAttachedSurface(surface1, backbuffer2); + ok(hr == DD_OK, "got %#lx.\n", hr); + ref = get_refcount((IUnknown *)backbuffer2); + ok(ref == 2, "got %ld.\n", ref); + ref = get_refcount((IUnknown *)backbuffer1); + ok(ref == 2, "got %ld.\n", ref); + check_surface_caps(surface1, DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_VISIBLE | DDSCAPS_FRONTBUFFER + | DDSCAPS_FLIP, placement, 3); + caps.dwCaps = DDSCAPS_FLIP; + hr = IDirectDrawSurface_GetAttachedSurface(backbuffer2, &caps, &tmp); + ok(hr == DD_OK, "got %#lx.\n", hr); + ok(tmp == backbuffer1, "got %p, %p.\n", backbuffer1, tmp); + ref = IDirectDrawSurface_Release(tmp); + ok(ref == 2, "got %ld.\n", ref); + + caps.dwCaps = DDSCAPS_PRIMARYSURFACE; + hr = IDirectDrawSurface_GetAttachedSurface(backbuffer2, &caps, &tmp); + ok(hr == DDERR_NOTFOUND, "got %#lx.\n", hr); + caps.dwCaps = DDSCAPS_FLIP; + + hr = IDirectDrawSurface_DeleteAttachedSurface(backbuffer2, 0, backbuffer1); + ok(hr == DDERR_CANNOTDETACHSURFACE, "got %#lx.\n", hr); + hr = IDirectDrawSurface_DeleteAttachedSurface(backbuffer1, 0, backbuffer2); + ok(hr == DDERR_SURFACENOTATTACHED, "got %#lx.\n", hr); + + ref = get_refcount((IUnknown *)backbuffer1); + ok(ref == 2, "got %ld.\n", ref); + hr = IDirectDrawSurface_DeleteAttachedSurface(surface1, 0, backbuffer2); + ok(hr == DD_OK, "got %#lx.\n", hr); + /* Looks like here is a ddraw bug here, when deleting a surface the next one in chain gets an extra reference. */ + ref = get_refcount((IUnknown *)backbuffer1); + todo_wine ok(ref == 3, "got %ld.\n", ref); + check_surface_caps(surface1, DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_VISIBLE | DDSCAPS_FRONTBUFFER + | DDSCAPS_FLIP, placement, 2); + check_surface_caps(backbuffer1, DDSCAPS_FLIP | DDSCAPS_BACKBUFFER | DDSCAPS_COMPLEX, placement, 0); + check_surface_caps(backbuffer2, DDSCAPS_3DDEVICE, placement, 0); + hr = IDirectDrawSurface_AddAttachedSurface(backbuffer1, backbuffer2); + ok(hr == DD_OK, "got %#lx.\n", hr); + check_surface_caps(backbuffer1, DDSCAPS_BACKBUFFER | DDSCAPS_FLIP | DDSCAPS_COMPLEX, placement, 0); + check_surface_caps(backbuffer2, DDSCAPS_3DDEVICE | DDSCAPS_FLIP, placement, 0); + check_surface_caps(surface1, DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_VISIBLE | DDSCAPS_FRONTBUFFER + | DDSCAPS_FLIP, placement, 3); + + hr = IDirectDrawSurface_AddAttachedSurface(surface1, backbuffer2); + ok(hr == DDERR_CANNOTATTACHSURFACE, "got %#lx.\n", hr); + hr = IDirectDrawSurface_AddAttachedSurface(backbuffer2, surface1); + ok(hr == DDERR_CANNOTATTACHSURFACE , "got %#lx.\n", hr); + hr = IDirectDrawSurface_AddAttachedSurface(backbuffer2, backbuffer1); + ok(hr == DDERR_CANNOTATTACHSURFACE, "got %#lx.\n", hr); + + hr = IDirectDrawSurface_DeleteAttachedSurface(backbuffer1, 0, backbuffer2); + ok(hr == DD_OK, "got %#lx.\n", hr); + check_surface_caps(surface1, DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_VISIBLE | DDSCAPS_FRONTBUFFER + | DDSCAPS_FLIP, placement, 2); + check_surface_caps(backbuffer1, DDSCAPS_BACKBUFFER | DDSCAPS_FLIP | DDSCAPS_COMPLEX, placement, 0); + check_surface_caps(backbuffer2, DDSCAPS_3DDEVICE, placement, 0); + + ref = IDirectDrawSurface_Release(backbuffer1); + todo_wine ok(ref == 2, "got %ld.\n", ref); + if (ref == 2) + ref = IDirectDrawSurface_Release(backbuffer1); + ref = IDirectDrawSurface_Release(surface1); + ok(!ref, "got %ld.\n", ref); + ref = IDirectDrawSurface_Release(backbuffer1); + ok(!ref, "got %ld.\n", ref); + ref = IDirectDrawSurface_Release(backbuffer2); + ok(!ref, "got %ld.\n", ref); + ref = IDirectDrawSurface_Release(surface2); + ok(!ref, "got %ld.\n", ref); /* Test depth surfaces of different sizes. */ memset(&surface_desc, 0, sizeof(surface_desc)); @@ -6087,6 +6571,10 @@ static void test_surface_attachment(void) ok(SUCCEEDED(hr), "Failed to attach surface, hr %#lx.\n", hr); refcount = get_refcount((IUnknown *)surface2); ok(refcount == 2, "Got unexpected refcount %lu.\n", refcount); + caps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + hr = IDirectDrawSurface_GetAttachedSurface(surface2, &caps, &tmp); + ok(hr == DDERR_NOTFOUND, "got %#lx.\n", hr); + hr = IDirectDrawSurface_AddAttachedSurface(surface1, surface2); ok(hr == DDERR_SURFACEALREADYATTACHED, "Got unexpected hr %#lx.\n", hr); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10992
From: Paul Gofman <pgofman@codeweavers.com> --- dlls/ddraw/surface.c | 16 ++++++++++++---- dlls/ddraw/tests/ddraw1.c | 2 +- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/dlls/ddraw/surface.c b/dlls/ddraw/surface.c index c48aeb7c839..5be55596128 100644 --- a/dlls/ddraw/surface.c +++ b/dlls/ddraw/surface.c @@ -6327,12 +6327,20 @@ static HRESULT ddraw_surface_reserve_memory(struct wined3d_texture *wined3d_text return hr; } +static BOOL force_3ddevice(struct ddraw *ddraw, const DDSURFACEDESC2 *desc, unsigned int surface_version) +{ + return surface_version == 1 && desc->ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE && !(ddraw->flags & DDRAW_NO3D); +} + static void wined3d_resource_desc_from_ddraw(struct ddraw *ddraw, - struct wined3d_resource_desc *wined3d_desc, const DDSURFACEDESC2 *desc) + struct wined3d_resource_desc *wined3d_desc, const DDSURFACEDESC2 *desc, unsigned int version) { - const DWORD caps = desc->ddsCaps.dwCaps; + DWORD caps = desc->ddsCaps.dwCaps; const DWORD caps2 = desc->ddsCaps.dwCaps2; + if (force_3ddevice(ddraw, desc, version)) + caps |= DDSCAPS_3DDEVICE; + wined3d_desc->resource_type = WINED3D_RTYPE_TEXTURE_2D; wined3d_desc->format = wined3dformat_from_ddrawformat(&desc->ddpfPixelFormat); wined3d_desc->multisample_type = WINED3D_MULTISAMPLE_NONE; @@ -6397,7 +6405,7 @@ static HRESULT ddraw_texture_init(struct ddraw_texture *texture, struct ddraw *d unsigned int i, j; HRESULT hr; - wined3d_resource_desc_from_ddraw(ddraw, &wined3d_desc, desc); + wined3d_resource_desc_from_ddraw(ddraw, &wined3d_desc, desc, texture->version); if (wined3d_desc.format == WINED3DFMT_UNKNOWN) { @@ -6976,7 +6984,7 @@ HRESULT ddraw_surface_create(struct ddraw *ddraw, const DDSURFACEDESC2 *surface_ if (desc->ddsCaps.dwCaps & DDSCAPS_ZBUFFER) bind_flags |= WINED3D_BIND_DEPTH_STENCIL; - else if (desc->ddsCaps.dwCaps & DDSCAPS_3DDEVICE) + else if ((desc->ddsCaps.dwCaps & DDSCAPS_3DDEVICE) || force_3ddevice(ddraw, desc, version)) bind_flags |= WINED3D_BIND_RENDER_TARGET; if (!(ddraw->flags & DDRAW_NO3D) && SUCCEEDED(hr = wined3d_check_device_format(ddraw->wined3d, diff --git a/dlls/ddraw/tests/ddraw1.c b/dlls/ddraw/tests/ddraw1.c index 6ce50821590..37af1eebef8 100644 --- a/dlls/ddraw/tests/ddraw1.c +++ b/dlls/ddraw/tests/ddraw1.c @@ -5705,7 +5705,7 @@ static void test_flip(void) if (have_3ddevice) { hr = IDirectDrawSurface_QueryInterface(backbuffer1, &IID_IDirect3DHALDevice, (void **)&device); - todo_wine ok(hr == DD_OK || broken(hr == E_FAIL), "got %#lx.\n", hr); + ok(hr == DD_OK || broken(hr == E_FAIL), "got %#lx.\n", hr); if (SUCCEEDED(hr)) IDirect3DDevice_Release(device); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10992
v2: * update dsurface tests so they don't hit broken() case on my Windows 11 due to unrelated reason (primary surface ends up with a size different from fullscreen which fails AddAttachedSurface); * add caps checks to BackBufferAttachmentFlipTest() so it is clear how attaching and detaching flags change it and why Flip succeeds or fails; * fix some things in attachment flags management "ddraw: Allow attaching flip chain surfaces." so both new and old dsurface tests pass; * remove now suceeding todo_wine in dsurface.c, d3d.c and visual.c tests. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10992#note_141286
Tests pass on Helen. I still need to review, though. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10992#note_141351
Oh yeah, while there is not so much code changes somehow I was breaking my head for a while how that logic and tests can possibly be fit in. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10992#note_141354
Surface attachment handling is always a bit of a pain, but I don't see anything obviously wrong with this, at least. I do kind of hate having both "is_root" and "is_chain_start", and I suspect that's going to end up being confusing. I haven't looked much into how hard it would be to avoid though. Perhaps different naming or some comments could help? Do we need "detach_clear_flags"? I.e., shouldn't the caps we remove depend purely on the state of the flip chain at detachment time, instead of on prior attachments and detachments? In terms of follow-up, this may be a good opportunity to fold BackBuffer3DAttachmentTest(), BackBufferAttachmentFlipTest(), and DX1_BackBufferFlipTest() into test_flip() and test_surface_attachment(), to the extent those don't already overlap. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10992#note_141416
I did a bit more testing and it looks like detach_clear_flags is needed, more so, when front / back flags are added at attach (without a complication of added flags on detach) whether it will be cleared or not depends on initial surface caps. So it seems to me, while the whole logic adding or removing those flags is convoluted and doesn't always make much sense to me, the invariant is that whatever flags were added through attach / detach mechanics they are going to be removed when the surface goes off the chain (which is currently not the case in my patch; it should or to detach_clear_flags in ddraw_surface_attach_surface and also exclude pre-existing flags from that). I am yet to finalize the change and merge the tests. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10992#note_141482
Do we have tests that attaching backbuffers is illegal in ddraw2/4/7? It would be nice to add them if not. 2/4: I apologize for not being able to think or read very well, but why do we need both is_root and is_chain_start? Why can't we just modify is_root? detach_clear_flags I don't like for reasons that are hard to describe. I think we can instead store the original flags (and I'd say "original_caps") and selectively restore those on detach? Does that work or am I missing something? 4/4: ``` + if (force_3ddevice(ddraw, desc, version)) + caps |= DDSCAPS_3DDEVICE; ``` I don't find this sort of thing very idiomatic; I would prefer to just do "if ((caps & 3DDEVICE) || (version == 1 && (caps & PRIMARYSURFACE)))". At that point it probably doesn't need a helper function either. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10992#note_141605
Do we have tests that attaching backbuffers is illegal in ddraw2/4/7? It would be nice to add them if not.
Yes, corresponding test_surface_attachment() in the later ddraw versions has attempts to attach surfaces the same way which are expected to fail.
I apologize for not being able to think or read very well, but why do we need both is_root and is_chain_start? Why can't we just modify is_root?
'is_root' is what distinguishes app-created surface (complex surface root in a general case) with auto-created attachments. is_chain_start designates the first surface in the constructed chain (which is always is_root but with these additions there can be multiple is_root surfaces). I didn't find a reasonable way to get rid of that which would look like a simplification to me. The flip chain start check is needed in ddraw_surface_cleanup (to know where to stop, we do not want to circle there to the beginning of the chain; if there is another 'is_root' surface it will be deconstructed when released by app). Then, DeleteAttachedSurface is not allowing to delete the linked chain list (returning DDERR_CANNOTDETACHSURFACE; the tests suggest that DDERR_SURFACENOTATTACHED takes precedence and this error code suggests that native ddraw also recognizes chain start as attached from the last element in chain but explicitly refuses to detach it). So it seems to me we really need to know the star t of the chain. That can of course be done differently like storing (yet another) 'first_attached' pointer equivalent but that doesn't seem nicer to me? In the next version I renamed it to is_flip_chain_start.
detach_clear_flags I don't like for reasons that are hard to describe. I think we can instead store the original flags (and I'd say "original_caps") and selectively restore those on detach? Does that work or am I missing something?
Yes, I have that in the next version which I am about to send soon (except for CAPS_FRONTBUFFER flag is not preserved but I am just excluding it from restore flags).
I don't find this sort of thing very idiomatic; I would prefer to just do "if ((caps & 3DDEVICE) || (version == 1 && (caps & PRIMARYSURFACE)))". At that point it probably doesn't need a helper function either.
There is also ddraw-\>flags & DDRAW_NO3D check? It seems like a rather convoluted condition, do you really want to duplicate it? We might avoid fixing up 'caps' variable ofc in favour of explicit use of `force_3ddevice() in wined3d_resource_desc_from_ddraw().` -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10992#note_141611
I do kind of hate having both "is_root" and "is_chain_start", and I suspect that's going to end up being confusing. I haven't looked much into how hard it would be to avoid though. Perhaps different naming or some comments could help?
So one thought I had about this is to replace the "is_root" flag with an "is_implicit" flag, somewhat analogous to how we have WINED3D_SWAPCHAIN_IMPLICIT for implicit swapchains in wined3d. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10992#note_141613
So one thought I had about this is to replace the "is_root" flag with an "is_implicit" flag, somewhat analogous to how we have WINED3D_SWAPCHAIN_IMPLICIT for implicit swapchains in wined3d.
I thought of that but didn't come up with particularly great name. I will rename is_root to is_implicit (which is !is_root). -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10992#note_141614
I do kind of hate having both "is_root" and "is_chain_start", and I suspect that's going to end up being confusing. I haven't looked much into how hard it would be to avoid though. Perhaps different naming or some comments could help?
So one thought I had about this is to replace the "is_root" flag with an "is_implicit" flag, somewhat analogous to how we have WINED3D_SWAPCHAIN_IMPLICIT for implicit swapchains in wined3d.
It might be a matter of taste, but I find this even less clear than the is_root/is_chain_start names... I'll think about it for a minute. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10992#note_141621
participants (4)
-
Elizabeth Figura (@zfigura) -
Henri Verbeet (@hverbeet) -
Paul Gofman -
Paul Gofman (@gofman)