[PATCH xf86-video-ati 2/2] Use reference counting for tracking KMS framebuffer lifetimes

Michel Dänzer michel at daenzer.net
Wed May 10 09:05:59 UTC 2017


From: Michel Dänzer <michel.daenzer at amd.com>

References are held by the pixmaps corresponding to the FBs (so
the same KMS FB can be reused as long as the pixmap exists) and by the
CRTCs scanning out from them (so a KMS FB is only destroyed once it's
not being scanned out anymore, preventing intermittent black screens and
worse issues due to a CRTC turning off when it should be on).

v2:
* Only increase reference count in drmmode_fb_reference if it was sane
  before
* Make drmmode_fb_reference's indentation match the rest of
  drmmode_display.h

Reviewed-by: Alex Deucher <alexander.deucher at amd.com> # v1
Signed-off-by: Michel Dänzer <michel.daenzer at amd.com>
---
 src/drmmode_display.c  | 147 +++++++++++++++++++++----------------------------
 src/drmmode_display.h  |  39 +++++++++++--
 src/radeon.h           |  73 ++++++++++++++++++++++++
 src/radeon_bo_helper.h |   3 -
 src/radeon_exa.c       |   2 +
 src/radeon_kms.c       |  64 ++++++++++++++++++---
 src/radeon_present.c   |   8 ---
 7 files changed, 226 insertions(+), 110 deletions(-)

diff --git a/src/drmmode_display.c b/src/drmmode_display.c
index a101ac233..ec3072621 100644
--- a/src/drmmode_display.c
+++ b/src/drmmode_display.c
@@ -375,6 +375,7 @@ drmmode_crtc_dpms(xf86CrtcPtr crtc, int mode)
 
 		drmModeSetCrtc(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
 			       0, 0, 0, NULL, 0, NULL);
+		drmmode_fb_reference(drmmode->fd, &drmmode_crtc->fb, NULL);
 	} else if (drmmode_crtc->dpms_mode != DPMSModeOn)
 		crtc->funcs->set_mode_major(crtc, &crtc->mode, crtc->rotation,
 					    crtc->x, crtc->y);
