diff options
Diffstat (limited to 'src/sna/sna_accel.c')
-rw-r--r-- | src/sna/sna_accel.c | 3306 |
1 files changed, 3306 insertions, 0 deletions
diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c new file mode 100644 index 00000000..bab2adb5 --- /dev/null +++ b/src/sna/sna_accel.c @@ -0,0 +1,3306 @@ +/* + * 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 <X11/fonts/font.h> +#include <X11/fonts/fontstruct.h> + +#include <xaarop.h> +#include <fb.h> +#include <dixfontstr.h> + +#ifdef RENDER +#include <mipict.h> +#include <fbpict.h> +#endif + +#include <sys/time.h> +#include <sys/mman.h> +#include <unistd.h> + +#if DEBUG_ACCEL +#undef DBG +#define DBG(x) ErrorF x +#else +#define NDEBUG 1 +#endif + +DevPrivateKeyRec sna_pixmap_index; + +#define PM_IS_SOLID(_draw, _pm) \ + (((_pm) & FbFullMask((_draw)->depth)) == FbFullMask((_draw)->depth)) + +#if DEBUG_ACCEL +static void _assert_pixmap_contains_box(PixmapPtr pixmap, BoxPtr box, const char *function) +{ + if (box->x1 < 0 || box->y1 < 0 || + box->x2 > pixmap->drawable.width || + box->y2 > pixmap->drawable.height) + { + ErrorF("%s: damage box is beyond the pixmap: box=(%d, %d), (%d, %d), pixmap=(%d, %d)\n", + __FUNCTION__, + box->x1, box->y1, box->x2, box->y2, + pixmap->drawable.width, + pixmap->drawable.height); + assert(0); + } +} +#define assert_pixmap_contains_box(p, b) _assert_pixmap_contains_box(p, b, __FUNCTION__) +#else +#define assert_pixmap_contains_box(p, b) +#endif + +static void sna_pixmap_destroy_gpu_bo(struct sna *sna, struct sna_pixmap *priv) +{ + kgem_bo_destroy(&sna->kgem, priv->gpu_bo); + priv->gpu_bo = NULL; +} + +static Bool sna_destroy_private(PixmapPtr pixmap, struct sna_pixmap *priv) +{ + struct sna *sna = to_sna_from_drawable(&pixmap->drawable); + + list_del(&priv->list); + + sna_damage_destroy(&priv->gpu_damage); + sna_damage_destroy(&priv->cpu_damage); + + if (priv->mapped) + munmap(pixmap->devPrivate.ptr, priv->gpu_bo->size); + + /* Always release the gpu bo back to the lower levels of caching */ + if (priv->gpu_bo) + kgem_bo_destroy(&sna->kgem, priv->gpu_bo); + + if (priv->cpu_bo) { + if (pixmap->usage_hint != CREATE_PIXMAP_USAGE_SCRATCH_HEADER && + kgem_bo_is_busy(&sna->kgem, priv->cpu_bo)) { + list_add_tail(&priv->list, &sna->deferred_free); + return false; + } + kgem_bo_sync(&sna->kgem, priv->cpu_bo, true); + kgem_bo_destroy(&sna->kgem, priv->cpu_bo); + } + + free(priv); + return TRUE; +} + +static uint32_t sna_pixmap_choose_tiling(PixmapPtr pixmap) +{ + struct sna *sna = to_sna_from_drawable(&pixmap->drawable); + uint32_t tiling, bit; + + /* Use tiling by default, but disable per user request */ + tiling = I915_TILING_X; + bit = pixmap->usage_hint == SNA_CREATE_FB ? + SNA_TILING_FB : 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 sna_pixmap *sna_pixmap_attach(PixmapPtr pixmap) +{ + struct sna *sna; + struct sna_pixmap *priv; + + priv = sna_pixmap(pixmap); + if (priv) + return priv; + + switch (pixmap->usage_hint) { + case CREATE_PIXMAP_USAGE_GLYPH_PICTURE: + return NULL; + } + + sna = to_sna_from_drawable(&pixmap->drawable); + if (!kgem_can_create_2d(&sna->kgem, + pixmap->drawable.width, + pixmap->drawable.height, + pixmap->drawable.bitsPerPixel, + sna_pixmap_choose_tiling(pixmap))) + return NULL; + + priv = calloc(1, sizeof(*priv)); + if (!priv) + return NULL; + + list_init(&priv->list); + priv->pixmap = pixmap; + + sna_set_pixmap(pixmap, priv); + return priv; +} + +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); + PixmapPtr pixmap; + struct sna_pixmap *priv; + int bpp = BitsPerPixel(depth); + + DBG(("%s(%d, %d, %d, tiling=%d)\n", __FUNCTION__, + width, height, depth, tiling)); + + if (tiling == I915_TILING_Y && !sna->have_render) + tiling = I915_TILING_X; + + tiling = kgem_choose_tiling(&sna->kgem, tiling, width, height, bpp); + if (!kgem_can_create_2d(&sna->kgem, width, height, bpp, tiling)) + return fbCreatePixmap(screen, width, height, depth, + CREATE_PIXMAP_USAGE_SCRATCH); + + /* you promise never to access this via the cpu... */ + pixmap = fbCreatePixmap(screen, 0, 0, depth, + CREATE_PIXMAP_USAGE_SCRATCH); + if (!pixmap) + return NullPixmap; + + priv = malloc(sizeof(*priv)); + if (!priv) { + fbDestroyPixmap(pixmap); + return NullPixmap; + } + + priv->gpu_bo = kgem_create_2d(&sna->kgem, + width, height, bpp, tiling, + 0); + if (priv->gpu_bo == NULL) { + free(priv); + fbDestroyPixmap(pixmap); + return NullPixmap; + } + + priv->source_count = 0; + priv->cpu_bo = NULL; + priv->cpu_damage = priv->gpu_damage = NULL; + priv->gpu_only = 1; + priv->pinned = 0; + priv->mapped = 0; + list_init(&priv->list); + + priv->pixmap = pixmap; + sna_set_pixmap(pixmap, priv); + + miModifyPixmapHeader(pixmap, + width, height, depth, bpp, + priv->gpu_bo->pitch, NULL); + pixmap->devPrivate.ptr = NULL; + + return pixmap; +} + + +static PixmapPtr sna_create_pixmap(ScreenPtr screen, + int width, int height, int depth, + unsigned int usage) +{ + PixmapPtr pixmap; + + DBG(("%s(%d, %d, %d, usage=%x)\n", __FUNCTION__, + width, height, depth, usage)); + + if (usage == CREATE_PIXMAP_USAGE_SCRATCH && + to_sna_from_screen(screen)->have_render) + return sna_pixmap_create_scratch(screen, + width, height, depth, + I915_TILING_Y); + + /* XXX could use last deferred free? */ + + pixmap = fbCreatePixmap(screen, width, height, depth, usage); + if (pixmap == NullPixmap) + return NullPixmap; + +/* XXX if (pixmap->drawable.devKind * height > 128) */ + sna_pixmap_attach(pixmap); + return pixmap; +} + +static Bool sna_destroy_pixmap(PixmapPtr pixmap) +{ + if (pixmap->refcnt == 1) { + struct sna_pixmap *priv = sna_pixmap(pixmap); + if (priv) { + if (!sna_destroy_private(pixmap, priv)) + return TRUE; + } + } + + return fbDestroyPixmap(pixmap); +} + +static void sna_pixmap_map_to_cpu(struct sna *sna, + PixmapPtr pixmap, + struct sna_pixmap *priv) +{ + DBG(("%s: AWOOGA, AWOOGA!\n", __FUNCTION__)); + + if (priv->mapped == 0) { + ScreenPtr screen = pixmap->drawable.pScreen; + void *ptr; + + ptr = kgem_bo_map(&sna->kgem, + priv->gpu_bo, + PROT_READ | PROT_WRITE); + assert(ptr != NULL); + + screen->ModifyPixmapHeader(pixmap, + pixmap->drawable.width, + pixmap->drawable.height, + pixmap->drawable.depth, + pixmap->drawable.bitsPerPixel, + priv->gpu_bo->pitch, + ptr); + priv->mapped = 1; + } + kgem_bo_submit(&sna->kgem, priv->gpu_bo); +} + +static inline void list_move(struct list *list, struct list *head) +{ + __list_del(list->prev, list->next); + list_add(list, head); +} + +void +sna_pixmap_move_to_cpu(PixmapPtr pixmap, bool write) +{ + struct sna *sna = to_sna_from_drawable(&pixmap->drawable); + struct sna_pixmap *priv; + + DBG(("%s(pixmap=%p, write=%d)\n", __FUNCTION__, pixmap, write)); + + priv = sna_pixmap(pixmap); + if (priv == NULL) { + DBG(("%s: not attached to %p\n", __FUNCTION__, pixmap)); + return; + } + + DBG(("%s: gpu_bo=%p, gpu_damage=%p, gpu_only=%d\n", + __FUNCTION__, priv->gpu_bo, priv->gpu_damage, priv->gpu_only)); + + if (priv->gpu_bo == NULL) { + DBG(("%s: no GPU bo\n", __FUNCTION__)); + goto done; + } + + if (priv->gpu_only) { + sna_pixmap_map_to_cpu(sna, pixmap, priv); + goto done; + } + + 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) { + struct kgem_bo *dst_bo; + Bool ok = FALSE; + + dst_bo = NULL; + if (sna->kgem.gen >= 30) + dst_bo = pixmap_vmap(&sna->kgem, pixmap); + if (dst_bo) + ok = sna->render.copy_boxes(sna, GXcopy, + pixmap, priv->gpu_bo, 0, 0, + pixmap, dst_bo, 0, 0, + box, n); + if (!ok) + sna_read_boxes(sna, + priv->gpu_bo, 0, 0, + pixmap, 0, 0, + box, n); + } + + __sna_damage_destroy(priv->gpu_damage); + priv->gpu_damage = NULL; + } + +done: + if (priv->cpu_bo) { + DBG(("%s: syncing CPU bo\n", __FUNCTION__)); + kgem_bo_sync(&sna->kgem, priv->cpu_bo, write); + } + + if (write) { + DBG(("%s: marking as damaged\n", __FUNCTION__)); + sna_damage_all(&priv->cpu_damage, + pixmap->drawable.width, + pixmap->drawable.height); + + if (priv->gpu_bo && !priv->pinned) + sna_pixmap_destroy_gpu_bo(sna, priv); + + if (priv->flush) + list_move(&priv->list, &sna->dirty_pixmaps); + } +} + +static Bool +region_subsumes_drawable(RegionPtr region, DrawablePtr drawable) +{ + const BoxRec *extents; + + if (REGION_NUM_RECTS(region) != 1) + return false; + + extents = RegionExtents(region); + return extents->x1 <= 0 && extents->y1 <= 0 && + extents->x2 >= drawable->width && + extents->y2 >= drawable->height; +} + +void +sna_drawable_move_region_to_cpu(DrawablePtr drawable, + RegionPtr region, + Bool write) +{ + struct sna *sna = to_sna_from_drawable(drawable); + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna_pixmap *priv; + int16_t dx, dy; + + DBG(("%s(pixmap=%p, [(%d, %d), (%d, %d)], write=%d)\n", + __FUNCTION__, pixmap, + RegionExtents(region)->x1, RegionExtents(region)->y1, + RegionExtents(region)->x2, RegionExtents(region)->y2, + write)); + + priv = sna_pixmap(pixmap); + if (priv == NULL) { + DBG(("%s: not attached to %p\n", __FUNCTION__, pixmap)); + return; + } + + if (priv->gpu_only) { + DBG(("%s: gpu only\n", __FUNCTION__)); + return sna_pixmap_map_to_cpu(sna, pixmap, priv); + } + + 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, write); + } + +#if 0 + pixman_region_intersect_rect(region, region, + 0, 0, + pixmap->drawable.width, + pixmap->drawable.height); +#endif + + if (priv->gpu_bo == NULL) + goto done; + + if (sna_damage_contains_box(priv->gpu_damage, + REGION_EXTENTS(NULL, region))) { + RegionRec want, need, *r; + + DBG(("%s: region intersects gpu damage\n", __FUNCTION__)); + + r = region; + /* expand the region to move 32x32 pixel blocks at a time */ + if (priv->cpu_damage == NULL) { + 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); + } + } + + pixman_region_init(&need); + if (sna_damage_intersect(priv->gpu_damage, r, &need)) { + BoxPtr box = REGION_RECTS(&need); + int n = REGION_NUM_RECTS(&need); + struct kgem_bo *dst_bo; + Bool ok = FALSE; + + dst_bo = NULL; + if (sna->kgem.gen >= 30) + dst_bo = pixmap_vmap(&sna->kgem, pixmap); + if (dst_bo) + ok = sna->render.copy_boxes(sna, GXcopy, + pixmap, priv->gpu_bo, 0, 0, + pixmap, dst_bo, 0, 0, + box, n); + if (!ok) + sna_read_boxes(sna, + priv->gpu_bo, 0, 0, + pixmap, 0, 0, + box, n); + + sna_damage_subtract(&priv->gpu_damage, + n <= REGION_NUM_RECTS(r) ? &need : r); + RegionUninit(&need); + } + if (r == &want) + pixman_region_fini(&want); + } + +done: + if (priv->cpu_bo) { + DBG(("%s: syncing cpu bo\n", __FUNCTION__)); + kgem_bo_sync(&sna->kgem, priv->cpu_bo, write); + } + + if (write) { + DBG(("%s: applying cpu damage\n", __FUNCTION__)); + assert_pixmap_contains_box(pixmap, RegionExtents(region)); + sna_damage_add(&priv->cpu_damage, region); + if (priv->flush) + list_move(&priv->list, &sna->dirty_pixmaps); + } + + if (dx | dy) + RegionTranslate(region, -dx, -dy); +} + +static inline Bool +_sna_drawable_use_gpu_bo(DrawablePtr drawable, const BoxPtr box) +{ + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna_pixmap *priv = sna_pixmap(pixmap); + BoxRec extents; + int16_t dx, dy; + + if (priv == NULL) + return FALSE; + if (priv->gpu_bo == NULL) + return FALSE; + + if (priv->cpu_damage == NULL) + return TRUE; + + assert(!priv->gpu_only); + get_drawable_deltas(drawable, pixmap, &dx, &dy); + + extents = *box; + extents.x1 += dx; + extents.x2 += dx; + extents.y1 += dy; + extents.y2 += dy; + + return sna_damage_contains_box(priv->cpu_damage, + &extents) == PIXMAN_REGION_OUT; +} + +static inline Bool +sna_drawable_use_gpu_bo(DrawablePtr drawable, const BoxPtr box) +{ + Bool ret = _sna_drawable_use_gpu_bo(drawable, box); + DBG(("%s((%d, %d), (%d, %d)) = %d\n", __FUNCTION__, + box->x1, box->y1, box->x2, box->y2, ret)); + return ret; +} + +static inline Bool +_sna_drawable_use_cpu_bo(DrawablePtr drawable, const BoxPtr box) +{ + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna_pixmap *priv = sna_pixmap(pixmap); + BoxRec extents; + int16_t dx, dy; + + if (priv == NULL) + return FALSE; + if (priv->cpu_bo == NULL) + return FALSE; + + if (priv->gpu_damage == NULL) + return TRUE; + + get_drawable_deltas(drawable, pixmap, &dx, &dy); + + extents = *box; + extents.x1 += dx; + extents.x2 += dx; + extents.y1 += dy; + extents.y2 += dy; + + return sna_damage_contains_box(priv->gpu_damage, + &extents) == PIXMAN_REGION_OUT; +} + +static inline Bool +sna_drawable_use_cpu_bo(DrawablePtr drawable, const BoxPtr box) +{ + Bool ret = _sna_drawable_use_cpu_bo(drawable, box); + DBG(("%s((%d, %d), (%d, %d)) = %d\n", __FUNCTION__, + box->x1, box->y1, box->x2, box->y2, ret)); + return ret; +} + +PixmapPtr +sna_pixmap_create_upload(ScreenPtr screen, + int width, int height, int depth) +{ + struct sna *sna = to_sna_from_screen(screen); + PixmapPtr pixmap; + struct sna_pixmap *priv; + int bpp = BitsPerPixel(depth); + int pad = ALIGN(width * bpp / 8, 4); + void *ptr; + + DBG(("%s(%d, %d, %d)\n", __FUNCTION__, width, height, depth)); + assert(width); + assert(height); + if (!sna->have_render || + !kgem_can_create_2d(&sna->kgem, + width, height, bpp, + I915_TILING_NONE)) + return fbCreatePixmap(screen, width, height, depth, + CREATE_PIXMAP_USAGE_SCRATCH); + + pixmap = fbCreatePixmap(screen, 0, 0, depth, + CREATE_PIXMAP_USAGE_SCRATCH); + if (!pixmap) + return NullPixmap; + + priv = malloc(sizeof(*priv)); + if (!priv) { + fbDestroyPixmap(pixmap); + return NullPixmap; + } + + priv->gpu_bo = kgem_create_buffer(&sna->kgem, pad*height, true, &ptr); + if (!priv->gpu_bo) { + free(priv); + fbDestroyPixmap(pixmap); + return NullPixmap; + } + + priv->gpu_bo->pitch = pad; + + priv->source_count = SOURCE_BIAS; + priv->cpu_bo = NULL; + priv->cpu_damage = priv->gpu_damage = NULL; + priv->gpu_only = 0; + priv->pinned = 0; + priv->mapped = 0; + list_init(&priv->list); + + priv->pixmap = pixmap; + sna_set_pixmap(pixmap, priv); + + miModifyPixmapHeader(pixmap, width, height, depth, bpp, pad, ptr); + return pixmap; +} + +struct sna_pixmap * +sna_pixmap_force_to_gpu(PixmapPtr pixmap) +{ + struct sna_pixmap *priv; + + DBG(("%s(pixmap=%p)\n", __FUNCTION__, pixmap)); + + priv = sna_pixmap(pixmap); + if (priv == NULL) { + priv = sna_pixmap_attach(pixmap); + if (priv == NULL) + return NULL; + + DBG(("%s: created priv and marking all cpu damaged\n", + __FUNCTION__)); + sna_damage_all(&priv->cpu_damage, + pixmap->drawable.width, + pixmap->drawable.height); + } + + if (priv->gpu_bo == NULL) { + struct sna *sna = to_sna_from_drawable(&pixmap->drawable); + + priv->gpu_bo = kgem_create_2d(&sna->kgem, + pixmap->drawable.width, + pixmap->drawable.height, + pixmap->drawable.bitsPerPixel, + sna_pixmap_choose_tiling(pixmap), + 0); + if (priv->gpu_bo == NULL) + return NULL; + + DBG(("%s: created gpu bo\n", __FUNCTION__)); + } + + if (!sna_pixmap_move_to_gpu(pixmap)) + return NULL; + + return priv; +} + +struct sna_pixmap * +sna_pixmap_move_to_gpu(PixmapPtr pixmap) +{ + struct sna *sna = to_sna_from_drawable(&pixmap->drawable); + struct sna_pixmap *priv; + BoxPtr box; + int n; + + DBG(("%s()\n", __FUNCTION__)); + + priv = sna_pixmap(pixmap); + if (priv == NULL) + return NULL; + + sna_damage_reduce(&priv->cpu_damage); + DBG(("%s: CPU damage? %d\n", __FUNCTION__, priv->cpu_damage != NULL)); + + if (priv->gpu_bo == NULL) { + if (!sna->kgem.wedged) + priv->gpu_bo = + kgem_create_2d(&sna->kgem, + pixmap->drawable.width, + pixmap->drawable.height, + pixmap->drawable.bitsPerPixel, + sna_pixmap_choose_tiling(pixmap), + priv->cpu_damage ? CREATE_INACTIVE : 0); + if (priv->gpu_bo == NULL) { + assert(list_is_empty(&priv->list)); + return NULL; + } + } + + if (priv->cpu_damage == NULL) + goto done; + + n = sna_damage_get_boxes(priv->cpu_damage, &box); + if (n) { + struct kgem_bo *src_bo; + Bool ok = FALSE; + + src_bo = pixmap_vmap(&sna->kgem, pixmap); + if (src_bo) + ok = sna->render.copy_boxes(sna, GXcopy, + pixmap, src_bo, 0, 0, + pixmap, priv->gpu_bo, 0, 0, + box, n); + if (!ok) { + if (n == 1 && !priv->pinned && + box->x1 <= 0 && box->y1 <= 0 && + box->x2 >= pixmap->drawable.width && + box->y2 >= pixmap->drawable.height) { + priv->gpu_bo = + sna_replace(sna, + priv->gpu_bo, + pixmap->drawable.width, + pixmap->drawable.height, + pixmap->drawable.bitsPerPixel, + pixmap->devPrivate.ptr, + pixmap->devKind); + } else { + sna_write_boxes(sna, + priv->gpu_bo, 0, 0, + pixmap->devPrivate.ptr, + pixmap->devKind, + pixmap->drawable.bitsPerPixel, + 0, 0, + box, n); + } + } + } + + __sna_damage_destroy(priv->cpu_damage); + priv->cpu_damage = NULL; + +done: + list_del(&priv->list); + return priv; +} + +static void sna_gc_move_to_cpu(GCPtr gc) +{ + DBG(("%s\n", __FUNCTION__)); + + if (gc->stipple) + sna_drawable_move_to_cpu(&gc->stipple->drawable, false); + + if (gc->fillStyle == FillTiled) + sna_drawable_move_to_cpu(&gc->tile.pixmap->drawable, false); +} + +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) +{ + struct sna *sna = to_sna_from_drawable(drawable); + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna_pixmap *priv = sna_pixmap(pixmap); + struct kgem_bo *src_bo; + Bool ok = FALSE; + 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 (!priv->pinned && nbox == 1 && + box->x1 <= 0 && box->y1 <= 0 && + box->x2 >= pixmap->drawable.width && + box->y2 >= pixmap->drawable.height) { + priv->gpu_bo = + sna_replace(sna, priv->gpu_bo, + pixmap->drawable.width, + pixmap->drawable.height, + pixmap->drawable.bitsPerPixel, + bits, stride); + return TRUE; + } + + get_drawable_deltas(drawable, pixmap, &dx, &dy); + x += dx + drawable->x; + y += dy + drawable->y; + + src_bo = kgem_create_map(&sna->kgem, bits, stride*h, 1); + if (src_bo) { + src_bo->pitch = stride; + ok = sna->render.copy_boxes(sna, gc->alu, + pixmap, src_bo, -x, -y, + pixmap, priv->gpu_bo, 0, 0, + box, nbox); + kgem_bo_sync(&sna->kgem, src_bo, true); + kgem_bo_destroy(&sna->kgem, src_bo); + } + + if (!ok && gc->alu == GXcopy) { + sna_write_boxes(sna, + priv->gpu_bo, 0, 0, + bits, + stride, + pixmap->drawable.bitsPerPixel, + -x, -y, + box, nbox); + ok = TRUE; + } + + return ok; +} + +static Bool +sna_put_image_blt(DrawablePtr drawable, GCPtr gc, RegionPtr region, + int x, int y, int w, int h, char *bits, int stride) +{ + struct sna *sna = to_sna_from_drawable(drawable); + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna_pixmap *priv = sna_pixmap(pixmap); + int16_t dx, dy; + + if (!priv->gpu_bo) + return false; + + if (priv->gpu_only) + return sna_put_image_upload_blt(drawable, gc, region, + x, y, w, h, bits, stride); + + if (gc->alu != GXcopy) + return false; + + if (priv->cpu_bo) + kgem_bo_sync(&sna->kgem, priv->cpu_bo, true); + + if (region_subsumes_drawable(region, &pixmap->drawable)) { + sna_damage_destroy(&priv->gpu_damage); + sna_damage_all(&priv->cpu_damage, + pixmap->drawable.width, + pixmap->drawable.height); + if (priv->gpu_bo && !priv->pinned) + sna_pixmap_destroy_gpu_bo(sna, priv); + } else { + assert_pixmap_contains_box(pixmap, RegionExtents(region)); + sna_damage_subtract(&priv->gpu_damage, region); + sna_damage_add(&priv->cpu_damage, region); + } + + get_drawable_deltas(drawable, pixmap, &dx, &dy); + dx += drawable->x; + dy += drawable->y; + + DBG(("%s: fbPutZImage(%d[+%d], %d[+%d], %d, %d)\n", + __FUNCTION__, + x+dx, pixmap->drawable.x, + y+dy, pixmap->drawable.y, + w, h)); + fbPutZImage(&pixmap->drawable, region, + GXcopy, ~0U, + x + dx, y + dy, w, h, + (FbStip*)bits, stride/sizeof(FbStip)); + 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_pixmap *priv = sna_pixmap(pixmap); + RegionRec region, *clip; + BoxRec box; + int16_t dx, dy; + + DBG(("%s((%d, %d)x(%d, %d)\n", __FUNCTION__, x, y, w, h)); + + if (w == 0 || h == 0) + return; + + if (priv == NULL) { + fbPutImage(drawable, gc, depth, x, y, w, h, left, format, bits); + return; + } + + get_drawable_deltas(drawable, pixmap, &dx, &dy); + + box.x1 = x + drawable->x + dx; + box.y1 = y + drawable->y + dy; + box.x2 = box.x1 + w; + box.y2 = box.y1 + h; + + if (box.x1 < 0) + box.x1 = 0; + if (box.y1 < 0) + box.y1 = 0; + if (box.x2 > pixmap->drawable.width) + box.x2 = pixmap->drawable.width; + if (box.y2 > pixmap->drawable.height) + box.y2 = pixmap->drawable.height; + + RegionInit(®ion, &box, 1); + + clip = fbGetCompositeClip(gc); + if (clip) { + RegionTranslate(clip, dx, dy); + RegionIntersect(®ion, ®ion, clip); + RegionTranslate(clip, -dx, -dy); + } + + if (format != ZPixmap || + !PM_IS_SOLID(drawable, gc->planemask) || + !sna_put_image_blt(drawable, gc, ®ion, + x, y, w, h, + bits, PixmapBytePad(w, depth))) { + RegionTranslate(®ion, -dx, -dy); + + sna_drawable_move_region_to_cpu(drawable, ®ion, true); + DBG(("%s: fbPutImage(%d, %d, %d, %d)\n", + __FUNCTION__, x, y, w, h)); + fbPutImage(drawable, gc, depth, x, y, w, h, left, format, bits); + } + + RegionUninit(®ion); +} + +static void +sna_copy_boxes(DrawablePtr src, DrawablePtr dst, GCPtr gc, + BoxPtr box, int n, + int dx, int dy, + Bool reverse, Bool upsidedown, Pixel bitplane, + void *closure) +{ + struct sna *sna = to_sna_from_drawable(dst); + PixmapPtr src_pixmap = get_drawable_pixmap(src); + PixmapPtr dst_pixmap = get_drawable_pixmap(dst); + struct sna_pixmap *src_priv = sna_pixmap(src_pixmap); + struct sna_pixmap *dst_priv = sna_pixmap(dst_pixmap); + int alu = gc ? gc->alu : GXcopy; + int16_t src_dx, src_dy; + int16_t dst_dx, dst_dy; + int stride, bpp; + char *bits; + RegionRec region; + Bool replaces; + + if (n == 0) + return; + + 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)); + + pixman_region_init_rects(®ion, box, n); + + bpp = dst_pixmap->drawable.bitsPerPixel; + + get_drawable_deltas(dst, dst_pixmap, &dst_dx, &dst_dy); + get_drawable_deltas(src, src_pixmap, &src_dx, &src_dy); + src_dx += dx; + src_dy += dy; + + replaces = alu == GXcopy && n == 1 && + box->x1 + dst_dx <= 0 && + box->y1 + dst_dy <= 0 && + box->x2 + dst_dx >= dst_pixmap->drawable.width && + box->y2 + dst_dy >= 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)); + + /* Try to maintain the data on the GPU */ + if (dst_priv && dst_priv->gpu_bo == NULL && + src_priv && src_priv->gpu_bo != NULL && + alu == GXcopy) { + uint32_t tiling = + sna_pixmap_choose_tiling(dst_pixmap); + + DBG(("%s: create dst GPU bo for copy\n", __FUNCTION__)); + + if (!sna->kgem.wedged && + kgem_can_create_2d(&sna->kgem, + dst_pixmap->drawable.width, + dst_pixmap->drawable.height, + dst_pixmap->drawable.bitsPerPixel, + tiling)) + dst_priv->gpu_bo = + kgem_create_2d(&sna->kgem, + dst_pixmap->drawable.width, + dst_pixmap->drawable.height, + dst_pixmap->drawable.bitsPerPixel, + tiling, 0); + } + + if (dst_priv && dst_priv->gpu_bo) { + if (!src_priv && !dst_priv->gpu_only) { + DBG(("%s: fallback - src_priv=%p but dst gpu_only=%d\n", + __FUNCTION__, + src_priv, dst_priv->gpu_only)); + goto fallback; + } + + if (alu != GXcopy && !sna_pixmap_move_to_gpu(dst_pixmap)) { + DBG(("%s: fallback - not a pure copy and failed to move dst to GPU\n", + __FUNCTION__)); + goto fallback; + } + + if (src_priv && src_priv->gpu_bo && + sna_pixmap_move_to_gpu(src_pixmap)) { + if (!sna->render.copy_boxes(sna, alu, + src_pixmap, src_priv->gpu_bo, src_dx, src_dy, + dst_pixmap, dst_priv->gpu_bo, dst_dx, dst_dy, + box, n)) { + DBG(("%s: fallback - accelerated copy boxes failed\n", + __FUNCTION__)); + goto fallback; + } + + if (replaces) { + sna_damage_destroy(&dst_priv->cpu_damage); + sna_damage_all(&dst_priv->gpu_damage, + dst_pixmap->drawable.width, + dst_pixmap->drawable.height); + } else { + RegionTranslate(®ion, dst_dx, dst_dy); + assert_pixmap_contains_box(dst_pixmap, + RegionExtents(®ion)); + sna_damage_add(&dst_priv->gpu_damage, ®ion); + if (alu == GXcopy) + sna_damage_subtract(&dst_priv->cpu_damage, + ®ion); + RegionTranslate(®ion, -dst_dx, -dst_dy); + } + } else { + if (alu != GXcopy) { + DBG(("%s: fallback - not a copy and source is on the CPU\n", + __FUNCTION__)); + goto fallback; + } + + if (src_priv) { + RegionTranslate(®ion, src_dx, src_dy); + sna_drawable_move_region_to_cpu(&src_pixmap->drawable, + ®ion, false); + RegionTranslate(®ion, -src_dx, -src_dy); + } + + if (!dst_priv->pinned && replaces) { + stride = src_pixmap->devKind; + bits = src_pixmap->devPrivate.ptr; + bits += src_dy * stride + src_dx * bpp / 8; + + dst_priv->gpu_bo = + sna_replace(sna, + dst_priv->gpu_bo, + dst_pixmap->drawable.width, + dst_pixmap->drawable.height, + bpp, bits, stride); + + sna_damage_destroy(&dst_priv->cpu_damage); + sna_damage_all(&dst_priv->gpu_damage, + dst_pixmap->drawable.width, + dst_pixmap->drawable.height); + } else { + DBG(("%s: dst is on the GPU, src is on the CPU, uploading\n", + __FUNCTION__)); + sna_write_boxes(sna, + dst_priv->gpu_bo, dst_dx, dst_dy, + src_pixmap->devPrivate.ptr, + src_pixmap->devKind, + src_pixmap->drawable.bitsPerPixel, + src_dx, src_dy, + box, n); + + RegionTranslate(®ion, dst_dx, dst_dy); + assert_pixmap_contains_box(dst_pixmap, + RegionExtents(®ion)); + sna_damage_add(&dst_priv->gpu_damage, + ®ion); + sna_damage_subtract(&dst_priv->cpu_damage, + ®ion); + RegionTranslate(®ion, -dst_dx, -dst_dy); + } + } + } else { + FbBits *dst_bits, *src_bits; + int dst_stride, src_stride; + +fallback: + DBG(("%s: fallback -- src=(%d, %d), dst=(%d, %d)\n", + __FUNCTION__, src_dx, src_dy, dst_dx, dst_dy)); + if (src_priv) { + RegionTranslate(®ion, src_dx, src_dy); + sna_drawable_move_region_to_cpu(&src_pixmap->drawable, + ®ion, false); + RegionTranslate(®ion, -src_dx, -src_dy); + } + + RegionTranslate(®ion, dst_dx, dst_dy); + if (dst_priv) { + if (alu == GXcopy) { + if (replaces) { + sna_damage_destroy(&dst_priv->gpu_damage); + sna_damage_all(&dst_priv->cpu_damage, + dst_pixmap->drawable.width, + dst_pixmap->drawable.height); + if (dst_priv->gpu_bo && !dst_priv->pinned) + sna_pixmap_destroy_gpu_bo(sna, dst_priv); + } else { + assert_pixmap_contains_box(dst_pixmap, + RegionExtents(®ion)); + sna_damage_subtract(&dst_priv->gpu_damage, + ®ion); + sna_damage_add(&dst_priv->cpu_damage, + ®ion); + } + } else + sna_drawable_move_region_to_cpu(&dst_pixmap->drawable, + ®ion, true); + } + + dst_stride = dst_pixmap->devKind; + src_stride = src_pixmap->devKind; + + dst_bits = (FbBits *) + ((char *)dst_pixmap->devPrivate.ptr + + dst_dy * dst_stride + dst_dx * bpp / 8); + src_bits = (FbBits *) + ((char *)src_pixmap->devPrivate.ptr + + src_dy * src_stride + src_dx * bpp / 8); + + if (alu == GXcopy && !reverse && !upsidedown && bpp >= 8) { + do { + DBG(("%s: memcpy_blt(box=(%d, %d), (%d, %d), src=(%d, %d), dst=(%d, %d), pitches=(%d, %d))\n", + __FUNCTION__, + box->x1, box->y1, + box->x2 - box->x1, + box->y2 - box->y1, + src_dx, src_dy, + dst_dx, dst_dy, + src_stride, dst_stride)); + 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 { + dst_stride /= sizeof(FbBits); + src_stride /= sizeof(FbBits); + do { + DBG(("%s: fbBlt (%d, %d), (%d, %d)\n", + __FUNCTION__, + box->x1, box->y1, + box->x2, box->y2)); + fbBlt(src_bits + box->y1 * src_stride, + src_stride, + box->x1 * bpp, + + dst_bits + box->y1 * dst_stride, + dst_stride, + box->x1 * bpp, + + (box->x2 - box->x1) * bpp, + (box->y2 - box->y1), + + alu, -1, bpp, + reverse, upsidedown); + box++; + }while (--n); + } + } + RegionUninit(®ion); +} + +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); + + DBG(("%s: src=(%d, %d)x(%d, %d) -> dst=(%d, %d)\n", + __FUNCTION__, src_x, src_y, width, height, dst_x, dst_y)); + + if (sna->kgem.wedged || + src->bitsPerPixel != dst->bitsPerPixel || + !PM_IS_SOLID(dst, gc->planemask)) { + BoxRec box; + RegionRec region; + + box.x1 = dst_x + dst->x; + box.y1 = dst_y + dst->y; + box.x2 = box.x1 + width; + box.y2 = box.y1 + height; + RegionInit(®ion, &box, 1); + + if (gc->pCompositeClip) + RegionIntersect(®ion, ®ion, gc->pCompositeClip); + + sna_drawable_move_region_to_cpu(dst, ®ion, true); + RegionTranslate(®ion, + src_x - dst_x - dst->x + src->x, + src_y - dst_y - dst->y + src->y); + sna_drawable_move_region_to_cpu(src, ®ion, false); + + return fbCopyArea(src, dst, gc, + src_x, src_y, + width, height, + dst_x, dst_y); + } + + return miDoCopy(src, dst, gc, + src_x, src_y, + width, height, + dst_x, dst_y, + sna_copy_boxes, 0, NULL); +} + +#define TRIM_BOX(box, d) do { \ + if (box.x1 < 0) box.x1 = 0; \ + if (box.x2 > d->width) box.x2 = d->width; \ + if (box.y1 < 0) box.y1 = 0; \ + if (box.y2 > d->height) box.y2 = d->height; \ +} while (0) + +#define CLIP_BOX(box, gc) \ + if (gc->pCompositeClip) { \ + BoxPtr extents = &gc->pCompositeClip->extents;\ + if (box.x1 < extents->x1) box.x1 = extents->x1; \ + if (box.x2 > extents->x2) box.x2 = extents->x2; \ + if (box.y1 < extents->y1) box.y1 = extents->y1; \ + if (box.y2 > extents->y2) box.y2 = extents->y2; \ + } + +#define TRANSLATE_BOX(box, d) do { \ + box.x1 += d->x; \ + box.x2 += d->x; \ + box.y1 += d->y; \ + box.y2 += d->y; \ +} while (0) + +#define TRIM_AND_TRANSLATE_BOX(box, d, gc) do { \ + TRIM_BOX(box, d); \ + TRANSLATE_BOX(box, d); \ + CLIP_BOX(box, gc); \ +} while (0) + +#define BOX_ADD_PT(box, x, y) do { \ + 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; \ +} while (0) + +#define BOX_ADD_RECT(box, x, y, w, h) do { \ + if (box.x1 > x) box.x1 = x; \ + else if (box.x2 < x + w) box.x2 = x + w; \ + if (box.y1 > y) box.y1 = y; \ + else if (box.y2 < y + h) box.y2 = y + h; \ +} while (0) + +#define BOX_EMPTY(box) (box.x2 <= box.x1 || box.y2 <= box.y1) + +static Bool +box_intersect(BoxPtr a, const BoxPtr 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 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) +{ + memset(fill, 0, sizeof(*fill)); + 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 Bool +sna_fill_spans_blt(DrawablePtr drawable, + struct kgem_bo *bo, struct sna_damage **damage, + GCPtr gc, int n, + DDXPointPtr pt, int *width, int sorted) +{ + struct sna *sna = to_sna_from_drawable(drawable); + PixmapPtr pixmap = get_drawable_pixmap(drawable); + struct sna_fill_op fill; + BoxPtr extents, clip; + int nclip; + int16_t dx, dy; + + if (!sna_fill_init_blt(&fill, sna, pixmap, bo, gc->alu, gc->fgPixel)) + return false; + + extents = REGION_EXTENTS(gc->screen, gc->pCompositeClip); + DBG(("%s: clip %d x [(%d, %d), (%d, %d)] x %d [(%d, %d)...]\n", + __FUNCTION__, + REGION_NUM_RECTS(gc->pCompositeClip), + extents->x1, extents->y1, extents->x2, extents->y2, + n, pt->x, pt->y)); + + get_drawable_deltas(drawable, pixmap, &dx, &dy); + while (n--) { + int X1 = pt->x; + int y = pt->y; + int X2 = X1 + (int)*width; + + if (!gc->miTranslate) { + X1 += drawable->x; + X2 += drawable->x; + y += drawable->y; + } + + 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; + + nclip = REGION_NUM_RECTS(gc->pCompositeClip); + if (nclip == 1) { + X1 += dx; + if (X1 < 0) + X1 = 0; + X2 += dx; + if (X2 > pixmap->drawable.width) + X2 = pixmap->drawable.width; + if (X2 > X1) { + fill.blt(sna, &fill, X1, y+dy, X2-X1, 1); + if (damage) { + BoxRec box; + + box.x1 = X1; + box.x2 = X2; + box.y1 = y + dy; + box.y2 = box.y1 + 1; + + assert_pixmap_contains_box(pixmap, &box); + sna_damage_add_box(damage, &box); + } + } + } else { + clip = REGION_RECTS(gc->pCompositeClip); + while (nclip--) { + if (clip->y1 <= y && y < clip->y2) { + int x1 = clip->x1; + int x2 = clip->x2; + + if (x1 < X1) + x1 = X1; + x1 += dx; + if (x1 < 0) + x1 = 0; + if (x2 > X2) + x2 = X2; + x2 += dx; + if (x2 > pixmap->drawable.width) + x2 = pixmap->drawable.width; + + if (x2 > x1) { + fill.blt(sna, &fill, + x1, y + dy, + x2-x1, 1); + if (damage) { + BoxRec box; + + box.x1 = x1; + box.y1 = y + dy; + box.x2 = x2; + box.y2 = box.y1 + 1; + + assert_pixmap_contains_box(pixmap, &box); + sna_damage_add_box(damage, &box); + } + } + } + clip++; + } + } + } + fill.done(sna, &fill); + return TRUE; +} + +static Bool +sna_spans_extents(DrawablePtr drawable, GCPtr gc, + int n, DDXPointPtr pt, int *width, + BoxPtr out) +{ + BoxRec box; + + if (n == 0) + return true; + + 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) { + if (!gc->miTranslate) + TRANSLATE_BOX(box, drawable); + CLIP_BOX(box, gc); + } + *out = box; + return BOX_EMPTY(box); +} + +static void +sna_fill_spans(DrawablePtr drawable, GCPtr gc, int n, + DDXPointPtr pt, int *width, int sorted) +{ + struct sna *sna = to_sna_from_drawable(drawable); + BoxRec extents; + RegionRec region; + + DBG(("%s(n=%d, pt[0]=(%d, %d)\n", + __FUNCTION__, n, pt[0].x, pt[0].y)); + + if (sna_spans_extents(drawable, gc, n, pt, width, &extents)) + return; + + DBG(("%s: extents (%d, %d), (%d, %d)\n", __FUNCTION__, + extents.x1, extents.y1, extents.x2, extents.y2)); + + if (sna->kgem.wedged) + goto fallback; + + if (gc->fillStyle == FillSolid && + PM_IS_SOLID(drawable, gc->planemask)) { + struct sna_pixmap *priv = sna_pixmap_from_drawable(drawable); + + DBG(("%s: trying solid fill [alu=%d, pixel=%08lx] blt paths\n", + __FUNCTION__, gc->alu, gc->fgPixel)); + + if (sna_drawable_use_gpu_bo(drawable, &extents) && + sna_fill_spans_blt(drawable, + priv->gpu_bo, + priv->gpu_only ? NULL : &priv->gpu_damage, + gc, n, pt, width, sorted)) + return; + + if (sna_drawable_use_cpu_bo(drawable, &extents) && + sna_fill_spans_blt(drawable, + priv->cpu_bo, &priv->cpu_damage, + gc, n, pt, width, sorted)) + return; + } + +fallback: + DBG(("%s: fallback\n", __FUNCTION__)); + RegionInit(®ion, &extents, 1); + if (gc->pCompositeClip) + RegionIntersect(®ion, ®ion, gc->pCompositeClip); + if (!RegionNotEmpty(®ion)) + return; + + sna_gc_move_to_cpu(gc); + sna_drawable_move_region_to_cpu(drawable, ®ion, true); + RegionUninit(®ion); + + fbFillSpans(drawable, gc, n, pt, width, sorted); +} + +static void +sna_set_spans(DrawablePtr drawable, GCPtr gc, char *src, + DDXPointPtr pt, int *width, int n, int sorted) +{ + BoxRec extents; + RegionRec region; + + if (sna_spans_extents(drawable, gc, n, pt, width, &extents)) + return; + + DBG(("%s: extents=(%d, %d), (%d, %d)\n", __FUNCTION__, + extents.x1, extents.y1, extents.x2, extents.y2)); + + RegionInit(®ion, &extents, 1); + if (gc->pCompositeClip) + RegionIntersect(®ion, ®ion, gc->pCompositeClip); + if (!RegionNotEmpty(®ion)) + return; + + sna_drawable_move_region_to_cpu(drawable, ®ion, true); + RegionUninit(®ion); + + fbSetSpans(drawable, gc, src, pt, width, n, sorted); +} + +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) +{ + BoxRec box; + RegionRec region; + + DBG(("%s: src=(%d, %d), dst=(%d, %d), size=%dx%d\n", __FUNCTION__, + src_x, src_y, dst_x, dst_y, w, h)); + + box.x1 = dst_x + dst->x; + box.y1 = dst_y + dst->y; + box.x2 = box.x1 + w; + box.y2 = box.y1 + h; + + RegionInit(®ion, &box, 1); + if (gc->pCompositeClip) + RegionIntersect(®ion, ®ion, gc->pCompositeClip); + + sna_drawable_move_region_to_cpu(dst, ®ion, true); + RegionTranslate(®ion, + src_x - dst_x - dst->x + src->x, + src_y - dst_y - dst->y + src->y); + sna_drawable_move_region_to_cpu(src, ®ion, false); + + return fbCopyPlane(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) +{ + struct sna *sna = to_sna_from_drawable(drawable); + PixmapPtr pixmap = get_drawable_pixmap(drawable); + RegionPtr clip = fbGetCompositeClip(gc); + struct sna_fill_op fill; + DDXPointRec last; + int16_t dx, dy; + + DBG(("%s: alu=%d, pixel=%08lx\n", __FUNCTION__, gc->alu, gc->fgPixel)); + + 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; + + while (n--) { + int x, y; + + x = pt->x; + y = pt->y; + pt++; + if (mode == CoordModePrevious) { + x += last.x; + y += last.x; + last.x = x; + last.y = y; + } else { + x += drawable->x; + y += drawable->y; + } + + if (RegionContainsPoint(clip, x, y, NULL)) { + fill.blt(sna, &fill, x + dx, y + dy, 1, 1); + if (damage) { + BoxRec box; + + box.x1 = x + dx; + box.y1 = x + dx; + box.x2 = box.x1 + 1; + box.y2 = box.y1 + 1; + + assert_pixmap_contains_box(pixmap, &box); + sna_damage_add_box(damage, &box); + } + } + } + fill.done(sna, &fill); + return TRUE; +} + +static Bool +sna_poly_point_extents(DrawablePtr drawable, GCPtr gc, + int mode, int n, DDXPointPtr pt, BoxPtr out) +{ + BoxRec box; + + if (n == 0) + return true; + + box.x2 = box.x1 = pt->x; + box.y2 = box.y1 = pt->y; + while (--n) { + pt++; + BOX_ADD_PT(box, pt->x, pt->y); + } + box.x2++; + box.y2++; + + TRIM_AND_TRANSLATE_BOX(box, drawable, gc); + *out = box; + return BOX_EMPTY(box); +} + +static void +sna_poly_point(DrawablePtr drawable, GCPtr gc, + int mode, int n, DDXPointPtr pt) +{ + struct sna *sna = to_sna_from_drawable(drawable); + BoxRec extents; + RegionRec region; + + DBG(("%s(mode=%d, n=%d, pt[0]=(%d, %d)\n", + __FUNCTION__, mode, n, pt[0].x, pt[0].y)); + + if (sna_poly_point_extents(drawable, gc, mode, n, pt, &extents)) + return; + + DBG(("%s: extents (%d, %d), (%d, %d)\n", __FUNCTION__, + extents.x1, extents.y1, extents.x2, extents.y2)); + + if (sna->kgem.wedged) + goto fallback; + + if (gc->fillStyle == FillSolid && + PM_IS_SOLID(drawable, gc->planemask)) { + struct sna_pixmap *priv = sna_pixmap_from_drawable(drawable); + + DBG(("%s: trying solid fill [%08lx] blt paths\n", + __FUNCTION__, gc->fgPixel)); + + if (sna_drawable_use_gpu_bo(drawable, &extents) && + sna_poly_point_blt(drawable, + priv->gpu_bo, + priv->gpu_only ? NULL : &priv->gpu_damage, + gc, mode, n, pt)) + return; + + if (sna_drawable_use_cpu_bo(drawable, &extents) && + sna_poly_point_blt(drawable, + priv->cpu_bo, + &priv->cpu_damage, + gc, mode, n, pt)) + return; + } + +fallback: + DBG(("%s: fallback\n", __FUNCTION__)); + RegionInit(®ion, &extents, 1); + if (gc->pCompositeClip) + RegionIntersect(®ion, ®ion, gc->pCompositeClip); + if (!RegionNotEmpty(®ion)) + return; + + sna_drawable_move_region_to_cpu(drawable, ®ion, true); + RegionUninit(®ion); + + fbPolyPoint(drawable, gc, mode, n, pt); +} + +static Bool +sna_poly_line_can_blt(int mode, int n, DDXPointPtr pt) +{ + int i; + + if (mode == CoordModePrevious) { + for (i = 1; i < n; i++) { + if (pt[i].x != 0 && pt[i].y != 0) + return FALSE; + } + } else { + for (i = 1; i < n; i++) { + if (pt[i].x != pt[i-1].x && pt[i].y != pt[i-1].y) + return FALSE; + } + } + + return TRUE; +} + +static Bool +sna_poly_line_blt(DrawablePtr drawable, + struct kgem_bo *bo, + struct sna_damage **damage, + GCPtr gc, int mode, int n, DDXPointPtr pt) +{ + struct sna *sna = to_sna_from_drawable(drawable); + PixmapPtr pixmap = get_drawable_pixmap(drawable); + RegionPtr clip = fbGetCompositeClip(gc); + struct sna_fill_op fill; + DDXPointRec last; + int16_t dx, dy; + int first; + + DBG(("%s: alu=%d, fg=%08lx\n", __FUNCTION__, gc->alu, gc->fgPixel)); + + 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; + first = 1; + + while (n--) { + int nclip; + BoxPtr box; + int x, y; + + x = pt->x; + y = pt->y; + pt++; + if (mode == CoordModePrevious) { + x += last.x; + y += last.x; + } else { + x += drawable->x; + y += drawable->y; + } + + if (!first) { + for (nclip = REGION_NUM_RECTS(clip), box = REGION_RECTS(clip); nclip--; box++) { + BoxRec r; + + if (last.x == x) { + r.x1 = last.x; + r.x2 = last.x + 1; + } else { + r.x1 = last.x < x ? last.x : x; + r.x2 = last.x > x ? last.x : x; + } + if (last.y == y) { + r.y1 = last.y; + r.y2 = last.y + 1; + } else { + r.y1 = last.y < y ? last.y : y; + r.y2 = last.y > y ? last.y : y; + } + DBG(("%s: (%d, %d) -> (%d, %d) clipping line (%d, %d), (%d, %d) against box (%d, %d), (%d, %d)\n", + __FUNCTION__, + last.x, last.y, x, y, + r.x1, r.y1, r.x2, r.y2, + box->x1, box->y1, box->x2, box->y2)); + if (box_intersect(&r, box)) { + r.x1 += dx; + r.x2 += dx; + r.y1 += dy; + r.y2 += dy; + DBG(("%s: blt (%d, %d), (%d, %d)\n", + __FUNCTION__, + r.x1, r.y1, r.x2, r.y2)); + fill.blt(sna, &fill, + r.x1, r.y1, + r.x2-r.x1, r.y2-r.y1); + if (damage) { + assert_pixmap_contains_box(pixmap, &r); + sna_damage_add_box(damage, &r); + } + } + } + } + + last.x = x; + last.y = y; + first = 0; + } + fill.done(sna, &fill); + return TRUE; +} + +static Bool +sna_poly_line_extents(DrawablePtr drawable, GCPtr gc, + int mode, int n, DDXPointPtr pt, + BoxPtr out) +{ + BoxRec box; + int extra = gc->lineWidth >> 1; + + if (n == 0) + return true; + + if (n > 1) { + if (gc->joinStyle == JoinMiter) + extra = 6 * gc->lineWidth; + else if (gc->capStyle == CapProjecting) + extra = gc->lineWidth; + } + + 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; + BOX_ADD_PT(box, x, y); + } + } else { + while (--n) { + pt++; + BOX_ADD_PT(box, pt->x, pt->y); + } + } + box.x2++; + box.y2++; + + if (extra) { + box.x1 -= extra; + box.x2 += extra; + box.y1 -= extra; + box.y2 += extra; + } + + TRIM_AND_TRANSLATE_BOX(box, drawable, gc); + *out = box; + return BOX_EMPTY(box); +} + +static void +sna_poly_line(DrawablePtr drawable, GCPtr gc, + int mode, int n, DDXPointPtr pt) +{ + struct sna *sna = to_sna_from_drawable(drawable); + BoxRec extents; + RegionRec region; + + DBG(("%s(mode=%d, n=%d, pt[0]=(%d, %d)\n", + __FUNCTION__, mode, n, pt[0].x, pt[0].y)); + + if (sna_poly_line_extents(drawable, gc, mode, n, pt, &extents)) + return; + + DBG(("%s: extents (%d, %d), (%d, %d)\n", __FUNCTION__, + extents.x1, extents.y1, extents.x2, extents.y2)); + + if (sna->kgem.wedged) + goto fallback; + + if (gc->fillStyle == FillSolid && + gc->lineStyle == LineSolid && + (gc->lineWidth == 0 || gc->lineWidth == 1) && + PM_IS_SOLID(drawable, gc->planemask) && + sna_poly_line_can_blt(mode, n, pt)) { + struct sna_pixmap *priv = sna_pixmap_from_drawable(drawable); + + DBG(("%s: trying solid fill [%08lx]\n", + __FUNCTION__, gc->fgPixel)); + + if (sna_drawable_use_gpu_bo(drawable, &extents) && + sna_poly_line_blt(drawable, + priv->gpu_bo, + priv->gpu_only ? NULL : &priv->gpu_damage, + gc, mode, n, pt)) + return; + + if (sna_drawable_use_cpu_bo(drawable, &extents) && + sna_poly_line_blt(drawable, + priv->cpu_bo, + &priv->cpu_damage, + gc, mode, n, pt)) + return; + } + +fallback: + DBG(("%s: fallback\n", __FUNCTION__)); + RegionInit(®ion, &extents, 1); + if (gc->pCompositeClip) + RegionIntersect(®ion, ®ion, gc->pCompositeClip); + if (!RegionNotEmpty(®ion)) + return; + + sna_gc_move_to_cpu(gc); + sna_drawable_move_region_to_cpu(drawable, ®ion, true); + RegionUninit(®ion); + + fbPolyLine(drawable, gc, mode, n, pt); +} + +static Bool +sna_poly_segment_can_blt(int n, xSegment *seg) +{ + while (n--) { + if (seg->x1 != seg->x2 && seg->y1 != seg->y2) + return FALSE; + + seg++; + } + + return TRUE; +} + +static Bool +sna_poly_segment_blt(DrawablePtr drawable, + struct kgem_bo *bo, + struct sna_damage **damage, + GCPtr gc, int n, xSegment *seg) +{ + struct sna *sna = to_sna_from_drawable(drawable); + PixmapPtr pixmap = get_drawable_pixmap(drawable); + RegionPtr clip = fbGetCompositeClip(gc); + struct sna_fill_op fill; + int16_t dx, dy; + + DBG(("%s: alu=%d, fg=%08lx\n", __FUNCTION__, gc->alu, gc->fgPixel)); + + if (!sna_fill_init_blt(&fill, sna, pixmap, bo, gc->alu, gc->fgPixel)) + return FALSE; + + get_drawable_deltas(drawable, pixmap, &dx, &dy); + while (n--) { + int x, y, width, height, nclip; + BoxPtr box; + + if (seg->x1 < seg->x2) { + x = seg->x1; + width = seg->x2; + } else { + x = seg->x2; + width = seg->x1; + } + width -= x - 1; + x += drawable->x; + + if (seg->y1 < seg->y2) { + y = seg->y1; + height = seg->y2; + } else { + y = seg->y2; + height = seg->y1; + } + height -= y - 1; + y += drawable->y; + + /* don't paint last pixel */ + if (gc->capStyle == CapNotLast) { + if (width == 1) + height--; + else + width--; + } + + DBG(("%s: [%d] (%d, %d)x(%d, %d) + (%d, %d)\n", __FUNCTION__, n, + x, y, width, height, dx, dy)); + for (nclip = REGION_NUM_RECTS(clip), box = REGION_RECTS(clip); nclip--; box++) { + BoxRec r = { x, y, x + width, y + height }; + if (box_intersect(&r, box)) { + r.x1 += dx; + r.x2 += dx; + r.y1 += dy; + r.y2 += dy; + fill.blt(sna, &fill, + r.x1, r.y1, + r.x2-r.x1, r.y2-r.y1); + if (damage) { + assert_pixmap_contains_box(pixmap, &r); + sna_damage_add_box(damage, &r); + } + } + } + + seg++; + } + fill.done(sna, &fill); + return TRUE; +} + +static Bool +sna_poly_segment_extents(DrawablePtr drawable, GCPtr gc, + int n, xSegment *seg, + BoxPtr out) +{ + BoxRec box; + int extra = gc->lineWidth; + + if (n == 0) + return true; + + if (gc->capStyle != CapProjecting) + extra >>= 1; + + 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; + } + + 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; + } + } + + box.x2++; + box.y2++; + + if (extra) { + box.x1 -= extra; + box.x2 += extra; + box.y1 -= extra; + box.y2 += extra; + } + + TRIM_AND_TRANSLATE_BOX(box, drawable, gc); + *out = box; + return BOX_EMPTY(box); +} + +static void +sna_poly_segment(DrawablePtr drawable, GCPtr gc, int n, xSegment *seg) +{ + struct sna *sna = to_sna_from_drawable(drawable); + BoxRec extents; + RegionRec region; + + DBG(("%s(n=%d, first=((%d, %d), (%d, %d))\n", __FUNCTION__, + n, seg->x1, seg->y1, seg->x2, seg->y2)); + + if (sna_poly_segment_extents(drawable, gc, n, seg, &extents)) + return; + + DBG(("%s: extents=(%d, %d), (%d, %d)\n", __FUNCTION__, + extents.x1, extents.y1, extents.x2, extents.y2)); + + if (sna->kgem.wedged) + goto fallback; + + if (gc->fillStyle == FillSolid && + gc->lineStyle == LineSolid && + gc->lineWidth == 0 && + PM_IS_SOLID(drawable, gc->planemask) && + sna_poly_segment_can_blt(n, seg)) { + struct sna_pixmap *priv = sna_pixmap_from_drawable(drawable); + + DBG(("%s: trying blt solid fill [%08lx] paths\n", + __FUNCTION__, gc->fgPixel)); + + if (sna_drawable_use_gpu_bo(drawable, &extents) && + sna_poly_segment_blt(drawable, + priv->gpu_bo, + priv->gpu_only ? NULL : &priv->gpu_damage, + gc, n, seg)) + return; + + if (sna_drawable_use_cpu_bo(drawable, &extents) && + sna_poly_segment_blt(drawable, + priv->cpu_bo, + &priv->cpu_damage, + gc, n, seg)) + return; + } + +fallback: + DBG(("%s: fallback\n", __FUNCTION__)); + RegionInit(®ion, &extents, 1); + if (gc->pCompositeClip) + RegionIntersect(®ion, ®ion, gc->pCompositeClip); + if (!RegionNotEmpty(®ion)) + return; + + sna_gc_move_to_cpu(gc); + sna_drawable_move_region_to_cpu(drawable, ®ion, true); + RegionUninit(®ion); + + fbPolySegment(drawable, gc, n, seg); +} + +static Bool +sna_poly_arc_extents(DrawablePtr drawable, GCPtr gc, + int n, xArc *arc, + BoxPtr out) +{ + int extra = gc->lineWidth >> 1; + BoxRec box; + + if (n == 0) + return true; + + box.x1 = arc->x; + box.x2 = box.x1 + arc->width; + box.y1 = arc->y; + box.y2 = box.y1 + arc->height; + + while (--n) { + arc++; + if (box.x1 > arc->x) + box.x1 = arc->x; + if (box.x2 < arc->x + arc->width) + box.x2 = arc->x + arc->width; + if (box.y1 > arc->y) + box.y1 = arc->y; + if (box.y2 < arc->y + arc->height) + box.y2 = arc->y + arc->height; + } + + if (extra) { + box.x1 -= extra; + box.x2 += extra; + box.y1 -= extra; + box.y2 += extra; + } + + box.x2++; + box.y2++; + + TRIM_AND_TRANSLATE_BOX(box, drawable, gc); + *out = box; + return BOX_EMPTY(box); +} + +static void +sna_poly_arc(DrawablePtr drawable, GCPtr gc, int n, xArc *arc) +{ + BoxRec extents; + RegionRec region; + + if (sna_poly_arc_extents(drawable, gc, n, arc, &extents)) + return; + + DBG(("%s: extents=(%d, %d), (%d, %d)\n", __FUNCTION__, + extents.x1, extents.y1, extents.x2, extents.y2)); + + RegionInit(®ion, &extents, 1); + if (gc->pCompositeClip) + RegionIntersect(®ion, ®ion, gc->pCompositeClip); + if (!RegionNotEmpty(®ion)) + return; + + sna_gc_move_to_cpu(gc); + sna_drawable_move_region_to_cpu(drawable, ®ion, true); + RegionUninit(®ion); + + fbPolyArc(drawable, gc, n, arc); +} + +static Bool +sna_poly_fill_rect_blt(DrawablePtr drawable, + struct kgem_bo *bo, + struct sna_damage **damage, + GCPtr gc, int n, + xRectangle *rect) +{ + struct sna *sna = to_sna_from_drawable(drawable); + PixmapPtr pixmap = get_drawable_pixmap(drawable); + RegionPtr clip = fbGetCompositeClip(gc); + struct sna_fill_op fill; + uint32_t pixel = gc->fillStyle == FillSolid ? gc->fgPixel : gc->tile.pixel; + int16_t dx, dy; + + DBG(("%s x %d [(%d, %d)+(%d, %d)...]\n", + __FUNCTION__, n, rect->x, rect->y, rect->width, rect->height)); + + 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 (REGION_NUM_RECTS(clip) == 1) { + BoxPtr box = REGION_RECTS(clip); + while (n--) { + BoxRec r; + + r.x1 = rect->x + drawable->x; + r.y1 = rect->y + drawable->y; + r.x2 = r.x1 + rect->width; + r.y2 = r.y1 + rect->height; + rect++; + + if (box_intersect(&r, box)) { + r.x1 += dx; + r.x2 += dx; + r.y1 += dy; + r.y2 += dy; + fill.blt(sna, &fill, + r.x1, r.y1, + r.x2-r.x1, r.y2-r.y1); + if (damage) { + assert_pixmap_contains_box(pixmap, &r); + sna_damage_add_box(damage, &r); + } + } + } + } else { + while (n--) { + RegionRec region; + BoxRec r,*box; + int nbox; + + r.x1 = rect->x + drawable->x; + r.y1 = rect->y + drawable->y; + r.x2 = r.x1 + rect->width; + r.y2 = r.y1 + rect->height; + rect++; + + RegionInit(®ion, &r, 1); + RegionIntersect(®ion, ®ion, clip); + + nbox = REGION_NUM_RECTS(®ion); + box = REGION_RECTS(®ion); + while (nbox--) { + box->x1 += dx; + box->x2 += dx; + box->y1 += dy; + box->y2 += dy; + fill.blt(sna, &fill, + box->x1, box->y1, + box->x2-box->x1, box->y2-box->y1); + if (damage) { + assert_pixmap_contains_box(pixmap, box); + sna_damage_add_box(damage, box); + } + box++; + } + + RegionUninit(®ion); + } + } + fill.done(sna, &fill); + return TRUE; +} + +static uint32_t +get_pixel(PixmapPtr pixmap) +{ + DBG(("%s\n", __FUNCTION__)); + sna_pixmap_move_to_cpu(pixmap, false); + 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 Bool +sna_poly_fill_rect_tiled(DrawablePtr drawable, + struct kgem_bo *bo, + struct sna_damage **damage, + GCPtr gc, int n, + xRectangle *rect) +{ + struct sna *sna = to_sna_from_drawable(drawable); + PixmapPtr pixmap = get_drawable_pixmap(drawable); + PixmapPtr tile = gc->tile.pixmap; + RegionPtr clip = fbGetCompositeClip(gc); + DDXPointPtr origin = &gc->patOrg; + CARD32 alu = gc->alu; + int tile_width, tile_height; + int16_t dx, dy; + + DBG(("%s x %d [(%d, %d)+(%d, %d)...]\n", + __FUNCTION__, n, rect->x, rect->y, rect->width, rect->height)); + + tile_width = tile->drawable.width; + tile_height = tile->drawable.height; + + get_drawable_deltas(drawable, pixmap, &dx, &dy); + + if (tile_width == 1 && tile_height == 1) { + struct sna_fill_op fill; + + if (!sna_fill_init_blt(&fill, sna, pixmap, bo, alu, get_pixel(tile))) { + DBG(("%s: unsupported blt\n", __FUNCTION__)); + return FALSE; + } + + if (REGION_NUM_RECTS(clip) == 1) { + BoxPtr box = REGION_RECTS(clip); + while (n--) { + BoxRec r; + + r.x1 = rect->x + drawable->x; + r.y1 = rect->y + drawable->y; + r.x2 = r.x1 + rect->width; + r.y2 = r.y1 + rect->height; + rect++; + + if (box_intersect(&r, box)) { + r.x1 += dx; + r.x2 += dx; + r.y1 += dy; + r.y2 += dy; + fill.blt(sna, &fill, + r.x1, r.y1, + r.x2-r.x1, r.y2-r.y1); + if (damage) { + assert_pixmap_contains_box(pixmap, &r); + sna_damage_add_box(damage, &r); + } + } + } + } else { + while (n--) { + RegionRec region; + BoxRec r,*box; + int nbox; + + r.x1 = rect->x + drawable->x; + r.y1 = rect->y + drawable->y; + r.x2 = r.x1 + rect->width; + r.y2 = r.y1 + rect->height; + rect++; + + RegionInit(®ion, &r, 1); + RegionIntersect(®ion, ®ion, clip); + + nbox = REGION_NUM_RECTS(®ion); + box = REGION_RECTS(®ion); + while (nbox--) { + box->x1 += dx; + box->x2 += dx; + box->y1 += dy; + box->y2 += dy; + fill.blt(sna, &fill, + box->x1, box->y1, + box->x2-box->x1, + box->y2-box->y1); + if (damage) { + assert_pixmap_contains_box(pixmap, box); + sna_damage_add_box(damage, box); + } + box++; + } + + RegionUninit(®ion); + } + } + fill.done(sna, &fill); + } else { + struct sna_copy_op copy; + + if (!sna_pixmap_move_to_gpu(tile)) + return FALSE; + + if (!sna_copy_init_blt(©, sna, + tile, sna_pixmap_get_bo(tile), + pixmap, bo, + alu)) { + DBG(("%s: unsupported blt\n", __FUNCTION__)); + return FALSE; + } + + if (REGION_NUM_RECTS(clip) == 1) { + const BoxPtr box = REGION_RECTS(clip); + while (n--) { + BoxRec r; + + r.x1 = rect->x + drawable->x; + r.y1 = rect->y + drawable->y; + r.x2 = r.x1 + rect->width; + r.y2 = 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; + while (height) { + int width = r.x2 - r.x1; + int dst_x = r.x1; + int tile_x = (r.x1 - drawable->x - origin->x) % tile_width; + int h = tile_height - tile_y; + if (h > height) + h = height; + height -= h; + + 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 box; + box.x1 = dst_x + dx; + box.y1 = dst_y + dy; + box.x2 = box.x1 + w; + box.y2 = box.y1 + h; + assert_pixmap_contains_box(pixmap, &box); + sna_damage_add_box(damage, &box); + } + + dst_x += w; + tile_x = 0; + } + dst_y += h; + tile_y = 0; + } + } + } + } else { + while (n--) { + RegionRec region; + BoxRec r,*box; + int nbox; + + r.x1 = rect->x + drawable->x; + r.y1 = rect->y + drawable->y; + r.x2 = r.x1 + rect->width; + r.y2 = r.y1 + rect->height; + rect++; + + RegionInit(®ion, &r, 1); + 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; + while (height) { + int width = box->x2 - box->x1; + int dst_x = box->x1; + int tile_x = (box->x1 - drawable->x - origin->x) % tile_width; + int h = tile_height - tile_y; + if (h > height) + h = height; + height -= h; + + 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 box; + + box.x1 = dst_x + dx; + box.y1 = dst_y + dy; + box.x2 = box.x1 + w; + box.y2 = box.y1 + h; + + assert_pixmap_contains_box(pixmap, &box); + sna_damage_add_box(damage, &box); + } + + dst_x += w; + tile_x = 0; + } + dst_y += h; + tile_y = 0; + } + box++; + } + + RegionUninit(®ion); + } + } + copy.done(sna, ©); + } + return TRUE; +} + +static Bool +sna_poly_fill_rect_extents(DrawablePtr drawable, GCPtr gc, + int n, xRectangle *rect, + BoxPtr out) +{ + BoxRec box; + + if (n == 0) + return true; + + box.x1 = rect->x; + box.x2 = box.x1 + rect->width; + box.y1 = rect->y; + box.y2 = box.y1 + rect->height; + + while (--n) { + rect++; + BOX_ADD_RECT(box, rect->x, rect->y, rect->width, rect->height); + } + + TRIM_AND_TRANSLATE_BOX(box, drawable, gc); + *out = box; + return BOX_EMPTY(box); +} + +static void +sna_poly_fill_rect(DrawablePtr draw, GCPtr gc, int n, xRectangle *rect) +{ + struct sna *sna = to_sna_from_drawable(draw); + BoxRec extents; + RegionRec region; + + 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)); + + if (sna_poly_fill_rect_extents(draw, gc, n, rect, &extents)) + return; + + if (sna->kgem.wedged) + goto fallback; + + if (!PM_IS_SOLID(draw, gc->planemask)) + goto fallback; + + if (gc->fillStyle == FillSolid || + (gc->fillStyle == FillTiled && gc->tileIsPixel)) { + struct sna_pixmap *priv = sna_pixmap_from_drawable(draw); + + DBG(("%s: solid fill [%08lx], testing for blt\n", + __FUNCTION__, + gc->fillStyle == FillSolid ? gc->fgPixel : gc->tile.pixel)); + + if (sna_drawable_use_gpu_bo(draw, &extents) && + sna_poly_fill_rect_blt(draw, + priv->gpu_bo, + priv->gpu_only ? NULL : &priv->gpu_damage, + gc, n, rect)) + return; + + if (sna_drawable_use_cpu_bo(draw, &extents) && + sna_poly_fill_rect_blt(draw, + priv->cpu_bo, + &priv->cpu_damage, + gc, n, rect)) + return; + } else if (gc->fillStyle == FillTiled) { + struct sna_pixmap *priv = sna_pixmap_from_drawable(draw); + + DBG(("%s: tiled fill, testing for blt\n", __FUNCTION__)); + + if (sna_drawable_use_gpu_bo(draw, &extents) && + sna_poly_fill_rect_tiled(draw, + priv->gpu_bo, + priv->gpu_only ? NULL : &priv->gpu_damage, + gc, n, rect)) + return; + + if (sna_drawable_use_cpu_bo(draw, &extents) && + sna_poly_fill_rect_tiled(draw, + priv->cpu_bo, + &priv->cpu_damage, + gc, n, rect)) + return; + } + +fallback: + DBG(("%s: fallback (%d, %d), (%d, %d)\n", __FUNCTION__, + extents.x1, extents.y1, extents.x2, extents.y2)); + RegionInit(®ion, &extents, 1); + if (gc->pCompositeClip) + RegionIntersect(®ion, ®ion, gc->pCompositeClip); + if (!RegionNotEmpty(®ion)) + return; + + sna_gc_move_to_cpu(gc); + sna_drawable_move_region_to_cpu(draw, ®ion, true); + RegionUninit(®ion); + + DBG(("%s: fallback - fbPolyFillRect\n", __FUNCTION__)); + fbPolyFillRect(draw, gc, n, rect); +} + +static void +sna_image_glyph(DrawablePtr drawable, GCPtr gc, + int x, int y, unsigned int n, + CharInfoPtr *info, pointer base) +{ + ExtentInfoRec extents; + BoxRec box; + RegionRec region; + + if (n == 0) + return; + + QueryGlyphExtents(gc->font, info, n, &extents); + if (extents.overallWidth >= 0) { + box.x1 = x; + box.x2 = x + extents.overallWidth; + } else { + box.x2 = x; + box.x1 = x + extents.overallWidth; + } + box.y1 = y - FONTASCENT(gc->font); + box.y2 = y + FONTDESCENT(gc->font); + TRIM_BOX(box, drawable); + TRANSLATE_BOX(box, drawable); + + DBG(("%s: extents(%d, %d), (%d, %d)\n", + __FUNCTION__, box.x1, box.y1, box.x2, box.y2)); + + RegionInit(®ion, &box, 1); + if (gc->pCompositeClip) + RegionIntersect(®ion, ®ion, gc->pCompositeClip); + if (!RegionNotEmpty(®ion)) + return; + + sna_gc_move_to_cpu(gc); + sna_drawable_move_region_to_cpu(drawable, ®ion, true); + RegionUninit(®ion); + + fbImageGlyphBlt(drawable, gc, x, y, n, info, base); +} + +static void +sna_poly_glyph(DrawablePtr drawable, GCPtr gc, + int x, int y, unsigned int n, + CharInfoPtr *info, pointer base) +{ + ExtentInfoRec extents; + BoxRec box; + RegionRec region; + + if (n == 0) + return; + + QueryGlyphExtents(gc->font, info, n, &extents); + box.x1 = x + extents.overallLeft; + box.y1 = y - extents.overallAscent; + box.x2 = x + extents.overallRight; + box.y2 = y + extents.overallDescent; + + TRIM_BOX(box, drawable); + TRANSLATE_BOX(box, drawable); + + DBG(("%s: extents(%d, %d), (%d, %d)\n", + __FUNCTION__, box.x1, box.y1, box.x2, box.y2)); + + RegionInit(®ion, &box, 1); + if (gc->pCompositeClip) + RegionIntersect(®ion, ®ion, gc->pCompositeClip); + if (!RegionNotEmpty(®ion)) + return; + + sna_gc_move_to_cpu(gc); + sna_drawable_move_region_to_cpu(drawable, ®ion, true); + RegionUninit(®ion); + + fbPolyGlyphBlt(drawable, gc, x, y, n, info, base); +} + +static void +sna_push_pixels(GCPtr gc, PixmapPtr bitmap, DrawablePtr drawable, + int w, int h, + int x, int y) +{ + BoxRec box; + RegionRec region; + + if (w == 0 || h == 0) + return; + + DBG(("%s (%d, %d)x(%d, %d)\n", __FUNCTION__, x, y, w, h)); + + box.x1 = x; + box.y1 = y; + if (!gc->miTranslate) { + box.x1 += drawable->x; + box.y1 += drawable->y; + } + box.x2 = box.x1 + w; + box.y2 = box.y1 + h; + + CLIP_BOX(box, gc); + if (BOX_EMPTY(box)) + return; + + DBG(("%s: extents(%d, %d), (%d, %d)\n", + __FUNCTION__, box.x1, box.y1, box.x2, box.y2)); + + RegionInit(®ion, &box, 1); + if (gc->pCompositeClip) + RegionIntersect(®ion, ®ion, gc->pCompositeClip); + if (!RegionNotEmpty(®ion)) + return; + + sna_gc_move_to_cpu(gc); + sna_pixmap_move_to_cpu(bitmap, false); + sna_drawable_move_region_to_cpu(drawable, ®ion, true); + RegionUninit(®ion); + + fbPushPixels(gc, bitmap, drawable, w, h, x, y); +} + +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, + miPolyRectangle, + sna_poly_arc, + miFillPolygon, + sna_poly_fill_rect, + miPolyFillArc, + miPolyText8, + miPolyText16, + miImageText8, + miImageText16, + sna_image_glyph, + sna_poly_glyph, + sna_push_pixels, +}; + +static void 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__)); + sna_pixmap_move_to_cpu(pixmap, true); + } +} + +static void +sna_validate_gc(GCPtr gc, unsigned long changes, DrawablePtr drawable) +{ + DBG(("%s\n", __FUNCTION__)); + + if (changes & GCTile && !gc->tileIsPixel) { + DBG(("%s: flushing tile pixmap\n", __FUNCTION__)); + sna_validate_pixmap(drawable, gc->tile.pixmap); + } + + if (changes & GCStipple && gc->stipple) { + DBG(("%s: flushing stipple pixmap\n", __FUNCTION__)); + sna_pixmap_move_to_cpu(gc->stipple, true); + } + + fbValidateGC(gc, changes, drawable); +} + +static const GCFuncs sna_gc_funcs = { + sna_validate_gc, + miChangeGC, + miCopyGC, + miDestroyGC, + miChangeClip, + miDestroyClip, + miCopyClip +}; + +static int sna_create_gc(GCPtr gc) +{ + if (!fbCreateGC(gc)) + return FALSE; + + 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) +{ + BoxRec extents; + RegionRec region; + + DBG(("%s (%d, %d, %d, %d)\n", __FUNCTION__, x, y, w, h)); + + extents.x1 = x + drawable->x; + extents.y1 = y + drawable->y; + extents.x2 = extents.x1 + w; + extents.y2 = extents.y1 + h; + RegionInit(®ion, &extents, 1); + + sna_drawable_move_region_to_cpu(drawable, ®ion, false); + fbGetImage(drawable, x, y, w, h, format, mask, dst); + + RegionUninit(®ion); +} + +static void +sna_get_spans(DrawablePtr drawable, int wMax, + DDXPointPtr pt, int *width, int n, char *start) +{ + BoxRec extents; + RegionRec region; + + if (sna_spans_extents(drawable, NULL, n, pt, width, &extents)) + return; + + RegionInit(®ion, &extents, 1); + sna_drawable_move_region_to_cpu(drawable, ®ion, false); + RegionUninit(®ion); + + fbGetSpans(drawable, wMax, pt, width, n, start); +} + +static void +sna_copy_window(WindowPtr win, DDXPointRec origin, RegionPtr src) +{ + struct sna *sna = to_sna_from_drawable(&win->drawable); + PixmapPtr pixmap = fbGetWindowPixmap(win); + RegionRec dst; + int dx, dy; + + DBG(("%s origin=(%d, %d)\n", __FUNCTION__, origin.x, origin.y)); + + if (sna->kgem.wedged) { + sna_pixmap_move_to_cpu(pixmap, true); + fbCopyWindow(win, origin, src); + return; + } + + dx = origin.x - win->drawable.x; + dy = origin.y - win->drawable.y; + RegionTranslate(src, -dx, -dy); + + RegionNull(&dst); + RegionIntersect(&dst, &win->borderClip, src); +#ifdef COMPOSITE + if (pixmap->screen_x || pixmap->screen_y) + RegionTranslate(&dst, -pixmap->screen_x, -pixmap->screen_y); +#endif + + miCopyRegion(&pixmap->drawable, &pixmap->drawable, + NULL, &dst, dx, dy, sna_copy_boxes, 0, NULL); + + RegionUninit(&dst); +} + +static Bool sna_change_window_attributes(WindowPtr win, unsigned long mask) +{ + 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__)); + sna_validate_pixmap(&win->drawable, win->background.pixmap); + } + + if (mask & CWBorderPixmap && win->borderIsPixel == FALSE) { + DBG(("%s: flushing border pixmap\n", __FUNCTION__)); + sna_validate_pixmap(&win->drawable, win->border.pixmap); + } + + return fbChangeWindowAttributes(win, mask); +} + +static void +sna_add_traps(PicturePtr picture, INT16 x, INT16 y, int n, xTrap *t) +{ + DBG(("%s (%d, %d) x %d\n", __FUNCTION__, x, y, n)); + + sna_drawable_move_to_cpu(picture->pDrawable, true); + + fbAddTraps(picture, x, y, n, t); +} + +static void +sna_accel_flush_callback(CallbackListPtr *list, + pointer user_data, pointer call_data) +{ + struct sna *sna = user_data; + + if (sna->kgem.flush == 0 && list_is_empty(&sna->dirty_pixmaps)) + return; + + DBG(("%s\n", __FUNCTION__)); + + /* flush any pending damage from shadow copies to tfp clients */ + while (!list_is_empty(&sna->dirty_pixmaps)) { + struct sna_pixmap *priv = list_first_entry(&sna->dirty_pixmaps, + struct sna_pixmap, + list); + sna_pixmap_move_to_gpu(priv->pixmap); + } + + kgem_submit(&sna->kgem); +} + +static void sna_deferred_free(struct sna *sna) +{ + struct sna_pixmap *priv, *next; + + list_for_each_entry_safe(priv, next, &sna->deferred_free, list) { + if (priv->cpu_bo->gpu) + continue; + + list_del(&priv->list); + kgem_bo_destroy(&sna->kgem, priv->cpu_bo); + fbDestroyPixmap(priv->pixmap); + free(priv); + } +} + +static uint64_t read_timer(int fd) +{ + uint64_t count = 0; + int ret = read(fd, &count, sizeof(count)); + return count; + (void)ret; +} + +static struct sna_pixmap *sna_accel_scanout(struct sna *sna) +{ + PixmapPtr front = sna->shadow ? sna->shadow : sna->front; + struct sna_pixmap *priv = sna_pixmap(front); + return priv && priv->gpu_bo ? priv : NULL; +} + +#if HAVE_SYS_TIMERFD_H +#include <sys/timerfd.h> +#include <errno.h> + +static void _sna_accel_disarm_timer(struct sna *sna, int id) +{ + struct itimerspec to; + + DBG(("%s[%d] (time=%ld)\n", __FUNCTION__, id, (long)GetTimeInMillis())); + + memset(&to, 0, sizeof(to)); + timerfd_settime(sna->timer[id], 0, &to, NULL); + sna->timer_active &= ~(1<<id); +} + +#define return_if_timer_active(id) do { \ + if (sna->timer_active & (1<<(id))) \ + return read_timer(sna->timer[id]) > 0; \ +} while (0) + +static Bool sna_accel_do_flush(struct sna *sna) +{ + struct itimerspec to; + struct sna_pixmap *priv; + + return_if_timer_active(FLUSH_TIMER); + + priv = sna_accel_scanout(sna); + if (priv == NULL) + return FALSE; + + if (priv->cpu_damage == NULL && priv->gpu_bo->rq == NULL) + return FALSE; + + if (sna->timer[FLUSH_TIMER] == -1) + return TRUE; + + DBG(("%s, time=%ld\n", __FUNCTION__, (long)GetTimeInMillis())); + + /* Initial redraw after 10ms. */ + to.it_value.tv_sec = 0; + to.it_value.tv_nsec = 10 * 1000 * 1000; + + /* Then periodic updates at 50Hz.*/ + to.it_interval.tv_sec = 0; + to.it_interval.tv_nsec = 20 * 1000 * 1000; + timerfd_settime(sna->timer[FLUSH_TIMER], 0, &to, NULL); + + sna->timer_active |= 1 << FLUSH_TIMER; + return FALSE; +} + +static Bool sna_accel_do_expire(struct sna *sna) +{ + struct itimerspec to; + + return_if_timer_active(EXPIRE_TIMER); + + if (!kgem_needs_expire(&sna->kgem)) + return FALSE; + + if (sna->timer[EXPIRE_TIMER] == -1) + return TRUE; + + /* Initial expiration after 5s. */ + to.it_value.tv_sec = 5; + to.it_value.tv_nsec = 0; + + /* Then periodic update every 1s.*/ + to.it_interval.tv_sec = 1; + to.it_interval.tv_nsec = 0; + timerfd_settime(sna->timer[EXPIRE_TIMER], 0, &to, NULL); + + sna->timer_active |= 1 << EXPIRE_TIMER; + return FALSE; +} + +static void sna_accel_create_timers(struct sna *sna) +{ + int id; + + for (id = 0; id < NUM_TIMERS; id++) + sna->timer[id] = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); +} +#else +static void sna_accel_create_timers(struct sna *sna) +{ + int id; + + for (id = 0; id < NUM_TIMERS; id++) + sna->timer[id] = -1; +} +static Bool sna_accel_do_flush(struct sna *sna) { return sna_accel_scanout(sna) != NULL; } +static Bool sna_accel_arm_expire(struct sna *sna) { return TRUE; } +static void _sna_accel_disarm_timer(struct sna *sna, int id) { } +#endif + +static void sna_accel_flush(struct sna *sna) +{ + struct sna_pixmap *priv = sna_accel_scanout(sna); + + DBG(("%s (time=%ld)\n", __FUNCTION__, (long)GetTimeInMillis())); + + sna_pixmap_move_to_gpu(priv->pixmap); + kgem_bo_flush(&sna->kgem, priv->gpu_bo); + + if (priv->gpu_bo->rq == NULL) + _sna_accel_disarm_timer(sna, FLUSH_TIMER); +} + +static void sna_accel_expire(struct sna *sna) +{ + DBG(("%s (time=%ld)\n", __FUNCTION__, (long)GetTimeInMillis())); + + if (!kgem_expire_cache(&sna->kgem)) + _sna_accel_disarm_timer(sna, EXPIRE_TIMER); +} + +static void sna_accel_install_timers(struct sna *sna) +{ + if (sna->timer[FLUSH_TIMER] != -1) + AddGeneralSocket(sna->timer[FLUSH_TIMER]); + + if (sna->timer[EXPIRE_TIMER] != -1) + AddGeneralSocket(sna->timer[EXPIRE_TIMER]); +} + +Bool sna_accel_pre_init(struct sna *sna) +{ + sna_accel_create_timers(sna); + return TRUE; +} + +Bool sna_accel_init(ScreenPtr screen, struct sna *sna) +{ + const char *backend; + + if (!dixRegisterPrivateKey(&sna_pixmap_index, PRIVATE_PIXMAP, 0)) + return FALSE; + + if (!AddCallback(&FlushCallback, sna_accel_flush_callback, sna)) + return FALSE; + + if (!sna_glyphs_init(screen)) + return FALSE; + + list_init(&sna->dirty_pixmaps); + list_init(&sna->deferred_free); + + sna_accel_install_timers(sna); + + screen->CreateGC = sna_create_gc; + screen->GetImage = sna_get_image; + screen->GetSpans = sna_get_spans; + screen->CopyWindow = sna_copy_window; + screen->ChangeWindowAttributes = sna_change_window_attributes; + screen->CreatePixmap = sna_create_pixmap; + screen->DestroyPixmap = sna_destroy_pixmap; + +#ifdef RENDER + { + PictureScreenPtr ps = GetPictureScreenIfSet(screen); + if (ps) { + ps->Composite = sna_composite; + ps->CompositeRects = sna_composite_rectangles; + ps->Glyphs = sna_glyphs; + ps->UnrealizeGlyph = sna_glyph_unrealize; + ps->AddTraps = sna_add_traps; + ps->Trapezoids = sna_composite_trapezoids; + } + } +#endif + + backend = "no"; + sna->have_render = false; + no_render_init(sna); + +#if !DEBUG_NO_RENDER + if (sna->chipset.info->gen >= 70) { + } else if (sna->chipset.info->gen >= 60) { + if ((sna->have_render = gen6_render_init(sna))) + backend = "Sandybridge"; + } else if (sna->chipset.info->gen >= 50) { + if ((sna->have_render = gen5_render_init(sna))) + backend = "Ironlake"; + } else if (sna->chipset.info->gen >= 40) { + if ((sna->have_render = gen4_render_init(sna))) + backend = "Broadwater"; + } else if (sna->chipset.info->gen >= 30) { + if ((sna->have_render = gen3_render_init(sna))) + backend = "gen3"; + } else if (sna->chipset.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)); + + xf86DrvMsg(sna->scrn->scrnIndex, X_INFO, + "SNA initialized with %s backend\n", + backend); + + return TRUE; +} + +Bool sna_accel_create(struct sna *sna) +{ + if (!sna_glyphs_create(sna)) + return FALSE; + + if (!sna_gradients_create(sna)) + return FALSE; + + return TRUE; +} + +void sna_accel_close(struct sna *sna) +{ + sna_glyphs_close(sna); + sna_gradients_close(sna); + + DeleteCallback(&FlushCallback, sna_accel_flush_callback, sna); +} + +static void sna_accel_throttle(struct sna *sna) +{ + if (sna->flags & SNA_NO_THROTTLE) + return; + + if (list_is_empty(&sna->kgem.requests)) + return; + + DBG(("%s (time=%ld)\n", __FUNCTION__, (long)GetTimeInMillis())); + + kgem_throttle(&sna->kgem); +} + +void sna_accel_block_handler(struct sna *sna) +{ + if (sna_accel_do_flush(sna)) + sna_accel_flush(sna); + + if (sna_accel_do_expire(sna)) + sna_accel_expire(sna); + + sna_accel_throttle(sna); +} + +void sna_accel_wakeup_handler(struct sna *sna) +{ + _kgem_retire(&sna->kgem); + sna_deferred_free(sna); + + if (sna->kgem.need_purge) + kgem_expire_cache(&sna->kgem); +} + +void sna_accel_free(struct sna *sna) +{ + int id; + + for (id = 0; id < NUM_TIMERS; id++) + if (sna->timer[id] != -1) { + close(sna->timer[id]); + sna->timer[id] = -1; + } +} |