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