1/************************************************************************** 2 3Copyright (c) 2011 Intel Corporation 4 5Permission is hereby granted, free of charge, to any person obtaining a 6copy of this software and associated documentation files (the 7"Software"), to deal in the Software without restriction, including 8without limitation the rights to use, copy, modify, merge, publish, 9distribute, sub license, and/or sell copies of the Software, and to 10permit persons to whom the Software is furnished to do so, subject to 11the following conditions: 12 13The above copyright notice and this permission notice (including the 14next paragraph) shall be included in all copies or substantial portions 15of the Software. 16 17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 20IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR 21ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23SOFTWARE 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 51struct sna_damage_box { 52 struct list list; 53 int size; 54} __attribute__((packed)); 55 56static struct sna_damage *__freed_damage; 57 58static inline bool region_is_singular(const RegionRec *r) 59{ 60 return r->data == NULL; 61} 62 63static 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 69static 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 120static 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 155static struct sna_damage_box * 156last_box(struct sna_damage *damage) 157{ 158 return list_entry(damage->embedded_box.list.prev, 159 struct sna_damage_box, 160 list); 161} 162 163static void 164reset_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 173static 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 179static 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 199struct sna_damage *sna_damage_create(void) 200{ 201 return _sna_damage_create(); 202} 203 204static 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 213static 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 330done: 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 338static 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 365static struct sna_damage * 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 375restart: 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 417static struct sna_damage * 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 427restart: 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 479static struct sna_damage * 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 490restart: 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 542static struct sna_damage * 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 553restart: 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 605static 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 630static 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 636static 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 642static 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 678inline 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 718fastcall 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 739fastcall 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 746inline static struct sna_damage * 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 802struct 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 824struct 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 832inline static struct sna_damage * 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 891struct 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 913struct 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? */ 922inline static struct sna_damage * 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 975struct 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 997struct 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 1006fastcall 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 1026fastcall 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 1033struct 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 1055struct 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 1088static 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 1099static 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)) { 1106no_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 1165fastcall 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 1183fastcall 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 1190inline 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 1236fastcall 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 1253fastcall 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 1260static 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 1317fastcall 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 1337fastcall 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 1345static 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 1401int _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 1421int _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 1428static 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 1434bool _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 1481static 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 1508bool _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 1529bool _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 1536static 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 1550struct 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 1568int _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 1582int _sna_damage_get_boxes(struct sna_damage *damage, const BoxRec **boxes) 1583{ 1584 return __sna_damage_get_boxes(damage, boxes); 1585} 1586#endif 1587 1588struct 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 1603void __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 1613struct sna_damage_selftest{ 1614 int width, height; 1615}; 1616 1617static 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 1643static 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 1665static 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 1678static 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 1692static 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 1704static 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 1717static 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 1730static 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 1755void 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 1807void _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