Quick update on Halogen

Hello everyone! I’ve been quiet for a while now on progress, and unfortunately this is because of a large increase in my workload both for school and work. I have been working on Halogen where I can, mostly on small tasks like commenting and cleaning up the code, but I have not added anything major since the last post.

I do not intend to stop developing Halogen! I actually have two posts in the pipeline that I need to finish writing and editing, one is about file and asset handling in Halogen, and the other is a more abstract article about modern rendering architectures and the features I intend to support for Halogen’s renderer. I hope to have some new code for the blog soon, and it’s looking like the workload should lighten in the coming weeks.

The roadmap hasn’t changed much since the last post, but to detail where my thoughts and research are right now:

  • Proper file and asset handling, from OS-level file management to abstracted assets and asset packs
  • Render architecture, from what data to package for draw calls, to the exact step-by-step process each frame
  • Restructuring app entry and game logic hooks for better performance – reduce virtual function calls in critical code paths

That’s primarily where my interests are at the moment, with the largest portion of my time spent focusing on asset handling for now. I hope to have new code to show and new concepts to explain very soon.

I mentioned I might rarely talk about life and travels, so here’s the first of those: I’ll be traveling to Denver in a few weeks for a Novo Amor concert! Being among my favorite artists, this will be a rather fun trip, and should provide me with a nice getaway to gather my thoughts and write better code when I come back. That’s how vacations work, right?

Thanks for reading!

Advertisements

Efficient input bindings

Hello again! Today, I want to quickly touch on how I handled input bindings in Halogen, and later on provide a bit of info on what’s being worked on now, and what to expect in the near future.

So, Halogen uses RawInput for its lowest-level keyboard and mouse handling. Using a keyboard with RawInput only provides us with a message that a key was pressed or released (and if you’re thinking about using RawInput, it should be noted that decoding that message can be a huge pain). Since RawInput doesn’t support any kind of polling (which would probably be too slow for realtime gameplay anyway), we have to use these messages to manually keep track of the state of each key. The same goes for the mouse, RawInput will provide a message with only the info that’s necessary, and it’s up to our input handler to make sure we properly track that information.

Before we get too much further, I want to explain what bindings are, and their value. First, here’s the terminology I use for discussing input in Halogen:

  • Action: any input that has a distinct “on” and “off” state, such as keys and mouse buttons.
  • Axis: any input whose value can vary, such as mouse position, thumbstick position, etc.
  • Trigger: an Action or Axis that is part of a Binding
  • Binding: A construct that maps a user-friendly ID to a set of Triggers

So, in Halogen jargon, a Binding is simply a mapping between some ID, and a set of triggers. Bindings are useful because 1) they allow us to map the same in-game action to different triggers without testing several inputs in our game-code (for example, you might want your game’s Reload binding to trigger from Keyboard R and Gamepad X), and 2) they provide a simple way for us to allow players to customize their bindings – and in this day and age, that should absolutely be a standard feature in any PC game.

One naive approach to implementing input bindings might look something like this:

struct ActionBinding
{
    const char* m_BindingName;
    Action m_Trigger;
    bool m_State;
}

void RegisterActionBinding(const char* bindingName, Action trigger)
{
    // Add the binding to some kind of map
    ActionBinding binding;
    binding.m_BindingName = bindingName;
    binding.m_Trigger = trigger;
    binding.m_State = false;

    m_ActionBindings[m_RegisteredBindings++] = binding;
}
...
void UpdateBindings()
{
    // please don't actually do this in your code...
    for (unsigned i = 0; i < m_RegisteredBindings; ++i)
    {
        Binding& binding = m_ActionBindings[i];
        binding.m_State = Core::Input::GetActionState(binding.m_Trigger);
    }
}
...
bool IsActionPressed(const char* bindingName)
{
    Binding& binding = GetBindingByName(bindingName);

    return binding.m_State;
}

