🔒 Repository is read-only – file editing is disabled.

PaganDE/smithay-0.7.0/src/backend/renderer/multigpu/mod.rs main

2987 linii Raw ← Powrót
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987
//!
//! 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:
//! - [`gbm::GbmGlesBackend`]
//!
//! 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`]-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, as such any [`Bind`]- and [`Offscreen`]-implementations
//! will be allocated on the target-gpu.
//!
//! The `ImportMem`-implementation will upload the texture on the render-gpu, failing *if*
//! the render-gpu does not support the format as normal. Be careful when mixing gpu's
//! with different memory-formats supported, what formats you annouce.
//!
//! In contrast the `ImportDma`-implementations will transparently create copies
//! of client buffers, if necessary, given it is unclear if a dmabuf can be imported on any given gpu.
//! The implementation strives for the best possible performance for a given setup,
//! when choosing a copy-path.
//!
//! Any `ExportMem`-implementations will originate from the render-gpu, which again
//! needs to support the requested format directly. No paths across other gpus are tested.
//!
//! *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 aliasable::boxed::AliasableBox;
use std::{
    any::{Any, TypeId},
    collections::HashMap,
    fmt,
    sync::{Arc, Mutex},
};

use super::{
    sync::SyncPoint, Bind, Blit, BlitFrame, Color32F, ContextId, DebugFlags, ExportMem, Frame, ImportDma,
    ImportMem, Offscreen, Renderer, RendererSuper, Texture, TextureFilter, TextureMapping,
};
#[cfg(feature = "wayland_frontend")]
use super::{ImportDmaWl, ImportMemWl};

#[cfg(feature = "wayland_frontend")]
use crate::{
    backend::renderer::{buffer_type, BufferType},
    wayland::{compositor::SurfaceData, dmabuf::get_dmabuf, shm},
};
use crate::{
    backend::{
        allocator::{
            dmabuf::{AnyError, Dmabuf},
            format::FormatSet,
            Allocator, Buffer as BufferTrait, Format, Fourcc, Modifier,
        },
        drm::DrmNode,
        renderer::sync,
        SwapBuffersError,
    },
    utils::{Buffer as BufferCoords, Physical, Rectangle, Size, Transform},
};
use tracing::{debug, info, info_span, instrument, trace, trace_span, warn};
#[cfg(feature = "wayland_frontend")]
use wayland_server::protocol::{wl_buffer, wl_shm, wl_surface::WlSurface};

#[cfg(all(feature = "backend_gbm", feature = "backend_egl", feature = "renderer_gl"))]
pub mod gbm;

/// Tracks available gpus from a given [`GraphicsApi`]
#[derive(Debug)]
pub struct GpuManager<A: GraphicsApi> {
    api: A,
    devices: Vec<A::Device>,
    dmabuf_cache: HashMap<(DrmNode, DrmNode), Option<(bool, Dmabuf)>>,
    span: tracing::Span,
}

/// 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 RendererSuper>::Error: 'static,
    <<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
    /// 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 RendererSuper>::Error),
    /// Error on the target device
    #[error("Error on the target device: {0:}")]
    Target(#[source] <<T::Device as ApiDevice>::Renderer as RendererSuper>::Error),
    /// Failed to import buffer using the api on any device
    #[error("Failed to import buffer")]
    ImportFailed,
    /// Buffer allocation failed
    #[error("Failed to allocate buffer")]
    AllocatorError(AnyError),
}

impl<R: GraphicsApi, T: GraphicsApi> Error<R, T> {
    fn transpose(self) -> Error<T, R> {
        match self {
            Error::Render(err) => Error::Target(err),
            Error::Target(err) => Error::Render(err),
            Error::RenderApiError(err) => Error::TargetApiError(err),
            Error::TargetApiError(err) => Error::RenderApiError(err),
            Error::NoDevice(d) => Error::NoDevice(d),
            Error::MismatchedDevice(d) => Error::MismatchedDevice(d),
            Error::DeviceMissing => Error::DeviceMissing,
            Error::ImportFailed => Error::ImportFailed,
            Error::AllocatorError(a) => Error::AllocatorError(a),
        }
    }
}

impl<R: GraphicsApi> Error<R, R> {
    fn generalize<T: GraphicsApi>(self) -> Error<R, T> {
        match self {
            Error::Render(err) => Error::Render(err),
            Error::Target(err) => Error::Render(err),
            Error::RenderApiError(err) => Error::RenderApiError(err),
            Error::TargetApiError(err) => Error::RenderApiError(err),
            Error::NoDevice(d) => Error::NoDevice(d),
            Error::MismatchedDevice(d) => Error::MismatchedDevice(d),
            Error::DeviceMissing => Error::DeviceMissing,
            Error::ImportFailed => Error::ImportFailed,
            Error::AllocatorError(a) => Error::AllocatorError(a),
        }
    }
}

impl<R: GraphicsApi, T: GraphicsApi> fmt::Debug for Error<R, T>
where
    R::Error: 'static,
    T::Error: 'static,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
    <<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            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"),
            Error::AllocatorError(err) => write!(f, "Error::AllocationError({})", err),
        }
    }
}

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 RendererSuper>::Error: Into<SwapBuffersError> + Send + Sync,
    <<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: Into<SwapBuffersError> + Send + Sync,
{
    #[inline]
    fn from(err: Error<R, T>) -> SwapBuffersError {
        match err {
            x @ Error::NoDevice(_) | x @ Error::DeviceMissing | x @ Error::AllocatorError(_) => {
                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> AsRef<A> for GpuManager<A> {
    fn as_ref(&self) -> &A {
        &self.api
    }
}

impl<A: GraphicsApi> AsMut<A> for GpuManager<A> {
    fn as_mut(&mut self) -> &mut A {
        &mut self.api
    }
}

impl<A: GraphicsApi> GpuManager<A> {
    /// Create a new [`GpuManager`] for a given [`GraphicsApi`].
    pub fn new(api: A) -> Result<GpuManager<A>, Error<A, A>> {
        let span = info_span!("renderer_multi", backend = A::identifier());

        let mut devices = Vec::new();
        api.enumerate(&mut devices).map_err(Error::RenderApiError)?;

        Ok(GpuManager {
            api,
            devices,
            dmabuf_cache: HashMap::new(),
            span,
        })
    }

    /// Get all devices enumerated by the API.
    pub fn devices(&mut self) -> Result<impl Iterator<Item = &A::Device>, A::Error> {
        if self.api.needs_enumeration() {
            self.api.enumerate(&mut self.devices)?;
        }
        Ok(self.devices.iter())
    }

    /// Get all devices enumerated by the API.
    pub fn devices_mut(&mut self) -> Result<impl Iterator<Item = &mut A::Device>, A::Error> {
        if self.api.needs_enumeration() {
            self.api.enumerate(&mut self.devices)?;
        }
        Ok(self.devices.iter_mut())
    }

    /// Create a [`MultiRenderer`] from a single device.
    ///
    /// This a convenience function to deal with the same types even, if you only need one device.
    /// Because no copies are necessary in these cases, all extra arguments can be omitted.
    #[instrument(level = "trace", parent = &self.span, skip(self))]
    #[profiling::function]
    pub fn single_renderer<'api>(
        &'api mut self,
        device: &DrmNode,
    ) -> Result<MultiRenderer<'api, 'api, A, A>, Error<A, A>> {
        if !self.devices.iter().any(|dev| dev.node() == device) || self.api.needs_enumeration() {
            self.api
                .enumerate(&mut self.devices)
                .map_err(Error::RenderApiError)?;
        }

        if !self.devices.iter().any(|dev| dev.node() == device) {
            return Err(Error::NoDevice(*device));
        }

        let (mut render, others) = self
            .devices
            .iter_mut()
            .partition::<Vec<_>, _>(|dev| dev.node() == device);

        Ok(MultiRenderer {
            render: render.remove(0),
            target: None,
            other_renderers: others,
            span: tracing::Span::current(),
        })
    }

    /// 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
    /// - `allocator` should referr to an `Allocator`, that works guaranteed with the `render_device`
    ///     to do offscreen composition on. Dma copies will be used, if buffers returned by the allocator
    ///     also work on the `target_device`.
    /// - `copy_format` denotes the format buffers will be allocated in for offscreen rendering.
    ///
    /// It is valid to pass the same devices for both, but you *should* use [`GpuManager::single_renderer`] in those cases.
    #[instrument(level = "trace", parent = &self.span, skip(self))]
    #[profiling::function]
    pub fn renderer<'api>(
        &'api mut self,
        render_device: &DrmNode,
        target_device: &DrmNode,
        copy_format: Fourcc,
    ) -> Result<MultiRenderer<'api, 'api, A, A>, Error<A, A>>
    where
        <A::Device as ApiDevice>::Renderer: Bind<Dmabuf>,
    {
        if !self.devices.iter().any(|device| device.node() == render_device)
            || !self.devices.iter().any(|device| device.node() == target_device)
            || self.api.needs_enumeration()
        {
            self.api
                .enumerate(&mut self.devices)
                .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 {
                render: render.remove(0),
                target: Some(TargetData {
                    device: target.remove(0),
                    cached_buffer: self
                        .dmabuf_cache
                        .entry((*render_device, *target_device))
                        .or_default(),
                    format: copy_format,
                }),
                other_renderers: others,
                span: tracing::Span::current(),
            })
        } else {
            Ok(MultiRenderer {
                render: render.remove(0),
                target: None,
                other_renderers: others,
                span: tracing::Span::current(),
            })
        }
    }

    /// 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
    /// - `allocator` should referr to an `Allocator`, that works guaranteed with the `render_device`
    ///     to do offscreen composition on. Dma copies will be used, if buffers returned by the allocator
    ///     also work on the `target_device`.
    /// - `copy_format` denotes the format buffers will be allocated in for offscreen rendering.
    #[instrument(level = "trace", skip(render_api, target_api), follows_from = [&render_api.span, &target_api.span])]
    #[profiling::function]
    pub fn cross_renderer<'render, 'target, B: GraphicsApi, Alloc: Allocator>(
        render_api: &'render mut Self,
        target_api: &'target mut GpuManager<B>,
        render_device: &DrmNode,
        target_device: &DrmNode,
        copy_format: Fourcc,
    ) -> Result<MultiRenderer<'render, 'target, A, B>, Error<A, B>>
    where
        <A::Device as ApiDevice>::Renderer: Bind<Dmabuf>,
        <B::Device as ApiDevice>::Renderer: ImportDma,
    {
        if !render_api
            .devices
            .iter()
            .any(|device| device.node() == render_device)
            || render_api.api.needs_enumeration()
        {
            render_api
                .api
                .enumerate(&mut render_api.devices)
                .map_err(Error::RenderApiError)?;
        }

        if !target_api
            .devices
            .iter()
            .any(|device| device.node() == target_device)
            || target_api.api.needs_enumeration()
        {
            target_api
                .api
                .enumerate(&mut target_api.devices)
                .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 {
                render: render.remove(0),
                target: Some(TargetData {
                    device: target,
                    cached_buffer: target_api
                        .dmabuf_cache
                        .entry((*render_device, *target_device))
                        .or_default(),
                    format: copy_format,
                }),
                other_renderers: others,
                span: tracing::Span::current(),
            })
        } else {
            Ok(MultiRenderer {
                render: render.remove(0),
                target: None,
                other_renderers: others,
                span: tracing::Span::current(),
            })
        }
    }

    /// 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.
    ///
    /// - `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, whose 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")]
    #[instrument(level = "trace", parent = &self.span, skip(self))]
    #[profiling::function]
    pub fn early_import(&mut self, 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::RendererSurfaceStateUserData,
            wayland::compositor::{with_surface_tree_upward, TraversalAction},
        };

        if self.devices.is_empty() {
            return Err(Error::DeviceMissing);
        }

        let mut result = Ok(());
        with_surface_tree_upward(
            surface,
            (),
            |_surface, states, _| {
                if let Some(data) = states.data_map.get::<RendererSurfaceStateUserData>() {
                    let mut data_ref = data.lock().unwrap();
                    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.
                            // FIXME: We should be able to get rid of this allocation here
                            let buffer_damage = data.damage().raw().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_or_touches(rect));

                                    for overlap in overlapping {
                                        rect = rect.merge(overlap);
                                    }
                                    new_damage.push(rect);
                                    new_damage
                                },
                            );

                            if let Err(err) = self.early_import_buffer(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")]
    #[profiling::function]
    fn early_import_buffer(
        &mut self,
        target_node: 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 dmabuf = get_dmabuf(buffer).unwrap();
                let mut texture = MultiTexture::from_surface(Some(surface), dmabuf.size(), dmabuf.format());

                if !self.devices.iter().any(|device| target_node == *device.node()) {
                    return Err(Error::DeviceMissing);
                }

                let mut devices = self.devices.iter_mut();
                let first = devices.next().unwrap();
                let src_node = import_on_src_node(dmabuf, Some(damage), &mut texture, first, None, devices)?;

                if src_node != target_node {
                    let mut texture_internal = texture.0.lock().unwrap();
                    let api_textures = texture_internal.textures.get_mut(&TypeId::of::<A>()).unwrap();

                    {
                        let target_texture = api_textures.get(&target_node);
                        if !matches!(
                            target_texture,
                            Some(GpuSingleTexture::Mem {
                                external_shadow: None,
                                ..
                            })
                        ) {
                            // if we don't have a mem texture, either
                            // - we don't have any import, in that case lets figure this out during first import
                            // - we have a dma shadow copy, in that case we can't copy without invalidating the shadow buffer
                            //   if the damage doesn't match up with what we be requested during the next import.
                            // - this also applies if it is an external copy, so we additionally need a shadow buffer before reading from memory.
                            return Ok(());
                        }
                    }

                    // if we do need to do a memory copy, we start with the export early

                    let src_texture = match api_textures.get(&src_node).unwrap() {
                        GpuSingleTexture::Direct(tex) => tex
                            .downcast_ref::<<<A::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>(
                            )
                            .unwrap(),
                        _ => unreachable!(),
                    };

                    let format = src_texture
                        .format()
                        // Check the target device supports the format, if not use 8-bit format
                        .filter(|format| {
                            self.devices
                                .iter()
                                .find(|device| target_node == *device.node())
                                .unwrap()
                                .renderer()
                                .mem_formats()
                                .any(|fmt| fmt == *format)
                        })
                        // The GL spec *always* supports this format.
                        // TODO: Re-evaluate this, once we support vulkan
                        .unwrap_or(Fourcc::Abgr8888);

                    let src_device = self
                        .devices
                        .iter_mut()
                        .find(|device| src_node == *device.node())
                        .unwrap();

                    let mappings = {
                        let damage = damage
                            .iter()
                            .flat_map(|rect| rect.intersection(Rectangle::from_size(src_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_or_touches(rect));

                                for overlap in overlapping {
                                    rect = rect.merge(overlap);
                                }
                                new_damage.push(rect);
                                new_damage
                            });
                        damage
                            .iter()
                            .copied()
                            .map(|damage| {
                                let mapping = src_device
                                    .renderer_mut()
                                    .copy_texture(src_texture, damage, format)
                                    .map_err(Error::Target)?;
                                Ok((damage, mapping))
                            })
                            .collect::<Result<Vec<_>, Error<A, A>>>()?
                    };

                    std::mem::drop(texture_internal);
                    texture.insert_mapping::<A, A, _>(
                        src_node,
                        target_node,
                        texture.size(),
                        mappings.into_iter(),
                    );
                    surface.data_map.insert_if_missing_threadsafe(|| texture.0);
                }

                Ok(())
            }
            #[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(())
            }
            Some(BufferType::SinglePixel) => {
                // no need to do anything
                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>) -> Result<(), Self::Error>;
    /// Method to force a re-enumeration, e.g. to free resources
    fn needs_enumeration(&self) -> bool {
        false
    }
    /// Unique name for representing the api type in log messages
    fn identifier() -> &'static str;
}

