-
-
Notifications
You must be signed in to change notification settings - Fork 21.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Core: Reimplement math structs as constexpr
#91992
base: master
Are you sure you want to change the base?
Conversation
You've done a great job explaining the technical considerations you faced when implementing this PR. Can you also provide an explanation about why these changes are beneficial? At face value, this looks like a stylistic change that ended up necessitating some functional changes that may have performance tradeoffs and may impact compatibility with older compiler versions (in addition to introducing risk throughout the engine). If performance is a factor, can you post the results of the benchmarking that you did while making this change? If the benefit is stylistic, can you explain why the benefit outweighs the cost? |
Style improvementsOne point of favour constexpr allows the compiler to remove dead code (unused variables) from the constructors. This conversion also removed duplicated methods. |
The compiler does that anyway, see #91089 (comment) |
Yes, that's why I'm approaching it from an improved style point of view and not performance. Like the principle of don't repeat yourself. https://en.wikipedia.org/wiki/Don%27t_repeat_yourself |
The primary functional benefits are those inherent to |
In that case, I'm not sure what to say. We can't really justify changing 1700 lines of deeply core code for no benefit. The ability to evaluate variables at compile-time is really nice for development since it helps you catch type errors while writing code. But you are applying this to code that has already been written, tested, and used in production for years. Effectively creating a risk of introducing new bugs into a code that works in the name of making the code easier to write. To be perfectly blunt, I think this falls into the "if its not broken, don't fix it" territory. If there were some other benefit to this change, then it would be a different story. So perhaps it makes sense to look into those performance benefits that we could take advantage of and form a picture of whether that is something worth pursuing. |
I'm used to separating foundational implementation from codebase integration in PRs, but it sounds like that's not viable here. One obvious option would be the ability to convert several runtime tests to TEST_CASE("[AABB] Constructor methods") {
const AABB aabb = AABB(Vector3(-1.5, 2, -2.5), Vector3(4, 5, 6));
const AABB aabb_copy = AABB(aabb);
CHECK_MESSAGE(
aabb == aabb_copy,
"AABBs created with the same dimensions but by different methods should be equal.");
} namespace AABBConstructorMethods {
inline constexpr AABB aabb = AABB(Vector3(-1.5, 2, -2.5), Vector3(4, 5, 6));
inline constexpr AABB aabb_copy = AABB(aabb);
static_assert(
aabb == aabb_copy,
"AABBs created with the same dimensions but by different methods should be equal.");
} //namespace AABBConstructorMethods |
a064c48
to
4232f28
Compare
case 1: | ||
return right; | ||
default: | ||
return levels[p_idx]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be an error instead IMO, the dev assert is there to make the error explicit and reliable, now it becomes a contextual segfault
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those error macros don't play nice with constexpr
environments out of the gate, but an early catch would still be nice in some form. Can't use consteval
nor std::is_constant_evaluated
in C++17, but __builtin_is_constant_evaluated
should do the trick.
4232f28
to
c3f0d6d
Compare
The scope of this is much greater than I expected, even after trying to trim things down. Gonna be converting this to a draft while I work on piecemealing the concepts across other PRs, starting with #92059. |
Supersedes #91089
In constrast to the above commit having
constexpr
as a sort of bonus addition from initializer lists, this commit focuses squarely on incorporating aconstexpr
foundation for various math structs. Because making everything about these structsconstexpr
would be extremely noisy & make micromanaging the more explicit changes a headache, this instead implements the concept for constructors and operators (and any constructor-helper funcs).The most obvious change revolves around
operator[]
for structs with union members. This is because, to my knowledge,constexpr
environments cannot dynamically change an active union member. Because construction values are explicitly assigned to named members, the usual approach of calling to the array union member will simply fail. This can be worked around by explicitly checking for the named values via a switch statement first.Some other aspects of the code that were adjusted are:
constexpr
was enough to make GCC "aware" of unused instances of these variables. These came up as actual warnings now, which made GHA builds fail, so removing them were the only non-math changes in this PR.operator[]
now usesize_t
: Works around a bizarre error on GHA where it was parsing certain values as invalid (Error: ./core/math/vector3.h:209:23: error: array subscript [0, 2] is outside array bounds of 'real_t [3]' {aka 'float [3]'} [-Werror=array-bounds]
). Couldn't replicate locally, so it's possible this is something particular to older GCC versions? Either way, the errors no longer occurred when switchingint
tosize_t
as the array index, which is probably the more modern approach anyway. It still recognizes actual oob access from arguments, so nothing was lost in translation.void
where applicable: Implementation was inconsistent across the different math structs on if they should return self orvoid
, with no real rhyme or reason to the decisions; as part of their reshuffling, I opted for the former. I'm fully aware the repo avoids the practice ofx = y = z
syntax, but this matches built-in/stdlib functionality & doesn't actually change any existing code; it's only "supported" in that it's functional.constexpr
& the destructor was already empty.inline constexpr
and class variables were converted tostatic constexpr
. Only applied to the files I was already adjusting.constexpr
, and checks with all-constexpr logic were converted tostatic_assert
.