使用抽象接口和向下铸造所需的指导

时间:2012-02-23 02:00:23

标签: c++ oop design-patterns rtti

我正在编写一个纯抽象接口,能够处理Direct3D 11或OpenGL 3(或更高版本)中的渲染。设计基本上是这样的:

// Abstract resource class
class IBuffer
{
public:
    // Destructor
    virtual ~IBuffer() { }

    // some pure virtual functions....
};

// Acts as a proxy class for ID3D11Buffer,
// on destruction calls COM Release()
class CBufferD3D11 : public IBuffer
{
public:
    // Construction and destruction.
    CBufferD3D11(ID3D11Buffer* buffer);

    // Releases the D3DResource
    ~CBufferD3D11();

    // Just left public for demo
    ID3D11Buffer* m_resource;   
};

// Abstract rendering class
class IRenderer
{
public:
    // Virtual destructor
    virtual ~IRenderer() {}

    // Factory function
    static IRenderer* Create(RenderType type);

    // Function to create a vertex buffer
    virtual IBuffer* CreateBuffer() = 0;

    // Function to enable a vertex buffer
    virtual void Enable(IBuffer* pBuffer) = 0;
};

// Acts a proxy class for the device object of Direct3D
class CRenderDevice : public IRenderer
{
public:
    // Constructor to create a rendering device
    CRenderDevice();

    // Function to enable a vertex buffer
    void Enable(IBuffer* pBuffer)
    {
        // This is a down cast, it could use dynamic_cast.
        // However this would be slow :(
        CBufferD3D11* pD3DBuffer = reinterpret_cast<CBufferD3D11*>(pBuffer);
        m_pContext->IASetVertexBuffers(0, 1, &pD3DBuffer, 0, 0);
    }
private:
    ID3D11Device* m_pDevice;
    ID3D11DeviceContext* m_pContext;
};

// Usage
void Foo()
{
    // Create the renderer which can then create a D3D11 buffer
    IRenderer* pRenderer = IRenderer::Create(D3D11);
    IBuffer* pBuffer = pRenderer->CreateBuffer();

    // Later during rendering
    pRenderer->Enable(pBuffer);
}

我对上述设计的问题是两个抽象接口之间的通信。渲染设备需要知道要启用/渲染的资源,但不幸的是,由于抽象,底层的Direct3D层只知道已经传递到IRenderer::Enable函数的更高级别的接口。

我已经研究过使用设计模式,但是不能确定哪一个最适合在未来的多线程渲染中使用。这会破坏构建渲染命令列表并在直接上下文[生产者消费者]上播放的多个设备上下文。

到目前为止,我能想到的最有效和线程安全的方法是使用向下转换的方法,通过reinterpret_cast或通过轻量级自定义RTTI。这样可以保持抽象的轻松,而不是远离API实现。因此,应用程序员可以在需要时利用渲染管道的其他功能。

使抽象接口在较低级别上进行通信而不需要向下转换的最佳方法是什么?商业游戏引擎往往会做什么?

我已经研究过开源引擎,但我真的不相信它们的实现适合我的需求。到目前为止,我见过的最好的是David Eberly's site,它使用更高级别的资源作为std :: map的关键。

1 个答案:

答案 0 :(得分:0)

我不是百分百肯定我在这里理解这个问题。如果您正在努力解决的问题是如何访问内部资源指针,为什么不向IBuffer添加一个方法来返回void *?

// Abstract resource class
class IBuffer
{
public:
    // Destructor
    virtual ~IBuffer() { }

    // Grab a pointer to the internal resource
    void* GetResource() = 0;
};

// Acts as a proxy class for ID3D11Buffer,
// on destruction calls COM Release()
class CBufferD3D11 : public IBuffer
{
public:
    // Construction and destruction.
    CBufferD3D11(ID3D11Buffer* buffer);

    // Releases the D3DResource
    ~CBufferD3D11();

    void* GetResource() { return m_resource; }

private:
    ID3D11Buffer* m_resource;   
};

然后,您可以将其强制转换为您希望处理的缓冲区类型。所以这个:

    CBufferD3D11* pD3DBuffer = reinterpret_cast<CBufferD3D11*>(pBuffer);
    m_pContext->IASetVertexBuffers(0, 1, &pD3DBuffer, 0, 0);

...变为:

    CBufferD3D11* pD3DBuffer =(CBufferD3D11*)(pBuffer->GetResource());
    m_pContext->IASetVertexBuffers(0, 1, &pD3DBuffer, 0, 0);

只要您的渲染器与正确的缓冲区配对,这应该可以正常工作吗?

我认为处理线程安全应该是另外一个需要担心的问题吗?即将调用包装在适当保护的缓冲区中。