Jump to content
  • Advertisement
Sign in to follow this  
CandleJack

API Independence. Is it possible?

This topic is 3581 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I had the idea of trying to make my game API independent by using the Factory Method design pattern. The basic design I had in mind was this: I have a class to encapsulate the application, WindowsApplication. From here, I made a subclass called DirectDrawApplication, which handles the initialization and shut down of DirectDraw as the graphics API (I know DirectDraw is obsolete technology, but I chose it because the book I have on game programming uses it). I've read that there is a Direct3D Sprite interface for doing 2D games with Direct3D that is actually faster than DirectDraw, so I had planned on learning Direct3D at some point in the future, and figured I would simply derive another subclass called Direct3DApplication which initialized/shut down Direct3D instead of DirectDraw, and in a perfect world everything would work fine. Then I realized that my Graphic class contained an array of DirectDraw surfaces to store the animation frames I load in. I figured that I could make use of the Factory Method design pattern here. I planned to create an abstract Surface class that encapsulate the functionality for any type of surface I might use, and have the DirectDrawApplication use a polymorphic getSurace method to return an instance of a subclass DirectDrawSurface. That way, a Direct3DApplication could implement its own getSurface to return some sort of Direct3DSurface object. Obviously I oversimplified things on paper, because when I put it into practice I found several pitfalls. For example, when I call the draw method of a Graphic object, it is sent the primary DirectDraw surface as a parameter and calls this surface's Blt method, which itself takes a parameter of type LPDIRECTDRAWSURFACE7. I obviously can't pass Blt my own DirectDrawSurface object as a parameter in the implementation. It almost seems I would need some way of converting my DirectDrawSurface object into a LPDIRECTDRAWSURFACE7 that could be sent to the Blt method, but I can't think of how this would be done, because somewhere there would need to be a return type or parameter of LPDIRECTDRAWSURFACE7 and this would break the whole design. Did I set my goals too high here? Is what I am trying to accomplish even possible? Edit: Here is a UML diagram of my current design, with the relevant classes highlighted in yellow. Hopefully I remembered my UML correctly O_o

Share this post


Link to post
Share on other sites
Advertisement
I'm hoping I understood you correctly, but do you mean that you want to write a game that gives you the choice of using DirectDraw or Direct3D for your game? If so this is what I'd recommend. Try creating two seperate functions for each API with the same parameters then depending on what API you're using, set up a function pointer instead of an if statement. Here's some code


typedef struct _DXSurface
{
int Width;
int Height;
int Bpp;
int Pitch;
void* PrivateData;
}DXSurface;

HRESULT CreateSurface_DDraw( char* filename, DXSurface* pSurface )
{
// Create surface here. Cast the PrivateData pointer to the
// appropriate interface type. (IDirectDrawSurface7*)
hr = lpDD->CreateSurface( &ddsd, &((IDirectDrawSurface7*) pSurface->PrivateData )) );

// ...

return hr;
}

HRESULT CreateSurface_D3D( char* filename, DXSurface* pSurface )
{
// Create surface here. Cast the PrivateData pointer to the
// appropriate interface type (IDirect3DSurface9)
hr = lpD3DDevice->CreateOffscreenPlainSurface( &((IDirect3DSurface9*) pSurface->PrivateData)) );

// ...

return hr;
}


HRESULT (*CreateSurface)( char*, DXSurface* );

if( UseDDraw )
{
CreateSurface = CreateSurface_DDraw;
}
else if( UseD3D )
{
CreateSurface = CreateSurface__D3D;
}



I hope that this may be useful to you. You can do something similar with classes using inheritance, but it's up to you.

Share this post


Link to post
Share on other sites
Try abstracting higher:
Instead of abstracting low level surfaces that create sprites, just go about abstracting sprites themselves, e.g. an ISprite interface implemented by, say, a D3d9Sprite and an OGLSprite.

Also, try abstracting more precisely:
Rather than a Direct3DApplication have a D3D9Renderer. An application could make use of several API's and you wouldn't seriously consider writing a new application class for each combination, imagine having a Direct3DOpenALPhysxHawknlBoostApplication [oh]

Share this post


Link to post
Share on other sites
Quote:
Original post by blueshogun96
I'm hoping I understood you correctly, but do you mean that you want to write a game that gives you the choice of using DirectDraw or Direct3D for your game? If so this is what I'd recommend. Try creating two seperate functions for each API with the same parameters then depending on what API you're using, set up a function pointer instead of an if statement. Here's some code

*** Source Snippet Removed ***

I hope that this may be useful to you. You can do something similar with classes using inheritance, but it's up to you.


Yup! In theory the design I want would be able to implement any Graphics API, whether it be DirectDraw, Direct3D, OpenGL, etc. But I really don't see myself doing anything other than DirectX. I doubt I'll ever be porting this to a non-Windows OS.

Quote:
Original post by dmatter
Try abstracting higher:
Instead of abstracting low level surfaces that create sprites, just go about abstracting sprites themselves, e.g. an ISprite interface implemented by, say, a D3d9Sprite and an OGLSprite.

Also, try abstracting more precisely:
Rather than a Direct3DApplication have a D3D9Renderer. An application could make use of several API's and you wouldn't seriously consider writing a new application class for each combination, imagine having a Direct3DOpenALPhysxHawknlBoostApplication [oh]


Haha good point, that would get kind of ridiculous. I feel kind of silly for not realizing this myself. I like the idea of a separate abstract Renderer object that can be subclassed into a DirectDrawRenderer or Direct3D9Renderer as you suggested. However I think my original problem still exists. I do have an abstract Sprite interface as you also suggested, which contains an array of Graphic objects. To explain my logic behind this design decision, the Graphic object is a representation of any single frame or animation. The reason I have multiple Graphics associated with a single sprite is that it seemed like it in some cases it would be more optimal if a large portion of the image was static and only a small part animated. (So say, a big fish would have one large single frame bitmap as the fish, and a few very small surfaces for an animated fin or a wiggling tail).

