Re: WineD3D refcounting fun part II :)

2006-02-24 Thread Robert Shearman

H. Verbeet wrote:


I also suspect there might be a couple of circular references in the
form of device-stateblock-object-device, but I haven't had time to
really look into that yet. I *think* wined3d shouldn't keep references
to objects in the device's Set* methods, but I remember that when I
removed them in IWineD3DDeviceImpl_SetVertexShader Oliver put them
back again in a later patch for some reason.
 



It would be good to write out on paper or otherwise the hierarchy of 
which lifetimes a given object controls so that you can be sure of this. 
There should be no loops.


I am not at all familiar with the wined3d code, but from what I've seen 
discussed here it would seem that wined3d shouldn't keep references to 
any objects. Instead the other objects should release the corresponding 
wined3d object when they are destroyed.


--
Rob Shearman





Re: WineD3D refcounting fun part II :)

2006-02-24 Thread H. Verbeet
On 24/02/06, Robert Shearman [EMAIL PROTECTED] wrote:
 I am not at all familiar with the wined3d code, but from what I've seen
 discussed here it would seem that wined3d shouldn't keep references to
 any objects. Instead the other objects should release the corresponding
 wined3d object when they are destroyed.

I don't think it does, generally. d3d8/9 objects usually contain a
wined3d object that they wrap, and when the reference count of the
d3d8/9 object reaches 0 it releases its wined3d object. That works
fine as long as the is no relation between the wined3d objects. The
moment a wined3d object keeps a reference to another wined3d object
there is a problem though, because the d3d8/9 object that wraps that
object can be released while there are wined3d objects that still keep
a reference to that wined3d object.

What I mentioned to Roderick on IRC was not really doing refcounts on
wined3d objects, but on the wrapper objects instead. Essentially
calling AddRef on a wined3d surface would just increase the reference
count on the d3d8/9 surface. When the d3d8/9 object's refcount reaches
0 it just calls the wined3d object's cleanup function directly instead
of the release.

From Roderick's post I don't get the impression that that particular
issue is the problem here though.




Re: WineD3D refcounting fun part II :)

2006-02-24 Thread Stefan Dösinger
Hi,
 If you run the test on d3d9 or using oliver's full d3d8 patch, you'll
 notice that not all memory is returned when quitting the program. (In
 3dmark2001 this is a big issue as because of this we are out of memory
 after running a few demos) The problem appears that wined3d surfaces
 contain a reference to the wined3d device. So when you run a game the d3d8
 and wined3d device refcounts are out of sync (the wined3d refcount is a lot
 higher because of all the refcounts from the surfaces).
Are you sure that the Direct3DDevice9 should be released? Windows addrefs the 
DirectDraw interface when a surface is created, I'd be surprised if Microsoft 
changed that behavior in d3d8 or d3d9. From your description I'd say that 
there are still Direct3D9 surfaces hanging, which shouldn't happen.

It could be that some surfaces(or textures) are in use by the WineD3DDevice, 
for example they are set as textures with IWineD3DDevice::SetTexture. 
Settexure addrefs the texture interface, which makes sense and is done by 
Windows too. This means that the textures must be unset before the surfaces 
and the device can be destroyed, if an app doesn't do so, I'd consider it 
buggy.

I'll look at the multitexture demo, and I'll get an IRC client running. I've 
never used IRC before(shame on me), but I think it makes discussing this 
easier.

Stefan


pgpHd4aa2Q9u4.pgp
Description: PGP signature



Re: WineD3D refcounting fun part II :)

2006-02-24 Thread Stefan Dösinger
Hi,

From the multitexture demo:
 void loadTexture( void )
 {
   D3DXCreateTextureFromFile( g_pd3dDevice, test.bmp, g_pTexture_0 );
   D3DXCreateTextureFromFile( g_pd3dDevice, checker.bmp, g_pTexture_1 );
 
   g_pd3dDevice-SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
   g_pd3dDevice-SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
 }
Two textures are created

 //-
 // Name: shutDown()
 // Desc: 
 //-
 void shutDown( void )
 {
 if( g_pTexture_0 != NULL ) 
 g_pTexture_0-Release();

 if( g_pVertexBuffer != NULL )
 g_pVertexBuffer-Release();
 
 if( g_pd3dDevice != NULL )
 g_pd3dDevice-Release();
 
 if( g_pD3D != NULL )
 g_pD3D-Release();
 }