/// A device produced by a [`GraphicsApi`].
pub trait ApiDevice: fmt::Debug {
    /// The [`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 an allocator for buffers renderers of this device can sample from and render to
    fn allocator(&mut self) -> &mut dyn Allocator<Buffer = Dmabuf, Error = AnyError>;

    /// 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.
pub struct MultiRenderer<'render, 'target, R: GraphicsApi, T: GraphicsApi> {
    render: &'render mut R::Device,
    target: Option<TargetData<'target, T>>,
    other_renderers: Vec<&'render mut R::Device>,
    span: tracing::Span,
}

impl<R: GraphicsApi, T: GraphicsApi> fmt::Debug for MultiRenderer<'_, '_, R, T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("MultiRenderer")
            .field("render", &self.render)
            .field("target", &self.target)
            .field("other_renderers", &self.other_renderers)
            .finish()
    }
}

impl<R: GraphicsApi, T: GraphicsApi> AsRef<<R::Device as ApiDevice>::Renderer>
    for MultiRenderer<'_, '_, R, T>
{
    fn as_ref(&self) -> &<R::Device as ApiDevice>::Renderer {
        self.render.renderer()
    }
}

impl<R: GraphicsApi, T: GraphicsApi> AsMut<<R::Device as ApiDevice>::Renderer>
    for MultiRenderer<'_, '_, R, T>
{
    fn as_mut(&mut self) -> &mut <R::Device as ApiDevice>::Renderer {
        self.render.renderer_mut()
    }
}

/// A Framebuffer of a [`MultiRenderer`].
pub struct MultiFramebuffer<'buffer, R: GraphicsApi, T: GraphicsApi>(MultiFramebufferInternal<'buffer, R, T>);
enum MultiFramebufferInternal<'buffer, R: GraphicsApi, T: GraphicsApi> {
    Render(<<R::Device as ApiDevice>::Renderer as RendererSuper>::Framebuffer<'buffer>),
    Target(<<T::Device as ApiDevice>::Renderer as RendererSuper>::Framebuffer<'buffer>),
}

impl<'buffer, R: GraphicsApi, T: GraphicsApi> fmt::Debug for MultiFramebuffer<'buffer, R, T>
where
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::Framebuffer<'buffer>: fmt::Debug,
    <<T::Device as ApiDevice>::Renderer as RendererSuper>::Framebuffer<'buffer>: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match &self.0 {
            MultiFramebufferInternal::Render(framebuffer) => framebuffer.fmt(f),
            MultiFramebufferInternal::Target(framebuffer) => framebuffer.fmt(f),
        }
    }
}

impl<R: GraphicsApi, T: GraphicsApi> Texture for MultiFramebuffer<'_, R, T> {
    fn size(&self) -> Size<i32, BufferCoords> {
        match &self.0 {
            MultiFramebufferInternal::Render(framebuffer) => framebuffer.size(),
            MultiFramebufferInternal::Target(framebuffer) => framebuffer.size(),
        }
    }

    fn width(&self) -> u32 {
        match &self.0 {
            MultiFramebufferInternal::Render(framebuffer) => framebuffer.width(),
            MultiFramebufferInternal::Target(framebuffer) => framebuffer.width(),
        }
    }

    fn height(&self) -> u32 {
        match &self.0 {
            MultiFramebufferInternal::Render(framebuffer) => framebuffer.height(),
            MultiFramebufferInternal::Target(framebuffer) => framebuffer.height(),
        }
    }

    fn format(&self) -> Option<Fourcc> {
        match &self.0 {
            MultiFramebufferInternal::Render(framebuffer) => framebuffer.format(),
            MultiFramebufferInternal::Target(framebuffer) => framebuffer.format(),
        }
    }
}

/// [`Frame`] implementation of a [`MultiRenderer`].
///
/// Leaking the frame will potentially keep it from doing necessary copies
/// of the internal framebuffer for some multi-gpu configurations. The result would
/// be no updated framebuffer contents.
/// Additionally, all problems related to the Frame-implementation of the underlying
/// [`GraphicsApi`] will be present.
pub struct MultiFrame<'render, 'target, 'frame, 'buffer, R: GraphicsApi, T: GraphicsApi>
where
    'buffer: 'frame,
    R: 'static,
    R::Error: 'static,
    T::Error: 'static,
    <R::Device as ApiDevice>::Renderer: ExportMem + ImportDma + ImportMem,
    <T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
    <<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
    node: DrmNode,

    frame: Option<<<R::Device as ApiDevice>::Renderer as RendererSuper>::Frame<'frame, 'buffer>>,
    framebuffer:
        Option<AliasableBox<<<R::Device as ApiDevice>::Renderer as RendererSuper>::Framebuffer<'frame>>>,
    target: Option<TargetFrameData<'target, 'frame, 'buffer, T>>,
    render: *mut &'render mut R::Device,

    dst_transform: Transform,
    size: Size<i32, Physical>,
    damage: Vec<Rectangle<i32, Physical>>,
    span: tracing::span::EnteredSpan,
}

struct TargetData<'target, T: GraphicsApi> {
    device: &'target mut T::Device,
    cached_buffer: &'target mut Option<(bool, Dmabuf)>,
    format: Fourcc,
}

struct TargetFrameData<'target, 'frame, 'buffer, T: GraphicsApi> {
    device: &'frame mut &'target mut T::Device,
    framebuffer: &'frame mut <<T::Device as ApiDevice>::Renderer as RendererSuper>::Framebuffer<'buffer>,
    texture: Option<<<T::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>,
    format: Fourcc,
}

impl<'frame, 'buffer, R: GraphicsApi + 'frame, T: GraphicsApi> fmt::Debug
    for MultiFrame<'_, '_, 'frame, 'buffer, R, T>
where
    'buffer: 'frame,
    R: 'static,
    R::Error: 'static,
    T::Error: 'static,
    <R::Device as ApiDevice>::Renderer: ExportMem + ImportDma + ImportMem,
    <T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
    <<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
    <<T::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: fmt::Debug,
    R::Device: fmt::Debug,
    T::Device: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("MultiFrame")
            .field("node", &self.node)
            .field("render", unsafe { &*self.render })
            .field("target", &self.target)
            .field("dst_transform", &self.dst_transform)
            .field("size", &self.size)
            .field("damage", &self.damage)
            .finish()
    }
}

