diff --git a/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp b/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp index aed115df..8fa10876 100644 --- a/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp +++ b/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp @@ -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(width), static_cast(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 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 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 " diff --git a/MobileGL/MG_Impl/EGLImpl/EGLImpl.h b/MobileGL/MG_Impl/EGLImpl/EGLImpl.h index 0a658ea1..b2ac4a08 100644 --- a/MobileGL/MG_Impl/EGLImpl/EGLImpl.h +++ b/MobileGL/MG_Impl/EGLImpl/EGLImpl.h @@ -9,6 +9,8 @@ #pragma once #include +#include + 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 diff --git a/MobileGL/MG_Impl/EGLImpl/Exporting/Definitions.cpp b/MobileGL/MG_Impl/EGLImpl/Exporting/Definitions.cpp index 4821af46..f0413844 100644 --- a/MobileGL/MG_Impl/EGLImpl/Exporting/Definitions.cpp +++ b/MobileGL/MG_Impl/EGLImpl/Exporting/Definitions.cpp @@ -9,14 +9,34 @@ #include #include "../EGLImpl.h" +#include + +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 WidenAttribList(const EGLint* attribs) { + std::vector widened; + if (attribs == nullptr) return widened; + for (; *attribs != EGL_NONE; attribs += 2) { + widened.push_back(static_cast(attribs[0])); + widened.push_back(static_cast(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(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 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(attrib_list)); + const std::vector 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); } diff --git a/MobileGL/MG_Impl/EGLImpl/Exporting/GlvndVendor.cpp b/MobileGL/MG_Impl/EGLImpl/Exporting/GlvndVendor.cpp index 9976b10e..9d85948b 100644 --- a/MobileGL/MG_Impl/EGLImpl/Exporting/GlvndVendor.cpp +++ b/MobileGL/MG_Impl/EGLImpl/Exporting/GlvndVendor.cpp @@ -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(eglWaitSync)}, {"eglCreateImage", reinterpret_cast(eglCreateImage)}, {"eglDestroyImage", reinterpret_cast(eglDestroyImage)}, + {"eglCreateImageKHR", reinterpret_cast(eglCreateImageKHR)}, + {"eglDestroyImageKHR", reinterpret_cast(eglDestroyImageKHR)}, {"eglCreatePlatformWindowSurface", reinterpret_cast(eglCreatePlatformWindowSurface)}, {"eglCreatePlatformPixmapSurface", reinterpret_cast(eglCreatePlatformPixmapSurface)}, // The platform and current-state queries: not in glvnd's required list, but the names an diff --git a/MobileGL/MG_Impl/EGLImpl/WaylandWindow.cpp b/MobileGL/MG_Impl/EGLImpl/WaylandWindow.cpp index 29e029ba..1fae94eb 100644 --- a/MobileGL/MG_Impl/EGLImpl/WaylandWindow.cpp +++ b/MobileGL/MG_Impl/EGLImpl/WaylandWindow.cpp @@ -324,6 +324,17 @@ namespace MobileGL::MG_Impl::EGLImpl::Wayland { return UniquePtr(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; diff --git a/MobileGL/MG_Impl/EGLImpl/WaylandWindow.h b/MobileGL/MG_Impl/EGLImpl/WaylandWindow.h index 41b02819..0317b6ad 100644 --- a/MobileGL/MG_Impl/EGLImpl/WaylandWindow.h +++ b/MobileGL/MG_Impl/EGLImpl/WaylandWindow.h @@ -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: diff --git a/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp b/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp index 4ffd3030..ec41f1cb 100644 --- a/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp +++ b/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp @@ -22,6 +22,7 @@ #include "../Sync/GL_Sync.h" #include "../Debug/GL_Debug.h" #include +#include #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 diff --git a/MobileGL/MG_Impl/GetProcAddress.cpp b/MobileGL/MG_Impl/GetProcAddress.cpp index fe94f063..f74a5721 100644 --- a/MobileGL/MG_Impl/GetProcAddress.cpp +++ b/MobileGL/MG_Impl/GetProcAddress.cpp @@ -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); diff --git a/MobileGL/MG_Impl/Pipe/Tracker.h b/MobileGL/MG_Impl/Pipe/Tracker.h index 93c55d59..b26b403b 100644 --- a/MobileGL/MG_Impl/Pipe/Tracker.h +++ b/MobileGL/MG_Impl/Pipe/Tracker.h @@ -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; diff --git a/MobileGL/MG_Remote/Client/BackendObject_Remote.cpp b/MobileGL/MG_Remote/Client/BackendObject_Remote.cpp index c09e8578..48861f15 100644 --- a/MobileGL/MG_Remote/Client/BackendObject_Remote.cpp +++ b/MobileGL/MG_Remote/Client/BackendObject_Remote.cpp @@ -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(static_cast(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