One texture is released

 void render( void )
 {
 snip
 g_pd3dDevice-SetTexture( 0, g_pTexture_0 );
   g_pd3dDevice-SetTexture( 1, g_pTexture_1 );
The texture stage is set - Texure AddRef. The texture isn't unset.

The missing release of the 2nd texture is definitly an application bug. What 
remains open is if not unsetting the texture stages is a bug too. Can someone 
check on Windows if (a) creating a surface or texture addrefs the 
Direct3DDevice, and if (b) assigning a texture to a texture stage addrefs the 
Texture? If (a) isn't true, we should unset all texture stages when the 
WineD3DDevice is released, if (b) is false we shouldn't do that too. If (a) 
and (b) are true, the application is buggy too. I'm sorry that I can't test, 
because I've no D3D9-Equipped windows machine available ATM.



pgpe28MHFguYZ.pgp
Description: PGP signature



Re: WineD3D refcounting fun part II :)

2006-02-24 Thread H. Verbeet
On 24/02/06, Stefan Dösinger [EMAIL PROTECTED] wrote:
 Are you sure that the Direct3DDevice9 should be released? Windows addrefs the
 DirectDraw interface when a surface is created, I'd be surprised if Microsoft
 changed that behavior in d3d8 or d3d9. From your description I'd say that
 there are still Direct3D9 surfaces hanging, which shouldn't happen.
I assume Roderick is talking about the final Release on the d3d
device, ie, the one you got from CreateDevice. The surfaces are
holding a couple of references to the device, and those should get
released when the surface is destroyed, yes. But the application is
also holding a single reference to de device.

Looking at the trace from the multitexture demo, it appears
GetContainer sometimes adds an extra reference to the device. I've
submitted a patch for this to wine-patches.




Re: WineD3D refcounting fun part II :)

2006-02-24 Thread H. Verbeet
On 24/02/06, Stefan Dösinger [EMAIL PROTECTED] wrote:
 remains open is if not unsetting the texture stages is a bug too. Can someone
 check on Windows if (a) creating a surface or texture addrefs the
 Direct3DDevice, and if (b) assigning a texture to a texture stage addrefs the
 Texture? If (a) isn't true, we should unset all texture stages when the
 WineD3DDevice is released, if (b) is false we shouldn't do that too. If (a)
 and (b) are true, the application is buggy too. I'm sorry that I can't test,
 because I've no D3D9-Equipped windows machine available ATM.
CreateTexture adds a reference to the device. SetTexture doesn't add a
reference to the texture.




WineD3D refcounting fun part II :)

2006-02-23 Thread Roderick Colenbrander
Hi,

During the d3d8 - wined3d transition I noticed one huge refcounting issue 
which affects both d3d8 and d3d9. The issue is illustrated by running 
http://www.codesampler.com/source/dx9_multitexture.zip (the same demo is 
available for dx8) and then modify wined3d's globalChangeGlRam in 
wined3d_main.c to let it output how it adjusts the memory.

If you run the test on d3d9 or using oliver's full d3d8 patch, you'll notice 
that not all memory is returned when quitting the program. (In 3dmark2001 
this is a big issue as because of this we are out of memory after running a 
few demos) The problem appears that wined3d surfaces contain a reference to 
the wined3d device. So when you run a game the d3d8 and wined3d device 
refcounts are out of sync (the wined3d refcount is a lot higher because of 
all the refcounts from the surfaces).

At the moment you quit a game the d3d8 device refcount nicely becomes zero and 
the object tries to kill itself. The release function also calls the wined3d 
device release function but the wined3d device object won't kill itself 
because its refcount doesn't become zero due to all the surfaces which still 
contain a reference to it. (the surfaces which useally are front-, 
backbuffers and more are normally released in the wined3d device release call 
when the refcount becomes zero)

One way to 'fix' the problem is to let d3d8/d3d9 handle the reference counts 
(remove the refcounts from wined3d; and let d3d8/d3d9 call some cleanup 
function to remove the garbage in case its refcount becomes zero)

Does anyone have a better solution?

Roderick




Re: WineD3D refcounting fun part II :)

