[Fix] (Remote): the present record presents a host-framed surface

A split client's swap reaches the server as a present record, and ServerVerbSink::OnPresent only
ran the backend's Present - an eglSwapBuffers of the pbuffer the context was made with.
BackendObject::SwapEGLBuffers is the only route to PresentHostFrame and a split client never takes
it, so over TCP the display host was never told a frame was drawn: the app offered thousands of
frames and presented none.

BackendObject::PresentHostFrameIfCurrent presents the host frame when the current surface is
host-framed and is a no-op otherwise; OnPresent calls it after Present and before the credit goes
back, so a client released by the credit is released by a frame that is on its way to the glass.
This commit is contained in:
BZLZHH
2026-10-01 09:47:18 -04:00
parent 16488d5801
commit c88c6ff196
3 changed files with 20 additions and 0 deletions
+6
View File
@@ -479,6 +479,12 @@ namespace MobileGL::MG_Backend {
return false;
}
Bool BackendObject::PresentHostFrameIfCurrent() {
const std::lock_guard<std::recursive_mutex> lock(m_eglStateMutex);
if (!m_eglSurfaceInitialized || m_eglSurfaceKind != SurfaceKind::HostFrame) return true;
return PresentHostFrame();
}
Bool BackendObject::IsEGLSurfaceCurrent(EGLSurface surface) const {
if (surface == EGL_NO_SURFACE) {
return false;
+5
View File
@@ -602,6 +602,11 @@ namespace MobileGL {
virtual Bool CreateEGLHostFrameSurface(EGLSurface surface, Uint32 width, Uint32 height);
virtual Bool InitHostFrameSurface(EGLint width, EGLint height);
virtual Bool PresentHostFrame();
// The present RECORD's half of a host-framed swap. A split client's swap reaches the
// server as a present record (ServerVerbSink::OnPresent), never as SwapEGLBuffers, so
// without this the host was never told a frame was drawn. True when the current
// surface is not host-framed: there is nothing to present then.
Bool PresentHostFrameIfCurrent();
virtual Bool MakeEGLCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx);
virtual Bool SwapEGLBuffers(EGLDisplay dpy, EGLSurface draw);
// Forwards the app-requested eglSwapInterval to the backend's native
@@ -404,6 +404,15 @@ namespace MobileGL::MG_Remote::Server {
// was wanted, recorded here so a future swap-without-present is looked for rather than
// assumed impossible. Presents() is exposed for a lane that wants to make the comparison.
table->Present();
// A HOST-FRAMED SURFACE IS PRESENTED BY TELLING THE HOST, not by the swap above (which
// swaps the pbuffer the context was made with). BackendObject::SwapEGLBuffers is the
// only other route to PresentHostFrame and a split client never reaches it, so this is
// where the display host learns the frame is drawn - before the credit goes back, so a
// client released by it is released by a frame that is on its way to the glass.
if (m_backend != nullptr) {
if (!m_backend->PresentHostFrameIfCurrent())
MGLOG_E_ONCE("OnPresent: the host frame was not presented (PresentHostFrame failed; see above)");
}
++m_presents;
// FrameSerial 0 means "the server stamps its own" (c1-v1 8.3): P5 has no client-side
// present credit, so the client sends 0 and the frame count on this side IS the serial.