xf86-video-intel: 23 commits - src/sna/kgem.c src/sna/kgem.h src/sna/sna_accel.c

Chris Wilson ickle at kemper.freedesktop.org
Tue Jan 24 05:39:15 PST 2012


 src/sna/kgem.c      |  147 ++++--
 src/sna/kgem.h      |    6 
 src/sna/sna_accel.c | 1149 +++++++++++++++++++++++++++++++++++++---------------
 3 files changed, 922 insertions(+), 380 deletions(-)

New commits:
commit 358a0861e642c26d925a69656039b4c95adba237
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Jan 24 13:30:19 2012 +0000

    sna: Do not discard CPU damage for a partial copy
    
    If we are copying a region that does not fill its extents (i.e. is not
    singular) then we must be care not to discard the CPU damage that is not
    overwritten by the copy.
    
    Fixes regression from 77ee92248582d65a03619d1bb1d93a74468eea00
    (sna: Use full usage flags for moving the dst pixmap for a copy).
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c
index 3800057..4b997a5 100644
--- a/src/sna/sna_accel.c
+++ b/src/sna/sna_accel.c
@@ -751,7 +751,7 @@ static inline bool pixmap_inplace(struct sna *sna,
 }
 
 static bool
-sna_pixmap_move_area_to_gpu(PixmapPtr pixmap, BoxPtr box);
+sna_pixmap_move_area_to_gpu(PixmapPtr pixmap, BoxPtr box, unsigned flags);
 
 static bool
 sna_pixmap_create_mappable_gpu(PixmapPtr pixmap)
@@ -1217,7 +1217,7 @@ sna_drawable_move_region_to_cpu(DrawablePtr drawable,
 	    priv->stride && priv->gpu_bo &&
 	    !kgem_bo_is_busy(priv->gpu_bo) &&
 	    region_inplace(sna, pixmap, region, priv) &&
-	    sna_pixmap_move_area_to_gpu(pixmap, &region->extents)) {
+	    sna_pixmap_move_area_to_gpu(pixmap, &region->extents, flags)) {
 		assert(flags & MOVE_WRITE);
 		kgem_bo_submit(&sna->kgem, priv->gpu_bo);
 
@@ -1496,7 +1496,7 @@ inline static unsigned drawable_gc_flags(DrawablePtr draw,
 }
 
 static bool
-sna_pixmap_move_area_to_gpu(PixmapPtr pixmap, BoxPtr box)
+sna_pixmap_move_area_to_gpu(PixmapPtr pixmap, BoxPtr box, unsigned int flags)
 {
 	struct sna *sna = to_sna_from_pixmap(pixmap);
 	struct sna_pixmap *priv = sna_pixmap(pixmap);
@@ -1535,6 +1535,9 @@ sna_pixmap_move_area_to_gpu(PixmapPtr pixmap, BoxPtr box)
 		}
 	}
 
