Tips on abstracting rendering interfaces for multiple renderers?

Started by
10 comments, last by _the_phantom_ 11 years, 8 months ago
Greetings!

I've long used D3D9 directly in my coding for years, and thought I'd like to undertake learning D3D11, and what better way to do this than to work out a small game idea I've been toying with. But even though XP is on it's way out, I still want my friends and others on XP to be able to run my projects, so I thought I'd like to abstract away the actual rendering calls so I could more or less use either one in my code without specifically targeting one or the other. I've been programming for years now, but to be honest, I'm still a bit green when it comes to situations like this. D3D9 and 11 seem largely different enough that I'm not sure how I could efficiently do this. I'd also be interesting in taking what I've learned and applying it to OpenGL so that some day in the (far) future I could consider cross-platform releases.

I stumbled across this page http://troylawlor.co...enderer-part-1/ -- It seemed to be everything I was hoping for but to my dismay it seems they either haven't finished the next installment or have abandoned it, given that several months has passed since part 1.

Does anyone have any tips, resources, articles, or anything they can share on doing this sort of thing, abstracting away rendering API? I do a lot of work through SlimDX but I'm not afraid of C/C++ (used that for years before I got in bed with C#) so I'm not really afraid of the language used in the articles or what have you. Anything would help. Thanks!
Advertisement
D3D11 is a stricter API than D3D9 (or OpenGL for that matter), as instead of a large, free-form state machine you have a rather limited set of state objects. Therefore, to make sure you're using D3D11 performantly and can take full advantage of its features in the future I'd recommend basing your abstract API on the D3D11 model (state objects, constant buffers) and emulating it on D3D9 and OpenGL as needed, instead of the other way around.

Here's one example of an abstracted rendering API which provides implementations on D3D11, OpenGL 3 and OpenGL ES 2 (the implementation is not open source, though):

http://clb.demon.fi/gfxapi/
Take a look at Tesla Engine: http://www.tesla-engine.net/

The guy there (I think he's Starnick from around the forums here) has a great design for a multi API renderer that is really interesting to look at.
Ugh, abstract bases classes. Not a fan.

For the most part I prefer low-level implementation functions and simple data structs, with the implementation of both being determined at compile time based on the platform I'm building for. So there might be a Texture.h with a function "CreateTexture", then a Texture_win.cpp that creates a D3D11 ID3D11Texture2D, then a Texture_ps3.cpp that does the PS3 equivalent, and so on.Then if you want you can build high-level classes on top of those functions.

You can actually use the same approach for more than just graphics, if you want. For instance file IO, threads, and other system-level stuff.

Ugh, abstract bases classes. Not a fan.

For the most part I prefer low-level implementation functions and simple data structs, with the implementation of both being determined at compile time based on the platform I'm building for. So there might be a Texture.h with a function "CreateTexture", then a Texture_win.cpp that creates a D3D11 ID3D11Texture2D, then a Texture_ps3.cpp that does the PS3 equivalent, and so on.Then if you want you can build high-level classes on top of those functions.

You can actually use the same approach for more than just graphics, if you want. For instance file IO, threads, and other system-level stuff.


That's actually an interesting idea.

Thank you everyone for your replies! I'm going to go over what I've got in front of me and if I have any more questions, I'll come back.

Ugh, abstract bases classes. Not a fan.

For the most part I prefer low-level implementation functions and simple data structs, with the implementation of both being determined at compile time based on the platform I'm building for. So there might be a Texture.h with a function "CreateTexture", then a Texture_win.cpp that creates a D3D11 ID3D11Texture2D, then a Texture_ps3.cpp that does the PS3 equivalent, and so on.Then if you want you can build high-level classes on top of those functions.

You can actually use the same approach for more than just graphics, if you want. For instance file IO, threads, and other system-level stuff.


I've made a platform agnostic renderer using your method and abstract base classes, I found that it was a giant pain managing all the platform defines to make sure that the proper helper structures get included, and I found that it was really difficult to abstract around all of the strange features of each renderer using the compile time solution. While I've also started working on a new project using abstract base classes. Why do your prefer compile time to abstract base classes, and how do you handle platform scaling, like D3D11 feature levels or OGL levels?
Perception is when one imagination clashes with another

I've made a platform agnostic renderer using your method and abstract base classes, I found that it was a giant pain managing all the platform defines to make sure that the proper helper structures get included


We have our structures in one header file, with one other header file that includes the right header based on the platform. I can't imagine why you'd need more than that.


and I found that it was really difficult to abstract around all of the strange features of each renderer using the compile time solution.


How does compile-time polymorphism at all limit you in terms of your ability to abstract out higher-level features? You can do all of the same things you can do with abstract base classes (if not more), the only difference is you don't eat a virtual function call every time you need to do something. I mentioned dealing with the small, low-level building blocks of a renderer but you can also have different platform implementations of higher-level features.


Why do your prefer compile time to abstract base classes, and


Like I already mentioned, I prefer not having virtual function calls and indirections all over the place.


how do you handle platform scaling, like D3D11 feature levels or OGL levels?


I don't, because I don't care about them. I mainly deal with consoles, which obviously skews my preferences quite a bit.
Ah I see, I had my helper structures in their own namespaces in their own files, like texture would be in TextureDX11.h and it would define a structure and any sort of DX11 specific functionality, so I had multiple files.

On your second point, I see your point. My problem was the way I had the actual architecture structured, not the compile time vs abstract base class approach, so oops.

And on the third point; fair enough.

Thanks for your insight.
Perception is when one imagination clashes with another
With dx9 you can run at most pixel shader model 3.0. If you are fine with that, there is no strong reason for you to migrate. I myself am using a deffered renderer, that does not need higher shader model, for my shaders have few instructions but run very often before target is rendered. Actualy, with deffered rendering, your shaders are very strict but passes often. I have been thinking that I would need to move onto higher pixel shader model, but since I moved onto deffered rendring, I can do miracles without long shaders.
Wrote a D3D8/9/10 GL2/3 engine years ago, and a GLES2/3+D3D11 one recently.
In both instances I decided to follow D3D10/11 API as it made the most sense, and like MJP I just include the right files for a given config to avoid virtuals.

The benefit of doing that over getting to a higher level of abstraction on top of the API is that I can write the abstraction layer for all of them at once, with very little API specific code.
(Of course if you want to write something that runs on all the API then you need to make sure that you use features common to them all.)

The only thing I've not taken care of is converting D3D11 HLSL to GLSL (seems to be the best way around as they carry more meaning than GLSL).
-* So many things to do, so little time to spend. *-

This topic is closed to new replies.

Advertisement