diff options
author | Yonit Halperin <yhalperi@redhat.com> | 2010-06-20 15:24:49 +0300 |
---|---|---|
committer | Alexander Larsson <alexl@redhat.com> | 2010-06-21 15:05:37 +0200 |
commit | 25bb38f643af6f0015df369a22176275b6ebfae0 (patch) | |
tree | 5bfc75812c6ba89086d1ab8782552de806fd53bb /server | |
parent | cfc1e95bda0e150b3de225c3572bb1004dad070e (diff) | |
download | spice-25bb38f643af6f0015df369a22176275b6ebfae0.tar.gz spice-25bb38f643af6f0015df369a22176275b6ebfae0.tar.xz spice-25bb38f643af6f0015df369a22176275b6ebfae0.zip |
applying zlib compression over glz on WAN connection
Diffstat (limited to 'server')
-rw-r--r-- | server/Makefile.am | 3 | ||||
-rw-r--r-- | server/red_worker.c | 156 | ||||
-rw-r--r-- | server/zlib_encoder.c | 104 | ||||
-rw-r--r-- | server/zlib_encoder.h | 47 |
4 files changed, 296 insertions, 14 deletions
diff --git a/server/Makefile.am b/server/Makefile.am index 9a6ac887..f87e3f45 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -61,6 +61,7 @@ libspice_server_la_LIBADD = \ $(CELT051_LIBS) \ $(SLIRP_LIBS) \ $(LIBRT) \ + $(Z_LIBS) \ $(NULL) if SUPPORT_TUNNEL @@ -105,6 +106,8 @@ libspice_server_la_SOURCES = \ generated_demarshallers.c \ generated_marshallers.c \ generated_marshallers.h \ + zlib_encoder.c \ + zlib_encoder.h \ $(TUNNEL_SRCS) \ $(COMMON_SRCS) \ $(NULL) diff --git a/server/red_worker.c b/server/red_worker.c index 387861c5..ff78483c 100644 --- a/server/red_worker.c +++ b/server/red_worker.c @@ -53,6 +53,7 @@ #include "marshaller.h" #include "demarshallers.h" #include "generated_marshallers.h" +#include "zlib_encoder.h" //#define COMPRESS_STAT //#define DUMP_BITMAP @@ -94,6 +95,9 @@ #define RED_COMPRESS_BUF_SIZE (1024 * 64) +#define ZLIB_DEFAULT_COMPRESSION_LEVEL 3 +#define MIN_GLZ_SIZE_FOR_ZLIB 100 + typedef int64_t red_time_t; static inline red_time_t timespec_to_red_time(struct timespec *time) @@ -172,6 +176,7 @@ static const char *lz_stat_name = "lz"; static const char *glz_stat_name = "glz"; static const char *quic_stat_name = "quic"; static const char *jpeg_stat_name = "jpeg"; +static const char *zlib_stat_name = "zlib_glz"; static inline void stat_compress_init(stat_info_t *info, const char *name) { @@ -468,6 +473,7 @@ typedef struct __attribute__ ((__packed__)) RedImage { SpiceLZPLTData lz_plt; SpiceSurface surface; SpiceJPEGData jpeg; + SpiceZlibGlzRGBData zlib_glz; }; } RedImage; @@ -562,6 +568,10 @@ typedef struct { int input_bufs_pos; RedCompressBuf *input_bufs[2]; } unstable_lines_data; + struct { + RedCompressBuf* next; + int size_left; + } compressed_data; // for encoding data that was already compressed by another method } u; char message_buf[512]; } EncoderData; @@ -586,6 +596,11 @@ typedef struct { EncoderData data; } JpegData; +typedef struct { + ZlibEncoderUsrContext usr; + EncoderData data; +} ZlibData; + /**********************************/ /* LZ dictionary related entities */ /**********************************/ @@ -671,6 +686,8 @@ struct DisplayChannel { int enable_jpeg; int jpeg_quality; + int enable_zlib_glz_wrap; + int zlib_level; #ifdef RED_STATISTICS StatNodeRef stat; uint64_t *cache_hits_counter; @@ -682,6 +699,7 @@ struct DisplayChannel { stat_info_t glz_stat; stat_info_t quic_stat; stat_info_t jpeg_stat; + stat_info_t zlib_glz_stat; #endif }; @@ -953,6 +971,9 @@ typedef struct RedWorker { JpegData jpeg_data; JpegEncoderContext *jpeg; + + ZlibData zlib_data; + ZlibEncoder *zlib; #ifdef PIPE_DEBUG uint32_t last_id; @@ -1027,9 +1048,16 @@ static void dump_bitmap(RedWorker *worker, SpiceBitmap *bitmap, uint32_t group_i #ifdef COMPRESS_STAT static void print_compress_stats(DisplayChannel *display_channel) { + uint64_t glz_enc_size; + if (!display_channel) { return; } + + glz_enc_size = display_channel->enable_zlib_glz_wrap ? + display_channel->zlib_glz_stat.comp_size : + display_channel->glz_stat.comp_size; + red_printf("==> Compression stats for display %u", display_channel->base.id); red_printf("Method \t count \torig_size(MB)\tenc_size(MB)\tenc_time(s)"); red_printf("QUIC \t%8d\t%13.2f\t%12.2f\t%12.2f", @@ -1044,6 +1072,12 @@ static void print_compress_stats(DisplayChannel *display_channel) stat_byte_to_mega(display_channel->glz_stat.comp_size), stat_cpu_time_to_sec(display_channel->glz_stat.total) ); + red_printf("ZLIB GLZ \t%8d\t%13.2f\t%12.2f\t%12.2f", + display_channel->zlib_glz_stat.count, + stat_byte_to_mega(display_channel->zlib_glz_stat.orig_size), + stat_byte_to_mega(display_channel->zlib_glz_stat.comp_size), + stat_cpu_time_to_sec(display_channel->zlib_glz_stat.total) + ); red_printf("LZ \t%8d\t%13.2f\t%12.2f\t%12.2f", display_channel->lz_stat.count, stat_byte_to_mega(display_channel->lz_stat.orig_size), @@ -1066,11 +1100,12 @@ static void print_compress_stats(DisplayChannel *display_channel) display_channel->quic_stat.orig_size + display_channel->jpeg_stat.orig_size), stat_byte_to_mega(display_channel->lz_stat.comp_size + - display_channel->glz_stat.comp_size + + glz_enc_size + display_channel->quic_stat.comp_size + display_channel->jpeg_stat.comp_size), stat_cpu_time_to_sec(display_channel->lz_stat.total + display_channel->glz_stat.total + + display_channel->zlib_glz_stat.total + display_channel->quic_stat.total + display_channel->jpeg_stat.total) ); @@ -5775,6 +5810,12 @@ static int jpeg_usr_more_space(JpegEncoderUsrContext *usr, uint8_t **io_ptr) return (encoder_usr_more_space(usr_data, (uint32_t **)io_ptr) << 2); } +static int zlib_usr_more_space(ZlibEncoderUsrContext *usr, uint8_t **io_ptr) +{ + EncoderData *usr_data = &(((ZlibData *)usr)->data); + return (encoder_usr_more_space(usr_data, (uint32_t **)io_ptr) << 2); +} + static inline int encoder_usr_more_lines(EncoderData *enc_data, uint8_t **lines) { uint32_t data_size; @@ -5927,6 +5968,27 @@ static int jpeg_usr_no_more_lines(JpegEncoderUsrContext *usr, uint8_t **lines) return 0; } +static int zlib_usr_more_input(ZlibEncoderUsrContext *usr, uint8_t** input) +{ + EncoderData *usr_data = &(((ZlibData *)usr)->data); + int buf_size; + + if (!usr_data->u.compressed_data.next) { + ASSERT(usr_data->u.compressed_data.size_left == 0); + return 0; + } + + *input = (uint8_t*)usr_data->u.compressed_data.next->buf; + buf_size = MIN(sizeof(usr_data->u.compressed_data.next->buf), + usr_data->u.compressed_data.size_left); + + usr_data->u.compressed_data.next = usr_data->u.compressed_data.next->send_next; + usr_data->u.compressed_data.size_left -= buf_size; + + return buf_size; + +} + static void glz_usr_free_image(GlzEncoderUsrContext *usr, GlzUsrImageContext *image) { GlzData *lz_data = (GlzData *)usr; @@ -6001,6 +6063,18 @@ static inline void red_init_jpeg(RedWorker *worker) } } +static inline void red_init_zlib(RedWorker *worker) +{ + worker->zlib_data.usr.more_space = zlib_usr_more_space; + worker->zlib_data.usr.more_input = zlib_usr_more_input; + + worker->zlib = zlib_encoder_create(&worker->zlib_data.usr, ZLIB_DEFAULT_COMPRESSION_LEVEL); + + if (!worker->zlib) { + PANIC("create zlib encoder failed"); + } +} + #ifdef __GNUC__ #define ATTR_PACKED __attribute__ ((__packed__)) #else @@ -6204,12 +6278,14 @@ static inline int red_glz_compress_image(DisplayChannel *display_channel, #endif ASSERT(BITMAP_FMT_IS_RGB[src->format]); GlzData *glz_data = &display_channel->glz_data; + ZlibData *zlib_data; LzImageType type = MAP_BITMAP_FMT_TO_LZ_IMAGE_TYPE[src->format]; RedGlzDrawable *glz_drawable; GlzDrawableInstanceItem *glz_drawable_instance; uint8_t *lines; unsigned int num_lines; - int size; + int glz_size; + int zlib_size; glz_data->data.bufs_tail = red_display_alloc_compress_buf(display_channel); glz_data->data.bufs_head = glz_data->data.bufs_tail; @@ -6241,21 +6317,67 @@ static inline int red_glz_compress_image(DisplayChannel *display_channel, num_lines = 0; } - size = glz_encode(display_channel->glz, type, src->x, src->y, - (src->flags & QXL_BITMAP_TOP_DOWN), lines, num_lines, - src->stride, (uint8_t*)glz_data->data.bufs_head->buf, - sizeof(glz_data->data.bufs_head->buf), - glz_drawable_instance, - &glz_drawable_instance->glz_instance); + glz_size = glz_encode(display_channel->glz, type, src->x, src->y, + (src->flags & QXL_BITMAP_TOP_DOWN), lines, num_lines, + src->stride, (uint8_t*)glz_data->data.bufs_head->buf, + sizeof(glz_data->data.bufs_head->buf), + glz_drawable_instance, + &glz_drawable_instance->glz_instance); + + stat_compress_add(&display_channel->glz_stat, start_time, src->stride * src->y, glz_size); + + if (!display_channel->enable_zlib_glz_wrap || (glz_size < MIN_GLZ_SIZE_FOR_ZLIB)) { + goto glz; + } +#ifdef COMPRESS_STAT + start_time = stat_now(); +#endif + zlib_data = &worker->zlib_data; + + zlib_data->data.bufs_tail = red_display_alloc_compress_buf(display_channel); + zlib_data->data.bufs_head = zlib_data->data.bufs_tail; + + if (!zlib_data->data.bufs_head) { + red_printf("failed to allocate zlib compress buffer"); + goto glz; + } + + zlib_data->data.bufs_head->send_next = NULL; + zlib_data->data.display_channel = display_channel; + + zlib_data->data.u.compressed_data.next = glz_data->data.bufs_head; + zlib_data->data.u.compressed_data.size_left = glz_size; + zlib_size = zlib_encode(worker->zlib, display_channel->zlib_level, + glz_size, (uint8_t*)zlib_data->data.bufs_head->buf, + sizeof(zlib_data->data.bufs_head->buf)); + + // the compressed buffer is bigger than the original data + if (zlib_size >= glz_size) { + while (zlib_data->data.bufs_head) { + RedCompressBuf *buf = zlib_data->data.bufs_head; + zlib_data->data.bufs_head = buf->send_next; + red_display_free_compress_buf(display_channel, buf); + } + goto glz; + } + + dest->descriptor.type = SPICE_IMAGE_TYPE_ZLIB_GLZ_RGB; + dest->zlib_glz.glz_data_size = glz_size; + dest->zlib_glz.data_size = zlib_size; + + o_comp_data->comp_buf = zlib_data->data.bufs_head; + o_comp_data->comp_buf_size = zlib_size; + + stat_compress_add(&display_channel->zlib_glz_stat, start_time, glz_size, zlib_size); + return TRUE; +glz: dest->descriptor.type = SPICE_IMAGE_TYPE_GLZ_RGB; - dest->lz_rgb.data_size = size; + dest->lz_rgb.data_size = glz_size; o_comp_data->comp_buf = glz_data->data.bufs_head; - o_comp_data->comp_buf_size = size; + o_comp_data->comp_buf_size = glz_size; - stat_compress_add(&display_channel->glz_stat, start_time, src->stride * src->y, - size); return TRUE; } @@ -9592,7 +9714,7 @@ static SpiceCanvas *create_ogl_context_common(RedWorker *worker, OGLCtx *ctx, ui oglctx_make_current(ctx); if (!(canvas = gl_canvas_create(width, height, depth, &worker->image_cache.base, - &worker->image_surfaces, NULL, NULL, + &worker->image_surfaces, NULL, NULL, NULL, &worker->preload_group_virt_mapping))) { return NULL; } @@ -9650,7 +9772,7 @@ static inline void *create_canvas_for_surface(RedWorker *worker, RedSurface *sur canvas = canvas_create_for_data(width, height, format, line_0, stride, &worker->image_cache.base, - &worker->image_surfaces, NULL, NULL, + &worker->image_surfaces, NULL, NULL, NULL, &worker->preload_group_virt_mapping); surface->context.top_down = TRUE; surface->context.canvas_draws_on_surface = TRUE; @@ -10459,6 +10581,9 @@ static void handle_new_display_channel(RedWorker *worker, RedsStreamContext *pee display_channel->enable_jpeg = IS_LOW_BANDWIDTH(); display_channel->jpeg_quality = 85; + display_channel->enable_zlib_glz_wrap = IS_LOW_BANDWIDTH(); + display_channel->zlib_level = ZLIB_DEFAULT_COMPRESSION_LEVEL; + red_ref_channel((RedChannel*)display_channel); on_new_display_channel(worker); red_unref_channel((RedChannel*)display_channel); @@ -10467,6 +10592,7 @@ static void handle_new_display_channel(RedWorker *worker, RedsStreamContext *pee stat_compress_init(&display_channel->glz_stat, glz_stat_name); stat_compress_init(&display_channel->quic_stat, quic_stat_name); stat_compress_init(&display_channel->jpeg_stat, jpeg_stat_name); + stat_compress_init(&display_channel->zlib_glz_stat, zlib_stat_name); } static void red_disconnect_cursor(RedChannel *channel) @@ -11017,6 +11143,7 @@ static void handle_dev_input(EventListener *listener, uint32_t events) stat_reset(&worker->display_channel->lz_stat); stat_reset(&worker->display_channel->glz_stat); stat_reset(&worker->display_channel->jpeg_stat); + stat_reset(&worker->display_channel->zlib_glz_stat); } #endif break; @@ -11180,6 +11307,7 @@ void *red_worker_main(void *arg) red_init_quic(&worker); red_init_lz(&worker); red_init_jpeg(&worker); + red_init_zlib(&worker); worker.epoll_timeout = INF_EPOLL_WAIT; for (;;) { struct epoll_event events[MAX_EPOLL_SOURCES]; diff --git a/server/zlib_encoder.c b/server/zlib_encoder.c new file mode 100644 index 00000000..e0d8d830 --- /dev/null +++ b/server/zlib_encoder.c @@ -0,0 +1,104 @@ +#include "red_common.h" +#include "zlib_encoder.h" +#include <zlib.h> + +struct ZlibEncoder { + ZlibEncoderUsrContext *usr; + + z_stream strm; + int last_level; +}; + +ZlibEncoder* zlib_encoder_create(ZlibEncoderUsrContext *usr, int level) +{ + ZlibEncoder *enc; + int z_ret; + + if (!usr->more_space || !usr->more_input) { + return NULL; + } + + enc = spice_new0(ZlibEncoder, 1); + + enc->usr = usr; + + enc->strm.zalloc = Z_NULL; + enc->strm.zfree = Z_NULL; + enc->strm.opaque = Z_NULL; + + z_ret = deflateInit(&enc->strm, level); + enc->last_level = level; + if (z_ret != Z_OK) { + red_printf("zlib error"); + free(enc); + return NULL; + } + + return enc; +} + +void zlib_encoder_destroy(ZlibEncoder *encoder) +{ + deflateEnd(&encoder->strm); + free(encoder); +} + +/* returns the total size of the encoded data */ +int zlib_encode(ZlibEncoder *zlib, int level, int input_size, + uint8_t *io_ptr, unsigned int num_io_bytes) +{ + int flush; + int enc_size = 0; + int out_size = 0; + int z_ret; + + z_ret = deflateReset(&zlib->strm); + + if (z_ret != Z_OK) { + red_error("deflateReset failed"); + } + + zlib->strm.next_out = io_ptr; + zlib->strm.avail_out = num_io_bytes; + + if (level != zlib->last_level) { + if (zlib->strm.avail_out == 0) { + zlib->strm.avail_out = zlib->usr->more_space(zlib->usr, &zlib->strm.next_out); + if (zlib->strm.avail_out == 0) { + red_error("not enough space"); + } + } + z_ret = deflateParams(&zlib->strm, level, Z_DEFAULT_STRATEGY); + if (z_ret != Z_OK) { + red_error("deflateParams failed"); + } + zlib->last_level = level; + } + + + do { + zlib->strm.avail_in = zlib->usr->more_input(zlib->usr, &zlib->strm.next_in); + if (zlib->strm.avail_in <= 0) { + red_error("more input failed\n"); + } + enc_size += zlib->strm.avail_in; + flush = (enc_size == input_size) ? Z_FINISH : Z_NO_FLUSH; + while (1) { + int deflate_size = zlib->strm.avail_out; + z_ret = deflate(&zlib->strm, flush); + ASSERT(z_ret != Z_STREAM_ERROR); + out_size += deflate_size - zlib->strm.avail_out; + if (zlib->strm.avail_out) { + break; + } + + zlib->strm.avail_out = zlib->usr->more_space(zlib->usr, &zlib->strm.next_out); + if (zlib->strm.avail_out == 0) { + red_error("not enough space"); + } + } + } while (flush != Z_FINISH); + + ASSERT(z_ret == Z_STREAM_END); + return out_size; +} diff --git a/server/zlib_encoder.h b/server/zlib_encoder.h new file mode 100644 index 00000000..0620fc7b --- /dev/null +++ b/server/zlib_encoder.h @@ -0,0 +1,47 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef _H_ZLIB_ENCODER +#define _H_ZLIB_ENCODER + +typedef struct ZlibEncoder ZlibEncoder; +typedef struct ZlibEncoderUsrContext ZlibEncoderUsrContext; + +struct ZlibEncoderUsrContext { + int (*more_space)(ZlibEncoderUsrContext *usr, uint8_t **io_ptr); + int (*more_input)(ZlibEncoderUsrContext *usr, uint8_t **input); +}; + +ZlibEncoder* zlib_encoder_create(ZlibEncoderUsrContext *usr, int level); +void zlib_encoder_destroy(ZlibEncoder *encoder); + +/* returns the total size of the encoded data */ +int zlib_encode(ZlibEncoder *zlib, int level, int input_size, + uint8_t *io_ptr, unsigned int num_io_bytes); +#endif |