Hi all, I've sent a patch for ddraw COM management, I did not have any comments and the patch has been rejected. Could someone tell me if something is wrong or lacking? Christian. PS: I've joined a small doc that explains how to use objects with multiple interfaces. This docs is based on what has been done before with the ddraw code plus the enhancement I did. --------------------------------------------------------------------------------- - COM OBJECTS MANAGEMENT - --------------------------------------------------------------------------------- 1) Definitions: --------------- a) Object classes An object class is a structure wich defines the data of an object and all the interfaces that act on the object data Example : struct ObjectClass { INTERFACE 1; INTERFACE 2; ............ INTERFACE N; DATA; } ; typedef struct ObjectClass ObjectClass; b) Objects An object is a instance of an object class. Example : An object is created as follows : ObjectClass* object = malloc(sizeof(ObjectClass),...) c) Interfaces An interface is a way for an application to interact with an object. It contains a pointer to a virtual table plus some other data than can help objects management. An interface is a structure, which is used internally, that can be defined with the INTERFACE macro : #define INTERFACE(iface) \ struct { \ iface interface; \ /* VTABLE */ LPVOID object; \ /* pointer to the object the interface belongs to */ } To add a field to the interface, just add it in this macro. For example, we could embed the version number inside an interface or a magic number to check if the interface's pointer points to a real interface. And, off course, modify some macros or create new ones to make use of them. 2) Implementing COM objects: --------------------------- a) Declaring a COM object with multiple interfaces To declare interfaces in a COM object the ICOM_VFIELD_MULTI must be used for each interface as shown below: struct IDirect3DImpl { /* IUnknown fields */ ICOM_VFIELD_MULTI(IDirect3D); ICOM_VFIELD_MULTI(IDirect3D2); ICOM_VFIELD_MULTI(IDirect3D3); ICOM_VFIELD_MULTI(IDirect3D7); DWORD ref; /* IDirect3D fields */ IDirectDrawImpl* ddraw; LPVOID private; }; b) Creating an object Just declare an pointer to an object and allocate the memory for it. Example: IDirect3DImpl* object = malloc(sizeof(IDirect3DImpl),...) c) Initializing interfaces It is done with the ICOM_INIT_INTERFACE(object_pointer, interface_name, virtual_table) ICOM_INIT_INTERFACE(object,IDirect3D, VTABLE_IDirect3D); ICOM_INIT_INTERFACE(object,IDirect3D2,VTABLE_IDirect3D2); ICOM_INIT_INTERFACE(object,IDirect3D3,VTABLE_IDirect3D3); ICOM_INIT_INTERFACE(object,IDirect3D7,VTABLE_IDirect3D4); d) Retreive an object from within an interface's function If we consider the function : IDirect3D_AddRef(IDirect3D iface,.......) To retrieve a pointer to an object within the function, both followings macro are used : 1) ICOM_OBJECT(object_class,iface) This macro return an pointer to the object, the value is of object_class* type Example: IDirect3DImpl* d3d = ICOM_OBJECT(IDirect3DImpl,iface); 2) ICOM_THIS_OBJECT(object class,iface) It simply declares a This pointer and initialize it with the object address. IDirect3DImpl* This = ICOM_OBJECT(IDirect3DImpl,iface); e) Sharing functions between multiple interfaces In the case where a function does no have to do specific actions depending on the interface, you can share directly a function. Example : If we consider the function : IDirect3D_AddRef(IDirect3D iface,.......) The different virtual tables would be : VTABLE_IDirect3D = {......,IDirect3D_AddRef,....); VTABLE_IDirect3D2 = {......,XCAST()IDirect3D_AddRef,....); VTABLE_IDirect3D3 = {......,XCAST()IDirect3D_AddRef,....); VTABLE_IDirect3D7 = {......,XCAST()IDirect3D_AddRef,....); Note that this is possible because the ICOM_OBJECT can retreive the object's pointer without knowing the interface involved. f) Thunking interface Interface thunking enable changing from one interface to another. The following macro COM_INTERFACE_CAST(impltype, ifnamefrom, ifnameto, ifaceptr) performs this task. impltype = object class ifnamefrom = interface type we have ifnamefrom = interface type we want ifaceptr = pointer to the interface to cast return = pointer to the casted interface Interface thunking is a another way to shared a single function across different interfaces but as shown below this is required only when structures must be translated, interfaces returned casted or to handle prototype change : Example: You want to share the IDirect3D_CreateDevice across all IDirect3DX interfaces (with X above 1 because a device in Direct3D is created in a different way). IDirect3D2_CreateDevice(IDirect3D2* iface,.....) is the only one implementation of the function. The others are thunks to the IDirect3D2_CreateDevice. IDirect3D3_CreateDevice(IDirect3D3* iface, .....,LPDIRECT3DDEVICE3 *lpdevice, LPUNKNOWN lpUnk) { IDirect3DDevice3 *dev; IDirect3D_CreateDevice(ICOM_INTERFACE_CAST(IDirect3DImpl,IDirect3D2,IDirect3D3,iface),.....,&dev); *lpdevice = ICOM_INTERFACE_CAST((IDirect3DImpl,IDirect3D3,IDirect3D2,device) } IDirect3D7_CreateDevice(IDirect3D7* iface, .....,LPDIRECT3DDEVICE7 *lpdevice) { IDirect3DDevice2 *dev; IDirect3D_CreateDevice(ICOM_INTERFACE_CAST(IDirect3DImpl,IDirect3D2,IDirect3D7,iface),.....,&dev); *lpdevice = ICOM_INTERFACE_CAST((IDirect3DImpl,IDirect3D7,IDirect3D2,dev) } Here, we have to deal with a returned interface and a prototype change. The VTABLE should be: VTABLE_IDirect3D2 = {......,IDirect3D2_CreateDevice,....); VTABLE_IDirect3D3 = {......,IDirect3D3_CreateDevice,....); VTABLE_IDirect3D7 = {......,IDirect3D7_CreateDevice,....); Note that there are few functions that need this, for the others, thunks are not necessary and the way described in e) must be preferred. g) Getting an specific interface of an object With the ICOM_INTERFACE(implobj, ifacename) macro, any interfaces can be retreive from an object. implobj = pointer to the object ifacename = interface name we want return = pointer to an interface Example: IDirect3DImpl* object; ............ IDirect3D3* iface_d3d3 = ICOM_INTERFACE(object,IDirect3D3) h) Calling an COM object functions from another If a function is shared across multiple interfaces, you should write: IDirect3D3_CreateDevice(IDirect3D* iface,.....) { ICOM_THIS_OBJECT(iface); ................. IDirect3D_AddRef(ICOM_INTERFACE(This,IDirect3D),.....); ................. } If you know the nature of iface (i.e. thunking is used or the function is used in only one interface), you can write : IDirect3D3_CreateDevice(IDirect3D* iface,.....) { ICOM_THIS_OBJECT(iface); ................. IDirect3D3_AddRef(iface,.....); ................. }
Christian Costa <titan.costa(a)wanadoo.fr> writes:
I've sent a patch for ddraw COM management, I did not have any comments and the patch has been rejected. Could someone tell me if something is wrong or lacking?
Well, I was hoping some of the COM experts would comment on that. If I understand it right you are avoiding writing some thunking routines for older interfaces, at the cost of an extra pointer access in every function. I'm not convinced it's a good trade-off, but I'd like to hear other opinions. -- Alexandre Julliard julliard(a)winehq.com
On Thu, Nov 14, 2002 at 04:35:00PM -0800, WINE wrote:
Christian Costa <titan.costa(a)wanadoo.fr> writes:
I've sent a patch for ddraw COM management, I did not have any comments and the patch has been rejected. Could someone tell me if something is wrong or lacking?
Well, I was hoping some of the COM experts would comment on that. If I understand it right you are avoiding writing some thunking routines for older interfaces, at the cost of an extra pointer access in every function. I'm not convinced it's a good trade-off, but I'd like to hear other opinions.
I do not really see the need for it either. The implementation functions know the interface they get passed, so the offset to the vtable ptr within the object is constant and can very easily be calculated by the compiler. As for increased function sharing and reduced thunks usage... True, but the number of functions is not really annoying or problematic. Ciao, Marcus
Well, I was hoping some of the COM experts would comment on that. If I understand it right you are avoiding writing some thunking routines for older interfaces, at the cost of an extra pointer access in every function. I'm not convinced it's a good trade-off, but I'd like to hear other opinions.
Well, in the other case, the thunked interface will also have a performance trade-off as it will introduce extra pointer arithmetic and function calls on each COM call. Of course, the 'dominant' one will not have any performance penalty at all.
As for increased function sharing and reduced thunks usage... True, but the number of functions is not really annoying or problematic.
Well, in some cases like in D3DDevice where you have 34 methods that are shared between different interfaces, it starts to get a little bit annoying to write 34 thunks :-) In any case, for the moment I rewrote most of the COM part for the Direct3D code using Christian's patch (as it made my life much easier :-) ). Now, if it won't go into the tree, I could add thunking to it (should not be that hard). Just take a decision soon so as to not have a 11 klines patch lying in my tree for too long... Lionel -- Lionel Ulmer - http://www.bbrox.org/
On Fri, Nov 15, 2002 at 09:53:33AM +0100, Lionel Ulmer wrote:
Well, I was hoping some of the COM experts would comment on that. If I understand it right you are avoiding writing some thunking routines for older interfaces, at the cost of an extra pointer access in every function. I'm not convinced it's a good trade-off, but I'd like to hear other opinions.
Well, in the other case, the thunked interface will also have a performance trade-off as it will introduce extra pointer arithmetic and function calls on each COM call. Of course, the 'dominant' one will not have any performance penalty at all.
As for increased function sharing and reduced thunks usage... True, but the number of functions is not really annoying or problematic.
Well, in some cases like in D3DDevice where you have 34 methods that are shared between different interfaces, it starts to get a little bit annoying to write 34 thunks :-)
In any case, for the moment I rewrote most of the COM part for the Direct3D code using Christian's patch (as it made my life much easier :-) ). Now, if it won't go into the tree, I could add thunking to it (should not be that hard). Just take a decision soon so as to not have a 11 klines patch lying in my tree for too long...
If this makes your work easier I would just say go for it. Ciao, Marcus
Lionel Ulmer <lionel.ulmer(a)free.fr> writes:
In any case, for the moment I rewrote most of the COM part for the Direct3D code using Christian's patch (as it made my life much easier :-) ). Now, if it won't go into the tree, I could add thunking to it (should not be that hard). Just take a decision soon so as to not have a 11 klines patch lying in my tree for too long...
I think I prefer if you write the thunks. Sure it's a bit annoying, but it only has to be done once, they don't really need to be maintained afterwards; and I'm afraid that if we add the "easy" macros they will get used everywhere, not just where it saves 34 thunks, and we will lose performance without good reason. -- Alexandre Julliard julliard(a)winehq.com
On 14 Nov 2002, Alexandre Julliard wrote:
Christian Costa <titan.costa(a)wanadoo.fr> writes:
I've sent a patch for ddraw COM management, I did not have any comments and the patch has been rejected. Could someone tell me if something is wrong or lacking?
Well, I was hoping some of the COM experts would comment on that. If I understand it right you are avoiding writing some thunking routines for older interfaces, at the cost of an extra pointer access in every function. I'm not convinced it's a good trade-off, but I'd like to hear other opinions.
Though it's not really in my interest to, I suppose I could comment: if you study the MS definitions in ddrawi.h hard enough, then you can conclude that this particular overhead exist in real DirectDraw interfaces as well (so perhaps Direct3D 8 broke so hard with earlier versions to get rid of this overhead). But I still agree with your objection - apps using newer (more powerful) interfaces generally expect better performance, and thus ought to get less overhead than older interfaces (and that's how it is in WineX). But then again, that overhead is probably negligible compared to other factors.
participants (6)
-
Alexandre Julliard -
Christian Costa -
Lionel Ulmer -
Marcus Meissner -
Marcus Meissner -
Ove Kaaven