Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I've seen lots of questions around this area but not this exact question so apologies if this is a duplicate.

I'm making a small 3d game. Well to be honest, it's just a little hobby project and likely won't turn out to be an actual game, I'll be happy to make a nice graphics demo and learn about 3d rendering and c++ design.

My intent is to use direct3d9 for rendering as I have some little experience of it, and it seems to meet my requirements. However if I've learned one thing as a programmer it's to ask "is there any conceivable reason that this component might be replaced by a different implmentation" and if the answer is yes then I need to design a proper abstraction and interface to that component. So even though I intend to implment d3d9 I need to design a 3d interface that could be implemented for d3d11, opengl...

My question then is what level is it best to do this at? I'm thinking that an interface capable of creating and later drawing

  • Vertex buffers and index buffers
  • Textures
  • Vertex and Pixel "shaders"
  • Some representation of drawing state (blending modes etc...)

In other words a fairly low level interface where my code to draw for example an animated model would use the interface to obtain abstract vertex buffers etc. I worry though that it's too low level to abstract out all the functionallity I need efficiently.

The alternative is to do this at a higher level where the interface can draw objects, animations, landscapes etc, and implement them for each system. This seems like more work, but it more flexible I guess.

So that's my question really, when abstracting out the drawing system, what level of interface works best?

share|improve this question
    
It may not answer your question entirely, but I would advise looking at sources of DirectX+OpenGL engines like OGRE and Irrlicht to see how they've abstracted it. –  The Communist Duck Mar 5 '11 at 17:51
add comment

5 Answers 5

up vote 7 down vote accepted

Apologies for the length of this response!

"is there any conceivable reason that this component might be replaced by a different implmentation" and if the answer is yes then I need to design a proper abstraction and interface to that component.

"Any conceivable reason" is far too extreme a stance to take. Almost every function could be parameterised in some way, every module given some different strategy or policy, and every constant tweaked. If you provide an extra level of abstraction for each of these then you end up writing 2x as much code for no immediate gain, just a potential gain later if you ever require that flexibility.

If you go down the route of providing interfaces so that each of these things can be a facade for one of several different options, you then have to decide how you supply these different options, including defaults, and when you try and make that as simple as the original object was you often end up with a whole extra layer on top. This can (and does) get absurd in a lot of real world 'enterprise' software - may I humbly suggest reading this satire piece which sadly hits too close to home in many cases: Why I Hate Frameworks.

The biggest argument against this is complexity. Let's make the (probably incorrect) assumption that a good interface exists that could apply equally well to two 3D rendering APIs, and that someone is able to describe this to you in an answer here. You would then be working to this interface, which would, due to its multi-purpose nature, undoubtedly cover elements that DX9 does not use - for example, the plethora of API extensions that are available in OpenGL but not in DirectX 9. This is an added complication that will make your code harder to understand and maintain, and slow your learning process. It's bad enough when using an existing multi-platform library such as SDL, because people are forever coming across odd functions and restrictions that exist purely to fix something on one platform, but you'd not only have to learn why that part of the interface is there but also how it may or may not interact with the parts that you are currently about to write. eg. You'd end up writing abstract objects that encapsulate aspects of OpenGL extensions that are necessary in your core rendering code, even though you have no way of using them yet.

However, in future, once you are competent with how the DX9 system works, you can look into how the OpenGL system operates. You will then see how you need to adapt your existing system to make both parts work, and will do so, with the handy existence of your existing DX9 code path as an effective unit test to ensure your refactoring is correct.

You are lucky in that you are working with one of the most fluid and malleable materials known to engineering - source code stored as computer data. Embrace that blessing by making changes to it when you know you need them, rather than doing them months in advance and burdening yourself in the meantime by having to write to an abstraction you aren't gaining from yet.

share|improve this answer
    
This is how I feel, but at the same time the common advice I see is that abstracting platform-specific code for a game is easy to do and should be done... I don't know what to think. –  Rei Miyasaka May 27 '12 at 9:13
add comment

However if I've learned one thing as a programmer it's to ask "is there any conceivable reason that this component might be replaced by a different implmentation" and if the answer is yes then I need to design a proper abstraction and interface to that component.

This isn't really answering your question, but from my experience as a programmer, premature generalization is just as bad as premature optimization. You rarely gain anything and have to maintain it over the course of the project.

My advice is to straight up use D3D9 client code in your project, and when you determine that you need to switch interfaces, make an interface with all the things you're currently using. Programmers are terrible at predicting what they might need, doubly so in your case where you're not terribly familiar with the material, so this way you're doing the least amount of work.

See also: http://c2.com/xp/YouArentGonnaNeedIt.html

share|improve this answer
    
The trouble with this is that if he later has to go for an abstraction, he then has to re-write all his interface code. And if D3D9 was globally included throughout his project, it's going to be damn hard to refactor all that code. –  DeadMG Mar 5 '11 at 18:26
1  
He's going to have to rewrite everything anyway when he has a use case that isn't covered by whatever assumptions he makes now anyway, though. Refactoring should be embraced, especially for a learning project. –  Tetrad Mar 5 '11 at 19:30
    
I'm exagerating the case a bit. I too hate unnecesary abstraction. But in this case I can say actually there is a good chance I'll want to do this with a different implementation... –  JohnB Mar 6 '11 at 16:46
add comment

Hm, looks like I don't have enough reputation to post comments to responses.

Both Kylotan and Tetrad gave excellent answers. What I would add is to suggest you separate rendering code from the game logic -- this will serve several purposes:

  • your code will be cleaner
  • it will be easier for you to make changes to your renderer without rewriting half of the game logic each time
  • eventually, providing completely different rendering modules is easier this way

As an added bonus, this way you will build your abstraction gradually as you work on your project. As you say, this is a learning project so take it easy and make it simple, adding complexity and abstractions as you go instead of making grand plans as you probably lack expertise to get it right initially (as Tetrad also suggested).

share|improve this answer
    
Oh indeed! But the question is what level do I separate it? At the level of drawing a whole town, or the level of drawing a triangle? Or where in between –  JohnB Mar 6 '11 at 16:48
    
A reasonable way would be to keep rendering API calls separate. That would probably be about 'drawing a building or a car' in your analogy (considering your buildings and cars are single objects :) ). An example: you want to animate your object (for simplicity let's assume only one can be played at a time). A game object could have a list of available sequences and their length and set the active animation and its speed while the renderer would manage, well, the rendering part. You may also need to obtain data back from the renderer, e.g. object's collision shape may change during animation. –  Gilead Mar 7 '11 at 22:27
add comment

You can't write a low-level abstraction over this kind of thing, because the details of a shader are completely different between D3D9, 11, OGL, etc. It's simply impossible.

You could check out th abstractions implemented in, say, SFML, SDL, OGRE if you want to see what other people have chosen to do for rendering abstractions.

share|improve this answer
add comment

I've tried to write an API that wraps around both D3D and OGL neatly. It's a massive undertaking, I learned. Many of their differences can be reconciled. For instance, loading shaders. I effectively encapsulate D3D and OGL shader code in my own format. IE: In D3D, you need to specify the shader profile, so the file itself has the shader profile written in it. When the encapsulateed shader source is handed to my API, it calls the appropriate D3D shader creation method with the appropriate shader profile and such.

However, there are still plenty of differences that I haven't solved, nor might I ever, because I've decided that a higher-level abstraction is easier to design and satisfactory for my requirements. I'm currently working on something sort of similar to the D3DXEffect framework. Objects have a render-technique, which contains render passes, which have a set of render states, a set of shaders, and require certain inputs. This allows me to abstract more of the API away.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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