Friday, June 26, 2015

Reflection and Automatic Serialization

Before you ask, no...I'm not talking about rendering reflections in a graphics context :)

Reflection is a term used to denote the ability for objects in a language to know about themselves at runtime (or "reflect" on their data). Other languages that have this concept built-in may also call this introspection. Either way, knowing about your objects at runtime can have some pretty amazing benefits, including:

  • A queryable registry of all of your objects and types
  • Objects can iterate over their member variables
  • Easy function binding with a scripting language
  • Automatically expose objects to an external editing tool
  • Automatic serialization and deserialization
These benefits can really save a lot of time and code complexity later on and having it built into your language of choice really makes it easy. However, Qi predominately uses C++ which does not have any concept of reflection at all. Therefore, we have to roll our own!

Tuesday, April 14, 2015

Porting to Windows

If you've been following the development of Qi so far, you may have noticed that a few of the things the engine is doing are generally UNIX-only (__attribute__ ((aligned(16))) comes to mind). That's because the main development machine for Qi is a Mac and everything done up until this point has been in Xcode. However, in the interests of having more compliant (and generally better) code I've decided to make sure that the engine can compile with multiple compilers (and therefore multiple platforms). The first target is Windows. However, the port wasn't as straightforward as I had hoped....

Friday, March 20, 2015

Building your Core library (Part 4) - Containers

Just about any software you write needs some way to store and parse data, game engines being no exception. The most common (and easiest) use of containers comes from the C++ Standard Template Library (STL) which provides easy access to containers such as vectors, lists, queues, maps, etc. However, game engines rarely rely on the containers in this library for multiple reasons:

  • You lose direct control over the container's performance
    • Game engines are performance-critical and anytime you lose the ability to optimize something it could make an impact on how high your framerate can be. Oftentimes the containers in STL are fairly generalized whereas the equivalent container in a game engine can be written for a more specific use-case, tuned for better performance.
  • How memory is allocated
    • By default, STL containers will use the operating system's memory allocator to allocate/deallocate memory. While you can supply custom allocators, they're a hassle to work with, are unable to carry state information, and force your code to have template creep. Game engines often need to have very specific control over how memory is used, sometimes even forbidding deallocations by game code altogether. This need for control usually rules out any use of the STL memory allocators. 
  • Underlying container algorithms
    • From a compiler implementation viewpoint, STL is just an API. How it's implemented underneath is totally up to the specific compiler you're using (GCC, CLANG, Visual Studio, etc.). Therefore, on one platform an std::map may be a red-black tree and on another it may not. Even if it is a red-black tree, that doesn't necessarily mean it's a good idea for your use case. Maybe your data is fairly small (< 100 elements). In that case, a red-black tree can be pretty overkill for storing your data as the overhead of processing it can be more expensive than just searching some linear list. There are of course other searchable STL containers (such as std::set) but the point is, you can't control the underlying implementation algorithm to be specific for your needs.
  • Cache locality
    • Properly exploiting cache locality can be a huge win for a game engine's performance. Certain algorithmic complexity requirements of STL operations may force the underlying implementations to store data in a way that can ruin cache locality (for example, constant-time deletion in an std::map). This kind of storage can be a nice win for lots of insertions/deletions into a container but oftentimes in a game there's more querying of the containers than there is container modifications.
  • Serialization
    • In a future post we'll get into the intricacies of serializing game objects. The things to keep in mind here is that any data stored in a game engine's containers can get automatically serialized for the user without them having to do anything. This can be much simpler than a user having to manually serialize data located in an STL container.
  • Container naming
    • This point may seem a little contrived, but oftentimes things like vector or map in a game engine would refer to a mathematical vector or a map designating some region of a game level (or a texture map). This kind of naming can be confusing for any programmers not familiar with STL. However, having containers named obvious container names (such as array instead of vector) may go a long way in removing any confusion.
