From face40e19e08e33f6a43d57fb17f8945f7c16e31 Mon Sep 17 00:00:00 2001 From: Marc-AndrĂ© Lureau Date: Sat, 14 Sep 2013 19:03:43 +0200 Subject: worker: move image cache to display Acked-by: Frediano Ziglio --- server/display-channel.h | 2 + server/red_worker.c | 119 +++++++++++--------------------------- server/spice_image_cache.c | 59 +++++++++++++++++++ server/spice_image_cache.h | 19 +++++-- server/stream.h | 139 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 247 insertions(+), 91 deletions(-) create mode 100644 server/stream.h diff --git a/server/display-channel.h b/server/display-channel.h index ec1c483f..055c2a78 100644 --- a/server/display-channel.h +++ b/server/display-channel.h @@ -344,6 +344,8 @@ struct DisplayChannel { RedCompressBuf *free_compress_bufs; + ImageCache image_cache; + #ifdef RED_STATISTICS uint64_t *cache_hits_counter; uint64_t *add_to_cache_counter; diff --git a/server/red_worker.c b/server/red_worker.c index 30010f77..61387812 100644 --- a/server/red_worker.c +++ b/server/red_worker.c @@ -433,8 +433,6 @@ typedef struct RedWorker { RedMemSlotInfo mem_slots; - ImageCache image_cache; - SpiceImageCompression image_compression; spice_wan_compression_t jpeg_state; spice_wan_compression_t zlib_glz_state; @@ -3324,65 +3322,9 @@ static void image_surface_init(RedWorker *worker) worker->image_surfaces.ops = &image_surfaces_ops; } -static void localize_bitmap(RedWorker *worker, SpiceImage **image_ptr, SpiceImage *image_store, - Drawable *drawable) -{ - SpiceImage *image = *image_ptr; - - if (image == NULL) { - spice_assert(drawable != NULL); - spice_assert(drawable->red_drawable->self_bitmap_image != NULL); - *image_ptr = drawable->red_drawable->self_bitmap_image; - return; - } - - if (image_cache_hit(&worker->image_cache, image->descriptor.id)) { - image_store->descriptor = image->descriptor; - image_store->descriptor.type = SPICE_IMAGE_TYPE_FROM_CACHE; - image_store->descriptor.flags = 0; - *image_ptr = image_store; - return; - } - - switch (image->descriptor.type) { - case SPICE_IMAGE_TYPE_QUIC: { - image_store->descriptor = image->descriptor; - image_store->u.quic = image->u.quic; - *image_ptr = image_store; -#ifdef IMAGE_CACHE_AGE - image_store->descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_ME; -#else - if (image_store->descriptor.width * image->descriptor.height >= 640 * 480) { - image_store->descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_ME; - } -#endif - break; - } - case SPICE_IMAGE_TYPE_BITMAP: - case SPICE_IMAGE_TYPE_SURFACE: - /* nothing */ - break; - default: - spice_error("invalid image type"); - } -} - -static void localize_brush(RedWorker *worker, SpiceBrush *brush, SpiceImage *image_store) -{ - if (brush->type == SPICE_BRUSH_TYPE_PATTERN) { - localize_bitmap(worker, &brush->u.pattern.pat, image_store, NULL); - } -} - -static void localize_mask(RedWorker *worker, SpiceQMask *mask, SpiceImage *image_store) -{ - if (mask->bitmap) { - localize_bitmap(worker, &mask->bitmap, image_store, NULL); - } -} - static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable) { + DisplayChannel *display = worker->display_channel; RedSurface *surface; SpiceCanvas *canvas; SpiceClip clip = drawable->red_drawable->clip; @@ -3390,7 +3332,7 @@ static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable) surface = &worker->surfaces[drawable->surface_id]; canvas = surface->context.canvas; - image_cache_aging(&worker->image_cache); + image_cache_aging(&display->image_cache); region_add(&surface->draw_dirty_region, &drawable->red_drawable->bbox); @@ -3398,8 +3340,8 @@ static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable) case QXL_DRAW_FILL: { SpiceFill fill = drawable->red_drawable->u.fill; SpiceImage img1, img2; - localize_brush(worker, &fill.brush, &img1); - localize_mask(worker, &fill.mask, &img2); + image_cache_localize_brush(&display->image_cache, &fill.brush, &img1); + image_cache_localize_mask(&display->image_cache, &fill.mask, &img2); canvas->ops->draw_fill(canvas, &drawable->red_drawable->bbox, &clip, &fill); break; @@ -3407,17 +3349,17 @@ static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable) case QXL_DRAW_OPAQUE: { SpiceOpaque opaque = drawable->red_drawable->u.opaque; SpiceImage img1, img2, img3; - localize_brush(worker, &opaque.brush, &img1); - localize_bitmap(worker, &opaque.src_bitmap, &img2, drawable); - localize_mask(worker, &opaque.mask, &img3); + image_cache_localize_brush(&display->image_cache, &opaque.brush, &img1); + image_cache_localize(&display->image_cache, &opaque.src_bitmap, &img2, drawable); + image_cache_localize_mask(&display->image_cache, &opaque.mask, &img3); canvas->ops->draw_opaque(canvas, &drawable->red_drawable->bbox, &clip, &opaque); break; } case QXL_DRAW_COPY: { SpiceCopy copy = drawable->red_drawable->u.copy; SpiceImage img1, img2; - localize_bitmap(worker, ©.src_bitmap, &img1, drawable); - localize_mask(worker, ©.mask, &img2); + image_cache_localize(&display->image_cache, ©.src_bitmap, &img1, drawable); + image_cache_localize_mask(&display->image_cache, ©.mask, &img2); canvas->ops->draw_copy(canvas, &drawable->red_drawable->bbox, &clip, ©); break; @@ -3425,7 +3367,7 @@ static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable) case QXL_DRAW_TRANSPARENT: { SpiceTransparent transparent = drawable->red_drawable->u.transparent; SpiceImage img1; - localize_bitmap(worker, &transparent.src_bitmap, &img1, drawable); + image_cache_localize(&display->image_cache, &transparent.src_bitmap, &img1, drawable); canvas->ops->draw_transparent(canvas, &drawable->red_drawable->bbox, &clip, &transparent); break; @@ -3433,7 +3375,7 @@ static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable) case QXL_DRAW_ALPHA_BLEND: { SpiceAlphaBlend alpha_blend = drawable->red_drawable->u.alpha_blend; SpiceImage img1; - localize_bitmap(worker, &alpha_blend.src_bitmap, &img1, drawable); + image_cache_localize(&display->image_cache, &alpha_blend.src_bitmap, &img1, drawable); canvas->ops->draw_alpha_blend(canvas, &drawable->red_drawable->bbox, &clip, &alpha_blend); break; @@ -3446,8 +3388,8 @@ static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable) case QXL_DRAW_BLEND: { SpiceBlend blend = drawable->red_drawable->u.blend; SpiceImage img1, img2; - localize_bitmap(worker, &blend.src_bitmap, &img1, drawable); - localize_mask(worker, &blend.mask, &img2); + image_cache_localize(&display->image_cache, &blend.src_bitmap, &img1, drawable); + image_cache_localize_mask(&display->image_cache, &blend.mask, &img2); canvas->ops->draw_blend(canvas, &drawable->red_drawable->bbox, &clip, &blend); break; @@ -3455,7 +3397,7 @@ static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable) case QXL_DRAW_BLACKNESS: { SpiceBlackness blackness = drawable->red_drawable->u.blackness; SpiceImage img1; - localize_mask(worker, &blackness.mask, &img1); + image_cache_localize_mask(&display->image_cache, &blackness.mask, &img1); canvas->ops->draw_blackness(canvas, &drawable->red_drawable->bbox, &clip, &blackness); break; @@ -3463,7 +3405,7 @@ static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable) case QXL_DRAW_WHITENESS: { SpiceWhiteness whiteness = drawable->red_drawable->u.whiteness; SpiceImage img1; - localize_mask(worker, &whiteness.mask, &img1); + image_cache_localize_mask(&display->image_cache, &whiteness.mask, &img1); canvas->ops->draw_whiteness(canvas, &drawable->red_drawable->bbox, &clip, &whiteness); break; @@ -3471,7 +3413,7 @@ static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable) case QXL_DRAW_INVERS: { SpiceInvers invers = drawable->red_drawable->u.invers; SpiceImage img1; - localize_mask(worker, &invers.mask, &img1); + image_cache_localize_mask(&display->image_cache, &invers.mask, &img1); canvas->ops->draw_invers(canvas, &drawable->red_drawable->bbox, &clip, &invers); break; @@ -3479,9 +3421,9 @@ static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable) case QXL_DRAW_ROP3: { SpiceRop3 rop3 = drawable->red_drawable->u.rop3; SpiceImage img1, img2, img3; - localize_brush(worker, &rop3.brush, &img1); - localize_bitmap(worker, &rop3.src_bitmap, &img2, drawable); - localize_mask(worker, &rop3.mask, &img3); + image_cache_localize_brush(&display->image_cache, &rop3.brush, &img1); + image_cache_localize(&display->image_cache, &rop3.src_bitmap, &img2, drawable); + image_cache_localize_mask(&display->image_cache, &rop3.mask, &img3); canvas->ops->draw_rop3(canvas, &drawable->red_drawable->bbox, &clip, &rop3); break; @@ -3489,9 +3431,9 @@ static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable) case QXL_DRAW_COMPOSITE: { SpiceComposite composite = drawable->red_drawable->u.composite; SpiceImage src, mask; - localize_bitmap(worker, &composite.src_bitmap, &src, drawable); + image_cache_localize(&display->image_cache, &composite.src_bitmap, &src, drawable); if (composite.mask_bitmap) - localize_bitmap(worker, &composite.mask_bitmap, &mask, drawable); + image_cache_localize(&display->image_cache, &composite.mask_bitmap, &mask, drawable); canvas->ops->draw_composite(canvas, &drawable->red_drawable->bbox, &clip, &composite); break; @@ -3499,7 +3441,7 @@ static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable) case QXL_DRAW_STROKE: { SpiceStroke stroke = drawable->red_drawable->u.stroke; SpiceImage img1; - localize_brush(worker, &stroke.brush, &img1); + image_cache_localize_brush(&display->image_cache, &stroke.brush, &img1); canvas->ops->draw_stroke(canvas, &drawable->red_drawable->bbox, &clip, &stroke); break; @@ -3507,8 +3449,8 @@ static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable) case QXL_DRAW_TEXT: { SpiceText text = drawable->red_drawable->u.text; SpiceImage img1, img2; - localize_brush(worker, &text.fore_brush, &img1); - localize_brush(worker, &text.back_brush, &img2); + image_cache_localize_brush(&display->image_cache, &text.fore_brush, &img1); + image_cache_localize_brush(&display->image_cache, &text.back_brush, &img2); canvas->ops->draw_text(canvas, &drawable->red_drawable->bbox, &clip, &text); break; @@ -7907,10 +7849,11 @@ static void red_migrate_display(RedWorker *worker, RedChannelClient *rcc) static SpiceCanvas *create_ogl_context_common(RedWorker *worker, OGLCtx *ctx, uint32_t width, uint32_t height, int32_t stride, uint8_t depth) { + DisplayChannel *display = worker->display_channel; SpiceCanvas *canvas; oglctx_make_current(ctx); - if (!(canvas = gl_canvas_create(width, height, depth, &worker->image_cache.base, + if (!(canvas = gl_canvas_create(width, height, depth, &display->image_cache.base, &worker->image_surfaces, NULL, NULL, NULL))) { return NULL; } @@ -7962,13 +7905,14 @@ static inline void *create_canvas_for_surface(RedWorker *worker, RedSurface *sur uint32_t renderer, uint32_t width, uint32_t height, int32_t stride, uint32_t format, void *line_0) { + DisplayChannel *display = worker->display_channel; SpiceCanvas *canvas; switch (renderer) { case RED_RENDERER_SW: canvas = canvas_create_for_data(width, height, format, line_0, stride, - &worker->image_cache.base, + &display->image_cache.base, &worker->image_surfaces, NULL, NULL, NULL); surface->context.top_down = TRUE; surface->context.canvas_draws_on_surface = TRUE; @@ -9050,6 +8994,7 @@ static void display_channel_create(RedWorker *worker, int migrate) display_channel->num_renderers = num_renderers; memcpy(display_channel->renderers, renderers, sizeof(display_channel->renderers)); display_channel->renderer = RED_RENDERER_INVALID; + image_cache_init(&display_channel->image_cache); } static void guest_set_client_capabilities(RedWorker *worker) @@ -9638,7 +9583,10 @@ static void handle_dev_reset_cursor(void *opaque, void *payload) static void handle_dev_reset_image_cache(void *opaque, void *payload) { - image_cache_reset(&((RedWorker *)opaque)->image_cache); + RedWorker *worker = opaque; + DisplayChannel *display = worker->display_channel; + + image_cache_reset(&display->image_cache); } static void handle_dev_destroy_surface_wait_async(void *opaque, void *payload) @@ -10183,7 +10131,6 @@ RedWorker* red_worker_new(QXLInstance *qxl, RedDispatcher *red_dispatcher) worker->streaming_video = streaming_video; worker->driver_cap_monitors_config = 0; ring_init(&worker->current_list); - image_cache_init(&worker->image_cache); image_surface_init(worker); drawables_init(worker); red_init_streams(worker); diff --git a/server/spice_image_cache.c b/server/spice_image_cache.c index 4f62a474..1c5de24e 100644 --- a/server/spice_image_cache.c +++ b/server/spice_image_cache.c @@ -19,6 +19,8 @@ #include #endif #include "spice_image_cache.h" +#include "red_parse_qxl.h" +#include "display-channel.h" static ImageCacheItem *image_cache_find(ImageCache *cache, uint64_t id) { @@ -153,3 +155,60 @@ void image_cache_aging(ImageCache *cache) } #endif } + +void image_cache_localize(ImageCache *cache, SpiceImage **image_ptr, + SpiceImage *image_store, Drawable *drawable) +{ + SpiceImage *image = *image_ptr; + + if (image == NULL) { + spice_assert(drawable != NULL); + spice_assert(drawable->red_drawable->self_bitmap_image != NULL); + *image_ptr = drawable->red_drawable->self_bitmap_image; + return; + } + + if (image_cache_hit(cache, image->descriptor.id)) { + image_store->descriptor = image->descriptor; + image_store->descriptor.type = SPICE_IMAGE_TYPE_FROM_CACHE; + image_store->descriptor.flags = 0; + *image_ptr = image_store; + return; + } + + switch (image->descriptor.type) { + case SPICE_IMAGE_TYPE_QUIC: { + image_store->descriptor = image->descriptor; + image_store->u.quic = image->u.quic; + *image_ptr = image_store; +#ifdef IMAGE_CACHE_AGE + image_store->descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_ME; +#else + if (image_store->descriptor.width * image->descriptor.height >= 640 * 480) { + image_store->descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_ME; + } +#endif + break; + } + case SPICE_IMAGE_TYPE_BITMAP: + case SPICE_IMAGE_TYPE_SURFACE: + /* nothing */ + break; + default: + spice_error("invalid image type"); + } +} + +void image_cache_localize_brush(ImageCache *cache, SpiceBrush *brush, SpiceImage *image_store) +{ + if (brush->type == SPICE_BRUSH_TYPE_PATTERN) { + image_cache_localize(cache, &brush->u.pattern.pat, image_store, NULL); + } +} + +void image_cache_localize_mask(ImageCache *cache, SpiceQMask *mask, SpiceImage *image_store) +{ + if (mask->bitmap) { + image_cache_localize(cache, &mask->bitmap, image_store, NULL); + } +} diff --git a/server/spice_image_cache.h b/server/spice_image_cache.h index 07ecefb7..6d6b32d1 100644 --- a/server/spice_image_cache.h +++ b/server/spice_image_cache.h @@ -22,9 +22,12 @@ #include "common/pixman_utils.h" #include "common/canvas_base.h" - #include "common/ring.h" +/* FIXME: move back to display_channel.h (once structs are private) */ +typedef struct Drawable Drawable; +typedef struct DisplayChannelClient DisplayChannelClient; + typedef struct ImageCacheItem { RingItem lru_link; uint64_t id; @@ -48,9 +51,15 @@ typedef struct ImageCache { #endif } ImageCache; -int image_cache_hit(ImageCache *cache, uint64_t id); -void image_cache_init(ImageCache *cache); -void image_cache_reset(ImageCache *cache); -void image_cache_aging(ImageCache *cache); +int image_cache_hit (ImageCache *cache, uint64_t id); +void image_cache_init (ImageCache *cache); +void image_cache_reset (ImageCache *cache); +void image_cache_aging (ImageCache *cache); +void image_cache_localize (ImageCache *cache, SpiceImage **image_ptr, + SpiceImage *image_store, Drawable *drawable); +void image_cache_localize_brush (ImageCache *cache, SpiceBrush *brush, + SpiceImage *image_store); +void image_cache_localize_mask (ImageCache *cache, SpiceQMask *mask, + SpiceImage *image_store); #endif diff --git a/server/stream.h b/server/stream.h new file mode 100644 index 00000000..55004143 --- /dev/null +++ b/server/stream.h @@ -0,0 +1,139 @@ +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + Copyright (C) 2009-2015 Red Hat, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . +*/ +#ifndef STREAM_H_ +#define STREAM_H_ + +#include +#include "utils.h" +#include "mjpeg_encoder.h" +#include "common/region.h" +#include "red_channel.h" +#include "spice_image_cache.h" + +#define RED_STREAM_DETACTION_MAX_DELTA ((1000 * 1000 * 1000) / 5) // 1/5 sec +#define RED_STREAM_CONTINUS_MAX_DELTA (1000 * 1000 * 1000) +#define RED_STREAM_TIMEOUT (1000 * 1000 * 1000) +#define RED_STREAM_FRAMES_START_CONDITION 20 +#define RED_STREAM_GRADUAL_FRAMES_START_CONDITION 0.2 +#define RED_STREAM_FRAMES_RESET_CONDITION 100 +#define RED_STREAM_MIN_SIZE (96 * 96) +#define RED_STREAM_INPUT_FPS_TIMEOUT ((uint64_t)5 * 1000 * 1000 * 1000) // 5 sec +#define RED_STREAM_CHANNEL_CAPACITY 0.8 +/* the client's stream report frequency is the minimum of the 2 values below */ +#define RED_STREAM_CLIENT_REPORT_WINDOW 5 // #frames +#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 + +typedef struct Stream Stream; + +typedef struct StreamActivateReportItem { + PipeItem pipe_item; + uint32_t stream_id; +} StreamActivateReportItem; + +enum { + STREAM_FRAME_NONE, + STREAM_FRAME_NATIVE, + STREAM_FRAME_CONTAINER, +}; + +#define STREAM_STATS +#ifdef STREAM_STATS +typedef struct StreamStats { + uint64_t num_drops_pipe; + uint64_t num_drops_fps; + uint64_t num_frames_sent; + uint64_t num_input_frames; + uint64_t size_sent; + + uint64_t start; + uint64_t end; +} StreamStats; +#endif + +typedef struct StreamAgent { + QRegion vis_region; /* the part of the surface area that is currently occupied by video + fragments */ + QRegion clip; /* the current video clipping. It can be different from vis_region: + for example, let c1 be the clip area at time t1, and c2 + be the clip area at time t2, where t1 < t2. If c1 contains c2, and + at least part of c1/c2, hasn't been covered by a non-video images, + vis_region will contain c2 and also the part of c1/c2 that still + displays fragments of the video */ + + PipeItem create_item; + PipeItem destroy_item; + Stream *stream; + uint64_t last_send_time; + MJpegEncoder *mjpeg_encoder; + DisplayChannelClient *dcc; + + int frames; + int drops; + int fps; + + uint32_t report_id; + uint32_t client_required_latency; +#ifdef STREAM_STATS + StreamStats stats; +#endif +} StreamAgent; + +typedef struct StreamClipItem { + PipeItem base; + int refs; + StreamAgent *stream_agent; + int clip_type; + SpiceClipRects *rects; +} StreamClipItem; + +StreamClipItem * stream_clip_item_new (DisplayChannelClient* dcc, + StreamAgent *agent); + +typedef struct ItemTrace { + red_time_t time; + int frames_count; + int gradual_frames_count; + int last_gradual_frame; + int width; + int height; + SpiceRect dest_area; +} ItemTrace; + +typedef struct Stream Stream; +struct Stream { + uint8_t refs; + Drawable *current; + red_time_t last_time; + int width; + int height; + SpiceRect dest_area; + int top_down; + Stream *next; + RingItem link; + + uint32_t num_input_frames; + uint64_t input_fps_start_time; + uint32_t input_fps; +}; + + +void stream_agent_stats_print (StreamAgent *agent); + +#endif /* STREAM_H */ -- cgit