summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJonathon Jongsma <jjongsma@redhat.com>2015-11-13 18:23:18 +0100
committerFabiano FidĂȘncio <fidencio@redhat.com>2015-11-13 18:40:41 +0100
commita439c1b50ab984a06aa311a32f0410e00d67c0c4 (patch)
tree7c8bee319492b517897bee7661bf33bab3210c35
parentcdb6b92f97679412503f4b726fdddeb892c46f56 (diff)
downloadspice-a439c1b50ab984a06aa311a32f0410e00d67c0c4.tar.gz
spice-a439c1b50ab984a06aa311a32f0410e00d67c0c4.tar.xz
spice-a439c1b50ab984a06aa311a32f0410e00d67c0c4.zip
worker: painfully move display_channel_add_drawablejjongsma_rebase
-rw-r--r--server/display-channel.c526
-rw-r--r--server/display-channel.h16
-rw-r--r--server/red_worker.c558
3 files changed, 553 insertions, 547 deletions
diff --git a/server/display-channel.c b/server/display-channel.c
index 63d56b46..e0688155 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);
@@ -320,3 +335,514 @@ void display_channel_set_stream_video(DisplayChannel *display, int stream_video)
display->stream_video = stream_video;
}
+
+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 f311d888..71d9453c 100644
--- a/server/display-channel.h
+++ b/server/display-channel.h
@@ -332,12 +332,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;
@@ -371,6 +371,9 @@ int display_channel_get_streams_timeout (DisplayCha
void display_channel_compress_stats_print (const DisplayChannel *display);
void display_channel_compress_stats_reset (DisplayChannel *display);
void display_channel_drawable_unref (DisplayChannel *display, Drawable *drawable);
+int display_channel_add_drawable (DisplayChannel *display,
+ Drawable *drawable);
+
static inline int is_equal_path(SpicePath *path1, SpicePath *path2)
{
@@ -487,4 +490,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 7560b9d0..e1036d47 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -593,7 +593,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;
@@ -602,7 +602,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(RedWorker *worker, Drawable *drawable)
+void red_pipes_add_drawable(RedWorker *worker, Drawable *drawable)
{
DisplayChannelClient *dcc;
RingItem *dcc_ring_item, *next;
@@ -625,8 +625,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(RedWorker *worker,
- Drawable *drawable, Drawable *pos_after)
+void red_pipes_add_drawable_after(RedWorker *worker,
+ Drawable *drawable, Drawable *pos_after)
{
DrawablePipeItem *dpi, *dpi_pos_after;
RingItem *dpi_link, *dpi_next;
@@ -674,7 +674,7 @@ static inline PipeItem *red_pipe_get_tail(DisplayChannelClient *dcc)
static void red_surface_unref(RedWorker *worker, uint32_t surface_id);
-static inline void red_pipes_remove_drawable(Drawable *drawable)
+void red_pipes_remove_drawable(Drawable *drawable)
{
DrawablePipeItem *dpi;
RingItem *item, *next;
@@ -972,10 +972,11 @@ static void red_flush_source_surfaces(RedWorker *worker, Drawable *drawable)
}
}
-static inline void current_remove_drawable(RedWorker *worker, Drawable *item)
+void current_remove_drawable(RedWorker *worker, Drawable *item)
{
DisplayChannel *display = worker->display_channel;
+ /* 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);
@@ -985,13 +986,7 @@ static inline void current_remove_drawable(RedWorker *worker, Drawable *item)
display->current_size--;
}
-static void remove_drawable(RedWorker *worker, Drawable *drawable)
-{
- red_pipes_remove_drawable(drawable);
- current_remove_drawable(worker, drawable);
-}
-
-static inline void current_remove(RedWorker *worker, TreeItem *item)
+void current_remove(RedWorker *worker, TreeItem *item)
{
TreeItem *now = item;
@@ -1000,8 +995,10 @@ static inline void current_remove(RedWorker *worker, 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(worker, SPICE_CONTAINEROF(now, Drawable, tree_item));
+ red_pipes_remove_drawable(drawable);
+ current_remove_drawable(display, drawable);
} else {
Container *container = (Container *)now;
@@ -1131,150 +1128,6 @@ static void red_clear_surface_drawables_from_pipes(RedWorker *worker,
}
}
-static inline void __exclude_region(RedWorker *worker, Ring *ring, TreeItem *item, QRegion *rgn,
- Ring **top_ring, Drawable *frame_candidate)
-{
- QRegion and_rgn;
-#ifdef RED_WORKER_STAT
- 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(worker->display_channel, 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(&worker->display_channel->__exclude_stat, start_time);
-}
-
-static void exclude_region(RedWorker *worker, Ring *ring, RingItem *ring_item, QRegion *rgn,
- TreeItem **last, Drawable *frame_candidate)
-{
-#ifdef RED_WORKER_STAT
- 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(worker, 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(worker, 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(&worker->display_channel->exclude_stat, start_time);
- return;
- }
- }
-
- while ((last && *last == (TreeItem *)ring_item) ||
- !(ring_item = ring_next(ring, ring_item))) {
- if (ring == top_ring) {
- stat_add(&worker->display_channel->exclude_stat, start_time);
- return;
- }
- ring_item = &container->base.siblings_link;
- container = container->base.container;
- ring = (container) ? &container->items : top_ring;
- }
- }
-}
-
-static inline void current_add_drawable(RedWorker *worker, Drawable *drawable, RingItem *pos)
-{
- DisplayChannel *display = worker->display_channel;
- RedSurface *surface;
- uint32_t surface_id = drawable->surface_id;
-
- surface = &worker->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)
{
@@ -1397,7 +1250,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);
@@ -1430,46 +1283,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;
@@ -1706,351 +1519,6 @@ static void dcc_destroy_stream_agents(DisplayChannelClient *dcc)
}
}
-static inline int red_current_add_equal(RedWorker *worker, DrawItem *item, TreeItem *other)
-{
- DisplayChannel *display = worker->display_channel;
- 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(worker->display_channel, drawable, other_drawable);
- current_add_drawable(worker, drawable, &other->siblings_link);
- other_drawable->refs++;
- current_remove_drawable(worker, other_drawable);
- if (add_after) {
- red_pipes_add_drawable_after(worker, drawable, other_drawable);
- } else {
- red_pipes_add_drawable(worker, 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(worker, other_drawable);
-
- /* sending the drawable to clients that already received
- * (or will receive) other_drawable */
- worker_ring_item = ring_get_head(&RED_CHANNEL(worker->display_channel)->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(worker->display_channel)->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(worker->display_channel)->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(worker, drawable, &other->siblings_link);
- remove_drawable(worker, other_drawable);
- red_pipes_add_drawable(worker, drawable);
- return TRUE;
- }
- break;
- case QXL_EFFECT_NOP_ON_DUP:
- if (is_same_drawable(drawable, other_drawable)) {
- return TRUE;
- }
- break;
- }
- return FALSE;
-}
-
-static inline int current_add(RedWorker *worker, Ring *ring, Drawable *drawable)
-{
- DrawItem *item = &drawable->tree_item;
-#ifdef RED_WORKER_STAT
- 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(worker, item, sibling)) {
- stat_add(&worker->display_channel->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(worker, 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(worker, 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(worker, 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(worker, ring, exclude_base, &exclude_rgn, NULL, drawable);
- stream_trace_update(worker->display_channel, drawable);
- streams_update_visible_region(worker->display_channel, 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(worker, 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(worker, drawable, ring);
- if (is_primary_surface(worker->display_channel, drawable->surface_id)) {
- detach_streams_behind(worker->display_channel, &drawable->tree_item.base.rgn, drawable);
- }
- }
- region_destroy(&exclude_rgn);
- stat_add(&worker->display_channel->add_stat, start_time);
- return TRUE;
-}
-
-static inline int current_add_with_shadow(RedWorker *worker, Ring *ring, Drawable *item)
-{
- DisplayChannel *display = worker->display_channel;
-#ifdef RED_WORKER_STAT
- 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(worker, item, ring);
- if (item->tree_item.effect == QXL_EFFECT_OPAQUE) {
- QRegion exclude_rgn;
- region_clone(&exclude_rgn, &item->tree_item.base.rgn);
- exclude_region(worker, 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(RedWorker *worker, Drawable *drawable)
-{
- DisplayChannel *display = worker->display_channel;
- int ret = FALSE, surface_id = drawable->surface_id;
- RedDrawable *red_drawable = drawable->red_drawable;
- Ring *ring = &worker->surfaces[surface_id].current;
-
- if (has_shadow(red_drawable)) {
- ret = current_add_with_shadow(worker, ring, drawable);
- } else {
- drawable_update_streamable(display, drawable);
- ret = current_add(worker, ring, drawable);
- }
-
-#ifdef RED_WORKER_STAT
- if ((++display->add_count % 100) == 0)
- print_stats(display);
-#endif
- return ret;
-}
-
static void red_get_area(RedWorker *worker, int surface_id, const SpiceRect *area, uint8_t *dest,
int dest_stride, int update)
{
@@ -2350,7 +1818,7 @@ static inline void red_process_draw(RedWorker *worker, RedDrawable *red_drawable
goto cleanup;
}
- if (red_add_drawable(worker, drawable)) {
+ if (display_channel_add_drawable(worker->display_channel, drawable)) {
red_pipes_add_drawable(worker, drawable);
}
cleanup: