23
\$\begingroup\$

When making a renderer that supports multiple graphics APIs, you would typically want to abstract your code in some sort of low level library that is tied with some graphics API like OpenGL, Vulkan, D3D11 and so on;

They work very differently from each other, so making a good generic API becomes essential; I've read that you would typically want to use a "back-end" that implements the basic functionality for each API you want to support, and a "front-end" which is what is used by the programmer to draw stuff on the screen.

How do I know if I'm making too much of a tight abstraction?

\$\endgroup\$
  • 5
    \$\begingroup\$ Are you 100% certain that your wrapper + Vulkan or D3D11 is faster than just always using OpenGL? \$\endgroup\$ – Mooing Duck Nov 30 '18 at 22:58
  • \$\begingroup\$ @Mooing Duck if used correctly, yes. \$\endgroup\$ – Gabriele Vierti Dec 1 '18 at 7:34
  • 4
    \$\begingroup\$ I would seriously doubt that. Most of the gains in Vulkan come from being very low-level and doing things in a very peculiar, specific way. Something that is generic enough to also do the same seamlessly in OpenGL or D3D almost certainly can't do that. Reimplementing the same stuff as OpenGL does (like many Vulkan tutorials do, ironically) results in 99.99% the same performance, with 20 times the work. \$\endgroup\$ – Damon Dec 1 '18 at 15:46
  • \$\begingroup\$ @Damon that's right, but newer APIs are specifically designed (and not adapted like OpenGL) to work on modern gpus; speaking of Vulkan, you can pretty much use the same API on every supported platform, contrarily to OpenGL, where you need to use the "Embedded Systems" version. Other than less cpu overhead and fancy features we don't have much, but in the future we might have some pretty surprising techniques that may run faster using Vk or D3D12 rather than OGL or D3D11 \$\endgroup\$ – Gabriele Vierti Dec 1 '18 at 16:04
44
\$\begingroup\$

First of all, consider if it is actually worth it to support more than one graphics API. Just using OpenGL will cover most platforms and will be "good enough" for all but the most graphically ambitious projects. Unless you work for a very large game studio which can afford to sink several thousand person-hours into implementing and testing multiple rendering backends and unless there are some DirectX or Vulcan specific features you really want to show off, it is usually not worth the hassle. Especially considering that you can save a lot of work by using an abstraction layer someone else created (a 3rd party library or game engine).

But let's assume that you already evaluated your options and came to the conclusion that it is both viable and worth the time to roll your own.

Then the main judge of your abstraction layer's software architecture are your front-end programmers.

  • Are they able to implement the game systems they want to implement without having to wonder about any low-level details?
  • Are they able to completely ignore that there is more than one graphics API?
  • Would it theoretically be possible to add yet another rendering backend without changing any of the front-end code?

If so, you succeeded.

\$\endgroup\$
  • 2
    \$\begingroup\$ One additional suggestion: is it worth emphasizing that the goal is to achieve the goals in the bullet points with the minimal level of effort--to just hit the target and go no further? Otherwise each one of these can turn into a rabbit hole for the typical developer who (myself included) often doesn't know when to leave good-enough alone. :) And also that the only reliable way to judge success is to develop and test the API iteratively with actual users; it's impossible to guess accurately, and leads to a rabbit hole scenario of over-engineering. \$\endgroup\$ – bob Nov 30 '18 at 15:48
  • 5
    \$\begingroup\$ I would add that, if you really do have to support multiple graphics libraries, there's almost certainly an off-the-shelf one that does everything you need to, so even then you really shouldn't roll your own. (Of course, there are exceptions, but if you're inexperienced enough to ask "how tight of an abstraction should I make" you're probably not working on a project that requires you to do it) \$\endgroup\$ – Fund Monica's Lawsuit Nov 30 '18 at 19:27
  • \$\begingroup\$ I'm not a graphics dev, but looking at compatibility frameworks from the history of databases and OSes, I'd add that it's almost certainly just not worth it to make your own general framework. The framework will almost certainly have to sacrifice performance for compatibility, which probably means you can't do much with those specialized features anyway. Existing frameworks are nearly certain to handle those issues better due to wider usage. Now, if you have the massive budget you describe and want to build a specialized framework (say for one game or series), that might be more doable. \$\endgroup\$ – jpmc26 Dec 2 '18 at 21:18
6
\$\begingroup\$

Start by identifying what you actually need out of the "wrapper" part of the API. It's generally very, very simple: you need the basic resources (buffers, shaders, textures, pipeline state) and a way to use those resources to construct a frame by submitting some draw calls.

Try to keep any high-level logic out of the wrapper portion of the API. If you implement a clever scene culling technique in this portion of the API, well now you are on the hook to duplicate that logic in all of the backend implementations. That's a lot of extra effort, so keep it simple. Scene management should be part of a higher-level portion of the API that uses the wrapper rather than being part of the wrapper itself.

Choose the targets you will support and understand them. It's hard to write decent wrappers for "everything," and you probably don't need to (arguably you don't need to write a single wrapper, either, as noted in Philipp's answer). It's almost impossible to write a decent wrapper if you don't know the APIs you're going to wrap already.

Evaluate the state of your API regularly. It should in general have a smaller surface area than the underlying wrapped APIs; if you find yourself creating one-to-one wrapper types for every D3D structure or every OpenGL function call you are probably veering off course.

Look at what work has gone before. Sokol and BGFX are APIs that provide levels of agnosticism that may be useful to you, and are relatively easy to understand (the former especially).

\$\endgroup\$
3
\$\begingroup\$

Another point not yet mentioned which you should consider is what your performance goals are. If your goal is to have a graphics library that will yield good performance out of a cheap hardware platform, but also be usable on a variety of platforms that are vastly more powerful but use a different API, it may make sense to design your API around whatever abstractions are used natively on the platform where performance is an issue. Even if this causes a 50% speed degradation on the more powerful platforms, it may be worth it if it allows a 20% speed improvement on the platform where performance matters most.

\$\endgroup\$

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.