dri2.c revision b1d344b3
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 <xf86drm.h>
38#include "xf86Module.h"
39#include "scrnintstr.h"
40#include "windowstr.h"
41#include "dri2.h"
42
43#include "xf86.h"
44
45static int dri2ScreenPrivateKeyIndex;
46static DevPrivateKey dri2ScreenPrivateKey = &dri2ScreenPrivateKeyIndex;
47static int dri2WindowPrivateKeyIndex;
48static DevPrivateKey dri2WindowPrivateKey = &dri2WindowPrivateKeyIndex;
49static int dri2PixmapPrivateKeyIndex;
50static DevPrivateKey dri2PixmapPrivateKey = &dri2PixmapPrivateKeyIndex;
51
52typedef struct _DRI2Drawable {
53    unsigned int	 refCount;
54    int			 width;
55    int			 height;
56    DRI2Buffer2Ptr	*buffers;
57    int			 bufferCount;
58    unsigned int	 pendingSequence;
59} DRI2DrawableRec, *DRI2DrawablePtr;
60
61typedef struct _DRI2Screen {
62    const char			*driverName;
63    const char			*deviceName;
64    int				 fd;
65    unsigned int		 lastSequence;
66
67    DRI2CreateBuffersProcPtr	 CreateBuffers;
68    DRI2DestroyBuffersProcPtr	 DestroyBuffers;
69
70    DRI2CreateBufferProcPtr	 CreateBuffer;
71    DRI2DestroyBufferProcPtr	 DestroyBuffer;
72    DRI2CopyRegionProcPtr	 CopyRegion;
73
74    HandleExposuresProcPtr       HandleExposures;
75} DRI2ScreenRec, *DRI2ScreenPtr;
76
77static DRI2ScreenPtr
78DRI2GetScreen(ScreenPtr pScreen)
79{
80    return dixLookupPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey);
81}
82
83static DRI2DrawablePtr
84DRI2GetDrawable(DrawablePtr pDraw)
85{
86    WindowPtr		  pWin;
87    PixmapPtr		  pPixmap;
88
89    if (pDraw->type == DRAWABLE_WINDOW)
90    {
91	pWin = (WindowPtr) pDraw;
92	return dixLookupPrivate(&pWin->devPrivates, dri2WindowPrivateKey);
93    }
94    else
95    {
96	pPixmap = (PixmapPtr) pDraw;
97	return dixLookupPrivate(&pPixmap->devPrivates, dri2PixmapPrivateKey);
98    }
99}
100
101int
102DRI2CreateDrawable(DrawablePtr pDraw)
103{
104    WindowPtr	    pWin;
105    PixmapPtr	    pPixmap;
106    DRI2DrawablePtr pPriv;
107
108    pPriv = DRI2GetDrawable(pDraw);
109    if (pPriv != NULL)
110    {
111	pPriv->refCount++;
112	return Success;
113    }
114
115    pPriv = xalloc(sizeof *pPriv);
116    if (pPriv == NULL)
117	return BadAlloc;
118
119    pPriv->refCount = 1;
120    pPriv->width = pDraw->width;
121    pPriv->height = pDraw->height;
122    pPriv->buffers = NULL;
123    pPriv->bufferCount = 0;
124
125    if (pDraw->type == DRAWABLE_WINDOW)
126    {
127	pWin = (WindowPtr) pDraw;
128	dixSetPrivate(&pWin->devPrivates, dri2WindowPrivateKey, pPriv);
129    }
130    else
131    {
132	pPixmap = (PixmapPtr) pDraw;
133	dixSetPrivate(&pPixmap->devPrivates, dri2PixmapPrivateKey, pPriv);
134    }
135
136    return Success;
137}
138
139static int
140find_attachment(DRI2DrawablePtr pPriv, unsigned attachment)
141{
142    int i;
143
144    if (pPriv->buffers == NULL) {
145	return -1;
146    }
147
148    for (i = 0; i < pPriv->bufferCount; i++) {
149	if ((pPriv->buffers[i] != NULL)
150	    && (pPriv->buffers[i]->attachment == attachment)) {
151	    return i;
152	}
153    }
154
155    return -1;
156}
157
158static DRI2Buffer2Ptr
159allocate_or_reuse_buffer(DrawablePtr pDraw, DRI2ScreenPtr ds,
160			 DRI2DrawablePtr pPriv,
161			 unsigned int attachment, unsigned int format,
162			 int dimensions_match)
163{
164    DRI2Buffer2Ptr buffer;
165    int old_buf;
166
167    old_buf = find_attachment(pPriv, attachment);
168
169    if ((old_buf < 0)
170	|| !dimensions_match
171	|| (pPriv->buffers[old_buf]->format != format)) {
172	buffer = (*ds->CreateBuffer)(pDraw, attachment, format);
173    } else {
174	buffer = pPriv->buffers[old_buf];
175	pPriv->buffers[old_buf] = NULL;
176    }
177
178    return buffer;
179}
180
181static DRI2Buffer2Ptr *
182do_get_buffers(DrawablePtr pDraw, int *width, int *height,
183	       unsigned int *attachments, int count, int *out_count,
184	       int has_format)
185{
186    DRI2ScreenPtr   ds = DRI2GetScreen(pDraw->pScreen);
187    DRI2DrawablePtr pPriv = DRI2GetDrawable(pDraw);
188    DRI2Buffer2Ptr  *buffers;
189    int need_real_front = 0;
190    int need_fake_front = 0;
191    int have_fake_front = 0;
192    int front_format = 0;
193    int dimensions_match;
194    int i;
195
196    if (!pPriv) {
197	*width = pDraw->width;
198	*height = pDraw->height;
199	*out_count = 0;
200	return NULL;
201    }
202
203    dimensions_match = (pDraw->width == pPriv->width)
204	&& (pDraw->height == pPriv->height);
205
206    buffers = xalloc((count + 1) * sizeof(buffers[0]));
207
208    if (ds->CreateBuffer) {
209	/* Version 2 API with CreateBuffer */
210	for (i = 0; i < count; i++) {
211	    const unsigned attachment = *(attachments++);
212	    const unsigned format = (has_format) ? *(attachments++) : 0;
213
214	    buffers[i] = allocate_or_reuse_buffer(pDraw, ds, pPriv, attachment,
215						  format, dimensions_match);
216
217	    /* If the drawable is a window and the front-buffer is requested,
218	     * silently add the fake front-buffer to the list of requested
219	     * attachments.  The counting logic in the loop accounts for the case
220	     * where the client requests both the fake and real front-buffer.
221	     */
222	    if (attachment == DRI2BufferBackLeft) {
223		need_real_front++;
224		front_format = format;
225	    }
226
227	    if (attachment == DRI2BufferFrontLeft) {
228		need_real_front--;
229		front_format = format;
230
231		if (pDraw->type == DRAWABLE_WINDOW) {
232		    need_fake_front++;
233		}
234	    }
235
236	    if (pDraw->type == DRAWABLE_WINDOW) {
237		if (attachment == DRI2BufferFakeFrontLeft) {
238		    need_fake_front--;
239		    have_fake_front = 1;
240		}
241	    }
242	}
243
244	if (need_real_front > 0) {
245	    buffers[i++] = allocate_or_reuse_buffer(pDraw, ds, pPriv,
246						    DRI2BufferFrontLeft,
247						    front_format, dimensions_match);
248	}
249
250	if (need_fake_front > 0) {
251	    buffers[i++] = allocate_or_reuse_buffer(pDraw, ds, pPriv,
252						    DRI2BufferFakeFrontLeft,
253						    front_format, dimensions_match);
254	    have_fake_front = 1;
255	}
256
257	*out_count = i;
258
259
260	if (pPriv->buffers != NULL) {
261	    for (i = 0; i < pPriv->bufferCount; i++) {
262		if (pPriv->buffers[i] != NULL) {
263		    (*ds->DestroyBuffer)(pDraw, pPriv->buffers[i]);
264		}
265	    }
266
267	    xfree(pPriv->buffers);
268	}
269    } else {
270	DRI2BufferPtr	buffers1;
271	unsigned int	temp_buf[32];
272	unsigned int	*temp = temp_buf;
273	int		i;
274	int		buffers_match = 1;
275
276	/* Version 1 API with CreateBuffers */
277
278	if ((count + 1) > 32) {
279	    temp = xalloc((count + 1) * sizeof(temp[0]));
280	}
281
282	for (i = 0; i < count; i++) {
283	    const unsigned attachment = *(attachments++);
284
285	    /* Version 1 doesn't deal with the format at all */
286	    if (has_format)
287		attachments++;
288
289	    /*
290	     * Make sure the client also gets the front buffer when
291	     * it asks for a back buffer
292	     */
293	    if (attachment == DRI2BufferBackLeft)
294		need_real_front++;
295
296	    /*
297	     * If the drawable is a window and the front-buffer is requested,
298	     * silently add the fake front-buffer to the list of requested
299	     * attachments.  The counting logic in the loop accounts for the
300	     * case where the client requests both the fake and real
301	     * front-buffer.
302	     */
303	    if (attachment == DRI2BufferFrontLeft) {
304		need_real_front--;
305		if (pDraw->type == DRAWABLE_WINDOW)
306		    need_fake_front++;
307	    }
308	    if (pDraw->type == DRAWABLE_WINDOW &&
309		attachment == DRI2BufferFakeFrontLeft)
310	    {
311		need_fake_front--;
312		have_fake_front = 1;
313	    }
314
315	    temp[i] = attachment;
316	}
317
318	if (need_real_front > 0)
319	    temp[count++] = DRI2BufferFrontLeft;
320
321	if (need_fake_front > 0) {
322	    temp[count++] = DRI2BufferFakeFrontLeft;
323	    have_fake_front = 1;
324	}
325
326	if (count != pPriv->bufferCount)
327	    buffers_match = 0;
328	else {
329	    for (i = 0; i < count; i++)
330		if (pPriv->buffers[i]->attachment != temp[i]) {
331		    buffers_match = 0;
332		    break;
333		}
334	}
335	if (pPriv->buffers == NULL || !dimensions_match || !buffers_match)
336	{
337            buffers1 = (*ds->CreateBuffers)(pDraw, temp, count);
338	    if (pPriv->buffers != NULL)
339		(*ds->DestroyBuffers)(pDraw, (DRI2BufferPtr) pPriv->buffers[0],
340				      pPriv->bufferCount);
341	}
342	else
343	    buffers1 = (DRI2BufferPtr) pPriv->buffers[0];
344
345        for (i = 0; i < count; i++)
346	    buffers[i] = (DRI2Buffer2Ptr) &buffers1[i];
347
348        *out_count = count;
349
350	if (pPriv->buffers)
351	    xfree (pPriv->buffers);
352
353	if (temp != temp_buf) {
354	    xfree(temp);
355	}
356    }
357
358    pPriv->buffers = buffers;
359    pPriv->bufferCount = *out_count;
360    pPriv->width = pDraw->width;
361    pPriv->height = pDraw->height;
362    *width = pPriv->width;
363    *height = pPriv->height;
364
365
366    /* If the client is getting a fake front-buffer, pre-fill it with the
367     * contents of the real front-buffer.  This ensures correct operation of
368     * applications that call glXWaitX before calling glDrawBuffer.
369     */
370    if (have_fake_front) {
371	BoxRec box;
372	RegionRec region;
373
374	box.x1 = 0;
375	box.y1 = 0;
376	box.x2 = pPriv->width;
377	box.y2 = pPriv->height;
378	REGION_INIT(pDraw->pScreen, &region, &box, 0);
379
380	DRI2CopyRegion(pDraw, &region, DRI2BufferFakeFrontLeft,
381		       DRI2BufferFrontLeft);
382    }
383
384    return pPriv->buffers;
385}
386
387DRI2Buffer2Ptr *
388DRI2GetBuffers(DrawablePtr pDraw, int *width, int *height,
389	       unsigned int *attachments, int count, int *out_count)
390{
391    return do_get_buffers(pDraw, width, height, attachments, count,
392			  out_count, FALSE);
393}
394
395DRI2Buffer2Ptr *
396DRI2GetBuffersWithFormat(DrawablePtr pDraw, int *width, int *height,
397			 unsigned int *attachments, int count, int *out_count)
398{
399    return do_get_buffers(pDraw, width, height, attachments, count,
400			  out_count, TRUE);
401}
402
403int
404DRI2CopyRegion(DrawablePtr pDraw, RegionPtr pRegion,
405	       unsigned int dest, unsigned int src)
406{
407    DRI2ScreenPtr   ds = DRI2GetScreen(pDraw->pScreen);
408    DRI2DrawablePtr pPriv;
409    DRI2BufferPtr   pDestBuffer, pSrcBuffer;
410    int		    i;
411
412    pPriv = DRI2GetDrawable(pDraw);
413    if (pPriv == NULL)
414	return BadDrawable;
415
416    pDestBuffer = NULL;
417    pSrcBuffer = NULL;
418    for (i = 0; i < pPriv->bufferCount; i++)
419    {
420	if (pPriv->buffers[i]->attachment == dest)
421	    pDestBuffer = (DRI2BufferPtr) pPriv->buffers[i];
422	if (pPriv->buffers[i]->attachment == src)
423	    pSrcBuffer = (DRI2BufferPtr) pPriv->buffers[i];
424    }
425    if (pSrcBuffer == NULL || pDestBuffer == NULL)
426	return BadValue;
427
428    (*ds->CopyRegion)(pDraw, pRegion, pDestBuffer, pSrcBuffer);
429
430    return Success;
431}
432
433void
434DRI2DestroyDrawable(DrawablePtr pDraw)
435{
436    DRI2ScreenPtr   ds = DRI2GetScreen(pDraw->pScreen);
437    DRI2DrawablePtr pPriv;
438    WindowPtr  	    pWin;
439    PixmapPtr	    pPixmap;
440
441    pPriv = DRI2GetDrawable(pDraw);
442    if (pPriv == NULL)
443	return;
444
445    pPriv->refCount--;
446    if (pPriv->refCount > 0)
447	return;
448
449    if (pPriv->buffers != NULL) {
450	int i;
451
452	if (ds->DestroyBuffer) {
453	    for (i = 0; i < pPriv->bufferCount; i++) {
454		(*ds->DestroyBuffer)(pDraw, pPriv->buffers[i]);
455	    }
456	} else {
457	    (*ds->DestroyBuffers)(pDraw, (DRI2BufferPtr) pPriv->buffers[0],
458				  pPriv->bufferCount);
459	}
460
461	xfree(pPriv->buffers);
462    }
463
464    xfree(pPriv);
465
466    if (pDraw->type == DRAWABLE_WINDOW)
467    {
468	pWin = (WindowPtr) pDraw;
469	dixSetPrivate(&pWin->devPrivates, dri2WindowPrivateKey, NULL);
470    }
471    else
472    {
473	pPixmap = (PixmapPtr) pDraw;
474	dixSetPrivate(&pPixmap->devPrivates, dri2PixmapPrivateKey, NULL);
475    }
476}
477
478Bool
479DRI2Connect(ScreenPtr pScreen, unsigned int driverType, int *fd,
480	    const char **driverName, const char **deviceName)
481{
482    DRI2ScreenPtr ds = DRI2GetScreen(pScreen);
483
484    if (ds == NULL)
485	return FALSE;
486
487    if (driverType != DRI2DriverDRI)
488	return BadValue;
489
490    *fd = ds->fd;
491    *driverName = ds->driverName;
492    *deviceName = ds->deviceName;
493
494    return TRUE;
495}
496
497Bool
498DRI2Authenticate(ScreenPtr pScreen, drm_magic_t magic)
499{
500    DRI2ScreenPtr ds = DRI2GetScreen(pScreen);
501
502    if (ds == NULL || drmAuthMagic(ds->fd, magic))
503	return FALSE;
504
505    return TRUE;
506}
507
508Bool
509DRI2ScreenInit(ScreenPtr pScreen, DRI2InfoPtr info)
510{
511    DRI2ScreenPtr ds;
512
513    ds = xalloc(sizeof *ds);
514    if (!ds)
515	return FALSE;
516
517    ds->fd	       = info->fd;
518    ds->driverName     = info->driverName;
519    ds->deviceName     = info->deviceName;
520
521    /* Prefer the new one-at-a-time buffer API */
522    if (info->version >= 2 && info->CreateBuffer && info->DestroyBuffer) {
523	ds->CreateBuffer   = info->CreateBuffer;
524	ds->DestroyBuffer  = info->DestroyBuffer;
525	ds->CreateBuffers  = NULL;
526	ds->DestroyBuffers = NULL;
527    } else if (info->CreateBuffers && info->DestroyBuffers) {
528	xf86DrvMsg(pScreen->myNum, X_WARNING,
529		   "[DRI2] Version 1 API (broken front buffer rendering)\n");
530	ds->CreateBuffer   = NULL;
531	ds->DestroyBuffer  = NULL;
532	ds->CreateBuffers  = info->CreateBuffers;
533	ds->DestroyBuffers = info->DestroyBuffers;
534    } else {
535	xf86DrvMsg(pScreen->myNum, X_ERROR,
536		   "[DRI2] Missing buffer management functions\n");
537	xfree(ds);
538	return FALSE;
539    }
540
541    if (!info->CopyRegion) {
542	xf86DrvMsg(pScreen->myNum, X_ERROR,
543		   "[DRI2] Missing copy region function\n");
544	xfree(ds);
545	return FALSE;
546    }
547    ds->CopyRegion     = info->CopyRegion;
548
549    dixSetPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey, ds);
550
551    xf86DrvMsg(pScreen->myNum, X_INFO, "[DRI2] Setup complete\n");
552
553    return TRUE;
554}
555
556void
557DRI2CloseScreen(ScreenPtr pScreen)
558{
559    DRI2ScreenPtr ds = DRI2GetScreen(pScreen);
560
561    xfree(ds);
562    dixSetPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey, NULL);
563}
564
565extern ExtensionModule dri2ExtensionModule;
566
567static pointer
568DRI2Setup(pointer module, pointer opts, int *errmaj, int *errmin)
569{
570    static Bool setupDone = FALSE;
571
572    if (!setupDone)
573    {
574	setupDone = TRUE;
575	LoadExtension(&dri2ExtensionModule, FALSE);
576    }
577    else
578    {
579	if (errmaj)
580	    *errmaj = LDR_ONCEONLY;
581    }
582
583    return (pointer) 1;
584}
585
586static XF86ModuleVersionInfo DRI2VersRec =
587{
588    "dri2",
589    MODULEVENDORSTRING,
590    MODINFOSTRING1,
591    MODINFOSTRING2,
592    XORG_VERSION_CURRENT,
593    1, 1, 0,
594    ABI_CLASS_EXTENSION,
595    ABI_EXTENSION_VERSION,
596    MOD_CLASS_NONE,
597    { 0, 0, 0, 0 }
598};
599
600_X_EXPORT XF86ModuleData dri2ModuleData = { &DRI2VersRec, DRI2Setup, NULL };
601
602void
603DRI2Version(int *major, int *minor)
604{
605    if (major != NULL)
606	*major = DRI2VersRec.majorversion;
607
608    if (minor != NULL)
609	*minor = DRI2VersRec.minorversion;
610}
611