It works, sure, but string comparisons are hardly quick enough for us to be using them so frequently. Bare in mind, inputs are tested several times every frame, which means we want it to be fast. In that regard, string comparisons won’t do. It’s also not particularly efficient to be storing the state of the binding in the binding itself; the state of each trigger is already stored in the main input handler, there’s no need to double up.

In order to optimize, let’s think about some of the traits of a binding system. First off, we know that each binding will probably need a user-friendly name, for UI elements. Next, we know that each binding will be (or at least, can be) unique from all others. Finally, at least for Halogen, I decided that having a distinct upper limit on the number of bindings allowed can help with optimizing. Knowing this, I came up with something like the following for Halogen:

struct Binding
{
    std::uint32_t m_BindingID;
    char* m_BindingName;
    Action m_Triggers[3];
}

void RegisterActionBinding(std::uint32_t bindingId, const char* name, Action defaultValue)
{
    m_ActionBindings[bindingId].m_BindingId = bindingId;
    m_ActionBindings[bindingId].m_BindingName = name;
    m_ActionBindings[bindingId].m_Triggers[0] = defaultValue;
}

bool IsPressed(std::uint32_t bindingId)
{
    bool state1 = Core::Input::GetActionState(m_ActionBindings[bindingId].m_Triggers[0]);

    // Get the state of the other triggers, if they're bound

    return state1 || state2 || state3;
}

const char* GetUiName(std::uint32_t bindingId)
{
    return m_ActionBindings[bindingId].m_BindingName;
}
...

enum MyGameBindings : std::uint32_t
{
    ACTION_RELOAD,
    ACTION_JUMP,
    ACTION_SHOOT
}

void Start()
{
    InputManager::RegisterActionBinding(ACTION_RELOAD, "Reload", KEY_R);
}

void MyObjectUpdate()
{
    bool reload = InputManager::IsPressed(ACTION_RELOAD);
    if (reload)
    {
        // Do reload stuff
    }
}

The binding layer keeps a simple array of a very small ActionBinding object, which is updated based on the info the user feeds in. The above is obviously pseudocode; the actual code in Halogen has error protection and verifies inputs to prevent reading unregistered bindings. It also uses the fixed-string implementation I detailed in a previous post instead of c-style strings, and it supports several other types of checks such as IsReleased, IsJustPressed (which is only true if any of the binding’s triggers were activated this frame), IsJustReleased, GetHoldTime, and more.

Users can create a simple enum of their input bindings, register them along with a string identifier, and poll with the enum values. Another huge upside to this is the bindings don’t store the state of their triggers; they just translate an ID to a handful of triggers, and push the trigger check to the lower-level input handler. As a result, this provides a relatively transparent layer through which we can access the state of our game’s inputs without hard-coding bindings into the game.

In the future, I intend to improve the way bindings are built, such that Axis and Action triggers can both be used on the same binding. For example, a keyboard would use Actions like WASD to move, but a gamepad would use a thumbstick, which is Axis. The goal is making the player’s choice of input transparent to the game logic, and eventually, that will be supported. Additionally, once the file system is complete, this system will support saving to and loading from a file.

A small roadmap

I want to quickly note some of the things I’ve been working on behind the scenes, and touch on where I want to take the engine next. As of right now, I’ve spent the vast majority of my time on the input and memory management code for Halogen; these two things give me a solid foundation on which to begin building other parts of the engine. I’ve also fleshed out the app entry and main game loop, and a simple logging system has been built.

Right now, my major focus is Halogen’s job manager. One of my goals with this engine is to take advantage of multithreading where possible; rather than using dedicated threads for certain major parts of the engine, like physics and audio, I’ve chosen to go with a job system; this allows each different module to queue small tasks that run asynchronously. The task system is coming along nicely; I hope to finish it up and write a short article on it soon, but it’s pretty standard stuff I imagine. Once that’s done, I plan to take a quick swing at file handling and get a very simple asset handler put together.

