<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::{hash_map::Entry, HashMap},
    convert::{AsMut, AsRef},
    fmt,
    rc::Rc,
    sync::Mutex,
};

#[cfg(feature = "wayland_frontend")]
use crate::wayland::{dmabuf::get_dmabuf, shm};
use crate::{
    backend::{
        allocator::{dmabuf::WeakDmabuf, Buffer as BufferTrait, 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());
}

/// Tracks available gpus from a given [`GraphicsApi`]
#[derive(Debug)]
pub struct GpuManager<A: GraphicsApi> {
    api: A,
    devices: Vec<A::Device>,
    dma_source: HashMap<WeakDmabuf, DrmNode>,
    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);
        let mut devices = Vec::new();
        api.enumerate(&mut devices, &log).map_err(Error::RenderApiError)?;
        if devices.is_empty() {
            return Err(Error::NoDevices);
        }

        Ok(GpuManager {
            api,
            devices,
            dma_source: HashMap::new(),
            log,
        })
    }

    /// Create a [`MultiRenderer`].
    ///
    /// - `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
    ///
    /// - the `Target` generic argument referrs to the object used by the `render_device` to render to before
    ///   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, Target>(
        &'a mut self,
        render_device: &DrmNode,
        target_device: &DrmNode,
    ) -> Result<MultiRenderer<'a, 'a, A, A, Target>, Error<A, A>>
    where
        <A::Device as ApiDevice>::Renderer: Offscreen<Target>,
    {
        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);
        if target_device != render_device {
            let (mut target, others) = others
                .into_iter()
                .partition::<Vec<_>, _>(|device| device.node() == target_device);

            Ok(MultiRenderer {
                dma_source: Some(&mut self.dma_source),
                render: RenderDevice::Device(render.remove(0)),
                target: Some(target.remove(0)),
                other_renderers: others,
                proxy_framebuffer: std::marker::PhantomData,
                log: self.log.clone(),
            })
        } else {
            Ok(MultiRenderer {
                dma_source: Some(&mut self.dma_source),
                render: RenderDevice::Device(render.remove(0)),
                target: None,
                other_renderers: others,
                proxy_framebuffer: std::marker::PhantomData,
                log: self.log.clone(),
            })
        }
    }

    /// Create a [`MultiRenderer`] from two different [`GraphicsApi`]s.
    ///
    /// - `render_api` should be the [`GpuManager`] used for the `render_device`.
    /// - `target_api` should be the [`GpuManager`] used for the `target_device`.
    /// - `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
    ///
    /// - the `Target` generic argument referrs to the object used by the `render_device` to render to before
    ///   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, Target>(
        render_api: &'a mut Self,
        target_api: &'b mut GpuManager<B>,
        render_device: &DrmNode,
        target_device: &DrmNode,
    ) -> Result<MultiRenderer<'a, 'b, A, B, Target>, Error<A, B>>
    where
        <A::Device as ApiDevice>::Renderer: Offscreen<Target>,
    {
        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
                .devices
                .iter_mut()
                .find(|device| device.node() == target_device)
                .unwrap();

            Ok(MultiRenderer {
                dma_source: Some(&mut render_api.dma_source),
                render: RenderDevice::Device(render.remove(0)),
                target: Some(target),
                other_renderers: others,
                proxy_framebuffer: std::marker::PhantomData,
                log: render_api.log.clone(),
            })
        } else {
            Ok(MultiRenderer {
                dma_source: Some(&mut render_api.dma_source),
                render: RenderDevice::Device(render.remove(0)),
                target: None,
                other_renderers: others,
                proxy_framebuffer: std::marker::PhantomData,
                log: target_api.log.clone(),
            })
        }
    }

    /// Function for optimizing buffer imports across multiple gpus.
    ///
    /// 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 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
    ///
    /// 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,
        source: Option<DrmNode>,
        target: DrmNode,
        buffer: &wl_buffer::WlBuffer,
        surface: &SurfaceData,
        damage: &[Rectangle<i32, BufferCoords>],
    ) -> Result<(), Error<A, A>>
    where
        A: 'static,
        <A::Device as ApiDevice>::Renderer: ImportMemWl + ImportDmaWl + ExportMem,
        <<A::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping: 'static,
    {
        match buffer_type(buffer) {
            Some(BufferType::Dma) => {
                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 dmabuf = get_dmabuf(buffer).unwrap();
                let format = dmabuf.format();

                let dma_source = self.dma_source.entry(dmabuf.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 Entry::Vacant(vacant) = dma_source {
                                vacant.insert(target);
                            }
                            let mut texture = MultiTexture::from_surface(Some(surface), imported.size());
                            texture.insert_texture::<A>(target, imported);
                            surface.data_map.insert_if_missing(|| texture.0);
                            return Ok(());
                        }
                        Err(err) => {
                            slog::trace!(
                                self.log,
                                "Error importing dmabuf (format: {:?}) to {}: {}",
                                format,
                                target,
                                err
                            );
                            slog::trace!(self.log, "Falling back to cpu-copy.");
                        }
                    }
                }

                // 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
                            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))
                                })
                                .collect::<Result<Vec<_>, Error<A, A>>>()?
                        };
                        gpu_texture.insert_mapping::<A, A, _>(
                            *import_renderer.node(),
                            target,
                            texture.size(),
                            mappings.into_iter(),
                        );
                        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;
    /// Errors this api returns
    type Error: std::error::Error;

    /// Enumerate available devices by:
    /// - removing gone devices from list
    /// - adding new devices to list
    ///
    /// 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 {
        self.render.renderer_mut()
    }
}

