[Feat] (MG_Impl): answer the two query parameters the getters were missing

GetQueryObjectValue implemented GL_QUERY_RESULT_AVAILABLE and GL_QUERY_RESULT and
rejected everything else, so direct_state_access.queries_functional threw on its
very first probe - GL_QUERY_TARGET - and never reached any of the checks it was
written for.

GL_QUERY_TARGET is state the object has carried all along; it just had no case.
GL_QUERY_RESULT_NO_WAIT is GL_QUERY_RESULT with the backend asked not to block,
and it brings a wrinkle the shared getter could not express: when the result has
not landed, GL_ARB_query_buffer_object leaves the destination untouched rather
than writing a placeholder. GetQueryObjectValue now reports "succeeded but
produced no value" through an optional out-parameter, and all five callers - the
four buffer forms and the four client-memory forms - skip the write on it.

The switch is deliberately widened by exactly these two names: its default
INVALID_ENUM is what the GL33 and GL40 query error cases rely on.

queries_functional passes on Espryt. On Magma it stops throwing and fails on a
value instead, which is a separate problem in the query results themselves.
This commit is contained in:
BZLZHH
2026-08-05 03:48:14 -04:00
parent 3b3b6e5b8b
commit 088f263495
+54 -7
View File
@@ -123,8 +123,13 @@ namespace MobileGL::MG_Impl::GLImpl {
}
// Shared GetQueryObject* implementation. Returns false when an error
// was recorded and no value should be written back.
Bool GetQueryObjectValue(GLuint id, GLenum pname, const char* function, Uint64& outValue) {
// was recorded and no value should be written back. `outValueProduced`, when given,
// additionally distinguishes "succeeded with a value" from "succeeded but the result is not
// ready" - the GL_QUERY_RESULT_NO_WAIT case, where GL_ARB_query_buffer_object says the
// destination is left alone rather than written with a placeholder.
Bool GetQueryObjectValue(GLuint id, GLenum pname, const char* function, Uint64& outValue,
Bool* outValueProduced = nullptr) {
if (outValueProduced) *outValueProduced = true;
const std::lock_guard<std::mutex> lock(g_queryObjectsMutex);
auto* queryObject = FindQueryObjectLocked(id);
if (!queryObject) {
@@ -137,6 +142,41 @@ namespace MobileGL::MG_Impl::GLImpl {
}
switch (pname) {
case GL_QUERY_TARGET:
// The target a query was begun with (or created with, for glCreateQueries) - state
// the object has carried all along, GL 4.6 core table 23.35.
outValue = queryObject->target;
return true;
case GL_QUERY_RESULT_NO_WAIT: {
if (queryObject->resultCached) {
outValue = queryObject->cachedResult;
return true;
}
Uint64 result = 0;
const auto getQueryResult64 = MG_Backend::gBackendFunctionsTable.GL.GetQueryResult64;
if (queryObject->backendHandle && getQueryResult64 &&
!getQueryResult64(queryObject->backendHandle, /*wait=*/false, &result)) {
// Not ready. The whole point of the no-wait form is that the caller's
// destination keeps whatever it already held.
if (outValueProduced) *outValueProduced = false;
outValue = 0;
return true;
}
if (queryObject->target == GL_ANY_SAMPLES_PASSED ||
queryObject->target == GL_ANY_SAMPLES_PASSED_CONSERVATIVE) {
result = result != 0 ? 1 : 0;
}
if (queryObject->backendHandle) {
if (const auto deleteBackendQuery = MG_Backend::gBackendFunctionsTable.GL.DeleteBackendQuery) {
deleteBackendQuery(queryObject->backendHandle);
}
queryObject->backendHandle = nullptr;
}
queryObject->cachedResult = result;
queryObject->resultCached = true;
outValue = result;
return true;
}
case GL_QUERY_RESULT_AVAILABLE: {
if (queryObject->resultCached || !queryObject->backendHandle) {
outValue = 1;
@@ -195,7 +235,10 @@ namespace MobileGL::MG_Impl::GLImpl {
if (!ResolveQueryResultDestination(buffer, offset, sizeof(T), function, bufferObject)) return;
Uint64 value = 0;
if (!GetQueryObjectValue(id, pname, function, value)) return;
Bool valueProduced = false;
if (!GetQueryObjectValue(id, pname, function, value, &valueProduced)) return;
// GL_QUERY_RESULT_NO_WAIT on a result that has not landed writes nothing at all.
if (!valueProduced) return;
const T narrowed = static_cast<T>(value);
bufferObject->UploadSubData({const_cast<T*>(&narrowed), sizeof(T)}, static_cast<SizeT>(offset));
@@ -533,7 +576,8 @@ namespace MobileGL::MG_Impl::GLImpl {
void GetQueryObjectiv(GLuint id, GLenum pname, GLint* params) {
Uint64 value = 0;
if (!GetQueryObjectValue(id, pname, __FUNCTION__, value) || !params) {
Bool valueProduced = false;
if (!GetQueryObjectValue(id, pname, __FUNCTION__, value, &valueProduced) || !valueProduced || !params) {
return;
}
constexpr Uint64 kMaxInt = static_cast<Uint64>(INT_MAX);
@@ -542,7 +586,8 @@ namespace MobileGL::MG_Impl::GLImpl {
void GetQueryObjectuiv(GLuint id, GLenum pname, GLuint* params) {
Uint64 value = 0;
if (!GetQueryObjectValue(id, pname, __FUNCTION__, value) || !params) {
Bool valueProduced = false;
if (!GetQueryObjectValue(id, pname, __FUNCTION__, value, &valueProduced) || !valueProduced || !params) {
return;
}
*params = static_cast<GLuint>(value & 0xFFFFFFFFull);
@@ -550,7 +595,8 @@ namespace MobileGL::MG_Impl::GLImpl {
void GetQueryObjecti64v(GLuint id, GLenum pname, GLint64* params) {
Uint64 value = 0;
if (!GetQueryObjectValue(id, pname, __FUNCTION__, value) || !params) {
Bool valueProduced = false;
if (!GetQueryObjectValue(id, pname, __FUNCTION__, value, &valueProduced) || !valueProduced || !params) {
return;
}
*params = static_cast<GLint64>(value);
@@ -558,7 +604,8 @@ namespace MobileGL::MG_Impl::GLImpl {
void GetQueryObjectui64v(GLuint id, GLenum pname, GLuint64* params) {
Uint64 value = 0;
if (!GetQueryObjectValue(id, pname, __FUNCTION__, value) || !params) {
Bool valueProduced = false;
if (!GetQueryObjectValue(id, pname, __FUNCTION__, value, &valueProduced) || !valueProduced || !params) {
return;
}
*params = static_cast<GLuint64>(value);