So the problem that it seems to me still exists is that the Renderer and the Graphic would be strongly coupled. Now that I think about it, I wonder if the Renderer could act as a surface manager, and instead of using Graphic objects, I could design some sort of Animation structure which would be a list of surface ID's of surfaces stored in the surface manager...

I think I'll try this and if I find any problems I'll return here with more questions. Let me know though if you see problems with this design, or if you have a more optimal solution.

Share this post


Link to post
Share on other sites
Quote:
Original post by CandleJack
So the problem that it seems to me still exists is that the Renderer and the Graphic would be strongly coupled. Now that I think about it, I wonder if the Renderer could act as a surface manager, and instead of using Graphic objects, I could design some sort of Animation structure which would be a list of surface ID's of surfaces stored in the surface manager...


What you do is, in your Renderer you have an abstract method Draw which takes in the Sprite base class, and in the DirectDrawRenderer, the Draw method does a dynamic_cast<DirectDrawSprite> to get at the type it's expecting. Something like this (in pseudocode):


interface ISprite
{
// whatever...
}

interface IRenderer
{
ISprite *CreateSprite(...);

void DrawSprite(ISprite *sprite, int x, int y, ...);
}

class DirectDrawSprite : ISprite
{
LPDIRECTDRAWSURFACE7 *surface;
// etc...
};

class DirectDrawRenderer : IRenderer
{
ISprite *CreateSprite(...)
{
return new DirectDrawSprite();
}

void DrawSprite(ISprite *sprite, int x, int y)
{
DirectDrawSprite *ddsprite = dynamic_cast<DirectDrawSprite>(sprite);
ddsprite->surface->Foo(); // etc...
}
};



In the rest of your code, you only care about IRenderer and ISprite, you never "see" the DirectDrawRenderer or DirectDrawSprite directly.

Share this post


Link to post
Share on other sites
Quote:
Original post by Codeka
Quote:
Original post by CandleJack
So the problem that it seems to me still exists is that the Renderer and the Graphic would be strongly coupled. Now that I think about it, I wonder if the Renderer could act as a surface manager, and instead of using Graphic objects, I could design some sort of Animation structure which would be a list of surface ID's of surfaces stored in the surface manager...


What you do is, in your Renderer you have an abstract method Draw which takes in the Sprite base class, and in the DirectDrawRenderer, the Draw method does a dynamic_cast<DirectDrawSprite> to get at the type it's expecting. Something like this (in pseudocode):

*** Source Snippet Removed ***

In the rest of your code, you only care about IRenderer and ISprite, you never "see" the DirectDrawRenderer or DirectDrawSprite directly.


Ah I see, so in this example, the surface is actually declared in the concrete subclass. That was the part I wasn't getting I guess. Thanks, I'll give this a shot.

One more quick question. It would be a waste of video memory to have redundant surfaces declared if I want to create 50 of the same sprite, since they all share the same graphics. I was thinking a simple solution might be to declare the surface pointers as static so that they're shared among all instances, and then only clean up the surfaces when all instances are shut down, using reference counting. Would this work?

Share this post


Link to post
Share on other sites
Quote:
Original post by CandleJack
One more quick question. It would be a waste of video memory to have redundant surfaces declared if I want to create 50 of the same sprite, since they all share the same graphics. I was thinking a simple solution might be to declare the surface pointers as static so that they're shared among all instances, and then only clean up the surfaces when all instances are shut down, using reference counting. Would this work?


The way I'd do this is by using a "surface manager", like you were talking about previously. The surface manager would manage the actual surfaces so each instance of the sprite would query the surface manager for a surface with a given name/identifier/etc and the surface manager would return the same pointer for the same name/identifier/etc.

The advantage of this over static pointers is that they're all managed from the same place. So you could, for example, clear out the surface manager when the level ends (if not all sprites are used on all levels, for example).

Share this post


Link to post
Share on other sites
Quote:
Original post by Codeka
Quote:
Original post by CandleJack
So the problem that it seems to me still exists is that the Renderer and the Graphic would be strongly coupled. Now that I think about it, I wonder if the Renderer could act as a surface manager, and instead of using Graphic objects, I could design some sort of Animation structure which would be a list of surface ID's of surfaces stored in the surface manager...


What you do is, in your Renderer you have an abstract method Draw which takes in the Sprite base class, and in the DirectDrawRenderer, the Draw method does a dynamic_cast<DirectDrawSprite> to get at the type it's expecting. Something like this (in pseudocode):

*** Source Snippet Removed ***

In the rest of your code, you only care about IRenderer and ISprite, you never "see" the DirectDrawRenderer or DirectDrawSprite directly.


This is exactly how I did it. I am working on a framework that can use OpenGL or DirectX9. I have let the OpenGL part of the framework fall behind a bit since I have to meet a deadline for a team project, but up until then the framework could render textures and models for both. I did run into some problems with polymorphism though and have since then moved to a state machine. It is not as easy or clean to add new renderers (DirectX10) but it makes using the framework much easier since I don't have to worry about type-casting.

For your content manager I did something like this


class IContent
{
virtual void Draw() = 0;
};
typedef class _Texture : IContent
{
private:
LPD3DTEXTURE9 dxTex; //or whatever the type is...cant remember
GLuint glTex;
public:
void Draw()
{
//DrawCode
}
} *Texture;



and then I have
std::map<std::string,Texture> texMap;

so when I create a Texture, I store it into the map and then I just search the map for the Texture when I need to store a reference in an object. It probably isn't the fastest way to code, but this method really makes accessing content easier.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!