impl<T: GraphicsApi> fmt::Debug for TargetData<'_, T>
where
    T::Device: fmt::Debug,
    <<T::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("TargetData")
            .field("device", self.device)
            .field("cached_buffer", &self.cached_buffer)
            .field("format", &self.format)
            .finish()
    }
}

impl<T: GraphicsApi> fmt::Debug for TargetFrameData<'_, '_, '_, T>
where
    T::Device: fmt::Debug,
    <<T::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("TargetFrameData")
            .field("device", self.device)
            .field("format", &self.format)
            .finish_non_exhaustive()
    }
}

// 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<'frame, 'buffer, R: GraphicsApi, T: GraphicsApi>
    AsRef<<<R::Device as ApiDevice>::Renderer as RendererSuper>::Frame<'frame, 'buffer>>
    for MultiFrame<'_, '_, 'frame, 'buffer, R, T>
where
    'buffer: 'frame,
    R: 'static,
    R::Error: 'static,
    T::Error: 'static,
    <R::Device as ApiDevice>::Renderer: ExportMem + ImportDma + ImportMem,
    <T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
    <<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
    fn as_ref(&self) -> &<<R::Device as ApiDevice>::Renderer as RendererSuper>::Frame<'frame, 'buffer> {
        self.frame.as_ref().unwrap()
    }
}

impl<'frame, 'buffer, R: GraphicsApi, T: GraphicsApi>
    AsMut<<<R::Device as ApiDevice>::Renderer as RendererSuper>::Frame<'frame, 'buffer>>
    for MultiFrame<'_, '_, 'frame, 'buffer, R, T>
where
    'buffer: 'frame,
    R: 'static,
    R::Error: 'static,
    T::Error: 'static,
    <R::Device as ApiDevice>::Renderer: ExportMem + ImportDma + ImportMem,
    <T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
    <<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
    fn as_mut(
        &mut self,
    ) -> &mut <<R::Device as ApiDevice>::Renderer as RendererSuper>::Frame<'frame, 'buffer> {
        self.frame.as_mut().unwrap()
    }
}

impl<R: GraphicsApi, T: GraphicsApi, Target> Offscreen<Target> for MultiRenderer<'_, '_, R, T>
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 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: Bind<Dmabuf> + ExportMem + ImportDma + ImportMem,
    <T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: Clone + Send,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
    <<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
    #[instrument(level = "trace", parent = &self.span, skip(self))]
    #[profiling::function]
    fn create_buffer(
        &mut self,
        format: Fourcc,
        size: Size<i32, BufferCoords>,
    ) -> Result<Target, <Self as RendererSuper>::Error> {
        if let Some(target) = self.target.as_mut() {
            target
                .device
                .renderer_mut()
                .create_buffer(format, size)
                .map_err(Error::Target)
        } else {
            self.render
                .renderer_mut()
                .create_buffer(format, size)
                .map_err(Error::Render)
        }
    }
}

impl<R: GraphicsApi, T: GraphicsApi, Target> Bind<Target> for MultiRenderer<'_, '_, R, T>
where
    <T::Device as ApiDevice>::Renderer: Bind<Target>,
    <R::Device as ApiDevice>::Renderer: Bind<Target>,
    // 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: Bind<Dmabuf> + ExportMem + ImportDma + ImportMem,
    <T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: Clone + Send,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
    <<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
    #[instrument(level = "trace", parent = &self.span, skip(self, bind))]
    #[profiling::function]
    fn bind<'a>(
        &mut self,
        bind: &'a mut Target,
    ) -> Result<<Self as RendererSuper>::Framebuffer<'a>, <Self as RendererSuper>::Error> {
        if let Some(target) = self.target.as_mut() {
            target
                .device
                .renderer_mut()
                .bind(bind)
                .map(MultiFramebufferInternal::Target)
                .map(MultiFramebuffer)
                .map_err(Error::Target)
        } else {
            self.render
                .renderer_mut()
                .bind(bind)
                .map(MultiFramebufferInternal::Render)
                .map(MultiFramebuffer)
                .map_err(Error::Render)
        }
    }

    fn supported_formats(&self) -> Option<FormatSet> {
        if let Some(target) = self.target.as_ref() {
            Bind::<Target>::supported_formats(target.device.renderer())
        } else {
            Bind::<Target>::supported_formats(self.render.renderer())
        }
    }
}

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

impl<'render, 'target, R: GraphicsApi, T: GraphicsApi> RendererSuper for MultiRenderer<'render, 'target, R, T>
where
    R: 'static,
    R::Error: 'static,
    T::Error: 'static,
    <R::Device as ApiDevice>::Renderer: Bind<Dmabuf> + ExportMem + ImportDma + ImportMem,
    <T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: Clone + Send,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
    <<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
    type Error = Error<R, T>;
    type TextureId = MultiTexture;
    type Framebuffer<'buffer> = MultiFramebuffer<'buffer, R, T>;
    type Frame<'frame, 'buffer>
        = MultiFrame<'render, 'target, 'frame, 'buffer, R, T>
    where
        'buffer: 'frame,
        Self: 'frame;
}

impl<'render, 'target, R: GraphicsApi, T: GraphicsApi> Renderer for MultiRenderer<'render, 'target, R, T>
where
    R: 'static,
    R::Error: 'static,
    T::Error: 'static,
    <R::Device as ApiDevice>::Renderer: Bind<Dmabuf> + ExportMem + ImportDma + ImportMem,
    <T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: Clone + Send,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
    <<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
    fn context_id(&self) -> ContextId<MultiTexture> {
        self.render.renderer().context_id().map()
    }

    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 set_debug_flags(&mut self, flags: DebugFlags) {
        self.render.renderer_mut().set_debug_flags(flags)
    }
    fn debug_flags(&self) -> DebugFlags {
        self.render.renderer().debug_flags()
    }

    #[instrument(level = "trace", parent = &self.span, skip(self, framebuffer))]
    #[profiling::function]
    fn render<'frame, 'buffer>(
        &'frame mut self,
        framebuffer: &'frame mut Self::Framebuffer<'buffer>,
        size: Size<i32, Physical>,
        dst_transform: Transform,
    ) -> Result<MultiFrame<'render, 'target, 'frame, 'buffer, R, T>, Self::Error>
    where
        'buffer: 'frame,
    {
        let target_state = if let Some(target) = self.target.as_mut() {
            let buffer_size = size.to_logical(1).to_buffer(1, Transform::Normal);

            if let Some((_, dmabuf)) = &target.cached_buffer {
                if dmabuf.size() != buffer_size || BufferTrait::format(dmabuf).code != target.format {
                    *target.cached_buffer = None;
                }
            };

            if target.cached_buffer.is_none() {
                match create_shared_dma_framebuffer::<R, T>(buffer_size, self.render, target) {
                    Ok(dmabuf) => {
                        *target.cached_buffer = Some((true, dmabuf));
                    }
                    Err(err) => {
                        warn!(
                            "Error importing dmabuf from {} to {}: {}",
                            *self.render.node(),
                            *target.device.node(),
                            err
                        );
                        info!("Falling back to cpu-copy.");
                        let modifiers = Bind::<Dmabuf>::supported_formats(self.render.renderer())
                            .unwrap_or_default()
                            .into_iter()
                            .filter(|format| format.code == target.format)
                            .map(|f| f.modifier)
                            .collect::<Vec<_>>();
                        let mut dmabuf = self
                            .render
                            .allocator()
                            .create_buffer(
                                buffer_size.w as u32,
                                buffer_size.h as u32,
                                target.format,
                                &modifiers,
                            )
                            .map_err(Error::AllocatorError)?;

                        {
                            // make sure we mark this as a framebuffer on render first (some GL drivers don't like us to do this later).
                            let mut render_framebuffer = self
                                .render
                                .renderer_mut()
                                .bind(&mut dmabuf)
                                .map_err(Error::Render)?;
                            self.render
                                .renderer_mut()
                                .render(&mut render_framebuffer, size, dst_transform)
                                .map_err(Error::Render)?;
                            // drop everything
                        }

                        *target.cached_buffer = Some((false, dmabuf));
                    }
                }
            };

            // try to import on target node
            let (direct, ref mut dmabuf) = target.cached_buffer.as_mut().unwrap();
            // TODO: We could cache that texture all the way back to the GpuManager in a HashMap<WeakDmabuf, Texture>.
            let texture = (*direct)
                .then(|| {
                    target
                        .device
                        .renderer_mut()
                        .import_dmabuf(dmabuf, Some(&[Rectangle::from_size(buffer_size)]))
                        .map_err(Error::Target)
                })
                .transpose()?;
            let framebuffer = self.render.renderer_mut().bind(dmabuf).map_err(Error::Render)?;

            Some((&mut target.device, framebuffer, texture, target.format))
        } else {
            None
        };

        let node = *self.render.node();
        let ptr = &mut self.render as *mut _;

        let mut target = None;
        let mut new_framebuffer = None;
        let frame = match &mut framebuffer.0 {
            MultiFramebufferInternal::Render(framebuffer) => self
                .render
                .renderer_mut()
                .render(framebuffer, size, dst_transform)
                .map_err(Error::Render)?,
            MultiFramebufferInternal::Target(target_framebuffer) => {
                let (target_device, render_framebuffer, texture, format) = target_state.unwrap();
                target = Some(TargetFrameData {
                    device: target_device,
                    framebuffer: target_framebuffer,
                    texture,
                    format,
                });
                let mut render_framebuffer = AliasableBox::from_unique(Box::new(render_framebuffer));

                // We extend the lifetime to 'frame, because this is self-referencial.
                // SAFETY:
                //  - We drop the framebuffer before `target`, which contains the referenced dmabuf
                //  - We drop the frame before the framebuffer as we store both in `MultiFrame`
                //  - `Frame` can't store an invalid pointer into the framebuffer, as the framebuffer is moved
                //    to the heap and won't be moved by the compiler thanks to `AliasableBox`.
                let frame = unsafe {
                    std::mem::transmute::<
                        <<R::Device as ApiDevice>::Renderer as RendererSuper>::Frame<'_, '_>,
                        <<R::Device as ApiDevice>::Renderer as RendererSuper>::Frame<'frame, 'buffer>,
                    >(
                        self.render
                            .renderer_mut()
                            .render(&mut *render_framebuffer, size, dst_transform)
                            .map_err(Error::Render)?,
                    )
                };
                new_framebuffer = Some(render_framebuffer);
                frame
            }
        };

        let span = trace_span!(
            parent: &self.span,
            "renderer_multi_frame",
            direct = target.as_ref().is_some_and(|t| t.texture.is_some()),
        )
        .entered();

        Ok(MultiFrame {
            node,
            frame: Some(frame),
            framebuffer: new_framebuffer,
            render: ptr, // this is fine, as long as we have the frame, this ptr is valid
            target,
            dst_transform,
            size,
            damage: Vec::new(),
            span,
        })
    }

    #[profiling::function]
    fn wait(&mut self, sync: &sync::SyncPoint) -> Result<(), Self::Error> {
        self.render.renderer_mut().wait(sync).map_err(Error::Render)
    }

    #[profiling::function]
    fn cleanup_texture_cache(&mut self) -> Result<(), Self::Error> {
        if let Some(target) = self.target.as_mut() {
            target
                .device
                .renderer_mut()
                .cleanup_texture_cache()
                .map_err(Error::Target)?;
        }
        self.render
            .renderer_mut()
            .cleanup_texture_cache()
            .map_err(Error::Render)?;
        Ok(())
    }
}

