summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYonit Halperin <yhalperi@redhat.com>2010-06-20 15:24:49 +0300
committerAlexander Larsson <alexl@redhat.com>2010-06-21 15:05:37 +0200
commit25bb38f643af6f0015df369a22176275b6ebfae0 (patch)
tree5bfc75812c6ba89086d1ab8782552de806fd53bb
parentcfc1e95bda0e150b3de225c3572bb1004dad070e (diff)
downloadspice-25bb38f643af6f0015df369a22176275b6ebfae0.tar.gz
spice-25bb38f643af6f0015df369a22176275b6ebfae0.tar.xz
spice-25bb38f643af6f0015df369a22176275b6ebfae0.zip
applying zlib compression over glz on WAN connection
-rw-r--r--client/Makefile.am2
-rw-r--r--client/canvas.h3
-rw-r--r--client/red_gdi_canvas.cpp3
-rw-r--r--client/red_gl_canvas.cpp3
-rw-r--r--client/red_sw_canvas.cpp6
-rw-r--r--client/windows/redc.vcproj12
-rw-r--r--client/x11/Makefile.am3
-rw-r--r--client/zlib_decoder.cpp58
-rw-r--r--client/zlib_decoder.h41
-rw-r--r--common/canvas_base.c51
-rw-r--r--common/canvas_base.h13
-rw-r--r--common/gdi_canvas.c4
-rw-r--r--common/gdi_canvas.h3
-rw-r--r--common/gl_canvas.c2
-rw-r--r--common/gl_canvas.h1
-rw-r--r--common/sw_canvas.c6
-rw-r--r--common/sw_canvas.h2
-rw-r--r--configure.ac3
-rw-r--r--server/Makefile.am3
-rw-r--r--server/red_worker.c156
-rw-r--r--server/zlib_encoder.c104
-rw-r--r--server/zlib_encoder.h47
-rw-r--r--spice.proto9
23 files changed, 504 insertions, 31 deletions
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 @@
/>
<Tool
Name="VCLinkerTool"
- AdditionalDependencies="log4cppD.lib pixman-1D.lib libeay32MTd.lib ssleay32MTd.lib ws2_32.lib msimg32.lib winmm.lib libcelt_0_5_1D.lib pthreadVC2d.lib version.lib CEGUIBase_Static_d.lib CEGUITGAImageCodec_Static_d.lib CEGUIExpatParser_Static_d.lib freetype2312MT_D.lib libexpatMT_D.lib pcre_D.lib CEGUIFalagardWRBase_Static_d.lib libjpeg-static-mt-debug.lib"
+ AdditionalDependencies="log4cppD.lib pixman-1D.lib libeay32MTd.lib ssleay32MTd.lib ws2_32.lib msimg32.lib winmm.lib libcelt_0_5_1D.lib pthreadVC2d.lib version.lib CEGUIBase_Static_d.lib CEGUITGAImageCodec_Static_d.lib CEGUIExpatParser_Static_d.lib freetype2312MT_D.lib libexpatMT_D.lib pcre_D.lib CEGUIFalagardWRBase_Static_d.lib libjpeg-static-mt-debug.lib zlibwapi.lib"
OutputFile="$(OutDir)\spicec.exe"
LinkIncremental="2"
AdditionalLibraryDirectories="&quot;$(SPICE_LIBS)\lib&quot;"
@@ -148,7 +148,7 @@
/>
<Tool
Name="VCLinkerTool"
- AdditionalDependencies="log4cpp.lib pixman-1.lib libeay32MT.lib ssleay32MT.lib ws2_32.lib msimg32.lib winmm.lib libcelt_0_5_1.lib pthreadVC2.lib version.lib CEGUIBase_Static.lib CEGUITGAImageCodec_Static.lib CEGUIExpatParser_Static.lib freetype2312MT.lib libexpatMT.lib pcre.lib CEGUIFalagardWRBase_Static.lib libjpeg-static-mt.lib"
+ AdditionalDependencies="log4cpp.lib pixman-1.lib libeay32MT.lib ssleay32MT.lib ws2_32.lib msimg32.lib winmm.lib libcelt_0_5_1.lib pthreadVC2.lib version.lib CEGUIBase_Static.lib CEGUITGAImageCodec_Static.lib CEGUIExpatParser_Static.lib freetype2312MT.lib libexpatMT.lib pcre.lib CEGUIFalagardWRBase_Static.lib libjpeg-static-mt.lib zlibwapi.lib"
OutputFile="$(OutDir)\spicec.exe"
LinkIncremental="1"
AdditionalLibraryDirectories="&quot;$(SPICE_LIBS)\lib&quot;"
@@ -437,6 +437,10 @@
RelativePath="..\utils.cpp"
>
</File>
+ <File
+ RelativePath="..\zlib_decoder.cpp"
+ >
+ </File>
</Filter>
<Filter
Name="Header Files"
@@ -671,6 +675,10 @@
RelativePath=".\win_platform.h"
>
</File>
+ <File
+ RelativePath="..\zlib_decoder.h"
+ >
+ </File>
</Filter>
<Filter
Name="Resource Files"
diff --git a/client/x11/Makefile.am b/client/x11/Makefile.am
index 3d90e9f0..81036fb3 100644
--- a/client/x11/Makefile.am
+++ b/client/x11/Makefile.am
@@ -112,6 +112,8 @@ RED_COMMON_SRCS = \
$(CLIENT_DIR)/tunnel_channel.h \
$(CLIENT_DIR)/utils.cpp \
$(CLIENT_DIR)/utils.h \
+ $(CLIENT_DIR)/zlib_decoder.cpp \
+ $(CLIENT_DIR)/zlib_decoder.h \
$(CLIENT_DIR)/icon.h \
$(CLIENT_DIR)/gui/softrenderer.h \
$(CLIENT_DIR)/gui/softrenderer.cpp \
@@ -174,6 +176,7 @@ spicec_LDFLAGS = \
$(SSL_LIBS) \
$(CEGUI_LIBS) \
$(JPEG_LIBS) \
+ $(Z_LIBS) \
$(SPICE_NONPKGCONFIG_LIBS)
spicec_LDADD = \
diff --git a/client/zlib_decoder.cpp b/client/zlib_decoder.cpp
new file mode 100644
index 00000000..68b1b339
--- /dev/null
+++ b/client/zlib_decoder.cpp
@@ -0,0 +1,58 @@
+#include "common.h"
+#include "zlib_decoder.h"
+#include "debug.h"
+#include "utils.h"
+
+static void op_decode(SpiceZlibDecoder *decoder,
+ uint8_t *data,
+ int data_size,
+ uint8_t *dest,
+ int dest_size)
+{
+ ZlibDecoder* _decoder = static_cast<ZlibDecoder*>(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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _H_ZLIB_DECODER
+#define _H_ZLIB_DECODER
+
+#include "common.h"
+#include "canvas_base.h"
+
+#define ZLIB_WINAPI
+#include <zlib.h>
+
+
+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;
@@ -108,6 +109,18 @@ struct _SpiceJpegDecoder {
};
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,
unsigned long from_addr, uint32_t add_size);
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 <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
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;