[Spice-devel] [PATCH] RFC: Document display channel drawable trees, etc
Frediano Ziglio
fziglio at redhat.com
Thu Jan 26 09:18:17 UTC 2017
>
> ---
> I started looking into a bug related to surfaces and drawing, but after a
> little initial investigation, I realized that I didn't really understand any
> of
> the code in this part of spice server. I discussed it a little bit with
> several
> others who also didn't have a good understanding of this part of the code. So
> I
> decided it needed to be documented so that we can understand it and work on
> it
> effectively. I've spent several long days trying to understand the details
> and
> trying to document them as well as I can. There is a lot of confusing
> terminology, and there are things I still don't fully understand, but I
> wanted
> to send this out to get feedback from others. Is it helpful? Do you see any
> cases where I misinterpreted things? Can anybody solve any pieces of the
> puzzle
> that I didn't quite figure out? etc. etc.
>
> I know it's rather dense and will probably require others to spend some
> serious
> time understanding the code, but I'd love to get some feedback on it.
>
Take some time to find this post, so before get really buried on the ML...
Some part are quite good and should be split and merged, other,
particularly exclude_region have too much questions to solve.
> server/display-channel-private.h | 5 +-
> server/display-channel.c | 256
> +++++++++++++++++++++++++++++++++++++++
> server/display-channel.h | 10 +-
> server/tree.c | 12 +-
> server/tree.h | 6 +
> 5 files changed, 285 insertions(+), 4 deletions(-)
>
> diff --git a/server/display-channel-private.h
> b/server/display-channel-private.h
> index 6e299b9..dc4d605 100644
> --- a/server/display-channel-private.h
> +++ b/server/display-channel-private.h
> @@ -32,7 +32,10 @@ struct DisplayChannelPrivate
> int enable_jpeg;
> int enable_zlib_glz_wrap;
>
> - Ring current_list; /* Drawable */
> + /* A ring of pending drawables for this DisplayChannel, regardless of which
> + * surface they're associated with. This list is mainly used to flush older
> + * drawables when we need to make room for new drawables. */
> + Ring current_list;
is this list containing ALL drawables allocated ?
Could be renamed to drawable_list ?
Wondering what these "current" are...
> uint32_t current_size;
>
> uint32_t drawable_count;
> diff --git a/server/display-channel.c b/server/display-channel.c
> index 6069883..83e76c1 100644
> --- a/server/display-channel.c
> +++ b/server/display-channel.c
> @@ -393,6 +393,8 @@ static void current_add_drawable(DisplayChannel *display,
> drawable->refs++;
> }
>
> +/* Unrefs the drawable and removes it from any rings that it's in, as well as
> + * removing any associated shadow item */
> static void current_remove_drawable(DisplayChannel *display, Drawable *item)
display_channel_remove_drawable ?
display_channel_unlink_drawable ?
> {
> /* todo: move all to unref? */
> @@ -422,6 +424,7 @@ static void drawable_remove_from_pipes(Drawable
> *drawable)
> }
> }
>
> +/* This function should never be called for Shadow TreeItems */
> static void current_remove(DisplayChannel *display, TreeItem *item)
> {
> TreeItem *now = item;
> @@ -439,22 +442,38 @@ static void current_remove(DisplayChannel *display,
> TreeItem *item)
> } else {
> Container *now_as_container = CONTAINER(now);
>
> + /* This is a questionable assert. It relies several assumptions
> + * to ensure that this function is never called for a
> + * TREE_ITEM_TYPE_SHADOW item. */
> spice_assert(now->type == TREE_ITEM_TYPE_CONTAINER);
>
> if ((ring_item = ring_get_head(&now_as_container->items))) {
> + /* descend into the container's child ring and continue
> + * iterating and removing those children */
> now = SPICE_CONTAINEROF(ring_item, TreeItem, siblings_link);
> continue;
> }
> + /* This item is a container but it has no children, so reset our
> + * iterator to the item's previous sibling and free this empty
> + * container */
Why a container without child? Maybe the result of other
processing?
> ring_item = now->siblings_link.prev;
> container_free(now_as_container);
> }
> if (now == item) {
> + /* This is true if the initial @item was a DRAWABLE, or if @item
> + * was a container and we've finished iterating over all of that
> + * container's children and returned back up to the parent and
> + * freed it (see below) */
> return;
> }
>
> + /* Increment the iterator to the next sibling. Note that if an item was
> + * removed above, ring_item will have been reset to the item before the
> + * item that was removed */
> if ((ring_item = ring_next(&container_of_now->items, ring_item))) {
> now = SPICE_CONTAINEROF(ring_item, TreeItem, siblings_link);
> } else {
> + /* there is no next sibling, so move one level up the tree */
> now = &container_of_now->base;
> }
> }
> @@ -467,10 +486,23 @@ static void current_remove_all(DisplayChannel *display,
> int surface_id)
>
> while ((ring_item = ring_get_head(ring))) {
> TreeItem *now = SPICE_CONTAINEROF(ring_item, TreeItem,
> siblings_link);
> + /* NOTE: current_remove() should never be called on Shadow type items
> + * or we will hit an assert. Fortunately, the 'current' ring is ordered
> + * in such a way that a DrawItem will always be placed before its
> + * associated Shadow in the tree. Since removing a DrawItem will also
> + * result in the associated Shadow item being removed from the tree,
> + * this loop will never call current_remove() on a Shadow item unless
> + * we change the order that items are inserted into the tree */
> current_remove(display, now);
> }
> }
>
> +/* Replace an existing Drawable in the tree with a new drawable that is
> + * equivalent. The new drawable is also added to the pipe.
> + *
> + * This function can fail if the items aren't actually equivalent (e.g. either
> + * item has a shadow, they have different effects, etc)
> + */
> static int current_add_equal(DisplayChannel *display, DrawItem *item,
> TreeItem *other)
> {
> DrawItem *other_draw_item;
> @@ -490,6 +522,9 @@ static int current_add_equal(DisplayChannel *display,
> DrawItem *item, TreeItem *
> other_drawable = SPICE_CONTAINEROF(other_draw_item, Drawable,
> tree_item);
>
> if (item->effect == QXL_EFFECT_OPAQUE) {
> + /* check whether the new item can safely replace the other drawable at
> + * the same position in the pipe, or whether it should be added to the
> + * end of the queue */
> int add_after = !!other_drawable->stream &&
> is_drawable_independent_from_surfaces(drawable);
> stream_maintenance(display, drawable, other_drawable);
Here questions about how stream are handled raise. Why stream need special
handling?
> @@ -553,6 +588,47 @@ static int current_add_equal(DisplayChannel *display,
> DrawItem *item, TreeItem *
> return FALSE;
> }
>
> +/* This function excludes the region from a single TreeItem
> + *
> + * If there is overlap between @rgn and the @item region, remove the
> + *
> + * overlapping intersection from both @rgn and the item's region (XXX WHY???)
> + *
> + * However, if the item is a DrawItem that has a shadow, we add an additional
> + * region to @rgn: the intersection of the shadow item's region with @rgn if
> + * @rgn was shifted over by the delta between the DrawItem and the Shadow.
> + * [WORKING THEORY: since the destination region for a COPY_BITS operation was
> + * excluded, we no longer need the source region that corresponds with that
> + * copy operation, so we can also exclude any drawables that affect that
> + * region. Not sure if that actually makes sense... ]
> + *
> + * If the item is a Shadow, we store the intersection between @rgn and the
> + * Shadow's region in Shadow::on_hold and remove that region from @rgn. This is
> + * done since a Shadow represents the source region for a COPY_BITS operation,
> + * and we need to make sure that this source region stays up-to-date until the
> + * copy operation is executed.
> + *
> + * Consider the following case:
> + * 1) the surface is fully black at the beginning
> + * 2) we add a new item to the tree which paints region A white
> + * 3) we add a new item to the tree which copies region A to region B
> + * 4) we add another new item to teh tree painting region A blue.
> + *
> + * After all operations are completed, region A should be blue, and region B
> + * should be white. If there were no copy operation (step 3), we could simply
> + * eliminate step 2 when we add item 4 to the tree, since step 4 overwrites the
> + * same region with a different color. However, if we did eliminate step 2,
> + * region B would be black after all operations were completed. So these
> + * regions that would normally be excluded are put "on hold" if they are part
> + * of a source region for a copy operation.
> + *
> + * @display: the display channel
> + * @ring: a fallback toplevel ring???
> + * @item: the tree item to exclude from @rgn
> + * @rgn: the region to exclude (?)
> + * @top_ring: ?
> + * @frame_candidate: ?
> + */
Need time to read again all this but looks like there is a lot of mechanical
"understanding" but no real understanding of the intention of these 2
(__exclude_region and exclude_region) functions.
> static void __exclude_region(DisplayChannel *display, Ring *ring, TreeItem
> *item, QRegion *rgn,
> Ring **top_ring, Drawable *frame_candidate)
> {
> @@ -560,51 +636,79 @@ static void __exclude_region(DisplayChannel *display,
> Ring *ring, TreeItem *item
> stat_start(&display->priv->__exclude_stat, start_time);
>
> region_clone(&and_rgn, rgn);
> + /* find intersection of the @rgn argument with the region of the @item arg */
> region_and(&and_rgn, &item->rgn);
> if (!region_is_empty(&and_rgn)) {
> if (IS_DRAW_ITEM(item)) {
> DrawItem *draw = DRAW_ITEM(item);
>
> if (draw->effect == QXL_EFFECT_OPAQUE) {
> + /* remove the intersection from the original @rgn */
> region_exclude(rgn, &and_rgn);
> }
>
> if (draw->shadow) {
> + /* this item represents the destination of a COPY_BITS
> + * operation. 'shadow' represents the source item for the copy
> + * operation */
> Shadow *shadow;
> int32_t x = item->rgn.extents.x1;
> int32_t y = item->rgn.extents.y1;
>
> + /* remove the intersection from the item's region */
> region_exclude(&draw->base.rgn, &and_rgn);
> shadow = draw->shadow;
> + /* shift the intersected region by the difference between the
> + * source and destination regions */
> region_offset(&and_rgn, shadow->base.rgn.extents.x1 - x,
> shadow->base.rgn.extents.y1 - y);
> + /* remove the shifted intersection region from the source
> + * (shadow) item's region. If the destination is excluded, we
> + * can also exclude the corresponding area from the source
> */
> region_exclude(&shadow->base.rgn, &and_rgn);
> + /* find the intersection between the shifted intersection
> + * region and the Shadow's 'on_hold' region. This represents
> + * the portion of the Shadow's region that we just removed that
> + * is currently stored in on_hold. */
> region_and(&and_rgn, &shadow->on_hold);
> if (!region_is_empty(&and_rgn)) {
> + /* Since we removed a portion of the Shadow's region, we
> + * can also remove that portion from on_hold */
> region_exclude(&shadow->on_hold, &and_rgn);
> + /* Since this region is no longer "on hold", add it back to
> + * the @rgn argument */
> region_or(rgn, &and_rgn);
> // in flat representation of current, shadow is always
> his owner next
> + // XXX: what does that mean?
> if (!tree_item_contained_by(&shadow->base, *top_ring)) {
> *top_ring = tree_item_container_items(&shadow->base,
> ring);
> }
> }
> } else {
> + /* XXX: ??? */
> if (frame_candidate) {
> Drawable *drawable = SPICE_CONTAINEROF(draw, Drawable,
> tree_item);
> stream_maintenance(display, frame_candidate, drawable);
> }
> + /* Remove the intersection from the DrawItem's region */
> region_exclude(&draw->base.rgn, &and_rgn);
> }
> } else if (item->type == TREE_ITEM_TYPE_CONTAINER) {
> + /* excludes the intersection between 'rgn' and item->rgn from the
> + * item's region */
> region_exclude(&item->rgn, &and_rgn);
>
> if (region_is_empty(&item->rgn)) { //assume container removal
> will follow
> Shadow *shadow;
>
> + /* exclude the intersection from the 'rgn' argument as well,
> + * but only if the item is now empty... WHY??? */
> region_exclude(rgn, &and_rgn);
> if ((shadow = tree_item_find_shadow(item))) {
> + /* add the shadow's on_hold region back to the 'rgn' argument */
> region_or(rgn, &shadow->on_hold);
> if (!tree_item_contained_by(&shadow->base, *top_ring)) {
> + /* what is top_ring used for??? */
> *top_ring = tree_item_container_items(&shadow->base,
> ring);
> }
> }
> @@ -614,14 +718,43 @@ static void __exclude_region(DisplayChannel *display,
> Ring *ring, TreeItem *item
>
> spice_assert(item->type == TREE_ITEM_TYPE_SHADOW);
> shadow = SHADOW(item);
> + /* Since a Shadow represents the source region for a COPY_BITS
> + * operation, we need to make sure that we don't remove existing
> + * drawables that draw to this source region. If we did, it would
> + * affect the copy operation. So we remove the intersection between
> + * @rgn and item->rgn from the @rgn argument to avoid excluding
> + * these drawables */
> region_exclude(rgn, &and_rgn);
> + /* adds this intersection to on_hold */
> region_or(&shadow->on_hold, &and_rgn);
> }
> }
> + /* clean up memory */
> region_destroy(&and_rgn);
> stat_add(&display->priv->__exclude_stat, start_time);
> }
>
> +/* This function iterates through the given @ring starting at @ring_item and
> + * continuing until reaching @last, and calls __exclude_item() on each item.
> + * Any items that have an empty region as a result of the __exclude_region()
> + * call are removed from the tree.
> + *
Just as a ring of goes also inside containers ?
Also... when does a container is created ?
How are items arranged in the tree ?
> + * XXX: I still don't have a great conceptual understanding of what we're
> + * trying to do by calling this function.
> + *
This is the problem.
Maybe is creating an hole in the current "painting" ?
Let's define the painting the result of the screen resulting from the drawing of
the drawables, so at the beginning we could have a screen
+---------------+
| |
| |
| |
| |
| |
+---------------+
we draw an icon (very small, Drawable1)
+---------------+
| |
| + |
| |
| |
| |
+---------------+
Then we draw a window a big bigger on it (Drawable2)
+---------------+
| +---+ |
| | | |
| +---+ |
| |
| |
+---------------+
both Drawable1 and Drawable2 are opaque. Before adding Drawable2 we basically remove all
drawables under Drawable2 region. Maybe this is a call to exclude_region(... region_of_Drawable1 ...) ?
> + * @ring: every time this function is called, @ring is a Surface's 'current'
> + * ring, or to the ring of children of a container within that ring.
> + * @ring_item: callers usually call this argument 'exclude_base'. We will
> + * iterate through the tree starting at this item
> + * @rgn: callers usually call this 'exclude_rgn' -- it appears to be the region
> + * we want to exclude from existing items in the tree. It is an in/out
> + * parameter and it may be modified as the result of calling this function
> + * @last: We will stop iterating at this item, and the function will return the
> + * next item after iteration is complete (which may be different than the
> + * passed value if that item was removed from the tree
> + * @frame_candidate: usually callers pass NULL, sometimes it's the drawable
> + * that's being added to the 'current' ring. What is its purpose?
> + */
> static void exclude_region(DisplayChannel *display, Ring *ring, RingItem
> *ring_item,
> QRegion *rgn, TreeItem **last, Drawable
> *frame_candidate)
> {
> @@ -640,40 +773,60 @@ static void exclude_region(DisplayChannel *display,
> Ring *ring, RingItem *ring_i
>
> spice_assert(!region_is_empty(&now->rgn));
>
> + /* check whether the ring_item item intersects the passed-in region */
> if (region_intersects(rgn, &now->rgn)) {
> + /* remove the overlapping portions of region and now->rgn, among
> + * other things. See documentation for __exclude_region() */
> __exclude_region(display, ring, now, rgn, &top_ring,
> frame_candidate);
>
> if (region_is_empty(&now->rgn)) {
> + /* __exclude_region() does not remove the region of shadow-type
> + * items */
> spice_assert(now->type != TREE_ITEM_TYPE_SHADOW);
> ring_item = now->siblings_link.prev;
> + /* if __exclude_region() removed the entire region for this
> + * sibling item, remove it from the 'current' tree */
> current_remove(display, now);
> if (last && *last == now) {
> + /* the caller wanted to stop at this item, but this item
> + * has been removed, so we set @last to the next item */
> SPICE_VERIFY(SPICE_OFFSETOF(TreeItem, siblings_link) ==
> 0);
> *last = (TreeItem *)ring_next(ring, ring_item);
> }
> } else if (now->type == TREE_ITEM_TYPE_CONTAINER) {
> + /* if this sibling is a container type, descend into the
> + * container's child ring and continue iterating */
> Container *container = CONTAINER(now);
> if ((ring_item = ring_get_head(&container->items))) {
> ring = &container->items;
> spice_assert(SPICE_CONTAINEROF(ring_item, TreeItem,
> siblings_link)->container);
> continue;
> }
> + /* container had no children, so reset ring_item to the
> + * container itself */
> ring_item = &now->siblings_link;
> }
>
> if (region_is_empty(rgn)) {
> + /* __exclude_region() removed the entire region from 'rgn', so
> + * no need to continue checking further items in the tree */
I fail to understand, maybe I miss some knowledge about __exclude_region,
so __exclude_region changed rgn and now->rgn ?
> stat_add(&display->priv->exclude_stat, start_time);
> return;
> }
> }
>
> SPICE_VERIFY(SPICE_OFFSETOF(TreeItem, siblings_link) == 0);
> + /* if this is the last item to check, or if the current ring is
> + * completed, don't go any further */
> while ((last && *last == (TreeItem *)ring_item) ||
> !(ring_item = ring_next(ring, ring_item))) {
> + /* we're currently iterating the top ring, so we're done */
> if (ring == top_ring) {
> stat_add(&display->priv->exclude_stat, start_time);
> return;
> }
> + /* we're iterating through a container child ring, so climb one
> + * level up the heirarchy and continue iterating that ring */
Ok, so this function iterate all the tree from top to bottom.
> ring_item = &container->base.siblings_link;
> container = container->base.container;
> ring = (container) ? &container->items : top_ring;
> @@ -681,6 +834,8 @@ static void exclude_region(DisplayChannel *display, Ring
> *ring, RingItem *ring_i
> }
> }
>
> +/* Add a @drawable (with a shadow) to the current ring.
Which current ring are we taking about? Surface, DisplayChannel ?
> + * The return value indicates whether the new item should be added to the pipe */
> static int current_add_with_shadow(DisplayChannel *display, Ring *ring,
> Drawable *item)
> {
> stat_start(&display->priv->add_stat, start_time);
> @@ -707,11 +862,19 @@ static int current_add_with_shadow(DisplayChannel
> *display, Ring *ring, Drawable
> stream_detach_behind(display, &shadow->base.rgn, NULL);
> }
>
> + /* Prepend the shadow to the beginning of the current ring */
> ring_add(ring, &shadow->base.siblings_link);
> + /* Prepend the draw item to the beginning of the current ring. NOTE: this
> + * means that the drawable is placed *before* its associated shadow in the
> + * tree. Changing this order will violate several unstated assumptions
> */
> current_add_drawable(display, item, ring);
> if (item->tree_item.effect == QXL_EFFECT_OPAQUE) {
> QRegion exclude_rgn;
> region_clone(&exclude_rgn, &item->tree_item.base.rgn);
> + /* Since the new drawable is opaque, remove overlapped regions from all
> + * items already in the tree. Start iterating through the tree
> + * starting with the shadow item to avoid excluding the new item
> + * itself */
> exclude_region(display, ring, &shadow->base.siblings_link,
> &exclude_rgn, NULL, NULL);
> region_destroy(&exclude_rgn);
> streams_update_visible_region(display, item);
> @@ -724,6 +887,8 @@ static int current_add_with_shadow(DisplayChannel
> *display, Ring *ring, Drawable
> return TRUE;
> }
>
> +/* Add a @drawable (without a shadow) to the current ring.
> + * The return value indicates whether the new item should be added to the pipe */
> static int current_add(DisplayChannel *display, Ring *ring, Drawable
> *drawable)
> {
> DrawItem *item = &drawable->tree_item;
> @@ -736,54 +901,116 @@ static int current_add(DisplayChannel *display, Ring
> *ring, Drawable *drawable)
> region_init(&exclude_rgn);
> now = ring_next(ring, ring);
>
> + /* check whether the new drawable region intersects any of the items
> + * already in the 'current' ring */
> while (now) {
> TreeItem *sibling = SPICE_CONTAINEROF(now, TreeItem, siblings_link);
> int test_res;
>
> if (!region_bounds_intersects(&item->base.rgn, &sibling->rgn)) {
> + /* the bounds of the two items are totally disjoint, so no need to
> + * check further. check the next item */
> now = ring_next(ring, now);
> continue;
> }
> + /* bounds overlap, but check whether the regions actually overlap */
> test_res = region_test(&item->base.rgn, &sibling->rgn,
> REGION_TEST_ALL);
> if (!(test_res & REGION_TEST_SHARED)) {
> + /* there's no overlap of the regions between these two items. Move
> + * on to the next one. */
> now = ring_next(ring, now);
> continue;
> } else if (sibling->type != TREE_ITEM_TYPE_SHADOW) {
> + /* there is an overlap between the two regions */
> + /* NOTE: Shadow types represent a source region for a COPY_BITS
> + * operation, they don't represent a region that will be drawn.
> + * Therefore, we don't check for overlap between the new
> + * DrawItem and any shadow items */
> if (!(test_res & REGION_TEST_RIGHT_EXCLUSIVE) &&
> !(test_res &
> REGION_TEST_LEFT_EXCLUSIVE)
> &&
> current_add_equal(display,
> item, sibling)) {
> + /* the regions were equivalent, so we just replaced the other
> + * drawable with the new one */
> stat_add(&display->priv->add_stat, start_time);
> + /* Caller doesn't need to add the new drawable to the pipe,
> + * since current_add_equal already added it to the pipe */
> return FALSE;
> }
>
> if (!(test_res & REGION_TEST_RIGHT_EXCLUSIVE) && item->effect ==
> QXL_EFFECT_OPAQUE) {
> + /* the new drawable is opaque and entirely contains the sibling item */
> Shadow *shadow;
> int skip = now == exclude_base;
>
> if ((shadow = tree_item_find_shadow(sibling))) {
> + /* The sibling item was the destination of a COPY_BITS operation */
> if (exclude_base) {
> + /* During a previous iteration through this loop, an
> + * obscured sibling item was removed from the tree, and
> + * exclude_base was set to the item immediately after
> + * the removed item (see below). This time through the
> + * loop, we encountered another sibling that was
> + * completely obscured, so we call exclude_region()
> + * using the previously saved item as our starting
> + * point. @exlude_rgn will be the union of any previous
> + * 'on_hold' regions from the shadows of previous
> + * iterations
> + *
> + * XXX: why do we only only call exclude_region() for
> + * the previous item if the next item is obscured and
> + * has a shadow???
> + */
> TreeItem *next = sibling;
> + /* XXX: What is the intent of this call? */
I miss something in this function.
> exclude_region(display, ring, exclude_base,
> &exclude_rgn, &next, NULL);
> if (next != sibling) {
> + /* the @next param is only changed if the given item
> + * was removed as a side-effect of calling
> + * exclude_region(), so update our loop variable
> */
> now = next ? &next->siblings_link : NULL;
> exclude_base = NULL;
> continue;
> }
> }
> + /* Since the destination item (sibling) of the COPY_BITS
> + * operation is fully obscured, we no longer need the
> + * source item (shadow) anymore. shadow->on_hold represents
> + * a region that would normally have been excluded by a
> + * previous call to __exclude_region() (see documentation
> + * for that function), but was put on hold to make sure we
> + * kept the source region up to date. Now that we no longer
> + * need this source region, this "on hold" region can be
> + * safely excluded again. */
> region_or(&exclude_rgn, &shadow->on_hold);
> }
> now = now->prev;
> + /* remove the obscured sibling from the 'current' tree, which
> + * will also remove its shadow (if any) */
> current_remove(display, sibling);
> + /* advance the loop variable */
> now = ring_next(ring, now);
> if (shadow || skip) {
> + /* XXX: don't understand this exclude_base stuff
> + * 'now' is currently set to the the item immediately AFTER
> + * the obscured sibling that we just removed. Why is this
> + * item used as an 'exclude_base'??
> + */
> exclude_base = now;
> }
> continue;
> }
>
> if (!(test_res & REGION_TEST_LEFT_EXCLUSIVE) &&
> is_opaque_item(sibling)) {
> + /* the sibling item is opaque and entirely contains the new drawable */
> Container *container;
>
> + /* XXX: still don't understand this exclude_base stuff. The
> + * first time through, @exclude_base will be NULL, but
> + * subsequent loops may set it to something.
> + * In addition, @exclude_rgn starts out empty, but previous
> + * iterations of this loop may have added various
> + * Shadow::on_hold regions to it.
> + */
> if (exclude_base) {
> exclude_region(display, ring, exclude_base,
> &exclude_rgn, NULL, NULL);
> region_clear(&exclude_rgn);
> @@ -791,13 +1018,20 @@ static int current_add(DisplayChannel *display, Ring
> *ring, Drawable *drawable)
> }
> if (sibling->type == TREE_ITEM_TYPE_CONTAINER) {
> container = CONTAINER(sibling);
> + /* NOTE: here, ring is reset to the ring of the container's children */
> ring = &container->items;
> + /* if the sibling item is a container, place the new
> + * drawable into that container */
> item->base.container = container;
> + /* Start iterating over the container's children to see if
> + * any of them intersect this new drawable */
> now = ring_next(ring, ring);
> continue;
> }
> spice_assert(IS_DRAW_ITEM(sibling));
> if (!DRAW_ITEM(sibling)->container_root) {
> + /* Create a new container to hold the sibling and the new
> + * drawable */
> container = container_new(DRAW_ITEM(sibling));
> if (!container) {
> spice_warning("create new container failed");
> @@ -805,17 +1039,32 @@ static int current_add(DisplayChannel *display, Ring
> *ring, Drawable *drawable)
> return FALSE;
> }
> item->base.container = container;
> + /* reset 'ring' to the container's children ring, so that
> + * we can add the new drawable to this ring below */
> ring = &container->items;
> }
> }
> }
> + /* If we've gotten here, that means that:
> + * - the new item is not opaque
> + * - We just created a container to hold the new drawable and the
> + * sibling that encloses it
> + * - ??? */
> if (!exclude_base) {
> exclude_base = now;
> }
> break;
> }
> + /* we've removed any obscured siblings and figured out which ring the new
> + * drawable needs to be added to, so let's add it. */
> if (item->effect == QXL_EFFECT_OPAQUE) {
> + /* @exclude_rgn may contain the union of on_hold regions from any
> + * Shadows that were associated with DrawItems that were removed from
> + * the tree. Add the new item's region to that */
> region_or(&exclude_rgn, &item->base.rgn);
> + /* XXX: Why do we need to call exclude_region() here again? I thought
> + * we had just iterated the whole list above to check for region
> + * intersections between this new item and existing items... */
> exclude_region(display, ring, exclude_base, &exclude_rgn, NULL,
> drawable);
> stream_trace_update(display, drawable);
> streams_update_visible_region(display, drawable);
> @@ -1623,6 +1872,8 @@ static void surface_update_dest(RedSurface *surface,
> const SpiceRect *area)
> canvas->ops->read_bits(canvas, dest, -stride, area);
> }
>
> +/* Draws all drawables associated with @surface, starting from the tail of the
> + * ring, and stopping after it draws @last */
Tail of which ring? And how is this ring sorted? I suppose
we draw from elder to newer.
> static void draw_until(DisplayChannel *display, RedSurface *surface,
> Drawable *last)
> {
> RingItem *ring_item;
> @@ -1646,6 +1897,11 @@ static void draw_until(DisplayChannel *display,
> RedSurface *surface, Drawable *l
> } while (now != last);
> }
>
> +/* Find the first Drawable in the @current ring that intersects the given
> + * @area, starting at item @from (or the head of the ring if @from is NULL).
> + *
> + * NOTE: this function expects @current to be a ring of Drawables, and more
> + * specifically an instance of Surface::current_list (not Surface::current)
Definitively we need better names, too many current!
> */
> static Drawable* current_find_intersects_rect(Ring *current, RingItem *from,
> const SpiceRect *area)
> {
> diff --git a/server/display-channel.h b/server/display-channel.h
> index ba83e8c..d7184a7 100644
> --- a/server/display-channel.h
> +++ b/server/display-channel.h
> @@ -162,8 +162,14 @@ typedef struct DrawContext {
>
> typedef struct RedSurface {
> uint32_t refs;
> - Ring current; /* TreeItem */
> - Ring current_list; /* Drawable */
> + /* A Ring representing a hierarchical tree structure. This tree includes
> + * DrawItems, Containers, and Shadows. It is used to efficiently determine
> + * which drawables overlap, and to exclude regions of drawables that are
> + * obscured by other drawables */
> + Ring current;
> + /* A ring of pending Drawables associated with this surface. This ring is
> + * actually used for drawing */
Is this just containing drawabled, right? Not the tree?
Why this is used for drawing and not the tree?
Does if have a specific order?
I though that the drawing was done using pipe items.
> + Ring current_list;
> DrawContext context;
>
> Ring depend_on_me;
> diff --git a/server/tree.c b/server/tree.c
> index ea11000..cc3a96f 100644
> --- a/server/tree.c
> +++ b/server/tree.c
> @@ -185,6 +185,9 @@ void tree_item_dump(TreeItem *item)
> tree_foreach(item, dump_item, &di);
> }
>
> +/* Creates a new Shadow item for the given DrawItem, with an offset of @delta.
> + * A shadow represents a source region for a COPY_BITS operation, while the
> + * DrawItem represents the destination region for the operation */
> Shadow* shadow_new(DrawItem *item, const SpicePoint *delta)
> {
> spice_return_val_if_fail(item->shadow == NULL, NULL);
> @@ -205,6 +208,11 @@ Shadow* shadow_new(DrawItem *item, const SpicePoint
> *delta)
> return shadow;
> }
>
> +/* Create a new container to hold @item and insert @item into this container.
> + *
> + * NOTE: This function assumes that @item is already inside a different Ring,
> + * so it removes @item from that ring before inserting it into the new
> + * container */
> Container* container_new(DrawItem *item)
> {
> Container *container = spice_new(Container, 1);
> @@ -268,6 +276,8 @@ Shadow* tree_item_find_shadow(TreeItem *item)
> return DRAW_ITEM(item)->shadow;
> }
>
> +/* return the Ring containing siblings of item, falling back to @ring if @item
> + * does not have a container */
> Ring *tree_item_container_items(TreeItem *item, Ring *ring)
> {
> return (item->container) ? &item->container->items : ring;
> @@ -281,7 +291,7 @@ int tree_item_contained_by(TreeItem *item, Ring *ring)
> if (now == ring) {
> return TRUE;
> }
> - } while ((item = &item->container->base));
> + } while ((item = &item->container->base)); /* move up one level */
>
> return FALSE;
> }
Comments on this file seems ready to be pushed.
> diff --git a/server/tree.h b/server/tree.h
> index 35b78ab..e579dd0 100644
> --- a/server/tree.h
> +++ b/server/tree.h
> @@ -43,12 +43,18 @@ struct TreeItem {
> RingItem siblings_link;
> uint32_t type;
> Container *container;
> + /* rgn holds the region of the item. As additional items get added to the
> + * tree, this region may be modified to represent the portion of the item
> + * that is not obscured by other items */
so it's the visible region, not the original one.
> QRegion rgn;
> };
>
> /* A region "below" a copy, or the src region of the copy */
> struct Shadow {
> TreeItem base;
> + /* holds the union of all parts of this source region that have been
> + * obscured by a drawable item that has been subsequently added to the tree
> + */
> QRegion on_hold;
basically here, opposite to rgn we hold the not visible region...
I would ask why.
> };
>
Frediano
More information about the Spice-devel
mailing list