From: Paul Gofman <pgofman@codeweavers.com> --- dlls/ddraw/ddraw_private.h | 2 + dlls/ddraw/surface.c | 156 ++++++++++++++++++++++++++++++++++-- dlls/ddraw/tests/d3d.c | 6 +- dlls/ddraw/tests/dsurface.c | 18 ++--- dlls/ddraw/tests/visual.c | 4 +- 5 files changed, 164 insertions(+), 22 deletions(-) diff --git a/dlls/ddraw/ddraw_private.h b/dlls/ddraw/ddraw_private.h index 01d3b84b208..7baf43a3f24 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 attach_saved_caps; /* 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. @@ -200,6 +201,7 @@ struct ddraw_surface #define MAX_COMPLEX_ATTACHED 6 struct ddraw_surface *complex_array[MAX_COMPLEX_ATTACHED]; unsigned int is_implicit : 1; + unsigned int is_flip_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 76d7c922eb0..b3cc5c684e5 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_implicit + && !(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_implicit && !surf->is_flip_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_implicit) { @@ -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)) @@ -1960,6 +1981,22 @@ static HRESULT WINAPI DECLSPEC_HOTPATCH ddraw_surface2_Blt(IDirectDrawSurface2 * src ? &src->IDirectDrawSurface_iface : NULL, src_rect, flags, fx); } +static void surface_update_backbuffer_count(struct ddraw_surface *surface, int inc) +{ + struct ddraw_surface *s; + + s = surface; + do + { + if (!s->is_implicit && s->surface_desc.dwFlags & DDSD_BACKBUFFERCOUNT) + { + s->surface_desc.dwBackBufferCount += inc; + break; + } + s = s->complex_array[0]; + } while (s != surface); +} + /***************************************************************************** * IDirectDrawSurface7::AddAttachedSurface * @@ -1981,15 +2018,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 +2052,55 @@ 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->attach_saved_caps = Surf->surface_desc.ddsCaps.dwCaps & ~DDSCAPS_FRONTBUFFER; + Surf->is_flip_chain_start = 0; + Surf->surface_desc.ddsCaps.dwCaps |= DDSCAPS_FLIP; + if (!This->complex_array[0]) + { + This->is_flip_chain_start = 1; + This->complex_array[0] = Surf; + Surf->complex_array[0] = This; + This->attach_saved_caps = This->surface_desc.ddsCaps.dwCaps & ~DDSCAPS_FRONTBUFFER; + This->surface_desc.ddsCaps.dwCaps |= DDSCAPS_FLIP; + if (!(This->surface_desc.ddsCaps.dwCaps & (DDSCAPS_FRONTBUFFER | DDSCAPS_BACKBUFFER))) + This->surface_desc.ddsCaps.dwCaps |= DDSCAPS_FRONTBUFFER; + + if (This->surface_desc.ddsCaps.dwCaps & DDSCAPS_BACKBUFFER) + Surf->surface_desc.ddsCaps.dwCaps |= DDSCAPS_FRONTBUFFER; + else + Surf->surface_desc.ddsCaps.dwCaps |= DDSCAPS_BACKBUFFER; + } + else + { + Surf->complex_array[0] = This->complex_array[0]; + This->complex_array[0] = Surf; + } + surface_update_backbuffer_count(This, 1); + wined3d_mutex_unlock(); + return DD_OK; + } + /* Check if the surface is already attached somewhere */ if (Surf->next_attached || Surf->first_attached != Surf) { @@ -2095,8 +2181,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; @@ -2188,7 +2277,57 @@ static HRESULT ddraw_surface_delete_attached_surface(struct ddraw_surface *surfa TRACE("surface %p, attachment %p, detach_iface %p.\n", surface, attachment, detach_iface); + if (!attachment) + return DDERR_CANNOTDETACHSURFACE; + wined3d_mutex_lock(); + + if (surface_attachable_for_flip(attachment)) + { + struct ddraw_surface *next; + 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_implicit || attachment->is_flip_chain_start) + { + wined3d_mutex_unlock(); + WARN("trying to detach non-root attachment %p.\n", attachment); + return DDERR_CANNOTDETACHSURFACE; + } + assert(attachment->complex_array[0]); + surface_update_backbuffer_count(surface, -1); + next = surface->complex_array[0] = attachment->complex_array[0]; + if (next == surface) + { + surface->is_flip_chain_start = 0; + surface->complex_array[0] = NULL; + surface->surface_desc.ddsCaps.dwCaps = surface->attach_saved_caps; + } + else + { + next->surface_desc.ddsCaps.dwCaps &= ~(DDSCAPS_FRONTBUFFER | DDSCAPS_BACKBUFFER); + if (attachment->surface_desc.ddsCaps.dwCaps & DDSCAPS_FRONTBUFFER) + { + surface->surface_desc.ddsCaps.dwCaps |= DDSCAPS_BACKBUFFER; + next->surface_desc.ddsCaps.dwCaps |= DDSCAPS_FRONTBUFFER; + } + else + { + next->surface_desc.ddsCaps.dwCaps |= DDSCAPS_BACKBUFFER; + } + } + attachment->surface_desc.ddsCaps.dwCaps = attachment->attach_saved_caps; + 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(); @@ -6987,6 +7126,7 @@ HRESULT ddraw_surface_create(struct ddraw *ddraw, const DDSURFACEDESC2 *surface_ unsigned int count = desc->dwBackBufferCount; struct ddraw_surface *last = root; + root->is_flip_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 e65160034f6..ad6eddb5ad2 100644 --- a/dlls/ddraw/tests/dsurface.c +++ b/dlls/ddraw/tests/dsurface.c @@ -1720,15 +1720,15 @@ 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)) { /* Try flipping the surfaces */ hr = IDirectDrawSurface_Flip(surface1, NULL, DDFLIP_WAIT); - todo_wine ok(hr == DD_OK, "Got hr %#lx.\n", hr); + 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); + ok(hr == DDERR_NOTFLIPPABLE, "Got hr %#lx.\n", hr); /* Try the reverse without detaching first */ hr = IDirectDrawSurface_AddAttachedSurface(surface2, surface1); @@ -1737,15 +1737,15 @@ static void BackBufferAttachmentFlipTest(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), + 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); + 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); + ok(hr == DDERR_NOTFLIPPABLE, "Got hr %#lx.\n", hr); /* Try to detach reversed */ hr = IDirectDrawSurface_DeleteAttachedSurface(surface1, 0, surface2); @@ -1755,15 +1755,15 @@ static void BackBufferAttachmentFlipTest(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), + 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); + 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); + 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); 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