diff options
Diffstat (limited to 'src/sna/sna_composite.c')
-rw-r--r-- | src/sna/sna_composite.c | 722 |
1 files changed, 722 insertions, 0 deletions
diff --git a/src/sna/sna_composite.c b/src/sna/sna_composite.c new file mode 100644 index 00000000..27b1ff3c --- /dev/null +++ b/src/sna/sna_composite.c @@ -0,0 +1,722 @@ +/* + * Copyright (c) 2011 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Chris Wilson <chris@chris-wilson.co.uk> + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "sna.h" +#include "sna_render.h" +#include "sna_render_inline.h" + +#include <mipict.h> +#include <fbpict.h> + +#if DEBUG_COMPOSITE +#undef DBG +#define DBG(x) ErrorF x +#else +#define NDEBUG 1 +#endif + +static void dst_move_area_to_cpu(PicturePtr picture, + uint8_t op, + int x, int y, + int width, int height) +{ + RegionRec area; + BoxRec box; + + DBG(("%s: (%d, %d), (%d %d)\n", __FUNCTION__, x, y, width, height)); + + box.x1 = x; + box.y1 = y; + box.x2 = x + width; + box.y2 = y + height; + RegionInit(&area, &box, 1); + if (picture->pCompositeClip) + RegionIntersect(&area, &area, picture->pCompositeClip); + sna_drawable_move_region_to_cpu(picture->pDrawable, &area, true); + RegionUninit(&area); +} + +#define BOUND(v) (INT16) ((v) < MINSHORT ? MINSHORT : (v) > MAXSHORT ? MAXSHORT : (v)) + +static inline pixman_bool_t +clip_to_dst(pixman_region16_t *region, + pixman_region16_t *clip, + int dx, + int dy) +{ + DBG(("%s: region: %dx[(%d, %d), (%d, %d)], clip: %dx[(%d, %d), (%d, %d)]\n", + __FUNCTION__, + pixman_region_n_rects(region), + region->extents.x1, region->extents.y1, + region->extents.x2, region->extents.y2, + pixman_region_n_rects(clip), + clip->extents.x1, clip->extents.y1, + clip->extents.x2, clip->extents.y2)); + + if (pixman_region_n_rects(region) == 1 && + pixman_region_n_rects(clip) == 1) { + pixman_box16_t *r = pixman_region_rectangles(region, NULL); + pixman_box16_t *c = pixman_region_rectangles(clip, NULL); + int v; + + if (r->x1 < (v = c->x1 + dx)) + r->x1 = BOUND(v); + if (r->x2 > (v = c->x2 + dx)) + r->x2 = BOUND(v); + if (r->y1 < (v = c->y1 + dy)) + r->y1 = BOUND(v); + if (r->y2 > (v = c->y2 + dy)) + r->y2 = BOUND(v); + + if (r->x1 >= r->x2 || r->y1 >= r->y2) + pixman_region_init(region); + } else if (!pixman_region_not_empty(clip)) { + return FALSE; + } else { + if (dx || dy) + pixman_region_translate(region, -dx, -dy); + if (!pixman_region_intersect(region, region, clip)) + return FALSE; + if (dx || dy) + pixman_region_translate(region, dx, dy); + } + return pixman_region_not_empty(region); +} + +static inline Bool +clip_to_src(RegionPtr region, PicturePtr p, int dx, int dy) +{ + Bool result; + + if (p->clientClipType == CT_NONE) + return TRUE; + + pixman_region_translate(p->clientClip, + p->clipOrigin.x + dx, + p->clipOrigin.y + dy); + + result = RegionIntersect(region, region, p->clientClip); + + pixman_region_translate(p->clientClip, + -(p->clipOrigin.x + dx), + -(p->clipOrigin.y + dy)); + + return result && pixman_region_not_empty(region); +} + +Bool +sna_compute_composite_region(RegionPtr region, + PicturePtr src, PicturePtr mask, PicturePtr dst, + INT16 src_x, INT16 src_y, + INT16 mask_x, INT16 mask_y, + INT16 dst_x, INT16 dst_y, + CARD16 width, CARD16 height) +{ + int v; + + DBG(("%s: dst=(%d, %d)x(%d, %d)\n", + __FUNCTION__, + dst_x, dst_y, + width, height)); + + region->extents.x1 = dst_x < 0 ? 0 : dst_x; + v = dst_x + width; + if (v > dst->pDrawable->width) + v = dst->pDrawable->width; + region->extents.x2 = v; + + region->extents.y1 = dst_y < 0 ? 0 : dst_y; + v = dst_y + height; + if (v > dst->pDrawable->height) + v = dst->pDrawable->height; + region->extents.y2 = v; + + region->data = 0; + + DBG(("%s: initial clip against dst->pDrawable: (%d, %d), (%d, %d)\n", + __FUNCTION__, + region->extents.x1, region->extents.y1, + region->extents.x2, region->extents.y2)); + + if (region->extents.x1 >= region->extents.x2 || + region->extents.y1 >= region->extents.y2) + return FALSE; + + region->extents.x1 += dst->pDrawable->x; + region->extents.x2 += dst->pDrawable->x; + region->extents.y1 += dst->pDrawable->y; + region->extents.y2 += dst->pDrawable->y; + + dst_x += dst->pDrawable->x; + dst_y += dst->pDrawable->y; + + /* clip against dst */ + if (!clip_to_dst(region, dst->pCompositeClip, 0, 0)) + return FALSE; + + DBG(("%s: clip against dst->pCompositeClip: (%d, %d), (%d, %d)\n", + __FUNCTION__, + region->extents.x1, region->extents.y1, + region->extents.x2, region->extents.y2)); + + if (dst->alphaMap) { + if (!clip_to_dst(region, dst->alphaMap->pCompositeClip, + -dst->alphaOrigin.x, + -dst->alphaOrigin.y)) { + pixman_region_fini (region); + return FALSE; + } + } + + /* clip against src */ + if (src->pDrawable) { + src_x += src->pDrawable->x; + src_y += src->pDrawable->y; + } + if (!clip_to_src(region, src, dst_x - src_x, dst_y - src_y)) { + pixman_region_fini (region); + return FALSE; + } + DBG(("%s: clip against src: (%d, %d), (%d, %d)\n", + __FUNCTION__, + region->extents.x1, region->extents.y1, + region->extents.x2, region->extents.y2)); + + if (src->alphaMap) { + if (!clip_to_src(region, src->alphaMap, + dst_x - (src_x - src->alphaOrigin.x), + dst_y - (src_y - src->alphaOrigin.y))) { + pixman_region_fini(region); + return FALSE; + } + } + + /* clip against mask */ + if (mask) { + if (mask->pDrawable) { + mask_x += mask->pDrawable->x; + mask_y += mask->pDrawable->y; + } + if (!clip_to_src(region, mask, dst_x - mask_x, dst_y - mask_y)) { + pixman_region_fini(region); + return FALSE; + } + if (mask->alphaMap) { + if (!clip_to_src(region, mask->alphaMap, + dst_x - (mask_x - mask->alphaOrigin.x), + dst_y - (mask_y - mask->alphaOrigin.y))) { + pixman_region_fini(region); + return FALSE; + } + } + + DBG(("%s: clip against mask: (%d, %d), (%d, %d)\n", + __FUNCTION__, + region->extents.x1, region->extents.y1, + region->extents.x2, region->extents.y2)); + } + + return pixman_region_not_empty(region); +} + +static void +trim_extents(BoxPtr extents, const PicturePtr p, int dx, int dy) +{ + const BoxPtr box = REGION_EXTENTS(NULL, p->pCompositeClip); + + DBG(("%s: trim((%d, %d), (%d, %d)) against ((%d, %d), (%d, %d)) + (%d, %d)\n", + __FUNCTION__, + extents->x1, extents->y1, extents->x2, extents->y2, + box->x1, box->y1, box->x2, box->y2, + dx, dy)); + + if (extents->x1 < box->x1 + dx) + extents->x1 = box->x1 + dx; + if (extents->x2 > box->x2 + dx) + extents->x2 = box->x2 + dx; + + if (extents->y1 < box->y1 + dy) + extents->y1 = box->y1 + dy; + if (extents->y2 > box->y2 + dy) + extents->y2 = box->y2 + dy; +} + +static void +_trim_source_extents(BoxPtr extents, const PicturePtr p, int dx, int dy) +{ + if (p->clientClipType != CT_NONE) + trim_extents(extents, p, dx, dy); +} + +static void +trim_source_extents(BoxPtr extents, const PicturePtr p, int dx, int dy) +{ + if (p->pDrawable) { + dx += p->pDrawable->x; + dy += p->pDrawable->y; + } + _trim_source_extents(extents, p, dx, dy); + if (p->alphaMap) + _trim_source_extents(extents, p->alphaMap, + dx - p->alphaOrigin.x, + dy - p->alphaOrigin.y); + + DBG(("%s: -> (%d, %d), (%d, %d)\n", + __FUNCTION__, + extents->x1, extents->y1, + extents->x2, extents->y2)); +} + +Bool +sna_compute_composite_extents(BoxPtr extents, + PicturePtr src, PicturePtr mask, PicturePtr dst, + INT16 src_x, INT16 src_y, + INT16 mask_x, INT16 mask_y, + INT16 dst_x, INT16 dst_y, + CARD16 width, CARD16 height) +{ + int v; + + DBG(("%s: dst=(%d, %d)x(%d, %d)\n", + __FUNCTION__, + dst_x, dst_y, + width, height)); + + extents->x1 = dst_x < 0 ? 0 : dst_x; + v = dst_x + width; + if (v > dst->pDrawable->width) + v = dst->pDrawable->width; + extents->x2 = v; + + extents->y1 = dst_y < 0 ? 0 : dst_y; + v = dst_y + height; + if (v > dst->pDrawable->height) + v = dst->pDrawable->height; + + DBG(("%s: initial clip against dst->pDrawable: (%d, %d), (%d, %d)\n", + __FUNCTION__, + extents->x1, extents->y1, + extents->x2, extents->y2)); + + if (extents->x1 >= extents->x2 || + extents->y1 >= extents->y2) + return FALSE; + + extents->x1 += dst->pDrawable->x; + extents->x2 += dst->pDrawable->x; + extents->y1 += dst->pDrawable->y; + extents->y2 += dst->pDrawable->y; + + dst_x += dst->pDrawable->x; + dst_y += dst->pDrawable->y; + + /* clip against dst */ + trim_extents(extents, dst, 0, 0); + if (dst->alphaMap) + trim_extents(extents, dst->alphaMap, + -dst->alphaOrigin.x, + -dst->alphaOrigin.y); + + DBG(("%s: clip against dst: (%d, %d), (%d, %d)\n", + __FUNCTION__, + extents->x1, extents->y1, + extents->x2, extents->y2)); + + trim_source_extents(extents, src, dst_x - src_x, dst_y - src_y); + if (mask) + trim_source_extents(extents, mask, + dst_x - mask_x, dst_y - mask_y); + + return extents->x1 < extents->x2 && extents->y1 < extents->y2; +} + +#if DEBUG_COMPOSITE +static void _assert_pixmap_contains_box(PixmapPtr pixmap, BoxPtr box, const char *function) +{ + if (box->x1 < 0 || box->y1 < 0 || + box->x2 > pixmap->drawable.width || + box->y2 > pixmap->drawable.height) + { + ErrorF("%s: damage box is beyond the pixmap: box=(%d, %d), (%d, %d), pixmap=(%d, %d)\n", + __FUNCTION__, + box->x1, box->y1, box->x2, box->y2, + pixmap->drawable.width, + pixmap->drawable.height); + assert(0); + } +} +#define assert_pixmap_contains_box(p, b) _assert_pixmap_contains_box(p, b, __FUNCTION__) +#else +#define assert_pixmap_contains_box(p, b) +#endif + +static void apply_damage(struct sna_composite_op *op, RegionPtr region) +{ + DBG(("%s: damage=%p, region=%d\n", + __FUNCTION__, op->damage, REGION_NUM_RECTS(region))); + + if (op->damage == NULL) + return; + + RegionTranslate(region, op->dst.x, op->dst.y); + + assert_pixmap_contains_box(op->dst.pixmap, RegionExtents(region)); + sna_damage_add(op->damage, region); +} + +void +sna_composite(CARD8 op, + PicturePtr src, + PicturePtr mask, + PicturePtr dst, + INT16 src_x, INT16 src_y, + INT16 mask_x, INT16 mask_y, + INT16 dst_x, INT16 dst_y, + CARD16 width, CARD16 height) +{ + struct sna *sna = to_sna_from_drawable(dst->pDrawable); + struct sna_composite_op tmp; + RegionRec region; + + DBG(("%s(%d src=(%d, %d), mask=(%d, %d), dst=(%d, %d)+(%d, %d), size=(%d, %d)\n", + __FUNCTION__, op, + src_x, src_y, + mask_x, mask_y, + dst_x, dst_y, dst->pDrawable->x, dst->pDrawable->y, + width, height)); + + if (sna->kgem.wedged) { + DBG(("%s: fallback -- wedged\n", __FUNCTION__)); + goto fallback; + } + + if (dst->alphaMap || src->alphaMap || (mask && mask->alphaMap)) { + DBG(("%s: fallback due to unhandled alpha-map\n", __FUNCTION__)); + goto fallback; + } + + if (too_small(sna, dst->pDrawable) && + !picture_is_gpu(src) && !picture_is_gpu(mask)) { + DBG(("%s: fallback due to too small\n", __FUNCTION__)); + goto fallback; + } + + if (!sna_compute_composite_region(®ion, + src, mask, dst, + src_x, src_y, + mask_x, mask_y, + dst_x, dst_y, + width, height)) + return; + + DBG(("%s: composite region extents: (%d, %d), (%d, %d) + (%d, %d)\n", + __FUNCTION__, + region.extents.x1, region.extents.y1, + region.extents.x2, region.extents.y2, + get_drawable_dx(dst->pDrawable), + get_drawable_dy(dst->pDrawable))); + + memset(&tmp, 0, sizeof(tmp)); + if (!sna->render.composite(sna, + op, src, mask, dst, + src_x, src_y, + mask_x, mask_y, + dst_x + dst->pDrawable->x, + dst_y + dst->pDrawable->y, + width, height, + &tmp)) { + DBG(("%s: fallback due unhandled composite op\n", __FUNCTION__)); + goto fallback; + } + + tmp.boxes(sna, &tmp, + REGION_RECTS(®ion), + REGION_NUM_RECTS(®ion)); + apply_damage(&tmp, ®ion); + tmp.done(sna, &tmp); + + REGION_UNINIT(NULL, ®ion); + return; + +fallback: + DBG(("%s -- fallback dst=(%d, %d)+(%d, %d), size=(%d, %d)\n", + __FUNCTION__, + dst_x, dst_y, + dst->pDrawable->x, dst->pDrawable->y, + width, height)); + + dst_move_area_to_cpu(dst, op, + dst_x + dst->pDrawable->x, + dst_y + dst->pDrawable->y, + width, height); + if (src->pDrawable) + sna_drawable_move_to_cpu(src->pDrawable, false); + if (mask && mask->pDrawable) + sna_drawable_move_to_cpu(mask->pDrawable, false); + + DBG(("%s: fallback -- fbCompposite\n", __FUNCTION__)); + fbComposite(op, src, mask, dst, + src_x, src_y, + mask_x, mask_y, + dst_x, dst_y, + width, height); +} + +static Bool +_pixman_region_init_clipped_rectangles(pixman_region16_t *region, + int num_rects, xRectangle *rects, + int tx, int ty, + int maxx, int maxy) +{ + pixman_box16_t stack_boxes[64], *boxes = stack_boxes; + pixman_bool_t ret; + int i, j; + + if (num_rects > ARRAY_SIZE(stack_boxes)) { + boxes = malloc(sizeof(pixman_box16_t) * num_rects); + if (boxes == NULL) + return FALSE; + } + + for (i = j = 0; i < num_rects; i++) { + boxes[j].x1 = rects[i].x + tx; + if (boxes[j].x1 < 0) + boxes[j].x1 = 0; + + boxes[j].y1 = rects[i].y + ty; + if (boxes[j].y1 < 0) + boxes[j].y1 = 0; + + boxes[j].x2 = rects[i].x + rects[i].width; + if (boxes[j].x2 > maxx) + boxes[j].x2 = maxx; + boxes[j].x2 += tx; + + boxes[j].y2 = rects[i].y + rects[i].height; + if (boxes[j].y2 > maxy) + boxes[j].y2 = maxy; + boxes[j].y2 += ty; + + if (boxes[j].x2 > boxes[j].x1 && boxes[j].y2 > boxes[j].y1) + j++; + } + + ret = TRUE; + if (j) + ret = pixman_region_init_rects(region, boxes, j); + else + pixman_region_init(region); + + if (boxes != stack_boxes) + free(boxes); + + DBG(("%s: nrects=%d, region=(%d, %d), (%d, %d) x %d\n", + __FUNCTION__, num_rects, + region->extents.x1, region->extents.y1, + region->extents.x2, region->extents.y2, + pixman_region_n_rects(region))); + return ret; +} + +void +sna_composite_rectangles(CARD8 op, + PicturePtr dst, + xRenderColor *color, + int num_rects, + xRectangle *rects) +{ + struct sna *sna = to_sna_from_drawable(dst->pDrawable); + PixmapPtr pixmap; + struct sna_pixmap *priv; + pixman_region16_t region; + pixman_box16_t *boxes; + int16_t dst_x, dst_y; + int num_boxes; + int error; + + DBG(("%s(op=%d, %08x x %d [(%d, %d)x(%d, %d) ...])\n", + __FUNCTION__, op, + (color->alpha >> 8 << 24) | + (color->red >> 8 << 16) | + (color->green >> 8 << 8) | + (color->blue >> 8 << 0), + num_rects, + rects[0].x, rects[0].y, rects[0].width, rects[0].height)); + + if (!num_rects) + return; + + if (!pixman_region_not_empty(dst->pCompositeClip)) { + DBG(("%s: empty clip, skipping\n", __FUNCTION__)); + return; + } + + if (!_pixman_region_init_clipped_rectangles(®ion, + num_rects, rects, + dst->pDrawable->x, dst->pDrawable->y, + dst->pDrawable->width, dst->pDrawable->height)) + { + DBG(("%s: allocation failed for region\n", __FUNCTION__)); + return; + } + + DBG(("%s: drawable extents (%d, %d),(%d, %d) x %d\n", + __FUNCTION__, + RegionExtents(®ion)->x1, RegionExtents(®ion)->y1, + RegionExtents(®ion)->x2, RegionExtents(®ion)->y2, + RegionNumRects(®ion))); + + if (!pixman_region_intersect(®ion, ®ion, dst->pCompositeClip) || + !pixman_region_not_empty(®ion)) { + DBG(("%s: zero-intersection between rectangles and clip\n", + __FUNCTION__)); + pixman_region_fini(®ion); + return; + } + + DBG(("%s: clipped extents (%d, %d),(%d, %d) x %d\n", + __FUNCTION__, + RegionExtents(®ion)->x1, RegionExtents(®ion)->y1, + RegionExtents(®ion)->x2, RegionExtents(®ion)->y2, + RegionNumRects(®ion))); + + pixmap = get_drawable_pixmap(dst->pDrawable); + get_drawable_deltas(dst->pDrawable, pixmap, &dst_x, &dst_y); + pixman_region_translate(®ion, dst_x, dst_y); + + DBG(("%s: pixmap +(%d, %d) extents (%d, %d),(%d, %d)\n", + __FUNCTION__, dst_x, dst_y, + RegionExtents(®ion)->x1, RegionExtents(®ion)->y1, + RegionExtents(®ion)->x2, RegionExtents(®ion)->y2)); + + if (sna->kgem.wedged) + goto fallback; + + if (dst->alphaMap) { + DBG(("%s: fallback, dst has an alpha-map\n", __FUNCTION__)); + goto fallback; + } + + boxes = pixman_region_rectangles(®ion, &num_boxes); + + if (op == PictOpClear) { + color->red = color->green = color->blue = color->alpha = 0; + } else if (color->alpha >= 0xff00 && op == PictOpOver) { + color->alpha = 0xffff; + op = PictOpSrc; + } + + if (too_small(sna, dst->pDrawable)) { + DBG(("%s: fallback, dst is too small\n", __FUNCTION__)); + goto fallback; + } + + priv = sna_pixmap_move_to_gpu(pixmap); + if (priv == NULL) { + DBG(("%s: fallback due to no GPU bo\n", __FUNCTION__)); + goto fallback; + } + + if (!sna->render.fill_boxes(sna, op, dst->format, color, + pixmap, priv->gpu_bo, + boxes, num_boxes)) { + DBG(("%s: fallback - acceleration failed\n", __FUNCTION__)); + goto fallback; + } + + if (!priv->gpu_only) { + assert_pixmap_contains_box(pixmap, RegionExtents(®ion)); + sna_damage_add(&priv->gpu_damage, ®ion); + } + + goto done; + +fallback: + DBG(("%s: fallback\n", __FUNCTION__)); + sna_drawable_move_region_to_cpu(&pixmap->drawable, ®ion, true); + + if (op == PictOpSrc || op == PictOpClear) { + PixmapPtr pixmap = get_drawable_pixmap(dst->pDrawable); + int nbox = REGION_NUM_RECTS(®ion); + BoxPtr box = REGION_RECTS(®ion); + uint32_t pixel; + + if (sna_get_pixel_from_rgba(&pixel, + color->red, + color->green, + color->blue, + color->alpha, + dst->format)) { + do { + DBG(("%s: fallback fill: (%d, %d)x(%d, %d) %08x\n", + __FUNCTION__, + box->x1, box->y1, + box->x2 - box->x1, + box->y2 - box->y1, + pixel)); + + pixman_fill(pixmap->devPrivate.ptr, + pixmap->devKind/sizeof(uint32_t), + pixmap->drawable.bitsPerPixel, + box->x1, box->y1, + box->x2 - box->x1, + box->y2 - box->y1, + pixel); + box++; + } while (--nbox); + } + } else { + PicturePtr src; + + src = CreateSolidPicture(0, color, &error); + if (src) { + do { + fbComposite(op, src, NULL, dst, + 0, 0, + 0, 0, + rects->x, rects->y, + rects->width, rects->height); + rects++; + } while (--num_rects); + FreePicture(src, 0); + } + } + +done: + /* XXX xserver-1.8: CompositeRects is not tracked by Damage, so we must + * manually append the damaged regions ourselves. + */ + DamageRegionAppend(&pixmap->drawable, ®ion); + DamageRegionProcessPending(&pixmap->drawable); + + pixman_region_fini(®ion); + return; +} |