There are never enough render engines..
After my post on the hidden cost of C++, Vince suggested I go write a small game in C, see how I like that.
While that’s an interesting idea – maybe for another day – I’m interested in looking at larger-scale problems than that. And since I’m lately itching to write a renderer again (the last one was 1998), that’s where I started.
The goal of this project is to explore the “pressure points” in writing games in C++1, to see if in some of these areas, the pain and delay can be alleviated. To make it as painful as possible, it’s of course going to be cross-platform. That means OS X and Windows, unless somebody wants to gift me a devkit for Xenon or PS3…
And even after that first bit of code, just hoisting out the most basic platform abstractions, a few lessons are to be had.
Banned Forever
I hope to keep the preprocessor mostly banned, except in some very limited areas where the libraries abstract actual hardware. It’s cause for a lot of confusion in real-world projects, so let’s try to keep it out. It is interesting to think if you could achieve the same effect without a preprocessor, just with language constructs – and what those would be.
Too Useful to be banned
Some C++ features are too useful to be banned. Writing a render engine in pure C, as Vince suggested, is possible, but tedious. So I guess I have to loosen my stance and allow some features to creep back in.
namespaces
Sure, you can prefix every single function, but it’s a messy business. It carries no performance cost to have them, so they’re in.
limited classes
Mostly, to get the convenient syntax of member function invocations. On non-virtual members, this carries a predictable cost. (I.e. I can predict what code the compiler will generate without having to look up the class API). Casts and copy constructors are still out, since they can implicitly generate code that I won’t be aware of when examining code.
So, basically, C-style structs that have member functions and access protection are what’s allowed.
With both of these, I’m curious about their impact on compilation time.
Missing Features
Some features are missing from C++ that would be extremely useful:
Anonymous functions.
Yes, the new standard has them – but at a high readability price.
Headerless compilation
Header files are a completely pointless waste of time, a remnant from the late 70’s. As a side effect, that would allow mapping platform-specific enums to abstraced enums without having to expose the platform-specific header that contains them.
Cleaner Syntax
Braces/semicolons do add a lot of noise. Can I get a whitespace-scoped language, like Python?
Don’t care
There are many issues I don’t care about right now. Consequently, I’ll go “off the shelf” with them.
math libraries.
I really don’t want to write the 22nd implementation of a vector class, thank you. I’ve written enough of that. Since they’re all owned by my employers, I’m opting for an open-source one. So far, CML is the chosen one. It is, rather poignantly, making the point that this is a facility that’s sorely missing from C++ for game development.
Window System Code
While I really do want to examine the effects cross-platform code carries, some things are too gross to be touched by humans. I started out using GLUT, but its shortcomings meant native window handling. That’s not what really concerns me here, so I’ll be basing things on SDL for now – at least for purposes of handling the windowing system.
Memory management
I didn’t need to touch that yet, but if it comes to that, there’s dlmalloc, or Fluid Studios’ memory system (no link, since their atrocious website doesn’t have links – it’s all Flash. But you can find it by starting here), and probably many others. And unless I absolutely have to touch memory management, I don’t want to go there.
-
I’m not sure if that’s even relevant for much longer. I see the number of teams writing entire engines definitely shrinking, since it’s very expensive. Often, off-the-shelf engines (or something another team in your company wrote) will be good enough. Since I’m a systems-level gal, I hope to stay on a team that works on an engine, though. Hence the focus on it. ↩
I was thinking about something similar recently. Basically the idea was to replace class inheritance with template specialization. This would remove the “virtual function table” access during runtime and would resolve everything compile time. Should probably go well with In-Order architectures. This of course has some drawbacks:
Since templates can’t be implemented in cpp files most code would be written in header files which affects compile time, readability and means that you can’t easily make a precompiled library from it. Also according to wiki some compilers have problems with partial template specialization but the VS and the GNU compiler should work which in my eyes covers all platforms. And lastly you will not be able to dynamically cast alot.
This is some code im currently playing around with. Dont really have much time with it and it really is just one of the crazy ideas.
static class MyArch { public: static char* Name() { return "MyArch"; } }; #ifdef MYARCH typedef MyArch Architecture; #endif //////////////////////////////////////////// template class SomeClass { public: void foo() { printf("BaseImplementation"); } }; ///////////////////////////////////////////////////// template<> class SomeClass { public: void foo() { printf("MyArchImplementation"); } }; int __stdcall main() { SomeClass someClass; someClass.foo(); return 0; }Florian
20 Nov 09 at 4:04 am
Great!
cat got my line breaks
Florian
20 Nov 09 at 4:05 am
“that would allow mapping platform-specific enums to abstracted enums without having to expose the platform-specific header that contains them.”
Externs consts can solve this, if you don’t mind some declaration cruft and (possibly) lookup overhead. If you do, why not generate an abstracted header in a custom build step?
Mike
6 Dec 09 at 8:32 pm
@Mike: The extern consts certainly solve this, if in a rather crufty way. I’m not too worried about lookup costs – after all, most/all graphics objects should be binary blobs to the engine – so I’ll give that a try.
The pre-processing is an interesting approach, but I’d like to reduce build times – more dependency checks certainly don’t help there.
Again, in an ideal world, we’d abandon the silly idea of headers. It’s really a crutch that pretty much all languages except C/C++/ObjC have given up on. Well, Erlang still clings to it, which does not make me happy either ;)
groby
16 Dec 09 at 7:34 pm
@Florian: Line breaks fixed.
As for a template-specialization based architecture: That’s nasty ;) Especially in terms of compile time. Templates are a major pain there.
And readability and short compile-time are crucial properties for a successful project. The longer it takes to both build the code base and come up to speed with it, the longer it takes to actually build a game – and that’s too expensive already.
groby
16 Dec 09 at 7:51 pm
GO language.
C is really too painful to write games, and you have your issues with C++ that are totally reasonable.
Try the GO language.
golang[dot]org
Pedro
3 Feb 10 at 11:17 pm
Hi Pedro!
I’ve definitely given GO a look – but it seems to have a major shortcoming in that it does not seem to allow manual memory management. (And yes, for many areas of game development that’s unfortunately still crucial).
I hope I’m proven wrong about that in the long run and GO gets extended in that direction.
Rachel
9 Feb 10 at 7:19 am