[PATCH 0/3] MR10992: ddraw: Allow attaching flip chain surfaces surfaces (for v1 surfaces).
From: Paul Gofman <pgofman@codeweavers.com> --- dlls/ddraw/ddraw_private.h | 1 + dlls/ddraw/surface.c | 127 ++++++++++++++++++++++++++++++++++--- 2 files changed, 120 insertions(+), 8 deletions(-) diff --git a/dlls/ddraw/ddraw_private.h b/dlls/ddraw/ddraw_private.h index f2c99a30d57..7ea9323d07b 100644 --- a/dlls/ddraw/ddraw_private.h +++ b/dlls/ddraw/ddraw_private.h @@ -203,6 +203,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..6e25d2d01f2 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,49 @@ 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->surface_desc.ddsCaps.dwCaps |= DDSCAPS_FLIP; + This->surface_desc.ddsCaps.dwCaps |= DDSCAPS_FLIP; + 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; + } + if (!This->complex_array[0]) + This->is_chain_start = 1; + 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 +2159,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 +2256,49 @@ 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]; + attachment->surface_desc.ddsCaps.dwCaps &= ~DDSCAPS_FLIP; + attachment->complex_array[0] = NULL; + IUnknown_Release(detach_iface); + if (surface->complex_array[0] == surface) + { + attachment->surface_desc.ddsCaps.dwCaps &= ~(DDSCAPS_FRONTBUFFER | DDSCAPS_BACKBUFFER); + surface->is_chain_start = 0; + surface->complex_array[0] = NULL; + surface->surface_desc.ddsCaps.dwCaps &= ~DDSCAPS_FLIP; + } + else if (!(surface->complex_array[0]->surface_desc.ddsCaps.dwCaps & (DDSCAPS_FRONTBUFFER | DDSCAPS_BACKBUFFER))) + { + surface->complex_array[0]->surface_desc.ddsCaps.dwCaps |= DDSCAPS_BACKBUFFER; + } + + attachment->attached_iface = NULL; + wined3d_mutex_unlock(); + return DD_OK; + } + if (!attachment || (attachment->first_attached != surface) || (attachment == surface) ) { wined3d_mutex_unlock(); @@ -6986,6 +7096,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) { -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10992
From: Paul Gofman <pgofman@codeweavers.com> --- dlls/ddraw/tests/ddraw1.c | 548 +++++++++++++++++++++++++++++++++++--- 1 file changed, 514 insertions(+), 34 deletions(-) diff --git a/dlls/ddraw/tests/ddraw1.c b/dlls/ddraw/tests/ddraw1.c index 007daf29407..e8e66a9b26c 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,242 @@ 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); + 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); + + 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 +6563,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 6e25d2d01f2..27712ab30f3 100644 --- a/dlls/ddraw/surface.c +++ b/dlls/ddraw/surface.c @@ -6317,12 +6317,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; @@ -6387,7 +6395,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) { @@ -6966,7 +6974,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 e8e66a9b26c..64cd711339a 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
That helps Warhammer: Dark Omen (classic) which creates primary surface (without DDSDCAPS_3DDEVICE) and backbuffer (with DDSDCAPS_3DDEVICE) separately and then attaches back to front. Such creation of separate backbuffer and adding that to another surface is only supported on v1 surfaces. Then, once such attach is supported, the things get broken after \_Flip for 3d device because it primary surface doesn't have 3DDEVICE flag and backing wined3d texture is not suitable as RT (while Flip swaps underlying wined3d texture). The last patch fixes that part by assuming 3DDEVICE on v1 primary surfaces for wined3d texture creation. I spent some time trying to fit these (and some extra ad-hoc) tests in a different potentially more straightforward way. I considered getting rid of either complex_array (in favour of adding all possible surfaces to the 'next_attached' chain) as well as instead trying to keep only one complex_array or its analogue. I didn't find a way to reasonably do it, it looks like the current structure represents the specifics of flip chain and other attached surfaces rather well. E. g., trying to keep 'only AddAttachSurface to next_attached chain and keep complex_array for non-root generated surfaces) breaks various aspects, e. g.: * the search through that chain doesn't wrap back to the first element (and that looks correct as far as Z buffers are concerned); * EnumAttachedSurface() should enumerate only the next flip chain member (or rollback to the first), the surfaces from attached chain are enumerated / searched down the chain. So it looks most straightforward to keep the structure (which makes full sense on later ddraw as flip chain can only be auto-constructed and complex surface creation) and fit in AddAttachSurface for flip chain images into that. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10992#note_141281
participants (2)
-
Paul Gofman -
Paul Gofman (@gofman)