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