[Fix] (EGL): answer client queries and proc lookups without bringing a session up

This commit is contained in:
2026-10-02 20:03:45 -04:00
parent 4095624f87
commit 4f679d669d
2 changed files with 38 additions and 1 deletions
+30 -1
View File
@@ -51,7 +51,14 @@ namespace MobileGL::MG_Impl::EGLImpl {
// re-initialize after the last eglTerminate tore everything down.
// Teardown-ish entry points keep using GetState() and fail benignly
// when MobileGL is not initialized.
//
// Bringing MobileGL up dials its server, and with none reachable that waits out the
// bring-up's whole budget. So it is not attempted then: the entry point fails at once, as
// it would on any implementation without a display to offer.
EGLStateContext* GetStateEnsureInitialized() {
if (!MG_State::pEGLContext && !MobileGL::ImplementationAvailable()) {
return nullptr;
}
MobileGL::EnsureInitialized();
return GetState();
}
@@ -852,6 +859,24 @@ namespace MobileGL::MG_Impl::EGLImpl {
}
char const* QueryString(EGLDisplay display, EGLint name) {
// THE CLIENT QUERIES BRING NOTHING UP. A dispatcher with several vendors asks every one of
// them for its client extensions before it picks one, so answering this must not cost a
// session - nor wait for a server that is not there.
if (display == EGL_NO_DISPLAY) {
switch (name) {
case EGL_VENDOR:
return "MobileGL";
case EGL_VERSION:
return "1.5 MobileGL";
case EGL_CLIENT_APIS:
return "OpenGL OpenGL_ES";
case EGL_EXTENSIONS:
return kClientExtensionString;
default:
if (auto* state = MG_State::pEGLContext.get()) state->SetError(EGL_BAD_PARAMETER);
return nullptr;
}
}
auto* state = GetStateEnsureInitialized();
if (!state) {
return nullptr;
@@ -1348,7 +1373,11 @@ namespace MobileGL::MG_Impl::EGLImpl {
if (!name) {
return nullptr;
}
MobileGL::EnsureInitialized();
// The answer is a static table; the bring-up is only the lazy-initialization contract, and
// it is skipped when there is no server to bring up against (see GetStateEnsureInitialized).
if (MobileGL::ImplementationAvailable()) {
MobileGL::EnsureInitialized();
}
MGLOG_D("eglGetProcAddress(%s)", name);
void* proc = MG_Impl::GetProcAddress(name);
@@ -191,6 +191,7 @@ namespace {
Int32 vendorDisplay = -1; // 1: the glvnd vendor's getPlatformDisplay did
Int32 vendorDevices = -1; // the vendor's eglQueryDevicesEXT count
Int32 glxLoaded = -1; // __glx_Main's answer
Int32 clientQueries = -1; // 1: client extensions and a proc address answered (no display needed)
Int64 elapsedMs = -1; // all of the above, end to end
};
@@ -214,6 +215,11 @@ namespace {
__EGLapiExports exports{};
__EGLapiImports imports{};
const bool vendor = __egl_Main(EGL_VENDOR_ABI_VERSION, &exports, nullptr, &imports) == EGL_TRUE;
// What a dispatcher asks every vendor before choosing one: never a session bring-up.
r.clientQueries = vendor && imports.getProcAddress("eglQueryString") != nullptr &&
EGL::QueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS) != nullptr &&
imports.getDispatchAddress("glClear") != nullptr &&
EGL::GetProcAddress("eglQueryDmaBufModifiersEXT") != nullptr;
r.vendorDisplay = vendor && imports.getPlatformDisplay(EGL_NONE, EGL_DEFAULT_DISPLAY, nullptr) != EGL_NO_DISPLAY;
r.getDisplay = EGL::GetDisplay(EGL_DEFAULT_DISPLAY) != EGL_NO_DISPLAY;
if (vendor) {
@@ -253,6 +259,7 @@ namespace {
EXPECT_EQ(r.getDisplay, 0);
EXPECT_EQ(r.vendorDevices, 0) << "no device either, and no error (glvnd fails its merge on one)";
EXPECT_EQ(r.glxLoaded, 0) << "the GLX vendor must refuse to load so libGLX falls back";
EXPECT_EQ(r.clientQueries, 1) << "client queries are answered without a server";
EXPECT_GE(r.elapsedMs, 0);
EXPECT_LT(r.elapsedMs, 1000) << "declining must not cost the bring-up's 20 s connect budget";
std::error_code ec;
@@ -313,6 +320,7 @@ namespace {
EXPECT_EQ(r.getDisplay, 1);
EXPECT_EQ(r.vendorDevices, 1);
EXPECT_EQ(r.glxLoaded, 1);
EXPECT_EQ(r.clientQueries, 1);
// A process that asked for software rendering is left to the system's other GL, server or not.
const EntryReport software = RunEntryPoints(server.endpoint, logBase, true);