Files
MobileGL/MobileGL/MG_Remote/Client/BackendObject_Remote.h
T
gold 035f1b07ae [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.
2026-10-01 01:11:21 +08:00

105 lines
6.0 KiB
C++

// MobileGL - MobileGL/MG_Remote/Client/BackendObject_Remote.h
// Copyright (c) 2025-2026 MobileGL-Dev
// Licensed under the GNU Lesser General Public License v3.0:
// https://www.gnu.org/licenses/gpl-3.0.txt
// https://www.gnu.org/licenses/lgpl-3.0.txt
// SPDX-License-Identifier: LGPL-3.0-only
// End of Source File Header
// THE CLIENT ROLE'S BackendObject. Owner: package c1.
//
// MG_Backend::Init() installs one of these into pActiveBackendObject when the transport
// resolved (the hook is v1's; the object is c1's, table 3). It is NOT a backend: it owns no
// context, no driver and no GL state. It is the frontend's single answer to four questions -
// "what can the device do", "what backend is it", "what table do I call", and the nine EGL
// lifecycle calls - and each of the four is answered somewhere that is not here.
//
// THE THREE TRAPS THE SCOUT FOUND, AND WHERE EACH IS PAID:
//
// 1. GetRendererInfo() RETURNS A REFERENCE (BackendObject.h:590), so the object cannot
// synthesise one per call. CapsMirror owns the storage; this class forwards. And
// LogBackendInfo() reads it at MG_Backend/Init.cpp:21, DURING MG_Backend::Init(), before
// any surface exists - so the mirror answers a placeholder and P5 accepts one imprecise
// startup log line. MG_Backend::Init() is NOT restructured (scout-caps-reply §1.2 (a)).
//
// 2. GetFormatCapabilities() IS NOT VIRTUAL (BackendObject.h:594). It returns the base class's
// own m_formatCapabilities member, so there is no accessor to override: the cache has to be
// PUSHED into that member, and the only moment this object can know to is when a snapshot
// lands. CapsMirror's adoption hook is that moment.
//
// 3. GetBackendType() MUST ANSWER THE SERVER'S BACKEND. There is no "Remote" enumerator and
// there must not be one: GL_Framebuffer.cpp:47, GL_Texture.cpp:6536 and CompileEnv.cpp:122
// switch on this value, and a value they do not know takes a WRONG ARM rather than failing.
//
// THE EGL VIRTUALS ARE BOTH FORWARDED AND KEPT. The base class runs a real state machine -
// surface registration, per-thread current-context bookkeeping, the lazy InitCapabilities latch,
// and SwapEGLBuffers' route into GetBackendFunctions().Present() - and the client needs all of
// it, because Present is a class-B emitter reached through exactly that route (the verb census's
// trap 3: Present has zero MG_Impl call sites). So each override does BOTH: it runs the real
// EGL work on the apply thread, through ServerLoop's control-frame channel (P5f fc; v1's
// function-pointer mailbox before it), and then lets the
// base class keep the client-side books.
//
// SetEGLSwapInterval IS NOT OVERRIDDEN (P10 B). It used to be, because the base implementation
// calls GetBackendFunctions().SetSwapInterval (BackendObject.cpp:402) and that slot was the last
// class-C Fatal in the emit table. The slot is now the forwarder the override called
// (Server::ServerSetEGLSwapInterval, EmitTables.cpp), so the base's route IS the forward and a
// second spelling of it here would only be a second place for the two to drift apart.
#pragma once
#include <Includes.h>
#include <MG_Backend/BackendObject.h>
namespace MobileGL::MG_Remote::Client {
class BackendObject_Remote final : public MG_Backend::BackendObject {
public:
BackendObject_Remote();
~BackendObject_Remote() override;
// ---- the eight pure virtuals ------------------------------------------------------
void Initialize() override;
Bool InitCapabilities() override;
Bool InitWindowSurface() override;
const RendererInfo& GetRendererInfo() const override;
String GetBackendAPIVersionString() const override;
const MG_Backend::GlobalBackendFunctionsTable& GetBackendFunctions() const override;
const MG_Backend::DynamicBackendParameters& GetDynamicParameters() const override;
BackendType GetBackendType() const override;
// ---- the EGL lifecycle virtuals (SetEGLSwapInterval is the base's: see above) -----
Bool InitializeEGLDisplay(EGLDisplay dpy, EGLint* major, EGLint* minor) override;
Bool CreateEGLWindowSurface(EGLSurface surface, const MG_Backend::WindowHandle& handle) override;
Bool ResizeEGLWindowSurface(EGLSurface surface, Uint32 width, Uint32 height) override;
Bool CreateEGLPbufferSurface(EGLSurface surface, EGLint width, EGLint height) override;
Bool MakeEGLCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) override;
Bool SwapEGLBuffers(EGLDisplay dpy, EGLSurface draw) override;
void ReleaseEGLSurface(EGLSurface surface) override;
void ReleaseEGLResources() override;
// Copies the caps mirror's FormatCapabilityCache into the base class's
// m_formatCapabilities. Public because CapsMirror's adoption hook is a free function
// and this is what it calls; it is the whole of trap 2's answer.
void RefreshFormatCapabilities();
protected:
Bool InitPbufferSurface(EGLint width, EGLint height) override;
private:
// P12 (on-screen server window), D1: MOBILEGL_IPC_SURFACE=server's arm of
// 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
// is exactly the shape trap 2 exists to prevent.
Uint64 m_formatsGeneration = 0;
};
} // namespace MobileGL::MG_Remote::Client