0
\$\begingroup\$

I'm developing a game. The game architecture is very modular. I have a "Graphics Engine", which uses either a Direct3D or OpenGL renderer. However the user does not have access to the renderers directly. Instead the "Graphics Engine" class is used, which uses a renderer interface.

I also have a resource management system. As for now, the renderers are respoinsible for allocating and releasing video memory. That couples my design in a bad way, since the "Graphics Engine" has undesirable logic for transferring resources between the RAM and the VideoRam through sending signals to the "Renderer". I want the resource management system to be responsible for allocating video memory.

I want to decouple the "Renderer" and "Graphics Engine" classes as much as possible, and make the renderer only responsible for rendering. Is there a way to obtain a universal pointer to a Video RAM buffer, and use it to render with both renderers?

My problem is basically that I can only obtain OpenGL or Direct3D handles, which can be used only from the relevant API. And the "Graphics Engine" should not know which renderer it uses.

\$\endgroup\$
3
  • \$\begingroup\$ isnt kinda like using a render target in xna, i guess there's no reason you can't wrap that up somehow in your own "API" \$\endgroup\$
    – War
    Aug 17, 2013 at 17:30
  • \$\begingroup\$ Are you not basically describing what most game engines do though? I personally use unity, at no point do i go near any DX or OpenGL API I simply declare game objects and add a renderer component to them, behind all that unity figures out the actual calls needed and its platform independent so whats your angle? \$\endgroup\$
    – War
    Aug 17, 2013 at 17:32
  • \$\begingroup\$ i guess you will have to code interfaces for the user to interact with and implement the neccesary functions once for each API. I don´t think there is a way of interacting with videocard in an API Independent way. \$\endgroup\$
    – Luis W
    Aug 17, 2013 at 17:35

1 Answer 1

2
\$\begingroup\$

This is just basic abstraction. Write an API-neutral interface for buffers, textures, and so on. Either use dynamic polymorphism (abstract interfaces with virtual functions) for these or compile-time static polymophism (a shared header with some ifdefs and then link different implementations whether you're compiling for a D3D-primary platforms or a GL-only platform). A simple, very incomplete draft of doing this would be:

// implement this interface for each graphics API you support
class IBuffer {
public:
  ~IBuffer() {}

  virtual size_t GetByteWidth() const = 0;
  virtual void UpdateData(const void* data, size_t width, size_t offset = 0) = 0;
  virtual void* Map() = 0;
  virtual void Unmap() = 0;
};

// implement this interface for each graphics API you support
class IGraphicsDevice {
public:
  virtual std::shared_ptr<IBuffer> CreateBuffer(size_t size) = 0;
};

// creates a device as appropriate
enum class EGraphicsType { Default, Direct3D, OpenGL, OpenGLES };
shared_ptr<IGraphicsDevice> GraphicsDeviceFactory(EGraphicsType type) {
  switch (type) {
  case EGraphicsType::Default:
#if defined(HAS_DIRECT3D)
  case EGraphicsType::Direct3D:
    return std::make_shared<GraphicsDeviceDirect3D>();
#endif
#if defined(HAS_OPENGL)
  case EGraphicsType::OpenGL:
    return std::make_shared<GraphicsDeviceOpenGL>();
#endif
#if defined(HAS_OPENGLES)
  case EGraphicsType::OpenGLES:
    return std::make_shared<GraphicsDeviceOpenGLES>();
#endif
  default:
    ASSERT(!"Unsupported graphics device");
    return nullptr;
  }
}

For static polymorphism, get rid of the virtual calls, directly embed the ID3DWhatever* values or GLint values wrapped in an #if defined(USE_FOO) to select which implementation to use, and make a variety of .cpp files for each platform and either only link the ones you need or wrap their contents in more macro conditionals. For some of the last-gen consoles the static polymorphism approach could have a substantial impact on performance as their CPUs weren't as good at efficiently handling virtual table indirections, though on the PC and likely the next-gen consoles this shouldn't be much of a problem. I don't have any information on mobile platforms regarding this.

\$\endgroup\$
1
  • \$\begingroup\$ As an addendum, I don't think it's quite common to call the compile-time technique mentioned "static polymorphism" but I don't know any other terminology that more accurately describes it. Primary point is that you can do it all at compile-time with no virtual function calls. You can use the PIMPL pattern to keep system dependencies out of your headers if you want, too, or just use void* and a lot of casting since both the D3D COM interface pointers and the GLint handles will fit in a void* on every architecture that matters to game development. \$\endgroup\$ Aug 18, 2013 at 19:23

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .