mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 18:13:23 +09:00
The last thing between the host-framed surface and a run: the sub-byte on the wire that names it. ServerOwned had one (7) and the host frame had none, so the encoder refused the op with UnknownWindowKind - a correct refusal, and the reason the first run of MOBILEGL_IPC_SURFACE=host failed in the codec rather than anywhere near the pixels. - protocol.fbs: WindowKind::HostFrame = 8, appended, legal on CreateWindowSurface only and always with nativeToken 0 - the same rules ServerOwned carries, for the same reason: no pointer of any process's window system crosses (Rule G/H). - SurfaceRefusal: HostFrameOnSetWindowHandle = 5, its own code rather than reusing the server-owned one: a refusal that names the wrong owner sends the next reader to the wrong place. - SurfaceOpCodec: the encode and decode arms, the two wire errors that go with them (HostFrameTokenNotZero latches as corruption, HostFrameOnSetWindowHandle is a named refusal), and the range bound that had ServerOwned as its upper limit now ends at HostFrame. - protocol_generated.h regenerated with the pinned submodule's flatc (scripts/gen_protocol.py), diff being exactly the two appended enumerators. Both ends must be rebuilt together, as any protocol revision here: the container's client and the device's server went out with this on either side.
111 lines
7.0 KiB
C++
111 lines
7.0 KiB
C++
// MobileGL - MobileGL/MG_Remote/Protocol/SurfaceOpCodec.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
|
|
|
|
// P5f, package fc: the codec between the inproc SurfaceControlFrame and the wire's
|
|
// ::MobileGL::Wire::SurfaceOp / ::MobileGL::Wire::SurfaceReply (protocol.fbs).
|
|
//
|
|
// The frame is the inproc payload (Server/SurfaceControlFrame.h); this is the ONLY place the
|
|
// frame meets the schema. Nothing in the inproc hot path encodes - the frame crosses the one-slot
|
|
// channel by value - but the codec is what makes "the same struct encodes under spawn" a tested
|
|
// claim rather than a plan, and it owns the two schema seams that are NOT integer translations:
|
|
//
|
|
// * SurfaceControlOp <-> ::MobileGL::Wire::SurfaceOpKind: numerically aligned for the ten framed ops and
|
|
// PINNED by static_asserts in the .cpp, but mapped through an explicit table. The four
|
|
// inproc-only kinds have no wire kind and refuse to encode by name.
|
|
// * MG_Backend::WindowBackend <-> ::MobileGL::Wire::WindowKind: the two enums do not agree (WindowKind's
|
|
// Surfaceless/Pbuffer are surface shapes, not window backends; WindowBackend's MetalLayer had
|
|
// no wire enumerator until fc added it), so the mapping is a table with named failures, never
|
|
// a cast (f0-egl census, schema gap 3).
|
|
//
|
|
// ERROR SEMANTICS. Decode is pure: it answers a SurfaceWireError. The FATALS live at the server
|
|
// entry point, ServerApplyWireSurfaceOp, because "a wire op the server cannot honour" is a
|
|
// contract violation, not a data problem: AndroidNativeWindowArrived dies
|
|
// Fatal{UnmigratedSurface, "AndroidNativeWindow@P12"} (the ANativeWindow* means nothing in the
|
|
// server's process; real window arrival is P12) and everything else dies
|
|
// Fatal{ProtocolCorruption, "SurfaceOp"}. P6's control pump is the intended caller; P5f's unit
|
|
// tests drive it directly. MetalLayerArrived likewise dies
|
|
// Fatal{UnmigratedSurface, "MetalLayer@P12"}: CAMetalLayer* is also process-local.
|
|
//
|
|
// P12 (on-screen server window). WindowKind::ServerOwned is the one window kind whose window is
|
|
// NOT the client's: the server substitutes its own (ServerLoop.cpp's ServerOwned arm). The codec
|
|
// maps it onto the frame-local Server::kServerOwnedWindowBackend and writes/reads nativeToken 0,
|
|
// always - so the client's window value never reaches the wire. A ServerOwned op with a non-zero
|
|
// token is a peer's corrupt bytes (Fatal{ProtocolCorruption, "SurfaceOp.nativeToken"}, latched);
|
|
// SetWindowHandle naming it is a named refusal (SurfaceRefusal::ServerOwnedOnSetWindowHandle),
|
|
// answered and not latched.
|
|
|
|
#pragma once
|
|
#include <Includes.h>
|
|
|
|
// The generated schema header (not forward declarations): flatbuffers 25 makes
|
|
// FlatBufferBuilder an alias template instantiation, which cannot be opaque-declared, and the
|
|
// Wire enums' underlying types are the schema's own - so this header is the dependency, kept
|
|
// honest by being the ONLY schema include the codec needs.
|
|
#include <MG_Remote/Protocol/generated/protocol_generated.h>
|
|
#include <MG_Remote/Protocol/mg_protocol_base.h>
|
|
#include <MG_Remote/Server/SurfaceControlFrame.h>
|
|
|
|
namespace MobileGL::MG_Backend {
|
|
enum class WindowBackend; // BackendObject.h; opaque-declared so this header stays light
|
|
} // namespace MobileGL::MG_Backend
|
|
|
|
namespace MobileGL::MG_Remote {
|
|
|
|
enum class SurfaceWireError {
|
|
None = 0,
|
|
UnknownOpKind, // no SurfaceControlOp for the wire kind (or None on the wire)
|
|
InprocOnlyOpOnTheWire, // a kind that may ride the inproc channel but never the wire
|
|
WindowKindNamesNoBackend,// Surfaceless/Pbuffer where a window backend is required
|
|
UnknownWindowKind, // a WindowKind the mapping table does not know
|
|
MetalLayerArrived, // -> Fatal{UnmigratedSurface, "MetalLayer@P12"}
|
|
AndroidNativeWindowArrived, // -> Fatal{UnmigratedSurface, "AndroidNativeWindow@P12"}
|
|
// P12 (on-screen server window), D2. WindowKind::ServerOwned is legal on
|
|
// CreateWindowSurface only, and only with nativeToken 0:
|
|
ServerOwnedTokenNotZero, // -> Fatal{ProtocolCorruption, "SurfaceOp.nativeToken"}, latched
|
|
ServerOwnedOnSetWindowHandle, // -> a NAMED REFUSAL (reply ok=false), not a latch
|
|
// MOBILEGL_IPC_SURFACE=host, the same two rules for the other on-screen shape:
|
|
HostFrameTokenNotZero, // -> Fatal{ProtocolCorruption, "SurfaceOp.nativeToken"}, latched
|
|
HostFrameOnSetWindowHandle, // -> a NAMED REFUSAL (reply ok=false), not a latch
|
|
};
|
|
|
|
const char* SurfaceWireErrorName(SurfaceWireError error);
|
|
|
|
// frame.kind <-> wire kind. Both answer false (and leave *out untouched) for None, the four
|
|
// inproc-only kinds, and any wire value the schema does not define.
|
|
bool WireKindForSurfaceControlOp(Server::SurfaceControlOp op, ::MobileGL::Wire::SurfaceOpKind* out);
|
|
bool SurfaceControlOpForWireKind(::MobileGL::Wire::SurfaceOpKind kind, Server::SurfaceControlOp* out);
|
|
|
|
// The explicit WindowBackend <-> WindowKind table. WireWindowKindForWindowBackend also maps
|
|
// WindowBackend::Unknown to WindowKind::None; WindowBackendForWireWindowKind maps
|
|
// WindowKind::None back to Unknown and answers FALSE for Surfaceless/Pbuffer (surface shapes,
|
|
// not window backends).
|
|
bool WireWindowKindForWindowBackend(MG_Backend::WindowBackend backend, ::MobileGL::Wire::WindowKind* out);
|
|
bool WindowBackendForWireWindowKind(::MobileGL::Wire::WindowKind kind, MG_Backend::WindowBackend* out);
|
|
|
|
// Encode one frame as a complete CtrlEnvelope{SurfaceOp} buffer (the builder is left
|
|
// Finished). Fails BEFORE touching the builder for a kind that may not cross.
|
|
SurfaceWireError EncodeSurfaceOpFrame(const Server::SurfaceControlFrame& frame,
|
|
flatbuffers::FlatBufferBuilder* builder);
|
|
|
|
// Validate + decode a wire SurfaceOp into a frame. Pure - it never aborts; the Fatal
|
|
// decisions belong to ServerApplyWireSurfaceOp. The reply half of the frame is reset.
|
|
SurfaceWireError DecodeWireSurfaceOp(const ::MobileGL::Wire::SurfaceOp& op, Server::SurfaceControlFrame* frame);
|
|
|
|
// The reply direction, same shape (CtrlEnvelope{SurfaceReply}).
|
|
void EncodeSurfaceReplyFrame(const Server::SurfaceControlFrame& frame,
|
|
flatbuffers::FlatBufferBuilder* builder);
|
|
void DecodeWireSurfaceReply(const ::MobileGL::Wire::SurfaceReply& reply, Server::SurfaceControlFrame* frame);
|
|
|
|
// The server-side wire entry: decode, then post the frame through ServerLoop's channel and
|
|
// hand the reply half back. A decode error is a NAMED Fatal (see the header block), never a
|
|
// dropped message. No transport calls this yet - P6's control pump will.
|
|
MobileGLResult ServerApplyWireSurfaceOp(const ::MobileGL::Wire::SurfaceOp& op,
|
|
Server::SurfaceControlFrame* replyOut);
|
|
|
|
} // namespace MobileGL::MG_Remote
|