1 /**************************************************************************
2 
3 Copyright (c) 2011 Intel Corporation
4 
5 Permission is hereby granted, free of charge, to any person obtaining a
6 copy of this software and associated documentation files (the
7 "Software"), to deal in the Software without restriction, including
8 without limitation the rights to use, copy, modify, merge, publish,
9 distribute, sub license, and/or sell copies of the Software, and to
10 permit persons to whom the Software is furnished to do so, subject to
11 the following conditions:
12 
13 The above copyright notice and this permission notice (including the
14 next paragraph) shall be included in all copies or substantial portions
15 of the Software.
16 
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
20 IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
21 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 
25  **************************************************************************/
26 
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30 
31 #include "sna.h"
32 #include "sna_damage.h"
33 
34 /*
35  * sna_damage is a batching layer on top of the regular pixman_region_t.
36  * It is required as the ever-growing accumulation of invidual small
37  * damage regions is an O(n^2) operation. Instead the accumulation of a
38  * batch can be done in closer to O(n.lgn), and so prevents abysmal
39  * performance in x11perf -copywinwin10.
40  *
41  * As with the core of SNA, damage is handled modally. That is, it
42  * accumulates whilst rendering and then subtracts during migration of the
43  * pixmap from GPU to CPU or vice versa. As such we can track the current
44  * mode globally and when that mode switches perform the update of the region
45  * in a single operation.
46  *
47  * Furthermore, we can track whether the whole pixmap is damaged and so
48  * cheapy discard no-ops.
49  */
50 
51 struct sna_damage_box {
52 	struct list list;
53 	int size;
54 } __attribute__((packed));
55 
56 static struct sna_damage *__freed_damage;
57 
region_is_singular(const RegionRec * r)58 static inline bool region_is_singular(const RegionRec *r)
59 {
60 	return r->data == NULL;
61 }
62 
region_is_singular_or_empty(const RegionRec * r)63 static inline bool region_is_singular_or_empty(const RegionRec *r)
64 {
65 	return r->data == NULL || r->data->numRects == 0;
66 }
67 
68 #if HAS_DEBUG_FULL
_debug_describe_region(char * buf,int max,const RegionRec * region)69 static const char *_debug_describe_region(char *buf, int max,
70 					  const RegionRec *region)
71 {
72 	const BoxRec *box;
73 	int n, len;
74 
75 	if (region == NULL)
76 		return "nil";
77 
78 	n = region_num_rects(region);
79 	if (n == 0)
80 		return "[0]";
81 
82 	if (n == 1) {
83 		sprintf(buf,
84 			"[(%d, %d), (%d, %d)]",
85 			region->extents.x1, region->extents.y1,
86 			region->extents.x2, region->extents.y2);
87 		return buf;
88 	}
89 
90 	len = sprintf(buf,
91 		      "[(%d, %d), (%d, %d) x %d: ",
92 		      region->extents.x1, region->extents.y1,
93 		      region->extents.x2, region->extents.y2,
94 		      n) + 3;
95 	max -= 2;
96 	box = region_rects(region);
97 	while (n--) {
98 		char tmp[80];
99 		int this;
100 
101 		this = snprintf(tmp, sizeof(tmp),
102 				"((%d, %d), (%d, %d))%s",
103 				box->x1, box->y1,
104 				box->x2, box->y2,
105 				n ? ", ..." : "");
106 		box++;
107 
108 		if (this > max - len)
109 			break;
110 
111 		len -= 3;
112 		memcpy(buf + len, tmp, this);
113 		len += this;
114 	}
115 	buf[len++] = ']';
116 	buf[len] = '\0';
117 	return buf;
118 }
119 
_debug_describe_damage(char * buf,int max,const struct sna_damage * damage)120 static const char *_debug_describe_damage(char *buf, int max,
121 					  const struct sna_damage *damage)
122 {
123 	char damage_str[500], region_str[500];
124 	int str_max;
125 
126 	if (damage == NULL)
127 		return "None";
128 
129 	str_max = max/2 - 6;
130 	if (str_max > sizeof(damage_str))
131 		str_max = sizeof(damage_str);
132 
133 	if (damage->mode == DAMAGE_ALL) {
134 		snprintf(buf, max, "[[(%d, %d), (%d, %d)]: all]",
135 			 damage->extents.x1, damage->extents.y1,
136 			 damage->extents.x2, damage->extents.y2);
137 	} else {
138 		if (damage->dirty) {
139 			sprintf(damage_str, "%c[ ...]",
140 				damage->mode == DAMAGE_SUBTRACT ? '-' : '+');
141 		} else
142 			damage_str[0] = '\0';
143 		snprintf(buf, max, "[[(%d, %d), (%d, %d)]: %s %s]%c",
144 			 damage->extents.x1, damage->extents.y1,
145 			 damage->extents.x2, damage->extents.y2,
146 			 _debug_describe_region(region_str, str_max,
147 						&damage->region),
148 			 damage_str, damage->dirty ? '*' : ' ');
149 	}
150 
151 	return buf;
152 }
153 #endif
154 
155 static struct sna_damage_box *
last_box(struct sna_damage * damage)156 last_box(struct sna_damage *damage)
157 {
158 	return list_entry(damage->embedded_box.list.prev,
159 			  struct sna_damage_box,
160 			  list);
161 }
162 
163 static void
reset_embedded_box(struct sna_damage * damage)164 reset_embedded_box(struct sna_damage *damage)
165 {
166 	damage->dirty = false;
167 	damage->box = damage->embedded_box.box;
168 	damage->embedded_box.size =
169 		damage->remain = ARRAY_SIZE(damage->embedded_box.box);
170 	list_init(&damage->embedded_box.list);
171 }
172 
reset_extents(struct sna_damage * damage)173 static void reset_extents(struct sna_damage *damage)
174 {
175 	damage->extents.x1 = damage->extents.y1 = MAXSHORT;
176 	damage->extents.x2 = damage->extents.y2 = MINSHORT;
177 }
178 
_sna_damage_create(void)179 static struct sna_damage *_sna_damage_create(void)
180 {
181 	struct sna_damage *damage;
182 
183 	if (__freed_damage) {
184 		damage = __freed_damage;
185 		__freed_damage = *(void **)__freed_damage;
186 	} else {
187 		damage = malloc(sizeof(*damage));
188 		if (damage == NULL)
189 			return NULL;
190 	}
191 	reset_embedded_box(damage);
192 	damage->mode = DAMAGE_ADD;
193 	pixman_region_init(&damage->region);
194 	reset_extents(damage);
195 
196 	return damage;
197 }
198 
sna_damage_create(void)199 struct sna_damage *sna_damage_create(void)
200 {
201 	return _sna_damage_create();
202 }
203 
free_list(struct list * head)204 static void free_list(struct list *head)
205 {
206 	while (!list_is_empty(head)) {
207 		struct list *l = head->next;
208 		list_del(l);
209 		free(l);
210 	}
211 }
212 
__sna_damage_reduce(struct sna_damage * damage)213 static void __sna_damage_reduce(struct sna_damage *damage)
214 {
215 	int n, nboxes;
216 	BoxPtr boxes, free_boxes = NULL;
217 	pixman_region16_t *region = &damage->region;
218 	struct sna_damage_box *iter;
219 
220 	assert(damage->mode != DAMAGE_ALL);
221 	assert(damage->dirty);
222 
223 	DBG(("    reduce: before region.n=%d\n", region_num_rects(region)));
224 
225 	nboxes = damage->embedded_box.size;
226 	list_for_each_entry(iter, &damage->embedded_box.list, list)
227 		nboxes += iter->size;
228 	DBG(("   nboxes=%d, residual=%d\n", nboxes, damage->remain));
229 	nboxes -= damage->remain;
230 	if (nboxes == 0)
231 		goto done;
232 	if (nboxes == 1) {
233 		pixman_region16_t tmp;
234 
235 		tmp.extents = damage->embedded_box.box[0];
236 		tmp.data = NULL;
237 
238 		if (damage->mode == DAMAGE_ADD)
239 			pixman_region_union(region, region, &tmp);
240 		else
241 			pixman_region_subtract(region, region, &tmp);
242 		damage->extents = region->extents;
243 
244 		goto done;
245 	}
246 
247 	if (damage->mode == DAMAGE_ADD)
248 		nboxes += region_num_rects(region);
249 
250 	iter = last_box(damage);
251 	n = iter->size - damage->remain;
252 	boxes = (BoxRec *)(iter+1);
253 	DBG(("   last box count=%d/%d, need=%d\n", n, iter->size, nboxes));
254 	if (nboxes > iter->size) {
255 		boxes = malloc(sizeof(BoxRec)*nboxes);
256 		if (boxes == NULL)
257 			goto done;
258 
259 		free_boxes = boxes;
260 	}
261 
262 	if (boxes != damage->embedded_box.box) {
263 		if (list_is_empty(&damage->embedded_box.list)) {
264 			DBG(("   copying embedded boxes\n"));
265 			memcpy(boxes,
266 			       damage->embedded_box.box,
267 			       n*sizeof(BoxRec));
268 		} else {
269 			if (boxes != (BoxPtr)(iter+1)) {
270 				DBG(("   copying %d boxes from last\n", n));
271 				memcpy(boxes, iter+1, n*sizeof(BoxRec));
272 			}
273 
274 			iter = list_entry(iter->list.prev,
275 					  struct sna_damage_box,
276 					  list);
277 			while (&iter->list != &damage->embedded_box.list) {
278 				DBG(("   copy %d boxes from %d\n",
279 				     iter->size, n));
280 				memcpy(boxes + n, iter+1,
281 				       iter->size * sizeof(BoxRec));
282 				n += iter->size;
283 
284 				iter = list_entry(iter->list.prev,
285 						  struct sna_damage_box,
286 						  list);
287 			}
288 
289 			DBG(("   copying embedded boxes to %d\n", n));
290 			memcpy(boxes + n,
291 			       damage->embedded_box.box,
292 			       sizeof(damage->embedded_box.box));
293 			n += damage->embedded_box.size;
294 		}
295 	}
296 
297 	if (damage->mode == DAMAGE_ADD) {
298 		memcpy(boxes + n,
299 		       region_rects(region),
300 		       region_num_rects(region)*sizeof(BoxRec));
301 		assert(n + region_num_rects(region) == nboxes);
302 		pixman_region_fini(region);
303 		pixman_region_init_rects(region, boxes, nboxes);
304 
305 		assert(pixman_region_not_empty(region));
306 		assert(damage->extents.x1 == region->extents.x1 &&
307 		       damage->extents.y1 == region->extents.y1 &&
308 		       damage->extents.x2 == region->extents.x2 &&
309 		       damage->extents.y2 == region->extents.y2);
310 	} else {
311 		pixman_region16_t tmp;
312 
313 		assert(n == nboxes);
314 		pixman_region_init_rects(&tmp, boxes, nboxes);
315 		pixman_region_subtract(region, region, &tmp);
316 		pixman_region_fini(&tmp);
317 
318 		assert(damage->extents.x1 <= region->extents.x1 &&
319 		       damage->extents.y1 <= region->extents.y1 &&
320 		       damage->extents.x2 >= region->extents.x2 &&
321 		       damage->extents.y2 >= region->extents.y2);
322 		if (pixman_region_not_empty(region))
323 			damage->extents = region->extents;
324 		else
325 			reset_extents(damage);
326 	}
327 
328 	free(free_boxes);
329 
330 done:
331 	damage->mode = DAMAGE_ADD;
332 	free_list(&damage->embedded_box.list);
333 	reset_embedded_box(damage);
334 
335 	DBG(("    reduce: after region.n=%d\n", region_num_rects(region)));
336 }
337 
_sna_damage_create_boxes(struct sna_damage * damage,int count)338 static bool _sna_damage_create_boxes(struct sna_damage *damage,
339 				     int count)
340 {
341 	struct sna_damage_box *box;
342 	int n;
343 
344 	box = last_box(damage);
345 	n = 4*box->size;
346 	if (n < count)
347 		n = ALIGN(count, 64);
348 
349 	DBG(("    %s(%d->%d): new\n", __FUNCTION__, count, n));
350 
351 	if (n >= (INT_MAX - sizeof(*box)) / sizeof(BoxRec))
352 		return false;
353 
354 	box = malloc(sizeof(*box) + sizeof(BoxRec)*n);
355 	if (box == NULL)
356 		return false;
357 
358 	list_add_tail(&box->list, &damage->embedded_box.list);
359 
360 	box->size = damage->remain = n;
361 	damage->box = (BoxRec *)(box + 1);
362 	return true;
363 }
364 
365 static struct sna_damage *
_sna_damage_create_elt(struct sna_damage * damage,const BoxRec * boxes,int count)366 _sna_damage_create_elt(struct sna_damage *damage,
367 		       const BoxRec *boxes, int count)
368 {
369 	int n;
370 
371 	DBG(("    %s: prev=(remain %d), count=%d\n",
372 	     __FUNCTION__, damage->remain, count));
373 	assert(count);
374 
375 restart:
376 	n = count;
377 	if (n > damage->remain)
378 		n = damage->remain;
379 	if (n) {
380 		memcpy(damage->box, boxes, n * sizeof(BoxRec));
381 		damage->box += n;
382 		damage->remain -= n;
383 		damage->dirty = true;
384 
385 		count -= n;
386 		boxes += n;
387 		if (count == 0)
388 			return damage;
389 	}
390 
391 	DBG(("    %s(): new elt\n", __FUNCTION__));
392 	assert(damage->remain == 0);
393 	assert(damage->box - (BoxRec *)(last_box(damage)+1) == last_box(damage)->size);
394 
395 	if (!_sna_damage_create_boxes(damage, count)) {
396 		unsigned mode;
397 
398 		if (!damage->dirty)
399 			return damage;
400 
401 		mode = damage->mode;
402 		__sna_damage_reduce(damage);
403 		damage->mode = mode;
404 
405 		goto restart;
406 	}
407 
408 	memcpy(damage->box, boxes, count * sizeof(BoxRec));
409 	damage->box += count;
410 	damage->remain -= count;
411 	damage->dirty = true;
412 	assert(damage->remain >= 0);
413 
414 	return damage;
415 }
416 
417 static struct sna_damage *
_sna_damage_create_elt_from_boxes(struct sna_damage * damage,const BoxRec * boxes,int count,int16_t dx,int16_t dy)418 _sna_damage_create_elt_from_boxes(struct sna_damage *damage,
419 				  const BoxRec *boxes, int count,
420 				  int16_t dx, int16_t dy)
421 {
422 	int i, n;
423 
424 	DBG(("    %s: prev=(remain %d)\n", __FUNCTION__, damage->remain));
425 	assert(count);
426 
427 restart:
428 	n = count;
429 	if (n > damage->remain)
430 		n = damage->remain;
431 	if (n) {
432 		for (i = 0; i < n; i++) {
433 			damage->box[i].x1 = boxes[i].x1 + dx;
434 			damage->box[i].x2 = boxes[i].x2 + dx;
435 			damage->box[i].y1 = boxes[i].y1 + dy;
436 			damage->box[i].y2 = boxes[i].y2 + dy;
437 		}
438 		damage->box += n;
439 		damage->remain -= n;
440 		damage->dirty = true;
441 
442 		count -= n;
443 		boxes += n;
444 		if (count == 0)
445 			return damage;
446 	}
447 
448 	DBG(("    %s(): new elt\n", __FUNCTION__));
449 	assert(damage->remain == 0);
450 	assert(damage->box - (BoxRec *)(last_box(damage)+1) == last_box(damage)->size);
451 
452 	if (!_sna_damage_create_boxes(damage, count)) {
453 		unsigned mode;
454 
455 		if (!damage->dirty)
456 			return damage;
457 
458 		mode = damage->mode;
459 		__sna_damage_reduce(damage);
460 		damage->mode = mode;
461 
462 		goto restart;
463 	}
464 
465 	for (i = 0; i < count; i++) {
466 		damage->box[i].x1 = boxes[i].x1 + dx;
467 		damage->box[i].x2 = boxes[i].x2 + dx;
468 		damage->box[i].y1 = boxes[i].y1 + dy;
469 		damage->box[i].y2 = boxes[i].y2 + dy;
470 	}
471 	damage->box += count;
472 	damage->remain -= count;
473 	damage->dirty = true;
474 	assert(damage->remain >= 0);
475 
476 	return damage;
477 }
478 
479 static struct sna_damage *
_sna_damage_create_elt_from_rectangles(struct sna_damage * damage,const xRectangle * r,int count,int16_t dx,int16_t dy)480 _sna_damage_create_elt_from_rectangles(struct sna_damage *damage,
481 				       const xRectangle *r, int count,
482 				       int16_t dx, int16_t dy)
483 {
484 	int i, n;
485 
486 	DBG(("    %s: prev=(remain %d), count=%d\n",
487 	     __FUNCTION__, damage->remain, count));
488 	assert(count);
489 
490 restart:
491 	n = count;
492 	if (n > damage->remain)
493 		n = damage->remain;
494 	if (n) {
495 		for (i = 0; i < n; i++) {
496 			damage->box[i].x1 = r[i].x + dx;
497 			damage->box[i].x2 = damage->box[i].x1 + r[i].width;
498 			damage->box[i].y1 = r[i].y + dy;
499 			damage->box[i].y2 = damage->box[i].y1 + r[i].height;
500 		}
501 		damage->box += n;
502 		damage->remain -= n;
503 		damage->dirty = true;
504 
505 		count -= n;
506 		r += n;
507 		if (count == 0)
508 			return damage;
509 	}
510 
511 	DBG(("    %s(): new elt\n", __FUNCTION__));
512 	assert(damage->remain == 0);
513 	assert(damage->box - (BoxRec *)(last_box(damage)+1) == last_box(damage)->size);
514 
515 	if (!_sna_damage_create_boxes(damage, count)) {
516 		unsigned mode;
517 
518 		if (!damage->dirty)
519 			return damage;
520 
521 		mode = damage->mode;
522 		__sna_damage_reduce(damage);
523 		damage->mode = mode;
524 
525 		goto restart;
526 	}
527 
528 	for (i = 0; i < count; i++) {
529 		damage->box[i].x1 = r[i].x + dx;
530 		damage->box[i].x2 = damage->box[i].x1 + r[i].width;
531 		damage->box[i].y1 = r[i].y + dy;
532 		damage->box[i].y2 = damage->box[i].y1 + r[i].height;
533 	}
534 	damage->box += count;
535 	damage->remain -= count;
536 	damage->dirty = true;
537 	assert(damage->remain >= 0);
538 
539 	return damage;
540 }
541 
542 static struct sna_damage *
_sna_damage_create_elt_from_points(struct sna_damage * damage,const DDXPointRec * p,int count,int16_t dx,int16_t dy)543 _sna_damage_create_elt_from_points(struct sna_damage *damage,
544 				   const DDXPointRec *p, int count,
545 				   int16_t dx, int16_t dy)
546 {
547 	int i, n;
548 
549 	DBG(("    %s: prev=(remain %d), count=%d\n",
550 	     __FUNCTION__, damage->remain, count));
551 	assert(count);
552 
553 restart:
554 	n = count;
555 	if (n > damage->remain)
556 		n = damage->remain;
557 	if (n) {
558 		for (i = 0; i < n; i++) {
559 			damage->box[i].x1 = p[i].x + dx;
560 			damage->box[i].x2 = damage->box[i].x1 + 1;
561 			damage->box[i].y1 = p[i].y + dy;
562 			damage->box[i].y2 = damage->box[i].y1 + 1;
563 		}
564 		damage->box += n;
565 		damage->remain -= n;
566 		damage->dirty = true;
567 
568 		count -= n;
569 		p += n;
570 		if (count == 0)
571 			return damage;
572 	}
573 
574 	DBG(("    %s(): new elt\n", __FUNCTION__));
575 	assert(damage->remain == 0);
576 	assert(damage->box - (BoxRec *)(last_box(damage)+1) == last_box(damage)->size);
577 
578 	if (!_sna_damage_create_boxes(damage, count)) {
579 		unsigned mode;
580 
581 		if (!damage->dirty)
582 			return damage;
583 
584 		mode = damage->mode;
585 		__sna_damage_reduce(damage);
586 		damage->mode = mode;
587 
588 		goto restart;
589 	}
590 
591 	for (i = 0; i < count; i++) {
592 		damage->box[i].x1 = p[i].x + dx;
593 		damage->box[i].x2 = damage->box[i].x1 + 1;
594 		damage->box[i].y1 = p[i].y + dy;
595 		damage->box[i].y2 = damage->box[i].y1 + 1;
596 	}
597 	damage->box += count;
598 	damage->remain -= count;
599 	damage->dirty = true;
600 	assert(damage->remain >= 0);
601 
602 	return damage;
603 }
604 
damage_union(struct sna_damage * damage,const BoxRec * box)605 static void damage_union(struct sna_damage *damage, const BoxRec *box)
606 {
607 	DBG(("%s: extending damage (%d, %d), (%d, %d) by (%d, %d), (%d, %d)\n",
608 	     __FUNCTION__,
609 	     damage->extents.x1, damage->extents.y1,
610 	     damage->extents.x2, damage->extents.y2,
611 	     box->x1, box->y1, box->x2, box->y2));
612 	assert(box->x2 > box->x1 && box->y2 > box->y1);
613 	if (damage->extents.x2 < damage->extents.x1) {
614 		damage->extents = *box;
615 	} else {
616 		if (damage->extents.x1 > box->x1)
617 			damage->extents.x1 = box->x1;
618 		if (damage->extents.x2 < box->x2)
619 			damage->extents.x2 = box->x2;
620 
621 		if (damage->extents.y1 > box->y1)
622 			damage->extents.y1 = box->y1;
623 		if (damage->extents.y2 < box->y2)
624 			damage->extents.y2 = box->y2;
625 	}
626 	assert(damage->extents.x2 > damage->extents.x1);
627 	assert(damage->extents.y2 > damage->extents.y1);
628 }
629 
_pixman_region_union_box(RegionRec * region,const BoxRec * box)630 static void _pixman_region_union_box(RegionRec *region, const BoxRec *box)
631 {
632 	RegionRec u = { *box, NULL };
633 	pixman_region_union(region, region, &u);
634 }
635 
box_contains_region(const BoxRec * b,const RegionRec * r)636 static bool box_contains_region(const BoxRec *b, const RegionRec *r)
637 {
638 	return (b->x1 <= r->extents.x1 && b->x2 >= r->extents.x2 &&
639 		b->y1 <= r->extents.y1 && b->y2 >= r->extents.y2);
640 }
641 
__sna_damage_add_box(struct sna_damage * damage,const BoxRec * box)642 static struct sna_damage *__sna_damage_add_box(struct sna_damage *damage,
643 					       const BoxRec *box)
644 {
645 	if (box->y2 <= box->y1 || box->x2 <= box->x1)
646 		return damage;
647 
648 	if (!damage) {
649 		damage = _sna_damage_create();
650 		if (damage == NULL)
651 			return NULL;
652 	} else switch (damage->mode) {
653 	case DAMAGE_ALL:
654 		return damage;
655 	case DAMAGE_SUBTRACT:
656 		__sna_damage_reduce(damage);
657 	case DAMAGE_ADD:
658 		break;
659 	}
660 
661 	if (region_is_singular_or_empty(&damage->region) ||
662 	    box_contains_region(box, &damage->region)) {
663 		_pixman_region_union_box(&damage->region, box);
664 		assert(damage->region.extents.x2 > damage->region.extents.x1);
665 		assert(damage->region.extents.y2 > damage->region.extents.y1);
666 		damage_union(damage, box);
667 		return damage;
668 	}
669 
670 	if (pixman_region_contains_rectangle(&damage->region,
671 					     (BoxPtr)box) == PIXMAN_REGION_IN)
672 		return damage;
673 
674 	damage_union(damage, box);
675 	return _sna_damage_create_elt(damage, box, 1);
676 }
677 
__sna_damage_add(struct sna_damage * damage,RegionPtr region)678 inline static struct sna_damage *__sna_damage_add(struct sna_damage *damage,
679 						  RegionPtr region)
680 {
681 	assert(RegionNotEmpty(region));
682 
683 	if (!damage) {
684 		damage = _sna_damage_create();
685 		if (damage == NULL)
686 			return NULL;
687 	} else switch (damage->mode) {
688 	case DAMAGE_ALL:
689 		return damage;
690 	case DAMAGE_SUBTRACT:
691 		__sna_damage_reduce(damage);
692 	case DAMAGE_ADD:
693 		break;
694 	}
695 
696 	if (region_is_singular(region))
697 		return __sna_damage_add_box(damage, &region->extents);
698 
699 	if (region_is_singular_or_empty(&damage->region)) {
700 		pixman_region_union(&damage->region, &damage->region, region);
701 		assert(damage->region.extents.x2 > damage->region.extents.x1);
702 		assert(damage->region.extents.y2 > damage->region.extents.y1);
703 		damage_union(damage, &region->extents);
704 		return damage;
705 	}
706 
707 	if (pixman_region_contains_rectangle(&damage->region,
708 					     &region->extents) == PIXMAN_REGION_IN)
709 		return damage;
710 
711 	damage_union(damage, &region->extents);
712 	return _sna_damage_create_elt(damage,
713 				      region_rects(region),
714 				      region_num_rects(region));
715 }
716 
717 #if HAS_DEBUG_FULL
_sna_damage_add(struct sna_damage * damage,RegionPtr region)718 fastcall struct sna_damage *_sna_damage_add(struct sna_damage *damage,
719 					    RegionPtr region)
720 {
721 	char region_buf[120];
722 	char damage_buf[1000];
723 
724 	DBG(("%s(%s + %s)\n", __FUNCTION__,
725 	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage),
726 	     _debug_describe_region(region_buf, sizeof(region_buf), region)));
727 
728 	damage = __sna_damage_add(damage, region);
729 
730 	DBG(("  = %s\n",
731 	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage)));
732 	assert(region_num_rects(&damage->region));
733 	assert(damage->region.extents.x2 > damage->region.extents.x1);
734 	assert(damage->region.extents.y2 > damage->region.extents.y1);
735 
736 	return damage;
737 }
738 #else
_sna_damage_add(struct sna_damage * damage,RegionPtr region)739 fastcall struct sna_damage *_sna_damage_add(struct sna_damage *damage,
740 					    RegionPtr region)
741 {
742 	return __sna_damage_add(damage, region);
743 }
744 #endif
745 
746 inline static struct sna_damage *
__sna_damage_add_boxes(struct sna_damage * damage,const BoxRec * box,int n,int16_t dx,int16_t dy)747 __sna_damage_add_boxes(struct sna_damage *damage,
748 		       const BoxRec *box, int n,
749 		       int16_t dx, int16_t dy)
750 {
751 	BoxRec extents;
752 	int i;
753 
754 	assert(n);
755 
756 	if (!damage) {
757 		damage = _sna_damage_create();
758 		if (damage == NULL)
759 			return NULL;
760 	} else switch (damage->mode) {
761 	case DAMAGE_ALL:
762 		return damage;
763 	case DAMAGE_SUBTRACT:
764 		__sna_damage_reduce(damage);
765 	case DAMAGE_ADD:
766 		break;
767 	}
768 
769 	assert(box[0].x2 > box[0].x1 && box[0].y2 > box[0].y1);
770 	extents = box[0];
771 	for (i = 1; i < n; i++) {
772 		assert(box[i].x2 > box[i].x1 && box[i].y2 > box[i].y1);
773 		if (extents.x1 > box[i].x1)
774 			extents.x1 = box[i].x1;
775 		if (extents.x2 < box[i].x2)
776 			extents.x2 = box[i].x2;
777 		if (extents.y1 > box[i].y1)
778 			extents.y1 = box[i].y1;
779 		if (extents.y2 < box[i].y2)
780 			extents.y2 = box[i].y2;
781 	}
782 
783 	assert(extents.y2 > extents.y1 && extents.x2 > extents.x1);
784 
785 	extents.x1 += dx;
786 	extents.x2 += dx;
787 	extents.y1 += dy;
788 	extents.y2 += dy;
789 
790 	if (n == 1)
791 		return __sna_damage_add_box(damage, &extents);
792 
793 	if (pixman_region_contains_rectangle(&damage->region,
794 					     &extents) == PIXMAN_REGION_IN)
795 		return damage;
796 
797 	damage_union(damage, &extents);
798 	return _sna_damage_create_elt_from_boxes(damage, box, n, dx, dy);
799 }
800 
801 #if HAS_DEBUG_FULL
_sna_damage_add_boxes(struct sna_damage * damage,const BoxRec * b,int n,int16_t dx,int16_t dy)802 struct sna_damage *_sna_damage_add_boxes(struct sna_damage *damage,
803 					 const BoxRec *b, int n,
804 					 int16_t dx, int16_t dy)
805 {
806 	char damage_buf[1000];
807 
808 	DBG(("%s(%s + [(%d, %d), (%d, %d) ... x %d])\n", __FUNCTION__,
809 	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage),
810 	     b->x1, b->y1, b->x2, b->y2, n));
811 
812 	damage = __sna_damage_add_boxes(damage, b, n, dx, dy);
813 
814 	DBG(("  = %s\n",
815 	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage)));
816 	if (region_num_rects(&damage->region)) {
817 		assert(damage->region.extents.x2 > damage->region.extents.x1);
818 		assert(damage->region.extents.y2 > damage->region.extents.y1);
819 	}
820 
821 	return damage;
822 }
823 #else
_sna_damage_add_boxes(struct sna_damage * damage,const BoxRec * b,int n,int16_t dx,int16_t dy)824 struct sna_damage *_sna_damage_add_boxes(struct sna_damage *damage,
825 					 const BoxRec *b, int n,
826 					 int16_t dx, int16_t dy)
827 {
828 	return __sna_damage_add_boxes(damage, b, n, dx, dy);
829 }
830 #endif
831 
832 inline static struct sna_damage *
__sna_damage_add_rectangles(struct sna_damage * damage,const xRectangle * r,int n,int16_t dx,int16_t dy)833 __sna_damage_add_rectangles(struct sna_damage *damage,
834 			    const xRectangle *r, int n,
835 			    int16_t dx, int16_t dy)
836 {
837 	BoxRec extents;
838 	int i;
839 
840 	assert(n);
841 
842 	assert(r[0].width && r[0].height);
843 	extents.x1 = r[0].x;
844 	extents.x2 = r[0].x + r[0].width;
845 	extents.y1 = r[0].y;
846 	extents.y2 = r[0].y + r[0].height;
847 	for (i = 1; i < n; i++) {
848 		assert(r[i].width && r[i].height);
849 		if (extents.x1 > r[i].x)
850 			extents.x1 = r[i].x;
851 		if (extents.x2 < r[i].x + r[i].width)
852 			extents.x2 = r[i].x + r[i].width;
853 		if (extents.y1 > r[i].y)
854 			extents.y1 = r[i].y;
855 		if (extents.y2 < r[i].y + r[i].height)
856 			extents.y2 = r[i].y + r[i].height;
857 	}
858 
859 	assert(extents.y2 > extents.y1 && extents.x2 > extents.x1);
860 
861 	extents.x1 += dx;
862 	extents.x2 += dx;
863 	extents.y1 += dy;
864 	extents.y2 += dy;
865 
866 	if (n == 1)
867 		return __sna_damage_add_box(damage, &extents);
868 
869 	if (!damage) {
870 		damage = _sna_damage_create();
871 		if (damage == NULL)
872 			return NULL;
873 	} else switch (damage->mode) {
874 	case DAMAGE_ALL:
875 		return damage;
876 	case DAMAGE_SUBTRACT:
877 		__sna_damage_reduce(damage);
878 	case DAMAGE_ADD:
879 		break;
880 	}
881 
882 	if (pixman_region_contains_rectangle(&damage->region,
883 					     &extents) == PIXMAN_REGION_IN)
884 		return damage;
885 
886 	damage_union(damage, &extents);
887 	return _sna_damage_create_elt_from_rectangles(damage, r, n, dx, dy);
888 }
889 
890 #if HAS_DEBUG_FULL
_sna_damage_add_rectangles(struct sna_damage * damage,const xRectangle * r,int n,int16_t dx,int16_t dy)891 struct sna_damage *_sna_damage_add_rectangles(struct sna_damage *damage,
892 					      const xRectangle *r, int n,
893 					      int16_t dx, int16_t dy)
894 {
895 	char damage_buf[1000];
896 
897 	DBG(("%s(%s + [(%d, %d)x(%d, %d) ... x %d])\n", __FUNCTION__,
898 	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage),
899 	     r->x, r->y, r->width, r->height, n));
900 
901 	damage = __sna_damage_add_rectangles(damage, r, n, dx, dy);
902 
903 	DBG(("  = %s\n",
904 	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage)));
905 	if (region_num_rects(&damage->region)) {
906 		assert(damage->region.extents.x2 > damage->region.extents.x1);
907 		assert(damage->region.extents.y2 > damage->region.extents.y1);
908 	}
909 
910 	return damage;
911 }
912 #else
_sna_damage_add_rectangles(struct sna_damage * damage,const xRectangle * r,int n,int16_t dx,int16_t dy)913 struct sna_damage *_sna_damage_add_rectangles(struct sna_damage *damage,
914 					      const xRectangle *r, int n,
915 					      int16_t dx, int16_t dy)
916 {
917 	return __sna_damage_add_rectangles(damage, r, n, dx, dy);
918 }
919 #endif
920 
921 /* XXX pass in extents? */
922 inline static struct sna_damage *
__sna_damage_add_points(struct sna_damage * damage,const DDXPointRec * p,int n,int16_t dx,int16_t dy)923 __sna_damage_add_points(struct sna_damage *damage,
924 			const DDXPointRec *p, int n,
925 			int16_t dx, int16_t dy)
926 {
927 	BoxRec extents;
928 	int i;
929 
930 	assert(n);
931 
932 	extents.x2 = extents.x1 = p[0].x;
933 	extents.y2 = extents.y1 = p[0].y;
934 	for (i = 1; i < n; i++) {
935 		if (extents.x1 > p[i].x)
936 			extents.x1 = p[i].x;
937 		else if (extents.x2 < p[i].x)
938 			extents.x2 = p[i].x;
939 		if (extents.y1 > p[i].y)
940 			extents.y1 = p[i].y;
941 		else if (extents.y2 < p[i].y)
942 			extents.y2 = p[i].y;
943 	}
944 
945 	extents.x1 += dx;
946 	extents.x2 += dx + 1;
947 	extents.y1 += dy;
948 	extents.y2 += dy + 1;
949 
950 	if (n == 1)
951 		return __sna_damage_add_box(damage, &extents);
952 
953 	if (!damage) {
954 		damage = _sna_damage_create();
955 		if (damage == NULL)
956 			return NULL;
957 	} else switch (damage->mode) {
958 	case DAMAGE_ALL:
959 		return damage;
960 	case DAMAGE_SUBTRACT:
961 		__sna_damage_reduce(damage);
962 	case DAMAGE_ADD:
963 		break;
964 	}
965 
966 	if (pixman_region_contains_rectangle(&damage->region,
967 					     &extents) == PIXMAN_REGION_IN)
968 		return damage;
969 
970 	damage_union(damage, &extents);
971 	return _sna_damage_create_elt_from_points(damage, p, n, dx, dy);
972 }
973 
974 #if HAS_DEBUG_FULL
_sna_damage_add_points(struct sna_damage * damage,const DDXPointRec * p,int n,int16_t dx,int16_t dy)975 struct sna_damage *_sna_damage_add_points(struct sna_damage *damage,
976 					  const DDXPointRec *p, int n,
977 					  int16_t dx, int16_t dy)
978 {
979 	char damage_buf[1000];
980 
981 	DBG(("%s(%s + [(%d, %d) ... x %d])\n", __FUNCTION__,
982 	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage),
983 	     p->x, p->y, n));
984 
985 	damage = __sna_damage_add_points(damage, p, n, dx, dy);
986 
987 	DBG(("  = %s\n",
988 	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage)));
989 	if (region_num_rects(&damage->region)) {
990 		assert(damage->region.extents.x2 > damage->region.extents.x1);
991 		assert(damage->region.extents.y2 > damage->region.extents.y1);
992 	}
993 
994 	return damage;
995 }
996 #else
_sna_damage_add_points(struct sna_damage * damage,const DDXPointRec * p,int n,int16_t dx,int16_t dy)997 struct sna_damage *_sna_damage_add_points(struct sna_damage *damage,
998 					  const DDXPointRec *p, int n,
999 					  int16_t dx, int16_t dy)
1000 {
1001 	return __sna_damage_add_points(damage, p, n, dx, dy);
1002 }
1003 #endif
1004 
1005 #if HAS_DEBUG_FULL
_sna_damage_add_box(struct sna_damage * damage,const BoxRec * box)1006 fastcall struct sna_damage *_sna_damage_add_box(struct sna_damage *damage,
1007 						const BoxRec *box)
1008 {
1009 	char damage_buf[1000];
1010 
1011 	DBG(("%s(%s + [(%d, %d), (%d, %d)])\n", __FUNCTION__,
1012 	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage),
1013 	     box->x1, box->y1, box->x2, box->y2));
1014 
1015 	damage = __sna_damage_add_box(damage, box);
1016 
1017 	DBG(("  = %s\n",
1018 	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage)));
1019 	assert(region_num_rects(&damage->region));
1020 	assert(damage->region.extents.x2 > damage->region.extents.x1);
1021 	assert(damage->region.extents.y2 > damage->region.extents.y1);
1022 
1023 	return damage;
1024 }
1025 #else
_sna_damage_add_box(struct sna_damage * damage,const BoxRec * box)1026 fastcall struct sna_damage *_sna_damage_add_box(struct sna_damage *damage,
1027 						const BoxRec *box)
1028 {
1029 	return __sna_damage_add_box(damage, box);
1030 }
1031 #endif
1032 
__sna_damage_all(struct sna_damage * damage,int width,int height)1033 struct sna_damage *__sna_damage_all(struct sna_damage *damage,
1034 				    int width, int height)
1035 {
1036 	DBG(("%s(%d, %d)\n", __FUNCTION__, width, height));
1037 
1038 	if (damage) {
1039 		pixman_region_fini(&damage->region);
1040 		free_list(&damage->embedded_box.list);
1041 		reset_embedded_box(damage);
1042 	} else {
1043 		damage = _sna_damage_create();
1044 		if (damage == NULL)
1045 			return NULL;
1046 	}
1047 
1048 	pixman_region_init_rect(&damage->region, 0, 0, width, height);
1049 	damage->extents = damage->region.extents;
1050 	damage->mode = DAMAGE_ALL;
1051 
1052 	return damage;
1053 }
1054 
_sna_damage_is_all(struct sna_damage * damage,int width,int height)1055 struct sna_damage *_sna_damage_is_all(struct sna_damage *damage,
1056 				      int width, int height)
1057 {
1058 	DBG(("%s(%d, %d)%s?\n", __FUNCTION__, width, height,
1059 	     damage->dirty ? "*" : ""));
1060 	DBG(("%s: (%d, %d), (%d, %d)\n", __FUNCTION__,
1061 	     damage->extents.x1, damage->extents.y1,
1062 	     damage->extents.x2, damage->extents.y2));
1063 
1064 	assert(damage->mode == DAMAGE_ADD);
1065 	assert(damage->extents.x1 == 0 &&
1066 	       damage->extents.y1 == 0 &&
1067 	       damage->extents.x2 == width &&
1068 	       damage->extents.y2 == height);
1069 
1070 	if (damage->dirty) {
1071 		__sna_damage_reduce(damage);
1072 		assert(RegionNotEmpty(&damage->region));
1073 	}
1074 
1075 	if (damage->region.data) {
1076 		DBG(("%s: no, not singular\n", __FUNCTION__));
1077 		return damage;
1078 	}
1079 
1080 	assert(damage->extents.x1 == 0 &&
1081 	       damage->extents.y1 == 0 &&
1082 	       damage->extents.x2 == width &&
1083 	       damage->extents.y2 == height);
1084 
1085 	return __sna_damage_all(damage, width, height);
1086 }
1087 
box_contains(const BoxRec * a,const BoxRec * b)1088 static bool box_contains(const BoxRec *a, const BoxRec *b)
1089 {
1090 	if (b->x1 < a->x1 || b->x2 > a->x2)
1091 		return false;
1092 
1093 	if (b->y1 < a->y1 || b->y2 > a->y2)
1094 		return false;
1095 
1096 	return true;
1097 }
1098 
__sna_damage_subtract(struct sna_damage * damage,RegionPtr region)1099 static struct sna_damage *__sna_damage_subtract(struct sna_damage *damage,
1100 						RegionPtr region)
1101 {
1102 	if (damage == NULL)
1103 		return NULL;
1104 
1105 	if (RegionNil(&damage->region)) {
1106 no_damage:
1107 		__sna_damage_destroy(damage);
1108 		return NULL;
1109 	}
1110 
1111 	assert(RegionNotEmpty(region));
1112 
1113 	if (!sna_damage_overlaps_box(damage, &region->extents))
1114 		return damage;
1115 
1116 	if (region_is_singular(region) &&
1117 	    box_contains(&region->extents, &damage->extents))
1118 		goto no_damage;
1119 
1120 	if (damage->mode == DAMAGE_ALL) {
1121 		pixman_region_subtract(&damage->region,
1122 				       &damage->region,
1123 				       region);
1124 		if (damage->region.extents.x2 <= damage->region.extents.x1 ||
1125 		    damage->region.extents.y2 <= damage->region.extents.y1)
1126 			goto no_damage;
1127 
1128 		damage->extents = damage->region.extents;
1129 		damage->mode = DAMAGE_ADD;
1130 		return damage;
1131 	}
1132 
1133 	if (damage->mode != DAMAGE_SUBTRACT) {
1134 		if (damage->dirty) {
1135 			__sna_damage_reduce(damage);
1136 			assert(RegionNotEmpty(&damage->region));
1137 		}
1138 
1139 		if (pixman_region_equal(region, &damage->region))
1140 			goto no_damage;
1141 
1142 		if (region_is_singular(&damage->region) &&
1143 		    region_is_singular(region)) {
1144 			pixman_region_subtract(&damage->region,
1145 					       &damage->region,
1146 					       region);
1147 			if (damage->region.extents.x2 <= damage->region.extents.x1 ||
1148 			    damage->region.extents.y2 <= damage->region.extents.y1)
1149 				goto no_damage;
1150 
1151 			damage->extents = damage->region.extents;
1152 			assert(pixman_region_not_empty(&damage->region));
1153 			return damage;
1154 		}
1155 
1156 		damage->mode = DAMAGE_SUBTRACT;
1157 	}
1158 
1159 	return _sna_damage_create_elt(damage,
1160 				      region_rects(region),
1161 				      region_num_rects(region));
1162 }
1163 
1164 #if HAS_DEBUG_FULL
_sna_damage_subtract(struct sna_damage * damage,RegionPtr region)1165 fastcall struct sna_damage *_sna_damage_subtract(struct sna_damage *damage,
1166 						 RegionPtr region)
1167 {
1168 	char damage_buf[1000];
1169 	char region_buf[120];
1170 
1171 	DBG(("%s(%s - %s)...\n", __FUNCTION__,
1172 	       _debug_describe_damage(damage_buf, sizeof(damage_buf), damage),
1173 	       _debug_describe_region(region_buf, sizeof(region_buf), region)));
1174 
1175 	damage = __sna_damage_subtract(damage, region);
1176 
1177 	DBG(("  = %s\n",
1178 	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage)));
1179 
1180 	return damage;
1181 }
1182 #else
_sna_damage_subtract(struct sna_damage * damage,RegionPtr region)1183 fastcall struct sna_damage *_sna_damage_subtract(struct sna_damage *damage,
1184 						 RegionPtr region)
1185 {
1186 	return __sna_damage_subtract(damage, region);
1187 }
1188 #endif
1189 
__sna_damage_subtract_box(struct sna_damage * damage,const BoxRec * box)1190 inline static struct sna_damage *__sna_damage_subtract_box(struct sna_damage *damage,
1191 							   const BoxRec *box)
1192 {
1193 	assert(box->x2 > box->x1 && box->y2 > box->y1);
1194 
1195 	if (damage == NULL)
1196 		return NULL;
1197 
1198 	if (RegionNil(&damage->region)) {
1199 		__sna_damage_destroy(damage);
1200 		return NULL;
1201 	}
1202 
1203 	if (!sna_damage_overlaps_box(damage, box))
1204 		return damage;
1205 
1206 	if (box_contains(box, &damage->extents)) {
1207 		__sna_damage_destroy(damage);
1208 		return NULL;
1209 	}
1210 
1211 	if (damage->mode != DAMAGE_SUBTRACT) {
1212 		if (damage->dirty) {
1213 			__sna_damage_reduce(damage);
1214 			assert(RegionNotEmpty(&damage->region));
1215 		}
1216 
1217 		if (region_is_singular(&damage->region)) {
1218 			pixman_region16_t region;
1219 
1220 			pixman_region_init_rects(&region, box, 1);
1221 			pixman_region_subtract(&damage->region,
1222 					       &damage->region,
1223 					       &region);
1224 			damage->extents = damage->region.extents;
1225 			damage->mode = DAMAGE_ADD;
1226 			return damage;
1227 		}
1228 
1229 		damage->mode = DAMAGE_SUBTRACT;
1230 	}
1231 
1232 	return _sna_damage_create_elt(damage, box, 1);
1233 }
1234 
1235 #if HAS_DEBUG_FULL
_sna_damage_subtract_box(struct sna_damage * damage,const BoxRec * box)1236 fastcall struct sna_damage *_sna_damage_subtract_box(struct sna_damage *damage,
1237 						     const BoxRec *box)
1238 {
1239 	char damage_buf[1000];
1240 
1241 	DBG(("%s(%s - (%d, %d), (%d, %d))...\n", __FUNCTION__,
1242 	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage),
1243 	     box->x1, box->y1, box->x2, box->y2));
1244 
1245 	damage = __sna_damage_subtract_box(damage, box);
1246 
1247 	DBG(("  = %s\n",
1248 	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage)));
1249 
1250 	return damage;
1251 }
1252 #else
_sna_damage_subtract_box(struct sna_damage * damage,const BoxRec * box)1253 fastcall struct sna_damage *_sna_damage_subtract_box(struct sna_damage *damage,
1254 						     const BoxRec *box)
1255 {
1256 	return __sna_damage_subtract_box(damage, box);
1257 }
1258 #endif
1259 
__sna_damage_subtract_boxes(struct sna_damage * damage,const BoxRec * box,int n,int dx,int dy)1260 static struct sna_damage *__sna_damage_subtract_boxes(struct sna_damage *damage,
1261 						      const BoxRec *box, int n,
1262 						      int dx, int dy)
1263 {
1264 	BoxRec extents;
1265 	int i;
1266 
1267 	if (damage == NULL)
1268 		return NULL;
1269 
1270 	if (RegionNil(&damage->region)) {
1271 		__sna_damage_destroy(damage);
1272 		return NULL;
1273 	}
1274 
1275 	assert(n);
1276 
1277 	assert(box[0].x2 > box[0].x1 && box[0].y2 > box[0].y1);
1278 	extents = box[0];
1279 	for (i = 1; i < n; i++) {
1280 		assert(box[i].x2 > box[i].x1 && box[i].y2 > box[i].y1);
1281 		if (extents.x1 > box[i].x1)
1282 			extents.x1 = box[i].x1;
1283 		if (extents.x2 < box[i].x2)
1284 			extents.x2 = box[i].x2;
1285 		if (extents.y1 > box[i].y1)
1286 			extents.y1 = box[i].y1;
1287 		if (extents.y2 < box[i].y2)
1288 			extents.y2 = box[i].y2;
1289 	}
1290 
1291 	assert(extents.y2 > extents.y1 && extents.x2 > extents.x1);
1292 
1293 	extents.x1 += dx;
1294 	extents.x2 += dx;
1295 	extents.y1 += dy;
1296 	extents.y2 += dy;
1297 
1298 	if (!sna_damage_overlaps_box(damage, &extents))
1299 		return damage;
1300 
1301 	if (n == 1)
1302 		return __sna_damage_subtract_box(damage, &extents);
1303 
1304 	if (damage->mode != DAMAGE_SUBTRACT) {
1305 		if (damage->dirty) {
1306 			__sna_damage_reduce(damage);
1307 			assert(RegionNotEmpty(&damage->region));
1308 		}
1309 
1310 		damage->mode = DAMAGE_SUBTRACT;
1311 	}
1312 
1313 	return _sna_damage_create_elt_from_boxes(damage, box, n, dx, dy);
1314 }
1315 
1316 #if HAS_DEBUG_FULL
_sna_damage_subtract_boxes(struct sna_damage * damage,const BoxRec * box,int n,int dx,int dy)1317 fastcall struct sna_damage *_sna_damage_subtract_boxes(struct sna_damage *damage,
1318 						       const BoxRec *box, int n,
1319 						       int dx, int dy)
1320 {
1321 	char damage_buf[1000];
1322 
1323 	DBG(("%s(%s - [(%d,%d), (%d,%d)...x%d])...\n", __FUNCTION__,
1324 	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage),
1325 	     box->x1 + dx, box->y1 + dy,
1326 	     box->x2 + dx, box->y2 + dy,
1327 	     n));
1328 
1329 	damage = __sna_damage_subtract_boxes(damage, box, n, dx, dy);
1330 
1331 	DBG(("  = %s\n",
1332 	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage)));
1333 
1334 	return damage;
1335 }
1336 #else
_sna_damage_subtract_boxes(struct sna_damage * damage,const BoxRec * box,int n,int dx,int dy)1337 fastcall struct sna_damage *_sna_damage_subtract_boxes(struct sna_damage *damage,
1338 						       const BoxRec *box, int n,
1339 						       int dx, int dy)
1340 {
1341 	return __sna_damage_subtract_boxes(damage, box, n, dx, dy);
1342 }
1343 #endif
1344 
__sna_damage_contains_box(struct sna_damage ** _damage,const BoxRec * box)1345 static int __sna_damage_contains_box(struct sna_damage **_damage,
1346 				     const BoxRec *box)
1347 {
1348 	struct sna_damage *damage = *_damage;
1349 	const BoxRec *b;
1350 	int n, count, ret;
1351 
1352 	if (damage->mode == DAMAGE_ALL)
1353 		return PIXMAN_REGION_IN;
1354 
1355 	if (!sna_damage_overlaps_box(damage, box))
1356 		return PIXMAN_REGION_OUT;
1357 
1358 	ret = pixman_region_contains_rectangle(&damage->region, (BoxPtr)box);
1359 	if (!damage->dirty)
1360 		return ret;
1361 
1362 	if (damage->mode == DAMAGE_ADD) {
1363 		if (ret == PIXMAN_REGION_IN)
1364 			return ret;
1365 
1366 		count = damage->embedded_box.size;
1367 		if (list_is_empty(&damage->embedded_box.list))
1368 			count -= damage->remain;
1369 
1370 		b = damage->embedded_box.box;
1371 		for (n = 0; n < count; n++) {
1372 			if (box_contains(&b[n], box))
1373 				return PIXMAN_REGION_IN;
1374 		}
1375 	} else {
1376 		if (ret == PIXMAN_REGION_OUT)
1377 			return ret;
1378 
1379 		count = damage->embedded_box.size;
1380 		if (list_is_empty(&damage->embedded_box.list))
1381 			count -= damage->remain;
1382 
1383 		b = damage->embedded_box.box;
1384 		for (n = 0; n < count; n++) {
1385 			if (box_contains(&b[n], box))
1386 				return PIXMAN_REGION_OUT;
1387 		}
1388 	}
1389 
1390 	__sna_damage_reduce(damage);
1391 	if (!pixman_region_not_empty(&damage->region)) {
1392 		__sna_damage_destroy(damage);
1393 		*_damage = NULL;
1394 		return PIXMAN_REGION_OUT;
1395 	}
1396 
1397 	return pixman_region_contains_rectangle(&damage->region, (BoxPtr)box);
1398 }
1399 
1400 #if HAS_DEBUG_FULL
_sna_damage_contains_box(struct sna_damage ** damage,const BoxRec * box)1401 int _sna_damage_contains_box(struct sna_damage **damage,
1402 			     const BoxRec *box)
1403 {
1404 	char damage_buf[1000];
1405 	int ret;
1406 
1407 	DBG(("%s(%s, [(%d, %d), (%d, %d)])\n", __FUNCTION__,
1408 	     _debug_describe_damage(damage_buf, sizeof(damage_buf), *damage),
1409 	     box->x1, box->y1, box->x2, box->y2));
1410 
1411 	ret = __sna_damage_contains_box(damage, box);
1412 	DBG(("  = %d", ret));
1413 	if (ret)
1414 		DBG((" [(%d, %d), (%d, %d)...]",
1415 		     box->x1, box->y1, box->x2, box->y2));
1416 	DBG(("\n"));
1417 
1418 	return ret;
1419 }
1420 #else
_sna_damage_contains_box(struct sna_damage ** damage,const BoxRec * box)1421 int _sna_damage_contains_box(struct sna_damage **damage,
1422 			     const BoxRec *box)
1423 {
1424 	return __sna_damage_contains_box(damage, box);
1425 }
1426 #endif
1427 
box_overlaps(const BoxRec * a,const BoxRec * b)1428 static bool box_overlaps(const BoxRec *a, const BoxRec *b)
1429 {
1430 	return (a->x1 < b->x2 && a->x2 > b->x1 &&
1431 		a->y1 < b->y2 && a->y2 > b->y1);
1432 }
1433 
_sna_damage_contains_box__no_reduce(const struct sna_damage * damage,const BoxRec * box)1434 bool _sna_damage_contains_box__no_reduce(const struct sna_damage *damage,
1435 					 const BoxRec *box)
1436 {
1437 	int n, count;
1438 	const BoxRec *b;
1439 
1440 	assert(damage && damage->mode != DAMAGE_ALL);
1441 	if (!box_contains(&damage->extents, box))
1442 		return false;
1443 
1444 	n = pixman_region_contains_rectangle((pixman_region16_t *)&damage->region, (BoxPtr)box);
1445 	if (!damage->dirty)
1446 		return n == PIXMAN_REGION_IN;
1447 
1448 	if (damage->mode == DAMAGE_ADD) {
1449 		if (n == PIXMAN_REGION_IN)
1450 			return true;
1451 
1452 		count = damage->embedded_box.size;
1453 		if (list_is_empty(&damage->embedded_box.list))
1454 			count -= damage->remain;
1455 
1456 		b = damage->embedded_box.box;
1457 		for (n = 0; n < count; n++) {
1458 			if (box_contains(&b[n], box))
1459 				return true;
1460 		}
1461 
1462 		return false;
1463 	} else {
1464 		if (n != PIXMAN_REGION_IN)
1465 			return false;
1466 
1467 		if (!list_is_empty(&damage->embedded_box.list))
1468 			return false;
1469 
1470 		count = damage->embedded_box.size - damage->remain;
1471 		b = damage->embedded_box.box;
1472 		for (n = 0; n < count; n++) {
1473 			if (box_overlaps(&b[n], box))
1474 				return false;
1475 		}
1476 
1477 		return true;
1478 	}
1479 }
1480 
__sna_damage_intersect(struct sna_damage * damage,RegionPtr region,RegionPtr result)1481 static bool __sna_damage_intersect(struct sna_damage *damage,
1482 				   RegionPtr region, RegionPtr result)
1483 {
1484 	assert(damage && damage->mode != DAMAGE_ALL);
1485 	assert(RegionNotEmpty(region));
1486 
1487 	if (region->extents.x2 <= damage->extents.x1 ||
1488 	    region->extents.x1 >= damage->extents.x2)
1489 		return false;
1490 
1491 	if (region->extents.y2 <= damage->extents.y1 ||
1492 	    region->extents.y1 >= damage->extents.y2)
1493 		return false;
1494 
1495 	if (damage->dirty)
1496 		__sna_damage_reduce(damage);
1497 
1498 	if (!pixman_region_not_empty(&damage->region))
1499 		return false;
1500 
1501 	RegionNull(result);
1502 	RegionIntersect(result, &damage->region, region);
1503 
1504 	return RegionNotEmpty(result);
1505 }
1506 
1507 #if HAS_DEBUG_FULL
_sna_damage_intersect(struct sna_damage * damage,RegionPtr region,RegionPtr result)1508 bool _sna_damage_intersect(struct sna_damage *damage,
1509 			   RegionPtr region, RegionPtr result)
1510 {
1511 	char damage_buf[1000];
1512 	char region_buf[120];
1513 	bool ret;
1514 
1515 	DBG(("%s(%s, %s)...\n", __FUNCTION__,
1516 	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage),
1517 	     _debug_describe_region(region_buf, sizeof(region_buf), region)));
1518 
1519 	ret = __sna_damage_intersect(damage, region, result);
1520 	if (ret)
1521 		DBG(("  = %s\n",
1522 		     _debug_describe_region(region_buf, sizeof(region_buf), result)));
1523 	else
1524 		DBG(("  = none\n"));
1525 
1526 	return ret;
1527 }
1528 #else
_sna_damage_intersect(struct sna_damage * damage,RegionPtr region,RegionPtr result)1529 bool _sna_damage_intersect(struct sna_damage *damage,
1530 			  RegionPtr region, RegionPtr result)
1531 {
1532 	return __sna_damage_intersect(damage, region, result);
1533 }
1534 #endif
1535 
__sna_damage_get_boxes(struct sna_damage * damage,const BoxRec ** boxes)1536 static int __sna_damage_get_boxes(struct sna_damage *damage, const BoxRec **boxes)
1537 {
1538 	assert(damage && damage->mode != DAMAGE_ALL);
1539 
1540 	if (damage->dirty)
1541 		__sna_damage_reduce(damage);
1542 
1543 	assert(!damage->dirty);
1544 	assert(damage->mode == DAMAGE_ADD);
1545 
1546 	*boxes = region_rects(&damage->region);
1547 	return region_num_rects(&damage->region);
1548 }
1549 
_sna_damage_reduce(struct sna_damage * damage)1550 struct sna_damage *_sna_damage_reduce(struct sna_damage *damage)
1551 {
1552 	DBG(("%s\n", __FUNCTION__));
1553 
1554 	__sna_damage_reduce(damage);
1555 
1556 	assert(!damage->dirty);
1557 	assert(damage->mode == DAMAGE_ADD);
1558 
1559 	if (!pixman_region_not_empty(&damage->region)) {
1560 		__sna_damage_destroy(damage);
1561 		damage = NULL;
1562 	}
1563 
1564 	return damage;
1565 }
1566 
1567 #if HAS_DEBUG_FULL
_sna_damage_get_boxes(struct sna_damage * damage,const BoxRec ** boxes)1568 int _sna_damage_get_boxes(struct sna_damage *damage, const BoxRec **boxes)
1569 {
1570 	char damage_buf[1000];
1571 	int count;
1572 
1573 	DBG(("%s(%s)...\n", __FUNCTION__,
1574 	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage)));
1575 
1576 	count = __sna_damage_get_boxes(damage, boxes);
1577 	DBG(("  = %d\n", count));
1578 
1579 	return count;
1580 }
1581 #else
_sna_damage_get_boxes(struct sna_damage * damage,const BoxRec ** boxes)1582 int _sna_damage_get_boxes(struct sna_damage *damage, const BoxRec **boxes)
1583 {
1584 	return __sna_damage_get_boxes(damage, boxes);
1585 }
1586 #endif
1587 
_sna_damage_combine(struct sna_damage * l,struct sna_damage * r,int dx,int dy)1588 struct sna_damage *_sna_damage_combine(struct sna_damage *l,
1589 				       struct sna_damage *r,
1590 				       int dx, int dy)
1591 {
1592 	if (r->dirty)
1593 		__sna_damage_reduce(r);
1594 
1595 	if (pixman_region_not_empty(&r->region)) {
1596 		pixman_region_translate(&r->region, dx, dy);
1597 		l = __sna_damage_add(l, &r->region);
1598 	}
1599 
1600 	return l;
1601 }
1602 
__sna_damage_destroy(struct sna_damage * damage)1603 void __sna_damage_destroy(struct sna_damage *damage)
1604 {
1605 	free_list(&damage->embedded_box.list);
1606 
1607 	pixman_region_fini(&damage->region);
1608 	*(void **)damage = __freed_damage;
1609 	__freed_damage = damage;
1610 }
1611 
1612 #if TEST_DAMAGE && HAS_DEBUG_FULL
1613 struct sna_damage_selftest{
1614 	int width, height;
1615 };
1616 
st_damage_init_random_box(struct sna_damage_selftest * test,BoxPtr box)1617 static void st_damage_init_random_box(struct sna_damage_selftest *test,
1618 				      BoxPtr box)
1619 {
1620 	int x, y, w, h;
1621 
1622 	if (test->width == 1) {
1623 		x = 0, w = 1;
1624 	} else {
1625 		x = rand() % (test->width - 1);
1626 		w = 1 + rand() % (test->width - x - 1);
1627 	}
1628 
1629 	if (test->height == 1) {
1630 		y = 0, h = 1;
1631 	} else {
1632 		y = rand() % (test->height - 1);
1633 		h = 1 + rand() % (test->height - y - 1);
1634 	}
1635 
1636 	box->x1 = x;
1637 	box->x2 = x+w;
1638 
1639 	box->y1 = y;
1640 	box->y2 = y+h;
1641 }
1642 
st_damage_init_random_region1(struct sna_damage_selftest * test,pixman_region16_t * region)1643 static void st_damage_init_random_region1(struct sna_damage_selftest *test,
1644 					  pixman_region16_t *region)
1645 {
1646 	int x, y, w, h;
1647 
1648 	if (test->width == 1) {
1649 		x = 0, w = 1;
1650 	} else {
1651 		x = rand() % (test->width - 1);
1652 		w = 1 + rand() % (test->width - x - 1);
1653 	}
1654 
1655 	if (test->height == 1) {
1656 		y = 0, h = 1;
1657 	} else {
1658 		y = rand() % (test->height - 1);
1659 		h = 1 + rand() % (test->height - y - 1);
1660 	}
1661 
1662 	pixman_region_init_rect(region, x, y, w, h);
1663 }
1664 
st_damage_add(struct sna_damage_selftest * test,struct sna_damage ** damage,pixman_region16_t * region)1665 static void st_damage_add(struct sna_damage_selftest *test,
1666 			  struct sna_damage **damage,
1667 			  pixman_region16_t *region)
1668 {
1669 	pixman_region16_t tmp;
1670 
1671 	st_damage_init_random_region1(test, &tmp);
1672 
1673 	if (!DAMAGE_IS_ALL(*damage))
1674 		sna_damage_add(damage, &tmp);
1675 	pixman_region_union(region, region, &tmp);
1676 }
1677 
st_damage_add_box(struct sna_damage_selftest * test,struct sna_damage ** damage,pixman_region16_t * region)1678 static void st_damage_add_box(struct sna_damage_selftest *test,
1679 			      struct sna_damage **damage,
1680 			      pixman_region16_t *region)
1681 {
1682 	RegionRec r;
1683 
1684 	st_damage_init_random_box(test, &r.extents);
1685 	r.data = NULL;
1686 
1687 	if (!DAMAGE_IS_ALL(*damage))
1688 		sna_damage_add_box(damage, &r.extents);
1689 	pixman_region_union(region, region, &r);
1690 }
1691 
st_damage_subtract(struct sna_damage_selftest * test,struct sna_damage ** damage,pixman_region16_t * region)1692 static void st_damage_subtract(struct sna_damage_selftest *test,
1693 			       struct sna_damage **damage,
1694 			       pixman_region16_t *region)
1695 {
1696 	pixman_region16_t tmp;
1697 
1698 	st_damage_init_random_region1(test, &tmp);
1699 
1700 	sna_damage_subtract(damage, &tmp);
1701 	pixman_region_subtract(region, region, &tmp);
1702 }
1703 
st_damage_subtract_box(struct sna_damage_selftest * test,struct sna_damage ** damage,pixman_region16_t * region)1704 static void st_damage_subtract_box(struct sna_damage_selftest *test,
1705 				   struct sna_damage **damage,
1706 				   pixman_region16_t *region)
1707 {
1708 	RegionRec r;
1709 
1710 	st_damage_init_random_box(test, &r.extents);
1711 	r.data = NULL;
1712 
1713 	sna_damage_subtract_box(damage, &r.extents);
1714 	pixman_region_subtract(region, region, &r);
1715 }
1716 
st_damage_all(struct sna_damage_selftest * test,struct sna_damage ** damage,pixman_region16_t * region)1717 static void st_damage_all(struct sna_damage_selftest *test,
1718 			  struct sna_damage **damage,
1719 			  pixman_region16_t *region)
1720 {
1721 	pixman_region16_t tmp;
1722 
1723 	pixman_region_init_rect(&tmp, 0, 0, test->width, test->height);
1724 
1725 	if (!DAMAGE_IS_ALL(*damage))
1726 		sna_damage_all(damage, test->width, test->height);
1727 	pixman_region_union(region, region, &tmp);
1728 }
1729 
st_check_equal(struct sna_damage_selftest * test,struct sna_damage ** damage,pixman_region16_t * region)1730 static bool st_check_equal(struct sna_damage_selftest *test,
1731 			   struct sna_damage **damage,
1732 			   pixman_region16_t *region)
1733 {
1734 	int d_num, r_num;
1735 	BoxPtr d_boxes, r_boxes;
1736 
1737 	d_num = *damage ? sna_damage_get_boxes(*damage, &d_boxes) : 0;
1738 	r_boxes = pixman_region_rectangles(region, &r_num);
1739 
1740 	if (d_num != r_num) {
1741 		ERR(("%s: damage and ref contain different number of rectangles\n",
1742 		     __FUNCTION__));
1743 		return false;
1744 	}
1745 
1746 	if (memcmp(d_boxes, r_boxes, d_num*sizeof(BoxRec))) {
1747 		ERR(("%s: damage and ref contain different rectangles\n",
1748 		     __FUNCTION__));
1749 		return false;
1750 	}
1751 
1752 	return true;
1753 }
1754 
sna_damage_selftest(void)1755 void sna_damage_selftest(void)
1756 {
1757 	void (*const op[])(struct sna_damage_selftest *test,
1758 			   struct sna_damage **damage,
1759 			   pixman_region16_t *region) = {
1760 		st_damage_add,
1761 		st_damage_add_box,
1762 		st_damage_subtract,
1763 		st_damage_subtract_box,
1764 		st_damage_all
1765 	};
1766 	bool (*const check[])(struct sna_damage_selftest *test,
1767 			      struct sna_damage **damage,
1768 			      pixman_region16_t *region) = {
1769 		st_check_equal,
1770 		//st_check_contains,
1771 	};
1772 	char region_buf[120];
1773 	char damage_buf[1000];
1774 	int pass;
1775 
1776 	for (pass = 0; pass < 16384; pass++) {
1777 		struct sna_damage_selftest test;
1778 		struct sna_damage *damage;
1779 		pixman_region16_t ref;
1780 		int iter, i;
1781 
1782 		iter = 1 + rand() % (1 + (pass / 64));
1783 		DBG(("%s: pass %d, iters=%d\n", __FUNCTION__, pass, iter));
1784 
1785 		test.width = 1 + rand() % 2048;
1786 		test.height = 1 + rand() % 2048;
1787 
1788 		damage = _sna_damage_create();
1789 		pixman_region_init(&ref);
1790 
1791 		for (i = 0; i < iter; i++) {
1792 			op[rand() % ARRAY_SIZE(op)](&test, &damage, &ref);
1793 		}
1794 
1795 		if (!check[rand() % ARRAY_SIZE(check)](&test, &damage, &ref)) {
1796 			FatalError("%s: failed - region = %s, damage = %s\n", __FUNCTION__,
1797 				   _debug_describe_region(region_buf, sizeof(region_buf), &ref),
1798 				   _debug_describe_damage(damage_buf, sizeof(damage_buf), damage));
1799 		}
1800 
1801 		pixman_region_fini(&ref);
1802 		sna_damage_destroy(&damage);
1803 	}
1804 }
1805 #endif
1806 
_sna_damage_debug_get_region(struct sna_damage * damage,RegionRec * r)1807 void _sna_damage_debug_get_region(struct sna_damage *damage, RegionRec *r)
1808 {
1809 	int n, nboxes;
1810 	BoxPtr boxes;
1811 	struct sna_damage_box *iter;
1812 
1813 	RegionCopy(r, &damage->region);
1814 	if (!damage->dirty)
1815 		return;
1816 
1817 	nboxes = damage->embedded_box.size;
1818 	list_for_each_entry(iter, &damage->embedded_box.list, list)
1819 		nboxes += iter->size;
1820 	nboxes -= damage->remain;
1821 	if (nboxes == 0)
1822 		return;
1823 
1824 	if (nboxes == 1) {
1825 		pixman_region16_t tmp;
1826 
1827 		tmp.extents = damage->embedded_box.box[0];
1828 		tmp.data = NULL;
1829 
1830 		if (damage->mode == DAMAGE_ADD)
1831 			pixman_region_union(r, r, &tmp);
1832 		else
1833 			pixman_region_subtract(r, r, &tmp);
1834 
1835 		return;
1836 	}
1837 
1838 	if (damage->mode == DAMAGE_ADD)
1839 		nboxes += region_num_rects(r);
1840 
1841 	iter = last_box(damage);
1842 	n = iter->size - damage->remain;
1843 	boxes = malloc(sizeof(BoxRec)*nboxes);
1844 	if (boxes == NULL)
1845 		return;
1846 
1847 	if (list_is_empty(&damage->embedded_box.list)) {
1848 		memcpy(boxes,
1849 		       damage->embedded_box.box,
1850 		       n*sizeof(BoxRec));
1851 	} else {
1852 		if (boxes != (BoxPtr)(iter+1))
1853 			memcpy(boxes, iter+1, n*sizeof(BoxRec));
1854 
1855 		iter = list_entry(iter->list.prev,
1856 				  struct sna_damage_box,
1857 				  list);
1858 		while (&iter->list != &damage->embedded_box.list) {
1859 			memcpy(boxes + n, iter+1,
1860 			       iter->size * sizeof(BoxRec));
1861 			n += iter->size;
1862 
1863 			iter = list_entry(iter->list.prev,
1864 					  struct sna_damage_box,
1865 					  list);
1866 		}
1867 
1868 		memcpy(boxes + n,
1869 		       damage->embedded_box.box,
1870 		       sizeof(damage->embedded_box.box));
1871 		n += damage->embedded_box.size;
1872 	}
1873 
1874 	if (damage->mode == DAMAGE_ADD) {
1875 		memcpy(boxes + n,
1876 		       region_rects(r),
1877 		       region_num_rects(r)*sizeof(BoxRec));
1878 		assert(n + region_num_rects(r) == nboxes);
1879 		pixman_region_fini(r);
1880 		pixman_region_init_rects(r, boxes, nboxes);
1881 
1882 		assert(pixman_region_not_empty(r));
1883 		assert(damage->extents.x1 == r->extents.x1 &&
1884 		       damage->extents.y1 == r->extents.y1 &&
1885 		       damage->extents.x2 == r->extents.x2 &&
1886 		       damage->extents.y2 == r->extents.y2);
1887 	} else {
1888 		pixman_region16_t tmp;
1889 
1890 		pixman_region_init_rects(&tmp, boxes, nboxes);
1891 		pixman_region_subtract(r, r, &tmp);
1892 		pixman_region_fini(&tmp);
1893 
1894 		assert(damage->extents.x1 <= r->extents.x1 &&
1895 		       damage->extents.y1 <= r->extents.y1 &&
1896 		       damage->extents.x2 >= r->extents.x2 &&
1897 		       damage->extents.y2 >= r->extents.y2);
1898 	}
1899 
1900 	free(boxes);
1901 }
1902