[Fix] (EGL): a second display is served, and glvnd gets its answers from one table

Two defects, both found by asking the client directly rather than by reading its log.

The backend refused to initialize any EGLDisplay but the first - "already bound to a different
EGLDisplay" - and the field it compared against has no reader anywhere else in the tree: the domain
rule was the whole of the rule.  What it cost was a compositor, which asks for the display it will
draw into AFTER probing another one.  The probe succeeded, the real display was refused, and the
caller reported the refusal one layer up as "no usable DRM render device" - a message that names
where it stopped, not why.

The second: glvnd asks a vendor for entry points from two places.  getProcAddress is how it builds
its own static EGL table, and getDispatchAddress is how it answers eglGetProcAddress for every name
it does not know itself - which is every extension entry point, the device queries among them.  This
side answered the second from a GL-only table, so an application asking for eglQueryDeviceStringEXT
got a null for a function the vendor implements.
This commit is contained in:
gold
2026-10-01 12:22:55 -04:00
committed by swung0x48
parent 0dad3fcaa1
commit f458924360
2 changed files with 23 additions and 15 deletions
+9 -5
View File
@@ -172,11 +172,15 @@ namespace MobileGL::MG_Backend {
return false;
}
if (m_eglDisplayInitialized && m_eglDisplay != dpy) {
MGLOG_E("InitializeEGLDisplay failed: backend already bound to a different EGLDisplay");
return false;
}
// A DISPLAY IS A CONNECTION, NOT A RESOURCE THIS BACKEND OWNS, and refusing the second one
// refused a caller for a reason that is not there: the surface and context tables are keyed
// by the display each was created on, every operation carries its own display handle, and
// this field has no reader anywhere - it was the whole of the rule.
//
// What the rule cost was a compositor. One asks for the display it will draw into AFTER
// probing another, so the second eglInitialize is the one that matters, and a refusal there
// is reported one layer up as "no usable DRM render device" - the probe having succeeded
// and the real display having failed, with nothing in between that says which was refused.
m_eglDisplay = dpy;
m_eglDisplayInitialized = true;
if (major) {
@@ -245,7 +245,17 @@ namespace {
{"eglLabelObjectKHR", reinterpret_cast<void*>(VendorLabelObjectKHR)},
};
void* EGLAPIENTRY VendorGetProcAddress(const char* procName) {
// THE SAME ANSWER TO BOTH OF GLVND'S QUESTIONS, and that is not a shortcut - glvnd asks them
// from two places and a library that answers one of them has a hole the size of the other:
//
// getProcAddress is asked for every entry point glvnd builds its own static EGL table
// from, so a null here drops the vendor outright.
// getDispatchAddress is what glvnd falls back to for eglGetProcAddress on any name it does
// not know itself - which is every extension entry point that is not in
// its list, the device queries among them. A vendor that answers these
// from a GL-only table hands the application a null for a function the
// vendor implements, and the caller reports the extension as missing.
void* LookupEntryPoint(const char* procName) {
if (procName == nullptr) {
return nullptr;
}
@@ -259,6 +269,8 @@ namespace {
return reinterpret_cast<void*>(MG_Impl::EGLImpl::GetProcAddress(procName));
}
void* EGLAPIENTRY VendorGetProcAddress(const char* procName) { return LookupEntryPoint(procName); }
// glvnd hands a vendor the index each EGL entry point occupies in its dispatch table, so that a
// vendor whose entry points are thin wrappers around glvnd can call back into the right slot.
// This library's entry points ARE the implementation - they never re-enter glvnd - so there is
@@ -269,15 +281,7 @@ namespace {
(void)index;
}
// glvnd builds its GL/GLES dispatch out of these: one address per entry point, taken from the
// vendor that owns the current display. This library exports them all, so the answer is its
// own table.
void* EGLAPIENTRY VendorGetDispatchAddress(const char* procName) {
if (procName == nullptr) {
return nullptr;
}
return reinterpret_cast<void*>(MG_Impl::EGLImpl::GetProcAddress(procName));
}
void* EGLAPIENTRY VendorGetDispatchAddress(const char* procName) { return LookupEntryPoint(procName); }
} // namespace
extern "C" MOBILEGL_EGL_API EGLBoolean __egl_Main(uint32_t version, const __EGLapiExports* exports,