Compile-time generation of RGB debug colors
Compile-time RGB color generation

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.

Screenshot of Handmade Hero Day 542 - Drawing Memory Occupancy Accurately

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:

Es wurde kein Alt-Text für dieses Bild angegeben.

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.

CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=13754

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.

Von (3ucky(3all - Uploaded to en:File:HSV cone.png first (see associated log) by (3ucky(3all; then transfered to Commons by Moongateclimber., CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=943857

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.

要查看或添加评论,请登录

Kai Wolf的更多文章

  • Managing transitive dependencies with Conan and CMake

    Managing transitive dependencies with Conan and CMake

    Back when I looked into Conan the first time in 2017 the state-of-the-art for dependency management in the C and C++…

  • Heterogeneous computing on Android devices

    Heterogeneous computing on Android devices

    In today's technical world with all its different devices, ranging from small, intelligent wristwatches, smartphones…

  • Strange graphs and dynamic programming

    Strange graphs and dynamic programming

    There exists a YouTube channel called Numberphile that discusses all interesting sorts of mathematics. I really do…

  • Working with C++ code in Android effectively

    Working with C++ code in Android effectively

    If you are working with native (C or C++) code in your Android project, you are probably using CMake or ndk-build for…

  • Using Conan in a non-intrusive way in CMake-based projects

    Using Conan in a non-intrusive way in CMake-based projects

    Ever since Software is eating the world an ongoing debate in the C and C++ development world is about proper dependency…

    1 条评论
  • Space carving of gemstones

    Space carving of gemstones

    Recently a potential customer approached me, asking me for a solution to 3D reconstruct inclusions and dirt in…

社区洞察

其他会员也浏览了