[Spice-devel] [PATCH 9/9] worker: painfully move display_channel_add_drawable

Jonathon Jongsma jjongsma at redhat.com
Fri Nov 13 08:40:02 PST 2015


On Fri, 2015-11-13 at 10:29 -0600, Jonathon Jongsma wrote:
> From: Marc-André Lureau <marcandre.lureau at gmail.com>
> 
> ---
>  server/display-channel.c | 526 ++++++++++++++++++++++++++++++++++++++++++++
>  server/display-channel.h |  15 +-
>  server/red_worker.c      | 559 ++--------------------------------------------
> -
>  3 files changed, 552 insertions(+), 548 deletions(-)
> 
> diff --git a/server/display-channel.c b/server/display-channel.c
> index 1d5d8d3..4f6cfc4 100644
> --- a/server/display-channel.c
> +++ b/server/display-channel.c
> @@ -20,6 +20,21 @@
>  
>  #include "display-channel.h"
>  
> +static stat_time_t display_channel_stat_now(DisplayChannel *display)
> +{
> +#ifdef RED_WORKER_STAT
> +    RedWorker *worker = COMMON_CHANNEL(display)->worker;
> +
> +    return stat_now(red_worker_get_clockid(worker));
> +
> +#else
> +    return 0;
> +#endif
> +}
> +
> +#define stat_start(display, var)                                        \
> +    G_GNUC_UNUSED stat_time_t var = display_channel_stat_now((display));
> +
>  void display_channel_compress_stats_reset(DisplayChannel *display)
>  {
>      spice_return_if_fail(display);
> @@ -406,3 +421,514 @@ bool display_channel_surface_has_canvas(DisplayChannel
> *display,
>  {
>      return display->surfaces[surface_id].context.canvas != NULL;
>  }
> +
> +static void streams_update_visible_region(DisplayChannel *display, Drawable
> *drawable)
> +{
> +    Ring *ring;
> +    RingItem *item;
> +    RingItem *dcc_ring_item, *next;
> +    DisplayChannelClient *dcc;
> +
> +    if (!red_channel_is_connected(RED_CHANNEL(display))) {
> +        return;
> +    }
> +
> +    if (!is_primary_surface(display, drawable->surface_id)) {
> +        return;
> +    }
> +
> +    ring = &display->streams;
> +    item = ring_get_head(ring);
> +
> +    while (item) {
> +        Stream *stream = SPICE_CONTAINEROF(item, Stream, link);
> +        StreamAgent *agent;
> +
> +        item = ring_next(ring, item);
> +
> +        if (stream->current == drawable) {
> +            continue;
> +        }
> +
> +        FOREACH_DCC(display, dcc_ring_item, next, dcc) {
> +            agent = &dcc->stream_agents[get_stream_id(display, stream)];
> +
> +            if (region_intersects(&agent->vis_region, &drawable
> ->tree_item.base.rgn)) {
> +                region_exclude(&agent->vis_region, &drawable
> ->tree_item.base.rgn);
> +                region_exclude(&agent->clip, &drawable->tree_item.base.rgn);
> +                dcc_add_stream_agent_clip(dcc, agent);
> +            }
> +        }
> +    }
> +}
> +
> +
> +static void current_add_drawable(DisplayChannel *display,
> +                                 Drawable *drawable, RingItem *pos)
> +{
> +    RedSurface *surface;
> +    uint32_t surface_id = drawable->surface_id;
> +
> +    surface = &display->surfaces[surface_id];
> +    ring_add_after(&drawable->tree_item.base.siblings_link, pos);
> +    ring_add(&display->current_list, &drawable->list_link);
> +    ring_add(&surface->current_list, &drawable->surface_list_link);
> +    display->current_size++;
> +    drawable->refs++;
> +}
> +
> +static int current_add_equal(DisplayChannel *display, DrawItem *item,
> TreeItem *other)
> +{
> +    DrawItem *other_draw_item;
> +    Drawable *drawable;
> +    Drawable *other_drawable;
> +
> +    if (other->type != TREE_ITEM_TYPE_DRAWABLE) {
> +        return FALSE;
> +    }
> +    other_draw_item = (DrawItem *)other;
> +
> +    if (item->shadow || other_draw_item->shadow || item->effect !=
> other_draw_item->effect) {
> +        return FALSE;
> +    }
> +
> +    drawable = SPICE_CONTAINEROF(item, Drawable, tree_item);
> +    other_drawable = SPICE_CONTAINEROF(other_draw_item, Drawable, tree_item);
> +
> +    if (item->effect == QXL_EFFECT_OPAQUE) {
> +        int add_after = !!other_drawable->stream &&
> +                        is_drawable_independent_from_surfaces(drawable);
> +        stream_maintenance(display, drawable, other_drawable);
> +        current_add_drawable(display, drawable, &other->siblings_link);
> +        other_drawable->refs++;
> +        current_remove_drawable(display, other_drawable);
> +        if (add_after) {
> +            red_pipes_add_drawable_after(display, drawable, other_drawable);
> +        } else {
> +            red_pipes_add_drawable(display, drawable);
> +        }
> +        red_pipes_remove_drawable(other_drawable);
> +        display_channel_drawable_unref(display, other_drawable);
> +        return TRUE;
> +    }
> +
> +    switch (item->effect) {
> +    case QXL_EFFECT_REVERT_ON_DUP:
> +        if (is_same_drawable(drawable, other_drawable)) {
> +
> +            DisplayChannelClient *dcc;
> +            DrawablePipeItem *dpi;
> +            RingItem *worker_ring_item, *dpi_ring_item;
> +
> +            other_drawable->refs++;
> +            current_remove_drawable(display, other_drawable);
> +
> +            /* sending the drawable to clients that already received
> +             * (or will receive) other_drawable */
> +            worker_ring_item = ring_get_head(&RED_CHANNEL(display)->clients);
> +            dpi_ring_item = ring_get_head(&other_drawable->pipes);
> +            /* dpi contains a sublist of dcc's, ordered the same */
> +            while (worker_ring_item) {
> +                dcc = SPICE_CONTAINEROF(worker_ring_item,
> DisplayChannelClient,
> +                                        common.base.channel_link);
> +                dpi = SPICE_CONTAINEROF(dpi_ring_item, DrawablePipeItem,
> base);
> +                while (worker_ring_item && (!dpi || dcc != dpi->dcc)) {
> +                    dcc_add_drawable(dcc, drawable);
> +                    worker_ring_item = ring_next(&RED_CHANNEL(display)
> ->clients,
> +                                                 worker_ring_item);
> +                    dcc = SPICE_CONTAINEROF(worker_ring_item,
> DisplayChannelClient,
> +                                            common.base.channel_link);
> +                }
> +
> +                if (dpi_ring_item) {
> +                    dpi_ring_item = ring_next(&other_drawable->pipes,
> dpi_ring_item);
> +                }
> +                if (worker_ring_item) {
> +                    worker_ring_item = ring_next(&RED_CHANNEL(display)
> ->clients,
> +                                                 worker_ring_item);
> +                }
> +            }
> +            /* not sending other_drawable where possible */
> +            red_pipes_remove_drawable(other_drawable);
> +
> +            display_channel_drawable_unref(display, other_drawable);
> +            return TRUE;
> +        }
> +        break;
> +    case QXL_EFFECT_OPAQUE_BRUSH:
> +        if (is_same_geometry(drawable, other_drawable)) {
> +            current_add_drawable(display, drawable, &other->siblings_link);
> +            red_pipes_remove_drawable(other_drawable);
> +            current_remove_drawable(display, other_drawable);
> +            red_pipes_add_drawable(display, drawable);
> +            return TRUE;
> +        }
> +        break;
> +    case QXL_EFFECT_NOP_ON_DUP:
> +        if (is_same_drawable(drawable, other_drawable)) {
> +            return TRUE;
> +        }
> +        break;
> +    }
> +    return FALSE;
> +}
> +
> +static void __exclude_region(DisplayChannel *display, Ring *ring, TreeItem
> *item, QRegion *rgn,
> +                             Ring **top_ring, Drawable *frame_candidate)
> +{
> +    QRegion and_rgn;
> +    stat_start(display, start_time);
> +
> +    region_clone(&and_rgn, rgn);
> +    region_and(&and_rgn, &item->rgn);
> +    if (!region_is_empty(&and_rgn)) {
> +        if (IS_DRAW_ITEM(item)) {
> +            DrawItem *draw = (DrawItem *)item;
> +
> +            if (draw->effect == QXL_EFFECT_OPAQUE) {
> +                region_exclude(rgn, &and_rgn);
> +            }
> +
> +            if (draw->shadow) {
> +                Shadow *shadow;
> +                int32_t x = item->rgn.extents.x1;
> +                int32_t y = item->rgn.extents.y1;
> +
> +                region_exclude(&draw->base.rgn, &and_rgn);
> +                shadow = draw->shadow;
> +                region_offset(&and_rgn, shadow->base.rgn.extents.x1 - x,
> +                              shadow->base.rgn.extents.y1 - y);
> +                region_exclude(&shadow->base.rgn, &and_rgn);
> +                region_and(&and_rgn, &shadow->on_hold);
> +                if (!region_is_empty(&and_rgn)) {
> +                    region_exclude(&shadow->on_hold, &and_rgn);
> +                    region_or(rgn, &and_rgn);
> +                    // in flat representation of current, shadow is always
> his owner next
> +                    if (!tree_item_contained_by((TreeItem*)shadow,
> *top_ring)) {
> +                        *top_ring =
> tree_item_container_items((TreeItem*)shadow, ring);
> +                    }
> +                }
> +            } else {
> +                if (frame_candidate) {
> +                    Drawable *drawable = SPICE_CONTAINEROF(draw, Drawable,
> tree_item);
> +                    stream_maintenance(display, frame_candidate, drawable);
> +                }
> +                region_exclude(&draw->base.rgn, &and_rgn);
> +            }
> +        } else if (item->type == TREE_ITEM_TYPE_CONTAINER) {
> +            region_exclude(&item->rgn, &and_rgn);
> +
> +            if (region_is_empty(&item->rgn)) {  //assume container removal
> will follow
> +                Shadow *shadow;
> +
> +                region_exclude(rgn, &and_rgn);
> +                if ((shadow = tree_item_find_shadow(item))) {
> +                    region_or(rgn, &shadow->on_hold);
> +                    if (!tree_item_contained_by((TreeItem*)shadow,
> *top_ring)) {
> +                        *top_ring =
> tree_item_container_items((TreeItem*)shadow, ring);
> +                    }
> +                }
> +            }
> +        } else {
> +            Shadow *shadow;
> +
> +            spice_assert(item->type == TREE_ITEM_TYPE_SHADOW);
> +            shadow = (Shadow *)item;
> +            region_exclude(rgn, &and_rgn);
> +            region_or(&shadow->on_hold, &and_rgn);
> +        }
> +    }
> +    region_destroy(&and_rgn);
> +    stat_add(&display->__exclude_stat, start_time);
> +}
> +
> +static void exclude_region(DisplayChannel *display, Ring *ring, RingItem
> *ring_item,
> +                           QRegion *rgn, TreeItem **last, Drawable
> *frame_candidate)
> +{
> +    Ring *top_ring;
> +    stat_start(display, start_time);
> +
> +    if (!ring_item) {
> +        return;
> +    }
> +
> +    top_ring = ring;
> +
> +    for (;;) {
> +        TreeItem *now = SPICE_CONTAINEROF(ring_item, TreeItem,
> siblings_link);
> +        Container *container = now->container;
> +
> +        spice_assert(!region_is_empty(&now->rgn));
> +
> +        if (region_intersects(rgn, &now->rgn)) {
> +            __exclude_region(display, ring, now, rgn, &top_ring,
> frame_candidate);
> +
> +            if (region_is_empty(&now->rgn)) {
> +                spice_assert(now->type != TREE_ITEM_TYPE_SHADOW);
> +                ring_item = now->siblings_link.prev;
> +                current_remove(display, now);
> +                if (last && *last == now) {
> +                    *last = (TreeItem *)ring_next(ring, ring_item);
> +                }
> +            } else if (now->type == TREE_ITEM_TYPE_CONTAINER) {
> +                Container *container = (Container *)now;
> +                if ((ring_item = ring_get_head(&container->items))) {
> +                    ring = &container->items;
> +                    spice_assert(((TreeItem *)ring_item)->container);
> +                    continue;
> +                }
> +                ring_item = &now->siblings_link;
> +            }
> +
> +            if (region_is_empty(rgn)) {
> +                stat_add(&display->exclude_stat, start_time);
> +                return;
> +            }
> +        }
> +
> +        while ((last && *last == (TreeItem *)ring_item) ||
> +               !(ring_item = ring_next(ring, ring_item))) {
> +            if (ring == top_ring) {
> +                stat_add(&display->exclude_stat, start_time);
> +                return;
> +            }
> +            ring_item = &container->base.siblings_link;
> +            container = container->base.container;
> +            ring = (container) ? &container->items : top_ring;
> +        }
> +    }
> +}
> +
> +static int current_add_with_shadow(DisplayChannel *display, Ring *ring,
> Drawable *item)
> +{
> +    stat_start(display, start_time);
> +    ++display->add_with_shadow_count;
> +
> +    RedDrawable *red_drawable = item->red_drawable;
> +    SpicePoint delta = {
> +        .x = red_drawable->u.copy_bits.src_pos.x - red_drawable->bbox.left,
> +        .y = red_drawable->u.copy_bits.src_pos.y - red_drawable->bbox.top
> +    };
> +
> +    Shadow *shadow = shadow_new(&item->tree_item, &delta);
> +    if (!shadow) {
> +        stat_add(&display->add_stat, start_time);
> +        return FALSE;
> +    }
> +    // item and his shadow must initially be placed in the same container.
> +    // for now putting them on root.
> +
> +    // only primary surface streams are supported
> +    if (is_primary_surface(display, item->surface_id)) {
> +        detach_streams_behind(display, &shadow->base.rgn, NULL);
> +    }
> +
> +    ring_add(ring, &shadow->base.siblings_link);
> +    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);
> +        exclude_region(display, ring, &shadow->base.siblings_link,
> &exclude_rgn, NULL, NULL);
> +        region_destroy(&exclude_rgn);
> +        streams_update_visible_region(display, item);
> +    } else {
> +        if (is_primary_surface(display, item->surface_id)) {
> +            detach_streams_behind(display, &item->tree_item.base.rgn, item);
> +        }
> +    }
> +    stat_add(&display->add_stat, start_time);
> +    return TRUE;
> +}
> +
> +static int current_add(DisplayChannel *display, Ring *ring, Drawable
> *drawable)
> +{
> +    DrawItem *item = &drawable->tree_item;
> +    RingItem *now;
> +    QRegion exclude_rgn;
> +    RingItem *exclude_base = NULL;
> +    stat_start(display, start_time);
> +
> +    spice_return_val_if_fail(!region_is_empty(&item->base.rgn), FALSE);
> +    region_init(&exclude_rgn);
> +    now = ring_next(ring, ring);
> +
> +    while (now) {
> +        TreeItem *sibling = SPICE_CONTAINEROF(now, TreeItem, siblings_link);
> +        int test_res;
> +
> +        if (!region_bounds_intersects(&item->base.rgn, &sibling->rgn)) {
> +            now = ring_next(ring, now);
> +            continue;
> +        }
> +        test_res = region_test(&item->base.rgn, &sibling->rgn,
> REGION_TEST_ALL);
> +        if (!(test_res & REGION_TEST_SHARED)) {
> +            now = ring_next(ring, now);
> +            continue;
> +        } else if (sibling->type != TREE_ITEM_TYPE_SHADOW) {
> +            if (!(test_res & REGION_TEST_RIGHT_EXCLUSIVE) &&
> +                                                   !(test_res &
> REGION_TEST_LEFT_EXCLUSIVE) &&
> +                                                   current_add_equal(display,
> item, sibling)) {
> +                stat_add(&display->add_stat, start_time);
> +                return FALSE;
> +            }
> +
> +            if (!(test_res & REGION_TEST_RIGHT_EXCLUSIVE) && item->effect ==
> QXL_EFFECT_OPAQUE) {
> +                Shadow *shadow;
> +                int skip = now == exclude_base;
> +
> +                if ((shadow = tree_item_find_shadow(sibling))) {
> +                    if (exclude_base) {
> +                        TreeItem *next = sibling;
> +                        exclude_region(display, ring, exclude_base,
> &exclude_rgn, &next, NULL);
> +                        if (next != sibling) {
> +                            now = next ? &next->siblings_link : NULL;
> +                            exclude_base = NULL;
> +                            continue;
> +                        }
> +                    }
> +                    region_or(&exclude_rgn, &shadow->on_hold);
> +                }
> +                now = now->prev;
> +                current_remove(display, sibling);
> +                now = ring_next(ring, now);
> +                if (shadow || skip) {
> +                    exclude_base = now;
> +                }
> +                continue;
> +            }
> +
> +            if (!(test_res & REGION_TEST_LEFT_EXCLUSIVE) &&
> is_opaque_item(sibling)) {
> +                Container *container;
> +
> +                if (exclude_base) {
> +                    exclude_region(display, ring, exclude_base, &exclude_rgn,
> NULL, NULL);
> +                    region_clear(&exclude_rgn);
> +                    exclude_base = NULL;
> +                }
> +                if (sibling->type == TREE_ITEM_TYPE_CONTAINER) {
> +                    container = (Container *)sibling;
> +                    ring = &container->items;
> +                    item->base.container = container;
> +                    now = ring_next(ring, ring);
> +                    continue;
> +                }
> +                spice_assert(IS_DRAW_ITEM(sibling));
> +                if (!DRAW_ITEM(sibling)->container_root) {
> +                    container = container_new(DRAW_ITEM(sibling));
> +                    if (!container) {
> +                        spice_warning("create new container failed");
> +                        region_destroy(&exclude_rgn);
> +                        return FALSE;
> +                    }
> +                    item->base.container = container;
> +                    ring = &container->items;
> +                }
> +            }
> +        }
> +        if (!exclude_base) {
> +            exclude_base = now;
> +        }
> +        break;
> +    }
> +    if (item->effect == QXL_EFFECT_OPAQUE) {
> +        region_or(&exclude_rgn, &item->base.rgn);
> +        exclude_region(display, ring, exclude_base, &exclude_rgn, NULL,
> drawable);
> +        stream_trace_update(display, drawable);
> +        streams_update_visible_region(display, drawable);
> +        /*
> +         * Performing the insertion after exclude_region for
> +         * safety (todo: Not sure if exclude_region can affect the drawable
> +         * if it is added to the tree before calling exclude_region).
> +         */
> +        current_add_drawable(display, drawable, ring);
> +    } else {
> +        /*
> +         * red_detach_streams_behind can affect the current tree since it may
> +         * trigger calls to update_area. Thus, the drawable should be added
> to the tree
> +         * before calling red_detach_streams_behind
> +         */
> +        current_add_drawable(display, drawable, ring);
> +        if (is_primary_surface(display, drawable->surface_id)) {
> +            detach_streams_behind(display, &drawable->tree_item.base.rgn,
> drawable);
> +        }
> +    }
> +    region_destroy(&exclude_rgn);
> +    stat_add(&display->add_stat, start_time);
> +    return TRUE;
> +}
> +
> +static bool drawable_can_stream(DisplayChannel *display, Drawable *drawable)
> +{
> +    RedDrawable *red_drawable = drawable->red_drawable;
> +    SpiceImage *image;
> +
> +    if (display->stream_video == SPICE_STREAM_VIDEO_OFF)
> +        return FALSE;

on this test and the following ones, the braces were removed compared to the
previous version. Not a big deal, but in general our coding style is to always
use braces.


> +
> +    if (!is_primary_surface(display, drawable->surface_id))
> +        return FALSE;
> +
> +    if (drawable->tree_item.effect != QXL_EFFECT_OPAQUE ||
> +        red_drawable->type != QXL_DRAW_COPY ||
> +        red_drawable->u.copy.rop_descriptor != SPICE_ROPD_OP_PUT)
> +        return FALSE;
> +
> +    image = red_drawable->u.copy.src_bitmap;
> +    if (image == NULL ||
> +        image->descriptor.type != SPICE_IMAGE_TYPE_BITMAP)
> +        return FALSE;
> +
> +    if (display->stream_video == SPICE_STREAM_VIDEO_FILTER) {
> +        SpiceRect* rect;
> +        int size;
> +
> +        rect = &drawable->red_drawable->u.copy.src_area;
> +        size = (rect->right - rect->left) * (rect->bottom - rect->top);
> +        if (size < RED_STREAM_MIN_SIZE)
> +            return FALSE;
> +    }
> +
> +    return TRUE;
> +}
> +
> +void display_channel_print_stats(DisplayChannel *display)
> +{
> +#ifdef RED_WORKER_STAT
> +    stat_time_t total = display->add_stat.total;
> +    spice_info("add with shadow count %u",
> +               display->add_with_shadow_count);
> +    display->add_with_shadow_count = 0;
> +    spice_info("add[%u] %f exclude[%u] %f __exclude[%u] %f",
> +               display->add_stat.count,
> +               stat_cpu_time_to_sec(total),
> +               display->exclude_stat.count,
> +               stat_cpu_time_to_sec(display->exclude_stat.total),
> +               display->__exclude_stat.count,
> +               stat_cpu_time_to_sec(display->__exclude_stat.total));
> +    spice_info("add %f%% exclude %f%% exclude2 %f%% __exclude %f%%",
> +               (double)(total - display->exclude_stat.total) / total * 100,
> +               (double)(display->exclude_stat.total) / total * 100,
> +               (double)(display->exclude_stat.total -
> +                        display->__exclude_stat.total) / display
> ->exclude_stat.total * 100,
> +               (double)(display->__exclude_stat.total) / display
> ->exclude_stat.total * 100);
> +    stat_reset(&display->add_stat);
> +    stat_reset(&display->exclude_stat);
> +    stat_reset(&display->__exclude_stat);
> +#endif
> +}

