Signed-off-by: Tiago Vignatti <tiago.vignatti@nokia.com> Reviewed-by: Nicolas Peninguy <nico@lostgeeks.org> Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
1233 lines
31 KiB
C
1233 lines
31 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 <errno.h>
|
|
#ifdef WITH_LIBDRM
|
|
#include <xf86drm.h>
|
|
#endif
|
|
#include "xf86Module.h"
|
|
#include "list.h"
|
|
#include "scrnintstr.h"
|
|
#include "windowstr.h"
|
|
#include "dixstruct.h"
|
|
#include "dri2.h"
|
|
#include "xf86VGAarbiter.h"
|
|
|
|
#include "xf86.h"
|
|
|
|
CARD8 dri2_major; /* version of DRI2 supported by DDX */
|
|
CARD8 dri2_minor;
|
|
|
|
static DevPrivateKeyRec dri2ScreenPrivateKeyRec;
|
|
#define dri2ScreenPrivateKey (&dri2ScreenPrivateKeyRec)
|
|
|
|
static DevPrivateKeyRec dri2WindowPrivateKeyRec;
|
|
#define dri2WindowPrivateKey (&dri2WindowPrivateKeyRec)
|
|
|
|
static DevPrivateKeyRec dri2PixmapPrivateKeyRec;
|
|
#define dri2PixmapPrivateKey (&dri2PixmapPrivateKeyRec)
|
|
|
|
static RESTYPE dri2DrawableRes;
|
|
|
|
typedef struct _DRI2Screen *DRI2ScreenPtr;
|
|
|
|
typedef struct _DRI2Drawable {
|
|
DRI2ScreenPtr dri2_screen;
|
|
DrawablePtr drawable;
|
|
struct list reference_list;
|
|
int width;
|
|
int height;
|
|
DRI2BufferPtr *buffers;
|
|
int bufferCount;
|
|
unsigned int swapsPending;
|
|
ClientPtr blockedClient;
|
|
Bool blockedOnMsc;
|
|
int swap_interval;
|
|
CARD64 swap_count;
|
|
int64_t target_sbc; /* -1 means no SBC wait outstanding */
|
|
CARD64 last_swap_target; /* most recently queued swap target */
|
|
CARD64 last_swap_msc; /* msc at completion of most recent swap */
|
|
CARD64 last_swap_ust; /* ust at completion of most recent swap */
|
|
int swap_limit; /* for N-buffering */
|
|
unsigned long serialNumber;
|
|
} DRI2DrawableRec, *DRI2DrawablePtr;
|
|
|
|
typedef struct _DRI2Screen {
|
|
ScreenPtr screen;
|
|
int refcnt;
|
|
unsigned int numDrivers;
|
|
const char **driverNames;
|
|
const char *deviceName;
|
|
int fd;
|
|
unsigned int lastSequence;
|
|
|
|
DRI2CreateBufferProcPtr CreateBuffer;
|
|
DRI2DestroyBufferProcPtr DestroyBuffer;
|
|
DRI2CopyRegionProcPtr CopyRegion;
|
|
DRI2ScheduleSwapProcPtr ScheduleSwap;
|
|
DRI2GetMSCProcPtr GetMSC;
|
|
DRI2ScheduleWaitMSCProcPtr ScheduleWaitMSC;
|
|
DRI2AuthMagicProcPtr AuthMagic;
|
|
|
|
HandleExposuresProcPtr HandleExposures;
|
|
|
|
ConfigNotifyProcPtr ConfigNotify;
|
|
} DRI2ScreenRec;
|
|
|
|
static DRI2ScreenPtr
|
|
DRI2GetScreen(ScreenPtr pScreen)
|
|
{
|
|
return dixLookupPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey);
|
|
}
|
|
|
|
static DRI2DrawablePtr
|
|
DRI2GetDrawable(DrawablePtr pDraw)
|
|
{
|
|
WindowPtr pWin;
|
|
PixmapPtr pPixmap;
|
|
|
|
switch (pDraw->type) {
|
|
case DRAWABLE_WINDOW:
|
|
pWin = (WindowPtr) pDraw;
|
|
return dixLookupPrivate(&pWin->devPrivates, dri2WindowPrivateKey);
|
|
case DRAWABLE_PIXMAP:
|
|
pPixmap = (PixmapPtr) pDraw;
|
|
return dixLookupPrivate(&pPixmap->devPrivates, dri2PixmapPrivateKey);
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static unsigned long
|
|
DRI2DrawableSerial(DrawablePtr pDraw)
|
|
{
|
|
ScreenPtr pScreen = pDraw->pScreen;
|
|
PixmapPtr pPix;
|
|
|
|
if (pDraw->type != DRAWABLE_WINDOW)
|
|
return pDraw->serialNumber;
|
|
|
|
pPix = pScreen->GetWindowPixmap((WindowPtr)pDraw);
|
|
return pPix->drawable.serialNumber;
|
|
}
|
|
|
|
static DRI2DrawablePtr
|
|
DRI2AllocateDrawable(DrawablePtr pDraw)
|
|
{
|
|
DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen);
|
|
DRI2DrawablePtr pPriv;
|
|
CARD64 ust;
|
|
WindowPtr pWin;
|
|
PixmapPtr pPixmap;
|
|
|
|
pPriv = malloc(sizeof *pPriv);
|
|
if (pPriv == NULL)
|
|
return NULL;
|
|
|
|
pPriv->dri2_screen = ds;
|
|
pPriv->drawable = pDraw;
|
|
pPriv->width = pDraw->width;
|
|
pPriv->height = pDraw->height;
|
|
pPriv->buffers = NULL;
|
|
pPriv->bufferCount = 0;
|
|
pPriv->swapsPending = 0;
|
|
pPriv->blockedClient = NULL;
|
|
pPriv->blockedOnMsc = FALSE;
|
|
pPriv->swap_count = 0;
|
|
pPriv->target_sbc = -1;
|
|
pPriv->swap_interval = 1;
|
|
/* Initialize last swap target from DDX if possible */
|
|
if (!ds->GetMSC || !(*ds->GetMSC)(pDraw, &ust, &pPriv->last_swap_target))
|
|
pPriv->last_swap_target = 0;
|
|
|
|
pPriv->swap_limit = 1; /* default to double buffering */
|
|
pPriv->last_swap_msc = 0;
|
|
pPriv->last_swap_ust = 0;
|
|
list_init(&pPriv->reference_list);
|
|
pPriv->serialNumber = DRI2DrawableSerial(pDraw);
|
|
|
|
if (pDraw->type == DRAWABLE_WINDOW) {
|
|
pWin = (WindowPtr) pDraw;
|
|
dixSetPrivate(&pWin->devPrivates, dri2WindowPrivateKey, pPriv);
|
|
} else {
|
|
pPixmap = (PixmapPtr) pDraw;
|
|
dixSetPrivate(&pPixmap->devPrivates, dri2PixmapPrivateKey, pPriv);
|
|
}
|
|
|
|
return pPriv;
|
|
}
|
|
|
|
typedef struct DRI2DrawableRefRec {
|
|
XID id;
|
|
XID dri2_id;
|
|
DRI2InvalidateProcPtr invalidate;
|
|
void *priv;
|
|
struct list link;
|
|
} DRI2DrawableRefRec, *DRI2DrawableRefPtr;
|
|
|
|
static DRI2DrawableRefPtr
|
|
DRI2LookupDrawableRef(DRI2DrawablePtr pPriv, XID id)
|
|
{
|
|
DRI2DrawableRefPtr ref;
|
|
|
|
list_for_each_entry(ref, &pPriv->reference_list, link) {
|
|
if (ref->id == id)
|
|
return ref;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
DRI2AddDrawableRef(DRI2DrawablePtr pPriv, XID id, XID dri2_id,
|
|
DRI2InvalidateProcPtr invalidate, void *priv)
|
|
{
|
|
DRI2DrawableRefPtr ref;
|
|
|
|
ref = malloc(sizeof *ref);
|
|
if (ref == NULL)
|
|
return BadAlloc;
|
|
|
|
if (!AddResource(dri2_id, dri2DrawableRes, pPriv)) {
|
|
free(ref);
|
|
return BadAlloc;
|
|
}
|
|
if (!DRI2LookupDrawableRef(pPriv, id))
|
|
if (!AddResource(id, dri2DrawableRes, pPriv)) {
|
|
FreeResourceByType(dri2_id, dri2DrawableRes, TRUE);
|
|
free(ref);
|
|
return BadAlloc;
|
|
}
|
|
|
|
ref->id = id;
|
|
ref->dri2_id = dri2_id;
|
|
ref->invalidate = invalidate;
|
|
ref->priv = priv;
|
|
list_add(&ref->link, &pPriv->reference_list);
|
|
|
|
return Success;
|
|
}
|
|
|
|
int
|
|
DRI2CreateDrawable(ClientPtr client, DrawablePtr pDraw, XID id,
|
|
DRI2InvalidateProcPtr invalidate, void *priv)
|
|
{
|
|
DRI2DrawablePtr pPriv;
|
|
XID dri2_id;
|
|
int rc;
|
|
|
|
pPriv = DRI2GetDrawable(pDraw);
|
|
if (pPriv == NULL)
|
|
pPriv = DRI2AllocateDrawable(pDraw);
|
|
if (pPriv == NULL)
|
|
return BadAlloc;
|
|
|
|
dri2_id = FakeClientID(client->index);
|
|
rc = DRI2AddDrawableRef(pPriv, id, dri2_id, invalidate, priv);
|
|
if (rc != Success)
|
|
return rc;
|
|
|
|
return Success;
|
|
}
|
|
|
|
static int DRI2DrawableGone(pointer p, XID id)
|
|
{
|
|
DRI2DrawablePtr pPriv = p;
|
|
DRI2ScreenPtr ds = pPriv->dri2_screen;
|
|
DRI2DrawableRefPtr ref, next;
|
|
WindowPtr pWin;
|
|
PixmapPtr pPixmap;
|
|
DrawablePtr pDraw;
|
|
int i;
|
|
|
|
list_for_each_entry_safe(ref, next, &pPriv->reference_list, link) {
|
|
if (ref->dri2_id == id) {
|
|
list_del(&ref->link);
|
|
/* If this was the last ref under this X drawable XID,
|
|
* unregister the X drawable resource. */
|
|
if (!DRI2LookupDrawableRef(pPriv, ref->id))
|
|
FreeResourceByType(ref->id, dri2DrawableRes, TRUE);
|
|
free(ref);
|
|
break;
|
|
}
|
|
|
|
if (ref->id == id) {
|
|
list_del(&ref->link);
|
|
FreeResourceByType(ref->dri2_id, dri2DrawableRes, TRUE);
|
|
free(ref);
|
|
}
|
|
}
|
|
|
|
if (!list_is_empty(&pPriv->reference_list))
|
|
return Success;
|
|
|
|
pDraw = pPriv->drawable;
|
|
if (pDraw->type == DRAWABLE_WINDOW) {
|
|
pWin = (WindowPtr) pDraw;
|
|
dixSetPrivate(&pWin->devPrivates, dri2WindowPrivateKey, NULL);
|
|
} else {
|
|
pPixmap = (PixmapPtr) pDraw;
|
|
dixSetPrivate(&pPixmap->devPrivates, dri2PixmapPrivateKey, NULL);
|
|
}
|
|
|
|
if (pPriv->buffers != NULL) {
|
|
for (i = 0; i < pPriv->bufferCount; i++)
|
|
(*ds->DestroyBuffer)(pDraw, pPriv->buffers[i]);
|
|
|
|
free(pPriv->buffers);
|
|
}
|
|
|
|
free(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 Bool
|
|
allocate_or_reuse_buffer(DrawablePtr pDraw, DRI2ScreenPtr ds,
|
|
DRI2DrawablePtr pPriv,
|
|
unsigned int attachment, unsigned int format,
|
|
int dimensions_match, DRI2BufferPtr *buffer)
|
|
{
|
|
int old_buf = find_attachment(pPriv, attachment);
|
|
|
|
if ((old_buf < 0)
|
|
|| !dimensions_match
|
|
|| (pPriv->buffers[old_buf]->format != format)) {
|
|
*buffer = (*ds->CreateBuffer)(pDraw, attachment, format);
|
|
pPriv->serialNumber = DRI2DrawableSerial(pDraw);
|
|
return TRUE;
|
|
|
|
} else {
|
|
*buffer = pPriv->buffers[old_buf];
|
|
pPriv->buffers[old_buf] = NULL;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
update_dri2_drawable_buffers(DRI2DrawablePtr pPriv, DrawablePtr pDraw,
|
|
DRI2BufferPtr *buffers, int *out_count, int *width, int *height)
|
|
{
|
|
DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen);
|
|
int i;
|
|
|
|
if (pPriv->buffers != NULL) {
|
|
for (i = 0; i < pPriv->bufferCount; i++) {
|
|
if (pPriv->buffers[i] != NULL) {
|
|
(*ds->DestroyBuffer)(pDraw, pPriv->buffers[i]);
|
|
}
|
|
}
|
|
|
|
free(pPriv->buffers);
|
|
}
|
|
|
|
pPriv->buffers = buffers;
|
|
pPriv->bufferCount = *out_count;
|
|
pPriv->width = pDraw->width;
|
|
pPriv->height = pDraw->height;
|
|
*width = pPriv->width;
|
|
*height = pPriv->height;
|
|
}
|
|
|
|
static DRI2BufferPtr *
|
|
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);
|
|
DRI2BufferPtr *buffers;
|
|
int need_real_front = 0;
|
|
int need_fake_front = 0;
|
|
int have_fake_front = 0;
|
|
int front_format = 0;
|
|
int dimensions_match;
|
|
int buffers_changed = 0;
|
|
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)
|
|
&& (pPriv->serialNumber == DRI2DrawableSerial(pDraw));
|
|
|
|
buffers = calloc((count + 1), sizeof(buffers[0]));
|
|
|
|
for (i = 0; i < count; i++) {
|
|
const unsigned attachment = *(attachments++);
|
|
const unsigned format = (has_format) ? *(attachments++) : 0;
|
|
|
|
if (allocate_or_reuse_buffer(pDraw, ds, pPriv, attachment,
|
|
format, dimensions_match,
|
|
&buffers[i]))
|
|
buffers_changed = 1;
|
|
|
|
if (buffers[i] == NULL)
|
|
goto err_out;
|
|
|
|
/* 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) {
|
|
if (allocate_or_reuse_buffer(pDraw, ds, pPriv, DRI2BufferFrontLeft,
|
|
front_format, dimensions_match,
|
|
&buffers[i]))
|
|
buffers_changed = 1;
|
|
|
|
if (buffers[i] == NULL)
|
|
goto err_out;
|
|
i++;
|
|
}
|
|
|
|
if (need_fake_front > 0) {
|
|
if (allocate_or_reuse_buffer(pDraw, ds, pPriv, DRI2BufferFakeFrontLeft,
|
|
front_format, dimensions_match,
|
|
&buffers[i]))
|
|
buffers_changed = 1;
|
|
|
|
if (buffers[i] == NULL)
|
|
goto err_out;
|
|
|
|
i++;
|
|
have_fake_front = 1;
|
|
}
|
|
|
|
*out_count = i;
|
|
|
|
update_dri2_drawable_buffers(pPriv, pDraw, buffers, out_count, width, 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 && buffers_changed) {
|
|
BoxRec box;
|
|
RegionRec region;
|
|
|
|
box.x1 = 0;
|
|
box.y1 = 0;
|
|
box.x2 = pPriv->width;
|
|
box.y2 = pPriv->height;
|
|
RegionInit(®ion, &box, 0);
|
|
|
|
DRI2CopyRegion(pDraw, ®ion, DRI2BufferFakeFrontLeft,
|
|
DRI2BufferFrontLeft);
|
|
}
|
|
|
|
return pPriv->buffers;
|
|
|
|
err_out:
|
|
|
|
*out_count = 0;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
if (buffers[i] != NULL)
|
|
(*ds->DestroyBuffer)(pDraw, buffers[i]);
|
|
}
|
|
|
|
free(buffers);
|
|
buffers = NULL;
|
|
|
|
update_dri2_drawable_buffers(pPriv, pDraw, buffers, out_count, width, height);
|
|
|
|
return buffers;
|
|
}
|
|
|
|
DRI2BufferPtr *
|
|
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);
|
|
}
|
|
|
|
DRI2BufferPtr *
|
|
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);
|
|
}
|
|
|
|
static void
|
|
DRI2InvalidateDrawable(DrawablePtr pDraw)
|
|
{
|
|
DRI2DrawablePtr pPriv = DRI2GetDrawable(pDraw);
|
|
DRI2DrawableRefPtr ref;
|
|
|
|
if (!pPriv)
|
|
return;
|
|
|
|
list_for_each_entry(ref, &pPriv->reference_list, link)
|
|
ref->invalidate(pDraw, ref->priv);
|
|
}
|
|
|
|
/*
|
|
* In the direct rendered case, we throttle the clients that have more
|
|
* than their share of outstanding swaps (and thus busy buffers) when a
|
|
* new GetBuffers request is received. In the AIGLX case, we allow the
|
|
* client to get the new buffers, but throttle when the next GLX request
|
|
* comes in (see __glXDRIcontextWait()).
|
|
*/
|
|
Bool
|
|
DRI2ThrottleClient(ClientPtr client, DrawablePtr pDraw)
|
|
{
|
|
DRI2DrawablePtr pPriv;
|
|
|
|
pPriv = DRI2GetDrawable(pDraw);
|
|
if (pPriv == NULL)
|
|
return FALSE;
|
|
|
|
/* Throttle to swap limit */
|
|
if ((pPriv->swapsPending >= pPriv->swap_limit) &&
|
|
!pPriv->blockedClient) {
|
|
ResetCurrentRequest(client);
|
|
client->sequence--;
|
|
IgnoreClient(client);
|
|
pPriv->blockedClient = client;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
__DRI2BlockClient(ClientPtr client, DRI2DrawablePtr pPriv)
|
|
{
|
|
if (pPriv->blockedClient == NULL) {
|
|
IgnoreClient(client);
|
|
pPriv->blockedClient = client;
|
|
}
|
|
}
|
|
|
|
void
|
|
DRI2BlockClient(ClientPtr client, DrawablePtr pDraw)
|
|
{
|
|
DRI2DrawablePtr pPriv;
|
|
|
|
pPriv = DRI2GetDrawable(pDraw);
|
|
if (pPriv == NULL)
|
|
return;
|
|
|
|
__DRI2BlockClient(client, pPriv);
|
|
pPriv->blockedOnMsc = 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;
|
|
}
|
|
|
|
/* Can this drawable be page flipped? */
|
|
Bool
|
|
DRI2CanFlip(DrawablePtr pDraw)
|
|
{
|
|
ScreenPtr pScreen = pDraw->pScreen;
|
|
WindowPtr pWin, pRoot;
|
|
PixmapPtr pWinPixmap, pRootPixmap;
|
|
|
|
if (pDraw->type == DRAWABLE_PIXMAP)
|
|
return TRUE;
|
|
|
|
pRoot = pScreen->root;
|
|
pRootPixmap = pScreen->GetWindowPixmap(pRoot);
|
|
|
|
pWin = (WindowPtr) pDraw;
|
|
pWinPixmap = pScreen->GetWindowPixmap(pWin);
|
|
if (pRootPixmap != pWinPixmap)
|
|
return FALSE;
|
|
if (!RegionEqual(&pWin->clipList, &pRoot->winSize))
|
|
return FALSE;
|
|
|
|
/* Does the window match the pixmap exactly? */
|
|
if (pDraw->x != 0 ||
|
|
pDraw->y != 0 ||
|
|
#ifdef COMPOSITE
|
|
pDraw->x != pWinPixmap->screen_x ||
|
|
pDraw->y != pWinPixmap->screen_y ||
|
|
#endif
|
|
pDraw->width != pWinPixmap->drawable.width ||
|
|
pDraw->height != pWinPixmap->drawable.height)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* Can we do a pixmap exchange instead of a blit? */
|
|
Bool
|
|
DRI2CanExchange(DrawablePtr pDraw)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
DRI2WaitMSCComplete(ClientPtr client, DrawablePtr pDraw, int frame,
|
|
unsigned int tv_sec, unsigned int tv_usec)
|
|
{
|
|
DRI2DrawablePtr pPriv;
|
|
|
|
pPriv = DRI2GetDrawable(pDraw);
|
|
if (pPriv == NULL)
|
|
return;
|
|
|
|
ProcDRI2WaitMSCReply(client, ((CARD64)tv_sec * 1000000) + tv_usec,
|
|
frame, pPriv->swap_count);
|
|
|
|
if (pPriv->blockedClient)
|
|
AttendClient(pPriv->blockedClient);
|
|
|
|
pPriv->blockedClient = NULL;
|
|
pPriv->blockedOnMsc = FALSE;
|
|
}
|
|
|
|
static void
|
|
DRI2WakeClient(ClientPtr client, DrawablePtr pDraw, int frame,
|
|
unsigned int tv_sec, unsigned int tv_usec)
|
|
{
|
|
ScreenPtr pScreen = pDraw->pScreen;
|
|
DRI2DrawablePtr pPriv;
|
|
|
|
pPriv = DRI2GetDrawable(pDraw);
|
|
if (pPriv == NULL) {
|
|
xf86DrvMsg(pScreen->myNum, X_ERROR,
|
|
"[DRI2] %s: bad drawable\n", __func__);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Swap completed.
|
|
* Wake the client iff:
|
|
* - it was waiting on SBC
|
|
* - was blocked due to GLX make current
|
|
* - was blocked due to swap throttling
|
|
* - is not blocked due to an MSC wait
|
|
*/
|
|
if (pPriv->target_sbc != -1 &&
|
|
pPriv->target_sbc <= pPriv->swap_count) {
|
|
ProcDRI2WaitMSCReply(client, ((CARD64)tv_sec * 1000000) + tv_usec,
|
|
frame, pPriv->swap_count);
|
|
pPriv->target_sbc = -1;
|
|
|
|
AttendClient(pPriv->blockedClient);
|
|
pPriv->blockedClient = NULL;
|
|
} else if (pPriv->target_sbc == -1 && !pPriv->blockedOnMsc) {
|
|
if (pPriv->blockedClient) {
|
|
AttendClient(pPriv->blockedClient);
|
|
pPriv->blockedClient = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
DRI2SwapComplete(ClientPtr client, DrawablePtr pDraw, int frame,
|
|
unsigned int tv_sec, unsigned int tv_usec, int type,
|
|
DRI2SwapEventPtr swap_complete, void *swap_data)
|
|
{
|
|
ScreenPtr pScreen = pDraw->pScreen;
|
|
DRI2DrawablePtr pPriv;
|
|
CARD64 ust = 0;
|
|
BoxRec box;
|
|
RegionRec region;
|
|
|
|
pPriv = DRI2GetDrawable(pDraw);
|
|
if (pPriv == NULL) {
|
|
xf86DrvMsg(pScreen->myNum, X_ERROR,
|
|
"[DRI2] %s: bad drawable\n", __func__);
|
|
return;
|
|
}
|
|
|
|
pPriv->swapsPending--;
|
|
pPriv->swap_count++;
|
|
|
|
box.x1 = 0;
|
|
box.y1 = 0;
|
|
box.x2 = pDraw->width;
|
|
box.y2 = pDraw->height;
|
|
RegionInit(®ion, &box, 0);
|
|
DRI2CopyRegion(pDraw, ®ion, DRI2BufferFakeFrontLeft,
|
|
DRI2BufferFrontLeft);
|
|
|
|
ust = ((CARD64)tv_sec * 1000000) + tv_usec;
|
|
if (swap_complete)
|
|
swap_complete(client, swap_data, type, ust, frame, pPriv->swap_count);
|
|
|
|
pPriv->last_swap_msc = frame;
|
|
pPriv->last_swap_ust = ust;
|
|
|
|
DRI2WakeClient(client, pDraw, frame, tv_sec, tv_usec);
|
|
}
|
|
|
|
Bool
|
|
DRI2WaitSwap(ClientPtr client, DrawablePtr pDrawable)
|
|
{
|
|
DRI2DrawablePtr pPriv = DRI2GetDrawable(pDrawable);
|
|
|
|
/* If we're currently waiting for a swap on this drawable, reset
|
|
* the request and suspend the client. We only support one
|
|
* blocked client per drawable. */
|
|
if ((pPriv->swapsPending) &&
|
|
pPriv->blockedClient == NULL) {
|
|
ResetCurrentRequest(client);
|
|
client->sequence--;
|
|
__DRI2BlockClient(client, pPriv);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
DRI2SwapBuffers(ClientPtr client, DrawablePtr pDraw, CARD64 target_msc,
|
|
CARD64 divisor, CARD64 remainder, CARD64 *swap_target,
|
|
DRI2SwapEventPtr func, void *data)
|
|
{
|
|
ScreenPtr pScreen = pDraw->pScreen;
|
|
DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen);
|
|
DRI2DrawablePtr pPriv;
|
|
DRI2BufferPtr pDestBuffer = NULL, pSrcBuffer = NULL;
|
|
int ret, i;
|
|
CARD64 ust, current_msc;
|
|
|
|
pPriv = DRI2GetDrawable(pDraw);
|
|
if (pPriv == NULL) {
|
|
xf86DrvMsg(pScreen->myNum, X_ERROR,
|
|
"[DRI2] %s: bad drawable\n", __func__);
|
|
return BadDrawable;
|
|
}
|
|
|
|
for (i = 0; i < pPriv->bufferCount; i++) {
|
|
if (pPriv->buffers[i]->attachment == DRI2BufferFrontLeft)
|
|
pDestBuffer = (DRI2BufferPtr) pPriv->buffers[i];
|
|
if (pPriv->buffers[i]->attachment == DRI2BufferBackLeft)
|
|
pSrcBuffer = (DRI2BufferPtr) pPriv->buffers[i];
|
|
}
|
|
if (pSrcBuffer == NULL || pDestBuffer == NULL) {
|
|
xf86DrvMsg(pScreen->myNum, X_ERROR,
|
|
"[DRI2] %s: drawable has no back or front?\n", __func__);
|
|
return BadDrawable;
|
|
}
|
|
|
|
/* Old DDX or no swap interval, just blit */
|
|
if (!ds->ScheduleSwap || !pPriv->swap_interval) {
|
|
BoxRec box;
|
|
RegionRec region;
|
|
|
|
box.x1 = 0;
|
|
box.y1 = 0;
|
|
box.x2 = pDraw->width;
|
|
box.y2 = pDraw->height;
|
|
RegionInit(®ion, &box, 0);
|
|
|
|
pPriv->swapsPending++;
|
|
|
|
(*ds->CopyRegion)(pDraw, ®ion, pDestBuffer, pSrcBuffer);
|
|
DRI2SwapComplete(client, pDraw, target_msc, 0, 0, DRI2_BLIT_COMPLETE,
|
|
func, data);
|
|
return Success;
|
|
}
|
|
|
|
/*
|
|
* In the simple glXSwapBuffers case, all params will be 0, and we just
|
|
* need to schedule a swap for the last swap target + the swap interval.
|
|
*/
|
|
if (target_msc == 0 && divisor == 0 && remainder == 0) {
|
|
/* If the current vblank count of the drawable's crtc is lower
|
|
* than the count stored in last_swap_target from a previous swap
|
|
* then reinitialize last_swap_target to the current crtc's msc,
|
|
* otherwise the swap will hang. This will happen if the drawable
|
|
* is moved to a crtc with a lower refresh rate, or a crtc that just
|
|
* got enabled.
|
|
*/
|
|
if (ds->GetMSC) {
|
|
if (!(*ds->GetMSC)(pDraw, &ust, ¤t_msc))
|
|
pPriv->last_swap_target = 0;
|
|
|
|
if (current_msc < pPriv->last_swap_target)
|
|
pPriv->last_swap_target = current_msc;
|
|
|
|
}
|
|
|
|
/*
|
|
* Swap target for this swap is last swap target + swap interval since
|
|
* we have to account for the current swap count, interval, and the
|
|
* number of pending swaps.
|
|
*/
|
|
*swap_target = pPriv->last_swap_target + pPriv->swap_interval;
|
|
|
|
} else {
|
|
/* glXSwapBuffersMscOML could have a 0 target_msc, honor it */
|
|
*swap_target = target_msc;
|
|
}
|
|
|
|
pPriv->swapsPending++;
|
|
ret = (*ds->ScheduleSwap)(client, pDraw, pDestBuffer, pSrcBuffer,
|
|
swap_target, divisor, remainder, func, data);
|
|
if (!ret) {
|
|
pPriv->swapsPending--; /* didn't schedule */
|
|
xf86DrvMsg(pScreen->myNum, X_ERROR,
|
|
"[DRI2] %s: driver failed to schedule swap\n", __func__);
|
|
return BadDrawable;
|
|
}
|
|
|
|
pPriv->last_swap_target = *swap_target;
|
|
|
|
/* According to spec, return expected swapbuffers count SBC after this swap
|
|
* will complete.
|
|
*/
|
|
*swap_target = pPriv->swap_count + pPriv->swapsPending;
|
|
|
|
DRI2InvalidateDrawable(pDraw);
|
|
|
|
return Success;
|
|
}
|
|
|
|
void
|
|
DRI2SwapInterval(DrawablePtr pDrawable, int interval)
|
|
{
|
|
ScreenPtr pScreen = pDrawable->pScreen;
|
|
DRI2DrawablePtr pPriv = DRI2GetDrawable(pDrawable);
|
|
|
|
if (pPriv == NULL) {
|
|
xf86DrvMsg(pScreen->myNum, X_ERROR,
|
|
"[DRI2] %s: bad drawable\n", __func__);
|
|
return;
|
|
}
|
|
|
|
/* fixme: check against arbitrary max? */
|
|
pPriv->swap_interval = interval;
|
|
}
|
|
|
|
int
|
|
DRI2GetMSC(DrawablePtr pDraw, CARD64 *ust, CARD64 *msc, CARD64 *sbc)
|
|
{
|
|
ScreenPtr pScreen = pDraw->pScreen;
|
|
DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen);
|
|
DRI2DrawablePtr pPriv;
|
|
Bool ret;
|
|
|
|
pPriv = DRI2GetDrawable(pDraw);
|
|
if (pPriv == NULL) {
|
|
xf86DrvMsg(pScreen->myNum, X_ERROR,
|
|
"[DRI2] %s: bad drawable\n", __func__);
|
|
return BadDrawable;
|
|
}
|
|
|
|
if (!ds->GetMSC) {
|
|
*ust = 0;
|
|
*msc = 0;
|
|
*sbc = pPriv->swap_count;
|
|
return Success;
|
|
}
|
|
|
|
/*
|
|
* Spec needs to be updated to include unmapped or redirected
|
|
* drawables
|
|
*/
|
|
|
|
ret = (*ds->GetMSC)(pDraw, ust, msc);
|
|
if (!ret)
|
|
return BadDrawable;
|
|
|
|
*sbc = pPriv->swap_count;
|
|
|
|
return Success;
|
|
}
|
|
|
|
int
|
|
DRI2WaitMSC(ClientPtr client, DrawablePtr pDraw, CARD64 target_msc,
|
|
CARD64 divisor, CARD64 remainder)
|
|
{
|
|
DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen);
|
|
DRI2DrawablePtr pPriv;
|
|
Bool ret;
|
|
|
|
pPriv = DRI2GetDrawable(pDraw);
|
|
if (pPriv == NULL)
|
|
return BadDrawable;
|
|
|
|
/* Old DDX just completes immediately */
|
|
if (!ds->ScheduleWaitMSC) {
|
|
DRI2WaitMSCComplete(client, pDraw, target_msc, 0, 0);
|
|
|
|
return Success;
|
|
}
|
|
|
|
ret = (*ds->ScheduleWaitMSC)(client, pDraw, target_msc, divisor, remainder);
|
|
if (!ret)
|
|
return BadDrawable;
|
|
|
|
return Success;
|
|
}
|
|
|
|
int
|
|
DRI2WaitSBC(ClientPtr client, DrawablePtr pDraw, CARD64 target_sbc)
|
|
{
|
|
DRI2DrawablePtr pPriv;
|
|
|
|
pPriv = DRI2GetDrawable(pDraw);
|
|
if (pPriv == NULL)
|
|
return BadDrawable;
|
|
|
|
/* target_sbc == 0 means to block until all pending swaps are
|
|
* finished. Recalculate target_sbc to get that behaviour.
|
|
*/
|
|
if (target_sbc == 0)
|
|
target_sbc = pPriv->swap_count + pPriv->swapsPending;
|
|
|
|
/* If current swap count already >= target_sbc, reply and
|
|
* return immediately with (ust, msc, sbc) triplet of
|
|
* most recent completed swap.
|
|
*/
|
|
if (pPriv->swap_count >= target_sbc) {
|
|
ProcDRI2WaitMSCReply(client, pPriv->last_swap_ust,
|
|
pPriv->last_swap_msc, pPriv->swap_count);
|
|
return Success;
|
|
}
|
|
|
|
pPriv->target_sbc = target_sbc;
|
|
__DRI2BlockClient(client, pPriv);
|
|
|
|
return Success;
|
|
}
|
|
|
|
Bool
|
|
DRI2HasSwapControl(ScreenPtr pScreen)
|
|
{
|
|
DRI2ScreenPtr ds = DRI2GetScreen(pScreen);
|
|
|
|
return ds->ScheduleSwap && ds->GetMSC;
|
|
}
|
|
|
|
Bool
|
|
DRI2Connect(ScreenPtr pScreen, unsigned int driverType, int *fd,
|
|
const char **driverName, const char **deviceName)
|
|
{
|
|
DRI2ScreenPtr ds;
|
|
|
|
if (!dixPrivateKeyRegistered(dri2ScreenPrivateKey))
|
|
return FALSE;
|
|
|
|
ds = DRI2GetScreen(pScreen);
|
|
if (ds == NULL || driverType >= ds->numDrivers ||
|
|
!ds->driverNames[driverType])
|
|
return FALSE;
|
|
|
|
*fd = ds->fd;
|
|
*driverName = ds->driverNames[driverType];
|
|
*deviceName = ds->deviceName;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
Bool
|
|
DRI2Authenticate(ScreenPtr pScreen, uint32_t magic)
|
|
{
|
|
DRI2ScreenPtr ds = DRI2GetScreen(pScreen);
|
|
|
|
if (ds == NULL || (*ds->AuthMagic)(ds->fd, magic))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static int
|
|
DRI2ConfigNotify(WindowPtr pWin, int x, int y, int w, int h, int bw,
|
|
WindowPtr pSib)
|
|
{
|
|
DrawablePtr pDraw = (DrawablePtr)pWin;
|
|
ScreenPtr pScreen = pDraw->pScreen;
|
|
DRI2ScreenPtr ds = DRI2GetScreen(pScreen);
|
|
DRI2DrawablePtr dd = DRI2GetDrawable(pDraw);
|
|
int ret;
|
|
|
|
if (ds->ConfigNotify) {
|
|
pScreen->ConfigNotify = ds->ConfigNotify;
|
|
|
|
ret = (*pScreen->ConfigNotify)(pWin, x, y, w, h, bw, pSib);
|
|
|
|
ds->ConfigNotify = pScreen->ConfigNotify;
|
|
pScreen->ConfigNotify = DRI2ConfigNotify;
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
if (!dd || (dd->width == w && dd->height == h))
|
|
return Success;
|
|
|
|
DRI2InvalidateDrawable(pDraw);
|
|
return Success;
|
|
}
|
|
|
|
Bool
|
|
DRI2ScreenInit(ScreenPtr pScreen, DRI2InfoPtr info)
|
|
{
|
|
DRI2ScreenPtr ds;
|
|
const char* driverTypeNames[] = {
|
|
"DRI", /* DRI2DriverDRI */
|
|
"VDPAU", /* DRI2DriverVDPAU */
|
|
};
|
|
unsigned int i;
|
|
CARD8 cur_minor;
|
|
|
|
if (info->version < 3)
|
|
return FALSE;
|
|
|
|
if (!xf86VGAarbiterAllowDRI(pScreen)) {
|
|
xf86DrvMsg(pScreen->myNum, X_WARNING,
|
|
"[DRI2] Direct rendering is not supported when VGA arb is necessary for the device\n");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!dixRegisterPrivateKey(&dri2ScreenPrivateKeyRec, PRIVATE_SCREEN, 0))
|
|
return FALSE;
|
|
|
|
if (!dixRegisterPrivateKey(&dri2WindowPrivateKeyRec, PRIVATE_WINDOW, 0))
|
|
return FALSE;
|
|
|
|
if (!dixRegisterPrivateKey(&dri2PixmapPrivateKeyRec, PRIVATE_PIXMAP, 0))
|
|
return FALSE;
|
|
|
|
ds = calloc(1, sizeof *ds);
|
|
if (!ds)
|
|
return FALSE;
|
|
|
|
ds->screen = pScreen;
|
|
ds->fd = info->fd;
|
|
ds->deviceName = info->deviceName;
|
|
dri2_major = 1;
|
|
|
|
ds->CreateBuffer = info->CreateBuffer;
|
|
ds->DestroyBuffer = info->DestroyBuffer;
|
|
ds->CopyRegion = info->CopyRegion;
|
|
|
|
if (info->version >= 4) {
|
|
ds->ScheduleSwap = info->ScheduleSwap;
|
|
ds->ScheduleWaitMSC = info->ScheduleWaitMSC;
|
|
ds->GetMSC = info->GetMSC;
|
|
cur_minor = 3;
|
|
} else {
|
|
cur_minor = 1;
|
|
}
|
|
|
|
if (info->version >= 5) {
|
|
ds->AuthMagic = info->AuthMagic;
|
|
}
|
|
|
|
/*
|
|
* if the driver doesn't provide an AuthMagic function or the info struct
|
|
* version is too low, it relies on the old method (using libdrm) or fail
|
|
*/
|
|
if (!ds->AuthMagic)
|
|
#ifdef WITH_LIBDRM
|
|
ds->AuthMagic = drmAuthMagic;
|
|
#else
|
|
goto err_out;
|
|
#endif
|
|
|
|
/* Initialize minor if needed and set to minimum provied by DDX */
|
|
if (!dri2_minor || dri2_minor > cur_minor)
|
|
dri2_minor = cur_minor;
|
|
|
|
if (info->version == 3 || info->numDrivers == 0) {
|
|
/* Driver too old: use the old-style driverName field */
|
|
ds->numDrivers = 1;
|
|
ds->driverNames = malloc(sizeof(*ds->driverNames));
|
|
if (!ds->driverNames)
|
|
goto err_out;
|
|
ds->driverNames[0] = info->driverName;
|
|
} else {
|
|
ds->numDrivers = info->numDrivers;
|
|
ds->driverNames = malloc(info->numDrivers * sizeof(*ds->driverNames));
|
|
if (!ds->driverNames)
|
|
goto err_out;
|
|
memcpy(ds->driverNames, info->driverNames,
|
|
info->numDrivers * sizeof(*ds->driverNames));
|
|
}
|
|
|
|
dixSetPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey, ds);
|
|
|
|
ds->ConfigNotify = pScreen->ConfigNotify;
|
|
pScreen->ConfigNotify = DRI2ConfigNotify;
|
|
|
|
xf86DrvMsg(pScreen->myNum, X_INFO, "[DRI2] Setup complete\n");
|
|
for (i = 0; i < sizeof(driverTypeNames) / sizeof(driverTypeNames[0]); i++) {
|
|
if (i < ds->numDrivers && ds->driverNames[i]) {
|
|
xf86DrvMsg(pScreen->myNum, X_INFO, "[DRI2] %s driver: %s\n",
|
|
driverTypeNames[i], ds->driverNames[i]);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
err_out:
|
|
xf86DrvMsg(pScreen->myNum, X_WARNING,
|
|
"[DRI2] Initialization failed for info version %d.\n", info->version);
|
|
free(ds);
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
DRI2CloseScreen(ScreenPtr pScreen)
|
|
{
|
|
DRI2ScreenPtr ds = DRI2GetScreen(pScreen);
|
|
|
|
free(ds->driverNames);
|
|
free(ds);
|
|
dixSetPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey, NULL);
|
|
}
|
|
|
|
extern ExtensionModule dri2ExtensionModule;
|
|
|
|
static pointer
|
|
DRI2Setup(pointer module, pointer opts, int *errmaj, int *errmin)
|
|
{
|
|
static Bool setupDone = FALSE;
|
|
|
|
dri2DrawableRes = CreateNewResourceType(DRI2DrawableGone, "DRI2Drawable");
|
|
|
|
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, 2, 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;
|
|
}
|