Files
fs2open.github.com/code/graphics/opengl/gropenglquery.cpp
T
Goober5000andClaude Opus 4.8 bed228d264 avoid undefined behavior from UNREACHABLE on reachable code paths (#7555)
* avoid undefined behavior from UNREACHABLE on reachable code paths

UNREACHABLE expands to __assume(false) / __builtin_unreachable() in
release builds, which is undefined behavior if the code is ever actually
reached. Many UNREACHABLE sites only guard against unexpected-but-
possible runtime states (values from tables, mission/save files, network
packets, scripting, or user input), so reaching them should degrade
gracefully rather than invoke UB.

Replace UNREACHABLE with Assertion(false, ...) at all such sites. It
still pops an error in debug builds, but in release it is a harmless
no-op that simply falls through to the following code. Sites that are
genuinely unreachable by local invariants (e.g. a switch over a bounded
random number) are left as UNREACHABLE.

A few converted sites had no safe fall-through -- an uninitialized read,
null dereference, or null call would have followed -- so they also get a
minimal recovery: initialize the value, guard the dereference, return
early, or skip the offending item.

Where the converted site was a simple guard of the form
    if (x) { Assertion(false, ...); return; }
hoist the check onto the invariant instead:
    Assertion(!x, ...);
    if (x) { return; }
This states the expectation directly and shows the offending value in
the debug popup, while keeping the same graceful runtime degradation.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

* addendum

After consultation with Lafiel, use UNREACHABLE in situations that are effectively impossible even if not provably impossible.  UNREACHABLE = "this shouldn't ever happen"; Assertion = "if this ever happens, it's a coding error".  Also fix a few other sites and change a couple to Warnings.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-09 11:20:25 +09:00

85 lines
1.9 KiB
C++

#ifdef USE_OPENGL_ES
#include "es_compatibility.h"
#endif
#include "graphics/opengl/gropenglquery.h"
#include "graphics/opengl/gropengl.h"
namespace {
struct query_object_slot {
bool used = false;
GLuint name = 0;
};
SCP_vector<query_object_slot> query_objects;
int get_new_query_slot() {
auto end = query_objects.end();
for (auto iter = query_objects.begin(); iter != end; ++iter) {
if (!iter->used) {
return (int) std::distance(query_objects.begin(), iter);
}
}
query_objects.emplace_back();
return (int) (query_objects.size() - 1);
}
query_object_slot& get_query_slot(int handle) {
Assertion(handle >= 0 && handle < (int)query_objects.size(), "Query object index %d is invalid!", handle);
return query_objects[handle];
}
}
int gr_opengl_create_query_object() {
auto idx = get_new_query_slot();
auto& slot = query_objects[idx];
slot.used = true;
glGenQueries(1, &slot.name);
return idx;
}
void gr_opengl_query_value(int obj, QueryType type) {
auto& slot = get_query_slot(obj);
switch(type) {
case QueryType::Timestamp:
Assertion(GLAD_GL_ARB_timer_query, "Timestamp queries are not available! Availability must be checked before calling this function!");
glQueryCounter(slot.name, GL_TIMESTAMP);
break;
default:
UNREACHABLE("Unhandled enum value %d!", static_cast<int>(type));
break;
}
}
bool gr_opengl_query_value_available(int obj) {
auto& slot = get_query_slot(obj);
GLuint available;
glGetQueryObjectuiv(slot.name, GL_QUERY_RESULT_AVAILABLE, &available);
return available == GL_TRUE;
}
std::uint64_t gr_opengl_get_query_value(int obj) {
auto& slot = get_query_slot(obj);
GLuint64 available;
glGetQueryObjectui64v(slot.name, GL_QUERY_RESULT, &available);
return available;
}
void gr_opengl_delete_query_object(int obj) {
auto& slot = get_query_slot(obj);
glDeleteQueries(1, &slot.name);
slot.name = 0;
slot.used = false;
}