2006-02-23 Thread H. Verbeet
O right. From the conversation on IRC I got the impression the issue
was the application calling the device's Release before calling the
surface's Release. Similar to the Surface / Texture issue. On windows
that should work, on wine it will probably fail at the moment.

Releasing the device after all its children are deleted should work
though, and it might simply be a matter of the object (be it a surface
or something else) not releasing its references to the device properly
upon deletion.

I also suspect there might be a couple of circular references in the
form of device-stateblock-object-device, but I haven't had time to
really look into that yet. I *think* wined3d shouldn't keep references
to objects in the device's Set* methods, but I remember that when I
removed them in IWineD3DDeviceImpl_SetVertexShader Oliver put them
back again in a later patch for some reason.




Re: Refcounting fun

2006-02-14 Thread H. Verbeet
On 13/02/06, Stefan Dösinger [EMAIL PROTECTED] wrote:
 I'd suggest that you send a patch taking whatever way you like, and ask AJ for
 his opinion.
If nobody else has a real opinion on the subject, I'll probably just
implement your idea, and see what problems I'll run into.




Re: Refcounting fun

2006-02-13 Thread H. Verbeet
  I'm not sure that connection is much more complex than the way it
  currently is. Right now the d3d7/8/9 wrapper objects are responsible
  for cleaning up their wined3d object, while with that change the
  wined3d object would become responsible for deleting the d3d7/8/9
  objects.
 Err, the WineD3D objects clean themselves, it's the duty of the d3d7/8/9
 objects the release them if they are not needed any more. If the WineD3D
 refcount falls to 0 then(the general case), the WineD3D object will clean
 itself. If the object is in use somewhere internally in WineD3D, it's
 destroyed as soon WineD3D releases that refcount.
I should have said releasing, of course :)

  It probably would. However, it's not all that different from the other
  option. The main difference seems to be that you don't separate the
  CleanUp and Release code, but use the parent in combination with a
  reference count that's guaranteed to be one as some sort of flag to
  decide whether to do the entire release code or just the cleanup part.
 I personally think that we should stay withhin the possibilities that COM
 gives us, instead of adding cross-library destroy callbacks. Why? Read the
 current ddraw surface creation code and the surface release code, and follow
 the callbacks between the different ddraw / surface types.

  That, and in order the use the container in that way you either have
  to store it somewhere in d3d7/8/9 or retrieve it on every call to
  Release. I'm not sure if that reduces the complexity all that much.
  Regardless of whether separating the Release and CleanUp code would
  make things a lot more complex, I do think it's somewhat cleaner.
  Also, note that most (all?) wined3d objects already have a CleanUp
  function.
 Well, at last it's up to Alexandre to decide which way to take.

 I also think that seperated release / cleanup code makes it easy to do sloopy
 and incorrect refcounting. The ddraw code is full of bad surface reference
 counting, which is cascaded by a bad reference counting in ddraw, which works
 for most games, but breaks e.g. Empire Earth.
Well, I agree it's not a great construction. I'm just not sure if
using the container in that way makes things much better. It would
mean having to retrieve the surface container in each call to
IDirect3D9Surface_Release. You can't easily store it in d3d9 since
when the wined3d surface's container changes the d3d9 container would
have to change as well, but you can't call much of anything in d3d9
without adding another callback. Also, you're now duplicating the
relation between a surface and it's container in d3d9, while I don't
think d3d9 should know that much about the specific implementation in
wined3d. That doesn't mean I couldn't live with going with that
solution, just that I'm not sure what the advantage would be.




Re: Refcounting fun

2006-02-13 Thread Stefan Dösinger
Hi,
 Well, I agree it's not a great construction. I'm just not sure if
 using the container in that way makes things much better. It would
 mean having to retrieve the surface container in each call to
 IDirect3D9Surface_Release.
Do you think it might be a performance problem?

 You can't easily store it in d3d9 since  
 when the wined3d surface's container changes the d3d9 container would
 have to change as well, but you can't call much of anything in d3d9
 without adding another callback. 
Can containers change? I thought that the container is set at creation time 
and can't change?

 Also, you're now duplicating the 
 relation between a surface and it's container in d3d9, while I don't
 think d3d9 should know that much about the specific implementation in
 wined3d. That doesn't mean I couldn't live with going with that
 solution, just that I'm not sure what the advantage would be.
