pub struct BufferRing { /* private fields */ }Expand description
A producer-agnostic pool of scanout slots with buffer age and damage accumulation.
The ring owns slot lifecycle and staleness; the producer owns the actual buffers and indexes them by slot. That split is what lets the same ring sit under a dumb buffer, a GBM surface or a Vulkan swapchain without knowing which.
§What it is for
Redrawing a whole frame when three pixels changed wastes bandwidth, and on
a memory-bound part that is the difference between hitting vblank and
missing it. Partial repaint avoids it, but only if the producer knows how
stale the buffer it has just been handed is – a buffer two frames old needs
everything that changed in those two frames repainted, not just this
frame’s damage. That is the EGL_EXT_buffer_age contract, and this is the
bookkeeping behind it.
Getting it wrong under-paints: the frame is composed of pixels from
different moments, and the error persists until something forces a full
repaint. So every path that cannot prove what a slot needs returns
Repaint::Full.
§Threading
Not synchronized, by design. Drive acquire, present and release from
the one thread the producer renders on – the present loop. &mut self on
the mutating methods makes sharing it across threads a compile error rather
than a race.
Implementations§
Source§impl BufferRing
impl BufferRing
Sourcepub const DEFAULT_MAX_SLOTS: usize = 3
pub const DEFAULT_MAX_SLOTS: usize = 3
How deep a ring grows unless the caller says otherwise.
Sourcepub const DEFAULT_MAX_DAMAGE_RECTS: usize = 16
pub const DEFAULT_MAX_DAMAGE_RECTS: usize = 16
How many rectangles a union may hold unless the caller says otherwise.
See BufferRing::new for what happens when a union exceeds this.
Sourcepub fn new(max_slots: usize, max_damage_rects: usize) -> Self
pub fn new(max_slots: usize, max_damage_rects: usize) -> Self
A ring that grows to max_slots, with unions capped at
max_damage_rects.
max_slots is clamped to at least one: a ring with no slots can never
hand one out, and a caller asking for zero has made an arithmetic
mistake rather than a request.
The rect cap is a bandwidth-versus-bookkeeping knob. A union that
exceeds it collapses to Repaint::Full, on the reasoning that
scattered small rectangles cost more in per-rect overhead than the
bandwidth they save.
Sourcepub fn age(&self, slot: usize) -> u32
pub fn age(&self, slot: usize) -> u32
How many presents have happened since this slot was last presented.
Zero means either that it has never been presented, so its contents are
undefined, or that it is the slot presented last, which is current.
acquire is what tells them apart where it matters to
a producer: it never hands out the slot presented last, and answers
Repaint::Full for one never presented.
Sourcepub fn acquire(&mut self) -> Option<Lease>
pub fn acquire(&mut self) -> Option<Lease>
Take a slot for the next frame, or None if every one is busy.
Prefers the freshest free slot, because freshest means least to repaint. Grows the ring if nothing is free and it is below capacity. Never returns the scanning slot.
None is not an error: it means the producer is ahead of the display
and should retry after the next vblank.
Sourcepub fn present(&mut self, slot: usize, frame_damage: &[Rect])
pub fn present(&mut self, slot: usize, frame_damage: &[Rect])
Commit a slot as the new scanout buffer.
frame_damage is what changed in this frame against the previous one –
not against whatever was in this slot, which is what the ring works out
for itself.
The outgoing scanning slot becomes releasable rather than free: its flip
has not landed, so the display engine may still be reading it. Freeing
it here is the mistake drmkit-scene’s invariant 1 (release only after
the replacing flip lands) forbids, in a different place.