Skip to main content

Allocator

Struct Allocator 

Source
pub struct Allocator { /* private fields */ }
Expand description

Assigns layers to display planes.

One allocator belongs to one CRTC and is kept across frames, because most of what makes it cheap is memory of the last one. A frame goes through at most four stages, and a steady scene stops at the first:

  1. The FB-only fast path. Only framebuffer ids changed, so the kernel has already accepted this arrangement — no test commit at all. This is invariant 4, and Layer::property_hash is what makes it safe to claim.
  2. Warm start. The layer set is unchanged but something else moved, so last frame’s assignment is re-offered and validated with one test.
  3. A full search. Bipartite matching seeds it, scored candidates order it, and backtracking drops the layer with the lowest keep-priority when the kernel refuses.
  4. Composition. Whatever is still unplaced is handed back for the caller to blend, rather than the frame failing.

The search is budgeted: DEFAULT_MAX_TEST_COMMITS test commits per search – a compositing frame runs two, see set_max_test_commits – after which it takes the best arrangement it has proved. Exhausting the budget is reported rather than hidden, because “this device is out of planes” and “raise the budget” want different answers from a caller.

A refusal is never treated as a defect. The kernel is the only authority on what a device can scan out simultaneously, and a TEST_ONLY that comes back EINVAL is that authority answering — which is why the allocator asks rather than predicting.

Implementations§

Source§

impl Allocator

Source

pub const DEFAULT_MAX_TEST_COMMITS: usize = 16

Default per-frame test-commit budget.

Source

pub fn new() -> Self

A fresh allocator with no cached state.

Source

pub fn set_canvas(&mut self, hosts: &[u32], layer: Option<&Layer>)

The composition canvas: which planes can carry it, and its plane’s properties. Empty hosts or no layer means the caller has none.

Where planes have no zpos the allocator picks the canvas plane itself, since only a plane between the composited run’s neighbors stacks the canvas where the run asked. Elsewhere the caller names it with hold_canvas_plane. Either way the allocator arms layer there in every test it issues, so that what the kernel accepts is the whole frame (P-18).

Source

pub const fn canvas_plane(&self) -> Option<u32>

The plane the last pass left for the canvas, tested with it armed.

None when nothing is composited, when no host fits, or where planes take a zpos and the pass held no plane for it; the caller arms the canvas here when it is Some. A cached pass reports the plane the search it reuses left, since that is the plane its test armed.

Source

pub const fn hold_canvas_plane(&mut self, plane: Option<u32>)

Where planes take a zpos: hold plane for the canvas this pass.

The plane must also be reserved (see set_reserved_planes). The search arms the canvas there in every test, and when it leaves layers composited reports the plane as canvas_plane and keeps it with the cached assignment, so a warm start re-tests the canvas with the layers rather than searching again. Ignored where planes stack by id, which pick their own.

Source

pub const fn set_max_test_commits(&mut self, max: usize)

Set the per-search test-commit budget.

A frame that composites runs two searches – the second holds a plane back for the composition canvas and re-validates with it armed – so such a frame can spend up to twice this. That is deliberate: starving the second search would leave it unable to validate, which is the one thing it exists to do.

Source

pub fn invalidate_allocation(&mut self)

Drop the warm-start cache so the next pass does a full search.

The scene calls this when a layer’s content type or update hint changed: those affect scoring but not the layer set, so warm-start — keyed on “same layers, still valid” — would otherwise keep the stale assignment and never move the layer to the plane its new hint prefers.

Source

pub fn forget_output(&mut self)

Forget everything cached about the previous output.

For a scene moving to a different CRTC. Both the assignment and the committed baseline describe that output’s planes, and a plane id means nothing on another pipe – diffing against a baseline from the old one would suppress properties the new one has never been told.

Stronger than invalidate_allocation, which only forces a fresh search and keeps the baseline.

Source