I think that we can't avoid keeping certain management things in d3d7/8/9 
because of the differences between the versions.

I've had a look at your proposal again, and I could handle it in ddraw. The 
only problem is that I have to be able to release(and destroy) the 
wined3dSurface without having the ddraw surface destroyed. This happens when 
I switch the surface implementation, or when I lose the primary surface due 
to a screen resolution change(Restoring the surface re-creates the 
WineD3DSurface in this case). The best solution for this would be to allow 
the cleanup CB to be NULL.

My opinion is to use the container in the d3d9 addref and release function, as 
it uses only methods that COM and WineD3D already have, and both d3d9 
surfaces and wined3d surfaces are valid COM objects(they have their own 
refcount) and we avoid callbacks. Furthermore the d3d9 and wined3d objects 
are clearly seperated.

What's the opinion of the other directx people? Lionel? Oliver?


pgpgRHGkRafal.pgp
Description: PGP signature



Re: Refcounting fun

2006-02-13 Thread H. Verbeet
On 13/02/06, Stefan Dösinger [EMAIL PROTECTED] wrote:
 Hi,
  Well, I agree it's not a great construction. I'm just not sure if
  using the container in that way makes things much better. It would
  mean having to retrieve the surface container in each call to
  IDirect3D9Surface_Release.
 Do you think it might be a performance problem?
I'm not sure, probably not that much, but it depends on whether the
AddRef / Release functions are called a lot. If we use GetContainer,
it would make a call to QueryInterface. If the surface hasn't got a
container it makes a call to GetDevice and returns the wined3d device.
We'd have to account for that as well. Or we could write a function to
just retrieve the pointer. We would of course also have to get the
d3d9 parent of the container.

  You can't easily store it in d3d9 since
  when the wined3d surface's container changes the d3d9 container would
  have to change as well, but you can't call much of anything in d3d9
  without adding another callback.
 Can containers change? I thought that the container is set at creation time
 and can't change?
I'm not sure if it happens or not (and if it is actually required when
it happens :)), but the function to change it is there. Of course if
we can be sure it never happens that would mean we could just store
the pointer to the container in d3d9 during creation time and set it
to NULL when releasing the container.




Re: Refcounting fun

2006-02-13 Thread Stefan Dösinger

 I'm not sure, probably not that much, but it depends on whether the
 AddRef / Release functions are called a lot. If we use GetContainer,
 it would make a call to QueryInterface. If the surface hasn't got a
 container it makes a call to GetDevice and returns the wined3d device.
 We'd have to account for that as well. Or we could write a function to
 just retrieve the pointer. We would of course also have to get the
 d3d9 parent of the container.
I think the returned device pointer is not a problem - just check the returned 
pointer against your device instead of NULL. As for GetParent / 
QueryInterface / GetContainer calls, it's necessary to release any unwanted 
increased refcount. If however, a GetContainer call for an D3D9 texture 
causes an AddRef or Release of that texture, the approach is impossible. I 
didn't find any evidence of that yet.

 I'm not sure if it happens or not (and if it is actually required when
 it happens :)), but the function to change it is there. Of course if
 we can be sure it never happens that would mean we could just store
 the pointer to the container in d3d9 during creation time and set it
 to NULL when releasing the container.
That would make everything much easier :)

I'd suggest that you send a patch taking whatever way you like, and ask AJ for 
his opinion.


pgp7V9mp9jXzu.pgp
Description: PGP signature



Refcounting fun

2006-02-12 Thread H. Verbeet
I've run into a somewhat annoying problem with d3d9/wined3d recently.
It appears that on d3d9 on Windows a surface either forwards its
AddRef / Release calls to its container if it has one, or just shares
the refcount directly. Either way, it comes down to the same thing.
Calling  AddRef on the surface increases the texture's refcount and
calling AddRef on the texture increases the surface's refcount. Same
thing for Release.

As a consequence, on Windows, it's perfectly legal to do something like this:

/* Create a texture */
IDirect3DDevice9_CreateTexture()
/* Get a surface. Adds a reference to the surface/texture combination */
IDirect3DTexture9_GetSurfaceLevel()
/* Release the texture. Note that the texture isn't freed yet, because
we still hold a reference to the surface */
IDirect3DTexture9_Release()
Do some stuff with the surface, perhaps call GetContainer, etc
/* Release the surface. The texture and all of it's surfaces will now
be released. */
IDirect3DSurface9_Release()

