Compile-time generation of RGB debug colors

Watching the latest episode of Handmade Hero on YouTube by Casey Muratori, there was an interesting application for generating data a priori (i.e. during the compilation of the program). For high performance or real-time critical applications it is advisable to do as many calculations as possible while compiling the program. This saves important CPU cycles at runtime, which can be used for other calculations.

Handmade Hero 1

Around the 40 minutes mark, Casey talks about the visualization of memory allocations using different colors in order to figure out how his game that he is developing openly makes use of main memory. He continues to reason about how to get the largest possible distance between the individual RGB colors to separate them better visually:

Handmade Hero 2

One problem with RGB color space is that the spectral colors red, green and blue are aligned in a 3D grid where each color spans one dimension inside this color space.

RGB cube

This property makes it difficult to evenly space out each (r,g,b) color triplet so that their distance is as large as possible to each other for a given number of distinct colors. One common solution to this problem is to use another color space where all the visible colors can be accessed in a linear fashion. Is is much simpler to even out several distinct points on a line than in 3D space.

HSV Cone

The HSV color space shown on the right is an alternative representation of the RGB color space where all the visible colors are arranged in a radial slice around the central axis of neutral colors from black at the bottom to white at the top. If we now virtually roll up the radial slice, we get a linear sequence of the different color levels from red to green to blue. We cam also easily transform a given color triplet from one color space to another.

In the Handmade Hero video linked above, Casey was asking for a fixed number of distinct debug colors to visualize the memory consumption of his game. In fact, we can generate this list of colors already during compile-time as all necessary informations are known beforehand. The generation of arbitrary data during compile-time such as lookup tables (LUTs) was historically a tedious job using C++. However, with the advent of C++11/C++14 this task became much simpler using constexpr:

Following is a short template function that is instantiated with a fix number N of colors. Inside the function a LUT is generated by dividing the radial HSV color slice in N different parts and then calculating the RGB color triplet pendants:

constexpr int floor(float x) {
    return static_cast<int>(x) - (x < static_cast<int>(x));
}

template <int N>
struct ColorLUT {
    constexpr ColorLUT() : values() {
        for (auto idx = 0; idx < N; ++idx) {
            const auto h = (idx + 1) / static_cast<float>(N);
            const auto i = floor(h * 6);
            const auto f = h * 6 - i;
            /* case 0: */ float r = 1, g = f, b = 0;
            switch (i % 6) {
                case 1: r = 1 - f, g = 1, b = 0; break;
                case 2: r = 0, g = 1, b = f; break;
                case 3: r = 0, g = 1 - f, b = 1; break;
                case 4: r = f, g = 0, b = 1; break;
                case 5: r = 1, g = 0, b = 1 - f; break;
            }
            values[idx][0] = r;
            values[idx][1] = g;
            values[idx][2] = b;
        }
    }
    float values[N][3];
};

On the calling site we can now easily create an arbitrary list of debug colors using:

int main() {
    constexpr auto DebugColorTable = ColorLUT<32>();
    for (const auto [r,g,b] : DebugColorTable.values) {
        std::cout << r << ", " << g << ", " << b << std::endl;
    }
}

Note that we cannot use std::floor() as constexpr is currently not implemented in the <cmath> header. However, this issue has been addressed already and may be implemented in a future version of C++.

I’m available for software consultancy, training and mentoring. Please contact me, if you are interested in my services.