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

Pavel Grunt pgrunt at redhat.com
Thu Nov 12 23:08:17 PST 2015


Hi,

On Thu, 2015-11-12 at 17:24 -0600, Jonathon Jongsma wrote:
> I plan to split this one up a little bit.
> 
Thanks. I just want to mention that it wrongly moves update_copy_graduality - it
introduces again the version using the global variable.

Pavel
> 
> On Thu, 2015-11-12 at 14:01 +0000, Frediano Ziglio wrote:
> > From: Marc-André Lureau <marcandre.lureau at gmail.com>
> > 
> > ---
> >  server/display-channel.c |  541 ++++++++++++++++++++++++
> >  server/display-channel.h |   53 ++-
> >  server/red_worker.c      | 1054 ++-----------------------------------------
> > --
> > -
> >  server/red_worker.h      |    1 +
> >  server/stream.c          |  360 ++++++++++++++++
> >  server/stream.h          |    9 +
> >  server/tree.c            |   50 +++
> >  server/tree.h            |    5 +
> >  8 files changed, 1050 insertions(+), 1023 deletions(-)
> > 
> > diff --git a/server/display-channel.c b/server/display-channel.c
> > index 824f601..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);
> > @@ -148,6 +163,21 @@ DisplayChannelClient *dcc_new(DisplayChannel *display,
> >      return dcc;
> >  }
> >  
> > +void dcc_add_stream_agent_clip(DisplayChannelClient* dcc, StreamAgent
> > *agent)
> > +{
> > +    StreamClipItem *item = stream_clip_item_new(dcc, agent);
> > +    int n_rects;
> > +
> > +    item->clip_type = SPICE_CLIP_TYPE_RECTS;
> > +
> > +    n_rects = pixman_region32_n_rects(&agent->clip);
> > +    item->rects = spice_malloc_n_m(n_rects, sizeof(SpiceRect),
> > sizeof(SpiceClipRects));
> > +    item->rects->num_rects = n_rects;
> > +    region_ret_rects(&agent->clip, item->rects->rects, n_rects);
> > +
> > +    red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), (PipeItem *)item);
> > +}
> > +
> >  MonitorsConfig* monitors_config_ref(MonitorsConfig *monitors_config)
> >  {
> >      monitors_config->refs++;
> > @@ -391,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(displa
> > y,
> > 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 599cce7..432cc61 100644
> > --- a/server/display-channel.h
> > +++ b/server/display-channel.h
> > @@ -158,6 +158,11 @@ struct Drawable {
> >      uint32_t process_commands_generation;
> >  };
> >  
> > +#define LINK_TO_DPI(ptr) SPICE_CONTAINEROF((ptr), DrawablePipeItem, base)
> > +#define DRAWABLE_FOREACH_DPI_SAFE(drawable, link, next, dpi)            \
> > +    SAFE_FOREACH(link, next, drawable,  &(drawable)->pipes, dpi,
> > LINK_TO_DPI(link))
> > +
> > +
> >  struct DisplayChannelClient {
> >      CommonChannelClient common;
> >      SpiceImageCompression image_compression;
> > @@ -242,6 +247,10 @@
> > DisplayChannelClient*      dcc_new                       
> >             (DisplayCha
> >  void                       dcc_push_monitors_config                 
> >  (DisplayChannelClient *dcc);
> >  void                       dcc_push_destroy_surface                 
> >  (DisplayChannelClient *dcc,
> >                                                                       
> >  uint32_t surface_id);
> > +void                       dcc_add_stream_agent_clip                
> >  (DisplayChannelClient* dcc,
> > +                                                                     
> >  StreamAgent *agent);
> > +void                       dcc_create_stream                        
> >  (DisplayChannelClient *dcc,
> > +                                                                      Strea
> > m
> > *stream);
> >  
> >  typedef struct DrawablePipeItem {
> >      RingItem base;  /* link for a list of pipe items held by Drawable */
> > @@ -347,12 +356,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;
> > @@ -397,9 +406,6 @@ typedef struct SurfaceCreateItem {
> >  
> >  void                       display_channel_set_stream_video         
> >  (DisplayChannel *display,
> >                                                                        int
> > stream_video);
> > -void                       display_channel_attach_stream            
> >  (DisplayChannel *display,
> > -                                                                     
> >  Drawable *drawable,
> > -                                                                      Strea
> > m
> > *stream);
> >  int                        display_channel_get_streams_timeout      
> >  (DisplayChannel *display);
> >  void                       display_channel_compress_stats_print      (const
> > DisplayChannel *display);
> >  void                       display_channel_compress_stats_reset     
> >  (DisplayChannel *display);
> > @@ -409,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)
> >  {
> > @@ -491,6 +499,23 @@ static inline int is_same_drawable(Drawable *d1,
> > Drawable
> > *d2)
> >      }
> >  }
> >  
> > +static inline int is_drawable_independent_from_surfaces(Drawable *drawable)
> > +{
> > +    int x;
> > +
> > +    for (x = 0; x < 3; ++x) {
> > +        if (drawable->surface_deps[x] != -1) {
> > +            return FALSE;
> > +        }
> > +    }
> > +    return TRUE;
> > +}
> > +
> > +static inline int has_shadow(RedDrawable *drawable)
> > +{
> > +    return drawable->type == QXL_COPY_BITS;
> > +}
> > +
> >  static inline int is_primary_surface(DisplayChannel *display, uint32_t
> > surface_id)
> >  {
> >      if (surface_id == 0) {
> > @@ -499,4 +524,22 @@ static inline int is_primary_surface(DisplayChannel
> > *display, uint32_t surface_i
> >      return FALSE;
> >  }
> >  
> > +static inline void region_add_clip_rects(QRegion *rgn, SpiceClipRects
> > *data)
> > +{
> > +    int i;
> > +
> > +    for (i = 0; i < data->num_rects; i++) {
> > +        region_add(rgn, data->rects + i);
> > +    }
> > +}
> > +
> > +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 82d39a9..8aa7e70 100644
> > --- a/server/red_worker.c
> > +++ b/server/red_worker.c
> > @@ -79,9 +79,6 @@
> >  
> >  #define DISPLAY_FREE_LIST_DEFAULT_SIZE 128
> >  
> > -#define FPS_TEST_INTERVAL 1
> > -#define MAX_FPS 30
> > -
> >  #define ZLIB_DEFAULT_COMPRESSION_LEVEL 3
> >  #define MIN_GLZ_SIZE_FOR_ZLIB 100
> >  
> > @@ -286,8 +283,6 @@ static void red_draw_drawable(DisplayChannel *display,
> > Drawable *item);
> >  static void red_update_area(DisplayChannel *display, const SpiceRect *area,
> > int surface_id);
> >  static void red_update_area_till(DisplayChannel *display, const SpiceRect
> > *area, int surface_id,
> >                                   Drawable *last);
> > -static void detach_stream(DisplayChannel *display, Stream *stream, int
> > detach_sized);
> > -static inline void display_channel_stream_maintenance(DisplayChannel
> > *display, Drawable *candidate, Drawable *sect);
> >  static inline void display_begin_send_message(RedChannelClient *rcc);
> >  static void red_release_glz(DisplayChannelClient *dcc);
> >  static void red_freeze_glz(DisplayChannelClient *dcc);
> > @@ -305,10 +300,6 @@ static void red_create_surface(DisplayChannel *display,
> > uint32_t surface_id, uin
> >                                 uint32_t height, int32_t stride, uint32_t
> > format,
> >                                 void *line_0, int data_is_valid, int
> > send_client);
> >  
> > -#define LINK_TO_DPI(ptr) SPICE_CONTAINEROF((ptr), DrawablePipeItem, base)
> > -#define DRAWABLE_FOREACH_DPI_SAFE(drawable, link, next, dpi)          \
> > -    SAFE_FOREACH(link, next, drawable,  &(drawable)->pipes, dpi,
> > LINK_TO_DPI(link))
> > -
> >  
> >  #define LINK_TO_GLZ(ptr) SPICE_CONTAINEROF((ptr), RedGlzDrawable, \
> >                                             drawable_link)
> > @@ -326,22 +317,6 @@ static void display_stream_clip_unref(DisplayChannel
> > *display, StreamClipItem *i
> >      free(item);
> >  }
> >  
> > -static void dcc_push_stream_agent_clip(DisplayChannelClient* dcc,
> > StreamAgent
> > *agent)
> > -{
> > -    StreamClipItem *item = stream_clip_item_new(dcc, agent);
> > -    int n_rects;
> > -
> > -    item->clip_type = SPICE_CLIP_TYPE_RECTS;
> > -
> > -    n_rects = pixman_region32_n_rects(&agent->clip);
> > -    item->rects = spice_malloc_n_m(n_rects, sizeof(SpiceRect),
> > sizeof(SpiceClipRects));
> > -    item->rects->num_rects = n_rects;
> > -    region_ret_rects(&agent->clip, item->rects->rects, n_rects);
> > -
> > -    red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), (PipeItem *)item);
> > -}
> > -
> > -
> >  void attach_stream(DisplayChannel *display, Drawable *drawable, Stream
> > *stream)
> >  {
> >      DisplayChannelClient *dcc;
> > @@ -378,7 +353,7 @@ void attach_stream(DisplayChannel *display, Drawable
> > *drawable, Stream *stream)
> >          if (!region_is_equal(&clip_in_draw_dest, &drawable
> > ->tree_item.base.rgn)) {
> >              region_remove(&agent->clip, &drawable->red_drawable->bbox);
> >              region_or(&agent->clip, &drawable->tree_item.base.rgn);
> > -            dcc_push_stream_agent_clip(dcc, agent);
> > +            dcc_add_stream_agent_clip(dcc, agent);
> >          }
> >  #ifdef STREAM_STATS
> >          agent->stats.num_input_frames++;
> > @@ -517,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;
> >  
> > @@ -526,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;
> > @@ -549,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;
> > @@ -596,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;
> > @@ -803,21 +778,6 @@ void display_channel_drawable_unref(DisplayChannel
> > *display, Drawable *drawable)
> >      display->drawable_count--;
> >  }
> >  
> > -static inline void remove_shadow(DrawItem *item)
> > -{
> > -    Shadow *shadow;
> > -
> > -    if (!item->shadow) {
> > -        return;
> > -    }
> > -    shadow = item->shadow;
> > -    item->shadow = NULL;
> > -    ring_remove(&shadow->base.siblings_link);
> > -    region_destroy(&shadow->base.rgn);
> > -    region_destroy(&shadow->on_hold);
> > -    free(shadow);
> > -}
> > -
> >  static void display_stream_trace_add_drawable(DisplayChannel *display,
> > Drawable *item)
> >  {
> >      ItemTrace *trace;
> > @@ -856,10 +816,11 @@ 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);
> > -    remove_shadow(&item->tree_item);
> > +    draw_item_remove_shadow(&item->tree_item);
> >      ring_remove(&item->tree_item.base.siblings_link);
> >      ring_remove(&item->list_link);
> >      ring_remove(&item->surface_list_link);
> > @@ -867,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;
> >  
> > @@ -883,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;
> >  
> > @@ -909,7 +866,7 @@ static inline void current_remove(DisplayChannel
> > *display,
> > TreeItem *item)
> >      }
> >  }
> >  
> > -static void current_clear(DisplayChannel *display, int surface_id)
> > +static void current_remove_all(DisplayChannel *display, int surface_id)
> >  {
> >      Ring *ring = &display->surfaces[surface_id].current;
> >      RingItem *ring_item;
> > @@ -1015,187 +972,8 @@ static void
> > red_clear_surface_drawables_from_pipes(DisplayChannel *display,
> >      }
> >  }
> >  
> > -static inline Shadow *__find_shadow(TreeItem *item)
> > -{
> > -    while (item->type == TREE_ITEM_TYPE_CONTAINER) {
> > -        if (!(item = (TreeItem *)ring_get_tail(&((Container *)item)-
> > >items)))
> > {
> > -            return NULL;
> > -        }
> > -    }
> > -
> > -    if (item->type != TREE_ITEM_TYPE_DRAWABLE) {
> > -        return NULL;
> > -    }
> > -
> > -    return ((DrawItem *)item)->shadow;
> > -}
> > -
> > -static inline Ring *ring_of(Ring *ring, TreeItem *item)
> > -{
> > -    return (item->container) ? &item->container->items : ring;
> > -}
> > -
> > -static inline int __contained_by(TreeItem *item, Ring *ring)
> > -{
> > -    spice_assert(item && ring);
> > -    do {
> > -        Ring *now = ring_of(ring, item);
> > -        if (now == ring) {
> > -            return TRUE;
> > -        }
> > -    } while ((item = (TreeItem *)item->container));
> > -
> > -    return FALSE;
> > -}
> > -
> > -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 (!__contained_by((TreeItem*)shadow, *top_ring)) {
> > -                        *top_ring = ring_of(ring, (TreeItem*)shadow);
> > -                    }
> > -                }
> > -            } else {
> > -                if (frame_candidate) {
> > -                    Drawable *drawable = SPICE_CONTAINEROF(draw, Drawable,
> > tree_item);
> > -                    display_channel_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 = __find_shadow(item))) {
> > -                    region_or(rgn, &shadow->on_hold);
> > -                    if (!__contained_by((TreeItem*)shadow, *top_ring)) {
> > -                        *top_ring = ring_of(ring, (TreeItem*)shadow);
> > -                    }
> > -                }
> > -            }
> > -        } 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++;
> > -}
> > -
> > -static void detach_stream(DisplayChannel *display, Stream *stream,
> > -                          int detach_sized)
> > +void detach_stream(DisplayChannel *display, Stream *stream,
> > +                   int detach_sized)
> >  {
> >      spice_assert(stream->current && stream->current->stream);
> >      spice_assert(stream->current->stream == stream);
> > @@ -1234,7 +1012,7 @@ static void
> > dcc_detach_stream_gracefully(DisplayChannelClient *dcc,
> >  
> >      /* stopping the client from playing older frames at once*/
> >      region_clear(&agent->clip);
> > -    dcc_push_stream_agent_clip(dcc, agent);
> > +    dcc_add_stream_agent_clip(dcc, agent);
> >  
> >      if (region_is_empty(&agent->vis_region)) {
> >          spice_debug("stream %d: vis region empty", stream_id);
> > @@ -1316,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);
> > @@ -1349,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_push_stream_agent_clip(dcc, agent);
> > -            }
> > -        }
> > -    }
> > -}
> > -
> >  static void display_channel_streams_timeout(DisplayChannel *display)
> >  {
> >      Ring *ring = &display->streams;
> > @@ -1406,17 +1144,6 @@ static void
> > display_channel_streams_timeout(DisplayChannel *display)
> >      }
> >  }
> >  
> > -static Stream *display_channel_stream_try_new(DisplayChannel *display)
> > -{
> > -    Stream *stream;
> > -    if (!display->free_streams) {
> > -        return NULL;
> > -    }
> > -    stream = display->free_streams;
> > -    display->free_streams = display->free_streams->next;
> > -    return stream;
> > -}
> > -
> >  static uint64_t red_stream_get_initial_bit_rate(DisplayChannelClient *dcc,
> >                                                  Stream *stream)
> >  {
> > @@ -1543,7 +1270,7 @@ static void
> > red_stream_update_client_playback_latency(void *opaque, uint32_t del
> >      main_dispatcher_set_mm_time_latency(RED_CHANNEL_CLIENT(agent->dcc)
> > ->client, agent->dcc->streams_max_latency);
> >  }
> >  
> > -static void dcc_create_stream(DisplayChannelClient *dcc, Stream *stream)
> > +void dcc_create_stream(DisplayChannelClient *dcc, Stream *stream)
> >  {
> >      StreamAgent *agent = &dcc->stream_agents[get_stream_id(DCC_TO_DC(dcc),
> > stream)];
> >  
> > @@ -1592,47 +1319,6 @@ static void dcc_create_stream(DisplayChannelClient
> > *dcc, Stream *stream)
> >  #endif
> >  }
> >  
> > -static void display_channel_create_stream(DisplayChannel *display, Drawable
> > *drawable)
> > -{
> > -    DisplayChannelClient *dcc;
> > -    RingItem *dcc_ring_item, *next;
> > -    Stream *stream;
> > -    SpiceRect* src_rect;
> > -
> > -    spice_assert(!drawable->stream);
> > -
> > -    if (!(stream = display_channel_stream_try_new(display))) {
> > -        return;
> > -    }
> > -
> > -    spice_assert(drawable->red_drawable->type == QXL_DRAW_COPY);
> > -    src_rect = &drawable->red_drawable->u.copy.src_area;
> > -
> > -    ring_add(&display->streams, &stream->link);
> > -    stream->current = drawable;
> > -    stream->last_time = drawable->creation_time;
> > -    stream->width = src_rect->right - src_rect->left;
> > -    stream->height = src_rect->bottom - src_rect->top;
> > -    stream->dest_area = drawable->red_drawable->bbox;
> > -    stream->refs = 1;
> > -    SpiceBitmap *bitmap = &drawable->red_drawable->u.copy.src_bitmap
> > ->u.bitmap;
> > -    stream->top_down = !!(bitmap->flags & SPICE_BITMAP_FLAGS_TOP_DOWN);
> > -    drawable->stream = stream;
> > -    stream->input_fps = MAX_FPS;
> > -    stream->num_input_frames = 0;
> > -    stream->input_fps_start_time = drawable->creation_time;
> > -    display->streams_size_total += stream->width * stream->height;
> > -    display->stream_count++;
> > -    FOREACH_DCC(display, dcc_ring_item, next, dcc) {
> > -        dcc_create_stream(dcc, stream);
> > -    }
> > -    spice_debug("stream %d %dx%d (%d, %d) (%d, %d)",
> > -                (int)(stream - display->streams_buf), stream->width,
> > -                stream->height, stream->dest_area.left, stream
> > ->dest_area.top,
> > -                stream->dest_area.right, stream->dest_area.bottom);
> > -    return;
> > -}
> > -
> >  static void dcc_create_all_streams(DisplayChannelClient *dcc)
> >  {
> >      Ring *ring = &DCC_TO_DC(dcc)->streams;
> > @@ -1677,681 +1363,6 @@ static void
> > dcc_destroy_stream_agents(DisplayChannelClient *dcc)
> >      }
> >  }
> >  
> > -static int is_next_stream_frame(DisplayChannel *display,
> > -                                const Drawable *candidate,
> > -                                const int other_src_width,
> > -                                const int other_src_height,
> > -                                const SpiceRect *other_dest,
> > -                                const red_time_t other_time,
> > -                                const Stream *stream,
> > -                                int container_candidate_allowed)
> > -{
> > -    RedDrawable *red_drawable;
> > -    int is_frame_container = FALSE;
> > -
> > -    if (!candidate->streamable) {
> > -        return STREAM_FRAME_NONE;
> > -    }
> > -
> > -    if (candidate->creation_time - other_time >
> > -            (stream ? RED_STREAM_CONTINUS_MAX_DELTA :
> > RED_STREAM_DETACTION_MAX_DELTA)) {
> > -        return STREAM_FRAME_NONE;
> > -    }
> > -
> > -    red_drawable = candidate->red_drawable;
> > -    if (!container_candidate_allowed) {
> > -        SpiceRect* candidate_src;
> > -
> > -        if (!rect_is_equal(&red_drawable->bbox, other_dest)) {
> > -            return STREAM_FRAME_NONE;
> > -        }
> > -
> > -        candidate_src = &red_drawable->u.copy.src_area;
> > -        if (candidate_src->right - candidate_src->left != other_src_width
> > ||
> > -            candidate_src->bottom - candidate_src->top != other_src_height)
> > {
> > -            return STREAM_FRAME_NONE;
> > -        }
> > -    } else {
> > -        if (rect_contains(&red_drawable->bbox, other_dest)) {
> > -            int candidate_area = rect_get_area(&red_drawable->bbox);
> > -            int other_area = rect_get_area(other_dest);
> > -            /* do not stream drawables that are significantly
> > -             * bigger than the original frame */
> > -            if (candidate_area > 2 * other_area) {
> > -                spice_debug("too big candidate:");
> > -                spice_debug("prev box ==>");
> > -                rect_debug(other_dest);
> > -                spice_debug("new box ==>");
> > -                rect_debug(&red_drawable->bbox);
> > -                return STREAM_FRAME_NONE;
> > -            }
> > -
> > -            if (candidate_area > other_area) {
> > -                is_frame_container = TRUE;
> > -            }
> > -        } else {
> > -            return STREAM_FRAME_NONE;
> > -        }
> > -    }
> > -
> > -    if (stream) {
> > -        SpiceBitmap *bitmap = &red_drawable->u.copy.src_bitmap->u.bitmap;
> > -        if (stream->top_down != !!(bitmap->flags &
> > SPICE_BITMAP_FLAGS_TOP_DOWN)) {
> > -            return STREAM_FRAME_NONE;
> > -        }
> > -    }
> > -    if (is_frame_container) {
> > -        return STREAM_FRAME_CONTAINER;
> > -    } else {
> > -        return STREAM_FRAME_NATIVE;
> > -    }
> > -}
> > -
> > -static void before_reattach_stream(DisplayChannel *display,
> > -                                   Stream *stream, Drawable *new_frame)
> > -{
> > -    DrawablePipeItem *dpi;
> > -    DisplayChannelClient *dcc;
> > -    int index;
> > -    StreamAgent *agent;
> > -    RingItem *ring_item, *next;
> > -
> > -    spice_return_if_fail(stream->current);
> > -
> > -    if (!red_channel_is_connected(RED_CHANNEL(display))) {
> > -        return;
> > -    }
> > -
> > -    if (new_frame->process_commands_generation == stream->current
> > ->process_commands_generation) {
> > -        spice_debug("ignoring drop, same process_commands_generation as
> > previous frame");
> > -        return;
> > -    }
> > -
> > -    index = get_stream_id(display, stream);
> > -    DRAWABLE_FOREACH_DPI_SAFE(stream->current, ring_item, next, dpi) {
> > -        dcc = dpi->dcc;
> > -        agent = &dcc->stream_agents[index];
> > -
> > -        if (!dcc->use_mjpeg_encoder_rate_control &&
> > -            !dcc->common.is_low_bandwidth) {
> > -            continue;
> > -        }
> > -
> > -        if (pipe_item_is_linked(&dpi->dpi_pipe_item)) {
> > -#ifdef STREAM_STATS
> > -            agent->stats.num_drops_pipe++;
> > -#endif
> > -            if (dcc->use_mjpeg_encoder_rate_control) {
> > -                mjpeg_encoder_notify_server_frame_drop(agent-
> > >mjpeg_encoder);
> > -            } else {
> > -                ++agent->drops;
> > -            }
> > -        }
> > -    }
> > -
> > -
> > -    FOREACH_DCC(display, ring_item, next, dcc) {
> > -        double drop_factor;
> > -
> > -        agent = &dcc->stream_agents[index];
> > -
> > -        if (dcc->use_mjpeg_encoder_rate_control) {
> > -            continue;
> > -        }
> > -        if (agent->frames / agent->fps < FPS_TEST_INTERVAL) {
> > -            agent->frames++;
> > -            continue;
> > -        }
> > -        drop_factor = ((double)agent->frames - (double)agent->drops) /
> > -            (double)agent->frames;
> > -        spice_debug("stream %d: #frames %u #drops %u", index, agent-
> > >frames,
> > agent->drops);
> > -        if (drop_factor == 1) {
> > -            if (agent->fps < MAX_FPS) {
> > -                agent->fps++;
> > -                spice_debug("stream %d: fps++ %u", index, agent->fps);
> > -            }
> > -        } else if (drop_factor < 0.9) {
> > -            if (agent->fps > 1) {
> > -                agent->fps--;
> > -                spice_debug("stream %d: fps--%u", index, agent->fps);
> > -            }
> > -        }
> > -        agent->frames = 1;
> > -        agent->drops = 0;
> > -    }
> > -}
> > -
> > -static void update_copy_graduality(DisplayChannel *display, Drawable
> > *drawable)
> > -{
> > -    SpiceBitmap *bitmap;
> > -    spice_return_if_fail(drawable->red_drawable->type == QXL_DRAW_COPY);
> > -
> > -    if (display->stream_video != SPICE_STREAM_VIDEO_FILTER) {
> > -        drawable->copy_bitmap_graduality = BITMAP_GRADUAL_INVALID;
> > -        return;
> > -    }
> > -
> > -    if (drawable->copy_bitmap_graduality != BITMAP_GRADUAL_INVALID) {
> > -        return; // already set
> > -    }
> > -
> > -    bitmap = &drawable->red_drawable->u.copy.src_bitmap->u.bitmap;
> > -
> > -    if (!bitmap_fmt_has_graduality(bitmap->format) ||
> > bitmap_has_extra_stride(bitmap) ||
> > -        (bitmap->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE)) {
> > -        drawable->copy_bitmap_graduality = BITMAP_GRADUAL_NOT_AVAIL;
> > -    } else  {
> > -        drawable->copy_bitmap_graduality =
> > bitmap_get_graduality_level(bitmap);
> > -    }
> > -}
> > -
> > -static int is_stream_start(Drawable *drawable)
> > -{
> > -    return ((drawable->frames_count >= RED_STREAM_FRAMES_START_CONDITION)
> > &&
> > -            (drawable->gradual_frames_count >=
> > -            (RED_STREAM_GRADUAL_FRAMES_START_CONDITION * drawable
> > ->frames_count)));
> > -}
> > -
> > -// returns whether a stream was created
> > -static int display_channel_stream_add_frame(DisplayChannel *display,
> > -                                            Drawable *frame_drawable,
> > -                                            int frames_count,
> > -                                            int gradual_frames_count,
> > -                                            int last_gradual_frame)
> > -{
> > -    update_copy_graduality(display, frame_drawable);
> > -    frame_drawable->frames_count = frames_count + 1;
> > -    frame_drawable->gradual_frames_count  = gradual_frames_count;
> > -
> > -    if (frame_drawable->copy_bitmap_graduality != BITMAP_GRADUAL_LOW) {
> > -        if ((frame_drawable->frames_count - last_gradual_frame) >
> > -            RED_STREAM_FRAMES_RESET_CONDITION) {
> > -            frame_drawable->frames_count = 1;
> > -            frame_drawable->gradual_frames_count = 1;
> > -        } else {
> > -            frame_drawable->gradual_frames_count++;
> > -        }
> > -
> > -        frame_drawable->last_gradual_frame = frame_drawable->frames_count;
> > -    } else {
> > -        frame_drawable->last_gradual_frame = last_gradual_frame;
> > -    }
> > -
> > -    if (is_stream_start(frame_drawable)) {
> > -        display_channel_create_stream(display, frame_drawable);
> > -        return TRUE;
> > -    }
> > -    return FALSE;
> > -}
> > -
> > -static void display_channel_stream_maintenance(DisplayChannel *display,
> > -                                               Drawable *candidate,
> > Drawable
> > *prev)
> > -{
> > -    int is_next_frame;
> > -
> > -    if (candidate->stream) {
> > -        return;
> > -    }
> > -
> > -    if (prev->stream) {
> > -        Stream *stream = prev->stream;
> > -
> > -        is_next_frame = is_next_stream_frame(display, candidate,
> > -                                             stream->width, stream->height,
> > -                                             &stream->dest_area, stream
> > ->last_time,
> > -                                             stream, TRUE);
> > -        if (is_next_frame != STREAM_FRAME_NONE) {
> > -            before_reattach_stream(display, stream, candidate);
> > -            detach_stream(display, stream, FALSE);
> > -            prev->streamable = FALSE; //prevent item trace
> > -            attach_stream(display, candidate, stream);
> > -            if (is_next_frame == STREAM_FRAME_CONTAINER) {
> > -                candidate->sized_stream = stream;
> > -            }
> > -        }
> > -    } else if (candidate->streamable) {
> > -        SpiceRect* prev_src = &prev->red_drawable->u.copy.src_area;
> > -
> > -        is_next_frame =
> > -            is_next_stream_frame(display, candidate, prev_src->right -
> > prev_src->left,
> > -                                 prev_src->bottom - prev_src->top,
> > -                                 &prev->red_drawable->bbox, prev
> > ->creation_time,
> > -                                 prev->stream,
> > -                                 FALSE);
> > -        if (is_next_frame != STREAM_FRAME_NONE) {
> > -            display_channel_stream_add_frame(display, candidate,
> > -                                             prev->frames_count,
> > -                                             prev->gradual_frames_count,
> > -                                             prev->last_gradual_frame);
> > -        }
> > -    }
> > -}
> > -
> > -static inline int is_drawable_independent_from_surfaces(Drawable *drawable)
> > -{
> > -    int x;
> > -
> > -    for (x = 0; x < 3; ++x) {
> > -        if (drawable->surface_deps[x] != -1) {
> > -            return FALSE;
> > -        }
> > -    }
> > -    return TRUE;
> > -}
> > -
> > -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);
> > -        display_channel_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;
> > -}
> > -
> > -#define FOREACH_STREAMS(display, item)                  \
> > -    for (item = ring_get_head(&(display)->streams);     \
> > -         item != NULL;                                  \
> > -         item = ring_next(&(display)->streams, item))
> > -
> > -static void red_use_stream_trace(DisplayChannel *display, Drawable
> > *drawable)
> > -{
> > -    ItemTrace *trace;
> > -    ItemTrace *trace_end;
> > -    RingItem *item;
> > -
> > -    if (drawable->stream || !drawable->streamable || drawable-
> > >frames_count)
> > {
> > -        return;
> > -    }
> > -
> > -    FOREACH_STREAMS(display, item) {
> > -        Stream *stream = SPICE_CONTAINEROF(item, Stream, link);
> > -        int is_next_frame = is_next_stream_frame(display,
> > -                                                 drawable,
> > -                                                 stream->width,
> > -                                                 stream->height,
> > -                                                 &stream->dest_area,
> > -                                                 stream->last_time,
> > -                                                 stream,
> > -                                                 TRUE);
> > -        if (is_next_frame != STREAM_FRAME_NONE) {
> > -            if (stream->current) {
> > -                stream->current->streamable = FALSE; //prevent item trace
> > -                before_reattach_stream(display, stream, drawable);
> > -                detach_stream(display, stream, FALSE);
> > -            }
> > -            attach_stream(display, drawable, stream);
> > -            if (is_next_frame == STREAM_FRAME_CONTAINER) {
> > -                drawable->sized_stream = stream;
> > -            }
> > -            return;
> > -        }
> > -    }
> > -
> > -    trace = display->items_trace;
> > -    trace_end = trace + NUM_TRACE_ITEMS;
> > -    for (; trace < trace_end; trace++) {
> > -        if (is_next_stream_frame(display, drawable, trace->width, trace
> > ->height,
> > -                                       &trace->dest_area, trace->time,
> > NULL,
> > FALSE) !=
> > -                                       STREAM_FRAME_NONE) {
> > -            if (display_channel_stream_add_frame(display, drawable,
> > -                                                 trace->frames_count,
> > -                                                 trace-
> > >gradual_frames_count,
> > -                                                 trace-
> > >last_gradual_frame))
> > {
> > -                return;
> > -            }
> > -        }
> > -    }
> > -}
> > -
> > -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 = __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);
> > -        red_use_stream_trace(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 void add_clip_rects(QRegion *rgn, SpiceClipRects *data)
> > -{
> > -    int i;
> > -
> > -    for (i = 0; i < data->num_rects; i++) {
> > -        region_add(rgn, data->rects + i);
> > -    }
> > -}
> > -
> > -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 inline int has_shadow(RedDrawable *drawable)
> > -{
> > -    return drawable->type == QXL_COPY_BITS;
> > -}
> > -
> > -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)
> >  {
> > @@ -2620,7 +1631,7 @@ static inline void red_process_draw(RedWorker *worker,
> > RedDrawable *red_drawable
> >          QRegion rgn;
> >  
> >          region_init(&rgn);
> > -        add_clip_rects(&rgn, red_drawable->clip.rects);
> > +        region_add_clip_rects(&rgn, red_drawable->clip.rects);
> >          region_and(&drawable->tree_item.base.rgn, &rgn);
> >          region_destroy(&rgn);
> >      }
> > @@ -2649,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:
> > @@ -2701,10 +1712,10 @@ static inline void red_process_surface(RedWorker
> > *worker, RedSurfaceCmd *surface
> >          }
> >          set_surface_release_info(&red_surface->destroy, surface
> > ->release_info, group_id);
> >          red_handle_depends_on_target_surface(display, surface_id);
> > -        /* note that red_handle_depends_on_target_surface must be called
> > before current_clear.
> > +        /* note that red_handle_depends_on_target_surface must be called
> > before current_remove_all.
> >             otherwise "current" will hold items that other drawables may
> > depend on, and then
> > -           red_current_clear will remove them from the pipe. */
> > -        current_clear(display, surface_id);
> > +           current_remove_all will remove them from the pipe. */
> > +        current_remove_all(display, surface_id);
> >          red_clear_surface_drawables_from_pipes(display, surface_id, FALSE);
> >          display_channel_surface_unref(display, surface_id);
> >          break;
> > @@ -3247,7 +2258,7 @@ static void red_current_flush(DisplayChannel *display,
> > int surface_id)
> >      while (!ring_is_empty(&display->surfaces[surface_id].current_list)) {
> >          free_one_drawable(display, FALSE);
> >      }
> > -    current_clear(display, surface_id);
> > +    current_remove_all(display, surface_id);
> >  }
> >  
> >  // adding the pipe item after pos. If pos == NULL, adding to head.
> > @@ -5044,7 +4055,7 @@ static void
> > surface_lossy_region_update(DisplayChannelClient *dcc,
> >          region_init(&clip_rgn);
> >          region_init(&draw_region);
> >          region_add(&draw_region, &drawable->bbox);
> > -        add_clip_rects(&clip_rgn, drawable->clip.rects);
> > +        region_add_clip_rects(&clip_rgn, drawable->clip.rects);
> >          region_and(&draw_region, &clip_rgn);
> >          if (lossy) {
> >              region_or(surface_lossy_region, &draw_region);
> > @@ -8590,10 +7601,10 @@ void
> > display_channel_destroy_surface_wait(DisplayChannel *display, int surface_i
> >          return;
> >  
> >      red_handle_depends_on_target_surface(display, surface_id);
> > -    /* note that red_handle_depends_on_target_surface must be called before
> > current_clear.
> > +    /* note that red_handle_depends_on_target_surface must be called before
> > current_remove_all.
> >         otherwise "current" will hold items that other drawables may depend
> > on, and then
> > -       current_clear will remove them from the pipe. */
> > -    current_clear(display, surface_id);
> > +       current_remove_all will remove them from the pipe. */
> > +    current_remove_all(display, surface_id);
> >      red_clear_surface_drawables_from_pipes(display, surface_id, TRUE);
> >  }
> >  
> > @@ -9627,3 +8638,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,
> > diff --git a/server/stream.c b/server/stream.c
> > index d61cec1..19ffa70 100644
> > --- a/server/stream.c
> > +++ b/server/stream.c
> > @@ -21,6 +21,12 @@
> >  #include "stream.h"
> >  #include "display-channel.h"
> >  
> > +#define FPS_TEST_INTERVAL 1
> > +#define FOREACH_STREAMS(display, item)                  \
> > +    for (item = ring_get_head(&(display)->streams);     \
> > +         item != NULL;                                  \
> > +         item = ring_next(&(display)->streams, item))
> > +
> >  void stream_agent_stats_print(StreamAgent *agent)
> >  {
> >  #ifdef STREAM_STATS
> > @@ -138,3 +144,357 @@ StreamClipItem
> > *stream_clip_item_new(DisplayChannelClient* dcc, StreamAgent *age
> >      item->refs = 1;
> >      return item;
> >  }
> > +
> > +static int is_stream_start(Drawable *drawable)
> > +{
> > +    return ((drawable->frames_count >= RED_STREAM_FRAMES_START_CONDITION)
> > &&
> > +            (drawable->gradual_frames_count >=
> > +             (RED_STREAM_GRADUAL_FRAMES_START_CONDITION * drawable
> > ->frames_count)));
> > +}
> > +
> > +static void update_copy_graduality(Drawable *drawable)
> > +{                                  ^
missing DisplayChannel -----------------|
> > +    SpiceBitmap *bitmap;
> > +    spice_return_if_fail(drawable->red_drawable->type == QXL_DRAW_COPY);
> > +
> > +    /* TODO: global property -> per dc/dcc */
> > +    if (streaming_video != SPICE_STREAM_VIDEO_FILTER) {
             display->stream_video
> > +        drawable->copy_bitmap_graduality = BITMAP_GRADUAL_INVALID;
> > +        return;
> > +    }
> > +
> > +    if (drawable->copy_bitmap_graduality != BITMAP_GRADUAL_INVALID) {
> > +        return; // already set
> > +    }
> > +
> > +    bitmap = &drawable->red_drawable->u.copy.src_bitmap->u.bitmap;
> > +
> > +    if (!bitmap_fmt_has_graduality(bitmap->format) ||
> > bitmap_has_extra_stride(bitmap) ||
> > +        (bitmap->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE)) {
> > +        drawable->copy_bitmap_graduality = BITMAP_GRADUAL_NOT_AVAIL;
> > +    } else  {
> > +        drawable->copy_bitmap_graduality =
> > bitmap_get_graduality_level(bitmap);
> > +    }
> > +}
> > +
> > +static int is_next_stream_frame(DisplayChannel *display,
> > +                                const Drawable *candidate,
> > +                                const int other_src_width,
> > +                                const int other_src_height,
> > +                                const SpiceRect *other_dest,
> > +                                const red_time_t other_time,
> > +                                const Stream *stream,
> > +                                int container_candidate_allowed)
> > +{
> > +    RedDrawable *red_drawable;
> > +    int is_frame_container = FALSE;
> > +
> > +    if (!candidate->streamable) {
> > +        return STREAM_FRAME_NONE;
> > +    }
> > +
> > +    if (candidate->creation_time - other_time >
> > +            (stream ? RED_STREAM_CONTINUS_MAX_DELTA :
> > RED_STREAM_DETACTION_MAX_DELTA)) {
> > +        return STREAM_FRAME_NONE;
> > +    }
> > +
> > +    red_drawable = candidate->red_drawable;
> > +    if (!container_candidate_allowed) {
> > +        SpiceRect* candidate_src;
> > +
> > +        if (!rect_is_equal(&red_drawable->bbox, other_dest)) {
> > +            return STREAM_FRAME_NONE;
> > +        }
> > +
> > +        candidate_src = &red_drawable->u.copy.src_area;
> > +        if (candidate_src->right - candidate_src->left != other_src_width
> > ||
> > +            candidate_src->bottom - candidate_src->top != other_src_height)
> > {
> > +            return STREAM_FRAME_NONE;
> > +        }
> > +    } else {
> > +        if (rect_contains(&red_drawable->bbox, other_dest)) {
> > +            int candidate_area = rect_get_area(&red_drawable->bbox);
> > +            int other_area = rect_get_area(other_dest);
> > +            /* do not stream drawables that are significantly
> > +             * bigger than the original frame */
> > +            if (candidate_area > 2 * other_area) {
> > +                spice_debug("too big candidate:");
> > +                spice_debug("prev box ==>");
> > +                rect_debug(other_dest);
> > +                spice_debug("new box ==>");
> > +                rect_debug(&red_drawable->bbox);
> > +                return STREAM_FRAME_NONE;
> > +            }
> > +
> > +            if (candidate_area > other_area) {
> > +                is_frame_container = TRUE;
> > +            }
> > +        } else {
> > +            return STREAM_FRAME_NONE;
> > +        }
> > +    }
> > +
> > +    if (stream) {
> > +        SpiceBitmap *bitmap = &red_drawable->u.copy.src_bitmap->u.bitmap;
> > +        if (stream->top_down != !!(bitmap->flags &
> > SPICE_BITMAP_FLAGS_TOP_DOWN)) {
> > +            return STREAM_FRAME_NONE;
> > +        }
> > +    }
> > +    if (is_frame_container) {
> > +        return STREAM_FRAME_CONTAINER;
> > +    } else {
> > +        return STREAM_FRAME_NATIVE;
> > +    }
> > +}
> > +
> > +static void before_reattach_stream(DisplayChannel *display,
> > +                                   Stream *stream, Drawable *new_frame)
> > +{
> > +    DrawablePipeItem *dpi;
> > +    DisplayChannelClient *dcc;
> > +    int index;
> > +    StreamAgent *agent;
> > +    RingItem *ring_item, *next;
> > +
> > +    spice_return_if_fail(stream->current);
> > +
> > +    if (!red_channel_is_connected(RED_CHANNEL(display))) {
> > +        return;
> > +    }
> > +
> > +    if (new_frame->process_commands_generation == stream->current
> > ->process_commands_generation) {
> > +        spice_debug("ignoring drop, same process_commands_generation as
> > previous frame");
> > +        return;
> > +    }
> > +
> > +    index = get_stream_id(display, stream);
> > +    DRAWABLE_FOREACH_DPI_SAFE(stream->current, ring_item, next, dpi) {
> > +        dcc = dpi->dcc;
> > +        agent = &dcc->stream_agents[index];
> > +
> > +        if (!dcc->use_mjpeg_encoder_rate_control &&
> > +            !dcc->common.is_low_bandwidth) {
> > +            continue;
> > +        }
> > +
> > +        if (pipe_item_is_linked(&dpi->dpi_pipe_item)) {
> > +#ifdef STREAM_STATS
> > +            agent->stats.num_drops_pipe++;
> > +#endif
> > +            if (dcc->use_mjpeg_encoder_rate_control) {
> > +                mjpeg_encoder_notify_server_frame_drop(agent-
> > >mjpeg_encoder);
> > +            } else {
> > +                ++agent->drops;
> > +            }
> > +        }
> > +    }
> > +
> > +
> > +    FOREACH_DCC(display, ring_item, next, dcc) {
> > +        double drop_factor;
> > +
> > +        agent = &dcc->stream_agents[index];
> > +
> > +        if (dcc->use_mjpeg_encoder_rate_control) {
> > +            continue;
> > +        }
> > +        if (agent->frames / agent->fps < FPS_TEST_INTERVAL) {
> > +            agent->frames++;
> > +            continue;
> > +        }
> > +        drop_factor = ((double)agent->frames - (double)agent->drops) /
> > +            (double)agent->frames;
> > +        spice_debug("stream %d: #frames %u #drops %u", index, agent-
> > >frames,
> > agent->drops);
> > +        if (drop_factor == 1) {
> > +            if (agent->fps < MAX_FPS) {
> > +                agent->fps++;
> > +                spice_debug("stream %d: fps++ %u", index, agent->fps);
> > +            }
> > +        } else if (drop_factor < 0.9) {
> > +            if (agent->fps > 1) {
> > +                agent->fps--;
> > +                spice_debug("stream %d: fps--%u", index, agent->fps);
> > +            }
> > +        }
> > +        agent->frames = 1;
> > +        agent->drops = 0;
> > +    }
> > +}
> > +
> > +static Stream *display_channel_stream_try_new(DisplayChannel *display)
> > +{
> > +    Stream *stream;
> > +    if (!display->free_streams) {
> > +        return NULL;
> > +    }
> > +    stream = display->free_streams;
> > +    display->free_streams = display->free_streams->next;
> > +    return stream;
> > +}
> > +
> > +static void display_channel_create_stream(DisplayChannel *display, Drawable
> > *drawable)
> > +{
> > +    DisplayChannelClient *dcc;
> > +    RingItem *dcc_ring_item, *next;
> > +    Stream *stream;
> > +    SpiceRect* src_rect;
> > +
> > +    spice_assert(!drawable->stream);
> > +
> > +    if (!(stream = display_channel_stream_try_new(display))) {
> > +        return;
> > +    }
> > +
> > +    spice_assert(drawable->red_drawable->type == QXL_DRAW_COPY);
> > +    src_rect = &drawable->red_drawable->u.copy.src_area;
> > +
> > +    ring_add(&display->streams, &stream->link);
> > +    stream->current = drawable;
> > +    stream->last_time = drawable->creation_time;
> > +    stream->width = src_rect->right - src_rect->left;
> > +    stream->height = src_rect->bottom - src_rect->top;
> > +    stream->dest_area = drawable->red_drawable->bbox;
> > +    stream->refs = 1;
> > +    SpiceBitmap *bitmap = &drawable->red_drawable->u.copy.src_bitmap
> > ->u.bitmap;
> > +    stream->top_down = !!(bitmap->flags & SPICE_BITMAP_FLAGS_TOP_DOWN);
> > +    drawable->stream = stream;
> > +    stream->input_fps = MAX_FPS;
> > +    stream->num_input_frames = 0;
> > +    stream->input_fps_start_time = drawable->creation_time;
> > +    display->streams_size_total += stream->width * stream->height;
> > +    display->stream_count++;
> > +    FOREACH_DCC(display, dcc_ring_item, next, dcc) {
> > +        dcc_create_stream(dcc, stream);
> > +    }
> > +    spice_debug("stream %d %dx%d (%d, %d) (%d, %d)",
> > +                (int)(stream - display->streams_buf), stream->width,
> > +                stream->height, stream->dest_area.left, stream
> > ->dest_area.top,
> > +                stream->dest_area.right, stream->dest_area.bottom);
> > +    return;
> > +}
> > +
> > +// returns whether a stream was created
> > +static int stream_add_frame(DisplayChannel *display,
> > +                            Drawable *frame_drawable,
> > +                            int frames_count,
> > +                            int gradual_frames_count,
> > +                            int last_gradual_frame)
> > +{
> > +    update_copy_graduality(frame_drawable);
> > +    frame_drawable->frames_count = frames_count + 1;
> > +    frame_drawable->gradual_frames_count  = gradual_frames_count;
> > +
> > +    if (frame_drawable->copy_bitmap_graduality != BITMAP_GRADUAL_LOW) {
> > +        if ((frame_drawable->frames_count - last_gradual_frame) >
> > +            RED_STREAM_FRAMES_RESET_CONDITION) {
> > +            frame_drawable->frames_count = 1;
> > +            frame_drawable->gradual_frames_count = 1;
> > +        } else {
> > +            frame_drawable->gradual_frames_count++;
> > +        }
> > +
> > +        frame_drawable->last_gradual_frame = frame_drawable->frames_count;
> > +    } else {
> > +        frame_drawable->last_gradual_frame = last_gradual_frame;
> > +    }
> > +
> > +    if (is_stream_start(frame_drawable)) {
> > +        display_channel_create_stream(display, frame_drawable);
> > +        return TRUE;
> > +    }
> > +    return FALSE;
> > +}
> > +
> > +/* TODO: document the difference between the 2 functions below */
> > +void stream_trace_update(DisplayChannel *display, Drawable *drawable)
> > +{
> > +    ItemTrace *trace;
> > +    ItemTrace *trace_end;
> > +    RingItem *item;
> > +
> > +    if (drawable->stream || !drawable->streamable || drawable-
> > >frames_count)
> > {
> > +        return;
> > +    }
> > +
> > +    FOREACH_STREAMS(display, item) {
> > +        Stream *stream = SPICE_CONTAINEROF(item, Stream, link);
> > +        int is_next_frame = is_next_stream_frame(display,
> > +                                                 drawable,
> > +                                                 stream->width,
> > +                                                 stream->height,
> > +                                                 &stream->dest_area,
> > +                                                 stream->last_time,
> > +                                                 stream,
> > +                                                 TRUE);
> > +        if (is_next_frame != STREAM_FRAME_NONE) {
> > +            if (stream->current) {
> > +                stream->current->streamable = FALSE; //prevent item trace
> > +                before_reattach_stream(display, stream, drawable);
> > +                detach_stream(display, stream, FALSE);
> > +            }
> > +            attach_stream(display, drawable, stream);
> > +            if (is_next_frame == STREAM_FRAME_CONTAINER) {
> > +                drawable->sized_stream = stream;
> > +            }
> > +            return;
> > +        }
> > +    }
> > +
> > +    trace = display->items_trace;
> > +    trace_end = trace + NUM_TRACE_ITEMS;
> > +    for (; trace < trace_end; trace++) {
> > +        if (is_next_stream_frame(display, drawable, trace->width, trace
> > ->height,
> > +                                       &trace->dest_area, trace->time,
> > NULL,
> > FALSE) !=
> > +                                       STREAM_FRAME_NONE) {
> > +            if (stream_add_frame(display, drawable,
> > +                                 trace->frames_count,
> > +                                 trace->gradual_frames_count,
> > +                                 trace->last_gradual_frame)) {
> > +                return;
> > +            }
> > +        }
> > +    }
> > +}
> > +
> > +void stream_maintenance(DisplayChannel *display,
> > +                        Drawable *candidate, Drawable *prev)
> > +{
> > +    int is_next_frame;
> > +
> > +    if (candidate->stream) {
> > +        return;
> > +    }
> > +
> > +    if (prev->stream) {
> > +        Stream *stream = prev->stream;
> > +
> > +        is_next_frame = is_next_stream_frame(display, candidate,
> > +                                             stream->width, stream->height,
> > +                                             &stream->dest_area, stream
> > ->last_time,
> > +                                             stream, TRUE);
> > +        if (is_next_frame != STREAM_FRAME_NONE) {
> > +            before_reattach_stream(display, stream, candidate);
> > +            detach_stream(display, stream, FALSE);
> > +            prev->streamable = FALSE; //prevent item trace
> > +            attach_stream(display, candidate, stream);
> > +            if (is_next_frame == STREAM_FRAME_CONTAINER) {
> > +                candidate->sized_stream = stream;
> > +            }
> > +        }
> > +    } else if (candidate->streamable) {
> > +        SpiceRect* prev_src = &prev->red_drawable->u.copy.src_area;
> > +
> > +        is_next_frame =
> > +            is_next_stream_frame(display, candidate, prev_src->right -
> > prev_src->left,
> > +                                 prev_src->bottom - prev_src->top,
> > +                                 &prev->red_drawable->bbox, prev
> > ->creation_time,
> > +                                 prev->stream,
> > +                                 FALSE);
> > +        if (is_next_frame != STREAM_FRAME_NONE) {
> > +            stream_add_frame(display, candidate,
> > +                             prev->frames_count,
> > +                             prev->gradual_frames_count,
> > +                             prev->last_gradual_frame);
> > +        }
> > +    }
> > +}
> > diff --git a/server/stream.h b/server/stream.h
> > index 4704937..bf78137 100644
> > --- a/server/stream.h
> > +++ b/server/stream.h
> > @@ -39,6 +39,7 @@
> >  #define RED_STREAM_CLIENT_REPORT_TIMEOUT 1000 // milliseconds
> >  #define RED_STREAM_DEFAULT_HIGH_START_BIT_RATE (10 * 1024 * 1024) // 10Mbps
> >  #define RED_STREAM_DEFAULT_LOW_START_BIT_RATE (2.5 * 1024 * 1024) //
> > 2.5Mbps
> > +#define MAX_FPS 30
> >  
> >  /* move back to display_channel once struct private */
> >  typedef struct DisplayChannel DisplayChannel;
> > @@ -143,5 +144,13 @@
> > void                  stream_unref                       
> >            (DisplayChan
> >  void                  stream_agent_unref                           
> >  (DisplayChannel *display,
> >                                                                      
> >  StreamAgent *agent);
> >  void                  stream_agent_stats_print                     
> >  (StreamAgent *agent);
> > +void                  stream_trace_update                          
> >  (DisplayChannel *display,
> > +                                                                     Drawab
> > le
> > *drawable);
> > +void                  stream_maintenance                           
> >  (DisplayChannel *display,
> > +                                                                     Drawab
> > le
> > *candidate,
> > +                                                                     Drawab
> > le
> > *prev);
> > +
> > +void attach_stream(DisplayChannel *display, Drawable *drawable, Stream
> > *stream);
> > +void detach_stream(DisplayChannel *display, Stream *stream, int
> > detach_sized);
> >  
> >  #endif /* STREAM_H */
> > diff --git a/server/tree.c b/server/tree.c
> > index ad31f09..1daa90c 100644
> > --- a/server/tree.c
> > +++ b/server/tree.c
> > @@ -250,3 +250,53 @@ void container_cleanup(Container *container)
> >          container = next;
> >      }
> >  }
> > +
> > +/* FIXME: document weird function: go down containers, and return drawable
> > ->shadow? */
> > +Shadow* tree_item_find_shadow(TreeItem *item)
> > +{
> > +    while (item->type == TREE_ITEM_TYPE_CONTAINER) {
> > +        if (!(item = (TreeItem *)ring_get_tail(&((Container *)item)-
> > >items)))
> > {
> > +            return NULL;
> > +        }
> > +    }
> > +
> > +    if (item->type != TREE_ITEM_TYPE_DRAWABLE) {
> > +        return NULL;
> > +    }
> > +
> > +    return ((DrawItem *)item)->shadow;
> > +}
> > +
> > +Ring *tree_item_container_items(TreeItem *item, Ring *ring)
> > +{
> > +    return (item->container) ? &item->container->items : ring;
> > +}
> > +
> > +int tree_item_contained_by(TreeItem *item, Ring *ring)
> > +{
> > +    spice_assert(item && ring);
> > +    do {
> > +        Ring *now = tree_item_container_items(item, ring);
> > +        if (now == ring) {
> > +            return TRUE;
> > +        }
> > +    } while ((item = (TreeItem *)item->container));
> > +
> > +    return FALSE;
> > +}
> > +
> > +void draw_item_remove_shadow(DrawItem *item)
> > +{
> > +    Shadow *shadow;
> > +
> > +    if (!item->shadow) {
> > +        return;
> > +    }
> > +    shadow = item->shadow;
> > +    item->shadow = NULL;
> > +    /* shadow_free? */
> > +    ring_remove(&shadow->base.siblings_link);
> > +    region_destroy(&shadow->base.rgn);
> > +    region_destroy(&shadow->on_hold);
> > +    free(shadow);
> > +}
> > diff --git a/server/tree.h b/server/tree.h
> > index 01d4ff9..8b9c6ba 100644
> > --- a/server/tree.h
> > +++ b/server/tree.h
> > @@ -80,6 +80,11 @@ static inline int is_opaque_item(TreeItem *item)
> >  }
> >  
> >  void       tree_item_dump                           (TreeItem *item);
> > +Shadow*    tree_item_find_shadow                    (TreeItem *item);
> > +int        tree_item_contained_by                   (TreeItem *item, Ring
> > *ring);
> > +Ring*      tree_item_container_items                (TreeItem *item, Ring
> > *ring);
> > +
> > +void       draw_item_remove_shadow                  (DrawItem *item);
> >  Shadow*    shadow_new                               (DrawItem *item, const
> > SpicePoint *delta);
> >  Container* container_new                            (DrawItem *item);
> >  void       container_free                           (Container *container);
> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/spice-devel


More information about the Spice-devel mailing list