Thursday, July 25, 2013

Decisions about rendering APIs

Since i started on this path of trying to make my game framework, i was thinking about implementing an abstract rendering API which would be able to have anything beneath it, such as Direct3D[9|10|11] or OpenGL[2.0|3.0|4.0|ES|...]. However, there are a few problems with that.

First, i don't know any OpenGL version, and i barely know how to do stuff in Direct3D9. I'm familiar with concepts of the vertex and index buffers, the deprecated-fixed-pipeline FVF (Flexible Vertex Format), texture binding and such, but i haven't actually used them.
Second, i don't know the differences between OpenGL and Direct3D. I've read about some of them, like having a different orientation of the coordinate system (OpenGL is right handed, D3D is left), which is just the tip of the iceberg (there are too many to list, and i've forgotten most of them).
Third, it takes a huge amount of time to write an abstract rendering API which can successfully handle all of those differences, and that's time i simply don't have. I don't want to be bothered thinking about how to make something abstract enough so it would work with.... something i know nothing about!

So in interest of my not going crazy, i've decided to stop thinking about OGL, and simply code like only one rendering API exists, and that is Direct3D9. I know people are moving on from D3D9 to D3D11, but that one is locked to Win7 or higher, while D3D9 can still be used by WinXP users. Even worse would be to start working with D3D11.1 or .2, which is locked to Win8, but as luck would have it, i don't have Win8, so it's out of the question anyway.

So the real question is, how do i make a rendering API which wraps around D3D9, but without having most of my game code (or engine code for that matter) riddled with D3D calls which would be insanely hard to refactor or maintain sometime in the future?
Thanks to the super awesome site GameDev.net, i found a semi solution. Well, several, actually, but i'm gonna talk about one i managed to understand.

The low level renderer implementation (D3D9 in this case) has a certain number of things that it knows about, and these are vertex and index buffers, textures, vertex definitions, shaders and render states (the list is maybe slightly larger than this, but these are the concepts that i remembered and understood). In order to draw anything to the screen, all the renderer wants and needs are these.

So you make a class for each of these concepts, that has a concrete implementation under the hood. You still only support the one rendering API, but everything is tucked nicely in a small class that handles only its responsibility. Which is what the SRP (Single Responsibility Principle) is all about. And the solution doesn't mean you're going to have D3D calls all over the place, it means you're going to have a bunch of small classes passed around, you'll interact with them by using their interface (like, passing a vector<vertex> to a function that internally copies the whole thing to IDirect3DVertexBuffer's memory location), and the only class that knows how to use their data is the low level renderer.

This also has another benefit: by implementing these classes to use a different underlying rendering API, i can just recompile the project, and get another executable which uses the other API. So there's actually no need to make an abstract interface to support multiple rendering APIs in runtime, but rather have another executable for the other API. This concept is similar to how you make multi platform code, simply replace a small set of classes to use a different implementation (but uses the exact same name), recompile and you're done.

My focus is going to be purely on D3D from now on, until now i still had some lingering thoughts about making it abstract for runtime usage via DLL loading, but having so little time, i think it's better to just do what i know right now, and worry about stuff i know nothing about later.

No comments:

Post a Comment