fn create_shared_dma_framebuffer<R, T: GraphicsApi>(
    buffer_size: Size<i32, BufferCoords>,
    src: &mut R::Device,
    target: &mut TargetData<'_, T>,
) -> Result<Dmabuf, Error<R, T>>
where
    R: GraphicsApi + 'static,
    R::Error: 'static,
    T::Error: 'static,
    <R::Device as ApiDevice>::Renderer: Bind<Dmabuf> + ExportMem + ImportDma + ImportMem,
    <T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
    <<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
    let target_formats = ImportDma::dmabuf_formats(target.device.renderer())
        .iter()
        .filter(|format| format.code == target.format)
        .copied()
        .collect::<FormatSet>();
    let render_formats = Bind::<Dmabuf>::supported_formats(src.renderer()).unwrap_or_default();
    let formats = target_formats.intersection(&render_formats);
    let target_modifiers = formats
        .map(|format| format.modifier)
        .filter(|modifier| *modifier != Modifier::Invalid)
        .collect::<Vec<_>>();

    if target_modifiers.is_empty() {
        return Err(Error::ImportFailed);
    }

    info!(
        "Found dma-copy set for {:?} <-> {:?}: {:?}@{:?}",
        src.node(),
        target.device.node(),
        target.format,
        target_modifiers,
    );

    let mut dmabuf = src
        .allocator()
        .create_buffer(
            buffer_size.w as u32,
            buffer_size.h as u32,
            target.format,
            &target_modifiers,
        )
        .map_err(Error::AllocatorError)?;

    // verify we can bind on src and import on target

    src.renderer_mut().bind(&mut dmabuf).map_err(Error::Render)?;

    target
        .device
        .renderer_mut()
        .import_dmabuf(&dmabuf, Some(&[Rectangle::from_size(buffer_size)]))
        .map_err(Error::Target)?;

    Ok(dmabuf)
}

impl<'frame, 'buffer, R: GraphicsApi, T: GraphicsApi> MultiFrame<'_, '_, 'frame, 'buffer, R, T>
where
    R: 'static,
    R::Error: 'static,
    T::Error: 'static,
    <R::Device as ApiDevice>::Renderer: ExportMem + ImportDma + ImportMem,
    <T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
    <<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
    fn flush_frame(&mut self) -> Result<(), Error<R, T>> {
        if self.target.is_some() {
            let _ = self.finish_internal()?;
            // now the frame is gone, lets use our unholy ptr till the end of this call:
            // SAFETY:
            // - The renderer will never be invalid because the lifetime of the frame must be shorter than the renderer.
            // - The pointer can't be aliased because of the following:
            //   - Creating a frame requires an `&mut` reference to the renderer, making the mutable borrow safe.
            //   - The mutable reference is used in a function which mutably borrows the frame, that either being `.finish()`
            //      (which takes ownership of the frame) or dropping the frame.
            let render = unsafe { &mut *self.render };

            // We extend the lifetime to 'frame, because this is self-referencial.
            // SAFETY:
            //  - We drop the framebuffer before `target`, which contains the referenced dmabuf
            //  - We drop the frame before the framebuffer as we store both in `MultiFrame`
            //  - `Frame` can't store an invalid pointer into the framebuffer, as the framebuffer is moved
            //    to the heap and won't be moved by the compiler thanks to `AliasableBox`.
            let frame = unsafe {
                std::mem::transmute::<
                    <<R::Device as ApiDevice>::Renderer as RendererSuper>::Frame<'_, '_>,
                    <<R::Device as ApiDevice>::Renderer as RendererSuper>::Frame<'frame, 'buffer>,
                >(
                    render
                        .renderer_mut()
                        .render(self.framebuffer.as_mut().unwrap(), self.size, self.dst_transform)
                        .map_err(Error::Render)?,
                )
            };
            self.frame = Some(frame);
        }
        Ok(())
    }

    #[instrument(level = "trace", parent = &self.span, skip(self))]
    #[profiling::function]
    fn finish_internal(&mut self) -> Result<sync::SyncPoint, Error<R, T>> {
        if let Some(frame) = self.frame.take() {
            let sync = frame.finish().map_err(Error::Render)?;

            // now the frame is gone, lets use our unholy ptr till the end of this call:
            // SAFETY:
            // - The renderer will never be invalid because the lifetime of the frame must be shorter than the renderer.
            // - The pointer can't be aliased because of the following:
            //   - Creating a frame requires an `&mut` reference to the renderer, making the mutable borrow safe.
            //   - The mutable reference is used in a function which mutably borrows the frame, that either being `.finish()`
            //      (which takes ownership of the frame) or dropping the frame.
            let render = unsafe { &mut *self.render };

            let damage_area = self.dst_transform.transform_size(self.size.to_logical(1));
            let mut damage = std::mem::take(&mut self.damage)
                .into_iter()
                .map(|rect| rect.to_logical(1).to_buffer(1, self.dst_transform, &damage_area))
                .collect::<Vec<_>>();

            let buffer_size = self.size.to_logical(1).to_buffer(1, Transform::Normal);
            if let Some(target) = self.target.as_mut() {
                if let Some(texture) = target.texture.as_ref() {
                    // try gpu copy
                    let damage = damage
                        .iter()
                        .map(|rect| rect.to_logical(1, Transform::Normal, &buffer_size).to_physical(1))
                        .collect::<Vec<_>>();
                    let mut frame = target
                        .device
                        .renderer_mut()
                        .render(target.framebuffer, self.size, Transform::Normal)
                        .map_err(Error::Target)?;
                    frame.wait(&sync).map_err(Error::Target)?;
                    frame
                        .clear(Color32F::TRANSPARENT, &damage)
                        .map_err(Error::Target)?;
                    frame
                        .render_texture_from_to(
                            texture,
                            Rectangle::from_size(buffer_size).to_f64(),
                            Rectangle::from_size(self.size),
                            &damage,
                            &[Rectangle::from_size(self.size)],
                            Transform::Normal,
                            1.0,
                        )
                        .map_err(Error::Target)?;
                    let sync = frame.finish().map_err(Error::Target)?;
                    render
                        .renderer_mut()
                        .cleanup_texture_cache()
                        .map_err(Error::Render)?;

                    return Ok(sync);
                }

                let format = if target
                    .device
                    .renderer()
                    .mem_formats()
                    .any(|fmt| fmt == target.format)
                {
                    target.format
                } else {
                    // GL mandates this to be supported for downloading, this will truncate 10-bit data
                    Fourcc::Abgr8888
                };

                // cpu copy
                damage.dedup();
                damage.retain(|rect| rect.overlaps_or_touches(Rectangle::from_size(buffer_size)));
                damage.retain(|rect| rect.size.h > 0 && rect.size.w > 0);

                let mut copy_rects = // merge overlapping rectangles
                    damage.iter().cloned().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: &Rectangle<i32, BufferCoords>| other.overlaps_or_touches(rect));

                        for overlap in overlapping {
                            rect = rect.merge(overlap);
                        }
                        new_damage.push(rect);
                        new_damage
                    });
                if copy_rects.len() > MAX_CPU_COPIES {
                    copy_rects = Vec::from([Rectangle::from_size(buffer_size)]);
                }

                let mut mappings = Vec::new();
                for rect in copy_rects {
                    let mapping = (
                        ExportMem::copy_framebuffer(
                            render.renderer_mut(),
                            self.framebuffer.as_ref().unwrap(),
                            rect,
                            format,
                        )
                        .map_err(Error::Render)?,
                        rect,
                    );
                    mappings.push(mapping);
                }

                if mappings.is_empty() {
                    render
                        .renderer_mut()
                        .cleanup_texture_cache()
                        .map_err(Error::Render)?;
                    return Ok(sync::SyncPoint::signaled());
                }

                let textures = mappings
                    .into_iter()
                    .map(|(mapping, rect)| {
                        let slice = ExportMem::map_texture(render.renderer_mut(), &mapping)
                            .map_err(Error::Render::<R, T>)?;
                        let texture = target
                            .device
                            .renderer_mut()
                            .import_memory(slice, TextureMapping::format(&mapping), rect.size, false)
                            .map_err(Error::Target)?;
                        Ok((texture, rect))
                    })
                    .collect::<Result<Vec<_>, _>>()?;

                let mut frame = target
                    .device
                    .renderer_mut()
                    .render(target.framebuffer, self.size, Transform::Normal)
                    .map_err(Error::Target)?;
                for (texture, rect) in textures {
                    for damage_rect in damage.iter().filter_map(|dmg_rect| dmg_rect.intersection(rect)) {
                        let dst = damage_rect
                            .to_logical(1, Transform::Normal, &buffer_size)
                            .to_physical(1);
                        let src = Rectangle::new(damage_rect.loc - rect.loc, damage_rect.size).to_f64();
                        let damage = &[Rectangle::from_size(dst.size)];
                        frame
                            .clear(Color32F::TRANSPARENT, &[dst])
                            .map_err(Error::Target)?;
                        frame
                            .render_texture_from_to(
                                &texture,
                                src,
                                dst,
                                damage,
                                &[Rectangle::from_size(self.size)],
                                Transform::Normal,
                                1.0,
                            )
                            .map_err(Error::Target)?;
                    }
                }
                let sync = frame.finish().map_err(Error::Target)?;
                render
                    .renderer_mut()
                    .cleanup_texture_cache()
                    .map_err(Error::Render)?;
                return Ok(sync);
            }

            render
                .renderer_mut()
                .cleanup_texture_cache()
                .map_err(Error::Render)?;
            return Ok(sync);
        }

        Ok(sync::SyncPoint::signaled())
    }
}

impl<R: GraphicsApi, T: GraphicsApi> Drop for MultiFrame<'_, '_, '_, '_, R, T>
where
    R: 'static,
    R::Error: 'static,
    T::Error: 'static,
    <R::Device as ApiDevice>::Renderer: ExportMem + ImportDma + ImportMem,
    <T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
    <<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
    fn drop(&mut self) {
        if let Err(err) = self.finish_internal() {
            warn!("Ignored error finishing MultiFrame on drop: {}", err);
        }
    }
}

