#[non_exhaustive]pub struct CommitReport {Show 18 fields
pub layers_total: usize,
pub layers_assigned: usize,
pub layers_composited: usize,
pub layers_unassigned: usize,
pub layers_skipped_no_frame: usize,
pub composition_buckets: usize,
pub pin_requests_unhonored: usize,
pub properties_written: usize,
pub fbs_attached: usize,
pub damaged_layers: usize,
pub in_fences_armed: usize,
pub in_fence_cpu_waits: usize,
pub test_commits_issued: usize,
pub fb_delta_fast_path: bool,
pub commit_seq: u64,
pub budget_exhausted: bool,
pub skipped_idle: bool,
pub placements: Vec<LayerPlacement>,
}Expand description
A snapshot of a single commit.
Every field starts at zero, false or empty. #[non_exhaustive] because the
C++ has grown counters additively — layers_skipped_no_frame,
fb_delta_fast_path, the fence counters — and keeping that freedom means a
new counter is not a breaking change.
Fields (Non-exhaustive)§
This struct is marked as non-exhaustive
Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.layers_total: usizeLayers in the scene at commit time.
§Accounting invariant
layers_total == layers_assigned
+ layers_composited
+ layers_unassigned
+ layers_skipped_no_frameChecked by accounting_balances. This is
why a layer with no format is never placed: counting it as both assigned
and skipped would break this.
layers_assigned: usizeLayers the allocator put directly on a hardware plane.
layers_composited: usizeLayers rescued by composition into the canvas. They do reach hardware — via the canvas plane rather than one of their own.
layers_unassigned: usizeLayers neither placed nor composited: dropped this frame.
layers_skipped_no_frame: usizeLayers whose source returned WouldBlock,
and which held their previous frame – or stayed absent, if they had
none.
Flow control, not failure — a live source with nothing to contribute this vblank. Graphable as a per-layer frame-stall signal.
composition_buckets: usizeComposition buckets emitted. 0 or 1 with a single full-screen canvas.
pin_requests_unhonored: usizeLayers that asked to be pinned to a plane but could not be.
The pin target was not on this CRTC, did not support the format, or was already claimed. The layer is not dropped — it falls back to normal allocation and is still counted in one of the placement tallies, so this does not affect the accounting invariant. Nonzero is a loud signal that a caller’s deterministic-plane assumption was violated, as opposed to a silent drop.
properties_written: usizeProperties enqueued on the atomic request — plane, CRTC, and connector state together.
A regression signal for property minimization: it should drop meaningfully on a commit whose layers did not change.
fbs_attached: usizeSubset of properties_written that are FB_ID attachments.
damaged_layers: usizeSubset of properties_written: layers that got an FB_DAMAGE_CLIPS
blob.
Zero on a full-frame commit — no damage reported, over budget, degenerate clips, or a plane without the property. Tells a bandwidth-sensitive consumer whether partial updates actually reach the kernel.
in_fences_armed: usizeLayers whose acquire fence was handed to the kernel as IN_FENCE_FD —
the real explicit-sync path, with no CPU stall.
The honest signal that explicit sync is live on this driver: a source
may always produce a fence and the plane still not accept it, which
shows up in in_fence_cpu_waits instead.
in_fence_cpu_waits: usizeLayers that carried a fence the plane could not take, so the scene blocked on a CPU wait before committing.
Real commits only; a test never waits. Both counters zero means no source produced a fence at all.
test_commits_issued: usizeInternal test commits the allocator issued while searching.
A cold start can spend the whole budget; a warm start is 1; the FB-only fast path is 0.
fb_delta_fast_path: boolWhether this commit took the FB-only fast path — only content changed on
already-placed layers, so the cached assignment was reused and the
TEST_ONLY skipped entirely.
Invariant 4’s observable signal. Implies
test_commits_issued == 0, which a steady-state consumer asserts to
confirm it is paying one atomic commit per frame rather than two.
commit_seq: u64Names this commit for
LayerScene::release_displaced:
real commits that the kernel took count from 1. 0 for a test commit
and for a rejected one.
budget_exhausted: boolWhether the per-frame test-commit budget, not the hardware, is what stopped more layers reaching planes.
When this is set, some of layers_composited is a capacity decision
rather than a plane shortage: there were compatible planes free and the
allocator ran out of permitted round trips to the kernel before it
could offer them. Composited layers still reach the screen, so this is
not an error – but a caller tuning a scene needs to tell “this device
has no more planes” from “raise the budget”, and layers_composited
alone cannot. Raise it with Allocator::set_max_test_commits.
skipped_idle: boolWhether the frame was suppressed as idle – no commit was issued at all.
Set only by a caller that gates on content changing, such as
ScanoutBackend::present_if_changed. When it is set every other field
is zero, because there was no commit to count: no layers were walked,
no properties written, no placements decided. A consumer reading
layers_assigned == 0 without checking this would conclude the frame
failed to reach a plane, when in fact the previous frame is still on
screen and correct.
The first frame is never suppressed, whatever the caller says about content: nothing is on screen yet, so “unchanged” describes nothing.
placements: Vec<LayerPlacement>Per-layer outcomes.
Implementations§
Source§impl CommitReport
impl CommitReport
Sourcepub fn suppressed() -> Self
pub fn suppressed() -> Self
The report for a frame that was suppressed as idle.
Every count is zero because there was no commit to count, and
skipped_idle says why. A constructor rather
than a struct literal because this type is #[non_exhaustive]: a
caller outside this crate cannot build one, and the suppressed frame is
the one shape it legitimately needs to.
Sourcepub fn placement_of(&self, layer: impl Into<LayerId>) -> Option<&LayerPlacement>
pub fn placement_of(&self, layer: impl Into<LayerId>) -> Option<&LayerPlacement>
What became of one layer this commit.
None when the layer was not in the frame at all – it was never
added, it was removed, or the handle is stale. A stale one cannot
match by accident: LayerHandle::layer_id
packs the slot’s generation, so a recycled slot yields a different id
and a handle to the layer that used to live there resolves to nothing.
Sourcepub fn was_composited(&self, layer: impl Into<LayerId>) -> bool
pub fn was_composited(&self, layer: impl Into<LayerId>) -> bool
Whether the layer went into the canvas rather than onto a plane of its own.
False for a layer that was dropped as well as for one that got a plane: composited means it reached the screen the slow way, and unassigned means it did not reach the screen at all. A caller throttling its repaints on this distinction wants them separated.
Sourcepub const fn accounting_balances(&self) -> bool
pub const fn accounting_balances(&self) -> bool
Whether the layer tallies add up to layers_total.
The C++ states this as a comment on the field. Making it checkable means the scene’s own tests can assert it every commit, which is how a layer counted twice — or not at all — gets caught at the point it happens.
Sourcepub const fn fast_path_consistent(&self) -> bool
pub const fn fast_path_consistent(&self) -> bool
Whether the fast path’s implied contract holds: taking it means issuing no test commits.