[Feature] (disaggregated): MOBILEGL_IPC_SURFACE=host - the display host owns the screen and the frames

The third on-screen shape, and the one this whole line was built toward: Anland keeps Wayland,
input and the display, and the render server draws into the frames the display host allocates.

    client (container)      asks for a window surface with no window of its own
    server (Android)        takes a frame from the host over the host frame bridge and binds it as
                            the draw target of that surface
    host (the app that
    owns the screen)        offers each frame, presents it when the server says it is drawn

The pieces, in the order a frame travels:

 - Config: MOBILEGL_IPC_SURFACE=host beside =server, and HostFrameWindowSurfaces() as the one
   predicate every client-side consumer asks (EGLImpl accepts a NULL window for either on-screen
   shape, and BackendObject_Remote routes the creation to the server instead of sending a handle).
 - Wire: the frame-local tag kHostFrameWindowBackend, the mirror of kServerOwnedWindowBackend - it
   names no WindowBackend of the pull build's enum, and the server consumes it before the unpack
   that would refuse it.
 - Server: ApplyHostFrameWindowSurface replaces nothing, unlike the server-owned arm: there is no
   window of either side's to substitute, because the backend takes the frames from the bridge.
 - Backend: SurfaceKind::HostFrame, InitHostFrameSurface (the EGL context comes from the pbuffer
   path; what changes is what it draws INTO) and PresentHostFrame (glFinish, answer the host, take
   the next frame) - and SwapEGLBuffers routes to it, because a host-framed surface has no
   swapchain to swap: the host is what puts the frame on the glass.

HostFrameTarget and HostFrameBridge, committed just before this, are what the backend uses.

