mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 13:48:30 +09:00
[Feat] (DirectGLES): implement GL_ANY_SAMPLES_PASSED occlusion queries
DirectGLES never registered BeginOcclusionQuery/EndOcclusionQuery, so
the frontend rejected the occlusion query targets entirely; the CTS
tests that use them (e.g. packed_depth_stencil.verify_partial/mixed_
attachments) left a stray GL_INVALID_ENUM that a later, unrelated
glGetError() check would report as its own failure
("Uploading buffer data failed" at gluDrawUtil.cpp:363).
Occlusion queries are core ES3 (glGenQueries/glBeginQuery(GL_ANY_
SAMPLES_PASSED, ...)/glEndQuery/glGetQueryObjectuiv), unlike the timer
queries which need GL_EXT_disjoint_timer_query, so they're wired up
unconditionally (independent of MOBILEGL_DISABLE_TIMERQUERY) using the
same handle-based GetQueryResult64/DeleteBackendQuery plumbing already
shared with timer queries. GetQueryResult64 now reads the 0/1 result
through the core 32-bit glGetQueryObjectuiv getter for occlusion
handles instead of the timer-only 64-bit GL_EXT_disjoint_timer_query
getter, since a driver can fully support core occlusion queries while
lacking that extension entirely.
This commit is contained in:
@@ -935,11 +935,16 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
funcsTable.GL.BeginTimeElapsedQuery = BeginTimeElapsedQuery;
|
funcsTable.GL.BeginTimeElapsedQuery = BeginTimeElapsedQuery;
|
||||||
funcsTable.GL.EndTimeElapsedQuery = EndTimeElapsedQuery;
|
funcsTable.GL.EndTimeElapsedQuery = EndTimeElapsedQuery;
|
||||||
funcsTable.GL.QueryCounterTimestamp = QueryCounterTimestamp;
|
funcsTable.GL.QueryCounterTimestamp = QueryCounterTimestamp;
|
||||||
funcsTable.GL.IsQueryResultAvailable = IsQueryResultAvailable;
|
|
||||||
funcsTable.GL.GetQueryResult64 = GetQueryResult64;
|
|
||||||
funcsTable.GL.DeleteBackendQuery = DeleteBackendQuery;
|
|
||||||
funcsTable.GL.GetGpuTimestampNs = GetGpuTimestampNs;
|
funcsTable.GL.GetGpuTimestampNs = GetGpuTimestampNs;
|
||||||
}
|
}
|
||||||
|
// Occlusion queries are core ES3 (independent of MOBILEGL_DISABLE_TIMERQUERY)
|
||||||
|
// and share the handle-based result/delete entries, which must exist even
|
||||||
|
// when the timer-query group above is disabled.
|
||||||
|
funcsTable.GL.BeginOcclusionQuery = BeginOcclusionQuery;
|
||||||
|
funcsTable.GL.EndOcclusionQuery = EndOcclusionQuery;
|
||||||
|
funcsTable.GL.IsQueryResultAvailable = IsQueryResultAvailable;
|
||||||
|
funcsTable.GL.GetQueryResult64 = GetQueryResult64;
|
||||||
|
funcsTable.GL.DeleteBackendQuery = DeleteBackendQuery;
|
||||||
funcsTableInitialized = true;
|
funcsTableInitialized = true;
|
||||||
}
|
}
|
||||||
return funcsTable;
|
return funcsTable;
|
||||||
|
|||||||
@@ -4360,6 +4360,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
struct GLESQueryObject {
|
struct GLESQueryObject {
|
||||||
GLuint queryId = 0;
|
GLuint queryId = 0;
|
||||||
Uint contextGeneration = 0;
|
Uint contextGeneration = 0;
|
||||||
|
// GL_ANY_SAMPLES_PASSED result is 0/1 and only ever reachable through the
|
||||||
|
// core (non-extension) glGetQueryObjectuiv getter - GL_EXT_disjoint_timer_query's
|
||||||
|
// 64-bit glGetQueryObjectui64vEXT is timer-specific and may be entirely absent
|
||||||
|
// on drivers that otherwise fully support core ES3 occlusion queries.
|
||||||
|
Bool isOcclusion = false;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4552,6 +4557,38 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
return new GLESQueryObject{queryId, g_syncContextGeneration};
|
return new GLESQueryObject{queryId, g_syncContextGeneration};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GL_SAMPLES_PASSED/GL_ANY_SAMPLES_PASSED(_CONSERVATIVE) occlusion queries. Unlike the
|
||||||
|
// timer queries above, these are core ES 3.0 (no GL_EXT_disjoint_timer_query needed).
|
||||||
|
Bool AreOcclusionQueriesSupported() {
|
||||||
|
return g_GLESFuncs.glGenQueries && g_GLESFuncs.glDeleteQueries && g_GLESFuncs.glBeginQuery &&
|
||||||
|
g_GLESFuncs.glEndQuery && g_GLESFuncs.glGetQueryObjectuiv;
|
||||||
|
}
|
||||||
|
|
||||||
|
BackendQueryHandle BeginOcclusionQuery() {
|
||||||
|
if (!IsBackendContextCurrentOnThisThread() || !AreOcclusionQueriesSupported()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
GLuint queryId = 0;
|
||||||
|
g_GLESFuncs.glGenQueries(1, &queryId);
|
||||||
|
if (queryId == 0) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
// ES only implements the boolean ANY_SAMPLES_PASSED variant, not an exact
|
||||||
|
// GL_SAMPLES_PASSED count; the frontend already coerces ANY_SAMPLES_PASSED*
|
||||||
|
// targets to boolean, and desktop GL_SAMPLES_PASSED reads a 0/1 approximation.
|
||||||
|
g_GLESFuncs.glBeginQuery(GL_ANY_SAMPLES_PASSED, queryId);
|
||||||
|
return new GLESQueryObject{queryId, g_syncContextGeneration, /*isOcclusion=*/true};
|
||||||
|
}
|
||||||
|
|
||||||
|
void EndOcclusionQuery(BackendQueryHandle handle) {
|
||||||
|
const auto* query = static_cast<GLESQueryObject*>(handle);
|
||||||
|
if (query == nullptr || query->contextGeneration != g_syncContextGeneration ||
|
||||||
|
!IsBackendContextCurrentOnThisThread() || !g_GLESFuncs.glEndQuery) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
g_GLESFuncs.glEndQuery(GL_ANY_SAMPLES_PASSED);
|
||||||
|
}
|
||||||
|
|
||||||
Bool IsQueryResultAvailable(BackendQueryHandle handle) {
|
Bool IsQueryResultAvailable(BackendQueryHandle handle) {
|
||||||
const auto* query = static_cast<GLESQueryObject*>(handle);
|
const auto* query = static_cast<GLESQueryObject*>(handle);
|
||||||
// Null/stale handles report available so the frontend proceeds to
|
// Null/stale handles report available so the frontend proceeds to
|
||||||
@@ -4578,7 +4615,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
// report it as produced and let the frontend cache it and release
|
// report it as produced and let the frontend cache it and release
|
||||||
// the handle.
|
// the handle.
|
||||||
if (query == nullptr || query->contextGeneration != g_syncContextGeneration ||
|
if (query == nullptr || query->contextGeneration != g_syncContextGeneration ||
|
||||||
!g_GLESFuncs.glGetQueryObjectuiv || !g_GLESFuncs.glGetQueryObjectui64vEXT) {
|
!g_GLESFuncs.glGetQueryObjectuiv ||
|
||||||
|
(!query->isOcclusion && !g_GLESFuncs.glGetQueryObjectui64vEXT)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// A thread that does not own the ES context cannot issue GL calls,
|
// A thread that does not own the ES context cannot issue GL calls,
|
||||||
@@ -4616,6 +4654,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
// context switch) the result may be garbage, which is tolerable for
|
// context switch) the result may be garbage, which is tolerable for
|
||||||
// an F3 GPU% readout, and consuming the latched flag here could hide
|
// an F3 GPU% readout, and consuming the latched flag here could hide
|
||||||
// the event from another observer.
|
// the event from another observer.
|
||||||
|
if (query->isOcclusion) {
|
||||||
|
GLuint result32 = 0;
|
||||||
|
g_GLESFuncs.glGetQueryObjectuiv(query->queryId, GL_QUERY_RESULT, &result32);
|
||||||
|
*outNanoseconds = static_cast<Uint64>(result32);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
GLuint64 result = 0;
|
GLuint64 result = 0;
|
||||||
g_GLESFuncs.glGetQueryObjectui64vEXT(query->queryId, GL_QUERY_RESULT, &result);
|
g_GLESFuncs.glGetQueryObjectui64vEXT(query->queryId, GL_QUERY_RESULT, &result);
|
||||||
*outNanoseconds = static_cast<Uint64>(result);
|
*outNanoseconds = static_cast<Uint64>(result);
|
||||||
|
|||||||
@@ -130,6 +130,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
BackendQueryHandle BeginTimeElapsedQuery();
|
BackendQueryHandle BeginTimeElapsedQuery();
|
||||||
void EndTimeElapsedQuery(BackendQueryHandle query);
|
void EndTimeElapsedQuery(BackendQueryHandle query);
|
||||||
BackendQueryHandle QueryCounterTimestamp();
|
BackendQueryHandle QueryCounterTimestamp();
|
||||||
|
// GL_ANY_SAMPLES_PASSED(_CONSERVATIVE) occlusion queries: core ES3, independent of
|
||||||
|
// GL_EXT_disjoint_timer_query and of MOBILEGL_DISABLE_TIMERQUERY. Results/deletion
|
||||||
|
// flow through GetQueryResult64/DeleteBackendQuery like the timer queries above.
|
||||||
|
BackendQueryHandle BeginOcclusionQuery();
|
||||||
|
void EndOcclusionQuery(BackendQueryHandle query);
|
||||||
Bool IsQueryResultAvailable(BackendQueryHandle query);
|
Bool IsQueryResultAvailable(BackendQueryHandle query);
|
||||||
// Returns true when a final value landed in *outNanoseconds (a zero for
|
// Returns true when a final value landed in *outNanoseconds (a zero for
|
||||||
// null or stale-generation handles IS final: the frontend may cache it
|
// null or stale-generation handles IS final: the frontend may cache it
|
||||||
|
|||||||
Reference in New Issue
Block a user