API 抽象层 - 避免混合使用 API 接口

API abstraction layer - avoid mixing of API interfaces

本文关键字:API 接口 混合 抽象      更新时间:2023-10-16

我一直计划为我的渲染引擎编写一个API抽象层。我要包含的两个 API 是 D3D11 和 D3D12。 因此,我首先为每个 API 编写了一些接口及其各自的实现。

以下代码片段对此进行了检查:

class IDevice
{
//... (pure) virtual methods
};
class CD3D11Device : public IDevice
{
//... virtual method implementations
};
class CD3D12Device : public IDevice
{
//... virtual method implementations
};

目前为止,一切都好。现在到实际问题: 如果我有另一个接口,其方法需要IDevice*作为参数,如何确保传递"正确"的设备?

class ISomeClass
{
public:
virtual void foo(IDevice* pDev) = 0;
};
class CD3D11SomeClass : public ISomeClass
{
public:
virtual void foo(IDevice* pDev) override
{
// should only be passed CD3D11Device
}
};
class CD3D12SomeClass : public ISomeClass
{
public:
virtual void foo(IDevice* pDev) override
{
// should only be passed CD3D12Device
}
};

我知道我每次都可以在IDevice*指针上调用dynamic_cast并检查nullptr但这既乏味又昂贵。

这个问题有什么优雅的解决方案吗?你们中有谁知道专业/商业游戏引擎是如何应对的吗?

你将无法将D3D11D3D12抽象在一起,除非你在抽象方面走得更远,而不仅仅是包装它们的接口。他们的设计过于对称对立。您需要的是设计一个高度抽象的渲染引擎界面。像MaterialImageModel这样的东西应该是严格的底部,旁边还有SceneRenderList

至于在单个应用程序中支持多个图形API,您在这里弄错了,如果您有D3D12,则D3D11代码路径中没有意义,选择应该是D3D11/GL而不是D3D12/Vulkan。这不是因为 API 相似性,而是因为应用程序中需要的功能集。

因为建议,D3D12并不意味着取代D3D11,前者存在于1%的应用程序,如AAA游戏或大型数据集上的重型GPGPU。如果您已经不是D3D11的专家,并且不知道为什么您必须使用D3D12,请不要使用它!