<NME> udev.rs
<BEF> use std::{
    borrow::Cow,
    cell::RefCell,
    collections::hash_map::{Entry, HashMap},
    os::unix::io::{AsRawFd, RawFd},
    path::PathBuf,
    rc::Rc,
    sync::{atomic::Ordering, Mutex},
    time::Duration,
};

use image::ImageBuffer;
use slog::Logger;

use smithay::{
    backend::{
        allocator::dmabuf::Dmabuf,
        drm::{DrmDevice, DrmError, DrmEvent, GbmBufferedSurface},
        egl::{EGLContext, EGLDisplay},
        libinput::{LibinputInputBackend, LibinputSessionInterface},
        renderer::{
            gles2::{Gles2Renderer, Gles2Texture},
            Bind, Frame, Renderer,
        },
        session::{auto::AutoSession, Session, Signal as SessionSignal},
        udev::{UdevBackend, UdevEvent},
        SwapBuffersError,
    },
    desktop::space::{RenderError, Space, SurfaceTree},
            Bind, Frame, Renderer,
        },
        session::{auto::AutoSession, Session, Signal as SessionSignal},
        udev::{all_gpus, primary_gpu, UdevBackend, UdevEvent},
        SwapBuffersError,
    },
    desktop::{
        space::{Space, SurfaceTree},
        Window,
    },
    input::pointer::{CursorImageAttributes, CursorImageStatus},
    output::{Mode, Output, PhysicalProperties, Subpixel},
    reexports::{
        calloop::{
            timer::{TimeoutAction, Timer},
            Dispatcher, EventLoop, LoopHandle, RegistrationToken,
        },
        drm::{
            self,
            control::{
                connector::{Info as ConnectorInfo, State as ConnectorState},
                crtc,
                encoder::Info as EncoderInfo,
                Device as ControlDevice,
            },
        },
        gbm::Device as GbmDevice,
        input::Libinput,
        seat::CursorImageStatus,
    },
};
#[cfg(feature = "egl")]
use smithay::{
    backend::{
        drm::DevPath,
        renderer::{ImportDma, ImportEgl},
        udev::primary_gpu,
    },
    wayland::dmabuf::init_dmabuf_global,
};

use crate::{
    drawing::*,
    state::{AnvilState, Backend},
};

#[cfg(feature = "debug")]
smithay::custom_elements!(CustomElem; UdevRenderer<'_>; SurfaceTree=SurfaceTree, PointerElement=PointerElement::<MultiTexture>, FpsElement=FpsElement::<MultiTexture>);
#[cfg(not(feature = "debug"))]
        compositor,
        input_method::{InputMethodHandle, InputMethodSeat},
    },
};

type UdevRenderer<'a> =
    MultiRenderer<'a, 'a, EglGlesBackend<Gles2Renderer>, EglGlesBackend<Gles2Renderer>, Gles2Renderbuffer>;

#[derive(Copy, Clone)]

#[derive(Debug, PartialEq)]
struct UdevOutputId {
    device_id: dev_t,
    crtc: crtc::Handle,
}

pub struct UdevData {
    pub session: AutoSession,
    #[cfg(feature = "egl")]
    primary_gpu: Option<PathBuf>,
    backends: HashMap<dev_t, BackendData>,
    signaler: Signaler<SessionSignal>,
    pointer_image: crate::cursor::Cursor,
    render_timer: TimerHandle<(u64, crtc::Handle)>,
}

impl Backend for UdevData {
    pointer_images: Vec<(xcursor::parser::Image, TextureBuffer<MultiTexture>)>,
    pointer_element: PointerElement<MultiTexture>,
    #[cfg(feature = "debug")]
    fps_texture: MultiTexture,
    signaler: Signaler<SessionSignal>,
    pointer_image: crate::cursor::Cursor,
    logger: slog::Logger,
}

#[cfg(feature = "egl")]
impl DmabufHandler for AnvilState<UdevData> {
    fn dmabuf_state(&mut self) -> &mut DmabufState {
        &mut self.backend_data.dmabuf_state.as_mut().unwrap().0
    }

    fn dmabuf_imported(&mut self, _global: &DmabufGlobal, dmabuf: Dmabuf) -> Result<(), ImportError> {
        self.backend_data
            .gpus
            .renderer::<Gles2Renderbuffer>(&self.backend_data.primary_gpu, &self.backend_data.primary_gpu)
            .and_then(|mut renderer| renderer.import_dmabuf(&dmabuf, None))
            .map(|_| ())
            .map_err(|_| ImportError::Failed)
    }
}
#[cfg(feature = "egl")]
delegate_dmabuf!(AnvilState<UdevData>);

impl Backend for UdevData {
    fn seat_name(&self) -> String {
        self.session.seat()
    }

    /*
     * Initialize the compositor
     */
    #[cfg(feature = "egl")]
    let primary_gpu = primary_gpu(&session.seat()).unwrap_or_default();

    // setup the timer
    let timer = Timer::new().unwrap();

    let data = UdevData {
        session,
        #[cfg(feature = "egl")]
        primary_gpu,
        backends: HashMap::new(),
        signaler: session_signal.clone(),
        pointer_image: crate::cursor::Cursor::load(&log),
        render_timer: timer.handle(),
    };
    let mut state = AnvilState::init(display.clone(), event_loop.handle(), data, log.clone(), true);

        image::io::Reader::with_format(std::io::Cursor::new(FPS_NUMBERS_PNG), image::ImageFormat::Png)
            .decode()
            .unwrap();
    #[cfg(feature = "debug")]
    let fps_texture = renderer
        .import_memory(
            &fps_image.to_rgba8(),
            (fps_image.width() as i32, fps_image.height() as i32).into(),
            false,
        )
        .expect("Unable to upload FPS texture");

    // init dmabuf support with format list from our primary gpu
    // TODO: This does not necessarily depend on egl, but mesa makes no use of it without wl_drm right now
    #[cfg(feature = "egl")]
    let dmabuf_state = {
        info!(
            log,
            "Trying to initialize EGL Hardware Acceleration via {:?}", primary_gpu
        );

        if renderer.bind_wl_display(&display.handle()).is_ok() {
            info!(log, "EGL hardware-acceleration enabled");
            let dmabuf_formats = renderer.dmabuf_formats().cloned().collect::<Vec<_>>();
            let mut state = DmabufState::new();
            let global = state.create_global::<AnvilState<UdevData>, _>(
                &display.handle(),
                dmabuf_formats,
                log.clone(),
            );
            Some((state, global))
        } else {
            None
        }
    };

    let data = UdevData {
        dh: display.handle(),
        #[cfg(feature = "egl")]
        dmabuf_state,
        session,
        primary_gpu,
        gpus,
        backends: HashMap::new(),
        signaler: session_signal.clone(),
        pointer_image: crate::cursor::Cursor::load(&log),
        pointer_images: Vec::new(),
        state.device_added(dev, path.into())
    }

