Switch between D3D and OGL

Started by
24 comments, last by Dae 18 years, 4 months ago
I'm sorry if there are topics on this. I did a search and found some information, but not much. I wanted to initially set the structure of my engine up so that I could switch between OGL and D3D on the fly. I know that my program would have to be structured well, and I might need to use a few if/else statements somewhere to see if the program is running D3D or OGL. This is basicly what I'm trying to accomplish:
#include <iostream>

namespace D3D {

class CFramework
{
private:

protected:

public:
	CFramework() {}
	void Draw() { std::cout << "D3D Draw();" << std::endl; }
};

}

namespace OGL {

class CFramework
{
private:

protected:

public:
	CFramework() {}
	void Draw() { std::cout << "OGL Draw();" << std::endl; }
};

}


class CFramework
{
private:
	enum libChoice {D3D, OGL};
	libChoice lib;

protected:

public:
	D3D::CFramework*    m_pD3DFrame;
	OGL::CFramework*    m_pOGLFrame;

	CFramework()
	{
		lib = D3D;
		if(lib == D3D)
			m_pD3DFrame = new D3D::CFramework();
		else if(lib == OGL)
			m_pOGLFrame = new OGL::CFramework();
	}
};
           


int main()
{
    CFramework* frame = new CFramework();
	
	//frame->m_pD3DFrame->Draw();
    frame.Draw();

    std::cin.get();
    
    return 0;
}
frame.Draw() doesn't work of course, that was just an idea, but wouldn't be a good idea because I'd have to make a function in that framework that D3D and OGL have. That or I have the instance of my choice, D3D or OGL, returned to me and I call Draw() from that. How would I get that working? or would I be forced to use if/else statements everywhere, or would I be able to if they were derived from the same hierarchy? Thanks for any info. :)
010001000110000101100101
Advertisement
You could create a common interface class:

class IFramework
{
public:
void Draw( void ) = 0;
void Release( void ) = 0;
};

Them make your concrete versions:

class D3DFramework : public IFramework
{
public:
void Draw( void ) {cout << "D3D" << end;}
void Release( void ) {delete this;}
};

class OGLFramework : public IFramework
{
public:
void Draw( void ) {cout << "OGL" << end;}
void Release( void ) {delete this;}
};

etc...

Then make a loader function:

enum libChoice {D3D, OGL};

IFramework* GetFramework( libChoice lib )
{
if(lib == D3D)
return new D3DFramework();
else if(lib == OGL)
return new OGLFramework();
}


int main()
{
IFramework* frame = GetFramework( D3D );

frame->Draw();
std::cin.get();

frame->Release();
return 0;

}


And trust me when I say this, it'll take a hell of a lot of work to make decent wrappers.
Haha, alright thanks man. Its too bad theres no like generic pointer that would point to D3D or OGL even though they are different classes. Deriving them from the same class works.. but eh wouldn't that be a little high maintenance.

If anyone knows of another way too I'd really appreciate it! :)
010001000110000101100101
If that were the case we'd only have one api...

My version runs to a class interface with about 60 odd methods, but that included texture/vertex and index buffers/pixel and vertex shaders. And it took bloody ages. A simple version with simple texture primitives wouldn't take too long to write.
How does that mean we'd only have one api?

Thanks for the help again, this is much more fun than c to f programs ;)

I had to get some help with this in IRC, and I've modified it a bit. It was recommended that I use an ADT (I think), and have a Mutex because the Instance() method wasn't safe. I think its coming along okay at least. If anyone comes across this topic (like I came across others) and wants to see more code.

