I plan to split this one up a little bit. On Thu, 2015-11-12 at 14:01 +0000, Frediano Ziglio wrote: > From: Marc-André Lureau <marcandre.lureau@xxxxxxxxx> > > --- > server/display-channel.c | 541 ++++++++++++++++++++++++ > server/display-channel.h | 53 ++- > server/red_worker.c | 1054 ++------------------------------------------- > - > server/red_worker.h | 1 + > server/stream.c | 360 ++++++++++++++++ > server/stream.h | 9 + > server/tree.c | 50 +++ > server/tree.h | 5 + > 8 files changed, 1050 insertions(+), 1023 deletions(-) > > diff --git a/server/display-channel.c b/server/display-channel.c > index 824f601..4f6cfc4 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); > @@ -148,6 +163,21 @@ DisplayChannelClient *dcc_new(DisplayChannel *display, > return dcc; > } > > +void dcc_add_stream_agent_clip(DisplayChannelClient* dcc, StreamAgent *agent) > +{ > + StreamClipItem *item = stream_clip_item_new(dcc, agent); > + int n_rects; > + > + item->clip_type = SPICE_CLIP_TYPE_RECTS; > + > + n_rects = pixman_region32_n_rects(&agent->clip); > + item->rects = spice_malloc_n_m(n_rects, sizeof(SpiceRect), > sizeof(SpiceClipRects)); > + item->rects->num_rects = n_rects; > + region_ret_rects(&agent->clip, item->rects->rects, n_rects); > + > + red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), (PipeItem *)item); > +} > + > MonitorsConfig* monitors_config_ref(MonitorsConfig *monitors_config) > { > monitors_config->refs++; > @@ -391,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_return_val_if_fail(!region_is_empty(&item->base.rgn), FALSE); > + region_init(&exclude_rgn); > + now = ring_next(ring, ring); > + > + while (now) { > + TreeItem *sibling = SPICE_CONTAINEROF(now, TreeItem, siblings_link); > + int test_res; > + > + if (!region_bounds_intersects(&item->base.rgn, &sibling->rgn)) { > + now = ring_next(ring, now); > + continue; > + } > + test_res = region_test(&item->base.rgn, &sibling->rgn, > REGION_TEST_ALL); > + if (!(test_res & REGION_TEST_SHARED)) { > + now = ring_next(ring, now); > + continue; > + } else if (sibling->type != TREE_ITEM_TYPE_SHADOW) { > + if (!(test_res & REGION_TEST_RIGHT_EXCLUSIVE) && > + !(test_res & > REGION_TEST_LEFT_EXCLUSIVE) && > + current_add_equal(display, > item, sibling)) { > + stat_add(&display->add_stat, start_time); > + return FALSE; > + } > + > + if (!(test_res & REGION_TEST_RIGHT_EXCLUSIVE) && item->effect == > QXL_EFFECT_OPAQUE) { > + Shadow *shadow; > + int skip = now == exclude_base; > + > + if ((shadow = tree_item_find_shadow(sibling))) { > + if (exclude_base) { > + TreeItem *next = sibling; > + exclude_region(display, ring, exclude_base, > &exclude_rgn, &next, NULL); > + if (next != sibling) { > + now = next ? &next->siblings_link : NULL; > + exclude_base = NULL; > + continue; > + } > + } > + region_or(&exclude_rgn, &shadow->on_hold); > + } > + now = now->prev; > + current_remove(display, sibling); > + now = ring_next(ring, now); > + if (shadow || skip) { > + exclude_base = now; > + } > + continue; > + } > + > + if (!(test_res & REGION_TEST_LEFT_EXCLUSIVE) && > is_opaque_item(sibling)) { > + Container *container; > + > + if (exclude_base) { > + exclude_region(display, ring, exclude_base, &exclude_rgn, > NULL, NULL); > + region_clear(&exclude_rgn); > + exclude_base = NULL; > + } > + if (sibling->type == TREE_ITEM_TYPE_CONTAINER) { > + container = (Container *)sibling; > + ring = &container->items; > + item->base.container = container; > + now = ring_next(ring, ring); > + continue; > + } > + spice_assert(IS_DRAW_ITEM(sibling)); > + if (!DRAW_ITEM(sibling)->container_root) { > + container = container_new(DRAW_ITEM(sibling)); > + if (!container) { > + spice_warning("create new container failed"); > + region_destroy(&exclude_rgn); > + return FALSE; > + } > + item->base.container = container; > + ring = &container->items; > + } > + } > + } > + if (!exclude_base) { > + exclude_base = now; > + } > + break; > + } > + if (item->effect == QXL_EFFECT_OPAQUE) { > + region_or(&exclude_rgn, &item->base.rgn); > + exclude_region(display, ring, exclude_base, &exclude_rgn, NULL, > drawable); > + stream_trace_update(display, drawable); > + streams_update_visible_region(display, drawable); > + /* > + * Performing the insertion after exclude_region for > + * safety (todo: Not sure if exclude_region can affect the drawable > + * if it is added to the tree before calling exclude_region). > + */ > + current_add_drawable(display, drawable, ring); > + } else { > + /* > + * red_detach_streams_behind can affect the current tree since it may > + * trigger calls to update_area. Thus, the drawable should be added > to the tree > + * before calling red_detach_streams_behind > + */ > + current_add_drawable(display, drawable, ring); > + if (is_primary_surface(display, drawable->surface_id)) { > + detach_streams_behind(display, &drawable->tree_item.base.rgn, > drawable); > + } > + } > + region_destroy(&exclude_rgn); > + stat_add(&display->add_stat, start_time); > + return TRUE; > +} > + > +static bool drawable_can_stream(DisplayChannel *display, Drawable *drawable) > +{ > + RedDrawable *red_drawable = drawable->red_drawable; > + SpiceImage *image; > + > + if (display->stream_video == SPICE_STREAM_VIDEO_OFF) > + return FALSE; > + > + if (!is_primary_surface(display, drawable->surface_id)) > + return FALSE; > + > + if (drawable->tree_item.effect != QXL_EFFECT_OPAQUE || > + red_drawable->type != QXL_DRAW_COPY || > + red_drawable->u.copy.rop_descriptor != SPICE_ROPD_OP_PUT) > + return FALSE; > + > + image = red_drawable->u.copy.src_bitmap; > + if (image == NULL || > + image->descriptor.type != SPICE_IMAGE_TYPE_BITMAP) > + return FALSE; > + > + if (display->stream_video == SPICE_STREAM_VIDEO_FILTER) { > + SpiceRect* rect; > + int size; > + > + rect = &drawable->red_drawable->u.copy.src_area; > + size = (rect->right - rect->left) * (rect->bottom - rect->top); > + if (size < RED_STREAM_MIN_SIZE) > + return FALSE; > + } > + > + return TRUE; > +} > + > +void display_channel_print_stats(DisplayChannel *display) > +{ > +#ifdef RED_WORKER_STAT > + stat_time_t total = display->add_stat.total; > + spice_info("add with shadow count %u", > + display->add_with_shadow_count); > + display->add_with_shadow_count = 0; > + spice_info("add[%u] %f exclude[%u] %f __exclude[%u] %f", > + display->add_stat.count, > + stat_cpu_time_to_sec(total), > + display->exclude_stat.count, > + stat_cpu_time_to_sec(display->exclude_stat.total), > + display->__exclude_stat.count, > + stat_cpu_time_to_sec(display->__exclude_stat.total)); > + spice_info("add %f%% exclude %f%% exclude2 %f%% __exclude %f%%", > + (double)(total - display->exclude_stat.total) / total * 100, > + (double)(display->exclude_stat.total) / total * 100, > + (double)(display->exclude_stat.total - > + display->__exclude_stat.total) / display > ->exclude_stat.total * 100, > + (double)(display->__exclude_stat.total) / display > ->exclude_stat.total * 100); > + stat_reset(&display->add_stat); > + stat_reset(&display->exclude_stat); > + stat_reset(&display->__exclude_stat); > +#endif > +} > + > +int display_channel_add_drawable(DisplayChannel *display, Drawable *drawable) > +{ > + int ret = FALSE, surface_id = drawable->surface_id; > + RedDrawable *red_drawable = drawable->red_drawable; > + Ring *ring = &display->surfaces[surface_id].current; > + > + if (has_shadow(red_drawable)) { > + ret = current_add_with_shadow(display, ring, drawable); > + } else { > + drawable->streamable = drawable_can_stream(display, drawable); > + ret = current_add(display, ring, drawable); > + } > + > + return ret; > +} > diff --git a/server/display-channel.h b/server/display-channel.h > index 599cce7..432cc61 100644 > --- a/server/display-channel.h > +++ b/server/display-channel.h > @@ -158,6 +158,11 @@ struct Drawable { > uint32_t process_commands_generation; > }; > > +#define LINK_TO_DPI(ptr) SPICE_CONTAINEROF((ptr), DrawablePipeItem, base) > +#define DRAWABLE_FOREACH_DPI_SAFE(drawable, link, next, dpi) \ > + SAFE_FOREACH(link, next, drawable, &(drawable)->pipes, dpi, > LINK_TO_DPI(link)) > + > + > struct DisplayChannelClient { > CommonChannelClient common; > SpiceImageCompression image_compression; > @@ -242,6 +247,10 @@ DisplayChannelClient* dcc_new > (DisplayCha > void dcc_push_monitors_config > (DisplayChannelClient *dcc); > void dcc_push_destroy_surface > (DisplayChannelClient *dcc, > > uint32_t surface_id); > +void dcc_add_stream_agent_clip > (DisplayChannelClient* dcc, > + > StreamAgent *agent); > +void dcc_create_stream > (DisplayChannelClient *dcc, > + Stream > *stream); > > typedef struct DrawablePipeItem { > RingItem base; /* link for a list of pipe items held by Drawable */ > @@ -347,12 +356,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; > @@ -397,9 +406,6 @@ typedef struct SurfaceCreateItem { > > void display_channel_set_stream_video > (DisplayChannel *display, > int > stream_video); > -void display_channel_attach_stream > (DisplayChannel *display, > - > Drawable *drawable, > - Stream > *stream); > int display_channel_get_streams_timeout > (DisplayChannel *display); > void display_channel_compress_stats_print (const > DisplayChannel *display); > void display_channel_compress_stats_reset > (DisplayChannel *display); > @@ -409,6 +415,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) > { > @@ -491,6 +499,23 @@ static inline int is_same_drawable(Drawable *d1, Drawable > *d2) > } > } > > +static inline int is_drawable_independent_from_surfaces(Drawable *drawable) > +{ > + int x; > + > + for (x = 0; x < 3; ++x) { > + if (drawable->surface_deps[x] != -1) { > + return FALSE; > + } > + } > + return TRUE; > +} > + > +static inline int has_shadow(RedDrawable *drawable) > +{ > + return drawable->type == QXL_COPY_BITS; > +} > + > static inline int is_primary_surface(DisplayChannel *display, uint32_t > surface_id) > { > if (surface_id == 0) { > @@ -499,4 +524,22 @@ static inline int is_primary_surface(DisplayChannel > *display, uint32_t surface_i > return FALSE; > } > > +static inline void region_add_clip_rects(QRegion *rgn, SpiceClipRects *data) > +{ > + int i; > + > + for (i = 0; i < data->num_rects; i++) { > + region_add(rgn, data->rects + i); > + } > +} > + > +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 82d39a9..8aa7e70 100644 > --- a/server/red_worker.c > +++ b/server/red_worker.c > @@ -79,9 +79,6 @@ > > #define DISPLAY_FREE_LIST_DEFAULT_SIZE 128 > > -#define FPS_TEST_INTERVAL 1 > -#define MAX_FPS 30 > - > #define ZLIB_DEFAULT_COMPRESSION_LEVEL 3 > #define MIN_GLZ_SIZE_FOR_ZLIB 100 > > @@ -286,8 +283,6 @@ static void red_draw_drawable(DisplayChannel *display, > Drawable *item); > static void red_update_area(DisplayChannel *display, const SpiceRect *area, > int surface_id); > static void red_update_area_till(DisplayChannel *display, const SpiceRect > *area, int surface_id, > Drawable *last); > -static void detach_stream(DisplayChannel *display, Stream *stream, int > detach_sized); > -static inline void display_channel_stream_maintenance(DisplayChannel > *display, Drawable *candidate, Drawable *sect); > static inline void display_begin_send_message(RedChannelClient *rcc); > static void red_release_glz(DisplayChannelClient *dcc); > static void red_freeze_glz(DisplayChannelClient *dcc); > @@ -305,10 +300,6 @@ static void red_create_surface(DisplayChannel *display, > uint32_t surface_id, uin > uint32_t height, int32_t stride, uint32_t > format, > void *line_0, int data_is_valid, int > send_client); > > -#define LINK_TO_DPI(ptr) SPICE_CONTAINEROF((ptr), DrawablePipeItem, base) > -#define DRAWABLE_FOREACH_DPI_SAFE(drawable, link, next, dpi) \ > - SAFE_FOREACH(link, next, drawable, &(drawable)->pipes, dpi, > LINK_TO_DPI(link)) > - > > #define LINK_TO_GLZ(ptr) SPICE_CONTAINEROF((ptr), RedGlzDrawable, \ > drawable_link) > @@ -326,22 +317,6 @@ static void display_stream_clip_unref(DisplayChannel > *display, StreamClipItem *i > free(item); > } > > -static void dcc_push_stream_agent_clip(DisplayChannelClient* dcc, StreamAgent > *agent) > -{ > - StreamClipItem *item = stream_clip_item_new(dcc, agent); > - int n_rects; > - > - item->clip_type = SPICE_CLIP_TYPE_RECTS; > - > - n_rects = pixman_region32_n_rects(&agent->clip); > - item->rects = spice_malloc_n_m(n_rects, sizeof(SpiceRect), > sizeof(SpiceClipRects)); > - item->rects->num_rects = n_rects; > - region_ret_rects(&agent->clip, item->rects->rects, n_rects); > - > - red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), (PipeItem *)item); > -} > - > - > void attach_stream(DisplayChannel *display, Drawable *drawable, Stream > *stream) > { > DisplayChannelClient *dcc; > @@ -378,7 +353,7 @@ void attach_stream(DisplayChannel *display, Drawable > *drawable, Stream *stream) > if (!region_is_equal(&clip_in_draw_dest, &drawable > ->tree_item.base.rgn)) { > region_remove(&agent->clip, &drawable->red_drawable->bbox); > region_or(&agent->clip, &drawable->tree_item.base.rgn); > - dcc_push_stream_agent_clip(dcc, agent); > + dcc_add_stream_agent_clip(dcc, agent); > } > #ifdef STREAM_STATS > agent->stats.num_input_frames++; > @@ -517,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; > > @@ -526,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; > @@ -549,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; > @@ -596,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; > @@ -803,21 +778,6 @@ void display_channel_drawable_unref(DisplayChannel > *display, Drawable *drawable) > display->drawable_count--; > } > > -static inline void remove_shadow(DrawItem *item) > -{ > - Shadow *shadow; > - > - if (!item->shadow) { > - return; > - } > - shadow = item->shadow; > - item->shadow = NULL; > - ring_remove(&shadow->base.siblings_link); > - region_destroy(&shadow->base.rgn); > - region_destroy(&shadow->on_hold); > - free(shadow); > -} > - > static void display_stream_trace_add_drawable(DisplayChannel *display, > Drawable *item) > { > ItemTrace *trace; > @@ -856,10 +816,11 @@ 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); > - remove_shadow(&item->tree_item); > + draw_item_remove_shadow(&item->tree_item); > ring_remove(&item->tree_item.base.siblings_link); > ring_remove(&item->list_link); > ring_remove(&item->surface_list_link); > @@ -867,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; > > @@ -883,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; > > @@ -909,7 +866,7 @@ static inline void current_remove(DisplayChannel *display, > TreeItem *item) > } > } > > -static void current_clear(DisplayChannel *display, int surface_id) > +static void current_remove_all(DisplayChannel *display, int surface_id) > { > Ring *ring = &display->surfaces[surface_id].current; > RingItem *ring_item; > @@ -1015,187 +972,8 @@ static void > red_clear_surface_drawables_from_pipes(DisplayChannel *display, > } > } > > -static inline Shadow *__find_shadow(TreeItem *item) > -{ > - while (item->type == TREE_ITEM_TYPE_CONTAINER) { > - if (!(item = (TreeItem *)ring_get_tail(&((Container *)item)->items))) > { > - return NULL; > - } > - } > - > - if (item->type != TREE_ITEM_TYPE_DRAWABLE) { > - return NULL; > - } > - > - return ((DrawItem *)item)->shadow; > -} > - > -static inline Ring *ring_of(Ring *ring, TreeItem *item) > -{ > - return (item->container) ? &item->container->items : ring; > -} > - > -static inline int __contained_by(TreeItem *item, Ring *ring) > -{ > - spice_assert(item && ring); > - do { > - Ring *now = ring_of(ring, item); > - if (now == ring) { > - return TRUE; > - } > - } while ((item = (TreeItem *)item->container)); > - > - return FALSE; > -} > - > -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 (!__contained_by((TreeItem*)shadow, *top_ring)) { > - *top_ring = ring_of(ring, (TreeItem*)shadow); > - } > - } > - } else { > - if (frame_candidate) { > - Drawable *drawable = SPICE_CONTAINEROF(draw, Drawable, > tree_item); > - display_channel_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 = __find_shadow(item))) { > - region_or(rgn, &shadow->on_hold); > - if (!__contained_by((TreeItem*)shadow, *top_ring)) { > - *top_ring = ring_of(ring, (TreeItem*)shadow); > - } > - } > - } > - } 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++; > -} > - > -static void detach_stream(DisplayChannel *display, Stream *stream, > - int detach_sized) > +void detach_stream(DisplayChannel *display, Stream *stream, > + int detach_sized) > { > spice_assert(stream->current && stream->current->stream); > spice_assert(stream->current->stream == stream); > @@ -1234,7 +1012,7 @@ static void > dcc_detach_stream_gracefully(DisplayChannelClient *dcc, > > /* stopping the client from playing older frames at once*/ > region_clear(&agent->clip); > - dcc_push_stream_agent_clip(dcc, agent); > + dcc_add_stream_agent_clip(dcc, agent); > > if (region_is_empty(&agent->vis_region)) { > spice_debug("stream %d: vis region empty", stream_id); > @@ -1316,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); > @@ -1349,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_push_stream_agent_clip(dcc, agent); > - } > - } > - } > -} > - > static void display_channel_streams_timeout(DisplayChannel *display) > { > Ring *ring = &display->streams; > @@ -1406,17 +1144,6 @@ static void > display_channel_streams_timeout(DisplayChannel *display) > } > } > > -static Stream *display_channel_stream_try_new(DisplayChannel *display) > -{ > - Stream *stream; > - if (!display->free_streams) { > - return NULL; > - } > - stream = display->free_streams; > - display->free_streams = display->free_streams->next; > - return stream; > -} > - > static uint64_t red_stream_get_initial_bit_rate(DisplayChannelClient *dcc, > Stream *stream) > { > @@ -1543,7 +1270,7 @@ static void > red_stream_update_client_playback_latency(void *opaque, uint32_t del > main_dispatcher_set_mm_time_latency(RED_CHANNEL_CLIENT(agent->dcc) > ->client, agent->dcc->streams_max_latency); > } > > -static void dcc_create_stream(DisplayChannelClient *dcc, Stream *stream) > +void dcc_create_stream(DisplayChannelClient *dcc, Stream *stream) > { > StreamAgent *agent = &dcc->stream_agents[get_stream_id(DCC_TO_DC(dcc), > stream)]; > > @@ -1592,47 +1319,6 @@ static void dcc_create_stream(DisplayChannelClient > *dcc, Stream *stream) > #endif > } > > -static void display_channel_create_stream(DisplayChannel *display, Drawable > *drawable) > -{ > - DisplayChannelClient *dcc; > - RingItem *dcc_ring_item, *next; > - Stream *stream; > - SpiceRect* src_rect; > - > - spice_assert(!drawable->stream); > - > - if (!(stream = display_channel_stream_try_new(display))) { > - return; > - } > - > - spice_assert(drawable->red_drawable->type == QXL_DRAW_COPY); > - src_rect = &drawable->red_drawable->u.copy.src_area; > - > - ring_add(&display->streams, &stream->link); > - stream->current = drawable; > - stream->last_time = drawable->creation_time; > - stream->width = src_rect->right - src_rect->left; > - stream->height = src_rect->bottom - src_rect->top; > - stream->dest_area = drawable->red_drawable->bbox; > - stream->refs = 1; > - SpiceBitmap *bitmap = &drawable->red_drawable->u.copy.src_bitmap > ->u.bitmap; > - stream->top_down = !!(bitmap->flags & SPICE_BITMAP_FLAGS_TOP_DOWN); > - drawable->stream = stream; > - stream->input_fps = MAX_FPS; > - stream->num_input_frames = 0; > - stream->input_fps_start_time = drawable->creation_time; > - display->streams_size_total += stream->width * stream->height; > - display->stream_count++; > - FOREACH_DCC(display, dcc_ring_item, next, dcc) { > - dcc_create_stream(dcc, stream); > - } > - spice_debug("stream %d %dx%d (%d, %d) (%d, %d)", > - (int)(stream - display->streams_buf), stream->width, > - stream->height, stream->dest_area.left, stream > ->dest_area.top, > - stream->dest_area.right, stream->dest_area.bottom); > - return; > -} > - > static void dcc_create_all_streams(DisplayChannelClient *dcc) > { > Ring *ring = &DCC_TO_DC(dcc)->streams; > @@ -1677,681 +1363,6 @@ static void > dcc_destroy_stream_agents(DisplayChannelClient *dcc) > } > } > > -static int is_next_stream_frame(DisplayChannel *display, > - const Drawable *candidate, > - const int other_src_width, > - const int other_src_height, > - const SpiceRect *other_dest, > - const red_time_t other_time, > - const Stream *stream, > - int container_candidate_allowed) > -{ > - RedDrawable *red_drawable; > - int is_frame_container = FALSE; > - > - if (!candidate->streamable) { > - return STREAM_FRAME_NONE; > - } > - > - if (candidate->creation_time - other_time > > - (stream ? RED_STREAM_CONTINUS_MAX_DELTA : > RED_STREAM_DETACTION_MAX_DELTA)) { > - return STREAM_FRAME_NONE; > - } > - > - red_drawable = candidate->red_drawable; > - if (!container_candidate_allowed) { > - SpiceRect* candidate_src; > - > - if (!rect_is_equal(&red_drawable->bbox, other_dest)) { > - return STREAM_FRAME_NONE; > - } > - > - candidate_src = &red_drawable->u.copy.src_area; > - if (candidate_src->right - candidate_src->left != other_src_width || > - candidate_src->bottom - candidate_src->top != other_src_height) { > - return STREAM_FRAME_NONE; > - } > - } else { > - if (rect_contains(&red_drawable->bbox, other_dest)) { > - int candidate_area = rect_get_area(&red_drawable->bbox); > - int other_area = rect_get_area(other_dest); > - /* do not stream drawables that are significantly > - * bigger than the original frame */ > - if (candidate_area > 2 * other_area) { > - spice_debug("too big candidate:"); > - spice_debug("prev box ==>"); > - rect_debug(other_dest); > - spice_debug("new box ==>"); > - rect_debug(&red_drawable->bbox); > - return STREAM_FRAME_NONE; > - } > - > - if (candidate_area > other_area) { > - is_frame_container = TRUE; > - } > - } else { > - return STREAM_FRAME_NONE; > - } > - } > - > - if (stream) { > - SpiceBitmap *bitmap = &red_drawable->u.copy.src_bitmap->u.bitmap; > - if (stream->top_down != !!(bitmap->flags & > SPICE_BITMAP_FLAGS_TOP_DOWN)) { > - return STREAM_FRAME_NONE; > - } > - } > - if (is_frame_container) { > - return STREAM_FRAME_CONTAINER; > - } else { > - return STREAM_FRAME_NATIVE; > - } > -} > - > -static void before_reattach_stream(DisplayChannel *display, > - Stream *stream, Drawable *new_frame) > -{ > - DrawablePipeItem *dpi; > - DisplayChannelClient *dcc; > - int index; > - StreamAgent *agent; > - RingItem *ring_item, *next; > - > - spice_return_if_fail(stream->current); > - > - if (!red_channel_is_connected(RED_CHANNEL(display))) { > - return; > - } > - > - if (new_frame->process_commands_generation == stream->current > ->process_commands_generation) { > - spice_debug("ignoring drop, same process_commands_generation as > previous frame"); > - return; > - } > - > - index = get_stream_id(display, stream); > - DRAWABLE_FOREACH_DPI_SAFE(stream->current, ring_item, next, dpi) { > - dcc = dpi->dcc; > - agent = &dcc->stream_agents[index]; > - > - if (!dcc->use_mjpeg_encoder_rate_control && > - !dcc->common.is_low_bandwidth) { > - continue; > - } > - > - if (pipe_item_is_linked(&dpi->dpi_pipe_item)) { > -#ifdef STREAM_STATS > - agent->stats.num_drops_pipe++; > -#endif > - if (dcc->use_mjpeg_encoder_rate_control) { > - mjpeg_encoder_notify_server_frame_drop(agent->mjpeg_encoder); > - } else { > - ++agent->drops; > - } > - } > - } > - > - > - FOREACH_DCC(display, ring_item, next, dcc) { > - double drop_factor; > - > - agent = &dcc->stream_agents[index]; > - > - if (dcc->use_mjpeg_encoder_rate_control) { > - continue; > - } > - if (agent->frames / agent->fps < FPS_TEST_INTERVAL) { > - agent->frames++; > - continue; > - } > - drop_factor = ((double)agent->frames - (double)agent->drops) / > - (double)agent->frames; > - spice_debug("stream %d: #frames %u #drops %u", index, agent->frames, > agent->drops); > - if (drop_factor == 1) { > - if (agent->fps < MAX_FPS) { > - agent->fps++; > - spice_debug("stream %d: fps++ %u", index, agent->fps); > - } > - } else if (drop_factor < 0.9) { > - if (agent->fps > 1) { > - agent->fps--; > - spice_debug("stream %d: fps--%u", index, agent->fps); > - } > - } > - agent->frames = 1; > - agent->drops = 0; > - } > -} > - > -static void update_copy_graduality(DisplayChannel *display, Drawable > *drawable) > -{ > - SpiceBitmap *bitmap; > - spice_return_if_fail(drawable->red_drawable->type == QXL_DRAW_COPY); > - > - if (display->stream_video != SPICE_STREAM_VIDEO_FILTER) { > - drawable->copy_bitmap_graduality = BITMAP_GRADUAL_INVALID; > - return; > - } > - > - if (drawable->copy_bitmap_graduality != BITMAP_GRADUAL_INVALID) { > - return; // already set > - } > - > - bitmap = &drawable->red_drawable->u.copy.src_bitmap->u.bitmap; > - > - if (!bitmap_fmt_has_graduality(bitmap->format) || > bitmap_has_extra_stride(bitmap) || > - (bitmap->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE)) { > - drawable->copy_bitmap_graduality = BITMAP_GRADUAL_NOT_AVAIL; > - } else { > - drawable->copy_bitmap_graduality = > bitmap_get_graduality_level(bitmap); > - } > -} > - > -static int is_stream_start(Drawable *drawable) > -{ > - return ((drawable->frames_count >= RED_STREAM_FRAMES_START_CONDITION) && > - (drawable->gradual_frames_count >= > - (RED_STREAM_GRADUAL_FRAMES_START_CONDITION * drawable > ->frames_count))); > -} > - > -// returns whether a stream was created > -static int display_channel_stream_add_frame(DisplayChannel *display, > - Drawable *frame_drawable, > - int frames_count, > - int gradual_frames_count, > - int last_gradual_frame) > -{ > - update_copy_graduality(display, frame_drawable); > - frame_drawable->frames_count = frames_count + 1; > - frame_drawable->gradual_frames_count = gradual_frames_count; > - > - if (frame_drawable->copy_bitmap_graduality != BITMAP_GRADUAL_LOW) { > - if ((frame_drawable->frames_count - last_gradual_frame) > > - RED_STREAM_FRAMES_RESET_CONDITION) { > - frame_drawable->frames_count = 1; > - frame_drawable->gradual_frames_count = 1; > - } else { > - frame_drawable->gradual_frames_count++; > - } > - > - frame_drawable->last_gradual_frame = frame_drawable->frames_count; > - } else { > - frame_drawable->last_gradual_frame = last_gradual_frame; > - } > - > - if (is_stream_start(frame_drawable)) { > - display_channel_create_stream(display, frame_drawable); > - return TRUE; > - } > - return FALSE; > -} > - > -static void display_channel_stream_maintenance(DisplayChannel *display, > - Drawable *candidate, Drawable > *prev) > -{ > - int is_next_frame; > - > - if (candidate->stream) { > - return; > - } > - > - if (prev->stream) { > - Stream *stream = prev->stream; > - > - is_next_frame = is_next_stream_frame(display, candidate, > - stream->width, stream->height, > - &stream->dest_area, stream > ->last_time, > - stream, TRUE); > - if (is_next_frame != STREAM_FRAME_NONE) { > - before_reattach_stream(display, stream, candidate); > - detach_stream(display, stream, FALSE); > - prev->streamable = FALSE; //prevent item trace > - attach_stream(display, candidate, stream); > - if (is_next_frame == STREAM_FRAME_CONTAINER) { > - candidate->sized_stream = stream; > - } > - } > - } else if (candidate->streamable) { > - SpiceRect* prev_src = &prev->red_drawable->u.copy.src_area; > - > - is_next_frame = > - is_next_stream_frame(display, candidate, prev_src->right - > prev_src->left, > - prev_src->bottom - prev_src->top, > - &prev->red_drawable->bbox, prev > ->creation_time, > - prev->stream, > - FALSE); > - if (is_next_frame != STREAM_FRAME_NONE) { > - display_channel_stream_add_frame(display, candidate, > - prev->frames_count, > - prev->gradual_frames_count, > - prev->last_gradual_frame); > - } > - } > -} > - > -static inline int is_drawable_independent_from_surfaces(Drawable *drawable) > -{ > - int x; > - > - for (x = 0; x < 3; ++x) { > - if (drawable->surface_deps[x] != -1) { > - return FALSE; > - } > - } > - return TRUE; > -} > - > -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); > - display_channel_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; > -} > - > -#define FOREACH_STREAMS(display, item) \ > - for (item = ring_get_head(&(display)->streams); \ > - item != NULL; \ > - item = ring_next(&(display)->streams, item)) > - > -static void red_use_stream_trace(DisplayChannel *display, Drawable *drawable) > -{ > - ItemTrace *trace; > - ItemTrace *trace_end; > - RingItem *item; > - > - if (drawable->stream || !drawable->streamable || drawable->frames_count) > { > - return; > - } > - > - FOREACH_STREAMS(display, item) { > - Stream *stream = SPICE_CONTAINEROF(item, Stream, link); > - int is_next_frame = is_next_stream_frame(display, > - drawable, > - stream->width, > - stream->height, > - &stream->dest_area, > - stream->last_time, > - stream, > - TRUE); > - if (is_next_frame != STREAM_FRAME_NONE) { > - if (stream->current) { > - stream->current->streamable = FALSE; //prevent item trace > - before_reattach_stream(display, stream, drawable); > - detach_stream(display, stream, FALSE); > - } > - attach_stream(display, drawable, stream); > - if (is_next_frame == STREAM_FRAME_CONTAINER) { > - drawable->sized_stream = stream; > - } > - return; > - } > - } > - > - trace = display->items_trace; > - trace_end = trace + NUM_TRACE_ITEMS; > - for (; trace < trace_end; trace++) { > - if (is_next_stream_frame(display, drawable, trace->width, trace > ->height, > - &trace->dest_area, trace->time, NULL, > FALSE) != > - STREAM_FRAME_NONE) { > - if (display_channel_stream_add_frame(display, drawable, > - trace->frames_count, > - trace->gradual_frames_count, > - trace->last_gradual_frame)) > { > - return; > - } > - } > - } > -} > - > -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_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) && > - > 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 = __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); > - red_use_stream_trace(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 void add_clip_rects(QRegion *rgn, SpiceClipRects *data) > -{ > - int i; > - > - for (i = 0; i < data->num_rects; i++) { > - region_add(rgn, data->rects + i); > - } > -} > - > -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 inline int has_shadow(RedDrawable *drawable) > -{ > - return drawable->type == QXL_COPY_BITS; > -} > - > -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) > { > @@ -2620,7 +1631,7 @@ static inline void red_process_draw(RedWorker *worker, > RedDrawable *red_drawable > QRegion rgn; > > region_init(&rgn); > - add_clip_rects(&rgn, red_drawable->clip.rects); > + region_add_clip_rects(&rgn, red_drawable->clip.rects); > region_and(&drawable->tree_item.base.rgn, &rgn); > region_destroy(&rgn); > } > @@ -2649,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: > @@ -2701,10 +1712,10 @@ static inline void red_process_surface(RedWorker > *worker, RedSurfaceCmd *surface > } > set_surface_release_info(&red_surface->destroy, surface > ->release_info, group_id); > red_handle_depends_on_target_surface(display, surface_id); > - /* note that red_handle_depends_on_target_surface must be called > before current_clear. > + /* note that red_handle_depends_on_target_surface must be called > before current_remove_all. > otherwise "current" will hold items that other drawables may > depend on, and then > - red_current_clear will remove them from the pipe. */ > - current_clear(display, surface_id); > + current_remove_all will remove them from the pipe. */ > + current_remove_all(display, surface_id); > red_clear_surface_drawables_from_pipes(display, surface_id, FALSE); > display_channel_surface_unref(display, surface_id); > break; > @@ -3247,7 +2258,7 @@ static void red_current_flush(DisplayChannel *display, > int surface_id) > while (!ring_is_empty(&display->surfaces[surface_id].current_list)) { > free_one_drawable(display, FALSE); > } > - current_clear(display, surface_id); > + current_remove_all(display, surface_id); > } > > // adding the pipe item after pos. If pos == NULL, adding to head. > @@ -5044,7 +4055,7 @@ static void > surface_lossy_region_update(DisplayChannelClient *dcc, > region_init(&clip_rgn); > region_init(&draw_region); > region_add(&draw_region, &drawable->bbox); > - add_clip_rects(&clip_rgn, drawable->clip.rects); > + region_add_clip_rects(&clip_rgn, drawable->clip.rects); > region_and(&draw_region, &clip_rgn); > if (lossy) { > region_or(surface_lossy_region, &draw_region); > @@ -8590,10 +7601,10 @@ void > display_channel_destroy_surface_wait(DisplayChannel *display, int surface_i > return; > > red_handle_depends_on_target_surface(display, surface_id); > - /* note that red_handle_depends_on_target_surface must be called before > current_clear. > + /* note that red_handle_depends_on_target_surface must be called before > current_remove_all. > otherwise "current" will hold items that other drawables may depend > on, and then > - current_clear will remove them from the pipe. */ > - current_clear(display, surface_id); > + current_remove_all will remove them from the pipe. */ > + current_remove_all(display, surface_id); > red_clear_surface_drawables_from_pipes(display, surface_id, TRUE); > } > > @@ -9627,3 +8638,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 3604dfd..d70e862 100644 > --- a/server/red_worker.h > +++ b/server/red_worker.h > @@ -110,6 +110,7 @@ QXLInstance* red_worker_get_qxl(RedWorker *worker); > RedChannel* red_worker_get_cursor_channel(RedWorker *worker); > RedChannel* red_worker_get_display_channel(RedWorker *worker); > void red_worker_print_stats(RedWorker *worker); > +clockid_t red_worker_get_clockid(RedWorker *worker); > > RedChannel *red_worker_new_channel(RedWorker *worker, int size, > const char *name, > diff --git a/server/stream.c b/server/stream.c > index d61cec1..19ffa70 100644 > --- a/server/stream.c > +++ b/server/stream.c > @@ -21,6 +21,12 @@ > #include "stream.h" > #include "display-channel.h" > > +#define FPS_TEST_INTERVAL 1 > +#define FOREACH_STREAMS(display, item) \ > + for (item = ring_get_head(&(display)->streams); \ > + item != NULL; \ > + item = ring_next(&(display)->streams, item)) > + > void stream_agent_stats_print(StreamAgent *agent) > { > #ifdef STREAM_STATS > @@ -138,3 +144,357 @@ StreamClipItem > *stream_clip_item_new(DisplayChannelClient* dcc, StreamAgent *age > item->refs = 1; > return item; > } > + > +static int is_stream_start(Drawable *drawable) > +{ > + return ((drawable->frames_count >= RED_STREAM_FRAMES_START_CONDITION) && > + (drawable->gradual_frames_count >= > + (RED_STREAM_GRADUAL_FRAMES_START_CONDITION * drawable > ->frames_count))); > +} > + > +static void update_copy_graduality(Drawable *drawable) > +{ > + SpiceBitmap *bitmap; > + spice_return_if_fail(drawable->red_drawable->type == QXL_DRAW_COPY); > + > + /* TODO: global property -> per dc/dcc */ > + if (streaming_video != SPICE_STREAM_VIDEO_FILTER) { > + drawable->copy_bitmap_graduality = BITMAP_GRADUAL_INVALID; > + return; > + } > + > + if (drawable->copy_bitmap_graduality != BITMAP_GRADUAL_INVALID) { > + return; // already set > + } > + > + bitmap = &drawable->red_drawable->u.copy.src_bitmap->u.bitmap; > + > + if (!bitmap_fmt_has_graduality(bitmap->format) || > bitmap_has_extra_stride(bitmap) || > + (bitmap->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE)) { > + drawable->copy_bitmap_graduality = BITMAP_GRADUAL_NOT_AVAIL; > + } else { > + drawable->copy_bitmap_graduality = > bitmap_get_graduality_level(bitmap); > + } > +} > + > +static int is_next_stream_frame(DisplayChannel *display, > + const Drawable *candidate, > + const int other_src_width, > + const int other_src_height, > + const SpiceRect *other_dest, > + const red_time_t other_time, > + const Stream *stream, > + int container_candidate_allowed) > +{ > + RedDrawable *red_drawable; > + int is_frame_container = FALSE; > + > + if (!candidate->streamable) { > + return STREAM_FRAME_NONE; > + } > + > + if (candidate->creation_time - other_time > > + (stream ? RED_STREAM_CONTINUS_MAX_DELTA : > RED_STREAM_DETACTION_MAX_DELTA)) { > + return STREAM_FRAME_NONE; > + } > + > + red_drawable = candidate->red_drawable; > + if (!container_candidate_allowed) { > + SpiceRect* candidate_src; > + > + if (!rect_is_equal(&red_drawable->bbox, other_dest)) { > + return STREAM_FRAME_NONE; > + } > + > + candidate_src = &red_drawable->u.copy.src_area; > + if (candidate_src->right - candidate_src->left != other_src_width || > + candidate_src->bottom - candidate_src->top != other_src_height) { > + return STREAM_FRAME_NONE; > + } > + } else { > + if (rect_contains(&red_drawable->bbox, other_dest)) { > + int candidate_area = rect_get_area(&red_drawable->bbox); > + int other_area = rect_get_area(other_dest); > + /* do not stream drawables that are significantly > + * bigger than the original frame */ > + if (candidate_area > 2 * other_area) { > + spice_debug("too big candidate:"); > + spice_debug("prev box ==>"); > + rect_debug(other_dest); > + spice_debug("new box ==>"); > + rect_debug(&red_drawable->bbox); > + return STREAM_FRAME_NONE; > + } > + > + if (candidate_area > other_area) { > + is_frame_container = TRUE; > + } > + } else { > + return STREAM_FRAME_NONE; > + } > + } > + > + if (stream) { > + SpiceBitmap *bitmap = &red_drawable->u.copy.src_bitmap->u.bitmap; > + if (stream->top_down != !!(bitmap->flags & > SPICE_BITMAP_FLAGS_TOP_DOWN)) { > + return STREAM_FRAME_NONE; > + } > + } > + if (is_frame_container) { > + return STREAM_FRAME_CONTAINER; > + } else { > + return STREAM_FRAME_NATIVE; > + } > +} > + > +static void before_reattach_stream(DisplayChannel *display, > + Stream *stream, Drawable *new_frame) > +{ > + DrawablePipeItem *dpi; > + DisplayChannelClient *dcc; > + int index; > + StreamAgent *agent; > + RingItem *ring_item, *next; > + > + spice_return_if_fail(stream->current); > + > + if (!red_channel_is_connected(RED_CHANNEL(display))) { > + return; > + } > + > + if (new_frame->process_commands_generation == stream->current > ->process_commands_generation) { > + spice_debug("ignoring drop, same process_commands_generation as > previous frame"); > + return; > + } > + > + index = get_stream_id(display, stream); > + DRAWABLE_FOREACH_DPI_SAFE(stream->current, ring_item, next, dpi) { > + dcc = dpi->dcc; > + agent = &dcc->stream_agents[index]; > + > + if (!dcc->use_mjpeg_encoder_rate_control && > + !dcc->common.is_low_bandwidth) { > + continue; > + } > + > + if (pipe_item_is_linked(&dpi->dpi_pipe_item)) { > +#ifdef STREAM_STATS > + agent->stats.num_drops_pipe++; > +#endif > + if (dcc->use_mjpeg_encoder_rate_control) { > + mjpeg_encoder_notify_server_frame_drop(agent->mjpeg_encoder); > + } else { > + ++agent->drops; > + } > + } > + } > + > + > + FOREACH_DCC(display, ring_item, next, dcc) { > + double drop_factor; > + > + agent = &dcc->stream_agents[index]; > + > + if (dcc->use_mjpeg_encoder_rate_control) { > + continue; > + } > + if (agent->frames / agent->fps < FPS_TEST_INTERVAL) { > + agent->frames++; > + continue; > + } > + drop_factor = ((double)agent->frames - (double)agent->drops) / > + (double)agent->frames; > + spice_debug("stream %d: #frames %u #drops %u", index, agent->frames, > agent->drops); > + if (drop_factor == 1) { > + if (agent->fps < MAX_FPS) { > + agent->fps++; > + spice_debug("stream %d: fps++ %u", index, agent->fps); > + } > + } else if (drop_factor < 0.9) { > + if (agent->fps > 1) { > + agent->fps--; > + spice_debug("stream %d: fps--%u", index, agent->fps); > + } > + } > + agent->frames = 1; > + agent->drops = 0; > + } > +} > + > +static Stream *display_channel_stream_try_new(DisplayChannel *display) > +{ > + Stream *stream; > + if (!display->free_streams) { > + return NULL; > + } > + stream = display->free_streams; > + display->free_streams = display->free_streams->next; > + return stream; > +} > + > +static void display_channel_create_stream(DisplayChannel *display, Drawable > *drawable) > +{ > + DisplayChannelClient *dcc; > + RingItem *dcc_ring_item, *next; > + Stream *stream; > + SpiceRect* src_rect; > + > + spice_assert(!drawable->stream); > + > + if (!(stream = display_channel_stream_try_new(display))) { > + return; > + } > + > + spice_assert(drawable->red_drawable->type == QXL_DRAW_COPY); > + src_rect = &drawable->red_drawable->u.copy.src_area; > + > + ring_add(&display->streams, &stream->link); > + stream->current = drawable; > + stream->last_time = drawable->creation_time; > + stream->width = src_rect->right - src_rect->left; > + stream->height = src_rect->bottom - src_rect->top; > + stream->dest_area = drawable->red_drawable->bbox; > + stream->refs = 1; > + SpiceBitmap *bitmap = &drawable->red_drawable->u.copy.src_bitmap > ->u.bitmap; > + stream->top_down = !!(bitmap->flags & SPICE_BITMAP_FLAGS_TOP_DOWN); > + drawable->stream = stream; > + stream->input_fps = MAX_FPS; > + stream->num_input_frames = 0; > + stream->input_fps_start_time = drawable->creation_time; > + display->streams_size_total += stream->width * stream->height; > + display->stream_count++; > + FOREACH_DCC(display, dcc_ring_item, next, dcc) { > + dcc_create_stream(dcc, stream); > + } > + spice_debug("stream %d %dx%d (%d, %d) (%d, %d)", > + (int)(stream - display->streams_buf), stream->width, > + stream->height, stream->dest_area.left, stream > ->dest_area.top, > + stream->dest_area.right, stream->dest_area.bottom); > + return; > +} > + > +// returns whether a stream was created > +static int stream_add_frame(DisplayChannel *display, > + Drawable *frame_drawable, > + int frames_count, > + int gradual_frames_count, > + int last_gradual_frame) > +{ > + update_copy_graduality(frame_drawable); > + frame_drawable->frames_count = frames_count + 1; > + frame_drawable->gradual_frames_count = gradual_frames_count; > + > + if (frame_drawable->copy_bitmap_graduality != BITMAP_GRADUAL_LOW) { > + if ((frame_drawable->frames_count - last_gradual_frame) > > + RED_STREAM_FRAMES_RESET_CONDITION) { > + frame_drawable->frames_count = 1; > + frame_drawable->gradual_frames_count = 1; > + } else { > + frame_drawable->gradual_frames_count++; > + } > + > + frame_drawable->last_gradual_frame = frame_drawable->frames_count; > + } else { > + frame_drawable->last_gradual_frame = last_gradual_frame; > + } > + > + if (is_stream_start(frame_drawable)) { > + display_channel_create_stream(display, frame_drawable); > + return TRUE; > + } > + return FALSE; > +} > + > +/* TODO: document the difference between the 2 functions below */ > +void stream_trace_update(DisplayChannel *display, Drawable *drawable) > +{ > + ItemTrace *trace; > + ItemTrace *trace_end; > + RingItem *item; > + > + if (drawable->stream || !drawable->streamable || drawable->frames_count) > { > + return; > + } > + > + FOREACH_STREAMS(display, item) { > + Stream *stream = SPICE_CONTAINEROF(item, Stream, link); > + int is_next_frame = is_next_stream_frame(display, > + drawable, > + stream->width, > + stream->height, > + &stream->dest_area, > + stream->last_time, > + stream, > + TRUE); > + if (is_next_frame != STREAM_FRAME_NONE) { > + if (stream->current) { > + stream->current->streamable = FALSE; //prevent item trace > + before_reattach_stream(display, stream, drawable); > + detach_stream(display, stream, FALSE); > + } > + attach_stream(display, drawable, stream); > + if (is_next_frame == STREAM_FRAME_CONTAINER) { > + drawable->sized_stream = stream; > + } > + return; > + } > + } > + > + trace = display->items_trace; > + trace_end = trace + NUM_TRACE_ITEMS; > + for (; trace < trace_end; trace++) { > + if (is_next_stream_frame(display, drawable, trace->width, trace > ->height, > + &trace->dest_area, trace->time, NULL, > FALSE) != > + STREAM_FRAME_NONE) { > + if (stream_add_frame(display, drawable, > + trace->frames_count, > + trace->gradual_frames_count, > + trace->last_gradual_frame)) { > + return; > + } > + } > + } > +} > + > +void stream_maintenance(DisplayChannel *display, > + Drawable *candidate, Drawable *prev) > +{ > + int is_next_frame; > + > + if (candidate->stream) { > + return; > + } > + > + if (prev->stream) { > + Stream *stream = prev->stream; > + > + is_next_frame = is_next_stream_frame(display, candidate, > + stream->width, stream->height, > + &stream->dest_area, stream > ->last_time, > + stream, TRUE); > + if (is_next_frame != STREAM_FRAME_NONE) { > + before_reattach_stream(display, stream, candidate); > + detach_stream(display, stream, FALSE); > + prev->streamable = FALSE; //prevent item trace > + attach_stream(display, candidate, stream); > + if (is_next_frame == STREAM_FRAME_CONTAINER) { > + candidate->sized_stream = stream; > + } > + } > + } else if (candidate->streamable) { > + SpiceRect* prev_src = &prev->red_drawable->u.copy.src_area; > + > + is_next_frame = > + is_next_stream_frame(display, candidate, prev_src->right - > prev_src->left, > + prev_src->bottom - prev_src->top, > + &prev->red_drawable->bbox, prev > ->creation_time, > + prev->stream, > + FALSE); > + if (is_next_frame != STREAM_FRAME_NONE) { > + stream_add_frame(display, candidate, > + prev->frames_count, > + prev->gradual_frames_count, > + prev->last_gradual_frame); > + } > + } > +} > diff --git a/server/stream.h b/server/stream.h > index 4704937..bf78137 100644 > --- a/server/stream.h > +++ b/server/stream.h > @@ -39,6 +39,7 @@ > #define RED_STREAM_CLIENT_REPORT_TIMEOUT 1000 // milliseconds > #define RED_STREAM_DEFAULT_HIGH_START_BIT_RATE (10 * 1024 * 1024) // 10Mbps > #define RED_STREAM_DEFAULT_LOW_START_BIT_RATE (2.5 * 1024 * 1024) // 2.5Mbps > +#define MAX_FPS 30 > > /* move back to display_channel once struct private */ > typedef struct DisplayChannel DisplayChannel; > @@ -143,5 +144,13 @@ void stream_unref > (DisplayChan > void stream_agent_unref > (DisplayChannel *display, > > StreamAgent *agent); > void stream_agent_stats_print > (StreamAgent *agent); > +void stream_trace_update > (DisplayChannel *display, > + Drawable > *drawable); > +void stream_maintenance > (DisplayChannel *display, > + Drawable > *candidate, > + Drawable > *prev); > + > +void attach_stream(DisplayChannel *display, Drawable *drawable, Stream > *stream); > +void detach_stream(DisplayChannel *display, Stream *stream, int > detach_sized); > > #endif /* STREAM_H */ > diff --git a/server/tree.c b/server/tree.c > index ad31f09..1daa90c 100644 > --- a/server/tree.c > +++ b/server/tree.c > @@ -250,3 +250,53 @@ void container_cleanup(Container *container) > container = next; > } > } > + > +/* FIXME: document weird function: go down containers, and return drawable > ->shadow? */ > +Shadow* tree_item_find_shadow(TreeItem *item) > +{ > + while (item->type == TREE_ITEM_TYPE_CONTAINER) { > + if (!(item = (TreeItem *)ring_get_tail(&((Container *)item)->items))) > { > + return NULL; > + } > + } > + > + if (item->type != TREE_ITEM_TYPE_DRAWABLE) { > + return NULL; > + } > + > + return ((DrawItem *)item)->shadow; > +} > + > +Ring *tree_item_container_items(TreeItem *item, Ring *ring) > +{ > + return (item->container) ? &item->container->items : ring; > +} > + > +int tree_item_contained_by(TreeItem *item, Ring *ring) > +{ > + spice_assert(item && ring); > + do { > + Ring *now = tree_item_container_items(item, ring); > + if (now == ring) { > + return TRUE; > + } > + } while ((item = (TreeItem *)item->container)); > + > + return FALSE; > +} > + > +void draw_item_remove_shadow(DrawItem *item) > +{ > + Shadow *shadow; > + > + if (!item->shadow) { > + return; > + } > + shadow = item->shadow; > + item->shadow = NULL; > + /* shadow_free? */ > + ring_remove(&shadow->base.siblings_link); > + region_destroy(&shadow->base.rgn); > + region_destroy(&shadow->on_hold); > + free(shadow); > +} > diff --git a/server/tree.h b/server/tree.h > index 01d4ff9..8b9c6ba 100644 > --- a/server/tree.h > +++ b/server/tree.h > @@ -80,6 +80,11 @@ static inline int is_opaque_item(TreeItem *item) > } > > void tree_item_dump (TreeItem *item); > +Shadow* tree_item_find_shadow (TreeItem *item); > +int tree_item_contained_by (TreeItem *item, Ring > *ring); > +Ring* tree_item_container_items (TreeItem *item, Ring > *ring); > + > +void draw_item_remove_shadow (DrawItem *item); > Shadow* shadow_new (DrawItem *item, const > SpicePoint *delta); > Container* container_new (DrawItem *item); > void container_free (Container *container); _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel