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

Jonathon Jongsma jjongsma at redhat.com
Tue Nov 17 12:05:03 PST 2015


Alternate version of this patch sent (and ACKed) in the earlier thread. see 
http://lists.freedesktop.org/archives/spice-devel/2015-November/023703.html

On Tue, 2015-11-17 at 16:37 +0000, Frediano Ziglio wrote:
> From: Marc-André Lureau <marcandre.lureau at gmail.com>
> 
> ---
>  server/display-channel.c | 526 +++++++++++++++++++++++++++++++++++++++++++
>  server/display-channel.h |  15 +-
>  server/red_worker.c      | 566 ++--------------------------------------------
> -
>  server/red_worker.h      |   1 +
>  4 files changed, 560 insertions(+), 548 deletions(-)
> 
> diff --git a/server/display-channel.c b/server/display-channel.c
> index 1d5d8d3..baec460 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_assert(!region_is_empty(&item->base.rgn));
> +    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;
> +
> +    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
> +}
> +
> +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;
> +}
> diff --git a/server/display-channel.h b/server/display-channel.h
> index 26b4e4e..a1e5947 100644
> --- a/server/display-channel.h
> +++ b/server/display-channel.h
> @@ -364,12 +364,12 @@ struct DisplayChannel {
>      RedCompressBuf *free_compress_bufs;
>  
>  /* TODO: some day unify this, make it more runtime.. */
> +    uint32_t add_count;
> +    uint32_t add_with_shadow_count;
>  #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;
> @@ -423,6 +423,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)
>  {
> @@ -539,4 +541,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 1c2ab67..5b14836 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;
> @@ -1377,350 +1188,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_assert(!region_is_empty(&item->base.rgn));
> -    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)
>  {
> @@ -2018,7 +1485,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:
> @@ -9011,3 +8478,10 @@ RedChannel* red_worker_get_display_channel(RedWorker
> *worker)
>  
>      return RED_CHANNEL(worker->display_channel);
>  }
> +
> +clockid_t red_worker_get_clockid(RedWorker *worker)
> +{
> +    spice_return_val_if_fail(worker, 0);
> +
> +    return worker->clockid;
> +}
> diff --git a/server/red_worker.h b/server/red_worker.h
> index 3604dfd..d70e862 100644
> --- a/server/red_worker.h
> +++ b/server/red_worker.h
> @@ -110,6 +110,7 @@ QXLInstance* red_worker_get_qxl(RedWorker *worker);
>  RedChannel* red_worker_get_cursor_channel(RedWorker *worker);
>  RedChannel* red_worker_get_display_channel(RedWorker *worker);
>  void red_worker_print_stats(RedWorker *worker);
> +clockid_t red_worker_get_clockid(RedWorker *worker);
>  
>  RedChannel *red_worker_new_channel(RedWorker *worker, int size,
>                                     const char *name,


More information about the Spice-devel mailing list