smithay/backend/renderer/mod.rs
1//! Rendering functionality and abstractions
2//!
3//! Collection of common traits and implementations
4//! to facilitate (possible hardware-accelerated) rendering.
5//!
6//! Supported rendering apis:
7//!
8//! - Raw OpenGL ES 2
9
10use crate::utils::{ids::id_gen, Buffer as BufferCoord, Physical, Point, Rectangle, Scale, Size, Transform};
11use cgmath::Matrix3;
12use std::{
13 any::TypeId,
14 cmp::Ordering,
15 error::Error,
16 fmt,
17 hash::{Hash, Hasher},
18 marker::PhantomData,
19 sync::Arc,
20};
21
22#[cfg(feature = "wayland_frontend")]
23use crate::wayland::{compositor::SurfaceData, shm::fourcc_to_shm_format};
24#[cfg(feature = "wayland_frontend")]
25use wayland_server::protocol::{wl_buffer, wl_shm};
26
27#[cfg(feature = "renderer_gl")]
28pub mod gles;
29
30#[cfg(feature = "renderer_glow")]
31pub mod glow;
32
33#[cfg(feature = "renderer_pixman")]
34pub mod pixman;
35
36mod color;
37pub use color::Color32F;
38
39use crate::backend::allocator::{dmabuf::Dmabuf, Format, Fourcc};
40#[cfg(all(
41 feature = "wayland_frontend",
42 feature = "backend_egl",
43 feature = "use_system_lib"
44))]
45use crate::backend::egl::{
46 display::{EGLBufferReader, BUFFER_READER},
47 Error as EglError,
48};
49
50use super::allocator::format::FormatSet;
51
52#[cfg(feature = "renderer_multi")]
53pub mod multigpu;
54
55pub mod utils;
56
57pub mod element;
58
59pub mod damage;
60
61pub mod sync;
62
63// Note: This doesn't fully work yet due to <https://github.com/rust-lang/rust/issues/67295>.
64// Use `--features renderer_test` when running doc tests manually.
65#[cfg(any(feature = "renderer_test", test, doctest))]
66pub mod test;
67
68/// Identifies a renderer context for a specific texture type.
69///
70/// Renderers with the same `ContextId` are assumed to be texture-compatible,
71/// meaning textures created by one can be imported into another.
72pub struct ContextId<T: Texture>(Arc<InnerContextId>, PhantomData<fn() -> T>);
73
74/// A type-erased [`ContextId`] without the `Texture` generic.
75///
76/// This allows representing and comparing renderer contexts across different texture types.
77#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
78pub struct ErasedContextId(Arc<InnerContextId>, TypeId);
79
80impl<T: Texture> ContextId<T> {
81 /// Allocates a new `ContextId`.
82 pub fn new() -> Self {
83 ContextId(Arc::new(InnerContextId::new()), PhantomData)
84 }
85
86 /// Maps this `ContextId` to one with another `Texture` type.
87 ///
88 /// This is typically used by wrapper or derivative renderers that define a new `TextureId`
89 /// while reusing the underlying context for rendering.
90 pub fn map<Tex: Texture>(self) -> ContextId<Tex> {
91 ContextId(self.0, PhantomData)
92 }
93
94 /// Returns an [`ErasedContextId`] representing this context without the texture type.
95 ///
96 /// This is useful when storing or comparing contexts across different texture types.
97 pub fn erased(self) -> ErasedContextId
98 where
99 T: 'static,
100 {
101 ErasedContextId(self.0, TypeId::of::<T>())
102 }
103}
104
105impl<T: Texture> Default for ContextId<T> {
106 fn default() -> Self {
107 Self::new()
108 }
109}
110
111impl<T: Texture> fmt::Debug for ContextId<T> {
112 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113 f.debug_tuple("ContextId")
114 .field(&self.0)
115 .field(&format_args!("_"))
116 .finish()
117 }
118}
119
120impl<T: Texture> Clone for ContextId<T> {
121 fn clone(&self) -> Self {
122 ContextId(self.0.clone(), PhantomData)
123 }
124}
125
126impl<T: Texture> PartialEq for ContextId<T> {
127 fn eq(&self, other: &Self) -> bool {
128 self.0 == other.0
129 }
130}
131
132impl<T: Texture> Eq for ContextId<T> {}
133
134impl<T: Texture> PartialOrd for ContextId<T> {
135 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
136 Some(self.cmp(other))
137 }
138}
139
140impl<T: Texture> Ord for ContextId<T> {
141 fn cmp(&self, other: &Self) -> Ordering {
142 self.0.cmp(&other.0)
143 }
144}
145
146impl<T: Texture> Hash for ContextId<T> {
147 fn hash<H: Hasher>(&self, state: &mut H) {
148 self.0.hash(state);
149 }
150}
151
152id_gen!(context_id);
153
154#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
155struct InnerContextId(usize);
156
157impl InnerContextId {
158 fn new() -> Self {
159 Self(context_id::next())
160 }
161}
162
163impl Drop for InnerContextId {
164 fn drop(&mut self) {
165 context_id::remove(self.0);
166 }
167}
168
169#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
170/// Texture filtering methods
171pub enum TextureFilter {
172 /// Returns the value of the texture element that is nearest (in Manhattan distance) to the center of the pixel being textured.
173 Linear,
174 /// Returns the weighted average of the four texture elements that are closest to the center of the pixel being textured.
175 Nearest,
176}
177
178impl Transform {
179 /// A projection matrix to apply this transformation
180 #[inline]
181 pub fn matrix(&self) -> Matrix3<f32> {
182 match self {
183 Transform::Normal => Matrix3::new(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0),
184 Transform::_90 => Matrix3::new(0.0, -1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0),
185 Transform::_180 => Matrix3::new(-1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 1.0),
186 Transform::_270 => Matrix3::new(0.0, 1.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0),
187 Transform::Flipped => Matrix3::new(-1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0),
188 Transform::Flipped90 => Matrix3::new(0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0),
189 Transform::Flipped180 => Matrix3::new(1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 1.0),
190 Transform::Flipped270 => Matrix3::new(0.0, -1.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0),
191 }
192 }
193}
194
195#[cfg(feature = "wayland_frontend")]
196impl From<wayland_server::protocol::wl_output::Transform> for Transform {
197 #[inline]
198 fn from(transform: wayland_server::protocol::wl_output::Transform) -> Transform {
199 use wayland_server::protocol::wl_output::Transform as WlTransform;
200 match transform {
201 WlTransform::Normal => Transform::Normal,
202 WlTransform::_90 => Transform::_90,
203 WlTransform::_180 => Transform::_180,
204 WlTransform::_270 => Transform::_270,
205 WlTransform::Flipped => Transform::Flipped,
206 WlTransform::Flipped90 => Transform::Flipped90,
207 WlTransform::Flipped180 => Transform::Flipped180,
208 WlTransform::Flipped270 => Transform::Flipped270,
209 _ => Transform::Normal,
210 }
211 }
212}
213
214/// Abstraction for Renderers, that can render into different targets
215pub trait Bind<Target>: Renderer {
216 /// Initialize a framebuffer with a given rendering target.
217 ///
218 /// This function *may* error, if:
219 /// - The specific given target handle is incompatible with the underlying rendering api
220 ///
221 /// **Note**: Some renderers might only be able to determine if a handle is compatible
222 /// during a `Renderer::render` call with the resulting `Framebuffer`.
223 fn bind<'a>(&mut self, target: &'a mut Target) -> Result<Self::Framebuffer<'a>, Self::Error>;
224
225 /// Supported pixel formats for given targets, if applicable.
226 fn supported_formats(&self) -> Option<FormatSet> {
227 None
228 }
229}
230
231/// A two dimensional texture
232pub trait Texture: fmt::Debug {
233 /// Size of the texture plane
234 fn size(&self) -> Size<i32, BufferCoord> {
235 Size::from((self.width() as i32, self.height() as i32))
236 }
237
238 /// Width of the texture plane
239 fn width(&self) -> u32;
240 /// Height of the texture plane
241 fn height(&self) -> u32;
242
243 /// Format of the texture, if available.
244 ///
245 /// In case the format is hidden by the implementation,
246 /// it should be assumed, that the pixel representation cannot be read.
247 ///
248 /// Thus [`ExportMem::copy_texture`], if implemented, will not succeed for this texture.
249 /// Note that this does **not** mean every texture with a format is guaranteed to be copyable.
250 fn format(&self) -> Option<Fourcc>;
251}
252
253/// A downloaded texture buffer
254pub trait TextureMapping: Texture {
255 /// Returns if the mapped buffer is flipped on the y-axis
256 /// (compared to the lower left being (0, 0))
257 fn flipped(&self) -> bool;
258
259 /// Format of the texture
260 fn format(&self) -> Fourcc {
261 Texture::format(self).expect("Texture Mappings need to have a format")
262 }
263}
264
265/// Helper trait for [`Renderer`], which defines a rendering api for a currently in-progress frame during [`Renderer::render`].
266///
267/// Dropping the [`Frame`] or explicitly calling [`Frame::finish`] will free any unused resources. If you need explicit control
268/// over resource clean-up take a look at [`Renderer::cleanup_texture_cache`].
269pub trait Frame {
270 /// Error type returned by the rendering operations of this renderer.
271 type Error: Error;
272 /// Texture Handle type used by this renderer.
273 type TextureId: Texture;
274
275 /// Returns the [`ContextId`] of the associated renderer.
276 fn context_id(&self) -> ContextId<Self::TextureId>;
277
278 /// Clear the complete current target with a single given color.
279 ///
280 /// The `at` parameter specifies a set of rectangles to clear in the current target. This allows partially
281 /// clearing the target which may be useful for damaged rendering.
282 ///
283 /// This operation is only valid in between a `begin` and `finish`-call.
284 /// If called outside this operation may error-out, do nothing or modify future rendering results in any way.
285 fn clear(&mut self, color: Color32F, at: &[Rectangle<i32, Physical>]) -> Result<(), Self::Error>;
286
287 /// Draw a solid color to the current target at the specified destination with the specified color.
288 fn draw_solid(
289 &mut self,
290 dst: Rectangle<i32, Physical>,
291 damage: &[Rectangle<i32, Physical>],
292 color: Color32F,
293 ) -> Result<(), Self::Error>;
294
295 /// Render a texture to the current target as a flat 2d-plane at a given
296 /// position and applying the given transformation with the given alpha value.
297 /// (Meaning `src_transform` should match the orientation of surface being rendered).
298 #[allow(clippy::too_many_arguments)]
299 fn render_texture_at(
300 &mut self,
301 texture: &Self::TextureId,
302 pos: Point<i32, Physical>,
303 texture_scale: i32,
304 output_scale: impl Into<Scale<f64>>,
305 src_transform: Transform,
306 damage: &[Rectangle<i32, Physical>],
307 opaque_regions: &[Rectangle<i32, Physical>],
308 alpha: f32,
309 ) -> Result<(), Self::Error> {
310 self.render_texture_from_to(
311 texture,
312 Rectangle::from_size(texture.size()).to_f64(),
313 Rectangle::new(
314 pos,
315 texture
316 .size()
317 .to_logical(texture_scale, src_transform)
318 .to_physical_precise_round(output_scale),
319 ),
320 damage,
321 opaque_regions,
322 src_transform,
323 alpha,
324 )
325 }
326
327 /// Render part of a texture as given by src to the current target into the rectangle described by dst
328 /// as a flat 2d-plane after applying the inverse of the given transformation.
329 /// (Meaning `src_transform` should match the orientation of surface being rendered).
330 #[allow(clippy::too_many_arguments)]
331 fn render_texture_from_to(
332 &mut self,
333 texture: &Self::TextureId,
334 src: Rectangle<f64, BufferCoord>,
335 dst: Rectangle<i32, Physical>,
336 damage: &[Rectangle<i32, Physical>],
337 opaque_regions: &[Rectangle<i32, Physical>],
338 src_transform: Transform,
339 alpha: f32,
340 ) -> Result<(), Self::Error>;
341
342 /// Output transformation that is applied to this frame
343 fn transformation(&self) -> Transform;
344
345 /// Wait for a [`SyncPoint`](sync::SyncPoint) to be signaled
346 fn wait(&mut self, sync: &sync::SyncPoint) -> Result<(), Self::Error>;
347
348 /// Finish this [`Frame`] returning any error that may happen during any cleanup.
349 ///
350 /// Dropping the frame instead may result in any of the following and is implementation dependent:
351 /// - All actions done to the frame vanish and are never executed
352 /// - A partial renderer with undefined framebuffer contents occurs
353 /// - All actions are performed as normal without errors being returned.
354 ///
355 /// Leaking the frame instead will leak resources and can cause any of the previous effects.
356 /// Leaking might make the renderer return Errors and force it's recreation.
357 /// Leaking may not cause otherwise undefined behavior and program execution will always continue normally.
358 fn finish(self) -> Result<sync::SyncPoint, Self::Error>;
359}
360
361bitflags::bitflags! {
362 /// Debug flags that can be enabled at runtime
363 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
364 pub struct DebugFlags: u32 {
365 /// Tint all rendered textures
366 const TINT = 0b00000001;
367 }
368}
369
370/// Workaround for <https://github.com/rust-lang/rust/issues/87479>, please look at [`Renderer`] instead.
371pub trait RendererSuper: fmt::Debug {
372 /// Error type returned by the rendering operations of this renderer.
373 type Error: Error;
374 /// Texture Handle type used by this renderer.
375 type TextureId: Texture;
376 /// Framebuffer to draw onto
377 type Framebuffer<'buffer>: Texture;
378 /// Type representing a currently in-progress frame during the [`Renderer::render`]-call
379 type Frame<'frame, 'buffer>: Frame<Error = Self::Error, TextureId = Self::TextureId>
380 where
381 'buffer: 'frame,
382 Self: 'frame;
383}
384
385/// Abstraction of commonly used rendering operations for compositors.
386///
387/// *Note*: Associated types are defined in [`RendererSuper`].
388pub trait Renderer: RendererSuper {
389 /// Returns the [`ContextId`] of this renderer
390 ///
391 /// See [`ContextId`] for more details.
392 fn context_id(&self) -> ContextId<Self::TextureId>;
393
394 /// Set the filter method to be used when rendering a texture into a smaller area than its size
395 fn downscale_filter(&mut self, filter: TextureFilter) -> Result<(), Self::Error>;
396 /// Set the filter method to be used when rendering a texture into a larger area than its size
397 fn upscale_filter(&mut self, filter: TextureFilter) -> Result<(), Self::Error>;
398
399 /// Set the enabled [`DebugFlags`]
400 fn set_debug_flags(&mut self, flags: DebugFlags);
401 /// Returns the current enabled [`DebugFlags`]
402 fn debug_flags(&self) -> DebugFlags;
403
404 /// Initialize a rendering context on the provided framebuffer with given dimensions and transformation.
405 ///
406 /// The `output_size` specifies the dimensions of the display **before** the `dst_transform` is
407 /// applied.
408 ///
409 /// This function *may* error, if:
410 /// - The given dimensions are unsupported (too large) for this renderer
411 /// - The given Transformation is not supported by the renderer (`Transform::Normal` is always supported).
412 /// - The underlying object of the given framebuffer is incompatible with this particular render instance.
413 fn render<'frame, 'buffer>(
414 &'frame mut self,
415 framebuffer: &'frame mut Self::Framebuffer<'buffer>,
416 output_size: Size<i32, Physical>,
417 dst_transform: Transform,
418 ) -> Result<Self::Frame<'frame, 'buffer>, Self::Error>
419 where
420 'buffer: 'frame;
421
422 /// Wait for a [`SyncPoint`](sync::SyncPoint) to be signaled
423 fn wait(&mut self, sync: &sync::SyncPoint) -> Result<(), Self::Error>;
424
425 /// Forcibly clean up the renderer internal texture cache
426 ///
427 /// Note: Resources used by the renderer will be implicitly cleaned-up after finishing
428 /// a [`Frame`] by either dropping the [`Frame`] or explicitly calling [`Frame::finish`].
429 /// This call can be used to clean-up resources in cases where either no [`Frame`] is used
430 /// at all to prevent resource pile-up or in case of only infrequent access to lower
431 /// system resource usage.
432 fn cleanup_texture_cache(&mut self) -> Result<(), Self::Error> {
433 Ok(())
434 }
435}
436
437/// Trait for renderers that support creating offscreen framebuffers to render into.
438///
439/// Usually also implement [`ExportMem`] to receive the framebuffers contents.
440pub trait Offscreen<Target>: Renderer + Bind<Target> {
441 /// Create a new instance of a framebuffer.
442 ///
443 /// This call *may* fail, if (but not limited to):
444 /// - The maximum amount of framebuffers for this renderer would be exceeded
445 /// - The format is not supported to be rendered into
446 /// - The size is too large for a framebuffer
447 fn create_buffer(&mut self, format: Fourcc, size: Size<i32, BufferCoord>) -> Result<Target, Self::Error>;
448}
449
450/// Trait for Renderers supporting importing wl_buffers using shared memory.
451#[cfg(feature = "wayland_frontend")]
452pub trait ImportMemWl: ImportMem {
453 /// Import a given shm-based buffer into the renderer (see [`buffer_type`]).
454 ///
455 /// Returns a texture_id, which can be used with [`Frame::render_texture_from_to`] (or [`Frame::render_texture_at`])
456 /// or implementation-specific functions.
457 ///
458 /// If not otherwise defined by the implementation, this texture id is only valid for the renderer, that created it.
459 /// This operation needs no bound or default rendering target.
460 ///
461 /// The implementation defines, if the id keeps being valid, if the buffer is released,
462 /// to avoid relying on implementation details, keep the buffer alive, until you destroyed this texture again.
463 ///
464 /// If provided the `SurfaceAttributes` can be used to do caching of rendering resources and is generally recommended.
465 ///
466 /// The `damage` argument provides a list of rectangle locating parts of the buffer that need to be updated. When provided
467 /// with an empty list `&[]`, the renderer is allowed to not update the texture at all.
468 fn import_shm_buffer(
469 &mut self,
470 buffer: &wl_buffer::WlBuffer,
471 surface: Option<&crate::wayland::compositor::SurfaceData>,
472 damage: &[Rectangle<i32, BufferCoord>],
473 ) -> Result<Self::TextureId, Self::Error>;
474
475 /// Returns supported formats for shared memory buffers.
476 ///
477 /// Will always contain At least `Argb8888` and `Xrgb8888`.
478 fn shm_formats(&self) -> Box<dyn Iterator<Item = wl_shm::Format>> {
479 Box::new(self.mem_formats().flat_map(fourcc_to_shm_format))
480 }
481}
482
483/// Trait for Renderers supporting importing bitmaps from memory.
484pub trait ImportMem: Renderer {
485 /// Import a given chunk of memory into the renderer.
486 ///
487 /// Returns a texture_id, which can be used with [`Frame::render_texture_from_to`] (or [`Frame::render_texture_at`])
488 /// or implementation-specific functions.
489 ///
490 /// If not otherwise defined by the implementation, this texture id is only valid for the renderer, that created it.
491 /// This operation needs no bound or default rendering target.
492 ///
493 /// Settings flipped to true will cause the buffer to be interpreted like the y-axis is flipped
494 /// (opposed to the lower left begin (0, 0)).
495 /// This is a texture specific property, so future uploads to the same texture via [`ImportMem::update_memory`]
496 /// will also be interpreted as flipped.
497 ///
498 /// The provided data slice needs to be in a format supported as indicated by [`ImportMem::mem_formats`].
499 /// Its length should thus be `size.w * size.h * bits_per_pixel`.
500 /// Anything beyond will be truncated, if the buffer is too small an error will be returned.
501 fn import_memory(
502 &mut self,
503 data: &[u8],
504 format: Fourcc,
505 size: Size<i32, BufferCoord>,
506 flipped: bool,
507 ) -> Result<Self::TextureId, Self::Error>;
508
509 /// Update a portion of a given chunk of memory into an existing texture.
510 ///
511 /// This operation needs no bound or default rendering target.
512 ///
513 /// The provided data slice needs to be in the same format used to create the texture and the same size of the texture.
514 /// Its length should this be `texture.size().w * texture.size().h * bits_per_pixel`.
515 /// Anything beyond will be ignored, if the buffer is too small an error will be returned.
516 ///
517 /// This function *may* error, if (but not limited to):
518 /// - The texture was not created using either [`ImportMemWl::import_shm_buffer`] or [`ImportMem::import_memory`].
519 /// External textures imported by other means (e.g. via ImportDma) may not be writable. This property is defined
520 /// by the implementation.
521 /// - The region is out of bounds of the initial size the texture was created with. Implementations are not required
522 /// to support resizing the original texture.
523 fn update_memory(
524 &mut self,
525 texture: &Self::TextureId,
526 data: &[u8],
527 region: Rectangle<i32, BufferCoord>,
528 ) -> Result<(), Self::Error>;
529
530 /// Returns supported formats for memory imports.
531 fn mem_formats(&self) -> Box<dyn Iterator<Item = Fourcc>>;
532}
533
534#[cfg(all(
535 feature = "wayland_frontend",
536 feature = "backend_egl",
537 feature = "use_system_lib"
538))]
539/// Trait for Renderers supporting importing wl_drm-based buffers.
540pub trait ImportEgl: Renderer {
541 /// Binds the underlying EGL display to the given Wayland display.
542 ///
543 /// This will allow clients to utilize EGL to create hardware-accelerated
544 /// surfaces. This renderer will thus be able to handle wl_drm-based buffers.
545 ///
546 /// ## Errors
547 ///
548 /// This might return [`EglExtensionNotSupported`](super::egl::Error::EglExtensionNotSupported)
549 /// if binding is not supported by the EGL implementation.
550 ///
551 /// This might return [`OtherEGLDisplayAlreadyBound`](super::egl::Error::OtherEGLDisplayAlreadyBound)
552 /// if called for the same [`Display`](wayland_server::Display) multiple times, as only one egl
553 /// display may be bound at any given time.
554 fn bind_wl_display(&mut self, display: &wayland_server::DisplayHandle) -> Result<(), EglError>;
555
556 /// Unbinds a previously bound egl display, if existing.
557 ///
558 /// *Note*: As a result any previously created egl-based WlBuffers will not be readable anymore.
559 /// Your compositor will have to deal with existing buffers of *unknown* type.
560 fn unbind_wl_display(&mut self);
561
562 /// Returns the underlying [`EGLBufferReader`].
563 ///
564 /// The primary use for this is calling [`buffer_dimensions`] or [`buffer_type`].
565 ///
566 /// Returns `None` if no [`Display`](wayland_server::Display) was previously bound to the underlying
567 /// [`EGLDisplay`](super::egl::EGLDisplay) (see [`ImportEgl::bind_wl_display`]).
568 fn egl_reader(&self) -> Option<&EGLBufferReader>;
569
570 /// Import a given wl_drm-based buffer into the renderer (see [`buffer_type`]).
571 ///
572 /// Returns a texture_id, which can be used with [`Frame::render_texture_from_to`] (or [`Frame::render_texture_at`])
573 /// or implementation-specific functions.
574 ///
575 /// If not otherwise defined by the implementation, this texture id is only valid for the renderer, that created it.
576 ///
577 /// This operation needs no bound or default rendering target.
578 ///
579 /// The implementation defines, if the id keeps being valid, if the buffer is released,
580 /// to avoid relying on implementation details, keep the buffer alive, until you destroyed this texture again.
581 fn import_egl_buffer(
582 &mut self,
583 buffer: &wl_buffer::WlBuffer,
584 surface: Option<&crate::wayland::compositor::SurfaceData>,
585 damage: &[Rectangle<i32, BufferCoord>],
586 ) -> Result<Self::TextureId, Self::Error>;
587}
588
589#[cfg(feature = "wayland_frontend")]
590/// Trait for Renderers supporting importing dmabuf-based wl_buffers
591pub trait ImportDmaWl: ImportDma {
592 /// Import a given dmabuf-based buffer into the renderer (see [`buffer_type`]).
593 ///
594 /// Returns a texture_id, which can be used with [`Frame::render_texture_from_to`] (or [`Frame::render_texture_at`])
595 /// or implementation-specific functions.
596 ///
597 /// If not otherwise defined by the implementation, this texture id is only valid for the renderer, that created it.
598 ///
599 /// This operation needs no bound or default rendering target.
600 ///
601 /// The implementation defines, if the id keeps being valid, if the buffer is released,
602 /// to avoid relying on implementation details, keep the buffer alive, until you destroyed this texture again.
603 fn import_dma_buffer(
604 &mut self,
605 buffer: &wl_buffer::WlBuffer,
606 _surface: Option<&crate::wayland::compositor::SurfaceData>,
607 damage: &[Rectangle<i32, BufferCoord>],
608 ) -> Result<Self::TextureId, Self::Error> {
609 let dmabuf = crate::wayland::dmabuf::get_dmabuf(buffer)
610 .expect("import_dma_buffer without checking buffer type?");
611 self.import_dmabuf(dmabuf, Some(damage))
612 }
613}
614
615/// Trait for Renderers supporting importing dmabufs.
616pub trait ImportDma: Renderer {
617 /// Returns supported formats for dmabufs.
618 fn dmabuf_formats(&self) -> FormatSet {
619 FormatSet::default()
620 }
621
622 /// Test if a specific dmabuf [`Format`] is supported
623 fn has_dmabuf_format(&self, format: Format) -> bool {
624 self.dmabuf_formats().contains(&format)
625 }
626
627 /// Import a given raw dmabuf into the renderer.
628 ///
629 /// Returns a texture_id, which can be used with [`Frame::render_texture_from_to`] (or [`Frame::render_texture_at`])
630 /// or implementation-specific functions.
631 ///
632 /// If not otherwise defined by the implementation, this texture id is only valid for the renderer, that created it.
633 ///
634 /// This operation needs no bound or default rendering target.
635 ///
636 /// The implementation defines, if the id keeps being valid, if the buffer is released,
637 /// to avoid relying on implementation details, keep the buffer alive, until you destroyed this texture again.
638 fn import_dmabuf(
639 &mut self,
640 dmabuf: &Dmabuf,
641 damage: Option<&[Rectangle<i32, BufferCoord>]>,
642 ) -> Result<Self::TextureId, Self::Error>;
643}
644
645// TODO: Replace this with a trait_alias, once that is stabilized.
646// pub type ImportAll = Renderer + ImportShm + ImportEgl;
647
648/// Common trait for renderers of any wayland buffer type
649#[cfg(feature = "wayland_frontend")]
650pub trait ImportAll: Renderer {
651 /// Import a given buffer into the renderer.
652 ///
653 /// Returns a texture_id, which can be used with [`Frame::render_texture_from_to`] (or [`Frame::render_texture_at`])
654 /// or implementation-specific functions.
655 ///
656 /// If not otherwise defined by the implementation, this texture id is only valid for the renderer, that created it.
657 ///
658 /// This operation needs no bound or default rendering target.
659 ///
660 /// The implementation defines, if the id keeps being valid, if the buffer is released,
661 /// to avoid relying on implementation details, keep the buffer alive, until you destroyed this texture again.
662 ///
663 /// If provided the `SurfaceAttributes` can be used to do caching of rendering resources and is generally recommended.
664 ///
665 /// The `damage` argument provides a list of rectangle locating parts of the buffer that need to be updated. When provided
666 /// with an empty list `&[]`, the renderer is allowed to not update the texture at all.
667 ///
668 /// Returns `None`, if the buffer type cannot be determined or does not correspond to a texture (e.g.: single pixel buffer).
669 fn import_buffer(
670 &mut self,
671 buffer: &wl_buffer::WlBuffer,
672 surface: Option<&crate::wayland::compositor::SurfaceData>,
673 damage: &[Rectangle<i32, BufferCoord>],
674 ) -> Option<Result<Self::TextureId, Self::Error>>;
675}
676
677// TODO: Do this with specialization, when possible and do default implementations
678#[cfg(all(
679 feature = "wayland_frontend",
680 feature = "backend_egl",
681 feature = "use_system_lib"
682))]
683impl<R: Renderer + ImportMemWl + ImportEgl + ImportDmaWl> ImportAll for R {
684 #[profiling::function]
685 fn import_buffer(
686 &mut self,
687 buffer: &wl_buffer::WlBuffer,
688 surface: Option<&SurfaceData>,
689 damage: &[Rectangle<i32, BufferCoord>],
690 ) -> Option<Result<Self::TextureId, Self::Error>> {
691 match buffer_type(buffer) {
692 Some(BufferType::Shm) => Some(self.import_shm_buffer(buffer, surface, damage)),
693 Some(BufferType::Egl) => Some(self.import_egl_buffer(buffer, surface, damage)),
694 Some(BufferType::Dma) => Some(self.import_dma_buffer(buffer, surface, damage)),
695 _ => None,
696 }
697 }
698}
699
700#[cfg(all(
701 feature = "wayland_frontend",
702 not(all(feature = "backend_egl", feature = "use_system_lib"))
703))]
704impl<R: Renderer + ImportMemWl + ImportDmaWl> ImportAll for R {
705 fn import_buffer(
706 &mut self,
707 buffer: &wl_buffer::WlBuffer,
708 surface: Option<&SurfaceData>,
709 damage: &[Rectangle<i32, BufferCoord>],
710 ) -> Option<Result<Self::TextureId, Self::Error>> {
711 match buffer_type(buffer) {
712 Some(BufferType::Shm) => Some(self.import_shm_buffer(buffer, surface, damage)),
713 Some(BufferType::Dma) => Some(self.import_dma_buffer(buffer, surface, damage)),
714 _ => None,
715 }
716 }
717}
718
719/// Trait for renderers supporting exporting contents of framebuffers or textures into memory.
720pub trait ExportMem: Renderer {
721 /// Texture type representing a downloaded pixel buffer.
722 type TextureMapping: TextureMapping;
723
724 /// Copies the contents of the provided target.
725 ///
726 /// This operation is not destructive, the contents of the framebuffer keep being valid.
727 ///
728 /// This function *may* fail, if (but not limited to):
729 /// - The framebuffer is not readable
730 /// - The region is out of bounds of the framebuffer
731 /// - There is not enough space to create the mapping
732 /// - It is not possible to convert the framebuffer into the provided format.
733 fn copy_framebuffer(
734 &mut self,
735 target: &Self::Framebuffer<'_>,
736 region: Rectangle<i32, BufferCoord>,
737 format: Fourcc,
738 ) -> Result<Self::TextureMapping, Self::Error>;
739
740 /// Copies the contents of the passed texture.
741 /// *Note*: This function may change or invalidate the current bind.
742 ///
743 /// Renderers are not required to support any format other than what was returned by `Texture::format`.
744 /// This operation is not destructive, the contents of the texture keep being valid.
745 ///
746 /// This function *may* fail, if:
747 /// - There is not enough space to create the mapping
748 /// - The texture does no allow copying for implementation-specfic reasons
749 /// - It is not possible to convert the texture into the provided format.
750 fn copy_texture(
751 &mut self,
752 texture: &Self::TextureId,
753 region: Rectangle<i32, BufferCoord>,
754 format: Fourcc,
755 ) -> Result<Self::TextureMapping, Self::Error>;
756
757 /// Returns whether the renderer should be able to read-back from the given texture.
758 ///
759 /// No actual copying shall be performed by this function nor is a format specified,
760 /// so it is still legal for [`ExportMem::copy_texture`] to return an error, if this
761 /// method returns `true`.
762 ///
763 /// This function *may* fail, if:
764 /// - A readability test did successfully complete (not that it returned `unreadble`!)
765 /// - Any of the state of the renderer is irrevesibly changed
766 fn can_read_texture(&mut self, texture: &Self::TextureId) -> Result<bool, Self::Error>;
767
768 /// Returns a read-only pointer to a previously created texture mapping.
769 ///
770 /// The format of the returned slice is given by [`Texture::format`] of the texture mapping.
771 ///
772 /// This function *may* fail, if (but not limited to):
773 /// - There is not enough space in memory
774 fn map_texture<'a>(&mut self, texture_mapping: &'a Self::TextureMapping)
775 -> Result<&'a [u8], Self::Error>;
776}
777
778/// Trait for renderers supporting blitting contents from one framebuffer to another.
779// We would like to require the following. But we can't because of <https://github.com/rust-lang/rust/issues/100013>.
780// for<'frame, 'buffer> Self::Frame<'frame, 'buffer>: BlitFrame<Self::Framebuffer<'buffer>>,
781pub trait Blit
782where
783 Self: Renderer,
784{
785 /// Copies the contents of `src` from one provided target to `dst` in the other provided target,
786 /// applying `filter` if necessary.
787 ///
788 /// This operation is non destructive, the contents of the source framebuffer
789 /// are kept intact as is any region not in `dst` for the target framebuffer.
790 ///
791 /// This function *may* fail, if (but not limited to):
792 /// - The source framebuffer is not readable
793 /// - The destination framebuffer is not writable
794 /// - `src` is out of bounds for the source framebuffer
795 /// - `dst` is out of bounds for the destination framebuffer
796 /// - `src` and `dst` sizes are different and interpolation is not supported by this renderer.
797 /// - source and target framebuffer are the same
798 fn blit(
799 &mut self,
800 from: &Self::Framebuffer<'_>,
801 to: &mut Self::Framebuffer<'_>,
802 src: Rectangle<i32, Physical>,
803 dst: Rectangle<i32, Physical>,
804 filter: TextureFilter,
805 ) -> Result<(), Self::Error>;
806}
807
808/// Trait for frames supporting blitting contents from/to the current framebuffer to/from another.
809pub trait BlitFrame<Framebuffer>
810where
811 Self: Frame,
812{
813 /// Copies the contents of the bound framebuffer to `dst` in the provided framebuffer,
814 /// applying `filter` if necessary.
815 ///
816 /// This operation is non destructive, the contents of the current framebuffer
817 /// are kept intact as is any region not in `dst` for the target framebuffer.
818 ///
819 /// This function *may* fail, if (but not limited to):
820 /// - The bound framebuffer is not readable
821 /// - The destination framebuffer is not writable
822 /// - `src` is out of bounds for the bound framebuffer
823 /// - `dst` is out of bounds for the destination framebuffer
824 /// - `src` and `dst` sizes are different and interpolation is not supported by this renderer.
825 /// - bound and target framebuffer are the same
826 fn blit_to(
827 &mut self,
828 to: &mut Framebuffer,
829 src: Rectangle<i32, Physical>,
830 dst: Rectangle<i32, Physical>,
831 filter: TextureFilter,
832 ) -> Result<(), Self::Error>;
833
834 /// Copies the contents of the provided framebuffer to `dst` in the bound framebuffer,
835 /// applying `filter` if necessary.
836 ///
837 /// This operation is non destructive, the contents of the source framebuffer
838 /// are kept intact as is any region not in `dst` for the bound framebuffer.
839 ///
840 /// This function *may* fail, if (but not limited to):
841 /// - The source framebuffer is not readable
842 /// - The bound framebuffer is not writable
843 /// - `src` is out of bounds for the source framebuffer
844 /// - `dst` is out of bounds for the bound framebuffer
845 /// - `src` and `dst` sizes are different and interpolation is not supported by this renderer.
846 /// - source and bound framebuffer are the same
847 fn blit_from(
848 &mut self,
849 from: &Framebuffer,
850 src: Rectangle<i32, Physical>,
851 dst: Rectangle<i32, Physical>,
852 filter: TextureFilter,
853 ) -> Result<(), Self::Error>;
854}
855
856#[cfg(feature = "wayland_frontend")]
857#[non_exhaustive]
858/// Buffer type of a given wl_buffer, if managed by smithay
859#[derive(Debug)]
860pub enum BufferType {
861 /// Buffer is managed by the [`crate::wayland::shm`] global
862 Shm,
863 #[cfg(all(feature = "backend_egl", feature = "use_system_lib"))]
864 /// Buffer is managed by a currently initialized [`crate::backend::egl::display::EGLBufferReader`]
865 Egl,
866 /// Buffer is managed by the [`crate::wayland::dmabuf`] global
867 Dma,
868 /// Buffer represents a singe pixel
869 SinglePixel,
870}
871
872/// Returns the *type* of a wl_buffer
873///
874/// Returns `None` if the type is not known to smithay
875/// or otherwise not supported (e.g. not initialized using one of smithays [`crate::wayland`]-handlers).
876#[cfg(feature = "wayland_frontend")]
877pub fn buffer_type(buffer: &wl_buffer::WlBuffer) -> Option<BufferType> {
878 use crate::wayland::shm::BufferAccessError;
879
880 if crate::wayland::dmabuf::get_dmabuf(buffer).is_ok() {
881 return Some(BufferType::Dma);
882 }
883
884 if !matches!(
885 crate::wayland::shm::with_buffer_contents(buffer, |_, _, _| ()),
886 Err(BufferAccessError::NotManaged)
887 ) {
888 return Some(BufferType::Shm);
889 }
890
891 if crate::wayland::single_pixel_buffer::get_single_pixel_buffer(buffer).is_ok() {
892 return Some(BufferType::SinglePixel);
893 }
894
895 // Not managed, check if this is an EGLBuffer
896 #[cfg(all(feature = "backend_egl", feature = "use_system_lib"))]
897 if BUFFER_READER
898 .lock()
899 .unwrap()
900 .as_ref()
901 .and_then(|x| x.upgrade())
902 .and_then(|x| x.egl_buffer_dimensions(buffer))
903 .is_some()
904 {
905 return Some(BufferType::Egl);
906 }
907
908 None
909}
910
911/// Returns if the buffer has an alpha channel
912///
913/// Returns `None` if the type is not known to smithay
914/// or otherwise not supported (e.g. not initialized using one of smithays [`crate::wayland`]-handlers).
915///
916/// Note: This is on a best-effort, but will never return false for a buffer
917/// with a format that supports alpha.
918#[cfg(feature = "wayland_frontend")]
919pub fn buffer_has_alpha(buffer: &wl_buffer::WlBuffer) -> Option<bool> {
920 use super::allocator::format::has_alpha;
921 use crate::wayland::shm::shm_format_to_fourcc;
922
923 if let Ok(dmabuf) = crate::wayland::dmabuf::get_dmabuf(buffer) {
924 return Some(crate::backend::allocator::format::has_alpha(dmabuf.0.format));
925 }
926
927 if let Ok(has_alpha) = crate::wayland::shm::with_buffer_contents(buffer, |_, _, data| {
928 shm_format_to_fourcc(data.format).is_some_and(has_alpha)
929 }) {
930 return Some(has_alpha);
931 }
932
933 if let Ok(spb) = crate::wayland::single_pixel_buffer::get_single_pixel_buffer(buffer) {
934 return Some(spb.has_alpha());
935 }
936
937 // Not managed, check if this is an EGLBuffer
938 #[cfg(all(feature = "backend_egl", feature = "use_system_lib"))]
939 if let Some(format) = BUFFER_READER
940 .lock()
941 .unwrap()
942 .as_ref()
943 .and_then(|x| x.upgrade())
944 .and_then(|x| x.egl_buffer_contents(buffer).ok())
945 .map(|b| b.format)
946 {
947 return Some(crate::backend::egl::display::EGLBufferReader::egl_buffer_has_alpha(format));
948 }
949
950 None
951}
952
953/// Returns the dimensions of a wl_buffer
954///
955/// *Note*: This will only return dimensions for buffer types known to smithay (see [`buffer_type`])
956#[cfg(feature = "wayland_frontend")]
957pub fn buffer_dimensions(buffer: &wl_buffer::WlBuffer) -> Option<Size<i32, BufferCoord>> {
958 use crate::{
959 backend::allocator::Buffer,
960 wayland::shm::{self, BufferAccessError},
961 };
962
963 if let Ok(buf) = crate::wayland::dmabuf::get_dmabuf(buffer) {
964 return Some((buf.width() as i32, buf.height() as i32).into());
965 }
966
967 if crate::wayland::single_pixel_buffer::get_single_pixel_buffer(buffer).is_ok() {
968 return Some(Size::from((1, 1)));
969 }
970
971 match shm::with_buffer_contents(buffer, |_, _, data| (data.width, data.height).into()) {
972 Ok(data) => Some(data),
973
974 Err(BufferAccessError::NotManaged) => {
975 // Not managed, check if this is an EGLBuffer
976 #[cfg(all(feature = "backend_egl", feature = "use_system_lib"))]
977 if let Some(dim) = BUFFER_READER
978 .lock()
979 .unwrap()
980 .as_ref()
981 .and_then(|x| x.upgrade())
982 .and_then(|x| x.egl_buffer_dimensions(buffer))
983 {
984 return Some(dim);
985 }
986
987 None
988 }
989
990 Err(_) => None,
991 }
992}
993
994/// Returns if the underlying buffer is y-inverted
995///
996/// *Note*: This will only return y-inverted for buffer types known to smithay (see [`buffer_type`])
997#[cfg(feature = "wayland_frontend")]
998#[profiling::function]
999pub fn buffer_y_inverted(buffer: &wl_buffer::WlBuffer) -> Option<bool> {
1000 if let Ok(dmabuf) = crate::wayland::dmabuf::get_dmabuf(buffer) {
1001 return Some(dmabuf.y_inverted());
1002 }
1003
1004 #[cfg(all(feature = "backend_egl", feature = "use_system_lib"))]
1005 if let Some(Ok(egl_buffer)) = BUFFER_READER
1006 .lock()
1007 .unwrap()
1008 .as_ref()
1009 .and_then(|x| x.upgrade())
1010 .map(|x| x.egl_buffer_contents(buffer))
1011 {
1012 return Some(egl_buffer.y_inverted);
1013 }
1014
1015 None
1016}