library: five protocols speak the envelope
The folded header stops being a rule in a document and becomes the layout on the wire. Verbs number from sixteen, leaving describe, enumerate, subscribe and unsubscribe reserved and answered the same way by every provider — none of them writes a line to do it. What each protocol used to carry in a field of its own now travels in the header: a vfs node and a display layer are the packet's target, and a reply opens with a status the envelope stamps rather than one each protocol spelled for itself. Display gains the most. One forty-byte request had served eleven verbs, so attach_scanout smuggled stride through x, refresh through y and format through colour, and every coordinate crossed as a bitcast. Per-operation structs end all three: the fields have their own names and their own signs, and the tile payload grows to 224 bytes because the prefix shrank. Scanout loses a message maximum of 64 it had no business declaring — it answers calls, and the floor for a call is 256 — and virtio-gpu stops hard-coding that number at its harness. Two changes are semantic rather than notational. A directory now ends at an entry with no name, because the fixed part of a reply always travels and a zero-length reply no longer exists to mean anything. And input joins the service harness, the last loop in the tree that answered no ping and heard no terminate; its subscriber table, its pruning and its fan-out are the same code, and a shutdown now asks it to stop instead of killing it. A new conformance case reads the registry's own listing and asks every protocol it finds for its name, its version and its verb count, then offers a verb nobody defines and requires -ENOSYS — the envelope's promise, checked against providers rather than against itself. What it cannot reach in that boot it names on the serial line instead of passing quietly. Suite 110/110.
This commit is contained in:
@@ -1,49 +1,55 @@
|
||||
//! The scanout wire protocol — what the compositor says to a native scanout driver (e.g.
|
||||
//! virtio-gpu) over its well-known `.scanout` endpoint to put a composited frame on screen.
|
||||
//! The driver owns the panel and the shared scanout surface it handed the compositor (via the
|
||||
//! display service's `attach_scanout`); the compositor composites into that surface, then asks
|
||||
//! the driver to present a damaged rectangle. Tiny by design — one present request. Separate
|
||||
//! from the display protocol because the directions differ: clients call the compositor over
|
||||
//! `.display`; the compositor calls the driver over `.scanout`. See docs/display-v2.md.
|
||||
//! virtio-gpu) over `/protocol/scanout` to put a composited frame on screen. The driver owns
|
||||
//! the panel and the shared scanout surface it handed the compositor (via the display
|
||||
//! service's `attach_scanout`); the compositor composites into that surface, then asks the
|
||||
//! driver to present a damaged rectangle. Tiny by design — one present request. Separate from
|
||||
//! the display protocol because the directions differ: clients call the compositor over
|
||||
//! `/protocol/display`; the compositor calls the driver over `/protocol/scanout`. See
|
||||
//! docs/display-v2.md.
|
||||
//!
|
||||
//! One scanout per driver instance, so `Header.target` is always 0.
|
||||
|
||||
const std = @import("std");
|
||||
const envelope = @import("envelope");
|
||||
|
||||
pub const Operation = enum(u32) {
|
||||
/// present(x, y, width, height): put the given rectangle of the shared scanout surface on
|
||||
/// the panel (on virtio-gpu: transfer-to-host of the region, then a fenced resource flush).
|
||||
present = 0,
|
||||
/// get_modes() -> ModesReply: the display modes this scanout can switch to (V5).
|
||||
get_modes = 1,
|
||||
/// set_mode(width, height): change the scanout resolution — the shared surface is sized to
|
||||
/// the largest mode, so this just re-points the scanout rectangle; the surface is unchanged.
|
||||
set_mode = 2,
|
||||
};
|
||||
|
||||
pub const Request = extern struct {
|
||||
operation: u32,
|
||||
/// `present(rect)`: put the given rectangle of the shared scanout surface on the panel (on
|
||||
/// virtio-gpu: transfer-to-host of the region, then a fenced resource flush).
|
||||
pub const Present = extern struct {
|
||||
x: u32 = 0,
|
||||
y: u32 = 0,
|
||||
width: u32 = 0,
|
||||
height: u32 = 0,
|
||||
};
|
||||
|
||||
pub const Reply = extern struct {
|
||||
status: i32, // 0 on success, negative on failure
|
||||
reserved: u32 = 0,
|
||||
};
|
||||
/// `set_mode(width, height)`: change the scanout resolution — the shared surface is sized to
|
||||
/// the largest mode, so this just re-points the scanout rectangle; the surface is unchanged.
|
||||
pub const SetMode = extern struct { width: u32, height: u32 };
|
||||
|
||||
/// One offered display mode.
|
||||
pub const Mode = extern struct { width: u32, height: u32 };
|
||||
pub const max_modes = 4;
|
||||
|
||||
/// The reply to `get_modes`: a small fixed list of modes.
|
||||
pub const ModesReply = extern struct {
|
||||
status: i32,
|
||||
count: u32,
|
||||
modes: [max_modes]Mode,
|
||||
/// The answer to `get_modes`: a small fixed list of modes. The success/failure verdict is
|
||||
/// the reply's `Status`, so this carries only the modes.
|
||||
pub const Modes = extern struct {
|
||||
count: u32 = 0,
|
||||
_padding: u32 = 0,
|
||||
modes: [max_modes]Mode = @splat(.{ .width = 0, .height = 0 }),
|
||||
};
|
||||
|
||||
pub const message_maximum: usize = 64;
|
||||
pub const request_size: usize = @sizeOf(Request);
|
||||
pub const reply_size: usize = @sizeOf(Reply);
|
||||
pub const modes_reply_size: usize = @sizeOf(ModesReply);
|
||||
pub const Protocol = envelope.Define(.{
|
||||
.name = "scanout",
|
||||
.version = 1,
|
||||
.operations = &.{
|
||||
.{ .name = "present", .request = Present },
|
||||
.{ .name = "get_modes", .reply = Modes },
|
||||
.{ .name = "set_mode", .request = SetMode },
|
||||
},
|
||||
});
|
||||
|
||||
pub const Operation = Protocol.Operation;
|
||||
|
||||
/// The call floor, like every synchronous protocol. This module used to declare
|
||||
/// 64 — the *push* floor — which was simply wrong: nothing here is pushed, and a
|
||||
/// provider sizing its receive buffer to 64 refuses (`-E2BIG`) any caller that
|
||||
/// sends up to the floor it is entitled to.
|
||||
pub const message_maximum: usize = Protocol.message_maximum;
|
||||
|
||||
Reference in New Issue
Block a user