图形API上的包装器

Jos*_*Pla 6 c++ opengl directx graphics

我非常喜欢拥有一个能够适应能力的游戏引擎,不仅仅是它能做什么,还有它如何处理新代码.最近,对于我的图形子系统,我编写了一个class覆盖的方法,其工作原理如下:

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. 
};
Run Code Online (Sandbox Code Playgroud)

我的想法是创建一个在大多数图形API中通用的函数列表.那么对于DirectX11,我只想创建一个新的孩子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. 
};
Run Code Online (Sandbox Code Playgroud)

然后,这些功能中的每一个都将DX11直接连接.我确实意识到这里有一层间接.人们是否被这个事实所关闭?

这是一种广泛使用的方法吗?还有什么我可以/应该做的吗?可以选择使用预处理器,但这对我来说似乎很麻烦.有人还向我提到了模板.你们有什么感想?

meg*_*dan 8

如果虚函数调用成为问题,则有一个编译时方法,它使用少量预处理器和编译器优化来删除虚拟调用.一种可能的实现是这样的:

使用纯虚函数声明基础渲染器:

class RendererBase {
public:
    virtual bool Draw() = 0;
};
Run Code Online (Sandbox Code Playgroud)

声明一个特定的实现:

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

RendererTypes.h根据要与某些预处理器一起使用的类型,创建一个标头以转发声明渲染器:

#ifdef DX11_RENDERER
    class RendererDX11;
    typedef RendererDX11 Renderer;
#else
    class RendererOGL;
    typedef RendererOGL Renderer;
#endif
Run Code Online (Sandbox Code Playgroud)

还要创建标题Renderer.h以包含渲染器的相应标头:

#ifdef DX11_RENDERER
    #include "RendererDX11.h"
#else
    #include "RendererOGL.h"
#endif
Run Code Online (Sandbox Code Playgroud)

现在,无论您在何处使用渲染器,都可以将其称为Renderer类型,包含RendererTypes.h在头文件和Renderer.hcpp文件中.

每个渲染器实现都应该在不同的项目中.然后创建不同的构建配置,以使用您要使用的任何渲染器实现进行编译.例如,您不希望为Linux配置包含DirectX代码.

在调试版本中,可能仍会进行虚函数调用,但在发布版本中,它们会被优化掉,因为您永远不会通过基类接口进行调用.它仅用于在编译时为渲染器类强制执行公共签名.

虽然这个方法确实需要一些预处理器,但它很小并且不会干扰代码的可读性,因为它是隔离的并且仅限于某些typedef和includes.一个缺点是您无法使用此方法在运行时切换渲染器实现,因为每个实现都将构建为单独的可执行文件.但是,实际上并不需要在运行时切换配置.

  • 值得注意的是,基类和虚函数是不必要的,因为所有代码都引用了`Renderer`具体类型.在修复之后,剩下的就是使用预处理器在源代码中选择正确的实现.在源代码中进行实现选择实际上没有任何可行的替代方案,但只需通过为当前系统提供适当的头包含路径,就可以更一般地通过构建机制来完成. (2认同)