From 66f198ffd1d07839443bc6166cb975a117fcab24 Mon Sep 17 00:00:00 2001
From: Victoria Brekenfeld <github@drakulix.de>
Date: Tue, 14 Jan 2025 19:22:59 +0100
Subject: [PATCH] renderer: Introduce explicit framebuffers

---
 Cargo.toml                                    |   1 +
 anvil/src/drawing.rs                          |   6 +-
 anvil/src/render.rs                           |   5 +-
 anvil/src/shell/element.rs                    |   2 +-
 anvil/src/udev.rs                             |   2 +-
 anvil/src/winit.rs                            |  49 +-
 anvil/src/x11.rs                              |  16 +-
 examples/buffer_test.rs                       |  29 +-
 examples/minimal.rs                           |  76 +--
 smallvil/src/winit.rs                         |  32 +-
 src/backend/drm/compositor/elements.rs        |   8 +-
 src/backend/drm/compositor/frame_result.rs    |  25 +-
 src/backend/drm/compositor/mod.rs             |  34 +-
 src/backend/drm/output.rs                     |  44 +-
 src/backend/renderer/damage/mod.rs            | 414 +++++--------
 src/backend/renderer/element/memory.rs        | 135 +----
 src/backend/renderer/element/mod.rs           | 140 +----
 src/backend/renderer/element/solid.rs         | 116 +---
 src/backend/renderer/element/surface.rs       | 183 +-----
 src/backend/renderer/element/tests.rs         |  24 +-
 src/backend/renderer/element/texture.rs       | 236 +-------
 .../renderer/element/utils/elements.rs        |  12 +-
 src/backend/renderer/gles/element.rs          |   4 +-
 src/backend/renderer/gles/mod.rs              | 509 +++++++---------
 src/backend/renderer/gles/texture.rs          |   4 +-
 src/backend/renderer/glow.rs                  |  95 ++-
 src/backend/renderer/mod.rs                   | 135 ++---
 src/backend/renderer/multigpu/gbm.rs          |  21 +-
 src/backend/renderer/multigpu/mod.rs          | 542 +++++++++++-------
 src/backend/renderer/pixman/mod.rs            | 171 +++---
 src/backend/renderer/test.rs                  |  57 +-
 src/backend/renderer/utils/wayland.rs         |  24 +-
 src/backend/winit/mod.rs                      |  42 +-
 src/desktop/space/element/mod.rs              |   2 +-
 src/desktop/space/element/wayland.rs          |   2 +-
 src/desktop/space/mod.rs                      |  11 +-
 src/desktop/space/wayland/layer.rs            |   2 +-
 src/desktop/space/wayland/window.rs           |   2 +-
 src/desktop/space/wayland/x11.rs              |   2 +-
 wlcs_anvil/src/main_loop.rs                   |  10 +-
 40 files changed, 1222 insertions(+), 2002 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml
index ea2d574e2dc3..03aff340f573 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -69,6 +69,7 @@ encoding_rs = { version = "0.8.33", optional = true }
 profiling = "1.0.13"
 smallvec = "1.11"
 pixman = { version = "0.2.1", features = ["drm-fourcc", "sync"], optional = true }
+aliasable = "0.1.3"
 
 
 [dev-dependencies]
diff --git a/anvil/src/drawing.rs b/anvil/src/drawing.rs
index 1f43faa36934..4cb028e2f509 100644
--- a/anvil/src/drawing.rs
+++ b/anvil/src/drawing.rs
@@ -191,14 +191,14 @@ where
 }
 
 #[cfg(feature = "debug")]
-impl<R> RenderElement<R> for FpsElement<<R as Renderer>::TextureId>
+impl<R> RenderElement<R> for FpsElement<R::TextureId>
 where
     R: Renderer + ImportAll,
