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, ®ion->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, ®ion->extents);
704 return damage;
705 }
706
707 if (pixman_region_contains_rectangle(&damage->region,
708 ®ion->extents) == PIXMAN_REGION_IN)
709 return damage;
710
711 damage_union(damage, ®ion->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, ®ion->extents))
1114 return damage;
1115
1116 if (region_is_singular(region) &&
1117 box_contains(®ion->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(®ion, box, 1);
1221 pixman_region_subtract(&damage->region,
1222 &damage->region,
1223 ®ion);
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