[Spice-commits] 2 commits - server/display-channel.c server/display-channel.h server/red_worker.c server/red_worker.h

Frediano Ziglio fziglio at kemper.freedesktop.org
Wed Nov 18 07:34:35 PST 2015


 server/display-channel.c |  547 ++++++++++++++++++++++++++++++++++++++++++
 server/display-channel.h |   19 +
 server/red_worker.c      |  598 ++---------------------------------------------
 server/red_worker.h      |    1 
 4 files changed, 601 insertions(+), 564 deletions(-)

New commits:
commit d9a488a62f2acfbf3b9d80ddffacc9805b5627e6
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Fri Nov 13 10:29:16 2015 -0600

    worker: painfully move display_channel_add_drawable
    
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>
    Acked-by: Frediano Ziglio <fziglio at redhat.com>

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

    worker: add compression parameters to dcc
    
    This allow different dcc to have different settings from default one.
    The parameters are copied initially from default settings but then they
    can change independently for each client.
    Even having a single client a future client is not affected by a
    previous setting on the old dcc.
    
    Signed-off-by: Marc-André Lureau <marcandre.lureau at gmail.com>
    [updated for the preferred compression]
    Signed-off-by: Pavel Grunt <pgrunt at redhat.com>
    Acked-by: Frediano Ziglio <fziglio at redhat.com>

