xenocara/driver/xf86-video-intel/uxa/uxa-render.c
oga 1cf6df0168 Update the intel driver to (mostly) a backport of 2.12.
It is missing a few commits that I have yet to verify (ones that try and
continue if we lock the gpu rendering engine and can't reset it, for
example) taht will be verified and sent out for extra testing soon.

Should contain a bunch of speedups and some correctness improvements
(though rendercheck still gives some errors that I am looking into).

This has been in snaps since the first day of c2k10, any known issues
with just this driver have (to my knowledge) been fixed since. A problem
with macbooks pointed out by otto happens with both this and the in-tree
driver and thus doesn't stop this moving forward.

As well as the 2.12 improvements, this driver also has a backport
(partially aided by the backports in RHEL 5 kindly provided by Dave
Airlie) from the kms code of modesetting support for ironlake (arrandale
and clarkdale: the IGDs build into intel nehalem cpu dies) which has
been tested on a number of chipsets. Note that Display port and eDP
displays have not yet been worked on (and probably won't until I can
find a displayport monitor), but VGA and lvds at least are known to
work, sure beats vesa.

"no objection on my side" matthieu@, prodding (as always) from princess
marco.
2010-07-18 14:47:47 +00:00

1923 lines
48 KiB
C

/*
* 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;
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 { /* PICT_TYPE_ABGR */
rshift = 0;
gshift = rbits;
bshift = gshift + gbits;
ashift = bshift + bbits;
}
*pixel = 0;
*pixel |= (blue >> (16 - bbits)) << bshift;
*pixel |= (red >> (16 - rbits)) << rshift;
*pixel |= (green >> (16 - gbits)) << gshift;
*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 {
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(&region, 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, &region);
return -1;
}
} else {
SourcePict *source = pSrc->pSourcePict;
PictSolidFill *solid = &source->solidFill;
if (source == NULL || source->type != SourcePictTypeSolidFill) {
REGION_UNINIT(pDst->pDrawable->pScreen, &region);
return -1;
}
pixel = solid->color;
}
if (!(*uxa_screen->info->prepare_solid)
(pDstPix, GXcopy, FB_ALLONES, pixel)) {
REGION_UNINIT(pDst->pDrawable->pScreen, &region);
return -1;
}
REGION_TRANSLATE(pScreen, &region, dst_off_x, dst_off_y);
nbox = REGION_NUM_RECTS(&region);
pbox = REGION_RECTS(&region);
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, &region);
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;
}
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;
}
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;
int depth, error;
int tx, ty;
GCPtr pGC;
depth = pSrc->pDrawable->depth;
if (!transform_is_integer_translation(pSrc->transform, &tx, &ty) ||
!drawable_contains(pSrc->pDrawable, x + tx, y + ty, width, height) ||
depth == 1 ||
pSrc->filter == PictFilterConvolution) {
/* 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->height) {
*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_pixmap_is_offscreen(pPixmap)) {
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 = 0;
*out_y = 0;
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 = x;
*out_y = y;
}
return src;
}
if (src->pDrawable) {
PicturePtr dst;
dst = uxa_acquire_drawable(screen, src,
x, y, width, height,
out_x, out_y);
if (!dst)
return 0;
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 = malloc(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)
free(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(&region,
num_rects, rects,
dst->pDrawable->x, dst->pDrawable->y))
goto fallback;
if (!pixman_region_intersect(&region, &region, dst->pCompositeClip)) {
pixman_region_fini(&region);
return;
}
/* XXX xserver-1.8: CompositeRects is not tracked by Damage, so we must
* manually append the damaged regions ourselves.
*/
DamageRegionAppend(dst->pDrawable, &region);
pixman_region_translate(&region, dst_x, dst_y);
boxes = pixman_region_rectangles(&region, &num_boxes);
extents = pixman_region_extents (&region);
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(&region);
return;
err_src:
FreePicture(src, 0);
err_region:
pixman_region_fini(&region);
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(&region, 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, &region);
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, &region);
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, &region);
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(&region);
pbox = REGION_RECTS(&region);
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, &region);
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;
}
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)
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
(&region, 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(&region),
REGION_NUM_RECTS(&region),
xSrc - xDst, ySrc - yDst, FALSE,
FALSE, 0, NULL);
REGION_UNINIT(pDst->pDrawable->pScreen,
&region);
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
(&region, 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,
&region,
(PixmapPtr) pSrc->
pDrawable, &patOrg,
FB_ALLONES, GXcopy);
REGION_UNINIT(pDst->pDrawable->pScreen,
&region);
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);
}
}