#ifndef VGPU_STREAM_H #define VGPU_STREAM_H #include #include /* offsetof */ #include /* alignas */ #include /* static_assert */ /* ===== Geometry — single source of truth (bare ABI, both ends agree) ===== */ #define VGPU_PAGE 4096u #define VGPU_SLOT_COUNT 3u #define VGPU_SLOT_STRIDE (32u * 1024u * 1024u) #define VGPU_RING_OFFSET (2u * 1024u * 1024u) #define VGPU_PRODUCER_OFFSET 0u #define VGPU_CONTROL_OFFSET VGPU_PAGE #define VGPU_REGION_BYTES (VGPU_RING_OFFSET + (uint64_t)VGPU_SLOT_COUNT * VGPU_SLOT_STRIDE) #define VGPU_MAX_WIDTH 3840u #define VGPU_MAX_HEIGHT 2160u #define VGPU_HEARTBEAT_PERIOD_MS 250u /* producer ticks heartbeat >= 4 Hz always */ #define VGPU_LATEST_NONE 0xFFFFFFFFu static_assert((uint64_t)VGPU_MAX_WIDTH * VGPU_MAX_HEIGHT * 4u <= VGPU_SLOT_STRIDE, "max-mode tight BGRA must fit one slot"); /* enum values travel as uint32 wire-values (not as enum fields → no width instability) */ enum { VGPU_FMT_BGRA8888 = 0 }; enum { VGPU_ST_INIT=0, VGPU_ST_CAPTURING=1, VGPU_ST_PAUSED=2, VGPU_ST_STOPPED=3, VGPU_ST_ERROR=4 }; enum { VGPU_BK_NONE=0, VGPU_BK_NVFBC=1, VGPU_BK_DDA=2, VGPU_BK_GDI=3 }; enum { VGPU_CMD_STOP=0, VGPU_CMD_RUN=1, VGPU_CMD_PAUSE=2 }; /* ===== Per-slot descriptor (under hot.seq[slot]) ===== */ typedef struct { uint32_t width; /* pixels */ uint32_t height; /* pixels */ uint32_t stride; /* bytes/row; INVARIANT: == width*4 (tight) */ uint32_t format; /* VGPU_FMT_* */ uint64_t frame_id; /* == producer.frame_id at publish time */ uint64_t timestamp_ns; /* capture time, monotonic */ } vgpu_desc_t; static_assert(sizeof(vgpu_desc_t) == 32, "desc layout"); static_assert(offsetof(vgpu_desc_t, width) == 0, "desc.width"); static_assert(offsetof(vgpu_desc_t, height) == 4, "desc.height"); static_assert(offsetof(vgpu_desc_t, stride) == 8, "desc.stride"); static_assert(offsetof(vgpu_desc_t, format) == 12, "desc.format"); static_assert(offsetof(vgpu_desc_t, frame_id) == 16, "desc.frame_id"); static_assert(offsetof(vgpu_desc_t, timestamp_ns) == 24, "desc.timestamp_ns"); /* ===== Producer block (host-RO): hot publish line + cold status line ===== */ typedef struct { /* --- hot publish line --- */ alignas(64) uint32_t latest; /* index of last; VGPU_LATEST_NONE until 1st frame */ uint32_t _r0; uint64_t frame_id; /* monotonic frame counter (8-aligned) */ uint32_t seq[VGPU_SLOT_COUNT]; /* per-slot seqlock: even=stable, odd=writing */ uint32_t _r1; vgpu_desc_t desc[VGPU_SLOT_COUNT]; /* self-describing slots */ /* --- cold status line --- */ alignas(64) uint64_t heartbeat; /* monotonic; ticks always (even STOPPED/PAUSED) */ uint32_t run_epoch; /* +1 per start (session break for host) */ uint32_t status; /* VGPU_ST_* */ uint32_t backend; /* VGPU_BK_* */ uint32_t error_code; /* 0=none; else fatal detail */ uint32_t applied_fps; /* publish-rate cap the producer actually applies; actual rate may be lower on static content or backend limits — host measures real fps from desc.timestamp_ns */ uint32_t supported_formats; /* bitmask (1u<