/* * 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 #ifdef WITH_LIBDRM #include #endif #include "xf86Module.h" #include "list.h" #include "scrnintstr.h" #include "windowstr.h" #include "dixstruct.h" #include "dri2.h" #include "dri2int.h" #include "xf86VGAarbiter.h" #include "damage.h" #include "xf86.h" CARD8 dri2_major; /* version of DRI2 supported by DDX */ CARD8 dri2_minor; uint32_t prime_id_allocate_bitmask; static DevPrivateKeyRec dri2ScreenPrivateKeyRec; #define dri2ScreenPrivateKey (&dri2ScreenPrivateKeyRec) static DevPrivateKeyRec dri2WindowPrivateKeyRec; #define dri2WindowPrivateKey (&dri2WindowPrivateKeyRec) static DevPrivateKeyRec dri2PixmapPrivateKeyRec; #define dri2PixmapPrivateKey (&dri2PixmapPrivateKeyRec) static DevPrivateKeyRec dri2ClientPrivateKeyRec; #define dri2ClientPrivateKey (&dri2ClientPrivateKeyRec) #define dri2ClientPrivate(_pClient) (dixLookupPrivate(&(_pClient)->devPrivates, \ dri2ClientPrivateKey)) typedef struct _DRI2Client { int prime_id; } DRI2ClientRec, *DRI2ClientPtr; static RESTYPE dri2DrawableRes; typedef struct _DRI2Screen *DRI2ScreenPtr; typedef struct _DRI2Drawable { DRI2ScreenPtr dri2_screen; DrawablePtr drawable; struct xorg_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 */ Bool needInvalidate; int prime_id; PixmapPtr prime_slave_pixmap; PixmapPtr redirectpixmap; } DRI2DrawableRec, *DRI2DrawablePtr; typedef struct _DRI2Screen { ScreenPtr screen; int refcnt; unsigned int numDrivers; const char **driverNames; const char *deviceName; int fd; unsigned int lastSequence; int prime_id; DRI2CreateBufferProcPtr CreateBuffer; DRI2DestroyBufferProcPtr DestroyBuffer; DRI2CopyRegionProcPtr CopyRegion; DRI2ScheduleSwapProcPtr ScheduleSwap; DRI2GetMSCProcPtr GetMSC; DRI2ScheduleWaitMSCProcPtr ScheduleWaitMSC; DRI2AuthMagic2ProcPtr AuthMagic; DRI2AuthMagicProcPtr LegacyAuthMagic; DRI2ReuseBufferNotifyProcPtr ReuseBufferNotify; DRI2SwapLimitValidateProcPtr SwapLimitValidate; DRI2GetParamProcPtr GetParam; HandleExposuresProcPtr HandleExposures; ConfigNotifyProcPtr ConfigNotify; SetWindowPixmapProcPtr SetWindowPixmap; DRI2CreateBuffer2ProcPtr CreateBuffer2; DRI2DestroyBuffer2ProcPtr DestroyBuffer2; DRI2CopyRegion2ProcPtr CopyRegion2; } DRI2ScreenRec; static void destroy_buffer(DrawablePtr pDraw, DRI2BufferPtr buffer, int prime_id); static DRI2ScreenPtr DRI2GetScreen(ScreenPtr pScreen) { return dixLookupPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey); } static ScreenPtr GetScreenPrime(ScreenPtr master, int prime_id) { ScreenPtr slave; if (prime_id == 0 || xorg_list_is_empty(&master->offload_slave_list)) { return master; } xorg_list_for_each_entry(slave, &master->offload_slave_list, offload_head) { DRI2ScreenPtr ds; ds = DRI2GetScreen(slave); if (ds == NULL) continue; if (ds->prime_id == prime_id) return slave; } return master; } static DRI2ScreenPtr DRI2GetScreenPrime(ScreenPtr master, int prime_id) { ScreenPtr slave = GetScreenPrime(master, prime_id); return DRI2GetScreen(slave); } 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 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; xorg_list_init(&pPriv->reference_list); pPriv->needInvalidate = FALSE; pPriv->redirectpixmap = NULL; pPriv->prime_slave_pixmap = NULL; if (pDraw->type == DRAWABLE_WINDOW) { pWin = (WindowPtr) pDraw; dixSetPrivate(&pWin->devPrivates, dri2WindowPrivateKey, pPriv); } else { pPixmap = (PixmapPtr) pDraw; dixSetPrivate(&pPixmap->devPrivates, dri2PixmapPrivateKey, pPriv); } return pPriv; } Bool DRI2SwapLimit(DrawablePtr pDraw, int swap_limit) { DRI2DrawablePtr pPriv = DRI2GetDrawable(pDraw); DRI2ScreenPtr ds; if (!pPriv) return FALSE; ds = pPriv->dri2_screen; if (!ds->SwapLimitValidate || !ds->SwapLimitValidate(pDraw, swap_limit)) return FALSE; pPriv->swap_limit = swap_limit; /* Check throttling */ if (pPriv->swapsPending >= pPriv->swap_limit) return TRUE; if (pPriv->target_sbc == -1 && !pPriv->blockedOnMsc) { if (pPriv->blockedClient) { AttendClient(pPriv->blockedClient); pPriv->blockedClient = NULL; } } return TRUE; } typedef struct DRI2DrawableRefRec { XID id; XID dri2_id; DRI2InvalidateProcPtr invalidate; void *priv; struct xorg_list link; } DRI2DrawableRefRec, *DRI2DrawableRefPtr; static DRI2DrawableRefPtr DRI2LookupDrawableRef(DRI2DrawablePtr pPriv, XID id) { DRI2DrawableRefPtr ref; xorg_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; xorg_list_add(&ref->link, &pPriv->reference_list); return Success; } int DRI2CreateDrawable2(ClientPtr client, DrawablePtr pDraw, XID id, DRI2InvalidateProcPtr invalidate, void *priv, XID *dri2_id_out) { DRI2DrawablePtr pPriv; DRI2ClientPtr dri2_client = dri2ClientPrivate(client); XID dri2_id; int rc; pPriv = DRI2GetDrawable(pDraw); if (pPriv == NULL) pPriv = DRI2AllocateDrawable(pDraw); if (pPriv == NULL) return BadAlloc; pPriv->prime_id = dri2_client->prime_id; dri2_id = FakeClientID(client->index); rc = DRI2AddDrawableRef(pPriv, id, dri2_id, invalidate, priv); if (rc != Success) return rc; if (dri2_id_out) *dri2_id_out = dri2_id; return Success; } int DRI2CreateDrawable(ClientPtr client, DrawablePtr pDraw, XID id, DRI2InvalidateProcPtr invalidate, void *priv) { return DRI2CreateDrawable2(client, pDraw, id, invalidate, priv, NULL); } static int DRI2DrawableGone(void *p, XID id) { DRI2DrawablePtr pPriv = p; DRI2DrawableRefPtr ref, next; WindowPtr pWin; PixmapPtr pPixmap; DrawablePtr pDraw; int i; xorg_list_for_each_entry_safe(ref, next, &pPriv->reference_list, link) { if (ref->dri2_id == id) { xorg_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) { xorg_list_del(&ref->link); FreeResourceByType(ref->dri2_id, dri2DrawableRes, TRUE); free(ref); } } if (!xorg_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->prime_slave_pixmap) { (*pPriv->prime_slave_pixmap->master_pixmap->drawable.pScreen->DestroyPixmap)(pPriv->prime_slave_pixmap->master_pixmap); (*pPriv->prime_slave_pixmap->drawable.pScreen->DestroyPixmap)(pPriv->prime_slave_pixmap); } if (pPriv->buffers != NULL) { for (i = 0; i < pPriv->bufferCount; i++) destroy_buffer(pDraw, pPriv->buffers[i], pPriv->prime_id); free(pPriv->buffers); } if (pPriv->redirectpixmap) { (*pDraw->pScreen->ReplaceScanoutPixmap)(pDraw, pPriv->redirectpixmap, FALSE); (*pDraw->pScreen->DestroyPixmap)(pPriv->redirectpixmap); } free(pPriv); return Success; } static DRI2BufferPtr create_buffer(DRI2ScreenPtr ds, DrawablePtr pDraw, unsigned int attachment, unsigned int format) { DRI2BufferPtr buffer; if (ds->CreateBuffer2) buffer = (*ds->CreateBuffer2)(GetScreenPrime(pDraw->pScreen, DRI2GetDrawable(pDraw)->prime_id), pDraw, attachment, format); else buffer = (*ds->CreateBuffer)(pDraw, attachment, format); return buffer; } static void destroy_buffer(DrawablePtr pDraw, DRI2BufferPtr buffer, int prime_id) { ScreenPtr primeScreen; DRI2ScreenPtr ds; primeScreen = GetScreenPrime(pDraw->pScreen, prime_id); ds = DRI2GetScreen(primeScreen); if (ds->DestroyBuffer2) (*ds->DestroyBuffer2)(primeScreen, pDraw, buffer); else (*ds->DestroyBuffer)(pDraw, buffer); } 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) || attachment == DRI2BufferFrontLeft || !dimensions_match || (pPriv->buffers[old_buf]->format != format)) { *buffer = create_buffer(ds, pDraw, attachment, format); return TRUE; } else { *buffer = pPriv->buffers[old_buf]; if (ds->ReuseBufferNotify) (*ds->ReuseBufferNotify) (pDraw, *buffer); 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) { int i; if (pPriv->buffers != NULL) { for (i = 0; i < pPriv->bufferCount; i++) { if (pPriv->buffers[i] != NULL) { destroy_buffer(pDraw, pPriv->buffers[i], pPriv->prime_id); } } 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) { DRI2DrawablePtr pPriv = DRI2GetDrawable(pDraw); DRI2ScreenPtr ds; 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; } ds = DRI2GetScreenPrime(pDraw->pScreen, pPriv->prime_id); dimensions_match = (pDraw->width == pPriv->width) && (pDraw->height == pPriv->height); buffers = calloc((count + 1), sizeof(buffers[0])); if (!buffers) goto err_out; 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); } pPriv->needInvalidate = TRUE; return pPriv->buffers; err_out: *out_count = 0; if (buffers) { for (i = 0; i < count; i++) { if (buffers[i] != NULL) destroy_buffer(pDraw, buffers[i], 0); } 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 || !pPriv->needInvalidate) return; pPriv->needInvalidate = FALSE; xorg_list_for_each_entry(ref, &pPriv->reference_list, link) ref->invalidate(pDraw, ref->priv, ref->id); } /* * 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; } static inline PixmapPtr GetDrawablePixmap(DrawablePtr drawable) { if (drawable->type == DRAWABLE_PIXMAP) return (PixmapPtr)drawable; else { struct _Window *pWin = (struct _Window *)drawable; return drawable->pScreen->GetWindowPixmap(pWin); } } /* * A TraverseTree callback to invalidate all windows using the same * pixmap */ static int DRI2InvalidateWalk(WindowPtr pWin, void *data) { if (pWin->drawable.pScreen->GetWindowPixmap(pWin) != data) return WT_DONTWALKCHILDREN; DRI2InvalidateDrawable(&pWin->drawable); return WT_WALKCHILDREN; } static void DRI2InvalidateDrawableAll(DrawablePtr pDraw) { if (pDraw->type == DRAWABLE_WINDOW) { WindowPtr pWin = (WindowPtr) pDraw; PixmapPtr pPixmap = pDraw->pScreen->GetWindowPixmap(pWin); /* * Find the top-most window using this pixmap */ while (pWin->parent && pDraw->pScreen->GetWindowPixmap(pWin->parent) == pPixmap) pWin = pWin->parent; /* * Walk the sub-tree to invalidate all of the * windows using the same pixmap */ TraverseTree(pWin, DRI2InvalidateWalk, pPixmap); DRI2InvalidateDrawable(&pPixmap->drawable); } else DRI2InvalidateDrawable(pDraw); } DrawablePtr DRI2UpdatePrime(DrawablePtr pDraw, DRI2BufferPtr pDest) { DRI2DrawablePtr pPriv = DRI2GetDrawable(pDraw); PixmapPtr spix; PixmapPtr mpix = GetDrawablePixmap(pDraw); ScreenPtr master, slave; Bool ret; master = mpix->drawable.pScreen; if (pDraw->type == DRAWABLE_WINDOW) { WindowPtr pWin = (WindowPtr)pDraw; PixmapPtr pPixmap = pDraw->pScreen->GetWindowPixmap(pWin); if (pDraw->pScreen->GetScreenPixmap(pDraw->pScreen) == pPixmap) { if (pPriv->redirectpixmap && pPriv->redirectpixmap->drawable.width == pDraw->width && pPriv->redirectpixmap->drawable.height == pDraw->height && pPriv->redirectpixmap->drawable.depth == pDraw->depth) { mpix = pPriv->redirectpixmap; } else { if (master->ReplaceScanoutPixmap) { mpix = (*master->CreatePixmap)(master, pDraw->width, pDraw->height, pDraw->depth, CREATE_PIXMAP_USAGE_SHARED); if (!mpix) return NULL; ret = (*master->ReplaceScanoutPixmap)(pDraw, mpix, TRUE); if (ret == FALSE) { (*master->DestroyPixmap)(mpix); return NULL; } pPriv->redirectpixmap = mpix; } else return NULL; } } else if (pPriv->redirectpixmap) { (*master->ReplaceScanoutPixmap)(pDraw, pPriv->redirectpixmap, FALSE); (*master->DestroyPixmap)(pPriv->redirectpixmap); pPriv->redirectpixmap = NULL; } } slave = GetScreenPrime(pDraw->pScreen, pPriv->prime_id); /* check if the pixmap is still fine */ if (pPriv->prime_slave_pixmap) { if (pPriv->prime_slave_pixmap->master_pixmap == mpix) return &pPriv->prime_slave_pixmap->drawable; else { (*pPriv->prime_slave_pixmap->master_pixmap->drawable.pScreen->DestroyPixmap)(pPriv->prime_slave_pixmap->master_pixmap); (*slave->DestroyPixmap)(pPriv->prime_slave_pixmap); pPriv->prime_slave_pixmap = NULL; } } spix = PixmapShareToSlave(mpix, slave); if (!spix) return NULL; pPriv->prime_slave_pixmap = spix; #ifdef COMPOSITE spix->screen_x = mpix->screen_x; spix->screen_y = mpix->screen_y; #endif DRI2InvalidateDrawableAll(pDraw); return &spix->drawable; } static void dri2_copy_region(DrawablePtr pDraw, RegionPtr pRegion, DRI2BufferPtr pDest, DRI2BufferPtr pSrc) { DRI2DrawablePtr pPriv = DRI2GetDrawable(pDraw); DRI2ScreenPtr ds; ScreenPtr primeScreen; primeScreen = GetScreenPrime(pDraw->pScreen, pPriv->prime_id); ds = DRI2GetScreen(primeScreen); if (ds->CopyRegion2) (*ds->CopyRegion2)(primeScreen, pDraw, pRegion, pDest, pSrc); else (*ds->CopyRegion) (pDraw, pRegion, pDest, pSrc); /* cause damage to the box */ if (pPriv->prime_id) { BoxRec box; RegionRec region; box.x1 = 0; box.x2 = box.x1 + pDraw->width; box.y1 = 0; box.y2 = box.y1 + pDraw->height; RegionInit(®ion, &box, 1); RegionTranslate(®ion, pDraw->x, pDraw->y); DamageRegionAppend(pDraw, ®ion); DamageRegionProcessPending(pDraw); RegionUninit(®ion); } } int DRI2CopyRegion(DrawablePtr pDraw, RegionPtr pRegion, unsigned int dest, unsigned int src) { 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; dri2_copy_region(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 && 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; } /* According to spec, return expected swapbuffers count SBC after this swap * will complete. This is ignored unless we return Success, but it must be * initialized on every path where we return Success or the caller will send * an uninitialized value off the stack to the client. So let's initialize * it as early as possible, just to be sure. */ *swap_target = pPriv->swap_count + pPriv->swapsPending + 1; 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 || pPriv->prime_id) { BoxRec box; RegionRec region; box.x1 = 0; box.y1 = 0; box.x2 = pDraw->width; box.y2 = pDraw->height; RegionInit(®ion, &box, 0); pPriv->swapsPending++; dri2_copy_region(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. */ target_msc = pPriv->last_swap_target + pPriv->swap_interval; } pPriv->swapsPending++; ret = (*ds->ScheduleSwap) (client, pDraw, pDestBuffer, pSrcBuffer, &target_msc, 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 = target_msc; DRI2InvalidateDrawableAll(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(ClientPtr client, ScreenPtr pScreen, unsigned int driverType, int *fd, const char **driverName, const char **deviceName) { DRI2ScreenPtr ds; uint32_t prime_id = DRI2DriverPrimeId(driverType); uint32_t driver_id = driverType & 0xffff; if (!dixPrivateKeyRegistered(dri2ScreenPrivateKey)) return FALSE; ds = DRI2GetScreenPrime(pScreen, prime_id); if (ds == NULL) return FALSE; if (driver_id >= ds->numDrivers || !ds->driverNames[driver_id]) return FALSE; *driverName = ds->driverNames[driver_id]; *deviceName = ds->deviceName; *fd = ds->fd; if (client) { DRI2ClientPtr dri2_client; dri2_client = dri2ClientPrivate(client); dri2_client->prime_id = prime_id; } return TRUE; } static int DRI2AuthMagic (ScreenPtr pScreen, uint32_t magic) { DRI2ScreenPtr ds = DRI2GetScreen(pScreen); if (ds == NULL) return -EINVAL; return (*ds->LegacyAuthMagic) (ds->fd, magic); } Bool DRI2Authenticate(ClientPtr client, ScreenPtr pScreen, uint32_t magic) { DRI2ScreenPtr ds; DRI2ClientPtr dri2_client = dri2ClientPrivate(client); ScreenPtr primescreen; ds = DRI2GetScreenPrime(pScreen, dri2_client->prime_id); if (ds == NULL) return FALSE; primescreen = GetScreenPrime(pScreen, dri2_client->prime_id); if ((*ds->AuthMagic)(primescreen, 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; } static void DRI2SetWindowPixmap(WindowPtr pWin, PixmapPtr pPix) { DrawablePtr pDraw = (DrawablePtr) pWin; ScreenPtr pScreen = pDraw->pScreen; DRI2ScreenPtr ds = DRI2GetScreen(pScreen); pScreen->SetWindowPixmap = ds->SetWindowPixmap; (*pScreen->SetWindowPixmap) (pWin, pPix); ds->SetWindowPixmap = pScreen->SetWindowPixmap; pScreen->SetWindowPixmap = DRI2SetWindowPixmap; DRI2InvalidateDrawableAll(pDraw); } #define MAX_PRIME DRI2DriverPrimeMask static int get_prime_id(void) { int i; /* start at 1, prime id 0 is just normal driver */ for (i = 1; i < MAX_PRIME; i++) { if (prime_id_allocate_bitmask & (1 << i)) continue; prime_id_allocate_bitmask |= (1 << i); return i; } return -1; } 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; if (!dixRegisterPrivateKey(&dri2ClientPrivateKeyRec, PRIVATE_CLIENT, sizeof(DRI2ClientRec))) 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 >= 8) { ds->AuthMagic = info->AuthMagic2; } if (info->version >= 5) { ds->LegacyAuthMagic = info->AuthMagic; } if (info->version >= 6) { ds->ReuseBufferNotify = info->ReuseBufferNotify; ds->SwapLimitValidate = info->SwapLimitValidate; } if (info->version >= 7) { ds->GetParam = info->GetParam; cur_minor = 4; } if (info->version >= 9) { ds->CreateBuffer2 = info->CreateBuffer2; if (info->CreateBuffer2 && pScreen->isGPU) { ds->prime_id = get_prime_id(); if (ds->prime_id == -1) { free(ds); return FALSE; } } ds->DestroyBuffer2 = info->DestroyBuffer2; ds->CopyRegion2 = info->CopyRegion2; } /* * if the driver doesn't provide an AuthMagic function or the info struct * version is too low, call through LegacyAuthMagic */ if (!ds->AuthMagic) { ds->AuthMagic = DRI2AuthMagic; /* * If the driver doesn't provide an AuthMagic function * it relies on the old method (using libdrm) or fails */ if (!ds->LegacyAuthMagic) #ifdef WITH_LIBDRM ds->LegacyAuthMagic = 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; ds->SetWindowPixmap = pScreen->SetWindowPixmap; pScreen->SetWindowPixmap = DRI2SetWindowPixmap; 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); pScreen->ConfigNotify = ds->ConfigNotify; pScreen->SetWindowPixmap = ds->SetWindowPixmap; if (ds->prime_id) prime_id_allocate_bitmask &= ~(1 << ds->prime_id); free(ds->driverNames); free(ds); dixSetPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey, NULL); } /* Called by InitExtensions() */ Bool DRI2ModuleSetup(void) { dri2DrawableRes = CreateNewResourceType(DRI2DrawableGone, "DRI2Drawable"); if (!dri2DrawableRes) return FALSE; return TRUE; } void DRI2Version(int *major, int *minor) { if (major != NULL) *major = 1; if (minor != NULL) *minor = 2; } int DRI2GetParam(ClientPtr client, DrawablePtr drawable, CARD64 param, BOOL *is_param_recognized, CARD64 *value) { DRI2ScreenPtr ds = DRI2GetScreen(drawable->pScreen); char high_byte = (param >> 24); switch (high_byte) { case 0: /* Parameter names whose high_byte is 0 are reserved for the X * server. The server currently recognizes no parameters. */ goto not_recognized; case 1: /* Parameter names whose high byte is 1 are reserved for the DDX. */ if (ds->GetParam) return ds->GetParam(client, drawable, param, is_param_recognized, value); else goto not_recognized; default: /* Other parameter names are reserved for future use. They are never * recognized. */ goto not_recognized; } not_recognized: *is_param_recognized = FALSE; return Success; }