/// [`Texture`]s produced by a [`MultiRenderer`].
#[derive(Debug, Clone)]
pub struct MultiTexture(Arc<Mutex<MultiTextureInternal>>);
#[derive(Debug)]
struct MultiTextureInternal {
    textures: HashMap<TypeId, HashMap<DrmNode, GpuSingleTexture>>,
    size: Size<i32, BufferCoords>,
    format: Option<Fourcc>,
    #[allow(dead_code)]
    buffer_format: Format,
}
// SAFETY: We require `Send` for textures of renderers suitable for the MultiRenderer.
//  Type erasure just forces us to do this instead.
unsafe impl Send for MultiTextureInternal {}

type DamageAnyTextureMappings = Vec<(Rectangle<i32, BufferCoords>, Box<dyn Any + 'static>)>;

#[derive(Debug)]
enum GpuSingleTexture {
    Direct(Box<dyn Any + 'static>),
    Dma {
        texture: Box<dyn Any + 'static>,
        dmabuf: Dmabuf,
        sync: Option<SyncPoint>,
    },
    Mem {
        external_shadow: Option<(Dmabuf, Box<dyn Any + 'static>)>,
        texture: Option<Box<dyn Any + 'static>>,
        mappings: Option<(DrmNode, DamageAnyTextureMappings)>,
    },
}

impl MultiTexture {
    #[cfg(feature = "wayland_frontend")]
    #[profiling::function]
    fn from_surface(
        surface: Option<&crate::wayland::compositor::SurfaceData>,
        size: Size<i32, BufferCoords>,
        buffer_format: Format,
    ) -> MultiTexture {
        let internal = surface
            .and_then(|surface| {
                surface
                    .data_map
                    .get::<Arc<Mutex<MultiTextureInternal>>>()
                    .cloned()
            })
            .unwrap_or_else(|| {
                Arc::new(Mutex::new(MultiTextureInternal {
                    textures: HashMap::new(),
                    size,
                    format: None,
                    buffer_format,
                }))
            });
        {
            let mut internal = internal.lock().unwrap();
            if internal.size != size || internal.buffer_format != buffer_format {
                internal.textures.clear();
                internal.format = None;
                internal.size = size;
                internal.buffer_format = buffer_format;
            }
        }
        MultiTexture(internal)
    }

    fn new(size: Size<i32, BufferCoords>, buffer_format: Format) -> MultiTexture {
        MultiTexture(Arc::new(Mutex::new(MultiTextureInternal {
            textures: HashMap::new(),
            size,
            format: None,
            buffer_format,
        })))
    }

    /// Attempt to get a texture of type `T: Renderer::TextureId` given the renderer type `A` for the given `DrmNode`.
    ///
    /// Will return `None` if either:
    ///
    /// - No textures are available for the Renderer type `A``
    /// - No texture of type `T` is available for the given `DrmNode`
    pub fn get<A: GraphicsApi + 'static>(
        &self,
        render: &DrmNode,
    ) -> Option<<<A::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>
    where
        <<A::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: Clone + Send + 'static,
    {
        let tex = self.0.lock().unwrap();
        tex.textures
            .get(&TypeId::of::<A>())
            .and_then(|textures| textures.get(render))
            .and_then(|texture| match texture {
                GpuSingleTexture::Direct(texture) => Some(texture),
                GpuSingleTexture::Dma { texture, .. } => Some(texture),
                GpuSingleTexture::Mem { texture, .. } => texture.as_ref(),
            })
            .and_then(|texture| {
                <dyn Any>::downcast_ref::<<<A::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>(
                    &**texture,
                )
            })
            .cloned()
    }

    fn needs_synchronization<A: GraphicsApi + 'static>(&self, render: &DrmNode) -> Option<SyncPoint>
    where
        <<A::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: 'static,
    {
        let mut tex = self.0.lock().unwrap();
        tex.textures
            .get_mut(&TypeId::of::<A>())
            .and_then(|textures| textures.get_mut(render))
            .and_then(|texture| match texture {
                GpuSingleTexture::Direct(_) => None,
                GpuSingleTexture::Dma { sync, .. } => sync.take(),
                GpuSingleTexture::Mem { .. } => None,
            })
    }

    fn insert_texture<A: GraphicsApi + 'static>(
        &mut self,
        render: DrmNode,
        texture: <<A::Device as ApiDevice>::Renderer as RendererSuper>::TextureId,
    ) where
        <<A::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: 'static,
    {
        let mut tex = self.0.lock().unwrap();
        let format = texture.format();
        if format != tex.format && !tex.textures.is_empty() {
            warn!(has = ?tex.format, got = ?format, "Multi-SubTexture with wrong format!");
            return;
        }
        tex.format = format;

        trace!(
            "Inserting into: {:p} for {:?}: {:?}",
            Arc::as_ptr(&self.0),
            render,
            tex
        );
        let textures = tex.textures.entry(TypeId::of::<A>()).or_default();
        textures.insert(render, GpuSingleTexture::Direct(Box::new(texture) as Box<_>));
    }

    #[cfg(feature = "wayland_frontend")]
    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_ref = self.0.lock().unwrap();
        let tex = &mut *tex_ref;

        let textures = tex.textures.entry(TypeId::of::<R>()).or_default();
        let (old_texture, old_mapping, external_shadow) = textures
            .remove(&render)
            .map(|single| match single {
                GpuSingleTexture::Mem {
                    texture,
                    mappings,
                    external_shadow,
                } => (texture, mappings, external_shadow),
                _ => (None, None, None),
            })
            .unwrap_or((None, None, None));
        let old_texture = old_texture.filter(|tex| {
            <dyn Any>::downcast_ref::<<<R::Device as ApiDevice>::Renderer as RendererSuper>::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 format = tex.format;
        let new_mappings = new_mappings
            .filter(|(_, mapping)| {
                let mapping_fmt = TextureMapping::format(mapping);
                if old_texture.is_some() && Some(mapping_fmt) != format {
                    warn!(has = ?format, got = ?mapping_fmt, "Multi-SubTexture Mapping with wrong format!");
                    false
                } else {
                    tex.format = Some(mapping_fmt);
                    true
                }
            })
            .map(|(r, m)| (r, Box::new(m) as Box<dyn Any + 'static>))
            .collect::<Vec<_>>();
        mappings.retain(|(region, _)| {
            !new_mappings
                .iter()
                .any(|(new_region, _)| new_region.contains_rect(*region))
        });
        mappings.extend(new_mappings);

        textures.insert(
            render,
            GpuSingleTexture::Mem {
                mappings: Some((source, mappings)),
                texture: old_texture,
                external_shadow,
            },
        );
    }
}

impl Texture for MultiTexture {
    fn size(&self) -> Size<i32, BufferCoords> {
        self.0.lock().unwrap().size
    }
    fn width(&self) -> u32 {
        self.0.lock().unwrap().size.w as u32
    }
    fn height(&self) -> u32 {
        self.0.lock().unwrap().size.h as u32
    }
    fn format(&self) -> Option<Fourcc> {
        self.0.lock().unwrap().format
    }
}

impl<R: GraphicsApi, T: GraphicsApi> Frame for MultiFrame<'_, '_, '_, '_, R, T>
where
    R: 'static,
    R::Error: 'static,
    T::Error: 'static,
    <R::Device as ApiDevice>::Renderer: ExportMem + ImportDma + ImportMem,
    <T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: Clone + Send,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
    <<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
    type Error = Error<R, T>;
    type TextureId = MultiTexture;

    fn context_id(&self) -> ContextId<MultiTexture> {
        self.frame.as_ref().unwrap().context_id().map()
    }

    #[instrument(level = "trace", parent = &self.span, skip(self))]
    #[profiling::function]
    fn clear(&mut self, color: Color32F, at: &[Rectangle<i32, Physical>]) -> Result<(), Error<R, T>> {
        self.damage.extend(at);
        self.frame
            .as_mut()
            .unwrap()
            .clear(color, at)
            .map_err(Error::Render)
    }

    #[instrument(level = "trace", parent = &self.span, skip(self))]
    #[profiling::function]
    fn draw_solid(
        &mut self,
        dst: Rectangle<i32, Physical>,
        damage: &[Rectangle<i32, Physical>],
        color: Color32F,
    ) -> Result<(), Self::Error> {
        self.damage.extend(damage.iter().copied().map(|mut rect| {
            rect.loc += dst.loc;
            rect
        }));
        self.frame
            .as_mut()
            .unwrap()
            .draw_solid(dst, damage, color)
            .map_err(Error::Render)
    }

    #[instrument(level = "trace", parent = &self.span, skip(self))]
    #[profiling::function]
    fn render_texture_from_to(
        &mut self,
        texture: &MultiTexture,
        src: Rectangle<f64, BufferCoords>,
        dst: Rectangle<i32, Physical>,
        damage: &[Rectangle<i32, Physical>],
        opaque_regions: &[Rectangle<i32, Physical>],
        src_transform: Transform,
        alpha: f32,
    ) -> Result<(), Error<R, T>> {
        let sync = texture.needs_synchronization::<R>(&self.node);
        if let Some(texture) = texture.get::<R>(&self.node) {
            self.damage.extend(damage.iter().copied().map(|mut rect| {
                rect.loc += dst.loc;
                rect
            }));
            if let Some(sync) = sync {
                if let Err(err) = self.frame.as_mut().unwrap().wait(&sync) {
                    trace!(?err, "Failed to import sync point, blocking");
                    let _ = sync.wait(); // ignore interrupt errors
                }
            }
            self.frame
                .as_mut()
                .unwrap()
                .render_texture_from_to(&texture, src, dst, damage, opaque_regions, src_transform, alpha)
                .map_err(Error::Render)
        } else {
            warn!(
                "Failed to render texture {:?}, import for wrong devices {:?}? {:?}",
                Arc::as_ptr(&texture.0),
                self.node,
                texture.0.lock().unwrap(),
            );
            Ok(())
        }
    }

    fn transformation(&self) -> Transform {
        self.frame.as_ref().unwrap().transformation()
    }

    #[profiling::function]
    fn wait(&mut self, sync: &sync::SyncPoint) -> Result<(), Self::Error> {
        self.frame.as_mut().unwrap().wait(sync).map_err(Error::Render)
    }

    #[profiling::function]
    fn finish(mut self) -> Result<sync::SyncPoint, Self::Error> {
        self.finish_internal()
    }
}

#[cfg(feature = "wayland_frontend")]
impl<R: GraphicsApi, T: GraphicsApi> ImportMemWl for MultiRenderer<'_, '_, R, T>
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: Bind<Dmabuf> + ExportMem + ImportDma + ImportMem,
    <T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: Clone + Send,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
    <<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
    #[instrument(level = "trace", parent = &self.span, skip(self))]
    #[profiling::function]
    fn import_shm_buffer(
        &mut self,
        buffer: &wl_buffer::WlBuffer,
        surface: Option<&crate::wayland::compositor::SurfaceData>,
        damage: &[Rectangle<i32, BufferCoords>],
    ) -> Result<<Self as RendererSuper>::TextureId, <Self as RendererSuper>::Error> {
        let shm_texture = self
            .render
            .renderer_mut()
            .import_shm_buffer(buffer, surface, damage)
            .map_err(Error::Render)?;
        let (dimensions, format) = shm::with_buffer_contents(buffer, |_, _, data| {
            Ok((
                (data.width, data.height).into(),
                shm::shm_format_to_fourcc(data.format)
                    .map(|code| Format {
                        code,
                        modifier: Modifier::Linear,
                    })
                    .ok_or(Error::ImportFailed)?,
            ))
        })
        .map_err(|_| Error::ImportFailed)??;
        let mut texture = MultiTexture::from_surface(surface, dimensions, format);
        texture.insert_texture::<R>(*self.render.node(), shm_texture);
        Ok(texture)
    }

    fn shm_formats(&self) -> Box<dyn Iterator<Item = wl_shm::Format>> {
        ImportMemWl::shm_formats(self.render.renderer())
    }
}

impl<R: GraphicsApi, T: GraphicsApi> ImportMem for MultiRenderer<'_, '_, R, T>
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: Bind<Dmabuf> + ExportMem + ImportDma + ImportMem,
    <T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: Clone + Send,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
    <<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
    #[instrument(level = "trace", parent = &self.span, skip(self))]
    #[profiling::function]
    fn import_memory(
        &mut self,
        data: &[u8],
        format: Fourcc,
        size: Size<i32, BufferCoords>,
        flipped: bool,
    ) -> Result<<Self as RendererSuper>::TextureId, <Self as RendererSuper>::Error> {
        let mem_texture = self
            .render
            .renderer_mut()
            .import_memory(data, format, size, flipped)
            .map_err(Error::Render)?;
        let mut texture = MultiTexture::new(
            size,
            Format {
                code: format,
                modifier: Modifier::Linear,
            },
        );
        texture.insert_texture::<R>(*self.render.node(), mem_texture);
        Ok(texture)
    }

    #[instrument(level = "trace", parent = &self.span, skip(self))]
    #[profiling::function]
    fn update_memory(
        &mut self,
        texture: &<Self as RendererSuper>::TextureId,
        data: &[u8],
        region: Rectangle<i32, BufferCoords>,
    ) -> Result<(), <Self as RendererSuper>::Error> {
        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)
    }

    fn mem_formats(&self) -> Box<dyn Iterator<Item = Fourcc>> {
        ImportMem::mem_formats(self.render.renderer())
    }
}

