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` and
7//! `src/present/gbm_producer.{hpp,cpp}`.
8
9use drmkit_scene::LayerBufferSource;
10
11/// Why a producer could not hand over a buffer.
12#[derive(Debug, thiserror::Error)]
13#[non_exhaustive]
14pub enum ProducerError {
15 /// The producer could not allocate what was asked for.
16 #[error("the producer could not allocate: {0}")]
17 Allocate(String),
18 /// The producer can make none of the layouts it was allowed.
19 ///
20 /// Refused rather than allocated outside the set: a buffer in a layout no
21 /// plane takes fails at the atomic commit, naming the framebuffer rather
22 /// than the layout that made it unscannable (drm-cxx `f5bed8f`).
23 #[error("the producer can make none of the allowed modifiers")]
24 Unsupported,
25}
26
27/// Something that allocates the buffers a scanout path presents.
28///
29/// Two questions, asked once each when a scanout path is built: *what layouts
30/// can you produce for this format*, and *give me a buffer in one of these*. The
31/// backend intersects the first answer against what the CRTC's planes can scan
32/// out, and the intersection is what it passes back in.
33///
34/// Splitting it this way is what lets a GBM, GL, or Vulkan renderer plug into
35/// the same backend: each knows what it can export, none of them needs to know
36/// anything about planes.
37pub trait ScanoutProducer {
38 /// Which modifiers this producer can allocate `fourcc` in and hand to KMS.
39 ///
40 /// An empty answer means "no opinion" and the backend falls back to
41 /// `LINEAR`.
42 fn exportable_modifiers(&mut self, fourcc: u32) -> Vec<u64>;
43
44 /// Allocate a buffer and wrap it as a scene source.
45 ///
46 /// `allowed` is the negotiated intersection: the layouts that both this
47 /// producer said it can export and some plane on the CRTC can scan out.
48 /// **Honor it.** A buffer allocated outside that set is one no plane will
49 /// take, and the refusal arrives at the atomic commit, naming the
50 /// framebuffer rather than the layout that made it unscannable.
51 ///
52 /// An empty `allowed` means no constraint -- the backend reaches it when no
53 /// plane exposes `IN_FORMATS` -- not that nothing is acceptable.
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}