dri2.c revision 8223e2f2
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 "xf86Module.h"
42#include "list.h"
43#include "scrnintstr.h"
44#include "windowstr.h"
45#include "dixstruct.h"
46#include "dri2.h"
47#include "xf86VGAarbiter.h"
48
49#include "xf86.h"
50
51CARD8 dri2_major; /* version of DRI2 supported by DDX */
52CARD8 dri2_minor;
53
54static DevPrivateKeyRec dri2ScreenPrivateKeyRec;
55#define dri2ScreenPrivateKey (&dri2ScreenPrivateKeyRec)
56
57static DevPrivateKeyRec dri2WindowPrivateKeyRec;
58#define dri2WindowPrivateKey (&dri2WindowPrivateKeyRec)
59
60static DevPrivateKeyRec dri2PixmapPrivateKeyRec;
61#define dri2PixmapPrivateKey (&dri2PixmapPrivateKeyRec)
62
63static RESTYPE       dri2DrawableRes;
64
65typedef struct _DRI2Screen *DRI2ScreenPtr;
66
67typedef struct _DRI2Drawable {
68    DRI2ScreenPtr        dri2_screen;
69    DrawablePtr		 drawable;
70    struct list		 reference_list;
71    int			 width;
72    int			 height;
73    DRI2BufferPtr	*buffers;
74    int			 bufferCount;
75    unsigned int	 swapsPending;
76    ClientPtr		 blockedClient;
77    Bool		 blockedOnMsc;
78    int			 swap_interval;
79    CARD64		 swap_count;
80    int64_t		 target_sbc; /* -1 means no SBC wait outstanding */
81    CARD64		 last_swap_target; /* most recently queued swap target */
82    CARD64		 last_swap_msc; /* msc at completion of most recent swap */
83    CARD64		 last_swap_ust; /* ust at completion of most recent swap */
84    int			 swap_limit; /* for N-buffering */
85    unsigned long	 serialNumber;
86} DRI2DrawableRec, *DRI2DrawablePtr;
87
88typedef struct _DRI2Screen {
89    ScreenPtr			 screen;
90    int				 refcnt;
91    unsigned int		 numDrivers;
92    const char			**driverNames;
93    const char			*deviceName;
94    int				 fd;
95    unsigned int		 lastSequence;
96
97    DRI2CreateBufferProcPtr	 CreateBuffer;
98    DRI2DestroyBufferProcPtr	 DestroyBuffer;
99    DRI2CopyRegionProcPtr	 CopyRegion;
100    DRI2ScheduleSwapProcPtr	 ScheduleSwap;
101    DRI2GetMSCProcPtr		 GetMSC;
102    DRI2ScheduleWaitMSCProcPtr	 ScheduleWaitMSC;
103    DRI2AuthMagicProcPtr	 AuthMagic;
104
105    HandleExposuresProcPtr       HandleExposures;
106
107    ConfigNotifyProcPtr		 ConfigNotify;
108} DRI2ScreenRec;
109
110static DRI2ScreenPtr
111DRI2GetScreen(ScreenPtr pScreen)
112{
113    return dixLookupPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey);
114}
115
116static DRI2DrawablePtr
117DRI2GetDrawable(DrawablePtr pDraw)
118{
119    WindowPtr pWin;
120    PixmapPtr pPixmap;
121
122    switch (pDraw->type) {
123    case DRAWABLE_WINDOW:
124	pWin = (WindowPtr) pDraw;
125	return dixLookupPrivate(&pWin->devPrivates, dri2WindowPrivateKey);
126    case DRAWABLE_PIXMAP:
127	pPixmap = (PixmapPtr) pDraw;
128	return dixLookupPrivate(&pPixmap->devPrivates, dri2PixmapPrivateKey);
129    default:
130	return NULL;
131    }
132}
133
134static unsigned long
135DRI2DrawableSerial(DrawablePtr pDraw)
136{
137    ScreenPtr pScreen = pDraw->pScreen;
138    PixmapPtr pPix;
139
140    if (pDraw->type != DRAWABLE_WINDOW)
141	return pDraw->serialNumber;
142
143    pPix = pScreen->GetWindowPixmap((WindowPtr)pDraw);
144    return pPix->drawable.serialNumber;
145}
146
147static DRI2DrawablePtr
148DRI2AllocateDrawable(DrawablePtr pDraw)
149{
150    DRI2ScreenPtr   ds = DRI2GetScreen(pDraw->pScreen);
151    DRI2DrawablePtr pPriv;
152    CARD64          ust;
153    WindowPtr pWin;
154    PixmapPtr pPixmap;
155
156    pPriv = malloc(sizeof *pPriv);
157    if (pPriv == NULL)
158	return NULL;
159
160    pPriv->dri2_screen = ds;
161    pPriv->drawable = pDraw;
162    pPriv->width = pDraw->width;
163    pPriv->height = pDraw->height;
164    pPriv->buffers = NULL;
165    pPriv->bufferCount = 0;
166    pPriv->swapsPending = 0;
167    pPriv->blockedClient = NULL;
168    pPriv->blockedOnMsc = FALSE;
169    pPriv->swap_count = 0;
170    pPriv->target_sbc = -1;
171    pPriv->swap_interval = 1;
172    /* Initialize last swap target from DDX if possible */
173    if (!ds->GetMSC || !(*ds->GetMSC)(pDraw, &ust, &pPriv->last_swap_target))
174	pPriv->last_swap_target = 0;
175
176    pPriv->swap_limit = 1; /* default to double buffering */
177    pPriv->last_swap_msc = 0;
178    pPriv->last_swap_ust = 0;
179    list_init(&pPriv->reference_list);
180    pPriv->serialNumber = DRI2DrawableSerial(pDraw);
181
182    if (pDraw->type == DRAWABLE_WINDOW) {
183	pWin = (WindowPtr) pDraw;
184	dixSetPrivate(&pWin->devPrivates, dri2WindowPrivateKey, pPriv);
185    } else {
186	pPixmap = (PixmapPtr) pDraw;
187	dixSetPrivate(&pPixmap->devPrivates, dri2PixmapPrivateKey, pPriv);
188    }
189
190    return pPriv;
191}
192
193typedef struct DRI2DrawableRefRec {
194    XID		  id;
195    XID		  dri2_id;
196    DRI2InvalidateProcPtr	invalidate;
197    void	 *priv;
198    struct list	  link;
199} DRI2DrawableRefRec, *DRI2DrawableRefPtr;
200
201static DRI2DrawableRefPtr
202DRI2LookupDrawableRef(DRI2DrawablePtr pPriv, XID id)
203{
204    DRI2DrawableRefPtr ref;
205
206    list_for_each_entry(ref, &pPriv->reference_list, link) {
207	if (ref->id == id)
208	    return ref;
209    }
210
211    return NULL;
212}
213
214static int
215DRI2AddDrawableRef(DRI2DrawablePtr pPriv, XID id, XID dri2_id,
216		   DRI2InvalidateProcPtr invalidate, void *priv)
217{
218    DRI2DrawableRefPtr ref;
219
220    ref = malloc(sizeof *ref);
221    if (ref == NULL)
222	return BadAlloc;
223
224    if (!AddResource(dri2_id, dri2DrawableRes, pPriv))
225	return BadAlloc;
226    if (!DRI2LookupDrawableRef(pPriv, id))
227	if (!AddResource(id, dri2DrawableRes, pPriv))
228	    return BadAlloc;
229
230    ref->id = id;
231    ref->dri2_id = dri2_id;
232    ref->invalidate = invalidate;
233    ref->priv = priv;
234    list_add(&ref->link, &pPriv->reference_list);
235
236    return Success;
237}
238
239int
240DRI2CreateDrawable(ClientPtr client, DrawablePtr pDraw, XID id,
241		   DRI2InvalidateProcPtr invalidate, void *priv)
242{
243    DRI2DrawablePtr pPriv;
244    XID dri2_id;
245    int rc;
246
247    pPriv = DRI2GetDrawable(pDraw);
248    if (pPriv == NULL)
249	pPriv = DRI2AllocateDrawable(pDraw);
250    if (pPriv == NULL)
251	return BadAlloc;
252
253    dri2_id = FakeClientID(client->index);
254    rc = DRI2AddDrawableRef(pPriv, id, dri2_id, invalidate, priv);
255    if (rc != Success)
256	return rc;
257
258    return Success;
259}
260
261static int DRI2DrawableGone(pointer p, XID id)
262{
263    DRI2DrawablePtr pPriv = p;
264    DRI2ScreenPtr   ds = pPriv->dri2_screen;
265    DRI2DrawableRefPtr ref, next;
266    WindowPtr pWin;
267    PixmapPtr pPixmap;
268    DrawablePtr pDraw;
269    int i;
270
271    list_for_each_entry_safe(ref, next, &pPriv->reference_list, link) {
272	if (ref->dri2_id == id) {
273	    list_del(&ref->link);
274	    /* If this was the last ref under this X drawable XID,
275	     * unregister the X drawable resource. */
276	    if (!DRI2LookupDrawableRef(pPriv, ref->id))
277		FreeResourceByType(ref->id, dri2DrawableRes, TRUE);
278	    free(ref);
279	    break;
280	}
281
282	if (ref->id == id) {
283	    list_del(&ref->link);
284	    FreeResourceByType(ref->dri2_id, dri2DrawableRes, TRUE);
285	    free(ref);
286	}
287    }
288
289    if (!list_is_empty(&pPriv->reference_list))
290	return Success;
291
292    pDraw = pPriv->drawable;
293    if (pDraw->type == DRAWABLE_WINDOW) {
294	pWin = (WindowPtr) pDraw;
295	dixSetPrivate(&pWin->devPrivates, dri2WindowPrivateKey, NULL);
296    } else {
297	pPixmap = (PixmapPtr) pDraw;
298	dixSetPrivate(&pPixmap->devPrivates, dri2PixmapPrivateKey, NULL);
299    }
300
301    if (pPriv->buffers != NULL) {
302	for (i = 0; i < pPriv->bufferCount; i++)
303	    (*ds->DestroyBuffer)(pDraw, pPriv->buffers[i]);
304
305	free(pPriv->buffers);
306    }
307
308    free(pPriv);
309
310    return Success;
311}
312
313static int
314find_attachment(DRI2DrawablePtr pPriv, unsigned attachment)
315{
316    int i;
317
318    if (pPriv->buffers == NULL) {
319	return -1;
320    }
321
322    for (i = 0; i < pPriv->bufferCount; i++) {
323	if ((pPriv->buffers[i] != NULL)
324	    && (pPriv->buffers[i]->attachment == attachment)) {
325	    return i;
326	}
327    }
328
329    return -1;
330}
331
332static Bool
333allocate_or_reuse_buffer(DrawablePtr pDraw, DRI2ScreenPtr ds,
334			 DRI2DrawablePtr pPriv,
335			 unsigned int attachment, unsigned int format,
336			 int dimensions_match, DRI2BufferPtr *buffer)
337{
338    int old_buf = find_attachment(pPriv, attachment);
339
340    if ((old_buf < 0)
341	|| !dimensions_match
342	|| (pPriv->buffers[old_buf]->format != format)) {
343	*buffer = (*ds->CreateBuffer)(pDraw, attachment, format);
344	pPriv->serialNumber = DRI2DrawableSerial(pDraw);
345	return TRUE;
346
347    } else {
348	*buffer = pPriv->buffers[old_buf];
349	pPriv->buffers[old_buf] = NULL;
350	return FALSE;
351    }
352}
353
354static void
355update_dri2_drawable_buffers(DRI2DrawablePtr pPriv, DrawablePtr pDraw,
356			     DRI2BufferPtr *buffers, int *out_count, int *width, int *height)
357{
358    DRI2ScreenPtr   ds = DRI2GetScreen(pDraw->pScreen);
359    int i;
360
361    if (pPriv->buffers != NULL) {
362	for (i = 0; i < pPriv->bufferCount; i++) {
363	    if (pPriv->buffers[i] != NULL) {
364		(*ds->DestroyBuffer)(pDraw, pPriv->buffers[i]);
365	    }
366	}
367
368	free(pPriv->buffers);
369    }
370
371    pPriv->buffers = buffers;
372    pPriv->bufferCount = *out_count;
373    pPriv->width = pDraw->width;
374    pPriv->height = pDraw->height;
375    *width = pPriv->width;
376    *height = pPriv->height;
377}
378
379static DRI2BufferPtr *
380do_get_buffers(DrawablePtr pDraw, int *width, int *height,
381	       unsigned int *attachments, int count, int *out_count,
382	       int has_format)
383{
384    DRI2ScreenPtr   ds = DRI2GetScreen(pDraw->pScreen);
385    DRI2DrawablePtr pPriv = DRI2GetDrawable(pDraw);
386    DRI2BufferPtr  *buffers;
387    int need_real_front = 0;
388    int need_fake_front = 0;
389    int have_fake_front = 0;
390    int front_format = 0;
391    int dimensions_match;
392    int buffers_changed = 0;
393    int i;
394
395    if (!pPriv) {
396	*width = pDraw->width;
397	*height = pDraw->height;
398	*out_count = 0;
399	return NULL;
400    }
401
402    dimensions_match = (pDraw->width == pPriv->width)
403	&& (pDraw->height == pPriv->height)
404	&& (pPriv->serialNumber == DRI2DrawableSerial(pDraw));
405
406    buffers = malloc((count + 1) * sizeof(buffers[0]));
407
408    for (i = 0; i < count; i++) {
409	const unsigned attachment = *(attachments++);
410	const unsigned format = (has_format) ? *(attachments++) : 0;
411
412	if (allocate_or_reuse_buffer(pDraw, ds, pPriv, attachment,
413				     format, dimensions_match,
414				     &buffers[i]))
415		buffers_changed = 1;
416
417	if (buffers[i] == NULL)
418	    goto err_out;
419
420	/* If the drawable is a window and the front-buffer is requested,
421	 * silently add the fake front-buffer to the list of requested
422	 * attachments.  The counting logic in the loop accounts for the case
423	 * where the client requests both the fake and real front-buffer.
424	 */
425	if (attachment == DRI2BufferBackLeft) {
426	    need_real_front++;
427	    front_format = format;
428	}
429
430	if (attachment == DRI2BufferFrontLeft) {
431	    need_real_front--;
432	    front_format = format;
433
434	    if (pDraw->type == DRAWABLE_WINDOW) {
435		need_fake_front++;
436	    }
437	}
438
439	if (pDraw->type == DRAWABLE_WINDOW) {
440	    if (attachment == DRI2BufferFakeFrontLeft) {
441		need_fake_front--;
442		have_fake_front = 1;
443	    }
444	}
445    }
446
447    if (need_real_front > 0) {
448	if (allocate_or_reuse_buffer(pDraw, ds, pPriv, DRI2BufferFrontLeft,
449				     front_format, dimensions_match,
450				     &buffers[i]))
451	    buffers_changed = 1;
452
453	if (buffers[i] == NULL)
454	    goto err_out;
455	i++;
456    }
457
458    if (need_fake_front > 0) {
459	if (allocate_or_reuse_buffer(pDraw, ds, pPriv, DRI2BufferFakeFrontLeft,
460				     front_format, dimensions_match,
461				     &buffers[i]))
462	    buffers_changed = 1;
463
464	if (buffers[i] == NULL)
465	    goto err_out;
466
467	i++;
468	have_fake_front = 1;
469    }
470
471    *out_count = i;
472
473    update_dri2_drawable_buffers(pPriv, pDraw, buffers, out_count, width, height);
474
475    /* If the client is getting a fake front-buffer, pre-fill it with the
476     * contents of the real front-buffer.  This ensures correct operation of
477     * applications that call glXWaitX before calling glDrawBuffer.
478     */
479    if (have_fake_front && buffers_changed) {
480	BoxRec box;
481	RegionRec region;
482
483	box.x1 = 0;
484	box.y1 = 0;
485	box.x2 = pPriv->width;
486	box.y2 = pPriv->height;
487	RegionInit(&region, &box, 0);
488
489	DRI2CopyRegion(pDraw, &region, DRI2BufferFakeFrontLeft,
490		       DRI2BufferFrontLeft);
491    }
492
493    return pPriv->buffers;
494
495err_out:
496
497    *out_count = 0;
498
499    for (i = 0; i < count; i++) {
500	    if (buffers[i] != NULL)
501		    (*ds->DestroyBuffer)(pDraw, buffers[i]);
502    }
503
504    free(buffers);
505    buffers = NULL;
506
507    update_dri2_drawable_buffers(pPriv, pDraw, buffers, out_count, width, height);
508
509    return buffers;
510}
511
512DRI2BufferPtr *
513DRI2GetBuffers(DrawablePtr pDraw, int *width, int *height,
514	       unsigned int *attachments, int count, int *out_count)
515{
516    return do_get_buffers(pDraw, width, height, attachments, count,
517			  out_count, FALSE);
518}
519
520DRI2BufferPtr *
521DRI2GetBuffersWithFormat(DrawablePtr pDraw, int *width, int *height,
522			 unsigned int *attachments, int count, int *out_count)
523{
524    return do_get_buffers(pDraw, width, height, attachments, count,
525			  out_count, TRUE);
526}
527
528static void
529DRI2InvalidateDrawable(DrawablePtr pDraw)
530{
531    DRI2DrawablePtr pPriv = DRI2GetDrawable(pDraw);
532    DRI2DrawableRefPtr ref;
533
534    if (!pPriv)
535        return;
536
537    list_for_each_entry(ref, &pPriv->reference_list, link)
538	ref->invalidate(pDraw, ref->priv);
539}
540
541/*
542 * In the direct rendered case, we throttle the clients that have more
543 * than their share of outstanding swaps (and thus busy buffers) when a
544 * new GetBuffers request is received.  In the AIGLX case, we allow the
545 * client to get the new buffers, but throttle when the next GLX request
546 * comes in (see __glXDRIcontextWait()).
547 */
548Bool
549DRI2ThrottleClient(ClientPtr client, DrawablePtr pDraw)
550{
551    DRI2DrawablePtr pPriv;
552
553    pPriv = DRI2GetDrawable(pDraw);
554    if (pPriv == NULL)
555	return FALSE;
556
557    /* Throttle to swap limit */
558    if ((pPriv->swapsPending >= pPriv->swap_limit) &&
559	!pPriv->blockedClient) {
560	ResetCurrentRequest(client);
561	client->sequence--;
562	IgnoreClient(client);
563	pPriv->blockedClient = client;
564	return TRUE;
565    }
566
567    return FALSE;
568}
569
570static void
571__DRI2BlockClient(ClientPtr client, DRI2DrawablePtr pPriv)
572{
573    if (pPriv->blockedClient == NULL) {
574	IgnoreClient(client);
575	pPriv->blockedClient = client;
576    }
577}
578
579void
580DRI2BlockClient(ClientPtr client, DrawablePtr pDraw)
581{
582    DRI2DrawablePtr pPriv;
583
584    pPriv = DRI2GetDrawable(pDraw);
585    if (pPriv == NULL)
586	return;
587
588    __DRI2BlockClient(client, pPriv);
589    pPriv->blockedOnMsc = TRUE;
590}
591
592int
593DRI2CopyRegion(DrawablePtr pDraw, RegionPtr pRegion,
594	       unsigned int dest, unsigned int src)
595{
596    DRI2ScreenPtr   ds = DRI2GetScreen(pDraw->pScreen);
597    DRI2DrawablePtr pPriv;
598    DRI2BufferPtr   pDestBuffer, pSrcBuffer;
599    int		    i;
600
601    pPriv = DRI2GetDrawable(pDraw);
602    if (pPriv == NULL)
603	return BadDrawable;
604
605    pDestBuffer = NULL;
606    pSrcBuffer = NULL;
607    for (i = 0; i < pPriv->bufferCount; i++)
608    {
609	if (pPriv->buffers[i]->attachment == dest)
610	    pDestBuffer = (DRI2BufferPtr) pPriv->buffers[i];
611	if (pPriv->buffers[i]->attachment == src)
612	    pSrcBuffer = (DRI2BufferPtr) pPriv->buffers[i];
613    }
614    if (pSrcBuffer == NULL || pDestBuffer == NULL)
615	return BadValue;
616
617    (*ds->CopyRegion)(pDraw, pRegion, pDestBuffer, pSrcBuffer);
618
619    return Success;
620}
621
622/* Can this drawable be page flipped? */
623Bool
624DRI2CanFlip(DrawablePtr pDraw)
625{
626    ScreenPtr pScreen = pDraw->pScreen;
627    WindowPtr pWin, pRoot;
628    PixmapPtr pWinPixmap, pRootPixmap;
629
630    if (pDraw->type == DRAWABLE_PIXMAP)
631	return TRUE;
632
633    pRoot = pScreen->root;
634    pRootPixmap = pScreen->GetWindowPixmap(pRoot);
635
636    pWin = (WindowPtr) pDraw;
637    pWinPixmap = pScreen->GetWindowPixmap(pWin);
638    if (pRootPixmap != pWinPixmap)
639	return FALSE;
640    if (!RegionEqual(&pWin->clipList, &pRoot->winSize))
641	return FALSE;
642
643    /* Does the window match the pixmap exactly? */
644    if (pDraw->x != 0 ||
645	pDraw->y != 0 ||
646#ifdef COMPOSITE
647	pDraw->x != pWinPixmap->screen_x ||
648	pDraw->y != pWinPixmap->screen_y ||
649#endif
650	pDraw->width != pWinPixmap->drawable.width ||
651	pDraw->height != pWinPixmap->drawable.height)
652	return FALSE;
653
654    return TRUE;
655}
656
657/* Can we do a pixmap exchange instead of a blit? */
658Bool
659DRI2CanExchange(DrawablePtr pDraw)
660{
661    return FALSE;
662}
663
664void
665DRI2WaitMSCComplete(ClientPtr client, DrawablePtr pDraw, int frame,
666		    unsigned int tv_sec, unsigned int tv_usec)
667{
668    DRI2DrawablePtr pPriv;
669
670    pPriv = DRI2GetDrawable(pDraw);
671    if (pPriv == NULL)
672	return;
673
674    ProcDRI2WaitMSCReply(client, ((CARD64)tv_sec * 1000000) + tv_usec,
675			 frame, pPriv->swap_count);
676
677    if (pPriv->blockedClient)
678	AttendClient(pPriv->blockedClient);
679
680    pPriv->blockedClient = NULL;
681    pPriv->blockedOnMsc = FALSE;
682}
683
684static void
685DRI2WakeClient(ClientPtr client, DrawablePtr pDraw, int frame,
686	       unsigned int tv_sec, unsigned int tv_usec)
687{
688    ScreenPtr	    pScreen = pDraw->pScreen;
689    DRI2DrawablePtr pPriv;
690
691    pPriv = DRI2GetDrawable(pDraw);
692    if (pPriv == NULL) {
693        xf86DrvMsg(pScreen->myNum, X_ERROR,
694		   "[DRI2] %s: bad drawable\n", __func__);
695	return;
696    }
697
698    /*
699     * Swap completed.
700     * Wake the client iff:
701     *   - it was waiting on SBC
702     *   - was blocked due to GLX make current
703     *   - was blocked due to swap throttling
704     *   - is not blocked due to an MSC wait
705     */
706    if (pPriv->target_sbc != -1 &&
707	pPriv->target_sbc <= pPriv->swap_count) {
708	ProcDRI2WaitMSCReply(client, ((CARD64)tv_sec * 1000000) + tv_usec,
709			     frame, pPriv->swap_count);
710	pPriv->target_sbc = -1;
711
712	AttendClient(pPriv->blockedClient);
713	pPriv->blockedClient = NULL;
714    } else if (pPriv->target_sbc == -1 && !pPriv->blockedOnMsc) {
715	if (pPriv->blockedClient) {
716	    AttendClient(pPriv->blockedClient);
717	    pPriv->blockedClient = NULL;
718	}
719    }
720}
721
722void
723DRI2SwapComplete(ClientPtr client, DrawablePtr pDraw, int frame,
724		   unsigned int tv_sec, unsigned int tv_usec, int type,
725		   DRI2SwapEventPtr swap_complete, void *swap_data)
726{
727    ScreenPtr	    pScreen = pDraw->pScreen;
728    DRI2DrawablePtr pPriv;
729    CARD64          ust = 0;
730    BoxRec          box;
731    RegionRec       region;
732
733    pPriv = DRI2GetDrawable(pDraw);
734    if (pPriv == NULL) {
735        xf86DrvMsg(pScreen->myNum, X_ERROR,
736		   "[DRI2] %s: bad drawable\n", __func__);
737	return;
738    }
739
740    pPriv->swapsPending--;
741    pPriv->swap_count++;
742
743    box.x1 = 0;
744    box.y1 = 0;
745    box.x2 = pDraw->width;
746    box.y2 = pDraw->height;
747    RegionInit(&region, &box, 0);
748    DRI2CopyRegion(pDraw, &region, DRI2BufferFakeFrontLeft,
749		   DRI2BufferFrontLeft);
750
751    ust = ((CARD64)tv_sec * 1000000) + tv_usec;
752    if (swap_complete)
753	swap_complete(client, swap_data, type, ust, frame, pPriv->swap_count);
754
755    pPriv->last_swap_msc = frame;
756    pPriv->last_swap_ust = ust;
757
758    DRI2WakeClient(client, pDraw, frame, tv_sec, tv_usec);
759}
760
761Bool
762DRI2WaitSwap(ClientPtr client, DrawablePtr pDrawable)
763{
764    DRI2DrawablePtr pPriv = DRI2GetDrawable(pDrawable);
765
766    /* If we're currently waiting for a swap on this drawable, reset
767     * the request and suspend the client.  We only support one
768     * blocked client per drawable. */
769    if ((pPriv->swapsPending) &&
770	pPriv->blockedClient == NULL) {
771	ResetCurrentRequest(client);
772	client->sequence--;
773	__DRI2BlockClient(client, pPriv);
774	return TRUE;
775    }
776
777    return FALSE;
778}
779
780int
781DRI2SwapBuffers(ClientPtr client, DrawablePtr pDraw, CARD64 target_msc,
782		CARD64 divisor, CARD64 remainder, CARD64 *swap_target,
783		DRI2SwapEventPtr func, void *data)
784{
785    ScreenPtr       pScreen = pDraw->pScreen;
786    DRI2ScreenPtr   ds = DRI2GetScreen(pDraw->pScreen);
787    DRI2DrawablePtr pPriv;
788    DRI2BufferPtr   pDestBuffer = NULL, pSrcBuffer = NULL;
789    int             ret, i;
790    CARD64          ust, current_msc;
791
792    pPriv = DRI2GetDrawable(pDraw);
793    if (pPriv == NULL) {
794        xf86DrvMsg(pScreen->myNum, X_ERROR,
795		   "[DRI2] %s: bad drawable\n", __func__);
796	return BadDrawable;
797    }
798
799    for (i = 0; i < pPriv->bufferCount; i++) {
800	if (pPriv->buffers[i]->attachment == DRI2BufferFrontLeft)
801	    pDestBuffer = (DRI2BufferPtr) pPriv->buffers[i];
802	if (pPriv->buffers[i]->attachment == DRI2BufferBackLeft)
803	    pSrcBuffer = (DRI2BufferPtr) pPriv->buffers[i];
804    }
805    if (pSrcBuffer == NULL || pDestBuffer == NULL) {
806        xf86DrvMsg(pScreen->myNum, X_ERROR,
807		   "[DRI2] %s: drawable has no back or front?\n", __func__);
808	return BadDrawable;
809    }
810
811    /* Old DDX or no swap interval, just blit */
812    if (!ds->ScheduleSwap || !pPriv->swap_interval) {
813	BoxRec box;
814	RegionRec region;
815
816	box.x1 = 0;
817	box.y1 = 0;
818	box.x2 = pDraw->width;
819	box.y2 = pDraw->height;
820	RegionInit(&region, &box, 0);
821
822	pPriv->swapsPending++;
823
824	(*ds->CopyRegion)(pDraw, &region, pDestBuffer, pSrcBuffer);
825	DRI2SwapComplete(client, pDraw, target_msc, 0, 0, DRI2_BLIT_COMPLETE,
826			 func, data);
827	return Success;
828    }
829
830    /*
831     * In the simple glXSwapBuffers case, all params will be 0, and we just
832     * need to schedule a swap for the last swap target + the swap interval.
833     */
834    if (target_msc == 0 && divisor == 0 && remainder == 0) {
835	/* If the current vblank count of the drawable's crtc is lower
836	 * than the count stored in last_swap_target from a previous swap
837	 * then reinitialize last_swap_target to the current crtc's msc,
838	 * otherwise the swap will hang. This will happen if the drawable
839	 * is moved to a crtc with a lower refresh rate, or a crtc that just
840	 * got enabled.
841	 */
842	if (ds->GetMSC) {
843	    if (!(*ds->GetMSC)(pDraw, &ust, &current_msc))
844		pPriv->last_swap_target = 0;
845
846	    if (current_msc < pPriv->last_swap_target)
847		pPriv->last_swap_target = current_msc;
848
849	}
850
851	/*
852	 * Swap target for this swap is last swap target + swap interval since
853	 * we have to account for the current swap count, interval, and the
854	 * number of pending swaps.
855	 */
856	*swap_target = pPriv->last_swap_target + pPriv->swap_interval;
857
858    } else {
859	/* glXSwapBuffersMscOML could have a 0 target_msc, honor it */
860	*swap_target = target_msc;
861    }
862
863    pPriv->swapsPending++;
864    ret = (*ds->ScheduleSwap)(client, pDraw, pDestBuffer, pSrcBuffer,
865			      swap_target, divisor, remainder, func, data);
866    if (!ret) {
867	pPriv->swapsPending--; /* didn't schedule */
868        xf86DrvMsg(pScreen->myNum, X_ERROR,
869		   "[DRI2] %s: driver failed to schedule swap\n", __func__);
870	return BadDrawable;
871    }
872
873    pPriv->last_swap_target = *swap_target;
874
875    /* According to spec, return expected swapbuffers count SBC after this swap
876     * will complete.
877     */
878    *swap_target = pPriv->swap_count + pPriv->swapsPending;
879
880    DRI2InvalidateDrawable(pDraw);
881
882    return Success;
883}
884
885void
886DRI2SwapInterval(DrawablePtr pDrawable, int interval)
887{
888    ScreenPtr       pScreen = pDrawable->pScreen;
889    DRI2DrawablePtr pPriv = DRI2GetDrawable(pDrawable);
890
891    if (pPriv == NULL) {
892        xf86DrvMsg(pScreen->myNum, X_ERROR,
893		   "[DRI2] %s: bad drawable\n", __func__);
894	return;
895    }
896
897    /* fixme: check against arbitrary max? */
898    pPriv->swap_interval = interval;
899}
900
901int
902DRI2GetMSC(DrawablePtr pDraw, CARD64 *ust, CARD64 *msc, CARD64 *sbc)
903{
904    ScreenPtr pScreen = pDraw->pScreen;
905    DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen);
906    DRI2DrawablePtr pPriv;
907    Bool ret;
908
909    pPriv = DRI2GetDrawable(pDraw);
910    if (pPriv == NULL) {
911        xf86DrvMsg(pScreen->myNum, X_ERROR,
912		   "[DRI2] %s: bad drawable\n", __func__);
913	return BadDrawable;
914    }
915
916    if (!ds->GetMSC) {
917	*ust = 0;
918	*msc = 0;
919	*sbc = pPriv->swap_count;
920	return Success;
921    }
922
923    /*
924     * Spec needs to be updated to include unmapped or redirected
925     * drawables
926     */
927
928    ret = (*ds->GetMSC)(pDraw, ust, msc);
929    if (!ret)
930	return BadDrawable;
931
932    *sbc = pPriv->swap_count;
933
934    return Success;
935}
936
937int
938DRI2WaitMSC(ClientPtr client, DrawablePtr pDraw, CARD64 target_msc,
939	    CARD64 divisor, CARD64 remainder)
940{
941    DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen);
942    DRI2DrawablePtr pPriv;
943    Bool ret;
944
945    pPriv = DRI2GetDrawable(pDraw);
946    if (pPriv == NULL)
947	return BadDrawable;
948
949    /* Old DDX just completes immediately */
950    if (!ds->ScheduleWaitMSC) {
951	DRI2WaitMSCComplete(client, pDraw, target_msc, 0, 0);
952
953	return Success;
954    }
955
956    ret = (*ds->ScheduleWaitMSC)(client, pDraw, target_msc, divisor, remainder);
957    if (!ret)
958	return BadDrawable;
959
960    return Success;
961}
962
963int
964DRI2WaitSBC(ClientPtr client, DrawablePtr pDraw, CARD64 target_sbc)
965{
966    DRI2DrawablePtr pPriv;
967
968    pPriv = DRI2GetDrawable(pDraw);
969    if (pPriv == NULL)
970	return BadDrawable;
971
972    /* target_sbc == 0 means to block until all pending swaps are
973     * finished. Recalculate target_sbc to get that behaviour.
974     */
975    if (target_sbc == 0)
976        target_sbc = pPriv->swap_count + pPriv->swapsPending;
977
978    /* If current swap count already >= target_sbc, reply and
979     * return immediately with (ust, msc, sbc) triplet of
980     * most recent completed swap.
981     */
982    if (pPriv->swap_count >= target_sbc) {
983        ProcDRI2WaitMSCReply(client, pPriv->last_swap_ust,
984                             pPriv->last_swap_msc, pPriv->swap_count);
985        return Success;
986    }
987
988    pPriv->target_sbc = target_sbc;
989    __DRI2BlockClient(client, pPriv);
990
991    return Success;
992}
993
994Bool
995DRI2HasSwapControl(ScreenPtr pScreen)
996{
997    DRI2ScreenPtr ds = DRI2GetScreen(pScreen);
998
999    return ds->ScheduleSwap && ds->GetMSC;
1000}
1001
1002Bool
1003DRI2Connect(ScreenPtr pScreen, unsigned int driverType, int *fd,
1004	    const char **driverName, const char **deviceName)
1005{
1006    DRI2ScreenPtr ds;
1007
1008    if (!dixPrivateKeyRegistered(dri2ScreenPrivateKey))
1009	return FALSE;
1010
1011    ds = DRI2GetScreen(pScreen);
1012    if (ds == NULL || driverType >= ds->numDrivers ||
1013	    !ds->driverNames[driverType])
1014	return FALSE;
1015
1016    *fd = ds->fd;
1017    *driverName = ds->driverNames[driverType];
1018    *deviceName = ds->deviceName;
1019
1020    return TRUE;
1021}
1022
1023Bool
1024DRI2Authenticate(ScreenPtr pScreen, uint32_t magic)
1025{
1026    DRI2ScreenPtr ds = DRI2GetScreen(pScreen);
1027
1028    if (ds == NULL || (*ds->AuthMagic)(ds->fd, magic))
1029        return FALSE;
1030
1031    return TRUE;
1032}
1033
1034static int
1035DRI2ConfigNotify(WindowPtr pWin, int x, int y, int w, int h, int bw,
1036		 WindowPtr pSib)
1037{
1038    DrawablePtr pDraw = (DrawablePtr)pWin;
1039    ScreenPtr pScreen = pDraw->pScreen;
1040    DRI2ScreenPtr ds = DRI2GetScreen(pScreen);
1041    DRI2DrawablePtr dd = DRI2GetDrawable(pDraw);
1042    int ret;
1043
1044    if (ds->ConfigNotify) {
1045	pScreen->ConfigNotify = ds->ConfigNotify;
1046
1047	ret = (*pScreen->ConfigNotify)(pWin, x, y, w, h, bw, pSib);
1048
1049	ds->ConfigNotify = pScreen->ConfigNotify;
1050	pScreen->ConfigNotify = DRI2ConfigNotify;
1051	if (ret)
1052	    return ret;
1053    }
1054
1055    if (!dd || (dd->width == w && dd->height == h))
1056	return Success;
1057
1058    DRI2InvalidateDrawable(pDraw);
1059    return Success;
1060}
1061
1062Bool
1063DRI2ScreenInit(ScreenPtr pScreen, DRI2InfoPtr info)
1064{
1065    DRI2ScreenPtr ds;
1066    const char* driverTypeNames[] = {
1067	"DRI", /* DRI2DriverDRI */
1068	"VDPAU", /* DRI2DriverVDPAU */
1069    };
1070    unsigned int i;
1071    CARD8 cur_minor;
1072
1073    if (info->version < 3)
1074	return FALSE;
1075
1076    if (!xf86VGAarbiterAllowDRI(pScreen)) {
1077        xf86DrvMsg(pScreen->myNum, X_WARNING,
1078                  "[DRI2] Direct rendering is not supported when VGA arb is necessary for the device\n");
1079        return FALSE;
1080    }
1081
1082    if (!dixRegisterPrivateKey(&dri2ScreenPrivateKeyRec, PRIVATE_SCREEN, 0))
1083	return FALSE;
1084
1085    if (!dixRegisterPrivateKey(&dri2WindowPrivateKeyRec, PRIVATE_WINDOW, 0))
1086	return FALSE;
1087
1088    if (!dixRegisterPrivateKey(&dri2PixmapPrivateKeyRec, PRIVATE_PIXMAP, 0))
1089	return FALSE;
1090
1091    ds = calloc(1, sizeof *ds);
1092    if (!ds)
1093	return FALSE;
1094
1095    ds->screen         = pScreen;
1096    ds->fd	       = info->fd;
1097    ds->deviceName     = info->deviceName;
1098    dri2_major         = 1;
1099
1100    ds->CreateBuffer   = info->CreateBuffer;
1101    ds->DestroyBuffer  = info->DestroyBuffer;
1102    ds->CopyRegion     = info->CopyRegion;
1103
1104    if (info->version >= 4) {
1105	ds->ScheduleSwap = info->ScheduleSwap;
1106	ds->ScheduleWaitMSC = info->ScheduleWaitMSC;
1107	ds->GetMSC = info->GetMSC;
1108	cur_minor = 3;
1109    } else {
1110	cur_minor = 1;
1111    }
1112
1113    if (info->version >= 5) {
1114        ds->AuthMagic = info->AuthMagic;
1115    }
1116
1117    /*
1118     * if the driver doesn't provide an AuthMagic function or the info struct
1119     * version is too low, it relies on the old method (using libdrm) or fail
1120     */
1121    if (!ds->AuthMagic)
1122#ifdef WITH_LIBDRM
1123        ds->AuthMagic = drmAuthMagic;
1124#else
1125        goto err_out;
1126#endif
1127
1128    /* Initialize minor if needed and set to minimum provied by DDX */
1129    if (!dri2_minor || dri2_minor > cur_minor)
1130	dri2_minor = cur_minor;
1131
1132    if (info->version == 3 || info->numDrivers == 0) {
1133	/* Driver too old: use the old-style driverName field */
1134	ds->numDrivers = 1;
1135	ds->driverNames = malloc(sizeof(*ds->driverNames));
1136	if (!ds->driverNames)
1137	    goto err_out;
1138	ds->driverNames[0] = info->driverName;
1139    } else {
1140	ds->numDrivers = info->numDrivers;
1141	ds->driverNames = malloc(info->numDrivers * sizeof(*ds->driverNames));
1142	if (!ds->driverNames)
1143		goto err_out;
1144	memcpy(ds->driverNames, info->driverNames,
1145	       info->numDrivers * sizeof(*ds->driverNames));
1146    }
1147
1148    dixSetPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey, ds);
1149
1150    ds->ConfigNotify = pScreen->ConfigNotify;
1151    pScreen->ConfigNotify = DRI2ConfigNotify;
1152
1153    xf86DrvMsg(pScreen->myNum, X_INFO, "[DRI2] Setup complete\n");
1154    for (i = 0; i < sizeof(driverTypeNames) / sizeof(driverTypeNames[0]); i++) {
1155	if (i < ds->numDrivers && ds->driverNames[i]) {
1156	    xf86DrvMsg(pScreen->myNum, X_INFO, "[DRI2]   %s driver: %s\n",
1157		       driverTypeNames[i], ds->driverNames[i]);
1158	}
1159    }
1160
1161    return TRUE;
1162
1163err_out:
1164    xf86DrvMsg(pScreen->myNum, X_WARNING,
1165            "[DRI2] Initialization failed for info version %d.\n", info->version);
1166    free(ds);
1167    return FALSE;
1168}
1169
1170void
1171DRI2CloseScreen(ScreenPtr pScreen)
1172{
1173    DRI2ScreenPtr ds = DRI2GetScreen(pScreen);
1174
1175    free(ds->driverNames);
1176    free(ds);
1177    dixSetPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey, NULL);
1178}
1179
1180extern ExtensionModule dri2ExtensionModule;
1181
1182static pointer
1183DRI2Setup(pointer module, pointer opts, int *errmaj, int *errmin)
1184{
1185    static Bool setupDone = FALSE;
1186
1187    dri2DrawableRes = CreateNewResourceType(DRI2DrawableGone, "DRI2Drawable");
1188
1189    if (!setupDone)
1190    {
1191	setupDone = TRUE;
1192	LoadExtension(&dri2ExtensionModule, FALSE);
1193    }
1194    else
1195    {
1196	if (errmaj)
1197	    *errmaj = LDR_ONCEONLY;
1198    }
1199
1200    return (pointer) 1;
1201}
1202
1203static XF86ModuleVersionInfo DRI2VersRec =
1204{
1205    "dri2",
1206    MODULEVENDORSTRING,
1207    MODINFOSTRING1,
1208    MODINFOSTRING2,
1209    XORG_VERSION_CURRENT,
1210    1, 2, 0,
1211    ABI_CLASS_EXTENSION,
1212    ABI_EXTENSION_VERSION,
1213    MOD_CLASS_NONE,
1214    { 0, 0, 0, 0 }
1215};
1216
1217_X_EXPORT XF86ModuleData dri2ModuleData = { &DRI2VersRec, DRI2Setup, NULL };
1218
1219void
1220DRI2Version(int *major, int *minor)
1221{
1222    if (major != NULL)
1223	*major = DRI2VersRec.majorversion;
1224
1225    if (minor != NULL)
1226	*minor = DRI2VersRec.minorversion;
1227}
1228