With the solution mentioned in my previous post, d3d7/8/9 objects would essentially not be real COM objects on their own, they would just wrap the relevant wined3d AddRef/Release functions, and not have refcounts of their own. Wined3d would be responsible for it's own refcounting, and cleaning up the d3d7/8/9 "object" as part of it's own cleanup. Separating the d3d9 cleanup and release code would obviously be part of that. Note that if we separate the cleanup and release code we can't just call the cleanup code from wined3d, because we don't know the type of the parent, all we have is an IUnknown pointer. Which is why we would have to pass a pointer to that function to wined3d during object creation.
I don't think that this will work for ddraw. The ddraw object connections are a little different from D3D9 / WineD3D. For example, in ddraw, the app creates a DirectDraw interface. This interface doesn't have any surfaces on it's own, the primary surfaces are created by the app. When a Direct3DDevice is created, the surfaces already exist. In D3D9, creating the Direct3DDevice automatically creates the surfaces. There's no Texture object in ddraw too. I don't think that a direct DDraw <-> WineD3D refcount mapping can work.
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?
The reference count appears to be shared between the texture and all of its surfaces. This is the output of a small test program I wrote:
texture_refs.c:67:Calling CreateTexture texture_refs.c:69:texture @ 001DB220 texture_refs.c:71:texture refs: 1 texture_refs.c:73:Calling GetSurfaceLevel (0) texture_refs.c:75:surface 0 @ 001DB420 texture_refs.c:78:texture refs: 2 texture_refs.c:79:surface 0 refs: 2 texture_refs.c:82:QueryInterface for surface on texture returned 0x80004002, ptr 00000000 texture_refs.c:84:QueryInterface for texture on surface 0 returned 0x80004002, ptr 00000000 texture_refs.c:86:Calling GetSurfaceLevel (1) texture_refs.c:88:surface 1 @ 001DB540 texture_refs.c:92:texture refs: 3 texture_refs.c:93:surface 0 refs: 3 texture_refs.c:94:surface 1 refs: 3 texture_refs.c:96:Calling Release on texture texture_refs.c:101:texture refs: 2 texture_refs.c:102:surface 0 refs: 2 texture_refs.c:103:surface 1 refs: 2 texture_refs.c:105:Calling Release on surface 1 texture_refs.c:110:texture refs: 1 texture_refs.c:111:surface 0 refs: 1 texture_refs.c:112:surface 1 refs: 1 texture_refs.c:114:Calling AddRef on surface 0 texture_refs.c:119:texture refs: 2 texture_refs.c:120:surface 0 refs: 2 texture_refs.c:121:surface 1 refs: 2
Seems to be really one refcount. I'd suggest to create a addref_override and release_ovveride member for Direct3D9 Surfaces (and Direct3D8), and set them when a texture is created, and unset them when the texture is destroyed and before the surfaces are released the last time(so destroying the surface works normally with IDirect3DSurface9::Release).
As a sidenote, such overrides bring some problems. The current ddraw implementation is full of them. The callbacks and the existance of 3 different IDirectDraw(Main, user, hal) implementations and 5 different IDirectDrawSurface(Main, user, hal, dib, zbuffer) implementations made it quite difficult to understand the code. I don't think that I've fully understood it by now.
I'll have a look about the consequences of a WineD3DTexture <-> WineD3DSurface refcount connection for ddraw. I think it's not hard to cope with that case, but I can't promise right now. Can you check if there are more refcount connections?
I think that I should line out the changes to WineD3D I am planning: I'm going to add some methods, like IWineD3DSurface::Blt, IWineD3DSurface::BltFast, ..., this doesn't make any difference for D3D9. I split up the WineD3DDevice initalisation code into the CreateDevice part, which doesn't initialize OpenGL, and a IWineD3DDevice::Init3D to create a swapchain and the render targets. As a counterpart, there's a IWineD3DDevice::Uninit3D, which destroys the swapchain, but not the rendertargets, and the IWineD3DDevice::Release method, which frees the rest. The necessary changes to D3D9 are a call to IWineD3DDevice::Init3D during creation, and IWineD3DDevice::Uninit3D when the d3d9 device is destroyed. The d3d9 device has to release it's rendertarget surfaces on it's own too.
For 2D operation without OpenGL, I've created a secound surface implementation, IWineX11Surface(well, bad name). It replaces a few methods(Blt, Lock, Unlock, Flip, ...), but uses the same management code. CreateSurface takes another parameter which determines the surface type. To avoid a too complex code, there's no way to change a X11 surface into a 3D surface and vice versa. If for some reason a switch is necessary, ddraw releases all WineD3D surfaces and re-creates them. The advantage is that if are 2D app is run with GL surfaces, DDraw operation is hardware accellerated. This can be configured by a setting in winecfg in future.
The ugly part is the screen setup. Right now, this is done in the swapchain code with GL, and I've made a switch in IWineD3DDevice::GetDisplayMode and the new IWineD3DDevice::SetDisplayMode to have a different setup code without a swapchain. As a dublicate of IWineD3DDevice::Present, there's a IWineD3DSurface::Flip method, which calls Present for GL surfaces, or handles a flip by it's own(X11 surfaces).
DDraw-specific management in handled in ddraw directly. This concernes complex surfaces(~containers in d3d9), surface attachments and the (IMO horrible) DDraw structures. The code in WineD3D works with WineD3D or D3D9 types(except of the DDBLTFX function). I hope that I can avoid including the ddraw headers in WineD3D. My first patches will add some WineD3D types which don't exist in d3d7 to avoid using D3D9 types in ddraw.