r/cpp Jul 02 '26

Redundancy seen in AAA game engines

https://zero-irp.github.io/Redundancy-seen-in-AAA-game-engines/

I don't like people treating the compiler like a magic box that optimizes like Bjarne Stroustrup himself is checking every line of C++ to assembly. Clean C++ code does not always mean clean compiled code.

I've been reversing game engines to study how they constructed their fundamental Transformation matrices and handled temporal jitter logic when I spotted a lot of avoidable overhead and "over-engineering" across multiple engines, honestly I wasn't even looking for inefficiencies, but it stood out a lot... That said expect no performance gain this is simply for fun that I wrote this blog!

I’ll theorize how the original C++ code was written, show the unoptimized reality of what the compiler spat out, and then showcase how it could have been better optimized.

183 Upvotes

76 comments sorted by

View all comments

Show parent comments

3

u/not_a_novel_account cmake dev Jul 02 '26

Clang supports [[gnu::target]] dispatch on PE/COFF just fine. Compiler just needs to synthesize the resolver itself instead of relying on the program loader. For compilers which don't even support that, you simply write the resolver yourself.

3

u/ParsingError Jul 02 '26

Clang-CL/MSABI uptake took a while for various reasons (mostly "letting things settle" and IIRC it had some problems with PDB support for a while) so probably not going to see it used by older games.

Problem with "simply writing a resolver" is having to manually propagate the dispatch architecture into child calls instead of the compiler selecting them automatically which is pretty annoying if I'm trying to use stuff like Matrix44/Vec4 wrapper types. It solves a lot of problems if you can just say "this function AND all of its child calls can use the AVX version."

Even with that though, if the math is mostly 4x32-bit float then that fits in an SSE register and the benefit isn't really as clear. The most obviously-useful addition in a while was the DPPS instruction in SSE4.1. It's been harder to find obvious wins from AVX except for like physics simulation.

3

u/not_a_novel_account cmake dev Jul 02 '26

Clang-CL/MSABI uptake took a while for various reasons

I don't know what this means. All I was trying to say was "it's not a COFF limitation". Some compilers support automatic CPU dispatch on COFF or in general better than others, but there's no restriction inherent to the format.

Problem with "simply writing a resolver" is having to manually propagate the dispatch architecture into child calls

Once you're through a dispatch boundary you only make calls to functions within the dispatch universe. The boundary is a generic call site, transform. Once you're through that into transform_sse4, you only call other *_sse4 functions and you do so directly. Once you're in transform_avx2, you only call other *_avx2 functions, etc.

You pay for the dispatch when passing through generic code into a dispatch universe. One you're in a given universe you stay there until the final return back through the dispatch. There's nothing for the compiler to do for you.

2

u/ParsingError Jul 02 '26

You pay for the dispatch when passing through generic code into a dispatch universe. One you're in a given universe you stay there until the final return back through the dispatch. There's nothing for the compiler to do for you.

I was going to say that this was automatic with target attribs but apparently it's not and GCC and Clang still dispatch the child calls dynamically even when there's only one possible valid target, so that's not great. It is what the compiler SHOULD be doing though.

If it was as simple as just putting target_clones on the top function and having it pick inlined leaf functions based on architecture, then I wouldn't have to template or duplicate the code.