2
\$\begingroup\$

I am writing a game engine in C++, or rather, a framework of reusable classes. So far, I have created low-level wrappers for OpenGL constructs. For example, a VBO class, a VAO class, and a texture class. I figure it would be a better idea to design my engine/framework to be agnostic of the API backends used below - so basically, make higher-level classes like Texture and Mesh, which are then implemented differently depending on if you use OpenGL or Direct3D.

The problem I'm having is that I can't quite figure out a good way to do this. How would I structure my classes? How high-level is enough?

I'm aware that OGRE3D uses the above mentioned approach, but I find the way Ogre does this to be confusing, to say the least. Can anybody provide me with good information on how to create an API agnostic game framework, or at least briefly explain how Ogre does it?

\$\endgroup\$
  • 2
    \$\begingroup\$ There are many ways to skin a cat. Which one is the best depends on your exact requirements. \$\endgroup\$ – Philipp Sep 14 '16 at 13:49
2
\$\begingroup\$

i figure it would be a better idea to design my engine/framework to be agnostic of the API backends used below

The very first question you need to ask yourself is why do this at all? Unless you're actually planning to port your code to D3D or some other API today, this kind of architecture astronaut thinking will likely just waste your time. And if you are planning to port to D3D today, why? You have code that works for OpenGL already, which works on every major desktop platform. What concrete benefit is your game going to get out of the ability to switch rendering backends?

Now the problem i'm having is that i can't quite figure out a good way to do this. How high-level is enough? Does anybody have a guideline to this, or good examples?

This is usually because you don't have a clear set of requirements about what this abstraction should provide. You should figure that out for yourself (it's not really possible for others to tell you what your usage goals should be).

What I'd recommend you do to tackle this is think of the lowest-level of abstraction you want the rest of your code to interact with, graphics-wise. For me, this is basically "textures, shaders and geometry" where "geometry" is "a collection of vertex and index buffers." To that end I'd write an abstraction that bundles up a collection of buffers and calls it Geometry. I would not write a Buffer class and implement it as D3DBuffer and OpenGLBuffer. That way lies madness: there are a lot of subtle differences between the buffer (and other similar) concepts in both APIs, and that can result in a very leaky abstraction.

It's very hard to say how high-level is good enough. That's got to be up to you and your needs. What is easier is to say how how low-level is not good enough. You need to provide some actual abstraction over these rendering APIs, some actual distance between them and the rest of your code. If your implementation of your abstraction ends up looking a lot like

void SomeFunction() {
  #ifdef OPENGL
    glSomeFunction();
  #else
    SomeD3DFunction();
}

then your abstraction is probably too low-level. Think bigger. Look at the where your gameplay code interacts with your rendering code and identify points where you think there's too much boilerplate, push those into your rendering abstraction.

Make it actually do some useful work for you rather than just translate calls from one API to another.

\$\endgroup\$
  • \$\begingroup\$ Buffer still might be there, just not the render-api buffer. It might be a collection of data arrays - vectors/floats, with semantic name assigned to each array. That Buffer might pack/interleave updated source data (from Lua in my case) into actual api buffer, and having semantics info will be helpful for automatic bindings to shader inputs. Sure, that's in case you want to play with custom data generated on the fly, rather than loaded from pre-compiled binary blobs, and if you still have no clear requirements for data/shaders upfront. \$\endgroup\$ – Vlad Sep 14 '16 at 15:55
  • \$\begingroup\$ Yes, my point is don't just make 1:1 wrappers. \$\endgroup\$ – Josh Sep 14 '16 at 15:57
  • \$\begingroup\$ ah thanks :) very clear answer. You are probably right - there is no reason tu support D3D. I hate it anyway :P \$\endgroup\$ – tubberd Sep 16 '16 at 1:24
  • \$\begingroup\$ But for example, supporting both OpenGL and Vulkan might be a good idea imho. \$\endgroup\$ – tubberd Sep 16 '16 at 1:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.