diff options
author | Jonathon Jongsma <jjongsma@redhat.com> | 2015-11-13 18:23:18 +0100 |
---|---|---|
committer | Fabiano FidĂȘncio <fidencio@redhat.com> | 2015-11-13 18:40:41 +0100 |
commit | a439c1b50ab984a06aa311a32f0410e00d67c0c4 (patch) | |
tree | 7c8bee319492b517897bee7661bf33bab3210c35 /server/display-channel.c | |
parent | cdb6b92f97679412503f4b726fdddeb892c46f56 (diff) | |
download | spice-jjongsma_rebase.tar.gz spice-jjongsma_rebase.tar.xz spice-jjongsma_rebase.zip |
worker: painfully move display_channel_add_drawablejjongsma_rebase
Diffstat (limited to 'server/display-channel.c')
-rw-r--r-- | server/display-channel.c | 526 |
1 files changed, 526 insertions, 0 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; +} |