-    <R as Renderer>::TextureId: 'static,
+    R::TextureId: 'static,
 {
     fn draw(
         &self,
-        frame: &mut <R as Renderer>::Frame<'_>,
+        frame: &mut R::Frame<'_, '_>,
         _src: Rectangle<f64, Buffer>,
         dst: Rectangle<i32, Physical>,
         damage: &[Rectangle<i32, Physical>],
diff --git a/anvil/src/render.rs b/anvil/src/render.rs
index 843cea19af93..54905e1a2c03 100644
--- a/anvil/src/render.rs
+++ b/anvil/src/render.rs
@@ -35,7 +35,7 @@ smithay::backend::renderer::element::render_elements! {
     // a feature-dependent lifetime, which introduces a lot more feature bounds
     // as the whole type changes and we can't have an unused lifetime (for when "debug" is disabled)
     // in the declaration.
-    Fps=FpsElement<<R as Renderer>::TextureId>,
+    Fps=FpsElement<R::TextureId>,
 }
 
 impl<R: Renderer> std::fmt::Debug for CustomRenderElements<R> {
@@ -195,6 +195,7 @@ pub fn render_output<'a, 'd, R>(
     space: &'a Space<WindowElement>,
     custom_elements: impl IntoIterator<Item = CustomRenderElements<R>>,
     renderer: &'a mut R,
+    framebuffer: &'a mut R::Framebuffer<'_>,
     damage_tracker: &'d mut OutputDamageTracker,
     age: usize,
     show_window_preview: bool,
@@ -205,5 +206,5 @@ where
 {
     let (elements, clear_color) =
         output_elements(output, space, custom_elements, renderer, show_window_preview);
-    damage_tracker.render_output(renderer, age, &elements, clear_color)
+    damage_tracker.render_output(renderer, framebuffer, age, &elements, clear_color)
 }
diff --git a/anvil/src/shell/element.rs b/anvil/src/shell/element.rs
index 50af3781bea8..6ae0f4836518 100644
--- a/anvil/src/shell/element.rs
+++ b/anvil/src/shell/element.rs
@@ -416,7 +416,7 @@ impl<R: Renderer> std::fmt::Debug for WindowRenderElement<R> {
 impl<R> AsRenderElements<R> for WindowElement
 where
     R: Renderer + ImportAll + ImportMem,
-    <R as Renderer>::TextureId: Clone + Texture + 'static,
+    R::TextureId: Clone + Texture + 'static,
 {
     type RenderElement = WindowRenderElement<R>;
 
diff --git a/anvil/src/udev.rs b/anvil/src/udev.rs
index 75ff68d2cd61..33d86dcc291d 100644
--- a/anvil/src/udev.rs
+++ b/anvil/src/udev.rs
@@ -1354,7 +1354,7 @@ impl AnvilState<UdevData> {
                 !has_rendered
             }
             Err(err) => {
-                warn!("Error during rendering: {:?}", err);
+                warn!("Error during rendering: {:#?}", err);
                 match err {
                     SwapBuffersError::AlreadySwapped => false,
                     SwapBuffersError::TemporaryFailure(err) => match err.downcast_ref::<DrmError>() {
diff --git a/anvil/src/winit.rs b/anvil/src/winit.rs
index 66fb0caaf628..6a526ac31879 100644
--- a/anvil/src/winit.rs
+++ b/anvil/src/winit.rs
@@ -291,31 +291,29 @@ pub fn run_winit() {
 
             #[cfg(feature = "debug")]
             let mut renderdoc = state.renderdoc.as_mut();
-            let render_res = backend.bind().and_then(|_| {
+
+            let age = if *full_redraw > 0 {
+                0
+            } else {
+                backend.buffer_age().unwrap_or(0)
+            };
+            #[cfg(feature = "debug")]
+            let window_handle = backend
+                .window()
+                .window_handle()
+                .map(|handle| {
+                    if let RawWindowHandle::Wayland(handle) = handle.as_raw() {
+                        handle.surface.as_ptr()
+                    } else {
+                        std::ptr::null_mut()
+                    }
+                })
+                .unwrap_or_else(|_| std::ptr::null_mut());
+            let render_res = backend.bind().and_then(|(renderer, mut fb)| {
                 #[cfg(feature = "debug")]
                 if let Some(renderdoc) = renderdoc.as_mut() {
-                    renderdoc.start_frame_capture(
-                        backend.renderer().egl_context().get_context_handle(),
-                        backend
-                            .window()
-                            .window_handle()
-                            .map(|handle| {
-                                if let RawWindowHandle::Wayland(handle) = handle.as_raw() {
-                                    handle.surface.as_ptr()
-                                } else {
-                                    std::ptr::null_mut()
-                                }
-                            })
-                            .unwrap_or_else(|_| std::ptr::null_mut()),
-                    );
+                    renderdoc.start_frame_capture(renderer.egl_context().get_context_handle(), window_handle);
                 }
-                let age = if *full_redraw > 0 {
-                    0
-                } else {
-                    backend.buffer_age().unwrap_or(0)
-                };
-
-                let renderer = backend.renderer();
 
                 let mut elements = Vec::<CustomRenderElements<GlesRenderer>>::new();
 
@@ -349,11 +347,12 @@ pub fn run_winit() {
                 #[cfg(feature = "debug")]
                 elements.push(CustomRenderElements::Fps(fps_element.clone()));
 
-                render_output(
+                let res = render_output(
                     &output,
                     space,
                     elements,
                     renderer,
+                    &mut fb,
                     damage_tracker,
                     age,
                     show_window_preview,
@@ -361,7 +360,9 @@ pub fn run_winit() {
                 .map_err(|err| match err {
                     OutputDamageTrackerError::Rendering(err) => err.into(),
                     _ => unreachable!(),
-                })
+                });
+
+                res
             });
 
             match render_res {
diff --git a/anvil/src/x11.rs b/anvil/src/x11.rs
index 88803754f820..154f6102f964 100644
--- a/anvil/src/x11.rs
+++ b/anvil/src/x11.rs
@@ -314,12 +314,15 @@ pub fn run_x11() {
             #[cfg(feature = "debug")]
             fps_element.update_fps(fps);
 
-            let (buffer, age) = backend_data.surface.buffer().expect("gbm device was destroyed");
-            if let Err(err) = backend_data.renderer.bind(buffer) {
-                error!("Error while binding buffer: {}", err);
-                profiling::finish_frame!();
-                continue;
-            }
+            let (mut buffer, age) = backend_data.surface.buffer().expect("gbm device was destroyed");
+            let mut fb = match backend_data.renderer.bind(&mut buffer) {
+                Ok(fb) => fb,
+                Err(err) => {
+                    error!("Error while binding buffer: {}", err);
+                    profiling::finish_frame!();
+                    continue;
+                }
+            };
 
             #[cfg(feature = "debug")]
             if let Some(renderdoc) = state.renderdoc.as_mut() {
@@ -394,6 +397,7 @@ pub fn run_x11() {
                 &state.space,
                 elements,
                 &mut backend_data.renderer,
+                &mut fb,
                 &mut backend_data.damage_tracker,
                 age.into(),
                 state.show_window_preview,
diff --git a/examples/buffer_test.rs b/examples/buffer_test.rs
index 26c2cefd1801..034c64ba84b2 100644
--- a/examples/buffer_test.rs
+++ b/examples/buffer_test.rs
@@ -202,7 +202,7 @@ fn buffer_test(args: TestArgs) {
     };
 
     // 2. alloc buffer
-    let buffer = allocator
+    let mut buffer = allocator
         .create_buffer(args.width, args.height, args.fourcc, &[args.modifier])
         .expect("Failed to allocate buffer");
 
@@ -223,12 +223,7 @@ fn buffer_test(args: TestArgs) {
             let context = EGLContext::new(&display).expect("Failed to create EGL context");
             let mut renderer = unsafe { GlesRenderer::new(context).expect("Failed to init GL ES renderer") };
 
-            render_into(
-                &mut renderer,
-                buffer.clone(),
-                args.width as i32,
-                args.height as i32,
-            );
+            render_into(&mut renderer, &mut buffer, args.width as i32, args.height as i32);
         }
     }
 
@@ -274,15 +269,15 @@ fn buffer_test(args: TestArgs) {
     }
 }
 
-fn render_into<R, T>(renderer: &mut R, buffer: T, w: i32, h: i32)
+fn render_into<R, T>(renderer: &mut R, buffer: &mut T, w: i32, h: i32)
 where
     R: Renderer + Bind<T>,
 {
     // Bind it as a framebuffer
-    renderer.bind(buffer).expect("Failed to bind dmabuf");
+    let mut framebuffer = renderer.bind(buffer).expect("Failed to bind dmabuf");
 
     let mut frame = renderer
-        .render((w, h).into(), Transform::Normal)
+        .render(&mut framebuffer, (w, h).into(), Transform::Normal)
         .expect("Failed to create render frame");
     frame
         .clear(
@@ -322,11 +317,13 @@ where
     let texture = renderer
         .import_dmabuf(&buffer, None)
         .expect("Failed to import dmabuf");
-    let offscreen = Offscreen::<T>::create_buffer(renderer, Fourcc::Abgr8888, (w, h).into())
+    let mut offscreen = Offscreen::<T>::create_buffer(renderer, Fourcc::Abgr8888, (w, h).into())
         .expect("Failed to create offscreen buffer");
-    renderer.bind(offscreen).expect("Failed to bind offscreen buffer");
+    let mut framebuffer = renderer
+        .bind(&mut offscreen)
+        .expect("Failed to bind offscreen buffer");
     let mut frame = renderer
-        .render((w, h).into(), Transform::Normal)
+        .render(&mut framebuffer, (w, h).into(), Transform::Normal)
         .expect("Failed to create render frame");
     frame
         .render_texture_at(
@@ -348,7 +345,11 @@ where
 
     if let Some(path) = dump {
         let mapping = renderer
-            .copy_framebuffer(Rectangle::from_size((w, h).into()), Fourcc::Abgr8888)
+            .copy_framebuffer(
+                &framebuffer,
+                Rectangle::from_size((w, h).into()),
+                Fourcc::Abgr8888,
+            )
             .expect("Failed to map framebuffer");
         let copy = renderer.map_texture(&mapping).expect("Failed to read mapping");
         image::save_buffer(path, copy, w as u32, h as u32, image::ColorType::Rgba8)
diff --git a/examples/minimal.rs b/examples/minimal.rs
index 80021d6a400d..310a66d8753e 100644
--- a/examples/minimal.rs
+++ b/examples/minimal.rs
@@ -205,49 +205,51 @@ pub fn run_winit() -> Result<(), Box<dyn std::error::Error>> {
             PumpStatus::Exit(_) => return Ok(()),
         };
 
-        backend.bind().unwrap();
-
         let size = backend.window_size();
         let damage = Rectangle::from_size(size);
+        {
+            let (renderer, mut framebuffer) = backend.bind().unwrap();
+            let elements = state
+                .xdg_shell_state
+                .toplevel_surfaces()
+                .iter()
+                .flat_map(|surface| {
+                    render_elements_from_surface_tree(
+                        renderer,
+                        surface.wl_surface(),
+                        (0, 0),
+                        1.0,
+                        1.0,
+                        Kind::Unspecified,
+                    )
+                })
+                .collect::<Vec<WaylandSurfaceRenderElement<GlesRenderer>>>();
+
+            let mut frame = renderer
+                .render(&mut framebuffer, size, Transform::Flipped180)
+                .unwrap();
+            frame.clear(Color32F::new(0.1, 0.0, 0.0, 1.0), &[damage]).unwrap();
+            draw_render_elements(&mut frame, 1.0, &elements, &[damage]).unwrap();
+            // We rely on the nested compositor to do the sync for us
+            let _ = frame.finish().unwrap();
 
-        let elements = state
-            .xdg_shell_state
-            .toplevel_surfaces()
-            .iter()
-            .flat_map(|surface| {
-                render_elements_from_surface_tree(
-                    backend.renderer(),
-                    surface.wl_surface(),
-                    (0, 0),
-                    1.0,
-                    1.0,
-                    Kind::Unspecified,
-                )
-            })
-            .collect::<Vec<WaylandSurfaceRenderElement<GlesRenderer>>>();
-
-        let mut frame = backend.renderer().render(size, Transform::Flipped180).unwrap();
-        frame.clear(Color32F::new(0.1, 0.0, 0.0, 1.0), &[damage]).unwrap();
-        draw_render_elements(&mut frame, 1.0, &elements, &[damage]).unwrap();
-        // We rely on the nested compositor to do the sync for us
-        let _ = frame.finish().unwrap();
-
-        for surface in state.xdg_shell_state.toplevel_surfaces() {
-            send_frames_surface_tree(surface.wl_surface(), start_time.elapsed().as_millis() as u32);
-        }
+            for surface in state.xdg_shell_state.toplevel_surfaces() {
+                send_frames_surface_tree(surface.wl_surface(), start_time.elapsed().as_millis() as u32);
+            }
 
-        if let Some(stream) = listener.accept()? {
-            println!("Got a client: {:?}", stream);
+            if let Some(stream) = listener.accept()? {
+                println!("Got a client: {:?}", stream);
 
-            let client = display
-                .handle()
-                .insert_client(stream, Arc::new(ClientState::default()))
-                .unwrap();
-            clients.push(client);
-        }
+                let client = display
+                    .handle()
+                    .insert_client(stream, Arc::new(ClientState::default()))
+                    .unwrap();
+                clients.push(client);
+            }
 
-        display.dispatch_clients(&mut state)?;
-        display.flush_clients()?;
+            display.dispatch_clients(&mut state)?;
+            display.flush_clients()?;
+        }
 
         // It is important that all events on the display have been dispatched and flushed to clients before
         // swapping buffers because this operation may block.
diff --git a/smallvil/src/winit.rs b/smallvil/src/winit.rs
index 782b6ee7d3c9..98e94ef8d316 100644
--- a/smallvil/src/winit.rs
+++ b/smallvil/src/winit.rs
@@ -68,18 +68,26 @@ pub fn init_winit(
                 let size = backend.window_size();
                 let damage = Rectangle::from_size(size);
 
-                backend.bind().unwrap();
-                smithay::desktop::space::render_output::<_, WaylandSurfaceRenderElement<GlesRenderer>, _, _>(
-                    &output,
-                    backend.renderer(),
-                    1.0,
-                    0,
-                    [&state.space],
-                    &[],
-                    &mut damage_tracker,
-                    [0.1, 0.1, 0.1, 1.0],
-                )
-                .unwrap();
+                {
+                    let (renderer, mut framebuffer) = backend.bind().unwrap();
+                    smithay::desktop::space::render_output::<
+                        _,
+                        WaylandSurfaceRenderElement<GlesRenderer>,
+                        _,
+                        _,
+                    >(
+                        &output,
+                        renderer,
+                        &mut framebuffer,
+                        1.0,
+                        0,
+                        [&state.space],
+                        &[],
+                        &mut damage_tracker,
+                        [0.1, 0.1, 0.1, 1.0],
+                    )
+                    .unwrap();
+                }
                 backend.submit(Some(&[damage])).unwrap();
 
                 state.space.elements().for_each(|window| {
diff --git a/src/backend/drm/compositor/elements.rs b/src/backend/drm/compositor/elements.rs
index 36fba72ef782..d0c793854d1d 100644
--- a/src/backend/drm/compositor/elements.rs
+++ b/src/backend/drm/compositor/elements.rs
@@ -39,12 +39,12 @@ where
 {
     fn draw(
         &self,
-        frame: &mut <R as Renderer>::Frame<'_>,
+        frame: &mut R::Frame<'_, '_>,
         _src: Rectangle<f64, Buffer>,
         dst: Rectangle<i32, Physical>,
         damage: &[Rectangle<i32, Physical>],
         _opaque_regions: &[Rectangle<i32, Physical>],
-    ) -> Result<(), <R as Renderer>::Error> {
+    ) -> Result<(), R::Error> {
         frame.clear(
             Color32F::TRANSPARENT,
             &damage
@@ -172,12 +172,12 @@ where
 {
     fn draw(
         &self,
-        _frame: &mut <R as Renderer>::Frame<'_>,
+        _frame: &mut R::Frame<'_, '_>,
         _src: Rectangle<f64, Buffer>,
         _dst: Rectangle<i32, Physical>,
         _damage: &[Rectangle<i32, Physical>],
         _opaque_regions: &[Rectangle<i32, Physical>],
-    ) -> Result<(), <R as Renderer>::Error> {
+    ) -> Result<(), R::Error> {
         // We do not actually draw anything here
         Ok(())
     }
diff --git a/src/backend/drm/compositor/frame_result.rs b/src/backend/drm/compositor/frame_result.rs
index 7c0cfdf898e9..2ca9a11824a6 100644
--- a/src/backend/drm/compositor/frame_result.rs
+++ b/src/backend/drm/compositor/frame_result.rs
@@ -12,7 +12,7 @@ use crate::{
             element::{Element, Id, RenderElement, RenderElementStates},
             sync::SyncPoint,
             utils::{CommitCounter, DamageSet, DamageSnapshot, OpaqueRegions},
-            Blit, Color32F, Frame, Renderer,
+            Bind, Blit, Color32F, Frame, Renderer,
         },
     },
     output::OutputNoMode,
@@ -261,7 +261,7 @@ where
     <B as AsDmabuf>::Error: std::fmt::Debug,
     F: Framebuffer,
 {
-    /// Blit the frame result into a currently bound buffer
+    /// Blit the frame result
     #[allow(clippy::too_many_arguments)]
     pub fn blit_frame_result<R>(
         &self,
@@ -269,12 +269,13 @@ where
         transform: Transform,
         scale: impl Into<Scale<f64>>,
         renderer: &mut R,
+        framebuffer: &mut R::Framebuffer<'_>,
         damage: impl IntoIterator<Item = Rectangle<i32, Physical>>,
         filter: impl IntoIterator<Item = Id>,
-    ) -> Result<SyncPoint, BlitFrameResultError<<R as Renderer>::Error, <B as AsDmabuf>::Error>>
+    ) -> Result<SyncPoint, BlitFrameResultError<R::Error, <B as AsDmabuf>::Error>>
     where
-        R: Renderer + Blit<Dmabuf>,
-        <R as Renderer>::TextureId: 'static,
+        R: Renderer + Bind<Dmabuf> + Blit,
+        R::TextureId: 'static,
         E: Element + RenderElement<R>,
     {
         let size = size.into();
@@ -335,7 +336,7 @@ where
             tracing::trace!("clearing frame damage {:#?}", clear_damage);
 
             let mut frame = renderer
-                .render(size, transform)
+                .render(framebuffer, size, transform)
                 .map_err(BlitFrameResultError::Rendering)?;
 
             frame
@@ -346,7 +347,7 @@ where
         }
 
         // first do the potential blit
-        if let Some((sync, dmabuf, geometry)) = primary_dmabuf {
+        if let Some((sync, mut dmabuf, geometry)) = primary_dmabuf {
             let blit_damage = damage
                 .iter()
                 .filter_map(|d| d.intersection(geometry))
@@ -355,10 +356,14 @@ where
             tracing::trace!("blitting frame with damage: {:#?}", blit_damage);
 
             renderer.wait(&sync).map_err(BlitFrameResultError::Rendering)?;
+            let fb = renderer
+                .bind(&mut dmabuf)
+                .map_err(BlitFrameResultError::Rendering)?;
             for rect in blit_damage {
                 renderer
-                    .blit_from(
-                        dmabuf.clone(),
+                    .blit(
+                        &fb,
+                        framebuffer,
                         rect,
                         rect,
                         crate::backend::renderer::TextureFilter::Linear,
@@ -372,7 +377,7 @@ where
             tracing::trace!("drawing {} frame element(s)", elements_to_render.len());
 
             let mut frame = renderer
-                .render(size, transform)
+                .render(framebuffer, size, transform)
                 .map_err(BlitFrameResultError::Rendering)?;
 
             for element in elements_to_render.iter().rev() {
diff --git a/src/backend/drm/compositor/mod.rs b/src/backend/drm/compositor/mod.rs
index 493a96a83520..66a0a2136157 100644
--- a/src/backend/drm/compositor/mod.rs
+++ b/src/backend/drm/compositor/mod.rs
@@ -140,8 +140,8 @@ use wayland_server::{protocol::wl_buffer::WlBuffer, Resource};
 
 #[cfg(feature = "renderer_pixman")]
 use crate::backend::renderer::{
-    pixman::{PixmanError, PixmanRenderBuffer, PixmanRenderer, PixmanTexture},
-    Frame as _, ImportAll, Unbind,
+    pixman::{PixmanError, PixmanRenderer, PixmanTexture},
+    ImportAll,
 };
 use crate::{
     backend::{
@@ -161,7 +161,7 @@ use crate::{
             },
             sync::SyncPoint,
             utils::{CommitCounter, DamageBag},
-            Bind, Color32F, DebugFlags, Renderer, Texture,
+            Bind, Color32F, DebugFlags, Frame as RendererFrame, Renderer, RendererSuper, Texture,
         },
         SwapBuffersError,
     },
@@ -832,7 +832,7 @@ pub(crate) type RenderFrameErrorType<A, F, R> = RenderFrameError<
     <A as Allocator>::Error,
     <<A as Allocator>::Buffer as AsDmabuf>::Error,
     <F as ExportFramebuffer<<A as Allocator>::Buffer>>::Error,
-    <R as Renderer>::Error,
+    <R as RendererSuper>::Error,
 >;
 
 #[derive(Debug)]
@@ -1684,8 +1684,7 @@ where
     where
         E: RenderElement<R>,
         R: Renderer + Bind<Dmabuf>,
-        <R as Renderer>::TextureId: Texture + 'static,
-        <R as Renderer>::Error: Send + Sync + 'static,
+        R::TextureId: Texture + 'static,
     {
         let mut clear_color = clear_color.into();
 
@@ -2165,7 +2164,7 @@ where
                 primary_plane_elements.len(),
                 self.surface.plane(),
             );
-            let (dmabuf, age) = {
+            let (mut dmabuf, age) = {
                 let primary_plane_state = next_frame_state.plane_state(self.surface.plane()).unwrap();
                 let config = primary_plane_state.config.as_ref().unwrap();
                 let slot = match &config.buffer.buffer {
@@ -2230,9 +2229,12 @@ where
                 )
                 .collect::<Vec<_>>();
 
+            let mut framebuffer = renderer
+                .bind(&mut dmabuf)
+                .map_err(|err| RenderFrameError::RenderFrame(OutputDamageTrackerError::Rendering(err)))?;
             let render_res =
                 self.damage_tracker
-                    .render_output_with(renderer, dmabuf, age, &elements, clear_color);
+                    .render_output(renderer, &mut framebuffer, age, &elements, clear_color);
 
             // restore the renderer debug flags
             renderer.set_debug_flags(renderer_debug_flags);
@@ -3297,14 +3299,14 @@ where
             }?;
 
             let ret = cursor_buffer
-                .map_mut::<_, Result<_, <PixmanRenderer as Renderer>::Error>>(
+                .map_mut::<_, Result<_, PixmanError>>(
                     0,
                     0,
                     cursor_buffer_size.w as u32,
                     cursor_buffer_size.h as u32,
                     |mbo| {
                         let plane_pixman_format = pixman::FormatCode::try_from(DrmFourcc::Argb8888).unwrap();
-                        let cursor_dst = unsafe {
+                        let mut cursor_dst = unsafe {
                             pixman::Image::from_raw_mut(
                                 plane_pixman_format,
                                 mbo.width() as usize,
@@ -3315,9 +3317,9 @@ where
                             )
                         }
                         .map_err(|_| PixmanError::ImportFailed)?;
-                        pixman_renderer.bind(PixmanRenderBuffer::from(cursor_dst))?;
-
-                        let mut frame = pixman_renderer.render(cursor_plane_size, output_transform)?;
+                        let mut framebuffer = pixman_renderer.bind(&mut cursor_dst)?;
+                        let mut frame =
+                            pixman_renderer.render(&mut framebuffer, cursor_plane_size, output_transform)?;
                         frame.clear(Color32F::TRANSPARENT, &[Rectangle::from_size(cursor_plane_size)])?;
                         let src = element.src();
                         let dst = Rectangle::from_size(element_geometry.size);
@@ -3336,8 +3338,6 @@ where
                 )
                 .expect("Lost track of cursor device");
 
-            _ = pixman_renderer.unbind();
-
             if let Err(err) = ret {
                 debug!("{err}");
                 return None;
@@ -4343,7 +4343,7 @@ pub enum RenderFrameError<
     A: std::error::Error + Send + Sync + 'static,
     B: std::error::Error + Send + Sync + 'static,
     F: std::error::Error + Send + Sync + 'static,
-    R: std::error::Error + Send + Sync + 'static,
+    R: std::error::Error,
 > {
     /// Preparing the frame encountered an error
     #[error(transparent)]
@@ -4358,7 +4358,7 @@ where
     A: std::error::Error + Send + Sync + 'static,
     B: std::error::Error + Send + Sync + 'static,
     F: std::error::Error + Send + Sync + 'static,
-    R: std::error::Error + Send + Sync + 'static,
+    R: std::error::Error,
 {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         match self {
diff --git a/src/backend/drm/output.rs b/src/backend/drm/output.rs
index 43725c4bd67a..4054ed483cb4 100644
--- a/src/backend/drm/output.rs
+++ b/src/backend/drm/output.rs
@@ -18,7 +18,7 @@ use crate::{
             gbm::GbmDevice,
             Allocator,
         },
-        renderer::{element::RenderElement, Bind, Color32F, DebugFlags, Renderer, Texture},
+        renderer::{element::RenderElement, Bind, Color32F, DebugFlags, Renderer, RendererSuper, Texture},
     },
     output::OutputModeSource,
 };
@@ -142,7 +142,7 @@ pub type DrmOutputManagerResult<U, A, F, R> = Result<
         <A as Allocator>::Error,
         <<A as Allocator>::Buffer as AsDmabuf>::Error,
         <F as ExportFramebuffer<<A as Allocator>::Buffer>>::Error,
-        <R as Renderer>::Error,
+        <R as RendererSuper>::Error,
     >,
 >;
 
@@ -224,8 +224,8 @@ where
     where
         E: RenderElement<R>,
         R: Renderer + Bind<Dmabuf>,
-        <R as Renderer>::TextureId: Texture + 'static,
-        <R as Renderer>::Error: Send + Sync + 'static,
+        R::TextureId: Texture + 'static,
+        R::Error: Send + Sync + 'static,
     {
         let output_mode_source = output_mode_source.into();
 
@@ -420,8 +420,8 @@ where
     where
         E: RenderElement<R>,
         R: Renderer + Bind<Dmabuf>,
-        <R as Renderer>::TextureId: Texture + 'static,
-        <R as Renderer>::Error: Send + Sync + 'static,
+        R::TextureId: Texture + 'static,
+        R::Error: Send + Sync + 'static,
     {
         use_mode_internal(
             &self.compositor,
@@ -449,8 +449,8 @@ where
     where
         E: RenderElement<R>,
         R: Renderer + Bind<Dmabuf>,
-        <R as Renderer>::TextureId: Texture + 'static,
-        <R as Renderer>::Error: Send + Sync + 'static,
+        R::TextureId: Texture + 'static,
+        R::Error: Send + Sync + 'static,
     {
         let mut write_guard = self.compositor.write().unwrap();
 
@@ -604,8 +604,8 @@ where
     where
         E: RenderElement<R>,
         R: Renderer + Bind<Dmabuf>,
-        <R as Renderer>::TextureId: Texture + 'static,
-        <R as Renderer>::Error: Send + Sync + 'static,
+        R::TextureId: Texture + 'static,
+        R::Error: Send + Sync + 'static,
     {
         self.with_compositor(|compositor| {
             compositor.render_frame(renderer, elements, clear_color, frame_mode)
@@ -666,8 +666,8 @@ where
     where
         E: RenderElement<R>,
         R: Renderer + Bind<Dmabuf>,
-        <R as Renderer>::TextureId: Texture + 'static,
-        <R as Renderer>::Error: Send + Sync + 'static,
+        R::TextureId: Texture + 'static,
+        R::Error: Send + Sync + 'static,
     {
         use_mode_internal(
             &self.compositor,
@@ -740,8 +740,8 @@ where
     U: 'static,
     E: RenderElement<R>,
     R: Renderer + Bind<Dmabuf>,
-    <R as Renderer>::TextureId: Texture + 'static,
-    <R as Renderer>::Error: Send + Sync + 'static,
+    R::TextureId: Texture + 'static,
+    R::Error: Send + Sync + 'static,
 {
     let mut write_guard = compositor.write().unwrap();
 
@@ -833,8 +833,8 @@ pub struct DrmOutputRenderElements<R, E>
 where
     E: RenderElement<R>,
     R: Renderer + Bind<Dmabuf>,
-    <R as Renderer>::TextureId: Texture + 'static,
-    <R as Renderer>::Error: Send + Sync + 'static,
+    R::TextureId: Texture + 'static,
+    R::Error: Send + Sync + 'static,
 {
     render_elements: HashMap<crtc::Handle, (Vec<E>, Color32F)>,
     _renderer: PhantomData<R>,
@@ -844,8 +844,8 @@ impl<R, E> DrmOutputRenderElements<R, E>
 where
     E: RenderElement<R>,
     R: Renderer + Bind<Dmabuf>,
-    <R as Renderer>::TextureId: Texture + 'static,
-    <R as Renderer>::Error: Send + Sync + 'static,
+    R::TextureId: Texture + 'static,
+    R::Error: Send + Sync + 'static,
 {
     /// Construct a new empty set of render elements
     pub fn new() -> Self {
@@ -869,8 +869,8 @@ impl<R, E> Default for DrmOutputRenderElements<R, E>
 where
     E: RenderElement<R>,
     R: Renderer + Bind<Dmabuf>,
-    <R as Renderer>::TextureId: Texture + 'static,
-    <R as Renderer>::Error: Send + Sync + 'static,
+    R::TextureId: Texture + 'static,
+    R::Error: Send + Sync + 'static,
 {
     fn default() -> Self {
         Self::new()
@@ -881,8 +881,8 @@ impl<R, E> DrmOutputRenderElements<R, E>
 where
     E: RenderElement<R>,
     R: Renderer + Bind<Dmabuf>,
-    <R as Renderer>::TextureId: Texture + 'static,
-    <R as Renderer>::Error: Send + Sync + 'static,
+    R::TextureId: Texture + 'static,
+    R::Error: Send + Sync + 'static,
 {
     /// Adds elements to be used when rendering for a given `crtc`.
     ///
diff --git a/src/backend/renderer/damage/mod.rs b/src/backend/renderer/damage/mod.rs
index 7267823322e3..f7f4b3f00929 100644
--- a/src/backend/renderer/damage/mod.rs
+++ b/src/backend/renderer/damage/mod.rs
@@ -27,114 +27,9 @@
 //!
 //! ```no_run
 //! # use smithay::{
-//! #     backend::renderer::{Color32F, DebugFlags, Frame, ImportMem, Renderer, Texture, TextureFilter, sync::SyncPoint},
+//! #     backend::renderer::{Color32F, DebugFlags, Frame, ImportMem, Renderer, Texture, TextureFilter, sync::SyncPoint, test::{DummyRenderer, DummyFramebuffer}},
 //! #     utils::{Buffer, Physical, Rectangle, Size},
 //! # };
-//! #
-//! # #[derive(Clone, Debug)]
-//! # struct FakeTexture;
-//! #
-//! # impl Texture for FakeTexture {
-//! #     fn width(&self) -> u32 {
-//! #         unimplemented!()
-//! #     }
-//! #     fn height(&self) -> u32 {
-//! #         unimplemented!()
-//! #     }
-//! #     fn format(&self) -> Option<Fourcc> {
-//! #         unimplemented!()
-//! #     }
-//! # }
-//! #
-//! # struct FakeFrame;
-//! #
-//! # impl Frame for FakeFrame {
-//! #     type Error = std::convert::Infallible;
-//! #     type TextureId = FakeTexture;
-//! #
-//! #     fn id(&self) -> usize { unimplemented!() }
-//! #     fn clear(&mut self, _: Color32F, _: &[Rectangle<i32, Physical>]) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn draw_solid(
-//! #         &mut self,
-//! #         _dst: Rectangle<i32, Physical>,
-//! #         _damage: &[Rectangle<i32, Physical>],
-//! #         _color: Color32F,
-//! #     ) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn render_texture_from_to(
-//! #         &mut self,
-//! #         _: &Self::TextureId,
-//! #         _: Rectangle<f64, Buffer>,
-//! #         _: Rectangle<i32, Physical>,
-//! #         _: &[Rectangle<i32, Physical>],
-//! #         _: &[Rectangle<i32, Physical>],
-//! #         _: Transform,
-//! #         _: f32,
-//! #     ) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn transformation(&self) -> Transform {
-//! #         unimplemented!()
-//! #     }
-//! #     fn finish(self) -> Result<SyncPoint, Self::Error> { unimplemented!() }
-//! #     fn wait(&mut self, sync: &SyncPoint) -> Result<(), Self::Error> { unimplemented!() }
-//! # }
-//! #
-//! # #[derive(Debug)]
-//! # struct FakeRenderer;
-//! #
-//! # impl Renderer for FakeRenderer {
-//! #     type Error = std::convert::Infallible;
-//! #     type TextureId = FakeTexture;
-//! #     type Frame<'a> = FakeFrame;
-//! #
-//! #     fn id(&self) -> usize {
-//! #         unimplemented!()
-//! #     }
-//! #     fn downscale_filter(&mut self, _: TextureFilter) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn upscale_filter(&mut self, _: TextureFilter) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn set_debug_flags(&mut self, _: DebugFlags) {
-//! #         unimplemented!()
-//! #     }
-//! #     fn debug_flags(&self) -> DebugFlags {
-//! #         unimplemented!()
-//! #     }
-//! #     fn render(&mut self, _: Size<i32, Physical>, _: Transform) -> Result<Self::Frame<'_>, Self::Error>
-//! #     {
-//! #         unimplemented!()
-//! #     }
-//! #     fn wait(&mut self, sync: &SyncPoint) -> Result<(), Self::Error> { unimplemented!() }
-//! # }
-//! #
-//! # impl ImportMem for FakeRenderer {
-//! #     fn import_memory(
-//! #         &mut self,
-//! #         _: &[u8],
-//! #         _: Fourcc,
-//! #         _: Size<i32, Buffer>,
-//! #         _: bool,
-//! #     ) -> Result<Self::TextureId, Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn update_memory(
-//! #         &mut self,
-//! #         _: &Self::TextureId,
-//! #         _: &[u8],
-//! #         _: Rectangle<i32, Buffer>,
-//! #     ) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn mem_formats(&self) -> Box<dyn Iterator<Item=Fourcc>> {
-//! #         unimplemented!()
-//! #     }
-//! # }
 //! use smithay::{
 //!     backend::{
 //!         allocator::Fourcc,
@@ -152,7 +47,8 @@
 //!
 //! const WIDTH: i32 = 10;
 //! const HEIGHT: i32 = 10;
-//! # let mut renderer = FakeRenderer;
+//! # let mut renderer = DummyRenderer;
+//! # let mut framebuffer = DummyFramebuffer;
 //! # let buffer_age = 0;
 //!
 //! // Initialize a new damage tracker for a static output
@@ -188,6 +84,7 @@
 //!     damage_tracker
 //!         .render_output(
 //!             &mut renderer,
+//!             &mut framebuffer,
 //!             buffer_age,
 //!             &[render_element],
 //!             [0.8, 0.8, 0.9, 1.0],
@@ -215,7 +112,7 @@ use super::{
     element::{Element, Id, RenderElement, RenderElementState, RenderElementStates},
     sync::SyncPoint,
     utils::CommitCounter,
-    Bind, Color32F,
+    Color32F,
 };
 
 use super::{Renderer, Texture};
@@ -411,35 +308,15 @@ impl OutputDamageTracker {
         &self.mode
     }
 
-    /// Render this output with the provided [`Renderer`] in the provided buffer
-    ///
-    /// - `elements` for this output in front-to-back order
-    #[instrument(level = "trace", parent = &self.span, skip(renderer, elements, buffer))]
-    #[profiling::function]
-    pub fn render_output_with<E, R, B>(
-        &mut self,
-        renderer: &mut R,
-        buffer: B,
-        age: usize,
-        elements: &[E],
-        clear_color: Color32F,
-    ) -> Result<RenderOutputResult<'_>, Error<R::Error>>
-    where
-        E: RenderElement<R>,
-        R: Renderer + Bind<B>,
-        <R as Renderer>::TextureId: Texture,
-    {
-        self.render_output_internal(renderer, age, elements, clear_color, |r| r.bind(buffer))
-    }
-
     /// Render this output with the provided [`Renderer`]
     ///
     /// - `elements` for this output in front-to-back order
-    #[instrument(level = "trace", parent = &self.span, skip(renderer, elements, clear_color))]
+    #[instrument(level = "trace", parent = &self.span, skip(renderer, framebuffer, elements, clear_color))]
     #[profiling::function]
     pub fn render_output<E, R>(
         &mut self,
         renderer: &mut R,
+        framebuffer: &mut R::Framebuffer<'_>,
         age: usize,
         elements: &[E],
         clear_color: impl Into<Color32F>,
@@ -447,9 +324,136 @@ impl OutputDamageTracker {
     where
         E: RenderElement<R>,
         R: Renderer,
-        <R as Renderer>::TextureId: Texture,
+        R::TextureId: Texture,
     {
-        self.render_output_internal(renderer, age, elements, clear_color.into(), |_| Ok(()))
+        let clear_color = clear_color.into();
+        let (output_size, output_scale, output_transform) =
+            std::convert::TryInto::<(Size<i32, Physical>, Scale<f64>, Transform)>::try_into(&self.mode)?;
+
+        // Output transform is specified in surface-rotation, so inversion gives us the
+        // render transform for the output itself.
+        let output_transform = output_transform.invert();
+
+        // We have to apply to output transform to the output size so that the intersection
+        // tests in damage_output_internal produces the correct results and do not crop
+        // damage with the wrong size
+        let output_geo = Rectangle::from_size(output_transform.transform_size(output_size));
+
+        // This will hold all the damage we need for this rendering step
+        let mut render_elements: Vec<&E> = Vec::with_capacity(elements.len());
+        let states = self.damage_output_internal(
+            age,
+            elements,
+            output_scale,
+            output_transform,
+            output_geo,
+            Some(clear_color),
+            &mut render_elements,
+        );
+
+        if self.damage.is_empty() {
+            trace!("no damage, skipping rendering");
+            return Ok(RenderOutputResult::skipped(states));
+        }
+
+        trace!(
+            "rendering with damage {:?} and opaque regions {:?}",
+            self.damage,
+            self.opaque_regions
+        );
+
+        let render_res = (|| {
+            // we have to take the element damage to be able to move it around
+            let mut element_damage = std::mem::take(&mut self.element_damage);
+            let mut element_opaque_regions = std::mem::take(&mut self.element_opaque_regions);
+            let mut frame = renderer.render(framebuffer, output_size, output_transform)?;
+
+            element_damage.clear();
+            element_damage.extend_from_slice(&self.damage);
+            element_damage =
+                Rectangle::subtract_rects_many_in_place(element_damage, self.opaque_regions.iter().copied());
+
+            trace!("clearing damage {:?}", element_damage);
+            frame.clear(clear_color, &element_damage)?;
+
+            for (z_index, element) in render_elements.iter().rev().enumerate() {
+                let element_id = element.id();
+                let element_geometry = element.geometry(output_scale);
+
+                element_damage.clear();
+                element_damage.extend(
+                    self.damage
+                        .iter()
+                        .filter_map(|d| d.intersection(element_geometry)),
+                );
+
+                let element_opaque_regions_range =
+                    self.opaque_regions_index.iter().rev().nth(z_index).unwrap();
+                element_damage = Rectangle::subtract_rects_many_in_place(
+                    element_damage,
+                    self.opaque_regions[..element_opaque_regions_range.start]
+                        .iter()
+                        .copied(),
+                );
+                element_damage.iter_mut().for_each(|d| {
+                    d.loc -= element_geometry.loc;
+                });
+
+                if element_damage.is_empty() {
+                    trace!(
+                        "skipping rendering element {:?} with geometry {:?}, no damage",
+                        element_id,
+                        element_geometry
+                    );
+                    continue;
+                }
+
+                element_opaque_regions.clear();
+                element_opaque_regions.extend(
+                    self.opaque_regions[element_opaque_regions_range.start..element_opaque_regions_range.end]
+                        .iter()
+                        .copied()
+                        .map(|mut rect| {
+                            rect.loc -= element_geometry.loc;
+                            rect
+                        }),
+                );
+
+                trace!(
+                    "rendering element {:?} with geometry {:?} and damage {:?}",
+                    element_id,
+                    element_geometry,
+                    element_damage,
+                );
+
+                element.draw(
+                    &mut frame,
+                    element.src(),
+                    element_geometry,
+                    &element_damage,
+                    &element_opaque_regions,
+                )?;
+            }
+
+            // return the element damage so that we can re-use the allocation
+            std::mem::swap(&mut self.element_damage, &mut element_damage);
+            std::mem::swap(&mut self.element_opaque_regions, &mut element_opaque_regions);
+            frame.finish()
+        })();
+
+        match render_res {
+            Ok(sync) => Ok(RenderOutputResult {
+                sync,
+                damage: Some(&self.damage),
+                states,
+            }),
+            Err(err) => {
+                // if the rendering errors on us, we need to be prepared, that this whole buffer was partially updated and thus now unusable.
+                // thus clean our old states before returning
+                self.last_state = Default::default();
+                Err(Error::Rendering(err))
+            }
+        }
     }
 
     /// Damage this output and return the damage without actually rendering the difference
@@ -781,150 +785,4 @@ impl OutputDamageTracker {
 
         element_render_states
     }
-
-    #[profiling::function]
-    fn render_output_internal<'a, 'e, E, R, F>(
-        &'a mut self,
-        renderer: &mut R,
-        age: usize,
-        elements: &'e [E],
-        clear_color: Color32F,
-        pre_render: F,
-    ) -> Result<RenderOutputResult<'a>, Error<R::Error>>
-    where
-        E: RenderElement<R>,
-        R: Renderer,
-        <R as Renderer>::TextureId: Texture,
-        F: FnOnce(&mut R) -> Result<(), <R as Renderer>::Error>,
-    {
-        let (output_size, output_scale, output_transform) =
-            std::convert::TryInto::<(Size<i32, Physical>, Scale<f64>, Transform)>::try_into(&self.mode)?;
-
-        // Output transform is specified in surface-rotation, so inversion gives us the
-        // render transform for the output itself.
-        let output_transform = output_transform.invert();
-
-        // We have to apply to output transform to the output size so that the intersection
-        // tests in damage_output_internal produces the correct results and do not crop
-        // damage with the wrong size
-        let output_geo = Rectangle::from_size(output_transform.transform_size(output_size));
-
-        // This will hold all the damage we need for this rendering step
-        let mut render_elements: Vec<&E> = Vec::with_capacity(elements.len());
-        let states = self.damage_output_internal(
-            age,
-            elements,
-            output_scale,
-            output_transform,
-            output_geo,
-            Some(clear_color),
-            &mut render_elements,
-        );
-
-        if self.damage.is_empty() {
-            trace!("no damage, skipping rendering");
-            return Ok(RenderOutputResult::skipped(states));
-        }
-
-        trace!(
-            "rendering with damage {:?} and opaque regions {:?}",
-            self.damage,
-            self.opaque_regions
-        );
-
-        pre_render(renderer).map_err(Error::Rendering)?;
-
-        let render_res = (|| {
-            // we have to take the element damage to be able to move it around
-            let mut element_damage = std::mem::take(&mut self.element_damage);
-            let mut element_opaque_regions = std::mem::take(&mut self.element_opaque_regions);
-            let mut frame = renderer.render(output_size, output_transform)?;
-
-            element_damage.clear();
-            element_damage.extend_from_slice(&self.damage);
-            element_damage =
-                Rectangle::subtract_rects_many_in_place(element_damage, self.opaque_regions.iter().copied());
-
-            trace!("clearing damage {:?}", element_damage);
-            frame.clear(clear_color, &element_damage)?;
-
-            for (z_index, element) in render_elements.iter().rev().enumerate() {
-                let element_id = element.id();
-                let element_geometry = element.geometry(output_scale);
-
-                element_damage.clear();
-                element_damage.extend(
-                    self.damage
-                        .iter()
-                        .filter_map(|d| d.intersection(element_geometry)),
-                );
-
-                let element_opaque_regions_range =
-                    self.opaque_regions_index.iter().rev().nth(z_index).unwrap();
-                element_damage = Rectangle::subtract_rects_many_in_place(
-                    element_damage,
-                    self.opaque_regions[..element_opaque_regions_range.start]
-                        .iter()
-                        .copied(),
-                );
-                element_damage.iter_mut().for_each(|d| {
-                    d.loc -= element_geometry.loc;
-                });
-
-                if element_damage.is_empty() {
-                    trace!(
-                        "skipping rendering element {:?} with geometry {:?}, no damage",
-                        element_id,
-                        element_geometry
-                    );
-                    continue;
-                }
-
-                element_opaque_regions.clear();
-                element_opaque_regions.extend(
-                    self.opaque_regions[element_opaque_regions_range.start..element_opaque_regions_range.end]
-                        .iter()
-                        .copied()
-                        .map(|mut rect| {
-                            rect.loc -= element_geometry.loc;
-                            rect
-                        }),
-                );
-
-                trace!(
-                    "rendering element {:?} with geometry {:?} and damage {:?}",
-                    element_id,
-                    element_geometry,
-                    element_damage,
-                );
-
-                element.draw(
-                    &mut frame,
-                    element.src(),
-                    element_geometry,
-                    &element_damage,
-                    &element_opaque_regions,
-                )?;
-            }
-
-            // return the element damage so that we can re-use the allocation
-            std::mem::swap(&mut self.element_damage, &mut element_damage);
-            std::mem::swap(&mut self.element_opaque_regions, &mut element_opaque_regions);
-            frame.finish()
-        })();
-
-        match render_res {
-            Ok(sync) => Ok(RenderOutputResult {
-                sync,
-                damage: Some(&self.damage),
-                states,
-            }),
-            Err(err) => {
-                // if the rendering errors on us, we need to be prepared, that this whole buffer was partially updated and thus now unusable.
-                // thus clean our old states before returning
-                self.last_state = Default::default();
-                Err(Error::Rendering(err))
-            }
-        }
-    }
 }
diff --git a/src/backend/renderer/element/memory.rs b/src/backend/renderer/element/memory.rs
index 85a48ba2d4c0..af9727065762 100644
--- a/src/backend/renderer/element/memory.rs
+++ b/src/backend/renderer/element/memory.rs
@@ -6,7 +6,7 @@
 //! in the smithay rendering pipeline. As software-rendered elements eventually have to
 //! upload to the GPU for rendering damage tracking is a crucial part. The [`MemoryRenderBuffer`]
 //! allows for efficient damage tracking by providing a [`RenderContext`] which accumulates the
-//! software-rendering damage. It automatically uploads the damaged parts to a [`Renderer::TextureId`]
+//! software-rendering damage. It automatically uploads the damaged parts to a [`RendererSuper::TextureId`](crate::backend::renderer::RendererSuper::TextureId)
 //! during rendering.
 //!
 //! # Why **not** to use this implementation
@@ -23,114 +23,9 @@
 //!
 //! ```no_run
 //! # use smithay::{
-//! #     backend::renderer::{Color32F, DebugFlags, Frame, ImportMem, Renderer, Texture, TextureFilter, sync::SyncPoint},
+//! #     backend::renderer::{Color32F, DebugFlags, Frame, ImportMem, Renderer, Texture, TextureFilter, sync::SyncPoint, test::{DummyRenderer, DummyFramebuffer}},
 //! #     utils::{Buffer, Physical},
 //! # };
-//! #
-//! # #[derive(Clone, Debug)]
-//! # struct FakeTexture;
-//! #
-//! # impl Texture for FakeTexture {
-//! #     fn width(&self) -> u32 {
-//! #         unimplemented!()
-//! #     }
-//! #     fn height(&self) -> u32 {
-//! #         unimplemented!()
-//! #     }
-//! #     fn format(&self) -> Option<Fourcc> {
-//! #         unimplemented!()
-//! #     }
-//! # }
-//! #
-//! # struct FakeFrame;
-//! #
-//! # impl Frame for FakeFrame {
-//! #     type Error = std::convert::Infallible;
-//! #     type TextureId = FakeTexture;
-//! #
-//! #     fn id(&self) -> usize { unimplemented!() }
-//! #     fn clear(&mut self, _: Color32F, _: &[Rectangle<i32, Physical>]) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn draw_solid(
-//! #         &mut self,
-//! #         _dst: Rectangle<i32, Physical>,
-//! #         _damage: &[Rectangle<i32, Physical>],
-//! #         _color: Color32F,
-//! #     ) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn render_texture_from_to(
-//! #         &mut self,
-//! #         _: &Self::TextureId,
-//! #         _: Rectangle<f64, Buffer>,
-//! #         _: Rectangle<i32, Physical>,
-//! #         _: &[Rectangle<i32, Physical>],
-//! #         _: &[Rectangle<i32, Physical>],
-//! #         _: Transform,
-//! #         _: f32,
-//! #     ) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn transformation(&self) -> Transform {
-//! #         unimplemented!()
-//! #     }
-//! #     fn finish(self) -> Result<SyncPoint, Self::Error> { unimplemented!() }
-//! #     fn wait(&mut self, sync: &SyncPoint) -> Result<(), Self::Error> { unimplemented!() }
-//! # }
-//! #
-//! # #[derive(Debug)]
-//! # struct FakeRenderer;
-//! #
-//! # impl Renderer for FakeRenderer {
-//! #     type Error = std::convert::Infallible;
-//! #     type TextureId = FakeTexture;
-//! #     type Frame<'a> = FakeFrame;
-//! #
-//! #     fn id(&self) -> usize {
-//! #         unimplemented!()
-//! #     }
-//! #     fn downscale_filter(&mut self, _: TextureFilter) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn upscale_filter(&mut self, _: TextureFilter) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn set_debug_flags(&mut self, _: DebugFlags) {
-//! #         unimplemented!()
-//! #     }
-//! #     fn debug_flags(&self) -> DebugFlags {
-//! #         unimplemented!()
-//! #     }
-//! #     fn render(&mut self, _: Size<i32, Physical>, _: Transform) -> Result<Self::Frame<'_>, Self::Error>
-//! #     {
-//! #         unimplemented!()
-//! #     }
-//! #     fn wait(&mut self, sync: &SyncPoint) -> Result<(), Self::Error> { unimplemented!() }
-//! # }
-//! #
-//! # impl ImportMem for FakeRenderer {
-//! #     fn import_memory(
-//! #         &mut self,
-//! #         _: &[u8],
-//! #         _: Fourcc,
-//! #         _: Size<i32, Buffer>,
-//! #         _: bool,
-//! #     ) -> Result<Self::TextureId, Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn update_memory(
-//! #         &mut self,
-//! #         _: &Self::TextureId,
-//! #         _: &[u8],
-//! #         _: Rectangle<i32, Buffer>,
-//! #     ) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn mem_formats(&self) -> Box<dyn Iterator<Item=Fourcc>> {
-//! #         unimplemented!()
-//! #     }
-//! # }
 //! use std::time::{Duration, Instant};
 //!
 //! use smithay::{
@@ -176,7 +71,8 @@
 //!
 //! // Initialize a static damage tracker
 //! let mut damage_tracker = OutputDamageTracker::new((800, 600), 1.0, Transform::Normal);
-//! # let mut renderer = FakeRenderer;
+//! # let mut renderer = DummyRenderer;
+//! # let mut framebuffer = DummyFramebuffer;
 //!
 //! let mut last_update = Instant::now();
 //!
@@ -202,7 +98,7 @@
 //!
 //!     // Render the element(s)
 //!     damage_tracker
-//!         .render_output(&mut renderer, 0, &[&render_element], [0.8, 0.8, 0.9, 1.0])
+//!         .render_output(&mut renderer, &mut framebuffer, 0, &[&render_element], [0.8, 0.8, 0.9, 1.0])
 //!         .expect("failed to render output");
 //! }
 //! ```
@@ -407,15 +303,12 @@ impl MemoryRenderBufferInner {
 
     #[instrument(level = "trace", skip(renderer))]
     #[profiling::function]
-    fn import_texture<R>(
-        &mut self,
-        renderer: &mut R,
-    ) -> Result<<R as Renderer>::TextureId, <R as Renderer>::Error>
+    fn import_texture<R>(&mut self, renderer: &mut R) -> Result<R::TextureId, R::Error>
     where
         R: Renderer + ImportMem,
-        <R as Renderer>::TextureId: Send + Clone + 'static,
+        R::TextureId: Send + Clone + 'static,
     {
-        let texture_id = (TypeId::of::<<R as Renderer>::TextureId>(), renderer.id());
+        let texture_id = (TypeId::of::<R::TextureId>(), renderer.id());
         let current_commit = self.damage_bag.current_commit();
         let last_commit = self.renderer_seen.get(&texture_id).copied();
         let buffer_damage = self
@@ -592,10 +485,10 @@ impl<R: Renderer> MemoryRenderBufferRenderElement<R> {
         src: Option<Rectangle<f64, Logical>>,
         size: Option<Size<i32, Logical>>,
         kind: Kind,
-    ) -> Result<Self, <R as Renderer>::Error>
+    ) -> Result<Self, R::Error>
     where
         R: ImportMem,
-        <R as Renderer>::TextureId: Send + Clone + 'static,
+        R::TextureId: Send + Clone + 'static,
     {
         let mut inner = buffer.inner.lock().unwrap();
         let texture = inner.import_texture(renderer)?;
@@ -737,18 +630,18 @@ impl<R: Renderer> Element for MemoryRenderBufferRenderElement<R> {
 impl<R> RenderElement<R> for MemoryRenderBufferRenderElement<R>
 where
     R: Renderer + ImportMem,
-    <R as Renderer>::TextureId: 'static,
+    R::TextureId: 'static,
 {
     #[instrument(level = "trace", skip(self, frame))]
     #[profiling::function]
-    fn draw<'a>(
+    fn draw(
         &self,
-        frame: &mut <R as Renderer>::Frame<'a>,
+        frame: &mut R::Frame<'_, '_>,
         src: Rectangle<f64, Buffer>,
         dst: Rectangle<i32, Physical>,
         damage: &[Rectangle<i32, Physical>],
         opaque_regions: &[Rectangle<i32, Physical>],
-    ) -> Result<(), <R as Renderer>::Error> {
+    ) -> Result<(), R::Error> {
         frame.render_texture_from_to(
             &self.texture,
             src,
diff --git a/src/backend/renderer/element/mod.rs b/src/backend/renderer/element/mod.rs
index 2a378b2002ee..bad491936ed1 100644
--- a/src/backend/renderer/element/mod.rs
+++ b/src/backend/renderer/element/mod.rs
@@ -385,7 +385,7 @@ pub trait RenderElement<R: Renderer>: Element {
     /// Draw this element
     fn draw(
         &self,
-        frame: &mut <R as Renderer>::Frame<'_>,
+        frame: &mut R::Frame<'_, '_>,
         src: Rectangle<f64, BufferCoords>,
         dst: Rectangle<i32, Physical>,
         damage: &[Rectangle<i32, Physical>],
@@ -474,7 +474,7 @@ where
 
     fn draw(
         &self,
-        frame: &mut <R as Renderer>::Frame<'_>,
+        frame: &mut R::Frame<'_, '_>,
         src: Rectangle<f64, BufferCoords>,
         dst: Rectangle<i32, Physical>,
         damage: &[Rectangle<i32, Physical>],
@@ -580,7 +580,7 @@ macro_rules! render_elements_internal {
         $vis enum $name<$lt, $renderer>
         where
             $renderer: $crate::backend::renderer::Renderer,
-            <$renderer as $crate::backend::renderer::Renderer>::TextureId: 'static,
+            <$renderer as $crate::backend::renderer::RendererSuper>::TextureId: 'static,
         {
             $(
                 $(
@@ -597,7 +597,7 @@ macro_rules! render_elements_internal {
         $vis enum $name<$lt, $renderer, $($custom),+>
         where
             $renderer: $crate::backend::renderer::Renderer,
-            <$renderer as $crate::backend::renderer::Renderer>::TextureId: 'static,
+            <$renderer as $crate::backend::renderer::RendererSuper>::TextureId: 'static,
             $(
                 $custom: $crate::backend::renderer::element::RenderElement<$renderer>,
             )+
@@ -758,18 +758,18 @@ macro_rules! render_elements_internal {
     (@draw <$renderer:ty>; $($(#[$meta:meta])* $body:ident=$field:ty $(as <$other_renderer:ty>)?),* $(,)?) => {
         fn draw(
             &self,
-            frame: &mut <$renderer as $crate::backend::renderer::Renderer>::Frame<'_>,
+            frame: &mut <$renderer as $crate::backend::renderer::RendererSuper>::Frame<'_, '_>,
             src: $crate::utils::Rectangle<f64, $crate::utils::Buffer>,
             dst: $crate::utils::Rectangle<i32, $crate::utils::Physical>,
             damage: &[$crate::utils::Rectangle<i32, $crate::utils::Physical>],
             opaque_regions: &[$crate::utils::Rectangle<i32, $crate::utils::Physical>],
-        ) -> Result<(), <$renderer as $crate::backend::renderer::Renderer>::Error>
+        ) -> Result<(), <$renderer as $crate::backend::renderer::RendererSuper>::Error>
         where
         $(
             $(
                 $renderer: std::convert::AsMut<$other_renderer>,
-                <$renderer as $crate::backend::renderer::Renderer>::Frame: std::convert::AsMut<<$other_renderer as $crate::backend::renderer::Renderer>::Frame>,
-                <$other_renderer as $crate::backend::renderer::Renderer>::Error: Into<<$renderer as $crate::backend::renderer::Renderer>::Error>,
+                <$renderer as $crate::backend::renderer::RendererSuper>::Frame: std::convert::AsMut<<$other_renderer as $crate::backend::renderer::RendererSuper>::Frame>,
+                <$other_renderer as $crate::backend::renderer::RendererSuper>::Error: Into<<$renderer as $crate::backend::renderer::RendererSuper>::Error>,
             )*
         )*
         {
@@ -803,12 +803,12 @@ macro_rules! render_elements_internal {
     (@draw $renderer:ty; $($(#[$meta:meta])* $body:ident=$field:ty $(as <$other_renderer:ty>)?),* $(,)?) => {
         fn draw(
             &self,
-            frame: &mut <$renderer as $crate::backend::renderer::Renderer>::Frame<'_>,
+            frame: &mut <$renderer as $crate::backend::renderer::RendererSuper>::Frame<'_, '_>,
             src: $crate::utils::Rectangle<f64, $crate::utils::Buffer>,
             dst: $crate::utils::Rectangle<i32, $crate::utils::Physical>,
             damage: &[$crate::utils::Rectangle<i32, $crate::utils::Physical>],
             opaque_regions: &[$crate::utils::Rectangle<i32, $crate::utils::Physical>],
-        ) -> Result<(), <$renderer as $crate::backend::renderer::Renderer>::Error>
+        ) -> Result<(), <$renderer as $crate::backend::renderer::RendererSuper>::Error>
         {
             match self {
                 $(
@@ -842,7 +842,7 @@ macro_rules! render_elements_internal {
         impl<$renderer> $crate::backend::renderer::element::Element for $name<$renderer>
         where
             $renderer: $crate::backend::renderer::Renderer,
-            <$renderer as $crate::backend::renderer::Renderer>::TextureId: 'static,
+            <$renderer as $crate::backend::renderer::RendererSuper>::TextureId: 'static,
             $($($target: $bound $(+ $additional_bound)*),+)?
         {
             $crate::render_elements_internal!(@body $($tail)*);
@@ -850,7 +850,7 @@ macro_rules! render_elements_internal {
         impl<$renderer> $crate::backend::renderer::element::RenderElement<$renderer> for $name<$renderer>
         where
             $renderer: $crate::backend::renderer::Renderer,
-            <$renderer as $crate::backend::renderer::Renderer>::TextureId: 'static,
+            <$renderer as $crate::backend::renderer::RendererSuper>::TextureId: 'static,
             $($($target: $bound $(+ $additional_bound)*),+)?
         {
             $crate::render_elements_internal!(@draw <$renderer>; $($tail)*);
@@ -860,7 +860,7 @@ macro_rules! render_elements_internal {
         impl<$lt, $renderer> $crate::backend::renderer::element::Element for $name<$lt, $renderer>
         where
             $renderer: $crate::backend::renderer::Renderer,
-            <$renderer as $crate::backend::renderer::Renderer>::TextureId: 'static,
+            <$renderer as $crate::backend::renderer::RendererSuper>::TextureId: 'static,
             $($($target: $bound $(+ $additional_bound)*),+)?
         {
             $crate::render_elements_internal!(@body $($tail)*);
@@ -868,7 +868,7 @@ macro_rules! render_elements_internal {
         impl<$lt, $renderer> $crate::backend::renderer::element::RenderElement<$renderer> for $name<$lt, $renderer>
         where
             $renderer: $crate::backend::renderer::Renderer,
-            <$renderer as $crate::backend::renderer::Renderer>::TextureId: 'static,
+            <$renderer as $crate::backend::renderer::RendererSuper>::TextureId: 'static,
             $($($target: $bound $(+ $additional_bound)*),+)?
         {
             $crate::render_elements_internal!(@draw <$renderer>; $($tail)*);
@@ -878,7 +878,7 @@ macro_rules! render_elements_internal {
         impl<$renderer, $($custom),+> $crate::backend::renderer::element::Element for $name<$renderer, $($custom),+>
         where
             $renderer: $crate::backend::renderer::Renderer,
-            <$renderer as $crate::backend::renderer::Renderer>::TextureId: 'static,
+            <$renderer as $crate::backend::renderer::RendererSuper>::TextureId: 'static,
             $(
                 $custom: $crate::backend::renderer::element::RenderElement<$renderer> + $crate::backend::renderer::element::Element,
             )+
@@ -889,7 +889,7 @@ macro_rules! render_elements_internal {
         impl<$renderer, $($custom),+> $crate::backend::renderer::element::RenderElement<$renderer> for $name<$renderer, $($custom),+>
         where
             $renderer: $crate::backend::renderer::Renderer,
-            <$renderer as $crate::backend::renderer::Renderer>::TextureId: 'static,
+            <$renderer as $crate::backend::renderer::RendererSuper>::TextureId: 'static,
             $(
                 $custom: $crate::backend::renderer::element::RenderElement<$renderer> + $crate::backend::renderer::element::Element,
             )+
@@ -902,7 +902,7 @@ macro_rules! render_elements_internal {
         impl<$lt, $renderer, $($custom),+> $crate::backend::renderer::element::Element for $name<$lt, $renderer, $($custom),+>
         where
             $renderer: $crate::backend::renderer::Renderer,
-            <$renderer as $crate::backend::renderer::Renderer>::TextureId: 'static,
+            <$renderer as $crate::backend::renderer::RendererSuper>::TextureId: 'static,
             $(
                 $custom: $crate::backend::renderer::element::RenderElement<$renderer> + $crate::backend::renderer::element::Element,
             )+
@@ -913,7 +913,7 @@ macro_rules! render_elements_internal {
         impl<$lt, $renderer, $($custom),+> $crate::backend::renderer::element::RenderElement<$renderer> for $name<$lt, $renderer, $($custom),+>
         where
             $renderer: $crate::backend::renderer::Renderer,
-            <$renderer as $crate::backend::renderer::Renderer>::TextureId: 'static,
+            <$renderer as $crate::backend::renderer::RendererSuper>::TextureId: 'static,
             $(
                 $custom: $crate::backend::renderer::element::RenderElement<$renderer> + $crate::backend::renderer::element::Element,
             )+
@@ -930,7 +930,7 @@ macro_rules! render_elements_internal {
         impl<$renderer> $crate::backend::renderer::element::RenderElement<$renderer> for $name
         where
             $renderer: $crate::backend::renderer::Renderer,
-            <$renderer as $crate::backend::renderer::Renderer>::TextureId: 'static,
+            <$renderer as $crate::backend::renderer::RendererSuper>::TextureId: 'static,
         {
             $crate::render_elements_internal!(@draw <$renderer>; $($tail)*);
         }
@@ -1146,12 +1146,12 @@ macro_rules! render_elements_internal {
 /// # impl<R: Renderer> RenderElement<R> for MyRenderElement1 {
 /// #     fn draw(
 /// #         &self,
-/// #         _frame: &mut <R as Renderer>::Frame<'_>,
+/// #         _frame: &mut R::Frame<'_, '_>,
 /// #         _src: Rectangle<f64, Buffer>,
 /// #         _dst: Rectangle<i32, Physical>,
 /// #         _damage: &[Rectangle<i32, Physical>],
 /// #         _opaque_regions: &[Rectangle<i32, Physical>],
-/// #     ) -> Result<(), <R as Renderer>::Error> {
+/// #     ) -> Result<(), R::Error> {
 /// #         unimplemented!()
 /// #     }
 /// # }
@@ -1175,14 +1175,14 @@ macro_rules! render_elements_internal {
 /// # }
 /// #
 /// # impl<R: Renderer> RenderElement<R> for MyRenderElement2 {
-/// #     fn draw<'a>(
+/// #     fn draw(
 /// #         &self,
-/// #         _frame: &mut <R as Renderer>::Frame<'a>,
+/// #         _frame: &mut R::Frame<'_, '_>,
 /// #         _src: Rectangle<f64, Buffer>,
 /// #         _dst: Rectangle<i32, Physical>,
 /// #         _damage: &[Rectangle<i32, Physical>],
 /// #         _opaque_regions: &[Rectangle<i32, Physical>],
-/// #     ) -> Result<(), <R as Renderer>::Error> {
+/// #     ) -> Result<(), R::Error> {
 /// #         unimplemented!()
 /// #     }
 /// # }
@@ -1251,97 +1251,15 @@ macro_rules! render_elements_internal {
 /// # use smithay::{
 /// #     backend::{
 /// #         allocator::Fourcc,
-/// #         renderer::{Color32F, DebugFlags, Frame, Renderer, Texture, TextureFilter, sync::SyncPoint},
+/// #         renderer::{Color32F, DebugFlags, Frame, Renderer, Texture, TextureFilter, sync::SyncPoint, gles::{GlesRenderer, GlesTexture}},
 /// #     },
 /// #     utils::{Buffer, Physical, Rectangle, Size, Transform},
 /// # };
-/// #
-/// # #[derive(Clone, Debug)]
-/// # struct MyRendererTextureId;
-/// #
-/// # impl Texture for MyRendererTextureId {
-/// #     fn width(&self) -> u32 {
-/// #         unimplemented!()
-/// #     }
-/// #     fn height(&self) -> u32 {
-/// #         unimplemented!()
-/// #     }
-/// #     fn format(&self) -> Option<Fourcc> {
-/// #         unimplemented!()
-/// #     }
-/// # }
-/// #
-/// # struct MyRendererFrame;
-/// #
-/// # impl Frame for MyRendererFrame {
-/// #     type Error = std::convert::Infallible;
-/// #     type TextureId = MyRendererTextureId;
-/// #
-/// #     fn id(&self) -> usize { unimplemented!() }
-/// #     fn clear(&mut self, _: Color32F, _: &[Rectangle<i32, Physical>]) -> Result<(), Self::Error> {
-/// #         unimplemented!()
-/// #     }
-/// #     fn draw_solid(
-/// #         &mut self,
-/// #         _dst: Rectangle<i32, Physical>,
-/// #         _damage: &[Rectangle<i32, Physical>],
-/// #         _color: Color32F,
-/// #     ) -> Result<(), Self::Error> {
-/// #         unimplemented!()
-/// #     }
-/// #     fn render_texture_from_to(
-/// #         &mut self,
-/// #         _: &Self::TextureId,
-/// #         _: Rectangle<f64, Buffer>,
-/// #         _: Rectangle<i32, Physical>,
-/// #         _: &[Rectangle<i32, Physical>],
-/// #         _: &[Rectangle<i32, Physical>],
-/// #         _: Transform,
-/// #         _: f32,
-/// #     ) -> Result<(), Self::Error> {
-/// #         unimplemented!()
-/// #     }
-/// #     fn transformation(&self) -> Transform {
-/// #         unimplemented!()
-/// #     }
-/// #     fn finish(self) -> Result<SyncPoint, Self::Error> { unimplemented!() }
-/// #     fn wait(&mut self, sync: &SyncPoint) -> Result<(), Self::Error> { unimplemented!() }
-/// # }
-/// #
-/// # #[derive(Debug)]
-/// # struct MyRenderer;
-/// #
-/// # impl Renderer for MyRenderer {
-/// #     type Error = std::convert::Infallible;
-/// #     type TextureId = MyRendererTextureId;
-/// #     type Frame<'a> = MyRendererFrame;
-/// #
-/// #     fn id(&self) -> usize {
-/// #         unimplemented!()
-/// #     }
-/// #     fn downscale_filter(&mut self, _: TextureFilter) -> Result<(), Self::Error> {
-/// #         unimplemented!()
-/// #     }
-/// #     fn upscale_filter(&mut self, _: TextureFilter) -> Result<(), Self::Error> {
-/// #         unimplemented!()
-/// #     }
-/// #     fn set_debug_flags(&mut self, flags: DebugFlags) {
-/// #         unimplemented!()
-/// #     }
-/// #     fn debug_flags(&self) -> DebugFlags {
-/// #         unimplemented!()
-/// #     }
-/// #     fn render(&mut self, _: Size<i32, Physical>, _: Transform) -> Result<Self::Frame<'_>, Self::Error>
-/// #     {
-/// #         unimplemented!()
-/// #     }
-/// #     fn wait(&mut self, sync: &SyncPoint) -> Result<(), Self::Error> { unimplemented!() }
-/// # }
 /// use smithay::backend::renderer::element::{render_elements, texture::TextureRenderElement};
 ///
 /// render_elements! {
-///     MyRenderElements<=MyRenderer>;
-///     Texture=TextureRenderElement<MyRendererTextureId>,
+///     MyRenderElements<=GlesRenderer>;
+///     Texture=TextureRenderElement<GlesTexture>,
 /// }
 /// ```
 #[macro_export]
@@ -1475,12 +1393,12 @@ where
 {
     fn draw(
         &self,
-        frame: &mut <R as Renderer>::Frame<'_>,
+        frame: &mut R::Frame<'_, '_>,
         src: Rectangle<f64, BufferCoords>,
         dst: Rectangle<i32, Physical>,
         damage: &[Rectangle<i32, Physical>],
         opaque_regions: &[Rectangle<i32, Physical>],
-    ) -> Result<(), <R as Renderer>::Error> {
+    ) -> Result<(), R::Error> {
         self.0.draw(frame, src, dst, damage, opaque_regions)
     }
 
diff --git a/src/backend/renderer/element/solid.rs b/src/backend/renderer/element/solid.rs
index 95b06b887e91..122507d223ef 100644
--- a/src/backend/renderer/element/solid.rs
+++ b/src/backend/renderer/element/solid.rs
@@ -6,115 +6,10 @@
 //! # use smithay::{
 //! #     backend::{
 //! #         allocator::Fourcc,
-//! #         renderer::{Color32F, DebugFlags, Frame, ImportMem, Renderer, Texture, TextureFilter, sync::SyncPoint},
+//! #         renderer::{Color32F, DebugFlags, Frame, ImportMem, Renderer, Texture, TextureFilter, sync::SyncPoint, test::{DummyRenderer, DummyFramebuffer}},
 //! #     },
 //! #     utils::{Buffer, Physical, Rectangle, Transform},
 //! # };
-//! # #[derive(Clone, Debug)]
-//! # struct FakeTexture;
-//! #
-//! # impl Texture for FakeTexture {
-//! #     fn width(&self) -> u32 {
-//! #         unimplemented!()
-//! #     }
-//! #     fn height(&self) -> u32 {
-//! #         unimplemented!()
-//! #     }
-//! #     fn format(&self) -> Option<Fourcc> {
-//! #         unimplemented!()
-//! #     }
-//! # }
-//! #
-//! # struct FakeFrame;
-//! #
-//! # impl Frame for FakeFrame {
-//! #     type Error = std::convert::Infallible;
-//! #     type TextureId = FakeTexture;
-//! #
-//! #     fn id(&self) -> usize {
-//! #         unimplemented!()
-//! #     }
-//! #     fn clear(&mut self, _: Color32F, _: &[Rectangle<i32, Physical>]) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn draw_solid(
-//! #         &mut self,
-//! #         _dst: Rectangle<i32, Physical>,
-//! #         _damage: &[Rectangle<i32, Physical>],
-//! #         _color: Color32F,
-//! #     ) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn render_texture_from_to(
-//! #         &mut self,
-//! #         _: &Self::TextureId,
-//! #         _: Rectangle<f64, Buffer>,
-//! #         _: Rectangle<i32, Physical>,
-//! #         _: &[Rectangle<i32, Physical>],
-//! #         _: &[Rectangle<i32, Physical>],
-//! #         _: Transform,
-//! #         _: f32,
-//! #     ) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn transformation(&self) -> Transform {
-//! #         unimplemented!()
-//! #     }
-//! #     fn finish(self) -> Result<SyncPoint, Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn wait(&mut self, sync: &SyncPoint) -> Result<(), Self::Error> { unimplemented!() }
-//! # }
-//! #
-//! # #[derive(Debug)]
-//! # struct FakeRenderer;
-//! #
-//! # impl Renderer for FakeRenderer {
-//! #     type Error = std::convert::Infallible;
-//! #     type TextureId = FakeTexture;
-//! #     type Frame<'a> = FakeFrame;
-//! #
-//! #     fn id(&self) -> usize {
-//! #         unimplemented!()
-//! #     }
-//! #     fn downscale_filter(&mut self, _: TextureFilter) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn upscale_filter(&mut self, _: TextureFilter) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn set_debug_flags(&mut self, _: DebugFlags) {
-//! #         unimplemented!()
-//! #     }
-//! #     fn debug_flags(&self) -> DebugFlags {
-//! #         unimplemented!()
-//! #     }
-//! #     fn render(&mut self, _: Size<i32, Physical>, _: Transform) -> Result<Self::Frame<'_>, Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn wait(&mut self, sync: &SyncPoint) -> Result<(), Self::Error> { unimplemented!() }
-//! # }
-//! #
-//! # impl ImportMem for FakeRenderer {
-//! #     fn import_memory(
-//! #         &mut self,
-//! #         _: &[u8],
-//! #         _: Fourcc,
-//! #         _: Size<i32, Buffer>,
-//! #         _: bool,
-//! #     ) -> Result<Self::TextureId, Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn update_memory(
-//! #         &mut self,
-//! #         _: &Self::TextureId,
-//! #         _: &[u8],
-//! #         _: Rectangle<i32, Buffer>,
-//! #     ) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn mem_formats(&self) -> Box<(dyn Iterator<Item=Fourcc> + 'static)> { unimplemented!() }
-//! # }
 //! use smithay::{
 //!     backend::renderer::{
 //!         damage::OutputDamageTracker,
@@ -135,7 +30,8 @@
 //!
 //! // Initialize a static damage tracked renderer
 //! let mut damage_tracker = OutputDamageTracker::new((800, 600), 1.0, Transform::Normal);
-//! # let mut renderer = FakeRenderer;
+//! # let mut renderer = DummyRenderer;
+//! # let mut framebuffer = DummyFramebuffer;
 //!
 //! loop {
 //!     // Create a render element from the buffer
@@ -144,7 +40,7 @@
 //!
 //!     // Render the element(s)
 //!     damage_tracker
-//!         .render_output(&mut renderer, 0, &[&render_element], [0.8, 0.8, 0.9, 1.0])
+//!         .render_output(&mut renderer, &mut framebuffer, 0, &[&render_element], [0.8, 0.8, 0.9, 1.0])
 //!         .expect("failed to render output");
 //! }
 //! ```
@@ -325,12 +221,12 @@ impl<R: Renderer> RenderElement<R> for SolidColorRenderElement {
     #[profiling::function]
     fn draw(
         &self,
-        frame: &mut <R as Renderer>::Frame<'_>,
+        frame: &mut R::Frame<'_, '_>,
         _src: Rectangle<f64, Buffer>,
         dst: Rectangle<i32, Physical>,
         damage: &[Rectangle<i32, Physical>],
         _opaque_regions: &[Rectangle<i32, Physical>],
-    ) -> Result<(), <R as Renderer>::Error> {
+    ) -> Result<(), R::Error> {
         frame.draw_solid(dst, damage, self.color)
     }
 
diff --git a/src/backend/renderer/element/surface.rs b/src/backend/renderer/element/surface.rs
index 8955e463d9f7..cca38f40be8b 100644
--- a/src/backend/renderer/element/surface.rs
+++ b/src/backend/renderer/element/surface.rs
@@ -24,169 +24,11 @@
 //! #     backend::allocator::{Fourcc, dmabuf::Dmabuf},
 //! #     backend::renderer::{
 //! #         Color32F, DebugFlags, Frame, ImportDma, ImportDmaWl, ImportMem, ImportMemWl, Renderer, Texture,
-//! #         TextureFilter, sync::SyncPoint,
+//! #         TextureFilter, sync::SyncPoint, test::{DummyRenderer, DummyFramebuffer},
 //! #     },
 //! #     utils::{Buffer, Physical},
 //! #     wayland::compositor::SurfaceData,
 //! # };
-//! #
-//! # #[derive(Clone, Debug)]
-//! # struct FakeTexture;
-//! #
-//! # impl Texture for FakeTexture {
-//! #     fn width(&self) -> u32 {
-//! #         unimplemented!()
-//! #     }
-//! #     fn height(&self) -> u32 {
-//! #         unimplemented!()
-//! #     }
-//! #     fn format(&self) -> Option<Fourcc> {
-//! #         unimplemented!()
-//! #     }
-//! # }
-//! #
-//! # struct FakeFrame;
-//! #
-//! # impl Frame for FakeFrame {
-//! #     type Error = std::convert::Infallible;
-//! #     type TextureId = FakeTexture;
-//! #
-//! #     fn id(&self) -> usize { unimplemented!() }
-//! #     fn clear(&mut self, _: Color32F, _: &[Rectangle<i32, Physical>]) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn draw_solid(
-//! #         &mut self,
-//! #         _dst: Rectangle<i32, Physical>,
-//! #         _damage: &[Rectangle<i32, Physical>],
-//! #         _color: Color32F,
-//! #     ) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn render_texture_from_to(
-//! #         &mut self,
-//! #         _: &Self::TextureId,
-//! #         _: Rectangle<f64, Buffer>,
-//! #         _: Rectangle<i32, Physical>,
-//! #         _: &[Rectangle<i32, Physical>],
-//! #         _: &[Rectangle<i32, Physical>],
-//! #         _: Transform,
-//! #         _: f32,
-//! #     ) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn transformation(&self) -> Transform {
-//! #         unimplemented!()
-//! #     }
-//! #     fn finish(self) -> Result<SyncPoint, Self::Error> { unimplemented!() }
-//! #     fn wait(&mut self, sync: &SyncPoint) -> Result<(), Self::Error> { unimplemented!() }
-//! # }
-//! #
-//! # #[derive(Debug)]
-//! # struct FakeRenderer;
-//! #
-//! # impl Renderer for FakeRenderer {
-//! #     type Error = std::convert::Infallible;
-//! #     type TextureId = FakeTexture;
-//! #     type Frame<'a> = FakeFrame;
-//! #
-//! #     fn id(&self) -> usize {
-//! #         unimplemented!()
-//! #     }
-//! #     fn downscale_filter(&mut self, _: TextureFilter) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn upscale_filter(&mut self, _: TextureFilter) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn set_debug_flags(&mut self, _: DebugFlags) {
-//! #         unimplemented!()
-//! #     }
-//! #     fn debug_flags(&self) -> DebugFlags {
-//! #         unimplemented!()
-//! #     }
-//! #     fn render(&mut self, _: Size<i32, Physical>, _: Transform) -> Result<Self::Frame<'_>, Self::Error>
-//! #     {
-//! #         unimplemented!()
-//! #     }
-//! #     fn wait(&mut self, sync: &SyncPoint) -> Result<(), Self::Error> { unimplemented!() }
-//! # }
-//! #
-//! # impl ImportMem for FakeRenderer {
-//! #     fn import_memory(
-//! #         &mut self,
-//! #         _: &[u8],
-//! #         _: Fourcc,
-//! #         _: Size<i32, Buffer>,
-//! #         _: bool,
-//! #     ) -> Result<Self::TextureId, Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn update_memory(
-//! #         &mut self,
-//! #         _: &Self::TextureId,
-//! #         _: &[u8],
-//! #         _: Rectangle<i32, Buffer>,
-//! #     ) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn mem_formats(&self) -> Box<dyn Iterator<Item=Fourcc>> {
-//! #         unimplemented!()
-//! #     }
-//! # }
-//! #
-//! # impl ImportMemWl for FakeRenderer {
-//! #     fn import_shm_buffer(
-//! #         &mut self,
-//! #         _buffer: &wayland_server::protocol::wl_buffer::WlBuffer,
-//! #         _surface: Option<&SurfaceData>,
-//! #         _damage: &[Rectangle<i32, Buffer>],
-//! #     ) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error> {
-//! #         unimplemented!()
-//! #     }
-//! # }
-//! # #[cfg(all(
-//! #     feature = "wayland_frontend",
-//! #     feature = "backend_egl",
-//! #     feature = "use_system_lib"
-//! # ))]
-//! # impl ImportEgl for FakeRenderer {
-//! #     fn bind_wl_display(
-//! #         &mut self,
-//! #         _display: &wayland_server::DisplayHandle,
-//! #     ) -> Result<(), egl::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #
-//! #     fn unbind_wl_display(&mut self) {
-//! #         unimplemented!()
-//! #     }
-//! #
-//! #     fn egl_reader(&self) -> Option<&EGLBufferReader> {
-//! #         unimplemented!()
-//! #     }
-//! #
-//! #     fn import_egl_buffer(
-//! #         &mut self,
-//! #         _buffer: &wayland_server::protocol::wl_buffer::WlBuffer,
-//! #         _surface: Option<&SurfaceData>,
-//! #         _damage: &[Rectangle<i32, Buffer>],
-//! #     ) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error> {
-//! #         unimplemented!()
-//! #     }
-//! # }
-//! #
-//! # impl ImportDma for FakeRenderer {
-//! #     fn import_dmabuf(
-//! #         &mut self,
-//! #         _dmabuf: &Dmabuf,
-//! #         _damage: Option<&[Rectangle<i32, Buffer>]>,
-//! #     ) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error> {
-//! #         unimplemented!()
-//! #     }
-//! # }
-//! #
-//! # impl ImportDmaWl for FakeRenderer {}
 //! use smithay::{
 //!     backend::renderer::{
 //!         damage::OutputDamageTracker,
@@ -204,17 +46,18 @@
 //!
 //! // Initialize a static damage tracked renderer
 //! let mut damage_tracker = OutputDamageTracker::new((800, 600), 1.0, Transform::Normal);
-//! # let mut renderer = FakeRenderer;
+//! # let mut renderer = DummyRenderer;
+//! # let mut framebuffer = DummyFramebuffer;
 //!
 //! loop {
 //!     // Create the render elements from the surface
 //!     let location = Point::from((100, 100));
-//!     let render_elements: Vec<WaylandSurfaceRenderElement<FakeRenderer>> =
+//!     let render_elements: Vec<WaylandSurfaceRenderElement<DummyRenderer>> =
 //!         render_elements_from_surface_tree(&mut renderer, &surface, location, 1.0, 1.0, Kind::Unspecified);
 //!
 //!     // Render the element(s)
 //!     damage_tracker
-//!         .render_output(&mut renderer, 0, &*render_elements, [0.8, 0.8, 0.9, 1.0])
+//!         .render_output(&mut renderer, &mut framebuffer, 0, &*render_elements, [0.8, 0.8, 0.9, 1.0])
 //!         .expect("failed to render output");
 //! }
 //! ```
@@ -254,7 +97,7 @@ pub fn render_elements_from_surface_tree<R, E>(
 ) -> Vec<E>
 where
     R: Renderer + ImportAll,
-    <R as Renderer>::TextureId: Clone + 'static,
+    R::TextureId: Clone + 'static,
     E: From<WaylandSurfaceRenderElement<R>>,
 {
     let location = location.into().to_f64();
@@ -314,7 +157,7 @@ where
 #[derive(Debug)]
 pub enum WaylandSurfaceTexture<R: Renderer> {
     /// A renderer texture
-    Texture(<R as Renderer>::TextureId),
+    Texture(R::TextureId),
     /// A solid color
     SolidColor(Color32F),
 }
@@ -355,9 +198,9 @@ impl<R: Renderer + ImportAll> WaylandSurfaceRenderElement<R> {
         location: Point<f64, Physical>,
         alpha: f32,
         kind: Kind,
-    ) -> Result<Option<Self>, <R as Renderer>::Error>
+    ) -> Result<Option<Self>, R::Error>
     where
-        <R as Renderer>::TextureId: Clone + 'static,
+        R::TextureId: Clone + 'static,
     {
         let id = Id::from_wayland_resource(surface);
         crate::backend::renderer::utils::import_surface(renderer, states)?;
@@ -387,7 +230,7 @@ impl<R: Renderer + ImportAll> WaylandSurfaceRenderElement<R> {
         data: &RendererSurfaceState,
     ) -> Option<Self>
     where
-        <R as Renderer>::TextureId: Clone + 'static,
+        R::TextureId: Clone + 'static,
     {
         let buffer = data.buffer()?.clone();
 
@@ -528,7 +371,7 @@ impl<R: Renderer + ImportAll> Element for WaylandSurfaceRenderElement<R> {
 impl<R> RenderElement<R> for WaylandSurfaceRenderElement<R>
 where
     R: Renderer + ImportAll,
-    <R as Renderer>::TextureId: Texture + 'static,
+    R::TextureId: Texture + 'static,
 {
     #[inline]
     fn underlying_storage(&self, _renderer: &mut R) -> Option<UnderlyingStorage<'_>> {
@@ -537,9 +380,9 @@ where
 
     #[instrument(level = "trace", skip(frame))]
     #[profiling::function]
-    fn draw<'a>(
+    fn draw(
         &self,
-        frame: &mut <R as Renderer>::Frame<'a>,
+        frame: &mut R::Frame<'_, '_>,
         src: Rectangle<f64, BufferCoords>,
         dst: Rectangle<i32, Physical>,
         damage: &[Rectangle<i32, Physical>],
diff --git a/src/backend/renderer/element/tests.rs b/src/backend/renderer/element/tests.rs
index 609df784ec14..ccbbf6e965eb 100644
--- a/src/backend/renderer/element/tests.rs
+++ b/src/backend/renderer/element/tests.rs
@@ -35,23 +35,23 @@ render_elements! {
 }
 
 render_elements! {
-    TextureIdTest<R> where R: ImportMem, <R as Renderer>::TextureId: Clone;
+    TextureIdTest<R> where R: ImportMem, R::TextureId: Clone;
     Memory=ImportMemRenderElement,
 }
 
 render_elements! {
-    TextureIdTest1<R> where R: ImportMem, <R as Renderer>::TextureId: 'static;
+    TextureIdTest1<R> where R: ImportMem, R::TextureId: 'static;
     Memory=ImportMemRenderElement,
 }
 
 render_elements! {
-    TextureIdTest2<'a, R, C> where R: ImportMem, <R as Renderer>::TextureId: 'a;
+    TextureIdTest2<'a, R, C> where R: ImportMem, R::TextureId: 'a;
     Memory=ImportMemRenderElement,
     Custom=&'a C,
 }
 
 render_elements! {
-    TextureIdTest3<'a, R, C> where R: ImportMem, <R as Renderer>::TextureId: Clone + 'a;
+    TextureIdTest3<'a, R, C> where R: ImportMem, R::TextureId: Clone + 'a;
     Memory=ImportMemRenderElement,
     Custom=&'a C,
 }
@@ -145,12 +145,12 @@ where
 {
     fn draw(
         &self,
-        _frame: &mut <R as Renderer>::Frame<'_>,
+        _frame: &mut R::Frame<'_, '_>,
         _src: Rectangle<f64, Buffer>,
         _dst: Rectangle<i32, Physical>,
         _damage: &[Rectangle<i32, Physical>],
         _opaque_regions: &[Rectangle<i32, Physical>],
-    ) -> Result<(), <R as Renderer>::Error> {
+    ) -> Result<(), R::Error> {
         todo!()
     }
 }
@@ -183,12 +183,12 @@ where
 {
     fn draw(
         &self,
-        _frame: &mut <R as Renderer>::Frame<'_>,
+        _frame: &mut R::Frame<'_, '_>,
         _src: Rectangle<f64, Buffer>,
         _dst: Rectangle<i32, Physical>,
         _damage: &[Rectangle<i32, Physical>],
         _opaque_regions: &[Rectangle<i32, Physical>],
-    ) -> Result<(), <R as Renderer>::Error> {
+    ) -> Result<(), R::Error> {
         todo!()
     }
 }
@@ -230,12 +230,12 @@ where
 {
     fn draw(
         &self,
-        _frame: &mut <R as Renderer>::Frame<'_>,
+        _frame: &mut R::Frame<'_, '_>,
         _src: Rectangle<f64, Buffer>,
         _dst: Rectangle<i32, Physical>,
         _damage: &[Rectangle<i32, Physical>],
         _opaque_regions: &[Rectangle<i32, Physical>],
-    ) -> Result<(), <R as Renderer>::Error> {
+    ) -> Result<(), R::Error> {
         todo!()
     }
 }
@@ -276,12 +276,12 @@ where
 {
     fn draw(
         &self,
-        _frame: &mut <R as Renderer>::Frame<'_>,
+        _frame: &mut R::Frame<'_, '_>,
         _src: Rectangle<f64, Buffer>,
         _dst: Rectangle<i32, Physical>,
         _damage: &[Rectangle<i32, Physical>],
         _opaque_regions: &[Rectangle<i32, Physical>],
-    ) -> Result<(), <R as Renderer>::Error> {
+    ) -> Result<(), R::Error> {
         todo!()
     }
 }
diff --git a/src/backend/renderer/element/texture.rs b/src/backend/renderer/element/texture.rs
index d10c4c611a2b..a9f69262d3b3 100644
--- a/src/backend/renderer/element/texture.rs
+++ b/src/backend/renderer/element/texture.rs
@@ -41,114 +41,9 @@
 //!
 //! ```no_run
 //! # use smithay::{
-//! #     backend::renderer::{Color32F, DebugFlags, Frame, ImportMem, Renderer, Texture, TextureFilter, sync::SyncPoint},
+//! #     backend::renderer::{Color32F, DebugFlags, Frame, ImportMem, Renderer, Texture, TextureFilter, sync::SyncPoint, test::{DummyRenderer, DummyFramebuffer}},
 //! #     utils::{Buffer, Physical, Rectangle, Size},
 //! # };
-//! #
-//! # #[derive(Clone, Debug)]
-//! # struct FakeTexture;
-//! #
-//! # impl Texture for FakeTexture {
-//! #     fn width(&self) -> u32 {
-//! #         unimplemented!()
-//! #     }
-//! #     fn height(&self) -> u32 {
-//! #         unimplemented!()
-//! #     }
-//! #     fn format(&self) -> Option<Fourcc> {
-//! #         unimplemented!()
-//! #     }
-//! # }
-//! #
-//! # struct FakeFrame;
-//! #
-//! # impl Frame for FakeFrame {
-//! #     type Error = std::convert::Infallible;
-//! #     type TextureId = FakeTexture;
-//! #
-//! #     fn id(&self) -> usize { unimplemented!() }
-//! #     fn clear(&mut self, _: Color32F, _: &[Rectangle<i32, Physical>]) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn draw_solid(
-//! #         &mut self,
-//! #         _dst: Rectangle<i32, Physical>,
-//! #         _damage: &[Rectangle<i32, Physical>],
-//! #         _color: Color32F,
-//! #     ) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn render_texture_from_to(
-//! #         &mut self,
-//! #         _: &Self::TextureId,
-//! #         _: Rectangle<f64, Buffer>,
-//! #         _: Rectangle<i32, Physical>,
-//! #         _: &[Rectangle<i32, Physical>],
-//! #         _: &[Rectangle<i32, Physical>],
-//! #         _: Transform,
-//! #         _: f32,
-//! #     ) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn transformation(&self) -> Transform {
-//! #         unimplemented!()
-//! #     }
-//! #     fn finish(self) -> Result<SyncPoint, Self::Error> { unimplemented!() }
-//! #     fn wait(&mut self, sync: &SyncPoint) -> Result<(), Self::Error> { unimplemented!() }
-//! # }
-//! #
-//! # #[derive(Debug)]
-//! # struct FakeRenderer;
-//! #
-//! # impl Renderer for FakeRenderer {
-//! #     type Error = std::convert::Infallible;
-//! #     type TextureId = FakeTexture;
-//! #     type Frame<'a> = FakeFrame;
-//! #
-//! #     fn id(&self) -> usize {
-//! #         unimplemented!()
-//! #     }
-//! #     fn downscale_filter(&mut self, _: TextureFilter) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn upscale_filter(&mut self, _: TextureFilter) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn set_debug_flags(&mut self, _: DebugFlags) {
-//! #         unimplemented!()
-//! #     }
-//! #     fn debug_flags(&self) -> DebugFlags {
-//! #         unimplemented!()
-//! #     }
-//! #     fn render(&mut self, _: Size<i32, Physical>, _: Transform) -> Result<Self::Frame<'_>, Self::Error>
-//! #     {
-//! #         unimplemented!()
-//! #     }
-//! #     fn wait(&mut self, sync: &SyncPoint) -> Result<(), Self::Error> { unimplemented!() }
-//! # }
-//! #
-//! # impl ImportMem for FakeRenderer {
-//! #     fn import_memory(
-//! #         &mut self,
-//! #         _: &[u8],
-//! #         _: Fourcc,
-//! #         _: Size<i32, Buffer>,
-//! #         _: bool,
-//! #     ) -> Result<Self::TextureId, Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn update_memory(
-//! #         &mut self,
-//! #         _: &Self::TextureId,
-//! #         _: &[u8],
-//! #         _: Rectangle<i32, Buffer>,
-//! #     ) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn mem_formats(&self) -> Box<dyn Iterator<Item=Fourcc>> {
-//! #         unimplemented!()
-//! #     }
-//! # }
 //! use smithay::{
 //!     backend::{
 //!         allocator::Fourcc,
@@ -167,7 +62,8 @@
 //! const HEIGHT: i32 = 10;
 //!
 //! let memory = vec![0; (WIDTH * 4 * HEIGHT) as usize];
-//! # let mut renderer = FakeRenderer;
+//! # let mut renderer = DummyRenderer;
+//! # let mut framebuffer = DummyFramebuffer;
 //!
 //! // Create the texture buffer from a chunk of memory
 //! let texture_buffer = TextureBuffer::from_memory(
@@ -192,7 +88,7 @@
 //!
 //!     // Render the element(s)
 //!     damage_tracker
-//!         .render_output(&mut renderer, 0, &[&render_element], [0.8, 0.8, 0.9, 1.0])
+//!         .render_output(&mut renderer, &mut framebuffer, 0, &[&render_element], [0.8, 0.8, 0.9, 1.0])
 //!         .expect("failed to render output");
 //! }
 //! ```
@@ -201,114 +97,9 @@
 //!
 //! ```no_run
 //! # use smithay::{
-//! #     backend::renderer::{Color32F, DebugFlags, Frame, ImportMem, Renderer, Texture, TextureFilter, sync::SyncPoint},
+//! #     backend::renderer::{Color32F, DebugFlags, Frame, ImportMem, Renderer, Texture, TextureFilter, sync::SyncPoint, test::{DummyRenderer, DummyFramebuffer}},
 //! #     utils::{Buffer, Physical},
 //! # };
-//! #
-//! # #[derive(Clone, Debug)]
-//! # struct FakeTexture;
-//! #
-//! # impl Texture for FakeTexture {
-//! #     fn width(&self) -> u32 {
-//! #         unimplemented!()
-//! #     }
-//! #     fn height(&self) -> u32 {
-//! #         unimplemented!()
-//! #     }
-//! #     fn format(&self) -> Option<Fourcc> {
-//! #         unimplemented!()
-//! #     }
-//! # }
-//! #
-//! # struct FakeFrame;
-//! #
-//! # impl Frame for FakeFrame {
-//! #     type Error = std::convert::Infallible;
-//! #     type TextureId = FakeTexture;
-//! #
-//! #     fn id(&self) -> usize { unimplemented!() }
-//! #     fn clear(&mut self, _: Color32F, _: &[Rectangle<i32, Physical>]) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn draw_solid(
-//! #         &mut self,
-//! #         _dst: Rectangle<i32, Physical>,
-//! #         _damage: &[Rectangle<i32, Physical>],
-//! #         _color: Color32F,
-//! #     ) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn render_texture_from_to(
-//! #         &mut self,
-//! #         _: &Self::TextureId,
-//! #         _: Rectangle<f64, Buffer>,
-//! #         _: Rectangle<i32, Physical>,
-//! #         _: &[Rectangle<i32, Physical>],
-//! #         _: &[Rectangle<i32, Physical>],
-//! #         _: Transform,
-//! #         _: f32,
-//! #     ) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn transformation(&self) -> Transform {
-//! #         unimplemented!()
-//! #     }
-//! #     fn finish(self) -> Result<SyncPoint, Self::Error> { unimplemented!() }
-//! #     fn wait(&mut self, sync: &SyncPoint) -> Result<(), Self::Error> { unimplemented!() }
-//! # }
-//! #
-//! # #[derive(Debug)]
-//! # struct FakeRenderer;
-//! #
-//! # impl Renderer for FakeRenderer {
-//! #     type Error = std::convert::Infallible;
-//! #     type TextureId = FakeTexture;
-//! #     type Frame<'a> = FakeFrame;
-//! #
-//! #     fn id(&self) -> usize {
-//! #         unimplemented!()
-//! #     }
-//! #     fn downscale_filter(&mut self, _: TextureFilter) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn upscale_filter(&mut self, _: TextureFilter) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn set_debug_flags(&mut self, _: DebugFlags) {
-//! #         unimplemented!()
-//! #     }
-//! #     fn debug_flags(&self) -> DebugFlags {
-//! #         unimplemented!()
-//! #     }
-//! #     fn render(&mut self, _: Size<i32, Physical>, _: Transform) -> Result<Self::Frame<'_>, Self::Error>
-//! #     {
-//! #         unimplemented!()
-//! #     }
-//! #     fn wait(&mut self, sync: &SyncPoint) -> Result<(), Self::Error> { unimplemented!() }
-//! # }
-//! #
-//! # impl ImportMem for FakeRenderer {
-//! #     fn import_memory(
-//! #         &mut self,
-//! #         _: &[u8],
-//! #         _: Fourcc,
-//! #         _: Size<i32, Buffer>,
-//! #         _: bool,
-//! #     ) -> Result<Self::TextureId, Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn update_memory(
-//! #         &mut self,
-//! #         _: &Self::TextureId,
-//! #         _: &[u8],
-//! #         _: Rectangle<i32, Buffer>,
-//! #     ) -> Result<(), Self::Error> {
-//! #         unimplemented!()
-//! #     }
-//! #     fn mem_formats(&self) -> Box<dyn Iterator<Item=Fourcc>> {
-//! #         unimplemented!()
-//! #     }
-//! # }
 //! use std::time::{Duration, Instant};
 //!
 //! use smithay::{
@@ -329,7 +120,8 @@
 //! const HEIGHT: i32 = 10;
 //!
 //! let memory = vec![0; (WIDTH * 4 * HEIGHT) as usize];
-//! # let mut renderer = FakeRenderer;
+//! # let mut renderer = DummyRenderer;
+//! # let mut framebuffer = DummyFramebuffer;
 //!
 //! // Create the texture buffer from a chunk of memory
 //! let mut texture_render_buffer = TextureRenderBuffer::from_memory(
@@ -397,7 +189,7 @@
 //!
 //!     // Render the element(s)
 //!     damage_tracker
-//!         .render_output(&mut renderer, 0, &[&render_element], [0.8, 0.8, 0.9, 1.0])
+//!         .render_output(&mut renderer, &mut framebuffer, 0, &[&render_element], [0.8, 0.8, 0.9, 1.0])
 //!         .expect("failed to render output");
 //! }
 //! ```
@@ -460,7 +252,7 @@ impl<T> TextureBuffer<T> {
         scale: i32,
         transform: Transform,
         opaque_regions: Option<Vec<Rectangle<i32, Buffer>>>,
-    ) -> Result<Self, <R as Renderer>::Error> {
+    ) -> Result<Self, R::Error> {
         let texture = renderer.import_memory(data, format, size.into(), flipped)?;
         Ok(TextureBuffer::from_texture(
             renderer,
@@ -523,7 +315,7 @@ impl<T: Texture> TextureRenderBuffer<T> {
         scale: i32,
         transform: Transform,
         opaque_regions: Option<Vec<Rectangle<i32, Buffer>>>,
-    ) -> Result<Self, <R as Renderer>::Error> {
+    ) -> Result<Self, R::Error> {
         let texture = renderer.import_memory(data, format, size.into(), flipped)?;
         Ok(TextureRenderBuffer::from_texture(
             renderer,
@@ -558,7 +350,7 @@ impl<T: Texture> TextureRenderBuffer<T> {
         data: &[u8],
         region: Rectangle<i32, Buffer>,
         opaque_regions: Option<Vec<Rectangle<i32, Buffer>>>,
-    ) -> Result<(), <R as Renderer>::Error> {
+    ) -> Result<(), R::Error> {
         assert_eq!(self.renderer_id, renderer.id());
         renderer.update_memory(&self.texture, data, region)?;
         self.damage_tracker.lock().unwrap().add([region]);
@@ -894,14 +686,14 @@ where
 {
     #[instrument(level = "trace", skip(self, frame))]
     #[profiling::function]
-    fn draw<'a>(
+    fn draw(
         &self,
-        frame: &mut <R as Renderer>::Frame<'a>,
+        frame: &mut R::Frame<'_, '_>,
         src: Rectangle<f64, Buffer>,
         dst: Rectangle<i32, Physical>,
         damage: &[Rectangle<i32, Physical>],
         opaque_regions: &[Rectangle<i32, Physical>],
-    ) -> Result<(), <R as Renderer>::Error> {
+    ) -> Result<(), R::Error> {
         if frame.id() != self.renderer_id {
             warn!("trying to render texture from different renderer");
             return Ok(());
diff --git a/src/backend/renderer/element/utils/elements.rs b/src/backend/renderer/element/utils/elements.rs
index 563945283486..607909a0aacf 100644
--- a/src/backend/renderer/element/utils/elements.rs
+++ b/src/backend/renderer/element/utils/elements.rs
@@ -95,12 +95,12 @@ impl<E: Element> Element for RescaleRenderElement<E> {
 impl<R: Renderer, E: RenderElement<R>> RenderElement<R> for RescaleRenderElement<E> {
     fn draw(
         &self,
-        frame: &mut <R as Renderer>::Frame<'_>,
+        frame: &mut R::Frame<'_, '_>,
         src: crate::utils::Rectangle<f64, crate::utils::Buffer>,
         dst: crate::utils::Rectangle<i32, crate::utils::Physical>,
         damage: &[crate::utils::Rectangle<i32, crate::utils::Physical>],
         opaque_regions: &[crate::utils::Rectangle<i32, crate::utils::Physical>],
-    ) -> Result<(), <R as Renderer>::Error> {
+    ) -> Result<(), R::Error> {
         self.element.draw(frame, src, dst, damage, opaque_regions)
     }
 
@@ -279,12 +279,12 @@ impl<E: Element> Element for CropRenderElement<E> {
 impl<R: Renderer, E: RenderElement<R>> RenderElement<R> for CropRenderElement<E> {
     fn draw(
         &self,
-        frame: &mut <R as Renderer>::Frame<'_>,
+        frame: &mut R::Frame<'_, '_>,
         src: crate::utils::Rectangle<f64, crate::utils::Buffer>,
         dst: crate::utils::Rectangle<i32, crate::utils::Physical>,
         damage: &[crate::utils::Rectangle<i32, crate::utils::Physical>],
         opaque_regions: &[crate::utils::Rectangle<i32, crate::utils::Physical>],
-    ) -> Result<(), <R as Renderer>::Error> {
+    ) -> Result<(), R::Error> {
         self.element.draw(frame, src, dst, damage, opaque_regions)
     }
 
@@ -381,12 +381,12 @@ impl<E: Element> Element for RelocateRenderElement<E> {
 impl<R: Renderer, E: RenderElement<R>> RenderElement<R> for RelocateRenderElement<E> {
     fn draw(
         &self,
-        frame: &mut <R as Renderer>::Frame<'_>,
+        frame: &mut R::Frame<'_, '_>,
         src: crate::utils::Rectangle<f64, crate::utils::Buffer>,
         dst: crate::utils::Rectangle<i32, crate::utils::Physical>,
         damage: &[crate::utils::Rectangle<i32, crate::utils::Physical>],
         opaque_regions: &[crate::utils::Rectangle<i32, crate::utils::Physical>],
-    ) -> Result<(), <R as Renderer>::Error> {
+    ) -> Result<(), R::Error> {
         self.element.draw(frame, src, dst, damage, opaque_regions)
     }
 
diff --git a/src/backend/renderer/gles/element.rs b/src/backend/renderer/gles/element.rs
index 6d16fea50c02..ac3d3c7746b9 100644
--- a/src/backend/renderer/gles/element.rs
+++ b/src/backend/renderer/gles/element.rs
@@ -109,7 +109,7 @@ impl RenderElement<GlesRenderer> for PixelShaderElement {
     #[profiling::function]
     fn draw(
         &self,
-        frame: &mut GlesFrame<'_>,
+        frame: &mut GlesFrame<'_, '_>,
         _src: Rectangle<f64, Buffer>,
         dst: Rectangle<i32, Physical>,
         damage: &[Rectangle<i32, Physical>],
@@ -204,7 +204,7 @@ impl RenderElement<GlesRenderer> for TextureShaderElement {
     #[profiling::function]
     fn draw(
         &self,
-        frame: &mut GlesFrame<'_>,
+        frame: &mut GlesFrame<'_, '_>,
         src: Rectangle<f64, Buffer>,
         dst: Rectangle<i32, Physical>,
         damage: &[Rectangle<i32, Physical>],
diff --git a/src/backend/renderer/gles/mod.rs b/src/backend/renderer/gles/mod.rs
index 4821de577b2f..736f761e3459 100644
--- a/src/backend/renderer/gles/mod.rs
+++ b/src/backend/renderer/gles/mod.rs
@@ -12,7 +12,7 @@ use std::{
     sync::{
         atomic::{AtomicBool, AtomicPtr, Ordering},
         mpsc::{channel, Receiver, Sender},
-        Arc, Mutex, RwLock,
+        Arc, Mutex, RwLock, RwLockWriteGuard,
     },
 };
 use tracing::{debug, error, info, info_span, instrument, span, span::EnteredSpan, trace, warn, Level};
@@ -35,7 +35,7 @@ use self::version::GlVersion;
 
 use super::{
     sync::SyncPoint, Bind, Blit, Color32F, DebugFlags, ExportMem, Frame, ImportDma, ImportMem, Offscreen,
-    Renderer, Texture, TextureFilter, TextureMapping, Unbind,
+    Renderer, RendererSuper, Texture, TextureFilter, TextureMapping,
 };
 use crate::backend::egl::{
     ffi::egl::{self as ffi_egl, types::EGLImage},
@@ -135,39 +135,44 @@ impl Drop for GlesRenderbufferInternal {
     }
 }
 
+/// A GL framebuffer
 #[derive(Debug)]
-enum GlesTarget {
+pub struct GlesTarget<'a>(GlesTargetInternal<'a>);
+
+#[derive(Debug)]
+enum GlesTargetInternal<'a> {
     Image {
         // TODO: Ideally we would be able to share the texture between renderers with shared EGLContexts though.
         // But we definitly don't want to add user data to a dmabuf to facilitate this. Maybe use the EGLContexts userdata for storing the buffers?
         buf: GlesBuffer,
-        dmabuf: Dmabuf,
+        dmabuf: &'a mut Dmabuf,
     },
     Surface {
-        surface: Rc<EGLSurface>,
+        surface: &'a mut EGLSurface,
     },
     Texture {
         texture: GlesTexture,
+        sync_lock: RwLockWriteGuard<'a, TextureSync>,
         fbo: ffi::types::GLuint,
         destruction_callback_sender: Sender<CleanupResource>,
     },
     Renderbuffer {
-        buf: GlesRenderbuffer,
+        buf: &'a mut GlesRenderbuffer,
         fbo: ffi::types::GLuint,
     },
 }
 
-impl GlesTarget {
+impl GlesTargetInternal<'_> {
     fn format(&self) -> Option<(ffi::types::GLenum, bool)> {
         match self {
-            GlesTarget::Image { dmabuf, .. } => {
-                let format = crate::backend::allocator::Buffer::format(dmabuf).code;
+            GlesTargetInternal::Image { dmabuf, .. } => {
+                let format = crate::backend::allocator::Buffer::format(*dmabuf).code;
                 let has_alpha = has_alpha(format);
                 let (format, _, _) = fourcc_to_gl_formats(format)?;
 
                 Some((format, has_alpha))
             }
-            GlesTarget::Surface { surface, .. } => {
+            GlesTargetInternal::Surface { surface, .. } => {
                 let format = surface.pixel_format();
                 let format = match (format.color_bits, format.alpha_bits) {
                     (24, 8) => ffi::RGB8,
@@ -178,23 +183,27 @@ impl GlesTarget {
 
                 Some((format, true))
             }
-            GlesTarget::Texture { texture, .. } => Some((texture.0.format?, texture.0.has_alpha)),
-            GlesTarget::Renderbuffer { buf, .. } => Some((buf.0.format, buf.0.has_alpha)),
+            GlesTargetInternal::Texture { texture, .. } => Some((texture.0.format?, texture.0.has_alpha)),
+            GlesTargetInternal::Renderbuffer { buf, .. } => Some((buf.0.format, buf.0.has_alpha)),
         }
     }
 
     #[profiling::function]
     fn make_current(&self, gl: &ffi::Gles2, egl: &EGLContext) -> Result<(), MakeCurrentError> {
         unsafe {
-            if let GlesTarget::Surface { surface, .. } = self {
+            if let GlesTargetInternal::Surface { surface, .. } = self {
                 egl.make_current_with_surface(surface)?;
                 gl.BindFramebuffer(ffi::FRAMEBUFFER, 0);
             } else {
                 egl.make_current()?;
                 match self {
-                    GlesTarget::Image { ref buf, .. } => gl.BindFramebuffer(ffi::FRAMEBUFFER, buf.fbo),
-                    GlesTarget::Texture { ref fbo, .. } => gl.BindFramebuffer(ffi::FRAMEBUFFER, *fbo),
-                    GlesTarget::Renderbuffer { ref fbo, .. } => gl.BindFramebuffer(ffi::FRAMEBUFFER, *fbo),
+                    GlesTargetInternal::Image { ref buf, .. } => {
+                        gl.BindFramebuffer(ffi::FRAMEBUFFER, buf.fbo)
+                    }
+                    GlesTargetInternal::Texture { ref fbo, .. } => gl.BindFramebuffer(ffi::FRAMEBUFFER, *fbo),
+                    GlesTargetInternal::Renderbuffer { ref fbo, .. } => {
+                        gl.BindFramebuffer(ffi::FRAMEBUFFER, *fbo)
+                    }
                     _ => unreachable!(),
                 }
             }
@@ -203,17 +212,17 @@ impl GlesTarget {
     }
 }
 
-impl Drop for GlesTarget {
+impl Drop for GlesTargetInternal<'_> {
     fn drop(&mut self) {
         match self {
-            GlesTarget::Texture {
+            GlesTargetInternal::Texture {
                 fbo,
                 destruction_callback_sender,
                 ..
             } => {
                 let _ = destruction_callback_sender.send(CleanupResource::FramebufferObject(*fbo));
             }
-            GlesTarget::Renderbuffer { buf, fbo, .. } => {
+            GlesTargetInternal::Renderbuffer { buf, fbo, .. } => {
                 let _ = buf
                     .0
                     .destruction_callback_sender
@@ -246,7 +255,6 @@ pub enum Capability {
 /// A renderer utilizing OpenGL ES
 pub struct GlesRenderer {
     // state
-    target: Option<GlesTarget>,
     min_filter: TextureFilter,
     max_filter: TextureFilter,
     debug_flags: DebugFlags,
@@ -288,12 +296,14 @@ pub struct GlesRenderer {
 
 /// Handle to the currently rendered frame during [`GlesRenderer::render`](Renderer::render).
 ///
-/// Leaking this frame will prevent it from synchronizing the rendered framebuffer,
-/// which might cause glitches. Additionally parts of the GL state might not be reset correctly,
-/// causing unexpected results for later render commands.
-/// The internal GL context and framebuffer will remain valid, no re-creation will be necessary.
-pub struct GlesFrame<'frame> {
+/// Leaking this frame will cause a variety of problems:
+/// - It might prevent the frame from synchronizing the rendered framebuffer causing glitches.
+/// - Depending on the bound target this can deadlock, if the same target is used later in any way.
+/// - Additionally parts of the GL state might not be reset correctly, causing unexpected results for later render commands.
+/// - The internal GL context and framebuffer will remain valid, no re-creation will be necessary.
+pub struct GlesFrame<'frame, 'buffer> {
     renderer: &'frame mut GlesRenderer,
+    target: &'frame mut GlesTarget<'buffer>,
     current_projection: Matrix3<f32>,
     transform: Transform,
     size: Size<i32, Physical>,
@@ -303,10 +313,11 @@ pub struct GlesFrame<'frame> {
     span: EnteredSpan,
 }
 
-impl fmt::Debug for GlesFrame<'_> {
+impl fmt::Debug for GlesFrame<'_, '_> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         f.debug_struct("GlesFrame")
             .field("renderer", &self.renderer)
+            .field("target", &self.target)
             .field("current_projection", &self.current_projection)
             .field("transform", &self.transform)
             .field("tex_program_override", &self.tex_program_override)
@@ -320,7 +331,6 @@ impl fmt::Debug for GlesRenderer {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         f.debug_struct("GlesRenderer")
             .field("buffers", &self.buffers)
-            .field("target", &self.target)
             .field("extensions", &self.extensions)
             .field("capabilities", &self.capabilities)
             .field("tex_program", &self.tex_program)
@@ -450,12 +460,10 @@ impl GlesRenderer {
     ///   `EGLContext` shared with the given one (see `EGLContext::new_shared`) and can be used on
     ///   any of these renderers.
     /// - This renderer has no default framebuffer, use `Bind::bind` before rendering.
-    /// - Binding a new target, while another one is already bound, will replace the current target.
     /// - Shm buffers can be released after a successful import, without the texture handle becoming invalid.
     /// - Texture filtering starts with Linear-downscaling and Linear-upscaling.
-    /// - The renderer might use two-pass rendering internally to facilitate color space transformations.
-    ///   As such it reserves any stencil buffer for internal use and makes no guarantee about previous framebuffer
-    ///   contents being accessible during the lifetime of a `GlesFrame`.
+    /// - If OpenGL ES 3.0 is not available and the underlying [`EGLContext`] is shared, memory textures
+    ///   will insert `glFinish`-calls into the pipeline. Consider not sharing contexts, if OpenGL ES 3 isn't available.
     pub unsafe fn with_capabilities(
         context: EGLContext,
         capabilities: impl IntoIterator<Item = Capability>,
@@ -597,7 +605,6 @@ impl GlesRenderer {
             min_filter: TextureFilter::Linear,
             max_filter: TextureFilter::Linear,
 
-            target: None,
             buffers: Vec::new(),
             dmabuf_cache: std::collections::HashMap::new(),
             vertices: Vec::with_capacity(6 * 16),
@@ -616,13 +623,57 @@ impl GlesRenderer {
         Ok(renderer)
     }
 
+    fn bind_texture<'a>(&mut self, texture: &'a GlesTexture) -> Result<GlesTarget<'a>, GlesError> {
+        unsafe {
+            self.egl.make_current()?;
+        }
+
+        let bind = || {
+            let mut sync_lock = texture.0.sync.write().unwrap();
+            let mut fbo = 0;
+            unsafe {
+                sync_lock.wait_for_all(&self.gl);
+                self.gl.GenFramebuffers(1, &mut fbo as *mut _);
+                self.gl.BindFramebuffer(ffi::FRAMEBUFFER, fbo);
+                self.gl.FramebufferTexture2D(
+                    ffi::FRAMEBUFFER,
+                    ffi::COLOR_ATTACHMENT0,
+                    ffi::TEXTURE_2D,
+                    texture.0.texture,
+                    0,
+                );
+                let status = self.gl.CheckFramebufferStatus(ffi::FRAMEBUFFER);
+                self.gl.BindFramebuffer(ffi::FRAMEBUFFER, 0);
+
+                if status != ffi::FRAMEBUFFER_COMPLETE {
+                    self.gl.DeleteFramebuffers(1, &mut fbo as *mut _);
+                    return Err(GlesError::FramebufferBindingError);
+                }
+            }
+
+            Ok(GlesTarget(GlesTargetInternal::Texture {
+                texture: texture.clone(),
+                sync_lock,
+                destruction_callback_sender: self.destruction_callback_sender.clone(),
+                fbo,
+            }))
+        };
+
+        bind().inspect_err(|_| {
+            if let Err(err) = self.unbind() {
+                self.span.in_scope(|| warn!(?err, "Failed to unbind on err"));
+            }
+        })
+    }
+
     #[profiling::function]
-    pub(crate) fn make_current(&mut self) -> Result<(), MakeCurrentError> {
-        if let Some(target) = self.target.as_ref() {
-            target.make_current(&self.gl, &self.egl)?;
-        } else {
-            unsafe { self.egl.make_current()? };
+    fn unbind(&mut self) -> Result<(), GlesError> {
+        unsafe {
+            self.egl.make_current()?;
         }
+        unsafe { self.gl.BindFramebuffer(ffi::FRAMEBUFFER, 0) };
+        self.cleanup();
+        self.egl.unbind()?;
         Ok(())
     }
 
@@ -682,13 +733,6 @@ impl GlesRenderer {
     }
 }
 
-/// Warning: If your context only supports OpenGL ES 2.0 and
-/// you are sharing EGLContexts, using [`import_shm_buffer`]
-/// will insert a `glFinish()` call for every buffer import
-/// to synchronize texture access.
-///
-/// As a compositor developer consider not sharing
-/// contexts, if OpenGL ES 3.0 is unavailable.
 #[cfg(feature = "wayland_frontend")]
 impl ImportMemWl for GlesRenderer {
     #[instrument(level = "trace", parent = &self.span, skip(self))]
@@ -714,8 +758,6 @@ impl ImportMemWl for GlesRenderer {
         });
 
         with_buffer_contents(buffer, |ptr, len, data| {
-            self.make_current()?;
-
             let offset = data.offset;
             let width = data.width;
             let height = data.height;
@@ -782,6 +824,7 @@ impl ImportMemWl for GlesRenderer {
 
             let mut sync_lock = texture.0.sync.write().unwrap();
             unsafe {
+                self.egl.make_current()?;
                 sync_lock.wait_for_all(&self.gl);
                 self.gl.BindTexture(ffi::TEXTURE_2D, texture.0.texture);
                 self.gl
@@ -869,8 +912,6 @@ impl ImportMem for GlesRenderer {
         size: Size<i32, BufferCoord>,
         flipped: bool,
     ) -> Result<GlesTexture, GlesError> {
-        self.make_current()?;
-
         if data.len()
             < (size.w * size.h) as usize
                 * (get_bpp(format).ok_or(GlesError::UnsupportedPixelFormat(format))? / 8)
@@ -902,6 +943,7 @@ impl ImportMem for GlesRenderer {
         let texture = GlesTexture(Arc::new({
             let mut tex = 0;
             unsafe {
+                self.egl.make_current()?;
                 self.gl.GenTextures(1, &mut tex);
                 self.gl.BindTexture(ffi::TEXTURE_2D, tex);
                 self.gl
@@ -952,12 +994,10 @@ impl ImportMem for GlesRenderer {
     #[profiling::function]
     fn update_memory(
         &mut self,
-        texture: &<Self as Renderer>::TextureId,
+        texture: &Self::TextureId,
         data: &[u8],
         region: Rectangle<i32, BufferCoord>,
-    ) -> Result<(), <Self as Renderer>::Error> {
-        self.make_current()?;
-
+    ) -> Result<(), Self::Error> {
         if texture.0.format.is_none() {
             return Err(GlesError::UnknownPixelFormat);
         }
@@ -976,6 +1016,7 @@ impl ImportMem for GlesRenderer {
 
         let mut sync_lock = texture.0.sync.write().unwrap();
         unsafe {
+            self.egl.make_current()?;
             sync_lock.wait_for_all(&self.gl);
             self.gl.BindTexture(ffi::TEXTURE_2D, texture.0.texture);
             self.gl
@@ -1065,8 +1106,6 @@ impl ImportEgl for GlesRenderer {
         // clean up check if the buffer is still alive. For wl_drm the
         // is_alive check will always return true and the cache entry
         // will never be cleaned up.
-        self.make_current()?;
-
         let egl = self
             .egl_reader
             .as_ref()
@@ -1109,7 +1148,6 @@ impl ImportDma for GlesRenderer {
             return Err(GlesError::GLExtensionNotSupported(&["GL_OES_EGL_image"]));
         }
 
-        self.make_current()?;
         self.existing_dmabuf_texture(buffer)?.map(Ok).unwrap_or_else(|| {
             let is_external = !self.egl.dmabuf_render_formats().contains(&buffer.format());
             let image = self
@@ -1176,6 +1214,9 @@ impl GlesRenderer {
         is_external: bool,
         tex: Option<u32>,
     ) -> Result<u32, GlesError> {
+        unsafe {
+            self.egl.make_current()?;
+        }
         let tex = tex.unwrap_or_else(|| unsafe {
             let mut tex = 0;
             self.gl.GenTextures(1, &mut tex);
@@ -1199,21 +1240,17 @@ impl GlesRenderer {
 impl ExportMem for GlesRenderer {
     type TextureMapping = GlesMapping;
 
-    #[instrument(level = "trace", parent = &self.span, skip(self))]
+    #[instrument(level = "trace", parent = &self.span, skip(self, target))]
     #[profiling::function]
     fn copy_framebuffer(
         &mut self,
+        target: &GlesTarget<'_>,
         region: Rectangle<i32, BufferCoord>,
         fourcc: Fourcc,
     ) -> Result<Self::TextureMapping, Self::Error> {
-        self.make_current()?;
+        target.0.make_current(&self.gl, &self.egl)?;
 
-        let (_, has_alpha) = self
-            .target
-            .as_ref()
-            .ok_or(GlesError::UnknownPixelFormat)?
-            .format()
-            .ok_or(GlesError::UnknownPixelFormat)?;
+        let (_, has_alpha) = target.0.format().ok_or(GlesError::UnknownPixelFormat)?;
         let (_, format, layout) = fourcc_to_gl_formats(fourcc).ok_or(GlesError::UnknownPixelFormat)?;
 
         let mut pbo = 0;
@@ -1225,13 +1262,12 @@ impl ExportMem for GlesRenderer {
             let size = (region.size.w * region.size.h * bpp as i32) as isize;
             self.gl
                 .BufferData(ffi::PIXEL_PACK_BUFFER, size, ptr::null(), ffi::STREAM_READ);
-            self.gl.ReadBuffer(
-                if matches!(self.target.as_ref(), None | Some(GlesTarget::Surface { .. })) {
+            self.gl
+                .ReadBuffer(if matches!(target.0, GlesTargetInternal::Surface { .. }) {
                     ffi::BACK
                 } else {
                     ffi::COLOR_ATTACHMENT0
-                },
-            );
+                });
             self.gl.ReadPixels(
                 region.loc.x,
                 region.loc.y,
@@ -1262,17 +1298,8 @@ impl ExportMem for GlesRenderer {
     }
 
     fn can_read_texture(&mut self, texture: &Self::TextureId) -> Result<bool, GlesError> {
-        let old_target = self.target.take();
-
         // if we can't bind the texture, we can't read it
-        let res = self.bind(texture.clone()).is_ok();
-
-        // restore
-        self.unbind()?;
-        self.target = old_target;
-        self.make_current()?;
-
-        Ok(res)
+        Ok(self.bind_texture(texture).is_ok())
     }
 
     #[instrument(level = "trace", parent = &self.span, skip(self))]
@@ -1284,8 +1311,8 @@ impl ExportMem for GlesRenderer {
         fourcc: Fourcc,
     ) -> Result<Self::TextureMapping, Self::Error> {
         let mut pbo = 0;
-        let old_target = self.target.take();
-        self.bind(texture.clone())?;
+        let target = self.bind_texture(texture)?;
+        target.0.make_current(&self.gl, &self.egl)?;
 
         let (_, format, layout) = fourcc_to_gl_formats(fourcc).ok_or(GlesError::UnknownPixelFormat)?;
         let bpp = gl_bpp(format, layout).expect("We check the format before") / 8;
@@ -1315,11 +1342,6 @@ impl ExportMem for GlesRenderer {
             self.gl.GetError()
         };
 
-        // restore old framebuffer
-        self.unbind()?;
-        self.target = old_target;
-        self.make_current()?;
-
         match err {
             ffi::NO_ERROR => Ok(GlesMapping {
                 pbo,
@@ -1341,7 +1363,10 @@ impl ExportMem for GlesRenderer {
         &mut self,
         texture_mapping: &'a Self::TextureMapping,
     ) -> Result<&'a [u8], Self::Error> {
-        self.make_current()?;
+        unsafe {
+            self.egl.make_current()?;
+        }
+
         let size = texture_mapping.size();
         let len = size.w * size.h * 4;
 
@@ -1369,53 +1394,36 @@ impl ExportMem for GlesRenderer {
     }
 }
 
-impl Bind<Rc<EGLSurface>> for GlesRenderer {
-    #[instrument(level = "trace", parent = &self.span, skip(self))]
-    #[profiling::function]
-    fn bind(&mut self, surface: Rc<EGLSurface>) -> Result<(), GlesError> {
-        unsafe {
-            self.egl.make_current()?;
-        }
-
-        self.target = Some(GlesTarget::Surface {
-            surface: surface.clone(),
-        });
-        let res = self.make_current();
-        if res.is_err() {
-            let _ = self.unbind();
-        }
-        res?;
-
-        Ok(())
+impl Bind<EGLSurface> for GlesRenderer {
+    fn bind<'a>(&mut self, surface: &'a mut EGLSurface) -> Result<GlesTarget<'a>, GlesError> {
+        Ok(GlesTarget(GlesTargetInternal::Surface { surface }))
     }
 }
 
 impl Bind<Dmabuf> for GlesRenderer {
-    #[instrument(level = "trace", parent = &self.span, skip(self))]
-    #[profiling::function]
-    fn bind(&mut self, dmabuf: Dmabuf) -> Result<(), GlesError> {
+    fn bind<'a>(&mut self, dmabuf: &'a mut Dmabuf) -> Result<GlesTarget<'a>, GlesError> {
         unsafe {
             self.egl.make_current()?;
         }
 
-        let bind = || {
-            let (buf, dmabuf) = self
+        let mut bind = |dmabuf: &'a mut Dmabuf| {
+            let buf = self
                 .buffers
                 .iter_mut()
                 .find(|buffer| {
                     if let Some(dma) = buffer.dmabuf.upgrade() {
-                        dma == dmabuf
+                        dma == *dmabuf
                     } else {
                         false
                     }
                 })
-                .map(|buf| Ok((buf.clone(), buf.dmabuf.upgrade().unwrap())))
+                .map(|buf| Ok(buf.clone()))
                 .unwrap_or_else(|| {
                     trace!("Creating EGLImage for Dmabuf: {:?}", dmabuf);
                     let image = self
                         .egl
                         .display()
-                        .create_image_from_dmabuf(&dmabuf)
+                        .create_image_from_dmabuf(dmabuf)
                         .map_err(GlesError::BindBufferEGLError)?;
 
                     unsafe {
@@ -1439,7 +1447,9 @@ impl Bind<Dmabuf> for GlesRenderer {
                         self.gl.BindFramebuffer(ffi::FRAMEBUFFER, 0);
 
                         if status != ffi::FRAMEBUFFER_COMPLETE {
-                            //TODO wrap image and drop here
+                            self.gl.DeleteFramebuffers(1, &mut fbo as *mut _);
+                            self.gl.DeleteRenderbuffers(1, &mut rbo as *mut _);
+                            ffi_egl::DestroyImageKHR(**self.egl.display().get_display_handle(), image);
                             return Err(GlesError::FramebufferBindingError);
                         }
                         let buf = GlesBuffer {
@@ -1451,20 +1461,18 @@ impl Bind<Dmabuf> for GlesRenderer {
 
                         self.buffers.push(buf.clone());
 
-                        Ok((buf, dmabuf))
+                        Ok(buf)
                     }
                 })?;
 
-            self.target = Some(GlesTarget::Image { buf, dmabuf });
-            std::result::Result::<(), GlesError>::Ok(())
+            Ok(GlesTarget(GlesTargetInternal::Image { buf, dmabuf }))
         };
-        let res = bind();
-        if res.is_err() {
-            let _ = self.unbind();
-        }
-        res?;
-        self.make_current()?;
-        Ok(())
+
+        bind(dmabuf).inspect_err(|_| {
+            if let Err(err) = self.unbind() {
+                self.span.in_scope(|| warn!(?err, "Failed to unbind on err"));
+            }
+        })
     }
 
     fn supported_formats(&self) -> Option<FormatSet> {
@@ -1473,29 +1481,32 @@ impl Bind<Dmabuf> for GlesRenderer {
 }
 
 impl Bind<GlesTexture> for GlesRenderer {
-    #[instrument(level = "trace", parent = &self.span, skip(self))]
-    #[profiling::function]
-    fn bind(&mut self, texture: GlesTexture) -> Result<(), GlesError> {
+    fn bind<'a>(&mut self, texture: &'a mut GlesTexture) -> Result<GlesTarget<'a>, GlesError> {
+        self.bind_texture(texture)
+    }
+}
+
+impl Bind<GlesRenderbuffer> for GlesRenderer {
+    fn bind<'a>(&mut self, renderbuffer: &'a mut GlesRenderbuffer) -> Result<GlesTarget<'a>, GlesError> {
         unsafe {
             self.egl.make_current()?;
         }
 
-        let bind = || {
+        let bind = |renderbuffer: &'a mut GlesRenderbuffer| {
             let mut fbo = 0;
             unsafe {
-                // TODO: we should keep the lock, while the Target is active
-                texture.0.sync.read().unwrap().wait_for_upload(&self.gl);
                 self.gl.GenFramebuffers(1, &mut fbo as *mut _);
                 self.gl.BindFramebuffer(ffi::FRAMEBUFFER, fbo);
-                self.gl.FramebufferTexture2D(
+                self.gl.BindRenderbuffer(ffi::RENDERBUFFER, renderbuffer.0.rbo);
+                self.gl.FramebufferRenderbuffer(
                     ffi::FRAMEBUFFER,
                     ffi::COLOR_ATTACHMENT0,
-                    ffi::TEXTURE_2D,
-                    texture.0.texture,
-                    0,
+                    ffi::RENDERBUFFER,
+                    renderbuffer.0.rbo,
                 );
                 let status = self.gl.CheckFramebufferStatus(ffi::FRAMEBUFFER);
                 self.gl.BindFramebuffer(ffi::FRAMEBUFFER, 0);
+                self.gl.BindRenderbuffer(ffi::RENDERBUFFER, 0);
 
                 if status != ffi::FRAMEBUFFER_COMPLETE {
                     self.gl.DeleteFramebuffers(1, &mut fbo as *mut _);
@@ -1503,21 +1514,17 @@ impl Bind<GlesTexture> for GlesRenderer {
                 }
             }
 
-            self.target = Some(GlesTarget::Texture {
-                texture,
-                destruction_callback_sender: self.destruction_callback_sender.clone(),
+            Ok(GlesTarget(GlesTargetInternal::Renderbuffer {
+                buf: renderbuffer,
                 fbo,
-            });
-            Ok(())
+            }))
         };
-        let res = bind();
-        if res.is_err() {
-            let _ = self.unbind();
-        }
-        res?;
-        self.make_current()?;
 
-        Ok(())
+        bind(renderbuffer).inspect_err(|_| {
+            if let Err(err) = self.unbind() {
+                self.span.in_scope(|| warn!(?err, "Failed to unbind on err"));
+            }
+        })
     }
 }
 
@@ -1529,8 +1536,6 @@ impl Offscreen<GlesTexture> for GlesRenderer {
         format: Fourcc,
         size: Size<i32, BufferCoord>,
     ) -> Result<GlesTexture, GlesError> {
-        self.make_current()?;
-
         let has_alpha = has_alpha(format);
         let (internal, format, layout) =
             fourcc_to_gl_formats(format).ok_or(GlesError::UnsupportedPixelFormat(format))?;
@@ -1541,6 +1546,7 @@ impl Offscreen<GlesTexture> for GlesRenderer {
         }
 
         let tex = unsafe {
+            self.egl.make_current()?;
             let mut tex = 0;
             self.gl.GenTextures(1, &mut tex);
             self.gl.BindTexture(ffi::TEXTURE_2D, tex);
@@ -1562,44 +1568,6 @@ impl Offscreen<GlesTexture> for GlesRenderer {
     }
 }
 
-impl Bind<GlesRenderbuffer> for GlesRenderer {
-    #[instrument(level = "trace", parent = &self.span, skip(self))]
-    #[profiling::function]
-    fn bind(&mut self, renderbuffer: GlesRenderbuffer) -> Result<(), GlesError> {
-        self.unbind()?;
-        self.make_current()?;
-
-        let mut fbo = 0;
-        unsafe {
-            self.gl.GenFramebuffers(1, &mut fbo as *mut _);
-            self.gl.BindFramebuffer(ffi::FRAMEBUFFER, fbo);
-            self.gl.BindRenderbuffer(ffi::RENDERBUFFER, renderbuffer.0.rbo);
-            self.gl.FramebufferRenderbuffer(
-                ffi::FRAMEBUFFER,
-                ffi::COLOR_ATTACHMENT0,
-                ffi::RENDERBUFFER,
-                renderbuffer.0.rbo,
-            );
-            let status = self.gl.CheckFramebufferStatus(ffi::FRAMEBUFFER);
-            self.gl.BindFramebuffer(ffi::FRAMEBUFFER, 0);
-            self.gl.BindRenderbuffer(ffi::RENDERBUFFER, 0);
-
-            if status != ffi::FRAMEBUFFER_COMPLETE {
-                self.gl.DeleteFramebuffers(1, &mut fbo as *mut _);
-                return Err(GlesError::FramebufferBindingError);
-            }
-        }
-
-        self.target = Some(GlesTarget::Renderbuffer {
-            buf: renderbuffer,
-            fbo,
-        });
-        self.make_current()?;
-
-        Ok(())
-    }
-}
-
 impl Offscreen<GlesRenderbuffer> for GlesRenderer {
     #[instrument(level = "trace", parent = &self.span, skip(self))]
     #[profiling::function]
@@ -1611,8 +1579,6 @@ impl Offscreen<GlesRenderbuffer> for GlesRenderer {
         if !self.capabilities.contains(&Capability::Renderbuffer) {
             return Err(GlesError::UnsupportedPixelFormat(format));
         }
-        self.make_current()?;
-
         let has_alpha = has_alpha(format);
         let (internal, _, _) =
             fourcc_to_gl_formats(format).ok_or(GlesError::UnsupportedPixelFormat(format))?;
@@ -1622,6 +1588,8 @@ impl Offscreen<GlesRenderbuffer> for GlesRenderer {
         }
 
         unsafe {
+            self.egl.make_current()?;
+
             let mut rbo = 0;
             self.gl.GenRenderbuffers(1, &mut rbo);
             self.gl.BindRenderbuffer(ffi::RENDERBUFFER, rbo);
@@ -1640,62 +1608,13 @@ impl Offscreen<GlesRenderbuffer> for GlesRenderer {
     }
 }
 
-impl<Target> Blit<Target> for GlesRenderer
-where
-    Self: Bind<Target>,
-{
-    #[instrument(level = "trace", parent = &self.span, skip(self, to))]
-    #[profiling::function]
-    fn blit_to(
-        &mut self,
-        to: Target,
-        src: Rectangle<i32, Physical>,
-        dst: Rectangle<i32, Physical>,
-        filter: TextureFilter,
-    ) -> Result<(), GlesError> {
-        let src_target = self.target.take().ok_or(GlesError::BlitError)?;
-        self.bind(to)?;
-        let dst_target = self.target.take().unwrap();
-        self.unbind()?;
-
-        let result = self.blit(&src_target, &dst_target, src, dst, filter);
-
-        self.target = Some(src_target);
-        self.make_current()?;
-
-        result
-    }
-
-    #[instrument(level = "trace", parent = &self.span, skip(self, from))]
-    #[profiling::function]
-    fn blit_from(
-        &mut self,
-        from: Target,
-        src: Rectangle<i32, Physical>,
-        dst: Rectangle<i32, Physical>,
-        filter: TextureFilter,
-    ) -> Result<(), GlesError> {
-        let dst_target = self.target.take().ok_or(GlesError::BlitError)?;
-        self.bind(from)?;
-        let src_target = self.target.take().unwrap();
-        self.unbind()?;
-
-        let result = self.blit(&src_target, &dst_target, src, dst, filter);
-
-        self.unbind()?;
-        self.target = Some(dst_target);
-        self.make_current()?;
-
-        result
-    }
-}
-
-impl GlesRenderer {
+impl Blit for GlesRenderer {
+    #[instrument(level = "trace", parent = &self.span, skip(self, src_target, dst_target))]
     #[profiling::function]
     fn blit(
         &mut self,
-        src_target: &GlesTarget,
-        dst_target: &GlesTarget,
+        src_target: &GlesTarget<'_>,
+        dst_target: &mut GlesTarget<'_>,
         src: Rectangle<i32, Physical>,
         dst: Rectangle<i32, Physical>,
         filter: TextureFilter,
@@ -1705,14 +1624,17 @@ impl GlesRenderer {
             return Err(GlesError::GLVersionNotSupported(version::GLES_3_0));
         }
 
-        match (src_target, dst_target) {
-            (GlesTarget::Surface { surface: src, .. }, GlesTarget::Surface { surface: dst, .. }) => unsafe {
+        match (&src_target.0, &dst_target.0) {
+            (
+                GlesTargetInternal::Surface { surface: src, .. },
+                GlesTargetInternal::Surface { surface: dst, .. },
+            ) => unsafe {
                 self.egl.make_current_with_draw_and_read_surface(dst, src)?;
             },
-            (GlesTarget::Surface { surface: src, .. }, _) => unsafe {
+            (GlesTargetInternal::Surface { surface: src, .. }, _) => unsafe {
                 self.egl.make_current_with_surface(src)?;
             },
-            (_, GlesTarget::Surface { surface: dst, .. }) => unsafe {
+            (_, GlesTargetInternal::Surface { surface: dst, .. }) => unsafe {
                 self.egl.make_current_with_surface(dst)?;
             },
             (_, _) => unsafe {
@@ -1720,26 +1642,26 @@ impl GlesRenderer {
             },
         }
 
-        match src_target {
-            GlesTarget::Image { ref buf, .. } => unsafe {
+        match &src_target.0 {
+            GlesTargetInternal::Image { ref buf, .. } => unsafe {
                 self.gl.BindFramebuffer(ffi::READ_FRAMEBUFFER, buf.fbo)
             },
-            GlesTarget::Texture { ref fbo, .. } => unsafe {
+            GlesTargetInternal::Texture { ref fbo, .. } => unsafe {
                 self.gl.BindFramebuffer(ffi::READ_FRAMEBUFFER, *fbo)
             },
-            GlesTarget::Renderbuffer { ref fbo, .. } => unsafe {
+            GlesTargetInternal::Renderbuffer { ref fbo, .. } => unsafe {
                 self.gl.BindFramebuffer(ffi::READ_FRAMEBUFFER, *fbo)
             },
             _ => {} // Note: The only target missing is `Surface` and handled above
         }
-        match dst_target {
-            GlesTarget::Image { ref buf, .. } => unsafe {
+        match &dst_target.0 {
+            GlesTargetInternal::Image { ref buf, .. } => unsafe {
                 self.gl.BindFramebuffer(ffi::DRAW_FRAMEBUFFER, buf.fbo)
             },
-            GlesTarget::Texture { ref fbo, .. } => unsafe {
+            GlesTargetInternal::Texture { ref fbo, .. } => unsafe {
                 self.gl.BindFramebuffer(ffi::DRAW_FRAMEBUFFER, *fbo)
             },
-            GlesTarget::Renderbuffer { ref fbo, .. } => unsafe {
+            GlesTargetInternal::Renderbuffer { ref fbo, .. } => unsafe {
                 self.gl.BindFramebuffer(ffi::DRAW_FRAMEBUFFER, *fbo)
             },
             _ => {} // Note: The only target missing is `Surface` and handled above
@@ -1779,20 +1701,6 @@ impl GlesRenderer {
     }
 }
 
-impl Unbind for GlesRenderer {
-    #[profiling::function]
-    fn unbind(&mut self) -> Result<(), <Self as Renderer>::Error> {
-        unsafe {
-            self.egl.make_current()?;
-        }
-        unsafe { self.gl.BindFramebuffer(ffi::FRAMEBUFFER, 0) };
-        self.target = None;
-        self.cleanup();
-        self.egl.unbind()?;
-        Ok(())
-    }
-}
-
 impl Drop for GlesRenderer {
     fn drop(&mut self) {
         let _guard = self.span.enter();
@@ -1844,7 +1752,9 @@ impl GlesRenderer {
     where
         F: FnOnce(&ffi::Gles2) -> R,
     {
-        self.make_current()?;
+        unsafe {
+            self.egl.make_current()?;
+        }
         Ok(func(&self.gl))
     }
 
@@ -1874,7 +1784,9 @@ impl GlesRenderer {
         src: impl AsRef<str>,
         additional_uniforms: &[UniformName<'_>],
     ) -> Result<GlesPixelProgram, GlesError> {
-        self.make_current()?;
+        unsafe {
+            self.egl.make_current()?;
+        }
 
         let shader = format!("#version 100\n{}", src.as_ref());
         let program = unsafe { link_program(&self.gl, shaders::VERTEX_SHADER, &shader)? };
@@ -2001,7 +1913,9 @@ impl GlesRenderer {
         shader: impl AsRef<str>,
         additional_uniforms: &[UniformName<'_>],
     ) -> Result<GlesTexProgram, GlesError> {
-        self.make_current()?;
+        unsafe {
+            self.egl.make_current()?;
+        }
 
         unsafe {
             texture_program(
@@ -2014,7 +1928,7 @@ impl GlesRenderer {
     }
 }
 
-impl GlesFrame<'_> {
+impl GlesFrame<'_, '_> {
     /// Run custom code in the GL context owned by this renderer.
     ///
     /// The OpenGL state of the renderer is considered an implementation detail
@@ -2032,11 +1946,17 @@ impl GlesFrame<'_> {
     }
 }
 
-impl Renderer for GlesRenderer {
+impl RendererSuper for GlesRenderer {
     type Error = GlesError;
     type TextureId = GlesTexture;
-    type Frame<'frame> = GlesFrame<'frame>;
+    type Framebuffer<'buffer> = GlesTarget<'buffer>;
+    type Frame<'frame, 'buffer>
+        = GlesFrame<'frame, 'buffer>
+    where
+        'buffer: 'frame;
+}
 
+impl Renderer for GlesRenderer {
     fn id(&self) -> usize {
         self.egl.user_data().get::<RendererId>().unwrap().0
     }
@@ -2050,13 +1970,25 @@ impl Renderer for GlesRenderer {
         Ok(())
     }
 
+    fn set_debug_flags(&mut self, flags: DebugFlags) {
+        self.debug_flags = flags;
+    }
+
+    fn debug_flags(&self) -> DebugFlags {
+        self.debug_flags
+    }
+
     #[profiling::function]
-    fn render(
-        &mut self,
+    fn render<'frame, 'buffer>(
+        &'frame mut self,
+        target: &'frame mut GlesTarget<'buffer>,
         mut output_size: Size<i32, Physical>,
         transform: Transform,
-    ) -> Result<GlesFrame<'_>, Self::Error> {
-        self.make_current()?;
+    ) -> Result<GlesFrame<'frame, 'buffer>, GlesError>
+    where
+        'buffer: 'frame,
+    {
+        target.0.make_current(&self.gl, &self.egl)?;
 
         unsafe {
             self.gl.Viewport(0, 0, output_size.w, output_size.h);
@@ -2098,6 +2030,7 @@ impl Renderer for GlesRenderer {
 
         Ok(GlesFrame {
             renderer: self,
+            target,
             // output transformation passed in by the user
             current_projection,
             transform,
@@ -2109,17 +2042,11 @@ impl Renderer for GlesRenderer {
         })
     }
 
-    fn set_debug_flags(&mut self, flags: DebugFlags) {
-        self.debug_flags = flags;
-    }
-
-    fn debug_flags(&self) -> DebugFlags {
-        self.debug_flags
-    }
-
     #[profiling::function]
     fn wait(&mut self, sync: &super::sync::SyncPoint) -> Result<(), Self::Error> {
-        self.make_current()?;
+        unsafe {
+            self.egl.make_current()?;
+        }
 
         let display = self.egl_context().display();
 
@@ -2152,7 +2079,9 @@ impl Renderer for GlesRenderer {
 
     #[profiling::function]
     fn cleanup_texture_cache(&mut self) -> Result<(), Self::Error> {
-        self.make_current()?;
+        unsafe {
+            self.egl.make_current()?;
+        }
         self.cleanup();
         Ok(())
     }
@@ -2213,7 +2142,7 @@ static OUTPUT_VERTS: [ffi::types::GLfloat; 8] = [
     1.0, -1.0, // bottom left
 ];
 
-impl Frame for GlesFrame<'_> {
+impl Frame for GlesFrame<'_, '_> {
     type TextureId = GlesTexture;
     type Error = GlesError;
 
@@ -2313,7 +2242,7 @@ impl Frame for GlesFrame<'_> {
     }
 }
 
-impl GlesFrame<'_> {
+impl GlesFrame<'_, '_> {
     #[profiling::function]
     fn finish_internal(&mut self) -> Result<SyncPoint, GlesError> {
         let _guard = self.span.enter();
@@ -2327,6 +2256,10 @@ impl GlesFrame<'_> {
             self.renderer.gl.Disable(ffi::BLEND);
         }
 
+        if let GlesTargetInternal::Texture { sync_lock, .. } = &mut self.target.0 {
+            sync_lock.update_write(&self.renderer.gl);
+        }
+
         // delayed destruction until the next frame rendering.
         self.renderer.cleanup();
 
@@ -2970,7 +2903,7 @@ impl GlesFrame<'_> {
     }
 }
 
-impl Drop for GlesFrame<'_> {
+impl Drop for GlesFrame<'_, '_> {
     fn drop(&mut self) {
         match self.finish_internal() {
             Ok(sync) => {
diff --git a/src/backend/renderer/gles/texture.rs b/src/backend/renderer/gles/texture.rs
index a88526444524..3450c01daf3d 100644
--- a/src/backend/renderer/gles/texture.rs
+++ b/src/backend/renderer/gles/texture.rs
@@ -93,8 +93,8 @@ impl TextureSync {
 
     pub(super) fn wait_for_all(&mut self, gl: &Gles2) {
         unsafe {
-            wait_for_syncpoint(&mut self.read_sync.get_mut().unwrap(), gl);
-            wait_for_syncpoint(&mut self.write_sync.get_mut().unwrap(), gl);
+            wait_for_syncpoint(self.read_sync.get_mut().unwrap(), gl);
+            wait_for_syncpoint(self.write_sync.get_mut().unwrap(), gl);
         }
     }
 
diff --git a/src/backend/renderer/glow.rs b/src/backend/renderer/glow.rs
index 205ff34ba3e0..9541a66fae0a 100644
--- a/src/backend/renderer/glow.rs
+++ b/src/backend/renderer/glow.rs
@@ -17,7 +17,7 @@ use crate::{
             element::UnderlyingStorage,
             gles::{element::*, *},
             sync, Bind, Blit, Color32F, DebugFlags, ExportMem, ImportDma, ImportMem, Offscreen, Renderer,
-            TextureFilter, Unbind,
+            RendererSuper, TextureFilter,
         },
     },
     utils::{Buffer as BufferCoord, Physical, Rectangle, Size, Transform},
@@ -45,8 +45,8 @@ pub struct GlowRenderer {
 /// [`Frame`] implementation of a [`GlowRenderer`].
 ///
 /// Leaking the frame will cause the same problems as leaking a [`GlesFrame`].
-pub struct GlowFrame<'a> {
-    frame: Option<GlesFrame<'a>>,
+pub struct GlowFrame<'frame, 'buffer> {
+    frame: Option<GlesFrame<'frame, 'buffer>>,
     glow: Arc<Context>,
 }
 
@@ -129,12 +129,14 @@ impl GlowRenderer {
     where
         F: FnOnce(&Arc<Context>) -> R,
     {
-        self.gl.make_current()?;
+        unsafe {
+            self.gl.egl_context().make_current()?;
+        }
         Ok(func(&self.glow))
     }
 }
 
-impl GlowFrame<'_> {
+impl GlowFrame<'_, '_> {
     /// Run custom code in the GL context owned by this renderer.
     ///
     /// The OpenGL state of the renderer is considered an implementation detail
@@ -156,9 +158,9 @@ impl GlowFrame<'_> {
 //  just as `TryFrom<GlesRenderer, Error=GlesError> for GlowRenderer`
 impl From<GlesRenderer> for GlowRenderer {
     #[inline]
-    fn from(mut renderer: GlesRenderer) -> GlowRenderer {
+    fn from(renderer: GlesRenderer) -> GlowRenderer {
         let glow = unsafe {
-            renderer.make_current().unwrap();
+            renderer.egl_context().make_current().unwrap();
             Context::from_loader_function(|s| crate::backend::egl::get_proc_address(s) as *const _)
         };
 
@@ -183,25 +185,32 @@ impl BorrowMut<GlesRenderer> for GlowRenderer {
     }
 }
 
-impl<'frame> Borrow<GlesFrame<'frame>> for GlowFrame<'frame> {
+impl<'frame, 'buffer> Borrow<GlesFrame<'frame, 'buffer>> for GlowFrame<'frame, 'buffer> {
     #[inline]
-    fn borrow(&self) -> &GlesFrame<'frame> {
+    fn borrow(&self) -> &GlesFrame<'frame, 'buffer> {
         self.frame.as_ref().unwrap()
     }
 }
 
-impl<'frame> BorrowMut<GlesFrame<'frame>> for GlowFrame<'frame> {
+impl<'frame, 'buffer> BorrowMut<GlesFrame<'frame, 'buffer>> for GlowFrame<'frame, 'buffer> {
     #[inline]
-    fn borrow_mut(&mut self) -> &mut GlesFrame<'frame> {
+    fn borrow_mut(&mut self) -> &mut GlesFrame<'frame, 'buffer> {
         self.frame.as_mut().unwrap()
     }
 }
 
-impl Renderer for GlowRenderer {
+impl RendererSuper for GlowRenderer {
     type Error = GlesError;
     type TextureId = GlesTexture;
-    type Frame<'frame> = GlowFrame<'frame>;
+    type Frame<'frame, 'buffer>
+        = GlowFrame<'frame, 'buffer>
+    where
+        'buffer: 'frame,
+        Self: 'frame;
+    type Framebuffer<'buffer> = GlesTarget<'buffer>;
+}
 
+impl Renderer for GlowRenderer {
     fn id(&self) -> usize {
         self.gl.id()
     }
@@ -221,13 +230,17 @@ impl Renderer for GlowRenderer {
     }
 
     #[profiling::function]
-    fn render(
-        &mut self,
+    fn render<'frame, 'buffer>(
+        &'frame mut self,
+        target: &'frame mut GlesTarget<'buffer>,
         output_size: Size<i32, Physical>,
         transform: Transform,
-    ) -> Result<GlowFrame<'_>, Self::Error> {
+    ) -> Result<GlowFrame<'frame, 'buffer>, Self::Error>
+    where
+        'buffer: 'frame,
+    {
         let glow = self.glow.clone();
-        let frame = self.gl.render(output_size, transform)?;
+        let frame = self.gl.render(target, output_size, transform)?;
         Ok(GlowFrame {
             frame: Some(frame),
             glow,
@@ -245,7 +258,7 @@ impl Renderer for GlowRenderer {
     }
 }
 
-impl Frame for GlowFrame<'_> {
+impl Frame for GlowFrame<'_, '_> {
     type TextureId = GlesTexture;
     type Error = GlesError;
 
@@ -330,7 +343,7 @@ impl Frame for GlowFrame<'_> {
     }
 }
 
-impl GlowFrame<'_> {
+impl GlowFrame<'_, '_> {
     #[profiling::function]
     fn finish_internal(&mut self) -> Result<sync::SyncPoint, GlesError> {
         if let Some(frame) = self.frame.take() {
@@ -341,7 +354,7 @@ impl GlowFrame<'_> {
     }
 }
 
-impl Drop for GlowFrame<'_> {
+impl Drop for GlowFrame<'_, '_> {
     fn drop(&mut self) {
         if let Err(err) = self.finish_internal() {
             warn!("Ignored error finishing GlowFrame on drop: {}", err);
@@ -381,10 +394,10 @@ impl ImportMem for GlowRenderer {
     #[profiling::function]
     fn update_memory(
         &mut self,
-        texture: &<Self as Renderer>::TextureId,
+        texture: &Self::TextureId,
         data: &[u8],
         region: Rectangle<i32, BufferCoord>,
-    ) -> Result<(), <Self as Renderer>::Error> {
+    ) -> Result<(), Self::Error> {
         self.gl.update_memory(texture, data, region)
     }
 
@@ -451,10 +464,11 @@ impl ExportMem for GlowRenderer {
     #[profiling::function]
     fn copy_framebuffer(
         &mut self,
+        from: &GlesTarget<'_>,
         region: Rectangle<i32, BufferCoord>,
         format: Fourcc,
     ) -> Result<Self::TextureMapping, Self::Error> {
-        self.gl.copy_framebuffer(region, format)
+        self.gl.copy_framebuffer(from, region, format)
     }
 
     #[profiling::function]
@@ -485,7 +499,7 @@ where
     GlesRenderer: Bind<T>,
 {
     #[profiling::function]
-    fn bind(&mut self, target: T) -> Result<(), GlesError> {
+    fn bind<'a>(&mut self, target: &'a mut T) -> Result<GlesTarget<'a>, GlesError> {
         self.gl.bind(target)
     }
     fn supported_formats(&self) -> Option<FormatSet> {
@@ -503,36 +517,17 @@ where
     }
 }
 
-impl<Target> Blit<Target> for GlowRenderer
-where
-    GlesRenderer: Blit<Target>,
-{
+impl Blit for GlowRenderer {
     #[profiling::function]
-    fn blit_to(
+    fn blit(
         &mut self,
-        to: Target,
+        from: &GlesTarget<'_>,
+        to: &mut GlesTarget<'_>,
         src: Rectangle<i32, Physical>,
         dst: Rectangle<i32, Physical>,
         filter: TextureFilter,
     ) -> Result<(), GlesError> {
-        self.gl.blit_to(to, src, dst, filter)
-    }
-
-    #[profiling::function]
-    fn blit_from(
-        &mut self,
-        from: Target,
-        src: Rectangle<i32, Physical>,
-        dst: Rectangle<i32, Physical>,
-        filter: TextureFilter,
-    ) -> Result<(), GlesError> {
-        self.gl.blit_from(from, src, dst, filter)
-    }
-}
-
-impl Unbind for GlowRenderer {
-    fn unbind(&mut self) -> Result<(), <Self as Renderer>::Error> {
-        self.gl.unbind()
+        self.gl.blit(from, to, src, dst, filter)
     }
 }
 
@@ -540,7 +535,7 @@ impl RenderElement<GlowRenderer> for PixelShaderElement {
     #[profiling::function]
     fn draw(
         &self,
-        frame: &mut GlowFrame<'_>,
+        frame: &mut GlowFrame<'_, '_>,
         src: Rectangle<f64, BufferCoord>,
         dst: Rectangle<i32, Physical>,
         damage: &[Rectangle<i32, Physical>],
@@ -558,7 +553,7 @@ impl RenderElement<GlowRenderer> for TextureShaderElement {
     #[profiling::function]
     fn draw(
         &self,
-        frame: &mut GlowFrame<'_>,
+        frame: &mut GlowFrame<'_, '_>,
         src: Rectangle<f64, BufferCoord>,
         dst: Rectangle<i32, Physical>,
         damage: &[Rectangle<i32, Physical>],
diff --git a/src/backend/renderer/mod.rs b/src/backend/renderer/mod.rs
index 7318acd506c7..76dcf506de6b 100644
--- a/src/backend/renderer/mod.rs
+++ b/src/backend/renderer/mod.rs
@@ -54,7 +54,9 @@ pub mod damage;
 
 pub mod sync;
 
-#[cfg(feature = "renderer_test")]
+// Note: This doesn't fully work yet due to <https://github.com/rust-lang/rust/issues/67295>.
+// Use `--features renderer_test` when running doc tests manually.
+#[cfg(any(feature = "renderer_test", test, doctest))]
 pub mod test;
 
 #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
@@ -103,27 +105,23 @@ impl From<wayland_server::protocol::wl_output::Transform> for Transform {
 }
 
 /// Abstraction for Renderers, that can render into different targets
-pub trait Bind<Target>: Unbind {
-    /// Bind a given rendering target, which will contain the rendering results until `unbind` is called.
+pub trait Bind<Target>: Renderer {
+    /// Initialize a framebuffer with a given rendering target.
     ///
-    /// Binding to target, while another one is already bound, is rendering defined.
-    /// Some renderers might happily replace the current target, while other might drop the call
-    /// or throw an error.
-    fn bind(&mut self, target: Target) -> Result<(), <Self as Renderer>::Error>;
+    /// The `output_size` specifies the dimensions of the display **before** the `dst_transform` is
+    /// applied.
+    ///
+    /// This function *may* error, if:
+    /// - The given dimensions are unsupported (too large) for this renderer
+    /// - The given Transformation is not supported by the renderer (`Transform::Normal` is always supported).
+    fn bind<'a>(&mut self, target: &'a mut Target) -> Result<Self::Framebuffer<'a>, Self::Error>;
+
     /// Supported pixel formats for given targets, if applicable.
     fn supported_formats(&self) -> Option<FormatSet> {
         None
     }
 }
 
-/// Functionality to unbind the current rendering target
-pub trait Unbind: Renderer {
-    /// Unbind the current rendering target.
-    ///
-    /// May fall back to a default target, if defined by the implementation.
-    fn unbind(&mut self) -> Result<(), <Self as Renderer>::Error>;
-}
-
 /// A two dimensional texture
 pub trait Texture: fmt::Debug {
     /// Size of the texture plane
@@ -263,17 +261,26 @@ bitflags::bitflags! {
         const TINT = 0b00000001;
     }
 }
-/// Abstraction of commonly used rendering operations for compositors.
-pub trait Renderer: fmt::Debug {
+
+/// Workaround for <https://github.com/rust-lang/rust/issues/87479>, please look at [`Renderer`] instead.
+pub trait RendererSuper: fmt::Debug {
     /// Error type returned by the rendering operations of this renderer.
     type Error: Error;
     /// Texture Handle type used by this renderer.
     type TextureId: Texture;
+    /// Framebuffer to draw onto
+    type Framebuffer<'buffer>;
     /// Type representing a currently in-progress frame during the [`Renderer::render`]-call
-    type Frame<'frame>: Frame<Error = Self::Error, TextureId = Self::TextureId> + 'frame
+    type Frame<'frame, 'buffer>: Frame<Error = Self::Error, TextureId = Self::TextureId>
     where
+        'buffer: 'frame,
         Self: 'frame;
+}
 
+/// Abstraction of commonly used rendering operations for compositors.
+///
+/// *Note*: Associated types are defined in [`RendererSuper`].
+pub trait Renderer: RendererSuper {
     /// Returns an id, that is unique to all renderers, that can use
     /// `TextureId`s originating from any of these renderers.
     fn id(&self) -> usize;
@@ -288,7 +295,7 @@ pub trait Renderer: fmt::Debug {
     /// Returns the current enabled [`DebugFlags`]
     fn debug_flags(&self) -> DebugFlags;
 
-    /// Initialize a rendering context on the current rendering target with given dimensions and transformation.
+    /// Initialize a rendering context on the provided framebuffer with given dimensions and transformation.
     ///
     /// The `output_size` specifies the dimensions of the display **before** the `dst_transform` is
     /// applied.
@@ -296,13 +303,14 @@ pub trait Renderer: fmt::Debug {
     /// This function *may* error, if:
     /// - The given dimensions are unsupported (too large) for this renderer
     /// - The given Transformation is not supported by the renderer (`Transform::Normal` is always supported).
-    /// - This renderer implements `Bind`, no target was bound *and* has no default target.
-    /// - (Renderers not implementing `Bind` always have a default target.)
-    fn render(
-        &mut self,
+    fn render<'frame, 'buffer>(
+        &'frame mut self,
+        framebuffer: &'frame mut Self::Framebuffer<'buffer>,
         output_size: Size<i32, Physical>,
         dst_transform: Transform,
-    ) -> Result<Self::Frame<'_>, Self::Error>;
+    ) -> Result<Self::Frame<'frame, 'buffer>, Self::Error>
+    where
+        'buffer: 'frame;
 
     /// Wait for a [`SyncPoint`](sync::SyncPoint) to be signaled
     fn wait(&mut self, sync: &sync::SyncPoint) -> Result<(), Self::Error>;
@@ -329,11 +337,7 @@ pub trait Offscreen<Target>: Renderer + Bind<Target> {
     /// - The maximum amount of framebuffers for this renderer would be exceeded
     /// - The format is not supported to be rendered into
     /// - The size is too large for a framebuffer
-    fn create_buffer(
-        &mut self,
-        format: Fourcc,
-        size: Size<i32, BufferCoord>,
-    ) -> Result<Target, <Self as Renderer>::Error>;
+    fn create_buffer(&mut self, format: Fourcc, size: Size<i32, BufferCoord>) -> Result<Target, Self::Error>;
 }
 
 /// Trait for Renderers supporting importing wl_buffers using shared memory.
@@ -359,7 +363,7 @@ pub trait ImportMemWl: ImportMem {
         buffer: &wl_buffer::WlBuffer,
         surface: Option<&crate::wayland::compositor::SurfaceData>,
         damage: &[Rectangle<i32, BufferCoord>],
-    ) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error>;
+    ) -> Result<Self::TextureId, Self::Error>;
 
     /// Returns supported formats for shared memory buffers.
     ///
@@ -393,7 +397,7 @@ pub trait ImportMem: Renderer {
         format: Fourcc,
         size: Size<i32, BufferCoord>,
         flipped: bool,
-    ) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error>;
+    ) -> Result<Self::TextureId, Self::Error>;
 
     /// Update a portion of a given chunk of memory into an existing texture.
     ///
@@ -411,10 +415,10 @@ pub trait ImportMem: Renderer {
     ///   to support resizing the original texture.
     fn update_memory(
         &mut self,
-        texture: &<Self as Renderer>::TextureId,
+        texture: &Self::TextureId,
         data: &[u8],
         region: Rectangle<i32, BufferCoord>,
-    ) -> Result<(), <Self as Renderer>::Error>;
+    ) -> Result<(), Self::Error>;
 
     /// Returns supported formats for memory imports.
     fn mem_formats(&self) -> Box<dyn Iterator<Item = Fourcc>>;
@@ -472,7 +476,7 @@ pub trait ImportEgl: Renderer {
         buffer: &wl_buffer::WlBuffer,
         surface: Option<&crate::wayland::compositor::SurfaceData>,
         damage: &[Rectangle<i32, BufferCoord>],
-    ) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error>;
+    ) -> Result<Self::TextureId, Self::Error>;
 }
 
 #[cfg(feature = "wayland_frontend")]
@@ -494,7 +498,7 @@ pub trait ImportDmaWl: ImportDma {
         buffer: &wl_buffer::WlBuffer,
         _surface: Option<&crate::wayland::compositor::SurfaceData>,
         damage: &[Rectangle<i32, BufferCoord>],
-    ) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error> {
+    ) -> Result<Self::TextureId, Self::Error> {
         let dmabuf = crate::wayland::dmabuf::get_dmabuf(buffer)
             .expect("import_dma_buffer without checking buffer type?");
         self.import_dmabuf(dmabuf, Some(damage))
@@ -528,7 +532,7 @@ pub trait ImportDma: Renderer {
         &mut self,
         dmabuf: &Dmabuf,
         damage: Option<&[Rectangle<i32, BufferCoord>]>,
-    ) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error>;
+    ) -> Result<Self::TextureId, Self::Error>;
 }
 
 // TODO: Replace this with a trait_alias, once that is stabilized.
@@ -560,7 +564,7 @@ pub trait ImportAll: Renderer {
         buffer: &wl_buffer::WlBuffer,
         surface: Option<&crate::wayland::compositor::SurfaceData>,
         damage: &[Rectangle<i32, BufferCoord>],
-    ) -> Option<Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error>>;
+    ) -> Option<Result<Self::TextureId, Self::Error>>;
 }
 
 // TODO: Do this with specialization, when possible and do default implementations
@@ -576,7 +580,7 @@ impl<R: Renderer + ImportMemWl + ImportEgl + ImportDmaWl> ImportAll for R {
         buffer: &wl_buffer::WlBuffer,
         surface: Option<&SurfaceData>,
         damage: &[Rectangle<i32, BufferCoord>],
-    ) -> Option<Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error>> {
+    ) -> Option<Result<Self::TextureId, Self::Error>> {
         match buffer_type(buffer) {
             Some(BufferType::Shm) => Some(self.import_shm_buffer(buffer, surface, damage)),
             Some(BufferType::Egl) => Some(self.import_egl_buffer(buffer, surface, damage)),
@@ -596,7 +600,7 @@ impl<R: Renderer + ImportMemWl + ImportDmaWl> ImportAll for R {
         buffer: &wl_buffer::WlBuffer,
         surface: Option<&SurfaceData>,
         damage: &[Rectangle<i32, BufferCoord>],
-    ) -> Option<Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error>> {
+    ) -> Option<Result<Self::TextureId, Self::Error>> {
         match buffer_type(buffer) {
             Some(BufferType::Shm) => Some(self.import_shm_buffer(buffer, surface, damage)),
             Some(BufferType::Dma) => Some(self.import_dma_buffer(buffer, surface, damage)),
@@ -610,7 +614,7 @@ pub trait ExportMem: Renderer {
     /// Texture type representing a downloaded pixel buffer.
     type TextureMapping: TextureMapping;
 
-    /// Copies the contents of the currently bound framebuffer.
+    /// Copies the contents of the provided target.
     ///
     /// This operation is not destructive, the contents of the framebuffer keep being valid.
     ///
@@ -621,9 +625,10 @@ pub trait ExportMem: Renderer {
     /// - It is not possible to convert the framebuffer into the provided format.
     fn copy_framebuffer(
         &mut self,
+        target: &Self::Framebuffer<'_>,
         region: Rectangle<i32, BufferCoord>,
         format: Fourcc,
-    ) -> Result<Self::TextureMapping, <Self as Renderer>::Error>;
+    ) -> Result<Self::TextureMapping, Self::Error>;
 
     /// Copies the contents of the passed texture.
     /// *Note*: This function may change or invalidate the current bind.
@@ -659,64 +664,36 @@ pub trait ExportMem: Renderer {
     ///
     /// This function *may* fail, if (but not limited to):
     /// - There is not enough space in memory
-    fn map_texture<'a>(
-        &mut self,
-        texture_mapping: &'a Self::TextureMapping,
-    ) -> Result<&'a [u8], <Self as Renderer>::Error>;
+    fn map_texture<'a>(&mut self, texture_mapping: &'a Self::TextureMapping)
+        -> Result<&'a [u8], Self::Error>;
 }
 
 /// Trait for renderers supporting blitting contents from one framebuffer to another.
-pub trait Blit<Target>
+pub trait Blit
 where
-    Self: Renderer + Bind<Target>,
+    Self: Renderer,
 {
-    /// Copies the contents of `src` in the current bound framebuffer to `dst` in Target,
-    /// applying `filter` if necessary.
-    ///
-    /// This operation is non destructive, the contents of the source framebuffer
-    /// are kept intact as is any region not in `dst` for the target framebuffer.
-    ///
-    /// This operation needs a bound or default rendering target.
-    /// The currently bound target is guaranteed to still be active after this operation.
-    ///
-    /// This function *may* fail, if (but not limited to):
-    /// - The source framebuffer is not readable / unset
-    /// - The destination framebuffer is not writable
-    /// - `src` is out of bounds for the source framebuffer
-    /// - `dst` is out of bounds for the destination framebuffer
-    /// - `src` and `dst` sizes are different and interpolation id not supported by this renderer.
-    /// - source and target framebuffer are the same, and `src` and `dst` overlap
-    fn blit_to(
-        &mut self,
-        to: Target,
-        src: Rectangle<i32, Physical>,
-        dst: Rectangle<i32, Physical>,
-        filter: TextureFilter,
-    ) -> Result<(), <Self as Renderer>::Error>;
-
-    /// Copies the contents of `src` in Target to `dst` of the current bound framebuffer,
+    /// Copies the contents of `src` from one provided target to `dst` in the other provided target,
     /// applying `filter` if necessary.
     ///
     /// This operation is non destructive, the contents of the source framebuffer
     /// are kept intact as is any region not in `dst` for the target framebuffer.
     ///
-    /// This operation needs a bound or default rendering target.
-    /// The currently bound target is guaranteed to still be active after this operation.
-    ///
     /// This function *may* fail, if (but not limited to):
     /// - The source framebuffer is not readable
-    /// - The destination framebuffer is not writable / unset
+    /// - The destination framebuffer is not writable
     /// - `src` is out of bounds for the source framebuffer
     /// - `dst` is out of bounds for the destination framebuffer
     /// - `src` and `dst` sizes are different and interpolation id not supported by this renderer.
     /// - source and target framebuffer are the same, and `src` and `dst` overlap
-    fn blit_from(
+    fn blit(
         &mut self,
-        from: Target,
+        from: &Self::Framebuffer<'_>,
+        to: &mut Self::Framebuffer<'_>,
         src: Rectangle<i32, Physical>,
         dst: Rectangle<i32, Physical>,
         filter: TextureFilter,
-    ) -> Result<(), <Self as Renderer>::Error>;
+    ) -> Result<(), Self::Error>;
 }
 
 #[cfg(feature = "wayland_frontend")]
diff --git a/src/backend/renderer/multigpu/gbm.rs b/src/backend/renderer/multigpu/gbm.rs
index e89b2fc1928d..32e75a1c2bca 100644
--- a/src/backend/renderer/multigpu/gbm.rs
+++ b/src/backend/renderer/multigpu/gbm.rs
@@ -16,7 +16,7 @@ use crate::backend::{
     renderer::{
         gles::{GlesError, GlesRenderer},
         multigpu::{ApiDevice, Error as MultiError, GraphicsApi},
-        Renderer,
+        Renderer, RendererSuper,
     },
     SwapBuffersError,
 };
@@ -223,7 +223,7 @@ impl<T: GraphicsApi, R: From<GlesRenderer> + Renderer<Error = GlesError>, A: AsF
     std::convert::From<GlesError> for MultiError<GbmGlesBackend<R, A>, T>
 where
     T::Error: 'static,
-    <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
+    <<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
 {
     #[inline]
     fn from(err: GlesError) -> MultiError<GbmGlesBackend<R, A>, T> {
@@ -278,7 +278,7 @@ where
         + ImportEgl
         + ExportMem
         + 'static,
-    <R as Renderer>::TextureId: Clone + Send,
+    R::TextureId: Clone + Send,
 {
     fn bind_wl_display(&mut self, display: &wayland_server::DisplayHandle) -> Result<(), EGLError> {
         self.render.renderer_mut().bind_wl_display(display)
@@ -296,7 +296,7 @@ where
         buffer: &wl_buffer::WlBuffer,
         surface: Option<&crate::wayland::compositor::SurfaceData>,
         damage: &[Rectangle<i32, BufferCoords>],
-    ) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error> {
+    ) -> Result<Self::TextureId, Self::Error> {
         if let Some(dmabuf) = Self::try_import_egl(self.render.renderer_mut(), buffer)
             .ok()
             .or_else(|| {
@@ -360,11 +360,14 @@ where
             )));
         }
 
-        renderer
-            .borrow_mut()
-            .make_current()
-            .map_err(GlesError::from)
-            .map_err(MultigpuError::Render)?;
+        unsafe {
+            renderer
+                .borrow_mut()
+                .egl_context()
+                .make_current()
+                .map_err(GlesError::from)
+                .map_err(MultigpuError::Render)?
+        };
 
         let egl = renderer
             .egl_reader()
diff --git a/src/backend/renderer/multigpu/mod.rs b/src/backend/renderer/multigpu/mod.rs
index 035ce3b24acc..218d0155f0c0 100644
--- a/src/backend/renderer/multigpu/mod.rs
+++ b/src/backend/renderer/multigpu/mod.rs
@@ -39,6 +39,7 @@
 //! 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,
@@ -48,7 +49,7 @@ use std::{
 
 use super::{
     sync::SyncPoint, Bind, Blit, Color32F, DebugFlags, ExportMem, Frame, ImportDma, ImportMem, Offscreen,
-    Renderer, Texture, TextureFilter, TextureMapping, Unbind,
+    Renderer, RendererSuper, Texture, TextureFilter, TextureMapping,
 };
 #[cfg(feature = "wayland_frontend")]
 use super::{ImportDmaWl, ImportMemWl};
@@ -93,8 +94,8 @@ pub enum Error<R: GraphicsApi, T: GraphicsApi>
 where
     R::Error: 'static,
     T::Error: 'static,
-    <<R::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
-    <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
+    <<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:?}")]
@@ -113,10 +114,10 @@ where
     DeviceMissing,
     /// Error on the rendering device
     #[error("Error on the rendering device: {0:}")]
-    Render(#[source] <<R::Device as ApiDevice>::Renderer as Renderer>::Error),
+    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 Renderer>::Error),
+    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,
@@ -161,8 +162,8 @@ impl<R: GraphicsApi, T: GraphicsApi> fmt::Debug for Error<R, T>
 where
     R::Error: 'static,
     T::Error: 'static,
-    <<R::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
-    <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
+    <<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 {
@@ -183,8 +184,8 @@ impl<R: GraphicsApi + 'static, T: GraphicsApi + 'static> From<Error<R, T>> for S
 where
     R::Error: Into<SwapBuffersError> + Send + Sync,
     T::Error: Into<SwapBuffersError> + Send + Sync,
-    <<R::Device as ApiDevice>::Renderer as Renderer>::Error: Into<SwapBuffersError> + Send + Sync,
-    <<T::Device as ApiDevice>::Renderer as Renderer>::Error: Into<SwapBuffersError> + Send + Sync,
+    <<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 {
@@ -585,7 +586,8 @@ impl<A: GraphicsApi> GpuManager<A> {
 
                     let src_texture = match api_textures.get(&src_node).unwrap() {
                         GpuSingleTexture::Direct(tex) => tex
-                            .downcast_ref::<<<A::Device as ApiDevice>::Renderer as Renderer>::TextureId>()
+                            .downcast_ref::<<<A::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>(
+                            )
                             .unwrap(),
                         _ => unreachable!(),
                     };
@@ -753,6 +755,26 @@ impl<R: GraphicsApi, T: GraphicsApi> AsMut<<R::Device as ApiDevice>::Renderer>
     }
 }
 
+/// 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),
+        }
+    }
+}
+
 /// [`Frame`] implementation of a [`MultiRenderer`].
 ///
 /// Leaking the frame will potentially keep it from doing necessary copies
@@ -760,21 +782,23 @@ impl<R: GraphicsApi, T: GraphicsApi> AsMut<<R::Device as ApiDevice>::Renderer>
 /// 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, R: GraphicsApi + 'frame, T: GraphicsApi>
+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 Renderer>::Error: 'static,
-    <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
+    <<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 Renderer>::Frame<'frame>>,
+    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,
-    target: &'frame mut Option<TargetData<'target, T>>,
-    target_texture: Option<<<T::Device as ApiDevice>::Renderer as Renderer>::TextureId>,
 
     dst_transform: Transform,
     size: Size<i32, Physical>,
@@ -788,16 +812,25 @@ struct TargetData<'target, T: GraphicsApi> {
     format: Fourcc,
 }
 
-impl<'frame, R: GraphicsApi + 'frame, T: GraphicsApi> fmt::Debug for MultiFrame<'_, '_, 'frame, R, T>
+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 Renderer>::Error: 'static,
-    <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
-    <<T::Device as ApiDevice>::Renderer as Renderer>::TextureId: fmt::Debug,
+    <<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,
 {
@@ -816,7 +849,7 @@ where
 impl<T: GraphicsApi> fmt::Debug for TargetData<'_, T>
 where
     T::Device: fmt::Debug,
-    <<T::Device as ApiDevice>::Renderer as Renderer>::TextureId: 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")
@@ -827,68 +860,60 @@ where
     }
 }
 
+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, R: GraphicsApi, T: GraphicsApi>
-    AsRef<<<R::Device as ApiDevice>::Renderer as Renderer>::Frame<'frame>>
-    for MultiFrame<'_, '_, 'frame, R, T>
+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 Renderer>::Error: 'static,
-    <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
+    <<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 Renderer>::Frame<'frame> {
+    fn as_ref(&self) -> &<<R::Device as ApiDevice>::Renderer as RendererSuper>::Frame<'frame, 'buffer> {
         self.frame.as_ref().unwrap()
     }
 }
 
-impl<'frame, R: GraphicsApi, T: GraphicsApi>
-    AsMut<<<R::Device as ApiDevice>::Renderer as Renderer>::Frame<'frame>>
-    for MultiFrame<'_, '_, 'frame, R, T>
+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 Renderer>::Error: 'static,
-    <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
+    <<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 Renderer>::Frame<'frame> {
+    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> Unbind for MultiRenderer<'_, '_, R, T>
-where
-    <T::Device as ApiDevice>::Renderer: Unbind,
-    <R::Device as ApiDevice>::Renderer: Unbind,
-    // We need this because the Renderer-impl does and Unbind requires Renderer
-    R: 'static,
-    R::Error: 'static,
-    T::Error: 'static,
-    <R::Device as ApiDevice>::Renderer: Bind<Dmabuf> + ExportMem + ImportDma + ImportMem,
-    <T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
-    <<R::Device as ApiDevice>::Renderer as Renderer>::TextureId: Clone + Send,
-    <<R::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
-    <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
-{
-    #[instrument(level = "trace", parent = &self.span, skip(self))]
-    #[profiling::function]
-    fn unbind(&mut self) -> Result<(), <Self as Renderer>::Error> {
-        if let Some(target) = self.target.as_mut() {
-            target.device.renderer_mut().unbind().map_err(Error::Target)
-        } else {
-            self.render.renderer_mut().unbind().map_err(Error::Render)
-        }
-    }
-}
-
 impl<R: GraphicsApi, T: GraphicsApi, Target> Offscreen<Target> for MultiRenderer<'_, '_, R, T>
 where
     <T::Device as ApiDevice>::Renderer: Offscreen<Target>,
@@ -896,18 +921,15 @@ where
     // We need these because the Bind-impl does and Offscreen requires Bind
     <T::Device as ApiDevice>::Renderer: Bind<Target>,
     <R::Device as ApiDevice>::Renderer: Bind<Target>,
-    // We need these because the Unbind-impl does and Offscreen requires Bind, which requires Unbind
-    <R::Device as ApiDevice>::Renderer: Unbind,
-    <T::Device as ApiDevice>::Renderer: Unbind,
     // We need these because the Renderer-impl does and Offscreen requires Bind, which requires Unbind, which requires Renderer
     R: 'static,
     R::Error: 'static,
     T::Error: 'static,
     <R::Device as ApiDevice>::Renderer: Bind<Dmabuf> + ExportMem + ImportDma + ImportMem,
     <T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
-    <<R::Device as ApiDevice>::Renderer as Renderer>::TextureId: Clone + Send,
-    <<R::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
-    <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
+    <<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]
@@ -915,7 +937,7 @@ where
         &mut self,
         format: Fourcc,
         size: Size<i32, BufferCoords>,
-    ) -> Result<Target, <Self as Renderer>::Error> {
+    ) -> Result<Target, <Self as RendererSuper>::Error> {
         if let Some(target) = self.target.as_mut() {
             target
                 .device
@@ -935,26 +957,37 @@ impl<R: GraphicsApi, T: GraphicsApi, Target> Bind<Target> for MultiRenderer<'_,
 where
     <T::Device as ApiDevice>::Renderer: Bind<Target>,
     <R::Device as ApiDevice>::Renderer: Bind<Target>,
-    // We need these because the Unbind-impl does and Bind requires Unbind
-    <R::Device as ApiDevice>::Renderer: Unbind,
-    <T::Device as ApiDevice>::Renderer: Unbind,
     // We need this because the Renderer-impl does and Bind requires Unbind, which requires Renderer
     R: 'static,
     R::Error: 'static,
     T::Error: 'static,
     <R::Device as ApiDevice>::Renderer: Bind<Dmabuf> + ExportMem + ImportDma + ImportMem,
     <T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
-    <<R::Device as ApiDevice>::Renderer as Renderer>::TextureId: Clone + Send,
-    <<R::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
-    <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
+    <<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(&mut self, bind: Target) -> Result<(), <Self as Renderer>::Error> {
+    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_err(Error::Target)
+            target
+                .device
+                .renderer_mut()
+                .bind(bind)
+                .map(MultiFramebufferInternal::Target)
+                .map(MultiFramebuffer)
+                .map_err(Error::Target)
         } else {
-            self.render.renderer_mut().bind(bind).map_err(Error::Render)
+            self.render
+                .renderer_mut()
+                .bind(bind)
+                .map(MultiFramebufferInternal::Render)
+                .map(MultiFramebuffer)
+                .map_err(Error::Render)
         }
     }
 
@@ -969,24 +1002,38 @@ where
 
 static MAX_CPU_COPIES: usize = 3; // TODO, benchmark this
 
-impl<'render, 'target, R: GraphicsApi, T: GraphicsApi> Renderer for MultiRenderer<'render, 'target, R, T>
+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 Renderer>::TextureId: Clone + Send,
-    <<R::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
-    <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
+    <<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 Frame<'frame>
-        = MultiFrame<'render, 'target, 'frame, R, T>
+    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 id(&self) -> usize {
         self.render.renderer().id()
     }
@@ -1011,14 +1058,18 @@ where
         self.render.renderer().debug_flags()
     }
 
-    #[instrument(level = "trace", parent = &self.span, skip(self))]
+    #[instrument(level = "trace", parent = &self.span, skip(self, framebuffer))]
     #[profiling::function]
-    fn render<'frame>(
+    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, R, T>, Self::Error> {
-        let target_texture = if let Some(target) = self.target.as_mut() {
+    ) -> 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 {
@@ -1046,7 +1097,7 @@ where
                             .filter(|format| format.code == target.format)
                             .map(|f| f.modifier)
                             .collect::<Vec<_>>();
-                        let dmabuf = self
+                        let mut dmabuf = self
                             .render
                             .allocator()
                             .create_buffer(
@@ -1057,18 +1108,29 @@ where
                             )
                             .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, dmabuf) = target.cached_buffer.as_mut().unwrap();
-            self.render
-                .renderer_mut()
-                .bind(dmabuf.clone())
-                .map_err(Error::Render)?;
-            (*direct)
+            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
@@ -1076,35 +1138,70 @@ where
                         .import_dmabuf(dmabuf, Some(&[Rectangle::from_size(buffer_size)]))
                         .map_err(Error::Target)
                 })
-                .transpose()?
+                .transpose()?;
+            let framebuffer = self.render.renderer_mut().bind(dmabuf).map_err(Error::Render)?;
 
-            // TODO: We could cache that texture all the way back to the GpuManager in a HashMap<WeakDmabuf, Texture>.
+            Some((&mut target.device, framebuffer, texture, target.format))
         } else {
             None
         };
 
         let node = *self.render.node();
-
         let ptr = &mut self.render as *mut _;
-        let frame = self
-            .render
-            .renderer_mut()
-            .render(size, dst_transform)
-            .map_err(Error::Render)?;
+
+        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_texture.is_some(),
+            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: &mut self.target,
-            target_texture,
+            target,
             dst_transform,
             size,
             damage: Vec::new(),
@@ -1145,8 +1242,8 @@ where
     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 Renderer>::Error: 'static,
-    <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
+    <<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()
@@ -1172,7 +1269,7 @@ where
         target_modifiers,
     );
 
-    let dmabuf = src
+    let mut dmabuf = src
         .allocator()
         .create_buffer(
             buffer_size.w as u32,
@@ -1184,7 +1281,7 @@ where
 
     // verify we can bind on src and import on target
 
-    src.renderer_mut().bind(dmabuf.clone()).map_err(Error::Render)?;
+    src.renderer_mut().bind(&mut dmabuf).map_err(Error::Render)?;
 
     target
         .device
@@ -1195,15 +1292,15 @@ where
     Ok(dmabuf)
 }
 
-impl<R: GraphicsApi, T: GraphicsApi> MultiFrame<'_, '_, '_, R, T>
+impl<R: GraphicsApi, T: GraphicsApi> 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 Renderer>::Error: 'static,
-    <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
+    <<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]
@@ -1228,7 +1325,7 @@ where
 
             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) = self.target_texture.as_ref() {
+                if let Some(texture) = target.texture.take() {
                     // try gpu copy
                     let damage = damage
                         .iter()
@@ -1237,7 +1334,7 @@ where
                     let mut frame = target
                         .device
                         .renderer_mut()
-                        .render(self.size, Transform::Normal)
+                        .render(target.framebuffer, self.size, Transform::Normal)
                         .map_err(Error::Target)?;
                     frame.wait(&sync).map_err(Error::Target)?;
                     frame
@@ -1245,7 +1342,7 @@ where
                         .map_err(Error::Target)?;
                     frame
                         .render_texture_from_to(
-                            texture,
+                            &texture,
                             Rectangle::from_size(buffer_size).to_f64(),
                             Rectangle::from_size(self.size),
                             &damage,
@@ -1300,8 +1397,13 @@ where
                 let mut mappings = Vec::new();
                 for rect in copy_rects {
                     let mapping = (
-                        ExportMem::copy_framebuffer(render.renderer_mut(), rect, format)
-                            .map_err(Error::Render)?,
+                        ExportMem::copy_framebuffer(
+                            render.renderer_mut(),
+                            self.framebuffer.as_ref().unwrap(),
+                            rect,
+                            format,
+                        )
+                        .map_err(Error::Render)?,
                         rect,
                     );
                     mappings.push(mapping);
@@ -1332,7 +1434,7 @@ where
                 let mut frame = target
                     .device
                     .renderer_mut()
-                    .render(self.size, Transform::Normal)
+                    .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)) {
@@ -1376,15 +1478,15 @@ where
     }
 }
 
-impl<R: GraphicsApi, T: GraphicsApi> Drop for MultiFrame<'_, '_, '_, R, T>
+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 Renderer>::Error: 'static,
-    <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
+    <<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() {
@@ -1478,9 +1580,9 @@ impl MultiTexture {
     pub fn get<A: GraphicsApi + 'static>(
         &self,
         render: &DrmNode,
-    ) -> Option<<<A::Device as ApiDevice>::Renderer as Renderer>::TextureId>
+    ) -> Option<<<A::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>
     where
-        <<A::Device as ApiDevice>::Renderer as Renderer>::TextureId: Clone + Send + 'static,
+        <<A::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: Clone + Send + 'static,
     {
         let tex = self.0.lock().unwrap();
         tex.textures
@@ -1492,7 +1594,7 @@ impl MultiTexture {
                 GpuSingleTexture::Mem { texture, .. } => texture.as_ref(),
             })
             .and_then(|texture| {
-                <dyn Any>::downcast_ref::<<<A::Device as ApiDevice>::Renderer as Renderer>::TextureId>(
+                <dyn Any>::downcast_ref::<<<A::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>(
                     &**texture,
                 )
             })
@@ -1501,7 +1603,7 @@ impl MultiTexture {
 
     fn needs_synchronization<A: GraphicsApi + 'static>(&self, render: &DrmNode) -> Option<SyncPoint>
     where
-        <<A::Device as ApiDevice>::Renderer as Renderer>::TextureId: 'static,
+        <<A::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: 'static,
     {
         let mut tex = self.0.lock().unwrap();
         tex.textures
@@ -1517,9 +1619,9 @@ impl MultiTexture {
     fn insert_texture<A: GraphicsApi + 'static>(
         &mut self,
         render: DrmNode,
-        texture: <<A::Device as ApiDevice>::Renderer as Renderer>::TextureId,
+        texture: <<A::Device as ApiDevice>::Renderer as RendererSuper>::TextureId,
     ) where
-        <<A::Device as ApiDevice>::Renderer as Renderer>::TextureId: 'static,
+        <<A::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: 'static,
     {
         let mut tex = self.0.lock().unwrap();
         let format = texture.format();
@@ -1575,7 +1677,7 @@ impl MultiTexture {
             })
             .unwrap_or((None, None, None));
         let old_texture = old_texture.filter(|tex| {
-            <dyn Any>::downcast_ref::<<<R::Device as ApiDevice>::Renderer as Renderer>::TextureId>(tex)
+            <dyn Any>::downcast_ref::<<<R::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>(tex)
                 .map(|tex| tex.size())
                 == Some(size)
         });
@@ -1598,7 +1700,7 @@ impl MultiTexture {
                     true
                 }
             })
-            .map(|(r, m)| (r, Box::new(m) as Box<_>))
+            .map(|(r, m)| (r, Box::new(m) as Box<dyn Any + 'static>))
             .collect::<Vec<_>>();
         mappings.retain(|(region, _)| {
             !new_mappings
@@ -1633,16 +1735,16 @@ impl Texture for MultiTexture {
     }
 }
 
-impl<R: GraphicsApi, T: GraphicsApi> Frame for MultiFrame<'_, '_, '_, R, T>
+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 Renderer>::TextureId: Clone + Send,
-    <<R::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
-    <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
+    <<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 TextureId = MultiTexture;
     type Error = Error<R, T>;
@@ -1746,9 +1848,9 @@ where
     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 Renderer>::TextureId: Clone + Send,
-    <<R::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
-    <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
+    <<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]
@@ -1757,7 +1859,7 @@ where
         buffer: &wl_buffer::WlBuffer,
         surface: Option<&crate::wayland::compositor::SurfaceData>,
         damage: &[Rectangle<i32, BufferCoords>],
-    ) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error> {
+    ) -> Result<<Self as RendererSuper>::TextureId, <Self as RendererSuper>::Error> {
         let shm_texture = self
             .render
             .renderer_mut()
@@ -1794,9 +1896,9 @@ where
     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 Renderer>::TextureId: Clone + Send,
-    <<R::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
-    <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
+    <<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]
@@ -1806,7 +1908,7 @@ where
         format: Fourcc,
         size: Size<i32, BufferCoords>,
         flipped: bool,
-    ) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error> {
+    ) -> Result<<Self as RendererSuper>::TextureId, <Self as RendererSuper>::Error> {
         let mem_texture = self
             .render
             .renderer_mut()
@@ -1827,10 +1929,10 @@ where
     #[profiling::function]
     fn update_memory(
         &mut self,
-        texture: &<Self as Renderer>::TextureId,
+        texture: &<Self as RendererSuper>::TextureId,
         data: &[u8],
         region: Rectangle<i32, BufferCoords>,
-    ) -> Result<(), <Self as Renderer>::Error> {
+    ) -> Result<(), <Self as RendererSuper>::Error> {
         let mem_texture = texture
             .get::<R>(self.render.node())
             .ok_or_else(|| Error::MismatchedDevice(*self.render.node()))?;
@@ -1857,9 +1959,9 @@ where
     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 Renderer>::TextureId: Clone + Send,
-    <<R::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
-    <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
+    <<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]
@@ -1868,7 +1970,7 @@ where
         buffer: &wl_buffer::WlBuffer,
         surface: Option<&SurfaceData>,
         damage: &[Rectangle<i32, BufferCoords>],
-    ) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error> {
+    ) -> 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();
@@ -1893,9 +1995,9 @@ where
     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 Renderer>::TextureId: Clone + Send,
-    <<R::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
-    <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
+    <<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())
@@ -1911,7 +2013,7 @@ where
         &mut self,
         dmabuf: &Dmabuf,
         damage: Option<&[Rectangle<i32, BufferCoords>]>,
-    ) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error> {
+    ) -> Result<<Self as RendererSuper>::TextureId, <Self as RendererSuper>::Error> {
         let texture = MultiTexture::new(dmabuf.size(), dmabuf.format());
         self.import_dmabuf_internal(dmabuf, texture, damage)
     }
@@ -1929,10 +2031,10 @@ 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 Renderer>::TextureId: 'static,
+    <<<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 Renderer>::TextureId: 'static,
+    <<<T as GraphicsApi>::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: 'static,
 {
     match dmabuf.node() {
         Some(node) => {
@@ -1994,7 +2096,7 @@ where
 }
 
 fn dma_shadow_copy<S, T>(
-    src_texture: &<<S::Device as ApiDevice>::Renderer as Renderer>::TextureId,
+    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,
@@ -2005,8 +2107,8 @@ where
     T: GraphicsApi,
     <S::Device as ApiDevice>::Renderer: Renderer + ImportDma + Bind<Dmabuf>,
     <T::Device as ApiDevice>::Renderer: Renderer + ImportDma,
-    <<S::Device as ApiDevice>::Renderer as Renderer>::TextureId: 'static,
-    <<T::Device as ApiDevice>::Renderer as Renderer>::TextureId: 'static,
+    <<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() {
@@ -2040,14 +2142,14 @@ where
             .map_err(Error::AllocatorError)?;
 
         let target_texture = if let Some(target) = target.as_mut() {
-            Box::<<<T::Device as ApiDevice>::Renderer as Renderer>::TextureId>::new(
+            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 Renderer>::TextureId>::new(
+            Box::<<<S::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>::new(
                 src.renderer_mut()
                     .import_dmabuf(&shadow_buffer, None)
                     .map_err(Error::Render)?,
@@ -2063,10 +2165,10 @@ where
             let _ = sync.wait(); // ignore interrupt errors
         }
     }
-    src_renderer.bind(shadow_buffer.clone()).map_err(Error::Render)?;
+    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(shadow_size, Transform::Normal)
+        .render(&mut framebuffer, shadow_size, Transform::Normal)
         .map_err(Error::Render)?;
 
     let damage_slice = [Rectangle::from_size(shadow_size)];
@@ -2105,11 +2207,11 @@ type BoxedTextureMappingAndDamage<S> = (
 
 type MemTexture<S, T> = (
     Option<Vec<BoxedTextureMappingAndDamage<S>>>,
-    Option<Box<<<<T as GraphicsApi>::Device as ApiDevice>::Renderer as Renderer>::TextureId>>,
+    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 Renderer>::TextureId,
+    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,
@@ -2244,7 +2346,7 @@ where
 fn texture_copy<S, T>(
     src: &mut S::Device,
     target: &mut T::Device,
-    src_texture: &<<S::Device as ApiDevice>::Renderer as Renderer>::TextureId,
+    src_texture: &<<S::Device as ApiDevice>::Renderer as RendererSuper>::TextureId,
     target_texture: &mut Option<GpuSingleTexture>,
     damage: Option<&[Rectangle<i32, BufferCoords>]>,
 ) -> Result<(), Error<S, T>>
@@ -2254,8 +2356,8 @@ where
     <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 Renderer>::TextureId: 'static,
-    <<T::Device as ApiDevice>::Renderer as Renderer>::TextureId: 'static,
+    <<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(_)))
@@ -2293,14 +2395,14 @@ where
                 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 Renderer>::TextureId>().unwrap())
+                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 Renderer>::TextureId>()
+                        .downcast_ref::<<<S::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>()
                         .unwrap()
                 })
                 .unwrap_or(src_texture);
@@ -2370,7 +2472,7 @@ where
                         .as_ref()
                         .map(|(_, texture)| {
                             texture
-                                .downcast_ref::<<<S::Device as ApiDevice>::Renderer as Renderer>::TextureId>()
+                                .downcast_ref::<<<S::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>()
                                 .unwrap()
                         })
                         .unwrap_or(src_texture);
@@ -2407,9 +2509,9 @@ where
     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 Renderer>::TextureId: Clone + Send,
-    <<R::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
-    <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
+    <<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(
@@ -2417,7 +2519,7 @@ where
         dmabuf: &Dmabuf,
         mut texture: MultiTexture,
         damage: Option<&[Rectangle<i32, BufferCoords>]>,
-    ) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error> {
+    ) -> Result<<Self as RendererSuper>::TextureId, <Self as RendererSuper>::Error> {
         let src_node = import_on_src_node::<R, T>(
             dmabuf,
             damage,
@@ -2456,7 +2558,7 @@ where
                 };
                 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 Renderer>::TextureId>()
+                        .downcast_ref::<<<T::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>()
                         .unwrap(),
                     _ => unreachable!(),
                 };
@@ -2493,7 +2595,7 @@ where
                 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 Renderer>::TextureId>()
+                        .downcast_ref::<<<R::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>()
                         .unwrap(),
                     _ => unreachable!(),
                 };
@@ -2604,32 +2706,36 @@ where
     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 Renderer>::TextureId: Clone + Send,
-    <<R::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
-    <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
+    <<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))]
+    #[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 Renderer>::Error> {
-        if let Some(target) = self.target.as_mut() {
-            target
-                .device
-                .renderer_mut()
-                .copy_framebuffer(region, format)
-                .map(|mapping| MultiTextureMapping(TextureMappingInternal::Either(mapping)))
-                .map_err(Error::Target)
-        } else {
-            self.render
+    ) -> 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(region, format)
+                .copy_framebuffer(fb, region, format)
                 .map(|mapping| MultiTextureMapping(TextureMappingInternal::Or(mapping)))
-                .map_err(Error::Render)
+                .map_err(Error::Render),
         }
     }
 
@@ -2666,7 +2772,7 @@ where
     fn map_texture<'c>(
         &mut self,
         texture_mapping: &'c Self::TextureMapping,
-    ) -> Result<&'c [u8], <Self as Renderer>::Error> {
+    ) -> Result<&'c [u8], <Self as RendererSuper>::Error> {
         match texture_mapping {
             MultiTextureMapping(TextureMappingInternal::Either(target_mapping)) => self
                 .target
@@ -2685,62 +2791,52 @@ where
     }
 }
 
-impl<R: GraphicsApi, T: GraphicsApi, BlitTarget> Blit<BlitTarget> for MultiRenderer<'_, '_, R, T>
+impl<R: GraphicsApi, T: GraphicsApi> Blit for MultiRenderer<'_, '_, R, T>
 where
-    <R::Device as ApiDevice>::Renderer: Blit<BlitTarget> + Bind<BlitTarget>,
-    <T::Device as ApiDevice>::Renderer: Blit<BlitTarget> + Bind<BlitTarget>,
+    <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 Renderer>::TextureId: Clone + Send,
-    <<R::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
-    <<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
+    <<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: BlitTarget,
-        src: Rectangle<i32, Physical>,
-        dst: Rectangle<i32, Physical>,
-        filter: TextureFilter,
-    ) -> Result<(), <Self as Renderer>::Error> {
-        if let Some(target) = self.target.as_mut() {
-            target
-                .device
-                .renderer_mut()
-                .blit_to(to, src, dst, filter)
-                .map_err(Error::Target)
-        } else {
-            self.render
-                .renderer_mut()
-                .blit_to(to, src, dst, filter)
-                .map_err(Error::Render)
-        }
-    }
-
-    #[instrument(level = "trace", parent = &self.span, skip(self, from))]
+    #[instrument(level = "trace", parent = &self.span, skip(self, from, to))]
     #[profiling::function]
-    fn blit_from(
+    fn blit(
         &mut self,
-        from: BlitTarget,
+        from: &MultiFramebuffer<'_, R, T>,
+        to: &mut MultiFramebuffer<'_, R, T>,
         src: Rectangle<i32, Physical>,
         dst: Rectangle<i32, Physical>,
         filter: TextureFilter,
-    ) -> Result<(), <Self as Renderer>::Error> {
+    ) -> 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(from, src, dst, filter)
+                .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(from, src, dst, filter)
+                .blit(from_fb, to_fb, src, dst, filter)
                 .map_err(Error::Render)
         }
     }
diff --git a/src/backend/renderer/pixman/mod.rs b/src/backend/renderer/pixman/mod.rs
index ff225d8cab16..c8db62b383e2 100644
--- a/src/backend/renderer/pixman/mod.rs
+++ b/src/backend/renderer/pixman/mod.rs
@@ -34,7 +34,7 @@ use wayland_server::protocol::wl_buffer;
 use super::ImportEgl;
 use super::{
     sync::SyncPoint, Bind, Color32F, DebugFlags, ExportMem, Frame, ImportDma, ImportMem, Offscreen, Renderer,
-    Texture, TextureFilter, TextureMapping, Unbind,
+    RendererSuper, Texture, TextureFilter, TextureMapping,
 };
 
 mod error;
@@ -62,21 +62,13 @@ const SUPPORTED_FORMATS: &[DrmFourcc] = &[
     DrmFourcc::Abgr2101010,
 ];
 
+/// A framebuffer of an [`PixmanRenderer`].
 #[derive(Debug)]
-enum PixmanTarget {
-    Image { dmabuf: Dmabuf, image: PixmanImage },
-    RenderBuffer(PixmanRenderBuffer),
-}
-
-/// Offscreen render buffer
+pub struct PixmanTarget<'a>(PixmanTargetInternal<'a>);
 #[derive(Debug)]
-pub struct PixmanRenderBuffer(pixman::Image<'static, 'static>);
-
-impl From<pixman::Image<'static, 'static>> for PixmanRenderBuffer {
-    #[inline]
-    fn from(value: pixman::Image<'static, 'static>) -> Self {
-        Self(value)
-    }
+enum PixmanTargetInternal<'a> {
+    Dmabuf { dmabuf: &'a Dmabuf, image: PixmanImage },
+    Image(&'a mut pixman::Image<'static, 'static>),
 }
 
 #[derive(Debug)]
@@ -239,8 +231,9 @@ impl Texture for PixmanTexture {
 
 /// Handle to the currently rendered frame during [`PixmanRenderer::render`](Renderer::render).
 #[derive(Debug)]
-pub struct PixmanFrame<'frame> {
+pub struct PixmanFrame<'frame, 'buffer> {
     renderer: &'frame mut PixmanRenderer,
+    target: &'frame mut PixmanTarget<'buffer>,
 
     transform: Transform,
     output_size: Size<i32, Physical>,
@@ -249,7 +242,7 @@ pub struct PixmanFrame<'frame> {
     finished: AtomicBool,
 }
 
-impl PixmanFrame<'_> {
+impl PixmanFrame<'_, '_> {
     fn draw_solid_color(
         &mut self,
         dst: Rectangle<i32, Physical>,
@@ -259,12 +252,12 @@ impl PixmanFrame<'_> {
         debug: DebugFlags,
     ) -> Result<(), PixmanError> {
         let mut binding;
-        let target_image = match self.renderer.target.as_mut().ok_or(PixmanError::NoTargetBound)? {
-            PixmanTarget::Image { image, .. } => {
+        let target_image = match &mut self.target.0 {
+            PixmanTargetInternal::Dmabuf { image, .. } => {
                 binding = image.0.image.lock().unwrap();
                 &mut *binding
             }
-            PixmanTarget::RenderBuffer(b) => &mut b.0,
+            PixmanTargetInternal::Image(b) => b,
         };
 
         let solid = pixman::Solid::new(color.components()).map_err(|_| PixmanError::Unsupported)?;
@@ -323,7 +316,7 @@ impl PixmanFrame<'_> {
     }
 }
 
-impl Frame for PixmanFrame<'_> {
+impl Frame for PixmanFrame<'_, '_> {
     type Error = PixmanError;
 
     type TextureId = PixmanTexture;
@@ -370,12 +363,12 @@ impl Frame for PixmanFrame<'_> {
         alpha: f32,
     ) -> Result<(), Self::Error> {
         let mut binding;
-        let target_image = match self.renderer.target.as_mut().ok_or(PixmanError::NoTargetBound)? {
-            PixmanTarget::Image { image, .. } => {
+        let target_image = match &mut self.target.0 {
+            PixmanTargetInternal::Dmabuf { image, .. } => {
                 binding = image.0.image.lock().unwrap();
                 &mut *binding
             }
-            PixmanTarget::RenderBuffer(b) => &mut b.0,
+            PixmanTargetInternal::Image(b) => b,
         };
         let src_image_accessor = texture.accessor()?;
 
@@ -626,16 +619,14 @@ impl Frame for PixmanFrame<'_> {
     }
 }
 
-impl PixmanFrame<'_> {
+impl PixmanFrame<'_, '_> {
     #[profiling::function]
     fn finish_internal(&mut self) -> Result<SyncPoint, PixmanError> {
         if self.finished.swap(true, Ordering::SeqCst) {
             return Ok(SyncPoint::signaled());
         }
 
-        if let PixmanTarget::Image { dmabuf, .. } =
-            self.renderer.target.as_ref().ok_or(PixmanError::NoTargetBound)?
-        {
+        if let PixmanTargetInternal::Dmabuf { dmabuf, .. } = &self.target.0 {
             dmabuf
                 .sync_plane(
                     0,
@@ -648,7 +639,7 @@ impl PixmanFrame<'_> {
     }
 }
 
-impl Drop for PixmanFrame<'_> {
+impl Drop for PixmanFrame<'_, '_> {
     fn drop(&mut self) {
         match self.finish_internal() {
             Ok(sync) => {
@@ -664,7 +655,6 @@ impl Drop for PixmanFrame<'_> {
 /// A renderer utilizing pixman
 #[derive(Debug)]
 pub struct PixmanRenderer {
-    target: Option<PixmanTarget>,
     downscale_filter: TextureFilter,
     upscale_filter: TextureFilter,
     debug_flags: DebugFlags,
@@ -680,7 +670,6 @@ impl PixmanRenderer {
     pub fn new() -> Result<Self, PixmanError> {
         let tint = pixman::Solid::new([0.0, 0.2, 0.0, 0.2]).map_err(|_| PixmanError::Unsupported)?;
         Ok(Self {
-            target: None,
             downscale_filter: TextureFilter::Linear,
             upscale_filter: TextureFilter::Linear,
             debug_flags: DebugFlags::empty(),
@@ -783,13 +772,17 @@ impl PixmanRenderer {
     }
 }
 
-impl Renderer for PixmanRenderer {
+impl RendererSuper for PixmanRenderer {
     type Error = PixmanError;
-
     type TextureId = PixmanTexture;
+    type Framebuffer<'buffer> = PixmanTarget<'buffer>;
+    type Frame<'frame, 'buffer>
+        = PixmanFrame<'frame, 'buffer>
+    where
+        'buffer: 'frame;
+}
 
-    type Frame<'frame> = PixmanFrame<'frame>;
-
+impl Renderer for PixmanRenderer {
     fn id(&self) -> usize {
         0
     }
@@ -812,15 +805,19 @@ impl Renderer for PixmanRenderer {
         self.debug_flags
     }
 
-    #[profiling::function]
-    fn render(
-        &mut self,
+    //#[profiling::function]
+    fn render<'frame, 'buffer>(
+        &'frame mut self,
+        target: &'frame mut PixmanTarget<'buffer>,
         output_size: Size<i32, Physical>,
         dst_transform: Transform,
-    ) -> Result<PixmanFrame<'_>, Self::Error> {
+    ) -> Result<PixmanFrame<'frame, 'buffer>, Self::Error>
+    where
+        'buffer: 'frame,
+    {
         self.cleanup();
 
-        if let PixmanTarget::Image { dmabuf, .. } = self.target.as_ref().ok_or(PixmanError::NoTargetBound)? {
+        if let PixmanTargetInternal::Dmabuf { dmabuf, .. } = &target.0 {
             dmabuf
                 .sync_plane(
                     0,
@@ -830,6 +827,7 @@ impl Renderer for PixmanRenderer {
         }
         Ok(PixmanFrame {
             renderer: self,
+            target,
 
             transform: dst_transform,
             output_size,
@@ -856,7 +854,7 @@ impl ImportMem for PixmanRenderer {
         format: drm_fourcc::DrmFourcc,
         size: Size<i32, BufferCoords>,
         flipped: bool,
-    ) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error> {
+    ) -> Result<Self::TextureId, Self::Error> {
         let format =
             pixman::FormatCode::try_from(format).map_err(|_| PixmanError::UnsupportedPixelFormat(format))?;
         let image = pixman::Image::new(format, size.w as usize, size.h as usize, false)
@@ -883,10 +881,10 @@ impl ImportMem for PixmanRenderer {
     #[profiling::function]
     fn update_memory(
         &mut self,
-        texture: &<Self as Renderer>::TextureId,
+        texture: &Self::TextureId,
         data: &[u8],
         region: Rectangle<i32, BufferCoords>,
-    ) -> Result<(), <Self as Renderer>::Error> {
+    ) -> Result<(), Self::Error> {
         #[cfg(feature = "wayland_frontend")]
         if texture.0 .0.buffer.is_some() {
             return Err(PixmanError::ImportFailed);
@@ -969,39 +967,38 @@ impl ExportMem for PixmanRenderer {
     #[profiling::function]
     fn copy_framebuffer(
         &mut self,
+        target: &PixmanTarget<'_>,
         region: Rectangle<i32, BufferCoords>,
         format: DrmFourcc,
-    ) -> Result<Self::TextureMapping, <Self as Renderer>::Error> {
+    ) -> Result<Self::TextureMapping, Self::Error> {
         let format_code =
             pixman::FormatCode::try_from(format).map_err(|_| PixmanError::UnsupportedPixelFormat(format))?;
         let mut copy_image =
             pixman::Image::new(format_code, region.size.w as usize, region.size.h as usize, false)
                 .map_err(|_| PixmanError::Unsupported)?;
 
-        if let Some(target) = self.target.as_ref() {
-            let binding;
-            let target_image = match target {
-                PixmanTarget::Image { dmabuf, image } => {
-                    dmabuf.sync_plane(0, DmabufSyncFlags::START | DmabufSyncFlags::READ)?;
-                    binding = image.0.image.lock().unwrap();
-                    &*binding
-                }
-                PixmanTarget::RenderBuffer(b) => &b.0,
-            };
+        let binding;
+        let target_image = match &target.0 {
+            PixmanTargetInternal::Dmabuf { dmabuf, image } => {
+                dmabuf.sync_plane(0, DmabufSyncFlags::START | DmabufSyncFlags::READ)?;
+                binding = image.0.image.lock().unwrap();
+                &*binding
+            }
+            PixmanTargetInternal::Image(b) => *b,
+        };
 
-            copy_image.composite32(
-                Operation::Src,
-                target_image,
-                None,
-                region.loc.into(),
-                (0, 0),
-                (0, 0),
-                region.size.into(),
-            );
-            if let PixmanTarget::Image { dmabuf, .. } = target {
-                dmabuf.sync_plane(0, DmabufSyncFlags::END | DmabufSyncFlags::READ)?;
-            };
-        }
+        copy_image.composite32(
+            Operation::Src,
+            target_image,
+            None,
+            region.loc.into(),
+            (0, 0),
+            (0, 0),
+            region.size.into(),
+        );
+        if let PixmanTargetInternal::Dmabuf { dmabuf, .. } = &target.0 {
+            dmabuf.sync_plane(0, DmabufSyncFlags::END | DmabufSyncFlags::READ)?;
+        };
 
         Ok(PixmanMapping(copy_image))
     }
@@ -1041,7 +1038,7 @@ impl ExportMem for PixmanRenderer {
     fn map_texture<'a>(
         &mut self,
         texture_mapping: &'a Self::TextureMapping,
-    ) -> Result<&'a [u8], <Self as Renderer>::Error> {
+    ) -> Result<&'a [u8], Self::Error> {
         Ok(unsafe {
             std::slice::from_raw_parts(
                 texture_mapping.0.data() as *const u8,
@@ -1075,7 +1072,7 @@ impl ImportEgl for PixmanRenderer {
         _buffer: &wl_buffer::WlBuffer,
         _surface: Option<&SurfaceData>,
         _damage: &[Rectangle<i32, BufferCoords>],
-    ) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error> {
+    ) -> Result<Self::TextureId, Self::Error> {
         Err(PixmanError::Unsupported)
     }
 }
@@ -1134,7 +1131,7 @@ impl ImportDma for PixmanRenderer {
         &mut self,
         dmabuf: &Dmabuf,
         _damage: Option<&[Rectangle<i32, BufferCoords>]>,
-    ) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error> {
+    ) -> Result<Self::TextureId, Self::Error> {
         if let Some(image) = self.existing_dmabuf(dmabuf) {
             return Ok(PixmanTexture(image));
         };
@@ -1162,17 +1159,9 @@ impl ImportDma for PixmanRenderer {
 #[cfg(feature = "wayland_frontend")]
 impl ImportDmaWl for PixmanRenderer {}
 
-impl Unbind for PixmanRenderer {
-    #[profiling::function]
-    fn unbind(&mut self) -> Result<(), <Self as Renderer>::Error> {
-        self.target = None;
-        Ok(())
-    }
-}
-
 impl Bind<Dmabuf> for PixmanRenderer {
-    #[profiling::function]
-    fn bind(&mut self, target: Dmabuf) -> Result<(), <Self as Renderer>::Error> {
+    //#[profiling::function]
+    fn bind<'a>(&mut self, target: &'a mut Dmabuf) -> Result<PixmanTarget<'a>, Self::Error> {
         let existing_image = self
             .buffers
             .iter()
@@ -1181,7 +1170,7 @@ impl Bind<Dmabuf> for PixmanRenderer {
                     .0
                     .dmabuf
                     .as_ref()
-                    .and_then(|map| map.dmabuf.upgrade().map(|buf| buf == target))
+                    .and_then(|map| map.dmabuf.upgrade().map(|buf| buf == *target))
                     .unwrap_or(false)
             })
             .cloned();
@@ -1189,16 +1178,15 @@ impl Bind<Dmabuf> for PixmanRenderer {
         let image = if let Some(image) = existing_image {
             image
         } else {
-            let image = self.import_dmabuf(&target, DmabufMappingMode::READ | DmabufMappingMode::WRITE)?;
+            let image = self.import_dmabuf(target, DmabufMappingMode::READ | DmabufMappingMode::WRITE)?;
             self.buffers.push(image.clone());
             image
         };
 
-        self.target = Some(PixmanTarget::Image {
+        Ok(PixmanTarget(PixmanTargetInternal::Dmabuf {
             dmabuf: target,
             image,
-        });
-        Ok(())
+        }))
     }
 
     fn supported_formats(&self) -> Option<FormatSet> {
@@ -1216,26 +1204,25 @@ impl Bind<Dmabuf> for PixmanRenderer {
     }
 }
 
-impl Offscreen<PixmanRenderBuffer> for PixmanRenderer {
+impl Offscreen<Image<'static, 'static>> for PixmanRenderer {
     #[profiling::function]
     fn create_buffer(
         &mut self,
         format: DrmFourcc,
         size: Size<i32, BufferCoords>,
-    ) -> Result<PixmanRenderBuffer, <Self as Renderer>::Error> {
+    ) -> Result<Image<'static, 'static>, Self::Error> {
         let format_code =
             FormatCode::try_from(format).map_err(|_| PixmanError::UnsupportedPixelFormat(format))?;
         let image = pixman::Image::new(format_code, size.w as usize, size.h as usize, true)
             .map_err(|_| PixmanError::Unsupported)?;
-        Ok(PixmanRenderBuffer::from(image))
+        Ok(image)
     }
 }
 
-impl Bind<PixmanRenderBuffer> for PixmanRenderer {
-    #[profiling::function]
-    fn bind(&mut self, target: PixmanRenderBuffer) -> Result<(), <Self as Renderer>::Error> {
-        self.target = Some(PixmanTarget::RenderBuffer(target));
-        Ok(())
+impl Bind<Image<'static, 'static>> for PixmanRenderer {
+    //#[profiling::function]
+    fn bind<'a>(&mut self, target: &'a mut Image<'static, 'static>) -> Result<PixmanTarget<'a>, Self::Error> {
+        Ok(PixmanTarget(PixmanTargetInternal::Image(target)))
     }
 
     fn supported_formats(&self) -> Option<FormatSet> {
diff --git a/src/backend/renderer/test.rs b/src/backend/renderer/test.rs
index c0a28471b743..c5b2efdaaca8 100644
--- a/src/backend/renderer/test.rs
+++ b/src/backend/renderer/test.rs
@@ -18,7 +18,8 @@ use crate::{
     backend::{
         allocator::{dmabuf::Dmabuf, Fourcc},
         renderer::{
-            sync::SyncPoint, DebugFlags, Frame, ImportDma, ImportMem, Renderer, Texture, TextureFilter,
+            sync::SyncPoint, DebugFlags, Frame, ImportDma, ImportMem, Renderer, RendererSuper, Texture,
+            TextureFilter,
         },
         SwapBuffersError,
     },
@@ -27,22 +28,10 @@ use crate::{
 
 use super::Color32F;
 
-#[derive(Debug)]
-pub struct DummyRenderer {}
-
-impl DummyRenderer {
-    pub fn new() -> DummyRenderer {
-        DummyRenderer {}
-    }
-}
+#[derive(Debug, Default)]
+pub struct DummyRenderer;
 
-impl Default for DummyRenderer {
-    fn default() -> Self {
-        Self::new()
-    }
-}
-
-/// Error returned by the TestRenderer
+/// Error returned by the DummyRenderer
 #[derive(thiserror::Error, Debug)]
 pub enum DummyError {
     /// Error accessing shm buffer
@@ -61,20 +50,31 @@ impl From<DummyError> for SwapBuffersError {
     }
 }
 
-impl Renderer for DummyRenderer {
+impl RendererSuper for DummyRenderer {
     type Error = DummyError;
     type TextureId = DummyTexture;
-    type Frame<'a> = DummyFrame;
+    type Frame<'frame, 'buffer>
+        = DummyFrame
+    where
+        'buffer: 'frame,
+        Self: 'frame;
+    type Framebuffer<'buffer> = DummyFramebuffer;
+}
 
+impl Renderer for DummyRenderer {
     fn id(&self) -> usize {
         0
     }
 
-    fn render(
-        &mut self,
+    fn render<'frame, 'buffer>(
+        &'frame mut self,
+        _target: &'frame mut DummyFramebuffer,
         _size: Size<i32, Physical>,
         _dst_transform: Transform,
-    ) -> Result<DummyFrame, Self::Error> {
+    ) -> Result<DummyFrame, Self::Error>
+    where
+        'buffer: 'frame,
+    {
         Ok(DummyFrame {})
     }
 
@@ -104,16 +104,16 @@ impl ImportMem for DummyRenderer {
         _format: Fourcc,
         _size: Size<i32, Buffer>,
         _flipped: bool,
-    ) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error> {
+    ) -> Result<Self::TextureId, Self::Error> {
         unimplemented!()
     }
 
     fn update_memory(
         &mut self,
-        _texture: &<Self as Renderer>::TextureId,
+        _texture: &Self::TextureId,
         _data: &[u8],
         _region: Rectangle<i32, Buffer>,
-    ) -> Result<(), <Self as Renderer>::Error> {
+    ) -> Result<(), Self::Error> {
         unimplemented!()
     }
 
@@ -129,7 +129,7 @@ impl ImportMemWl for DummyRenderer {
         buffer: &wl_buffer::WlBuffer,
         surface: Option<&SurfaceData>,
         _damage: &[Rectangle<i32, Buffer>],
-    ) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error> {
+    ) -> Result<Self::TextureId, Self::Error> {
         use std::ptr;
         use wayland::shm::with_buffer_contents;
         let ret = with_buffer_contents(buffer, |ptr, len, data| {
@@ -167,7 +167,7 @@ impl ImportDma for DummyRenderer {
         &mut self,
         _dmabuf: &Dmabuf,
         _damage: Option<&[Rectangle<i32, Buffer>]>,
-    ) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error> {
+    ) -> Result<Self::TextureId, Self::Error> {
         unimplemented!()
     }
 }
@@ -198,7 +198,7 @@ impl ImportEgl for DummyRenderer {
         _buffer: &wl_buffer::WlBuffer,
         _surface: Option<&wayland::compositor::SurfaceData>,
         _damage: &[Rectangle<i32, Buffer>],
-    ) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error> {
+    ) -> Result<Self::TextureId, Self::Error> {
         unimplemented!()
     }
 }
@@ -206,6 +206,9 @@ impl ImportEgl for DummyRenderer {
 #[cfg(feature = "wayland_frontend")]
 impl ImportDmaWl for DummyRenderer {}
 
+#[derive(Debug)]
+pub struct DummyFramebuffer;
+
 #[derive(Debug)]
 pub struct DummyFrame {}
 
diff --git a/src/backend/renderer/utils/wayland.rs b/src/backend/renderer/utils/wayland.rs
index e72e221facec..08dedbf8c6df 100644
--- a/src/backend/renderer/utils/wayland.rs
+++ b/src/backend/renderer/utils/wayland.rs
@@ -319,9 +319,9 @@ impl RendererSurfaceState {
     pub fn texture<R>(&self, id: usize) -> Option<&R::TextureId>
     where
         R: Renderer,
-        <R as Renderer>::TextureId: 'static,
+        R::TextureId: 'static,
     {
-        let texture_id = (TypeId::of::<<R as Renderer>::TextureId>(), id);
+        let texture_id = (TypeId::of::<R::TextureId>(), id);
         self.textures.get(&texture_id).and_then(|e| e.downcast_ref())
     }
 
@@ -486,13 +486,13 @@ where
 /// to let smithay handle buffer management.
 #[instrument(level = "trace", skip_all)]
 #[profiling::function]
-pub fn import_surface<R>(renderer: &mut R, states: &SurfaceData) -> Result<(), <R as Renderer>::Error>
+pub fn import_surface<R>(renderer: &mut R, states: &SurfaceData) -> Result<(), R::Error>
 where
     R: Renderer + ImportAll,
-    <R as Renderer>::TextureId: 'static,
+    R::TextureId: 'static,
 {
     if let Some(data) = states.data_map.get::<RendererSurfaceStateUserData>() {
-        let texture_id = (TypeId::of::<<R as Renderer>::TextureId>(), renderer.id());
+        let texture_id = (TypeId::of::<R::TextureId>(), renderer.id());
         let mut data_ref = data.lock().unwrap();
         let data = &mut *data_ref;
 
@@ -537,15 +537,15 @@ where
 /// to let smithay handle buffer management.
 #[instrument(level = "trace", skip_all)]
 #[profiling::function]
-pub fn import_surface_tree<R>(renderer: &mut R, surface: &WlSurface) -> Result<(), <R as Renderer>::Error>
+pub fn import_surface_tree<R>(renderer: &mut R, surface: &WlSurface) -> Result<(), R::Error>
 where
     R: Renderer + ImportAll,
-    <R as Renderer>::TextureId: 'static,
+    R::TextureId: 'static,
 {
     let scale = 1.0;
     let location: Point<f64, Physical> = (0.0, 0.0).into();
 
-    let texture_id = (TypeId::of::<<R as Renderer>::TextureId>(), renderer.id());
+    let texture_id = (TypeId::of::<R::TextureId>(), renderer.id());
     let mut result = Ok(());
     with_surface_tree_downward(
         surface,
@@ -592,15 +592,15 @@ where
 /// to let smithay handle buffer management.
 #[instrument(level = "trace", skip(frame, scale, elements))]
 #[profiling::function]
-pub fn draw_render_elements<'a, R, S, E>(
-    frame: &mut <R as Renderer>::Frame<'a>,
+pub fn draw_render_elements<R, S, E>(
+    frame: &mut R::Frame<'_, '_>,
     scale: S,
     elements: &[E],
     damage: &[Rectangle<i32, Physical>],
-) -> Result<Option<Vec<Rectangle<i32, Physical>>>, <R as Renderer>::Error>
+) -> Result<Option<Vec<Rectangle<i32, Physical>>>, R::Error>
 where
     R: Renderer,
-    <R as Renderer>::TextureId: 'static,
+    R::TextureId: 'static,
     S: Into<Scale<f64>>,
     E: RenderElement<R>,
 {
diff --git a/src/backend/winit/mod.rs b/src/backend/winit/mod.rs
index 9458eb39629d..a82511b52f34 100644
--- a/src/backend/winit/mod.rs
+++ b/src/backend/winit/mod.rs
@@ -10,7 +10,7 @@
 //! you want on the initialization of the backend. These functions will provide you
 //! with two objects:
 //!
-//! - a [`WinitGraphicsBackend`], which can give you an implementation of a [`Renderer`]
+//! - a [`WinitGraphicsBackend`], which can give you an implementation of a [`Renderer`](crate::backend::renderer::Renderer)
 //!   (or even [`GlesRenderer`]) through its `renderer` method in addition to further
 //!   functionality to access and manage the created winit-window.
 //! - a [`WinitEventLoop`], which dispatches some [`WinitEvent`] from the host graphics server.
@@ -19,7 +19,6 @@
 //! two traits for the winit backend.
 
 use std::io::Error as IoError;
-use std::rc::Rc;
 use std::sync::Arc;
 use std::time::Duration;
 
@@ -59,14 +58,12 @@ mod input;
 
 pub use self::input::*;
 
-use super::renderer::Renderer;
-
 /// Create a new [`WinitGraphicsBackend`], which implements the
-/// [`Renderer`] trait and a corresponding [`WinitEventLoop`].
+/// [`Renderer`](crate::backend::renderer::Renderer) trait and a corresponding [`WinitEventLoop`].
 pub fn init<R>() -> Result<(WinitGraphicsBackend<R>, WinitEventLoop), Error>
 where
-    R: From<GlesRenderer> + Bind<Rc<EGLSurface>>,
-    crate::backend::SwapBuffersError: From<<R as Renderer>::Error>,
+    R: From<GlesRenderer> + Bind<EGLSurface>,
+    crate::backend::SwapBuffersError: From<R::Error>,
 {
     init_from_attributes(
         WinitWindow::default_attributes()
@@ -76,15 +73,15 @@ where
     )
 }
 
-/// Create a new [`WinitGraphicsBackend`], which implements the [`Renderer`]
+/// Create a new [`WinitGraphicsBackend`], which implements the [`Renderer`](crate::backend::renderer::Renderer)
 /// trait, from a given [`WindowAttributes`] struct and a corresponding
 /// [`WinitEventLoop`].
 pub fn init_from_attributes<R>(
     attributes: WindowAttributes,
 ) -> Result<(WinitGraphicsBackend<R>, WinitEventLoop), Error>
 where
-    R: From<GlesRenderer> + Bind<Rc<EGLSurface>>,
-    crate::backend::SwapBuffersError: From<<R as Renderer>::Error>,
+    R: From<GlesRenderer> + Bind<EGLSurface>,
+    crate::backend::SwapBuffersError: From<R::Error>,
 {
     init_from_attributes_with_gl_attr(
         attributes,
@@ -97,7 +94,7 @@ where
     )
 }
 
-/// Create a new [`WinitGraphicsBackend`], which implements the [`Renderer`]
+/// Create a new [`WinitGraphicsBackend`], which implements the [`Renderer`](crate::backend::renderer::Renderer)
 /// trait, from a given [`WindowAttributes`] struct, as well as given
 /// [`GlAttributes`] for further customization of the rendering pipeline and a
 /// corresponding [`WinitEventLoop`].
@@ -106,8 +103,8 @@ pub fn init_from_attributes_with_gl_attr<R>(
     gl_attributes: GlAttributes,
 ) -> Result<(WinitGraphicsBackend<R>, WinitEventLoop), Error>
 where
-    R: From<GlesRenderer> + Bind<Rc<EGLSurface>>,
-    crate::backend::SwapBuffersError: From<<R as Renderer>::Error>,
+    R: From<GlesRenderer> + Bind<EGLSurface>,
+    crate::backend::SwapBuffersError: From<R::Error>,
 {
     let span = info_span!("backend_winit", window = tracing::field::Empty);
     let _guard = span.enter();
@@ -182,7 +179,6 @@ where
         (display, context, surface, is_x11)
     };
 
-    let egl = Rc::new(surface);
     let renderer = unsafe { GlesRenderer::new(context)?.into() };
     let damage_tracking = display.supports_damage();
 
@@ -196,7 +192,7 @@ where
             window: window.clone(),
             span: span.clone(),
             _display: display,
-            egl_surface: egl,
+            egl_surface: surface,
             damage_tracking,
             bind_size: None,
             renderer,
@@ -246,7 +242,7 @@ pub struct WinitGraphicsBackend<R> {
     renderer: R,
     // The display isn't used past this point but must be kept alive.
     _display: EGLDisplay,
-    egl_surface: Rc<EGLSurface>,
+    egl_surface: EGLSurface,
     window: Arc<WinitWindow>,
     damage_tracking: bool,
     bind_size: Option<Size<i32, Physical>>,
@@ -255,8 +251,8 @@ pub struct WinitGraphicsBackend<R> {
 
 impl<R> WinitGraphicsBackend<R>
 where
-    R: Bind<Rc<EGLSurface>>,
-    crate::backend::SwapBuffersError: From<<R as Renderer>::Error>,
+    R: Bind<EGLSurface>,
+    crate::backend::SwapBuffersError: From<R::Error>,
 {
     /// Window size of the underlying window
     pub fn window_size(&self) -> Size<i32, Physical> {
@@ -282,7 +278,7 @@ where
     /// Bind the underlying window to the underlying renderer.
     #[instrument(level = "trace", parent = &self.span, skip(self))]
     #[profiling::function]
-    pub fn bind(&mut self) -> Result<(), crate::backend::SwapBuffersError> {
+    pub fn bind(&mut self) -> Result<(&mut R, R::Framebuffer<'_>), crate::backend::SwapBuffersError> {
         // NOTE: we must resize before making the current context current, otherwise the back
         // buffer will be latched. Some nvidia drivers may not like it, but a lot of wayland
         // software does the order that way due to mesa latching back buffer on each
@@ -293,17 +289,17 @@ where
         }
         self.bind_size = Some(window_size);
 
-        self.renderer.bind(self.egl_surface.clone())?;
+        let fb = self.renderer.bind(&mut self.egl_surface).map_err(Into::into)?;
 
-        Ok(())
+        Ok((&mut self.renderer, fb))
     }
 
     /// Retrieve the underlying `EGLSurface` for advanced operations
     ///
     /// **Note:** Don't carelessly use this to manually bind the renderer to the surface,
     /// `WinitGraphicsBackend::bind` transparently handles window resizes for you.
-    pub fn egl_surface(&self) -> Rc<EGLSurface> {
-        self.egl_surface.clone()
+    pub fn egl_surface(&self) -> &EGLSurface {
+        &self.egl_surface
     }
 
     /// Retrieve the buffer age of the current backbuffer of the window.
diff --git a/src/desktop/space/element/mod.rs b/src/desktop/space/element/mod.rs
index 749ea61ca394..ab65858634e6 100644
--- a/src/desktop/space/element/mod.rs
+++ b/src/desktop/space/element/mod.rs
@@ -169,7 +169,7 @@ impl<
         E: AsRenderElements<R>,
     > AsRenderElements<R> for SpaceElements<'a, E>
 where
-    <R as Renderer>::TextureId: Clone + Texture + 'static,
+    R::TextureId: Clone + Texture + 'static,
     <E as AsRenderElements<R>>::RenderElement: 'a,
     SpaceRenderElements<R, <E as AsRenderElements<R>>::RenderElement>:
         From<Wrap<<E as AsRenderElements<R>>::RenderElement>>,
diff --git a/src/desktop/space/element/wayland.rs b/src/desktop/space/element/wayland.rs
index de9e3d0d64d3..59f05745a5f5 100644
--- a/src/desktop/space/element/wayland.rs
+++ b/src/desktop/space/element/wayland.rs
@@ -32,7 +32,7 @@ impl IsAlive for SurfaceTree {
 impl<R> AsRenderElements<R> for SurfaceTree
 where
     R: Renderer + ImportAll,
-    <R as Renderer>::TextureId: Clone + 'static,
+    R::TextureId: Clone + 'static,
 {
     type RenderElement = WaylandSurfaceRenderElement<R>;
 
diff --git a/src/desktop/space/mod.rs b/src/desktop/space/mod.rs
index ee93e289f30a..a2dad8a0297d 100644
--- a/src/desktop/space/mod.rs
+++ b/src/desktop/space/mod.rs
@@ -386,7 +386,7 @@ impl<E: SpaceElement + PartialEq> Space<E> {
         alpha: f32,
     ) -> Vec<<E as AsRenderElements<R>>::RenderElement>
     where
-        <R as Renderer>::TextureId: Texture + 'static,
+        R::TextureId: Texture + 'static,
         E: AsRenderElements<R>,
         <E as AsRenderElements<R>>::RenderElement: 'a,
     {
@@ -426,7 +426,7 @@ impl<E: SpaceElement + PartialEq> Space<E> {
         alpha: f32,
     ) -> Result<Vec<SpaceRenderElements<R, <E as AsRenderElements<R>>::RenderElement>>, OutputError>
     where
-        <R as Renderer>::TextureId: Clone + Texture + 'static,
+        R::TextureId: Clone + Texture + 'static,
         E: AsRenderElements<R>,
         <E as AsRenderElements<R>>::RenderElement: 'a,
         SpaceRenderElements<R, <E as AsRenderElements<R>>::RenderElement>:
@@ -588,7 +588,7 @@ pub fn space_render_elements<
     alpha: f32,
 ) -> Result<Vec<SpaceRenderElements<R, <E as AsRenderElements<R>>::RenderElement>>, OutputNoMode>
 where
-    <R as Renderer>::TextureId: Clone + Texture + 'static,
+    R::TextureId: Clone + Texture + 'static,
     <E as AsRenderElements<R>>::RenderElement: 'a,
     SpaceRenderElements<R, <E as AsRenderElements<R>>::RenderElement>:
         From<Wrap<<E as AsRenderElements<R>>::RenderElement>>,
@@ -675,6 +675,7 @@ pub fn render_output<
 >(
     output: &Output,
     renderer: &mut R,
+    framebuffer: &mut R::Framebuffer<'_>,
     alpha: f32,
     age: usize,
     spaces: S,
@@ -683,7 +684,7 @@ pub fn render_output<
     clear_color: impl Into<Color32F>,
 ) -> Result<RenderOutputResult<'d>, OutputDamageTrackerError<R::Error>>
 where
-    <R as Renderer>::TextureId: Clone + Texture + 'static,
+    R::TextureId: Clone + Texture + 'static,
     <E as AsRenderElements<R>>::RenderElement: 'a,
     SpaceRenderElements<R, <E as AsRenderElements<R>>::RenderElement>:
         From<Wrap<<E as AsRenderElements<R>>::RenderElement>>,
@@ -700,5 +701,5 @@ where
     render_elements.extend(custom_elements.iter().map(OutputRenderElements::Custom));
     render_elements.extend(space_render_elements.into_iter().map(OutputRenderElements::Space));
 
-    damage_tracker.render_output(renderer, age, &render_elements, clear_color)
+    damage_tracker.render_output(renderer, framebuffer, age, &render_elements, clear_color)
 }
diff --git a/src/desktop/space/wayland/layer.rs b/src/desktop/space/wayland/layer.rs
index 4c59e186ece7..4b28fdb0e4f9 100644
--- a/src/desktop/space/wayland/layer.rs
+++ b/src/desktop/space/wayland/layer.rs
@@ -13,7 +13,7 @@ use crate::{
 impl<R> AsRenderElements<R> for LayerSurface
 where
     R: Renderer + ImportAll,
-    <R as Renderer>::TextureId: Clone + 'static,
+    R::TextureId: Clone + 'static,
 {
     type RenderElement = WaylandSurfaceRenderElement<R>;
 
diff --git a/src/desktop/space/wayland/window.rs b/src/desktop/space/wayland/window.rs
index 0390aaa588cc..c85e3007dff1 100644
--- a/src/desktop/space/wayland/window.rs
+++ b/src/desktop/space/wayland/window.rs
@@ -87,7 +87,7 @@ impl SpaceElement for Window {
 impl<R> AsRenderElements<R> for Window
 where
     R: Renderer + ImportAll,
-    <R as Renderer>::TextureId: Clone + 'static,
+    R::TextureId: Clone + 'static,
 {
     type RenderElement = WaylandSurfaceRenderElement<R>;
 
diff --git a/src/desktop/space/wayland/x11.rs b/src/desktop/space/wayland/x11.rs
index 1711894d971e..e82e0d525a9c 100644
--- a/src/desktop/space/wayland/x11.rs
+++ b/src/desktop/space/wayland/x11.rs
@@ -97,7 +97,7 @@ impl SpaceElement for X11Surface {
 impl<R> crate::backend::renderer::element::AsRenderElements<R> for X11Surface
 where
     R: Renderer + ImportAll,
-    <R as Renderer>::TextureId: Clone + 'static,
+    R::TextureId: Clone + 'static,
 {
     type RenderElement = WaylandSurfaceRenderElement<R>;
 
diff --git a/wlcs_anvil/src/main_loop.rs b/wlcs_anvil/src/main_loop.rs
index fa40b6f8379b..d0f9e728e3dd 100644
--- a/wlcs_anvil/src/main_loop.rs
+++ b/wlcs_anvil/src/main_loop.rs
@@ -7,7 +7,11 @@ use std::{
 use smithay::{
     backend::{
         input::ButtonState,
-        renderer::{damage::OutputDamageTracker, element::AsRenderElements, test::DummyRenderer},
+        renderer::{
+            damage::OutputDamageTracker,
+            element::AsRenderElements,
+            test::{DummyFramebuffer, DummyRenderer},
+        },
     },
     input::pointer::{
         ButtonEvent, CursorImageAttributes, CursorImageStatus, MotionEvent, RelativeMotionEvent,
@@ -62,7 +66,8 @@ pub fn run(channel: Channel<WlcsEvent>) {
         })
         .unwrap();
 
-    let mut renderer = DummyRenderer::new();
+    let mut renderer = DummyRenderer;
+    let mut framebuffer = DummyFramebuffer;
 
     let mode = Mode {
         size: (800, 600).into(),
@@ -150,6 +155,7 @@ pub fn run(channel: Channel<WlcsEvent>) {
                 &state.space,
                 elements,
                 &mut renderer,
+                &mut framebuffer,
                 &mut damage_tracker,
                 0,
                 false,