#[cfg(feature = "wayland_frontend")]
impl<R: GraphicsApi, T: GraphicsApi> ImportDmaWl for MultiRenderer<'_, '_, R, T>
where
    <R::Device as ApiDevice>::Renderer: ImportDmaWl + ImportMem + ExportMem,
    <T::Device as ApiDevice>::Renderer: Bind<Dmabuf> + 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: Bind<Dmabuf> + ExportMem + ImportDma + ImportMem,
    <T::Device as ApiDevice>::Renderer: Bind<Dmabuf> + ImportDma + ImportMem,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: Clone + Send,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
    <<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
    #[instrument(level = "trace", parent = &self.span, skip(self))]
    #[profiling::function]
    fn import_dma_buffer(
        &mut self,
        buffer: &wl_buffer::WlBuffer,
        surface: Option<&SurfaceData>,
        damage: &[Rectangle<i32, BufferCoords>],
    ) -> Result<<Self as RendererSuper>::TextureId, <Self as RendererSuper>::Error> {
        let dmabuf = get_dmabuf(buffer).expect("import_dma_buffer without checking buffer type?");
        let texture = MultiTexture::from_surface(surface, dmabuf.size(), dmabuf.format());
        let texture_ref = texture.0.clone();
        let res = self.import_dmabuf_internal(dmabuf, texture, Some(damage));
        if res.is_ok() {
            if let Some(surface) = surface {
                surface.data_map.insert_if_missing_threadsafe(|| texture_ref);
            }
        }
        res
    }
}

impl<R: GraphicsApi, T: GraphicsApi> ImportDma for MultiRenderer<'_, '_, R, T>
where
    <R::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
    <T::Device as ApiDevice>::Renderer: Bind<Dmabuf> + 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: Bind<Dmabuf> + ExportMem + ImportDma + ImportMem,
    <T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: Clone + Send,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
    <<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
    fn dmabuf_formats(&self) -> FormatSet {
        ImportDma::dmabuf_formats(self.render.renderer())
    }

    fn has_dmabuf_format(&self, format: Format) -> bool {
        ImportDma::has_dmabuf_format(self.render.renderer(), format)
    }

    #[instrument(level = "trace", parent = &self.span, skip(self))]
    #[profiling::function]
    fn import_dmabuf(
        &mut self,
        dmabuf: &Dmabuf,
        damage: Option<&[Rectangle<i32, BufferCoords>]>,
    ) -> Result<<Self as RendererSuper>::TextureId, <Self as RendererSuper>::Error> {
        let texture = MultiTexture::new(dmabuf.size(), dmabuf.format());
        self.import_dmabuf_internal(dmabuf, texture, damage)
    }
}

fn import_on_src_node<'a, R, T>(
    dmabuf: &Dmabuf,
    damage: Option<&[Rectangle<i32, BufferCoords>]>,
    texture: &mut MultiTexture,
    render: &mut R::Device,
    mut target: Option<&mut T::Device>,
    mut others: impl Iterator<Item = &'a mut R::Device>,
) -> Result<DrmNode, Error<R, T>>
where
    R: GraphicsApi + 'static,
    <R as GraphicsApi>::Device: 'a,
    <<R as GraphicsApi>::Device as ApiDevice>::Renderer: Renderer + ImportDma,
    <<<R as GraphicsApi>::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: 'static,
    T: GraphicsApi + 'static,
    <<T as GraphicsApi>::Device as ApiDevice>::Renderer: Renderer + ImportDma,
    <<<T as GraphicsApi>::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: 'static,
{
    match dmabuf.node() {
        Some(node) => {
            if node == *render.node() {
                let imported = render
                    .renderer_mut()
                    .import_dmabuf(dmabuf, damage)
                    .map_err(Error::Render)?;
                texture.insert_texture::<R>(node, imported);
            } else if target.as_ref().is_some_and(|target| node == *target.node()) {
                let imported = target
                    .unwrap()
                    .renderer_mut()
                    .import_dmabuf(dmabuf, damage)
                    .map_err(Error::Target)?;
                texture.insert_texture::<T>(node, imported);
            } else if let Some(other) = others.find(|other| node == *other.node()) {
                let imported = other
                    .renderer_mut()
                    .import_dmabuf(dmabuf, damage)
                    .map_err(Error::Render)?;
                texture.insert_texture::<R>(node, imported);
            } else {
                return Err(Error::DeviceMissing);
            };

            Ok(node)
        }
        None => {
            // try them all
            let node = if let Ok(imported) = render.renderer_mut().import_dmabuf(dmabuf, damage) {
                let node = *render.node();
                texture.insert_texture::<R>(node, imported);
                node
            } else if let Some(imported) = target
                .as_mut()
                .and_then(|target| target.renderer_mut().import_dmabuf(dmabuf, damage).ok())
            {
                let node = *target.as_ref().unwrap().node();
                texture.insert_texture::<T>(node, imported);
                node
            } else if let Some((node, imported)) = others.find_map(|other| {
                other
                    .renderer_mut()
                    .import_dmabuf(dmabuf, damage)
                    .ok()
                    .map(|imported| (*other.node(), imported))
            }) {
                texture.insert_texture::<R>(node, imported);
                node
            } else {
                return Err(Error::DeviceMissing);
            };
            dmabuf.set_node(node);

            Ok(node)
        }
    }
}

