xenocara/xserver/glx/glxdri.c

1159 lines
35 KiB
C

/*
* Copyright © 2006 Red Hat, Inc
*
* Permission to use, copy, modify, distribute, and sell this software
* and its documentation for any purpose is hereby granted without
* fee, provided that the above copyright notice appear in all copies
* and that both that copyright notice and this permission notice
* appear in supporting documentation, and that the name of Red Hat,
* Inc not be used in advertising or publicity pertaining to
* distribution of the software without specific, written prior
* permission. Red Hat, Inc makes no representations about the
* suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* RED HAT, INC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
* NO EVENT SHALL RED HAT, INC BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL 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 PERFORMANCE OF THIS SOFTWARE.
*/
#ifdef HAVE_DIX_CONFIG_H
#include <dix-config.h>
#endif
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>
#include <dlfcn.h>
#include <drm.h>
#include <GL/gl.h>
#include <GL/internal/dri_interface.h>
#include <GL/glxtokens.h>
#include <windowstr.h>
#include <os.h>
#include <damage.h>
#define _XF86DRI_SERVER_
#include <drm_sarea.h>
#include <xf86drm.h>
#include <X11/dri/xf86driproto.h>
#include <xf86str.h>
#include <xf86.h>
#include <dri.h>
#include "servermd.h"
#define DRI_NEW_INTERFACE_ONLY
#include "glxserver.h"
#include "glxutil.h"
#include "glxdricommon.h"
#include "glapitable.h"
#include "glapi.h"
#include "glthread.h"
#include "dispatch.h"
#include "extension_string.h"
typedef struct __GLXDRIscreen __GLXDRIscreen;
typedef struct __GLXDRIcontext __GLXDRIcontext;
typedef struct __GLXDRIdrawable __GLXDRIdrawable;
struct __GLXDRIscreen {
__GLXscreen base;
__DRIscreen *driScreen;
void *driver;
xf86EnterVTProc *enterVT;
xf86LeaveVTProc *leaveVT;
const __DRIcoreExtension *core;
const __DRIlegacyExtension *legacy;
const __DRIcopySubBufferExtension *copySubBuffer;
const __DRIswapControlExtension *swapControl;
const __DRIconfig **driConfigs;
#ifdef __DRI_TEX_OFFSET
const __DRItexOffsetExtension *texOffset;
DRITexOffsetStartProcPtr texOffsetStart;
DRITexOffsetFinishProcPtr texOffsetFinish;
__GLXDRIdrawable *texOffsetOverride[16];
GLuint lastTexOffsetOverride;
#endif
unsigned char glx_enable_bits[__GLX_EXT_BYTES];
};
struct __GLXDRIcontext {
__GLXcontext base;
__DRIcontext *driContext;
XID hwContextID;
};
struct __GLXDRIdrawable {
__GLXdrawable base;
__DRIdrawable *driDrawable;
/* Pulled in from old __GLXpixmap */
#ifdef __DRI_TEX_OFFSET
GLint texname;
__GLXDRIcontext *ctx;
unsigned long long offset;
DamagePtr pDamage;
#endif
};
static void
__glXDRIleaveServer(GLboolean rendering)
{
int i;
for (i = 0; rendering && i < screenInfo.numScreens; i++) {
__GLXDRIscreen *const screen =
(__GLXDRIscreen *) glxGetScreen(screenInfo.screens[i]);
GLuint lastOverride = screen->lastTexOffsetOverride;
if (lastOverride) {
__GLXDRIdrawable **texOffsetOverride = screen->texOffsetOverride;
int j;
for (j = 0; j < lastOverride; j++) {
__GLXDRIdrawable *pGlxPix = texOffsetOverride[j];
if (pGlxPix && pGlxPix->texname) {
pGlxPix->offset =
screen->texOffsetStart((PixmapPtr) pGlxPix->base.pDraw);
}
}
}
}
DRIBlockHandler(NULL, NULL, NULL);
for (i = 0; rendering && i < screenInfo.numScreens; i++) {
__GLXDRIscreen *const screen =
(__GLXDRIscreen *) glxGetScreen(screenInfo.screens[i]);
GLuint lastOverride = screen->lastTexOffsetOverride;
if (lastOverride) {
__GLXDRIdrawable **texOffsetOverride = screen->texOffsetOverride;
int j;
for (j = 0; j < lastOverride; j++) {
__GLXDRIdrawable *pGlxPix = texOffsetOverride[j];
if (pGlxPix && pGlxPix->texname) {
screen->texOffset->setTexOffset(pGlxPix->ctx->driContext,
pGlxPix->texname,
pGlxPix->offset,
pGlxPix->base.pDraw->depth,
((PixmapPtr) pGlxPix->base.
pDraw)->devKind);
}
}
}
}
}
static void
__glXDRIenterServer(GLboolean rendering)
{
int i;
for (i = 0; rendering && i < screenInfo.numScreens; i++) {
__GLXDRIscreen *const screen = (__GLXDRIscreen *)
glxGetScreen(screenInfo.screens[i]);
if (screen->lastTexOffsetOverride) {
CALL_Flush(GET_DISPATCH(), ());
break;
}
}
DRIWakeupHandler(NULL, 0, NULL);
}
static void
__glXDRIdoReleaseTexImage(__GLXDRIscreen * screen, __GLXDRIdrawable * drawable)
{
GLuint lastOverride = screen->lastTexOffsetOverride;
if (lastOverride) {
__GLXDRIdrawable **texOffsetOverride = screen->texOffsetOverride;
int i;
for (i = 0; i < lastOverride; i++) {
if (texOffsetOverride[i] == drawable) {
if (screen->texOffsetFinish)
screen->texOffsetFinish((PixmapPtr) drawable->base.pDraw);
texOffsetOverride[i] = NULL;
if (i + 1 == lastOverride) {
lastOverride = 0;
while (i--) {
if (texOffsetOverride[i]) {
lastOverride = i + 1;
break;
}
}
screen->lastTexOffsetOverride = lastOverride;
break;
}
}
}
}
}
static void
__glXDRIdrawableDestroy(__GLXdrawable * drawable)
{
__GLXDRIdrawable *private = (__GLXDRIdrawable *) drawable;
__GLXDRIscreen *screen;
int i;
for (i = 0; i < screenInfo.numScreens; i++) {
screen = (__GLXDRIscreen *) glxGetScreen(screenInfo.screens[i]);
__glXDRIdoReleaseTexImage(screen, private);
}
/* If the X window was destroyed, the dri DestroyWindow hook will
* aready have taken care of this, so only call if pDraw isn't NULL. */
if (drawable->pDraw != NULL) {
screen = (__GLXDRIscreen *) glxGetScreen(drawable->pDraw->pScreen);
(*screen->core->destroyDrawable) (private->driDrawable);
__glXenterServer(GL_FALSE);
DRIDestroyDrawable(drawable->pDraw->pScreen,
serverClient, drawable->pDraw);
__glXleaveServer(GL_FALSE);
}
__glXDrawableRelease(drawable);
free(private);
}
static GLboolean
__glXDRIdrawableSwapBuffers(ClientPtr client, __GLXdrawable * basePrivate)
{
__GLXDRIdrawable *private = (__GLXDRIdrawable *) basePrivate;
__GLXDRIscreen *screen =
(__GLXDRIscreen *) glxGetScreen(basePrivate->pDraw->pScreen);
(*screen->core->swapBuffers) (private->driDrawable);
return TRUE;
}
static int
__glXDRIdrawableSwapInterval(__GLXdrawable * baseDrawable, int interval)
{
__GLXDRIdrawable *draw = (__GLXDRIdrawable *) baseDrawable;
__GLXDRIscreen *screen =
(__GLXDRIscreen *) glxGetScreen(baseDrawable->pDraw->pScreen);
if (screen->swapControl)
screen->swapControl->setSwapInterval(draw->driDrawable, interval);
return 0;
}
static void
__glXDRIdrawableCopySubBuffer(__GLXdrawable * basePrivate,
int x, int y, int w, int h)
{
__GLXDRIdrawable *private = (__GLXDRIdrawable *) basePrivate;
__GLXDRIscreen *screen = (__GLXDRIscreen *)
glxGetScreen(basePrivate->pDraw->pScreen);
if (screen->copySubBuffer)
screen->copySubBuffer->copySubBuffer(private->driDrawable, x, y, w, h);
}
static void
__glXDRIcontextDestroy(__GLXcontext * baseContext)
{
__GLXDRIcontext *context = (__GLXDRIcontext *) baseContext;
__GLXDRIscreen *screen = (__GLXDRIscreen *) context->base.pGlxScreen;
Bool retval;
screen->core->destroyContext(context->driContext);
__glXenterServer(GL_FALSE);
retval = DRIDestroyContext(baseContext->pGlxScreen->pScreen,
context->hwContextID);
__glXleaveServer(GL_FALSE);
__glXContextDestroy(&context->base);
free(context);
}
static int
__glXDRIcontextMakeCurrent(__GLXcontext * baseContext)
{
__GLXDRIcontext *context = (__GLXDRIcontext *) baseContext;
__GLXDRIscreen *screen = (__GLXDRIscreen *) context->base.pGlxScreen;
__GLXDRIdrawable *draw = (__GLXDRIdrawable *) baseContext->drawPriv;
__GLXDRIdrawable *read = (__GLXDRIdrawable *) baseContext->readPriv;
return (*screen->core->bindContext) (context->driContext,
draw->driDrawable, read->driDrawable);
}
static int
__glXDRIcontextLoseCurrent(__GLXcontext * baseContext)
{
__GLXDRIcontext *context = (__GLXDRIcontext *) baseContext;
__GLXDRIscreen *screen = (__GLXDRIscreen *) context->base.pGlxScreen;
return (*screen->core->unbindContext) (context->driContext);
}
static int
__glXDRIcontextCopy(__GLXcontext * baseDst, __GLXcontext * baseSrc,
unsigned long mask)
{
__GLXDRIcontext *dst = (__GLXDRIcontext *) baseDst;
__GLXDRIcontext *src = (__GLXDRIcontext *) baseSrc;
__GLXDRIscreen *screen = (__GLXDRIscreen *) dst->base.pGlxScreen;
return (*screen->core->copyContext) (dst->driContext,
src->driContext, mask);
}
static void
glxFillAlphaChannel(CARD32 *pixels, CARD32 rowstride, int width, int height)
{
int i;
CARD32 *p, *end;
rowstride /= 4;
for (i = 0; i < height; i++) {
p = pixels;
end = p + width;
while (p < end)
*p++ |= 0xFF000000;
pixels += rowstride;
}
}
static Bool
testTexOffset(__GLXDRIscreen * const screen, PixmapPtr pPixmap)
{
Bool ret;
if (!screen->texOffsetStart || !screen->texOffset)
return FALSE;
__glXenterServer(GL_FALSE);
ret = screen->texOffsetStart(pPixmap) != ~0ULL;
__glXleaveServer(GL_FALSE);
return ret;
}
/*
* (sticking this here for lack of a better place)
* Known issues with the GLX_EXT_texture_from_pixmap implementation:
* - In general we ignore the fbconfig, lots of examples follow
* - No fbconfig handling for multiple mipmap levels
* - No fbconfig handling for 1D textures
* - No fbconfig handling for TEXTURE_TARGET
* - No fbconfig exposure of Y inversion state
* - No GenerateMipmapEXT support (due to no FBO support)
* - No support for anything but 16bpp and 32bpp-sparse pixmaps
*/
static int
__glXDRIbindTexImage(__GLXcontext * baseContext,
int buffer, __GLXdrawable * glxPixmap)
{
RegionPtr pRegion = NULL;
PixmapPtr pixmap;
int bpp, override = 0, texname;
GLenum format, type;
ScreenPtr pScreen = glxPixmap->pDraw->pScreen;
__GLXDRIdrawable *driDraw = (__GLXDRIdrawable *) glxPixmap;
__GLXDRIscreen *const screen = (__GLXDRIscreen *) glxGetScreen(pScreen);
CALL_GetIntegerv(GET_DISPATCH(), (glxPixmap->target == GL_TEXTURE_2D ?
GL_TEXTURE_BINDING_2D :
GL_TEXTURE_BINDING_RECTANGLE_NV,
&texname));
if (!texname)
return __glXError(GLXBadContextState);
pixmap = (PixmapPtr) glxPixmap->pDraw;
if (testTexOffset(screen, pixmap)) {
__GLXDRIdrawable **texOffsetOverride = screen->texOffsetOverride;
int i, firstEmpty = 16;
for (i = 0; i < 16; i++) {
if (texOffsetOverride[i] == driDraw)
goto alreadyin;
if (firstEmpty == 16 && !texOffsetOverride[i])
firstEmpty = i;
}
if (firstEmpty == 16) {
ErrorF("%s: Failed to register texture offset override\n",
__func__);
goto nooverride;
}
if (firstEmpty >= screen->lastTexOffsetOverride)
screen->lastTexOffsetOverride = firstEmpty + 1;
texOffsetOverride[firstEmpty] = driDraw;
alreadyin:
override = 1;
driDraw->ctx = (__GLXDRIcontext *) baseContext;
if (texname == driDraw->texname)
return Success;
driDraw->texname = texname;
screen->texOffset->setTexOffset(driDraw->ctx->driContext, texname, 0,
pixmap->drawable.depth,
pixmap->devKind);
}
nooverride:
if (!driDraw->pDamage) {
if (!override) {
driDraw->pDamage = DamageCreate(NULL, NULL, DamageReportNone,
TRUE, pScreen, NULL);
if (!driDraw->pDamage)
return BadAlloc;
DamageRegister((DrawablePtr) pixmap, driDraw->pDamage);
}
pRegion = NULL;
}
else {
pRegion = DamageRegion(driDraw->pDamage);
if (RegionNil(pRegion))
return Success;
}
/* XXX 24bpp packed, 8, etc */
if (pixmap->drawable.depth >= 24) {
bpp = 4;
format = GL_BGRA;
type =
#if X_BYTE_ORDER == X_BIG_ENDIAN
!override ? GL_UNSIGNED_INT_8_8_8_8_REV :
#endif
GL_UNSIGNED_BYTE;
}
else {
bpp = 2;
format = GL_RGB;
type = GL_UNSIGNED_SHORT_5_6_5;
}
if (pRegion == NULL) {
void *data = NULL;
if (!override) {
unsigned pitch = PixmapBytePad(pixmap->drawable.width,
pixmap->drawable.depth);
data = malloc(pitch * pixmap->drawable.height);
__glXenterServer(GL_FALSE);
pScreen->GetImage(&pixmap->drawable, 0 /*pixmap->drawable.x */ ,
0 /*pixmap->drawable.y */ ,
pixmap->drawable.width,
pixmap->drawable.height, ZPixmap, ~0, data);
__glXleaveServer(GL_FALSE);
if (pixmap->drawable.depth == 24)
glxFillAlphaChannel(data,
pitch,
pixmap->drawable.width,
pixmap->drawable.height);
CALL_PixelStorei(GET_DISPATCH(), (GL_UNPACK_ROW_LENGTH,
pitch / bpp));
CALL_PixelStorei(GET_DISPATCH(), (GL_UNPACK_SKIP_PIXELS, 0));
CALL_PixelStorei(GET_DISPATCH(), (GL_UNPACK_SKIP_ROWS, 0));
}
CALL_TexImage2D(GET_DISPATCH(),
(glxPixmap->target,
0,
bpp == 4 ? 4 : 3,
pixmap->drawable.width,
pixmap->drawable.height, 0, format, type, data));
free(data);
}
else if (!override) {
int i, numRects;
BoxPtr p;
numRects = RegionNumRects(pRegion);
p = RegionRects(pRegion);
CALL_PixelStorei(GET_DISPATCH(), (GL_UNPACK_SKIP_PIXELS, 0));
CALL_PixelStorei(GET_DISPATCH(), (GL_UNPACK_SKIP_ROWS, 0));
for (i = 0; i < numRects; i++) {
unsigned pitch = PixmapBytePad(p[i].x2 - p[i].x1,
pixmap->drawable.depth);
void *data = malloc(pitch * (p[i].y2 - p[i].y1));
__glXenterServer(GL_FALSE);
pScreen->GetImage(&pixmap->drawable, /*pixmap->drawable.x + */
p[i].x1,
/*pixmap->drawable.y */ +p[i].y1,
p[i].x2 - p[i].x1,
p[i].y2 - p[i].y1, ZPixmap, ~0, data);
__glXleaveServer(GL_FALSE);
if (pixmap->drawable.depth == 24)
glxFillAlphaChannel(data,
pitch,
p[i].x2 - p[i].x1, p[i].y2 - p[i].y1);
CALL_PixelStorei(GET_DISPATCH(), (GL_UNPACK_ROW_LENGTH,
pitch / bpp));
CALL_TexSubImage2D(GET_DISPATCH(),
(glxPixmap->target,
0,
p[i].x1, p[i].y1,
p[i].x2 - p[i].x1, p[i].y2 - p[i].y1,
format, type, data));
free(data);
}
}
if (!override)
DamageEmpty(driDraw->pDamage);
return Success;
}
static int
__glXDRIreleaseTexImage(__GLXcontext * baseContext,
int buffer, __GLXdrawable * pixmap)
{
__GLXDRIscreen *screen =
(__GLXDRIscreen *) glxGetScreen(pixmap->pDraw->pScreen);
__GLXDRIdrawable *drawable = (__GLXDRIdrawable *) pixmap;
__glXDRIdoReleaseTexImage(screen, drawable);
return Success;
}
static __GLXtextureFromPixmap __glXDRItextureFromPixmap = {
__glXDRIbindTexImage,
__glXDRIreleaseTexImage
};
static void
__glXDRIscreenDestroy(__GLXscreen * baseScreen)
{
int i;
__GLXDRIscreen *screen = (__GLXDRIscreen *) baseScreen;
screen->core->destroyScreen(screen->driScreen);
dlclose(screen->driver);
__glXScreenDestroy(baseScreen);
if (screen->driConfigs) {
for (i = 0; screen->driConfigs[i] != NULL; i++)
free((__DRIconfig **) screen->driConfigs[i]);
free(screen->driConfigs);
}
free(screen);
}
static __GLXcontext *
__glXDRIscreenCreateContext(__GLXscreen * baseScreen,
__GLXconfig * glxConfig,
__GLXcontext * baseShareContext)
{
__GLXDRIscreen *screen = (__GLXDRIscreen *) baseScreen;
__GLXDRIcontext *context, *shareContext;
__GLXDRIconfig *config = (__GLXDRIconfig *) glxConfig;
VisualPtr visual;
int i;
GLboolean retval;
__DRIcontext *driShare;
drm_context_t hwContext;
ScreenPtr pScreen = baseScreen->pScreen;
shareContext = (__GLXDRIcontext *) baseShareContext;
if (shareContext)
driShare = shareContext->driContext;
else
driShare = NULL;
if (baseShareContext && baseShareContext->isDirect)
return NULL;
context = calloc(1, sizeof *context);
if (context == NULL)
return NULL;
context->base.destroy = __glXDRIcontextDestroy;
context->base.makeCurrent = __glXDRIcontextMakeCurrent;
context->base.loseCurrent = __glXDRIcontextLoseCurrent;
context->base.copy = __glXDRIcontextCopy;
context->base.textureFromPixmap = &__glXDRItextureFromPixmap;
/* Find the requested X visual */
visual = pScreen->visuals;
for (i = 0; i < pScreen->numVisuals; i++, visual++)
if (visual->vid == glxConfig->visualID)
break;
if (i == pScreen->numVisuals)
return NULL;
context->hwContextID = FakeClientID(0);
__glXenterServer(GL_FALSE);
retval = DRICreateContext(baseScreen->pScreen, visual,
context->hwContextID, &hwContext);
__glXleaveServer(GL_FALSE);
if (!retval)
return NULL;
context->driContext = screen->legacy->createNewContext(screen->driScreen, config->driConfig, 0, /* render type */
driShare,
hwContext, context);
if (context->driContext == NULL) {
__glXenterServer(GL_FALSE);
retval = DRIDestroyContext(baseScreen->pScreen, context->hwContextID);
__glXleaveServer(GL_FALSE);
free(context);
return NULL;
}
return &context->base;
}
static __GLXdrawable *
__glXDRIscreenCreateDrawable(ClientPtr client,
__GLXscreen * screen,
DrawablePtr pDraw,
XID drawId,
int type, XID glxDrawId, __GLXconfig * glxConfig)
{
__GLXDRIscreen *driScreen = (__GLXDRIscreen *) screen;
__GLXDRIconfig *config = (__GLXDRIconfig *) glxConfig;
__GLXDRIdrawable *private;
GLboolean retval;
drm_drawable_t hwDrawable;
private = calloc(1, sizeof *private);
if (private == NULL)
return NULL;
if (!__glXDrawableInit(&private->base, screen,
pDraw, type, glxDrawId, glxConfig)) {
free(private);
return NULL;
}
private->base.destroy = __glXDRIdrawableDestroy;
private->base.swapBuffers = __glXDRIdrawableSwapBuffers;
private->base.copySubBuffer = __glXDRIdrawableCopySubBuffer;
private->base.waitX = NULL;
private->base.waitGL = NULL;
__glXenterServer(GL_FALSE);
retval = DRICreateDrawable(screen->pScreen, serverClient,
pDraw, &hwDrawable);
__glXleaveServer(GL_FALSE);
if (!retval) {
free(private);
return NULL;
}
/* The last argument is 'attrs', which is used with pbuffers which
* we currently don't support. */
private->driDrawable =
(driScreen->legacy->createNewDrawable) (driScreen->driScreen,
config->driConfig,
hwDrawable, 0, NULL, private);
if (private->driDrawable == NULL) {
__glXenterServer(GL_FALSE);
DRIDestroyDrawable(screen->pScreen, serverClient, pDraw);
__glXleaveServer(GL_FALSE);
free(private);
return NULL;
}
return &private->base;
}
static GLboolean
getDrawableInfo(__DRIdrawable * driDrawable,
unsigned int *index, unsigned int *stamp,
int *x, int *y, int *width, int *height,
int *numClipRects, drm_clip_rect_t ** ppClipRects,
int *backX, int *backY,
int *numBackClipRects, drm_clip_rect_t ** ppBackClipRects,
void *data)
{
__GLXDRIdrawable *drawable = data;
ScreenPtr pScreen;
drm_clip_rect_t *pClipRects, *pBackClipRects;
GLboolean retval;
size_t size;
/* If the X window has been destroyed, give up here. */
if (drawable->base.pDraw == NULL)
return GL_FALSE;
pScreen = drawable->base.pDraw->pScreen;
__glXenterServer(GL_FALSE);
retval = DRIGetDrawableInfo(pScreen, drawable->base.pDraw, index, stamp,
x, y, width, height,
numClipRects, &pClipRects,
backX, backY,
numBackClipRects, &pBackClipRects);
__glXleaveServer(GL_FALSE);
if (retval && *numClipRects > 0) {
size = sizeof(drm_clip_rect_t) * *numClipRects;
*ppClipRects = malloc(size);
/* Clip cliprects to screen dimensions (redirected windows) */
if (*ppClipRects != NULL) {
int i, j;
for (i = 0, j = 0; i < *numClipRects; i++) {
(*ppClipRects)[j].x1 = max(pClipRects[i].x1, 0);
(*ppClipRects)[j].y1 = max(pClipRects[i].y1, 0);
(*ppClipRects)[j].x2 = min(pClipRects[i].x2, pScreen->width);
(*ppClipRects)[j].y2 = min(pClipRects[i].y2, pScreen->height);
if ((*ppClipRects)[j].x1 < (*ppClipRects)[j].x2 &&
(*ppClipRects)[j].y1 < (*ppClipRects)[j].y2) {
j++;
}
}
if (*numClipRects != j) {
*numClipRects = j;
*ppClipRects = realloc(*ppClipRects,
sizeof(drm_clip_rect_t) * *numClipRects);
}
}
else
*numClipRects = 0;
}
else {
*ppClipRects = NULL;
*numClipRects = 0;
}
if (retval && *numBackClipRects > 0) {
size = sizeof(drm_clip_rect_t) * *numBackClipRects;
*ppBackClipRects = malloc(size);
if (*ppBackClipRects != NULL)
memcpy(*ppBackClipRects, pBackClipRects, size);
else
*numBackClipRects = 0;
}
else {
*ppBackClipRects = NULL;
*numBackClipRects = 0;
}
return retval;
}
static void
__glXReportDamage(__DRIdrawable * driDraw,
int x, int y,
drm_clip_rect_t * rects, int num_rects,
GLboolean front_buffer, void *data)
{
__GLXDRIdrawable *drawable = data;
DrawablePtr pDraw = drawable->base.pDraw;
RegionRec region;
__glXenterServer(GL_FALSE);
if (RegionInitBoxes(&region, (BoxPtr) rects, num_rects)) {
RegionTranslate(&region, pDraw->x, pDraw->y);
DamageDamageRegion(pDraw, &region);
RegionUninit(&region);
}
else {
while (num_rects--) {
RegionInit(&region, (BoxPtr) rects++, 1);
RegionTranslate(&region, pDraw->x, pDraw->y);
DamageDamageRegion(pDraw, &region);
RegionUninit(&region);
}
}
__glXleaveServer(GL_FALSE);
}
static const __DRIgetDrawableInfoExtension getDrawableInfoExtension = {
{__DRI_GET_DRAWABLE_INFO, __DRI_GET_DRAWABLE_INFO_VERSION},
getDrawableInfo
};
static const __DRIdamageExtension damageExtension = {
{__DRI_DAMAGE, __DRI_DAMAGE_VERSION},
__glXReportDamage,
};
static const __DRIextension *loader_extensions[] = {
&systemTimeExtension.base,
&getDrawableInfoExtension.base,
&damageExtension.base,
NULL
};
static Bool
glxDRIEnterVT(int index, int flags)
{
ScrnInfoPtr scrn = xf86Screens[index];
Bool ret;
__GLXDRIscreen *screen = (__GLXDRIscreen *)
glxGetScreen(screenInfo.screens[index]);
LogMessage(X_INFO, "AIGLX: Resuming AIGLX clients after VT switch\n");
scrn->EnterVT = screen->enterVT;
ret = scrn->EnterVT(index, flags);
screen->enterVT = scrn->EnterVT;
scrn->EnterVT = glxDRIEnterVT;
if (!ret)
return FALSE;
glxResumeClients();
return TRUE;
}
static void
glxDRILeaveVT(int index, int flags)
{
ScrnInfoPtr scrn = xf86Screens[index];
__GLXDRIscreen *screen = (__GLXDRIscreen *)
glxGetScreen(screenInfo.screens[index]);
LogMessage(X_INFO, "AIGLX: Suspending AIGLX clients for VT switch\n");
glxSuspendClients();
scrn->LeaveVT = screen->leaveVT;
(*screen->leaveVT) (index, flags);
screen->leaveVT = scrn->LeaveVT;
scrn->LeaveVT = glxDRILeaveVT;
}
static void
initializeExtensions(__GLXDRIscreen * screen)
{
const __DRIextension **extensions;
int i;
extensions = screen->core->getExtensions(screen->driScreen);
for (i = 0; extensions[i]; i++) {
#ifdef __DRI_READ_DRAWABLE
if (strcmp(extensions[i]->name, __DRI_READ_DRAWABLE) == 0) {
__glXEnableExtension(screen->glx_enable_bits,
"GLX_SGI_make_current_read");
LogMessage(X_INFO, "AIGLX: enabled GLX_SGI_make_current_read\n");
}
#endif
#ifdef __DRI_COPY_SUB_BUFFER
if (strcmp(extensions[i]->name, __DRI_COPY_SUB_BUFFER) == 0) {
screen->copySubBuffer =
(__DRIcopySubBufferExtension *) extensions[i];
__glXEnableExtension(screen->glx_enable_bits,
"GLX_MESA_copy_sub_buffer");
LogMessage(X_INFO, "AIGLX: enabled GLX_MESA_copy_sub_buffer\n");
}
#endif
#ifdef __DRI_SWAP_CONTROL
if (strcmp(extensions[i]->name, __DRI_SWAP_CONTROL) == 0) {
screen->swapControl = (__DRIswapControlExtension *) extensions[i];
__glXEnableExtension(screen->glx_enable_bits,
"GLX_SGI_swap_control");
__glXEnableExtension(screen->glx_enable_bits,
"GLX_MESA_swap_control");
LogMessage(X_INFO,
"AIGLX: enabled GLX_SGI_swap_control and GLX_MESA_swap_control\n");
}
#endif
#ifdef __DRI_TEX_OFFSET
if (strcmp(extensions[i]->name, __DRI_TEX_OFFSET) == 0) {
screen->texOffset = (__DRItexOffsetExtension *) extensions[i];
LogMessage(X_INFO,
"AIGLX: enabled GLX_texture_from_pixmap with driver support\n");
}
#endif
/* Ignore unknown extensions */
}
}
static __GLXscreen *
__glXDRIscreenProbe(ScreenPtr pScreen)
{
drm_handle_t hSAREA;
drmAddress pSAREA = NULL;
char *BusID;
__DRIversion ddx_version;
__DRIversion dri_version;
__DRIversion drm_version;
__DRIframebuffer framebuffer;
int fd = -1;
int status;
drm_magic_t magic;
drmVersionPtr version;
int newlyopened;
char *driverName;
drm_handle_t hFB;
int junk;
__GLXDRIscreen *screen;
Bool isCapable;
size_t buffer_size;
ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
if (!xf86LoaderCheckSymbol("DRIQueryDirectRenderingCapable") ||
!DRIQueryDirectRenderingCapable(pScreen, &isCapable) || !isCapable) {
LogMessage(X_INFO,
"AIGLX: Screen %d is not DRI capable\n", pScreen->myNum);
return NULL;
}
screen = calloc(1, sizeof *screen);
if (screen == NULL)
return NULL;
screen->base.destroy = __glXDRIscreenDestroy;
screen->base.createContext = __glXDRIscreenCreateContext;
screen->base.createDrawable = __glXDRIscreenCreateDrawable;
screen->base.swapInterval = __glXDRIdrawableSwapInterval;
screen->base.pScreen = pScreen;
__glXInitExtensionEnableBits(screen->glx_enable_bits);
/* DRI protocol version. */
dri_version.major = XF86DRI_MAJOR_VERSION;
dri_version.minor = XF86DRI_MINOR_VERSION;
dri_version.patch = XF86DRI_PATCH_VERSION;
if (!DRIOpenConnection(pScreen, &hSAREA, &BusID)) {
LogMessage(X_ERROR, "AIGLX error: DRIOpenConnection failed\n");
goto handle_error;
}
fd = drmOpenOnce(NULL, BusID, &newlyopened);
if (fd < 0) {
LogMessage(X_ERROR, "AIGLX error: drmOpenOnce failed (%s)\n",
strerror(-fd));
goto handle_error;
}
if (drmGetMagic(fd, &magic)) {
LogMessage(X_ERROR, "AIGLX error: drmGetMagic failed\n");
goto handle_error;
}
version = drmGetVersion(fd);
if (version) {
drm_version.major = version->version_major;
drm_version.minor = version->version_minor;
drm_version.patch = version->version_patchlevel;
drmFreeVersion(version);
}
else {
drm_version.major = -1;
drm_version.minor = -1;
drm_version.patch = -1;
}
if (newlyopened && !DRIAuthConnection(pScreen, magic)) {
LogMessage(X_ERROR, "AIGLX error: DRIAuthConnection failed\n");
goto handle_error;
}
/* Get device name (like "tdfx") and the ddx version numbers.
* We'll check the version in each DRI driver's "createNewScreen"
* function. */
if (!DRIGetClientDriverName(pScreen,
&ddx_version.major,
&ddx_version.minor,
&ddx_version.patch, &driverName)) {
LogMessage(X_ERROR, "AIGLX error: DRIGetClientDriverName failed\n");
goto handle_error;
}
screen->driver = glxProbeDriver(driverName,
(void **) &screen->core,
__DRI_CORE, __DRI_CORE_VERSION,
(void **) &screen->legacy,
__DRI_LEGACY, __DRI_LEGACY_VERSION);
if (screen->driver == NULL) {
goto handle_error;
}
/*
* Get device-specific info. pDevPriv will point to a struct
* (such as DRIRADEONRec in xfree86/driver/ati/radeon_dri.h) that
* has information about the screen size, depth, pitch, ancilliary
* buffers, DRM mmap handles, etc.
*/
if (!DRIGetDeviceInfo(pScreen, &hFB, &junk,
&framebuffer.size, &framebuffer.stride,
&framebuffer.dev_priv_size, &framebuffer.dev_priv)) {
LogMessage(X_ERROR, "AIGLX error: XF86DRIGetDeviceInfo failed\n");
goto handle_error;
}
framebuffer.width = pScreen->width;
framebuffer.height = pScreen->height;
/* Map the framebuffer region. */
status = drmMap(fd, hFB, framebuffer.size,
(drmAddressPtr) & framebuffer.base);
if (status != 0) {
LogMessage(X_ERROR, "AIGLX error: drmMap of framebuffer failed (%s)\n",
strerror(-status));
goto handle_error;
}
/* Map the SAREA region. Further mmap regions may be setup in
* each DRI driver's "createNewScreen" function.
*/
status = drmMap(fd, hSAREA, SAREA_MAX, &pSAREA);
if (status != 0) {
LogMessage(X_ERROR, "AIGLX error: drmMap of SAREA failed (%s)\n",
strerror(-status));
goto handle_error;
}
screen->driScreen =
(*screen->legacy->createNewScreen) (pScreen->myNum,
&ddx_version,
&dri_version,
&drm_version,
&framebuffer,
pSAREA,
fd,
loader_extensions,
&screen->driConfigs, screen);
if (screen->driScreen == NULL) {
LogMessage(X_ERROR, "AIGLX error: Calling driver entry point failed\n");
goto handle_error;
}
screen->base.fbconfigs = glxConvertConfigs(screen->core,
screen->driConfigs,
GLX_WINDOW_BIT);
initializeExtensions(screen);
DRIGetTexOffsetFuncs(pScreen, &screen->texOffsetStart,
&screen->texOffsetFinish);
__glXScreenInit(&screen->base, pScreen);
/* The first call simply determines the length of the extension string.
* This allows us to allocate some memory to hold the extension string,
* but it requires that we call __glXGetExtensionString a second time.
*/
buffer_size = __glXGetExtensionString(screen->glx_enable_bits, NULL);
if (buffer_size > 0) {
free(screen->base.GLXextensions);
screen->base.GLXextensions = xnfalloc(buffer_size);
(void) __glXGetExtensionString(screen->glx_enable_bits,
screen->base.GLXextensions);
}
__glXsetEnterLeaveServerFuncs(__glXDRIenterServer, __glXDRIleaveServer);
screen->enterVT = pScrn->EnterVT;
pScrn->EnterVT = glxDRIEnterVT;
screen->leaveVT = pScrn->LeaveVT;
pScrn->LeaveVT = glxDRILeaveVT;
LogMessage(X_INFO, "AIGLX: Loaded and initialized %s\n", driverName);
return &screen->base;
handle_error:
if (pSAREA != NULL)
drmUnmap(pSAREA, SAREA_MAX);
if (framebuffer.base != NULL)
drmUnmap((drmAddress) framebuffer.base, framebuffer.size);
if (fd >= 0)
drmCloseOnce(fd);
DRICloseConnection(pScreen);
if (screen->driver)
dlclose(screen->driver);
free(screen);
LogMessage(X_ERROR, "AIGLX: reverting to software rendering\n");
return NULL;
}
_X_EXPORT __GLXprovider __glXDRIProvider = {
__glXDRIscreenProbe,
"DRI",
NULL
};