diff options
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | src/Makefile.am | 6 | ||||
-rw-r--r-- | src/i830.h | 16 | ||||
-rw-r--r-- | src/i830_accel.c | 31 | ||||
-rw-r--r-- | src/i830_debug.c | 2 | ||||
-rw-r--r-- | src/i830_dri.c | 2 | ||||
-rw-r--r-- | src/i830_driver.c | 153 | ||||
-rw-r--r-- | src/i830_exa.c | 131 | ||||
-rw-r--r-- | src/i830_memory.c | 16 | ||||
-rw-r--r-- | src/i830_video.c | 4 | ||||
-rw-r--r-- | uxa/Makefile.am | 20 | ||||
-rw-r--r-- | uxa/uxa-accel.c | 1038 | ||||
-rw-r--r-- | uxa/uxa-glyphs.c | 880 | ||||
-rw-r--r-- | uxa/uxa-priv.h | 444 | ||||
-rw-r--r-- | uxa/uxa-render.c | 1052 | ||||
-rw-r--r-- | uxa/uxa-unaccel.c | 370 | ||||
-rw-r--r-- | uxa/uxa.c | 573 | ||||
-rw-r--r-- | uxa/uxa.h | 671 |
19 files changed, 5334 insertions, 78 deletions
diff --git a/Makefile.am b/Makefile.am index b2398a88..5db07de9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -19,7 +19,7 @@ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. AUTOMAKE_OPTIONS = foreign -SUBDIRS = src man +SUBDIRS = uxa src man EXTRA_DIST = README DISTCLEANFILES = doltcompile diff --git a/configure.ac b/configure.ac index db16c356..b24a1541 100644 --- a/configure.ac +++ b/configure.ac @@ -251,6 +251,7 @@ XORG_CHECK_LINUXDOC AC_OUTPUT([ Makefile + uxa/Makefile src/Makefile src/xvmc/Makefile src/bios_reader/Makefile diff --git a/src/Makefile.am b/src/Makefile.am index 2932233d..dd92c8d9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -31,13 +31,13 @@ SUBDIRS = xvmc bios_reader ch7017 ch7xxx ivch sil164 tfp410 $(REGDUMPER) # TODO: -nostdlib/-Bstatic/-lgcc platform magic, not installing the .a, etc. AM_CFLAGS = @WARN_CFLAGS@ @XORG_CFLAGS@ @DRM_CFLAGS@ @DRI_CFLAGS@ \ - @PCIACCESS_CFLAGS@ \ - @XMODES_CFLAGS@ -DI830_XV -DI830_USE_XAA -DI830_USE_EXA + @PCIACCESS_CFLAGS@ -I../uxa \ + @XMODES_CFLAGS@ -DI830_XV -DI830_USE_XAA -DI830_USE_EXA -DI830_USE_UXA intel_drv_la_LTLIBRARIES = intel_drv.la intel_drv_la_LDFLAGS = -module -avoid-version intel_drv_ladir = @moduledir@/drivers -intel_drv_la_LIBADD = -lm +intel_drv_la_LIBADD = -lm ../uxa/libuxa.la if XSERVER_LIBPCIACCESS intel_drv_la_LIBADD += @PCIACCESS_LIBS@ endif @@ -83,7 +83,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifdef I830_USE_EXA #include "exa.h" +#include "uxa.h" Bool I830EXAInit(ScreenPtr pScreen); +Bool i830_uxa_init(ScreenPtr pScreen); unsigned long long I830TexOffsetStart(PixmapPtr pPix); #endif @@ -355,6 +357,14 @@ enum backlight_control { BCM_KERNEL, }; +typedef enum accel_method { + ACCEL_UNINIT = 0, + ACCEL_NONE, + ACCEL_XAA, + ACCEL_EXA, + ACCEL_UXA +} accel_method_t; + typedef struct _I830Rec { unsigned char *MMIOBase; unsigned char *GTTBase; @@ -496,8 +506,7 @@ typedef struct _I830Rec { Bool fence_used[FENCE_NEW_NR]; - Bool useEXA; - Bool noAccel; + accel_method_t accel; Bool SWCursor; #ifdef I830_USE_XAA XAAInfoRecPtr AccelInfoRec; @@ -518,6 +527,7 @@ typedef struct _I830Rec { #ifdef I830_USE_EXA ExaDriverPtr EXADriverPtr; + uxa_driver_t *uxa_driver; PixmapPtr pSrcPixmap; #endif @@ -902,7 +912,7 @@ static inline int i830_fb_compression_supported(I830Ptr pI830) /* fbc depends on tiled surface. And we don't support tiled * front buffer with XAA now. */ - if (!pI830->tiling || (IS_I965G(pI830) && !pI830->useEXA)) + if (!pI830->tiling || (IS_I965G(pI830) && pI830->accel <= ACCEL_XAA)) return FALSE; return TRUE; } diff --git a/src/i830_accel.c b/src/i830_accel.c index c3cd08e0..579de31c 100644 --- a/src/i830_accel.c +++ b/src/i830_accel.c @@ -67,12 +67,12 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. unsigned long intel_get_pixmap_offset(PixmapPtr pPix) { +#ifdef I830_USE_EXA ScreenPtr pScreen = pPix->drawable.pScreen; ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; I830Ptr pI830 = I830PTR(pScrn); -#ifdef I830_USE_EXA - if (pI830->useEXA) + if (pI830->accel == ACCEL_EXA) return exaGetPixmapOffset(pPix); #endif return (unsigned long)pPix->devPrivate.ptr - (unsigned long)pI830->FbBase; @@ -81,17 +81,15 @@ intel_get_pixmap_offset(PixmapPtr pPix) unsigned long intel_get_pixmap_pitch(PixmapPtr pPix) { +#ifdef I830_USE_EXA ScreenPtr pScreen = pPix->drawable.pScreen; ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; I830Ptr pI830 = I830PTR(pScrn); -#ifdef I830_USE_EXA - if (pI830->useEXA) + if (pI830->accel == ACCEL_EXA) return exaGetPixmapPitch(pPix); #endif -#ifdef I830_USE_XAA return (unsigned long)pPix->devKind; -#endif } int @@ -151,6 +149,9 @@ I830WaitLpRing(ScrnInfoPtr pScrn, int n, int timeout_millis) #ifdef I830_USE_EXA pI830->EXADriverPtr = NULL; #endif +#ifdef I830_USE_UXA + pI830->uxa_driver = NULL; +#endif FatalError("lockup\n"); } @@ -176,7 +177,7 @@ I830Sync(ScrnInfoPtr pScrn) if (I810_DEBUG & (DEBUG_VERBOSE_ACCEL | DEBUG_VERBOSE_SYNC)) ErrorF("I830Sync\n"); - if (pI830->noAccel) + if (pI830->accel == ACCEL_NONE) return; #ifdef XF86DRI @@ -278,15 +279,25 @@ I830SelectBuffer(ScrnInfoPtr pScrn, int buffer) Bool I830AccelInit(ScreenPtr pScreen) { -#ifdef I830_USE_EXA ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; I830Ptr pI830 = I830PTR(pScrn); - if (pI830->useEXA) + switch (pI830->accel) { +#ifdef I830_USE_UXA + case ACCEL_UXA: + return i830_uxa_init(pScreen); +#endif +#ifdef I830_USE_EXA + case ACCEL_EXA: return I830EXAInit(pScreen); #endif #ifdef I830_USE_XAA - return I830XAAInit(pScreen); + case ACCEL_XAA: + return I830XAAInit(pScreen); #endif + case ACCEL_UNINIT: + case ACCEL_NONE: + break; + } return FALSE; } diff --git a/src/i830_debug.c b/src/i830_debug.c index 1671e255..f1205cc6 100644 --- a/src/i830_debug.c +++ b/src/i830_debug.c @@ -1314,6 +1314,8 @@ i830_valid_command (uint32_t cmd) if (!mi_cmds[opcode]) return -1; break; + case 1: + return -1; case 2: /* 2D */ count = (cmd & 0x1f) + 2; opcode = (cmd >> 22) & 0x7f; diff --git a/src/i830_dri.c b/src/i830_dri.c index 46783721..0e5d81dd 100644 --- a/src/i830_dri.c +++ b/src/i830_dri.c @@ -589,7 +589,7 @@ I830DRIScreenInit(ScreenPtr pScreen) #if DRIINFO_MAJOR_VERSION > 5 || \ (DRIINFO_MAJOR_VERSION == 5 && DRIINFO_MINOR_VERSION >= 3) - if (pI830->useEXA) + if (pI830->accel == ACCEL_EXA) pDRIInfo->texOffsetStart = I830TexOffsetStart; #endif diff --git a/src/i830_driver.c b/src/i830_driver.c index b6fac9f8..ed974ce2 100644 --- a/src/i830_driver.c +++ b/src/i830_driver.c @@ -290,9 +290,7 @@ static PciChipsets I830PciChipsets[] = { */ typedef enum { -#if defined(I830_USE_XAA) && defined(I830_USE_EXA) OPTION_ACCELMETHOD, -#endif OPTION_NOACCEL, OPTION_SW_CURSOR, OPTION_CACHE_LINES, @@ -318,9 +316,7 @@ typedef enum { } I830Opts; static OptionInfoRec I830Options[] = { -#if defined(I830_USE_XAA) && defined(I830_USE_EXA) {OPTION_ACCELMETHOD, "AccelMethod", OPTV_ANYSTR, {0}, FALSE}, -#endif {OPTION_NOACCEL, "NoAccel", OPTV_BOOLEAN, {0}, FALSE}, {OPTION_SW_CURSOR, "SWcursor", OPTV_BOOLEAN, {0}, FALSE}, {OPTION_CACHE_LINES, "CacheLines", OPTV_INTEGER, {0}, FALSE}, @@ -1338,6 +1334,15 @@ i830_detect_chipset(ScrnInfoPtr pScrn) return TRUE; } +static const char *accel_name[] = +{ + "unspecified", + "no", + "XAA", + "EXA", + "UXA", +}; + /** * This is called per zaphod head (so usually just once) to do initialization * before the Screen is created. @@ -1554,7 +1559,7 @@ I830PreInit(ScrnInfoPtr pScrn, int flags) num_pipe, num_pipe > 1 ? "s" : ""); if (xf86ReturnOptValBool(pI830->Options, OPTION_NOACCEL, FALSE)) { - pI830->noAccel = TRUE; + pI830->accel = ACCEL_NONE; } /* @@ -1568,29 +1573,38 @@ I830PreInit(ScrnInfoPtr pScrn, int flags) * All this *could* go away if we removed XAA support from this driver, * for example. :) */ - if (!pI830->noAccel) { + if (pI830->accel == ACCEL_UNINIT) { + pI830->accel = ACCEL_NONE; +#ifdef I830_USE_XAA + pI830->accel = ACCEL_XAA; +#endif #ifdef I830_USE_EXA - pI830->useEXA = TRUE; -#else - pI830->useEXA = FALSE; + pI830->accel = ACCEL_EXA; +#endif +#ifdef I830_USE_UXA + pI830->accel = ACCEL_UXA; #endif -#if defined(I830_USE_XAA) && defined(I830_USE_EXA) +#if I830_USE_XAA + I830_USE_EXA + I830_USE_UXA >= 2 from = X_DEFAULT; if ((s = (char *)xf86GetOptValString(pI830->Options, OPTION_ACCELMETHOD))) { if (!xf86NameCmp(s, "EXA")) { from = X_CONFIG; - pI830->useEXA = TRUE; + pI830->accel = ACCEL_EXA; } else if (!xf86NameCmp(s, "XAA")) { from = X_CONFIG; - pI830->useEXA = FALSE; + pI830->accel = ACCEL_XAA; + } + else if (!xf86NameCmp(s, "UXA")) { + from = X_CONFIG; + pI830->accel = ACCEL_UXA; } } #endif - xf86DrvMsg(pScrn->scrnIndex, from, "Using %s for acceleration\n", - pI830->useEXA ? "EXA" : "XAA"); } + xf86DrvMsg(pScrn->scrnIndex, from, "Using %s acceleration\n", + accel_name[pI830->accel]); if (xf86ReturnOptValBool(pI830->Options, OPTION_SW_CURSOR, FALSE)) { pI830->SWCursor = TRUE; @@ -1601,7 +1615,7 @@ I830PreInit(ScrnInfoPtr pScrn, int flags) #ifdef XF86DRI if (!pI830->directRenderingDisabled) { - if (pI830->noAccel || pI830->SWCursor) { + if (pI830->accel == ACCEL_NONE || pI830->SWCursor) { xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "DRI is disabled because it " "needs HW cursor and 2D acceleration.\n"); pI830->directRenderingDisabled = TRUE; @@ -1774,7 +1788,7 @@ I830PreInit(ScrnInfoPtr pScrn, int flags) if (!IS_I965G(pI830) && pScrn->virtualY > 2048) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Cannot support > 2048 vertical lines. disabling acceleration.\n"); - pI830->noAccel = TRUE; + pI830->accel = ACCEL_NONE; } /* Set display resolution */ @@ -1788,18 +1802,19 @@ I830PreInit(ScrnInfoPtr pScrn, int flags) xf86LoaderReqSymLists(I810fbSymbols, NULL); + switch (pI830->accel) { #ifdef I830_USE_XAA - if (!pI830->noAccel && !pI830->useEXA) { + case ACCEL_XAA: if (!xf86LoadSubModule(pScrn, "xaa")) { PreInitCleanup(pScrn); return FALSE; } xf86LoaderReqSymLists(I810xaaSymbols, NULL); - } + break; #endif #ifdef I830_USE_EXA - if (!pI830->noAccel && pI830->useEXA) { + case ACCEL_EXA: { XF86ModReqInfo req; int errmaj, errmin; @@ -1817,8 +1832,12 @@ I830PreInit(ScrnInfoPtr pScrn, int flags) return FALSE; } xf86LoaderReqSymLists(I830exaSymbols, NULL); + break; } #endif + default: + break; + } if (!pI830->SWCursor) { if (!xf86LoadSubModule(pScrn, "ramdac")) { PreInitCleanup(pScrn); @@ -1878,7 +1897,7 @@ i830_stop_ring(ScrnInfoPtr pScrn, Bool flush) pI830->entityPrivate->RingRunning = 0; /* Flush the ring buffer (if enabled), then disable it. */ - if (!pI830->noAccel) { + if (pI830->accel != ACCEL_NONE) { temp = INREG(LP_RING + RING_LEN); if (temp & RING_VALID) { i830_refresh_ring(pScrn); @@ -1900,7 +1919,7 @@ i830_start_ring(ScrnInfoPtr pScrn) DPRINTF(PFX, "SetRingRegs\n"); - if (pI830->noAccel) + if (pI830->accel == ACCEL_NONE) return; if (!I830IsPrimary(pScrn)) return; @@ -2441,7 +2460,7 @@ IntelEmitInvarientState(ScrnInfoPtr pScrn) I830Ptr pI830 = I830PTR(pScrn); uint32_t ctx_addr; - if (pI830->noAccel) + if (pI830->accel == ACCEL_NONE) return; #ifdef XF86DRI @@ -2496,12 +2515,12 @@ I830BlockHandler(int i, pI830->BlockHandler = pScreen->BlockHandler; pScreen->BlockHandler = I830BlockHandler; - if (pScrn->vtSema && !pI830->noAccel) { + if (pScrn->vtSema && pI830->accel != ACCEL_NONE) { /* Emit a flush of the rendering cache, or on the 965 and beyond * rendering results may not hit the framebuffer until significantly * later. */ - if (!pI830->noAccel && (pI830->need_mi_flush || pI830->batch_used)) + if (pI830->accel != ACCEL_NONE && (pI830->need_mi_flush || pI830->batch_used)) I830EmitFlush(pScrn); /* Flush the batch, so that any rendering is executed in a timely @@ -3001,12 +3020,12 @@ I830ScreenInit(int scrnIndex, ScreenPtr pScreen, int argc, char **argv) pI830->XvEnabled = !pI830->XvDisabled; if (pI830->XvEnabled) { if (!I830IsPrimary(pScrn)) { - if (!pI8301->XvEnabled || pI830->noAccel) { + if (!pI8301->XvEnabled || pI830->accel == ACCEL_NONE) { pI830->XvEnabled = FALSE; xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Xv is disabled.\n"); } } else - if (pI830->noAccel || pI830->StolenOnly) { + if (pI830->accel == ACCEL_NONE || pI830->StolenOnly) { xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Xv is disabled because it " "needs 2D accel and AGPGART.\n"); pI830->XvEnabled = FALSE; @@ -3016,18 +3035,18 @@ I830ScreenInit(int scrnIndex, ScreenPtr pScreen, int argc, char **argv) pI830->XvEnabled = FALSE; #endif - if (!pI830->noAccel) { + if (pI830->accel != ACCEL_NONE) { if (pI830->memory_manager == NULL && pI830->LpRing->mem->size == 0) { xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Disabling acceleration because the ring buffer " "allocation failed.\n"); - pI830->noAccel = TRUE; + pI830->accel = ACCEL_NONE; } } #ifdef I830_XV if (pI830->XvEnabled) { - if (pI830->noAccel) { + if (pI830->accel == ACCEL_NONE) { xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Disabling Xv because it " "needs 2D acceleration.\n"); pI830->XvEnabled = FALSE; @@ -3049,7 +3068,7 @@ I830ScreenInit(int scrnIndex, ScreenPtr pScreen, int argc, char **argv) */ if (pI830->directRenderingEnabled) { - if (pI830->noAccel || pI830->SWCursor || (pI830->StolenOnly && I830IsPrimary(pScrn))) { + if (pI830->accel == ACCEL_NONE || pI830->SWCursor || (pI830->StolenOnly && I830IsPrimary(pScrn))) { xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "DRI is disabled because it " "needs HW cursor, 2D accel and AGPGART.\n"); pI830->directRenderingEnabled = FALSE; @@ -3123,7 +3142,7 @@ I830ScreenInit(int scrnIndex, ScreenPtr pScreen, int argc, char **argv) DPRINTF(PFX, "assert( if(!I830EnterVT(scrnIndex, 0)) )\n"); - if (!pI830->useEXA) { + if (pI830->accel <= ACCEL_XAA) { if (I830IsPrimary(pScrn)) { if (!I830InitFBManager(pScreen, &(pI830->FbMemBox))) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, @@ -3171,7 +3190,7 @@ I830ScreenInit(int scrnIndex, ScreenPtr pScreen, int argc, char **argv) DPRINTF(PFX, "assert( if(!I830InitFBManager(pScreen, &(pI830->FbMemBox))) )\n"); - if (!pI830->noAccel) { + if (pI830->accel != ACCEL_NONE) { if (!I830AccelInit(pScreen)) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Hardware acceleration initialization failed\n"); @@ -3596,12 +3615,19 @@ I830CloseScreen(int scrnIndex, ScreenPtr pScreen) } #endif #ifdef I830_USE_EXA - if (pI830->useEXA && pI830->EXADriverPtr) { + if (pI830->EXADriverPtr) { exaDriverFini(pScreen); xfree(pI830->EXADriverPtr); pI830->EXADriverPtr = NULL; } #endif +#ifdef I830_USE_UXA + if (pI830->uxa_driver) { + uxa_driver_fini (pScreen); + xfree (pI830->uxa_driver); + pI830->uxa_driver = NULL; + } +#endif xf86_cursors_fini (pScreen); i830_allocator_fini(pScrn); @@ -3830,19 +3856,34 @@ i830WaitSync(ScrnInfoPtr pScrn) { I830Ptr pI830 = I830PTR(pScrn); + switch (pI830->accel) { #ifdef I830_USE_XAA - if (!pI830->noAccel && !pI830->useEXA && pI830->AccelInfoRec - && pI830->AccelInfoRec->NeedToSync) { - (*pI830->AccelInfoRec->Sync)(pScrn); - pI830->AccelInfoRec->NeedToSync = FALSE; - } + case ACCEL_XAA: + if (pI830->AccelInfoRec && pI830->AccelInfoRec->NeedToSync) { + (*pI830->AccelInfoRec->Sync)(pScrn); + pI830->AccelInfoRec->NeedToSync = FALSE; + } + break; #endif #ifdef I830_USE_EXA - if (!pI830->noAccel && pI830->useEXA && pI830->EXADriverPtr) { - ScreenPtr pScreen = screenInfo.screens[pScrn->scrnIndex]; - exaWaitSync(pScreen); - } + case ACCEL_EXA: + if (pI830->EXADriverPtr) { + ScreenPtr pScreen = screenInfo.screens[pScrn->scrnIndex]; + exaWaitSync(pScreen); + } + break; #endif +#ifdef I830_USE_UXA + case ACCEL_UXA: + if (pI830->uxa_driver) { + ScreenPtr pScreen = screenInfo.screens[pScrn->scrnIndex]; + uxa_wait_sync(pScreen); + } + break; +#endif + default: + break; + } } void @@ -3850,16 +3891,32 @@ i830MarkSync(ScrnInfoPtr pScrn) { I830Ptr pI830 = I830PTR(pScrn); + switch (pI830->accel) { #ifdef I830_USE_XAA - if (!pI830->useEXA && pI830->AccelInfoRec) - pI830->AccelInfoRec->NeedToSync = TRUE; + case ACCEL_XAA: + if (pI830->AccelInfoRec) + pI830->AccelInfoRec->NeedToSync = TRUE; + break; #endif #ifdef I830_USE_EXA - if (pI830->useEXA && pI830->EXADriverPtr) { - ScreenPtr pScreen = screenInfo.screens[pScrn->scrnIndex]; - exaMarkSync(pScreen); - } + case ACCEL_EXA: + if (pI830->EXADriverPtr) { + ScreenPtr pScreen = screenInfo.screens[pScrn->scrnIndex]; + exaMarkSync(pScreen); + } + break; +#endif +#ifdef I830_USE_UXA + case ACCEL_UXA: + if (pI830->uxa_driver) { + ScreenPtr pScreen = screenInfo.screens[pScrn->scrnIndex]; + exaMarkSync(pScreen); + } + break; #endif + default: + break; + } } void diff --git a/src/i830_exa.c b/src/i830_exa.c index 75ccd742..a6705f48 100644 --- a/src/i830_exa.c +++ b/src/i830_exa.c @@ -430,7 +430,7 @@ I830EXAInit(ScreenPtr pScreen) pI830->EXADriverPtr = exaDriverAlloc(); if (pI830->EXADriverPtr == NULL) { - pI830->noAccel = TRUE; + pI830->accel = ACCEL_NONE; return FALSE; } memset(pI830->EXADriverPtr, 0, sizeof(*pI830->EXADriverPtr)); @@ -558,7 +558,7 @@ I830EXAInit(ScreenPtr pScreen) pI830->EXADriverPtr->exa_minor = 0; if(!exaDriverInit(pScreen, pI830->EXADriverPtr)) { xfree(pI830->EXADriverPtr); - pI830->noAccel = TRUE; + pI830->accel = ACCEL_NONE; return FALSE; } } @@ -568,6 +568,133 @@ I830EXAInit(ScreenPtr pScreen) return TRUE; } +static Bool +i830_uxa_pixmap_is_offscreen(PixmapPtr pPixmap) +{ + ScrnInfoPtr pScrn = xf86Screens[pPixmap->drawable.pScreen->myNum]; + I830Ptr pI830 = I830PTR(pScrn); + + /* XXX for now, eventually we'll support 'real' off-screen pixmaps */ + if ((void *)pPixmap->devPrivate.ptr >= (void *)pI830->FbBase && + (void *)pPixmap->devPrivate.ptr < + (void *)(pI830->FbBase + pI830->FbMapSize)) + { + return TRUE; + } else { + return FALSE; + } +} + + +Bool +i830_uxa_init (ScreenPtr pScreen) +{ + ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; + I830Ptr pI830 = I830PTR(pScrn); + + pI830->uxa_driver = uxa_driver_alloc(); + if (pI830->uxa_driver == NULL) { + pI830->accel = ACCEL_NONE; + return FALSE; + } + memset(pI830->uxa_driver, 0, sizeof(*pI830->uxa_driver)); + + pI830->bufferOffset = 0; + pI830->uxa_driver->uxa_major = 1; + pI830->uxa_driver->uxa_minor = 0; + + /* Limits are described in the BLT engine chapter under Graphics Data Size + * Limitations, and the descriptions of SURFACE_STATE, 3DSTATE_BUFFER_INFO, + * 3DSTATE_DRAWING_RECTANGLE, 3DSTATE_MAP_INFO, and 3DSTATE_MAP_INFO. + * + * i845 through i965 limits 2D rendering to 65536 lines and pitch of 32768. + * + * i965 limits 3D surface to (2*element size)-aligned offset if un-tiled. + * i965 limits 3D surface to 4kB-aligned offset if tiled. + * i965 limits 3D surfaces to w,h of ?,8192. + * i965 limits 3D surface to pitch of 1B - 128kB. + * i965 limits 3D surface pitch alignment to 1 or 2 times the element size. + * i965 limits 3D surface pitch alignment to 512B if tiled. + * i965 limits 3D destination drawing rect to w,h of 8192,8192. + * + * i915 limits 3D textures to 4B-aligned offset if un-tiled. + * i915 limits 3D textures to ~4kB-aligned offset if tiled. + * i915 limits 3D textures to width,height of 2048,2048. + * i915 limits 3D textures to pitch of 16B - 8kB, in dwords. + * i915 limits 3D destination to ~4kB-aligned offset if tiled. + * i915 limits 3D destination to pitch of 16B - 8kB, in dwords, if un-tiled. + * i915 limits 3D destination to pitch of 512B - 8kB, in tiles, if tiled. + * i915 limits 3D destination to POT aligned pitch if tiled. + * i915 limits 3D destination drawing rect to w,h of 2048,2048. + * + * i845 limits 3D textures to 4B-aligned offset if un-tiled. + * i845 limits 3D textures to ~4kB-aligned offset if tiled. + * i845 limits 3D textures to width,height of 2048,2048. + * i845 limits 3D textures to pitch of 4B - 8kB, in dwords. + * i845 limits 3D destination to 4B-aligned offset if un-tiled. + * i845 limits 3D destination to ~4kB-aligned offset if tiled. + * i845 limits 3D destination to pitch of 8B - 8kB, in dwords. + * i845 limits 3D destination drawing rect to w,h of 2048,2048. + * + * For the tiled issues, the only tiled buffer we draw to should be + * the front, which will have an appropriate pitch/offset already set up, + * so EXA doesn't need to worry. + */ + if (IS_I965G(pI830)) { + pI830->uxa_driver->maxX = 8192; + pI830->uxa_driver->maxY = 8192; + } else { + pI830->uxa_driver->maxX = 2048; + pI830->uxa_driver->maxY = 2048; + } + + /* Sync */ + pI830->uxa_driver->WaitMarker = I830EXASync; + + /* Solid fill */ + pI830->uxa_driver->PrepareSolid = I830EXAPrepareSolid; + pI830->uxa_driver->Solid = I830EXASolid; + pI830->uxa_driver->DoneSolid = I830EXADoneSolid; + + /* Copy */ + pI830->uxa_driver->PrepareCopy = I830EXAPrepareCopy; + pI830->uxa_driver->Copy = I830EXACopy; + pI830->uxa_driver->DoneCopy = I830EXADoneCopy; + + /* Composite */ + if (!IS_I9XX(pI830)) { + pI830->uxa_driver->CheckComposite = i830_check_composite; + pI830->uxa_driver->PrepareComposite = i830_prepare_composite; + pI830->uxa_driver->Composite = i830_composite; + pI830->uxa_driver->DoneComposite = i830_done_composite; + } else if (IS_I915G(pI830) || IS_I915GM(pI830) || + IS_I945G(pI830) || IS_I945GM(pI830) || IS_G33CLASS(pI830)) + { + pI830->uxa_driver->CheckComposite = i915_check_composite; + pI830->uxa_driver->PrepareComposite = i915_prepare_composite; + pI830->uxa_driver->Composite = i830_composite; + pI830->uxa_driver->DoneComposite = i830_done_composite; + } else { + pI830->uxa_driver->CheckComposite = i965_check_composite; + pI830->uxa_driver->PrepareComposite = i965_prepare_composite; + pI830->uxa_driver->Composite = i965_composite; + pI830->uxa_driver->DoneComposite = i830_done_composite; + } + pI830->uxa_driver->PixmapIsOffscreen = i830_uxa_pixmap_is_offscreen; + + if(!uxa_driver_init(pScreen, pI830->uxa_driver)) { + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "UXA initialization failed\n"); + xfree(pI830->uxa_driver); + pI830->accel = ACCEL_NONE; + return FALSE; + } + + I830SelectBuffer(pScrn, I830_SELECT_FRONT); + + return TRUE; +} + #ifdef XF86DRI #ifndef ExaOffscreenMarkUsed diff --git a/src/i830_memory.c b/src/i830_memory.c index b62bda05..c1748b3e 100644 --- a/src/i830_memory.c +++ b/src/i830_memory.c @@ -449,7 +449,7 @@ i830_allocator_init(ScrnInfoPtr pScrn, unsigned long offset, unsigned long size) mmsize = size; /* EXA area is fixed. */ - if (pI830->useEXA) { + if (pI830->accel == ACCEL_EXA) { mmsize -= ROUND_TO_PAGE(3 * pScrn->displayWidth * pI830->cpp * pScrn->virtualY); } @@ -1022,7 +1022,7 @@ i830_allocate_ringbuffer(ScrnInfoPtr pScrn) { I830Ptr pI830 = I830PTR(pScrn); - if (pI830->noAccel || pI830->memory_manager || pI830->LpRing->mem != NULL) + if (pI830->accel == ACCEL_NONE || pI830->memory_manager || pI830->LpRing->mem != NULL) return TRUE; /* We don't have any mechanism in the DRM yet to alert it that we've moved @@ -1167,7 +1167,7 @@ i830_allocate_framebuffer(ScrnInfoPtr pScrn, I830Ptr pI830, BoxPtr FbMemBox, minspace = pitch * pScrn->virtualY; avail = pScrn->videoRam * 1024; - if (!pI830->useEXA) { + if (pI830->accel == ACCEL_XAA) { maxCacheLines = (avail - minspace) / pitch; /* This shouldn't happen. */ if (maxCacheLines < 0) { @@ -1198,7 +1198,7 @@ i830_allocate_framebuffer(ScrnInfoPtr pScrn, I830Ptr pI830, BoxPtr FbMemBox, "Allocating %d scanlines for pixmap cache\n", cacheLines); } else { - /* For EXA, we have a separate allocation for the linear allocator + /* For non-XAA, we have a separate allocation for the linear allocator * which also does the pixmap cache. */ cacheLines = 0; @@ -1213,7 +1213,7 @@ i830_allocate_framebuffer(ScrnInfoPtr pScrn, I830Ptr pI830, BoxPtr FbMemBox, * acceleration operations (non-XY COLOR_BLT) can't be done to tiled * buffers. */ - if (!pI830->useEXA && IS_I965G(pI830)) + if (pI830->accel <= ACCEL_XAA && IS_I965G(pI830)) tiling = FALSE; else tiling = pI830->tiling; @@ -1412,7 +1412,7 @@ i830_allocate_2d_memory(ScrnInfoPtr pScrn) } /* even in XAA, 965G needs state mem buffer for rendering */ - if (IS_I965G(pI830) && !pI830->noAccel && + if (IS_I965G(pI830) && pI830->accel != ACCEL_NONE && pI830->gen4_render_state_mem == NULL) { pI830->gen4_render_state_mem = @@ -1450,7 +1450,7 @@ i830_allocate_2d_memory(ScrnInfoPtr pScrn) return FALSE; #ifdef I830_USE_EXA - if (pI830->useEXA) { + if (pI830->accel == ACCEL_EXA) { if (pI830->exa_offscreen == NULL) { /* Default EXA to having 3 screens worth of offscreen memory space * (for pixmaps). @@ -1478,7 +1478,7 @@ i830_allocate_2d_memory(ScrnInfoPtr pScrn) } #endif /* I830_USE_EXA */ - if (!pI830->noAccel && !pI830->useEXA) { + if (pI830->accel == ACCEL_XAA) { /* The lifetime fixed offset of xaa scratch is probably not required, * but we do some setup using it at XAAInit() time. And XAA may not * end up being supported with GEM anyway. diff --git a/src/i830_video.c b/src/i830_video.c index 486f6708..1719835c 100644 --- a/src/i830_video.c +++ b/src/i830_video.c @@ -2460,13 +2460,13 @@ I830PutImage(ScrnInfoPtr pScrn, } #ifdef I830_USE_EXA - if (pPriv->textured && pI830->useEXA) { + if (pPriv->textured && pI830->accel == ACCEL_EXA) { /* Force the pixmap into framebuffer so we can draw to it. */ exaMoveInPixmap(pPixmap); } #endif - if (pPriv->textured && !pI830->useEXA && + if (pPriv->textured && pI830->accel <= ACCEL_XAA && (((char *)pPixmap->devPrivate.ptr < (char *)pI830->FbBase) || ((char *)pPixmap->devPrivate.ptr >= (char *)pI830->FbBase + pI830->FbMapSize))) { diff --git a/uxa/Makefile.am b/uxa/Makefile.am new file mode 100644 index 00000000..641b4146 --- /dev/null +++ b/uxa/Makefile.am @@ -0,0 +1,20 @@ +noinst_LTLIBRARIES = libuxa.la + +# Override these since UXA doesn't need them and the needed files aren't +# built (in hw/xfree86/os-support/solaris) until after UXA is built +SOLARIS_ASM_CFLAGS="" + +INCLUDES = \ + $(XORG_INCS) + +AM_CFLAGS = $(WARN_CFLAGS) $(XORG_CFLAGS) $(DIX_CFLAGS) + +libuxa_la_SOURCES = \ + uxa.c \ + uxa.h \ + uxa-accel.c \ + uxa-glyphs.c \ + uxa-render.c \ + uxa-priv.h \ + uxa-unaccel.c + diff --git a/uxa/uxa-accel.c b/uxa/uxa-accel.c new file mode 100644 index 00000000..7c7b3e9b --- /dev/null +++ b/uxa/uxa-accel.c @@ -0,0 +1,1038 @@ +/* + * 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. + * + * Authors: + * Eric Anholt <eric@anholt.net> + * Michel Dänzer <michel@tungstengraphics.com> + * + */ + +#ifdef HAVE_DIX_CONFIG_H +#include <dix-config.h> +#endif +#include "uxa-priv.h" +#include <X11/fonts/fontstruct.h> +#include "dixfontstr.h" +#include "uxa.h" + +static void +uxa_fill_spans(DrawablePtr pDrawable, GCPtr pGC, int n, + DDXPointPtr ppt, int *pwidth, int fSorted) +{ + ScreenPtr pScreen = pDrawable->pScreen; + uxa_screen_t *uxa_screen = uxa_get_screen(pScreen); + RegionPtr pClip = fbGetCompositeClip(pGC); + PixmapPtr pPixmap = uxa_get_drawable_pixmap (pDrawable); + BoxPtr pextent, pbox; + int nbox; + int extentX1, extentX2, extentY1, extentY2; + int fullX1, fullX2, fullY1; + int partX1, partX2; + int off_x, off_y; + + if (uxa_screen->swappedOut || pGC->fillStyle != FillSolid || + !(pPixmap = uxa_get_offscreen_pixmap (pDrawable, &off_x, &off_y)) || + !(*uxa_screen->info->PrepareSolid) (pPixmap, + pGC->alu, + pGC->planemask, + pGC->fgPixel)) + { + uxa_check_fill_spans (pDrawable, pGC, n, ppt, pwidth, fSorted); + return; + } + + pextent = REGION_EXTENTS(pGC->pScreen, pClip); + extentX1 = pextent->x1; + extentY1 = pextent->y1; + extentX2 = pextent->x2; + extentY2 = pextent->y2; + while (n--) + { + fullX1 = ppt->x; + fullY1 = ppt->y; + fullX2 = fullX1 + (int) *pwidth; + ppt++; + pwidth++; + + if (fullY1 < extentY1 || extentY2 <= fullY1) + continue; + + if (fullX1 < extentX1) + fullX1 = extentX1; + + if (fullX2 > extentX2) + fullX2 = extentX2; + + if (fullX1 >= fullX2) + continue; + + nbox = REGION_NUM_RECTS (pClip); + if (nbox == 1) + { + (*uxa_screen->info->Solid) (pPixmap, + fullX1 + off_x, fullY1 + off_y, + fullX2 + off_x, fullY1 + 1 + off_y); + } + else + { + pbox = REGION_RECTS(pClip); + while(nbox--) + { + if (pbox->y1 <= fullY1 && fullY1 < pbox->y2) + { + partX1 = pbox->x1; + if (partX1 < fullX1) + partX1 = fullX1; + partX2 = pbox->x2; + if (partX2 > fullX2) + partX2 = fullX2; + if (partX2 > partX1) { + (*uxa_screen->info->Solid) (pPixmap, + partX1 + off_x, fullY1 + off_y, + partX2 + off_x, fullY1 + 1 + off_y); + } + } + pbox++; + } + } + } + (*uxa_screen->info->DoneSolid) (pPixmap); + uxa_mark_sync(pScreen); +} + +static Bool +uxa_do_put_image (DrawablePtr pDrawable, GCPtr pGC, int depth, int x, int y, + int w, int h, int format, char *bits, int src_stride) +{ + uxa_screen_t *uxa_screen = uxa_get_screen(pDrawable->pScreen); + PixmapPtr pPix = uxa_get_drawable_pixmap (pDrawable); + RegionPtr pClip; + BoxPtr pbox; + int nbox; + int xoff, yoff; + int bpp = pDrawable->bitsPerPixel; + Bool access_prepared = FALSE; + + /* Don't bother with under 8bpp, XYPixmaps. */ + if (format != ZPixmap || bpp < 8) + return FALSE; + + /* Only accelerate copies: no rop or planemask. */ + if (!UXA_PM_IS_SOLID(pDrawable, pGC->planemask) || pGC->alu != GXcopy) + return FALSE; + + if (uxa_screen->swappedOut) + return FALSE; + + pPix = uxa_get_offscreen_pixmap (pDrawable, &xoff, &yoff); + + if (!pPix || !uxa_screen->info->UploadToScreen) + return FALSE; + + x += pDrawable->x; + y += pDrawable->y; + + pClip = fbGetCompositeClip(pGC); + for (nbox = REGION_NUM_RECTS(pClip), + pbox = REGION_RECTS(pClip); + nbox--; + pbox++) + { + int x1 = x; + int y1 = y; + int x2 = x + w; + int y2 = y + h; + char *src; + Bool ok; + + if (x1 < pbox->x1) + x1 = pbox->x1; + if (y1 < pbox->y1) + y1 = pbox->y1; + if (x2 > pbox->x2) + x2 = pbox->x2; + if (y2 > pbox->y2) + y2 = pbox->y2; + if (x1 >= x2 || y1 >= y2) + continue; + + src = bits + (y1 - y) * src_stride + (x1 - x) * (bpp / 8); + ok = uxa_screen->info->UploadToScreen(pPix, x1 + xoff, y1 + yoff, + x2 - x1, y2 - y1, src, src_stride); + /* If we fail to accelerate the upload, fall back to using unaccelerated + * fb calls. + */ + if (!ok) { + FbStip *dst; + FbStride dst_stride; + int dstBpp; + int dstXoff, dstYoff; + + if (!access_prepared) { + uxa_prepare_access(pDrawable, UXA_PREPARE_DEST); + + access_prepared = TRUE; + } + + fbGetStipDrawable(pDrawable, dst, dst_stride, dstBpp, + dstXoff, dstYoff); + + fbBltStip((FbStip *)bits + (y1 - y) * (src_stride / sizeof(FbStip)), + src_stride / sizeof(FbStip), + (x1 - x) * dstBpp, + dst + (y1 + dstYoff) * dst_stride, + dst_stride, + (x1 + dstXoff) * dstBpp, + (x2 - x1) * dstBpp, + y2 - y1, + GXcopy, FB_ALLONES, dstBpp); + } + } + + if (access_prepared) + uxa_finish_access(pDrawable, UXA_PREPARE_DEST); + else + uxa_mark_sync(pDrawable->pScreen); + + return TRUE; +} + +#ifdef MITSHM + +static Bool +uxa_do_shm_put_image(DrawablePtr pDrawable, GCPtr pGC, int depth, + unsigned int format, int w, int h, int sx, int sy, int sw, + int sh, int dx, int dy, char *data) +{ + int src_stride = PixmapBytePad(w, depth); + + if (uxa_do_put_image(pDrawable, pGC, depth, dx, dy, sw, sh, format, data + + sy * src_stride + sx * BitsPerPixel(depth) / 8, + src_stride)) + return TRUE; + + if (format == ZPixmap) + { + PixmapPtr pPixmap; + + pPixmap = GetScratchPixmapHeader(pDrawable->pScreen, w, h, depth, + BitsPerPixel(depth), PixmapBytePad(w, depth), (pointer)data); + if (!pPixmap) + return FALSE; + + uxa_prepare_access (pDrawable, UXA_PREPARE_DEST); + + fbCopyArea((DrawablePtr)pPixmap, pDrawable, pGC, sx, sy, sw, sh, dx, dy); + uxa_finish_access(pDrawable, UXA_PREPARE_DEST); + + FreeScratchPixmapHeader(pPixmap); + + return TRUE; + } + + return FALSE; +} + +/* The actual ShmPutImage isn't wrapped by the damage layer, so we need to + * inform any interested parties of the damage incurred to the drawable. + * + * We also need to set the pending damage to ensure correct migration in all + * cases. + */ +void +uxa_shm_put_image(DrawablePtr pDrawable, GCPtr pGC, int depth, unsigned int format, + int w, int h, int sx, int sy, int sw, int sh, int dx, int dy, + char *data) +{ + if (!uxa_do_shm_put_image(pDrawable, pGC, depth, format, w, h, sx, sy, sw, sh, + dx, dy, data)) { + uxa_prepare_access (pDrawable, UXA_PREPARE_DEST); + fbShmPutImage(pDrawable, pGC, depth, format, w, h, sx, sy, sw, sh, dx, dy, + data); + uxa_finish_access(pDrawable, UXA_PREPARE_DEST); + } +} + +ShmFuncs uxa_shm_funcs = { NULL, uxa_shm_put_image }; + +#endif + +static void +uxa_put_image (DrawablePtr pDrawable, GCPtr pGC, int depth, int x, int y, + int w, int h, int leftPad, int format, char *bits) +{ +#ifdef MITSHM + if (!uxa_do_shm_put_image(pDrawable, pGC, depth, format, w, h, 0, 0, w, h, x, y, + bits)) +#else + if (!uxa_do_put_image(pDrawable, pGC, depth, x, y, w, h, format, bits, + PixmapBytePad(w, pDrawable->depth))) +#endif + uxa_check_put_image(pDrawable, pGC, depth, x, y, w, h, leftPad, format, + bits); +} + +static Bool inline +uxa_copy_n_to_n_two_dir (DrawablePtr pSrcDrawable, DrawablePtr pDstDrawable, + GCPtr pGC, BoxPtr pbox, int nbox, int dx, int dy) +{ + uxa_screen_t *uxa_screen = uxa_get_screen(pDstDrawable->pScreen); + PixmapPtr pSrcPixmap, pDstPixmap; + int src_off_x, src_off_y, dst_off_x, dst_off_y; + int dirsetup; + + /* Need to get both pixmaps to call the driver routines */ + pSrcPixmap = uxa_get_offscreen_pixmap (pSrcDrawable, &src_off_x, &src_off_y); + pDstPixmap = uxa_get_offscreen_pixmap (pDstDrawable, &dst_off_x, &dst_off_y); + if (!pSrcPixmap || !pDstPixmap) + return FALSE; + + /* + * Now the case of a chip that only supports xdir = ydir = 1 or + * xdir = ydir = -1, but we have xdir != ydir. + */ + dirsetup = 0; /* No direction set up yet. */ + for (; nbox; pbox++, nbox--) { + if (dx >= 0 && (src_off_y + pbox->y1 + dy) != pbox->y1) { + /* Do a xdir = ydir = -1 blit instead. */ + if (dirsetup != -1) { + if (dirsetup != 0) + uxa_screen->info->DoneCopy(pDstPixmap); + dirsetup = -1; + if (!(*uxa_screen->info->PrepareCopy)(pSrcPixmap, + pDstPixmap, + -1, -1, + pGC ? pGC->alu : GXcopy, + pGC ? pGC->planemask : + FB_ALLONES)) + return FALSE; + } + (*uxa_screen->info->Copy)(pDstPixmap, + src_off_x + pbox->x1 + dx, + src_off_y + pbox->y1 + dy, + dst_off_x + pbox->x1, + dst_off_y + pbox->y1, + pbox->x2 - pbox->x1, + pbox->y2 - pbox->y1); + } else if (dx < 0 && (src_off_y + pbox->y1 + dy) != pbox->y1) { + /* Do a xdir = ydir = 1 blit instead. */ + if (dirsetup != 1) { + if (dirsetup != 0) + uxa_screen->info->DoneCopy(pDstPixmap); + dirsetup = 1; + if (!(*uxa_screen->info->PrepareCopy)(pSrcPixmap, + pDstPixmap, + 1, 1, + pGC ? pGC->alu : GXcopy, + pGC ? pGC->planemask : + FB_ALLONES)) + return FALSE; + } + (*uxa_screen->info->Copy)(pDstPixmap, + src_off_x + pbox->x1 + dx, + src_off_y + pbox->y1 + dy, + dst_off_x + pbox->x1, + dst_off_y + pbox->y1, + pbox->x2 - pbox->x1, + pbox->y2 - pbox->y1); + } else if (dx >= 0) { + /* + * xdir = 1, ydir = -1. + * Perform line-by-line xdir = ydir = 1 blits, going up. + */ + int i; + if (dirsetup != 1) { + if (dirsetup != 0) + uxa_screen->info->DoneCopy(pDstPixmap); + dirsetup = 1; + if (!(*uxa_screen->info->PrepareCopy)(pSrcPixmap, + pDstPixmap, + 1, 1, + pGC ? pGC->alu : GXcopy, + pGC ? pGC->planemask : + FB_ALLONES)) + return FALSE; + } + for (i = pbox->y2 - pbox->y1 - 1; i >= 0; i--) + (*uxa_screen->info->Copy)(pDstPixmap, + src_off_x + pbox->x1 + dx, + src_off_y + pbox->y1 + dy + i, + dst_off_x + pbox->x1, + dst_off_y + pbox->y1 + i, + pbox->x2 - pbox->x1, 1); + } else { + /* + * xdir = -1, ydir = 1. + * Perform line-by-line xdir = ydir = -1 blits, going down. + */ + int i; + if (dirsetup != -1) { + if (dirsetup != 0) + uxa_screen->info->DoneCopy(pDstPixmap); + dirsetup = -1; + if (!(*uxa_screen->info->PrepareCopy)(pSrcPixmap, + pDstPixmap, + -1, -1, + pGC ? pGC->alu : GXcopy, + pGC ? pGC->planemask : + FB_ALLONES)) + return FALSE; + } + for (i = 0; i < pbox->y2 - pbox->y1; i++) + (*uxa_screen->info->Copy)(pDstPixmap, + src_off_x + pbox->x1 + dx, + src_off_y + pbox->y1 + dy + i, + dst_off_x + pbox->x1, + dst_off_y + pbox->y1 + i, + pbox->x2 - pbox->x1, 1); + } + } + if (dirsetup != 0) + uxa_screen->info->DoneCopy(pDstPixmap); + uxa_mark_sync(pDstDrawable->pScreen); + return TRUE; +} + +void +uxa_copy_n_to_n (DrawablePtr pSrcDrawable, + DrawablePtr pDstDrawable, + GCPtr pGC, + BoxPtr pbox, + int nbox, + int dx, + int dy, + Bool reverse, + Bool upsidedown, + Pixel bitplane, + void *closure) +{ + uxa_screen_t *uxa_screen = uxa_get_screen(pDstDrawable->pScreen); + int src_off_x, src_off_y; + int dst_off_x, dst_off_y; + PixmapPtr pSrcPixmap, pDstPixmap; + + pSrcPixmap = uxa_get_drawable_pixmap (pSrcDrawable); + pDstPixmap = uxa_get_drawable_pixmap (pDstDrawable); + + uxa_get_drawable_deltas (pSrcDrawable, pSrcPixmap, &src_off_x, &src_off_y); + uxa_get_drawable_deltas (pDstDrawable, pDstPixmap, &dst_off_x, &dst_off_y); + + /* Mixed directions must be handled specially if the card is lame */ + if ((uxa_screen->info->flags & UXA_TWO_BITBLT_DIRECTIONS) && + reverse != upsidedown) { + if (uxa_copy_n_to_n_two_dir(pSrcDrawable, pDstDrawable, pGC, pbox, nbox, + dx, dy)) + return; + goto fallback; + } + + if (!uxa_pixmap_is_offscreen(pSrcPixmap) || + !uxa_pixmap_is_offscreen(pDstPixmap) || + !(*uxa_screen->info->PrepareCopy) (pSrcPixmap, pDstPixmap, reverse ? -1 : 1, + upsidedown ? -1 : 1, + pGC ? pGC->alu : GXcopy, + pGC ? pGC->planemask : FB_ALLONES)) { + goto fallback; + } + + while (nbox--) + { + (*uxa_screen->info->Copy) (pDstPixmap, + pbox->x1 + dx + src_off_x, + pbox->y1 + dy + src_off_y, + pbox->x1 + dst_off_x, pbox->y1 + dst_off_y, + pbox->x2 - pbox->x1, pbox->y2 - pbox->y1); + pbox++; + } + + (*uxa_screen->info->DoneCopy) (pDstPixmap); + uxa_mark_sync (pDstDrawable->pScreen); + + return; + +fallback: + UXA_FALLBACK(("from %p to %p (%c,%c)\n", pSrcDrawable, pDstDrawable, + uxa_drawable_location(pSrcDrawable), + uxa_drawable_location(pDstDrawable))); + uxa_prepare_access (pDstDrawable, UXA_PREPARE_DEST); + uxa_prepare_access (pSrcDrawable, UXA_PREPARE_SRC); + fbCopyNtoN (pSrcDrawable, pDstDrawable, pGC, pbox, nbox, dx, dy, reverse, + upsidedown, bitplane, closure); + uxa_finish_access (pSrcDrawable, UXA_PREPARE_SRC); + uxa_finish_access (pDstDrawable, UXA_PREPARE_DEST); +} + +RegionPtr +uxa_copy_area(DrawablePtr pSrcDrawable, DrawablePtr pDstDrawable, GCPtr pGC, + int srcx, int srcy, int width, int height, int dstx, int dsty) +{ + uxa_screen_t *uxa_screen = uxa_get_screen(pDstDrawable->pScreen); + + if (uxa_screen->swappedOut) { + return uxa_check_copy_area(pSrcDrawable, pDstDrawable, pGC, + srcx, srcy, width, height, dstx, dsty); + } + + return fbDoCopy (pSrcDrawable, pDstDrawable, pGC, + srcx, srcy, width, height, + dstx, dsty, uxa_copy_n_to_n, 0, NULL); +} + +static void +uxa_poly_point(DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, + DDXPointPtr ppt) +{ + int i; + xRectangle *prect; + + /* If we can't reuse the current GC as is, don't bother accelerating the + * points. + */ + if (pGC->fillStyle != FillSolid) { + uxa_check_poly_point(pDrawable, pGC, mode, npt, ppt); + return; + } + + prect = xalloc(sizeof(xRectangle) * npt); + for (i = 0; i < npt; i++) { + prect[i].x = ppt[i].x; + prect[i].y = ppt[i].y; + if (i > 0 && mode == CoordModePrevious) { + prect[i].x += prect[i - 1].x; + prect[i].y += prect[i - 1].y; + } + prect[i].width = 1; + prect[i].height = 1; + } + pGC->ops->PolyFillRect(pDrawable, pGC, npt, prect); + xfree(prect); +} + +/** + * uxa_poly_lines() checks if it can accelerate the lines as a group of + * horizontal or vertical lines (rectangles), and uses existing rectangle fill + * acceleration if so. + */ +static void +uxa_poly_lines(DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, + DDXPointPtr ppt) +{ + xRectangle *prect; + int x1, x2, y1, y2; + int i; + + /* Don't try to do wide lines or non-solid fill style. */ + if (pGC->lineWidth != 0 || pGC->lineStyle != LineSolid || + pGC->fillStyle != FillSolid) { + uxa_check_poly_lines(pDrawable, pGC, mode, npt, ppt); + return; + } + + prect = xalloc(sizeof(xRectangle) * (npt - 1)); + x1 = ppt[0].x; + y1 = ppt[0].y; + /* If we have any non-horizontal/vertical, fall back. */ + for (i = 0; i < npt - 1; i++) { + if (mode == CoordModePrevious) { + x2 = x1 + ppt[i + 1].x; + y2 = y1 + ppt[i + 1].y; + } else { + x2 = ppt[i + 1].x; + y2 = ppt[i + 1].y; + } + + if (x1 != x2 && y1 != y2) { + xfree(prect); + uxa_check_poly_lines(pDrawable, pGC, mode, npt, ppt); + return; + } + + if (x1 < x2) { + prect[i].x = x1; + prect[i].width = x2 - x1 + 1; + } else { + prect[i].x = x2; + prect[i].width = x1 - x2 + 1; + } + if (y1 < y2) { + prect[i].y = y1; + prect[i].height = y2 - y1 + 1; + } else { + prect[i].y = y2; + prect[i].height = y1 - y2 + 1; + } + + x1 = x2; + y1 = y2; + } + pGC->ops->PolyFillRect(pDrawable, pGC, npt - 1, prect); + xfree(prect); +} + +/** + * uxa_poly_segment() checks if it can accelerate the lines as a group of + * horizontal or vertical lines (rectangles), and uses existing rectangle fill + * acceleration if so. + */ +static void +uxa_poly_segment (DrawablePtr pDrawable, GCPtr pGC, int nseg, + xSegment *pSeg) +{ + xRectangle *prect; + int i; + + /* Don't try to do wide lines or non-solid fill style. */ + if (pGC->lineWidth != 0 || pGC->lineStyle != LineSolid || + pGC->fillStyle != FillSolid) + { + uxa_check_poly_segment(pDrawable, pGC, nseg, pSeg); + return; + } + + /* If we have any non-horizontal/vertical, fall back. */ + for (i = 0; i < nseg; i++) { + if (pSeg[i].x1 != pSeg[i].x2 && pSeg[i].y1 != pSeg[i].y2) { + uxa_check_poly_segment(pDrawable, pGC, nseg, pSeg); + return; + } + } + + prect = xalloc(sizeof(xRectangle) * nseg); + for (i = 0; i < nseg; i++) { + if (pSeg[i].x1 < pSeg[i].x2) { + prect[i].x = pSeg[i].x1; + prect[i].width = pSeg[i].x2 - pSeg[i].x1 + 1; + } else { + prect[i].x = pSeg[i].x2; + prect[i].width = pSeg[i].x1 - pSeg[i].x2 + 1; + } + if (pSeg[i].y1 < pSeg[i].y2) { + prect[i].y = pSeg[i].y1; + prect[i].height = pSeg[i].y2 - pSeg[i].y1 + 1; + } else { + prect[i].y = pSeg[i].y2; + prect[i].height = pSeg[i].y1 - pSeg[i].y2 + 1; + } + + /* don't paint last pixel */ + if (pGC->capStyle == CapNotLast) { + if (prect[i].width == 1) + prect[i].height--; + else + prect[i].width--; + } + } + pGC->ops->PolyFillRect(pDrawable, pGC, nseg, prect); + xfree(prect); +} + +static Bool uxa_fill_region_solid (DrawablePtr pDrawable, RegionPtr pRegion, + Pixel pixel, CARD32 planemask, CARD32 alu); + +static void +uxa_poly_fill_rect(DrawablePtr pDrawable, + GCPtr pGC, + int nrect, + xRectangle *prect) +{ + uxa_screen_t *uxa_screen = uxa_get_screen(pDrawable->pScreen); + RegionPtr pClip = fbGetCompositeClip(pGC); + PixmapPtr pPixmap = uxa_get_drawable_pixmap(pDrawable); + register BoxPtr pbox; + BoxPtr pextent; + int extentX1, extentX2, extentY1, extentY2; + int fullX1, fullX2, fullY1, fullY2; + int partX1, partX2, partY1, partY2; + int xoff, yoff; + int xorg, yorg; + int n; + RegionPtr pReg = RECTS_TO_REGION(pScreen, nrect, prect, CT_UNSORTED); + + /* Compute intersection of rects and clip region */ + REGION_TRANSLATE(pScreen, pReg, pDrawable->x, pDrawable->y); + REGION_INTERSECT(pScreen, pReg, pClip, pReg); + + if (!REGION_NUM_RECTS(pReg)) + goto out; + + uxa_get_drawable_deltas(pDrawable, pPixmap, &xoff, &yoff); + + if (uxa_screen->swappedOut) + goto fallback; + + /* For ROPs where overlaps don't matter, convert rectangles to region and + * call uxa_fill_region_{solid,tiled}. + */ + if ((pGC->fillStyle == FillSolid || pGC->fillStyle == FillTiled) && + (nrect == 1 || pGC->alu == GXcopy || pGC->alu == GXclear || + pGC->alu == GXnoop || pGC->alu == GXcopyInverted || + pGC->alu == GXset)) { + if (((pGC->fillStyle == FillSolid || pGC->tileIsPixel) && + uxa_fill_region_solid(pDrawable, pReg, pGC->fillStyle == FillSolid ? + pGC->fgPixel : pGC->tile.pixel, pGC->planemask, + pGC->alu)) || + (pGC->fillStyle == FillTiled && !pGC->tileIsPixel && + uxa_fill_region_tiled(pDrawable, pReg, pGC->tile.pixmap, &pGC->patOrg, + pGC->planemask, pGC->alu))) { + goto out; + } + } + + if (pGC->fillStyle != FillSolid && + !(pGC->tileIsPixel && pGC->fillStyle == FillTiled)) + { + goto fallback; + } + + if (!uxa_pixmap_is_offscreen (pPixmap) || + !(*uxa_screen->info->PrepareSolid) (pPixmap, + pGC->alu, + pGC->planemask, + pGC->fgPixel)) + { +fallback: + uxa_check_poly_fill_rect (pDrawable, pGC, nrect, prect); + goto out; + } + + xorg = pDrawable->x; + yorg = pDrawable->y; + + pextent = REGION_EXTENTS(pGC->pScreen, pClip); + extentX1 = pextent->x1; + extentY1 = pextent->y1; + extentX2 = pextent->x2; + extentY2 = pextent->y2; + while (nrect--) + { + fullX1 = prect->x + xorg; + fullY1 = prect->y + yorg; + fullX2 = fullX1 + (int) prect->width; + fullY2 = fullY1 + (int) prect->height; + prect++; + + if (fullX1 < extentX1) + fullX1 = extentX1; + + if (fullY1 < extentY1) + fullY1 = extentY1; + + if (fullX2 > extentX2) + fullX2 = extentX2; + + if (fullY2 > extentY2) + fullY2 = extentY2; + + if ((fullX1 >= fullX2) || (fullY1 >= fullY2)) + continue; + n = REGION_NUM_RECTS (pClip); + if (n == 1) + { + (*uxa_screen->info->Solid) (pPixmap, + fullX1 + xoff, fullY1 + yoff, + fullX2 + xoff, fullY2 + yoff); + } + else + { + pbox = REGION_RECTS(pClip); + /* + * clip the rectangle to each box in the clip region + * this is logically equivalent to calling Intersect(), + * but rectangles may overlap each other here. + */ + while(n--) + { + partX1 = pbox->x1; + if (partX1 < fullX1) + partX1 = fullX1; + partY1 = pbox->y1; + if (partY1 < fullY1) + partY1 = fullY1; + partX2 = pbox->x2; + if (partX2 > fullX2) + partX2 = fullX2; + partY2 = pbox->y2; + if (partY2 > fullY2) + partY2 = fullY2; + + pbox++; + + if (partX1 < partX2 && partY1 < partY2) { + (*uxa_screen->info->Solid) (pPixmap, + partX1 + xoff, partY1 + yoff, + partX2 + xoff, partY2 + yoff); + } + } + } + } + (*uxa_screen->info->DoneSolid) (pPixmap); + uxa_mark_sync(pDrawable->pScreen); + +out: + REGION_UNINIT(pScreen, pReg); + REGION_DESTROY(pScreen, pReg); +} + +const GCOps uxa_ops = { + uxa_fill_spans, + uxa_check_set_spans, + uxa_put_image, + uxa_copy_area, + uxa_check_copy_plane, + uxa_poly_point, + uxa_poly_lines, + uxa_poly_segment, + miPolyRectangle, + uxa_check_poly_arc, + miFillPolygon, + uxa_poly_fill_rect, + miPolyFillArc, + miPolyText8, + miPolyText16, + miImageText8, + miImageText16, + uxa_check_image_glyph_blt, + uxa_check_poly_glyph_blt, + uxa_check_push_pixels, +}; + +void +uxa_copy_window(WindowPtr pWin, DDXPointRec ptOldOrg, RegionPtr prgnSrc) +{ + RegionRec rgnDst; + int dx, dy; + PixmapPtr pPixmap = (*pWin->drawable.pScreen->GetWindowPixmap) (pWin); + + dx = ptOldOrg.x - pWin->drawable.x; + dy = ptOldOrg.y - pWin->drawable.y; + REGION_TRANSLATE(pWin->drawable.pScreen, prgnSrc, -dx, -dy); + + REGION_INIT (pWin->drawable.pScreen, &rgnDst, NullBox, 0); + + REGION_INTERSECT(pWin->drawable.pScreen, &rgnDst, &pWin->borderClip, prgnSrc); +#ifdef COMPOSITE + if (pPixmap->screen_x || pPixmap->screen_y) + REGION_TRANSLATE (pWin->drawable.pScreen, &rgnDst, + -pPixmap->screen_x, -pPixmap->screen_y); +#endif + + fbCopyRegion (&pPixmap->drawable, &pPixmap->drawable, + NULL, + &rgnDst, dx, dy, uxa_copy_n_to_n, 0, NULL); + + REGION_UNINIT(pWin->drawable.pScreen, &rgnDst); +} + +static Bool +uxa_fill_region_solid (DrawablePtr pDrawable, + RegionPtr pRegion, + Pixel pixel, + CARD32 planemask, + CARD32 alu) +{ + uxa_screen_t *uxa_screen = uxa_get_screen(pDrawable->pScreen); + PixmapPtr pPixmap = uxa_get_drawable_pixmap (pDrawable); + int xoff, yoff; + Bool ret = FALSE; + + uxa_get_drawable_deltas(pDrawable, pPixmap, &xoff, &yoff); + REGION_TRANSLATE(pScreen, pRegion, xoff, yoff); + + if (uxa_pixmap_is_offscreen (pPixmap) && + (*uxa_screen->info->PrepareSolid) (pPixmap, alu, planemask, pixel)) + { + int nbox; + BoxPtr pBox; + + nbox = REGION_NUM_RECTS (pRegion); + pBox = REGION_RECTS (pRegion); + + while (nbox--) + { + (*uxa_screen->info->Solid) (pPixmap, pBox->x1, pBox->y1, pBox->x2, + pBox->y2); + pBox++; + } + (*uxa_screen->info->DoneSolid) (pPixmap); + uxa_mark_sync(pDrawable->pScreen); + + ret = TRUE; + } + + REGION_TRANSLATE(pScreen, pRegion, -xoff, -yoff); + + return ret; +} + +/* Try to do an accelerated tile of the pTile into pRegion of pDrawable. + * Based on fbFillRegionTiled(), fbTile(). + */ +Bool +uxa_fill_region_tiled (DrawablePtr pDrawable, + RegionPtr pRegion, + PixmapPtr pTile, + DDXPointPtr pPatOrg, + CARD32 planemask, + CARD32 alu) +{ + uxa_screen_t *uxa_screen = uxa_get_screen(pDrawable->pScreen); + PixmapPtr pPixmap; + int xoff, yoff; + int tileWidth, tileHeight; + int nbox = REGION_NUM_RECTS (pRegion); + BoxPtr pBox = REGION_RECTS (pRegion); + Bool ret = FALSE; + + tileWidth = pTile->drawable.width; + tileHeight = pTile->drawable.height; + + /* If we're filling with a solid color, grab it out and go to + * FillRegionSolid, saving numerous copies. + */ + if (tileWidth == 1 && tileHeight == 1) + return uxa_fill_region_solid(pDrawable, pRegion, + uxa_get_pixmap_first_pixel (pTile), planemask, + alu); + + pPixmap = uxa_get_drawable_pixmap (pDrawable); + uxa_get_drawable_deltas(pDrawable, pPixmap, &xoff, &yoff); + REGION_TRANSLATE(pScreen, pRegion, xoff, yoff); + + pPixmap = uxa_get_offscreen_pixmap (pDrawable, &xoff, &yoff); + + if (!pPixmap || !uxa_pixmap_is_offscreen(pTile)) + goto out; + + if ((*uxa_screen->info->PrepareCopy) (pTile, pPixmap, 1, 1, alu, planemask)) + { + while (nbox--) + { + int height = pBox->y2 - pBox->y1; + int dstY = pBox->y1; + int tileY; + + modulus(dstY - yoff - pDrawable->y - pPatOrg->y, tileHeight, tileY); + + while (height > 0) { + int width = pBox->x2 - pBox->x1; + int dstX = pBox->x1; + int tileX; + int h = tileHeight - tileY; + + if (h > height) + h = height; + height -= h; + + modulus(dstX - xoff - pDrawable->x - pPatOrg->x, tileWidth, + tileX); + + while (width > 0) { + int w = tileWidth - tileX; + if (w > width) + w = width; + width -= w; + + (*uxa_screen->info->Copy) (pPixmap, tileX, tileY, dstX, dstY, + w, h); + dstX += w; + tileX = 0; + } + dstY += h; + tileY = 0; + } + pBox++; + } + (*uxa_screen->info->DoneCopy) (pPixmap); + uxa_mark_sync(pDrawable->pScreen); + + ret = TRUE; + } + +out: + REGION_TRANSLATE(pScreen, pRegion, -xoff, -yoff); + + return ret; +} + + +/** + * Accelerates GetImage for solid ZPixmap downloads from framebuffer memory. + * + * This is probably the only case we actually care about. The rest fall through + * to migration and fbGetImage, which hopefully will result in migration pushing + * the pixmap out of framebuffer. + */ +void +uxa_get_image (DrawablePtr pDrawable, int x, int y, int w, int h, + unsigned int format, unsigned long planeMask, char *d) +{ + uxa_screen_t *uxa_screen = uxa_get_screen(pDrawable->pScreen); + BoxRec Box; + PixmapPtr pPix = uxa_get_drawable_pixmap (pDrawable); + int xoff, yoff; + Bool ok; + + uxa_get_drawable_deltas (pDrawable, pPix, &xoff, &yoff); + + Box.x1 = pDrawable->y + x + xoff; + Box.y1 = pDrawable->y + y + yoff; + Box.x2 = Box.x1 + w; + Box.y2 = Box.y1 + h; + + if (uxa_screen->swappedOut) + goto fallback; + + pPix = uxa_get_offscreen_pixmap (pDrawable, &xoff, &yoff); + + if (pPix == NULL || uxa_screen->info->DownloadFromScreen == NULL) + goto fallback; + + /* Only cover the ZPixmap, solid copy case. */ + if (format != ZPixmap || !UXA_PM_IS_SOLID(pDrawable, planeMask)) + goto fallback; + + /* Only try to handle the 8bpp and up cases, since we don't want to think + * about <8bpp. + */ + if (pDrawable->bitsPerPixel < 8) + goto fallback; + + ok = uxa_screen->info->DownloadFromScreen(pPix, pDrawable->x + x + xoff, + pDrawable->y + y + yoff, w, h, d, + PixmapBytePad(w, pDrawable->depth)); + if (ok) { + uxa_wait_sync(pDrawable->pScreen); + goto out; + } + +fallback: + UXA_FALLBACK(("from %p (%c)\n", pDrawable, + uxa_drawable_location(pDrawable))); + + uxa_prepare_access (pDrawable, UXA_PREPARE_SRC); + fbGetImage (pDrawable, x, y, w, h, format, planeMask, d); + uxa_finish_access (pDrawable, UXA_PREPARE_SRC); + +out: + return; +} diff --git a/uxa/uxa-glyphs.c b/uxa/uxa-glyphs.c new file mode 100644 index 00000000..3c446405 --- /dev/null +++ b/uxa/uxa-glyphs.c @@ -0,0 +1,880 @@ +/* + * Copyright © 2008 Red Hat, Inc. + * Partly based on code Copyright © 2000 SuSE, 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 not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. Red Hat makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * Red Hat DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL Red Hat + * 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. + * + * 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 SuSE not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. SuSE makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE + * 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. + * + * Author: Owen Taylor <otaylor@fishsoup.net> + * Based on code by: Keith Packard + */ + +#ifdef HAVE_DIX_CONFIG_H +#include <dix-config.h> +#endif + +#include <stdlib.h> + +#include "uxa-priv.h" + +#include "mipict.h" + +#if DEBUG_GLYPH_CACHE +#define DBG_GLYPH_CACHE(a) ErrorF a +#else +#define DBG_GLYPH_CACHE(a) +#endif + +/* Width of the pixmaps we use for the caches; this should be less than + * max texture size of the driver; this may need to actually come from + * the driver. + */ +#define CACHE_PICTURE_WIDTH 1024 + +/* Maximum number of glyphs we buffer on the stack before flushing + * rendering to the mask or destination surface. + */ +#define GLYPH_BUFFER_SIZE 256 + +typedef struct { + PicturePtr source; + uxa_composite_rect_t rects[GLYPH_BUFFER_SIZE]; + int count; +} uxa_glyph_buffer_t; + +typedef enum { + UXA_GLYPH_SUCCESS, /* Glyph added to render buffer */ + UXA_GLYPH_FAIL, /* out of memory, etc */ + UXA_GLYPH_NEED_FLUSH, /* would evict a glyph already in the buffer */ +} uxa_glyph_cache_result_t; + +void +uxa_glyphs_init(ScreenPtr pScreen) +{ + uxa_screen_t *uxa_screen = uxa_get_screen(pScreen); + int i = 0; + + memset(uxa_screen->glyphCaches, 0, sizeof(uxa_screen->glyphCaches)); + + uxa_screen->glyphCaches[i].format = PICT_a8; + uxa_screen->glyphCaches[i].glyphWidth = uxa_screen->glyphCaches[i].glyphHeight = 16; + i++; + uxa_screen->glyphCaches[i].format = PICT_a8; + uxa_screen->glyphCaches[i].glyphWidth = uxa_screen->glyphCaches[i].glyphHeight = 32; + i++; + uxa_screen->glyphCaches[i].format = PICT_a8r8g8b8; + uxa_screen->glyphCaches[i].glyphWidth = uxa_screen->glyphCaches[i].glyphHeight = 16; + i++; + uxa_screen->glyphCaches[i].format = PICT_a8r8g8b8; + uxa_screen->glyphCaches[i].glyphWidth = uxa_screen->glyphCaches[i].glyphHeight = 32; + i++; + + assert(i == UXA_NUM_GLYPH_CACHES); + + for (i = 0; i < UXA_NUM_GLYPH_CACHES; i++) { + uxa_screen->glyphCaches[i].columns = CACHE_PICTURE_WIDTH / uxa_screen->glyphCaches[i].glyphWidth; + uxa_screen->glyphCaches[i].size = 256; + uxa_screen->glyphCaches[i].hashSize = 557; + } +} + +static void +uxa_unrealize_glyph_caches(ScreenPtr pScreen, + unsigned int format) +{ + uxa_screen_t *uxa_screen = uxa_get_screen(pScreen); + int i; + + for (i = 0; i < UXA_NUM_GLYPH_CACHES; i++) { + uxa_glyph_cache_t *cache = &uxa_screen->glyphCaches[i]; + + if (cache->format != format) + continue; + + if (cache->picture) { + FreePicture ((pointer) cache->picture, (XID) 0); + cache->picture = NULL; + } + + if (cache->hashEntries) { + xfree(cache->hashEntries); + cache->hashEntries = NULL; + } + + if (cache->glyphs) { + xfree(cache->glyphs); + cache->glyphs = NULL; + } + cache->glyphCount = 0; + } +} + +/* All caches for a single format share a single pixmap for glyph storage, + * allowing mixing glyphs of different sizes without paying a penalty + * for switching between source pixmaps. (Note that for a size of font + * right at the border between two sizes, we might be switching for almost + * every glyph.) + * + * This function allocates the storage pixmap, and then fills in the + * rest of the allocated structures for all caches with the given format. + */ +static Bool +uxa_realize_glyph_caches(ScreenPtr pScreen, + unsigned int format) +{ + uxa_screen_t *uxa_screen = uxa_get_screen(pScreen); + int depth = PIXMAN_FORMAT_DEPTH(format); + PictFormatPtr pPictFormat; + PixmapPtr pPixmap; + PicturePtr pPicture; + int height; + int i; + int error; + + pPictFormat = PictureMatchFormat(pScreen, depth, format); + if (!pPictFormat) + return FALSE; + + /* Compute the total vertical size needed for the format */ + + height = 0; + for (i = 0; i < UXA_NUM_GLYPH_CACHES; i++) { + uxa_glyph_cache_t *cache = &uxa_screen->glyphCaches[i]; + int rows; + + if (cache->format != format) + continue; + + cache->yOffset = height; + + rows = (cache->size + cache->columns - 1) / cache->columns; + height += rows * cache->glyphHeight; + } + + /* Now allocate the pixmap and picture */ + + pPixmap = (*pScreen->CreatePixmap) (pScreen, + CACHE_PICTURE_WIDTH, + height, depth, 0); + if (!pPixmap) + return FALSE; + + pPicture = CreatePicture(0, &pPixmap->drawable, pPictFormat, + 0, 0, serverClient, &error); + + (*pScreen->DestroyPixmap) (pPixmap); /* picture holds a refcount */ + + if (!pPicture) + return FALSE; + + /* And store the picture in all the caches for the format */ + + for (i = 0; i < UXA_NUM_GLYPH_CACHES; i++) { + uxa_glyph_cache_t *cache = &uxa_screen->glyphCaches[i]; + int j; + + if (cache->format != format) + continue; + + cache->picture = pPicture; + cache->picture->refcnt++; + cache->hashEntries = xalloc(sizeof(int) * cache->hashSize); + cache->glyphs = xalloc(sizeof(uxa_cached_glyph_t) * cache->size); + cache->glyphCount = 0; + + if (!cache->hashEntries || !cache->glyphs) + goto bail; + + for (j = 0; j < cache->hashSize; j++) + cache->hashEntries[j] = -1; + + cache->evictionPosition = rand() % cache->size; + } + + /* Each cache references the picture individually */ + FreePicture ((pointer) pPicture, (XID) 0); + return TRUE; + +bail: + uxa_unrealize_glyph_caches(pScreen, format); + return FALSE; +} + +void +uxa_glyphs_fini (ScreenPtr pScreen) +{ + uxa_screen_t *uxa_screen = uxa_get_screen(pScreen); + int i; + + for (i = 0; i < UXA_NUM_GLYPH_CACHES; i++) { + uxa_glyph_cache_t *cache = &uxa_screen->glyphCaches[i]; + + if (cache->picture) + uxa_unrealize_glyph_caches(pScreen, cache->format); + } +} + +static int +uxa_glyph_cache_hash_lookup(uxa_glyph_cache_t *cache, GlyphPtr pGlyph) +{ + int slot; + + slot = (*(CARD32 *) pGlyph->sha1) % cache->hashSize; + + while (TRUE) { /* hash table can never be full */ + int entryPos = cache->hashEntries[slot]; + if (entryPos == -1) + return -1; + + if (memcmp(pGlyph->sha1, cache->glyphs[entryPos].sha1, sizeof(pGlyph->sha1)) == 0){ + return entryPos; + } + + slot--; + if (slot < 0) + slot = cache->hashSize - 1; + } +} + +static void +uxa_glyph_cache_hash_insert(uxa_glyph_cache_t *cache, + GlyphPtr pGlyph, + int pos) +{ + int slot; + + memcpy(cache->glyphs[pos].sha1, pGlyph->sha1, sizeof(pGlyph->sha1)); + + slot = (*(CARD32 *) pGlyph->sha1) % cache->hashSize; + + while (TRUE) { /* hash table can never be full */ + if (cache->hashEntries[slot] == -1) { + cache->hashEntries[slot] = pos; + return; + } + + slot--; + if (slot < 0) + slot = cache->hashSize - 1; + } +} + +static void +uxa_glyph_cache_hash_remove(uxa_glyph_cache_t *cache, + int pos) +{ + int slot; + int emptiedSlot = -1; + + slot = (*(CARD32 *) cache->glyphs[pos].sha1) % cache->hashSize; + + while (TRUE) { /* hash table can never be full */ + int entryPos = cache->hashEntries[slot]; + + if (entryPos == -1) + return; + + if (entryPos == pos) { + cache->hashEntries[slot] = -1; + emptiedSlot = slot; + } else if (emptiedSlot != -1) { + /* See if we can move this entry into the emptied slot, we can't + * do that if if entry would have hashed between the current position + * and the emptied slot. (taking wrapping into account). Bad positions + * are: + * + * | XXXXXXXXXX | + * i j + * + * |XXX XXXX| + * j i + * + * i - slot, j - emptiedSlot + * + * (Knuth 6.4R) + */ + + int entrySlot = (*(CARD32 *) cache->glyphs[entryPos].sha1) % cache->hashSize; + + if (!((entrySlot >= slot && entrySlot < emptiedSlot) || + (emptiedSlot < slot && (entrySlot < emptiedSlot || entrySlot >= slot)))) + { + cache->hashEntries[emptiedSlot] = entryPos; + cache->hashEntries[slot] = -1; + emptiedSlot = slot; + } + } + + slot--; + if (slot < 0) + slot = cache->hashSize - 1; + } +} + +#define CACHE_X(pos) (((pos) % cache->columns) * cache->glyphWidth) +#define CACHE_Y(pos) (cache->yOffset + ((pos) / cache->columns) * cache->glyphHeight) + +/* The most efficient thing to way to upload the glyph to the screen + * is to use the UploadToScreen() driver hook; this allows us to + * pipeline glyph uploads and to avoid creating offscreen pixmaps for + * glyphs that we'll never use again. + */ +static Bool +uxa_glyph_cache_upload_glyph(ScreenPtr pScreen, + uxa_glyph_cache_t *cache, + int pos, + GlyphPtr pGlyph) +{ + uxa_screen_t *uxa_screen = uxa_get_screen(pScreen); + PicturePtr pGlyphPicture = GlyphPicture(pGlyph)[pScreen->myNum]; + PixmapPtr pGlyphPixmap = (PixmapPtr)pGlyphPicture->pDrawable; + PixmapPtr pCachePixmap = (PixmapPtr)cache->picture->pDrawable; + int cacheXoff, cacheYoff; + + if (!uxa_screen->info->UploadToScreen || uxa_screen->swappedOut) + return FALSE; + + /* If the glyph pixmap is already uploaded, no point in doing + * things this way */ + if (uxa_pixmap_is_offscreen(pGlyphPixmap)) + return FALSE; + + /* UploadToScreen only works if bpp match */ + if (pGlyphPixmap->drawable.bitsPerPixel != pCachePixmap->drawable.bitsPerPixel) + return FALSE; + + pCachePixmap = uxa_get_offscreen_pixmap ((DrawablePtr)pCachePixmap, &cacheXoff, &cacheYoff); + if (!pCachePixmap) + return FALSE; + + if (!uxa_screen->info->UploadToScreen(pCachePixmap, + CACHE_X(pos) + cacheXoff, + CACHE_Y(pos) + cacheYoff, + pGlyph->info.width, + pGlyph->info.height, + (char *)pGlyphPixmap->devPrivate.ptr, + pGlyphPixmap->devKind)) + return FALSE; + + return TRUE; +} + +static uxa_glyph_cache_result_t +uxa_glyph_cache_buffer_glyph(ScreenPtr pScreen, + uxa_glyph_cache_t *cache, + uxa_glyph_buffer_t *buffer, + GlyphPtr pGlyph, + int xGlyph, + int yGlyph) +{ + uxa_composite_rect_t *rect; + int pos; + + if (buffer->source && buffer->source != cache->picture) + return UXA_GLYPH_NEED_FLUSH; + + if (!cache->picture) { + if (!uxa_realize_glyph_caches(pScreen, cache->format)) + return UXA_GLYPH_FAIL; + } + + DBG_GLYPH_CACHE(("(%d,%d,%s): buffering glyph %lx\n", + cache->glyphWidth, cache->glyphHeight, cache->format == PICT_a8 ? "A" : "ARGB", + (long)*(CARD32 *) pGlyph->sha1)); + + pos = uxa_glyph_cache_hash_lookup(cache, pGlyph); + if (pos != -1) { + DBG_GLYPH_CACHE((" found existing glyph at %d\n", pos)); + } else { + if (cache->glyphCount < cache->size) { + /* Space remaining; we fill from the start */ + pos = cache->glyphCount; + cache->glyphCount++; + DBG_GLYPH_CACHE((" storing glyph in free space at %d\n", pos)); + + uxa_glyph_cache_hash_insert(cache, pGlyph, pos); + + } else { + /* Need to evict an entry. We have to see if any glyphs + * already in the output buffer were at this position in + * the cache + */ + + pos = cache->evictionPosition; + DBG_GLYPH_CACHE((" evicting glyph at %d\n", pos)); + if (buffer->count) { + int x, y; + int i; + + x = CACHE_X(pos); + y = CACHE_Y(pos); + + for (i = 0; i < buffer->count; i++) { + if (buffer->rects[i].xSrc == x && buffer->rects[i].ySrc == y) { + DBG_GLYPH_CACHE((" must flush buffer\n")); + return UXA_GLYPH_NEED_FLUSH; + } + } + } + + /* OK, we're all set, swap in the new glyph */ + uxa_glyph_cache_hash_remove(cache, pos); + uxa_glyph_cache_hash_insert(cache, pGlyph, pos); + + /* And pick a new eviction position */ + cache->evictionPosition = rand() % cache->size; + } + + /* Now actually upload the glyph into the cache picture; if + * we can't do it with UploadToScreen (because the glyph is + * offscreen, etc), we fall back to CompositePicture. + */ + if (!uxa_glyph_cache_upload_glyph(pScreen, cache, pos, pGlyph)) { + CompositePicture (PictOpSrc, + GlyphPicture(pGlyph)[pScreen->myNum], + None, + cache->picture, + 0, 0, + 0, 0, + CACHE_X(pos), + CACHE_Y(pos), + pGlyph->info.width, + pGlyph->info.height); + } + + } + + + buffer->source = cache->picture; + + rect = &buffer->rects[buffer->count]; + rect->xSrc = CACHE_X(pos); + rect->ySrc = CACHE_Y(pos); + rect->xDst = xGlyph - pGlyph->info.x; + rect->yDst = yGlyph - pGlyph->info.y; + rect->width = pGlyph->info.width; + rect->height = pGlyph->info.height; + + buffer->count++; + + return UXA_GLYPH_SUCCESS; +} + +#undef CACHE_X +#undef CACHE_Y + +static uxa_glyph_cache_result_t +uxa_buffer_glyph(ScreenPtr pScreen, + uxa_glyph_buffer_t *buffer, + GlyphPtr pGlyph, + int xGlyph, + int yGlyph) +{ + uxa_screen_t *uxa_screen = uxa_get_screen(pScreen); + unsigned int format = (GlyphPicture(pGlyph)[pScreen->myNum])->format; + int width = pGlyph->info.width; + int height = pGlyph->info.height; + uxa_composite_rect_t *rect; + PicturePtr source; + int i; + + if (buffer->count == GLYPH_BUFFER_SIZE) + return UXA_GLYPH_NEED_FLUSH; + + if (PICT_FORMAT_BPP(format) == 1) + format = PICT_a8; + + for (i = 0; i < UXA_NUM_GLYPH_CACHES; i++) { + uxa_glyph_cache_t *cache = &uxa_screen->glyphCaches[i]; + + if (format == cache->format && + width <= cache->glyphWidth && + height <= cache->glyphHeight) { + uxa_glyph_cache_result_t result = uxa_glyph_cache_buffer_glyph(pScreen, &uxa_screen->glyphCaches[i], + buffer, + pGlyph, xGlyph, yGlyph); + switch (result) { + case UXA_GLYPH_FAIL: + break; + case UXA_GLYPH_SUCCESS: + case UXA_GLYPH_NEED_FLUSH: + return result; + } + } + } + + /* Couldn't find the glyph in the cache, use the glyph picture directly */ + + source = GlyphPicture(pGlyph)[pScreen->myNum]; + if (buffer->source && buffer->source != source) + return UXA_GLYPH_NEED_FLUSH; + + buffer->source = source; + + rect = &buffer->rects[buffer->count]; + rect->xSrc = 0; + rect->ySrc = 0; + rect->xDst = xGlyph - pGlyph->info.x; + rect->yDst = yGlyph - pGlyph->info.y; + rect->width = pGlyph->info.width; + rect->height = pGlyph->info.height; + + buffer->count++; + + return UXA_GLYPH_SUCCESS; +} + +static void +uxa_glyphs_to_mask(PicturePtr pMask, + uxa_glyph_buffer_t *buffer) +{ + uxa_composite_rects(PictOpAdd, buffer->source, pMask, + buffer->count, buffer->rects); + + buffer->count = 0; + buffer->source = NULL; +} + +static void +uxa_glyphs_to_dst(CARD8 op, + PicturePtr pSrc, + PicturePtr pDst, + uxa_glyph_buffer_t *buffer, + INT16 xSrc, + INT16 ySrc, + INT16 xDst, + INT16 yDst) +{ + int i; + + for (i = 0; i < buffer->count; i++) { + uxa_composite_rect_t *rect = &buffer->rects[i]; + + CompositePicture (op, + pSrc, + buffer->source, + pDst, + xSrc + rect->xDst - xDst, + ySrc + rect->yDst - yDst, + rect->xSrc, + rect->ySrc, + rect->xDst, + rect->yDst, + rect->width, + rect->height); + } + + buffer->count = 0; + buffer->source = NULL; +} + +/* Cut and paste from render/glyph.c - probably should export it instead */ +static void +uxa_glyph_extents (int nlist, + GlyphListPtr list, + GlyphPtr *glyphs, + BoxPtr extents) +{ + int x1, x2, y1, y2; + int n; + GlyphPtr glyph; + int x, y; + + x = 0; + y = 0; + extents->x1 = MAXSHORT; + extents->x2 = MINSHORT; + extents->y1 = MAXSHORT; + extents->y2 = MINSHORT; + while (nlist--) + { + x += list->xOff; + y += list->yOff; + n = list->len; + list++; + while (n--) + { + glyph = *glyphs++; + x1 = x - glyph->info.x; + if (x1 < MINSHORT) + x1 = MINSHORT; + y1 = y - glyph->info.y; + if (y1 < MINSHORT) + y1 = MINSHORT; + x2 = x1 + glyph->info.width; + if (x2 > MAXSHORT) + x2 = MAXSHORT; + y2 = y1 + glyph->info.height; + if (y2 > MAXSHORT) + y2 = MAXSHORT; + if (x1 < extents->x1) + extents->x1 = x1; + if (x2 > extents->x2) + extents->x2 = x2; + if (y1 < extents->y1) + extents->y1 = y1; + if (y2 > extents->y2) + extents->y2 = y2; + x += glyph->info.xOff; + y += glyph->info.yOff; + } + } +} + +/** + * Returns TRUE if the glyphs in the lists intersect. Only checks based on + * bounding box, which appears to be good enough to catch most cases at least. + */ +static Bool +uxa_glyphs_intersect(int nlist, GlyphListPtr list, GlyphPtr *glyphs) +{ + int x1, x2, y1, y2; + int n; + GlyphPtr glyph; + int x, y; + BoxRec extents; + Bool first = TRUE; + + x = 0; + y = 0; + while (nlist--) { + x += list->xOff; + y += list->yOff; + n = list->len; + list++; + while (n--) { + glyph = *glyphs++; + + if (glyph->info.width == 0 || glyph->info.height == 0) { + x += glyph->info.xOff; + y += glyph->info.yOff; + continue; + } + + x1 = x - glyph->info.x; + if (x1 < MINSHORT) + x1 = MINSHORT; + y1 = y - glyph->info.y; + if (y1 < MINSHORT) + y1 = MINSHORT; + x2 = x1 + glyph->info.width; + if (x2 > MAXSHORT) + x2 = MAXSHORT; + y2 = y1 + glyph->info.height; + if (y2 > MAXSHORT) + y2 = MAXSHORT; + + if (first) { + extents.x1 = x1; + extents.y1 = y1; + extents.x2 = x2; + extents.y2 = y2; + first = FALSE; + } else { + if (x1 < extents.x2 && x2 > extents.x1 && + y1 < extents.y2 && y2 > extents.y1) + { + return TRUE; + } + + if (x1 < extents.x1) + extents.x1 = x1; + if (x2 > extents.x2) + extents.x2 = x2; + if (y1 < extents.y1) + extents.y1 = y1; + if (y2 > extents.y2) + extents.y2 = y2; + } + x += glyph->info.xOff; + y += glyph->info.yOff; + } + } + + return FALSE; +} + +#define NeedsComponent(f) (PICT_FORMAT_A(f) != 0 && PICT_FORMAT_RGB(f) != 0) + +void +uxa_glyphs (CARD8 op, + PicturePtr pSrc, + PicturePtr pDst, + PictFormatPtr maskFormat, + INT16 xSrc, + INT16 ySrc, + int nlist, + GlyphListPtr list, + GlyphPtr *glyphs) +{ + PicturePtr pPicture; + PixmapPtr pMaskPixmap = 0; + PicturePtr pMask; + ScreenPtr pScreen = pDst->pDrawable->pScreen; + int width = 0, height = 0; + int x, y; + int xDst = list->xOff, yDst = list->yOff; + int n; + GlyphPtr glyph; + int error; + BoxRec extents = {0, 0, 0, 0}; + CARD32 component_alpha; + uxa_glyph_buffer_t buffer; + + /* If we don't have a mask format but all the glyphs have the same format + * and don't intersect, use the glyph format as mask format for the full + * benefits of the glyph cache. + */ + if (!maskFormat) { + Bool sameFormat = TRUE; + int i; + + maskFormat = list[0].format; + + for (i = 0; i < nlist; i++) { + if (maskFormat->format != list[i].format->format) { + sameFormat = FALSE; + break; + } + } + + if (!sameFormat || (maskFormat->depth != 1 && + uxa_glyphs_intersect(nlist, list, glyphs))) { + maskFormat = NULL; + } + } + + if (maskFormat) + { + GCPtr pGC; + xRectangle rect; + + uxa_glyph_extents (nlist, list, glyphs, &extents); + + if (extents.x2 <= extents.x1 || extents.y2 <= extents.y1) + return; + width = extents.x2 - extents.x1; + height = extents.y2 - extents.y1; + + if (maskFormat->depth == 1) { + PictFormatPtr a8Format = PictureMatchFormat (pScreen, 8, PICT_a8); + + if (a8Format) + maskFormat = a8Format; + } + + pMaskPixmap = (*pScreen->CreatePixmap) (pScreen, width, height, + maskFormat->depth, + CREATE_PIXMAP_USAGE_SCRATCH); + if (!pMaskPixmap) + return; + component_alpha = NeedsComponent(maskFormat->format); + pMask = CreatePicture (0, &pMaskPixmap->drawable, + maskFormat, CPComponentAlpha, &component_alpha, + serverClient, &error); + if (!pMask) + { + (*pScreen->DestroyPixmap) (pMaskPixmap); + return; + } + pGC = GetScratchGC (pMaskPixmap->drawable.depth, pScreen); + ValidateGC (&pMaskPixmap->drawable, pGC); + rect.x = 0; + rect.y = 0; + rect.width = width; + rect.height = height; + (*pGC->ops->PolyFillRect) (&pMaskPixmap->drawable, pGC, 1, &rect); + FreeScratchGC (pGC); + x = -extents.x1; + y = -extents.y1; + } + else + { + pMask = pDst; + x = 0; + y = 0; + } + buffer.count = 0; + buffer.source = NULL; + while (nlist--) + { + x += list->xOff; + y += list->yOff; + n = list->len; + while (n--) + { + glyph = *glyphs++; + pPicture = GlyphPicture (glyph)[pScreen->myNum]; + + if (glyph->info.width > 0 && glyph->info.height > 0 && + uxa_buffer_glyph(pScreen, &buffer, glyph, x, y) == UXA_GLYPH_NEED_FLUSH) + { + if (maskFormat) + uxa_glyphs_to_mask(pMask, &buffer); + else + uxa_glyphs_to_dst(op, pSrc, pDst, &buffer, + xSrc, ySrc, xDst, yDst); + + uxa_buffer_glyph(pScreen, &buffer, glyph, x, y); + } + + x += glyph->info.xOff; + y += glyph->info.yOff; + } + list++; + } + + if (maskFormat) + uxa_glyphs_to_mask(pMask, &buffer); + else + uxa_glyphs_to_dst(op, pSrc, pDst, &buffer, + xSrc, ySrc, xDst, yDst); + + if (maskFormat) + { + x = extents.x1; + y = extents.y1; + CompositePicture (op, + pSrc, + pMask, + pDst, + xSrc + x - xDst, + ySrc + y - yDst, + 0, 0, + x, y, + width, height); + FreePicture ((pointer) pMask, (XID) 0); + (*pScreen->DestroyPixmap) (pMaskPixmap); + } +} diff --git a/uxa/uxa-priv.h b/uxa/uxa-priv.h new file mode 100644 index 00000000..acc82bba --- /dev/null +++ b/uxa/uxa-priv.h @@ -0,0 +1,444 @@ +/* + * + * Copyright © 2000,2008 Keith Packard + * 2005 Zack Rusin, Trolltech + * + * 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. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS 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. + */ + +#ifndef UXAPRIV_H +#define UXAPRIV_H + +#ifdef HAVE_DIX_CONFIG_H +#include <dix-config.h> +#else +#include <xorg-server.h> +#endif + +#include "uxa.h" + +#include <X11/X.h> +#define NEED_EVENTS +#include <X11/Xproto.h> +#ifdef MITSHM +#define _XSHM_SERVER_ +#include <X11/extensions/shmstr.h> +#endif +#include "scrnintstr.h" +#include "pixmapstr.h" +#include "windowstr.h" +#include "servermd.h" +#include "mibstore.h" +#include "colormapst.h" +#include "gcstruct.h" +#include "input.h" +#include "mipointer.h" +#include "mi.h" +#include "dix.h" +#include "fb.h" +#include "fboverlay.h" +#ifdef RENDER +//#include "fbpict.h" +#include "glyphstr.h" +#endif +#include "damage.h" + +#define DEBUG_TRACE_FALL 0 +#define DEBUG_MIGRATE 0 +#define DEBUG_PIXMAP 0 +#define DEBUG_OFFSCREEN 0 +#define DEBUG_GLYPH_CACHE 0 + +#if DEBUG_TRACE_FALL +#define UXA_FALLBACK(x) \ +do { \ + ErrorF("UXA fallback at %s: ", __FUNCTION__); \ + ErrorF x; \ +} while (0) + +char +uxa_drawable_location(DrawablePtr pDrawable); +#else +#define UXA_FALLBACK(x) +#endif + +#if DEBUG_PIXMAP +#define DBG_PIXMAP(a) ErrorF a +#else +#define DBG_PIXMAP(a) +#endif + +typedef struct { + unsigned char sha1[20]; +} uxa_cached_glyph_t; + +typedef struct { + /* The identity of the cache, statically configured at initialization */ + unsigned int format; + int glyphWidth; + int glyphHeight; + + int size; /* Size of cache; eventually this should be dynamically determined */ + + /* Hash table mapping from glyph sha1 to position in the glyph; we use + * open addressing with a hash table size determined based on size and large + * enough so that we always have a good amount of free space, so we can + * use linear probing. (Linear probing is preferrable to double hashing + * here because it allows us to easily remove entries.) + */ + int *hashEntries; + int hashSize; + + uxa_cached_glyph_t *glyphs; + int glyphCount; /* Current number of glyphs */ + + PicturePtr picture; /* Where the glyphs of the cache are stored */ + int yOffset; /* y location within the picture where the cache starts */ + int columns; /* Number of columns the glyphs are layed out in */ + int evictionPosition; /* Next random position to evict a glyph */ +} uxa_glyph_cache_t; + +#define UXA_NUM_GLYPH_CACHES 4 + +typedef void (*EnableDisableFBAccessProcPtr)(int, Bool); +typedef struct { + uxa_driver_t *info; + CreateGCProcPtr SavedCreateGC; + CloseScreenProcPtr SavedCloseScreen; + GetImageProcPtr SavedGetImage; + GetSpansProcPtr SavedGetSpans; + CreatePixmapProcPtr SavedCreatePixmap; + DestroyPixmapProcPtr SavedDestroyPixmap; + CopyWindowProcPtr SavedCopyWindow; + ChangeWindowAttributesProcPtr SavedChangeWindowAttributes; + BitmapToRegionProcPtr SavedBitmapToRegion; +#ifdef RENDER + CompositeProcPtr SavedComposite; + TrianglesProcPtr SavedTriangles; + GlyphsProcPtr SavedGlyphs; + TrapezoidsProcPtr SavedTrapezoids; + AddTrapsProcPtr SavedAddTraps; +#endif + + Bool swappedOut; + unsigned disableFbCount; + unsigned offScreenCounter; + + uxa_glyph_cache_t glyphCaches[UXA_NUM_GLYPH_CACHES]; +} uxa_screen_t; + +/* + * This is the only completely portable way to + * compute this info. + */ +#ifndef BitsPerPixel +#define BitsPerPixel(d) (\ + PixmapWidthPaddingInfo[d].notPower2 ? \ + (PixmapWidthPaddingInfo[d].bytesPerPixel * 8) : \ + ((1 << PixmapWidthPaddingInfo[d].padBytesLog2) * 8 / \ + (PixmapWidthPaddingInfo[d].padRoundUp+1))) +#endif + +extern DevPrivateKey uxa_screen_key; +#define uxa_get_screen(s) ((uxa_screen_t *)dixLookupPrivate(&(s)->devPrivates, uxa_screen_key)) + +/** Align an offset to an arbitrary alignment */ +#define UXA_ALIGN(offset, align) (((offset) + (align) - 1) - \ + (((offset) + (align) - 1) % (align))) +/** Align an offset to a power-of-two alignment */ +#define UXA_ALIGN2(offset, align) (((offset) + (align) - 1) & ~((align) - 1)) + +typedef struct { + INT16 xSrc; + INT16 ySrc; + INT16 xDst; + INT16 yDst; + INT16 width; + INT16 height; +} uxa_composite_rect_t; + +/** + * exaDDXDriverInit must be implemented by the DDX using EXA, and is the place + * to set EXA options or hook in screen functions to handle using EXA as the AA. + */ +void exaDDXDriverInit (ScreenPtr pScreen); + +void +uxa_prepare_access_window(WindowPtr pWin); + +void +uxa_finish_access_window(WindowPtr pWin); + +/* uxa-unaccel.c */ +void +uxa_prepare_access_gc(GCPtr pGC); + +void +uxa_finish_access_gc(GCPtr pGC); + +void +uxa_check_fill_spans (DrawablePtr pDrawable, GCPtr pGC, int nspans, + DDXPointPtr ppt, int *pwidth, int fSorted); + +void +uxa_check_set_spans (DrawablePtr pDrawable, GCPtr pGC, char *psrc, + DDXPointPtr ppt, int *pwidth, int nspans, int fSorted); + +void +uxa_check_put_image (DrawablePtr pDrawable, GCPtr pGC, int depth, + int x, int y, int w, int h, int leftPad, int format, + char *bits); + +RegionPtr +uxa_check_copy_area (DrawablePtr pSrc, DrawablePtr pDst, GCPtr pGC, + int srcx, int srcy, int w, int h, int dstx, int dsty); + +RegionPtr +uxa_check_copy_plane (DrawablePtr pSrc, DrawablePtr pDst, GCPtr pGC, + int srcx, int srcy, int w, int h, int dstx, int dsty, + unsigned long bitPlane); + +void +uxa_check_poly_point (DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, + DDXPointPtr pptInit); + +void +uxa_check_poly_lines (DrawablePtr pDrawable, GCPtr pGC, + int mode, int npt, DDXPointPtr ppt); + +void +uxa_check_poly_segment (DrawablePtr pDrawable, GCPtr pGC, + int nsegInit, xSegment *pSegInit); + +void +uxa_check_poly_arc (DrawablePtr pDrawable, GCPtr pGC, + int narcs, xArc *pArcs); + +void +uxa_check_poly_fill_rect (DrawablePtr pDrawable, GCPtr pGC, + int nrect, xRectangle *prect); + +void +uxa_check_image_glyph_blt (DrawablePtr pDrawable, GCPtr pGC, + int x, int y, unsigned int nglyph, + CharInfoPtr *ppci, pointer pglyphBase); + +void +uxa_check_poly_glyph_blt (DrawablePtr pDrawable, GCPtr pGC, + int x, int y, unsigned int nglyph, + CharInfoPtr *ppci, pointer pglyphBase); + +void +uxa_check_push_pixels (GCPtr pGC, PixmapPtr pBitmap, + DrawablePtr pDrawable, + int w, int h, int x, int y); + +void +uxa_check_get_spans (DrawablePtr pDrawable, + int wMax, + DDXPointPtr ppt, + int *pwidth, + int nspans, + char *pdstStart); + +void +uxa_check_add_traps (PicturePtr pPicture, + INT16 x_off, + INT16 y_off, + int ntrap, + xTrap *traps); + +/* uxa-accel.c */ + +static _X_INLINE Bool +uxa_gc_reads_destination(DrawablePtr pDrawable, unsigned long planemask, + unsigned int fillStyle, unsigned char alu) +{ + return ((alu != GXcopy && alu != GXclear &&alu != GXset && + alu != GXcopyInverted) || fillStyle == FillStippled || + !UXA_PM_IS_SOLID(pDrawable, planemask)); +} + +void +uxa_copy_window(WindowPtr pWin, DDXPointRec ptOldOrg, RegionPtr prgnSrc); + +Bool +uxa_fill_region_tiled (DrawablePtr pDrawable, RegionPtr pRegion, PixmapPtr pTile, + DDXPointPtr pPatOrg, CARD32 planemask, CARD32 alu); + +void +uxa_shm_put_image(DrawablePtr pDrawable, GCPtr pGC, int depth, unsigned int format, + int w, int h, int sx, int sy, int sw, int sh, int dx, int dy, + char *data); + +void +uxa_get_image (DrawablePtr pDrawable, int x, int y, int w, int h, + unsigned int format, unsigned long planeMask, char *d); + +extern const GCOps uxa_ops; + +#ifdef MITSHM +extern ShmFuncs uxa_shm_funcs; + +/* XXX these come from shmint.h, which isn't exported by the server */ +void +ShmRegisterFuncs(ScreenPtr pScreen, ShmFuncsPtr funcs); + +void +ShmSetPixmapFormat(ScreenPtr pScreen, int format); + +void +fbShmPutImage(XSHM_PUT_IMAGE_ARGS); + +#endif + +#ifdef RENDER + +/* XXX these are in fbpict.h, which is not installed */ +void +fbComposite (CARD8 op, + PicturePtr pSrc, + PicturePtr pMask, + PicturePtr pDst, + INT16 xSrc, + INT16 ySrc, + INT16 xMask, + INT16 yMask, + INT16 xDst, + INT16 yDst, + CARD16 width, + CARD16 height); + +void +fbAddTraps (PicturePtr pPicture, + INT16 xOff, + INT16 yOff, + int ntrap, + xTrap *traps); + +void +uxa_check_composite (CARD8 op, + PicturePtr pSrc, + PicturePtr pMask, + PicturePtr pDst, + INT16 xSrc, + INT16 ySrc, + INT16 xMask, + INT16 yMask, + INT16 xDst, + INT16 yDst, + CARD16 width, + CARD16 height); +#endif + +/* uxa.c */ +void +uxa_prepare_access(DrawablePtr pDrawable, int index); + +void +uxa_finish_access(DrawablePtr pDrawable, int index); + +void +uxa_get_drawable_deltas (DrawablePtr pDrawable, PixmapPtr pPixmap, + int *xp, int *yp); + +Bool +uxa_drawable_is_offscreen (DrawablePtr pDrawable); + +Bool +uxa_pixmap_is_offscreen(PixmapPtr p); + +PixmapPtr +uxa_get_offscreen_pixmap (DrawablePtr pDrawable, int *xp, int *yp); + +PixmapPtr +uxa_get_drawable_pixmap(DrawablePtr pDrawable); + +RegionPtr +uxa_copy_area(DrawablePtr pSrcDrawable, DrawablePtr pDstDrawable, GCPtr pGC, + int srcx, int srcy, int width, int height, int dstx, int dsty); + +void +uxa_copy_n_to_n (DrawablePtr pSrcDrawable, + DrawablePtr pDstDrawable, + GCPtr pGC, + BoxPtr pbox, + int nbox, + int dx, + int dy, + Bool reverse, + Bool upsidedown, + Pixel bitplane, + void *closure); + +/* uxa_render.c */ +Bool +uxa_op_reads_destination (CARD8 op); + +void +uxa_composite(CARD8 op, + PicturePtr pSrc, + PicturePtr pMask, + PicturePtr pDst, + INT16 xSrc, + INT16 ySrc, + INT16 xMask, + INT16 yMask, + INT16 xDst, + INT16 yDst, + CARD16 width, + CARD16 height); + +void +uxa_composite_rects(CARD8 op, + PicturePtr pSrc, + PicturePtr pDst, + int nrect, + uxa_composite_rect_t *rects); + +void +uxa_trapezoids (CARD8 op, PicturePtr pSrc, PicturePtr pDst, + PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc, + int ntrap, xTrapezoid *traps); + +void +uxa_triangles (CARD8 op, PicturePtr pSrc, PicturePtr pDst, + PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc, + int ntri, xTriangle *tris); + +/* uxa_glyph.c */ +void +uxa_glyphs_init(ScreenPtr pScreen); + +void +uxa_glyphs_fini (ScreenPtr pScreen); + +void +uxa_glyphs (CARD8 op, + PicturePtr pSrc, + PicturePtr pDst, + PictFormatPtr maskFormat, + INT16 xSrc, + INT16 ySrc, + int nlist, + GlyphListPtr list, + GlyphPtr *glyphs); + +#endif /* UXAPRIV_H */ diff --git a/uxa/uxa-render.c b/uxa/uxa-render.c new file mode 100644 index 00000000..38e6088f --- /dev/null +++ b/uxa/uxa-render.c @@ -0,0 +1,1052 @@ +/* + * 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. + */ + +#ifdef HAVE_DIX_CONFIG_H +#include <dix-config.h> +#endif + +#include <stdlib.h> + +#include "uxa-priv.h" + +#ifdef RENDER +#include "mipict.h" + +#if DEBUG_TRACE_FALL +static void uxa_composite_fallback_pict_desc(PicturePtr pict, char *string, int n) +{ + char format[20]; + char size[20]; + char loc; + int temp; + + if (!pict) { + snprintf(string, n, "None"); + return; + } + + switch (pict->format) + { + case PICT_a8r8g8b8: + snprintf(format, 20, "ARGB8888"); + break; + case PICT_x8r8g8b8: + snprintf(format, 20, "XRGB8888"); + break; + case PICT_r5g6b5: + snprintf(format, 20, "RGB565 "); + break; + case PICT_x1r5g5b5: + snprintf(format, 20, "RGB555 "); + break; + case PICT_a8: + snprintf(format, 20, "A8 "); + break; + case PICT_a1: + snprintf(format, 20, "A1 "); + break; + default: + snprintf(format, 20, "0x%x", (int)pict->format); + break; + } + + loc = uxa_get_drawable_pixmap(pict->pDrawable, &temp, &temp) ? 's' : 'm'; + + snprintf(size, 20, "%dx%d%s", pict->pDrawable->width, + pict->pDrawable->height, pict->repeat ? + " R" : ""); + + snprintf(string, n, "%p:%c fmt %s (%s)", pict->pDrawable, loc, format, size); +} + +static void +uxa_print_composite_fallback(CARD8 op, + PicturePtr pSrc, + PicturePtr pMask, + PicturePtr pDst) +{ + char sop[20]; + char srcdesc[40], maskdesc[40], dstdesc[40]; + + switch(op) + { + case PictOpSrc: + sprintf(sop, "Src"); + break; + case PictOpOver: + sprintf(sop, "Over"); + break; + default: + sprintf(sop, "0x%x", (int)op); + break; + } + + uxa_composite_fallback_pict_desc(pSrc, srcdesc, 40); + uxa_composite_fallback_pict_desc(pMask, maskdesc, 40); + uxa_composite_fallback_pict_desc(pDst, dstdesc, 40); + + ErrorF("Composite fallback: op %s, \n" + " src %s, \n" + " mask %s, \n" + " dst %s, \n", + sop, srcdesc, maskdesc, dstdesc); +} +#endif /* DEBUG_TRACE_FALL */ + +Bool +uxa_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 Bool +uxa_get_pixel_from_rgba(CARD32 *pixel, + CARD16 red, + CARD16 green, + CARD16 blue, + CARD16 alpha, + CARD32 format) +{ + int rbits, bbits, gbits, abits; + int rshift, bshift, gshift, ashift; + + *pixel = 0; + + if (!PICT_FORMAT_COLOR(format)) + return FALSE; + + rbits = PICT_FORMAT_R(format); + gbits = PICT_FORMAT_G(format); + bbits = PICT_FORMAT_B(format); + abits = PICT_FORMAT_A(format); + + if (PICT_FORMAT_TYPE(format) == PICT_TYPE_ARGB) { + bshift = 0; + gshift = bbits; + rshift = gshift + gbits; + ashift = rshift + rbits; + } else { /* PICT_TYPE_ABGR */ + rshift = 0; + gshift = rbits; + bshift = gshift + gbits; + ashift = bshift + bbits; + } + + *pixel |= ( blue >> (16 - bbits)) << bshift; + *pixel |= ( red >> (16 - rbits)) << rshift; + *pixel |= (green >> (16 - gbits)) << gshift; + *pixel |= (alpha >> (16 - abits)) << ashift; + + return TRUE; +} + +static Bool +uxa_get_rgba_from_pixel(CARD32 pixel, + CARD16 *red, + CARD16 *green, + CARD16 *blue, + CARD16 *alpha, + CARD32 format) +{ + int rbits, bbits, gbits, abits; + int rshift, bshift, gshift, ashift; + + if (!PICT_FORMAT_COLOR(format)) + return FALSE; + + rbits = PICT_FORMAT_R(format); + gbits = PICT_FORMAT_G(format); + bbits = PICT_FORMAT_B(format); + abits = PICT_FORMAT_A(format); + + if (PICT_FORMAT_TYPE(format) == PICT_TYPE_ARGB) { + bshift = 0; + gshift = bbits; + rshift = gshift + gbits; + ashift = rshift + rbits; + } else { /* PICT_TYPE_ABGR */ + rshift = 0; + gshift = rbits; + bshift = gshift + gbits; + ashift = bshift + bbits; + } + + *red = ((pixel >> rshift ) & ((1 << rbits) - 1)) << (16 - rbits); + while (rbits < 16) { + *red |= *red >> rbits; + rbits <<= 1; + } + + *green = ((pixel >> gshift ) & ((1 << gbits) - 1)) << (16 - gbits); + while (gbits < 16) { + *green |= *green >> gbits; + gbits <<= 1; + } + + *blue = ((pixel >> bshift ) & ((1 << bbits) - 1)) << (16 - bbits); + while (bbits < 16) { + *blue |= *blue >> bbits; + bbits <<= 1; + } + + if (abits) { + *alpha = ((pixel >> ashift ) & ((1 << abits) - 1)) << (16 - abits); + while (abits < 16) { + *alpha |= *alpha >> abits; + abits <<= 1; + } + } else + *alpha = 0xffff; + + return TRUE; +} + +static int +uxa_try_driver_solid_fill(PicturePtr pSrc, + PicturePtr pDst, + INT16 xSrc, + INT16 ySrc, + INT16 xDst, + INT16 yDst, + CARD16 width, + CARD16 height) +{ + uxa_screen_t *uxa_screen = uxa_get_screen(pDst->pDrawable->pScreen); + RegionRec region; + BoxPtr pbox; + int nbox; + int dst_off_x, dst_off_y; + PixmapPtr pSrcPix, pDstPix; + CARD32 pixel; + CARD16 red, green, blue, alpha; + + pDstPix = uxa_get_drawable_pixmap (pDst->pDrawable); + pSrcPix = uxa_get_drawable_pixmap (pSrc->pDrawable); + + xDst += pDst->pDrawable->x; + yDst += pDst->pDrawable->y; + xSrc += pSrc->pDrawable->x; + ySrc += pSrc->pDrawable->y; + + if (!miComputeCompositeRegion (®ion, pSrc, NULL, pDst, + xSrc, ySrc, 0, 0, xDst, yDst, + width, height)) + return 1; + + uxa_get_drawable_deltas (pDst->pDrawable, pDstPix, &dst_off_x, &dst_off_y); + + REGION_TRANSLATE(pScreen, ®ion, dst_off_x, dst_off_y); + + pixel = uxa_get_pixmap_first_pixel (pSrcPix); + + if (!uxa_pixmap_is_offscreen(pDstPix)) { + REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); + return 0; + } + + if (!uxa_get_rgba_from_pixel(pixel, &red, &green, &blue, &alpha, + pSrc->format)) + { + REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); + return -1; + } + + if (!uxa_get_pixel_from_rgba(&pixel, red, green, blue, alpha, + pDst->format)) + { + REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); + return -1; + } + + if (!(*uxa_screen->info->PrepareSolid) (pDstPix, GXcopy, 0xffffffff, pixel)) + { + REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); + return -1; + } + + nbox = REGION_NUM_RECTS(®ion); + pbox = REGION_RECTS(®ion); + + while (nbox--) + { + (*uxa_screen->info->Solid) (pDstPix, pbox->x1, pbox->y1, pbox->x2, pbox->y2); + pbox++; + } + + (*uxa_screen->info->DoneSolid) (pDstPix); + uxa_mark_sync(pDst->pDrawable->pScreen); + + REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); + return 1; +} + +static int +uxa_try_driver_composite_rects(CARD8 op, + PicturePtr pSrc, + PicturePtr pDst, + int nrect, + uxa_composite_rect_t *rects) +{ + uxa_screen_t *uxa_screen = uxa_get_screen(pDst->pDrawable->pScreen); + int src_off_x, src_off_y, dst_off_x, dst_off_y; + PixmapPtr pSrcPix, pDstPix; + struct _Pixmap scratch; + + if (!uxa_screen->info->PrepareComposite) + return -1; + + pSrcPix = uxa_get_drawable_pixmap(pSrc->pDrawable); + + pDstPix = uxa_get_drawable_pixmap(pDst->pDrawable); + + if (uxa_screen->info->CheckComposite && + !(*uxa_screen->info->CheckComposite) (op, pSrc, NULL, pDst)) + { + return -1; + } + + uxa_get_drawable_deltas (pDst->pDrawable, pDstPix, &dst_off_x, &dst_off_y); + + pSrcPix = uxa_get_offscreen_pixmap (pSrc->pDrawable, &src_off_x, &src_off_y); + if (!uxa_pixmap_is_offscreen(pDstPix)) + return 0; + + if (!pSrcPix && uxa_screen->info->UploadToScratch) + { + pSrcPix = uxa_get_drawable_pixmap (pSrc->pDrawable); + if ((*uxa_screen->info->UploadToScratch) (pSrcPix, &scratch)) + pSrcPix = &scratch; + } + + if (!pSrcPix) + return 0; + + if (!(*uxa_screen->info->PrepareComposite) (op, pSrc, NULL, pDst, pSrcPix, + NULL, pDstPix)) + return -1; + + while (nrect--) + { + INT16 xDst = rects->xDst + pDst->pDrawable->x; + INT16 yDst = rects->yDst + pDst->pDrawable->y; + INT16 xSrc = rects->xSrc + pSrc->pDrawable->x; + INT16 ySrc = rects->ySrc + pSrc->pDrawable->y; + + RegionRec region; + BoxPtr pbox; + int nbox; + + if (!miComputeCompositeRegion (®ion, pSrc, NULL, pDst, + xSrc, ySrc, 0, 0, xDst, yDst, + rects->width, rects->height)) + goto next_rect; + + REGION_TRANSLATE(pScreen, ®ion, dst_off_x, dst_off_y); + + nbox = REGION_NUM_RECTS(®ion); + pbox = REGION_RECTS(®ion); + + xSrc = xSrc + src_off_x - xDst - dst_off_x; + ySrc = ySrc + src_off_y - yDst - dst_off_y; + + while (nbox--) + { + (*uxa_screen->info->Composite) (pDstPix, + pbox->x1 + xSrc, + pbox->y1 + ySrc, + 0, 0, + pbox->x1, + pbox->y1, + pbox->x2 - pbox->x1, + pbox->y2 - pbox->y1); + pbox++; + } + + next_rect: + REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); + + rects++; + } + + (*uxa_screen->info->DoneComposite) (pDstPix); + uxa_mark_sync(pDst->pDrawable->pScreen); + + return 1; +} + +/** + * Copy a number of rectangles from source to destination in a single + * operation. This is specialized for building a glyph mask: we don'y + * have a mask argument because we don't need it for that, and we + * don't have he special-case fallbacks found in uxa_composite() - if the + * driver can support it, we use the driver functionality, otherwise we + * fallback straight to software. + */ +void +uxa_composite_rects(CARD8 op, + PicturePtr pSrc, + PicturePtr pDst, + int nrect, + uxa_composite_rect_t *rects) +{ + int n; + uxa_composite_rect_t *r; + + /************************************************************/ + + ValidatePicture (pSrc); + ValidatePicture (pDst); + + if (uxa_try_driver_composite_rects(op, pSrc, pDst, nrect, rects) != 1) { + n = nrect; + r = rects; + while (n--) { + uxa_check_composite (op, pSrc, NULL, pDst, + r->xSrc, r->ySrc, + 0, 0, + r->xDst, r->yDst, + r->width, r->height); + r++; + } + } + + /************************************************************/ + +} + +static int +uxa_try_driver_composite(CARD8 op, + PicturePtr pSrc, + PicturePtr pMask, + PicturePtr pDst, + INT16 xSrc, + INT16 ySrc, + INT16 xMask, + INT16 yMask, + INT16 xDst, + INT16 yDst, + CARD16 width, + CARD16 height) +{ + uxa_screen_t *uxa_screen = uxa_get_screen(pDst->pDrawable->pScreen); + RegionRec region; + BoxPtr pbox; + int nbox; + int src_off_x, src_off_y, mask_off_x, mask_off_y, dst_off_x, dst_off_y; + PixmapPtr pSrcPix, pMaskPix = NULL, pDstPix; + struct _Pixmap scratch; + + pSrcPix = uxa_get_drawable_pixmap(pSrc->pDrawable); + pDstPix = uxa_get_drawable_pixmap(pDst->pDrawable); + if (pMask) + pMaskPix = uxa_get_drawable_pixmap(pMask->pDrawable); + + xDst += pDst->pDrawable->x; + yDst += pDst->pDrawable->y; + + if (pMask) { + xMask += pMask->pDrawable->x; + yMask += pMask->pDrawable->y; + } + + xSrc += pSrc->pDrawable->x; + ySrc += pSrc->pDrawable->y; + + if (uxa_screen->info->CheckComposite && + !(*uxa_screen->info->CheckComposite) (op, pSrc, pMask, pDst)) + { + return -1; + } + + if (!miComputeCompositeRegion (®ion, pSrc, pMask, pDst, + xSrc, ySrc, xMask, yMask, xDst, yDst, + width, height)) + return 1; + + uxa_get_drawable_deltas (pDst->pDrawable, pDstPix, &dst_off_x, &dst_off_y); + + REGION_TRANSLATE(pScreen, ®ion, dst_off_x, dst_off_y); + + pSrcPix = uxa_get_offscreen_pixmap (pSrc->pDrawable, &src_off_x, &src_off_y); + if (pMask) + pMaskPix = uxa_get_offscreen_pixmap (pMask->pDrawable, &mask_off_x, + &mask_off_y); + + if (!uxa_pixmap_is_offscreen(pDstPix)) { + REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); + return 0; + } + + if (!pSrcPix && (!pMask || pMaskPix) && uxa_screen->info->UploadToScratch) { + pSrcPix = uxa_get_drawable_pixmap (pSrc->pDrawable); + if ((*uxa_screen->info->UploadToScratch) (pSrcPix, &scratch)) + pSrcPix = &scratch; + } else if (pSrcPix && pMask && !pMaskPix && uxa_screen->info->UploadToScratch) { + pMaskPix = uxa_get_drawable_pixmap (pMask->pDrawable); + if ((*uxa_screen->info->UploadToScratch) (pMaskPix, &scratch)) + pMaskPix = &scratch; + } + + if (!pSrcPix || (pMask && !pMaskPix)) { + REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); + return 0; + } + + if (!(*uxa_screen->info->PrepareComposite) (op, pSrc, pMask, pDst, pSrcPix, + pMaskPix, pDstPix)) + { + REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); + return -1; + } + + nbox = REGION_NUM_RECTS(®ion); + pbox = REGION_RECTS(®ion); + + xMask = xMask + mask_off_x - xDst - dst_off_x; + yMask = yMask + mask_off_y - yDst - dst_off_y; + + xSrc = xSrc + src_off_x - xDst - dst_off_x; + ySrc = ySrc + src_off_y - yDst - dst_off_y; + + while (nbox--) + { + (*uxa_screen->info->Composite) (pDstPix, + pbox->x1 + xSrc, + pbox->y1 + ySrc, + pbox->x1 + xMask, + pbox->y1 + yMask, + pbox->x1, + pbox->y1, + pbox->x2 - pbox->x1, + pbox->y2 - pbox->y1); + pbox++; + } + (*uxa_screen->info->DoneComposite) (pDstPix); + uxa_mark_sync(pDst->pDrawable->pScreen); + + REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); + return 1; +} + +/** + * uxa_try_magic_two_pass_composite_helper implements PictOpOver using two passes of + * simpler operations PictOpOutReverse and PictOpAdd. Mainly used for component + * alpha and limited 1-tmu cards. + * + * From http://anholt.livejournal.com/32058.html: + * + * The trouble is that component-alpha rendering requires two different sources + * for blending: one for the source value to the blender, which is the + * per-channel multiplication of source and mask, and one for the source alpha + * for multiplying with the destination channels, which is the multiplication + * of the source channels by the mask alpha. So the equation for Over is: + * + * dst.A = src.A * mask.A + (1 - (src.A * mask.A)) * dst.A + * dst.R = src.R * mask.R + (1 - (src.A * mask.R)) * dst.R + * dst.G = src.G * mask.G + (1 - (src.A * mask.G)) * dst.G + * dst.B = src.B * mask.B + (1 - (src.A * mask.B)) * dst.B + * + * But we can do some simpler operations, right? How about PictOpOutReverse, + * which has a source factor of 0 and dest factor of (1 - source alpha). We + * can get the source alpha value (srca.X = src.A * mask.X) out of the texture + * blenders pretty easily. So we can do a component-alpha OutReverse, which + * gets us: + * + * dst.A = 0 + (1 - (src.A * mask.A)) * dst.A + * dst.R = 0 + (1 - (src.A * mask.R)) * dst.R + * dst.G = 0 + (1 - (src.A * mask.G)) * dst.G + * dst.B = 0 + (1 - (src.A * mask.B)) * dst.B + * + * OK. And if an op doesn't use the source alpha value for the destination + * factor, then we can do the channel multiplication in the texture blenders + * to get the source value, and ignore the source alpha that we wouldn't use. + * We've supported this in the Radeon driver for a long time. An example would + * be PictOpAdd, which does: + * + * dst.A = src.A * mask.A + dst.A + * dst.R = src.R * mask.R + dst.R + * dst.G = src.G * mask.G + dst.G + * dst.B = src.B * mask.B + dst.B + * + * Hey, this looks good! If we do a PictOpOutReverse and then a PictOpAdd right + * after it, we get: + * + * dst.A = src.A * mask.A + ((1 - (src.A * mask.A)) * dst.A) + * dst.R = src.R * mask.R + ((1 - (src.A * mask.R)) * dst.R) + * dst.G = src.G * mask.G + ((1 - (src.A * mask.G)) * dst.G) + * dst.B = src.B * mask.B + ((1 - (src.A * mask.B)) * dst.B) + */ + +static int +uxa_try_magic_two_pass_composite_helper(CARD8 op, + PicturePtr pSrc, + PicturePtr pMask, + PicturePtr pDst, + INT16 xSrc, + INT16 ySrc, + INT16 xMask, + INT16 yMask, + INT16 xDst, + INT16 yDst, + CARD16 width, + CARD16 height) +{ + uxa_screen_t *uxa_screen = uxa_get_screen(pDst->pDrawable->pScreen); + + assert(op == PictOpOver); + + if (uxa_screen->info->CheckComposite && + (!(*uxa_screen->info->CheckComposite)(PictOpOutReverse, pSrc, pMask, + pDst) || + !(*uxa_screen->info->CheckComposite)(PictOpAdd, pSrc, pMask, pDst))) + { + return -1; + } + + /* Now, we think we should be able to accelerate this operation. First, + * composite the destination to be the destination times the source alpha + * factors. + */ + uxa_composite(PictOpOutReverse, pSrc, pMask, pDst, xSrc, ySrc, xMask, yMask, + xDst, yDst, width, height); + + /* Then, add in the source value times the destination alpha factors (1.0). + */ + uxa_composite(PictOpAdd, pSrc, pMask, pDst, xSrc, ySrc, xMask, yMask, + xDst, yDst, width, height); + + return 1; +} + +void +uxa_composite(CARD8 op, + PicturePtr pSrc, + PicturePtr pMask, + PicturePtr pDst, + INT16 xSrc, + INT16 ySrc, + INT16 xMask, + INT16 yMask, + INT16 xDst, + INT16 yDst, + CARD16 width, + CARD16 height) +{ + uxa_screen_t *uxa_screen = uxa_get_screen(pDst->pDrawable->pScreen); + int ret = -1; + Bool saveSrcRepeat = pSrc->repeat; + Bool saveMaskRepeat = pMask ? pMask->repeat : 0; + RegionRec region; + + /* We currently don't support acceleration of gradients, or other pictures + * with a NULL pDrawable. + */ + if (uxa_screen->swappedOut || + pSrc->pDrawable == NULL || (pMask != NULL && pMask->pDrawable == NULL)) + { + goto fallback; + } + + /* Remove repeat in source if useless */ + if (pSrc->repeat && !pSrc->transform && xSrc >= 0 && + (xSrc + width) <= pSrc->pDrawable->width && ySrc >= 0 && + (ySrc + height) <= pSrc->pDrawable->height) + pSrc->repeat = 0; + + if (!pMask) + { + if ((op == PictOpSrc && + ((pSrc->format == pDst->format) || + (pSrc->format==PICT_a8r8g8b8 && pDst->format==PICT_x8r8g8b8) || + (pSrc->format==PICT_a8b8g8r8 && pDst->format==PICT_x8b8g8r8))) || + (op == PictOpOver && !pSrc->alphaMap && !pDst->alphaMap && + pSrc->format == pDst->format && + (pSrc->format==PICT_x8r8g8b8 || pSrc->format==PICT_x8b8g8r8))) + { + if (pSrc->pDrawable->width == 1 && + pSrc->pDrawable->height == 1 && + pSrc->repeat) + { + ret = uxa_try_driver_solid_fill(pSrc, pDst, xSrc, ySrc, xDst, yDst, + width, height); + if (ret == 1) + goto done; + } + else if (pSrc->pDrawable != NULL && + !pSrc->repeat && + !pSrc->transform) + { + xDst += pDst->pDrawable->x; + yDst += pDst->pDrawable->y; + xSrc += pSrc->pDrawable->x; + ySrc += pSrc->pDrawable->y; + + if (!miComputeCompositeRegion (®ion, pSrc, pMask, pDst, + xSrc, ySrc, xMask, yMask, xDst, + yDst, width, height)) + goto done; + + + uxa_copy_n_to_n (pSrc->pDrawable, pDst->pDrawable, NULL, + REGION_RECTS(®ion), REGION_NUM_RECTS(®ion), + xSrc - xDst, ySrc - yDst, + FALSE, FALSE, 0, NULL); + REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); + goto done; + } + else if (pSrc->pDrawable != NULL && + pSrc->pDrawable->type == DRAWABLE_PIXMAP && + !pSrc->transform && + pSrc->repeatType == RepeatNormal) + { + DDXPointRec patOrg; + + /* Let's see if the driver can do the repeat in one go */ + if (uxa_screen->info->PrepareComposite && !pSrc->alphaMap && + !pDst->alphaMap) + { + ret = uxa_try_driver_composite(op, pSrc, pMask, pDst, xSrc, + ySrc, xMask, yMask, xDst, yDst, + width, height); + if (ret == 1) + goto done; + } + + /* Now see if we can use uxa_fill_region_tiled() */ + xDst += pDst->pDrawable->x; + yDst += pDst->pDrawable->y; + xSrc += pSrc->pDrawable->x; + ySrc += pSrc->pDrawable->y; + + if (!miComputeCompositeRegion (®ion, pSrc, pMask, pDst, xSrc, + ySrc, xMask, yMask, xDst, yDst, + width, height)) + goto done; + + /* pattern origin is the point in the destination drawable + * corresponding to (0,0) in the source */ + patOrg.x = xDst - xSrc; + patOrg.y = yDst - ySrc; + + ret = uxa_fill_region_tiled(pDst->pDrawable, ®ion, + (PixmapPtr)pSrc->pDrawable, + &patOrg, FB_ALLONES, GXcopy); + + REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); + + if (ret) + goto done; + } + } + } + + /* Remove repeat in mask if useless */ + if (pMask && pMask->repeat && !pMask->transform && xMask >= 0 && + (xMask + width) <= pMask->pDrawable->width && yMask >= 0 && + (yMask + height) <= pMask->pDrawable->height) + pMask->repeat = 0; + + if (uxa_screen->info->PrepareComposite && + !pSrc->alphaMap && (!pMask || !pMask->alphaMap) && !pDst->alphaMap) + { + Bool isSrcSolid; + + ret = uxa_try_driver_composite(op, pSrc, pMask, pDst, xSrc, ySrc, xMask, + yMask, xDst, yDst, width, height); + if (ret == 1) + goto done; + + /* For generic masks and solid src pictures, mach64 can do Over in two + * passes, similar to the component-alpha case. + */ + isSrcSolid = pSrc->pDrawable->width == 1 && + pSrc->pDrawable->height == 1 && + pSrc->repeat; + + /* If we couldn't do the Composite in a single pass, and it was a + * component-alpha Over, see if we can do it in two passes with + * an OutReverse and then an Add. + */ + if (ret == -1 && op == PictOpOver && pMask && + (pMask->componentAlpha || isSrcSolid)) { + ret = uxa_try_magic_two_pass_composite_helper(op, pSrc, pMask, pDst, + xSrc, ySrc, + xMask, yMask, xDst, yDst, + width, height); + if (ret == 1) + goto done; + } + } + +fallback: +#if DEBUG_TRACE_FALL + uxa_print_composite_fallback (op, pSrc, pMask, pDst); +#endif + + uxa_check_composite (op, pSrc, pMask, pDst, xSrc, ySrc, + xMask, yMask, xDst, yDst, width, height); + +done: + pSrc->repeat = saveSrcRepeat; + if (pMask) + pMask->repeat = saveMaskRepeat; +} +#endif + +/** + * Same as miCreateAlphaPicture, except it uses uxa_check_poly_fill_rect instead + * of PolyFillRect to initialize the pixmap after creating it, to prevent + * the pixmap from being migrated. + * + * See the comments about uxa_trapezoids and uxa_triangles. + */ +static PicturePtr +uxa_create_alpha_picture (ScreenPtr pScreen, + PicturePtr pDst, + PictFormatPtr pPictFormat, + CARD16 width, + CARD16 height) +{ + PixmapPtr pPixmap; + PicturePtr pPicture; + GCPtr pGC; + int error; + xRectangle rect; + + if (width > 32767 || height > 32767) + return 0; + + if (!pPictFormat) + { + if (pDst->polyEdge == PolyEdgeSharp) + pPictFormat = PictureMatchFormat (pScreen, 1, PICT_a1); + else + pPictFormat = PictureMatchFormat (pScreen, 8, PICT_a8); + if (!pPictFormat) + return 0; + } + + pPixmap = (*pScreen->CreatePixmap) (pScreen, width, height, + pPictFormat->depth, 0); + if (!pPixmap) + return 0; + pGC = GetScratchGC (pPixmap->drawable.depth, pScreen); + if (!pGC) + { + (*pScreen->DestroyPixmap) (pPixmap); + return 0; + } + ValidateGC (&pPixmap->drawable, pGC); + rect.x = 0; + rect.y = 0; + rect.width = width; + rect.height = height; + uxa_check_poly_fill_rect (&pPixmap->drawable, pGC, 1, &rect); + FreeScratchGC (pGC); + pPicture = CreatePicture (0, &pPixmap->drawable, pPictFormat, + 0, 0, serverClient, &error); + (*pScreen->DestroyPixmap) (pPixmap); + return pPicture; +} + +/** + * uxa_trapezoids is essentially a copy of miTrapezoids that uses + * uxa_create_alpha_picture instead of miCreateAlphaPicture. + * + * The problem with miCreateAlphaPicture is that it calls PolyFillRect + * to initialize the contents after creating the pixmap, which + * causes the pixmap to be moved in for acceleration. The subsequent + * call to RasterizeTrapezoid won't be accelerated however, which + * forces the pixmap to be moved out again. + * + * uxa_create_alpha_picture avoids this roundtrip by using uxa_check_poly_fill_rect + * to initialize the contents. + */ +void +uxa_trapezoids (CARD8 op, PicturePtr pSrc, PicturePtr pDst, + PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc, + int ntrap, xTrapezoid *traps) +{ + ScreenPtr pScreen = pDst->pDrawable->pScreen; + PictureScreenPtr ps = GetPictureScreen(pScreen); + BoxRec bounds; + Bool direct = op == PictOpAdd && miIsSolidAlpha (pSrc); + + if (maskFormat || direct) { + miTrapezoidBounds (ntrap, traps, &bounds); + + if (bounds.y1 >= bounds.y2 || bounds.x1 >= bounds.x2) + return; + } + + /* + * Check for solid alpha add + */ + if (direct) + { + DrawablePtr pDraw = pDst->pDrawable; + PixmapPtr pixmap = uxa_get_drawable_pixmap (pDraw); + int xoff, yoff; + + uxa_get_drawable_deltas(pDraw, pixmap, &xoff, &yoff); + + xoff += pDraw->x; + yoff += pDraw->y; + + uxa_prepare_access(pDraw, UXA_PREPARE_DEST); + + for (; ntrap; ntrap--, traps++) + (*ps->RasterizeTrapezoid) (pDst, traps, 0, 0); + + uxa_finish_access(pDraw, UXA_PREPARE_DEST); + } + else if (maskFormat) + { + PicturePtr pPicture; + INT16 xDst, yDst; + INT16 xRel, yRel; + + xDst = traps[0].left.p1.x >> 16; + yDst = traps[0].left.p1.y >> 16; + + pPicture = uxa_create_alpha_picture (pScreen, pDst, maskFormat, + bounds.x2 - bounds.x1, + bounds.y2 - bounds.y1); + if (!pPicture) + return; + + uxa_prepare_access(pPicture->pDrawable, UXA_PREPARE_DEST); + for (; ntrap; ntrap--, traps++) + (*ps->RasterizeTrapezoid) (pPicture, traps, + -bounds.x1, -bounds.y1); + uxa_finish_access(pPicture->pDrawable, UXA_PREPARE_DEST); + + xRel = bounds.x1 + xSrc - xDst; + yRel = bounds.y1 + ySrc - yDst; + CompositePicture (op, pSrc, pPicture, pDst, + xRel, yRel, 0, 0, bounds.x1, bounds.y1, + bounds.x2 - bounds.x1, + bounds.y2 - bounds.y1); + FreePicture (pPicture, 0); + } + else + { + if (pDst->polyEdge == PolyEdgeSharp) + maskFormat = PictureMatchFormat (pScreen, 1, PICT_a1); + else + maskFormat = PictureMatchFormat (pScreen, 8, PICT_a8); + for (; ntrap; ntrap--, traps++) + uxa_trapezoids (op, pSrc, pDst, maskFormat, xSrc, ySrc, 1, traps); + } +} + +/** + * uxa_triangles is essentially a copy of miTriangles that uses + * uxa_create_alpha_picture instead of miCreateAlphaPicture. + * + * The problem with miCreateAlphaPicture is that it calls PolyFillRect + * to initialize the contents after creating the pixmap, which + * causes the pixmap to be moved in for acceleration. The subsequent + * call to AddTriangles won't be accelerated however, which forces the pixmap + * to be moved out again. + * + * uxa_create_alpha_picture avoids this roundtrip by using uxa_check_poly_fill_rect + * to initialize the contents. + */ +void +uxa_triangles (CARD8 op, PicturePtr pSrc, PicturePtr pDst, + PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc, + int ntri, xTriangle *tris) +{ + ScreenPtr pScreen = pDst->pDrawable->pScreen; + PictureScreenPtr ps = GetPictureScreen(pScreen); + BoxRec bounds; + Bool direct = op == PictOpAdd && miIsSolidAlpha (pSrc); + + if (maskFormat || direct) { + miTriangleBounds (ntri, tris, &bounds); + + if (bounds.y1 >= bounds.y2 || bounds.x1 >= bounds.x2) + return; + } + + /* + * Check for solid alpha add + */ + if (direct) + { + DrawablePtr pDraw = pDst->pDrawable; + uxa_prepare_access(pDraw, UXA_PREPARE_DEST); + (*ps->AddTriangles) (pDst, 0, 0, ntri, tris); + uxa_finish_access(pDraw, UXA_PREPARE_DEST); + } + else if (maskFormat) + { + PicturePtr pPicture; + INT16 xDst, yDst; + INT16 xRel, yRel; + + xDst = tris[0].p1.x >> 16; + yDst = tris[0].p1.y >> 16; + + pPicture = uxa_create_alpha_picture (pScreen, pDst, maskFormat, + bounds.x2 - bounds.x1, + bounds.y2 - bounds.y1); + if (!pPicture) + return; + + uxa_prepare_access(pPicture->pDrawable, UXA_PREPARE_DEST); + (*ps->AddTriangles) (pPicture, -bounds.x1, -bounds.y1, ntri, tris); + uxa_finish_access(pPicture->pDrawable, UXA_PREPARE_DEST); + + xRel = bounds.x1 + xSrc - xDst; + yRel = bounds.y1 + ySrc - yDst; + CompositePicture (op, pSrc, pPicture, pDst, + xRel, yRel, 0, 0, bounds.x1, bounds.y1, + bounds.x2 - bounds.x1, bounds.y2 - bounds.y1); + FreePicture (pPicture, 0); + } + else + { + if (pDst->polyEdge == PolyEdgeSharp) + maskFormat = PictureMatchFormat (pScreen, 1, PICT_a1); + else + maskFormat = PictureMatchFormat (pScreen, 8, PICT_a8); + + for (; ntri; ntri--, tris++) + uxa_triangles (op, pSrc, pDst, maskFormat, xSrc, ySrc, 1, tris); + } +} diff --git a/uxa/uxa-unaccel.c b/uxa/uxa-unaccel.c new file mode 100644 index 00000000..27194208 --- /dev/null +++ b/uxa/uxa-unaccel.c @@ -0,0 +1,370 @@ +/* + * + * Copyright © 1999 Keith Packard + * + * 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. + */ + +#include "uxa-priv.h" + +#ifdef RENDER +#include "mipict.h" +#endif + +/* + * These functions wrap the low-level fb rendering functions and + * synchronize framebuffer/accelerated drawing by stalling until + * the accelerator is idle + */ + +/** + * Calls uxa_prepare_access with UXA_PREPARE_SRC for the tile, if that is the + * current fill style. + * + * Solid doesn't use an extra pixmap source, and Stippled/OpaqueStippled are + * 1bpp and never in fb, so we don't worry about them. + * We should worry about them for completeness sake and going forward. + */ +void +uxa_prepare_access_gc(GCPtr pGC) +{ + if (pGC->stipple) + uxa_prepare_access(&pGC->stipple->drawable, UXA_PREPARE_MASK); + if (pGC->fillStyle == FillTiled) + uxa_prepare_access(&pGC->tile.pixmap->drawable, UXA_PREPARE_SRC); +} + +/** + * Finishes access to the tile in the GC, if used. + */ +void +uxa_finish_access_gc(GCPtr pGC) +{ + if (pGC->fillStyle == FillTiled) + uxa_finish_access(&pGC->tile.pixmap->drawable, UXA_PREPARE_MASK); + if (pGC->stipple) + uxa_finish_access(&pGC->stipple->drawable, UXA_PREPARE_SRC); +} + +#if DEBUG_TRACE_FALL +char +uxa_drawable_location(DrawablePtr pDrawable) +{ + return uxa_drawable_is_offscreen(pDrawable) ? 's' : 'm'; +} +#endif /* DEBUG_TRACE_FALL */ + +void +uxa_check_fill_spans (DrawablePtr pDrawable, GCPtr pGC, int nspans, + DDXPointPtr ppt, int *pwidth, int fSorted) +{ + UXA_FALLBACK(("to %p (%c)\n", pDrawable, uxa_drawable_location(pDrawable))); + uxa_prepare_access (pDrawable, UXA_PREPARE_DEST); + uxa_prepare_access_gc (pGC); + fbFillSpans (pDrawable, pGC, nspans, ppt, pwidth, fSorted); + uxa_finish_access_gc (pGC); + uxa_finish_access (pDrawable, UXA_PREPARE_DEST); +} + +void +uxa_check_set_spans (DrawablePtr pDrawable, GCPtr pGC, char *psrc, + DDXPointPtr ppt, int *pwidth, int nspans, int fSorted) +{ + UXA_FALLBACK(("to %p (%c)\n", pDrawable, uxa_drawable_location(pDrawable))); + uxa_prepare_access (pDrawable, UXA_PREPARE_DEST); + fbSetSpans (pDrawable, pGC, psrc, ppt, pwidth, nspans, fSorted); + uxa_finish_access (pDrawable, UXA_PREPARE_DEST); +} + +void +uxa_check_put_image (DrawablePtr pDrawable, GCPtr pGC, int depth, + int x, int y, int w, int h, int leftPad, int format, + char *bits) +{ + UXA_FALLBACK(("to %p (%c)\n", pDrawable, uxa_drawable_location(pDrawable))); + uxa_prepare_access (pDrawable, UXA_PREPARE_DEST); + fbPutImage (pDrawable, pGC, depth, x, y, w, h, leftPad, format, bits); + uxa_finish_access (pDrawable, UXA_PREPARE_DEST); +} + +RegionPtr +uxa_check_copy_area (DrawablePtr pSrc, DrawablePtr pDst, GCPtr pGC, + int srcx, int srcy, int w, int h, int dstx, int dsty) +{ + RegionPtr ret; + + UXA_FALLBACK(("from %p to %p (%c,%c)\n", pSrc, pDst, + uxa_drawable_location(pSrc), uxa_drawable_location(pDst))); + uxa_prepare_access (pDst, UXA_PREPARE_DEST); + uxa_prepare_access (pSrc, UXA_PREPARE_SRC); + ret = fbCopyArea (pSrc, pDst, pGC, srcx, srcy, w, h, dstx, dsty); + uxa_finish_access (pSrc, UXA_PREPARE_SRC); + uxa_finish_access (pDst, UXA_PREPARE_DEST); + + return ret; +} + +RegionPtr +uxa_check_copy_plane (DrawablePtr pSrc, DrawablePtr pDst, GCPtr pGC, + int srcx, int srcy, int w, int h, int dstx, int dsty, + unsigned long bitPlane) +{ + RegionPtr ret; + + UXA_FALLBACK(("from %p to %p (%c,%c)\n", pSrc, pDst, + uxa_drawable_location(pSrc), uxa_drawable_location(pDst))); + uxa_prepare_access (pDst, UXA_PREPARE_DEST); + uxa_prepare_access (pSrc, UXA_PREPARE_SRC); + ret = fbCopyPlane (pSrc, pDst, pGC, srcx, srcy, w, h, dstx, dsty, + bitPlane); + uxa_finish_access (pSrc, UXA_PREPARE_SRC); + uxa_finish_access (pDst, UXA_PREPARE_DEST); + + return ret; +} + +void +uxa_check_poly_point (DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, + DDXPointPtr pptInit) +{ + UXA_FALLBACK(("to %p (%c)\n", pDrawable, uxa_drawable_location(pDrawable))); + uxa_prepare_access (pDrawable, UXA_PREPARE_DEST); + fbPolyPoint (pDrawable, pGC, mode, npt, pptInit); + uxa_finish_access (pDrawable, UXA_PREPARE_DEST); +} + +void +uxa_check_poly_lines (DrawablePtr pDrawable, GCPtr pGC, + int mode, int npt, DDXPointPtr ppt) +{ + UXA_FALLBACK(("to %p (%c), width %d, mode %d, count %d\n", + pDrawable, uxa_drawable_location(pDrawable), + pGC->lineWidth, mode, npt)); + + if (pGC->lineWidth == 0) { + uxa_prepare_access (pDrawable, UXA_PREPARE_DEST); + uxa_prepare_access_gc (pGC); + fbPolyLine (pDrawable, pGC, mode, npt, ppt); + uxa_finish_access_gc (pGC); + uxa_finish_access (pDrawable, UXA_PREPARE_DEST); + return; + } + /* fb calls mi functions in the lineWidth != 0 case. */ + fbPolyLine (pDrawable, pGC, mode, npt, ppt); +} + +void +uxa_check_poly_segment (DrawablePtr pDrawable, GCPtr pGC, + int nsegInit, xSegment *pSegInit) +{ + UXA_FALLBACK(("to %p (%c) width %d, count %d\n", pDrawable, + uxa_drawable_location(pDrawable), pGC->lineWidth, nsegInit)); + if (pGC->lineWidth == 0) { + uxa_prepare_access (pDrawable, UXA_PREPARE_DEST); + uxa_prepare_access_gc (pGC); + fbPolySegment (pDrawable, pGC, nsegInit, pSegInit); + uxa_finish_access_gc (pGC); + uxa_finish_access (pDrawable, UXA_PREPARE_DEST); + return; + } + /* fb calls mi functions in the lineWidth != 0 case. */ + fbPolySegment (pDrawable, pGC, nsegInit, pSegInit); +} + +void +uxa_check_poly_arc (DrawablePtr pDrawable, GCPtr pGC, + int narcs, xArc *pArcs) +{ + UXA_FALLBACK(("to %p (%c)\n", pDrawable, uxa_drawable_location(pDrawable))); + + /* Disable this as fbPolyArc can call miZeroPolyArc which in turn + * can call accelerated functions, that as yet, haven't been notified + * with uxa_finish_access(). + */ +#if 0 + if (pGC->lineWidth == 0) + { + uxa_prepare_access (pDrawable, UXA_PREPARE_DEST); + uxa_prepare_access_gc (pGC); + fbPolyArc (pDrawable, pGC, narcs, pArcs); + uxa_finish_access_gc (pGC); + uxa_finish_access (pDrawable, UXA_PREPARE_DEST); + return; + } +#endif + miPolyArc (pDrawable, pGC, narcs, pArcs); +} + +void +uxa_check_poly_fill_rect (DrawablePtr pDrawable, GCPtr pGC, + int nrect, xRectangle *prect) +{ + UXA_FALLBACK(("to %p (%c)\n", pDrawable, uxa_drawable_location(pDrawable))); + + uxa_prepare_access (pDrawable, UXA_PREPARE_DEST); + uxa_prepare_access_gc (pGC); + fbPolyFillRect (pDrawable, pGC, nrect, prect); + uxa_finish_access_gc (pGC); + uxa_finish_access (pDrawable, UXA_PREPARE_DEST); +} + +void +uxa_check_image_glyph_blt (DrawablePtr pDrawable, GCPtr pGC, + int x, int y, unsigned int nglyph, + CharInfoPtr *ppci, pointer pglyphBase) +{ + UXA_FALLBACK(("to %p (%c)\n", pDrawable, + uxa_drawable_location(pDrawable))); + uxa_prepare_access (pDrawable, UXA_PREPARE_DEST); + uxa_prepare_access_gc (pGC); + fbImageGlyphBlt (pDrawable, pGC, x, y, nglyph, ppci, pglyphBase); + uxa_finish_access_gc (pGC); + uxa_finish_access (pDrawable, UXA_PREPARE_DEST); +} + +void +uxa_check_poly_glyph_blt (DrawablePtr pDrawable, GCPtr pGC, + int x, int y, unsigned int nglyph, + CharInfoPtr *ppci, pointer pglyphBase) +{ + UXA_FALLBACK(("to %p (%c), style %d alu %d\n", pDrawable, + uxa_drawable_location(pDrawable), pGC->fillStyle, pGC->alu)); + uxa_prepare_access (pDrawable, UXA_PREPARE_DEST); + uxa_prepare_access_gc (pGC); + fbPolyGlyphBlt (pDrawable, pGC, x, y, nglyph, ppci, pglyphBase); + uxa_finish_access_gc (pGC); + uxa_finish_access (pDrawable, UXA_PREPARE_DEST); +} + +void +uxa_check_push_pixels (GCPtr pGC, PixmapPtr pBitmap, + DrawablePtr pDrawable, + int w, int h, int x, int y) +{ + UXA_FALLBACK(("from %p to %p (%c,%c)\n", pBitmap, pDrawable, + uxa_drawable_location(&pBitmap->drawable), + uxa_drawable_location(pDrawable))); + uxa_prepare_access (pDrawable, UXA_PREPARE_DEST); + uxa_prepare_access (&pBitmap->drawable, UXA_PREPARE_SRC); + uxa_prepare_access_gc (pGC); + fbPushPixels (pGC, pBitmap, pDrawable, w, h, x, y); + uxa_finish_access_gc (pGC); + uxa_finish_access (&pBitmap->drawable, UXA_PREPARE_SRC); + uxa_finish_access (pDrawable, UXA_PREPARE_DEST); +} + +void +uxa_check_get_spans (DrawablePtr pDrawable, + int wMax, + DDXPointPtr ppt, + int *pwidth, + int nspans, + char *pdstStart) +{ + UXA_FALLBACK(("from %p (%c)\n", pDrawable, uxa_drawable_location(pDrawable))); + uxa_prepare_access (pDrawable, UXA_PREPARE_SRC); + fbGetSpans (pDrawable, wMax, ppt, pwidth, nspans, pdstStart); + uxa_finish_access (pDrawable, UXA_PREPARE_SRC); +} + +void +uxa_check_composite (CARD8 op, + PicturePtr pSrc, + PicturePtr pMask, + PicturePtr pDst, + INT16 xSrc, + INT16 ySrc, + INT16 xMask, + INT16 yMask, + INT16 xDst, + INT16 yDst, + CARD16 width, + CARD16 height) +{ + UXA_FALLBACK(("from picts %p/%p to pict %p\n", + pSrc, pMask, pDst)); + + uxa_prepare_access (pDst->pDrawable, UXA_PREPARE_DEST); + if (pSrc->pDrawable != NULL) + uxa_prepare_access (pSrc->pDrawable, UXA_PREPARE_SRC); + if (pMask && pMask->pDrawable != NULL) + uxa_prepare_access (pMask->pDrawable, UXA_PREPARE_MASK); + fbComposite (op, + pSrc, + pMask, + pDst, + xSrc, + ySrc, + xMask, + yMask, + xDst, + yDst, + width, + height); + if (pMask && pMask->pDrawable != NULL) + uxa_finish_access (pMask->pDrawable, UXA_PREPARE_MASK); + if (pSrc->pDrawable != NULL) + uxa_finish_access (pSrc->pDrawable, UXA_PREPARE_SRC); + uxa_finish_access (pDst->pDrawable, UXA_PREPARE_DEST); +} + +void +uxa_check_add_traps (PicturePtr pPicture, + INT16 x_off, + INT16 y_off, + int ntrap, + xTrap *traps) +{ + UXA_FALLBACK(("to pict %p (%c)\n", + uxa_drawable_location(pPicture->pDrawable))); + uxa_prepare_access(pPicture->pDrawable, UXA_PREPARE_DEST); + fbAddTraps (pPicture, x_off, y_off, ntrap, traps); + uxa_finish_access(pPicture->pDrawable, UXA_PREPARE_DEST); +} + +/** + * Gets the 0,0 pixel of a pixmap. Used for doing solid fills of tiled pixmaps + * that happen to be 1x1. Pixmap must be at least 8bpp. + * + * XXX This really belongs in fb, so it can be aware of tiling and etc. + */ +CARD32 +uxa_get_pixmap_first_pixel (PixmapPtr pPixmap) +{ + CARD32 pixel; + void *fb; + + uxa_prepare_access (&pPixmap->drawable, UXA_PREPARE_SRC); + fb = pPixmap->devPrivate.ptr; + + switch (pPixmap->drawable.bitsPerPixel) { + case 32: + pixel = *(CARD32 *)fb; + break; + case 16: + pixel = *(CARD16 *)fb; + break; + default: + pixel = *(CARD8 *)fb; + break; + } + uxa_finish_access(&pPixmap->drawable, UXA_PREPARE_SRC); + + return pixel; +} diff --git a/uxa/uxa.c b/uxa/uxa.c new file mode 100644 index 00000000..9745f8bb --- /dev/null +++ b/uxa/uxa.c @@ -0,0 +1,573 @@ +/* + * 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 UXA, and has various + * functions not responsible for performing rendering, pixmap migration, or + * memory management. + */ + +#ifdef HAVE_DIX_CONFIG_H +#include <dix-config.h> +#endif + +#include <stdlib.h> + +#include "uxa-priv.h" +#include <X11/fonts/fontstruct.h> +#include "dixfontstr.h" +#include "uxa.h" + +DevPrivateKey uxa_screen_key = &uxa_screen_key; + +/** + * uxa_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 + * uxa_get_drawable_pixmap() on the drawable. + */ +PixmapPtr +uxa_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 +uxa_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; +} + +/** + * uxa_pixmap_is_offscreen() is used to determine if a pixmap is in offscreen + * memory, meaning that acceleration could probably be done to it, and that it + * will need to be wrapped by PrepareAccess()/FinishAccess() when accessing it + * with the CPU. + * + * Note that except for UploadToScreen()/DownloadFromScreen() (which explicitly + * deal with moving pixmaps in and out of system memory), UXA will give drivers + * pixmaps as arguments for which uxa_pixmap_is_offscreen() is TRUE. + * + * @return TRUE if the given drawable is in framebuffer memory. + */ +Bool +uxa_pixmap_is_offscreen(PixmapPtr p) +{ + ScreenPtr pScreen = p->drawable.pScreen; + uxa_screen_t *uxa_screen = uxa_get_screen(pScreen); + + if (uxa_screen->info->PixmapIsOffscreen) + return uxa_screen->info->PixmapIsOffscreen(p); + + return FALSE; +} + +/** + * uxa_drawable_is_offscreen() is a convenience wrapper for uxa_pixmap_is_offscreen(). + */ +Bool +uxa_drawable_is_offscreen (DrawablePtr pDrawable) +{ + return uxa_pixmap_is_offscreen (uxa_get_drawable_pixmap (pDrawable)); +} + +/** + * 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 +uxa_get_offscreen_pixmap (DrawablePtr drawable, int *xp, int *yp) +{ + PixmapPtr pixmap = uxa_get_drawable_pixmap (drawable); + + uxa_get_drawable_deltas (drawable, pixmap, xp, yp); + + if (uxa_pixmap_is_offscreen (pixmap)) + return pixmap; + else + return NULL; +} + + + +/** + * uxa_prepare_access() is UXA's wrapper for the driver's PrepareAccess() handler. + * + * It deals with waiting for synchronization with the card, determining if + * PrepareAccess() is necessary, and working around PrepareAccess() failure. + */ +void +uxa_prepare_access(DrawablePtr pDrawable, int index) +{ + ScreenPtr pScreen = pDrawable->pScreen; + uxa_screen_t *uxa_screen = uxa_get_screen(pScreen); + PixmapPtr pPixmap = uxa_get_drawable_pixmap (pDrawable); + Bool offscreen = uxa_pixmap_is_offscreen(pPixmap); + + if (!offscreen) + return; + + /* XXX should be pPixmap eventually */ + uxa_wait_sync (pScreen); + + if (uxa_screen->info->PrepareAccess) + (*uxa_screen->info->PrepareAccess) (pPixmap, index); +} + +/** + * uxa_finish_access() is UXA's wrapper for the driver's FinishAccess() handler. + * + * It deals with calling the driver's FinishAccess() only if necessary. + */ +void +uxa_finish_access(DrawablePtr pDrawable, int index) +{ + ScreenPtr pScreen = pDrawable->pScreen; + uxa_screen_t *uxa_screen = uxa_get_screen(pScreen); + PixmapPtr pPixmap = uxa_get_drawable_pixmap (pDrawable); + + if (uxa_screen->info->FinishAccess == NULL) + return; + + if (!uxa_pixmap_is_offscreen (pPixmap)) + return; + + (*uxa_screen->info->FinishAccess) (pPixmap, index); +} + +/** + * uxa_validate_gc() sets the ops to UXA's implementations, which may be + * accelerated or may sync the card and fall back to fb. + */ +static void +uxa_validate_gc (GCPtr pGC, unsigned long changes, DrawablePtr pDrawable) +{ + /* fbValidateGC will do direct access to pixmaps if the tiling has changed. + * Preempt fbValidateGC by doing its work and masking the change out, so + * that we can do the Prepare/FinishAccess. + */ +#ifdef FB_24_32BIT + if ((changes & GCTile) && fbGetRotatedPixmap(pGC)) { + (*pGC->pScreen->DestroyPixmap) (fbGetRotatedPixmap(pGC)); + fbGetRotatedPixmap(pGC) = 0; + } + + if (pGC->fillStyle == FillTiled) { + PixmapPtr pOldTile, pNewTile; + + pOldTile = pGC->tile.pixmap; + if (pOldTile->drawable.bitsPerPixel != pDrawable->bitsPerPixel) + { + pNewTile = fbGetRotatedPixmap(pGC); + if (!pNewTile || + pNewTile ->drawable.bitsPerPixel != pDrawable->bitsPerPixel) + { + if (pNewTile) + (*pGC->pScreen->DestroyPixmap) (pNewTile); + /* fb24_32ReformatTile will do direct access of a newly- + * allocated pixmap. This isn't a problem yet, since we don't + * put pixmaps in FB until at least one accelerated UXA op. + */ + uxa_prepare_access(&pOldTile->drawable, UXA_PREPARE_SRC); + pNewTile = fb24_32ReformatTile (pOldTile, + pDrawable->bitsPerPixel); + uxa_finish_access(&pOldTile->drawable, UXA_PREPARE_SRC); + } + if (pNewTile) + { + fbGetRotatedPixmap(pGC) = pOldTile; + pGC->tile.pixmap = pNewTile; + changes |= GCTile; + } + } + } +#endif + if (changes & GCTile) { + if (!pGC->tileIsPixel && FbEvenTile (pGC->tile.pixmap->drawable.width * + pDrawable->bitsPerPixel)) + { + uxa_prepare_access(&pGC->tile.pixmap->drawable, UXA_PREPARE_SRC); + fbPadPixmap (pGC->tile.pixmap); + uxa_finish_access(&pGC->tile.pixmap->drawable, UXA_PREPARE_SRC); + } + /* Mask out the GCTile change notification, now that we've done FB's + * job for it. + */ + changes &= ~GCTile; + } + + uxa_prepare_access_gc(pGC); + fbValidateGC (pGC, changes, pDrawable); + uxa_finish_access_gc(pGC); + + pGC->ops = (GCOps *) &uxa_ops; +} + +static GCFuncs uxaGCFuncs = { + uxa_validate_gc, + miChangeGC, + miCopyGC, + miDestroyGC, + miChangeClip, + miDestroyClip, + miCopyClip +}; + +/** + * uxa_create_gc makes a new GC and hooks up its funcs handler, so that + * uxa_validate_gc() will get called. + */ +static int +uxa_create_gc (GCPtr pGC) +{ + if (!fbCreateGC (pGC)) + return FALSE; + + pGC->funcs = &uxaGCFuncs; + + return TRUE; +} + +void +uxa_prepare_access_window(WindowPtr pWin) +{ + if (pWin->backgroundState == BackgroundPixmap) + uxa_prepare_access(&pWin->background.pixmap->drawable, UXA_PREPARE_SRC); + + if (pWin->borderIsPixel == FALSE) + uxa_prepare_access(&pWin->border.pixmap->drawable, UXA_PREPARE_SRC); +} + +void +uxa_finish_access_window(WindowPtr pWin) +{ + if (pWin->backgroundState == BackgroundPixmap) + uxa_finish_access(&pWin->background.pixmap->drawable, UXA_PREPARE_SRC); + + if (pWin->borderIsPixel == FALSE) + uxa_finish_access(&pWin->border.pixmap->drawable, UXA_PREPARE_SRC); +} + +static Bool +uxa_change_window_attributes(WindowPtr pWin, unsigned long mask) +{ + Bool ret; + + uxa_prepare_access_window(pWin); + ret = fbChangeWindowAttributes(pWin, mask); + uxa_finish_access_window(pWin); + return ret; +} + +static RegionPtr +uxa_bitmap_to_region(PixmapPtr pPix) +{ + RegionPtr ret; + uxa_prepare_access(&pPix->drawable, UXA_PREPARE_SRC); + ret = fbPixmapToRegion(pPix); + uxa_finish_access(&pPix->drawable, UXA_PREPARE_SRC); + return ret; +} + +/** + * uxa_close_screen() unwraps its wrapped screen functions and tears down UXA's + * screen private, before calling down to the next CloseSccreen. + */ +static Bool +uxa_close_screen(int i, ScreenPtr pScreen) +{ + uxa_screen_t *uxa_screen = uxa_get_screen(pScreen); +#ifdef RENDER + PictureScreenPtr ps = GetPictureScreenIfSet(pScreen); +#endif + + uxa_glyphs_fini(pScreen); + + pScreen->CreateGC = uxa_screen->SavedCreateGC; + pScreen->CloseScreen = uxa_screen->SavedCloseScreen; + pScreen->GetImage = uxa_screen->SavedGetImage; + pScreen->GetSpans = uxa_screen->SavedGetSpans; + pScreen->CreatePixmap = uxa_screen->SavedCreatePixmap; + pScreen->DestroyPixmap = uxa_screen->SavedDestroyPixmap; + pScreen->CopyWindow = uxa_screen->SavedCopyWindow; + pScreen->ChangeWindowAttributes = uxa_screen->SavedChangeWindowAttributes; + pScreen->BitmapToRegion = uxa_screen->SavedBitmapToRegion; +#ifdef RENDER + if (ps) { + ps->Composite = uxa_screen->SavedComposite; + ps->Glyphs = uxa_screen->SavedGlyphs; + ps->Trapezoids = uxa_screen->SavedTrapezoids; + ps->AddTraps = uxa_screen->SavedAddTraps; + ps->Triangles = uxa_screen->SavedTriangles; + } +#endif + + xfree (uxa_screen); + + return (*pScreen->CloseScreen) (i, pScreen); +} + +/** + * This function allocates a driver structure for UXA drivers to fill in. By + * having UXA allocate the structure, the driver structure can be extended + * without breaking ABI between UXA and the drivers. The driver's + * responsibility is to check beforehand that the UXA 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 + */ +uxa_driver_t * +uxa_driver_alloc(void) +{ + return xcalloc(1, sizeof(uxa_driver_t)); +} + +/** + * @param pScreen screen being initialized + * @param pScreenInfo UXA driver record + * + * uxa_driver_init sets up UXA given a driver record filled in by the driver. + * pScreenInfo should have been allocated by uxa_driver_alloc(). See the + * comments in _UxaDriver for what must be filled in and what is optional. + * + * @return TRUE if UXA was successfully initialized. + */ +Bool +uxa_driver_init(ScreenPtr screen, uxa_driver_t *uxa_driver) +{ + uxa_screen_t *uxa_screen; +#ifdef RENDER + PictureScreenPtr ps; +#endif + + if (!uxa_driver) + return FALSE; + + if (uxa_driver->uxa_major != UXA_VERSION_MAJOR || + uxa_driver->uxa_minor > UXA_VERSION_MINOR) + { + LogMessage(X_ERROR, "UXA(%d): driver's UXA version requirements " + "(%d.%d) are incompatible with UXA version (%d.%d)\n", + screen->myNum, + uxa_driver->uxa_major, uxa_driver->uxa_minor, + UXA_VERSION_MAJOR, UXA_VERSION_MINOR); + return FALSE; + } + + if (!uxa_driver->PrepareSolid) { + LogMessage(X_ERROR, "UXA(%d): uxa_driver_t::PrepareSolid must be " + "non-NULL\n", screen->myNum); + return FALSE; + } + + if (!uxa_driver->PrepareCopy) { + LogMessage(X_ERROR, "UXA(%d): uxa_driver_t::PrepareCopy must be " + "non-NULL\n", screen->myNum); + return FALSE; + } + + if (!uxa_driver->WaitMarker) { + LogMessage(X_ERROR, "UXA(%d): uxa_driver_t::WaitMarker must be " + "non-NULL\n", screen->myNum); + return FALSE; + } + + /* If the driver doesn't set any max pitch values, we'll just assume + * that there's a limitation by pixels, and that it's the same as + * maxX. + * + * We want maxPitchPixels or maxPitchBytes to be set so we can check + * pixmaps against the max pitch in uxaCreatePixmap() -- it matters + * whether a pixmap is rejected because of its pitch or + * because of its width. + */ + if (!uxa_driver->maxPitchPixels && !uxa_driver->maxPitchBytes) + { + uxa_driver->maxPitchPixels = uxa_driver->maxX; + } + +#ifdef RENDER + ps = GetPictureScreenIfSet(screen); +#endif + + uxa_screen = xcalloc (sizeof (uxa_screen_t), 1); + + if (!uxa_screen) { + LogMessage(X_WARNING, "UXA(%d): Failed to allocate screen private\n", + screen->myNum); + return FALSE; + } + + uxa_screen->info = uxa_driver; + + dixSetPrivate(&screen->devPrivates, uxa_screen_key, uxa_screen); + +// exaDDXDriverInit(screen); + + /* + * Replace various fb screen functions + */ + uxa_screen->SavedCloseScreen = screen->CloseScreen; + screen->CloseScreen = uxa_close_screen; + + uxa_screen->SavedCreateGC = screen->CreateGC; + screen->CreateGC = uxa_create_gc; + + uxa_screen->SavedGetImage = screen->GetImage; + screen->GetImage = uxa_get_image; + + uxa_screen->SavedGetSpans = screen->GetSpans; + screen->GetSpans = uxa_check_get_spans; + + uxa_screen->SavedCopyWindow = screen->CopyWindow; + screen->CopyWindow = uxa_copy_window; + + uxa_screen->SavedChangeWindowAttributes = screen->ChangeWindowAttributes; + screen->ChangeWindowAttributes = uxa_change_window_attributes; + + uxa_screen->SavedBitmapToRegion = screen->BitmapToRegion; + screen->BitmapToRegion = uxa_bitmap_to_region; + +#ifdef RENDER + if (ps) { + uxa_screen->SavedComposite = ps->Composite; + ps->Composite = uxa_composite; + + uxa_screen->SavedGlyphs = ps->Glyphs; + ps->Glyphs = uxa_glyphs; + + uxa_screen->SavedTriangles = ps->Triangles; + ps->Triangles = uxa_triangles; + + uxa_screen->SavedTrapezoids = ps->Trapezoids; + ps->Trapezoids = uxa_trapezoids; + + uxa_screen->SavedAddTraps = ps->AddTraps; + ps->AddTraps = uxa_check_add_traps; + } +#endif + +#ifdef MITSHM + /* Re-register with the MI funcs, which don't allow shared pixmaps. + * Shared pixmaps are almost always a performance loss for us, but this + * still allows for SHM PutImage. + */ + ShmRegisterFuncs(screen, &uxa_shm_funcs); +#endif + + uxa_glyphs_init(screen); + + LogMessage(X_INFO, "UXA(%d): Driver registered support for the following" + " operations:\n", screen->myNum); + assert(uxa_driver->PrepareSolid != NULL); + LogMessage(X_INFO, " Solid\n"); + assert(uxa_driver->PrepareCopy != NULL); + LogMessage(X_INFO, " Copy\n"); + if (uxa_driver->PrepareComposite != NULL) { + LogMessage(X_INFO, " Composite (RENDER acceleration)\n"); + } + if (uxa_driver->UploadToScreen != NULL) { + LogMessage(X_INFO, " UploadToScreen\n"); + } + if (uxa_driver->DownloadFromScreen != NULL) { + LogMessage(X_INFO, " DownloadFromScreen\n"); + } + + return TRUE; +} + +/** + * uxa_driver_fini tears down UXA on a given screen. + * + * @param pScreen screen being torn down. + */ +void +uxa_driver_fini (ScreenPtr pScreen) +{ + /*right now does nothing*/ +} + +/** + * uxa_mark_sync() should be called after any asynchronous drawing by the hardware. + * + * @param pScreen screen which drawing occurred on + * + * uxa_mark_sync() 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 uxa_mark_sync(ScreenPtr pScreen) +{ + uxa_screen_t *uxa_screen = uxa_get_screen(pScreen); + + uxa_screen->info->needsSync = TRUE; + if (uxa_screen->info->MarkSync != NULL) { + uxa_screen->info->lastMarker = (*uxa_screen->info->MarkSync)(pScreen); + } +} + +/** + * uxa_wait_sync() 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 uxa_wait_sync(ScreenPtr pScreen) +{ + uxa_screen_t *uxa_screen = uxa_get_screen(pScreen); + + if (uxa_screen->info->needsSync && !uxa_screen->swappedOut) { + (*uxa_screen->info->WaitMarker)(pScreen, uxa_screen->info->lastMarker); + uxa_screen->info->needsSync = FALSE; + } +} diff --git a/uxa/uxa.h b/uxa/uxa.h new file mode 100644 index 00000000..57e84f37 --- /dev/null +++ b/uxa/uxa.h @@ -0,0 +1,671 @@ +/* + * Copyright © 2000, 2008 Keith Packard + * 2004 Eric Anholt + * 2005 Zack Rusin + * + * 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 copyright holders not be used in + * advertising or publicity pertaining to distribution of the software without + * specific, written prior permission. Copyright holders make no + * representations about the suitability of this software for any purpose. It + * is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS 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 + * UXA - the unified memory acceleration architecture. + * + * This is the header containing the public API of UXA for uxa drivers. + */ + +#ifndef UXA_H +#define UXA_H + +#include "scrnintstr.h" +#include "pixmapstr.h" +#include "windowstr.h" +#include "gcstruct.h" +#include "picturestr.h" +#include "fb.h" + +#define UXA_VERSION_MAJOR 1 +#define UXA_VERSION_MINOR 0 +#define UXA_VERSION_RELEASE 0 + +/** + * The UxaDriver structure is allocated through uxa_driver_alloc(), and then + * fllled in by drivers. + */ +typedef struct _UxaDriver { + /** + * uxa_major and uxa_minor should be set by the driver to the version of + * UXA which the driver was compiled for (or configures itself at runtime + * to support). This allows UXA to extend the structure for new features + * without breaking ABI for drivers compiled against older versions. + */ + int uxa_major, uxa_minor; + + /** + * The flags field is bitfield of boolean values controlling UXA's behavior. + * + * The flags include UXA_TWO_BITBLT_DIRECTIONS. + */ + int flags; + + /** @{ */ + /** + * maxX controls the X coordinate limitation for rendering from the card. + * The driver should never receive a request for rendering beyond maxX + * in the X direction from the origin of a pixmap. + */ + int maxX; + + /** + * maxY controls the Y coordinate limitation for rendering from the card. + * The driver should never receive a request for rendering beyond maxY + * in the Y direction from the origin of a pixmap. + */ + int maxY; + /** @} */ + + /* private */ + Bool needsSync; + int lastMarker; + + /** @name Solid + * @{ + */ + /** + * PrepareSolid() sets up the driver for doing a solid fill. + * @param pPixmap Destination pixmap + * @param alu raster operation + * @param planemask write mask for the fill + * @param fg "foreground" color for the fill + * + * This call should set up the driver for doing a series of solid fills + * through the Solid() call. The alu raster op is one of the GX* + * graphics functions listed in X.h, and typically maps to a similar + * single-byte "ROP" setting in all hardware. The planemask controls + * which bits of the destination should be affected, and will only represent + * the bits up to the depth of pPixmap. The fg is the pixel value of the + * foreground color referred to in ROP descriptions. + * + * Note that many drivers will need to store some of the data in the driver + * private record, for sending to the hardware with each drawing command. + * + * The PrepareSolid() call is required of all drivers, but it may fail for any + * reason. Failure results in a fallback to software rendering. + */ + Bool (*PrepareSolid) (PixmapPtr pPixmap, + int alu, + Pixel planemask, + Pixel fg); + + /** + * Solid() performs a solid fill set up in the last PrepareSolid() call. + * + * @param pPixmap destination pixmap + * @param x1 left coordinate + * @param y1 top coordinate + * @param x2 right coordinate + * @param y2 bottom coordinate + * + * Performs the fill set up by the last PrepareSolid() call, covering the + * area from (x1,y1) to (x2,y2) in pPixmap. Note that the coordinates are + * in the coordinate space of the destination pixmap, so the driver will + * need to set up the hardware's offset and pitch for the destination + * coordinates according to the pixmap's offset and pitch within + * framebuffer. + * + * This call is required if PrepareSolid() ever succeeds. + */ + void (*Solid) (PixmapPtr pPixmap, int x1, int y1, int x2, int y2); + + /** + * DoneSolid() finishes a set of solid fills. + * + * @param pPixmap destination pixmap. + * + * The DoneSolid() call is called at the end of a series of consecutive + * Solid() calls following a successful PrepareSolid(). This allows drivers + * to finish up emitting drawing commands that were buffered, or clean up + * state from PrepareSolid(). + * + * This call is required if PrepareSolid() ever succeeds. + */ + void (*DoneSolid) (PixmapPtr pPixmap); + /** @} */ + + /** @name Copy + * @{ + */ + /** + * PrepareCopy() sets up the driver for doing a copy within video + * memory. + * + * @param pSrcPixmap source pixmap + * @param pDstPixmap destination pixmap + * @param dx X copy direction + * @param dy Y copy direction + * @param alu raster operation + * @param planemask write mask for the fill + * + * This call should set up the driver for doing a series of copies from the + * the pSrcPixmap to the pDstPixmap. The dx flag will be positive if the + * hardware should do the copy from the left to the right, and dy will be + * positive if the copy should be done from the top to the bottom. This + * is to deal with self-overlapping copies when pSrcPixmap == pDstPixmap. + * If your hardware can only support blits that are (left to right, top to + * bottom) or (right to left, bottom to top), then you should set + * #UXA_TWO_BITBLT_DIRECTIONS, and UXA will break down Copy operations to + * ones that meet those requirements. The alu raster op is one of the GX* + * graphics functions listed in X.h, and typically maps to a similar + * single-byte "ROP" setting in all hardware. The planemask controls which + * bits of the destination should be affected, and will only represent the + * bits up to the depth of pPixmap. + * + * Note that many drivers will need to store some of the data in the driver + * private record, for sending to the hardware with each drawing command. + * + * The PrepareCopy() call is required of all drivers, but it may fail for any + * reason. Failure results in a fallback to software rendering. + */ + Bool (*PrepareCopy) (PixmapPtr pSrcPixmap, + PixmapPtr pDstPixmap, + int dx, + int dy, + int alu, + Pixel planemask); + + /** + * Copy() performs a copy set up in the last PrepareCopy call. + * + * @param pDstPixmap destination pixmap + * @param srcX source X coordinate + * @param srcY source Y coordinate + * @param dstX destination X coordinate + * @param dstY destination Y coordinate + * @param width width of the rectangle to be copied + * @param height height of the rectangle to be copied. + * + * Performs the copy set up by the last PrepareCopy() call, copying the + * rectangle from (srcX, srcY) to (srcX + width, srcY + width) in the source + * pixmap to the same-sized rectangle at (dstX, dstY) in the destination + * pixmap. Those rectangles may overlap in memory, if + * pSrcPixmap == pDstPixmap. Note that this call does not receive the + * pSrcPixmap as an argument -- if it's needed in this function, it should + * be stored in the driver private during PrepareCopy(). As with Solid(), + * the coordinates are in the coordinate space of each pixmap, so the driver + * will need to set up source and destination pitches and offsets from those + * pixmaps, probably using uxaGetPixmapOffset() and uxa_get_pixmap_pitch(). + * + * This call is required if PrepareCopy ever succeeds. + */ + void (*Copy) (PixmapPtr pDstPixmap, + int srcX, + int srcY, + int dstX, + int dstY, + int width, + int height); + + /** + * DoneCopy() finishes a set of copies. + * + * @param pPixmap destination pixmap. + * + * The DoneCopy() call is called at the end of a series of consecutive + * Copy() calls following a successful PrepareCopy(). This allows drivers + * to finish up emitting drawing commands that were buffered, or clean up + * state from PrepareCopy(). + * + * This call is required if PrepareCopy() ever succeeds. + */ + void (*DoneCopy) (PixmapPtr pDstPixmap); + /** @} */ + + /** @name Composite + * @{ + */ + /** + * CheckComposite() checks to see if a composite operation could be + * accelerated. + * + * @param op Render operation + * @param pSrcPicture source Picture + * @param pMaskPicture mask picture + * @param pDstPicture destination Picture + * + * The CheckComposite() call checks if the driver could handle acceleration + * of op with the given source, mask, and destination pictures. This allows + * drivers to check source and destination formats, supported operations, + * transformations, and component alpha state, and send operations it can't + * support to software rendering early on. This avoids costly pixmap + * migration to the wrong places when the driver can't accelerate + * operations. Note that because migration hasn't happened, the driver + * can't know during CheckComposite() what the offsets and pitches of the + * pixmaps are going to be. + * + * See PrepareComposite() for more details on likely issues that drivers + * will have in accelerating Composite operations. + * + * The CheckComposite() call is recommended if PrepareComposite() is + * implemented, but is not required. + */ + Bool (*CheckComposite) (int op, + PicturePtr pSrcPicture, + PicturePtr pMaskPicture, + PicturePtr pDstPicture); + + /** + * PrepareComposite() sets up the driver for doing a Composite operation + * described in the Render extension protocol spec. + * + * @param op Render operation + * @param pSrcPicture source Picture + * @param pMaskPicture mask picture + * @param pDstPicture destination Picture + * @param pSrc source pixmap + * @param pMask mask pixmap + * @param pDst destination pixmap + * + * This call should set up the driver for doing a series of Composite + * operations, as described in the Render protocol spec, with the given + * pSrcPicture, pMaskPicture, and pDstPicture. The pSrc, pMask, and + * pDst are the pixmaps containing the pixel data, and should be used for + * setting the offset and pitch used for the coordinate spaces for each of + * the Pictures. + * + * Notes on interpreting Picture structures: + * - The Picture structures will always have a valid pDrawable. + * - The Picture structures will never have alphaMap set. + * - The mask Picture (and therefore pMask) may be NULL, in which case the + * operation is simply src OP dst instead of src IN mask OP dst, and + * mask coordinates should be ignored. + * - pMarkPicture may have componentAlpha set, which greatly changes + * the behavior of the Composite operation. componentAlpha has no effect + * when set on pSrcPicture or pDstPicture. + * - The source and mask Pictures may have a transformation set + * (Picture->transform != NULL), which means that the source coordinates + * should be transformed by that transformation, resulting in scaling, + * rotation, etc. The PictureTransformPoint() call can transform + * coordinates for you. Transforms have no effect on Pictures when used + * as a destination. + * - The source and mask pictures may have a filter set. PictFilterNearest + * and PictFilterBilinear are defined in the Render protocol, but others + * may be encountered, and must be handled correctly (usually by + * PrepareComposite failing, and falling back to software). Filters have + * no effect on Pictures when used as a destination. + * - The source and mask Pictures may have repeating set, which must be + * respected. Many chipsets will be unable to support repeating on + * pixmaps that have a width or height that is not a power of two. + * + * If your hardware can't support source pictures (textures) with + * non-power-of-two pitches, you should set #UXA_OFFSCREEN_ALIGN_POT. + * + * Note that many drivers will need to store some of the data in the driver + * private record, for sending to the hardware with each drawing command. + * + * The PrepareComposite() call is not required. However, it is highly + * recommended for performance of antialiased font rendering and performance + * of cairo applications. Failure results in a fallback to software + * rendering. + */ + Bool (*PrepareComposite) (int op, + PicturePtr pSrcPicture, + PicturePtr pMaskPicture, + PicturePtr pDstPicture, + PixmapPtr pSrc, + PixmapPtr pMask, + PixmapPtr pDst); + + /** + * Composite() performs a Composite operation set up in the last + * PrepareComposite() call. + * + * @param pDstPixmap destination pixmap + * @param srcX source X coordinate + * @param srcY source Y coordinate + * @param maskX source X coordinate + * @param maskY source Y coordinate + * @param dstX destination X coordinate + * @param dstY destination Y coordinate + * @param width destination rectangle width + * @param height destination rectangle height + * + * Performs the Composite operation set up by the last PrepareComposite() + * call, to the rectangle from (dstX, dstY) to (dstX + width, dstY + height) + * in the destination Pixmap. Note that if a transformation was set on + * the source or mask Pictures, the source rectangles may not be the same + * size as the destination rectangles and filtering. Getting the coordinate + * transformation right at the subpixel level can be tricky, and rendercheck + * can test this for you. + * + * This call is required if PrepareComposite() ever succeeds. + */ + void (*Composite) (PixmapPtr pDst, + int srcX, + int srcY, + int maskX, + int maskY, + int dstX, + int dstY, + int width, + int height); + + /** + * DoneComposite() finishes a set of Composite operations. + * + * @param pPixmap destination pixmap. + * + * The DoneComposite() call is called at the end of a series of consecutive + * Composite() calls following a successful PrepareComposite(). This allows + * drivers to finish up emitting drawing commands that were buffered, or + * clean up state from PrepareComposite(). + * + * This call is required if PrepareComposite() ever succeeds. + */ + void (*DoneComposite) (PixmapPtr pDst); + /** @} */ + + /** + * UploadToScreen() loads a rectangle of data from src into pDst. + * + * @param pDst destination pixmap + * @param x destination X coordinate. + * @param y destination Y coordinate + * @param width width of the rectangle to be copied + * @param height height of the rectangle to be copied + * @param src pointer to the beginning of the source data + * @param src_pitch pitch (in bytes) of the lines of source data. + * + * UploadToScreen() copies data in system memory beginning at src (with + * pitch src_pitch) into the destination pixmap from (x, y) to + * (x + width, y + height). This is typically done with hostdata uploads, + * where the CPU sets up a blit command on the hardware with instructions + * that the blit data will be fed through some sort of aperture on the card. + * + * If UploadToScreen() is performed asynchronously, it is up to the driver + * to call uxa_mark_sync(). This is in contrast to most other acceleration + * calls in UXA. + * + * UploadToScreen() can aid in pixmap migration, but is most important for + * the performance of uxa_glyphs() (antialiased font drawing) by allowing + * pipelining of data uploads, avoiding a sync of the card after each glyph. + * + * @return TRUE if the driver successfully uploaded the data. FALSE + * indicates that UXA should fall back to doing the upload in software. + * + * UploadToScreen() is not required, but is recommended if Composite + * acceleration is supported. + */ + Bool (*UploadToScreen) (PixmapPtr pDst, + int x, + int y, + int w, + int h, + char *src, + int src_pitch); + + /** + * UploadToScratch() is used to upload a pixmap to a scratch area for + * acceleration. + * + * @param pSrc source pixmap in host memory + * @param pDst fake, scratch pixmap to be set up in offscreen memory. + * + * The UploadToScratch() call was added to support Xati before Xati had + * support for hostdata uploads and before uxa_glyphs() was written. It + * behaves incorrectly (uses an invalid pixmap as pDst), + * and UploadToScreen() should be implemented instead. + * + * Drivers implementing UploadToScratch() had to set up space (likely in a + * statically allocated area) in offscreen memory, copy pSrc to that + * scratch area, and adust pDst->devKind for the pitch and + * pDst->devPrivate.ptr for the pointer to that scratch area. The driver + * was responsible for syncing (as it was implemented using memcpy() in + * Xati), and only the data from the last UploadToScratch() was guaranteed + * to be valid at any given time. + * + * UploadToScratch() should not be implemented by drivers, and will likely + * be removed in a future version of UXA. + */ + Bool (*UploadToScratch) (PixmapPtr pSrc, + PixmapPtr pDst); + + /** + * DownloadFromScreen() loads a rectangle of data from pSrc into dst + * + * @param pSrc source pixmap + * @param x source X coordinate. + * @param y source Y coordinate + * @param width width of the rectangle to be copied + * @param height height of the rectangle to be copied + * @param dst pointer to the beginning of the destination data + * @param dst_pitch pitch (in bytes) of the lines of destination data. + * + * DownloadFromScreen() copies data from offscreen memory in pSrc from + * (x, y) to (x + width, y + height), to system memory starting at + * dst (with pitch dst_pitch). This would usually be done + * using scatter-gather DMA, supported by a DRM call, or by blitting to AGP + * and then synchronously reading from AGP. Because the implementation + * might be synchronous, UXA leaves it up to the driver to call + * uxa_mark_sync() if DownloadFromScreen() was asynchronous. This is in + * contrast to most other acceleration calls in UXA. + * + * DownloadFromScreen() can aid in the largest bottleneck in pixmap + * migration, which is the read from framebuffer when evicting pixmaps from + * framebuffer memory. Thus, it is highly recommended, even though + * implementations are typically complicated. + * + * @return TRUE if the driver successfully downloaded the data. FALSE + * indicates that UXA should fall back to doing the download in software. + * + * DownloadFromScreen() is not required, but is highly recommended. + */ + Bool (*DownloadFromScreen)(PixmapPtr pSrc, + int x, int y, + int w, int h, + char *dst, int dst_pitch); + + /** + * MarkSync() requests that the driver mark a synchronization point, + * returning an driver-defined integer marker which could be requested for + * synchronization to later in WaitMarker(). This might be used in the + * future to avoid waiting for full hardware stalls before accessing pixmap + * data with the CPU, but is not important in the current incarnation of + * UXA. + * + * Note that drivers should call uxa_mark_sync() when they have done some + * acceleration, rather than their own MarkSync() handler, as otherwise UXA + * will be unaware of the driver's acceleration and not sync to it during + * fallbacks. + * + * MarkSync() is optional. + */ + int (*MarkSync) (ScreenPtr pScreen); + + /** + * WaitMarker() waits for all rendering before the given marker to have + * completed. If the driver does not implement MarkSync(), marker is + * meaningless, and all rendering by the hardware should be completed before + * WaitMarker() returns. + * + * Note that drivers should call uxa_wait_sync() to wait for all acceleration + * to finish, as otherwise UXA will be unaware of the driver having + * synchronized, resulting in excessive WaitMarker() calls. + * + * WaitMarker() is required of all drivers. + */ + void (*WaitMarker) (ScreenPtr pScreen, int marker); + + /** @{ */ + /** + * PrepareAccess() is called before CPU access to an offscreen pixmap. + * + * @param pPix the pixmap being accessed + * @param index the index of the pixmap being accessed. + * + * PrepareAccess() will be called before CPU access to an offscreen pixmap. + * This can be used to set up hardware surfaces for byteswapping or + * untiling, or to adjust the pixmap's devPrivate.ptr for the purpose of + * making CPU access use a different aperture. + * + * The index is one of #UXA_PREPARE_DEST, #UXA_PREPARE_SRC, or + * #UXA_PREPARE_MASK, indicating which pixmap is in question. Since only up + * to three pixmaps will have PrepareAccess() called on them per operation, + * drivers can have a small, statically-allocated space to maintain state + * for PrepareAccess() and FinishAccess() in. Note that the same pixmap may + * have PrepareAccess() called on it more than once, for uxample when doing + * a copy within the same pixmap (so it gets PrepareAccess as() + * #UXA_PREPARE_DEST and then as #UXA_PREPARE_SRC). + * + * PrepareAccess() may fail. An uxample might be the case of hardware that + * can set up 1 or 2 surfaces for CPU access, but not 3. If PrepareAccess() + * fails, UXA will migrate the pixmap to system memory. + * DownloadFromScreen() must be implemented and must not fail if a driver + * wishes to fail in PrepareAccess(). PrepareAccess() must not fail when + * pPix is the visible screen, because the visible screen can not be + * migrated. + * + * @return TRUE if PrepareAccess() successfully prepared the pixmap for CPU + * drawing. + * @return FALSE if PrepareAccess() is unsuccessful and UXA should use + * DownloadFromScreen() to migate the pixmap out. + */ + Bool (*PrepareAccess)(PixmapPtr pPix, int index); + + /** + * FinishAccess() is called after CPU access to an offscreen pixmap. + * + * @param pPix the pixmap being accessed + * @param index the index of the pixmap being accessed. + * + * FinishAccess() will be called after finishing CPU access of an offscreen + * pixmap set up by PrepareAccess(). Note that the FinishAccess() will not be + * called if PrepareAccess() failed and the pixmap was migrated out. + */ + void (*FinishAccess)(PixmapPtr pPix, int index); + + /** + * PixmapIsOffscreen() is an optional driver replacement to + * uxa_pixmap_is_offscreen(). Set to NULL if you want the standard behaviour + * of uxa_pixmap_is_offscreen(). + * + * @param pPix the pixmap + * @return TRUE if the given drawable is in framebuffer memory. + * + * uxa_pixmap_is_offscreen() is used to determine if a pixmap is in offscreen + * memory, meaning that acceleration could probably be done to it, and that it + * will need to be wrapped by PrepareAccess()/FinishAccess() when accessing it + * with the CPU. + * + * + */ + Bool (*PixmapIsOffscreen)(PixmapPtr pPix); + + /** @name PrepareAccess() and FinishAccess() indices + * @{ + */ + /** + * UXA_PREPARE_DEST is the index for a pixmap that may be drawn to or + * read from. + */ + #define UXA_PREPARE_DEST 0 + /** + * UXA_PREPARE_SRC is the index for a pixmap that may be read from + */ + #define UXA_PREPARE_SRC 1 + /** + * UXA_PREPARE_SRC is the index for a second pixmap that may be read + * from. + */ + #define UXA_PREPARE_MASK 2 + /** @} */ + + /** + * maxPitchPixels controls the pitch limitation for rendering from + * the card. + * The driver should never receive a request for rendering a pixmap + * that has a pitch (in pixels) beyond maxPitchPixels. + * + * Setting this field is optional -- if your hardware doesn't have + * a pitch limitation in pixels, don't set this. If neither this value + * nor maxPitchBytes is set, then maxPitchPixels is set to maxX. + * If set, it must not be smaller than maxX. + * + * @sa maxPitchBytes + */ + int maxPitchPixels; + + /** + * maxPitchBytes controls the pitch limitation for rendering from + * the card. + * The driver should never receive a request for rendering a pixmap + * that has a pitch (in bytes) beyond maxPitchBytes. + * + * Setting this field is optional -- if your hardware doesn't have + * a pitch limitation in bytes, don't set this. + * If set, it must not be smaller than maxX * 4. + * There's no default value for maxPitchBytes. + * + * @sa maxPitchPixels + */ + int maxPitchBytes; + + /** @} */ +} uxa_driver_t; + +/** @name UXA driver flags + * @{ + */ +/** + * UXA_TWO_BITBLT_DIRECTIONS indicates to UXA that the driver can only + * support copies that are (left-to-right, top-to-bottom) or + * (right-to-left, bottom-to-top). + */ +#define UXA_TWO_BITBLT_DIRECTIONS (1 << 2) + +/** @} */ + +uxa_driver_t * +uxa_driver_alloc(void); + +Bool +uxa_driver_init(ScreenPtr screen, uxa_driver_t *uxa_driver); + +void +uxa_driver_fini(ScreenPtr pScreen); + +void +uxa_mark_sync(ScreenPtr pScreen); + +void +uxa_wait_sync(ScreenPtr pScreen); + +void +uxaEnableDisableFBAccess (int index, Bool enable); + +CARD32 +uxa_get_pixmap_first_pixel (PixmapPtr pPixmap); + +/** + * Returns TRUE if the given planemask covers all the significant bits in the + * pixel values for pDrawable. + */ +#define UXA_PM_IS_SOLID(_pDrawable, _pm) \ + (((_pm) & FbFullMask((_pDrawable)->depth)) == \ + FbFullMask((_pDrawable)->depth)) + +#endif /* UXA_H */ |