From 25bb38f643af6f0015df369a22176275b6ebfae0 Mon Sep 17 00:00:00 2001 From: Yonit Halperin Date: Sun, 20 Jun 2010 15:24:49 +0300 Subject: applying zlib compression over glz on WAN connection --- client/Makefile.am | 2 + client/canvas.h | 3 + client/red_gdi_canvas.cpp | 3 +- client/red_gl_canvas.cpp | 3 +- client/red_sw_canvas.cpp | 6 +- client/windows/redc.vcproj | 12 +++- client/x11/Makefile.am | 3 + client/zlib_decoder.cpp | 58 +++++++++++++++++ client/zlib_decoder.h | 41 ++++++++++++ common/canvas_base.c | 51 ++++++++++++--- common/canvas_base.h | 13 ++++ common/gdi_canvas.c | 4 +- common/gdi_canvas.h | 3 +- common/gl_canvas.c | 2 + common/gl_canvas.h | 1 + common/sw_canvas.c | 6 ++ common/sw_canvas.h | 2 + configure.ac | 3 + server/Makefile.am | 3 + server/red_worker.c | 156 +++++++++++++++++++++++++++++++++++++++++---- server/zlib_encoder.c | 104 ++++++++++++++++++++++++++++++ server/zlib_encoder.h | 47 ++++++++++++++ spice.proto | 9 +++ 23 files changed, 504 insertions(+), 31 deletions(-) create mode 100644 client/zlib_decoder.cpp create mode 100644 client/zlib_decoder.h create mode 100644 server/zlib_encoder.c create mode 100644 server/zlib_encoder.h diff --git a/client/Makefile.am b/client/Makefile.am index f700ed65..fb69ce64 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -111,6 +111,8 @@ RED_COMMON_SRCS = \ threads.h \ utils.cpp \ utils.h \ + zlib_decoder.cpp \ + zlib_decoder.h \ $(NULL) MAINTAINERCLEANFILES = $(spice_built_sources) diff --git a/client/canvas.h b/client/canvas.h index ff70e116..87bc060f 100644 --- a/client/canvas.h +++ b/client/canvas.h @@ -30,6 +30,7 @@ #include "glz_decoded_image.h" #include "glz_decoder.h" #include "jpeg_decoder.h" +#include "zlib_decoder.h" enum CanvasType { CANVAS_TYPE_INVALID, @@ -446,6 +447,7 @@ protected: GlzDecoder& glz_decoder() {return _glz_decoder;} JpegDecoder& jpeg_decoder() { return _jpeg_decoder;} + ZlibDecoder& zlib_decoder() { return _zlib_decoder;} private: void access_test(void* ptr, size_t size); @@ -468,6 +470,7 @@ private: GlzDecoder _glz_decoder; JpegDecoder _jpeg_decoder; + ZlibDecoder _zlib_decoder; CSurfaces& _csurfaces; diff --git a/client/red_gdi_canvas.cpp b/client/red_gdi_canvas.cpp index 453023e3..72b31df3 100644 --- a/client/red_gdi_canvas.cpp +++ b/client/red_gdi_canvas.cpp @@ -38,7 +38,8 @@ GDICanvas::GDICanvas(int width, int height, uint32_t format, &palette_cache.base, &csurfaces.base, &glz_decoder(), - &jpeg_decoder()))) { + &jpeg_decoder(), + &zlib_decoder()))) { THROW("create canvas failed"); } } diff --git a/client/red_gl_canvas.cpp b/client/red_gl_canvas.cpp index 1a219cdb..d7841b94 100644 --- a/client/red_gl_canvas.cpp +++ b/client/red_gl_canvas.cpp @@ -41,7 +41,8 @@ GCanvas::GCanvas(int width, int height, uint32_t format, RedWindow *win, &palette_cache.base, &csurfaces.base, &glz_decoder(), - &jpeg_decoder()))) { + &jpeg_decoder(), + &zlib_decoder()))) { THROW("create canvas failed"); } } diff --git a/client/red_sw_canvas.cpp b/client/red_sw_canvas.cpp index 7a8daf4d..b580e61d 100644 --- a/client/red_sw_canvas.cpp +++ b/client/red_sw_canvas.cpp @@ -43,14 +43,16 @@ SCanvas::SCanvas(bool onscreen, &palette_cache.base, &csurfaces.base, &glz_decoder(), - &jpeg_decoder()); + &jpeg_decoder(), + &zlib_decoder()); } else { _canvas = canvas_create(width, height, format, &pixmap_cache.base, &palette_cache.base, &csurfaces.base, &glz_decoder(), - &jpeg_decoder()); + &jpeg_decoder(), + &zlib_decoder()); } if (_canvas == NULL) { THROW("create canvas failed"); diff --git a/client/windows/redc.vcproj b/client/windows/redc.vcproj index 8f9e5092..37ca23ae 100644 --- a/client/windows/redc.vcproj +++ b/client/windows/redc.vcproj @@ -69,7 +69,7 @@ /> + + + + (decoder); + _decoder->decode(data, data_size, dest, dest_size); +} + +ZlibDecoder::ZlibDecoder() +{ + int z_ret; + + _z_strm.zalloc = Z_NULL; + _z_strm.zfree = Z_NULL; + _z_strm.opaque = Z_NULL; + _z_strm.next_in = Z_NULL; + _z_strm.avail_in = 0; + z_ret = inflateInit(&_z_strm); + if (z_ret != Z_OK) { + THROW("zlib decoder init failed, error %d", z_ret); + } + + static SpiceZlibDecoderOps decoder_ops = { + op_decode, + }; + + ops = &decoder_ops; +} + +ZlibDecoder::~ZlibDecoder() +{ + inflateEnd(&_z_strm); +} + + +void ZlibDecoder::decode(uint8_t *data, int data_size, uint8_t *dest, int dest_size) +{ + int z_ret; + + inflateReset(&_z_strm); + _z_strm.next_in = data; + _z_strm.avail_in = data_size; + _z_strm.next_out = dest; + _z_strm.avail_out = dest_size; + + z_ret = inflate(&_z_strm, Z_FINISH); + + if (z_ret != Z_STREAM_END) { + THROW("zlib inflate failed, error %d", z_ret); + } +} diff --git a/client/zlib_decoder.h b/client/zlib_decoder.h new file mode 100644 index 00000000..84b6f836 --- /dev/null +++ b/client/zlib_decoder.h @@ -0,0 +1,41 @@ +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + Copyright (C) 2010 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 _H_ZLIB_DECODER +#define _H_ZLIB_DECODER + +#include "common.h" +#include "canvas_base.h" + +#define ZLIB_WINAPI +#include + + +class ZlibDecoder : public SpiceZlibDecoder { +public: + ZlibDecoder(); + ~ZlibDecoder(); + + void decode(uint8_t *data, int data_size, uint8_t *dest, int dest_size); + +private: + z_stream _z_strm; + +}; + +#endif diff --git a/common/canvas_base.c b/common/canvas_base.c index 26bc52c6..0148270f 100644 --- a/common/canvas_base.c +++ b/common/canvas_base.c @@ -186,6 +186,7 @@ typedef struct CanvasBase { LzData lz_data; GlzData glz_data; SpiceJpegDecoder* jpeg; + SpiceZlibDecoder* zlib; void *usr_data; spice_destroy_fn_t usr_data_destroy; @@ -817,6 +818,21 @@ static pixman_image_t *canvas_get_lz(CanvasBase *canvas, LZImage *image, int inv return lz_data->decode_data.out_surface; } +static pixman_image_t *canvas_get_glz_rgb_common(CanvasBase *canvas, uint8_t *data, + int want_original) +{ + if (canvas->glz_data.decoder == NULL) { + CANVAS_ERROR("glz not supported"); + } + + canvas->glz_data.decoder->ops->decode(canvas->glz_data.decoder, + data, NULL, + &canvas->glz_data.decode_data); + + /* global_decode calls alloc_lz_image, which sets canvas->glz_data.surface */ + return (canvas->glz_data.decode_data.out_surface); +} + // don't handle plts since bitmaps with plt can be decoded globally to RGB32 (because // same byte sequence can be transformed to different RGB pixels by different plts) static pixman_image_t *canvas_get_glz(CanvasBase *canvas, LZImage *image, @@ -827,15 +843,25 @@ static pixman_image_t *canvas_get_glz(CanvasBase *canvas, LZImage *image, canvas->glz_data.decode_data.dc = canvas->dc; #endif - if (canvas->glz_data.decoder == NULL) { - CANVAS_ERROR("glz not supported"); + return canvas_get_glz_rgb_common(canvas, image->lz_rgb.data, want_original); +} + +static pixman_image_t *canvas_get_zlib_glz_rgb(CanvasBase *canvas, SpiceZlibGlzRGBImage *image, + int want_original) +{ + uint8_t *glz_data; + pixman_image_t *surface; + + if (canvas->zlib == NULL) { + CANVAS_ERROR("zlib not supported"); } - canvas->glz_data.decoder->ops->decode(canvas->glz_data.decoder, - image->lz_rgb.data, NULL, - &canvas->glz_data.decode_data); - /* global_decode calls alloc_lz_image, which sets canvas->glz_data.surface */ - return (canvas->glz_data.decode_data.out_surface); + glz_data = (uint8_t*)spice_malloc(image->zlib_glz.glz_data_size); + canvas->zlib->ops->decode(canvas->zlib, image->zlib_glz.data, image->zlib_glz.data_size, + glz_data, image->zlib_glz.glz_data_size); + surface = canvas_get_glz_rgb_common(canvas, glz_data, want_original); + free(glz_data); + return surface; } //#define DEBUG_DUMP_BITMAP @@ -1025,7 +1051,8 @@ static pixman_image_t *canvas_get_image_internal(CanvasBase *canvas, SPICE_ADDRE #ifdef SW_CANVAS_CACHE !(descriptor->flags & SPICE_IMAGE_FLAGS_CACHE_REPLACE_ME) && #endif - (descriptor->type != SPICE_IMAGE_TYPE_GLZ_RGB)) { + (descriptor->type != SPICE_IMAGE_TYPE_GLZ_RGB) && + (descriptor->type != SPICE_IMAGE_TYPE_ZLIB_GLZ_RGB)) { return NULL; } @@ -1067,8 +1094,12 @@ static pixman_image_t *canvas_get_image_internal(CanvasBase *canvas, SPICE_ADDRE surface = canvas_get_glz(canvas, image, want_original); break; } + case SPICE_IMAGE_TYPE_ZLIB_GLZ_RGB: { + SpiceZlibGlzRGBImage *image = (SpiceZlibGlzRGBImage *)descriptor; + surface = canvas_get_zlib_glz_rgb(canvas, image, want_original); + break; + } #endif - case SPICE_IMAGE_TYPE_FROM_CACHE: surface = canvas->bits_cache->ops->get(canvas->bits_cache, descriptor->id); break; @@ -3305,6 +3336,7 @@ static int canvas_base_init(CanvasBase *canvas, SpiceCanvasOps *ops, , SpiceImageSurfaces *surfaces , SpiceGlzDecoder *glz_decoder , SpiceJpegDecoder *jpeg_decoder + , SpiceZlibDecoder *zlib_decoder #ifndef SW_CANVAS_NO_CHUNKS , SpiceVirtMapping *virt_mapping #endif @@ -3339,6 +3371,7 @@ static int canvas_base_init(CanvasBase *canvas, SpiceCanvasOps *ops, canvas->surfaces = surfaces; canvas->glz_data.decoder = glz_decoder; canvas->jpeg = jpeg_decoder; + canvas->zlib = zlib_decoder; canvas->format = format; diff --git a/common/canvas_base.h b/common/canvas_base.h index b54fce57..9dfbe854 100644 --- a/common/canvas_base.h +++ b/common/canvas_base.h @@ -32,6 +32,7 @@ typedef struct _SpiceImageSurfaces SpiceImageSurfaces; typedef struct _SpicePaletteCache SpicePaletteCache; typedef struct _SpiceGlzDecoder SpiceGlzDecoder; typedef struct _SpiceJpegDecoder SpiceJpegDecoder; +typedef struct _SpiceZlibDecoder SpiceZlibDecoder; typedef struct _SpiceVirtMapping SpiceVirtMapping; typedef struct _SpiceCanvas SpiceCanvas; @@ -107,6 +108,18 @@ struct _SpiceJpegDecoder { SpiceJpegDecoderOps *ops; }; +typedef struct { + void (*decode)(SpiceZlibDecoder *decoder, + uint8_t *data, + int data_size, + uint8_t *dest, + int dest_size); +} SpiceZlibDecoderOps; + +struct _SpiceZlibDecoder { + SpiceZlibDecoderOps *ops; +}; + typedef struct { void *(*get_virt)(SpiceVirtMapping *mapping, unsigned long addr, uint32_t add_size); void (*validate_virt)(SpiceVirtMapping *mapping, unsigned long virt, diff --git a/common/gdi_canvas.c b/common/gdi_canvas.c index fea23906..76a76740 100644 --- a/common/gdi_canvas.c +++ b/common/gdi_canvas.c @@ -1851,6 +1851,7 @@ SpiceCanvas *gdi_canvas_create(int width, int height, , SpiceImageSurfaces *surfaces , SpiceGlzDecoder *glz_decoder , SpiceJpegDecoder *jpeg_decoder + , SpiceZlibDecoder *zlib_decoder ) { GdiCanvas *canvas; @@ -1870,7 +1871,8 @@ SpiceCanvas *gdi_canvas_create(int width, int height, #endif , surfaces , glz_decoder - , jpeg_decoder); + , jpeg_decoder + , zlib_decoder); canvas->dc = dc; canvas->lock = lock; return (SpiceCanvas *)canvas; diff --git a/common/gdi_canvas.h b/common/gdi_canvas.h index 02e053d2..3fdf07e9 100644 --- a/common/gdi_canvas.h +++ b/common/gdi_canvas.h @@ -32,7 +32,8 @@ SpiceCanvas *gdi_canvas_create(int width, int height, SpicePaletteCache *palette_cache, SpiceImageSurfaces *surfaces, SpiceGlzDecoder *glz_decoder, - SpiceJpegDecoder *jpeg_decoder); + SpiceJpegDecoder *jpeg_decoder, + SpiceZlibDecoder *zlib_decoder); void gdi_canvas_init(); diff --git a/common/gl_canvas.c b/common/gl_canvas.c index 444fa4bb..688b635e 100644 --- a/common/gl_canvas.c +++ b/common/gl_canvas.c @@ -817,6 +817,7 @@ SpiceCanvas *gl_canvas_create(int width, int height, uint32_t format , SpiceImageSurfaces *surfaces , SpiceGlzDecoder *glz_decoder , SpiceJpegDecoder *jpeg_decoder + , SpiceZlibDecoder *zlib_decoder #ifndef SW_CANVAS_NO_CHUNKS , SpiceVirtMapping *virt_mapping #endif @@ -845,6 +846,7 @@ SpiceCanvas *gl_canvas_create(int width, int height, uint32_t format , surfaces , glz_decoder , jpeg_decoder + , zlib_decoder #ifndef SW_CANVAS_NO_CHUNKS , virt_mapping #endif diff --git a/common/gl_canvas.h b/common/gl_canvas.h index cd76f8d9..d3f707a8 100644 --- a/common/gl_canvas.h +++ b/common/gl_canvas.h @@ -31,6 +31,7 @@ SpiceCanvas *gl_canvas_create(int width, int height, uint32_t format , SpiceImageSurfaces *surfaces , SpiceGlzDecoder *glz_decoder , SpiceJpegDecoder *jpeg_decoder + , SpiceZlibDecoder *zlib_decoder #ifndef SW_CANVAS_NO_CHUNKS , SpiceVirtMapping *virt_mapping #endif diff --git a/common/sw_canvas.c b/common/sw_canvas.c index c1a7392a..56865c31 100644 --- a/common/sw_canvas.c +++ b/common/sw_canvas.c @@ -1172,6 +1172,7 @@ static SpiceCanvas *canvas_create_common(pixman_image_t *image, , SpiceImageSurfaces *surfaces , SpiceGlzDecoder *glz_decoder , SpiceJpegDecoder *jpeg_decoder + , SpiceZlibDecoder *zlib_decoder #ifndef SW_CANVAS_NO_CHUNKS , SpiceVirtMapping *virt_mapping #endif @@ -1200,6 +1201,7 @@ static SpiceCanvas *canvas_create_common(pixman_image_t *image, , surfaces , glz_decoder , jpeg_decoder + , zlib_decoder #ifndef SW_CANVAS_NO_CHUNKS , virt_mapping #endif @@ -1222,6 +1224,7 @@ SpiceCanvas *canvas_create(int width, int height, uint32_t format , SpiceImageSurfaces *surfaces , SpiceGlzDecoder *glz_decoder , SpiceJpegDecoder *jpeg_decoder + , SpiceZlibDecoder *zlib_decoder #ifndef SW_CANVAS_NO_CHUNKS , SpiceVirtMapping *virt_mapping #endif @@ -1242,6 +1245,7 @@ SpiceCanvas *canvas_create(int width, int height, uint32_t format , surfaces , glz_decoder , jpeg_decoder + , zlib_decoder #ifndef SW_CANVAS_NO_CHUNKS , virt_mapping #endif @@ -1259,6 +1263,7 @@ SpiceCanvas *canvas_create_for_data(int width, int height, uint32_t format, , SpiceImageSurfaces *surfaces , SpiceGlzDecoder *glz_decoder , SpiceJpegDecoder *jpeg_decoder + , SpiceZlibDecoder *zlib_decoder #ifndef SW_CANVAS_NO_CHUNKS , SpiceVirtMapping *virt_mapping #endif @@ -1279,6 +1284,7 @@ SpiceCanvas *canvas_create_for_data(int width, int height, uint32_t format, , surfaces , glz_decoder , jpeg_decoder + , zlib_decoder #ifndef SW_CANVAS_NO_CHUNKS , virt_mapping #endif diff --git a/common/sw_canvas.h b/common/sw_canvas.h index 63a78630..05e4071f 100644 --- a/common/sw_canvas.h +++ b/common/sw_canvas.h @@ -36,6 +36,7 @@ SpiceCanvas *canvas_create(int width, int height, uint32_t format , SpiceImageSurfaces *surfaces , SpiceGlzDecoder *glz_decoder , SpiceJpegDecoder *jpeg_decoder + , SpiceZlibDecoder *zlib_decoder #ifndef SW_CANVAS_NO_CHUNKS , SpiceVirtMapping *virt_mapping #endif @@ -51,6 +52,7 @@ SpiceCanvas *canvas_create_for_data(int width, int height, uint32_t format, uint , SpiceImageSurfaces *surfaces , SpiceGlzDecoder *glz_decoder , SpiceJpegDecoder *jpeg_decoder + , SpiceZlibDecoder *zlib_decoder #ifndef SW_CANVAS_NO_CHUNKS , SpiceVirtMapping *virt_mapping #endif diff --git a/configure.ac b/configure.ac index ef5f7f78..0e91547f 100644 --- a/configure.ac +++ b/configure.ac @@ -234,6 +234,9 @@ AC_CHECK_LIB(jpeg, jpeg_destroy_decompress, AC_MSG_ERROR([libjpeg not found])) AC_SUBST(JPEG_LIBS) +AC_CHECK_LIB(z, deflate, Z_LIBS='-lz', AC_MSG_ERROR([zlib not found])) +AC_SUBST(Z_LIBS) + dnl =========================================================================== dnl check compiler flags 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 + +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 diff --git a/spice.proto b/spice.proto index 6cedfac1..84d3fa98 100644 --- a/spice.proto +++ b/spice.proto @@ -293,6 +293,7 @@ enum8 image_type { SURFACE, JPEG, FROM_CACHE_LOSSLESS, + ZLIB_GLZ_RGB, }; flags8 image_flags { @@ -470,6 +471,12 @@ struct LZPLTData { uint8 data[data_size] @end @nomarshal; }; +struct ZlibGlzRGBData { + uint32 glz_data_size; + uint32 data_size; + uint8 data[data_size] @end @nomarshal; +} @ctype(SpiceZlibGlzRGBData); + struct Surface { uint32 surface_id; }; @@ -491,6 +498,8 @@ struct Image { BinaryData binary_data @ctype(SpiceQUICData); case LZ_PLT: LZPLTData lzplt_data @ctype(SpiceLZPLTData); + case ZLIB_GLZ_RGB: + ZlibGlzRGBData zlib_glz_data @ctype(SpiceZlibGlzRGBData); case SURFACE: Surface surface_data; } u @end; -- cgit