// Hack for implementing Renderer::render..
#[derive(Debug)]
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)
        }
    }

    fn supported_formats(&self) -> Option<HashSet<crate::backend::allocator::Format>> {
        if let Some(target) = self.target.as_ref() {
            target.renderer().supported_formats()
        } else {
            Bind::<Target>::supported_formats(self.render.renderer())
        }
    }
}

static MAX_CPU_COPIES: usize = 3; // TODO, benchmark this

impl<'a, 'b, R: GraphicsApi, T: GraphicsApi, Target> Renderer for MultiRenderer<'a, 'b, R, T, Target>
where
    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 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> {
        self.render
            .renderer_mut()
            .downscale_filter(filter)
            .map_err(Error::Render)
    }
    fn upscale_filter(&mut self, filter: TextureFilter) -> Result<(), Self::Error> {
        self.render
            .renderer_mut()
            .upscale_filter(filter)
            .map_err(Error::Render)
    }

    fn render<F, Res>(
        &mut self,
        size: Size<i32, Physical>,
        dst_transform: Transform,
        rendering: F,
    ) -> Result<Res, Self::Error>
    where
        F: FnOnce(&mut Self, &mut Self::Frame) -> Res,
    {
        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,
    <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
{
    fn import_shm_buffer(
        &mut self,
        buffer: &wl_buffer::WlBuffer,
        surface: Option<&crate::wayland::compositor::SurfaceData>,
        damage: &[Rectangle<i32, BufferCoords>],
    ) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error> {
        let shm_texture = self
            .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
            .get::<R>(self.render.node())
            .ok_or_else(|| Error::MismatchedDevice(*self.render.node()))?;
        self.render
            .renderer_mut()
            .update_memory(&*mem_texture, data, region)
            .map_err(Error::Render)
    }
}

#[cfg(feature = "wayland_frontend")]
impl<'a, 'b, R: GraphicsApi, T: GraphicsApi, Target> ImportDmaWl for MultiRenderer<'a, 'b, R, T, Target>
where
    <R::Device as ApiDevice>::Renderer: ImportDmaWl + 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_dma_buffer(
        &mut self,
        buffer: &wl_buffer::WlBuffer,
        surface: Option<&SurfaceData>,
        damage: &[Rectangle<i32, BufferCoords>],
    ) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error> {
        let dmabuf = get_dmabuf(buffer).expect("import_dma_buffer without checking buffer type?");
        let texture = MultiTexture::from_surface(surface, dmabuf.size());
        let texture_ref = texture.0.clone();
        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);
            }
        }
        res
    }
}

impl<'a, 'b, R: GraphicsApi, T: GraphicsApi, Target> ImportDma for MultiRenderer<'a, 'b, R, T, Target>
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 dmabuf_formats<'c>(&'c self) -> Box<dyn Iterator<Item = &'c Format> + 'c> {
        self.render.renderer().dmabuf_formats()
    }

    fn import_dmabuf(
        &mut self,
        dmabuf: &Dmabuf,
        damage: Option<&[Rectangle<i32, BufferCoords>]>,
    ) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error> {
        let texture = MultiTexture::new(dmabuf.size());
        self.import_dmabuf_internal(None, dmabuf, texture, damage)
    }
}

impl<'a, 'b, R: GraphicsApi, T: GraphicsApi, Target> MultiRenderer<'a, 'b, R, T, Target>
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,
        new_damage: Vec<Rectangle<i32, BufferCoords>>,
        source: Option<DrmNode>,
        dmabuf: &Dmabuf,
        texture: &mut MultiTexture,
    ) -> Result<(), <Self as Renderer>::Error> {
        let dma_source = self.dma_source.as_mut().unwrap().entry(dmabuf.weak());
        if let Some(import_renderer) = self.target.as_mut().filter(|target| match source {
            Some(s) => &s == target.node(),
            None => true,
        }) {
            if let Ok(dma_texture) = import_renderer
                .renderer_mut()
                .import_dmabuf(dmabuf, Some(&*new_damage))
            {
                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,
            } else {
                None
            };
            tex.texture = Some(Box::new(new_texture) as Box<_>);
        } else {
            // update
            let texture = <dyn Any>::downcast_ref::<
                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: Fix storing of copied textures.

Previously we were accidentally storing an `Option<TextureType>`
instead of a `TextureType` inside our AnyMap, resulting in being
unable to retrieve the textures later as the types didn't match up.

This commit fixes this and just stores nothing in case of an error.

<DFF> @@ -1581,7 +1581,9 @@ where
             } else {
                 None
             };
-            tex.texture = Some(Box::new(new_texture) as Box<_>);
+            if let Some(new_texture) = new_texture {
+                tex.texture = Some(Box::new(new_texture) as Box<_>);
+            }
         } else {
             // update
             let texture = <dyn Any>::downcast_ref::<

multigpu: Fix storing of copied textures.