dri2.c revision a035e2b2
1/* 2 * Copyright © 2013 Intel Corporation 3 * Copyright © 2014 Broadcom 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the "Software"), 7 * to deal in the Software without restriction, including without limitation 8 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 * and/or sell copies of the Software, and to permit persons to whom the 10 * Software is furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice (including the next 13 * paragraph) shall be included in all copies or substantial portions of the 14 * Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 * IN THE SOFTWARE. 23 */ 24 25/** 26 * @file dri2.c 27 * 28 * Implements generic support for DRI2 on KMS, using glamor pixmaps 29 * for color buffer management (no support for other aux buffers), and 30 * the DRM vblank ioctls. 31 * 32 * This doesn't implement pageflipping yet. 33 */ 34 35#ifdef HAVE_DIX_CONFIG_H 36#include "dix-config.h" 37#endif 38 39#include <time.h> 40#include "list.h" 41#include "xf86.h" 42#include "driver.h" 43#include "dri2.h" 44 45#ifdef GLAMOR_HAS_GBM 46 47enum ms_dri2_frame_event_type { 48 MS_DRI2_QUEUE_SWAP, 49 MS_DRI2_QUEUE_FLIP, 50 MS_DRI2_WAIT_MSC, 51}; 52 53typedef struct ms_dri2_frame_event { 54 ScreenPtr screen; 55 56 DrawablePtr drawable; 57 ClientPtr client; 58 enum ms_dri2_frame_event_type type; 59 int frame; 60 xf86CrtcPtr crtc; 61 62 struct xorg_list drawable_resource, client_resource; 63 64 /* for swaps & flips only */ 65 DRI2SwapEventPtr event_complete; 66 void *event_data; 67 DRI2BufferPtr front; 68 DRI2BufferPtr back; 69} ms_dri2_frame_event_rec, *ms_dri2_frame_event_ptr; 70 71typedef struct { 72 int refcnt; 73 PixmapPtr pixmap; 74} ms_dri2_buffer_private_rec, *ms_dri2_buffer_private_ptr; 75 76static DevPrivateKeyRec ms_dri2_client_key; 77static RESTYPE frame_event_client_type, frame_event_drawable_type; 78static int ms_dri2_server_generation; 79 80struct ms_dri2_resource { 81 XID id; 82 RESTYPE type; 83 struct xorg_list list; 84}; 85 86static struct ms_dri2_resource * 87ms_get_resource(XID id, RESTYPE type) 88{ 89 struct ms_dri2_resource *resource; 90 void *ptr; 91 92 ptr = NULL; 93 dixLookupResourceByType(&ptr, id, type, NULL, DixWriteAccess); 94 if (ptr) 95 return ptr; 96 97 resource = malloc(sizeof(*resource)); 98 if (resource == NULL) 99 return NULL; 100 101 if (!AddResource(id, type, resource)) 102 return NULL; 103 104 resource->id = id; 105 resource->type = type; 106 xorg_list_init(&resource->list); 107 return resource; 108} 109 110static inline PixmapPtr 111get_drawable_pixmap(DrawablePtr drawable) 112{ 113 ScreenPtr screen = drawable->pScreen; 114 115 if (drawable->type == DRAWABLE_PIXMAP) 116 return (PixmapPtr) drawable; 117 else 118 return screen->GetWindowPixmap((WindowPtr) drawable); 119} 120 121static DRI2Buffer2Ptr 122ms_dri2_create_buffer2(ScreenPtr screen, DrawablePtr drawable, 123 unsigned int attachment, unsigned int format) 124{ 125 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 126 DRI2Buffer2Ptr buffer; 127 PixmapPtr pixmap; 128 CARD32 size; 129 CARD16 pitch; 130 ms_dri2_buffer_private_ptr private; 131 132 buffer = calloc(1, sizeof *buffer); 133 if (buffer == NULL) 134 return NULL; 135 136 private = calloc(1, sizeof(*private)); 137 if (private == NULL) { 138 free(buffer); 139 return NULL; 140 } 141 142 pixmap = NULL; 143 if (attachment == DRI2BufferFrontLeft) { 144 pixmap = get_drawable_pixmap(drawable); 145 if (pixmap && pixmap->drawable.pScreen != screen) 146 pixmap = NULL; 147 if (pixmap) 148 pixmap->refcnt++; 149 } 150 151 if (pixmap == NULL) { 152 int pixmap_width = drawable->width; 153 int pixmap_height = drawable->height; 154 int pixmap_cpp = (format != 0) ? format : drawable->depth; 155 156 /* Assume that non-color-buffers require special 157 * device-specific handling. Mesa currently makes no requests 158 * for non-color aux buffers. 159 */ 160 switch (attachment) { 161 case DRI2BufferAccum: 162 case DRI2BufferBackLeft: 163 case DRI2BufferBackRight: 164 case DRI2BufferFakeFrontLeft: 165 case DRI2BufferFakeFrontRight: 166 case DRI2BufferFrontLeft: 167 case DRI2BufferFrontRight: 168 break; 169 170 case DRI2BufferStencil: 171 case DRI2BufferDepth: 172 case DRI2BufferDepthStencil: 173 case DRI2BufferHiz: 174 default: 175 xf86DrvMsg(scrn->scrnIndex, X_WARNING, 176 "Request for DRI2 buffer attachment %d unsupported\n", 177 attachment); 178 free(private); 179 free(buffer); 180 return NULL; 181 } 182 183 pixmap = screen->CreatePixmap(screen, 184 pixmap_width, 185 pixmap_height, 186 pixmap_cpp, 187 0); 188 if (pixmap == NULL) { 189 free(private); 190 free(buffer); 191 return NULL; 192 } 193 } 194 195 buffer->attachment = attachment; 196 buffer->cpp = pixmap->drawable.bitsPerPixel / 8; 197 buffer->format = format; 198 /* The buffer's flags field is unused by the client drivers in 199 * Mesa currently. 200 */ 201 buffer->flags = 0; 202 203 buffer->name = glamor_name_from_pixmap(pixmap, &pitch, &size); 204 buffer->pitch = pitch; 205 if (buffer->name == -1) { 206 xf86DrvMsg(scrn->scrnIndex, X_ERROR, 207 "Failed to get DRI2 name for pixmap\n"); 208 screen->DestroyPixmap(pixmap); 209 free(private); 210 free(buffer); 211 return NULL; 212 } 213 214 buffer->driverPrivate = private; 215 private->refcnt = 1; 216 private->pixmap = pixmap; 217 218 return buffer; 219} 220 221static DRI2Buffer2Ptr 222ms_dri2_create_buffer(DrawablePtr drawable, unsigned int attachment, 223 unsigned int format) 224{ 225 return ms_dri2_create_buffer2(drawable->pScreen, drawable, attachment, 226 format); 227} 228 229static void 230ms_dri2_reference_buffer(DRI2Buffer2Ptr buffer) 231{ 232 if (buffer) { 233 ms_dri2_buffer_private_ptr private = buffer->driverPrivate; 234 private->refcnt++; 235 } 236} 237 238static void ms_dri2_destroy_buffer2(ScreenPtr unused, DrawablePtr unused2, 239 DRI2Buffer2Ptr buffer) 240{ 241 if (!buffer) 242 return; 243 244 if (buffer->driverPrivate) { 245 ms_dri2_buffer_private_ptr private = buffer->driverPrivate; 246 if (--private->refcnt == 0) { 247 ScreenPtr screen = private->pixmap->drawable.pScreen; 248 screen->DestroyPixmap(private->pixmap); 249 free(private); 250 free(buffer); 251 } 252 } else { 253 free(buffer); 254 } 255} 256 257static void ms_dri2_destroy_buffer(DrawablePtr drawable, DRI2Buffer2Ptr buffer) 258{ 259 ms_dri2_destroy_buffer2(NULL, drawable, buffer); 260} 261 262static void 263ms_dri2_copy_region2(ScreenPtr screen, DrawablePtr drawable, RegionPtr pRegion, 264 DRI2BufferPtr destBuffer, DRI2BufferPtr sourceBuffer) 265{ 266 ms_dri2_buffer_private_ptr src_priv = sourceBuffer->driverPrivate; 267 ms_dri2_buffer_private_ptr dst_priv = destBuffer->driverPrivate; 268 PixmapPtr src_pixmap = src_priv->pixmap; 269 PixmapPtr dst_pixmap = dst_priv->pixmap; 270 DrawablePtr src = (sourceBuffer->attachment == DRI2BufferFrontLeft) 271 ? drawable : &src_pixmap->drawable; 272 DrawablePtr dst = (destBuffer->attachment == DRI2BufferFrontLeft) 273 ? drawable : &dst_pixmap->drawable; 274 int off_x = 0, off_y = 0; 275 Bool translate = FALSE; 276 RegionPtr pCopyClip; 277 GCPtr gc; 278 279 if (destBuffer->attachment == DRI2BufferFrontLeft && 280 drawable->pScreen != screen) { 281 dst = DRI2UpdatePrime(drawable, destBuffer); 282 if (!dst) 283 return; 284 if (dst != drawable) 285 translate = TRUE; 286 } 287 288 if (translate && drawable->type == DRAWABLE_WINDOW) { 289#ifdef COMPOSITE 290 PixmapPtr pixmap = get_drawable_pixmap(drawable); 291 off_x = -pixmap->screen_x; 292 off_y = -pixmap->screen_y; 293#endif 294 off_x += drawable->x; 295 off_y += drawable->y; 296 } 297 298 gc = GetScratchGC(dst->depth, screen); 299 if (!gc) 300 return; 301 302 pCopyClip = REGION_CREATE(screen, NULL, 0); 303 REGION_COPY(screen, pCopyClip, pRegion); 304 if (translate) 305 REGION_TRANSLATE(screen, pCopyClip, off_x, off_y); 306 (*gc->funcs->ChangeClip) (gc, CT_REGION, pCopyClip, 0); 307 ValidateGC(dst, gc); 308 309 /* It's important that this copy gets submitted before the direct 310 * rendering client submits rendering for the next frame, but we 311 * don't actually need to submit right now. The client will wait 312 * for the DRI2CopyRegion reply or the swap buffer event before 313 * rendering, and we'll hit the flush callback chain before those 314 * messages are sent. We submit our batch buffers from the flush 315 * callback chain so we know that will happen before the client 316 * tries to render again. 317 */ 318 gc->ops->CopyArea(src, dst, gc, 319 0, 0, 320 drawable->width, drawable->height, 321 off_x, off_y); 322 323 FreeScratchGC(gc); 324} 325 326static void 327ms_dri2_copy_region(DrawablePtr drawable, RegionPtr pRegion, 328 DRI2BufferPtr destBuffer, DRI2BufferPtr sourceBuffer) 329{ 330 ms_dri2_copy_region2(drawable->pScreen, drawable, pRegion, destBuffer, 331 sourceBuffer); 332} 333 334static uint64_t 335gettime_us(void) 336{ 337 struct timespec tv; 338 339 if (clock_gettime(CLOCK_MONOTONIC, &tv)) 340 return 0; 341 342 return (uint64_t)tv.tv_sec * 1000000 + tv.tv_nsec / 1000; 343} 344 345/** 346 * Get current frame count and frame count timestamp, based on drawable's 347 * crtc. 348 */ 349static int 350ms_dri2_get_msc(DrawablePtr draw, CARD64 *ust, CARD64 *msc) 351{ 352 int ret; 353 xf86CrtcPtr crtc = ms_dri2_crtc_covering_drawable(draw); 354 355 /* Drawable not displayed, make up a *monotonic* value */ 356 if (crtc == NULL) { 357 *ust = gettime_us(); 358 *msc = 0; 359 return TRUE; 360 } 361 362 ret = ms_get_crtc_ust_msc(crtc, ust, msc); 363 364 if (ret) 365 return FALSE; 366 367 return TRUE; 368} 369 370static XID 371get_client_id(ClientPtr client) 372{ 373 XID *ptr = dixGetPrivateAddr(&client->devPrivates, &ms_dri2_client_key); 374 if (*ptr == 0) 375 *ptr = FakeClientID(client->index); 376 return *ptr; 377} 378 379/* 380 * Hook this frame event into the server resource 381 * database so we can clean it up if the drawable or 382 * client exits while the swap is pending 383 */ 384static Bool 385ms_dri2_add_frame_event(ms_dri2_frame_event_ptr info) 386{ 387 struct ms_dri2_resource *resource; 388 389 resource = ms_get_resource(get_client_id(info->client), 390 frame_event_client_type); 391 if (resource == NULL) 392 return FALSE; 393 394 xorg_list_add(&info->client_resource, &resource->list); 395 396 resource = ms_get_resource(info->drawable->id, frame_event_drawable_type); 397 if (resource == NULL) { 398 xorg_list_del(&info->client_resource); 399 return FALSE; 400 } 401 402 xorg_list_add(&info->drawable_resource, &resource->list); 403 404 return TRUE; 405} 406 407static void 408ms_dri2_del_frame_event(ms_dri2_frame_event_rec *info) 409{ 410 xorg_list_del(&info->client_resource); 411 xorg_list_del(&info->drawable_resource); 412 413 if (info->front) 414 ms_dri2_destroy_buffer(NULL, info->front); 415 if (info->back) 416 ms_dri2_destroy_buffer(NULL, info->back); 417 418 free(info); 419} 420 421static void 422ms_dri2_blit_swap(DrawablePtr drawable, 423 DRI2BufferPtr dst, 424 DRI2BufferPtr src) 425{ 426 BoxRec box; 427 RegionRec region; 428 429 box.x1 = 0; 430 box.y1 = 0; 431 box.x2 = drawable->width; 432 box.y2 = drawable->height; 433 REGION_INIT(pScreen, ®ion, &box, 0); 434 435 ms_dri2_copy_region(drawable, ®ion, dst, src); 436} 437 438struct ms_dri2_vblank_event { 439 XID drawable_id; 440 ClientPtr client; 441 DRI2SwapEventPtr event_complete; 442 void *event_data; 443}; 444 445static void 446ms_dri2_flip_abort(modesettingPtr ms, void *data) 447{ 448 struct ms_present_vblank_event *event = data; 449 450 ms->drmmode.dri2_flipping = FALSE; 451 free(event); 452} 453 454static void 455ms_dri2_flip_handler(modesettingPtr ms, uint64_t msc, 456 uint64_t ust, void *data) 457{ 458 struct ms_dri2_vblank_event *event = data; 459 uint32_t frame = msc; 460 uint32_t tv_sec = ust / 1000000; 461 uint32_t tv_usec = ust % 1000000; 462 DrawablePtr drawable; 463 int status; 464 465 status = dixLookupDrawable(&drawable, event->drawable_id, serverClient, 466 M_ANY, DixWriteAccess); 467 if (status == Success) 468 DRI2SwapComplete(event->client, drawable, frame, tv_sec, tv_usec, 469 DRI2_FLIP_COMPLETE, event->event_complete, 470 event->event_data); 471 472 ms->drmmode.dri2_flipping = FALSE; 473 free(event); 474} 475 476static Bool 477ms_dri2_schedule_flip(ms_dri2_frame_event_ptr info) 478{ 479 DrawablePtr draw = info->drawable; 480 ScreenPtr screen = draw->pScreen; 481 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 482 modesettingPtr ms = modesettingPTR(scrn); 483 ms_dri2_buffer_private_ptr back_priv = info->back->driverPrivate; 484 struct ms_dri2_vblank_event *event; 485 drmmode_crtc_private_ptr drmmode_crtc = info->crtc->driver_private; 486 487 event = calloc(1, sizeof(struct ms_dri2_vblank_event)); 488 if (!event) 489 return FALSE; 490 491 event->drawable_id = draw->id; 492 event->client = info->client; 493 event->event_complete = info->event_complete; 494 event->event_data = info->event_data; 495 496 if (ms_do_pageflip(screen, back_priv->pixmap, event, 497 drmmode_crtc->vblank_pipe, FALSE, 498 ms_dri2_flip_handler, 499 ms_dri2_flip_abort)) { 500 ms->drmmode.dri2_flipping = TRUE; 501 return TRUE; 502 } 503 return FALSE; 504} 505 506static Bool 507update_front(DrawablePtr draw, DRI2BufferPtr front) 508{ 509 ScreenPtr screen = draw->pScreen; 510 PixmapPtr pixmap = get_drawable_pixmap(draw); 511 ms_dri2_buffer_private_ptr priv = front->driverPrivate; 512 CARD32 size; 513 CARD16 pitch; 514 int name; 515 516 name = glamor_name_from_pixmap(pixmap, &pitch, &size); 517 if (name < 0) 518 return FALSE; 519 520 front->name = name; 521 522 (*screen->DestroyPixmap) (priv->pixmap); 523 front->pitch = pixmap->devKind; 524 front->cpp = pixmap->drawable.bitsPerPixel / 8; 525 priv->pixmap = pixmap; 526 pixmap->refcnt++; 527 528 return TRUE; 529} 530 531static Bool 532can_exchange(ScrnInfoPtr scrn, DrawablePtr draw, 533 DRI2BufferPtr front, DRI2BufferPtr back) 534{ 535 ms_dri2_buffer_private_ptr front_priv = front->driverPrivate; 536 ms_dri2_buffer_private_ptr back_priv = back->driverPrivate; 537 PixmapPtr front_pixmap; 538 PixmapPtr back_pixmap = back_priv->pixmap; 539 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn); 540 int num_crtcs_on = 0; 541 int i; 542 543 for (i = 0; i < config->num_crtc; i++) { 544 drmmode_crtc_private_ptr drmmode_crtc = config->crtc[i]->driver_private; 545 546 /* Don't do pageflipping if CRTCs are rotated. */ 547#ifdef GLAMOR_HAS_GBM 548 if (drmmode_crtc->rotate_bo.gbm) 549 return FALSE; 550#endif 551 552 if (xf86_crtc_on(config->crtc[i])) 553 num_crtcs_on++; 554 } 555 556 /* We can't do pageflipping if all the CRTCs are off. */ 557 if (num_crtcs_on == 0) 558 return FALSE; 559 560 if (!update_front(draw, front)) 561 return FALSE; 562 563 front_pixmap = front_priv->pixmap; 564 565 if (front_pixmap->drawable.width != back_pixmap->drawable.width) 566 return FALSE; 567 568 if (front_pixmap->drawable.height != back_pixmap->drawable.height) 569 return FALSE; 570 571 if (front_pixmap->drawable.bitsPerPixel != 572 back_pixmap->drawable.bitsPerPixel) 573 return FALSE; 574 575 if (front_pixmap->devKind != back_pixmap->devKind) 576 return FALSE; 577 578 return TRUE; 579} 580 581static Bool 582can_flip(ScrnInfoPtr scrn, DrawablePtr draw, 583 DRI2BufferPtr front, DRI2BufferPtr back) 584{ 585 modesettingPtr ms = modesettingPTR(scrn); 586 587 return draw->type == DRAWABLE_WINDOW && 588 ms->drmmode.pageflip && 589 !ms->drmmode.sprites_visible && 590 !ms->drmmode.present_flipping && 591 scrn->vtSema && 592 DRI2CanFlip(draw) && can_exchange(scrn, draw, front, back); 593} 594 595static void 596ms_dri2_exchange_buffers(DrawablePtr draw, DRI2BufferPtr front, 597 DRI2BufferPtr back) 598{ 599 ms_dri2_buffer_private_ptr front_priv = front->driverPrivate; 600 ms_dri2_buffer_private_ptr back_priv = back->driverPrivate; 601 ScreenPtr screen = draw->pScreen; 602 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 603 modesettingPtr ms = modesettingPTR(scrn); 604 msPixmapPrivPtr front_pix = msGetPixmapPriv(&ms->drmmode, front_priv->pixmap); 605 msPixmapPrivPtr back_pix = msGetPixmapPriv(&ms->drmmode, back_priv->pixmap); 606 msPixmapPrivRec tmp_pix; 607 RegionRec region; 608 int tmp; 609 610 /* Swap BO names so DRI works */ 611 tmp = front->name; 612 front->name = back->name; 613 back->name = tmp; 614 615 /* Swap pixmap privates */ 616 tmp_pix = *front_pix; 617 *front_pix = *back_pix; 618 *back_pix = tmp_pix; 619 620 glamor_egl_exchange_buffers(front_priv->pixmap, back_priv->pixmap); 621 622 /* Post damage on the front buffer so that listeners, such 623 * as DisplayLink know take a copy and shove it over the USB. 624 */ 625 region.extents.x1 = region.extents.y1 = 0; 626 region.extents.x2 = front_priv->pixmap->drawable.width; 627 region.extents.y2 = front_priv->pixmap->drawable.height; 628 region.data = NULL; 629 DamageRegionAppend(&front_priv->pixmap->drawable, ®ion); 630 DamageRegionProcessPending(&front_priv->pixmap->drawable); 631} 632 633static void 634ms_dri2_frame_event_handler(uint64_t msc, 635 uint64_t usec, 636 void *data) 637{ 638 ms_dri2_frame_event_ptr frame_info = data; 639 DrawablePtr drawable = frame_info->drawable; 640 ScreenPtr screen = frame_info->screen; 641 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 642 uint32_t tv_sec = usec / 1000000; 643 uint32_t tv_usec = usec % 1000000; 644 645 if (!drawable) { 646 ms_dri2_del_frame_event(frame_info); 647 return; 648 } 649 650 switch (frame_info->type) { 651 case MS_DRI2_QUEUE_FLIP: 652 if (can_flip(scrn, drawable, frame_info->front, frame_info->back) && 653 ms_dri2_schedule_flip(frame_info)) { 654 ms_dri2_exchange_buffers(drawable, frame_info->front, frame_info->back); 655 break; 656 } 657 /* else fall through to blit */ 658 case MS_DRI2_QUEUE_SWAP: 659 ms_dri2_blit_swap(drawable, frame_info->front, frame_info->back); 660 DRI2SwapComplete(frame_info->client, drawable, msc, tv_sec, tv_usec, 661 DRI2_BLIT_COMPLETE, 662 frame_info->client ? frame_info->event_complete : NULL, 663 frame_info->event_data); 664 break; 665 666 case MS_DRI2_WAIT_MSC: 667 if (frame_info->client) 668 DRI2WaitMSCComplete(frame_info->client, drawable, 669 msc, tv_sec, tv_usec); 670 break; 671 672 default: 673 xf86DrvMsg(scrn->scrnIndex, X_WARNING, 674 "%s: unknown vblank event (type %d) received\n", __func__, 675 frame_info->type); 676 break; 677 } 678 679 ms_dri2_del_frame_event(frame_info); 680} 681 682static void 683ms_dri2_frame_event_abort(void *data) 684{ 685 ms_dri2_frame_event_ptr frame_info = data; 686 687 ms_dri2_del_frame_event(frame_info); 688} 689 690/** 691 * Request a DRM event when the requested conditions will be satisfied. 692 * 693 * We need to handle the event and ask the server to wake up the client when 694 * we receive it. 695 */ 696static int 697ms_dri2_schedule_wait_msc(ClientPtr client, DrawablePtr draw, CARD64 target_msc, 698 CARD64 divisor, CARD64 remainder) 699{ 700 ScreenPtr screen = draw->pScreen; 701 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 702 ms_dri2_frame_event_ptr wait_info; 703 int ret; 704 xf86CrtcPtr crtc = ms_dri2_crtc_covering_drawable(draw); 705 CARD64 current_msc, current_ust, request_msc; 706 uint32_t seq; 707 uint64_t queued_msc; 708 709 /* Drawable not visible, return immediately */ 710 if (!crtc) 711 goto out_complete; 712 713 wait_info = calloc(1, sizeof(*wait_info)); 714 if (!wait_info) 715 goto out_complete; 716 717 wait_info->screen = screen; 718 wait_info->drawable = draw; 719 wait_info->client = client; 720 wait_info->type = MS_DRI2_WAIT_MSC; 721 722 if (!ms_dri2_add_frame_event(wait_info)) { 723 free(wait_info); 724 wait_info = NULL; 725 goto out_complete; 726 } 727 728 /* Get current count */ 729 ret = ms_get_crtc_ust_msc(crtc, ¤t_ust, ¤t_msc); 730 731 /* 732 * If divisor is zero, or current_msc is smaller than target_msc, 733 * we just need to make sure target_msc passes before waking up the 734 * client. 735 */ 736 if (divisor == 0 || current_msc < target_msc) { 737 /* If target_msc already reached or passed, set it to 738 * current_msc to ensure we return a reasonable value back 739 * to the caller. This keeps the client from continually 740 * sending us MSC targets from the past by forcibly updating 741 * their count on this call. 742 */ 743 seq = ms_drm_queue_alloc(crtc, wait_info, 744 ms_dri2_frame_event_handler, 745 ms_dri2_frame_event_abort); 746 if (!seq) 747 goto out_free; 748 749 if (current_msc >= target_msc) 750 target_msc = current_msc; 751 752 ret = ms_queue_vblank(crtc, MS_QUEUE_ABSOLUTE, target_msc, &queued_msc, seq); 753 if (!ret) { 754 static int limit = 5; 755 if (limit) { 756 xf86DrvMsg(scrn->scrnIndex, X_WARNING, 757 "%s:%d get vblank counter failed: %s\n", 758 __FUNCTION__, __LINE__, 759 strerror(errno)); 760 limit--; 761 } 762 goto out_free; 763 } 764 765 wait_info->frame = queued_msc; 766 DRI2BlockClient(client, draw); 767 return TRUE; 768 } 769 770 /* 771 * If we get here, target_msc has already passed or we don't have one, 772 * so we queue an event that will satisfy the divisor/remainder equation. 773 */ 774 request_msc = current_msc - (current_msc % divisor) + 775 remainder; 776 /* 777 * If calculated remainder is larger than requested remainder, 778 * it means we've passed the last point where 779 * seq % divisor == remainder, so we need to wait for the next time 780 * that will happen. 781 */ 782 if ((current_msc % divisor) >= remainder) 783 request_msc += divisor; 784 785 seq = ms_drm_queue_alloc(crtc, wait_info, 786 ms_dri2_frame_event_handler, 787 ms_dri2_frame_event_abort); 788 if (!seq) 789 goto out_free; 790 791 if (!ms_queue_vblank(crtc, MS_QUEUE_ABSOLUTE, request_msc, &queued_msc, seq)) { 792 static int limit = 5; 793 if (limit) { 794 xf86DrvMsg(scrn->scrnIndex, X_WARNING, 795 "%s:%d get vblank counter failed: %s\n", 796 __FUNCTION__, __LINE__, 797 strerror(errno)); 798 limit--; 799 } 800 goto out_free; 801 } 802 803 wait_info->frame = queued_msc; 804 805 DRI2BlockClient(client, draw); 806 807 return TRUE; 808 809 out_free: 810 ms_dri2_del_frame_event(wait_info); 811 out_complete: 812 DRI2WaitMSCComplete(client, draw, target_msc, 0, 0); 813 return TRUE; 814} 815 816/** 817 * ScheduleSwap is responsible for requesting a DRM vblank event for 818 * the appropriate frame, or executing the swap immediately if it 819 * doesn't need to wait. 820 * 821 * When the swap is complete, the driver should call into the server so it 822 * can send any swap complete events that have been requested. 823 */ 824static int 825ms_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, 826 DRI2BufferPtr front, DRI2BufferPtr back, 827 CARD64 *target_msc, CARD64 divisor, 828 CARD64 remainder, DRI2SwapEventPtr func, void *data) 829{ 830 ScreenPtr screen = draw->pScreen; 831 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 832 int ret, flip = 0; 833 xf86CrtcPtr crtc = ms_dri2_crtc_covering_drawable(draw); 834 ms_dri2_frame_event_ptr frame_info = NULL; 835 uint64_t current_msc, current_ust; 836 uint64_t request_msc; 837 uint32_t seq; 838 ms_queue_flag ms_flag = MS_QUEUE_ABSOLUTE; 839 uint64_t queued_msc; 840 841 /* Drawable not displayed... just complete the swap */ 842 if (!crtc) 843 goto blit_fallback; 844 845 frame_info = calloc(1, sizeof(*frame_info)); 846 if (!frame_info) 847 goto blit_fallback; 848 849 frame_info->screen = screen; 850 frame_info->drawable = draw; 851 frame_info->client = client; 852 frame_info->event_complete = func; 853 frame_info->event_data = data; 854 frame_info->front = front; 855 frame_info->back = back; 856 frame_info->crtc = crtc; 857 frame_info->type = MS_DRI2_QUEUE_SWAP; 858 859 if (!ms_dri2_add_frame_event(frame_info)) { 860 free(frame_info); 861 frame_info = NULL; 862 goto blit_fallback; 863 } 864 865 ms_dri2_reference_buffer(front); 866 ms_dri2_reference_buffer(back); 867 868 ret = ms_get_crtc_ust_msc(crtc, ¤t_ust, ¤t_msc); 869 if (ret != Success) 870 goto blit_fallback; 871 872 /* Flips need to be submitted one frame before */ 873 if (can_flip(scrn, draw, front, back)) { 874 frame_info->type = MS_DRI2_QUEUE_FLIP; 875 flip = 1; 876 } 877 878 /* Correct target_msc by 'flip' if frame_info->type == MS_DRI2_QUEUE_FLIP. 879 * Do it early, so handling of different timing constraints 880 * for divisor, remainder and msc vs. target_msc works. 881 */ 882 if (*target_msc > 0) 883 *target_msc -= flip; 884 885 /* If non-pageflipping, but blitting/exchanging, we need to use 886 * DRM_VBLANK_NEXTONMISS to avoid unreliable timestamping later 887 * on. 888 */ 889 if (flip == 0) 890 ms_flag |= MS_QUEUE_NEXT_ON_MISS; 891 892 /* 893 * If divisor is zero, or current_msc is smaller than target_msc 894 * we just need to make sure target_msc passes before initiating 895 * the swap. 896 */ 897 if (divisor == 0 || current_msc < *target_msc) { 898 899 /* If target_msc already reached or passed, set it to 900 * current_msc to ensure we return a reasonable value back 901 * to the caller. This makes swap_interval logic more robust. 902 */ 903 if (current_msc >= *target_msc) 904 *target_msc = current_msc; 905 906 seq = ms_drm_queue_alloc(crtc, frame_info, 907 ms_dri2_frame_event_handler, 908 ms_dri2_frame_event_abort); 909 if (!seq) 910 goto blit_fallback; 911 912 if (!ms_queue_vblank(crtc, ms_flag, *target_msc, &queued_msc, seq)) { 913 xf86DrvMsg(scrn->scrnIndex, X_WARNING, 914 "divisor 0 get vblank counter failed: %s\n", 915 strerror(errno)); 916 goto blit_fallback; 917 } 918 919 *target_msc = queued_msc + flip; 920 frame_info->frame = *target_msc; 921 922 return TRUE; 923 } 924 925 /* 926 * If we get here, target_msc has already passed or we don't have one, 927 * and we need to queue an event that will satisfy the divisor/remainder 928 * equation. 929 */ 930 931 request_msc = current_msc - (current_msc % divisor) + 932 remainder; 933 934 /* 935 * If the calculated deadline vbl.request.sequence is smaller than 936 * or equal to current_msc, it means we've passed the last point 937 * when effective onset frame seq could satisfy 938 * seq % divisor == remainder, so we need to wait for the next time 939 * this will happen. 940 941 * This comparison takes the DRM_VBLANK_NEXTONMISS delay into account. 942 */ 943 if (request_msc <= current_msc) 944 request_msc += divisor; 945 946 seq = ms_drm_queue_alloc(crtc, frame_info, 947 ms_dri2_frame_event_handler, 948 ms_dri2_frame_event_abort); 949 if (!seq) 950 goto blit_fallback; 951 952 /* Account for 1 frame extra pageflip delay if flip > 0 */ 953 if (!ms_queue_vblank(crtc, ms_flag, request_msc - flip, &queued_msc, seq)) { 954 xf86DrvMsg(scrn->scrnIndex, X_WARNING, 955 "final get vblank counter failed: %s\n", 956 strerror(errno)); 957 goto blit_fallback; 958 } 959 960 /* Adjust returned value for 1 fame pageflip offset of flip > 0 */ 961 *target_msc = queued_msc + flip; 962 frame_info->frame = *target_msc; 963 964 return TRUE; 965 966 blit_fallback: 967 ms_dri2_blit_swap(draw, front, back); 968 DRI2SwapComplete(client, draw, 0, 0, 0, DRI2_BLIT_COMPLETE, func, data); 969 if (frame_info) 970 ms_dri2_del_frame_event(frame_info); 971 *target_msc = 0; /* offscreen, so zero out target vblank count */ 972 return TRUE; 973} 974 975static int 976ms_dri2_frame_event_client_gone(void *data, XID id) 977{ 978 struct ms_dri2_resource *resource = data; 979 980 while (!xorg_list_is_empty(&resource->list)) { 981 ms_dri2_frame_event_ptr info = 982 xorg_list_first_entry(&resource->list, 983 ms_dri2_frame_event_rec, 984 client_resource); 985 986 xorg_list_del(&info->client_resource); 987 info->client = NULL; 988 } 989 free(resource); 990 991 return Success; 992} 993 994static int 995ms_dri2_frame_event_drawable_gone(void *data, XID id) 996{ 997 struct ms_dri2_resource *resource = data; 998 999 while (!xorg_list_is_empty(&resource->list)) { 1000 ms_dri2_frame_event_ptr info = 1001 xorg_list_first_entry(&resource->list, 1002 ms_dri2_frame_event_rec, 1003 drawable_resource); 1004 1005 xorg_list_del(&info->drawable_resource); 1006 info->drawable = NULL; 1007 } 1008 free(resource); 1009 1010 return Success; 1011} 1012 1013static Bool 1014ms_dri2_register_frame_event_resource_types(void) 1015{ 1016 frame_event_client_type = 1017 CreateNewResourceType(ms_dri2_frame_event_client_gone, 1018 "Frame Event Client"); 1019 if (!frame_event_client_type) 1020 return FALSE; 1021 1022 frame_event_drawable_type = 1023 CreateNewResourceType(ms_dri2_frame_event_drawable_gone, 1024 "Frame Event Drawable"); 1025 if (!frame_event_drawable_type) 1026 return FALSE; 1027 1028 return TRUE; 1029} 1030 1031Bool 1032ms_dri2_screen_init(ScreenPtr screen) 1033{ 1034 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 1035 modesettingPtr ms = modesettingPTR(scrn); 1036 DRI2InfoRec info; 1037 const char *driver_names[2] = { NULL, NULL }; 1038 1039 if (!glamor_supports_pixmap_import_export(screen)) { 1040 xf86DrvMsg(scrn->scrnIndex, X_WARNING, 1041 "DRI2: glamor lacks support for pixmap import/export\n"); 1042 } 1043 1044 if (!xf86LoaderCheckSymbol("DRI2Version")) 1045 return FALSE; 1046 1047 if (!dixRegisterPrivateKey(&ms_dri2_client_key, 1048 PRIVATE_CLIENT, sizeof(XID))) 1049 return FALSE; 1050 1051 if (serverGeneration != ms_dri2_server_generation) { 1052 ms_dri2_server_generation = serverGeneration; 1053 if (!ms_dri2_register_frame_event_resource_types()) { 1054 xf86DrvMsg(scrn->scrnIndex, X_WARNING, 1055 "Cannot register DRI2 frame event resources\n"); 1056 return FALSE; 1057 } 1058 } 1059 1060 memset(&info, '\0', sizeof(info)); 1061 info.fd = ms->fd; 1062 info.driverName = NULL; /* Compat field, unused. */ 1063 info.deviceName = drmGetDeviceNameFromFd(ms->fd); 1064 1065 info.version = 9; 1066 info.CreateBuffer = ms_dri2_create_buffer; 1067 info.DestroyBuffer = ms_dri2_destroy_buffer; 1068 info.CopyRegion = ms_dri2_copy_region; 1069 info.ScheduleSwap = ms_dri2_schedule_swap; 1070 info.GetMSC = ms_dri2_get_msc; 1071 info.ScheduleWaitMSC = ms_dri2_schedule_wait_msc; 1072 info.CreateBuffer2 = ms_dri2_create_buffer2; 1073 info.DestroyBuffer2 = ms_dri2_destroy_buffer2; 1074 info.CopyRegion2 = ms_dri2_copy_region2; 1075 1076 /* Ask Glamor to obtain the DRI driver name via EGL_MESA_query_driver. */ 1077 driver_names[0] = glamor_egl_get_driver_name(screen); 1078 1079 if (driver_names[0]) { 1080 /* There is no VDPAU driver for Intel, fallback to the generic 1081 * OpenGL/VAAPI va_gl backend to emulate VDPAU. Otherwise, 1082 * guess that the DRI and VDPAU drivers have the same name. 1083 */ 1084 if (strcmp(driver_names[0], "i965") == 0 || 1085 strcmp(driver_names[0], "iris") == 0) { 1086 driver_names[1] = "va_gl"; 1087 } else { 1088 driver_names[1] = driver_names[0]; 1089 } 1090 1091 info.numDrivers = 2; 1092 info.driverNames = driver_names; 1093 } else { 1094 /* EGL_MESA_query_driver was unavailable; let dri2.c select the 1095 * driver and fill in these fields for us. 1096 */ 1097 info.numDrivers = 0; 1098 info.driverNames = NULL; 1099 } 1100 1101 return DRI2ScreenInit(screen, &info); 1102} 1103 1104void 1105ms_dri2_close_screen(ScreenPtr screen) 1106{ 1107 DRI2CloseScreen(screen); 1108} 1109 1110#endif /* GLAMOR_HAS_GBM */ 1111