/* * 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 #endif #include #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; }