/* * Copyright © 2001 Keith Packard * * Partly based on code that is Copyright © The XFree86 Project 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 Keith Packard not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Keith Packard makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL KEITH PACKARD 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. */ /** @file * This file covers the initialization and teardown of EXA, and has various * functions not responsible for performing rendering, pixmap migration, or * memory management. */ #ifdef HAVE_DIX_CONFIG_H #include #endif #include #include "exa_priv.h" #include #include "dixfontstr.h" #include "exa.h" #include "cw.h" static int exaGeneration; int exaScreenPrivateIndex; int exaPixmapPrivateIndex; /** * exaGetPixmapOffset() returns the offset (in bytes) within the framebuffer of * the beginning of the given pixmap. * * Note that drivers are free to, and often do, munge this offset as necessary * for handing to the hardware -- for example, translating it into a different * aperture. This function may need to be extended in the future if we grow * support for having multiple card-accessible offscreen, such as an AGP memory * pool alongside the framebuffer pool. */ unsigned long exaGetPixmapOffset(PixmapPtr pPix) { ExaScreenPriv (pPix->drawable.pScreen); return ((unsigned long)pPix->devPrivate.ptr - (unsigned long)pExaScr->info->memoryBase); } /** * exaGetPixmapPitch() returns the pitch (in bytes) of the given pixmap. * * This is a helper to make driver code more obvious, due to the rather obscure * naming of the pitch field in the pixmap. */ unsigned long exaGetPixmapPitch(PixmapPtr pPix) { return pPix->devKind; } /** * exaGetPixmapSize() returns the size in bytes of the given pixmap in video * memory. Only valid when the pixmap is currently in framebuffer. */ unsigned long exaGetPixmapSize(PixmapPtr pPix) { ExaPixmapPrivPtr pExaPixmap; pExaPixmap = ExaGetPixmapPriv(pPix); if (pExaPixmap != NULL) return pExaPixmap->size; return 0; } /** * exaGetDrawablePixmap() returns a backing pixmap for a given drawable. * * @param pDrawable the drawable being requested. * * This function returns the backing pixmap for a drawable, whether it is a * redirected window, unredirected window, or already a pixmap. Note that * coordinate translation is needed when drawing to the backing pixmap of a * redirected window, and the translation coordinates are provided by calling * exaGetOffscreenPixmap() on the drawable. */ PixmapPtr exaGetDrawablePixmap(DrawablePtr pDrawable) { if (pDrawable->type == DRAWABLE_WINDOW) return pDrawable->pScreen->GetWindowPixmap ((WindowPtr) pDrawable); else return (PixmapPtr) pDrawable; } /** * exaDrawableDirty() marks a pixmap backing a drawable as dirty, allowing for * optimizations in pixmap migration when no changes have occurred. */ void exaDrawableDirty (DrawablePtr pDrawable) { ExaPixmapPrivPtr pExaPixmap; pExaPixmap = ExaGetPixmapPriv(exaGetDrawablePixmap (pDrawable)); if (pExaPixmap != NULL) pExaPixmap->dirty = TRUE; } static Bool exaDestroyPixmap (PixmapPtr pPixmap) { if (pPixmap->refcnt == 1) { ExaPixmapPriv (pPixmap); if (pExaPixmap->area) { DBG_PIXMAP(("-- 0x%p (0x%x) (%dx%d)\n", (void*)pPixmap->drawable.id, ExaGetPixmapPriv(pPixmap)->area->offset, pPixmap->drawable.width, pPixmap->drawable.height)); /* Free the offscreen area */ exaOffscreenFree (pPixmap->drawable.pScreen, pExaPixmap->area); pPixmap->devPrivate = pExaPixmap->devPrivate; pPixmap->devKind = pExaPixmap->devKind; } } return fbDestroyPixmap (pPixmap); } static PixmapPtr exaCreatePixmap(ScreenPtr pScreen, int w, int h, int depth) { PixmapPtr pPixmap; ExaPixmapPrivPtr pExaPixmap; int bpp; ExaScreenPriv(pScreen); if (w > 32767 || h > 32767) return NullPixmap; pPixmap = fbCreatePixmap (pScreen, w, h, depth); if (!pPixmap) return NULL; pExaPixmap = ExaGetPixmapPriv(pPixmap); /* Glyphs have w/h equal to zero, and may not be migrated. See exaGlyphs. */ if (!w || !h) pExaPixmap->score = EXA_PIXMAP_SCORE_PINNED; else pExaPixmap->score = EXA_PIXMAP_SCORE_INIT; pExaPixmap->area = NULL; pExaPixmap->dirty = FALSE; return pPixmap; } Bool exaPixmapIsOffscreen(PixmapPtr p) { ScreenPtr pScreen = p->drawable.pScreen; ExaScreenPriv(pScreen); return ((unsigned long) ((CARD8 *) p->devPrivate.ptr - (CARD8 *) pExaScr->info->memoryBase) < pExaScr->info->memorySize); } Bool exaDrawableIsOffscreen (DrawablePtr pDrawable) { return exaPixmapIsOffscreen (exaGetDrawablePixmap (pDrawable)); } PixmapPtr exaGetOffscreenPixmap (DrawablePtr pDrawable, int *xp, int *yp) { PixmapPtr pPixmap; int x, y; if (pDrawable->type == DRAWABLE_WINDOW) { pPixmap = (*pDrawable->pScreen->GetWindowPixmap) ((WindowPtr) pDrawable); #ifdef COMPOSITE x = -pPixmap->screen_x; y = -pPixmap->screen_y; #else x = 0; y = 0; #endif } else { pPixmap = (PixmapPtr) pDrawable; x = 0; y = 0; } *xp = x; *yp = y; if (exaPixmapIsOffscreen (pPixmap)) return pPixmap; else return NULL; } void exaPrepareAccess(DrawablePtr pDrawable, int index) { ScreenPtr pScreen = pDrawable->pScreen; ExaScreenPriv (pScreen); PixmapPtr pPixmap; pPixmap = exaGetDrawablePixmap (pDrawable); if (exaPixmapIsOffscreen (pPixmap)) exaWaitSync (pDrawable->pScreen); else return; if (pExaScr->info->PrepareAccess == NULL) return; if (!(*pExaScr->info->PrepareAccess) (pPixmap, index)) { ExaPixmapPriv (pPixmap); if (pExaPixmap->score != EXA_PIXMAP_SCORE_PINNED) FatalError("Driver failed PrepareAccess on a pinned pixmap\n"); exaMoveOutPixmap (pPixmap); } } void exaFinishAccess(DrawablePtr pDrawable, int index) { ScreenPtr pScreen = pDrawable->pScreen; ExaScreenPriv (pScreen); PixmapPtr pPixmap; if (index == EXA_PREPARE_DEST) exaDrawableDirty (pDrawable); if (pExaScr->info->FinishAccess == NULL) return; pPixmap = exaGetDrawablePixmap (pDrawable); if (!exaPixmapIsOffscreen (pPixmap)) return; (*pExaScr->info->FinishAccess) (pPixmap, index); } static void exaValidateGC (GCPtr pGC, Mask changes, DrawablePtr pDrawable) { fbValidateGC (pGC, changes, pDrawable); if (exaDrawableIsOffscreen (pDrawable)) pGC->ops = (GCOps *) &exaOps; else pGC->ops = (GCOps *) &exaAsyncPixmapGCOps; } static GCFuncs exaGCFuncs = { exaValidateGC, miChangeGC, miCopyGC, miDestroyGC, miChangeClip, miDestroyClip, miCopyClip }; static int exaCreateGC (GCPtr pGC) { if (!fbCreateGC (pGC)) return FALSE; pGC->funcs = &exaGCFuncs; return TRUE; } static Bool exaCloseScreen(int i, ScreenPtr pScreen) { ExaScreenPriv(pScreen); #ifdef RENDER PictureScreenPtr ps = GetPictureScreenIfSet(pScreen); #endif pScreen->CreateGC = pExaScr->SavedCreateGC; pScreen->CloseScreen = pExaScr->SavedCloseScreen; pScreen->GetImage = pExaScr->SavedGetImage; pScreen->GetSpans = pExaScr->SavedGetSpans; pScreen->PaintWindowBackground = pExaScr->SavedPaintWindowBackground; pScreen->PaintWindowBorder = pExaScr->SavedPaintWindowBorder; pScreen->CreatePixmap = pExaScr->SavedCreatePixmap; pScreen->DestroyPixmap = pExaScr->SavedDestroyPixmap; pScreen->CopyWindow = pExaScr->SavedCopyWindow; #ifdef RENDER if (ps) { ps->Composite = pExaScr->SavedComposite; ps->Glyphs = pExaScr->SavedGlyphs; } #endif xfree (pExaScr); return (*pScreen->CloseScreen) (i, pScreen); } /** * This function allocates a driver structure for EXA drivers to fill in. By * having EXA allocate the structure, the driver structure can be extended * without breaking ABI between EXA and the drivers. The driver's * responsibility is to check beforehand that the EXA module has a matching * major number and sufficient minor. Drivers are responsible for freeing the * driver structure using xfree(). * * @return a newly allocated, zero-filled driver structure */ ExaDriverPtr exaDriverAlloc(void) { return xcalloc(1, sizeof(ExaDriverRec)); } /** * @param pScreen screen being initialized * @param pScreenInfo EXA driver record * * exaDriverInit sets up EXA given a driver record filled in by the driver. * pScreenInfo should have been allocated by exaDriverAlloc(). See the * comments in _ExaDriver for what must be filled in and what is optional. * * @return TRUE if EXA was successfully initialized. */ Bool exaDriverInit (ScreenPtr pScreen, ExaDriverPtr pScreenInfo) { ExaScreenPrivPtr pExaScr; if (pScreenInfo->exa_major != EXA_VERSION_MAJOR || pScreenInfo->exa_minor > EXA_VERSION_MINOR) { LogMessage(X_ERROR, "EXA(%d): driver's EXA version requirements " "(%d.%d) are incompatible with EXA version (%d.%d)\n", pScreen->myNum, pScreenInfo->exa_major, pScreenInfo->exa_minor, EXA_VERSION_MAJOR, EXA_VERSION_MINOR); return FALSE; } #ifdef RENDER PictureScreenPtr ps = GetPictureScreenIfSet(pScreen); #endif if (exaGeneration != serverGeneration) { exaScreenPrivateIndex = AllocateScreenPrivateIndex(); exaPixmapPrivateIndex = AllocatePixmapPrivateIndex(); exaGeneration = serverGeneration; } pExaScr = xcalloc (sizeof (ExaScreenPrivRec), 1); if (!pExaScr) { LogMessage(X_WARNING, "EXA(%d): Failed to allocate screen private\n", pScreen->myNum); return FALSE; } pExaScr->info = pScreenInfo; pScreen->devPrivates[exaScreenPrivateIndex].ptr = (pointer) pExaScr; exaDDXDriverInit(pScreen); /* * Replace various fb screen functions */ pExaScr->SavedCloseScreen = pScreen->CloseScreen; pScreen->CloseScreen = exaCloseScreen; pExaScr->SavedCreateGC = pScreen->CreateGC; pScreen->CreateGC = exaCreateGC; pExaScr->SavedGetImage = pScreen->GetImage; pScreen->GetImage = ExaCheckGetImage; pExaScr->SavedGetSpans = pScreen->GetSpans; pScreen->GetSpans = ExaCheckGetSpans; pExaScr->SavedCopyWindow = pScreen->CopyWindow; pScreen->CopyWindow = exaCopyWindow; pExaScr->SavedPaintWindowBackground = pScreen->PaintWindowBackground; pScreen->PaintWindowBackground = exaPaintWindow; pExaScr->SavedPaintWindowBorder = pScreen->PaintWindowBorder; pScreen->PaintWindowBorder = exaPaintWindow; pScreen->BackingStoreFuncs.SaveAreas = ExaCheckSaveAreas; pScreen->BackingStoreFuncs.RestoreAreas = ExaCheckRestoreAreas; #ifdef RENDER if (ps) { pExaScr->SavedComposite = ps->Composite; ps->Composite = exaComposite; pExaScr->SavedGlyphs = ps->Glyphs; ps->Glyphs = exaGlyphs; } #endif miDisableCompositeWrapper(pScreen); /* * Hookup offscreen pixmaps */ if ((pExaScr->info->flags & EXA_OFFSCREEN_PIXMAPS) && pExaScr->info->offScreenBase < pExaScr->info->memorySize) { if (!AllocatePixmapPrivate(pScreen, exaPixmapPrivateIndex, sizeof (ExaPixmapPrivRec))) { LogMessage(X_WARNING, "EXA(%d): Failed to allocate pixmap private\n", pScreen->myNum); return FALSE; } pExaScr->SavedCreatePixmap = pScreen->CreatePixmap; pScreen->CreatePixmap = exaCreatePixmap; pExaScr->SavedDestroyPixmap = pScreen->DestroyPixmap; pScreen->DestroyPixmap = exaDestroyPixmap; } else { LogMessage(X_INFO, "EXA(%d): No offscreen pixmaps\n", pScreen->myNum); if (!AllocatePixmapPrivate(pScreen, exaPixmapPrivateIndex, 0)) return FALSE; } DBG_PIXMAP(("============== %ld < %ld\n", pExaScr->info->offScreenBase, pExaScr->info->memorySize)); if (pExaScr->info->offScreenBase < pExaScr->info->memorySize) { if (!exaOffscreenInit (pScreen)) { LogMessage(X_WARNING, "EXA(%d): Offscreen pixmap setup failed\n", pScreen->myNum); return FALSE; } } return TRUE; } /** * exaDriverFini tears down EXA on a given screen. * * @param pScreen screen being torn down. */ void exaDriverFini (ScreenPtr pScreen) { /*right now does nothing*/ } /** * exaMarkSync() should be called after any asynchronous drawing by the hardware. * * @param pScreen screen which drawing occurred on * * exaMarkSync() sets a flag to indicate that some asynchronous drawing has * happened and a WaitSync() will be necessary before relying on the contents of * offscreen memory from the CPU's perspective. It also calls an optional * driver MarkSync() callback, the return value of which may be used to do partial * synchronization with the hardware in the future. */ void exaMarkSync(ScreenPtr pScreen) { ExaScreenPriv(pScreen); pExaScr->info->needsSync = TRUE; if (pExaScr->info->MarkSync != NULL) { pExaScr->info->lastMarker = (*pExaScr->info->MarkSync)(pScreen); } } /** * exaWaitSync() ensures that all drawing has been completed. * * @param pScreen screen being synchronized. * * Calls down into the driver to ensure that all previous drawing has completed. * It should always be called before relying on the framebuffer contents * reflecting previous drawing, from a CPU perspective. */ void exaWaitSync(ScreenPtr pScreen) { ExaScreenPriv(pScreen); if (pExaScr->info->needsSync && !pExaScr->swappedOut) { (*pExaScr->info->WaitMarker)(pScreen, pExaScr->info->lastMarker); pExaScr->info->needsSync = FALSE; } }