Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

dri2: Implement handling of pageflip completion events.

Requests pageflip completion events from the kernel.
Implements pageflip completion handler to finalize
and timestamp swaps.

Completion handler includes a consistency check, and
disambiguation if multiple crtc's are involved in a
pageflip (e.g., clone mode, extendend desktop). Only
the timestamp of the crtc whose vblank event initially
triggered the swap is used, but handler waits for flip
completion on all involved crtc's before completing the
swap and releasing the old framebuffer.

This code is almost identical to the code used in the
ati/radeon ddx and intel ddx.

Signed-off-by: Mario Kleiner <mario.kleiner@tuebingen.mpg.de>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
  • Loading branch information...
commit 820916e722db2997a53128f2f370d14a02d4401c 1 parent 4da68ce
Mario Kleiner kleinerm authored Ben Skeggs committed

Showing 3 changed files with 191 additions and 10 deletions. Show diff stats Hide diff stats

  1. +103 4 src/drmmode_display.c
  2. +84 5 src/nouveau_dri2.c
  3. +4 1 src/nv_proto.h
107 src/drmmode_display.c
@@ -83,6 +83,21 @@ typedef struct {
83 83 drmmode_prop_ptr props;
84 84 } drmmode_output_private_rec, *drmmode_output_private_ptr;
85 85
  86 +typedef struct {
  87 + drmmode_ptr drmmode;
  88 + unsigned old_fb_id;
  89 + int flip_count;
  90 + void *event_data;
  91 + unsigned int fe_frame;
  92 + unsigned int fe_tv_sec;
  93 + unsigned int fe_tv_usec;
  94 +} drmmode_flipdata_rec, *drmmode_flipdata_ptr;
  95 +
  96 +typedef struct {
  97 + drmmode_flipdata_ptr flipdata;
  98 + Bool dispatch_me;
  99 +} drmmode_flipevtcarrier_rec, *drmmode_flipevtcarrier_ptr;
  100 +
86 101 static void drmmode_output_dpms(xf86OutputPtr output, int mode);
87 102
88 103 static drmmode_ptr
@@ -1245,13 +1260,17 @@ drmmode_cursor_init(ScreenPtr pScreen)
1245 1260 }
1246 1261
1247 1262 Bool
1248   -drmmode_page_flip(DrawablePtr draw, PixmapPtr back, void *priv)
  1263 +drmmode_page_flip(DrawablePtr draw, PixmapPtr back, void *priv,
  1264 + unsigned int ref_crtc_hw_id)
1249 1265 {
1250 1266 ScrnInfoPtr scrn = xf86Screens[draw->pScreen->myNum];
1251 1267 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
1252 1268 drmmode_crtc_private_ptr crtc = config->crtc[0]->driver_private;
1253 1269 drmmode_ptr mode = crtc->drmmode;
1254 1270 int ret, i, old_fb_id;
  1271 + int emitted = 0;
  1272 + drmmode_flipdata_ptr flipdata;
  1273 + drmmode_flipevtcarrier_ptr flipcarrier;
1255 1274
1256 1275 old_fb_id = mode->fb_id;
1257 1276 ret = drmModeAddFB(mode->fd, scrn->virtualX, scrn->virtualY,
@@ -1264,24 +1283,64 @@ drmmode_page_flip(DrawablePtr draw, PixmapPtr back, void *priv)
1264 1283 return FALSE;
1265 1284 }
1266 1285
  1286 + flipdata = calloc(1, sizeof(drmmode_flipdata_rec));
  1287 + if (!flipdata) {
  1288 + xf86DrvMsg(scrn->scrnIndex, X_WARNING,
  1289 + "flip queue: data alloc failed.\n");
  1290 + goto error_undo;
  1291 + }
  1292 +
  1293 + flipdata->event_data = priv;
  1294 + flipdata->drmmode = mode;
  1295 +
1267 1296 for (i = 0; i < config->num_crtc; i++) {
1268 1297 crtc = config->crtc[i]->driver_private;
1269 1298
1270 1299 if (!config->crtc[i]->enabled)
1271 1300 continue;
1272 1301
  1302 + flipdata->flip_count++;
  1303 +
  1304 + flipcarrier = calloc(1, sizeof(drmmode_flipevtcarrier_rec));
  1305 + if (!flipcarrier) {
  1306 + xf86DrvMsg(scrn->scrnIndex, X_WARNING,
  1307 + "flip queue: carrier alloc failed.\n");
  1308 + if (emitted == 0)
  1309 + free(flipdata);
  1310 + goto error_undo;
  1311 + }
  1312 +
  1313 + /* Only the reference crtc will finally deliver its page flip
  1314 + * completion event. All other crtc's events will be discarded.
  1315 + */
  1316 + flipcarrier->dispatch_me = ((1 << i) == ref_crtc_hw_id);
  1317 + flipcarrier->flipdata = flipdata;
  1318 +
1273 1319 ret = drmModePageFlip(mode->fd, crtc->mode_crtc->crtc_id,
1274   - mode->fb_id, 0, priv);
  1320 + mode->fb_id, DRM_MODE_PAGE_FLIP_EVENT,
  1321 + flipcarrier);
1275 1322 if (ret) {
1276 1323 xf86DrvMsg(scrn->scrnIndex, X_WARNING,
1277 1324 "flip queue failed: %s\n", strerror(errno));
1278   - return FALSE;
  1325 +
  1326 + free(flipcarrier);
  1327 + if (emitted == 0)
  1328 + free(flipdata);
  1329 + goto error_undo;
1279 1330 }
  1331 +
  1332 + emitted++;
1280 1333 }
1281 1334
1282   - drmModeRmFB(mode->fd, old_fb_id);
  1335 + /* Will release old fb after all crtc's completed flip. */
  1336 + flipdata->old_fb_id = old_fb_id;
