Frediano, Jonathon ... Pavel's comment[0] was taken into consideration on this version? [0]: http://lists.freedesktop.org/archives/spice-devel/2015-November/023560.html On Mon, Nov 16, 2015 at 12:06 PM, Frediano Ziglio <fziglio@xxxxxxxxxx> wrote: > From: Marc-André Lureau <marcandre.lureau@xxxxxxxxx> > > --- > server/display-channel.c | 526 ++++++++++++++++++++++++++++++++++++++++++++ > server/display-channel.h | 15 +- > server/red_worker.c | 559 ++--------------------------------------------- > server/stream.c | 7 +- > 4 files changed, 556 insertions(+), 551 deletions(-) > > diff --git a/server/display-channel.c b/server/display-channel.c > index 1d5d8d3..baec460 100644 > --- a/server/display-channel.c > +++ b/server/display-channel.c > @@ -20,6 +20,21 @@ > > #include "display-channel.h" > > +static stat_time_t display_channel_stat_now(DisplayChannel *display) > +{ > +#ifdef RED_WORKER_STAT > + RedWorker *worker = COMMON_CHANNEL(display)->worker; > + > + return stat_now(red_worker_get_clockid(worker)); > + > +#else > + return 0; > +#endif > +} > + > +#define stat_start(display, var) \ > + G_GNUC_UNUSED stat_time_t var = display_channel_stat_now((display)); > + > void display_channel_compress_stats_reset(DisplayChannel *display) > { > spice_return_if_fail(display); > @@ -406,3 +421,514 @@ bool display_channel_surface_has_canvas(DisplayChannel *display, > { > return display->surfaces[surface_id].context.canvas != NULL; > } > + > +static void streams_update_visible_region(DisplayChannel *display, Drawable *drawable) > +{ > + Ring *ring; > + RingItem *item; > + RingItem *dcc_ring_item, *next; > + DisplayChannelClient *dcc; > + > + if (!red_channel_is_connected(RED_CHANNEL(display))) { > + return; > + } > + > + if (!is_primary_surface(display, drawable->surface_id)) { > + return; > + } > + > + ring = &display->streams; > + item = ring_get_head(ring); > + > + while (item) { > + Stream *stream = SPICE_CONTAINEROF(item, Stream, link); > + StreamAgent *agent; > + > + item = ring_next(ring, item); > + > + if (stream->current == drawable) { > + continue; > + } > + > + FOREACH_DCC(display, dcc_ring_item, next, dcc) { > + agent = &dcc->stream_agents[get_stream_id(display, stream)]; > + > + if (region_intersects(&agent->vis_region, &drawable->tree_item.base.rgn)) { > + region_exclude(&agent->vis_region, &drawable->tree_item.base.rgn); > + region_exclude(&agent->clip, &drawable->tree_item.base.rgn); > + dcc_add_stream_agent_clip(dcc, agent); > + } > + } > + } > +} > + > + > +static void current_add_drawable(DisplayChannel *display, > + Drawable *drawable, RingItem *pos) > +{ > + RedSurface *surface; > + uint32_t surface_id = drawable->surface_id; > + > + surface = &display->surfaces[surface_id]; > + ring_add_after(&drawable->tree_item.base.siblings_link, pos); > + ring_add(&display->current_list, &drawable->list_link); > + ring_add(&surface->current_list, &drawable->surface_list_link); > + display->current_size++; > + drawable->refs++; > +} > + > +static int current_add_equal(DisplayChannel *display, DrawItem *item, TreeItem *other) > +{ > + DrawItem *other_draw_item; > + Drawable *drawable; > + Drawable *other_drawable; > + > + if (other->type != TREE_ITEM_TYPE_DRAWABLE) { > + return FALSE; > + } > + other_draw_item = (DrawItem *)other; > + > + if (item->shadow || other_draw_item->shadow || item->effect != other_draw_item->effect) { > + return FALSE; > + } > + > + drawable = SPICE_CONTAINEROF(item, Drawable, tree_item); > + other_drawable = SPICE_CONTAINEROF(other_draw_item, Drawable, tree_item); > + > + if (item->effect == QXL_EFFECT_OPAQUE) { > + int add_after = !!other_drawable->stream && > + is_drawable_independent_from_surfaces(drawable); > + stream_maintenance(display, drawable, other_drawable); > + current_add_drawable(display, drawable, &other->siblings_link); > + other_drawable->refs++; > + current_remove_drawable(display, other_drawable); > + if (add_after) { > + red_pipes_add_drawable_after(display, drawable, other_drawable); > + } else { > + red_pipes_add_drawable(display, drawable); > + } > + red_pipes_remove_drawable(other_drawable); > + display_channel_drawable_unref(display, other_drawable); > + return TRUE; > + } > + > + switch (item->effect) { > + case QXL_EFFECT_REVERT_ON_DUP: > + if (is_same_drawable(drawable, other_drawable)) { > + > + DisplayChannelClient *dcc; > + DrawablePipeItem *dpi; > + RingItem *worker_ring_item, *dpi_ring_item; > + > + other_drawable->refs++; > + current_remove_drawable(display, other_drawable); > + > + /* sending the drawable to clients that already received > + * (or will receive) other_drawable */ > + worker_ring_item = ring_get_head(&RED_CHANNEL(display)->clients); > + dpi_ring_item = ring_get_head(&other_drawable->pipes); > + /* dpi contains a sublist of dcc's, ordered the same */ > + while (worker_ring_item) { > + dcc = SPICE_CONTAINEROF(worker_ring_item, DisplayChannelClient, > + common.base.channel_link); > + dpi = SPICE_CONTAINEROF(dpi_ring_item, DrawablePipeItem, base); > + while (worker_ring_item && (!dpi || dcc != dpi->dcc)) { > + dcc_add_drawable(dcc, drawable); > + worker_ring_item = ring_next(&RED_CHANNEL(display)->clients, > + worker_ring_item); > + dcc = SPICE_CONTAINEROF(worker_ring_item, DisplayChannelClient, > + common.base.channel_link); > + } > + > + if (dpi_ring_item) { > + dpi_ring_item = ring_next(&other_drawable->pipes, dpi_ring_item); > + } > + if (worker_ring_item) { > + worker_ring_item = ring_next(&RED_CHANNEL(display)->clients, > + worker_ring_item); > + } > + } > + /* not sending other_drawable where possible */ > + red_pipes_remove_drawable(other_drawable); > + > + display_channel_drawable_unref(display, other_drawable); > + return TRUE; > + } > + break; > + case QXL_EFFECT_OPAQUE_BRUSH: > + if (is_same_geometry(drawable, other_drawable)) { > + current_add_drawable(display, drawable, &other->siblings_link); > + red_pipes_remove_drawable(other_drawable); > + current_remove_drawable(display, other_drawable); > + red_pipes_add_drawable(display, drawable); > + return TRUE; > + } > + break; > + case QXL_EFFECT_NOP_ON_DUP: > + if (is_same_drawable(drawable, other_drawable)) { > + return TRUE; > + } > + break; > + } > + return FALSE; > +} > + > +static void __exclude_region(DisplayChannel *display, Ring *ring, TreeItem *item, QRegion *rgn, > + Ring **top_ring, Drawable *frame_candidate) > +{ > + QRegion and_rgn; > + stat_start(display, start_time); > + > + region_clone(&and_rgn, rgn); > + region_and(&and_rgn, &item->rgn); > + if (!region_is_empty(&and_rgn)) { > + if (IS_DRAW_ITEM(item)) { > + DrawItem *draw = (DrawItem *)item; > + > + if (draw->effect == QXL_EFFECT_OPAQUE) { > + region_exclude(rgn, &and_rgn); > + } > + > + if (draw->shadow) { > + Shadow *shadow; > + int32_t x = item->rgn.extents.x1; > + int32_t y = item->rgn.extents.y1; > + > + region_exclude(&draw->base.rgn, &and_rgn); > + shadow = draw->shadow; > + region_offset(&and_rgn, shadow->base.rgn.extents.x1 - x, > + shadow->base.rgn.extents.y1 - y); > + region_exclude(&shadow->base.rgn, &and_rgn); > + region_and(&and_rgn, &shadow->on_hold); > + if (!region_is_empty(&and_rgn)) { > + region_exclude(&shadow->on_hold, &and_rgn); > + region_or(rgn, &and_rgn); > + // in flat representation of current, shadow is always his owner next > + if (!tree_item_contained_by((TreeItem*)shadow, *top_ring)) { > + *top_ring = tree_item_container_items((TreeItem*)shadow, ring); > + } > + } > + } else { > + if (frame_candidate) { > + Drawable *drawable = SPICE_CONTAINEROF(draw, Drawable, tree_item); > + stream_maintenance(display, frame_candidate, drawable); > + } > + region_exclude(&draw->base.rgn, &and_rgn); > + } > + } else if (item->type == TREE_ITEM_TYPE_CONTAINER) { > + region_exclude(&item->rgn, &and_rgn); > + > + if (region_is_empty(&item->rgn)) { //assume container removal will follow > + Shadow *shadow; > + > + region_exclude(rgn, &and_rgn); > + if ((shadow = tree_item_find_shadow(item))) { > + region_or(rgn, &shadow->on_hold); > + if (!tree_item_contained_by((TreeItem*)shadow, *top_ring)) { > + *top_ring = tree_item_container_items((TreeItem*)shadow, ring); > + } > + } > + } > + } else { > + Shadow *shadow; > + > + spice_assert(item->type == TREE_ITEM_TYPE_SHADOW); > + shadow = (Shadow *)item; > + region_exclude(rgn, &and_rgn); > + region_or(&shadow->on_hold, &and_rgn); > + } > + } > + region_destroy(&and_rgn); > + stat_add(&display->__exclude_stat, start_time); > +} > + > +static void exclude_region(DisplayChannel *display, Ring *ring, RingItem *ring_item, > + QRegion *rgn, TreeItem **last, Drawable *frame_candidate) > +{ > + Ring *top_ring; > + stat_start(display, start_time); > + > + if (!ring_item) { > + return; > + } > + > + top_ring = ring; > + > + for (;;) { > + TreeItem *now = SPICE_CONTAINEROF(ring_item, TreeItem, siblings_link); > + Container *container = now->container; > + > + spice_assert(!region_is_empty(&now->rgn)); > + > + if (region_intersects(rgn, &now->rgn)) { > + __exclude_region(display, ring, now, rgn, &top_ring, frame_candidate); > + > + if (region_is_empty(&now->rgn)) { > + spice_assert(now->type != TREE_ITEM_TYPE_SHADOW); > + ring_item = now->siblings_link.prev; > + current_remove(display, now); > + if (last && *last == now) { > + *last = (TreeItem *)ring_next(ring, ring_item); > + } > + } else if (now->type == TREE_ITEM_TYPE_CONTAINER) { > + Container *container = (Container *)now; > + if ((ring_item = ring_get_head(&container->items))) { > + ring = &container->items; > + spice_assert(((TreeItem *)ring_item)->container); > + continue; > + } > + ring_item = &now->siblings_link; > + } > + > + if (region_is_empty(rgn)) { > + stat_add(&display->exclude_stat, start_time); > + return; > + } > + } > + > + while ((last && *last == (TreeItem *)ring_item) || > + !(ring_item = ring_next(ring, ring_item))) { > + if (ring == top_ring) { > + stat_add(&display->exclude_stat, start_time); > + return; > + } > + ring_item = &container->base.siblings_link; > + container = container->base.container; > + ring = (container) ? &container->items : top_ring; > + } > + } > +} > + > +static int current_add_with_shadow(DisplayChannel *display, Ring *ring, Drawable *item) > +{ > + stat_start(display, start_time); > + ++display->add_with_shadow_count; > + > + RedDrawable *red_drawable = item->red_drawable; > + SpicePoint delta = { > + .x = red_drawable->u.copy_bits.src_pos.x - red_drawable->bbox.left, > + .y = red_drawable->u.copy_bits.src_pos.y - red_drawable->bbox.top > + }; > + > + Shadow *shadow = shadow_new(&item->tree_item, &delta); > + if (!shadow) { > + stat_add(&display->add_stat, start_time); > + return FALSE; > + } > + // item and his shadow must initially be placed in the same container. > + // for now putting them on root. > + > + // only primary surface streams are supported > + if (is_primary_surface(display, item->surface_id)) { > + detach_streams_behind(display, &shadow->base.rgn, NULL); > + } > + > + ring_add(ring, &shadow->base.siblings_link); > + current_add_drawable(display, item, ring); > + if (item->tree_item.effect == QXL_EFFECT_OPAQUE) { > + QRegion exclude_rgn; > + region_clone(&exclude_rgn, &item->tree_item.base.rgn); > + exclude_region(display, ring, &shadow->base.siblings_link, &exclude_rgn, NULL, NULL); > + region_destroy(&exclude_rgn); > + streams_update_visible_region(display, item); > + } else { > + if (is_primary_surface(display, item->surface_id)) { > + detach_streams_behind(display, &item->tree_item.base.rgn, item); > + } > + } > + stat_add(&display->add_stat, start_time); > + return TRUE; > +} > + > +static int current_add(DisplayChannel *display, Ring *ring, Drawable *drawable) > +{ > + DrawItem *item = &drawable->tree_item; > + RingItem *now; > + QRegion exclude_rgn; > + RingItem *exclude_base = NULL; > + stat_start(display, start_time); > + > + spice_assert(!region_is_empty(&item->base.rgn)); > + region_init(&exclude_rgn); > + now = ring_next(ring, ring); > + > + while (now) { > + TreeItem *sibling = SPICE_CONTAINEROF(now, TreeItem, siblings_link); > + int test_res; > + > + if (!region_bounds_intersects(&item->base.rgn, &sibling->rgn)) { > + now = ring_next(ring, now); > + continue; > + } > + test_res = region_test(&item->base.rgn, &sibling->rgn, REGION_TEST_ALL); > + if (!(test_res & REGION_TEST_SHARED)) { > + now = ring_next(ring, now); > + continue; > + } else if (sibling->type != TREE_ITEM_TYPE_SHADOW) { > + if (!(test_res & REGION_TEST_RIGHT_EXCLUSIVE) && > + !(test_res & REGION_TEST_LEFT_EXCLUSIVE) && > + current_add_equal(display, item, sibling)) { > + stat_add(&display->add_stat, start_time); > + return FALSE; > + } > + > + if (!(test_res & REGION_TEST_RIGHT_EXCLUSIVE) && item->effect == QXL_EFFECT_OPAQUE) { > + Shadow *shadow; > + int skip = now == exclude_base; > + > + if ((shadow = tree_item_find_shadow(sibling))) { > + if (exclude_base) { > + TreeItem *next = sibling; > + exclude_region(display, ring, exclude_base, &exclude_rgn, &next, NULL); > + if (next != sibling) { > + now = next ? &next->siblings_link : NULL; > + exclude_base = NULL; > + continue; > + } > + } > + region_or(&exclude_rgn, &shadow->on_hold); > + } > + now = now->prev; > + current_remove(display, sibling); > + now = ring_next(ring, now); > + if (shadow || skip) { > + exclude_base = now; > + } > + continue; > + } > + > + if (!(test_res & REGION_TEST_LEFT_EXCLUSIVE) && is_opaque_item(sibling)) { > + Container *container; > + > + if (exclude_base) { > + exclude_region(display, ring, exclude_base, &exclude_rgn, NULL, NULL); > + region_clear(&exclude_rgn); > + exclude_base = NULL; > + } > + if (sibling->type == TREE_ITEM_TYPE_CONTAINER) { > + container = (Container *)sibling; > + ring = &container->items; > + item->base.container = container; > + now = ring_next(ring, ring); > + continue; > + } > + spice_assert(IS_DRAW_ITEM(sibling)); > + if (!DRAW_ITEM(sibling)->container_root) { > + container = container_new(DRAW_ITEM(sibling)); > + if (!container) { > + spice_warning("create new container failed"); > + region_destroy(&exclude_rgn); > + return FALSE; > + } > + item->base.container = container; > + ring = &container->items; > + } > + } > + } > + if (!exclude_base) { > + exclude_base = now; > + } > + break; > + } > + if (item->effect == QXL_EFFECT_OPAQUE) { > + region_or(&exclude_rgn, &item->base.rgn); > + exclude_region(display, ring, exclude_base, &exclude_rgn, NULL, drawable); > + stream_trace_update(display, drawable); > + streams_update_visible_region(display, drawable); > + /* > + * Performing the insertion after exclude_region for > + * safety (todo: Not sure if exclude_region can affect the drawable > + * if it is added to the tree before calling exclude_region). > + */ > + current_add_drawable(display, drawable, ring); > + } else { > + /* > + * red_detach_streams_behind can affect the current tree since it may > + * trigger calls to update_area. Thus, the drawable should be added to the tree > + * before calling red_detach_streams_behind > + */ > + current_add_drawable(display, drawable, ring); > + if (is_primary_surface(display, drawable->surface_id)) { > + detach_streams_behind(display, &drawable->tree_item.base.rgn, drawable); > + } > + } > + region_destroy(&exclude_rgn); > + stat_add(&display->add_stat, start_time); > + return TRUE; > +} > + > +static bool drawable_can_stream(DisplayChannel *display, Drawable *drawable) > +{ > + RedDrawable *red_drawable = drawable->red_drawable; > + SpiceImage *image; > + > + if (display->stream_video == SPICE_STREAM_VIDEO_OFF) > + return FALSE; > + > + if (!is_primary_surface(display, drawable->surface_id)) > + return FALSE; > + > + if (drawable->tree_item.effect != QXL_EFFECT_OPAQUE || > + red_drawable->type != QXL_DRAW_COPY || > + red_drawable->u.copy.rop_descriptor != SPICE_ROPD_OP_PUT) > + return FALSE; > + > + image = red_drawable->u.copy.src_bitmap; > + if (image == NULL || > + image->descriptor.type != SPICE_IMAGE_TYPE_BITMAP) > + return FALSE; > + > + if (display->stream_video == SPICE_STREAM_VIDEO_FILTER) { > + SpiceRect* rect; > + int size; > + > + rect = &drawable->red_drawable->u.copy.src_area; > + size = (rect->right - rect->left) * (rect->bottom - rect->top); > + if (size < RED_STREAM_MIN_SIZE) > + return FALSE; > + } > + > + return TRUE; > +} > + > +void display_channel_print_stats(DisplayChannel *display) > +{ > +#ifdef RED_WORKER_STAT > + stat_time_t total = display->add_stat.total; > + spice_info("add with shadow count %u", > + display->add_with_shadow_count); > + display->add_with_shadow_count = 0; > + spice_info("add[%u] %f exclude[%u] %f __exclude[%u] %f", > + display->add_stat.count, > + stat_cpu_time_to_sec(total), > + display->exclude_stat.count, > + stat_cpu_time_to_sec(display->exclude_stat.total), > + display->__exclude_stat.count, > + stat_cpu_time_to_sec(display->__exclude_stat.total)); > + spice_info("add %f%% exclude %f%% exclude2 %f%% __exclude %f%%", > + (double)(total - display->exclude_stat.total) / total * 100, > + (double)(display->exclude_stat.total) / total * 100, > + (double)(display->exclude_stat.total - > + display->__exclude_stat.total) / display->exclude_stat.total * 100, > + (double)(display->__exclude_stat.total) / display->exclude_stat.total * 100); > + stat_reset(&display->add_stat); > + stat_reset(&display->exclude_stat); > + stat_reset(&display->__exclude_stat); > +#endif > +} > + > +int display_channel_add_drawable(DisplayChannel *display, Drawable *drawable) > +{ > + int ret = FALSE, surface_id = drawable->surface_id; > + RedDrawable *red_drawable = drawable->red_drawable; > + Ring *ring = &display->surfaces[surface_id].current; > + > + if (has_shadow(red_drawable)) { > + ret = current_add_with_shadow(display, ring, drawable); > + } else { > + drawable->streamable = drawable_can_stream(display, drawable); > + ret = current_add(display, ring, drawable); > + } > + > + return ret; > +} > diff --git a/server/display-channel.h b/server/display-channel.h > index 26b4e4e..a1e5947 100644 > --- a/server/display-channel.h > +++ b/server/display-channel.h > @@ -364,12 +364,12 @@ struct DisplayChannel { > RedCompressBuf *free_compress_bufs; > > /* TODO: some day unify this, make it more runtime.. */ > + uint32_t add_count; > + uint32_t add_with_shadow_count; > #ifdef RED_WORKER_STAT > stat_info_t add_stat; > stat_info_t exclude_stat; > stat_info_t __exclude_stat; > - uint32_t add_count; > - uint32_t add_with_shadow_count; > #endif > #ifdef RED_STATISTICS > uint64_t *cache_hits_counter; > @@ -423,6 +423,8 @@ void display_channel_surface_unref (DisplayCha > bool display_channel_surface_has_canvas (DisplayChannel *display, > uint32_t surface_id); > void display_channel_show_tree (DisplayChannel *display); > +int display_channel_add_drawable (DisplayChannel *display, > + Drawable *drawable); > > static inline int is_equal_path(SpicePath *path1, SpicePath *path2) > { > @@ -539,4 +541,13 @@ static inline void region_add_clip_rects(QRegion *rgn, SpiceClipRects *data) > } > } > > +void red_pipes_add_drawable(DisplayChannel *display, Drawable *drawable); > +void current_remove_drawable(DisplayChannel *display, Drawable *item); > +void red_pipes_add_drawable_after(DisplayChannel *display, > + Drawable *drawable, Drawable *pos_after); > +void red_pipes_remove_drawable(Drawable *drawable); > +void dcc_add_drawable(DisplayChannelClient *dcc, Drawable *drawable); > +void current_remove(DisplayChannel *display, TreeItem *item); > +void detach_streams_behind(DisplayChannel *display, QRegion *region, Drawable *drawable); > + > #endif /* DISPLAY_CHANNEL_H_ */ > diff --git a/server/red_worker.c b/server/red_worker.c > index 6a29e00..7ed195d 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; > @@ -1552,350 +1363,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) > { > @@ -2193,7 +1660,7 @@ static inline void red_process_draw(RedWorker *worker, RedDrawable *red_drawable > goto cleanup; > } > > - if (red_add_drawable(worker->display_channel, drawable)) { > + if (display_channel_add_drawable(worker->display_channel, drawable)) { > red_pipes_add_drawable(worker->display_channel, drawable); > } > cleanup: > diff --git a/server/stream.c b/server/stream.c > index 54f81a8..19ffa70 100644 > --- a/server/stream.c > +++ b/server/stream.c > @@ -152,12 +152,13 @@ static int is_stream_start(Drawable *drawable) > (RED_STREAM_GRADUAL_FRAMES_START_CONDITION * drawable->frames_count))); > } > > -static void update_copy_graduality(DisplayChannel *display, Drawable *drawable) > +static void update_copy_graduality(Drawable *drawable) > { > SpiceBitmap *bitmap; > spice_return_if_fail(drawable->red_drawable->type == QXL_DRAW_COPY); > > - if (display->stream_video != SPICE_STREAM_VIDEO_FILTER) { > + /* TODO: global property -> per dc/dcc */ > + if (streaming_video != SPICE_STREAM_VIDEO_FILTER) { > drawable->copy_bitmap_graduality = BITMAP_GRADUAL_INVALID; > return; > } > @@ -379,7 +380,7 @@ static int stream_add_frame(DisplayChannel *display, > int gradual_frames_count, > int last_gradual_frame) > { > - update_copy_graduality(display, frame_drawable); > + update_copy_graduality(frame_drawable); > frame_drawable->frames_count = frames_count + 1; > frame_drawable->gradual_frames_count = gradual_frames_count; > > -- > 2.4.3 > > _______________________________________________ > Spice-devel mailing list > Spice-devel@xxxxxxxxxxxxxxxxxxxxx > http://lists.freedesktop.org/mailman/listinfo/spice-devel _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel