[Fix] (Client): serialize multi-threaded GL streams, follow Wayland window resizes, export the KHR image aliases

This commit is contained in:
2026-10-02 01:38:21 -04:00
parent 0b314ddae7
commit ccf8eb6571
10 changed files with 259 additions and 7 deletions
+63 -3
View File
@@ -76,15 +76,22 @@ namespace MobileGL::MG_Impl::EGLImpl {
//
// An unchanged token emits NOTHING: an identical eglMakeCurrent is a legal no-op in
// EGL and the session is already bound to that context.
//
// THE COMPARISON IS AGAINST THE BINDING LAST SENT, NOT THIS THREAD'S PREVIOUS CONTEXT
// (GLStreamScope in EGLImpl.h): the session has one current context and another thread
// may have moved it since this one last bound. The caller holds EGLOperationMutex.
thread_local Uint64 t_threadContextToken = 0;
Uint64 g_boundContextToken = 0; // guarded by EGLOperationMutex
void EmitContextBinding(EGLStateContext* state, EGLContext switched, Uint64 previousToken) {
(void)previousToken;
if (MG_Config::Transport == MG_Config::TransportMode::Monolith) return;
// The apply thread IS the server in this process; a record emitted there would
// wait on the thread that has to apply it (EmitAndWait), which is a deadlock and
// not a slow path.
if (MG_Remote::Client::RunsAsTheServerRole()) return;
const Uint64 token = switched == EGL_NO_CONTEXT ? 0 : state->GetContextClientToken(switched);
if (token == previousToken) return;
(void)MG_Remote::Client::EmitBindContextRecord(token);
t_threadContextToken = switched == EGL_NO_CONTEXT ? 0 : state->GetContextClientToken(switched);
StreamBindCallingThreadLocked();
}
#endif
@@ -93,6 +100,24 @@ namespace MobileGL::MG_Impl::EGLImpl {
return mutex;
}
#if MOBILEGL_BUILD_DISAGGREGATED
} // namespace
Bool StreamGateActive() {
return MG_Config::Transport != MG_Config::TransportMode::Monolith &&
!MG_Remote::Client::RunsAsTheServerRole();
}
std::recursive_mutex& StreamMutex() { return EGLOperationMutex(); }
void StreamBindCallingThreadLocked() {
const Uint64 token = t_threadContextToken;
if (token == g_boundContextToken) return;
if (MG_Remote::Client::EmitBindContextRecord(token)) g_boundContextToken = token;
}
namespace {
#endif
String CurrentThreadIdString() {
std::ostringstream stream;
stream << std::this_thread::get_id();
@@ -220,6 +245,25 @@ namespace MobileGL::MG_Impl::EGLImpl {
*out = surface;
return true;
}
// A WAYLAND WINDOW THE APPLICATION RESIZED (wl_egl_window_resize). As with a driver's own
// window surface, the new size takes effect at the next make-current or swap: the EGL
// surface reports it, the wl_shm presentation adopts it, and the drawable behind it is
// resized so the frame rendered at the new size is the frame read back. Qt resizes the
// window and then makes the context current, so the make-current arm is the one it hits.
void ApplyWaylandResize(EGLStateContext* state, EGLDisplay dpy, EGLSurface surface) {
const auto it = WaylandSurfaces().find(surface);
if (it == WaylandSurfaces().end()) return;
EGLint width = 0;
EGLint height = 0;
if (!it->second->TakeResize(&width, &height)) return;
(void)state->ResizeSurface(dpy, surface, width, height);
auto* backendObject = MG_Backend::pActiveBackendObject.get();
if (backendObject == nullptr ||
!backendObject->ResizeEGLWindowSurface(surface, static_cast<Uint32>(width), static_cast<Uint32>(height))) {
MGLOG_E("Wayland: the drawable behind a %dx%d window could not be resized", width, height);
}
}
#endif
} // namespace
@@ -321,6 +365,10 @@ namespace MobileGL::MG_Impl::EGLImpl {
EGLBoolean SwapBuffers(EGLDisplay dpy, EGLSurface draw) {
const std::lock_guard<std::recursive_mutex> operationLock(EGLOperationMutex());
#if MOBILEGL_BUILD_DISAGGREGATED
// The present is this thread's context's (GLStreamScope in EGLImpl.h).
if (StreamGateActive()) StreamBindCallingThreadLocked();
#endif
auto* state = GetState();
if (!state) {
return EGL_FALSE;
@@ -348,6 +396,10 @@ namespace MobileGL::MG_Impl::EGLImpl {
state->SetError(EGL_BAD_SURFACE);
return EGL_FALSE;
}
#if MOBILEGL_WAYLAND_WINDOWS
// A resize made between frames takes effect for the next one.
ApplyWaylandResize(state, dpy, draw);
#endif
return EGL_TRUE;
}
@@ -455,6 +507,9 @@ namespace MobileGL::MG_Impl::EGLImpl {
MGLOG_D("eglMakeCurrent begin thread=%s dpy=%p draw=%p read=%p ctx=%p oldDpy=%p oldDraw=%p oldRead=%p oldCtx=%p",
threadId.c_str(), dpy, draw, read, ctx, oldDisplay, oldDraw, oldRead, oldContext);
#if MOBILEGL_WAYLAND_WINDOWS
if (draw != EGL_NO_SURFACE) ApplyWaylandResize(state, dpy, draw);
#endif
if (!state->MakeCurrent(dpy, draw, read, ctx)) {
const EGLint error = state->ConsumeError();
MGLOG_D("eglMakeCurrent rejected by EGLState thread=%s error=0x%04x", threadId.c_str(), error);
@@ -520,6 +575,11 @@ namespace MobileGL::MG_Impl::EGLImpl {
#if MOBILEGL_BUILD_DISAGGREGATED
if (contextToken != 0 && MG_Config::Transport != MG_Config::TransportMode::Monolith &&
!MG_Remote::Client::RunsAsTheServerRole()) {
const std::lock_guard<std::recursive_mutex> streamLock(EGLOperationMutex());
// The server drops its binding to 0 when the current context is destroyed
// (ServerSession::DestroyContext's compare-and-swap); the gate mirrors it so the next
// write re-binds.
if (g_boundContextToken == contextToken) g_boundContextToken = 0;
const auto emitted = MG_Remote::Client::SendDestroyContextFrame(contextToken);
if (emitted == MG_Remote::Client::ContextFrameEmit::Refused) {
MGLOG_E_ONCE("eglDestroyContext: the server holds no context token %llu; the "
+60
View File
@@ -9,6 +9,8 @@
#pragma once
#include <Includes.h>
#include <mutex>
namespace MobileGL::MG_Impl::EGLImpl {
EGLSurface CreateWindowSurface(EGLDisplay dpy, EGLConfig config, NativeWindowType window,
const EGLint* attrib_list);
@@ -62,4 +64,62 @@ namespace MobileGL::MG_Impl::EGLImpl {
const EGLAttrib* attrib_list);
EGLBoolean WaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags);
__eglMustCastToProperFunctionPointerType GetProcAddress(const char* name);
#if MOBILEGL_BUILD_DISAGGREGATED
// THE CLIENT'S STREAM GATE. Under a split transport every thread of the process writes into
// ONE ring, and the server attributes each record to the context the last applied
// `bind_context` named - one current context per SESSION. A context, though, is current per
// THREAD, and Qt renders each window on its own QSGRenderThread: two threads' records then
// interleave on the ring with no binding between them and land in the wrong context's
// applier (empty sampler windows at a Clear, another context's buffers under an upload).
//
// So every GL entry point and every EGL operation that writes to the stream runs under one
// process-wide lock (EGLOperationMutex, which the EGL side already held), and before it
// writes, the server is re-bound to the CALLING thread's context whenever the last binding
// actually sent names a different one. A no-op scope on a monolith transport and on the
// server's own apply thread.
Bool StreamGateActive();
std::recursive_mutex& StreamMutex();
void StreamBindCallingThreadLocked();
class GLStreamScope {
public:
GLStreamScope() {
if (!StreamGateActive()) return;
m_mutex = &StreamMutex();
m_mutex->lock();
StreamBindCallingThreadLocked();
}
~GLStreamScope() {
if (m_mutex != nullptr) m_mutex->unlock();
}
GLStreamScope(const GLStreamScope&) = delete;
GLStreamScope& operator=(const GLStreamScope&) = delete;
private:
std::recursive_mutex* m_mutex = nullptr;
};
// The lock alone, for every EGL entry point: an EGL call may publish into the ring too (a
// surface forwarder's catch-up wait, a present, a context frame), and a second thread's GL
// record written into the middle of it is a torn record the server latches the session on.
class StreamLockScope {
public:
StreamLockScope() {
if (!StreamGateActive()) return;
m_mutex = &StreamMutex();
m_mutex->lock();
}
~StreamLockScope() {
if (m_mutex != nullptr) m_mutex->unlock();
}
StreamLockScope(const StreamLockScope&) = delete;
StreamLockScope& operator=(const StreamLockScope&) = delete;
private:
std::recursive_mutex* m_mutex = nullptr;
};
#else
class GLStreamScope {};
class StreamLockScope {};
#endif
} // namespace MobileGL::MG_Impl::EGLImpl
@@ -9,14 +9,34 @@
#include <Includes.h>
#include "../EGLImpl.h"
#include <vector>
namespace {
// The KHR/EXT spellings take EGLint attribute lists where the core 1.5 entry points take
// EGLAttrib (pointer-sized). Reinterpreting one as the other reads two EGLints as one
// attribute on 64-bit, so the list is widened instead.
std::vector<EGLAttrib> WidenAttribList(const EGLint* attribs) {
std::vector<EGLAttrib> widened;
if (attribs == nullptr) return widened;
for (; *attribs != EGL_NONE; attribs += 2) {
widened.push_back(static_cast<EGLAttrib>(attribs[0]));
widened.push_back(static_cast<EGLAttrib>(attribs[1]));
}
widened.push_back(EGL_NONE);
return widened;
}
} // namespace
MOBILEGL_EGL_API EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, NativeWindowType window,
const EGLint* attrib_list) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglCreateWindowSurface(dpy=%p, config=%p, window=%p, attrib_list=%p)", dpy, config, window, attrib_list);
return MobileGL::MG_Impl::EGLImpl::CreateWindowSurface(dpy, config, window, attrib_list);
}
MOBILEGL_EGL_API EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint* attrib_list, EGLConfig* configs,
EGLint config_size, EGLint* num_config) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglChooseConfig(dpy=%p, attrib_list=%p, configs=%p, config_size=%d, num_config=%p)", dpy, attrib_list,
configs, config_size, num_config);
return MobileGL::MG_Impl::EGLImpl::ChooseConfig(dpy, attrib_list, configs, config_size, num_config);
@@ -24,6 +44,7 @@ MOBILEGL_EGL_API EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint* attrib
MOBILEGL_EGL_API EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext shareCtx,
const EGLint* attrib_list) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglCreateContext(dpy=%p, config=%p, shareCtx=%p, attrib_list=%p)", dpy, config, shareCtx, attrib_list);
#ifdef TRACY_ENABLE
tracy::StartupProfiler();
@@ -32,26 +53,31 @@ MOBILEGL_EGL_API EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, E
}
MOBILEGL_EGL_API EGLBoolean eglInitialize(EGLDisplay dpy, EGLint* major, EGLint* minor) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglInitialize(dpy=%p, major=%p, minor=%p)", dpy, major, minor);
return MobileGL::MG_Impl::EGLImpl::Initialize(dpy, major, minor);
}
MOBILEGL_EGL_API EGLDisplay eglGetDisplay(NativeDisplayType display) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglGetDisplay(display=%p)", display);
return MobileGL::MG_Impl::EGLImpl::GetDisplay(display);
}
MOBILEGL_EGL_API EGLint eglGetError() {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglGetError()");
return MobileGL::MG_Impl::EGLImpl::GetError();
}
MOBILEGL_EGL_API EGLBoolean eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglMakeCurrent(dpy=%p, draw=%p, read=%p, ctx=%p)", dpy, draw, read, ctx);
return MobileGL::MG_Impl::EGLImpl::MakeCurrent(dpy, draw, read, ctx);
}
MOBILEGL_EGL_API EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglDestroyContext(dpy=%p, ctx=%p)", dpy, ctx);
#ifdef TRACY_ENABLE
tracy::ShutdownProfiler();
@@ -60,56 +86,67 @@ MOBILEGL_EGL_API EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx) {
}
MOBILEGL_EGL_API EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglDestroySurface(dpy=%p, surface=%p)", dpy, surface);
return MobileGL::MG_Impl::EGLImpl::DestroySurface(dpy, surface);
}
MOBILEGL_EGL_API EGLBoolean eglTerminate(EGLDisplay dpy) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglTerminate(dpy=%p)", dpy);
return MobileGL::MG_Impl::EGLImpl::Terminate(dpy);
}
MOBILEGL_EGL_API EGLBoolean eglReleaseThread(void) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglReleaseThread()");
return MobileGL::MG_Impl::EGLImpl::ReleaseThread();
}
MOBILEGL_EGL_API EGLContext eglGetCurrentContext(void) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglGetCurrentContext()");
return MobileGL::MG_Impl::EGLImpl::GetCurrentContext();
}
MOBILEGL_EGL_API EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint* value) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglGetConfigAttrib(dpy=%p, config=%p, attribute=%d, value=%p)", dpy, config, attribute, value);
return MobileGL::MG_Impl::EGLImpl::GetConfigAttrib(dpy, config, attribute, value);
}
MOBILEGL_EGL_API EGLBoolean eglBindAPI(EGLenum api) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglBindAPI(api=%u)", api);
return MobileGL::MG_Impl::EGLImpl::BindAPI(api);
}
MOBILEGL_EGL_API EGLSurface eglGetCurrentSurface(EGLint readdraw) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglGetCurrentSurface(readdraw=%d)", readdraw);
return MobileGL::MG_Impl::EGLImpl::GetCurrentSurface(readdraw);
}
MOBILEGL_EGL_API EGLBoolean eglQuerySurface(EGLDisplay display, EGLSurface surface, EGLint attribute, EGLint* value) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglQuerySurface(display=%p, surface=%p, attribute=%d, value=%p)", display, surface, attribute, value);
return MobileGL::MG_Impl::EGLImpl::QuerySurface(display, surface, attribute, value);
}
MOBILEGL_EGL_API char const* eglQueryString(EGLDisplay display, EGLint name) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglQueryString(display=%p, name=%d)", display, name);
return MobileGL::MG_Impl::EGLImpl::QueryString(display, name);
}
MOBILEGL_EGL_API EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglSwapInterval(dpy=%p, interval=%d)", dpy, interval);
return MobileGL::MG_Impl::EGLImpl::SwapInterval(dpy, interval);
}
MOBILEGL_EGL_API EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglSwapBuffers(dpy=%p, draw=%p)", dpy, draw);
#ifdef TRACY_ENABLE
FrameMark;
@@ -118,32 +155,38 @@ MOBILEGL_EGL_API EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw) {
}
MOBILEGL_EGL_API EGLSurface eglCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, const EGLint* attrib_list) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglCreatePbufferSurface(dpy=%p, config=%p, attrib_list=%p)", dpy, config, attrib_list);
return MobileGL::MG_Impl::EGLImpl::CreatePbufferSurface(dpy, config, attrib_list);
}
MOBILEGL_EGL_API __eglMustCastToProperFunctionPointerType eglGetProcAddress(const char* name) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglGetProcAddress(name=%s)", name ? name : "null");
return MobileGL::MG_Impl::EGLImpl::GetProcAddress(name);
}
MOBILEGL_EGL_API EGLBoolean eglBindTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglBindTexImage(dpy=%p, surface=%p, buffer=%d)", dpy, surface, buffer);
return MobileGL::MG_Impl::EGLImpl::BindTexImage(dpy, surface, buffer);
}
MOBILEGL_EGL_API EGLBoolean eglReleaseTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglReleaseTexImage(dpy=%p, surface=%p, buffer=%d)", dpy, surface, buffer);
return MobileGL::MG_Impl::EGLImpl::ReleaseTexImage(dpy, surface, buffer);
}
MOBILEGL_EGL_API EGLBoolean eglCopyBuffers(EGLDisplay dpy, EGLSurface surface, EGLNativePixmapType target) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglCopyBuffers(dpy=%p, surface=%p, target=%p)", dpy, surface, target);
return MobileGL::MG_Impl::EGLImpl::CopyBuffers(dpy, surface, target);
}
MOBILEGL_EGL_API EGLSurface eglCreatePbufferFromClientBuffer(EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer,
EGLConfig config, const EGLint* attrib_list) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglCreatePbufferFromClientBuffer(dpy=%p, buftype=%u, buffer=%p, config=%p, attrib_list=%p)", dpy, buftype,
buffer, config, attrib_list);
return MobileGL::MG_Impl::EGLImpl::CreatePbufferFromClientBuffer(dpy, buftype, buffer, config, attrib_list);
@@ -151,85 +194,119 @@ MOBILEGL_EGL_API EGLSurface eglCreatePbufferFromClientBuffer(EGLDisplay dpy, EGL
MOBILEGL_EGL_API EGLSurface eglCreatePixmapSurface(EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap,
const EGLint* attrib_list) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglCreatePixmapSurface(dpy=%p, config=%p, pixmap=%p, attrib_list=%p)", dpy, config, pixmap, attrib_list);
return MobileGL::MG_Impl::EGLImpl::CreatePixmapSurface(dpy, config, pixmap, attrib_list);
}
MOBILEGL_EGL_API EGLBoolean eglGetConfigs(EGLDisplay dpy, EGLConfig* configs, EGLint config_size, EGLint* num_config) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglGetConfigs(dpy=%p, configs=%p, config_size=%d, num_config=%p)", dpy, configs, config_size, num_config);
return MobileGL::MG_Impl::EGLImpl::GetConfigs(dpy, configs, config_size, num_config);
}
MOBILEGL_EGL_API EGLDisplay eglGetCurrentDisplay(void) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglGetCurrentDisplay()");
return MobileGL::MG_Impl::EGLImpl::GetCurrentDisplay();
}
MOBILEGL_EGL_API EGLenum eglQueryAPI(void) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglQueryAPI()");
return MobileGL::MG_Impl::EGLImpl::QueryAPI();
}
MOBILEGL_EGL_API EGLBoolean eglQueryContext(EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint* value) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglQueryContext(dpy=%p, ctx=%p, attribute=%d, value=%p)", dpy, ctx, attribute, value);
return MobileGL::MG_Impl::EGLImpl::QueryContext(dpy, ctx, attribute, value);
}
MOBILEGL_EGL_API EGLBoolean eglSurfaceAttrib(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglSurfaceAttrib(dpy=%p, surface=%p, attribute=%d, value=%d)", dpy, surface, attribute, value);
return MobileGL::MG_Impl::EGLImpl::SurfaceAttrib(dpy, surface, attribute, value);
}
MOBILEGL_EGL_API EGLBoolean eglWaitClient(void) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglWaitClient()");
return MobileGL::MG_Impl::EGLImpl::WaitClient();
}
MOBILEGL_EGL_API EGLBoolean eglWaitGL(void) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglWaitGL()");
return MobileGL::MG_Impl::EGLImpl::WaitGL();
}
MOBILEGL_EGL_API EGLBoolean eglWaitNative(EGLint engine) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglWaitNative(engine=%d)", engine);
return MobileGL::MG_Impl::EGLImpl::WaitNative(engine);
}
MOBILEGL_EGL_API EGLSync eglCreateSync(EGLDisplay dpy, EGLenum type, const EGLAttrib* attrib_list) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglCreateSync(dpy=%p, type=%u, attrib_list=%p)", dpy, type, attrib_list);
return MobileGL::MG_Impl::EGLImpl::CreateSync(dpy, type, attrib_list);
}
MOBILEGL_EGL_API EGLBoolean eglDestroySync(EGLDisplay dpy, EGLSync sync) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglDestroySync(dpy=%p, sync=%p)", dpy, sync);
return MobileGL::MG_Impl::EGLImpl::DestroySync(dpy, sync);
}
MOBILEGL_EGL_API EGLint eglClientWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglClientWaitSync(dpy=%p, sync=%p, flags=%d, timeout=%llu)", dpy, sync, flags,
static_cast<unsigned long long>(timeout));
return MobileGL::MG_Impl::EGLImpl::ClientWaitSync(dpy, sync, flags, timeout);
}
MOBILEGL_EGL_API EGLBoolean eglGetSyncAttrib(EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib* value) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglGetSyncAttrib(dpy=%p, sync=%p, attribute=%d, value=%p)", dpy, sync, attribute, value);
return MobileGL::MG_Impl::EGLImpl::GetSyncAttrib(dpy, sync, attribute, value);
}
MOBILEGL_EGL_API EGLImage eglCreateImage(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer,
const EGLAttrib* attrib_list) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglCreateImage(dpy=%p, ctx=%p, target=%u, buffer=%p, attrib_list=%p)", dpy, ctx, target, buffer,
attrib_list);
return MobileGL::MG_Impl::EGLImpl::CreateImage(dpy, ctx, target, buffer, attrib_list);
}
MOBILEGL_EGL_API EGLBoolean eglDestroyImage(EGLDisplay dpy, EGLImage image) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglDestroyImage(dpy=%p, image=%p)", dpy, image);
return MobileGL::MG_Impl::EGLImpl::DestroyImage(dpy, image);
}
// EGL_KHR_image_base's spellings. KWin loads these two by name and calls eglDestroyImageKHR
// for every cached client buffer when it dies, wl_shm ones included; a null answer from
// eglGetProcAddress made that a jump to address 0.
MOBILEGL_EGL_API EGLImage eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer,
const EGLint* attrib_list) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglCreateImageKHR(dpy=%p, ctx=%p, target=%u, buffer=%p, attrib_list=%p)", dpy, ctx, target, buffer,
attrib_list);
const std::vector<EGLAttrib> attribs = WidenAttribList(attrib_list);
return MobileGL::MG_Impl::EGLImpl::CreateImage(dpy, ctx, target, buffer, attrib_list ? attribs.data() : nullptr);
}
MOBILEGL_EGL_API EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImage image) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglDestroyImageKHR(dpy=%p, image=%p)", dpy, image);
return MobileGL::MG_Impl::EGLImpl::DestroyImage(dpy, image);
}
MOBILEGL_EGL_API EGLDisplay eglGetPlatformDisplay(EGLenum platform, void* native_display,
const EGLAttrib* attrib_list) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglGetPlatformDisplay(platform=%u, native_display=%p, attrib_list=%p)", platform, native_display,
attrib_list);
return MobileGL::MG_Impl::EGLImpl::GetPlatformDisplay(platform, native_display, attrib_list);
@@ -237,14 +314,17 @@ MOBILEGL_EGL_API EGLDisplay eglGetPlatformDisplay(EGLenum platform, void* native
MOBILEGL_EGL_API EGLDisplay eglGetPlatformDisplayEXT(EGLenum platform, void* native_display,
const EGLint* attrib_list) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglGetPlatformDisplayEXT(platform=%u, native_display=%p, attrib_list=%p)", platform, native_display,
attrib_list);
return MobileGL::MG_Impl::EGLImpl::GetPlatformDisplay(
platform, native_display, reinterpret_cast<const EGLAttrib*>(attrib_list));
const std::vector<EGLAttrib> attribs = WidenAttribList(attrib_list);
return MobileGL::MG_Impl::EGLImpl::GetPlatformDisplay(platform, native_display,
attrib_list ? attribs.data() : nullptr);
}
MOBILEGL_EGL_API EGLSurface eglCreatePlatformWindowSurface(EGLDisplay dpy, EGLConfig config, void* native_window,
const EGLAttrib* attrib_list) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglCreatePlatformWindowSurface(dpy=%p, config=%p, native_window=%p, attrib_list=%p)", dpy, config,
native_window, attrib_list);
return MobileGL::MG_Impl::EGLImpl::CreatePlatformWindowSurface(dpy, config, native_window, attrib_list);
@@ -252,12 +332,14 @@ MOBILEGL_EGL_API EGLSurface eglCreatePlatformWindowSurface(EGLDisplay dpy, EGLCo
MOBILEGL_EGL_API EGLSurface eglCreatePlatformPixmapSurface(EGLDisplay dpy, EGLConfig config, void* native_pixmap,
const EGLAttrib* attrib_list) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglCreatePlatformPixmapSurface(dpy=%p, config=%p, native_pixmap=%p, attrib_list=%p)", dpy, config,
native_pixmap, attrib_list);
return MobileGL::MG_Impl::EGLImpl::CreatePlatformPixmapSurface(dpy, config, native_pixmap, attrib_list);
}
MOBILEGL_EGL_API EGLBoolean eglWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags) {
const MobileGL::MG_Impl::EGLImpl::StreamLockScope mglStreamLock;
MGLOG_D("eglWaitSync(dpy=%p, sync=%p, flags=%d)", dpy, sync, flags);
return MobileGL::MG_Impl::EGLImpl::WaitSync(dpy, sync, flags);
}
@@ -32,6 +32,11 @@
#include "../EGLImpl.h"
#include "../EGLPlatformExtensions.h"
// Defined in Definitions.cpp; eglext.h declares them only under EGL_EGLEXT_PROTOTYPES.
extern "C" EGLImage eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer,
const EGLint* attrib_list);
extern "C" EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImage image);
namespace {
using namespace MobileGL;
@@ -221,6 +226,8 @@ namespace {
{"eglWaitSync", reinterpret_cast<void*>(eglWaitSync)},
{"eglCreateImage", reinterpret_cast<void*>(eglCreateImage)},
{"eglDestroyImage", reinterpret_cast<void*>(eglDestroyImage)},
{"eglCreateImageKHR", reinterpret_cast<void*>(eglCreateImageKHR)},
{"eglDestroyImageKHR", reinterpret_cast<void*>(eglDestroyImageKHR)},
{"eglCreatePlatformWindowSurface", reinterpret_cast<void*>(eglCreatePlatformWindowSurface)},
{"eglCreatePlatformPixmapSurface", reinterpret_cast<void*>(eglCreatePlatformPixmapSurface)},
// The platform and current-state queries: not in glvnd's required list, but the names an
@@ -324,6 +324,17 @@ namespace MobileGL::MG_Impl::EGLImpl::Wayland {
return UniquePtr<WindowSurface>(new WindowSurface(std::move(impl)));
}
Bool WindowSurface::TakeResize(EGLint* width, EGLint* height) {
Impl& impl = *m_impl;
if (impl.window == nullptr || impl.window->width <= 0 || impl.window->height <= 0) return false;
if (impl.window->width == impl.width && impl.window->height == impl.height) return false;
impl.width = impl.window->width;
impl.height = impl.window->height;
*width = impl.width;
*height = impl.height;
return true;
}
Bool WindowSurface::Present() {
Impl& impl = *m_impl;
if (impl.window == nullptr) return false;
+5
View File
@@ -61,6 +61,11 @@ namespace MobileGL::MG_Impl::EGLImpl::Wayland {
// window; the swap itself goes on regardless.
Bool Present();
// wl_egl_window_resize's effect, taken: true (with the new size) when the application gave
// its wl_egl_window a size this presentation is not at yet. The presentation adopts it -
// its next wl_shm buffer is that size - and the caller resizes the drawable behind it.
Bool TakeResize(EGLint* width, EGLint* height);
struct Impl;
private:
@@ -22,6 +22,7 @@
#include "../Sync/GL_Sync.h"
#include "../Debug/GL_Debug.h"
#include <MG_State/GLState/Core.h>
#include <MG_Impl/EGLImpl/EGLImpl.h>
#define DECLARE_GL_FUNCTION_STUB_HEAD(type, name, ...) MOBILEGL_GL_API type gl##name(__VA_ARGS__) {
@@ -40,22 +41,26 @@
#define DECLARE_GL_FUNCTION_END(type, name, ...) \
ZoneScopedC(TRACY_ZONECOLOR_ENTRY); \
MGLOG_D("Implementing function: %s(...)", __FUNCTION__); \
const MobileGL::MG_Impl::EGLImpl::GLStreamScope mglStreamScope; \
return MobileGL::MG_Impl::GLImpl::name(__VA_ARGS__); \
}
#define DECLARE_GL_FUNCTION_END_NO_RETURN(type, name, ...) \
ZoneScopedC(TRACY_ZONECOLOR_ENTRY); \
MGLOG_D("Implementing function: %s(...)", __FUNCTION__); \
const MobileGL::MG_Impl::EGLImpl::GLStreamScope mglStreamScope; \
MobileGL::MG_Impl::GLImpl::name(__VA_ARGS__); \
}
#else
#define DECLARE_GL_FUNCTION_END(type, name, ...) \
MGLOG_D("Implementing function: %s(...)", __FUNCTION__); \
const MobileGL::MG_Impl::EGLImpl::GLStreamScope mglStreamScope; \
return MobileGL::MG_Impl::GLImpl::name(__VA_ARGS__); \
}
#define DECLARE_GL_FUNCTION_END_NO_RETURN(type, name, ...) \
MGLOG_D("Implementing function: %s(...)", __FUNCTION__); \
const MobileGL::MG_Impl::EGLImpl::GLStreamScope mglStreamScope; \
MobileGL::MG_Impl::GLImpl::name(__VA_ARGS__); \
}
#endif
@@ -116,12 +121,14 @@ DECLARE_GL_FUNCTION_HEAD(void, EnableVertexAttribArray, GLuint index) DECLARE_GL
// call really did have nothing to do; MGPipeClientFinish answers false-cheap there too.
MOBILEGL_GL_API void glFinish() {
MGLOG_D("Implementing function: %s(...)", __FUNCTION__);
const MobileGL::MG_Impl::EGLImpl::GLStreamScope mglStreamScope;
#if MOBILEGL_BUILD_DISAGGREGATED
MobileGL::MG_Pipe::MGPipeClientFinish();
#endif
}
MOBILEGL_GL_API void glFlush() {
MGLOG_D("Implementing function: %s(...)", __FUNCTION__);
const MobileGL::MG_Impl::EGLImpl::GLStreamScope mglStreamScope;
#if MOBILEGL_BUILD_DISAGGREGATED
MobileGL::MG_Pipe::MGPipeClientFlush();
#endif
+5
View File
@@ -15,6 +15,9 @@
#endif
extern "C" EGLDisplay eglGetPlatformDisplayEXT(EGLenum platform, void* native_display, const EGLint* attrib_list);
extern "C" EGLImage eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer,
const EGLint* attrib_list);
extern "C" EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImage image);
#define GETPROC(name, var) \
if (strcmp(#name, var) == 0) { \
@@ -64,6 +67,8 @@ namespace MobileGL::MG_Impl {
GETPROC(eglGetSyncAttrib, name);
GETPROC(eglCreateImage, name);
GETPROC(eglDestroyImage, name);
GETPROC(eglCreateImageKHR, name);
GETPROC(eglDestroyImageKHR, name);
GETPROC(eglGetPlatformDisplay, name);
GETPROC(eglGetPlatformDisplayEXT, name);
GETPROC(eglCreatePlatformWindowSurface, name);
+9 -1
View File
@@ -303,9 +303,15 @@ namespace MobileGL::MG_Pipe {
// A different context is a different server: nothing the tracker latched about
// the old one says anything about this one, and the first walk on a fresh
// context must publish a COMPLETE state rather than an increment.
if (m_context != &ctx) {
//
// KEYED ON THE CONTEXT'S NEVER-REUSED ID, NOT ITS ADDRESS: a context destroyed and
// remade at the same heap address (Qt does this) is a fresh server-side applier
// with empty windows, and an address compare would skip the re-prime that refills
// them.
if (m_context != &ctx || m_contextId != ctx.GetTextureContextId()) {
Reset();
m_context = &ctx;
m_contextId = ctx.GetTextureContextId();
}
const Bool wasPrimed = m_primed;
@@ -811,6 +817,7 @@ namespace MobileGL::MG_Pipe {
m_staged = RenderStateParameters{};
m_stagedAttribs = AttribDefaults{};
m_context = nullptr;
m_contextId = 0;
m_lastDirty = 0;
m_primed = false;
m_freshlyPrimed = false;
@@ -917,6 +924,7 @@ namespace MobileGL::MG_Pipe {
AttribDefaults m_stagedAttribs{};
const void* m_context = nullptr;
Uint64 m_contextId = 0;
Uint32 m_lastDirty = 0;
Bool m_primed = false;
Bool m_freshlyPrimed = false;
@@ -367,7 +367,14 @@ namespace MobileGL::MG_Remote::Client {
// InitCapabilities' answer comes from a snapshot the server can only publish once its
// own InitCapabilities has run - and ServerMakeEGLCurrent is what publishes it.
WaitForApplyBeforeEglForwarder("MakeEGLCurrent");
if (!Server::ServerMakeEGLCurrent(dpy, draw, read, ctx)) return false;
// The frame carries the context's CLIENT TOKEN rather than its handle (the server's
// MakeCurrent arm binds the session to it before the native bind). Identity is all the
// server reads from the field otherwise, and a token is as unique as a handle.
const Uint64 contextToken =
(ctx != EGL_NO_CONTEXT && MG_State::pEGLContext) ? MG_State::pEGLContext->GetContextClientToken(ctx) : 0;
const EGLContext wireContext =
contextToken != 0 ? reinterpret_cast<EGLContext>(static_cast<std::uintptr_t>(contextToken)) : ctx;
if (!Server::ServerMakeEGLCurrent(dpy, draw, read, wireContext)) return false;
if (!MG_Backend::BackendObject::MakeEGLCurrent(dpy, draw, read, ctx)) return false;
// R-12 ARM (a) ON EVERY SUCCESSFUL MAKE-CURRENT (codex 12). ServerMakeEGLCurrent above