1283 1337
1284 1338 return TRUE;
  1339 +
  1340 +error_undo:
  1341 + drmModeRmFB(mode->fd, mode->fb_id);
  1342 + mode->fb_id = old_fb_id;
  1343 + return FALSE;
1285 1344 }
1286 1345
1287 1346 #ifdef HAVE_LIBUDEV
@@ -1347,6 +1406,42 @@ drmmode_uevent_fini(ScrnInfoPtr scrn)
1347 1406 }
1348 1407
1349 1408 static void
  1409 +drmmode_flip_handler(int fd, unsigned int frame, unsigned int tv_sec,
  1410 + unsigned int tv_usec, void *event_data)
  1411 +{
  1412 + drmmode_flipevtcarrier_ptr flipcarrier = event_data;
  1413 + drmmode_flipdata_ptr flipdata = flipcarrier->flipdata;
  1414 + drmmode_ptr drmmode = flipdata->drmmode;
  1415 +
  1416 + /* Is this the event whose info shall be delivered to higher level? */
  1417 + if (flipcarrier->dispatch_me) {
  1418 + /* Yes: Cache msc, ust for later delivery. */
  1419 + flipdata->fe_frame = frame;
  1420 + flipdata->fe_tv_sec = tv_sec;
  1421 + flipdata->fe_tv_usec = tv_usec;
  1422 + }
  1423 + free(flipcarrier);
  1424 +
  1425 + /* Last crtc completed flip? */
  1426 + flipdata->flip_count--;
  1427 + if (flipdata->flip_count > 0)
  1428 + return;
  1429 +
  1430 + /* Release framebuffer */
  1431 + drmModeRmFB(drmmode->fd, flipdata->old_fb_id);
  1432 +
  1433 + if (flipdata->event_data == NULL) {
  1434 + free(flipdata);
  1435 + return;
  1436 + }
  1437 +
  1438 + /* Deliver cached msc, ust from reference crtc to flip event handler */
  1439 + nouveau_dri2_flip_event_handler(flipdata->fe_frame, flipdata->fe_tv_sec,
  1440 + flipdata->fe_tv_usec, flipdata->event_data);
  1441 + free(flipdata);
  1442 +}
  1443 +
  1444 +static void