In wine, the call to IDirect3DTexture9_Release() frees the texture.
(It also calls Release on it's surfaces, but since the surface had an
extra reference from the call to GetSurfaceLevel the surface is still
ok). Calling any function on the surface that needs access to the
container will now have undefines results, most likely a crash.

Naively, I tried to fix this by having the surface keep a reference to
it's container. That keeps the code above from crashing, but only
because it creates a circular reference between the surface and the
container. Oops.

Fixing the problem for wined3d is probably not even that hard. Just
separate the Release code from the Cleanup code, then have
WineD3DSurface forward its AddRef / Release calls to the container, if
it has one. The container can then call the Cleanup code for its
surfaces when it gets deleted.

However, there is another problem, related to the way d3d9 (and
ddraw/d3d7/d3d8, when their respective rewrites are finished) wraps
wined3d. After fixing the problem for wined3d as above, the relation
between IDirect3DSurface9 and IDirect3DTexture9 in wine would look
roughly like this:


|---|   |---|
| IDirect3DSurface9 |   | IDirect3DTexture9 |
|---|   |---|
^   ^
|   |
||   ||
| WineD3DSurface |-| WineD3DTexture |
||   ||

WineD3DSurface and WineD3DTexture share reference counts,
IDirect3DSurface9 keeps a reference to WineD3DSurface, and
IDirect3DTexture keeps a reference to WineD3DTexture. WineD3DSurface
and WineD3DTexture don't keep references to their parents (and can't,
without creating circular references).

So releasing the texture before the surface would still not work,
since the d3d9 texture still has only 1 reference, the one it got when
it was created. That means that in the call to
IDirect3DTexture9_Release the d3d9 texture gets freed, while the
wined3d texture doesn't. When we do something with the d3d9 surface
that needs the container, the wined3d surface correctly returns its
container, but when d3d9 then tries to get the parent of that
container it still dies.

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.

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?




Re: Refcounting fun

2006-02-12 Thread Stefan Dösinger
 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




Re: Refcounting fun

2006-02-12 Thread H. Verbeet
On 12/02/06, Stefan Dösinger [EMAIL PROTECTED] wrote:
 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?
It returns 0x80004002 (E_NOINTERFACE). Same thing for the reverse.

 When the app releases the compound, it (hopefully) calls the release method of
 the first surface.
At least for d3d9, it's not guaranteed which surface / texture gets
released last. Could be the texture, could be the first surface, could
be any of the other surfaces.

 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.
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.

 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 
texture_refs.c:84:QueryInterface for texture on surface 0 returned
0x80004002, ptr 
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

The refcounts in that program are retrieved like this:
static int get_refcount(IUnknown *object)
{
IUnknown_AddRef(object);
return IUnknown_Release(object);
}




Re: Refcounting fun

2006-02-12 Thread Stefan Dösinger
 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 
 texture_refs.c:84:QueryInterface for texture on surface 0 returned
 0x80004002, ptr 
 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 

Re: Refcounting fun

2006-02-12 Thread Stefan Dösinger
Hi,
I've thought over this: If you want to connect the IWineD3DTexture and 
IWineD3DSurface refcounts only, then there's no problem for me. If you want 
to connect the Texture's and Surface's Parents refcount, then you have do the 
same for the rendertargets and the swapchain(I guess that Windows does that 
too), and I can use that connection to manage complex ddraw surfaces.

Stefan


pgpwAKmwOyez0.pgp
Description: PGP signature



Re: Refcounting fun

2006-02-12 Thread H. Verbeet
On 12/02/06, Stefan Dösinger [EMAIL PROTECTED] wrote:
 Hi,
 I've thought over this: If you want to connect the IWineD3DTexture and
 IWineD3DSurface refcounts only, then there's no problem for me. If you want
 to connect the Texture's and Surface's Parents refcount,
Are you talking about IDirect3DSurface9 = IDirect3DTexture9 or do
you mean IDirect3DSurface9 = IWineD3DSurface?

 then you have do the
 same for the rendertargets and the swapchain(I guess that Windows does that
 too), and I can use that connection to manage complex ddraw surfaces.
