One way to solve this would be to have wined3d handle the reference counting for d3d7/8/9 as well, so that their AddRef/Release calls just call AddRef/Release on the wined3d object they wrap. That would also mean we need to pass a pointer to the d3d7/8/9 object's cleanup function. (Releasing a d3d9 surface to 0 should cause a d3d9 texture to be freed). Note that for creating a texture, currently a similair construction is used, in that IWineD3DDeviceImpl_CreateTexture gets passed a pointer to a d3d9 surface create function, in order to create the texture's surfaces.
I have had some reference counting problems with ddraw too, but they were more simple(Mainly, that needed WineD3D objects had no ddraw counterpart, or that WineD3D destroys the ddraw parents, although they were still needed).
DDraw doesn't use containers, instead it has surface attachments and complex surfaces, with some reference counting difficulties. My solution was to handle that things in ddraw.
Could it be that d3d9 surfaces and textures are the same objects on windows? What happens if you QueryInterface the surface for the texture GUID?
Obviously doing all that would be quite a change in the way d3d is setup in wine, and would also have some implications for the d3d7 and d3d8 rewrites. I'm wondering if this solution would be acceptable, and whether perhaps there are other, better solutions. Anyone care to comment?
It might be interesting if there are more such refcount connections. For textures, my parent/child connections are:
DDraw surface 1 -> WineD3DTexture IParent -> WineD3DSurface 1 DDraw surface 2 -> WineD3DSurface 2 etc
Directdraw textures are the same objects as directdraw surfaces, so they share the refcount.
First, I create a ddraw surface, and create a WineD3DTexture for it. The surface is the child for texture. WineD3D calls my surface creation callback, which creates a WineD3DSurface for the ddraw surface on the first call, and a Parent object for the WineD3Dsurface. The IParent objects only purpose is to release it's child when it's destroyed. For other levels, I create a new ddraw surface, which is the parent of the new WineD3D surface, and attach it to the first ddraw surface. The attachment list is managed in ddraw.
When the app releases the compound, it (hopefully) calls the release method of the first surface. This releases the WineD3D texture, which causes a release to the IParent object and the further ddraw surfaces. The parent and the ddraw surfaces release the Wined3d surfaces. (That code works for 1 level textures, I haven't yet tried an app which uses multiple levels, or I silently broke that functionality)
My suggestion is to keep d3dX specific refcounting in ddraw / d3d8 / d3d9, and use the WineD3D refcounts for wined3d internal things. A quick guessis to seperate the D3D9 release code from the cleanup code, instead of doing this in WineD3D.
What happens if a texture has 2 surface levels? A GetSurfaceLevel for the first level AddRefs both the texture and the surface. A GetSurfaceLevel for the secound addrefs the texture and the 2nd surface, but what happens to the first one? A Release() of the first surface Releases() the texture, what happens to the secound surface?
Stefan