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