    // init dmabuf support with format list from all gpus
    // TODO: We need to update this list, when the set of gpus changes
    // TODO2: This does not necessarily depend on egl, but mesa makes no use of it without wl_drm right now
    #[cfg(feature = "egl")]
    {
        let mut formats = Vec::new();
        for backend_data in state.backend_data.backends.values() {
            formats.extend(backend_data.renderer.borrow().dmabuf_formats().cloned());
        }

        init_dmabuf_global(
            &mut *display.borrow_mut(),
            formats,
            |buffer, mut ddata| {
                let anvil_state = ddata.get::<AnvilState<UdevData>>().unwrap();
                for backend_data in anvil_state.backend_data.backends.values() {
                    if backend_data.renderer.borrow_mut().import_dmabuf(buffer).is_ok() {
                        return true;
                    }
                }
                false
            },
            log.clone(),
        );
        state.backend_data.session.clone().into(),
    );
    libinput_context.udev_assign_seat(&state.seat_name).unwrap();
    let mut libinput_backend = LibinputInputBackend::new(libinput_context, log.clone());
    libinput_backend.link(session_signal);

    /*
     * Bind all our objects that get driven by the event loop
     */
    event_loop
        .handle()
        .insert_source(libinput_backend, move |event, _, data| {
            let dh = data.state.backend_data.dh.clone();
            data.state.process_input_event(&dh, event)
        })
        .unwrap();
    event_loop
        .handle()
        .insert_source(notifier, |(), &mut (), _data| {})
        .unwrap();
    for (dev, path) in udev_backend.device_list() {
        state.device_added(&mut display, dev, path.into())
    }

    event_loop
        .handle()
        .insert_source(udev_backend, move |event, _, data| match event {
            UdevEvent::Added { device_id, path } => {
                data.state.device_added(&mut data.display, device_id, path)
            }
            UdevEvent::Changed { device_id } => data.state.device_changed(&mut data.display, device_id),
            UdevEvent::Removed { device_id } => data.state.device_removed(device_id),
        })
        .unwrap();

pub type RenderSurface = GbmBufferedSurface<Rc<RefCell<GbmDevice<SessionFd>>>, SessionFd>;

struct SurfaceData {
    surface: RenderSurface,
    global: Option<Global<wl_output::WlOutput>>,
    #[cfg(feature = "debug")]
     * And run our loop
     */

