From d9a488a62f2acfbf3b9d80ddffacc9805b5627e6 Mon Sep 17 00:00:00 2001 From: Marc-André Lureau Date: Fri, 13 Nov 2015 10:29:16 -0600 Subject: worker: painfully move display_channel_add_drawable Acked-by: Jonathon Jongsma Acked-by: Frediano Ziglio --- server/display-channel.c | 538 ++++++++++++++++++++++++++++++++++++++++++++ server/display-channel.h | 11 + server/red_worker.c | 566 ++--------------------------------------------- server/red_worker.h | 1 + 4 files changed, 570 insertions(+), 546 deletions(-) diff --git a/server/display-channel.c b/server/display-channel.c index 04667e88..c26b5dde 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 9c8f1371..8deb71cf 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 9db8d2a7..16677fcc 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 2995b8fb..729ce2b8 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, -- cgit