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, &region, &box, 0);
435 
436     ms_dri2_copy_region(drawable, &region, 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, &region);
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, &current_ust, &current_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, &current_ust, &current_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