Compiles and links in this tree (libMobileGL.so and libMobileGLServer.so, exit 0).  What is NOT
here yet, and is the next thing: the run that puts Plasma on the phone's screen through it.
This commit is contained in:
gold
2026-10-01 01:11:21 +08:00
parent c7ab618ab0
commit 035f1b07ae
14 changed files with 298 additions and 30 deletions
+10
View File
@@ -499,6 +499,7 @@ namespace MobileGL::MG_Config {
enum class IpcSurface : Uint8 {
Offscreen = 0, // a window surface is the client's own window, as before (the default)
Server = 1, // a window surface is the SERVER's window: WindowKind::ServerOwned
Host = 2, // a window surface draws into frames the DISPLAY HOST owns; the host presents
};
// IpcTable::AdoptTier when MOBILEGL_IPC_ADOPT_TIER is unset (or unparseable): T0 by default,
// with QUIET fallbacks. Not a tier a user can type - the knob admits 0..2.
@@ -729,6 +730,15 @@ namespace MobileGL::MG_Config {
inline Bool ServerOwnedWindowSurfaces() {
return Transport == TransportMode::Spawn && Ipc.Surface == IpcSurface::Server;
}
// P12: the other on-screen shape, and the mirror image of the one above. The display host
// (the Android side that owns the screen) allocates the frames, offers each one over the
// host frame bridge, and puts it on the glass when this side says it is drawn - so a
// client that asks for this has no window of its own either, and the server takes the
// frames rather than making a window.
inline Bool HostFrameWindowSurfaces() {
return Transport == TransportMode::Spawn && Ipc.Surface == IpcSurface::Host;
}
#else
// The whole point: in a build without MG_Remote this folds at compile time, so
// `if (MG_Config::Transport != MG_Config::TransportMode::Monolith)` in Init.cpp is a
+11 -7
View File
@@ -494,8 +494,8 @@ namespace MobileGL::MG_ConfigLoader {
}
}
// P12 (on-screen server window): MOBILEGL_IPC_SURFACE = offscreen | server (Config.h has
// the semantics). An unknown value is named and read as the default, like every knob here.
// P12 (on-screen): MOBILEGL_IPC_SURFACE = offscreen | server | host (Config.h has the
// semantics). An unknown value is named and read as the default, like every knob here.
{
String surface;
QueryEnvVariable("MOBILEGL_IPC_SURFACE", surface, "offscreen");
@@ -505,9 +505,11 @@ namespace MobileGL::MG_ConfigLoader {
ipc.Surface = MG_Config::IpcSurface::Offscreen;
} else if (surface == "server") {
ipc.Surface = MG_Config::IpcSurface::Server;
} else if (surface == "host") {
ipc.Surface = MG_Config::IpcSurface::Host;
} else {
MGLOG_W("Config: Ignoring invalid env variable MOBILEGL_IPC_SURFACE='%s'; expected "
"offscreen|server, using offscreen",
"offscreen|server|host, using offscreen",
surface.c_str());
ipc.Surface = MG_Config::IpcSurface::Offscreen;
}
@@ -515,12 +517,14 @@ namespace MobileGL::MG_ConfigLoader {
// parses this before RunSession sets its own transport, and has no client half anyway.)
const char* role = std::getenv("MOBILEGL_IPC_ROLE");
const Bool serverProcess = role != nullptr && std::strcmp(role, "server") == 0;
if (ipc.Surface == MG_Config::IpcSurface::Server && !serverProcess &&
if (ipc.Surface != MG_Config::IpcSurface::Offscreen && !serverProcess &&
MG_Config::Transport != MG_Config::TransportMode::Spawn) {
// Said, not silently honoured: there is no remote server here to own a window.
MGLOG_W("Config: MOBILEGL_IPC_SURFACE=server is IGNORED - it asks a remote server "
"(MOBILEGL_TRANSPORT=spawn, a fork or tcp:// control) to own the window surface, "
"and this run has none. Window surfaces stay the client's own");
MGLOG_W("Config: MOBILEGL_IPC_SURFACE=%s is IGNORED - it asks for an on-screen "
"surface (the server's own window, or the frames the display host owns) and "
"this run has no remote server to provide one: MOBILEGL_TRANSPORT is not "
"spawn. Window surfaces stay the client's own",
ipc.Surface == MG_Config::IpcSurface::Server ? "server" : "host");
}
}
+48
View File
@@ -209,6 +209,28 @@ namespace MobileGL::MG_Backend {
return true;
}
// The frame itself is the backend's business (InitHostFrameSurface); what happens here is
// the registration every surface kind shares, so the swap path and the state mirrors see
// a host-framed surface exactly like any other.
Bool BackendObject::CreateEGLHostFrameSurface(EGLSurface surface, Uint32 width, Uint32 height) {
const std::lock_guard<std::recursive_mutex> lock(m_eglStateMutex);
if (!m_eglDisplayInitialized) {
MGLOG_E("CreateEGLHostFrameSurface failed: EGL display is not initialized");
return false;
}
if (surface == EGL_NO_SURFACE) {
MGLOG_E("CreateEGLHostFrameSurface failed: invalid EGLSurface");
return false;
}
auto& state = m_eglSurfaces[surface];
state = EGLSurfaceState{
.Kind = SurfaceKind::HostFrame,
.Width = static_cast<EGLint>(std::max<Uint32>(width, 1)),
.Height = static_cast<EGLint>(std::max<Uint32>(height, 1)),
};
return ActivateEGLSurface(surface);
}
Bool BackendObject::CreateEGLPbufferSurface(EGLSurface surface, EGLint width, EGLint height) {
const std::lock_guard<std::recursive_mutex> lock(m_eglStateMutex);
return RegisterEGLPbufferSurface(surface, width, height) && ActivateEGLSurface(surface);
@@ -290,6 +312,11 @@ namespace MobileGL::MG_Backend {
MGLOG_E("ActivateEGLSurface failed: backend InitPbufferSurface failed");
return false;
}
} else if (surfaceState->Kind == SurfaceKind::HostFrame) {
if (!InitHostFrameSurface(surfaceState->Width, surfaceState->Height)) {
MGLOG_E("ActivateEGLSurface failed: backend InitHostFrameSurface failed");
return false;
}
} else {
MGLOG_E("ActivateEGLSurface failed: unsupported surface kind");
return false;
@@ -387,6 +414,12 @@ namespace MobileGL::MG_Backend {
return false;
}
// A host-framed surface has no swapchain: presenting it means telling the host the frame
// is drawn, because the host is what puts it on the glass.
if (m_eglSurfaceKind == SurfaceKind::HostFrame) {
return PresentHostFrame();
}
const auto& backendFunctions = GetBackendFunctions();
if (!backendFunctions.Present) {
MGLOG_E("SwapEGLBuffers failed: backend Present function is null");
@@ -404,6 +437,21 @@ namespace MobileGL::MG_Backend {
}
}
// A backend that has no host-framed surfaces says so by name: the server may ask for one
// (MOBILEGL_IPC_SURFACE=host) against a build that cannot serve it, and that has to be a
// refusal with a reason rather than a surface that draws nowhere.
Bool BackendObject::InitHostFrameSurface(EGLint width, EGLint height) {
(void)width;
(void)height;
MGLOG_E("InitHostFrameSurface: this backend has no host-framed surfaces");
return false;
}
Bool BackendObject::PresentHostFrame() {
MGLOG_E("PresentHostFrame: this backend has no host-framed surfaces");
return false;
}
Bool BackendObject::IsEGLSurfaceCurrent(EGLSurface surface) const {
if (surface == EGL_NO_SURFACE) {
return false;
+12 -1
View File
@@ -595,6 +595,13 @@ namespace MobileGL {
virtual Bool CreateEGLWindowSurface(EGLSurface surface, const WindowHandle& handle);
virtual Bool ResizeEGLWindowSurface(EGLSurface surface, Uint32 width, Uint32 height);
virtual Bool CreateEGLPbufferSurface(EGLSurface surface, EGLint width, EGLint height);
// The host owns the screen: this side never gets a window of the client's to draw
// into, so the surface it draws into is a frame the host allocated and offered over
// the host frame bridge. Each swap tells the host the frame is done and takes the
// next; presentation is the host's, not a swapchain's.
virtual Bool CreateEGLHostFrameSurface(EGLSurface surface, Uint32 width, Uint32 height);
virtual Bool InitHostFrameSurface(EGLint width, EGLint height);
virtual Bool PresentHostFrame();
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
@@ -616,7 +623,11 @@ namespace MobileGL {
enum class SurfaceKind {
None,
Window,
Pbuffer
Pbuffer,
// A surface whose drawing target is a frame the display host owns. There is no
// swapchain behind it: the host puts the frame on the glass when this side says
// it is drawn, which is why presenting one is its own call and not a Present.
HostFrame
};
struct EGLCurrentState {
+12 -4
View File
@@ -63,13 +63,21 @@ namespace MobileGL::MG_Backend {
sockaddr_un address{};
address.sun_family = AF_UNIX;
if (std::strlen(name) >= sizeof(address.sun_path)) {
why = std::format("the socket path is longer than the kernel takes: {}", name);
why = std::format("the socket name is longer than the kernel takes: {}", name);
Close();
return false;
}
std::strncpy(address.sun_path, name, sizeof(address.sun_path) - 1);
// A leftover socket file from a server that died is not a reason to refuse the host.
::unlink(name);
// "@name" is the abstract namespace: sun_path[0] is NUL and the name follows it.
// Nothing is created on disk, so nothing is left behind and no file permission stands
// between two apps that may not write each other's directories.
if (name[0] == '@') {
address.sun_path[0] = 0;
std::strncpy(address.sun_path + 1, name + 1, sizeof(address.sun_path) - 2);
} else {
std::strncpy(address.sun_path, name, sizeof(address.sun_path) - 1);
// A leftover socket file from a server that died is not a reason to refuse the host.
::unlink(name);
}
if (::bind(m_listen, reinterpret_cast<sockaddr*>(&address), sizeof(address)) != 0) {
why = std::format("bind({}): {}", name, std::strerror(errno));
Close();
+5 -1
View File
@@ -29,7 +29,11 @@ namespace MobileGL::MG_Backend {
// makes a mismatched pair fail loudly instead of being read as plausible numbers.
inline constexpr Uint32 kHostFrameMagic = 0x4D474C41u; // the host spells it "ALGM" in memory
inline constexpr Uint32 kHostFrameVersion = 1u;
inline constexpr const char* kHostFrameSocket = "/data/local/tmp/mobilegl_bridge.sock";
// AN ABSTRACT NAME, not a path: the two ends are separate apps, and neither may create a
// socket file in /data/local/tmp (its directory grants others traverse only). An abstract
// name lives in the kernel namespace instead of the filesystem, so there is no file
// permission to get wrong - the same reason this project names its own endpoints @mgl-*.
inline constexpr const char* kHostFrameSocket = "@mobilegl-host-frame";
struct HostFrameOffer {
Uint32 Magic;
+42 -17
View File
@@ -137,8 +137,11 @@ namespace MobileGL::MG_Impl::EGLImpl {
// the window is the SERVER's: a headless client passes none, so NULL is not
// EGL_BAD_NATIVE_WINDOW here. Whatever the client passed never reaches the wire - the
// remote backend sends WindowKind::ServerOwned with token 0 (BackendObject_Remote).
const Bool serverOwnedWindow = MG_Config::ServerOwnedWindowSurfaces();
if (IsNullNativeHandle(window) && !serverOwnedWindow) {
// Either on-screen shape leaves this side without a window of its own: =server asks the
// server for its own window, =host draws into the frames the display host offers.
const Bool windowlessOnScreen =
MG_Config::ServerOwnedWindowSurfaces() || MG_Config::HostFrameWindowSurfaces();
if (IsNullNativeHandle(window) && !windowlessOnScreen) {
#else
if (IsNullNativeHandle(window)) {
#endif
@@ -157,15 +160,15 @@ namespace MobileGL::MG_Impl::EGLImpl {
// alike; the same game in a landscape window was right). The native window it handed us
// knows the size the game believes it has: ask for THAT, and the server fixes its window
// buffer to it and aspect-fits the view (MobileGLDisplayActivity), letterboxed.
if (serverOwnedWindow && requestedWidth == 0 && requestedHeight == 0 && !IsNullNativeHandle(window)) {
if (windowlessOnScreen && requestedWidth == 0 && requestedHeight == 0 && !IsNullNativeHandle(window)) {
auto* native = reinterpret_cast<ANativeWindow*>(ToVoidHandle(window));
const int32_t nativeWidth = ANativeWindow_getWidth(native);
const int32_t nativeHeight = ANativeWindow_getHeight(native);
if (nativeWidth > 0 && nativeHeight > 0) {
requestedWidth = static_cast<Uint32>(nativeWidth);
requestedHeight = static_cast<Uint32>(nativeHeight);
MGLOG_I("eglCreateWindowSurface: no EGL_WIDTH/EGL_HEIGHT; the server-owned window is asked for the "
"native window's %dx%d", nativeWidth, nativeHeight);
MGLOG_I("eglCreateWindowSurface: no EGL_WIDTH/EGL_HEIGHT; the on-screen surface is asked for "
"the native window's %dx%d", nativeWidth, nativeHeight);
}
}
#endif
@@ -177,11 +180,20 @@ namespace MobileGL::MG_Impl::EGLImpl {
};
#if MOBILEGL_BUILD_DISAGGREGATED
EGLSurface surface = serverOwnedWindow
? state->CreateServerOwnedWindowSurface(
dpy, config, windowHandle.Handle, static_cast<EGLint>(windowHandle.Width),
static_cast<EGLint>(windowHandle.Height), /*platformWindow=*/false)
: state->CreateWindowSurface(dpy, config, window, attrib_list);
EGLSurface surface = EGL_NO_SURFACE;
if (MG_Config::ServerOwnedWindowSurfaces()) {
// The server's own window: it makes one and draws into it.
surface = state->CreateServerOwnedWindowSurface(
dpy, config, windowHandle.Handle, static_cast<EGLint>(windowHandle.Width),
static_cast<EGLint>(windowHandle.Height), /*platformWindow=*/false);
} else if (MG_Config::HostFrameWindowSurfaces()) {
// The display host's frames, as above: no window of the client's either way.
surface = state->CreateHostFrameWindowSurface(
dpy, config, windowHandle.Handle, static_cast<EGLint>(windowHandle.Width),
static_cast<EGLint>(windowHandle.Height), /*platformWindow=*/false);
} else {
surface = state->CreateWindowSurface(dpy, config, window, attrib_list);
}
#else
EGLSurface surface = state->CreateWindowSurface(dpy, config, window, attrib_list);
#endif
@@ -724,8 +736,11 @@ namespace MobileGL::MG_Impl::EGLImpl {
#if MOBILEGL_BUILD_DISAGGREGATED
// P12 (on-screen server window), D1: as CreateWindowSurface - with MOBILEGL_IPC_SURFACE=server
// the window is the server's, and a headless client's NULL is accepted.
const Bool serverOwnedWindow = MG_Config::ServerOwnedWindowSurfaces();
if (native_window == nullptr && !serverOwnedWindow) {
// Either on-screen shape leaves this side without a window of its own: =server asks the
// server for its own window, =host draws into the frames the display host offers.
const Bool windowlessOnScreen =
MG_Config::ServerOwnedWindowSurfaces() || MG_Config::HostFrameWindowSurfaces();
if (native_window == nullptr && !windowlessOnScreen) {
#else
if (native_window == nullptr) {
#endif
@@ -749,11 +764,21 @@ namespace MobileGL::MG_Impl::EGLImpl {
};
#if MOBILEGL_BUILD_DISAGGREGATED
EGLSurface surface = serverOwnedWindow
? state->CreateServerOwnedWindowSurface(
dpy, config, native_window, static_cast<EGLint>(windowHandle.Width),
static_cast<EGLint>(windowHandle.Height), /*platformWindow=*/true)
: state->CreatePlatformWindowSurface(dpy, config, native_window, attrib_list);
EGLSurface surface = EGL_NO_SURFACE;
if (MG_Config::ServerOwnedWindowSurfaces()) {
// The server's own window: it makes one and draws into it.
surface = state->CreateServerOwnedWindowSurface(
dpy, config, native_window, static_cast<EGLint>(windowHandle.Width),
static_cast<EGLint>(windowHandle.Height), /*platformWindow=*/true);
} else if (MG_Config::HostFrameWindowSurfaces()) {
// The display host's frames: it allocates them, the server draws into them,
// and the host puts each one on the glass when the server says it is drawn.
surface = state->CreateHostFrameWindowSurface(
dpy, config, native_window, static_cast<EGLint>(windowHandle.Width),
static_cast<EGLint>(windowHandle.Height), /*platformWindow=*/true);
} else {
surface = state->CreatePlatformWindowSurface(dpy, config, native_window, attrib_list);
}
#else
EGLSurface surface = state->CreatePlatformWindowSurface(dpy, config, native_window, attrib_list);
#endif
@@ -216,6 +216,10 @@ namespace MobileGL::MG_Remote::Client {
WaitForApplyBeforeEglForwarder("CreateEGLWindowSurface");
// P12 (D1): unless the window is the SERVER's - then there is no handle of ours to tell.
if (MG_Config::ServerOwnedWindowSurfaces()) return CreateServerOwnedWindowSurface(surface, handle);
// The other on-screen shape: the frames belong to the display host, and the server takes
// them from the bridge. As for the server-owned shape, no window of the client's crosses
// the wire - only the size it asked for.
if (MG_Config::HostFrameWindowSurfaces()) return CreateHostFrameWindowSurface(surface, handle);
Server::ServerSetWindowHandle(handle);
if (!Server::ServerCreateEGLWindowSurface(surface, handle)) return false;
// The server's surface init published the default framebuffer's shape as a
@@ -295,6 +299,10 @@ namespace MobileGL::MG_Remote::Client {
// dereferenced - the client's InitWindowSurface is a no-op - and never crosses the wire
// (the ServerOwned frame carries token 0).
char g_serverOwnedWindowPlaceholder = 0;
// The host-framed shape names no window of the client's either; the base class still wants
// a handle to register the surface with, and this is the same stand-in the server-owned
// shape uses.
char g_hostFrameWindowPlaceholder = 0;
} // namespace
Bool BackendObject_Remote::CreateServerOwnedWindowSurface(EGLSurface surface,
@@ -360,6 +368,46 @@ namespace MobileGL::MG_Remote::Client {
return MG_Backend::BackendObject::CreateEGLWindowSurface(surface, local);
}
Bool BackendObject_Remote::CreateHostFrameWindowSurface(EGLSurface surface,
const MG_Backend::WindowHandle& handle) {
// The size crosses the wire and nothing else of the client's does: the server takes frames
// from the display host at that size and answers with the extent the surface has.
const Server::ServerOwnedWindowReply reply =
Server::ServerCreateHostFrameWindowSurface(surface, handle.Width, handle.Height);
if (!reply.ok) {
// FAILS BY NAME ON THIS SIDE TOO, as the server-owned shape does: the server logged its
// reason, and the refusal code is what lets this line say the same thing.
switch (reply.refusal) {
case Server::SurfaceRefusalCode::SurfaceModeMismatch:
MGLOG_E("MG_Remote client: Refuse HostFrame (SurfaceModeMismatch) - MOBILEGL_IPC_SURFACE=host "
"asked for this %ux%u window surface in a session that already chose a pbuffer: only "
"one rendering path is active per session. eglCreateWindowSurface fails with "
"EGL_BAD_NATIVE_WINDOW",
handle.Width, handle.Height);
break;
case Server::SurfaceRefusalCode::NoServerWindow:
MGLOG_E("MG_Remote client: Refuse HostFrame (NoServerWindow) - the server could not take a frame "
"from the display host for this %ux%u surface; its log names what the host or the "
"bridge said (its own answer was %ux%u)",
handle.Width, handle.Height, reply.width, reply.height);
break;
default:
MGLOG_E("MG_Remote client: Refuse HostFrame (code %u) for this %ux%u window surface",
static_cast<Uint>(reply.refusal), handle.Width, handle.Height);
break;
}
return false;
}
// From here the frames are the server's and the host's business; this side only registers the
// surface, exactly as it does for the server-owned shape.
MG_Backend::WindowHandle local = handle;
if (local.Backend == MG_Backend::WindowBackend::Unknown) local.Backend = MG_Backend::WindowBackend::Android;
if (local.Handle == nullptr) local.Handle = &g_hostFrameWindowPlaceholder;
local.Width = reply.width;
local.Height = reply.height;
return MG_Backend::BackendObject::CreateEGLWindowSurface(surface, local);
}
Bool BackendObject_Remote::MakeEGLCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read,
EGLContext ctx) {
// FORWARD FIRST, THEN RUN THE BASE. The server has to own the context before the base
@@ -91,6 +91,9 @@ namespace MobileGL::MG_Remote::Client {
// CreateEGLWindowSurface - ONE ServerOwned frame, no SetWindowHandle, the server's real
// geometry adopted before it returns, and every refusal named in this process's log.
Bool CreateServerOwnedWindowSurface(EGLSurface surface, const MG_Backend::WindowHandle& handle);
// MOBILEGL_IPC_SURFACE=host: the frames are the display host's, and the server takes
// them from the host frame bridge. Nothing of the client's crosses the wire here either.
Bool CreateHostFrameWindowSurface(EGLSurface surface, const MG_Backend::WindowHandle& handle);
// The generation of the snapshot m_formatCapabilities was filled from. Exposed only
// through the log line on a refresh: a cache that silently stopped tracking the mirror
+69
View File
@@ -287,6 +287,7 @@ namespace MobileGL::MG_Remote::Server {
m_surfaceMode = SessionSurfaceMode::None;
m_holdsWindowLease = false;
m_serverOwnedSurfaces.clear();
m_hostFrameSurfaces.clear();
m_windowLostRequested.store(false, std::memory_order_release);
m_serverWindowsLost.store(0, std::memory_order_release);
m_testLogFloodLines = ApplyLogFloodLinesForTest();
@@ -1338,6 +1339,8 @@ namespace MobileGL::MG_Remote::Server {
// P12 (D3): the SERVER's window, asked for by a headless client. Before the unpack,
// which would refuse the frame-local tag as a WindowBackend out of range.
if (frame.windowBackend == kServerOwnedWindowBackend) return ApplyServerOwnedWindowSurface(backend, frame);
// The mirror image: the frames are the display host's, and it presents them.
if (frame.windowBackend == kHostFrameWindowBackend) return ApplyHostFrameWindowSurface(backend, frame);
MG_Backend::WindowHandle window;
if (!UnpackWindowHandle(frame, &window)) {
frame.ok = false;
@@ -1445,6 +1448,7 @@ namespace MobileGL::MG_Remote::Server {
const EGLSurface surface = HandleFromToken<EGLSurface>(frame.surface);
backend->ReleaseEGLSurface(surface);
ForgetServerOwnedSurface(surface);
ForgetHostFrameSurface(surface);
// N-3: a released surface the held tuple names may have taken the context with it
// (BackendObject::ReleaseEGLSurface -> OnEGLSurfaceReleased -> DestroyEGLContext once
// nothing holds it current). Forgetting when the base class only DEFERRED the destroy
@@ -1658,6 +1662,52 @@ namespace MobileGL::MG_Remote::Server {
return MOBILEGL_OK;
}
MobileGLResult ServerLoop::ApplyHostFrameWindowSurface(MG_Backend::BackendObject* backend,
SurfaceControlFrame& frame) {
frame.ok = false;
frame.refusal = static_cast<Uint8>(SurfaceRefusalCode::None);
const Uint32 wantWidth = frame.width > 0 ? static_cast<Uint32>(frame.width) : 0u;
const Uint32 wantHeight = frame.height > 0 ? static_cast<Uint32>(frame.height) : 0u;
// Both on-screen shapes are the same session mode: one rendering path per session, and
// this is the on-screen one whichever side owns the window.
if (!SessionSurfaceModeAdmits(m_surfaceMode, /*serverOwnedWindow=*/true)) {
frame.refusal = static_cast<Uint8>(SurfaceRefusalCode::SurfaceModeMismatch);
MGLOG_E("MG_Remote server: SurfaceModeMismatch - a HostFrame CreateWindowSurface (seq %llu, "
"%ux%u) in a session whose surface mode is %s. Refused, the session is not latched",
static_cast<unsigned long long>(frame.seq), wantWidth, wantHeight,
SessionSurfaceModeName(m_surfaceMode));
return MOBILEGL_ERR_INVALID_ARGUMENT;
}
const EGLSurface surface = HandleFromToken<EGLSurface>(frame.surface);
// THE SUBSTITUTION, the other way round from ServerOwned: nothing of the client's and
// nothing of the server's goes into the backend call - the backend takes the frames from
// the display host, and every swap hands the next one back to it.
frame.ok = backend->CreateEGLHostFrameSurface(surface, wantWidth, wantHeight);
if (!frame.ok) {
MGLOG_E("MG_Remote server: the backend could not take the display host frames (seq %llu, %ux%u)",
static_cast<unsigned long long>(frame.seq), wantWidth, wantHeight);
return MOBILEGL_OK;
}
ForgetCurrentTuple();
m_surfaceMode = SessionSurfaceMode::OnScreen;
if (!IsHostFrameSurface(surface)) m_hostFrameSurfaces.push_back(surface);
frame.width = static_cast<Int>(wantWidth);
frame.height = static_cast<Int>(wantHeight);
MGLOG_I("MG_Remote server: surface=window %ux%u owner=host (HostFrame seq %llu)", wantWidth,
wantHeight, static_cast<unsigned long long>(frame.seq));
return MOBILEGL_OK;
}
Bool ServerLoop::IsHostFrameSurface(EGLSurface surface) const {
return std::find(m_hostFrameSurfaces.begin(), m_hostFrameSurfaces.end(), surface) !=
m_hostFrameSurfaces.end();
}
void ServerLoop::ForgetHostFrameSurface(EGLSurface surface) {
m_hostFrameSurfaces.erase(std::remove(m_hostFrameSurfaces.begin(), m_hostFrameSurfaces.end(), surface),
m_hostFrameSurfaces.end());
}
Bool ServerLoop::IsServerOwnedSurface(EGLSurface surface) const {
return std::find(m_serverOwnedSurfaces.begin(), m_serverOwnedSurfaces.end(), surface) !=
m_serverOwnedSurfaces.end();
@@ -1770,6 +1820,25 @@ namespace MobileGL::MG_Remote::Server {
return rc == MOBILEGL_OK && frame.ok;
}
ServerOwnedWindowReply ServerCreateHostFrameWindowSurface(EGLSurface surface, Uint32 width, Uint32 height) {
// One frame, and no SetWindowHandle in front of it: the client names no window, and this
// side does not make one - the frames are the display host's, taken from the bridge.
SurfaceControlFrame frame;
frame.kind = SurfaceControlOp::CreateWindowSurface;
frame.surface = TokenFromHandle(surface);
frame.windowBackend = kHostFrameWindowBackend;
frame.nativeToken = 0;
frame.width = static_cast<Int>(width);
frame.height = static_cast<Int>(height);
const MobileGLResult rc = ServerLoopInstance().RunSurfaceControlFrame(frame);
ServerOwnedWindowReply reply;
reply.transport = rc;
reply.ok = rc == MOBILEGL_OK && frame.ok;
reply.refusal = static_cast<SurfaceRefusalCode>(frame.refusal);
reply.width = frame.width > 0 ? static_cast<Uint32>(frame.width) : 0u;
reply.height = frame.height > 0 ? static_cast<Uint32>(frame.height) : 0u;
return reply;
}
ServerOwnedWindowReply ServerCreateServerOwnedWindowSurface(EGLSurface surface, Uint32 width, Uint32 height) {
// P12 (D1/D2): ONE frame, and no SetWindowHandle in front of it - the window is the
// server's, so there is nothing of the client's to name. The frame-local tag is what the
+11
View File
@@ -459,6 +459,9 @@ namespace MobileGL::MG_Remote::Server {
// P12. The ServerOwned arm of the CreateWindowSurface dispatch (D3, D4).
MobileGLResult ApplyServerOwnedWindowSurface(MG_Backend::BackendObject* backend, SurfaceControlFrame& frame);
MobileGLResult ApplyHostFrameWindowSurface(MG_Backend::BackendObject* backend, SurfaceControlFrame& frame);
Bool IsHostFrameSurface(EGLSurface surface) const;
void ForgetHostFrameSurface(EGLSurface surface);
// P12 (D6), apply thread: the lost window's backend surface goes, the lease ends, and the
// session latches ServerWindowLost. Runs from PumpControlRequest when Detach asked.
void ReleaseLostServerWindow();
@@ -471,6 +474,10 @@ namespace MobileGL::MG_Remote::Server {
void ForgetServerOwnedSurface(EGLSurface surface);
// Apply thread only: this session's surfaces created on the server's window; reset by Start().
std::vector<EGLSurface> m_serverOwnedSurfaces;
// The surfaces whose drawing target is a frame the display host owns. Kept apart from
// the server-owned list because the two are different paths with different owners,
// and a resize or a release has to reach the right one.
std::vector<EGLSurface> m_hostFrameSurfaces;
// ServerDisplay's lost hook: called under the display's lock from Detach's thread. Sets the
// request and rings the apply thread's bell; never blocks.
static void ServerWindowLostThunk(void* self);
@@ -659,6 +666,10 @@ namespace MobileGL::MG_Remote::Server {
Uint32 height = 0;
};
ServerOwnedWindowReply ServerCreateServerOwnedWindowSurface(EGLSurface surface, Uint32 width, Uint32 height);
// The host-framed on-screen shape (MOBILEGL_IPC_SURFACE=host): the client names no window
// either, but the frames come from the display host over the host frame bridge, and the
// host presents them. The reply carries the size the surface was made at.
ServerOwnedWindowReply ServerCreateHostFrameWindowSurface(EGLSurface surface, Uint32 width, Uint32 height);
// P12 review fix: ResizeWindowSurface for a surface created on the server's window. The server
// resizes its WINDOW (a geometry request) and replies with the window's real extent.
ServerOwnedWindowReply ServerResizeServerOwnedWindowSurface(EGLSurface surface, Uint32 width, Uint32 height);
@@ -87,6 +87,14 @@ namespace MobileGL::MG_Remote::Server {
// substitutes its own window there; no pointer crosses the wire (Rule G/H).
inline constexpr Int kServerOwnedWindowBackend = 0x10000;
// The OTHER on-screen shape, and its mirror image. With MOBILEGL_IPC_SURFACE=server the
// client has no window and the SERVER's own goes into the backend call; with =host the
// client still has no window, but the frames are the display host's - it allocates them,
// offers each one over the host frame bridge, and puts them on the glass itself. Same
// kind of frame-local tag as the one above, and for the same reason: it names no
// WindowBackend of MG_Backend's enum, which is in the pull build (gate G1).
inline constexpr Int kHostFrameWindowBackend = 0x10001;
// P12. Why the server declined a surface op, carried back in the reply half (`refusal`). The
// values ARE the wire's SurfaceRefusal (protocol.fbs); SurfaceOpCodec pins the agreement with
// static_asserts, the way it pins SurfaceControlOp against SurfaceOpKind. None of them latches
+12
View File
@@ -1091,6 +1091,18 @@ namespace MobileGL {
return surface;
}
// MOBILEGL_IPC_SURFACE=host: the registration is the server-owned one verbatim, because
// what makes a window surface host-framed is not its EGL state but where its pixels go -
// and that is decided one layer down, by the backend the creation calls next.
EGLContext::EGLSurfaceHandle EGLContext::CreateHostFrameWindowSurface(EGLDisplayHandle display,
EGLConfigHandle config,
const void* nativeWindow,
EGLint width, EGLint height,
Bool platformWindow) {
return CreateServerOwnedWindowSurface(display, config, nativeWindow, width, height,
platformWindow);
}
Bool EGLContext::SetSurfaceExtent(EGLSurfaceHandle surface, EGLint width, EGLint height) {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
auto surfaceIt = m_surfaces.find(surface);
+7
View File
@@ -97,6 +97,13 @@ namespace MobileGL {
EGLSurfaceHandle CreateServerOwnedWindowSurface(EGLDisplayHandle display, EGLConfigHandle config,
const void* nativeWindow, EGLint width,
EGLint height, Bool platformWindow);
// MOBILEGL_IPC_SURFACE=host: the same registration under the other on-screen name.
// The EGL state does not care which side owns the frames - that is the backend's
// business - but a trace has to be able to tell the two runs apart, and the two
// paths part company in the backend call that follows.
EGLSurfaceHandle CreateHostFrameWindowSurface(EGLDisplayHandle display, EGLConfigHandle config,
const void* nativeWindow, EGLint width,
EGLint height, Bool platformWindow);
// P12: the server's REAL extent of a surface - a server-owned window's geometry, from
// the CreateWindowSurface reply and from every surface-changed event after it - so
// eglQuerySurface(EGL_WIDTH/EGL_HEIGHT) answers what the server renders at. Unlike