xf86-video-intel: 4 commits - src/sna/sna_accel.c src/sna/sna_glyphs.c src/sna/sna_render.c src/sna/sna_trapezoids_imprecise.c src/sna/sna_trapezoids_mono.c src/sna/sna_trapezoids_precise.c test/.gitignore test/Makefile.am test/render-glyphs.c test/render-triangle.c test/test.h test/test_image.c

Chris Wilson ickle at kemper.freedesktop.org
Mon May 18 05:41:26 PDT 2015


 src/sna/sna_accel.c                |    9 
 src/sna/sna_glyphs.c               |   91 ++++---
 src/sna/sna_render.c               |    2 
 src/sna/sna_trapezoids_imprecise.c |    3 
 src/sna/sna_trapezoids_mono.c      |   50 +++-
 src/sna/sna_trapezoids_precise.c   |    3 
 test/.gitignore                    |    2 
 test/Makefile.am                   |    2 
 test/render-glyphs.c               |  441 +++++++++++++++++++++++++++++++++++++
 test/render-triangle.c             |  180 +++++++++++++++
 test/test.h                        |    9 
 test/test_image.c                  |   36 ---
 12 files changed, 749 insertions(+), 79 deletions(-)

New commits:
commit 3852977f14a00db770df7efab79207116aaecbf0
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon May 18 09:58:08 2015 +0100

    test: Add a fidelity test for triangle edge rendering
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/sna_trapezoids_imprecise.c b/src/sna/sna_trapezoids_imprecise.c
index 4e90f9c..8bc7c8a 100644
--- a/src/sna/sna_trapezoids_imprecise.c
+++ b/src/sna/sna_trapezoids_imprecise.c
@@ -1707,8 +1707,7 @@ static void span_thread_add_box(struct sna *sna, void *data,
 {
 	struct span_thread_boxes *b = data;
 
-	__DBG(("%s: adding %d boxes with alpha=%f\n",
-	       __FUNCTION__, count, alpha));
+	__DBG(("%s: adding box with alpha=%f\n", __FUNCTION__, alpha));
 
 	if (unlikely(b->num_boxes == SPAN_THREAD_MAX_BOXES)) {
 		DBG(("%s: flushing %d boxes\n", __FUNCTION__, b->num_boxes));
diff --git a/src/sna/sna_trapezoids_mono.c b/src/sna/sna_trapezoids_mono.c
index 5b65fa4..07a7867 100644
--- a/src/sna/sna_trapezoids_mono.c
+++ b/src/sna/sna_trapezoids_mono.c
@@ -79,7 +79,7 @@ struct mono {
 	struct mono_polygon polygon;
 };
 
-#define I(x) pixman_fixed_to_int ((x) + pixman_fixed_1_minus_e/2)
+#define I(x) pixman_fixed_to_int((x) + pixman_fixed_1_minus_e/2)
 
 static struct quorem
 floored_muldivrem(int32_t x, int32_t a, int32_t b)
@@ -250,22 +250,22 @@ mono_add_line(struct mono *mono,
 
 		e->dxdy = floored_muldivrem(dx, pixman_fixed_1, dy);
 
-		e->x = floored_muldivrem((ytop - dst_y) * pixman_fixed_1 + pixman_fixed_1_minus_e/2 - p1->y,
+		e->x = floored_muldivrem((ytop - dst_y) * pixman_fixed_1 + pixman_fixed_1/2 - p1->y,
 					 dx, dy);
 		e->x.quo += p1->x;
 		e->x.rem -= dy;
 
 		e->dy = dy;
-
-		__DBG(("%s: initial x=%d [%d.%d/%d] + dxdy=%d.%d/%d\n",
-		       __FUNCTION__,
-		       I(e->x.quo), e->x.quo, e->x.rem, e->dy,
-		       e->dxdy.quo, e->dxdy.rem, e->dy));
 	}
 	e->x.quo += dst_x*pixman_fixed_1;
+	__DBG(("%s: initial x=%d [%d.%d/%d] + dxdy=%d.%d/%d\n",
+	       __FUNCTION__,
+	       I(e->x.quo), e->x.quo, e->x.rem, e->dy,
+	       e->dxdy.quo, e->dxdy.rem, e->dy));
 
 	{
 		struct mono_edge **ptail = &polygon->y_buckets[ytop - mono->clip.extents.y1];
+		assert(ytop - mono->clip.extents.y1 < mono->clip.extents.y2 - mono->clip.extents.y1);
 		if (*ptail)
 			(*ptail)->prev = e;
 		e->next = *ptail;
@@ -369,6 +369,10 @@ static struct mono_edge *mono_filter(struct mono_edge *edges)
 		    e->x.rem == n->x.rem &&
 		    e->dxdy.quo == n->dxdy.quo &&
 		    e->dxdy.rem == n->dxdy.rem) {
+			assert(e->dy == n->dy);
+			__DBG(("%s: discarding cancellation pair (%d.%d) + (%d.%d)\n",
+			       __FUNCTION__, e->x.quo, e->x.rem, e->dxdy.quo, e->dxdy.rem));
+
 			if (e->prev)
 				e->prev->next = n->next;
 			else
@@ -379,8 +383,11 @@ static struct mono_edge *mono_filter(struct mono_edge *edges)
 				break;
 
 			e = n->next;
-		} else
+		} else {
+			__DBG(("%s: adding edge (%d.%d) + (%d.%d)/%d, height=%d\n",
+			       __FUNCTION__, n->x.quo, n->x.rem, n->dxdy.quo, n->dxdy.rem, n->dy, n->height_left));
 			e = n;
+		}
 	}
 
 	return edges;
@@ -571,6 +578,8 @@ mono_row(struct mono *c, int16_t y, int16_t h)
 	int winding = 0;
 	BoxRec box;
 
+	__DBG(("%s: y=%d, h=%d\n", __FUNCTION__, y, h));
+
 	DBG_MONO_EDGES(edge);
 	VALIDATE_MONO_EDGES(&c->head);
 
@@ -581,6 +590,8 @@ mono_row(struct mono *c, int16_t y, int16_t h)
 		struct mono_edge *next = edge->next;
 		int16_t xend = I(edge->x.quo);
 
+		__DBG(("%s: adding edge dir=%d [winding=%d], x=%d [%d]\n",
+		       __FUNCTION__, edge->dir, winding + edge->dir, xend, edge->x.quo));
 		if (--edge->height_left) {
 			if (edge->dy) {
 				edge->x.quo += edge->dxdy.quo;
@@ -589,6 +600,8 @@ mono_row(struct mono *c, int16_t y, int16_t h)
 					++edge->x.quo;
 					edge->x.rem -= edge->dy;
 				}
+				__DBG(("%s: stepped edge (%d.%d) + (%d.%d)/%d, height=%d, prev_x=%d\n",
+				       __FUNCTION__, edge->x.quo, edge->x.rem, edge->dxdy.quo, edge->dxdy.rem, edge->dy, edge->height_left, edge->x.quo));
 			}
 
 			if (edge->x.quo < prev_x) {
@@ -612,17 +625,22 @@ mono_row(struct mono *c, int16_t y, int16_t h)
 		winding += edge->dir;
 		if (winding == 0) {
 			assert(I(next->x.quo) >= xend);
-			if (I(next->x.quo) > xend + 1) {
+			if (I(next->x.quo) > xend) {
+				__DBG(("%s: end span: %d\n", __FUNCTION__, xend));
 				if (xstart < c->clip.extents.x1)
 					xstart = c->clip.extents.x1;
 				if (xend > c->clip.extents.x2)
 					xend = c->clip.extents.x2;
-				if (xend > xstart)
+				if (xend > xstart) {
+					__DBG(("%s: emit span [%d, %d]\n", __FUNCTION__, xstart, xend));
 					c->span(c, xstart, xend, &box);
+				}
 				xstart = INT16_MIN;
 			}
-		} else if (xstart == INT16_MIN)
+		} else if (xstart == INT16_MIN) {
+			__DBG(("%s: starting new span: %d\n", __FUNCTION__, xend));
 			xstart = xend;
+		}
 
 		edge = next;
 	}
@@ -684,9 +702,14 @@ mono_render(struct mono *mono)
 	for (i = 0; i < h; i = j) {
 		j = i + 1;
 
+		__DBG(("%s: row=%d, new edges? %d\n", __FUNCTION__,
+		       i, polygon->y_buckets[i] != NULL));
+
 		if (polygon->y_buckets[i])
 			mono_merge_edges(mono, polygon->y_buckets[i]);
 
+		__DBG(("%s: row=%d, vertical? %d\n", __FUNCTION__,
+		       i, mono->is_vertical));
 		if (mono->is_vertical) {
 			struct mono_edge *e = mono->head.next;
 			int min_height = h - i;
@@ -701,6 +724,7 @@ mono_render(struct mono *mono)
 				j++;
 			if (j != i + 1)
 				mono_step_edges(mono, j - (i + 1));
+			__DBG(("%s: %d vertical rows\n", __FUNCTION__, j-i));
 		}
 
 		mono_row(mono, i, j-i);
@@ -1423,10 +1447,13 @@ mono_triangles_span_converter(struct sna *sna,
 		mono_render(&mono);
 		mono.op.done(mono.sna, &mono.op);
 	}
+	mono_fini(&mono);
 
 	if (!was_clear && !operator_is_bounded(op)) {
 		xPointFixed p1, p2;
 
+		DBG(("%s: performing unbounded clear\n", __FUNCTION__));
+
 		if (!mono_init(&mono, 2+3*count))
 			return false;
 
@@ -1472,7 +1499,6 @@ mono_triangles_span_converter(struct sna *sna,
 		mono_fini(&mono);
 	}
 
-	mono_fini(&mono);
 	REGION_UNINIT(NULL, &mono.clip);
 	return true;
 }
diff --git a/src/sna/sna_trapezoids_precise.c b/src/sna/sna_trapezoids_precise.c
index c983852..242b4ac 100644
--- a/src/sna/sna_trapezoids_precise.c
+++ b/src/sna/sna_trapezoids_precise.c
@@ -1655,8 +1655,7 @@ static void span_thread_add_box(struct sna *sna, void *data,
 {
 	struct span_thread_boxes *b = data;
 
-	__DBG(("%s: adding %d boxes with alpha=%f\n",
-	       __FUNCTION__, count, alpha));
+	__DBG(("%s: adding box with alpha=%f\n", __FUNCTION__, alpha));
 
 	if (unlikely(b->num_boxes == SPAN_THREAD_MAX_BOXES)) {
 		DBG(("%s: flushing %d boxes\n", __FUNCTION__, b->num_boxes));
diff --git a/test/.gitignore b/test/.gitignore
index 373ba15..2a68c3d 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -11,6 +11,7 @@ cursor-test
 render-fill
 render-trapezoid
 render-trapezoid-image
+render-triangle
 render-fill-copy
 render-composite-solid
 render-composite-solid-mask
diff --git a/test/Makefile.am b/test/Makefile.am
index aaa656a..50f2126 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -15,6 +15,7 @@ stress_TESTS = \
 	render-glyphs \
 	render-trapezoid \
 	render-trapezoid-image \
+	render-triangle \
 	render-fill-copy \
 	render-composite-solid \
 	render-composite-solid-mask \
diff --git a/test/render-triangle.c b/test/render-triangle.c
new file mode 100644
index 0000000..165834c
--- /dev/null
+++ b/test/render-triangle.c
@@ -0,0 +1,180 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "test.h"
+
+enum edge {
+	EDGE_SHARP = PolyEdgeSharp,
+	EDGE_SMOOTH,
+};
+
+static void set_edge(Display *dpy, Picture p, enum edge edge)
+{
+	XRenderPictureAttributes a;
+
+	a.poly_edge = edge;
+	XRenderChangePicture(dpy, p, CPPolyEdge, &a);
+}
+
+static XRenderPictFormat *mask_format(Display *dpy, enum mask mask)
+{
+	switch (mask) {
+	default:
+	case MASK_NONE: return NULL;
+	case MASK_A1: return XRenderFindStandardFormat(dpy, PictStandardA1);
+	case MASK_A8: return XRenderFindStandardFormat(dpy, PictStandardA8);
+	}
+}
+
+static const char *mask_name(enum mask mask)
+{
+	switch (mask) {
+	default:
+	case MASK_NONE: return "none";
+	case MASK_A1: return "a1";
+	case MASK_A8: return "a8";
+	}
+}
+
+static const char *edge_name(enum edge edge)
+{
+	switch (edge) {
+	default:
+	case EDGE_SHARP: return "sharp";
+	case EDGE_SMOOTH: return "smooth";
+	}
+}
+
+static void clear(struct test_display *dpy, struct test_target *tt)
+{
+	XRenderColor render_color = {0};
+	XRenderFillRectangle(dpy->dpy, PictOpClear, tt->picture, &render_color,
+			     0, 0, tt->width, tt->height);
+}
+
+static void step_to_point(int step, int width, int height, XPointFixed *p)
+{
+	do {
+		p->x = (step - 64) << 16;
+		p->y = -64 << 16;
+
+		step -= width - 128;
+		if (step <= 0)
+			return;
+
+		p->x = (width + 64) << 16;
+		p->y = (step - 64) << 16;
+		step -= height - 128;
+
+		if (step <= 0)
+			return;
+
+		p->x = (width + 64 - step) << 16;
+		p->y = (height + 64) << 16;
+		step -= width - 128;
+
+		if (step <= 0)
+			return;
+
+		p->x = -64 << 16;
+		p->y = (height + 64 - step) << 16;
+		step -= height - 128;
+	} while (step > 0);
+}
+
+static void edge_test(struct test *t,
+		      enum mask mask,
+		      enum edge edge,
+		      enum target target)
+{
+	struct test_target out, ref;
+	XRenderColor white = { 0xffff, 0xffff, 0xffff, 0xffff };
+	Picture src_ref, src_out;
+	XTriangle tri;
+	unsigned step, max;
+
+	test_target_create_render(&t->out, target, &out);
+	set_edge(t->out.dpy, out.picture, edge);
+	src_out = XRenderCreateSolidFill(t->out.dpy, &white);
+
+	test_target_create_render(&t->ref, target, &ref);
+	set_edge(t->ref.dpy, ref.picture, edge);
+	src_ref = XRenderCreateSolidFill(t->ref.dpy, &white);
+
+	printf("Testing edges (with mask %s and %s edges) (%s): ",
+	       mask_name(mask),
+	       edge_name(edge),
+	       test_target_name(target));
+	fflush(stdout);
+
+	max = 2*(out.width + 128 + out.height+128);
+	step = 0;
+	for (step = 0; step <= max; step++) {
+		char buf[80];
+
+		step_to_point(step, out.width, out.height, &tri.p1);
+		step_to_point(step + out.width + 128,
+			      out.width, out.height,
+			      &tri.p2);
+		step_to_point(step + out.height + 128 + 2*(out.width + 128),
+			      out.width, out.height,
+			      &tri.p3);
+
+		sprintf(buf,
+			"tri=((%d, %d), (%d, %d), (%d, %d))\n",
+			tri.p1.x >> 16, tri.p1.y >> 16,
+			tri.p2.x >> 16, tri.p2.y >> 16,
+			tri.p3.x >> 16, tri.p3.y >> 16);
+
+		clear(&t->out, &out);
+		XRenderCompositeTriangles(t->out.dpy,
+					  PictOpSrc,
+					  src_out,
+					  out.picture,
+					  mask_format(t->out.dpy, mask),
+					  0, 0,
+					  &tri, 1);
+
+		clear(&t->ref, &ref);
+		XRenderCompositeTriangles(t->ref.dpy,
+					  PictOpSrc,
+					  src_ref,
+					  ref.picture,
+					  mask_format(t->ref.dpy, mask),
+					  0, 0,
+					  &tri, 1);
+
+		test_compare(t,
+			     out.draw, out.format,
+			     ref.draw, ref.format,
+			     0, 0, out.width, out.height,
+			     buf);
+	}
+
+	XRenderFreePicture(t->out.dpy, src_out);
+	test_target_destroy_render(&t->out, &out);
+
+	XRenderFreePicture(t->ref.dpy, src_ref);
+	test_target_destroy_render(&t->ref, &ref);
+
+	printf("pass\n");
+}
+
+int main(int argc, char **argv)
+{
+	struct test test;
+	enum target target;
+	enum mask mask;
+	enum edge edge;
+
+	test_init(&test, argc, argv);
+
+	for (target = TARGET_FIRST; target <= TARGET_LAST; target++) {
+		for (mask = MASK_NONE; mask <= MASK_A8; mask++)
+			for (edge = EDGE_SHARP; edge <= EDGE_SMOOTH; edge++)
+				edge_test(&test, mask, edge, target);
+	}
+
+	return 0;
+}
commit fe64672c83e75ccb3f7a1bf37e05df05d0c05392
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon May 18 09:18:41 2015 +0100

    sna: Wrap GetImage with sigtrap
    
    Mostly for completeness, though it is still remotely possibly for the
    dst pointer to raise a SIGBUS (just less likely since it is not a i915
    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 c79af6a..9a5079f 100644
--- a/src/sna/sna_accel.c
+++ b/src/sna/sna_accel.c
@@ -17072,7 +17072,7 @@ sna_get_image__fast(PixmapPtr pixmap,
 	if (priv == NULL || priv->gpu_damage == NULL)
 		return false;
 
-	if (priv->clear) {
+	if (priv->clear && sigtrap_get() == 0) {
 		int w = region->extents.x2 - region->extents.x1;
 		int h = region->extents.y2 - region->extents.y1;
 		int pitch = PixmapBytePad(w, pixmap->drawable.depth);
@@ -17098,6 +17098,7 @@ sna_get_image__fast(PixmapPtr pixmap,
 				    priv->clear_color);
 		}
 
+		sigtrap_put();
 		return true;
 	}
 
commit d4c82a16bc3ad89a9684c82cd1dd4bcef117ed8c
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon May 18 09:16:12 2015 +0100

    test: Remove the blit through a temporary Pixmap
    
    Originally this was inplace so that we wouldn't simply migrate the
    target away from the GPU whenever we inspected results. That is no
    longer a problem and so we can speed up the tests by skipping the
    temporary.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/test/test_image.c b/test/test_image.c
index d15a8af..1c07699 100644
--- a/test/test_image.c
+++ b/test/test_image.c
@@ -197,13 +197,10 @@ void test_compare(struct test *t,
 		  const char *info)
 {
 	XImage out_image, ref_image;
-	Pixmap tmp;
-	char *out, *ref;
+	uint32_t *out, *ref;
 	char buf[600];
 	uint32_t mask;
 	int i, j;
-	XGCValues gcv;
-	GC gc;
 
 	if (w * h * 4 > t->out.max_shm_size)
 		return test_compare_fallback(t,
@@ -214,37 +211,24 @@ void test_compare(struct test *t,
 	test_init_image(&out_image, &t->out.shm, out_format, w, h);
 	test_init_image(&ref_image, &t->ref.shm, ref_format, w, h);
 
-	gcv.graphics_exposures = 0;
-
 	die_unless(out_image.depth == ref_image.depth);
 	die_unless(out_image.bits_per_pixel == ref_image.bits_per_pixel);
 	die_unless(out_image.bits_per_pixel == 32);
 
-	mask = depth_mask(out_image.depth);
+	XShmGetImage(t->out.dpy, out_draw, &out_image, x, y, AllPlanes);
+	out = (uint32_t *)out_image.data;
 
-	tmp = XCreatePixmap(t->out.dpy, out_draw, w, h, out_image.depth);
-	gc = XCreateGC(t->out.dpy, tmp, GCGraphicsExposures, &gcv);
-	XCopyArea(t->out.dpy, out_draw, tmp, gc, x, y, w, h, 0, 0);
-	XShmGetImage(t->out.dpy, tmp, &out_image, 0, 0, AllPlanes);
-	XFreeGC(t->out.dpy, gc);
-	XFreePixmap(t->out.dpy, tmp);
-	out = out_image.data;
-
-	tmp = XCreatePixmap(t->ref.dpy, ref_draw, w, h, ref_image.depth);
-	gc = XCreateGC(t->ref.dpy, tmp, GCGraphicsExposures, &gcv);
-	XCopyArea(t->ref.dpy, ref_draw, tmp, gc, x, y, w, h, 0, 0);
-	XShmGetImage(t->ref.dpy, tmp, &ref_image, 0, 0, AllPlanes);
-	XFreeGC(t->ref.dpy, gc);
-	XFreePixmap(t->ref.dpy, tmp);
-	ref = ref_image.data;
+	XShmGetImage(t->ref.dpy, ref_draw, &ref_image, x, y, AllPlanes);
+	ref = (uint32_t *)ref_image.data;
 
 	/* Start with an exact comparison. However, one quicky desires
 	 * a fuzzy comparator to hide hardware inaccuracies...
 	 */
+	mask = depth_mask(out_image.depth);
 	for (j = 0; j < h; j++) {
 		for (i = 0; i < w; i++) {
-			uint32_t a = ((uint32_t *)out)[i] & mask;
-			uint32_t b = ((uint32_t *)ref)[i] & mask;
+			uint32_t a = out[i] & mask;
+			uint32_t b = ref[i] & mask;
 			if (a != b && pixel_difference(a, b) > MAX_DELTA) {
 				show_pixels(buf,
 					    &out_image, &ref_image,
@@ -255,8 +239,8 @@ void test_compare(struct test *t,
 				    x,i, y,j, a, b, pixel_difference(a, b), buf, info);
 			}
 		}
-		out += out_image.bytes_per_line;
-		ref += ref_image.bytes_per_line;
+		out = (uint32_t *)((char *)out + out_image.bytes_per_line);
+		ref = (uint32_t *)((char *)ref + ref_image.bytes_per_line);
 	}
 }
 
commit b10ef9cf5c9cc844f432e9024deeb78fa1034a8e
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun May 17 20:45:25 2015 +0100

    sna/glyphs: Improve handling of low bitdepth mask format conversions
    
    We shouldn't just discard the mask if the user requests that we render
    the glyphs through a low bitdepth mask - and in doing so we should also
    be careful not to improve the bitdepth of that mask (since we don't take
    into account the extra quantisation desired).
    
    Testcase: render-glyphs
    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 af98301..c79af6a 100644
--- a/src/sna/sna_accel.c
+++ b/src/sna/sna_accel.c
@@ -817,8 +817,8 @@ create_pixmap(struct sna *sna, ScreenPtr screen,
 		datasize += adjust;
 	}
 
-	DBG(("%s: allocating pixmap %dx%d, depth=%d, size=%ld\n",
-	     __FUNCTION__, width, height, depth, (long)datasize));
+	DBG(("%s: allocating pixmap %dx%d, depth=%d/%d, size=%ld\n",
+	     __FUNCTION__, width, height, depth, bpp, (long)datasize));
 	pixmap = AllocatePixmap(screen, datasize);
 	if (!pixmap)
 		return NullPixmap;
@@ -4091,7 +4091,7 @@ sna_pixmap_create_upload(ScreenPtr screen,
 	assert(width);
 	assert(height);
 
-	if (depth == 1)
+	if (depth < 8)
 		return create_pixmap(sna, screen, width, height, depth,
 				     CREATE_PIXMAP_USAGE_SCRATCH);
 
diff --git a/src/sna/sna_glyphs.c b/src/sna/sna_glyphs.c
index 6d57e8f..9e945f0 100644
--- a/src/sna/sna_glyphs.c
+++ b/src/sna/sna_glyphs.c
@@ -74,7 +74,7 @@
 #define NO_GLYPHS_VIA_MASK 0
 #define FORCE_SMALL_MASK 0 /* -1 = never, 1 = always */
 #define NO_GLYPHS_SLOW 0
-#define NO_DISCARD_MASK 0
+#define DISCARD_MASK 0 /* -1 = never, 1 = always */
 
 #define CACHE_PICTURE_SIZE 1024
 #define GLYPH_MIN_SIZE 8
@@ -1094,6 +1094,9 @@ sna_glyph_get_image(GlyphPtr g, ScreenPtr s)
 
 static inline bool use_small_mask(struct sna *sna, int16_t width, int16_t height, int depth)
 {
+	if (depth < 8)
+		return true;
+
 	if (FORCE_SMALL_MASK)
 		return FORCE_SMALL_MASK > 0;
 
@@ -1156,12 +1159,6 @@ glyphs_via_mask(struct sna *sna,
 	src_x += box.x1 - list->xOff;
 	src_y += box.y1 - list->yOff;
 
-	if (format->depth < 8) {
-		format = PictureMatchFormat(screen, 8, PICT_a8);
-		if (!format)
-			return false;
-	}
-
 	component_alpha = NeedsComponent(format->format);
 	if (use_small_mask(sna, width, height, format->depth)) {
 		pixman_image_t *mask_image;
@@ -1179,7 +1176,7 @@ use_small_mask:
 			return false;
 
 		mask_image =
-			pixman_image_create_bits(format->depth << 24 | format->format,
+			pixman_image_create_bits(pixmap->drawable.bitsPerPixel << 24 | format->format,
 						 width, height,
 						 pixmap->devPrivate.ptr,
 						 pixmap->devKind);
@@ -1386,10 +1383,11 @@ next_image:
 					DBG(("%s: atlas format=%08x, mask format=%08x\n",
 					     __FUNCTION__,
 					     (int)p->atlas->format,
-					     (int)(format->depth << 24 | format->format)));
+					     (int)mask->format));
 
 					memset(&tmp, 0, sizeof(tmp));
-					if (p->atlas->format == (format->depth << 24 | format->format)) {
+					if (p->atlas->format == mask->format ||
+					    alphaless(p->atlas->format) == mask->format) {
 						ok = sna->render.composite(sna, PictOpAdd,
 									   p->atlas, NULL, mask,
 									   0, 0, 0, 0, 0, 0,
@@ -1564,6 +1562,8 @@ skip_glyph:
 out:
 	if (list_extents != stack_extents)
 		free(list_extents);
+	DBG(("%s: format=%08d, depth=%d\n",
+	     __FUNCTION__, format->format, format->depth));
 	return format;
 }
 
@@ -1573,24 +1573,34 @@ static bool can_discard_mask(uint8_t op, PicturePtr src, PictFormatPtr mask,
 	PictFormatPtr g;
 	uint32_t color;
 
-	if (NO_DISCARD_MASK)
-		return false;
+	if (DISCARD_MASK)
+		return DISCARD_MASK > 0;
 
 	DBG(("%s: nlist=%d, mask=%08x, depth %d, op=%d (bounded? %d)\n",
 	     __FUNCTION__, nlist,
 	     mask ? (unsigned)mask->format : 0, mask ? mask->depth : 0,
 	     op, op_is_bounded(op)));
 
-	if (nlist == 1 && list->len == 1)
-		return true;
+	if (nlist == 1 && list->len == 1) {
+		if (mask == list->format)
+			return true;
+
+		g = list->format;
+		goto skip;
+	}
 
-	if (!op_is_bounded(op))
+	if (!op_is_bounded(op)) {
+		DBG(("%s: unbounded op, not discarding\n", __FUNCTION__));
 		return false;
+	}
 
 	/* No glyphs overlap and we are not performing a mask conversion. */
 	g = glyphs_format(nlist, list, glyphs);
-	if (mask == g)
+	if (mask == g) {
+		DBG(("%s: mask matches glyphs format, no conversion, so discard mask\n",
+		     __FUNCTION__));
 		return true;
+	}
 
 	DBG(("%s: preferred mask format %08x, depth %d\n",
 	     __FUNCTION__, g ? (unsigned)g->format : 0,  g ? g->depth : 0));
@@ -1605,18 +1615,41 @@ static bool can_discard_mask(uint8_t op, PicturePtr src, PictFormatPtr mask,
 
 			list++;
 		}
+
+		if (!sna_picture_is_solid(src, &color))
+			return false;
+
+		return color >> 24 == 0xff;
 	} else {
-		if (PICT_FORMAT_A(mask->format) >= PICT_FORMAT_A(g->format))
+skip:
+		if (mask->format == g->format)
 			return true;
 
-		if (g->depth != 1)
-			return false;
-	}
+		if (mask->format == alphaless(g->format))
+			return true;
+
+		if (PICT_FORMAT_TYPE(g->format) == PICT_TYPE_A &&
+		    PICT_FORMAT_TYPE(mask->format) != PICT_TYPE_A)
+			return true;
 
-	if (!sna_picture_is_solid(src, &color))
 		return false;
+	}
+}
 
-	return color >> 24 == 0xff;
+static uint32_t pixman_format(PictFormatPtr short_format)
+{
+	uint32_t bpp;
+
+	bpp = short_format->depth;
+	if (bpp <= 1)
+		bpp = 1;
+	else if (bpp <= 8)
+		bpp = 8;
+	else if (bpp <= 16)
+		bpp = 16;
+	else
+		bpp = 32;
+	return bpp << 24 | short_format->format;
 }
 
 static void
@@ -1756,7 +1789,7 @@ next:
 		if (sigtrap_get() == 0) {
 			if (mask_format) {
 				pixman_composite_glyphs(op, src_image, dst_image,
-							mask_format->format | (mask_format->depth << 24),
+							pixman_format(mask_format),
 							src_x + src_dx + region.extents.x1 - dst_x,
 							src_y + src_dy + region.extents.y1 - dst_y,
 							region.extents.x1, region.extents.y1,
@@ -1815,10 +1848,10 @@ out:
 			     x, y,
 			     mask_format->depth,
 			     (long)mask_format->format,
-			     (long)(mask_format->depth << 24 | mask_format->format),
+			     (long)pixman_format(mask_format),
 			     NeedsComponent(mask_format->format)));
 			mask_image =
-				pixman_image_create_bits(mask_format->depth << 24 | mask_format->format,
+				pixman_image_create_bits(pixman_format(mask_format),
 							 region.extents.x2 - region.extents.x1,
 							 region.extents.y2 - region.extents.y1,
 							 NULL, 0);
@@ -2086,12 +2119,6 @@ glyphs_via_image(struct sna *sna,
 	src_x += box.x1 - list->xOff;
 	src_y += box.y1 - list->yOff;
 
-	if (format->depth < 8) {
-		format = PictureMatchFormat(screen, 8, PICT_a8);
-		if (!format)
-			return false;
-	}
-
 	DBG(("%s: small mask [format=%lx, depth=%d, size=%d], rendering glyphs to upload buffer\n",
 	     __FUNCTION__, (unsigned long)format->format,
 	     format->depth, (uint32_t)width*height*format->depth));
@@ -2104,7 +2131,7 @@ glyphs_via_image(struct sna *sna,
 		return false;
 
 	mask_image =
-		pixman_image_create_bits(format->depth << 24 | format->format,
+		pixman_image_create_bits(pixmap->drawable.bitsPerPixel << 24 | format->format,
 					 width, height,
 					 pixmap->devPrivate.ptr,
 					 pixmap->devKind);
diff --git a/src/sna/sna_render.c b/src/sna/sna_render.c
index 8a9c7f4..d95536e 100644
--- a/src/sna/sna_render.c
+++ b/src/sna/sna_render.c
@@ -54,7 +54,7 @@ sna_format_for_depth(int depth)
 {
 	switch (depth) {
 	case 1: return PICT_a1;
-	case 4: return PICT_a4;
+	case 4: return PICT_x4a4;
 	case 8: return PICT_a8;
 	case 15: return PICT_x1r5g5b5;
 	case 16: return PICT_r5g6b5;
diff --git a/test/.gitignore b/test/.gitignore
index da174be..373ba15 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -18,6 +18,7 @@ render-copyarea
 render-copyarea-mask
 render-copyarea-size
 render-copy-alphaless
+render-glyphs
 mixed-stress
 lowlevel-blt-bench
 vsync.avi
diff --git a/test/Makefile.am b/test/Makefile.am
index 9b37222..aaa656a 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -12,6 +12,7 @@ stress_TESTS = \
 	DrawSegments \
 	cursor-test \
 	render-fill \
+	render-glyphs \
 	render-trapezoid \
 	render-trapezoid-image \
 	render-fill-copy \
diff --git a/test/render-glyphs.c b/test/render-glyphs.c
new file mode 100644
index 0000000..8822e36
--- /dev/null
+++ b/test/render-glyphs.c
@@ -0,0 +1,441 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include <X11/Xutil.h> /* for XDestroyImage */
+#include <pixman.h> /* for pixman blt functions */
+
+#include "test.h"
+
+static const XRenderColor colors[] = {
+	/* red, green, blue, alpha */
+	{ 0 },
+	{ 0, 0, 0, 0xffff },
+	{ 0xffff, 0, 0, 0xffff },
+	{ 0, 0xffff, 0, 0xffff },
+	{ 0, 0, 0xffff, 0xffff },
+	{ 0xffff, 0xffff, 0xffff, 0xffff },
+};
+
+static struct clip {
+	void *func;
+} clips[] = {
+	{ NULL },
+};
+
+static int _x_error_occurred;
+
+static int
+_check_error_handler(Display     *display,
+		     XErrorEvent *event)
+{
+	_x_error_occurred = 1;
+	return False; /* ignored */
+}
+
+static void clear(struct test_display *dpy,
+		  struct test_target *tt,
+		  const XRenderColor *c)
+{
+	XRenderFillRectangle(dpy->dpy, PictOpClear, tt->picture, c,
+			     0, 0, tt->width, tt->height);
+}
+
+static bool check_op(struct test_display *dpy, int op, struct test_target *tt)
+{
+	XRenderColor render_color = {0};
+
+	XSync(dpy->dpy, True);
+	_x_error_occurred = 0;
+
+	XRenderFillRectangle(dpy->dpy, op,
+			     tt->picture, &render_color,
+			     0, 0, 0, 0);
+
+	XSync(dpy->dpy, True);
+	return _x_error_occurred == 0;
+}
+
+struct glyph_iter {
+	enum {
+		GLYPHS, OP, DST, SRC, MASK, CLIP,
+	} stage;
+
+	int glyph_format;
+	int op;
+	int dst_color;
+	int src_color;
+	int mask_format;
+	int clip;
+
+	struct {
+		struct test_display *dpy;
+		struct test_target tt;
+		GlyphSet glyphset;
+		Picture src;
+		XRenderPictFormat *mask_format;
+	} ref, out;
+};
+
+static void glyph_iter_init(struct glyph_iter *gi,
+			    struct test *t, enum target target)
+{
+	memset(gi, 0, sizeof(*gi));
+
+	gi->out.dpy = &t->out;
+	test_target_create_render(&t->out, target, &gi->out.tt);
+
+	gi->ref.dpy = &t->ref;
+	test_target_create_render(&t->ref, target, &gi->ref.tt);
+
+	gi->stage = GLYPHS;
+	gi->glyph_format = -1;
+	gi->op = -1;
+	gi->dst_color = -1;
+	gi->src_color = -1;
+	gi->mask_format = -1;
+	gi->clip = -1;
+}
+
+static void render_clear(char *image, int image_size, int bpp)
+{
+	memset(image, 0, image_size);
+}
+
+static void render_black(char *image, int image_size, int bpp)
+{
+	if (bpp == 4) {
+		uint32_t *p = (uint32_t *)image;
+		image_size /= 4;
+		while (image_size--)
+			*p++ = 0x000000ff;
+	} else
+		memset(image, 0x55, image_size);
+}
+
+static void render_green(char *image, int image_size, int bpp)
+{
+	if (bpp == 4) {
+		uint32_t *p = (uint32_t *)image;
+		image_size /= 4;
+		while (image_size--)
+			*p++ = 0xffff0000;
+	} else
+		memset(image, 0xaa, image_size);
+}
+
+static void render_white(char *image, int image_size, int bpp)
+{
+	memset(image, 0xff, image_size);
+}
+
+static GlyphSet create_glyphs(Display *dpy, int format_id)
+{
+#define N_GLYPHS 4
+	XRenderPictFormat *format;
+	XGlyphInfo glyph = { 8, 8, 0, 0, 8, 0 };
+	char image[4*8*8];
+	GlyphSet glyphset;
+	Glyph gid;
+	int image_size;
+	int bpp;
+	int n;
+
+	format = XRenderFindStandardFormat(dpy, format_id);
+	if (format == NULL)
+		return 0;
+
+	switch (format_id) {
+	case PictStandardARGB32:
+	case PictStandardRGB24:
+		image_size = 4 * 8 * 8;
+		bpp = 4;
+		break;
+	case PictStandardA8:
+	case PictStandardA4:
+		image_size = 8 * 8;
+		bpp = 1;
+		break;
+	case PictStandardA1:
+		image_size = 8;
+		bpp = 0;
+		break;
+	default:
+		return 0;
+	}
+
+	glyphset = XRenderCreateGlyphSet(dpy, format);
+	for (n = 0; n < N_GLYPHS; n++) {
+		gid = n;
+
+		switch (n) {
+		case 0: render_clear(image, image_size, bpp); break;
+		case 1: render_black(image, image_size, bpp); break;
+		case 2: render_green(image, image_size, bpp); break;
+		case 3: render_white(image, image_size, bpp); break;
+		}
+
+		XRenderAddGlyphs(dpy, glyphset,
+				 &gid, &glyph, 1, image, image_size);
+	}
+
+	return glyphset;
+}
+
+static const char *glyph_name(int n)
+{
+	switch (n) {
+	case 0: return "clear";
+	case 1: return "black";
+	case 2: return "green";
+	case 3: return "white";
+	default: return "unknown";
+	}
+}
+
+static bool glyph_iter_next(struct glyph_iter *gi)
+{
+restart:
+	if (gi->stage == GLYPHS) {
+		if (++gi->glyph_format == PictStandardNUM)
+			return false;
+
+		if (gi->out.glyphset)
+			XRenderFreeGlyphSet(gi->out.dpy->dpy,
+					    gi->out.glyphset);
+		gi->out.glyphset = create_glyphs(gi->out.dpy->dpy,
+					       gi->glyph_format);
+
+		if (gi->ref.glyphset)
+			XRenderFreeGlyphSet(gi->ref.dpy->dpy,
+					    gi->ref.glyphset);
+		gi->ref.glyphset = create_glyphs(gi->ref.dpy->dpy,
+					       gi->glyph_format);
+
+		gi->stage++;
+	}
+
+	if (gi->stage == OP) {
+		do {
+			if (++gi->op == 255)
+				goto reset_op;
+		} while (!check_op(gi->out.dpy, gi->op, &gi->out.tt) ||
+			 !check_op(gi->ref.dpy, gi->op, &gi->ref.tt));
+
+		gi->stage++;
+	}
+
+	if (gi->stage == DST) {
+		if (++gi->dst_color == ARRAY_SIZE(colors))
+			goto reset_dst;
+
+		gi->stage++;
+	}
+
+	if (gi->stage == SRC) {
+		if (++gi->src_color == ARRAY_SIZE(colors))
+			goto reset_src;
+
+		if (gi->ref.src)
+			XRenderFreePicture(gi->ref.dpy->dpy, gi->ref.src);
+		gi->ref.src = XRenderCreateSolidFill(gi->ref.dpy->dpy,
+						     &colors[gi->src_color]);
+
+		if (gi->out.src)
+			XRenderFreePicture(gi->out.dpy->dpy, gi->out.src);
+		gi->out.src = XRenderCreateSolidFill(gi->out.dpy->dpy,
+						     &colors[gi->src_color]);
+
+		gi->stage++;
+	}
+
+	if (gi->stage == MASK) {
+		if (++gi->mask_format > PictStandardNUM)
+			goto reset_mask;
+
+		if (gi->mask_format == PictStandardRGB24)
+			gi->mask_format++;
+
+		if (gi->mask_format < PictStandardNUM) {
+			gi->out.mask_format = XRenderFindStandardFormat(gi->out.dpy->dpy,
+									gi->mask_format);
+			gi->ref.mask_format = XRenderFindStandardFormat(gi->ref.dpy->dpy,
+									gi->mask_format);
+		} else {
+			gi->out.mask_format = NULL;
+			gi->ref.mask_format = NULL;
+		}
+
+		gi->stage++;
+	}
+
+	if (gi->stage == CLIP) {
+		if (++gi->clip == ARRAY_SIZE(clips))
+			goto reset_clip;
+
+		gi->stage++;
+	}
+
+	gi->stage--;
+	return true;
+
+reset_op:
+	gi->op = -1;
+reset_dst:
+	gi->dst_color = -1;
+reset_src:
+	gi->src_color = -1;
+reset_mask:
+	gi->mask_format = -1;
+reset_clip:
+	gi->clip = -1;
+	gi->stage--;
+	goto restart;
+}
+
+static void glyph_iter_fini(struct glyph_iter *gi)
+{
+	if (gi->out.glyphset)
+		XRenderFreeGlyphSet (gi->out.dpy->dpy, gi->out.glyphset);
+	if (gi->ref.glyphset)
+		XRenderFreeGlyphSet (gi->ref.dpy->dpy, gi->ref.glyphset);
+
+	test_target_destroy_render(gi->out.dpy, &gi->out.tt);
+	test_target_destroy_render(gi->ref.dpy, &gi->ref.tt);
+}
+
+static const char *stdformat_to_str(int id)
+{
+	switch (id) {
+	case PictStandardARGB32: return "ARGB32";
+	case PictStandardRGB24: return "RGB24";
+	case PictStandardA8: return "A8";
+	case PictStandardA4: return "A4";
+	case PictStandardA1: return "A1";
+	default: return "none";
+	}
+}
+
+static char *glyph_iter_to_string(struct glyph_iter *gi,
+				  const char *format,
+				  ...)
+{
+	static char buf[100];
+	va_list ap;
+	int len;
+
+	len = sprintf(buf, "glyphs=%s, op=%d, dst=%08x, src=%08x, mask=%s",
+		      stdformat_to_str(gi->glyph_format), gi->op,
+		      xrender_color(&colors[gi->dst_color]),
+		      xrender_color(&colors[gi->src_color]),
+		      stdformat_to_str(gi->mask_format));
+
+	if (format) {
+		buf[len++] = ' ';
+		va_start(ap, format);
+		vsprintf(buf+len, format, ap);
+		va_end(ap);
+	}
+
+	return buf;
+}
+
+static void single(struct test *t, enum target target)
+{
+	struct glyph_iter gi;
+	int n;
+
+	printf("Testing single glyph (%s): ", test_target_name(target));
+	fflush(stdout);
+
+	glyph_iter_init(&gi, t, target);
+	while (glyph_iter_next(&gi)) {
+		XGlyphElt8 elt;
+		char id[N_GLYPHS];
+
+		for (n = 0; n < N_GLYPHS; n++) {
+			id[n] = n;
+
+			elt.chars = &id[n];
+			elt.nchars = 1;
+			elt.xOff = 0;
+			elt.yOff = 0;
+
+			clear(gi.out.dpy, &gi.out.tt, &colors[gi.dst_color]);
+			elt.glyphset = gi.out.glyphset;
+			XRenderCompositeText8 (gi.out.dpy->dpy, gi.op,
+					       gi.out.src,
+					       gi.out.tt.picture,
+					       gi.out.mask_format,
+					       0, 0,
+					       0, 8,
+					       &elt, 1);
+
+			clear(gi.ref.dpy, &gi.ref.tt, &colors[gi.dst_color]);
+			elt.glyphset = gi.ref.glyphset;
+			XRenderCompositeText8 (gi.ref.dpy->dpy, gi.op,
+					       gi.ref.src,
+					       gi.ref.tt.picture,
+					       gi.ref.mask_format,
+					       0, 0,
+					       0, 8,
+					       &elt, 1);
+			test_compare(t,
+				     gi.out.tt.draw, gi.out.tt.format,
+				     gi.ref.tt.draw, gi.ref.tt.format,
+				     0, 0, gi.out.tt.width, gi.out.tt.height,
+				     glyph_iter_to_string(&gi,
+							  "glyph=%s",
+							  glyph_name(n)));
+		}
+
+		elt.chars = &id[0];
+		elt.nchars = n;
+		clear(gi.out.dpy, &gi.out.tt, &colors[gi.dst_color]);
+		elt.glyphset = gi.out.glyphset;
+		XRenderCompositeText8 (gi.out.dpy->dpy, gi.op,
+				       gi.out.src,
+				       gi.out.tt.picture,
+				       gi.out.mask_format,
+				       0, 0,
+				       0, 8,
+				       &elt, 1);
+
+		clear(gi.ref.dpy, &gi.ref.tt, &colors[gi.dst_color]);
+		elt.glyphset = gi.ref.glyphset;
+		XRenderCompositeText8 (gi.ref.dpy->dpy, gi.op,
+				       gi.ref.src,
+				       gi.ref.tt.picture,
+				       gi.ref.mask_format,
+				       0, 0,
+				       0, 8,
+				       &elt, 1);
+		test_compare(t,
+			     gi.out.tt.draw, gi.out.tt.format,
+			     gi.ref.tt.draw, gi.ref.tt.format,
+			     0, 0, gi.out.tt.width, gi.out.tt.height,
+			     glyph_iter_to_string(&gi, "all"));
+	}
+	glyph_iter_fini(&gi);
+}
+
+int main(int argc, char **argv)
+{
+	struct test test;
+	int t;
+
+	test_init(&test, argc, argv);
+	XSetErrorHandler(_check_error_handler);
+
+	for (t = TARGET_FIRST; t <= TARGET_LAST; t++) {
+		single(&test, t);
+		//overlapping(&test, t);
+		//gap(&test, t);
+		//mixed(&test, t);
+	}
+
+	return 0;
+}
diff --git a/test/test.h b/test/test.h
index a3ef979..9eec1cf 100644
--- a/test/test.h
+++ b/test/test.h
@@ -107,6 +107,15 @@ static inline uint32_t color(uint8_t red, uint8_t green, uint8_t blue, uint8_t a
 	return alpha << 24 | ra >> 8 << 16 | ga >> 8 << 8 | ba >> 8;
 }
 
+static inline uint32_t xrender_color(const XRenderColor *c)
+{
+	uint32_t ra = c->red * c->alpha;
+	uint32_t ga = c->green * c->alpha;
+	uint32_t ba = c->blue * c->alpha;
+
+	return c->alpha >> 8 << 24 | ra >> 24 << 16 | ga >> 24 << 8 | ba >> 24;
+}
+
 void test_timer_start(struct test_display *t, struct timespec *tv);
 double test_timer_stop(struct test_display *t, struct timespec *tv);
 


More information about the xorg-commit mailing list