summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Wilson <chris@chris-wilson.co.uk>2014-09-24 08:05:01 +0100
committerChris Wilson <chris@chris-wilson.co.uk>2014-09-24 08:10:07 +0100
commit911a0ad8d0520554dc55883997dd59dc3f5f9139 (patch)
treee0fccb6f241e8fee566e3f9aefd880a7cde235d1
parentdbe6d105a60ff28419b549d439bbb29f50b28f08 (diff)
sna/trapezoids: Flesh out alternate rasterisers for tristrips
And undo the accidental commit of commit 4e00cbe35d1a409a02ee27d991213d9a0807e500 Author: Chris Wilson <chris@chris-wilson.co.uk> Date: Mon Sep 22 08:54:57 2014 +0100 traps Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
-rw-r--r--src/sna/sna_trapezoids.h32
-rw-r--r--src/sna/sna_trapezoids_imprecise.c23
-rw-r--r--src/sna/sna_trapezoids_mono.c159
-rw-r--r--src/sna/sna_trapezoids_precise.c344
4 files changed, 533 insertions, 25 deletions
diff --git a/src/sna/sna_trapezoids.h b/src/sna/sna_trapezoids.h
index 5acf5317..e8f7f3f4 100644
--- a/src/sna/sna_trapezoids.h
+++ b/src/sna/sna_trapezoids.h
@@ -227,10 +227,37 @@ triangles_mask_converter(CARD8 op, PicturePtr src, PicturePtr dst,
int count, xTriangle *tri);
bool
+mono_tristrip_span_converter(struct sna *sna,
+ CARD8 op, PicturePtr src, PicturePtr dst,
+ INT16 src_x, INT16 src_y,
+ int count, xPointFixed *points);
+bool
+imprecise_tristrip_span_converter(struct sna *sna,
+ CARD8 op, PicturePtr src, PicturePtr dst,
+ PictFormatPtr maskFormat, INT16 src_x, INT16 src_y,
+ int count, xPointFixed *points);
+bool
+precise_tristrip_span_converter(struct sna *sna,
+ CARD8 op, PicturePtr src, PicturePtr dst,
+ PictFormatPtr maskFormat, INT16 src_x, INT16 src_y,
+ int count, xPointFixed *points);
+
+static inline bool
tristrip_span_converter(struct sna *sna,
CARD8 op, PicturePtr src, PicturePtr dst,
PictFormatPtr maskFormat, INT16 src_x, INT16 src_y,
- int count, xPointFixed *points);
+ int count, xPointFixed *points)
+{
+ if (NO_SCAN_CONVERTER)
+ return false;
+
+ if (is_mono(dst, maskFormat))
+ return mono_tristrip_span_converter(sna, op, src, dst, src_x, src_y, count, points);
+ else if (is_precise(dst, maskFormat))
+ return precise_tristrip_span_converter(sna, op, src, dst, maskFormat, src_x, src_y, count, points);
+ else
+ return imprecise_tristrip_span_converter(sna, op, src, dst, maskFormat, src_x, src_y, count, points);
+}
inline static void trapezoid_origin(const xLineFixed *l, int16_t *x, int16_t *y)
{
@@ -332,8 +359,7 @@ xTriangleValid(const xTriangle *t)
static inline int pixman_fixed_to_fast(pixman_fixed_t v)
{
- //return (v + ((1<<(16-FAST_SAMPLES_shift-1))-1)) >> (16 - FAST_SAMPLES_shift);
- return (v + ((1<<(16-FAST_SAMPLES_shift-1)))) >> (16 - FAST_SAMPLES_shift);
+ return (v + ((1<<(16-FAST_SAMPLES_shift-1))-1)) >> (16 - FAST_SAMPLES_shift);
}
bool trapezoids_bounds(int n, const xTrapezoid *t, BoxPtr box);
diff --git a/src/sna/sna_trapezoids_imprecise.c b/src/sna/sna_trapezoids_imprecise.c
index 8bb54265..82306651 100644
--- a/src/sna/sna_trapezoids_imprecise.c
+++ b/src/sna/sna_trapezoids_imprecise.c
@@ -42,7 +42,7 @@
#undef SAMPLES_X
#undef SAMPLES_Y
-#if 1
+#if 0
#define __DBG DBG
#else
#define __DBG(x)
@@ -633,6 +633,7 @@ polygon_add_line(struct polygon *polygon,
unsigned ix = EDGE_Y_BUCKET_INDEX(e->ytop,
polygon->ymin);
polygon->y_buckets[ix] = prev->next;
+ polygon->num_edges--;
return;
}
}
@@ -1940,7 +1941,7 @@ imprecise_trapezoid_span_converter(struct sna *sna,
dy *= FAST_SAMPLES_Y;
num_threads = 1;
- if (!NO_GPU_THREADS && 0 &&
+ if (!NO_GPU_THREADS &&
(flags & COMPOSITE_SPANS_RECTILINEAR) == 0 &&
tmp.thread_boxes &&
thread_choose_span(&tmp, dst, maskFormat, &clip))
@@ -3876,10 +3877,10 @@ tristrip_thread(void *arg)
}
bool
-tristrip_span_converter(struct sna *sna,
- CARD8 op, PicturePtr src, PicturePtr dst,
- PictFormatPtr maskFormat, INT16 src_x, INT16 src_y,
- int count, xPointFixed *points)
+imprecise_tristrip_span_converter(struct sna *sna,
+ CARD8 op, PicturePtr src, PicturePtr dst,
+ PictFormatPtr maskFormat, INT16 src_x, INT16 src_y,
+ int count, xPointFixed *points)
{
struct sna_composite_spans_op tmp;
BoxRec extents;
@@ -3888,16 +3889,6 @@ tristrip_span_converter(struct sna *sna,
int dx, dy, num_threads;
bool was_clear;
- if (NO_SCAN_CONVERTER)
- return false;
-
- /* XXX strict adherence to the Render specification */
- if (is_precise(dst, maskFormat)) {
- DBG(("%s: fallback -- precise rasterisation requested\n",
- __FUNCTION__));
- return false;
- }
-
if (!sna->render.check_composite_spans(sna, op, src, dst, 0, 0, 0)) {
DBG(("%s: fallback -- composite spans not supported\n",
__FUNCTION__));
diff --git a/src/sna/sna_trapezoids_mono.c b/src/sna/sna_trapezoids_mono.c
index acd2b101..d53e5359 100644
--- a/src/sna/sna_trapezoids_mono.c
+++ b/src/sna/sna_trapezoids_mono.c
@@ -356,7 +356,7 @@ static struct mono_edge *mono_filter(struct mono_edge *edges)
struct mono_edge *e;
e = edges;
- do {
+ while (e->next) {
struct mono_edge *n = e->next;
if (e->dir == -n->dir &&
e->height_left == n->height_left &&
@@ -373,8 +373,8 @@ static struct mono_edge *mono_filter(struct mono_edge *edges)
e = n->next;
} else
- e = e->next;
- } while (e->next);
+ e = n;
+ }
return edges;
}
@@ -1425,3 +1425,156 @@ mono_triangles_span_converter(struct sna *sna,
REGION_UNINIT(NULL, &mono.clip);
return true;
}
+
+bool
+mono_tristrip_span_converter(struct sna *sna,
+ CARD8 op, PicturePtr src, PicturePtr dst,
+ INT16 src_x, INT16 src_y,
+ int count, xPointFixed *points)
+{
+ struct mono mono;
+ BoxRec extents;
+ int16_t dst_x, dst_y;
+ int16_t dx, dy;
+ bool was_clear;
+ int n;
+
+ mono.sna = sna;
+
+ dst_x = pixman_fixed_to_int(points[0].x);
+ dst_y = pixman_fixed_to_int(points[0].y);
+
+ miPointFixedBounds(count, points, &extents);
+ DBG(("%s: extents (%d, %d), (%d, %d)\n",
+ __FUNCTION__, extents.x1, extents.y1, extents.x2, extents.y2));
+
+ if (extents.y1 >= extents.y2 || extents.x1 >= extents.x2)
+ return true;
+
+ if (!sna_compute_composite_region(&mono.clip,
+ src, NULL, dst,
+ src_x + extents.x1 - dst_x,
+ src_y + extents.y1 - dst_y,
+ 0, 0,
+ extents.x1, extents.y1,
+ extents.x2 - extents.x1,
+ extents.y2 - extents.y1)) {
+ DBG(("%s: triangles do not intersect drawable clips\n",
+ __FUNCTION__)) ;
+ return true;
+ }
+
+ dx = dst->pDrawable->x;
+ dy = dst->pDrawable->y;
+
+ DBG(("%s: after clip -- extents (%d, %d), (%d, %d), delta=(%d, %d) src -> (%d, %d)\n",
+ __FUNCTION__,
+ mono.clip.extents.x1, mono.clip.extents.y1,
+ mono.clip.extents.x2, mono.clip.extents.y2,
+ dx, dy,
+ src_x + mono.clip.extents.x1 - dst_x - dx,
+ src_y + mono.clip.extents.y1 - dst_y - dy));
+
+ was_clear = sna_drawable_is_clear(dst->pDrawable);
+
+ if (!mono_init(&mono, 2*count))
+ return false;
+
+ mono_add_line(&mono, dx, dy,
+ points[0].y, points[1].y,
+ &points[0], &points[1], -1);
+ n = 2;
+ do {
+ mono_add_line(&mono, dx, dy,
+ points[n-2].y, points[n].y,
+ &points[n-2], &points[n], 1);
+ if (++n == count)
+ break;
+
+ mono_add_line(&mono, dx, dy,
+ points[n-2].y, points[n].y,
+ &points[n-2], &points[n], -1);
+ if (++n == count)
+ break;
+ } while (1);
+ mono_add_line(&mono, dx, dy,
+ points[n-2].y, points[n-1].y,
+ &points[n-2], &points[n-1], 1);
+
+ if (mono.sna->render.composite(mono.sna, op, src, NULL, dst,
+ src_x + mono.clip.extents.x1 - dst_x - dx,
+ src_y + mono.clip.extents.y1 - dst_y - dy,
+ 0, 0,
+ mono.clip.extents.x1, mono.clip.extents.y1,
+ mono.clip.extents.x2 - mono.clip.extents.x1,
+ mono.clip.extents.y2 - mono.clip.extents.y1,
+ COMPOSITE_PARTIAL, memset(&mono.op, 0, sizeof(mono.op)))) {
+ if (mono.clip.data == NULL && mono.op.damage == NULL)
+ mono.span = mono_span__fast;
+ else
+ mono.span = mono_span;
+ mono_render(&mono);
+ mono.op.done(mono.sna, &mono.op);
+ }
+
+ if (!was_clear && !operator_is_bounded(op)) {
+ xPointFixed p1, p2;
+
+ if (!mono_init(&mono, 2+2*count))
+ return false;
+
+ p1.y = mono.clip.extents.y1 * pixman_fixed_1;
+ p2.y = mono.clip.extents.y2 * pixman_fixed_1;
+
+ p1.x = mono.clip.extents.x1 * pixman_fixed_1;
+ p2.x = mono.clip.extents.x1 * pixman_fixed_1;
+ mono_add_line(&mono, 0, 0, p1.y, p2.y, &p1, &p2, -1);
+
+ p1.x = mono.clip.extents.x2 * pixman_fixed_1;
+ p2.x = mono.clip.extents.x2 * pixman_fixed_1;
+ mono_add_line(&mono, 0, 0, p1.y, p2.y, &p1, &p2, 1);
+
+ mono_add_line(&mono, dx, dy,
+ points[0].y, points[1].y,
+ &points[0], &points[1], -1);
+ n = 2;
+ do {
+ mono_add_line(&mono, dx, dy,
+ points[n-2].y, points[n].y,
+ &points[n-2], &points[n], 1);
+ if (++n == count)
+ break;
+
+ mono_add_line(&mono, dx, dy,
+ points[n-2].y, points[n].y,
+ &points[n-2], &points[n], -1);
+ if (++n == count)
+ break;
+ } while (1);
+ mono_add_line(&mono, dx, dy,
+ points[n-2].y, points[n-1].y,
+ &points[n-2], &points[n-1], 1);
+
+ if (mono.sna->render.composite(mono.sna,
+ PictOpClear,
+ mono.sna->clear, NULL, dst,
+ 0, 0,
+ 0, 0,
+ mono.clip.extents.x1, mono.clip.extents.y1,
+ mono.clip.extents.x2 - mono.clip.extents.x1,
+ mono.clip.extents.y2 - mono.clip.extents.y1,
+ COMPOSITE_PARTIAL, memset(&mono.op, 0, sizeof(mono.op)))) {
+ if (mono.clip.data == NULL && mono.op.damage == NULL)
+ mono.span = mono_span__fast;
+ else
+ mono.span = mono_span;
+ mono_render(&mono);
+ mono.op.done(mono.sna, &mono.op);
+ }
+ 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 99ed087e..af05fe46 100644
--- a/src/sna/sna_trapezoids_precise.c
+++ b/src/sna/sna_trapezoids_precise.c
@@ -663,7 +663,7 @@ static struct edge *filter(struct edge *edges)
struct edge *e;
e = edges;
- do {
+ while (e->next) {
struct edge *n = e->next;
if (e->dir == -n->dir &&
e->height_left == n->height_left &&
@@ -680,8 +680,8 @@ static struct edge *filter(struct edge *edges)
e = n->next;
} else
- e = e->next;
- } while (e->next);
+ e = n;
+ }
return edges;
}
@@ -3238,3 +3238,341 @@ precise_trapezoid_span_fallback(CARD8 op, PicturePtr src, PicturePtr dst,
return true;
}
+
+static inline void
+project_point_onto_grid(const xPointFixed *in,
+ int dx, int dy,
+ xPointFixed *out)
+{
+ out->x = dx + pixman_fixed_to_grid_x(in->x);
+ out->y = dy + pixman_fixed_to_grid_y(in->y);
+ __DBG(("%s: (%f, %f) -> (%d, %d)\n",
+ __FUNCTION__,
+ pixman_fixed_to_double(in->x),
+ pixman_fixed_to_double(in->y),
+ out->x, out->y));
+}
+
+inline static void
+polygon_add_line(struct polygon *polygon,
+ const xPointFixed *p1,
+ const xPointFixed *p2)
+{
+ struct edge *e = &polygon->edges[polygon->num_edges];
+ int dx = p2->x - p1->x;
+ int dy = p2->y - p1->y;
+ int top, bot;
+
+ if (dy == 0)
+ return;
+
+ __DBG(("%s: line=(%d, %d), (%d, %d)\n",
+ __FUNCTION__, (int)p1->x, (int)p1->y, (int)p2->x, (int)p2->y));
+
+ e->dir = 1;
+ if (dy < 0) {
+ const xPointFixed *t;
+
+ dx = -dx;
+ dy = -dy;
+
+ e->dir = -1;
+
+ t = p1;
+ p1 = p2;
+ p2 = t;
+ }
+ assert (dy > 0);
+ e->dy = dy;
+
+ top = MAX(p1->y, polygon->ymin);
+ bot = MIN(p2->y, polygon->ymax);
+ if (bot <= top)
+ return;
+
+ e->ytop = top;
+ e->height_left = bot - top;
+ if (e->height_left <= 0)
+ return;
+
+ __DBG(("%s: edge height=%d\n", __FUNCTION__, e->dir * e->height_left));
+
+ if (dx == 0) {
+ e->x.quo = p1->x;
+ e->x.rem = -dy;
+ e->dxdy.quo = 0;
+ e->dxdy.rem = 0;
+ e->dy = 0;
+ } else {
+ e->dxdy = floored_divrem(dx, dy);
+ if (top == p1->y) {
+ e->x.quo = p1->x;
+ e->x.rem = -dy;
+ } else {
+ e->x = floored_muldivrem(top - p1->y, dx, dy);
+ e->x.quo += p1->x;
+ e->x.rem -= dy;
+ }
+ }
+
+ if (polygon->num_edges > 0) {
+ struct edge *prev = &polygon->edges[polygon->num_edges-1];
+ /* detect degenerate triangles inserted into tristrips */
+ if (e->dir == -prev->dir &&
+ e->ytop == prev->ytop &&
+ e->height_left == prev->height_left &&
+ e->x.quo == prev->x.quo &&
+ e->x.rem == prev->x.rem &&
+ e->dxdy.quo == prev->dxdy.quo &&
+ e->dxdy.rem == prev->dxdy.rem) {
+ unsigned ix = EDGE_Y_BUCKET_INDEX(e->ytop,
+ polygon->ymin);
+ polygon->y_buckets[ix] = prev->next;
+ polygon->num_edges--;
+ return;
+ }
+ }
+
+ _polygon_insert_edge_into_its_y_bucket(polygon, e);
+ polygon->num_edges++;
+}
+
+struct tristrip_thread {
+ struct sna *sna;
+ const struct sna_composite_spans_op *op;
+ const xPointFixed *points;
+ RegionPtr clip;
+ span_func_t span;
+ BoxRec extents;
+ int dx, dy, draw_y;
+ int count;
+ bool unbounded;
+};
+
+static void
+tristrip_thread(void *arg)
+{
+ struct tristrip_thread *thread = arg;
+ struct span_thread_boxes boxes;
+ struct tor tor;
+ xPointFixed p[4];
+ int n, cw, ccw;
+
+ if (!tor_init(&tor, &thread->extents, 2*thread->count))
+ return;
+
+ boxes.op = thread->op;
+ boxes.num_boxes = 0;
+
+ cw = ccw = 0;
+ project_point_onto_grid(&thread->points[0], thread->dx, thread->dy, &p[cw]);
+ project_point_onto_grid(&thread->points[1], thread->dx, thread->dy, &p[2+ccw]);
+ polygon_add_line(tor.polygon, &p[2+ccw], &p[cw]);
+ n = 2;
+ do {
+ cw = !cw;
+ project_point_onto_grid(&thread->points[n], thread->dx, thread->dy, &p[cw]);
+ polygon_add_line(tor.polygon, &p[!cw], &p[cw]);
+ if (++n == thread->count)
+ break;
+
+ ccw = !ccw;
+ project_point_onto_grid(&thread->points[n], thread->dx, thread->dy, &p[2+ccw]);
+ polygon_add_line(tor.polygon, &p[2+ccw], &p[2+!ccw]);
+ if (++n == thread->count)
+ break;
+ } while (1);
+ polygon_add_line(tor.polygon, &p[cw], &p[2+ccw]);
+ assert(tor.polygon->num_edges <= 2*thread->count);
+
+ tor_render(thread->sna, &tor,
+ (struct sna_composite_spans_op *)&boxes, thread->clip,
+ thread->span, thread->unbounded);
+
+ tor_fini(&tor);
+
+ if (boxes.num_boxes) {
+ DBG(("%s: flushing %d boxes\n", __FUNCTION__, boxes.num_boxes));
+ assert(boxes.num_boxes <= SPAN_THREAD_MAX_BOXES);
+ thread->op->thread_boxes(thread->sna, thread->op,
+ boxes.boxes, boxes.num_boxes);
+ }
+}
+
+bool
+precise_tristrip_span_converter(struct sna *sna,
+ CARD8 op, PicturePtr src, PicturePtr dst,
+ PictFormatPtr maskFormat, INT16 src_x, INT16 src_y,
+ int count, xPointFixed *points)
+{
+ struct sna_composite_spans_op tmp;
+ BoxRec extents;
+ pixman_region16_t clip;
+ int16_t dst_x, dst_y;
+ int dx, dy, num_threads;
+ bool was_clear;
+
+ if (!sna->render.check_composite_spans(sna, op, src, dst, 0, 0, 0)) {
+ DBG(("%s: fallback -- composite spans not supported\n",
+ __FUNCTION__));
+ return false;
+ }
+
+ dst_x = pixman_fixed_to_int(points[0].x);
+ dst_y = pixman_fixed_to_int(points[0].y);
+
+ miPointFixedBounds(count, points, &extents);
+ DBG(("%s: extents (%d, %d), (%d, %d)\n",
+ __FUNCTION__, extents.x1, extents.y1, extents.x2, extents.y2));
+
+ if (extents.y1 >= extents.y2 || extents.x1 >= extents.x2)
+ return true;
+
+#if 0
+ if (extents.y2 - extents.y1 < 64 && extents.x2 - extents.x1 < 64) {
+ DBG(("%s: fallback -- traps extents too small %dx%d\n",
+ __FUNCTION__, extents.y2 - extents.y1, extents.x2 - extents.x1));
+ return false;
+ }
+#endif
+
+ if (!sna_compute_composite_region(&clip,
+ src, NULL, dst,
+ src_x + extents.x1 - dst_x,
+ src_y + extents.y1 - dst_y,
+ 0, 0,
+ extents.x1, extents.y1,
+ extents.x2 - extents.x1,
+ extents.y2 - extents.y1)) {
+ DBG(("%s: triangles do not intersect drawable clips\n",
+ __FUNCTION__)) ;
+ return true;
+ }
+
+ if (!sna->render.check_composite_spans(sna, op, src, dst,
+ clip.extents.x2 - clip.extents.x1,
+ clip.extents.y2 - clip.extents.y1,
+ 0)) {
+ DBG(("%s: fallback -- composite spans not supported\n",
+ __FUNCTION__));
+ return false;
+ }
+
+ extents = *RegionExtents(&clip);
+ dx = dst->pDrawable->x;
+ dy = dst->pDrawable->y;
+
+ DBG(("%s: after clip -- extents (%d, %d), (%d, %d), delta=(%d, %d) src -> (%d, %d)\n",
+ __FUNCTION__,
+ extents.x1, extents.y1,
+ extents.x2, extents.y2,
+ dx, dy,
+ src_x + extents.x1 - dst_x - dx,
+ src_y + extents.y1 - dst_y - dy));
+
+ was_clear = sna_drawable_is_clear(dst->pDrawable);
+
+ memset(&tmp, 0, sizeof(tmp));
+ if (!sna->render.composite_spans(sna, op, src, dst,
+ src_x + extents.x1 - dst_x - dx,
+ src_y + extents.y1 - dst_y - dy,
+ extents.x1, extents.y1,
+ extents.x2 - extents.x1,
+ extents.y2 - extents.y1,
+ 0,
+ &tmp)) {
+ DBG(("%s: fallback -- composite spans render op not supported\n",
+ __FUNCTION__));
+ return false;
+ }
+
+ dx *= SAMPLES_X;
+ dy *= SAMPLES_Y;
+
+ num_threads = 1;
+ if (!NO_GPU_THREADS && 0 &&
+ tmp.thread_boxes &&
+ thread_choose_span(&tmp, dst, maskFormat, &clip))
+ num_threads = sna_use_threads(extents.x2 - extents.x1,
+ extents.y2 - extents.y1,
+ 16);
+ if (num_threads == 1) {
+ struct tor tor;
+ xPointFixed p[4];
+ int cw, ccw, n;
+
+ if (!tor_init(&tor, &extents, 2*count))
+ goto skip;
+
+ cw = ccw = 0;
+ project_point_onto_grid(&points[0], dx, dy, &p[cw]);
+ project_point_onto_grid(&points[1], dx, dy, &p[2+ccw]);
+ polygon_add_line(tor.polygon, &p[2+ccw], &p[cw]);
+ n = 2;
+ do {
+ cw = !cw;
+ project_point_onto_grid(&points[n], dx, dy, &p[cw]);
+ polygon_add_line(tor.polygon, &p[!cw], &p[cw]);
+ if (++n == count)
+ break;
+
+ ccw = !ccw;
+ project_point_onto_grid(&points[n], dx, dy, &p[2+ccw]);
+ polygon_add_line(tor.polygon, &p[2+ccw], &p[2+!ccw]);
+ if (++n == count)
+ break;
+ } while (1);
+ polygon_add_line(tor.polygon, &p[cw], &p[2+ccw]);
+ assert(tor.polygon->num_edges <= 2*count);
+
+ tor_render(sna, &tor, &tmp, &clip,
+ choose_span(&tmp, dst, maskFormat, &clip),
+ !was_clear && maskFormat && !operator_is_bounded(op));
+
+ tor_fini(&tor);
+ } else {
+ struct tristrip_thread threads[num_threads];
+ int y, h, n;
+
+ DBG(("%s: using %d threads for tristrip compositing %dx%d\n",
+ __FUNCTION__, num_threads,
+ clip.extents.x2 - clip.extents.x1,
+ clip.extents.y2 - clip.extents.y1));
+
+ threads[0].sna = sna;
+ threads[0].op = &tmp;
+ threads[0].points = points;
+ threads[0].count = count;
+ threads[0].extents = clip.extents;
+ threads[0].clip = &clip;
+ threads[0].dx = dx;
+ threads[0].dy = dy;
+ threads[0].draw_y = dst->pDrawable->y;
+ threads[0].unbounded = !was_clear && maskFormat && !operator_is_bounded(op);
+ threads[0].span = thread_choose_span(&tmp, dst, maskFormat, &clip);
+
+ y = clip.extents.y1;
+ h = clip.extents.y2 - clip.extents.y1;
+ h = (h + num_threads - 1) / num_threads;
+ num_threads -= (num_threads-1) * h >= clip.extents.y2 - clip.extents.y1;
+
+ for (n = 1; n < num_threads; n++) {
+ threads[n] = threads[0];
+ threads[n].extents.y1 = y;
+ threads[n].extents.y2 = y += h;
+
+ sna_threads_run(n, tristrip_thread, &threads[n]);
+ }
+
+ assert(y < threads[0].extents.y2);
+ threads[0].extents.y1 = y;
+ tristrip_thread(&threads[0]);
+
+ sna_threads_wait();
+ }
+skip:
+ tmp.done(sna, &tmp);
+
+ REGION_UNINIT(NULL, &clip);
+ return true;
+}