diff --git a/server/display-channel.c b/server/display-channel.c
index 222b2e3..04667e8 100644
--- a/server/display-channel.c
+++ b/server/display-channel.c
@@ -124,7 +124,11 @@ DisplayChannelClient *dcc_new(DisplayChannel *display,
                               RedClient *client, RedsStream *stream,
                               int mig_target,
                               uint32_t *common_caps, int num_common_caps,
-                              uint32_t *caps, int num_caps)
+                              uint32_t *caps, int num_caps,
+                              SpiceImageCompression image_compression,
+                              spice_wan_compression_t jpeg_state,
+                              spice_wan_compression_t zlib_glz_state)
+
 {
     DisplayChannelClient *dcc;
 
@@ -137,6 +141,9 @@ DisplayChannelClient *dcc_new(DisplayChannel *display,
 
     ring_init(&dcc->palette_cache_lru);
     dcc->palette_cache_available = CLIENT_PALETTE_CACHE_SIZE;
+    dcc->image_compression = image_compression;
+    dcc->jpeg_state = jpeg_state;
+    dcc->zlib_glz_state = zlib_glz_state;
 
     return dcc;
 }
diff --git a/server/display-channel.h b/server/display-channel.h
index 5e11d0e..9c8f137 100644
--- a/server/display-channel.h
+++ b/server/display-channel.h
@@ -172,6 +172,9 @@ struct Drawable {
 
 struct DisplayChannelClient {
     CommonChannelClient common;
+    SpiceImageCompression image_compression;
+    spice_wan_compression_t jpeg_state;
+    spice_wan_compression_t zlib_glz_state;
 
     int expect_init;
 
@@ -242,7 +245,10 @@ DisplayChannelClient*      dcc_new                                   (DisplayCha
                                                                       uint32_t *common_caps,
                                                                       int num_common_caps,
                                                                       uint32_t *caps,
-                                                                      int num_caps);
+                                                                      int num_caps,
+                                                                      SpiceImageCompression image_compression,
+                                                                      spice_wan_compression_t jpeg_state,
+                                                                      spice_wan_compression_t zlib_glz_state);
 void                       dcc_push_monitors_config                  (DisplayChannelClient *dcc);
 void                       dcc_push_destroy_surface                  (DisplayChannelClient *dcc,
                                                                       uint32_t surface_id);
diff --git a/server/red_worker.c b/server/red_worker.c
index 9837e09..9db8d2a 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -3814,8 +3814,7 @@ static inline int red_compress_image(DisplayChannelClient *dcc,
                                      compress_send_data_t* o_comp_data)
 {
     DisplayChannel *display_channel = DCC_TO_DC(dcc);
-    SpiceImageCompression image_compression =
-        display_channel->common.worker->image_compression;
+    SpiceImageCompression image_compression = dcc->image_compression;
     int quic_compress = FALSE;
 
     if ((image_compression == SPICE_IMAGE_COMPRESSION_OFF) ||
@@ -4219,15 +4218,14 @@ static void fill_mask(RedChannelClient *rcc, SpiceMarshaller *m,
                       SpiceImage *mask_bitmap, Drawable *drawable)
 {
     DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
-    DisplayChannel *display = DCC_TO_DC(dcc);
 
     if (mask_bitmap && m) {
-        if (display->common.worker->image_compression != SPICE_IMAGE_COMPRESSION_OFF) {
-            SpiceImageCompression save_img_comp =
-                display->common.worker->image_compression;
-            display->common.worker->image_compression = SPICE_IMAGE_COMPRESSION_OFF;
+        if (dcc->image_compression != SPICE_IMAGE_COMPRESSION_OFF) {
+            /* todo: pass compression argument */
+            SpiceImageCompression save_img_comp = dcc->image_compression;
+            dcc->image_compression = SPICE_IMAGE_COMPRESSION_OFF;
             fill_bits(dcc, m, mask_bitmap, drawable, FALSE);
-            display->common.worker->image_compression = save_img_comp;
+            dcc->image_compression = save_img_comp;
         } else {
             fill_bits(dcc, m, mask_bitmap, drawable, FALSE);
         }
@@ -6113,7 +6111,7 @@ static void red_marshall_image(RedChannelClient *rcc, SpiceMarshaller *m, ImageI
 
     compress_send_data_t comp_send_data = {0};
 
-    comp_mode = display_channel->common.worker->image_compression;
+    comp_mode = dcc->image_compression;
 
     if (((comp_mode == SPICE_IMAGE_COMPRESSION_AUTO_LZ) ||
         (comp_mode == SPICE_IMAGE_COMPRESSION_AUTO_GLZ)) && !bitmap_has_extra_stride(&bitmap)) {
@@ -7211,10 +7209,10 @@ static int display_channel_handle_migrate_data(RedChannelClient *rcc, uint32_t s
 
     if (migrate_data->low_bandwidth_setting) {
         red_channel_client_ack_set_client_window(rcc, WIDE_CLIENT_ACK_WINDOW);
-        if (DCC_TO_WORKER(dcc)->jpeg_state == SPICE_WAN_COMPRESSION_AUTO) {
+        if (dcc->jpeg_state == SPICE_WAN_COMPRESSION_AUTO) {
             display_channel->enable_jpeg = TRUE;
         }
-        if (DCC_TO_WORKER(dcc)->zlib_glz_state == SPICE_WAN_COMPRESSION_AUTO) {
+        if (dcc->zlib_glz_state == SPICE_WAN_COMPRESSION_AUTO) {
             display_channel->enable_zlib_glz_wrap = TRUE;
         }
     }
@@ -7282,6 +7280,7 @@ static int display_channel_handle_preferred_compression(DisplayChannelClient *dc
     case SPICE_IMAGE_COMPRESSION_GLZ:
     case SPICE_IMAGE_COMPRESSION_OFF:
         display_channel->common.worker->image_compression = pc->image_compression;
+        dcc->image_compression = pc->image_compression;
         return TRUE;
     default:
         spice_warning("preferred-compression: unsupported image compression setting");
@@ -7752,7 +7751,8 @@ static void handle_new_display_channel(RedWorker *worker, RedClient *client, Red
     display_channel = worker->display_channel;
     spice_info("add display channel client");
     dcc = dcc_new(display_channel, client, stream, migrate,
-                  common_caps, num_common_caps, caps, num_caps);
+                  common_caps, num_common_caps, caps, num_caps,
+                  worker->image_compression, worker->jpeg_state, worker->zlib_glz_state);
     if (!dcc) {
         return;
     }
@@ -7767,19 +7767,19 @@ static void handle_new_display_channel(RedWorker *worker, RedClient *client, Red
                      DISPLAY_FREE_LIST_DEFAULT_SIZE * sizeof(SpiceResourceID));
     dcc->send_data.free_list.res_size = DISPLAY_FREE_LIST_DEFAULT_SIZE;
 
-    if (worker->jpeg_state == SPICE_WAN_COMPRESSION_AUTO) {
+    if (dcc->jpeg_state == SPICE_WAN_COMPRESSION_AUTO) {
         display_channel->enable_jpeg = dcc->common.is_low_bandwidth;
     } else {
-        display_channel->enable_jpeg = (worker->jpeg_state == SPICE_WAN_COMPRESSION_ALWAYS);
+        display_channel->enable_jpeg = (dcc->jpeg_state == SPICE_WAN_COMPRESSION_ALWAYS);
     }
 
     // todo: tune quality according to bandwidth
     display_channel->jpeg_quality = 85;
 
-    if (worker->zlib_glz_state == SPICE_WAN_COMPRESSION_AUTO) {
+    if (dcc->zlib_glz_state == SPICE_WAN_COMPRESSION_AUTO) {
         display_channel->enable_zlib_glz_wrap = dcc->common.is_low_bandwidth;
     } else {
-        display_channel->enable_zlib_glz_wrap = (worker->zlib_glz_state ==
+        display_channel->enable_zlib_glz_wrap = (dcc->zlib_glz_state ==
                                                  SPICE_WAN_COMPRESSION_ALWAYS);
     }
 


More information about the Spice-commits mailing list