Coming up after the job manager is easily the most exciting part of the engine for me: rendering. I have always loved writing rendering code; one of the first major things I wrote was a deferred shading pipeline for an open-source Java game engine when I was 16. Writing shaders, reading about complex new rendering approaches, all of that stuff fascinates me. The problem is, I often want to dive into writing render code right off the bat. Last year, I wrote a simple engine in C# and the first thing I worked on was rendering. I quickly realized that the renderer in this engine was not so much a stand-alone module as it should be, but deeply woven into every part of the engine. So with Halogen, I’ve consciously put in effort to work on other aspects of the engine first, and to build a foundation for the renderer to stand on its own.

Thanks again for reading!

Cleaner C++: Wrapping Function Pointers

Hey everyone. This post is less about a specific part of Halogen, and more about C++ in general. I aim to cover what a function pointer is, how to use them syntactically in C++, and finally I’ll provide and explain a useful class for wrapping their functionality into a cleaner API.

So, to start, here’s a  basic function in C++:

void PrintNum(int num)
{
    std::cout << "Number: " << num << std::endl;
}

As you can see, this function simply prints a number to the standard output. Calling this function directly looks something like PrintNum(15);.

C++ has a feature called function pointers, which allows us to hold functions as variables. This can come in handy when you want modular functionality without writing lots of different classes. For example, Halogen uses function pointers in its task manager, where each Task class holds a pointer to the function it should execute asynchronously. Another great advantage is that generally, a function pointer is only sizeof(void*);, which makes them pretty lightweight. Here’s what a pointer to the above function would look like:

void(*myFunctionPointer)(int) = PrintNum;
...
myFunctionPointer(5);

Yikes. Though useful, function pointers are definitely not pretty, syntactically speaking. The identifier is buried in the middle, which can make it a bit hard to notice sometimes. Most of the time you can circumvent this with a typedef:

typedef void(*PrintFunc)(int);
...
PrintFunc myFunctionPointer = PrintNum;

This is much cleaner, but still requires an ugly typedef every time you want to point to a function. What if we could do away with that typedef, but still have readable function pointers in our code? Sounds like a job for templates! C++11 provides std::function, a heavyweight implementation of a function pointer wrapper with tons of functionality, but it’s not particularly fast and takes up considerably more space than necessary, based on my testing. So, I decided to write my own simplified version that provides only what’s necessary for Halogen.

One naive approach to this might look something like this:

template <class ReturnType, class... Args>
class Function
{
//impl goes here
};

...

Function<void, int> myFunction = PrintNum;
myFunction.execute(5);

Already, we’ve come a long way. But this is only scratching the surface! One of the first and easiest things we can do to clean up the code even further is turn Function into a functor (go figure) by overloading the () operator:

ReturnType operator() (Args... argv)
{
    m_InternalPointer(argv);
}

...

Function<void, int> myFunction = PrintNum;
myFunction(5);

This cleans up the call-site of the function pointer, but initializing the class is still a bit ambiguous. Does it take int as an argument, or is int the return type? If int is the return type, what is the void for? Most people would probably quickly figure these questions out, but what if we could make our template parameters look more like a function declaration in the first place? Partial template specialization can help us.

template <class>
class Function;

template <class ReturnType, class... Args>
class Function<ReturnType(Args...)>
{
// impl here
}

Knowing how to properly use templates and partial template specialization is key here. Here’s what’s happening: first, we declare (but do not define) a class called Function, which takes 1 template parameter – which we’ll explain shortly. Next, we specialize by defining a version of Function that takes a ReturnType template, and a list of arguments in a variadic template. Then, we define how those 2 template arguments combine into the 1 function argument of the original Function declaration: <ReturnType(Args...)> . What this means is that in the first declaration of Function, the unknown template is actually a function pointer defined by the second Function definition! With this, we now have clean initialization and call sites:

