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