#include <iostream>#include <vector>#include <windows.h>class CMutex{private:protected:	public:	HANDLE Create();	bool Lock(HANDLE);	bool Unlock(HANDLE);};HANDLE CMutex::Create(){	HANDLE hMutex = 0; 	// Create a mutex with no initial owner.	hMutex = CreateMutex( 		NULL,                       // default security attributes		FALSE,                      // initially not owned		NULL);	if (hMutex == NULL) {		std::cout << "CreateMutex error: \n" << GetLastError() << std::endl;	} else {		if (GetLastError() == ERROR_ALREADY_EXISTS) {			std::cout << "CreateMutex opened existing mutex. \n" << std::endl;		} else {			std::cout << "CreateMutex created new mutex. \n" << std::endl;		}	}    	return hMutex;}bool CMutex::Lock(HANDLE hMutex){	DWORD dwWaitResult;	// Request ownership of mutex. 	dwWaitResult = WaitForSingleObject( 		hMutex,   // handle to mutex		5000L);   // five-second time-out interval	switch (dwWaitResult) {		// The thread got mutex ownership.		case WAIT_OBJECT_0:			return true;		// Cannot get mutex ownership due to time-out.		case WAIT_TIMEOUT: 			return false; 		// Got ownership of the abandoned mutex object.		case WAIT_ABANDONED:			return false; 	}	return true;}bool CMutex::Unlock(HANDLE hMutex){	if (!ReleaseMutex(hMutex)) { 		// Deal with error.		return false;	} }CMutex* Mutex = new CMutex;//extern CMutex* Mutex;class D3DFramework;class OGLFramework;//C*class name* = Class//A*class name* = Abstract Classclass ARenderSystem{private:public:	virtual void Draw(void) = 0;	virtual void Release(void) = 0;};class CSystem{private:	enum libChoice {D3D, OGL};	static libChoice lib;	static ARenderSystem* m_aRender;	static CSystem* m_cInstance;	static std::string m_sClassName;protected:	CSystem() {}public:	void SwitchRenderSystem();	static ARenderSystem* Render();	static CSystem* Instance();};CSystem* CSystem::m_cInstance = 0;ARenderSystem* CSystem::m_aRender = 0;CSystem::libChoice CSystem::lib = D3D;std::string CSystem::m_sClassName = "CSystem";namespace D3D{class CRenderSystem : public ARenderSystem{public:	virtual void Draw(void) { std::cout << "D3D" << std::endl; }	virtual void Release(void) {  }};}namespace OGL{class CRenderSystem : public ARenderSystem{public:	virtual void Draw(void) { std::cout << "OGL" << std::endl; }	virtual void Release(void) {  }};}ARenderSystem* CSystem::Render(){	if(m_aRender == 0)		if(lib == D3D)			m_aRender = new D3D::CRenderSystem();		else if(lib == OGL)			m_aRender = new OGL::CRenderSystem();	return m_aRender;}CSystem* CSystem::Instance(){	HANDLE hMutex = Mutex->Create();	Mutex->Lock(hMutex);	if(m_cInstance == 0)    	m_cInstance = new CSystem();	Mutex->Unlock(hMutex);	return m_cInstance;}void CSystem::SwitchRenderSystem(){	if(lib == D3D) {		lib = OGL;		m_aRender = new OGL::CRenderSystem();	} else {		lib = D3D;		m_aRender = new D3D::CRenderSystem();	}	//call updates required}int main(){    CSystem* System = CSystem::Instance();    CSystem* Something = CSystem::Instance();    System->Render()->Draw();        System->SwitchRenderSystem();    System->Render()->Draw();        std::cin.get();    System->Render()->Release();    return 0;}
010001000110000101100101
I don't think you really need mutexes unless you are doing multi-threading, and even then I think it's a bad idea to have rendering code in multiple threads.

You'd want to stick a Release call in your switchrendersystem method to release the current system if there is one, or you'll get a memory leak. I've never seen a setup that uses both opengl and directx at the same time though...
Quote:Original post by Dae
Haha, alright thanks man. Its too bad theres no like generic pointer that would point to D3D or OGL even though they are different classes. Deriving them from the same class works.. but eh wouldn't that be a little high maintenance.

If anyone knows of another way too I'd really appreciate it! :)


That's how you'd do it. That's how I've done it. It works quite well actually. Very little fuss.
[ search: google ][ programming: msdn | boost | opengl ][ languages: nihongo ]
Quote:Original post by Dae
Haha, alright thanks man. Its too bad theres no like generic pointer that would point to D3D or OGL even though they are different classes. Deriving them from the same class works.. but eh wouldn't that be a little high maintenance.

If anyone knows of another way too I'd really appreciate it! :)
Look up the Bridge design pattern.
Quote:Original post by Dae
Haha, alright thanks man. Its too bad theres no like generic pointer that would point to D3D or OGL even though they are different classes. Deriving them from the same class works.. but eh wouldn't that be a little high maintenance.

If anyone knows of another way too I'd really appreciate it! :)


Well, C++ is not PHP [smile]. Having this kind of generic pointer is not compatible with the C++ notion of strict and safe typing.

In fact, this code:
class OglRenderer { ... };class D3dRenderer { ... };class Renderer { ... };// ...Renderer *r = (Renderer *)(new OglRenderer());

Will compile. If you carrefully craft both Renderer, OglRenderer and D3dRenderer then it may also work, if you are luck enough. To create binary compatible classes, they must have the same member variables AND the same functions. Then, maybe the compiler will let you do this (of course, this is horrible, non standard, is not proven to work, is more than hard to maintain (doh! I forgot that I added a dword in OglRenderer), break encapsulation (because the only way to have the same variables in OglRenderer and D3dRenderer is to have all your variables, meaning that the OglRenderer will need to declare some room for the D3D variables, even if it don't need them), and have a bunch of other problems that may prevent it to work (what destructor get called when you delete your false Renderer object?).

Deriving a class is far more easier than you might think. First, it doesn't add much maintenance to your project - it is certainly not a high maintenance. If you want to add one public function to your Renderer then you have to add it in OglRenderer and D3dRenderer - this is not what I'd call "a lot of work" [smile]. Moreover, the compiler will automatically call the correct function, use the correct variable, and so on. In fact, once you'll get used to it, you'll see that it is damn easy.

(Actually it is so easy that your next step is probably to abuse inheritance. Don't worry, that's a normal move [smile])

HTH,
Been down this road a few times and as long as you stick to the Fixed Function Pipeline model of D3D/OGL, then you're fine.

The real mind/brain-warp begins when you want to add shaders into the mix. Blech.

Instead of charging ahead though, I'd really evaluate if you need to support them or not.

Other than that, you're pretty much on the right track IMHO. I'd second the opinion that you should stay away from multithreaded code at this point, especially when you're just starting to get the rendering framework off the ground.

hth,

This topic is closed to new replies.

Advertisement