From 16488d58018616df0b58f495812af759ba8dd0eb Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Thu, 1 Oct 2026 09:47:12 -0400 Subject: [PATCH] [Feature] (GL): glFlush presents the display host's frame for a compositor that never swaps kwin draws the desktop into the display host's frame and then calls glFlush - and glFlush alone; it never calls eglSwapBuffers. The stream reached the server and the server drew, but the display host was never told a frame was done, so nothing reached the glass. When the surfaceless-bind hook has taken the host frame, glFlush now swaps that surface after flushing the stream: that swap is the host's "frame is done". A swap of a surface the EGL state does not know used to return EGL_BAD_SURFACE without a word - the whole reason the host frame's presents were invisible - so that refusal is now reported once. --- MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp | 1 + MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp b/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp index c08f0f7b..679ae341 100644 --- a/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp +++ b/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp @@ -254,6 +254,7 @@ namespace MobileGL::MG_Impl::EGLImpl { return EGL_FALSE; } if (!state->ValidateSurfaceOnDisplay(dpy, draw)) { + MGLOG_E_ONCE("eglSwapBuffers: surface %p is not a surface of display %p (EGL_BAD_SURFACE)", draw, dpy); state->SetError(EGL_BAD_SURFACE); return EGL_FALSE; } diff --git a/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp b/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp index fe66fe77..caf9a262 100644 --- a/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp +++ b/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp @@ -29,6 +29,9 @@ // names is an object eglCreateImageKHR built (its geometry, its fourcc and the descriptors it owns), // and that object lives in the EGL state. Same include the other GLImpl files that touch it use. #include +// The compositor's frame boundary: glFlush presents the display host's frame when the surfaceless +// bind hook took one (see EGLImpl.cpp). The EGL side declares the pair and the swap entry. +#include #define DECLARE_GL_FUNCTION_STUB_HEAD(type, name, ...) MOBILEGL_GL_API type gl##name(__VA_ARGS__) { @@ -132,6 +135,17 @@ MOBILEGL_GL_API void glFlush() { #if MOBILEGL_BUILD_DISAGGREGATED MobileGL::MG_Pipe::MGPipeClientFlush(); #endif + // A COMPOSITOR'S FRAME BOUNDARY. kwin binds the display host's frame surface and draws the + // desktop into it, then calls glFlush - and glFlush alone. It never calls eglSwapBuffers, so + // without this the stream reached the server and the server drew, but the display host was + // never told a frame was done: the host's own present loop (AHardwareBuffer_acquire -> + // draw -> notify) is what puts pixels on the glass, and its notify is exactly this swap. + // Guarded by whether the surfaceless-bind hook actually took the host frame. + if (MobileGL::MG_Impl::EGLImpl::g_hostFramePresentSurface != nullptr && + MobileGL::MG_Impl::EGLImpl::g_hostFramePresentDisplay != nullptr) { + MobileGL::MG_Impl::EGLImpl::SwapBuffers(MobileGL::MG_Impl::EGLImpl::g_hostFramePresentDisplay, + MobileGL::MG_Impl::EGLImpl::g_hostFramePresentSurface); + } } DECLARE_GL_FUNCTION_HEAD(void, FramebufferRenderbuffer, GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) DECLARE_GL_FUNCTION_END_NO_RETURN(void, FramebufferRenderbuffer, target, attachment, renderbuffertarget, renderbuffer) DECLARE_GL_FUNCTION_HEAD(void, FramebufferTexture2D, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) DECLARE_GL_FUNCTION_END_NO_RETURN(void, FramebufferTexture2D, target, attachment, textarget, texture, level)