diff options
author | Søren Sandmann <ssp@redhat.com> | 2010-06-02 10:13:56 -0400 |
---|---|---|
committer | Søren Sandmann Pedersen <ssp@redhat.com> | 2010-09-14 06:32:52 -0400 |
commit | 65d5ac5a0eecbf0ee3abdb6998ce5e615e05bb4e (patch) | |
tree | e004657c913933cfe4115d2d481f2ffbafae45b0 | |
parent | e691e979b476f1ff9c6c93e12f6cea02dcb8c841 (diff) |
Add a copy of UXA, but don't use it yet
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/uxa/Makefile.am | 17 | ||||
-rw-r--r-- | src/uxa/uxa-accel.c | 1257 | ||||
-rw-r--r-- | src/uxa/uxa-glyphs.c | 1172 | ||||
-rw-r--r-- | src/uxa/uxa-priv.h | 426 | ||||
-rw-r--r-- | src/uxa/uxa-render.c | 1957 | ||||
-rw-r--r-- | src/uxa/uxa-unaccel.c | 430 | ||||
-rw-r--r-- | src/uxa/uxa.c | 579 | ||||
-rw-r--r-- | src/uxa/uxa.h | 583 |
10 files changed, 6424 insertions, 0 deletions
diff --git a/configure.ac b/configure.ac index 844001e..e37a84d 100644 --- a/configure.ac +++ b/configure.ac @@ -82,5 +82,6 @@ AC_SUBST([DRIVER_NAME]) AC_CONFIG_FILES([ Makefile src/Makefile + src/uxa/Makefile ]) AC_OUTPUT diff --git a/src/Makefile.am b/src/Makefile.am index 14b68ec..fd1ba81 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -25,6 +25,8 @@ # _ladir passes a dummy rpath to libtool so the thing will actually link # TODO: -nostdlib/-Bstatic/-lgcc platform magic, not installing the .a, etc. +SUBDIRS=uxa + AM_CFLAGS = $(XORG_CFLAGS) $(PCIACCESS_CFLAGS) $(CWARNFLAGS) qxl_drv_la_LTLIBRARIES = qxl_drv.la diff --git a/src/uxa/Makefile.am b/src/uxa/Makefile.am new file mode 100644 index 0000000..c875b63 --- /dev/null +++ b/src/uxa/Makefile.am @@ -0,0 +1,17 @@ +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="" + +AM_CFLAGS = $(CWARNFLAGS) $(XORG_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/src/uxa/uxa-accel.c b/src/uxa/uxa-accel.c new file mode 100644 index 0000000..e4dbfcc --- /dev/null +++ b/src/uxa/uxa-accel.c @@ -0,0 +1,1257 @@ +/* + * 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" +#include "mipict.h" + +static CARD32 +format_for_depth(int depth) +{ + switch (depth) { + case 1: return PICT_a1; + case 4: return PICT_a4; + case 8: return PICT_a8; + case 15: return PICT_x1r5g5b5; + case 16: return PICT_r5g6b5; + default: + case 24: return PICT_x8r8g8b8; + case 32: return PICT_a8r8g8b8; + } +} + +static void +uxa_fill_spans(DrawablePtr pDrawable, GCPtr pGC, int n, + DDXPointPtr ppt, int *pwidth, int fSorted) +{ + ScreenPtr screen = pDrawable->pScreen; + uxa_screen_t *uxa_screen = uxa_get_screen(screen); + RegionPtr pClip = fbGetCompositeClip(pGC); + PixmapPtr dst_pixmap, src_pixmap = NULL; + BoxPtr pextent, pbox; + int nbox; + int extentX1, extentX2, extentY1, extentY2; + int fullX1, fullX2, fullY1; + int partX1, partX2; + int off_x, off_y; + xRenderColor color; + PictFormatPtr format; + PicturePtr dst, src; + int error; + + if (uxa_screen->swappedOut || uxa_screen->force_fallback) + goto fallback; + + if (pGC->fillStyle != FillSolid) + goto fallback; + + dst_pixmap = uxa_get_offscreen_pixmap(pDrawable, &off_x, &off_y); + if (!dst_pixmap) + goto fallback; + + if (pGC->alu != GXcopy || pGC->planemask != FB_ALLONES) + goto solid; + + format = PictureMatchFormat(screen, + dst_pixmap->drawable.depth, + format_for_depth(dst_pixmap->drawable.depth)); + dst = CreatePicture(0, &dst_pixmap->drawable, format, 0, 0, serverClient, &error); + if (!dst) + goto solid; + + ValidatePicture(dst); + + uxa_get_rgba_from_pixel(pGC->fgPixel, + &color.red, + &color.green, + &color.blue, + &color.alpha, + format_for_depth(dst_pixmap->drawable.depth)); + src = CreateSolidPicture(0, &color, &error); + if (!src) { + FreePicture(dst, 0); + goto solid; + } + + if (!uxa_screen->info->check_composite(PictOpSrc, src, NULL, dst, 0, 0)) { + FreePicture(src, 0); + FreePicture(dst, 0); + goto solid; + } + + if (!uxa_screen->info->check_composite_texture || + !uxa_screen->info->check_composite_texture(screen, src)) { + PicturePtr solid; + int src_off_x, src_off_y; + + solid = uxa_acquire_solid(screen, src->pSourcePict); + FreePicture(src, 0); + + src = solid; + src_pixmap = uxa_get_offscreen_pixmap(src->pDrawable, + &src_off_x, &src_off_y); + if (!src_pixmap) { + FreePicture(src, 0); + FreePicture(dst, 0); + goto solid; + } + } + + if (!uxa_screen->info->prepare_composite(PictOpSrc, src, NULL, dst, src_pixmap, NULL, dst_pixmap)) { + FreePicture(src, 0); + FreePicture(dst, 0); + goto solid; + } + + pextent = REGION_EXTENTS(pGC->screen, 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->composite(dst_pixmap, + 0, 0, 0, 0, + fullX1 + off_x, + fullY1 + off_y, + fullX2 - fullX1, 1); + } else { + pbox = REGION_RECTS(pClip); + while (nbox--) { + if (pbox->y1 > fullY1) + break; + + if (pbox->y1 <= fullY1) { + partX1 = pbox->x1; + if (partX1 < fullX1) + partX1 = fullX1; + + partX2 = pbox->x2; + if (partX2 > fullX2) + partX2 = fullX2; + + if (partX2 > partX1) { + uxa_screen->info->composite(dst_pixmap, + 0, 0, 0, 0, + partX1 + off_x, + fullY1 + off_y, + partX2 - partX1, 1); + } + } + pbox++; + } + } + } + + uxa_screen->info->done_composite(dst_pixmap); + FreePicture(src, 0); + FreePicture(dst, 0); + return; + +solid: + if (uxa_screen->info->check_solid && + !uxa_screen->info->check_solid(pDrawable, pGC->alu, pGC->planemask)) + goto fallback; + + if (!(*uxa_screen->info->prepare_solid) (dst_pixmap, + pGC->alu, + pGC->planemask, + pGC->fgPixel)) + goto fallback; + + pextent = REGION_EXTENTS(pGC->screen, 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) (dst_pixmap, + 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) (dst_pixmap, + partX1 + off_x, + fullY1 + off_y, + partX2 + off_x, + fullY1 + 1 + off_y); + } + } + pbox++; + } + } + } + (*uxa_screen->info->done_solid) (dst_pixmap); + + return; + +fallback: + uxa_check_fill_spans(pDrawable, pGC, n, ppt, pwidth, fSorted); +} + +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; + RegionPtr pClip; + BoxPtr pbox; + int nbox; + int xoff, yoff; + int bpp = pDrawable->bitsPerPixel; + + /* Don't bother with under 8bpp, XYPixmaps. */ + if (format != ZPixmap || bpp < 8) + return FALSE; + + if (uxa_screen->swappedOut || uxa_screen->force_fallback) + return FALSE; + + if (!uxa_screen->info->put_image) + return FALSE; + + /* Only accelerate copies: no rop or planemask. */ + if (!UXA_PM_IS_SOLID(pDrawable, pGC->planemask) || pGC->alu != GXcopy) + return FALSE; + + pPix = uxa_get_offscreen_pixmap(pDrawable, &xoff, &yoff); + if (!pPix) + 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->put_image(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 (!uxa_prepare_access(pDrawable, UXA_ACCESS_RW)) + return FALSE; + + 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); + + uxa_finish_access(pDrawable); + } + } + + + return TRUE; +} + +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) +{ + if (!uxa_do_put_image(pDrawable, pGC, depth, x, y, w, h, format, bits, + PixmapBytePad(w, pDrawable->depth))) { + 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->done_copy(pDstPixmap); + dirsetup = -1; + if (!(*uxa_screen->info->prepare_copy) + (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->done_copy(pDstPixmap); + dirsetup = 1; + if (!(*uxa_screen->info->prepare_copy) + (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->done_copy(pDstPixmap); + dirsetup = 1; + if (!(*uxa_screen->info->prepare_copy) + (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->done_copy(pDstPixmap); + dirsetup = -1; + if (!(*uxa_screen->info->prepare_copy) + (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->done_copy(pDstPixmap); + 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) +{ + ScreenPtr screen = pDstDrawable->pScreen; + uxa_screen_t *uxa_screen = uxa_get_screen(screen); + 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); + if (!pSrcPixmap || !pDstPixmap) + goto fallback; + + if (uxa_screen->info->check_copy && + !uxa_screen->info->check_copy(pSrcPixmap, pDstPixmap, + pGC ? pGC->alu : GXcopy, + pGC ? pGC->planemask : FB_ALLONES)) + goto fallback; + + 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(pDstPixmap)) + goto fallback; + + if (uxa_pixmap_is_offscreen(pSrcPixmap)) { + if (!(*uxa_screen->info->prepare_copy) (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->done_copy) (pDstPixmap); + } else { + int stride, bpp; + char *src; + + if (!uxa_screen->info->put_image) + goto fallback; + + /* Don't bother with under 8bpp, XYPixmaps. */ + bpp = pSrcPixmap->drawable.bitsPerPixel; + if (bpp != pDstDrawable->bitsPerPixel || bpp < 8) + goto fallback; + + /* Only accelerate copies: no rop or planemask. */ + if (pGC && (!UXA_PM_IS_SOLID(pSrcDrawable, pGC->planemask) || pGC->alu != GXcopy)) + goto fallback; + + src = pSrcPixmap->devPrivate.ptr; + stride = pSrcPixmap->devKind; + bpp /= 8; + while (nbox--) { + if (!uxa_screen->info->put_image(pDstPixmap, + pbox->x1 + dst_off_x, + pbox->y1 + dst_off_y, + pbox->x2 - pbox->x1, + pbox->y2 - pbox->y1, + (char *) src + + (pbox->y1 + dy + src_off_y) * stride + + (pbox->x1 + dx + src_off_x) * bpp, + stride)) + goto fallback; + + pbox++; + } + } + + return; + +fallback: + UXA_FALLBACK(("from %p to %p (%c,%c)\n", pSrcDrawable, pDstDrawable, + uxa_drawable_location(pSrcDrawable), + uxa_drawable_location(pDstDrawable))); + if (uxa_prepare_access(pDstDrawable, UXA_ACCESS_RW)) { + if (uxa_prepare_access(pSrcDrawable, UXA_ACCESS_RO)) { + fbCopyNtoN(pSrcDrawable, pDstDrawable, pGC, pbox, nbox, + dx, dy, reverse, upsidedown, bitplane, + closure); + uxa_finish_access(pSrcDrawable); + } + uxa_finish_access(pDstDrawable); + } +} + +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 || uxa_screen->force_fallback) { + return uxa_check_copy_area(pSrcDrawable, pDstDrawable, pGC, + srcx, srcy, width, height, dstx, + dsty); + } + + return miDoCopy(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); + if (!prect) + return; + 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)); + if (!prect) + return; + 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); + if (!prect) + return; + 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; + 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; + + if (uxa_screen->swappedOut || uxa_screen->force_fallback) + goto fallback; + + pPixmap = uxa_get_offscreen_pixmap (pDrawable, &xoff, &yoff); + if (!pPixmap) + 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_screen->info->check_solid && + !uxa_screen->info->check_solid(pDrawable, pGC->alu, pGC->planemask)) { + goto fallback; + } + + if (!(*uxa_screen->info->prepare_solid) (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->done_solid) (pPixmap); + +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 + + miCopyRegion(&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) +{ + ScreenPtr screen = pDrawable->pScreen; + uxa_screen_t *uxa_screen = uxa_get_screen(screen); + PixmapPtr pixmap; + int xoff, yoff; + int nbox; + BoxPtr pBox, extents; + Bool ret = FALSE; + + pixmap = uxa_get_offscreen_pixmap(pDrawable, &xoff, &yoff); + if (!pixmap) + return FALSE; + + REGION_TRANSLATE(screen, pRegion, xoff, yoff); + + nbox = REGION_NUM_RECTS(pRegion); + pBox = REGION_RECTS(pRegion); + extents = REGION_EXTENTS(screen, pRegion); + + /* Using GEM, the relocation costs outweigh the advantages of the blitter */ + if (nbox == 1 || (alu != GXcopy && alu != GXclear) || planemask != FB_ALLONES) { +try_solid: + if (uxa_screen->info->check_solid && + !uxa_screen->info->check_solid(&pixmap->drawable, alu, planemask)) + goto err; + + if (!uxa_screen->info->prepare_solid(pixmap, alu, planemask, pixel)) + goto err; + + while (nbox--) { + uxa_screen->info->solid(pixmap, + pBox->x1, pBox->y1, + pBox->x2, pBox->y2); + pBox++; + } + + uxa_screen->info->done_solid(pixmap); + } else { + PicturePtr dst, src; + PixmapPtr src_pixmap = NULL; + xRenderColor color; + int error; + + dst = CreatePicture(0, &pixmap->drawable, + PictureMatchFormat(screen, + pixmap->drawable.depth, + format_for_depth(pixmap->drawable.depth)), + 0, 0, serverClient, &error); + if (!dst) + goto err; + + ValidatePicture(dst); + + uxa_get_rgba_from_pixel(pixel, + &color.red, + &color.green, + &color.blue, + &color.alpha, + format_for_depth(pixmap->drawable.depth)); + src = CreateSolidPicture(0, &color, &error); + if (!src) { + FreePicture(dst, 0); + goto err; + } + + if (!uxa_screen->info->check_composite(PictOpSrc, src, NULL, dst, + extents->x2 - extents->x1, + extents->y2 - extents->y1)) { + FreePicture(src, 0); + FreePicture(dst, 0); + goto try_solid; + } + + if (!uxa_screen->info->check_composite_texture || + !uxa_screen->info->check_composite_texture(screen, src)) { + PicturePtr solid; + int src_off_x, src_off_y; + + solid = uxa_acquire_solid(screen, src->pSourcePict); + FreePicture(src, 0); + + src = solid; + src_pixmap = uxa_get_offscreen_pixmap(src->pDrawable, + &src_off_x, &src_off_y); + if (!src_pixmap) { + FreePicture(src, 0); + FreePicture(dst, 0); + goto err; + } + } + + if (!uxa_screen->info->prepare_composite(PictOpSrc, src, NULL, dst, src_pixmap, NULL, pixmap)) { + FreePicture(src, 0); + FreePicture(dst, 0); + goto err; + } + + while (nbox--) { + uxa_screen->info->composite(pixmap, + 0, 0, 0, 0, + pBox->x1, + pBox->y1, + pBox->x2 - pBox->x1, + pBox->y2 - pBox->y1); + pBox++; + } + + uxa_screen->info->done_composite(pixmap); + FreePicture(src, 0); + FreePicture(dst, 0); + } + + ret = TRUE; + +err: + REGION_TRANSLATE(screen, 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_offscreen_pixmap(pDrawable, &xoff, &yoff); + if (!pPixmap || !uxa_pixmap_is_offscreen(pTile)) + goto out; + + if (uxa_screen->info->check_copy && + !uxa_screen->info->check_copy(pTile, pPixmap, alu, planemask)) + return FALSE; + + REGION_TRANSLATE(pScreen, pRegion, xoff, yoff); + + if ((*uxa_screen->info->prepare_copy) (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->done_copy) (pPixmap); + + 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) +{ + ScreenPtr screen = pDrawable->pScreen; + uxa_screen_t *uxa_screen = uxa_get_screen(screen); + 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 || uxa_screen->force_fallback) + goto fallback; + + pPix = uxa_get_offscreen_pixmap(pDrawable, &xoff, &yoff); + + if (pPix == NULL || uxa_screen->info->get_image == 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->get_image(pPix, pDrawable->x + x + xoff, + pDrawable->y + y + yoff, w, h, d, + PixmapBytePad(w, pDrawable->depth)); + if (ok) + return; + +fallback: + UXA_FALLBACK(("from %p (%c)\n", pDrawable, + uxa_drawable_location(pDrawable))); + + if (uxa_prepare_access(pDrawable, UXA_ACCESS_RO)) { + fbGetImage(pDrawable, x, y, w, h, format, planeMask, d); + uxa_finish_access(pDrawable); + } + + return; +} diff --git a/src/uxa/uxa-glyphs.c b/src/uxa/uxa-glyphs.c new file mode 100644 index 0000000..3ff504c --- /dev/null +++ b/src/uxa/uxa-glyphs.c @@ -0,0 +1,1172 @@ +/* + * Copyright © 2010 Intel Corporation + * Partly based on code 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 Intel not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. Intel makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL INTEL + * 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 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: Chris Wilson <chris@chris-wilson.co.uk> + * Based on code by: Keith Packard <keithp@keithp.com> and Owen Taylor <otaylor@fishsoup.net> + */ + +#ifdef HAVE_DIX_CONFIG_H +#include <dix-config.h> +#endif + +#include <stdlib.h> + +#include "uxa-priv.h" + +#include "mipict.h" + +/* 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_SIZE 1024 +#define GLYPH_MIN_SIZE 8 +#define GLYPH_MAX_SIZE 64 +#define GLYPH_CACHE_SIZE (CACHE_PICTURE_SIZE * CACHE_PICTURE_SIZE / (GLYPH_MIN_SIZE * GLYPH_MIN_SIZE)) + +struct uxa_glyph { + uxa_glyph_cache_t *cache; + uint16_t x, y; + uint16_t size, pos; +}; + +static int uxa_glyph_index; + +static inline struct uxa_glyph *uxa_glyph_get_private(GlyphPtr glyph) +{ + return dixLookupPrivate(&glyph->devPrivates, &uxa_glyph_index); +} + +static inline void uxa_glyph_set_private(GlyphPtr glyph, struct uxa_glyph *priv) +{ + dixSetPrivate(&glyph->devPrivates, &uxa_glyph_index, priv); +} + +#define NeedsComponent(f) (PICT_FORMAT_A(f) != 0 && PICT_FORMAT_RGB(f) != 0) + +static void uxa_unrealize_glyph_caches(ScreenPtr pScreen) +{ + uxa_screen_t *uxa_screen = uxa_get_screen(pScreen); + int i; + + for (i = 0; i < UXA_NUM_GLYPH_CACHE_FORMATS; i++) { + uxa_glyph_cache_t *cache = &uxa_screen->glyphCaches[i]; + + if (cache->picture) + FreePicture(cache->picture, 0); + + if (cache->glyphs) + xfree(cache->glyphs); + } +} + +void uxa_glyphs_fini(ScreenPtr pScreen) +{ + uxa_unrealize_glyph_caches(pScreen); +} + +/* 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) +{ + uxa_screen_t *uxa_screen = uxa_get_screen(pScreen); + unsigned int formats[] = { + PIXMAN_a8, + PIXMAN_a8r8g8b8, + }; + int i; + + memset(uxa_screen->glyphCaches, 0, sizeof(uxa_screen->glyphCaches)); + + for (i = 0; i < sizeof(formats)/sizeof(formats[0]); i++) { + uxa_glyph_cache_t *cache = &uxa_screen->glyphCaches[i]; + PixmapPtr pixmap; + PicturePtr picture; + CARD32 component_alpha; + int depth = PIXMAN_FORMAT_DEPTH(formats[i]); + int error; + PictFormatPtr pPictFormat = PictureMatchFormat(pScreen, depth, formats[i]); + if (!pPictFormat) + goto bail; + + /* Now allocate the pixmap and picture */ + pixmap = pScreen->CreatePixmap(pScreen, + CACHE_PICTURE_SIZE, CACHE_PICTURE_SIZE, depth, + 0 /* INTEL_CREATE_PIXMAP_TILING_X -- FIXME */); + if (!pixmap) + goto bail; + assert (uxa_pixmap_is_offscreen(pixmap)); + + component_alpha = NeedsComponent(pPictFormat->format); + picture = CreatePicture(0, &pixmap->drawable, pPictFormat, + CPComponentAlpha, &component_alpha, + serverClient, &error); + + pScreen->DestroyPixmap(pixmap); + + if (!picture) + goto bail; + + ValidatePicture(picture); + + cache->picture = picture; + cache->glyphs = xcalloc(sizeof(GlyphPtr), GLYPH_CACHE_SIZE); + if (!cache->glyphs) + goto bail; + + cache->evict = rand() % GLYPH_CACHE_SIZE; + } + assert(i == UXA_NUM_GLYPH_CACHE_FORMATS); + + return TRUE; + +bail: + uxa_unrealize_glyph_caches(pScreen); + return FALSE; +} + + +Bool uxa_glyphs_init(ScreenPtr pScreen) +{ + if (!dixRequestPrivate(&uxa_glyph_index, 0)) + return FALSE; + + if (!uxa_realize_glyph_caches(pScreen)) + return FALSE; + + return TRUE; +} + +/* The most efficient thing to way to upload the glyph to the screen + * is to use CopyArea; uxa pixmaps are always offscreen. + */ +static void +uxa_glyph_cache_upload_glyph(ScreenPtr screen, + uxa_glyph_cache_t * cache, + GlyphPtr glyph, + int x, int y) +{ + PicturePtr pGlyphPicture = GlyphPicture(glyph)[screen->myNum]; + PixmapPtr pGlyphPixmap = (PixmapPtr) pGlyphPicture->pDrawable; + PixmapPtr pCachePixmap = (PixmapPtr) cache->picture->pDrawable; + PixmapPtr scratch; + GCPtr gc; + + gc = GetScratchGC(pCachePixmap->drawable.depth, screen); + if (!gc) + return; + + ValidateGC(&pCachePixmap->drawable, gc); + + scratch = pGlyphPixmap; + /* Create a temporary bo to stream the updates to the cache */ + if (pGlyphPixmap->drawable.depth != pCachePixmap->drawable.depth || + !uxa_pixmap_is_offscreen(scratch)) { + scratch = screen->CreatePixmap(screen, + glyph->info.width, + glyph->info.height, + pCachePixmap->drawable.depth, + UXA_CREATE_PIXMAP_FOR_MAP); + if (scratch) { + if (pGlyphPixmap->drawable.depth != pCachePixmap->drawable.depth) { + PicturePtr picture; + int error; + + picture = CreatePicture(0, &scratch->drawable, + PictureMatchFormat(screen, + pCachePixmap->drawable.depth, + cache->picture->format), + 0, NULL, + serverClient, &error); + if (picture) { + ValidatePicture(picture); + uxa_composite(PictOpSrc, pGlyphPicture, NULL, picture, + 0, 0, + 0, 0, + 0, 0, + glyph->info.width, glyph->info.height); + FreePicture(picture, 0); + } + } else { + uxa_copy_area(&pGlyphPixmap->drawable, + &scratch->drawable, + gc, + 0, 0, + glyph->info.width, glyph->info.height, + 0, 0); + } + } else { + scratch = pGlyphPixmap; + } + } + + uxa_copy_area(&scratch->drawable, &pCachePixmap->drawable, gc, + 0, 0, + glyph->info.width, glyph->info.height, + x, y); + + if (scratch != pGlyphPixmap) + screen->DestroyPixmap(scratch); + + FreeScratchGC(gc); +} + +void +uxa_glyph_unrealize(ScreenPtr pScreen, + GlyphPtr pGlyph) +{ + struct uxa_glyph *priv; + + priv = uxa_glyph_get_private(pGlyph); + if (priv == NULL) + return; + + priv->cache->glyphs[priv->pos] = NULL; + + uxa_glyph_set_private(pGlyph, NULL); + xfree(priv); +} + +/* 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 x, y, n; + + x1 = y1 = MAXSHORT; + x2 = y2 = MINSHORT; + x = y = 0; + while (nlist--) { + x += list->xOff; + y += list->yOff; + n = list->len; + list++; + while (n--) { + GlyphPtr glyph = *glyphs++; + int v; + + v = x - glyph->info.x; + if (v < x1) + x1 = v; + v += glyph->info.width; + if (v > x2) + x2 = v; + + v = y - glyph->info.y; + if (v < y1) + y1 = v; + v += glyph->info.height; + if (v > y2) + y2 = v; + + x += glyph->info.xOff; + y += glyph->info.yOff; + } + } + + extents->x1 = x1 < MINSHORT ? MINSHORT : x1; + extents->x2 = x2 > MAXSHORT ? MAXSHORT : x2; + extents->y1 = y1 < MINSHORT ? MINSHORT : y1; + extents->y2 = y2 > MAXSHORT ? MAXSHORT : y2; +} + +/** + * 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; + int x, y; + BoxRec extents; + Bool first = TRUE; + + x = 0; + y = 0; + extents.x1 = 0; + extents.y1 = 0; + extents.x2 = 0; + extents.y2 = 0; + while (nlist--) { + x += list->xOff; + y += list->yOff; + n = list->len; + list++; + while (n--) { + GlyphPtr 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; +} + +static void +uxa_check_glyphs(CARD8 op, + PicturePtr src, + PicturePtr dst, + PictFormatPtr maskFormat, + INT16 xSrc, + INT16 ySrc, int nlist, GlyphListPtr list, GlyphPtr * glyphs) +{ + int screen = dst->pDrawable->pScreen->myNum; + pixman_image_t *image; + PixmapPtr scratch; + PicturePtr mask; + int width = 0, height = 0; + int x, y, n; + int xDst = list->xOff, yDst = list->yOff; + BoxRec extents = { 0, 0, 0, 0 }; + + if (maskFormat) { + pixman_format_code_t format; + CARD32 component_alpha; + int error; + + 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; + + format = maskFormat->format | + (BitsPerPixel(maskFormat->depth) << 24); + image = + pixman_image_create_bits(format, width, height, NULL, 0); + if (!image) + return; + + scratch = GetScratchPixmapHeader(dst->pDrawable->pScreen, width, height, + PIXMAN_FORMAT_DEPTH(format), + PIXMAN_FORMAT_BPP(format), + pixman_image_get_stride(image), + pixman_image_get_data(image)); + + if (!scratch) { + pixman_image_unref(image); + return; + } + + component_alpha = NeedsComponent(maskFormat->format); + mask = CreatePicture(0, &scratch->drawable, + maskFormat, CPComponentAlpha, + &component_alpha, serverClient, &error); + if (!mask) { + FreeScratchPixmapHeader(scratch); + pixman_image_unref(image); + return; + } + ValidatePicture(mask); + + x = -extents.x1; + y = -extents.y1; + } else { + mask = dst; + x = 0; + y = 0; + } + + while (nlist--) { + x += list->xOff; + y += list->yOff; + n = list->len; + while (n--) { + GlyphPtr glyph = *glyphs++; + PicturePtr g = GlyphPicture(glyph)[screen]; + if (g) { + if (maskFormat) { + CompositePicture(PictOpAdd, g, NULL, mask, + 0, 0, + 0, 0, + x - glyph->info.x, + y - glyph->info.y, + glyph->info.width, + glyph->info.height); + } else { + CompositePicture(op, src, g, dst, + xSrc + (x - glyph->info.x) - xDst, + ySrc + (y - glyph->info.y) - yDst, + 0, 0, + x - glyph->info.x, + y - glyph->info.y, + glyph->info.width, + glyph->info.height); + } + } + + x += glyph->info.xOff; + y += glyph->info.yOff; + } + list++; + } + + if (maskFormat) { + x = extents.x1; + y = extents.y1; + CompositePicture(op, src, mask, dst, + xSrc + x - xDst, + ySrc + y - yDst, + 0, 0, + x, y, + width, height); + FreePicture(mask, 0); + FreeScratchPixmapHeader(scratch); + pixman_image_unref(image); + } +} + +static inline unsigned int +uxa_glyph_size_to_count(int size) +{ + size /= GLYPH_MIN_SIZE; + return size * size; +} + +static inline unsigned int +uxa_glyph_count_to_mask(int count) +{ + return ~(count - 1); +} + +static inline unsigned int +uxa_glyph_size_to_mask(int size) +{ + return uxa_glyph_count_to_mask(uxa_glyph_size_to_count(size)); +} + +static PicturePtr +uxa_glyph_cache(ScreenPtr screen, GlyphPtr glyph, int *out_x, int *out_y) +{ + uxa_screen_t *uxa_screen = uxa_get_screen(screen); + PicturePtr glyph_picture = GlyphPicture(glyph)[screen->myNum]; + uxa_glyph_cache_t *cache = &uxa_screen->glyphCaches[PICT_FORMAT_RGB(glyph_picture->format) != 0]; + struct uxa_glyph *priv = NULL; + int size, mask, pos, s; + + if (glyph->info.width > GLYPH_MAX_SIZE || glyph->info.height > GLYPH_MAX_SIZE) + return NULL; + + for (size = GLYPH_MIN_SIZE; size <= GLYPH_MAX_SIZE; size *= 2) + if (glyph->info.width <= size && glyph->info.height <= size) + break; + + s = uxa_glyph_size_to_count(size); + mask = uxa_glyph_count_to_mask(s); + pos = (cache->count + s - 1) & mask; + if (pos < GLYPH_CACHE_SIZE) { + cache->count = pos + s; + } else { + for (s = size; s <= GLYPH_MAX_SIZE; s *= 2) { + int i = cache->evict & uxa_glyph_size_to_mask(s); + GlyphPtr evicted = cache->glyphs[i]; + if (evicted == NULL) + continue; + + priv = uxa_glyph_get_private(evicted); + if (priv->size >= s) { + cache->glyphs[i] = NULL; + uxa_glyph_set_private(evicted, NULL); + pos = cache->evict & uxa_glyph_size_to_mask(size); + } else + priv = NULL; + break; + } + if (priv == NULL) { + int count = uxa_glyph_size_to_count(size); + mask = uxa_glyph_count_to_mask(count); + pos = cache->evict & mask; + for (s = 0; s < count; s++) { + GlyphPtr evicted = cache->glyphs[pos + s]; + if (evicted != NULL) { + if (priv != NULL) + xfree(priv); + + priv = uxa_glyph_get_private(evicted); + uxa_glyph_set_private(evicted, NULL); + cache->glyphs[pos + s] = NULL; + } + } + } + + /* And pick a new eviction position */ + cache->evict = rand() % GLYPH_CACHE_SIZE; + } + + if (priv == NULL) { + priv = xalloc(sizeof(struct uxa_glyph)); + if (priv == NULL) + return NULL; + } + + uxa_glyph_set_private(glyph, priv); + cache->glyphs[pos] = glyph; + + priv->cache = cache; + priv->size = size; + priv->pos = pos; + s = pos / ((GLYPH_MAX_SIZE / GLYPH_MIN_SIZE) * (GLYPH_MAX_SIZE / GLYPH_MIN_SIZE)); + priv->x = s % (CACHE_PICTURE_SIZE / GLYPH_MAX_SIZE) * GLYPH_MAX_SIZE; + priv->y = (s / (CACHE_PICTURE_SIZE / GLYPH_MAX_SIZE)) * GLYPH_MAX_SIZE; + for (s = GLYPH_MIN_SIZE; s < GLYPH_MAX_SIZE; s *= 2) { + if (pos & 1) + priv->x += s; + if (pos & 2) + priv->y += s; + pos >>= 2; + } + + uxa_glyph_cache_upload_glyph(screen, cache, glyph, priv->x, priv->y); + + *out_x = priv->x; + *out_y = priv->y; + return cache->picture; +} + +static int +uxa_glyphs_to_dst(CARD8 op, + PicturePtr pSrc, + PicturePtr pDst, + INT16 src_x, INT16 src_y, + INT16 xDst, INT16 yDst, + int nlist, GlyphListPtr list, GlyphPtr * glyphs, + BoxPtr extents) +{ + ScreenPtr screen = pDst->pDrawable->pScreen; + uxa_screen_t *uxa_screen = uxa_get_screen(screen); + PixmapPtr src_pixmap, dst_pixmap; + PicturePtr localSrc, glyph_atlas; + int x, y, n, nrect; + BoxRec box; + + if (uxa_screen->info->check_composite_texture && + uxa_screen->info->check_composite_texture(screen, pSrc)) { + if (pSrc->pDrawable) { + int src_off_x, src_off_y; + + src_pixmap = uxa_get_offscreen_pixmap(pSrc->pDrawable, &src_off_x, &src_off_y); + if (src_pixmap == NULL) + return -1; + + src_x += pSrc->pDrawable->x + src_off_x; + src_y += pSrc->pDrawable->y + src_off_y; + } else { + src_pixmap = NULL; + } + localSrc = pSrc; + } else { + int width, height; + + if (extents == NULL) { + uxa_glyph_extents(nlist, list, glyphs, &box); + extents = &box; + } + + width = extents->x2 - extents->x1; + height = extents->y2 - extents->y1; + if (width == 0 || height == 0) + return 0; + + if (pSrc->pDrawable) { + int src_off_x, src_off_y; + + src_off_x = extents->x1 - xDst; + src_off_y = extents->y1 - yDst; + localSrc = uxa_acquire_drawable(screen, pSrc, + src_x + src_off_x, src_y + src_off_y, + width, height, + &src_x, &src_y); + if (uxa_screen->info->check_composite_texture && + !uxa_screen->info->check_composite_texture(screen, localSrc)) { + if (localSrc != pSrc) + FreePicture(localSrc, 0); + return -1; + } + + src_pixmap = uxa_get_offscreen_pixmap(localSrc->pDrawable, &src_off_x, &src_off_y); + if (src_pixmap == NULL) { + if (localSrc != pSrc) + FreePicture(localSrc, 0); + return -1; + } + + src_x += localSrc->pDrawable->x + src_off_x; + src_y += localSrc->pDrawable->y + src_off_y; + } else { + localSrc = uxa_acquire_pattern(screen, pSrc, + PICT_a8r8g8b8, x, y, width, height); + if (!localSrc) + return 1; + + src_pixmap = uxa_get_drawable_pixmap(localSrc->pDrawable); + if (src_pixmap == NULL) { + FreePicture(localSrc, 0); + return -1; + } + + src_x = src_y = 0; + } + } + + dst_pixmap = uxa_get_offscreen_pixmap(pDst->pDrawable, &x, &y); + x += xDst + pDst->pDrawable->x - list->xOff; + y += yDst + pDst->pDrawable->y - list->yOff; + + glyph_atlas = NULL; + while (nlist--) { + x += list->xOff; + y += list->yOff; + n = list->len; + while (n--) { + GlyphPtr glyph = *glyphs++; + PicturePtr this_atlas; + int mask_x, mask_y; + struct uxa_glyph *priv; + + if (glyph->info.width == 0 || glyph->info.height == 0) + goto next_glyph; + + priv = uxa_glyph_get_private(glyph); + if (priv != NULL) { + mask_x = priv->x; + mask_y = priv->y; + this_atlas = priv->cache->picture; + } else { + if (glyph_atlas) { + uxa_screen->info->done_composite(dst_pixmap); + glyph_atlas = NULL; + } + this_atlas = uxa_glyph_cache(screen, glyph, &mask_x, &mask_y); + if (this_atlas == NULL) { + /* no cache for this glyph */ + this_atlas = GlyphPicture(glyph)[screen->myNum]; + mask_x = mask_y = 0; + } + } + + if (this_atlas != glyph_atlas) { + PixmapPtr mask_pixmap; + + if (glyph_atlas) + uxa_screen->info->done_composite(dst_pixmap); + + mask_pixmap = + uxa_get_drawable_pixmap(this_atlas->pDrawable); + assert (uxa_pixmap_is_offscreen(mask_pixmap)); + + if (!uxa_screen->info->prepare_composite(op, + localSrc, this_atlas, pDst, + src_pixmap, mask_pixmap, dst_pixmap)) + return -1; + + glyph_atlas = this_atlas; + } + + nrect = REGION_NUM_RECTS(pDst->pCompositeClip); + if (nrect == 1) { + uxa_screen->info->composite(dst_pixmap, + x + src_x, y + src_y, + mask_x, mask_y, + x - glyph->info.x, + y - glyph->info.y, + glyph->info.width, + glyph->info.height); + } else { + BoxPtr rects = REGION_RECTS(pDst->pCompositeClip); + do { + int x1 = x - glyph->info.x, dx = 0; + int y1 = y - glyph->info.y, dy = 0; + int x2 = x1 + glyph->info.width; + int y2 = y1 + glyph->info.height; + + if (x1 < rects->x1) + dx = rects->x1 - x1, x1 = rects->x1; + if (x2 > rects->x2) + x2 = rects->x2; + if (y1 < rects->y1) + dy = rects->y1 - y1, y1 = rects->y1; + if (y2 > rects->y2) + y2 = rects->y2; + + if (x1 < x2 && y1 < y2) { + uxa_screen->info->composite(dst_pixmap, + x1 + src_x, y1 + src_y, + dx + mask_x, dy + mask_y, + x1, y1, + x2 - x1, y2 - y1); + } + rects++; + } while (--nrect); + } + +next_glyph: + x += glyph->info.xOff; + y += glyph->info.yOff; + } + list++; + } + if (glyph_atlas) + uxa_screen->info->done_composite(dst_pixmap); + + if (localSrc != pSrc) + FreePicture(localSrc, 0); + + return 0; +} + +static void +uxa_clear_pixmap(ScreenPtr screen, + uxa_screen_t *uxa_screen, + PixmapPtr pixmap) +{ + if (uxa_screen->info->check_solid && + !uxa_screen->info->check_solid(&pixmap->drawable, GXcopy, FB_ALLONES)) + goto fallback; + + if (!uxa_screen->info->prepare_solid(pixmap, GXcopy, FB_ALLONES, 0)) + goto fallback; + + uxa_screen->info->solid(pixmap, + 0, 0, + pixmap->drawable.width, + pixmap->drawable.height); + + uxa_screen->info->done_solid(pixmap); + return; + +fallback: + { + GCPtr gc; + + gc = GetScratchGC(pixmap->drawable.depth, screen); + if (gc) { + xRectangle rect; + + ValidateGC(&pixmap->drawable, gc); + + rect.x = 0; + rect.y = 0; + rect.width = pixmap->drawable.width; + rect.height = pixmap->drawable.height; + gc->ops->PolyFillRect(&pixmap->drawable, gc, 1, &rect); + + FreeScratchGC(gc); + } + } +} + +static int +uxa_glyphs_via_mask(CARD8 op, + PicturePtr pSrc, + PicturePtr pDst, + PictFormatPtr maskFormat, + INT16 xSrc, INT16 ySrc, + INT16 xDst, INT16 yDst, + int nlist, GlyphListPtr list, GlyphPtr * glyphs, + BoxPtr extents) +{ + ScreenPtr screen = pDst->pDrawable->pScreen; + uxa_screen_t *uxa_screen = uxa_get_screen(screen); + CARD32 component_alpha; + PixmapPtr pixmap; + PicturePtr glyph_atlas, mask; + int x, y, width, height; + int dst_off_x, dst_off_y; + int n, error; + BoxRec box; + + if (!extents) { + uxa_glyph_extents(nlist, list, glyphs, &box); + + if (box.x2 <= box.x1 || box.y2 <= box.y1) + return 0; + + extents = &box; + dst_off_x = box.x1; + dst_off_y = box.y1; + } else { + dst_off_x = dst_off_y = 0; + } + + width = extents->x2 - extents->x1; + height = extents->y2 - extents->y1; + x = -extents->x1; + y = -extents->y1; + + if (maskFormat->depth == 1) { + PictFormatPtr a8Format = + PictureMatchFormat(screen, 8, PICT_a8); + + if (!a8Format) + return -1; + + maskFormat = a8Format; + } + + pixmap = screen->CreatePixmap(screen, width, height, + maskFormat->depth, + CREATE_PIXMAP_USAGE_SCRATCH); + if (!pixmap) + return 1; + + uxa_clear_pixmap(screen, uxa_screen, pixmap); + + component_alpha = NeedsComponent(maskFormat->format); + mask = CreatePicture(0, &pixmap->drawable, + maskFormat, CPComponentAlpha, + &component_alpha, serverClient, &error); + screen->DestroyPixmap(pixmap); + + if (!mask) + return 1; + + ValidatePicture(mask); + + glyph_atlas = NULL; + while (nlist--) { + x += list->xOff; + y += list->yOff; + n = list->len; + while (n--) { + GlyphPtr glyph = *glyphs++; + PicturePtr this_atlas; + int src_x, src_y; + struct uxa_glyph *priv; + + if (glyph->info.width == 0 || glyph->info.height == 0) + goto next_glyph; + + priv = uxa_glyph_get_private(glyph); + if (priv != NULL) { + src_x = priv->x; + src_y = priv->y; + this_atlas = priv->cache->picture; + } else { + if (glyph_atlas) { + uxa_screen->info->done_composite(pixmap); + glyph_atlas = NULL; + } + this_atlas = uxa_glyph_cache(screen, glyph, &src_x, &src_y); + if (this_atlas == NULL) { + /* no cache for this glyph */ + this_atlas = GlyphPicture(glyph)[screen->myNum]; + src_x = src_y = 0; + } + } + + if (this_atlas != glyph_atlas) { + PixmapPtr src_pixmap; + + if (glyph_atlas) + uxa_screen->info->done_composite(pixmap); + + src_pixmap = + uxa_get_drawable_pixmap(this_atlas->pDrawable); + assert (uxa_pixmap_is_offscreen(src_pixmap)); + + if (!uxa_screen->info->prepare_composite(PictOpAdd, + this_atlas, NULL, mask, + src_pixmap, NULL, pixmap)) + return -1; + + glyph_atlas = this_atlas; + } + + uxa_screen->info->composite(pixmap, + src_x, src_y, + 0, 0, + x - glyph->info.x, + y - glyph->info.y, + glyph->info.width, + glyph->info.height); + +next_glyph: + x += glyph->info.xOff; + y += glyph->info.yOff; + } + list++; + } + if (glyph_atlas) + uxa_screen->info->done_composite(pixmap); + + uxa_composite(op, + pSrc, mask, pDst, + dst_off_x + xSrc - xDst, + dst_off_y + ySrc - yDst, + 0, 0, + dst_off_x, dst_off_y, + width, height); + + FreePicture(mask, 0); + return 0; +} + +void +uxa_glyphs(CARD8 op, + PicturePtr pSrc, + PicturePtr pDst, + PictFormatPtr maskFormat, + INT16 xSrc, INT16 ySrc, + int nlist, GlyphListPtr list, GlyphPtr * glyphs) +{ + ScreenPtr screen = pDst->pDrawable->pScreen; + uxa_screen_t *uxa_screen = uxa_get_screen(screen); + int xDst = list->xOff, yDst = list->yOff; + BoxRec extents = { 0, 0, 0, 0 }; + Bool have_extents = FALSE; + int width, height, ret; + PicturePtr localDst = pDst; + + if (!uxa_screen->info->prepare_composite || + uxa_screen->swappedOut || + uxa_screen->force_fallback || + !uxa_drawable_is_offscreen(pDst->pDrawable) || + pDst->alphaMap || pSrc->alphaMap) { +fallback: + uxa_check_glyphs(op, pSrc, pDst, maskFormat, xSrc, ySrc, nlist, list, glyphs); + return; + } + + /* basic sanity check */ + if (uxa_screen->info->check_composite && + !uxa_screen->info->check_composite(op, pSrc, NULL, pDst, 0, 0)) { + goto fallback; + } + + ValidatePicture(pSrc); + ValidatePicture(pDst); + + if (!maskFormat) { + /* If we don't have a mask format but all the glyphs have the same format, + * require ComponentAlpha and don't intersect, use the glyph format as mask + * format for the full benefits of the glyph cache. + */ + if (NeedsComponent(list[0].format->format)) { + 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 || + uxa_glyphs_intersect(nlist, list, glyphs)) + maskFormat = NULL; + } + } + + if (!maskFormat && + uxa_screen->info->check_composite_target && + !uxa_screen->info->check_composite_target(uxa_get_drawable_pixmap(pDst->pDrawable))) { + int depth = pDst->pDrawable->depth; + PixmapPtr pixmap; + int x, y, error; + GCPtr gc; + + pixmap = uxa_get_drawable_pixmap(pDst->pDrawable); + if (uxa_screen->info->check_copy && + !uxa_screen->info->check_copy(pixmap, pixmap, GXcopy, FB_ALLONES)) + goto fallback; + + uxa_glyph_extents(nlist, list, glyphs, &extents); + + /* clip against dst bounds */ + if (extents.x1 < 0) + extents.x1 = 0; + if (extents.y1 < 0) + extents.y1 = 0; + if (extents.x2 > pDst->pDrawable->width) + extents.x2 = pDst->pDrawable->width; + if (extents.y2 > pDst->pDrawable->height) + extents.y2 = pDst->pDrawable->height; + + if (extents.x2 <= extents.x1 || extents.y2 <= extents.y1) + return; + width = extents.x2 - extents.x1; + height = extents.y2 - extents.y1; + x = -extents.x1; + y = -extents.y1; + have_extents = TRUE; + + xDst += x; + yDst += y; + + pixmap = screen->CreatePixmap(screen, + width, height, depth, + CREATE_PIXMAP_USAGE_SCRATCH); + if (!pixmap) + return; + + gc = GetScratchGC(depth, screen); + if (!gc) { + screen->DestroyPixmap(pixmap); + return; + } + + ValidateGC(&pixmap->drawable, gc); + gc->ops->CopyArea(pDst->pDrawable, &pixmap->drawable, gc, + extents.x1, extents.y1, + width, height, + 0, 0); + FreeScratchGC(gc); + + localDst = CreatePicture(0, &pixmap->drawable, + PictureMatchFormat(screen, depth, pDst->format), + 0, 0, serverClient, &error); + screen->DestroyPixmap(pixmap); + + if (!localDst) + return; + + ValidatePicture(localDst); + } + + if (maskFormat) { + ret = uxa_glyphs_via_mask(op, + pSrc, localDst, maskFormat, + xSrc, ySrc, + xDst, yDst, + nlist, list, glyphs, + have_extents ? &extents : NULL); + } else { + ret = uxa_glyphs_to_dst(op, + pSrc, localDst, + xSrc, ySrc, + xDst, yDst, + nlist, list, glyphs, + have_extents ? &extents : NULL); + } + if (ret) { + if (localDst != pDst) + FreePicture(localDst, 0); + + goto fallback; + } + + if (localDst != pDst) { + GCPtr gc; + + gc = GetScratchGC(pDst->pDrawable->depth, screen); + if (gc) { + ValidateGC(pDst->pDrawable, gc); + gc->ops->CopyArea(localDst->pDrawable, pDst->pDrawable, gc, + 0, 0, + width, height, + extents.x1, extents.y1); + FreeScratchGC(gc); + } + + FreePicture(localDst, 0); + } +} diff --git a/src/uxa/uxa-priv.h b/src/uxa/uxa-priv.h new file mode 100644 index 0000000..6492ca7 --- /dev/null +++ b/src/uxa/uxa-priv.h @@ -0,0 +1,426 @@ +/* + * + * 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_CONFIG_H +#include <config.h> +#endif +#ifdef HAVE_DIX_CONFIG_H +#include <dix-config.h> +#else +#include <xorg-server.h> +#endif +#include "xf86.h" + +#include "uxa.h" + +#include <X11/X.h> +#define NEED_EVENTS +#include <X11/Xproto.h> +#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" + +/* Provide substitutes for gcc's __FUNCTION__ on other compilers */ +#if !defined(__GNUC__) && !defined(__FUNCTION__) +# if defined(__STDC__) && (__STDC_VERSION__>=199901L) /* C99 */ +# define __FUNCTION__ __func__ +# else +# define __FUNCTION__ "" +# endif +#endif + +/* 1.6 and earlier server compat */ +#ifndef miGetCompositeClip +#define miCopyRegion fbCopyRegion +#define miDoCopy fbDoCopy +#endif + +#define DEBUG_MIGRATE 0 +#define DEBUG_PIXMAP 0 +#define DEBUG_OFFSCREEN 0 +#define DEBUG_GLYPH_CACHE 0 + +#define UXA_FALLBACK(x) \ +if (uxa_get_screen(screen)->fallback_debug) { \ + ErrorF("UXA fallback at %s: ", __FUNCTION__); \ + ErrorF x; \ +} + +char uxa_drawable_location(DrawablePtr pDrawable); + +#if DEBUG_PIXMAP +#define DBG_PIXMAP(a) ErrorF a +#else +#define DBG_PIXMAP(a) +#endif + +typedef struct { + PicturePtr picture; /* Where the glyphs of the cache are stored */ + GlyphPtr *glyphs; + uint16_t count; + uint16_t evict; +} uxa_glyph_cache_t; + +#define UXA_NUM_GLYPH_CACHE_FORMATS 2 + +typedef struct { + uint32_t color; + PicturePtr picture; +} uxa_solid_cache_t; + +#define UXA_NUM_SOLID_CACHE 16 + +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; + CompositeRectsProcPtr SavedCompositeRects; + TrianglesProcPtr SavedTriangles; + GlyphsProcPtr SavedGlyphs; + TrapezoidsProcPtr SavedTrapezoids; + AddTrapsProcPtr SavedAddTraps; + UnrealizeGlyphProcPtr SavedUnrealizeGlyph; +#endif + EnableDisableFBAccessProcPtr SavedEnableDisableFBAccess; + + Bool force_fallback; + Bool fallback_debug; + Bool swappedOut; + unsigned disableFbCount; + unsigned offScreenCounter; + + uxa_glyph_cache_t glyphCaches[UXA_NUM_GLYPH_CACHE_FORMATS]; + + PicturePtr solid_clear, solid_black, solid_white; + uxa_solid_cache_t solid_cache[UXA_NUM_SOLID_CACHE]; + int solid_cache_size; +} 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 int uxa_screen_index; +static inline uxa_screen_t *uxa_get_screen(ScreenPtr screen) +{ + return (uxa_screen_t *) dixLookupPrivate(&screen->devPrivates, + &uxa_screen_index); +} + +/** 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); + +Bool uxa_prepare_access_window(WindowPtr pWin); + +void uxa_finish_access_window(WindowPtr pWin); + +/* uxa-unaccel.c */ +Bool 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_paint_window(WindowPtr pWin, RegionPtr pRegion, int what); + +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_paint_window(WindowPtr pWin, RegionPtr pRegion, int what); + +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 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 */ +Bool uxa_prepare_access(DrawablePtr pDrawable, uxa_access_t access); + +void uxa_finish_access(DrawablePtr pDrawable); + +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_solid_rects (CARD8 op, + PicturePtr dst, + xRenderColor *color, + int num_rects, + xRectangle *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); + +PicturePtr +uxa_acquire_solid(ScreenPtr screen, SourcePict *source); + +PicturePtr +uxa_acquire_drawable(ScreenPtr pScreen, + PicturePtr pSrc, + INT16 x, INT16 y, + CARD16 width, CARD16 height, + INT16 * out_x, INT16 * out_y); + +PicturePtr +uxa_acquire_pattern(ScreenPtr pScreen, + PicturePtr pSrc, + pixman_format_code_t format, + INT16 x, INT16 y, + CARD16 width, CARD16 height); + +Bool +uxa_get_rgba_from_pixel(CARD32 pixel, + CARD16 * red, + CARD16 * green, + CARD16 * blue, + CARD16 * alpha, + CARD32 format); + +/* uxa_glyph.c */ +Bool 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); + +void +uxa_glyph_unrealize(ScreenPtr pScreen, + GlyphPtr pGlyph); + +#endif /* UXAPRIV_H */ diff --git a/src/uxa/uxa-render.c b/src/uxa/uxa-render.c new file mode 100644 index 0000000..0e4fa91 --- /dev/null +++ b/src/uxa/uxa-render.c @@ -0,0 +1,1957 @@ +/* + * 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" +#include <xorgVersion.h> + +#ifdef RENDER +#include "mipict.h" + +static void uxa_composite_fallback_pict_desc(PicturePtr pict, char *string, + int n) +{ + char format[20]; + char size[20]; + char loc; + + if (!pict) { + snprintf(string, n, "None"); + return; + } + + if (pict->pDrawable == NULL) { + snprintf(string, n, "source-only"); + 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_drawable_is_offscreen(pict->pDrawable) ? '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)%s", + pict->pDrawable, loc, format, size, + pict->alphaMap ? " with alpha map" :""); +} + +static const char * +op_to_string(CARD8 op) +{ + switch (op) { +#define C(x) case PictOp##x: return #x + C(Clear); + C(Src); + C(Dst); + C(Over); + C(OverReverse); + C(In); + C(InReverse); + C(Out); + C(OutReverse); + C(Atop); + C(AtopReverse); + C(Xor); + C(Add); + C(Saturate); + + /* + * Operators only available in version 0.2 + */ + C(DisjointClear); + C(DisjointSrc); + C(DisjointDst); + C(DisjointOver); + C(DisjointOverReverse); + C(DisjointIn); + C(DisjointInReverse); + C(DisjointOut); + C(DisjointOutReverse); + C(DisjointAtop); + C(DisjointAtopReverse); + C(DisjointXor); + + C(ConjointClear); + C(ConjointSrc); + C(ConjointDst); + C(ConjointOver); + C(ConjointOverReverse); + C(ConjointIn); + C(ConjointInReverse); + C(ConjointOut); + C(ConjointOutReverse); + C(ConjointAtop); + C(ConjointAtopReverse); + C(ConjointXor); + + /* + * Operators only available in version 0.11 + */ + C(Multiply); + C(Screen); + C(Overlay); + C(Darken); + C(Lighten); + C(ColorDodge); + C(ColorBurn); + C(HardLight); + C(SoftLight); + C(Difference); + C(Exclusion); + C(HSLHue); + C(HSLSaturation); + C(HSLColor); + C(HSLLuminosity); + default: return "garbage"; +#undef C + } +} + +static void +uxa_print_composite_fallback(const char *func, CARD8 op, + PicturePtr pSrc, PicturePtr pMask, PicturePtr pDst) +{ + uxa_screen_t *uxa_screen = uxa_get_screen(pDst->pDrawable->pScreen); + char srcdesc[40], maskdesc[40], dstdesc[40]; + + if (! uxa_screen->fallback_debug) + return; + + /* Limit the noise if fallbacks are expected. */ + if (uxa_screen->force_fallback) + return; + + 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 at %s:\n" + " op %s, \n" + " src %s, \n" + " mask %s, \n" + " dst %s, \n" + " screen %s\n", + func, op_to_string (op), srcdesc, maskdesc, dstdesc, + uxa_screen->swappedOut ? "swapped out" : "normal"); +} + +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; + + rbits = PICT_FORMAT_R(format); + gbits = PICT_FORMAT_G(format); + bbits = PICT_FORMAT_B(format); + abits = PICT_FORMAT_A(format); + if (abits == 0) + abits = PICT_FORMAT_BPP(format) - (rbits+gbits+bbits); + + if (PICT_FORMAT_TYPE(format) == PICT_TYPE_A) { + *pixel = alpha >> (16 - abits); + return TRUE; + } + + if (!PICT_FORMAT_COLOR(format)) + return FALSE; + + if (PICT_FORMAT_TYPE(format) == PICT_TYPE_ARGB) { + bshift = 0; + gshift = bbits; + rshift = gshift + gbits; + ashift = rshift + rbits; + } else if (PICT_FORMAT_TYPE(format) == PICT_TYPE_ABGR) { + rshift = 0; + gshift = rbits; + bshift = gshift + gbits; + ashift = bshift + bbits; + } else if (PICT_FORMAT_TYPE(format) == PICT_TYPE_BGRA) { + ashift = 0; + rshift = abits; + gshift = rshift + rbits; + bshift = gshift + gbits; + } else { + return FALSE; + } + + *pixel = 0; + *pixel |= (blue >> (16 - bbits)) << bshift; + *pixel |= (green >> (16 - gbits)) << gshift; + *pixel |= (red >> (16 - rbits)) << rshift; + *pixel |= (alpha >> (16 - abits)) << ashift; + + return TRUE; +} + +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; + + 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_A) { + rshift = gshift = bshift = ashift = 0; + } else if (PICT_FORMAT_TYPE(format) == PICT_TYPE_ARGB) { + bshift = 0; + gshift = bbits; + rshift = gshift + gbits; + ashift = rshift + rbits; + } else if (PICT_FORMAT_TYPE(format) == PICT_TYPE_ABGR) { + rshift = 0; + gshift = rbits; + bshift = gshift + gbits; + ashift = bshift + bbits; + } else if (PICT_FORMAT_TYPE(format) == PICT_TYPE_BGRA) { + ashift = 0; + rshift = abits; + if (abits == 0) + rshift = PICT_FORMAT_BPP(format) - (rbits+gbits+bbits); + gshift = rshift + rbits; + bshift = gshift + gbits; + } else { + return FALSE; + } + + if (rbits) { + *red = ((pixel >> rshift) & ((1 << rbits) - 1)) << (16 - rbits); + while (rbits < 16) { + *red |= *red >> rbits; + rbits <<= 1; + } + } else + *red = 0; + + if (gbits) { + *green = ((pixel >> gshift) & ((1 << gbits) - 1)) << (16 - gbits); + while (gbits < 16) { + *green |= *green >> gbits; + gbits <<= 1; + } + } else + *green = 0; + + if (bbits) { + *blue = ((pixel >> bshift) & ((1 << bbits) - 1)) << (16 - bbits); + while (bbits < 16) { + *blue |= *blue >> bbits; + bbits <<= 1; + } + } else + *blue = 0; + + if (abits) { + *alpha = + ((pixel >> ashift) & ((1 << abits) - 1)) << (16 - abits); + while (abits < 16) { + *alpha |= *alpha >> abits; + abits <<= 1; + } + } else + *alpha = 0xffff; + + return TRUE; +} + +Bool +uxa_get_color_for_pixmap (PixmapPtr pixmap, + CARD32 src_format, + CARD32 dst_format, + CARD32 *pixel) +{ + CARD16 red, green, blue, alpha; + + *pixel = uxa_get_pixmap_first_pixel(pixmap); + + if (src_format != dst_format) { + if (!uxa_get_rgba_from_pixel(*pixel, + &red, &green, &blue, &alpha, + src_format)) + return FALSE; + + if (!uxa_get_pixel_from_rgba(pixel, + red, green, blue, alpha, + dst_format)) + return FALSE; + } + + 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 = NULL, pDstPix; + CARD32 pixel; + + if (uxa_screen->info->check_solid && !uxa_screen->info->check_solid(pDst->pDrawable, GXcopy, FB_ALLONES)) + return -1; + + pDstPix = uxa_get_offscreen_pixmap(pDst->pDrawable, &dst_off_x, &dst_off_y); + if (!pDstPix) + return -1; + + xDst += pDst->pDrawable->x; + yDst += pDst->pDrawable->y; + + if (pSrc->pDrawable) { + pSrcPix = uxa_get_drawable_pixmap(pSrc->pDrawable); + xSrc += pSrc->pDrawable->x; + ySrc += pSrc->pDrawable->y; + } + + if (!miComputeCompositeRegion(®ion, pSrc, NULL, pDst, + xSrc, ySrc, 0, 0, xDst, yDst, + width, height)) + return 1; + + if (pSrcPix) { + if (! uxa_get_color_for_pixmap (pSrcPix, pSrc->format, pDst->format, &pixel)) { + REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); + return -1; + } + } else { + SourcePict *source = pSrc->pSourcePict; + PictSolidFill *solid = &source->solidFill; + + if (source == NULL || source->type != SourcePictTypeSolidFill) { + REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); + return -1; + } + + if (pDst->format == PICT_a8r8g8b8) { + pixel = solid->color; + } else if (pDst->format == PICT_x8r8g8b8) { + pixel = solid->color | 0xff000000; + } else { + CARD16 red, green, blue, alpha; + + if (!uxa_get_rgba_from_pixel(solid->color, + &red, &green, &blue, &alpha, + PICT_a8r8g8b8) || + !uxa_get_pixel_from_rgba(&pixel, + red, green, blue, alpha, + pDst->format)) { + REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); + return -1; + } + } + } + + if (!(*uxa_screen->info->prepare_solid) + (pDstPix, GXcopy, FB_ALLONES, pixel)) { + REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); + return -1; + } + + REGION_TRANSLATE(pScreen, ®ion, dst_off_x, dst_off_y); + + 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->done_solid) (pDstPix); + + REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); + return 1; +} + +static PicturePtr +uxa_picture_for_pixman_format(ScreenPtr pScreen, + pixman_format_code_t format, + int width, int height) +{ + PicturePtr pPicture; + PixmapPtr pPixmap; + int error; + + if (format == PIXMAN_a1) + format = PIXMAN_a8; + + /* fill alpha if unset */ + if (PIXMAN_FORMAT_A(format) == 0) + format = PIXMAN_a8r8g8b8; + + pPixmap = (*pScreen->CreatePixmap)(pScreen, width, height, + PIXMAN_FORMAT_DEPTH(format), + UXA_CREATE_PIXMAP_FOR_MAP); + if (!pPixmap) + return 0; + + pPicture = CreatePicture(0, &pPixmap->drawable, + PictureMatchFormat(pScreen, + PIXMAN_FORMAT_DEPTH(format), + format), + 0, 0, serverClient, &error); + (*pScreen->DestroyPixmap) (pPixmap); + if (!pPicture) + return 0; + + ValidatePicture(pPicture); + + return pPicture; +} + +static PicturePtr +uxa_picture_from_pixman_image(ScreenPtr screen, + pixman_image_t * image, + pixman_format_code_t format) +{ + uxa_screen_t *uxa_screen = uxa_get_screen(screen); + PicturePtr picture; + PixmapPtr pixmap; + int width, height; + + width = pixman_image_get_width(image); + height = pixman_image_get_height(image); + + picture = uxa_picture_for_pixman_format(screen, format, + width, height); + if (!picture) + return 0; + + if (uxa_screen->info->put_image && + ((picture->pDrawable->depth << 24) | picture->format) == format && + uxa_screen->info->put_image((PixmapPtr)picture->pDrawable, + 0, 0, + width, height, + (char *)pixman_image_get_data(image), + pixman_image_get_stride(image))) + return picture; + + pixmap = GetScratchPixmapHeader(screen, width, height, + PIXMAN_FORMAT_DEPTH(format), + PIXMAN_FORMAT_BPP(format), + pixman_image_get_stride(image), + pixman_image_get_data(image)); + if (!pixmap) { + FreePicture(picture, 0); + return 0; + } + + if (((picture->pDrawable->depth << 24) | picture->format) == format) { + GCPtr gc; + + gc = GetScratchGC(PIXMAN_FORMAT_DEPTH(format), screen); + if (!gc) { + FreeScratchPixmapHeader(pixmap); + FreePicture(picture, 0); + return 0; + } + ValidateGC(picture->pDrawable, gc); + + (*gc->ops->CopyArea) (&pixmap->drawable, picture->pDrawable, + gc, 0, 0, width, height, 0, 0); + + FreeScratchGC(gc); + } else { + PicturePtr src; + int error; + + src = CreatePicture(0, &pixmap->drawable, + PictureMatchFormat(screen, + PIXMAN_FORMAT_DEPTH(format), + format), + 0, 0, serverClient, &error); + if (!src) { + FreeScratchPixmapHeader(pixmap); + FreePicture(picture, 0); + return 0; + } + ValidatePicture(src); + + if (uxa_prepare_access(picture->pDrawable, UXA_ACCESS_RW)) { + fbComposite(PictOpSrc, src, NULL, picture, + 0, 0, 0, 0, 0, 0, width, height); + uxa_finish_access(picture->pDrawable); + } + + FreePicture(src, 0); + } + FreeScratchPixmapHeader(pixmap); + + return picture; +} + +static PicturePtr +uxa_create_solid(ScreenPtr screen, uint32_t color) +{ + PixmapPtr pixmap; + PicturePtr picture; + XID repeat = RepeatNormal; + int error = 0; + + pixmap = (*screen->CreatePixmap)(screen, 1, 1, 32, + UXA_CREATE_PIXMAP_FOR_MAP); + if (!pixmap) + return 0; + + if (!uxa_prepare_access((DrawablePtr)pixmap, UXA_ACCESS_RW)) { + (*screen->DestroyPixmap)(pixmap); + return 0; + } + *((uint32_t *)pixmap->devPrivate.ptr) = color; + uxa_finish_access((DrawablePtr)pixmap); + + picture = CreatePicture(0, &pixmap->drawable, + PictureMatchFormat(screen, 32, PICT_a8r8g8b8), + CPRepeat, &repeat, serverClient, &error); + (*screen->DestroyPixmap)(pixmap); + + return picture; +} + +static PicturePtr +uxa_solid_clear(ScreenPtr screen) +{ + uxa_screen_t *uxa_screen = uxa_get_screen(screen); + PicturePtr picture; + + if (!uxa_screen->solid_clear) { + uxa_screen->solid_clear = uxa_create_solid(screen, 0); + if (!uxa_screen->solid_clear) + return 0; + } + picture = uxa_screen->solid_clear; + return picture; +} + +PicturePtr +uxa_acquire_solid(ScreenPtr screen, SourcePict *source) +{ + uxa_screen_t *uxa_screen = uxa_get_screen(screen); + PictSolidFill *solid = &source->solidFill; + PicturePtr picture; + int i; + + if ((solid->color >> 24) == 0) { + picture = uxa_solid_clear(screen); + if (!picture) + return 0; + + goto DONE; + } else if (solid->color == 0xff000000) { + if (!uxa_screen->solid_black) { + uxa_screen->solid_black = uxa_create_solid(screen, 0xff000000); + if (!uxa_screen->solid_black) + return 0; + } + picture = uxa_screen->solid_black; + goto DONE; + } else if (solid->color == 0xffffffff) { + if (!uxa_screen->solid_white) { + uxa_screen->solid_white = uxa_create_solid(screen, 0xffffffff); + if (!uxa_screen->solid_white) + return 0; + } + picture = uxa_screen->solid_white; + goto DONE; + } + + for (i = 0; i < uxa_screen->solid_cache_size; i++) { + if (uxa_screen->solid_cache[i].color == solid->color) { + picture = uxa_screen->solid_cache[i].picture; + goto DONE; + } + } + + picture = uxa_create_solid(screen, solid->color); + if (!picture) + return 0; + + if (uxa_screen->solid_cache_size == UXA_NUM_SOLID_CACHE) { + i = rand() % UXA_NUM_SOLID_CACHE; + FreePicture(uxa_screen->solid_cache[i].picture, 0); + } else + uxa_screen->solid_cache_size++; + + uxa_screen->solid_cache[i].picture = picture; + uxa_screen->solid_cache[i].color = solid->color; + +DONE: + picture->refcnt++; + return picture; +} + +PicturePtr +uxa_acquire_pattern(ScreenPtr pScreen, + PicturePtr pSrc, + pixman_format_code_t format, + INT16 x, INT16 y, CARD16 width, CARD16 height) +{ + PicturePtr pDst; + + if (pSrc->pSourcePict) { + SourcePict *source = pSrc->pSourcePict; + if (source->type == SourcePictTypeSolidFill) + return uxa_acquire_solid (pScreen, source); + } + + pDst = uxa_picture_for_pixman_format(pScreen, format, width, height); + if (!pDst) + return 0; + + if (uxa_prepare_access(pDst->pDrawable, UXA_ACCESS_RW)) { + fbComposite(PictOpSrc, pSrc, NULL, pDst, + x, y, 0, 0, 0, 0, width, height); + uxa_finish_access(pDst->pDrawable); + return pDst; + } else { + FreePicture(pDst, 0); + return 0; + } +} + +static Bool +transform_is_integer_translation(PictTransformPtr t, int *tx, int *ty) +{ + if (t == NULL) { + *tx = *ty = 0; + return TRUE; + } + + if (t->matrix[0][0] != IntToxFixed(1) || + t->matrix[0][1] != 0 || + t->matrix[1][0] != 0 || + t->matrix[1][1] != IntToxFixed(1) || + t->matrix[2][0] != 0 || + t->matrix[2][1] != 0 || + t->matrix[2][2] != IntToxFixed(1)) + return FALSE; + + if (xFixedFrac(t->matrix[0][2]) != 0 || + xFixedFrac(t->matrix[1][2]) != 0) + return FALSE; + + *tx = xFixedToInt(t->matrix[0][2]); + *ty = xFixedToInt(t->matrix[1][2]); + return TRUE; +} + +static PicturePtr +uxa_render_picture(ScreenPtr screen, + PicturePtr src, + pixman_format_code_t format, + INT16 x, INT16 y, + CARD16 width, CARD16 height) +{ + PicturePtr picture; + int ret = 0; + + /* XXX we need a mechanism for the card to choose the fallback format */ + + /* force alpha channel in case source does not entirely cover the extents */ + if (PIXMAN_FORMAT_A(format) == 0) + format = PIXMAN_a8r8g8b8; /* available on all hardware */ + + picture = uxa_picture_for_pixman_format(screen, format, width, height); + if (!picture) + return 0; + + if (uxa_prepare_access(picture->pDrawable, UXA_ACCESS_RW)) { + if (uxa_prepare_access(src->pDrawable, UXA_ACCESS_RO)) { + ret = 1; + fbComposite(PictOpSrc, src, NULL, picture, + x, y, 0, 0, 0, 0, width, height); + uxa_finish_access(src->pDrawable); + } + uxa_finish_access(picture->pDrawable); + } + + if (!ret) { + FreePicture(picture, 0); + return 0; + } + + return picture; +} + +PicturePtr +uxa_acquire_drawable(ScreenPtr pScreen, + PicturePtr pSrc, + INT16 x, INT16 y, + CARD16 width, CARD16 height, + INT16 * out_x, INT16 * out_y) +{ + PixmapPtr pPixmap; + PicturePtr pDst; + GCPtr pGC; + int depth, error; + int tx, ty; + + depth = pSrc->pDrawable->depth; + if (depth == 1 || + pSrc->filter == PictFilterConvolution || /* XXX */ + !transform_is_integer_translation(pSrc->transform, &tx, &ty)) { + /* XXX extract the sample extents and do the transformation on the GPU */ + pDst = uxa_render_picture(pScreen, pSrc, + pSrc->format | (BitsPerPixel(pSrc->pDrawable->depth) << 24), + x, y, width, height); + + goto done; + } else { + if (width == pSrc->pDrawable->width && height == pSrc->pDrawable->depth) { + *out_x = x + pSrc->pDrawable->x; + *out_y = y + pSrc->pDrawable->y; + return pSrc; + } + } + + pPixmap = pScreen->CreatePixmap(pScreen, + width, height, depth, + CREATE_PIXMAP_USAGE_SCRATCH); + if (!pPixmap) + return 0; + + /* Skip the copy if the result remains in memory and not a bo */ + if (!uxa_drawable_is_offscreen(&pPixmap->drawable)) { + pScreen->DestroyPixmap(pPixmap); + return 0; + } + + pGC = GetScratchGC(depth, pScreen); + if (!pGC) { + pScreen->DestroyPixmap(pPixmap); + return 0; + } + + ValidateGC(&pPixmap->drawable, pGC); + pGC->ops->CopyArea(pSrc->pDrawable, &pPixmap->drawable, pGC, + x + tx, y + ty, width, height, 0, 0); + FreeScratchGC(pGC); + + pDst = CreatePicture(0, &pPixmap->drawable, + PictureMatchFormat(pScreen, depth, pSrc->format), + 0, 0, serverClient, &error); + pScreen->DestroyPixmap(pPixmap); + ValidatePicture(pDst); + +done: + pDst->componentAlpha = pSrc->componentAlpha; + *out_x = x; + *out_y = y; + return pDst; +} + +static PicturePtr +uxa_acquire_picture(ScreenPtr screen, + PicturePtr src, + pixman_format_code_t format, + INT16 x, INT16 y, + CARD16 width, CARD16 height, + INT16 * out_x, INT16 * out_y) +{ + uxa_screen_t *uxa_screen = uxa_get_screen(screen); + + if (uxa_screen->info->check_composite_texture && + uxa_screen->info->check_composite_texture(screen, src)) { + if (src->pDrawable) { + *out_x = x + src->pDrawable->x; + *out_y = y + src->pDrawable->y; + } else { + *out_x = 0; + *out_y = 0; + } + return src; + } + + if (src->pDrawable) { + PicturePtr dst; + + dst = uxa_acquire_drawable(screen, src, + x, y, width, height, + out_x, out_y); + if (uxa_screen->info->check_composite_texture && + !uxa_screen->info->check_composite_texture(screen, dst)) { + if (dst != src) + FreePicture(dst, 0); + return 0; + } + + return dst; + } + + *out_x = 0; + *out_y = 0; + return uxa_acquire_pattern(screen, src, + format, x, y, width, height); +} + +static PicturePtr +uxa_acquire_source(ScreenPtr screen, + PicturePtr pict, + INT16 x, INT16 y, + CARD16 width, CARD16 height, + INT16 * out_x, INT16 * out_y) +{ + return uxa_acquire_picture (screen, pict, + PICT_a8r8g8b8, + x, y, + width, height, + out_x, out_y); +} + +static PicturePtr +uxa_acquire_mask(ScreenPtr screen, + PicturePtr pict, + INT16 x, INT16 y, + INT16 width, INT16 height, + INT16 * out_x, INT16 * out_y) +{ + return uxa_acquire_picture (screen, pict, + PICT_a8, + x, y, + width, height, + out_x, out_y); +} + +static Bool +_pixman_region_init_rectangles(pixman_region16_t *region, + int num_rects, + xRectangle *rects, + int tx, int ty) +{ + pixman_box16_t stack_boxes[64], *boxes = stack_boxes; + pixman_bool_t ret; + int i; + + if (num_rects > sizeof(stack_boxes) / sizeof(stack_boxes[0])) { + boxes = xalloc(sizeof(pixman_box16_t) * num_rects); + if (boxes == NULL) + return FALSE; + } + + for (i = 0; i < num_rects; i++) { + boxes[i].x1 = rects[i].x + tx; + boxes[i].y1 = rects[i].y + ty; + boxes[i].x2 = rects[i].x + tx + rects[i].width; + boxes[i].y2 = rects[i].y + ty + rects[i].height; + } + + ret = pixman_region_init_rects(region, boxes, num_rects); + + if (boxes != stack_boxes) + xfree(boxes); + + return ret; +} + +void +uxa_solid_rects (CARD8 op, + PicturePtr dst, + xRenderColor *color, + int num_rects, + xRectangle *rects) +{ + ScreenPtr screen = dst->pDrawable->pScreen; + uxa_screen_t *uxa_screen = uxa_get_screen(screen); + PixmapPtr dst_pixmap, src_pixmap = NULL; + pixman_region16_t region; + pixman_box16_t *boxes, *extents; + PicturePtr src; + int dst_x, dst_y; + int num_boxes; + + if (!pixman_region_not_empty(dst->pCompositeClip)) + return; + + if (dst->alphaMap) + goto fallback; + + dst_pixmap = uxa_get_offscreen_pixmap(dst->pDrawable, &dst_x, &dst_y); + if (!dst_pixmap) + goto fallback; + + if (!_pixman_region_init_rectangles(®ion, + num_rects, rects, + dst->pDrawable->x, dst->pDrawable->y)) + goto fallback; + + if (!pixman_region_intersect(®ion, ®ion, dst->pCompositeClip)) { + pixman_region_fini(®ion); + return; + } + + /* XXX xserver-1.8: CompositeRects is not tracked by Damage, so we must + * manually append the damaged regions ourselves. + */ + DamageRegionAppend(dst->pDrawable, ®ion); + + pixman_region_translate(®ion, dst_x, dst_y); + boxes = pixman_region_rectangles(®ion, &num_boxes); + extents = pixman_region_extents (®ion); + + if (op == PictOpClear) + color->red = color->green = color->blue = color->alpha = 0; + if (color->alpha >= 0xff00 && op == PictOpOver) { + color->alpha = 0xffff; + op = PictOpSrc; + } + + /* Using GEM, the relocation costs outweigh the advantages of the blitter */ + if (num_boxes == 1 && (op == PictOpSrc || op == PictOpClear)) { + CARD32 pixel; + +try_solid: + if (uxa_screen->info->check_solid && + !uxa_screen->info->check_solid(&dst_pixmap->drawable, GXcopy, FB_ALLONES)) + goto err_region; + + if (!uxa_get_pixel_from_rgba(&pixel, + color->red, + color->green, + color->blue, + color->alpha, + dst->format)) + goto err_region; + + if (!uxa_screen->info->prepare_solid(dst_pixmap, GXcopy, FB_ALLONES, pixel)) + goto err_region; + + while (num_boxes--) { + uxa_screen->info->solid(dst_pixmap, + boxes->x1, boxes->y1, + boxes->x2, boxes->y2); + boxes++; + } + + uxa_screen->info->done_solid(dst_pixmap); + } else { + int error; + + src = CreateSolidPicture(0, color, &error); + if (!src) + goto err_region; + + if (!uxa_screen->info->check_composite(op, src, NULL, dst, + extents->x2 - extents->x1, + extents->y2 - extents->y1)) { + if (op == PictOpSrc || op == PictOpClear) { + FreePicture(src, 0); + goto try_solid; + } + + goto err_src; + } + + if (!uxa_screen->info->check_composite_texture || + !uxa_screen->info->check_composite_texture(screen, src)) { + PicturePtr solid; + int src_off_x, src_off_y; + + solid = uxa_acquire_solid(screen, src->pSourcePict); + FreePicture(src, 0); + + src = solid; + src_pixmap = uxa_get_offscreen_pixmap(src->pDrawable, + &src_off_x, &src_off_y); + if (!src_pixmap) + goto err_src; + } + + if (!uxa_screen->info->prepare_composite(op, src, NULL, dst, src_pixmap, NULL, dst_pixmap)) + goto err_src; + + while (num_boxes--) { + uxa_screen->info->composite(dst_pixmap, + 0, 0, 0, 0, + boxes->x1, + boxes->y1, + boxes->x2 - boxes->x1, + boxes->y2 - boxes->y1); + boxes++; + } + + uxa_screen->info->done_composite(dst_pixmap); + FreePicture(src, 0); + } + + pixman_region_fini(®ion); + return; + +err_src: + FreePicture(src, 0); +err_region: + pixman_region_fini(®ion); +fallback: + uxa_screen->SavedCompositeRects(op, dst, color, num_rects, rects); +} + +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) +{ + ScreenPtr screen = pDst->pDrawable->pScreen; + uxa_screen_t *uxa_screen = uxa_get_screen(screen); + RegionRec region; + BoxPtr pbox; + int nbox; + int xDst_copy, yDst_copy; + int src_off_x, src_off_y, mask_off_x, mask_off_y, dst_off_x, dst_off_y; + PixmapPtr pSrcPix, pMaskPix = NULL, pDstPix; + PicturePtr localSrc, localMask = NULL; + PicturePtr localDst = pDst; + + if (uxa_screen->info->check_composite && + !(*uxa_screen->info->check_composite) (op, pSrc, pMask, pDst, width, height)) + return -1; + + if (uxa_screen->info->check_composite_target && + !uxa_screen->info->check_composite_target(uxa_get_drawable_pixmap(pDst->pDrawable))) { + int depth = pDst->pDrawable->depth; + PixmapPtr pixmap; + int error; + GCPtr gc; + + pixmap = uxa_get_drawable_pixmap(pDst->pDrawable); + if (uxa_screen->info->check_copy && + !uxa_screen->info->check_copy(pixmap, pixmap, GXcopy, FB_ALLONES)) + return -1; + + pixmap = screen->CreatePixmap(screen, + width, height, depth, + CREATE_PIXMAP_USAGE_SCRATCH); + if (!pixmap) + return 0; + + gc = GetScratchGC(depth, screen); + if (!gc) { + screen->DestroyPixmap(pixmap); + return 0; + } + + ValidateGC(&pixmap->drawable, gc); + gc->ops->CopyArea(pDst->pDrawable, &pixmap->drawable, gc, + xDst, yDst, width, height, 0, 0); + FreeScratchGC(gc); + + xDst_copy = xDst; xDst = 0; + yDst_copy = yDst; yDst = 0; + + localDst = CreatePicture(0, &pixmap->drawable, + PictureMatchFormat(screen, depth, pDst->format), + 0, 0, serverClient, &error); + screen->DestroyPixmap(pixmap); + + if (!localDst) + return 0; + + ValidatePicture(localDst); + } + + pDstPix = + uxa_get_offscreen_pixmap(localDst->pDrawable, &dst_off_x, &dst_off_y); + if (!pDstPix) { + if (localDst != pDst) + FreePicture(localDst, 0); + return -1; + } + + xDst += localDst->pDrawable->x; + yDst += localDst->pDrawable->y; + + localSrc = uxa_acquire_source(screen, pSrc, + xSrc, ySrc, + width, height, + &xSrc, &ySrc); + if (!localSrc) { + if (localDst != pDst) + FreePicture(localDst, 0); + return 0; + } + + if (pMask) { + localMask = uxa_acquire_mask(screen, pMask, + xMask, yMask, + width, height, + &xMask, &yMask); + if (!localMask) { + if (localSrc != pSrc) + FreePicture(localSrc, 0); + if (localDst != pDst) + FreePicture(localDst, 0); + + return 0; + } + } + + if (!miComputeCompositeRegion(®ion, localSrc, localMask, localDst, + xSrc, ySrc, xMask, yMask, xDst, yDst, + width, height)) { + if (localSrc != pSrc) + FreePicture(localSrc, 0); + if (localMask && localMask != pMask) + FreePicture(localMask, 0); + if (localDst != pDst) + FreePicture(localDst, 0); + + return 1; + } + + if (localSrc->pDrawable) { + pSrcPix = uxa_get_offscreen_pixmap(localSrc->pDrawable, + &src_off_x, &src_off_y); + if (!pSrcPix) { + REGION_UNINIT(screen, ®ion); + + if (localSrc != pSrc) + FreePicture(localSrc, 0); + if (localMask && localMask != pMask) + FreePicture(localMask, 0); + if (localDst != pDst) + FreePicture(localDst, 0); + + return 0; + } + } else { + pSrcPix = NULL; + } + + if (localMask) { + if (localMask->pDrawable) { + pMaskPix = uxa_get_offscreen_pixmap(localMask->pDrawable, + &mask_off_x, &mask_off_y); + if (!pMaskPix) { + REGION_UNINIT(screen, ®ion); + + if (localSrc != pSrc) + FreePicture(localSrc, 0); + if (localMask && localMask != pMask) + FreePicture(localMask, 0); + if (localDst != pDst) + FreePicture(localDst, 0); + + return 0; + } + } else { + pMaskPix = NULL; + } + } + + if (!(*uxa_screen->info->prepare_composite) + (op, localSrc, localMask, localDst, pSrcPix, pMaskPix, pDstPix)) { + REGION_UNINIT(screen, ®ion); + + if (localSrc != pSrc) + FreePicture(localSrc, 0); + if (localMask && localMask != pMask) + FreePicture(localMask, 0); + if (localDst != pDst) + FreePicture(localDst, 0); + + return -1; + } + + if (pMask) { + xMask = xMask + mask_off_x - xDst; + yMask = yMask + mask_off_y - yDst; + } + + xSrc = xSrc + src_off_x - xDst; + ySrc = ySrc + src_off_y - yDst; + + nbox = REGION_NUM_RECTS(®ion); + pbox = REGION_RECTS(®ion); + while (nbox--) { + (*uxa_screen->info->composite) (pDstPix, + pbox->x1 + xSrc, + pbox->y1 + ySrc, + pbox->x1 + xMask, + pbox->y1 + yMask, + pbox->x1 + dst_off_x, + pbox->y1 + dst_off_y, + pbox->x2 - pbox->x1, + pbox->y2 - pbox->y1); + pbox++; + } + (*uxa_screen->info->done_composite) (pDstPix); + + REGION_UNINIT(screen, ®ion); + + if (localSrc != pSrc) + FreePicture(localSrc, 0); + if (localMask && localMask != pMask) + FreePicture(localMask, 0); + + if (localDst != pDst) { + GCPtr gc; + + gc = GetScratchGC(pDst->pDrawable->depth, screen); + if (gc) { + ValidateGC(pDst->pDrawable, gc); + gc->ops->CopyArea(localDst->pDrawable, pDst->pDrawable, gc, + 0, 0, width, height, xDst_copy, yDst_copy); + FreeScratchGC(gc); + } + + FreePicture(localDst, 0); + } + + 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) +{ + ScreenPtr screen = pDst->pDrawable->pScreen; + uxa_screen_t *uxa_screen = uxa_get_screen(screen); + PicturePtr localDst = pDst; + int xDst_copy, yDst_copy; + + assert(op == PictOpOver); + + if (uxa_screen->info->check_composite && + (!(*uxa_screen->info->check_composite) (PictOpOutReverse, pSrc, + pMask, pDst, width, height) + || !(*uxa_screen->info->check_composite) (PictOpAdd, pSrc, pMask, + pDst, width, height))) { + return -1; + } + + if (uxa_screen->info->check_composite_target && + !uxa_screen->info->check_composite_target(uxa_get_drawable_pixmap(pDst->pDrawable))) { + int depth = pDst->pDrawable->depth; + PixmapPtr pixmap; + int error; + GCPtr gc; + + pixmap = uxa_get_drawable_pixmap(pDst->pDrawable); + if (uxa_screen->info->check_copy && + !uxa_screen->info->check_copy(pixmap, pixmap, GXcopy, FB_ALLONES)) + return -1; + + pixmap = screen->CreatePixmap(screen, + width, height, depth, + CREATE_PIXMAP_USAGE_SCRATCH); + if (!pixmap) + return 0; + + gc = GetScratchGC(depth, screen); + if (!gc) { + screen->DestroyPixmap(pixmap); + return 0; + } + + ValidateGC(&pixmap->drawable, gc); + gc->ops->CopyArea(pDst->pDrawable, &pixmap->drawable, gc, + xDst, yDst, width, height, 0, 0); + FreeScratchGC(gc); + + xDst_copy = xDst; xDst = 0; + yDst_copy = yDst; yDst = 0; + + localDst = CreatePicture(0, &pixmap->drawable, + PictureMatchFormat(screen, depth, pDst->format), + 0, 0, serverClient, &error); + screen->DestroyPixmap(pixmap); + + if (!localDst) + return 0; + + ValidatePicture(localDst); + } + + /* 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, localDst, + 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, localDst, + xSrc, ySrc, + xMask, yMask, + xDst, yDst, + width, height); + + if (localDst != pDst) { + GCPtr gc; + + gc = GetScratchGC(pDst->pDrawable->depth, screen); + if (gc) { + ValidateGC(pDst->pDrawable, gc); + gc->ops->CopyArea(localDst->pDrawable, pDst->pDrawable, gc, + 0, 0, width, height, xDst_copy, yDst_copy); + FreeScratchGC(gc); + } + + FreePicture(localDst, 0); + } + + return 1; +} + +static int +compatible_formats (CARD8 op, PicturePtr dst, PicturePtr src) +{ + if (op == PictOpSrc) { + if (src->format == dst->format) + return 1; + + /* Is the destination an alpha-less version of source? */ + if (dst->format == PICT_FORMAT(PICT_FORMAT_BPP(src->format), + PICT_FORMAT_TYPE(src->format), + 0, + PICT_FORMAT_R(src->format), + PICT_FORMAT_G(src->format), + PICT_FORMAT_B(src->format))) + return 1; + + /* XXX xrgb is promoted to argb during image upload... */ +#if 0 + if (dst->format == PICT_a8r8g8b8 && src->format == PICT_x8r8g8b8) + return 1; +#endif + } else if (op == PictOpOver) { + if (PICT_FORMAT_A(src->format)) + return 0; + + return src->format == dst->format; + } + + return 0; +} + +static int +drawable_contains (DrawablePtr drawable, int x, int y, int w, int h) +{ + if (x < 0 || y < 0) + return FALSE; + + if (x + w > drawable->width) + return FALSE; + + if (y + h > drawable->height) + return FALSE; + + return TRUE; +} + +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; + int tx, ty; + + if (uxa_screen->swappedOut || uxa_screen->force_fallback) + goto fallback; + + if (!uxa_drawable_is_offscreen(pDst->pDrawable)) + goto fallback; + + if (pDst->alphaMap || pSrc->alphaMap || (pMask && pMask->alphaMap)) + goto fallback; + + /* Remove repeat in source if useless */ + if (pSrc->pDrawable && pSrc->repeat && pSrc->filter != PictFilterConvolution && + transform_is_integer_translation(pSrc->transform, &tx, &ty) && + (pSrc->pDrawable->width > 1 || pSrc->pDrawable->height > 1) && + drawable_contains(pSrc->pDrawable, xSrc + tx, ySrc + ty, width, height)) + pSrc->repeat = 0; + + if (!pMask) { + if (op == PictOpClear) { + PicturePtr clear = uxa_solid_clear(pDst->pDrawable->pScreen); + if (clear && + uxa_try_driver_solid_fill(clear, pDst, + xSrc, ySrc, + xDst, yDst, + width, height) == 1) + goto done; + } + + if (pSrc->pDrawable == NULL) { + if (pSrc->pSourcePict) { + SourcePict *source = pSrc->pSourcePict; + if (source->type == SourcePictTypeSolidFill) { + if (op == PictOpSrc || + (op == PictOpOver && + (source->solidFill.color & 0xff000000) == 0xff000000)) { + ret = uxa_try_driver_solid_fill(pSrc, pDst, + xSrc, ySrc, + xDst, yDst, + width, height); + if (ret == 1) + goto done; + } + } + } + } else if (pSrc->pDrawable->width == 1 && + pSrc->pDrawable->height == 1 && + pSrc->repeat && + (op == PictOpSrc || (op == PictOpOver && !PICT_FORMAT_A(pSrc->format)))) { + ret = uxa_try_driver_solid_fill(pSrc, pDst, + xSrc, ySrc, + xDst, yDst, + width, height); + if (ret == 1) + goto done; + } else if (compatible_formats (op, pDst, pSrc) && + pSrc->filter != PictFilterConvolution && + transform_is_integer_translation(pSrc->transform, &tx, &ty)) { + if (!pSrc->repeat && + drawable_contains(pSrc->pDrawable, + xSrc + tx, ySrc + ty, + width, height)) { + xDst += pDst->pDrawable->x; + yDst += pDst->pDrawable->y; + xSrc += pSrc->pDrawable->x + tx; + ySrc += pSrc->pDrawable->y + ty; + + 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->repeat && pSrc->repeatType == RepeatNormal && + pSrc->pDrawable->type == DRAWABLE_PIXMAP) { + DDXPointRec patOrg; + + /* Let's see if the driver can do the repeat + * in one go + */ + if (uxa_screen->info->prepare_composite) { + 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 + tx; + ySrc += pSrc->pDrawable->y + ty; + + 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->pDrawable && pMask->repeat && + pMask->filter != PictFilterConvolution && + transform_is_integer_translation(pMask->transform, &tx, &ty) && + (pMask->pDrawable->width > 1 || pMask->pDrawable->height > 1) && + drawable_contains(pMask->pDrawable, xMask + tx, yMask + ty, width, height)) + pMask->repeat = 0; + + if (uxa_screen->info->prepare_composite) { + 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 ? + pSrc->pDrawable->width == 1 && + pSrc->pDrawable->height == 1 && + pSrc->repeat : + pSrc->pSourcePict ? + pSrc->pSourcePict->type == SourcePictTypeSolidFill : + 0; + + /* 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: + uxa_print_composite_fallback("uxa_composite", + op, pSrc, pMask, pDst); + + 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; + int error; + + 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, + UXA_CREATE_PIXMAP_FOR_MAP); + if (!pPixmap) + return 0; + 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 src, PicturePtr dst, + PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc, + int ntrap, xTrapezoid * traps) +{ + ScreenPtr screen = dst->pDrawable->pScreen; + BoxRec bounds; + Bool direct; + + direct = op == PictOpAdd && miIsSolidAlpha(src); + 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 = dst->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; + + if (uxa_prepare_access(pDraw, UXA_ACCESS_RW)) { + PictureScreenPtr ps = GetPictureScreen(screen); + + for (; ntrap; ntrap--, traps++) + (*ps->RasterizeTrapezoid) (dst, traps, 0, 0); + uxa_finish_access(pDraw); + } + } else if (maskFormat) { + PixmapPtr scratch = NULL; + PicturePtr mask; + INT16 xDst, yDst; + INT16 xRel, yRel; + int width, height; + pixman_image_t *image; + pixman_format_code_t format; + + xDst = traps[0].left.p1.x >> 16; + yDst = traps[0].left.p1.y >> 16; + + width = bounds.x2 - bounds.x1; + height = bounds.y2 - bounds.y1; + + format = maskFormat->format | + (BitsPerPixel(maskFormat->depth) << 24); + image = + pixman_image_create_bits(format, width, height, NULL, 0); + if (!image) + return; + + for (; ntrap; ntrap--, traps++) + pixman_rasterize_trapezoid(image, + (pixman_trapezoid_t *) traps, + -bounds.x1, -bounds.y1); + if (uxa_drawable_is_offscreen(dst->pDrawable)) { + mask = uxa_picture_from_pixman_image(screen, image, format); + } else { + int error; + + scratch = GetScratchPixmapHeader(screen, width, height, + PIXMAN_FORMAT_DEPTH(format), + PIXMAN_FORMAT_BPP(format), + pixman_image_get_stride(image), + pixman_image_get_data(image)); + mask = CreatePicture(0, &scratch->drawable, + PictureMatchFormat(screen, + PIXMAN_FORMAT_DEPTH(format), + format), + 0, 0, serverClient, &error); + } + if (!mask) { + if (scratch) + FreeScratchPixmapHeader(scratch); + pixman_image_unref(image); + return; + } + + xRel = bounds.x1 + xSrc - xDst; + yRel = bounds.y1 + ySrc - yDst; + CompositePicture(op, src, mask, dst, + xRel, yRel, + 0, 0, + bounds.x1, bounds.y1, + width, height); + FreePicture(mask, 0); + + if (scratch) + FreeScratchPixmapHeader(scratch); + pixman_image_unref(image); + } else { + if (dst->polyEdge == PolyEdgeSharp) + maskFormat = PictureMatchFormat(screen, 1, PICT_a1); + else + maskFormat = PictureMatchFormat(screen, 8, PICT_a8); + for (; ntrap; ntrap--, traps++) + uxa_trapezoids(op, src, dst, 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; + if (uxa_prepare_access(pDraw, UXA_ACCESS_RW)) { + (*ps->AddTriangles) (pDst, 0, 0, ntri, tris); + uxa_finish_access(pDraw); + } + } else if (maskFormat) { + PicturePtr pPicture; + INT16 xDst, yDst; + INT16 xRel, yRel; + int width = bounds.x2 - bounds.x1; + int height = bounds.y2 - bounds.y1; + GCPtr pGC; + xRectangle rect; + + xDst = tris[0].p1.x >> 16; + yDst = tris[0].p1.y >> 16; + + pPicture = uxa_create_alpha_picture(pScreen, pDst, maskFormat, + width, height); + if (!pPicture) + return; + + /* Clear the alpha picture to 0. */ + pGC = GetScratchGC(pPicture->pDrawable->depth, pScreen); + if (!pGC) { + FreePicture(pPicture, 0); + return; + } + ValidateGC(pPicture->pDrawable, pGC); + rect.x = 0; + rect.y = 0; + rect.width = width; + rect.height = height; + uxa_check_poly_fill_rect(pPicture->pDrawable, pGC, 1, &rect); + FreeScratchGC(pGC); + + if (uxa_prepare_access(pPicture->pDrawable, UXA_ACCESS_RW)) { + (*ps->AddTriangles) (pPicture, -bounds.x1, -bounds.y1, + ntri, tris); + uxa_finish_access(pPicture->pDrawable); + } + + 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/src/uxa/uxa-unaccel.c b/src/uxa/uxa-unaccel.c new file mode 100644 index 0000000..15be821 --- /dev/null +++ b/src/uxa/uxa-unaccel.c @@ -0,0 +1,430 @@ +/* + * + * 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. + */ +Bool uxa_prepare_access_gc(GCPtr pGC) +{ + if (pGC->stipple) + if (!uxa_prepare_access(&pGC->stipple->drawable, UXA_ACCESS_RO)) + return FALSE; + if (pGC->fillStyle == FillTiled) + if (!uxa_prepare_access + (&pGC->tile.pixmap->drawable, UXA_ACCESS_RO)) { + if (pGC->stipple) + uxa_finish_access(&pGC->stipple->drawable); + return FALSE; + } + return TRUE; +} + +/** + * 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); + if (pGC->stipple) + uxa_finish_access(&pGC->stipple->drawable); +} + +char uxa_drawable_location(DrawablePtr pDrawable) +{ + return uxa_drawable_is_offscreen(pDrawable) ? 's' : 'm'; +} + +void +uxa_check_fill_spans(DrawablePtr pDrawable, GCPtr pGC, int nspans, + DDXPointPtr ppt, int *pwidth, int fSorted) +{ + ScreenPtr screen = pDrawable->pScreen; + + UXA_FALLBACK(("to %p (%c)\n", pDrawable, + uxa_drawable_location(pDrawable))); + if (uxa_prepare_access(pDrawable, UXA_ACCESS_RW)) { + if (uxa_prepare_access_gc(pGC)) { + fbFillSpans(pDrawable, pGC, nspans, ppt, pwidth, + fSorted); + uxa_finish_access_gc(pGC); + } + uxa_finish_access(pDrawable); + } +} + +void +uxa_check_set_spans(DrawablePtr pDrawable, GCPtr pGC, char *psrc, + DDXPointPtr ppt, int *pwidth, int nspans, int fSorted) +{ + ScreenPtr screen = pDrawable->pScreen; + + UXA_FALLBACK(("to %p (%c)\n", pDrawable, + uxa_drawable_location(pDrawable))); + if (uxa_prepare_access(pDrawable, UXA_ACCESS_RW)) { + fbSetSpans(pDrawable, pGC, psrc, ppt, pwidth, nspans, fSorted); + uxa_finish_access(pDrawable); + } +} + +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) +{ + ScreenPtr screen = pDrawable->pScreen; + + UXA_FALLBACK(("to %p (%c)\n", pDrawable, + uxa_drawable_location(pDrawable))); + if (uxa_prepare_access(pDrawable, UXA_ACCESS_RW)) { + fbPutImage(pDrawable, pGC, depth, x, y, w, h, leftPad, format, + bits); + uxa_finish_access(pDrawable); + } +} + +RegionPtr +uxa_check_copy_area(DrawablePtr pSrc, DrawablePtr pDst, GCPtr pGC, + int srcx, int srcy, int w, int h, int dstx, int dsty) +{ + ScreenPtr screen = pSrc->pScreen; + RegionPtr ret = NULL; + + UXA_FALLBACK(("from %p to %p (%c,%c)\n", pSrc, pDst, + uxa_drawable_location(pSrc), + uxa_drawable_location(pDst))); + if (uxa_prepare_access(pDst, UXA_ACCESS_RW)) { + if (uxa_prepare_access(pSrc, UXA_ACCESS_RO)) { + ret = + fbCopyArea(pSrc, pDst, pGC, srcx, srcy, w, h, dstx, + dsty); + uxa_finish_access(pSrc); + } + uxa_finish_access(pDst); + } + 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) +{ + ScreenPtr screen = pSrc->pScreen; + RegionPtr ret = NULL; + + UXA_FALLBACK(("from %p to %p (%c,%c)\n", pSrc, pDst, + uxa_drawable_location(pSrc), + uxa_drawable_location(pDst))); + if (uxa_prepare_access(pDst, UXA_ACCESS_RW)) { + if (uxa_prepare_access(pSrc, UXA_ACCESS_RO)) { + ret = + fbCopyPlane(pSrc, pDst, pGC, srcx, srcy, w, h, dstx, + dsty, bitPlane); + uxa_finish_access(pSrc); + } + uxa_finish_access(pDst); + } + return ret; +} + +void +uxa_check_poly_point(DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, + DDXPointPtr pptInit) +{ + ScreenPtr screen = pDrawable->pScreen; + + UXA_FALLBACK(("to %p (%c)\n", pDrawable, + uxa_drawable_location(pDrawable))); + if (uxa_prepare_access(pDrawable, UXA_ACCESS_RW)) { + fbPolyPoint(pDrawable, pGC, mode, npt, pptInit); + uxa_finish_access(pDrawable); + } +} + +void +uxa_check_poly_lines(DrawablePtr pDrawable, GCPtr pGC, + int mode, int npt, DDXPointPtr ppt) +{ + ScreenPtr screen = pDrawable->pScreen; + + 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) { + if (uxa_prepare_access(pDrawable, UXA_ACCESS_RW)) { + if (uxa_prepare_access_gc(pGC)) { + fbPolyLine(pDrawable, pGC, mode, npt, ppt); + uxa_finish_access_gc(pGC); + } + uxa_finish_access(pDrawable); + } + 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) +{ + ScreenPtr screen = pDrawable->pScreen; + + UXA_FALLBACK(("to %p (%c) width %d, count %d\n", pDrawable, + uxa_drawable_location(pDrawable), pGC->lineWidth, + nsegInit)); + if (pGC->lineWidth == 0) { + if (uxa_prepare_access(pDrawable, UXA_ACCESS_RW)) { + if (uxa_prepare_access_gc(pGC)) { + fbPolySegment(pDrawable, pGC, nsegInit, + pSegInit); + uxa_finish_access_gc(pGC); + } + uxa_finish_access(pDrawable); + } + 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) +{ + ScreenPtr screen = pDrawable->pScreen; + + 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) { + if (uxa_prepare_access(pDrawable, UXA_ACCESS_RW)) { + if (uxa_prepare_access_gc(pGC)) { + fbPolyArc(pDrawable, pGC, narcs, pArcs); + uxa_finish_access_gc(pGC); + } + uxa_finish_access(pDrawable); + } + return; + } +#endif + miPolyArc(pDrawable, pGC, narcs, pArcs); +} + +void +uxa_check_poly_fill_rect(DrawablePtr pDrawable, GCPtr pGC, + int nrect, xRectangle * prect) +{ + ScreenPtr screen = pDrawable->pScreen; + + UXA_FALLBACK(("to %p (%c)\n", pDrawable, + uxa_drawable_location(pDrawable))); + + if (uxa_prepare_access(pDrawable, UXA_ACCESS_RW)) { + if (uxa_prepare_access_gc(pGC)) { + fbPolyFillRect(pDrawable, pGC, nrect, prect); + uxa_finish_access_gc(pGC); + } + uxa_finish_access(pDrawable); + } +} + +void +uxa_check_image_glyph_blt(DrawablePtr pDrawable, GCPtr pGC, + int x, int y, unsigned int nglyph, + CharInfoPtr * ppci, pointer pglyphBase) +{ + ScreenPtr screen = pDrawable->pScreen; + + UXA_FALLBACK(("to %p (%c)\n", pDrawable, + uxa_drawable_location(pDrawable))); + if (uxa_prepare_access(pDrawable, UXA_ACCESS_RW)) { + if (uxa_prepare_access_gc(pGC)) { + fbImageGlyphBlt(pDrawable, pGC, x, y, nglyph, ppci, + pglyphBase); + uxa_finish_access_gc(pGC); + } + uxa_finish_access(pDrawable); + } +} + +void +uxa_check_poly_glyph_blt(DrawablePtr pDrawable, GCPtr pGC, + int x, int y, unsigned int nglyph, + CharInfoPtr * ppci, pointer pglyphBase) +{ + ScreenPtr screen = pDrawable->pScreen; + + UXA_FALLBACK(("to %p (%c), style %d alu %d\n", pDrawable, + uxa_drawable_location(pDrawable), pGC->fillStyle, + pGC->alu)); + if (uxa_prepare_access(pDrawable, UXA_ACCESS_RW)) { + if (uxa_prepare_access_gc(pGC)) { + fbPolyGlyphBlt(pDrawable, pGC, x, y, nglyph, ppci, + pglyphBase); + uxa_finish_access_gc(pGC); + } + uxa_finish_access(pDrawable); + } +} + +void +uxa_check_push_pixels(GCPtr pGC, PixmapPtr pBitmap, + DrawablePtr pDrawable, int w, int h, int x, int y) +{ + ScreenPtr screen = pDrawable->pScreen; + + UXA_FALLBACK(("from %p to %p (%c,%c)\n", pBitmap, pDrawable, + uxa_drawable_location(&pBitmap->drawable), + uxa_drawable_location(pDrawable))); + if (uxa_prepare_access(pDrawable, UXA_ACCESS_RW)) { + if (uxa_prepare_access(&pBitmap->drawable, UXA_ACCESS_RO)) { + if (uxa_prepare_access_gc(pGC)) { + fbPushPixels(pGC, pBitmap, pDrawable, w, h, x, + y); + uxa_finish_access_gc(pGC); + } + uxa_finish_access(&pBitmap->drawable); + } + uxa_finish_access(pDrawable); + } +} + +void +uxa_check_get_spans(DrawablePtr pDrawable, + int wMax, + DDXPointPtr ppt, int *pwidth, int nspans, char *pdstStart) +{ + ScreenPtr screen = pDrawable->pScreen; + + UXA_FALLBACK(("from %p (%c)\n", pDrawable, + uxa_drawable_location(pDrawable))); + if (uxa_prepare_access(pDrawable, UXA_ACCESS_RO)) { + fbGetSpans(pDrawable, wMax, ppt, pwidth, nspans, pdstStart); + uxa_finish_access(pDrawable); + } +} + +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) +{ + ScreenPtr screen = pDst->pDrawable->pScreen; + + UXA_FALLBACK(("from picts %p/%p to pict %p\n", pSrc, pMask, pDst)); + + if (uxa_prepare_access(pDst->pDrawable, UXA_ACCESS_RW)) { + if (pSrc->pDrawable == NULL || + uxa_prepare_access(pSrc->pDrawable, UXA_ACCESS_RO)) { + if (!pMask || pMask->pDrawable == NULL || + uxa_prepare_access(pMask->pDrawable, UXA_ACCESS_RO)) + { + fbComposite(op, pSrc, pMask, pDst, + xSrc, ySrc, + xMask, yMask, + xDst, yDst, + width, height); + if (pMask && pMask->pDrawable != NULL) + uxa_finish_access(pMask->pDrawable); + } + if (pSrc->pDrawable != NULL) + uxa_finish_access(pSrc->pDrawable); + } + uxa_finish_access(pDst->pDrawable); + } +} + +void +uxa_check_add_traps(PicturePtr pPicture, + INT16 x_off, INT16 y_off, int ntrap, xTrap * traps) +{ + ScreenPtr screen = pPicture->pDrawable->pScreen; + + UXA_FALLBACK(("to pict %p (%c)\n", pPicture, + uxa_drawable_location(pPicture->pDrawable))); + if (uxa_prepare_access(pPicture->pDrawable, UXA_ACCESS_RW)) { + fbAddTraps(pPicture, x_off, y_off, ntrap, traps); + uxa_finish_access(pPicture->pDrawable); + } +} + +/** + * 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; + + if (!uxa_prepare_access(&pPixmap->drawable, UXA_ACCESS_RO)) + return 0; + + 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); + + return pixel; +} diff --git a/src/uxa/uxa.c b/src/uxa/uxa.c new file mode 100644 index 0000000..b661bb8 --- /dev/null +++ b/src/uxa/uxa.c @@ -0,0 +1,579 @@ +/* + * 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" + +int uxa_screen_index; + +/** + * 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->pixmap_is_offscreen) + return uxa_screen->info->pixmap_is_offscreen(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. + */ +Bool uxa_prepare_access(DrawablePtr pDrawable, uxa_access_t access) +{ + 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 TRUE; + + if (uxa_screen->info->prepare_access) + return (*uxa_screen->info->prepare_access) (pPixmap, access); + return TRUE; +} + +/** + * uxa_finish_access() is UXA's wrapper for the driver's finish_access() handler. + * + * It deals with calling the driver's finish_access() only if necessary. + */ +void uxa_finish_access(DrawablePtr pDrawable) +{ + ScreenPtr pScreen = pDrawable->pScreen; + uxa_screen_t *uxa_screen = uxa_get_screen(pScreen); + PixmapPtr pPixmap = uxa_get_drawable_pixmap(pDrawable); + + if (uxa_screen->info->finish_access == NULL) + return; + + if (!uxa_pixmap_is_offscreen(pPixmap)) + return; + + (*uxa_screen->info->finish_access) (pPixmap); +} + +/** + * 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/finish_access. + */ +#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. + */ + if (uxa_prepare_access + (&pOldTile->drawable, UXA_ACCESS_RO)) { + pNewTile = + fb24_32ReformatTile(pOldTile, + pDrawable-> + bitsPerPixel); + uxa_finish_access(&pOldTile->drawable); + } + } + 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)) { + if (uxa_prepare_access + (&pGC->tile.pixmap->drawable, UXA_ACCESS_RW)) { + fbPadPixmap(pGC->tile.pixmap); + uxa_finish_access(&pGC->tile.pixmap->drawable); + } + } + /* Mask out the GCTile change notification, now that we've + * done FB's job for it. + */ + changes &= ~GCTile; + } + + if (changes & GCStipple && pGC->stipple) { + /* We can't inline stipple handling like we do for GCTile + * because it sets fbgc privates. + */ + if (uxa_prepare_access(&pGC->stipple->drawable, UXA_ACCESS_RW)) { + fbValidateGC(pGC, changes, pDrawable); + uxa_finish_access(&pGC->stipple->drawable); + } + } else { + fbValidateGC(pGC, changes, pDrawable); + } + + 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; +} + +Bool uxa_prepare_access_window(WindowPtr pWin) +{ + if (pWin->backgroundState == BackgroundPixmap) { + if (!uxa_prepare_access + (&pWin->background.pixmap->drawable, UXA_ACCESS_RO)) + return FALSE; + } + + if (pWin->borderIsPixel == FALSE) { + if (!uxa_prepare_access + (&pWin->border.pixmap->drawable, UXA_ACCESS_RO)) { + if (pWin->backgroundState == BackgroundPixmap) + uxa_finish_access(&pWin->background.pixmap-> + drawable); + return FALSE; + } + } + return TRUE; +} + +void uxa_finish_access_window(WindowPtr pWin) +{ + if (pWin->backgroundState == BackgroundPixmap) + uxa_finish_access(&pWin->background.pixmap->drawable); + + if (pWin->borderIsPixel == FALSE) + uxa_finish_access(&pWin->border.pixmap->drawable); +} + +static Bool uxa_change_window_attributes(WindowPtr pWin, unsigned long mask) +{ + Bool ret; + + if (!uxa_prepare_access_window(pWin)) + return FALSE; + ret = fbChangeWindowAttributes(pWin, mask); + uxa_finish_access_window(pWin); + return ret; +} + +static RegionPtr uxa_bitmap_to_region(PixmapPtr pPix) +{ + RegionPtr ret; + if (!uxa_prepare_access(&pPix->drawable, UXA_ACCESS_RO)) + return NULL; + ret = fbPixmapToRegion(pPix); + uxa_finish_access(&pPix->drawable); + return ret; +} + +static void uxa_xorg_enable_disable_fb_access(int index, Bool enable) +{ + ScreenPtr screen = screenInfo.screens[index]; + uxa_screen_t *uxa_screen = uxa_get_screen(screen); + + if (!enable && uxa_screen->disableFbCount++ == 0) + uxa_screen->swappedOut = TRUE; + + if (enable && --uxa_screen->disableFbCount == 0) + uxa_screen->swappedOut = FALSE; + + if (uxa_screen->SavedEnableDisableFBAccess) + uxa_screen->SavedEnableDisableFBAccess(index, enable); +} + +void uxa_set_fallback_debug(ScreenPtr screen, Bool enable) +{ + uxa_screen_t *uxa_screen = uxa_get_screen(screen); + + uxa_screen->fallback_debug = enable; +} + +void uxa_set_force_fallback(ScreenPtr screen, Bool value) +{ + uxa_screen_t *uxa_screen = uxa_get_screen(screen); + + uxa_screen->force_fallback = value; +} + +/** + * 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); + ScrnInfoPtr scrn = xf86Screens[pScreen->myNum]; +#ifdef RENDER + PictureScreenPtr ps = GetPictureScreenIfSet(pScreen); +#endif + int n; + + if (uxa_screen->solid_clear) + FreePicture(uxa_screen->solid_clear, 0); + if (uxa_screen->solid_black) + FreePicture(uxa_screen->solid_black, 0); + if (uxa_screen->solid_white) + FreePicture(uxa_screen->solid_white, 0); + for (n = 0; n < uxa_screen->solid_cache_size; n++) + FreePicture(uxa_screen->solid_cache[n].picture, 0); + + 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; + scrn->EnableDisableFBAccess = uxa_screen->SavedEnableDisableFBAccess; +#ifdef RENDER + if (ps) { + ps->Composite = uxa_screen->SavedComposite; + ps->CompositeRects = uxa_screen->SavedCompositeRects; + ps->Glyphs = uxa_screen->SavedGlyphs; + ps->Trapezoids = uxa_screen->SavedTrapezoids; + ps->AddTraps = uxa_screen->SavedAddTraps; + ps->Triangles = uxa_screen->SavedTriangles; + + ps->UnrealizeGlyph = uxa_screen->SavedUnrealizeGlyph; + } +#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; + ScrnInfoPtr scrn = xf86Screens[screen->myNum]; + + 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->prepare_solid) { + LogMessage(X_ERROR, + "UXA(%d): uxa_driver_t::prepare_solid must be " + "non-NULL\n", screen->myNum); + return FALSE; + } + + if (!uxa_driver->prepare_copy) { + LogMessage(X_ERROR, + "UXA(%d): uxa_driver_t::prepare_copy must be " + "non-NULL\n", screen->myNum); + return FALSE; + } + 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_index, uxa_screen); + + uxa_screen->force_fallback = FALSE; + + uxa_screen->solid_cache_size = 0; + uxa_screen->solid_clear = 0; + uxa_screen->solid_black = 0; + uxa_screen->solid_white = 0; + +// 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; + + uxa_screen->SavedEnableDisableFBAccess = scrn->EnableDisableFBAccess; + scrn->EnableDisableFBAccess = uxa_xorg_enable_disable_fb_access; + +#ifdef RENDER + { + PictureScreenPtr ps = GetPictureScreenIfSet(screen); + if (ps) { + uxa_screen->SavedComposite = ps->Composite; + ps->Composite = uxa_composite; + + uxa_screen->SavedCompositeRects = ps->CompositeRects; + ps->CompositeRects = uxa_solid_rects; + + uxa_screen->SavedGlyphs = ps->Glyphs; + ps->Glyphs = uxa_glyphs; + + uxa_screen->SavedUnrealizeGlyph = ps->UnrealizeGlyph; + ps->UnrealizeGlyph = uxa_glyph_unrealize; + + 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 + + if (!uxa_glyphs_init(screen)) + return FALSE; + + LogMessage(X_INFO, + "UXA(%d): Driver registered support for the following" + " operations:\n", screen->myNum); + assert(uxa_driver->prepare_solid != NULL); + LogMessage(X_INFO, " solid\n"); + assert(uxa_driver->prepare_copy != NULL); + LogMessage(X_INFO, " copy\n"); + if (uxa_driver->prepare_composite != NULL) { + LogMessage(X_INFO, " composite (RENDER acceleration)\n"); + } + if (uxa_driver->put_image != NULL) { + LogMessage(X_INFO, " put_image\n"); + } + if (uxa_driver->get_image != NULL) { + LogMessage(X_INFO, " get_image\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 */ +} diff --git a/src/uxa/uxa.h b/src/uxa/uxa.h new file mode 100644 index 0000000..cb08665 --- /dev/null +++ b/src/uxa/uxa.h @@ -0,0 +1,583 @@ +/* + * 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 + +typedef enum { + UXA_ACCESS_RO, + UXA_ACCESS_RW +} uxa_access_t; + +/** + * 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; + + /** @name solid + * @{ + */ + /** + * check_solid() checks whether the driver can do a solid fill to this drawable. + * @param pDrawable Destination drawable + * @param alu raster operation + * @param planemask write mask for the fill + * + * The check_solid() call is recommended if prepare_solid() is + * implemented, but is not required. + */ + Bool(*check_solid) (DrawablePtr pDrawable, int alu, Pixel planemask); + + /** + * prepare_solid() 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 prepare_solid() call is required of all drivers, but it may fail + * for any reason. Failure results in a fallback to software rendering. + */ + Bool(*prepare_solid) (PixmapPtr pPixmap, + int alu, Pixel planemask, Pixel fg); + + /** + * solid() performs a solid fill set up in the last prepare_solid() + * 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 prepare_solid() 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 prepare_solid() ever succeeds. + */ + void (*solid) (PixmapPtr pPixmap, int x1, int y1, int x2, int y2); + + /** + * done_solid() finishes a set of solid fills. + * + * @param pPixmap destination pixmap. + * + * The done_solid() call is called at the end of a series of consecutive + * solid() calls following a successful prepare_solid(). This allows + * drivers to finish up emitting drawing commands that were buffered, or + * clean up state from prepare_solid(). + * + * This call is required if prepare_solid() ever succeeds. + */ + void (*done_solid) (PixmapPtr pPixmap); + /** @} */ + + /** @name copy + * @{ + */ + /** + * check_copy() checks whether the driver can blit between the two Pictures + */ + Bool(*check_copy) (PixmapPtr pSrc, PixmapPtr pDst, int alu, Pixel planemask); + /** + * prepare_copy() 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 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 prepare_copy() call is required of all drivers, but it may fail + * for any reason. Failure results in a fallback to software rendering. + */ + Bool(*prepare_copy) (PixmapPtr pSrcPixmap, + PixmapPtr pDstPixmap, + int dx, int dy, int alu, Pixel planemask); + + /** + * copy() performs a copy set up in the last prepare_copy 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 prepare_copy() 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 prepare_copy(). 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 prepare_copy ever succeeds. + */ + void (*copy) (PixmapPtr pDstPixmap, + int srcX, + int srcY, int dstX, int dstY, int width, int height); + + /** + * done_copy() finishes a set of copies. + * + * @param pPixmap destination pixmap. + * + * The done_copy() call is called at the end of a series of consecutive + * copy() calls following a successful prepare_copy(). This allows + * drivers to finish up emitting drawing commands that were buffered, + * or clean up state from prepare_copy(). + * + * This call is required if prepare_copy() ever succeeds. + */ + void (*done_copy) (PixmapPtr pDstPixmap); + /** @} */ + + /** @name composite + * @{ + */ + /** + * check_composite() 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 + * @param width The width of the composite operation + * @param height The height of the composite operation + * + * The check_composite() 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. + * + * See prepare_composite() for more details on likely issues that + * drivers will have in accelerating composite operations. + * + * The check_composite() call is recommended if prepare_composite() is + * implemented, but is not required. + */ + Bool(*check_composite) (int op, + PicturePtr pSrcPicture, + PicturePtr pMaskPicture, + PicturePtr pDstPicture, + int width, int height); + + /** + * check_composite_target() checks to see if the destination of the composite + * operation can be used without midification. + * + * @param pixmap Destination Pixmap + * + * The check_composite_target() call is recommended if prepare_composite() is + * implemented, but is not required. + */ + Bool(*check_composite_target) (PixmapPtr pixmap); + + /** + * check_composite_texture() checks to see if a source to the composite + * operation can be used without midification. + * + * @param pScreen Screen + * @param pPicture Picture + * + * The check_composite_texture() call is recommended if prepare_composite() is + * implemented, but is not required. + */ + Bool(*check_composite_texture) (ScreenPtr pScreen, + PicturePtr pPicture); + + /** + * prepare_composite() 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 prepare_composite 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 prepare_composite() 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(*prepare_composite) (int op, + PicturePtr pSrcPicture, + PicturePtr pMaskPicture, + PicturePtr pDstPicture, + PixmapPtr pSrc, + PixmapPtr pMask, PixmapPtr pDst); + + /** + * composite() performs a composite operation set up in the last + * prepare_composite() 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 + * prepare_composite() 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 prepare_composite() ever succeeds. + */ + void (*composite) (PixmapPtr pDst, + int srcX, + int srcY, + int maskX, + int maskY, + int dstX, int dstY, int width, int height); + + /** + * done_composite() finishes a set of composite operations. + * + * @param pPixmap destination pixmap. + * + * The done_composite() call is called at the end of a series of + * consecutive composite() calls following a successful + * prepare_composite(). This allows drivers to finish up emitting + * drawing commands that were buffered, or clean up state from + * prepare_composite(). + * + * This call is required if prepare_composite() ever succeeds. + */ + void (*done_composite) (PixmapPtr pDst); + /** @} */ + + /** + * put_image() 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. + * + * put_image() 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. + * + * put_image() 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. + * + * put_image() is not required, but is recommended if composite + * acceleration is supported. + */ + Bool(*put_image) (PixmapPtr pDst, + int x, + int y, int w, int h, char *src, int src_pitch); + + /** + * get_image() 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. + * + * get_image() 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. + * + * @return TRUE if the driver successfully downloaded the data. FALSE + * indicates that UXA should fall back to doing the download in + * software. + * + * get_image() is not required, but is highly recommended. + */ + Bool(*get_image) (PixmapPtr pSrc, + int x, int y, + int w, int h, char *dst, int dst_pitch); + + /** @{ */ + /** + * prepare_access() is called before CPU access to an offscreen pixmap. + * + * @param pPix the pixmap being accessed + * @param index the index of the pixmap being accessed. + * + * prepare_access() 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 prepare_access() called on them + * per operation, drivers can have a small, statically-allocated space + * to maintain state for prepare_access() and finish_access() in. + * Note that the same pixmap may have prepare_access() called on it + * more than once, for uxample when doing a copy within the same + * pixmap (so it gets prepare_access as + * #UXA_PREPARE_DEST and then as #UXA_PREPARE_SRC). + * + * prepare_access() may fail. An example might be the case of + * hardware that can set up 1 or 2 surfaces for CPU access, but not + * 3. If prepare_access() + * fails, UXA will migrate the pixmap to system memory. + * get_image() must be implemented and must not fail if a driver + * wishes to fail in prepare_access(). prepare_access() must not + * fail when pPix is the visible screen, because the visible screen + * can not be migrated. + * + * @return TRUE if prepare_access() successfully prepared the pixmap + * for CPU drawing. + * @return FALSE if prepare_access() is unsuccessful and UXA should use + * get_image() to migate the pixmap out. + */ + Bool(*prepare_access) (PixmapPtr pPix, uxa_access_t access); + + /** + * finish_access() is called after CPU access to an offscreen pixmap. + * + * @param pPix the pixmap being accessed + * @param index the index of the pixmap being accessed. + * + * finish_access() will be called after finishing CPU access of an + * offscreen pixmap set up by prepare_access(). Note that the + * finish_access() will not be called if prepare_access() failed. + */ + void (*finish_access) (PixmapPtr pPix); + + /** + * 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 + * prepare_access()/finish_access() when accessing it with the CPU. + */ + Bool(*pixmap_is_offscreen) (PixmapPtr pPix); + + /** @} */ +} 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) + +/** @} */ + +/** @name UXA CreatePixmap hint flags + * @{ + */ +/** + * Flag to hint that the first operation on the pixmap will be a + * prepare_access. + */ +#define UXA_CREATE_PIXMAP_FOR_MAP 0x20000000 +/** @} */ + +uxa_driver_t *uxa_driver_alloc(void); + +Bool uxa_driver_init(ScreenPtr screen, uxa_driver_t * uxa_driver); + +void uxa_driver_fini(ScreenPtr pScreen); + +CARD32 uxa_get_pixmap_first_pixel(PixmapPtr pPixmap); + +Bool +uxa_get_color_for_pixmap (PixmapPtr pixmap, + CARD32 src_format, + CARD32 dst_format, + CARD32 *pixel); + +void uxa_set_fallback_debug(ScreenPtr screen, Bool enable); +void uxa_set_force_fallback(ScreenPtr screen, Bool enable); + +/** + * 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 */ |