I haven't verified it, but the MSDN documentation for GetContainer
appears to imply that it does.
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/directx9_c/IDirect3DSurface9__GetContainer.asp
Unfortunately my knowledge of ddraw and d3d7 is somewhat limited, so
it's a bit hard for me to oversee all the implications those changes
would have for ddraw / d3d7. The choice for Surface and Texture as an
example is somewhat arbitrary I guess, but probably the one for which
it matters the most. (As in, is most likely to break stuff if it isn't
implemented correctly). In principle it would change for any object
that has a container or uses similar construction. Perhaps it would
even make sense to forward AddRef and Release calls for all d3d7/8/9
objects that directly wrap a wined3d object.

For reference, the code for Release  CleanUp would look something
like this for the different objects:

/* WineD3D Surface */
ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface) {
IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
if (This-container) {
return IWineD3DBase_Release(This-container);
} else {
/* Handle our own refcounting */
}
}

void WINAPI IWineD3DSurfaceImpl_CleanUp(IWineD3DSurface *iface) {
IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;

/* Do some cleaning for ourselves */

This-parentCleanUp(This-parent);
}

/* WineD3D Texture */
ULONG WINAPI IWineD3DTextureImpl_Release(IWineD3DTexture *iface) {
IWineD3DTextureImpl *This = (IWineD3DTextureImpl *)iface;
ULONG ref = InterlockedDecrement(This-resource.ref);
if (!ref) {
IWineD3DTexture_CleanUp(iface);
}

return ref;
}

void WINAPI IWineD3DTextureImpl_CleanUp(IWineD3DTexture *iface) {
IWineD3DTextureImpl *This = (IWineD3DTextureImpl *)iface;
int i;
for (i = 0; i  This-baseTexture.levels; ++i) {
IWineD3DSurface_Cleanup(This-surfaces[i]);
}

/* Do some cleaning for ourselves */

This-parentCleanUp(This-parent);
}

/* IDirect3DSurface9 */
ULONG WINAPI IDirect3DSurface9Impl_Release(IDirect3DSurface9 *iface) {
IDirect3DSurface9Impl *This = (IDirect3DSurface9Impl *)iface;
return IWineD3DSurface_Release(This-wineD3DSurface);
}

void WINAPI IDirect3DSurface9Impl_CleanUp(IDirect3DSurface9 *iface) {
IDirect3DSurface9Impl *This = (IDirect3DSurface9Impl *)iface;
/* Cleanup Some stuff */
}

/* IDirect3DTexture9 */
ULONG WINAPI IDirect3DTexture9Impl_Release(IDirect3DTexture9 *iface) {
IDirect3DTexture9Impl *This = (IDirect3DTexture9Impl *)iface;
return IWineD3DTexture_Release(This-wineD3DTexture);
}

void WINAPI IDirect3DTexture9Impl_CleanUp(IDirect3DTexture9 *iface) {
IDirect3DTexture9Impl *This = (IDirect3DTexture9Impl *)iface;
/* Cleanup Some stuff */
}


AddRef would follow a similar pattern, although somewhat simpler since
it doesn't have to handle cleaning up the object.




Re: Refcounting fun

2006-02-12 Thread Stefan Dösinger
Hi,
 Are you talking about IDirect3DSurface9 = IDirect3DTexture9 or do
 you mean IDirect3DSurface9 = IWineD3DSurface?
