611 lines
15 KiB
C
611 lines
15 KiB
C
/*
|
|
* Copyright © 2007, 2008 Red Hat, Inc.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Soft-
|
|
* ware"), to deal in the Software without restriction, including without
|
|
* limitation the rights to use, copy, modify, merge, publish, distribute,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, provided that the above copyright
|
|
* notice(s) and this permission notice appear in all copies of the Soft-
|
|
* ware and that both the above copyright notice(s) and this permission
|
|
* notice appear in supporting documentation.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
|
|
* ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY
|
|
* RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN
|
|
* THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSE-
|
|
* QUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
|
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFOR-
|
|
* MANCE OF THIS SOFTWARE.
|
|
*
|
|
* Except as contained in this notice, the name of a copyright holder shall
|
|
* not be used in advertising or otherwise to promote the sale, use or
|
|
* other dealings in this Software without prior written authorization of
|
|
* the copyright holder.
|
|
*
|
|
* Authors:
|
|
* Kristian Høgsberg (krh@redhat.com)
|
|
*/
|
|
|
|
#ifdef HAVE_XORG_CONFIG_H
|
|
#include <xorg-config.h>
|
|
#endif
|
|
|
|
#include <xf86drm.h>
|
|
#include "xf86Module.h"
|
|
#include "scrnintstr.h"
|
|
#include "windowstr.h"
|
|
#include "dri2.h"
|
|
|
|
#include "xf86.h"
|
|
|
|
static int dri2ScreenPrivateKeyIndex;
|
|
static DevPrivateKey dri2ScreenPrivateKey = &dri2ScreenPrivateKeyIndex;
|
|
static int dri2WindowPrivateKeyIndex;
|
|
static DevPrivateKey dri2WindowPrivateKey = &dri2WindowPrivateKeyIndex;
|
|
static int dri2PixmapPrivateKeyIndex;
|
|
static DevPrivateKey dri2PixmapPrivateKey = &dri2PixmapPrivateKeyIndex;
|
|
|
|
typedef struct _DRI2Drawable {
|
|
unsigned int refCount;
|
|
int width;
|
|
int height;
|
|
DRI2Buffer2Ptr *buffers;
|
|
int bufferCount;
|
|
unsigned int pendingSequence;
|
|
} DRI2DrawableRec, *DRI2DrawablePtr;
|
|
|
|
typedef struct _DRI2Screen {
|
|
const char *driverName;
|
|
const char *deviceName;
|
|
int fd;
|
|
unsigned int lastSequence;
|
|
|
|
DRI2CreateBuffersProcPtr CreateBuffers;
|
|
DRI2DestroyBuffersProcPtr DestroyBuffers;
|
|
|
|
DRI2CreateBufferProcPtr CreateBuffer;
|
|
DRI2DestroyBufferProcPtr DestroyBuffer;
|
|
DRI2CopyRegionProcPtr CopyRegion;
|
|
|
|
HandleExposuresProcPtr HandleExposures;
|
|
} DRI2ScreenRec, *DRI2ScreenPtr;
|
|
|
|
static DRI2ScreenPtr
|
|
DRI2GetScreen(ScreenPtr pScreen)
|
|
{
|
|
return dixLookupPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey);
|
|
}
|
|
|
|
static DRI2DrawablePtr
|
|
DRI2GetDrawable(DrawablePtr pDraw)
|
|
{
|
|
WindowPtr pWin;
|
|
PixmapPtr pPixmap;
|
|
|
|
if (pDraw->type == DRAWABLE_WINDOW)
|
|
{
|
|
pWin = (WindowPtr) pDraw;
|
|
return dixLookupPrivate(&pWin->devPrivates, dri2WindowPrivateKey);
|
|
}
|
|
else
|
|
{
|
|
pPixmap = (PixmapPtr) pDraw;
|
|
return dixLookupPrivate(&pPixmap->devPrivates, dri2PixmapPrivateKey);
|
|
}
|
|
}
|
|
|
|
int
|
|
DRI2CreateDrawable(DrawablePtr pDraw)
|
|
{
|
|
WindowPtr pWin;
|
|
PixmapPtr pPixmap;
|
|
DRI2DrawablePtr pPriv;
|
|
|
|
pPriv = DRI2GetDrawable(pDraw);
|
|
if (pPriv != NULL)
|
|
{
|
|
pPriv->refCount++;
|
|
return Success;
|
|
}
|
|
|
|
pPriv = xalloc(sizeof *pPriv);
|
|
if (pPriv == NULL)
|
|
return BadAlloc;
|
|
|
|
pPriv->refCount = 1;
|
|
pPriv->width = pDraw->width;
|
|
pPriv->height = pDraw->height;
|
|
pPriv->buffers = NULL;
|
|
pPriv->bufferCount = 0;
|
|
|
|
if (pDraw->type == DRAWABLE_WINDOW)
|
|
{
|
|
pWin = (WindowPtr) pDraw;
|
|
dixSetPrivate(&pWin->devPrivates, dri2WindowPrivateKey, pPriv);
|
|
}
|
|
else
|
|
{
|
|
pPixmap = (PixmapPtr) pDraw;
|
|
dixSetPrivate(&pPixmap->devPrivates, dri2PixmapPrivateKey, pPriv);
|
|
}
|
|
|
|
return Success;
|
|
}
|
|
|
|
static int
|
|
find_attachment(DRI2DrawablePtr pPriv, unsigned attachment)
|
|
{
|
|
int i;
|
|
|
|
if (pPriv->buffers == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < pPriv->bufferCount; i++) {
|
|
if ((pPriv->buffers[i] != NULL)
|
|
&& (pPriv->buffers[i]->attachment == attachment)) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static DRI2Buffer2Ptr
|
|
allocate_or_reuse_buffer(DrawablePtr pDraw, DRI2ScreenPtr ds,
|
|
DRI2DrawablePtr pPriv,
|
|
unsigned int attachment, unsigned int format,
|
|
int dimensions_match)
|
|
{
|
|
DRI2Buffer2Ptr buffer;
|
|
int old_buf;
|
|
|
|
old_buf = find_attachment(pPriv, attachment);
|
|
|
|
if ((old_buf < 0)
|
|
|| !dimensions_match
|
|
|| (pPriv->buffers[old_buf]->format != format)) {
|
|
buffer = (*ds->CreateBuffer)(pDraw, attachment, format);
|
|
} else {
|
|
buffer = pPriv->buffers[old_buf];
|
|
pPriv->buffers[old_buf] = NULL;
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
static DRI2Buffer2Ptr *
|
|
do_get_buffers(DrawablePtr pDraw, int *width, int *height,
|
|
unsigned int *attachments, int count, int *out_count,
|
|
int has_format)
|
|
{
|
|
DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen);
|
|
DRI2DrawablePtr pPriv = DRI2GetDrawable(pDraw);
|
|
DRI2Buffer2Ptr *buffers;
|
|
int need_real_front = 0;
|
|
int need_fake_front = 0;
|
|
int have_fake_front = 0;
|
|
int front_format = 0;
|
|
int dimensions_match;
|
|
int i;
|
|
|
|
if (!pPriv) {
|
|
*width = pDraw->width;
|
|
*height = pDraw->height;
|
|
*out_count = 0;
|
|
return NULL;
|
|
}
|
|
|
|
dimensions_match = (pDraw->width == pPriv->width)
|
|
&& (pDraw->height == pPriv->height);
|
|
|
|
buffers = xalloc((count + 1) * sizeof(buffers[0]));
|
|
|
|
if (ds->CreateBuffer) {
|
|
/* Version 2 API with CreateBuffer */
|
|
for (i = 0; i < count; i++) {
|
|
const unsigned attachment = *(attachments++);
|
|
const unsigned format = (has_format) ? *(attachments++) : 0;
|
|
|
|
buffers[i] = allocate_or_reuse_buffer(pDraw, ds, pPriv, attachment,
|
|
format, dimensions_match);
|
|
|
|
/* If the drawable is a window and the front-buffer is requested,
|
|
* silently add the fake front-buffer to the list of requested
|
|
* attachments. The counting logic in the loop accounts for the case
|
|
* where the client requests both the fake and real front-buffer.
|
|
*/
|
|
if (attachment == DRI2BufferBackLeft) {
|
|
need_real_front++;
|
|
front_format = format;
|
|
}
|
|
|
|
if (attachment == DRI2BufferFrontLeft) {
|
|
need_real_front--;
|
|
front_format = format;
|
|
|
|
if (pDraw->type == DRAWABLE_WINDOW) {
|
|
need_fake_front++;
|
|
}
|
|
}
|
|
|
|
if (pDraw->type == DRAWABLE_WINDOW) {
|
|
if (attachment == DRI2BufferFakeFrontLeft) {
|
|
need_fake_front--;
|
|
have_fake_front = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (need_real_front > 0) {
|
|
buffers[i++] = allocate_or_reuse_buffer(pDraw, ds, pPriv,
|
|
DRI2BufferFrontLeft,
|
|
front_format, dimensions_match);
|
|
}
|
|
|
|
if (need_fake_front > 0) {
|
|
buffers[i++] = allocate_or_reuse_buffer(pDraw, ds, pPriv,
|
|
DRI2BufferFakeFrontLeft,
|
|
front_format, dimensions_match);
|
|
have_fake_front = 1;
|
|
}
|
|
|
|
*out_count = i;
|
|
|
|
|
|
if (pPriv->buffers != NULL) {
|
|
for (i = 0; i < pPriv->bufferCount; i++) {
|
|
if (pPriv->buffers[i] != NULL) {
|
|
(*ds->DestroyBuffer)(pDraw, pPriv->buffers[i]);
|
|
}
|
|
}
|
|
|
|
xfree(pPriv->buffers);
|
|
}
|
|
} else {
|
|
DRI2BufferPtr buffers1;
|
|
unsigned int temp_buf[32];
|
|
unsigned int *temp = temp_buf;
|
|
int i;
|
|
int buffers_match = 1;
|
|
|
|
/* Version 1 API with CreateBuffers */
|
|
|
|
if ((count + 1) > 32) {
|
|
temp = xalloc((count + 1) * sizeof(temp[0]));
|
|
}
|
|
|
|
for (i = 0; i < count; i++) {
|
|
const unsigned attachment = *(attachments++);
|
|
|
|
/* Version 1 doesn't deal with the format at all */
|
|
if (has_format)
|
|
attachments++;
|
|
|
|
/*
|
|
* Make sure the client also gets the front buffer when
|
|
* it asks for a back buffer
|
|
*/
|
|
if (attachment == DRI2BufferBackLeft)
|
|
need_real_front++;
|
|
|
|
/*
|
|
* If the drawable is a window and the front-buffer is requested,
|
|
* silently add the fake front-buffer to the list of requested
|
|
* attachments. The counting logic in the loop accounts for the
|
|
* case where the client requests both the fake and real
|
|
* front-buffer.
|
|
*/
|
|
if (attachment == DRI2BufferFrontLeft) {
|
|
need_real_front--;
|
|
if (pDraw->type == DRAWABLE_WINDOW)
|
|
need_fake_front++;
|
|
}
|
|
if (pDraw->type == DRAWABLE_WINDOW &&
|
|
attachment == DRI2BufferFakeFrontLeft)
|
|
{
|
|
need_fake_front--;
|
|
have_fake_front = 1;
|
|
}
|
|
|
|
temp[i] = attachment;
|
|
}
|
|
|
|
if (need_real_front > 0)
|
|
temp[count++] = DRI2BufferFrontLeft;
|
|
|
|
if (need_fake_front > 0) {
|
|
temp[count++] = DRI2BufferFakeFrontLeft;
|
|
have_fake_front = 1;
|
|
}
|
|
|
|
if (count != pPriv->bufferCount)
|
|
buffers_match = 0;
|
|
else {
|
|
for (i = 0; i < count; i++)
|
|
if (pPriv->buffers[i]->attachment != temp[i]) {
|
|
buffers_match = 0;
|
|
break;
|
|
}
|
|
}
|
|
if (pPriv->buffers == NULL || !dimensions_match || !buffers_match)
|
|
{
|
|
buffers1 = (*ds->CreateBuffers)(pDraw, temp, count);
|
|
if (pPriv->buffers != NULL)
|
|
(*ds->DestroyBuffers)(pDraw, (DRI2BufferPtr) pPriv->buffers[0],
|
|
pPriv->bufferCount);
|
|
}
|
|
else
|
|
buffers1 = (DRI2BufferPtr) pPriv->buffers[0];
|
|
|
|
for (i = 0; i < count; i++)
|
|
buffers[i] = (DRI2Buffer2Ptr) &buffers1[i];
|
|
|
|
*out_count = count;
|
|
|
|
if (pPriv->buffers)
|
|
xfree (pPriv->buffers);
|
|
|
|
if (temp != temp_buf) {
|
|
xfree(temp);
|
|
}
|
|
}
|
|
|
|
pPriv->buffers = buffers;
|
|
pPriv->bufferCount = *out_count;
|
|
pPriv->width = pDraw->width;
|
|
pPriv->height = pDraw->height;
|
|
*width = pPriv->width;
|
|
*height = pPriv->height;
|
|
|
|
|
|
/* If the client is getting a fake front-buffer, pre-fill it with the
|
|
* contents of the real front-buffer. This ensures correct operation of
|
|
* applications that call glXWaitX before calling glDrawBuffer.
|
|
*/
|
|
if (have_fake_front) {
|
|
BoxRec box;
|
|
RegionRec region;
|
|
|
|
box.x1 = 0;
|
|
box.y1 = 0;
|
|
box.x2 = pPriv->width;
|
|
box.y2 = pPriv->height;
|
|
REGION_INIT(pDraw->pScreen, ®ion, &box, 0);
|
|
|
|
DRI2CopyRegion(pDraw, ®ion, DRI2BufferFakeFrontLeft,
|
|
DRI2BufferFrontLeft);
|
|
}
|
|
|
|
return pPriv->buffers;
|
|
}
|
|
|
|
DRI2Buffer2Ptr *
|
|
DRI2GetBuffers(DrawablePtr pDraw, int *width, int *height,
|
|
unsigned int *attachments, int count, int *out_count)
|
|
{
|
|
return do_get_buffers(pDraw, width, height, attachments, count,
|
|
out_count, FALSE);
|
|
}
|
|
|
|
DRI2Buffer2Ptr *
|
|
DRI2GetBuffersWithFormat(DrawablePtr pDraw, int *width, int *height,
|
|
unsigned int *attachments, int count, int *out_count)
|
|
{
|
|
return do_get_buffers(pDraw, width, height, attachments, count,
|
|
out_count, TRUE);
|
|
}
|
|
|
|
int
|
|
DRI2CopyRegion(DrawablePtr pDraw, RegionPtr pRegion,
|
|
unsigned int dest, unsigned int src)
|
|
{
|
|
DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen);
|
|
DRI2DrawablePtr pPriv;
|
|
DRI2BufferPtr pDestBuffer, pSrcBuffer;
|
|
int i;
|
|
|
|
pPriv = DRI2GetDrawable(pDraw);
|
|
if (pPriv == NULL)
|
|
return BadDrawable;
|
|
|
|
pDestBuffer = NULL;
|
|
pSrcBuffer = NULL;
|
|
for (i = 0; i < pPriv->bufferCount; i++)
|
|
{
|
|
if (pPriv->buffers[i]->attachment == dest)
|
|
pDestBuffer = (DRI2BufferPtr) pPriv->buffers[i];
|
|
if (pPriv->buffers[i]->attachment == src)
|
|
pSrcBuffer = (DRI2BufferPtr) pPriv->buffers[i];
|
|
}
|
|
if (pSrcBuffer == NULL || pDestBuffer == NULL)
|
|
return BadValue;
|
|
|
|
(*ds->CopyRegion)(pDraw, pRegion, pDestBuffer, pSrcBuffer);
|
|
|
|
return Success;
|
|
}
|
|
|
|
void
|
|
DRI2DestroyDrawable(DrawablePtr pDraw)
|
|
{
|
|
DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen);
|
|
DRI2DrawablePtr pPriv;
|
|
WindowPtr pWin;
|
|
PixmapPtr pPixmap;
|
|
|
|
pPriv = DRI2GetDrawable(pDraw);
|
|
if (pPriv == NULL)
|
|
return;
|
|
|
|
pPriv->refCount--;
|
|
if (pPriv->refCount > 0)
|
|
return;
|
|
|
|
if (pPriv->buffers != NULL) {
|
|
int i;
|
|
|
|
if (ds->DestroyBuffer) {
|
|
for (i = 0; i < pPriv->bufferCount; i++) {
|
|
(*ds->DestroyBuffer)(pDraw, pPriv->buffers[i]);
|
|
}
|
|
} else {
|
|
(*ds->DestroyBuffers)(pDraw, (DRI2BufferPtr) pPriv->buffers[0],
|
|
pPriv->bufferCount);
|
|
}
|
|
|
|
xfree(pPriv->buffers);
|
|
}
|
|
|
|
xfree(pPriv);
|
|
|
|
if (pDraw->type == DRAWABLE_WINDOW)
|
|
{
|
|
pWin = (WindowPtr) pDraw;
|
|
dixSetPrivate(&pWin->devPrivates, dri2WindowPrivateKey, NULL);
|
|
}
|
|
else
|
|
{
|
|
pPixmap = (PixmapPtr) pDraw;
|
|
dixSetPrivate(&pPixmap->devPrivates, dri2PixmapPrivateKey, NULL);
|
|
}
|
|
}
|
|
|
|
Bool
|
|
DRI2Connect(ScreenPtr pScreen, unsigned int driverType, int *fd,
|
|
const char **driverName, const char **deviceName)
|
|
{
|
|
DRI2ScreenPtr ds = DRI2GetScreen(pScreen);
|
|
|
|
if (ds == NULL)
|
|
return FALSE;
|
|
|
|
if (driverType != DRI2DriverDRI)
|
|
return BadValue;
|
|
|
|
*fd = ds->fd;
|
|
*driverName = ds->driverName;
|
|
*deviceName = ds->deviceName;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
Bool
|
|
DRI2Authenticate(ScreenPtr pScreen, drm_magic_t magic)
|
|
{
|
|
DRI2ScreenPtr ds = DRI2GetScreen(pScreen);
|
|
|
|
if (ds == NULL || drmAuthMagic(ds->fd, magic))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
Bool
|
|
DRI2ScreenInit(ScreenPtr pScreen, DRI2InfoPtr info)
|
|
{
|
|
DRI2ScreenPtr ds;
|
|
|
|
ds = xalloc(sizeof *ds);
|
|
if (!ds)
|
|
return FALSE;
|
|
|
|
ds->fd = info->fd;
|
|
ds->driverName = info->driverName;
|
|
ds->deviceName = info->deviceName;
|
|
|
|
/* Prefer the new one-at-a-time buffer API */
|
|
if (info->version >= 2 && info->CreateBuffer && info->DestroyBuffer) {
|
|
ds->CreateBuffer = info->CreateBuffer;
|
|
ds->DestroyBuffer = info->DestroyBuffer;
|
|
ds->CreateBuffers = NULL;
|
|
ds->DestroyBuffers = NULL;
|
|
} else if (info->CreateBuffers && info->DestroyBuffers) {
|
|
xf86DrvMsg(pScreen->myNum, X_WARNING,
|
|
"[DRI2] Version 1 API (broken front buffer rendering)\n");
|
|
ds->CreateBuffer = NULL;
|
|
ds->DestroyBuffer = NULL;
|
|
ds->CreateBuffers = info->CreateBuffers;
|
|
ds->DestroyBuffers = info->DestroyBuffers;
|
|
} else {
|
|
xf86DrvMsg(pScreen->myNum, X_ERROR,
|
|
"[DRI2] Missing buffer management functions\n");
|
|
xfree(ds);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!info->CopyRegion) {
|
|
xf86DrvMsg(pScreen->myNum, X_ERROR,
|
|
"[DRI2] Missing copy region function\n");
|
|
xfree(ds);
|
|
return FALSE;
|
|
}
|
|
ds->CopyRegion = info->CopyRegion;
|
|
|
|
dixSetPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey, ds);
|
|
|
|
xf86DrvMsg(pScreen->myNum, X_INFO, "[DRI2] Setup complete\n");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
DRI2CloseScreen(ScreenPtr pScreen)
|
|
{
|
|
DRI2ScreenPtr ds = DRI2GetScreen(pScreen);
|
|
|
|
xfree(ds);
|
|
dixSetPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey, NULL);
|
|
}
|
|
|
|
extern ExtensionModule dri2ExtensionModule;
|
|
|
|
static pointer
|
|
DRI2Setup(pointer module, pointer opts, int *errmaj, int *errmin)
|
|
{
|
|
static Bool setupDone = FALSE;
|
|
|
|
if (!setupDone)
|
|
{
|
|
setupDone = TRUE;
|
|
LoadExtension(&dri2ExtensionModule, FALSE);
|
|
}
|
|
else
|
|
{
|
|
if (errmaj)
|
|
*errmaj = LDR_ONCEONLY;
|
|
}
|
|
|
|
return (pointer) 1;
|
|
}
|
|
|
|
static XF86ModuleVersionInfo DRI2VersRec =
|
|
{
|
|
"dri2",
|
|
MODULEVENDORSTRING,
|
|
MODINFOSTRING1,
|
|
MODINFOSTRING2,
|
|
XORG_VERSION_CURRENT,
|
|
1, 1, 0,
|
|
ABI_CLASS_EXTENSION,
|
|
ABI_EXTENSION_VERSION,
|
|
MOD_CLASS_NONE,
|
|
{ 0, 0, 0, 0 }
|
|
};
|
|
|
|
_X_EXPORT XF86ModuleData dri2ModuleData = { &DRI2VersRec, DRI2Setup, NULL };
|
|
|
|
void
|
|
DRI2Version(int *major, int *minor)
|
|
{
|
|
if (major != NULL)
|
|
*major = DRI2VersRec.majorversion;
|
|
|
|
if (minor != NULL)
|
|
*minor = DRI2VersRec.minorversion;
|
|
}
|