<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,
            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
                    .renderer_mut()
                    .map_texture(mapping)
                    .map_err(Error::Target)?;
                self.render
                    .renderer_mut()
                    .import_memory(mapped, mapping.size(), false)
                    .ok()
            } else if let Some(source) = self
                .other_renderers
                .iter_mut()
                    "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
                    .renderer_mut()
                    .map_texture(mapping)
                    .map_err(Error::Render)?;
                self.render
                    .renderer_mut()
                    .import_memory(mapped, mapping.size(), false)
                    .ok()
            } else {
                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 {
                        texture.size(),
                        region
                    );
                    if let Ok(mapped) = source.renderer_mut().map_texture(mapping) {
                        let _ = self.render.renderer_mut().update_memory(texture, mapped, region);
                    }
                }
            } else if let Some(source) = self
                .other_renderers
                    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
                        texture.size(),
                        region
                    );
                    if let Ok(mapped) = source.renderer_mut().map_texture(mapping) {
                        let _ = self.render.renderer_mut().update_memory(texture, mapped, region);
                    }
                }
            };
        }
                    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: Don't eat errors

<DFF> @@ -1609,10 +1609,12 @@ where
                     .renderer_mut()
                     .map_texture(mapping)
                     .map_err(Error::Target)?;
-                self.render
-                    .renderer_mut()
-                    .import_memory(mapped, mapping.size(), false)
-                    .ok()
+                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()
@@ -1634,10 +1636,12 @@ where
                     .renderer_mut()
                     .map_texture(mapping)
                     .map_err(Error::Render)?;
-                self.render
-                    .renderer_mut()
-                    .import_memory(mapped, mapping.size(), false)
-                    .ok()
+                Some(
+                    self.render
+                        .renderer_mut()
+                        .import_memory(mapped, mapping.size(), false)
+                        .map_err(Error::Render)?,
+                )
             } else {
                 None
             };
@@ -1666,9 +1670,14 @@ where
                         texture.size(),
                         region
                     );
-                    if let Ok(mapped) = source.renderer_mut().map_texture(mapping) {
-                        let _ = self.render.renderer_mut().update_memory(texture, mapped, 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
@@ -1686,9 +1695,14 @@ where
                         texture.size(),
                         region
                     );
-                    if let Ok(mapped) = source.renderer_mut().map_texture(mapping) {
-                        let _ = self.render.renderer_mut().update_memory(texture, mapped, 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)?;
                 }
             };
         }

multigpu: Don't eat errors