sna_damage.c revision 03b705cf
11.13Sjmmv/************************************************************************** 21.1Sjmmv 31.1SjmmvCopyright (c) 2011 Intel Corporation 41.13Sjmmv 51.1SjmmvPermission is hereby granted, free of charge, to any person obtaining a 61.1Sjmmvcopy of this software and associated documentation files (the 71.1Sjmmv"Software"), to deal in the Software without restriction, including 81.3Sjmmvwithout limitation the rights to use, copy, modify, merge, publish, 91.3Sjmmvdistribute, sub license, and/or sell copies of the Software, and to 101.1Sjmmvpermit persons to whom the Software is furnished to do so, subject to 111.1Sjmmvthe following conditions: 121.1Sjmmv 131.1SjmmvThe above copyright notice and this permission notice (including the 141.1Sjmmvnext paragraph) shall be included in all copies or substantial portions 151.1Sjmmvof the Software. 161.1Sjmmv 171.1SjmmvTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 181.1SjmmvOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 191.1SjmmvMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 201.1SjmmvIN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR 211.1SjmmvANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 221.1SjmmvTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 231.1SjmmvSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 241.1Sjmmv 251.1Sjmmv **************************************************************************/ 261.1Sjmmv 271.1Sjmmv#ifdef HAVE_CONFIG_H 281.1Sjmmv#include "config.h" 291.1Sjmmv#endif 301.1Sjmmv 311.1Sjmmv#include "sna.h" 321.1Sjmmv#include "sna_damage.h" 331.1Sjmmv 341.1Sjmmv/* 351.1Sjmmv * sna_damage is a batching layer on top of the regular pixman_region_t. 361.1Sjmmv * It is required as the ever-growing accumulation of invidual small 371.1Sjmmv * damage regions is an O(n^2) operation. Instead the accumulation of a 381.1Sjmmv * batch can be done in closer to O(n.lgn), and so prevents abysmal 391.1Sjmmv * performance in x11perf -copywinwin10. 401.1Sjmmv * 411.1Sjmmv * As with the core of SNA, damage is handled modally. That is, it 421.13Sjmmv * accumulates whilst rendering and then subtracts during migration of the 431.1Sjmmv * pixmap from GPU to CPU or vice versa. As such we can track the current 441.1Sjmmv * mode globally and when that mode switches perform the update of the region 451.11Schristos * in a single operation. 461.1Sjmmv * 471.1Sjmmv * Furthermore, we can track whether the whole pixmap is damaged and so 481.4Sjmmv * cheapy discard no-ops. 491.1Sjmmv */ 501.1Sjmmv 511.1Sjmmvstruct sna_damage_box { 521.1Sjmmv struct list list; 531.1Sjmmv int size; 541.1Sjmmv} __attribute__((packed)); 551.1Sjmmv 561.1Sjmmvstatic struct sna_damage *__freed_damage; 571.1Sjmmv 581.1Sjmmvstatic inline bool region_is_singular(RegionRec *r) 591.1Sjmmv{ 601.1Sjmmv return r->data == NULL; 611.1Sjmmv} 621.1Sjmmv 631.1Sjmmv#if HAS_DEBUG_FULL 641.1Sjmmvstatic const char *_debug_describe_region(char *buf, int max, 651.1Sjmmv RegionPtr region) 661.1Sjmmv{ 671.8Sjmmv BoxPtr extents; 681.1Sjmmv BoxPtr box; 691.1Sjmmv int n; 701.1Sjmmv int len; 711.1Sjmmv 721.1Sjmmv if (region == NULL) 731.1Sjmmv return "nil"; 741.1Sjmmv 751.1Sjmmv n = REGION_NUM_RECTS(region); 761.1Sjmmv if (n == 0) 771.1Sjmmv return "[0]"; 781.1Sjmmv 791.1Sjmmv extents = REGION_EXTENTS(NULL, region); 801.1Sjmmv if (n == 1) { 811.1Sjmmv sprintf(buf, 821.1Sjmmv "[(%d, %d), (%d, %d)]", 831.1Sjmmv extents->x1, extents->y1, 841.1Sjmmv extents->x2, extents->y2); 851.1Sjmmv return buf; 861.10Sjmmv } 871.1Sjmmv 881.4Sjmmv len = sprintf(buf, 891.4Sjmmv "[(%d, %d), (%d, %d) x %d: ", 901.4Sjmmv extents->x1, extents->y1, 911.1Sjmmv extents->x2, extents->y2, 921.12Schristos n) + 3; 931.1Sjmmv max -= 2; 941.4Sjmmv box = REGION_RECTS(region); 951.1Sjmmv while (n--) { 961.1Sjmmv char tmp[80]; 971.1Sjmmv int this; 981.1Sjmmv 991.1Sjmmv this = snprintf(tmp, sizeof(tmp), 1001.1Sjmmv "((%d, %d), (%d, %d))%s", 1011.1Sjmmv box->x1, box->y1, 1021.1Sjmmv box->x2, box->y2, 1031.1Sjmmv n ? ", ..." : ""); 1041.10Sjmmv box++; 1051.10Sjmmv 1061.10Sjmmv if (this > max - len) 1071.4Sjmmv break; 1081.1Sjmmv 1091.1Sjmmv len -= 3; 1101.1Sjmmv memcpy(buf + len, tmp, this); 1111.1Sjmmv len += this; 1121.4Sjmmv } 1131.1Sjmmv buf[len++] = ']'; 1141.1Sjmmv buf[len] = '\0'; 1151.1Sjmmv return buf; 1161.1Sjmmv} 1171.10Sjmmv 1181.1Sjmmvstatic const char *_debug_describe_damage(char *buf, int max, 1191.1Sjmmv const struct sna_damage *damage) 1201.1Sjmmv{ 1211.4Sjmmv char damage_str[500], region_str[500]; 1221.1Sjmmv int str_max; 1231.1Sjmmv 1241.1Sjmmv if (damage == NULL) 1251.1Sjmmv return "None"; 1261.10Sjmmv 1271.1Sjmmv str_max = max/2 - 6; 1281.1Sjmmv if (str_max > sizeof(damage_str)) 1291.1Sjmmv str_max = sizeof(damage_str); 1301.1Sjmmv 1311.1Sjmmv if (damage->mode == DAMAGE_ALL) { 1321.1Sjmmv snprintf(buf, max, "[[(%d, %d), (%d, %d)]: all]", 1331.1Sjmmv damage->extents.x1, damage->extents.y1, 1341.1Sjmmv damage->extents.x2, damage->extents.y2); 1351.1Sjmmv } else { 1361.1Sjmmv if (damage->dirty) { 1371.1Sjmmv sprintf(damage_str, "%c[ ...]", 1381.1Sjmmv damage->mode == DAMAGE_SUBTRACT ? '-' : '+'); 1391.12Schristos } else 1401.12Schristos damage_str[0] = '\0'; 1411.12Schristos snprintf(buf, max, "[[(%d, %d), (%d, %d)]: %s %s]%c", 1421.12Schristos damage->extents.x1, damage->extents.y1, 1431.1Sjmmv damage->extents.x2, damage->extents.y2, 1441.1Sjmmv _debug_describe_region(region_str, str_max, 1451.1Sjmmv &damage->region), 1461.1Sjmmv damage_str, damage->dirty ? '*' : ' '); 1471.1Sjmmv } 1481.1Sjmmv 1491.1Sjmmv return buf; 1501.1Sjmmv} 1511.1Sjmmv#endif 1521.1Sjmmv 1531.1Sjmmvstatic void 1541.1Sjmmvreset_embedded_box(struct sna_damage *damage) 1551.4Sjmmv{ 1561.1Sjmmv damage->dirty = false; 1571.1Sjmmv damage->box = damage->embedded_box.box; 1581.1Sjmmv damage->embedded_box.size = 1591.1Sjmmv damage->remain = ARRAY_SIZE(damage->embedded_box.box); 1601.10Sjmmv list_init(&damage->embedded_box.list); 1611.1Sjmmv} 1621.1Sjmmv 1631.1Sjmmvstatic void reset_extents(struct sna_damage *damage) 1641.1Sjmmv{ 1651.1Sjmmv damage->extents.x1 = damage->extents.y1 = MAXSHORT; 1661.1Sjmmv damage->extents.x2 = damage->extents.y2 = MINSHORT; 1671.1Sjmmv} 1681.1Sjmmv 1691.1Sjmmvstatic struct sna_damage *_sna_damage_create(void) 1701.1Sjmmv{ 1711.1Sjmmv struct sna_damage *damage; 1721.1Sjmmv 1731.1Sjmmv if (__freed_damage) { 1741.1Sjmmv damage = __freed_damage; 1751.1Sjmmv __freed_damage = *(void **)__freed_damage; 1761.1Sjmmv } else { 1771.1Sjmmv damage = malloc(sizeof(*damage)); 1781.1Sjmmv if (damage == NULL) 1791.1Sjmmv return NULL; 1801.1Sjmmv } 1811.1Sjmmv reset_embedded_box(damage); 1821.1Sjmmv damage->mode = DAMAGE_ADD; 1831.1Sjmmv pixman_region_init(&damage->region); 1841.1Sjmmv reset_extents(damage); 1851.1Sjmmv 1861.1Sjmmv return damage; 1871.4Sjmmv} 1881.4Sjmmv 1891.4Sjmmvstruct sna_damage *sna_damage_create(void) 1901.4Sjmmv{ 1911.4Sjmmv return _sna_damage_create(); 1921.4Sjmmv} 1931.4Sjmmv 1941.4Sjmmvstatic bool _sna_damage_create_boxes(struct sna_damage *damage, 1951.1Sjmmv int count) 1961.1Sjmmv{ 1971.1Sjmmv struct sna_damage_box *box; 1981.1Sjmmv int n; 1991.8Sjmmv 2001.8Sjmmv box = list_entry(damage->embedded_box.list.prev, 2011.8Sjmmv struct sna_damage_box, 2021.8Sjmmv list); 2031.8Sjmmv n = 4*box->size; 2041.8Sjmmv if (n < count) 2051.8Sjmmv n = ALIGN(count, 64); 2061.8Sjmmv 2071.8Sjmmv DBG((" %s(%d->%d): new\n", __FUNCTION__, count, n)); 2081.8Sjmmv 2091.8Sjmmv box = malloc(sizeof(*box) + sizeof(BoxRec)*n); 2101.8Sjmmv if (box == NULL) 2111.8Sjmmv return false; 2121.8Sjmmv 2131.8Sjmmv list_add_tail(&box->list, &damage->embedded_box.list); 2141.8Sjmmv 2151.8Sjmmv box->size = damage->remain = n; 2161.8Sjmmv damage->box = (BoxRec *)(box + 1); 2171.8Sjmmv return true; 2181.8Sjmmv} 2191.8Sjmmv 2201.8Sjmmvstatic struct sna_damage * 2211.8Sjmmv_sna_damage_create_elt(struct sna_damage *damage, 2221.8Sjmmv const BoxRec *boxes, int count) 2231.8Sjmmv{ 2241.8Sjmmv int n; 2251.1Sjmmv 2261.1Sjmmv DBG((" %s: prev=(remain %d), count=%d\n", 2271.1Sjmmv __FUNCTION__, damage->remain, count)); 2281.1Sjmmv 2291.1Sjmmv damage->dirty = true; 2301.1Sjmmv n = count; 2311.1Sjmmv if (n > damage->remain) 2321.1Sjmmv n = damage->remain; 2331.1Sjmmv if (n) { 2341.1Sjmmv memcpy(damage->box, boxes, n * sizeof(BoxRec)); 2351.1Sjmmv damage->box += n; 2361.1Sjmmv damage->remain -= n; 2371.1Sjmmv 2381.1Sjmmv count -= n; 2391.1Sjmmv boxes += n; 2401.1Sjmmv if (count == 0) 2411.1Sjmmv return damage; 2421.1Sjmmv } 2431.1Sjmmv 2441.1Sjmmv DBG((" %s(): new elt\n", __FUNCTION__)); 2451.1Sjmmv 2461.1Sjmmv if (_sna_damage_create_boxes(damage, count)) { 2471.1Sjmmv memcpy(damage->box, boxes, count * sizeof(BoxRec)); 2481.1Sjmmv damage->box += count; 2491.1Sjmmv damage->remain -= count; 2501.1Sjmmv } 2511.1Sjmmv assert(damage->remain >= 0); 2521.1Sjmmv 2531.1Sjmmv return damage; 2541.1Sjmmv} 2551.1Sjmmv 2561.1Sjmmvstatic struct sna_damage * 2571.1Sjmmv_sna_damage_create_elt_from_boxes(struct sna_damage *damage, 2581.1Sjmmv const BoxRec *boxes, int count, 2591.1Sjmmv int16_t dx, int16_t dy) 2601.1Sjmmv{ 2611.7Sjmmv int i, n; 2621.1Sjmmv 2631.1Sjmmv DBG((" %s: prev=(remain %d)\n", __FUNCTION__, damage->remain)); 2641.7Sjmmv 2651.1Sjmmv damage->dirty = true; 2661.1Sjmmv n = count; 2671.13Sjmmv if (n > damage->remain) 2681.1Sjmmv n = damage->remain; 2691.1Sjmmv if (n) { 2701.1Sjmmv for (i = 0; i < n; i++) { 2711.1Sjmmv damage->box[i].x1 = boxes[i].x1 + dx; 2721.1Sjmmv damage->box[i].x2 = boxes[i].x2 + dx; 2731.1Sjmmv damage->box[i].y1 = boxes[i].y1 + dy; 2741.1Sjmmv damage->box[i].y2 = boxes[i].y2 + dy; 2751.1Sjmmv } 2761.1Sjmmv damage->box += n; 2771.1Sjmmv damage->remain -= n; 2781.1Sjmmv 2791.1Sjmmv count -= n; 2801.1Sjmmv boxes += n; 2811.1Sjmmv if (count == 0) 2821.1Sjmmv return damage; 2831.1Sjmmv } 2841.1Sjmmv 2851.1Sjmmv DBG((" %s(): new elt\n", __FUNCTION__)); 2861.1Sjmmv 2871.1Sjmmv if (!_sna_damage_create_boxes(damage, count)) 2881.1Sjmmv return damage; 2891.1Sjmmv 2901.1Sjmmv for (i = 0; i < count; i++) { 2911.1Sjmmv damage->box[i].x1 = boxes[i].x1 + dx; 2921.1Sjmmv damage->box[i].x2 = boxes[i].x2 + dx; 2931.1Sjmmv damage->box[i].y1 = boxes[i].y1 + dy; 2941.1Sjmmv damage->box[i].y2 = boxes[i].y2 + dy; 2951.1Sjmmv } 2961.13Sjmmv damage->box += count; 2971.1Sjmmv damage->remain -= count; 2981.1Sjmmv assert(damage->remain >= 0); 2991.1Sjmmv 3001.1Sjmmv return damage; 3011.1Sjmmv} 3021.1Sjmmv 3031.1Sjmmvstatic struct sna_damage * 3041.1Sjmmv_sna_damage_create_elt_from_rectangles(struct sna_damage *damage, 3051.1Sjmmv const xRectangle *r, int count, 3061.1Sjmmv int16_t dx, int16_t dy) 3071.1Sjmmv{ 3081.1Sjmmv int i, n; 3091.1Sjmmv 3101.1Sjmmv DBG((" %s: prev=(remain %d), count=%d\n", 3111.1Sjmmv __FUNCTION__, damage->remain, count)); 3121.1Sjmmv 3131.1Sjmmv damage->dirty = true; 3141.1Sjmmv n = count; 3151.1Sjmmv if (n > damage->remain) 3161.1Sjmmv n = damage->remain; 3171.1Sjmmv if (n) { 3181.1Sjmmv for (i = 0; i < n; i++) { 3191.1Sjmmv damage->box[i].x1 = r[i].x + dx; 3201.9Sjmmv damage->box[i].x2 = damage->box[i].x1 + r[i].width; 3211.9Sjmmv damage->box[i].y1 = r[i].y + dy; 3221.6Sjmmv damage->box[i].y2 = damage->box[i].y1 + r[i].height; 3231.1Sjmmv } 3241.1Sjmmv damage->box += n; 3251.1Sjmmv damage->remain -= n; 3261.1Sjmmv 3271.1Sjmmv count -= n; 3281.1Sjmmv r += n; 3291.1Sjmmv if (count == 0) 3301.1Sjmmv return damage; 3311.1Sjmmv } 3321.5Sjmmv 3331.1Sjmmv DBG((" %s(): new elt\n", __FUNCTION__)); 3341.1Sjmmv 3351.1Sjmmv if (!_sna_damage_create_boxes(damage, count)) 3361.1Sjmmv return damage; 3371.1Sjmmv 3381.1Sjmmv for (i = 0; i < count; i++) { 3391.1Sjmmv damage->box[i].x1 = r[i].x + dx; 3401.1Sjmmv damage->box[i].x2 = damage->box[i].x1 + r[i].width; 3411.5Sjmmv damage->box[i].y1 = r[i].y + dy; 3421.1Sjmmv damage->box[i].y2 = damage->box[i].y1 + r[i].height; 3431.1Sjmmv } 3441.1Sjmmv damage->box += count; 3451.5Sjmmv damage->remain -= count; 3461.1Sjmmv assert(damage->remain >= 0); 3471.1Sjmmv 3481.1Sjmmv return damage; 3491.1Sjmmv} 3501.1Sjmmv 3511.1Sjmmvstatic struct sna_damage * 3521.1Sjmmv_sna_damage_create_elt_from_points(struct sna_damage *damage, 3531.1Sjmmv const DDXPointRec *p, int count, 3541.1Sjmmv int16_t dx, int16_t dy) 3551.1Sjmmv{ 3561.1Sjmmv int i, n; 3571.1Sjmmv 3581.9Sjmmv DBG((" %s: prev=(remain %d), count=%d\n", 3591.1Sjmmv __FUNCTION__, damage->remain, count)); 3601.1Sjmmv 3611.13Sjmmv damage->dirty = true; 3621.1Sjmmv n = count; 3631.1Sjmmv if (n > damage->remain) 3641.9Sjmmv n = damage->remain; 3651.9Sjmmv if (n) { 3661.9Sjmmv for (i = 0; i < n; i++) { 3671.9Sjmmv damage->box[i].x1 = p[i].x + dx; 3681.1Sjmmv damage->box[i].x2 = damage->box[i].x1 + 1; 3691.1Sjmmv damage->box[i].y1 = p[i].y + dy; 3701.1Sjmmv damage->box[i].y2 = damage->box[i].y1 + 1; 3711.1Sjmmv } 3721.1Sjmmv damage->box += n; 3731.1Sjmmv damage->remain -= n; 3741.1Sjmmv 3751.1Sjmmv count -= n; 3761.1Sjmmv p += n; 3771.1Sjmmv if (count == 0) 3781.1Sjmmv return damage; 3791.1Sjmmv } 3801.1Sjmmv 3811.1Sjmmv DBG((" %s(): new elt\n", __FUNCTION__)); 3821.1Sjmmv 3831.1Sjmmv if (! _sna_damage_create_boxes(damage, count)) 3841.1Sjmmv return damage; 3851.1Sjmmv 3861.1Sjmmv for (i = 0; i < count; i++) { 3871.1Sjmmv damage->box[i].x1 = p[i].x + dx; 3881.1Sjmmv damage->box[i].x2 = damage->box[i].x1 + 1; 3891.1Sjmmv damage->box[i].y1 = p[i].y + dy; 3901.1Sjmmv damage->box[i].y2 = damage->box[i].y1 + 1; 3911.1Sjmmv } 3921.1Sjmmv damage->box += count; 3931.1Sjmmv damage->remain -= count; 3941.7Sjmmv assert(damage->remain >= 0); 3951.1Sjmmv 3961.1Sjmmv return damage; 3971.7Sjmmv} 3981.1Sjmmv 3991.1Sjmmvstatic void free_list(struct list *head) 4001.13Sjmmv{ 4011.1Sjmmv while (!list_is_empty(head)) { 4021.1Sjmmv struct list *l = head->next; 4031.1Sjmmv list_del(l); 4041.1Sjmmv free(l); 4051.1Sjmmv } 4061.1Sjmmv} 4071.1Sjmmv 4081.1Sjmmvstatic void __sna_damage_reduce(struct sna_damage *damage) 4091.1Sjmmv{ 4101.1Sjmmv int n, nboxes; 4111.1Sjmmv BoxPtr boxes, free_boxes = NULL; 4121.1Sjmmv pixman_region16_t *region = &damage->region; 4131.1Sjmmv struct sna_damage_box *iter; 4141.1Sjmmv 4151.1Sjmmv assert(damage->mode != DAMAGE_ALL); 4161.1Sjmmv assert(damage->dirty); 4171.1Sjmmv 4181.1Sjmmv DBG((" reduce: before region.n=%ld\n", (long)REGION_NUM_RECTS(region))); 4191.1Sjmmv 4201.1Sjmmv nboxes = damage->embedded_box.size; 421 list_for_each_entry(iter, &damage->embedded_box.list, list) 422 nboxes += iter->size; 423 DBG((" nboxes=%d, residual=%d\n", nboxes, damage->remain)); 424 nboxes -= damage->remain; 425 if (nboxes == 0) 426 goto done; 427 if (nboxes == 1) { 428 pixman_region16_t tmp; 429 430 tmp.extents = damage->embedded_box.box[0]; 431 tmp.data = NULL; 432 433 if (damage->mode == DAMAGE_ADD) 434 pixman_region_union(region, region, &tmp); 435 else 436 pixman_region_subtract(region, region, &tmp); 437 damage->extents = region->extents; 438 439 goto done; 440 } 441 442 if (damage->mode == DAMAGE_ADD) 443 nboxes += REGION_NUM_RECTS(region); 444 445 iter = list_entry(damage->embedded_box.list.prev, 446 struct sna_damage_box, 447 list); 448 n = iter->size - damage->remain; 449 boxes = (BoxRec *)(iter+1); 450 DBG((" last box count=%d/%d, need=%d\n", n, iter->size, nboxes)); 451 if (nboxes > iter->size) { 452 boxes = malloc(sizeof(BoxRec)*nboxes); 453 if (boxes == NULL) 454 goto done; 455 456 free_boxes = boxes; 457 } 458 459 if (boxes != damage->embedded_box.box) { 460 if (list_is_empty(&damage->embedded_box.list)) { 461 DBG((" copying embedded boxes\n")); 462 memcpy(boxes, 463 damage->embedded_box.box, 464 n*sizeof(BoxRec)); 465 } else { 466 if (boxes != (BoxPtr)(iter+1)) { 467 DBG((" copying %d boxes from last\n", n)); 468 memcpy(boxes, iter+1, n*sizeof(BoxRec)); 469 } 470 471 iter = list_entry(iter->list.prev, 472 struct sna_damage_box, 473 list); 474 while (&iter->list != &damage->embedded_box.list) { 475 DBG((" copy %d boxes from %d\n", 476 iter->size, n)); 477 memcpy(boxes + n, iter+1, 478 iter->size * sizeof(BoxRec)); 479 n += iter->size; 480 481 iter = list_entry(iter->list.prev, 482 struct sna_damage_box, 483 list); 484 } 485 486 DBG((" copying embedded boxes to %d\n", n)); 487 memcpy(boxes + n, 488 damage->embedded_box.box, 489 sizeof(damage->embedded_box.box)); 490 n += damage->embedded_box.size; 491 } 492 } 493 494 if (damage->mode == DAMAGE_ADD) { 495 memcpy(boxes + n, 496 REGION_RECTS(region), 497 REGION_NUM_RECTS(region)*sizeof(BoxRec)); 498 assert(n + REGION_NUM_RECTS(region) == nboxes); 499 pixman_region_fini(region); 500 pixman_region_init_rects(region, boxes, nboxes); 501 502 assert(pixman_region_not_empty(region)); 503 assert(damage->extents.x1 == region->extents.x1 && 504 damage->extents.y1 == region->extents.y1 && 505 damage->extents.x2 == region->extents.x2 && 506 damage->extents.y2 == region->extents.y2); 507 } else { 508 pixman_region16_t tmp; 509 510 assert(n == nboxes); 511 pixman_region_init_rects(&tmp, boxes, nboxes); 512 pixman_region_subtract(region, region, &tmp); 513 pixman_region_fini(&tmp); 514 515 assert(damage->extents.x1 <= region->extents.x1 && 516 damage->extents.y1 <= region->extents.y1 && 517 damage->extents.x2 >= region->extents.x2 && 518 damage->extents.y2 >= region->extents.y2); 519 if (pixman_region_not_empty(region)) 520 damage->extents = region->extents; 521 else 522 reset_extents(damage); 523 } 524 525 free(free_boxes); 526 527done: 528 damage->mode = DAMAGE_ADD; 529 free_list(&damage->embedded_box.list); 530 reset_embedded_box(damage); 531 532 DBG((" reduce: after region.n=%ld\n", (long)REGION_NUM_RECTS(region))); 533} 534 535static void damage_union(struct sna_damage *damage, const BoxRec *box) 536{ 537 DBG(("%s: extending damage (%d, %d), (%d, %d) by (%d, %d), (%d, %d)\n", 538 __FUNCTION__, 539 damage->extents.x1, damage->extents.y1, 540 damage->extents.x2, damage->extents.y2, 541 box->x1, box->y1, box->x2, box->y2)); 542 assert(box->x2 > box->x1 && box->y2 > box->y1); 543 if (damage->extents.x2 < damage->extents.x1) { 544 damage->extents = *box; 545 } else { 546 if (damage->extents.x1 > box->x1) 547 damage->extents.x1 = box->x1; 548 if (damage->extents.x2 < box->x2) 549 damage->extents.x2 = box->x2; 550 551 if (damage->extents.y1 > box->y1) 552 damage->extents.y1 = box->y1; 553 if (damage->extents.y2 < box->y2) 554 damage->extents.y2 = box->y2; 555 } 556 assert(damage->extents.x2 > damage->extents.x1); 557 assert(damage->extents.y2 > damage->extents.y1); 558} 559 560static void _pixman_region_union_box(RegionRec *region, const BoxRec *box) 561{ 562 RegionRec u = { *box, NULL }; 563 pixman_region_union(region, region, &u); 564} 565 566static bool box_contains_region(const BoxRec *b, const RegionRec *r) 567{ 568 return (b->x1 <= r->extents.x1 && b->x2 >= r->extents.x2 && 569 b->y1 <= r->extents.y1 && b->y2 >= r->extents.y2); 570} 571 572static struct sna_damage *__sna_damage_add_box(struct sna_damage *damage, 573 const BoxRec *box) 574{ 575 if (box->y2 <= box->y1 || box->x2 <= box->x1) 576 return damage; 577 578 if (!damage) { 579 damage = _sna_damage_create(); 580 if (damage == NULL) 581 return NULL; 582 } else switch (damage->mode) { 583 case DAMAGE_ALL: 584 return damage; 585 case DAMAGE_SUBTRACT: 586 __sna_damage_reduce(damage); 587 case DAMAGE_ADD: 588 break; 589 } 590 591 if (REGION_NUM_RECTS(&damage->region) <= 1 || 592 box_contains_region(box, &damage->region)) { 593 _pixman_region_union_box(&damage->region, box); 594 assert(damage->region.extents.x2 > damage->region.extents.x1); 595 assert(damage->region.extents.y2 > damage->region.extents.y1); 596 damage_union(damage, box); 597 return damage; 598 } 599 600 if (pixman_region_contains_rectangle(&damage->region, 601 (BoxPtr)box) == PIXMAN_REGION_IN) 602 return damage; 603 604 damage_union(damage, box); 605 return _sna_damage_create_elt(damage, box, 1); 606} 607 608inline static struct sna_damage *__sna_damage_add(struct sna_damage *damage, 609 RegionPtr region) 610{ 611 assert(RegionNotEmpty(region)); 612 613 if (!damage) { 614 damage = _sna_damage_create(); 615 if (damage == NULL) 616 return NULL; 617 } else switch (damage->mode) { 618 case DAMAGE_ALL: 619 return damage; 620 case DAMAGE_SUBTRACT: 621 __sna_damage_reduce(damage); 622 case DAMAGE_ADD: 623 break; 624 } 625 626 if (region->data == NULL) 627 return __sna_damage_add_box(damage, ®ion->extents); 628 629 if (REGION_NUM_RECTS(&damage->region) <= 1) { 630 pixman_region_union(&damage->region, &damage->region, region); 631 assert(damage->region.extents.x2 > damage->region.extents.x1); 632 assert(damage->region.extents.y2 > damage->region.extents.y1); 633 damage_union(damage, ®ion->extents); 634 return damage; 635 } 636 637 if (pixman_region_contains_rectangle(&damage->region, 638 ®ion->extents) == PIXMAN_REGION_IN) 639 return damage; 640 641 damage_union(damage, ®ion->extents); 642 return _sna_damage_create_elt(damage, 643 REGION_RECTS(region), 644 REGION_NUM_RECTS(region)); 645} 646 647#if HAS_DEBUG_FULL 648fastcall struct sna_damage *_sna_damage_add(struct sna_damage *damage, 649 RegionPtr region) 650{ 651 char region_buf[120]; 652 char damage_buf[1000]; 653 654 DBG(("%s(%s + %s)\n", __FUNCTION__, 655 _debug_describe_damage(damage_buf, sizeof(damage_buf), damage), 656 _debug_describe_region(region_buf, sizeof(region_buf), region))); 657 658 damage = __sna_damage_add(damage, region); 659 660 ErrorF(" = %s\n", 661 _debug_describe_damage(damage_buf, sizeof(damage_buf), damage)); 662 assert(RegionNumRects(&damage->region)); 663 assert(damage->region.extents.x2 > damage->region.extents.x1); 664 assert(damage->region.extents.y2 > damage->region.extents.y1); 665 666 return damage; 667} 668#else 669fastcall struct sna_damage *_sna_damage_add(struct sna_damage *damage, 670 RegionPtr region) 671{ 672 return __sna_damage_add(damage, region); 673} 674#endif 675 676inline static struct sna_damage * 677__sna_damage_add_boxes(struct sna_damage *damage, 678 const BoxRec *box, int n, 679 int16_t dx, int16_t dy) 680{ 681 BoxRec extents; 682 int i; 683 684 assert(n); 685 686 if (!damage) { 687 damage = _sna_damage_create(); 688 if (damage == NULL) 689 return NULL; 690 } else switch (damage->mode) { 691 case DAMAGE_ALL: 692 return damage; 693 case DAMAGE_SUBTRACT: 694 __sna_damage_reduce(damage); 695 case DAMAGE_ADD: 696 break; 697 } 698 699 assert(box[0].x2 > box[0].x1 && box[0].y2 > box[0].y1); 700 extents = box[0]; 701 for (i = 1; i < n; i++) { 702 assert(box[i].x2 > box[i].x1 && box[i].y2 > box[i].y1); 703 if (extents.x1 > box[i].x1) 704 extents.x1 = box[i].x1; 705 if (extents.x2 < box[i].x2) 706 extents.x2 = box[i].x2; 707 if (extents.y1 > box[i].y1) 708 extents.y1 = box[i].y1; 709 if (extents.y2 < box[i].y2) 710 extents.y2 = box[i].y2; 711 } 712 713 assert(extents.y2 > extents.y1 && extents.x2 > extents.x1); 714 715 extents.x1 += dx; 716 extents.x2 += dx; 717 extents.y1 += dy; 718 extents.y2 += dy; 719 720 if (n == 1) 721 return __sna_damage_add_box(damage, &extents); 722 723 if (pixman_region_contains_rectangle(&damage->region, 724 &extents) == PIXMAN_REGION_IN) 725 return damage; 726 727 damage_union(damage, &extents); 728 return _sna_damage_create_elt_from_boxes(damage, box, n, dx, dy); 729} 730 731#if HAS_DEBUG_FULL 732struct sna_damage *_sna_damage_add_boxes(struct sna_damage *damage, 733 const BoxRec *b, int n, 734 int16_t dx, int16_t dy) 735{ 736 char damage_buf[1000]; 737 738 DBG(("%s(%s + [(%d, %d), (%d, %d) ... x %d])\n", __FUNCTION__, 739 _debug_describe_damage(damage_buf, sizeof(damage_buf), damage), 740 b->x1, b->y1, b->x2, b->y2, n)); 741 742 damage = __sna_damage_add_boxes(damage, b, n, dx, dy); 743 744 ErrorF(" = %s\n", 745 _debug_describe_damage(damage_buf, sizeof(damage_buf), damage)); 746 if (RegionNumRects(&damage->region)) { 747 assert(damage->region.extents.x2 > damage->region.extents.x1); 748 assert(damage->region.extents.y2 > damage->region.extents.y1); 749 } 750 751 return damage; 752} 753#else 754struct sna_damage *_sna_damage_add_boxes(struct sna_damage *damage, 755 const BoxRec *b, int n, 756 int16_t dx, int16_t dy) 757{ 758 return __sna_damage_add_boxes(damage, b, n, dx, dy); 759} 760#endif 761 762inline static struct sna_damage * 763__sna_damage_add_rectangles(struct sna_damage *damage, 764 const xRectangle *r, int n, 765 int16_t dx, int16_t dy) 766{ 767 BoxRec extents; 768 int i; 769 770 assert(n); 771 772 assert(r[0].width && r[0].height); 773 extents.x1 = r[0].x; 774 extents.x2 = r[0].x + r[0].width; 775 extents.y1 = r[0].y; 776 extents.y2 = r[0].y + r[0].height; 777 for (i = 1; i < n; i++) { 778 assert(r[i].width && r[i].height); 779 if (extents.x1 > r[i].x) 780 extents.x1 = r[i].x; 781 if (extents.x2 < r[i].x + r[i].width) 782 extents.x2 = r[i].x + r[i].width; 783 if (extents.y1 > r[i].y) 784 extents.y1 = r[i].y; 785 if (extents.y2 < r[i].y + r[i].height) 786 extents.y2 = r[i].y + r[i].height; 787 } 788 789 assert(extents.y2 > extents.y1 && extents.x2 > extents.x1); 790 791 extents.x1 += dx; 792 extents.x2 += dx; 793 extents.y1 += dy; 794 extents.y2 += dy; 795 796 if (n == 1) 797 return __sna_damage_add_box(damage, &extents); 798 799 if (!damage) { 800 damage = _sna_damage_create(); 801 if (damage == NULL) 802 return NULL; 803 } else switch (damage->mode) { 804 case DAMAGE_ALL: 805 return damage; 806 case DAMAGE_SUBTRACT: 807 __sna_damage_reduce(damage); 808 case DAMAGE_ADD: 809 break; 810 } 811 812 if (pixman_region_contains_rectangle(&damage->region, 813 &extents) == PIXMAN_REGION_IN) 814 return damage; 815 816 damage_union(damage, &extents); 817 return _sna_damage_create_elt_from_rectangles(damage, r, n, dx, dy); 818} 819 820#if HAS_DEBUG_FULL 821struct sna_damage *_sna_damage_add_rectangles(struct sna_damage *damage, 822 const xRectangle *r, int n, 823 int16_t dx, int16_t dy) 824{ 825 char damage_buf[1000]; 826 827 DBG(("%s(%s + [(%d, %d)x(%d, %d) ... x %d])\n", __FUNCTION__, 828 _debug_describe_damage(damage_buf, sizeof(damage_buf), damage), 829 r->x, r->y, r->width, r->height, n)); 830 831 damage = __sna_damage_add_rectangles(damage, r, n, dx, dy); 832 833 ErrorF(" = %s\n", 834 _debug_describe_damage(damage_buf, sizeof(damage_buf), damage)); 835 if (RegionNumRects(&damage->region)) { 836 assert(damage->region.extents.x2 > damage->region.extents.x1); 837 assert(damage->region.extents.y2 > damage->region.extents.y1); 838 } 839 840 return damage; 841} 842#else 843struct sna_damage *_sna_damage_add_rectangles(struct sna_damage *damage, 844 const xRectangle *r, int n, 845 int16_t dx, int16_t dy) 846{ 847 return __sna_damage_add_rectangles(damage, r, n, dx, dy); 848} 849#endif 850 851/* XXX pass in extents? */ 852inline static struct sna_damage * 853__sna_damage_add_points(struct sna_damage *damage, 854 const DDXPointRec *p, int n, 855 int16_t dx, int16_t dy) 856{ 857 BoxRec extents; 858 int i; 859 860 assert(n); 861 862 extents.x2 = extents.x1 = p[0].x; 863 extents.y2 = extents.y1 = p[0].y; 864 for (i = 1; i < n; i++) { 865 if (extents.x1 > p[i].x) 866 extents.x1 = p[i].x; 867 else if (extents.x2 < p[i].x) 868 extents.x2 = p[i].x; 869 if (extents.y1 > p[i].y) 870 extents.y1 = p[i].y; 871 else if (extents.y2 < p[i].y) 872 extents.y2 = p[i].y; 873 } 874 875 extents.x1 += dx; 876 extents.x2 += dx + 1; 877 extents.y1 += dy; 878 extents.y2 += dy + 1; 879 880 if (n == 1) 881 return __sna_damage_add_box(damage, &extents); 882 883 if (!damage) { 884 damage = _sna_damage_create(); 885 if (damage == NULL) 886 return NULL; 887 } else switch (damage->mode) { 888 case DAMAGE_ALL: 889 return damage; 890 case DAMAGE_SUBTRACT: 891 __sna_damage_reduce(damage); 892 case DAMAGE_ADD: 893 break; 894 } 895 896 if (pixman_region_contains_rectangle(&damage->region, 897 &extents) == PIXMAN_REGION_IN) 898 return damage; 899 900 damage_union(damage, &extents); 901 _sna_damage_create_elt_from_points(damage, p, n, dx, dy); 902 903 return damage; 904} 905 906#if HAS_DEBUG_FULL 907struct sna_damage *_sna_damage_add_points(struct sna_damage *damage, 908 const DDXPointRec *p, int n, 909 int16_t dx, int16_t dy) 910{ 911 char damage_buf[1000]; 912 913 DBG(("%s(%s + [(%d, %d) ... x %d])\n", __FUNCTION__, 914 _debug_describe_damage(damage_buf, sizeof(damage_buf), damage), 915 p->x, p->y, n)); 916 917 damage = __sna_damage_add_points(damage, p, n, dx, dy); 918 919 ErrorF(" = %s\n", 920 _debug_describe_damage(damage_buf, sizeof(damage_buf), damage)); 921 if (RegionNumRects(&damage->region)) { 922 assert(damage->region.extents.x2 > damage->region.extents.x1); 923 assert(damage->region.extents.y2 > damage->region.extents.y1); 924 } 925 926 return damage; 927} 928#else 929struct sna_damage *_sna_damage_add_points(struct sna_damage *damage, 930 const DDXPointRec *p, int n, 931 int16_t dx, int16_t dy) 932{ 933 return __sna_damage_add_points(damage, p, n, dx, dy); 934} 935#endif 936 937#if HAS_DEBUG_FULL 938fastcall struct sna_damage *_sna_damage_add_box(struct sna_damage *damage, 939 const BoxRec *box) 940{ 941 char damage_buf[1000]; 942 943 DBG(("%s(%s + [(%d, %d), (%d, %d)])\n", __FUNCTION__, 944 _debug_describe_damage(damage_buf, sizeof(damage_buf), damage), 945 box->x1, box->y1, box->x2, box->y2)); 946 947 damage = __sna_damage_add_box(damage, box); 948 949 ErrorF(" = %s\n", 950 _debug_describe_damage(damage_buf, sizeof(damage_buf), damage)); 951 assert(RegionNumRects(&damage->region)); 952 assert(damage->region.extents.x2 > damage->region.extents.x1); 953 assert(damage->region.extents.y2 > damage->region.extents.y1); 954 955 return damage; 956} 957#else 958fastcall struct sna_damage *_sna_damage_add_box(struct sna_damage *damage, 959 const BoxRec *box) 960{ 961 return __sna_damage_add_box(damage, box); 962} 963#endif 964 965struct sna_damage *__sna_damage_all(struct sna_damage *damage, 966 int width, int height) 967{ 968 DBG(("%s(%d, %d)\n", __FUNCTION__, width, height)); 969 970 if (damage) { 971 pixman_region_fini(&damage->region); 972 free_list(&damage->embedded_box.list); 973 reset_embedded_box(damage); 974 } else { 975 damage = _sna_damage_create(); 976 if (damage == NULL) 977 return NULL; 978 } 979 980 pixman_region_init_rect(&damage->region, 0, 0, width, height); 981 damage->extents = damage->region.extents; 982 damage->mode = DAMAGE_ALL; 983 984 return damage; 985} 986 987struct sna_damage *_sna_damage_is_all(struct sna_damage *damage, 988 int width, int height) 989{ 990 DBG(("%s(%d, %d)%s?\n", __FUNCTION__, width, height, 991 damage->dirty ? "*" : "")); 992 DBG(("%s: (%d, %d), (%d, %d)\n", __FUNCTION__, 993 damage->extents.x1, damage->extents.y1, 994 damage->extents.x2, damage->extents.y2)); 995 996 assert(damage->mode == DAMAGE_ADD); 997 assert(damage->extents.x1 == 0 && 998 damage->extents.y1 == 0 && 999 damage->extents.x2 == width && 1000 damage->extents.y2 == height); 1001 1002 if (damage->dirty) { 1003 __sna_damage_reduce(damage); 1004 assert(RegionNotEmpty(&damage->region)); 1005 } 1006 1007 if (damage->region.data) { 1008 DBG(("%s: no, not singular\n", __FUNCTION__)); 1009 return damage; 1010 } 1011 1012 assert(damage->extents.x1 == 0 && 1013 damage->extents.y1 == 0 && 1014 damage->extents.x2 == width && 1015 damage->extents.y2 == height); 1016 1017 return __sna_damage_all(damage, width, height); 1018} 1019 1020static bool box_contains(const BoxRec *a, const BoxRec *b) 1021{ 1022 if (b->x1 < a->x1 || b->x2 > a->x2) 1023 return false; 1024 1025 if (b->y1 < a->y1 || b->y2 > a->y2) 1026 return false; 1027 1028 return true; 1029} 1030 1031static struct sna_damage *__sna_damage_subtract(struct sna_damage *damage, 1032 RegionPtr region) 1033{ 1034 if (damage == NULL) 1035 return NULL; 1036 1037 if (RegionNil(&damage->region)) { 1038no_damage: 1039 __sna_damage_destroy(damage); 1040 return NULL; 1041 } 1042 1043 assert(RegionNotEmpty(region)); 1044 1045 if (!sna_damage_overlaps_box(damage, ®ion->extents)) 1046 return damage; 1047 1048 if (region_is_singular(region) && 1049 box_contains(®ion->extents, &damage->extents)) 1050 goto no_damage; 1051 1052 if (damage->mode == DAMAGE_ALL) { 1053 pixman_region_subtract(&damage->region, 1054 &damage->region, 1055 region); 1056 if (damage->region.extents.x2 <= damage->region.extents.x1 || 1057 damage->region.extents.y2 <= damage->region.extents.y1) 1058 goto no_damage; 1059 1060 damage->extents = damage->region.extents; 1061 damage->mode = DAMAGE_ADD; 1062 return damage; 1063 } 1064 1065 if (damage->mode != DAMAGE_SUBTRACT) { 1066 if (damage->dirty) { 1067 __sna_damage_reduce(damage); 1068 assert(RegionNotEmpty(&damage->region)); 1069 } 1070 1071 if (pixman_region_equal(region, &damage->region)) 1072 goto no_damage; 1073 1074 if (region_is_singular(&damage->region) && 1075 region_is_singular(region)) { 1076 pixman_region_subtract(&damage->region, 1077 &damage->region, 1078 region); 1079 if (damage->region.extents.x2 <= damage->region.extents.x1 || 1080 damage->region.extents.y2 <= damage->region.extents.y1) 1081 goto no_damage; 1082 1083 damage->extents = damage->region.extents; 1084 assert(pixman_region_not_empty(&damage->region)); 1085 return damage; 1086 } 1087 1088 damage->mode = DAMAGE_SUBTRACT; 1089 } 1090 1091 return _sna_damage_create_elt(damage, 1092 REGION_RECTS(region), 1093 REGION_NUM_RECTS(region)); 1094} 1095 1096#if HAS_DEBUG_FULL 1097fastcall struct sna_damage *_sna_damage_subtract(struct sna_damage *damage, 1098 RegionPtr region) 1099{ 1100 char damage_buf[1000]; 1101 char region_buf[120]; 1102 1103 ErrorF("%s(%s - %s)...\n", __FUNCTION__, 1104 _debug_describe_damage(damage_buf, sizeof(damage_buf), damage), 1105 _debug_describe_region(region_buf, sizeof(region_buf), region)); 1106 1107 damage = __sna_damage_subtract(damage, region); 1108 1109 ErrorF(" = %s\n", 1110 _debug_describe_damage(damage_buf, sizeof(damage_buf), damage)); 1111 1112 return damage; 1113} 1114#else 1115fastcall struct sna_damage *_sna_damage_subtract(struct sna_damage *damage, 1116 RegionPtr region) 1117{ 1118 return __sna_damage_subtract(damage, region); 1119} 1120#endif 1121 1122inline static struct sna_damage *__sna_damage_subtract_box(struct sna_damage *damage, 1123 const BoxRec *box) 1124{ 1125 assert(box->x2 > box->x1 && box->y2 > box->y1); 1126 1127 if (damage == NULL) 1128 return NULL; 1129 1130 if (RegionNil(&damage->region)) { 1131 __sna_damage_destroy(damage); 1132 return NULL; 1133 } 1134 1135 if (!sna_damage_overlaps_box(damage, box)) 1136 return damage; 1137 1138 if (box_contains(box, &damage->extents)) { 1139 __sna_damage_destroy(damage); 1140 return NULL; 1141 } 1142 1143 if (damage->mode != DAMAGE_SUBTRACT) { 1144 if (damage->dirty) { 1145 __sna_damage_reduce(damage); 1146 assert(RegionNotEmpty(&damage->region)); 1147 } 1148 1149 if (region_is_singular(&damage->region)) { 1150 pixman_region16_t region; 1151 1152 pixman_region_init_rects(®ion, box, 1); 1153 pixman_region_subtract(&damage->region, 1154 &damage->region, 1155 ®ion); 1156 damage->extents = damage->region.extents; 1157 damage->mode = DAMAGE_ADD; 1158 return damage; 1159 } 1160 1161 damage->mode = DAMAGE_SUBTRACT; 1162 } 1163 1164 return _sna_damage_create_elt(damage, box, 1); 1165} 1166 1167#if HAS_DEBUG_FULL 1168fastcall struct sna_damage *_sna_damage_subtract_box(struct sna_damage *damage, 1169 const BoxRec *box) 1170{ 1171 char damage_buf[1000]; 1172 1173 ErrorF("%s(%s - (%d, %d), (%d, %d))...\n", __FUNCTION__, 1174 _debug_describe_damage(damage_buf, sizeof(damage_buf), damage), 1175 box->x1, box->y1, box->x2, box->y2); 1176 1177 damage = __sna_damage_subtract_box(damage, box); 1178 1179 ErrorF(" = %s\n", 1180 _debug_describe_damage(damage_buf, sizeof(damage_buf), damage)); 1181 1182 return damage; 1183} 1184#else 1185fastcall struct sna_damage *_sna_damage_subtract_box(struct sna_damage *damage, 1186 const BoxRec *box) 1187{ 1188 return __sna_damage_subtract_box(damage, box); 1189} 1190#endif 1191 1192static struct sna_damage *__sna_damage_subtract_boxes(struct sna_damage *damage, 1193 const BoxRec *box, int n, 1194 int dx, int dy) 1195{ 1196 BoxRec extents; 1197 int i; 1198 1199 if (damage == NULL) 1200 return NULL; 1201 1202 if (RegionNil(&damage->region)) { 1203 __sna_damage_destroy(damage); 1204 return NULL; 1205 } 1206 1207 assert(n); 1208 1209 assert(box[0].x2 > box[0].x1 && box[0].y2 > box[0].y1); 1210 extents = box[0]; 1211 for (i = 1; i < n; i++) { 1212 assert(box[i].x2 > box[i].x1 && box[i].y2 > box[i].y1); 1213 if (extents.x1 > box[i].x1) 1214 extents.x1 = box[i].x1; 1215 if (extents.x2 < box[i].x2) 1216 extents.x2 = box[i].x2; 1217 if (extents.y1 > box[i].y1) 1218 extents.y1 = box[i].y1; 1219 if (extents.y2 < box[i].y2) 1220 extents.y2 = box[i].y2; 1221 } 1222 1223 assert(extents.y2 > extents.y1 && extents.x2 > extents.x1); 1224 1225 extents.x1 += dx; 1226 extents.x2 += dx; 1227 extents.y1 += dy; 1228 extents.y2 += dy; 1229 1230 if (!sna_damage_overlaps_box(damage, &extents)) 1231 return damage; 1232 1233 if (n == 1) 1234 return __sna_damage_subtract_box(damage, &extents); 1235 1236 if (damage->mode != DAMAGE_SUBTRACT) { 1237 if (damage->dirty) { 1238 __sna_damage_reduce(damage); 1239 assert(RegionNotEmpty(&damage->region)); 1240 } 1241 1242 damage->mode = DAMAGE_SUBTRACT; 1243 } 1244 1245 return _sna_damage_create_elt_from_boxes(damage, box, n, dx, dy); 1246} 1247 1248#if HAS_DEBUG_FULL 1249fastcall struct sna_damage *_sna_damage_subtract_boxes(struct sna_damage *damage, 1250 const BoxRec *box, int n, 1251 int dx, int dy) 1252{ 1253 char damage_buf[1000]; 1254 1255 ErrorF("%s(%s - [(%d,%d), (%d,%d)...x%d])...\n", __FUNCTION__, 1256 _debug_describe_damage(damage_buf, sizeof(damage_buf), damage), 1257 box->x1 + dx, box->y1 + dy, 1258 box->x2 + dx, box->y2 + dy, 1259 n); 1260 1261 damage = __sna_damage_subtract_boxes(damage, box, n, dx, dy); 1262 1263 ErrorF(" = %s\n", 1264 _debug_describe_damage(damage_buf, sizeof(damage_buf), damage)); 1265 1266 return damage; 1267} 1268#else 1269fastcall struct sna_damage *_sna_damage_subtract_boxes(struct sna_damage *damage, 1270 const BoxRec *box, int n, 1271 int dx, int dy) 1272{ 1273 return __sna_damage_subtract_boxes(damage, box, n, dx, dy); 1274} 1275#endif 1276 1277static int __sna_damage_contains_box(struct sna_damage *damage, 1278 const BoxRec *box) 1279{ 1280 int ret; 1281 1282 if (!damage) 1283 return PIXMAN_REGION_OUT; 1284 1285 if (damage->mode == DAMAGE_ALL) 1286 return PIXMAN_REGION_IN; 1287 1288 if (!sna_damage_overlaps_box(damage, box)) 1289 return PIXMAN_REGION_OUT; 1290 1291 ret = pixman_region_contains_rectangle(&damage->region, (BoxPtr)box); 1292 if (!damage->dirty) 1293 return ret; 1294 1295 if (damage->mode == DAMAGE_ADD) { 1296 if (ret == PIXMAN_REGION_IN) 1297 return ret; 1298 } else { 1299 if (ret == PIXMAN_REGION_OUT) 1300 return ret; 1301 } 1302 1303 __sna_damage_reduce(damage); 1304 return pixman_region_contains_rectangle(&damage->region, (BoxPtr)box); 1305} 1306 1307#if HAS_DEBUG_FULL 1308int _sna_damage_contains_box(struct sna_damage *damage, 1309 const BoxRec *box) 1310{ 1311 char damage_buf[1000]; 1312 int ret; 1313 1314 DBG(("%s(%s, [(%d, %d), (%d, %d)])\n", __FUNCTION__, 1315 _debug_describe_damage(damage_buf, sizeof(damage_buf), damage), 1316 box->x1, box->y1, box->x2, box->y2)); 1317 1318 ret = __sna_damage_contains_box(damage, box); 1319 ErrorF(" = %d", ret); 1320 if (ret) 1321 ErrorF(" [(%d, %d), (%d, %d)...]", 1322 box->x1, box->y1, box->x2, box->y2); 1323 ErrorF("\n"); 1324 1325 return ret; 1326} 1327#else 1328int _sna_damage_contains_box(struct sna_damage *damage, 1329 const BoxRec *box) 1330{ 1331 return __sna_damage_contains_box(damage, box); 1332} 1333#endif 1334 1335static bool box_overlaps(const BoxRec *a, const BoxRec *b) 1336{ 1337 return (a->x1 < b->x2 && a->x2 > b->x1 && 1338 a->y1 < b->y2 && a->y2 > b->y1); 1339} 1340 1341bool _sna_damage_contains_box__no_reduce(const struct sna_damage *damage, 1342 const BoxRec *box) 1343{ 1344 int n, count; 1345 const BoxRec *b; 1346 1347 assert(damage && damage->mode != DAMAGE_ALL); 1348 if (!box_contains(&damage->extents, box)) 1349 return false; 1350 1351 n = pixman_region_contains_rectangle((pixman_region16_t *)&damage->region, (BoxPtr)box); 1352 if (!damage->dirty) 1353 return n == PIXMAN_REGION_IN; 1354 1355 if (damage->mode == DAMAGE_ADD) { 1356 if (n == PIXMAN_REGION_IN) 1357 return true; 1358 1359 count = damage->embedded_box.size; 1360 if (list_is_empty(&damage->embedded_box.list)) 1361 count -= damage->remain; 1362 1363 b = damage->embedded_box.box; 1364 for (n = 0; n < count; n++) { 1365 if (box_contains(&b[n], box)) 1366 return true; 1367 } 1368 1369 return false; 1370 } else { 1371 if (n != PIXMAN_REGION_IN) 1372 return false; 1373 1374 if (!list_is_empty(&damage->embedded_box.list)) 1375 return false; 1376 1377 count = damage->embedded_box.size - damage->remain; 1378 b = damage->embedded_box.box; 1379 for (n = 0; n < count; n++) { 1380 if (box_overlaps(&b[n], box)) 1381 return false; 1382 } 1383 1384 return true; 1385 } 1386} 1387 1388static bool __sna_damage_intersect(struct sna_damage *damage, 1389 RegionPtr region, RegionPtr result) 1390{ 1391 assert(damage && damage->mode != DAMAGE_ALL); 1392 assert(RegionNotEmpty(region)); 1393 1394 if (region->extents.x2 <= damage->extents.x1 || 1395 region->extents.x1 >= damage->extents.x2) 1396 return false; 1397 1398 if (region->extents.y2 <= damage->extents.y1 || 1399 region->extents.y1 >= damage->extents.y2) 1400 return false; 1401 1402 if (damage->dirty) 1403 __sna_damage_reduce(damage); 1404 1405 if (!pixman_region_not_empty(&damage->region)) 1406 return false; 1407 1408 RegionNull(result); 1409 RegionIntersect(result, &damage->region, region); 1410 1411 return RegionNotEmpty(result); 1412} 1413 1414#if HAS_DEBUG_FULL 1415bool _sna_damage_intersect(struct sna_damage *damage, 1416 RegionPtr region, RegionPtr result) 1417{ 1418 char damage_buf[1000]; 1419 char region_buf[120]; 1420 bool ret; 1421 1422 ErrorF("%s(%s, %s)...\n", __FUNCTION__, 1423 _debug_describe_damage(damage_buf, sizeof(damage_buf), damage), 1424 _debug_describe_region(region_buf, sizeof(region_buf), region)); 1425 1426 ret = __sna_damage_intersect(damage, region, result); 1427 if (ret) 1428 ErrorF(" = %s\n", 1429 _debug_describe_region(region_buf, sizeof(region_buf), result)); 1430 else 1431 ErrorF(" = none\n"); 1432 1433 return ret; 1434} 1435#else 1436bool _sna_damage_intersect(struct sna_damage *damage, 1437 RegionPtr region, RegionPtr result) 1438{ 1439 return __sna_damage_intersect(damage, region, result); 1440} 1441#endif 1442 1443static int __sna_damage_get_boxes(struct sna_damage *damage, BoxPtr *boxes) 1444{ 1445 assert(damage && damage->mode != DAMAGE_ALL); 1446 1447 if (damage->dirty) 1448 __sna_damage_reduce(damage); 1449 1450 *boxes = REGION_RECTS(&damage->region); 1451 return REGION_NUM_RECTS(&damage->region); 1452} 1453 1454struct sna_damage *_sna_damage_reduce(struct sna_damage *damage) 1455{ 1456 DBG(("%s\n", __FUNCTION__)); 1457 1458 __sna_damage_reduce(damage); 1459 if (!pixman_region_not_empty(&damage->region)) { 1460 __sna_damage_destroy(damage); 1461 damage = NULL; 1462 } 1463 1464 return damage; 1465} 1466 1467#if HAS_DEBUG_FULL 1468int _sna_damage_get_boxes(struct sna_damage *damage, BoxPtr *boxes) 1469{ 1470 char damage_buf[1000]; 1471 int count; 1472 1473 ErrorF("%s(%s)...\n", __FUNCTION__, 1474 _debug_describe_damage(damage_buf, sizeof(damage_buf), damage)); 1475 1476 count = __sna_damage_get_boxes(damage, boxes); 1477 ErrorF(" = %d\n", count); 1478 1479 return count; 1480} 1481#else 1482int _sna_damage_get_boxes(struct sna_damage *damage, BoxPtr *boxes) 1483{ 1484 return __sna_damage_get_boxes(damage, boxes); 1485} 1486#endif 1487 1488struct sna_damage *_sna_damage_combine(struct sna_damage *l, 1489 struct sna_damage *r, 1490 int dx, int dy) 1491{ 1492 if (r->dirty) 1493 __sna_damage_reduce(r); 1494 1495 if (pixman_region_not_empty(&r->region)) { 1496 pixman_region_translate(&r->region, dx, dy); 1497 l = __sna_damage_add(l, &r->region); 1498 } 1499 1500 return l; 1501} 1502 1503void __sna_damage_destroy(struct sna_damage *damage) 1504{ 1505 free_list(&damage->embedded_box.list); 1506 1507 pixman_region_fini(&damage->region); 1508 *(void **)damage = __freed_damage; 1509 __freed_damage = damage; 1510} 1511 1512#if TEST_DAMAGE && HAS_DEBUG_FULL 1513struct sna_damage_selftest{ 1514 int width, height; 1515}; 1516 1517static void st_damage_init_random_box(struct sna_damage_selftest *test, 1518 BoxPtr box) 1519{ 1520 int x, y, w, h; 1521 1522 if (test->width == 1) { 1523 x = 0, w = 1; 1524 } else { 1525 x = rand() % (test->width - 1); 1526 w = 1 + rand() % (test->width - x - 1); 1527 } 1528 1529 if (test->height == 1) { 1530 y = 0, h = 1; 1531 } else { 1532 y = rand() % (test->height - 1); 1533 h = 1 + rand() % (test->height - y - 1); 1534 } 1535 1536 box->x1 = x; 1537 box->x2 = x+w; 1538 1539 box->y1 = y; 1540 box->y2 = y+h; 1541} 1542 1543static void st_damage_init_random_region1(struct sna_damage_selftest *test, 1544 pixman_region16_t *region) 1545{ 1546 int x, y, w, h; 1547 1548 if (test->width == 1) { 1549 x = 0, w = 1; 1550 } else { 1551 x = rand() % (test->width - 1); 1552 w = 1 + rand() % (test->width - x - 1); 1553 } 1554 1555 if (test->height == 1) { 1556 y = 0, h = 1; 1557 } else { 1558 y = rand() % (test->height - 1); 1559 h = 1 + rand() % (test->height - y - 1); 1560 } 1561 1562 pixman_region_init_rect(region, x, y, w, h); 1563} 1564 1565static void st_damage_add(struct sna_damage_selftest *test, 1566 struct sna_damage **damage, 1567 pixman_region16_t *region) 1568{ 1569 pixman_region16_t tmp; 1570 1571 st_damage_init_random_region1(test, &tmp); 1572 1573 if (!DAMAGE_IS_ALL(*damage)) 1574 sna_damage_add(damage, &tmp); 1575 pixman_region_union(region, region, &tmp); 1576} 1577 1578static void st_damage_add_box(struct sna_damage_selftest *test, 1579 struct sna_damage **damage, 1580 pixman_region16_t *region) 1581{ 1582 RegionRec r; 1583 1584 st_damage_init_random_box(test, &r.extents); 1585 r.data = NULL; 1586 1587 if (!DAMAGE_IS_ALL(*damage)) 1588 sna_damage_add_box(damage, &r.extents); 1589 pixman_region_union(region, region, &r); 1590} 1591 1592static void st_damage_subtract(struct sna_damage_selftest *test, 1593 struct sna_damage **damage, 1594 pixman_region16_t *region) 1595{ 1596 pixman_region16_t tmp; 1597 1598 st_damage_init_random_region1(test, &tmp); 1599 1600 sna_damage_subtract(damage, &tmp); 1601 pixman_region_subtract(region, region, &tmp); 1602} 1603 1604static void st_damage_subtract_box(struct sna_damage_selftest *test, 1605 struct sna_damage **damage, 1606 pixman_region16_t *region) 1607{ 1608 RegionRec r; 1609 1610 st_damage_init_random_box(test, &r.extents); 1611 r.data = NULL; 1612 1613 sna_damage_subtract_box(damage, &r.extents); 1614 pixman_region_subtract(region, region, &r); 1615} 1616 1617static void st_damage_all(struct sna_damage_selftest *test, 1618 struct sna_damage **damage, 1619 pixman_region16_t *region) 1620{ 1621 pixman_region16_t tmp; 1622 1623 pixman_region_init_rect(&tmp, 0, 0, test->width, test->height); 1624 1625 if (!DAMAGE_IS_ALL(*damage)) 1626 sna_damage_all(damage, test->width, test->height); 1627 pixman_region_union(region, region, &tmp); 1628} 1629 1630static bool st_check_equal(struct sna_damage_selftest *test, 1631 struct sna_damage **damage, 1632 pixman_region16_t *region) 1633{ 1634 int d_num, r_num; 1635 BoxPtr d_boxes, r_boxes; 1636 1637 d_num = *damage ? sna_damage_get_boxes(*damage, &d_boxes) : 0; 1638 r_boxes = pixman_region_rectangles(region, &r_num); 1639 1640 if (d_num != r_num) { 1641 ErrorF("%s: damage and ref contain different number of rectangles\n", 1642 __FUNCTION__); 1643 return false; 1644 } 1645 1646 if (memcmp(d_boxes, r_boxes, d_num*sizeof(BoxRec))) { 1647 ErrorF("%s: damage and ref contain different rectangles\n", 1648 __FUNCTION__); 1649 return false; 1650 } 1651 1652 return true; 1653} 1654 1655void sna_damage_selftest(void) 1656{ 1657 void (*const op[])(struct sna_damage_selftest *test, 1658 struct sna_damage **damage, 1659 pixman_region16_t *region) = { 1660 st_damage_add, 1661 st_damage_add_box, 1662 st_damage_subtract, 1663 st_damage_subtract_box, 1664 st_damage_all 1665 }; 1666 bool (*const check[])(struct sna_damage_selftest *test, 1667 struct sna_damage **damage, 1668 pixman_region16_t *region) = { 1669 st_check_equal, 1670 //st_check_contains, 1671 }; 1672 char region_buf[120]; 1673 char damage_buf[1000]; 1674 int pass; 1675 1676 for (pass = 0; pass < 16384; pass++) { 1677 struct sna_damage_selftest test; 1678 struct sna_damage *damage; 1679 pixman_region16_t ref; 1680 int iter, i; 1681 1682 iter = 1 + rand() % (1 + (pass / 64)); 1683 ErrorF("%s: pass %d, iters=%d\n", __FUNCTION__, pass, iter); 1684 1685 test.width = 1 + rand() % 2048; 1686 test.height = 1 + rand() % 2048; 1687 1688 damage = _sna_damage_create(); 1689 pixman_region_init(&ref); 1690 1691 for (i = 0; i < iter; i++) { 1692 op[rand() % ARRAY_SIZE(op)](&test, &damage, &ref); 1693 } 1694 1695 if (!check[rand() % ARRAY_SIZE(check)](&test, &damage, &ref)) { 1696 ErrorF("%s: failed - region = %s, damage = %s\n", __FUNCTION__, 1697 _debug_describe_region(region_buf, sizeof(region_buf), &ref), 1698 _debug_describe_damage(damage_buf, sizeof(damage_buf), damage)); 1699 assert(0); 1700 } 1701 1702 pixman_region_fini(&ref); 1703 sna_damage_destroy(&damage); 1704 } 1705} 1706#endif 1707 1708void _sna_damage_debug_get_region(struct sna_damage *damage, RegionRec *r) 1709{ 1710 int n, nboxes; 1711 BoxPtr boxes; 1712 struct sna_damage_box *iter; 1713 1714 RegionCopy(r, &damage->region); 1715 if (!damage->dirty) 1716 return; 1717 1718 nboxes = damage->embedded_box.size; 1719 list_for_each_entry(iter, &damage->embedded_box.list, list) 1720 nboxes += iter->size; 1721 nboxes -= damage->remain; 1722 if (nboxes == 0) 1723 return; 1724 1725 if (nboxes == 1) { 1726 pixman_region16_t tmp; 1727 1728 tmp.extents = damage->embedded_box.box[0]; 1729 tmp.data = NULL; 1730 1731 if (damage->mode == DAMAGE_ADD) 1732 pixman_region_union(r, r, &tmp); 1733 else 1734 pixman_region_subtract(r, r, &tmp); 1735 1736 return; 1737 } 1738 1739 if (damage->mode == DAMAGE_ADD) 1740 nboxes += REGION_NUM_RECTS(r); 1741 1742 iter = list_entry(damage->embedded_box.list.prev, 1743 struct sna_damage_box, 1744 list); 1745 n = iter->size - damage->remain; 1746 boxes = malloc(sizeof(BoxRec)*nboxes); 1747 if (boxes == NULL) 1748 return; 1749 1750 if (list_is_empty(&damage->embedded_box.list)) { 1751 memcpy(boxes, 1752 damage->embedded_box.box, 1753 n*sizeof(BoxRec)); 1754 } else { 1755 if (boxes != (BoxPtr)(iter+1)) 1756 memcpy(boxes, iter+1, n*sizeof(BoxRec)); 1757 1758 iter = list_entry(iter->list.prev, 1759 struct sna_damage_box, 1760 list); 1761 while (&iter->list != &damage->embedded_box.list) { 1762 memcpy(boxes + n, iter+1, 1763 iter->size * sizeof(BoxRec)); 1764 n += iter->size; 1765 1766 iter = list_entry(iter->list.prev, 1767 struct sna_damage_box, 1768 list); 1769 } 1770 1771 memcpy(boxes + n, 1772 damage->embedded_box.box, 1773 sizeof(damage->embedded_box.box)); 1774 n += damage->embedded_box.size; 1775 } 1776 1777 if (damage->mode == DAMAGE_ADD) { 1778 memcpy(boxes + n, 1779 REGION_RECTS(r), 1780 REGION_NUM_RECTS(r)*sizeof(BoxRec)); 1781 assert(n + REGION_NUM_RECTS(r) == nboxes); 1782 pixman_region_fini(r); 1783 pixman_region_init_rects(r, boxes, nboxes); 1784 1785 assert(pixman_region_not_empty(r)); 1786 assert(damage->extents.x1 == r->extents.x1 && 1787 damage->extents.y1 == r->extents.y1 && 1788 damage->extents.x2 == r->extents.x2 && 1789 damage->extents.y2 == r->extents.y2); 1790 } else { 1791 pixman_region16_t tmp; 1792 1793 pixman_region_init_rects(&tmp, boxes, nboxes); 1794 pixman_region_subtract(r, r, &tmp); 1795 pixman_region_fini(&tmp); 1796 1797 assert(damage->extents.x1 <= r->extents.x1 && 1798 damage->extents.y1 <= r->extents.y1 && 1799 damage->extents.x2 >= r->extents.x2 && 1800 damage->extents.y2 >= r->extents.y2); 1801 } 1802 1803 free(boxes); 1804} 1805