diff options
Diffstat (limited to 'server/red_worker.c')
-rw-r--r-- | server/red_worker.c | 209 |
1 files changed, 158 insertions, 51 deletions
diff --git a/server/red_worker.c b/server/red_worker.c index 823c25b6..69262102 100644 --- a/server/red_worker.c +++ b/server/red_worker.c @@ -374,6 +374,12 @@ typedef struct ImageItem { typedef struct Drawable Drawable; +enum { + STREAM_FRAME_NONE, + STREAM_FRAME_NATIVE, + STREAM_FRAME_CONTAINER, +}; + typedef struct Stream Stream; struct Stream { uint8_t refs; @@ -792,6 +798,7 @@ struct Drawable { int gradual_frames_count; int last_gradual_frame; Stream *stream; + Stream *sized_stream; int streamable; BitmapGradualType copy_bitmap_graduality; uint32_t group_id; @@ -994,7 +1001,7 @@ static void red_update_area(RedWorker *worker, const SpiceRect *area, int surfac static void red_release_cursor(RedWorker *worker, CursorItem *cursor); static inline void release_drawable(RedWorker *worker, Drawable *item); static void red_display_release_stream(RedWorker *worker, StreamAgent *agent); -static inline void red_detach_stream(RedWorker *worker, Stream *stream); +static inline void red_detach_stream(RedWorker *worker, Stream *stream, int detach_sized); static void red_stop_stream(RedWorker *worker, Stream *stream); static inline void red_stream_maintenance(RedWorker *worker, Drawable *candidate, Drawable *sect); static inline void display_begin_send_message(RedChannelClient *rcc); @@ -1747,7 +1754,7 @@ static inline void release_drawable(RedWorker *worker, Drawable *drawable) spice_assert(ring_is_empty(&drawable->pipes)); if (drawable->stream) { - red_detach_stream(worker, drawable->stream); + red_detach_stream(worker, drawable->stream, TRUE); } region_destroy(&drawable->tree_item.base.rgn); @@ -2424,11 +2431,14 @@ static void red_release_stream(RedWorker *worker, Stream *stream) } } -static inline void red_detach_stream(RedWorker *worker, Stream *stream) +static inline void red_detach_stream(RedWorker *worker, Stream *stream, int detach_sized) { spice_assert(stream->current && stream->current->stream); spice_assert(stream->current->stream == stream); stream->current->stream = NULL; + if (detach_sized) { + stream->current->sized_stream = NULL; + } stream->current = NULL; } @@ -2531,6 +2541,7 @@ static void red_stop_stream(RedWorker *worker, Stream *stream) spice_assert(ring_item_is_linked(&stream->link)); spice_assert(!stream->current); + spice_debug("stream %d", get_stream_id(worker, stream)); WORKER_FOREACH_DCC(worker, item, dcc) { StreamAgent *stream_agent; stream_agent = &dcc->stream_agents[get_stream_id(worker, stream)]; @@ -2586,11 +2597,13 @@ static inline void red_display_detach_stream_gracefully(DisplayChannelClient *dc /* (1) The caller should detach the drawable from the stream. This will * lead to sending the drawable losslessly, as an ordinary drawable. */ if (red_display_drawable_is_in_pipe(dcc, stream->current)) { - spice_debug("stream %d: upgrade by linked drawable. box ==>", stream_id); + spice_debug("stream %d: upgrade by linked drawable. sized %d, box ==>", + stream_id, stream->current->sized_stream != NULL); rect_debug(&stream->current->red_drawable->bbox); return; } - spice_debug("stream %d: upgrade by drawable. box ==>", stream_id); + spice_debug("stream %d: upgrade by drawable. sized %d, box ==>", + stream_id, stream->current->sized_stream != NULL); rect_debug(&stream->current->red_drawable->bbox); rcc = &dcc->common.base; channel = rcc->channel; @@ -2629,7 +2642,7 @@ static inline void red_detach_stream_gracefully(RedWorker *worker, Stream *strea red_display_detach_stream_gracefully(dcc, stream); } if (stream->current) { - red_detach_stream(worker, stream); + red_detach_stream(worker, stream, TRUE); } } @@ -2653,14 +2666,15 @@ static void red_detach_streams_behind(RedWorker *worker, QRegion *region) if (region_intersects(&agent->vis_region, region)) { red_display_detach_stream_gracefully(dcc, stream); detach_stream = 1; + spice_debug("stream %d", get_stream_id(worker, stream)); } } if (detach_stream && stream->current) { - red_detach_stream(worker, stream); + red_detach_stream(worker, stream, TRUE); } else if (!has_clients) { if (stream->current && region_intersects(&stream->current->tree_item.base.rgn, region)) { - red_detach_stream(worker, stream); + red_detach_stream(worker, stream, TRUE); } } } @@ -2840,7 +2854,7 @@ static void red_create_stream(RedWorker *worker, Drawable *drawable) stream_width = src_rect->right - src_rect->left; stream_height = src_rect->bottom - src_rect->top; - stream->mjpeg_encoder = mjpeg_encoder_new(stream_width, stream_height); + stream->mjpeg_encoder = mjpeg_encoder_new(); ring_add(&worker->streams, &stream->link); stream->current = drawable; @@ -2858,7 +2872,9 @@ static void red_create_stream(RedWorker *worker, Drawable *drawable) red_display_create_stream(dcc, stream); } worker->stream_count++; - + spice_debug("stream %d %dx%d (%d, %d) (%d, %d)", (int)(stream - worker->streams_buf), stream->width, + stream->height, stream->dest_area.left, stream->dest_area.top, + stream->dest_area.right, stream->dest_area.bottom); return; } @@ -2919,34 +2935,64 @@ static inline int __red_is_next_stream_frame(RedWorker *worker, const int other_src_height, const SpiceRect *other_dest, const red_time_t other_time, - const Stream *stream) + const Stream *stream, + int container_candidate_allowed) { RedDrawable *red_drawable; + int is_frame_container = FALSE; if (candidate->creation_time - other_time > (stream ? RED_STREAM_CONTINUS_MAX_DELTA : RED_STREAM_DETACTION_MAX_DELTA)) { - return FALSE; + 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 FALSE; - } + if (!rect_is_equal(&red_drawable->bbox, other_dest)) { + return STREAM_FRAME_NONE; + } - SpiceRect* 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 FALSE; + 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 FALSE; + return STREAM_FRAME_NONE; } } - return TRUE; + if (is_frame_container) { + return STREAM_FRAME_CONTAINER; + } else { + return STREAM_FRAME_NATIVE; + } } static inline int red_is_next_stream_frame(RedWorker *worker, const Drawable *candidate, @@ -2960,7 +3006,8 @@ static inline int red_is_next_stream_frame(RedWorker *worker, const Drawable *ca return __red_is_next_stream_frame(worker, candidate, prev_src->right - prev_src->left, prev_src->bottom - prev_src->top, &prev->red_drawable->bbox, prev->creation_time, - prev->stream); + prev->stream, + FALSE); } static void reset_rate(DisplayChannelClient *dcc, StreamAgent *stream_agent) @@ -3102,20 +3149,31 @@ static inline void red_stream_maintenance(RedWorker *worker, Drawable *candidate return; } - if (!red_is_next_stream_frame(worker, candidate, prev)) { - return; - } - if ((stream = prev->stream)) { - pre_stream_item_swap(worker, stream); - red_detach_stream(worker, stream); - prev->streamable = FALSE; //prevent item trace - red_attach_stream(worker, candidate, stream); + int is_next_frame = __red_is_next_stream_frame(worker, + candidate, + stream->width, + stream->height, + &stream->dest_area, + stream->last_time, + stream, + TRUE); + if (is_next_frame != STREAM_FRAME_NONE) { + pre_stream_item_swap(worker, stream); + red_detach_stream(worker, stream, FALSE); + prev->streamable = FALSE; //prevent item trace + red_attach_stream(worker, candidate, stream); + if (is_next_frame == STREAM_FRAME_CONTAINER) { + candidate->sized_stream = stream; + } + } } else { - red_stream_add_frame(worker, candidate, - prev->frames_count, - prev->gradual_frames_count, - prev->last_gradual_frame); + if (red_is_next_stream_frame(worker, candidate, prev) != STREAM_FRAME_NONE) { + red_stream_add_frame(worker, candidate, + prev->frames_count, + prev->gradual_frames_count, + prev->last_gradual_frame); + } } } @@ -3243,14 +3301,24 @@ static inline void red_use_stream_trace(RedWorker *worker, Drawable *drawable) while (item) { Stream *stream = SPICE_CONTAINEROF(item, Stream, link); - if (!stream->current && __red_is_next_stream_frame(worker, - drawable, - stream->width, - stream->height, - &stream->dest_area, - stream->last_time, - stream)) { + int is_next_frame = __red_is_next_stream_frame(worker, + 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 + pre_stream_item_swap(worker, stream); + red_detach_stream(worker, stream, FALSE); + } red_attach_stream(worker, drawable, stream); + if (is_next_frame == STREAM_FRAME_CONTAINER) { + drawable->sized_stream = stream; + } return; } item = ring_next(ring, item); @@ -3260,7 +3328,8 @@ static inline void red_use_stream_trace(RedWorker *worker, Drawable *drawable) trace_end = trace + NUM_TRACE_ITEMS; for (; trace < trace_end; trace++) { if (__red_is_next_stream_frame(worker, drawable, trace->width, trace->height, - &trace->dest_area, trace->time, NULL)) { + &trace->dest_area, trace->time, NULL, FALSE) != + STREAM_FRAME_NONE) { if (red_stream_add_frame(worker, drawable, trace->frames_count, trace->gradual_frames_count, @@ -8097,8 +8166,12 @@ static inline int red_marshall_stream_data(RedChannelClient *rcc, SpiceImage *image; RedWorker *worker = dcc->common.worker; int n; + int width, height; - spice_assert(stream); + if (!stream) { + spice_assert(drawable->sized_stream); + stream = drawable->sized_stream; + } spice_assert(drawable->red_drawable->type == QXL_DRAW_COPY); worker = display_channel->common.worker; @@ -8108,6 +8181,20 @@ static inline int red_marshall_stream_data(RedChannelClient *rcc, return FALSE; } + if (drawable->sized_stream) { + if (red_channel_client_test_remote_cap(rcc, SPICE_DISPLAY_CAP_SIZED_STREAM)) { + SpiceRect *src_rect = &drawable->red_drawable->u.copy.src_area; + + width = src_rect->right - src_rect->left; + height = src_rect->bottom - src_rect->top; + } else { + return FALSE; + } + } else { + width = stream->width; + height = stream->height; + } + StreamAgent *agent = &dcc->stream_agents[get_stream_id(worker, stream)]; uint64_t time_now = red_now(); size_t outbuf_size; @@ -8118,6 +8205,7 @@ static inline int red_marshall_stream_data(RedChannelClient *rcc, outbuf_size = dcc->send_data.stream_outbuf_size; if (!mjpeg_encoder_start_frame(stream->mjpeg_encoder, image->u.bitmap.format, + width, height, &dcc->send_data.stream_outbuf, &outbuf_size)) { return FALSE; @@ -8129,14 +8217,32 @@ static inline int red_marshall_stream_data(RedChannelClient *rcc, n = mjpeg_encoder_end_frame(stream->mjpeg_encoder); dcc->send_data.stream_outbuf_size = outbuf_size; - red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DATA, NULL); + if (!drawable->sized_stream) { + SpiceMsgDisplayStreamData stream_data; + + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DATA, NULL); - SpiceMsgDisplayStreamData stream_data; + stream_data.base.id = get_stream_id(worker, stream); + stream_data.base.multi_media_time = drawable->red_drawable->mm_time; + stream_data.data_size = n; - stream_data.base.id = get_stream_id(worker, stream); - stream_data.base.multi_media_time = drawable->red_drawable->mm_time; - stream_data.data_size = n; - spice_marshall_msg_display_stream_data(base_marshaller, &stream_data); + spice_marshall_msg_display_stream_data(base_marshaller, &stream_data); + } else { + SpiceMsgDisplayStreamDataSized stream_data; + + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DATA_SIZED, NULL); + + stream_data.base.id = get_stream_id(worker, stream); + stream_data.base.multi_media_time = drawable->red_drawable->mm_time; + stream_data.data_size = n; + stream_data.width = width; + stream_data.height = height; + stream_data.dest = drawable->red_drawable->bbox; + + spice_debug("stream %d: sized frame: dest ==> ", stream_data.base.id); + rect_debug(&stream_data.dest); + spice_marshall_msg_display_stream_data_sized(base_marshaller, &stream_data); + } spice_marshaller_add_ref(base_marshaller, dcc->send_data.stream_outbuf, n); agent->last_send_time = time_now; @@ -8150,7 +8256,9 @@ static inline void marshall_qxl_drawable(RedChannelClient *rcc, DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base); spice_assert(display_channel && rcc); - if (item->stream && red_marshall_stream_data(rcc, m, item)) { + /* allow sized frames to be streamed, even if they where replaced by another frame, since + * newer frames might not cover sized frames completely if they are bigger */ + if ((item->stream || item->sized_stream) && red_marshall_stream_data(rcc, m, item)) { return; } if (!display_channel->enable_jpeg) @@ -8400,7 +8508,6 @@ static void red_display_marshall_upgrade(RedChannelClient *rcc, SpiceMarshaller SpiceMarshaller *src_bitmap_out, *mask_bitmap_out; spice_assert(rcc && rcc->channel && item && item->drawable); - red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_COPY, &item->base); red_drawable = item->drawable->red_drawable; |