[Feat] (MG_Impl, MG_Backend): GL_ARB_timer_query on both backends

Implements GL timer queries end to end: a frontend query registry
(modeled on the sync module - mutex-guarded objects wrapping opaque
backend handles behind optional function pointers) serving
glGenQueries/glBeginQuery/glEndQuery(GL_TIME_ELAPSED)/glQueryCounter
(GL_TIMESTAMP)/glGetQueryObject*/glGetQueryiv with GL 3.3 error
semantics and a graceful zero-result fallback when a backend cannot
time.

DirectGLES backs spans with GL_EXT_disjoint_timer_query (context-
generation-stamped handles, bounded result waits). DirectVulkan gets a
VkTimerQueryManager: per-frame-in-flight timestamp query pools reset at
command-buffer begin (outside render passes), records harvested by
frame serial before their pool recycles, elapsed = masked tick delta x
timestampPeriod; handles are stamped with a renderer generation that
also now guards fence syncs across renderer recreation. GL_QUERY_
COUNTER_BITS reports 0 unless the live backend can actually time
(dynamic IsTimerQuerySupported hook), and a failed blocking read keeps
the handle alive so the real value stays reachable once the frame
submits.

GL_ARB_timer_query is advertised only when the device supports timing
and MOBILEGL_DISABLE_TIMERQUERY is unset - LWJGL keys Minecraft's F3
'GPU: x%' line off exactly that extension string; verified on device
(Adreno 830) on both backends.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-10 01:08:38 +00:00
co-authored by Claude Fable 5
parent 7d31a6fcd7
commit 7eb3994b02
25 changed files with 1917 additions and 91 deletions
+22 -2
View File
@@ -793,6 +793,18 @@ namespace MobileGL::MG_Impl::GLImpl {
params[0] = static_cast<GLint64>(value);
return;
}
case GL_TIMESTAMP: {
// Handled here (not via the 32-bit GetIntegerv fallback) so the
// full 64-bit GPU timestamp survives; LWJGL reads it this way.
Int64 timestamp = 0;
if (!MG_Config::Features.DisableTimerQuery) {
if (const auto getGpuTimestampNs = MG_Backend::gBackendFunctionsTable.GL.GetGpuTimestampNs) {
timestamp = getGpuTimestampNs();
}
}
params[0] = static_cast<GLint64>(timestamp);
return;
}
default:
break;
}
@@ -1563,9 +1575,17 @@ namespace MobileGL::MG_Impl::GLImpl {
case GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT:
*params = 0; // texture-buffer range entrypoints are stubbed
return;
case GL_TIMESTAMP:
*params = 0; // timer-query entrypoints are stubbed
case GL_TIMESTAMP: {
Int64 timestamp = 0;
if (!MG_Config::Features.DisableTimerQuery) {
if (const auto getGpuTimestampNs = MG_Backend::gBackendFunctionsTable.GL.GetGpuTimestampNs) {
timestamp = getGpuTimestampNs();
}
}
// 32-bit query: clamp per the GL state-query conversion rules.
*params = timestamp > static_cast<Int64>(INT_MAX) ? INT_MAX : static_cast<GLint>(timestamp);
return;
}
case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING:
if (const auto& obj =
MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::TransformFeedback).GetBoundObject()) {