For many years I’ve been evaluating and using various game specific open source libraries and at the same time I was designing and implementing my own. Despite the fact that many libraries are quite competent on what they do, their overall design leaves a few things to be desired. Some of the concepts described here sound naive but you can’t imagine how many libraries get them wrong. This article focuses on a few good practices that designers/implementers of performance critical libraries should be aware of.
This article is built around five pillars:
- How public interfaces should look like.
- Data oriented design.
- The importance of thread-awareness.
- Memory management.
- And some general concepts.
Who is the target audience:
- People who want to create performance critical libraries/middleware.
- People who want to attract serious users and not only hobbyists.
- Mainly directed to opensource.
Who is not the target audience:
Continue reading “Designing good C++ game middleware”
- People who want to create middleware solely for their own amusement.
- C++ purists.
There are some universally accepted truths in software and one of those truths is that all software has bugs. Some people are more careful than others but nobody is perfect and nobody can write 100% bug free code. Another truth is that hunting bugs is rarely a fun job. It shifts the focus and sometimes it forces a mental context switch (graphics guys know that context switches can be slow). I always considered AnKi as a playground. A codebase where I can experiment, learn and have fun. Since the inception of AnKi I knew that bugs and debugging in general will spoil that fun so I had to find a way to have a robust codebase so I can focus on the fun parts. I think to some degree I’ve managed to avoid the endless pains of debugging by using the tricks I’ll describe in the following lines.
The first and, in my opinion, the most powerful tool against bugs is the almighty assertions. AnKi has a ton of assertions. To put it into perspective, there are 852 assertions throughout the code and it’s roughly 1 assertion every 84.8 lines of code. Some subsystems like util (AnKi’s equivalent of STL) have to be extremely robust so this particular subsystem has 1 assertion every 37 lines of code. In practice AnKi almost never segfaults when assertions are enabled. In the majority of cases an assertion is triggered and finding out what’s wrong is a mater of minutes.
Another thing I’ve tried early on and improved over the years is based on the idea that the commonly used subsystems have to be as robust as possible. One of these subsystems is util (contains a list, something like std::vector, hashmap, memory management, strings and other). Util is a widely used subsystem and it’s extremely important to be reliable. As I mentioned earlier, util is rigged with assertions. On top of that there are quite a few unit and performance tests. So unit tests is another weapon against bugs. In AnKi’s case unit testing is not that excessive but they cover some commonly used cases.
AnKi loves wrappers. To protect against common mistakes there are some wrappers that have debug capabilities. One example is anki::Array (something like std::array). anki::Array wraps constant size arrays and it asserts on out of bounds access (unlike std::array). Also, there is SArray that is constructed using a pointer to some memory and a size. SArray will also assert on out of bounds assess. These wrappers, and more, ensure that stupid mistakes will be caught.
The last weapon is the excellent valgrind and more precisely valgrind’s memcheck tool. Once and a white I run a demo on top of valgrind just to make sure that everything is in order. There were some occasions where all of the above methods failed but valgrind found the culprit. Unfortunately in complex workloads valgrind can be quite slow.
Of course C++ bugs are not the only source of bugs. Half of the bugs in AnKi are graphical glitches and rendering problems. Graphics related bugs heavily depend on the GPU and the driver and debugging those can be painful. Spending a whole week debugging can be kind of painful. There are some tools that help graphics related problems but those will not be covered by this article.
To sum up, what worked for AnKi is tons of assertions, robust core subsystems, some unit testing and quality code.
That’s all for now.
UPDATE: Adding a new concept
UPDATE 24/Apr/2013: Fixing concept 3
The new C++ standard, namely C++11, is here at last; offering many additions to the language’s core as well as in the companion library, the STL. Without doubt it will change the way we think and work but nobody can predict if it is for better or worst. The experimentation period is nearly over, only a handful of features missing from GCC and clang and the C++ engineers will have to learn and master the new tricks in both fronts (core and STL). For those familiar with boost library the second front should be an easy transaction to the new STL, for the first though we need tutorials and lots of them. This little article is one tutorial that extends the already published ones.
One of the new cool features is the variadic templates, simply put, templates with variable number of template parameters. To put it into context this is a variadic template:
Continue reading “C++11: Variadic templates. Part I”
When constructing a C++ class a good practice is to never have any member variables in public scope and this includes containers. In this little article we will discuss how to have a C++ container as a private member and be able to expose its contents without exposing the container itself.
Continue reading “C++ Class: Accessing contents but not the container”
Multithreading is a concept that exist for many decades in computer science, nevertheless, only in recent years it become a trend in game development with the arrival of multi-core CPUs in our home PCs. In this small article we will discuss how AnKi utilizes the power of multiple processors. Also the source code of an example can be found at the end of the article.
The concept of multiple threads offers many advantages in applications that execute many uniform jobs at the same time, for example a webserver needs to serve multiple hosts at the same time and without a host waiting for a previous request to finish. A game application though used to have a very standard and linear flow. For example, we first update the AI, then the physics, then we update the world, we do visibility determination and lastly we render, then we repeat the same again. Sometimes its difficult to execute some of these distinctive steps in parallel because the data of the previous step will be used by the next. I bet high class development studios have found ways to blend these steps but in AnKi we use a more simple approach. We use multiple threads to run uniform jobs in parallel.
One good example to illustrate how AnKi uses the power of multiple threads is the visibility determination algorithm. One step of visibility determination is the test for every renderable scene node against the camera’s view frustum. If we have N nodes to test and M threads we can roughly assign for testing the first N/M nodes to the first thread, the next N/M nodes to the next thread etc.
Continue reading “Multithreading: Threadpool”
C++ Standard Template Library (STL) offers a smart pointer called auto_ptr. auto_ptr is a template class that acts as a wrapper for a single dynamically allocated class instance. Its purpose and its usefulness is to deallocate the memory when this smart pointer gets out of scope. Its a way to do automatic garbage collection in C++ by saving us from the extra code writing. One extra reason to use auto_ptr is that it makes the code less error prone simply because it doesn’t rely on the programmer to do the memory dealocation.
In this short article we will discuss a case where auto_ptr fails to do what its supposed to do….
Continue reading “STL auto_ptr: A bad idea”