@@ -447,8 +448,9 @@ void drmmode_copy_fb(ScrnInfoPtr pScrn, drmmode_ptr drmmode)
 {
 	xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
 	RADEONInfoPtr info = RADEONPTR(pScrn);
-	PixmapPtr src, dst;
 	ScreenPtr pScreen = pScrn->pScreen;
+	PixmapPtr src, dst = pScreen->GetScreenPixmap(pScreen);
+	struct drmmode_fb *fb = radeon_pixmap_get_fb(dst);
 	int fbcon_id = 0;
 	Bool force;
 	GCPtr gc;
@@ -464,7 +466,7 @@ void drmmode_copy_fb(ScrnInfoPtr pScrn, drmmode_ptr drmmode)
 	if (!fbcon_id)
 		return;
 
-	if (fbcon_id == drmmode->fb_id) {
+	if (fbcon_id == fb->handle) {
 		/* in some rare case there might be no fbcon and we might already
 		 * be the one with the current fb to avoid a false deadlck in
 		 * kernel ttm code just do nothing as anyway there is nothing
@@ -477,8 +479,6 @@ void drmmode_copy_fb(ScrnInfoPtr pScrn, drmmode_ptr drmmode)
 	if (!src)
 		return;
 
-	dst = pScreen->GetScreenPixmap(pScreen);
-
 	gc = GetScratchGC(pScrn->depth, pScreen);
 	ValidateGC(&dst->drawable, gc);
 
@@ -505,8 +505,6 @@ drmmode_crtc_scanout_destroy(drmmode_ptr drmmode,
 	}
 
 	if (scanout->bo) {
-		drmModeRmFB(drmmode->fd, scanout->fb_id);
-		scanout->fb_id = 0;
 		radeon_bo_unmap(scanout->bo);
 		radeon_bo_unref(scanout->bo);
 		scanout->bo = NULL;
@@ -571,15 +569,9 @@ drmmode_crtc_scanout_create(xf86CrtcPtr crtc, struct drmmode_scanout *scanout,
 	scanout->bo = radeon_alloc_pixmap_bo(pScrn, width, height, pScrn->depth,
 					     tiling, pScrn->bitsPerPixel,
 					     &pitch, &surface, &tiling);
-	if (scanout->bo == NULL)
-		goto error;
-
-	if (drmModeAddFB(drmmode->fd, width, height, pScrn->depth,
-			   pScrn->bitsPerPixel, pitch,
-			   scanout->bo->handle,
-			   &scanout->fb_id) != 0) {
-		ErrorF("failed to add scanout fb\n");
-		goto error;
+	if (!scanout->bo) {
+		ErrorF("failed to create CRTC scanout BO\n");
+		return NULL;
 	}
 
 	scanout->pixmap = drmmode_create_bo_pixmap(pScrn,
@@ -587,13 +579,17 @@ drmmode_crtc_scanout_create(xf86CrtcPtr crtc, struct drmmode_scanout *scanout,
 						 pScrn->depth,
 						 pScrn->bitsPerPixel,
 						 pitch, scanout->bo, NULL);
-	if (scanout->pixmap) {
+	if (!scanout->pixmap) {
+		ErrorF("failed to create CRTC scanout pixmap\n");
+		goto error;
+	}
+
+	if (radeon_pixmap_get_fb(scanout->pixmap)) {
 		scanout->width = width;
 		scanout->height = height;
 	} else {
-		xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
-			   "Couldn't allocate scanout pixmap for CRTC\n");
-error:
+		ErrorF("failed to create CRTC scanout FB\n");
+error:		
 		drmmode_crtc_scanout_destroy(drmmode, scanout);
 	}
 
@@ -706,8 +702,8 @@ drmmode_handle_transform(xf86CrtcPtr crtc)
 
 static void
 drmmode_crtc_prime_scanout_update(xf86CrtcPtr crtc, DisplayModePtr mode,
-				  unsigned scanout_id, int *fb_id, int *x,
-				  int *y)
+				  unsigned scanout_id, struct drmmode_fb **fb,
+				  int *x, int *y)
 {
 	ScrnInfoPtr scrn = crtc->scrn;
 	ScreenPtr screen = scrn->pScreen;
@@ -759,7 +755,7 @@ drmmode_crtc_prime_scanout_update(xf86CrtcPtr crtc, DisplayModePtr mode,
 		}
 	}
 
-	*fb_id = drmmode_crtc->scanout[scanout_id].fb_id;
+	*fb = radeon_pixmap_get_fb(drmmode_crtc->scanout[scanout_id].pixmap);
 	*x = *y = 0;
 	drmmode_crtc->scanout_id = scanout_id;
 }
@@ -768,7 +764,8 @@ drmmode_crtc_prime_scanout_update(xf86CrtcPtr crtc, DisplayModePtr mode,
 
 static void
 drmmode_crtc_scanout_update(xf86CrtcPtr crtc, DisplayModePtr mode,
-			    unsigned scanout_id, int *fb_id, int *x, int *y)
+			    unsigned scanout_id, struct drmmode_fb **fb, int *x,
+			    int *y)
 {
 	ScrnInfoPtr scrn = crtc->scrn;
 	ScreenPtr screen = scrn->pScreen;
@@ -804,7 +801,7 @@ drmmode_crtc_scanout_update(xf86CrtcPtr crtc, DisplayModePtr mode,
 		box->x2 = max(box->x2, scrn->virtualX);
 		box->y2 = max(box->y2, scrn->virtualY);
 
-		*fb_id = drmmode_crtc->scanout[scanout_id].fb_id;
+		*fb = radeon_pixmap_get_fb(drmmode_crtc->scanout[scanout_id].pixmap);
 		*x = *y = 0;
 
 		radeon_scanout_do_update(crtc, scanout_id);
@@ -841,7 +838,7 @@ drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
 	int output_count = 0;
 	Bool ret = FALSE;
 	int i;
-	int fb_id;
+	struct drmmode_fb *fb = NULL;
 	drmModeModeInfo kmode;
 
 	/* The root window contents may be undefined before the WindowExposures
@@ -889,15 +886,14 @@ drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
 
 		drmmode_ConvertToKMode(crtc->scrn, &kmode, mode);
 
-		fb_id = drmmode->fb_id;
 #ifdef RADEON_PIXMAP_SHARING
 		if (crtc->randr_crtc && crtc->randr_crtc->scanout_pixmap) {
 			drmmode_crtc_prime_scanout_update(crtc, mode, scanout_id,
-							  &fb_id, &x, &y);
+							  &fb, &x, &y);
 		} else
 #endif
-		if (drmmode_crtc->rotate.fb_id) {
-			fb_id = drmmode_crtc->rotate.fb_id;
+		if (drmmode_crtc->rotate.pixmap) {
+			fb = radeon_pixmap_get_fb(drmmode_crtc->rotate.pixmap);
 			x = y = 0;
 
 		} else if (!radeon_is_gpu_screen(pScreen) &&
@@ -907,22 +903,24 @@ drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
 #endif
 			    info->shadow_primary)) {
 			drmmode_crtc_scanout_update(crtc, mode, scanout_id,
-						    &fb_id, &x, &y);
+						    &fb, &x, &y);
 		}
 
-		if (fb_id == 0) {
-			if (drmModeAddFB(drmmode->fd,
-					 pScrn->virtualX,
-					 pScrn->virtualY,
-					 pScrn->depth, pScrn->bitsPerPixel,
-					 pScrn->displayWidth * info->pixel_bytes,
-					 info->front_bo->handle,
-					 &drmmode->fb_id) < 0) {
-				ErrorF("failed to add fb\n");
-				goto done;
-			}
-
-			fb_id = drmmode->fb_id;
+		if (!fb)
+			fb = radeon_pixmap_get_fb(pScreen->GetWindowPixmap(pScreen->root));
+		if (!fb) {
+			fb = radeon_fb_create(drmmode->fd, pScrn->virtualX,
+					      pScrn->virtualY, pScrn->depth,
+					      pScrn->bitsPerPixel,
+					      pScrn->displayWidth * info->pixel_bytes,
+					      info->front_bo->handle);
+			/* Prevent refcnt of ad-hoc FBs from reaching 2 */
+			drmmode_fb_reference(drmmode->fd, &drmmode_crtc->fb, NULL);
+			drmmode_crtc->fb = fb;
+		}
+		if (!fb) {
+			ErrorF("failed to add FB for modeset\n");
+			goto done;
 		}
 
 		/* Wait for any pending flip to finish */
@@ -932,13 +930,15 @@ drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
 
 		if (drmModeSetCrtc(drmmode->fd,
 				   drmmode_crtc->mode_crtc->crtc_id,
-				   fb_id, x, y, output_ids,
+				   fb->handle, x, y, output_ids,
 				   output_count, &kmode) != 0) {
 			xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
 				   "failed to set mode: %s\n", strerror(errno));
 			goto done;
-		} else
+		} else {
 			ret = TRUE;
+			drmmode_fb_reference(drmmode->fd, &drmmode_crtc->fb, fb);
+		}
 
 		if (pScreen)
 			xf86CrtcSetScreenSubpixelOrder(pScreen);
