1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// SPDX-FileCopyrightText: (c) 2026 Joel Winarske
// SPDX-License-Identifier: MIT
//! Scene composition for drmkit.
//!
//! Port of `src/scene/` from drm-cxx @ `dc2915b`.
//!
//! A [`LayerScene`] owns one CRTC's stack of layers and the machinery that
//! turns them into a frame: which plane each layer lands on, what to composite
//! when there are more layers than planes, and when a buffer may go back to
//! whoever produced it.
//!
//! # Driving a frame
//!
//! ```text
//! let mut scene = LayerScene::new(crtc_id);
//! let handle = scene.add_layer(Box::new(source));
//! scene.layer_mut(handle)?.set_display(DisplayParams { .. });
//!
//! // per frame
//! let mut build = scene.build_frame(®istry, crtc_index, kind, &mut committer)?;
//! emit_frame(&mut request, &map, &mut build, modeset)?;
//! arm_acquire_fences(&mut build, &mut request, &map, true)?;
//! let result = request.commit(&device, flags);
//! let report = scene.finalize_frame(build, classify(&result));
//! // ... and when the page-flip event arrives
//! scene.flip_landed();
//! ```
//!
//! The split between `build_frame` and `finalize_frame` is the part worth
//! understanding. `build_frame` acquires a buffer from every source, runs the
//! allocator — which may issue `TEST_ONLY` commits through the committer to
//! ask the kernel whether an arrangement is legal — and hands back a
//! [`FrameBuild`] describing what to program. It does not commit: the caller
//! owns the request, so it can add a modeset, arm fences, or refuse the frame.
//! `finalize_frame` then takes the kernel's answer and settles the
//! bookkeeping, which is what arms the flip and what decides which buffers are
//! now safe to release.
//!
//! A `FrameBuild` dropped without `finalize_frame` leaks its acquisitions, and
//! says so in a debug assertion rather than silently.
//!
//! # A source with nothing ready
//!
//! [`SourceError::WouldBlock`] from a source is not an error. It is the
//! documented "no frame yet" signal — a V4L2 device before its first sample,
//! an appsink mid-preroll — and the layer is *skipped* for that frame and
//! counted in the report, keeping the plane it had and the framebuffer already
//! on it. A frame does not fail because one producer was late, and a starved
//! layer does not blink off screen.
//!
//! # The release contract
//!
//! Two of the four pinned invariants this crate carries are about *when* a
//! buffer goes back to its source:
//!
//! - **Invariant 1** — a buffer is released only after the page flip that stops
//! scanning it out completes, two commits deep, never when the ioctl returns.
//! Releasing early lets a producer overwrite a buffer the display engine is
//! still reading, which tears on hardware whose producer driver attaches no
//! reservation fence.
//! - **Invariant 2** — a *failed* commit releases that frame's acquisitions
//! immediately, because no flip is in flight to gate them on, and a
//! non-`EACCES` failure does not suspend the scene. Self-healing producer
//! rings depend on being able to reuse their buffers after a rejection.
//!
//! [`AcquiredBuffer`] is move-only, so handing a buffer back early is a move
//! and using it afterwards does not compile — the ownership half of invariant 1
//! is enforced by the type. The timing half cannot be, and is pinned against a
//! real device in `tests/release_invariants_vkms.rs`.
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;