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

Frediano Ziglio fziglio at redhat.com
Thu Nov 12 06:01:01 PST 2015


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(display, item, sibling)) {
+                stat_add(&display->add_stat, start_time);
+                return FALSE;
+            }
+
+            if (!(test_res & REGION_TEST_RIGHT_EXCLUSIVE) && item->effect == QXL_EFFECT_OPAQUE) {
+                Shadow *shadow;
+                int skip = now == exclude_base;
+
+                if ((shadow = tree_item_find_shadow(sibling))) {
+                    if (exclude_base) {
+                        TreeItem *next = sibling;
+                        exclude_region(display, ring, exclude_base, &exclude_rgn, &next, NULL);
+                        if (next != sibling) {
+                            now = next ? &next->siblings_link : NULL;
+                            exclude_base = NULL;
+                            continue;
+                        }
+                    }
+                    region_or(&exclude_rgn, &shadow->on_hold);
+                }
+                now = now->prev;
+                current_remove(display, sibling);
+                now = ring_next(ring, now);
+                if (shadow || skip) {
+                    exclude_base = now;
+                }
+                continue;
+            }
+
+            if (!(test_res & REGION_TEST_LEFT_EXCLUSIVE) && is_opaque_item(sibling)) {
+                Container *container;
+
+                if (exclude_base) {
+                    exclude_region(display, ring, exclude_base, &exclude_rgn, NULL, NULL);
+                    region_clear(&exclude_rgn);
+                    exclude_base = NULL;
+                }
+                if (sibling->type == TREE_ITEM_TYPE_CONTAINER) {
+                    container = (Container *)sibling;
+                    ring = &container->items;
+                    item->base.container = container;
+                    now = ring_next(ring, ring);
+                    continue;
+                }
+                spice_assert(IS_DRAW_ITEM(sibling));
+                if (!DRAW_ITEM(sibling)->container_root) {
+                    container = container_new(DRAW_ITEM(sibling));
+                    if (!container) {
+                        spice_warning("create new container failed");
+                        region_destroy(&exclude_rgn);
+                        return FALSE;
+                    }
+                    item->base.container = container;
+                    ring = &container->items;
+                }
+            }
+        }
+        if (!exclude_base) {
+            exclude_base = now;
+        }
+        break;
+    }
+    if (item->effect == QXL_EFFECT_OPAQUE) {
+        region_or(&exclude_rgn, &item->base.rgn);
+        exclude_region(display, ring, exclude_base, &exclude_rgn, NULL, drawable);
+        stream_trace_update(display, drawable);
+        streams_update_visible_region(display, drawable);
+        /*
+         * Performing the insertion after exclude_region for
+         * safety (todo: Not sure if exclude_region can affect the drawable
+         * if it is added to the tree before calling exclude_region).
+         */
+        current_add_drawable(display, drawable, ring);
+    } else {
+        /*
+         * red_detach_streams_behind can affect the current tree since it may
+         * trigger calls to update_area. Thus, the drawable should be added to the tree
+         * before calling red_detach_streams_behind
+         */
+        current_add_drawable(display, drawable, ring);
+        if (is_primary_surface(display, drawable->surface_id)) {
+            detach_streams_behind(display, &drawable->tree_item.base.rgn, drawable);
+        }
+    }
+    region_destroy(&exclude_rgn);
+    stat_add(&display->add_stat, start_time);
+    return TRUE;
+}
+
+static bool drawable_can_stream(DisplayChannel *display, Drawable *drawable)
+{
+    RedDrawable *red_drawable = drawable->red_drawable;
+    SpiceImage *image;
+
+    if (display->stream_video == SPICE_STREAM_VIDEO_OFF)
+        return FALSE;
+
+    if (!is_primary_surface(display, drawable->surface_id))
+        return FALSE;
+
+    if (drawable->tree_item.effect != QXL_EFFECT_OPAQUE ||
+        red_drawable->type != QXL_DRAW_COPY ||
+        red_drawable->u.copy.rop_descriptor != SPICE_ROPD_OP_PUT)
+        return FALSE;
+
+    image = red_drawable->u.copy.src_bitmap;
+    if (image == NULL ||
+        image->descriptor.type != SPICE_IMAGE_TYPE_BITMAP)
+        return FALSE;
+
+    if (display->stream_video == SPICE_STREAM_VIDEO_FILTER) {
+        SpiceRect* rect;
+        int size;
+
+        rect = &drawable->red_drawable->u.copy.src_area;
+        size = (rect->right - rect->left) * (rect->bottom - rect->top);
+        if (size < RED_STREAM_MIN_SIZE)
+            return FALSE;
+    }
+
+    return TRUE;
+}
+
+void display_channel_print_stats(DisplayChannel *display)
+{
+#ifdef RED_WORKER_STAT
+    stat_time_t total = display->add_stat.total;
+    spice_info("add with shadow count %u",
+               display->add_with_shadow_count);
+    display->add_with_shadow_count = 0;
+    spice_info("add[%u] %f exclude[%u] %f __exclude[%u] %f",
+               display->add_stat.count,
+               stat_cpu_time_to_sec(total),
+               display->exclude_stat.count,
+               stat_cpu_time_to_sec(display->exclude_stat.total),
+               display->__exclude_stat.count,
+               stat_cpu_time_to_sec(display->__exclude_stat.total));
+    spice_info("add %f%% exclude %f%% exclude2 %f%% __exclude %f%%",
+               (double)(total - display->exclude_stat.total) / total * 100,
+               (double)(display->exclude_stat.total) / total * 100,
+               (double)(display->exclude_stat.total -
+                        display->__exclude_stat.total) / display->exclude_stat.total * 100,
+               (double)(display->__exclude_stat.total) / display->exclude_stat.total * 100);
+    stat_reset(&display->add_stat);
+    stat_reset(&display->exclude_stat);
+    stat_reset(&display->__exclude_stat);
+#endif
+}
+
+int display_channel_add_drawable(DisplayChannel *display, Drawable *drawable)
+{
+    int ret = FALSE, surface_id = drawable->surface_id;
+    RedDrawable *red_drawable = drawable->red_drawable;
+    Ring *ring = &display->surfaces[surface_id].current;
+
+    if (has_shadow(red_drawable)) {
+        ret = current_add_with_shadow(display, ring, drawable);
+    } else {
+        drawable->streamable = drawable_can_stream(display, drawable);
+        ret = current_add(display, ring, drawable);
+    }
+
+    return ret;
+}
diff --git a/server/display-channel.h b/server/display-channel.h
index 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,
+                                                                      Stream *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,
-                                                                      Stream *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)
+{
+    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) {
+        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,
+                                                                     Drawable *drawable);
+void                  stream_maintenance                            (DisplayChannel *display,
+                                                                     Drawable *candidate,
+                                                                     Drawable *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);
-- 
2.4.3



More information about the Spice-devel mailing list