pub struct LayerScene { /* private fields */ }Expand description
One CRTC’s layers and the commit machinery around them.
Layers are added once and kept: add_layer returns a
LayerHandle that stays valid until the layer is removed, and a stale
one resolves to nothing rather than to whatever now occupies the slot. Per
frame, the caller changes what it wants — geometry through
set_display, content through the source — and
asks for a build_frame.
The scene is deliberately not a commit loop. It builds a plan and reads the kernel’s answer; issuing the commit, attaching a modeset, and dispatching the page-flip event belong to the caller, because a compositor has its own ideas about all three. See the crate documentation for the shape of a frame.
Four of the six pinned invariants are the scene’s: buffers released a flip late rather than an ioctl early (1), a failed commit releasing immediately because no flip will gate it (2), the FB-only fast path (4), and teardown not letting go of what an outstanding flip still scans out (5). The first three are what the type does. The fifth it checks: landing the flip is the caller’s job, and dropping a scene with one still armed asserts.
Implementations§
Source§impl LayerScene
impl LayerScene
Sourcepub const fn is_suspended(&self) -> bool
pub const fn is_suspended(&self) -> bool
Whether the scene is suspended after losing DRM master.
Sourcepub const fn has_pending_flip(&self) -> bool
pub const fn has_pending_flip(&self) -> bool
Whether a page-flip event is armed and undispatched.
Invariant 5. Dropping the scene while this is true tears down a framebuffer the flip still references.
Sourcepub const fn flip_landed(&mut self)
pub const fn flip_landed(&mut self)
Record that the armed page-flip event was dispatched.
Sourcepub fn resume(&mut self)
pub fn resume(&mut self)
Lift a suspension after the session resumes.
Whoever held the CRTC meanwhile may have left planes lit, so the next frame turns off every plane it does not use.
Sourcepub fn on_session_paused(&mut self)
pub fn on_session_paused(&mut self)
The session is losing DRM master: hand every held buffer back to its source, then tell each source and the canvas to stop using the device.
Port of the reference’s LayerScene::on_session_paused. The held
buffers go back first, while their sources are still in a state to
accept a release; each source then forgets or tears down what is bound
to the descriptor, and the canvas forgets its buffers without an ioctl.
A flip still armed is counted as landed: its event belongs to a
descriptor that is being revoked and will never be dispatched.
Call on_session_resumed with the new
device when the session comes back.
Sourcepub fn on_session_resumed(&mut self, device: &Device) -> Result<(), SceneError>
pub fn on_session_resumed(&mut self, device: &Device) -> Result<(), SceneError>
The session is back, on device: rebuild everything that belonged to
the old descriptor, and lift any suspension.
Port of the reference’s LayerScene::on_session_resumed. The
allocator forgets the previous output – its assignment, committed
baseline and failure cache describe kernel state the old descriptor
took with it – so the next frame is searched and emitted in full, and
is never skipped as unchanged. Each source re-allocates or re-imports
against device. The canvas is made again at its size and format; if
that fails composition is off until
enable_composition is called again, as
the reference drops its canvas then.
The plane registry is the caller’s, passed to each
build_frame: probe it again on device too.
§Errors
SceneError::Source with the first source that cannot resume, as
the reference stops at it. The sources after it have not been resumed,
and the scene stays suspended if it was; calling this again retries.
Sourcepub fn add_layer(&mut self, source: Box<dyn LayerBufferSource>) -> LayerHandle
pub fn add_layer(&mut self, source: Box<dyn LayerBufferSource>) -> LayerHandle
Add a layer backed by source.
Sourcepub fn remove_layer(&mut self, handle: LayerHandle) -> bool
pub fn remove_layer(&mut self, handle: LayerHandle) -> bool
Remove a layer, returning whether there was one. A stale handle is a
no-op and returns false.
If the layer’s buffers are still in flight, its source is kept alive
until they come back — releasing to a dropped source would strand them.
The source hears LayerBufferSource::on_retired first.
Sourcepub fn layer(&self, handle: LayerHandle) -> Option<&SceneLayer>
pub fn layer(&self, handle: LayerHandle) -> Option<&SceneLayer>
Borrow a layer, or None if the handle is stale.
Sourcepub fn layer_mut(&mut self, handle: LayerHandle) -> Option<&mut SceneLayer>
pub fn layer_mut(&mut self, handle: LayerHandle) -> Option<&mut SceneLayer>
Borrow a layer mutably, or None if the handle is stale.
Sourcepub fn handles(&self) -> impl Iterator<Item = LayerHandle> + '_
pub fn handles(&self) -> impl Iterator<Item = LayerHandle> + '_
Every live handle, in slot order.
Sourcepub fn build_frame<C: TestCommitter>(
&mut self,
registry: &PlaneRegistry,
crtc_index: u32,
kind: CommitKind,
committer: &mut C,
) -> Result<FrameBuild, SceneError>
pub fn build_frame<C: TestCommitter>( &mut self, registry: &PlaneRegistry, crtc_index: u32, kind: CommitKind, committer: &mut C, ) -> Result<FrameBuild, SceneError>
Acquire from every layer, lower it, and run the allocator.
A source with nothing to contribute is skipped and counted, never
treated as a failure — see SourceError::WouldBlock.
§Errors
SceneError::Suspendedwhile the scene is suspended.SceneError::Sourceif a source failed for a real reason.SceneError::NotMasterif the allocator lost DRM master.SceneError::DetachPendingif arebindto another CRTC left planes lit thatcommit_detachhas not yet turned off.
Sourcepub fn enable_composition(
&mut self,
device: &Device,
width: u32,
height: u32,
) -> Result<(), DumbError>
pub fn enable_composition( &mut self, device: &Device, width: u32, height: u32, ) -> Result<(), DumbError>
Give the scene a composition canvas.
Layers the allocator cannot place are blended into this surface and it goes on a plane of its own. Without one they are reported unassigned and simply do not reach the screen.
Separate from new because the search itself is
device-free and this is not: the canvas owns two dumb buffers.
The canvas is ARGB8888, which every plane that matters takes.
enable_composition_on is for the ones
that do not.
§Errors
drmkit_dumb::DumbError if either buffer cannot be allocated.
Sourcepub fn enable_composition_on(
&mut self,
device: &Device,
width: u32,
height: u32,
plane: &PlaneCapabilities,
) -> Result<(), DumbError>
pub fn enable_composition_on( &mut self, device: &Device, width: u32, height: u32, plane: &PlaneCapabilities, ) -> Result<(), DumbError>
Give the scene a canvas in a format plane can scan out.
On a controller with one plane and no ARGB8888 – tilcdc on a
BeagleBone, i.MX LCDIF, the small SPI panels – an ARGB8888 canvas
cannot be armed at all, so every layer that overflows the plane count
is dropped rather than composited. This negotiates instead, and blends
in ARGB8888 regardless: the conversion happens once at flush rather
than quantizing at every layer.
§Errors
drmkit_dumb::DumbError::InvalidConfig if the plane advertises nothing
the canvas
can write – a YUV-only overlay, say – and otherwise as
enable_composition.
Sourcepub fn canvas_fourcc(&self) -> Option<u32>
pub fn canvas_fourcc(&self) -> Option<u32>
What the composition canvas scans out as, if there is one.
Sourcepub fn finalize_frame(
&mut self,
build: FrameBuild,
result: KernelResult,
) -> CommitReport
pub fn finalize_frame( &mut self, build: FrameBuild, result: KernelResult, ) -> CommitReport
Reconcile scene state with the kernel’s answer, releasing buffers per invariants 1, 2 and 5.
Sourcepub fn finalize_frame_with_fence(
&mut self,
build: FrameBuild,
result: KernelResult,
release_fence: Option<&SyncFence>,
) -> CommitReport
pub fn finalize_frame_with_fence( &mut self, build: FrameBuild, result: KernelResult, release_fence: Option<&SyncFence>, ) -> CommitReport
Finalize a frame, handing over the commit’s OUT_FENCE.
release_fence signals once this commit’s buffers are on screen —
which is the moment the buffers it displaced are off screen and safe
to render into again. Sources that opted in through
wants_release_fence get a
duplicate of it alongside each released buffer, so a GPU producer waits
on it GPU-side instead of blocking until a later release edge.
Ask wants_release_fence whether to arm
OUT_FENCE_PTR at all: with no source listening, the fence costs a
descriptor per commit and nothing reads it.
Passing None is finalize_frame, and is
correct whenever the CRTC has no OUT_FENCE_PTR or the commit did not
ask for one.
Sourcepub const fn set_force_full_property_writes(&mut self, force: bool)
pub const fn set_force_full_property_writes(&mut self, force: bool)
Re-emit every property on every commit, for a driver that mishandles a partial write.
Off by default. A quirk escape hatch, not a tuning knob: it defeats invariant 4’s minimal-write path and multiplies per-frame property traffic, and the only reason to reach for it is a driver that gets the minimal set wrong. Exposed here because the allocator it configures is the scene’s, and a caller stuck on such a driver otherwise has no way in.
Sourcepub const fn force_full_property_writes(&self) -> bool
pub const fn force_full_property_writes(&self) -> bool
Whether full property writes are being forced.
Sourcepub fn content_changed(&self) -> bool
pub fn content_changed(&self) -> bool
Whether this frame is worth committing at all.
The whole-commit skip. When every live source says it has nothing new and no layer has moved, the frame can be dropped without issuing an atomic commit — the display goes on scanning out what is already there. That is a power win on any device and the thing that lets a self-refresh panel stay in self-refresh, which it cannot do if a commit arrives every vblank saying nothing changed.
Answers true for anything it cannot rule out. A source that cannot
tell whether its buffer changed reports “changed” by default, so a CPU
producer painting into a dumb buffer never gets skipped; the layer set
changing counts, geometry changing counts, and the first frame always
counts, because nothing is on screen yet for “unchanged” to describe.
Sourcepub fn find_by_identity_tag(&self, tag: u64) -> Option<LayerHandle>
pub fn find_by_identity_tag(&self, tag: u64) -> Option<LayerHandle>
The first layer carrying tag, if any.
The lookup that makes SceneLayer::set_identity_tag worth setting:
a caller that survived a rebind, a session resume, or its own restart
can find its layers again from its own identifiers rather than from
handles it may no longer hold.
Sourcepub const fn pending_detach(&self) -> Option<&PendingDetach>
pub const fn pending_detach(&self) -> Option<&PendingDetach>
What a rebind left lit on the CRTC the scene moved away from, until it is committed.
Sourcepub fn detach_committed(&mut self)
pub fn detach_committed(&mut self)
Record that the pending detach reached the kernel.
commit_detach calls this after a successful
commit; a caller committing the detach some other way must too.
Sourcepub fn rebind(&mut self, crtc_id: u32, width: u32, height: u32) -> RebindReport
pub fn rebind(&mut self, crtc_id: u32, width: u32, height: u32) -> RebindReport
Move this scene to another output, keeping its layers.
For an output that changed underneath the caller: a mode set, a different CRTC after a session resume, a display swapped for another. Layer handles survive — that is the point, since a caller that had to rebuild its layers would have to rebuild everything it knows about them too.
Everything cached about the previous output is dropped – the assignment, the committed baseline, the failure cache – and the next commit is a full emit, with which the caller must set the new mode.
Moving to another CRTC leaves the old one’s planes lit, and the
scene cannot turn them off itself: it does not commit. They are queued
as a PendingDetach, and until the caller commits it through
commit_detach, build_frame
refuses with SceneError::DetachPending. Two reasons it cannot be
skipped: the kernel will not move a plane from one CRTC to another in a
single commit, so a plane both pipes can use is rejected outright on the
new one; and a plane the new pipe cannot use would go on showing this
scene’s last frame on the old output indefinitely. A rebind to the same
CRTC queues nothing. Upstream fixed the same defect in a335bef
(drm-cxx#340), from inside its rebind, which owns a device.
Returns what will not fit. A layer whose destination lies outside the new mode is not dropped — the caller may be about to move it, and deciding on its behalf would be worse than saying so.
Sourcepub fn wants_release_fence(&self) -> bool
pub fn wants_release_fence(&self) -> bool
Whether any live source wants this commit’s OUT_FENCE.
The commit path asks before arming OUT_FENCE_PTR: the fence costs a
descriptor per commit that has to be closed, and with nothing listening
it is a descriptor leak dressed as a feature.
Sourcepub fn release_displaced(&mut self, commit_seq: u64)
pub fn release_displaced(&mut self, commit_seq: u64)
Hand back now the buffers that the page flip of real commit
commit_seq (its CommitReport::commit_seq) took off screen,
instead of at the next real commit.
A buffer leaves the screen when the flip of the commit after the one that showed it completes, and the scene hands it back at the commit after that (invariant 1) – right for a caller that commits ahead of its flips. A caller that waits for each flip before committing again, feeding producers that wait for a free buffer before rendering, may never make that next commit: a producer with two buffers then waits for good, and one with three gets each buffer a frame late. Call this once the flip completes – after dispatching its event, or at the start of the next frame – to hand those buffers back then.
A no-op unless commit_seq names the most recent real commit, so a
late or repeated call cannot release a buffer still on screen. Port of
upstream’s release_displaced (ee90026).
Sourcepub fn drain(&mut self)
pub fn drain(&mut self)
Hand every held buffer back, without waiting on the kernel.
Invariant 5. For teardown, session pause, and rebind. Landing an
armed flip first is the caller’s job — see
has_pending_flip.