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.
Two modules, and nothing else changes yet: they are the half of the host-framed surface that is
about the frame itself, kept apart from the surface lifecycle so they can be reviewed and built
on their own.
HostFrameTarget (DirectGLES): an AHardwareBuffer becomes a renderbuffer through an EGLImage and a
framebuffer that is the drawing target while its frame is current. The extension strings decide
first and the four entry points are taken from eglGetProcAddress only after they have, which is
the shape the T0 AHB import in Managers.cpp already uses; a driver that lacks one of them is
answered with its name, never an abort. Every failure path drops what it made, so no GL name and
no EGLImage outlives a refusal.
HostFrameBridge (MG_Backend): the display host's own protocol, restated field for field - a
handle ahead of a fixed-size description on one AF_UNIX connection, then the answer, then the
host's readback of the pixels it can see. The host dials; this side listens on the well-known
name. A handle that arrives without its description is released before the failure is reported,
because a reference held past the connection starves the pool the host draws its frames from.
Why the host allocates the frames at all is measured, not chosen: AHardwareBuffer_createFromHandle
accepts an AHardwareBuffer this process allocated and refuses one the display queue handed over
(gralloc BAD_VALUE) - same process, same descriptor, same handle shape. That result is what these
two modules are built around, and it is written down where the next reader will look for it.
Both compile against this tree's own flags. Wiring them into the surface lifecycle, the swap path
and the client's opt-in is the next step, not this one.
Measured on the Redmi today: the Droidspaces container shares the phone's network namespace, so a
client in the container reaches the server's loopback listener with no adb forward and no LAN
address; the server owns the window (MOBILEGL_IPC_SURFACE=server), the Adreno does the drawing,
and a screenshot of the phone shows the colour that headless client asked for.
The two traps are written down because both cost a run: MOBILEGL_IPC_REQUIRE_SAME_BUILD=1 on the
server turns a Connect handshake's build-stamp warning into Refuse{BuildFingerprint} against a
release APK whose stamp is 0, and the client then dies on a caps gate that names something else.
The leg now reports OK on the device: offered=3 imported=3 drawn=3 hostReadback=3 agreed=3 --
three self-allocated AHardwareBuffers from the display host imported as EGLImages by this side,
drawn into, and read back by the host at both the first pixel and the far corner.
Getting there took one measurement and one correction. The handle message is 256 bytes on this
path and recvHandleFromUnixSocket drains it exactly (pending 256 -> 100, then 0 -> 100, then
0 -> 0), so the description that follows it was never the problem; the comparison was. This leg
was checking the display host's messages against kT4Magic -- the dma-buf protocol's magic, which
belongs to the container peer -- while the host stamps AHB_BRIDGE_MAGIC on all three of its
messages. A perfect round trip was reported as 'the offer's sideband did not follow its handle',
which named the wrong cause and cost the time. kT4HostMagic is that value, and the leg now also
prints the handle's footprint and the bytes it actually read, so the next failure of this shape
cannot be mistaken for the other one.