1350 1445 drmmode_wakeup_handler(pointer data, int err, pointer p)
1351 1446 {
1352 1447 ScrnInfoPtr scrn = data;
@@ -1376,6 +1471,10 @@ drmmode_screen_init(ScreenPtr pScreen)
1376 1471 /* Plug in a vblank event handler */
1377 1472 drmmode->event_context.version = DRM_EVENT_CONTEXT_VERSION;
1378 1473 drmmode->event_context.vblank_handler = nouveau_dri2_vblank_handler;
  1474 +
  1475 + /* Plug in a pageflip completion event handler */
  1476 + drmmode->event_context.page_flip_handler = drmmode_flip_handler;
  1477 +
1379 1478 AddGeneralSocket(drmmode->fd);
1380 1479
1381 1480 /* Register a wakeup handler to get informed on DRM events */
89 src/nouveau_dri2.c
@@ -140,6 +140,7 @@ struct nouveau_dri2_vblank_state {
140 140 DRI2BufferPtr src;
141 141 DRI2SwapEventPtr func;
142 142 void *data;
  143 + unsigned int frame;
143 144 };
144 145
145 146 static Bool
@@ -225,6 +226,18 @@ nouveau_dri2_finish_swap(DrawablePtr draw, unsigned int frame,
225 226 REGION_INIT(0, &reg, (&(BoxRec){ 0, 0, draw->width, draw->height }), 0);
226 227 REGION_TRANSLATE(0, &reg, draw->x, draw->y);
227 228
  229 + /* Main crtc for this drawable shall finally deliver pageflip event. */
  230 + unsigned int ref_crtc_hw_id = nv_window_belongs_to_crtc(scrn, draw->x,
  231 + draw->y,
  232 + draw->width,
  233 + draw->height);
  234 +
  235 + /* Whenever first crtc is involved, choose it as reference, as
  236 + * its vblank event triggered this swap.
  237 + */
  238 + if (ref_crtc_hw_id & 1)
  239 + ref_crtc_hw_id = 1;
  240 +
228 241 /* Throttle on the previous frame before swapping */
229 242 nouveau_bo_map(dst_bo, NOUVEAU_BO_RD);
230 243 nouveau_bo_unmap(dst_bo);
@@ -249,7 +262,7 @@ nouveau_dri2_finish_swap(DrawablePtr draw, unsigned int frame,
249 262
250 263 if (DRI2CanFlip(draw)) {
251 264 type = DRI2_FLIP_COMPLETE;
252   - ret = drmmode_page_flip(draw, src_pix, s);
  265 + ret = drmmode_page_flip(draw, src_pix, s, ref_crtc_hw_id);
253 266 if (!ret)
254 267 goto out;
255 268 }
@@ -258,6 +271,10 @@ nouveau_dri2_finish_swap(DrawablePtr draw, unsigned int frame,
258 271 SWAP(nouveau_pixmap(dst_pix)->bo, nouveau_pixmap(src_pix)->bo);
259 272
260 273 DamageRegionProcessPending(draw);
  274 +
  275 + /* If it is a page flip, finish it in the flip event handler. */
  276 + if (type == DRI2_FLIP_COMPLETE)
  277 + return;
261 278 } else {
262 279 type = DRI2_BLIT_COMPLETE;
263 280
@@ -292,7 +309,7 @@ nouveau_dri2_schedule_swap(ClientPtr client, DrawablePtr draw,
292 309 DRI2SwapEventPtr func, void *data)
293 310 {
294 311 struct nouveau_dri2_vblank_state *s;
295   - CARD64 current_msc;
  312 + CARD64 current_msc, expect_msc;
296 313 int ret;
297 314
298 315 /* Initialize a swap structure */
@@ -301,7 +318,7 @@ nouveau_dri2_schedule_swap(ClientPtr client, DrawablePtr draw,
301 318 return FALSE;
302 319
303 320 *s = (struct nouveau_dri2_vblank_state)
304   - { SWAP, client, draw->id, dst, src, func, data };
  321 + { SWAP, client, draw->id, dst, src, func, data, 0 };
305 322
306 323 if (can_sync_to_vblank(draw)) {
307 324 /* Get current sequence */
@@ -319,10 +336,10 @@ nouveau_dri2_schedule_swap(ClientPtr client, DrawablePtr draw,
319 336 ret = nouveau_wait_vblank(draw, DRM_VBLANK_ABSOLUTE |
320 337 DRM_VBLANK_EVENT,
321 338 max(current_msc, *target_msc - 1),
322   - NULL, NULL, s);
  339 + &expect_msc, NULL, s);
323 340 if (ret)
324 341 goto fail;
325   -
  342 + s->frame = (unsigned int) expect_msc & 0xffffffff;
326 343 } else {
327 344 /* We can't/don't want to sync to vblank, just swap. */
328 345 nouveau_dri2_finish_swap(draw, 0, 0, 0, s);
@@ -426,6 +443,68 @@ nouveau_dri2_vblank_handler(int fd, unsigned int frame,
426 443 }
427 444 }
428 445
  446 +void
  447 +nouveau_dri2_flip_event_handler(unsigned int frame, unsigned int tv_sec,
  448 + unsigned int tv_usec, void *event_data)
  449 +{
  450 + struct nouveau_dri2_vblank_state *flip = event_data;
  451 + DrawablePtr draw;
  452 + ScreenPtr screen;
  453 + ScrnInfoPtr scrn;
  454 + int status;
  455 + PixmapPtr pixmap;
  456 +
  457 + status = dixLookupDrawable(&draw, flip->draw, serverClient,
  458 + M_ANY, DixWriteAccess);
  459 + if (status != Success) {
  460 + free(flip);
  461 + return;
  462 + }
  463 +
  464 + screen = draw->pScreen;
  465 + scrn = xf86Screens[screen->myNum];
  466 +
  467 + pixmap = screen->GetScreenPixmap(screen);
  468 + xf86DrvMsgVerb(scrn->scrnIndex, X_INFO, 4,
  469 + "%s: flipevent : width %d x height %d : msc %d : ust = %d.%06d\n",
  470 + __func__, pixmap->drawable.width, pixmap->drawable.height,
  471 + frame, tv_sec, tv_usec);
  472 +
  473 + /* We assume our flips arrive in order, so we don't check the frame */
  474 + switch (flip->action) {
  475 + case SWAP:
  476 + /* Check for too small vblank count of pageflip completion,
  477 + * taking wraparound into account. This usually means some
  478 + * defective kms pageflip completion, causing wrong (msc, ust)
  479 + * return values and possible visual corruption.
  480 + * Skip test for frame == 0, as this is a valid constant value
  481 + * reported by all Linux kernels at least up to Linux 3.0.
  482 + */
  483 + if ((frame != 0) &&
  484 + (frame < flip->frame) && (flip->frame - frame < 5)) {
  485 + xf86DrvMsg(scrn->scrnIndex, X_WARNING,
  486 + "%s: Pageflip has impossible msc %d < target_msc %d\n",
  487 + __func__, frame, flip->frame);
  488 + /* All-Zero values signal failure of (msc, ust)
  489 + * timestamping to client.
  490 + */
  491 + frame = tv_sec = tv_usec = 0;
  492 + }
  493 +
  494 + DRI2SwapComplete(flip->client, draw, frame, tv_sec, tv_usec,
  495 + DRI2_FLIP_COMPLETE, flip->func,
  496 + flip->data);
  497 + break;
  498 + default:
  499 + xf86DrvMsg(scrn->scrnIndex, X_WARNING,
  500 + "%s: unknown vblank event received\n", __func__);
  501 + /* Unknown type */
  502 + break;
  503 + }
  504 +
  505 + free(flip);
  506 +}
  507 +
429 508 Bool
430 509 nouveau_dri2_init(ScreenPtr pScreen)
431 510 {
5 src/nv_proto.h
@@ -7,7 +7,8 @@ void drmmode_adjust_frame(ScrnInfoPtr pScrn, int x, int y, int flags);
7 7 void drmmode_remove_fb(ScrnInfoPtr pScrn);
8 8 Bool drmmode_cursor_init(ScreenPtr pScreen);
9 9 void drmmode_fbcon_copy(ScreenPtr pScreen);
10   -Bool drmmode_page_flip(DrawablePtr draw, PixmapPtr back, void *priv);
  10 +Bool drmmode_page_flip(DrawablePtr draw, PixmapPtr back, void *priv,
  11 + unsigned int ref_crtc_hw_id);
11 12 void drmmode_screen_init(ScreenPtr pScreen);
12 13 void drmmode_screen_fini(ScreenPtr pScreen);
13 14
@@ -26,6 +27,8 @@ Bool nouveau_allocate_surface(ScrnInfoPtr scrn, int width, int height,
26 27 void nouveau_dri2_vblank_handler(int fd, unsigned int frame,
27 28 unsigned int tv_sec, unsigned int tv_usec,
28 29 void *event_data);
  30 +void nouveau_dri2_flip_event_handler(unsigned int frame, unsigned int tv_sec,
  31 + unsigned int tv_usec, void *event_data);
29 32 Bool nouveau_dri2_init(ScreenPtr pScreen);
30 33 void nouveau_dri2_fini(ScreenPtr pScreen);
31 34

0 comments on commit 820916e

Please sign in to comment.
Something went wrong with that request. Please try again.