Function<void(int)> myFunction = PrintNum;
myFunction(5);

And for completeness, here’s the full code:

template <class>
class Function;

template <classReturnType, class... FunctionArgs>
class Function<ReturnType(FunctionArgs...)>
{
public:

    typedef ReturnType(*Pointer)(FunctionArgs...);

    // Non-explicit means we can build straight from a compatible function
    Function(Pointer func)
        : m_InternalPointer(func) {}

    // Override the () operator to turn it into a functor
    ReturnType operator() (FunctionArgs... argv)
    {
        returnm_InternalPointer(argv...);
    }

private:

    Pointer m_InternalPointer;
};

Member Function Pointers

Did I hear someone say member functions? Alright, you talked me into it. For my implementation, I decided that the member function wrapper would have to be its own separate class, since pointers to member functions are rather different, and bring along their own complications. First, let’s see what a member function pointer looks like in its barest form:

struct Obj
{
    void Function(int arg);
}
...
Obj myObj;
void(Obj::*myFunctionPtr)(int) = &Obj::Function;
...
(myObj.*myFunctionPointer)(5);

Not even the call sites are pretty with member function pointers. Note that in the type, we have to prefix the regular function pointer identifier with Obj:: in order to tell the compiler what class of which the function is a member. We also have to dereference the function before we can call it, and we have to call it on an instance of the class where the function came from. Since most of these are fairly trivial additions to our existing class, there’s not much to explain, so here’s the code:

template<class Obj, class>
class MemberFunction;

template <class Obj, class ReturnType, class... FunctionArgs>
class MemberFunction<Obj, ReturnType(FunctionArgs...)>
{
public:

    typedef ReturnType(Obj::*Pointer)(FunctionArgs...);

    MemberFunction() = delete;

    MemberFunction(Obj* obj, Pointer func)
        : m_ObjHandle(obj), m_InternalPointer(func)
    {}

    ReturnType operator() (FunctionArgs... argv)
    {
        return (m_ObjHandle->*m_InternalPointer)(argv...);
    }
private:

    Obj* m_ObjHandle;
    Pointer m_InternalPointer;
};

I hope this helped you understand function pointers better, and with any luck these 2 classes can provide some help to someone. Thanks for reading!

Memory management in Halogen

Hello! In this post, I intend to detail some of the memory management techniques employed in Halogen. Bare in mind that the engine is still heavily under development, so this aspect of the engine in particular is likely to change as time goes on, but it’s also a topic that I think is important to address early on.

The main goals for the memory management system are pretty standard:

  • Keep fragmentation to a minimum
  • Organize objects in memory based on their lifetime and relevance to each other
  • Maximize cache friendliness
  • Provide debugging and troubleshooting facilities in the event of memory leaks or other memory related bugs

Right off the bat I want to mention Stefan Reinalter’s incredible series of posts on how memory is managed in his engine. The system he details in these posts served as the basis for the memory management system in Halogen. If you haven’t read these posts yet, here is a link to the first in the series.

Halogen’s memory management is based on the article above. To quickly summarize, memory is handled by a template class called an Arena. Arenas take a template for an allocator, a tracker, and a thread policy (in the future, they will also include bounds checking and other things, as detailed in the series). The result is an Arena for each region of memory your application uses; an Application arena, which handles allocating (and eventually deallocating) objects with application lifetime; a Frame arena, which can be used for any allocations needed during a single frame; and more. The Arenas can also be made to respect multithreaded calls to allocate and free memory. Here is an example usage:

// Create an instance of a fixed-length string in the app arena
String<32>* myStr = HE_NEW(String<32>, g_ApplicationArena)("Hello!");
// Allocates a big object and aligns it to a 16-byte boundary
BigStruct* bigObject = HE_NEW_ALIGNED(BigStruct, g_FrameArena, 16);

...