I meant IWineD3DSurface = IWineD3DTexture and Parent(IWineD3DSurface) = 
Parent(IWineD3DTexture). The first case has no consequences for ddraw, if the 
secound one is forced by WineD3D this would cause a connection between 
IDirect3DSurface9 = IDirect3DTexture9 and between {Direct Draw texture root 
surface} = {Other ddraw texture surfaces}

 Unfortunately my knowledge of ddraw and
 d3d7 is somewhat limited, so it's a bit hard for me to oversee all the
 implications those changes would have for ddraw / d3d7. The choice for
 Surface and Texture as an example is somewhat arbitrary I guess, but
 probably the one for which it matters the most. (As in, is most likely to
 break stuff if it isn't implemented correctly). In principle it would
 change for any object that has a container or uses similar construction.
 Perhaps it would even make sense to forward AddRef and Release calls for
 all d3d7/8/9 objects that directly wrap a wined3d object.

 For reference, the code for Release  CleanUp would look something
 like this for the different objects:
I could deal with such a Release / cleanup difference in ddraw, but I don't 
like the idea. I think it creates a too complex connection between WineD3D 
and the d3d libs. My d3d9 knowledge is limited too, but what if you do 
something like this: (sort of pseude-code only)

IDirect3DSurface9::AddRef
{
  if(This-container /* Or get the container from WineD3D */)
  {
 return IUnknown_AddRef(This-container);
  }
  else
  {
ULONG ref = This-ref++;
etc
  }
}

IDirect3DSurface9::Releae
{
  if(This-container /* Or get the container from WineD3D */)
  {
 return IUnknown_Release(This-container);
  }
  else
  {
ULONG ref = This-ref--;

if(ref == 0)
{
   /*Destroy code*/
   IWineD3DSurface_Release(This-wineD3DSurface);
}
return ref;
  }
}

IDirect3DTexure9::AddRef
{
/* normal addref, nothing special */
}

IDirect3DTexure9::Release
{
  ULONG ref = This-ref--;

  if(ref == 0)
  {
for(all surfaces in this container)
{
  surfaceImpl-container = NULL;
  /* Or place this call in WineD3DTexture::Releaase*/

  IWineD3DTexture_Release(This-wineD3DTexture);
  /* IWineD3DTexture_Release will call the release method
   * of the IDirect3D9Surface(because it releases the parent),
   * run the surface's release code(not the container, because
   * it's set to NULL and destroy the surface, because it's own
   * refcount is 1 from creation, and we never changed it.
   * The D3D9 surface release code releases the wineD3DSurface,
   * and everthing is cleaned.
   */

   HeapFree(GetProcessHeap, 0, This);
}
  }
}

The important part is to set the surface's container when they are created, 
before the first AddRef() or Release() is called, and to set the container to 
NULL when the container is destroyed and before the surfaces are released. 
You can eighter track the containers in d3d9, or use wineD3D to get the 
container.

Could this work?

Stefan


pgpgDc14pzzRd.pgp
Description: PGP signature



Re: Refcounting fun

2006-02-12 Thread Stefan Dösinger
Hi
 IDirect3DTexure9::Release
 {
   ULONG ref = This-ref--;

   if(ref == 0)
   {
     for(all surfaces in this container)
     {
       surfaceImpl-container = NULL;
       /* Or place this call in WineD3DTexture::Releaase*/

       IWineD3DTexture_Release(This-wineD3DTexture);
       /* IWineD3DTexture_Release will call the release method
        * of the IDirect3D9Surface(because it releases the parent),
        * run the surface's release code(not the container, because
        * it's set to NULL and destroy the surface, because it's own
        * refcount is 1 from creation, and we never changed it.
        * The D3D9 surface release code releases the wineD3DSurface,
        * and everthing is cleaned.
        */

        HeapFree(GetProcessHeap, 0, This);
     }
   }
 }
Oops, little problem here: That should be

IDirect3DTexure9::Release
{
  ULONG ref = This-ref--;
  if(ref == 0)
  {
for(all surfaces in this container)
{
  surfaceImpl-container = NULL;
  /* Don't free the objects in this loop of course ;) */
}

/* Or place this call in WineD3DTexture::Releaase*/
IWineD3DTexture_Release(This-wineD3DTexture);

/* IWineD3DTexture_Release will call the release method
 * of the IDirect3D9Surface(because it releases the parent),
 * run the surface's release code(not the container, because
 * it's set to NULL and destroy the surface, because it's own
 * refcount is 1 from creation, and we never changed it.
 * The D3D9 surface release code releases the wineD3DSurface,
 * and everthing is cleaned.
 */
 HeapFree(GetProcessHeap, 0, This);
  }
}


pgpHYJNFmRtkY.pgp
Description: PGP signature



Re: Refcounting fun

2006-02-12 Thread H. Verbeet
On 12/02/06, Stefan Dösinger [EMAIL PROTECTED] wrote:
 I meant IWineD3DSurface = IWineD3DTexture and Parent(IWineD3DSurface) =
 Parent(IWineD3DTexture). The first case has no consequences for ddraw, if the
 secound one is forced by WineD3D this would cause a connection between
 IDirect3DSurface9 = IDirect3DTexture9 and between {Direct Draw texture root
 surface} = {Other ddraw texture surfaces}
Connecting the parents' refcounts wasn't really the idea, at least not
directly. I think it's probably a good idea to have as little logic in
the wrappers as possible. However, they are of course indirectly
connected through wined3d; The connection between IDirect3DSurface9
and IDirect3DTexture9 is the reason for doing it in the first place.

  For reference, the code for Release  CleanUp would look something
  like this for the different objects:
 I could deal with such a Release / cleanup difference in ddraw, but I don't
 like the idea. I think it creates a too complex connection between WineD3D
 and the d3d libs.
I'm not sure that connection is much more complex than the way it
currently is. Right now the d3d7/8/9 wrapper objects are responsible
for cleaning up their wined3d object, while with that change the
wined3d object would become responsible for deleting the d3d7/8/9
objects.

 The important part is to set the surface's container when they are created,
 before the first AddRef() or Release() is called, and to set the container to
 NULL when the container is destroyed and before the surfaces are released.
 You can eighter track the containers in d3d9, or use wineD3D to get the
 container.

 Could this work?
It probably would. However, it's not all that different from the other
option. The main difference seems to be that you don't separate the
CleanUp and Release code, but use the parent in combination with a
reference count that's guaranteed to be one as some sort of flag to
decide whether to do the entire release code or just the cleanup part.
That, and in order the use the container in that way you either have
to store it somewhere in d3d7/8/9 or retrieve it on every call to
Release. I'm not sure if that reduces the complexity all that much.
Regardless of whether separating the Release and CleanUp code would
make things a lot more complex, I do think it's somewhat cleaner.
Also, note that most (all?) wined3d objects already have a CleanUp
function.




Re: Refcounting fun

2006-02-12 Thread Stefan Dösinger
 Connecting the parents' refcounts wasn't really the idea, at least not
 directly. I think it's probably a good idea to have as little logic in
 the wrappers as possible. However, they are of course indirectly
 connected through wined3d; The connection between IDirect3DSurface9
 and IDirect3DTexture9 is the reason for doing it in the first place.
Right, I can't imagine how a IWineD3DTexture = IWineD3DSurface connection 
can help you, except if you use that connection to build the Direct3D9Texture 
= Direct3D9Surface connection.

 I'm not sure that connection is much more complex than the way it
 currently is. Right now the d3d7/8/9 wrapper objects are responsible
 for cleaning up their wined3d object, while with that change the
 wined3d object would become responsible for deleting the d3d7/8/9
 objects.
Err, the WineD3D objects clean themselves, it's the duty of the d3d7/8/9 
objects the release them if they are not needed any more. If the WineD3D 
refcount falls to 0 then(the general case), the WineD3D object will clean 
itself. If the object is in use somewhere internally in WineD3D, it's 
destroyed as soon WineD3D releases that refcount.

The exception is when WineD3D asked d3d7/8/9 to create objects via 
callbacks(e.g. the callback during texture creation), then wineD3D releases 
the parents of the objects it created. The parents take care for cleaning up, 
and they release their WineD3D child.



 It probably would. However, it's not all that different from the other
 option. The main difference seems to be that you don't separate the
 CleanUp and Release code, but use the parent in combination with a
 reference count that's guaranteed to be one as some sort of flag to
 decide whether to do the entire release code or just the cleanup part.
I personally think that we should stay withhin the possibilities that COM 
gives us, instead of adding cross-library destroy callbacks. Why? Read the 
current ddraw surface creation code and the surface release code, and follow 
the callbacks between the different ddraw / surface types.

 That, and in order the use the container in that way you either have
 to store it somewhere in d3d7/8/9 or retrieve it on every call to
 Release. I'm not sure if that reduces the complexity all that much.
 Regardless of whether separating the Release and CleanUp code would
 make things a lot more complex, I do think it's somewhat cleaner.
 Also, note that most (all?) wined3d objects already have a CleanUp
 function.
Well, at last it's up to Alexandre to decide which way to take.

I also think that seperated release / cleanup code makes it easy to do sloopy 
and incorrect refcounting. The ddraw code is full of bad surface reference 
counting, which is cascaded by a bad reference counting in ddraw, which works 
for most games, but breaks e.g. Empire Earth.


pgp6kYgx6pwvm.pgp
Description: PGP signature