+	if ((flags & MOVE_READ) == 0)
+		sna_damage_subtract_box(&priv->cpu_damage, box);
+
 	sna_damage_reduce(&priv->cpu_damage);
 	if (priv->cpu_damage == NULL) {
 		list_del(&priv->list);
@@ -1746,7 +1749,8 @@ _sna_drawable_use_gpu_bo(DrawablePtr drawable,
 	}
 
 move_to_gpu:
-	if (!sna_pixmap_move_area_to_gpu(pixmap, &extents)) {
+	if (!sna_pixmap_move_area_to_gpu(pixmap, &extents,
+					 MOVE_READ | MOVE_WRITE)) {
 		DBG(("%s: failed to move-to-gpu, fallback\n", __FUNCTION__));
 		return FALSE;
 	}
@@ -3138,8 +3142,19 @@ sna_copy_boxes(DrawablePtr src, DrawablePtr dst, GCPtr gc,
 	if (dst_priv == NULL)
 		goto fallback;
 
-	if (replaces)
+	if (src_priv == NULL && !copy_use_gpu_bo(sna, dst_priv, &region)) {
+		DBG(("%s: fallback - unattached to source and not use dst gpu bo\n",
+		     __FUNCTION__));
+		goto fallback;
+	}
+
+	if (replaces) {
+		sna_damage_destroy(&dst_priv->gpu_damage);
 		sna_damage_destroy(&dst_priv->cpu_damage);
+		list_del(&dst_priv->list);
+		dst_priv->undamaged = true;
+		dst_priv->clear = false;
+	}
 
 	/* Try to maintain the data on the GPU */
 	if (dst_priv->gpu_bo == NULL &&
@@ -3163,17 +3178,23 @@ sna_copy_boxes(DrawablePtr src, DrawablePtr dst, GCPtr gc,
 	}
 
 	if (dst_priv->gpu_bo) {
-		if (!src_priv && !copy_use_gpu_bo(sna, dst_priv, &region)) {
-			DBG(("%s: fallback - src_priv=%p and not use dst gpu bo\n",
-			     __FUNCTION__, src_priv));
-			goto fallback;
-		}
-
-		if (!sna_pixmap_move_to_gpu(dst_pixmap,
-					    MOVE_WRITE | (alu_overwrites(alu) ? 0 : MOVE_READ))) {
-			DBG(("%s: fallback - not a pure copy and failed to move dst to GPU\n",
-			     __FUNCTION__));
-			goto fallback;
+		if (!DAMAGE_IS_ALL(dst_priv->gpu_damage)) {
+			BoxRec extents = region.extents;
+			extents.x1 += dst_dx;
+			extents.x2 += dst_dx;
+			extents.y1 += dst_dy;
+			extents.y2 += dst_dy;
+			if (!sna_pixmap_move_area_to_gpu(dst_pixmap, &extents,
+							 MOVE_WRITE | (n == 1 && alu_overwrites(alu) ? 0 : MOVE_READ))) {
+				DBG(("%s: fallback - not a pure copy and failed to move dst to GPU\n",
+				     __FUNCTION__));
+				goto fallback;
+			}
+		} else {
+			dst_priv->clear = false;
+			if (!dst_priv->pinned)
+				list_move(&dst_priv->inactive,
+					  &sna->active_pixmaps);
 		}
 
 		if (src_priv && src_priv->clear) {
commit 672c6ad8170572c0f2562d5a8381ab2d17fd1e1b
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Jan 24 12:56:59 2012 +0000

    sna: Silence the compiler with a few DBG format warnings
    
    The size of the integers being printed changed causing warnings on 32/64
    bit.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c
index 9e58d4b..3800057 100644
--- a/src/sna/sna_accel.c
+++ b/src/sna/sna_accel.c
@@ -1478,7 +1478,7 @@ inline static unsigned drawable_gc_flags(DrawablePtr draw,
 
 	if (fbGetGCPrivate(gc)->and) {
 		DBG(("%s: read due to rop %d:%x\n",
-		     __FUNCTION__, gc->alu, fbGetGCPrivate(gc)->and));
+		     __FUNCTION__, gc->alu, (unsigned)fbGetGCPrivate(gc)->and));
 		return MOVE_READ | MOVE_WRITE;
 	}
 
@@ -5595,7 +5595,7 @@ sna_poly_line_blt(DrawablePtr drawable,
 	DDXPointRec last;
 	int16_t dx, dy;
 
-	DBG(("%s: alu=%d, fg=%08lx\n", __FUNCTION__, gc->alu, pixel));
+	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;
@@ -6049,8 +6049,8 @@ sna_poly_line(DrawablePtr drawable, GCPtr gc,
 	}
 
 	if (gc_is_solid(gc, &color)) {
-		DBG(("%s: trying solid fill [%08lx]\n",
-		     __FUNCTION__, color));
+		DBG(("%s: trying solid fill [%08x]\n",
+		     __FUNCTION__, (unsigned)color));
 
 		if (data.flags & 4) {
 			if (sna_drawable_use_gpu_bo(drawable,
@@ -6984,8 +6984,8 @@ sna_poly_segment(DrawablePtr drawable, GCPtr gc, int n, xSegment *seg)
 	if (gc->lineStyle != LineSolid || gc->lineWidth > 1)
 		goto spans_fallback;
 	if (gc_is_solid(gc, &color)) {
-		DBG(("%s: trying blt solid fill [%08lx, flags=%x] paths\n",
-		     __FUNCTION__, color, data.flags));
+		DBG(("%s: trying blt solid fill [%08x, flags=%x] paths\n",
+		     __FUNCTION__, (unsigned)color, data.flags));
 
 		if (data.flags & 4) {
 			if (sna_drawable_use_gpu_bo(drawable,
commit 238f3d9bc563950475ee823f844197cf15a909e2
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Jan 24 12:19:38 2012 +0000

    sna: A deferred attachment will never have a CPU bo
    
    So we can remove that check in sna_copy_boxes.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c
index fc96118..9e58d4b 100644
--- a/src/sna/sna_accel.c
+++ b/src/sna/sna_accel.c
@@ -3217,9 +3217,6 @@ sna_copy_boxes(DrawablePtr src, DrawablePtr dst, GCPtr gc,
 						       dst_pixmap->drawable.height);
 					list_del(&dst_priv->list);
 					dst_priv->undamaged = false;
-
-					dst_priv->clear = true;
-					dst_priv->clear_color = src_priv->clear_color;
 				} else {
 					assert_pixmap_contains_box(dst_pixmap,
 								   RegionExtents(&region));
@@ -3260,9 +3257,7 @@ sna_copy_boxes(DrawablePtr src, DrawablePtr dst, GCPtr gc,
 					RegionTranslate(&region, -dst_dx, -dst_dy);
 				}
 			}
-		} else if ((src_priv ||
-			    (src_priv = _sna_pixmap_attach(src_pixmap))) &&
-			   src_priv->cpu_bo) {
+		} else if (src_priv && src_priv->cpu_bo) {
 			if (!sna->render.copy_boxes(sna, alu,
 						    src_pixmap, src_priv->cpu_bo, src_dx, src_dy,
 						    dst_pixmap, dst_priv->gpu_bo, dst_dx, dst_dy,
commit c353a8cfde838b070f3e71d3946f3b40d1d5113a
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Jan 24 10:52:27 2012 +0000

    sna: Revise use of the inplace hint to favour small cacheable operations
    
    The goal of the heuristic is to reduce readbacks and damage tracking on
    active GPU bo whilst simultaneously offering the best performance for
    small operations which would prefer to be performed on the shadow rather
    than in place.
    
    This restores ShmPutImage performance.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c
index fbb60b8..fc96118 100644
--- a/src/sna/sna_accel.c
+++ b/src/sna/sna_accel.c
@@ -871,6 +871,8 @@ skip_inplace_map:
 
 	if (flags & MOVE_INPLACE_HINT &&
 	    priv->stride && priv->gpu_bo &&
+	    !kgem_bo_is_busy(priv->gpu_bo) &&
+	    pixmap_inplace(sna, pixmap, priv) &&
 	    sna_pixmap_move_to_gpu(pixmap, flags)) {
 		assert(flags & MOVE_WRITE);
 		kgem_bo_submit(&sna->kgem, priv->gpu_bo);
@@ -1021,6 +1023,25 @@ region_subsumes_damage(const RegionRec *region, struct sna_damage *damage)
 						(BoxPtr)de) == PIXMAN_REGION_IN;
 }
 
+static bool
+region_overlaps_damage(const RegionRec *region, struct sna_damage *damage)
+{
+	const BoxRec *re, *de;
+
+	DBG(("%s?\n", __FUNCTION__));
+	assert(damage);
+
+	re = &region->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 < de->x2 && re->x2 > de->x1 &&
+		re->y1 < de->y2 && re->y2 > de->y1);
+}
+
 #ifndef NDEBUG
 static bool
 pixmap_contains_damage(PixmapPtr pixmap, struct sna_damage *damage)
@@ -1057,13 +1078,8 @@ static inline bool region_inplace(struct sna *sna,
 		return true;
 	}
 
-	if (priv->mapped) {
-		DBG(("%s: already mapped\n", __FUNCTION__));
-		return true;
-	}
-
 	if (priv->cpu_damage &&
-	    region_subsumes_damage(region, priv->cpu_damage)) {
+	    region_overlaps_damage(region, priv->cpu_damage)) {
 		DBG(("%s: uncovered CPU damage pending\n", __FUNCTION__));
 		return false;
 	}
@@ -1199,9 +1215,8 @@ sna_drawable_move_region_to_cpu(DrawablePtr drawable,
 
 	if (flags & MOVE_INPLACE_HINT &&
 	    priv->stride && priv->gpu_bo &&
-	    !(priv->cpu_damage &&
-	      sna_damage_contains_box__no_reduce(priv->cpu_damage,
-						 &region->extents)) &&
+	    !kgem_bo_is_busy(priv->gpu_bo) &&
+	    region_inplace(sna, pixmap, region, priv) &&
 	    sna_pixmap_move_area_to_gpu(pixmap, &region->extents)) {
 		assert(flags & MOVE_WRITE);
 		kgem_bo_submit(&sna->kgem, priv->gpu_bo);
@@ -2314,6 +2329,11 @@ static bool upload_inplace(struct sna *sna,
 			   struct sna_pixmap *priv,
 			   RegionRec *region)
 {
+	if (priv->mapped) {
+		DBG(("%s: already mapped\n", __FUNCTION__));
+		return true;
+	}
+
 	if (!region_inplace(sna, pixmap, region, priv))
 		return false;
 
@@ -3029,19 +3049,27 @@ static bool copy_use_gpu_bo(struct sna *sna,
 			    struct sna_pixmap *priv,
 			    RegionPtr region)
 {
-	if (region_inplace(sna, priv->pixmap, region, priv))
+	if (region_inplace(sna, priv->pixmap, region, priv)) {
+		DBG(("%s: perform in place, use gpu bo\n", __FUNCTION__));
 		return true;
+	}
 
-	if (!priv->cpu_bo)
+	if (!priv->cpu_bo) {
+		DBG(("%s: no cpu bo, copy to shadow\n", __FUNCTION__));
 		return false;
+	}
 
 	if (kgem_bo_is_busy(priv->cpu_bo)) {
-		if (priv->cpu_bo->exec)
+		if (priv->cpu_bo->exec) {
+			DBG(("%s: cpu bo is busy, use gpu bo\n", __FUNCTION__));
 			return true;
+		}
 
 		kgem_retire(&sna->kgem);
 	}
 
+	DBG(("%s: cpu bo busy? %d\n", __FUNCTION__,
+	     kgem_bo_is_busy(priv->cpu_bo)));
 	return kgem_bo_is_busy(priv->cpu_bo);
 }
 
commit 29547421f392a9b589ba069cbfbba01dbe535d93
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Jan 24 10:23:46 2012 +0000

    sna: Use the reduced ROP from the fbGC to accurately determine readback
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c
index c6a7ca3..fbb60b8 100644
--- a/src/sna/sna_accel.c
+++ b/src/sna/sna_accel.c
@@ -1433,22 +1433,27 @@ static bool alu_overwrites(uint8_t alu)
 	}
 }
 
+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 read)
 {
 	unsigned flags;
 
-	if (!alu_overwrites(gc->alu)) {
-		DBG(("%s: read due to alu %d\n", __FUNCTION__, gc->alu));
-		return MOVE_READ | MOVE_WRITE;
-	}
-
-	if (!PM_IS_SOLID(draw, gc->planemask)) {
-		DBG(("%s: read due to planemask %lx\n",
-		     __FUNCTION__, gc->planemask));
-		return MOVE_READ | MOVE_WRITE;
-	}
+	assert(sna_gc(gc)->changes == 0);
 
 	if (gc->fillStyle == FillStippled) {
 		DBG(("%s: read due to fill %d\n",
@@ -1456,7 +1461,15 @@ inline static unsigned drawable_gc_flags(DrawablePtr draw,
 		return MOVE_READ | MOVE_WRITE;
 	}
 
+	if (fbGetGCPrivate(gc)->and) {
+		DBG(("%s: read due to rop %d:%x\n",
+		     __FUNCTION__, gc->alu, fbGetGCPrivate(gc)->and));
+		return MOVE_READ | MOVE_WRITE;
+	}
+
 	DBG(("%s: try operating on drawable inplace\n", __FUNCTION__));
+	assert(drawable_gc_inplace_hint(draw, gc));
+
 	flags = MOVE_WRITE;
 	if (USE_INPLACE)
 		flags |= MOVE_INPLACE_HINT;
@@ -4482,6 +4495,7 @@ fallback:
 							       gc, n > 1)))
 		goto out;
 
+	DBG(("%s: fbFillSpans\n", __FUNCTION__));
 	fbFillSpans(drawable, gc, n, pt, width, sorted);
 out:
 	RegionUninit(&region);
@@ -4519,6 +4533,7 @@ fallback:
 							       gc, true)))
 		goto out;
 
+	DBG(("%s: fbSetSpans\n", __FUNCTION__));
 	fbSetSpans(drawable, gc, src, pt, width, n, sorted);
 out:
 	RegionUninit(&region);
@@ -5835,7 +5850,7 @@ _use_zero_spans(DrawablePtr drawable, GCPtr gc, const BoxRec *extents)
 	if (USE_ZERO_SPANS)
 		return USE_ZERO_SPANS > 0;
 
-	if ((drawable_gc_flags(drawable, gc, false) & MOVE_INPLACE_HINT) == 0)
+	if (!drawable_gc_inplace_hint(drawable, gc))
 		return TRUE;
 
 	/* XXX check for GPU stalls on the gc (stipple, tile, etc) */
@@ -5899,7 +5914,7 @@ _use_wide_spans(DrawablePtr drawable, GCPtr gc, const BoxRec *extents)
 	if (USE_WIDE_SPANS)
 		return USE_WIDE_SPANS > 0;
 
-	if ((drawable_gc_flags(drawable, gc, false) & MOVE_INPLACE_HINT) == 0)
+	if (!drawable_gc_inplace_hint(drawable, gc))
 		return TRUE;
 
 	/* XXX check for GPU stalls on the gc (stipple, tile, etc) */
@@ -8204,7 +8219,6 @@ fallback:
 
 	if (!sna_gc_move_to_cpu(gc, draw))
 		goto out;
-
 	if (!sna_drawable_move_region_to_cpu(draw, &data.region,
 					     drawable_gc_flags(draw, gc,
 							       true)))
@@ -9536,7 +9550,6 @@ fallback:
 
 	if (!sna_gc_move_to_cpu(gc, draw))
 		goto out;
-
 	if (!sna_drawable_move_region_to_cpu(draw, &region,
 					     drawable_gc_flags(draw, gc,
 							       n > 1)))
@@ -9716,7 +9729,6 @@ fallback:
 
 	if (!sna_gc_move_to_cpu(gc, draw))
 		goto out;
-
 	if (!sna_drawable_move_region_to_cpu(draw, &data.region,
 					     drawable_gc_flags(draw, gc,
 							       true)))
@@ -10117,7 +10129,6 @@ force_fallback:
 
 		if (!sna_gc_move_to_cpu(gc, drawable))
 			goto out;
-
 		if (!sna_drawable_move_region_to_cpu(drawable, &region,
 						     drawable_gc_flags(drawable,
 								       gc, true)))
@@ -10356,9 +10367,9 @@ force_fallback:
 
 		if (!sna_gc_move_to_cpu(gc, drawable))
 			goto out;
-		if(!sna_drawable_move_region_to_cpu(drawable, &region,
-						    drawable_gc_flags(drawable,
-								      gc, n > 1)))
+		if (!sna_drawable_move_region_to_cpu(drawable, &region,
+						     drawable_gc_flags(drawable,
+								       gc, n > 1)))
 			goto out;
 
 		DBG(("%s: fallback -- fbImageGlyphBlt\n", __FUNCTION__));
@@ -10673,6 +10684,7 @@ fallback:
 					     drawable_gc_flags(drawable,
 							       gc, true)))
 		goto out;
+
 	DBG(("%s: fallback -- fbPolyGlyphBlt\n", __FUNCTION__));
 	fbPolyGlyphBlt(drawable, gc, x, y, n, info, base);
 
commit ec794f7594ae5e2c223b7f7255ff8979d9fb06c1
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Jan 23 22:14:15 2012 +0000

    sna: Track a proxy's dirty status upon itself rather than the target
    
    As proxy's are short-lived and are not used outside of the operation for
    which they are created, dirtied or flushed, we can safely copy the dirty
    status onto the proxy object itself.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/kgem.c b/src/sna/kgem.c
index c0c97bc..d8a3017 100644
--- a/src/sna/kgem.c
+++ b/src/sna/kgem.c
@@ -2900,6 +2900,7 @@ struct kgem_bo *kgem_create_proxy(struct kgem_bo *target,
 		return NULL;
 
 	bo->io = target->io;
+	bo->dirty = target->dirty;
 	bo->reusable = false;
 	bo->proxy = kgem_bo_reference(target);
 	bo->delta = offset;
diff --git a/src/sna/kgem.h b/src/sna/kgem.h
index 8a86171..652c2d7 100644
--- a/src/sna/kgem.h
+++ b/src/sna/kgem.h
@@ -414,16 +414,12 @@ static inline bool kgem_bo_is_dirty(struct kgem_bo *bo)
 	if (bo == NULL)
 		return FALSE;
 
-	if (bo->proxy)
-		bo = bo->proxy;
 	return bo->dirty;
 }
 
 static inline void kgem_bo_mark_dirty(struct kgem_bo *bo)
 {
 	DBG_HDR(("%s: handle=%d\n", __FUNCTION__, bo->handle));
-	if (bo->proxy)
-		bo = bo->proxy;
 	bo->dirty = true;
 }
 
commit 2574a04c940e032fdcf1cd39a0950515e8652471
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Jan 23 21:45:29 2012 +0000

    sna: Split the active bo cache into per-tiling caches
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/kgem.c b/src/sna/kgem.c
index 3bcd5cc..c0c97bc 100644
--- a/src/sna/kgem.c
+++ b/src/sna/kgem.c
@@ -486,9 +486,9 @@ static struct list *inactive(struct kgem *kgem, int size)
 	return &kgem->inactive[cache_bucket(size)];
 }
 
-static struct list *active(struct kgem *kgem, int size)
+static struct list *active(struct kgem *kgem, int size, int tiling)
 {
-	return &kgem->active[cache_bucket(size)];
+	return &kgem->active[cache_bucket(size)][tiling];
 }
 
 static size_t
@@ -577,8 +577,10 @@ void kgem_init(struct kgem *kgem, int fd, struct pci_device *dev, int gen)
 	list_init(&kgem->flushing);
 	for (i = 0; i < ARRAY_SIZE(kgem->inactive); i++)
 		list_init(&kgem->inactive[i]);
-	for (i = 0; i < ARRAY_SIZE(kgem->active); i++)
-		list_init(&kgem->active[i]);
+	for (i = 0; i < ARRAY_SIZE(kgem->active); i++) {
+		for (j = 0; j < ARRAY_SIZE(kgem->active[i]); j++)
+			list_init(&kgem->active[i][j]);
+	}
 	for (i = 0; i < ARRAY_SIZE(kgem->vma); i++) {
 		for (j = 0; j < ARRAY_SIZE(kgem->vma[i].inactive); j++)
 			list_init(&kgem->vma[i].inactive[j]);
@@ -983,7 +985,7 @@ static void __kgem_bo_destroy(struct kgem *kgem, struct kgem_bo *bo)
 	bo->scanout = bo->flush = false;
 	if (bo->rq) {
 		DBG(("%s: handle=%d -> active\n", __FUNCTION__, bo->handle));
-		list_add(&bo->list, &kgem->active[bo->bucket]);
+		list_add(&bo->list, &kgem->active[bo->bucket][bo->tiling]);
 		return;
 	}
 
@@ -995,7 +997,7 @@ static void __kgem_bo_destroy(struct kgem *kgem, struct kgem_bo *bo)
 			DBG(("%s: handle=%d -> flushing\n",
 			     __FUNCTION__, bo->handle));
 			list_add(&bo->request, &kgem->flushing);
-			list_add(&bo->list, &kgem->active[bo->bucket]);
+			list_add(&bo->list, &kgem->active[bo->bucket][bo->tiling]);
 			bo->rq = &_kgem_static_request;
 			return;
 		}
@@ -1808,7 +1810,7 @@ search_linear_cache(struct kgem *kgem, unsigned int size, unsigned flags)
 
 	if (!use_active &&
 	    list_is_empty(inactive(kgem, size)) &&
-	    !list_is_empty(active(kgem, size)) &&
+	    !list_is_empty(active(kgem, size, I915_TILING_NONE)) &&
 	    !kgem_retire(kgem))
 		return NULL;
 
@@ -1849,7 +1851,7 @@ search_linear_cache(struct kgem *kgem, unsigned int size, unsigned flags)
 		}
 	}
 
-	cache = use_active ? active(kgem, size) : inactive(kgem, size);
+	cache = use_active ? active(kgem, size, I915_TILING_NONE) : inactive(kgem, size);
 	list_for_each_entry(bo, cache, list) {
 		assert(bo->refcnt == 0);
 		assert(bo->reusable);
@@ -2157,7 +2159,7 @@ struct kgem_bo *kgem_create_2d(struct kgem *kgem,
 {
 	struct list *cache;
 	struct kgem_bo *bo, *next;
-	uint32_t pitch, untiled_pitch, tiled_height[3], size;
+	uint32_t pitch, untiled_pitch, tiled_height, size;
 	uint32_t handle;
 	int i;
 
@@ -2232,66 +2234,96 @@ struct kgem_bo *kgem_create_2d(struct kgem *kgem,
 	if (flags & CREATE_INACTIVE)
 		goto skip_active_search;
 
-	untiled_pitch = kgem_untiled_pitch(kgem,
-					   width, bpp,
-					   flags & CREATE_SCANOUT);
-	for (i = 0; i <= I915_TILING_Y; i++)
-		tiled_height[i] = kgem_aligned_height(kgem, height, i);
-
-	/* Best active match, or first near-miss */
-	next = NULL;
-	cache = active(kgem, size);
-	list_for_each_entry(bo, cache, list) {
-		assert(bo->bucket == cache_bucket(size));
-		assert(!bo->purged);
-		assert(bo->refcnt == 0);
-		assert(bo->reusable);
+	/* Best active match */
+	cache = active(kgem, size, tiling);
+	if (tiling) {
+		tiled_height = kgem_aligned_height(kgem, height, tiling);
+		list_for_each_entry(bo, cache, list) {
+			assert(bo->bucket == cache_bucket(size));
+			assert(!bo->purged);
+			assert(bo->refcnt == 0);
+			assert(bo->reusable);
+			assert(bo->tiling == tiling);
 
-		if (bo->tiling) {
 			if (bo->pitch < pitch) {
 				DBG(("tiled and pitch too small: tiling=%d, (want %d), pitch=%d, need %d\n",
 				     bo->tiling, tiling,
 				     bo->pitch, pitch));
 				continue;
 			}
-		} else
-			bo->pitch = untiled_pitch;
 
-		if (bo->pitch * tiled_height[bo->tiling] > bo->size)
-			continue;
 
-		if (bo->tiling != tiling) {
-			if (next == NULL && bo->tiling < tiling)
-				next = bo;
-			continue;
+			if (bo->pitch * tiled_height > bo->size)
+				continue;
+
+			kgem_bo_remove_from_active(kgem, bo);
+
+			bo->unique_id = kgem_get_unique_id(kgem);
+			bo->delta = 0;
+			DBG(("  1:from active: pitch=%d, tiling=%d, handle=%d, id=%d\n",
+			     bo->pitch, bo->tiling, bo->handle, bo->unique_id));
+			return kgem_bo_reference(bo);
 		}
+	} else {
+		list_for_each_entry(bo, cache, list) {
+			assert(bo->bucket == cache_bucket(size));
+			assert(!bo->purged);
+			assert(bo->refcnt == 0);
+			assert(bo->reusable);
+			assert(bo->tiling == tiling);
 
-		kgem_bo_remove_from_active(kgem, bo);
+			if (bo->size < size)
+				continue;
 
-		bo->unique_id = kgem_get_unique_id(kgem);
-		bo->delta = 0;
-		DBG(("  1:from active: pitch=%d, tiling=%d, handle=%d, id=%d\n",
-		     bo->pitch, bo->tiling, bo->handle, bo->unique_id));
-		return kgem_bo_reference(bo);
+			kgem_bo_remove_from_active(kgem, bo);
+
+			bo->pitch = pitch;
+			bo->unique_id = kgem_get_unique_id(kgem);
+			bo->delta = 0;
+			DBG(("  1:from active: pitch=%d, tiling=%d, handle=%d, id=%d\n",
+			     bo->pitch, bo->tiling, bo->handle, bo->unique_id));
+			return kgem_bo_reference(bo);
+		}
 	}
 
-	if (next && (flags & CREATE_EXACT) == 0) {
-		list_del(&next->list);
-		if (next->rq == &_kgem_static_request)
-			list_del(&next->request);
+	if ((flags & CREATE_EXACT) == 0) { /* allow an active near-miss? */
+		untiled_pitch = kgem_untiled_pitch(kgem,
+						   width, bpp,
+						   flags & CREATE_SCANOUT);
+		i = tiling;
+		while (--i >= 0) {
+			tiled_height = kgem_surface_size(kgem,
+							 kgem->has_relaxed_fencing,
+							 flags & CREATE_SCANOUT,
+							 width, height, bpp, tiling, &pitch);
+			cache = active(kgem, tiled_height, i);
+			tiled_height = kgem_aligned_height(kgem, height, i);
+			list_for_each_entry(bo, cache, list) {
+				assert(!bo->purged);
+				assert(bo->refcnt == 0);
+				assert(bo->reusable);
 
-		if (next->purged && !kgem_bo_clear_purgeable(kgem, next)) {
-			kgem_bo_free(kgem, next);
-		} else {
-			kgem_bo_remove_from_active(kgem, next);
-
-			next->unique_id = kgem_get_unique_id(kgem);
-			next->delta = 0;
-			DBG(("  2:from active: pitch=%d, tiling=%d, handle=%d, id=%d\n",
-			     next->pitch, next->tiling, next->handle, next->unique_id));
-			assert(next->refcnt == 0);
-			assert(next->reusable);
-			return kgem_bo_reference(next);
+				if (bo->tiling) {
+					if (bo->pitch < pitch) {
+						DBG(("tiled and pitch too small: tiling=%d, (want %d), pitch=%d, need %d\n",
+						     bo->tiling, tiling,
+						     bo->pitch, pitch));
+						continue;
+					}
+				} else
+					bo->pitch = untiled_pitch;
+
+				if (bo->pitch * tiled_height > bo->size)
+					continue;
+
+				kgem_bo_remove_from_active(kgem, bo);
+
+				bo->unique_id = kgem_get_unique_id(kgem);
+				bo->delta = 0;
+				DBG(("  1:from active: pitch=%d, tiling=%d, handle=%d, id=%d\n",
+				     bo->pitch, bo->tiling, bo->handle, bo->unique_id));
+				return kgem_bo_reference(bo);
+			}
 		}
 	}
 
diff --git a/src/sna/kgem.h b/src/sna/kgem.h
index 6800b64..8a86171 100644
--- a/src/sna/kgem.h
+++ b/src/sna/kgem.h
@@ -117,7 +117,7 @@ struct kgem {
 		KGEM_BLT,
 	} mode, ring;
 
-	struct list flushing, active[NUM_CACHE_BUCKETS], inactive[NUM_CACHE_BUCKETS];
+	struct list flushing, active[NUM_CACHE_BUCKETS][3], inactive[NUM_CACHE_BUCKETS];
 	struct list partial;
 	struct list requests;
 	struct kgem_request *next_request;
commit 566cbf1ef53b1f970289fcd5b3b389a74beb7e78
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Jan 23 21:15:10 2012 +0000

    sna: Abort search for a linear bo if we encounter a purged buffer
    
    Given the rarity of encountering a purged buffer versus the frequency of
    scanning the list and the then likely result of allocation a new buffer,
    simply abort the search on the first purged bo.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/kgem.c b/src/sna/kgem.c
index 1e083ea..3bcd5cc 100644
--- a/src/sna/kgem.c
+++ b/src/sna/kgem.c
@@ -1802,7 +1802,7 @@ void kgem_cleanup_cache(struct kgem *kgem)
 static struct kgem_bo *
 search_linear_cache(struct kgem *kgem, unsigned int size, unsigned flags)
 {
-	struct kgem_bo *bo, *next, *first = NULL;
+	struct kgem_bo *bo, *first = NULL;
 	bool use_active = (flags & CREATE_INACTIVE) == 0;
 	struct list *cache;
 
@@ -1850,7 +1850,7 @@ search_linear_cache(struct kgem *kgem, unsigned int size, unsigned flags)
 	}
 
 	cache = use_active ? active(kgem, size) : inactive(kgem, size);
-	list_for_each_entry_safe(bo, next, cache, list) {
+	list_for_each_entry(bo, cache, list) {
 		assert(bo->refcnt == 0);
 		assert(bo->reusable);
 		assert(!!bo->rq == !!use_active);
@@ -1863,7 +1863,7 @@ search_linear_cache(struct kgem *kgem, unsigned int size, unsigned flags)
 
 		if (bo->purged && !kgem_bo_clear_purgeable(kgem, bo)) {
 			kgem_bo_free(kgem, bo);
-			continue;
+			break;
 		}
 
 		if (I915_TILING_NONE != bo->tiling) {
commit b8d3353624be51f2b618467c899a9ce3cbf3cbfe
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Jan 23 19:13:22 2012 +0000

    sna: Refactor tests for a solid gc to use the new function
    
    This way all paths can test to see if they might be able to reduce the
    tiled fill or the opaque fill into a solid fill.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c
index f8bee58..c6a7ca3 100644
--- a/src/sna/sna_accel.c
+++ b/src/sna/sna_accel.c
@@ -4264,7 +4264,7 @@ sna_poly_fill_rect_stippled_blt(DrawablePtr drawable,
 				GCPtr gc, int n, xRectangle *rect,
 				const BoxRec *extents, unsigned clipped);
 
-static bool
+static inline bool
 gc_is_solid(GCPtr gc, uint32_t *color)
 {
 	if (gc->fillStyle == FillSolid ||
@@ -4965,7 +4965,8 @@ static Bool
 sna_poly_point_blt(DrawablePtr drawable,
 		   struct kgem_bo *bo,
 		   struct sna_damage **damage,
-		   GCPtr gc, int mode, int n, DDXPointPtr pt,
+		   GCPtr gc, uint32_t pixel,
+		   int mode, int n, DDXPointPtr pt,
 		   bool clipped)
 {
 	PixmapPtr pixmap = get_drawable_pixmap(drawable);
@@ -4978,7 +4979,7 @@ sna_poly_point_blt(DrawablePtr drawable,
 	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))
+	if (!sna_fill_init_blt(&fill, sna, pixmap, bo, gc->alu, pixel))
 		return FALSE;
 
 	get_drawable_deltas(drawable, pixmap, &dx, &dy);
@@ -5095,6 +5096,7 @@ sna_poly_point(DrawablePtr drawable, GCPtr gc,
 	struct sna *sna = to_sna_from_pixmap(pixmap);
 	RegionRec region;
 	unsigned flags;
+	uint32_t color;
 
 	DBG(("%s(mode=%d, n=%d, pt[0]=(%d, %d)\n",
 	     __FUNCTION__, mode, n, pt[0].x, pt[0].y));
@@ -5119,8 +5121,7 @@ sna_poly_point(DrawablePtr drawable, GCPtr gc,
 		goto fallback;
 	}
 
-	if (gc->fillStyle == FillSolid &&
-	    PM_IS_SOLID(drawable, gc->planemask)) {
+	if (PM_IS_SOLID(drawable, gc->planemask) && gc_is_solid(gc, &color)) {
 		struct sna_pixmap *priv = sna_pixmap(pixmap);
 		struct sna_damage **damage;
 
@@ -5130,13 +5131,13 @@ sna_poly_point(DrawablePtr drawable, GCPtr gc,
 		if (sna_drawable_use_gpu_bo(drawable, &region.extents, &damage) &&
 		    sna_poly_point_blt(drawable,
 				       priv->gpu_bo, damage,
-				       gc, mode, n, pt, flags & 2))
+				       gc, color, mode, n, pt, flags & 2))
 			return;
 
 		if (sna_drawable_use_cpu_bo(drawable, &region.extents, &damage) &&
 		    sna_poly_point_blt(drawable,
 				       priv->cpu_bo, damage,
-				       gc, mode, n, pt, flags & 2))
+				       gc, color, mode, n, pt, flags & 2))
 			return;
 	}
 
@@ -5545,7 +5546,8 @@ static Bool
 sna_poly_line_blt(DrawablePtr drawable,
 		  struct kgem_bo *bo,
 		  struct sna_damage **damage,
-		  GCPtr gc, int mode, int n, DDXPointPtr pt,
+		  GCPtr gc, uint32_t pixel,
+		  int mode, int n, DDXPointPtr pt,
 		  const BoxRec *extents, bool clipped)
 {
 	PixmapPtr pixmap = get_drawable_pixmap(drawable);
@@ -5555,9 +5557,9 @@ sna_poly_line_blt(DrawablePtr drawable,
 	DDXPointRec last;
 	int16_t dx, dy;
 
-	DBG(("%s: alu=%d, fg=%08lx\n", __FUNCTION__, gc->alu, gc->fgPixel));
+	DBG(("%s: alu=%d, fg=%08lx\n", __FUNCTION__, gc->alu, pixel));
 
-	if (!sna_fill_init_blt(&fill, sna, pixmap, bo, gc->alu, gc->fgPixel))
+	if (!sna_fill_init_blt(&fill, sna, pixmap, bo, gc->alu, pixel))
 		return FALSE;
 
 	get_drawable_deltas(drawable, pixmap, &dx, &dy);
@@ -5949,6 +5951,7 @@ sna_poly_line(DrawablePtr drawable, GCPtr gc,
 {
 	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));
@@ -6007,9 +6010,9 @@ sna_poly_line(DrawablePtr drawable, GCPtr gc,
 		goto spans_fallback;
 	}
 
-	if (gc->fillStyle == FillSolid) {
+	if (gc_is_solid(gc, &color)) {
 		DBG(("%s: trying solid fill [%08lx]\n",
-		     __FUNCTION__, gc->fgPixel));
+		     __FUNCTION__, color));
 
 		if (data.flags & 4) {
 			if (sna_drawable_use_gpu_bo(drawable,
@@ -6017,7 +6020,7 @@ sna_poly_line(DrawablePtr drawable, GCPtr gc,
 						    &data.damage) &&
 			    sna_poly_line_blt(drawable,
 					      priv->gpu_bo, data.damage,
-					      gc, mode, n, pt,
+					      gc, color, mode, n, pt,
 					      &data.region.extents,
 					      data.flags & 2))
 				return;
@@ -6027,7 +6030,7 @@ sna_poly_line(DrawablePtr drawable, GCPtr gc,
 						    &data.damage) &&
 			    sna_poly_line_blt(drawable,
 					      priv->cpu_bo, data.damage,
-					      gc, mode, n, pt,
+					      gc, color, mode, n, pt,
 					      &data.region.extents,
 					      data.flags & 2))
 				return;
@@ -6118,8 +6121,6 @@ sna_poly_line(DrawablePtr drawable, GCPtr gc,
 spans_fallback:
 	if (use_wide_spans(drawable, gc, &data.region.extents) &&
 	    sna_drawable_use_gpu_bo(drawable, &data.region.extents, &data.damage)) {
-		uint32_t color;
-
 		DBG(("%s: converting line into spans\n", __FUNCTION__));
 		get_drawable_deltas(drawable, data.pixmap, &data.dx, &data.dy);
 		sna_gc(gc)->priv = &data;
@@ -6225,7 +6226,8 @@ static Bool
 sna_poly_segment_blt(DrawablePtr drawable,
 		     struct kgem_bo *bo,
 		     struct sna_damage **damage,
-		     GCPtr gc, int n, xSegment *seg,
+		     GCPtr gc, uint32_t pixel,
+		     int n, xSegment *seg,
 		     const BoxRec *extents, unsigned clipped)
 {
 	PixmapPtr pixmap = get_drawable_pixmap(drawable);
@@ -6237,7 +6239,7 @@ sna_poly_segment_blt(DrawablePtr drawable,
 	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, gc->fgPixel))
+	if (!sna_fill_init_blt(&fill, sna, pixmap, bo, gc->alu, pixel))
 		return FALSE;
 
 	get_drawable_deltas(drawable, pixmap, &dx, &dy);
@@ -6895,6 +6897,7 @@ 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__,
@@ -6942,9 +6945,9 @@ sna_poly_segment(DrawablePtr drawable, GCPtr gc, int n, xSegment *seg)
 		goto fallback;
 	if (gc->lineStyle != LineSolid || gc->lineWidth > 1)
 		goto spans_fallback;
-	if (gc->fillStyle == FillSolid) {
+	if (gc_is_solid(gc, &color)) {
 		DBG(("%s: trying blt solid fill [%08lx, flags=%x] paths\n",
-		     __FUNCTION__, gc->fgPixel, data.flags));
+		     __FUNCTION__, color, data.flags));
 
 		if (data.flags & 4) {
 			if (sna_drawable_use_gpu_bo(drawable,
@@ -6952,7 +6955,7 @@ sna_poly_segment(DrawablePtr drawable, GCPtr gc, int n, xSegment *seg)
 						    &data.damage) &&
 			    sna_poly_segment_blt(drawable,
 						 priv->gpu_bo, data.damage,
-						 gc, n, seg,
+						 gc, color, n, seg,
 						 &data.region.extents,
 						 data.flags & 2))
 				return;
@@ -6962,7 +6965,7 @@ sna_poly_segment(DrawablePtr drawable, GCPtr gc, int n, xSegment *seg)
 						    &data.damage) &&
 			    sna_poly_segment_blt(drawable,
 						 priv->cpu_bo, data.damage,
-						 gc, n, seg,
+						 gc, color, n, seg,
 						 &data.region.extents,
 						 data.flags & 2))
 				return;
@@ -7031,7 +7034,7 @@ sna_poly_segment(DrawablePtr drawable, GCPtr gc, int n, xSegment *seg)
 				i = sna_poly_fill_rect_stippled_blt(drawable,
 								    priv->gpu_bo, data.damage,
 								    gc, n, rect,
-								    &data.region.extents, 
+								    &data.region.extents,
 								    data.flags & 2);
 			}
 			free (rect);
@@ -7045,7 +7048,6 @@ spans_fallback:
 	if (use_wide_spans(drawable, gc, &data.region.extents) &&
 	    sna_drawable_use_gpu_bo(drawable, &data.region.extents, &data.damage)) {
 		void (*line)(DrawablePtr, GCPtr, int, int, DDXPointPtr);
-		uint32_t color;
 		int i;
 
 		DBG(("%s: converting segments into spans\n", __FUNCTION__));
@@ -9418,6 +9420,7 @@ sna_poly_fill_rect(DrawablePtr draw, GCPtr gc, int n, xRectangle *rect)
 	struct sna_damage **damage;
 	RegionRec region;
 	unsigned flags;
+	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),
@@ -9469,11 +9472,7 @@ sna_poly_fill_rect(DrawablePtr draw, GCPtr gc, int n, xRectangle *rect)
 		}
 	}
 
-	if (gc->fillStyle == FillSolid ||
-	    (gc->fillStyle == FillTiled && gc->tileIsPixel) ||
-	    (gc->fillStyle == FillOpaqueStippled && gc->bgPixel == gc->fgPixel)) {
-		uint32_t color = gc->fillStyle == FillTiled ? gc->tile.pixel : gc->fgPixel;
-
+	if (gc_is_solid(gc, &color)) {
 		DBG(("%s: solid fill [%08x], testing for blt\n",
 		     __FUNCTION__, color));
 
commit 78238b3a37c1c8a152bd3920e73aa6034c0f663f
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Jan 23 18:00:05 2012 +0000

    sna: Prefer mi span filling functions to call through to the GPU
    
    Having removed the double analysis for the fast paths, at least, the
    span filling code on the GPU is now faster than doing the same
    operations in cache memory for the majority of cases. So allow the
    driver to prefer to use those functions when it has a GPU bo.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c
index 6fd5fc4..f8bee58 100644
--- a/src/sna/sna_accel.c
+++ b/src/sna/sna_accel.c
@@ -59,7 +59,7 @@
 #define FORCE_FLUSH 0
 
 #define USE_INPLACE 1
-#define USE_WIDE_SPANS 0 /* -1 force CPU, 1 force GPU */
+#define USE_WIDE_SPANS 1 /* -1 force CPU, 1 force GPU */
 #define USE_ZERO_SPANS 1 /* -1 force CPU, 1 force GPU */
 #define USE_BO_FOR_SCRATCH_PIXMAP 1
 
commit c5c77d04d79666962fd7c2d86bc6e7aef716084b
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Jan 23 18:24:21 2012 +0000

    sna: Reduce GC to solid if possible along general spans path
    
    Again, we should no longer be hitting this code if at all possible, but
    for completeness...
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c
index f1486c0..6fd5fc4 100644
--- a/src/sna/sna_accel.c
+++ b/src/sna/sna_accel.c
@@ -3951,8 +3951,8 @@ sna_fill_spans__fill_clip_boxes(DrawablePtr drawable,
 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,
+		   GCPtr gc, uint32_t pixel,
+		   int n, DDXPointPtr pt, int *width, int sorted,
 		   const BoxRec *extents, unsigned clipped)
 {
 	PixmapPtr pixmap = get_drawable_pixmap(drawable);
@@ -3971,7 +3971,7 @@ sna_fill_spans_blt(DrawablePtr drawable,
 	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, gc->fgPixel))
+	if (!sna_fill_init_blt(&fill, sna, pixmap, bo, gc->alu, pixel))
 		return false;
 
 	get_drawable_deltas(drawable, pixmap, &dx, &dy);
@@ -4264,11 +4264,25 @@ sna_poly_fill_rect_stippled_blt(DrawablePtr drawable,
 				GCPtr gc, int n, xRectangle *rect,
 				const BoxRec *extents, unsigned clipped);
 
+static 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));
@@ -4277,10 +4291,10 @@ sna_fill_spans__gpu(DrawablePtr drawable, GCPtr gc, int n,
 	if (n == 0)
 		return;
 
-	if (gc->fillStyle == FillSolid) {
+	if (gc_is_solid(gc, &color)) {
 		sna_fill_spans_blt(drawable,
 				   data->bo, data->damage,
-				   gc, n, pt, width, sorted,
+				   gc, color, n, pt, width, sorted,
 				   &data->region.extents, data->flags & 2);
 	} else {
 		/* Try converting these to a set of rectangles instead */
@@ -4364,6 +4378,7 @@ sna_fill_spans(DrawablePtr drawable, GCPtr gc, int n,
 	struct sna *sna = to_sna_from_pixmap(pixmap);
 	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));
@@ -4393,7 +4408,7 @@ sna_fill_spans(DrawablePtr drawable, GCPtr gc, int n,
 	if (!PM_IS_SOLID(drawable, gc->planemask))
 		goto fallback;
 
-	if (gc->fillStyle == FillSolid) {
+	if (gc_is_solid(gc, &color)) {
 		struct sna_pixmap *priv = sna_pixmap(pixmap);
 		struct sna_damage **damage;
 
@@ -4403,14 +4418,14 @@ sna_fill_spans(DrawablePtr drawable, GCPtr gc, int n,
 		if (sna_drawable_use_gpu_bo(drawable, &region.extents, &damage) &&
 		    sna_fill_spans_blt(drawable,
 				       priv->gpu_bo, damage,
-				       gc, n, pt, width, sorted,
+				       gc, color, n, pt, width, sorted,
 				       &region.extents, flags & 2))
 			return;
 
 		if (sna_drawable_use_cpu_bo(drawable, &region.extents, &damage) &&
 		    sna_fill_spans_blt(drawable,
 				       priv->cpu_bo, damage,
-				       gc, n, pt, width, sorted,
+				       gc, color, n, pt, width, sorted,
 				       &region.extents, flags & 2))
 			return;
 	} else {
@@ -5928,19 +5943,6 @@ use_wide_spans(DrawablePtr drawable, GCPtr gc, const BoxRec *extents)
 	return ret;
 }
 
-static 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_poly_line(DrawablePtr drawable, GCPtr gc,
 	      int mode, int n, DDXPointPtr pt)
commit 03611f662db854dc3b14f32c0315bf45bc415bbf
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Jan 23 19:59:06 2012 +0000

    sna: Guard against being passed n==0 to FillSpans
    
    Apparently this does happen, notably by miFillPolyArc.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c
index 291f5b2..f1486c0 100644
--- a/src/sna/sna_accel.c
+++ b/src/sna/sna_accel.c
@@ -3789,8 +3789,7 @@ sna_fill_spans__fill(DrawablePtr drawable,
 	DBG(("%s: alu=%d, fg=%08lx, count=%d\n",
 	     __FUNCTION__, gc->alu, gc->fgPixel, n));
 
-	assert(n);
-	do {
+	while (n) {
 		BoxRec *b = box;
 		int nbox = n;
 		if (nbox > ARRAY_SIZE(box))
@@ -3807,7 +3806,7 @@ sna_fill_spans__fill(DrawablePtr drawable,
 		} while (--nbox);
 		if (b != box)
 			op->boxes(data->sna, op, box, b - box);
-	} while (n);
+	}
 }
 
 static void
@@ -3821,7 +3820,7 @@ sna_fill_spans__fill_offset(DrawablePtr drawable,
 
 	DBG(("%s: alu=%d, fg=%08lx\n", __FUNCTION__, gc->alu, gc->fgPixel));
 
-	do {
+	while (n) {
 		BoxRec *b = box;
 		int nbox = n;
 		if (nbox > ARRAY_SIZE(box))
@@ -3838,7 +3837,7 @@ sna_fill_spans__fill_offset(DrawablePtr drawable,
 		} while (--nbox);
 		if (b != box)
 			op->boxes(data->sna, op, box, b - box);
-	} while (n);
+	}
 }
 
 static void
@@ -3856,8 +3855,7 @@ sna_fill_spans__fill_clip_extents(DrawablePtr drawable,
 	     extents->x1, extents->y1,
 	     extents->x2, extents->y2));
 
-	assert(n);
-	do {
+	while (n--) {
 		*(DDXPointRec *)b = *pt++;
 		b->x2 = b->x1 + (int)*width++;
 		b->y2 = b->y1 + 1;
@@ -3871,7 +3869,7 @@ sna_fill_spans__fill_clip_extents(DrawablePtr drawable,
 				b = box;
 			}
 		}
-	} while (--n);
+	}
 	if (b != box)
 		op->boxes(data->sna, op, box, b - box);
 }
@@ -3892,8 +3890,7 @@ sna_fill_spans__fill_clip_boxes(DrawablePtr drawable,
 	     data->region.extents.x1, data->region.extents.y1,
 	     data->region.extents.x2, data->region.extents.y2));
 
-	assert(n);
-	do {
+	while (n--) {
 		int16_t X1 = pt->x;
 		int16_t y = pt->y;
 		int16_t X2 = X1 + (int)*width;
@@ -3946,7 +3943,7 @@ sna_fill_spans__fill_clip_boxes(DrawablePtr drawable,
 				b = box;
 			}
 		}
-	} while (--n);
+	}
 	if (b != box)
 		op->boxes(data->sna, op, box, b - box);
 }
@@ -4277,6 +4274,8 @@ sna_fill_spans__gpu(DrawablePtr drawable, GCPtr gc, int n,
 	     __FUNCTION__, n, pt[0].x, pt[0].y, width[0], sorted));
 
 	assert(PM_IS_SOLID(drawable, gc->planemask));
+	if (n == 0)
+		return;
 
 	if (gc->fillStyle == FillSolid) {
 		sna_fill_spans_blt(drawable,
commit 7ac13a4d5aca1627b3a5fc9e7261d5dfafba970b
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Jan 24 00:21:12 2012 +0000

    sna: Provide a fast path for miZeroLine for PolySegment
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c
index f2e810d..291f5b2 100644
--- a/src/sna/sna_accel.c
+++ b/src/sna/sna_accel.c
@@ -7044,6 +7044,7 @@ spans_fallback:
 	if (use_wide_spans(drawable, gc, &data.region.extents) &&
 	    sna_drawable_use_gpu_bo(drawable, &data.region.extents, &data.damage)) {
 		void (*line)(DrawablePtr, GCPtr, int, int, DDXPointPtr);
+		uint32_t color;
 		int i;
 
 		DBG(("%s: converting segments into spans\n", __FUNCTION__));
@@ -7066,14 +7067,50 @@ spans_fallback:
 		}
 
 		get_drawable_deltas(drawable, data.pixmap, &data.dx, &data.dy);
-
-		data.bo = priv->gpu_bo;
 		sna_gc(gc)->priv = &data;
-		gc->ops->FillSpans = sna_fill_spans__gpu;
 
-		for (i = 0; i < n; i++)
-			line(drawable, gc, CoordModeOrigin, 2,
-			     (DDXPointPtr)&seg[i]);
+		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,
+					       priv->gpu_bo, gc->alu, color))
+				goto fallback;
+
+			data.op = &fill;
+
+			if ((data.flags & 2) == 0) {
+				if (data.dx | data.dy)
+					gc->ops->FillSpans = sna_fill_spans__fill_offset;
+				else
+					gc->ops->FillSpans = sna_fill_spans__fill;
+			} else {
+				region_maybe_clip(&data.region,
+						  gc->pCompositeClip);
+				if (!RegionNotEmpty(&data.region))
+					return;
+
+				if (region_is_singular(&data.region))
+					gc->ops->FillSpans = sna_fill_spans__fill_clip_extents;
+				else
+					gc->ops->FillSpans = sna_fill_spans__fill_clip_boxes;
+			}
+			assert(gc->miTranslate);
+			for (i = 0; i < n; i++)
+				line(drawable, gc, CoordModeOrigin, 2,
+				     (DDXPointPtr)&seg[i]);
+
+			fill.done(data.sna, &fill);
+		} else {
+			data.bo = priv->gpu_bo;
+			gc->ops->FillSpans = sna_fill_spans__gpu;
+
+			for (i = 0; i < n; i++)
+				line(drawable, gc, CoordModeOrigin, 2,
+				     (DDXPointPtr)&seg[i]);
+		}
 
 		gc->ops->FillSpans = sna_fill_spans;
 		if (data.damage)
commit e7817a2206bd0b1cc4e4458686c328f7b41ea32c
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Jan 23 17:43:55 2012 +0000

    sna: Override sna_fill_spans for PolySegment
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c
index 510f017..f2e810d 100644
--- a/src/sna/sna_accel.c
+++ b/src/sna/sna_accel.c
@@ -6892,24 +6892,24 @@ sna_poly_segment_extents(DrawablePtr drawable, GCPtr gc,
 static void
 sna_poly_segment(DrawablePtr drawable, GCPtr gc, int n, xSegment *seg)
 {
-	PixmapPtr pixmap;
-	struct sna *sna;
-	struct sna_damage **damage;
-	RegionRec region;
-	unsigned flags;
+	struct sna_pixmap *priv;
+	struct sna_fill_spans data;
 
 	DBG(("%s(n=%d, first=((%d, %d), (%d, %d)), lineWidth=%d\n",
 	     __FUNCTION__,
 	     n, seg->x1, seg->y1, seg->x2, seg->y2,
 	     gc->lineWidth));
 
-	flags = sna_poly_segment_extents(drawable, gc, n, seg, &region.extents);
-	if (flags == 0)
+	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__,
-	     region.extents.x1, region.extents.y1,
-	     region.extents.x2, region.extents.y2));
+	     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;
@@ -6917,10 +6917,15 @@ sna_poly_segment(DrawablePtr drawable, GCPtr gc, int n, xSegment *seg)
 	if (!ACCEL_POLY_SEGMENT)
 		goto fallback;
 
-	pixmap = get_drawable_pixmap(drawable);
-	sna = to_sna_from_pixmap(pixmap);
+	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(sna)) {
+	if (wedged(data.sna)) {
 		DBG(("%s: fallback -- wedged\n", __FUNCTION__));
 		goto fallback;
 	}
@@ -6931,50 +6936,50 @@ sna_poly_segment(DrawablePtr drawable, GCPtr gc, int n, xSegment *seg)
 	     gc->lineStyle, gc->lineStyle == LineSolid,
 	     gc->lineWidth,
 	     gc->planemask, PM_IS_SOLID(drawable, gc->planemask),
-	     flags & 4));
+	     data.flags & 4));
 	if (!PM_IS_SOLID(drawable, gc->planemask))
 		goto fallback;
 	if (gc->lineStyle != LineSolid || gc->lineWidth > 1)
 		goto spans_fallback;
 	if (gc->fillStyle == FillSolid) {
-		struct sna_pixmap *priv = sna_pixmap(pixmap);
-
 		DBG(("%s: trying blt solid fill [%08lx, flags=%x] paths\n",
-		     __FUNCTION__, gc->fgPixel,flags));
+		     __FUNCTION__, gc->fgPixel, data.flags));
 
-		if (flags & 4) {
+		if (data.flags & 4) {
 			if (sna_drawable_use_gpu_bo(drawable,
-						    &region.extents,
-						    &damage) &&
+						    &data.region.extents,
+						    &data.damage) &&
 			    sna_poly_segment_blt(drawable,
-						 priv->gpu_bo, damage,
+						 priv->gpu_bo, data.damage,
 						 gc, n, seg,
-						 &region.extents, flags & 2))
+						 &data.region.extents,
+						 data.flags & 2))
 				return;
 
 			if (sna_drawable_use_cpu_bo(drawable,
-						    &region.extents,
-						    &damage) &&
+						    &data.region.extents,
+						    &data.damage) &&
 			    sna_poly_segment_blt(drawable,
-						 priv->cpu_bo, damage,
+						 priv->cpu_bo, data.damage,
 						 gc, n, seg,
-						 &region.extents, flags & 2))
+						 &data.region.extents,
+						 data.flags & 2))
 				return;
 		} else {
-			if (use_zero_spans(drawable, gc, &region.extents) &&
+			if (use_zero_spans(drawable, gc, &data.region.extents) &&
 			    sna_drawable_use_gpu_bo(drawable,
-						    &region.extents,
-						    &damage) &&
+						    &data.region.extents,
+						    &data.damage) &&
 			    sna_poly_zero_segment_blt(drawable,
-						      priv->gpu_bo, damage,
-						      gc, n, seg, &region.extents, flags & 2))
+						      priv->gpu_bo, data.damage,
+						      gc, n, seg,
+						      &data.region.extents,
+						      data.flags & 2))
 				return;
 		}
-	} else if (flags & 4) {
-		struct sna_pixmap *priv = sna_pixmap(pixmap);
-
+	} else if (data.flags & 4) {
 		/* Try converting these to a set of rectangles instead */
-		if (sna_drawable_use_gpu_bo(drawable, &region.extents, &damage)) {
+		if (sna_drawable_use_gpu_bo(drawable, &data.region.extents, &data.damage)) {
 			xRectangle *rect;
 			int i;
 
@@ -7017,14 +7022,16 @@ sna_poly_segment(DrawablePtr drawable, GCPtr gc, int n, xSegment *seg)
 
 			if (gc->fillStyle == FillTiled) {
 				i = sna_poly_fill_rect_tiled_blt(drawable,
-								 priv->gpu_bo, damage,
+								 priv->gpu_bo, data.damage,
 								 gc, n, rect,
-								 &region.extents, flags & 2);
+								 &data.region.extents,
+								 data.flags & 2);
 			} else {
 				i = sna_poly_fill_rect_stippled_blt(drawable,
-								    priv->gpu_bo, damage,
+								    priv->gpu_bo, data.damage,
 								    gc, n, rect,
-								    &region.extents, flags & 2);
+								    &data.region.extents, 
+								    data.flags & 2);
 			}
 			free (rect);
 
@@ -7033,10 +7040,9 @@ sna_poly_segment(DrawablePtr drawable, GCPtr gc, int n, xSegment *seg)
 		}
 	}
 
-	/* XXX Do we really want to base this decision on the amalgam ? */
 spans_fallback:
-	if (use_wide_spans(drawable, gc, &region.extents) &&
-	    sna_drawable_use_gpu_bo(drawable, &region.extents, &damage)) {
+	if (use_wide_spans(drawable, gc, &data.region.extents) &&
+	    sna_drawable_use_gpu_bo(drawable, &data.region.extents, &data.damage)) {
 		void (*line)(DrawablePtr, GCPtr, int, int, DDXPointPtr);
 		int i;
 
@@ -7052,39 +7058,53 @@ spans_fallback:
 			break;
 		case LineOnOffDash:
 		case LineDoubleDash:
-			line = miWideDash;
+			if (gc->lineWidth == 0)
+				line = miZeroDashLine;
+			else
+				line = miWideDash;
 			break;
 		}
 
+		get_drawable_deltas(drawable, data.pixmap, &data.dx, &data.dy);
+
+		data.bo = priv->gpu_bo;
+		sna_gc(gc)->priv = &data;
+		gc->ops->FillSpans = sna_fill_spans__gpu;
+
 		for (i = 0; i < n; i++)
 			line(drawable, gc, CoordModeOrigin, 2,
 			     (DDXPointPtr)&seg[i]);
+
+		gc->ops->FillSpans = sna_fill_spans;
+		if (data.damage)
+			sna_damage_add(data.damage, &data.region);
+		RegionUninit(&data.region);
 		return;
 	}
 
 fallback:
 	DBG(("%s: fallback\n", __FUNCTION__));
-	if (gc->lineWidth) {
-		miPolySegment(drawable, gc, n, seg);
-		return;
-	}
-
-	region.data = NULL;
-	region_maybe_clip(&region, gc->pCompositeClip);
-	if (!RegionNotEmpty(&region))
+	region_maybe_clip(&data.region, gc->pCompositeClip);
+	if (!RegionNotEmpty(&data.region))
 		return;
 
 	if (!sna_gc_move_to_cpu(gc, drawable))
 		goto out;
-	if (!sna_drawable_move_region_to_cpu(drawable, &region,
+	if (!sna_drawable_move_region_to_cpu(drawable, &data.region,
 					     drawable_gc_flags(drawable, gc,
 							       n > 1)))
 		goto out;
 
+	/* Install FillSpans in case we hit a fallback path in fbPolySegment */
+	sna_gc(gc)->priv = &data.region;
+	gc->ops->FillSpans = sna_fill_spans__cpu;
+
 	DBG(("%s: fbPolySegment\n", __FUNCTION__));
 	fbPolySegment(drawable, gc, n, seg);
+
+	gc->ops->FillSpans = sna_fill_spans;
 out:
-	RegionUninit(&region);
+	RegionUninit(&data.region);
 }
 
 static unsigned
commit 5b8db54d0fa9eab2610feed6947e917f2e400e1a
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Jan 24 00:21:12 2012 +0000

    sna: Provide a fast path for miZeroLine for PolyLine
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c
index e4db541..510f017 100644
--- a/src/sna/sna_accel.c
+++ b/src/sna/sna_accel.c
@@ -6121,37 +6121,71 @@ spans_fallback:
 
 		DBG(("%s: converting line into spans\n", __FUNCTION__));
 		get_drawable_deltas(drawable, data.pixmap, &data.dx, &data.dy);
-
-		/* Note that the WideDash functions alternate between filling
-		 * using fgPixel and bgPixel so we need to reset state between
-		 * FillSpans.
-		 */
-		data.bo = priv->gpu_bo;
 		sna_gc(gc)->priv = &data;
-		gc->ops->FillSpans = sna_fill_spans__gpu;
 
-		switch (gc->lineStyle) {
-		default:
-			assert(0);
-		case LineSolid:
-			if (gc->lineWidth == 0) {
-				DBG(("%s: miZeroLine\n", __FUNCTION__));
-				miZeroLine(drawable, gc, mode, n, pt);
+		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,
+					       priv->gpu_bo, gc->alu, color))
+				goto fallback;
+
+			data.op = &fill;
+
+			if ((data.flags & 2) == 0) {
+				if (data.dx | data.dy)
+					gc->ops->FillSpans = sna_fill_spans__fill_offset;
+				else
+					gc->ops->FillSpans = sna_fill_spans__fill;
 			} else {
-				DBG(("%s: miWideLine\n", __FUNCTION__));
-				miWideLine(drawable, gc, mode, n, pt);
+				region_maybe_clip(&data.region,
+						  gc->pCompositeClip);
+				if (!RegionNotEmpty(&data.region))
+					return;
+
+				if (region_is_singular(&data.region))
+					gc->ops->FillSpans = sna_fill_spans__fill_clip_extents;
+				else
+					gc->ops->FillSpans = sna_fill_spans__fill_clip_boxes;
 			}
-			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);
+			assert(gc->miTranslate);
+
+			miZeroLine(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.
+			 */
+			data.bo = priv->gpu_bo;
+			gc->ops->FillSpans = sna_fill_spans__gpu;
+
+			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;
 			}
-			break;
 		}
 
 		gc->ops->FillSpans = sna_fill_spans;
commit 280a9165db5382b93ab449158106bdd5aa7b2329
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Jan 23 17:43:55 2012 +0000

    sna: Override sna_fill_spans for PolyLine
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c
index cb3bef3..e4db541 100644
--- a/src/sna/sna_accel.c
+++ b/src/sna/sna_accel.c
@@ -5929,28 +5929,39 @@ use_wide_spans(DrawablePtr drawable, GCPtr gc, const BoxRec *extents)
 	return ret;
 }
 
+static 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_poly_line(DrawablePtr drawable, GCPtr gc,
 	      int mode, int n, DDXPointPtr pt)
 {
-	PixmapPtr pixmap;
-	struct sna *sna;
 	struct sna_pixmap *priv;
-	struct sna_damage **damage;
-	RegionRec region;
-	unsigned flags;
+	struct sna_fill_spans data;
 
 	DBG(("%s(mode=%d, n=%d, pt[0]=(%d, %d), lineWidth=%d\n",
 	     __FUNCTION__, mode, n, pt[0].x, pt[0].y, gc->lineWidth));
 
-	flags = sna_poly_line_extents(drawable, gc, mode, n, pt,
-				      &region.extents);
-	if (flags == 0)
+	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__,
-	     region.extents.x1, region.extents.y1,
-	     region.extents.x2, region.extents.y2));
+	     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;
@@ -5958,9 +5969,9 @@ sna_poly_line(DrawablePtr drawable, GCPtr gc,
 	if (!ACCEL_POLY_LINE)
 		goto fallback;
 
-	pixmap = get_drawable_pixmap(drawable);
-	sna = to_sna_from_pixmap(pixmap);
-	if (wedged(sna)) {
+	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;
 	}
@@ -5971,15 +5982,15 @@ sna_poly_line(DrawablePtr drawable, GCPtr gc,
 	     gc->lineStyle, gc->lineStyle == LineSolid,
 	     gc->lineWidth,
 	     gc->planemask, PM_IS_SOLID(drawable, gc->planemask),
-	     flags & 4));
+	     data.flags & 4));
 
 	if (!PM_IS_SOLID(drawable, gc->planemask))
 		goto fallback;
 