pub fn record_committed(&mut self, plane_id: u32, layer: LayerRef<'_>)

Record what the kernel accepted, so the next frame’s fast path has a baseline. Call after a successful real commit, never after a test.

A TEST_ONLY applies nothing to hardware, so recording one would let a later commit diff against state the kernel never took and suppress properties it still requires.

Source

pub fn record_commit(&mut self, applied: &[(u32, LayerRef<'_>)])

Replace the committed baseline with what a real commit just applied.

Planes absent from applied are dropped rather than left behind: the commit that carried this assignment explicitly disabled every candidate plane it did not use, so any baseline they had describes state the kernel no longer holds.

Call after a successful real commit, never after a test – see record_committed.

Source

pub fn record_commit_stacked( &mut self, applied: &[(u32, LayerRef<'_>)], zpos: &[(u32, u64)], )

record_commit, for a commit that wrote a stacked zpos in place of some layers’ own (see stacked_zpos).

The snapshot takes the written value, because the next frame’s diff has to compare against what the kernel holds. The hash stays the layer’s own, because the fast path compares it against the layer as the caller will present it next frame, which is the requested zpos – and an unchanged assignment of unchanged requests stacks the same.

Source

pub fn committed_baseline( &self, plane_id: u32, layer: LayerId, ) -> Option<&PropertySnapshot>

What the kernel last took on plane_id, if this commit may diff against it.

None means every property must be written:

  • the plane has no baseline – first use, or it was detached since;
  • the baseline belongs to a different layer, so the incoming one would silently inherit any property the outgoing layer set and it does not (a stale rotation, a stale alpha);
  • full writes were forced for a driver that mishandles partial ones.
Source

pub fn set_reserved_planes(&mut self, planes: &[u32])

Hold these planes back from the search.

A reserved plane is not offered to any layer, and the caller arms it itself. The composition canvas needs this: with more layers than planes the search would otherwise claim every one of them, and the canvas that exists to rescue the overflow would have nowhere to land — so the overflow is dropped instead of composited, which is the opposite of what the fallback is for.

Reserving costs a plane whether or not it ends up used, so a caller should reserve only when it can tell the search will overflow.

Source

pub fn plane_is_off(&self, plane_id: u32) -> bool

Whether the kernel currently has nothing on plane_id.

A plane with no baseline was never activated, or was detached by the commit that dropped it. Either way the kernel already has it off, and disabling it again is a pair of property writes the kernel still has to walk on every frame.

Source

pub fn lit_planes(&self) -> Vec<u32>

Every plane the kernel has a framebuffer on, as far as this allocator committed it.

What a scene leaving its CRTC has to turn off before the baseline that records it is forgotten: nothing else knows these planes are lit.

Source

pub const fn set_force_full_property_writes(&mut self, force: bool)

Re-emit every property on every commit, for drivers that mishandle a partial write. Off by default; this is a quirk escape hatch, not a tuning knob – it multiplies per-frame property traffic.

Source

pub const fn force_full_property_writes(&self) -> bool

Whether full property writes are being forced.

Source

pub fn forget_layer(&mut self, layer: LayerId)

Drop every trace of a layer the scene has removed.

Without this the cached assignment still names the departed layer, so the FB-only fast path sees “a previously-placed layer is gone” and falls back to a tested pass on the frame after every removal – one wasted TEST_ONLY commit for a configuration that cannot have become invalid, since dropping a layer only ever frees resources.

The committed baseline’s entry is kept and only its identity cleared, as upstream does: it is the only record that the plane is still lit, and a cleared identity forces a full property write for the next layer to land there.

Source

pub fn forget_plane(&mut self, plane_id: u32)

Forget a plane’s committed baseline, after detaching it.

Source

pub const fn previous_allocation(&self) -> &PlaneAssignment

The cached assignment from the previous pass.

Source

pub fn is_fb_only_frame(&self, present: &[LayerRef<'_>]) -> bool

Invariant 4. Whether only content changed on already-placed layers.

True when every plane committed last frame still maps to the same layer, and that layer’s current property_hash matches what was recorded at commit. Since the hash excludes FB_ID and IN_FENCE_FD, a match proves geometry, format, and modifier are identical to the assignment the kernel already accepted — so the redundant TEST_ONLY can be skipped and the real commit’s own atomic_check left as the sole arbiter.

Any placement, format, or modifier edit defeats it, as does a previously-placed layer vanishing this frame.

Source

pub fn allocate<C: TestCommitter>( &mut self, layers: &[LayerRef<'_>], registry: &PlaneRegistry, crtc_index: u32, committer: &mut C, ) -> Result<Allocation, TestFailure>

Assign layers to planes.

Layers that cannot be placed come back in Allocation::composited for the caller’s composition fallback.

§Errors

TestFailure::NotMaster if DRM master was lost mid-search. Ordinary rejections are handled internally by trying smaller assignments.

Source

pub const fn canvas_zpos(&self) -> Option<u64>

Where planes take a zpos, the zpos the composition canvas asks for after the last allocate: that of the lowest placed layer that overlaps a composited one and must cover it, for the canvas to stack just under (drm-cxx 07226e4, #343). None when no placed layer must cover the canvas, which may then stack on top, and where planes stack by id, where the canvas plane’s id places it.

The slot asks for a placed layer’s own zpos, so the canvas has to stack under ties: stacked_zpos_beneath.

Source

pub fn record_probe_rejection( &mut self, crtc_index: u32, plane_id: u32, layer: &Layer, )

Record that a plane rejected a (fourcc, modifier) pair, so a lying IN_FORMATS costs one probe rather than a dropped edge re-probed every frame.

Trait Implementations§

Source§

impl Debug for Allocator

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Allocator

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.