// Later, delete those objects
HE_DELETE(myStr, g_ApplicationArena);
HE_DELETE(bigObject, g_FrameArena);

I hope to provide more information on this part of Halogen’s memory systems as they mature, but as you can see, they’re derived from the article linked above, which does a stellar job of explaining how it all comes together.

Fixed-length strings

Early on, I decided that the engine doesn’t need to make use of growing/shrinking strings like std::string. On the other hand, it’s not particularly friendly or easy to use const char* everywhere, so I decided to make a simple fixed-length string implementation for the engine. The result looks like this:

String<32> myStr("Hello");
// supports plenty of operators
myStr += " world!";
myStr[5] = '_';
const char* rawStr = myStr.GetRawString();

I went with knowing the string size at compile-time in order to ensure that the raw array doesn’t have to be new‘d in the string implementation – this avoids having to couple the memory arenas described above with the string.

For now, that’s all on memory management. Up next, I’ll talk about the roadmap for the engine as well as touch on the input binding system. Thanks for reading!

Game loops, and why they’re important

Hello again! As promised, this post will discuss the game loop implementation I’ve decided on for Halogen. Most of the code in this article (and probably most of the future articles too) is pseudocode, and exists more as an example than usable code.

So, to begin, we should define what a “game loop” is – or, at least how I interpret the term. The game loop describes the most central loop of an engine or game’s code. For example:

void RunLoop()
{
    while (gameShouldRun)
    {
        DoLogic();
        Draw();
    }
}

Obviously, this is a very basic example of a game loop, but the idea is there – as long as the game needs to run, this loop repeats, running the game logic and drawing the scene.

Now, there are a few different ways to implement a game loop. I don’t intend to go into detail on this subject, because it’s been covered far more effectively than I could cover it in several places (like here, arguably the most popular article on game loops and time step management), but I will briefly touch on some of the ways to do it, as well as their drawbacks.

Variable time steps

As we all know, it’s virtually impossible to guarantee a steady framerate. However, if you don’t accommodate for the changes in framerate, your game will behave drastically different between someone running at 30 frames per second, and someone running at 300 frames per second. One of the easiest ways to work around this is with a variable time step:

void RunLoop()
{
    while (gameShouldRun)
    {
        float timeSinceLastFrame = Time::GetDeltaTime();

        DoLogic(timeSinceLastFrame);

        Draw();
    }
}

Using the time since the last frame, we can make sure we step objects at the right speed. However, the major drawbacks to this approach are two-fold. First, this approach forces your logic and rendering to run at the same rate, which is definitely not necessary. Second, for large steps, your simulation could become unstable, especially physics. Neither of these are particularly desirable traits, so we’ll pass on this one.

Fixed time steps

So if variable time steps cause a bunch of problems, what if the steps didn’t vary?

void RunLoop()
{
    while (gameShouldRun)
    {
        float startTime = Time::GetTimeNow();
        DoLogic();
        Draw();

        float frameTime = Time::GetTimeNow() - startTime;
        sleep(FIXED_FRAME_TIME - frameTime); // sleep for the remaining time
    }
}

While this might solve some issues, it should be abundantly clear that this approach still suffers from issues. For one, it still forces your rendering and logic to happen at the same rate. The other problem arises if your frames take longer than the allotted time – suddenly your simulation is moving in slo-mo. Another major downside to this approach is wasted potential – why should you force 60 frames per second on someone whose system could push 200?

Combining the two

The big drawback the two previous approaches can’t overcome is the coupling of logic speed and render speed, so we want our loop to avoid this issue particularly. Specifically, we would like our logic to run at a fixed rate, and allow the rendering to run as quickly as it can. The general solution looks something like this:

void RunLoop()
{
    float lag = 0.0F;

    while (gameShouldRun)
    {
        float timeSinceLastFrame = Time::GetDeltaTime();

        lag += timeSinceLastFrame;

        while (lag > FIXED_FRAME_TIME)
        {
            DoFixedUpdate();
            lag -= FIXED_FRAME_TIME;
        }

        Draw(lag); // Use the lag to interpolate between fixed frames
    }
}

