1/*
2 * Copyright (c) 2011 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 *
23 * Authors:
24 *    Chris Wilson <chris@chris-wilson.co.uk>
25 *
26 */
27
28#ifdef HAVE_CONFIG_H
29#include "config.h"
30#endif
31
32#include "sna.h"
33#include "sna_render.h"
34#include "sna_render_inline.h"
35#include "fb/fbpict.h"
36
37#include <mipict.h>
38
39#define NO_COMPOSITE 0
40#define NO_COMPOSITE_RECTANGLES 0
41
42#define BOUND(v)	(INT16) ((v) < MINSHORT ? MINSHORT : (v) > MAXSHORT ? MAXSHORT : (v))
43
44bool sna_composite_create(struct sna *sna)
45{
46	xRenderColor color = { 0 };
47	int error;
48
49	sna->clear = CreateSolidPicture(0, &color, &error);
50	return sna->clear != NULL;
51}
52
53void sna_composite_close(struct sna *sna)
54{
55	DBG(("%s\n", __FUNCTION__));
56
57	if (sna->clear) {
58		FreePicture(sna->clear, 0);
59		sna->clear = NULL;
60	}
61}
62
63static inline bool
64region_is_singular(pixman_region16_t *region)
65{
66	return region->data == NULL;
67}
68
69static inline bool
70region_is_empty(pixman_region16_t *region)
71{
72	return region->data && region->data->numRects == 0;
73}
74
75static inline pixman_bool_t
76clip_to_dst(pixman_region16_t *region,
77	    pixman_region16_t *clip,
78	    int		dx,
79	    int		dy)
80{
81	DBG(("%s: region: %dx[(%d, %d), (%d, %d)], clip: %dx[(%d, %d), (%d, %d)]\n",
82	     __FUNCTION__,
83	     pixman_region_n_rects(region),
84	     region->extents.x1, region->extents.y1,
85	     region->extents.x2, region->extents.y2,
86	     pixman_region_n_rects(clip),
87	     clip->extents.x1, clip->extents.y1,
88	     clip->extents.x2, clip->extents.y2));
89
90	if (region_is_singular(region) && region_is_singular(clip)) {
91		pixman_box16_t *r = &region->extents;
92		pixman_box16_t *c = &clip->extents;
93		int v;
94
95		if (r->x1 < (v = c->x1 + dx))
96			r->x1 = BOUND(v);
97		if (r->x2 > (v = c->x2 + dx))
98			r->x2 = BOUND(v);
99		if (r->y1 < (v = c->y1 + dy))
100			r->y1 = BOUND(v);
101		if (r->y2 > (v = c->y2 + dy))
102			r->y2 = BOUND(v);
103
104		if (r->x1 >= r->x2 || r->y1 >= r->y2) {
105			pixman_region_init(region);
106			return FALSE;
107		}
108
109		return true;
110	} else if (region_is_empty(clip)) {
111		return FALSE;
112	} else {
113		if (dx | dy)
114			pixman_region_translate(region, -dx, -dy);
115		if (!pixman_region_intersect(region, region, clip))
116			return FALSE;
117		if (dx | dy)
118			pixman_region_translate(region, dx, dy);
119
120		return !region_is_empty(region);
121	}
122}
123
124static inline bool
125picture_has_clip(PicturePtr p)
126{
127#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,16,99,1,0)
128	return p->clientClip;
129#else
130	return p->clientClipType != CT_NONE;
131#endif
132}
133
134static inline bool
135clip_to_src(RegionPtr region, PicturePtr p, int dx, int dy)
136{
137	bool result;
138
139	if (!picture_has_clip(p))
140		return true;
141
142	pixman_region_translate(p->clientClip,
143				p->clipOrigin.x + dx,
144				p->clipOrigin.y + dy);
145
146	result = RegionIntersect(region, region, p->clientClip);
147
148	pixman_region_translate(p->clientClip,
149				-(p->clipOrigin.x + dx),
150				-(p->clipOrigin.y + dy));
151
152	return result && !region_is_empty(region);
153}
154
155bool
156sna_compute_composite_region(RegionPtr region,
157			     PicturePtr src, PicturePtr mask, PicturePtr dst,
158			     INT16 src_x,  INT16 src_y,
159			     INT16 mask_x, INT16 mask_y,
160			     INT16 dst_x,  INT16 dst_y,
161			     CARD16 width, CARD16 height)
162{
163	int v;
164
165	DBG(("%s: dst=(%d, %d)x(%d, %d)\n",
166	     __FUNCTION__,
167	     dst_x, dst_y,
168	     width, height));
169
170	region->extents.x1 = dst_x < 0 ? 0 : dst_x;
171	v = dst_x + width;
172	if (v > dst->pDrawable->width)
173		v = dst->pDrawable->width;
174	region->extents.x2 = v;
175
176	region->extents.y1 = dst_y < 0 ? 0 : dst_y;
177	v = dst_y + height;
178	if (v > dst->pDrawable->height)
179		v = dst->pDrawable->height;
180	region->extents.y2 = v;
181
182	region->data = 0;
183
184	DBG(("%s: initial clip against dst->pDrawable: (%d, %d), (%d, %d)\n",
185	     __FUNCTION__,
186	     region->extents.x1, region->extents.y1,
187	     region->extents.x2, region->extents.y2));
188
189	if (region->extents.x1 >= region->extents.x2 ||
190	    region->extents.y1 >= region->extents.y2)
191		return false;
192
193	region->extents.x1 += dst->pDrawable->x;
194	region->extents.x2 += dst->pDrawable->x;
195	region->extents.y1 += dst->pDrawable->y;
196	region->extents.y2 += dst->pDrawable->y;
197
198	dst_x += dst->pDrawable->x;
199	dst_y += dst->pDrawable->y;
200
201	/* clip against dst */
202	if (!clip_to_dst(region, dst->pCompositeClip, 0, 0))
203		return false;
204
205	DBG(("%s: clip against dst->pCompositeClip: (%d, %d), (%d, %d)\n",
206	     __FUNCTION__,
207	     region->extents.x1, region->extents.y1,
208	     region->extents.x2, region->extents.y2));
209
210	if (dst->alphaMap) {
211		if (!clip_to_dst(region, dst->alphaMap->pCompositeClip,
212				 -dst->alphaOrigin.x,
213				 -dst->alphaOrigin.y)) {
214			pixman_region_fini (region);
215			return false;
216		}
217	}
218
219	/* clip against src */
220	if (src) {
221		if (src->pDrawable) {
222			src_x += src->pDrawable->x;
223			src_y += src->pDrawable->y;
224		}
225		if (!clip_to_src(region, src, dst_x - src_x, dst_y - src_y)) {
226			pixman_region_fini (region);
227			return false;
228		}
229		DBG(("%s: clip against src (%dx%d clip=%d): (%d, %d), (%d, %d)\n",
230		       __FUNCTION__,
231		       src->pDrawable ? src->pDrawable->width : 0,
232		       src->pDrawable ? src->pDrawable->height : 0,
233		       picture_has_clip(src),
234		       region->extents.x1, region->extents.y1,
235		       region->extents.x2, region->extents.y2));
236
237		if (src->alphaMap) {
238			if (!clip_to_src(region, src->alphaMap,
239					 dst_x - (src_x - src->alphaOrigin.x),
240					 dst_y - (src_y - src->alphaOrigin.y))) {
241				pixman_region_fini(region);
242				return false;
243			}
244		}
245	}
246
247	/* clip against mask */
248	if (mask) {
249		if (mask->pDrawable) {
250			mask_x += mask->pDrawable->x;
251			mask_y += mask->pDrawable->y;
252		}
253		if (!clip_to_src(region, mask, dst_x - mask_x, dst_y - mask_y)) {
254			pixman_region_fini(region);
255			return false;
256		}
257		if (mask->alphaMap) {
258			if (!clip_to_src(region, mask->alphaMap,
259					 dst_x - (mask_x - mask->alphaOrigin.x),
260					 dst_y - (mask_y - mask->alphaOrigin.y))) {
261				pixman_region_fini(region);
262				return false;
263			}
264		}
265
266		DBG(("%s: clip against mask: (%d, %d), (%d, %d)\n",
267		     __FUNCTION__,
268		     region->extents.x1, region->extents.y1,
269		     region->extents.x2, region->extents.y2));
270	}
271
272	return !region_is_empty(region);
273}
274
275static void
276trim_extents(BoxPtr extents, const PicturePtr p, int dx, int dy)
277{
278	const BoxPtr box = REGION_EXTENTS(NULL, p->pCompositeClip);
279
280	DBG(("%s: trim((%d, %d), (%d, %d)) against ((%d, %d), (%d, %d)) + (%d, %d)\n",
281	     __FUNCTION__,
282	     extents->x1, extents->y1, extents->x2, extents->y2,
283	     box->x1, box->y1, box->x2, box->y2,
284	     dx, dy));
285
286	if (extents->x1 < box->x1 + dx)
287		extents->x1 = box->x1 + dx;
288	if (extents->x2 > box->x2 + dx)
289		extents->x2 = box->x2 + dx;
290
291	if (extents->y1 < box->y1 + dy)
292		extents->y1 = box->y1 + dy;
293	if (extents->y2 > box->y2 + dy)
294		extents->y2 = box->y2 + dy;
295}
296
297static void
298_trim_source_extents(BoxPtr extents, const PicturePtr p, int dx, int dy)
299{
300	if (picture_has_clip(p))
301		trim_extents(extents, p, dx, dy);
302}
303
304static void
305trim_source_extents(BoxPtr extents, const PicturePtr p, int dx, int dy)
306{
307	if (p->pDrawable) {
308		dx += p->pDrawable->x;
309		dy += p->pDrawable->y;
310	}
311	_trim_source_extents(extents, p, dx, dy);
312	if (p->alphaMap)
313		_trim_source_extents(extents, p->alphaMap,
314				     dx - p->alphaOrigin.x,
315				     dy - p->alphaOrigin.y);
316
317	DBG(("%s: -> (%d, %d), (%d, %d)\n",
318	     __FUNCTION__,
319	     extents->x1, extents->y1,
320	     extents->x2, extents->y2));
321}
322
323bool
324sna_compute_composite_extents(BoxPtr extents,
325			      PicturePtr src, PicturePtr mask, PicturePtr dst,
326			      INT16 src_x,  INT16 src_y,
327			      INT16 mask_x, INT16 mask_y,
328			      INT16 dst_x,  INT16 dst_y,
329			      CARD16 width, CARD16 height)
330{
331	int v;
332
333	DBG(("%s: dst=(%d, %d)x(%d, %d)\n",
334	     __FUNCTION__,
335	     dst_x, dst_y,
336	     width, height));
337
338	extents->x1 = dst_x < 0 ? 0 : dst_x;
339	v = dst_x + width;
340	if (v > dst->pDrawable->width)
341		v = dst->pDrawable->width;
342	extents->x2 = v;
343
344	extents->y1 = dst_y < 0 ? 0 : dst_y;
345	v = dst_y + height;
346	if (v > dst->pDrawable->height)
347		v = dst->pDrawable->height;
348	extents->y2 = v;
349
350	DBG(("%s: initial clip against dst->pDrawable: (%d, %d), (%d, %d)\n",
351	     __FUNCTION__,
352	     extents->x1, extents->y1,
353	     extents->x2, extents->y2));
354
355	if (extents->x1 >= extents->x2 || extents->y1 >= extents->y2)
356		return false;
357
358	extents->x1 += dst->pDrawable->x;
359	extents->x2 += dst->pDrawable->x;
360	extents->y1 += dst->pDrawable->y;
361	extents->y2 += dst->pDrawable->y;
362
363	if (extents->x1 < dst->pCompositeClip->extents.x1)
364		extents->x1 = dst->pCompositeClip->extents.x1;
365	if (extents->x2 > dst->pCompositeClip->extents.x2)
366		extents->x2 = dst->pCompositeClip->extents.x2;
367
368	if (extents->y1 < dst->pCompositeClip->extents.y1)
369		extents->y1 = dst->pCompositeClip->extents.y1;
370	if (extents->y2 > dst->pCompositeClip->extents.y2)
371		extents->y2 = dst->pCompositeClip->extents.y2;
372
373	DBG(("%s: initial clip against dst->pCompositeClip: (%d, %d), (%d, %d)\n",
374	     __FUNCTION__,
375	     extents->x1, extents->y1,
376	     extents->x2, extents->y2));
377
378	if (extents->x1 >= extents->x2 || extents->y1 >= extents->y2)
379		return false;
380
381	dst_x += dst->pDrawable->x;
382	dst_y += dst->pDrawable->y;
383
384	/* clip against dst */
385	trim_extents(extents, dst, 0, 0);
386	if (dst->alphaMap)
387		trim_extents(extents, dst->alphaMap,
388			     -dst->alphaOrigin.x,
389			     -dst->alphaOrigin.y);
390
391	DBG(("%s: clip against dst: (%d, %d), (%d, %d)\n",
392	     __FUNCTION__,
393	     extents->x1, extents->y1,
394	     extents->x2, extents->y2));
395
396	if (src)
397		trim_source_extents(extents, src, dst_x - src_x, dst_y - src_y);
398	if (mask)
399		trim_source_extents(extents, mask,
400				    dst_x - mask_x, dst_y - mask_y);
401
402	if (extents->x1 >= extents->x2 || extents->y1 >= extents->y2)
403		return false;
404
405	if (region_is_singular(dst->pCompositeClip))
406		return true;
407
408	return pixman_region_contains_rectangle(dst->pCompositeClip,
409						extents) != PIXMAN_REGION_OUT;
410}
411
412#if HAS_DEBUG_FULL
413static void _assert_pixmap_contains_box(PixmapPtr pixmap, BoxPtr box, const char *function)
414{
415	if (box->x1 < 0 || box->y1 < 0 ||
416	    box->x2 > pixmap->drawable.width ||
417	    box->y2 > pixmap->drawable.height)
418	{
419		FatalError("%s: damage box is beyond the pixmap: box=(%d, %d), (%d, %d), pixmap=(%d, %d)\n",
420			   function,
421			   box->x1, box->y1, box->x2, box->y2,
422			   pixmap->drawable.width,
423			   pixmap->drawable.height);
424	}
425}
426#define assert_pixmap_contains_box(p, b) _assert_pixmap_contains_box(p, b, __FUNCTION__)
427#else
428#define assert_pixmap_contains_box(p, b)
429#endif
430
431static void apply_damage(struct sna_composite_op *op, RegionPtr region)
432{
433	DBG(("%s: damage=%p, region=%d [(%d, %d), (%d, %d) + (%d, %d)]\n",
434	     __FUNCTION__, op->damage, region_num_rects(region),
435	     region->extents.x1, region->extents.y1,
436	     region->extents.x2, region->extents.y2,
437	     op->dst.x, op->dst.y));
438
439	if (op->damage == NULL)
440		return;
441
442	if (op->dst.x | op->dst.y)
443		RegionTranslate(region, op->dst.x, op->dst.y);
444
445	assert_pixmap_contains_box(op->dst.pixmap, RegionExtents(region));
446	if (region->data == NULL &&
447	    region->extents.x2 - region->extents.x1 == op->dst.width &&
448	    region->extents.y2 - region->extents.y1 == op->dst.height) {
449		*op->damage = _sna_damage_all(*op->damage,
450					      op->dst.width,
451					      op->dst.height);
452		op->damage = NULL;
453	} else
454		sna_damage_add(op->damage, region);
455
456	assert(!op->damage || !DAMAGE_IS_ALL(*op->damage));
457}
458
459static inline bool use_cpu(PixmapPtr pixmap, struct sna_pixmap *priv,
460			   CARD8 op, INT16 width, INT16 height)
461{
462	if (priv->cpu_bo && kgem_bo_is_busy(priv->cpu_bo))
463		return false;
464
465	if (DAMAGE_IS_ALL(priv->cpu_damage) &&
466	    (op > PictOpSrc ||
467	     width  < pixmap->drawable.width ||
468	     height < pixmap->drawable.height))
469		return true;
470
471	if (priv->gpu_bo)
472		return false;
473
474	return (priv->create & KGEM_CAN_CREATE_GPU) == 0;
475}
476
477static void validate_source(PicturePtr picture)
478{
479#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,10,99,901,0)
480	miCompositeSourceValidate(picture);
481#else
482	miCompositeSourceValidate(picture,
483				  0, 0,
484				  picture->pDrawable ? picture->pDrawable->width : 0,
485				  picture->pDrawable ? picture->pDrawable->height : 0);
486#endif
487}
488
489void
490sna_composite_fb(CARD8 op,
491		 PicturePtr src,
492		 PicturePtr mask,
493		 PicturePtr dst,
494		 RegionPtr region,
495		 INT16 src_x, INT16 src_y,
496		 INT16 msk_x, INT16 msk_y,
497		 INT16 dst_x, INT16 dst_y,
498		 CARD16 width, CARD16 height)
499{
500	pixman_image_t *src_image, *mask_image, *dest_image;
501	int src_xoff, src_yoff;
502	int msk_xoff, msk_yoff;
503	int dst_xoff, dst_yoff;
504	int16_t tx, ty;
505	unsigned flags;
506
507	DBG(("%s -- op=%d, fallback dst=(%d, %d)+(%d, %d), size=(%d, %d): region=((%d,%d), (%d, %d))\n",
508	     __FUNCTION__, op,
509	     dst_x, dst_y,
510	     dst->pDrawable->x, dst->pDrawable->y,
511	     width, height,
512	     region->extents.x1, region->extents.y1,
513	     region->extents.x2, region->extents.y2));
514
515	if (src->pDrawable) {
516		DBG(("%s: fallback -- move src to cpu\n", __FUNCTION__));
517		if (!sna_drawable_move_to_cpu(src->pDrawable,
518					      MOVE_READ))
519			return;
520
521		if (src->alphaMap &&
522		    !sna_drawable_move_to_cpu(src->alphaMap->pDrawable,
523					      MOVE_READ))
524			return;
525	}
526
527	validate_source(src);
528
529	if (mask) {
530		if (mask->pDrawable) {
531			DBG(("%s: fallback -- move mask to cpu\n", __FUNCTION__));
532			if (!sna_drawable_move_to_cpu(mask->pDrawable,
533						      MOVE_READ))
534				return;
535
536			if (mask->alphaMap &&
537			    !sna_drawable_move_to_cpu(mask->alphaMap->pDrawable,
538						      MOVE_READ))
539				return;
540		}
541
542		validate_source(mask);
543	}
544
545	DBG(("%s: fallback -- move dst to cpu\n", __FUNCTION__));
546	if (op <= PictOpSrc && !dst->alphaMap)
547		flags = MOVE_WRITE | MOVE_INPLACE_HINT;
548	else
549		flags = MOVE_WRITE | MOVE_READ;
550	if (!sna_drawable_move_region_to_cpu(dst->pDrawable, region, flags))
551		return;
552	if (dst->alphaMap &&
553	    !sna_drawable_move_to_cpu(dst->alphaMap->pDrawable, flags))
554		return;
555
556	if (mask == NULL &&
557	    src->pDrawable &&
558	    dst->pDrawable->bitsPerPixel >= 8 &&
559	    src->filter != PictFilterConvolution &&
560	    (op == PictOpSrc || (op == PictOpOver && !PICT_FORMAT_A(src->format))) &&
561	    (dst->format == src->format || dst->format == alphaless(src->format)) &&
562	    sna_transform_is_imprecise_integer_translation(src->transform, src->filter,
563							   dst->polyMode == PolyModePrecise,
564							   &tx, &ty)) {
565		PixmapPtr dst_pixmap = get_drawable_pixmap(dst->pDrawable);
566		PixmapPtr src_pixmap = get_drawable_pixmap(src->pDrawable);
567		int16_t sx = src_x + tx - (dst->pDrawable->x + dst_x);
568		int16_t sy = src_y + ty - (dst->pDrawable->y + dst_y);
569
570		assert(src->pDrawable->bitsPerPixel == dst->pDrawable->bitsPerPixel);
571		assert(src_pixmap->drawable.bitsPerPixel == dst_pixmap->drawable.bitsPerPixel);
572
573		if (region->extents.x1 + sx >= 0 &&
574		    region->extents.y1 + sy >= 0 &&
575		    region->extents.x2 + sx <= src->pDrawable->width &&
576		    region->extents.y2 + sy <= src->pDrawable->height) {
577			if (sigtrap_get() == 0) {
578				const BoxRec *box = region_rects(region);
579				int nbox = region_num_rects(region);
580
581				sx += src->pDrawable->x;
582				sy += src->pDrawable->y;
583				if (get_drawable_deltas(src->pDrawable, src_pixmap, &tx, &ty))
584					sx += tx, sy += ty;
585
586				assert(region->extents.x1 + sx >= 0);
587				assert(region->extents.x2 + sx <= src_pixmap->drawable.width);
588				assert(region->extents.y1 + sy >= 0);
589				assert(region->extents.y2 + sy <= src_pixmap->drawable.height);
590
591				get_drawable_deltas(dst->pDrawable, dst_pixmap, &tx, &ty);
592
593				assert(nbox);
594				do {
595					assert(box->x1 + sx >= 0);
596					assert(box->x2 + sx <= src_pixmap->drawable.width);
597					assert(box->y1 + sy >= 0);
598					assert(box->y2 + sy <= src_pixmap->drawable.height);
599
600					assert(box->x1 + tx >= 0);
601					assert(box->x2 + tx <= dst_pixmap->drawable.width);
602					assert(box->y1 + ty >= 0);
603					assert(box->y2 + ty <= dst_pixmap->drawable.height);
604
605					assert(box->x2 > box->x1 && box->y2 > box->y1);
606
607					sigtrap_assert_active();
608					memcpy_blt(src_pixmap->devPrivate.ptr,
609						   dst_pixmap->devPrivate.ptr,
610						   dst_pixmap->drawable.bitsPerPixel,
611						   src_pixmap->devKind,
612						   dst_pixmap->devKind,
613						   box->x1 + sx, box->y1 + sy,
614						   box->x1 + tx, box->y1 + ty,
615						   box->x2 - box->x1, box->y2 - box->y1);
616					box++;
617				} while (--nbox);
618				sigtrap_put();
619			}
620
621			return;
622		}
623	}
624
625	src_image = image_from_pict(src, FALSE, &src_xoff, &src_yoff);
626	mask_image = image_from_pict(mask, FALSE, &msk_xoff, &msk_yoff);
627	dest_image = image_from_pict(dst, TRUE, &dst_xoff, &dst_yoff);
628
629	if (src_image && dest_image && !(mask && !mask_image))
630		sna_image_composite(op, src_image, mask_image, dest_image,
631				    src_x + src_xoff, src_y + src_yoff,
632				    msk_x + msk_xoff, msk_y + msk_yoff,
633				    dst_x + dst_xoff, dst_y + dst_yoff,
634				    width, height);
635
636	free_pixman_pict(src, src_image);
637	free_pixman_pict(mask, mask_image);
638	free_pixman_pict(dst, dest_image);
639}
640
641void
642sna_composite(CARD8 op,
643	      PicturePtr src,
644	      PicturePtr mask,
645	      PicturePtr dst,
646	      INT16 src_x,  INT16 src_y,
647	      INT16 mask_x, INT16 mask_y,
648	      INT16 dst_x,  INT16 dst_y,
649	      CARD16 width, CARD16 height)
650{
651	PixmapPtr pixmap = get_drawable_pixmap(dst->pDrawable);
652	struct sna *sna = to_sna_from_pixmap(pixmap);
653	struct sna_pixmap *priv;
654	struct sna_composite_op tmp;
655	RegionRec region;
656	int dx, dy;
657
658	DBG(("%s(pixmap=%ld, op=%d, src=%ld+(%d, %d), mask=%ld+(%d, %d), dst=%ld+(%d, %d)+(%d, %d), size=(%d, %d)\n",
659	     __FUNCTION__,
660	     pixmap->drawable.serialNumber, op,
661	     get_picture_id(src), src_x, src_y,
662	     get_picture_id(mask), mask_x, mask_y,
663	     get_picture_id(dst), dst_x, dst_y,
664	     dst->pDrawable->x, dst->pDrawable->y,
665	     width, height));
666
667	if (region_is_empty(dst->pCompositeClip)) {
668		DBG(("%s: empty clip, skipping\n", __FUNCTION__));
669		return;
670	}
671
672	if (op == PictOpClear) {
673		DBG(("%s: discarding source and mask for clear\n", __FUNCTION__));
674		mask = NULL;
675		if (sna->clear)
676			src = sna->clear;
677	}
678
679	if (!sna_compute_composite_region(&region,
680					  src, mask, dst,
681					  src_x,  src_y,
682					  mask_x, mask_y,
683					  dst_x,  dst_y,
684					  width,  height))
685		return;
686
687	if (mask && sna_composite_mask_is_opaque(mask)) {
688		DBG(("%s: removing opaque %smask\n",
689		     __FUNCTION__,
690		     mask->componentAlpha && PICT_FORMAT_RGB(mask->format) ? "CA " : ""));
691		mask = NULL;
692	}
693
694	if (NO_COMPOSITE)
695		goto fallback;
696
697	if (wedged(sna)) {
698		DBG(("%s: fallback -- wedged\n", __FUNCTION__));
699		goto fallback;
700	}
701
702	if (!can_render_to_picture(dst)) {
703		DBG(("%s: fallback due to unhandled picture\n", __FUNCTION__));
704		goto fallback;
705	}
706
707	priv = sna_pixmap(pixmap);
708	if (priv == NULL) {
709		DBG(("%s: fallback as destination pixmap=%ld is unattached\n",
710		     __FUNCTION__, pixmap->drawable.serialNumber));
711		goto fallback;
712	}
713
714	if (use_cpu(pixmap, priv, op, width, height) &&
715	    !picture_is_gpu(sna, src, PREFER_GPU_RENDER) &&
716	    !picture_is_gpu(sna, mask, PREFER_GPU_RENDER)) {
717		DBG(("%s: fallback, dst pixmap=%ld is too small (or completely damaged)\n",
718		     __FUNCTION__, pixmap->drawable.serialNumber));
719		goto fallback;
720	}
721
722	dx = region.extents.x1 - (dst_x + dst->pDrawable->x);
723	dy = region.extents.y1 - (dst_y + dst->pDrawable->y);
724
725	DBG(("%s: composite region extents:+(%d, %d) -> (%d, %d), (%d, %d) + (%d, %d)\n",
726	     __FUNCTION__,
727	     dx, dy,
728	     region.extents.x1, region.extents.y1,
729	     region.extents.x2, region.extents.y2,
730	     get_drawable_dx(dst->pDrawable),
731	     get_drawable_dy(dst->pDrawable)));
732
733	if (op <= PictOpSrc && priv->cpu_damage) {
734		int16_t x, y;
735
736		if (get_drawable_deltas(dst->pDrawable, pixmap, &x, &y))
737			pixman_region_translate(&region, x, y);
738
739		sna_damage_subtract(&priv->cpu_damage, &region);
740		if (priv->cpu_damage == NULL) {
741			list_del(&priv->flush_list);
742			priv->cpu = false;
743		}
744
745		if (x|y)
746			pixman_region_translate(&region, -x, -y);
747	}
748
749	if (!sna->render.composite(sna,
750				   op, src, mask, dst,
751				   src_x + dx,  src_y + dy,
752				   mask_x + dx, mask_y + dy,
753				   region.extents.x1,
754				   region.extents.y1,
755				   region.extents.x2 - region.extents.x1,
756				   region.extents.y2 - region.extents.y1,
757				   region.data ? COMPOSITE_PARTIAL : 0,
758				   memset(&tmp, 0, sizeof(tmp)))) {
759		DBG(("%s: fallback due unhandled composite op\n", __FUNCTION__));
760		goto fallback;
761	}
762	assert(!tmp.damage || !DAMAGE_IS_ALL(*tmp.damage));
763
764	if (region.data == NULL)
765		tmp.box(sna, &tmp, &region.extents);
766	else
767		tmp.boxes(sna, &tmp,
768			  RegionBoxptr(&region),
769			  region_num_rects(&region));
770	apply_damage(&tmp, &region);
771	tmp.done(sna, &tmp);
772
773	goto out;
774
775fallback:
776	DBG(("%s: fallback -- fbComposite\n", __FUNCTION__));
777	sna_composite_fb(op, src, mask, dst, &region,
778			 src_x,  src_y,
779			 mask_x, mask_y,
780			 dst_x,  dst_y,
781			 width,  height);
782out:
783	REGION_UNINIT(NULL, &region);
784}
785
786void
787sna_composite_rectangles(CARD8		 op,
788			 PicturePtr	 dst,
789			 xRenderColor	*color,
790			 int		 num_rects,
791			 xRectangle	*rects)
792{
793	struct sna *sna = to_sna_from_drawable(dst->pDrawable);
794	PixmapPtr pixmap;
795	struct sna_pixmap *priv;
796	struct kgem_bo *bo;
797	struct sna_damage **damage;
798	pixman_region16_t region;
799	pixman_box16_t stack_boxes[64], *boxes = stack_boxes, *b;
800	int16_t dst_x, dst_y;
801	int i, num_boxes;
802	unsigned hint;
803
804	DBG(("%s(pixmap=%ld, op=%d, %08x x %d [(%d, %d)x(%d, %d) ...])\n",
805	     __FUNCTION__,
806	     get_drawable_pixmap(dst->pDrawable)->drawable.serialNumber,
807	     op,
808	     (color->alpha >> 8 << 24) |
809	     (color->red   >> 8 << 16) |
810	     (color->green >> 8 << 8) |
811	     (color->blue  >> 8 << 0),
812	     num_rects,
813	     rects[0].x, rects[0].y, rects[0].width, rects[0].height));
814
815	if (!num_rects)
816		return;
817
818	if (region_is_empty(dst->pCompositeClip)) {
819		DBG(("%s: empty clip, skipping\n", __FUNCTION__));
820		return;
821	}
822
823	if (color->alpha <= 0x00ff) {
824		if (PICT_FORMAT_TYPE(dst->format) == PICT_TYPE_A ||
825		    (color->red|color->green|color->blue) <= 0x00ff) {
826			switch (op) {
827			case PictOpOver:
828			case PictOpOutReverse:
829			case PictOpAdd:
830				return;
831			case  PictOpInReverse:
832			case  PictOpSrc:
833				op = PictOpClear;
834				break;
835			case  PictOpAtopReverse:
836				op = PictOpOut;
837				break;
838			case  PictOpXor:
839				op = PictOpOverReverse;
840				break;
841			}
842		} else {
843			switch (op) {
844			case PictOpOver:
845			case PictOpOutReverse:
846				return;
847			case  PictOpInReverse:
848				op = PictOpClear;
849				break;
850			case  PictOpAtopReverse:
851				op = PictOpOut;
852				break;
853			case  PictOpXor:
854				op = PictOpOverReverse;
855				break;
856			}
857		}
858	} else if (color->alpha >= 0xff00) {
859		switch (op) {
860		case PictOpOver:
861			op = PictOpSrc;
862			break;
863		case PictOpInReverse:
864			return;
865		case PictOpOutReverse:
866			op = PictOpClear;
867			break;
868		case  PictOpAtopReverse:
869			op = PictOpOverReverse;
870			break;
871		case  PictOpXor:
872			op = PictOpOut;
873			break;
874		case PictOpAdd:
875			if (PICT_FORMAT_TYPE(dst->format) == PICT_TYPE_A ||
876			    (color->red&color->green&color->blue) >= 0xff00)
877				op = PictOpSrc;
878			break;
879		}
880	}
881
882	/* Avoid reducing overlapping translucent rectangles */
883	if ((op == PictOpOver || op == PictOpAdd) &&
884	    num_rects == 1 &&
885	    sna_drawable_is_clear(dst->pDrawable))
886		op = PictOpSrc;
887
888	DBG(("%s: converted to op %d\n", __FUNCTION__, op));
889
890	if (num_rects > ARRAY_SIZE(stack_boxes)) {
891		boxes = malloc(sizeof(pixman_box16_t) * num_rects);
892		if (boxes == NULL)
893			return;
894	}
895
896	for (i = num_boxes = 0; i < num_rects; i++) {
897		boxes[num_boxes].x1 = rects[i].x + dst->pDrawable->x;
898		if (boxes[num_boxes].x1 < dst->pCompositeClip->extents.x1)
899			boxes[num_boxes].x1 = dst->pCompositeClip->extents.x1;
900
901		boxes[num_boxes].y1 = rects[i].y + dst->pDrawable->y;
902		if (boxes[num_boxes].y1 < dst->pCompositeClip->extents.y1)
903			boxes[num_boxes].y1 = dst->pCompositeClip->extents.y1;
904
905		boxes[num_boxes].x2 = bound(rects[i].x + dst->pDrawable->x, rects[i].width);
906		if (boxes[num_boxes].x2 > dst->pCompositeClip->extents.x2)
907			boxes[num_boxes].x2 = dst->pCompositeClip->extents.x2;
908
909		boxes[num_boxes].y2 = bound(rects[i].y + dst->pDrawable->y, rects[i].height);
910		if (boxes[num_boxes].y2 > dst->pCompositeClip->extents.y2)
911			boxes[num_boxes].y2 = dst->pCompositeClip->extents.y2;
912
913		DBG(("%s[%d] (%d, %d)x(%d, %d) -> (%d, %d), (%d, %d)\n",
914		     __FUNCTION__, i,
915		     rects[i].x, rects[i].y, rects[i].width, rects[i].height,
916		     boxes[num_boxes].x1, boxes[num_boxes].y1, boxes[num_boxes].x2, boxes[num_boxes].y2));
917
918		if (boxes[num_boxes].x2 > boxes[num_boxes].x1 &&
919		    boxes[num_boxes].y2 > boxes[num_boxes].y1)
920			num_boxes++;
921	}
922
923	if (num_boxes == 0)
924		goto cleanup_boxes;
925
926	if (!pixman_region_init_rects(&region, boxes, num_boxes))
927		goto cleanup_boxes;
928
929	DBG(("%s: nrects=%d, region=(%d, %d), (%d, %d) x %d\n",
930	     __FUNCTION__, num_rects,
931	     region.extents.x1, region.extents.y1,
932	     region.extents.x2, region.extents.y2,
933	     num_boxes));
934
935	if (dst->pCompositeClip->data &&
936	    (!pixman_region_intersect(&region, &region, dst->pCompositeClip) ||
937	     region_is_empty(&region))) {
938		DBG(("%s: zero-intersection between rectangles and clip\n",
939		     __FUNCTION__));
940		goto cleanup_region;
941	}
942
943	DBG(("%s: clipped extents (%d, %d),(%d, %d) x %d\n",
944	     __FUNCTION__,
945	     RegionExtents(&region)->x1, RegionExtents(&region)->y1,
946	     RegionExtents(&region)->x2, RegionExtents(&region)->y2,
947	     region_num_rects(&region)));
948
949	/* XXX xserver-1.8: CompositeRects is not tracked by Damage, so we must
950	 * manually append the damaged regions ourselves.
951	 *
952	 * Note that DamageRegionAppend() will apply the drawable-deltas itself.
953	 */
954	DamageRegionAppend(dst->pDrawable, &region);
955
956	pixmap = get_drawable_pixmap(dst->pDrawable);
957	if (get_drawable_deltas(dst->pDrawable, pixmap, &dst_x, &dst_y))
958		pixman_region_translate(&region, dst_x, dst_y);
959
960	DBG(("%s: pixmap +(%d, %d) extents (%d, %d),(%d, %d)\n",
961	     __FUNCTION__, dst_x, dst_y,
962	     RegionExtents(&region)->x1, RegionExtents(&region)->y1,
963	     RegionExtents(&region)->x2, RegionExtents(&region)->y2));
964	assert_pixmap_contains_box(pixmap, RegionExtents(&region));
965
966	if (NO_COMPOSITE_RECTANGLES)
967		goto fallback;
968
969	if (wedged(sna))
970		goto fallback;
971
972	if (!can_render_to_picture(dst)) {
973		DBG(("%s: fallback, dst has an incompatible picture\n", __FUNCTION__));
974		goto fallback;
975	}
976
977	priv = sna_pixmap(pixmap);
978	if (priv == NULL || too_small(priv)) {
979		DBG(("%s: fallback, dst pixmap=%ld too small or not attached\n",
980		     __FUNCTION__, pixmap->drawable.serialNumber));
981		goto fallback;
982	}
983
984	/* If we going to be overwriting any CPU damage with a subsequent
985	 * operation, then we may as well delete it without moving it
986	 * first to the GPU.
987	 */
988	hint = can_render(sna) ? PREFER_GPU : 0;
989	if (op <= PictOpSrc) {
990		if (priv->clear) {
991			uint32_t pixel;
992			bool ok;
993
994			if (op == PictOpClear) {
995				if (priv->clear_color == 0)
996					goto done;
997
998				ok = sna_get_pixel_from_rgba(&pixel,
999							     0, 0, 0, 0,
1000							     dst->format);
1001			} else {
1002				ok = sna_get_pixel_from_rgba(&pixel,
1003							     color->red,
1004							     color->green,
1005							     color->blue,
1006							     color->alpha,
1007							     dst->format);
1008			}
1009			if (ok && priv->clear_color == pixel) {
1010				DBG(("%s: matches current clear, skipping\n",
1011				     __FUNCTION__));
1012				goto done;
1013			}
1014		}
1015
1016		if (region.data == NULL) {
1017			hint |= IGNORE_DAMAGE;
1018			if (region_subsumes_drawable(&region, &pixmap->drawable))
1019				hint |= REPLACES;
1020			if (priv->cpu_damage &&
1021			    (hint & REPLACES ||
1022			     region_subsumes_damage(&region, priv->cpu_damage))) {
1023				DBG(("%s: discarding existing CPU damage\n", __FUNCTION__));
1024				if (priv->gpu_bo && priv->gpu_bo->proxy) {
1025					assert(priv->gpu_damage == NULL);
1026					kgem_bo_destroy(&sna->kgem, priv->gpu_bo);
1027					priv->gpu_bo = NULL;
1028				}
1029				sna_damage_destroy(&priv->cpu_damage);
1030				list_del(&priv->flush_list);
1031			}
1032			if (hint & REPLACES ||
1033			    box_inplace(pixmap, &region.extents)) {
1034				if (priv->gpu_bo && priv->cpu_damage == NULL) {
1035					DBG(("%s: promoting to full GPU\n", __FUNCTION__));
1036					assert(priv->gpu_bo->proxy == NULL);
1037					sna_damage_all(&priv->gpu_damage, pixmap);
1038				}
1039			}
1040		}
1041		if (priv->cpu_damage == NULL) {
1042			DBG(("%s: dropping last-cpu hint\n", __FUNCTION__));
1043			priv->cpu = false;
1044		}
1045	}
1046
1047	bo = sna_drawable_use_bo(&pixmap->drawable, hint,
1048				 &region.extents, &damage);
1049	if (bo == NULL) {
1050		DBG(("%s: fallback due to no GPU bo\n", __FUNCTION__));
1051		goto fallback;
1052	}
1053	if (hint & REPLACES)
1054		kgem_bo_pair_undo(&sna->kgem, priv->gpu_bo, priv->cpu_bo);
1055
1056	if (op <= PictOpSrc) {
1057		b = pixman_region_rectangles(&region, &num_boxes);
1058		if (!sna->render.fill_boxes(sna, op, dst->format, color,
1059					    &pixmap->drawable, bo, b, num_boxes)) {
1060			DBG(("%s: fallback - acceleration failed\n", __FUNCTION__));
1061			goto fallback;
1062		}
1063	} else if (dst->pCompositeClip->data == NULL) {
1064		for (i = 0; i < num_boxes; i++) {
1065			boxes[i].x1 += dst_x;
1066			boxes[i].x2 += dst_x;
1067			boxes[i].y1 += dst_y;
1068			boxes[i].y2 += dst_y;
1069		}
1070		if (!sna->render.fill_boxes(sna, op, dst->format, color,
1071					    &pixmap->drawable, bo, boxes, num_boxes)) {
1072			DBG(("%s: fallback - acceleration failed\n", __FUNCTION__));
1073			goto fallback;
1074		}
1075	} else {
1076		for (i = 0; i < num_boxes; i++) {
1077			RegionRec tmp = { boxes[i] };
1078			if (pixman_region_intersect(&tmp, &tmp, dst->pCompositeClip)) {
1079				int n = 0;
1080
1081				b = pixman_region_rectangles(&tmp, &n);
1082				if (n) {
1083					if (dst_x | dst_y)
1084						pixman_region_translate(&tmp, dst_x, dst_y);
1085
1086					n = !sna->render.fill_boxes(sna, op, dst->format, color,
1087								    &pixmap->drawable, bo, b, n);
1088				}
1089
1090				pixman_region_fini(&tmp);
1091
1092				if (n) {
1093					DBG(("%s: fallback - acceleration failed\n", __FUNCTION__));
1094					goto fallback;
1095				}
1096			}
1097		}
1098	}
1099
1100	if (damage)
1101		sna_damage_add(damage, &region);
1102
1103	/* Clearing a pixmap after creation is a common operation, so take
1104	 * advantage and reduce further damage operations.
1105	 */
1106	if (region_subsumes_drawable(&region, &pixmap->drawable)) {
1107		if (damage) {
1108			sna_damage_all(damage, pixmap);
1109			sna_damage_destroy(damage == &priv->gpu_damage ?
1110					   &priv->cpu_damage : &priv->gpu_damage);
1111		}
1112
1113		if (op <= PictOpSrc && bo == priv->gpu_bo) {
1114			bool ok;
1115
1116			assert(DAMAGE_IS_ALL(priv->gpu_damage));
1117
1118			priv->clear_color = 0;
1119			ok = true;
1120			if (op == PictOpSrc)
1121				ok = sna_get_pixel_from_rgba(&priv->clear_color,
1122							     color->red,
1123							     color->green,
1124							     color->blue,
1125							     color->alpha,
1126							     dst->format);
1127			priv->clear = ok;
1128			DBG(("%s: pixmap=%ld marking clear [%08x]? %d\n",
1129			     __FUNCTION__, pixmap->drawable.serialNumber,
1130			     priv->clear_color, ok));
1131		}
1132	}
1133	goto done;
1134
1135fallback:
1136	DBG(("%s: fallback\n", __FUNCTION__));
1137	if (op <= PictOpSrc)
1138		hint = MOVE_WRITE;
1139	else
1140		hint = MOVE_WRITE | MOVE_READ;
1141	if (!sna_drawable_move_region_to_cpu(&pixmap->drawable, &region, hint))
1142		goto done;
1143
1144	if (dst->alphaMap &&
1145	    !sna_drawable_move_to_cpu(dst->alphaMap->pDrawable, hint))
1146		goto done;
1147
1148	assert(pixmap->devPrivate.ptr);
1149
1150	if (sigtrap_get() == 0) {
1151		if (op <= PictOpSrc) {
1152			int nbox = region_num_rects(&region);
1153			const BoxRec *box = region_rects(&region);
1154			uint32_t pixel;
1155
1156			if (op == PictOpClear)
1157				pixel = 0;
1158			else if (!sna_get_pixel_from_rgba(&pixel,
1159							  color->red,
1160							  color->green,
1161							  color->blue,
1162							  color->alpha,
1163							  dst->format))
1164				goto fallback_composite;
1165
1166			sigtrap_assert_active();
1167			if (pixel == 0 &&
1168			    box->x2 - box->x1 == pixmap->drawable.width &&
1169			    box->y2 - box->y1 == pixmap->drawable.height) {
1170				memset(pixmap->devPrivate.ptr, 0,
1171				       pixmap->devKind*pixmap->drawable.height);
1172			} else do {
1173				DBG(("%s: fallback fill: (%d, %d)x(%d, %d) %08x\n",
1174				     __FUNCTION__,
1175				     box->x1, box->y1,
1176				     box->x2 - box->x1,
1177				     box->y2 - box->y1,
1178				     pixel));
1179
1180				pixman_fill(pixmap->devPrivate.ptr,
1181					    pixmap->devKind/sizeof(uint32_t),
1182					    pixmap->drawable.bitsPerPixel,
1183					    box->x1, box->y1,
1184					    box->x2 - box->x1,
1185					    box->y2 - box->y1,
1186					    pixel);
1187				box++;
1188			} while (--nbox);
1189		} else {
1190			PicturePtr src;
1191			int error;
1192
1193fallback_composite:
1194			DBG(("%s: fallback -- fbComposite()\n", __FUNCTION__));
1195			src = CreateSolidPicture(0, color, &error);
1196			if (src) {
1197				do {
1198					fbComposite(op, src, NULL, dst,
1199						    0, 0,
1200						    0, 0,
1201						    rects->x, rects->y,
1202						    rects->width, rects->height);
1203					rects++;
1204				} while (--num_rects);
1205				FreePicture(src, 0);
1206			}
1207		}
1208		sigtrap_put();
1209	}
1210
1211done:
1212	DamageRegionProcessPending(dst->pDrawable);
1213
1214cleanup_region:
1215	pixman_region_fini(&region);
1216cleanup_boxes:
1217	if (boxes != stack_boxes)
1218		free(boxes);
1219}
1220