dri2.c revision d566a54b
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;
360    XID dri2_id;
361    int rc;
362
363    if (!dixPrivateKeyRegistered(dri2ScreenPrivateKey))
364        return BadValue;
365
366    dri2_client = dri2ClientPrivate(client);
367
368    pPriv = DRI2GetDrawable(pDraw);
369    if (pPriv == NULL)
370        pPriv = DRI2AllocateDrawable(pDraw);
371    if (pPriv == NULL)
372        return BadAlloc;
373
374    pPriv->prime_id = dri2_client->prime_id;
375
376    dri2_id = FakeClientID(client->index);
377    rc = DRI2AddDrawableRef(pPriv, id, dri2_id, invalidate, priv);
378    if (rc != Success)
379        return rc;
380
381    if (dri2_id_out)
382        *dri2_id_out = dri2_id;
383
384    return Success;
385}
386
387int
388DRI2CreateDrawable(ClientPtr client, DrawablePtr pDraw, XID id,
389                   DRI2InvalidateProcPtr invalidate, void *priv)
390{
391    return DRI2CreateDrawable2(client, pDraw, id, invalidate, priv, NULL);
392}
393
394static int
395DRI2DrawableGone(void *p, XID id)
396{
397    DRI2DrawablePtr pPriv = p;
398    DRI2DrawableRefPtr ref, next;
399    WindowPtr pWin;
400    PixmapPtr pPixmap;
401    DrawablePtr pDraw;
402    int i;
403
404    xorg_list_for_each_entry_safe(ref, next, &pPriv->reference_list, link) {
405        if (ref->dri2_id == id) {
406            xorg_list_del(&ref->link);
407            /* If this was the last ref under this X drawable XID,
408             * unregister the X drawable resource. */
409            if (!DRI2LookupDrawableRef(pPriv, ref->id))
410                FreeResourceByType(ref->id, dri2DrawableRes, TRUE);
411            free(ref);
412            break;
413        }
414
415        if (ref->id == id) {
416            xorg_list_del(&ref->link);
417            FreeResourceByType(ref->dri2_id, dri2DrawableRes, TRUE);
418            free(ref);
419        }
420    }
421
422    if (!xorg_list_is_empty(&pPriv->reference_list))
423        return Success;
424
425    pDraw = pPriv->drawable;
426    if (pDraw->type == DRAWABLE_WINDOW) {
427        pWin = (WindowPtr) pDraw;
428        dixSetPrivate(&pWin->devPrivates, dri2WindowPrivateKey, NULL);
429    }
430    else {
431        pPixmap = (PixmapPtr) pDraw;
432        dixSetPrivate(&pPixmap->devPrivates, dri2PixmapPrivateKey, NULL);
433    }
434
435    if (pPriv->prime_secondary_pixmap) {
436        (*pPriv->prime_secondary_pixmap->primary_pixmap->drawable.pScreen->DestroyPixmap)(pPriv->prime_secondary_pixmap->primary_pixmap);
437        (*pPriv->prime_secondary_pixmap->drawable.pScreen->DestroyPixmap)(pPriv->prime_secondary_pixmap);
438    }
439
440    if (pPriv->buffers != NULL) {
441        for (i = 0; i < pPriv->bufferCount; i++)
442            destroy_buffer(pDraw, pPriv->buffers[i], pPriv->prime_id);
443
444        free(pPriv->buffers);
445    }
446
447    if (pPriv->redirectpixmap) {
448        (*pDraw->pScreen->ReplaceScanoutPixmap)(pDraw, pPriv->redirectpixmap, FALSE);
449        (*pDraw->pScreen->DestroyPixmap)(pPriv->redirectpixmap);
450    }
451
452    dri2WakeAll(CLIENT_SIGNAL_ANY, pPriv, WAKE_SWAP);
453    dri2WakeAll(CLIENT_SIGNAL_ANY, pPriv, WAKE_MSC);
454    dri2WakeAll(CLIENT_SIGNAL_ANY, pPriv, WAKE_SBC);
455
456    free(pPriv);
457
458    return Success;
459}
460
461static DRI2BufferPtr
462create_buffer(DRI2ScreenPtr ds, DrawablePtr pDraw,
463              unsigned int attachment, unsigned int format)
464{
465    DRI2BufferPtr buffer;
466    if (ds->CreateBuffer2)
467        buffer = (*ds->CreateBuffer2)(GetScreenPrime(pDraw->pScreen,
468                                                     DRI2GetDrawable(pDraw)->prime_id),
469                                      pDraw, attachment, format);
470    else
471        buffer = (*ds->CreateBuffer)(pDraw, attachment, format);
472    return buffer;
473}
474
475static void
476destroy_buffer(DrawablePtr pDraw, DRI2BufferPtr buffer, int prime_id)
477{
478    ScreenPtr primeScreen;
479    DRI2ScreenPtr ds;
480    primeScreen = GetScreenPrime(pDraw->pScreen, prime_id);
481    ds = DRI2GetScreen(primeScreen);
482    if (ds->DestroyBuffer2)
483        (*ds->DestroyBuffer2)(primeScreen, pDraw, buffer);
484    else
485        (*ds->DestroyBuffer)(pDraw, buffer);
486}
487
488static int
489find_attachment(DRI2DrawablePtr pPriv, unsigned attachment)
490{
491    int i;
492
493    if (pPriv->buffers == NULL) {
494        return -1;
495    }
496
497    for (i = 0; i < pPriv->bufferCount; i++) {
498        if ((pPriv->buffers[i] != NULL)
499            && (pPriv->buffers[i]->attachment == attachment)) {
500            return i;
501        }
502    }
503
504    return -1;
505}
506
507static Bool
508allocate_or_reuse_buffer(DrawablePtr pDraw, DRI2ScreenPtr ds,
509                         DRI2DrawablePtr pPriv,
510                         unsigned int attachment, unsigned int format,
511                         int dimensions_match, DRI2BufferPtr * buffer)
512{
513    int old_buf = find_attachment(pPriv, attachment);
514
515    if ((old_buf < 0)
516        || attachment == DRI2BufferFrontLeft
517        || !dimensions_match || (pPriv->buffers[old_buf]->format != format)) {
518        *buffer = create_buffer(ds, pDraw, attachment, format);
519        return TRUE;
520
521    }
522    else {
523        *buffer = pPriv->buffers[old_buf];
524
525        if (ds->ReuseBufferNotify)
526            (*ds->ReuseBufferNotify) (pDraw, *buffer);
527
528        pPriv->buffers[old_buf] = NULL;
529        return FALSE;
530    }
531}
532
533static void
534update_dri2_drawable_buffers(DRI2DrawablePtr pPriv, DrawablePtr pDraw,
535                             DRI2BufferPtr * buffers, int out_count, int *width,
536                             int *height)
537{
538    int i;
539
540    if (pPriv->buffers != NULL) {
541        for (i = 0; i < pPriv->bufferCount; i++) {
542            if (pPriv->buffers[i] != NULL) {
543                destroy_buffer(pDraw, pPriv->buffers[i], pPriv->prime_id);
544            }
545        }
546
547        free(pPriv->buffers);
548    }
549
550    pPriv->buffers = buffers;
551    pPriv->bufferCount = out_count;
552    pPriv->width = pDraw->width;
553    pPriv->height = pDraw->height;
554    *width = pPriv->width;
555    *height = pPriv->height;
556}
557
558static DRI2BufferPtr *
559do_get_buffers(DrawablePtr pDraw, int *width, int *height,
560               unsigned int *attachments, int count, int *out_count,
561               int has_format)
562{
563    DRI2DrawablePtr pPriv = DRI2GetDrawable(pDraw);
564    DRI2ScreenPtr ds;
565    DRI2BufferPtr *buffers;
566    int need_real_front = 0;
567    int need_fake_front = 0;
568    int have_fake_front = 0;
569    int front_format = 0;
570    int dimensions_match;
571    int buffers_changed = 0;
572    int i;
573
574    if (!pPriv) {
575        *width = pDraw->width;
576        *height = pDraw->height;
577        *out_count = 0;
578        return NULL;
579    }
580
581    ds = DRI2GetScreenPrime(pDraw->pScreen, pPriv->prime_id);
582
583    dimensions_match = (pDraw->width == pPriv->width)
584        && (pDraw->height == pPriv->height);
585
586    buffers = calloc((count + 1), sizeof(buffers[0]));
587    if (!buffers)
588        goto err_out;
589
590    for (i = 0; i < count; i++) {
591        const unsigned attachment = *(attachments++);
592        const unsigned format = (has_format) ? *(attachments++) : 0;
593
594        if (allocate_or_reuse_buffer(pDraw, ds, pPriv, attachment,
595                                     format, dimensions_match, &buffers[i]))
596            buffers_changed = 1;
597
598        if (buffers[i] == NULL)
599            goto err_out;
600
601        /* If the drawable is a window and the front-buffer is requested,
602         * silently add the fake front-buffer to the list of requested
603         * attachments.  The counting logic in the loop accounts for the case
604         * where the client requests both the fake and real front-buffer.
605         */
606        if (attachment == DRI2BufferBackLeft) {
607            need_real_front++;
608            front_format = format;
609        }
610
611        if (attachment == DRI2BufferFrontLeft) {
612            need_real_front--;
613            front_format = format;
614
615            if (pDraw->type == DRAWABLE_WINDOW) {
616                need_fake_front++;
617            }
618        }
619
620        if (pDraw->type == DRAWABLE_WINDOW) {
621            if (attachment == DRI2BufferFakeFrontLeft) {
622                need_fake_front--;
623                have_fake_front = 1;
624            }
625        }
626    }
627
628    if (need_real_front > 0) {
629        if (allocate_or_reuse_buffer(pDraw, ds, pPriv, DRI2BufferFrontLeft,
630                                     front_format, dimensions_match,
631                                     &buffers[i]))
632            buffers_changed = 1;
633
634        if (buffers[i] == NULL)
635            goto err_out;
636        i++;
637    }
638
639    if (need_fake_front > 0) {
640        if (allocate_or_reuse_buffer(pDraw, ds, pPriv, DRI2BufferFakeFrontLeft,
641                                     front_format, dimensions_match,
642                                     &buffers[i]))
643            buffers_changed = 1;
644
645        if (buffers[i] == NULL)
646            goto err_out;
647
648        i++;
649        have_fake_front = 1;
650    }
651
652    *out_count = i;
653
654    update_dri2_drawable_buffers(pPriv, pDraw, buffers, *out_count, width,
655                                 height);
656
657    /* If the client is getting a fake front-buffer, pre-fill it with the
658     * contents of the real front-buffer.  This ensures correct operation of
659     * applications that call glXWaitX before calling glDrawBuffer.
660     */
661    if (have_fake_front && buffers_changed) {
662        BoxRec box;
663        RegionRec region;
664
665        box.x1 = 0;
666        box.y1 = 0;
667        box.x2 = pPriv->width;
668        box.y2 = pPriv->height;
669        RegionInit(&region, &box, 0);
670
671        DRI2CopyRegion(pDraw, &region, DRI2BufferFakeFrontLeft,
672                       DRI2BufferFrontLeft);
673    }
674
675    pPriv->needInvalidate = TRUE;
676
677    return pPriv->buffers;
678
679 err_out:
680
681    *out_count = 0;
682
683    if (buffers) {
684        for (i = 0; i < count; i++) {
685            if (buffers[i] != NULL)
686                destroy_buffer(pDraw, buffers[i], 0);
687        }
688
689        free(buffers);
690        buffers = NULL;
691    }
692
693    update_dri2_drawable_buffers(pPriv, pDraw, buffers, *out_count, width,
694                                 height);
695
696    return buffers;
697}
698
699DRI2BufferPtr *
700DRI2GetBuffers(DrawablePtr pDraw, int *width, int *height,
701               unsigned int *attachments, int count, int *out_count)
702{
703    return do_get_buffers(pDraw, width, height, attachments, count,
704                          out_count, FALSE);
705}
706
707DRI2BufferPtr *
708DRI2GetBuffersWithFormat(DrawablePtr pDraw, int *width, int *height,
709                         unsigned int *attachments, int count, int *out_count)
710{
711    return do_get_buffers(pDraw, width, height, attachments, count,
712                          out_count, TRUE);
713}
714
715static void
716DRI2InvalidateDrawable(DrawablePtr pDraw)
717{
718    DRI2DrawablePtr pPriv = DRI2GetDrawable(pDraw);
719    DRI2DrawableRefPtr ref = NULL;
720
721    if (!pPriv || !pPriv->needInvalidate)
722        return;
723
724    pPriv->needInvalidate = FALSE;
725
726    xorg_list_for_each_entry(ref, &pPriv->reference_list, link)
727        ref->invalidate(pDraw, ref->priv, ref->id);
728}
729
730/*
731 * In the direct rendered case, we throttle the clients that have more
732 * than their share of outstanding swaps (and thus busy buffers) when a
733 * new GetBuffers request is received.  In the AIGLX case, we allow the
734 * client to get the new buffers, but throttle when the next GLX request
735 * comes in (see __glXDRIcontextWait()).
736 */
737Bool
738DRI2ThrottleClient(ClientPtr client, DrawablePtr pDraw)
739{
740    DRI2DrawablePtr pPriv;
741
742    pPriv = DRI2GetDrawable(pDraw);
743    if (pPriv == NULL)
744        return FALSE;
745
746    /* Throttle to swap limit */
747    if (pPriv->swapsPending >= pPriv->swap_limit) {
748        if (dri2Sleep(client, pPriv, WAKE_SWAP)) {
749            ResetCurrentRequest(client);
750            client->sequence--;
751            return TRUE;
752        }
753    }
754
755    return FALSE;
756}
757
758void
759DRI2BlockClient(ClientPtr client, DrawablePtr pDraw)
760{
761    DRI2DrawablePtr pPriv;
762
763    pPriv = DRI2GetDrawable(pDraw);
764    if (pPriv == NULL)
765        return;
766
767    dri2Sleep(client, pPriv, WAKE_MSC);
768}
769
770static inline PixmapPtr GetDrawablePixmap(DrawablePtr drawable)
771{
772    if (drawable->type == DRAWABLE_PIXMAP)
773        return (PixmapPtr)drawable;
774    else {
775        struct _Window *pWin = (struct _Window *)drawable;
776        return drawable->pScreen->GetWindowPixmap(pWin);
777    }
778}
779
780/*
781 * A TraverseTree callback to invalidate all windows using the same
782 * pixmap
783 */
784static int
785DRI2InvalidateWalk(WindowPtr pWin, void *data)
786{
787    if (pWin->drawable.pScreen->GetWindowPixmap(pWin) != data)
788        return WT_DONTWALKCHILDREN;
789    DRI2InvalidateDrawable(&pWin->drawable);
790    return WT_WALKCHILDREN;
791}
792
793static void
794DRI2InvalidateDrawableAll(DrawablePtr pDraw)
795{
796    if (pDraw->type == DRAWABLE_WINDOW) {
797        WindowPtr pWin = (WindowPtr) pDraw;
798        PixmapPtr pPixmap = pDraw->pScreen->GetWindowPixmap(pWin);
799
800        /*
801         * Find the top-most window using this pixmap
802         */
803        while (pWin->parent &&
804               pDraw->pScreen->GetWindowPixmap(pWin->parent) == pPixmap)
805            pWin = pWin->parent;
806
807        /*
808         * Walk the sub-tree to invalidate all of the
809         * windows using the same pixmap
810         */
811        TraverseTree(pWin, DRI2InvalidateWalk, pPixmap);
812        DRI2InvalidateDrawable(&pPixmap->drawable);
813    }
814    else
815        DRI2InvalidateDrawable(pDraw);
816}
817
818DrawablePtr DRI2UpdatePrime(DrawablePtr pDraw, DRI2BufferPtr pDest)
819{
820    DRI2DrawablePtr pPriv = DRI2GetDrawable(pDraw);
821    PixmapPtr spix;
822    PixmapPtr mpix = GetDrawablePixmap(pDraw);
823    ScreenPtr primary, secondary;
824    Bool ret;
825
826    primary = mpix->drawable.pScreen;
827
828    if (pDraw->type == DRAWABLE_WINDOW) {
829        WindowPtr pWin = (WindowPtr)pDraw;
830        PixmapPtr pPixmap = pDraw->pScreen->GetWindowPixmap(pWin);
831
832        if (pDraw->pScreen->GetScreenPixmap(pDraw->pScreen) == pPixmap) {
833            if (pPriv->redirectpixmap &&
834                pPriv->redirectpixmap->drawable.width == pDraw->width &&
835                pPriv->redirectpixmap->drawable.height == pDraw->height &&
836                pPriv->redirectpixmap->drawable.depth == pDraw->depth) {
837                mpix = pPriv->redirectpixmap;
838            } else {
839                if (primary->ReplaceScanoutPixmap) {
840                    mpix = (*primary->CreatePixmap)(primary, pDraw->width, pDraw->height,
841                                                   pDraw->depth, CREATE_PIXMAP_USAGE_SHARED);
842                    if (!mpix)
843                        return NULL;
844
845                    ret = (*primary->ReplaceScanoutPixmap)(pDraw, mpix, TRUE);
846                    if (ret == FALSE) {
847                        (*primary->DestroyPixmap)(mpix);
848                        return NULL;
849                    }
850                    pPriv->redirectpixmap = mpix;
851                } else
852                    return NULL;
853            }
854        } else if (pPriv->redirectpixmap) {
855            (*primary->ReplaceScanoutPixmap)(pDraw, pPriv->redirectpixmap, FALSE);
856            (*primary->DestroyPixmap)(pPriv->redirectpixmap);
857            pPriv->redirectpixmap = NULL;
858        }
859    }
860
861    secondary = GetScreenPrime(pDraw->pScreen, pPriv->prime_id);
862
863    /* check if the pixmap is still fine */
864    if (pPriv->prime_secondary_pixmap) {
865        if (pPriv->prime_secondary_pixmap->primary_pixmap == mpix)
866            return &pPriv->prime_secondary_pixmap->drawable;
867        else {
868            PixmapUnshareSecondaryPixmap(pPriv->prime_secondary_pixmap);
869            (*pPriv->prime_secondary_pixmap->primary_pixmap->drawable.pScreen->DestroyPixmap)(pPriv->prime_secondary_pixmap->primary_pixmap);
870            (*secondary->DestroyPixmap)(pPriv->prime_secondary_pixmap);
871            pPriv->prime_secondary_pixmap = NULL;
872        }
873    }
874
875    spix = PixmapShareToSecondary(mpix, secondary);
876    if (!spix)
877        return NULL;
878
879    pPriv->prime_secondary_pixmap = spix;
880#ifdef COMPOSITE
881    spix->screen_x = mpix->screen_x;
882    spix->screen_y = mpix->screen_y;
883#endif
884
885    DRI2InvalidateDrawableAll(pDraw);
886    return &spix->drawable;
887}
888
889static void dri2_copy_region(DrawablePtr pDraw, RegionPtr pRegion,
890                             DRI2BufferPtr pDest, DRI2BufferPtr pSrc)
891{
892    DRI2DrawablePtr pPriv = DRI2GetDrawable(pDraw);
893    DRI2ScreenPtr ds;
894    ScreenPtr primeScreen;
895
896    primeScreen = GetScreenPrime(pDraw->pScreen, pPriv->prime_id);
897    ds = DRI2GetScreen(primeScreen);
898
899    if (ds->CopyRegion2)
900        (*ds->CopyRegion2)(primeScreen, pDraw, pRegion, pDest, pSrc);
901    else
902        (*ds->CopyRegion) (pDraw, pRegion, pDest, pSrc);
903
904    /* cause damage to the box */
905    if (pPriv->prime_id) {
906       BoxRec box;
907       RegionRec region;
908       box.x1 = 0;
909       box.x2 = box.x1 + pDraw->width;
910       box.y1 = 0;
911       box.y2 = box.y1 + pDraw->height;
912       RegionInit(&region, &box, 1);
913       RegionTranslate(&region, pDraw->x, pDraw->y);
914       DamageRegionAppend(pDraw, &region);
915       DamageRegionProcessPending(pDraw);
916       RegionUninit(&region);
917    }
918}
919
920int
921DRI2CopyRegion(DrawablePtr pDraw, RegionPtr pRegion,
922               unsigned int dest, unsigned int src)
923{
924    DRI2DrawablePtr pPriv;
925    DRI2BufferPtr pDestBuffer, pSrcBuffer;
926    int i;
927
928    pPriv = DRI2GetDrawable(pDraw);
929    if (pPriv == NULL)
930        return BadDrawable;
931
932    pDestBuffer = NULL;
933    pSrcBuffer = NULL;
934    for (i = 0; i < pPriv->bufferCount; i++) {
935        if (pPriv->buffers[i]->attachment == dest)
936            pDestBuffer = (DRI2BufferPtr) pPriv->buffers[i];
937        if (pPriv->buffers[i]->attachment == src)
938            pSrcBuffer = (DRI2BufferPtr) pPriv->buffers[i];
939    }
940    if (pSrcBuffer == NULL || pDestBuffer == NULL)
941        return BadValue;
942
943    dri2_copy_region(pDraw, pRegion, pDestBuffer, pSrcBuffer);
944
945    return Success;
946}
947
948/* Can this drawable be page flipped? */
949Bool
950DRI2CanFlip(DrawablePtr pDraw)
951{
952    ScreenPtr pScreen = pDraw->pScreen;
953    WindowPtr pWin, pRoot;
954    PixmapPtr pWinPixmap, pRootPixmap;
955
956    if (pDraw->type == DRAWABLE_PIXMAP)
957        return TRUE;
958
959    pRoot = pScreen->root;
960    pRootPixmap = pScreen->GetWindowPixmap(pRoot);
961
962    pWin = (WindowPtr) pDraw;
963    pWinPixmap = pScreen->GetWindowPixmap(pWin);
964    if (pRootPixmap != pWinPixmap)
965        return FALSE;
966    if (!RegionEqual(&pWin->clipList, &pRoot->winSize))
967        return FALSE;
968
969    /* Does the window match the pixmap exactly? */
970    if (pDraw->x != 0 || pDraw->y != 0 ||
971#ifdef COMPOSITE
972        pDraw->x != pWinPixmap->screen_x || pDraw->y != pWinPixmap->screen_y ||
973#endif
974        pDraw->width != pWinPixmap->drawable.width ||
975        pDraw->height != pWinPixmap->drawable.height)
976        return FALSE;
977
978    return TRUE;
979}
980
981/* Can we do a pixmap exchange instead of a blit? */
982Bool
983DRI2CanExchange(DrawablePtr pDraw)
984{
985    return FALSE;
986}
987
988void
989DRI2WaitMSCComplete(ClientPtr client, DrawablePtr pDraw, int frame,
990                    unsigned int tv_sec, unsigned int tv_usec)
991{
992    DRI2DrawablePtr pPriv;
993
994    pPriv = DRI2GetDrawable(pDraw);
995    if (pPriv == NULL)
996        return;
997
998    ProcDRI2WaitMSCReply(client, ((CARD64) tv_sec * 1000000) + tv_usec,
999                         frame, pPriv->swap_count);
1000
1001    dri2WakeAll(client, pPriv, WAKE_MSC);
1002}
1003
1004static void
1005DRI2WakeClient(ClientPtr client, DrawablePtr pDraw, int frame,
1006               unsigned int tv_sec, unsigned int tv_usec)
1007{
1008    ScreenPtr pScreen = pDraw->pScreen;
1009    DRI2DrawablePtr pPriv;
1010
1011    pPriv = DRI2GetDrawable(pDraw);
1012    if (pPriv == NULL) {
1013        xf86DrvMsg(pScreen->myNum, X_ERROR,
1014                   "[DRI2] %s: bad drawable\n", __func__);
1015        return;
1016    }
1017
1018    /*
1019     * Swap completed.
1020     * Wake the client iff:
1021     *   - it was waiting on SBC
1022     *   - was blocked due to GLX make current
1023     *   - was blocked due to swap throttling
1024     *   - is not blocked due to an MSC wait
1025     */
1026    if (pPriv->target_sbc != -1 && pPriv->target_sbc <= pPriv->swap_count) {
1027        if (dri2WakeAll(client, pPriv, WAKE_SBC)) {
1028            ProcDRI2WaitMSCReply(client, ((CARD64) tv_sec * 1000000) + tv_usec,
1029                                 frame, pPriv->swap_count);
1030            pPriv->target_sbc = -1;
1031        }
1032    }
1033
1034    dri2WakeAll(CLIENT_SIGNAL_ANY, pPriv, WAKE_SWAP);
1035}
1036
1037void
1038DRI2SwapComplete(ClientPtr client, DrawablePtr pDraw, int frame,
1039                 unsigned int tv_sec, unsigned int tv_usec, int type,
1040                 DRI2SwapEventPtr swap_complete, void *swap_data)
1041{
1042    ScreenPtr pScreen = pDraw->pScreen;
1043    DRI2DrawablePtr pPriv;
1044    CARD64 ust = 0;
1045    BoxRec box;
1046    RegionRec region;
1047
1048    pPriv = DRI2GetDrawable(pDraw);
1049    if (pPriv == NULL) {
1050        xf86DrvMsg(pScreen->myNum, X_ERROR,
1051                   "[DRI2] %s: bad drawable\n", __func__);
1052        return;
1053    }
1054
1055    pPriv->swapsPending--;
1056    pPriv->swap_count++;
1057
1058    box.x1 = 0;
1059    box.y1 = 0;
1060    box.x2 = pDraw->width;
1061    box.y2 = pDraw->height;
1062    RegionInit(&region, &box, 0);
1063    DRI2CopyRegion(pDraw, &region, DRI2BufferFakeFrontLeft,
1064                   DRI2BufferFrontLeft);
1065
1066    ust = ((CARD64) tv_sec * 1000000) + tv_usec;
1067    if (swap_complete)
1068        swap_complete(client, swap_data, type, ust, frame, pPriv->swap_count);
1069
1070    pPriv->last_swap_msc = frame;
1071    pPriv->last_swap_ust = ust;
1072
1073    DRI2WakeClient(client, pDraw, frame, tv_sec, tv_usec);
1074}
1075
1076Bool
1077DRI2WaitSwap(ClientPtr client, DrawablePtr pDrawable)
1078{
1079    DRI2DrawablePtr pPriv = DRI2GetDrawable(pDrawable);
1080
1081    /* If we're currently waiting for a swap on this drawable, reset
1082     * the request and suspend the client. */
1083    if (pPriv && pPriv->swapsPending) {
1084        if (dri2Sleep(client, pPriv, WAKE_SWAP)) {
1085            ResetCurrentRequest(client);
1086            client->sequence--;
1087            return TRUE;
1088        }
1089    }
1090
1091    return FALSE;
1092}
1093
1094
1095
1096int
1097DRI2SwapBuffers(ClientPtr client, DrawablePtr pDraw, CARD64 target_msc,
1098                CARD64 divisor, CARD64 remainder, CARD64 * swap_target,
1099                DRI2SwapEventPtr func, void *data)
1100{
1101    ScreenPtr pScreen = pDraw->pScreen;
1102    DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen);
1103    DRI2DrawablePtr pPriv;
1104    DRI2BufferPtr pDestBuffer = NULL, pSrcBuffer = NULL;
1105    int ret, i;
1106    CARD64 ust, current_msc;
1107
1108    pPriv = DRI2GetDrawable(pDraw);
1109    if (pPriv == NULL) {
1110        xf86DrvMsg(pScreen->myNum, X_ERROR,
1111                   "[DRI2] %s: bad drawable\n", __func__);
1112        return BadDrawable;
1113    }
1114
1115    /* According to spec, return expected swapbuffers count SBC after this swap
1116     * will complete. This is ignored unless we return Success, but it must be
1117     * initialized on every path where we return Success or the caller will send
1118     * an uninitialized value off the stack to the client. So let's initialize
1119     * it as early as possible, just to be sure.
1120     */
1121    *swap_target = pPriv->swap_count + pPriv->swapsPending + 1;
1122
1123    for (i = 0; i < pPriv->bufferCount; i++) {
1124        if (pPriv->buffers[i]->attachment == DRI2BufferFrontLeft)
1125            pDestBuffer = (DRI2BufferPtr) pPriv->buffers[i];
1126        if (pPriv->buffers[i]->attachment == DRI2BufferBackLeft)
1127            pSrcBuffer = (DRI2BufferPtr) pPriv->buffers[i];
1128    }
1129    if (pSrcBuffer == NULL || pDestBuffer == NULL) {
1130        xf86DrvMsg(pScreen->myNum, X_ERROR,
1131                   "[DRI2] %s: drawable has no back or front?\n", __func__);
1132        return BadDrawable;
1133    }
1134
1135    /* Old DDX or no swap interval, just blit */
1136    if (!ds->ScheduleSwap || !pPriv->swap_interval || pPriv->prime_id) {
1137        BoxRec box;
1138        RegionRec region;
1139
1140        box.x1 = 0;
1141        box.y1 = 0;
1142        box.x2 = pDraw->width;
1143        box.y2 = pDraw->height;
1144        RegionInit(&region, &box, 0);
1145
1146        pPriv->swapsPending++;
1147
1148        dri2_copy_region(pDraw, &region, pDestBuffer, pSrcBuffer);
1149        DRI2SwapComplete(client, pDraw, target_msc, 0, 0, DRI2_BLIT_COMPLETE,
1150                         func, data);
1151        return Success;
1152    }
1153
1154    /*
1155     * In the simple glXSwapBuffers case, all params will be 0, and we just
1156     * need to schedule a swap for the last swap target + the swap interval.
1157     */
1158    if (target_msc == 0 && divisor == 0 && remainder == 0) {
1159        /* If the current vblank count of the drawable's crtc is lower
1160         * than the count stored in last_swap_target from a previous swap
1161         * then reinitialize last_swap_target to the current crtc's msc,
1162         * otherwise the swap will hang. This will happen if the drawable
1163         * is moved to a crtc with a lower refresh rate, or a crtc that just
1164         * got enabled.
1165         */
1166        if (ds->GetMSC) {
1167            if (!(*ds->GetMSC) (pDraw, &ust, &current_msc))
1168                pPriv->last_swap_target = 0;
1169
1170            if (current_msc < pPriv->last_swap_target)
1171                pPriv->last_swap_target = current_msc;
1172
1173        }
1174
1175        /*
1176         * Swap target for this swap is last swap target + swap interval since
1177         * we have to account for the current swap count, interval, and the
1178         * number of pending swaps.
1179         */
1180        target_msc = pPriv->last_swap_target + pPriv->swap_interval;
1181
1182    }
1183
1184    pPriv->swapsPending++;
1185    ret = (*ds->ScheduleSwap) (client, pDraw, pDestBuffer, pSrcBuffer,
1186                               &target_msc, divisor, remainder, func, data);
1187    if (!ret) {
1188        pPriv->swapsPending--;  /* didn't schedule */
1189        xf86DrvMsg(pScreen->myNum, X_ERROR,
1190                   "[DRI2] %s: driver failed to schedule swap\n", __func__);
1191        return BadDrawable;
1192    }
1193
1194    pPriv->last_swap_target = target_msc;
1195
1196    DRI2InvalidateDrawableAll(pDraw);
1197
1198    return Success;
1199}
1200
1201void
1202DRI2SwapInterval(DrawablePtr pDrawable, int interval)
1203{
1204    ScreenPtr pScreen = pDrawable->pScreen;
1205    DRI2DrawablePtr pPriv = DRI2GetDrawable(pDrawable);
1206
1207    if (pPriv == NULL) {
1208        xf86DrvMsg(pScreen->myNum, X_ERROR,
1209                   "[DRI2] %s: bad drawable\n", __func__);
1210        return;
1211    }
1212
1213    /* fixme: check against arbitrary max? */
1214    pPriv->swap_interval = interval;
1215}
1216
1217int
1218DRI2GetMSC(DrawablePtr pDraw, CARD64 * ust, CARD64 * msc, CARD64 * sbc)
1219{
1220    ScreenPtr pScreen = pDraw->pScreen;
1221    DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen);
1222    DRI2DrawablePtr pPriv;
1223    Bool ret;
1224
1225    pPriv = DRI2GetDrawable(pDraw);
1226    if (pPriv == NULL) {
1227        xf86DrvMsg(pScreen->myNum, X_ERROR,
1228                   "[DRI2] %s: bad drawable\n", __func__);
1229        return BadDrawable;
1230    }
1231
1232    if (!ds->GetMSC) {
1233        *ust = 0;
1234        *msc = 0;
1235        *sbc = pPriv->swap_count;
1236        return Success;
1237    }
1238
1239    /*
1240     * Spec needs to be updated to include unmapped or redirected
1241     * drawables
1242     */
1243
1244    ret = (*ds->GetMSC) (pDraw, ust, msc);
1245    if (!ret)
1246        return BadDrawable;
1247
1248    *sbc = pPriv->swap_count;
1249
1250    return Success;
1251}
1252
1253int
1254DRI2WaitMSC(ClientPtr client, DrawablePtr pDraw, CARD64 target_msc,
1255            CARD64 divisor, CARD64 remainder)
1256{
1257    DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen);
1258    DRI2DrawablePtr pPriv;
1259    Bool ret;
1260
1261    pPriv = DRI2GetDrawable(pDraw);
1262    if (pPriv == NULL)
1263        return BadDrawable;
1264
1265    /* Old DDX just completes immediately */
1266    if (!ds->ScheduleWaitMSC) {
1267        DRI2WaitMSCComplete(client, pDraw, target_msc, 0, 0);
1268
1269        return Success;
1270    }
1271
1272    ret =
1273        (*ds->ScheduleWaitMSC) (client, pDraw, target_msc, divisor, remainder);
1274    if (!ret)
1275        return BadDrawable;
1276
1277    return Success;
1278}
1279
1280int
1281DRI2WaitSBC(ClientPtr client, DrawablePtr pDraw, CARD64 target_sbc)
1282{
1283    DRI2DrawablePtr pPriv;
1284
1285    pPriv = DRI2GetDrawable(pDraw);
1286    if (pPriv == NULL)
1287        return BadDrawable;
1288
1289    if (pPriv->target_sbc != -1) /* already in use */
1290        return BadDrawable;
1291
1292    /* target_sbc == 0 means to block until all pending swaps are
1293     * finished. Recalculate target_sbc to get that behaviour.
1294     */
1295    if (target_sbc == 0)
1296        target_sbc = pPriv->swap_count + pPriv->swapsPending;
1297
1298    /* If current swap count already >= target_sbc, reply and
1299     * return immediately with (ust, msc, sbc) triplet of
1300     * most recent completed swap.
1301     */
1302    if (pPriv->swap_count >= target_sbc) {
1303        ProcDRI2WaitMSCReply(client, pPriv->last_swap_ust,
1304                             pPriv->last_swap_msc, pPriv->swap_count);
1305        return Success;
1306    }
1307
1308    if (!dri2Sleep(client, pPriv, WAKE_SBC))
1309        return BadAlloc;
1310
1311    pPriv->target_sbc = target_sbc;
1312    return Success;
1313}
1314
1315Bool
1316DRI2HasSwapControl(ScreenPtr pScreen)
1317{
1318    DRI2ScreenPtr ds = DRI2GetScreen(pScreen);
1319
1320    return ds->ScheduleSwap && ds->GetMSC;
1321}
1322
1323Bool
1324DRI2Connect(ClientPtr client, ScreenPtr pScreen,
1325            unsigned int driverType, int *fd,
1326            const char **driverName, const char **deviceName)
1327{
1328    DRI2ScreenPtr ds;
1329    uint32_t prime_id = DRI2DriverPrimeId(driverType);
1330    uint32_t driver_id = driverType & 0xffff;
1331
1332    if (!dixPrivateKeyRegistered(dri2ScreenPrivateKey))
1333        return FALSE;
1334
1335    ds = DRI2GetScreenPrime(pScreen, prime_id);
1336    if (ds == NULL)
1337        return FALSE;
1338
1339    if (driver_id >= ds->numDrivers ||
1340        !ds->driverNames[driver_id])
1341        return FALSE;
1342
1343    *driverName = ds->driverNames[driver_id];
1344    *deviceName = ds->deviceName;
1345    *fd = ds->fd;
1346
1347    if (client) {
1348        DRI2ClientPtr dri2_client;
1349        dri2_client = dri2ClientPrivate(client);
1350        dri2_client->prime_id = prime_id;
1351    }
1352
1353    return TRUE;
1354}
1355
1356static int
1357DRI2AuthMagic (ScreenPtr pScreen, uint32_t magic)
1358{
1359    DRI2ScreenPtr ds = DRI2GetScreen(pScreen);
1360    if (ds == NULL)
1361        return -EINVAL;
1362
1363    return (*ds->LegacyAuthMagic) (ds->fd, magic);
1364}
1365
1366Bool
1367DRI2Authenticate(ClientPtr client, ScreenPtr pScreen, uint32_t magic)
1368{
1369    DRI2ScreenPtr ds;
1370    DRI2ClientPtr dri2_client;
1371    ScreenPtr primescreen;
1372
1373    if (!dixPrivateKeyRegistered(dri2ScreenPrivateKey))
1374        return FALSE;
1375
1376    dri2_client = dri2ClientPrivate(client);
1377
1378    ds = DRI2GetScreenPrime(pScreen, dri2_client->prime_id);
1379    if (ds == NULL)
1380        return FALSE;
1381
1382    primescreen = GetScreenPrime(pScreen, dri2_client->prime_id);
1383    if ((*ds->AuthMagic)(primescreen, magic))
1384        return FALSE;
1385    return TRUE;
1386}
1387
1388static int
1389DRI2ConfigNotify(WindowPtr pWin, int x, int y, int w, int h, int bw,
1390                 WindowPtr pSib)
1391{
1392    DrawablePtr pDraw = (DrawablePtr) pWin;
1393    ScreenPtr pScreen = pDraw->pScreen;
1394    DRI2ScreenPtr ds = DRI2GetScreen(pScreen);
1395    DRI2DrawablePtr dd = DRI2GetDrawable(pDraw);
1396    int ret;
1397
1398    if (ds->ConfigNotify) {
1399        pScreen->ConfigNotify = ds->ConfigNotify;
1400
1401        ret = (*pScreen->ConfigNotify) (pWin, x, y, w, h, bw, pSib);
1402
1403        ds->ConfigNotify = pScreen->ConfigNotify;
1404        pScreen->ConfigNotify = DRI2ConfigNotify;
1405        if (ret)
1406            return ret;
1407    }
1408
1409    if (!dd || (dd->width == w && dd->height == h))
1410        return Success;
1411
1412    DRI2InvalidateDrawable(pDraw);
1413    return Success;
1414}
1415
1416static void
1417DRI2SetWindowPixmap(WindowPtr pWin, PixmapPtr pPix)
1418{
1419    ScreenPtr pScreen = pWin->drawable.pScreen;
1420    DRI2ScreenPtr ds = DRI2GetScreen(pScreen);
1421
1422    pScreen->SetWindowPixmap = ds->SetWindowPixmap;
1423    (*pScreen->SetWindowPixmap) (pWin, pPix);
1424    ds->SetWindowPixmap = pScreen->SetWindowPixmap;
1425    pScreen->SetWindowPixmap = DRI2SetWindowPixmap;
1426
1427    DRI2InvalidateDrawable(&pWin->drawable);
1428}
1429
1430#define MAX_PRIME DRI2DriverPrimeMask
1431static int
1432get_prime_id(void)
1433{
1434    int i;
1435    /* start at 1, prime id 0 is just normal driver */
1436    for (i = 1; i < MAX_PRIME; i++) {
1437         if (prime_id_allocate_bitmask & (1 << i))
1438             continue;
1439
1440         prime_id_allocate_bitmask |= (1 << i);
1441         return i;
1442    }
1443    return -1;
1444}
1445
1446#include "pci_ids/pci_id_driver_map.h"
1447
1448static char *
1449dri2_probe_driver_name(ScreenPtr pScreen, DRI2InfoPtr info)
1450{
1451#ifdef WITH_LIBDRM
1452    int i, j;
1453    char *driver = NULL;
1454    drmDevicePtr dev;
1455
1456    /* For non-PCI devices and drmGetDevice fail, just assume that
1457     * the 3D driver is named the same as the kernel driver. This is
1458     * currently true for vc4 and msm (freedreno).
1459     */
1460    if (drmGetDevice(info->fd, &dev) || dev->bustype != DRM_BUS_PCI) {
1461        drmVersionPtr version = drmGetVersion(info->fd);
1462
1463        if (!version) {
1464            xf86DrvMsg(pScreen->myNum, X_ERROR,
1465                       "[DRI2] Couldn't drmGetVersion() on non-PCI device, "
1466                       "no driver name found.\n");
1467            return NULL;
1468        }
1469
1470        driver = strndup(version->name, version->name_len);
1471        drmFreeVersion(version);
1472        return driver;
1473    }
1474
1475    for (i = 0; driver_map[i].driver; i++) {
1476        if (dev->deviceinfo.pci->vendor_id != driver_map[i].vendor_id)
1477            continue;
1478
1479        if (driver_map[i].num_chips_ids == -1) {
1480             driver = strdup(driver_map[i].driver);
1481             goto out;
1482        }
1483
1484        for (j = 0; j < driver_map[i].num_chips_ids; j++) {
1485            if (driver_map[i].chip_ids[j] == dev->deviceinfo.pci->device_id) {
1486                driver = strdup(driver_map[i].driver);
1487                goto out;
1488            }
1489        }
1490    }
1491
1492    xf86DrvMsg(pScreen->myNum, X_ERROR,
1493               "[DRI2] No driver mapping found for PCI device "
1494               "0x%04x / 0x%04x\n",
1495               dev->deviceinfo.pci->vendor_id, dev->deviceinfo.pci->device_id);
1496out:
1497    drmFreeDevice(&dev);
1498    return driver;
1499#else
1500    return NULL;
1501#endif
1502}
1503
1504Bool
1505DRI2ScreenInit(ScreenPtr pScreen, DRI2InfoPtr info)
1506{
1507    DRI2ScreenPtr ds;
1508
1509    const char *driverTypeNames[] = {
1510        "DRI",                  /* DRI2DriverDRI */
1511        "VDPAU",                /* DRI2DriverVDPAU */
1512    };
1513    unsigned int i;
1514    CARD8 cur_minor;
1515
1516    if (info->version < 3)
1517        return FALSE;
1518
1519    if (!dixRegisterPrivateKey(&dri2ScreenPrivateKeyRec, PRIVATE_SCREEN, 0))
1520        return FALSE;
1521
1522    if (!dixRegisterPrivateKey(&dri2WindowPrivateKeyRec, PRIVATE_WINDOW, 0))
1523        return FALSE;
1524
1525    if (!dixRegisterPrivateKey(&dri2PixmapPrivateKeyRec, PRIVATE_PIXMAP, 0))
1526        return FALSE;
1527
1528    if (!dixRegisterPrivateKey(&dri2ClientPrivateKeyRec, PRIVATE_CLIENT, sizeof(DRI2ClientRec)))
1529        return FALSE;
1530
1531    ds = calloc(1, sizeof *ds);
1532    if (!ds)
1533        return FALSE;
1534
1535    ds->screen = pScreen;
1536    ds->fd = info->fd;
1537    ds->deviceName = info->deviceName;
1538    dri2_major = 1;
1539
1540    ds->CreateBuffer = info->CreateBuffer;
1541    ds->DestroyBuffer = info->DestroyBuffer;
1542    ds->CopyRegion = info->CopyRegion;
1543    cur_minor = 1;
1544
1545    if (info->version >= 4) {
1546        ds->ScheduleSwap = info->ScheduleSwap;
1547        ds->ScheduleWaitMSC = info->ScheduleWaitMSC;
1548        ds->GetMSC = info->GetMSC;
1549        cur_minor = 3;
1550    }
1551
1552    if (info->version >= 5) {
1553        ds->LegacyAuthMagic = info->AuthMagic;
1554    }
1555
1556    if (info->version >= 6) {
1557        ds->ReuseBufferNotify = info->ReuseBufferNotify;
1558        ds->SwapLimitValidate = info->SwapLimitValidate;
1559    }
1560
1561    if (info->version >= 7) {
1562        ds->GetParam = info->GetParam;
1563        cur_minor = 4;
1564    }
1565
1566    if (info->version >= 8) {
1567        ds->AuthMagic = info->AuthMagic2;
1568    }
1569
1570    if (info->version >= 9) {
1571        ds->CreateBuffer2 = info->CreateBuffer2;
1572        if (info->CreateBuffer2 && pScreen->isGPU) {
1573            ds->prime_id = get_prime_id();
1574            if (ds->prime_id == -1) {
1575                free(ds);
1576                return FALSE;
1577            }
1578        }
1579        ds->DestroyBuffer2 = info->DestroyBuffer2;
1580        ds->CopyRegion2 = info->CopyRegion2;
1581    }
1582
1583    /*
1584     * if the driver doesn't provide an AuthMagic function or the info struct
1585     * version is too low, call through LegacyAuthMagic
1586     */
1587    if (!ds->AuthMagic) {
1588        ds->AuthMagic = DRI2AuthMagic;
1589        /*
1590         * If the driver doesn't provide an AuthMagic function
1591         * it relies on the old method (using libdrm) or fails
1592         */
1593        if (!ds->LegacyAuthMagic)
1594#ifdef WITH_LIBDRM
1595            ds->LegacyAuthMagic = drmAuthMagic;
1596#else
1597            goto err_out;
1598#endif
1599    }
1600
1601    /* Initialize minor if needed and set to minimum provied by DDX */
1602    if (!dri2_minor || dri2_minor > cur_minor)
1603        dri2_minor = cur_minor;
1604
1605    if (info->version == 3 || info->numDrivers == 0) {
1606        /* Driver too old: use the old-style driverName field */
1607        ds->numDrivers = info->driverName ? 1 : 2;
1608        ds->driverNames = xallocarray(ds->numDrivers, sizeof(*ds->driverNames));
1609        if (!ds->driverNames)
1610            goto err_out;
1611
1612        if (info->driverName) {
1613            ds->driverNames[0] = info->driverName;
1614        } else {
1615            /* FIXME dri2_probe_driver_name() returns a strdup-ed string,
1616             * currently this gets leaked */
1617            ds->driverNames[0] = ds->driverNames[1] = dri2_probe_driver_name(pScreen, info);
1618            if (!ds->driverNames[0])
1619                return FALSE;
1620
1621            /* There is no VDPAU driver for i965, fallback to the generic
1622             * OpenGL/VAAPI va_gl backend to emulate VDPAU on i965. */
1623            if (strcmp(ds->driverNames[0], "i965") == 0)
1624                ds->driverNames[1] = "va_gl";
1625        }
1626    }
1627    else {
1628        ds->numDrivers = info->numDrivers;
1629        ds->driverNames = xallocarray(info->numDrivers, sizeof(*ds->driverNames));
1630        if (!ds->driverNames)
1631            goto err_out;
1632        memcpy(ds->driverNames, info->driverNames,
1633               info->numDrivers * sizeof(*ds->driverNames));
1634    }
1635
1636    dixSetPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey, ds);
1637
1638    ds->ConfigNotify = pScreen->ConfigNotify;
1639    pScreen->ConfigNotify = DRI2ConfigNotify;
1640
1641    ds->SetWindowPixmap = pScreen->SetWindowPixmap;
1642    pScreen->SetWindowPixmap = DRI2SetWindowPixmap;
1643
1644    xf86DrvMsg(pScreen->myNum, X_INFO, "[DRI2] Setup complete\n");
1645    for (i = 0; i < ARRAY_SIZE(driverTypeNames); i++) {
1646        if (i < ds->numDrivers && ds->driverNames[i]) {
1647            xf86DrvMsg(pScreen->myNum, X_INFO, "[DRI2]   %s driver: %s\n",
1648                       driverTypeNames[i], ds->driverNames[i]);
1649        }
1650    }
1651
1652    return TRUE;
1653
1654 err_out:
1655    xf86DrvMsg(pScreen->myNum, X_WARNING,
1656               "[DRI2] Initialization failed for info version %d.\n",
1657               info->version);
1658    free(ds);
1659    return FALSE;
1660}
1661
1662void
1663DRI2CloseScreen(ScreenPtr pScreen)
1664{
1665    DRI2ScreenPtr ds = DRI2GetScreen(pScreen);
1666
1667    pScreen->ConfigNotify = ds->ConfigNotify;
1668    pScreen->SetWindowPixmap = ds->SetWindowPixmap;
1669
1670    if (ds->prime_id)
1671        prime_id_allocate_bitmask &= ~(1 << ds->prime_id);
1672    free(ds->driverNames);
1673    free(ds);
1674    dixSetPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey, NULL);
1675}
1676
1677/* Called by InitExtensions() */
1678Bool
1679DRI2ModuleSetup(void)
1680{
1681    dri2DrawableRes = CreateNewResourceType(DRI2DrawableGone, "DRI2Drawable");
1682    if (!dri2DrawableRes)
1683        return FALSE;
1684
1685    return TRUE;
1686}
1687
1688void
1689DRI2Version(int *major, int *minor)
1690{
1691    if (major != NULL)
1692        *major = 1;
1693
1694    if (minor != NULL)
1695        *minor = 2;
1696}
1697
1698int
1699DRI2GetParam(ClientPtr client,
1700             DrawablePtr drawable,
1701             CARD64 param,
1702             BOOL *is_param_recognized,
1703             CARD64 *value)
1704{
1705    DRI2ScreenPtr ds = DRI2GetScreen(drawable->pScreen);
1706    char high_byte = (param >> 24);
1707
1708    switch (high_byte) {
1709    case 0:
1710        /* Parameter names whose high_byte is 0 are reserved for the X
1711         * server. The server currently recognizes no parameters.
1712         */
1713        goto not_recognized;
1714    case 1:
1715        /* Parameter names whose high byte is 1 are reserved for the DDX. */
1716        if (ds->GetParam)
1717            return ds->GetParam(client, drawable, param,
1718                                is_param_recognized, value);
1719        else
1720            goto not_recognized;
1721    default:
1722        /* Other parameter names are reserved for future use. They are never
1723         * recognized.
1724         */
1725        goto not_recognized;
1726    }
1727
1728not_recognized:
1729    *is_param_recognized = FALSE;
1730    return Success;
1731}
1732