xf86-video-intel: 4 commits - src/sna/fb src/sna/kgem.c src/sna/kgem.h

Chris Wilson ickle at kemper.freedesktop.org
Wed Jul 18 01:43:32 PDT 2012


 src/sna/fb/fbbitmap.c |   11 +
 src/sna/fb/fbblt.c    |    5 
 src/sna/fb/fbclip.c   |   13 +
 src/sna/fb/fbclip.h   |   25 +--
 src/sna/fb/fbfill.c   |   30 +--
 src/sna/fb/fbtile.c   |   27 +--
 src/sna/kgem.c        |  387 +++++++++++++++++++++++++++++++++++++-------------
 src/sna/kgem.h        |    2 
 8 files changed, 342 insertions(+), 158 deletions(-)

New commits:
commit bee1a14618797b3d3a1c1a20eb72644fa907c048
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Jul 18 09:38:32 2012 +0100

    sna: Fix processing of the last fallback box
    
    The evil typo caused us to misalign the clip boxes and run over a
    garbage array on 64-bit builds.
    
    Reported-by: Edward Sheldrake <ejsheldrake at gmail.com>
    Reported-by: Clemens Eisserer <linuxhippy at gmail.com>
    Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=52163
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/fb/fbclip.c b/src/sna/fb/fbclip.c
index 5a71b0c..37011a7 100644
--- a/src/sna/fb/fbclip.c
+++ b/src/sna/fb/fbclip.c
@@ -73,7 +73,7 @@ fbClipBoxes(const RegionRec *region, const BoxRec *box, const BoxRec **end)
 		return &region->extents;
 	}
 
-	c0 = (const BoxRec *)region->data + 1;
+	c0 = (const BoxRec *)(region->data + 1);
 	c1 = c0 + region->data->numRects;
 
 	if (c0->y2 <= box->y1) {
commit 88cb1968b6dbf3edfa885da9503e91124af46007
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Jul 18 09:38:03 2012 +0100

    sna: Add more DBG for fallback processing
    
    Hunting the lost box...
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/fb/fbbitmap.c b/src/sna/fb/fbbitmap.c
index fa5d032..0758728 100644
--- a/src/sna/fb/fbbitmap.c
+++ b/src/sna/fb/fbbitmap.c
@@ -37,6 +37,11 @@ static inline void add(RegionPtr region,
 	r->x1 = x1; r->y1 = y1;
 	r->x2 = x2; r->y2 = y2;
 
+	DBG(("%s[%d/%d]: (%d, %d), (%d, %d)\n",
+	     __FUNCTION__,
+	     region->data->numRects, region->data->size,
+	     x1, y1, x2, y2));
+
 	if (x1 < region->extents.x1)
 		region->extents.x1 = x1;
 	if (x2 > region->extents.x2)
@@ -138,5 +143,11 @@ fbBitmapToRegion(PixmapPtr pixmap)
 	} else
 		region->extents.x1 = region->extents.x2 = 0;
 
+	DBG(("%s: region extents=(%d, %d), (%d, %d) x %d\n",
+	     __FUNCTION__,
+	     region->extents.x1, region->extents.y1,
+	     region->extents.x2, region->extents.y2,
+	     RegionNumRects(region)));
+
 	return region;
 }
diff --git a/src/sna/fb/fbblt.c b/src/sna/fb/fbblt.c
index fd55c85..3b3fa48 100644
--- a/src/sna/fb/fbblt.c
+++ b/src/sna/fb/fbblt.c
@@ -285,7 +285,10 @@ fbBlt(FbBits *srcLine, FbStride srcStride, int srcX,
 		s += srcX >> 3;
 		d += dstX >> 3;
 
-		DBG(("%s fast blt\n", __FUNCTION__));
+		DBG(("%s fast blt, src_stride=%d, dst_stride=%d, width=%d (offset=%d)\n",
+		     __FUNCTION__,
+		     srcStride, dstStride, width,
+		     srcLine - dstLine));
 
 		if ((srcLine < dstLine && srcLine + width > dstLine) ||
 		    (dstLine < srcLine && dstLine + width > srcLine))
diff --git a/src/sna/fb/fbclip.c b/src/sna/fb/fbclip.c
index 8d9c4db..5a71b0c 100644
--- a/src/sna/fb/fbclip.c
+++ b/src/sna/fb/fbclip.c
@@ -76,11 +76,16 @@ fbClipBoxes(const RegionRec *region, const BoxRec *box, const BoxRec **end)
 	c0 = (const BoxRec *)region->data + 1;
 	c1 = c0 + region->data->numRects;
 
-	if (c0->y2 <= box->y1)
+	if (c0->y2 <= box->y1) {
+		DBG(("%s: first clip (%d, %d), (%d, %d) before box (%d, %d), (%d, %d)\n",
+		     __FUNCTION__,
+		     c0->x1, c0->y1, c0->x2, c0->y2,
+		     box->x1, box->y1, box->x2, box->y2));
 		c0 = find_clip_row_for_y(c0, c1, box->y1);
+	}
 
-	DBG(("%s: c0=(%d, %d),(%d, %d)\n",
-	     __FUNCTION__, c0->x1, c0->y1, c0->x2, c0->y2));
+	DBG(("%s: c0=(%d, %d),(%d, %d) x %ld\n",
+	     __FUNCTION__, c0->x1, c0->y1, c0->x2, c0->y2, c1 - c0));
 
 	*end = c1;
 	return c0;
diff --git a/src/sna/fb/fbclip.h b/src/sna/fb/fbclip.h
index feb2d2c..f07e63c 100644
--- a/src/sna/fb/fbclip.h
+++ b/src/sna/fb/fbclip.h
@@ -46,6 +46,13 @@ box_intersect(BoxPtr a, const BoxRec *b)
 	return a->x1 < a->x2 && a->y1 < a->y2;
 }
 
