Wine Developer's Guide/Outline of DirectDraw Architecture

From WineHQ Wiki
Jump to: navigation, search

This is an outline of the architecture. Many details are skipped, but hopefully this is useful.

1 DirectDraw inheritance tree

Main
 |
User
 |-----------\
XVidMode    DGA2

Most of the DirectDraw functionality is implemented in a common base class. Derived classes are responsible for providing display mode methods (EnumDisplayModes, SetDisplayMode, RestoreDisplayMode), GetCaps, GetDeviceIdentifier and internal functions called to create primary and backbuffer surfaces.

User provides for DirectDraw capabilities based on drawing to a Wine window. It uses the User DirectDrawSurface implementation for primary and backbuffer surfaces.

XVidMode attempt to use the XFree86 VidMode extension to set the display resolution to match the parameters to SetDisplayMode.

DGA2 attempt to use the XFree86 DGA 2.x extension to set the display resolution and direct access to the framebuffer, if the full-screen-exclusive cooperative level is used. If not, it just uses the User implementation.

2 DirectDrawSurface inheritance tree

Main
 |--------------\
 |              |
DIB        Fake Z-Buffer
 |
 |------\---------\
 |      |         |
User   DGA2   DIBTexture

Main provides a very simple base class that does not implement any of the image-related functions. Therefore it does not place any constraints on how the surface data is stored.

DIB stores the surface data in a DIB section. It is used by the Main DirectDraw driver to create off-screen surfaces.

User implements primary and backbuffer surfaces for the User DirectDraw driver. If it is a primary surface, it will attempt to keep itself synchronized to the window.

DGA2 surfaces claims an appropriate section of framebuffer space and lets DIB build its DIB section on top of it.

Fake Z-Buffer surfaces are used by Direct3D to indicate that a primary surface has an associated z-buffer. For a first implementation, it doesn't need to store any image data since it is just a placeholder.

(Actually 3D programs will rarely use Lock or GetDC on primary surfaces, backbuffers or z-buffers so we may want to arrange for lazy allocation of the DIB sections.)

3 Interface Thunks

Only the most recent version of an interface needs to be implemented. Other versions are handled by having thunks convert their parameters and call the root version.

Not all interface versions have thunks. Some versions could be combined because their parameters were compatible. For example if a structure changes but the structure has a dwSize field, methods using that structure are compatible, as long as the implementation remembers to take the dwSize into account.

Interface thunks for Direct3D are more complicated since the paradigm changed between versions.

4 Logical Object Layout

The objects are split into the generic part (essentially the fields for Main) and a private part. This is necessary because some objects can be created with CoCreateInstance, then initialized later. Only at initialization time do we know which class to use. Each class except Main declares a Part structure and adds that to its Impl.

For example, the DIBTexture DirectDrawSurface implementation looks like this:

struct DIBTexture_DirectDrawSurfaceImpl_Part
{
        union DIBTexture_data data; /*declared in the real header*/
};

typedef struct
{
        struct DIB_DirectDrawSurfaceImpl_Part dib;
        struct DIBTexture_DirectDrawSurfaceImpl_Part dibtexture;
} DIBTexture_DirectDrawSurfaceImpl;

So the DIBTexture surface class is derived from the DIB surface class and it adds one piece of data, a union.

Main does not have a Part structure. Its fields are stored in IDirectDrawImpl/IDirectDrawSurfaceImpl.

To access private data, one says

DIBTexture_DirectDrawSurfaceImpl* priv = This->private;
do_something_with(priv->dibtexture.data);

5 Creating Objects

Classes have two methods relevant to object creation, Create and Construct. To create a new object, the Create method of the class is called. It allocates enough memory for IDirectDrawImpl or IDirectDrawSurfaceImpl as well as the private data for derived classes and then calls Construct.

Each class Construct method calls the base class Construct, then does the necessary initialization.

For example, creating a primary surface with the user ddraw driver calls User_DirectDrawSurface_Create which allocates memory for the object and calls User_DirectDrawSurface_Construct to initialize it. This calls DIB_DirectDrawSurface_Construct which calls Main_DirectDrawSurface_Construct.