    while state.running.load(Ordering::SeqCst) {
        let mut calloop_data = CalloopData { state, display };
        let result = event_loop.dispatch(Some(Duration::from_millis(16)), &mut calloop_data);
        CalloopData { state, display } = calloop_data;

        if result.is_err() {
            state.running.store(false, Ordering::SeqCst);
        } else {
struct BackendData {
    _restart_token: SignalToken,
    surfaces: Rc<RefCell<HashMap<crtc::Handle, Rc<RefCell<SurfaceData>>>>>,
    pointer_images: Vec<(xcursor::parser::Image, Gles2Texture)>,
    #[cfg(feature = "debug")]
    fps_texture: Gles2Texture,
    renderer: Rc<RefCell<Gles2Renderer>>,
    gbm: Rc<RefCell<GbmDevice<SessionFd>>>,
    registration_token: RegistrationToken,
    event_dispatcher: Dispatcher<'static, DrmDevice<SessionFd>, AnvilState<UdevData>>,
    dev_id: u64,
}

fn scan_connectors(
    device: &DrmDevice<SessionFd>,
    gbm: &Rc<RefCell<GbmDevice<SessionFd>>>,
    renderer: &mut Gles2Renderer,
    display: &mut Display,
    space: &mut Space,
    signaler: &Signaler<SessionSignal>,
    #[cfg(feature = "debug")]
    fps: fps_ticker::Fps,
    #[cfg(feature = "debug")]
    fps_element: FpsElement<MultiTexture>,
}

impl Drop for SurfaceData {
    fn drop(&mut self) {
        if let Some(global) = self.global.take() {
            self.dh.remove_global::<AnvilState<UdevBackend>>(global);
        }
    }
}

    let mut backends = HashMap::new();

    // very naive way of finding good crtc/encoder/connector combinations. This problem is np-complete
    for connector_info in connector_infos {
        let encoder_infos = connector_info
    device: &DrmDevice<SessionFd>,
    gbm: &Rc<RefCell<GbmDevice<SessionFd>>>,
    display: &mut Display<AnvilState<UdevData>>,
    space: &mut Space<Window>,
    #[cfg(feature = "debug")] fps_texture: &MultiTexture,
    signaler: &Signaler<SessionSignal>,
    logger: &::slog::Logger,
) -> HashMap<crtc::Handle, Rc<RefCell<SurfaceData>>> {
    // Get a set of all modesetting resource handles (excluding planes):
    let res_handles = device.resource_handles().unwrap();

    // Find all connected output ports.
    let connector_infos: Vec<ConnectorInfo> = res_handles
        .connectors()
        .iter()
        .map(|conn| device.get_connector(*conn, true).unwrap())
        .filter(|conn| conn.state() == ConnectorState::Connected)
        .inspect(|conn| info!(logger, "Connected: {:?}", conn.interface()))
        .collect();

    let mut backends = HashMap::new();

    let (render_node, formats) = {
        let display = unsafe { EGLDisplay::new(&*gbm.borrow(), logger.clone()).unwrap() };
        let node = match EGLDevice::device_for_display(&display)
            .ok()
            .and_then(|x| x.try_get_render_node().ok().flatten())
        {
            Some(node) => node,
            None => return HashMap::new(),
        };
        let context = EGLContext::new(&display, logger.clone()).unwrap();
            };
            surface.link(signaler.clone());

            let renderer_formats =
                Bind::<Dmabuf>::supported_formats(renderer).expect("Dmabuf renderer without formats");

            let gbm_surface =
                match GbmBufferedSurface::new(surface, gbm.clone(), renderer_formats, logger.clone()) {
                    Ok(renderer) => renderer,
                    Err(err) => {
                        warn!(logger, "Failed to create rendering surface: {}", err);
            .flat_map(|encoder_handle| device.get_encoder(*encoder_handle))
            .collect::<Vec<EncoderInfo>>();

        let crtcs = encoder_infos
            .iter()
            .flat_map(|encoder_info| res_handles.filter_crtcs(encoder_info.possible_crtcs()));

        for crtc in crtcs {
            // Skip CRTCs used by previous connectors.
            let entry = match backends.entry(crtc) {
                Entry::Vacant(entry) => entry,
                Entry::Occupied(_) => continue,
            };

            info!(
                logger,
                "Trying to setup connector {:?}-{} with crtc {:?}",
                connector_info.interface(),
                connector_info.interface_id(),
                crtc,
            );

            let mode = connector_info.modes()[0];
            let mut surface = match device.create_surface(crtc, mode, &[connector_info.handle()]) {
                Ok(surface) => surface,
                Err(err) => {
                    warn!(logger, "Failed to create drm surface: {}", err);
                    continue;
                }
            };
            surface.link(signaler.clone());

            let gbm_surface =
                match GbmBufferedSurface::new(surface, gbm.clone(), formats.clone(), logger.clone()) {
                    Ok(renderer) => renderer,
                    Err(err) => {
                        warn!(logger, "Failed to create rendering surface: {}", err);
                        continue;
                    }
                };

            let size = mode.size();
            let mode = Mode {
                size: (size.0 as i32, size.1 as i32).into(),
                refresh: mode.vrefresh() as i32 * 1000,
            };

            output.user_data().insert_if_missing(|| UdevOutputId {
                crtc,
                device_id: device.device_id(),
            });

            entry.insert(Rc::new(RefCell::new(SurfaceData {
                surface: gbm_surface,
                global: Some(global),
                #[cfg(feature = "debug")]
            let (phys_w, phys_h) = connector_info.size().unwrap_or((0, 0));
            let output = Output::new(
                output_name,
                PhysicalProperties {
                    size: (phys_w as i32, phys_h as i32).into(),
                    subpixel: Subpixel::Unknown,
                    make: "Smithay".into(),
                    model: "Generic DRM".into(),
                },
                None,
            );
            let global = output.create_global::<AnvilState<UdevData>>(&display.handle());
            let position = (
                space
                    .outputs()
                    .fold(0, |acc, o| acc + space.output_geometry(o).unwrap().size.w),
                0,
            )
                .into();
            output.change_current_state(Some(mode), None, None, Some(position));
            output.set_preferred(mode);
            space.map_output(&output, position);

            output
                .user_data()
                .insert_if_missing(|| UdevOutputId { crtc, device_id });

            let damage_tracked_renderer = DamageTrackedRenderer::from_output(&output);
            #[cfg(feature = "debug")]
            let fps_element = FpsElement::new(fps_texture.clone());

            entry.insert(Rc::new(RefCell::new(SurfaceData {
                dh: display.handle(),
                device_id,
                render_node,
                surface: gbm_surface,
                global: Some(global),
            None => return,
        };

        let egl = match EGLDisplay::new(&gbm, self.log.clone()) {
            Ok(display) => display,
            Err(err) => {
                warn!(
                    self.log,
                    "Skipping device {:?}, because of egl display error: {}", device_id, err
                );
                return;
            }
        };

        let context = match EGLContext::new(&egl, self.log.clone()) {
            Ok(context) => context,
            Err(err) => {
                warn!(
                    self.log,
                    "Skipping device {:?}, because of egl context error: {}", device_id, err
                );
                return;
            }
        };

        let renderer = Rc::new(RefCell::new(unsafe {
            Gles2Renderer::new(context, self.log.clone()).unwrap()
        }));

        #[cfg(feature = "egl")]
        if path.canonicalize().ok() == self.backend_data.primary_gpu {
            info!(self.log, "Initializing EGL Hardware Acceleration via {:?}", path);
            if renderer
                .borrow_mut()
                .bind_wl_display(&*self.display.borrow())
                .is_ok()
            {
                info!(self.log, "EGL hardware-acceleration enabled");
            }
        }

        let gbm = Rc::new(RefCell::new(gbm));
        let backends = Rc::new(RefCell::new(scan_connectors(
            &device,
            &gbm,
            &mut *renderer.borrow_mut(),
            &mut *self.display.borrow_mut(),
            &mut *self.space.borrow_mut(),
            &self.backend_data.signaler,
            &self.log,
        )));

        let dev_id = device.device_id();
        let handle = self.handle.clone();
        let restart_token = self.backend_data.signaler.register(move |signal| match signal {
            SessionSignal::ActivateSession | SessionSignal::ActivateDevice { .. } => {

        // Report device open failures.
        let (mut device, gbm) = match devices {
            Some((Ok(drm), Ok(gbm))) => (drm, gbm),
            Some((Err(err), _)) => {
                warn!(
                    self.log,
                    "Skipping device {:?}, because of drm error: {}", device_id, err
                );
                return;
            }
            Some((_, Err(err))) => {
                // TODO try DumbBuffer allocator in this case
                warn!(
                    self.log,
                    "Skipping device {:?}, because of gbm error: {}", device_id, err
                );
                return;
        for backend in backends.borrow_mut().values() {
            // render first frame
            trace!(self.log, "Scheduling frame");
            schedule_initial_render(backend.clone(), renderer.clone(), &self.handle, self.log.clone());
        }

        #[cfg(feature = "debug")]
        let fps_texture = import_bitmap(
            &mut renderer.borrow_mut(),
            &image::io::Reader::with_format(std::io::Cursor::new(FPS_NUMBERS_PNG), image::ImageFormat::Png)
                .decode()
                .unwrap()
                .to_rgba8(),
        )
        .expect("Unable to upload FPS texture");

        self.backend_data.backends.insert(
            dev_id,
            BackendData {
            &device,
                registration_token,
                event_dispatcher,
                surfaces: backends,
                renderer,
                gbm,
                pointer_images: Vec::new(),
                #[cfg(feature = "debug")]
                fps_texture,
                dev_id,
            },
        );
    }

    fn device_changed(&mut self, device: dev_t) {
        //quick and dirty, just re-init all backends
        if let Some(ref mut backend_data) = self.backend_data.backends.get_mut(&device) {
            let logger = self.log.clone();
            let loop_handle = self.handle.clone();
            let signaler = self.backend_data.signaler.clone();
                DrmEvent::VBlank(crtc) => data.state.render(node, Some(crtc)),
                DrmEvent::Error(error) => {
                    error!(data.state.log, "{:?}", error);
                }
            });
                .filter(|o| {
                    o.user_data()
                        .get::<UdevOutputId>()
                        .map(|id| id.device_id == device)
                        .unwrap_or(false)
                })
                .cloned()
                backend.clone(),
                &self.handle,
                self.log.clone(),
            );
        }

            let source = backend_data.event_dispatcher.as_source_mut();
            let mut backends = backend_data.surfaces.borrow_mut();
            *backends = scan_connectors(
                &source,
                &backend_data.gbm,
                &mut *backend_data.renderer.borrow_mut(),
                &mut *self.display.borrow_mut(),
                &mut *self.space.borrow_mut(),
                &signaler,
        );
    }

            // fixup window coordinates
            crate::shell::fixup_positions(&mut *space);

            for renderer in backends.values() {
                let logger = logger.clone();
                // render first frame
                schedule_initial_render(
                    renderer.clone(),
                    backend_data.renderer.clone(),
                    &loop_handle,
                    logger,
                );
            }
        }
    }

    fn device_removed(&mut self, device: dev_t) {
        // drop the backends on this side
        if let Some(backend_data) = self.backend_data.backends.remove(&device) {
            // drop surfaces
            backend_data.surfaces.borrow_mut().clear();
            debug!(self.log, "Surfaces dropped");
                        .get::<UdevOutputId>()
                        .map(|id| id.device_id == node)
                        .unwrap_or(false)
                })
                .filter(|o| {
                    o.user_data()
                        .get::<UdevOutputId>()
                        .map(|id| id.device_id == device)
                        .unwrap_or(false)
                })
                .cloned()
            let source = backend_data.event_dispatcher.as_source_mut();
            let mut backends = backend_data.surfaces.borrow_mut();
            *backends = scan_connectors(
                node,
                &source,
                &backend_data.gbm,
                display,
            let _device = self.handle.remove(backend_data.registration_token);
            let _device = backend_data.event_dispatcher.into_source_inner();

            // don't use hardware acceleration anymore, if this was the primary gpu
            #[cfg(feature = "egl")]
            if _device.dev_path().and_then(|path| path.canonicalize().ok()) == self.backend_data.primary_gpu {
                backend_data.renderer.borrow_mut().unbind_wl_display();
            }
            debug!(self.log, "Dropping device");
        }
    }

    // If crtc is `Some()`, render it, else render all crtcs
    fn render(&mut self, dev_id: u64, crtc: Option<crtc::Handle>) {
        let device_backend = match self.backend_data.backends.get_mut(&dev_id) {
            Some(backend) => backend,
            None => {
                // render first frame
                schedule_initial_render(&mut self.backend_data.gpus, surface.clone(), &loop_handle, logger);
            }
        }
    }

    fn device_removed(&mut self, device: dev_t) {
        let node = match DrmNode::from_dev_id(device).ok() {
            Some(node) => node,
            None => return, // we already logged a warning on device_added
        };
        // drop the backends on this side
        if let Some(backend_data) = self.backend_data.backends.remove(&node) {
            // drop surfaces
            backend_data.surfaces.borrow_mut().clear();
            debug!(self.log, "Surfaces dropped");

            for output in self
                .space
                .outputs()
                .filter(|o| {
                    o.user_data()
                        .get::<UdevOutputId>()
                .backend_data
                .pointer_image
                .get_image(1 /*scale*/, self.start_time.elapsed().as_millis() as u32);
            let renderer = &mut *device_backend.renderer.borrow_mut();
            let pointer_images = &mut device_backend.pointer_images;
            let pointer_image = pointer_images
                .iter()
                .find_map(|(image, texture)| if image == &frame { Some(texture) } else { None })
                .cloned()
                .unwrap_or_else(|| {
                    let image =
                        ImageBuffer::from_raw(frame.width, frame.height, &*frame.pixels_rgba).unwrap();
                    let texture = import_bitmap(renderer, &image).expect("Failed to import cursor bitmap");
                    pointer_images.push((frame, texture.clone()));
                    texture
                });

            let result = render_surface(
                &mut *surface.borrow_mut(),
                renderer,
                device_backend.dev_id,
                crtc,
                &mut *self.space.borrow_mut(),
                self.pointer_location,
                &pointer_image,
                #[cfg(feature = "debug")]
                &device_backend.fps_texture,
                &*self.dnd_icon.lock().unwrap(),
                &mut *self.cursor_status.lock().unwrap(),
                &self.log,
            } else {
                &mut surfaces_iter
            };

        let mut outputs = Vec::new();
        for (&crtc, surface) in to_render_iter {
            // TODO get scale from the rendersurface when supporting HiDPI
            let frame = self
                .backend_data
                .pointer_image
                .get_image(1 /*scale*/, self.start_time.elapsed().as_millis() as u32);
            let primary_gpu = self.backend_data.primary_gpu;
            let mut renderer = self
                .backend_data
                .gpus
                .renderer::<Gles2Renderbuffer>(&primary_gpu, &surface.borrow().render_node)
                .unwrap();
            let pointer_images = &mut self.backend_data.pointer_images;
            let pointer_image = pointer_images
                .iter()
                .find_map(|(image, texture)| {
                    if image == &frame {
                        Some(texture.clone())
                    } else {
                        None
                    }
                })
                .unwrap_or_else(|| {
                    let texture = TextureBuffer::from_memory(
                        &mut renderer,
                        &frame.pixels_rgba,
                        (frame.width as i32, frame.height as i32),
                        false,
                        1,
                        Transform::Normal,
#[allow(clippy::too_many_arguments)]
fn render_surface(
    surface: &mut SurfaceData,
    renderer: &mut Gles2Renderer,
    device_id: dev_t,
    crtc: crtc::Handle,
    space: &mut Space,
    pointer_location: Point<f64, Logical>,
    pointer_image: &Gles2Texture,
    #[cfg(feature = "debug")] fps_texture: &Gles2Texture,
    dnd_icon: &Option<wl_surface::WlSurface>,
    cursor_status: &mut CursorImageStatus,
    logger: &slog::Logger,
            }) {
                output.clone()
            } else {
                // somehow we got called with an invalid output
                continue;
            };

            let result = render_surface(
                &mut *surface.borrow_mut(),
                &mut renderer,
                &mut self.space,
                &output,
                self.seat.input_method().unwrap(),
                self.pointer_location,
                &pointer_image,
                &mut self.backend_data.pointer_element,
                &self.dnd_icon,
                &mut *self.cursor_status.lock().unwrap(),
                &self.log,
            );
            let reschedule = match &result {
                Ok((has_rendered, _)) => !has_rendered,
                Err(err) => {
                    warn!(self.log, "Error during rendering: {:?}", err);
                    match err {
                        SwapBuffersError::AlreadySwapped => false,
                        SwapBuffersError::TemporaryFailure(err) => !matches!(
                            err.downcast_ref::<DrmError>(),
                            Some(&DrmError::DeviceInactive)
                                | Some(&DrmError::Access {
                                    source: drm::SystemError::PermissionDenied,
                                    ..
                                })
                        ),
                        SwapBuffersError::ContextLost(err) => panic!("Rendering loop lost: {}", err),
                    }
                }
            };

            if reschedule {
                let timer = Timer::from_duration(Duration::from_millis(
                    1000 /*a seconds*/ / 60, /*refresh rate*/
                ));
                self.handle
                    .insert_source(timer, move |_, _, data| {
                        data.state.render(dev_id, Some(crtc));
                        TimeoutAction::Drop
                    })
                    .expect("failed to schedule frame timer");
            }

            if let Ok((_, states)) = result {
                outputs.push((output.clone(), states));
            }
        }

        std::mem::drop(surfaces);
        for (output, states) in outputs {
            // Send frame events so that client start drawing their next frame
            self.send_frames(&output, &states);
        }
    }
}

#[allow(clippy::too_many_arguments)]
fn render_surface<'a>(
    surface: &'a mut SurfaceData,
    renderer: &mut UdevRenderer<'a>,
    space: &mut Space<Window>,
    output: &Output,
    input_method: &InputMethodHandle,
    pointer_location: Point<f64, Logical>,
    pointer_image: &TextureBuffer<MultiTexture>,
    pointer_element: &mut PointerElement<MultiTexture>,
    dnd_icon: &Option<wl_surface::WlSurface>,
    cursor_status: &mut CursorImageStatus,
    logger: &slog::Logger,
) -> Result<(bool, RenderElementStates), SwapBuffersError> {
    surface.surface.frame_submitted()?;

    let output_geometry = space.output_geometry(output).unwrap();
    let scale = Scale::from(output.current_scale().fractional_scale());

    let (dmabuf, age) = surface.surface.next_buffer()?;
    renderer.bind(dmabuf)?;

    let mut elements: Vec<CustomRenderElements<_>> = Vec::new();
    }
}

fn schedule_initial_render<Data: 'static>(
    surface: Rc<RefCell<SurfaceData>>,
    renderer: Rc<RefCell<Gles2Renderer>>,
    evt_handle: &LoopHandle<'static, Data>,
    logger: ::slog::Logger,
) {
    let result = {
        let mut surface = surface.borrow_mut();
        let mut renderer = renderer.borrow_mut();
        initial_render(&mut surface.surface, &mut *renderer)
    };
    if let Err(err) = result {
        match err {
                    .get::<Mutex<CursorImageAttributes>>()
                    .unwrap()
                // TODO dont reschedule after 3(?) retries
                warn!(logger, "Failed to submit page_flip: {}", err);
                let handle = evt_handle.clone();
                evt_handle.insert_idle(move |_| schedule_initial_render(surface, renderer, &handle, logger));
            }
            SwapBuffersError::ContextLost(err) => panic!("Rendering loop lost: {}", err),
        }
    }
}

fn initial_render(surface: &mut RenderSurface, renderer: &mut Gles2Renderer) -> Result<(), SwapBuffersError> {
    let (dmabuf, _age) = surface.next_buffer()?;
    renderer.bind(dmabuf)?;
    // Does not matter if we render an empty frame
            }
            if reset {
                *cursor_status = CursorImageStatus::Default;
            }

            pointer_element.set_status(cursor_status.clone());
        }

        elements.extend(pointer_element.render_elements(cursor_pos_scaled, scale));

        // draw the dnd icon if applicable
        {
            if let Some(wl_surface) = dnd_icon.as_ref() {
                if wl_surface.alive() {
                    elements.extend(AsRenderElements::<UdevRenderer<'a>>::render_elements(
                        &SurfaceTree::from_surface(wl_surface),
                        cursor_pos_scaled,
                        scale,
                    ));
                }
            }
        }
    }

    #[cfg(feature = "debug")]
    {
        surface.fps_element.update_fps(surface.fps.avg().round() as u32);
        surface.fps.tick();
        elements.push(CustomRenderElements::Fps(surface.fps_element.clone()));
    }

    // and draw to our buffer
    let render_res = render_output(
        output,
        space,
        &*elements,
        renderer,
        &mut surface.damage_tracked_renderer,
        age.into(),
        logger,
    )
    .map(|(damage, states)| (damage.is_some(), states));

    match render_res.map_err(|err| match err {
        DamageTrackedRendererError::Rendering(err) => err.into(),
        _ => unreachable!(),
    }) {
        Ok((true, states)) => {
            surface
                .surface
                .queue_buffer()
                .map_err(Into::<SwapBuffersError>::into)?;
            Ok((true, states))
        }
        x => x,
    }
}

fn schedule_initial_render(
    gpus: &mut GpuManager<EglGlesBackend<Gles2Renderer>>,
    surface: Rc<RefCell<SurfaceData>>,
    evt_handle: &LoopHandle<'static, CalloopData<UdevData>>,
    logger: ::slog::Logger,
) {
    let node = surface.borrow().render_node;
    let result = {
        let mut renderer = gpus.renderer::<Gles2Renderbuffer>(&node, &node).unwrap();
        let mut surface = surface.borrow_mut();
        initial_render(&mut surface.surface, &mut renderer)
    };
    if let Err(err) = result {
        match err {
            SwapBuffersError::AlreadySwapped => {}
            SwapBuffersError::TemporaryFailure(err) => {
                // TODO dont reschedule after 3(?) retries
                warn!(logger, "Failed to submit page_flip: {}", err);
                let handle = evt_handle.clone();
                evt_handle.insert_idle(move |data| {
                    schedule_initial_render(&mut data.state.backend_data.gpus, surface, &handle, logger)
                });
            }
            SwapBuffersError::ContextLost(err) => panic!("Rendering loop lost: {}", err),
        }
    }
}

fn initial_render(
    surface: &mut RenderSurface,
    renderer: &mut UdevRenderer<'_>,
) -> Result<(), SwapBuffersError> {
    let (dmabuf, _age) = surface.next_buffer()?;
    renderer.bind(dmabuf)?;
    // Does not matter if we render an empty frame
    renderer
        .render((1, 1).into(), Transform::Normal, |_, frame| {
            frame
                .clear(CLEAR_COLOR, &[Rectangle::from_loc_and_size((0, 0), (1, 1))])
                .map_err(Into::<SwapBuffersError>::into)
        })
        .map_err(Into::<SwapBuffersError>::into)
        .and_then(|x| x.map_err(Into::<SwapBuffersError>::into))?;
    surface.queue_buffer()?;
    surface.reset_buffers();
    Ok(())
}

<MSG> anvil/udev: Use MultiGPU renderer

<DFF> @@ -9,21 +9,31 @@ use std::{
     time::Duration,
 };
 
-use image::ImageBuffer;
 use slog::Logger;
 
+use crate::{
+    drawing::*,
+    state::{AnvilState, Backend},
+};
+#[cfg(feature = "debug")]
+use image::GenericImageView;
+#[cfg(feature = "egl")]
+use smithay::{
+    backend::renderer::{ImportDma, ImportEgl},
+    wayland::dmabuf::init_dmabuf_global,
+};
 use smithay::{
     backend::{
-        allocator::dmabuf::Dmabuf,
-        drm::{DrmDevice, DrmError, DrmEvent, GbmBufferedSurface},
+        drm::{DrmDevice, DrmError, DrmEvent, DrmNode, GbmBufferedSurface, NodeType},
         egl::{EGLContext, EGLDisplay},
         libinput::{LibinputInputBackend, LibinputSessionInterface},
         renderer::{
-            gles2::{Gles2Renderer, Gles2Texture},
-            Bind, Frame, Renderer,
+            gles2::Gles2Renderbuffer,
+            multigpu::{egl::EglGlesBackend, GpuManager, MultiRenderer, MultiTexture},
+            Bind, Frame, ImportMem, Renderer,
         },
         session::{auto::AutoSession, Session, Signal as SessionSignal},
-        udev::{UdevBackend, UdevEvent},
+        udev::{all_gpus, primary_gpu, UdevBackend, UdevEvent},
         SwapBuffersError,
     },
     desktop::space::{RenderError, Space, SurfaceTree},
@@ -58,21 +68,8 @@ use smithay::{
         seat::CursorImageStatus,
     },
 };
-#[cfg(feature = "egl")]
-use smithay::{
-    backend::{
-        drm::DevPath,
-        renderer::{ImportDma, ImportEgl},
-        udev::primary_gpu,
-    },
-    wayland::dmabuf::init_dmabuf_global,
-};
-
-use crate::{
-    drawing::*,
-    state::{AnvilState, Backend},
-};
 
+type UdevRenderer<'a> = MultiRenderer<'a, 'a, EglGlesBackend, EglGlesBackend, Gles2Renderbuffer>;
 #[cfg(feature = "debug")]
 smithay::custom_elements!(CustomElem; UdevRenderer<'_>; SurfaceTree=SurfaceTree, PointerElement=PointerElement::<MultiTexture>, FpsElement=FpsElement::<MultiTexture>);
 #[cfg(not(feature = "debug"))]
@@ -88,18 +85,21 @@ impl AsRawFd for SessionFd {
 
 #[derive(Debug, PartialEq)]
 struct UdevOutputId {
-    device_id: dev_t,
+    device_id: DrmNode,
     crtc: crtc::Handle,
 }
 
 pub struct UdevData {
     pub session: AutoSession,
-    #[cfg(feature = "egl")]
-    primary_gpu: Option<PathBuf>,
-    backends: HashMap<dev_t, BackendData>,
+    primary_gpu: DrmNode,
+    gpus: GpuManager<EglGlesBackend>,
+    backends: HashMap<DrmNode, BackendData>,
+    pointer_images: Vec<(xcursor::parser::Image, MultiTexture)>,
+    #[cfg(feature = "debug")]
+    fps_texture: MultiTexture,
     signaler: Signaler<SessionSignal>,
     pointer_image: crate::cursor::Cursor,
-    render_timer: TimerHandle<(u64, crtc::Handle)>,
+    render_timer: TimerHandle<(DrmNode, crtc::Handle)>,
 }
 
 impl Backend for UdevData {
@@ -138,20 +138,66 @@ pub fn run_udev(log: Logger) {
     /*
      * Initialize the compositor
      */
+    let primary_gpu = primary_gpu(&session.seat())
+        .unwrap()
+        .and_then(|x| DrmNode::from_path(x).ok()?.node_with_type(NodeType::Render)?.ok())
+        .unwrap_or_else(|| {
+            all_gpus(&session.seat())
+                .unwrap()
+                .into_iter()
+                .find_map(|x| DrmNode::from_path(x).ok())
+                .expect("No GPU!")
+        });
+
+    #[cfg_attr(not(feature = "egl"), allow(unused_mut))]
+    let mut gpus = GpuManager::new(EglGlesBackend, log.clone()).unwrap();
+    #[cfg_attr(not(feature = "egl"), allow(unused_mut))]
+    #[cfg(any(feature = "egl", feature = "debug"))]
+    let mut renderer = gpus
+        .renderer::<_, Gles2Renderbuffer>(|_| None, &primary_gpu, &primary_gpu)
+        .unwrap();
+
+    #[cfg(feature = "egl")]
+    {
+        info!(
+            log,
+            "Trying to initialize EGL Hardware Acceleration via {:?}", primary_gpu
+        );
+        if renderer.bind_wl_display(&*display.borrow()).is_ok() {
+            info!(log, "EGL hardware-acceleration enabled");
+        }
+    }
+
+    #[cfg(feature = "debug")]
+    let fps_image =
+        image::io::Reader::with_format(std::io::Cursor::new(FPS_NUMBERS_PNG), image::ImageFormat::Png)
+            .decode()
+            .unwrap();
+    #[cfg(feature = "debug")]
+    let fps_texture = renderer
+        .import_memory(
+            &fps_image.to_rgba8(),
+            (fps_image.width() as i32, fps_image.height() as i32).into(),
+            false,
+        )
+        .expect("Unable to upload FPS texture");
+
     #[cfg(feature = "egl")]
-    let primary_gpu = primary_gpu(&session.seat()).unwrap_or_default();
+    let dmabuf_formats = renderer.dmabuf_formats().cloned().collect::<Vec<_>>();
 
     // setup the timer
     let timer = Timer::new().unwrap();
-
     let data = UdevData {
         session,
-        #[cfg(feature = "egl")]
         primary_gpu,
+        gpus,
         backends: HashMap::new(),
         signaler: session_signal.clone(),
         pointer_image: crate::cursor::Cursor::load(&log),
+        pointer_images: Vec::new(),
         render_timer: timer.handle(),
+        #[cfg(feature = "debug")]
+        fps_texture,
     };
     let mut state = AnvilState::init(display.clone(), event_loop.handle(), data, log.clone(), true);
 
@@ -205,27 +251,26 @@ pub fn run_udev(log: Logger) {
         state.device_added(dev, path.into())
     }
 
-    // init dmabuf support with format list from all gpus
-    // TODO: We need to update this list, when the set of gpus changes
-    // TODO2: This does not necessarily depend on egl, but mesa makes no use of it without wl_drm right now
+    // init dmabuf support with format list from our primary gpu
+    // TODO: This does not necessarily depend on egl, but mesa makes no use of it without wl_drm right now
     #[cfg(feature = "egl")]
     {
-        let mut formats = Vec::new();
-        for backend_data in state.backend_data.backends.values() {
-            formats.extend(backend_data.renderer.borrow().dmabuf_formats().cloned());
-        }
-
         init_dmabuf_global(
             &mut *display.borrow_mut(),
-            formats,
+            dmabuf_formats,
             |buffer, mut ddata| {
                 let anvil_state = ddata.get::<AnvilState<UdevData>>().unwrap();
-                for backend_data in anvil_state.backend_data.backends.values() {
-                    if backend_data.renderer.borrow_mut().import_dmabuf(buffer).is_ok() {
-                        return true;
-                    }
-                }
-                false
+                anvil_state
+                    .backend_data
+                    .gpus
+                    .renderer::<_, Gles2Renderbuffer>(
+                        |_| None,
+                        &anvil_state.backend_data.primary_gpu,
+                        &anvil_state.backend_data.primary_gpu,
+                    )
+                    .unwrap()
+                    .import_dmabuf(buffer, None)
+                    .is_ok()
             },
             log.clone(),
         );
@@ -267,6 +312,7 @@ pub fn run_udev(log: Logger) {
 pub type RenderSurface = GbmBufferedSurface<Rc<RefCell<GbmDevice<SessionFd>>>, SessionFd>;
 
 struct SurfaceData {
+    node: DrmNode,
     surface: RenderSurface,
     global: Option<Global<wl_output::WlOutput>>,
     #[cfg(feature = "debug")]
@@ -284,20 +330,16 @@ impl Drop for SurfaceData {
 struct BackendData {
     _restart_token: SignalToken,
     surfaces: Rc<RefCell<HashMap<crtc::Handle, Rc<RefCell<SurfaceData>>>>>,
-    pointer_images: Vec<(xcursor::parser::Image, Gles2Texture)>,
-    #[cfg(feature = "debug")]
-    fps_texture: Gles2Texture,
-    renderer: Rc<RefCell<Gles2Renderer>>,
     gbm: Rc<RefCell<GbmDevice<SessionFd>>>,
     registration_token: RegistrationToken,
     event_dispatcher: Dispatcher<'static, DrmDevice<SessionFd>, AnvilState<UdevData>>,
-    dev_id: u64,
+    dev_id: DrmNode,
 }
 
 fn scan_connectors(
+    node: DrmNode,
     device: &DrmDevice<SessionFd>,
     gbm: &Rc<RefCell<GbmDevice<SessionFd>>>,
-    renderer: &mut Gles2Renderer,
     display: &mut Display,
     space: &mut Space,
     signaler: &Signaler<SessionSignal>,
@@ -317,6 +359,12 @@ fn scan_connectors(
 
     let mut backends = HashMap::new();
 
+    let formats = {
+        let display = EGLDisplay::new(&*gbm.borrow(), logger.clone()).unwrap();
+        let context = EGLContext::new(&display, logger.clone()).unwrap();
+        context.dmabuf_render_formats().clone()
+    };
+
     // very naive way of finding good crtc/encoder/connector combinations. This problem is np-complete
     for connector_info in connector_infos {
         let encoder_infos = connector_info
@@ -355,11 +403,8 @@ fn scan_connectors(
             };
             surface.link(signaler.clone());
 
-            let renderer_formats =
-                Bind::<Dmabuf>::supported_formats(renderer).expect("Dmabuf renderer without formats");
-
             let gbm_surface =
-                match GbmBufferedSurface::new(surface, gbm.clone(), renderer_formats, logger.clone()) {
+                match GbmBufferedSurface::new(surface, gbm.clone(), formats.clone(), logger.clone()) {
                     Ok(renderer) => renderer,
                     Err(err) => {
                         warn!(logger, "Failed to create rendering surface: {}", err);
@@ -412,10 +457,15 @@ fn scan_connectors(
 
             output.user_data().insert_if_missing(|| UdevOutputId {
                 crtc,
-                device_id: device.device_id(),
+                device_id: DrmNode::from_fd(device.as_raw_fd())
+                    .unwrap()
+                    .node_with_type(NodeType::Render)
+                    .unwrap()
+                    .unwrap(),
             });
 
             entry.insert(Rc::new(RefCell::new(SurfaceData {
+                node,
                 surface: gbm_surface,
                 global: Some(global),
                 #[cfg(feature = "debug")]
@@ -459,56 +509,22 @@ impl AnvilState<UdevData> {
             None => return,
         };
 
-        let egl = match EGLDisplay::new(&gbm, self.log.clone()) {
-            Ok(display) => display,
-            Err(err) => {
-                warn!(
-                    self.log,
-                    "Skipping device {:?}, because of egl display error: {}", device_id, err
-                );
-                return;
-            }
-        };
-
-        let context = match EGLContext::new(&egl, self.log.clone()) {
-            Ok(context) => context,
-            Err(err) => {
-                warn!(
-                    self.log,
-                    "Skipping device {:?}, because of egl context error: {}", device_id, err
-                );
-                return;
-            }
-        };
-
-        let renderer = Rc::new(RefCell::new(unsafe {
-            Gles2Renderer::new(context, self.log.clone()).unwrap()
-        }));
-
-        #[cfg(feature = "egl")]
-        if path.canonicalize().ok() == self.backend_data.primary_gpu {
-            info!(self.log, "Initializing EGL Hardware Acceleration via {:?}", path);
-            if renderer
-                .borrow_mut()
-                .bind_wl_display(&*self.display.borrow())
-                .is_ok()
-            {
-                info!(self.log, "EGL hardware-acceleration enabled");
-            }
-        }
-
         let gbm = Rc::new(RefCell::new(gbm));
+        let dev_id = DrmNode::from_fd(device.as_raw_fd())
+            .unwrap()
+            .node_with_type(NodeType::Render)
+            .unwrap()
+            .unwrap();
         let backends = Rc::new(RefCell::new(scan_connectors(
+            dev_id,
             &device,
             &gbm,
-            &mut *renderer.borrow_mut(),
             &mut *self.display.borrow_mut(),
             &mut *self.space.borrow_mut(),
             &self.backend_data.signaler,
             &self.log,
         )));
 
-        let dev_id = device.device_id();
         let handle = self.handle.clone();
         let restart_token = self.backend_data.signaler.register(move |signal| match signal {
             SessionSignal::ActivateSession | SessionSignal::ActivateDevice { .. } => {
@@ -533,19 +549,14 @@ impl AnvilState<UdevData> {
         for backend in backends.borrow_mut().values() {
             // render first frame
             trace!(self.log, "Scheduling frame");
-            schedule_initial_render(backend.clone(), renderer.clone(), &self.handle, self.log.clone());
+            schedule_initial_render(
+                &mut self.backend_data.gpus,
+                backend.clone(),
+                &self.handle,
+                self.log.clone(),
+            );
         }
 
-        #[cfg(feature = "debug")]
-        let fps_texture = import_bitmap(
-            &mut renderer.borrow_mut(),
-            &image::io::Reader::with_format(std::io::Cursor::new(FPS_NUMBERS_PNG), image::ImageFormat::Png)
-                .decode()
-                .unwrap()
-                .to_rgba8(),
-        )
-        .expect("Unable to upload FPS texture");
-
         self.backend_data.backends.insert(
             dev_id,
             BackendData {
@@ -553,19 +564,20 @@ impl AnvilState<UdevData> {
                 registration_token,
                 event_dispatcher,
                 surfaces: backends,
-                renderer,
                 gbm,
-                pointer_images: Vec::new(),
-                #[cfg(feature = "debug")]
-                fps_texture,
                 dev_id,
             },
         );
     }
 
     fn device_changed(&mut self, device: dev_t) {
+        let node = DrmNode::from_dev_id(device)
+            .unwrap()
+            .node_with_type(NodeType::Render)
+            .unwrap()
+            .unwrap();
         //quick and dirty, just re-init all backends
-        if let Some(ref mut backend_data) = self.backend_data.backends.get_mut(&device) {
+        if let Some(ref mut backend_data) = self.backend_data.backends.get_mut(&node) {
             let logger = self.log.clone();
             let loop_handle = self.handle.clone();
             let signaler = self.backend_data.signaler.clone();
@@ -577,7 +589,7 @@ impl AnvilState<UdevData> {
                 .filter(|o| {
                     o.user_data()
                         .get::<UdevOutputId>()
-                        .map(|id| id.device_id == device)
+                        .map(|id| id.device_id == node)
                         .unwrap_or(false)
                 })
                 .cloned()
@@ -590,9 +602,9 @@ impl AnvilState<UdevData> {
             let source = backend_data.event_dispatcher.as_source_mut();
             let mut backends = backend_data.surfaces.borrow_mut();
             *backends = scan_connectors(
+                node,
                 &source,
                 &backend_data.gbm,
-                &mut *backend_data.renderer.borrow_mut(),
                 &mut *self.display.borrow_mut(),
                 &mut *self.space.borrow_mut(),
                 &signaler,
@@ -602,22 +614,18 @@ impl AnvilState<UdevData> {
             // fixup window coordinates
             crate::shell::fixup_positions(&mut *space);
 
-            for renderer in backends.values() {
+            for surface in backends.values() {
                 let logger = logger.clone();
                 // render first frame
-                schedule_initial_render(
-                    renderer.clone(),
-                    backend_data.renderer.clone(),
-                    &loop_handle,
-                    logger,
-                );
+                schedule_initial_render(&mut self.backend_data.gpus, surface.clone(), &loop_handle, logger);
             }
         }
     }
 
     fn device_removed(&mut self, device: dev_t) {
+        let node = DrmNode::from_dev_id(device).unwrap();
         // drop the backends on this side
-        if let Some(backend_data) = self.backend_data.backends.remove(&device) {
+        if let Some(backend_data) = self.backend_data.backends.remove(&node) {
             // drop surfaces
             backend_data.surfaces.borrow_mut().clear();
             debug!(self.log, "Surfaces dropped");
@@ -628,7 +636,7 @@ impl AnvilState<UdevData> {
                 .filter(|o| {
                     o.user_data()
                         .get::<UdevOutputId>()
-                        .map(|id| id.device_id == device)
+                        .map(|id| id.device_id == node)
                         .unwrap_or(false)
                 })
                 .cloned()
@@ -642,17 +650,12 @@ impl AnvilState<UdevData> {
             let _device = self.handle.remove(backend_data.registration_token);
             let _device = backend_data.event_dispatcher.into_source_inner();
 
-            // don't use hardware acceleration anymore, if this was the primary gpu
-            #[cfg(feature = "egl")]
-            if _device.dev_path().and_then(|path| path.canonicalize().ok()) == self.backend_data.primary_gpu {
-                backend_data.renderer.borrow_mut().unbind_wl_display();
-            }
             debug!(self.log, "Dropping device");
         }
     }
 
     // If crtc is `Some()`, render it, else render all crtcs
-    fn render(&mut self, dev_id: u64, crtc: Option<crtc::Handle>) {
+    fn render(&mut self, dev_id: DrmNode, crtc: Option<crtc::Handle>) {
         let device_backend = match self.backend_data.backends.get_mut(&dev_id) {
             Some(backend) => backend,
             None => {
@@ -682,30 +685,39 @@ impl AnvilState<UdevData> {
                 .backend_data
                 .pointer_image
                 .get_image(1 /*scale*/, self.start_time.elapsed().as_millis() as u32);
-            let renderer = &mut *device_backend.renderer.borrow_mut();
-            let pointer_images = &mut device_backend.pointer_images;
+            let primary_gpu = self.backend_data.primary_gpu;
+            let mut renderer = self
+                .backend_data
+                .gpus
+                .renderer::<_, Gles2Renderbuffer>(move |_| Some(primary_gpu), &primary_gpu, &dev_id)
+                .unwrap();
+            let pointer_images = &mut self.backend_data.pointer_images;
             let pointer_image = pointer_images
                 .iter()
                 .find_map(|(image, texture)| if image == &frame { Some(texture) } else { None })
                 .cloned()
                 .unwrap_or_else(|| {
-                    let image =
-                        ImageBuffer::from_raw(frame.width, frame.height, &*frame.pixels_rgba).unwrap();
-                    let texture = import_bitmap(renderer, &image).expect("Failed to import cursor bitmap");
+                    let texture = renderer
+                        .import_memory(
+                            &frame.pixels_rgba,
+                            (frame.width as i32, frame.height as i32).into(),
+                            false,
+                        )
+                        .expect("Failed to import cursor bitmap");
                     pointer_images.push((frame, texture.clone()));
                     texture
                 });
 
             let result = render_surface(
                 &mut *surface.borrow_mut(),
-                renderer,
+                &mut renderer,
                 device_backend.dev_id,
                 crtc,
                 &mut *self.space.borrow_mut(),
                 self.pointer_location,
                 &pointer_image,
                 #[cfg(feature = "debug")]
-                &device_backend.fps_texture,
+                &self.backend_data.fps_texture,
                 &*self.dnd_icon.lock().unwrap(),
                 &mut *self.cursor_status.lock().unwrap(),
                 &self.log,
@@ -747,13 +759,13 @@ impl AnvilState<UdevData> {
 #[allow(clippy::too_many_arguments)]
 fn render_surface(
     surface: &mut SurfaceData,
-    renderer: &mut Gles2Renderer,
-    device_id: dev_t,
+    renderer: &mut UdevRenderer<'_>,
+    device_id: DrmNode,
     crtc: crtc::Handle,
     space: &mut Space,
     pointer_location: Point<f64, Logical>,
-    pointer_image: &Gles2Texture,
-    #[cfg(feature = "debug")] fps_texture: &Gles2Texture,
+    pointer_image: &MultiTexture,
+    #[cfg(feature = "debug")] fps_texture: &MultiTexture,
     dnd_icon: &Option<wl_surface::WlSurface>,
     cursor_status: &mut CursorImageStatus,
     logger: &slog::Logger,
@@ -847,16 +859,19 @@ fn render_surface(
     }
 }
 
-fn schedule_initial_render<Data: 'static>(
+fn schedule_initial_render(
+    gpus: &mut GpuManager<EglGlesBackend>,
     surface: Rc<RefCell<SurfaceData>>,
-    renderer: Rc<RefCell<Gles2Renderer>>,
-    evt_handle: &LoopHandle<'static, Data>,
+    evt_handle: &LoopHandle<'static, AnvilState<UdevData>>,
     logger: ::slog::Logger,
 ) {
+    let node = surface.borrow().node;
     let result = {
+        let mut renderer = gpus
+            .renderer::<_, Gles2Renderbuffer>(|_| None, &node, &node)
+            .unwrap();
         let mut surface = surface.borrow_mut();
-        let mut renderer = renderer.borrow_mut();
-        initial_render(&mut surface.surface, &mut *renderer)
+        initial_render(&mut surface.surface, &mut renderer)
     };
     if let Err(err) = result {
         match err {
@@ -865,14 +880,19 @@ fn schedule_initial_render<Data: 'static>(
                 // TODO dont reschedule after 3(?) retries
                 warn!(logger, "Failed to submit page_flip: {}", err);
                 let handle = evt_handle.clone();
-                evt_handle.insert_idle(move |_| schedule_initial_render(surface, renderer, &handle, logger));
+                evt_handle.insert_idle(move |data| {
+                    schedule_initial_render(&mut data.backend_data.gpus, surface, &handle, logger)
+                });
             }
             SwapBuffersError::ContextLost(err) => panic!("Rendering loop lost: {}", err),
         }
     }
 }
 
-fn initial_render(surface: &mut RenderSurface, renderer: &mut Gles2Renderer) -> Result<(), SwapBuffersError> {
+fn initial_render(
+    surface: &mut RenderSurface,
+    renderer: &mut UdevRenderer<'_>,
+) -> Result<(), SwapBuffersError> {
     let (dmabuf, _age) = surface.next_buffer()?;
     renderer.bind(dmabuf)?;
     // Does not matter if we render an empty frame

anvil/udev: Use MultiGPU renderer