dri2.c revision 52397711
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    const int dimensions_match = (pDraw->width == pPriv->width)
194	&& (pDraw->height == pPriv->height);
195    int i;
196
197
198    buffers = xalloc((count + 1) * sizeof(buffers[0]));
199
200    if (ds->CreateBuffer) {
201	/* Version 2 API with CreateBuffer */
202	for (i = 0; i < count; i++) {
203	    const unsigned attachment = *(attachments++);
204	    const unsigned format = (has_format) ? *(attachments++) : 0;
205
206	    buffers[i] = allocate_or_reuse_buffer(pDraw, ds, pPriv, attachment,
207						  format, dimensions_match);
208
209	    /* If the drawable is a window and the front-buffer is requested,
210	     * silently add the fake front-buffer to the list of requested
211	     * attachments.  The counting logic in the loop accounts for the case
212	     * where the client requests both the fake and real front-buffer.
213	     */
214	    if (attachment == DRI2BufferBackLeft) {
215		need_real_front++;
216		front_format = format;
217	    }
218
219	    if (attachment == DRI2BufferFrontLeft) {
220		need_real_front--;
221		front_format = format;
222
223		if (pDraw->type == DRAWABLE_WINDOW) {
224		    need_fake_front++;
225		}
226	    }
227
228	    if (pDraw->type == DRAWABLE_WINDOW) {
229		if (attachment == DRI2BufferFakeFrontLeft) {
230		    need_fake_front--;
231		    have_fake_front = 1;
232		}
233	    }
234	}
235
236	if (need_real_front > 0) {
237	    buffers[i++] = allocate_or_reuse_buffer(pDraw, ds, pPriv,
238						    DRI2BufferFrontLeft,
239						    front_format, dimensions_match);
240	}
241
242	if (need_fake_front > 0) {
243	    buffers[i++] = allocate_or_reuse_buffer(pDraw, ds, pPriv,
244						    DRI2BufferFakeFrontLeft,
245						    front_format, dimensions_match);
246	    have_fake_front = 1;
247	}
248
249	*out_count = i;
250
251
252	if (pPriv->buffers != NULL) {
253	    for (i = 0; i < pPriv->bufferCount; i++) {
254		if (pPriv->buffers[i] != NULL) {
255		    (*ds->DestroyBuffer)(pDraw, pPriv->buffers[i]);
256		}
257	    }
258
259	    xfree(pPriv->buffers);
260	}
261    } else {
262	DRI2BufferPtr	buffers1;
263	unsigned int	temp_buf[32];
264	unsigned int	*temp = temp_buf;
265	int		i;
266	int		buffers_match = 1;
267
268	/* Version 1 API with CreateBuffers */
269
270	if ((count + 1) > 32) {
271	    temp = xalloc((count + 1) * sizeof(temp[0]));
272	}
273
274	for (i = 0; i < count; i++) {
275	    const unsigned attachment = *(attachments++);
276
277	    /* Version 1 doesn't deal with the format at all */
278	    if (has_format)
279		attachments++;
280
281	    /*
282	     * Make sure the client also gets the front buffer when
283	     * it asks for a back buffer
284	     */
285	    if (attachment == DRI2BufferBackLeft)
286		need_real_front++;
287
288	    /*
289	     * If the drawable is a window and the front-buffer is requested,
290	     * silently add the fake front-buffer to the list of requested
291	     * attachments.  The counting logic in the loop accounts for the
292	     * case where the client requests both the fake and real
293	     * front-buffer.
294	     */
295	    if (attachment == DRI2BufferFrontLeft) {
296		need_real_front--;
297		if (pDraw->type == DRAWABLE_WINDOW)
298		    need_fake_front++;
299	    }
300	    if (pDraw->type == DRAWABLE_WINDOW &&
301		attachment == DRI2BufferFakeFrontLeft)
302	    {
303		need_fake_front--;
304		have_fake_front = 1;
305	    }
306
307	    temp[i] = attachment;
308	}
309
310	if (need_real_front > 0)
311	    temp[count++] = DRI2BufferFrontLeft;
312
313	if (need_fake_front > 0) {
314	    temp[count++] = DRI2BufferFakeFrontLeft;
315	    have_fake_front = 1;
316	}
317
318	if (count != pPriv->bufferCount)
319	    buffers_match = 0;
320	else {
321	    for (i = 0; i < count; i++)
322		if (pPriv->buffers[i]->attachment != temp[i]) {
323		    buffers_match = 0;
324		    break;
325		}
326	}
327	if (pPriv->buffers == NULL || !dimensions_match || !buffers_match)
328	{
329            buffers1 = (*ds->CreateBuffers)(pDraw, temp, count);
330	    if (pPriv->buffers != NULL)
331		(*ds->DestroyBuffers)(pDraw, (DRI2BufferPtr) pPriv->buffers[0],
332				      pPriv->bufferCount);
333	}
334	else
335	    buffers1 = (DRI2BufferPtr) pPriv->buffers[0];
336
337        for (i = 0; i < count; i++)
338	    buffers[i] = (DRI2Buffer2Ptr) &buffers1[i];
339
340        *out_count = count;
341
342	if (pPriv->buffers)
343	    xfree (pPriv->buffers);
344
345	if (temp != temp_buf) {
346	    xfree(temp);
347	}
348    }
349
350    pPriv->buffers = buffers;
351    pPriv->bufferCount = *out_count;
352    pPriv->width = pDraw->width;
353    pPriv->height = pDraw->height;
354    *width = pPriv->width;
355    *height = pPriv->height;
356
357
358    /* If the client is getting a fake front-buffer, pre-fill it with the
359     * contents of the real front-buffer.  This ensures correct operation of
360     * applications that call glXWaitX before calling glDrawBuffer.
361     */
362    if (have_fake_front) {
363	BoxRec box;
364	RegionRec region;
365
366	box.x1 = 0;
367	box.y1 = 0;
368	box.x2 = pPriv->width;
369	box.y2 = pPriv->height;
370	REGION_INIT(pDraw->pScreen, &region, &box, 0);
371
372	DRI2CopyRegion(pDraw, &region, DRI2BufferFakeFrontLeft,
373		       DRI2BufferFrontLeft);
374    }
375
376    return pPriv->buffers;
377}
378
379DRI2Buffer2Ptr *
380DRI2GetBuffers(DrawablePtr pDraw, int *width, int *height,
381	       unsigned int *attachments, int count, int *out_count)
382{
383    return do_get_buffers(pDraw, width, height, attachments, count,
384			  out_count, FALSE);
385}
386
387DRI2Buffer2Ptr *
388DRI2GetBuffersWithFormat(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, TRUE);
393}
394
395int
396DRI2CopyRegion(DrawablePtr pDraw, RegionPtr pRegion,
397	       unsigned int dest, unsigned int src)
398{
399    DRI2ScreenPtr   ds = DRI2GetScreen(pDraw->pScreen);
400    DRI2DrawablePtr pPriv;
401    DRI2BufferPtr   pDestBuffer, pSrcBuffer;
402    int		    i;
403
404    pPriv = DRI2GetDrawable(pDraw);
405    if (pPriv == NULL)
406	return BadDrawable;
407
408    pDestBuffer = NULL;
409    pSrcBuffer = NULL;
410    for (i = 0; i < pPriv->bufferCount; i++)
411    {
412	if (pPriv->buffers[i]->attachment == dest)
413	    pDestBuffer = (DRI2BufferPtr) pPriv->buffers[i];
414	if (pPriv->buffers[i]->attachment == src)
415	    pSrcBuffer = (DRI2BufferPtr) pPriv->buffers[i];
416    }
417    if (pSrcBuffer == NULL || pDestBuffer == NULL)
418	return BadValue;
419
420    (*ds->CopyRegion)(pDraw, pRegion, pDestBuffer, pSrcBuffer);
421
422    return Success;
423}
424
425void
426DRI2DestroyDrawable(DrawablePtr pDraw)
427{
428    DRI2ScreenPtr   ds = DRI2GetScreen(pDraw->pScreen);
429    DRI2DrawablePtr pPriv;
430    WindowPtr  	    pWin;
431    PixmapPtr	    pPixmap;
432
433    pPriv = DRI2GetDrawable(pDraw);
434    if (pPriv == NULL)
435	return;
436
437    pPriv->refCount--;
438    if (pPriv->refCount > 0)
439	return;
440
441    if (pPriv->buffers != NULL) {
442	int i;
443
444	if (ds->DestroyBuffer) {
445	    for (i = 0; i < pPriv->bufferCount; i++) {
446		(*ds->DestroyBuffer)(pDraw, pPriv->buffers[i]);
447	    }
448	} else {
449	    (*ds->DestroyBuffers)(pDraw, (DRI2BufferPtr) pPriv->buffers[0],
450				  pPriv->bufferCount);
451	}
452
453	xfree(pPriv->buffers);
454    }
455
456    xfree(pPriv);
457
458    if (pDraw->type == DRAWABLE_WINDOW)
459    {
460	pWin = (WindowPtr) pDraw;
461	dixSetPrivate(&pWin->devPrivates, dri2WindowPrivateKey, NULL);
462    }
463    else
464    {
465	pPixmap = (PixmapPtr) pDraw;
466	dixSetPrivate(&pPixmap->devPrivates, dri2PixmapPrivateKey, NULL);
467    }
468}
469
470Bool
471DRI2Connect(ScreenPtr pScreen, unsigned int driverType, int *fd,
472	    const char **driverName, const char **deviceName)
473{
474    DRI2ScreenPtr ds = DRI2GetScreen(pScreen);
475
476    if (ds == NULL)
477	return FALSE;
478
479    if (driverType != DRI2DriverDRI)
480	return BadValue;
481
482    *fd = ds->fd;
483    *driverName = ds->driverName;
484    *deviceName = ds->deviceName;
485
486    return TRUE;
487}
488
489Bool
490DRI2Authenticate(ScreenPtr pScreen, drm_magic_t magic)
491{
492    DRI2ScreenPtr ds = DRI2GetScreen(pScreen);
493
494    if (ds == NULL || drmAuthMagic(ds->fd, magic))
495	return FALSE;
496
497    return TRUE;
498}
499
500Bool
501DRI2ScreenInit(ScreenPtr pScreen, DRI2InfoPtr info)
502{
503    DRI2ScreenPtr ds;
504
505    ds = xalloc(sizeof *ds);
506    if (!ds)
507	return FALSE;
508
509    ds->fd	       = info->fd;
510    ds->driverName     = info->driverName;
511    ds->deviceName     = info->deviceName;
512
513    /* Prefer the new one-at-a-time buffer API */
514    if (info->version >= 2 && info->CreateBuffer && info->DestroyBuffer) {
515	ds->CreateBuffer   = info->CreateBuffer;
516	ds->DestroyBuffer  = info->DestroyBuffer;
517	ds->CreateBuffers  = NULL;
518	ds->DestroyBuffers = NULL;
519    } else if (info->CreateBuffers && info->DestroyBuffers) {
520	xf86DrvMsg(pScreen->myNum, X_WARNING,
521		   "[DRI2] Version 1 API (broken front buffer rendering)\n");
522	ds->CreateBuffer   = NULL;
523	ds->DestroyBuffer  = NULL;
524	ds->CreateBuffers  = info->CreateBuffers;
525	ds->DestroyBuffers = info->DestroyBuffers;
526    } else {
527	xf86DrvMsg(pScreen->myNum, X_ERROR,
528		   "[DRI2] Missing buffer management functions\n");
529	xfree(ds);
530	return FALSE;
531    }
532
533    if (!info->CopyRegion) {
534	xf86DrvMsg(pScreen->myNum, X_ERROR,
535		   "[DRI2] Missing copy region function\n");
536	xfree(ds);
537	return FALSE;
538    }
539    ds->CopyRegion     = info->CopyRegion;
540
541    dixSetPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey, ds);
542
543    xf86DrvMsg(pScreen->myNum, X_INFO, "[DRI2] Setup complete\n");
544
545    return TRUE;
546}
547
548void
549DRI2CloseScreen(ScreenPtr pScreen)
550{
551    DRI2ScreenPtr ds = DRI2GetScreen(pScreen);
552
553    xfree(ds);
554    dixSetPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey, NULL);
555}
556
557extern ExtensionModule dri2ExtensionModule;
558
559static pointer
560DRI2Setup(pointer module, pointer opts, int *errmaj, int *errmin)
561{
562    static Bool setupDone = FALSE;
563
564    if (!setupDone)
565    {
566	setupDone = TRUE;
567	LoadExtension(&dri2ExtensionModule, FALSE);
568    }
569    else
570    {
571	if (errmaj)
572	    *errmaj = LDR_ONCEONLY;
573    }
574
575    return (pointer) 1;
576}
577
578static XF86ModuleVersionInfo DRI2VersRec =
579{
580    "dri2",
581    MODULEVENDORSTRING,
582    MODINFOSTRING1,
583    MODINFOSTRING2,
584    XORG_VERSION_CURRENT,
585    1, 1, 0,
586    ABI_CLASS_EXTENSION,
587    ABI_EXTENSION_VERSION,
588    MOD_CLASS_NONE,
589    { 0, 0, 0, 0 }
590};
591
592_X_EXPORT XF86ModuleData dri2ModuleData = { &DRI2VersRec, DRI2Setup, NULL };
593
594void
595DRI2Version(int *major, int *minor)
596{
597    if (major != NULL)
598	*major = DRI2VersRec.majorversion;
599
600    if (minor != NULL)
601	*minor = DRI2VersRec.minorversion;
602}
603