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
47 enum ms_dri2_frame_event_type {
48 MS_DRI2_QUEUE_SWAP,
49 MS_DRI2_QUEUE_FLIP,
50 MS_DRI2_WAIT_MSC,
51 };
52
53 typedef 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
71 typedef struct {
72 int refcnt;
73 PixmapPtr pixmap;
74 } ms_dri2_buffer_private_rec, *ms_dri2_buffer_private_ptr;
75
76 static DevPrivateKeyRec ms_dri2_client_key;
77 static RESTYPE frame_event_client_type, frame_event_drawable_type;
78 static int ms_dri2_server_generation;
79
80 struct ms_dri2_resource {
81 XID id;
82 RESTYPE type;
83 struct xorg_list list;
84 };
85
86 static struct ms_dri2_resource *
ms_get_resource(XID id,RESTYPE type)87 ms_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
110 static inline PixmapPtr
get_drawable_pixmap(DrawablePtr drawable)111 get_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
121 static DRI2Buffer2Ptr
ms_dri2_create_buffer2(ScreenPtr screen,DrawablePtr drawable,unsigned int attachment,unsigned int format)122 ms_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
222 static DRI2Buffer2Ptr
ms_dri2_create_buffer(DrawablePtr drawable,unsigned int attachment,unsigned int format)223 ms_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
230 static void
ms_dri2_reference_buffer(DRI2Buffer2Ptr buffer)231 ms_dri2_reference_buffer(DRI2Buffer2Ptr buffer)
232 {
233 if (buffer) {
234 ms_dri2_buffer_private_ptr private = buffer->driverPrivate;
235 private->refcnt++;
236 }
237 }
238
ms_dri2_destroy_buffer2(ScreenPtr unused,DrawablePtr unused2,DRI2Buffer2Ptr buffer)239 static 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
ms_dri2_destroy_buffer(DrawablePtr drawable,DRI2Buffer2Ptr buffer)258 static void ms_dri2_destroy_buffer(DrawablePtr drawable, DRI2Buffer2Ptr buffer)
259 {
260 ms_dri2_destroy_buffer2(NULL, drawable, buffer);
261 }
262
263 static void
ms_dri2_copy_region2(ScreenPtr screen,DrawablePtr drawable,RegionPtr pRegion,DRI2BufferPtr destBuffer,DRI2BufferPtr sourceBuffer)264 ms_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
327 static void
ms_dri2_copy_region(DrawablePtr drawable,RegionPtr pRegion,DRI2BufferPtr destBuffer,DRI2BufferPtr sourceBuffer)328 ms_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
335 static uint64_t
gettime_us(void)336 gettime_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 */
350 static int
ms_dri2_get_msc(DrawablePtr draw,CARD64 * ust,CARD64 * msc)351 ms_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
371 static XID
get_client_id(ClientPtr client)372 get_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 */
385 static Bool
ms_dri2_add_frame_event(ms_dri2_frame_event_ptr info)386 ms_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
408 static void
ms_dri2_del_frame_event(ms_dri2_frame_event_rec * info)409 ms_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
422 static void
ms_dri2_blit_swap(DrawablePtr drawable,DRI2BufferPtr dst,DRI2BufferPtr src)423 ms_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
439 struct ms_dri2_vblank_event {
440 XID drawable_id;
441 ClientPtr client;
442 DRI2SwapEventPtr event_complete;
443 void *event_data;
444 };
445
446 static void
ms_dri2_flip_abort(modesettingPtr ms,void * data)447 ms_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
455 static void
ms_dri2_flip_handler(modesettingPtr ms,uint64_t msc,uint64_t ust,void * data)456 ms_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
477 static Bool
ms_dri2_schedule_flip(ms_dri2_frame_event_ptr info)478 ms_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
508 static Bool
update_front(DrawablePtr draw,DRI2BufferPtr front)509 update_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
534 static Bool
can_exchange(ScrnInfoPtr scrn,DrawablePtr draw,DRI2BufferPtr front,DRI2BufferPtr back)535 can_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
584 static Bool
can_flip(ScrnInfoPtr scrn,DrawablePtr draw,DRI2BufferPtr front,DRI2BufferPtr back)585 can_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
598 static void
ms_dri2_exchange_buffers(DrawablePtr draw,DRI2BufferPtr front,DRI2BufferPtr back)599 ms_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
636 static void
ms_dri2_frame_event_handler(uint64_t msc,uint64_t usec,void * data)637 ms_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
685 static void
ms_dri2_frame_event_abort(void * data)686 ms_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 */
699 static int
ms_dri2_schedule_wait_msc(ClientPtr client,DrawablePtr draw,CARD64 target_msc,CARD64 divisor,CARD64 remainder)700 ms_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 */
827 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)828 ms_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
978 static int
ms_dri2_frame_event_client_gone(void * data,XID id)979 ms_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
997 static int
ms_dri2_frame_event_drawable_gone(void * data,XID id)998 ms_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
1016 static Bool
ms_dri2_register_frame_event_resource_types(void)1017 ms_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
1034 Bool
ms_dri2_screen_init(ScreenPtr screen)1035 ms_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
1109 void
ms_dri2_close_screen(ScreenPtr screen)1110 ms_dri2_close_screen(ScreenPtr screen)
1111 {
1112 DRI2CloseScreen(screen);
1113 }
1114
1115 #endif /* GLAMOR_HAS_GBM */
1116