smithay/backend/renderer/utils/
wayland.rs

1#[cfg(feature = "backend_drm")]
2use crate::wayland::drm_syncobj::{DrmSyncPoint, DrmSyncobjCachedState};
3use crate::{
4    backend::renderer::{
5        ContextId, ErasedContextId, ImportAll, Renderer, Texture, buffer_dimensions, buffer_has_alpha,
6        element::RenderElement,
7    },
8    utils::{Buffer as BufferCoord, Coordinate, Logical, Physical, Point, Rectangle, Scale, Size, Transform},
9    wayland::{
10        compositor::{
11            self, BufferAssignment, Damage, RectangleKind, SUBSURFACE_ROLE, SubsurfaceCachedState,
12            SurfaceAttributes, SurfaceData, TraversalAction, add_destruction_hook, is_sync_subsurface,
13            with_surface_tree_downward, with_surface_tree_upward,
14        },
15        viewporter,
16    },
17};
18
19use std::{
20    any::Any,
21    collections::{HashMap, hash_map::Entry},
22    sync::{Arc, Mutex},
23};
24
25use super::{CommitCounter, DamageBag, DamageSet, DamageSnapshot, SurfaceView};
26use tracing::{error, instrument, warn};
27use wayland_server::protocol::{wl_buffer::WlBuffer, wl_surface::WlSurface};
28
29/// Type stored in WlSurface states data_map
30///
31/// ```rs
32/// compositor::with_states(surface, |states| {
33///     let data = states.data_map.get::<RendererSurfaceStateUserData>();
34/// });
35/// ```
36pub type RendererSurfaceStateUserData = Mutex<RendererSurfaceState>;
37
38/// Surface state for rendering related data
39#[derive(Default, Debug)]
40pub struct RendererSurfaceState {
41    pub(crate) buffer_dimensions: Option<Size<i32, BufferCoord>>,
42    pub(crate) buffer_scale: i32,
43    pub(crate) buffer_transform: Transform,
44    pub(crate) buffer_has_alpha: Option<bool>,
45    pub(crate) buffer: Option<Buffer>,
46    pub(crate) damage: DamageBag<i32, BufferCoord>,
47    pub(crate) renderer_seen: HashMap<ErasedContextId, CommitCounter>,
48    pub(crate) textures: HashMap<ErasedContextId, Box<dyn Any>>,
49    pub(crate) surface_view: Option<SurfaceView>,
50    pub(crate) opaque_regions: Vec<Rectangle<i32, Logical>>,
51}
52
53/// SAFETY: Only thing unsafe here is the `Box<dyn Any>`, which are the textures.
54/// Those are guarded by our Renderers handling thread-safety and the `ContextId`.
55/// Theoretically a renderer could be thread-safe, but its texture type isn't, but that is **very** theoretical.
56unsafe impl Send for RendererSurfaceState {}
57unsafe impl Sync for RendererSurfaceState {}
58
59#[derive(Debug)]
60struct InnerBuffer {
61    buffer: WlBuffer,
62    #[cfg(feature = "backend_drm")]
63    acquire_point: Option<DrmSyncPoint>,
64    #[cfg(feature = "backend_drm")]
65    release_point: Option<DrmSyncPoint>,
66}
67
68impl Drop for InnerBuffer {
69    #[inline]
70    fn drop(&mut self) {
71        self.buffer.release();
72        #[cfg(feature = "backend_drm")]
73        if let Some(release_point) = &self.release_point {
74            if let Err(err) = release_point.signal() {
75                tracing::error!("Failed to signal syncobj release point: {}", err);
76            }
77        }
78    }
79}
80
81/// A wayland buffer
82#[derive(Debug, Clone)]
83pub struct Buffer {
84    inner: Arc<InnerBuffer>,
85}
86
87impl Buffer {
88    /// Create a buffer with implicit sync
89    pub fn with_implicit(buffer: WlBuffer) -> Self {
90        Self {
91            inner: Arc::new(InnerBuffer {
92                buffer,
93                #[cfg(feature = "backend_drm")]
94                acquire_point: None,
95                #[cfg(feature = "backend_drm")]
96                release_point: None,
97            }),
98        }
99    }
100
101    /// Create a buffer with explicit acquire and release sync points
102    #[cfg(feature = "backend_drm")]
103    pub fn with_explicit(buffer: WlBuffer, acquire_point: DrmSyncPoint, release_point: DrmSyncPoint) -> Self {
104        Self {
105            inner: Arc::new(InnerBuffer {
106                buffer,
107                acquire_point: Some(acquire_point),
108                release_point: Some(release_point),
109            }),
110        }
111    }
112
113    #[cfg(feature = "backend_drm")]
114    #[allow(dead_code)]
115    pub(crate) fn acquire_point(&self) -> Option<&DrmSyncPoint> {
116        self.inner.acquire_point.as_ref()
117    }
118}
119
120impl std::ops::Deref for Buffer {
121    type Target = WlBuffer;
122
123    #[inline]
124    fn deref(&self) -> &Self::Target {
125        &self.inner.buffer
126    }
127}
128
129impl PartialEq<WlBuffer> for Buffer {
130    #[inline]
131    fn eq(&self, other: &WlBuffer) -> bool {
132        self.inner.buffer == *other
133    }
134}
135
136impl PartialEq<WlBuffer> for &Buffer {
137    #[inline]
138    fn eq(&self, other: &WlBuffer) -> bool {
139        self.inner.buffer == *other
140    }
141}
142
143impl RendererSurfaceState {
144    #[profiling::function]
145    pub(crate) fn update_buffer(&mut self, states: &SurfaceData) {
146        #[cfg(feature = "backend_drm")]
147        let mut guard = states.cached_state.get::<DrmSyncobjCachedState>();
148        #[cfg(feature = "backend_drm")]
149        let syncobj_state = guard.current();
150
151        let mut guard = states.cached_state.get::<SurfaceAttributes>();
152        let attrs = guard.current();
153
154        let new_buffer = matches!(attrs.buffer, Some(BufferAssignment::NewBuffer(_)));
155        match attrs.buffer.take() {
156            Some(BufferAssignment::NewBuffer(buffer)) => {
157                self.buffer_dimensions = buffer_dimensions(&buffer);
158                if self.buffer_dimensions.is_none() {
159                    // This results in us rendering nothing (can happen e.g. for failed egl-buffer-calls),
160                    // but it is better than crashing the compositor for a bad buffer
161                    self.reset();
162                    return;
163                }
164                self.buffer_has_alpha = buffer_has_alpha(&buffer);
165                self.buffer_scale = attrs.buffer_scale;
166                self.buffer_transform = attrs.buffer_transform.into();
167
168                if !self.buffer.as_ref().is_some_and(|b| b == buffer) {
169                    self.buffer = Some(Buffer {
170                        inner: Arc::new(InnerBuffer {
171                            buffer,
172                            #[cfg(feature = "backend_drm")]
173                            acquire_point: syncobj_state.acquire_point.take(),
174                            #[cfg(feature = "backend_drm")]
175                            release_point: syncobj_state.release_point.take(),
176                        }),
177                    });
178                }
179
180                self.textures.clear();
181            }
182            Some(BufferAssignment::Removed) => {
183                self.reset();
184                return;
185            }
186            None => {}
187        };
188
189        let Some(buffer_dimensions) = self.buffer_dimensions else {
190            // nothing to be done without a buffer
191            return;
192        };
193
194        let surface_size = buffer_dimensions.to_logical(self.buffer_scale, self.buffer_transform);
195        let surface_view = SurfaceView::from_states(states, surface_size, attrs.client_scale);
196        let surface_view_changed = self.surface_view.replace(surface_view) != Some(surface_view);
197
198        // if we received a new buffer also process the attached damage
199        if new_buffer {
200            let buffer_damage = attrs.damage.drain(..).flat_map(|dmg| {
201                match dmg {
202                    Damage::Buffer(rect) => rect,
203                    Damage::Surface(rect) => surface_view.rect_to_local(rect).to_i32_up().to_buffer(
204                        self.buffer_scale,
205                        self.buffer_transform,
206                        &surface_size,
207                    ),
208                }
209                .intersection(Rectangle::from_size(buffer_dimensions))
210            });
211            self.damage.add(buffer_damage);
212        }
213
214        // if the buffer or our view changed rebuild our opaque regions
215        if new_buffer || surface_view_changed {
216            self.opaque_regions.clear();
217            if !self.buffer_has_alpha.unwrap_or(true) {
218                self.opaque_regions.push(Rectangle::from_size(surface_view.dst))
219            } else if let Some(region_attributes) = &attrs.opaque_region {
220                let opaque_regions = region_attributes
221                    .rects
222                    .iter()
223                    .map(|(kind, rect)| {
224                        let dest_size = surface_view.dst;
225
226                        let rect_constrained_loc = rect.loc.constrain(Rectangle::from_size(dest_size));
227                        let rect_clamped_size = rect
228                            .size
229                            .clamp((0, 0), (dest_size.to_point() - rect_constrained_loc).to_size());
230
231                        let rect = Rectangle::new(rect_constrained_loc, rect_clamped_size);
232
233                        (kind, rect)
234                    })
235                    .fold(
236                        std::mem::take(&mut self.opaque_regions),
237                        |mut new_regions, (kind, rect)| {
238                            match kind {
239                                RectangleKind::Add => {
240                                    let added_regions = rect.subtract_rects(
241                                        new_regions
242                                            .iter()
243                                            .filter(|region| region.overlaps_or_touches(rect))
244                                            .copied(),
245                                    );
246                                    new_regions.extend(added_regions);
247                                }
248                                RectangleKind::Subtract => {
249                                    new_regions =
250                                        Rectangle::subtract_rects_many_in_place(new_regions, [rect]);
251                                }
252                            }
253
254                            new_regions
255                        },
256                    );
257
258                self.opaque_regions = opaque_regions;
259            }
260        }
261    }
262
263    /// Get the current commit position of this surface
264    ///
265    /// The position should be saved after calling [`damage_since`](RendererSurfaceState::damage_since) and
266    /// provided as the commit in the next call.
267    pub fn current_commit(&self) -> CommitCounter {
268        self.damage.current_commit()
269    }
270
271    /// Gets the damage since the last commit
272    ///
273    /// If either the commit is `None` or the commit is too old
274    /// the whole buffer will be returned as damage.
275    pub fn damage_since(&self, commit: Option<CommitCounter>) -> DamageSet<i32, BufferCoord> {
276        self.damage.damage_since(commit).unwrap_or_else(|| {
277            self.buffer_dimensions
278                .as_ref()
279                .map(|size| DamageSet::from_slice(&[Rectangle::from_size(*size)]))
280                .unwrap_or_default()
281        })
282    }
283
284    /// Gets the current damage of this surface
285    pub fn damage(&self) -> DamageSnapshot<i32, BufferCoord> {
286        self.damage.snapshot()
287    }
288
289    /// Returns the logical size of the current attached buffer
290    pub fn buffer_size(&self) -> Option<Size<i32, Logical>> {
291        self.buffer_dimensions
292            .as_ref()
293            .map(|dim| dim.to_logical(self.buffer_scale, self.buffer_transform))
294    }
295
296    /// Returns the scale of the current attached buffer
297    pub fn buffer_scale(&self) -> i32 {
298        self.buffer_scale
299    }
300
301    /// Returns the transform of the current attached buffer
302    pub fn buffer_transform(&self) -> Transform {
303        self.buffer_transform
304    }
305
306    /// Returns the logical size of the surface.
307    ///
308    /// Note: The surface size may not be equal to the buffer size in case
309    /// a viewport has been attached to the surface.
310    pub fn surface_size(&self) -> Option<Size<i32, Logical>> {
311        self.surface_view.map(|view| view.dst)
312    }
313
314    /// Get the attached buffer.
315    /// Can be used to check if surface is mapped
316    pub fn buffer(&self) -> Option<&Buffer> {
317        self.buffer.as_ref()
318    }
319
320    /// Gets a reference to the texture for the specified renderer context
321    pub fn texture<T>(&self, id: ContextId<T>) -> Option<&T>
322    where
323        T: Texture + 'static,
324    {
325        self.textures.get(&id.erased()).and_then(|e| e.downcast_ref())
326    }
327
328    /// Gets the opaque regions of this surface
329    pub fn opaque_regions(&self) -> Option<&[Rectangle<i32, Logical>]> {
330        // If the surface is unmapped there can be no opaque regions
331        self.surface_size()?;
332
333        // To make it easier for upstream, but to no re-allocate a
334        // new vec if the opaque regions change for return None
335        // on empty regions
336        if self.opaque_regions.is_empty() {
337            return None;
338        }
339
340        Some(&self.opaque_regions[..])
341    }
342
343    /// Gets the [`SurfaceView`] of this surface
344    pub fn view(&self) -> Option<SurfaceView> {
345        self.surface_view
346    }
347
348    fn reset(&mut self) {
349        self.buffer_dimensions = None;
350        self.buffer = None;
351        self.textures.clear();
352        self.damage.reset();
353        self.surface_view = None;
354        self.buffer_has_alpha = None;
355        self.opaque_regions.clear();
356    }
357}
358
359/// Handler to let smithay take over buffer management.
360///
361/// Needs to be called first on the commit-callback of
362/// [`crate::wayland::compositor::CompositorHandler::commit`].
363///
364/// Consumes the buffer of [`SurfaceAttributes`], the buffer will
365/// not be accessible anymore, but [`draw_render_elements`] and other
366/// `draw_*` helpers of the [desktop module](`crate::desktop`) will
367/// become usable for surfaces handled this way.
368#[profiling::function]
369pub fn on_commit_buffer_handler<D: 'static>(surface: &WlSurface) {
370    if !is_sync_subsurface(surface) {
371        let mut new_surfaces = Vec::new();
372        with_surface_tree_upward(
373            surface,
374            (),
375            |_, _, _| TraversalAction::DoChildren(()),
376            |surf, states, _| {
377                if states
378                    .data_map
379                    .insert_if_missing_threadsafe(|| Mutex::new(RendererSurfaceState::default()))
380                {
381                    new_surfaces.push(surf.clone());
382                }
383                let mut data = states
384                    .data_map
385                    .get::<RendererSurfaceStateUserData>()
386                    .unwrap()
387                    .lock()
388                    .unwrap();
389                data.update_buffer(states);
390            },
391            |_, _, _| true,
392        );
393        for surf in &new_surfaces {
394            add_destruction_hook(surf, |_: &mut D, surface| {
395                // We reset the state on destruction before the user_data is dropped
396                // to prevent a deadlock which can happen if we try to send a buffer
397                // release during drop. This also enables us to free resources earlier
398                // like the stored textures
399                compositor::with_states(surface, |data| {
400                    if let Some(mut state) = data
401                        .data_map
402                        .get::<RendererSurfaceStateUserData>()
403                        .map(|s| s.lock().unwrap())
404                    {
405                        state.reset();
406                    }
407                    #[cfg(feature = "renderer_multi")]
408                    crate::backend::renderer::multigpu::clear_surface_textures(data);
409                });
410            });
411        }
412    }
413}
414
415impl SurfaceView {
416    fn from_states(states: &SurfaceData, surface_size: Size<i32, Logical>, client_scale: f64) -> SurfaceView {
417        viewporter::ensure_viewport_valid(states, surface_size);
418        let mut viewport_state = states.cached_state.get::<viewporter::ViewportCachedState>();
419        let viewport = viewport_state.current();
420
421        let src = viewport
422            .src
423            .unwrap_or_else(|| Rectangle::from_size(surface_size.to_f64()));
424        let dst = viewport.size().unwrap_or(
425            surface_size
426                .to_f64()
427                .to_client(1.)
428                .to_logical(client_scale)
429                .to_i32_round(),
430        );
431        let offset = if states.role == Some(SUBSURFACE_ROLE) {
432            states
433                .cached_state
434                .get::<SubsurfaceCachedState>()
435                .current()
436                .location
437        } else {
438            Default::default()
439        };
440        SurfaceView { src, dst, offset }
441    }
442
443    pub(crate) fn rect_to_global<N>(&self, rect: Rectangle<N, Logical>) -> Rectangle<f64, Logical>
444    where
445        N: Coordinate,
446    {
447        let scale = self.scale();
448        let mut rect = rect.to_f64();
449        rect.loc -= self.src.loc;
450        rect.upscale(scale)
451    }
452
453    pub(crate) fn rect_to_local<N>(&self, rect: Rectangle<N, Logical>) -> Rectangle<f64, Logical>
454    where
455        N: Coordinate,
456    {
457        let scale = self.scale();
458        let mut rect = rect.to_f64().downscale(scale);
459        rect.loc += self.src.loc;
460        rect
461    }
462
463    fn scale(&self) -> Scale<f64> {
464        Scale::from((
465            self.dst.w as f64 / self.src.size.w,
466            self.dst.h as f64 / self.src.size.h,
467        ))
468    }
469}
470
471/// Access the buffer related states associated to this surface
472///
473/// Calls [`compositor::with_states`] internally.
474///
475/// Returns `None`, if there never was a commit processed through `on_commit_buffer_handler`.
476pub fn with_renderer_surface_state<F, T>(surface: &WlSurface, cb: F) -> Option<T>
477where
478    F: FnOnce(&mut RendererSurfaceState) -> T,
479{
480    compositor::with_states(surface, |states| {
481        let data = states.data_map.get::<RendererSurfaceStateUserData>()?;
482        Some(cb(&mut data.lock().unwrap()))
483    })
484}
485
486/// Imports buffers of a surface using a given [`Renderer`]
487///
488/// This (or `import_surface_tree`) need to be called before`draw_render_elements`, if used later.
489///
490/// Note: This will do nothing, if you are not using
491/// [`crate::backend::renderer::utils::on_commit_buffer_handler`]
492/// to let smithay handle buffer management.
493#[instrument(level = "trace", skip_all)]
494#[profiling::function]
495pub fn import_surface<R>(renderer: &mut R, states: &SurfaceData) -> Result<(), R::Error>
496where
497    R: Renderer + ImportAll,
498    R::TextureId: 'static,
499{
500    if let Some(data) = states.data_map.get::<RendererSurfaceStateUserData>() {
501        let context_id = renderer.context_id().erased();
502        let mut data_ref = data.lock().unwrap();
503        let data = &mut *data_ref;
504
505        let last_commit = data.renderer_seen.get(&context_id);
506        let buffer_damage = data.damage_since(last_commit.copied());
507        if let Entry::Vacant(e) = data.textures.entry(context_id.clone()) {
508            if let Some(buffer) = data.buffer.as_ref() {
509                // There is no point in importing a single pixel buffer
510                if matches!(
511                    crate::backend::renderer::buffer_type(buffer),
512                    Some(crate::backend::renderer::BufferType::SinglePixel)
513                ) {
514                    return Ok(());
515                }
516
517                match renderer.import_buffer(buffer, Some(states), &buffer_damage) {
518                    Some(Ok(m)) => {
519                        e.insert(Box::new(m));
520                        data.renderer_seen.insert(context_id, data.current_commit());
521                    }
522                    Some(Err(err)) => {
523                        warn!("Error loading buffer: {}", err);
524                        return Err(err);
525                    }
526                    None => {
527                        error!("Unknown buffer format for: {:?}", buffer);
528                    }
529                }
530            }
531        }
532    }
533
534    Ok(())
535}
536
537/// Imports buffers of a surface and its subsurfaces using a given [`Renderer`].
538///
539/// This (or `import_surface`) need to be called before `draw_render_elements`, if used later.
540///
541/// Note: This will do nothing, if you are not using
542/// [`crate::backend::renderer::utils::on_commit_buffer_handler`]
543/// to let smithay handle buffer management.
544#[instrument(level = "trace", skip_all)]
545#[profiling::function]
546pub fn import_surface_tree<R>(renderer: &mut R, surface: &WlSurface) -> Result<(), R::Error>
547where
548    R: Renderer + ImportAll,
549    R::TextureId: 'static,
550{
551    let scale = 1.0;
552    let location: Point<f64, Physical> = (0.0, 0.0).into();
553
554    let mut result = Ok(());
555    with_surface_tree_downward(
556        surface,
557        location,
558        |_surface, states, location| {
559            let mut location = *location;
560            // Import a new buffer if necessary
561            if let Err(err) = import_surface(renderer, states) {
562                result = Err(err);
563            }
564
565            if let Some(data) = states.data_map.get::<RendererSurfaceStateUserData>() {
566                let mut data_ref = data.lock().unwrap();
567                let data = &mut *data_ref;
568                // Now, should we be drawn ?
569                if data.textures.contains_key(&renderer.context_id().erased()) {
570                    // if yes, also process the children
571                    let surface_view = data.surface_view.unwrap();
572                    location += surface_view.offset.to_f64().to_physical(scale);
573                    TraversalAction::DoChildren(location)
574                } else {
575                    // we are not displayed, so our children are neither
576                    TraversalAction::SkipChildren
577                }
578            } else {
579                // we are not displayed, so our children are neither
580                TraversalAction::SkipChildren
581            }
582        },
583        |_, _, _| {},
584        |_, _, _| true,
585    );
586    result
587}
588
589/// Draws the render elements using a given [`Renderer`] and [`Frame`](crate::backend::renderer::Frame)
590///
591/// - `scale` needs to be equivalent to the fractional scale the rendered result should have.
592/// - `location` is the position the surface should be drawn at.
593/// - `damage` is the set of regions that should be drawn relative to the same origin as the location.
594///
595/// Note: This element will render nothing, if you are not using
596/// [`crate::backend::renderer::utils::on_commit_buffer_handler`]
597/// to let smithay handle buffer management.
598#[instrument(level = "trace", skip(frame, scale, elements))]
599#[profiling::function]
600pub fn draw_render_elements<R, S, E>(
601    frame: &mut R::Frame<'_, '_>,
602    scale: S,
603    elements: &[E],
604    damage: &[Rectangle<i32, Physical>],
605) -> Result<Option<Vec<Rectangle<i32, Physical>>>, R::Error>
606where
607    R: Renderer,
608    R::TextureId: 'static,
609    S: Into<Scale<f64>>,
610    E: RenderElement<R>,
611{
612    let scale = scale.into();
613
614    let mut render_elements: Vec<&E> = Vec::with_capacity(elements.len());
615    let mut opaque_regions: Vec<Rectangle<i32, Physical>> = Vec::new();
616    let mut render_damage: Vec<Rectangle<i32, Physical>> = Vec::with_capacity(damage.len());
617
618    for element in elements {
619        let element_geometry = element.geometry(scale);
620
621        // Then test if the element is completely hidden behind opaque regions
622        let is_hidden = element_geometry
623            .subtract_rects(opaque_regions.iter().copied())
624            .is_empty();
625
626        if is_hidden {
627            // No need to draw a completely hidden element
628            continue;
629        }
630
631        render_damage.extend(Rectangle::subtract_rects_many(
632            damage.iter().copied(),
633            opaque_regions.iter().copied(),
634        ));
635
636        opaque_regions.extend(element.opaque_regions(scale).into_iter().map(|mut region| {
637            region.loc += element_geometry.loc;
638            region
639        }));
640        render_elements.insert(0, element);
641    }
642
643    // Optimize the damage for rendering
644    render_damage.dedup();
645    render_damage.retain(|rect| !rect.is_empty());
646    // filter damage outside of the output gep and merge overlapping rectangles
647    render_damage = render_damage
648        .into_iter()
649        .fold(Vec::new(), |new_damage, mut rect| {
650            // replace with drain_filter, when that becomes stable to reuse the original Vec's memory
651            let (overlapping, mut new_damage): (Vec<_>, Vec<_>) = new_damage
652                .into_iter()
653                .partition(|other| other.overlaps_or_touches(rect));
654
655            for overlap in overlapping {
656                rect = rect.merge(overlap);
657            }
658            new_damage.push(rect);
659            new_damage
660        });
661
662    if render_damage.is_empty() {
663        return Ok(None);
664    }
665
666    for element in render_elements.iter() {
667        let element_geometry = element.geometry(scale);
668
669        let element_damage = damage
670            .iter()
671            .filter_map(|d| d.intersection(element_geometry))
672            .map(|mut d| {
673                d.loc -= element_geometry.loc;
674                d
675            })
676            .collect::<Vec<_>>();
677
678        if element_damage.is_empty() {
679            continue;
680        }
681
682        element.draw(frame, element.src(), element_geometry, &element_damage, &[], None)?;
683    }
684
685    Ok(Some(render_damage))
686}