void qxl_user_framebuffer_destroy(struct drm_framebuffer *fb)
 {
        struct qxl_framebuffer *qxl_fb = to_qxl_framebuffer(fb);
+       struct qxl_bo *bo = gem_to_qxl_bo(qxl_fb->obj);
 
+       WARN_ON(bo->shadow);
        drm_gem_object_unreference_unlocked(qxl_fb->obj);
        drm_framebuffer_cleanup(fb);
        kfree(qxl_fb);
            .x2 = qfb->base.width,
            .y2 = qfb->base.height
        };
+       bool same_shadow = false;
 
        if (old_state->fb) {
                qfb_old = to_qxl_framebuffer(old_state->fb);
        if (bo == bo_old)
                return;
 
+       if (bo_old && bo_old->shadow && bo->shadow &&
+           bo_old->shadow == bo->shadow) {
+               same_shadow = true;
+       }
+
        if (bo_old && bo_old->is_primary) {
-               qxl_io_destroy_primary(qdev);
+               if (!same_shadow)
+                       qxl_io_destroy_primary(qdev);
                bo_old->is_primary = false;
        }
 
        if (!bo->is_primary) {
-               qxl_io_create_primary(qdev, 0, bo);
+               if (!same_shadow)
+                       qxl_io_create_primary(qdev, 0, bo);
                bo->is_primary = true;
        }
+
        qxl_draw_dirty_fb(qdev, qfb, bo, 0, 0, &norect, 1, 1);
 }
 
 static int qxl_plane_prepare_fb(struct drm_plane *plane,
                                struct drm_plane_state *new_state)
 {
+       struct qxl_device *qdev = plane->dev->dev_private;
        struct drm_gem_object *obj;
-       struct qxl_bo *user_bo;
+       struct qxl_bo *user_bo, *old_bo = NULL;
        int ret;
 
        if (!new_state->fb)
        obj = to_qxl_framebuffer(new_state->fb)->obj;
        user_bo = gem_to_qxl_bo(obj);
 
+       if (plane->type == DRM_PLANE_TYPE_PRIMARY &&
+           user_bo->is_dumb && !user_bo->shadow) {
+               if (plane->state->fb) {
+                       obj = to_qxl_framebuffer(plane->state->fb)->obj;
+                       old_bo = gem_to_qxl_bo(obj);
+               }
+               if (old_bo && old_bo->shadow &&
+                   user_bo->gem_base.size == old_bo->gem_base.size &&
+                   plane->state->crtc     == new_state->crtc &&
+                   plane->state->crtc_w   == new_state->crtc_w &&
+                   plane->state->crtc_h   == new_state->crtc_h &&
+                   plane->state->src_x    == new_state->src_x &&
+                   plane->state->src_y    == new_state->src_y &&
+                   plane->state->src_w    == new_state->src_w &&
+                   plane->state->src_h    == new_state->src_h &&
+                   plane->state->rotation == new_state->rotation &&
+                   plane->state->zpos     == new_state->zpos) {
+                       drm_gem_object_get(&old_bo->shadow->gem_base);
+                       user_bo->shadow = old_bo->shadow;
+               } else {
+                       qxl_bo_create(qdev, user_bo->gem_base.size,
+                                     true, true, QXL_GEM_DOMAIN_VRAM, NULL,
+                                     &user_bo->shadow);
+               }
+       }
+
        ret = qxl_bo_pin(user_bo, QXL_GEM_DOMAIN_CPU, NULL);
        if (ret)
                return ret;
        obj = to_qxl_framebuffer(old_state->fb)->obj;
        user_bo = gem_to_qxl_bo(obj);
        qxl_bo_unpin(user_bo);
+
+       if (user_bo->shadow && !user_bo->is_primary) {
+               drm_gem_object_put_unlocked(&user_bo->shadow->gem_base);
+               user_bo->shadow = NULL;
+       }
 }
 
 static const uint32_t qxl_cursor_plane_formats[] = {