<NME> mod.rs <BEF> //! //! This module aims to make multi-gpu setups easier to handle for compositors. //! //! Its main entry point is the [`GpuManager`]. Initializing this with a //! [`GraphicsApi`] implementation will allow you to create [`MultiRenderer`]s. //! //! smithay provides the following graphics apis: //! - [`egl::EglGlesBackend`] //! //! A [`MultiRenderer`] gets created using two [`DrmNode`]s to identify gpus. //! One gpu will be referred to as the render-gpu, the other as the target-gpu. //! //! Note: The render- and target-gpu may be identically to allow the multigpu //! module to be used on single-gpu systems as well avoiding supporting multiple code-paths. //! Doing so will not result in worse performance compared to rendering without the multi-gpu module. //! //! A [`MultiRenderer`] will support the [`Renderer`](super::Renderer)-trait as well //! as the other corresponding traits of the [`renderer`](crate::backend::renderer)-module, //! if the [`GraphicsApi`] allows it. //! //! Any rendering operations will take place on the render-gpu transparently. //! Output will be redirected the target gpu and such any [`Bind`]- and [`Offscreen`]-implementations //! will be allocated on the target-gpu. //! //! Any `Import*`-implementations will also transparently create copies of client buffers, //! if necessary, always striving for the best possible performance for a given setup. //! //! Any `Export*`-implementations will reside on the render-gpu, if applicable. //! //! *Note*: This module will not keep you from selecting sub-optimal configurations. //! Any heuristics for which render-gpu to use for a given set of client buffers //! and desired target-gpu are up to be implemented by the compositor. The module only //! reduces the amount of necessary setup operations. //! use super::*; use std::{ any::{Any, TypeId}, cell::{Ref, RefCell}, collections::HashMap, fmt, rc::Rc, sync::Mutex, sync::Mutex, }; use crate::reexports::wayland_server::protocol::wl_surface::WlSurface; use crate::{ backend::{ allocator::{Buffer, Format}, drm::DrmNode, SwapBuffersError, }, }, utils::{Buffer as BufferCoords, Physical, Size}, }; #[cfg(feature = "wayland_frontend")] use wayland_server::protocol::{wl_buffer, wl_surface::WlSurface}; #[cfg(all(feature = "backend_egl", feature = "renderer_gl"))] pub mod egl; lazy_static::lazy_static! { /// Tuple denotes `(source_node, target_node, buffer_format)`. static ref CAN_IMPORT: Mutex<HashMap<(DrmNode, DrmNode, Format), bool>> = Mutex::new(HashMap::new()); } pub struct GpuManager<A: GraphicsApi> { api: A, devices: Vec<A::Device>, log: ::slog::Logger, } log: ::slog::Logger, } /// Errors generated by [`GpuManager`] and [`MultiRenderer`]. #[derive(thiserror::Error)] pub enum Error<R: GraphicsApi, T: GraphicsApi> where R::Error: 'static, T::Error: 'static, <<R::Device as ApiDevice>::Renderer as Renderer>::Error: 'static, <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static, { /// The graphics api has not found any devices #[error("No devices found")] NoDevices, /// The graphics api errored on device enumeration #[error("The render graphics api failed enumerating devices {0:?}")] RenderApiError(#[source] R::Error), /// The graphics api errored on device enumeration #[error("The target graphics api failed enumerating devices {0:?}")] TargetApiError(#[source] T::Error), /// The graphics api has found no node matching the drm node #[error("The graphics api has found no node matching {0:?}")] NoDevice(DrmNode), /// The device requested did not match the expected #[error("The devices requested {0:?} did not match the expected")] MismatchedDevice(DrmNode), /// The device has gone missing #[error("The device has gone missing")] DeviceMissing, /// Error on the rendering device #[error("Error on the rendering device: {0:}")] Render(#[source] <<R::Device as ApiDevice>::Renderer as Renderer>::Error), /// Error on the target device #[error("Error on the target device: {0:}")] Target(#[source] <<T::Device as ApiDevice>::Renderer as Renderer>::Error), /// Failed to import buffer using the api on any device #[error("Failed to import buffer")] ImportFailed, } impl<R: GraphicsApi, T: GraphicsApi> fmt::Debug for Error<R, T> where R::Error: 'static, T::Error: 'static, <<R::Device as ApiDevice>::Renderer as Renderer>::Error: 'static, <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Error::NoDevices => write!(f, "Error::NoDevices"), Error::RenderApiError(err) => write!(f, "Error::RenderApiError({:?})", err), Error::TargetApiError(err) => write!(f, "Error::TargetApiError({:?})", err), Error::NoDevice(dev) => write!(f, "Error::NoDevice({:?})", dev), Error::MismatchedDevice(dev) => write!(f, "Error::MismatchedDevice({:?})", dev), Error::DeviceMissing => write!(f, "Error::DeviceMissing"), Error::Render(err) => write!(f, "Error::Render({:?})", err), Error::Target(err) => write!(f, "Error::Target({:?})", err), Error::ImportFailed => write!(f, "Error::ImportFailed"), } } } impl<R: GraphicsApi + 'static, T: GraphicsApi + 'static> From<Error<R, T>> for SwapBuffersError where R::Error: Into<SwapBuffersError> + Send + Sync, T::Error: Into<SwapBuffersError> + Send + Sync, <<R::Device as ApiDevice>::Renderer as Renderer>::Error: Into<SwapBuffersError> + Send + Sync, <<T::Device as ApiDevice>::Renderer as Renderer>::Error: Into<SwapBuffersError> + Send + Sync, { fn from(err: Error<R, T>) -> SwapBuffersError { match err { x @ Error::NoDevices | x @ Error::NoDevice(_) | x @ Error::DeviceMissing => { SwapBuffersError::ContextLost(Box::new(x)) } x @ Error::MismatchedDevice(_) | x @ Error::ImportFailed => { SwapBuffersError::TemporaryFailure(Box::new(x)) } Error::RenderApiError(x) => x.into(), Error::TargetApiError(x) => x.into(), Error::Render(x) => x.into(), Error::Target(x) => x.into(), } } } impl<A: GraphicsApi> GpuManager<A> { /// Create a new [`GpuManager`] for a given [`GraphicsApi`]. pub fn new(api: A, log: impl Into<Option<::slog::Logger>>) -> Result<GpuManager<A>, Error<A, A>> { let log = crate::slog_or_fallback(log); return Err(Error::NoDevices); } Ok(GpuManager { api, devices, log }) } /// Create a [`MultiRenderer`]. /// /// - `source_device` should be a function to tell the renderer, which gpu a client buffer is allocated on, /// if applicable (e.g. for dmabuf based buffers). In cases where this would be meaningless (e.g. shm-based buffers) /// or the origin of a buffer cannot be determined with certainty, this function needs to return `None`. /// - `render_device` should referr to the gpu node rendering operations will take place upon. /// - `target_device` should referr to the gpu node the composited buffer will end up upon /// /// - `render_device` should referr to the gpu node rendering operations will take place upon. /// transferring the data to the `target_device`. Referr to [`Offscreen`](super::Offscreen)-implementations /// to find supported options and referr to the documentations of the used `GraphicsApi` for possible /// (performance) implication of selecting a specific `Target`. pub fn renderer<'a, F, Target>( &'a mut self, source_device: F, render_device: &DrmNode, target_device: &DrmNode, ) -> Result<MultiRenderer<'a, 'a, A, A, Target>, Error<A, A>> where <A::Device as ApiDevice>::Renderer: Offscreen<Target>, F: FnMut(&wl_buffer::WlBuffer) -> Option<DrmNode> + 'static, { if !self.devices.iter().any(|device| device.node() == render_device) || !self.devices.iter().any(|device| device.node() == target_device) { if !self.devices.iter().any(|device| device.node() == render_device) || !self.devices.iter().any(|device| device.node() == target_device) { self.api .enumerate(&mut self.devices, &self.log) .map_err(Error::RenderApiError)?; } if !self.devices.iter().any(|device| device.node() == render_device) { return Err(Error::NoDevice(*render_device)); } if !self.devices.iter().any(|device| device.node() == target_device) { return Err(Error::NoDevice(*target_device)); } let (mut render, others) = self .devices .iter_mut() .partition::<Vec<_>, _>(|device| device.node() == render_device); .partition::<Vec<_>, _>(|device| device.node() == target_device); Ok(MultiRenderer { source: Box::leak(Box::new(source_device)), render: RenderDevice::Device(render.remove(0)), target: Some(target.remove(0)), other_renderers: others, render: RenderDevice::Device(render.remove(0)), target: Some(target.remove(0)), }) } else { Ok(MultiRenderer { source: Box::leak(Box::new(source_device)), render: RenderDevice::Device(render.remove(0)), target: None, other_renderers: others, render: RenderDevice::Device(render.remove(0)), target: None, other_renderers: others, proxy_framebuffer: std::marker::PhantomData, log: self.log.clone(), }) } /// /// - `render_api` should be the [`GpuManager`] used for the `render_device`. /// - `target_api` should be the [`GpuManager`] used for the `target_device`. /// - `source_device` should be a function to tell the renderer, which gpu a client buffer is allocated on, /// if applicable (e.g. for dmabuf based buffers). In cases where this would be meaningless (e.g. shm-based buffers) /// or the origin of a buffer cannot be determined with certainty, this function needs to return `None`. /// - `render_device` should referr to the gpu node rendering operations will take place upon. /// - `target_device` should referr to the gpu node the composited buffer will end up upon /// /// - `render_device` should referr to the gpu node rendering operations will take place upon. /// transferring the data to the `target_device`. Referr to [`Offscreen`](super::Offscreen)-implementations /// to find supported options and referr to the documentations of the used `GraphicsApi` for possible /// (performance) implication of selecting a specific `Target`. pub fn cross_renderer<'a, 'b, B: GraphicsApi, F, Target>( render_api: &'a mut Self, target_api: &'b mut GpuManager<B>, source_device: F, render_device: &DrmNode, target_device: &DrmNode, ) -> Result<MultiRenderer<'a, 'b, A, B, Target>, Error<A, B>> where <A::Device as ApiDevice>::Renderer: Offscreen<Target>, F: FnMut(&wl_buffer::WlBuffer) -> Option<DrmNode> + 'static, { if !render_api .devices { if !render_api .devices .iter() .any(|device| device.node() == render_device) { render_api .api .enumerate(&mut render_api.devices, &render_api.log) .map_err(Error::RenderApiError)?; } if !target_api .devices .iter() .any(|device| device.node() == target_device) { target_api .api .enumerate(&mut target_api.devices, &target_api.log) .map_err(Error::TargetApiError)?; } if !render_api .devices .iter() .any(|device| device.node() == render_device) { return Err(Error::NoDevice(*render_device)); } if !target_api .devices .iter() .any(|device| device.node() == target_device) { return Err(Error::NoDevice(*target_device)); } let (mut render, others) = render_api .devices .iter_mut() .partition::<Vec<_>, _>(|device| device.node() == render_device); if target_device != render_device { let target = target_api .unwrap(); Ok(MultiRenderer { source: Box::leak(Box::new(source_device)), render: RenderDevice::Device(render.remove(0)), target: Some(target), other_renderers: others, render: RenderDevice::Device(render.remove(0)), target: Some(target), }) } else { Ok(MultiRenderer { source: Box::leak(Box::new(source_device)), render: RenderDevice::Device(render.remove(0)), target: None, other_renderers: others, render: RenderDevice::Device(render.remove(0)), target: None, other_renderers: others, proxy_framebuffer: std::marker::PhantomData, log: target_api.log.clone(), }) } } /// If you are using [`MultiRenderer`]s do rendering of your client buffers, /// you can call `early_import` on commit to start necessary copy processes early. /// /// - `source` may specify on which gpu node the provided buffer is allocated, if applicable. /// - `target` referrs to the gpu node, that the buffer needs to be accessable on later. /// *Note*: Usually this will be **render**ing gpu of a [`MultiRenderer`] /// - `surface` is the wayland surface, whos buffer and subsurfaces buffers shall be imported /// - `target` referrs to the gpu node, that the buffer needs to be accessable on later. /// *Note*: Usually this will be **render**ing gpu of a [`MultiRenderer`] /// - `surface` is the wayland surface, whos buffer and subsurfaces buffers shall be imported /// /// Note: This will do nothing, if you are not using /// [`crate::backend::renderer::utils::on_commit_buffer_handler`] /// to let smithay handle buffer management. #[cfg(feature = "wayland_frontend")] pub fn early_import( &mut self, source: Option<DrmNode>, target: DrmNode, surface: &WlSurface, ) -> Result<(), Error<A, A>> where A: 'static, <A::Device as ApiDevice>::Renderer: ImportMemWl + ImportDmaWl + ExportMem, <<A::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping: 'static, { use crate::{ backend::renderer::utils::RendererSurfaceState, wayland::compositor::{with_surface_tree_upward, TraversalAction}, }; let mut result = Ok(()); with_surface_tree_upward( surface, (), |_surface, states, _| { if let Some(data) = states.data_map.get::<RefCell<RendererSurfaceState>>() { let mut data_ref = data.borrow_mut(); let data = &mut *data_ref; if data.textures.is_empty() { // Import a new buffer if available if let Some(buffer) = data.buffer.as_ref() { // We do an optimistic optimization here, so contrary to many much more defensive damage-tracking algorithms, // we only import the most recent set of damage here. // If we need more on rendering - which we cannot know at this point - we will call import_missing later // to receive the rest. let buffer_damage = data.damage().take(1).flatten().cloned().fold( Vec::<Rectangle<i32, BufferCoords>>::new(), |damage, mut rect| { // replace with drain_filter, when that becomes stable to reuse the original Vec's memory let (overlapping, mut new_damage): (Vec<_>, Vec<_>) = damage.into_iter().partition(|other| other.overlaps(rect)); for overlap in overlapping { rect = rect.merge(overlap); } new_damage.push(rect); new_damage }, ); if let Err(err) = self.early_import_buffer(source, target, buffer, states, &*buffer_damage) { result = Err(err); } } // Now, was the import successful? if result.is_ok() { TraversalAction::DoChildren(()) } else { // we are not displayed, so our children are neither TraversalAction::SkipChildren } } else { TraversalAction::SkipChildren } } else { TraversalAction::SkipChildren } }, |_, _, _| {}, |_, _, _| true, ); result } #[cfg(feature = "wayland_frontend")] fn early_import_buffer( &mut self, { match buffer_type(buffer) { Some(BufferType::Dma) => { let (mut target_device, mut others) = self .devices .iter_mut() .partition::<Vec<_>, _>(|device| device.node() == &target); let target_device = target_device.get_mut(0).ok_or(Error::DeviceMissing)?; let format = buffer.as_ref().user_data().get::<Dmabuf>().unwrap().format(); let source = source.filter(|source| source != &target); let can_import = source.as_ref().map(|_| CAN_IMPORT.lock().unwrap()); let might_import = source .and_then(|source| can_import.as_ref().unwrap().get(&(source, target, format))) .copied() .unwrap_or(true); if might_import { match target_device .renderer_mut() .import_dma_buffer(buffer, Some(surface), damage) { Ok(imported) => { if let (Some(ref mut can_import), Some(source)) = (can_import, source) { can_import.insert((source, target, format), true); } let mut texture = MultiTexture::from_surface(Some(surface), imported.size()); texture.insert_texture::<A>(target, imported); .renderer_mut() return Ok(()); } Err(err) => { slog::warn!( self.log, "Error importing dmabuf (format: {:?}) from {:?} to {}: {}", format, source, target, err ); slog::info!(self.log, "Falling back to cpu-copy."); if let (Some(ref mut can_import), Some(source)) = (can_import, source) { can_import.insert((source, target, format), false); } } } } // if we do need to do a memory copy, we start with the export here for import_renderer in others .iter_mut() .filter(|device| source.as_ref().map(|src| src == device.node()).unwrap_or(true)) { if let Ok(texture) = import_renderer .renderer_mut() .import_dma_buffer(buffer, Some(surface), damage) { let mut gpu_texture = MultiTexture::from_surface(Some(surface), texture.size()); let mappings = if gpu_texture.get::<A>(&target).is_none() { // force full copy .renderer_mut() .import_dma_buffer(buffer, Some(surface), damage) { if let Entry::Vacant(vacant) = dma_source { vacant.insert(*import_renderer.node()); } let mut gpu_texture = MultiTexture::from_surface(Some(surface), texture.size()); let mappings = if gpu_texture.get::<A>(&target).is_none() { // force full copy let damage = Rectangle::from_loc_and_size((0, 0), texture.size()); vec![( damage, import_renderer .renderer_mut() .copy_texture(&texture, damage) .map_err(Error::Target)?, )] } else { let damage = damage .iter() .flat_map(|rect| { rect.intersection(Rectangle::from_loc_and_size((0, 0), texture.size())) }) .fold(Vec::<Rectangle<i32, BufferCoords>>::new(), |damage, mut rect| { // replace with drain_filter, when that becomes stable to reuse the original Vec's memory let (overlapping, mut new_damage): (Vec<_>, Vec<_>) = damage.into_iter().partition(|other| other.overlaps(rect)); for overlap in overlapping { rect = rect.merge(overlap); } new_damage.push(rect); new_damage }); // do a partial copy damage .iter() .copied() .map(|damage| { let mapping = import_renderer .renderer_mut() .copy_texture(&texture, damage) .map_err(Error::Target)?; Ok((damage, mapping)) }) Ok(()) } Some(BufferType::Shm) => { // this is async anyway let shm_texture = self .devices .iter_mut() .find(|dev| dev.node() == &target) .ok_or(Error::DeviceMissing)? .renderer_mut() .import_shm_buffer(buffer, Some(surface), damage) .map_err(Error::<A, A>::Target)?; let mut texture = MultiTexture::from_surface(Some(surface), buffer_dimensions(buffer).unwrap()); texture.insert_texture::<A>(target, shm_texture); surface.data_map.insert_if_missing(|| texture.0); Ok(()) } None => { ); surface.data_map.insert_if_missing(|| gpu_texture.0); return Ok(()); } } Err(Error::ImportFailed) } #[cfg(all( feature = "wayland_frontend", feature = "backend_egl", feature = "use_system_lib" ))] Some(BufferType::Egl) => { // we need specialization for requiring ImportEGL // or require ImportAll, which will block this function for all // renderers that cannot import egl buffers, so we just don't // and sadly go the slow path Ok(()) } Some(BufferType::Shm) => { // we just need to upload in import_shm_buffer Ok(()) } None => { // welp, nothing we can do Ok(()) } } } } /// A graphics api, that supports enumerating graphics devices pub trait GraphicsApi { /// Devices this api produces type Device: ApiDevice; /// as well as transparently importing client buffers residing on different gpus. #[derive(Debug)] pub struct MultiRenderer<'a, 'b, R: GraphicsApi, T: GraphicsApi, Target> { source: *mut dyn FnMut(&wl_buffer::WlBuffer) -> Option<DrmNode>, render: RenderDevice<'a, R>, target: Option<&'b mut T::Device>, other_renderers: Vec<&'a mut R::Device>, /// Existing devices are guranteed to be not recreated fn enumerate(&self, list: &mut Vec<Self::Device>, log: &slog::Logger) -> Result<(), Self::Error>; } /// A device produced by a [`GraphicsApi`]. pub trait ApiDevice { /// The [`Renderer`](super::Renderer) this devices contains type Renderer: Renderer; /// Returns a reference to the underlying renderer fn renderer(&self) -> &Self::Renderer; /// Returns a mutable reference to the underlying renderer fn renderer_mut(&mut self) -> &mut Self::Renderer; /// Returns a [`DrmNode`] representing the graphics device fn node(&self) -> &DrmNode; } /// Renderer, that transparently copies rendering results to another gpu, /// as well as transparently importing client buffers residing on different gpus. #[derive(Debug)] pub struct MultiRenderer<'a, 'b, R: GraphicsApi, T: GraphicsApi, Target> { dma_source: Option<&'a mut HashMap<WeakDmabuf, DrmNode>>, render: RenderDevice<'a, R>, target: Option<&'b mut T::Device>, other_renderers: Vec<&'a mut R::Device>, proxy_framebuffer: std::marker::PhantomData<Target>, log: ::slog::Logger, } impl<'a, 'b, R: GraphicsApi, T: GraphicsApi, Target> AsRef<<R::Device as ApiDevice>::Renderer> for MultiRenderer<'a, 'b, R, T, Target> { fn as_ref(&self) -> &<R::Device as ApiDevice>::Renderer { self.render.renderer() } } impl<'a, 'b, R: GraphicsApi, T: GraphicsApi, Target> AsMut<<R::Device as ApiDevice>::Renderer> for MultiRenderer<'a, 'b, R, T, Target> { fn as_mut(&mut self) -> &mut <R::Device as ApiDevice>::Renderer { } } impl<'a, 'b, R: GraphicsApi, T: GraphicsApi, Target> Drop for MultiRenderer<'a, 'b, R, T, Target> { fn drop(&mut self) { let _ = unsafe { Box::from_raw(self.source as *mut _) }; } } /// [`Frame`](super::Frame) implementation of a [`MultiRenderer`]. #[derive(Debug)] pub struct MultiFrame<R: GraphicsApi, T: GraphicsApi> { enum RenderDevice<'a, A: GraphicsApi> { Device(&'a mut A::Device), // Hack to avoid lifetime problems in Renderer::render Renderer(*mut <A::Device as ApiDevice>::Renderer, DrmNode), } impl<'a, A: GraphicsApi> RenderDevice<'a, A> { /* fn unwrap_device(&mut self) -> &mut A::Device { match self { RenderDevice::Device(dev) => *dev, RenderDevice::Renderer(_, _) => panic!("unwrap called on RenderDevice::Renderer"), } } */ fn node(&self) -> &DrmNode { match self { RenderDevice::Device(dev) => dev.node(), RenderDevice::Renderer(_, node) => node, } } fn renderer(&self) -> &<A::Device as ApiDevice>::Renderer { match self { RenderDevice::Device(dev) => dev.renderer(), RenderDevice::Renderer(renderer, _) => unsafe { &**renderer }, } } fn renderer_mut(&mut self) -> &mut <A::Device as ApiDevice>::Renderer { match self { RenderDevice::Device(dev) => dev.renderer_mut(), RenderDevice::Renderer(renderer, _) => unsafe { &mut **renderer }, } } } /// [`Frame`](super::Frame) implementation of a [`MultiRenderer`]. #[derive(Debug)] pub struct MultiFrame<R: GraphicsApi, T: GraphicsApi> { node: DrmNode, // FIXME: With GAT this would not need to be a raw-pointer frame: *mut <<R::Device as ApiDevice>::Renderer as Renderer>::Frame, damage: Vec<Rectangle<i32, Physical>>, // We need this for the associated Error type of the Frame implementation _target: std::marker::PhantomData<T>, log: ::slog::Logger, } // These casts are ok, because the frame cannot outlive the MultiFrame, // see MultiRenderer::render for how this hack works and why it is necessary. impl<R: GraphicsApi, T: GraphicsApi> AsRef<<<R::Device as ApiDevice>::Renderer as Renderer>::Frame> for MultiFrame<R, T> { fn as_ref(&self) -> &<<R::Device as ApiDevice>::Renderer as Renderer>::Frame { unsafe { &*self.frame } } } impl<R: GraphicsApi, T: GraphicsApi> AsMut<<<R::Device as ApiDevice>::Renderer as Renderer>::Frame> for MultiFrame<R, T> { fn as_mut(&mut self) -> &mut <<R::Device as ApiDevice>::Renderer as Renderer>::Frame { unsafe { &mut *self.frame } } } impl<'a, 'b, R: GraphicsApi, T: GraphicsApi, Target> Unbind for MultiRenderer<'a, 'b, R, T, Target> where <T::Device as ApiDevice>::Renderer: Unbind, <R::Device as ApiDevice>::Renderer: Unbind, // We need this because the Renderer-impl does and Unbind requires Renderer R: 'static, R::Error: 'static, T::Error: 'static, <R::Device as ApiDevice>::Renderer: Offscreen<Target> + ExportDma + ExportMem + ImportDma + ImportMem, <T::Device as ApiDevice>::Renderer: ImportDma + ImportMem, <<R::Device as ApiDevice>::Renderer as Renderer>::Error: 'static, <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static, { fn unbind(&mut self) -> Result<(), <Self as Renderer>::Error> { if let Some(target) = self.target.as_mut() { target.renderer_mut().unbind().map_err(Error::Target) } else { self.render.renderer_mut().unbind().map_err(Error::Render) } } } impl<'a, 'b, R: GraphicsApi, T: GraphicsApi, Target, Other> Offscreen<Target> for MultiRenderer<'a, 'b, R, T, Other> where <T::Device as ApiDevice>::Renderer: Offscreen<Target>, <R::Device as ApiDevice>::Renderer: Offscreen<Target>, // We need these because the Bind-impl does and Offscreen requires Bind <T::Device as ApiDevice>::Renderer: Bind<Target>, <R::Device as ApiDevice>::Renderer: Bind<Target>, // We need these because the Unbind-impl does and Offscreen requires Bind, which requires Unbind <R::Device as ApiDevice>::Renderer: Unbind, <T::Device as ApiDevice>::Renderer: Unbind, // We need these because the Renderer-impl does and Offscreen requires Bind, which requires Unbind, which requires Renderer R: 'static, R::Error: 'static, T::Error: 'static, <R::Device as ApiDevice>::Renderer: Offscreen<Other> + ExportDma + ExportMem + ImportDma + ImportMem, <T::Device as ApiDevice>::Renderer: ImportDma + ImportMem, <<R::Device as ApiDevice>::Renderer as Renderer>::Error: 'static, <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static, { fn create_buffer(&mut self, size: Size<i32, BufferCoords>) -> Result<Target, <Self as Renderer>::Error> { if let Some(target) = self.target.as_mut() { target.renderer_mut().create_buffer(size).map_err(Error::Target) } else { self.render .renderer_mut() .create_buffer(size) .map_err(Error::Render) } } } impl<'a, 'b, R: GraphicsApi, T: GraphicsApi, Target, Other> Bind<Target> for MultiRenderer<'a, 'b, R, T, Other> where <T::Device as ApiDevice>::Renderer: Bind<Target>, <R::Device as ApiDevice>::Renderer: Bind<Target>, // We need these because the Unbind-impl does and Bind requires Unbind <R::Device as ApiDevice>::Renderer: Unbind, <T::Device as ApiDevice>::Renderer: Unbind, // We need this because the Renderer-impl does and Bind requires Unbind, which requires Renderer R: 'static, R::Error: 'static, T::Error: 'static, <R::Device as ApiDevice>::Renderer: Offscreen<Other> + ExportDma + ExportMem + ImportDma + ImportMem, <T::Device as ApiDevice>::Renderer: ImportDma + ImportMem, <<R::Device as ApiDevice>::Renderer as Renderer>::Error: 'static, <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static, { fn bind(&mut self, bind: Target) -> Result<(), <Self as Renderer>::Error> { if let Some(target) = self.target.as_mut() { target.renderer_mut().bind(bind).map_err(Error::Target) } else { self.render.renderer_mut().bind(bind).map_err(Error::Render) } } } let node = *self.render.node(); let source_ref = self.source; // we need to move some stuff into the closure temporarily let mut target = self.target.take(); let mut other_renderers = self.other_renderers.drain(..).collect::<Vec<_>>(); let target_ref = &mut target; let other_renderers_ref = &mut other_renderers; let log = self.log.clone(); let res = self .render .renderer_mut() .render(size, dst_transform, move |render, frame| { let mut new_renderer = MultiRenderer { source: source_ref, render: RenderDevice::Renderer(render, node), target: target_ref.take(), other_renderers: other_renderers_ref.drain(..).collect(), type Error = Error<R, T>; type TextureId = MultiTexture; type Frame = MultiFrame<R, T>; fn id(&self) -> usize { self.render.renderer().id() } fn downscale_filter(&mut self, filter: TextureFilter) -> Result<(), Self::Error> { }; let res = rendering(&mut new_renderer, &mut frame); // don't return them, but reset the reference, so we can restore self on error *target_ref = new_renderer.target.take(); *other_renderers_ref = new_renderer.other_renderers.drain(..).collect(); // don't free source std::mem::forget(new_renderer); (res, frame.damage) }) .map_err(Error::Render); // restore self self.target = target; self.other_renderers = other_renderers; // then possibly return the error let (res, damage) = res?; let mut damage = damage let buffer_size = size.to_logical(1).to_buffer(1, dst_transform); if self.target.is_some() { let render_buffer = Offscreen::<Target>::create_buffer(self.render.renderer_mut(), buffer_size) .map_err(Error::Render)?; self.render .renderer_mut() .bind(render_buffer) .map_err(Error::Render)?; } let node = *self.render.node(); // we need to move some stuff into the closure temporarily let mut dma_source = self.dma_source.take(); let mut target = self.target.take(); let mut other_renderers = self.other_renderers.drain(..).collect::<Vec<_>>(); let dma_source_ref = &mut dma_source; let target_ref = &mut target; let other_renderers_ref = &mut other_renderers; let log = self.log.clone(); let res = self .render .renderer_mut() .render(size, dst_transform, move |render, frame| { let mut new_renderer = MultiRenderer { dma_source: dma_source_ref.take(), render: RenderDevice::Renderer(render, node), target: target_ref.take(), other_renderers: other_renderers_ref.drain(..).collect(), proxy_framebuffer: std::marker::PhantomData, log: log.clone(), }; let mut frame = MultiFrame { node, frame, // we cheat here and use a raw-ptr, because otherwise your associated type would gain an uncostraint lifetime parameter damage: Vec::new(), _target: std::marker::PhantomData::<T>, log, }; let res = rendering(&mut new_renderer, &mut frame); // don't return yet, but first reset the references, so we can restore self on error *dma_source_ref = new_renderer.dma_source.take(); *target_ref = new_renderer.target.take(); *other_renderers_ref = new_renderer.other_renderers.drain(..).collect(); (res, frame.damage) }) .map_err(Error::Render); // restore self self.dma_source = dma_source; self.target = target; self.other_renderers = other_renderers; // then possibly return the error let (res, damage) = res?; let mut damage = damage .into_iter() .map(|rect| { rect.to_logical(1) .to_buffer(1, dst_transform, &size.to_logical(1)) }) .collect::<Vec<_>>(); if let Some(target) = self.target.as_mut() { { let mut can_import = CAN_IMPORT.lock().unwrap(); let dmabuf = self .render .renderer_mut() .export_framebuffer(buffer_size) .map_err(Error::Render)?; let might_import = *can_import .get(&(*self.render.node(), *target.node(), dmabuf.format())) .unwrap_or(&true); if might_import { // try gpu copy match target.renderer_mut().import_dmabuf(&dmabuf, Some(&damage)) { Ok(texture) => { // import successful let damage = damage .iter() .map(|rect| rect.to_logical(1, dst_transform, &buffer_size).to_physical(1)) .collect::<Vec<_>>(); target .renderer_mut() .render(size, dst_transform, |_renderer, frame| { frame.render_texture_from_to( &texture, Rectangle::from_loc_and_size((0, 0), buffer_size).to_f64(), Rectangle::from_loc_and_size((0, 0), size), &damage, dst_transform.invert(), 1.0, ) }) .and_then(std::convert::identity) .map_err(Error::Target)?; can_import.insert((*self.render.node(), *target.node(), dmabuf.format()), true); return Ok(res); } Err(err) => { let (source, target, format) = (*self.render.node(), *target.node(), dmabuf.format()); slog::warn!( self.log, "Error importing dmabuf (format: {:?}) from {} to {}: {}", format, source, target, err ); slog::info!(self.log, "Falling back to cpu-copy."); can_import.insert((source, target, format), false); } } } } // cpu copy if damage.len() > MAX_CPU_COPIES { damage = Vec::from([Rectangle::from_loc_and_size((0, 0), buffer_size)]); } damage.dedup(); damage.retain(|rect| rect.overlaps(Rectangle::from_loc_and_size((0, 0), buffer_size))); damage.retain(|rect| rect.size.h > 0 && rect.size.w > 0); // merge overlapping rectangles damage = damage.into_iter().fold(Vec::new(), |new_damage, mut rect| { // replace with drain_filter, when that becomes stable to reuse the original Vec's memory let (overlapping, mut new_damage): (Vec<_>, Vec<_>) = new_damage.into_iter().partition(|other| other.overlaps(rect)); for overlap in overlapping { rect = rect.merge(overlap); } new_damage.push(rect); new_damage }); let mut mappings = Vec::new(); for rect in damage { let mapping = ( self.render .renderer_mut() .copy_framebuffer(rect) .map_err(Error::Render)?, rect, ); mappings.push(mapping); } mappings.sort_by(|map1, map2| { let size1 = map1.1.size; let size2 = map2.1.size; (size1.w * size1.h).cmp(&(size2.w * size2.h)) }); let render = &mut self.render; target .renderer_mut() .render(size, dst_transform, move |target, frame| { for mapping in mappings { let slice = render .renderer_mut() .map_texture(&mapping.0) .map_err(Error::Render::<R, T>)?; let texture = target .import_memory(slice, mapping.1.size, false) .map_err(Error::Target)?; let dst = mapping .1 .to_logical(1, Transform::Normal, &buffer_size) .to_physical(1); frame .render_texture_from_to( &texture, Rectangle::from_loc_and_size((0, 0), mapping.1.size).to_f64(), dst, &[Rectangle::from_loc_and_size((0, 0), dst.size)], Transform::Normal, 1.0, ) .map_err(Error::Target)?; } Ok(()) }) .map_err(Error::Target) .and_then(std::convert::identity)?; } Ok(res) } } /// [`Texture`](super::Texture)s produced by a [`MultiRenderer`]. #[derive(Debug, Clone)] pub struct MultiTexture(Rc<RefCell<MultiTextureInternal>>); #[derive(Debug)] struct MultiTextureInternal { textures: HashMap<TypeId, HashMap<DrmNode, GpuSingleTexture>>, size: Size<i32, BufferCoords>, } type DamageAnyTextureMappings = Vec<(Rectangle<i32, BufferCoords>, Box<dyn Any + 'static>)>; #[derive(Debug)] struct GpuSingleTexture { mapping: Option<(DrmNode, DamageAnyTextureMappings)>, texture: Option<Box<dyn Any + 'static>>, } impl MultiTexture { #[cfg(feature = "wayland_frontend")] fn from_surface( surface: Option<&crate::wayland::compositor::SurfaceData>, size: Size<i32, BufferCoords>, ) -> MultiTexture { let internal = surface .and_then(|surface| { surface .data_map .get::<Rc<RefCell<MultiTextureInternal>>>() .cloned() }) .unwrap_or_else(|| { Rc::new(RefCell::new(MultiTextureInternal { textures: HashMap::new(), size, })) }); { let mut internal = internal.borrow_mut(); if internal.size != size { internal.textures.clear(); internal.size = size; } } MultiTexture(internal) } fn new(size: Size<i32, BufferCoords>) -> MultiTexture { MultiTexture(Rc::new(RefCell::new(MultiTextureInternal { textures: HashMap::new(), size, }))) } fn get<A: GraphicsApi + 'static>( &self, render: &DrmNode, ) -> Option<Ref<'_, <<A::Device as ApiDevice>::Renderer as Renderer>::TextureId>> where <<A::Device as ApiDevice>::Renderer as Renderer>::TextureId: 'static, { let tex = self.0.borrow(); // TODO: use Ref::filter_map when stabilized if tex .textures .get(&TypeId::of::<A>()) .and_then(|textures| textures.get(render)) .and_then(|texture| texture.texture.as_ref()) .and_then(|texture| { <dyn Any>::downcast_ref::<<<A::Device as ApiDevice>::Renderer as Renderer>::TextureId>( &**texture, ) }) .is_some() { Some(Ref::map(tex, |tex| { tex.textures.get(&TypeId::of::<A>()) .and_then(|textures| textures.get(render)) .and_then(|texture| texture.texture.as_ref()) .and_then(|texture| <dyn Any>::downcast_ref::<<<A::Device as ApiDevice>::Renderer as Renderer>::TextureId>(&**texture)) .unwrap() })) } else { None } } fn insert_texture<A: GraphicsApi + 'static>( &mut self, render: DrmNode, texture: <<A::Device as ApiDevice>::Renderer as Renderer>::TextureId, ) where <<A::Device as ApiDevice>::Renderer as Renderer>::TextureId: 'static, { let mut tex = self.0.borrow_mut(); slog::trace!( crate::slog_or_fallback(None), "Inserting into: {:p} for {:?}: {:?}", self.0.as_ptr(), render, tex ); let textures = tex.textures.entry(TypeId::of::<A>()).or_default(); textures.insert( render, GpuSingleTexture { mapping: None, texture: Some(Box::new(texture) as Box<_>), }, ); } fn insert_mapping< R: GraphicsApi + 'static, T: GraphicsApi + 'static, I: Iterator< Item = ( Rectangle<i32, BufferCoords>, <<T::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping, ), >, >( &mut self, source: DrmNode, render: DrmNode, size: Size<i32, BufferCoords>, new_mappings: I, ) where <T::Device as ApiDevice>::Renderer: ExportMem, <<T::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping: 'static, { let mut tex = self.0.borrow_mut(); let textures = tex.textures.entry(TypeId::of::<R>()).or_default(); let (old_texture, old_mapping) = textures .remove(&render) .map(|single| (single.texture, single.mapping)) .unwrap_or((None, None)); let old_texture = old_texture.filter(|tex| { <dyn Any>::downcast_ref::<<<R::Device as ApiDevice>::Renderer as Renderer>::TextureId>(tex) .map(|tex| tex.size()) == Some(size) }); let mut mappings = old_mapping .filter(|(old_src, _)| *old_src == source) .map(|(_, mappings)| mappings) .unwrap_or_default(); // don't keep old mappings that are superseeded by new ones let new_mappings = new_mappings .map(|(r, m)| (r, Box::new(m) as Box<_>)) .collect::<Vec<_>>(); mappings.retain(|(region, _)| { !new_mappings .iter() .any(|(new_region, _)| new_region.contains_rect(*region)) }); mappings.extend(new_mappings); textures.insert( render, GpuSingleTexture { mapping: Some((source, mappings)), texture: old_texture, }, ); } } impl Texture for MultiTexture { fn size(&self) -> Size<i32, BufferCoords> { self.0.borrow().size } fn width(&self) -> u32 { self.0.borrow().size.w as u32 } fn height(&self) -> u32 { self.0.borrow().size.h as u32 } } impl<R: GraphicsApi, T: GraphicsApi> Frame for MultiFrame<R, T> where R: 'static, R::Error: 'static, T::Error: 'static, <<R::Device as ApiDevice>::Renderer as Renderer>::Error: 'static, <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static, { type Error = Error<R, T>; type TextureId = MultiTexture; fn clear(&mut self, color: [f32; 4], at: &[Rectangle<i32, Physical>]) -> Result<(), Self::Error> { self.damage.extend(at); unsafe { &mut *self.frame } .clear(color, at) .map_err(Error::Render) } fn render_texture_from_to( &mut self, texture: &Self::TextureId, src: Rectangle<f64, BufferCoords>, dst: Rectangle<i32, Physical>, damage: &[Rectangle<i32, Physical>], src_transform: Transform, alpha: f32, ) -> Result<(), Self::Error> { if let Some(texture) = texture.get::<R>(&self.node) { self.damage.extend(damage.iter().map(|rect| { let rect = rect.to_f64(); let dst = dst.to_f64(); let (x, y, w, h) = (rect.loc.x, rect.loc.y, rect.size.w, rect.size.h); Rectangle::from_loc_and_size( ( ((x - src.loc.x) / src.size.w * dst.size.w) + dst.loc.x, ((y - src.loc.y) / src.size.h * dst.size.h) + dst.loc.y, ), (w / src.size.w * dst.size.w, h / src.size.h * dst.size.h), ) .to_i32_up() })); unsafe { &mut *self.frame } .render_texture_from_to(&*texture, src, dst, damage, src_transform, alpha) .map_err(Error::Render) } else { slog::warn!( self.log, "Failed to render texture {:?}, import for wrong devices {:?}? {:?}", texture.0.as_ptr(), self.node, texture.0.borrow(), ); Ok(()) } } fn transformation(&self) -> Transform { unsafe { &mut *self.frame }.transformation() } } #[cfg(feature = "wayland_frontend")] impl<'a, 'b, R: GraphicsApi, T: GraphicsApi, Target> ImportMemWl for MultiRenderer<'a, 'b, R, T, Target> where <R::Device as ApiDevice>::Renderer: ImportMemWl, // We need this because the Renderer-impl does and ImportMem requires Renderer R: 'static, R::Error: 'static, T::Error: 'static, <R::Device as ApiDevice>::Renderer: Offscreen<Target> + ExportDma + ExportMem + ImportDma + ImportMem, <T::Device as ApiDevice>::Renderer: ImportDma + ImportMem, <<R::Device as ApiDevice>::Renderer as Renderer>::Error: 'static, .user_data() .get::<Dmabuf>() .expect("import_dma_buffer without checking buffer type?"); let tranch = unsafe { &mut *self.source }(buffer); let texture = MultiTexture::from_surface(surface, dmabuf.size()); let texture_ref = texture.0.clone(); let res = self.import_dmabuf_internal(tranch, dmabuf, texture, Some(damage)); if res.is_ok() { if let Some(surface) = surface { surface.data_map.insert_if_missing(|| texture_ref); .render .renderer_mut() .import_shm_buffer(buffer, surface, damage) .expect("import_shm_buffer without checking buffer type?"); let dimensions = shm::with_buffer_contents(buffer, |_, data| (data.width, data.height).into()) .map_err(|_| Error::ImportFailed)?; let mut texture = MultiTexture::from_surface(surface, dimensions); texture.insert_texture::<R>(*self.render.node(), shm_texture); Ok(texture) } fn shm_formats(&self) -> &[wl_shm::Format] { self.render.renderer().shm_formats() } } impl<'a, 'b, R: GraphicsApi, T: GraphicsApi, Target> ImportMem for MultiRenderer<'a, 'b, R, T, Target> where <R::Device as ApiDevice>::Renderer: ImportMem, // We need this because the Renderer-impl does and ImportMem requires Renderer R: 'static, R::Error: 'static, T::Error: 'static, <R::Device as ApiDevice>::Renderer: Offscreen<Target> + ExportDma + ExportMem + ImportDma + ImportMem, <T::Device as ApiDevice>::Renderer: ImportDma + ImportMem, <<R::Device as ApiDevice>::Renderer as Renderer>::Error: 'static, <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static, { fn import_memory( &mut self, data: &[u8], size: Size<i32, BufferCoords>, flipped: bool, ) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error> { let mem_texture = self .render .renderer_mut() .import_memory(data, size, flipped) .map_err(Error::Render)?; let mut texture = MultiTexture::new(size); texture.insert_texture::<R>(*self.render.node(), mem_texture); Ok(texture) } fn update_memory( &mut self, texture: &<Self as Renderer>::TextureId, data: &[u8], region: Rectangle<i32, BufferCoords>, ) -> Result<(), <Self as Renderer>::Error> { let texture = MultiTexture::new(texture.size()); let mem_texture = texture mut texture: MultiTexture, damage: Option<&[Rectangle<i32, BufferCoords>]>, ) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error> { let source = source.filter(|source| source != self.render.node()); let can_import = source.as_ref().map(|_| CAN_IMPORT.lock().unwrap()); let might_import = source .and_then(|source| { can_import .as_ref() .unwrap() .get(&(source, *self.render.node(), dmabuf.format())) }) .copied() .unwrap_or(true); if might_import { match self.render.renderer_mut().import_dmabuf(dmabuf, damage) { Ok(imported) => { if let (Some(ref mut can_import), Some(source)) = (can_import, source) { can_import.insert((source, *self.render.node(), dmabuf.format()), true); } texture.insert_texture::<R>(*self.render.node(), imported); return Ok(texture); } Err(err) => { let target = *self.render.node(); slog::warn!( self.log, "Error importing dmabuf (format: {:?}) from {:?} to {}: {}", dmabuf.format(), source, target, err ); slog::info!(self.log, "Falling back to cpu-copy."); if let (Some(ref mut can_import), Some(source)) = (can_import, source) { can_import.insert((source, target, dmabuf.format()), false); } } } } // lets check if we don't have a mapping let size = texture.0.borrow().size; let needs_reimport = texture } .borrow_mut() .textures .get_mut(&TypeId::of::<R>()) .and_then(|nodes_textures| nodes_textures.get_mut(self.render.node())) .map(|texture| match texture.mapping.as_ref() { None => true, // in the few cases, were we need to rerender more, then was damaged by the client, <T::Device as ApiDevice>::Renderer: ExportMem, <<R::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping: 'static, <<T::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping: 'static, T: 'static, // We need this because the Renderer-impl does and ImportDma requires Renderer R: 'static, <R::Device as ApiDevice>::Renderer: Offscreen<Target> + ExportDma + ExportMem + ImportDma + ImportMem, <T::Device as ApiDevice>::Renderer: ImportDma + ImportMem, <<R::Device as ApiDevice>::Renderer as Renderer>::Error: 'static, <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static, { fn dmabuf_formats<'c>(&'c self) -> Box<dyn Iterator<Item = &'c Format> + 'c> { .unwrap_or(true); if needs_reimport { // no (usable) early-import :( if let Some(import_renderer) = self.target.as_mut() { if let Ok(dma_texture) = import_renderer.renderer_mut().import_dmabuf(dmabuf, damage) { let mappings = damage .unwrap_or(&[Rectangle::from_loc_and_size((0, 0), dma_texture.size())]) .iter() where <R::Device as ApiDevice>::Renderer: ImportDma + ImportMem + ExportMem, <T::Device as ApiDevice>::Renderer: ExportMem, <<R::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping: 'static, <<T::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping: 'static, T: 'static, // We need this because the Renderer-impl does and ImportDma requires Renderer R: 'static, <R::Device as ApiDevice>::Renderer: Offscreen<Target> + ExportDma + ExportMem + ImportDma + ImportMem, <T::Device as ApiDevice>::Renderer: ImportDma + ImportMem, <<R::Device as ApiDevice>::Renderer as Renderer>::Error: 'static, <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static, { fn import_missing( &mut self, } } for import_renderer in self .other_renderers .iter_mut() .filter(|device| source.as_ref().map(|src| src == device.node()).unwrap_or(true)) { if let Ok(dma_texture) = import_renderer.renderer_mut().import_dmabuf(dmabuf, damage) { let mappings = damage .unwrap_or(&[Rectangle::from_loc_and_size((0, 0), texture.size())]) if let Entry::Vacant(vacant) = dma_source { vacant.insert(*import_renderer.node()); } let mappings = new_damage .into_iter() .map(|damage| { let mapping = import_renderer .renderer_mut() .copy_texture(&dma_texture, damage) .map_err(Error::Target)?; slog::debug!( crate::slog_or_fallback(None), "Creating mapping for: {:?}", damage ); Ok((damage, mapping)) }) .collect::<Result<Vec<_>, Error<R, T>>>()?; texture.insert_mapping::<R, T, _>( *import_renderer.node(), *self.render.node(), dma_texture.size(), mappings.into_iter(), ); return Ok(()); } } let (first, last) = match source { Some(s) => self .other_renderers .iter_mut() .partition::<Vec<_>, _>(|other| other.node() == &s), None => (Vec::new(), self.other_renderers.iter_mut().collect()), }; for import_renderer in first.into_iter().chain(last.into_iter()) { if let Ok(dma_texture) = import_renderer .renderer_mut() .import_dmabuf(dmabuf, Some(&*new_damage)) { let mappings = new_damage .into_iter() .map(|damage| { let mapping = import_renderer .renderer_mut() .copy_texture(&dma_texture, damage) .map_err(Error::Render)?; Ok((damage, mapping)) }) .collect::<Result<Vec<_>, Error<R, T>>>()?; texture.insert_mapping::<R, R, _>( *import_renderer.node(), *self.render.node(), texture.size(), mappings.into_iter(), ); return Ok(()); } } Err(Error::ImportFailed) } fn import_dmabuf_internal( &mut self, source: Option<DrmNode>, dmabuf: &Dmabuf, mut texture: MultiTexture, damage: Option<&[Rectangle<i32, BufferCoords>]>, ) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error> { let dma_source = self.dma_source.as_mut().unwrap().entry(dmabuf.weak()); if matches!(dma_source, Entry::Vacant(_)) || matches!(dma_source, Entry::Occupied(ref x) if x.get() == self.render.node()) { match self.render.renderer_mut().import_dmabuf(dmabuf, damage) { Ok(imported) => { if let Entry::Vacant(vacant) = dma_source { vacant.insert(*self.render.node()); } texture.insert_texture::<R>(*self.render.node(), imported); return Ok(texture); } Err(err) => { slog::trace!( self.log, "Error importing dmabuf (format: {:?}) to {}: {}", dmabuf.format(), self.render.node(), err ); slog::trace!(self.log, "Falling back to cpu-copy."); } } } let damage = damage.map(|damage| { damage .iter() .flat_map(|rect| rect.intersection(Rectangle::from_loc_and_size((0, 0), dmabuf.size()))) .fold(Vec::<Rectangle<i32, BufferCoords>>::new(), |damage, mut rect| { // replace with drain_filter, when that becomes stable to reuse the original Vec's memory let (overlapping, mut new_damage): (Vec<_>, Vec<_>) = damage.into_iter().partition(|other| other.overlaps(rect)); for overlap in overlapping { rect = rect.merge(overlap); } new_damage.push(rect); new_damage }) }); let source = if let Entry::Occupied(ref occ) = dma_source { Some(*occ.get()) } else { source }; let render_node = self.render.node(); // lets check if we don't have a mapping let size = texture.0.borrow().size; let new_damage = texture .0 .borrow_mut() .textures .get_mut(&TypeId::of::<R>()) .and_then(|nodes_textures| nodes_textures.get_mut(render_node)) .map(|texture| match texture.mapping.as_ref() { None => vec![Rectangle::from_loc_and_size((0, 0), size)], // in the few cases, were we need to rerender more, than was damaged by the client, // we might have not been continuously rendering this buffer. So we need to assume, // everything might have been damaged in the meantime. // In those cases we cannot assume that our existing texture + early-import is sufficiently // recent and we need to reimport. Some((_, mappings)) => damage .as_ref() .filter(|_| texture.texture.is_some()) // we need a full import in that case .cloned() .unwrap_or_else(|| vec![Rectangle::from_loc_and_size((0, 0), size)]) .into_iter() .filter(|rect| !mappings.iter().any(|(region, _)| region.contains_rect(*rect))) .collect(), }) .unwrap_or_else(|| vec![Rectangle::from_loc_and_size((0, 0), size)]); slog::trace!( crate::slog_or_fallback(None), "Copying dmabuf {:?} from {:?} to {:?} for {:?}: {:?}", dmabuf.handles().collect::<Vec<_>>(), source, render_node, texture.0.as_ptr(), texture.0.borrow(), ); if !new_damage.is_empty() { // no (complete) early-import :( slog::trace!( crate::slog_or_fallback(None), "Missing damage {:?}: {:?}", texture.0.as_ptr(), new_damage ); self.import_missing(new_damage, source, dmabuf, &mut texture)?; } // else we have an early import(!) let mut texture_ref = texture.0.borrow_mut(); let tex = texture_ref .textures .get_mut(&TypeId::of::<R>()) .unwrap() .get_mut(self.render.node()) .unwrap(); let (foreign_node, mappings) = tex.mapping.take().unwrap(); if tex.texture.is_none() { // full upload let new_texture = if let Some(source) = self .target .as_mut() .filter(|target| target.node() == &foreign_node) { assert!(mappings[0].0.loc == (0, 0).into()); assert!(mappings[0].0.size == size); let mapping = <dyn Any>::downcast_ref::< <<T::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping, >(&*mappings[0].1) .unwrap(); slog::trace!( crate::slog_or_fallback(None), "Importing mapping as full buffer {:?}", mapping.size() ); let mapped = source .renderer_mut() .map_texture(mapping) .map_err(Error::Target)?; Some( self.render .renderer_mut() .import_memory(mapped, mapping.size(), false) .map_err(Error::Render)?, ) } else if let Some(source) = self .other_renderers .iter_mut() .find(|device| device.node() == &foreign_node) { assert!(mappings[0].0.loc == (0, 0).into()); assert!(mappings[0].0.size == size); let mapping = <dyn Any>::downcast_ref::< <<R::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping, >(&*mappings[0].1) .unwrap(); slog::trace!( crate::slog_or_fallback(None), "Importing mapping as full buffer {:?}", mapping.size() ); let mapped = source .renderer_mut() .map_texture(mapping) .map_err(Error::Render)?; Some( self.render .renderer_mut() .import_memory(mapped, mapping.size(), false) .map_err(Error::Render)?, ) } else { slog::warn!( crate::slog_or_fallback(None), "Failed to find device for importing: {:?}", dmabuf ); None }; if let Some(new_texture) = new_texture { tex.texture = Some(Box::new(new_texture) as Box<_>); } } else { // update let texture = <dyn Any>::downcast_ref::< <<R::Device as ApiDevice>::Renderer as Renderer>::TextureId, >(tex.texture.as_ref().unwrap()) .unwrap(); if let Some(source) = self .target .as_mut() .filter(|target| target.node() == &foreign_node) { for (region, mapping) in mappings { let mapping = <dyn Any>::downcast_ref::< <<T::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping, >(&*mapping) .unwrap(); slog::trace!( crate::slog_or_fallback(None), "Updating texture {:?} with mapping at {:?}", texture.size(), region ); let mapped = source .renderer_mut() .map_texture(mapping) .map_err(Error::Target)?; self.render .renderer_mut() .update_memory(texture, mapped, region) .map_err(Error::Render)?; } } else if let Some(source) = self .other_renderers .iter_mut() .find(|device| device.node() == &foreign_node) { for (region, mapping) in mappings { let mapping = <dyn Any>::downcast_ref::< <<R::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping, >(&*mapping) .unwrap(); slog::trace!( crate::slog_or_fallback(None), "Updating texture {:?} with mapping at {:?}", texture.size(), region ); let mapped = source .renderer_mut() .map_texture(mapping) .map_err(Error::Render)?; self.render .renderer_mut() .update_memory(texture, mapped, region) .map_err(Error::Render)?; } } else { slog::warn!( crate::slog_or_fallback(None), "Failed to find device for updating: {:?}", dmabuf ); }; } std::mem::drop(texture_ref); Ok(texture) } } /// [`TextureMapping`](super::TextureMapping)s produced by [`ExportMem`]-implementations of /// [`MultiRenderer`]s. pub struct MultiTextureMapping<A: GraphicsApi, B: GraphicsApi>(TextureMappingInternal<A, B>) where <A::Device as ApiDevice>::Renderer: ExportMem, <B::Device as ApiDevice>::Renderer: ExportMem; enum TextureMappingInternal<A: GraphicsApi, B: GraphicsApi> where <A::Device as ApiDevice>::Renderer: ExportMem, <B::Device as ApiDevice>::Renderer: ExportMem, { Either(<<A::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping), Or(<<B::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping), } impl<A: GraphicsApi, B: GraphicsApi> fmt::Debug for MultiTextureMapping<A, B> where <A::Device as ApiDevice>::Renderer: ExportMem, <B::Device as ApiDevice>::Renderer: ExportMem, <<A::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping: fmt::Debug, <<B::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self.0 { TextureMappingInternal::Either(ref mapping) => mapping.fmt(f), TextureMappingInternal::Or(ref mapping) => mapping.fmt(f), } } } impl<A: GraphicsApi, B: GraphicsApi> Texture for MultiTextureMapping<A, B> where <A::Device as ApiDevice>::Renderer: ExportMem, <B::Device as ApiDevice>::Renderer: ExportMem, { fn size(&self) -> Size<i32, BufferCoords> { match self { MultiTextureMapping::<A, B>(TextureMappingInternal::Either(x)) => x.size(), MultiTextureMapping::<A, B>(TextureMappingInternal::Or(x)) => x.size(), } } fn width(&self) -> u32 { match self { MultiTextureMapping::<A, B>(TextureMappingInternal::Either(x)) => x.width(), MultiTextureMapping::<A, B>(TextureMappingInternal::Or(x)) => x.width(), } } fn height(&self) -> u32 { match self { MultiTextureMapping::<A, B>(TextureMappingInternal::Either(x)) => x.height(), MultiTextureMapping::<A, B>(TextureMappingInternal::Or(x)) => x.height(), } } } impl<A: GraphicsApi, B: GraphicsApi> TextureMapping for MultiTextureMapping<A, B> where <A::Device as ApiDevice>::Renderer: ExportMem, <B::Device as ApiDevice>::Renderer: ExportMem, { fn flipped(&self) -> bool { match self { MultiTextureMapping::<A, B>(TextureMappingInternal::Either(x)) => x.flipped(), MultiTextureMapping::<A, B>(TextureMappingInternal::Or(x)) => x.flipped(), } } } impl<'a, 'b, R: GraphicsApi, T: GraphicsApi, Target> ExportMem for MultiRenderer<'a, 'b, R, T, Target> where <T::Device as ApiDevice>::Renderer: ExportMem, <R::Device as ApiDevice>::Renderer: ExportMem, // We need this because the Renderer-impl does and ExportMem requires Renderer R: 'static, R::Error: 'static, T::Error: 'static, <R::Device as ApiDevice>::Renderer: Offscreen<Target> + ExportDma + ExportMem + ImportDma + ImportMem, <T::Device as ApiDevice>::Renderer: ImportDma + ImportMem, <<R::Device as ApiDevice>::Renderer as Renderer>::Error: 'static, <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static, { type TextureMapping = MultiTextureMapping<T, R>; fn copy_framebuffer( &mut self, region: Rectangle<i32, BufferCoords>, ) -> Result<Self::TextureMapping, <Self as Renderer>::Error> { if let Some(target) = self.target.as_mut() { target .renderer_mut() .copy_framebuffer(region) .map(|mapping| MultiTextureMapping(TextureMappingInternal::Either(mapping))) .map_err(Error::Target) } else { self.render .renderer_mut() .copy_framebuffer(region) .map(|mapping| MultiTextureMapping(TextureMappingInternal::Or(mapping))) .map_err(Error::Render) } } fn copy_texture( &mut self, texture: &Self::TextureId, region: Rectangle<i32, BufferCoords>, ) -> Result<Self::TextureMapping, Self::Error> { let tex = texture .get::<R>(self.render.node()) .ok_or_else(|| Error::MismatchedDevice(*self.render.node()))?; self.render .renderer_mut() .copy_texture(&*tex, region) .map(|mapping| MultiTextureMapping(TextureMappingInternal::Or(mapping))) .map_err(Error::Render) } fn map_texture<'c>( &mut self, texture_mapping: &'c Self::TextureMapping, ) -> Result<&'c [u8], <Self as Renderer>::Error> { match texture_mapping { MultiTextureMapping(TextureMappingInternal::Either(target_mapping)) => self .target .as_mut() .unwrap() .renderer_mut() .map_texture(target_mapping) .map_err(Error::Target), MultiTextureMapping(TextureMappingInternal::Or(render_mapping)) => self .render .renderer_mut() .map_texture(render_mapping) .map_err(Error::Render), } } } impl<'a, 'b, R: GraphicsApi, T: GraphicsApi, Target> ExportDma for MultiRenderer<'a, 'b, R, T, Target> where <T::Device as ApiDevice>::Renderer: ExportDma, // We need this because the Renderer-impl does and ExportDma requires Renderer R: 'static, R::Error: 'static, T::Error: 'static, <R::Device as ApiDevice>::Renderer: Offscreen<Target> + ExportDma + ExportMem + ImportDma + ImportMem, <T::Device as ApiDevice>::Renderer: ImportDma + ImportMem, <<R::Device as ApiDevice>::Renderer as Renderer>::Error: 'static, <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static, { fn export_framebuffer( &mut self, size: Size<i32, BufferCoords>, ) -> Result<Dmabuf, <Self as Renderer>::Error> { if let Some(target) = self.target.as_mut() { target .renderer_mut() .export_framebuffer(size) .map_err(Error::Target) } else { self.render .renderer_mut() .export_framebuffer(size) .map_err(Error::Render) } } fn export_texture( &mut self, texture: &<Self as Renderer>::TextureId, ) -> Result<Dmabuf, <Self as Renderer>::Error> { let tex = texture .get::<R>(self.render.node()) .ok_or_else(|| Error::MismatchedDevice(*self.render.node()))?; self.render .renderer_mut() .export_texture(&*tex) .map_err(Error::Render) } } <MSG> multigpu: Remove `source` param to not depend on wl <DFF> @@ -37,7 +37,7 @@ use super::*; use std::{ any::{Any, TypeId}, cell::{Ref, RefCell}, - collections::HashMap, + collections::{hash_map::Entry, HashMap}, fmt, rc::Rc, sync::Mutex, @@ -47,7 +47,7 @@ use std::{ use crate::reexports::wayland_server::protocol::wl_surface::WlSurface; use crate::{ backend::{ - allocator::{Buffer, Format}, + allocator::{dmabuf::WeakDmabuf, Buffer, Format}, drm::DrmNode, SwapBuffersError, }, @@ -66,6 +66,7 @@ lazy_static::lazy_static! { pub struct GpuManager<A: GraphicsApi> { api: A, devices: Vec<A::Device>, + dma_source: HashMap<WeakDmabuf, DrmNode>, log: ::slog::Logger, } @@ -162,14 +163,16 @@ impl<A: GraphicsApi> GpuManager<A> { return Err(Error::NoDevices); } - Ok(GpuManager { api, devices, log }) + Ok(GpuManager { + api, + devices, + dma_source: HashMap::new(), + log, + }) } /// Create a [`MultiRenderer`]. /// - /// - `source_device` should be a function to tell the renderer, which gpu a client buffer is allocated on, - /// if applicable (e.g. for dmabuf based buffers). In cases where this would be meaningless (e.g. shm-based buffers) - /// or the origin of a buffer cannot be determined with certainty, this function needs to return `None`. /// - `render_device` should referr to the gpu node rendering operations will take place upon. /// - `target_device` should referr to the gpu node the composited buffer will end up upon /// @@ -177,15 +180,13 @@ impl<A: GraphicsApi> GpuManager<A> { /// transferring the data to the `target_device`. Referr to [`Offscreen`](super::Offscreen)-implementations /// to find supported options and referr to the documentations of the used `GraphicsApi` for possible /// (performance) implication of selecting a specific `Target`. - pub fn renderer<'a, F, Target>( + pub fn renderer<'a, Target>( &'a mut self, - source_device: F, render_device: &DrmNode, target_device: &DrmNode, ) -> Result<MultiRenderer<'a, 'a, A, A, Target>, Error<A, A>> where <A::Device as ApiDevice>::Renderer: Offscreen<Target>, - F: FnMut(&wl_buffer::WlBuffer) -> Option<DrmNode> + 'static, { if !self.devices.iter().any(|device| device.node() == render_device) || !self.devices.iter().any(|device| device.node() == target_device) @@ -212,7 +213,7 @@ impl<A: GraphicsApi> GpuManager<A> { .partition::<Vec<_>, _>(|device| device.node() == target_device); Ok(MultiRenderer { - source: Box::leak(Box::new(source_device)), + dma_source: Some(&mut self.dma_source), render: RenderDevice::Device(render.remove(0)), target: Some(target.remove(0)), other_renderers: others, @@ -221,7 +222,7 @@ impl<A: GraphicsApi> GpuManager<A> { }) } else { Ok(MultiRenderer { - source: Box::leak(Box::new(source_device)), + dma_source: Some(&mut self.dma_source), render: RenderDevice::Device(render.remove(0)), target: None, other_renderers: others, @@ -235,9 +236,6 @@ impl<A: GraphicsApi> GpuManager<A> { /// /// - `render_api` should be the [`GpuManager`] used for the `render_device`. /// - `target_api` should be the [`GpuManager`] used for the `target_device`. - /// - `source_device` should be a function to tell the renderer, which gpu a client buffer is allocated on, - /// if applicable (e.g. for dmabuf based buffers). In cases where this would be meaningless (e.g. shm-based buffers) - /// or the origin of a buffer cannot be determined with certainty, this function needs to return `None`. /// - `render_device` should referr to the gpu node rendering operations will take place upon. /// - `target_device` should referr to the gpu node the composited buffer will end up upon /// @@ -245,16 +243,14 @@ impl<A: GraphicsApi> GpuManager<A> { /// transferring the data to the `target_device`. Referr to [`Offscreen`](super::Offscreen)-implementations /// to find supported options and referr to the documentations of the used `GraphicsApi` for possible /// (performance) implication of selecting a specific `Target`. - pub fn cross_renderer<'a, 'b, B: GraphicsApi, F, Target>( + pub fn cross_renderer<'a, 'b, B: GraphicsApi, Target>( render_api: &'a mut Self, target_api: &'b mut GpuManager<B>, - source_device: F, render_device: &DrmNode, target_device: &DrmNode, ) -> Result<MultiRenderer<'a, 'b, A, B, Target>, Error<A, B>> where <A::Device as ApiDevice>::Renderer: Offscreen<Target>, - F: FnMut(&wl_buffer::WlBuffer) -> Option<DrmNode> + 'static, { if !render_api .devices @@ -304,7 +300,7 @@ impl<A: GraphicsApi> GpuManager<A> { .unwrap(); Ok(MultiRenderer { - source: Box::leak(Box::new(source_device)), + dma_source: Some(&mut render_api.dma_source), render: RenderDevice::Device(render.remove(0)), target: Some(target), other_renderers: others, @@ -313,7 +309,7 @@ impl<A: GraphicsApi> GpuManager<A> { }) } else { Ok(MultiRenderer { - source: Box::leak(Box::new(source_device)), + dma_source: Some(&mut render_api.dma_source), render: RenderDevice::Device(render.remove(0)), target: None, other_renderers: others, @@ -328,7 +324,7 @@ impl<A: GraphicsApi> GpuManager<A> { /// If you are using [`MultiRenderer`]s do rendering of your client buffers, /// you can call `early_import` on commit to start necessary copy processes early. /// - /// - `source` may specify on which gpu node the provided buffer is allocated, if applicable. + /// - `source` may specify on which gpu node the provided buffer is allocated on, if applicable. /// - `target` referrs to the gpu node, that the buffer needs to be accessable on later. /// *Note*: Usually this will be **render**ing gpu of a [`MultiRenderer`] /// - `surface` is the wayland surface, whos buffer and subsurfaces buffers shall be imported @@ -419,28 +415,26 @@ impl<A: GraphicsApi> GpuManager<A> { { match buffer_type(buffer) { Some(BufferType::Dma) => { - let (mut target_device, mut others) = self + let (mut target_device, others) = self .devices .iter_mut() .partition::<Vec<_>, _>(|device| device.node() == &target); let target_device = target_device.get_mut(0).ok_or(Error::DeviceMissing)?; let format = buffer.as_ref().user_data().get::<Dmabuf>().unwrap().format(); - let source = source.filter(|source| source != &target); - let can_import = source.as_ref().map(|_| CAN_IMPORT.lock().unwrap()); - let might_import = source - .and_then(|source| can_import.as_ref().unwrap().get(&(source, target, format))) - .copied() - .unwrap_or(true); - - if might_import { + let dma_source = self + .dma_source + .entry(buffer.as_ref().user_data().get::<Dmabuf>().unwrap().weak()); + if matches!(dma_source, Entry::Vacant(_)) + || matches!(dma_source, Entry::Occupied(ref x) if x.get() == &target) + { match target_device .renderer_mut() .import_dma_buffer(buffer, Some(surface), damage) { Ok(imported) => { - if let (Some(ref mut can_import), Some(source)) = (can_import, source) { - can_import.insert((source, target, format), true); + if let Entry::Vacant(vacant) = dma_source { + vacant.insert(target); } let mut texture = MultiTexture::from_surface(Some(surface), imported.size()); texture.insert_texture::<A>(target, imported); @@ -448,32 +442,39 @@ impl<A: GraphicsApi> GpuManager<A> { return Ok(()); } Err(err) => { - slog::warn!( + slog::trace!( self.log, - "Error importing dmabuf (format: {:?}) from {:?} to {}: {}", + "Error importing dmabuf (format: {:?}) to {}: {}", format, - source, target, err ); - slog::info!(self.log, "Falling back to cpu-copy."); - if let (Some(ref mut can_import), Some(source)) = (can_import, source) { - can_import.insert((source, target, format), false); - } + slog::trace!(self.log, "Falling back to cpu-copy."); } } } - // if we do need to do a memory copy, we start with the export here - for import_renderer in others - .iter_mut() - .filter(|device| source.as_ref().map(|src| src == device.node()).unwrap_or(true)) - { + // if we do need to do a memory copy, we start with the export early + let source = if let Entry::Occupied(ref occ) = dma_source { + Some(*occ.get()) + } else { + source + }; + let (first, last) = match source { + Some(s) => others + .into_iter() + .partition::<Vec<_>, _>(|other| other.node() == &s), + None => (Vec::new(), others), + }; + for import_renderer in first.into_iter().chain(last.into_iter()) { if let Ok(texture) = import_renderer .renderer_mut() .import_dma_buffer(buffer, Some(surface), damage) { + if let Entry::Vacant(vacant) = dma_source { + vacant.insert(*import_renderer.node()); + } let mut gpu_texture = MultiTexture::from_surface(Some(surface), texture.size()); let mappings = if gpu_texture.get::<A>(&target).is_none() { // force full copy @@ -525,19 +526,7 @@ impl<A: GraphicsApi> GpuManager<A> { Ok(()) } Some(BufferType::Shm) => { - // this is async anyway - let shm_texture = self - .devices - .iter_mut() - .find(|dev| dev.node() == &target) - .ok_or(Error::DeviceMissing)? - .renderer_mut() - .import_shm_buffer(buffer, Some(surface), damage) - .map_err(Error::<A, A>::Target)?; - let mut texture = - MultiTexture::from_surface(Some(surface), buffer_dimensions(buffer).unwrap()); - texture.insert_texture::<A>(target, shm_texture); - surface.data_map.insert_if_missing(|| texture.0); + // we just need to upload in import_shm_buffer Ok(()) } None => { @@ -580,7 +569,7 @@ pub trait ApiDevice { /// as well as transparently importing client buffers residing on different gpus. #[derive(Debug)] pub struct MultiRenderer<'a, 'b, R: GraphicsApi, T: GraphicsApi, Target> { - source: *mut dyn FnMut(&wl_buffer::WlBuffer) -> Option<DrmNode>, + dma_source: Option<&'a mut HashMap<WeakDmabuf, DrmNode>>, render: RenderDevice<'a, R>, target: Option<&'b mut T::Device>, other_renderers: Vec<&'a mut R::Device>, @@ -628,12 +617,6 @@ impl<'a, A: GraphicsApi> RenderDevice<'a, A> { } } -impl<'a, 'b, R: GraphicsApi, T: GraphicsApi, Target> Drop for MultiRenderer<'a, 'b, R, T, Target> { - fn drop(&mut self) { - let _ = unsafe { Box::from_raw(self.source as *mut _) }; - } -} - /// [`Frame`](super::Frame) implementation of a [`MultiRenderer`]. #[derive(Debug)] pub struct MultiFrame<R: GraphicsApi, T: GraphicsApi> { @@ -787,19 +770,22 @@ where } let node = *self.render.node(); - let source_ref = self.source; + // we need to move some stuff into the closure temporarily + let mut dma_source = self.dma_source.take(); let mut target = self.target.take(); let mut other_renderers = self.other_renderers.drain(..).collect::<Vec<_>>(); + let dma_source_ref = &mut dma_source; let target_ref = &mut target; let other_renderers_ref = &mut other_renderers; + let log = self.log.clone(); let res = self .render .renderer_mut() .render(size, dst_transform, move |render, frame| { let mut new_renderer = MultiRenderer { - source: source_ref, + dma_source: dma_source_ref.take(), render: RenderDevice::Renderer(render, node), target: target_ref.take(), other_renderers: other_renderers_ref.drain(..).collect(), @@ -815,17 +801,21 @@ where }; let res = rendering(&mut new_renderer, &mut frame); - // don't return them, but reset the reference, so we can restore self on error + + // don't return yet, but first reset the references, so we can restore self on error + *dma_source_ref = new_renderer.dma_source.take(); *target_ref = new_renderer.target.take(); *other_renderers_ref = new_renderer.other_renderers.drain(..).collect(); - // don't free source - std::mem::forget(new_renderer); + (res, frame.damage) }) .map_err(Error::Render); + // restore self + self.dma_source = dma_source; self.target = target; self.other_renderers = other_renderers; + // then possibly return the error let (res, damage) = res?; let mut damage = damage @@ -1279,10 +1269,9 @@ where .user_data() .get::<Dmabuf>() .expect("import_dma_buffer without checking buffer type?"); - let tranch = unsafe { &mut *self.source }(buffer); let texture = MultiTexture::from_surface(surface, dmabuf.size()); let texture_ref = texture.0.clone(); - let res = self.import_dmabuf_internal(tranch, dmabuf, texture, Some(damage)); + let res = self.import_dmabuf_internal(None, dmabuf, texture, Some(damage)); if res.is_ok() { if let Some(surface) = surface { surface.data_map.insert_if_missing(|| texture_ref); @@ -1341,45 +1330,38 @@ where mut texture: MultiTexture, damage: Option<&[Rectangle<i32, BufferCoords>]>, ) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error> { - let source = source.filter(|source| source != self.render.node()); - let can_import = source.as_ref().map(|_| CAN_IMPORT.lock().unwrap()); - let might_import = source - .and_then(|source| { - can_import - .as_ref() - .unwrap() - .get(&(source, *self.render.node(), dmabuf.format())) - }) - .copied() - .unwrap_or(true); - - if might_import { + let dma_source = self.dma_source.as_mut().unwrap().entry(dmabuf.weak()); + if matches!(dma_source, Entry::Vacant(_)) + || matches!(dma_source, Entry::Occupied(ref x) if x.get() == self.render.node()) + { match self.render.renderer_mut().import_dmabuf(dmabuf, damage) { Ok(imported) => { - if let (Some(ref mut can_import), Some(source)) = (can_import, source) { - can_import.insert((source, *self.render.node(), dmabuf.format()), true); + if let Entry::Vacant(vacant) = dma_source { + vacant.insert(*self.render.node()); } texture.insert_texture::<R>(*self.render.node(), imported); return Ok(texture); } Err(err) => { - let target = *self.render.node(); - slog::warn!( + slog::trace!( self.log, - "Error importing dmabuf (format: {:?}) from {:?} to {}: {}", + "Error importing dmabuf (format: {:?}) to {}: {}", dmabuf.format(), - source, - target, + self.render.node(), err ); - slog::info!(self.log, "Falling back to cpu-copy."); - if let (Some(ref mut can_import), Some(source)) = (can_import, source) { - can_import.insert((source, target, dmabuf.format()), false); - } + slog::trace!(self.log, "Falling back to cpu-copy."); } } } + let source = if let Entry::Occupied(ref occ) = dma_source { + Some(*occ.get()) + } else { + source + }; + let render_node = self.render.node(); + // lets check if we don't have a mapping let size = texture.0.borrow().size; let needs_reimport = texture @@ -1387,7 +1369,7 @@ where .borrow_mut() .textures .get_mut(&TypeId::of::<R>()) - .and_then(|nodes_textures| nodes_textures.get_mut(self.render.node())) + .and_then(|nodes_textures| nodes_textures.get_mut(render_node)) .map(|texture| match texture.mapping.as_ref() { None => true, // in the few cases, were we need to rerender more, then was damaged by the client, @@ -1406,8 +1388,14 @@ where .unwrap_or(true); if needs_reimport { // no (usable) early-import :( - if let Some(import_renderer) =
multigpu: Remove `source` param to not depend on wl