fn dma_shadow_copy<S, T>(
    src_texture: &<<S::Device as ApiDevice>::Renderer as RendererSuper>::TextureId,
    damage: Option<&[Rectangle<i32, BufferCoords>]>,
    slot: &mut Option<(Dmabuf, Box<dyn Any + 'static>, Option<SyncPoint>)>,
    src: &mut S::Device,
    mut target: Option<&mut T::Device>,
) -> Result<(), Error<S, T>>
where
    S: GraphicsApi,
    T: GraphicsApi,
    <S::Device as ApiDevice>::Renderer: Renderer + ImportDma + Bind<Dmabuf>,
    <T::Device as ApiDevice>::Renderer: Renderer + ImportDma,
    <<S::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: 'static,
    <<T::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: 'static,
{
    let format = src_texture.format().unwrap_or(Fourcc::Abgr8888);
    let read_formats = if let Some(target) = target.as_ref() {
        ImportDma::dmabuf_formats(target.renderer())
    } else {
        ImportDma::dmabuf_formats(src.renderer())
    }
    .iter()
    .filter(|f| f.code == format)
    .copied()
    .collect::<FormatSet>();
    let write_formats = Bind::<Dmabuf>::supported_formats(src.renderer()).unwrap_or_default();
    let modifiers = read_formats
        .intersection(&write_formats)
        .map(|f| f.modifier)
        .collect::<Vec<_>>();

    if modifiers.is_empty() {
        return Err(Error::ImportFailed);
    }

    let ((shadow_buffer, _, existing_sync_point), is_new_buffer) = if slot
        .as_ref()
        .is_some_and(|(buffer, _, _)| buffer.format().code == format)
    {
        (slot.as_mut().unwrap(), false)
    } else {
        let shadow_buffer = src
            .allocator()
            .create_buffer(src_texture.width(), src_texture.height(), format, &modifiers)
            .map_err(Error::AllocatorError)?;

        let target_texture = if let Some(target) = target.as_mut() {
            Box::<<<T::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>::new(
                target
                    .renderer_mut()
                    .import_dmabuf(&shadow_buffer, None)
                    .map_err(Error::Target)?,
            ) as Box<dyn Any + 'static>
        } else {
            Box::<<<S::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>::new(
                src.renderer_mut()
                    .import_dmabuf(&shadow_buffer, None)
                    .map_err(Error::Render)?,
            ) as Box<dyn Any + 'static>
        };
        (slot.get_or_insert((shadow_buffer, target_texture, None)), true)
    };

    let src_renderer = src.renderer_mut();
    if let Some(sync) = existing_sync_point.take() {
        if let Err(err) = src_renderer.wait(&sync) {
            debug!(?err, "Unable to wait for existing sync_point, blocking..");
            let _ = sync.wait(); // ignore interrupt errors
        }
    }
    let mut framebuffer = src_renderer.bind(shadow_buffer).map_err(Error::Render)?;
    let shadow_size = Size::from((src_texture.width() as i32, src_texture.height() as i32));
    let mut frame = src_renderer
        .render(&mut framebuffer, shadow_size, Transform::Normal)
        .map_err(Error::Render)?;

    let damage_slice = [Rectangle::from_size(shadow_size)];
    let damage = unsafe {
        std::mem::transmute::<Option<&[Rectangle<i32, BufferCoords>]>, Option<&[Rectangle<i32, Physical>]>>(
            damage,
        )
    } // TODO: use bytemuck for stuff like this?
    .filter(|_| !is_new_buffer)
    .unwrap_or(&damage_slice);

    frame
        .clear(Color32F::TRANSPARENT, damage)
        .map_err(Error::Render)?;
    frame
        .render_texture_from_to(
            src_texture,
            Rectangle::from_size(src_texture.size()).to_f64(),
            Rectangle::from_size(shadow_size),
            damage,
            &[],
            Transform::Normal,
            1.0,
        )
        .map_err(Error::Render)?;
    *existing_sync_point = Some(frame.finish().map_err(Error::Render)?);

    // shadow buffer contains our copy and is readable by target and the original buffer was never migrated
    Ok(())
}

type BoxedTextureMappingAndDamage<S> = (
    Box<<<<S as GraphicsApi>::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping>,
    Rectangle<i32, BufferCoords>,
);

type MemTexture<S, T> = (
    Option<Vec<BoxedTextureMappingAndDamage<S>>>,
    Option<Box<<<<T as GraphicsApi>::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>>,
);

fn mem_copy<S, T>(
    src_texture: &<<S::Device as ApiDevice>::Renderer as RendererSuper>::TextureId,
    damage: Option<&[Rectangle<i32, BufferCoords>]>,
    slot: &mut Option<MemTexture<S, T>>,
    src: &mut S::Device,
    target: &mut T::Device,
) -> Result<(), Error<S, T>>
where
    S: GraphicsApi,
    T: GraphicsApi,
    <S::Device as ApiDevice>::Renderer: Renderer + ExportMem + Bind<Dmabuf>,
    <T::Device as ApiDevice>::Renderer: Renderer + ImportMem,
{
    let format = src_texture
        .format()
        // Check the target device supports the format, if not use 8-bit format
        .filter(|format| target.renderer().mem_formats().any(|fmt| fmt == *format))
        // The GL spec *always* supports this format.
        // TODO: Re-evaluate this, once we support vulkan
        .unwrap_or(Fourcc::Abgr8888);

    let texture_rect = Rectangle::from_size((src_texture.width() as i32, src_texture.height() as i32).into());
    let damage = damage.map(|damage| {
        damage
            .iter()
            .flat_map(|rect| rect.intersection(texture_rect))
            .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_or_touches(rect));

                for overlap in overlapping {
                    rect = rect.merge(overlap);
                }
                new_damage.push(rect);
                new_damage
            })
    });

    if slot.is_some() {
        let (mapping, texture) = slot.as_mut().unwrap();
        let mappings = match mapping.take() {
            Some(mut mappings) => {
                mappings.retain(|(mapping, _)| TextureMapping::format(&**mapping) == format);

                let damage_slice = [texture_rect];
                let new_damage = damage
                    .as_deref()
                    .unwrap_or(&damage_slice)
                    .iter()
                    .filter(|rect| !mappings.iter().any(|(_, region)| region.contains_rect(**rect)))
                    .copied()
                    .collect::<Vec<_>>();

                if texture.is_none()
                    && (mappings.len() != 1
                        || <dyn TextureMapping>::size(&*mappings[0].0) != texture_rect.size
                        || !new_damage.is_empty())
                {
                    let mapping = src
                        .renderer_mut()
                        .copy_texture(src_texture, texture_rect, format)
                        .map_err(Error::Render)?;
                    trace!("Creating mapping for: {:?}", damage);
                    mappings = vec![(Box::new(mapping), texture_rect)];
                } else {
                    mappings.extend(
                        new_damage
                            .into_iter()
                            .map(|damage| {
                                let mapping = src
                                    .renderer_mut()
                                    .copy_texture(src_texture, damage, format)
                                    .map_err(Error::Render)?;
                                trace!("Creating mapping for: {:?}", damage);
                                Ok((Box::new(mapping), damage))
                            })
                            .collect::<Result<Vec<_>, Error<S, T>>>()?,
                    );
                }

                mappings
            }
            None => {
                let mapping = src
                    .renderer_mut()
                    .copy_texture(src_texture, texture_rect, format)
                    .map_err(Error::Render)?;
                trace!("Creating mapping for: {:?}", damage);
                vec![(Box::new(mapping), texture_rect)]
            }
        };

        for (mapping, damage) in mappings {
            let data = src.renderer_mut().map_texture(&mapping).map_err(Error::Render)?;
            if let Some(texture) = texture.as_mut() {
                trace!(
                    "Updating texture {:?} with mapping at {:?}",
                    texture.size(),
                    damage,
                );
                target
                    .renderer_mut()
                    .update_memory(texture, data, damage)
                    .map_err(Error::Target)?;
            } else {
                trace!("Importing mapping as full buffer {:?}", mapping.size());
                let target_texture = target
                    .renderer_mut()
                    .import_memory(data, format, texture_rect.size, false)
                    .map_err(Error::Target)?;
                *texture = Some(Box::new(target_texture));
            }
        }
    } else {
        let mapping = src
            .renderer_mut()
            .copy_texture(src_texture, texture_rect, format)
            .map_err(Error::Render)?;
        trace!("Importing mapping as full buffer {:?}", mapping.size());
        let data = src.renderer_mut().map_texture(&mapping).map_err(Error::Render)?;

        let target_texture = target
            .renderer_mut()
            .import_memory(data, format, texture_rect.size, false)
            .map_err(Error::Target)?;
        *slot = Some((None, Some(Box::new(target_texture))));
    };

    Ok(())
}

fn texture_copy<S, T>(
    src: &mut S::Device,
    target: &mut T::Device,
    src_texture: &<<S::Device as ApiDevice>::Renderer as RendererSuper>::TextureId,
    target_texture: &mut Option<GpuSingleTexture>,
    damage: Option<&[Rectangle<i32, BufferCoords>]>,
) -> Result<(), Error<S, T>>
where
    S: GraphicsApi,
    T: GraphicsApi,
    <S::Device as ApiDevice>::Renderer: Renderer + ImportDma + ExportMem + Bind<Dmabuf>,
    <<S::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping: 'static,
    <T::Device as ApiDevice>::Renderer: Renderer + ImportDma + ImportMem,
    <<S::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: 'static,
    <<T::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: 'static,
{
    // we need to reset in a few cases
    if matches!(&target_texture, Some(GpuSingleTexture::Direct(_)))
        || matches!(
            &target_texture,
            Some(GpuSingleTexture::Mem { mappings: Some((node, _)), .. }) if node != src.node()
        )
    {
        *target_texture = None;
    }

    match target_texture.take() {
        Some(GpuSingleTexture::Direct(_)) => unreachable!(),
        Some(GpuSingleTexture::Mem {
            mut external_shadow,
            texture,
            mappings,
        }) => {
            if let Some((dmabuf, texture)) = external_shadow.take() {
                let mut slot = Some((dmabuf, texture, None));
                dma_shadow_copy::<S, S>(src_texture, damage, &mut slot, src, None)
                    .map_err(Error::generalize::<T>)?;
                external_shadow = slot.map(|(dmabuf, texture, sync_point)| {
                    if let Some(sync) = sync_point {
                        // ignore interrupt errors
                        src.renderer_mut().wait(&sync).unwrap_or_else(|_| {
                            let _ = sync.wait();
                        });
                    }
                    (dmabuf, texture)
                });
            }

            let mut slot = Some((
                mappings.map(|(_, mappings)| mappings.into_iter().map(|(damage, mapping)|
                    (mapping.downcast::<<<S::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping>().unwrap(), damage)
                ).collect::<Vec<_>>()),
                texture.map(|texture| texture.downcast::<<<T::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>().unwrap())
            ));

            let src_texture = external_shadow
                .as_ref()
                .map(|(_, texture)| {
                    texture
                        .downcast_ref::<<<S::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>()
                        .unwrap()
                })
                .unwrap_or(src_texture);
            let res = mem_copy::<S, T>(src_texture, damage, &mut slot, src, target);

            *target_texture = slot.map(|(mappings, texture)| GpuSingleTexture::Mem {
                external_shadow,
                texture: texture.map(|texture| texture as Box<dyn Any + 'static>),
                mappings: mappings.map(|mappings| {
                    (
                        *src.node(),
                        mappings
                            .into_iter()
                            .map(|(mapping, damage)| (damage, mapping as Box<dyn Any + 'static>))
                            .collect(),
                    )
                }),
            });
            res
        }
        Some(GpuSingleTexture::Dma {
            texture,
            dmabuf,
            sync,
        }) => {
            let mut slot = Some((dmabuf, texture, sync));
            let res = dma_shadow_copy::<S, T>(src_texture, damage, &mut slot, src, Some(target));
            *target_texture = slot.map(|(dmabuf, texture, sync)| GpuSingleTexture::Dma {
                texture,
                dmabuf,
                sync,
            });
            res
        }
        None => {
            let mut slot = None;
            match dma_shadow_copy::<S, T>(src_texture, damage, &mut slot, src, Some(target)) {
                Ok(()) => {
                    *target_texture = slot.map(|(dmabuf, texture, sync)| GpuSingleTexture::Dma {
                        texture: texture as Box<dyn Any + 'static>,
                        dmabuf,
                        sync,
                    });
                    Ok(())
                }
                Err(err) => {
                    trace!(?err, "Dma shadow copy failed, falling back to cpu");

                    let mut external_shadow = None;
                    if !ExportMem::can_read_texture(src.renderer_mut(), src_texture).map_err(Error::Render)? {
                        let mut slot = None;
                        dma_shadow_copy::<S, S>(src_texture, damage, &mut slot, src, None)
                            .map_err(Error::generalize::<T>)?;
                        external_shadow = slot.map(|(dmabuf, texture, sync_point)| {
                            if let Some(sync) = sync_point {
                                // ignore interrupt errors
                                src.renderer_mut().wait(&sync).unwrap_or_else(|_| {
                                    let _ = sync.wait();
                                });
                            }
                            (dmabuf, texture as Box<dyn Any + 'static>)
                        });
                    };

                    let mut slot = None;
                    let src_texture = external_shadow
                        .as_ref()
                        .map(|(_, texture)| {
                            texture
                                .downcast_ref::<<<S::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>()
                                .unwrap()
                        })
                        .unwrap_or(src_texture);

                    let res = mem_copy::<S, T>(src_texture, damage, &mut slot, src, target);
                    *target_texture = slot.map(|(mappings, texture)| GpuSingleTexture::Mem {
                        texture: texture.map(|texture| texture as Box<dyn Any + 'static>),
                        mappings: mappings.map(|mappings| {
                            (
                                *src.node(),
                                mappings
                                    .into_iter()
                                    .map(|(mapping, damage)| (damage, mapping as Box<dyn Any + 'static>))
                                    .collect(),
                            )
                        }),
                        external_shadow,
                    });
                    res
                }
            }
        }
    }
}

impl<R: GraphicsApi, T: GraphicsApi> MultiRenderer<'_, '_, R, T>
where
    <R::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
    <T::Device as ApiDevice>::Renderer: Bind<Dmabuf> + 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: Bind<Dmabuf> + ExportMem + ImportDma + ImportMem,
    <T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: Clone + Send,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
    <<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
    #[profiling::function]
    fn import_dmabuf_internal(
        &mut self,
        dmabuf: &Dmabuf,
        mut texture: MultiTexture,
        damage: Option<&[Rectangle<i32, BufferCoords>]>,
    ) -> Result<<Self as RendererSuper>::TextureId, <Self as RendererSuper>::Error> {
        let src_node = import_on_src_node::<R, T>(
            dmabuf,
            damage,
            &mut texture,
            self.render,
            self.target.as_mut().map(|target| &mut *target.device),
            self.other_renderers.iter_mut().map(|d| &mut **d),
        )?;

        if src_node == *self.render.node() {
            // when we are on the same node, we are done
            Ok(texture)
        } else {
            // else we need to copy
            if self
                .target
                .as_ref()
                .is_some_and(|target| src_node == *target.device.node())
            {
                let mut texture_internal = texture.0.lock().unwrap();
                // make sure our target exists
                texture_internal.textures.entry(TypeId::of::<R>()).or_default();

                // get_many_mut would be very nice here
                let (mut src_api_textures, other_api_textures) = texture_internal
                    .textures
                    .iter_mut()
                    .partition::<Vec<_>, _>(|(k, _v)| **k == TypeId::of::<T>());
                let mut target_api_textures = other_api_textures
                    .into_iter()
                    .find(|(k, _)| **k == TypeId::of::<R>());
                let mut target_texture = if TypeId::of::<T>() == TypeId::of::<R>() {
                    src_api_textures[0].1.remove(self.render.node())
                } else {
                    target_api_textures.as_mut().unwrap().1.remove(self.render.node())
                };
                let src_texture = match src_api_textures[0].1.get(&src_node).unwrap() {
                    GpuSingleTexture::Direct(tex) => tex
                        .downcast_ref::<<<T::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>()
                        .unwrap(),
                    _ => unreachable!(),
                };

                let res = texture_copy::<T, R>(
                    self.target.as_mut().unwrap().device,
                    self.render,
                    src_texture,
                    &mut target_texture,
                    damage,
                )
                .map_err(Error::transpose);

                if let Some(target_texture) = target_texture.filter(|_| res.is_ok()) {
                    if TypeId::of::<T>() == TypeId::of::<R>() {
                        src_api_textures[0].1.insert(*self.render.node(), target_texture);
                    } else {
                        target_api_textures
                            .unwrap()
                            .1
                            .insert(*self.render.node(), target_texture);
                    }
                }

                std::mem::drop(texture_internal);
                res.map(|_| texture)
            } else if let Some(other) = self
                .other_renderers
                .iter_mut()
                .find(|other| src_node == *other.node())
            {
                let mut texture_internal = texture.0.lock().unwrap();
                let api_textures = texture_internal.textures.get_mut(&TypeId::of::<R>()).unwrap();
                let mut target_texture = api_textures.remove(self.render.node());
                let src_texture = match api_textures.get(&src_node).unwrap() {
                    GpuSingleTexture::Direct(tex) => tex
                        .downcast_ref::<<<R::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>()
                        .unwrap(),
                    _ => unreachable!(),
                };

                let res = texture_copy::<R, R>(other, self.render, src_texture, &mut target_texture, damage)
                    .map_err(Error::generalize::<T>);

                if let Some(target_texture) = target_texture.filter(|_| res.is_ok()) {
                    api_textures.insert(*self.render.node(), target_texture);
                }

                std::mem::drop(texture_internal);
                res.map(|_| texture)
            } else {
                Err(Error::DeviceMissing)
            }
        }
    }
}

/// [`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(),
        }
    }
    fn format(&self) -> Option<Fourcc> {
        match self {
            MultiTextureMapping::<A, B>(TextureMappingInternal::Either(x)) => Texture::format(x),
            MultiTextureMapping::<A, B>(TextureMappingInternal::Or(x)) => Texture::format(x),
        }
    }
}
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(),
        }
    }

    fn format(&self) -> Fourcc {
        match self {
            MultiTextureMapping::<A, B>(TextureMappingInternal::Either(x)) => TextureMapping::format(x),
            MultiTextureMapping::<A, B>(TextureMappingInternal::Or(x)) => TextureMapping::format(x),
        }
    }
}

