diff options
author | Anas Nashif <anas.nashif@intel.com> | 2012-11-06 21:16:29 -0800 |
---|---|---|
committer | Anas Nashif <anas.nashif@intel.com> | 2012-11-06 21:16:29 -0800 |
commit | 1501461b978a770b6fc8883901d6c3d177661667 (patch) | |
tree | 476568764b15008dce7c60e818480d3e5432e176 /src/sna/sna_accel.c | |
download | xf86-video-intel-1501461b978a770b6fc8883901d6c3d177661667.tar.gz xf86-video-intel-1501461b978a770b6fc8883901d6c3d177661667.tar.bz2 xf86-video-intel-1501461b978a770b6fc8883901d6c3d177661667.zip |
Imported Upstream version 2.20.10upstream/2.20.10
Diffstat (limited to 'src/sna/sna_accel.c')
-rw-r--r-- | src/sna/sna_accel.c | 14576 |
1 files changed, 14576 insertions, 0 deletions
diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c new file mode 100644 index 000000000..a8a0c931a --- /dev/null +++ b/src/sna/sna_accel.c @@ -0,0 +1,14576 @@ +/* + * 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_reg.h" +#include "rop.h" + +#include <X11/fonts/font.h> +#include <X11/fonts/fontstruct.h> + +#include <dixfontstr.h> + +#include <mi.h> +#include <migc.h> +#include <miline.h> +#include <micmap.h> +#ifdef RENDER +#include <mipict.h> +#endif +#include <shmint.h> + +#include <sys/time.h> +#include <sys/mman.h> +#include <unistd.h> + +#define FORCE_INPLACE 0 +#define FORCE_FALLBACK 0 +#define FORCE_FLUSH 0 + +#define DEFAULT_TILING I915_TILING_X + +#define USE_INPLACE 1 +#define USE_WIDE_SPANS 0 /* -1 force CPU, 1 force GPU */ +#define USE_ZERO_SPANS 1 /* -1 force CPU, 1 force GPU */ +#define USE_INACTIVE 0 +#define USE_CPU_BO 1 + +#define MIGRATE_ALL 0 +#define DBG_NO_CPU_UPLOAD 0 +#define DBG_NO_CPU_DOWNLOAD 0 + +#define ACCEL_FILL_SPANS 1 +#define ACCEL_SET_SPANS 1 +#define ACCEL_PUT_IMAGE 1 +#define ACCEL_COPY_AREA 1 +#define ACCEL_COPY_PLANE 1 +#define ACCEL_COPY_WINDOW 1 +#define ACCEL_POLY_POINT 1 +#define ACCEL_POLY_LINE 1 +#define ACCEL_POLY_SEGMENT 1 +#define ACCEL_POLY_RECTANGLE 1 +#define ACCEL_POLY_ARC 1 +#define ACCEL_POLY_FILL_POLYGON 1 +#define ACCEL_POLY_FILL_RECT 1 +#define ACCEL_POLY_FILL_ARC 1 +#define ACCEL_POLY_TEXT8 1 +#define ACCEL_POLY_TEXT16 1 +#define ACCEL_POLY_GLYPH 1 +#define ACCEL_IMAGE_TEXT8 1 +#define ACCEL_IMAGE_TEXT16 1 +#define ACCEL_IMAGE_GLYPH 1 +#define ACCEL_PUSH_PIXELS 1 + +#define NO_TILE_8x8 0 +#define NO_STIPPLE_8x8 0 + +#if 0 +static void __sna_fallback_flush(DrawablePtr d) +{ + PixmapPtr pixmap = get_drawable_pixmap(d); + struct sna *sna = to_sna_from_pixmap(pixmap); + struct sna_pixmap *priv; + BoxRec box; + PixmapPtr tmp; + int i, j; + char *src, *dst; + + DBG(("%s: uploading CPU damage...\n", __FUNCTION__)); + priv = sna_pixmap_move_to_gpu(pixmap, MOVE_READ); + if (priv == NULL) + return; + + DBG(("%s: downloading GPU damage...\n", __FUNCTION__)); + if (!sna_pixmap_move_to_cpu(pixmap, MOVE_READ)) + return; + + box.x1 = box.y1 = 0; + box.x2 = pixmap->drawable.width; + box.y2 = pixmap->drawable.height; + + tmp = sna_pixmap_create_unattached(pixmap->drawable.pScreen, + pixmap->drawable.width, + pixmap->drawable.height, + pixmap->drawable.depth, + 0); + + DBG(("%s: comparing with direct read...\n", __FUNCTION__)); + sna_read_boxes(sna, + priv->gpu_bo, 0, 0, + tmp, 0, 0, + &box, 1); + + src = pixmap->devPrivate.ptr; + dst = tmp->devPrivate.ptr; + for (i = 0; i < tmp->drawable.height; i++) { + if (memcmp(src, dst, tmp->drawable.width * tmp->drawable.bitsPerPixel >> 3)) { + for (j = 0; src[j] == dst[j]; j++) + ; + ErrorF("mismatch at (%d, %d)\n", + 8*j / tmp->drawable.bitsPerPixel, i); + abort(); + } + src += pixmap->devKind; + dst += tmp->devKind; + } + tmp->drawable.pScreen->DestroyPixmap(tmp); +} +#define FALLBACK_FLUSH(d) __sna_fallback_flush(d) +#else +#define FALLBACK_FLUSH(d) +#endif + +static int sna_font_key; + +static const uint8_t copy_ROP[] = { + ROP_0, /* GXclear */ + ROP_DSa, /* GXand */ + ROP_SDna, /* GXandReverse */ + ROP_S, /* GXcopy */ + ROP_DSna, /* GXandInverted */ + ROP_D, /* GXnoop */ + ROP_DSx, /* GXxor */ + ROP_DSo, /* GXor */ + ROP_DSon, /* GXnor */ + ROP_DSxn, /* GXequiv */ + ROP_Dn, /* GXinvert */ + ROP_SDno, /* GXorReverse */ + ROP_Sn, /* GXcopyInverted */ + ROP_DSno, /* GXorInverted */ + ROP_DSan, /* GXnand */ + ROP_1 /* GXset */ +}; +static const uint8_t fill_ROP[] = { + ROP_0, + ROP_DPa, + ROP_PDna, + ROP_P, + ROP_DPna, + ROP_D, + ROP_DPx, + ROP_DPo, + ROP_DPon, + ROP_PDxn, + ROP_Dn, + ROP_PDno, + ROP_Pn, + ROP_DPno, + ROP_DPan, + ROP_1 +}; + +static const GCOps sna_gc_ops; +static const GCOps sna_gc_ops__cpu; +static GCOps sna_gc_ops__tmp; +static const GCFuncs sna_gc_funcs; +static const GCFuncs sna_gc_funcs__cpu; + +static inline void region_set(RegionRec *r, const BoxRec *b) +{ + r->extents = *b; + r->data = NULL; +} + +static inline void region_maybe_clip(RegionRec *r, RegionRec *clip) +{ + if (clip->data) + RegionIntersect(r, r, clip); +} + +static inline bool region_is_singular(const RegionRec *r) +{ + return r->data == NULL; +} + +typedef struct box32 { + int32_t x1, y1, x2, y2; +} Box32Rec; + +#define PM_IS_SOLID(_draw, _pm) \ + (((_pm) & FbFullMask((_draw)->depth)) == FbFullMask((_draw)->depth)) + +#ifdef DEBUG_PIXMAP +static void _assert_pixmap_contains_box(PixmapPtr pixmap, const BoxRec *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); + } +} + +static void _assert_pixmap_contains_box_with_offset(PixmapPtr pixmap, const BoxRec *box, int dx, int dy, const char *function) +{ + BoxRec b = *box; + b.x1 += dx; b.x2 += dx; + b.y1 += dy; b.y2 += dy; + _assert_pixmap_contains_box(pixmap, &b, function); +} + +static void _assert_pixmap_contains_boxes(PixmapPtr pixmap, const BoxRec *box, int n, int dx, int dy, const char *function) +{ + BoxRec extents; + + extents = *box; + while (--n) { + ++box; + + if (box->x1 < extents.x1) + extents.x1 = box->x1; + if (box->x2 > extents.x2) + extents.x2 = box->x2; + + if (box->y1 < extents.y1) + extents.y1 = box->y1; + if (box->y2 > extents.y2) + extents.y2 = box->y2; + } + extents.x1 += dx; + extents.x2 += dx; + extents.y1 += dy; + extents.y2 += dy; + _assert_pixmap_contains_box(pixmap, &extents, function); +} + + +static void _assert_pixmap_contains_points(PixmapPtr pixmap, const DDXPointRec *pt, int n, int dx, int dy, const char *function) +{ + BoxRec extents; + + extents.x2 = extents.x1 = pt->x; + extents.y2 = extents.y1 = pt->y; + while (--n) { + ++pt; + + if (pt->x < extents.x1) + extents.x1 = pt->x; + else if (pt->x > extents.x2) + extents.x2 = pt->x; + + if (pt->y < extents.y1) + extents.y1 = pt->y; + else if (pt->y > extents.y2) + extents.y2 = pt->y; + } + extents.x1 += dx; + extents.x2 += dx + 1; + extents.y1 += dy; + extents.y2 += dy + 1; + _assert_pixmap_contains_box(pixmap, &extents, function); +} + +static void _assert_drawable_contains_box(DrawablePtr drawable, const BoxRec *box, const char *function) +{ + if (box->x1 < drawable->x || + box->y1 < drawable->y || + box->x2 > drawable->x + drawable->width || + box->y2 > drawable->y + drawable->height) + { + ErrorF("%s: damage box is beyond the drawable: box=(%d, %d), (%d, %d), drawable=(%d, %d)x(%d, %d)\n", + __FUNCTION__, + box->x1, box->y1, box->x2, box->y2, + drawable->x, drawable->y, + drawable->width, drawable->height); + assert(0); + } +} + +static void assert_pixmap_damage(PixmapPtr p) +{ + struct sna_pixmap *priv; + RegionRec reg, cpu, gpu; + + priv = sna_pixmap(p); + if (priv == NULL) + return; + + if (priv->clear) { + assert(DAMAGE_IS_ALL(priv->gpu_damage)); + assert(priv->cpu_damage == NULL); + } + + if (DAMAGE_IS_ALL(priv->gpu_damage) && DAMAGE_IS_ALL(priv->cpu_damage)) { + /* special upload buffer */ + assert(priv->gpu_bo && priv->gpu_bo->proxy); + assert(priv->cpu_bo == NULL); + return; + } + + assert(!DAMAGE_IS_ALL(priv->gpu_damage) || priv->cpu_damage == NULL); + assert(!DAMAGE_IS_ALL(priv->cpu_damage) || priv->gpu_damage == NULL); + + /* Avoid reducing damage to minimise interferrence */ + RegionNull(®); + RegionNull(&gpu); + RegionNull(&cpu); + + if (priv->gpu_damage) + _sna_damage_debug_get_region(DAMAGE_PTR(priv->gpu_damage), &gpu); + + if (priv->cpu_damage) + _sna_damage_debug_get_region(DAMAGE_PTR(priv->cpu_damage), &cpu); + + RegionIntersect(®, &cpu, &gpu); + assert(!RegionNotEmpty(®)); + + RegionUninit(®); + RegionUninit(&gpu); + RegionUninit(&cpu); +} + +#define assert_pixmap_contains_box(p, b) _assert_pixmap_contains_box(p, b, __FUNCTION__) +#define assert_pixmap_contains_box_with_offset(p, b, dx, dy) _assert_pixmap_contains_box_with_offset(p, b, dx, dy, __FUNCTION__) +#define assert_drawable_contains_box(d, b) _assert_drawable_contains_box(d, b, __FUNCTION__) +#define assert_pixmap_contains_boxes(p, b, n, x, y) _assert_pixmap_contains_boxes(p, b, n, x, y, __FUNCTION__) +#define assert_pixmap_contains_points(p, pt, n, x, y) _assert_pixmap_contains_points(p, pt, n, x, y, __FUNCTION__) + +#else +#define assert_pixmap_contains_box(p, b) +#define assert_pixmap_contains_box_with_offset(p, b, dx, dy) +#define assert_pixmap_contains_boxes(p, b, n, x, y) +#define assert_pixmap_contains_points(p, pt, n, x, y) +#define assert_drawable_contains_box(d, b) +#define assert_pixmap_damage(p) +#endif + +inline static bool +sna_fill_init_blt(struct sna_fill_op *fill, + struct sna *sna, + PixmapPtr pixmap, + struct kgem_bo *bo, + uint8_t alu, + uint32_t pixel) +{ + return sna->render.fill(sna, alu, pixmap, bo, pixel, fill); +} + +static bool +sna_copy_init_blt(struct sna_copy_op *copy, + struct sna *sna, + PixmapPtr src, struct kgem_bo *src_bo, + PixmapPtr dst, struct kgem_bo *dst_bo, + uint8_t alu) +{ + memset(copy, 0, sizeof(*copy)); + return sna->render.copy(sna, alu, src, src_bo, dst, dst_bo, copy); +} + +static void sna_pixmap_free_gpu(struct sna *sna, struct sna_pixmap *priv) +{ + sna_damage_destroy(&priv->gpu_damage); + priv->clear = false; + + if (priv->gpu_bo && !priv->pinned) { + kgem_bo_destroy(&sna->kgem, priv->gpu_bo); + priv->gpu_bo = NULL; + } + + if (priv->mapped) { + assert(!priv->shm); + priv->pixmap->devPrivate.ptr = NULL; + priv->mapped = false; + } + + list_del(&priv->inactive); + + /* and reset the upload counter */ + priv->source_count = SOURCE_BIAS; +} + +static bool must_check +sna_pixmap_alloc_cpu(struct sna *sna, + PixmapPtr pixmap, + struct sna_pixmap *priv, + bool from_gpu) +{ + /* Restore after a GTT mapping? */ + assert(!priv->shm); + if (priv->ptr) + goto done; + + DBG(("%s: pixmap=%ld\n", __FUNCTION__, pixmap->drawable.serialNumber)); + assert(priv->stride); + + if (priv->create & KGEM_CAN_CREATE_CPU) { + DBG(("%s: allocating CPU buffer (%dx%d)\n", __FUNCTION__, + pixmap->drawable.width, pixmap->drawable.height)); + + priv->cpu_bo = kgem_create_cpu_2d(&sna->kgem, + pixmap->drawable.width, + pixmap->drawable.height, + pixmap->drawable.bitsPerPixel, + from_gpu ? 0 : CREATE_CPU_MAP | CREATE_INACTIVE); + if (priv->cpu_bo) { + priv->ptr = kgem_bo_map__cpu(&sna->kgem, priv->cpu_bo); + priv->stride = priv->cpu_bo->pitch; + if (priv->ptr) { + DBG(("%s: allocated CPU handle=%d (snooped? %d)\n", __FUNCTION__, + priv->cpu_bo->handle, priv->cpu_bo->snoop)); +#ifdef DEBUG_MEMORY + sna->debug_memory.cpu_bo_allocs++; + sna->debug_memory.cpu_bo_bytes += kgem_bo_size(priv->cpu_bo); + } else { + kgem_bo_destroy(&sna->kgem, priv->cpu_bo); + priv->cpu_bo = NULL; +#endif + } + } + } + + if (priv->ptr == NULL) { + DBG(("%s: allocating ordinary memory for shadow pixels [%d bytes]\n", + __FUNCTION__, priv->stride * pixmap->drawable.height)); + priv->ptr = malloc(priv->stride * pixmap->drawable.height); + } + + assert(priv->ptr); +done: + pixmap->devPrivate.ptr = priv->ptr; + pixmap->devKind = priv->stride; + assert(priv->stride); + return priv->ptr != NULL; +} + +static void sna_pixmap_free_cpu(struct sna *sna, struct sna_pixmap *priv) +{ + assert(priv->cpu_damage == NULL); + assert(list_is_empty(&priv->list)); + + if (priv->cpu_bo) { + DBG(("%s: discarding CPU buffer, handle=%d, size=%d\n", + __FUNCTION__, priv->cpu_bo->handle, kgem_bo_size(priv->cpu_bo))); +#ifdef DEBUG_MEMORY + sna->debug_memory.cpu_bo_allocs--; + sna->debug_memory.cpu_bo_bytes -= kgem_bo_size(priv->cpu_bo); +#endif + if (priv->cpu_bo->flush) { + assert(priv->cpu_bo->reusable == false); + kgem_bo_sync__cpu(&sna->kgem, priv->cpu_bo); + sna_accel_watch_flush(sna, -1); + } + kgem_bo_destroy(&sna->kgem, priv->cpu_bo); + priv->cpu_bo = NULL; + } else + free(priv->ptr); + + priv->ptr = NULL; + if (!priv->mapped) + priv->pixmap->devPrivate.ptr = NULL; +} + +static inline uint32_t default_tiling(PixmapPtr pixmap, + uint32_t tiling) +{ + struct sna_pixmap *priv = sna_pixmap(pixmap); + struct sna *sna = to_sna_from_pixmap(pixmap); + + /* Try to avoid hitting the Y-tiling GTT mapping bug on 855GM */ + if (sna->kgem.gen == 21) + return I915_TILING_X; + + /* Only on later generations was the render pipeline + * more flexible than the BLT. So on gen2/3, prefer to + * keep large objects accessible through the BLT. + */ + if (sna->kgem.gen < 40 && + (pixmap->drawable.width > sna->render.max_3d_size || + pixmap->drawable.height > sna->render.max_3d_size)) + return I915_TILING_X; + + if (tiling == I915_TILING_Y && + sna_damage_is_all(&priv->cpu_damage, + pixmap->drawable.width, + pixmap->drawable.height)) { + DBG(("%s: entire source is damaged, using Y-tiling\n", + __FUNCTION__)); + sna_damage_destroy(&priv->gpu_damage); + priv->undamaged = false; + + return I915_TILING_Y; + } + + return tiling; +} + +constant static uint32_t sna_pixmap_choose_tiling(PixmapPtr pixmap, + uint32_t tiling) +{ + struct sna *sna = to_sna_from_pixmap(pixmap); + uint32_t bit; + + /* Use tiling by default, but disable per user request */ + if (pixmap->usage_hint == SNA_CREATE_FB) { + tiling = -I915_TILING_X; + bit = SNA_TILING_FB; + } else { + tiling = default_tiling(pixmap, tiling); + bit = SNA_TILING_2D; + } + if ((sna->tiling && (1 << bit)) == 0) + tiling = I915_TILING_NONE; + + /* Also adjust tiling if it is not supported or likely to + * slow us down, + */ + return kgem_choose_tiling(&sna->kgem, tiling, + pixmap->drawable.width, + pixmap->drawable.height, + pixmap->drawable.bitsPerPixel); +} + +struct kgem_bo *sna_pixmap_change_tiling(PixmapPtr pixmap, uint32_t tiling) +{ + struct sna_pixmap *priv = sna_pixmap(pixmap); + struct sna *sna = to_sna_from_pixmap(pixmap); + struct kgem_bo *bo; + BoxRec box; + + DBG(("%s: changing tiling %d -> %d for %dx%d pixmap\n", + __FUNCTION__, priv->gpu_bo->tiling, tiling, + pixmap->drawable.width, pixmap->drawable.height)); + + if (priv->pinned) { + DBG(("%s: can't convert pinned bo\n", __FUNCTION__)); + return NULL; + } + + if (wedged(sna)) { + DBG(("%s: can't convert bo, wedged\n", __FUNCTION__)); + return NULL; + } + + assert_pixmap_damage(pixmap); + + bo = kgem_create_2d(&sna->kgem, + pixmap->drawable.width, + pixmap->drawable.height, + pixmap->drawable.bitsPerPixel, + tiling, 0); + if (bo == NULL) { + DBG(("%s: allocation failed\n", __FUNCTION__)); + return NULL; + } + + box.x1 = box.y1 = 0; + box.x2 = pixmap->drawable.width; + box.y2 = pixmap->drawable.height; + + if (!sna->render.copy_boxes(sna, GXcopy, + pixmap, priv->gpu_bo, 0, 0, + pixmap, bo, 0, 0, + &box, 1, 0)) { + DBG(("%s: copy failed\n", __FUNCTION__)); + kgem_bo_destroy(&sna->kgem, bo); + return NULL; + } + + kgem_bo_destroy(&sna->kgem, priv->gpu_bo); + + if (priv->mapped) { + assert(!priv->shm); + pixmap->devPrivate.ptr = NULL; + priv->mapped = false; + } + + return priv->gpu_bo = bo; +} + +static inline void sna_set_pixmap(PixmapPtr pixmap, struct sna_pixmap *sna) +{ + ((void **)dixGetPrivateAddr(&pixmap->devPrivates, &sna_pixmap_key))[1] = sna; + assert(sna_pixmap(pixmap) == sna); +} + +static struct sna_pixmap * +_sna_pixmap_init(struct sna_pixmap *priv, PixmapPtr pixmap) +{ + list_init(&priv->list); + list_init(&priv->inactive); + priv->source_count = SOURCE_BIAS; + priv->pixmap = pixmap; + + return priv; +} + +static struct sna_pixmap * +_sna_pixmap_reset(PixmapPtr pixmap) +{ + struct sna_pixmap *priv; + + assert(pixmap->drawable.type == DRAWABLE_PIXMAP); + assert(pixmap->drawable.class == 0); + assert(pixmap->drawable.x == 0); + assert(pixmap->drawable.y == 0); + + priv = sna_pixmap(pixmap); + assert(priv != NULL); + + memset(priv, 0, sizeof(*priv)); + return _sna_pixmap_init(priv, pixmap); +} + +static struct sna_pixmap *sna_pixmap_attach(PixmapPtr pixmap) +{ + struct sna_pixmap *priv; + + priv = calloc(1, sizeof(*priv)); + if (!priv) + return NULL; + + sna_set_pixmap(pixmap, priv); + return _sna_pixmap_init(priv, pixmap); +} + +bool sna_pixmap_attach_to_bo(PixmapPtr pixmap, struct kgem_bo *bo) +{ + struct sna_pixmap *priv; + + priv = sna_pixmap_attach(pixmap); + if (!priv) + return false; + + priv->gpu_bo = kgem_bo_reference(bo); + sna_damage_all(&priv->gpu_damage, + pixmap->drawable.width, + pixmap->drawable.height); + + return true; +} + +static int bits_per_pixel(int depth) +{ + switch (depth) { + case 1: return 1; + case 4: + case 8: return 8; + case 15: + case 16: return 16; + case 24: + case 30: + case 32: return 32; + default: return 0; + } +} +static PixmapPtr +create_pixmap(struct sna *sna, ScreenPtr screen, + int width, int height, int depth, + unsigned usage_hint) +{ + PixmapPtr pixmap; + size_t datasize; + size_t stride; + int base, bpp; + + bpp = bits_per_pixel(depth); + if (bpp == 0) + return NullPixmap; + + stride = ((width * bpp + FB_MASK) >> FB_SHIFT) * sizeof(FbBits); + if (stride / 4 > 32767 || height > 32767) + return NullPixmap; + + datasize = height * stride; + base = screen->totalPixmapSize; + if (base & 15) { + int adjust = 16 - (base & 15); + base += adjust; + datasize += adjust; + } + + pixmap = AllocatePixmap(screen, datasize); + if (!pixmap) + return NullPixmap; + + ((void **)dixGetPrivateAddr(&pixmap->devPrivates, &sna_pixmap_key))[0] = sna; + assert(to_sna_from_pixmap(pixmap) == sna); + + pixmap->drawable.type = DRAWABLE_PIXMAP; + pixmap->drawable.class = 0; + pixmap->drawable.pScreen = screen; + pixmap->drawable.depth = depth; + pixmap->drawable.bitsPerPixel = bpp; + pixmap->drawable.id = 0; + pixmap->drawable.serialNumber = NEXT_SERIAL_NUMBER; + pixmap->drawable.x = 0; + pixmap->drawable.y = 0; + pixmap->drawable.width = width; + pixmap->drawable.height = height; + pixmap->devKind = stride; + pixmap->refcnt = 1; + pixmap->devPrivate.ptr = (char *)pixmap + base; + +#ifdef COMPOSITE + pixmap->screen_x = 0; + pixmap->screen_y = 0; +#endif + + pixmap->usage_hint = usage_hint; + + DBG(("%s: serial=%ld, usage=%d, %dx%d\n", + __FUNCTION__, + pixmap->drawable.serialNumber, + pixmap->usage_hint, + pixmap->drawable.width, + pixmap->drawable.height)); + + return pixmap; +} + +static PixmapPtr +sna_pixmap_create_shm(ScreenPtr screen, + int width, int height, int depth, + char *addr) +{ + struct sna *sna = to_sna_from_screen(screen); + int bpp = bits_per_pixel(depth); + int pitch = PixmapBytePad(width, depth); + struct sna_pixmap *priv; + PixmapPtr pixmap; + + DBG(("%s(%dx%d, depth=%d, bpp=%d, pitch=%d)\n", + __FUNCTION__, width, height, depth, bpp, pitch)); + + if (wedged(sna) || bpp == 0 || pitch*height <= 4096) { +fallback: + pixmap = sna_pixmap_create_unattached(screen, 0, 0, depth); + if (pixmap == NULL) + return NULL; + + if (!screen->ModifyPixmapHeader(pixmap, width, height, depth, + bpp, pitch, addr)) { + screen->DestroyPixmap(pixmap); + return NULL; + } + + return pixmap; + } + + if (sna->freed_pixmap) { + pixmap = sna->freed_pixmap; + sna->freed_pixmap = pixmap->devPrivate.ptr; + + pixmap->usage_hint = 0; + pixmap->refcnt = 1; + + pixmap->drawable.width = width; + pixmap->drawable.height = height; + pixmap->drawable.depth = depth; + pixmap->drawable.bitsPerPixel = bpp; + pixmap->drawable.serialNumber = NEXT_SERIAL_NUMBER; + + DBG(("%s: serial=%ld, %dx%d\n", + __FUNCTION__, + pixmap->drawable.serialNumber, + pixmap->drawable.width, + pixmap->drawable.height)); + + priv = _sna_pixmap_reset(pixmap); + } else { + pixmap = create_pixmap(sna, screen, 0, 0, depth, 0); + if (pixmap == NullPixmap) + return NullPixmap; + + pixmap->drawable.width = width; + pixmap->drawable.height = height; + pixmap->drawable.depth = depth; + pixmap->drawable.bitsPerPixel = bpp; + + priv = sna_pixmap_attach(pixmap); + if (!priv) { + FreePixmap(pixmap); + return NullPixmap; + } + } + + priv->cpu_bo = kgem_create_map(&sna->kgem, addr, pitch*height, false); + if (priv->cpu_bo == NULL) { + priv->header = true; + sna_pixmap_destroy(pixmap); + goto fallback; + } + priv->cpu_bo->flush = true; + priv->cpu_bo->pitch = pitch; + priv->cpu_bo->reusable = false; + sna_accel_watch_flush(sna, 1); +#ifdef DEBUG_MEMORY + sna->debug_memory.cpu_bo_allocs++; + sna->debug_memory.cpu_bo_bytes += kgem_bo_size(priv->cpu_bo); +#endif + + priv->cpu = true; + priv->shm = true; + sna_damage_all(&priv->cpu_damage, width, height); + + pixmap->devKind = pitch; + pixmap->devPrivate.ptr = addr; + return pixmap; +} + +PixmapPtr +sna_pixmap_create_unattached(ScreenPtr screen, + int width, int height, int depth) +{ + return create_pixmap(to_sna_from_screen(screen), + screen, width, height, depth, + CREATE_PIXMAP_USAGE_SCRATCH); +} + +static PixmapPtr +sna_pixmap_create_scratch(ScreenPtr screen, + int width, int height, int depth, + uint32_t tiling) +{ + struct sna *sna = to_sna_from_screen(screen); + struct sna_pixmap *priv; + PixmapPtr pixmap; + int bpp; + + DBG(("%s(%d, %d, %d, tiling=%d)\n", __FUNCTION__, + width, height, depth, tiling)); + + bpp = bits_per_pixel(depth); + if (tiling == I915_TILING_Y && !sna->have_render) + tiling = I915_TILING_X; + + if (tiling == I915_TILING_Y && + (width > sna->render.max_3d_size || + height > sna->render.max_3d_size)) + tiling = I915_TILING_X; + + tiling = kgem_choose_tiling(&sna->kgem, tiling, width, height, bpp); + + /* you promise never to access this via the cpu... */ + if (sna->freed_pixmap) { + pixmap = sna->freed_pixmap; + sna->freed_pixmap = pixmap->devPrivate.ptr; + + pixmap->usage_hint = CREATE_PIXMAP_USAGE_SCRATCH; + pixmap->refcnt = 1; + + pixmap->drawable.width = width; + pixmap->drawable.height = height; + pixmap->drawable.depth = depth; + pixmap->drawable.bitsPerPixel = bpp; + pixmap->drawable.serialNumber = NEXT_SERIAL_NUMBER; + + DBG(("%s: serial=%ld, usage=%d, %dx%d\n", + __FUNCTION__, + pixmap->drawable.serialNumber, + pixmap->usage_hint, + pixmap->drawable.width, + pixmap->drawable.height)); + + priv = _sna_pixmap_reset(pixmap); + } else { + pixmap = create_pixmap(sna, screen, 0, 0, depth, + CREATE_PIXMAP_USAGE_SCRATCH); + if (pixmap == NullPixmap) + return NullPixmap; + + pixmap->drawable.width = width; + pixmap->drawable.height = height; + pixmap->drawable.depth = depth; + pixmap->drawable.bitsPerPixel = bpp; + + priv = sna_pixmap_attach(pixmap); + if (!priv) { + FreePixmap(pixmap); + return NullPixmap; + } + } + + priv->stride = PixmapBytePad(width, depth); + pixmap->devPrivate.ptr = NULL; + + priv->gpu_bo = kgem_create_2d(&sna->kgem, + width, height, bpp, tiling, + CREATE_TEMPORARY); + if (priv->gpu_bo == NULL) { + free(priv); + FreePixmap(pixmap); + return NullPixmap; + } + + priv->header = true; + sna_damage_all(&priv->gpu_damage, width, height); + + return pixmap; +} + +#ifdef CREATE_PIXMAP_USAGE_SHARED +static Bool +sna_share_pixmap_backing(PixmapPtr pixmap, ScreenPtr slave, void **fd_handle) +{ + struct sna *sna = to_sna_from_pixmap(pixmap); + struct sna_pixmap *priv; + int fd; + + DBG(("%s: pixmap=%ld\n", __FUNCTION__, pixmap->drawable.serialNumber)); + + priv = sna_pixmap_move_to_gpu(pixmap, + MOVE_READ | MOVE_WRITE | __MOVE_DRI | __MOVE_FORCE); + if (priv == NULL) + return FALSE; + + assert(!priv->shm); + assert(priv->gpu_bo); + assert(priv->stride); + + /* XXX negotiate format and stride restrictions */ + if (priv->gpu_bo->tiling != I915_TILING_NONE || + priv->gpu_bo->pitch & 255) { + struct kgem_bo *bo; + BoxRec box; + + DBG(("%s: removing tiling %d, and aligning pitch for %dx%d pixmap=%ld\n", + __FUNCTION__, priv->gpu_bo->tiling, + pixmap->drawable.width, pixmap->drawable.height, + pixmap->drawable.serialNumber)); + + if (priv->pinned & ~(PIN_DRI | PIN_PRIME)) { + DBG(("%s: can't convert pinned bo\n", __FUNCTION__)); + return FALSE; + } + + assert_pixmap_damage(pixmap); + + bo = kgem_create_2d(&sna->kgem, + pixmap->drawable.width, + pixmap->drawable.height, + pixmap->drawable.bitsPerPixel, + I915_TILING_NONE, + CREATE_GTT_MAP | CREATE_PRIME); + if (bo == NULL) { + DBG(("%s: allocation failed\n", __FUNCTION__)); + return FALSE; + } + + box.x1 = box.y1 = 0; + box.x2 = pixmap->drawable.width; + box.y2 = pixmap->drawable.height; + + assert(!wedged(sna)); /* XXX */ + if (!sna->render.copy_boxes(sna, GXcopy, + pixmap, priv->gpu_bo, 0, 0, + pixmap, bo, 0, 0, + &box, 1, 0)) { + DBG(("%s: copy failed\n", __FUNCTION__)); + kgem_bo_destroy(&sna->kgem, bo); + return FALSE; + } + + kgem_bo_destroy(&sna->kgem, priv->gpu_bo); + priv->gpu_bo = bo; + + if (priv->mapped) { + pixmap->devPrivate.ptr = NULL; + priv->mapped = false; + } + } + assert(priv->gpu_bo->tiling == I915_TILING_NONE); + assert((priv->gpu_bo->pitch & 255) == 0); + + /* And export the bo->pitch via pixmap->devKind */ + pixmap->devPrivate.ptr = kgem_bo_map__async(&sna->kgem, priv->gpu_bo); + if (pixmap->devPrivate.ptr == NULL) + return FALSE; + + pixmap->devKind = priv->gpu_bo->pitch; + priv->mapped = true; + + fd = kgem_bo_export_to_prime(&sna->kgem, priv->gpu_bo); + if (fd == -1) + return FALSE; + + priv->pinned |= PIN_PRIME; + + *fd_handle = (void *)(intptr_t)fd; + return TRUE; +} + +static Bool +sna_set_shared_pixmap_backing(PixmapPtr pixmap, void *fd_handle) +{ + struct sna *sna = to_sna_from_pixmap(pixmap); + struct sna_pixmap *priv; + struct kgem_bo *bo; + + DBG(("%s: pixmap=%ld, size=%dx%d, depth=%d/%d, stride=%d\n", + __FUNCTION__, pixmap->drawable.serialNumber, + pixmap->drawable.width, pixmap->drawable.height, + pixmap->drawable.depth, pixmap->drawable.bitsPerPixel, + pixmap->devKind)); + + priv = sna_pixmap(pixmap); + if (priv == NULL) + return FALSE; + + assert(!priv->pinned); + assert(priv->gpu_bo == NULL); + assert(priv->cpu_bo == NULL); + assert(priv->cpu_damage == NULL); + assert(priv->gpu_damage == NULL); + + bo = kgem_create_for_prime(&sna->kgem, + (intptr_t)fd_handle, + pixmap->devKind * pixmap->drawable.height); + if (bo == NULL) + return FALSE; + + sna_damage_all(&priv->gpu_damage, + pixmap->drawable.width, + pixmap->drawable.height); + + bo->pitch = pixmap->devKind; + priv->stride = pixmap->devKind; + + priv->gpu_bo = bo; + priv->pinned |= PIN_PRIME; + + close((intptr_t)fd_handle); + return TRUE; +} + +static PixmapPtr +sna_create_pixmap_shared(struct sna *sna, ScreenPtr screen, + int width, int height, int depth) +{ + PixmapPtr pixmap; + struct sna_pixmap *priv; + + DBG(("%s: depth=%d\n", __FUNCTION__, depth)); + + /* Create a stub to be attached later */ + pixmap = create_pixmap(sna, screen, 0, 0, depth, 0); + if (pixmap == NullPixmap) + return NullPixmap; + + pixmap->devKind = 0; + pixmap->devPrivate.ptr = NULL; + + priv = sna_pixmap_attach(pixmap); + if (priv == NULL) { + free(pixmap); + return NullPixmap; + } + + priv->stride = 0; + priv->create = 0; + + if (width|height) { + int bpp = bits_per_pixel(depth); + + priv->gpu_bo = kgem_create_2d(&sna->kgem, + width, height, bpp, + I915_TILING_NONE, + CREATE_GTT_MAP | CREATE_PRIME); + if (priv->gpu_bo == NULL) { + free(priv); + FreePixmap(pixmap); + return NullPixmap; + } + + /* minimal interface for sharing is linear, 256 byte pitch */ + assert(priv->gpu_bo->tiling == I915_TILING_NONE); + assert((priv->gpu_bo->pitch & 255) == 0); + + pixmap->devPrivate.ptr = + kgem_bo_map__async(&sna->kgem, priv->gpu_bo); + if (pixmap->devPrivate.ptr == NULL) { + free(priv); + FreePixmap(pixmap); + return FALSE; + } + + pixmap->devKind = priv->gpu_bo->pitch; + pixmap->drawable.width = width; + pixmap->drawable.height = height; + + priv->stride = priv->gpu_bo->pitch; + priv->mapped = true; + + sna_damage_all(&priv->gpu_damage, width, height); + } + + return pixmap; +} +#endif + +static PixmapPtr sna_create_pixmap(ScreenPtr screen, + int width, int height, int depth, + unsigned int usage) +{ + struct sna *sna = to_sna_from_screen(screen); + PixmapPtr pixmap; + unsigned flags; + int pad; + + DBG(("%s(%d, %d, %d, usage=%x)\n", __FUNCTION__, + width, height, depth, usage)); + +#ifdef CREATE_PIXMAP_USAGE_SHARED + if (usage == CREATE_PIXMAP_USAGE_SHARED) + return sna_create_pixmap_shared(sna, screen, + width, height, depth); +#endif + + if ((width|height) == 0) { + usage = -1; + goto fallback; + } + assert(width && height); + + flags = kgem_can_create_2d(&sna->kgem, width, height, depth); + if (flags == 0) { + DBG(("%s: can not use GPU, just creating shadow\n", + __FUNCTION__)); + goto fallback; + } + + if (!can_render(sna)) + flags = 0; + + if (usage == CREATE_PIXMAP_USAGE_SCRATCH) { + if (flags & KGEM_CAN_CREATE_GPU) + return sna_pixmap_create_scratch(screen, + width, height, depth, + I915_TILING_X); + else + goto fallback; + } + + if (usage == SNA_CREATE_SCRATCH) { + if (flags & KGEM_CAN_CREATE_GPU) + return sna_pixmap_create_scratch(screen, + width, height, depth, + I915_TILING_Y); + else + goto fallback; + } + + if (usage == CREATE_PIXMAP_USAGE_GLYPH_PICTURE) + flags &= ~KGEM_CAN_CREATE_GPU; + if (usage == CREATE_PIXMAP_USAGE_BACKING_PIXMAP) + usage = 0; + + pad = PixmapBytePad(width, depth); + if (pad * height <= 4096) { + DBG(("%s: small buffer [%d], attaching to shadow pixmap\n", + __FUNCTION__, pad * height)); + pixmap = create_pixmap(sna, screen, + width, height, depth, usage); + if (pixmap == NullPixmap) + return NullPixmap; + + sna_pixmap_attach(pixmap); + } else { + struct sna_pixmap *priv; + + DBG(("%s: creating GPU pixmap %dx%d, stride=%d, flags=%x\n", + __FUNCTION__, width, height, pad, flags)); + + pixmap = create_pixmap(sna, screen, 0, 0, depth, usage); + if (pixmap == NullPixmap) + return NullPixmap; + + pixmap->drawable.width = width; + pixmap->drawable.height = height; + pixmap->devKind = pad; + pixmap->devPrivate.ptr = NULL; + + priv = sna_pixmap_attach(pixmap); + if (priv == NULL) { + free(pixmap); + goto fallback; + } + + priv->stride = pad; + priv->create = flags; + } + + return pixmap; + +fallback: + return create_pixmap(sna, screen, width, height, depth, usage); +} + +void sna_add_flush_pixmap(struct sna *sna, + struct sna_pixmap *priv, + struct kgem_bo *bo) +{ + DBG(("%s: marking pixmap=%ld for flushing\n", + __FUNCTION__, priv->pixmap->drawable.serialNumber)); + assert(bo); + list_move(&priv->list, &sna->flush_pixmaps); + + if (bo->exec == NULL) { + DBG(("%s: new flush bo, flushin before\n", __FUNCTION__)); + kgem_submit(&sna->kgem); + } +} + +static void __sna_free_pixmap(struct sna *sna, + PixmapPtr pixmap, + struct sna_pixmap *priv) +{ + list_del(&priv->list); + list_del(&priv->inactive); + + sna_damage_destroy(&priv->gpu_damage); + sna_damage_destroy(&priv->cpu_damage); + + sna_pixmap_free_cpu(sna, priv); + + if (priv->header) { + assert(!priv->shm); + pixmap->devPrivate.ptr = sna->freed_pixmap; + sna->freed_pixmap = pixmap; + } else { + free(priv); + FreePixmap(pixmap); + } +} + +static Bool sna_destroy_pixmap(PixmapPtr pixmap) +{ + struct sna *sna; + struct sna_pixmap *priv; + + if (--pixmap->refcnt) + return TRUE; + + priv = sna_pixmap(pixmap); + DBG(("%s: pixmap=%ld, attached?=%d\n", + __FUNCTION__, pixmap->drawable.serialNumber, priv != NULL)); + if (priv == NULL) { + FreePixmap(pixmap); + return TRUE; + } + + assert_pixmap_damage(pixmap); + sna = to_sna_from_pixmap(pixmap); + + /* Always release the gpu bo back to the lower levels of caching */ + if (priv->gpu_bo) { + kgem_bo_destroy(&sna->kgem, priv->gpu_bo); + priv->gpu_bo = NULL; + } + + if (priv->shm && kgem_bo_is_busy(priv->cpu_bo)) { + sna_add_flush_pixmap(sna, priv, priv->cpu_bo); + kgem_bo_submit(&sna->kgem, priv->cpu_bo); /* XXX ShmDetach */ + } else + __sna_free_pixmap(sna, pixmap, priv); + return TRUE; +} + +void sna_pixmap_destroy(PixmapPtr pixmap) +{ + assert(pixmap->refcnt == 1); + assert(sna_pixmap(pixmap) == NULL || sna_pixmap(pixmap)->header == true); + + sna_destroy_pixmap(pixmap); +} + +static inline bool pixmap_inplace(struct sna *sna, + PixmapPtr pixmap, + struct sna_pixmap *priv) +{ + if (FORCE_INPLACE) + return FORCE_INPLACE > 0; + + if (wedged(sna) && !priv->pinned) + return false; + + if (priv->mapped) + return true; + + return (pixmap->devKind * pixmap->drawable.height >> 12) > + sna->kgem.half_cpu_cache_pages; +} + +static bool +sna_pixmap_create_mappable_gpu(PixmapPtr pixmap) +{ + struct sna *sna = to_sna_from_pixmap(pixmap); + struct sna_pixmap *priv = sna_pixmap(pixmap);; + + if (wedged(sna)) + return false; + + assert_pixmap_damage(pixmap); + + assert(priv->gpu_bo == NULL); + priv->gpu_bo = + kgem_create_2d(&sna->kgem, + pixmap->drawable.width, + pixmap->drawable.height, + pixmap->drawable.bitsPerPixel, + sna_pixmap_choose_tiling(pixmap, DEFAULT_TILING), + CREATE_GTT_MAP | CREATE_INACTIVE); + + return priv->gpu_bo && kgem_bo_is_mappable(&sna->kgem, priv->gpu_bo); +} + +static inline bool use_cpu_bo_for_download(struct sna *sna, + struct sna_pixmap *priv, + const BoxRec *box) +{ + if (DBG_NO_CPU_DOWNLOAD) + return false; + + if (wedged(sna)) + return false; + + if (priv->cpu_bo == NULL || !sna->kgem.can_blt_cpu) + return false; + + if (kgem_bo_is_busy(priv->gpu_bo) || kgem_bo_is_busy(priv->cpu_bo)) { + DBG(("%s: yes, either bo is busy, so use GPU for readback\n", + __FUNCTION__)); + return true; + } + + /* Is it worth detiling? */ + if (kgem_bo_is_mappable(&sna->kgem, priv->gpu_bo) && + (box->y2 - box->y1 - 1) * priv->gpu_bo->pitch < 4096) { + DBG(("%s: no, tiny transfer, expect to read inplace\n", + __FUNCTION__)); + return false; + } + + DBG(("%s: yes, default action\n", __FUNCTION__)); + return true; +} + +static inline bool use_cpu_bo_for_upload(struct sna *sna, + struct sna_pixmap *priv, + unsigned flags) +{ + if (DBG_NO_CPU_UPLOAD) + return false; + + if (wedged(sna)) + return false; + + if (priv->cpu_bo == NULL) + return false; + + DBG(("%s? flags=%x, gpu busy?=%d, cpu busy?=%d\n", __FUNCTION__, + flags, + kgem_bo_is_busy(priv->gpu_bo), + kgem_bo_is_busy(priv->cpu_bo))); + + if (flags & (MOVE_WRITE | MOVE_ASYNC_HINT)) + return true; + + return kgem_bo_is_busy(priv->gpu_bo) || kgem_bo_is_busy(priv->cpu_bo); +} + +static inline bool operate_inplace(struct sna_pixmap *priv, unsigned flags) +{ + if ((flags & MOVE_INPLACE_HINT) == 0 || priv->gpu_bo == NULL) + return false; + + if (flags & MOVE_WRITE && kgem_bo_is_busy(priv->gpu_bo)) + return false; + + return priv->stride != 0; +} + +bool +_sna_pixmap_move_to_cpu(PixmapPtr pixmap, unsigned int flags) +{ + struct sna *sna = to_sna_from_pixmap(pixmap); + struct sna_pixmap *priv; + + DBG(("%s(pixmap=%ld, %dx%d, flags=%x)\n", __FUNCTION__, + pixmap->drawable.serialNumber, + pixmap->drawable.width, + pixmap->drawable.height, + flags)); + + assert_pixmap_damage(pixmap); + + priv = sna_pixmap(pixmap); + if (priv == NULL) { + DBG(("%s: not attached\n", __FUNCTION__)); + return true; + } + + DBG(("%s: gpu_bo=%d, gpu_damage=%p, cpu_damage=%p, is-clear?=%d\n", + __FUNCTION__, + priv->gpu_bo ? priv->gpu_bo->handle : 0, + priv->gpu_damage, priv->cpu_damage, priv->clear)); + + if (USE_INPLACE && (flags & MOVE_READ) == 0) { + assert(flags & MOVE_WRITE); + DBG(("%s: no readbck, discarding gpu damage [%d], pending clear[%d]\n", + __FUNCTION__, priv->gpu_damage != NULL, priv->clear)); + + if (priv->create & KGEM_CAN_CREATE_GPU && + pixmap_inplace(sna, pixmap, priv)) { + assert(!priv->shm); + DBG(("%s: write inplace\n", __FUNCTION__)); + if (priv->gpu_bo) { + if (__kgem_bo_is_busy(&sna->kgem, + priv->gpu_bo)) { + if (priv->pinned) + goto skip_inplace_map; + + DBG(("%s: discard busy GPU bo\n", __FUNCTION__)); + sna_pixmap_free_gpu(sna, priv); + } + } + if (priv->gpu_bo == NULL && + !sna_pixmap_create_mappable_gpu(pixmap)) + goto skip_inplace_map; + + if (!priv->mapped) { + pixmap->devPrivate.ptr = + kgem_bo_map(&sna->kgem, priv->gpu_bo); + if (pixmap->devPrivate.ptr == NULL) + goto skip_inplace_map; + + priv->mapped = true; + } + pixmap->devKind = priv->gpu_bo->pitch; + + sna_damage_all(&priv->gpu_damage, + pixmap->drawable.width, + pixmap->drawable.height); + sna_damage_destroy(&priv->cpu_damage); + priv->undamaged = false; + priv->clear = false; + priv->cpu = false; + list_del(&priv->list); + if (priv->cpu_bo) { + assert(!priv->shm); + assert(!priv->cpu_bo->flush); + sna_pixmap_free_cpu(sna, priv); + } + + assert_pixmap_damage(pixmap); + return true; + } + +skip_inplace_map: + sna_damage_destroy(&priv->gpu_damage); + if (priv->cpu_bo && !priv->cpu_bo->flush && + __kgem_bo_is_busy(&sna->kgem, priv->cpu_bo)) { + DBG(("%s: discarding busy CPU bo\n", __FUNCTION__)); + assert(!priv->shm); + assert(priv->gpu_bo == NULL || priv->gpu_damage == NULL); + + sna_damage_destroy(&priv->cpu_damage); + priv->undamaged = false; + + sna_pixmap_free_gpu(sna, priv); + sna_pixmap_free_cpu(sna, priv); + } + } + + if (DAMAGE_IS_ALL(priv->cpu_damage)) { + DBG(("%s: CPU all-damaged\n", __FUNCTION__)); + goto done; + } + + assert(priv->gpu_bo == NULL || priv->gpu_bo->proxy == NULL); + + if (operate_inplace(priv, flags) && + pixmap_inplace(sna, pixmap, priv) && + sna_pixmap_move_to_gpu(pixmap, flags)) { + kgem_bo_submit(&sna->kgem, priv->gpu_bo); + + DBG(("%s: try to operate inplace\n", __FUNCTION__)); + assert(priv->cpu == false); + + pixmap->devPrivate.ptr = + kgem_bo_map(&sna->kgem, priv->gpu_bo); + if (pixmap->devPrivate.ptr != NULL) { + priv->mapped = true; + pixmap->devKind = priv->gpu_bo->pitch; + if (flags & MOVE_WRITE) { + sna_damage_all(&priv->gpu_damage, + pixmap->drawable.width, + pixmap->drawable.height); + sna_damage_destroy(&priv->cpu_damage); + list_del(&priv->list); + priv->undamaged = false; + priv->clear = false; + } + + assert_pixmap_damage(pixmap); + DBG(("%s: operate inplace\n", __FUNCTION__)); + return true; + } + + priv->mapped = false; + } + + if (priv->mapped) { + assert(!priv->shm); + pixmap->devPrivate.ptr = NULL; + priv->mapped = false; + } + + if (priv->clear && priv->cpu_bo && !priv->cpu_bo->flush && + __kgem_bo_is_busy(&sna->kgem, priv->cpu_bo)) { + assert(!priv->shm); + assert(DAMAGE_IS_ALL(priv->gpu_damage)); + sna_pixmap_free_cpu(sna, priv); + } + + if (pixmap->devPrivate.ptr == NULL && + !sna_pixmap_alloc_cpu(sna, pixmap, priv, priv->gpu_damage != NULL)) + return false; + + if (priv->clear) { + DBG(("%s: applying clear [%08x]\n", + __FUNCTION__, priv->clear_color)); + + if (priv->cpu_bo) { + DBG(("%s: syncing CPU bo\n", __FUNCTION__)); + kgem_bo_sync__cpu(&sna->kgem, priv->cpu_bo); + } + + if (priv->clear_color == 0 || pixmap->drawable.bitsPerPixel == 8) { + memset(pixmap->devPrivate.ptr, priv->clear_color, + pixmap->devKind * pixmap->drawable.height); + } else { + pixman_fill(pixmap->devPrivate.ptr, + pixmap->devKind/sizeof(uint32_t), + pixmap->drawable.bitsPerPixel, + 0, 0, + pixmap->drawable.width, + pixmap->drawable.height, + priv->clear_color); + } + + sna_damage_all(&priv->cpu_damage, + pixmap->drawable.width, + pixmap->drawable.height); + sna_pixmap_free_gpu(sna, priv); + priv->undamaged = false; + priv->clear = false; + } + + if (priv->gpu_damage) { + BoxPtr box; + int n; + + DBG(("%s: flushing GPU damage\n", __FUNCTION__)); + + n = sna_damage_get_boxes(priv->gpu_damage, &box); + if (n) { + bool ok = false; + + if (use_cpu_bo_for_download(sna, priv, &priv->gpu_damage->extents)) { + DBG(("%s: using CPU bo for download from GPU\n", __FUNCTION__)); + ok = sna->render.copy_boxes(sna, GXcopy, + pixmap, priv->gpu_bo, 0, 0, + pixmap, priv->cpu_bo, 0, 0, + box, n, COPY_LAST); + } + if (!ok) + sna_read_boxes(sna, + priv->gpu_bo, 0, 0, + pixmap, 0, 0, + box, n); + } + + __sna_damage_destroy(DAMAGE_PTR(priv->gpu_damage)); + priv->gpu_damage = NULL; + priv->undamaged = true; + } + + if (flags & MOVE_WRITE || priv->create & KGEM_CAN_CREATE_LARGE) { + DBG(("%s: marking as damaged\n", __FUNCTION__)); + sna_damage_all(&priv->cpu_damage, + pixmap->drawable.width, + pixmap->drawable.height); + sna_pixmap_free_gpu(sna, priv); + priv->undamaged = false; + + if (priv->flush) { + assert(!priv->shm); + sna_add_flush_pixmap(sna, priv, priv->gpu_bo); + } + } + +done: + if (flags & MOVE_WRITE) { + priv->source_count = SOURCE_BIAS; + assert(priv->gpu_bo == NULL || priv->gpu_bo->proxy == NULL); + if (priv->gpu_bo && priv->gpu_bo->domain != DOMAIN_GPU) { + DBG(("%s: discarding inactive GPU bo\n", __FUNCTION__)); + assert(DAMAGE_IS_ALL(priv->cpu_damage)); + sna_pixmap_free_gpu(sna, priv); + priv->undamaged = false; + } + } + + if (priv->cpu_bo) { + if ((flags & MOVE_ASYNC_HINT) == 0) { + DBG(("%s: syncing CPU bo\n", __FUNCTION__)); + kgem_bo_sync__cpu(&sna->kgem, priv->cpu_bo); + } + if (flags & MOVE_WRITE) { + DBG(("%s: discarding GPU bo in favour of CPU bo\n", __FUNCTION__)); + sna_pixmap_free_gpu(sna, priv); + priv->undamaged = false; + } + } + priv->cpu = (flags & MOVE_ASYNC_HINT) == 0; + assert(pixmap->devPrivate.ptr); + assert(pixmap->devKind); + assert_pixmap_damage(pixmap); + return true; +} + +static bool +region_overlaps_damage(const RegionRec *region, + struct sna_damage *damage, + int dx, int dy) +{ + const BoxRec *re, *de; + + DBG(("%s?\n", __FUNCTION__)); + + if (damage == NULL) + return false; + + if (DAMAGE_IS_ALL(damage)) + return true; + + re = ®ion->extents; + de = &DAMAGE_PTR(damage)->extents; + DBG(("%s: region (%d, %d), (%d, %d), damage (%d, %d), (%d, %d)\n", + __FUNCTION__, + re->x1, re->y1, re->x2, re->y2, + de->x1, de->y1, de->x2, de->y2)); + + return (re->x1 + dx < de->x2 && re->x2 + dx > de->x1 && + re->y1 + dy < de->y2 && re->y2 + dy > de->y1); +} + +#ifndef NDEBUG +static bool +pixmap_contains_damage(PixmapPtr pixmap, struct sna_damage *damage) +{ + if (damage == NULL) + return true; + + damage = DAMAGE_PTR(damage); + return (damage->extents.x2 <= pixmap->drawable.width && + damage->extents.y2 <= pixmap->drawable.height && + damage->extents.x1 >= 0 && + damage->extents.y1 >= 0); +} +#endif + +static inline bool region_inplace(struct sna *sna, + PixmapPtr pixmap, + RegionPtr region, + struct sna_pixmap *priv, + bool write_only) +{ + assert_pixmap_damage(pixmap); + + if (FORCE_INPLACE) + return FORCE_INPLACE > 0; + + if (wedged(sna) && !priv->pinned) + return false; + + if (priv->cpu) { + DBG(("%s: no, preferring last action of CPU\n", __FUNCTION__)); + return false; + } + + if (!write_only && + region_overlaps_damage(region, priv->cpu_damage, 0, 0)) { + DBG(("%s: no, uncovered CPU damage pending\n", __FUNCTION__)); + return false; + } + + if (priv->flush) { + DBG(("%s: yes, exported via dri, will flush\n", __FUNCTION__)); + return true; + } + + if (priv->mapped) { + DBG(("%s: yes, already mapped, continuiung\n", __FUNCTION__)); + return true; + } + + if (DAMAGE_IS_ALL(priv->gpu_damage)) { + DBG(("%s: yes, already wholly damaged on the GPU\n", __FUNCTION__)); + return true; + } + + DBG(("%s: (%dx%d), inplace? %d\n", + __FUNCTION__, + region->extents.x2 - region->extents.x1, + region->extents.y2 - region->extents.y1, + ((int)(region->extents.x2 - region->extents.x1) * + (int)(region->extents.y2 - region->extents.y1) * + pixmap->drawable.bitsPerPixel >> 12) + >= sna->kgem.half_cpu_cache_pages)); + return ((int)(region->extents.x2 - region->extents.x1) * + (int)(region->extents.y2 - region->extents.y1) * + pixmap->drawable.bitsPerPixel >> 12) + >= sna->kgem.half_cpu_cache_pages; +} + +bool +sna_drawable_move_region_to_cpu(DrawablePtr drawable, + RegionPtr region, + unsigned flags) +{ + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna *sna = to_sna_from_pixmap(pixmap); + struct sna_pixmap *priv; + int16_t dx, dy; + + DBG(("%s(pixmap=%ld (%dx%d), [(%d, %d), (%d, %d)], flags=%d)\n", + __FUNCTION__, pixmap->drawable.serialNumber, + pixmap->drawable.width, pixmap->drawable.height, + RegionExtents(region)->x1, RegionExtents(region)->y1, + RegionExtents(region)->x2, RegionExtents(region)->y2, + flags)); + + assert_pixmap_damage(pixmap); + if (flags & MOVE_WRITE) { + assert_drawable_contains_box(drawable, ®ion->extents); + } + + priv = sna_pixmap(pixmap); + if (priv == NULL) { + DBG(("%s: not attached to %p\n", __FUNCTION__, pixmap)); + return true; + } + + if (sna_damage_is_all(&priv->cpu_damage, + pixmap->drawable.width, + pixmap->drawable.height)) { + DBG(("%s: pixmap=%ld all damaged on CPU\n", + __FUNCTION__, pixmap->drawable.serialNumber)); + + sna_damage_destroy(&priv->gpu_damage); + priv->undamaged = false; + + if (flags & MOVE_WRITE) + sna_pixmap_free_gpu(sna, priv); + + if (pixmap->devPrivate.ptr == NULL && + !sna_pixmap_alloc_cpu(sna, pixmap, priv, false)) + return false; + + goto out; + } + + if (flags & MOVE_WHOLE_HINT) + return _sna_pixmap_move_to_cpu(pixmap, flags); + + if (priv->gpu_bo == NULL && + (priv->create & KGEM_CAN_CREATE_GPU) == 0 && + flags & MOVE_WRITE) + return _sna_pixmap_move_to_cpu(pixmap, flags); + + get_drawable_deltas(drawable, pixmap, &dx, &dy); + DBG(("%s: delta=(%d, %d)\n", __FUNCTION__, dx, dy)); + if (dx | dy) + RegionTranslate(region, dx, dy); + + if (region_subsumes_drawable(region, &pixmap->drawable)) { + DBG(("%s: region subsumes drawable\n", __FUNCTION__)); + if (dx | dy) + RegionTranslate(region, -dx, -dy); + return _sna_pixmap_move_to_cpu(pixmap, flags); + } + + if (USE_INPLACE && (flags & MOVE_READ) == 0) { + DBG(("%s: no read, checking to see if we can stream the write into the GPU bo\n", + __FUNCTION__)); + assert(flags & MOVE_WRITE); + + if (priv->stride && priv->gpu_bo && + kgem_bo_can_map(&sna->kgem, priv->gpu_bo) && + region_inplace(sna, pixmap, region, priv, true)) { + assert(priv->gpu_bo->proxy == NULL); + if (!__kgem_bo_is_busy(&sna->kgem, priv->gpu_bo)) { + pixmap->devPrivate.ptr = + kgem_bo_map(&sna->kgem, priv->gpu_bo); + if (pixmap->devPrivate.ptr == NULL) { + if (dx | dy) + RegionTranslate(region, -dx, -dy); + return false; + } + + priv->mapped = true; + pixmap->devKind = priv->gpu_bo->pitch; + + sna_damage_subtract(&priv->cpu_damage, region); + if (priv->cpu_damage == NULL) { + list_del(&priv->list); + sna_damage_all(&priv->gpu_damage, + pixmap->drawable.width, + pixmap->drawable.height); + priv->undamaged = false; + } else + sna_damage_add(&priv->gpu_damage, + region); + + priv->clear = false; + priv->cpu = false; + assert_pixmap_damage(pixmap); + if (dx | dy) + RegionTranslate(region, -dx, -dy); + return true; + } + } + + if (priv->cpu_bo && !priv->cpu_bo->flush) { + if (__kgem_bo_is_busy(&sna->kgem, priv->cpu_bo)) { + sna_damage_subtract(&priv->cpu_damage, region); + if (!sna_pixmap_move_to_gpu(pixmap, MOVE_WRITE)) { + if (dx | dy) + RegionTranslate(region, -dx, -dy); + return false; + } + + assert(!priv->shm); + sna_pixmap_free_cpu(sna, priv); + } + } + + if (priv->gpu_bo == NULL && priv->stride && + sna_pixmap_choose_tiling(pixmap, DEFAULT_TILING) != I915_TILING_NONE && + region_inplace(sna, pixmap, region, priv, true) && + sna_pixmap_create_mappable_gpu(pixmap)) { + pixmap->devPrivate.ptr = + kgem_bo_map(&sna->kgem, priv->gpu_bo); + if (pixmap->devPrivate.ptr == NULL) { + if (dx | dy) + RegionTranslate(region, -dx, -dy); + return false; + } + + priv->mapped = true; + pixmap->devKind = priv->gpu_bo->pitch; + + sna_damage_subtract(&priv->cpu_damage, region); + if (priv->cpu_damage == NULL) { + list_del(&priv->list); + sna_damage_all(&priv->gpu_damage, + pixmap->drawable.width, + pixmap->drawable.height); + priv->undamaged = false; + } else + sna_damage_add(&priv->gpu_damage, region); + + assert_pixmap_damage(pixmap); + priv->clear = false; + priv->cpu = false; + if (dx | dy) + RegionTranslate(region, -dx, -dy); + return true; + } + } + + if (operate_inplace(priv, flags) && + kgem_bo_can_map(&sna->kgem, priv->gpu_bo) && + region_inplace(sna, pixmap, region, priv, (flags & MOVE_READ) == 0)) { + kgem_bo_submit(&sna->kgem, priv->gpu_bo); + + DBG(("%s: try to operate inplace\n", __FUNCTION__)); + + pixmap->devPrivate.ptr = + kgem_bo_map(&sna->kgem, priv->gpu_bo); + if (pixmap->devPrivate.ptr != NULL) { + priv->mapped = true; + pixmap->devKind = priv->gpu_bo->pitch; + if (flags & MOVE_WRITE && + !DAMAGE_IS_ALL(priv->gpu_damage)) { + sna_damage_add(&priv->gpu_damage, region); + if (sna_damage_is_all(&priv->gpu_damage, + pixmap->drawable.width, + pixmap->drawable.height)) { + DBG(("%s: replaced entire pixmap, destroying CPU shadow\n", + __FUNCTION__)); + sna_damage_destroy(&priv->cpu_damage); + priv->undamaged = false; + list_del(&priv->list); + } else + sna_damage_subtract(&priv->cpu_damage, + region); + } + assert_pixmap_damage(pixmap); + priv->clear = false; + priv->cpu = false; + if (dx | dy) + RegionTranslate(region, -dx, -dy); + DBG(("%s: operate inplace\n", __FUNCTION__)); + return true; + } + + priv->mapped = false; + } + + if (priv->clear && flags & MOVE_WRITE) { + DBG(("%s: pending clear, moving whole pixmap for partial write\n", __FUNCTION__)); + if (dx | dy) + RegionTranslate(region, -dx, -dy); + return _sna_pixmap_move_to_cpu(pixmap, flags | MOVE_READ); + } + + if (priv->mapped) { + assert(!priv->shm); + pixmap->devPrivate.ptr = NULL; + priv->mapped = false; + } + + if (pixmap->devPrivate.ptr == NULL && + !sna_pixmap_alloc_cpu(sna, pixmap, priv, priv->gpu_damage != NULL)) { + if (dx | dy) + RegionTranslate(region, -dx, -dy); + return false; + } + + if (priv->gpu_bo == NULL) { + assert(priv->gpu_damage == NULL); + goto done; + } + + assert(priv->gpu_bo->proxy == NULL); + if (priv->clear) { + int n = REGION_NUM_RECTS(region); + BoxPtr box = REGION_RECTS(region); + + DBG(("%s: pending clear, doing partial fill\n", __FUNCTION__)); + if (priv->cpu_bo) { + DBG(("%s: syncing CPU bo\n", __FUNCTION__)); + kgem_bo_sync__cpu(&sna->kgem, priv->cpu_bo); + } + + do { + 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, + priv->clear_color); + box++; + } while (--n); + + if (region->extents.x2 - region->extents.x1 > 1 || + region->extents.y2 - region->extents.y1 > 1) { + sna_damage_subtract(&priv->gpu_damage, region); + priv->clear = false; + } + goto done; + } + + if ((flags & MOVE_READ) == 0) { + assert(flags & MOVE_WRITE); + sna_damage_subtract(&priv->gpu_damage, region); + goto done; + } + + if (MIGRATE_ALL && priv->gpu_damage) { + BoxPtr box; + int n = sna_damage_get_boxes(priv->gpu_damage, &box); + if (n) { + bool ok; + + DBG(("%s: forced migration\n", __FUNCTION__)); + + assert(pixmap_contains_damage(pixmap, priv->gpu_damage)); + + ok = false; + if (use_cpu_bo_for_download(sna, priv, &priv->gpu_damage->extents)) { + DBG(("%s: using CPU bo for download from GPU\n", __FUNCTION__)); + ok = sna->render.copy_boxes(sna, GXcopy, + pixmap, priv->gpu_bo, 0, 0, + pixmap, priv->cpu_bo, 0, 0, + box, n, COPY_LAST); + } + if (!ok) + sna_read_boxes(sna, + priv->gpu_bo, 0, 0, + pixmap, 0, 0, + box, n); + } + sna_damage_destroy(&priv->gpu_damage); + priv->undamaged = true; + } + + if (priv->gpu_damage && + (DAMAGE_IS_ALL(priv->gpu_damage) || + sna_damage_overlaps_box(priv->gpu_damage, ®ion->extents))) { + DBG(("%s: region (%dx%d) overlaps gpu damage\n", + __FUNCTION__, + region->extents.x2 - region->extents.x1, + region->extents.y2 - region->extents.y1)); + + if (priv->cpu_damage == NULL) { + if ((flags & MOVE_WRITE) == 0 && + region->extents.x2 - region->extents.x1 == 1 && + region->extents.y2 - region->extents.y1 == 1) { + /* Often associated with synchronisation, KISS */ + DBG(("%s: single pixel read\n", __FUNCTION__)); + sna_read_boxes(sna, + priv->gpu_bo, 0, 0, + pixmap, 0, 0, + ®ion->extents, 1); + goto done; + } + } else { + if (sna_damage_contains_box__no_reduce(priv->cpu_damage, + ®ion->extents)) { + DBG(("%s: region already in CPU damage\n", + __FUNCTION__)); + goto done; + } + } + + if (sna_damage_contains_box(priv->gpu_damage, + ®ion->extents) != PIXMAN_REGION_OUT) { + RegionRec want, *r = region; + + DBG(("%s: region (%dx%d) intersects gpu damage\n", + __FUNCTION__, + region->extents.x2 - region->extents.x1, + region->extents.y2 - region->extents.y1)); + + if ((flags & MOVE_WRITE) == 0 && + region->extents.x2 - region->extents.x1 == 1 && + region->extents.y2 - region->extents.y1 == 1) { + sna_read_boxes(sna, + priv->gpu_bo, 0, 0, + pixmap, 0, 0, + ®ion->extents, 1); + goto done; + } + + /* Expand the region to move 32x32 pixel blocks at a + * time, as we assume that we will continue writing + * afterwards and so aim to coallesce subsequent + * reads. + */ + if (flags & MOVE_WRITE) { + int n = REGION_NUM_RECTS(region), i; + BoxPtr boxes = REGION_RECTS(region); + BoxPtr blocks = malloc(sizeof(BoxRec) * REGION_NUM_RECTS(region)); + if (blocks) { + for (i = 0; i < n; i++) { + blocks[i].x1 = boxes[i].x1 & ~31; + if (blocks[i].x1 < 0) + blocks[i].x1 = 0; + + blocks[i].x2 = (boxes[i].x2 + 31) & ~31; + if (blocks[i].x2 > pixmap->drawable.width) + blocks[i].x2 = pixmap->drawable.width; + + blocks[i].y1 = boxes[i].y1 & ~31; + if (blocks[i].y1 < 0) + blocks[i].y1 = 0; + + blocks[i].y2 = (boxes[i].y2 + 31) & ~31; + if (blocks[i].y2 > pixmap->drawable.height) + blocks[i].y2 = pixmap->drawable.height; + } + if (pixman_region_init_rects(&want, blocks, i)) + r = &want; + free(blocks); + } + } + + if (region_subsumes_damage(r, priv->gpu_damage)) { + BoxPtr box; + int n; + + DBG(("%s: region wholly contains damage\n", + __FUNCTION__)); + + n = sna_damage_get_boxes(priv->gpu_damage, + &box); + if (n) { + bool ok = false; + + if (use_cpu_bo_for_download(sna, priv, &priv->gpu_damage->extents)) { + DBG(("%s: using CPU bo for download from GPU\n", __FUNCTION__)); + ok = sna->render.copy_boxes(sna, GXcopy, + pixmap, priv->gpu_bo, 0, 0, + pixmap, priv->cpu_bo, 0, 0, + box, n, COPY_LAST); + } + + if (!ok) + sna_read_boxes(sna, + priv->gpu_bo, 0, 0, + pixmap, 0, 0, + box, n); + } + + sna_damage_destroy(&priv->gpu_damage); + priv->undamaged = true; + } else if (DAMAGE_IS_ALL(priv->gpu_damage) || + sna_damage_contains_box__no_reduce(priv->gpu_damage, + &r->extents)) { + BoxPtr box = REGION_RECTS(r); + int n = REGION_NUM_RECTS(r); + bool ok = false; + + DBG(("%s: region wholly inside damage\n", + __FUNCTION__)); + + if (use_cpu_bo_for_download(sna, priv, &r->extents)) { + DBG(("%s: using CPU bo for download from GPU\n", __FUNCTION__)); + ok = sna->render.copy_boxes(sna, GXcopy, + pixmap, priv->gpu_bo, 0, 0, + pixmap, priv->cpu_bo, 0, 0, + box, n, COPY_LAST); + } + if (!ok) + sna_read_boxes(sna, + priv->gpu_bo, 0, 0, + pixmap, 0, 0, + box, n); + + sna_damage_subtract(&priv->gpu_damage, r); + priv->undamaged = true; + } else { + RegionRec need; + + pixman_region_init(&need); + if (sna_damage_intersect(priv->gpu_damage, r, &need)) { + BoxPtr box = REGION_RECTS(&need); + int n = REGION_NUM_RECTS(&need); + bool ok = false; + + DBG(("%s: region intersects damage\n", + __FUNCTION__)); + + if (use_cpu_bo_for_download(sna, priv, &need.extents)) { + DBG(("%s: using CPU bo for download from GPU\n", __FUNCTION__)); + ok = sna->render.copy_boxes(sna, GXcopy, + pixmap, priv->gpu_bo, 0, 0, + pixmap, priv->cpu_bo, 0, 0, + box, n, COPY_LAST); + } + if (!ok) + sna_read_boxes(sna, + priv->gpu_bo, 0, 0, + pixmap, 0, 0, + box, n); + + sna_damage_subtract(&priv->gpu_damage, r); + priv->undamaged = true; + RegionUninit(&need); + } + } + if (r == &want) + pixman_region_fini(&want); + } + } + +done: + if (flags & MOVE_WRITE) { + DBG(("%s: applying cpu damage\n", __FUNCTION__)); + assert(!DAMAGE_IS_ALL(priv->cpu_damage)); + assert_pixmap_contains_box(pixmap, RegionExtents(region)); + sna_damage_add(&priv->cpu_damage, region); + sna_damage_reduce_all(&priv->cpu_damage, + pixmap->drawable.width, + pixmap->drawable.height); + if (DAMAGE_IS_ALL(priv->cpu_damage)) { + if (priv->gpu_bo) { + DBG(("%s: replaced entire pixmap\n", + __FUNCTION__)); + sna_pixmap_free_gpu(sna, priv); + } + priv->undamaged = false; + } + if (priv->flush) { + assert(!priv->shm); + sna_add_flush_pixmap(sna, priv, priv->gpu_bo); + } + } + + if (dx | dy) + RegionTranslate(region, -dx, -dy); + +out: + if (flags & MOVE_WRITE) { + priv->source_count = SOURCE_BIAS; + assert(priv->gpu_bo == NULL || priv->gpu_bo->proxy == NULL); + assert(!priv->flush || !list_is_empty(&priv->list)); + } + if ((flags & MOVE_ASYNC_HINT) == 0 && priv->cpu_bo) { + DBG(("%s: syncing cpu bo\n", __FUNCTION__)); + kgem_bo_sync__cpu(&sna->kgem, priv->cpu_bo); + assert(!kgem_bo_is_busy(priv->cpu_bo)); + } + priv->cpu = (flags & MOVE_ASYNC_HINT) == 0; + assert(pixmap->devPrivate.ptr); + assert(pixmap->devKind); + assert_pixmap_damage(pixmap); + return true; +} + +static inline bool box_empty(const BoxRec *box) +{ + return box->x2 <= box->x1 || box->y2 <= box->y1; +} + +bool +sna_drawable_move_to_cpu(DrawablePtr drawable, unsigned flags) +{ + RegionRec region; + PixmapPtr pixmap; + int16_t dx, dy; + + if (drawable->type == DRAWABLE_PIXMAP) + return sna_pixmap_move_to_cpu((PixmapPtr)drawable, flags); + + pixmap = get_window_pixmap((WindowPtr)drawable); + get_drawable_deltas(drawable, pixmap, &dx, &dy); + + DBG(("%s: (%d, %d)x(%d, %d) + (%d, %d), flags=%x\n", + __FUNCTION__, + drawable->x, drawable->y, + drawable->width, drawable->height, + dx, dy, flags)); + + region.extents.x1 = drawable->x + dx; + region.extents.y1 = drawable->y + dy; + region.extents.x2 = region.extents.x1 + drawable->width; + region.extents.y2 = region.extents.y1 + drawable->height; + region.data = NULL; + + if (region.extents.x1 < 0) + region.extents.x1 = 0; + if (region.extents.y1 < 0) + region.extents.y1 = 0; + if (region.extents.x2 > pixmap->drawable.width) + region.extents.x2 = pixmap->drawable.width; + if (region.extents.y2 > pixmap->drawable.height) + region.extents.y2 = pixmap->drawable.height; + + if (box_empty(®ion.extents)) + return true; + + return sna_drawable_move_region_to_cpu(&pixmap->drawable, ®ion, flags); +} + +static bool alu_overwrites(uint8_t alu) +{ + switch (alu) { + case GXclear: + case GXcopy: + case GXcopyInverted: + case GXset: + return true; + default: + return false; + } +} + +inline static bool drawable_gc_inplace_hint(DrawablePtr draw, GCPtr gc) +{ + if (!alu_overwrites(gc->alu)) + return false; + + if (!PM_IS_SOLID(draw, gc->planemask)) + return false; + + if (gc->fillStyle == FillStippled) + return false; + + return true; +} + +inline static unsigned +drawable_gc_flags(DrawablePtr draw, GCPtr gc, bool partial) +{ + assert(sna_gc(gc)->changes == 0); + + if (gc->fillStyle == FillStippled) { + DBG(("%s: read due to fill %d\n", + __FUNCTION__, gc->fillStyle)); + return MOVE_READ | MOVE_WRITE; + } + + if (fb_gc(gc)->and | fb_gc(gc)->bgand) { + DBG(("%s: read due to rrop %d:%x\n", + __FUNCTION__, gc->alu, (unsigned)fb_gc(gc)->and)); + return MOVE_READ | MOVE_WRITE; + } + + DBG(("%s: try operating on drawable inplace [hint? %d]\n", + __FUNCTION__, drawable_gc_inplace_hint(draw, gc))); + + return (partial ? MOVE_READ : 0) | MOVE_WRITE | MOVE_INPLACE_HINT; +} + +static inline struct sna_pixmap * +sna_pixmap_mark_active(struct sna *sna, struct sna_pixmap *priv) +{ + assert(priv->gpu_bo); + if (USE_INACTIVE && + !priv->pinned && priv->gpu_bo->proxy == NULL && + (priv->create & KGEM_CAN_CREATE_LARGE) == 0) + list_move(&priv->inactive, &sna->active_pixmaps); + return priv; +} + +static bool +sna_pixmap_move_area_to_gpu(PixmapPtr pixmap, const BoxRec *box, unsigned int flags) +{ + struct sna *sna = to_sna_from_pixmap(pixmap); + struct sna_pixmap *priv = sna_pixmap(pixmap); + RegionRec i, r; + + DBG(("%s()\n", __FUNCTION__)); + + assert_pixmap_damage(pixmap); + assert_pixmap_contains_box(pixmap, box); + assert(!wedged(sna)); + + if (sna_damage_is_all(&priv->gpu_damage, + pixmap->drawable.width, + pixmap->drawable.height)) { + sna_damage_destroy(&priv->cpu_damage); + priv->undamaged = false; + list_del(&priv->list); + goto done; + } + + if ((flags & MOVE_READ) == 0) + sna_damage_subtract_box(&priv->cpu_damage, box); + + sna_damage_reduce(&priv->cpu_damage); + assert_pixmap_damage(pixmap); + + if (priv->cpu_damage == NULL) { + priv->undamaged = false; + list_del(&priv->list); + return sna_pixmap_move_to_gpu(pixmap, flags); + } + + if (priv->gpu_bo == NULL) { + unsigned create, tiling; + + create = CREATE_INACTIVE; + if (pixmap->usage_hint == SNA_CREATE_FB) + create |= CREATE_EXACT | CREATE_SCANOUT; + + tiling = (flags & MOVE_SOURCE_HINT) ? I915_TILING_Y : DEFAULT_TILING; + tiling = sna_pixmap_choose_tiling(pixmap, tiling); + + priv->gpu_bo = kgem_create_2d(&sna->kgem, + pixmap->drawable.width, + pixmap->drawable.height, + pixmap->drawable.bitsPerPixel, + tiling, create); + if (priv->gpu_bo == NULL) + return false; + + DBG(("%s: created gpu bo\n", __FUNCTION__)); + } + assert(priv->gpu_bo->proxy == NULL); + + if (priv->mapped) { + assert(!priv->shm); + pixmap->devPrivate.ptr = NULL; + priv->mapped = false; + } + + region_set(&r, box); + if (MIGRATE_ALL || region_subsumes_damage(&r, priv->cpu_damage)) { + int n; + + n = sna_damage_get_boxes(priv->cpu_damage, (BoxPtr *)&box); + if (n) { + bool ok = false; + + if (use_cpu_bo_for_upload(sna, priv, 0)) { + DBG(("%s: using CPU bo for upload to GPU\n", __FUNCTION__)); + ok = sna->render.copy_boxes(sna, GXcopy, + pixmap, priv->cpu_bo, 0, 0, + pixmap, priv->gpu_bo, 0, 0, + box, n, 0); + if (ok && priv->shm) { + assert(!priv->flush); + sna_add_flush_pixmap(sna, priv, priv->cpu_bo); + } + } + if (!ok) { + if (pixmap->devPrivate.ptr == NULL) { + assert(priv->stride && priv->ptr); + pixmap->devPrivate.ptr = priv->ptr; + pixmap->devKind = priv->stride; + } + if (n == 1 && !priv->pinned && + box->x1 <= 0 && box->y1 <= 0 && + box->x2 >= pixmap->drawable.width && + box->y2 >= pixmap->drawable.height) { + ok = sna_replace(sna, pixmap, + &priv->gpu_bo, + pixmap->devPrivate.ptr, + pixmap->devKind); + } else { + ok = sna_write_boxes(sna, pixmap, + priv->gpu_bo, 0, 0, + pixmap->devPrivate.ptr, + pixmap->devKind, + 0, 0, + box, n); + } + if (!ok) + return false; + } + } + + sna_damage_destroy(&priv->cpu_damage); + list_del(&priv->list); + priv->undamaged = true; + } else if (DAMAGE_IS_ALL(priv->cpu_damage) || + sna_damage_contains_box__no_reduce(priv->cpu_damage, box)) { + bool ok = false; + if (use_cpu_bo_for_upload(sna, priv, 0)) { + DBG(("%s: using CPU bo for upload to GPU\n", __FUNCTION__)); + ok = sna->render.copy_boxes(sna, GXcopy, + pixmap, priv->cpu_bo, 0, 0, + pixmap, priv->gpu_bo, 0, 0, + box, 1, 0); + if (ok && priv->shm) { + assert(!priv->flush); + sna_add_flush_pixmap(sna, priv, priv->cpu_bo); + } + } + if (!ok) { + if (pixmap->devPrivate.ptr == NULL) { + assert(priv->stride && priv->ptr); + pixmap->devPrivate.ptr = priv->ptr; + pixmap->devKind = priv->stride; + } + ok = sna_write_boxes(sna, pixmap, + priv->gpu_bo, 0, 0, + pixmap->devPrivate.ptr, + pixmap->devKind, + 0, 0, + box, 1); + } + if (!ok) + return false; + + sna_damage_subtract(&priv->cpu_damage, &r); + priv->undamaged = true; + } else if (sna_damage_intersect(priv->cpu_damage, &r, &i)) { + int n = REGION_NUM_RECTS(&i); + bool ok; + + box = REGION_RECTS(&i); + ok = false; + if (use_cpu_bo_for_upload(sna, priv, 0)) { + DBG(("%s: using CPU bo for upload to GPU, %d boxes\n", __FUNCTION__, n)); + ok = sna->render.copy_boxes(sna, GXcopy, + pixmap, priv->cpu_bo, 0, 0, + pixmap, priv->gpu_bo, 0, 0, + box, n, 0); + if (ok && priv->shm) { + assert(!priv->flush); + sna_add_flush_pixmap(sna, priv, priv->cpu_bo); + } + } + if (!ok) { + if (pixmap->devPrivate.ptr == NULL) { + assert(priv->stride && priv->ptr); + pixmap->devPrivate.ptr = priv->ptr; + pixmap->devKind = priv->stride; + } + ok = sna_write_boxes(sna, pixmap, + priv->gpu_bo, 0, 0, + pixmap->devPrivate.ptr, + pixmap->devKind, + 0, 0, + box, n); + } + if (!ok) + return false; + + sna_damage_subtract(&priv->cpu_damage, &r); + priv->undamaged = true; + RegionUninit(&i); + } + + if (priv->shm) { + assert(!priv->flush); + sna_add_flush_pixmap(sna, priv, priv->cpu_bo); + } + +done: + if (flags & MOVE_WRITE) { + priv->clear = false; + priv->cpu = false; + if (priv->cpu_damage == NULL && box_inplace(pixmap, box)) { + DBG(("%s: large operation on undamaged, promoting to full GPU\n", + __FUNCTION__)); + sna_damage_all(&priv->gpu_damage, + pixmap->drawable.width, + pixmap->drawable.height); + priv->undamaged = false; + } + } + + assert(!priv->gpu_bo->proxy || (flags & MOVE_WRITE) == 0); + return sna_pixmap_mark_active(sna, priv) != NULL; +} + +struct kgem_bo * +sna_drawable_use_bo(DrawablePtr drawable, unsigned flags, const BoxRec *box, + struct sna_damage ***damage) +{ + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna_pixmap *priv = sna_pixmap(pixmap); + struct sna *sna; + RegionRec region; + int16_t dx, dy; + int ret; + + DBG(("%s pixmap=%ld, box=((%d, %d), (%d, %d)), flags=%x...\n", + __FUNCTION__, + pixmap->drawable.serialNumber, + box->x1, box->y1, box->x2, box->y2, + flags)); + + assert_pixmap_damage(pixmap); + assert_drawable_contains_box(drawable, box); + + if (priv == NULL) { + DBG(("%s: not attached\n", __FUNCTION__)); + return NULL; + } + + if (priv->gpu_bo && priv->gpu_bo->proxy) { + DBG(("%s: cached upload proxy, discard and revert to GPU\n", + __FUNCTION__)); + assert(priv->gpu_damage == NULL); + kgem_bo_destroy(&to_sna_from_pixmap(pixmap)->kgem, + priv->gpu_bo); + priv->gpu_bo = NULL; + goto use_cpu_bo; + } + + if (priv->flush) + flags |= PREFER_GPU; + if (priv->shm) + flags &= ~PREFER_GPU; + if (priv->cpu && (flags & FORCE_GPU) == 0) + flags &= ~PREFER_GPU; + + DBG(("%s: flush=%d, shm=%d, cpu=%d => flags=%x\n", + __FUNCTION__, priv->flush, priv->shm, priv->cpu, flags)); + + if ((flags & PREFER_GPU) == 0 && + (!priv->gpu_damage || !kgem_bo_is_busy(priv->gpu_bo))) { + DBG(("%s: try cpu as GPU bo is idle\n", __FUNCTION__)); + goto use_cpu_bo; + } + + if (DAMAGE_IS_ALL(priv->gpu_damage)) { + DBG(("%s: use GPU fast path (all-damaged)\n", __FUNCTION__)); + assert(priv->cpu_damage == NULL); + goto use_gpu_bo; + } + + if (DAMAGE_IS_ALL(priv->cpu_damage)) { + assert(priv->gpu_damage == NULL); + if ((flags & FORCE_GPU) == 0 || priv->cpu_bo) { + DBG(("%s: use CPU fast path (all-damaged), and not forced-gpu\n", + __FUNCTION__)); + goto use_cpu_bo; + } + } + + DBG(("%s: gpu? %d, damaged? %d; cpu? %d, damaged? %d\n", __FUNCTION__, + priv->gpu_bo ? priv->gpu_bo->handle : 0, priv->gpu_damage != NULL, + priv->cpu_bo ? priv->cpu_bo->handle : 0, priv->cpu_damage != NULL)); + if (priv->gpu_bo == NULL) { + unsigned int move; + + if ((flags & FORCE_GPU) == 0 && + (priv->create & KGEM_CAN_CREATE_GPU) == 0) { + DBG(("%s: untiled, will not force allocation\n", + __FUNCTION__)); + goto use_cpu_bo; + } + + if ((flags & IGNORE_CPU) == 0) { + if (priv->cpu_bo) { + if (to_sna_from_pixmap(pixmap)->kgem.can_blt_cpu) { + if (kgem_bo_is_busy(priv->cpu_bo)) { + DBG(("%s: already using CPU bo, will not force allocation\n", + __FUNCTION__)); + goto use_cpu_bo; + } + + if ((flags & RENDER_GPU) == 0) { + DBG(("%s: prefer cpu", __FUNCTION__)); + goto use_cpu_bo; + } + } else { + if (kgem_bo_is_busy(priv->cpu_bo)) { + DBG(("%s: CPU bo active, must force allocation\n", + __FUNCTION__)); + goto create_gpu_bo; + } + } + } + + if ((flags & FORCE_GPU) == 0 && priv->cpu_damage) { + if ((flags & PREFER_GPU) == 0) { + DBG(("%s: already damaged and prefer cpu", + __FUNCTION__)); + goto use_cpu_bo; + } + + if (!box_inplace(pixmap, box)) { + DBG(("%s: damaged with a small operation, will not force allocation\n", + __FUNCTION__)); + goto use_cpu_bo; + } + } + } else if (priv->cpu_damage) { + get_drawable_deltas(drawable, pixmap, &dx, &dy); + + region.extents = *box; + region.extents.x1 += dx; + region.extents.x2 += dx; + region.extents.y1 += dy; + region.extents.y2 += dy; + region.data = NULL; + + sna_damage_subtract(&priv->cpu_damage, ®ion); + if (priv->cpu_damage == NULL) { + list_del(&priv->list); + priv->undamaged = false; + priv->cpu = false; + } + } + +create_gpu_bo: + move = MOVE_WRITE | MOVE_READ; + if (flags & FORCE_GPU) + move |= __MOVE_FORCE; + if (!sna_pixmap_move_to_gpu(pixmap, move)) + goto use_cpu_bo; + + DBG(("%s: allocated GPU bo for operation\n", __FUNCTION__)); + goto done; + } + + get_drawable_deltas(drawable, pixmap, &dx, &dy); + + region.extents = *box; + region.extents.x1 += dx; + region.extents.x2 += dx; + region.extents.y1 += dy; + region.extents.y2 += dy; + + DBG(("%s extents (%d, %d), (%d, %d)\n", __FUNCTION__, + region.extents.x1, region.extents.y1, + region.extents.x2, region.extents.y2)); + + if (priv->gpu_damage) { + if (!priv->cpu_damage) { + if (sna_damage_contains_box__no_reduce(priv->gpu_damage, + ®ion.extents)) { + DBG(("%s: region wholly contained within GPU damage\n", + __FUNCTION__)); + goto use_gpu_bo; + } else { + DBG(("%s: partial GPU damage with no CPU damage, continuing to use GPU\n", + __FUNCTION__)); + priv->cpu = false; + goto done; + } + } + + ret = sna_damage_contains_box(priv->gpu_damage, ®ion.extents); + if (ret == PIXMAN_REGION_IN) { + DBG(("%s: region wholly contained within GPU damage\n", + __FUNCTION__)); + goto use_gpu_bo; + } + + if (ret != PIXMAN_REGION_OUT) { + DBG(("%s: region partially contained within GPU damage\n", + __FUNCTION__)); + goto move_to_gpu; + } + } + + if ((flags & IGNORE_CPU) == 0 && priv->cpu_damage) { + ret = sna_damage_contains_box(priv->cpu_damage, ®ion.extents); + if (ret == PIXMAN_REGION_IN) { + DBG(("%s: region wholly contained within CPU damage\n", + __FUNCTION__)); + goto use_cpu_bo; + } + + if (box_inplace(pixmap, box)) { + DBG(("%s: forcing inplace\n", __FUNCTION__)); + goto move_to_gpu; + } + + if (ret != PIXMAN_REGION_OUT) { + DBG(("%s: region partially contained within CPU damage\n", + __FUNCTION__)); + goto use_cpu_bo; + } + } + +move_to_gpu: + if (!sna_pixmap_move_area_to_gpu(pixmap, ®ion.extents, + flags & IGNORE_CPU ? MOVE_WRITE : MOVE_READ | MOVE_WRITE)) { + DBG(("%s: failed to move-to-gpu, fallback\n", __FUNCTION__)); + assert(priv->gpu_bo == NULL); + goto use_cpu_bo; + } + +done: + assert(priv->gpu_bo != NULL); + if (sna_damage_is_all(&priv->gpu_damage, + pixmap->drawable.width, + pixmap->drawable.height)) { + sna_damage_destroy(&priv->cpu_damage); + list_del(&priv->list); + priv->undamaged = false; + *damage = NULL; + } else + *damage = &priv->gpu_damage; + + DBG(("%s: using GPU bo with damage? %d\n", + __FUNCTION__, *damage != NULL)); + assert(priv->gpu_bo->proxy == NULL); + assert(priv->clear == false); + assert(priv->cpu == false); + return priv->gpu_bo; + +use_gpu_bo: + DBG(("%s: using whole GPU bo\n", __FUNCTION__)); + assert(priv->gpu_bo != NULL); + assert(priv->gpu_bo->proxy == NULL); + priv->clear = false; + priv->cpu = false; + if (USE_INACTIVE && + !priv->pinned && (priv->create & KGEM_CAN_CREATE_LARGE) == 0) + list_move(&priv->inactive, + &to_sna_from_pixmap(pixmap)->active_pixmaps); + *damage = NULL; + return priv->gpu_bo; + +use_cpu_bo: + if (!USE_CPU_BO) + return NULL; + + if (priv->cpu_bo == NULL) + return NULL; + + sna = to_sna_from_pixmap(pixmap); + if ((flags & FORCE_GPU) == 0 && + !__kgem_bo_is_busy(&sna->kgem, priv->cpu_bo)) { + DBG(("%s: has CPU bo, but is idle and acceleration not forced\n", + __FUNCTION__)); + return NULL; + } + + get_drawable_deltas(drawable, pixmap, &dx, &dy); + + region.extents = *box; + region.extents.x1 += dx; + region.extents.x2 += dx; + region.extents.y1 += dy; + region.extents.y2 += dy; + region.data = NULL; + + /* Both CPU and GPU are busy, prefer to use the GPU */ + if (priv->gpu_bo && kgem_bo_is_busy(priv->gpu_bo)) + goto move_to_gpu; + + assert(priv->gpu_bo == NULL || priv->gpu_bo->proxy == NULL); + + if (flags & RENDER_GPU) { + if (priv->gpu_bo && priv->gpu_bo->tiling) + goto move_to_gpu; + + if (priv->cpu_bo->pitch >= 4096) + goto move_to_gpu; + + if (!sna->kgem.can_blt_cpu) + goto move_to_gpu; + } + + if (!sna->kgem.can_blt_cpu) + return NULL; + + if (!sna_drawable_move_region_to_cpu(&pixmap->drawable, ®ion, + MOVE_READ | MOVE_ASYNC_HINT)) { + DBG(("%s: failed to move-to-cpu, fallback\n", __FUNCTION__)); + return NULL; + } + + if (sna_damage_is_all(&priv->cpu_damage, + pixmap->drawable.width, + pixmap->drawable.height)) { + sna_damage_destroy(&priv->gpu_damage); + *damage = NULL; + } else { + if (priv->cpu_damage && + sna_damage_contains_box__no_reduce(priv->cpu_damage, + ®ion.extents)) + *damage = NULL; + else + *damage = &priv->cpu_damage; + } + + if (priv->shm) { + assert(!priv->flush); + sna_add_flush_pixmap(sna, priv, priv->cpu_bo); + + /* As we may have flushed and retired,, recheck for busy bo */ + if ((flags & FORCE_GPU) == 0 && !kgem_bo_is_busy(priv->cpu_bo)) + return NULL; + } + if (priv->flush) { + assert(!priv->shm); + sna_add_flush_pixmap(sna, priv, priv->gpu_bo); + } + + DBG(("%s: using CPU bo with damage? %d\n", + __FUNCTION__, *damage != NULL)); + assert(priv->clear == false); + return priv->cpu_bo; +} + +PixmapPtr +sna_pixmap_create_upload(ScreenPtr screen, + int width, int height, int depth, + unsigned flags) +{ + struct sna *sna = to_sna_from_screen(screen); + PixmapPtr pixmap; + struct sna_pixmap *priv; + int bpp = bits_per_pixel(depth); + void *ptr; + + DBG(("%s(%d, %d, %d, flags=%x)\n", __FUNCTION__, + width, height, depth, flags)); + assert(width); + assert(height); + + if (sna->freed_pixmap) { + pixmap = sna->freed_pixmap; + sna->freed_pixmap = pixmap->devPrivate.ptr; + + pixmap->drawable.serialNumber = NEXT_SERIAL_NUMBER; + pixmap->refcnt = 1; + } else { + pixmap = create_pixmap(sna, screen, 0, 0, depth, 0); + if (!pixmap) + return NullPixmap; + + priv = malloc(sizeof(*priv)); + if (!priv) { + FreePixmap(pixmap); + return NullPixmap; + } + + sna_set_pixmap(pixmap, priv); + } + + priv = _sna_pixmap_reset(pixmap); + priv->header = true; + + priv->gpu_bo = kgem_create_buffer_2d(&sna->kgem, + width, height, bpp, + flags, &ptr); + if (!priv->gpu_bo) { + free(priv); + FreePixmap(pixmap); + return NullPixmap; + } + + /* Marking both the shadow and the GPU bo is a little dubious, + * but will work so long as we always check before doing the + * transfer. + */ + sna_damage_all(&priv->gpu_damage, width, height); + sna_damage_all(&priv->cpu_damage, width, height); + + pixmap->drawable.width = width; + pixmap->drawable.height = height; + pixmap->drawable.depth = depth; + pixmap->drawable.bitsPerPixel = bpp; + pixmap->drawable.serialNumber = NEXT_SERIAL_NUMBER; + pixmap->devKind = priv->gpu_bo->pitch; + pixmap->devPrivate.ptr = ptr; + + pixmap->usage_hint = 0; + if (!kgem_buffer_is_inplace(priv->gpu_bo)) + pixmap->usage_hint = 1; + + DBG(("%s: serial=%ld, usage=%d\n", + __FUNCTION__, + pixmap->drawable.serialNumber, + pixmap->usage_hint)); + + return pixmap; +} + +struct sna_pixmap * +sna_pixmap_move_to_gpu(PixmapPtr pixmap, unsigned flags) +{ + struct sna *sna = to_sna_from_pixmap(pixmap); + struct sna_pixmap *priv; + BoxPtr box; + int n; + + DBG(("%s(pixmap=%ld, usage=%d), flags=%x\n", + __FUNCTION__, + pixmap->drawable.serialNumber, + pixmap->usage_hint, + flags)); + + if ((flags & __MOVE_FORCE) == 0 && wedged(sna)) + return NULL; + + priv = sna_pixmap(pixmap); + if (priv == NULL) { + DBG(("%s: not attached\n", __FUNCTION__)); + if ((flags & __MOVE_DRI) == 0) + return NULL; + + DBG(("%s: forcing the creation on the GPU\n", __FUNCTION__)); + + priv = sna_pixmap_attach(pixmap); + if (priv == NULL) + return NULL; + + sna_damage_all(&priv->cpu_damage, + pixmap->drawable.width, + pixmap->drawable.height); + } + + if (sna_damage_is_all(&priv->gpu_damage, + pixmap->drawable.width, + pixmap->drawable.height)) { + DBG(("%s: already all-damaged\n", __FUNCTION__)); + sna_damage_destroy(&priv->cpu_damage); + list_del(&priv->list); + priv->undamaged = false; + assert(priv->cpu == false); + goto active; + } + + if (flags & MOVE_WRITE && priv->gpu_bo && priv->gpu_bo->proxy) { + DBG(("%s: discarding cached upload buffer\n", __FUNCTION__)); + kgem_bo_destroy(&sna->kgem, priv->gpu_bo); + priv->gpu_bo = NULL; + } + + if ((flags & MOVE_READ) == 0) + sna_damage_destroy(&priv->cpu_damage); + + sna_damage_reduce(&priv->cpu_damage); + assert_pixmap_damage(pixmap); + DBG(("%s: CPU damage? %d\n", __FUNCTION__, priv->cpu_damage != NULL)); + if (priv->gpu_bo == NULL) { + DBG(("%s: creating GPU bo (%dx%d@%d), create=%x\n", + __FUNCTION__, + pixmap->drawable.width, + pixmap->drawable.height, + pixmap->drawable.bitsPerPixel, + priv->create)); + assert(!priv->mapped); + if (flags & __MOVE_FORCE || priv->create & KGEM_CAN_CREATE_GPU) { + unsigned create, tiling; + + assert(pixmap->drawable.width > 0); + assert(pixmap->drawable.height > 0); + assert(pixmap->drawable.bitsPerPixel >= 8); + + tiling = (flags & MOVE_SOURCE_HINT) ? I915_TILING_Y : DEFAULT_TILING; + tiling = sna_pixmap_choose_tiling(pixmap, tiling); + + create = 0; + if (priv->cpu_damage && priv->cpu_bo == NULL) + create = CREATE_GTT_MAP | CREATE_INACTIVE; + + priv->gpu_bo = + kgem_create_2d(&sna->kgem, + pixmap->drawable.width, + pixmap->drawable.height, + pixmap->drawable.bitsPerPixel, + tiling, create); + } + if (priv->gpu_bo == NULL) { + DBG(("%s: not creating GPU bo\n", __FUNCTION__)); + assert(list_is_empty(&priv->list)); + return NULL; + } + + if (flags & MOVE_WRITE && priv->cpu_damage == NULL) { + /* Presume that we will only ever write to the GPU + * bo. Readbacks are expensive but fairly constant + * in cost for all sizes i.e. it is the act of + * synchronisation that takes the most time. This is + * mitigated by avoiding fallbacks in the first place. + */ + sna_damage_all(&priv->gpu_damage, + pixmap->drawable.width, + pixmap->drawable.height); + DBG(("%s: marking as all-damaged for GPU\n", + __FUNCTION__)); + goto active; + } + } + + if (priv->gpu_bo->proxy) { + DBG(("%s: reusing cached upload\n", __FUNCTION__)); + assert((flags & MOVE_WRITE) == 0); + return priv; + } + + if (priv->cpu_damage == NULL) + goto done; + + if (priv->mapped) { + assert(priv->stride); + pixmap->devPrivate.ptr = priv->ptr; + pixmap->devKind = priv->stride; + priv->mapped = false; + } + + n = sna_damage_get_boxes(priv->cpu_damage, &box); + if (n) { + bool ok; + + assert(pixmap_contains_damage(pixmap, priv->cpu_damage)); + DBG(("%s: uploading %d damage boxes\n", __FUNCTION__, n)); + + if (!priv->cpu) + flags |= MOVE_ASYNC_HINT; + + ok = false; + if (use_cpu_bo_for_upload(sna, priv, flags)) { + DBG(("%s: using CPU bo for upload to GPU\n", __FUNCTION__)); + ok = sna->render.copy_boxes(sna, GXcopy, + pixmap, priv->cpu_bo, 0, 0, + pixmap, priv->gpu_bo, 0, 0, + box, n, 0); + } + if (!ok) { + if (pixmap->devPrivate.ptr == NULL) { + assert(priv->stride && priv->ptr); + pixmap->devPrivate.ptr = priv->ptr; + pixmap->devKind = priv->stride; + } + if (n == 1 && !priv->pinned && + (box->x2 - box->x1) >= pixmap->drawable.width && + (box->y2 - box->y1) >= pixmap->drawable.height) { + ok = sna_replace(sna, pixmap, + &priv->gpu_bo, + pixmap->devPrivate.ptr, + pixmap->devKind); + } else { + ok = sna_write_boxes(sna, pixmap, + priv->gpu_bo, 0, 0, + pixmap->devPrivate.ptr, + pixmap->devKind, + 0, 0, + box, n); + } + if (!ok) + return NULL; + } + } + + __sna_damage_destroy(DAMAGE_PTR(priv->cpu_damage)); + priv->cpu_damage = NULL; + priv->undamaged = true; + + if (priv->shm) { + assert(!priv->flush); + sna_add_flush_pixmap(sna, priv, priv->cpu_bo); + } + + /* For large bo, try to keep only a single copy around */ + if (priv->create & KGEM_CAN_CREATE_LARGE) { + sna_damage_all(&priv->gpu_damage, + pixmap->drawable.width, + pixmap->drawable.height); + sna_pixmap_free_cpu(sna, priv); + } +done: + list_del(&priv->list); + + sna_damage_reduce_all(&priv->gpu_damage, + pixmap->drawable.width, + pixmap->drawable.height); + if (DAMAGE_IS_ALL(priv->gpu_damage)) { + priv->undamaged = false; + if (priv->ptr) { + assert(priv->cpu_bo == NULL || !priv->cpu_bo->flush); + assert(!priv->shm); + sna_pixmap_free_cpu(sna, priv); + } + } + +active: + if (flags & MOVE_WRITE) + priv->clear = false; + priv->cpu = false; + assert(!priv->gpu_bo->proxy || (flags & MOVE_WRITE) == 0); + return sna_pixmap_mark_active(sna, priv); +} + +static bool must_check sna_validate_pixmap(DrawablePtr draw, PixmapPtr pixmap) +{ + if (draw->bitsPerPixel == pixmap->drawable.bitsPerPixel && + FbEvenTile(pixmap->drawable.width * + pixmap->drawable.bitsPerPixel)) { + DBG(("%s: flushing pixmap\n", __FUNCTION__)); + if (!sna_pixmap_move_to_cpu(pixmap, MOVE_READ)) + return false; + + fbPadPixmap(pixmap); + } + + return true; +} + +static bool must_check sna_gc_move_to_cpu(GCPtr gc, + DrawablePtr drawable, + RegionPtr region) +{ + struct sna_gc *sgc = sna_gc(gc); + long changes = sgc->changes; + + DBG(("%s, changes=%lx\n", __FUNCTION__, changes)); + + assert(gc->ops == (GCOps *)&sna_gc_ops); + gc->ops = (GCOps *)&sna_gc_ops__cpu; + + sgc->old_funcs = gc->funcs; + gc->funcs = (GCFuncs *)&sna_gc_funcs__cpu; + + sgc->priv = gc->pCompositeClip; + gc->pCompositeClip = region; + + if (gc->clientClipType == CT_PIXMAP) { + PixmapPtr clip = gc->clientClip; + gc->clientClip = BitmapToRegion(gc->pScreen, clip); + gc->pScreen->DestroyPixmap(clip); + gc->clientClipType = gc->clientClip ? CT_REGION : CT_NONE; + changes |= GCClipMask; + } else + changes &= ~GCClipMask; + + if (changes || drawable->serialNumber != sgc->serial) { + gc->serialNumber = sgc->serial; + + if (fb_gc(gc)->bpp != drawable->bitsPerPixel) { + changes |= GCStipple | GCForeground | GCBackground | GCPlaneMask; + fb_gc(gc)->bpp = drawable->bitsPerPixel; + } + + if (changes & GCTile && !gc->tileIsPixel) { + DBG(("%s: flushing tile pixmap\n", __FUNCTION__)); + if (!sna_validate_pixmap(drawable, gc->tile.pixmap)) + return false; + } + + if (changes & GCStipple && gc->stipple) { + DBG(("%s: flushing stipple pixmap\n", __FUNCTION__)); + if (!sna_validate_pixmap(drawable, gc->stipple)) + return false; + } + + fbValidateGC(gc, changes, drawable); + + gc->serialNumber = drawable->serialNumber; + sgc->serial = drawable->serialNumber; + } + sgc->changes = 0; + + switch (gc->fillStyle) { + case FillTiled: + return sna_drawable_move_to_cpu(&gc->tile.pixmap->drawable, MOVE_READ); + case FillStippled: + case FillOpaqueStippled: + return sna_drawable_move_to_cpu(&gc->stipple->drawable, MOVE_READ); + default: + return true; + } +} + +static void sna_gc_move_to_gpu(GCPtr gc) +{ + assert(gc->ops == (GCOps *)&sna_gc_ops__cpu); + assert(gc->funcs == (GCFuncs *)&sna_gc_funcs__cpu); + + gc->ops = (GCOps *)&sna_gc_ops; + gc->funcs = sna_gc(gc)->old_funcs; + gc->pCompositeClip = sna_gc(gc)->priv; +} + +static inline bool clip_box(BoxPtr box, GCPtr gc) +{ + const BoxRec *clip; + bool clipped; + + clip = &gc->pCompositeClip->extents; + + clipped = !region_is_singular(gc->pCompositeClip); + if (box->x1 < clip->x1) + box->x1 = clip->x1, clipped = true; + if (box->x2 > clip->x2) + box->x2 = clip->x2, clipped = true; + + if (box->y1 < clip->y1) + box->y1 = clip->y1, clipped = true; + if (box->y2 > clip->y2) + box->y2 = clip->y2, clipped = true; + + return clipped; +} + +static inline void translate_box(BoxPtr box, DrawablePtr d) +{ + box->x1 += d->x; + box->x2 += d->x; + + box->y1 += d->y; + box->y2 += d->y; +} + +static inline bool trim_and_translate_box(BoxPtr box, DrawablePtr d, GCPtr gc) +{ + translate_box(box, d); + return clip_box(box, gc); +} + +static inline bool box32_clip(Box32Rec *box, GCPtr gc) +{ + bool clipped = !region_is_singular(gc->pCompositeClip); + const BoxRec *clip = &gc->pCompositeClip->extents; + + if (box->x1 < clip->x1) + box->x1 = clip->x1, clipped = true; + if (box->x2 > clip->x2) + box->x2 = clip->x2, clipped = true; + + if (box->y1 < clip->y1) + box->y1 = clip->y1, clipped = true; + if (box->y2 > clip->y2) + box->y2 = clip->y2, clipped = true; + + return clipped; +} + +static inline void box32_translate(Box32Rec *box, DrawablePtr d) +{ + box->x1 += d->x; + box->x2 += d->x; + + box->y1 += d->y; + box->y2 += d->y; +} + +static inline bool box32_trim_and_translate(Box32Rec *box, DrawablePtr d, GCPtr gc) +{ + box32_translate(box, d); + return box32_clip(box, gc); +} + +static inline void box_add_pt(BoxPtr box, int16_t x, int16_t y) +{ + if (box->x1 > x) + box->x1 = x; + else if (box->x2 < x) + box->x2 = x; + + if (box->y1 > y) + box->y1 = y; + else if (box->y2 < y) + box->y2 = y; +} + +static int16_t bound(int16_t a, uint16_t b) +{ + int v = (int)a + (int)b; + if (v > MAXSHORT) + return MAXSHORT; + return v; +} + +static int16_t clamp(int16_t a, int16_t b) +{ + int v = (int)a + (int)b; + if (v > MAXSHORT) + return MAXSHORT; + if (v < MINSHORT) + return MINSHORT; + return v; +} + +static inline bool box32_to_box16(const Box32Rec *b32, BoxRec *b16) +{ + b16->x1 = b32->x1; + b16->y1 = b32->y1; + b16->x2 = b32->x2; + b16->y2 = b32->y2; + + return b16->x2 > b16->x1 && b16->y2 > b16->y1; +} + +static inline void box32_add_rect(Box32Rec *box, const xRectangle *r) +{ + int32_t v; + + v = r->x; + if (box->x1 > v) + box->x1 = v; + v += r->width; + if (box->x2 < v) + box->x2 = v; + + v = r->y; + if (box->y1 > v) + box->y1 = v; + v += r->height; + if (box->y2 < v) + box->y2 = v; +} + +static bool +sna_put_image_upload_blt(DrawablePtr drawable, GCPtr gc, RegionPtr region, + int x, int y, int w, int h, char *bits, int stride) +{ + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna *sna = to_sna_from_pixmap(pixmap); + struct sna_pixmap *priv = sna_pixmap(pixmap); + BoxPtr box; + int nbox; + int16_t dx, dy; + + box = REGION_RECTS(region); + nbox = REGION_NUM_RECTS(region); + + DBG(("%s: %d x [(%d, %d), (%d, %d)...]\n", + __FUNCTION__, nbox, + box->x1, box->y1, box->x2, box->y2)); + + if (gc->alu != GXcopy) + return false; + + if (priv->gpu_bo == NULL && + !sna_pixmap_create_mappable_gpu(pixmap)) + return false; + + assert(priv->gpu_bo); + assert(priv->gpu_bo->proxy == NULL); + + if (!priv->pinned && nbox == 1 && + box->x1 <= 0 && box->y1 <= 0 && + box->x2 >= pixmap->drawable.width && + box->y2 >= pixmap->drawable.height) + return sna_replace(sna, pixmap, &priv->gpu_bo, bits, stride); + + get_drawable_deltas(drawable, pixmap, &dx, &dy); + x += dx + drawable->x; + y += dy + drawable->y; + + return sna_write_boxes(sna, pixmap, + priv->gpu_bo, 0, 0, + bits, stride, -x, -y, + box, nbox); +} + +static bool upload_inplace(struct sna *sna, + PixmapPtr pixmap, + struct sna_pixmap *priv, + RegionRec *region) +{ + if (priv->shm) { + DBG(("%s: no, SHM Pixmap\n", __FUNCTION__)); + return false; + } + + if (priv->create & KGEM_CAN_CREATE_LARGE) { + if (priv->gpu_bo) { + DBG(("%s: yes, large buffer and already have GPU bo\n", + __FUNCTION__)); + return true; + } + if (priv->cpu_bo){ + DBG(("%s: no, large buffer and already have CPU bo\n", + __FUNCTION__)); + return false; + } + } + + if (priv->cpu_bo && kgem_bo_is_busy(priv->cpu_bo) && + !(priv->gpu_bo && kgem_bo_is_busy(priv->gpu_bo))) { + DBG(("%s: yes, CPU bo is busy, but the GPU bo is not\n", __FUNCTION__)); + return true; + } + + if (!region_inplace(sna, pixmap, region, priv, true)) { + DBG(("%s? no, region not suitable\n", __FUNCTION__)); + return false; + } + + if (sna->kgem.has_llc && !priv->flush) { + if (priv->cpu_bo) { + if (priv->cpu_damage && + kgem_bo_is_busy(priv->cpu_bo) && + !region_subsumes_damage(region, priv->cpu_damage)) { + DBG(("%s? yes, CPU bo is busy\n", __FUNCTION__)); + return true; + } + + DBG(("%s? no, have CPU bo\n", __FUNCTION__)); + return false; + } + + if (priv->create & KGEM_CAN_CREATE_CPU) { + DBG(("%s? no, can create CPU bo\n", __FUNCTION__)); + return false; + } + } + + if (priv->gpu_bo) { + if (priv->gpu_bo->proxy) + return false; + + if (!kgem_bo_can_map(&sna->kgem, priv->gpu_bo)) { + DBG(("%s? no, GPU bo not mappable\n", __FUNCTION__)); + return false; + } + + if (!kgem_bo_is_busy(priv->gpu_bo)) { + DBG(("%s? yes, GPU bo is idle\n", __FUNCTION__)); + return true; + } + + if (!priv->pinned && + region_subsumes_drawable(region, &pixmap->drawable)) { + DBG(("%s? yes, will replace busy GPU\n", __FUNCTION__)); + return true; + } + } + + if ((priv->create & (KGEM_CAN_CREATE_GPU | KGEM_CAN_CREATE_CPU)) == KGEM_CAN_CREATE_GPU && + region_subsumes_drawable(region, &pixmap->drawable)) { + DBG(("%s? yes, will fill fresh GPU bo\n", __FUNCTION__)); + return true; + } + + return false; +} + +static bool +sna_put_zpixmap_blt(DrawablePtr drawable, GCPtr gc, RegionPtr region, + int x, int y, int w, int h, char *bits, int stride) +{ + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna *sna = to_sna_from_pixmap(pixmap); + struct sna_pixmap *priv = sna_pixmap(pixmap); + char *dst_bits; + int dst_stride; + BoxRec *box; + int16_t dx, dy; + int n; + + assert_pixmap_contains_box(pixmap, RegionExtents(region)); + + if (gc->alu != GXcopy) + return false; + + if (!priv) { + if (drawable->depth < 8) + return false; + + goto blt; + } + + /* XXX performing the upload inplace is currently about 20x slower + * for putimage10 on gen6 -- mostly due to slow page faulting in kernel. + * So we try again with vma caching and only for pixmaps who will be + * immediately flushed... + */ + if (upload_inplace(sna, pixmap, priv, region) && + sna_put_image_upload_blt(drawable, gc, region, + x, y, w, h, bits, stride)) { + if (!DAMAGE_IS_ALL(priv->gpu_damage)) { + DBG(("%s: marking damage\n", __FUNCTION__)); + if (region_subsumes_drawable(region, &pixmap->drawable)) + sna_damage_destroy(&priv->cpu_damage); + else + sna_damage_subtract(&priv->cpu_damage, region); + if (priv->cpu_damage == NULL) { + sna_damage_all(&priv->gpu_damage, + pixmap->drawable.width, + pixmap->drawable.height); + list_del(&priv->list); + priv->undamaged = false; + } else + sna_damage_add(&priv->gpu_damage, region); + } + + /* And mark as having a valid GTT mapping for future uploads */ + if (priv->stride && kgem_bo_can_map(&sna->kgem, priv->gpu_bo)) { + pixmap->devPrivate.ptr = + kgem_bo_map__async(&sna->kgem, priv->gpu_bo); + if (pixmap->devPrivate.ptr) { + priv->mapped = true; + pixmap->devKind = priv->gpu_bo->pitch; + } + } + + assert_pixmap_damage(pixmap); + priv->clear = false; + priv->cpu = false; + return true; + } + + if (priv->gpu_bo && priv->gpu_bo->proxy) { + DBG(("%s: discarding cached upload buffer\n", __FUNCTION__)); + kgem_bo_destroy(&sna->kgem, priv->gpu_bo); + priv->gpu_bo = NULL; + } + + if (priv->mapped) { + assert(!priv->shm); + pixmap->devPrivate.ptr = NULL; + priv->mapped = false; + } + + /* If the GPU is currently accessing the CPU pixmap, then + * we will need to wait for that to finish before we can + * modify the memory. + * + * However, we can queue some writes to the GPU bo to avoid + * the wait. Or we can try to replace the CPU bo. + */ + if (!priv->shm && priv->cpu_bo && __kgem_bo_is_busy(&sna->kgem, priv->cpu_bo)) { + assert(!priv->cpu_bo->flush); + DBG(("%s: cpu bo will stall, upload damage and discard\n", + __FUNCTION__)); + if (priv->cpu_damage) { + if (!region_subsumes_drawable(region, &pixmap->drawable)) { + sna_damage_subtract(&priv->cpu_damage, region); + if (!sna_pixmap_move_to_gpu(pixmap, MOVE_READ | MOVE_ASYNC_HINT)) + return false; + } else { + sna_damage_destroy(&priv->cpu_damage); + priv->undamaged = false; + } + } + assert(priv->cpu_damage == NULL); + sna_damage_all(&priv->gpu_damage, + pixmap->drawable.width, + pixmap->drawable.height); + sna_pixmap_free_cpu(sna, priv); + assert(pixmap->devPrivate.ptr == NULL); + } + + if (pixmap->devPrivate.ptr == NULL && + !sna_pixmap_alloc_cpu(sna, pixmap, priv, false)) + return true; + + if (priv->cpu_bo) { + DBG(("%s: syncing CPU bo\n", __FUNCTION__)); + kgem_bo_sync__cpu(&sna->kgem, priv->cpu_bo); + } + + if (priv->clear) { + DBG(("%s: applying clear [%08x]\n", + __FUNCTION__, priv->clear_color)); + + if (priv->clear_color == 0) { + memset(pixmap->devPrivate.ptr, + 0, pixmap->devKind * pixmap->drawable.height); + } else { + pixman_fill(pixmap->devPrivate.ptr, + pixmap->devKind/sizeof(uint32_t), + pixmap->drawable.bitsPerPixel, + 0, 0, + pixmap->drawable.width, + pixmap->drawable.height, + priv->clear_color); + } + + sna_damage_all(&priv->cpu_damage, + pixmap->drawable.width, + pixmap->drawable.height); + sna_pixmap_free_gpu(sna, priv); + priv->undamaged = false; + } + + if (!DAMAGE_IS_ALL(priv->cpu_damage)) { + DBG(("%s: marking damage\n", __FUNCTION__)); + if (region_subsumes_drawable(region, &pixmap->drawable)) { + DBG(("%s: replacing entire pixmap\n", __FUNCTION__)); + sna_damage_all(&priv->cpu_damage, + pixmap->drawable.width, + pixmap->drawable.height); + sna_pixmap_free_gpu(sna, priv); + priv->undamaged = false; + assert(priv->gpu_damage == NULL); + } else { + sna_damage_subtract(&priv->gpu_damage, region); + sna_damage_add(&priv->cpu_damage, region); + if (priv->gpu_bo && + sna_damage_is_all(&priv->cpu_damage, + pixmap->drawable.width, + pixmap->drawable.height)) { + DBG(("%s: replaced entire pixmap\n", __FUNCTION__)); + sna_pixmap_free_gpu(sna, priv); + priv->undamaged = false; + } + } + if (priv->flush) { + assert(!priv->shm); + sna_add_flush_pixmap(sna, priv, priv->gpu_bo); + } + } + assert(!priv->flush || !list_is_empty(&priv->list)); + priv->cpu = true; + +blt: + get_drawable_deltas(drawable, pixmap, &dx, &dy); + x += dx + drawable->x; + y += dy + drawable->y; + + DBG(("%s: upload(%d, %d, %d, %d)\n", __FUNCTION__, x, y, w, h)); + + dst_stride = pixmap->devKind; + dst_bits = pixmap->devPrivate.ptr; + + /* Region is pre-clipped and translated into pixmap space */ + box = REGION_RECTS(region); + n = REGION_NUM_RECTS(region); + do { + DBG(("%s: copy box (%d, %d)->(%d, %d)x(%d, %d)\n", + __FUNCTION__, + box->x1 - x, box->y1 - y, + box->x1, box->y1, + box->x2 - box->x1, box->y2 - box->y1)); + + assert(box->x2 > box->x1); + assert(box->y2 > box->y1); + + assert(box->x1 >= 0); + assert(box->y1 >= 0); + assert(box->x2 <= pixmap->drawable.width); + assert(box->y2 <= pixmap->drawable.height); + + assert(box->x1 - x >= 0); + assert(box->y1 - y >= 0); + assert(box->x2 - x <= w); + assert(box->y2 - y <= h); + + memcpy_blt(bits, dst_bits, + pixmap->drawable.bitsPerPixel, + stride, dst_stride, + box->x1 - x, box->y1 - y, + box->x1, box->y1, + box->x2 - box->x1, box->y2 - box->y1); + box++; + } while (--n); + + assert_pixmap_damage(pixmap); + return true; +} + +static inline uint8_t byte_reverse(uint8_t b) +{ + return ((b * 0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL >> 32; +} + +static inline uint8_t blt_depth(int depth) +{ + switch (depth) { + case 8: return 0; + case 15: return 0x2; + case 16: return 0x1; + default: return 0x3; + } +} + +static bool +sna_put_xybitmap_blt(DrawablePtr drawable, GCPtr gc, RegionPtr region, + int x, int y, int w, int h, char *bits) +{ + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna *sna = to_sna_from_pixmap(pixmap); + struct sna_damage **damage; + struct kgem_bo *bo; + BoxRec *box; + int16_t dx, dy; + int n; + uint8_t rop = copy_ROP[gc->alu]; + + bo = sna_drawable_use_bo(&pixmap->drawable, PREFER_GPU, + ®ion->extents, &damage); + if (bo == NULL) + return false; + + if (bo->tiling == I915_TILING_Y) { + DBG(("%s: converting bo from Y-tiling\n", __FUNCTION__)); + assert(bo == sna_pixmap_get_bo(pixmap)); + bo = sna_pixmap_change_tiling(pixmap, I915_TILING_X); + if (bo == NULL) { + DBG(("%s: fallback -- unable to change tiling\n", + __FUNCTION__)); + return false; + } + } + + assert_pixmap_contains_box(pixmap, RegionExtents(region)); + if (damage) + sna_damage_add(damage, region); + assert_pixmap_damage(pixmap); + + DBG(("%s: upload(%d, %d, %d, %d)\n", __FUNCTION__, x, y, w, h)); + + get_drawable_deltas(drawable, pixmap, &dx, &dy); + x += dx + drawable->x; + y += dy + drawable->y; + + kgem_set_mode(&sna->kgem, KGEM_BLT); + + /* Region is pre-clipped and translated into pixmap space */ + box = REGION_RECTS(region); + n = REGION_NUM_RECTS(region); + do { + int bx1 = (box->x1 - x) & ~7; + int bx2 = (box->x2 - x + 7) & ~7; + int bw = (bx2 - bx1)/8; + int bh = box->y2 - box->y1; + int bstride = ALIGN(bw, 2); + int src_stride; + uint8_t *dst, *src; + uint32_t *b; + struct kgem_bo *upload; + void *ptr; + + if (!kgem_check_batch(&sna->kgem, 8) || + !kgem_check_bo_fenced(&sna->kgem, bo) || + !kgem_check_reloc_and_exec(&sna->kgem, 2)) { + _kgem_submit(&sna->kgem); + _kgem_set_mode(&sna->kgem, KGEM_BLT); + } + + upload = kgem_create_buffer(&sna->kgem, + bstride*bh, + KGEM_BUFFER_WRITE_INPLACE, + &ptr); + if (!upload) + break; + + dst = ptr; + bstride -= bw; + + src_stride = BitmapBytePad(w); + src = (uint8_t*)bits + (box->y1 - y) * src_stride + bx1/8; + src_stride -= bw; + do { + int i = bw; + do { + *dst++ = byte_reverse(*src++); + } while (--i); + dst += bstride; + src += src_stride; + } while (--bh); + + b = sna->kgem.batch + sna->kgem.nbatch; + b[0] = XY_MONO_SRC_COPY | 3 << 20; + b[0] |= ((box->x1 - x) & 7) << 17; + b[1] = bo->pitch; + if (sna->kgem.gen >= 40 && bo->tiling) { + b[0] |= BLT_DST_TILED; + b[1] >>= 2; + } + b[1] |= blt_depth(drawable->depth) << 24; + b[1] |= rop << 16; + b[2] = box->y1 << 16 | box->x1; + b[3] = box->y2 << 16 | box->x2; + b[4] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 4, bo, + I915_GEM_DOMAIN_RENDER << 16 | + I915_GEM_DOMAIN_RENDER | + KGEM_RELOC_FENCED, + 0); + b[5] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 5, + upload, + I915_GEM_DOMAIN_RENDER << 16 | + KGEM_RELOC_FENCED, + 0); + b[6] = gc->bgPixel; + b[7] = gc->fgPixel; + + sna->kgem.nbatch += 8; + kgem_bo_destroy(&sna->kgem, upload); + + box++; + } while (--n); + + sna->blt_state.fill_bo = 0; + return true; +} + +static bool +sna_put_xypixmap_blt(DrawablePtr drawable, GCPtr gc, RegionPtr region, + int x, int y, int w, int h, int left,char *bits) +{ + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna *sna = to_sna_from_pixmap(pixmap); + struct sna_damage **damage; + struct kgem_bo *bo; + int16_t dx, dy; + unsigned i, skip; + + if (gc->alu != GXcopy) + return false; + + bo = sna_drawable_use_bo(&pixmap->drawable, PREFER_GPU, + ®ion->extents, &damage); + if (bo == NULL) + return false; + + if (bo->tiling == I915_TILING_Y) { + DBG(("%s: converting bo from Y-tiling\n", __FUNCTION__)); + assert(bo == sna_pixmap_get_bo(pixmap)); + bo = sna_pixmap_change_tiling(pixmap, I915_TILING_X); + if (bo == NULL) { + DBG(("%s: fallback -- unable to change tiling\n", + __FUNCTION__)); + return false; + } + } + + assert_pixmap_contains_box(pixmap, RegionExtents(region)); + if (damage) + sna_damage_add(damage, region); + assert_pixmap_damage(pixmap); + + DBG(("%s: upload(%d, %d, %d, %d)\n", __FUNCTION__, x, y, w, h)); + + get_drawable_deltas(drawable, pixmap, &dx, &dy); + x += dx + drawable->x; + y += dy + drawable->y; + + kgem_set_mode(&sna->kgem, KGEM_BLT); + + skip = h * BitmapBytePad(w + left); + for (i = 1 << (gc->depth-1); i; i >>= 1, bits += skip) { + const BoxRec *box = REGION_RECTS(region); + int n = REGION_NUM_RECTS(region); + + if ((gc->planemask & i) == 0) + continue; + + /* Region is pre-clipped and translated into pixmap space */ + do { + int bx1 = (box->x1 - x) & ~7; + int bx2 = (box->x2 - x + 7) & ~7; + int bw = (bx2 - bx1)/8; + int bh = box->y2 - box->y1; + int bstride = ALIGN(bw, 2); + int src_stride; + uint8_t *dst, *src; + uint32_t *b; + struct kgem_bo *upload; + void *ptr; + + if (!kgem_check_batch(&sna->kgem, 12) || + !kgem_check_bo_fenced(&sna->kgem, bo) || + !kgem_check_reloc_and_exec(&sna->kgem, 2)) { + _kgem_submit(&sna->kgem); + _kgem_set_mode(&sna->kgem, KGEM_BLT); + } + + upload = kgem_create_buffer(&sna->kgem, + bstride*bh, + KGEM_BUFFER_WRITE_INPLACE, + &ptr); + if (!upload) + break; + + dst = ptr; + bstride -= bw; + + src_stride = BitmapBytePad(w); + src = (uint8_t*)bits + (box->y1 - y) * src_stride + bx1/8; + src_stride -= bw; + do { + int j = bw; + do { + *dst++ = byte_reverse(*src++); + } while (--j); + dst += bstride; + src += src_stride; + } while (--bh); + + b = sna->kgem.batch + sna->kgem.nbatch; + b[0] = XY_FULL_MONO_PATTERN_MONO_SRC_BLT | 3 << 20; + b[0] |= ((box->x1 - x) & 7) << 17; + b[1] = bo->pitch; + if (sna->kgem.gen >= 40 && bo->tiling) { + b[0] |= BLT_DST_TILED; + b[1] >>= 2; + } + b[1] |= 1 << 31; /* solid pattern */ + b[1] |= blt_depth(drawable->depth) << 24; + b[1] |= 0xce << 16; /* S or (D and !P) */ + b[2] = box->y1 << 16 | box->x1; + b[3] = box->y2 << 16 | box->x2; + b[4] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 4, + bo, + I915_GEM_DOMAIN_RENDER << 16 | + I915_GEM_DOMAIN_RENDER | + KGEM_RELOC_FENCED, + 0); + b[5] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 5, + upload, + I915_GEM_DOMAIN_RENDER << 16 | + KGEM_RELOC_FENCED, + 0); + b[6] = 0; + b[7] = i; + b[8] = i; + b[9] = i; + b[10] = -1; + b[11] = -1; + + sna->kgem.nbatch += 12; + kgem_bo_destroy(&sna->kgem, upload); + + box++; + } while (--n); + } + + sna->blt_state.fill_bo = 0; + return true; +} + +static void +sna_put_image(DrawablePtr drawable, GCPtr gc, int depth, + int x, int y, int w, int h, int left, int format, + char *bits) +{ + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna *sna = to_sna_from_pixmap(pixmap); + struct sna_pixmap *priv = sna_pixmap(pixmap); + RegionRec region; + int16_t dx, dy; + + DBG(("%s((%d, %d)x(%d, %d), depth=%d, format=%d)\n", + __FUNCTION__, x, y, w, h, depth, format)); + + if (w == 0 || h == 0) + return; + + get_drawable_deltas(drawable, pixmap, &dx, &dy); + + region.extents.x1 = x + drawable->x; + region.extents.y1 = y + drawable->y; + region.extents.x2 = region.extents.x1 + w; + region.extents.y2 = region.extents.y1 + h; + region.data = NULL; + + if (!region_is_singular(gc->pCompositeClip) || + gc->pCompositeClip->extents.x1 > region.extents.x1 || + gc->pCompositeClip->extents.y1 > region.extents.y1 || + gc->pCompositeClip->extents.x2 < region.extents.x2 || + gc->pCompositeClip->extents.y2 < region.extents.y2) { + RegionIntersect(®ion, ®ion, gc->pCompositeClip); + if (!RegionNotEmpty(®ion)) + return; + } + + if (priv == NULL) { + DBG(("%s: fallback -- unattached(%d, %d, %d, %d)\n", + __FUNCTION__, x, y, w, h)); + goto fallback; + } + + RegionTranslate(®ion, dx, dy); + + if (FORCE_FALLBACK) + goto fallback; + + if (wedged(sna)) + goto fallback; + + if (!ACCEL_PUT_IMAGE) + goto fallback; + + switch (format) { + case ZPixmap: + if (!PM_IS_SOLID(drawable, gc->planemask)) + goto fallback; + + if (sna_put_zpixmap_blt(drawable, gc, ®ion, + x, y, w, h, + bits, PixmapBytePad(w, depth))) + return; + break; + + case XYBitmap: + if (!PM_IS_SOLID(drawable, gc->planemask)) + goto fallback; + + if (sna_put_xybitmap_blt(drawable, gc, ®ion, + x, y, w, h, + bits)) + return; + break; + + case XYPixmap: + if (sna_put_xypixmap_blt(drawable, gc, ®ion, + x, y, w, h, left, + bits)) + return; + break; + + default: + return; + } + +fallback: + DBG(("%s: fallback\n", __FUNCTION__)); + RegionTranslate(®ion, -dx, -dy); + + if (!sna_gc_move_to_cpu(gc, drawable, ®ion)) + goto out; + if (!sna_drawable_move_region_to_cpu(drawable, ®ion, + format == XYPixmap ? + MOVE_READ | MOVE_WRITE : + drawable_gc_flags(drawable, gc, false))) + goto out_gc; + + DBG(("%s: fbPutImage(%d, %d, %d, %d)\n", + __FUNCTION__, x, y, w, h)); + fbPutImage(drawable, gc, depth, x, y, w, h, left, format, bits); + FALLBACK_FLUSH(drawable); +out_gc: + sna_gc_move_to_gpu(gc); +out: + RegionUninit(®ion); +} + +static bool +move_to_gpu(PixmapPtr pixmap, struct sna_pixmap *priv, + const BoxRec *box, uint8_t alu) +{ + int w = box->x2 - box->x1; + int h = box->y2 - box->y1; + int count; + + if (DAMAGE_IS_ALL(priv->gpu_damage)) + return true; + + if (priv->gpu_bo) { + if (alu != GXcopy) + return true; + + if (!priv->cpu) + return true; + + if (priv->gpu_bo->tiling) + return true; + } else { + if ((priv->create & KGEM_CAN_CREATE_GPU) == 0) + return false; + if (priv->shm) + return false; + } + + count = priv->source_count++; + if (priv->cpu_bo) { + if (priv->cpu_bo->flush && count > SOURCE_BIAS) + return true; + + if (sna_pixmap_choose_tiling(pixmap, + DEFAULT_TILING) == I915_TILING_NONE) + return false; + + if (priv->cpu) + return false; + + return count > SOURCE_BIAS; + } else { + if (w == pixmap->drawable.width && h == pixmap->drawable.height) + return count > SOURCE_BIAS; + + return count * w*h >= (SOURCE_BIAS+2) * (int)pixmap->drawable.width * pixmap->drawable.height; + } +} + +static BoxPtr +reorder_boxes(BoxPtr box, int n, int dx, int dy) +{ + BoxPtr new, base, next, tmp; + + DBG(("%s x %d dx=%d, dy=%d\n", __FUNCTION__, n, dx, dy)); + + if (dy <= 0 && dx <= 0) { + new = malloc(sizeof(BoxRec) * n); + if (new == NULL) + return NULL; + + tmp = new; + next = box + n; + do { + *tmp++ = *--next; + } while (next != box); + } else if (dy < 0) { + new = malloc(sizeof(BoxRec) * n); + if (new == NULL) + return NULL; + + base = next = box + n - 1; + while (base >= box) { + while (next >= box && base->y1 == next->y1) + next--; + tmp = next + 1; + while (tmp <= base) + *new++ = *tmp++; + base = next; + } + new -= n; + } else { + new = malloc(sizeof(BoxRec) * n); + if (!new) + return NULL; + + base = next = box; + while (base < box + n) { + while (next < box + n && next->y1 == base->y1) + next++; + tmp = next; + while (tmp != base) + *new++ = *--tmp; + base = next; + } + new -= n; + } + + return new; +} + +static void +sna_self_copy_boxes(DrawablePtr src, DrawablePtr dst, GCPtr gc, + RegionPtr region,int dx, int dy, + Pixel bitplane, void *closure) +{ + PixmapPtr pixmap = get_drawable_pixmap(src); + struct sna *sna = to_sna_from_pixmap(pixmap); + struct sna_pixmap *priv = sna_pixmap(pixmap); + BoxPtr box = RegionRects(region); + int n = RegionNumRects(region); + int alu = gc ? gc->alu : GXcopy; + int16_t tx, ty; + + assert(RegionNumRects(region)); + if (((dx | dy) == 0 && alu == GXcopy)) + return; + + if (n > 1 && (dx | dy) < 0) { + box = reorder_boxes(box, n, dx, dy); + if (box == NULL) + return; + } + + DBG(("%s (boxes=%dx[(%d, %d), (%d, %d)...], src=+(%d, %d), alu=%d, pix.size=%dx%d)\n", + __FUNCTION__, n, + region->extents.x1, region->extents.y1, + region->extents.x2, region->extents.y2, + dx, dy, alu, + pixmap->drawable.width, pixmap->drawable.height)); + + get_drawable_deltas(src, pixmap, &tx, &ty); + dx += tx; + dy += ty; + if (dst != src) + get_drawable_deltas(dst, pixmap, &tx, &ty); + + if (priv == NULL || DAMAGE_IS_ALL(priv->cpu_damage)) + goto fallback; + + if (priv->gpu_damage) { + if (alu == GXcopy && priv->clear) + goto out; + + assert(priv->gpu_bo->proxy == NULL); + if (!sna_pixmap_move_to_gpu(pixmap, MOVE_WRITE | MOVE_READ)) { + DBG(("%s: fallback - not a pure copy and failed to move dst to GPU\n", + __FUNCTION__)); + goto fallback; + } + + if (!sna->render.copy_boxes(sna, alu, + pixmap, priv->gpu_bo, dx, dy, + pixmap, priv->gpu_bo, tx, ty, + box, n, 0)) { + DBG(("%s: fallback - accelerated copy boxes failed\n", + __FUNCTION__)); + goto fallback; + } + + if (!DAMAGE_IS_ALL(priv->gpu_damage)) { + RegionTranslate(region, tx, ty); + sna_damage_add(&priv->gpu_damage, region); + } + assert_pixmap_damage(pixmap); + } else { +fallback: + DBG(("%s: fallback", __FUNCTION__)); + if (!sna_pixmap_move_to_cpu(pixmap, MOVE_READ | MOVE_WRITE)) + goto out; + + if (alu == GXcopy && pixmap->drawable.bitsPerPixel >= 8) { + FbBits *dst_bits, *src_bits; + int stride = pixmap->devKind; + int bpp = pixmap->drawable.bitsPerPixel; + int i; + + dst_bits = (FbBits *) + ((char *)pixmap->devPrivate.ptr + + ty * stride + tx * bpp / 8); + src_bits = (FbBits *) + ((char *)pixmap->devPrivate.ptr + + dy * stride + dx * bpp / 8); + + for (i = 0; i < n; i++) + memmove_box(src_bits, dst_bits, + bpp, stride, box+i, + dx, dy); + } else { + if (gc && !sna_gc_move_to_cpu(gc, dst, region)) + goto out; + + get_drawable_deltas(src, pixmap, &tx, &ty); + miCopyRegion(src, dst, gc, + region, dx - tx, dy - ty, + fbCopyNtoN, 0, NULL); + + if (gc) + sna_gc_move_to_gpu(gc); + } + } + +out: + if (box != RegionRects(region)) + free(box); +} + +static inline bool +sna_pixmap_is_gpu(PixmapPtr pixmap) +{ + struct sna_pixmap *priv = sna_pixmap(pixmap); + + if (priv == NULL || priv->clear) + return false; + + if (DAMAGE_IS_ALL(priv->gpu_damage) || + (priv->gpu_bo && kgem_bo_is_busy(priv->gpu_bo) && !priv->gpu_bo->proxy)) + return true; + + return priv->cpu_bo && kgem_bo_is_busy(priv->cpu_bo); +} + +static int +source_prefer_gpu(struct sna_pixmap *priv) +{ + if (priv == NULL) { + DBG(("%s: source unattached, use cpu\n", __FUNCTION__)); + return 0; + } + + if (priv->clear) { + DBG(("%s: source is clear, don't force use of GPU\n", __FUNCTION__)); + return 0; + } + + if (priv->gpu_damage) { + DBG(("%s: source has gpu damage, force gpu\n", __FUNCTION__)); + return PREFER_GPU | FORCE_GPU; + } + + if (priv->cpu_bo && kgem_bo_is_busy(priv->cpu_bo)) { + DBG(("%s: source has busy CPU bo, force gpu\n", __FUNCTION__)); + return PREFER_GPU | FORCE_GPU; + } + + if (DAMAGE_IS_ALL(priv->cpu_damage)) + return 0; + + DBG(("%s: source has GPU bo? %d\n", + __FUNCTION__, priv->gpu_bo != NULL)); + return priv->gpu_bo != NULL; +} + +static void +sna_copy_boxes(DrawablePtr src, DrawablePtr dst, GCPtr gc, + RegionPtr region, int dx, int dy, + Pixel bitplane, void *closure) +{ + PixmapPtr src_pixmap = get_drawable_pixmap(src); + struct sna_pixmap *src_priv = sna_pixmap(src_pixmap); + PixmapPtr dst_pixmap = get_drawable_pixmap(dst); + struct sna_pixmap *dst_priv = sna_pixmap(dst_pixmap); + struct sna *sna = to_sna_from_pixmap(src_pixmap); + struct sna_damage **damage; + struct kgem_bo *bo; + unsigned hint; + int16_t src_dx, src_dy; + int16_t dst_dx, dst_dy; + BoxPtr box = RegionRects(region); + int n = RegionNumRects(region); + int alu = gc->alu; + int stride, bpp; + char *bits; + bool replaces; + + assert(RegionNumRects(region)); + + if (src_pixmap == dst_pixmap) + return sna_self_copy_boxes(src, dst, gc, + region, dx, dy, + bitplane, closure); + + DBG(("%s (boxes=%dx[(%d, %d), (%d, %d)...], src=+(%d, %d), alu=%d, src.size=%dx%d, dst.size=%dx%d)\n", + __FUNCTION__, n, + box[0].x1, box[0].y1, box[0].x2, box[0].y2, + dx, dy, alu, + src_pixmap->drawable.width, src_pixmap->drawable.height, + dst_pixmap->drawable.width, dst_pixmap->drawable.height)); + + assert_pixmap_damage(dst_pixmap); + assert_pixmap_damage(src_pixmap); + + bpp = dst_pixmap->drawable.bitsPerPixel; + + get_drawable_deltas(dst, dst_pixmap, &dst_dx, &dst_dy); + RegionTranslate(region, dst_dx, dst_dy); + get_drawable_deltas(src, src_pixmap, &src_dx, &src_dy); + src_dx += dx - dst_dx; + src_dy += dy - dst_dy; + + assert_pixmap_contains_box(dst_pixmap, RegionExtents(region)); + assert_pixmap_contains_box_with_offset(src_pixmap, + RegionExtents(region), + src_dx, src_dy); + + replaces = n == 1 && + box->x1 <= 0 && + box->y1 <= 0 && + box->x2 >= dst_pixmap->drawable.width && + box->y2 >= dst_pixmap->drawable.height; + + DBG(("%s: dst=(priv=%p, gpu_bo=%p, cpu_bo=%p), src=(priv=%p, gpu_bo=%p, cpu_bo=%p), replaces=%d\n", + __FUNCTION__, + dst_priv, + dst_priv ? dst_priv->gpu_bo : NULL, + dst_priv ? dst_priv->cpu_bo : NULL, + src_priv, + src_priv ? src_priv->gpu_bo : NULL, + src_priv ? src_priv->cpu_bo : NULL, + replaces)); + + if (dst_priv == NULL) + goto fallback; + + hint = source_prefer_gpu(src_priv) ?: + region_inplace(sna, dst_pixmap, region, + dst_priv, alu_overwrites(alu)); + if (dst_priv->cpu_damage && alu_overwrites(alu)) { + DBG(("%s: overwritting CPU damage\n", __FUNCTION__)); + if (region_subsumes_damage(region, dst_priv->cpu_damage)) { + DBG(("%s: discarding existing CPU damage\n", __FUNCTION__)); + if (dst_priv->gpu_bo && dst_priv->gpu_bo->proxy) { + kgem_bo_destroy(&sna->kgem, dst_priv->gpu_bo); + dst_priv->gpu_bo = NULL; + } + sna_damage_destroy(&dst_priv->cpu_damage); + list_del(&dst_priv->list); + dst_priv->cpu = false; + } + if (region->data == NULL) + hint |= IGNORE_CPU; + } + + bo = sna_drawable_use_bo(&dst_pixmap->drawable, hint, + ®ion->extents, &damage); + if (bo) { + if (src_priv && src_priv->clear) { + DBG(("%s: applying src clear[%08x] to dst\n", + __FUNCTION__, src_priv->clear_color)); + if (n == 1) { + if (!sna->render.fill_one(sna, + dst_pixmap, bo, + src_priv->clear_color, + box->x1, box->y1, + box->x2, box->y2, + alu)) { + DBG(("%s: unsupported fill\n", + __FUNCTION__)); + goto fallback; + } + } else { + struct sna_fill_op fill; + + if (!sna_fill_init_blt(&fill, sna, + dst_pixmap, bo, + alu, src_priv->clear_color)) { + DBG(("%s: unsupported fill\n", + __FUNCTION__)); + goto fallback; + } + + fill.boxes(sna, &fill, box, n); + fill.done(sna, &fill); + } + + if (damage) + sna_damage_add(damage, region); + return; + } + + if (src_priv && + move_to_gpu(src_pixmap, src_priv, ®ion->extents, alu) && + sna_pixmap_move_to_gpu(src_pixmap, MOVE_READ)) { + DBG(("%s: move whole src_pixmap to GPU and copy\n", + __FUNCTION__)); + if (!sna->render.copy_boxes(sna, alu, + src_pixmap, src_priv->gpu_bo, src_dx, src_dy, + dst_pixmap, bo, 0, 0, + box, n, 0)) { + DBG(("%s: fallback - accelerated copy boxes failed\n", + __FUNCTION__)); + goto fallback; + } + + if (damage) + sna_damage_add(damage, region); + return; + } + + if (src_priv && + region_overlaps_damage(region, src_priv->gpu_damage, + src_dx, src_dy)) { + BoxRec area; + + DBG(("%s: region overlaps GPU damage, upload and copy\n", + __FUNCTION__)); + + area = region->extents; + area.x1 += src_dx; + area.x2 += src_dx; + area.y1 += src_dy; + area.y2 += src_dy; + + if (!sna_pixmap_move_area_to_gpu(src_pixmap, &area, + MOVE_READ)) + goto fallback; + + if (!sna->render.copy_boxes(sna, alu, + src_pixmap, src_priv->gpu_bo, src_dx, src_dy, + dst_pixmap, bo, 0, 0, + box, n, 0)) { + DBG(("%s: fallback - accelerated copy boxes failed\n", + __FUNCTION__)); + goto fallback; + } + + if (damage) + sna_damage_add(damage, region); + return; + } + + if (bo != dst_priv->gpu_bo) + goto fallback; + + if (src_priv && src_priv->cpu_bo) { + bool ret; + + DBG(("%s: region overlaps CPU damage, copy from CPU bo\n", + __FUNCTION__)); + + assert(bo != dst_priv->cpu_bo); + + RegionTranslate(region, src_dx, src_dy); + ret = sna_drawable_move_region_to_cpu(&src_pixmap->drawable, + region, + MOVE_READ | MOVE_ASYNC_HINT); + RegionTranslate(region, -src_dx, -src_dy); + if (!ret) + goto fallback; + + if (!sna->render.copy_boxes(sna, alu, + src_pixmap, src_priv->cpu_bo, src_dx, src_dy, + dst_pixmap, bo, 0, 0, + box, n, 0)) { + DBG(("%s: fallback - accelerated copy boxes failed\n", + __FUNCTION__)); + goto fallback; + } + + if (src_priv->shm) { + assert(!src_priv->flush); + sna_add_flush_pixmap(sna, src_priv, src_priv->cpu_bo); + } + + if (damage) + sna_damage_add(damage, region); + return; + } + + if (src_priv == NULL && + sna->kgem.has_userptr && + __kgem_bo_is_busy(&sna->kgem, bo) && + box_inplace(src_pixmap, ®ion->extents)) { + struct kgem_bo *src_bo; + bool ok = false; + + DBG(("%s: upload through a temporary map\n", + __FUNCTION__)); + + src_bo = kgem_create_map(&sna->kgem, + src_pixmap->devPrivate.ptr, + src_pixmap->devKind * src_pixmap->drawable.height, + true); + if (src_bo) { + src_bo->flush = true; + src_bo->pitch = src_pixmap->devKind; + src_bo->reusable = false; + + ok = sna->render.copy_boxes(sna, alu, + src_pixmap, src_bo, src_dx, src_dy, + dst_pixmap, bo, 0, 0, + box, n, COPY_LAST); + + kgem_bo_sync__cpu(&sna->kgem, src_bo); + kgem_bo_destroy(&sna->kgem, src_bo); + } + + if (ok) { + if (damage) + sna_damage_add(damage, region); + return; + } + } + + if (alu != GXcopy) { + PixmapPtr tmp; + struct kgem_bo *src_bo; + int i; + + assert(src_pixmap->drawable.depth != 1); + + DBG(("%s: creating temporary source upload for non-copy alu [%d]\n", + __FUNCTION__, alu)); + + tmp = sna_pixmap_create_upload(src->pScreen, + region->extents.x2 - region->extents.x1, + region->extents.y2 - region->extents.y1, + src->depth, + KGEM_BUFFER_WRITE_INPLACE); + if (tmp == NullPixmap) + return; + + src_bo = sna_pixmap_get_bo(tmp); + assert(src_bo != NULL); + + dx = -region->extents.x1; + dy = -region->extents.y1; + for (i = 0; i < n; i++) { + assert(box[i].x1 + src_dx >= 0); + assert(box[i].y1 + src_dy >= 0); + assert(box[i].x2 + src_dx <= src_pixmap->drawable.width); + assert(box[i].y2 + src_dy <= src_pixmap->drawable.height); + + assert(box[i].x1 + dx >= 0); + assert(box[i].y1 + dy >= 0); + assert(box[i].x2 + dx <= tmp->drawable.width); + assert(box[i].y2 + dy <= tmp->drawable.height); + + memcpy_blt(src_pixmap->devPrivate.ptr, + tmp->devPrivate.ptr, + src_pixmap->drawable.bitsPerPixel, + src_pixmap->devKind, + tmp->devKind, + box[i].x1 + src_dx, + box[i].y1 + src_dy, + box[i].x1 + dx, + box[i].y1 + dy, + box[i].x2 - box[i].x1, + box[i].y2 - box[i].y1); + } + + if (n == 1 && + tmp->drawable.width == src_pixmap->drawable.width && + tmp->drawable.height == src_pixmap->drawable.height) + kgem_proxy_bo_attach(src_bo, &src_priv->gpu_bo); + + if (!sna->render.copy_boxes(sna, alu, + tmp, src_bo, dx, dy, + dst_pixmap, bo, 0, 0, + box, n, 0)) { + DBG(("%s: fallback - accelerated copy boxes failed\n", + __FUNCTION__)); + tmp->drawable.pScreen->DestroyPixmap(tmp); + goto fallback; + } + tmp->drawable.pScreen->DestroyPixmap(tmp); + + if (damage) + sna_damage_add(damage, region); + return; + } else { + DBG(("%s: dst is on the GPU, src is on the CPU, uploading into dst\n", + __FUNCTION__)); + + if (src_priv) { + /* Fixup the shadow pointer as necessary */ + if (src_priv->mapped) { + assert(!src_priv->shm); + src_pixmap->devPrivate.ptr = NULL; + src_priv->mapped = false; + } + if (src_pixmap->devPrivate.ptr == NULL) { + if (!src_priv->ptr) /* uninitialised!*/ + return; + assert(src_priv->stride); + src_pixmap->devPrivate.ptr = src_priv->ptr; + src_pixmap->devKind = src_priv->stride; + } + } + + if (!dst_priv->pinned && replaces) { + stride = src_pixmap->devKind; + bits = src_pixmap->devPrivate.ptr; + bits += (src_dy + box->y1) * stride + (src_dx + box->x1) * bpp / 8; + + if (!sna_replace(sna, dst_pixmap, + &dst_priv->gpu_bo, + bits, stride)) + goto fallback; + } else { + assert(!DAMAGE_IS_ALL(dst_priv->cpu_damage)); + if (!sna_write_boxes(sna, dst_pixmap, + dst_priv->gpu_bo, 0, 0, + src_pixmap->devPrivate.ptr, + src_pixmap->devKind, + src_dx, src_dy, + box, n)) + goto fallback; + } + + dst_priv->cpu = false; + if (damage) { + if (replaces) { + sna_damage_destroy(&dst_priv->cpu_damage); + sna_damage_all(&dst_priv->gpu_damage, + dst_pixmap->drawable.width, + dst_pixmap->drawable.height); + list_del(&dst_priv->list); + dst_priv->undamaged = false; + } else + sna_damage_add(&dst_priv->gpu_damage, + region); + assert_pixmap_damage(dst_pixmap); + } + } + + return; + } + +fallback: + if (alu == GXcopy && src_priv && src_priv->clear) { + DBG(("%s: copying clear [%08x]\n", + __FUNCTION__, src_priv->clear_color)); + + if (dst_priv) { + if (!sna_drawable_move_region_to_cpu(&dst_pixmap->drawable, + region, + MOVE_WRITE | MOVE_INPLACE_HINT)) + return; + } + + do { + pixman_fill(dst_pixmap->devPrivate.ptr, + dst_pixmap->devKind/sizeof(uint32_t), + dst_pixmap->drawable.bitsPerPixel, + box->x1, box->y1, + box->x2 - box->x1, + box->y2 - box->y1, + src_priv->clear_color); + box++; + } while (--n); + } else { + FbBits *dst_bits, *src_bits; + int dst_stride, src_stride; + + DBG(("%s: fallback -- src=(%d, %d), dst=(%d, %d)\n", + __FUNCTION__, src_dx, src_dy, dst_dx, dst_dy)); + if (src_priv) { + unsigned mode; + + RegionTranslate(region, src_dx, src_dy); + + assert_pixmap_contains_box(src_pixmap, + RegionExtents(region)); + + mode = MOVE_READ; + if (src_priv->cpu_bo == NULL) + mode |= MOVE_INPLACE_HINT; + + if (!sna_drawable_move_region_to_cpu(&src_pixmap->drawable, + region, mode)) + return; + + RegionTranslate(region, -src_dx, -src_dy); + } + + if (dst_priv) { + unsigned mode; + + if (alu_overwrites(alu)) + mode = MOVE_WRITE | MOVE_INPLACE_HINT; + else + mode = MOVE_WRITE | MOVE_READ; + if (!sna_drawable_move_region_to_cpu(&dst_pixmap->drawable, + region, mode)) + return; + } + + dst_stride = dst_pixmap->devKind; + src_stride = src_pixmap->devKind; + + if (alu == GXcopy && bpp >= 8) { + dst_bits = (FbBits *)dst_pixmap->devPrivate.ptr; + src_bits = (FbBits *) + ((char *)src_pixmap->devPrivate.ptr + + src_dy * src_stride + src_dx * bpp / 8); + + do { + DBG(("%s: memcpy_blt(box=(%d, %d), (%d, %d), src=(%d, %d), pitches=(%d, %d))\n", + __FUNCTION__, + box->x1, box->y1, + box->x2 - box->x1, + box->y2 - box->y1, + src_dx, src_dy, + src_stride, dst_stride)); + + assert(box->x1 >= 0); + assert(box->y1 >= 0); + assert(box->x2 <= dst_pixmap->drawable.width); + assert(box->y2 <= dst_pixmap->drawable.height); + + assert(box->x1 + src_dx >= 0); + assert(box->y1 + src_dy >= 0); + assert(box->x2 + src_dx <= src_pixmap->drawable.width); + assert(box->y2 + src_dy <= src_pixmap->drawable.height); + + memcpy_blt(src_bits, dst_bits, bpp, + src_stride, dst_stride, + box->x1, box->y1, + box->x1, box->y1, + box->x2 - box->x1, + box->y2 - box->y1); + box++; + } while (--n); + } else { + DBG(("%s: fallback -- miCopyRegion\n", __FUNCTION__)); + + RegionTranslate(region, -dst_dx, -dst_dy); + + if (!sna_gc_move_to_cpu(gc, dst, region)) + return; + + miCopyRegion(src, dst, gc, + region, dx, dy, + fbCopyNtoN, 0, NULL); + + sna_gc_move_to_gpu(gc); + } + } +} + +typedef void (*sna_copy_func)(DrawablePtr src, DrawablePtr dst, GCPtr gc, + RegionPtr region, int dx, int dy, + Pixel bitPlane, void *closure); + +inline static bool +box_intersect(BoxPtr a, const BoxRec *b) +{ + if (a->x1 < b->x1) + a->x1 = b->x1; + if (a->x2 > b->x2) + a->x2 = b->x2; + if (a->y1 < b->y1) + a->y1 = b->y1; + if (a->y2 > b->y2) + a->y2 = b->y2; + + return a->x1 < a->x2 && a->y1 < a->y2; +} + +static RegionPtr +sna_do_copy(DrawablePtr src, DrawablePtr dst, GCPtr gc, + int sx, int sy, + int width, int height, + int dx, int dy, + sna_copy_func copy, Pixel bitPlane, void *closure) +{ + RegionPtr clip, free_clip = NULL; + RegionRec region; + bool expose; + + DBG(("%s: src=(%d, %d), dst=(%d, %d), size=(%dx%d)\n", + __FUNCTION__, sx, sy, dx, dy, width, height)); + + /* Short cut for unmapped windows */ + if (dst->type == DRAWABLE_WINDOW && !((WindowPtr)dst)->realized) { + DBG(("%s: unmapped\n", __FUNCTION__)); + return NULL; + } + + if (src->pScreen->SourceValidate) + src->pScreen->SourceValidate(src, sx, sy, + width, height, + gc->subWindowMode); + + sx += src->x; + sy += src->y; + + dx += dst->x; + dy += dst->y; + + DBG(("%s: after drawable: src=(%d, %d), dst=(%d, %d), size=(%dx%d)\n", + __FUNCTION__, sx, sy, dx, dy, width, height)); + + region.extents.x1 = dx; + region.extents.y1 = dy; + region.extents.x2 = bound(dx, width); + region.extents.y2 = bound(dy, height); + region.data = NULL; + + DBG(("%s: dst extents (%d, %d), (%d, %d)\n", __FUNCTION__, + region.extents.x1, region.extents.y1, + region.extents.x2, region.extents.y2)); + + if (!box_intersect(®ion.extents, &gc->pCompositeClip->extents)) { + DBG(("%s: dst clipped out\n", __FUNCTION__)); + return NULL; + } + + region.extents.x1 = clamp(region.extents.x1, sx - dx); + region.extents.x2 = clamp(region.extents.x2, sx - dx); + region.extents.y1 = clamp(region.extents.y1, sy - dy); + region.extents.y2 = clamp(region.extents.y2, sy - dy); + + /* Compute source clip region */ + clip = NULL; + if (src == dst && gc->clientClipType == CT_NONE) { + DBG(("%s: using gc clip for src\n", __FUNCTION__)); + clip = gc->pCompositeClip; + } else if (src->type == DRAWABLE_PIXMAP) { + DBG(("%s: pixmap -- no source clipping\n", __FUNCTION__)); + } else if (gc->subWindowMode == IncludeInferiors) { + /* + * XFree86 DDX empties the border clip when the + * VT is inactive, make sure the region isn't empty + */ + if (((WindowPtr)src)->parent || + !RegionNotEmpty(&((WindowPtr)src)->borderClip)) { + DBG(("%s: include inferiors\n", __FUNCTION__)); + free_clip = clip = NotClippedByChildren((WindowPtr)src); + } + } else { + DBG(("%s: window clip\n", __FUNCTION__)); + clip = &((WindowPtr)src)->clipList; + } + if (clip == NULL) { + DBG(("%s: fast source clip against extents\n", __FUNCTION__)); + expose = true; + if (region.extents.x1 < src->x) { + region.extents.x1 = src->x; + expose = false; + } + if (region.extents.y1 < src->y) { + region.extents.y1 = src->y; + expose = false; + } + if (region.extents.x2 > src->x + (int) src->width) { + region.extents.x2 = src->x + (int) src->width; + expose = false; + } + if (region.extents.y2 > src->y + (int) src->height) { + region.extents.y2 = src->y + (int) src->height; + expose = false; + } + if (box_empty(®ion.extents)) + return NULL; + } else { + expose = false; + RegionIntersect(®ion, ®ion, clip); + if (free_clip) + RegionDestroy(free_clip); + } + DBG(("%s: src extents (%d, %d), (%d, %d) x %d\n", __FUNCTION__, + region.extents.x1, region.extents.y1, + region.extents.x2, region.extents.y2, + RegionNumRects(®ion))); + RegionTranslate(®ion, dx-sx, dy-sy); + if (gc->pCompositeClip->data) + RegionIntersect(®ion, ®ion, gc->pCompositeClip); + DBG(("%s: copy region (%d, %d), (%d, %d) x %d\n", __FUNCTION__, + region.extents.x1, region.extents.y1, + region.extents.x2, region.extents.y2, + RegionNumRects(®ion))); + + if (RegionNotEmpty(®ion)) + copy(src, dst, gc, ®ion, sx-dx, sy-dy, bitPlane, closure); + RegionUninit(®ion); + + /* Pixmap sources generate a NoExposed (we return NULL to do this) */ + clip = NULL; + if (!expose && gc->fExpose) + clip = miHandleExposures(src, dst, gc, + sx - src->x, sy - src->y, + width, height, + dx - dst->x, dy - dst->y, + (unsigned long) bitPlane); + return clip; +} + +static void +sna_fallback_copy_boxes(DrawablePtr src, DrawablePtr dst, GCPtr gc, + RegionPtr region, int dx, int dy, + Pixel bitplane, void *closure) +{ + DBG(("%s (boxes=%dx[(%d, %d), (%d, %d)...], src=+(%d, %d), alu=%d\n", + __FUNCTION__, RegionNumRects(region), + region->extents.x1, region->extents.y1, + region->extents.x2, region->extents.y2, + dx, dy, gc->alu)); + + if (!sna_gc_move_to_cpu(gc, dst, region)) + return; + + if (src == dst || + get_drawable_pixmap(src) == get_drawable_pixmap(dst)) { + if (!sna_drawable_move_to_cpu(dst, MOVE_WRITE | MOVE_READ)) + goto out_gc; + } else { + RegionTranslate(region, dx, dy); + if (!sna_drawable_move_region_to_cpu(src, region, MOVE_READ)) + goto out_gc; + RegionTranslate(region, -dx, -dy); + + if (!sna_drawable_move_region_to_cpu(dst, region, + drawable_gc_flags(dst, gc, false))) + goto out_gc; + } + + miCopyRegion(src, dst, gc, + region, dx, dy, + fbCopyNtoN, 0, NULL); + FALLBACK_FLUSH(dst); +out_gc: + sna_gc_move_to_gpu(gc); +} + +static RegionPtr +sna_copy_area(DrawablePtr src, DrawablePtr dst, GCPtr gc, + int src_x, int src_y, + int width, int height, + int dst_x, int dst_y) +{ + struct sna *sna = to_sna_from_drawable(dst); + sna_copy_func copy; + + if (gc->planemask == 0) + return NULL; + + DBG(("%s: src=(%d, %d)x(%d, %d)+(%d, %d) -> dst=(%d, %d)+(%d, %d)\n", + __FUNCTION__, + src_x, src_y, width, height, src->x, src->y, + dst_x, dst_y, dst->x, dst->y)); + + if (FORCE_FALLBACK || !ACCEL_COPY_AREA || wedged(sna) || + !PM_IS_SOLID(dst, gc->planemask)) + copy = sna_fallback_copy_boxes; + else if (src == dst) + copy = sna_self_copy_boxes; + else + copy = sna_copy_boxes; + + return sna_do_copy(src, dst, gc, + src_x, src_y, + width, height, + dst_x, dst_y, + copy, 0, NULL); +} + +static const BoxRec * +find_clip_box_for_y(const BoxRec *begin, const BoxRec *end, int16_t y) +{ + const BoxRec *mid; + + if (end == begin) + return end; + + if (end - begin == 1) { + if (begin->y2 > y) + return begin; + else + return end; + } + + mid = begin + (end - begin) / 2; + if (mid->y2 > y) + /* If no box is found in [begin, mid], the function + * will return @mid, which is then known to be the + * correct answer. + */ + return find_clip_box_for_y(begin, mid, y); + else + return find_clip_box_for_y(mid, end, y); +} + +struct sna_fill_spans { + struct sna *sna; + PixmapPtr pixmap; + RegionRec region; + unsigned flags; + struct kgem_bo *bo; + struct sna_damage **damage; + int16_t dx, dy; + void *op; +}; + +static void +sna_poly_point__cpu(DrawablePtr drawable, GCPtr gc, + int mode, int n, DDXPointPtr pt) +{ + fbPolyPoint(drawable, gc, mode, n, pt, -1); +} + +static void +sna_poly_point__fill(DrawablePtr drawable, GCPtr gc, + int mode, int n, DDXPointPtr pt) +{ + struct sna_fill_spans *data = sna_gc(gc)->priv; + struct sna_fill_op *op = data->op; + BoxRec box[512]; + DDXPointRec last; + + DBG(("%s: count=%d\n", __FUNCTION__, n)); + + last.x = drawable->x + data->dx; + last.y = drawable->y + data->dy; + while (n) { + BoxRec *b = box; + unsigned nbox = n; + if (nbox > ARRAY_SIZE(box)) + nbox = ARRAY_SIZE(box); + n -= nbox; + do { + *(DDXPointRec *)b = *pt++; + + b->x1 += last.x; + b->y1 += last.y; + if (mode == CoordModePrevious) + last = *(DDXPointRec *)b; + + b->x2 = b->x1 + 1; + b->y2 = b->y1 + 1; + b++; + } while (--nbox); + op->boxes(data->sna, op, box, b - box); + } +} + +static void +sna_poly_point__gpu(DrawablePtr drawable, GCPtr gc, + int mode, int n, DDXPointPtr pt) +{ + struct sna_fill_spans *data = sna_gc(gc)->priv; + struct sna_fill_op fill; + BoxRec box[512]; + DDXPointRec last; + + if (!sna_fill_init_blt(&fill, + data->sna, data->pixmap, + data->bo, gc->alu, gc->fgPixel)) + return; + + DBG(("%s: count=%d\n", __FUNCTION__, n)); + + last.x = drawable->x; + last.y = drawable->y; + while (n) { + BoxRec *b = box; + unsigned nbox = n; + if (nbox > ARRAY_SIZE(box)) + nbox = ARRAY_SIZE(box); + n -= nbox; + do { + *(DDXPointRec *)b = *pt++; + + b->x1 += last.x; + b->y1 += last.y; + if (mode == CoordModePrevious) + last = *(DDXPointRec *)b; + + if (RegionContainsPoint(&data->region, + b->x1, b->y1, NULL)) { + b->x1 += data->dx; + b->y1 += data->dy; + b->x2 = b->x1 + 1; + b->y2 = b->y1 + 1; + b++; + } + } while (--nbox); + fill.boxes(data->sna, &fill, box, b - box); + } + fill.done(data->sna, &fill); +} + +static void +sna_poly_point__fill_clip_extents(DrawablePtr drawable, GCPtr gc, + int mode, int n, DDXPointPtr pt) +{ + struct sna_fill_spans *data = sna_gc(gc)->priv; + struct sna_fill_op *op = data->op; + const BoxRec *extents = &data->region.extents; + BoxRec box[512], *b = box; + const BoxRec *const last_box = b + ARRAY_SIZE(box); + DDXPointRec last; + + DBG(("%s: count=%d\n", __FUNCTION__, n)); + + last.x = drawable->x + data->dx; + last.y = drawable->y + data->dy; + while (n--) { + *(DDXPointRec *)b = *pt++; + + b->x1 += last.x; + b->y1 += last.y; + if (mode == CoordModePrevious) + last = *(DDXPointRec *)b; + + if (b->x1 >= extents->x1 && b->x1 < extents->x2 && + b->y1 >= extents->y1 && b->y1 < extents->y2) { + b->x2 = b->x1 + 1; + b->y2 = b->y1 + 1; + if (++b == last_box) { + op->boxes(data->sna, op, box, last_box - box); + b = box; + } + } + } + if (b != box) + op->boxes(data->sna, op, box, b - box); +} + +static void +sna_poly_point__fill_clip_boxes(DrawablePtr drawable, GCPtr gc, + int mode, int n, DDXPointPtr pt) +{ + struct sna_fill_spans *data = sna_gc(gc)->priv; + struct sna_fill_op *op = data->op; + RegionRec *clip = &data->region; + BoxRec box[512], *b = box; + const BoxRec *const last_box = b + ARRAY_SIZE(box); + DDXPointRec last; + + DBG(("%s: count=%d\n", __FUNCTION__, n)); + + last.x = drawable->x + data->dx; + last.y = drawable->y + data->dy; + while (n--) { + *(DDXPointRec *)b = *pt++; + + b->x1 += last.x; + b->y1 += last.y; + if (mode == CoordModePrevious) + last = *(DDXPointRec *)b; + + if (RegionContainsPoint(clip, b->x1, b->y1, NULL)) { + b->x2 = b->x1 + 1; + b->y2 = b->y1 + 1; + if (++b == last_box) { + op->boxes(data->sna, op, box, last_box - box); + b = box; + } + } + } + if (b != box) + op->boxes(data->sna, op, box, b - box); +} + +static void +sna_fill_spans__fill(DrawablePtr drawable, + GCPtr gc, int n, + DDXPointPtr pt, int *width, int sorted) +{ + struct sna_fill_spans *data = sna_gc(gc)->priv; + struct sna_fill_op *op = data->op; + BoxRec box[512]; + + DBG(("%s: alu=%d, fg=%08lx, count=%d\n", + __FUNCTION__, gc->alu, gc->fgPixel, n)); + + while (n) { + BoxRec *b = box; + int nbox = n; + if (nbox > ARRAY_SIZE(box)) + nbox = ARRAY_SIZE(box); + n -= nbox; + do { + *(DDXPointRec *)b = *pt++; + b->x2 = b->x1 + (int)*width++; + b->y2 = b->y1 + 1; + DBG(("%s: (%d, %d), (%d, %d)\n", + __FUNCTION__, b->x1, b->y1, b->x2, b->y2)); + assert(b->x1 >= drawable->x); + assert(b->x2 <= drawable->x + drawable->width); + assert(b->y1 >= drawable->y); + assert(b->y2 <= drawable->y + drawable->height); + if (b->x2 > b->x1) { + if (b != box && + b->y1 == b[-1].y2 && + b->x1 == b[-1].x1 && + b->x2 == b[-1].x2) + b[-1].y2 = b->y2; + else + b++; + } + } while (--nbox); + if (b != box) + op->boxes(data->sna, op, box, b - box); + } +} + +static void +sna_fill_spans__dash(DrawablePtr drawable, + GCPtr gc, int n, + DDXPointPtr pt, int *width, int sorted) +{ + struct sna_fill_spans *data = sna_gc(gc)->priv; + struct sna_fill_op *op = data->op; + + if (op->base.u.blt.pixel == gc->fgPixel) + sna_fill_spans__fill(drawable, gc, n, pt, width, sorted); +} + +static void +sna_fill_spans__fill_offset(DrawablePtr drawable, + GCPtr gc, int n, + DDXPointPtr pt, int *width, int sorted) +{ + struct sna_fill_spans *data = sna_gc(gc)->priv; + struct sna_fill_op *op = data->op; + BoxRec box[512]; + + DBG(("%s: alu=%d, fg=%08lx\n", __FUNCTION__, gc->alu, gc->fgPixel)); + + while (n) { + BoxRec *b = box; + int nbox = n; + if (nbox > ARRAY_SIZE(box)) + nbox = ARRAY_SIZE(box); + n -= nbox; + do { + *(DDXPointRec *)b = *pt++; + b->x1 += data->dx; + b->y1 += data->dy; + b->x2 = b->x1 + (int)*width++; + b->y2 = b->y1 + 1; + if (b->x2 > b->x1) + b++; + } while (--nbox); + if (b != box) + op->boxes(data->sna, op, box, b - box); + } +} + +static void +sna_fill_spans__dash_offset(DrawablePtr drawable, + GCPtr gc, int n, + DDXPointPtr pt, int *width, int sorted) +{ + struct sna_fill_spans *data = sna_gc(gc)->priv; + struct sna_fill_op *op = data->op; + + if (op->base.u.blt.pixel == gc->fgPixel) + sna_fill_spans__fill_offset(drawable, gc, n, pt, width, sorted); +} + +static void +sna_fill_spans__fill_clip_extents(DrawablePtr drawable, + GCPtr gc, int n, + DDXPointPtr pt, int *width, int sorted) +{ + struct sna_fill_spans *data = sna_gc(gc)->priv; + struct sna_fill_op *op = data->op; + const BoxRec *extents = &data->region.extents; + BoxRec box[512], *b = box, *const last_box = box + ARRAY_SIZE(box); + + DBG(("%s: alu=%d, fg=%08lx, count=%d, extents=(%d, %d), (%d, %d)\n", + __FUNCTION__, gc->alu, gc->fgPixel, n, + extents->x1, extents->y1, + extents->x2, extents->y2)); + + while (n--) { + DBG(("%s: [%d] pt=(%d, %d), width=%d\n", + __FUNCTION__, n, pt->x, pt->y, *width)); + *(DDXPointRec *)b = *pt++; + b->x2 = b->x1 + (int)*width++; + b->y2 = b->y1 + 1; + if (box_intersect(b, extents)) { + DBG(("%s: [%d] clipped=(%d, %d), (%d, %d)\n", + __FUNCTION__, n, b->x1, b->y1, b->x2, b->y2)); + if (data->dx|data->dy) { + b->x1 += data->dx; b->x2 += data->dx; + b->y1 += data->dy; b->y2 += data->dy; + } + if (b != box && + b->y1 == b[-1].y2 && + b->x1 == b[-1].x1 && + b->x2 == b[-1].x2) { + b[-1].y2 = b->y2; + } else if (++b == last_box) { + op->boxes(data->sna, op, box, last_box - box); + b = box; + } + } + } + if (b != box) + op->boxes(data->sna, op, box, b - box); +} + +static void +sna_fill_spans__dash_clip_extents(DrawablePtr drawable, + GCPtr gc, int n, + DDXPointPtr pt, int *width, int sorted) +{ + struct sna_fill_spans *data = sna_gc(gc)->priv; + struct sna_fill_op *op = data->op; + + if (op->base.u.blt.pixel == gc->fgPixel) + sna_fill_spans__fill_clip_extents(drawable, gc, n, pt, width, sorted); +} + +static void +sna_fill_spans__fill_clip_boxes(DrawablePtr drawable, + GCPtr gc, int n, + DDXPointPtr pt, int *width, int sorted) +{ + struct sna_fill_spans *data = sna_gc(gc)->priv; + struct sna_fill_op *op = data->op; + BoxRec box[512], *b = box, *const last_box = box + ARRAY_SIZE(box); + const BoxRec * const clip_start = RegionBoxptr(&data->region); + const BoxRec * const clip_end = clip_start + data->region.data->numRects; + + DBG(("%s: alu=%d, fg=%08lx, count=%d, extents=(%d, %d), (%d, %d)\n", + __FUNCTION__, gc->alu, gc->fgPixel, n, + data->region.extents.x1, data->region.extents.y1, + data->region.extents.x2, data->region.extents.y2)); + + while (n--) { + int16_t X1 = pt->x; + int16_t y = pt->y; + int16_t X2 = X1 + (int)*width; + const BoxRec *c; + + pt++; + width++; + + if (y < data->region.extents.y1 || data->region.extents.y2 <= y) + continue; + + if (X1 < data->region.extents.x1) + X1 = data->region.extents.x1; + + if (X2 > data->region.extents.x2) + X2 = data->region.extents.x2; + + if (X1 >= X2) + continue; + + c = find_clip_box_for_y(clip_start, clip_end, y); + while (c != clip_end) { + if (y + 1 <= c->y1 || X2 <= c->x1) + break; + + if (X1 >= c->x2) { + c++; + continue; + } + + b->x1 = c->x1; + b->x2 = c->x2; + c++; + + if (b->x1 < X1) + b->x1 = X1; + if (b->x2 > X2) + b->x2 = X2; + if (b->x2 <= b->x1) + continue; + + b->x1 += data->dx; + b->x2 += data->dx; + b->y1 = y + data->dy; + b->y2 = b->y1 + 1; + if (++b == last_box) { + op->boxes(data->sna, op, box, last_box - box); + b = box; + } + } + } + if (b != box) + op->boxes(data->sna, op, box, b - box); +} + +static void +sna_fill_spans__dash_clip_boxes(DrawablePtr drawable, + GCPtr gc, int n, + DDXPointPtr pt, int *width, int sorted) +{ + struct sna_fill_spans *data = sna_gc(gc)->priv; + struct sna_fill_op *op = data->op; + + if (op->base.u.blt.pixel == gc->fgPixel) + sna_fill_spans__fill_clip_boxes(drawable, gc, n, pt, width, sorted); +} + +static bool +sna_fill_spans_blt(DrawablePtr drawable, + struct kgem_bo *bo, struct sna_damage **damage, + GCPtr gc, uint32_t pixel, + int n, DDXPointPtr pt, int *width, int sorted, + const BoxRec *extents, unsigned clipped) +{ + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna *sna = to_sna_from_pixmap(pixmap); + int16_t dx, dy; + struct sna_fill_op fill; + BoxRec box[512], *b = box, *const last_box = box + ARRAY_SIZE(box); + static void * const jump[] = { + &&no_damage, + &&damage, + &&no_damage_clipped, + &&damage_clipped, + }; + unsigned v; + + DBG(("%s: alu=%d, fg=%08lx, damge=%p, clipped?=%d\n", + __FUNCTION__, gc->alu, gc->fgPixel, damage, clipped)); + + if (!sna_fill_init_blt(&fill, sna, pixmap, bo, gc->alu, pixel)) + return false; + + get_drawable_deltas(drawable, pixmap, &dx, &dy); + + v = (damage != NULL) | clipped; + goto *jump[v]; + +no_damage: + if (dx|dy) { + do { + int nbox = n; + if (nbox > last_box - box) + nbox = last_box - box; + n -= nbox; + do { + *(DDXPointRec *)b = *pt++; + b->x1 += dx; + b->y1 += dy; + b->x2 = b->x1 + (int)*width++; + b->y2 = b->y1 + 1; + b++; + } while (--nbox); + fill.boxes(sna, &fill, box, b - box); + b = box; + } while (n); + } else { + do { + int nbox = n; + if (nbox > last_box - box) + nbox = last_box - box; + n -= nbox; + do { + *(DDXPointRec *)b = *pt++; + b->x2 = b->x1 + (int)*width++; + b->y2 = b->y1 + 1; + b++; + } while (--nbox); + fill.boxes(sna, &fill, box, b - box); + b = box; + } while (n); + } + goto done; + +damage: + do { + *(DDXPointRec *)b = *pt++; + b->x1 += dx; + b->y1 += dy; + b->x2 = b->x1 + (int)*width++; + b->y2 = b->y1 + 1; + + if (++b == last_box) { + assert_pixmap_contains_boxes(pixmap, box, last_box-box, 0, 0); + fill.boxes(sna, &fill, box, last_box - box); + sna_damage_add_boxes(damage, box, last_box - box, 0, 0); + b = box; + } + } while (--n); + if (b != box) { + assert_pixmap_contains_boxes(pixmap, box, b-box, 0, 0); + fill.boxes(sna, &fill, box, b - box); + sna_damage_add_boxes(damage, box, b - box, 0, 0); + } + goto done; + +no_damage_clipped: + { + RegionRec clip; + + region_set(&clip, extents); + region_maybe_clip(&clip, gc->pCompositeClip); + if (!RegionNotEmpty(&clip)) + return true; + + assert(dx + clip.extents.x1 >= 0); + assert(dy + clip.extents.y1 >= 0); + assert(dx + clip.extents.x2 <= pixmap->drawable.width); + assert(dy + clip.extents.y2 <= pixmap->drawable.height); + + DBG(("%s: clip %d x [(%d, %d), (%d, %d)] x %d [(%d, %d)...]\n", + __FUNCTION__, + REGION_NUM_RECTS(&clip), + clip.extents.x1, clip.extents.y1, clip.extents.x2, clip.extents.y2, + n, pt->x, pt->y)); + + if (clip.data == NULL) { + do { + *(DDXPointRec *)b = *pt++; + b->x2 = b->x1 + (int)*width++; + b->y2 = b->y1 + 1; + + if (box_intersect(b, &clip.extents)) { + if (dx|dy) { + b->x1 += dx; b->x2 += dx; + b->y1 += dy; b->y2 += dy; + } + if (++b == last_box) { + fill.boxes(sna, &fill, box, last_box - box); + b = box; + } + } + } while (--n); + } else { + const BoxRec * const clip_start = RegionBoxptr(&clip); + const BoxRec * const clip_end = clip_start + clip.data->numRects; + do { + int16_t X1 = pt->x; + int16_t y = pt->y; + int16_t X2 = X1 + (int)*width; + const BoxRec *c; + + pt++; + width++; + + if (y < extents->y1 || extents->y2 <= y) + continue; + + if (X1 < extents->x1) + X1 = extents->x1; + + if (X2 > extents->x2) + X2 = extents->x2; + + if (X1 >= X2) + continue; + + c = find_clip_box_for_y(clip_start, + clip_end, + y); + while (c != clip_end) { + if (y + 1 <= c->y1 || X2 <= c->x1) + break; + + if (X1 >= c->x2) { + c++; + continue; + } + + b->x1 = c->x1; + b->x2 = c->x2; + c++; + + if (b->x1 < X1) + b->x1 = X1; + if (b->x2 > X2) + b->x2 = X2; + if (b->x2 <= b->x1) + continue; + + b->x1 += dx; + b->x2 += dx; + b->y1 = y + dy; + b->y2 = b->y1 + 1; + if (++b == last_box) { + fill.boxes(sna, &fill, box, last_box - box); + b = box; + } + } + } while (--n); + RegionUninit(&clip); + } + if (b != box) + fill.boxes(sna, &fill, box, b - box); + goto done; + } + +damage_clipped: + { + RegionRec clip; + + region_set(&clip, extents); + region_maybe_clip(&clip, gc->pCompositeClip); + if (!RegionNotEmpty(&clip)) + return true; + + assert(dx + clip.extents.x1 >= 0); + assert(dy + clip.extents.y1 >= 0); + assert(dx + clip.extents.x2 <= pixmap->drawable.width); + assert(dy + clip.extents.y2 <= pixmap->drawable.height); + + DBG(("%s: clip %d x [(%d, %d), (%d, %d)] x %d [(%d, %d)...]\n", + __FUNCTION__, + REGION_NUM_RECTS(&clip), + clip.extents.x1, clip.extents.y1, clip.extents.x2, clip.extents.y2, + n, pt->x, pt->y)); + + if (clip.data == NULL) { + do { + *(DDXPointRec *)b = *pt++; + b->x2 = b->x1 + (int)*width++; + b->y2 = b->y1 + 1; + + if (box_intersect(b, &clip.extents)) { + b->x1 += dx; + b->x2 += dx; + b->y1 += dy; + b->y2 += dy; + if (++b == last_box) { + assert_pixmap_contains_boxes(pixmap, box, b-box, 0, 0); + fill.boxes(sna, &fill, box, last_box - box); + sna_damage_add_boxes(damage, box, b - box, 0, 0); + b = box; + } + } + } while (--n); + } else { + const BoxRec * const clip_start = RegionBoxptr(&clip); + const BoxRec * const clip_end = clip_start + clip.data->numRects; + do { + int16_t X1 = pt->x; + int16_t y = pt->y; + int16_t X2 = X1 + (int)*width; + const BoxRec *c; + + pt++; + width++; + + if (y < extents->y1 || extents->y2 <= y) + continue; + + if (X1 < extents->x1) + X1 = extents->x1; + + if (X2 > extents->x2) + X2 = extents->x2; + + if (X1 >= X2) + continue; + + c = find_clip_box_for_y(clip_start, + clip_end, + y); + while (c != clip_end) { + if (y + 1 <= c->y1 || X2 <= c->x1) + break; + + if (X1 >= c->x2) { + c++; + continue; + } + + b->x1 = c->x1; + b->x2 = c->x2; + c++; + + if (b->x1 < X1) + b->x1 = X1; + if (b->x2 > X2) + b->x2 = X2; + if (b->x2 <= b->x1) + continue; + + b->x1 += dx; + b->x2 += dx; + b->y1 = y + dy; + b->y2 = b->y1 + 1; + if (++b == last_box) { + assert_pixmap_contains_boxes(pixmap, box, last_box-box, 0, 0); + fill.boxes(sna, &fill, box, last_box - box); + sna_damage_add_boxes(damage, box, last_box - box, 0, 0); + b = box; + } + } + } while (--n); + RegionUninit(&clip); + } + if (b != box) { + assert_pixmap_contains_boxes(pixmap, box, b-box, 0, 0); + fill.boxes(sna, &fill, box, b - box); + sna_damage_add_boxes(damage, box, b - box, 0, 0); + } + goto done; + } + +done: + fill.done(sna, &fill); + assert_pixmap_damage(pixmap); + return true; +} + +static bool +sna_poly_fill_rect_tiled_blt(DrawablePtr drawable, + struct kgem_bo *bo, + struct sna_damage **damage, + GCPtr gc, int n, xRectangle *rect, + const BoxRec *extents, unsigned clipped); + +static bool +sna_poly_fill_rect_stippled_blt(DrawablePtr drawable, + struct kgem_bo *bo, + struct sna_damage **damage, + GCPtr gc, int n, xRectangle *rect, + const BoxRec *extents, unsigned clipped); + +static inline bool +gc_is_solid(GCPtr gc, uint32_t *color) +{ + if (gc->fillStyle == FillSolid || + (gc->fillStyle == FillTiled && gc->tileIsPixel) || + (gc->fillStyle == FillOpaqueStippled && gc->bgPixel == gc->fgPixel)) { + *color = gc->fillStyle == FillTiled ? gc->tile.pixel : gc->fgPixel; + return true; + } + + return false; +} + +static void +sna_fill_spans__gpu(DrawablePtr drawable, GCPtr gc, int n, + DDXPointPtr pt, int *width, int sorted) +{ + struct sna_fill_spans *data = sna_gc(gc)->priv; + uint32_t color; + + DBG(("%s(n=%d, pt[0]=(%d, %d)+%d, sorted=%d\n", + __FUNCTION__, n, pt[0].x, pt[0].y, width[0], sorted)); + + assert(PM_IS_SOLID(drawable, gc->planemask)); + if (n == 0) + return; + + /* The mi routines do not attempt to keep the spans it generates + * within the clip, so we must run them through the clipper. + */ + + if (gc_is_solid(gc, &color)) { + sna_fill_spans_blt(drawable, + data->bo, NULL, + gc, color, n, pt, width, sorted, + &data->region.extents, 2); + } else { + /* Try converting these to a set of rectangles instead */ + xRectangle *rect; + int i; + + DBG(("%s: converting to rectagnles\n", __FUNCTION__)); + + rect = malloc (n * sizeof (xRectangle)); + if (rect == NULL) + return; + + for (i = 0; i < n; i++) { + rect[i].x = pt[i].x - drawable->x; + rect[i].width = width[i]; + rect[i].y = pt[i].y - drawable->y; + rect[i].height = 1; + } + + if (gc->fillStyle == FillTiled) { + (void)sna_poly_fill_rect_tiled_blt(drawable, + data->bo, NULL, + gc, n, rect, + &data->region.extents, 2); + } else { + (void)sna_poly_fill_rect_stippled_blt(drawable, + data->bo, NULL, + gc, n, rect, + &data->region.extents, 2); + } + free (rect); + } +} + +static unsigned +sna_spans_extents(DrawablePtr drawable, GCPtr gc, + int n, DDXPointPtr pt, int *width, + BoxPtr out) +{ + BoxRec box; + bool clipped = false; + + if (n == 0) + return 0; + + box.x1 = pt->x; + box.x2 = box.x1 + *width; + box.y2 = box.y1 = pt->y; + + while (--n) { + pt++; + width++; + if (box.x1 > pt->x) + box.x1 = pt->x; + if (box.x2 < pt->x + *width) + box.x2 = pt->x + *width; + + if (box.y1 > pt->y) + box.y1 = pt->y; + else if (box.y2 < pt->y) + box.y2 = pt->y; + } + box.y2++; + + if (gc) + clipped = clip_box(&box, gc); + if (box_empty(&box)) + return 0; + + *out = box; + return 1 | clipped << 1; +} + +static void +sna_fill_spans(DrawablePtr drawable, GCPtr gc, int n, + DDXPointPtr pt, int *width, int sorted) +{ + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna *sna = to_sna_from_pixmap(pixmap); + struct sna_damage **damage; + struct kgem_bo *bo; + RegionRec region; + unsigned flags; + uint32_t color; + + DBG(("%s(n=%d, pt[0]=(%d, %d)+%d, sorted=%d\n", + __FUNCTION__, n, pt[0].x, pt[0].y, width[0], sorted)); + + flags = sna_spans_extents(drawable, gc, n, pt, width, ®ion.extents); + if (flags == 0) + return; + + DBG(("%s: extents (%d, %d), (%d, %d)\n", __FUNCTION__, + region.extents.x1, region.extents.y1, + region.extents.x2, region.extents.y2)); + + if (FORCE_FALLBACK) + goto fallback; + + if (!ACCEL_FILL_SPANS) + goto fallback; + + if (wedged(sna)) { + DBG(("%s: fallback -- wedged\n", __FUNCTION__)); + goto fallback; + } + + DBG(("%s: fillStyle=%x [%d], mask=%lx [%d]\n", __FUNCTION__, + gc->fillStyle, gc->fillStyle == FillSolid, + gc->planemask, PM_IS_SOLID(drawable, gc->planemask))); + if (!PM_IS_SOLID(drawable, gc->planemask)) + goto fallback; + + bo = sna_drawable_use_bo(drawable, PREFER_GPU, + ®ion.extents, &damage); + if (bo) { + if (gc_is_solid(gc, &color)) { + DBG(("%s: trying solid fill [alu=%d, pixel=%08lx] blt paths\n", + __FUNCTION__, gc->alu, gc->fgPixel)); + + sna_fill_spans_blt(drawable, + bo, damage, + gc, color, n, pt, width, sorted, + ®ion.extents, flags & 2); + } else { + /* Try converting these to a set of rectangles instead */ + xRectangle *rect; + int i; + + DBG(("%s: converting to rectagnles\n", __FUNCTION__)); + + rect = malloc (n * sizeof (xRectangle)); + if (rect == NULL) + return; + + for (i = 0; i < n; i++) { + rect[i].x = pt[i].x - drawable->x; + rect[i].width = width[i]; + rect[i].y = pt[i].y - drawable->y; + rect[i].height = 1; + } + + if (gc->fillStyle == FillTiled) { + i = sna_poly_fill_rect_tiled_blt(drawable, + bo, damage, + gc, n, rect, + ®ion.extents, flags & 2); + } else { + i = sna_poly_fill_rect_stippled_blt(drawable, + bo, damage, + gc, n, rect, + ®ion.extents, flags & 2); + } + free (rect); + + if (i) + return; + } + } + +fallback: + DBG(("%s: fallback\n", __FUNCTION__)); + region.data = NULL; + region_maybe_clip(®ion, gc->pCompositeClip); + if (!RegionNotEmpty(®ion)) + return; + + if (!sna_gc_move_to_cpu(gc, drawable, ®ion)) + goto out; + if (!sna_drawable_move_region_to_cpu(drawable, ®ion, + drawable_gc_flags(drawable, gc, n > 1))) + goto out_gc; + + DBG(("%s: fbFillSpans\n", __FUNCTION__)); + fbFillSpans(drawable, gc, n, pt, width, sorted); + FALLBACK_FLUSH(drawable); +out_gc: + sna_gc_move_to_gpu(gc); +out: + RegionUninit(®ion); +} + +static void +sna_set_spans(DrawablePtr drawable, GCPtr gc, char *src, + DDXPointPtr pt, int *width, int n, int sorted) +{ + RegionRec region; + + if (sna_spans_extents(drawable, gc, n, pt, width, ®ion.extents) == 0) + return; + + DBG(("%s: extents=(%d, %d), (%d, %d)\n", __FUNCTION__, + region.extents.x1, region.extents.y1, + region.extents.x2, region.extents.y2)); + + if (FORCE_FALLBACK) + goto fallback; + + if (!ACCEL_SET_SPANS) + goto fallback; + +fallback: + region.data = NULL; + region_maybe_clip(®ion, gc->pCompositeClip); + if (!RegionNotEmpty(®ion)) + return; + + if (!sna_gc_move_to_cpu(gc, drawable, ®ion)) + goto out; + if (!sna_drawable_move_region_to_cpu(drawable, ®ion, + drawable_gc_flags(drawable, gc, n > 1))) + goto out_gc; + + DBG(("%s: fbSetSpans\n", __FUNCTION__)); + fbSetSpans(drawable, gc, src, pt, width, n, sorted); + FALLBACK_FLUSH(drawable); +out_gc: + sna_gc_move_to_gpu(gc); +out: + RegionUninit(®ion); +} + +struct sna_copy_plane { + struct sna_damage **damage; + struct kgem_bo *bo; +}; + +static void +sna_copy_bitmap_blt(DrawablePtr _bitmap, DrawablePtr drawable, GCPtr gc, + RegionRec *region, int sx, int sy, + Pixel bitplane, void *closure) +{ + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna *sna = to_sna_from_pixmap(pixmap); + struct sna_copy_plane *arg = closure; + PixmapPtr bitmap = (PixmapPtr)_bitmap; + uint32_t br00, br13; + int16_t dx, dy; + BoxPtr box; + int n; + + DBG(("%s: plane=%x (%d,%d),(%d,%d)x%d\n", + __FUNCTION__, (unsigned)bitplane, RegionNumRects(region), + region->extents.x1, region->extents.y1, + region->extents.x2, region->extents.y2)); + + box = RegionRects(region); + n = RegionNumRects(region); + assert(n); + + get_drawable_deltas(drawable, pixmap, &dx, &dy); + assert_pixmap_contains_boxes(pixmap, box, n, dx, dy); + + br00 = 3 << 20; + br13 = arg->bo->pitch; + if (sna->kgem.gen >= 40 && arg->bo->tiling) { + br00 |= BLT_DST_TILED; + br13 >>= 2; + } + br13 |= blt_depth(drawable->depth) << 24; + br13 |= copy_ROP[gc->alu] << 16; + + kgem_set_mode(&sna->kgem, KGEM_BLT); + do { + int bx1 = (box->x1 + sx) & ~7; + int bx2 = (box->x2 + sx + 7) & ~7; + int bw = (bx2 - bx1)/8; + int bh = box->y2 - box->y1; + int bstride = ALIGN(bw, 2); + int src_stride; + uint8_t *dst, *src; + uint32_t *b; + + DBG(("%s: box(%d, %d), (%d, %d), sx=(%d,%d) bx=[%d, %d]\n", + __FUNCTION__, + box->x1, box->y1, + box->x2, box->y2, + sx, sy, bx1, bx2)); + + src_stride = bstride*bh; + if (src_stride <= 128) { + src_stride = ALIGN(src_stride, 8) / 4; + if (!kgem_check_batch(&sna->kgem, 7+src_stride) || + !kgem_check_bo_fenced(&sna->kgem, arg->bo) || + !kgem_check_reloc(&sna->kgem, 1)) { + _kgem_submit(&sna->kgem); + _kgem_set_mode(&sna->kgem, KGEM_BLT); + } + + b = sna->kgem.batch + sna->kgem.nbatch; + b[0] = XY_MONO_SRC_COPY_IMM | (5 + src_stride) | br00; + b[0] |= ((box->x1 + sx) & 7) << 17; + b[1] = br13; + b[2] = (box->y1 + dy) << 16 | (box->x1 + dx); + b[3] = (box->y2 + dy) << 16 | (box->x2 + dx); + b[4] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 4, + arg->bo, + I915_GEM_DOMAIN_RENDER << 16 | + I915_GEM_DOMAIN_RENDER | + KGEM_RELOC_FENCED, + 0); + b[5] = gc->bgPixel; + b[6] = gc->fgPixel; + + sna->kgem.nbatch += 7 + src_stride; + + dst = (uint8_t *)&b[7]; + src_stride = bitmap->devKind; + src = bitmap->devPrivate.ptr; + src += (box->y1 + sy) * src_stride + bx1/8; + src_stride -= bstride; + do { + int i = bstride; + do { + *dst++ = byte_reverse(*src++); + *dst++ = byte_reverse(*src++); + i -= 2; + } while (i); + src += src_stride; + } while (--bh); + } else { + struct kgem_bo *upload; + void *ptr; + + if (!kgem_check_batch(&sna->kgem, 8) || + !kgem_check_bo_fenced(&sna->kgem, arg->bo) || + !kgem_check_reloc_and_exec(&sna->kgem, 2)) { + _kgem_submit(&sna->kgem); + _kgem_set_mode(&sna->kgem, KGEM_BLT); + } + + upload = kgem_create_buffer(&sna->kgem, + bstride*bh, + KGEM_BUFFER_WRITE_INPLACE, + &ptr); + if (!upload) + break; + + b = sna->kgem.batch + sna->kgem.nbatch; + b[0] = XY_MONO_SRC_COPY | br00; + b[0] |= ((box->x1 + sx) & 7) << 17; + b[1] = br13; + b[2] = (box->y1 + dy) << 16 | (box->x1 + dx); + b[3] = (box->y2 + dy) << 16 | (box->x2 + dx); + b[4] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 4, + arg->bo, + I915_GEM_DOMAIN_RENDER << 16 | + I915_GEM_DOMAIN_RENDER | + KGEM_RELOC_FENCED, + 0); + b[5] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 5, + upload, + I915_GEM_DOMAIN_RENDER << 16 | + KGEM_RELOC_FENCED, + 0); + b[6] = gc->bgPixel; + b[7] = gc->fgPixel; + + sna->kgem.nbatch += 8; + + dst = ptr; + src_stride = bitmap->devKind; + src = bitmap->devPrivate.ptr; + src += (box->y1 + sy) * src_stride + bx1/8; + src_stride -= bstride; + do { + int i = bstride; + do { + *dst++ = byte_reverse(*src++); + *dst++ = byte_reverse(*src++); + i -= 2; + } while (i); + src += src_stride; + } while (--bh); + + kgem_bo_destroy(&sna->kgem, upload); + } + + box++; + } while (--n); + + if (arg->damage) { + RegionTranslate(region, dx, dy); + sna_damage_add(arg->damage, region); + } + assert_pixmap_damage(pixmap); + sna->blt_state.fill_bo = 0; +} + +static void +sna_copy_plane_blt(DrawablePtr source, DrawablePtr drawable, GCPtr gc, + RegionPtr region, int sx, int sy, + Pixel bitplane, void *closure) +{ + PixmapPtr dst_pixmap = get_drawable_pixmap(drawable); + PixmapPtr src_pixmap = get_drawable_pixmap(source); + struct sna *sna = to_sna_from_pixmap(dst_pixmap); + struct sna_copy_plane *arg = closure; + int16_t dx, dy; + int bit = ffs(bitplane) - 1; + uint32_t br00, br13; + BoxPtr box = RegionRects(region); + int n = RegionNumRects(region); + + DBG(("%s: plane=%x [%d] x%d\n", __FUNCTION__, + (unsigned)bitplane, bit, n)); + + if (n == 0) + return; + + get_drawable_deltas(source, src_pixmap, &dx, &dy); + sx += dx; + sy += dy; + + get_drawable_deltas(drawable, dst_pixmap, &dx, &dy); + assert_pixmap_contains_boxes(dst_pixmap, box, n, dx, dy); + + br00 = XY_MONO_SRC_COPY | 3 << 20; + br13 = arg->bo->pitch; + if (sna->kgem.gen >= 40 && arg->bo->tiling) { + br00 |= BLT_DST_TILED; + br13 >>= 2; + } + br13 |= blt_depth(drawable->depth) << 24; + br13 |= copy_ROP[gc->alu] << 16; + + kgem_set_mode(&sna->kgem, KGEM_BLT); + do { + int bx1 = (box->x1 + sx) & ~7; + int bx2 = (box->x2 + sx + 7) & ~7; + int bw = (bx2 - bx1)/8; + int bh = box->y2 - box->y1; + int bstride = ALIGN(bw, 2); + uint32_t *b; + struct kgem_bo *upload; + void *ptr; + + DBG(("%s: box(%d, %d), (%d, %d), sx=(%d,%d) bx=[%d, %d]\n", + __FUNCTION__, + box->x1, box->y1, + box->x2, box->y2, + sx, sy, bx1, bx2)); + + if (!kgem_check_batch(&sna->kgem, 8) || + !kgem_check_bo_fenced(&sna->kgem, arg->bo) || + !kgem_check_reloc_and_exec(&sna->kgem, 2)) { + _kgem_submit(&sna->kgem); + _kgem_set_mode(&sna->kgem, KGEM_BLT); + } + + upload = kgem_create_buffer(&sna->kgem, + bstride*bh, + KGEM_BUFFER_WRITE_INPLACE, + &ptr); + if (!upload) + break; + + switch (source->bitsPerPixel) { + case 32: + { + uint32_t *src = src_pixmap->devPrivate.ptr; + int src_stride = src_pixmap->devKind/sizeof(uint32_t); + uint8_t *dst = ptr; + + src += (box->y1 + sy) * src_stride; + src += bx1; + + src_stride -= bw * 8; + bstride -= bw; + + do { + int i = bw; + do { + uint8_t v = 0; + + v |= ((*src++ >> bit) & 1) << 7; + v |= ((*src++ >> bit) & 1) << 6; + v |= ((*src++ >> bit) & 1) << 5; + v |= ((*src++ >> bit) & 1) << 4; + v |= ((*src++ >> bit) & 1) << 3; + v |= ((*src++ >> bit) & 1) << 2; + v |= ((*src++ >> bit) & 1) << 1; + v |= ((*src++ >> bit) & 1) << 0; + + *dst++ = v; + } while (--i); + dst += bstride; + src += src_stride; + } while (--bh); + break; + } + case 16: + { + uint16_t *src = src_pixmap->devPrivate.ptr; + int src_stride = src_pixmap->devKind/sizeof(uint16_t); + uint8_t *dst = ptr; + + src += (box->y1 + sy) * src_stride; + src += bx1; + + src_stride -= bw * 8; + bstride -= bw; + + do { + int i = bw; + do { + uint8_t v = 0; + + v |= ((*src++ >> bit) & 1) << 7; + v |= ((*src++ >> bit) & 1) << 6; + v |= ((*src++ >> bit) & 1) << 5; + v |= ((*src++ >> bit) & 1) << 4; + v |= ((*src++ >> bit) & 1) << 3; + v |= ((*src++ >> bit) & 1) << 2; + v |= ((*src++ >> bit) & 1) << 1; + v |= ((*src++ >> bit) & 1) << 0; + + *dst++ = v; + } while (--i); + dst += bstride; + src += src_stride; + } while (--bh); + break; + } + default: + assert(0); + case 8: + { + uint8_t *src = src_pixmap->devPrivate.ptr; + int src_stride = src_pixmap->devKind/sizeof(uint8_t); + uint8_t *dst = ptr; + + src += (box->y1 + sy) * src_stride; + src += bx1; + + src_stride -= bw * 8; + bstride -= bw; + + do { + int i = bw; + do { + uint8_t v = 0; + + v |= ((*src++ >> bit) & 1) << 7; + v |= ((*src++ >> bit) & 1) << 6; + v |= ((*src++ >> bit) & 1) << 5; + v |= ((*src++ >> bit) & 1) << 4; + v |= ((*src++ >> bit) & 1) << 3; + v |= ((*src++ >> bit) & 1) << 2; + v |= ((*src++ >> bit) & 1) << 1; + v |= ((*src++ >> bit) & 1) << 0; + + *dst++ = v; + } while (--i); + dst += bstride; + src += src_stride; + } while (--bh); + break; + } + } + + b = sna->kgem.batch + sna->kgem.nbatch; + b[0] = br00 | ((box->x1 + sx) & 7) << 17; + b[1] = br13; + b[2] = (box->y1 + dy) << 16 | (box->x1 + dx); + b[3] = (box->y2 + dy) << 16 | (box->x2 + dx); + b[4] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 4, + arg->bo, + I915_GEM_DOMAIN_RENDER << 16 | + I915_GEM_DOMAIN_RENDER | + KGEM_RELOC_FENCED, + 0); + b[5] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 5, + upload, + I915_GEM_DOMAIN_RENDER << 16 | + KGEM_RELOC_FENCED, + 0); + b[6] = gc->bgPixel; + b[7] = gc->fgPixel; + + sna->kgem.nbatch += 8; + kgem_bo_destroy(&sna->kgem, upload); + + box++; + } while (--n); + + if (arg->damage) { + RegionTranslate(region, dx, dy); + sna_damage_add(arg->damage, region); + } + assert_pixmap_damage(dst_pixmap); + sna->blt_state.fill_bo = 0; +} + +static RegionPtr +sna_copy_plane(DrawablePtr src, DrawablePtr dst, GCPtr gc, + int src_x, int src_y, + int w, int h, + int dst_x, int dst_y, + unsigned long bit) +{ + PixmapPtr pixmap = get_drawable_pixmap(dst); + struct sna *sna = to_sna_from_pixmap(pixmap); + RegionRec region, *ret = NULL; + struct sna_copy_plane arg; + + DBG(("%s: src=(%d, %d), dst=(%d, %d), size=%dx%d\n", __FUNCTION__, + src_x, src_y, dst_x, dst_y, w, h)); + + if (gc->planemask == 0) + goto empty; + + if (src->bitsPerPixel == 1 && (bit&1) == 0) + goto empty; + + region.extents.x1 = dst_x + dst->x; + region.extents.y1 = dst_y + dst->y; + region.extents.x2 = region.extents.x1 + w; + region.extents.y2 = region.extents.y1 + h; + region.data = NULL; + RegionIntersect(®ion, ®ion, gc->pCompositeClip); + + DBG(("%s: dst extents (%d, %d), (%d, %d)\n", + __FUNCTION__, + region.extents.x1, region.extents.y1, + region.extents.x2, region.extents.y2)); + + { + RegionRec clip; + + clip.extents.x1 = src->x - (src->x + src_x) + (dst->x + dst_x); + clip.extents.y1 = src->y - (src->y + src_y) + (dst->y + dst_y); + clip.extents.x2 = clip.extents.x1 + src->width; + clip.extents.y2 = clip.extents.y1 + src->height; + clip.data = NULL; + + DBG(("%s: src extents (%d, %d), (%d, %d)\n", + __FUNCTION__, + clip.extents.x1, clip.extents.y1, + clip.extents.x2, clip.extents.y2)); + + RegionIntersect(®ion, ®ion, &clip); + } + DBG(("%s: dst^src extents (%d, %d), (%d, %d)\n", + __FUNCTION__, + region.extents.x1, region.extents.y1, + region.extents.x2, region.extents.y2)); + if (!RegionNotEmpty(®ion)) + goto empty; + + RegionTranslate(®ion, + src_x - dst_x - dst->x + src->x, + src_y - dst_y - dst->y + src->y); + + if (!sna_drawable_move_region_to_cpu(src, ®ion, MOVE_READ)) + goto out; + + RegionTranslate(®ion, + -(src_x - dst_x - dst->x + src->x), + -(src_y - dst_y - dst->y + src->y)); + + if (FORCE_FALLBACK) + goto fallback; + + if (!ACCEL_COPY_PLANE) + goto fallback; + + if (wedged(sna)) + goto fallback; + + if (!PM_IS_SOLID(dst, gc->planemask)) + goto fallback; + + arg.bo = sna_drawable_use_bo(dst, PREFER_GPU, + ®ion.extents, &arg.damage); + if (arg.bo) { + if (arg.bo->tiling == I915_TILING_Y) { + assert(arg.bo == sna_pixmap_get_bo(pixmap)); + arg.bo = sna_pixmap_change_tiling(pixmap, I915_TILING_X); + if (arg.bo == NULL) { + DBG(("%s: fallback -- unable to change tiling\n", + __FUNCTION__)); + goto fallback; + } + } + RegionUninit(®ion); + return sna_do_copy(src, dst, gc, + src_x, src_y, + w, h, + dst_x, dst_y, + src->depth == 1 ? sna_copy_bitmap_blt : sna_copy_plane_blt, + (Pixel)bit, &arg); + } + +fallback: + DBG(("%s: fallback\n", __FUNCTION__)); + if (!sna_gc_move_to_cpu(gc, dst, ®ion)) + goto out; + if (!sna_drawable_move_region_to_cpu(dst, ®ion, + drawable_gc_flags(dst, gc, false))) + goto out_gc; + + DBG(("%s: fbCopyPlane(%d, %d, %d, %d, %d,%d) %x\n", + __FUNCTION__, src_x, src_y, w, h, dst_x, dst_y, (unsigned)bit)); + ret = miDoCopy(src, dst, gc, + src_x, src_y, w, h, dst_x, dst_y, + src->bitsPerPixel > 1 ? fbCopyNto1 : fbCopy1toN, + bit, 0); + FALLBACK_FLUSH(dst); +out_gc: + sna_gc_move_to_gpu(gc); +out: + RegionUninit(®ion); + return ret; +empty: + return miHandleExposures(src, dst, gc, + src_x, src_y, + w, h, + dst_x, dst_y, bit); +} + +static bool +sna_poly_point_blt(DrawablePtr drawable, + struct kgem_bo *bo, + struct sna_damage **damage, + GCPtr gc, int mode, int n, DDXPointPtr pt, + bool clipped) +{ + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna *sna = to_sna_from_pixmap(pixmap); + BoxRec box[512], *b = box, * const last_box = box + ARRAY_SIZE(box); + struct sna_fill_op fill; + DDXPointRec last; + int16_t dx, dy; + + DBG(("%s: alu=%d, pixel=%08lx, clipped?=%d\n", + __FUNCTION__, gc->alu, gc->fgPixel, clipped)); + + if (!sna_fill_init_blt(&fill, sna, pixmap, bo, gc->alu, gc->fgPixel)) + return false; + + get_drawable_deltas(drawable, pixmap, &dx, &dy); + + last.x = drawable->x; + last.y = drawable->y; + + if (!clipped) { + last.x += dx; + last.y += dy; + + assert_pixmap_contains_points(pixmap, pt, n, last.x, last.y); + sna_damage_add_points(damage, pt, n, last.x, last.y); + do { + unsigned nbox = n; + if (nbox > ARRAY_SIZE(box)) + nbox = ARRAY_SIZE(box); + n -= nbox; + do { + *(DDXPointRec *)b = *pt++; + + b->x1 += last.x; + b->y1 += last.y; + if (mode == CoordModePrevious) + last = *(DDXPointRec *)b; + + b->x2 = b->x1 + 1; + b->y2 = b->y1 + 1; + b++; + } while (--nbox); + fill.boxes(sna, &fill, box, b - box); + b = box; + } while (n); + } else { + RegionPtr clip = gc->pCompositeClip; + + while (n--) { + int x, y; + + x = pt->x; + y = pt->y; + pt++; + if (mode == CoordModePrevious) { + x += last.x; + y += last.y; + last.x = x; + last.y = y; + } else { + x += drawable->x; + y += drawable->y; + } + + if (RegionContainsPoint(clip, x, y, NULL)) { + b->x1 = x + dx; + b->y1 = y + dy; + b->x2 = b->x1 + 1; + b->y2 = b->y1 + 1; + if (++b == last_box){ + assert_pixmap_contains_boxes(pixmap, box, last_box-box, 0, 0); + fill.boxes(sna, &fill, box, last_box - box); + if (damage) + sna_damage_add_boxes(damage, box, last_box-box, 0, 0); + b = box; + } + } + } + if (b != box){ + assert_pixmap_contains_boxes(pixmap, box, b-box, 0, 0); + fill.boxes(sna, &fill, box, b - box); + if (damage) + sna_damage_add_boxes(damage, box, b-box, 0, 0); + } + } + fill.done(sna, &fill); + assert_pixmap_damage(pixmap); + return true; +} + +static unsigned +sna_poly_point_extents(DrawablePtr drawable, GCPtr gc, + int mode, int n, DDXPointPtr pt, BoxPtr out) +{ + BoxRec box; + bool clipped; + + if (n == 0) + return 0; + + box.x2 = box.x1 = pt->x; + box.y2 = box.y1 = pt->y; + if (mode == CoordModePrevious) { + DDXPointRec last = *pt++; + while (--n) { + last.x += pt->x; + last.y += pt->y; + pt++; + box_add_pt(&box, last.x, last.y); + } + } else { + while (--n) { + ++pt; + box_add_pt(&box, pt->x, pt->y); + } + } + box.x2++; + box.y2++; + + clipped = trim_and_translate_box(&box, drawable, gc); + if (box_empty(&box)) + return 0; + + *out = box; + return 1 | clipped << 1; +} + +static void +sna_poly_point(DrawablePtr drawable, GCPtr gc, + int mode, int n, DDXPointPtr pt) +{ + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna *sna = to_sna_from_pixmap(pixmap); + RegionRec region; + unsigned flags; + + DBG(("%s(mode=%d, n=%d, pt[0]=(%d, %d)\n", + __FUNCTION__, mode, n, pt[0].x, pt[0].y)); + + flags = sna_poly_point_extents(drawable, gc, mode, n, pt, ®ion.extents); + if (flags == 0) + return; + + DBG(("%s: extents (%d, %d), (%d, %d), flags=%x\n", __FUNCTION__, + region.extents.x1, region.extents.y1, + region.extents.x2, region.extents.y2, + flags)); + + if (FORCE_FALLBACK) + goto fallback; + + if (!ACCEL_POLY_POINT) + goto fallback; + + if (wedged(sna)) { + DBG(("%s: fallback -- wedged\n", __FUNCTION__)); + goto fallback; + } + + if (PM_IS_SOLID(drawable, gc->planemask)) { + struct sna_damage **damage; + struct kgem_bo *bo; + + DBG(("%s: trying solid fill [%08lx] blt paths\n", + __FUNCTION__, gc->fgPixel)); + + if ((bo = sna_drawable_use_bo(drawable, 0, + ®ion.extents, &damage)) && + sna_poly_point_blt(drawable, bo, damage, + gc, mode, n, pt, flags & 2)) + return; + } + +fallback: + DBG(("%s: fallback\n", __FUNCTION__)); + region.data = NULL; + region_maybe_clip(®ion, gc->pCompositeClip); + if (!RegionNotEmpty(®ion)) + return; + + if (!sna_gc_move_to_cpu(gc, drawable, ®ion)) + goto out; + if (!sna_drawable_move_region_to_cpu(drawable, ®ion, + MOVE_READ | MOVE_WRITE)) + goto out_gc; + + DBG(("%s: fbPolyPoint\n", __FUNCTION__)); + fbPolyPoint(drawable, gc, mode, n, pt, flags); + FALLBACK_FLUSH(drawable); +out_gc: + sna_gc_move_to_gpu(gc); +out: + RegionUninit(®ion); +} + +static bool +sna_poly_zero_line_blt(DrawablePtr drawable, + struct kgem_bo *bo, + struct sna_damage **damage, + GCPtr gc, int mode, const int _n, const DDXPointRec * const _pt, + const BoxRec *extents, unsigned clipped) +{ + static void * const _jump[] = { + &&no_damage, + &&damage, + + &&no_damage_offset, + &&damage_offset, + }; + + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna *sna = to_sna_from_pixmap(pixmap); + int x2, y2, xstart, ystart, oc2; + unsigned int bias = miGetZeroLineBias(drawable->pScreen); + bool degenerate = true; + struct sna_fill_op fill; + RegionRec clip; + BoxRec box[512], *b, * const last_box = box + ARRAY_SIZE(box); + const BoxRec *last_extents; + int16_t dx, dy; + void *jump, *ret; + + DBG(("%s: alu=%d, pixel=%lx, n=%d, clipped=%d, damage=%p\n", + __FUNCTION__, gc->alu, gc->fgPixel, _n, clipped, damage)); + if (!sna_fill_init_blt(&fill, sna, pixmap, bo, gc->alu, gc->fgPixel)) + return false; + + get_drawable_deltas(drawable, pixmap, &dx, &dy); + + region_set(&clip, extents); + if (clipped) { + region_maybe_clip(&clip, gc->pCompositeClip); + if (!RegionNotEmpty(&clip)) + return true; + } + + jump = _jump[(damage != NULL) | !!(dx|dy) << 1]; + DBG(("%s: [clipped=%x] extents=(%d, %d), (%d, %d), delta=(%d, %d), damage=%p\n", + __FUNCTION__, clipped, + clip.extents.x1, clip.extents.y1, + clip.extents.x2, clip.extents.y2, + dx, dy, damage)); + + extents = REGION_RECTS(&clip); + last_extents = extents + REGION_NUM_RECTS(&clip); + + b = box; + do { + int n = _n; + const DDXPointRec *pt = _pt; + + xstart = pt->x + drawable->x; + ystart = pt->y + drawable->y; + + x2 = xstart; + y2 = ystart; + oc2 = 0; + OUTCODES(oc2, x2, y2, extents); + + while (--n) { + int16_t sdx, sdy; + int adx, ady, length; + int e, e1, e2, e3; + int x1 = x2, x; + int y1 = y2, y; + int oc1 = oc2; + int octant; + + ++pt; + + x2 = pt->x; + y2 = pt->y; + if (mode == CoordModePrevious) { + x2 += x1; + y2 += y1; + } else { + x2 += drawable->x; + y2 += drawable->y; + } + DBG(("%s: segment (%d, %d) to (%d, %d)\n", + __FUNCTION__, x1, y1, x2, y2)); + if (x2 == x1 && y2 == y1) + continue; + + degenerate = false; + + oc2 = 0; + OUTCODES(oc2, x2, y2, extents); + if (oc1 & oc2) + continue; + + CalcLineDeltas(x1, y1, x2, y2, + adx, ady, sdx, sdy, + 1, 1, octant); + + DBG(("%s: adx=(%d, %d), sdx=(%d, %d), oc1=%x, oc2=%x\n", + __FUNCTION__, adx, ady, sdx, sdy, oc1, oc2)); + if (adx == 0 || ady == 0) { + if (x1 <= x2) { + b->x1 = x1; + b->x2 = x2; + } else { + b->x1 = x2; + b->x2 = x1; + } + if (y1 <= y2) { + b->y1 = y1; + b->y2 = y2; + } else { + b->y1 = y2; + b->y2 = y1; + } + b->x2++; + b->y2++; + if (oc1 | oc2) { + bool intersects; + + intersects = box_intersect(b, extents); + assert(intersects); + } + if (++b == last_box) { + ret = &&rectangle_continue; + goto *jump; +rectangle_continue: + b = box; + } + } else if (adx >= ady) { + int x2_clipped = x2, y2_clipped = y2; + bool dirty; + + /* X-major segment */ + e1 = ady << 1; + e2 = e1 - (adx << 1); + e = e1 - adx; + length = adx; + + FIXUP_ERROR(e, octant, bias); + + x = x1; + y = y1; + + if (oc1 | oc2) { + int pt1_clipped, pt2_clipped; + + if (miZeroClipLine(extents->x1, extents->y1, + extents->x2-1, extents->y2-1, + &x, &y, &x2_clipped, &y2_clipped, + adx, ady, + &pt1_clipped, &pt2_clipped, + octant, bias, oc1, oc2) == -1) + continue; + + length = abs(x2_clipped - x); + if (length == 0) + continue; + + if (pt1_clipped) { + int clipdx = abs(x - x1); + int clipdy = abs(y - y1); + e += clipdy * e2 + (clipdx - clipdy) * e1; + } + } + + e3 = e2 - e1; + e = e - e1; + + b->x1 = x; + b->y1 = y; + dirty = false; + while (length--) { + e += e1; + dirty = true; + if (e >= 0) { + e += e3; + + if (sdx < 0) { + b->x2 = b->x1 + 1; + b->x1 = x; + } else + b->x2 = x + 1; + b->y2 = b->y1 + 1; + + if (++b == last_box) { + ret = &&X_continue; + goto *jump; +X_continue: + b = box; + } + + b->x1 = x + sdx; + b->y1 = y += sdy; + dirty = false; + } + x += sdx; + } + if (dirty) { + x -= sdx; + if (sdx < 0) { + b->x2 = b->x1 + 1; + b->x1 = x; + } else + b->x2 = x + 1; + b->y2 = b->y1 + 1; + + if (++b == last_box) { + ret = &&X2_continue; + goto *jump; +X2_continue: + b = box; + } + } + } else { + int x2_clipped = x2, y2_clipped = y2; + bool dirty; + + /* Y-major segment */ + e1 = adx << 1; + e2 = e1 - (ady << 1); + e = e1 - ady; + length = ady; + + SetYMajorOctant(octant); + FIXUP_ERROR(e, octant, bias); + + x = x1; + y = y1; + + if (oc1 | oc2) { + int pt1_clipped, pt2_clipped; + + if (miZeroClipLine(extents->x1, extents->y1, + extents->x2-1, extents->y2-1, + &x, &y, &x2_clipped, &y2_clipped, + adx, ady, + &pt1_clipped, &pt2_clipped, + octant, bias, oc1, oc2) == -1) + continue; + + length = abs(y2_clipped - y); + if (length == 0) + continue; + + if (pt1_clipped) { + int clipdx = abs(x - x1); + int clipdy = abs(y - y1); + e += clipdx * e2 + (clipdy - clipdx) * e1; + } + } + + e3 = e2 - e1; + e = e - e1; + + b->x1 = x; + b->y1 = y; + dirty = false; + while (length--) { + e += e1; + dirty = true; + if (e >= 0) { + e += e3; + + if (sdy < 0) { + b->y2 = b->y1 + 1; + b->y1 = y; + } else + b->y2 = y + 1; + b->x2 = x + 1; + + if (++b == last_box) { + ret = &&Y_continue; + goto *jump; +Y_continue: + b = box; + } + + b->x1 = x += sdx; + b->y1 = y + sdy; + dirty = false; + } + y += sdy; + } + + if (dirty) { + y -= sdy; + if (sdy < 0) { + b->y2 = b->y1 + 1; + b->y1 = y; + } else + b->y2 = y + 1; + b->x2 = x + 1; + + if (++b == last_box) { + ret = &&Y2_continue; + goto *jump; +Y2_continue: + b = box; + } + } + } + } + +#if 0 + /* Only do the CapNotLast check on the last segment + * and only if the endpoint wasn't clipped. And then, if the last + * point is the same as the first point, do not draw it, unless the + * line is degenerate + */ + if (!pt2_clipped && + gc->capStyle != CapNotLast && + !(xstart == x2 && ystart == y2 && !degenerate)) + { + b->x2 = x2; + b->y2 = y2; + if (b->x2 < b->x1) { + int16_t t = b->x1; + b->x1 = b->x2; + b->x2 = t; + } + if (b->y2 < b->y1) { + int16_t t = b->y1; + b->y1 = b->y2; + b->y2 = t; + } + b->x2++; + b->y2++; + b++; + } +#endif + } while (++extents != last_extents); + + if (b != box) { + ret = &&done; + goto *jump; + } + +done: + fill.done(sna, &fill); + assert_pixmap_damage(pixmap); + RegionUninit(&clip); + return true; + +damage: + assert_pixmap_contains_boxes(pixmap, box, b-box, 0, 0); + sna_damage_add_boxes(damage, box, b-box, 0, 0); +no_damage: + fill.boxes(sna, &fill, box, b-box); + goto *ret; + +no_damage_offset: + { + BoxRec *bb = box; + do { + bb->x1 += dx; + bb->x2 += dx; + bb->y1 += dy; + bb->y2 += dy; + } while (++bb != b); + assert_pixmap_contains_boxes(pixmap, box, b-box, 0, 0); + fill.boxes(sna, &fill, box, b - box); + } + goto *ret; + +damage_offset: + { + BoxRec *bb = box; + do { + bb->x1 += dx; + bb->x2 += dx; + bb->y1 += dy; + bb->y2 += dy; + } while (++bb != b); + assert_pixmap_contains_boxes(pixmap, box, b-box, 0, 0); + fill.boxes(sna, &fill, box, b - box); + sna_damage_add_boxes(damage, box, b - box, 0, 0); + } + goto *ret; +} + +static bool +sna_poly_line_blt(DrawablePtr drawable, + struct kgem_bo *bo, + struct sna_damage **damage, + GCPtr gc, uint32_t pixel, + int mode, int n, DDXPointPtr pt, + const BoxRec *extents, bool clipped) +{ + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna *sna = to_sna_from_pixmap(pixmap); + BoxRec boxes[512], *b = boxes, * const last_box = boxes + ARRAY_SIZE(boxes); + struct sna_fill_op fill; + DDXPointRec last; + int16_t dx, dy; + + DBG(("%s: alu=%d, fg=%08x\n", __FUNCTION__, gc->alu, (unsigned)pixel)); + + if (!sna_fill_init_blt(&fill, sna, pixmap, bo, gc->alu, pixel)) + return false; + + get_drawable_deltas(drawable, pixmap, &dx, &dy); + + if (!clipped) { + dx += drawable->x; + dy += drawable->y; + + last.x = pt->x + dx; + last.y = pt->y + dy; + pt++; + + while (--n) { + DDXPointRec p; + + p = *pt++; + if (mode == CoordModePrevious) { + p.x += last.x; + p.y += last.y; + } else { + p.x += dx; + p.y += dy; + } + if (last.x == p.x) { + b->x1 = last.x; + b->x2 = last.x + 1; + } else if (last.x < p.x) { + b->x1 = last.x; + b->x2 = p.x; + } else { + b->x1 = p.x; + b->x2 = last.x; + } + if (last.y == p.y) { + b->y1 = last.y; + b->y2 = last.y + 1; + } else if (last.y < p.y) { + b->y1 = last.y; + b->y2 = p.y; + } else { + b->y1 = p.y; + b->y2 = last.y; + } + DBG(("%s: blt (%d, %d), (%d, %d)\n", + __FUNCTION__, + b->x1, b->y1, b->x2, b->y2)); + if (++b == last_box) { + assert_pixmap_contains_boxes(pixmap, boxes, last_box-boxes, 0, 0); + fill.boxes(sna, &fill, boxes, last_box - boxes); + if (damage) + sna_damage_add_boxes(damage, boxes, last_box - boxes, 0, 0); + b = boxes; + } + + last = p; + } + } else { + RegionRec clip; + + region_set(&clip, extents); + region_maybe_clip(&clip, gc->pCompositeClip); + if (!RegionNotEmpty(&clip)) + return true; + + last.x = pt->x + drawable->x; + last.y = pt->y + drawable->y; + pt++; + + if (clip.data == NULL) { + while (--n) { + DDXPointRec p; + + p = *pt++; + if (mode == CoordModePrevious) { + p.x += last.x; + p.y += last.y; + } else { + p.x += drawable->x; + p.y += drawable->y; + } + if (last.x == p.x) { + b->x1 = last.x; + b->x2 = last.x + 1; + } else if (last.x < p.x) { + b->x1 = last.x; + b->x2 = p.x; + } else { + b->x1 = p.x; + b->x2 = last.x; + } + if (last.y == p.y) { + b->y1 = last.y; + b->y2 = last.y + 1; + } else if (last.y < p.y) { + b->y1 = last.y; + b->y2 = p.y; + } else { + b->y1 = p.y; + b->y2 = last.y; + } + DBG(("%s: blt (%d, %d), (%d, %d)\n", + __FUNCTION__, + b->x1, b->y1, b->x2, b->y2)); + if (box_intersect(b, &clip.extents)) { + b->x1 += dx; + b->x2 += dx; + b->y1 += dy; + b->y2 += dy; + if (++b == last_box) { + assert_pixmap_contains_boxes(pixmap, boxes, last_box-boxes, 0, 0); + fill.boxes(sna, &fill, boxes, last_box - boxes); + if (damage) + sna_damage_add_boxes(damage, boxes, last_box - boxes, 0, 0); + b = boxes; + } + } + + last = p; + } + } else { + const BoxRec * const clip_start = RegionBoxptr(&clip); + const BoxRec * const clip_end = clip_start + clip.data->numRects; + const BoxRec *c; + + while (--n) { + DDXPointRec p; + BoxRec box; + + p = *pt++; + if (mode == CoordModePrevious) { + p.x += last.x; + p.y += last.y; + } else { + p.x += drawable->x; + p.y += drawable->y; + } + if (last.x == p.x) { + box.x1 = last.x; + box.x2 = last.x + 1; + } else if (last.x < p.x) { + box.x1 = last.x; + box.x2 = p.x; + } else { + box.x1 = p.x; + box.x2 = last.x; + } + if (last.y == p.y) { + box.y1 = last.y; + box.y2 = last.y + 1; + } else if (last.y < p.y) { + box.y1 = last.y; + box.y2 = p.y; + } else { + box.y1 = p.y; + box.y2 = last.y; + } + DBG(("%s: blt (%d, %d), (%d, %d)\n", + __FUNCTION__, + box.x1, box.y1, box.x2, box.y2)); + + c = find_clip_box_for_y(clip_start, + clip_end, + box.y1); + while (c != clip_end) { + if (box.y2 <= c->y1) + break; + + *b = box; + if (box_intersect(b, c++)) { + b->x1 += dx; + b->x2 += dx; + b->y1 += dy; + b->y2 += dy; + if (++b == last_box) { + assert_pixmap_contains_boxes(pixmap, boxes, last_box-boxes, 0, 0); + fill.boxes(sna, &fill, boxes, last_box-boxes); + if (damage) + sna_damage_add_boxes(damage, boxes, last_box-boxes, 0, 0); + b = boxes; + } + } + } + + last = p; + } + } + RegionUninit(&clip); + } + if (b != boxes) { + assert_pixmap_contains_boxes(pixmap, boxes, b-boxes, 0, 0); + fill.boxes(sna, &fill, boxes, b - boxes); + if (damage) + sna_damage_add_boxes(damage, boxes, b - boxes, 0, 0); + } + fill.done(sna, &fill); + assert_pixmap_damage(pixmap); + return true; +} + +static unsigned +sna_poly_line_extents(DrawablePtr drawable, GCPtr gc, + int mode, int n, DDXPointPtr pt, + BoxPtr out) +{ + BoxRec box; + bool clip, blt = true; + + if (n == 0) + return 0; + + box.x2 = box.x1 = pt->x; + box.y2 = box.y1 = pt->y; + if (mode == CoordModePrevious) { + int x = box.x1; + int y = box.y1; + while (--n) { + pt++; + x += pt->x; + y += pt->y; + if (blt) + blt &= pt->x == 0 || pt->y == 0; + box_add_pt(&box, x, y); + } + } else { + int x = box.x1; + int y = box.y1; + while (--n) { + pt++; + if (blt) { + blt &= pt->x == x || pt->y == y; + x = pt->x; + y = pt->y; + } + box_add_pt(&box, pt->x, pt->y); + } + } + box.x2++; + box.y2++; + + if (gc->lineWidth) { + int extra = gc->lineWidth >> 1; + if (n > 1) { + if (gc->joinStyle == JoinMiter) + extra = 6 * gc->lineWidth; + else if (gc->capStyle == CapProjecting) + extra = gc->lineWidth; + } + if (extra) { + box.x1 -= extra; + box.x2 += extra; + box.y1 -= extra; + box.y2 += extra; + } + } + + clip = trim_and_translate_box(&box, drawable, gc); + if (box_empty(&box)) + return 0; + + *out = box; + return 1 | blt << 2 | clip << 1; +} + +/* Only use our spans code if the destination is busy and we can't perform + * the operation in place. + * + * Currently it looks to be faster to use the GPU for zero spans on all + * platforms. + */ +inline static int +_use_zero_spans(DrawablePtr drawable, GCPtr gc, const BoxRec *extents) +{ + if (USE_ZERO_SPANS) + return USE_ZERO_SPANS > 0; + + return !drawable_gc_inplace_hint(drawable, gc); +} + +static int +use_zero_spans(DrawablePtr drawable, GCPtr gc, const BoxRec *extents) +{ + bool ret = _use_zero_spans(drawable, gc, extents); + DBG(("%s? %d\n", __FUNCTION__, ret)); + return ret; +} + +/* Only use our spans code if the destination is busy and we can't perform + * the operation in place. + * + * Currently it looks to be faster to use the CPU for wide spans on all + * platforms, slow MI code. But that does not take into account the true + * cost of readback? + */ +inline static int +_use_wide_spans(DrawablePtr drawable, GCPtr gc, const BoxRec *extents) +{ + if (USE_WIDE_SPANS) + return USE_WIDE_SPANS > 0; + + return !drawable_gc_inplace_hint(drawable, gc); +} + +static int +use_wide_spans(DrawablePtr drawable, GCPtr gc, const BoxRec *extents) +{ + int ret = _use_wide_spans(drawable, gc, extents); + DBG(("%s? %d\n", __FUNCTION__, ret)); + return ret; +} + +static void +sna_poly_line(DrawablePtr drawable, GCPtr gc, + int mode, int n, DDXPointPtr pt) +{ + struct sna_pixmap *priv; + struct sna_fill_spans data; + uint32_t color; + + DBG(("%s(mode=%d, n=%d, pt[0]=(%d, %d), lineWidth=%d\n", + __FUNCTION__, mode, n, pt[0].x, pt[0].y, gc->lineWidth)); + + data.flags = sna_poly_line_extents(drawable, gc, mode, n, pt, + &data.region.extents); + if (data.flags == 0) + return; + + DBG(("%s: extents (%d, %d), (%d, %d)\n", __FUNCTION__, + data.region.extents.x1, data.region.extents.y1, + data.region.extents.x2, data.region.extents.y2)); + + data.region.data = NULL; + + if (FORCE_FALLBACK) + goto fallback; + + if (!ACCEL_POLY_LINE) + goto fallback; + + data.pixmap = get_drawable_pixmap(drawable); + data.sna = to_sna_from_pixmap(data.pixmap); + if (wedged(data.sna)) { + DBG(("%s: fallback -- wedged\n", __FUNCTION__)); + goto fallback; + } + + DBG(("%s: fill=%d [%d], line=%d [%d], width=%d, mask=%lu [%d], rectlinear=%d\n", + __FUNCTION__, + gc->fillStyle, gc->fillStyle == FillSolid, + gc->lineStyle, gc->lineStyle == LineSolid, + gc->lineWidth, + gc->planemask, PM_IS_SOLID(drawable, gc->planemask), + data.flags & 4)); + + if (!PM_IS_SOLID(drawable, gc->planemask)) + goto fallback; + + priv = sna_pixmap(data.pixmap); + if (!priv) { + DBG(("%s: not attached to pixmap %ld\n", + __FUNCTION__, data.pixmap->drawable.serialNumber)); + goto fallback; + } + + if (gc->lineStyle != LineSolid) { + DBG(("%s: lineStyle, %d, is not solid\n", + __FUNCTION__, gc->lineStyle)); + goto spans_fallback; + } + if (!(gc->lineWidth == 0 || + (gc->lineWidth == 1 && (n == 1 || gc->alu == GXcopy)))) { + DBG(("%s: non-zero lineWidth %d\n", + __FUNCTION__, gc->lineWidth)); + goto spans_fallback; + } + + if (gc_is_solid(gc, &color)) { + DBG(("%s: trying solid fill [%08x]\n", + __FUNCTION__, (unsigned)color)); + + if (data.flags & 4) { + data.bo = sna_drawable_use_bo(drawable, PREFER_GPU, + &data.region.extents, + &data.damage); + if (data.bo && + sna_poly_line_blt(drawable, + data.bo, data.damage, + gc, color, mode, n, pt, + &data.region.extents, + data.flags & 2)) + return; + } else { /* !rectilinear */ + if ((data.bo = sna_drawable_use_bo(drawable, + use_zero_spans(drawable, gc, &data.region.extents), + &data.region.extents, + &data.damage)) && + sna_poly_zero_line_blt(drawable, + data.bo, data.damage, + gc, mode, n, pt, + &data.region.extents, + data.flags & 2)) + return; + + } + } else if (data.flags & 4) { + /* Try converting these to a set of rectangles instead */ + data.bo = sna_drawable_use_bo(drawable, PREFER_GPU, + &data.region.extents, &data.damage); + if (data.bo) { + DDXPointRec p1, p2; + xRectangle *rect; + int i; + + DBG(("%s: converting to rectagnles\n", __FUNCTION__)); + + rect = malloc (n * sizeof (xRectangle)); + if (rect == NULL) + return; + + p1 = pt[0]; + for (i = 1; i < n; i++) { + if (mode == CoordModePrevious) { + p2.x = p1.x + pt[i].x; + p2.y = p1.y + pt[i].y; + } else + p2 = pt[i]; + if (p1.x < p2.x) { + rect[i].x = p1.x; + rect[i].width = p2.x - p1.x + 1; + } else if (p1.x > p2.x) { + rect[i].x = p2.x; + rect[i].width = p1.x - p2.x + 1; + } else { + rect[i].x = p1.x; + rect[i].width = 1; + } + if (p1.y < p2.y) { + rect[i].y = p1.y; + rect[i].height = p2.y - p1.y + 1; + } else if (p1.y > p2.y) { + rect[i].y = p2.y; + rect[i].height = p1.y - p2.y + 1; + } else { + rect[i].y = p1.y; + rect[i].height = 1; + } + + /* don't paint last pixel */ + if (gc->capStyle == CapNotLast) { + if (p1.x == p2.x) + rect[i].height--; + else + rect[i].width--; + } + p1 = p2; + } + + if (gc->fillStyle == FillTiled) { + i = sna_poly_fill_rect_tiled_blt(drawable, + data.bo, data.damage, + gc, n - 1, rect + 1, + &data.region.extents, + data.flags & 2); + } else { + i = sna_poly_fill_rect_stippled_blt(drawable, + data.bo, data.damage, + gc, n - 1, rect + 1, + &data.region.extents, + data.flags & 2); + } + free (rect); + + if (i) + return; + } + } + +spans_fallback: + if ((data.bo = sna_drawable_use_bo(drawable, + use_wide_spans(drawable, gc, &data.region.extents), + &data.region.extents, &data.damage))) { + DBG(("%s: converting line into spans\n", __FUNCTION__)); + get_drawable_deltas(drawable, data.pixmap, &data.dx, &data.dy); + sna_gc(gc)->priv = &data; + + if (gc->lineWidth == 0 && gc_is_solid(gc, &color)) { + struct sna_fill_op fill; + + if (gc->lineStyle == LineSolid) { + if (!sna_fill_init_blt(&fill, + data.sna, data.pixmap, + data.bo, gc->alu, color)) + goto fallback; + + data.op = &fill; + + if ((data.flags & 2) == 0) { + if (data.dx | data.dy) + sna_gc_ops__tmp.FillSpans = sna_fill_spans__fill_offset; + else + sna_gc_ops__tmp.FillSpans = sna_fill_spans__fill; + } else { + region_maybe_clip(&data.region, + gc->pCompositeClip); + if (!RegionNotEmpty(&data.region)) + return; + + if (region_is_singular(&data.region)) + sna_gc_ops__tmp.FillSpans = sna_fill_spans__fill_clip_extents; + else + sna_gc_ops__tmp.FillSpans = sna_fill_spans__fill_clip_boxes; + } + assert(gc->miTranslate); + + gc->ops = &sna_gc_ops__tmp; + DBG(("%s: miZeroLine (solid fill)\n", __FUNCTION__)); + miZeroLine(drawable, gc, mode, n, pt); + fill.done(data.sna, &fill); + } else { + data.op = &fill; + + if ((data.flags & 2) == 0) { + if (data.dx | data.dy) + sna_gc_ops__tmp.FillSpans = sna_fill_spans__dash_offset; + else + sna_gc_ops__tmp.FillSpans = sna_fill_spans__dash; + } else { + region_maybe_clip(&data.region, + gc->pCompositeClip); + if (!RegionNotEmpty(&data.region)) + return; + + if (region_is_singular(&data.region)) + sna_gc_ops__tmp.FillSpans = sna_fill_spans__dash_clip_extents; + else + sna_gc_ops__tmp.FillSpans = sna_fill_spans__dash_clip_boxes; + } + assert(gc->miTranslate); + + DBG(("%s: miZeroLine (solid dash)\n", __FUNCTION__)); + if (!sna_fill_init_blt(&fill, + data.sna, data.pixmap, + data.bo, gc->alu, color)) + goto fallback; + + gc->ops = &sna_gc_ops__tmp; + miZeroDashLine(drawable, gc, mode, n, pt); + fill.done(data.sna, &fill); + + if (sna_fill_init_blt(&fill, + data.sna, data.pixmap, + data.bo, gc->alu, + gc->bgPixel)) { + miZeroDashLine(drawable, gc, mode, n, pt); + fill.done(data.sna, &fill); + } + } + } else { + /* Note that the WideDash functions alternate + * between filling using fgPixel and bgPixel + * so we need to reset state between FillSpans and + * cannot use the fill fast paths. + */ + sna_gc_ops__tmp.FillSpans = sna_fill_spans__gpu; + gc->ops = &sna_gc_ops__tmp; + + switch (gc->lineStyle) { + default: + assert(0); + case LineSolid: + if (gc->lineWidth == 0) { + DBG(("%s: miZeroLine\n", __FUNCTION__)); + miZeroLine(drawable, gc, mode, n, pt); + } else { + DBG(("%s: miWideLine\n", __FUNCTION__)); + miWideLine(drawable, gc, mode, n, pt); + } + break; + case LineOnOffDash: + case LineDoubleDash: + if (gc->lineWidth == 0) { + DBG(("%s: miZeroDashLine\n", __FUNCTION__)); + miZeroDashLine(drawable, gc, mode, n, pt); + } else { + DBG(("%s: miWideDash\n", __FUNCTION__)); + miWideDash(drawable, gc, mode, n, pt); + } + break; + } + } + + gc->ops = (GCOps *)&sna_gc_ops; + if (data.damage) { + if (data.dx | data.dy) + pixman_region_translate(&data.region, data.dx, data.dy); + assert_pixmap_contains_box(data.pixmap, &data.region.extents); + sna_damage_add(data.damage, &data.region); + assert_pixmap_damage(data.pixmap); + } + RegionUninit(&data.region); + return; + } + +fallback: + DBG(("%s: fallback\n", __FUNCTION__)); + region_maybe_clip(&data.region, gc->pCompositeClip); + if (!RegionNotEmpty(&data.region)) + return; + + if (!sna_gc_move_to_cpu(gc, drawable, &data.region)) + goto out; + if (!sna_drawable_move_region_to_cpu(drawable, &data.region, + drawable_gc_flags(drawable, gc, + !(data.flags & 4 && n == 2)))) + goto out_gc; + + DBG(("%s: fbPolyLine\n", __FUNCTION__)); + fbPolyLine(drawable, gc, mode, n, pt); + FALLBACK_FLUSH(drawable); + +out_gc: + sna_gc_move_to_gpu(gc); +out: + RegionUninit(&data.region); +} + +static inline void box_from_seg(BoxPtr b, xSegment *seg, GCPtr gc) +{ + if (seg->x1 == seg->x2) { + if (seg->y1 > seg->y2) { + b->y2 = seg->y1 + 1; + b->y1 = seg->y2 + 1; + if (gc->capStyle != CapNotLast) + b->y1--; + } else { + b->y1 = seg->y1; + b->y2 = seg->y2; + if (gc->capStyle != CapNotLast) + b->y2++; + } + b->x1 = seg->x1; + b->x2 = seg->x1 + 1; + } else { + if (seg->x1 > seg->x2) { + b->x2 = seg->x1 + 1; + b->x1 = seg->x2 + 1; + if (gc->capStyle != CapNotLast) + b->x1--; + } else { + b->x1 = seg->x1; + b->x2 = seg->x2; + if (gc->capStyle != CapNotLast) + b->x2++; + } + b->y1 = seg->y1; + b->y2 = seg->y1 + 1; + } + + DBG(("%s: seg=(%d,%d),(%d,%d); box=(%d,%d),(%d,%d)\n", + __FUNCTION__, + seg->x1, seg->y1, seg->x2, seg->y2, + b->x1, b->y1, b->x2, b->y2)); +} + +static bool +sna_poly_segment_blt(DrawablePtr drawable, + struct kgem_bo *bo, + struct sna_damage **damage, + GCPtr gc, uint32_t pixel, + int n, xSegment *seg, + const BoxRec *extents, unsigned clipped) +{ + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna *sna = to_sna_from_pixmap(pixmap); + BoxRec boxes[512], *b = boxes, * const last_box = boxes + ARRAY_SIZE(boxes); + struct sna_fill_op fill; + int16_t dx, dy; + + DBG(("%s: n=%d, alu=%d, fg=%08lx, clipped=%d\n", + __FUNCTION__, n, gc->alu, gc->fgPixel, clipped)); + + if (!sna_fill_init_blt(&fill, sna, pixmap, bo, gc->alu, pixel)) + return false; + + get_drawable_deltas(drawable, pixmap, &dx, &dy); + + if (!clipped) { + dx += drawable->x; + dy += drawable->y; + if (dx|dy) { + do { + unsigned nbox = n; + if (nbox > ARRAY_SIZE(boxes)) + nbox = ARRAY_SIZE(boxes); + n -= nbox; + do { + box_from_seg(b, seg++, gc); + if (b->y2 > b->y1 && b->x2 > b->x1) { + b->x1 += dx; + b->x2 += dx; + b->y1 += dy; + b->y2 += dy; + b++; + } + } while (--nbox); + + if (b != boxes) { + fill.boxes(sna, &fill, boxes, b-boxes); + if (damage) + sna_damage_add_boxes(damage, boxes, b-boxes, 0, 0); + b = boxes; + } + } while (n); + } else { + do { + unsigned nbox = n; + if (nbox > ARRAY_SIZE(boxes)) + nbox = ARRAY_SIZE(boxes); + n -= nbox; + do { + box_from_seg(b++, seg++, gc); + } while (--nbox); + + if (b != boxes) { + fill.boxes(sna, &fill, boxes, b-boxes); + if (damage) + sna_damage_add_boxes(damage, boxes, b-boxes, 0, 0); + b = boxes; + } + } while (n); + } + } else { + RegionRec clip; + + region_set(&clip, extents); + region_maybe_clip(&clip, gc->pCompositeClip); + if (!RegionNotEmpty(&clip)) + goto done; + + if (clip.data) { + const BoxRec * const clip_start = RegionBoxptr(&clip); + const BoxRec * const clip_end = clip_start + clip.data->numRects; + const BoxRec *c; + do { + BoxRec box; + + box_from_seg(&box, seg++, gc); + box.x1 += drawable->x; + box.x2 += drawable->x; + box.y1 += drawable->y; + box.y2 += drawable->y; + c = find_clip_box_for_y(clip_start, + clip_end, + box.y1); + while (c != clip_end) { + if (box.y2 <= c->y1) + break; + + *b = box; + if (box_intersect(b, c++)) { + b->x1 += dx; + b->x2 += dx; + b->y1 += dy; + b->y2 += dy; + if (++b == last_box) { + fill.boxes(sna, &fill, boxes, last_box-boxes); + if (damage) + sna_damage_add_boxes(damage, boxes, last_box-boxes, 0, 0); + b = boxes; + } + } + } + } while (--n); + } else { + do { + box_from_seg(b, seg++, gc); + b->x1 += drawable->x; + b->x2 += drawable->x; + b->y1 += drawable->y; + b->y2 += drawable->y; + if (box_intersect(b, &clip.extents)) { + b->x1 += dx; + b->x2 += dx; + b->y1 += dy; + b->y2 += dy; + if (++b == last_box) { + fill.boxes(sna, &fill, boxes, last_box-boxes); + if (damage) + sna_damage_add_boxes(damage, boxes, last_box-boxes, 0, 0); + b = boxes; + } + } + } while (--n); + } + RegionUninit(&clip); + } + if (b != boxes) { + fill.boxes(sna, &fill, boxes, b - boxes); + if (damage) + sna_damage_add_boxes(damage, boxes, b - boxes, 0, 0); + } +done: + fill.done(sna, &fill); + assert_pixmap_damage(pixmap); + return true; +} + +static bool +sna_poly_zero_segment_blt(DrawablePtr drawable, + struct kgem_bo *bo, + struct sna_damage **damage, + GCPtr gc, const int _n, const xSegment *_s, + const BoxRec *extents, unsigned clipped) +{ + static void * const _jump[] = { + &&no_damage, + &&damage, + + &&no_damage_offset, + &&damage_offset, + }; + + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna *sna = to_sna_from_pixmap(pixmap); + unsigned int bias = miGetZeroLineBias(drawable->pScreen); + struct sna_fill_op fill; + RegionRec clip; + const BoxRec *last_extents; + BoxRec box[512], *b; + BoxRec *const last_box = box + ARRAY_SIZE(box); + int16_t dx, dy; + void *jump, *ret; + + DBG(("%s: alu=%d, pixel=%lx, n=%d, clipped=%d, damage=%p\n", + __FUNCTION__, gc->alu, gc->fgPixel, _n, clipped, damage)); + if (!sna_fill_init_blt(&fill, sna, pixmap, bo, gc->alu, gc->fgPixel)) + return false; + + get_drawable_deltas(drawable, pixmap, &dx, &dy); + + region_set(&clip, extents); + if (clipped) { + region_maybe_clip(&clip, gc->pCompositeClip); + if (!RegionNotEmpty(&clip)) + return true; + } + DBG(("%s: [clipped] extents=(%d, %d), (%d, %d), delta=(%d, %d)\n", + __FUNCTION__, + clip.extents.x1, clip.extents.y1, + clip.extents.x2, clip.extents.y2, + dx, dy)); + + jump = _jump[(damage != NULL) | !!(dx|dy) << 1]; + + b = box; + extents = REGION_RECTS(&clip); + last_extents = extents + REGION_NUM_RECTS(&clip); + do { + int n = _n; + const xSegment *s = _s; + do { + int16_t sdx, sdy; + int adx, ady, length; + int e, e1, e2, e3; + int x1, x2; + int y1, y2; + int oc1, oc2; + int octant; + + x1 = s->x1 + drawable->x; + y1 = s->y1 + drawable->y; + x2 = s->x2 + drawable->x; + y2 = s->y2 + drawable->y; + s++; + + DBG(("%s: segment (%d, %d) to (%d, %d)\n", + __FUNCTION__, x1, y1, x2, y2)); + if (x2 == x1 && y2 == y1) + continue; + + oc1 = 0; + OUTCODES(oc1, x1, y1, extents); + oc2 = 0; + OUTCODES(oc2, x2, y2, extents); + if (oc1 & oc2) + continue; + + CalcLineDeltas(x1, y1, x2, y2, + adx, ady, sdx, sdy, + 1, 1, octant); + + DBG(("%s: adx=(%d, %d), sdx=(%d, %d)\n", + __FUNCTION__, adx, ady, sdx, sdy)); + if (adx == 0 || ady == 0) { + if (x1 <= x2) { + b->x1 = x1; + b->x2 = x2; + } else { + b->x1 = x2; + b->x2 = x1; + } + if (y1 <= y2) { + b->y1 = y1; + b->y2 = y2; + } else { + b->y1 = y2; + b->y2 = y1; + } + b->x2++; + b->y2++; + if (oc1 | oc2) + box_intersect(b, extents); + if (++b == last_box) { + ret = &&rectangle_continue; + goto *jump; +rectangle_continue: + b = box; + } + } else if (adx >= ady) { + bool dirty; + + /* X-major segment */ + e1 = ady << 1; + e2 = e1 - (adx << 1); + e = e1 - adx; + length = adx; /* don't draw endpoint in main loop */ + + FIXUP_ERROR(e, octant, bias); + + if (oc1 | oc2) { + int pt1_clipped, pt2_clipped; + int x = x1, y = y1; + + if (miZeroClipLine(extents->x1, extents->y1, + extents->x2-1, extents->y2-1, + &x1, &y1, &x2, &y2, + adx, ady, + &pt1_clipped, &pt2_clipped, + octant, bias, oc1, oc2) == -1) + continue; + + length = abs(x2 - x1); + if (length == 0) + continue; + + if (pt1_clipped) { + int clipdx = abs(x1 - x); + int clipdy = abs(y1 - y); + e += clipdy * e2 + (clipdx - clipdy) * e1; + } + } + e3 = e2 - e1; + e = e - e1; + + b->x1 = x1; + b->y1 = y1; + dirty = false; + while (length--) { + dirty = true; + e += e1; + if (e >= 0) { + e += e3; + + if (sdx < 0) { + b->x2 = b->x1 + 1; + b->x1 = x1; + } else + b->x2 = x1 + 1; + b->y2 = b->y1 + 1; + + DBG(("%s: horizontal step: (%d, %d), box: (%d, %d), (%d, %d)\n", + __FUNCTION__, x1, y1, + b->x1, b->y1, b->x2, b->y2)); + + if (++b == last_box) { + ret = &&X_continue; + goto *jump; +X_continue: + b = box; + } + + b->x1 = x1 + sdx; + b->y1 = y1 += sdy; + dirty = false; + } + x1 += sdx; + } + if (dirty) { + x1 -= sdx; + DBG(("%s: horizontal tail: (%d, %d)\n", + __FUNCTION__, x1, y1)); + if (sdx < 0) { + b->x2 = b->x1 + 1; + b->x1 = x1; + } else + b->x2 = x1 + 1; + b->y2 = b->y1 + 1; + + if (++b == last_box) { + ret = &&X2_continue; + goto *jump; +X2_continue: + b = box; + } + } + } else { + bool dirty; + + /* Y-major segment */ + e1 = adx << 1; + e2 = e1 - (ady << 1); + e = e1 - ady; + length = ady; /* don't draw endpoint in main loop */ + + SetYMajorOctant(octant); + FIXUP_ERROR(e, octant, bias); + + if (oc1 | oc2) { + int pt1_clipped, pt2_clipped; + int x = x1, y = y1; + + if (miZeroClipLine(extents->x1, extents->y1, + extents->x2-1, extents->y2-1, + &x1, &y1, &x2, &y2, + adx, ady, + &pt1_clipped, &pt2_clipped, + octant, bias, oc1, oc2) == -1) + continue; + + length = abs(y2 - y1); + if (length == 0) + continue; + + if (pt1_clipped) { + int clipdx = abs(x1 - x); + int clipdy = abs(y1 - y); + e += clipdx * e2 + (clipdy - clipdx) * e1; + } + } + + e3 = e2 - e1; + e = e - e1; + + b->x1 = x1; + b->y1 = y1; + dirty = false; + while (length--) { + e += e1; + dirty = true; + if (e >= 0) { + e += e3; + + if (sdy < 0) { + b->y2 = b->y1 + 1; + b->y1 = y1; + } else + b->y2 = y1 + 1; + b->x2 = x1 + 1; + + if (++b == last_box) { + ret = &&Y_continue; + goto *jump; +Y_continue: + b = box; + } + + b->x1 = x1 += sdx; + b->y1 = y1 + sdy; + dirty = false; + } + y1 += sdy; + } + + if (dirty) { + y1 -= sdy; + if (sdy < 0) { + b->y2 = b->y1 + 1; + b->y1 = y1; + } else + b->y2 = y1 + 1; + b->x2 = x1 + 1; + + if (++b == last_box) { + ret = &&Y2_continue; + goto *jump; +Y2_continue: + b = box; + } + } + } + } while (--n); + } while (++extents != last_extents); + + if (b != box) { + ret = &&done; + goto *jump; + } + +done: + fill.done(sna, &fill); + assert_pixmap_damage(pixmap); + RegionUninit(&clip); + return true; + +damage: + sna_damage_add_boxes(damage, box, b-box, 0, 0); +no_damage: + fill.boxes(sna, &fill, box, b-box); + goto *ret; + +no_damage_offset: + { + BoxRec *bb = box; + do { + bb->x1 += dx; + bb->x2 += dx; + bb->y1 += dy; + bb->y2 += dy; + } while (++bb != b); + fill.boxes(sna, &fill, box, b - box); + } + goto *ret; + +damage_offset: + { + BoxRec *bb = box; + do { + bb->x1 += dx; + bb->x2 += dx; + bb->y1 += dy; + bb->y2 += dy; + } while (++bb != b); + fill.boxes(sna, &fill, box, b - box); + sna_damage_add_boxes(damage, box, b - box, 0, 0); + } + goto *ret; +} + +static unsigned +sna_poly_segment_extents(DrawablePtr drawable, GCPtr gc, + int n, xSegment *seg, + BoxPtr out) +{ + BoxRec box; + bool clipped, can_blit; + + if (n == 0) + return 0; + + if (seg->x2 >= seg->x1) { + box.x1 = seg->x1; + box.x2 = seg->x2; + } else { + box.x2 = seg->x1; + box.x1 = seg->x2; + } + + if (seg->y2 >= seg->y1) { + box.y1 = seg->y1; + box.y2 = seg->y2; + } else { + box.y2 = seg->y1; + box.y1 = seg->y2; + } + + can_blit = seg->x1 == seg->x2 || seg->y1 == seg->y2; + while (--n) { + seg++; + if (seg->x2 > seg->x1) { + if (seg->x1 < box.x1) box.x1 = seg->x1; + if (seg->x2 > box.x2) box.x2 = seg->x2; + } else { + if (seg->x2 < box.x1) box.x1 = seg->x2; + if (seg->x1 > box.x2) box.x2 = seg->x1; + } + + if (seg->y2 > seg->y1) { + if (seg->y1 < box.y1) box.y1 = seg->y1; + if (seg->y2 > box.y2) box.y2 = seg->y2; + } else { + if (seg->y2 < box.y1) box.y1 = seg->y2; + if (seg->y1 > box.y2) box.y2 = seg->y1; + } + + if (can_blit && !(seg->x1 == seg->x2 || seg->y1 == seg->y2)) + can_blit = false; + } + + box.x2++; + box.y2++; + + if (gc->lineWidth) { + int extra = gc->lineWidth; + if (gc->capStyle != CapProjecting) + extra >>= 1; + if (extra) { + box.x1 -= extra; + box.x2 += extra; + box.y1 -= extra; + box.y2 += extra; + } + } + + DBG(("%s: unclipped, untranslated extents (%d, %d), (%d, %d)\n", + __FUNCTION__, box.x1, box.y1, box.x2, box.y2)); + + clipped = trim_and_translate_box(&box, drawable, gc); + if (box_empty(&box)) + return 0; + + *out = box; + return 1 | clipped << 1 | can_blit << 2; +} + +static void +sna_poly_segment(DrawablePtr drawable, GCPtr gc, int n, xSegment *seg) +{ + struct sna_pixmap *priv; + struct sna_fill_spans data; + uint32_t color; + + DBG(("%s(n=%d, first=((%d, %d), (%d, %d)), lineWidth=%d\n", + __FUNCTION__, + n, seg->x1, seg->y1, seg->x2, seg->y2, + gc->lineWidth)); + + data.flags = sna_poly_segment_extents(drawable, gc, n, seg, + &data.region.extents); + if (data.flags == 0) + return; + + DBG(("%s: extents=(%d, %d), (%d, %d)\n", __FUNCTION__, + data.region.extents.x1, data.region.extents.y1, + data.region.extents.x2, data.region.extents.y2)); + + data.region.data = NULL; + + if (FORCE_FALLBACK) + goto fallback; + + if (!ACCEL_POLY_SEGMENT) + goto fallback; + + data.pixmap = get_drawable_pixmap(drawable); + data.sna = to_sna_from_pixmap(data.pixmap); + priv = sna_pixmap(data.pixmap); + if (priv == NULL) { + DBG(("%s: fallback -- unattached\n", __FUNCTION__)); + goto fallback; + } + + if (wedged(data.sna)) { + DBG(("%s: fallback -- wedged\n", __FUNCTION__)); + goto fallback; + } + + DBG(("%s: fill=%d [%d], line=%d [%d], width=%d, mask=%lu [%d], rectlinear=%d\n", + __FUNCTION__, + gc->fillStyle, gc->fillStyle == FillSolid, + gc->lineStyle, gc->lineStyle == LineSolid, + gc->lineWidth, + gc->planemask, PM_IS_SOLID(drawable, gc->planemask), + data.flags & 4)); + if (!PM_IS_SOLID(drawable, gc->planemask)) + goto fallback; + + if (gc->lineStyle != LineSolid || gc->lineWidth > 1) + goto spans_fallback; + if (gc_is_solid(gc, &color)) { + DBG(("%s: trying blt solid fill [%08x, flags=%x] paths\n", + __FUNCTION__, (unsigned)color, data.flags)); + + if (data.flags & 4) { + if ((data.bo = sna_drawable_use_bo(drawable, PREFER_GPU, + &data.region.extents, + &data.damage)) && + sna_poly_segment_blt(drawable, + data.bo, data.damage, + gc, color, n, seg, + &data.region.extents, + data.flags & 2)) + return; + } else { + if ((data.bo = sna_drawable_use_bo(drawable, + use_zero_spans(drawable, gc, &data.region.extents), + &data.region.extents, + &data.damage)) && + sna_poly_zero_segment_blt(drawable, + data.bo, data.damage, + gc, n, seg, + &data.region.extents, + data.flags & 2)) + return; + } + } else if (data.flags & 4) { + /* Try converting these to a set of rectangles instead */ + xRectangle *rect; + int i; + + data.bo = sna_drawable_use_bo(drawable, PREFER_GPU, + &data.region.extents, + &data.damage); + if (data.bo == NULL) + goto fallback; + + DBG(("%s: converting to rectagnles\n", __FUNCTION__)); + + rect = malloc (n * sizeof (xRectangle)); + if (rect == NULL) + return; + + for (i = 0; i < n; i++) { + if (seg[i].x1 < seg[i].x2) { + rect[i].x = seg[i].x1; + rect[i].width = seg[i].x2 - seg[i].x1 + 1; + } else if (seg[i].x1 > seg[i].x2) { + rect[i].x = seg[i].x2; + rect[i].width = seg[i].x1 - seg[i].x2 + 1; + } else { + rect[i].x = seg[i].x1; + rect[i].width = 1; + } + if (seg[i].y1 < seg[i].y2) { + rect[i].y = seg[i].y1; + rect[i].height = seg[i].y2 - seg[i].y1 + 1; + } else if (seg[i].y1 > seg[i].y2) { + rect[i].y = seg[i].y2; + rect[i].height = seg[i].y1 - seg[i].y2 + 1; + } else { + rect[i].y = seg[i].y1; + rect[i].height = 1; + } + + /* don't paint last pixel */ + if (gc->capStyle == CapNotLast) { + if (seg[i].x1 == seg[i].x2) + rect[i].height--; + else + rect[i].width--; + } + } + + if (gc->fillStyle == FillTiled) { + i = sna_poly_fill_rect_tiled_blt(drawable, + data.bo, data.damage, + gc, n, rect, + &data.region.extents, + data.flags & 2); + } else { + i = sna_poly_fill_rect_stippled_blt(drawable, + data.bo, data.damage, + gc, n, rect, + &data.region.extents, + data.flags & 2); + } + free (rect); + + if (i) + return; + } + +spans_fallback: + if ((data.bo = sna_drawable_use_bo(drawable, + use_wide_spans(drawable, gc, &data.region.extents), + &data.region.extents, + &data.damage))) { + void (*line)(DrawablePtr, GCPtr, int, int, DDXPointPtr); + int i; + + DBG(("%s: converting segments into spans\n", __FUNCTION__)); + + switch (gc->lineStyle) { + default: + case LineSolid: + if (gc->lineWidth == 0) + line = miZeroLine; + else + line = miWideLine; + break; + case LineOnOffDash: + case LineDoubleDash: + if (gc->lineWidth == 0) + line = miZeroDashLine; + else + line = miWideDash; + break; + } + + get_drawable_deltas(drawable, data.pixmap, &data.dx, &data.dy); + sna_gc(gc)->priv = &data; + + if (gc->lineWidth == 0 && + gc->lineStyle == LineSolid && + gc_is_solid(gc, &color)) { + struct sna_fill_op fill; + + if (!sna_fill_init_blt(&fill, + data.sna, data.pixmap, + data.bo, gc->alu, color)) + goto fallback; + + data.op = &fill; + + if ((data.flags & 2) == 0) { + if (data.dx | data.dy) + sna_gc_ops__tmp.FillSpans = sna_fill_spans__fill_offset; + else + sna_gc_ops__tmp.FillSpans = sna_fill_spans__fill; + } else { + region_maybe_clip(&data.region, + gc->pCompositeClip); + if (!RegionNotEmpty(&data.region)) + return; + + if (region_is_singular(&data.region)) + sna_gc_ops__tmp.FillSpans = sna_fill_spans__fill_clip_extents; + else + sna_gc_ops__tmp.FillSpans = sna_fill_spans__fill_clip_boxes; + } + assert(gc->miTranslate); + gc->ops = &sna_gc_ops__tmp; + for (i = 0; i < n; i++) + line(drawable, gc, CoordModeOrigin, 2, + (DDXPointPtr)&seg[i]); + + fill.done(data.sna, &fill); + } else { + sna_gc_ops__tmp.FillSpans = sna_fill_spans__gpu; + gc->ops = &sna_gc_ops__tmp; + + for (i = 0; i < n; i++) + line(drawable, gc, CoordModeOrigin, 2, + (DDXPointPtr)&seg[i]); + } + + gc->ops = (GCOps *)&sna_gc_ops; + if (data.damage) { + if (data.dx | data.dy) + pixman_region_translate(&data.region, data.dx, data.dy); + assert_pixmap_contains_box(data.pixmap, &data.region.extents); + sna_damage_add(data.damage, &data.region); + } + assert_pixmap_damage(data.pixmap); + RegionUninit(&data.region); + return; + } + +fallback: + DBG(("%s: fallback\n", __FUNCTION__)); + region_maybe_clip(&data.region, gc->pCompositeClip); + if (!RegionNotEmpty(&data.region)) + return; + + if (!sna_gc_move_to_cpu(gc, drawable, &data.region)) + goto out; + if (!sna_drawable_move_region_to_cpu(drawable, &data.region, + drawable_gc_flags(drawable, gc, + !(data.flags & 4 && n == 1)))) + goto out_gc; + + DBG(("%s: fbPolySegment\n", __FUNCTION__)); + fbPolySegment(drawable, gc, n, seg); + FALLBACK_FLUSH(drawable); + +out_gc: + sna_gc_move_to_gpu(gc); +out: + RegionUninit(&data.region); +} + +static unsigned +sna_poly_rectangle_extents(DrawablePtr drawable, GCPtr gc, + int n, xRectangle *r, + BoxPtr out) +{ + Box32Rec box; + int extra = gc->lineWidth >> 1; + bool clipped; + + if (n == 0) + return 0; + + box.x1 = r->x; + box.y1 = r->y; + box.x2 = box.x1 + r->width; + box.y2 = box.y1 + r->height; + + while (--n) + box32_add_rect(&box, ++r); + + box.x2++; + box.y2++; + + if (extra) { + box.x1 -= extra; + box.x2 += extra; + box.y1 -= extra; + box.y2 += extra; + } + + clipped = box32_trim_and_translate(&box, drawable, gc); + if (!box32_to_box16(&box, out)) + return 0; + + return 1 | clipped << 1; +} + +static bool +sna_poly_rectangle_blt(DrawablePtr drawable, + struct kgem_bo *bo, + struct sna_damage **damage, + GCPtr gc, int n, xRectangle *r, + const BoxRec *extents, unsigned clipped) +{ + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna *sna = to_sna_from_pixmap(pixmap); + struct sna_fill_op fill; + BoxRec boxes[512], *b = boxes, *const last_box = boxes+ARRAY_SIZE(boxes); + int16_t dx, dy; + static void * const jump[] = { + &&wide, + &&zero, + &&wide_clipped, + &&zero_clipped, + }; + + DBG(("%s: n=%d, alu=%d, width=%d, fg=%08lx, damge=%p, clipped?=%d\n", + __FUNCTION__, n, gc->alu, gc->lineWidth, gc->fgPixel, damage, clipped)); + if (!sna_fill_init_blt(&fill, sna, pixmap, bo, gc->alu, gc->fgPixel)) + return false; + + get_drawable_deltas(drawable, pixmap, &dx, &dy); + + goto *jump[(gc->lineWidth <= 1) | clipped]; + +zero: + dx += drawable->x; + dy += drawable->y; + + do { + xRectangle rr = *r++; + + if ((rr.width | rr.height) == 0) + continue; + + DBG(("%s - zero : r[%d] = (%d, %d) x (%d, %d)\n", __FUNCTION__, + n, rr.x, rr.y, rr.width, rr.height)); + rr.x += dx; + rr.y += dy; + + if (b+4 > last_box) { + fill.boxes(sna, &fill, boxes, b-boxes); + if (damage) + sna_damage_add_boxes(damage, boxes, b-boxes, 0, 0); + b = boxes; + } + + if (rr.width <= 1 || rr.height <= 1) { + b->x1 = rr.x; + b->y1 = rr.y; + b->x2 = rr.x + rr.width + (rr.height != 0); + b->y2 = rr.y + rr.height + (rr.width != 0); + DBG(("%s: blt (%d, %d), (%d, %d)\n", + __FUNCTION__, + b->x1, b->y1, b->x2,b->y2)); + b++; + } else { + b[0].x1 = rr.x; + b[0].y1 = rr.y; + b[0].x2 = rr.x + rr.width + 1; + b[0].y2 = rr.y + 1; + + b[1] = b[0]; + b[1].y1 += rr.height; + b[1].y2 += rr.height; + + b[2].y1 = rr.y + 1; + b[2].y2 = rr.y + rr.height; + b[2].x1 = rr.x; + b[2].x2 = rr.x + 1; + + b[3] = b[2]; + b[3].x1 += rr.width; + b[3].x2 += rr.width; + + b += 4; + } + } while (--n); + goto done; + +zero_clipped: + { + RegionRec clip; + BoxRec box[4]; + int count; + + region_set(&clip, extents); + region_maybe_clip(&clip, gc->pCompositeClip); + if (!RegionNotEmpty(&clip)) + goto done; + + if (clip.data) { + const BoxRec * const clip_start = RegionBoxptr(&clip); + const BoxRec * const clip_end = clip_start + clip.data->numRects; + const BoxRec *c; + do { + xRectangle rr = *r++; + + DBG(("%s - zero, clipped complex: r[%d] = (%d, %d) x (%d, %d)\n", __FUNCTION__, + n, rr.x, rr.y, rr.width, rr.height)); + + if ((rr.width | rr.height) == 0) + continue; + + rr.x += drawable->x; + rr.y += drawable->y; + + if (rr.width <= 1 || rr.height <= 1) { + box[0].x1 = rr.x; + box[0].y1 = rr.y; + box[0].x2 = rr.x + rr.width + (rr.height != 0); + box[0].y2 = rr.y + rr.height + (rr.width != 0); + count = 1; + } else { + box[0].x1 = rr.x; + box[0].y1 = rr.y; + box[0].x2 = rr.x + rr.width + 1; + box[0].y2 = rr.y + 1; + + box[1] = box[0]; + box[1].y1 += rr.height; + box[1].y2 += rr.height; + + box[2].y1 = rr.y + 1; + box[2].y2 = rr.y + rr.height; + box[2].x1 = rr.x; + box[2].x2 = rr.x + 1; + + box[3] = box[2]; + box[3].x1 += rr.width; + box[3].x2 += rr.width; + count = 4; + } + + while (count--) { + c = find_clip_box_for_y(clip_start, + clip_end, + box[count].y1); + while (c != clip_end) { + if (box[count].y2 <= c->y1) + break; + + *b = box[count]; + if (box_intersect(b, c++)) { + b->x1 += dx; + b->x2 += dx; + b->y1 += dy; + b->y2 += dy; + if (++b == last_box) { + fill.boxes(sna, &fill, boxes, last_box-boxes); + if (damage) + sna_damage_add_boxes(damage, boxes, last_box-boxes, 0, 0); + b = boxes; + } + } + + } + } + } while (--n); + } else { + do { + xRectangle rr = *r++; + DBG(("%s - zero, clip: r[%d] = (%d, %d) x (%d, %d)\n", __FUNCTION__, + n, rr.x, rr.y, rr.width, rr.height)); + + if ((rr.width | rr.height) == 0) + continue; + + rr.x += drawable->x; + rr.y += drawable->y; + + if (rr.width <= 1 || rr.height <= 1) { + box[0].x1 = rr.x; + box[0].y1 = rr.y; + box[0].x2 = rr.x + rr.width + (rr.height != 0); + box[0].y2 = rr.y + rr.height + (rr.width != 0); + count = 1; + } else { + box[0].x1 = rr.x; + box[0].y1 = rr.y; + box[0].x2 = rr.x + rr.width + 1; + box[0].y2 = rr.y + 1; + + box[1] = box[0]; + box[1].y1 += rr.height; + box[1].y2 += rr.height; + + box[2].y1 = rr.y + 1; + box[2].y2 = rr.y + rr.height; + box[2].x1 = rr.x; + box[2].x2 = rr.x + 1; + + box[3] = box[2]; + box[3].x1 += rr.width; + box[3].x2 += rr.width; + count = 4; + } + + while (count--) { + *b = box[count]; + if (box_intersect(b, &clip.extents)) { + b->x1 += dx; + b->x2 += dx; + b->y1 += dy; + b->y2 += dy; + if (++b == last_box) { + fill.boxes(sna, &fill, boxes, last_box-boxes); + if (damage) + sna_damage_add_boxes(damage, boxes, last_box-boxes, 0, 0); + b = boxes; + } + } + + } + } while (--n); + } + RegionUninit(&clip); + } + goto done; + +wide_clipped: + { + RegionRec clip; + BoxRec box[4]; + int16_t offset2 = gc->lineWidth; + int16_t offset1 = offset2 >> 1; + int16_t offset3 = offset2 - offset1; + + region_set(&clip, extents); + region_maybe_clip(&clip, gc->pCompositeClip); + DBG(("%s: wide clipped: extents=((%d, %d), (%d, %d))\n", + __FUNCTION__, + clip.extents.x1, clip.extents.y1, + clip.extents.x2, clip.extents.y2)); + if (!RegionNotEmpty(&clip)) + goto done; + + if (clip.data) { + const BoxRec * const clip_start = RegionBoxptr(&clip); + const BoxRec * const clip_end = clip_start + clip.data->numRects; + const BoxRec *c; + do { + xRectangle rr = *r++; + int count; + + if ((rr.width | rr.height) == 0) + continue; + + rr.x += drawable->x; + rr.y += drawable->y; + + if (rr.height <= offset2 || rr.width <= offset2) { + if (rr.height == 0) { + box[0].x1 = rr.x; + box[0].x2 = rr.x + rr.width; + } else { + box[0].x1 = rr.x - offset1; + box[0].x2 = rr.x + rr.width + offset3; + } + if (rr.width == 0) { + box[0].y1 = rr.y; + box[0].y2 = rr.y + rr.height; + } else { + box[0].y1 = rr.y - offset1; + box[0].y2 = rr.y + rr.height + offset3; + } + count = 1; + } else { + box[0].x1 = rr.x - offset1; + box[0].x2 = box[0].x1 + rr.width + offset2; + box[0].y1 = rr.y - offset1; + box[0].y2 = box[0].y1 + offset2; + + box[1].x1 = rr.x - offset1; + box[1].x2 = box[1].x1 + offset2; + box[1].y1 = rr.y + offset3; + box[1].y2 = rr.y + rr.height - offset1; + + box[2] = box[1]; + box[2].x1 += rr.width; + box[2].x2 += rr.width; + + box[3] = box[0]; + box[3].y1 += rr.height; + box[3].y2 += rr.height; + count = 4; + } + + while (count--) { + c = find_clip_box_for_y(clip_start, + clip_end, + box[count].y1); + while (c != clip_end) { + if (box[count].y2 <= c->y1) + break; + + *b = box[count]; + if (box_intersect(b, c++)) { + b->x1 += dx; + b->x2 += dx; + b->y1 += dy; + b->y2 += dy; + if (++b == last_box) { + fill.boxes(sna, &fill, boxes, last_box-boxes); + if (damage) + sna_damage_add_boxes(damage, boxes, last_box-boxes, 0, 0); + b = boxes; + } + } + } + } + } while (--n); + } else { + DBG(("%s: singular clip offset1=%d, offset2=%d, offset3=%d\n", + __FUNCTION__, offset1, offset2, offset3)); + do { + xRectangle rr = *r++; + int count; + rr.x += drawable->x; + rr.y += drawable->y; + + DBG(("%s: r=(%d, %d)x(%d, %d)\n", + __FUNCTION__, rr.x, rr.y, rr.width, rr.height)); + if (rr.height <= offset2 || rr.width <= offset2) { + if (rr.height == 0) { + box[0].x1 = rr.x; + box[0].x2 = rr.x + rr.width; + } else { + box[0].x1 = rr.x - offset1; + box[0].x2 = box[0].x1 + rr.width + offset2; + } + if (rr.width == 0) { + box[0].y1 = rr.y; + box[0].y2 = rr.y + rr.height; + } else { + box[0].y1 = rr.y - offset1; + box[0].y2 = box[0].y1 + rr.height + offset2; + } + count = 1; + } else { + box[0].x1 = rr.x - offset1; + box[0].x2 = box[0].x1 + rr.width + offset2; + box[0].y1 = rr.y - offset1; + box[0].y2 = box[0].y1 + offset2; + DBG(("%s: box[0]=(%d, %d), (%d, %d)\n", + __FUNCTION__, + box[0].x1, box[0].y1, + box[0].x2, box[0].y2)); + + box[1].x1 = rr.x - offset1; + box[1].x2 = box[1].x1 + offset2; + box[1].y1 = rr.y + offset3; + box[1].y2 = rr.y + rr.height - offset1; + DBG(("%s: box[1]=(%d, %d), (%d, %d)\n", + __FUNCTION__, + box[1].x1, box[1].y1, + box[1].x2, box[1].y2)); + + box[2] = box[1]; + box[2].x1 += rr.width; + box[2].x2 += rr.width; + DBG(("%s: box[2]=(%d, %d), (%d, %d)\n", + __FUNCTION__, + box[2].x1, box[2].y1, + box[2].x2, box[2].y2)); + + box[3] = box[0]; + box[3].y1 += rr.height; + box[3].y2 += rr.height; + DBG(("%s: box[3]=(%d, %d), (%d, %d)\n", + __FUNCTION__, + box[3].x1, box[3].y1, + box[3].x2, box[3].y2)); + + count = 4; + } + + while (count--) { + *b = box[count]; + if (box_intersect(b, &clip.extents)) { + b->x1 += dx; + b->x2 += dx; + b->y1 += dy; + b->y2 += dy; + if (++b == last_box) { + fill.boxes(sna, &fill, boxes, last_box-boxes); + if (damage) + sna_damage_add_boxes(damage, boxes, last_box-boxes, 0, 0); + b = boxes; + } + } + } + } while (--n); + } + RegionUninit(&clip); + } + goto done; + +wide: + { + int offset2 = gc->lineWidth; + int offset1 = offset2 >> 1; + int offset3 = offset2 - offset1; + + dx += drawable->x; + dy += drawable->y; + + do { + xRectangle rr = *r++; + + if ((rr.width | rr.height) == 0) + continue; + + rr.x += dx; + rr.y += dy; + + if (b+4 > last_box) { + fill.boxes(sna, &fill, boxes, last_box-boxes); + if (damage) + sna_damage_add_boxes(damage, boxes, last_box-boxes, 0, 0); + b = boxes; + } + + if (rr.height <= offset2 || rr.width <= offset2) { + if (rr.height == 0) { + b->x1 = rr.x; + b->x2 = rr.x + rr.width; + } else { + b->x1 = rr.x - offset1; + b->x2 = rr.x + rr.width + offset3; + } + if (rr.width == 0) { + b->y1 = rr.y; + b->y2 = rr.y + rr.height; + } else { + b->y1 = rr.y - offset1; + b->y2 = rr.y + rr.height + offset3; + } + b++; + } else { + b[0].x1 = rr.x - offset1; + b[0].x2 = b[0].x1 + rr.width + offset2; + b[0].y1 = rr.y - offset1; + b[0].y2 = b[0].y1 + offset2; + + b[1].x1 = rr.x - offset1; + b[1].x2 = b[1].x1 + offset2; + b[1].y1 = rr.y + offset3; + b[1].y2 = rr.y + rr.height - offset1; + + b[2] = b[1]; + b[2].x1 += rr.width; + b[2].x2 += rr.width; + + b[3] = b[0]; + b[3].y1 += rr.height; + b[3].y2 += rr.height; + b += 4; + } + } while (--n); + } + goto done; + +done: + if (b != boxes) { + fill.boxes(sna, &fill, boxes, b-boxes); + if (damage) + sna_damage_add_boxes(damage, boxes, b-boxes, 0, 0); + } + fill.done(sna, &fill); + assert_pixmap_damage(pixmap); + return true; +} + +static void +sna_poly_rectangle(DrawablePtr drawable, GCPtr gc, int n, xRectangle *r) +{ + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna *sna = to_sna_from_pixmap(pixmap); + struct sna_damage **damage; + struct kgem_bo *bo; + RegionRec region; + unsigned flags; + + DBG(("%s(n=%d, first=((%d, %d)x(%d, %d)), lineWidth=%d\n", + __FUNCTION__, + n, r->x, r->y, r->width, r->height, + gc->lineWidth)); + + flags = sna_poly_rectangle_extents(drawable, gc, n, r, ®ion.extents); + if (flags == 0) + return; + + DBG(("%s: extents=(%d, %d), (%d, %d), flags=%x\n", __FUNCTION__, + region.extents.x1, region.extents.y1, + region.extents.x2, region.extents.y2, + flags)); + + if (FORCE_FALLBACK) + goto fallback; + + if (!ACCEL_POLY_RECTANGLE) + goto fallback; + + if (wedged(sna)) { + DBG(("%s: fallback -- wedged\n", __FUNCTION__)); + goto fallback; + } + + DBG(("%s: line=%d [%d], join=%d [%d], mask=%lu [%d]\n", + __FUNCTION__, + gc->lineStyle, gc->lineStyle == LineSolid, + gc->joinStyle, gc->joinStyle == JoinMiter, + gc->planemask, PM_IS_SOLID(drawable, gc->planemask))); + + if (!PM_IS_SOLID(drawable, gc->planemask)) + goto fallback; + + if (gc->lineStyle == LineSolid && gc->joinStyle == JoinMiter) { + DBG(("%s: trying blt solid fill [%08lx] paths\n", + __FUNCTION__, gc->fgPixel)); + if ((bo = sna_drawable_use_bo(drawable, PREFER_GPU, + ®ion.extents, &damage)) && + sna_poly_rectangle_blt(drawable, bo, damage, + gc, n, r, ®ion.extents, flags&2)) + return; + } else { + /* Not a trivial outline, but we still maybe able to break it + * down into simpler operations that we can accelerate. + */ + if (sna_drawable_use_bo(drawable, PREFER_GPU, + ®ion.extents, &damage)) { + miPolyRectangle(drawable, gc, n, r); + return; + } + } + +fallback: + DBG(("%s: fallback\n", __FUNCTION__)); + + region.data = NULL; + region_maybe_clip(®ion, gc->pCompositeClip); + if (!RegionNotEmpty(®ion)) + return; + + if (!sna_gc_move_to_cpu(gc, drawable, ®ion)) + goto out; + if (!sna_drawable_move_region_to_cpu(drawable, ®ion, + drawable_gc_flags(drawable, gc, true))) + goto out_gc; + + DBG(("%s: miPolyRectangle\n", __FUNCTION__)); + miPolyRectangle(drawable, gc, n, r); + FALLBACK_FLUSH(drawable); +out_gc: + sna_gc_move_to_gpu(gc); +out: + RegionUninit(®ion); +} + +static unsigned +sna_poly_arc_extents(DrawablePtr drawable, GCPtr gc, + int n, xArc *arc, + BoxPtr out) +{ + BoxRec box; + bool clipped; + int v; + + if (n == 0) + return 0; + + box.x1 = arc->x; + box.x2 = bound(box.x1, arc->width); + box.y1 = arc->y; + box.y2 = bound(box.y1, arc->height); + + while (--n) { + arc++; + if (box.x1 > arc->x) + box.x1 = arc->x; + v = bound(arc->x, arc->width); + if (box.x2 < v) + box.x2 = v; + if (box.y1 > arc->y) + box.y1 = arc->y; + v = bound(arc->y, arc->height); + if (box.y2 < v) + box.y2 = v; + } + + v = gc->lineWidth >> 1; + if (v) { + box.x1 -= v; + box.x2 += v; + box.y1 -= v; + box.y2 += v; + } + + box.x2++; + box.y2++; + + clipped = trim_and_translate_box(&box, drawable, gc); + if (box_empty(&box)) + return 0; + + *out = box; + return 1 | clipped << 1; +} + +static void +sna_poly_arc(DrawablePtr drawable, GCPtr gc, int n, xArc *arc) +{ + struct sna_fill_spans data; + struct sna_pixmap *priv; + + DBG(("%s(n=%d, lineWidth=%d\n", __FUNCTION__, n, gc->lineWidth)); + + data.flags = sna_poly_arc_extents(drawable, gc, n, arc, + &data.region.extents); + if (data.flags == 0) + return; + + DBG(("%s: extents=(%d, %d), (%d, %d), flags=%x\n", __FUNCTION__, + data.region.extents.x1, data.region.extents.y1, + data.region.extents.x2, data.region.extents.y2, + data.flags)); + + data.region.data = NULL; + + if (FORCE_FALLBACK) + goto fallback; + + if (!ACCEL_POLY_ARC) + goto fallback; + + data.pixmap = get_drawable_pixmap(drawable); + data.sna = to_sna_from_pixmap(data.pixmap); + priv = sna_pixmap(data.pixmap); + if (priv == NULL) { + DBG(("%s: fallback -- unattached\n", __FUNCTION__)); + goto fallback; + } + + if (wedged(data.sna)) { + DBG(("%s: fallback -- wedged\n", __FUNCTION__)); + goto fallback; + } + + if (!PM_IS_SOLID(drawable, gc->planemask)) + goto fallback; + + if ((data.bo = sna_drawable_use_bo(drawable, + use_wide_spans(drawable, gc, &data.region.extents), + &data.region.extents, &data.damage))) { + uint32_t color; + + DBG(("%s: converting arcs into spans\n", __FUNCTION__)); + get_drawable_deltas(drawable, data.pixmap, &data.dx, &data.dy); + + if (gc_is_solid(gc, &color)) { + sna_gc(gc)->priv = &data; + + assert(gc->miTranslate); + if (gc->lineStyle == LineSolid) { + struct sna_fill_op fill; + + if (!sna_fill_init_blt(&fill, + data.sna, data.pixmap, + data.bo, gc->alu, color)) + goto fallback; + + if ((data.flags & 2) == 0) { + if (data.dx | data.dy) + sna_gc_ops__tmp.FillSpans = sna_fill_spans__fill_offset; + else + sna_gc_ops__tmp.FillSpans = sna_fill_spans__fill; + sna_gc_ops__tmp.PolyPoint = sna_poly_point__fill; + } else { + region_maybe_clip(&data.region, + gc->pCompositeClip); + if (!RegionNotEmpty(&data.region)) + return; + + if (region_is_singular(&data.region)) { + sna_gc_ops__tmp.FillSpans = sna_fill_spans__fill_clip_extents; + sna_gc_ops__tmp.PolyPoint = sna_poly_point__fill_clip_extents; + } else { + sna_gc_ops__tmp.FillSpans = sna_fill_spans__fill_clip_boxes; + sna_gc_ops__tmp.PolyPoint = sna_poly_point__fill_clip_boxes; + } + } + + data.op = &fill; + gc->ops = &sna_gc_ops__tmp; + if (gc->lineWidth == 0) + miZeroPolyArc(drawable, gc, n, arc); + else + miPolyArc(drawable, gc, n, arc); + gc->ops = (GCOps *)&sna_gc_ops; + + fill.done(data.sna, &fill); + } else { + region_maybe_clip(&data.region, + gc->pCompositeClip); + if (!RegionNotEmpty(&data.region)) + return; + + sna_gc_ops__tmp.FillSpans = sna_fill_spans__gpu; + sna_gc_ops__tmp.PolyPoint = sna_poly_point__gpu; + + gc->ops = &sna_gc_ops__tmp; + if (gc->lineWidth == 0) + miZeroPolyArc(drawable, gc, n, arc); + else + miPolyArc(drawable, gc, n, arc); + gc->ops = (GCOps *)&sna_gc_ops; + } + + if (data.damage) { + if (data.dx | data.dy) + pixman_region_translate(&data.region, data.dx, data.dy); + assert_pixmap_contains_box(data.pixmap, &data.region.extents); + sna_damage_add(data.damage, &data.region); + } + assert_pixmap_damage(data.pixmap); + RegionUninit(&data.region); + return; + } + + /* XXX still around 10x slower for x11perf -ellipse */ + if (gc->lineWidth == 0) + miZeroPolyArc(drawable, gc, n, arc); + else + miPolyArc(drawable, gc, n, arc); + return; + } + +fallback: + DBG(("%s -- fallback\n", __FUNCTION__)); + region_maybe_clip(&data.region, gc->pCompositeClip); + if (!RegionNotEmpty(&data.region)) + return; + + if (!sna_gc_move_to_cpu(gc, drawable, &data.region)) + goto out; + if (!sna_drawable_move_region_to_cpu(drawable, &data.region, + MOVE_READ | MOVE_WRITE)) + goto out_gc; + + DBG(("%s -- fbPolyArc\n", __FUNCTION__)); + fbPolyArc(drawable, gc, n, arc); + FALLBACK_FLUSH(drawable); + +out_gc: + sna_gc_move_to_gpu(gc); +out: + RegionUninit(&data.region); +} + +static bool +sna_poly_fill_rect_blt(DrawablePtr drawable, + struct kgem_bo *bo, + struct sna_damage **damage, + GCPtr gc, uint32_t pixel, + int n, xRectangle *rect, + const BoxRec *extents, + bool clipped) +{ + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna *sna = to_sna_from_pixmap(pixmap); + struct sna_fill_op fill; + BoxRec boxes[512], *b = boxes, *const last_box = boxes+ARRAY_SIZE(boxes); + int16_t dx, dy; + + DBG(("%s x %d [(%d, %d)x(%d, %d)...]+(%d,%d), clipped?=%d\n", + __FUNCTION__, n, + rect->x, rect->y, rect->width, rect->height, + drawable->x, drawable->y, + clipped)); + + if (n == 1 && region_is_singular(gc->pCompositeClip)) { + BoxRec r; + bool success = true; + + r.x1 = rect->x + drawable->x; + r.y1 = rect->y + drawable->y; + r.x2 = bound(r.x1, rect->width); + r.y2 = bound(r.y1, rect->height); + if (box_intersect(&r, &gc->pCompositeClip->extents)) { + get_drawable_deltas(drawable, pixmap, &dx, &dy); + r.x1 += dx; r.y1 += dy; + r.x2 += dx; r.y2 += dy; + if (sna->render.fill_one(sna, pixmap, bo, pixel, + r.x1, r.y1, r.x2, r.y2, + gc->alu)) { + if (damage) { + assert_pixmap_contains_box(pixmap, &r); + if (r.x2 - r.x1 == pixmap->drawable.width && + r.y2 - r.y1 == pixmap->drawable.height) { + sna_damage_all(damage, + pixmap->drawable.width, + pixmap->drawable.height); + } else + sna_damage_add_box(damage, &r); + } + assert_pixmap_damage(pixmap); + + if ((gc->alu == GXcopy || gc->alu == GXclear) && + r.x2 - r.x1 == pixmap->drawable.width && + r.y2 - r.y1 == pixmap->drawable.height) { + struct sna_pixmap *priv = sna_pixmap(pixmap); + if (bo == priv->gpu_bo) { + sna_damage_all(&priv->gpu_damage, + pixmap->drawable.width, + pixmap->drawable.height); + sna_damage_destroy(&priv->cpu_damage); + list_del(&priv->list); + priv->undamaged = false; + priv->clear = true; + priv->clear_color = gc->alu == GXcopy ? pixel : 0; + + DBG(("%s: pixmap=%ld, marking clear [%08x]\n", + __FUNCTION__, pixmap->drawable.serialNumber, priv->clear_color)); + } + } + } else + success = false; + } + + return success; + } + + if (!sna_fill_init_blt(&fill, sna, pixmap, bo, gc->alu, pixel)) { + DBG(("%s: unsupported blt\n", __FUNCTION__)); + return false; + } + + get_drawable_deltas(drawable, pixmap, &dx, &dy); + if (!clipped) { + dx += drawable->x; + dy += drawable->y; + + sna_damage_add_rectangles(damage, rect, n, dx, dy); + if (dx|dy) { + do { + unsigned nbox = n; + if (nbox > ARRAY_SIZE(boxes)) + nbox = ARRAY_SIZE(boxes); + n -= nbox; + do { + b->x1 = rect->x + dx; + b->y1 = rect->y + dy; + b->x2 = b->x1 + rect->width; + b->y2 = b->y1 + rect->height; + b++; + rect++; + } while (--nbox); + fill.boxes(sna, &fill, boxes, b-boxes); + b = boxes; + } while (n); + } else { + do { + unsigned nbox = n; + if (nbox > ARRAY_SIZE(boxes)) + nbox = ARRAY_SIZE(boxes); + n -= nbox; + do { + b->x1 = rect->x; + b->y1 = rect->y; + b->x2 = b->x1 + rect->width; + b->y2 = b->y1 + rect->height; + b++; + rect++; + } while (--nbox); + fill.boxes(sna, &fill, boxes, b-boxes); + b = boxes; + } while (n); + } + } else { + RegionRec clip; + + region_set(&clip, extents); + region_maybe_clip(&clip, gc->pCompositeClip); + if (!RegionNotEmpty(&clip)) + goto done; + + if (clip.data == NULL) { + do { + b->x1 = rect->x + drawable->x; + b->y1 = rect->y + drawable->y; + b->x2 = bound(b->x1, rect->width); + b->y2 = bound(b->y1, rect->height); + rect++; + + if (box_intersect(b, &clip.extents)) { + b->x1 += dx; + b->x2 += dx; + b->y1 += dy; + b->y2 += dy; + if (++b == last_box) { + fill.boxes(sna, &fill, boxes, last_box-boxes); + if (damage) + sna_damage_add_boxes(damage, boxes, last_box-boxes, 0, 0); + b = boxes; + } + } + } while (--n); + } else { + const BoxRec * const clip_start = RegionBoxptr(&clip); + const BoxRec * const clip_end = clip_start + clip.data->numRects; + const BoxRec *c; + + do { + BoxRec box; + + box.x1 = rect->x + drawable->x; + box.y1 = rect->y + drawable->y; + box.x2 = bound(box.x1, rect->width); + box.y2 = bound(box.y1, rect->height); + rect++; + + c = find_clip_box_for_y(clip_start, + clip_end, + box.y1); + while (c != clip_end) { + if (box.y2 <= c->y1) + break; + + *b = box; + if (box_intersect(b, c++)) { + b->x1 += dx; + b->x2 += dx; + b->y1 += dy; + b->y2 += dy; + if (++b == last_box) { + fill.boxes(sna, &fill, boxes, last_box-boxes); + if (damage) + sna_damage_add_boxes(damage, boxes, last_box-boxes, 0, 0); + b = boxes; + } + } + + } + } while (--n); + } + + RegionUninit(&clip); + if (b != boxes) { + fill.boxes(sna, &fill, boxes, b-boxes); + if (damage) + sna_damage_add_boxes(damage, boxes, b-boxes, 0, 0); + } + } +done: + fill.done(sna, &fill); + assert_pixmap_damage(pixmap); + return true; +} + +static uint32_t +get_pixel(PixmapPtr pixmap) +{ + DBG(("%s\n", __FUNCTION__)); + if (!sna_pixmap_move_to_cpu(pixmap, MOVE_READ)) + return 0; + + switch (pixmap->drawable.bitsPerPixel) { + case 32: return *(uint32_t *)pixmap->devPrivate.ptr; + case 16: return *(uint16_t *)pixmap->devPrivate.ptr; + default: return *(uint8_t *)pixmap->devPrivate.ptr; + } +} + +static void +sna_poly_fill_polygon(DrawablePtr draw, GCPtr gc, + int shape, int mode, + int n, DDXPointPtr pt) +{ + struct sna_fill_spans data; + struct sna_pixmap *priv; + + DBG(("%s(n=%d, PlaneMask: %lx (solid %d), solid fill: %d [style=%d, tileIsPixel=%d], alu=%d)\n", __FUNCTION__, + n, gc->planemask, !!PM_IS_SOLID(draw, gc->planemask), + (gc->fillStyle == FillSolid || + (gc->fillStyle == FillTiled && gc->tileIsPixel)), + gc->fillStyle, gc->tileIsPixel, + gc->alu)); + DBG(("%s: draw=%ld, offset=(%d, %d), size=%dx%d\n", + __FUNCTION__, draw->serialNumber, + draw->x, draw->y, draw->width, draw->height)); + + data.flags = sna_poly_point_extents(draw, gc, mode, n, pt, + &data.region.extents); + if (data.flags == 0) { + DBG(("%s, nothing to do\n", __FUNCTION__)); + return; + } + + DBG(("%s: extents(%d, %d), (%d, %d), flags=%x\n", __FUNCTION__, + data.region.extents.x1, data.region.extents.y1, + data.region.extents.x2, data.region.extents.y2, + data.flags)); + + data.region.data = NULL; + + if (FORCE_FALLBACK) + goto fallback; + + if (!ACCEL_POLY_FILL_POLYGON) + goto fallback; + + data.pixmap = get_drawable_pixmap(draw); + data.sna = to_sna_from_pixmap(data.pixmap); + priv = sna_pixmap(data.pixmap); + if (priv == NULL) { + DBG(("%s: fallback -- unattached\n", __FUNCTION__)); + goto fallback; + } + + if (wedged(data.sna)) { + DBG(("%s: fallback -- wedged\n", __FUNCTION__)); + goto fallback; + } + + if (!PM_IS_SOLID(draw, gc->planemask)) + goto fallback; + + if ((data.bo = sna_drawable_use_bo(draw, + (shape == Convex ? use_zero_spans : use_wide_spans)(draw, gc, &data.region.extents), + &data.region.extents, + &data.damage))) { + uint32_t color; + + sna_gc(gc)->priv = &data; + get_drawable_deltas(draw, data.pixmap, &data.dx, &data.dy); + + if (gc_is_solid(gc, &color)) { + struct sna_fill_op fill; + + if (!sna_fill_init_blt(&fill, + data.sna, data.pixmap, + data.bo, gc->alu, color)) + goto fallback; + + data.op = &fill; + + if ((data.flags & 2) == 0) { + if (data.dx | data.dy) + sna_gc_ops__tmp.FillSpans = sna_fill_spans__fill_offset; + else + sna_gc_ops__tmp.FillSpans = sna_fill_spans__fill; + } else { + region_maybe_clip(&data.region, + gc->pCompositeClip); + if (!RegionNotEmpty(&data.region)) + return; + + if (region_is_singular(&data.region)) + sna_gc_ops__tmp.FillSpans = sna_fill_spans__fill_clip_extents; + else + sna_gc_ops__tmp.FillSpans = sna_fill_spans__fill_clip_boxes; + } + assert(gc->miTranslate); + gc->ops = &sna_gc_ops__tmp; + + miFillPolygon(draw, gc, shape, mode, n, pt); + fill.done(data.sna, &fill); + } else { + sna_gc_ops__tmp.FillSpans = sna_fill_spans__gpu; + gc->ops = &sna_gc_ops__tmp; + + miFillPolygon(draw, gc, shape, mode, n, pt); + } + + gc->ops = (GCOps *)&sna_gc_ops; + if (data.damage) { + if (data.dx | data.dy) + pixman_region_translate(&data.region, data.dx, data.dy); + assert_pixmap_contains_box(data.pixmap, &data.region.extents); + sna_damage_add(data.damage, &data.region); + } + assert_pixmap_damage(data.pixmap); + RegionUninit(&data.region); + return; + } + +fallback: + DBG(("%s: fallback (%d, %d), (%d, %d)\n", __FUNCTION__, + data.region.extents.x1, data.region.extents.y1, + data.region.extents.x2, data.region.extents.y2)); + region_maybe_clip(&data.region, gc->pCompositeClip); + if (!RegionNotEmpty(&data.region)) { + DBG(("%s: nothing to do, all clipped\n", __FUNCTION__)); + return; + } + + if (!sna_gc_move_to_cpu(gc, draw, &data.region)) + goto out; + if (!sna_drawable_move_region_to_cpu(draw, &data.region, + drawable_gc_flags(draw, gc, true))) + goto out_gc; + + DBG(("%s: fallback -- miFillPolygon -> sna_fill_spans__cpu\n", + __FUNCTION__)); + miFillPolygon(draw, gc, shape, mode, n, pt); +out_gc: + sna_gc_move_to_gpu(gc); +out: + RegionUninit(&data.region); +} + +static struct kgem_bo * +sna_pixmap_get_source_bo(PixmapPtr pixmap) +{ + struct sna_pixmap *priv = sna_pixmap(pixmap); + + if (priv == NULL) { + struct kgem_bo *upload; + struct sna *sna = to_sna_from_pixmap(pixmap); + void *ptr; + + upload = kgem_create_buffer_2d(&sna->kgem, + pixmap->drawable.width, + pixmap->drawable.height, + pixmap->drawable.bitsPerPixel, + KGEM_BUFFER_WRITE_INPLACE, + &ptr); + if (upload == NULL) + return NULL; + + memcpy_blt(pixmap->devPrivate.ptr, ptr, + pixmap->drawable.bitsPerPixel, + pixmap->devKind, upload->pitch, + 0, 0, + 0, 0, + pixmap->drawable.width, + pixmap->drawable.height); + + return upload; + } + + if (priv->gpu_damage && !sna_pixmap_move_to_gpu(pixmap, MOVE_READ)) + return NULL; + + if (priv->cpu_damage && priv->cpu_bo) + return kgem_bo_reference(priv->cpu_bo); + + if (!sna_pixmap_force_to_gpu(pixmap, MOVE_READ)) + return NULL; + + return kgem_bo_reference(priv->gpu_bo); +} + +/* +static bool +tile(DrawablePtr drawable, + struct kgem_bo *bo, struct sna_damage **damage, + PixmapPtr tile, const DDXPointRec * const origin, int alu, + int n, xRectangle *rect, + const BoxRec *extents, unsigned clipped) + */ + +static bool +sna_poly_fill_rect_tiled_8x8_blt(DrawablePtr drawable, + struct kgem_bo *bo, struct sna_damage **damage, + struct kgem_bo *tile_bo, GCPtr gc, + int n, const xRectangle *r, + const BoxRec *extents, unsigned clipped) +{ + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna *sna = to_sna_from_pixmap(pixmap); + const DDXPointRec * const origin = &gc->patOrg; + uint32_t br00, br13; + int tx, ty; + int16_t dx, dy; + uint32_t *b; + + if (NO_TILE_8x8) + return false; + + DBG(("%s x %d [(%d, %d)x(%d, %d)...], clipped=%x\n", + __FUNCTION__, n, r->x, r->y, r->width, r->height, clipped)); + + kgem_set_mode(&sna->kgem, KGEM_BLT); + if (!kgem_check_batch(&sna->kgem, 8+2*3) || + !kgem_check_reloc(&sna->kgem, 2) || + !kgem_check_bo_fenced(&sna->kgem, bo)) { + _kgem_submit(&sna->kgem); + _kgem_set_mode(&sna->kgem, KGEM_BLT); + } + + br00 = XY_SCANLINE_BLT; + br13 = bo->pitch; + if (sna->kgem.gen >= 40 && bo->tiling) { + br00 |= BLT_DST_TILED; + br13 >>= 2; + } + br13 |= blt_depth(drawable->depth) << 24; + br13 |= fill_ROP[gc->alu] << 16; + + get_drawable_deltas(drawable, pixmap, &dx, &dy); + assert(extents->x1 + dx >= 0); + assert(extents->y1 + dy >= 0); + assert(extents->x2 + dx <= pixmap->drawable.width); + assert(extents->y2 + dy <= pixmap->drawable.height); + + if (!clipped) { + dx += drawable->x; + dy += drawable->y; + + sna_damage_add_rectangles(damage, r, n, dx, dy); + if (n == 1) { + tx = (r->x - origin->x) % 8; + if (tx < 0) + tx = 8 - tx; + ty = (r->y - origin->y) % 8; + if (ty < 0) + ty = 8 - ty; + + assert(r->x + dx >= 0); + assert(r->y + dy >= 0); + assert(r->x + dx + r->width <= pixmap->drawable.width); + assert(r->y + dy + r->height <= pixmap->drawable.height); + + b = sna->kgem.batch + sna->kgem.nbatch; + b[0] = XY_PAT_BLT | tx << 12 | ty << 8 | 3 << 20 | (br00 & BLT_DST_TILED); + b[1] = br13; + b[2] = (r->y + dy) << 16 | (r->x + dx); + b[3] = (r->y + r->height + dy) << 16 | (r->x + r->width + dx); + b[4] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 4, bo, + I915_GEM_DOMAIN_RENDER << 16 | + I915_GEM_DOMAIN_RENDER | + KGEM_RELOC_FENCED, + 0); + b[5] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 5, tile_bo, + I915_GEM_DOMAIN_RENDER << 16 | + KGEM_RELOC_FENCED, + 0); + sna->kgem.nbatch += 6; + } else do { + int n_this_time; + + b = sna->kgem.batch + sna->kgem.nbatch; + b[0] = XY_SETUP_BLT | 3 << 20; + b[1] = br13; + b[2] = 0; + b[3] = 0; + b[4] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 4, bo, + I915_GEM_DOMAIN_RENDER << 16 | + I915_GEM_DOMAIN_RENDER | + KGEM_RELOC_FENCED, + 0); + b[5] = gc->bgPixel; + b[6] = gc->fgPixel; + b[7] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 7, tile_bo, + I915_GEM_DOMAIN_RENDER << 16 | + KGEM_RELOC_FENCED, + 0); + sna->kgem.nbatch += 8; + + n_this_time = n; + if (3*n_this_time > sna->kgem.surface - sna->kgem.nbatch - KGEM_BATCH_RESERVED) + n_this_time = (sna->kgem.surface - sna->kgem.nbatch - KGEM_BATCH_RESERVED) / 3; + assert(n_this_time); + n -= n_this_time; + + b = sna->kgem.batch + sna->kgem.nbatch; + sna->kgem.nbatch += 3*n_this_time; + do { + assert(r->x + dx >= 0); + assert(r->y + dy >= 0); + assert(r->x + dx + r->width <= pixmap->drawable.width); + assert(r->y + dy + r->height <= pixmap->drawable.height); + + tx = (r->x - origin->x) % 8; + if (tx < 0) + tx = 8 - tx; + ty = (r->y - origin->y) % 8; + if (ty < 0) + ty = 8 - ty; + + b[0] = br00 | tx << 12 | ty << 8; + b[1] = (r->y + dy) << 16 | (r->x + dx); + b[2] = (r->y + r->height + dy) << 16 | (r->x + r->width + dx); + b += 3; r++; + } while (--n_this_time); + + if (!n) + break; + + _kgem_submit(&sna->kgem); + _kgem_set_mode(&sna->kgem, KGEM_BLT); + } while (1); + } else { + RegionRec clip; + + region_set(&clip, extents); + region_maybe_clip(&clip, gc->pCompositeClip); + if (!RegionNotEmpty(&clip)) + goto done; + + b = sna->kgem.batch + sna->kgem.nbatch; + b[0] = XY_SETUP_BLT | 3 << 20; + b[1] = br13; + b[2] = 0; + b[3] = 0; + b[4] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 4, bo, + I915_GEM_DOMAIN_RENDER << 16 | + I915_GEM_DOMAIN_RENDER | + KGEM_RELOC_FENCED, + 0); + b[5] = gc->bgPixel; + b[6] = gc->fgPixel; + b[7] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 7, tile_bo, + I915_GEM_DOMAIN_RENDER << 16 | + KGEM_RELOC_FENCED, + 0); + sna->kgem.nbatch += 8; + + if (clip.data == NULL) { + const BoxRec *c = &clip.extents; + while (n--) { + BoxRec box; + + box.x1 = r->x + drawable->x; + box.y1 = r->y + drawable->y; + box.x2 = bound(box.x1, r->width); + box.y2 = bound(box.y1, r->height); + r++; + + if (box_intersect(&box, c)) { + if (!kgem_check_batch(&sna->kgem, 3)) { + _kgem_submit(&sna->kgem); + _kgem_set_mode(&sna->kgem, KGEM_BLT); + b = sna->kgem.batch + sna->kgem.nbatch; + b[0] = XY_SETUP_BLT | 3 << 20; + b[1] = br13; + b[2] = 0; + b[3] = 0; + b[4] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 4, bo, + I915_GEM_DOMAIN_RENDER << 16 | + I915_GEM_DOMAIN_RENDER | + KGEM_RELOC_FENCED, + 0); + b[5] = gc->bgPixel; + b[6] = gc->fgPixel; + b[7] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 7, tile_bo, + I915_GEM_DOMAIN_RENDER << 16 | + KGEM_RELOC_FENCED, + 0); + sna->kgem.nbatch += 8; + } + + assert(box.x1 + dx >= 0); + assert(box.y1 + dy >= 0); + assert(box.x2 + dx <= pixmap->drawable.width); + assert(box.y2 + dy <= pixmap->drawable.height); + + ty = (box.y1 - drawable->y - origin->y) % 8; + if (ty < 0) + ty = 8 - ty; + + tx = (box.x1 - drawable->x - origin->x) % 8; + if (tx < 0) + tx = 8 - tx; + + b = sna->kgem.batch + sna->kgem.nbatch; + b[0] = br00 | tx << 12 | ty << 8; + b[1] = (box.y1 + dy) << 16 | (box.x1 + dx); + b[2] = (box.y2 + dy) << 16 | (box.x2 + dx); + sna->kgem.nbatch += 3; + } + } + } else { + const BoxRec * const clip_start = RegionBoxptr(&clip); + const BoxRec * const clip_end = clip_start + clip.data->numRects; + const BoxRec *c; + + do { + BoxRec box; + + box.x1 = r->x + drawable->x; + box.y1 = r->y + drawable->y; + box.x2 = bound(box.x1, r->width); + box.y2 = bound(box.y1, r->height); + r++; + + c = find_clip_box_for_y(clip_start, + clip_end, + box.y1); + while (c != clip_end) { + BoxRec bb; + + if (box.y2 <= c->y1) + break; + + bb = box; + if (box_intersect(&bb, c++)) { + if (!kgem_check_batch(&sna->kgem, 3)) { + _kgem_submit(&sna->kgem); + _kgem_set_mode(&sna->kgem, KGEM_BLT); + b = sna->kgem.batch + sna->kgem.nbatch; + b[0] = XY_SETUP_BLT | 3 << 20; + b[1] = br13; + b[2] = 0; + b[3] = 0; + b[4] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 4, bo, + I915_GEM_DOMAIN_RENDER << 16 | + I915_GEM_DOMAIN_RENDER | + KGEM_RELOC_FENCED, + 0); + b[5] = gc->bgPixel; + b[6] = gc->fgPixel; + b[7] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 7, tile_bo, + I915_GEM_DOMAIN_RENDER << 16 | + KGEM_RELOC_FENCED, + 0); + sna->kgem.nbatch += 8; + } + + assert(bb.x1 + dx >= 0); + assert(bb.y1 + dy >= 0); + assert(bb.x2 + dx <= pixmap->drawable.width); + assert(bb.y2 + dy <= pixmap->drawable.height); + + ty = (bb.y1 - drawable->y - origin->y) % 8; + if (ty < 0) + ty = 8 - ty; + + tx = (bb.x1 - drawable->x - origin->x) % 8; + if (tx < 0) + tx = 8 - tx; + + b = sna->kgem.batch + sna->kgem.nbatch; + b[0] = br00 | tx << 12 | ty << 8; + b[1] = (bb.y1 + dy) << 16 | (bb.x1 + dx); + b[2] = (bb.y2 + dy) << 16 | (bb.x2 + dx); + sna->kgem.nbatch += 3; + } + } + } while (--n); + } + } +done: + assert_pixmap_damage(pixmap); + sna->blt_state.fill_bo = 0; + return true; +} + +static bool +sna_poly_fill_rect_tiled_nxm_blt(DrawablePtr drawable, + struct kgem_bo *bo, + struct sna_damage **damage, + GCPtr gc, int n, const xRectangle *rect, + const BoxRec *extents, unsigned clipped) +{ + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna *sna = to_sna_from_pixmap(pixmap); + PixmapPtr tile = gc->tile.pixmap; + struct kgem_bo *upload; + int w, h, cpp; + void *ptr; + bool ret; + + DBG(("%s: %dx%d\n", __FUNCTION__, + tile->drawable.width, tile->drawable.height)); + + if (!sna_pixmap_move_to_cpu(tile, MOVE_READ)) + return false; + + upload = kgem_create_buffer(&sna->kgem, 8*tile->drawable.bitsPerPixel, + KGEM_BUFFER_WRITE_INPLACE, + &ptr); + if (upload == NULL) + return false; + + assert(tile->drawable.height && tile->drawable.height <= 8); + assert(tile->drawable.width && tile->drawable.width <= 8); + + cpp = tile->drawable.bitsPerPixel/8; + for (h = 0; h < tile->drawable.height; h++) { + uint8_t *src = (uint8_t *)tile->devPrivate.ptr + tile->devKind*h; + uint8_t *dst = (uint8_t *)ptr + 8*cpp*h; + + w = tile->drawable.width*cpp; + memcpy(dst, src, w); + while (w < 8*cpp) { + memcpy(dst+w, dst, w); + w *= 2; + } + } + while (h < 8) { + memcpy((uint8_t*)ptr + h*w, ptr, h*w); + h *= 2; + } + + ret = sna_poly_fill_rect_tiled_8x8_blt(drawable, bo, damage, + upload, gc, n, rect, + extents, clipped); + + kgem_bo_destroy(&sna->kgem, upload); + return ret; +} + +static bool +sna_poly_fill_rect_tiled_blt(DrawablePtr drawable, + struct kgem_bo *bo, + struct sna_damage **damage, + GCPtr gc, int n, xRectangle *rect, + const BoxRec *extents, unsigned clipped) +{ + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna *sna = to_sna_from_pixmap(pixmap); + PixmapPtr tile = gc->tile.pixmap; + struct kgem_bo *tile_bo; + const DDXPointRec * const origin = &gc->patOrg; + struct sna_copy_op copy; + CARD32 alu = gc->alu; + int tile_width, tile_height; + int16_t dx, dy; + + DBG(("%s x %d [(%d, %d)x(%d, %d)...]\n", + __FUNCTION__, n, rect->x, rect->y, rect->width, rect->height)); + + tile_width = tile->drawable.width; + tile_height = tile->drawable.height; + if ((tile_width | tile_height) == 1) { + DBG(("%s: single pixel tile pixmap ,converting to solid fill\n", + __FUNCTION__)); + return sna_poly_fill_rect_blt(drawable, bo, damage, + gc, get_pixel(tile), + n, rect, + extents, clipped); + } + + /* XXX [248]x[238] tiling can be reduced to a pattern fill. + * Also we can do the lg2 reduction for BLT and use repeat modes for + * RENDER. + */ + + if ((tile->drawable.width | tile->drawable.height) == 8) { + bool ret; + + tile_bo = sna_pixmap_get_source_bo(tile); + ret = sna_poly_fill_rect_tiled_8x8_blt(drawable, bo, damage, + tile_bo, gc, n, rect, + extents, clipped); + kgem_bo_destroy(&sna->kgem, tile_bo); + + return ret; + } + + if ((tile->drawable.width | tile->drawable.height) <= 0xc && + is_power_of_two(tile->drawable.width) && + is_power_of_two(tile->drawable.height)) + return sna_poly_fill_rect_tiled_nxm_blt(drawable, bo, damage, + gc, n, rect, + extents, clipped); + + tile_bo = sna_pixmap_get_source_bo(tile); + if (tile_bo == NULL) { + DBG(("%s: unable to move tile go GPU, fallback\n", + __FUNCTION__)); + return false; + } + + if (!sna_copy_init_blt(©, sna, tile, tile_bo, pixmap, bo, alu)) { + DBG(("%s: unsupported blt\n", __FUNCTION__)); + kgem_bo_destroy(&sna->kgem, tile_bo); + return false; + } + + get_drawable_deltas(drawable, pixmap, &dx, &dy); + if (!clipped) { + dx += drawable->x; + dy += drawable->y; + + sna_damage_add_rectangles(damage, rect, n, dx, dy); + do { + xRectangle r = *rect++; + int16_t tile_y = (r.y - origin->y) % tile_height; + if (tile_y < 0) + tile_y += tile_height; + + assert(r.x + dx >= 0); + assert(r.y + dy >= 0); + assert(r.x + dx + r.width <= pixmap->drawable.width); + assert(r.y + dy + r.height <= pixmap->drawable.height); + + r.y += dy; + do { + int16_t width = r.width; + int16_t x = r.x + dx, tile_x; + int16_t h = tile_height - tile_y; + if (h > r.height) + h = r.height; + r.height -= h; + + tile_x = (r.x - origin->x) % tile_width; + if (tile_x < 0) + tile_x += tile_width; + + do { + int16_t w = tile_width - tile_x; + if (w > width) + w = width; + width -= w; + + copy.blt(sna, ©, + tile_x, tile_y, + w, h, + x, r.y); + + x += w; + tile_x = 0; + } while (width); + r.y += h; + tile_y = 0; + } while (r.height); + } while (--n); + } else { + RegionRec clip; + + region_set(&clip, extents); + region_maybe_clip(&clip, gc->pCompositeClip); + if (!RegionNotEmpty(&clip)) + goto done; + + if (clip.data == NULL) { + const BoxRec *box = &clip.extents; + while (n--) { + BoxRec r; + + r.x1 = rect->x + drawable->x; + r.y1 = rect->y + drawable->y; + r.x2 = bound(r.x1, rect->width); + r.y2 = bound(r.y1, rect->height); + rect++; + + if (box_intersect(&r, box)) { + int height = r.y2 - r.y1; + int dst_y = r.y1; + int tile_y = (r.y1 - drawable->y - origin->y) % tile_height; + if (tile_y < 0) + tile_y += tile_height; + + while (height) { + int width = r.x2 - r.x1; + int dst_x = r.x1, tile_x; + int h = tile_height - tile_y; + if (h > height) + h = height; + height -= h; + + tile_x = (r.x1 - drawable->x - origin->x) % tile_width; + if (tile_x < 0) + tile_x += tile_width; + + while (width > 0) { + int w = tile_width - tile_x; + if (w > width) + w = width; + width -= w; + + copy.blt(sna, ©, + tile_x, tile_y, + w, h, + dst_x + dx, dst_y + dy); + if (damage) { + BoxRec b; + + b.x1 = dst_x + dx; + b.y1 = dst_y + dy; + b.x2 = b.x1 + w; + b.y2 = b.y1 + h; + + assert_pixmap_contains_box(pixmap, &b); + sna_damage_add_box(damage, &b); + } + + dst_x += w; + tile_x = 0; + } + dst_y += h; + tile_y = 0; + } + } + } + } else { + while (n--) { + RegionRec region; + BoxRec *box; + int nbox; + + region.extents.x1 = rect->x + drawable->x; + region.extents.y1 = rect->y + drawable->y; + region.extents.x2 = bound(region.extents.x1, rect->width); + region.extents.y2 = bound(region.extents.y1, rect->height); + rect++; + + region.data = NULL; + RegionIntersect(®ion, ®ion, &clip); + + nbox = REGION_NUM_RECTS(®ion); + box = REGION_RECTS(®ion); + while (nbox--) { + int height = box->y2 - box->y1; + int dst_y = box->y1; + int tile_y = (box->y1 - drawable->y - origin->y) % tile_height; + if (tile_y < 0) + tile_y += tile_height; + + while (height) { + int width = box->x2 - box->x1; + int dst_x = box->x1, tile_x; + int h = tile_height - tile_y; + if (h > height) + h = height; + height -= h; + + tile_x = (box->x1 - drawable->x - origin->x) % tile_width; + if (tile_x < 0) + tile_x += tile_width; + + while (width > 0) { + int w = tile_width - tile_x; + if (w > width) + w = width; + width -= w; + + copy.blt(sna, ©, + tile_x, tile_y, + w, h, + dst_x + dx, dst_y + dy); + if (damage) { + BoxRec b; + + b.x1 = dst_x + dx; + b.y1 = dst_y + dy; + b.x2 = b.x1 + w; + b.y2 = b.y1 + h; + + assert_pixmap_contains_box(pixmap, &b); + sna_damage_add_box(damage, &b); + } + + dst_x += w; + tile_x = 0; + } + dst_y += h; + tile_y = 0; + } + box++; + } + + RegionUninit(®ion); + } + } + + RegionUninit(&clip); + } +done: + copy.done(sna, ©); + assert_pixmap_damage(pixmap); + kgem_bo_destroy(&sna->kgem, tile_bo); + return true; +} + +static bool +sna_poly_fill_rect_stippled_8x8_blt(DrawablePtr drawable, + struct kgem_bo *bo, + struct sna_damage **damage, + GCPtr gc, int n, xRectangle *r, + const BoxRec *extents, unsigned clipped) +{ + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna *sna = to_sna_from_pixmap(pixmap); + uint32_t pat[2] = {0, 0}, br00, br13; + int16_t dx, dy; + uint32_t *b; + + if (NO_STIPPLE_8x8) + return false; + + DBG(("%s: alu=%d, upload (%d, %d), (%d, %d), origin (%d, %d)\n", + __FUNCTION__, gc->alu, + extents->x1, extents->y1, + extents->x2, extents->y2, + gc->patOrg.x, gc->patOrg.y)); + + get_drawable_deltas(drawable, pixmap, &dx, &dy); + { + unsigned px = (0 - gc->patOrg.x - dx) & 7; + unsigned py = (0 - gc->patOrg.y - dy) & 7; + DBG(("%s: pat offset (%d, %d)\n", __FUNCTION__ ,px, py)); + br00 = XY_SCANLINE_BLT | px << 12 | py << 8 | 3 << 20; + br13 = bo->pitch; + if (sna->kgem.gen >= 40 && bo->tiling) { + br00 |= BLT_DST_TILED; + br13 >>= 2; + } + br13 |= (gc->fillStyle == FillStippled) << 28; + br13 |= blt_depth(drawable->depth) << 24; + br13 |= fill_ROP[gc->alu] << 16; + } + + { + uint8_t *dst = (uint8_t *)pat; + const uint8_t *src = gc->stipple->devPrivate.ptr; + int stride = gc->stipple->devKind; + int j = gc->stipple->drawable.height; + do { + *dst++ = byte_reverse(*src); + src += stride; + } while (--j); + } + + kgem_set_mode(&sna->kgem, KGEM_BLT); + if (!kgem_check_batch(&sna->kgem, 9 + 2*3) || + !kgem_check_bo_fenced(&sna->kgem, bo) || + !kgem_check_reloc(&sna->kgem, 1)) { + _kgem_submit(&sna->kgem); + _kgem_set_mode(&sna->kgem, KGEM_BLT); + } + + if (!clipped) { + dx += drawable->x; + dy += drawable->y; + + sna_damage_add_rectangles(damage, r, n, dx, dy); + if (n == 1) { + DBG(("%s: single unclipped rect (%d, %d)x(%d, %d)\n", + __FUNCTION__, r->x + dx, r->y + dy, r->width, r->height)); + + b = sna->kgem.batch + sna->kgem.nbatch; + b[0] = XY_MONO_PAT | (br00 & (BLT_DST_TILED | 0x7<<12 | 0x7<<8)) | 3<<20; + b[1] = br13; + b[2] = (r->y + dy) << 16 | (r->x + dx); + b[3] = (r->y + r->height + dy) << 16 | (r->x + r->width + dx); + b[4] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 4, bo, + I915_GEM_DOMAIN_RENDER << 16 | + I915_GEM_DOMAIN_RENDER | + KGEM_RELOC_FENCED, + 0); + b[5] = gc->bgPixel; + b[6] = gc->fgPixel; + b[7] = pat[0]; + b[8] = pat[1]; + sna->kgem.nbatch += 9; + } else do { + int n_this_time; + + b = sna->kgem.batch + sna->kgem.nbatch; + b[0] = XY_SETUP_MONO_PATTERN_SL_BLT | 3 << 20; + b[1] = br13; + b[2] = 0; + b[3] = 0; + b[4] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 4, bo, + I915_GEM_DOMAIN_RENDER << 16 | + I915_GEM_DOMAIN_RENDER | + KGEM_RELOC_FENCED, + 0); + b[5] = gc->bgPixel; + b[6] = gc->fgPixel; + b[7] = pat[0]; + b[8] = pat[1]; + sna->kgem.nbatch += 9; + + n_this_time = n; + if (3*n_this_time > sna->kgem.surface - sna->kgem.nbatch - KGEM_BATCH_RESERVED) + n_this_time = (sna->kgem.surface - sna->kgem.nbatch - KGEM_BATCH_RESERVED) / 3; + assert(n_this_time); + n -= n_this_time; + + b = sna->kgem.batch + sna->kgem.nbatch; + sna->kgem.nbatch += 3 * n_this_time; + do { + DBG(("%s: rect (%d, %d)x(%d, %d)\n", + __FUNCTION__, r->x + dx, r->y + dy, r->width, r->height)); + assert(r->x + dx >= 0); + assert(r->y + dy >= 0); + assert(r->x + dx + r->width <= pixmap->drawable.width); + assert(r->y + dy + r->height <= pixmap->drawable.height); + + b[0] = br00; + b[1] = (r->y + dy) << 16 | (r->x + dx); + b[2] = (r->y + r->height + dy) << 16 | (r->x + r->width + dx); + + b += 3; r++; + } while(--n_this_time); + + if (!n) + break; + + _kgem_submit(&sna->kgem); + _kgem_set_mode(&sna->kgem, KGEM_BLT); + } while (1); + } else { + RegionRec clip; + + region_set(&clip, extents); + region_maybe_clip(&clip, gc->pCompositeClip); + if (!RegionNotEmpty(&clip)) + return true; + + b = sna->kgem.batch + sna->kgem.nbatch; + b[0] = XY_SETUP_MONO_PATTERN_SL_BLT | 3 << 20; + b[1] = br13; + b[2] = 0; + b[3] = 0; + b[4] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 4, bo, + I915_GEM_DOMAIN_RENDER << 16 | + I915_GEM_DOMAIN_RENDER | + KGEM_RELOC_FENCED, + 0); + b[5] = gc->bgPixel; + b[6] = gc->fgPixel; + b[7] = pat[0]; + b[8] = pat[1]; + sna->kgem.nbatch += 9; + + if (clip.data == NULL) { + do { + BoxRec box; + + box.x1 = r->x + drawable->x; + box.y1 = r->y + drawable->y; + box.x2 = bound(box.x1, r->width); + box.y2 = bound(box.y1, r->height); + r++; + + if (box_intersect(&box, &clip.extents)) { + if (!kgem_check_batch(&sna->kgem, 3)) { + _kgem_submit(&sna->kgem); + _kgem_set_mode(&sna->kgem, KGEM_BLT); + + b = sna->kgem.batch + sna->kgem.nbatch; + b[0] = XY_SETUP_MONO_PATTERN_SL_BLT | 3 << 20; + b[1] = br13; + b[2] = 0; + b[3] = 0; + b[4] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 4, bo, + I915_GEM_DOMAIN_RENDER << 16 | + I915_GEM_DOMAIN_RENDER | + KGEM_RELOC_FENCED, + 0); + b[5] = gc->bgPixel; + b[6] = gc->fgPixel; + b[7] = pat[0]; + b[8] = pat[1]; + sna->kgem.nbatch += 9; + } + + b = sna->kgem.batch + sna->kgem.nbatch; + sna->kgem.nbatch += 3; + b[0] = br00; + b[1] = (box.y1 + dy) << 16 | (box.x1 + dx); + b[2] = (box.y2 + dy) << 16 | (box.x2 + dx); + } + } while (--n); + } else { + const BoxRec * const clip_start = RegionBoxptr(&clip); + const BoxRec * const clip_end = clip_start + clip.data->numRects; + const BoxRec *c; + + do { + BoxRec box; + + box.x1 = r->x + drawable->x; + box.y1 = r->y + drawable->y; + box.x2 = bound(box.x1, r->width); + box.y2 = bound(box.y1, r->height); + r++; + + c = find_clip_box_for_y(clip_start, + clip_end, + box.y1); + while (c != clip_end) { + BoxRec bb; + if (box.y2 <= c->y1) + break; + + bb = box; + if (box_intersect(&bb, c++)) { + if (!kgem_check_batch(&sna->kgem, 3)) { + _kgem_submit(&sna->kgem); + _kgem_set_mode(&sna->kgem, KGEM_BLT); + + b = sna->kgem.batch + sna->kgem.nbatch; + b[0] = XY_SETUP_MONO_PATTERN_SL_BLT | 3 << 20; + b[1] = br13; + b[2] = 0; + b[3] = 0; + b[4] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 4, bo, + I915_GEM_DOMAIN_RENDER << 16 | + I915_GEM_DOMAIN_RENDER | + KGEM_RELOC_FENCED, + 0); + b[5] = gc->bgPixel; + b[6] = gc->fgPixel; + b[7] = pat[0]; + b[8] = pat[1]; + sna->kgem.nbatch += 9; + } + + b = sna->kgem.batch + sna->kgem.nbatch; + sna->kgem.nbatch += 3; + b[0] = br00; + b[1] = (bb.y1 + dy) << 16 | (bb.x1 + dx); + b[2] = (bb.y2 + dy) << 16 | (bb.x2 + dx); + } + } + } while (--n); + } + } + + assert_pixmap_damage(pixmap); + sna->blt_state.fill_bo = 0; + return true; +} + +static bool +sna_poly_fill_rect_stippled_nxm_blt(DrawablePtr drawable, + struct kgem_bo *bo, + struct sna_damage **damage, + GCPtr gc, int n, xRectangle *r, + const BoxRec *extents, unsigned clipped) +{ + PixmapPtr scratch, stipple; + uint8_t bytes[8], *dst = bytes; + const uint8_t *src, *end; + int j, stride; + bool ret; + + DBG(("%s: expanding %dx%d stipple to 8x8\n", + __FUNCTION__, + gc->stipple->drawable.width, + gc->stipple->drawable.height)); + + scratch = GetScratchPixmapHeader(drawable->pScreen, + 8, 8, 1, 1, 1, bytes); + if (scratch == NullPixmap) + return false; + + stipple = gc->stipple; + gc->stipple = scratch; + + stride = stipple->devKind; + src = stipple->devPrivate.ptr; + end = src + stride * stipple->drawable.height; + for(j = 0; j < 8; j++) { + switch (stipple->drawable.width) { + case 1: *dst = (*src & 1) * 0xff; break; + case 2: *dst = (*src & 3) * 0x55; break; + case 4: *dst = (*src & 15) * 0x11; break; + case 8: *dst = *src; break; + default: assert(0); break; + } + dst++; + src += stride; + if (src == end) + src = stipple->devPrivate.ptr; + } + + ret = sna_poly_fill_rect_stippled_8x8_blt(drawable, bo, damage, + gc, n, r, extents, clipped); + + gc->stipple = stipple; + FreeScratchPixmapHeader(scratch); + + return ret; +} + +static bool +sna_poly_fill_rect_stippled_1_blt(DrawablePtr drawable, + struct kgem_bo *bo, + struct sna_damage **damage, + GCPtr gc, int n, xRectangle *r, + const BoxRec *extents, unsigned clipped) +{ + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna *sna = to_sna_from_pixmap(pixmap); + PixmapPtr stipple = gc->stipple; + const DDXPointRec *origin = &gc->patOrg; + int16_t dx, dy; + uint32_t br00, br13; + + DBG(("%s: upload (%d, %d), (%d, %d), origin (%d, %d)\n", __FUNCTION__, + extents->x1, extents->y1, + extents->x2, extents->y2, + origin->x, origin->y)); + + get_drawable_deltas(drawable, pixmap, &dx, &dy); + kgem_set_mode(&sna->kgem, KGEM_BLT); + + br00 = 3 << 20; + br13 = bo->pitch; + if (sna->kgem.gen >= 40 && bo->tiling) { + br00 |= BLT_DST_TILED; + br13 >>= 2; + } + br13 |= (gc->fillStyle == FillStippled) << 29; + br13 |= blt_depth(drawable->depth) << 24; + br13 |= copy_ROP[gc->alu] << 16; + + if (!clipped) { + dx += drawable->x; + dy += drawable->y; + + sna_damage_add_rectangles(damage, r, n, dx, dy); + do { + int bx1 = (r->x - origin->x) & ~7; + int bx2 = (r->x + r->width - origin->x + 7) & ~7; + int bw = (bx2 - bx1)/8; + int bh = r->height; + int bstride = ALIGN(bw, 2); + int src_stride; + uint8_t *dst, *src; + uint32_t *b; + + DBG(("%s: rect (%d, %d)x(%d, %d) stipple [%d,%d]\n", + __FUNCTION__, + r->x, r->y, r->width, r->height, + bx1, bx2)); + + src_stride = bstride*bh; + if (src_stride <= 128) { + src_stride = ALIGN(src_stride, 8) / 4; + if (!kgem_check_batch(&sna->kgem, 7+src_stride) || + !kgem_check_bo_fenced(&sna->kgem, bo) || + !kgem_check_reloc(&sna->kgem, 1)) { + _kgem_submit(&sna->kgem); + _kgem_set_mode(&sna->kgem, KGEM_BLT); + } + + b = sna->kgem.batch + sna->kgem.nbatch; + b[0] = XY_MONO_SRC_COPY_IMM | (5 + src_stride) | br00; + b[0] |= ((r->x - origin->x) & 7) << 17; + b[1] = br13; + b[2] = (r->y + dy) << 16 | (r->x + dx); + b[3] = (r->y + r->height + dy) << 16 | (r->x + r->width + dx); + b[4] = kgem_add_reloc(&sna->kgem, + sna->kgem.nbatch + 4, bo, + I915_GEM_DOMAIN_RENDER << 16 | + I915_GEM_DOMAIN_RENDER | + KGEM_RELOC_FENCED, + 0); + b[5] = gc->bgPixel; + b[6] = gc->fgPixel; + + sna->kgem.nbatch += 7 + src_stride; + + dst = (uint8_t *)&b[7]; + src_stride = stipple->devKind; + src = stipple->devPrivate.ptr; + src += (r->y - origin->y) * src_stride + bx1/8; + src_stride -= bstride; + do { + int i = bstride; + do { + *dst++ = byte_reverse(*src++); + *dst++ = byte_reverse(*src++); + i -= 2; + } while (i); + src += src_stride; + } while (--bh); + } else { + struct kgem_bo *upload; + void *ptr; + + if (!kgem_check_batch(&sna->kgem, 8) || + !kgem_check_bo_fenced(&sna->kgem, bo) || + !kgem_check_reloc_and_exec(&sna->kgem, 2)) { + _kgem_submit(&sna->kgem); + _kgem_set_mode(&sna->kgem, KGEM_BLT); + } + + upload = kgem_create_buffer(&sna->kgem, + bstride*bh, + KGEM_BUFFER_WRITE_INPLACE, + &ptr); + if (!upload) + break; + + dst = ptr; + src_stride = stipple->devKind; + src = stipple->devPrivate.ptr; + src += (r->y - origin->y) * src_stride + bx1/8; + src_stride -= bstride; + do { + int i = bstride; + do { + *dst++ = byte_reverse(*src++); + *dst++ = byte_reverse(*src++); + i -= 2; + } while (i); + src += src_stride; + } while (--bh); + b = sna->kgem.batch + sna->kgem.nbatch; + b[0] = XY_MONO_SRC_COPY | br00; + b[0] |= ((r->x - origin->x) & 7) << 17; + b[1] = br13; + b[2] = (r->y + dy) << 16 | (r->x + dx); + b[3] = (r->y + r->height + dy) << 16 | (r->x + r->width + dx); + b[4] = kgem_add_reloc(&sna->kgem, + sna->kgem.nbatch + 4, bo, + I915_GEM_DOMAIN_RENDER << 16 | + I915_GEM_DOMAIN_RENDER | + KGEM_RELOC_FENCED, + 0); + b[5] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 5, + upload, + I915_GEM_DOMAIN_RENDER << 16 | + KGEM_RELOC_FENCED, + 0); + b[6] = gc->bgPixel; + b[7] = gc->fgPixel; + + sna->kgem.nbatch += 8; + kgem_bo_destroy(&sna->kgem, upload); + } + + r++; + } while (--n); + } else { + RegionRec clip; + DDXPointRec pat; + + region_set(&clip, extents); + region_maybe_clip(&clip, gc->pCompositeClip); + if (!RegionNotEmpty(&clip)) + return true; + + pat.x = origin->x + drawable->x; + pat.y = origin->y + drawable->y; + + if (clip.data == NULL) { + do { + BoxRec box; + int bx1, bx2, bw, bh, bstride; + int src_stride; + uint8_t *dst, *src; + uint32_t *b; + struct kgem_bo *upload; + void *ptr; + + box.x1 = r->x + drawable->x; + box.x2 = bound(r->x, r->width); + box.y1 = r->y + drawable->y; + box.y2 = bound(r->y, r->height); + r++; + + if (!box_intersect(&box, &clip.extents)) + continue; + + bx1 = (box.x1 - pat.x) & ~7; + bx2 = (box.x2 - pat.x + 7) & ~7; + bw = (bx2 - bx1)/8; + bh = box.y2 - box.y1; + bstride = ALIGN(bw, 2); + + DBG(("%s: rect (%d, %d)x(%d, %d), box (%d,%d),(%d,%d) stipple [%d,%d], pitch=%d, stride=%d\n", + __FUNCTION__, + r->x, r->y, r->width, r->height, + box.x1, box.y1, box.x2, box.y2, + bx1, bx2, bw, bstride)); + + src_stride = bstride*bh; + if (src_stride <= 128) { + src_stride = ALIGN(src_stride, 8) / 4; + if (!kgem_check_batch(&sna->kgem, 7+src_stride) || + !kgem_check_bo_fenced(&sna->kgem, bo) || + !kgem_check_reloc(&sna->kgem, 1)) { + _kgem_submit(&sna->kgem); + _kgem_set_mode(&sna->kgem, KGEM_BLT); + } + + b = sna->kgem.batch + sna->kgem.nbatch; + b[0] = XY_MONO_SRC_COPY_IMM | (5 + src_stride) | br00; + b[0] |= ((box.x1 - pat.x) & 7) << 17; + b[1] = br13; + b[2] = (box.y1 + dy) << 16 | (box.x1 + dx); + b[3] = (box.y2 + dy) << 16 | (box.x2 + dx); + b[4] = kgem_add_reloc(&sna->kgem, + sna->kgem.nbatch + 4, bo, + I915_GEM_DOMAIN_RENDER << 16 | + I915_GEM_DOMAIN_RENDER | + KGEM_RELOC_FENCED, + 0); + b[5] = gc->bgPixel; + b[6] = gc->fgPixel; + + sna->kgem.nbatch += 7 + src_stride; + + dst = (uint8_t *)&b[7]; + src_stride = stipple->devKind; + src = stipple->devPrivate.ptr; + src += (box.y1 - pat.y) * src_stride + bx1/8; + src_stride -= bstride; + do { + int i = bstride; + do { + *dst++ = byte_reverse(*src++); + *dst++ = byte_reverse(*src++); + i -= 2; + } while (i); + src += src_stride; + } while (--bh); + } else { + if (!kgem_check_batch(&sna->kgem, 8) || + !kgem_check_bo_fenced(&sna->kgem, bo) || + !kgem_check_reloc_and_exec(&sna->kgem, 2)) { + _kgem_submit(&sna->kgem); + _kgem_set_mode(&sna->kgem, KGEM_BLT); + } + + upload = kgem_create_buffer(&sna->kgem, + bstride*bh, + KGEM_BUFFER_WRITE_INPLACE, + &ptr); + if (!upload) + break; + + dst = ptr; + src_stride = stipple->devKind; + src = stipple->devPrivate.ptr; + src += (box.y1 - pat.y) * src_stride + bx1/8; + src_stride -= bstride; + do { + int i = bstride; + do { + *dst++ = byte_reverse(*src++); + *dst++ = byte_reverse(*src++); + i -= 2; + } while (i); + src += src_stride; + } while (--bh); + + b = sna->kgem.batch + sna->kgem.nbatch; + b[0] = XY_MONO_SRC_COPY | br00; + b[0] |= ((box.x1 - pat.x) & 7) << 17; + b[1] = br13; + b[2] = (box.y1 + dy) << 16 | (box.x1 + dx); + b[3] = (box.y2 + dy) << 16 | (box.x2 + dx); + b[4] = kgem_add_reloc(&sna->kgem, + sna->kgem.nbatch + 4, bo, + I915_GEM_DOMAIN_RENDER << 16 | + I915_GEM_DOMAIN_RENDER | + KGEM_RELOC_FENCED, + 0); + b[5] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 5, + upload, + I915_GEM_DOMAIN_RENDER << 16 | + KGEM_RELOC_FENCED, + 0); + b[6] = gc->bgPixel; + b[7] = gc->fgPixel; + + sna->kgem.nbatch += 8; + kgem_bo_destroy(&sna->kgem, upload); + } + } while (--n); + } else { + const BoxRec * const clip_start = RegionBoxptr(&clip); + const BoxRec * const clip_end = clip_start + clip.data->numRects; + const BoxRec *c; + + do { + BoxRec unclipped; + int bx1, bx2, bw, bh, bstride; + int src_stride; + uint8_t *dst, *src; + uint32_t *b; + struct kgem_bo *upload; + void *ptr; + + unclipped.x1 = r->x + drawable->x; + unclipped.x2 = bound(r->x, r->width); + unclipped.y1 = r->y + drawable->y; + unclipped.y2 = bound(r->y, r->height); + r++; + + c = find_clip_box_for_y(clip_start, + clip_end, + unclipped.y1); + while (c != clip_end) { + BoxRec box; + + if (unclipped.y2 <= c->y1) + break; + + box = unclipped; + if (!box_intersect(&box, c++)) + continue; + + bx1 = (box.x1 - pat.x) & ~7; + bx2 = (box.x2 - pat.x + 7) & ~7; + bw = (bx2 - bx1)/8; + bh = box.y2 - box.y1; + bstride = ALIGN(bw, 2); + + DBG(("%s: rect (%d, %d)x(%d, %d), box (%d,%d),(%d,%d) stipple [%d,%d]\n", + __FUNCTION__, + r->x, r->y, r->width, r->height, + box.x1, box.y1, box.x2, box.y2, + bx1, bx2)); + + src_stride = bstride*bh; + if (src_stride <= 128) { + src_stride = ALIGN(src_stride, 8) / 4; + if (!kgem_check_batch(&sna->kgem, 7+src_stride) || + !kgem_check_bo_fenced(&sna->kgem, bo) || + !kgem_check_reloc(&sna->kgem, 1)) { + _kgem_submit(&sna->kgem); + _kgem_set_mode(&sna->kgem, KGEM_BLT); + } + + b = sna->kgem.batch + sna->kgem.nbatch; + b[0] = XY_MONO_SRC_COPY_IMM | (5 + src_stride) | br00; + b[0] |= ((box.x1 - pat.x) & 7) << 17; + b[1] = br13; + b[2] = (box.y1 + dy) << 16 | (box.x1 + dx); + b[3] = (box.y2 + dy) << 16 | (box.x2 + dx); + b[4] = kgem_add_reloc(&sna->kgem, + sna->kgem.nbatch + 4, bo, + I915_GEM_DOMAIN_RENDER << 16 | + I915_GEM_DOMAIN_RENDER | + KGEM_RELOC_FENCED, + 0); + b[5] = gc->bgPixel; + b[6] = gc->fgPixel; + + sna->kgem.nbatch += 7 + src_stride; + + dst = (uint8_t *)&b[7]; + src_stride = stipple->devKind; + src = stipple->devPrivate.ptr; + src += (box.y1 - pat.y) * src_stride + bx1/8; + src_stride -= bstride; + do { + int i = bstride; + do { + *dst++ = byte_reverse(*src++); + *dst++ = byte_reverse(*src++); + i -= 2; + } while (i); + src += src_stride; + } while (--bh); + } else { + if (!kgem_check_batch(&sna->kgem, 8) || + !kgem_check_bo_fenced(&sna->kgem, bo) || + !kgem_check_reloc_and_exec(&sna->kgem, 2)) { + _kgem_submit(&sna->kgem); + _kgem_set_mode(&sna->kgem, KGEM_BLT); + } + + upload = kgem_create_buffer(&sna->kgem, + bstride*bh, + KGEM_BUFFER_WRITE_INPLACE, + &ptr); + if (!upload) + break; + + dst = ptr; + src_stride = stipple->devKind; + src = stipple->devPrivate.ptr; + src += (box.y1 - pat.y) * src_stride + bx1/8; + src_stride -= bstride; + do { + int i = bstride; + do { + *dst++ = byte_reverse(*src++); + *dst++ = byte_reverse(*src++); + i -= 2; + } while (i); + src += src_stride; + } while (--bh); + + b = sna->kgem.batch + sna->kgem.nbatch; + b[0] = XY_MONO_SRC_COPY | br00; + b[0] |= ((box.x1 - pat.x) & 7) << 17; + b[1] = br13; + b[2] = (box.y1 + dy) << 16 | (box.x1 + dx); + b[3] = (box.y2 + dy) << 16 | (box.x2 + dx); + b[4] = kgem_add_reloc(&sna->kgem, + sna->kgem.nbatch + 4, bo, + I915_GEM_DOMAIN_RENDER << 16 | + I915_GEM_DOMAIN_RENDER | + KGEM_RELOC_FENCED, + 0); + b[5] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 5, + upload, + I915_GEM_DOMAIN_RENDER << 16 | + KGEM_RELOC_FENCED, + 0); + b[6] = gc->bgPixel; + b[7] = gc->fgPixel; + + sna->kgem.nbatch += 8; + kgem_bo_destroy(&sna->kgem, upload); + } + } + } while (--n); + + } + } + + sna->blt_state.fill_bo = 0; + return true; +} + +static void +sna_poly_fill_rect_stippled_n_box__imm(struct sna *sna, + struct kgem_bo *bo, + uint32_t br00, uint32_t br13, + const GC *gc, + const BoxRec *box, + const DDXPointRec *origin) +{ + int x1, x2, y1, y2; + uint32_t *b; + + for (y1 = box->y1; y1 < box->y2; y1 = y2) { + int oy = (y1 - origin->y) % gc->stipple->drawable.height; + if (oy < 0) + oy += gc->stipple->drawable.height; + + y2 = box->y2; + if (y2 - y1 > gc->stipple->drawable.height - oy) + y2 = y1 + gc->stipple->drawable.height - oy; + + for (x1 = box->x1; x1 < box->x2; x1 = x2) { + int bx1, bx2, bw, bh, len, ox; + uint8_t *dst, *src; + + x2 = box->x2; + ox = (x1 - origin->x) % gc->stipple->drawable.width; + if (ox < 0) + ox += gc->stipple->drawable.width; + bx1 = ox & ~7; + bx2 = ox + (x2 - x1); + if (bx2 > gc->stipple->drawable.width) { + bx2 = gc->stipple->drawable.width; + x2 = x1 + bx2-ox; + } + bw = (bx2 - bx1 + 7)/8; + bw = ALIGN(bw, 2); + bh = y2 - y1; + + DBG(("%s: box((%d, %d)x(%d, %d)) origin=(%d, %d), pat=(%d, %d), up=(%d, %d), stipple=%dx%d\n", + __FUNCTION__, + x1, y1, x2-x1, y2-y1, + origin->x, origin->y, + ox, oy, bx1, bx2, + gc->stipple->drawable.width, + gc->stipple->drawable.height)); + + len = bw*bh; + len = ALIGN(len, 8) / 4; + if (!kgem_check_batch(&sna->kgem, 7+len) || + !kgem_check_bo_fenced(&sna->kgem, bo) || + !kgem_check_reloc(&sna->kgem, 1)) { + _kgem_submit(&sna->kgem); + _kgem_set_mode(&sna->kgem, KGEM_BLT); + } + + b = sna->kgem.batch + sna->kgem.nbatch; + b[0] = br00 | (5 + len) | (ox & 7) << 17; + b[1] = br13; + b[2] = y1 << 16 | x1; + b[3] = y2 << 16 | x2; + b[4] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 4, + bo, + I915_GEM_DOMAIN_RENDER << 16 | + I915_GEM_DOMAIN_RENDER | + KGEM_RELOC_FENCED, + 0); + b[5] = gc->bgPixel; + b[6] = gc->fgPixel; + + sna->kgem.nbatch += 7 + len; + + dst = (uint8_t *)&b[7]; + len = gc->stipple->devKind; + src = gc->stipple->devPrivate.ptr; + src += oy*len + ox/8; + len -= bw; + do { + int i = bw; + do { + *dst++ = byte_reverse(*src++); + *dst++ = byte_reverse(*src++); + i -= 2; + } while (i); + src += len; + } while (--bh); + } + } +} + +static void +sna_poly_fill_rect_stippled_n_box(struct sna *sna, + struct kgem_bo *bo, + struct kgem_bo **tile, + uint32_t br00, uint32_t br13, + const GC *gc, + const BoxRec *box, + const DDXPointRec *origin) +{ + int x1, x2, y1, y2; + int w = gc->stipple->drawable.width; + int h = gc->stipple->drawable.height; + int stride = gc->stipple->devKind; + uint32_t *b; + + if ((((box->y2-box->y1) | (box->x2-box->x1)) & ~31) == 0) { + br00 = XY_MONO_SRC_COPY_IMM |(br00 & (BLT_DST_TILED | 3 << 20)); + sna_poly_fill_rect_stippled_n_box__imm(sna, bo, + br00, br13, gc, + box, origin); + return; + } + + for (y1 = box->y1; y1 < box->y2; y1 = y2) { + int row, oy = (y1 - origin->y) % gc->stipple->drawable.height; + if (oy < 0) + oy += h; + + y2 = box->y2; + if (y2 - y1 > h - oy) + y2 = y1 + h - oy; + + row = oy * stride; + for (x1 = box->x1; x1 < box->x2; x1 = x2) { + int bx1, bx2, bw, bh, len, ox; + bool use_tile; + + x2 = box->x2; + ox = (x1 - origin->x) % w; + if (ox < 0) + ox += w; + bx1 = ox & ~7; + bx2 = ox + (x2 - x1); + if (bx2 > w) { + bx2 = w; + x2 = x1 + bx2-ox; + } + + use_tile = y2-y1 == h && x2-x1 == w; + + DBG(("%s: box((%d, %d)x(%d, %d)) origin=(%d, %d), pat=(%d, %d), up=(%d, %d), stipple=%dx%d, full tile?=%d\n", + __FUNCTION__, + x1, y1, x2-x1, y2-y1, + origin->x, origin->y, + ox, oy, bx1, bx2, w, h, + use_tile)); + + bw = (bx2 - bx1 + 7)/8; + bw = ALIGN(bw, 2); + bh = y2 - y1; + + len = bw*bh; + len = ALIGN(len, 8) / 4; + if (!kgem_check_batch(&sna->kgem, 7+len) || + !kgem_check_bo_fenced(&sna->kgem, bo) || + !kgem_check_reloc(&sna->kgem, 2)) { + _kgem_submit(&sna->kgem); + _kgem_set_mode(&sna->kgem, KGEM_BLT); + } + + b = sna->kgem.batch + sna->kgem.nbatch; + + if (!use_tile && len <= 128) { + uint8_t *dst, *src; + + b[0] = XY_MONO_SRC_COPY_IMM; + b[0] |= (br00 & (BLT_DST_TILED | 3 << 20)); + b[0] |= (ox & 7) << 17; + b[0] |= (5 + len); + b[1] = br13; + b[2] = y1 << 16 | x1; + b[3] = y2 << 16 | x2; + b[4] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 4, + bo, + I915_GEM_DOMAIN_RENDER << 16 | + I915_GEM_DOMAIN_RENDER | + KGEM_RELOC_FENCED, + 0); + b[5] = gc->bgPixel; + b[6] = gc->fgPixel; + + sna->kgem.nbatch += 7 + len; + + dst = (uint8_t *)&b[7]; + len = gc->stipple->devKind; + src = gc->stipple->devPrivate.ptr; + src += oy*len + ox/8; + len -= bw; + do { + int i = bw; + do { + *dst++ = byte_reverse(*src++); + *dst++ = byte_reverse(*src++); + i -= 2; + } while (i); + src += len; + } while (--bh); + } else { + bool has_tile = use_tile && *tile; + struct kgem_bo *upload; + uint8_t *dst, *src; + void *ptr; + + if (has_tile) { + upload = kgem_bo_reference(*tile); + } else { + upload = kgem_create_buffer(&sna->kgem, bw*bh, + KGEM_BUFFER_WRITE_INPLACE, + &ptr); + if (!upload) + return; + } + + b = sna->kgem.batch + sna->kgem.nbatch; + b[0] = br00 | (ox & 7) << 17; + b[1] = br13; + b[2] = y1 << 16 | x1; + b[3] = y2 << 16 | x2; + b[4] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 4, + bo, + I915_GEM_DOMAIN_RENDER << 16 | + I915_GEM_DOMAIN_RENDER | + KGEM_RELOC_FENCED, + 0); + b[5] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 5, + upload, + I915_GEM_DOMAIN_RENDER << 16 | + KGEM_RELOC_FENCED, + 0); + b[6] = gc->bgPixel; + b[7] = gc->fgPixel; + + sna->kgem.nbatch += 8; + + if (!has_tile) { + dst = ptr; + len = stride; + src = gc->stipple->devPrivate.ptr; + src += row + (ox >> 3); + len -= bw; + do { + int i = bw; + do { + *dst++ = byte_reverse(*src++); + *dst++ = byte_reverse(*src++); + i -= 2; + } while (i); + src += len; + } while (--bh); + if (use_tile) + *tile = kgem_bo_reference(upload); + } + + kgem_bo_destroy(&sna->kgem, upload); + } + } + } +} + +static bool +sna_poly_fill_rect_stippled_n_blt__imm(DrawablePtr drawable, + struct kgem_bo *bo, + struct sna_damage **damage, + GCPtr gc, int n, xRectangle *r, + const BoxRec *extents, unsigned clipped) +{ + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna *sna = to_sna_from_pixmap(pixmap); + DDXPointRec origin = gc->patOrg; + int16_t dx, dy; + uint32_t br00, br13; + + DBG(("%s: upload (%d, %d), (%d, %d), origin (%d, %d), clipped=%d, alu=%d, opaque=%d\n", __FUNCTION__, + extents->x1, extents->y1, + extents->x2, extents->y2, + origin.x, origin.y, + clipped, gc->alu, gc->fillStyle == FillOpaqueStippled)); + + get_drawable_deltas(drawable, pixmap, &dx, &dy); + kgem_set_mode(&sna->kgem, KGEM_BLT); + + br00 = XY_MONO_SRC_COPY_IMM | 3 << 20; + br13 = bo->pitch; + if (sna->kgem.gen >= 40 && bo->tiling) { + br00 |= BLT_DST_TILED; + br13 >>= 2; + } + br13 |= (gc->fillStyle == FillStippled) << 29; + br13 |= blt_depth(drawable->depth) << 24; + br13 |= copy_ROP[gc->alu] << 16; + + origin.x += dx + drawable->x; + origin.y += dy + drawable->y; + + if (!clipped) { + dx += drawable->x; + dy += drawable->y; + + sna_damage_add_rectangles(damage, r, n, dx, dy); + do { + BoxRec box; + + box.x1 = r->x + dx; + box.y1 = r->y + dy; + box.x2 = box.x1 + r->width; + box.y2 = box.y1 + r->height; + + sna_poly_fill_rect_stippled_n_box__imm(sna, bo, + br00, br13, gc, + &box, &origin); + r++; + } while (--n); + } else { + RegionRec clip; + + region_set(&clip, extents); + region_maybe_clip(&clip, gc->pCompositeClip); + if (!RegionNotEmpty(&clip)) { + DBG(("%s: all clipped\n", __FUNCTION__)); + return true; + } + + if (clip.data == NULL) { + DBG(("%s: clipped to extents ((%d, %d), (%d, %d))\n", + __FUNCTION__, + clip.extents.x1, clip.extents.y1, + clip.extents.x2, clip.extents.y2)); + do { + BoxRec box; + + box.x1 = r->x + drawable->x; + box.x2 = bound(box.x1, r->width); + box.y1 = r->y + drawable->y; + box.y2 = bound(box.y1, r->height); + r++; + + DBG(("%s: box (%d, %d), (%d, %d)\n", + __FUNCTION__, + box.x1, box.y1, box.x2, box.y2)); + if (!box_intersect(&box, &clip.extents)) + continue; + + box.x1 += dx; box.x2 += dx; + box.y1 += dy; box.y2 += dy; + + sna_poly_fill_rect_stippled_n_box__imm(sna, bo, + br00, br13, gc, + &box, &origin); + } while (--n); + } else { + const BoxRec * const clip_start = RegionBoxptr(&clip); + const BoxRec * const clip_end = clip_start + clip.data->numRects; + const BoxRec *c; + + DBG(("%s: clipped to boxes: start((%d, %d), (%d, %d)); end=((%d, %d), (%d, %d))\n", __FUNCTION__, + clip_start->x1, clip_start->y1, + clip_start->x2, clip_start->y2, + clip_end->x1, clip_end->y1, + clip_end->x2, clip_end->y2)); + do { + BoxRec unclipped; + + unclipped.x1 = r->x + drawable->x; + unclipped.x2 = bound(unclipped.x1, r->width); + unclipped.y1 = r->y + drawable->y; + unclipped.y2 = bound(unclipped.y1, r->height); + r++; + + c = find_clip_box_for_y(clip_start, + clip_end, + unclipped.y1); + while (c != clip_end) { + BoxRec box; + + if (unclipped.y2 <= c->y1) + break; + + box = unclipped; + if (!box_intersect(&box, c++)) + continue; + + box.x1 += dx; box.x2 += dx; + box.y1 += dy; box.y2 += dy; + + sna_poly_fill_rect_stippled_n_box__imm(sna, bo, + br00, br13, gc, + &box, &origin); + } + } while (--n); + } + } + + assert_pixmap_damage(pixmap); + sna->blt_state.fill_bo = 0; + return true; +} + +static bool +sna_poly_fill_rect_stippled_n_blt(DrawablePtr drawable, + struct kgem_bo *bo, + struct sna_damage **damage, + GCPtr gc, int n, xRectangle *r, + const BoxRec *extents, unsigned clipped) +{ + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna *sna = to_sna_from_pixmap(pixmap); + DDXPointRec origin = gc->patOrg; + struct kgem_bo *tile = NULL; + int16_t dx, dy; + uint32_t br00, br13; + + DBG(("%s: upload (%d, %d), (%d, %d), origin (%d, %d), clipped=%d, alu=%d, opaque=%d\n", __FUNCTION__, + extents->x1, extents->y1, + extents->x2, extents->y2, + origin.x, origin.y, + clipped, gc->alu, gc->fillStyle == FillOpaqueStippled)); + + if (((gc->stipple->drawable.width | gc->stipple->drawable.height) & ~31) == 0) + return sna_poly_fill_rect_stippled_n_blt__imm(drawable, + bo, damage, + gc, n, r, + extents, clipped); + + get_drawable_deltas(drawable, pixmap, &dx, &dy); + kgem_set_mode(&sna->kgem, KGEM_BLT); + + br00 = XY_MONO_SRC_COPY | 3 << 20; + br13 = bo->pitch; + if (sna->kgem.gen >= 40 && bo->tiling) { + br00 |= BLT_DST_TILED; + br13 >>= 2; + } + br13 |= (gc->fillStyle == FillStippled) << 29; + br13 |= blt_depth(drawable->depth) << 24; + br13 |= copy_ROP[gc->alu] << 16; + + origin.x += dx + drawable->x; + origin.y += dy + drawable->y; + + if (!clipped) { + dx += drawable->x; + dy += drawable->y; + + sna_damage_add_rectangles(damage, r, n, dx, dy); + do { + BoxRec box; + + box.x1 = r->x + dx; + box.y1 = r->y + dy; + box.x2 = box.x1 + r->width; + box.y2 = box.y1 + r->height; + + sna_poly_fill_rect_stippled_n_box(sna, bo, &tile, + br00, br13, gc, + &box, &origin); + r++; + } while (--n); + } else { + RegionRec clip; + + region_set(&clip, extents); + region_maybe_clip(&clip, gc->pCompositeClip); + if (!RegionNotEmpty(&clip)) { + DBG(("%s: all clipped\n", __FUNCTION__)); + return true; + } + + if (clip.data == NULL) { + DBG(("%s: clipped to extents ((%d, %d), (%d, %d))\n", + __FUNCTION__, + clip.extents.x1, clip.extents.y1, + clip.extents.x2, clip.extents.y2)); + do { + BoxRec box; + + box.x1 = r->x + drawable->x; + box.x2 = bound(box.x1, r->width); + box.y1 = r->y + drawable->y; + box.y2 = bound(box.y1, r->height); + r++; + + DBG(("%s: box (%d, %d), (%d, %d)\n", + __FUNCTION__, + box.x1, box.y1, box.x2, box.y2)); + if (!box_intersect(&box, &clip.extents)) + continue; + + box.x1 += dx; box.x2 += dx; + box.y1 += dy; box.y2 += dy; + + sna_poly_fill_rect_stippled_n_box(sna, bo, &tile, + br00, br13, gc, + &box, &origin); + } while (--n); + } else { + const BoxRec * const clip_start = RegionBoxptr(&clip); + const BoxRec * const clip_end = clip_start + clip.data->numRects; + const BoxRec *c; + + DBG(("%s: clipped to boxes: start((%d, %d), (%d, %d)); end=((%d, %d), (%d, %d))\n", __FUNCTION__, + clip_start->x1, clip_start->y1, + clip_start->x2, clip_start->y2, + clip_end->x1, clip_end->y1, + clip_end->x2, clip_end->y2)); + do { + BoxRec unclipped; + + unclipped.x1 = r->x + drawable->x; + unclipped.x2 = bound(unclipped.x1, r->width); + unclipped.y1 = r->y + drawable->y; + unclipped.y2 = bound(unclipped.y1, r->height); + r++; + + c = find_clip_box_for_y(clip_start, + clip_end, + unclipped.y1); + while (c != clip_end) { + BoxRec box; + + if (unclipped.y2 <= c->y1) + break; + + box = unclipped; + if (!box_intersect(&box, c++)) + continue; + + box.x1 += dx; box.x2 += dx; + box.y1 += dy; box.y2 += dy; + + sna_poly_fill_rect_stippled_n_box(sna, bo, &tile, + br00, br13, gc, + &box, &origin); + } + } while (--n); + } + } + + assert_pixmap_damage(pixmap); + if (tile) + kgem_bo_destroy(&sna->kgem, tile); + sna->blt_state.fill_bo = 0; + return true; +} + +static bool +sna_poly_fill_rect_stippled_blt(DrawablePtr drawable, + struct kgem_bo *bo, + struct sna_damage **damage, + GCPtr gc, int n, xRectangle *rect, + const BoxRec *extents, unsigned clipped) +{ + + PixmapPtr stipple = gc->stipple; + + if (bo->tiling == I915_TILING_Y) { + PixmapPtr pixmap = get_drawable_pixmap(drawable); + + DBG(("%s: converting bo from Y-tiling\n", __FUNCTION__)); + /* This is cheating, but only the gpu_bo can be tiled */ + assert(bo == sna_pixmap_get_bo(pixmap)); + bo = sna_pixmap_change_tiling(pixmap, I915_TILING_X); + if (bo == NULL) { + DBG(("%s: fallback -- unable to change tiling\n", + __FUNCTION__)); + return false; + } + } + + if (!sna_drawable_move_to_cpu(&stipple->drawable, MOVE_READ)) + return false; + + DBG(("%s: origin (%d, %d), extents (stipple): (%d, %d), stipple size %dx%d\n", + __FUNCTION__, gc->patOrg.x, gc->patOrg.y, + extents->x2 - gc->patOrg.x - drawable->x, + extents->y2 - gc->patOrg.y - drawable->y, + stipple->drawable.width, stipple->drawable.height)); + + if ((stipple->drawable.width | stipple->drawable.height) == 8) + return sna_poly_fill_rect_stippled_8x8_blt(drawable, bo, damage, + gc, n, rect, + extents, clipped); + + if ((stipple->drawable.width | stipple->drawable.height) <= 0xc && + is_power_of_two(stipple->drawable.width) && + is_power_of_two(stipple->drawable.height)) + return sna_poly_fill_rect_stippled_nxm_blt(drawable, bo, damage, + gc, n, rect, + extents, clipped); + + if (extents->x2 - gc->patOrg.x - drawable->x <= stipple->drawable.width && + extents->y2 - gc->patOrg.y - drawable->y <= stipple->drawable.height) { + if (stipple->drawable.width <= 8 && stipple->drawable.height <= 8) + return sna_poly_fill_rect_stippled_8x8_blt(drawable, bo, damage, + gc, n, rect, + extents, clipped); + else + return sna_poly_fill_rect_stippled_1_blt(drawable, bo, damage, + gc, n, rect, + extents, clipped); + } else { + return sna_poly_fill_rect_stippled_n_blt(drawable, bo, damage, + gc, n, rect, + extents, clipped); + } +} + +static unsigned +sna_poly_fill_rect_extents(DrawablePtr drawable, GCPtr gc, + int *_n, xRectangle **_r, + BoxPtr out) +{ + int n; + xRectangle *r; + Box32Rec box; + bool clipped; + + if (*_n == 0) + return 0; + + DBG(("%s: [0] = (%d, %d)x(%d, %d)\n", + __FUNCTION__, (*_r)->x, (*_r)->y, (*_r)->width, (*_r)->height)); + + /* Remove any zero-size rectangles from the array */ + while (*_n && ((*_r)->width == 0 || (*_r)->height == 0)) + --*_n, ++*_r; + + if (*_n == 0) + return 0; + + n = *_n; + r = *_r; + + box.x1 = r->x; + box.x2 = box.x1 + r->width; + box.y1 = r->y; + box.y2 = box.y1 + r->height; + r++; + + while (--n) { + if (r->width == 0 || r->height == 0) + goto slow; + + box32_add_rect(&box, r++); + } + goto done; +slow: + { + xRectangle *rr = r; + do { + do { + --*_n, r++; + } while (--n && (r->width == 0 || r->height == 0)); + while (n && r->width && r->height) { + box32_add_rect(&box, r); + *rr++ = *r++; + n--; + } + } while (n); + } +done: + + clipped = box32_trim_and_translate(&box, drawable, gc); + if (!box32_to_box16(&box, out)) + return 0; + + return 1 | clipped << 1; +} + +static void +sna_poly_fill_rect(DrawablePtr draw, GCPtr gc, int n, xRectangle *rect) +{ + PixmapPtr pixmap = get_drawable_pixmap(draw); + struct sna *sna = to_sna_from_pixmap(pixmap); + struct sna_pixmap *priv = sna_pixmap(pixmap); + struct sna_damage **damage; + struct kgem_bo *bo; + RegionRec region; + unsigned flags, hint; + uint32_t color; + + DBG(("%s(n=%d, PlaneMask: %lx (solid %d), solid fill: %d [style=%d, tileIsPixel=%d], alu=%d)\n", __FUNCTION__, + n, gc->planemask, !!PM_IS_SOLID(draw, gc->planemask), + (gc->fillStyle == FillSolid || + (gc->fillStyle == FillTiled && gc->tileIsPixel)), + gc->fillStyle, gc->tileIsPixel, + gc->alu)); + + flags = sna_poly_fill_rect_extents(draw, gc, &n, &rect, ®ion.extents); + if (flags == 0) { + DBG(("%s, nothing to do\n", __FUNCTION__)); + return; + } + + DBG(("%s: extents(%d, %d), (%d, %d), flags=%x\n", __FUNCTION__, + region.extents.x1, region.extents.y1, + region.extents.x2, region.extents.y2, + flags)); + + if (FORCE_FALLBACK || !ACCEL_POLY_FILL_RECT) { + DBG(("%s: fallback forced\n", __FUNCTION__)); + goto fallback; + } + + if (priv == NULL) { + DBG(("%s: fallback -- unattached\n", __FUNCTION__)); + goto fallback; + } + + if (wedged(sna)) { + DBG(("%s: fallback -- wedged\n", __FUNCTION__)); + goto fallback; + } + + if (!PM_IS_SOLID(draw, gc->planemask)) { + DBG(("%s: fallback -- planemask=%#lx (not-solid)\n", + __FUNCTION__, gc->planemask)); + goto fallback; + } + + /* Clear the cpu damage so that we refresh the GPU status of the + * pixmap upon a redraw after a period of inactivity. + */ + hint = PREFER_GPU; + if (n == 1 && gc->fillStyle != FillStippled && alu_overwrites(gc->alu)) { + region.data = NULL; + if (priv->cpu_damage && + region_is_singular(gc->pCompositeClip)) { + if (region_subsumes_damage(®ion, priv->cpu_damage)) { + DBG(("%s: discarding existing CPU damage\n", __FUNCTION__)); + if (priv->gpu_bo && priv->gpu_bo->proxy) { + kgem_bo_destroy(&sna->kgem, priv->gpu_bo); + priv->gpu_bo = NULL; + } + sna_damage_destroy(&priv->cpu_damage); + list_del(&priv->list); + } + hint |= IGNORE_CPU; + } + if (priv->cpu_damage == NULL && + (region_subsumes_drawable(®ion, &pixmap->drawable) || + box_inplace(pixmap, ®ion.extents))) { + DBG(("%s: promoting to full GPU\n", __FUNCTION__)); + if (priv->gpu_bo) { + sna_damage_all(&priv->gpu_damage, + pixmap->drawable.width, + pixmap->drawable.height); + priv->undamaged = false; + } + } + if (priv->cpu_damage == NULL) { + DBG(("%s: dropping last-cpu hint\n", __FUNCTION__)); + priv->cpu = false; + } + } + + /* If the source is already on the GPU, keep the operation on the GPU */ + if (gc->fillStyle == FillTiled) { + if (!gc->tileIsPixel && sna_pixmap_is_gpu(gc->tile.pixmap)) { + DBG(("%s: source is already on the gpu\n", __FUNCTION__)); + hint |= PREFER_GPU | FORCE_GPU; + } + } + + bo = sna_drawable_use_bo(draw, hint, ®ion.extents, &damage); + if (bo == NULL) { + DBG(("%s: not using GPU, hint=%x\n", __FUNCTION__, hint)); + goto fallback; + } + + if (gc_is_solid(gc, &color)) { + DBG(("%s: solid fill [%08x], testing for blt\n", + __FUNCTION__, color)); + + if (sna_poly_fill_rect_blt(draw, + bo, damage, + gc, color, n, rect, + ®ion.extents, flags & 2)) + return; + } else if (gc->fillStyle == FillTiled) { + DBG(("%s: tiled fill, testing for blt\n", __FUNCTION__)); + + if (sna_poly_fill_rect_tiled_blt(draw, bo, damage, + gc, n, rect, + ®ion.extents, flags & 2)) + return; + } else { + DBG(("%s: stippled fill, testing for blt\n", __FUNCTION__)); + + if (sna_poly_fill_rect_stippled_blt(draw, bo, damage, + gc, n, rect, + ®ion.extents, flags & 2)) + return; + } + +fallback: + DBG(("%s: fallback (%d, %d), (%d, %d)\n", __FUNCTION__, + region.extents.x1, region.extents.y1, + region.extents.x2, region.extents.y2)); + region.data = NULL; + region_maybe_clip(®ion, gc->pCompositeClip); + if (!RegionNotEmpty(®ion)) { + DBG(("%s: nothing to do, all clipped\n", __FUNCTION__)); + return; + } + + if (!sna_gc_move_to_cpu(gc, draw, ®ion)) + goto out; + if (!sna_drawable_move_region_to_cpu(draw, ®ion, + drawable_gc_flags(draw, gc, n > 1))) + goto out_gc; + + DBG(("%s: fallback - fbPolyFillRect\n", __FUNCTION__)); + fbPolyFillRect(draw, gc, n, rect); + FALLBACK_FLUSH(draw); +out_gc: + sna_gc_move_to_gpu(gc); +out: + RegionUninit(®ion); +} + +static void +sna_poly_fill_arc(DrawablePtr draw, GCPtr gc, int n, xArc *arc) +{ + struct sna_fill_spans data; + struct sna_pixmap *priv; + + DBG(("%s(n=%d, PlaneMask: %lx (solid %d), solid fill: %d [style=%d, tileIsPixel=%d], alu=%d)\n", __FUNCTION__, + n, gc->planemask, !!PM_IS_SOLID(draw, gc->planemask), + (gc->fillStyle == FillSolid || + (gc->fillStyle == FillTiled && gc->tileIsPixel)), + gc->fillStyle, gc->tileIsPixel, + gc->alu)); + + data.flags = sna_poly_arc_extents(draw, gc, n, arc, + &data.region.extents); + if (data.flags == 0) + return; + + DBG(("%s: extents(%d, %d), (%d, %d), flags=%x\n", __FUNCTION__, + data.region.extents.x1, data.region.extents.y1, + data.region.extents.x2, data.region.extents.y2, + data.flags)); + + data.region.data = NULL; + + if (FORCE_FALLBACK) + goto fallback; + + if (!ACCEL_POLY_FILL_ARC) + goto fallback; + + data.pixmap = get_drawable_pixmap(draw); + data.sna = to_sna_from_pixmap(data.pixmap); + priv = sna_pixmap(data.pixmap); + if (priv == NULL) { + DBG(("%s: fallback -- unattached\n", __FUNCTION__)); + goto fallback; + } + + if (wedged(data.sna)) { + DBG(("%s: fallback -- wedged\n", __FUNCTION__)); + goto fallback; + } + + if (!PM_IS_SOLID(draw, gc->planemask)) + goto fallback; + + if ((data.bo = sna_drawable_use_bo(draw, PREFER_GPU, + &data.region.extents, + &data.damage))) { + uint32_t color; + + get_drawable_deltas(draw, data.pixmap, &data.dx, &data.dy); + sna_gc(gc)->priv = &data; + + if (gc_is_solid(gc, &color)) { + struct sna_fill_op fill; + + if (!sna_fill_init_blt(&fill, + data.sna, data.pixmap, + data.bo, gc->alu, color)) + goto fallback; + + data.op = &fill; + + if ((data.flags & 2) == 0) { + if (data.dx | data.dy) + sna_gc_ops__tmp.FillSpans = sna_fill_spans__fill_offset; + else + sna_gc_ops__tmp.FillSpans = sna_fill_spans__fill; + } else { + region_maybe_clip(&data.region, + gc->pCompositeClip); + if (!RegionNotEmpty(&data.region)) + return; + + if (region_is_singular(&data.region)) + sna_gc_ops__tmp.FillSpans = sna_fill_spans__fill_clip_extents; + else + sna_gc_ops__tmp.FillSpans = sna_fill_spans__fill_clip_boxes; + } + assert(gc->miTranslate); + gc->ops = &sna_gc_ops__tmp; + + miPolyFillArc(draw, gc, n, arc); + fill.done(data.sna, &fill); + } else { + sna_gc_ops__tmp.FillSpans = sna_fill_spans__gpu; + gc->ops = &sna_gc_ops__tmp; + + miPolyFillArc(draw, gc, n, arc); + } + + gc->ops = (GCOps *)&sna_gc_ops; + if (data.damage) { + if (data.dx | data.dy) + pixman_region_translate(&data.region, data.dx, data.dy); + assert_pixmap_contains_box(data.pixmap, &data.region.extents); + sna_damage_add(data.damage, &data.region); + } + assert_pixmap_damage(data.pixmap); + RegionUninit(&data.region); + return; + } + +fallback: + DBG(("%s: fallback (%d, %d), (%d, %d)\n", __FUNCTION__, + data.region.extents.x1, data.region.extents.y1, + data.region.extents.x2, data.region.extents.y2)); + region_maybe_clip(&data.region, gc->pCompositeClip); + if (!RegionNotEmpty(&data.region)) { + DBG(("%s: nothing to do, all clipped\n", __FUNCTION__)); + return; + } + + if (!sna_gc_move_to_cpu(gc, draw, &data.region)) + goto out; + if (!sna_drawable_move_region_to_cpu(draw, &data.region, + drawable_gc_flags(draw, gc, true))) + goto out_gc; + + DBG(("%s: fallback -- miPolyFillArc -> sna_fill_spans__cpu\n", + __FUNCTION__)); + + miPolyFillArc(draw, gc, n, arc); +out_gc: + sna_gc_move_to_gpu(gc); +out: + RegionUninit(&data.region); +} + +struct sna_font { + CharInfoRec glyphs8[256]; + CharInfoRec *glyphs16[256]; +}; +#define GLYPH_INVALID (void *)1 +#define GLYPH_EMPTY (void *)2 +#define GLYPH_CLEAR (void *)3 + +static Bool +sna_realize_font(ScreenPtr screen, FontPtr font) +{ + struct sna_font *priv; + + DBG(("%s (key=%d)\n", __FUNCTION__, sna_font_key)); + + priv = calloc(1, sizeof(struct sna_font)); + if (priv == NULL) + return FALSE; + + if (!FontSetPrivate(font, sna_font_key, priv)) { + free(priv); + return FALSE; + } + + return TRUE; +} + +static Bool +sna_unrealize_font(ScreenPtr screen, FontPtr font) +{ + struct sna_font *priv = FontGetPrivate(font, sna_font_key); + int i, j; + + DBG(("%s (key=%d)\n", __FUNCTION__, sna_font_key)); + + if (priv == NULL) + return TRUE; + + for (i = 0; i < 256; i++) { + if ((uintptr_t)priv->glyphs8[i].bits & ~3) + free(priv->glyphs8[i].bits); + } + for (j = 0; j < 256; j++) { + if (priv->glyphs16[j] == NULL) + continue; + + for (i = 0; i < 256; i++) { + if ((uintptr_t)priv->glyphs16[j][i].bits & ~3) + free(priv->glyphs16[j][i].bits); + } + free(priv->glyphs16[j]); + } + free(priv); + + FontSetPrivate(font, sna_font_key, NULL); + return TRUE; +} + +static bool +sna_glyph_blt(DrawablePtr drawable, GCPtr gc, + int _x, int _y, unsigned int _n, + CharInfoPtr *_info, + RegionRec *clip, + uint32_t fg, uint32_t bg, + bool transparent) +{ + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna *sna = to_sna_from_pixmap(pixmap); + struct kgem_bo *bo; + struct sna_damage **damage; + const BoxRec *extents, *last_extents; + uint32_t *b; + int16_t dx, dy; + uint32_t br00; + + uint8_t rop = transparent ? copy_ROP[gc->alu] : ROP_S; + + DBG(("%s (%d, %d) x %d, fg=%08x, bg=%08x alu=%02x\n", + __FUNCTION__, _x, _y, _n, fg, bg, rop)); + + if (wedged(sna)) { + DBG(("%s: fallback -- wedged\n", __FUNCTION__)); + return false; + } + + bo = sna_drawable_use_bo(drawable, PREFER_GPU, &clip->extents, &damage); + if (bo == NULL) + return false; + + if (bo->tiling == I915_TILING_Y) { + DBG(("%s: converting bo from Y-tiling\n", __FUNCTION__)); + assert(bo == sna_pixmap_get_bo(pixmap)); + bo = sna_pixmap_change_tiling(pixmap, I915_TILING_X); + if (bo == NULL) { + DBG(("%s: fallback -- unable to change tiling\n", + __FUNCTION__)); + return false; + } + } + + get_drawable_deltas(drawable, pixmap, &dx, &dy); + _x += drawable->x + dx; + _y += drawable->y + dy; + + RegionTranslate(clip, dx, dy); + extents = REGION_RECTS(clip); + last_extents = extents + REGION_NUM_RECTS(clip); + + if (!transparent) /* emulate miImageGlyphBlt */ + sna_blt_fill_boxes(sna, GXcopy, + bo, drawable->bitsPerPixel, + bg, extents, REGION_NUM_RECTS(clip)); + + kgem_set_mode(&sna->kgem, KGEM_BLT); + if (!kgem_check_batch(&sna->kgem, 16) || + !kgem_check_bo_fenced(&sna->kgem, bo) || + !kgem_check_reloc(&sna->kgem, 1)) { + _kgem_submit(&sna->kgem); + _kgem_set_mode(&sna->kgem, KGEM_BLT); + } + + DBG(("%s: glyph clip box (%d, %d), (%d, %d)\n", + __FUNCTION__, + extents->x1, extents->y1, + extents->x2, extents->y2)); + + b = sna->kgem.batch + sna->kgem.nbatch; + b[0] = XY_SETUP_BLT | 3 << 20; + b[1] = bo->pitch; + if (sna->kgem.gen >= 40 && bo->tiling) { + b[0] |= BLT_DST_TILED; + b[1] >>= 2; + } + b[1] |= 1 << 30 | transparent << 29 | blt_depth(drawable->depth) << 24 | rop << 16; + b[2] = extents->y1 << 16 | extents->x1; + b[3] = extents->y2 << 16 | extents->x2; + b[4] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 4, bo, + I915_GEM_DOMAIN_RENDER << 16 | + I915_GEM_DOMAIN_RENDER | + KGEM_RELOC_FENCED, + 0); + b[5] = bg; + b[6] = fg; + b[7] = 0; + sna->kgem.nbatch += 8; + + br00 = XY_TEXT_IMMEDIATE_BLT; + if (bo->tiling && sna->kgem.gen >= 40) + br00 |= BLT_DST_TILED; + + do { + CharInfoPtr *info = _info; + int x = _x, y = _y, n = _n; + + do { + CharInfoPtr c = *info++; + int w = GLYPHWIDTHPIXELS(c); + int h = GLYPHHEIGHTPIXELS(c); + int w8 = (w + 7) >> 3; + int x1, y1, len; + + if (c->bits == GLYPH_EMPTY) + goto skip; + + if (!transparent && c->bits == GLYPH_CLEAR) + goto skip; + + len = (w8 * h + 7) >> 3 << 1; + x1 = x + c->metrics.leftSideBearing; + y1 = y - c->metrics.ascent; + + DBG(("%s glyph: (%d, %d) -> (%d, %d) x (%d[%d], %d), len=%d\n" ,__FUNCTION__, + x,y, x1, y1, w, w8, h, len)); + + if (x1 >= extents->x2 || y1 >= extents->y2) + goto skip; + if (x1 + w <= extents->x1 || y1 + h <= extents->y1) + goto skip; + + + if (!kgem_check_batch(&sna->kgem, 3+len)) { + _kgem_submit(&sna->kgem); + _kgem_set_mode(&sna->kgem, KGEM_BLT); + + DBG(("%s: new batch, glyph clip box (%d, %d), (%d, %d)\n", + __FUNCTION__, + extents->x1, extents->y1, + extents->x2, extents->y2)); + + b = sna->kgem.batch + sna->kgem.nbatch; + b[0] = XY_SETUP_BLT | 3 << 20; + b[1] = bo->pitch; + if (sna->kgem.gen >= 40 && bo->tiling) { + b[0] |= BLT_DST_TILED; + b[1] >>= 2; + } + b[1] |= 1 << 30 | transparent << 29 | blt_depth(drawable->depth) << 24 | rop << 16; + b[2] = extents->y1 << 16 | extents->x1; + b[3] = extents->y2 << 16 | extents->x2; + b[4] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 4, bo, + I915_GEM_DOMAIN_RENDER << 16 | + I915_GEM_DOMAIN_RENDER | + KGEM_RELOC_FENCED, + 0); + b[5] = bg; + b[6] = fg; + b[7] = 0; + sna->kgem.nbatch += 8; + } + + b = sna->kgem.batch + sna->kgem.nbatch; + sna->kgem.nbatch += 3 + len; + + b[0] = br00 | (1 + len); + b[1] = (uint16_t)y1 << 16 | (uint16_t)x1; + b[2] = (uint16_t)(y1+h) << 16 | (uint16_t)(x1+w); + if (c->bits == GLYPH_CLEAR) { + memset(b+3, 0, len*4); + } else { + uint64_t *src = (uint64_t *)c->bits; + uint64_t *dst = (uint64_t *)(b + 3); + do { + *dst++ = *src++; + len -= 2; + } while (len); + } + + if (damage) { + BoxRec r; + + r.x1 = x1; + r.y1 = y1; + r.x2 = x1 + w; + r.y2 = y1 + h; + if (box_intersect(&r, extents)) + sna_damage_add_box(damage, &r); + } +skip: + x += c->metrics.characterWidth; + } while (--n); + + if (++extents == last_extents) + break; + + if (kgem_check_batch(&sna->kgem, 3)) { + b = sna->kgem.batch + sna->kgem.nbatch; + sna->kgem.nbatch += 3; + + DBG(("%s: glyph clip box (%d, %d), (%d, %d)\n", + __FUNCTION__, + extents->x1, extents->y1, + extents->x2, extents->y2)); + + b[0] = XY_SETUP_CLIP; + b[1] = extents->y1 << 16 | extents->x1; + b[2] = extents->y2 << 16 | extents->x2; + } + } while (1); + + assert_pixmap_damage(pixmap); + sna->blt_state.fill_bo = 0; + return true; +} + +static void +sna_glyph_extents(FontPtr font, + CharInfoPtr *info, + unsigned long count, + ExtentInfoRec *extents) +{ + extents->drawDirection = font->info.drawDirection; + extents->fontAscent = font->info.fontAscent; + extents->fontDescent = font->info.fontDescent; + + extents->overallAscent = info[0]->metrics.ascent; + extents->overallDescent = info[0]->metrics.descent; + extents->overallLeft = info[0]->metrics.leftSideBearing; + extents->overallRight = info[0]->metrics.rightSideBearing; + extents->overallWidth = info[0]->metrics.characterWidth; + + while (--count) { + CharInfoPtr p =*++info; + int v; + + if (p->metrics.ascent > extents->overallAscent) + extents->overallAscent = p->metrics.ascent; + if (p->metrics.descent > extents->overallDescent) + extents->overallDescent = p->metrics.descent; + + v = extents->overallWidth + p->metrics.leftSideBearing; + if (v < extents->overallLeft) + extents->overallLeft = v; + + v = extents->overallWidth + p->metrics.rightSideBearing; + if (v > extents->overallRight) + extents->overallRight = v; + + extents->overallWidth += p->metrics.characterWidth; + } +} + +static bool sna_set_glyph(CharInfoPtr in, CharInfoPtr out) +{ + int w = GLYPHWIDTHPIXELS(in); + int h = GLYPHHEIGHTPIXELS(in); + int stride = GLYPHWIDTHBYTESPADDED(in); + uint8_t *dst, *src; + int clear = 1; + + out->metrics = in->metrics; + + /* Skip empty glyphs */ + if (w == 0 || h == 0 || ((w|h) == 1 && (in->bits[0] & 1) == 0)) { + out->bits = GLYPH_EMPTY; + return true; + } + + w = (w + 7) >> 3; + + out->bits = malloc((w*h + 7) & ~7); + if (out->bits == NULL) + return false; + + VG(memset(out->bits, 0, (w*h + 7) & ~7)); + src = (uint8_t *)in->bits; + dst = (uint8_t *)out->bits; + stride -= w; + do { + int i = w; + do { + clear &= *src == 0; + *dst++ = byte_reverse(*src++); + } while (--i); + src += stride; + } while (--h); + + if (clear) { + free(out->bits); + out->bits = GLYPH_CLEAR; + } + + return true; +} + +inline static bool sna_get_glyph8(FontPtr font, struct sna_font *priv, + uint8_t g, CharInfoPtr *out) +{ + unsigned long n; + CharInfoPtr p, ret; + + p = &priv->glyphs8[g]; + if (p->bits) { + *out = p; + return p->bits != GLYPH_INVALID; + } + + font->get_glyphs(font, 1, &g, Linear8Bit, &n, &ret); + if (n == 0) { + p->bits = GLYPH_INVALID; + return false; + } + + return sna_set_glyph(ret, *out = p); +} + +inline static bool sna_get_glyph16(FontPtr font, struct sna_font *priv, + uint16_t g, CharInfoPtr *out) +{ + unsigned long n; + CharInfoPtr page, p, ret; + + page = priv->glyphs16[g>>8]; + if (page == NULL) + page = priv->glyphs16[g>>8] = calloc(256, sizeof(CharInfoRec)); + + p = &page[g&0xff]; + if (p->bits) { + *out = p; + return p->bits != GLYPH_INVALID; + } + + font->get_glyphs(font, 1, (unsigned char *)&g, + FONTLASTROW(font) ? TwoD16Bit : Linear16Bit, + &n, &ret); + if (n == 0) { + p->bits = GLYPH_INVALID; + return false; + } + + return sna_set_glyph(ret, *out = p); +} + +static inline bool sna_font_too_large(FontPtr font) +{ + int top = max(FONTMAXBOUNDS(font, ascent), FONTASCENT(font)); + int bot = max(FONTMAXBOUNDS(font, descent), FONTDESCENT(font)); + int width = max(FONTMAXBOUNDS(font, characterWidth), -FONTMINBOUNDS(font, characterWidth)); + DBG(("%s? (%d + %d) x %d: %d > 124\n", __FUNCTION__, + top, bot, width, (top + bot) * (width + 7)/8)); + return (top + bot) * (width + 7)/8 > 124; +} + +static int +sna_poly_text8(DrawablePtr drawable, GCPtr gc, + int x, int y, + int count, char *chars) +{ + struct sna_font *priv = gc->font->devPrivates[sna_font_key]; + CharInfoPtr info[255]; + ExtentInfoRec extents; + RegionRec region; + long unsigned i, n; + uint32_t fg; + + for (i = n = 0; i < count; i++) { + if (sna_get_glyph8(gc->font, priv, chars[i], &info[n])) + n++; + } + if (n == 0) + return x; + + sna_glyph_extents(gc->font, info, n, &extents); + region.extents.x1 = x + extents.overallLeft; + region.extents.y1 = y - extents.overallAscent; + region.extents.x2 = x + extents.overallRight; + region.extents.y2 = y + extents.overallDescent; + + translate_box(®ion.extents, drawable); + clip_box(®ion.extents, gc); + if (box_empty(®ion.extents)) + return x + extents.overallRight; + + region.data = NULL; + region_maybe_clip(®ion, gc->pCompositeClip); + if (!RegionNotEmpty(®ion)) + return x + extents.overallRight; + + if (FORCE_FALLBACK) + goto fallback; + + if (!ACCEL_POLY_TEXT8) + goto fallback; + + if (sna_font_too_large(gc->font)) + goto fallback; + + if (!PM_IS_SOLID(drawable, gc->planemask)) + goto fallback; + + if (!gc_is_solid(gc, &fg)) + goto fallback; + + if (!sna_glyph_blt(drawable, gc, x, y, n, info, ®ion, fg, -1, true)) { +fallback: + DBG(("%s: fallback\n", __FUNCTION__)); + gc->font->get_glyphs(gc->font, count, (unsigned char *)chars, + Linear8Bit, &n, info); + + if (!sna_gc_move_to_cpu(gc, drawable, ®ion)) + goto out; + if (!sna_drawable_move_region_to_cpu(drawable, ®ion, + MOVE_READ | MOVE_WRITE)) + goto out_gc; + + DBG(("%s: fallback -- fbPolyGlyphBlt\n", __FUNCTION__)); + fbPolyGlyphBlt(drawable, gc, x, y, n, + info, FONTGLYPHS(gc->font)); + FALLBACK_FLUSH(drawable); +out_gc: + sna_gc_move_to_gpu(gc); + } +out: + RegionUninit(®ion); + return x + extents.overallRight; +} + +static int +sna_poly_text16(DrawablePtr drawable, GCPtr gc, + int x, int y, + int count, unsigned short *chars) +{ + struct sna_font *priv = gc->font->devPrivates[sna_font_key]; + CharInfoPtr info[255]; + ExtentInfoRec extents; + RegionRec region; + long unsigned i, n; + uint32_t fg; + + for (i = n = 0; i < count; i++) { + if (sna_get_glyph16(gc->font, priv, chars[i], &info[n])) + n++; + } + if (n == 0) + return x; + + sna_glyph_extents(gc->font, info, n, &extents); + region.extents.x1 = x + extents.overallLeft; + region.extents.y1 = y - extents.overallAscent; + region.extents.x2 = x + extents.overallRight; + region.extents.y2 = y + extents.overallDescent; + + translate_box(®ion.extents, drawable); + clip_box(®ion.extents, gc); + if (box_empty(®ion.extents)) + return x + extents.overallRight; + + region.data = NULL; + region_maybe_clip(®ion, gc->pCompositeClip); + if (!RegionNotEmpty(®ion)) + return x + extents.overallRight; + + if (FORCE_FALLBACK) + goto fallback; + + if (!ACCEL_POLY_TEXT16) + goto fallback; + + if (sna_font_too_large(gc->font)) + goto fallback; + + if (!PM_IS_SOLID(drawable, gc->planemask)) + goto fallback; + + if (!gc_is_solid(gc, &fg)) + goto fallback; + + if (!sna_glyph_blt(drawable, gc, x, y, n, info, ®ion, fg, -1, true)) { +fallback: + DBG(("%s: fallback\n", __FUNCTION__)); + gc->font->get_glyphs(gc->font, count, (unsigned char *)chars, + FONTLASTROW(gc->font) ? TwoD16Bit : Linear16Bit, + &n, info); + + if (!sna_gc_move_to_cpu(gc, drawable, ®ion)) + goto out; + if (!sna_drawable_move_region_to_cpu(drawable, ®ion, + MOVE_READ | MOVE_WRITE)) + goto out_gc; + + DBG(("%s: fallback -- fbPolyGlyphBlt\n", __FUNCTION__)); + fbPolyGlyphBlt(drawable, gc, x, y, n, + info, FONTGLYPHS(gc->font)); + FALLBACK_FLUSH(drawable); +out_gc: + sna_gc_move_to_gpu(gc); + } +out: + RegionUninit(®ion); + return x + extents.overallRight; +} + +static void +sna_image_text8(DrawablePtr drawable, GCPtr gc, + int x, int y, + int count, char *chars) +{ + struct sna_font *priv = gc->font->devPrivates[sna_font_key]; + CharInfoPtr info[255]; + ExtentInfoRec extents; + RegionRec region; + long unsigned i, n; + + for (i = n = 0; i < count; i++) { + if (sna_get_glyph8(gc->font, priv, chars[i], &info[n])) + n++; + } + if (n == 0) + return; + + sna_glyph_extents(gc->font, info, n, &extents); + region.extents.x1 = x + MIN(0, extents.overallLeft); + region.extents.y1 = y - extents.fontAscent; + region.extents.x2 = x + MAX(extents.overallWidth, extents.overallRight); + region.extents.y2 = y + extents.fontDescent; + + DBG(("%s: count=%ld/%d, extents=(left=%d, right=%d, width=%d, ascent=%d, descent=%d), box=(%d, %d), (%d, %d)\n", + __FUNCTION__, n, count, + extents.overallLeft, extents.overallRight, extents.overallWidth, + extents.fontAscent, extents.fontDescent, + region.extents.x1, region.extents.y1, + region.extents.x2, region.extents.y2)); + + translate_box(®ion.extents, drawable); + clip_box(®ion.extents, gc); + if (box_empty(®ion.extents)) + return; + + region.data = NULL; + region_maybe_clip(®ion, gc->pCompositeClip); + if (!RegionNotEmpty(®ion)) + return; + + DBG(("%s: clipped extents (%d, %d), (%d, %d)\n", + __FUNCTION__, + region.extents.x1, region.extents.y1, + region.extents.x2, region.extents.y2)); + + if (FORCE_FALLBACK) + goto fallback; + + if (!ACCEL_IMAGE_TEXT8) + goto fallback; + + if (sna_font_too_large(gc->font)) + goto fallback; + + if (!PM_IS_SOLID(drawable, gc->planemask)) + goto fallback; + + if (!sna_glyph_blt(drawable, gc, x, y, n, info, ®ion, + gc->fgPixel, gc->bgPixel, false)) { +fallback: + DBG(("%s: fallback\n", __FUNCTION__)); + gc->font->get_glyphs(gc->font, count, (unsigned char *)chars, + Linear8Bit, &n, info); + + if (!sna_gc_move_to_cpu(gc, drawable, ®ion)) + goto out; + if (!sna_drawable_move_region_to_cpu(drawable, ®ion, + MOVE_READ | MOVE_WRITE)) + goto out_gc; + + DBG(("%s: fallback -- fbImageGlyphBlt\n", __FUNCTION__)); + fbImageGlyphBlt(drawable, gc, x, y, n, + info, FONTGLYPHS(gc->font)); + FALLBACK_FLUSH(drawable); +out_gc: + sna_gc_move_to_gpu(gc); + } +out: + RegionUninit(®ion); +} + +static void +sna_image_text16(DrawablePtr drawable, GCPtr gc, + int x, int y, + int count, unsigned short *chars) +{ + struct sna_font *priv = gc->font->devPrivates[sna_font_key]; + CharInfoPtr info[255]; + ExtentInfoRec extents; + RegionRec region; + long unsigned i, n; + + for (i = n = 0; i < count; i++) { + if (sna_get_glyph16(gc->font, priv, chars[i], &info[n])) + n++; + } + if (n == 0) + return; + + sna_glyph_extents(gc->font, info, n, &extents); + region.extents.x1 = x + MIN(0, extents.overallLeft); + region.extents.y1 = y - extents.fontAscent; + region.extents.x2 = x + MAX(extents.overallWidth, extents.overallRight); + region.extents.y2 = y + extents.fontDescent; + + DBG(("%s: count=%ld/%d, extents=(left=%d, right=%d, width=%d, ascent=%d, descent=%d), box=(%d, %d), (%d, %d)\n", + __FUNCTION__, n, count, + extents.overallLeft, extents.overallRight, extents.overallWidth, + extents.fontAscent, extents.fontDescent, + region.extents.x1, region.extents.y1, + region.extents.x2, region.extents.y2)); + + translate_box(®ion.extents, drawable); + clip_box(®ion.extents, gc); + if (box_empty(®ion.extents)) + return; + + region.data = NULL; + region_maybe_clip(®ion, gc->pCompositeClip); + if (!RegionNotEmpty(®ion)) + return; + + DBG(("%s: clipped extents (%d, %d), (%d, %d)\n", + __FUNCTION__, + region.extents.x1, region.extents.y1, + region.extents.x2, region.extents.y2)); + + if (FORCE_FALLBACK) + goto fallback; + + if (!ACCEL_IMAGE_TEXT16) + goto fallback; + + if (sna_font_too_large(gc->font)) + goto fallback; + + if (!PM_IS_SOLID(drawable, gc->planemask)) + goto fallback; + + if (!sna_glyph_blt(drawable, gc, x, y, n, info, ®ion, + gc->fgPixel, gc->bgPixel, false)) { +fallback: + DBG(("%s: fallback\n", __FUNCTION__)); + gc->font->get_glyphs(gc->font, count, (unsigned char *)chars, + FONTLASTROW(gc->font) ? TwoD16Bit : Linear16Bit, + &n, info); + + if (!sna_gc_move_to_cpu(gc, drawable, ®ion)) + goto out; + if (!sna_drawable_move_region_to_cpu(drawable, ®ion, + MOVE_READ | MOVE_WRITE)) + goto out_gc; + + DBG(("%s: fallback -- fbImageGlyphBlt\n", __FUNCTION__)); + fbImageGlyphBlt(drawable, gc, x, y, n, + info, FONTGLYPHS(gc->font)); + FALLBACK_FLUSH(drawable); +out_gc: + sna_gc_move_to_gpu(gc); + } +out: + RegionUninit(®ion); +} + +/* XXX Damage bypasses the Text interface and so we lose our custom gluphs */ +static bool +sna_reversed_glyph_blt(DrawablePtr drawable, GCPtr gc, + int _x, int _y, unsigned int _n, + CharInfoPtr *_info, pointer _base, + struct kgem_bo *bo, + struct sna_damage **damage, + RegionPtr clip, + uint32_t fg, uint32_t bg, + bool transparent) +{ + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna *sna = to_sna_from_pixmap(pixmap); + const BoxRec *extents, *last_extents; + uint32_t *b; + int16_t dx, dy; + uint8_t rop = transparent ? copy_ROP[gc->alu] : ROP_S; + + if (bo->tiling == I915_TILING_Y) { + DBG(("%s: converting bo from Y-tiling\n", __FUNCTION__)); + assert(bo == sna_pixmap_get_bo(pixmap)); + bo = sna_pixmap_change_tiling(pixmap, I915_TILING_X); + if (bo == NULL) { + DBG(("%s: fallback -- unable to change tiling\n", + __FUNCTION__)); + return false; + } + } + + get_drawable_deltas(drawable, pixmap, &dx, &dy); + _x += drawable->x + dx; + _y += drawable->y + dy; + + RegionTranslate(clip, dx, dy); + extents = REGION_RECTS(clip); + last_extents = extents + REGION_NUM_RECTS(clip); + + if (!transparent) /* emulate miImageGlyphBlt */ + sna_blt_fill_boxes(sna, GXcopy, + bo, drawable->bitsPerPixel, + bg, extents, REGION_NUM_RECTS(clip)); + + kgem_set_mode(&sna->kgem, KGEM_BLT); + if (!kgem_check_batch(&sna->kgem, 16) || + !kgem_check_bo_fenced(&sna->kgem, bo) || + !kgem_check_reloc(&sna->kgem, 1)) { + _kgem_submit(&sna->kgem); + _kgem_set_mode(&sna->kgem, KGEM_BLT); + } + + DBG(("%s: glyph clip box (%d, %d), (%d, %d)\n", + __FUNCTION__, + extents->x1, extents->y1, + extents->x2, extents->y2)); + b = sna->kgem.batch + sna->kgem.nbatch; + b[0] = XY_SETUP_BLT | 1 << 20; + b[1] = bo->pitch; + if (sna->kgem.gen >= 40 && bo->tiling) { + b[0] |= BLT_DST_TILED; + b[1] >>= 2; + } + b[1] |= 1 << 30 | transparent << 29 | blt_depth(drawable->depth) << 24 | rop << 16; + b[2] = extents->y1 << 16 | extents->x1; + b[3] = extents->y2 << 16 | extents->x2; + b[4] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 4, bo, + I915_GEM_DOMAIN_RENDER << 16 | + I915_GEM_DOMAIN_RENDER | + KGEM_RELOC_FENCED, + 0); + b[5] = bg; + b[6] = fg; + b[7] = 0; + sna->kgem.nbatch += 8; + + do { + CharInfoPtr *info = _info; + int x = _x, y = _y, n = _n; + + do { + CharInfoPtr c = *info++; + uint8_t *glyph = FONTGLYPHBITS(base, c); + int w = GLYPHWIDTHPIXELS(c); + int h = GLYPHHEIGHTPIXELS(c); + int stride = GLYPHWIDTHBYTESPADDED(c); + int w8 = (w + 7) >> 3; + int x1, y1, len, i; + uint8_t *byte; + + if (w == 0 || h == 0) + goto skip; + + len = (w8 * h + 7) >> 3 << 1; + x1 = x + c->metrics.leftSideBearing; + y1 = y - c->metrics.ascent; + + DBG(("%s glyph: (%d, %d) -> (%d, %d) x (%d[%d], %d), len=%d\n" ,__FUNCTION__, + x,y, x1, y1, w, w8, h, len)); + + if (x1 >= extents->x2 || y1 >= extents->y2 || + x1 + w <= extents->x1 || y1 + h <= extents->y1) { + DBG(("%s: glyph is clipped (%d, %d)x(%d,%d) against extents (%d, %d), (%d, %d)\n", + __FUNCTION__, + x1, y1, w, h, + extents->x1, extents->y1, + extents->x2, extents->y2)); + goto skip; + } + + if (!transparent) { + int clear = 1, j = h; + uint8_t *g = glyph; + + do { + i = w8; + do { + clear = *g++ == 0; + } while (clear && --i); + g += stride - w8; + } while (clear && --j); + if (clear) { + DBG(("%s: skipping clear glyph for ImageGlyph\n", + __FUNCTION__)); + goto skip; + } + } + + if (!kgem_check_batch(&sna->kgem, 3+len)) { + _kgem_submit(&sna->kgem); + _kgem_set_mode(&sna->kgem, KGEM_BLT); + + DBG(("%s: new batch, glyph clip box (%d, %d), (%d, %d)\n", + __FUNCTION__, + extents->x1, extents->y1, + extents->x2, extents->y2)); + + b = sna->kgem.batch + sna->kgem.nbatch; + b[0] = XY_SETUP_BLT | 1 << 20; + b[1] = bo->pitch; + if (sna->kgem.gen >= 40 && bo->tiling) { + b[0] |= BLT_DST_TILED; + b[1] >>= 2; + } + b[1] |= 1 << 30 | transparent << 29 | blt_depth(drawable->depth) << 24 | rop << 16; + b[2] = extents->y1 << 16 | extents->x1; + b[3] = extents->y2 << 16 | extents->x2; + b[4] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 4, + bo, + I915_GEM_DOMAIN_RENDER << 16 | + I915_GEM_DOMAIN_RENDER | + KGEM_RELOC_FENCED, + 0); + b[5] = bg; + b[6] = fg; + b[7] = 0; + sna->kgem.nbatch += 8; + } + + b = sna->kgem.batch + sna->kgem.nbatch; + sna->kgem.nbatch += 3 + len; + + b[0] = XY_TEXT_IMMEDIATE_BLT | (1 + len); + if (bo->tiling && sna->kgem.gen >= 40) + b[0] |= BLT_DST_TILED; + b[1] = (uint16_t)y1 << 16 | (uint16_t)x1; + b[2] = (uint16_t)(y1+h) << 16 | (uint16_t)(x1+w); + + byte = (uint8_t *)&b[3]; + stride -= w8; + do { + i = w8; + do { + *byte++ = byte_reverse(*glyph++); + } while (--i); + glyph += stride; + } while (--h); + while ((byte - (uint8_t *)&b[3]) & 7) + *byte++ = 0; + assert((uint32_t *)byte == sna->kgem.batch + sna->kgem.nbatch); + + if (damage) { + BoxRec r; + + r.x1 = x1; + r.y1 = y1; + r.x2 = x1 + w; + r.y2 = y1 + h; + if (box_intersect(&r, extents)) + sna_damage_add_box(damage, &r); + } +skip: + x += c->metrics.characterWidth; + } while (--n); + + if (++extents == last_extents) + break; + + if (kgem_check_batch(&sna->kgem, 3 + 5)) { + b = sna->kgem.batch + sna->kgem.nbatch; + sna->kgem.nbatch += 3; + + DBG(("%s: glyph clip box (%d, %d), (%d, %d)\n", + __FUNCTION__, + extents->x1, extents->y1, + extents->x2, extents->y2)); + + b[0] = XY_SETUP_CLIP; + b[1] = extents->y1 << 16 | extents->x1; + b[2] = extents->y2 << 16 | extents->x2; + } + } while (1); + + assert_pixmap_damage(pixmap); + sna->blt_state.fill_bo = 0; + return true; +} + +static void +sna_image_glyph(DrawablePtr drawable, GCPtr gc, + int x, int y, unsigned int n, + CharInfoPtr *info, pointer base) +{ + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna *sna = to_sna_from_pixmap(pixmap); + ExtentInfoRec extents; + RegionRec region; + struct sna_damage **damage; + struct kgem_bo *bo; + + if (n == 0) + return; + + sna_glyph_extents(gc->font, info, n, &extents); + region.extents.x1 = x + MIN(0, extents.overallLeft); + region.extents.y1 = y - extents.fontAscent; + region.extents.x2 = x + MAX(extents.overallWidth, extents.overallRight); + region.extents.y2 = y + extents.fontDescent; + + DBG(("%s: count=%d, extents=(left=%d, right=%d, width=%d, ascent=%d, descent=%d), box=(%d, %d), (%d, %d)\n", + __FUNCTION__, n, + extents.overallLeft, extents.overallRight, extents.overallWidth, + extents.fontAscent, extents.fontDescent, + region.extents.x1, region.extents.y1, + region.extents.x2, region.extents.y2)); + + translate_box(®ion.extents, drawable); + clip_box(®ion.extents, gc); + if (box_empty(®ion.extents)) + return; + + DBG(("%s: extents(%d, %d), (%d, %d)\n", __FUNCTION__, + region.extents.x1, region.extents.y1, + region.extents.x2, region.extents.y2)); + + region.data = NULL; + region_maybe_clip(®ion, gc->pCompositeClip); + if (!RegionNotEmpty(®ion)) + return; + + if (FORCE_FALLBACK) + goto fallback; + + if (!ACCEL_IMAGE_GLYPH) + goto fallback; + + if (wedged(sna)) { + DBG(("%s: fallback -- wedged\n", __FUNCTION__)); + goto fallback; + } + + if (!PM_IS_SOLID(drawable, gc->planemask)) + goto fallback; + + if (sna_font_too_large(gc->font)) + goto fallback; + + if ((bo = sna_drawable_use_bo(drawable, PREFER_GPU, + ®ion.extents, &damage)) && + sna_reversed_glyph_blt(drawable, gc, x, y, n, info, base, + bo, damage, ®ion, + gc->fgPixel, gc->bgPixel, false)) + goto out; + +fallback: + DBG(("%s: fallback\n", __FUNCTION__)); + if (!sna_gc_move_to_cpu(gc, drawable, ®ion)) + goto out; + if (!sna_drawable_move_region_to_cpu(drawable, ®ion, + MOVE_READ | MOVE_WRITE)) + goto out_gc; + + DBG(("%s: fallback -- fbImageGlyphBlt\n", __FUNCTION__)); + fbImageGlyphBlt(drawable, gc, x, y, n, info, base); + FALLBACK_FLUSH(drawable); + +out_gc: + sna_gc_move_to_gpu(gc); +out: + RegionUninit(®ion); +} + +static void +sna_poly_glyph(DrawablePtr drawable, GCPtr gc, + int x, int y, unsigned int n, + CharInfoPtr *info, pointer base) +{ + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna *sna = to_sna_from_pixmap(pixmap); + ExtentInfoRec extents; + RegionRec region; + struct sna_damage **damage; + struct kgem_bo *bo; + uint32_t fg; + + if (n == 0) + return; + + sna_glyph_extents(gc->font, info, n, &extents); + region.extents.x1 = x + extents.overallLeft; + region.extents.y1 = y - extents.overallAscent; + region.extents.x2 = x + extents.overallRight; + region.extents.y2 = y + extents.overallDescent; + + translate_box(®ion.extents, drawable); + clip_box(®ion.extents, gc); + if (box_empty(®ion.extents)) + return; + + DBG(("%s: extents(%d, %d), (%d, %d)\n", __FUNCTION__, + region.extents.x1, region.extents.y1, + region.extents.x2, region.extents.y2)); + + region.data = NULL; + region_maybe_clip(®ion, gc->pCompositeClip); + if (!RegionNotEmpty(®ion)) + return; + + if (FORCE_FALLBACK) + goto fallback; + + if (!ACCEL_POLY_GLYPH) + goto fallback; + + if (wedged(sna)) { + DBG(("%s: fallback -- wedged\n", __FUNCTION__)); + goto fallback; + } + + if (!PM_IS_SOLID(drawable, gc->planemask)) + goto fallback; + + if (!gc_is_solid(gc, &fg)) + goto fallback; + + if (sna_font_too_large(gc->font)) + goto fallback; + + if ((bo = sna_drawable_use_bo(drawable, PREFER_GPU, + ®ion.extents, &damage)) && + sna_reversed_glyph_blt(drawable, gc, x, y, n, info, base, + bo, damage, ®ion, fg, -1, true)) + goto out; + +fallback: + DBG(("%s: fallback\n", __FUNCTION__)); + if (!sna_gc_move_to_cpu(gc, drawable, ®ion)) + goto out; + if (!sna_drawable_move_region_to_cpu(drawable, ®ion, + MOVE_READ | MOVE_WRITE)) + goto out_gc; + + DBG(("%s: fallback -- fbPolyGlyphBlt\n", __FUNCTION__)); + fbPolyGlyphBlt(drawable, gc, x, y, n, info, base); + FALLBACK_FLUSH(drawable); + +out_gc: + sna_gc_move_to_gpu(gc); +out: + RegionUninit(®ion); +} + +static bool +sna_push_pixels_solid_blt(GCPtr gc, + PixmapPtr bitmap, + DrawablePtr drawable, + RegionPtr region) +{ + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna *sna = to_sna_from_pixmap(pixmap); + struct sna_damage **damage; + struct kgem_bo *bo; + BoxRec *box; + int16_t dx, dy; + int n; + uint8_t rop = copy_ROP[gc->alu]; + + bo = sna_drawable_use_bo(drawable, PREFER_GPU, ®ion->extents, &damage); + if (bo == NULL) + return false; + + if (bo->tiling == I915_TILING_Y) { + DBG(("%s: converting bo from Y-tiling\n", __FUNCTION__)); + assert(bo == sna_pixmap_get_bo(pixmap)); + bo = sna_pixmap_change_tiling(pixmap, I915_TILING_X); + if (bo == NULL) { + DBG(("%s: fallback -- unable to change tiling\n", + __FUNCTION__)); + return false; + } + } + + get_drawable_deltas(drawable, pixmap, &dx, &dy); + RegionTranslate(region, dx, dy); + + assert_pixmap_contains_box(pixmap, RegionExtents(region)); + if (damage) + sna_damage_add(damage, region); + assert_pixmap_damage(pixmap); + + DBG(("%s: upload(%d, %d, %d, %d)\n", __FUNCTION__, + region->extents.x1, region->extents.y1, + region->extents.x2, region->extents.y2)); + + kgem_set_mode(&sna->kgem, KGEM_BLT); + + /* Region is pre-clipped and translated into pixmap space */ + box = REGION_RECTS(region); + n = REGION_NUM_RECTS(region); + do { + int bx1 = (box->x1 - region->extents.x1) & ~7; + int bx2 = (box->x2 - region->extents.x1 + 7) & ~7; + int bw = (bx2 - bx1)/8; + int bh = box->y2 - box->y1; + int bstride = ALIGN(bw, 2); + int src_stride; + uint8_t *dst, *src; + uint32_t *b; + struct kgem_bo *upload; + void *ptr; + + if (!kgem_check_batch(&sna->kgem, 8) || + !kgem_check_bo_fenced(&sna->kgem, bo) || + !kgem_check_reloc_and_exec(&sna->kgem, 2)) { + _kgem_submit(&sna->kgem); + _kgem_set_mode(&sna->kgem, KGEM_BLT); + } + + upload = kgem_create_buffer(&sna->kgem, + bstride*bh, + KGEM_BUFFER_WRITE_INPLACE, + &ptr); + if (!upload) + break; + + dst = ptr; + + src_stride = bitmap->devKind; + src = (uint8_t*)bitmap->devPrivate.ptr; + src += (box->y1 - region->extents.y1) * src_stride + bx1/8; + src_stride -= bstride; + do { + int i = bstride; + do { + *dst++ = byte_reverse(*src++); + *dst++ = byte_reverse(*src++); + i -= 2; + } while (i); + src += src_stride; + } while (--bh); + + b = sna->kgem.batch + sna->kgem.nbatch; + b[0] = XY_MONO_SRC_COPY | 3 << 20; + b[0] |= ((box->x1 - region->extents.x1) & 7) << 17; + b[1] = bo->pitch; + if (sna->kgem.gen >= 40 && bo->tiling) { + b[0] |= BLT_DST_TILED; + b[1] >>= 2; + } + b[1] |= 1 << 29; + b[1] |= blt_depth(drawable->depth) << 24; + b[1] |= rop << 16; + b[2] = box->y1 << 16 | box->x1; + b[3] = box->y2 << 16 | box->x2; + b[4] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 4, bo, + I915_GEM_DOMAIN_RENDER << 16 | + I915_GEM_DOMAIN_RENDER | + KGEM_RELOC_FENCED, + 0); + b[5] = kgem_add_reloc(&sna->kgem, sna->kgem.nbatch + 5, + upload, + I915_GEM_DOMAIN_RENDER << 16 | + KGEM_RELOC_FENCED, + 0); + b[6] = gc->bgPixel; + b[7] = gc->fgPixel; + + sna->kgem.nbatch += 8; + kgem_bo_destroy(&sna->kgem, upload); + + box++; + } while (--n); + + sna->blt_state.fill_bo = 0; + return true; +} + +static void +sna_push_pixels(GCPtr gc, PixmapPtr bitmap, DrawablePtr drawable, + int w, int h, + int x, int y) +{ + RegionRec region; + + if (w == 0 || h == 0) + return; + + DBG(("%s (%d, %d)x(%d, %d)\n", __FUNCTION__, x, y, w, h)); + + region.extents.x1 = x; + region.extents.y1 = y; + region.extents.x2 = region.extents.x1 + w; + region.extents.y2 = region.extents.y1 + h; + + clip_box(®ion.extents, gc); + if (box_empty(®ion.extents)) + return; + + DBG(("%s: extents(%d, %d), (%d, %d)\n", __FUNCTION__, + region.extents.x1, region.extents.y1, + region.extents.x2, region.extents.y2)); + + region.data = NULL; + region_maybe_clip(®ion, gc->pCompositeClip); + if (!RegionNotEmpty(®ion)) + return; + + switch (gc->fillStyle) { + case FillSolid: + if (sna_push_pixels_solid_blt(gc, bitmap, drawable, ®ion)) + return; + break; + default: + break; + } + + DBG(("%s: fallback\n", __FUNCTION__)); + if (!sna_gc_move_to_cpu(gc, drawable, ®ion)) + goto out; + if (!sna_pixmap_move_to_cpu(bitmap, MOVE_READ)) + goto out_gc; + if (!sna_drawable_move_region_to_cpu(drawable, ®ion, + drawable_gc_flags(drawable, gc, false))) + goto out_gc; + + DBG(("%s: fallback, fbPushPixels(%d, %d, %d %d)\n", + __FUNCTION__, w, h, x, y)); + fbPushPixels(gc, bitmap, drawable, w, h, x, y); + FALLBACK_FLUSH(drawable); +out_gc: + sna_gc_move_to_gpu(gc); +out: + RegionUninit(®ion); +} + +static const GCOps sna_gc_ops = { + sna_fill_spans, + sna_set_spans, + sna_put_image, + sna_copy_area, + sna_copy_plane, + sna_poly_point, + sna_poly_line, + sna_poly_segment, + sna_poly_rectangle, + sna_poly_arc, + sna_poly_fill_polygon, + sna_poly_fill_rect, + sna_poly_fill_arc, + sna_poly_text8, + sna_poly_text16, + sna_image_text8, + sna_image_text16, + sna_image_glyph, + sna_poly_glyph, + sna_push_pixels, +}; + +static const GCOps sna_gc_ops__cpu = { + fbFillSpans, + fbSetSpans, + fbPutImage, + fbCopyArea, + fbCopyPlane, + sna_poly_point__cpu, + fbPolyLine, + fbPolySegment, + miPolyRectangle, + fbPolyArc, + miFillPolygon, + fbPolyFillRect, + miPolyFillArc, + miPolyText8, + miPolyText16, + miImageText8, + miImageText16, + fbImageGlyphBlt, + fbPolyGlyphBlt, + fbPushPixels +}; + +static GCOps sna_gc_ops__tmp = { + sna_fill_spans, + sna_set_spans, + sna_put_image, + sna_copy_area, + sna_copy_plane, + sna_poly_point, + sna_poly_line, + sna_poly_segment, + sna_poly_rectangle, + sna_poly_arc, + sna_poly_fill_polygon, + sna_poly_fill_rect, + sna_poly_fill_arc, + sna_poly_text8, + sna_poly_text16, + sna_image_text8, + sna_image_text16, + sna_image_glyph, + sna_poly_glyph, + sna_push_pixels, +}; + +static void +sna_validate_gc(GCPtr gc, unsigned long changes, DrawablePtr drawable) +{ + DBG(("%s changes=%lx\n", __FUNCTION__, changes)); + + if (changes & (GCClipMask|GCSubwindowMode) || + drawable->serialNumber != (gc->serialNumber & DRAWABLE_SERIAL_BITS) || + (gc->clientClipType != CT_NONE && (changes & (GCClipXOrigin | GCClipYOrigin)))) + miComputeCompositeClip(gc, drawable); + + sna_gc(gc)->changes |= changes; +} + +static const GCFuncs sna_gc_funcs = { + sna_validate_gc, + miChangeGC, + miCopyGC, + miDestroyGC, + miChangeClip, + miDestroyClip, + miCopyClip +}; + +static const GCFuncs sna_gc_funcs__cpu = { + fbValidateGC, + miChangeGC, + miCopyGC, + miDestroyGC, + miChangeClip, + miDestroyClip, + miCopyClip +}; + +static int sna_create_gc(GCPtr gc) +{ + gc->miTranslate = 1; + gc->fExpose = 1; + + fb_gc(gc)->bpp = bits_per_pixel(gc->depth); + + gc->funcs = (GCFuncs *)&sna_gc_funcs; + gc->ops = (GCOps *)&sna_gc_ops; + return true; +} + +static void +sna_get_image(DrawablePtr drawable, + int x, int y, int w, int h, + unsigned int format, unsigned long mask, + char *dst) +{ + RegionRec region; + unsigned int flags; + + if (!fbDrawableEnabled(drawable)) + return; + + DBG(("%s (%d, %d)x(%d, %d)\n", __FUNCTION__, x, y, w, h)); + + region.extents.x1 = x + drawable->x; + region.extents.y1 = y + drawable->y; + region.extents.x2 = region.extents.x1 + w; + region.extents.y2 = region.extents.y1 + h; + region.data = NULL; + + flags = MOVE_READ; + if ((w | h) == 1) + flags |= MOVE_INPLACE_HINT; + if (w == drawable->width) + flags |= MOVE_WHOLE_HINT; + if (!sna_drawable_move_region_to_cpu(drawable, ®ion, flags)) + return; + + if (format == ZPixmap && + drawable->bitsPerPixel >= 8 && + PM_IS_SOLID(drawable, mask)) { + PixmapPtr pixmap = get_drawable_pixmap(drawable); + int16_t dx, dy; + + DBG(("%s: copy box (%d, %d), (%d, %d)\n", + __FUNCTION__, + region.extents.x1, region.extents.y1, + region.extents.x2, region.extents.y2)); + get_drawable_deltas(drawable, pixmap, &dx, &dy); + memcpy_blt(pixmap->devPrivate.ptr, dst, drawable->bitsPerPixel, + pixmap->devKind, PixmapBytePad(w, drawable->depth), + region.extents.x1 + dx, + region.extents.y1 + dy, + 0, 0, w, h); + } else + fbGetImage(drawable, x, y, w, h, format, mask, dst); +} + +static void +sna_get_spans(DrawablePtr drawable, int wMax, + DDXPointPtr pt, int *width, int n, char *start) +{ + RegionRec region; + + if (!fbDrawableEnabled(drawable)) + return; + + if (sna_spans_extents(drawable, NULL, n, pt, width, ®ion.extents) == 0) + return; + + region.data = NULL; + if (!sna_drawable_move_region_to_cpu(drawable, ®ion, MOVE_READ)) + return; + + fbGetSpans(drawable, wMax, pt, width, n, start); +} + +static void +sna_copy_window(WindowPtr win, DDXPointRec origin, RegionPtr src) +{ + PixmapPtr pixmap = get_window_pixmap(win); + struct sna *sna = to_sna_from_pixmap(pixmap); + RegionRec dst; + int dx, dy; + + DBG(("%s origin=(%d, %d)\n", __FUNCTION__, origin.x, origin.y)); + if (!fbWindowEnabled(win)) + return; + + dx = origin.x - win->drawable.x; + dy = origin.y - win->drawable.y; + RegionTranslate(src, -dx, -dy); + + RegionNull(&dst); + RegionIntersect(&dst, &win->borderClip, src); + if (!RegionNotEmpty(&dst)) + return; + +#ifdef COMPOSITE + if (pixmap->screen_x | pixmap->screen_y) + RegionTranslate(&dst, -pixmap->screen_x, -pixmap->screen_y); +#endif + + if (wedged(sna) || FORCE_FALLBACK || !ACCEL_COPY_WINDOW) { + DBG(("%s: fallback -- wedged\n", __FUNCTION__)); + if (!sna_pixmap_move_to_cpu(pixmap, MOVE_READ | MOVE_WRITE)) + return; + + miCopyRegion(&pixmap->drawable, &pixmap->drawable, + 0, &dst, dx, dy, fbCopyNtoN, 0, NULL); + } else { + sna_self_copy_boxes(&pixmap->drawable, &pixmap->drawable, NULL, + &dst, dx, dy, 0, NULL); + } + + RegionUninit(&dst); +} + +static Bool sna_change_window_attributes(WindowPtr win, unsigned long mask) +{ + bool ret = true; + + DBG(("%s\n", __FUNCTION__)); + + /* Check if the fb layer wishes to modify the attached pixmaps, + * to fix up mismatches between the window and pixmap depths. + */ + if (mask & CWBackPixmap && win->backgroundState == BackgroundPixmap) { + DBG(("%s: flushing background pixmap\n", __FUNCTION__)); + ret &= sna_validate_pixmap(&win->drawable, win->background.pixmap); + } + + if (mask & CWBorderPixmap && win->borderIsPixel == false) { + DBG(("%s: flushing border pixmap\n", __FUNCTION__)); + ret &= sna_validate_pixmap(&win->drawable, win->border.pixmap); + } + + return ret; +} + +static void +sna_accel_flush_callback(CallbackListPtr *list, + pointer user_data, pointer call_data) +{ + struct sna *sna = user_data; + struct sna_pixmap *priv; + + /* XXX we should be able to reduce the frequency of flushes further + * by checking for outgoing damage events or sync replies. Tricky, + * and doesn't appear to mitigate the performance loss. + */ + DBG(("%s: flush?=%d, dirty?=%d\n", __FUNCTION__, + sna->kgem.flush, !list_is_empty(&sna->flush_pixmaps))); + + /* flush any pending damage from shadow copies to tfp clients */ + while (!list_is_empty(&sna->flush_pixmaps)) { + bool ret; + + priv = list_first_entry(&sna->flush_pixmaps, + struct sna_pixmap, list); + + list_del(&priv->list); + if (priv->shm) { + DBG(("%s: syncing SHM pixmap=%ld\n", __FUNCTION__, + priv->pixmap->drawable.serialNumber)); + ret = sna_pixmap_move_to_cpu(priv->pixmap, + MOVE_READ | MOVE_WRITE); + assert(!ret || priv->gpu_bo == NULL); + if (priv->pixmap->refcnt == 0) + __sna_free_pixmap(sna, priv->pixmap, priv); + } else { + DBG(("%s: flushing DRI pixmap=%ld\n", __FUNCTION__, + priv->pixmap->drawable.serialNumber)); + ret = sna_pixmap_move_to_gpu(priv->pixmap, + MOVE_READ | __MOVE_FORCE); + } + (void)ret; + } + + if (sna->kgem.flush) + kgem_submit(&sna->kgem); +} + +static struct sna_pixmap *sna_accel_scanout(struct sna *sna) +{ + struct sna_pixmap *priv; + + if (sna->vblank_interval == 0) + return NULL; + + if (sna->front == NULL) + return NULL; + + priv = sna_pixmap(sna->front); + return priv && priv->gpu_bo ? priv : NULL; +} + +#define TIME currentTime.milliseconds +static void sna_accel_disarm_timer(struct sna *sna, int id) +{ + DBG(("%s[%d] (time=%ld)\n", __FUNCTION__, id, (long)TIME)); + sna->timer_active &= ~(1<<id); +} + +static bool has_offload_slaves(struct sna *sna) +{ +#if HAS_PIXMAP_SHARING + ScreenPtr screen = sna->scrn->pScreen; + PixmapDirtyUpdatePtr dirty; + + xorg_list_for_each_entry(dirty, &screen->pixmap_dirty_list, ent) { + if (RegionNotEmpty(DamageRegion(dirty->damage))) + return true; + } +#endif + return false; +} + +static bool has_shadow(struct sna *sna) +{ + DamagePtr damage = sna->mode.shadow_damage; + + if (!(damage && RegionNotEmpty(DamageRegion(damage)))) + return false; + + DBG(("%s: has pending damage\n", __FUNCTION__)); + if ((sna->flags & SNA_TEAR_FREE) == 0) + return true; + + DBG(("%s: outstanding flips: %d\n", + __FUNCTION__, sna->mode.shadow_flip)); + return !sna->mode.shadow_flip; +} + +static bool start_flush(struct sna *sna, struct sna_pixmap *scanout) +{ + DBG(("%s: scanout=%d shadow?=%d, slaves?=%d, (cpu?=%d || gpu?=%d))\n", + __FUNCTION__, + scanout && scanout->gpu_bo ? scanout->gpu_bo->handle : 0, + has_shadow(sna), has_offload_slaves(sna), + scanout && scanout->cpu_damage != NULL, + scanout && scanout->gpu_bo && scanout->gpu_bo->exec != NULL)); + + if (has_offload_slaves(sna)) + return true; + + if (has_shadow(sna)) + return true; + + if (!scanout) + return false; + + return scanout->cpu_damage || scanout->gpu_bo->exec; +} + +static bool stop_flush(struct sna *sna, struct sna_pixmap *scanout) +{ + DBG(("%s: scanout=%d shadow?=%d, slaves?=%d, (cpu?=%d || gpu?=%d))\n", + __FUNCTION__, + scanout && scanout->gpu_bo ? scanout->gpu_bo->handle : 0, + has_shadow(sna), has_offload_slaves(sna), + scanout && scanout->cpu_damage != NULL, + scanout && scanout->gpu_bo && scanout->gpu_bo->rq != NULL)); + + if (has_offload_slaves(sna)) + return true; + + if (has_shadow(sna)) + return true; + + if (!scanout) + return false; + + return scanout->cpu_damage || scanout->gpu_bo->needs_flush; +} + +static bool sna_accel_do_flush(struct sna *sna) +{ + struct sna_pixmap *priv; + int interval; + + priv = sna_accel_scanout(sna); + if (priv == NULL && !sna->mode.shadow_active && !has_offload_slaves(sna)) { + DBG(("%s -- no scanout attached\n", __FUNCTION__)); + sna_accel_disarm_timer(sna, FLUSH_TIMER); + return false; + } + + if (sna->flags & SNA_NO_DELAYED_FLUSH) + return true; + + interval = sna->vblank_interval ?: 20; + if (sna->timer_active & (1<<(FLUSH_TIMER))) { + int32_t delta = sna->timer_expire[FLUSH_TIMER] - TIME; + DBG(("%s: flush timer active: delta=%d\n", + __FUNCTION__, delta)); + if (delta <= 3) { + DBG(("%s (time=%ld), triggered\n", __FUNCTION__, (long)TIME)); + sna->timer_expire[FLUSH_TIMER] = TIME + interval; + return true; + } + } else { + if (!start_flush(sna, priv)) { + DBG(("%s -- no pending write to scanout\n", __FUNCTION__)); + if (priv) + kgem_bo_flush(&sna->kgem, priv->gpu_bo); + } else { + sna->timer_active |= 1 << FLUSH_TIMER; + sna->timer_expire[FLUSH_TIMER] = TIME + interval / 2; + DBG(("%s (time=%ld), starting\n", __FUNCTION__, (long)TIME)); + } + } + + return false; +} + +static bool sna_accel_do_throttle(struct sna *sna) +{ + if (sna->flags & SNA_NO_THROTTLE) + return false; + + if (sna->timer_active & (1<<(THROTTLE_TIMER))) { + int32_t delta = sna->timer_expire[THROTTLE_TIMER] - TIME; + if (delta <= 3) { + DBG(("%s (time=%ld), triggered\n", __FUNCTION__, (long)TIME)); + sna->timer_expire[THROTTLE_TIMER] = TIME + 20; + return true; + } + } else { + if (!sna->kgem.need_retire) { + DBG(("%s -- no pending activity\n", __FUNCTION__)); + } else { + DBG(("%s (time=%ld), starting\n", __FUNCTION__, (long)TIME)); + sna->timer_active |= 1 << THROTTLE_TIMER; + sna->timer_expire[THROTTLE_TIMER] = TIME + 20; + } + } + + return false; +} + +static bool sna_accel_do_expire(struct sna *sna) +{ + if (sna->timer_active & (1<<(EXPIRE_TIMER))) { + int32_t delta = sna->timer_expire[EXPIRE_TIMER] - TIME; + if (delta <= 3) { + DBG(("%s (time=%ld), triggered\n", __FUNCTION__, (long)TIME)); + sna->timer_expire[EXPIRE_TIMER] = + TIME + MAX_INACTIVE_TIME * 1000; + return true; + } + } else { + if (sna->kgem.need_expire) { + sna->timer_active |= 1 << EXPIRE_TIMER; + sna->timer_expire[EXPIRE_TIMER] = + TIME + MAX_INACTIVE_TIME * 1000; + DBG(("%s (time=%ld), starting\n", __FUNCTION__, (long)TIME)); + } + } + + return false; +} + +static bool sna_accel_do_inactive(struct sna *sna) +{ + if (!USE_INACTIVE) + return false; + + if (sna->timer_active & (1<<(INACTIVE_TIMER))) { + int32_t delta = sna->timer_expire[INACTIVE_TIMER] - TIME; + if (delta <= 3) { + sna->timer_expire[INACTIVE_TIMER] = + TIME + 120 * 1000; + DBG(("%s (time=%ld), triggered\n", __FUNCTION__, (long)TIME)); + return true; + } + } else { + if (!list_is_empty(&sna->active_pixmaps)) { + sna->timer_active |= 1 << INACTIVE_TIMER; + sna->timer_expire[INACTIVE_TIMER] = + TIME + 120 * 1000; + DBG(("%s (time=%ld), starting\n", __FUNCTION__, (long)TIME)); + } + } + + return false; +} + +static int32_t sna_timeout(struct sna *sna) +{ + int32_t now = TIME, next = 0; + int i; + + DBG(("%s: now=%d, active=%08x\n", + __FUNCTION__, (int)now, sna->timer_active)); + for (i = 0; i < NUM_TIMERS; i++) { + if (sna->timer_active & (1 << i)) { + int32_t delta = sna->timer_expire[i] - now; + DBG(("%s: timer[%d] expires in %d [%d]\n", + __FUNCTION__, i, delta, sna->timer_expire[i])); + if (next == 0 || delta < next) + next = delta; + } + } + + DBG(("%s: active=%08x, next=+%d\n", + __FUNCTION__, sna->timer_active, next)); + return next; +} + +static void sna_accel_post_damage(struct sna *sna) +{ +#if HAS_PIXMAP_SHARING + ScreenPtr screen = sna->scrn->pScreen; + PixmapDirtyUpdatePtr dirty; + bool flush = false; + + xorg_list_for_each_entry(dirty, &screen->pixmap_dirty_list, ent) { + RegionRec region, *damage; + PixmapPtr src, dst; + BoxPtr box; + int n; + + damage = DamageRegion(dirty->damage); + if (!RegionNotEmpty(damage)) + continue; + + src = dirty->src; + dst = dirty->slave_dst->master_pixmap; + + region.extents.x1 = dirty->x; + region.extents.x2 = dirty->x + dst->drawable.width; + region.extents.y1 = dirty->y; + region.extents.y2 = dirty->y + dst->drawable.height; + region.data = NULL; + + DBG(("%s: pushing damage ((%d, %d), (%d, %d))x%d to slave pixmap=%ld, ((%d, %d), (%d, %d))\n", __FUNCTION__, + damage->extents.x1, damage->extents.y1, + damage->extents.x2, damage->extents.y2, + RegionNumRects(damage), + dst->drawable.serialNumber, + region.extents.x1, region.extents.y1, + region.extents.x2, region.extents.y2)); + + RegionIntersect(®ion, ®ion, damage); + + box = REGION_RECTS(®ion); + n = REGION_NUM_RECTS(®ion); + if (wedged(sna)) { +fallback: + if (!sna_pixmap_move_to_cpu(src, MOVE_READ)) + goto skip; + + if (!sna_pixmap_move_to_cpu(dst, MOVE_READ | MOVE_WRITE | MOVE_INPLACE_HINT)) + goto skip; + + assert(src->drawable.bitsPerPixel == dst->drawable.bitsPerPixel); + do { + DBG(("%s: copy box (%d, %d)->(%d, %d)x(%d, %d)\n", + __FUNCTION__, + box->x1, box->y1, + box->x1 - dirty->x, box->y1 - dirty->y, + box->x2 - box->x1, box->y2 - box->y1)); + + assert(box->x2 > box->x1); + assert(box->y2 > box->y1); + + assert(box->x1 >= 0); + assert(box->y1 >= 0); + assert(box->x2 <= src->drawable.width); + assert(box->y2 <= src->drawable.height); + + assert(box->x1 - dirty->x >= 0); + assert(box->y1 - dirty->y >= 0); + assert(box->x2 - dirty->x <= src->drawable.width); + assert(box->y2 - dirty->y <= src->drawable.height); + + memcpy_blt(src->devPrivate.ptr, + dst->devPrivate.ptr, + src->drawable.bitsPerPixel, + src->devKind, dst->devKind, + box->x1, box->y1, + box->x1 - dirty->x, + box->y1 - dirty->y, + box->x2 - box->x1, + box->y2 - box->y1); + box++; + } while (--n); + } else { + if (!sna_pixmap_move_to_gpu(src, MOVE_READ | __MOVE_FORCE)) + goto fallback; + + if (!sna_pixmap_move_to_gpu(dst, MOVE_READ | MOVE_WRITE | __MOVE_FORCE)) + goto fallback; + + if (!sna->render.copy_boxes(sna, GXcopy, + src, sna_pixmap_get_bo(src), 0, 0, + dst, sna_pixmap_get_bo(dst), -dirty->x, -dirty->y, + box, n, COPY_LAST)) + goto fallback; + + flush = true; + } + + RegionTranslate(®ion, -dirty->x, -dirty->y); + DamageRegionAppend(&dirty->slave_dst->drawable, ®ion); + +skip: + RegionUninit(®ion); + DamageEmpty(dirty->damage); + } + if (flush) + kgem_submit(&sna->kgem); +#endif +} + +static void sna_accel_flush(struct sna *sna) +{ + struct sna_pixmap *priv = sna_accel_scanout(sna); + bool busy; + + DBG(("%s (time=%ld), cpu damage? %d, exec? %d nbatch=%d, busy? %d\n", + __FUNCTION__, (long)TIME, + priv && priv->cpu_damage, + priv && priv->gpu_bo->exec != NULL, + sna->kgem.nbatch, + sna->kgem.busy)); + + busy = stop_flush(sna, priv); + if (!sna->kgem.busy && !busy) + sna_accel_disarm_timer(sna, FLUSH_TIMER); + sna->kgem.busy = busy; + + if (priv) { + sna_pixmap_force_to_gpu(priv->pixmap, + MOVE_READ | MOVE_ASYNC_HINT); + kgem_bo_flush(&sna->kgem, priv->gpu_bo); + assert(!priv->cpu); + } + + sna_mode_redisplay(sna); + sna_accel_post_damage(sna); +} + +static void sna_accel_throttle(struct sna *sna) +{ + DBG(("%s (time=%ld)\n", __FUNCTION__, (long)TIME)); + + if (sna->kgem.need_throttle) { + kgem_submit(&sna->kgem); + kgem_throttle(&sna->kgem); + } + + if (!sna->kgem.need_retire) + sna_accel_disarm_timer(sna, THROTTLE_TIMER); +} + +static void sna_accel_expire(struct sna *sna) +{ + DBG(("%s (time=%ld)\n", __FUNCTION__, (long)TIME)); + + if (!kgem_expire_cache(&sna->kgem)) + sna_accel_disarm_timer(sna, EXPIRE_TIMER); +} + +static void sna_accel_inactive(struct sna *sna) +{ + struct sna_pixmap *priv; + struct list preserve; + + DBG(("%s (time=%ld)\n", __FUNCTION__, (long)TIME)); + +#if HAS_FULL_DEBUG + { + unsigned count, bytes; + + count = bytes = 0; + list_for_each_entry(priv, &sna->inactive_clock[1], inactive) + if (!priv->pinned) + count++, bytes += kgem_bo_size(priv->gpu_bo); + + DBG(("%s: trimming %d inactive GPU buffers, %d bytes\n", + __FUNCTION__, count, bytes)); + + count = bytes = 0; + list_for_each_entry(priv, &sna->active_pixmaps, inactive) { + if (priv->ptr && + sna_damage_is_all(&priv->gpu_damage, + priv->pixmap->drawable.width, + priv->pixmap->drawable.height)) { + count++, bytes += priv->pixmap->devKind * priv->pixmap->drawable.height; + } + } + + DBG(("%s: trimming %d inactive CPU buffers, %d bytes\n", + __FUNCTION__, count, bytes)); + } +#endif + + /* clear out the oldest inactive pixmaps */ + list_init(&preserve); + while (!list_is_empty(&sna->inactive_clock[1])) { + priv = list_first_entry(&sna->inactive_clock[1], + struct sna_pixmap, + inactive); + assert((priv->create & KGEM_CAN_CREATE_LARGE) == 0); + assert(priv->gpu_bo); + assert(!priv->gpu_bo->proxy); + + /* XXX Rather than discarding the GPU buffer here, we + * could mark it purgeable and allow the shrinker to + * reap its storage only under memory pressure. + */ + list_del(&priv->inactive); + if (priv->pinned) + continue; + + if (priv->ptr && + sna_damage_is_all(&priv->gpu_damage, + priv->pixmap->drawable.width, + priv->pixmap->drawable.height)) { + DBG(("%s: discarding inactive CPU shadow\n", + __FUNCTION__)); + sna_damage_destroy(&priv->cpu_damage); + list_del(&priv->list); + + assert(priv->cpu_bo == NULL || !priv->cpu_bo->flush); + assert(!priv->shm); + sna_pixmap_free_cpu(sna, priv); + priv->undamaged = false; + priv->cpu = false; + + list_add(&priv->inactive, &preserve); + } else { + DBG(("%s: discarding inactive GPU bo handle=%d\n", + __FUNCTION__, priv->gpu_bo->handle)); + if (!sna_pixmap_move_to_cpu(priv->pixmap, + MOVE_READ | MOVE_WRITE | MOVE_ASYNC_HINT)) + list_add(&priv->inactive, &preserve); + } + } + + /* Age the current inactive pixmaps */ + sna->inactive_clock[1].next = sna->inactive_clock[0].next; + sna->inactive_clock[0].next->prev = &sna->inactive_clock[1]; + sna->inactive_clock[0].prev->next = &sna->inactive_clock[1]; + sna->inactive_clock[1].prev = sna->inactive_clock[0].prev; + + sna->inactive_clock[0].next = sna->active_pixmaps.next; + sna->active_pixmaps.next->prev = &sna->inactive_clock[0]; + sna->active_pixmaps.prev->next = &sna->inactive_clock[0]; + sna->inactive_clock[0].prev = sna->active_pixmaps.prev; + + sna->active_pixmaps.next = preserve.next; + preserve.next->prev = &sna->active_pixmaps; + preserve.prev->next = &sna->active_pixmaps; + sna->active_pixmaps.prev = preserve.prev; + + if (list_is_empty(&sna->inactive_clock[1]) && + list_is_empty(&sna->inactive_clock[0]) && + list_is_empty(&sna->active_pixmaps)) + sna_accel_disarm_timer(sna, INACTIVE_TIMER); +} + +#ifdef DEBUG_MEMORY +static bool sna_accel_do_debug_memory(struct sna *sna) +{ + int32_t delta = sna->timer_expire[DEBUG_MEMORY_TIMER] - TIME; + + if (delta <= 3) { + sna->timer_expire[DEBUG_MEMORY_TIMER] = TIME + 10 * 1000; + return true; + } else + return false; +} + +static void sna_accel_debug_memory(struct sna *sna) +{ + ErrorF("Allocated bo: %d, %ld bytes\n", + sna->kgem.debug_memory.bo_allocs, + (long)sna->kgem.debug_memory.bo_bytes); + ErrorF("Allocated CPU bo: %d, %ld bytes\n", + sna->debug_memory.cpu_bo_allocs, + (long)sna->debug_memory.cpu_bo_bytes); +} + +#else +#define sna_accel_do_debug_memory(x) 0 +static void sna_accel_debug_memory(struct sna *sna) { } +#endif + +static ShmFuncs shm_funcs = { sna_pixmap_create_shm, NULL }; + +static PixmapPtr +sna_get_window_pixmap(WindowPtr window) +{ + return get_window_pixmap(window); +} + +static void +sna_set_window_pixmap(WindowPtr window, PixmapPtr pixmap) +{ + *(PixmapPtr *)dixGetPrivateAddr(&window->devPrivates, &sna_window_key) = pixmap; +} + +static Bool +sna_create_window(WindowPtr win) +{ + sna_set_window_pixmap(win, win->drawable.pScreen->devPrivate); + return TRUE; +} + +static Bool +sna_map_window(WindowPtr win) +{ + return TRUE; +} + +static Bool +sna_position_window(WindowPtr win, int x, int y) +{ + return TRUE; +} + +static Bool +sna_unmap_window(WindowPtr win) +{ + return TRUE; +} + +static Bool +sna_destroy_window(WindowPtr win) +{ + sna_dri_destroy_window(win); + return TRUE; +} + +static void +sna_query_best_size(int class, + unsigned short *width, unsigned short *height, + ScreenPtr screen) +{ + unsigned short w; + + switch (class) { + case CursorShape: + if (*width > screen->width) + *width = screen->width; + if (*height > screen->height) + *height = screen->height; + break; + + case TileShape: + case StippleShape: + w = *width; + if ((w & (w - 1)) && w < FB_UNIT) { + for (w = 1; w < *width; w <<= 1) + ; + *width = w; + } + break; + } +} + +static void sna_store_colors(ColormapPtr cmap, int n, xColorItem *def) +{ +} + +static bool sna_picture_init(ScreenPtr screen) +{ + PictureScreenPtr ps; + + if (!miPictureInit(screen, NULL, 0)) + return false; + + ps = GetPictureScreen(screen); + assert(ps != NULL); + + ps->Composite = sna_composite; + ps->CompositeRects = sna_composite_rectangles; + ps->Glyphs = sna_glyphs; + if (xf86IsEntityShared(xf86ScreenToScrn(screen)->entityList[0])) + ps->Glyphs = sna_glyphs__shared; + ps->UnrealizeGlyph = sna_glyph_unrealize; + ps->AddTraps = sna_add_traps; + ps->Trapezoids = sna_composite_trapezoids; + ps->Triangles = sna_composite_triangles; +#if PICTURE_SCREEN_VERSION >= 2 + ps->TriStrip = sna_composite_tristrip; + ps->TriFan = sna_composite_trifan; +#endif + + return true; +} + +bool sna_accel_init(ScreenPtr screen, struct sna *sna) +{ + const char *backend; + + sna_font_key = AllocateFontPrivateIndex(); + + list_init(&sna->flush_pixmaps); + list_init(&sna->active_pixmaps); + list_init(&sna->inactive_clock[0]); + list_init(&sna->inactive_clock[1]); + + AddGeneralSocket(sna->kgem.fd); + +#ifdef DEBUG_MEMORY + sna->timer_expire[DEBUG_MEMORY_TIMER] = GetTimeInMillis()+ 10 * 1000; +#endif + + screen->defColormap = FakeClientID(0); + /* let CreateDefColormap do whatever it wants for pixels */ + screen->blackPixel = screen->whitePixel = (Pixel) 0; + screen->QueryBestSize = sna_query_best_size; + assert(screen->GetImage == NULL); + screen->GetImage = sna_get_image; + assert(screen->GetSpans == NULL); + screen->GetSpans = sna_get_spans; + assert(screen->CreateWindow == NULL); + screen->CreateWindow = sna_create_window; + assert(screen->DestroyWindow == NULL); + screen->DestroyWindow = sna_destroy_window; + screen->PositionWindow = sna_position_window; + screen->ChangeWindowAttributes = sna_change_window_attributes; + screen->RealizeWindow = sna_map_window; + screen->UnrealizeWindow = sna_unmap_window; + screen->CopyWindow = sna_copy_window; + assert(screen->CreatePixmap == NULL); + screen->CreatePixmap = sna_create_pixmap; + assert(screen->DestroyPixmap == NULL); + screen->DestroyPixmap = sna_destroy_pixmap; +#ifdef CREATE_PIXMAP_USAGE_SHARED + screen->SharePixmapBacking = sna_share_pixmap_backing; + screen->SetSharedPixmapBacking = sna_set_shared_pixmap_backing; +#endif + screen->RealizeFont = sna_realize_font; + screen->UnrealizeFont = sna_unrealize_font; + assert(screen->CreateGC == NULL); + screen->CreateGC = sna_create_gc; + screen->CreateColormap = miInitializeColormap; + screen->DestroyColormap = (void (*)(ColormapPtr)) NoopDDA; + screen->InstallColormap = miInstallColormap; + screen->UninstallColormap = miUninstallColormap; + screen->ListInstalledColormaps = miListInstalledColormaps; + screen->ResolveColor = miResolveColor; + assert(screen->StoreColors == NULL); + screen->StoreColors = sna_store_colors; + screen->BitmapToRegion = fbBitmapToRegion; + +#if HAS_PIXMAP_SHARING + screen->StartPixmapTracking = PixmapStartDirtyTracking; + screen->StopPixmapTracking = PixmapStopDirtyTracking; +#endif + + assert(screen->GetWindowPixmap == NULL); + screen->GetWindowPixmap = sna_get_window_pixmap; + assert(screen->SetWindowPixmap == NULL); + screen->SetWindowPixmap = sna_set_window_pixmap; + + if (sna->kgem.has_userptr) + ShmRegisterFuncs(screen, &shm_funcs); + else + ShmRegisterFbFuncs(screen); + + if (!sna_picture_init(screen)) + return false; + + backend = "no"; + sna->have_render = false; + no_render_init(sna); + +#if !DEBUG_NO_RENDER + if (sna->info->gen >= 80) { + } else if (sna->info->gen >= 70) { + if ((sna->have_render = gen7_render_init(sna))) + backend = "IvyBridge"; + } else if (sna->info->gen >= 60) { + if ((sna->have_render = gen6_render_init(sna))) + backend = "SandyBridge"; + } else if (sna->info->gen >= 50) { + if ((sna->have_render = gen5_render_init(sna))) + backend = "Ironlake"; + } else if (sna->info->gen >= 40) { + if ((sna->have_render = gen4_render_init(sna))) + backend = "Broadwater"; + } else if (sna->info->gen >= 30) { + if ((sna->have_render = gen3_render_init(sna))) + backend = "gen3"; + } else if (sna->info->gen >= 20) { + if ((sna->have_render = gen2_render_init(sna))) + backend = "gen2"; + } +#endif + DBG(("%s(backend=%s, have_render=%d)\n", + __FUNCTION__, backend, sna->have_render)); + + kgem_reset(&sna->kgem); + + xf86DrvMsg(sna->scrn->scrnIndex, X_INFO, + "SNA initialized with %s backend\n", + backend); + + return true; +} + +void sna_accel_create(struct sna *sna) +{ + if (!sna_glyphs_create(sna)) + goto fail; + + if (!sna_gradients_create(sna)) + goto fail; + + if (!sna_composite_create(sna)) + goto fail; + + return; + +fail: + xf86DrvMsg(sna->scrn->scrnIndex, X_ERROR, + "Failed to allocate caches, disabling RENDER acceleration\n"); + sna->have_render = false; + no_render_init(sna); +} + +void sna_accel_watch_flush(struct sna *sna, int enable) +{ + DBG(("%s: enable=%d\n", __FUNCTION__, enable)); + assert(enable); + + if (sna->watch_flush == 0) { + DBG(("%s: installing watchers\n", __FUNCTION__)); + assert(enable > 0); + if (!AddCallback(&FlushCallback, sna_accel_flush_callback, sna)) { + xf86DrvMsg(sna->scrn->scrnIndex, X_Error, + "Failed to attach ourselves to the flush callbacks, expect missing synchronisation with DRI clients (e.g a compositor)\n"); + } + sna->watch_flush++; + } + + sna->watch_flush += enable; +} + +void sna_accel_close(struct sna *sna) +{ + sna_composite_close(sna); + sna_gradients_close(sna); + sna_glyphs_close(sna); + + while (sna->freed_pixmap) { + PixmapPtr pixmap = sna->freed_pixmap; + sna->freed_pixmap = pixmap->devPrivate.ptr; + assert(pixmap->refcnt == 0); + free(sna_pixmap(pixmap)); + FreePixmap(pixmap); + } + + DeleteCallback(&FlushCallback, sna_accel_flush_callback, sna); + + kgem_cleanup_cache(&sna->kgem); +} + +void sna_accel_block_handler(struct sna *sna, struct timeval **tv) +{ + UpdateCurrentTimeIf(); + + if (sna->kgem.nbatch && kgem_is_idle(&sna->kgem)) { + DBG(("%s: GPU idle, flushing\n", __FUNCTION__)); + _kgem_submit(&sna->kgem); + } + + if (sna_accel_do_flush(sna)) + sna_accel_flush(sna); + assert(sna->flags & SNA_NO_DELAYED_FLUSH || + sna_accel_scanout(sna) == NULL || + sna_accel_scanout(sna)->gpu_bo->exec == NULL || + sna->timer_active & (1<<(FLUSH_TIMER))); + + if (sna_accel_do_throttle(sna)) + sna_accel_throttle(sna); + assert(sna->flags & SNA_NO_THROTTLE || + !sna->kgem.need_retire || + sna->timer_active & (1<<(THROTTLE_TIMER))); + + if (sna_accel_do_expire(sna)) + sna_accel_expire(sna); + assert(!sna->kgem.need_expire || + sna->timer_active & (1<<(EXPIRE_TIMER))); + + if (sna_accel_do_inactive(sna)) + sna_accel_inactive(sna); + + if (sna_accel_do_debug_memory(sna)) + sna_accel_debug_memory(sna); + + if (sna->watch_flush == 1) { + DBG(("%s: removing watchers\n", __FUNCTION__)); + DeleteCallback(&FlushCallback, sna_accel_flush_callback, sna); + sna->watch_flush = 0; + } + + if (sna->timer_active) { + int32_t timeout; + + DBG(("%s: evaluating timers, active=%x\n", + __FUNCTION__, sna->timer_active)); + timeout = sna_timeout(sna); + if (timeout) { + if (*tv == NULL) { + *tv = &sna->timer_tv; + goto set_tv; + } + if ((*tv)->tv_sec * 1000 + (*tv)->tv_usec / 1000 > timeout) { +set_tv: + (*tv)->tv_sec = timeout / 1000; + (*tv)->tv_usec = timeout % 1000 * 1000; + } + } + } +} + +void sna_accel_wakeup_handler(struct sna *sna) +{ + DBG(("%s\n", __FUNCTION__)); + + if (sna->kgem.need_retire) + kgem_retire(&sna->kgem); + if (!sna->mode.shadow_active && !sna->kgem.need_retire) { + DBG(("%s: GPU idle, flushing\n", __FUNCTION__)); + kgem_submit(&sna->kgem); + } + if (sna->kgem.need_purge) + kgem_purge_cache(&sna->kgem); +} + +void sna_accel_free(struct sna *sna) +{ +} |