dri2.c revision 5a112b11
1/*
2 * Copyright © 2007, 2008 Red Hat, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Soft-
6 * ware"), to deal in the Software without restriction, including without
7 * limitation the rights to use, copy, modify, merge, publish, distribute,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, provided that the above copyright
10 * notice(s) and this permission notice appear in all copies of the Soft-
11 * ware and that both the above copyright notice(s) and this permission
12 * notice appear in supporting documentation.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
16 * ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY
17 * RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN
18 * THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSE-
19 * QUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
20 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
21 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFOR-
22 * MANCE OF THIS SOFTWARE.
23 *
24 * Except as contained in this notice, the name of a copyright holder shall
25 * not be used in advertising or otherwise to promote the sale, use or
26 * other dealings in this Software without prior written authorization of
27 * the copyright holder.
28 *
29 * Authors:
30 *   Kristian Høgsberg (krh@redhat.com)
31 */
32
33#ifdef HAVE_XORG_CONFIG_H
34#include <xorg-config.h>
35#endif
36
37#include <errno.h>
38#ifdef WITH_LIBDRM
39#include <xf86drm.h>
40#endif
41#include "list.h"
42#include "scrnintstr.h"
43#include "windowstr.h"
44#include "dixstruct.h"
45#include "dri2.h"
46#include "dri2int.h"
47#include "damage.h"
48#include "xf86.h"
49
50CARD8 dri2_major;               /* version of DRI2 supported by DDX */
51CARD8 dri2_minor;
52
53uint32_t prime_id_allocate_bitmask;
54
55static DevPrivateKeyRec dri2ScreenPrivateKeyRec;
56
57#define dri2ScreenPrivateKey (&dri2ScreenPrivateKeyRec)
58
59static DevPrivateKeyRec dri2WindowPrivateKeyRec;
60
61#define dri2WindowPrivateKey (&dri2WindowPrivateKeyRec)
62
63static DevPrivateKeyRec dri2PixmapPrivateKeyRec;
64
65#define dri2PixmapPrivateKey (&dri2PixmapPrivateKeyRec)
66
67static DevPrivateKeyRec dri2ClientPrivateKeyRec;
68
69#define dri2ClientPrivateKey (&dri2ClientPrivateKeyRec)
70
71#define dri2ClientPrivate(_pClient) (dixLookupPrivate(&(_pClient)->devPrivates, \
72                                                      dri2ClientPrivateKey))
73
74typedef struct _DRI2Client {
75    int prime_id;
76} DRI2ClientRec, *DRI2ClientPtr;
77
78static RESTYPE dri2DrawableRes;
79
80typedef struct _DRI2Screen *DRI2ScreenPtr;
81
82typedef struct _DRI2Drawable {
83    DRI2ScreenPtr dri2_screen;
84    DrawablePtr drawable;
85    struct xorg_list reference_list;
86    int width;
87    int height;
88    DRI2BufferPtr *buffers;
89    int bufferCount;
90    unsigned int swapsPending;
91    int swap_interval;
92    CARD64 swap_count;
93    int64_t target_sbc;         /* -1 means no SBC wait outstanding */
94    CARD64 last_swap_target;    /* most recently queued swap target */
95    CARD64 last_swap_msc;       /* msc at completion of most recent swap */
96    CARD64 last_swap_ust;       /* ust at completion of most recent swap */
97    int swap_limit;             /* for N-buffering */
98    unsigned blocked[3];
99    Bool needInvalidate;
100    int prime_id;
101    PixmapPtr prime_secondary_pixmap;
102    PixmapPtr redirectpixmap;
103} DRI2DrawableRec, *DRI2DrawablePtr;
104
105typedef struct _DRI2Screen {
106    ScreenPtr screen;
107    int refcnt;
108    unsigned int numDrivers;
109    const char **driverNames;
110    const char *deviceName;
111    int fd;
112    unsigned int lastSequence;
113    int prime_id;
114
115    DRI2CreateBufferProcPtr CreateBuffer;
116    DRI2DestroyBufferProcPtr DestroyBuffer;
117    DRI2CopyRegionProcPtr CopyRegion;
118    DRI2ScheduleSwapProcPtr ScheduleSwap;
119    DRI2GetMSCProcPtr GetMSC;
120    DRI2ScheduleWaitMSCProcPtr ScheduleWaitMSC;
121    DRI2AuthMagic2ProcPtr AuthMagic;
122    DRI2AuthMagicProcPtr LegacyAuthMagic;
123    DRI2ReuseBufferNotifyProcPtr ReuseBufferNotify;
124    DRI2SwapLimitValidateProcPtr SwapLimitValidate;
125    DRI2GetParamProcPtr GetParam;
126
127    HandleExposuresProcPtr HandleExposures;
128
129    ConfigNotifyProcPtr ConfigNotify;
130    SetWindowPixmapProcPtr SetWindowPixmap;
131    DRI2CreateBuffer2ProcPtr CreateBuffer2;
132    DRI2DestroyBuffer2ProcPtr DestroyBuffer2;
133    DRI2CopyRegion2ProcPtr CopyRegion2;
134} DRI2ScreenRec;
135
136static void
137destroy_buffer(DrawablePtr pDraw, DRI2BufferPtr buffer, int prime_id);
138
139enum DRI2WakeType {
140    WAKE_SBC,
141    WAKE_MSC,
142    WAKE_SWAP,
143};
144
145#define Wake(c, t) (void *)((uintptr_t)(c) | (t))
146
147static Bool
148dri2WakeClient(ClientPtr client, void *closure)
149{
150    ClientWakeup(client);
151    return TRUE;
152}
153
154static Bool
155dri2WakeAll(ClientPtr client, DRI2DrawablePtr pPriv, enum DRI2WakeType t)
156{
157    int count;
158
159    if (!pPriv->blocked[t])
160        return FALSE;
161
162    count = ClientSignalAll(client, dri2WakeClient, Wake(pPriv, t));
163    pPriv->blocked[t] -= count;
164    return count;
165}
166
167static Bool
168dri2Sleep(ClientPtr client, DRI2DrawablePtr pPriv, enum DRI2WakeType t)
169{
170    if (ClientSleep(client, dri2WakeClient, Wake(pPriv, t))) {
171        pPriv->blocked[t]++;
172        return TRUE;
173    }
174    return FALSE;
175}
176
177static DRI2ScreenPtr
178DRI2GetScreen(ScreenPtr pScreen)
179{
180    return dixLookupPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey);
181}
182
183static ScreenPtr
184GetScreenPrime(ScreenPtr primary, int prime_id)
185{
186    ScreenPtr secondary;
187    if (prime_id == 0) {
188        return primary;
189    }
190    xorg_list_for_each_entry(secondary, &primary->secondary_list, secondary_head) {
191        DRI2ScreenPtr ds;
192
193        if (!secondary->is_offload_secondary)
194            continue;
195
196        ds = DRI2GetScreen(secondary);
197        if (ds == NULL)
198            continue;
199
200        if (ds->prime_id == prime_id)
201            return secondary;
202    }
203    return primary;
204}
205
206static DRI2ScreenPtr
207DRI2GetScreenPrime(ScreenPtr primary, int prime_id)
208{
209    ScreenPtr secondary = GetScreenPrime(primary, prime_id);
210    return DRI2GetScreen(secondary);
211}
212
213static DRI2DrawablePtr
214DRI2GetDrawable(DrawablePtr pDraw)
215{
216    WindowPtr pWin;
217    PixmapPtr pPixmap;
218
219    switch (pDraw->type) {
220    case DRAWABLE_WINDOW:
221        pWin = (WindowPtr) pDraw;
222        return dixLookupPrivate(&pWin->devPrivates, dri2WindowPrivateKey);
223    case DRAWABLE_PIXMAP:
224        pPixmap = (PixmapPtr) pDraw;
225        return dixLookupPrivate(&pPixmap->devPrivates, dri2PixmapPrivateKey);
226    default:
227        return NULL;
228    }
229}
230
231static DRI2DrawablePtr
232DRI2AllocateDrawable(DrawablePtr pDraw)
233{
234    DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen);
235    DRI2DrawablePtr pPriv;
236    CARD64 ust;
237    WindowPtr pWin;
238    PixmapPtr pPixmap;
239
240    pPriv = malloc(sizeof *pPriv);
241    if (pPriv == NULL)
242        return NULL;
243
244    pPriv->dri2_screen = ds;
245    pPriv->drawable = pDraw;
246    pPriv->width = pDraw->width;
247    pPriv->height = pDraw->height;
248    pPriv->buffers = NULL;
249    pPriv->bufferCount = 0;
250    pPriv->swapsPending = 0;
251    pPriv->swap_count = 0;
252    pPriv->target_sbc = -1;
253    pPriv->swap_interval = 1;
254    /* Initialize last swap target from DDX if possible */
255    if (!ds->GetMSC || !(*ds->GetMSC) (pDraw, &ust, &pPriv->last_swap_target))
256        pPriv->last_swap_target = 0;
257
258    memset(pPriv->blocked, 0, sizeof(pPriv->blocked));
259    pPriv->swap_limit = 1;      /* default to double buffering */
260    pPriv->last_swap_msc = 0;
261    pPriv->last_swap_ust = 0;
262    xorg_list_init(&pPriv->reference_list);
263    pPriv->needInvalidate = FALSE;
264    pPriv->redirectpixmap = NULL;
265    pPriv->prime_secondary_pixmap = NULL;
266    if (pDraw->type == DRAWABLE_WINDOW) {
267        pWin = (WindowPtr) pDraw;
268        dixSetPrivate(&pWin->devPrivates, dri2WindowPrivateKey, pPriv);
269    }
270    else {
271        pPixmap = (PixmapPtr) pDraw;
272        dixSetPrivate(&pPixmap->devPrivates, dri2PixmapPrivateKey, pPriv);
273    }
274
275    return pPriv;
276}
277
278Bool
279DRI2SwapLimit(DrawablePtr pDraw, int swap_limit)
280{
281    DRI2DrawablePtr pPriv = DRI2GetDrawable(pDraw);
282    DRI2ScreenPtr ds;
283
284    if (!pPriv)
285        return FALSE;
286
287    ds = pPriv->dri2_screen;
288
289    if (!ds->SwapLimitValidate || !ds->SwapLimitValidate(pDraw, swap_limit))
290        return FALSE;
291
292    pPriv->swap_limit = swap_limit;
293
294    /* Check throttling */
295    if (pPriv->swapsPending >= pPriv->swap_limit)
296        return TRUE;
297
298    dri2WakeAll(CLIENT_SIGNAL_ANY, pPriv, WAKE_SWAP);
299    return TRUE;
300}
301
302typedef struct DRI2DrawableRefRec {
303    XID id;
304    XID dri2_id;
305    DRI2InvalidateProcPtr invalidate;
306    void *priv;
307    struct xorg_list link;
308} DRI2DrawableRefRec, *DRI2DrawableRefPtr;
309
310static DRI2DrawableRefPtr
311DRI2LookupDrawableRef(DRI2DrawablePtr pPriv, XID id)
312{
313    DRI2DrawableRefPtr ref = NULL;
314
315    xorg_list_for_each_entry(ref, &pPriv->reference_list, link) {
316        if (ref->id == id)
317            return ref;
318    }
319
320    return NULL;
321}
322
323static int
324DRI2AddDrawableRef(DRI2DrawablePtr pPriv, XID id, XID dri2_id,
325                   DRI2InvalidateProcPtr invalidate, void *priv)
326{
327    DRI2DrawableRefPtr ref;
328
329    ref = malloc(sizeof *ref);
330    if (ref == NULL)
331        return BadAlloc;
332
333    if (!AddResource(dri2_id, dri2DrawableRes, pPriv)) {
334        free(ref);
335        return BadAlloc;
336    }
337    if (!DRI2LookupDrawableRef(pPriv, id))
338        if (!AddResource(id, dri2DrawableRes, pPriv)) {
339            FreeResourceByType(dri2_id, dri2DrawableRes, TRUE);
340            free(ref);
341            return BadAlloc;
342        }
343
344    ref->id = id;
345    ref->dri2_id = dri2_id;
346    ref->invalidate = invalidate;
347    ref->priv = priv;
348    xorg_list_add(&ref->link, &pPriv->reference_list);
349
350    return Success;
351}
352
353int
354DRI2CreateDrawable2(ClientPtr client, DrawablePtr pDraw, XID id,
355                    DRI2InvalidateProcPtr invalidate, void *priv,
356                    XID *dri2_id_out)
357{
358    DRI2DrawablePtr pPriv;
359    DRI2ClientPtr dri2_client = dri2ClientPrivate(client);
360    XID dri2_id;
361    int rc;
362
363    pPriv = DRI2GetDrawable(pDraw);
364    if (pPriv == NULL)
365        pPriv = DRI2AllocateDrawable(pDraw);
366    if (pPriv == NULL)
367        return BadAlloc;
368
369    pPriv->prime_id = dri2_client->prime_id;
370
371    dri2_id = FakeClientID(client->index);
372    rc = DRI2AddDrawableRef(pPriv, id, dri2_id, invalidate, priv);
373    if (rc != Success)
374        return rc;
375
376    if (dri2_id_out)
377        *dri2_id_out = dri2_id;
378
379    return Success;
380}
381
382int
383DRI2CreateDrawable(ClientPtr client, DrawablePtr pDraw, XID id,
384                   DRI2InvalidateProcPtr invalidate, void *priv)
385{
386    return DRI2CreateDrawable2(client, pDraw, id, invalidate, priv, NULL);
387}
388
389static int
390DRI2DrawableGone(void *p, XID id)
391{
392    DRI2DrawablePtr pPriv = p;
393    DRI2DrawableRefPtr ref, next;
394    WindowPtr pWin;
395    PixmapPtr pPixmap;
396    DrawablePtr pDraw;
397    int i;
398
399    xorg_list_for_each_entry_safe(ref, next, &pPriv->reference_list, link) {
400        if (ref->dri2_id == id) {
401            xorg_list_del(&ref->link);
402            /* If this was the last ref under this X drawable XID,
403             * unregister the X drawable resource. */
404            if (!DRI2LookupDrawableRef(pPriv, ref->id))
405                FreeResourceByType(ref->id, dri2DrawableRes, TRUE);
406            free(ref);
407            break;
408        }
409
410        if (ref->id == id) {
411            xorg_list_del(&ref->link);
412            FreeResourceByType(ref->dri2_id, dri2DrawableRes, TRUE);
413            free(ref);
414        }
415    }
416
417    if (!xorg_list_is_empty(&pPriv->reference_list))
418        return Success;
419
420    pDraw = pPriv->drawable;
421    if (pDraw->type == DRAWABLE_WINDOW) {
422        pWin = (WindowPtr) pDraw;
423        dixSetPrivate(&pWin->devPrivates, dri2WindowPrivateKey, NULL);
424    }
425    else {
426        pPixmap = (PixmapPtr) pDraw;
427        dixSetPrivate(&pPixmap->devPrivates, dri2PixmapPrivateKey, NULL);
428    }
429
430    if (pPriv->prime_secondary_pixmap) {
431        (*pPriv->prime_secondary_pixmap->primary_pixmap->drawable.pScreen->DestroyPixmap)(pPriv->prime_secondary_pixmap->primary_pixmap);
432        (*pPriv->prime_secondary_pixmap->drawable.pScreen->DestroyPixmap)(pPriv->prime_secondary_pixmap);
433    }
434
435    if (pPriv->buffers != NULL) {
436        for (i = 0; i < pPriv->bufferCount; i++)
437            destroy_buffer(pDraw, pPriv->buffers[i], pPriv->prime_id);
438
439        free(pPriv->buffers);
440    }
441
442    if (pPriv->redirectpixmap) {
443        (*pDraw->pScreen->ReplaceScanoutPixmap)(pDraw, pPriv->redirectpixmap, FALSE);
444        (*pDraw->pScreen->DestroyPixmap)(pPriv->redirectpixmap);
445    }
446
447    dri2WakeAll(CLIENT_SIGNAL_ANY, pPriv, WAKE_SWAP);
448    dri2WakeAll(CLIENT_SIGNAL_ANY, pPriv, WAKE_MSC);
449    dri2WakeAll(CLIENT_SIGNAL_ANY, pPriv, WAKE_SBC);
450
451    free(pPriv);
452
453    return Success;
454}
455
456static DRI2BufferPtr
457create_buffer(DRI2ScreenPtr ds, DrawablePtr pDraw,
458              unsigned int attachment, unsigned int format)
459{
460    DRI2BufferPtr buffer;
461    if (ds->CreateBuffer2)
462        buffer = (*ds->CreateBuffer2)(GetScreenPrime(pDraw->pScreen,
463                                                     DRI2GetDrawable(pDraw)->prime_id),
464                                      pDraw, attachment, format);
465    else
466        buffer = (*ds->CreateBuffer)(pDraw, attachment, format);
467    return buffer;
468}
469
470static void
471destroy_buffer(DrawablePtr pDraw, DRI2BufferPtr buffer, int prime_id)
472{
473    ScreenPtr primeScreen;
474    DRI2ScreenPtr ds;
475    primeScreen = GetScreenPrime(pDraw->pScreen, prime_id);
476    ds = DRI2GetScreen(primeScreen);
477    if (ds->DestroyBuffer2)
478        (*ds->DestroyBuffer2)(primeScreen, pDraw, buffer);
479    else
480        (*ds->DestroyBuffer)(pDraw, buffer);
481}
482
483static int
484find_attachment(DRI2DrawablePtr pPriv, unsigned attachment)
485{
486    int i;
487
488    if (pPriv->buffers == NULL) {
489        return -1;
490    }
491
492    for (i = 0; i < pPriv->bufferCount; i++) {
493        if ((pPriv->buffers[i] != NULL)
494            && (pPriv->buffers[i]->attachment == attachment)) {
495            return i;
496        }
497    }
498
499    return -1;
500}
501
502static Bool
503allocate_or_reuse_buffer(DrawablePtr pDraw, DRI2ScreenPtr ds,
504                         DRI2DrawablePtr pPriv,
505                         unsigned int attachment, unsigned int format,
506                         int dimensions_match, DRI2BufferPtr * buffer)
507{
508    int old_buf = find_attachment(pPriv, attachment);
509
510    if ((old_buf < 0)
511        || attachment == DRI2BufferFrontLeft
512        || !dimensions_match || (pPriv->buffers[old_buf]->format != format)) {
513        *buffer = create_buffer(ds, pDraw, attachment, format);
514        return TRUE;
515
516    }
517    else {
518        *buffer = pPriv->buffers[old_buf];
519
520        if (ds->ReuseBufferNotify)
521            (*ds->ReuseBufferNotify) (pDraw, *buffer);
522
523        pPriv->buffers[old_buf] = NULL;
524        return FALSE;
525    }
526}
527
528static void
529update_dri2_drawable_buffers(DRI2DrawablePtr pPriv, DrawablePtr pDraw,
530                             DRI2BufferPtr * buffers, int out_count, int *width,
531                             int *height)
532{
533    int i;
534
535    if (pPriv->buffers != NULL) {
536        for (i = 0; i < pPriv->bufferCount; i++) {
537            if (pPriv->buffers[i] != NULL) {
538                destroy_buffer(pDraw, pPriv->buffers[i], pPriv->prime_id);
539            }
540        }
541
542        free(pPriv->buffers);
543    }
544
545    pPriv->buffers = buffers;
546    pPriv->bufferCount = out_count;
547    pPriv->width = pDraw->width;
548    pPriv->height = pDraw->height;
549    *width = pPriv->width;
550    *height = pPriv->height;
551}
552
553static DRI2BufferPtr *
554do_get_buffers(DrawablePtr pDraw, int *width, int *height,
555               unsigned int *attachments, int count, int *out_count,
556               int has_format)
557{
558    DRI2DrawablePtr pPriv = DRI2GetDrawable(pDraw);
559    DRI2ScreenPtr ds;
560    DRI2BufferPtr *buffers;
561    int need_real_front = 0;
562    int need_fake_front = 0;
563    int have_fake_front = 0;
564    int front_format = 0;
565    int dimensions_match;
566    int buffers_changed = 0;
567    int i;
568
569    if (!pPriv) {
570        *width = pDraw->width;
571        *height = pDraw->height;
572        *out_count = 0;
573        return NULL;
574    }
575
576    ds = DRI2GetScreenPrime(pDraw->pScreen, pPriv->prime_id);
577
578    dimensions_match = (pDraw->width == pPriv->width)
579        && (pDraw->height == pPriv->height);
580
581    buffers = calloc((count + 1), sizeof(buffers[0]));
582    if (!buffers)
583        goto err_out;
584
585    for (i = 0; i < count; i++) {
586        const unsigned attachment = *(attachments++);
587        const unsigned format = (has_format) ? *(attachments++) : 0;
588
589        if (allocate_or_reuse_buffer(pDraw, ds, pPriv, attachment,
590                                     format, dimensions_match, &buffers[i]))
591            buffers_changed = 1;
592
593        if (buffers[i] == NULL)
594            goto err_out;
595
596        /* If the drawable is a window and the front-buffer is requested,
597         * silently add the fake front-buffer to the list of requested
598         * attachments.  The counting logic in the loop accounts for the case
599         * where the client requests both the fake and real front-buffer.
600         */
601        if (attachment == DRI2BufferBackLeft) {
602            need_real_front++;
603            front_format = format;
604        }
605
606        if (attachment == DRI2BufferFrontLeft) {
607            need_real_front--;
608            front_format = format;
609
610            if (pDraw->type == DRAWABLE_WINDOW) {
611                need_fake_front++;
612            }
613        }
614
615        if (pDraw->type == DRAWABLE_WINDOW) {
616            if (attachment == DRI2BufferFakeFrontLeft) {
617                need_fake_front--;
618                have_fake_front = 1;
619            }
620        }
621    }
622
623    if (need_real_front > 0) {
624        if (allocate_or_reuse_buffer(pDraw, ds, pPriv, DRI2BufferFrontLeft,
625                                     front_format, dimensions_match,
626                                     &buffers[i]))
627            buffers_changed = 1;
628
629        if (buffers[i] == NULL)
630            goto err_out;
631        i++;
632    }
633
634    if (need_fake_front > 0) {
635        if (allocate_or_reuse_buffer(pDraw, ds, pPriv, DRI2BufferFakeFrontLeft,
636                                     front_format, dimensions_match,
637                                     &buffers[i]))
638            buffers_changed = 1;
639
640        if (buffers[i] == NULL)
641            goto err_out;
642
643        i++;
644        have_fake_front = 1;
645    }
646
647    *out_count = i;
648
649    update_dri2_drawable_buffers(pPriv, pDraw, buffers, *out_count, width,
650                                 height);
651
652    /* If the client is getting a fake front-buffer, pre-fill it with the
653     * contents of the real front-buffer.  This ensures correct operation of
654     * applications that call glXWaitX before calling glDrawBuffer.
655     */
656    if (have_fake_front && buffers_changed) {
657        BoxRec box;
658        RegionRec region;
659
660        box.x1 = 0;
661        box.y1 = 0;
662        box.x2 = pPriv->width;
663        box.y2 = pPriv->height;
664        RegionInit(&region, &box, 0);
665
666        DRI2CopyRegion(pDraw, &region, DRI2BufferFakeFrontLeft,
667                       DRI2BufferFrontLeft);
668    }
669
670    pPriv->needInvalidate = TRUE;
671
672    return pPriv->buffers;
673
674 err_out:
675
676    *out_count = 0;
677
678    if (buffers) {
679        for (i = 0; i < count; i++) {
680            if (buffers[i] != NULL)
681                destroy_buffer(pDraw, buffers[i], 0);
682        }
683
684        free(buffers);
685        buffers = NULL;
686    }
687
688    update_dri2_drawable_buffers(pPriv, pDraw, buffers, *out_count, width,
689                                 height);
690
691    return buffers;
692}
693
694DRI2BufferPtr *
695DRI2GetBuffers(DrawablePtr pDraw, int *width, int *height,
696               unsigned int *attachments, int count, int *out_count)
697{
698    return do_get_buffers(pDraw, width, height, attachments, count,
699                          out_count, FALSE);
700}
701
702DRI2BufferPtr *
703DRI2GetBuffersWithFormat(DrawablePtr pDraw, int *width, int *height,
704                         unsigned int *attachments, int count, int *out_count)
705{
706    return do_get_buffers(pDraw, width, height, attachments, count,
707                          out_count, TRUE);
708}
709
710static void
711DRI2InvalidateDrawable(DrawablePtr pDraw)
712{
713    DRI2DrawablePtr pPriv = DRI2GetDrawable(pDraw);
714    DRI2DrawableRefPtr ref = NULL;
715
716    if (!pPriv || !pPriv->needInvalidate)
717        return;
718
719    pPriv->needInvalidate = FALSE;
720
721    xorg_list_for_each_entry(ref, &pPriv->reference_list, link)
722        ref->invalidate(pDraw, ref->priv, ref->id);
723}
724
725/*
726 * In the direct rendered case, we throttle the clients that have more
727 * than their share of outstanding swaps (and thus busy buffers) when a
728 * new GetBuffers request is received.  In the AIGLX case, we allow the
729 * client to get the new buffers, but throttle when the next GLX request
730 * comes in (see __glXDRIcontextWait()).
731 */
732Bool
733DRI2ThrottleClient(ClientPtr client, DrawablePtr pDraw)
734{
735    DRI2DrawablePtr pPriv;
736
737    pPriv = DRI2GetDrawable(pDraw);
738    if (pPriv == NULL)
739        return FALSE;
740
741    /* Throttle to swap limit */
742    if (pPriv->swapsPending >= pPriv->swap_limit) {
743        if (dri2Sleep(client, pPriv, WAKE_SWAP)) {
744            ResetCurrentRequest(client);
745            client->sequence--;
746            return TRUE;
747        }
748    }
749
750    return FALSE;
751}
752
753void
754DRI2BlockClient(ClientPtr client, DrawablePtr pDraw)
755{
756    DRI2DrawablePtr pPriv;
757
758    pPriv = DRI2GetDrawable(pDraw);
759    if (pPriv == NULL)
760        return;
761
762    dri2Sleep(client, pPriv, WAKE_MSC);
763}
764
765static inline PixmapPtr GetDrawablePixmap(DrawablePtr drawable)
766{
767    if (drawable->type == DRAWABLE_PIXMAP)
768        return (PixmapPtr)drawable;
769    else {
770        struct _Window *pWin = (struct _Window *)drawable;
771        return drawable->pScreen->GetWindowPixmap(pWin);
772    }
773}
774
775/*
776 * A TraverseTree callback to invalidate all windows using the same
777 * pixmap
778 */
779static int
780DRI2InvalidateWalk(WindowPtr pWin, void *data)
781{
782    if (pWin->drawable.pScreen->GetWindowPixmap(pWin) != data)
783        return WT_DONTWALKCHILDREN;
784    DRI2InvalidateDrawable(&pWin->drawable);
785    return WT_WALKCHILDREN;
786}
787
788static void
789DRI2InvalidateDrawableAll(DrawablePtr pDraw)
790{
791    if (pDraw->type == DRAWABLE_WINDOW) {
792        WindowPtr pWin = (WindowPtr) pDraw;
793        PixmapPtr pPixmap = pDraw->pScreen->GetWindowPixmap(pWin);
794
795        /*
796         * Find the top-most window using this pixmap
797         */
798        while (pWin->parent &&
799               pDraw->pScreen->GetWindowPixmap(pWin->parent) == pPixmap)
800            pWin = pWin->parent;
801
802        /*
803         * Walk the sub-tree to invalidate all of the
804         * windows using the same pixmap
805         */
806        TraverseTree(pWin, DRI2InvalidateWalk, pPixmap);
807        DRI2InvalidateDrawable(&pPixmap->drawable);
808    }
809    else
810        DRI2InvalidateDrawable(pDraw);
811}
812
813DrawablePtr DRI2UpdatePrime(DrawablePtr pDraw, DRI2BufferPtr pDest)
814{
815    DRI2DrawablePtr pPriv = DRI2GetDrawable(pDraw);
816    PixmapPtr spix;
817    PixmapPtr mpix = GetDrawablePixmap(pDraw);
818    ScreenPtr primary, secondary;
819    Bool ret;
820
821    primary = mpix->drawable.pScreen;
822
823    if (pDraw->type == DRAWABLE_WINDOW) {
824        WindowPtr pWin = (WindowPtr)pDraw;
825        PixmapPtr pPixmap = pDraw->pScreen->GetWindowPixmap(pWin);
826
827        if (pDraw->pScreen->GetScreenPixmap(pDraw->pScreen) == pPixmap) {
828            if (pPriv->redirectpixmap &&
829                pPriv->redirectpixmap->drawable.width == pDraw->width &&
830                pPriv->redirectpixmap->drawable.height == pDraw->height &&
831                pPriv->redirectpixmap->drawable.depth == pDraw->depth) {
832                mpix = pPriv->redirectpixmap;
833            } else {
834                if (primary->ReplaceScanoutPixmap) {
835                    mpix = (*primary->CreatePixmap)(primary, pDraw->width, pDraw->height,
836                                                   pDraw->depth, CREATE_PIXMAP_USAGE_SHARED);
837                    if (!mpix)
838                        return NULL;
839
840                    ret = (*primary->ReplaceScanoutPixmap)(pDraw, mpix, TRUE);
841                    if (ret == FALSE) {
842                        (*primary->DestroyPixmap)(mpix);
843                        return NULL;
844                    }
845                    pPriv->redirectpixmap = mpix;
846                } else
847                    return NULL;
848            }
849        } else if (pPriv->redirectpixmap) {
850            (*primary->ReplaceScanoutPixmap)(pDraw, pPriv->redirectpixmap, FALSE);
851            (*primary->DestroyPixmap)(pPriv->redirectpixmap);
852            pPriv->redirectpixmap = NULL;
853        }
854    }
855
856    secondary = GetScreenPrime(pDraw->pScreen, pPriv->prime_id);
857
858    /* check if the pixmap is still fine */
859    if (pPriv->prime_secondary_pixmap) {
860        if (pPriv->prime_secondary_pixmap->primary_pixmap == mpix)
861            return &pPriv->prime_secondary_pixmap->drawable;
862        else {
863            PixmapUnshareSecondaryPixmap(pPriv->prime_secondary_pixmap);
864            (*pPriv->prime_secondary_pixmap->primary_pixmap->drawable.pScreen->DestroyPixmap)(pPriv->prime_secondary_pixmap->primary_pixmap);
865            (*secondary->DestroyPixmap)(pPriv->prime_secondary_pixmap);
866            pPriv->prime_secondary_pixmap = NULL;
867        }
868    }
869
870    spix = PixmapShareToSecondary(mpix, secondary);
871    if (!spix)
872        return NULL;
873
874    pPriv->prime_secondary_pixmap = spix;
875#ifdef COMPOSITE
876    spix->screen_x = mpix->screen_x;
877    spix->screen_y = mpix->screen_y;
878#endif
879
880    DRI2InvalidateDrawableAll(pDraw);
881    return &spix->drawable;
882}
883
884static void dri2_copy_region(DrawablePtr pDraw, RegionPtr pRegion,
885                             DRI2BufferPtr pDest, DRI2BufferPtr pSrc)
886{
887    DRI2DrawablePtr pPriv = DRI2GetDrawable(pDraw);
888    DRI2ScreenPtr ds;
889    ScreenPtr primeScreen;
890
891    primeScreen = GetScreenPrime(pDraw->pScreen, pPriv->prime_id);
892    ds = DRI2GetScreen(primeScreen);
893
894    if (ds->CopyRegion2)
895        (*ds->CopyRegion2)(primeScreen, pDraw, pRegion, pDest, pSrc);
896    else
897        (*ds->CopyRegion) (pDraw, pRegion, pDest, pSrc);
898
899    /* cause damage to the box */
900    if (pPriv->prime_id) {
901       BoxRec box;
902       RegionRec region;
903       box.x1 = 0;
904       box.x2 = box.x1 + pDraw->width;
905       box.y1 = 0;
906       box.y2 = box.y1 + pDraw->height;
907       RegionInit(&region, &box, 1);
908       RegionTranslate(&region, pDraw->x, pDraw->y);
909       DamageRegionAppend(pDraw, &region);
910       DamageRegionProcessPending(pDraw);
911       RegionUninit(&region);
912    }
913}
914
915int
916DRI2CopyRegion(DrawablePtr pDraw, RegionPtr pRegion,
917               unsigned int dest, unsigned int src)
918{
919    DRI2DrawablePtr pPriv;
920    DRI2BufferPtr pDestBuffer, pSrcBuffer;
921    int i;
922
923    pPriv = DRI2GetDrawable(pDraw);
924    if (pPriv == NULL)
925        return BadDrawable;
926
927    pDestBuffer = NULL;
928    pSrcBuffer = NULL;
929    for (i = 0; i < pPriv->bufferCount; i++) {
930        if (pPriv->buffers[i]->attachment == dest)
931            pDestBuffer = (DRI2BufferPtr) pPriv->buffers[i];
932        if (pPriv->buffers[i]->attachment == src)
933            pSrcBuffer = (DRI2BufferPtr) pPriv->buffers[i];
934    }
935    if (pSrcBuffer == NULL || pDestBuffer == NULL)
936        return BadValue;
937
938    dri2_copy_region(pDraw, pRegion, pDestBuffer, pSrcBuffer);
939
940    return Success;
941}
942
943/* Can this drawable be page flipped? */
944Bool
945DRI2CanFlip(DrawablePtr pDraw)
946{
947    ScreenPtr pScreen = pDraw->pScreen;
948    WindowPtr pWin, pRoot;
949    PixmapPtr pWinPixmap, pRootPixmap;
950
951    if (pDraw->type == DRAWABLE_PIXMAP)
952        return TRUE;
953
954    pRoot = pScreen->root;
955    pRootPixmap = pScreen->GetWindowPixmap(pRoot);
956
957    pWin = (WindowPtr) pDraw;
958    pWinPixmap = pScreen->GetWindowPixmap(pWin);
959    if (pRootPixmap != pWinPixmap)
960        return FALSE;
961    if (!RegionEqual(&pWin->clipList, &pRoot->winSize))
962        return FALSE;
963
964    /* Does the window match the pixmap exactly? */
965    if (pDraw->x != 0 || pDraw->y != 0 ||
966#ifdef COMPOSITE
967        pDraw->x != pWinPixmap->screen_x || pDraw->y != pWinPixmap->screen_y ||
968#endif
969        pDraw->width != pWinPixmap->drawable.width ||
970        pDraw->height != pWinPixmap->drawable.height)
971        return FALSE;
972
973    return TRUE;
974}
975
976/* Can we do a pixmap exchange instead of a blit? */
977Bool
978DRI2CanExchange(DrawablePtr pDraw)
979{
980    return FALSE;
981}
982
983void
984DRI2WaitMSCComplete(ClientPtr client, DrawablePtr pDraw, int frame,
985                    unsigned int tv_sec, unsigned int tv_usec)
986{
987    DRI2DrawablePtr pPriv;
988
989    pPriv = DRI2GetDrawable(pDraw);
990    if (pPriv == NULL)
991        return;
992
993    ProcDRI2WaitMSCReply(client, ((CARD64) tv_sec * 1000000) + tv_usec,
994                         frame, pPriv->swap_count);
995
996    dri2WakeAll(client, pPriv, WAKE_MSC);
997}
998
999static void
1000DRI2WakeClient(ClientPtr client, DrawablePtr pDraw, int frame,
1001               unsigned int tv_sec, unsigned int tv_usec)
1002{
1003    ScreenPtr pScreen = pDraw->pScreen;
1004    DRI2DrawablePtr pPriv;
1005
1006    pPriv = DRI2GetDrawable(pDraw);
1007    if (pPriv == NULL) {
1008        xf86DrvMsg(pScreen->myNum, X_ERROR,
1009                   "[DRI2] %s: bad drawable\n", __func__);
1010        return;
1011    }
1012
1013    /*
1014     * Swap completed.
1015     * Wake the client iff:
1016     *   - it was waiting on SBC
1017     *   - was blocked due to GLX make current
1018     *   - was blocked due to swap throttling
1019     *   - is not blocked due to an MSC wait
1020     */
1021    if (pPriv->target_sbc != -1 && pPriv->target_sbc <= pPriv->swap_count) {
1022        if (dri2WakeAll(client, pPriv, WAKE_SBC)) {
1023            ProcDRI2WaitMSCReply(client, ((CARD64) tv_sec * 1000000) + tv_usec,
1024                                 frame, pPriv->swap_count);
1025            pPriv->target_sbc = -1;
1026        }
1027    }
1028
1029    dri2WakeAll(CLIENT_SIGNAL_ANY, pPriv, WAKE_SWAP);
1030}
1031
1032void
1033DRI2SwapComplete(ClientPtr client, DrawablePtr pDraw, int frame,
1034                 unsigned int tv_sec, unsigned int tv_usec, int type,
1035                 DRI2SwapEventPtr swap_complete, void *swap_data)
1036{
1037    ScreenPtr pScreen = pDraw->pScreen;
1038    DRI2DrawablePtr pPriv;
1039    CARD64 ust = 0;
1040    BoxRec box;
1041    RegionRec region;
1042
1043    pPriv = DRI2GetDrawable(pDraw);
1044    if (pPriv == NULL) {
1045        xf86DrvMsg(pScreen->myNum, X_ERROR,
1046                   "[DRI2] %s: bad drawable\n", __func__);
1047        return;
1048    }
1049
1050    pPriv->swapsPending--;
1051    pPriv->swap_count++;
1052
1053    box.x1 = 0;
1054    box.y1 = 0;
1055    box.x2 = pDraw->width;
1056    box.y2 = pDraw->height;
1057    RegionInit(&region, &box, 0);
1058    DRI2CopyRegion(pDraw, &region, DRI2BufferFakeFrontLeft,
1059                   DRI2BufferFrontLeft);
1060
1061    ust = ((CARD64) tv_sec * 1000000) + tv_usec;
1062    if (swap_complete)
1063        swap_complete(client, swap_data, type, ust, frame, pPriv->swap_count);
1064
1065    pPriv->last_swap_msc = frame;
1066    pPriv->last_swap_ust = ust;
1067
1068    DRI2WakeClient(client, pDraw, frame, tv_sec, tv_usec);
1069}
1070
1071Bool
1072DRI2WaitSwap(ClientPtr client, DrawablePtr pDrawable)
1073{
1074    DRI2DrawablePtr pPriv = DRI2GetDrawable(pDrawable);
1075
1076    /* If we're currently waiting for a swap on this drawable, reset
1077     * the request and suspend the client. */
1078    if (pPriv && pPriv->swapsPending) {
1079        if (dri2Sleep(client, pPriv, WAKE_SWAP)) {
1080            ResetCurrentRequest(client);
1081            client->sequence--;
1082            return TRUE;
1083        }
1084    }
1085
1086    return FALSE;
1087}
1088
1089
1090
1091int
1092DRI2SwapBuffers(ClientPtr client, DrawablePtr pDraw, CARD64 target_msc,
1093                CARD64 divisor, CARD64 remainder, CARD64 * swap_target,
1094                DRI2SwapEventPtr func, void *data)
1095{
1096    ScreenPtr pScreen = pDraw->pScreen;
1097    DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen);
1098    DRI2DrawablePtr pPriv;
1099    DRI2BufferPtr pDestBuffer = NULL, pSrcBuffer = NULL;
1100    int ret, i;
1101    CARD64 ust, current_msc;
1102
1103    pPriv = DRI2GetDrawable(pDraw);
1104    if (pPriv == NULL) {
1105        xf86DrvMsg(pScreen->myNum, X_ERROR,
1106                   "[DRI2] %s: bad drawable\n", __func__);
1107        return BadDrawable;
1108    }
1109
1110    /* According to spec, return expected swapbuffers count SBC after this swap
1111     * will complete. This is ignored unless we return Success, but it must be
1112     * initialized on every path where we return Success or the caller will send
1113     * an uninitialized value off the stack to the client. So let's initialize
1114     * it as early as possible, just to be sure.
1115     */
1116    *swap_target = pPriv->swap_count + pPriv->swapsPending + 1;
1117
1118    for (i = 0; i < pPriv->bufferCount; i++) {
1119        if (pPriv->buffers[i]->attachment == DRI2BufferFrontLeft)
1120            pDestBuffer = (DRI2BufferPtr) pPriv->buffers[i];
1121        if (pPriv->buffers[i]->attachment == DRI2BufferBackLeft)
1122            pSrcBuffer = (DRI2BufferPtr) pPriv->buffers[i];
1123    }
1124    if (pSrcBuffer == NULL || pDestBuffer == NULL) {
1125        xf86DrvMsg(pScreen->myNum, X_ERROR,
1126                   "[DRI2] %s: drawable has no back or front?\n", __func__);
1127        return BadDrawable;
1128    }
1129
1130    /* Old DDX or no swap interval, just blit */
1131    if (!ds->ScheduleSwap || !pPriv->swap_interval || pPriv->prime_id) {
1132        BoxRec box;
1133        RegionRec region;
1134
1135        box.x1 = 0;
1136        box.y1 = 0;
1137        box.x2 = pDraw->width;
1138        box.y2 = pDraw->height;
1139        RegionInit(&region, &box, 0);
1140
1141        pPriv->swapsPending++;
1142
1143        dri2_copy_region(pDraw, &region, pDestBuffer, pSrcBuffer);
1144        DRI2SwapComplete(client, pDraw, target_msc, 0, 0, DRI2_BLIT_COMPLETE,
1145                         func, data);
1146        return Success;
1147    }
1148
1149    /*
1150     * In the simple glXSwapBuffers case, all params will be 0, and we just
1151     * need to schedule a swap for the last swap target + the swap interval.
1152     */
1153    if (target_msc == 0 && divisor == 0 && remainder == 0) {
1154        /* If the current vblank count of the drawable's crtc is lower
1155         * than the count stored in last_swap_target from a previous swap
1156         * then reinitialize last_swap_target to the current crtc's msc,
1157         * otherwise the swap will hang. This will happen if the drawable
1158         * is moved to a crtc with a lower refresh rate, or a crtc that just
1159         * got enabled.
1160         */
1161        if (ds->GetMSC) {
1162            if (!(*ds->GetMSC) (pDraw, &ust, &current_msc))
1163                pPriv->last_swap_target = 0;
1164
1165            if (current_msc < pPriv->last_swap_target)
1166                pPriv->last_swap_target = current_msc;
1167
1168        }
1169
1170        /*
1171         * Swap target for this swap is last swap target + swap interval since
1172         * we have to account for the current swap count, interval, and the
1173         * number of pending swaps.
1174         */
1175        target_msc = pPriv->last_swap_target + pPriv->swap_interval;
1176
1177    }
1178
1179    pPriv->swapsPending++;
1180    ret = (*ds->ScheduleSwap) (client, pDraw, pDestBuffer, pSrcBuffer,
1181                               &target_msc, divisor, remainder, func, data);
1182    if (!ret) {
1183        pPriv->swapsPending--;  /* didn't schedule */
1184        xf86DrvMsg(pScreen->myNum, X_ERROR,
1185                   "[DRI2] %s: driver failed to schedule swap\n", __func__);
1186        return BadDrawable;
1187    }
1188
1189    pPriv->last_swap_target = target_msc;
1190
1191    DRI2InvalidateDrawableAll(pDraw);
1192
1193    return Success;
1194}
1195
1196void
1197DRI2SwapInterval(DrawablePtr pDrawable, int interval)
1198{
1199    ScreenPtr pScreen = pDrawable->pScreen;
1200    DRI2DrawablePtr pPriv = DRI2GetDrawable(pDrawable);
1201
1202    if (pPriv == NULL) {
1203        xf86DrvMsg(pScreen->myNum, X_ERROR,
1204                   "[DRI2] %s: bad drawable\n", __func__);
1205        return;
1206    }
1207
1208    /* fixme: check against arbitrary max? */
1209    pPriv->swap_interval = interval;
1210}
1211
1212int
1213DRI2GetMSC(DrawablePtr pDraw, CARD64 * ust, CARD64 * msc, CARD64 * sbc)
1214{
1215    ScreenPtr pScreen = pDraw->pScreen;
1216    DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen);
1217    DRI2DrawablePtr pPriv;
1218    Bool ret;
1219
1220    pPriv = DRI2GetDrawable(pDraw);
1221    if (pPriv == NULL) {
1222        xf86DrvMsg(pScreen->myNum, X_ERROR,
1223                   "[DRI2] %s: bad drawable\n", __func__);
1224        return BadDrawable;
1225    }
1226
1227    if (!ds->GetMSC) {
1228        *ust = 0;
1229        *msc = 0;
1230        *sbc = pPriv->swap_count;
1231        return Success;
1232    }
1233
1234    /*
1235     * Spec needs to be updated to include unmapped or redirected
1236     * drawables
1237     */
1238
1239    ret = (*ds->GetMSC) (pDraw, ust, msc);
1240    if (!ret)
1241        return BadDrawable;
1242
1243    *sbc = pPriv->swap_count;
1244
1245    return Success;
1246}
1247
1248int
1249DRI2WaitMSC(ClientPtr client, DrawablePtr pDraw, CARD64 target_msc,
1250            CARD64 divisor, CARD64 remainder)
1251{
1252    DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen);
1253    DRI2DrawablePtr pPriv;
1254    Bool ret;
1255
1256    pPriv = DRI2GetDrawable(pDraw);
1257    if (pPriv == NULL)
1258        return BadDrawable;
1259
1260    /* Old DDX just completes immediately */
1261    if (!ds->ScheduleWaitMSC) {
1262        DRI2WaitMSCComplete(client, pDraw, target_msc, 0, 0);
1263
1264        return Success;
1265    }
1266
1267    ret =
1268        (*ds->ScheduleWaitMSC) (client, pDraw, target_msc, divisor, remainder);
1269    if (!ret)
1270        return BadDrawable;
1271
1272    return Success;
1273}
1274
1275int
1276DRI2WaitSBC(ClientPtr client, DrawablePtr pDraw, CARD64 target_sbc)
1277{
1278    DRI2DrawablePtr pPriv;
1279
1280    pPriv = DRI2GetDrawable(pDraw);
1281    if (pPriv == NULL)
1282        return BadDrawable;
1283
1284    if (pPriv->target_sbc != -1) /* already in use */
1285        return BadDrawable;
1286
1287    /* target_sbc == 0 means to block until all pending swaps are
1288     * finished. Recalculate target_sbc to get that behaviour.
1289     */
1290    if (target_sbc == 0)
1291        target_sbc = pPriv->swap_count + pPriv->swapsPending;
1292
1293    /* If current swap count already >= target_sbc, reply and
1294     * return immediately with (ust, msc, sbc) triplet of
1295     * most recent completed swap.
1296     */
1297    if (pPriv->swap_count >= target_sbc) {
1298        ProcDRI2WaitMSCReply(client, pPriv->last_swap_ust,
1299                             pPriv->last_swap_msc, pPriv->swap_count);
1300        return Success;
1301    }
1302
1303    if (!dri2Sleep(client, pPriv, WAKE_SBC))
1304        return BadAlloc;
1305
1306    pPriv->target_sbc = target_sbc;
1307    return Success;
1308}
1309
1310Bool
1311DRI2HasSwapControl(ScreenPtr pScreen)
1312{
1313    DRI2ScreenPtr ds = DRI2GetScreen(pScreen);
1314
1315    return ds->ScheduleSwap && ds->GetMSC;
1316}
1317
1318Bool
1319DRI2Connect(ClientPtr client, ScreenPtr pScreen,
1320            unsigned int driverType, int *fd,
1321            const char **driverName, const char **deviceName)
1322{
1323    DRI2ScreenPtr ds;
1324    uint32_t prime_id = DRI2DriverPrimeId(driverType);
1325    uint32_t driver_id = driverType & 0xffff;
1326
1327    if (!dixPrivateKeyRegistered(dri2ScreenPrivateKey))
1328        return FALSE;
1329
1330    ds = DRI2GetScreenPrime(pScreen, prime_id);
1331    if (ds == NULL)
1332        return FALSE;
1333
1334    if (driver_id >= ds->numDrivers ||
1335        !ds->driverNames[driver_id])
1336        return FALSE;
1337
1338    *driverName = ds->driverNames[driver_id];
1339    *deviceName = ds->deviceName;
1340    *fd = ds->fd;
1341
1342    if (client) {
1343        DRI2ClientPtr dri2_client;
1344        dri2_client = dri2ClientPrivate(client);
1345        dri2_client->prime_id = prime_id;
1346    }
1347
1348    return TRUE;
1349}
1350
1351static int
1352DRI2AuthMagic (ScreenPtr pScreen, uint32_t magic)
1353{
1354    DRI2ScreenPtr ds = DRI2GetScreen(pScreen);
1355    if (ds == NULL)
1356        return -EINVAL;
1357
1358    return (*ds->LegacyAuthMagic) (ds->fd, magic);
1359}
1360
1361Bool
1362DRI2Authenticate(ClientPtr client, ScreenPtr pScreen, uint32_t magic)
1363{
1364    DRI2ScreenPtr ds;
1365    DRI2ClientPtr dri2_client = dri2ClientPrivate(client);
1366    ScreenPtr primescreen;
1367
1368    ds = DRI2GetScreenPrime(pScreen, dri2_client->prime_id);
1369    if (ds == NULL)
1370        return FALSE;
1371
1372    primescreen = GetScreenPrime(pScreen, dri2_client->prime_id);
1373    if ((*ds->AuthMagic)(primescreen, magic))
1374        return FALSE;
1375    return TRUE;
1376}
1377
1378static int
1379DRI2ConfigNotify(WindowPtr pWin, int x, int y, int w, int h, int bw,
1380                 WindowPtr pSib)
1381{
1382    DrawablePtr pDraw = (DrawablePtr) pWin;
1383    ScreenPtr pScreen = pDraw->pScreen;
1384    DRI2ScreenPtr ds = DRI2GetScreen(pScreen);
1385    DRI2DrawablePtr dd = DRI2GetDrawable(pDraw);
1386    int ret;
1387
1388    if (ds->ConfigNotify) {
1389        pScreen->ConfigNotify = ds->ConfigNotify;
1390
1391        ret = (*pScreen->ConfigNotify) (pWin, x, y, w, h, bw, pSib);
1392
1393        ds->ConfigNotify = pScreen->ConfigNotify;
1394        pScreen->ConfigNotify = DRI2ConfigNotify;
1395        if (ret)
1396            return ret;
1397    }
1398
1399    if (!dd || (dd->width == w && dd->height == h))
1400        return Success;
1401
1402    DRI2InvalidateDrawable(pDraw);
1403    return Success;
1404}
1405
1406static void
1407DRI2SetWindowPixmap(WindowPtr pWin, PixmapPtr pPix)
1408{
1409    ScreenPtr pScreen = pWin->drawable.pScreen;
1410    DRI2ScreenPtr ds = DRI2GetScreen(pScreen);
1411
1412    pScreen->SetWindowPixmap = ds->SetWindowPixmap;
1413    (*pScreen->SetWindowPixmap) (pWin, pPix);
1414    ds->SetWindowPixmap = pScreen->SetWindowPixmap;
1415    pScreen->SetWindowPixmap = DRI2SetWindowPixmap;
1416
1417    DRI2InvalidateDrawable(&pWin->drawable);
1418}
1419
1420#define MAX_PRIME DRI2DriverPrimeMask
1421static int
1422get_prime_id(void)
1423{
1424    int i;
1425    /* start at 1, prime id 0 is just normal driver */
1426    for (i = 1; i < MAX_PRIME; i++) {
1427         if (prime_id_allocate_bitmask & (1 << i))
1428             continue;
1429
1430         prime_id_allocate_bitmask |= (1 << i);
1431         return i;
1432    }
1433    return -1;
1434}
1435
1436#include "pci_ids/pci_id_driver_map.h"
1437
1438static char *
1439dri2_probe_driver_name(ScreenPtr pScreen, DRI2InfoPtr info)
1440{
1441#ifdef WITH_LIBDRM
1442    int i, j;
1443    char *driver = NULL;
1444    drmDevicePtr dev;
1445
1446    /* For non-PCI devices and drmGetDevice fail, just assume that
1447     * the 3D driver is named the same as the kernel driver. This is
1448     * currently true for vc4 and msm (freedreno).
1449     */
1450    if (drmGetDevice(info->fd, &dev) || dev->bustype != DRM_BUS_PCI) {
1451        drmVersionPtr version = drmGetVersion(info->fd);
1452
1453        if (!version) {
1454            xf86DrvMsg(pScreen->myNum, X_ERROR,
1455                       "[DRI2] Couldn't drmGetVersion() on non-PCI device, "
1456                       "no driver name found.\n");
1457            return NULL;
1458        }
1459
1460        driver = strndup(version->name, version->name_len);
1461        drmFreeVersion(version);
1462        return driver;
1463    }
1464
1465    for (i = 0; driver_map[i].driver; i++) {
1466        if (dev->deviceinfo.pci->vendor_id != driver_map[i].vendor_id)
1467            continue;
1468
1469        if (driver_map[i].num_chips_ids == -1) {
1470             driver = strdup(driver_map[i].driver);
1471             goto out;
1472        }
1473
1474        for (j = 0; j < driver_map[i].num_chips_ids; j++) {
1475            if (driver_map[i].chip_ids[j] == dev->deviceinfo.pci->device_id) {
1476                driver = strdup(driver_map[i].driver);
1477                goto out;
1478            }
1479        }
1480    }
1481
1482    xf86DrvMsg(pScreen->myNum, X_ERROR,
1483               "[DRI2] No driver mapping found for PCI device "
1484               "0x%04x / 0x%04x\n",
1485               dev->deviceinfo.pci->vendor_id, dev->deviceinfo.pci->device_id);
1486out:
1487    drmFreeDevice(&dev);
1488    return driver;
1489#else
1490    return NULL;
1491#endif
1492}
1493
1494Bool
1495DRI2ScreenInit(ScreenPtr pScreen, DRI2InfoPtr info)
1496{
1497    DRI2ScreenPtr ds;
1498
1499    const char *driverTypeNames[] = {
1500        "DRI",                  /* DRI2DriverDRI */
1501        "VDPAU",                /* DRI2DriverVDPAU */
1502    };
1503    unsigned int i;
1504    CARD8 cur_minor;
1505
1506    if (info->version < 3)
1507        return FALSE;
1508
1509    if (!dixRegisterPrivateKey(&dri2ScreenPrivateKeyRec, PRIVATE_SCREEN, 0))
1510        return FALSE;
1511
1512    if (!dixRegisterPrivateKey(&dri2WindowPrivateKeyRec, PRIVATE_WINDOW, 0))
1513        return FALSE;
1514
1515    if (!dixRegisterPrivateKey(&dri2PixmapPrivateKeyRec, PRIVATE_PIXMAP, 0))
1516        return FALSE;
1517
1518    if (!dixRegisterPrivateKey(&dri2ClientPrivateKeyRec, PRIVATE_CLIENT, sizeof(DRI2ClientRec)))
1519        return FALSE;
1520
1521    ds = calloc(1, sizeof *ds);
1522    if (!ds)
1523        return FALSE;
1524
1525    ds->screen = pScreen;
1526    ds->fd = info->fd;
1527    ds->deviceName = info->deviceName;
1528    dri2_major = 1;
1529
1530    ds->CreateBuffer = info->CreateBuffer;
1531    ds->DestroyBuffer = info->DestroyBuffer;
1532    ds->CopyRegion = info->CopyRegion;
1533    cur_minor = 1;
1534
1535    if (info->version >= 4) {
1536        ds->ScheduleSwap = info->ScheduleSwap;
1537        ds->ScheduleWaitMSC = info->ScheduleWaitMSC;
1538        ds->GetMSC = info->GetMSC;
1539        cur_minor = 3;
1540    }
1541
1542    if (info->version >= 5) {
1543        ds->LegacyAuthMagic = info->AuthMagic;
1544    }
1545
1546    if (info->version >= 6) {
1547        ds->ReuseBufferNotify = info->ReuseBufferNotify;
1548        ds->SwapLimitValidate = info->SwapLimitValidate;
1549    }
1550
1551    if (info->version >= 7) {
1552        ds->GetParam = info->GetParam;
1553        cur_minor = 4;
1554    }
1555
1556    if (info->version >= 8) {
1557        ds->AuthMagic = info->AuthMagic2;
1558    }
1559
1560    if (info->version >= 9) {
1561        ds->CreateBuffer2 = info->CreateBuffer2;
1562        if (info->CreateBuffer2 && pScreen->isGPU) {
1563            ds->prime_id = get_prime_id();
1564            if (ds->prime_id == -1) {
1565                free(ds);
1566                return FALSE;
1567            }
1568        }
1569        ds->DestroyBuffer2 = info->DestroyBuffer2;
1570        ds->CopyRegion2 = info->CopyRegion2;
1571    }
1572
1573    /*
1574     * if the driver doesn't provide an AuthMagic function or the info struct
1575     * version is too low, call through LegacyAuthMagic
1576     */
1577    if (!ds->AuthMagic) {
1578        ds->AuthMagic = DRI2AuthMagic;
1579        /*
1580         * If the driver doesn't provide an AuthMagic function
1581         * it relies on the old method (using libdrm) or fails
1582         */
1583        if (!ds->LegacyAuthMagic)
1584#ifdef WITH_LIBDRM
1585            ds->LegacyAuthMagic = drmAuthMagic;
1586#else
1587            goto err_out;
1588#endif
1589    }
1590
1591    /* Initialize minor if needed and set to minimum provied by DDX */
1592    if (!dri2_minor || dri2_minor > cur_minor)
1593        dri2_minor = cur_minor;
1594
1595    if (info->version == 3 || info->numDrivers == 0) {
1596        /* Driver too old: use the old-style driverName field */
1597        ds->numDrivers = info->driverName ? 1 : 2;
1598        ds->driverNames = xallocarray(ds->numDrivers, sizeof(*ds->driverNames));
1599        if (!ds->driverNames)
1600            goto err_out;
1601
1602        if (info->driverName) {
1603            ds->driverNames[0] = info->driverName;
1604        } else {
1605            /* FIXME dri2_probe_driver_name() returns a strdup-ed string,
1606             * currently this gets leaked */
1607            ds->driverNames[0] = ds->driverNames[1] = dri2_probe_driver_name(pScreen, info);
1608            if (!ds->driverNames[0])
1609                return FALSE;
1610
1611            /* There is no VDPAU driver for i965, fallback to the generic
1612             * OpenGL/VAAPI va_gl backend to emulate VDPAU on i965. */
1613            if (strcmp(ds->driverNames[0], "i965") == 0)
1614                ds->driverNames[1] = "va_gl";
1615        }
1616    }
1617    else {
1618        ds->numDrivers = info->numDrivers;
1619        ds->driverNames = xallocarray(info->numDrivers, sizeof(*ds->driverNames));
1620        if (!ds->driverNames)
1621            goto err_out;
1622        memcpy(ds->driverNames, info->driverNames,
1623               info->numDrivers * sizeof(*ds->driverNames));
1624    }
1625
1626    dixSetPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey, ds);
1627
1628    ds->ConfigNotify = pScreen->ConfigNotify;
1629    pScreen->ConfigNotify = DRI2ConfigNotify;
1630
1631    ds->SetWindowPixmap = pScreen->SetWindowPixmap;
1632    pScreen->SetWindowPixmap = DRI2SetWindowPixmap;
1633
1634    xf86DrvMsg(pScreen->myNum, X_INFO, "[DRI2] Setup complete\n");
1635    for (i = 0; i < ARRAY_SIZE(driverTypeNames); i++) {
1636        if (i < ds->numDrivers && ds->driverNames[i]) {
1637            xf86DrvMsg(pScreen->myNum, X_INFO, "[DRI2]   %s driver: %s\n",
1638                       driverTypeNames[i], ds->driverNames[i]);
1639        }
1640    }
1641
1642    return TRUE;
1643
1644 err_out:
1645    xf86DrvMsg(pScreen->myNum, X_WARNING,
1646               "[DRI2] Initialization failed for info version %d.\n",
1647               info->version);
1648    free(ds);
1649    return FALSE;
1650}
1651
1652void
1653DRI2CloseScreen(ScreenPtr pScreen)
1654{
1655    DRI2ScreenPtr ds = DRI2GetScreen(pScreen);
1656
1657    pScreen->ConfigNotify = ds->ConfigNotify;
1658    pScreen->SetWindowPixmap = ds->SetWindowPixmap;
1659
1660    if (ds->prime_id)
1661        prime_id_allocate_bitmask &= ~(1 << ds->prime_id);
1662    free(ds->driverNames);
1663    free(ds);
1664    dixSetPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey, NULL);
1665}
1666
1667/* Called by InitExtensions() */
1668Bool
1669DRI2ModuleSetup(void)
1670{
1671    dri2DrawableRes = CreateNewResourceType(DRI2DrawableGone, "DRI2Drawable");
1672    if (!dri2DrawableRes)
1673        return FALSE;
1674
1675    return TRUE;
1676}
1677
1678void
1679DRI2Version(int *major, int *minor)
1680{
1681    if (major != NULL)
1682        *major = 1;
1683
1684    if (minor != NULL)
1685        *minor = 2;
1686}
1687
1688int
1689DRI2GetParam(ClientPtr client,
1690             DrawablePtr drawable,
1691             CARD64 param,
1692             BOOL *is_param_recognized,
1693             CARD64 *value)
1694{
1695    DRI2ScreenPtr ds = DRI2GetScreen(drawable->pScreen);
1696    char high_byte = (param >> 24);
1697
1698    switch (high_byte) {
1699    case 0:
1700        /* Parameter names whose high_byte is 0 are reserved for the X
1701         * server. The server currently recognizes no parameters.
1702         */
1703        goto not_recognized;
1704    case 1:
1705        /* Parameter names whose high byte is 1 are reserved for the DDX. */
1706        if (ds->GetParam)
1707            return ds->GetParam(client, drawable, param,
1708                                is_param_recognized, value);
1709        else
1710            goto not_recognized;
1711    default:
1712        /* Other parameter names are reserved for future use. They are never
1713         * recognized.
1714         */
1715        goto not_recognized;
1716    }
1717
1718not_recognized:
1719    *is_param_recognized = FALSE;
1720    return Success;
1721}
1722