impl<R: GraphicsApi, T: GraphicsApi> ExportMem for MultiRenderer<'_, '_, R, T>
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: Bind<Dmabuf> + ExportMem + ImportDma + ImportMem,
    <T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: Clone + Send,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
    <<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
    type TextureMapping = MultiTextureMapping<T, R>;

    #[instrument(level = "trace", parent = &self.span, skip(self, framebuffer))]
    #[profiling::function]
    fn copy_framebuffer(
        &mut self,
        framebuffer: &MultiFramebuffer<'_, R, T>,
        region: Rectangle<i32, BufferCoords>,
        format: Fourcc,
    ) -> Result<Self::TextureMapping, <Self as RendererSuper>::Error> {
        match &framebuffer.0 {
            MultiFramebufferInternal::Target(fb) => {
                let target = self.target.as_mut().unwrap();
                target
                    .device
                    .renderer_mut()
                    .copy_framebuffer(fb, region, format)
                    .map(|mapping| MultiTextureMapping(TextureMappingInternal::Either(mapping)))
                    .map_err(Error::Target)
            }
            MultiFramebufferInternal::Render(fb) => self
                .render
                .renderer_mut()
                .copy_framebuffer(fb, region, format)
                .map(|mapping| MultiTextureMapping(TextureMappingInternal::Or(mapping)))
                .map_err(Error::Render),
        }
    }

    #[instrument(level = "trace", parent = &self.span, skip(self))]
    #[profiling::function]
    fn copy_texture(
        &mut self,
        texture: &Self::TextureId,
        region: Rectangle<i32, BufferCoords>,
        format: Fourcc,
    ) -> 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, format)
            .map(|mapping| MultiTextureMapping(TextureMappingInternal::Or(mapping)))
            .map_err(Error::Render)
    }

    fn can_read_texture(&mut self, texture: &Self::TextureId) -> Result<bool, Self::Error> {
        let tex = texture
            .get::<R>(self.render.node())
            .ok_or_else(|| Error::MismatchedDevice(*self.render.node()))?;
        self.render
            .renderer_mut()
            .can_read_texture(&tex)
            .map_err(Error::Render)
    }

    #[instrument(level = "trace", parent = &self.span, skip(self, texture_mapping))]
    #[profiling::function]
    fn map_texture<'c>(
        &mut self,
        texture_mapping: &'c Self::TextureMapping,
    ) -> Result<&'c [u8], <Self as RendererSuper>::Error> {
        match texture_mapping {
            MultiTextureMapping(TextureMappingInternal::Either(target_mapping)) => self
                .target
                .as_mut()
                .unwrap()
                .device
                .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<'frame, 'buffer, R: GraphicsApi, T: GraphicsApi> BlitFrame<MultiFramebuffer<'buffer, R, T>>
    for MultiFrame<'_, '_, 'frame, 'buffer, R, T>
where
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::Frame<'frame, 'buffer>:
        BlitFrame<<<R::Device as ApiDevice>::Renderer as RendererSuper>::Framebuffer<'buffer>>,
    <T::Device as ApiDevice>::Renderer: Blit,
    R: 'static,
    R::Error: 'static,
    T::Error: 'static,
    <R::Device as ApiDevice>::Renderer: Bind<Dmabuf> + ExportMem + ImportDma + ImportMem,
    <T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: Clone + Send,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
    <<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
    #[instrument(level = "trace", parent = &self.span, skip(self, to))]
    #[profiling::function]
    fn blit_to(
        &mut self,
        to: &mut MultiFramebuffer<'buffer, R, T>,
        src: Rectangle<i32, Physical>,
        dst: Rectangle<i32, Physical>,
        filter: TextureFilter,
    ) -> Result<(), Self::Error> {
        self.flush_frame()?;
        if let Some(target) = self.target.as_mut() {
            let MultiFramebufferInternal::Target(ref mut to_fb) = &mut to.0 else {
                unreachable!()
            };
            target
                .device
                .renderer_mut()
                .blit(target.framebuffer, to_fb, src, dst, filter)
                .map_err(Error::Target)
        } else {
            let MultiFramebufferInternal::Render(ref mut to_fb) = &mut to.0 else {
                unreachable!()
            };
            self.frame
                .as_mut()
                .unwrap()
                .blit_to(to_fb, src, dst, filter)
                .map_err(Error::Render)
        }
    }

    #[instrument(level = "trace", parent = &self.span, skip(self, from))]
    #[profiling::function]
    fn blit_from(
        &mut self,
        from: &MultiFramebuffer<'buffer, R, T>,
        src: Rectangle<i32, Physical>,
        dst: Rectangle<i32, Physical>,
        filter: TextureFilter,
    ) -> Result<(), Self::Error> {
        self.flush_frame()?;
        if let Some(target) = self.target.as_mut() {
            let MultiFramebufferInternal::Target(ref from_fb) = &from.0 else {
                unreachable!()
            };
            target
                .device
                .renderer_mut()
                .blit(from_fb, target.framebuffer, src, dst, filter)
                .map_err(Error::Target)
        } else {
            let MultiFramebufferInternal::Render(ref from_fb) = &from.0 else {
                unreachable!()
            };
            self.frame
                .as_mut()
                .unwrap()
                .blit_from(from_fb, src, dst, filter)
                .map_err(Error::Render)
        }
    }
}

impl<R: GraphicsApi, T: GraphicsApi> Blit for MultiRenderer<'_, '_, R, T>
where
    <R::Device as ApiDevice>::Renderer: Blit,
    <T::Device as ApiDevice>::Renderer: Blit,
    // We need this because the Renderer-impl does and Blit requires Renderer
    R: 'static,
    R::Error: 'static,
    T::Error: 'static,
    <R::Device as ApiDevice>::Renderer: Bind<Dmabuf> + ExportMem + ImportDma + ImportMem,
    <T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: Clone + Send,
    <<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
    <<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
    #[instrument(level = "trace", parent = &self.span, skip(self, from, to))]
    #[profiling::function]
    fn blit(
        &mut self,
        from: &MultiFramebuffer<'_, R, T>,
        to: &mut MultiFramebuffer<'_, R, T>,
        src: Rectangle<i32, Physical>,
        dst: Rectangle<i32, Physical>,
        filter: TextureFilter,
    ) -> Result<(), <Self as RendererSuper>::Error> {
        if let Some(target) = self.target.as_mut() {
            let MultiFramebufferInternal::Target(ref from_fb) = &from.0 else {
                unreachable!()
            };
            let MultiFramebufferInternal::Target(ref mut to_fb) = &mut to.0 else {
                unreachable!()
            };
            target
                .device
                .renderer_mut()
                .blit(from_fb, to_fb, src, dst, filter)
                .map_err(Error::Target)
        } else {
            let MultiFramebufferInternal::Render(ref from_fb) = &from.0 else {
                unreachable!()
            };
            let MultiFramebufferInternal::Render(ref mut to_fb) = &mut to.0 else {
                unreachable!()
            };
            self.render
                .renderer_mut()
                .blit(from_fb, to_fb, src, dst, filter)
                .map_err(Error::Render)
        }
    }
}