1use std::os::unix::io::AsFd;
4
5use thiserror::Error;
6
7use drm::{
8 buffer::PlanarBuffer,
9 control::{framebuffer, Device, FbCmd2Flags},
10};
11use drm_fourcc::DrmModifier;
12use tracing::{trace, warn};
13#[cfg(feature = "wayland_frontend")]
14use wayland_server::protocol::wl_buffer::WlBuffer;
15
16#[cfg(feature = "wayland_frontend")]
17use crate::backend::allocator::Buffer;
18use crate::backend::{
19 allocator::{
20 dmabuf::Dmabuf,
21 format::{get_bpp, get_depth, get_opaque},
22 gbm::GbmBuffer,
23 Fourcc,
24 },
25 drm::DrmDeviceFd,
26};
27use crate::utils::DevPath;
28
29use super::{error::AccessError, warn_legacy_fb_export, Framebuffer};
30
31#[derive(Debug)]
33pub struct GbmFramebuffer {
34 fb: framebuffer::Handle,
35 format: drm_fourcc::DrmFormat,
36 drm: DrmDeviceFd,
37}
38
39impl Drop for GbmFramebuffer {
40 #[inline]
41 fn drop(&mut self) {
42 trace!(fb = ?self.fb, "destroying framebuffer");
43 if let Err(err) = self.drm.destroy_framebuffer(self.fb) {
44 warn!(fb = ?self.fb, ?err, "failed to destroy framebuffer");
45 }
46 }
47}
48
49impl AsRef<framebuffer::Handle> for GbmFramebuffer {
50 #[inline]
51 fn as_ref(&self) -> &framebuffer::Handle {
52 &self.fb
53 }
54}
55
56impl Framebuffer for GbmFramebuffer {
57 #[inline]
58 fn format(&self) -> drm_fourcc::DrmFormat {
59 self.format
60 }
61}
62
63#[cfg(feature = "wayland_frontend")]
71#[profiling::function]
72pub fn framebuffer_from_wayland_buffer<A: AsFd + 'static>(
73 drm: &DrmDeviceFd,
74 gbm: &gbm::Device<A>,
75 buffer: &WlBuffer,
76 use_opaque: bool,
77) -> Result<Option<GbmFramebuffer>, Error> {
78 if let Ok(dmabuf) = crate::wayland::dmabuf::get_dmabuf(buffer) {
79 if dmabuf.format().modifier != DrmModifier::Invalid {
88 return Ok(Some(framebuffer_from_dmabuf(
89 drm, gbm, dmabuf, use_opaque, false,
90 )?));
91 }
92 }
93
94 #[cfg(all(feature = "backend_egl", feature = "use_system_lib"))]
95 if matches!(
96 crate::backend::renderer::buffer_type(buffer),
97 Some(crate::backend::renderer::BufferType::Egl)
98 ) {
99 let bo = gbm
100 .import_buffer_object_from_wayland::<()>(buffer, gbm::BufferObjectFlags::SCANOUT)
101 .map(|bo| GbmBuffer::from_bo(bo, true))
102 .map_err(Error::Import)?;
103 let (fb, format) = framebuffer_from_bo_internal(
104 drm,
105 BufferObjectInternal {
106 bo: &bo,
107 offsets: None,
108 pitches: None,
109 },
110 use_opaque,
111 true,
112 )
113 .map_err(Error::Drm)?;
114
115 return Ok(Some(GbmFramebuffer {
116 fb,
117 format,
118 drm: drm.clone(),
119 }));
120 }
121
122 Ok(None)
123}
124
125#[derive(Error, Debug)]
127pub enum Error {
128 #[error("failed to import the dmabuf to gbm")]
130 Import(std::io::Error),
131 #[error("failed to add a framebuffer for the bo")]
133 Drm(AccessError),
134}
135
136#[profiling::function]
141pub fn framebuffer_from_dmabuf<A: AsFd + 'static>(
142 drm: &DrmDeviceFd,
143 gbm: &gbm::Device<A>,
144 dmabuf: &Dmabuf,
145 use_opaque: bool,
146 allow_legacy: bool,
147) -> Result<GbmFramebuffer, Error> {
148 let bo: GbmBuffer = dmabuf
149 .import_to(gbm, gbm::BufferObjectFlags::SCANOUT)
150 .map_err(Error::Import)?;
151
152 let mut offsets: [u32; 4] = [0; 4];
156 let mut pitches: [u32; 4] = [0; 4];
157
158 for (index, offset) in dmabuf.offsets().enumerate() {
159 offsets[index] = offset;
160 }
161
162 for (index, stride) in dmabuf.strides().enumerate() {
163 pitches[index] = stride;
164 }
165
166 framebuffer_from_bo_internal(
167 drm,
168 BufferObjectInternal {
169 bo: &bo,
170 offsets: Some(offsets),
171 pitches: Some(pitches),
172 },
173 use_opaque,
174 allow_legacy,
175 )
176 .map_err(Error::Drm)
177 .map(|(fb, format)| GbmFramebuffer {
178 fb,
179 format,
180 drm: drm.clone(),
181 })
182}
183
184#[profiling::function]
186pub fn framebuffer_from_bo(
187 drm: &DrmDeviceFd,
188 bo: &GbmBuffer,
189 use_opaque: bool,
190) -> Result<GbmFramebuffer, AccessError> {
191 framebuffer_from_bo_internal(
192 drm,
193 BufferObjectInternal {
194 bo,
195 offsets: None,
196 pitches: None,
197 },
198 use_opaque,
199 true,
200 )
201 .map(|(fb, format)| GbmFramebuffer {
202 fb,
203 format,
204 drm: drm.clone(),
205 })
206}
207
208struct BufferObjectInternal<'a> {
209 bo: &'a GbmBuffer,
210 pitches: Option<[u32; 4]>,
211 offsets: Option<[u32; 4]>,
212}
213
214impl std::ops::Deref for BufferObjectInternal<'_> {
215 type Target = GbmBuffer;
216
217 #[inline]
218 fn deref(&self) -> &Self::Target {
219 self.bo
220 }
221}
222
223impl PlanarBuffer for BufferObjectInternal<'_> {
224 #[inline]
225 fn size(&self) -> (u32, u32) {
226 PlanarBuffer::size(self.bo)
227 }
228
229 #[inline]
230 fn format(&self) -> drm_fourcc::DrmFourcc {
231 PlanarBuffer::format(self.bo)
232 }
233
234 #[inline]
235 fn modifier(&self) -> Option<DrmModifier> {
236 PlanarBuffer::modifier(self.bo)
237 }
238
239 #[inline]
240 fn pitches(&self) -> [u32; 4] {
241 self.pitches.unwrap_or_else(|| PlanarBuffer::pitches(self.bo))
242 }
243
244 #[inline]
245 fn handles(&self) -> [Option<drm::buffer::Handle>; 4] {
246 PlanarBuffer::handles(self.bo)
247 }
248
249 #[inline]
250 fn offsets(&self) -> [u32; 4] {
251 self.offsets.unwrap_or_else(|| PlanarBuffer::offsets(self.bo))
252 }
253}
254
255struct OpaqueBufferWrapper<'a, B>(&'a B);
256impl<B> PlanarBuffer for OpaqueBufferWrapper<'_, B>
257where
258 B: PlanarBuffer,
259{
260 #[inline]
261 fn size(&self) -> (u32, u32) {
262 self.0.size()
263 }
264
265 #[inline]
266 fn format(&self) -> Fourcc {
267 let fmt = self.0.format();
268 get_opaque(fmt).unwrap_or(fmt)
269 }
270
271 #[inline]
272 fn modifier(&self) -> Option<DrmModifier> {
273 self.0.modifier()
274 }
275
276 #[inline]
277 fn pitches(&self) -> [u32; 4] {
278 self.0.pitches()
279 }
280
281 #[inline]
282 fn handles(&self) -> [Option<drm::buffer::Handle>; 4] {
283 self.0.handles()
284 }
285
286 #[inline]
287 fn offsets(&self) -> [u32; 4] {
288 self.0.offsets()
289 }
290}
291
292#[profiling::function]
293#[inline]
294fn framebuffer_from_bo_internal<D>(
295 drm: &D,
296 bo: BufferObjectInternal<'_>,
297 use_opaque: bool,
298 allow_legacy: bool,
299) -> Result<(framebuffer::Handle, drm_fourcc::DrmFormat), AccessError>
300where
301 D: drm::control::Device + DevPath,
302{
303 let modifier = bo.modifier();
304 let flags = if bo.modifier().is_some() {
305 FbCmd2Flags::MODIFIERS
306 } else {
307 FbCmd2Flags::empty()
308 };
309
310 let ret = if use_opaque {
311 let opaque_wrapper = OpaqueBufferWrapper(&bo);
312 drm.add_planar_framebuffer(&opaque_wrapper, flags).map(|fb| {
313 (
314 fb,
315 drm_fourcc::DrmFormat {
316 code: opaque_wrapper.format(),
317 modifier: modifier.unwrap_or(DrmModifier::Invalid),
318 },
319 )
320 })
321 } else {
322 drm.add_planar_framebuffer(&bo, flags).map(|fb| {
323 (
324 fb,
325 drm_fourcc::DrmFormat {
326 code: bo.format(),
327 modifier: modifier.unwrap_or(DrmModifier::Invalid),
328 },
329 )
330 })
331 };
332
333 let (fb, format) = match ret {
334 Ok(fb) => fb,
335 Err(source) => {
336 if !allow_legacy {
337 return Err(AccessError {
338 errmsg: "Failed to add framebuffer",
339 dev: drm.dev_path(),
340 source,
341 });
342 }
343
344 warn_legacy_fb_export();
346
347 if bo.plane_count() > 1 {
348 return Err(AccessError {
349 errmsg: "Failed to add framebuffer",
350 dev: drm.dev_path(),
351 source,
352 });
353 }
354
355 let fourcc = bo.format();
356 let (depth, bpp) = get_depth(fourcc)
357 .and_then(|d| get_bpp(fourcc).map(|b| (d, b)))
358 .ok_or_else(|| AccessError {
359 errmsg: "Unknown format for legacy framebuffer",
360 dev: drm.dev_path(),
361 source,
362 })?;
363
364 let fb = drm
365 .add_framebuffer(&*bo, depth as u32, bpp as u32)
366 .map_err(|source| AccessError {
367 errmsg: "Failed to add framebuffer",
368 dev: drm.dev_path(),
369 source,
370 })?;
371 (
372 fb,
373 drm_fourcc::DrmFormat {
374 code: fourcc,
375 modifier: drm_fourcc::DrmModifier::Invalid,
376 },
377 )
378 }
379 };
380 Ok((fb, format))
381}