-	priv = sna_pixmap(pixmap);
+	priv = sna_pixmap(data.pixmap);
 	if (!priv) {
 		DBG(("%s: not attached to pixmap %ld\n",
-		     __FUNCTION__, pixmap->drawable.serialNumber));
+		     __FUNCTION__, data.pixmap->drawable.serialNumber));
 		goto fallback;
 	}
 
@@ -5999,39 +6010,42 @@ sna_poly_line(DrawablePtr drawable, GCPtr gc,
 		DBG(("%s: trying solid fill [%08lx]\n",
 		     __FUNCTION__, gc->fgPixel));
 
-		if (flags & 4) {
+		if (data.flags & 4) {
 			if (sna_drawable_use_gpu_bo(drawable,
-						    &region.extents,
-						    &damage) &&
+						    &data.region.extents,
+						    &data.damage) &&
 			    sna_poly_line_blt(drawable,
-					      priv->gpu_bo, damage,
+					      priv->gpu_bo, data.damage,
 					      gc, mode, n, pt,
-					      &region.extents, flags & 2))
+					      &data.region.extents,
+					      data.flags & 2))
 				return;
 
 			if (sna_drawable_use_cpu_bo(drawable,
-						    &region.extents,
-						    &damage) &&
+						    &data.region.extents,
+						    &data.damage) &&
 			    sna_poly_line_blt(drawable,
-					      priv->cpu_bo, damage,
+					      priv->cpu_bo, data.damage,
 					      gc, mode, n, pt,
-					      &region.extents, flags & 2))
+					      &data.region.extents,
+					      data.flags & 2))
 				return;
 		} else { /* !rectilinear */
-			if (use_zero_spans(drawable, gc, &region.extents) &&
+			if (use_zero_spans(drawable, gc, &data.region.extents) &&
 			    sna_drawable_use_gpu_bo(drawable,
-						    &region.extents,
-						    &damage) &&
+						    &data.region.extents,
+						    &data.damage) &&
 			    sna_poly_zero_line_blt(drawable,
-						   priv->gpu_bo, damage,
+						   priv->gpu_bo, data.damage,
 						   gc, mode, n, pt,
-						   &region.extents, flags & 2))
+						   &data.region.extents,
+						   data.flags & 2))
 				return;
 
 		}
