summaryrefslogtreecommitdiff
path: root/src/sna/sna_render.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sna/sna_render.c')
-rw-r--r--src/sna/sna_render.c888
1 files changed, 888 insertions, 0 deletions
diff --git a/src/sna/sna_render.c b/src/sna/sna_render.c
new file mode 100644
index 00000000..b6a44d26
--- /dev/null
+++ b/src/sna/sna_render.c
@@ -0,0 +1,888 @@
+/*
+ * Copyright © 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>
+ *
+ */
+
+#include "sna.h"
+#include "sna_render.h"
+
+#include <fb.h>
+
+#if DEBUG_RENDER
+#undef DBG
+#define DBG(x) ErrorF x
+#else
+#define NDEBUG 1
+#endif
+
+#define NO_REDIRECT 0
+#define NO_CONVERT 0
+#define NO_FIXUP 0
+#define NO_EXTRACT 0
+
+void sna_kgem_reset(struct kgem *kgem)
+{
+ struct sna *sna = container_of(kgem, struct sna, kgem);
+
+ sna->render.reset(sna);
+}
+
+void sna_kgem_flush(struct kgem *kgem)
+{
+ struct sna *sna = container_of(kgem, struct sna, kgem);
+
+ sna->render.flush(sna);
+
+ if (sna->render.solid_cache.dirty)
+ sna_render_flush_solid(sna);
+}
+
+void sna_kgem_context_switch(struct kgem *kgem, int new_mode)
+{
+ struct sna *sna = container_of(kgem, struct sna, kgem);
+
+ sna->render.context_switch(sna, new_mode);
+}
+
+CARD32
+sna_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 30: return PICT_x2r10g10b10;
+ case 32: return PICT_a8r8g8b8;
+ }
+}
+
+static Bool
+no_render_composite(struct sna *sna,
+ uint8_t op,
+ PicturePtr src,
+ PicturePtr mask,
+ PicturePtr dst,
+ int16_t src_x, int16_t src_y,
+ int16_t mask_x, int16_t mask_y,
+ int16_t dst_x, int16_t dst_y,
+ int16_t width, int16_t height,
+ struct sna_composite_op *tmp)
+{
+ DBG(("%s ()\n", __FUNCTION__));
+
+ if (mask == NULL &&
+ sna_blt_composite(sna,
+ op, src, dst,
+ src_x, src_y,
+ dst_x, dst_y,
+ width, height,
+ tmp))
+ return TRUE;
+
+ return FALSE;
+}
+
+static Bool
+no_render_copy_boxes(struct sna *sna, uint8_t alu,
+ PixmapPtr src, struct kgem_bo *src_bo, int16_t src_dx, int16_t src_dy,
+ PixmapPtr dst, struct kgem_bo *dst_bo, int16_t dst_dx, int16_t dst_dy,
+ const BoxRec *box, int n)
+{
+ DBG(("%s (n=%d)\n", __FUNCTION__, n));
+
+ return sna_blt_copy_boxes(sna, alu,
+ src_bo, src_dx, src_dy,
+ dst_bo, dst_dx, dst_dy,
+ dst->drawable.bitsPerPixel,
+ box, n);
+}
+
+static Bool
+no_render_copy(struct sna *sna, uint8_t alu,
+ PixmapPtr src, struct kgem_bo *src_bo,
+ PixmapPtr dst, struct kgem_bo *dst_bo,
+ struct sna_copy_op *tmp)
+{
+ DBG(("%s ()\n", __FUNCTION__));
+
+ if (src->drawable.bitsPerPixel != dst->drawable.bitsPerPixel &&
+ sna_blt_copy(sna, alu,
+ src_bo, dst_bo, dst->drawable.bitsPerPixel,
+ tmp))
+ return TRUE;
+
+ return FALSE;
+}
+
+static Bool
+no_render_fill_boxes(struct sna *sna,
+ CARD8 op,
+ PictFormat format,
+ const xRenderColor *color,
+ PixmapPtr dst, struct kgem_bo *dst_bo,
+ const BoxRec *box, int n)
+{
+ uint8_t alu = GXcopy;
+ uint32_t pixel;
+
+ DBG(("%s (op=%d, color=(%04x,%04x,%04x, %04x))\n",
+ __FUNCTION__, op,
+ color->red, color->green, color->blue, color->alpha));
+
+ if (color == 0)
+ op = PictOpClear;
+
+ if (op == PictOpClear) {
+ alu = GXclear;
+ op = PictOpSrc;
+ }
+
+ if (op == PictOpOver) {
+ if ((color->alpha >= 0xff00))
+ op = PictOpSrc;
+ }
+
+ if (op != PictOpSrc)
+ return FALSE;
+
+ if (!sna_get_pixel_from_rgba(&pixel,
+ color->red,
+ color->green,
+ color->blue,
+ color->alpha,
+ format))
+ return FALSE;
+
+ return sna_blt_fill_boxes(sna, alu,
+ dst_bo, dst->drawable.bitsPerPixel,
+ pixel, box, n);
+}
+
+static Bool
+no_render_fill(struct sna *sna, uint8_t alu,
+ PixmapPtr dst, struct kgem_bo *dst_bo,
+ uint32_t color,
+ struct sna_fill_op *tmp)
+{
+ DBG(("%s (alu=%d, color=%08x)\n", __FUNCTION__, alu, color));
+ return sna_blt_fill(sna, alu,
+ dst_bo, dst->drawable.bitsPerPixel,
+ color,
+ tmp);
+}
+
+static void no_render_reset(struct sna *sna)
+{
+}
+
+static void no_render_flush(struct sna *sna)
+{
+}
+
+static void
+no_render_context_switch(struct sna *sna,
+ int new_mode)
+{
+}
+
+static void
+no_render_fini(struct sna *sna)
+{
+}
+
+void no_render_init(struct sna *sna)
+{
+ struct sna_render *render = &sna->render;
+
+ render->composite = no_render_composite;
+
+ render->copy_boxes = no_render_copy_boxes;
+ render->copy = no_render_copy;
+
+ render->fill_boxes = no_render_fill_boxes;
+ render->fill = no_render_fill;
+
+ render->reset = no_render_reset;
+ render->flush = no_render_flush;
+ render->context_switch = no_render_context_switch;
+ render->fini = no_render_fini;
+
+ if (sna->kgem.gen >= 60)
+ sna->kgem.ring = KGEM_BLT;
+}
+
+static Bool
+move_to_gpu(PixmapPtr pixmap, const BoxPtr box)
+{
+ struct sna_pixmap *priv;
+ int count, w, h;
+
+ if (pixmap->usage_hint)
+ return FALSE;
+
+ w = box->x2 - box->x1;
+ h = box->y2 - box->y1;
+ if (w == pixmap->drawable.width || h == pixmap->drawable.height)
+ return TRUE;
+
+ count = SOURCE_BIAS;
+ priv = sna_pixmap(pixmap);
+ if (priv)
+ count = ++priv->source_count;
+
+ DBG(("%s: migrate box (%d, %d), (%d, %d)? source count=%d, fraction=%d/%d [%d]\n",
+ __FUNCTION__,
+ box->x1, box->y1, box->x2, box->y2,
+ count, w*h,
+ pixmap->drawable.width * pixmap->drawable.height,
+ pixmap->drawable.width * pixmap->drawable.height / (w*h)));
+
+ return count*w*h >= pixmap->drawable.width * pixmap->drawable.height;
+}
+
+static Bool
+texture_is_cpu(PixmapPtr pixmap, const BoxPtr box)
+{
+ struct sna_pixmap *priv = sna_pixmap(pixmap);
+
+ if (priv == NULL)
+ return TRUE;
+
+ if (priv->gpu_only)
+ return FALSE;
+
+ if (priv->gpu_bo == NULL)
+ return TRUE;
+
+ if (!priv->cpu_damage)
+ return FALSE;
+
+ if (sna_damage_contains_box(priv->gpu_damage, box) != PIXMAN_REGION_OUT)
+ return FALSE;
+
+ return sna_damage_contains_box(priv->cpu_damage, box) != PIXMAN_REGION_OUT;
+}
+
+static struct kgem_bo *upload(struct sna *sna,
+ struct sna_composite_channel *channel,
+ PixmapPtr pixmap,
+ int16_t x, int16_t y, int16_t w, int16_t h,
+ BoxPtr box)
+{
+ struct kgem_bo *bo;
+
+ DBG(("%s: origin=(%d, %d), box=(%d, %d), (%d, %d), pixmap=%dx%d\n",
+ __FUNCTION__, x, y, box->x1, box->y1, box->x2, box->y2, pixmap->drawable.width, pixmap->drawable.height));
+
+ bo = kgem_upload_source_image(&sna->kgem,
+ pixmap->devPrivate.ptr,
+ box->x1, box->y1, w, h,
+ pixmap->devKind,
+ pixmap->drawable.bitsPerPixel);
+ if (bo) {
+ channel->offset[0] -= box->x1;
+ channel->offset[1] -= box->y1;
+ channel->scale[0] = 1./w;
+ channel->scale[1] = 1./h;
+ channel->width = w;
+ channel->height = h;
+ }
+
+ return bo;
+}
+
+int
+sna_render_pixmap_bo(struct sna *sna,
+ struct sna_composite_channel *channel,
+ PixmapPtr pixmap,
+ int16_t x, int16_t y,
+ int16_t w, int16_t h,
+ int16_t dst_x, int16_t dst_y)
+{
+ struct kgem_bo *bo = NULL;
+ struct sna_pixmap *priv;
+ BoxRec box;
+
+ DBG(("%s (%d, %d)x(%d, %d)\n", __FUNCTION__, x, y, w,h));
+
+ /* XXX handle transformed repeat */
+ if (w == 0 || h == 0 || channel->transform) {
+ box.x1 = box.y1 = 0;
+ box.x2 = pixmap->drawable.width;
+ box.y2 = pixmap->drawable.height;
+ } else {
+ box.x1 = x;
+ box.y1 = y;
+ box.x2 = x + w;
+ box.y2 = y + h;
+
+ if (channel->repeat != RepeatNone) {
+ if (box.x1 < 0 ||
+ box.y1 < 0 ||
+ box.x2 > pixmap->drawable.width ||
+ box.y2 > pixmap->drawable.height) {
+ box.x1 = box.y1 = 0;
+ box.x2 = pixmap->drawable.width;
+ box.y2 = pixmap->drawable.height;
+ }
+ } else {
+ if (box.x1 < 0)
+ box.x1 = 0;
+ if (box.y1 < 0)
+ box.y1 = 0;
+ if (box.x2 > pixmap->drawable.width)
+ box.x2 = pixmap->drawable.width;
+ if (box.y2 > pixmap->drawable.height)
+ box.y2 = pixmap->drawable.height;
+ }
+ }
+
+ w = box.x2 - box.x1;
+ h = box.y2 - box.y1;
+ DBG(("%s box=(%d, %d), (%d, %d): (%d, %d)/(%d, %d)\n", __FUNCTION__,
+ box.x1, box.y1, box.x2, box.y2, w, h,
+ pixmap->drawable.width, pixmap->drawable.height));
+ if (w <= 0 || h <= 0) {
+ DBG(("%s: sample extents outside of texture -> clear\n",
+ __FUNCTION__));
+ return 0;
+ }
+
+ channel->height = pixmap->drawable.height;
+ channel->width = pixmap->drawable.width;
+ channel->scale[0] = 1. / pixmap->drawable.width;
+ channel->scale[1] = 1. / pixmap->drawable.height;
+ channel->offset[0] = x - dst_x;
+ channel->offset[1] = y - dst_y;
+
+ DBG(("%s: offset=(%d, %d), size=(%d, %d)\n",
+ __FUNCTION__,
+ channel->offset[0], channel->offset[1],
+ pixmap->drawable.width, pixmap->drawable.height));
+
+ if (texture_is_cpu(pixmap, &box) && !move_to_gpu(pixmap, &box)) {
+ /* If we are using transient data, it is better to copy
+ * to an amalgamated upload buffer so that we don't
+ * stall on releasing the cpu bo immediately upon
+ * completion of the operation.
+ */
+ if (pixmap->usage_hint != CREATE_PIXMAP_USAGE_SCRATCH_HEADER &&
+ w * pixmap->drawable.bitsPerPixel * h > 8*4096) {
+ priv = sna_pixmap_attach(pixmap);
+ bo = pixmap_vmap(&sna->kgem, pixmap);
+ if (bo)
+ bo = kgem_bo_reference(bo);
+ }
+
+ if (bo == NULL) {
+ DBG(("%s: uploading CPU box\n", __FUNCTION__));
+ bo = upload(sna, channel, pixmap, x,y, w,h, &box);
+ }
+ }
+
+ if (bo == NULL) {
+ priv = sna_pixmap_force_to_gpu(pixmap);
+ if (priv)
+ bo = kgem_bo_reference(priv->gpu_bo);
+ else
+ bo = upload(sna, channel, pixmap, x,y, w,h, &box);
+ }
+
+ channel->bo = bo;
+ return bo != NULL;
+}
+
+int
+sna_render_picture_extract(struct sna *sna,
+ PicturePtr picture,
+ struct sna_composite_channel *channel,
+ int16_t x, int16_t y,
+ int16_t w, int16_t h,
+ int16_t dst_x, int16_t dst_y)
+{
+ struct kgem_bo *bo = NULL;
+ PixmapPtr pixmap = get_drawable_pixmap(picture->pDrawable);
+ int16_t ox, oy;
+ BoxRec box;
+
+#if NO_EXTRACT
+ return -1;
+#endif
+
+ DBG(("%s (%d, %d)x(%d, %d) [dst=(%d, %d)]\n",
+ __FUNCTION__, x, y, w, h, dst_x, dst_y));
+
+ if (w == 0 || h == 0) {
+ DBG(("%s: fallback -- unknown bounds\n", __FUNCTION__));
+ return -1;
+ }
+
+ ox = box.x1 = x;
+ oy = box.y1 = y;
+ box.x2 = x + w;
+ box.y2 = y + h;
+ if (channel->transform) {
+ pixman_vector_t v;
+
+ pixman_transform_bounds(channel->transform, &box);
+
+ v.vector[0] = ox << 16;
+ v.vector[1] = oy << 16;
+ v.vector[2] = 1 << 16;
+ pixman_transform_point(channel->transform, &v);
+ ox = v.vector[0] / v.vector[2];
+ oy = v.vector[1] / v.vector[2];
+ }
+
+ if (channel->repeat != RepeatNone) {
+ if (box.x1 < 0 ||
+ box.y1 < 0 ||
+ box.x2 > pixmap->drawable.width ||
+ box.y2 > pixmap->drawable.height) {
+ /* XXX tiled repeats? */
+ box.x1 = box.y1 = 0;
+ box.x2 = pixmap->drawable.width;
+ box.y2 = pixmap->drawable.height;
+
+ if (!channel->is_affine) {
+ DBG(("%s: fallback -- repeating project transform too large for texture\n",
+ __FUNCTION__));
+ return -1;
+ }
+ }
+ } else {
+ if (box.x1 < 0)
+ box.x1 = 0;
+ if (box.y1 < 0)
+ box.y1 = 0;
+ if (box.x2 > pixmap->drawable.width)
+ box.x2 = pixmap->drawable.width;
+ if (box.y2 > pixmap->drawable.height)
+ box.y2 = pixmap->drawable.height;
+ }
+
+ w = box.x2 - box.x1;
+ h = box.y2 - box.y1;
+ DBG(("%s box=(%d, %d), (%d, %d): (%d, %d)/(%d, %d)\n", __FUNCTION__,
+ box.x1, box.y1, box.x2, box.y2, w, h,
+ pixmap->drawable.width, pixmap->drawable.height));
+ if (w <= 0 || h <= 0) {
+ DBG(("%s: sample extents outside of texture -> clear\n",
+ __FUNCTION__));
+ return 0;
+ }
+
+ if (w > sna->render.max_3d_size || h > sna->render.max_3d_size) {
+ DBG(("%s: fallback -- sample too large for texture (%d, %d)x(%d, %d)\n",
+ __FUNCTION__, box.x1, box.y1, w, h));
+ return -1;
+ }
+
+ if (texture_is_cpu(pixmap, &box) && !move_to_gpu(pixmap, &box)) {
+ bo = kgem_upload_source_image(&sna->kgem,
+ pixmap->devPrivate.ptr,
+ box.x1, box.y1, w, h,
+ pixmap->devKind,
+ pixmap->drawable.bitsPerPixel);
+ if (!bo) {
+ DBG(("%s: failed to upload source image, using clear\n",
+ __FUNCTION__));
+ return 0;
+ }
+ } else {
+ if (!sna_pixmap_move_to_gpu(pixmap)) {
+ DBG(("%s: falback -- pixmap is not on the GPU\n",
+ __FUNCTION__));
+ return -1;
+ }
+
+ bo = kgem_create_2d(&sna->kgem, w, h,
+ pixmap->drawable.bitsPerPixel,
+ kgem_choose_tiling(&sna->kgem,
+ I915_TILING_X, w, h,
+ pixmap->drawable.bitsPerPixel),
+ 0);
+ if (!bo) {
+ DBG(("%s: failed to create bo, using clear\n",
+ __FUNCTION__));
+ return 0;
+ }
+
+ if (!sna_blt_copy_boxes(sna, GXcopy,
+ sna_pixmap_get_bo(pixmap), 0, 0,
+ bo, -box.x1, -box.y1,
+ pixmap->drawable.bitsPerPixel,
+ &box, 1)) {
+ DBG(("%s: fallback -- unable to copy boxes\n",
+ __FUNCTION__));
+ return -1;
+ }
+ }
+
+ if (ox == x && oy == y) {
+ x = y = 0;
+ } else if (channel->transform) {
+ pixman_vector_t v;
+ pixman_transform_t m;
+
+ v.vector[0] = (ox - box.x1) << 16;
+ v.vector[1] = (oy - box.y1) << 16;
+ v.vector[2] = 1 << 16;
+ pixman_transform_invert(&m, channel->transform);
+ pixman_transform_point(&m, &v);
+ x = v.vector[0] / v.vector[2];
+ y = v.vector[1] / v.vector[2];
+ } else {
+ x = ox - box.x1;
+ y = oy - box.y1;
+ }
+
+ channel->offset[0] = x - dst_x;
+ channel->offset[1] = y - dst_y;
+ channel->scale[0] = 1./w;
+ channel->scale[1] = 1./h;
+ channel->width = w;
+ channel->height = h;
+ channel->bo = bo;
+ return 1;
+}
+
+int
+sna_render_picture_fixup(struct sna *sna,
+ PicturePtr picture,
+ struct sna_composite_channel *channel,
+ int16_t x, int16_t y,
+ int16_t w, int16_t h,
+ int16_t dst_x, int16_t dst_y)
+{
+ pixman_image_t *dst, *src;
+ uint32_t pitch;
+ int dx, dy;
+ void *ptr;
+
+#if NO_FIXUP
+ return -1;
+#endif
+
+ DBG(("%s: (%d, %d)x(%d, %d)\n", __FUNCTION__, x, y, w, h));
+
+ if (w == 0 || h == 0) {
+ DBG(("%s: fallback - unknown bounds\n", __FUNCTION__));
+ return -1;
+ }
+ if (w > sna->render.max_3d_size || h > sna->render.max_3d_size) {
+ DBG(("%s: fallback - too large (%dx%d)\n", __FUNCTION__, w, h));
+ return -1;
+ }
+
+ if (PICT_FORMAT_RGB(picture->format) == 0) {
+ pitch = ALIGN(w, 4);
+ channel->pict_format = PIXMAN_a8;
+ } else {
+ pitch = sizeof(uint32_t)*w;
+ channel->pict_format = PIXMAN_a8r8g8b8;
+ }
+ if (channel->pict_format != picture->format) {
+ DBG(("%s: converting to %08x (pitch=%d) from %08x\n",
+ __FUNCTION__, channel->pict_format, pitch, picture->format));
+ }
+
+ channel->bo = kgem_create_buffer(&sna->kgem,
+ pitch*h, KGEM_BUFFER_WRITE,
+ &ptr);
+ if (!channel->bo) {
+ DBG(("%s: failed to create upload buffer, using clear\n",
+ __FUNCTION__));
+ return 0;
+ }
+
+ /* XXX Convolution filter? */
+ memset(ptr, 0, pitch*h);
+ channel->bo->pitch = pitch;
+
+ /* Composite in the original format to preserve idiosyncracies */
+ if (picture->format == channel->pict_format)
+ dst = pixman_image_create_bits(picture->format, w, h, ptr, pitch);
+ else
+ dst = pixman_image_create_bits(picture->format, w, h, NULL, 0);
+ if (!dst) {
+ kgem_bo_destroy(&sna->kgem, channel->bo);
+ return 0;
+ }
+
+ if (picture->pDrawable)
+ sna_drawable_move_to_cpu(picture->pDrawable, false);
+
+ src = image_from_pict(picture, FALSE, &dx, &dy);
+ if (src == NULL) {
+ pixman_image_unref(dst);
+ kgem_bo_destroy(&sna->kgem, channel->bo);
+ return 0;
+ }
+
+ DBG(("%s: compositing tmp=(%d+%d, %d+%d)x(%d, %d)\n",
+ __FUNCTION__, x, dx, y, dy, w, h));
+ pixman_image_composite(PictOpSrc, src, NULL, dst,
+ x + dx, y + dy,
+ 0, 0,
+ 0, 0,
+ w, h);
+ free_pixman_pict(picture, src);
+
+ /* Then convert to card format */
+ if (picture->format != channel->pict_format) {
+ DBG(("%s: performing post-conversion %08x->%08x (%d, %d)\n",
+ __FUNCTION__,
+ picture->format, channel->pict_format,
+ w, h));
+
+ src = dst;
+ dst = pixman_image_create_bits(channel->pict_format,
+ w, h, ptr, pitch);
+
+ pixman_image_composite(PictOpSrc, src, NULL, dst,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ w, h);
+ pixman_image_unref(src);
+ }
+ pixman_image_unref(dst);
+
+ channel->width = w;
+ channel->height = h;
+
+ channel->filter = PictFilterNearest;
+ channel->repeat = RepeatNone;
+ channel->is_affine = TRUE;
+
+ channel->scale[0] = 1./w;
+ channel->scale[1] = 1./h;
+ channel->offset[0] = -dst_x;
+ channel->offset[1] = -dst_y;
+ channel->transform = NULL;
+
+ return 1;
+}
+
+int
+sna_render_picture_convert(struct sna *sna,
+ PicturePtr picture,
+ struct sna_composite_channel *channel,
+ PixmapPtr pixmap,
+ int16_t x, int16_t y,
+ int16_t w, int16_t h,
+ int16_t dst_x, int16_t dst_y)
+{
+ uint32_t pitch;
+ pixman_image_t *src, *dst;
+ BoxRec box;
+ void *ptr;
+
+#if NO_CONVERT
+ return -1;
+#endif
+
+ box.x1 = x;
+ box.y1 = y;
+ box.x2 = x + w;
+ box.y2 = y + h;
+
+ if (channel->transform) {
+ DBG(("%s: has transform, uploading whole surface\n",
+ __FUNCTION__));
+ box.x1 = box.y1 = 0;
+ box.x2 = pixmap->drawable.width;
+ box.y2 = pixmap->drawable.height;
+ }
+
+ if (box.x1 < 0)
+ box.x1 = 0;
+ if (box.y1 < 0)
+ box.y1 = 0;
+ if (box.x2 > pixmap->drawable.width)
+ box.x2 = pixmap->drawable.width;
+ if (box.y2 > pixmap->drawable.height)
+ box.y2 = pixmap->drawable.height;
+
+ w = box.x2 - box.x1;
+ h = box.y2 - box.y1;
+
+ DBG(("%s: convert (%d, %d)x(%d, %d), source size %dx%d\n",
+ __FUNCTION__, box.x1, box.y1, w, h,
+ pixmap->drawable.width,
+ pixmap->drawable.height));
+
+ sna_pixmap_move_to_cpu(pixmap, false);
+
+ src = pixman_image_create_bits(picture->format,
+ pixmap->drawable.width,
+ pixmap->drawable.height,
+ pixmap->devPrivate.ptr,
+ pixmap->devKind);
+ if (!src)
+ return 0;
+
+ if (PICT_FORMAT_RGB(picture->format) == 0) {
+ pitch = ALIGN(w, 4);
+ channel->pict_format = PIXMAN_a8;
+ DBG(("%s: converting to a8 (pitch=%d) from %08x\n",
+ __FUNCTION__, pitch, picture->format));
+ } else {
+ pitch = sizeof(uint32_t)*w;
+ channel->pict_format = PIXMAN_a8r8g8b8;
+ DBG(("%s: converting to a8r8g8b8 (pitch=%d) from %08x\n",
+ __FUNCTION__, pitch, picture->format));
+ }
+
+ channel->bo = kgem_create_buffer(&sna->kgem,
+ pitch*h, KGEM_BUFFER_WRITE,
+ &ptr);
+ if (!channel->bo) {
+ pixman_image_unref(src);
+ return 0;
+ }
+
+ channel->bo->pitch = pitch;
+ dst = pixman_image_create_bits(channel->pict_format, w, h, ptr, pitch);
+ if (!dst) {
+ kgem_bo_destroy(&sna->kgem, channel->bo);
+ pixman_image_unref(src);
+ return 0;
+ }
+
+ pixman_image_composite(PictOpSrc, src, NULL, dst,
+ box.x1, box.y1,
+ 0, 0,
+ 0, 0,
+ w, h);
+ pixman_image_unref(dst);
+ pixman_image_unref(src);
+
+ channel->width = w;
+ channel->height = h;
+
+ channel->scale[0] = 1. / w;
+ channel->scale[1] = 1. / h;
+ channel->offset[0] = x - dst_x - box.x1;
+ channel->offset[1] = y - dst_y - box.y1;
+
+ DBG(("%s: offset=(%d, %d), size=(%d, %d) ptr[0]=%08x\n",
+ __FUNCTION__,
+ channel->offset[0], channel->offset[1],
+ channel->width, channel->height,
+ *(uint32_t*)ptr));
+ return 1;
+}
+
+Bool
+sna_render_composite_redirect(struct sna *sna,
+ struct sna_composite_op *op,
+ int x, int y, int width, int height)
+{
+ struct sna_composite_redirect *t = &op->redirect;
+ int bpp = op->dst.pixmap->drawable.bitsPerPixel;
+ struct sna_pixmap *priv;
+ struct kgem_bo *bo;
+
+#if NO_REDIRECT
+ return FALSE;
+#endif
+
+ DBG(("%s: target too large (%dx%d), copying to temporary %dx%d\n",
+ __FUNCTION__, op->dst.width, op->dst.height, width,height));
+
+ if (!width || !height)
+ return FALSE;
+
+ priv = sna_pixmap(op->dst.pixmap);
+ if (priv->gpu_bo == NULL) {
+ DBG(("%s: fallback -- no GPU bo attached\n", __FUNCTION__));
+ return FALSE;
+ }
+
+ if (!sna_pixmap_move_to_gpu(op->dst.pixmap))
+ return FALSE;
+
+ /* We can process the operation in a single pass,
+ * but the target is too large for the 3D pipeline.
+ * Copy into a smaller surface and replace afterwards.
+ */
+ bo = kgem_create_2d(&sna->kgem,
+ width, height, bpp,
+ kgem_choose_tiling(&sna->kgem, I915_TILING_X,
+ width, height, bpp),
+ 0);
+ if (!bo)
+ return FALSE;
+
+ t->box.x1 = x + op->dst.x;
+ t->box.y1 = y + op->dst.y;
+ t->box.x2 = t->box.x1 + width;
+ t->box.y2 = t->box.y1 + height;
+
+ DBG(("%s: original box (%d, %d), (%d, %d)\n",
+ __FUNCTION__, t->box.x1, t->box.y1, t->box.x2, t->box.y2));
+
+ if (!sna_blt_copy_boxes(sna, GXcopy,
+ op->dst.bo, 0, 0,
+ bo, -t->box.x1, -t->box.y1,
+ bpp, &t->box, 1)) {
+ kgem_bo_destroy(&sna->kgem, bo);
+ return FALSE;
+ }
+
+ t->real_bo = priv->gpu_bo;
+ op->dst.bo = bo;
+ op->dst.x = -x;
+ op->dst.y = -y;
+ op->dst.width = width;
+ op->dst.height = height;
+ op->damage = &priv->gpu_damage;
+ return TRUE;
+}
+
+void
+sna_render_composite_redirect_done(struct sna *sna,
+ const struct sna_composite_op *op)
+{
+ const struct sna_composite_redirect *t = &op->redirect;
+
+ if (t->real_bo) {
+ DBG(("%s: copying temporary to dst\n", __FUNCTION__));
+
+ sna_blt_copy_boxes(sna, GXcopy,
+ op->dst.bo, -t->box.x1, -t->box.y1,
+ t->real_bo, 0, 0,
+ op->dst.pixmap->drawable.bitsPerPixel,
+ &t->box, 1);
+
+ kgem_bo_destroy(&sna->kgem, op->dst.bo);
+ }
+}