ddraw: Complex surfaces form a tree.

This commit is contained in:
Stefan Dösinger 2007-04-27 15:31:12 +02:00 committed by Alexandre Julliard
parent 87544190a5
commit 9e3e799ca3
5 changed files with 99 additions and 77 deletions

View file

@ -1797,9 +1797,6 @@ IDirectDrawImpl_CreateNewSurface(IDirectDrawImpl *This,
(*ppSurf)->next_attached = NULL;
(*ppSurf)->first_attached = *ppSurf;
(*ppSurf)->next_complex = NULL;
(*ppSurf)->first_complex = *ppSurf;
/* Needed to re-create the surface on an implementation change */
(*ppSurf)->ImplType = ImplType;
@ -1958,10 +1955,10 @@ CreateAdditionalSurfaces(IDirectDrawImpl *This,
{
UINT i, level = 0;
HRESULT hr;
IDirectDrawSurfaceImpl *last = root;
for(i = 0; i < count; i++)
{
IDirectDrawSurfaceImpl *object2 = NULL;
IDirectDrawSurfaceImpl *iterator;
/* increase the mipmap level, but only if a mipmap is created
* In this case, also halve the size
@ -1982,12 +1979,9 @@ CreateAdditionalSurfaces(IDirectDrawImpl *This,
return hr;
}
/* Add the new surface to the complex attachment list */
object2->first_complex = root;
object2->next_complex = NULL;
iterator = root;
while(iterator->next_complex) iterator = iterator->next_complex;
iterator->next_complex = object2;
/* Add the new surface to the complex attachment array */
last->complex_array[0] = object2;
last = object2;
/* Remove the (possible) back buffer cap from the new surface description,
* because only one surface in the flipping chain is a back buffer, one
@ -2306,6 +2300,7 @@ IDirectDrawImpl_CreateSurface(IDirectDraw7 *iface,
ERR("IDirectDrawImpl_CreateNewSurface failed with %08x\n", hr);
return hr;
}
object->is_complex_root = TRUE;
*Surf = ICOM_INTERFACE(object, IDirectDrawSurface7);

View file

@ -232,8 +232,18 @@ struct IDirectDrawSurfaceImpl
/* This implementation handles attaching surfaces to other surfaces */
IDirectDrawSurfaceImpl *next_attached;
IDirectDrawSurfaceImpl *first_attached;
IDirectDrawSurfaceImpl *next_complex;
IDirectDrawSurfaceImpl *first_complex;
/* 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.
* Only the cube texture root has 6 surfaces attached, which then have a normal mipmap chain attached
* to them. So hardcode the array to 6, a dynamic array or a list would be an overkill.
*/
#define MAX_COMPLEX_ATTACHED 6
IDirectDrawSurfaceImpl *complex_array[MAX_COMPLEX_ATTACHED];
/* You can't traverse the tree upwards. Only a flag for Surface::Release because its needed there,
* but no pointer to prevent temptations to traverse it in the wrong direction.
*/
BOOL is_complex_root;
/* Surface description, for GetAttachedSurface */
DDSURFACEDESC2 surface_desc;

View file

@ -379,11 +379,13 @@ IDirect3DDeviceImpl_7_Release(IDirect3DDevice7 *iface)
HeapFree(GetProcessHeap(), 0, This->Handles);
TRACE("Releasing target %p %p\n", This->target, This->ddraw->d3d_target);
/* Release the render target and the WineD3D render target
* (See IDirect3D7::CreateDevice for more comments on this)
*/
IDirectDrawSurface7_Release(ICOM_INTERFACE(This->target, IDirectDrawSurface7));
IDirectDrawSurface7_Release(ICOM_INTERFACE(This->ddraw->d3d_target,IDirectDrawSurface7));
TRACE("Target release done\n");
This->ddraw->d3ddevice = NULL;
@ -391,6 +393,7 @@ IDirect3DDeviceImpl_7_Release(IDirect3DDevice7 *iface)
HeapFree(GetProcessHeap(), 0, This);
}
TRACE("Done\n");
return ref;
}

View file

@ -785,7 +785,7 @@ DestroyCallback(IDirectDrawSurface7 *surf,
* part of a complex compound. They will get released when destroying
* the root
*/
if( (Impl->first_complex != Impl) || (Impl->first_attached != Impl) )
if( (!Impl->is_complex_root) || (Impl->first_attached != Impl) )
return DDENUMRET_OK;
/* Skip our depth stencil surface, it will be released with the render target */
if( Impl == ddraw->DepthStencilBuffer)

View file

@ -266,6 +266,8 @@ static void IDirectDrawSurfaceImpl_Destroy(IDirectDrawSurfaceImpl *This)
* capabilities of the WineD3DDevice are uninitialized, which causes the
* swapchain to be released.
*
* When a complex sublevel falls to ref zero, then this is ignored.
*
* Returns:
* The new refcount
*
@ -284,11 +286,12 @@ IDirectDrawSurfaceImpl_Release(IDirectDrawSurface7 *iface)
IDirectDrawSurfaceImpl *surf;
IDirectDrawImpl *ddraw;
IUnknown *ifaceToRelease = This->ifaceToRelease;
int i;
/* Complex attached surfaces are destroyed implicitely when the root is released */
if(This->first_complex != This)
if(!This->is_complex_root)
{
WARN("(%p) Attempt to destroy a surface that is attached to a complex root %p\n", This, This->first_complex);
WARN("(%p) Attempt to destroy a surface that is not a complex root\n", This);
return ref;
}
ddraw = This->ddraw;
@ -345,17 +348,33 @@ IDirectDrawSurfaceImpl_Release(IDirectDrawSurface7 *iface)
* The same applies for textures without an
* IWineD3DTexture object attached
*/
surf = This;
while(surf)
{
IParent *Parent;
IParent *Parent;
IWineD3DSurface_GetParent(surf->WineD3DSurface,
(IUnknown **) &Parent);
IParent_Release(Parent); /* For the getParent */
IParent_Release(Parent); /* To release it */
surf = surf->next_complex;
for(i = 0; i < MAX_COMPLEX_ATTACHED; i++)
{
if(This->complex_array[i])
{
/* Only the topmost level can have more than 1 surfaces in the complex
* attachment array(Cube texture roots), for all others there is only
* one
*/
surf = This->complex_array[i];
while(surf)
{
IWineD3DSurface_GetParent(surf->WineD3DSurface,
(IUnknown **) &Parent);
IParent_Release(Parent); /* For the getParent */
IParent_Release(Parent); /* To release it */
surf = surf->complex_array[0];
}
}
}
/* Now the top-level surface */
IWineD3DSurface_GetParent(This->WineD3DSurface,
(IUnknown **) &Parent);
IParent_Release(Parent); /* For the getParent */
IParent_Release(Parent); /* To release it */
}
/* The refcount test shows that the palette is detached when the surface is destroyed */
@ -363,15 +382,26 @@ IDirectDrawSurfaceImpl_Release(IDirectDrawSurface7 *iface)
NULL);
/* Loop through all complex attached surfaces,
* and destroy them
* and destroy them.
*
* Yet again, only the root can have more than one complexly attached surface, all the others
* have a total of one;
*/
while( (surf = This->next_complex) )
for(i = 0; i < MAX_COMPLEX_ATTACHED; i++)
{
This->next_complex = surf->next_complex; /* Unchain it from the complex listing */
IDirectDrawSurfaceImpl_Destroy(surf); /* Destroy it */
if(!This->complex_array[i]) break;
surf = This->complex_array[i];
This->complex_array[i] = NULL;
while(surf)
{
IDirectDrawSurfaceImpl *destroy = surf;
surf = surf->complex_array[0]; /* Iterate through the "tree" */
IDirectDrawSurfaceImpl_Destroy(destroy); /* Destroy it */
}
}
/* Destroy the surface.
/* Destroy the root surface.
*/
IDirectDrawSurfaceImpl_Destroy(This);
@ -388,13 +418,14 @@ IDirectDrawSurfaceImpl_Release(IDirectDrawSurface7 *iface)
* Returns an attached surface with the requested caps. Surface attachment
* and complex surfaces are not clearly described by the MSDN or sdk,
* so this method is tricky and likely to contain problems.
* This implementation searches the complex chain first, then the
* attachment chain, and then it checks if the caps match to itself.
* This implementation searches the complex list first, then the
* attachment chain.
*
* The chains are searched from This down to the last surface in the chain,
* not from the first element in the chain. The first surface found is
* returned. The MSDN says that this method fails if more than one surface
* matches the caps, but apparently this is incorrect.
* matches the caps, but it is not sure if that is right. The attachment
* structure may not even allow two matching surfaces.
*
* The found surface is AddRef-ed before it is returned.
*
@ -416,6 +447,7 @@ IDirectDrawSurfaceImpl_GetAttachedSurface(IDirectDrawSurface7 *iface,
ICOM_THIS_FROM(IDirectDrawSurfaceImpl, IDirectDrawSurface7, iface);
IDirectDrawSurfaceImpl *surf;
DDSCAPS2 our_caps;
int i;
TRACE("(%p)->(%p,%p)\n", This, Caps, Surface);
@ -431,11 +463,11 @@ IDirectDrawSurfaceImpl_GetAttachedSurface(IDirectDrawSurface7 *iface,
TRACE("(%p): Looking for caps: %x,%x,%x,%x\n", This, our_caps.dwCaps, our_caps.dwCaps2, our_caps.dwCaps3, our_caps.dwCaps4); /* FIXME: Better debugging */
/* First, look at the complex chain */
surf = This;
while( (surf = surf->next_complex) )
for(i = 0; i < MAX_COMPLEX_ATTACHED; i++)
{
surf = This->complex_array[i];
if(!surf) break;
if (TRACE_ON(ddraw))
{
TRACE("Surface: (%p) caps: %x,%x,%x,%x\n", surf,
@ -451,8 +483,7 @@ IDirectDrawSurfaceImpl_GetAttachedSurface(IDirectDrawSurface7 *iface,
/* MSDN: "This method fails if more than one surface is attached
* that matches the capabilities requested."
*
* The mipmap demo of the DirectX7 sdk shows what to do here:
* apparently apps expect the first found surface to be returned.
* Not sure how to test this.
*/
TRACE("(%p): Returning surface %p\n", This, surf);
@ -480,13 +511,6 @@ IDirectDrawSurfaceImpl_GetAttachedSurface(IDirectDrawSurface7 *iface,
if (((surf->surface_desc.ddsCaps.dwCaps & our_caps.dwCaps) == our_caps.dwCaps) &&
((surf->surface_desc.ddsCaps.dwCaps2 & our_caps.dwCaps2) == our_caps.dwCaps2)) {
/* MSDN: "This method fails if more than one surface is attached
* that matches the capabilities requested."
*
* The mipmap demo of the DirectX7 sdk shows what to do here:
* apparently apps expect the first found surface to be returned.
*/
TRACE("(%p): Returning surface %p\n", This, surf);
*Surface = ICOM_INTERFACE(surf, IDirectDrawSurface7);
IDirectDrawSurface7_AddRef(*Surface);
@ -494,22 +518,6 @@ IDirectDrawSurfaceImpl_GetAttachedSurface(IDirectDrawSurface7 *iface,
}
}
/* Is this valid? */
#if 0
if (((This->surface_desc.ddsCaps.dwCaps & our_caps.dwCaps) == our_caps.dwCaps) &&
((This->surface_desc.ddsCaps.dwCaps2 & our_caps.dwCaps2) == our_caps.dwCaps2) &&
This == This->first_complex)
{
TRACE("(%p): Returning surface %p\n", This, This);
*Surface = ICOM_INTERFACE(This, IDirectDrawSurface7);
IDirectDrawSurface7_AddRef(*Surface);
return DD_OK;
}
#endif
/* What to do here? Continue with the surface root?? */
TRACE("(%p) Didn't find a valid surface\n", This);
return DDERR_NOTFOUND;
}
@ -737,9 +745,12 @@ IDirectDrawSurfaceImpl_Blt(IDirectDrawSurface7 *iface,
/*****************************************************************************
* IDirectDrawSurface7::AddAttachedSurface
*
* Attaches a surface to another surface. Surface attachments are
* incorrectly described in the SDK and the MSDN, and this method
* is prone to bugs. The surface that is attached is AddRef-ed.
* Attaches a surface to another surface. How the surface attachments work
* is not totally understood yet, and this method is prone to problems.
* he surface that is attached is AddRef-ed.
*
* Tests with complex surfaces suggest that the surface attachments form a
* tree, but no method to test this has been found yet.
*
* The attachment list consists of a first surface (first_attached) and
* for each surface a pointer to the next attached surface (next_attached).
@ -747,22 +758,20 @@ IDirectDrawSurfaceImpl_Blt(IDirectDrawSurface7 *iface,
* first_attached points to the surface itself. A surface that has
* no successors in the chain has next_attached set to NULL.
*
* Newly attached surfaces are attached right after the root surface. The
* complex chains are handled separately in a similar chain, with
* first_complex and next_complex. If a surface is attached to a complex
* surface compound, it's attached to the surface that the app requested,
* not the complex root. See GetAttachedSurface for a description
* how surfaces are found.
* Newly attached surfaces are attached right after the root surface.
* If a surface is attached to a complex surface compound, it's attached to
* the surface that the app requested, not the complex root. See
* GetAttachedSurface for a description how surfaces are found.
*
* 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, but there's no code yet
* to activate them. Mipmaps could be tricky to activate in WineD3D.
* Back buffers should work in 2D mode, but they are not tested.
* Rendering to the primary surface and switching between that and
* double buffering is not yet implemented in WineD3D, so for 3D it might
* have unexpected results.
* So far only Z-Buffer 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(Not sure if
* they can be attached at all). Rendering to the primary surface and
* switching between that and double buffering is not yet implemented in
* WineD3D, so for 3D it might have unexpected results.
*
* Params:
* Attach: Surface to attach to iface
@ -1222,6 +1231,7 @@ IDirectDrawSurfaceImpl_EnumAttachedSurfaces(IDirectDrawSurface7 *iface,
ICOM_THIS_FROM(IDirectDrawSurfaceImpl, IDirectDrawSurface7, iface);
IDirectDrawSurfaceImpl *surf;
DDSURFACEDESC2 desc;
int i;
/* Attached surfaces aren't handled in WineD3D */
TRACE("(%p)->(%p,%p)\n",This,context,cb);
@ -1229,8 +1239,11 @@ IDirectDrawSurfaceImpl_EnumAttachedSurfaces(IDirectDrawSurface7 *iface,
if(!cb)
return DDERR_INVALIDPARAMS;
for (surf = This->next_complex; surf != NULL; surf = surf->next_complex)
for(i = 0; i < MAX_COMPLEX_ATTACHED; i++)
{
surf = This->complex_array[i];
if(!surf) break;
IDirectDrawSurface7_AddRef(ICOM_INTERFACE(surf, IDirectDrawSurface7));
desc = surf->surface_desc;
/* check: != DDENUMRET_OK or == DDENUMRET_CANCEL? */
@ -2258,6 +2271,7 @@ IDirectDrawSurfaceImpl_SetPalette(IDirectDrawSurface7 *iface,
break;
}
TRACE("Setting palette on %p\n", attach);
IDirectDrawSurface7_SetPalette(attach,
Pal);
surf = ICOM_OBJECT(IDirectDrawSurfaceImpl, IDirectDrawSurface7, attach);