@@ -983,7 +983,9 @@ done:
 	} else {
 		crtc->active = TRUE;
 
-		if (fb_id != drmmode_crtc->scanout[scanout_id].fb_id)
+		if (drmmode_crtc->scanout[scanout_id].pixmap &&
+		    fb != radeon_pixmap_get_fb(drmmode_crtc->
+					       scanout[scanout_id].pixmap))
 			drmmode_crtc_scanout_free(drmmode_crtc);
 		else if (!drmmode_crtc->tear_free) {
 			drmmode_crtc_scanout_destroy(drmmode,
@@ -2157,13 +2159,9 @@ static Bool
 drmmode_xf86crtc_resize (ScrnInfoPtr scrn, int width, int height)
 {
 	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
-	drmmode_crtc_private_ptr
-		    drmmode_crtc = xf86_config->crtc[0]->driver_private;
-	drmmode_ptr drmmode = drmmode_crtc->drmmode;
 	RADEONInfoPtr info = RADEONPTR(scrn);
 	struct radeon_bo *old_front = NULL;
 	ScreenPtr   screen = xf86ScrnToScreen(scrn);
-	uint32_t    old_fb_id;
 	int	    i, pitch, old_width, old_height, old_pitch;
 	int aligned_height;
 	uint32_t screen_size;
@@ -2263,8 +2261,6 @@ drmmode_xf86crtc_resize (ScrnInfoPtr scrn, int width, int height)
 	old_width = scrn->virtualX;
 	old_height = scrn->virtualY;
 	old_pitch = scrn->displayWidth;
-	old_fb_id = drmmode->fb_id;
-	drmmode->fb_id = 0;
 	old_front = info->front_bo;
 
 	scrn->virtualX = width;
@@ -2346,8 +2342,6 @@ drmmode_xf86crtc_resize (ScrnInfoPtr scrn, int width, int height)
 				       crtc->rotation, crtc->x, crtc->y);
 	}
 
-	if (old_fb_id)
-		drmModeRmFB(drmmode->fd, old_fb_id);
 	if (old_front)
 		radeon_bo_unref(old_front);
 
@@ -2361,7 +2355,6 @@ drmmode_xf86crtc_resize (ScrnInfoPtr scrn, int width, int height)
 	scrn->virtualX = old_width;
 	scrn->virtualY = old_height;
 	scrn->displayWidth = old_pitch;
-	drmmode->fb_id = old_fb_id;
 
 	return FALSE;
 }
@@ -2375,7 +2368,7 @@ drmmode_clear_pending_flip(xf86CrtcPtr crtc)
 {
 	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
 
-	drmmode_crtc->flip_pending = FALSE;
+	drmmode_crtc->flip_pending = NULL;
 
 	if (!crtc->enabled ||
 	    (drmmode_crtc->pending_dpms_mode != DPMSModeOn &&
@@ -2419,7 +2412,7 @@ drmmode_flip_abort(xf86CrtcPtr crtc, void *event_data)
 static void
 drmmode_flip_handler(xf86CrtcPtr crtc, uint32_t frame, uint64_t usec, void *event_data)
 {
-	RADEONInfoPtr info = RADEONPTR(crtc->scrn);
+	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
 	drmmode_flipdata_ptr flipdata = event_data;
 
 	/* Is this the event whose info shall be delivered to higher level? */
@@ -2439,12 +2432,11 @@ drmmode_flip_handler(xf86CrtcPtr crtc, uint32_t frame, uint64_t usec, void *even
 		else
 			flipdata->handler(crtc, frame, usec, flipdata->event_data);
 
-		/* Release framebuffer */
-		drmModeRmFB(info->drmmode.fd, flipdata->old_fb_id);
-
 		free(flipdata);
 	}
 
+	drmmode_fb_reference(drmmode_crtc->drmmode->fd, &drmmode_crtc->fb,
+			     drmmode_crtc->flip_pending);
 	drmmode_clear_pending_flip(crtc);
 }
 
@@ -2701,6 +2693,8 @@ Bool drmmode_set_desired_modes(ScrnInfoPtr pScrn, drmmode_ptr drmmode,
 				drmModeSetCrtc(drmmode->fd,
 					       drmmode_crtc->mode_crtc->crtc_id,
 					       0, 0, 0, NULL, 0, NULL);
+				drmmode_fb_reference(drmmode->fd,
+						     &drmmode_crtc->fb, NULL);
 			}
 			continue;
 		}
@@ -2960,18 +2954,11 @@ Bool radeon_do_pageflip(ScrnInfoPtr scrn, ClientPtr client,
 	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
 	xf86CrtcPtr crtc = NULL;
 	drmmode_crtc_private_ptr drmmode_crtc = config->crtc[0]->driver_private;
-	drmmode_ptr drmmode = drmmode_crtc->drmmode;
 	int i;
 	uint32_t flip_flags = flip_sync == FLIP_ASYNC ? DRM_MODE_PAGE_FLIP_ASYNC : 0;
 	drmmode_flipdata_ptr flipdata;
 	uintptr_t drm_queue_seq = 0;
-	uint32_t new_front_handle;
-
-	if (!radeon_get_pixmap_handle(new_front, &new_front_handle)) {
-		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
-			   "flip queue: failed to get new front handle\n");
-		return FALSE;
-	}
+	struct drmmode_fb *fb;
 
         flipdata = calloc(1, sizeof(drmmode_flipdata_rec));
         if (!flipdata) {
@@ -2980,15 +2967,11 @@ Bool radeon_do_pageflip(ScrnInfoPtr scrn, ClientPtr client,
              goto error;
         }
 
-	/*
-	 * Create a new handle for the back buffer
-	 */
-	flipdata->old_fb_id = drmmode->fb_id;
-	if (drmModeAddFB(drmmode->fd, new_front->drawable.width,
-			 new_front->drawable.height, scrn->depth,
-			 scrn->bitsPerPixel, new_front->devKind,
-			 new_front_handle, &drmmode->fb_id))
+	fb = radeon_pixmap_get_fb(new_front);
+	if (!fb) {
+		ErrorF("Failed to get FB for flip\n");
 		goto error;
+	}
 
 	/*
 	 * Queue flips on all enabled CRTCs
@@ -3032,7 +3015,7 @@ Bool radeon_do_pageflip(ScrnInfoPtr scrn, ClientPtr client,
 		if (drmmode_crtc->hw_id == ref_crtc_hw_id) {
 			if (drmmode_page_flip_target_absolute(pRADEONEnt,
 							      drmmode_crtc,
-							      drmmode->fb_id,
+							      fb->handle,
 							      flip_flags,
 							      drm_queue_seq,
 							      target_msc) != 0)
@@ -3040,13 +3023,13 @@ Bool radeon_do_pageflip(ScrnInfoPtr scrn, ClientPtr client,
 		} else {
 			if (drmmode_page_flip_target_relative(pRADEONEnt,
 							      drmmode_crtc,
-							      drmmode->fb_id,
+							      fb->handle,
 							      flip_flags,
 							      drm_queue_seq, 0) != 0)
 				goto flip_error;
 		}
 
-		drmmode_crtc->flip_pending = TRUE;
+		drmmode_crtc->flip_pending = fb;
 		drm_queue_seq = 0;
 	}
 
@@ -3058,12 +3041,6 @@ flip_error:
 		   strerror(errno));
 
 error:
-	if (flipdata && flipdata->flip_count <= 1 &&
-	    drmmode->fb_id != flipdata->old_fb_id) {
-		drmModeRmFB(drmmode->fd, drmmode->fb_id);
-		drmmode->fb_id = flipdata->old_fb_id;
-	}
-
 	if (drm_queue_seq)
 		radeon_drm_abort_entry(drm_queue_seq);
 	else if (crtc)
diff --git a/src/drmmode_display.h b/src/drmmode_display.h
index 35d64179d..14d1cb034 100644
--- a/src/drmmode_display.h
+++ b/src/drmmode_display.h
@@ -41,7 +41,6 @@
 
 typedef struct {
   int fd;
-  unsigned fb_id;
   drmModeFBPtr mode_fb;
   int cpp;
   struct radeon_bo_manager *bufmgr;
@@ -60,7 +59,6 @@ typedef struct {
 } drmmode_rec, *drmmode_ptr;
 
 typedef struct {
-  unsigned old_fb_id;
   int flip_count;
   void *event_data;
   unsigned int fe_frame;
@@ -70,10 +68,14 @@ typedef struct {
   radeon_drm_abort_proc abort;
 } drmmode_flipdata_rec, *drmmode_flipdata_ptr;
 
+struct drmmode_fb {
+	int refcnt;
+	uint32_t handle;
+};
+
 struct drmmode_scanout {
     struct radeon_bo *bo;
     PixmapPtr pixmap;
-    unsigned fb_id;
     int width, height;
 };
 
@@ -102,8 +104,10 @@ typedef struct {
      * modeset)
      */
     Bool need_modeset;
-    /* A flip is pending for this CRTC */
-    Bool flip_pending;
+    /* A flip to this FB is pending for this CRTC */
+    struct drmmode_fb *flip_pending;
+    /* The FB currently being scanned out by this CRTC, if any */
+    struct drmmode_fb *fb;
 } drmmode_crtc_private_rec, *drmmode_crtc_private_ptr;
 
 typedef struct {
@@ -135,6 +139,31 @@ enum drmmode_flip_sync {
 };
 
 
+static inline void
+drmmode_fb_reference(int drm_fd, struct drmmode_fb **old, struct drmmode_fb *new)
+{
+    if (new) {
+	if (new->refcnt <= 0)
+	    ErrorF("New FB's refcnt was %d in %s\n", new->refcnt, __func__);
+	else
+	    new->refcnt++;
+    }
+
+    if (*old) {
+	if ((*old)->refcnt <= 0) {
+	    ErrorF("Old FB's refcnt was %d in %s\n", (*old)->refcnt, __func__);
+	} else {
+	    if (--(*old)->refcnt == 0) {
+		drmModeRmFB(drm_fd, (*old)->handle);
+		free(*old);
+	    }
+	}
+    }
+
+    *old = new;
+}
+
+
 extern int drmmode_page_flip_target_absolute(RADEONEntPtr pRADEONEnt,
 					     drmmode_crtc_private_ptr drmmode_crtc,
 					     int fb_id, uint32_t flags,
diff --git a/src/radeon.h b/src/radeon.h
index 2cb188e1f..febe580b6 100644
--- a/src/radeon.h
+++ b/src/radeon.h
@@ -288,6 +288,7 @@ struct radeon_pixmap {
 	uint_fast32_t gpu_write;
 
 	struct radeon_bo *bo;
+	struct drmmode_fb *fb;
 
 	uint32_t tiling_flags;
 
@@ -313,6 +314,7 @@ static inline void radeon_set_pixmap_private(PixmapPtr pixmap, struct radeon_pix
 
 struct radeon_exa_pixmap_priv {
     struct radeon_bo *bo;
+    struct drmmode_fb *fb;
     uint32_t tiling_flags;
     struct radeon_surface surface;
     Bool bo_mapped;
@@ -609,6 +611,9 @@ extern void  RADEONCopySwap(uint8_t *dst, uint8_t *src, unsigned int size, int s
 extern void RADEONInit3DEngine(ScrnInfoPtr pScrn);
 extern int radeon_cs_space_remaining(ScrnInfoPtr pScrn);
 
+/* radeon_bo_helper.c */
+extern Bool radeon_get_pixmap_handle(PixmapPtr pixmap, uint32_t *handle);
+
 /* radeon_commonfuncs.c */
 extern void RADEONWaitForVLine(ScrnInfoPtr pScrn, PixmapPtr pPix,
 			       xf86CrtcPtr crtc, int start, int stop);
@@ -706,6 +711,8 @@ static inline Bool radeon_set_pixmap_bo(PixmapPtr pPix, struct radeon_bo *bo)
 		radeon_bo_unref(priv->bo);
 	    }
 
+	    drmmode_fb_reference(info->drmmode.fd, &priv->fb, NULL);
+
 	    if (!bo) {
 		free(priv);
 		priv = NULL;
@@ -790,6 +797,72 @@ static inline Bool radeon_get_pixmap_shared(PixmapPtr pPix)
     return FALSE;
 }
 
+static inline struct drmmode_fb*
+radeon_fb_create(int drm_fd, uint32_t width, uint32_t height, uint8_t depth,
+		 uint8_t bpp, uint32_t pitch, uint32_t handle)
+{
+    struct drmmode_fb *fb  = malloc(sizeof(*fb));
+
+    if (!fb)
+	return NULL;
+
+    fb->refcnt = 1;
+    if (drmModeAddFB(drm_fd, width, height, depth, bpp, pitch, handle,
+		     &fb->handle) == 0)
+	return fb;
+
+    free(fb);
+    return NULL;
+}
+
+static inline struct drmmode_fb*
+radeon_pixmap_create_fb(int drm_fd, PixmapPtr pix)
+{
+    uint32_t handle;
+
+    if (!radeon_get_pixmap_handle(pix, &handle))
+	return NULL;
+
+    return radeon_fb_create(drm_fd, pix->drawable.width, pix->drawable.height,
+			    pix->drawable.depth, pix->drawable.bitsPerPixel,
+			    pix->devKind, handle);
+}
+
+static inline struct drmmode_fb*
+radeon_pixmap_get_fb(PixmapPtr pix)
+{
+    RADEONInfoPtr info = RADEONPTR(xf86ScreenToScrn(pix->drawable.pScreen));
+
+#ifdef USE_GLAMOR
+    if (info->use_glamor) {
+	struct radeon_pixmap *priv = radeon_get_pixmap_private(pix);
+
+	if (!priv)
+	    return NULL;
+
+	if (!priv->fb)
+	    priv->fb = radeon_pixmap_create_fb(info->drmmode.fd, pix);
+
+	return priv->fb;
+    } else
+#endif
+    if (info->accelOn)
+    {
+	struct radeon_exa_pixmap_priv *driver_priv =
+	    exaGetPixmapDriverPrivate(pix);
+
+	if (!driver_priv)
+	    return NULL;
+
+	if (!driver_priv->fb)
+	    driver_priv->fb = radeon_pixmap_create_fb(info->drmmode.fd, pix);
+
+	return driver_priv->fb;
+    }
+
+    return NULL;
+}
+
 #define CP_PACKET0(reg, n)						\
 	(RADEON_CP_PACKET0 | ((n) << 16) | ((reg) >> 2))
 #define CP_PACKET1(reg0, reg1)						\
diff --git a/src/radeon_bo_helper.h b/src/radeon_bo_helper.h
index f1aed5516..771342502 100644
--- a/src/radeon_bo_helper.h
+++ b/src/radeon_bo_helper.h
@@ -28,9 +28,6 @@ radeon_alloc_pixmap_bo(ScrnInfoPtr pScrn, int width, int height, int depth,
 		       int usage_hint, int bitsPerPixel, int *new_pitch,
 		       struct radeon_surface *new_surface, uint32_t *new_tiling);
 
-extern Bool
-radeon_get_pixmap_handle(PixmapPtr pixmap, uint32_t *handle);
-
 extern uint32_t
 radeon_get_pixmap_tiling_flags(PixmapPtr pPix);
 
diff --git a/src/radeon_exa.c b/src/radeon_exa.c
index 1e457a8bb..d8dd7fdce 100644
--- a/src/radeon_exa.c
+++ b/src/radeon_exa.c
@@ -300,6 +300,7 @@ void *RADEONEXACreatePixmap2(ScreenPtr pScreen, int width, int height,
 
 void RADEONEXADestroyPixmap(ScreenPtr pScreen, void *driverPriv)
 {
+    RADEONInfoPtr info = RADEONPTR(xf86ScreenToScrn(pScreen));
     struct radeon_exa_pixmap_priv *driver_priv = driverPriv;
 
     if (!driverPriv)
@@ -307,6 +308,7 @@ void RADEONEXADestroyPixmap(ScreenPtr pScreen, void *driverPriv)
 
     if (driver_priv->bo)
 	radeon_bo_unref(driver_priv->bo);
+    drmmode_fb_reference(info->drmmode.fd, &driver_priv->fb, NULL);
     free(driverPriv);
 }
 
diff --git a/src/radeon_kms.c b/src/radeon_kms.c
index b3427c462..2b410eb3d 100644
--- a/src/radeon_kms.c
+++ b/src/radeon_kms.c
@@ -772,6 +772,17 @@ radeon_prime_scanout_flip_abort(xf86CrtcPtr crtc, void *event_data)
 }
 
 static void
+radeon_prime_scanout_flip_handler(xf86CrtcPtr crtc, uint32_t msc, uint64_t usec,
+				  void *event_data)
+{
+    drmmode_crtc_private_ptr drmmode_crtc = event_data;
+
+    drmmode_fb_reference(drmmode_crtc->drmmode->fd, &drmmode_crtc->fb,
+			 drmmode_crtc->flip_pending);
+    radeon_prime_scanout_flip_abort(crtc, event_data);
+}
+
+static void
 radeon_prime_scanout_flip(PixmapDirtyUpdatePtr ent)
 {
     ScreenPtr screen = ent->slave_dst->drawable.pScreen;
@@ -798,7 +809,8 @@ radeon_prime_scanout_flip(PixmapDirtyUpdatePtr ent)
     drm_queue_seq = radeon_drm_queue_alloc(crtc,
 					   RADEON_DRM_QUEUE_CLIENT_DEFAULT,
 					   RADEON_DRM_QUEUE_ID_DEFAULT,
-					   drmmode_crtc, NULL,
+					   drmmode_crtc,
+					   radeon_prime_scanout_flip_handler,
 					   radeon_prime_scanout_flip_abort);
     if (drm_queue_seq == RADEON_DRM_QUEUE_ERROR) {
 	xf86DrvMsg(scrn->scrnIndex, X_WARNING,
@@ -806,8 +818,17 @@ radeon_prime_scanout_flip(PixmapDirtyUpdatePtr ent)
 	return;
     }
 
+    drmmode_crtc->flip_pending =
+	radeon_pixmap_get_fb(drmmode_crtc->scanout[scanout_id].pixmap);
+    if (!drmmode_crtc->flip_pending) {
+	xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+		   "Failed to get FB for PRIME flip.\n");
+	radeon_drm_abort_entry(drm_queue_seq);
+	return;
+    }
+
     if (drmmode_page_flip_target_relative(pRADEONEnt, drmmode_crtc,
-					  drmmode_crtc->scanout[scanout_id].fb_id,
+					  drmmode_crtc->flip_pending->handle,
 					  0, drm_queue_seq, 0) != 0) {
 	xf86DrvMsg(scrn->scrnIndex, X_WARNING, "flip queue failed in %s: %s\n",
 		   __func__, strerror(errno));
@@ -817,7 +838,6 @@ radeon_prime_scanout_flip(PixmapDirtyUpdatePtr ent)
 
     drmmode_crtc->scanout_id = scanout_id;
     drmmode_crtc->scanout_update_pending = TRUE;
-    drmmode_crtc->flip_pending = TRUE;
 }
 
 static void
@@ -1053,10 +1073,14 @@ radeon_scanout_update(xf86CrtcPtr xf86_crtc)
 static void
 radeon_scanout_flip_abort(xf86CrtcPtr crtc, void *event_data)
 {
-    drmmode_crtc_private_ptr drmmode_crtc = event_data;
+    radeon_prime_scanout_flip_abort(crtc, event_data);
+}
 
-    drmmode_crtc->scanout_update_pending = FALSE;
-    drmmode_clear_pending_flip(crtc);
+static void
+radeon_scanout_flip_handler(xf86CrtcPtr crtc, uint32_t msc, uint64_t usec,
+			    void *event_data)
+{
+    radeon_prime_scanout_flip_handler(crtc, msc, usec, event_data);
 }
 
 static void
@@ -1080,7 +1104,8 @@ radeon_scanout_flip(ScreenPtr pScreen, RADEONInfoPtr info,
     drm_queue_seq = radeon_drm_queue_alloc(xf86_crtc,
 					   RADEON_DRM_QUEUE_CLIENT_DEFAULT,
 					   RADEON_DRM_QUEUE_ID_DEFAULT,
-					   drmmode_crtc, NULL,
+					   drmmode_crtc,
+					   radeon_scanout_flip_handler,
 					   radeon_scanout_flip_abort);
     if (drm_queue_seq == RADEON_DRM_QUEUE_ERROR) {
 	xf86DrvMsg(scrn->scrnIndex, X_WARNING,
@@ -1088,8 +1113,17 @@ radeon_scanout_flip(ScreenPtr pScreen, RADEONInfoPtr info,
 	return;
     }
 
+    drmmode_crtc->flip_pending =
+	radeon_pixmap_get_fb(drmmode_crtc->scanout[scanout_id].pixmap);
+    if (!drmmode_crtc->flip_pending) {
+	xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+		   "Failed to get FB for scanout flip.\n");
+	radeon_drm_abort_entry(drm_queue_seq);
+	return;
+    }
+
     if (drmmode_page_flip_target_relative(pRADEONEnt, drmmode_crtc,
-					  drmmode_crtc->scanout[scanout_id].fb_id,
+					  drmmode_crtc->flip_pending->handle,
 					  0, drm_queue_seq, 0) != 0) {
 	xf86DrvMsg(scrn->scrnIndex, X_WARNING, "flip queue failed in %s: %s\n",
 		   __func__, strerror(errno));
@@ -1099,7 +1133,6 @@ radeon_scanout_flip(ScreenPtr pScreen, RADEONInfoPtr info,
 
     drmmode_crtc->scanout_id = scanout_id;
     drmmode_crtc->scanout_update_pending = TRUE;
-    drmmode_crtc->flip_pending = TRUE;
 }
 
 static void RADEONBlockHandler_KMS(BLOCKHANDLER_ARGS_DECL)
@@ -1114,6 +1147,19 @@ static void RADEONBlockHandler_KMS(BLOCKHANDLER_ARGS_DECL)
     (*pScreen->BlockHandler) (BLOCKHANDLER_ARGS);
     pScreen->BlockHandler = RADEONBlockHandler_KMS;
 
+    if (!pScrn->vtSema) {
+	radeon_cs_flush_indirect(pScrn);
+
+	for (c = 0; c < xf86_config->num_crtc; c++) {
+	    drmmode_crtc_private_ptr drmmode_crtc =
+		xf86_config->crtc[c]->driver_private;
+
+	    drmmode_fb_reference(info->drmmode.fd, &drmmode_crtc->fb, NULL);
+	}
+
+	return;
+    }
+
     if (!radeon_is_gpu_screen(pScreen))
     {
 	for (c = 0; c < xf86_config->num_crtc; c++) {
diff --git a/src/radeon_present.c b/src/radeon_present.c
index 90632d0ec..635d10861 100644
--- a/src/radeon_present.c
+++ b/src/radeon_present.c
@@ -373,7 +373,6 @@ radeon_present_unflip(ScreenPtr screen, uint64_t event_id)
     enum drmmode_flip_sync flip_sync =
 	(radeon_present_screen_info.capabilities & PresentCapabilityAsync) ?
 	FLIP_ASYNC : FLIP_VSYNC;
-    int old_fb_id;
     int i;
 
     radeon_cs_flush_indirect(scrn);
@@ -396,12 +395,6 @@ radeon_present_unflip(ScreenPtr screen, uint64_t event_id)
 	return;
 
 modeset:
-    /* info->drmmode.fb_id still points to the FB for the last flipped BO.
-     * Clear it, drmmode_set_mode_major will re-create it
-     */
-    old_fb_id = info->drmmode.fb_id;
-    info->drmmode.fb_id = 0;
-
     radeon_bo_wait(info->front_bo);
     for (i = 0; i < config->num_crtc; i++) {
 	xf86CrtcPtr crtc = config->crtc[i];
@@ -417,7 +410,6 @@ modeset:
 	    drmmode_crtc->need_modeset = TRUE;
     }
 
-    drmModeRmFB(info->drmmode.fd, old_fb_id);
     present_event_notify(event_id, 0, 0);
 
     info->drmmode.present_flipping = FALSE;
-- 
2.11.0



More information about the amd-gfx mailing list