This code will only run the fixed-rate update loop as often as specified, while letting the renderer churn out frames as quickly as it can. We’ve successfully decoupled logic updates from render speed, but we’ve also created a new complication: interpolation. If the renderer decides to draw a frame between two fixed updates, the player could end up seeing the same frame twice – at which point, 120 frames per second looks just the same as 60. Interpolation helps this – the renderer would perform interpolation before drawing, in order to smooth out transitions between frames – but we’re now throwing potentially messy interpolation code into our renderer, which doesn’t sound particularly ideal.

This approach can also suffer from a phenomenon called the spiral of death, in which the time it takes to do a full frame is longer than your allotted time. The lag value continually grows, which forces more updates, which grows the lag variable… you get the point.

The Halogen loop

Now that we’ve established some of the ways you can write a game loop, I’d like to talk about how I decided to implement the core loop in Halogen, as well as some of my reasoning for doing so.

The core loop of Halogen is based on the last example given above, but draws much inspiration from Unity’s Monobehavior lifecycle as well (an excellent graphic on this can be found here). Essentially, we’re using both a fixed update and variable-rate update, the latter of which internally handles interpolation before drawing the frame. Here’s what this looks like, in pseudocode:

void RunLoop()
{
    float lag = 0.0F;

    while (gameShouldRun)
    {
        float timeSinceLastFrame = Time::GetDeltaTime();
        lag += timeSinceLastFrame;

        // Handle windows messages every drawn frame
        // This keeps input up to date for each variable update
        HandleWindowsMessages();

        while (lag > FIXED_FRAME_TIME)
        {
            DoFixedUpdate();
            lag -= FIXED_FRAME_TIME;
        }

        // Handles anything that doesn't rely on fixed framerate,
        // for example particle effects, camera rotation, etc.
        // Also handles interpolation
        DoVariableUpdate(timeSinceLastFrame);

        // The renderer uses interpolated values from the variable update
        // no more messy interpolation inside the renderer!
        Draw();
    }
}

The end result is exposing a fixed update function to the user, which can be used when a reliable time step is required (like physics code), but also exposing a variable update function that is called every time the scene is rendered. Users can even specify if they want their objects to use the built-in interpolation system or not, and use this variable update function to create their own interpolation or predictions. Another great thing about this is thread safety – since both of the update functions are called from the same thread, albeit at different rates, there’s no inherent risk like there might be if the fixed update function were in its own thread.

So, that’s it for my first entry to the blog. I’d love to hear feedback on the concept, as I feel very confident that this is a strong approach to a critical part of the engine. Coming next on the blog, I intend to talk about some of the memory management techniques I’ve chosen for Halogen.

Thanks for reading!

Hello friends, I am here.

Hello reader, my name is Brendan. I’m a self-taught programmer, soon to be attending college for computer science. I have a passion for game development; this year, I was lucky to attend my first GDC. As such, a large amount of this blog will pertain to game programming and development.

 

This blog is mainly going to be an outlet for my thoughts as I develop a game engine from scratch, called Halogen. The engine is written in C++, so the vast majority of the blog will be C++ related. I have been asked on more than one occasion why I’ve chosen to build my own engine, rather than use one of the many existing engines out there. My response is always that it’s less about using an engine for me, and more about understanding how it works. Being able to architect and build the entire engine is a fascinating challenge to me, and I hope that it proves an interesting read as I progress. I may infrequently blog about travels, gaming, or life in general, but I do intend for the majority of the posts here to pertain to my work and progress on Halogen.

 

So, in short, welcome to my blog where I make yet another C++ game engine. My first relevant post will come soon, where I will talk about the core game loop architecture of Halogen. I hope you enjoy!