test: Add a fidelity test for triangle edge rendering

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
This commit is contained in:
Chris Wilson 2015-05-18 09:58:08 +01:00
parent fe64672c83
commit 3852977f14
6 changed files with 222 additions and 16 deletions

View File

@ -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));

View File

@ -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;
}

View File

@ -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));

1
test/.gitignore vendored
View File

@ -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

View File

@ -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 \

180
test/render-triangle.c Normal file
View File

@ -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;
}