As far as I can tell, this function is no longer used. See below.


> +
> +int display_channel_add_drawable(DisplayChannel *display, Drawable *drawable)
> +{
> +    int ret = FALSE, surface_id = drawable->surface_id;
> +    RedDrawable *red_drawable = drawable->red_drawable;
> +    Ring *ring = &display->surfaces[surface_id].current;
> +
> +    if (has_shadow(red_drawable)) {
> +        ret = current_add_with_shadow(display, ring, drawable);
> +    } else {
> +        drawable->streamable = drawable_can_stream(display, drawable);
> +        ret = current_add(display, ring, drawable);
> +    }
> +
> +    return ret;
> +}

compared with the previous version (red_add_drawable()), this function removes
the call to print_stats() (which was renamed to display_channel_print_stats()).
I think this was the only location where this function was called. I don't know
whether this debug information was useful or not, since I've never used it. But
if we're not going to ever print out the stats, we should probably just get rid
of the funciton.


> diff --git a/server/display-channel.h b/server/display-channel.h
> index ce9ce50..432cc61 100644
> --- a/server/display-channel.h
> +++ b/server/display-channel.h
> @@ -356,12 +356,12 @@ struct DisplayChannel {
>      RedCompressBuf *free_compress_bufs;
>  
>  /* TODO: some day unify this, make it more runtime.. */
> +    uint32_t add_count;

As far as I can tell, this is no longer used anywhere due to the removal of the
print_stats() functionality from add_drawable() above


> +    uint32_t add_with_shadow_count;

...and this one is incremented in current_add_with_shadow(), but it is only read
in display_channel_print_stats() (which is no longer called from anywhere).
So it seems these two values could perhaps be removed. So I don't understand why
they were moved out from inside the #ifdef...


>  #ifdef RED_WORKER_STAT
>      stat_info_t add_stat;
>      stat_info_t exclude_stat;
>      stat_info_t __exclude_stat;
> -    uint32_t add_count;
> -    uint32_t add_with_shadow_count;
>  #endif
>  #ifdef RED_STATISTICS
>      uint64_t *cache_hits_counter;
> @@ -415,6 +415,8 @@ void                       display_channel_surface_unref  
>            (DisplayCha
>  bool                       display_channel_surface_has_canvas       
>  (DisplayChannel *display,
>                                                                       
>  uint32_t surface_id);
>  void                       display_channel_show_tree                
>  (DisplayChannel *display);
> +int                        display_channel_add_drawable             
>  (DisplayChannel *display,
> +                                                                     
>  Drawable *drawable);
>  
>  static inline int is_equal_path(SpicePath *path1, SpicePath *path2)
>  {
> @@ -531,4 +533,13 @@ static inline void region_add_clip_rects(QRegion *rgn,
> SpiceClipRects *data)
>      }
>  }
>  
> +void red_pipes_add_drawable(DisplayChannel *display, Drawable *drawable);
> +void current_remove_drawable(DisplayChannel *display, Drawable *item);
> +void red_pipes_add_drawable_after(DisplayChannel *display,
> +                                  Drawable *drawable, Drawable *pos_after);
> +void red_pipes_remove_drawable(Drawable *drawable);
> +void dcc_add_drawable(DisplayChannelClient *dcc, Drawable *drawable);
> +void current_remove(DisplayChannel *display, TreeItem *item);
> +void detach_streams_behind(DisplayChannel *display, QRegion *region, Drawable
> *drawable);
> +
>  #endif /* DISPLAY_CHANNEL_H_ */
> diff --git a/server/red_worker.c b/server/red_worker.c
> index fcb5128..8aa7e70 100644
> --- a/server/red_worker.c
> +++ b/server/red_worker.c
> @@ -492,7 +492,7 @@ static int cursor_is_connected(RedWorker *worker)
>          red_channel_is_connected(RED_CHANNEL(worker->cursor_channel));
>  }
>  
> -static void dcc_add_drawable(DisplayChannelClient *dcc, Drawable *drawable)
> +void dcc_add_drawable(DisplayChannelClient *dcc, Drawable *drawable)
>  {
>      DrawablePipeItem *dpi;
>  
> @@ -501,7 +501,7 @@ static void dcc_add_drawable(DisplayChannelClient *dcc,
> Drawable *drawable)
>      red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), &dpi
> ->dpi_pipe_item);
>  }
>  
> -static void red_pipes_add_drawable(DisplayChannel *display, Drawable
> *drawable)
> +void red_pipes_add_drawable(DisplayChannel *display, Drawable *drawable)
>  {
>      DisplayChannelClient *dcc;
>      RingItem *dcc_ring_item, *next;
> @@ -524,8 +524,8 @@ static void dcc_add_drawable_to_tail(DisplayChannelClient
> *dcc, Drawable *drawab
>      red_channel_client_pipe_add_tail(RED_CHANNEL_CLIENT(dcc), &dpi
> ->dpi_pipe_item);
>  }
>  
> -static inline void red_pipes_add_drawable_after(DisplayChannel *display,
> -                                                Drawable *drawable, Drawable
> *pos_after)
> +void red_pipes_add_drawable_after(DisplayChannel *display,
> +                                  Drawable *drawable, Drawable *pos_after)
>  {
>      DrawablePipeItem *dpi, *dpi_pos_after;
>      RingItem *dpi_link, *dpi_next;
> @@ -571,7 +571,7 @@ static inline PipeItem
> *red_pipe_get_tail(DisplayChannelClient *dcc)
>      return (PipeItem*)ring_get_tail(&RED_CHANNEL_CLIENT(dcc)->pipe);
>  }
>  
> -static inline void red_pipes_remove_drawable(Drawable *drawable)
> +void red_pipes_remove_drawable(Drawable *drawable)
>  {
>      DrawablePipeItem *dpi;
>      RingItem *item, *next;
> @@ -816,8 +816,9 @@ static void red_flush_source_surfaces(DisplayChannel
> *display, Drawable *drawabl
>      }
>  }
>  
> -static inline void current_remove_drawable(DisplayChannel *display, Drawable
> *item)
> +void current_remove_drawable(DisplayChannel *display, Drawable *item)
>  {
> +    /* todo: move all to unref? */
>      display_stream_trace_add_drawable(display, item);
>      draw_item_remove_shadow(&item->tree_item);
>      ring_remove(&item->tree_item.base.siblings_link);
> @@ -827,13 +828,7 @@ static inline void current_remove_drawable(DisplayChannel
> *display, Drawable *it
>      display->current_size--;
>  }
>  
> -static void remove_drawable(DisplayChannel *display, Drawable *drawable)
> -{
> -    red_pipes_remove_drawable(drawable);
> -    current_remove_drawable(display, drawable);
> -}
> -
> -static inline void current_remove(DisplayChannel *display, TreeItem *item)
> +void current_remove(DisplayChannel *display, TreeItem *item)
>  {
>      TreeItem *now = item;
>  
> @@ -843,8 +838,10 @@ static inline void current_remove(DisplayChannel
> *display, TreeItem *item)
>          RingItem *ring_item;
>  
>          if (now->type == TREE_ITEM_TYPE_DRAWABLE) {
> +            Drawable *drawable = SPICE_CONTAINEROF(now, Drawable, tree_item);
>              ring_item = now->siblings_link.prev;
> -            remove_drawable(display, SPICE_CONTAINEROF(now, Drawable,
> tree_item));
> +            red_pipes_remove_drawable(drawable);
> +            current_remove_drawable(display, drawable);
>          } else {
>              Container *container = (Container *)now;
>  
> @@ -975,152 +972,6 @@ static void
> red_clear_surface_drawables_from_pipes(DisplayChannel *display,
>      }
>  }
>  
> -static void __exclude_region(DisplayChannel *display, Ring *ring, TreeItem
> *item, QRegion *rgn,
> -                             Ring **top_ring, Drawable *frame_candidate)
> -{
> -    QRegion and_rgn;
> -#ifdef RED_WORKER_STAT
> -    RedWorker *worker = COMMON_CHANNEL(display)->worker;
> -    stat_time_t start_time = stat_now(worker->clockid);
> -#endif
> -
> -    region_clone(&and_rgn, rgn);
> -    region_and(&and_rgn, &item->rgn);
> -    if (!region_is_empty(&and_rgn)) {
> -        if (IS_DRAW_ITEM(item)) {
> -            DrawItem *draw = (DrawItem *)item;
> -
> -            if (draw->effect == QXL_EFFECT_OPAQUE) {
> -                region_exclude(rgn, &and_rgn);
> -            }
> -
> -            if (draw->shadow) {
> -                Shadow *shadow;
> -                int32_t x = item->rgn.extents.x1;
> -                int32_t y = item->rgn.extents.y1;
> -
> -                region_exclude(&draw->base.rgn, &and_rgn);
> -                shadow = draw->shadow;
> -                region_offset(&and_rgn, shadow->base.rgn.extents.x1 - x,
> -                              shadow->base.rgn.extents.y1 - y);
> -                region_exclude(&shadow->base.rgn, &and_rgn);
> -                region_and(&and_rgn, &shadow->on_hold);
> -                if (!region_is_empty(&and_rgn)) {
> -                    region_exclude(&shadow->on_hold, &and_rgn);
> -                    region_or(rgn, &and_rgn);
> -                    // in flat representation of current, shadow is always
> his owner next
> -                    if (!tree_item_contained_by((TreeItem*)shadow,
> *top_ring)) {
> -                        *top_ring =
> tree_item_container_items((TreeItem*)shadow, ring);
> -                    }
> -                }
> -            } else {
> -                if (frame_candidate) {
> -                    Drawable *drawable = SPICE_CONTAINEROF(draw, Drawable,
> tree_item);
> -                    stream_maintenance(display, frame_candidate, drawable);
> -                }
> -                region_exclude(&draw->base.rgn, &and_rgn);
> -            }
> -        } else if (item->type == TREE_ITEM_TYPE_CONTAINER) {
> -            region_exclude(&item->rgn, &and_rgn);
> -
> -            if (region_is_empty(&item->rgn)) {  //assume container removal
> will follow
> -                Shadow *shadow;
> -
> -                region_exclude(rgn, &and_rgn);
> -                if ((shadow = tree_item_find_shadow(item))) {
> -                    region_or(rgn, &shadow->on_hold);
> -                    if (!tree_item_contained_by((TreeItem*)shadow,
> *top_ring)) {
> -                        *top_ring =
> tree_item_container_items((TreeItem*)shadow, ring);
> -                    }
> -                }
> -            }
> -        } else {
> -            Shadow *shadow;
> -
> -            spice_assert(item->type == TREE_ITEM_TYPE_SHADOW);
> -            shadow = (Shadow *)item;
> -            region_exclude(rgn, &and_rgn);
> -            region_or(&shadow->on_hold, &and_rgn);
> -        }
> -    }
> -    region_destroy(&and_rgn);
> -    stat_add(&display->__exclude_stat, start_time);
> -}
> -
> -static void exclude_region(DisplayChannel *display, Ring *ring, RingItem
> *ring_item,
> -                           QRegion *rgn, TreeItem **last, Drawable
> *frame_candidate)
> -{
> -#ifdef RED_WORKER_STAT
> -    RedWorker *worker = COMMON_CHANNEL(display)->worker;
> -    stat_time_t start_time = stat_now(worker->clockid);
> -#endif
> -    Ring *top_ring;
> -
> -    if (!ring_item) {
> -        return;
> -    }
> -
> -    top_ring = ring;
> -
> -    for (;;) {
> -        TreeItem *now = SPICE_CONTAINEROF(ring_item, TreeItem,
> siblings_link);
> -        Container *container = now->container;
> -
> -        spice_assert(!region_is_empty(&now->rgn));
> -
> -        if (region_intersects(rgn, &now->rgn)) {
> -            __exclude_region(display, ring, now, rgn, &top_ring,
> frame_candidate);
> -
> -            if (region_is_empty(&now->rgn)) {
> -                spice_assert(now->type != TREE_ITEM_TYPE_SHADOW);
> -                ring_item = now->siblings_link.prev;
> -                current_remove(display, now);
> -                if (last && *last == now) {
> -                    *last = (TreeItem *)ring_next(ring, ring_item);
> -                }
> -            } else if (now->type == TREE_ITEM_TYPE_CONTAINER) {
> -                Container *container = (Container *)now;
> -                if ((ring_item = ring_get_head(&container->items))) {
> -                    ring = &container->items;
> -                    spice_assert(((TreeItem *)ring_item)->container);
> -                    continue;
> -                }
> -                ring_item = &now->siblings_link;
> -            }
> -
> -            if (region_is_empty(rgn)) {
> -                stat_add(&display->exclude_stat, start_time);
> -                return;
> -            }
> -        }
> -
> -        while ((last && *last == (TreeItem *)ring_item) ||
> -               !(ring_item = ring_next(ring, ring_item))) {
> -            if (ring == top_ring) {
> -                stat_add(&display->exclude_stat, start_time);
> -                return;
> -            }
> -            ring_item = &container->base.siblings_link;
> -            container = container->base.container;
> -            ring = (container) ? &container->items : top_ring;
> -        }
> -    }
> -}
> -
> -static void current_add_drawable(DisplayChannel *display,
> -                                 Drawable *drawable, RingItem *pos)
> -{
> -    RedSurface *surface;
> -    uint32_t surface_id = drawable->surface_id;
> -
> -    surface = &display->surfaces[surface_id];
> -    ring_add_after(&drawable->tree_item.base.siblings_link, pos);
> -    ring_add(&display->current_list, &drawable->list_link);
> -    ring_add(&surface->current_list, &drawable->surface_list_link);
> -    display->current_size++;
> -    drawable->refs++;
> -}
> -
>  void detach_stream(DisplayChannel *display, Stream *stream,
>                     int detach_sized)
>  {
> @@ -1243,7 +1094,7 @@ static void detach_stream_gracefully(DisplayChannel
> *display, Stream *stream,
>   *           involves sending an upgrade image to the client, this drawable
> won't be rendered
>   *           (see dcc_detach_stream_gracefully).
>   */
> -static void detach_streams_behind(DisplayChannel *display, QRegion *region,
> Drawable *drawable)
> +void detach_streams_behind(DisplayChannel *display, QRegion *region, Drawable
> *drawable)
>  {
>      Ring *ring = &display->streams;
>      RingItem *item = ring_get_head(ring);
> @@ -1276,46 +1127,6 @@ static void detach_streams_behind(DisplayChannel
> *display, QRegion *region, Draw
>      }
>  }
>  
> -static void streams_update_visible_region(DisplayChannel *display, Drawable
> *drawable)
> -{
> -    Ring *ring;
> -    RingItem *item;
> -    RingItem *dcc_ring_item, *next;
> -    DisplayChannelClient *dcc;
> -
> -    if (!red_channel_is_connected(RED_CHANNEL(display))) {
> -        return;
> -    }
> -
> -    if (!is_primary_surface(display, drawable->surface_id)) {
> -        return;
> -    }
> -
> -    ring = &display->streams;
> -    item = ring_get_head(ring);
> -
> -    while (item) {
> -        Stream *stream = SPICE_CONTAINEROF(item, Stream, link);
> -        StreamAgent *agent;
> -
> -        item = ring_next(ring, item);
> -
> -        if (stream->current == drawable) {
> -            continue;
> -        }
> -
> -        FOREACH_DCC(display, dcc_ring_item, next, dcc) {
> -            agent = &dcc->stream_agents[get_stream_id(display, stream)];
> -
> -            if (region_intersects(&agent->vis_region, &drawable
> ->tree_item.base.rgn)) {
> -                region_exclude(&agent->vis_region, &drawable
> ->tree_item.base.rgn);
> -                region_exclude(&agent->clip, &drawable->tree_item.base.rgn);
> -                dcc_add_stream_agent_clip(dcc, agent);
> -            }
> -        }
> -    }
> -}
> -
>  static void display_channel_streams_timeout(DisplayChannel *display)
>  {
>      Ring *ring = &display->streams;
> @@ -1552,350 +1363,6 @@ static void
> dcc_destroy_stream_agents(DisplayChannelClient *dcc)
>      }
>  }
>  
> -static inline int red_current_add_equal(DisplayChannel *display, DrawItem
> *item, TreeItem *other)
> -{
> -    DrawItem *other_draw_item;
> -    Drawable *drawable;
> -    Drawable *other_drawable;
> -
> -    if (other->type != TREE_ITEM_TYPE_DRAWABLE) {
> -        return FALSE;
> -    }
> -    other_draw_item = (DrawItem *)other;
> -
> -    if (item->shadow || other_draw_item->shadow || item->effect !=
> other_draw_item->effect) {
> -        return FALSE;
> -    }
> -
> -    drawable = SPICE_CONTAINEROF(item, Drawable, tree_item);
> -    other_drawable = SPICE_CONTAINEROF(other_draw_item, Drawable, tree_item);
> -
> -    if (item->effect == QXL_EFFECT_OPAQUE) {
> -        int add_after = !!other_drawable->stream &&
> -                        is_drawable_independent_from_surfaces(drawable);
> -        stream_maintenance(display, drawable, other_drawable);
> -        current_add_drawable(display, drawable, &other->siblings_link);
> -        other_drawable->refs++;
> -        current_remove_drawable(display, other_drawable);
> -        if (add_after) {
> -            red_pipes_add_drawable_after(display, drawable, other_drawable);
> -        } else {
> -            red_pipes_add_drawable(display, drawable);
> -        }
> -        red_pipes_remove_drawable(other_drawable);
> -        display_channel_drawable_unref(display, other_drawable);
> -        return TRUE;
> -    }
> -
> -    switch (item->effect) {
> -    case QXL_EFFECT_REVERT_ON_DUP:
> -        if (is_same_drawable(drawable, other_drawable)) {
> -
> -            DisplayChannelClient *dcc;
> -            DrawablePipeItem *dpi;
> -            RingItem *worker_ring_item, *dpi_ring_item;
> -
> -            other_drawable->refs++;
> -            current_remove_drawable(display, other_drawable);
> -
> -            /* sending the drawable to clients that already received
> -             * (or will receive) other_drawable */
> -            worker_ring_item = ring_get_head(&RED_CHANNEL(display)->clients);
> -            dpi_ring_item = ring_get_head(&other_drawable->pipes);
> -            /* dpi contains a sublist of dcc's, ordered the same */
> -            while (worker_ring_item) {
> -                dcc = SPICE_CONTAINEROF(worker_ring_item,
> DisplayChannelClient,
> -                                        common.base.channel_link);
> -                dpi = SPICE_CONTAINEROF(dpi_ring_item, DrawablePipeItem,
> base);
> -                while (worker_ring_item && (!dpi || dcc != dpi->dcc)) {
> -                    dcc_add_drawable(dcc, drawable);
> -                    worker_ring_item = ring_next(&RED_CHANNEL(display)
> ->clients,
> -                                                 worker_ring_item);
> -                    dcc = SPICE_CONTAINEROF(worker_ring_item,
> DisplayChannelClient,
> -                                            common.base.channel_link);
> -                }
> -
> -                if (dpi_ring_item) {
> -                    dpi_ring_item = ring_next(&other_drawable->pipes,
> dpi_ring_item);
> -                }
> -                if (worker_ring_item) {
> -                    worker_ring_item = ring_next(&RED_CHANNEL(display)
> ->clients,
> -                                                 worker_ring_item);
> -                }
> -            }
> -            /* not sending other_drawable where possible */
> -            red_pipes_remove_drawable(other_drawable);
> -
> -            display_channel_drawable_unref(display, other_drawable);
> -            return TRUE;
> -        }
> -        break;
> -    case QXL_EFFECT_OPAQUE_BRUSH:
> -        if (is_same_geometry(drawable, other_drawable)) {
> -            current_add_drawable(display, drawable, &other->siblings_link);
> -            remove_drawable(display, other_drawable);
> -            red_pipes_add_drawable(display, drawable);
> -            return TRUE;
> -        }
> -        break;
> -    case QXL_EFFECT_NOP_ON_DUP:
> -        if (is_same_drawable(drawable, other_drawable)) {
> -            return TRUE;
> -        }
> -        break;
> -    }
> -    return FALSE;
> -}
> -
> -static int current_add(DisplayChannel *display, Ring *ring, Drawable
> *drawable)
> -{
> -    DrawItem *item = &drawable->tree_item;
> -#ifdef RED_WORKER_STAT
> -    RedWorker *worker = COMMON_CHANNEL(display)->worker;
> -    stat_time_t start_time = stat_now(worker->clockid);
> -#endif
> -    RingItem *now;
> -    QRegion exclude_rgn;
> -    RingItem *exclude_base = NULL;
> -
> -    spice_return_val_if_fail(!region_is_empty(&item->base.rgn), FALSE);
> -    region_init(&exclude_rgn);
> -    now = ring_next(ring, ring);
> -
> -    while (now) {
> -        TreeItem *sibling = SPICE_CONTAINEROF(now, TreeItem, siblings_link);
> -        int test_res;
> -
> -        if (!region_bounds_intersects(&item->base.rgn, &sibling->rgn)) {
> -            now = ring_next(ring, now);
> -            continue;
> -        }
> -        test_res = region_test(&item->base.rgn, &sibling->rgn,
> REGION_TEST_ALL);
> -        if (!(test_res & REGION_TEST_SHARED)) {
> -            now = ring_next(ring, now);
> -            continue;
> -        } else if (sibling->type != TREE_ITEM_TYPE_SHADOW) {
> -            if (!(test_res & REGION_TEST_RIGHT_EXCLUSIVE) &&
> -                                                   !(test_res &
> REGION_TEST_LEFT_EXCLUSIVE) &&
> -                                                  
>  red_current_add_equal(display, item, sibling)) {
> -                stat_add(&display->add_stat, start_time);
> -                return FALSE;
> -            }
> -
> -            if (!(test_res & REGION_TEST_RIGHT_EXCLUSIVE) && item->effect ==
> QXL_EFFECT_OPAQUE) {
> -                Shadow *shadow;
> -                int skip = now == exclude_base;
> -
> -                if ((shadow = tree_item_find_shadow(sibling))) {
> -                    if (exclude_base) {
> -                        TreeItem *next = sibling;
> -                        exclude_region(display, ring, exclude_base,
> &exclude_rgn, &next, NULL);
> -                        if (next != sibling) {
> -                            now = next ? &next->siblings_link : NULL;
> -                            exclude_base = NULL;
> -                            continue;
> -                        }
> -                    }
> -                    region_or(&exclude_rgn, &shadow->on_hold);
> -                }
> -                now = now->prev;
> -                current_remove(display, sibling);
> -                now = ring_next(ring, now);
> -                if (shadow || skip) {
> -                    exclude_base = now;
> -                }
> -                continue;
> -            }
> -
> -            if (!(test_res & REGION_TEST_LEFT_EXCLUSIVE) &&
> is_opaque_item(sibling)) {
> -                Container *container;
> -
> -                if (exclude_base) {
> -                    exclude_region(display, ring, exclude_base, &exclude_rgn,
> NULL, NULL);
> -                    region_clear(&exclude_rgn);
> -                    exclude_base = NULL;
> -                }
> -                if (sibling->type == TREE_ITEM_TYPE_CONTAINER) {
> -                    container = (Container *)sibling;
> -                    ring = &container->items;
> -                    item->base.container = container;
> -                    now = ring_next(ring, ring);
> -                    continue;
> -                }
> -                spice_assert(IS_DRAW_ITEM(sibling));
> -                if (!DRAW_ITEM(sibling)->container_root) {
> -                    container = container_new(DRAW_ITEM(sibling));
> -                    if (!container) {
> -                        spice_warning("create new container failed");
> -                        region_destroy(&exclude_rgn);
> -                        return FALSE;
> -                    }
> -                    item->base.container = container;
> -                    ring = &container->items;
> -                }
> -            }
> -        }
> -        if (!exclude_base) {
> -            exclude_base = now;
> -        }
> -        break;
> -    }
> -    if (item->effect == QXL_EFFECT_OPAQUE) {
> -        region_or(&exclude_rgn, &item->base.rgn);
> -        exclude_region(display, ring, exclude_base, &exclude_rgn, NULL,
> drawable);
> -        stream_trace_update(display, drawable);
> -        streams_update_visible_region(display, drawable);
> -        /*
> -         * Performing the insertion after exclude_region for
> -         * safety (todo: Not sure if exclude_region can affect the drawable
> -         * if it is added to the tree before calling exclude_region).
> -         */
> -        current_add_drawable(display, drawable, ring);
> -    } else {
> -        /*
> -         * red_detach_streams_behind can affect the current tree since it may
> -         * trigger calls to update_area. Thus, the drawable should be added
> to the tree
> -         * before calling red_detach_streams_behind
> -         */
> -        current_add_drawable(display, drawable, ring);
> -        if (is_primary_surface(display, drawable->surface_id)) {
> -            detach_streams_behind(display, &drawable->tree_item.base.rgn,
> drawable);
> -        }
> -    }
> -    region_destroy(&exclude_rgn);
> -    stat_add(&display->add_stat, start_time);
> -    return TRUE;
> -}
> -
> -static int current_add_with_shadow(DisplayChannel *display, Ring *ring,
> Drawable *item)
> -{
> -#ifdef RED_WORKER_STAT
> -    RedWorker *worker = COMMON_CHANNEL(display)->worker;
> -    stat_time_t start_time = stat_now(worker->clockid);
> -    ++display->add_with_shadow_count;
> -#endif
> -
> -    RedDrawable *red_drawable = item->red_drawable;
> -    SpicePoint delta = {
> -        .x = red_drawable->u.copy_bits.src_pos.x - red_drawable->bbox.left,
> -        .y = red_drawable->u.copy_bits.src_pos.y - red_drawable->bbox.top
> -    };
> -
> -    Shadow *shadow = shadow_new(&item->tree_item, &delta);
> -    if (!shadow) {
> -        stat_add(&display->add_stat, start_time);
> -        return FALSE;
> -    }
> -    // item and his shadow must initially be placed in the same container.
> -    // for now putting them on root.
> -
> -    // only primary surface streams are supported
> -    if (is_primary_surface(display, item->surface_id)) {
> -        detach_streams_behind(display, &shadow->base.rgn, NULL);
> -    }
> -
> -    ring_add(ring, &shadow->base.siblings_link);
> -    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);
> -        exclude_region(display, ring, &shadow->base.siblings_link,
> &exclude_rgn, NULL, NULL);
> -        region_destroy(&exclude_rgn);
> -        streams_update_visible_region(display, item);
> -    } else {
> -        if (is_primary_surface(display, item->surface_id)) {
> -            detach_streams_behind(display, &item->tree_item.base.rgn, item);
> -        }
> -    }
> -    stat_add(&display->add_stat, start_time);
> -    return TRUE;
> -}
> -
> -static void drawable_update_streamable(DisplayChannel *display, Drawable
> *drawable)
> -{
> -    RedDrawable *red_drawable = drawable->red_drawable;
> -    SpiceImage *image;
> -
> -    if (display->stream_video == SPICE_STREAM_VIDEO_OFF) {
> -        return;
> -    }
> -
> -    if (!is_primary_surface(display, drawable->surface_id)) {
> -        return;
> -    }
> -
> -    if (drawable->tree_item.effect != QXL_EFFECT_OPAQUE ||
> -        red_drawable->type != QXL_DRAW_COPY ||
> -        red_drawable->u.copy.rop_descriptor != SPICE_ROPD_OP_PUT) {
> -        return;
> -    }
> -
> -    image = red_drawable->u.copy.src_bitmap;
> -    if (image == NULL ||
> -        image->descriptor.type != SPICE_IMAGE_TYPE_BITMAP) {
> -        return;
> -    }
> -
> -    if (display->stream_video == SPICE_STREAM_VIDEO_FILTER) {
> -        SpiceRect* rect;
> -        int size;
> -
> -        rect = &drawable->red_drawable->u.copy.src_area;
> -        size = (rect->right - rect->left) * (rect->bottom - rect->top);
> -        if (size < RED_STREAM_MIN_SIZE) {
> -            return;
> -        }
> -    }
> -
> -    drawable->streamable = TRUE;
> -}
> -
> -void print_stats(DisplayChannel *display)
> -{
> -#ifdef RED_WORKER_STAT
> -    stat_time_t total = display->add_stat.total;
> -    spice_info("add with shadow count %u",
> -               display->add_with_shadow_count);
> -    display->add_with_shadow_count = 0;
> -    spice_info("add[%u] %f exclude[%u] %f __exclude[%u] %f",
> -               display->add_stat.count,
> -               stat_cpu_time_to_sec(total),
> -               display->exclude_stat.count,
> -               stat_cpu_time_to_sec(display->exclude_stat.total),
> -               display->__exclude_stat.count,
> -               stat_cpu_time_to_sec(display->__exclude_stat.total));
> -    spice_info("add %f%% exclude %f%% exclude2 %f%% __exclude %f%%",
> -               (double)(total - display->exclude_stat.total) / total * 100,
> -               (double)(display->exclude_stat.total) / total * 100,
> -               (double)(display->exclude_stat.total -
> -                        display->__exclude_stat.total) / display
> ->exclude_stat.total * 100,
> -               (double)(display->__exclude_stat.total) / display
> ->exclude_stat.total * 100);
> -    stat_reset(&display->add_stat);
> -    stat_reset(&display->exclude_stat);
> -    stat_reset(&display->__exclude_stat);
> -#endif
> -}
> -
> - static int red_add_drawable(DisplayChannel *display, Drawable *drawable)
> -{
> -    int ret = FALSE, surface_id = drawable->surface_id;
> -    RedDrawable *red_drawable = drawable->red_drawable;
> -    Ring *ring = &display->surfaces[surface_id].current;
> -
> -    if (has_shadow(red_drawable)) {
> -        ret = current_add_with_shadow(display, ring, drawable);
> -    } else {
> -        drawable_update_streamable(display, drawable);
> -        ret = current_add(display, ring, drawable);
> -    }
> -
> -#ifdef RED_WORKER_STAT
> -    if ((++display->add_count % 100) == 0)
> -        print_stats(display);
> -#endif
> -    return ret;
> -}
> -
>  static void red_get_area(DisplayChannel *display, int surface_id, const
> SpiceRect *area,
>                           uint8_t *dest, int dest_stride, int update)
>  {
> @@ -2193,7 +1660,7 @@ static inline void red_process_draw(RedWorker *worker,
> RedDrawable *red_drawable
>          goto cleanup;
>      }
>  
> -    if (red_add_drawable(worker->display_channel, drawable)) {
> +    if (display_channel_add_drawable(worker->display_channel, drawable)) {
>          red_pipes_add_drawable(worker->display_channel, drawable);
>      }
>  cleanup:


More information about the Spice-devel mailing list