+#define run_box(b, c) \
+	DBG(("%s: box=(%d, %d), (%d, %d), clip=(%d, %d), (%d, %d)\n", \
+	     __FUNCTION__, (b)->x1, (b)->y1, (b)->x2, (b)->y2, (c)->x1, (c)->y1, (c)->x2, (c)->y2)); \
+	if ((b)->y2 <= (c)->y1) break; \
+	if ((b)->x1 >= (c)->x2) continue; \
+	if ((b)->x2 <= (c)->x1) { if ((b)->y2 <= (c)->y2) break; continue; }
+
 static inline void
 fbDrawableRun(DrawablePtr d, GCPtr gc, const BoxRec *box,
 	      void (*func)(DrawablePtr, GCPtr, const BoxRec *b, void *data),
@@ -55,14 +62,7 @@ fbDrawableRun(DrawablePtr d, GCPtr gc, const BoxRec *box,
 	for (c = fbClipBoxes(gc->pCompositeClip, box, &end); c != end; c++) {
 		BoxRec b;
 
-		if (box->x1 >= c->x2)
-			continue;
-		if (box->x2 <= c->x1) {
-			if (box->y2 <= c->y2)
-				break;
-			else
-				continue;
-		}
+		run_box(box, c);
 
 		b = *box;
 		if (box_intersect(&b, c))
@@ -77,14 +77,7 @@ fbDrawableRunUnclipped(DrawablePtr d, GCPtr gc, const BoxRec *box,
 {
 	const BoxRec *c, *end;
 	for (c = fbClipBoxes(gc->pCompositeClip, box, &end); c != end; c++) {
-		if (box->x1 >= c->x2)
-			continue;
-		if (box->x2 <= c->x1) {
-			if (box->y2 <= c->y2)
-				break;
-			else
-				continue;
-		}
+		run_box(box, c);
 		func(d, gc, c, data);
 	}
 }
diff --git a/src/sna/fb/fbfill.c b/src/sna/fb/fbfill.c
index 3df1f9c..a9ae2bc 100644
--- a/src/sna/fb/fbfill.c
+++ b/src/sna/fb/fbfill.c
@@ -141,28 +141,14 @@ fbFill(DrawablePtr drawable, GCPtr gc, int x, int y, int width, int height)
 
 	case FillTiled:
 		{
-			PixmapPtr pTile = gc->tile.pixmap;
-			FbBits *tile;
-			FbStride tileStride;
-			int tileBpp;
-			int tileWidth;
-			int tileHeight;
-			_X_UNUSED int tileXoff, tileYoff;
-
-			fbGetDrawable(&pTile->drawable, tile,
-				      tileStride, tileBpp, tileXoff, tileYoff);
-			tileWidth = pTile->drawable.width;
-			tileHeight = pTile->drawable.height;
-			fbTile(dst + (y + dstYoff) * dstStride,
-			       dstStride,
-			       (x + dstXoff) * dstBpp,
-			       width * dstBpp, height,
-			       tile,
-			       tileStride,
-			       tileWidth * tileBpp,
-			       tileHeight,
-			       gc->alu, pgc->pm,
-			       dstBpp,
+			PixmapPtr tile = gc->tile.pixmap;
+
+			fbTile(dst + (y + dstYoff) * dstStride, dstStride,
+			       (x + dstXoff) * dstBpp, width * dstBpp, height,
+			       tile->devPrivate.ptr, tile->devKind / sizeof(FbBits),
+			       tile->drawable.width * tile->drawable.bitsPerPixel,
+			       tile->drawable.height,
+			       gc->alu, pgc->pm, dstBpp,
 			       (gc->patOrg.x + drawable->x + dstXoff) * dstBpp,
 			       gc->patOrg.y + drawable->y - y);
 			break;
diff --git a/src/sna/fb/fbtile.c b/src/sna/fb/fbtile.c
index 5586553..c350671 100644
--- a/src/sna/fb/fbtile.c
+++ b/src/sna/fb/fbtile.c
@@ -99,30 +99,29 @@ fbOddTile(FbBits *dst, FbStride dstStride, int dstX,
 	  int xRot, int yRot)
 {
 	int tileX, tileY;
-	int widthTmp;
-	int h, w;
 	int x, y;
 
+	DBG(("%s tile=%dx%d, size=%dx%d\n", __FUNCTION__,
+	     tileWidth, tileHeight, width, height));
+
 	modulus(-yRot, tileHeight, tileY);
 	y = 0;
 	while (height) {
-		h = tileHeight - tileY;
+		int ww = width;
+		int h = tileHeight - tileY;
 		if (h > height)
 			h = height;
 		height -= h;
-		widthTmp = width;
 		x = dstX;
 		modulus(dstX - xRot, tileWidth, tileX);
-		while (widthTmp) {
-			w = tileWidth - tileX;
-			if (w > widthTmp)
-				w = widthTmp;
-			widthTmp -= w;
-			fbBlt(tile + tileY * tileStride,
-			      tileStride,
-			      tileX,
-			      dst + y * dstStride,
-			      dstStride, x, w, h, alu, pm, bpp, FALSE, FALSE);
+		while (ww) {
+			int w = tileWidth - tileX;
+			if (w > ww)
+				w = ww;
+			ww -= w;
+			fbBlt(tile + tileY * tileStride, tileStride, tileX,
+			      dst + y * dstStride, dstStride,
+			      x, w, h, alu, pm, bpp, FALSE, FALSE);
 			x += w;
 			tileX = 0;
 		}
commit 36f2e46619598e9bca4fe1207aa2f157bfa1ecf4
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Jul 18 00:45:54 2012 +0100

    sna: Reuse the snoopable cache more frequently for upload buffers
    
    Now that we are keeping a small cache of snoopable buffers, experiment
    with using them for uploads more frequently.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/kgem.c b/src/sna/kgem.c
index d9e27e2..6327e49 100644
--- a/src/sna/kgem.c
+++ b/src/sna/kgem.c
@@ -3950,15 +3950,122 @@ static struct kgem_partial_bo *partial_bo_alloc(int num_pages)
 static inline bool
 use_snoopable_buffer(struct kgem *kgem, uint32_t flags)
 {
-	assert(kgem->gen != 40);
-
-	if ((flags & KGEM_BUFFER_WRITE_INPLACE) == KGEM_BUFFER_WRITE_INPLACE)
-		return true;
-
 	if ((flags & KGEM_BUFFER_WRITE) == 0)
 		return kgem->gen >= 30;
 
-	return false;
+	return true;
+}
+
+static struct kgem_partial_bo *
+search_snoopable_buffer(struct kgem *kgem, unsigned alloc)
+{
+	struct kgem_partial_bo *bo;
+	struct kgem_bo *old;
+
+	old = search_vmap_cache(kgem, alloc, 0);
+	if (old) {
+		bo = malloc(sizeof(*bo));
+		if (bo == NULL)
+			return NULL;
+
+		memcpy(&bo->base, old, sizeof(*old));
+		if (old->rq)
+			list_replace(&old->request, &bo->base.request);
+		else
+			list_init(&bo->base.request);
+		list_replace(&old->vma, &bo->base.vma);
+		list_init(&bo->base.list);
+		free(old);
+
+		DBG(("%s: created CPU handle=%d for buffer, size %d\n",
+		     __FUNCTION__, bo->base.handle, num_pages(&bo->base)));
+
+		assert(bo->base.vmap);
+		assert(bo->base.tiling == I915_TILING_NONE);
+		assert(num_pages(&bo->base) >= alloc);
+
+		bo->mem = kgem_bo_map__cpu(kgem, &bo->base);
+		if (bo->mem) {
+			bo->mmapped = true;
+			bo->need_io = false;
+			bo->base.io = true;
+			bo->base.refcnt = 1;
+
+			return bo;
+		} else
+			kgem_bo_free(kgem, &bo->base);
+	}
+
+	return NULL;
+}
+
+static struct kgem_partial_bo *
+create_snoopable_buffer(struct kgem *kgem, unsigned alloc)
+{
+	struct kgem_partial_bo *bo;
+
+	if (kgem->has_cache_level) {
+		uint32_t handle;
+
+		handle = gem_create(kgem->fd, alloc);
+		if (handle == 0)
+			return NULL;
+
+		if (!gem_set_cache_level(kgem->fd, handle, I915_CACHE_LLC)) {
+			gem_close(kgem->fd, handle);
+			return NULL;
+		}
+
+		bo = malloc(sizeof(*bo));
+		if (bo == NULL) {
+			gem_close(kgem->fd, handle);
+			return NULL;
+		}
+
+		debug_alloc(kgem, alloc);
+		__kgem_bo_init(&bo->base, handle, alloc);
+		DBG(("%s: created CPU handle=%d for buffer, size %d\n",
+		     __FUNCTION__, bo->base.handle, alloc));
+
+		bo->base.reusable = false;
+		bo->base.vmap = true;
+
+		bo->mem = kgem_bo_map__cpu(kgem, &bo->base);
+		if (bo->mem) {
+			bo->mmapped = true;
+			bo->need_io = false;
+			bo->base.io = true;
+			return bo;
+		} else {
+			bo->base.refcnt = 0; /* for valgrind */
+			kgem_bo_free(kgem, &bo->base);
+		}
+	}
+
+	if (kgem->has_vmap) {
+		bo = partial_bo_alloc(alloc);
+		if (bo) {
+			uint32_t handle = gem_vmap(kgem->fd, bo->mem,
+						   alloc * PAGE_SIZE, false);
+			if (handle == 0 ||
+			    !__kgem_bo_init(&bo->base, handle, alloc)) {
+				free(bo);
+			} else {
+				DBG(("%s: created vmap handle=%d for buffer\n",
+				     __FUNCTION__, bo->base.handle));
+
+				bo->base.io = true;
+				bo->base.vmap = true;
+				bo->base.map = MAKE_VMAP_MAP(bo);
+				bo->mmapped = true;
+				bo->need_io = false;
+
+				return bo;
+			}
+		}
+	}
+
+	return NULL;
 }
 
 struct kgem_bo *kgem_create_buffer(struct kgem *kgem,
@@ -4199,105 +4306,23 @@ struct kgem_bo *kgem_create_buffer(struct kgem *kgem,
 		}
 	}
 #endif
-	/* Be more parsimonious with pwrite/pread buffers */
+	/* Be more parsimonious with pwrite/pread/cacheable buffers */
 	if ((flags & KGEM_BUFFER_INPLACE) == 0)
 		alloc = NUM_PAGES(size);
 
 	if (use_snoopable_buffer(kgem, flags)) {
-		old = search_vmap_cache(kgem, NUM_PAGES(size), 0);
-		if (old) {
-			bo = malloc(sizeof(*bo));
-			if (bo == NULL)
-				return NULL;
-
-			memcpy(&bo->base, old, sizeof(*old));
-			if (old->rq)
-				list_replace(&old->request, &bo->base.request);
-			else
-				list_init(&bo->base.request);
-			list_replace(&old->vma, &bo->base.vma);
-			list_init(&bo->base.list);
-			free(old);
-
-			assert(bo->base.vmap);
-			assert(bo->base.tiling == I915_TILING_NONE);
-			assert(num_pages(&bo->base) >= NUM_PAGES(size));
-
-			bo->mem = kgem_bo_map__cpu(kgem, &bo->base);
-			if (bo->mem) {
-				bo->mmapped = true;
-				bo->need_io = false;
-				bo->base.io = true;
-				bo->base.refcnt = 1;
-
-				alloc = num_pages(&bo->base);
-				goto init;
-			} else {
-				kgem_bo_free(kgem, &bo->base);
-				bo = NULL;
-			}
-		}
-
-		if (kgem->has_cache_level) {
-			uint32_t handle;
-
-			handle = gem_create(kgem->fd, alloc);
-			if (handle == 0)
-				return NULL;
-
-			if (!gem_set_cache_level(kgem->fd, handle, I915_CACHE_LLC)) {
-				gem_close(kgem->fd, handle);
-				return NULL;
-			}
-
-			bo = malloc(sizeof(*bo));
-			if (bo == NULL) {
-				gem_close(kgem->fd, handle);
-				return NULL;
-			}
-
-			debug_alloc(kgem, alloc);
-			__kgem_bo_init(&bo->base, handle, alloc);
-			DBG(("%s: created CPU handle=%d for buffer, size %d\n",
-			     __FUNCTION__, bo->base.handle, alloc));
-
-			bo->base.reusable = false;
-			bo->base.vmap = true;
-
-			bo->mem = kgem_bo_map__cpu(kgem, &bo->base);
-			if (bo->mem) {
-				bo->mmapped = true;
-				bo->need_io = false;
-				bo->base.io = true;
-				goto init;
-			} else {
-				bo->base.refcnt = 0; /* for valgrind */
-				kgem_bo_free(kgem, &bo->base);
-				bo = NULL;
-			}
+		bo = search_snoopable_buffer(kgem, alloc);
+		if (bo) {
+			flags &= ~KGEM_BUFFER_INPLACE;
+			alloc = num_pages(&bo->base);
+			goto init;
 		}
 
-		if (kgem->has_vmap) {
-			bo = partial_bo_alloc(alloc);
+		if ((flags & KGEM_BUFFER_WRITE_INPLACE) != KGEM_BUFFER_WRITE_INPLACE) {
+			bo = create_snoopable_buffer(kgem, alloc);
 			if (bo) {
-				uint32_t handle = gem_vmap(kgem->fd, bo->mem,
-							   alloc * PAGE_SIZE, false);
-				if (handle == 0 ||
-				    !__kgem_bo_init(&bo->base, handle, alloc)) {
-					free(bo);
-					bo = NULL;
-				} else {
-					DBG(("%s: created vmap handle=%d for buffer\n",
-					     __FUNCTION__, bo->base.handle));
-
-					bo->base.io = true;
-					bo->base.vmap = true;
-					bo->base.map = MAKE_VMAP_MAP(bo);
-					bo->mmapped = true;
-					bo->need_io = false;
-
-					goto init;
-				}
+				flags &= ~KGEM_BUFFER_INPLACE;
+				goto init;
 			}
 		}
 	}
@@ -4331,6 +4356,12 @@ struct kgem_bo *kgem_create_buffer(struct kgem *kgem,
 		bo->need_io = flags & KGEM_BUFFER_WRITE;
 		bo->base.io = true;
 	} else {
+		if (use_snoopable_buffer(kgem, flags)) {
+			bo = create_snoopable_buffer(kgem, alloc);
+			if (bo)
+				goto init;
+		}
+
 		bo = malloc(sizeof(*bo));
 		if (bo == NULL)
 			return NULL;
commit 73f07abbd2d78418e5a66262f293b5ed80b7ccb4
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Jul 18 00:19:49 2012 +0100

    sna: Maintain a short-lived cache of snoopable CPU bo for older gen
    
    Once again, we find that frequent buffer creation and manipulation of the
    GTT is a painful experience leading to noticeable and frequent application
    stalls. So mitigate the need for fresh pages by keeping a small stash of
    recently freed and inactive 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 4c6ca57..d9e27e2 100644
--- a/src/sna/kgem.c
+++ b/src/sna/kgem.c
@@ -53,6 +53,9 @@
 static struct kgem_bo *
 search_linear_cache(struct kgem *kgem, unsigned int num_pages, unsigned flags);
 
+static struct kgem_bo *
+search_vmap_cache(struct kgem *kgem, unsigned int num_pages, unsigned flags);
+
 #define DBG_NO_HW 0
 #define DBG_NO_TILING 0
 #define DBG_NO_CACHE 0
@@ -810,6 +813,7 @@ void kgem_init(struct kgem *kgem, int fd, struct pci_device *dev, int gen)
 	list_init(&kgem->flushing);
 	list_init(&kgem->sync_list);
 	list_init(&kgem->large);
+	list_init(&kgem->vmap);
 	for (i = 0; i < ARRAY_SIZE(kgem->inactive); i++)
 		list_init(&kgem->inactive[i]);
 	for (i = 0; i < ARRAY_SIZE(kgem->active); i++) {
@@ -1212,6 +1216,9 @@ static void kgem_bo_free(struct kgem *kgem, struct kgem_bo *bo)
 inline static void kgem_bo_move_to_inactive(struct kgem *kgem,
 					    struct kgem_bo *bo)
 {
+	DBG(("%s: moving %d from flush to inactive\n",
+	     __FUNCTION__, bo->handle));
+
 	assert(bo->reusable);
 	assert(bo->rq == NULL);
 	assert(bo->domain != DOMAIN_GPU);
@@ -1295,6 +1302,73 @@ static void _kgem_bo_delete_partial(struct kgem *kgem, struct kgem_bo *bo)
 		io->used = bo->delta;
 }
 
+static void kgem_bo_move_to_vmap(struct kgem *kgem, struct kgem_bo *bo)
+{
+	if (num_pages(bo) > kgem->max_cpu_size >> 13) {
+		kgem_bo_free(kgem, bo);
+		return;
+	}
+
+	assert(bo->tiling == I915_TILING_NONE);
+	assert(bo->rq == NULL);
+	assert(!bo->io);
+
+	DBG(("%s: moving %d to vmap\n", __FUNCTION__, bo->handle));
+	list_add(&bo->list, &kgem->vmap);
+}
+
+static struct kgem_bo *
+search_vmap_cache(struct kgem *kgem, unsigned int num_pages, unsigned flags)
+{
+	struct kgem_bo *bo, *first = NULL;
+
+	DBG(("%s: num_pages=%d, flags=%x\n", __FUNCTION__, num_pages, flags));
+
+	if (list_is_empty(&kgem->vmap)) {
+		DBG(("%s: inactive and cache empty\n", __FUNCTION__));
+		if (!__kgem_throttle_retire(kgem, flags)) {
+			DBG(("%s: nothing retired\n", __FUNCTION__));
+			return NULL;
+		}
+	}
+
+	list_for_each_entry(bo, &kgem->vmap, list) {
+		assert(bo->refcnt == 0);
+		assert(bo->vmap);
+		assert(bo->tiling == I915_TILING_NONE);
+		assert(bo->rq == NULL);
+
+		if (num_pages > num_pages(bo))
+			continue;
+
+		if (num_pages(bo) > 2*num_pages) {
+			if (first == NULL)
+				first = bo;
+			continue;
+		}
+
+		list_del(&bo->list);
+		bo->pitch = 0;
+		bo->delta = 0;
+
+		DBG(("  %s: found handle=%d (num_pages=%d) in vmap cache\n",
+		     __FUNCTION__, bo->handle, num_pages(bo)));
+		return bo;
+	}
+
+	if (first) {
+		list_del(&first->list);
+		first->pitch = 0;
+		first->delta = 0;
+
+		DBG(("  %s: found handle=%d (num_pages=%d) in vmap cache\n",
+		     __FUNCTION__, first->handle, num_pages(first)));
+		return first;
+	}
+
+	return NULL;
+}
+
 static void __kgem_bo_destroy(struct kgem *kgem, struct kgem_bo *bo)
 {
 	DBG(("%s: handle=%d\n", __FUNCTION__, bo->handle));
@@ -1309,22 +1383,6 @@ static void __kgem_bo_destroy(struct kgem *kgem, struct kgem_bo *bo)
 	if (DBG_NO_CACHE)
 		goto destroy;
 
-	if (bo->vmap) {
-		assert(!bo->flush);
-		DBG(("%s: handle=%d is vmapped, tracking until free\n",
-		     __FUNCTION__, bo->handle));
-		if (bo->rq == NULL) {
-			if (bo->needs_flush && kgem_busy(kgem, bo->handle)) {
-				list_add(&bo->request, &kgem->flushing);
-				bo->rq = &_kgem_static_request;
-			} else
-				kgem_bo_free(kgem, bo);
-		} else {
-			assert(!bo->sync);
-		}
-		return;
-	}
-
 	if (bo->io) {
 		struct kgem_bo *base;
 
@@ -1344,6 +1402,21 @@ static void __kgem_bo_destroy(struct kgem *kgem, struct kgem_bo *bo)
 		}
 	}
 
+	if (bo->vmap) {
+		assert(!bo->flush);
+		DBG(("%s: handle=%d is vmapped, tracking until free\n",
+		     __FUNCTION__, bo->handle));
+		if (bo->rq == NULL) {
+			if (bo->needs_flush && kgem_busy(kgem, bo->handle)) {
+				list_add(&bo->request, &kgem->flushing);
+				bo->rq = &_kgem_static_request;
+			}
+		}
+		if (bo->rq == NULL)
+			kgem_bo_move_to_vmap(kgem, bo);
+		return;
+	}
+
 	if (!bo->reusable) {
 		DBG(("%s: handle=%d, not reusable\n",
 		     __FUNCTION__, bo->handle));
@@ -1406,7 +1479,6 @@ static void __kgem_bo_destroy(struct kgem *kgem, struct kgem_bo *bo)
 		     __FUNCTION__, bo->handle));
 	}
 
-	DBG(("%s: handle=%d -> inactive\n", __FUNCTION__, bo->handle));
 	kgem_bo_move_to_inactive(kgem, bo);
 	return;
 
@@ -1475,16 +1547,16 @@ static bool kgem_retire__flushing(struct kgem *kgem)
 		if (kgem_busy(kgem, bo->handle))
 			break;
 
-		DBG(("%s: moving %d from flush to inactive\n",
-		     __FUNCTION__, bo->handle));
 		bo->needs_flush = false;
 		bo->domain = DOMAIN_NONE;
 		bo->rq = NULL;
 		list_del(&bo->request);
 
 		if (!bo->refcnt) {
-			assert(bo->reusable);
-			if (kgem_bo_set_purgeable(kgem, bo)) {
+			if (bo->vmap) {
+				kgem_bo_move_to_vmap(kgem, bo);
+			} else if (kgem_bo_set_purgeable(kgem, bo)) {
+				assert(bo->reusable);
 				kgem_bo_move_to_inactive(kgem, bo);
 				retired = true;
 			} else
@@ -1546,6 +1618,16 @@ static bool kgem_retire__requests(struct kgem *kgem)
 			if (bo->refcnt)
 				continue;
 
+			if (bo->vmap) {
+				if (bo->needs_flush) {
+					list_add(&bo->request, &kgem->flushing);
+					bo->rq = &_kgem_static_request;
+				} else {
+					kgem_bo_move_to_vmap(kgem, bo);
+				}
+				continue;
+			}
+
 			if (!bo->reusable) {
 				DBG(("%s: closing %d\n",
 				     __FUNCTION__, bo->handle));
@@ -1555,8 +1637,6 @@ static bool kgem_retire__requests(struct kgem *kgem)
 
 			if (!bo->needs_flush) {
 				if (kgem_bo_set_purgeable(kgem, bo)) {
-					DBG(("%s: moving %d to inactive\n",
-					     __FUNCTION__, bo->handle));
 					kgem_bo_move_to_inactive(kgem, bo);
 					retired = true;
 				} else {
@@ -2177,12 +2257,35 @@ bool kgem_expire_cache(struct kgem *kgem)
 	bool idle;
 	unsigned int i;
 
+	time(&now);
+
 	while (__kgem_freed_bo) {
 		bo = __kgem_freed_bo;
 		__kgem_freed_bo = *(struct kgem_bo **)bo;
 		free(bo);
 	}
 
+
+	expire = 0;
+	list_for_each_entry(bo, &kgem->vmap, list) {
+		if (bo->delta) {
+			expire = now - MAX_INACTIVE_TIME/2;
+			break;
+		}
+
+		bo->delta = now;
+	}
+	if (expire) {
+		while (!list_is_empty(&kgem->vmap)) {
+			bo = list_last_entry(&kgem->vmap, struct kgem_bo, list);
+
+			if (bo->delta > expire)
+				break;
+
+			kgem_bo_free(kgem, bo);
+		}
+	}
+
 	kgem_retire(kgem);
 	if (kgem->wedged)
 		kgem_cleanup(kgem);
@@ -2192,7 +2295,6 @@ bool kgem_expire_cache(struct kgem *kgem)
 	if (kgem->need_purge)
 		kgem_purge_cache(kgem);
 
-	time(&now);
 	expire = 0;
 
 	idle = !kgem->need_retire;
@@ -2291,6 +2393,17 @@ void kgem_cleanup_cache(struct kgem *kgem)
 						     struct kgem_bo, list));
 	}
 
+	while (!list_is_empty(&kgem->vmap))
+		kgem_bo_free(kgem,
+			     list_last_entry(&kgem->vmap,
+					     struct kgem_bo, list));
+
+	while (__kgem_freed_bo) {
+		struct kgem_bo *bo = __kgem_freed_bo;
+		__kgem_freed_bo = *(struct kgem_bo **)bo;
+		free(bo);
+	}
+
 	kgem->need_purge = false;
 	kgem->need_expire = false;
 }
@@ -2743,9 +2856,6 @@ struct kgem_bo *kgem_create_2d(struct kgem *kgem,
 	size /= PAGE_SIZE;
 	bucket = cache_bucket(size);
 
-	if (flags & CREATE_FORCE)
-		goto create;
-
 	if (bucket >= NUM_CACHE_BUCKETS) {
 		DBG(("%s: large bo num pages=%d, bucket=%d\n",
 		     __FUNCTION__, size, bucket));
@@ -3101,6 +3211,7 @@ struct kgem_bo *kgem_create_cpu_2d(struct kgem *kgem,
 				   uint32_t flags)
 {
 	struct kgem_bo *bo;
+	int stride, size;
 
 	DBG(("%s(%dx%d, bpp=%d)\n", __FUNCTION__, width, height, bpp));
 
@@ -3120,9 +3231,26 @@ struct kgem_bo *kgem_create_cpu_2d(struct kgem *kgem,
 		return bo;
 	}
 
+	assert(width > 0 && height > 0);
+	stride = ALIGN(width, 2) * bpp >> 3;
+	stride = ALIGN(stride, 4);
+	size = stride * ALIGN(height, 2);
+	assert(size >= PAGE_SIZE);
+
+	DBG(("%s: %dx%d, %d bpp, stride=%d\n",
+	     __FUNCTION__, width, height, bpp, stride));
+
+	bo = search_vmap_cache(kgem, NUM_PAGES(size), 0);
+	if (bo) {
+		assert(bo->tiling == I915_TILING_NONE);
+		assert(bo->vmap);
+		bo->refcnt = 1;
+		bo->pitch = stride;
+		return bo;
+	}
+
 	if (kgem->has_cache_level) {
-		bo = kgem_create_2d(kgem, width, height, bpp,
-				    I915_TILING_NONE, flags | CREATE_FORCE);
+		bo = kgem_create_linear(kgem, size, flags);
 		if (bo == NULL)
 			return NULL;
 
@@ -3136,19 +3264,13 @@ struct kgem_bo *kgem_create_cpu_2d(struct kgem *kgem,
 			return NULL;
 		}
 
+		bo->pitch = stride;
 		return bo;
 	}
 
 	if (kgem->has_vmap) {
-		int stride, size;
 		void *ptr;
 
-		stride = ALIGN(width, 2) * bpp >> 3;
-		stride = ALIGN(stride, 4);
-		size = ALIGN(height, 2) * stride;
-
-		assert(size >= PAGE_SIZE);
-
 		/* XXX */
 		//if (posix_memalign(&ptr, 64, ALIGN(size, 64)))
 		if (posix_memalign(&ptr, PAGE_SIZE, ALIGN(size, PAGE_SIZE)))
@@ -4082,6 +4204,40 @@ struct kgem_bo *kgem_create_buffer(struct kgem *kgem,
 		alloc = NUM_PAGES(size);
 
 	if (use_snoopable_buffer(kgem, flags)) {
+		old = search_vmap_cache(kgem, NUM_PAGES(size), 0);
+		if (old) {
+			bo = malloc(sizeof(*bo));
+			if (bo == NULL)
+				return NULL;
+
+			memcpy(&bo->base, old, sizeof(*old));
+			if (old->rq)
+				list_replace(&old->request, &bo->base.request);
+			else
+				list_init(&bo->base.request);
+			list_replace(&old->vma, &bo->base.vma);
+			list_init(&bo->base.list);
+			free(old);
+
+			assert(bo->base.vmap);
+			assert(bo->base.tiling == I915_TILING_NONE);
+			assert(num_pages(&bo->base) >= NUM_PAGES(size));
+
+			bo->mem = kgem_bo_map__cpu(kgem, &bo->base);
+			if (bo->mem) {
+				bo->mmapped = true;
+				bo->need_io = false;
+				bo->base.io = true;
+				bo->base.refcnt = 1;
+
+				alloc = num_pages(&bo->base);
+				goto init;
+			} else {
+				kgem_bo_free(kgem, &bo->base);
+				bo = NULL;
+			}
+		}
+
 		if (kgem->has_cache_level) {
 			uint32_t handle;
 
@@ -4102,8 +4258,8 @@ struct kgem_bo *kgem_create_buffer(struct kgem *kgem,
 
 			debug_alloc(kgem, alloc);
 			__kgem_bo_init(&bo->base, handle, alloc);
-			DBG(("%s: created handle=%d for buffer\n",
-			     __FUNCTION__, bo->base.handle));
+			DBG(("%s: created CPU handle=%d for buffer, size %d\n",
+			     __FUNCTION__, bo->base.handle, alloc));
 
 			bo->base.reusable = false;
 			bo->base.vmap = true;
diff --git a/src/sna/kgem.h b/src/sna/kgem.h
index 63be218..b8d755c 100644
--- a/src/sna/kgem.h
+++ b/src/sna/kgem.h
@@ -125,6 +125,7 @@ struct kgem {
 	struct list large;
 	struct list active[NUM_CACHE_BUCKETS][3];
 	struct list inactive[NUM_CACHE_BUCKETS];
+	struct list vmap;
 	struct list batch_partials, active_partials;
 	struct list requests;
 	struct list sync_list;
@@ -243,7 +244,6 @@ enum {
 	CREATE_TEMPORARY = 0x20,
 	CREATE_NO_RETIRE = 0x40,
 	CREATE_NO_THROTTLE = 0x40,
-	CREATE_FORCE = 0x80,
 };
 struct kgem_bo *kgem_create_2d(struct kgem *kgem,
 			       int width,


More information about the xorg-commit mailing list