From 8bea52bb0b55e2b041fbd43bc36221b5eb07b863 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sun, 12 Feb 2012 12:32:49 +0000 Subject: [PATCH] Add preliminary damage tracking This is initially based around the requirements for handling internal fallbacks to the image compositor and reducing the number of pixels required to be transferred. Signed-off-by: Chris Wilson --- src/Makefile.sources | 2 + src/cairo-compositor.c | 21 ++++ src/cairo-damage-private.h | 82 ++++++++++++++ src/cairo-damage.c | 214 ++++++++++++++++++++++++++++++++++++ src/cairo-image-surface.c | 6 +- src/cairo-region-private.h | 6 + src/cairo-region.c | 32 ++++++ src/cairo-surface-private.h | 1 + src/cairo-surface.c | 17 +++ src/cairo-types-private.h | 1 + 10 files changed, 379 insertions(+), 3 deletions(-) create mode 100644 src/cairo-damage-private.h create mode 100644 src/cairo-damage.c diff --git a/src/Makefile.sources b/src/Makefile.sources index 67e3537d1..6bcdd4357 100644 --- a/src/Makefile.sources +++ b/src/Makefile.sources @@ -66,6 +66,7 @@ cairo_private = \ cairo-compositor-private.h \ cairo-contour-private.h \ cairo-composite-rectangles-private.h \ + cairo-damage-private.h \ cairo-default-context-private.h \ cairo-device-private.h \ cairo-error-private.h \ @@ -142,6 +143,7 @@ cairo_sources = \ cairo-composite-rectangles.c \ cairo-compositor.c \ cairo-contour.c \ + cairo-damage.c \ cairo-debug.c \ cairo-default-context.c \ cairo-device.c \ diff --git a/src/cairo-compositor.c b/src/cairo-compositor.c index cf943e734..f6b06e57b 100644 --- a/src/cairo-compositor.c +++ b/src/cairo-compositor.c @@ -38,6 +38,7 @@ #include "cairoint.h" #include "cairo-compositor-private.h" +#include "cairo-damage-private.h" #include "cairo-error-private.h" cairo_int_status_t @@ -65,6 +66,10 @@ _cairo_compositor_paint (const cairo_compositor_t *compositor, compositor = compositor->delegate; } while (status == CAIRO_INT_STATUS_UNSUPPORTED); + if (status == CAIRO_INT_STATUS_SUCCESS && surface->damage) + surface->damage = _cairo_damage_add_rectangle (surface->damage, + &extents.unbounded); + _cairo_composite_rectangles_fini (&extents); return status; @@ -96,6 +101,10 @@ _cairo_compositor_mask (const cairo_compositor_t *compositor, compositor = compositor->delegate; } while (status == CAIRO_INT_STATUS_UNSUPPORTED); + if (status == CAIRO_INT_STATUS_SUCCESS && surface->damage) + surface->damage = _cairo_damage_add_rectangle (surface->damage, + &extents.unbounded); + _cairo_composite_rectangles_fini (&extents); return status; @@ -135,6 +144,10 @@ _cairo_compositor_stroke (const cairo_compositor_t *compositor, compositor = compositor->delegate; } while (status == CAIRO_INT_STATUS_UNSUPPORTED); + if (status == CAIRO_INT_STATUS_SUCCESS && surface->damage) + surface->damage = _cairo_damage_add_rectangle (surface->damage, + &extents.unbounded); + _cairo_composite_rectangles_fini (&extents); return status; @@ -170,6 +183,10 @@ _cairo_compositor_fill (const cairo_compositor_t *compositor, compositor = compositor->delegate; } while (status == CAIRO_INT_STATUS_UNSUPPORTED); + if (status == CAIRO_INT_STATUS_SUCCESS && surface->damage) + surface->damage = _cairo_damage_add_rectangle (surface->damage, + &extents.unbounded); + _cairo_composite_rectangles_fini (&extents); return status; @@ -207,6 +224,10 @@ _cairo_compositor_glyphs (const cairo_compositor_t *compositor, compositor = compositor->delegate; } while (status == CAIRO_INT_STATUS_UNSUPPORTED); + if (status == CAIRO_INT_STATUS_SUCCESS && surface->damage) + surface->damage = _cairo_damage_add_rectangle (surface->damage, + &extents.unbounded); + _cairo_composite_rectangles_fini (&extents); return status; diff --git a/src/cairo-damage-private.h b/src/cairo-damage-private.h new file mode 100644 index 000000000..28768fd30 --- /dev/null +++ b/src/cairo-damage-private.h @@ -0,0 +1,82 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2012 Intel Corporation + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Chris Wilson + * + * Contributor(s): + * Chris Wilson + */ + +#ifndef CAIRO_DAMAGE_PRIVATE_H +#define CAIRO_DAMAGE_PRIVATE_H + +#include "cairo-types-private.h" + +#include + +CAIRO_BEGIN_DECLS + +struct _cairo_damage { + cairo_status_t status; + cairo_region_t *region; + + int dirty, remain; + struct _cairo_damage_chunk { + struct _cairo_damage_chunk *next; + cairo_box_t *base; + int count; + int size; + } chunks, *tail; + cairo_box_t boxes[32]; +}; + +cairo_private cairo_damage_t * +_cairo_damage_create (void); + +cairo_private cairo_damage_t * +_cairo_damage_add_box (cairo_damage_t *damage, + const cairo_box_t *box); + +cairo_private cairo_damage_t * +_cairo_damage_add_rectangle (cairo_damage_t *damage, + const cairo_rectangle_int_t *rect); + +cairo_private cairo_damage_t * +_cairo_damage_add_region (cairo_damage_t *damage, + const cairo_region_t *region); + +cairo_private cairo_damage_t * +_cairo_damage_reduce (cairo_damage_t *damage); + +cairo_private void +_cairo_damage_destroy (cairo_damage_t *damage); + +CAIRO_END_DECLS + +#endif /* CAIRO_DAMAGE_PRIVATE_H */ diff --git a/src/cairo-damage.c b/src/cairo-damage.c new file mode 100644 index 000000000..7b95c845f --- /dev/null +++ b/src/cairo-damage.c @@ -0,0 +1,214 @@ +/* + * Copyright © 2012 Intel Corporation + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Chris Wilson + * + * Contributor(s): + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-damage-private.h" +#include "cairo-region-private.h" + +static const cairo_damage_t __cairo_damage__nil = { CAIRO_STATUS_NO_MEMORY }; + +cairo_damage_t * +_cairo_damage_create (void) +{ + cairo_damage_t *damage; + + damage = malloc (sizeof (*damage)); + if (unlikely (damage == NULL)) { + _cairo_error_throw(CAIRO_STATUS_NO_MEMORY); + return (cairo_damage_t *) &__cairo_damage__nil; + } + + damage->status = CAIRO_STATUS_SUCCESS; + damage->region = NULL; + damage->dirty = 0; + damage->tail = &damage->chunks; + damage->chunks.base = damage->boxes; + damage->chunks.size = ARRAY_LENGTH(damage->boxes); + damage->chunks.count = 0; + damage->chunks.next = NULL; + + damage->remain = damage->chunks.size; + + return damage; +} + +void +_cairo_damage_destroy (cairo_damage_t *damage) +{ + struct _cairo_damage_chunk *chunk, *next; + + for (chunk = damage->chunks.next; chunk != NULL; chunk = next) { + next = chunk->next; + free (chunk); + } + cairo_region_destroy (damage->region); + free (damage); +} + +static cairo_damage_t * +_cairo_damage_add_boxes(cairo_damage_t *damage, + const cairo_box_t *boxes, + int count) +{ + struct _cairo_damage_chunk *chunk; + int n, size; + + if (damage == NULL) + damage = _cairo_damage_create (); + + damage->dirty += count; + + n = count; + if (n > damage->remain) + n = damage->remain; + + memcpy (damage->tail->base + damage->tail->count, boxes, n); + + count -= n; + damage->tail->count += n; + damage->remain -= n; + + if (count == 0) + return damage; + + size = 2 * damage->tail->size; + if (size < count) + size = (count + 64) & ~63; + + chunk = malloc (sizeof (*chunk) + sizeof (cairo_box_t) * size); + if (unlikely (chunk == NULL)) { + _cairo_damage_destroy (damage); + return (cairo_damage_t *) &__cairo_damage__nil; + } + + chunk->next = NULL; + chunk->base = (cairo_box_t *) (chunk + 1); + chunk->size = size; + chunk->count = count; + + damage->tail->next = chunk; + damage->remain = size - count; + + memcpy (damage->tail->base, boxes + n, count); + + return damage; +} + +cairo_damage_t * +_cairo_damage_add_box(cairo_damage_t *damage, + const cairo_box_t *box) +{ + return _cairo_damage_add_boxes(damage, box, 1); +} + +cairo_damage_t * +_cairo_damage_add_rectangle(cairo_damage_t *damage, + const cairo_rectangle_int_t *r) +{ + cairo_box_t box; + + box.p1.x = r->x; + box.p1.y = r->y; + box.p2.x = r->x + r->width; + box.p2.y = r->y + r->height; + + return _cairo_damage_add_boxes(damage, &box, 1); +} + +cairo_damage_t * +_cairo_damage_add_region (cairo_damage_t *damage, + const cairo_region_t *region) +{ + cairo_box_t *boxes; + int nbox; + + boxes = _cairo_region_get_boxes (region, &nbox); + return _cairo_damage_add_boxes(damage, boxes, nbox); +} + +cairo_damage_t * +_cairo_damage_reduce (cairo_damage_t *damage) +{ + cairo_box_t *free_boxes = NULL; + cairo_box_t *boxes, *b; + struct _cairo_damage_chunk *chunk, *last; + + if (damage == NULL || !damage->dirty) + return damage; + + if (damage->region) { + cairo_region_t *region; + + region = damage->region; + damage->region = NULL; + + damage = _cairo_damage_add_region (damage, region); + cairo_region_destroy (region); + + if (unlikely (damage->status)) + return damage; + } + + boxes = damage->tail->base; + if (damage->dirty > damage->tail->size) { + boxes = free_boxes = malloc (damage->dirty * sizeof (cairo_box_t)); + if (unlikely (boxes == NULL)) { + _cairo_damage_destroy (damage); + return (cairo_damage_t *) &__cairo_damage__nil; + } + + b = boxes; + last = NULL; + } else { + b = boxes + damage->tail->count; + last = damage->tail; + } + + for (chunk = &damage->chunks; chunk != last; chunk = chunk->next) { + memcpy (b, chunk->base, chunk->count * sizeof (cairo_box_t)); + b += chunk->count; + } + + damage->region = _cairo_region_create_from_boxes (boxes, damage->dirty); + free (free_boxes); + + if (unlikely (damage->region->status)) { + _cairo_damage_destroy (damage); + return (cairo_damage_t *) &__cairo_damage__nil; + } + + damage->dirty = 0; + return damage; +} diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c index 05c99b778..fede14cd7 100644 --- a/src/cairo-image-surface.c +++ b/src/cairo-image-surface.c @@ -544,7 +544,7 @@ slim_hidden_def (cairo_image_surface_create_for_data); * * Since: 1.2 **/ - unsigned char * +unsigned char * cairo_image_surface_get_data (cairo_surface_t *surface) { cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface; @@ -970,10 +970,10 @@ const cairo_surface_backend_t _cairo_image_surface_backend = { NULL, /* show_page */ _cairo_image_surface_get_extents, - _cairo_image_surface_get_font_options, + NULL, NULL, /* flush */ - NULL, /* mark dirty */ + NULL, _cairo_image_surface_paint, _cairo_image_surface_mask, diff --git a/src/cairo-region-private.h b/src/cairo-region-private.h index 11070ba76..549e50878 100644 --- a/src/cairo-region-private.h +++ b/src/cairo-region-private.h @@ -66,6 +66,12 @@ _cairo_region_init_rectangle (cairo_region_t *region, cairo_private void _cairo_region_fini (cairo_region_t *region); +cairo_private cairo_region_t * +_cairo_region_create_from_boxes (const cairo_box_t *boxes, int count); + +cairo_private cairo_box_t * +_cairo_region_get_boxes (const cairo_region_t *region, int *nbox); + CAIRO_END_DECLS #endif /* CAIRO_REGION_PRIVATE_H */ diff --git a/src/cairo-region.c b/src/cairo-region.c index f3ccb898a..a7ec50cba 100644 --- a/src/cairo-region.c +++ b/src/cairo-region.c @@ -276,6 +276,38 @@ cairo_region_create_rectangles (const cairo_rectangle_int_t *rects, } slim_hidden_def (cairo_region_create_rectangles); +cairo_region_t * +_cairo_region_create_from_boxes (const cairo_box_t *boxes, int count) +{ + cairo_region_t *region; + + region = _cairo_malloc (sizeof (cairo_region_t)); + if (unlikely (region == NULL)) + return _cairo_region_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + CAIRO_REFERENCE_COUNT_INIT (®ion->ref_count, 1); + region->status = CAIRO_STATUS_SUCCESS; + + if (! pixman_region32_init_rects (®ion->rgn, + (pixman_box32_t *)boxes, count)) { + free (region); + return _cairo_region_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + return region; +} + +cairo_box_t * +_cairo_region_get_boxes (const cairo_region_t *region, int *nbox) +{ + if (region->status) { + nbox = 0; + return NULL; + } + + return (cairo_box_t *) pixman_region32_rectangles (CONST_CAST ®ion->rgn, nbox); +} + /** * cairo_region_create_rectangle: * @rectangle: a #cairo_rectangle_int_t diff --git a/src/cairo-surface-private.h b/src/cairo-surface-private.h index af24e250a..657592d11 100644 --- a/src/cairo-surface-private.h +++ b/src/cairo-surface-private.h @@ -62,6 +62,7 @@ struct _cairo_surface { cairo_status_t status; unsigned int unique_id; unsigned int serial; + cairo_damage_t *damage; unsigned finished : 1; unsigned is_clear : 1; diff --git a/src/cairo-surface.c b/src/cairo-surface.c index 9a4b88d0c..13f0909ee 100644 --- a/src/cairo-surface.c +++ b/src/cairo-surface.c @@ -40,6 +40,7 @@ #include "cairo-array-private.h" #include "cairo-clip-private.h" +#include "cairo-damage-private.h" #include "cairo-device-private.h" #include "cairo-error-private.h" #include "cairo-image-surface-private.h" @@ -104,6 +105,7 @@ const cairo_surface_t name = { \ status, /* status */ \ 0, /* unique id */ \ 0, /* serial */ \ + NULL, /* damage */ \ FALSE, /* finished */ \ TRUE, /* is_clear */ \ FALSE, /* has_font_options */ \ @@ -410,6 +412,7 @@ _cairo_surface_init (cairo_surface_t *surface, surface->finished = FALSE; surface->is_clear = FALSE; surface->serial = 0; + surface->damage = NULL; surface->owns_device = (device != NULL); _cairo_user_data_array_init (&surface->user_data); @@ -845,6 +848,9 @@ cairo_surface_destroy (cairo_surface_t *surface) /* paranoid check that nobody took a reference whilst finishing */ assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count)); + if (surface->damage) + _cairo_damage_destroy (surface->damage); + _cairo_user_data_array_fini (&surface->user_data); _cairo_user_data_array_fini (&surface->mime_data); @@ -1424,6 +1430,17 @@ cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface, surface->is_clear = FALSE; surface->serial++; + if (surface->damage) { + cairo_box_t box; + + box.p1.x = x; + box.p1.y = y; + box.p2.x = x + width; + box.p2.y = y + height; + + surface->damage = _cairo_damage_add_box (surface->damage, &box); + } + if (surface->backend->mark_dirty_rectangle != NULL) { /* XXX: FRAGILE: We're ignoring the scaling component of * device_transform here. I don't know what the right thing to diff --git a/src/cairo-types-private.h b/src/cairo-types-private.h index 2fdd0a19a..3a1882be9 100644 --- a/src/cairo-types-private.h +++ b/src/cairo-types-private.h @@ -66,6 +66,7 @@ typedef struct _cairo_color_stop cairo_color_stop_t; typedef struct _cairo_contour cairo_contour_t; typedef struct _cairo_contour_chain cairo_contour_chain_t; typedef struct _cairo_contour_iter cairo_contour_iter_t; +typedef struct _cairo_damage cairo_damage_t; typedef struct _cairo_device_backend cairo_device_backend_t; typedef struct _cairo_font_face_backend cairo_font_face_backend_t; typedef struct _cairo_gstate cairo_gstate_t;