Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wrapper over Graphics APIs

I'm a huge fan of having a game engine that has the abilty to adapt, not just in what it can do, but also in how it can handle new code. Recently, for my graphics subsystem, I wrote a class to be overriden that works like this:

class LowLevelGraphicsInterface {
    virtual bool setRenderTarget(const RenderTarget* renderTarget) = 0;
    virtual bool setStreamSource(const VertexBuffer* vertexBuffer) = 0;
    virtual bool setShader(const Shader* shader) = 0;
    virtual bool draw(void) = 0;

    //etc. 
};

My idea was to create a list of functions that are universal among most graphics APIs. Then for DirectX11 I would just create a new child class:

class LGI_DX11 : public LowLevelGraphicsInterface {
    virtual bool setRenderTarget(const RenderTarget* renderTarget);
    virtual bool setStreamSource(const VertexBuffer* vertexBuffer);
    virtual bool setShader(const Shader* shader);
    virtual bool draw(void);

    //etc. 
};

Each of these functions would then interface with DX11 directly. I do realize that there is a layer of indirection here. Are people turned off by this fact?

Is this a widely used method? Is there something else I could/should be doing? There is the option of using the preprocessor but that seems messy to me. Someone also mentioned templates to me. What do you guys think?

like image 837
Joseph Pla Avatar asked Sep 28 '14 20:09

Joseph Pla


1 Answers

If the virtual function calls become a problem, there is a compile time method that removes virtual calls using a small amount of preprocessor and a compiler optimization. One possible implementation is something like this:

Declare your base renderer with pure virtual functions:

class RendererBase {
public:
    virtual bool Draw() = 0;
};

Declare a specific implementation:

#include <d3d11.h>
class RendererDX11 : public RendererBase {
public:
    bool Draw();
private:
    // D3D11 specific data
};

Create a header RendererTypes.h to forward declare your renderer based on the type you want to use with some preprocessor:

#ifdef DX11_RENDERER
    class RendererDX11;
    typedef RendererDX11 Renderer;
#else
    class RendererOGL;
    typedef RendererOGL Renderer;
#endif

Also create a header Renderer.h to include appropriate headers for your renderer:

#ifdef DX11_RENDERER
    #include "RendererDX11.h"
#else
    #include "RendererOGL.h"
#endif

Now everywhere you use your renderer refer to it as the Renderer type, include RendererTypes.h in your header files and Renderer.h in your cpp files.

Each of your renderer implementations should be in different projects. Then create different build configurations to compile with whichever renderer implementation you want to use. You don't want to include DirectX code for a Linux configuration for example.

In debug builds, virtual function calls might still be made, but in release builds they are optimized away because you are never making calls through the base class interface. It is only being used to enforce a common signature for your renderer classes at compile time.

While you do need a little bit of preprocessor for this method, it is minimal and doesn't interfere with the readability of your code since it is isolated and limited to some typedefs and includes. The one downside is that you cannot switch renderer implementations at runtime using this method as each implementation will be built to a separate executable. However, there really isn't much need for switching configurations at runtime anyway.

like image 85
megadan Avatar answered Sep 28 '22 07:09

megadan