These are just a few points to use in arguing against the use of STL in any performance-critical code. I'm not saying that STL isn't great or that you shouldn't use it, just that these kinds of things should be considered before using it all over a game engine. Personally I'm a large fan of how easy STL is to use, especially because many programmers understand it and can fairly easily pickup code where it's in use. 

Now that we've gotten through all of the reasons Qi doesn't use STL, let's get into the custom containers currently in the engine

Tuesday, February 24, 2015

Unit Testing

This week I decided to take a quick break from the series on building the core Qi library in order to talk about an important part of any code project, testing! Even though this might not be a very interesting part of the project, it's important to get into place earlier rather than later. If you have an easy-to-use unit test system and already have it in place and ready to go, it's fairly painless to add new tests as you add new features to the engine. If you wait until later, it's becomes an impossible task of writing tons of tests to exercise features that you haven't worked on for months.

You could always write your own test system but this is very solved problem with many good options out there. Previously I've made pretty extensive use of UnitTest++ which has a very simple-to-use interface and works pretty well. However, with Qi I've decided to change it up and make use of Google's gtest. This is a pretty lightweight open-source unit test framework that you can easily integrate into any project. Setting it up is pretty easy and you can follow the documentation for your specific platform.

Tuesday, February 17, 2015

Building your Core library (Part 3) - Math

Games are math. Just about every system you create will have some sort of mathematical component to it. Something as seemingly simple as having a character walk forward can use obstacle avoidance, animation blending, and position transformation just to name a few. That doesn't even touch the mathematics required to actually render the character. Luckily, as complex as the math in a game can get, it all really boils down to just three simple base concepts:
  • Vectors
  • Matrices
  • Quaternions
These different concepts will be so crucial to the rest of the engine that it's important to think about a generic API for ease of use as well as performance. Qi will handle this by abstracting away the complexities of dealing with any underlying math operations as well as exploit SIMD operations to get better use out of the underlying CPU.

Thursday, February 5, 2015

Building your Core library (Part 2) - Logging

Developing a game engine is a complex task that will be fraught with many logic bugs. To save time later, it's helpful to develop the engine code with some kind of system that makes it easy for the user to report information about the current state of the engine. This is where a logger comes into play. Imagine that you've recently found a bug in the engine and want to know the flow of events that led up to it. You could add a bunch of print statements all over the code and binary-search your way to the solution hours or even days later. Or, you could just go look at the output log that the engine already generated for you! An example log may look something like this:

Engine Log Output ----------------------------------------
Engine.cpp(277)  : Initializing Rendering System
Renderer.cpp(135): Loaded shader "bloom"
Renderer.cpp(135): Loaded shader "blur"
Renderer.cpp(116): Can't load shader "hdr"
Renderer.cpp(170): Renderer initialized
Engine.cpp(278)  : Initializing Physics System
.....
Engine.cpp(278)  : Physics system failed to initialize!

Obviously you would want much more information that this, but that should give you an idea of what a simple log could look like. It's important to color-code the log because later on when the log has tens of thousands of entries, looking for specific colors can be a very quick way to get the information you need.

Let's make a quick list of features that the Qi logger should support:
  • User friendly
  • Threadsafe
  • Automatically add information to the log about where the message came from
  • Logging channels
  • Per-channel event registering
  • Automatically log all messages to a file in case the engine crashes

I'd like the logger to be able to work as simply as this:

Qi_LogInfo("Initializing screen size (%u x %u)", screen_width, screen_height);

Tuesday, February 3, 2015

Building your Core library (Part 1)

When you think about a game engine, there are many aspects that probably jump to your mind first: rendering, physics, AI, etc... While these are all of the more "user-facing" parts of an engine, there are many parts which go unseen that are just as (if not more) important. Just to name a few (in no particular order):

  • Debugging Facilities
    • The engine should log messages about what it's currently doing and output it to a log somewhere. This can be an invaluable tool for a programmer trying to understand the flow of events in the engine.
    • Messages are great, but oftentimes to really understand a bug you need to visualize the problem. Debug drawing utilities allow programmers to insert drawing code for simple primitive types into the rendering command stream for easy visualization.