//! Utility module for helpers around drawing [`WlSurface`](wayland_server::protocol::wl_surface::WlSurface)s //! and [`RenderElement`](super::element::RenderElement)s with [`Renderer`](super::Renderer)s. use crate::utils::{Buffer as BufferCoord, Coordinate, Logical, Physical, Point, Rectangle, Size}; use std::{collections::VecDeque, fmt, sync::Arc}; #[cfg(feature = "wayland_frontend")] mod wayland; #[cfg(feature = "wayland_frontend")] pub use self::wayland::*; /// A simple wrapper for counting commits /// /// The purpose of the counter is to keep track /// on the number of times something has changed. /// It provides an easy way to obtain the distance /// between two instances of a [`CommitCounter`]. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)] pub struct CommitCounter(usize); impl CommitCounter { /// Increment the commit counter pub fn increment(&mut self) { self.0 = self.0.wrapping_add(1) } /// Get the distance between two [`CommitCounter`]s /// /// If the [`CommitCounter`] is incremented on each recorded /// damage this returns the count of damage that happened /// between the [`CommitCounter`]s /// /// Returns `None` in case the distance could not be calculated. /// If uses as part of damage tracking the tracked element /// should be considered as fully damaged. pub fn distance(&self, previous_commit: Option) -> Option { // if commit > commit_count we have overflown, in that case the following map might result // in a false-positive, if commit is still very large. So we force false in those cases. // That will result in a potentially sub-optimal full damage every usize::MAX frames, // which is acceptable. previous_commit .filter(|commit| commit <= self) .map(|commit| self.0.wrapping_sub(commit.0)) } } impl From for CommitCounter { #[inline] fn from(counter: usize) -> Self { CommitCounter(counter) } } /// A tracker for holding damage /// /// It keeps track of the submitted damage /// and automatically caps the damage /// with the specified limit. /// /// See [`DamageSnapshot`] for more /// information. pub struct DamageBag { limit: usize, state: DamageSnapshot, } /// A snapshot of the current state of a [`DamageBag`] /// /// The snapshot can be used to get an immutable view /// into the current state of a [`DamageBag`]. /// It provides an easy way to get the damage between two /// [`CommitCounter`]s. pub struct DamageSnapshot { limit: usize, commit_counter: CommitCounter, damage: Arc; MAX_DAMAGE_RECTS]>>>, } impl Clone for DamageSnapshot { #[inline] fn clone(&self) -> Self { Self { limit: self.limit, commit_counter: self.commit_counter, damage: self.damage.clone(), } } } impl fmt::Debug for DamageBag { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("DamageBag") .field("limit", &self.limit) .field("state", &self.state) .finish() } } impl fmt::Debug for DamageBag { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("DamageBag") .field("limit", &self.limit) .field("state", &self.state) .finish() } } impl fmt::Debug for DamageBag { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("DamageBag") .field("limit", &self.limit) .field("state", &self.state) .finish() } } impl fmt::Debug for DamageSnapshot { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("DamageSnapshot") .field("commit_counter", &self.commit_counter) .field("damage", &self.damage) .finish() } } impl fmt::Debug for DamageSnapshot { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("DamageSnapshot") .field("commit_counter", &self.commit_counter) .field("damage", &self.damage) .finish() } } impl fmt::Debug for DamageSnapshot { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("DamageSnapshot") .field("commit_counter", &self.commit_counter) .field("damage", &self.damage) .finish() } } const MAX_DAMAGE_AGE: usize = 4; const MAX_DAMAGE_RECTS: usize = 16; const MAX_DAMAGE_SET: usize = MAX_DAMAGE_RECTS * 2; impl Default for DamageBag { #[inline] fn default() -> Self { DamageBag::new(MAX_DAMAGE_AGE) } } impl DamageSnapshot { fn new(limit: usize) -> Self { DamageSnapshot { limit, commit_counter: CommitCounter::default(), damage: Arc::new(VecDeque::with_capacity(limit)), } } /// Create an empty damage snapshot pub fn empty() -> Self { DamageSnapshot { limit: 0, commit_counter: CommitCounter::default(), damage: Default::default(), } } /// Gets the current [`CommitCounter`] of this snapshot /// /// The returned [`CommitCounter`] should be stored after /// calling [`damage_since`](DamageSnapshot::damage_since) /// and provided to the next call of [`damage_since`](DamageSnapshot::damage_since) /// to query the damage between these two [`CommitCounter`]s. #[inline] pub fn current_commit(&self) -> CommitCounter { self.commit_counter } /// Provides raw access to the stored damage pub fn raw(&self) -> impl Iterator>> { self.damage.iter().map(|d| d.iter()) } fn reset(&mut self) { Arc::make_mut(&mut self.damage).clear(); self.commit_counter.increment(); } } impl DamageSnapshot { /// Get the damage since the last commit /// /// Returns `None` in case the [`CommitCounter`] is too old /// or the damage has been reset. In that case the whole /// element geometry should be considered as damaged /// /// If the commit is recent enough and no damage has occurred /// an empty `Vec` will be returned pub fn damage_since(&self, commit: Option) -> Option> { let distance = self.commit_counter.distance(commit); if distance .map(|distance| distance <= self.damage.len()) .unwrap_or(false) { let mut damage_set = DamageSet::default(); for damage in self.damage.iter().take(distance.unwrap()) { damage_set.damage.extend_from_slice(damage); } Some(damage_set) } else { None } } fn add(&mut self, damage: impl IntoIterator>) { // FIXME: Get rid of this allocation here let mut damage = damage.into_iter().filter(|d| !d.is_empty()).collect::>(); if damage.is_empty() { // do not track empty damage return; } damage.dedup(); let inner_damage = Arc::make_mut(&mut self.damage); inner_damage.push_front(smallvec::SmallVec::from_vec(damage)); inner_damage.truncate(self.limit); self.commit_counter.increment(); } } impl DamageBag { /// Initialize a a new [`DamageBag`] with the specified limit pub fn new(limit: usize) -> Self { DamageBag { limit, state: DamageSnapshot::new(limit), } } /// Gets the current [`CommitCounter`] of this tracker #[inline] pub fn current_commit(&self) -> CommitCounter { self.state.current_commit() } /// Provides raw access to the stored damage pub fn raw(&self) -> impl Iterator>> { self.state.raw() } /// Reset the damage /// /// This should be called when the /// tracked item has been resized pub fn reset(&mut self) { self.state.reset() } } impl DamageBag { /// Get a snapshot of the current damage pub fn snapshot(&self) -> DamageSnapshot { self.state.clone() } } impl DamageBag { /// Add some damage to the tracker pub fn add(&mut self, damage: impl IntoIterator>) { self.state.add(damage) } /// Get the damage since the last commit /// /// Returns `None` in case the [`CommitCounter`] is too old /// or the damage has been reset. In that case the whole /// element geometry should be considered as damaged /// /// If the commit is recent enough and no damage has occurred /// an empty `Vec` will be returned pub fn damage_since(&self, commit: Option) -> Option> { self.state.damage_since(commit) } } /// A set of damage returned from [`DamageBag::damage_since`] of [`DamageSnapshot::damage_since`] pub struct DamageSet { damage: smallvec::SmallVec<[Rectangle; MAX_DAMAGE_SET]>, } impl Default for DamageSet { fn default() -> Self { Self { damage: Default::default(), } } } impl DamageSet { /// Copy the damage from a slice into a new `DamageSet`. #[inline] pub fn from_slice(slice: &[Rectangle]) -> Self { Self { damage: smallvec::SmallVec::from_slice(slice), } } } impl std::ops::Deref for DamageSet { type Target = [Rectangle]; #[inline] fn deref(&self) -> &Self::Target { &self.damage } } impl IntoIterator for DamageSet { type Item = Rectangle; type IntoIter = DamageSetIter; #[inline] fn into_iter(self) -> Self::IntoIter { DamageSetIter { inner: self.damage.into_iter(), } } } impl FromIterator> for DamageSet { #[inline] fn from_iter>>(iter: T) -> Self { Self { damage: smallvec::SmallVec::from_iter(iter), } } } /// Iterator for [`DamageSet::into_iter`] pub struct DamageSetIter { inner: smallvec::IntoIter<[Rectangle; MAX_DAMAGE_SET]>, } impl fmt::Debug for DamageSetIter { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("DamageSetIter") .field("inner", &self.inner) .finish() } } impl fmt::Debug for DamageSetIter { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("DamageSetIter") .field("inner", &self.inner) .finish() } } impl fmt::Debug for DamageSetIter { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("DamageSetIter") .field("inner", &self.inner) .finish() } } impl Iterator for DamageSetIter { type Item = Rectangle; #[inline] fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } impl fmt::Debug for DamageSet { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("DamageSet").field("damage", &self.damage).finish() } } impl fmt::Debug for DamageSet { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("DamageSet").field("damage", &self.damage).finish() } } impl fmt::Debug for DamageSet { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("DamageSet").field("damage", &self.damage).finish() } } const MAX_OPAQUE_REGIONS: usize = 16; /// Wrapper for a set of opaque regions pub struct OpaqueRegions { regions: smallvec::SmallVec<[Rectangle; MAX_OPAQUE_REGIONS]>, } impl Default for OpaqueRegions where N: Default, { #[inline] fn default() -> Self { Self { regions: Default::default(), } } } impl OpaqueRegions { /// Copy the opaque regions from a slice into a new `OpaqueRegions`. #[inline] pub fn from_slice(slice: &[Rectangle]) -> Self { Self { regions: smallvec::SmallVec::from_slice(slice), } } } impl std::ops::Deref for OpaqueRegions { type Target = [Rectangle]; #[inline] fn deref(&self) -> &Self::Target { &self.regions } } impl IntoIterator for OpaqueRegions { type Item = Rectangle; type IntoIter = OpaqueRegionsIter; #[inline] fn into_iter(self) -> Self::IntoIter { OpaqueRegionsIter { inner: self.regions.into_iter(), } } } impl FromIterator> for OpaqueRegions { #[inline] fn from_iter>>(iter: T) -> Self { Self { regions: smallvec::SmallVec::from_iter(iter), } } } /// Iterator for [`OpaqueRegions::into_iter`] pub struct OpaqueRegionsIter { inner: smallvec::IntoIter<[Rectangle; MAX_OPAQUE_REGIONS]>, } impl fmt::Debug for OpaqueRegionsIter { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("OpaqueRegionsIter") .field("inner", &self.inner) .finish() } } impl fmt::Debug for OpaqueRegionsIter { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("OpaqueRegionsIter") .field("inner", &self.inner) .finish() } } impl fmt::Debug for OpaqueRegionsIter { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("OpaqueRegionsIter") .field("inner", &self.inner) .finish() } } impl Iterator for OpaqueRegionsIter { type Item = Rectangle; #[inline] fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } impl fmt::Debug for OpaqueRegions { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("OpaqueRegions") .field("regions", &self.regions) .finish() } } impl fmt::Debug for OpaqueRegions { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("OpaqueRegions") .field("regions", &self.regions) .finish() } } impl fmt::Debug for OpaqueRegions { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("OpaqueRegions") .field("regions", &self.regions) .finish() } } /// Defines a view into the surface #[derive(Debug, Default, PartialEq, Clone, Copy)] pub struct SurfaceView { /// The logical source used for cropping pub src: Rectangle, /// The logical destination size used for scaling pub dst: Size, /// The logical offset for a sub-surface pub offset: Point, }