drmkit_present/producer.rs
1// SPDX-FileCopyrightText: (c) 2026 Joel Winarske
2// SPDX-License-Identifier: MIT
3
4//! The seam between a renderer and the scanout path.
5//!
6//! Port of `src/present/scanout_producer.hpp`.
7
8use drmkit_scene::LayerBufferSource;
9
10/// Why a producer could not hand over a buffer.
11#[derive(Debug, thiserror::Error)]
12#[non_exhaustive]
13pub enum ProducerError {
14 /// The producer could not allocate what was asked for.
15 #[error("the producer could not allocate: {0}")]
16 Allocate(String),
17 /// The producer can make none of the layouts it was allowed.
18 ///
19 /// Refused rather than allocated outside the set: a buffer in a layout no
20 /// plane takes fails at the atomic commit, naming the framebuffer rather
21 /// than the layout that made it unscannable (drm-cxx `f5bed8f`).
22 #[error("the producer can make none of the allowed modifiers")]
23 Unsupported,
24}
25
26/// Something that allocates the buffers a scanout path presents.
27///
28/// Two questions, asked once each when a scanout path is built: *what layouts
29/// can you produce for this format*, and *give me a buffer in one of these*. The
30/// backend intersects the first answer against what the CRTC's planes can scan
31/// out, and the intersection is what it passes back in.
32///
33/// Splitting it this way is what lets a GBM, GL, or Vulkan renderer plug into
34/// the same backend: each knows what it can export, none of them needs to know
35/// anything about planes.
36pub trait ScanoutProducer {
37 /// Which modifiers this producer can allocate `fourcc` in and hand to KMS.
38 ///
39 /// An empty answer means "no opinion" and the backend falls back to
40 /// `LINEAR`.
41 fn exportable_modifiers(&mut self, fourcc: u32) -> Vec<u64>;
42
43 /// Allocate a buffer and wrap it as a scene source.
44 ///
45 /// `allowed` is the negotiated intersection: the layouts that both this
46 /// producer said it can export and some plane on the CRTC can scan out.
47 /// **Honor it.** A buffer allocated outside that set is one no plane will
48 /// take, and the refusal arrives at the atomic commit, naming the
49 /// framebuffer rather than the layout that made it unscannable.
50 ///
51 /// An empty `allowed` means no constraint, not that nothing is acceptable.
52 /// The backend never passes one: where no plane exposes `IN_FORMATS`, or
53 /// none overlaps what this producer can make, it passes `LINEAR` alone.
54 ///
55 /// # Errors
56 ///
57 /// [`ProducerError::Allocate`] if the buffer cannot be produced;
58 /// [`ProducerError::Unsupported`] if the producer can make none of the
59 /// `allowed` layouts.
60 fn create_buffer(
61 &mut self,
62 width: u32,
63 height: u32,
64 fourcc: u32,
65 allowed: &[u64],
66 ) -> Result<Box<dyn LayerBufferSource>, ProducerError>;
67}