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
457static inline bool use_cpu(PixmapPtr pixmap, struct sna_pixmap *priv,
458			   CARD8 op, INT16 width, INT16 height)
459{
460	if (priv->cpu_bo && kgem_bo_is_busy(priv->cpu_bo))
461		return false;
462
463	if (DAMAGE_IS_ALL(priv->cpu_damage) &&
464	    (op > PictOpSrc ||
465	     width  < pixmap->drawable.width ||
466	     height < pixmap->drawable.height))
467		return true;
468
469	if (priv->gpu_bo)
470		return false;
471
472	return (priv->create & KGEM_CAN_CREATE_GPU) == 0;
473}
474
475static void validate_source(PicturePtr picture)
476{
477#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,10,99,901,0)
478	miCompositeSourceValidate(picture);
479#else
480	miCompositeSourceValidate(picture,
481				  0, 0,
482				  picture->pDrawable ? picture->pDrawable->width : 0,
483				  picture->pDrawable ? picture->pDrawable->height : 0);
484#endif
485}
486
487void
488sna_composite_fb(CARD8 op,
489		 PicturePtr src,
490		 PicturePtr mask,
491		 PicturePtr dst,
492		 RegionPtr region,
493		 INT16 src_x, INT16 src_y,
494		 INT16 msk_x, INT16 msk_y,
495		 INT16 dst_x, INT16 dst_y,
496		 CARD16 width, CARD16 height)
497{
498	pixman_image_t *src_image, *mask_image, *dest_image;
499	int src_xoff, src_yoff;
500	int msk_xoff, msk_yoff;
501	int dst_xoff, dst_yoff;
502	int16_t tx, ty;
503	unsigned flags;
504
505	DBG(("%s -- op=%d, fallback dst=(%d, %d)+(%d, %d), size=(%d, %d): region=((%d,%d), (%d, %d))\n",
506	     __FUNCTION__, op,
507	     dst_x, dst_y,
508	     dst->pDrawable->x, dst->pDrawable->y,
509	     width, height,
510	     region->extents.x1, region->extents.y1,
511	     region->extents.x2, region->extents.y2));
512
513	if (src->pDrawable) {
514		DBG(("%s: fallback -- move src to cpu\n", __FUNCTION__));
515		if (!sna_drawable_move_to_cpu(src->pDrawable,
516					      MOVE_READ))
517			return;
518
519		if (src->alphaMap &&
520		    !sna_drawable_move_to_cpu(src->alphaMap->pDrawable,
521					      MOVE_READ))
522			return;
523	}
524
525	validate_source(src);
526
527	if (mask) {
528		if (mask->pDrawable) {
529			DBG(("%s: fallback -- move mask to cpu\n", __FUNCTION__));
530			if (!sna_drawable_move_to_cpu(mask->pDrawable,
531						      MOVE_READ))
532				return;
533
534			if (mask->alphaMap &&
535			    !sna_drawable_move_to_cpu(mask->alphaMap->pDrawable,
536						      MOVE_READ))
537				return;
538		}
539
540		validate_source(mask);
541	}
542
543	DBG(("%s: fallback -- move dst to cpu\n", __FUNCTION__));
544	if (op <= PictOpSrc && !dst->alphaMap)
545		flags = MOVE_WRITE | MOVE_INPLACE_HINT;
546	else
547		flags = MOVE_WRITE | MOVE_READ;
548	if (!sna_drawable_move_region_to_cpu(dst->pDrawable, region, flags))
549		return;
550	if (dst->alphaMap &&
551	    !sna_drawable_move_to_cpu(dst->alphaMap->pDrawable, flags))
552		return;
553
554	if (mask == NULL &&
555	    src->pDrawable &&
556	    dst->pDrawable->bitsPerPixel >= 8 &&
557	    src->filter != PictFilterConvolution &&
558	    (op == PictOpSrc || (op == PictOpOver && !PICT_FORMAT_A(src->format))) &&
559	    (dst->format == src->format || dst->format == alphaless(src->format)) &&
560	    sna_transform_is_imprecise_integer_translation(src->transform, src->filter,
561							   dst->polyMode == PolyModePrecise,
562							   &tx, &ty)) {
563		PixmapPtr dst_pixmap = get_drawable_pixmap(dst->pDrawable);
564		PixmapPtr src_pixmap = get_drawable_pixmap(src->pDrawable);
565		int16_t sx = src_x + tx - (dst->pDrawable->x + dst_x);
566		int16_t sy = src_y + ty - (dst->pDrawable->y + dst_y);
567
568		assert(src->pDrawable->bitsPerPixel == dst->pDrawable->bitsPerPixel);
569		assert(src_pixmap->drawable.bitsPerPixel == dst_pixmap->drawable.bitsPerPixel);
570
571		if (region->extents.x1 + sx >= 0 &&
572		    region->extents.y1 + sy >= 0 &&
573		    region->extents.x2 + sx <= src->pDrawable->width &&
574		    region->extents.y2 + sy <= src->pDrawable->height) {
575			if (sigtrap_get() == 0) {
576				const BoxRec *box = region_rects(region);
577				int nbox = region_num_rects(region);
578
579				sx += src->pDrawable->x;
580				sy += src->pDrawable->y;
581				if (get_drawable_deltas(src->pDrawable, src_pixmap, &tx, &ty))
582					sx += tx, sy += ty;
583
584				assert(region->extents.x1 + sx >= 0);
585				assert(region->extents.x2 + sx <= src_pixmap->drawable.width);
586				assert(region->extents.y1 + sy >= 0);
587				assert(region->extents.y2 + sy <= src_pixmap->drawable.height);
588
589				get_drawable_deltas(dst->pDrawable, dst_pixmap, &tx, &ty);
590
591				assert(nbox);
592				do {
593					assert(box->x1 + sx >= 0);
594					assert(box->x2 + sx <= src_pixmap->drawable.width);
595					assert(box->y1 + sy >= 0);
596					assert(box->y2 + sy <= src_pixmap->drawable.height);
597
598					assert(box->x1 + tx >= 0);
599					assert(box->x2 + tx <= dst_pixmap->drawable.width);
600					assert(box->y1 + ty >= 0);
601					assert(box->y2 + ty <= dst_pixmap->drawable.height);
602
603					assert(box->x2 > box->x1 && box->y2 > box->y1);
604
605					sigtrap_assert_active();
606					memcpy_blt(src_pixmap->devPrivate.ptr,
607						   dst_pixmap->devPrivate.ptr,
608						   dst_pixmap->drawable.bitsPerPixel,
609						   src_pixmap->devKind,
610						   dst_pixmap->devKind,
611						   box->x1 + sx, box->y1 + sy,
612						   box->x1 + tx, box->y1 + ty,
613						   box->x2 - box->x1, box->y2 - box->y1);
614					box++;
615				} while (--nbox);
616				sigtrap_put();
617			}
618
619			return;
620		}
621	}
622
623	src_image = image_from_pict(src, FALSE, &src_xoff, &src_yoff);
624	mask_image = image_from_pict(mask, FALSE, &msk_xoff, &msk_yoff);
625	dest_image = image_from_pict(dst, TRUE, &dst_xoff, &dst_yoff);
626
627	if (src_image && dest_image && !(mask && !mask_image))
628		sna_image_composite(op, src_image, mask_image, dest_image,
629				    src_x + src_xoff, src_y + src_yoff,
630				    msk_x + msk_xoff, msk_y + msk_yoff,
631				    dst_x + dst_xoff, dst_y + dst_yoff,
632				    width, height);
633
634	free_pixman_pict(src, src_image);
635	free_pixman_pict(mask, mask_image);
636	free_pixman_pict(dst, dest_image);
637}
638
639void
640sna_composite(CARD8 op,
641	      PicturePtr src,
642	      PicturePtr mask,
643	      PicturePtr dst,
644	      INT16 src_x,  INT16 src_y,
645	      INT16 mask_x, INT16 mask_y,
646	      INT16 dst_x,  INT16 dst_y,
647	      CARD16 width, CARD16 height)
648{
649	PixmapPtr pixmap = get_drawable_pixmap(dst->pDrawable);
650	struct sna *sna = to_sna_from_pixmap(pixmap);
651	struct sna_pixmap *priv;
652	struct sna_composite_op tmp;
653	RegionRec region;
654	int dx, dy;
655
656	DBG(("%s(%d src=%ld+(%d, %d), mask=%ld+(%d, %d), dst=%ld+(%d, %d)+(%d, %d), size=(%d, %d)\n",
657	     __FUNCTION__, op,
658	     get_picture_id(src), src_x, src_y,
659	     get_picture_id(mask), mask_x, mask_y,
660	     get_picture_id(dst), dst_x, dst_y,
661	     dst->pDrawable->x, dst->pDrawable->y,
662	     width, height));
663
664	if (region_is_empty(dst->pCompositeClip)) {
665		DBG(("%s: empty clip, skipping\n", __FUNCTION__));
666		return;
667	}
668
669	if (op == PictOpClear) {
670		DBG(("%s: discarding source and mask for clear\n", __FUNCTION__));
671		mask = NULL;
672		if (sna->clear)
673			src = sna->clear;
674	}
675
676	if (mask && sna_composite_mask_is_opaque(mask)) {
677		DBG(("%s: removing opaque %smask\n",
678		     __FUNCTION__,
679		     mask->componentAlpha && PICT_FORMAT_RGB(mask->format) ? "CA " : ""));
680		mask = NULL;
681	}
682
683	if (!sna_compute_composite_region(&region,
684					  src, mask, dst,
685					  src_x,  src_y,
686					  mask_x, mask_y,
687					  dst_x,  dst_y,
688					  width,  height))
689		return;
690
691	if (NO_COMPOSITE)
692		goto fallback;
693
694	if (wedged(sna)) {
695		DBG(("%s: fallback -- wedged\n", __FUNCTION__));
696		goto fallback;
697	}
698
699	if (!can_render_to_picture(dst)) {
700		DBG(("%s: fallback due to unhandled picture\n", __FUNCTION__));
701		goto fallback;
702	}
703
704	priv = sna_pixmap(pixmap);
705	if (priv == NULL) {
706		DBG(("%s: fallback as destination pixmap=%ld is unattached\n",
707		     __FUNCTION__, pixmap->drawable.serialNumber));
708		goto fallback;
709	}
710
711	if (use_cpu(pixmap, priv, op, width, height) &&
712	    !picture_is_gpu(sna, src, PREFER_GPU_RENDER) &&
713	    !picture_is_gpu(sna, mask, PREFER_GPU_RENDER)) {
714		DBG(("%s: fallback, dst pixmap=%ld is too small (or completely damaged)\n",
715		     __FUNCTION__, pixmap->drawable.serialNumber));
716		goto fallback;
717	}
718
719	dx = region.extents.x1 - (dst_x + dst->pDrawable->x);
720	dy = region.extents.y1 - (dst_y + dst->pDrawable->y);
721
722	DBG(("%s: composite region extents:+(%d, %d) -> (%d, %d), (%d, %d) + (%d, %d)\n",
723	     __FUNCTION__,
724	     dx, dy,
725	     region.extents.x1, region.extents.y1,
726	     region.extents.x2, region.extents.y2,
727	     get_drawable_dx(dst->pDrawable),
728	     get_drawable_dy(dst->pDrawable)));
729
730	if (op <= PictOpSrc && priv->cpu_damage) {
731		int16_t x, y;
732
733		if (get_drawable_deltas(dst->pDrawable, pixmap, &x, &y))
734			pixman_region_translate(&region, x, y);
735
736		sna_damage_subtract(&priv->cpu_damage, &region);
737		if (priv->cpu_damage == NULL) {
738			list_del(&priv->flush_list);
739			priv->cpu = false;
740		}
741
742		if (x|y)
743			pixman_region_translate(&region, -x, -y);
744	}
745
746	if (!sna->render.composite(sna,
747				   op, src, mask, dst,
748				   src_x + dx,  src_y + dy,
749				   mask_x + dx, mask_y + dy,
750				   region.extents.x1,
751				   region.extents.y1,
752				   region.extents.x2 - region.extents.x1,
753				   region.extents.y2 - region.extents.y1,
754				   region.data ? COMPOSITE_PARTIAL : 0,
755				   memset(&tmp, 0, sizeof(tmp)))) {
756		DBG(("%s: fallback due unhandled composite op\n", __FUNCTION__));
757		goto fallback;
758	}
759
760	if (region.data == NULL)
761		tmp.box(sna, &tmp, &region.extents);
762	else
763		tmp.boxes(sna, &tmp,
764			  RegionBoxptr(&region),
765			  region_num_rects(&region));
766	apply_damage(&tmp, &region);
767	tmp.done(sna, &tmp);
768
769	goto out;
770
771fallback:
772	DBG(("%s: fallback -- fbComposite\n", __FUNCTION__));
773	sna_composite_fb(op, src, mask, dst, &region,
774			 src_x,  src_y,
775			 mask_x, mask_y,
776			 dst_x,  dst_y,
777			 width,  height);
778out:
779	REGION_UNINIT(NULL, &region);
780}
781
782void
783sna_composite_rectangles(CARD8		 op,
784			 PicturePtr	 dst,
785			 xRenderColor	*color,
786			 int		 num_rects,
787			 xRectangle	*rects)
788{
789	struct sna *sna = to_sna_from_drawable(dst->pDrawable);
790	PixmapPtr pixmap;
791	struct sna_pixmap *priv;
792	struct kgem_bo *bo;
793	struct sna_damage **damage;
794	pixman_region16_t region;
795	pixman_box16_t stack_boxes[64], *boxes = stack_boxes, *b;
796	int16_t dst_x, dst_y;
797	int i, num_boxes;
798	unsigned hint;
799
800	DBG(("%s(op=%d, %08x x %d [(%d, %d)x(%d, %d) ...])\n",
801	     __FUNCTION__, op,
802	     (color->alpha >> 8 << 24) |
803	     (color->red   >> 8 << 16) |
804	     (color->green >> 8 << 8) |
805	     (color->blue  >> 8 << 0),
806	     num_rects,
807	     rects[0].x, rects[0].y, rects[0].width, rects[0].height));
808
809	if (!num_rects)
810		return;
811
812	if (region_is_empty(dst->pCompositeClip)) {
813		DBG(("%s: empty clip, skipping\n", __FUNCTION__));
814		return;
815	}
816
817	if ((color->red|color->green|color->blue|color->alpha) <= 0x00ff) {
818		switch (op) {
819		case PictOpOver:
820		case PictOpOutReverse:
821		case PictOpAdd:
822			return;
823		case  PictOpInReverse:
824		case  PictOpSrc:
825			op = PictOpClear;
826			break;
827		case  PictOpAtopReverse:
828			op = PictOpOut;
829			break;
830		case  PictOpXor:
831			op = PictOpOverReverse;
832			break;
833		}
834	}
835	if (color->alpha <= 0x00ff) {
836		switch (op) {
837		case PictOpOver:
838		case PictOpOutReverse:
839			return;
840		case  PictOpInReverse:
841			op = PictOpClear;
842			break;
843		case  PictOpAtopReverse:
844			op = PictOpOut;
845			break;
846		case  PictOpXor:
847			op = PictOpOverReverse;
848			break;
849		}
850	} else if (color->alpha >= 0xff00) {
851		switch (op) {
852		case PictOpOver:
853			op = PictOpSrc;
854			break;
855		case PictOpInReverse:
856			return;
857		case PictOpOutReverse:
858			op = PictOpClear;
859			break;
860		case  PictOpAtopReverse:
861			op = PictOpOverReverse;
862			break;
863		case  PictOpXor:
864			op = PictOpOut;
865			break;
866		}
867	}
868
869	/* Avoid reducing overlapping translucent rectangles */
870	if (op == PictOpOver &&
871	    num_rects == 1 &&
872	    sna_drawable_is_clear(dst->pDrawable))
873		op = PictOpSrc;
874
875	DBG(("%s: converted to op %d\n", __FUNCTION__, op));
876
877	if (num_rects > ARRAY_SIZE(stack_boxes)) {
878		boxes = malloc(sizeof(pixman_box16_t) * num_rects);
879		if (boxes == NULL)
880			return;
881	}
882
883	for (i = num_boxes = 0; i < num_rects; i++) {
884		boxes[num_boxes].x1 = rects[i].x + dst->pDrawable->x;
885		if (boxes[num_boxes].x1 < dst->pCompositeClip->extents.x1)
886			boxes[num_boxes].x1 = dst->pCompositeClip->extents.x1;
887
888		boxes[num_boxes].y1 = rects[i].y + dst->pDrawable->y;
889		if (boxes[num_boxes].y1 < dst->pCompositeClip->extents.y1)
890			boxes[num_boxes].y1 = dst->pCompositeClip->extents.y1;
891
892		boxes[num_boxes].x2 = bound(rects[i].x + dst->pDrawable->x, rects[i].width);
893		if (boxes[num_boxes].x2 > dst->pCompositeClip->extents.x2)
894			boxes[num_boxes].x2 = dst->pCompositeClip->extents.x2;
895
896		boxes[num_boxes].y2 = bound(rects[i].y + dst->pDrawable->y, rects[i].height);
897		if (boxes[num_boxes].y2 > dst->pCompositeClip->extents.y2)
898			boxes[num_boxes].y2 = dst->pCompositeClip->extents.y2;
899
900		DBG(("%s[%d] (%d, %d)x(%d, %d) -> (%d, %d), (%d, %d)\n",
901		     __FUNCTION__, i,
902		     rects[i].x, rects[i].y, rects[i].width, rects[i].height,
903		     boxes[num_boxes].x1, boxes[num_boxes].y1, boxes[num_boxes].x2, boxes[num_boxes].y2));
904
905		if (boxes[num_boxes].x2 > boxes[num_boxes].x1 &&
906		    boxes[num_boxes].y2 > boxes[num_boxes].y1)
907			num_boxes++;
908	}
909
910	if (num_boxes == 0)
911		goto cleanup_boxes;
912
913	if (!pixman_region_init_rects(&region, boxes, num_boxes))
914		goto cleanup_boxes;
915
916	DBG(("%s: nrects=%d, region=(%d, %d), (%d, %d) x %d\n",
917	     __FUNCTION__, num_rects,
918	     region.extents.x1, region.extents.y1,
919	     region.extents.x2, region.extents.y2,
920	     num_boxes));
921
922	if (dst->pCompositeClip->data &&
923	    (!pixman_region_intersect(&region, &region, dst->pCompositeClip) ||
924	     region_is_empty(&region))) {
925		DBG(("%s: zero-intersection between rectangles and clip\n",
926		     __FUNCTION__));
927		goto cleanup_region;
928	}
929
930	DBG(("%s: clipped extents (%d, %d),(%d, %d) x %d\n",
931	     __FUNCTION__,
932	     RegionExtents(&region)->x1, RegionExtents(&region)->y1,
933	     RegionExtents(&region)->x2, RegionExtents(&region)->y2,
934	     region_num_rects(&region)));
935
936	/* XXX xserver-1.8: CompositeRects is not tracked by Damage, so we must
937	 * manually append the damaged regions ourselves.
938	 *
939	 * Note that DamageRegionAppend() will apply the drawable-deltas itself.
940	 */
941	DamageRegionAppend(dst->pDrawable, &region);
942
943	pixmap = get_drawable_pixmap(dst->pDrawable);
944	if (get_drawable_deltas(dst->pDrawable, pixmap, &dst_x, &dst_y))
945		pixman_region_translate(&region, dst_x, dst_y);
946
947	DBG(("%s: pixmap +(%d, %d) extents (%d, %d),(%d, %d)\n",
948	     __FUNCTION__, dst_x, dst_y,
949	     RegionExtents(&region)->x1, RegionExtents(&region)->y1,
950	     RegionExtents(&region)->x2, RegionExtents(&region)->y2));
951	assert_pixmap_contains_box(pixmap, RegionExtents(&region));
952
953	if (NO_COMPOSITE_RECTANGLES)
954		goto fallback;
955
956	if (wedged(sna))
957		goto fallback;
958
959	if (!can_render_to_picture(dst)) {
960		DBG(("%s: fallback, dst has an incompatible picture\n", __FUNCTION__));
961		goto fallback;
962	}
963
964	priv = sna_pixmap(pixmap);
965	if (priv == NULL || too_small(priv)) {
966		DBG(("%s: fallback, dst pixmap=%ld too small or not attached\n",
967		     __FUNCTION__, pixmap->drawable.serialNumber));
968		goto fallback;
969	}
970
971	/* If we going to be overwriting any CPU damage with a subsequent
972	 * operation, then we may as well delete it without moving it
973	 * first to the GPU.
974	 */
975	hint = can_render(sna) ? PREFER_GPU : 0;
976	if (op <= PictOpSrc) {
977		if (priv->clear) {
978			uint32_t pixel;
979			bool ok;
980
981			if (op == PictOpClear) {
982				ok = sna_get_pixel_from_rgba(&pixel,
983							     0, 0, 0, 0,
984							     dst->format);
985			} else {
986				ok = sna_get_pixel_from_rgba(&pixel,
987							     color->red,
988							     color->green,
989							     color->blue,
990							     color->alpha,
991							     dst->format);
992			}
993			if (ok && priv->clear_color == pixel)
994				goto done;
995		}
996
997		if (region.data == NULL) {
998			hint |= IGNORE_DAMAGE;
999			if (region_subsumes_drawable(&region, &pixmap->drawable))
1000				hint |= REPLACES;
1001			if (priv->cpu_damage &&
1002			    (hint & REPLACES ||
1003			     region_subsumes_damage(&region, priv->cpu_damage))) {
1004				DBG(("%s: discarding existing CPU damage\n", __FUNCTION__));
1005				if (priv->gpu_bo && priv->gpu_bo->proxy) {
1006					assert(priv->gpu_damage == NULL);
1007					kgem_bo_destroy(&sna->kgem, priv->gpu_bo);
1008					priv->gpu_bo = NULL;
1009				}
1010				sna_damage_destroy(&priv->cpu_damage);
1011				list_del(&priv->flush_list);
1012			}
1013			if (hint & REPLACES ||
1014			    box_inplace(pixmap, &region.extents)) {
1015				if (priv->gpu_bo && priv->cpu_damage == NULL) {
1016					DBG(("%s: promoting to full GPU\n", __FUNCTION__));
1017					assert(priv->gpu_bo->proxy == NULL);
1018					sna_damage_all(&priv->gpu_damage, pixmap);
1019				}
1020			}
1021		}
1022		if (priv->cpu_damage == NULL) {
1023			DBG(("%s: dropping last-cpu hint\n", __FUNCTION__));
1024			priv->cpu = false;
1025		}
1026	}
1027
1028	bo = sna_drawable_use_bo(&pixmap->drawable, hint,
1029				 &region.extents, &damage);
1030	if (bo == NULL) {
1031		DBG(("%s: fallback due to no GPU bo\n", __FUNCTION__));
1032		goto fallback;
1033	}
1034	if (hint & REPLACES)
1035		kgem_bo_pair_undo(&sna->kgem, priv->gpu_bo, priv->cpu_bo);
1036
1037	if (op <= PictOpSrc) {
1038		b = pixman_region_rectangles(&region, &num_boxes);
1039		if (!sna->render.fill_boxes(sna, op, dst->format, color,
1040					    &pixmap->drawable, bo, b, num_boxes)) {
1041			DBG(("%s: fallback - acceleration failed\n", __FUNCTION__));
1042			goto fallback;
1043		}
1044	} else if (dst->pCompositeClip->data == NULL) {
1045		for (i = 0; i < num_boxes; i++) {
1046			boxes[i].x1 += dst_x;
1047			boxes[i].x2 += dst_x;
1048			boxes[i].y1 += dst_y;
1049			boxes[i].y2 += dst_y;
1050		}
1051		if (!sna->render.fill_boxes(sna, op, dst->format, color,
1052					    &pixmap->drawable, bo, boxes, num_boxes)) {
1053			DBG(("%s: fallback - acceleration failed\n", __FUNCTION__));
1054			goto fallback;
1055		}
1056	} else {
1057		for (i = 0; i < num_boxes; i++) {
1058			RegionRec tmp = { boxes[i] };
1059			if (pixman_region_intersect(&tmp, &tmp, dst->pCompositeClip)) {
1060				int n = 0;
1061
1062				b = pixman_region_rectangles(&tmp, &n);
1063				if (n) {
1064					if (dst_x | dst_y)
1065						pixman_region_translate(&tmp, dst_x, dst_y);
1066
1067					n = !sna->render.fill_boxes(sna, op, dst->format, color,
1068								    &pixmap->drawable, bo, b, n);
1069				}
1070
1071				pixman_region_fini(&tmp);
1072
1073				if (n) {
1074					DBG(("%s: fallback - acceleration failed\n", __FUNCTION__));
1075					goto fallback;
1076				}
1077			}
1078		}
1079	}
1080
1081	if (damage)
1082		sna_damage_add(damage, &region);
1083
1084	/* Clearing a pixmap after creation is a common operation, so take
1085	 * advantage and reduce further damage operations.
1086	 */
1087	if (region_subsumes_drawable(&region, &pixmap->drawable)) {
1088		if (damage) {
1089			sna_damage_all(damage, pixmap);
1090			sna_damage_destroy(damage == &priv->gpu_damage ?
1091					   &priv->cpu_damage : &priv->gpu_damage);
1092		}
1093
1094		if (op <= PictOpSrc && bo == priv->gpu_bo) {
1095			bool ok;
1096
1097			assert(DAMAGE_IS_ALL(priv->gpu_damage));
1098
1099			priv->clear_color = 0;
1100			ok = true;
1101			if (op == PictOpSrc)
1102				ok = sna_get_pixel_from_rgba(&priv->clear_color,
1103							     color->red,
1104							     color->green,
1105							     color->blue,
1106							     color->alpha,
1107							     dst->format);
1108			priv->clear = ok;
1109			DBG(("%s: pixmap=%ld marking clear [%08x]? %d\n",
1110			     __FUNCTION__, pixmap->drawable.serialNumber,
1111			     priv->clear_color, ok));
1112		}
1113	}
1114	goto done;
1115
1116fallback:
1117	DBG(("%s: fallback\n", __FUNCTION__));
1118	if (op <= PictOpSrc)
1119		hint = MOVE_WRITE;
1120	else
1121		hint = MOVE_WRITE | MOVE_READ;
1122	if (!sna_drawable_move_region_to_cpu(&pixmap->drawable, &region, hint))
1123		goto done;
1124
1125	if (dst->alphaMap &&
1126	    !sna_drawable_move_to_cpu(dst->alphaMap->pDrawable, hint))
1127		goto done;
1128
1129	assert(pixmap->devPrivate.ptr);
1130
1131	if (sigtrap_get() == 0) {
1132		if (op <= PictOpSrc) {
1133			int nbox = region_num_rects(&region);
1134			const BoxRec *box = region_rects(&region);
1135			uint32_t pixel;
1136
1137			if (op == PictOpClear)
1138				pixel = 0;
1139			else if (!sna_get_pixel_from_rgba(&pixel,
1140							  color->red,
1141							  color->green,
1142							  color->blue,
1143							  color->alpha,
1144							  dst->format))
1145				goto fallback_composite;
1146
1147			sigtrap_assert_active();
1148			if (pixel == 0 &&
1149			    box->x2 - box->x1 == pixmap->drawable.width &&
1150			    box->y2 - box->y1 == pixmap->drawable.height) {
1151				memset(pixmap->devPrivate.ptr, 0,
1152				       pixmap->devKind*pixmap->drawable.height);
1153			} else do {
1154				DBG(("%s: fallback fill: (%d, %d)x(%d, %d) %08x\n",
1155				     __FUNCTION__,
1156				     box->x1, box->y1,
1157				     box->x2 - box->x1,
1158				     box->y2 - box->y1,
1159				     pixel));
1160
1161				pixman_fill(pixmap->devPrivate.ptr,
1162					    pixmap->devKind/sizeof(uint32_t),
1163					    pixmap->drawable.bitsPerPixel,
1164					    box->x1, box->y1,
1165					    box->x2 - box->x1,
1166					    box->y2 - box->y1,
1167					    pixel);
1168				box++;
1169			} while (--nbox);
1170		} else {
1171			PicturePtr src;
1172			int error;
1173
1174fallback_composite:
1175			DBG(("%s: fallback -- fbComposite()\n", __FUNCTION__));
1176			src = CreateSolidPicture(0, color, &error);
1177			if (src) {
1178				do {
1179					fbComposite(op, src, NULL, dst,
1180						    0, 0,
1181						    0, 0,
1182						    rects->x, rects->y,
1183						    rects->width, rects->height);
1184					rects++;
1185				} while (--num_rects);
1186				FreePicture(src, 0);
1187			}
1188		}
1189		sigtrap_put();
1190	}
1191
1192done:
1193	DamageRegionProcessPending(dst->pDrawable);
1194
1195cleanup_region:
1196	pixman_region_fini(&region);
1197cleanup_boxes:
1198	if (boxes != stack_boxes)
1199		free(boxes);
1200}
1201