Требуется руководство по использованию абстрактных интерфейсов и приведения вниз - PullRequest
1 голос
/ 23 февраля 2012

Я нахожусь в процессе программирования чистого абстрактного интерфейса, который способен обрабатывать рендеринг в 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. В результате, прикладной программист может использовать дополнительные функции конвейера рендеринга, если это необходимо.

Каков наилучший способ заставить абстрактные интерфейсы взаимодействовать на более низком уровне без необходимости понижающего преобразования? Что обычно делают коммерческие игровые движки?

Я изучал движки с открытым исходным кодом, но я действительно не уверен, что их реализации подходят для моих нужд. Лучшее, что я видел до сих пор, было на сайте Дэвида Эберли , который использует ресурсы более высокого уровня в качестве ключа к std :: map.

1 Ответ

0 голосов
/ 23 февраля 2012

Я не уверен на 100%, что понимаю проблему здесь.Если бит, с которым вы боретесь, это как получить доступ к внутреннему указателю ресурса, почему бы не добавить метод к 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);

Пока ваш рендерер работает в паре с правильными буферами, это должно работать нормально, верно?подумали бы, что безопасность потоков должна оставаться отдельной проблемой, о которой стоит беспокоиться?т.е. обернуть вызовы в буферы в подходящей защите.

...