/* * 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 SAA, and has various * functions not responsible for performing rendering, pixmap migration, or * memory management. */ #ifdef HAVE_DIX_CONFIG_H #include #endif #include #include "saa_priv.h" #include #include "regionstr.h" #include "saa.h" #include "saa_priv.h" #ifdef SAA_DEVPRIVATEKEYREC DevPrivateKeyRec saa_screen_index; DevPrivateKeyRec saa_pixmap_index; DevPrivateKeyRec saa_gc_index; #else int saa_screen_index = -1; int saa_pixmap_index = -1; int saa_gc_index = -1; #endif /** * saa_get_drawable_pixmap() 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 * saa_get_drawable_pixmap() on the drawable. */ PixmapPtr saa_get_drawable_pixmap(DrawablePtr pDrawable) { if (pDrawable->type == DRAWABLE_WINDOW) return pDrawable->pScreen->GetWindowPixmap((WindowPtr) pDrawable); else return (PixmapPtr) pDrawable; } /** * Sets the offsets to add to coordinates to make them address the same bits in * the backing drawable. These coordinates are nonzero only for redirected * windows. */ void saa_get_drawable_deltas(DrawablePtr pDrawable, PixmapPtr pPixmap, int *xp, int *yp) { #ifdef COMPOSITE if (pDrawable->type == DRAWABLE_WINDOW) { *xp = -pPixmap->screen_x; *yp = -pPixmap->screen_y; return; } #endif *xp = 0; *yp = 0; } /** * Returns the pixmap which backs a drawable, and the offsets to add to * coordinates to make them address the same bits in the backing drawable. */ PixmapPtr saa_get_pixmap(DrawablePtr drawable, int *xp, int *yp) { PixmapPtr pixmap = saa_get_drawable_pixmap(drawable); saa_get_drawable_deltas(drawable, pixmap, xp, yp); return pixmap; } static Bool saa_download_from_hw(PixmapPtr pix, RegionPtr readback) { struct saa_screen_priv *sscreen = saa_screen(pix->drawable.pScreen); struct saa_driver *driver = sscreen->driver; struct saa_pixmap *spix = saa_pixmap(pix); void *addr; Bool ret; if (spix->mapped_access) driver->release_from_cpu(driver, pix, spix->mapped_access); ret = driver->download_from_hw(driver, pix, readback); if (spix->mapped_access) { addr = driver->sync_for_cpu(driver, pix, spix->mapped_access); if (addr != NULL) spix->addr = addr; } return ret; } Bool saa_prepare_access_pixmap(PixmapPtr pix, saa_access_t access, RegionPtr read_reg) { ScreenPtr pScreen = pix->drawable.pScreen; struct saa_screen_priv *sscreen = saa_screen(pScreen); struct saa_driver *driver = sscreen->driver; struct saa_pixmap *spix = saa_pixmap(pix); saa_access_t map_access = 0; Bool ret = TRUE; if (read_reg && REGION_NOTEMPTY(pScreen, read_reg)) ret = saa_download_from_hw(pix, read_reg); if (!ret) { LogMessage(X_ERROR, "Prepare access pixmap failed.\n"); return ret; } if ((access & SAA_ACCESS_R) != 0 && spix->read_access++ == 0) map_access = SAA_ACCESS_R; if ((access & SAA_ACCESS_W) != 0 && spix->write_access++ == 0) map_access |= SAA_ACCESS_W; if (map_access) { if (spix->auth_loc != saa_loc_override) { (void)driver->sync_for_cpu(driver, pix, map_access); spix->addr = driver->map(driver, pix, map_access); } else spix->addr = spix->override; spix->mapped_access |= map_access; } pix->devPrivate.ptr = spix->addr; return TRUE; } void saa_finish_access_pixmap(PixmapPtr pix, saa_access_t access) { struct saa_screen_priv *sscreen = saa_screen(pix->drawable.pScreen); struct saa_driver *driver = sscreen->driver; struct saa_pixmap *spix = saa_pixmap(pix); saa_access_t unmap_access = 0; if ((access & SAA_ACCESS_R) != 0 && --spix->read_access == 0) unmap_access = SAA_ACCESS_R; if ((access & SAA_ACCESS_W) != 0 && --spix->write_access == 0) unmap_access |= SAA_ACCESS_W; if (spix->read_access < 0) LogMessage(X_ERROR, "Incorrect read access.\n"); if (spix->write_access < 0) LogMessage(X_ERROR, "Incorrect write access.\n"); if (unmap_access) { if (spix->auth_loc != saa_loc_override) { driver->unmap(driver, pix, unmap_access); driver->release_from_cpu(driver, pix, unmap_access); } spix->mapped_access &= ~unmap_access; } if (!spix->mapped_access) { spix->addr = NULL; pix->devPrivate.ptr = SAA_INVALID_ADDRESS; } } /* * Callback that is called after a rendering operation. We try to * determine whether it's a shadow damage or a hw damage and call the * driver callback. */ static void saa_report_damage(DamagePtr damage, RegionPtr reg, void *closure) { PixmapPtr pixmap = (PixmapPtr) closure; struct saa_pixmap *spix = saa_get_saa_pixmap(pixmap); struct saa_driver *driver = saa_screen(pixmap->drawable.pScreen)->driver; if (spix->read_access || spix->write_access) LogMessage(X_ERROR, "Damage report inside prepare access.\n"); driver->operation_complete(driver, pixmap); DamageEmpty(damage); } Bool saa_add_damage(PixmapPtr pixmap) { ScreenPtr pScreen = pixmap->drawable.pScreen; struct saa_pixmap *spix = saa_get_saa_pixmap(pixmap); if (spix->damage) return TRUE; spix->damage = DamageCreate(saa_report_damage, NULL, DamageReportRawRegion, TRUE, pScreen, pixmap); if (!spix->damage) return FALSE; DamageRegister(&pixmap->drawable, spix->damage); DamageSetReportAfterOp(spix->damage, TRUE); return TRUE; } static inline RegionPtr saa_pix_damage_region(struct saa_pixmap *spix) { return (spix->damage ? DamageRegion(spix->damage) : NULL); } Bool saa_pad_read(DrawablePtr draw) { ScreenPtr pScreen = draw->pScreen; PixmapPtr pix; int xp; int yp; BoxRec box; RegionRec entire; Bool ret; (void)pScreen; pix = saa_get_pixmap(draw, &xp, &yp); box.x1 = draw->x + xp; box.y1 = draw->y + yp; box.x2 = box.x1 + draw->width; box.y2 = box.y1 + draw->height; REGION_INIT(pScreen, &entire, &box, 1); ret = saa_prepare_access_pixmap(pix, SAA_ACCESS_R, &entire); REGION_UNINIT(pScreen, &entire); return ret; } Bool saa_pad_read_box(DrawablePtr draw, int x, int y, int w, int h) { ScreenPtr pScreen = draw->pScreen; PixmapPtr pix; int xp; int yp; BoxRec box; RegionRec entire; Bool ret; (void)pScreen; pix = saa_get_pixmap(draw, &xp, &yp); box.x1 = x + xp; box.y1 = y + yp; box.x2 = box.x1 + w; box.y2 = box.y1 + h; REGION_INIT(pScreen, &entire, &box, 1); ret = saa_prepare_access_pixmap(pix, SAA_ACCESS_R, &entire); REGION_UNINIT(pScreen, &entire); return ret; } /** * Prepares a drawable destination for access, and maps it read-write. * If check_read is TRUE, pGC should point to a valid GC. The drawable * may then be mapped write-only if the pending operation admits. */ Bool saa_pad_write(DrawablePtr draw, GCPtr pGC, Bool check_read, saa_access_t * access) { int xp; int yp; PixmapPtr pixmap = saa_get_pixmap(draw, &xp, &yp); struct saa_pixmap *spix = saa_pixmap(pixmap); *access = SAA_ACCESS_W; /* * If the to-be-damaged area doesn't depend at all on previous * rendered contents, we don't need to do any readback. */ if (check_read && !saa_gc_reads_destination(draw, pGC)) return saa_prepare_access_pixmap(pixmap, *access, NULL); *access |= SAA_ACCESS_R; /* * Read back the area to be damaged. */ return saa_prepare_access_pixmap(pixmap, *access, saa_pix_damage_pending(spix)); } void saa_fad_read(DrawablePtr draw) { saa_finish_access_pixmap(saa_get_drawable_pixmap(draw), SAA_ACCESS_R); } void saa_fad_write(DrawablePtr draw, saa_access_t access) { PixmapPtr pix = saa_get_drawable_pixmap(draw); struct saa_pixmap *spix = saa_pixmap(pix); saa_finish_access_pixmap(pix, access); if (spix->damage) saa_pixmap_dirty(pix, FALSE, saa_pix_damage_pending(spix)); } Bool saa_gc_reads_destination(DrawablePtr pDrawable, GCPtr pGC) { return ((pGC->alu != GXcopy && pGC->alu != GXclear && pGC->alu != GXset && pGC->alu != GXcopyInverted) || pGC->fillStyle == FillStippled || pGC->clientClip != NULL || !SAA_PM_IS_SOLID(pDrawable, pGC->planemask)); } Bool saa_op_reads_destination(CARD8 op) { /* FALSE (does not read destination) is the list of ops in the protocol * document with "0" in the "Fb" column and no "Ab" in the "Fa" column. * That's just Clear and Src. ReduceCompositeOp() will already have * converted con/disjoint clear/src to Clear or Src. */ switch (op) { case PictOpClear: case PictOpSrc: return FALSE; default: return TRUE; } } static void saa_validate_gc(GCPtr pGC, unsigned long changes, DrawablePtr pDrawable) { /* fbValidateGC will do direct access to pixmaps if the tiling has changed. * Do a few smart things so fbValidateGC can do it's work. */ ScreenPtr pScreen = pDrawable->pScreen; struct saa_screen_priv *sscreen = saa_screen(pScreen); struct saa_gc_priv *sgc = saa_gc(pGC); PixmapPtr pTile = NULL; Bool finish_current_tile = FALSE; /* Either of these conditions is enough to trigger access to a tile pixmap. */ /* With pGC->tileIsPixel == 1, you run the risk of dereferencing an invalid tile pixmap pointer. */ if (pGC->fillStyle == FillTiled || ((changes & GCTile) && !pGC->tileIsPixel)) { pTile = pGC->tile.pixmap; /* Sometimes tile pixmaps are swapped, you need access to: * - The current tile if it depth matches. * - Or the rotated tile if that one matches depth and !(changes & GCTile). * - Or the current tile pixmap and a newly created one. */ if (pTile && pTile->drawable.depth != pDrawable->depth && !(changes & GCTile)) { PixmapPtr pRotatedTile = fbGetRotatedPixmap(pGC); if (pRotatedTile && pRotatedTile->drawable.depth == pDrawable->depth) pTile = pRotatedTile; else finish_current_tile = TRUE; /* CreatePixmap will be called. */ } } if (pGC->stipple && !saa_pad_read(&pGC->stipple->drawable)) { LogMessage(X_ERROR, "Failed stipple prepareaccess.\n"); return; } if (pTile && !saa_pad_read(&pTile->drawable)) { LogMessage(X_ERROR, "Failed stipple prepareaccess.\n"); goto out_no_tile; } /* Calls to Create/DestroyPixmap have to be identified as special, so * up sscreen->fallback_count. */ sscreen->fallback_count++; saa_swap(sgc, pGC, funcs); (*pGC->funcs->ValidateGC) (pGC, changes, pDrawable); saa_swap(sgc, pGC, funcs); if (finish_current_tile && pGC->tile.pixmap) saa_fad_write(&pGC->tile.pixmap->drawable, SAA_ACCESS_W); sscreen->fallback_count--; if (pTile) saa_fad_read(&pTile->drawable); out_no_tile: if (pGC->stipple) saa_fad_read(&pGC->stipple->drawable); } static void saa_destroy_gc(GCPtr pGC) { struct saa_gc_priv *sgc = saa_gc(pGC); saa_swap(sgc, pGC, funcs); (*pGC->funcs->DestroyGC) (pGC); saa_swap(sgc, pGC, funcs); } static void saa_change_gc(GCPtr pGC, unsigned long mask) { struct saa_gc_priv *sgc = saa_gc(pGC); saa_swap(sgc, pGC, funcs); (*pGC->funcs->ChangeGC) (pGC, mask); saa_swap(sgc, pGC, funcs); } static void saa_copy_gc(GCPtr pGCSrc, unsigned long mask, GCPtr pGCDst) { struct saa_gc_priv *sgc = saa_gc(pGCDst); saa_swap(sgc, pGCDst, funcs); (*pGCDst->funcs->CopyGC) (pGCSrc, mask, pGCDst); saa_swap(sgc, pGCDst, funcs); } static void saa_change_clip(GCPtr pGC, int type, pointer pvalue, int nrects) { struct saa_gc_priv *sgc = saa_gc(pGC); saa_swap(sgc, pGC, funcs); (*pGC->funcs->ChangeClip) (pGC, type, pvalue, nrects); saa_swap(sgc, pGC, funcs); } static void saa_copy_clip(GCPtr pGCDst, GCPtr pGCSrc) { struct saa_gc_priv *sgc = saa_gc(pGCDst); saa_swap(sgc, pGCDst, funcs); (*pGCDst->funcs->CopyClip) (pGCDst, pGCSrc); saa_swap(sgc, pGCDst, funcs); } static void saa_destroy_clip(GCPtr pGC) { struct saa_gc_priv *sgc = saa_gc(pGC); saa_swap(sgc, pGC, funcs); (*pGC->funcs->DestroyClip) (pGC); saa_swap(sgc, pGC, funcs); } static GCFuncs saa_gc_funcs = { saa_validate_gc, saa_change_gc, saa_copy_gc, saa_destroy_gc, saa_change_clip, saa_destroy_clip, saa_copy_clip }; /** * saa_create_gc makes a new GC and hooks up its funcs handler, so that * saa_validate_gc() will get called. */ int saa_create_gc(GCPtr pGC) { ScreenPtr pScreen = pGC->pScreen; struct saa_screen_priv *sscreen = saa_screen(pScreen); struct saa_gc_priv *sgc = saa_gc(pGC); Bool ret; saa_swap(sscreen, pScreen, CreateGC); ret = pScreen->CreateGC(pGC); if (ret) { saa_wrap(sgc, pGC, funcs, &saa_gc_funcs); saa_wrap(sgc, pGC, ops, &saa_gc_ops); } saa_swap(sscreen, pScreen, CreateGC); return ret; } static Bool saa_prepare_access_window(WindowPtr pWin) { if (pWin->backgroundState == BackgroundPixmap) { if (!saa_pad_read(&pWin->background.pixmap->drawable)) return FALSE; } if (pWin->borderIsPixel == FALSE) { if (!saa_pad_read(&pWin->border.pixmap->drawable)) { if (pWin->backgroundState == BackgroundPixmap) saa_fad_read(&pWin->background.pixmap->drawable); return FALSE; } } return TRUE; } static void saa_finish_access_window(WindowPtr pWin) { if (pWin->backgroundState == BackgroundPixmap) saa_fad_read(&pWin->background.pixmap->drawable); if (pWin->borderIsPixel == FALSE) saa_fad_read(&pWin->border.pixmap->drawable); } static Bool saa_change_window_attributes(WindowPtr pWin, unsigned long mask) { Bool ret; if (!saa_prepare_access_window(pWin)) return FALSE; ret = fbChangeWindowAttributes(pWin, mask); saa_finish_access_window(pWin); return ret; } RegionPtr saa_bitmap_to_region(PixmapPtr pPix) { RegionPtr ret; if (!saa_pad_read(&pPix->drawable)) return NULL; ret = fbPixmapToRegion(pPix); saa_fad_read(&pPix->drawable); return ret; } void saa_set_fallback_debug(ScreenPtr screen, Bool enable) { struct saa_screen_priv *sscreen = saa_screen(screen); sscreen->fallback_debug = enable; } /** * saa_close_screen() unwraps its wrapped screen functions and tears down SAA's * screen private, before calling down to the next CloseScreen. */ Bool saa_close_screen(CLOSE_SCREEN_ARGS_DECL) { struct saa_screen_priv *sscreen = saa_screen(pScreen); struct saa_driver *driver = sscreen->driver; if (pScreen->devPrivate) { /* Destroy the pixmap created by miScreenInit() *before* * chaining up as we finalize ourselves here and so this * is the last chance we have of releasing our resources * associated with the Pixmap. So do it first. */ (void)(*pScreen->DestroyPixmap) (pScreen->devPrivate); pScreen->devPrivate = NULL; } saa_unwrap(sscreen, pScreen, CloseScreen); saa_unwrap(sscreen, pScreen, CreateGC); saa_unwrap(sscreen, pScreen, ChangeWindowAttributes); saa_unwrap(sscreen, pScreen, CreatePixmap); saa_unwrap(sscreen, pScreen, DestroyPixmap); saa_unwrap(sscreen, pScreen, ModifyPixmapHeader); saa_unwrap(sscreen, pScreen, BitmapToRegion); #ifdef RENDER saa_render_takedown(pScreen); #endif saa_unaccel_takedown(pScreen); driver->takedown(driver); free(sscreen); return (*pScreen->CloseScreen) (CLOSE_SCREEN_ARGS); } struct saa_driver * saa_get_driver(ScreenPtr pScreen) { return saa_screen(pScreen)->driver; } /** * @param pScreen screen being initialized * @param pScreenInfo SAA driver record * * saa_driver_init sets up SAA given a driver record filled in by the driver. * pScreenInfo should have been allocated by saa_driver_alloc(). See the * comments in _SaaDriver for what must be filled in and what is optional. * * @return TRUE if SAA was successfully initialized. */ Bool saa_driver_init(ScreenPtr screen, struct saa_driver * saa_driver) { struct saa_screen_priv *sscreen; if (!saa_driver) return FALSE; if (saa_driver->saa_major != SAA_VERSION_MAJOR || saa_driver->saa_minor > SAA_VERSION_MINOR) { LogMessage(X_ERROR, "SAA(%d): driver's SAA version requirements " "(%d.%d) are incompatible with SAA version (%d.%d)\n", screen->myNum, saa_driver->saa_major, saa_driver->saa_minor, SAA_VERSION_MAJOR, SAA_VERSION_MINOR); return FALSE; } #if 0 if (!saa_driver->prepare_solid) { LogMessage(X_ERROR, "SAA(%d): saa_driver_t::prepare_solid must be " "non-NULL\n", screen->myNum); return FALSE; } if (!saa_driver->prepare_copy) { LogMessage(X_ERROR, "SAA(%d): saa_driver_t::prepare_copy must be " "non-NULL\n", screen->myNum); return FALSE; } #endif #ifdef SAA_DEVPRIVATEKEYREC if (!dixRegisterPrivateKey(&saa_screen_index, PRIVATE_SCREEN, 0)) { LogMessage(X_ERROR, "Failed to register SAA screen private.\n"); return FALSE; } if (!dixRegisterPrivateKey(&saa_pixmap_index, PRIVATE_PIXMAP, saa_driver->pixmap_size)) { LogMessage(X_ERROR, "Failed to register SAA pixmap private.\n"); return FALSE; } if (!dixRegisterPrivateKey(&saa_gc_index, PRIVATE_GC, sizeof(struct saa_gc_priv))) { LogMessage(X_ERROR, "Failed to register SAA gc private.\n"); return FALSE; } #else if (!dixRequestPrivate(&saa_screen_index, 0)) { LogMessage(X_ERROR, "Failed to register SAA screen private.\n"); return FALSE; } if (!dixRequestPrivate(&saa_pixmap_index, saa_driver->pixmap_size)) { LogMessage(X_ERROR, "Failed to register SAA pixmap private.\n"); return FALSE; } if (!dixRequestPrivate(&saa_gc_index, sizeof(struct saa_gc_priv))) { LogMessage(X_ERROR, "Failed to register SAA gc private.\n"); return FALSE; } #endif sscreen = calloc(1, sizeof(*sscreen)); if (!sscreen) { LogMessage(X_WARNING, "SAA(%d): Failed to allocate screen private\n", screen->myNum); return FALSE; } sscreen->driver = saa_driver; dixSetPrivate(&screen->devPrivates, &saa_screen_index, sscreen); /* * Replace various fb screen functions */ saa_wrap(sscreen, screen, CloseScreen, saa_close_screen); saa_wrap(sscreen, screen, CreateGC, saa_create_gc); saa_wrap(sscreen, screen, ChangeWindowAttributes, saa_change_window_attributes); saa_wrap(sscreen, screen, CreatePixmap, saa_create_pixmap); saa_wrap(sscreen, screen, DestroyPixmap, saa_destroy_pixmap); saa_wrap(sscreen, screen, ModifyPixmapHeader, saa_modify_pixmap_header); saa_wrap(sscreen, screen, BitmapToRegion, saa_bitmap_to_region); saa_unaccel_setup(screen); #ifdef RENDER saa_render_setup(screen); #endif return TRUE; } Bool saa_resources_init(ScreenPtr screen) { /* if (!saa_glyphs_init(screen)) return FALSE; */ return TRUE; }