-	} else if (flags & 4) {
+	} else if (data.flags & 4) {
 		/* Try converting these to a set of rectangles instead */
-		if (sna_drawable_use_gpu_bo(drawable, &region.extents, &damage)) {
+		if (sna_drawable_use_gpu_bo(drawable, &data.region.extents, &data.damage)) {
 			DDXPointRec p1, p2;
 			xRectangle *rect;
 			int i;
@@ -6047,9 +6061,8 @@ sna_poly_line(DrawablePtr drawable, GCPtr gc,
 				if (mode == CoordModePrevious) {
 					p2.x = p1.x + pt[i].x;
 					p2.y = p1.y + pt[i].y;
-				} else {
+				} else
 					p2 = pt[i];
-				}
 				if (p1.x < p2.x) {
 					rect[i].x = p1.x;
 					rect[i].width = p2.x - p1.x + 1;
@@ -6083,14 +6096,16 @@ sna_poly_line(DrawablePtr drawable, GCPtr gc,
 
 			if (gc->fillStyle == FillTiled) {
 				i = sna_poly_fill_rect_tiled_blt(drawable,
-								 priv->gpu_bo, damage,
+								 priv->gpu_bo, data.damage,
 								 gc, n - 1, rect + 1,
-								 &region.extents, flags & 2);
+								 &data.region.extents,
+								 data.flags & 2);
 			} else {
 				i = sna_poly_fill_rect_stippled_blt(drawable,
-								    priv->gpu_bo, damage,
+								    priv->gpu_bo, data.damage,
 								    gc, n - 1, rect + 1,
-								    &region.extents, flags & 2);
+								    &data.region.extents,
+								    data.flags & 2);
 			}
 			free (rect);
 
@@ -6100,9 +6115,21 @@ sna_poly_line(DrawablePtr drawable, GCPtr gc,
 	}
 
 spans_fallback:
-	if (use_wide_spans(drawable, gc, &region.extents) &&
-	    sna_drawable_use_gpu_bo(drawable, &region.extents, &damage)) {
+	if (use_wide_spans(drawable, gc, &data.region.extents) &&
+	    sna_drawable_use_gpu_bo(drawable, &data.region.extents, &data.damage)) {
+		uint32_t color;
+
 		DBG(("%s: converting line into spans\n", __FUNCTION__));
+		get_drawable_deltas(drawable, data.pixmap, &data.dx, &data.dy);
+
+		/* Note that the WideDash functions alternate between filling
+		 * using fgPixel and bgPixel so we need to reset state between
+		 * FillSpans.
+		 */
+		data.bo = priv->gpu_bo;
+		sna_gc(gc)->priv = &data;
+		gc->ops->FillSpans = sna_fill_spans__gpu;
+
 		switch (gc->lineStyle) {
 		default:
 			assert(0);
@@ -6126,35 +6153,37 @@ spans_fallback:
 			}
 			break;
 		}
+
+		gc->ops->FillSpans = sna_fill_spans;
+		if (data.damage)
+			sna_damage_add(data.damage, &data.region);
+		RegionUninit(&data.region);
 		return;
 	}
 
 fallback:
 	DBG(("%s: fallback\n", __FUNCTION__));
-	if (gc->lineWidth) {
-		if (gc->lineStyle != LineSolid)
-			miWideDash(drawable, gc, mode, n, pt);
-		else
-			miWideLine(drawable, gc, mode, n, pt);
-		return;
-	}
-
-	region.data = NULL;
-	region_maybe_clip(&region, gc->pCompositeClip);
-	if (!RegionNotEmpty(&region))
+	region_maybe_clip(&data.region, gc->pCompositeClip);
+	if (!RegionNotEmpty(&data.region))
 		return;
 
 	if (!sna_gc_move_to_cpu(gc, drawable))
 		goto out;
-	if (!sna_drawable_move_region_to_cpu(drawable, &region,
+	if (!sna_drawable_move_region_to_cpu(drawable, &data.region,
 					     drawable_gc_flags(drawable, gc,
 							       n > 2)))
 		goto out;
 
+	/* Install FillSpans in case we hit a fallback path in fbPolyLine */
+	sna_gc(gc)->priv = &data.region;
+	gc->ops->FillSpans = sna_fill_spans__cpu;
+
 	DBG(("%s: fbPolyLine\n", __FUNCTION__));
 	fbPolyLine(drawable, gc, mode, n, pt);
+
+	gc->ops->FillSpans = sna_fill_spans;
 out:
-	RegionUninit(&region);
+	RegionUninit(&data.region);
 }
 
 static Bool
@@ -7628,19 +7657,6 @@ sna_poly_arc_extents(DrawablePtr drawable, GCPtr gc,
 	return 1 | clipped << 1;
 }
 
-static 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_poly_arc(DrawablePtr drawable, GCPtr gc, int n, xArc *arc)
 {
commit 72fc3122b85fe1a1a16c7f7401c300089273fd88
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Jan 24 01:27:08 2012 +0000

    sna: Prefer to always use span generation for PolyArc
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c
index ea57004..cb3bef3 100644
--- a/src/sna/sna_accel.c
+++ b/src/sna/sna_accel.c
@@ -7629,21 +7629,6 @@ sna_poly_arc_extents(DrawablePtr drawable, GCPtr gc,
 }
 
 static bool
-arc_to_spans(GCPtr gc, int n)
-{
-	if (gc->lineStyle != LineSolid)
-		return false;
-
-	if (gc->lineWidth == 0)
-		return true;
-
-	if (n == 1)
-		return true;
-
-	return false;
-}
-
-static bool
 gc_is_solid(GCPtr gc, uint32_t *color)
 {
 	if (gc->fillStyle == FillSolid ||
@@ -7697,9 +7682,7 @@ sna_poly_arc(DrawablePtr drawable, GCPtr gc, int n, xArc *arc)
 	if (!PM_IS_SOLID(drawable, gc->planemask))
 		goto fallback;
 
-	/* For "simple" cases use the miPolyArc to spans path */
 	if (use_wide_spans(drawable, gc, &data.region.extents) &&
-	    arc_to_spans(gc, n) &&
 	    sna_drawable_use_gpu_bo(drawable,
 				    &data.region.extents, &data.damage)) {
 		uint32_t color;
commit 98879e3786b7f6241378a32a10f2d46eb01baf85
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Jan 23 23:22:00 2012 +0000

    sna: Specialise PolyPoint for callback from miZeroPolyArc
    
    miZeroPolyArc may use either FillSpans for PolyPoint to generate its
    curves, so also provide custom point filling routines.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c
index 429ec2f..ea57004 100644
--- a/src/sna/sna_accel.c
+++ b/src/sna/sna_accel.c
@@ -3670,6 +3670,114 @@ struct sna_fill_spans {
 };
 
 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__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)
@@ -4959,6 +5067,13 @@ sna_poly_point_extents(DrawablePtr drawable, GCPtr gc,
 }
 
 static void
+sna_poly_point__cpu(DrawablePtr drawable, GCPtr gc,
+	       int mode, int n, DDXPointPtr pt)
+{
+	fbPolyPoint(drawable, gc, mode, n, pt);
+}
+
+static void
 sna_poly_point(DrawablePtr drawable, GCPtr gc,
 	       int mode, int n, DDXPointPtr pt)
 {
@@ -7608,22 +7723,28 @@ sna_poly_arc(DrawablePtr drawable, GCPtr gc, int n, xArc *arc)
 					gc->ops->FillSpans = sna_fill_spans__fill_offset;
 				else
 					gc->ops->FillSpans = sna_fill_spans__fill;
+				gc->ops->PolyPoint = sna_poly_point__fill;
 			} else {
 				region_maybe_clip(&data.region,
 						  gc->pCompositeClip);
 				if (!RegionNotEmpty(&data.region))
 					return;
 
-				if (region_is_singular(&data.region))
+				if (region_is_singular(&data.region)) {
 					gc->ops->FillSpans = sna_fill_spans__fill_clip_extents;
-				else
+					gc->ops->PolyPoint = sna_poly_point__fill_clip_extents;
+				} else {
 					gc->ops->FillSpans = sna_fill_spans__fill_clip_boxes;
+					gc->ops->PolyPoint = sna_poly_point__fill_clip_boxes;
+				}
 			}
 			assert(gc->miTranslate);
 			if (gc->lineWidth == 0)
 				miZeroPolyArc(drawable, gc, n, arc);
 			else
 				miPolyArc(drawable, gc, n, arc);
+
+			gc->ops->PolyPoint = sna_poly_point;
 			gc->ops->FillSpans = sna_fill_spans;
 
 			fill.done(data.sna, &fill);
@@ -7657,10 +7778,12 @@ fallback:
 	/* Install FillSpans in case we hit a fallback path in fbPolyArc */
 	sna_gc(gc)->priv = &data.region;
 	gc->ops->FillSpans = sna_fill_spans__cpu;
+	gc->ops->PolyPoint = sna_poly_point__cpu;
 
 	DBG(("%s -- fbPolyArc\n", __FUNCTION__));
 	fbPolyArc(drawable, gc, n, arc);
 
+	gc->ops->PolyPoint = sna_poly_point;
 	gc->ops->FillSpans = sna_fill_spans;
 out:
 	RegionUninit(&data.region);
commit 5b10cb4a5ce84bd677b0a67a17d8674defb19f31
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Jan 23 17:02:46 2012 +0000

    sna: Override sna_fill_spans for PolyArc
    
    This is the missing like that allows the GPU to outperform fbPolyArc.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c
index f6495d8..429ec2f 100644
--- a/src/sna/sna_accel.c
+++ b/src/sna/sna_accel.c
@@ -7528,22 +7528,37 @@ arc_to_spans(GCPtr gc, int n)
 	return false;
 }
 
+static 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_poly_arc(DrawablePtr drawable, GCPtr gc, int n, xArc *arc)
 {
-	PixmapPtr pixmap = get_drawable_pixmap(drawable);
-	struct sna *sna = to_sna_from_pixmap(pixmap);
-	struct sna_damage **damage;
-	RegionRec region;
+	struct sna_fill_spans data;
+	struct sna_pixmap *priv;
 
 	DBG(("%s(n=%d, lineWidth=%d\n", __FUNCTION__, n, gc->lineWidth));
 
-	if (sna_poly_arc_extents(drawable, gc, n, arc, &region.extents) == 0)
+	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)\n", __FUNCTION__,
-	     region.extents.x1, region.extents.y1,
-	     region.extents.x2, region.extents.y2));
+	     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;
@@ -7551,7 +7566,15 @@ sna_poly_arc(DrawablePtr drawable, GCPtr gc, int n, xArc *arc)
 	if (!ACCEL_POLY_ARC)
 		goto fallback;
 
-	if (wedged(sna)) {
+	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;
 	}
@@ -7560,10 +7583,56 @@ sna_poly_arc(DrawablePtr drawable, GCPtr gc, int n, xArc *arc)
 		goto fallback;
 
 	/* For "simple" cases use the miPolyArc to spans path */
-	if (use_wide_spans(drawable, gc, &region.extents) &&
+	if (use_wide_spans(drawable, gc, &data.region.extents) &&
 	    arc_to_spans(gc, n) &&
-	    sna_drawable_use_gpu_bo(drawable, &region.extents, &damage)) {
+	    sna_drawable_use_gpu_bo(drawable,
+				    &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)) {
+			struct sna_fill_op fill;
+
+			if (!sna_fill_init_blt(&fill,
+					       data.sna, data.pixmap,
+					       priv->gpu_bo, gc->alu, color))
+				goto fallback;
+
+			data.op = &fill;
+			sna_gc(gc)->priv = &data;
+
+			if ((data.flags & 2) == 0) {
+				if (data.dx | data.dy)
+					gc->ops->FillSpans = sna_fill_spans__fill_offset;
+				else
+					gc->ops->FillSpans = sna_fill_spans__fill;
+			} else {
+				region_maybe_clip(&data.region,
+						  gc->pCompositeClip);
+				if (!RegionNotEmpty(&data.region))
+					return;
+
+				if (region_is_singular(&data.region))
+					gc->ops->FillSpans = sna_fill_spans__fill_clip_extents;
+				else
+					gc->ops->FillSpans = sna_fill_spans__fill_clip_boxes;
+			}
+			assert(gc->miTranslate);
+			if (gc->lineWidth == 0)
+				miZeroPolyArc(drawable, gc, n, arc);
+			else
+				miPolyArc(drawable, gc, n, arc);
+			gc->ops->FillSpans = sna_fill_spans;
+
+			fill.done(data.sna, &fill);
+			if (data.damage)
+				sna_damage_add(data.damage, &data.region);
+			RegionUninit(&data.region);
+			return;
+		}
+
 		/* XXX still around 10x slower for x11perf -ellipse */
 		if (gc->lineWidth == 0)
 			miZeroPolyArc(drawable, gc, n, arc);
@@ -7574,29 +7643,27 @@ sna_poly_arc(DrawablePtr drawable, GCPtr gc, int n, xArc *arc)
 
 fallback:
 	DBG(("%s -- fallback\n", __FUNCTION__));
-	if (gc->lineWidth) {
-		DBG(("%s -- miPolyArc\n", __FUNCTION__));
-		miPolyArc(drawable, gc, n, arc);
-		return;
-	}
-
-	region.data = NULL;
-	region_maybe_clip(&region, gc->pCompositeClip);
-	if (!RegionNotEmpty(&region))
+	region_maybe_clip(&data.region, gc->pCompositeClip);
+	if (!RegionNotEmpty(&data.region))
 		return;
 
 	if (!sna_gc_move_to_cpu(gc, drawable))
 		goto out;
-	if (!sna_drawable_move_region_to_cpu(drawable, &region,
+	if (!sna_drawable_move_region_to_cpu(drawable, &data.region,
 					     drawable_gc_flags(drawable,
 							       gc, true)))
 		goto out;
 
-	/* XXX may still fallthrough to miZeroPolyArc */
+	/* Install FillSpans in case we hit a fallback path in fbPolyArc */
+	sna_gc(gc)->priv = &data.region;
+	gc->ops->FillSpans = sna_fill_spans__cpu;
+
 	DBG(("%s -- fbPolyArc\n", __FUNCTION__));
 	fbPolyArc(drawable, gc, n, arc);
+
+	gc->ops->FillSpans = sna_fill_spans;
 out:
-	RegionUninit(&region);
+	RegionUninit(&data.region);
 }
 
 static Bool
@@ -7805,19 +7872,6 @@ get_pixel(PixmapPtr pixmap)
 	}
 }
 
-static 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_poly_fill_polygon(DrawablePtr draw, GCPtr gc,
 		      int shape, int mode,
@@ -9420,16 +9474,14 @@ sna_poly_fill_arc(DrawablePtr draw, GCPtr gc, int n, xArc *arc)
 					gc->ops->FillSpans = sna_fill_spans__fill_clip_boxes;
 			}
 			assert(gc->miTranslate);
-			miPolyFillArc(draw, gc, n, arc);
 
+			miPolyFillArc(draw, gc, n, arc);
 			fill.done(data.sna, &fill);
 		} else {
 			data.bo = priv->gpu_bo;
 			gc->ops->FillSpans = sna_fill_spans__gpu;
 
 			miPolyFillArc(draw, gc, n, arc);
-
-			gc->ops->FillSpans = sna_fill_spans;
 		}
 
 		gc->ops->FillSpans = sna_fill_spans;
commit 6390a44a69d7f13a3ba16e8b48c640dc39dd227f
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Jan 23 15:57:53 2012 +0000

    sna: Pass the reduce clip region to sna_fill_spans__cpu
    
    Since we compute it for the pixmap migration, we may as well use to
    perform the clipping within FillSpans as well.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c
index 46fa245..f6495d8 100644
--- a/src/sna/sna_accel.c
+++ b/src/sna/sna_accel.c
@@ -3602,11 +3602,12 @@ sna_fill_spans__cpu(DrawablePtr drawable,
 		    GCPtr gc, int n,
 		    DDXPointPtr pt, int *width, int sorted)
 {
+	RegionRec *clip = sna_gc(gc)->priv;
 	BoxRec extents;
 
 	DBG(("%s x %d\n", __FUNCTION__, n));
 
-	extents = gc->pCompositeClip->extents;
+	extents = clip->extents;
 	while (n--) {
 		BoxRec b;
 
@@ -3620,16 +3621,14 @@ sna_fill_spans__cpu(DrawablePtr drawable,
 		if (!box_intersect(&b, &extents))
 			continue;
 
-		if (region_is_singular(gc->pCompositeClip)) {
+		if (region_is_singular(clip)) {
 			fbFill(drawable, gc, b.x1, b.y1, b.x2 - b.x1, 1);
 		} else {
-			const BoxRec * const clip_start = RegionBoxptr(gc->pCompositeClip);
-			const BoxRec * const clip_end = clip_start + gc->pCompositeClip->data->numRects;
+			const BoxRec * const clip_start = RegionBoxptr(clip);
+			const BoxRec * const clip_end = clip_start + clip->data->numRects;
 			const BoxRec *c;
 
-			c = find_clip_box_for_y(clip_start,
-						clip_end,
-						b.y1);
+			c = find_clip_box_for_y(clip_start, clip_end, b.y1);
 			while (c != clip_end) {
 				int16_t x1, x2;
 
@@ -7941,8 +7940,11 @@ fallback:
 							       true)))
 		goto out;
 
-	DBG(("%s: fallback -- miFillPolygon -> sna_fill_spans__cpu\n", __FUNCTION__));
+	DBG(("%s: fallback -- miFillPolygon -> sna_fill_spans__cpu\n",
+	     __FUNCTION__));
+	sna_gc(gc)->priv = &data.region;
 	gc->ops->FillSpans = sna_fill_spans__cpu;
+
 	miFillPolygon(draw, gc, shape, mode, n, pt);
 	gc->ops->FillSpans = sna_fill_spans;
 out:
commit 40e3745debe9738362661c7d7f498f3489c28876
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Jan 23 15:42:18 2012 +0000

    sna: Wrap gc->ops->PolyFillArc
    
    The goal is to avoid the overhead of performing multiple region analysis
    when calling sna_fill_spans by doing it once at the top level and then
    choose the most appropriate drawing method.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c
index c04783e..46fa245 100644
--- a/src/sna/sna_accel.c
+++ b/src/sna/sna_accel.c
@@ -77,7 +77,7 @@
 #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_FILL_ARC 1
 #define ACCEL_POLY_TEXT8 1
 #define ACCEL_POLY_TEXT16 1
 #define ACCEL_IMAGE_TEXT8 1
@@ -7464,17 +7464,17 @@ out:
 	RegionUninit(&region);
 }
 
-static Bool
+static unsigned
 sna_poly_arc_extents(DrawablePtr drawable, GCPtr gc,
 		     int n, xArc *arc,
 		     BoxPtr out)
 {
-	int extra = gc->lineWidth >> 1;
 	BoxRec box;
+	bool clipped;
 	int v;
 
 	if (n == 0)
-		return true;
+		return 0;
 
 	box.x1 = arc->x;
 	box.x2 = bound(box.x1, arc->width);
@@ -7495,19 +7495,23 @@ sna_poly_arc_extents(DrawablePtr drawable, GCPtr gc,
 			box.y2 = v;
 	}
 
-	if (extra) {
-		box.x1 -= extra;
-		box.x2 += extra;
-		box.y1 -= extra;
-		box.y2 += extra;
+	v = gc->lineWidth >> 1;
+	if (v) {
+		box.x1 -= v;
+		box.x2 += v;
+		box.y1 -= v;
+		box.y2 += v;
 	}
 
 	box.x2++;
 	box.y2++;
 
-	trim_and_translate_box(&box, drawable, gc);
+	clipped = trim_and_translate_box(&box, drawable, gc);
+	if (box_empty(&box))
+		return 0;
+
 	*out = box;
-	return box_empty(&box);
+	return 1 | clipped << 1;
 }
 
 static bool
@@ -7535,7 +7539,7 @@ sna_poly_arc(DrawablePtr drawable, GCPtr gc, int n, xArc *arc)
 
 	DBG(("%s(n=%d, lineWidth=%d\n", __FUNCTION__, n, gc->lineWidth));
 
-	if (sna_poly_arc_extents(drawable, gc, n, arc, &region.extents))
+	if (sna_poly_arc_extents(drawable, gc, n, arc, &region.extents) == 0)
 		return;
 
 	DBG(("%s: extents=(%d, %d), (%d, %d)\n", __FUNCTION__,
@@ -9331,6 +9335,137 @@ out:
 	RegionUninit(&region);
 }
 
+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 (use_wide_spans(draw, gc, &data.region.extents) &&
+	    sna_drawable_use_gpu_bo(draw,
+				    &data.region.extents,
+				    &data.damage)) {
+		uint32_t color;
+
+		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,
+					       priv->gpu_bo, gc->alu, color))
+				goto fallback;
+
+			data.op = &fill;
+			sna_gc(gc)->priv = &data;
+
+			if ((data.flags & 2) == 0) {
+				if (data.dx | data.dy)
+					gc->ops->FillSpans = sna_fill_spans__fill_offset;
+				else
+					gc->ops->FillSpans = sna_fill_spans__fill;
+			} else {
+				region_maybe_clip(&data.region,
+						  gc->pCompositeClip);
+				if (!RegionNotEmpty(&data.region))
+					return;
+
+				if (region_is_singular(&data.region))
+					gc->ops->FillSpans = sna_fill_spans__fill_clip_extents;
+				else
+					gc->ops->FillSpans = sna_fill_spans__fill_clip_boxes;
+			}
+			assert(gc->miTranslate);
+			miPolyFillArc(draw, gc, n, arc);
+
+			fill.done(data.sna, &fill);
+		} else {
+			data.bo = priv->gpu_bo;
+			gc->ops->FillSpans = sna_fill_spans__gpu;
+
+			miPolyFillArc(draw, gc, n, arc);
+
+			gc->ops->FillSpans = sna_fill_spans;
+		}
+
+		gc->ops->FillSpans = sna_fill_spans;
+		if (data.damage)
+			sna_damage_add(data.damage, &data.region);
+		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))
+		goto out;
+
+	if (!sna_drawable_move_region_to_cpu(draw, &data.region,
+					     drawable_gc_flags(draw, gc,
+							       true)))
+		goto out;
+
+	DBG(("%s: fallback -- miFillPolygon -> sna_fill_spans__cpu\n",
+	     __FUNCTION__));
+	sna_gc(gc)->priv = &data.region;
+	gc->ops->FillSpans = sna_fill_spans__cpu;
+
+	miPolyFillArc(draw, gc, n, arc);
+	gc->ops->FillSpans = sna_fill_spans;
+out:
+	RegionUninit(&data.region);
+}
+
 struct sna_font {
 	CharInfoRec glyphs8[256];
 	CharInfoRec *glyphs16[256];
@@ -10468,7 +10603,7 @@ static GCOps sna_gc_ops = {
 	sna_poly_arc,
 	sna_poly_fill_polygon,
 	sna_poly_fill_rect,
-	miPolyFillArc,
+	sna_poly_fill_arc,
 	sna_poly_text8,
 	sna_poly_text16,
 	sna_image_text8,
commit 55c2211143b3be2827fd4e299c3c211f3bd7935b
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Jan 23 18:15:36 2012 +0000

    sna: Use a streamlined sna_fill_spans for when we know we want to use the gpu
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c
index 57a8439..c04783e 100644
--- a/src/sna/sna_accel.c
+++ b/src/sna/sna_accel.c
@@ -3664,6 +3664,7 @@ struct sna_fill_spans {
 	PixmapPtr pixmap;
 	RegionRec region;
 	unsigned flags;
+	struct kgem_bo *bo;
 	struct sna_damage **damage;
 	int16_t dx, dy;
 	void *op;
@@ -4145,6 +4146,71 @@ done:
 	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 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;
+
+	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 (gc->fillStyle == FillSolid) {
+		sna_fill_spans_blt(drawable,
+				   data->bo, data->damage,
+				   gc, n, pt, width, sorted,
+				   &data->region.extents, data->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) {
+			sna_poly_fill_rect_tiled_blt(drawable,
+						     data->bo, data->damage,
+						     gc, n, rect,
+						     &data->region.extents,
+						     data->flags & 2);
+		} else {
+			sna_poly_fill_rect_stippled_blt(drawable,
+							data->bo, data->damage,
+							gc, n, rect,
+							&data->region.extents,
+							data->flags & 2);
+		}
+		free (rect);
+	}
+}
+
 static unsigned
 sna_spans_extents(DrawablePtr drawable, GCPtr gc,
 		  int n, DDXPointPtr pt, int *width,
@@ -4184,20 +4250,6 @@ sna_spans_extents(DrawablePtr drawable, GCPtr gc,
 	return 1 | clipped << 1;
 }
 
-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 void
 sna_fill_spans(DrawablePtr drawable, GCPtr gc, int n,
 	       DDXPointPtr pt, int *width, int sorted)
@@ -7820,6 +7872,7 @@ sna_poly_fill_polygon(DrawablePtr draw, GCPtr gc,
 				    &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)) {
@@ -7831,7 +7884,6 @@ sna_poly_fill_polygon(DrawablePtr draw, GCPtr gc,
 				goto fallback;
 
 			data.op = &fill;
-			sna_gc(gc)->priv = &data;
 
 			if ((data.flags & 2) == 0) {
 				if (data.dx | data.dy)
@@ -7850,18 +7902,20 @@ sna_poly_fill_polygon(DrawablePtr draw, GCPtr gc,
 					gc->ops->FillSpans = sna_fill_spans__fill_clip_boxes;
 			}
 			assert(gc->miTranslate);
-			miFillPolygon(draw, gc, shape, mode, n, pt);
-			gc->ops->FillSpans = sna_fill_spans;
 
+			miFillPolygon(draw, gc, shape, mode, n, pt);
 			fill.done(data.sna, &fill);
-			if (data.damage)
-				sna_damage_add(data.damage, &data.region);
-			RegionUninit(&data.region);
-			return;
+		} else {
+			data.bo = priv->gpu_bo;
+			gc->ops->FillSpans = sna_fill_spans__gpu;
+
+			miFillPolygon(draw, gc, shape, mode, n, pt);
 		}
 
-		/* XXX */
-		miFillPolygon(draw, gc, shape, mode, n, pt);
+		gc->ops->FillSpans = sna_fill_spans;
+		if (data.damage)
+			sna_damage_add(data.damage, &data.region);
+		RegionUninit(&data.region);
 		return;
 	}
 
commit b20ae331566e92f1a995f49fd0760f020db25035
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Jan 23 19:49:18 2012 +0000

    sna: Switch to using spans based on use_wide_spans()
    
    This just gives us a hook with which to conveniently enable or disable
    the new code.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c
index c258812..57a8439 100644
--- a/src/sna/sna_accel.c
+++ b/src/sna/sna_accel.c
@@ -7814,7 +7814,8 @@ sna_poly_fill_polygon(DrawablePtr draw, GCPtr gc,
 	if (!PM_IS_SOLID(draw, gc->planemask))
 		goto fallback;
 
-	if (sna_drawable_use_gpu_bo(draw,
+	if (use_wide_spans(draw, gc, &data.region.extents) &&
+	    sna_drawable_use_gpu_bo(draw,
 				    &data.region.extents,
 				    &data.damage)) {
 		uint32_t color;


More information about the xorg-commit mailing list