Patch to support LZ4 compression algorithm

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello

My name is Javier Celaya and I work for Flexible Solutions [1]. We develop a 
virtualization product that includes a VDI module with Spice. As part of this 
module, we have included support for the LZ4 compression algorithm [2], and we 
would like to contribute it to the main project.

The LZ4 algorithm focuses on speed sacrificing compression ratio. In our tests, 
we obtained a compression ratio of x1.75~x2, while QUIC obtained ~x4. However, 
LZ4 was around 4~5 times faster than QUIC or GLZ. The objective was to 
implement a Spice client for devices with limited CPU performance, like 
smartphones or ARM set-top-boxes.

We have tried to keep things simple. Our approach consists in replacing the LZ 
algorithm with LZ4 when Spice is built with the --enable-lz4 switch. Adding it 
as a new option would also require changes in the software that uses the 
server, like QEMU. Then, each image is compressed independently. Since the LZ4 
dictionary size is quite small (64KB), there is no improvement in compressing 
all the images as a stream.

I send you three patches, for spice-common, spice and spice-gtk. The 
compression code is in spice, the decompression is in spice-common, and
spice-gtk only contains the macros needed to find liblz4 during configure.

We are open to comments and critics.
Best regards

[1] http://flexvm.es/
[2] https://code.google.com/p/lz4/
diff --git a/common/canvas_base.c b/common/canvas_base.c
index 2753fae..a1bfc27 100644
--- a/common/canvas_base.c
+++ b/common/canvas_base.c
@@ -26,6 +26,10 @@
 #include <stdio.h>
 #include <math.h>
 
+#ifdef USE_LZ4
+#include <arpa/inet.h>
+#include <lz4.h>
+#endif
 #include <spice/macros.h>
 #include "log.h"
 #include "quic.h"
@@ -543,6 +547,64 @@ static pixman_image_t *canvas_get_jpeg(CanvasBase *canvas, SpiceImage *image, in
     return surface;
 }
 
+#ifdef USE_LZ4
+static pixman_image_t *canvas_get_lz4(CanvasBase *canvas, SpiceImage *image, int invers)
+{
+    pixman_image_t *surface = NULL;
+    int dec_size, enc_size;
+    int stride;
+    int stride_abs;
+    uint8_t *dest, *data, *data_end;
+    int width, height, direction;
+    LZ4_streamDecode_t *stream;
+
+    spice_chunks_linearize(image->u.lz4.data);
+    data = image->u.lz4.data->chunk[0].data;
+    data_end = data + image->u.lz4.data->chunk[0].len;
+    width = image->descriptor.width;
+    height = image->descriptor.height;
+    direction = *(data++);
+
+    surface = surface_create(
+#ifdef WIN32
+                             canvas->dc,
+#endif
+                             PIXMAN_a8r8g8b8,
+                             width, height, direction == 0);
+    if (surface == NULL) {
+        spice_warning("create surface failed");
+        return NULL;
+    }
+
+    stream = LZ4_createStreamDecode();
+    dest = (uint8_t *)pixman_image_get_data(surface);
+    stride = pixman_image_get_stride(surface);
+    stride_abs = abs(stride);
+    if (direction == 1) {
+        dest -= (stride_abs * (height - 1));
+    }
+
+    do {
+        // Read next compressed block
+        enc_size = ntohl(*((uint32_t *)data));
+        data += 4;
+        dec_size = LZ4_decompress_safe_continue(stream, (const char *) data,
+                                                (char *) dest, enc_size, height * stride_abs);
+        if (dec_size <= 0) {
+            spice_warning("Error decoding LZ4 block\n");
+            pixman_image_unref(surface);
+            surface = NULL;
+            break;
+        }
+        dest += dec_size;
+        data += enc_size;
+    } while (data < data_end);
+
+    LZ4_freeStreamDecode(stream);
+    return surface;
+}
+#endif
+
 static pixman_image_t *canvas_get_jpeg_alpha(CanvasBase *canvas,
                                              SpiceImage *image, int invers)
 {
@@ -1119,6 +1181,15 @@ static pixman_image_t *canvas_get_image_internal(CanvasBase *canvas, SpiceImage
         surface = canvas_get_jpeg_alpha(canvas, image, 0);
         break;
     }
+    case SPICE_IMAGE_TYPE_LZ4: {
+#ifdef USE_LZ4
+        surface = canvas_get_lz4(canvas, image, 0);
+#else
+        spice_warning("Lz4 compression algorithm not supported.\n");
+        surface = NULL;
+#endif
+        break;
+    }
 #if defined(SW_CANVAS_CACHE)
     case SPICE_IMAGE_TYPE_GLZ_RGB: {
         surface = canvas_get_glz(canvas, image, want_original);
diff --git a/common/draw.h b/common/draw.h
index 3704358..1155dc5 100644
--- a/common/draw.h
+++ b/common/draw.h
@@ -120,7 +120,7 @@ typedef struct SpiceSurface {
 typedef struct SpiceQUICData {
     uint32_t data_size;
     SpiceChunks *data;
-} SpiceQUICData, SpiceLZRGBData, SpiceJPEGData;
+} SpiceQUICData, SpiceLZRGBData, SpiceJPEGData, SpiceLZ4Data;
 
 typedef struct SpiceLZPLTData {
     uint8_t flags;
@@ -153,6 +153,7 @@ typedef struct SpiceImage {
         SpiceLZRGBData      lz_rgb;
         SpiceLZPLTData      lz_plt;
         SpiceJPEGData       jpeg;
+        SpiceLZ4Data        lz4;
         SpiceZlibGlzRGBData zlib_glz;
         SpiceJPEGAlphaData  jpeg_alpha;
     } u;
diff --git a/spice.proto b/spice.proto
index 316d287..01493c9 100644
--- a/spice.proto
+++ b/spice.proto
@@ -358,6 +358,7 @@ enum8 image_type {
     FROM_CACHE_LOSSLESS,
     ZLIB_GLZ_RGB,
     JPEG_ALPHA,
+    LZ4,
 };
 
 flags8 image_flags {
@@ -568,6 +569,8 @@ struct Image {
         BinaryData lz_rgb;
     case JPEG:
         BinaryData jpeg;
+    case LZ4:
+        BinaryData lz4;
     case LZ_PLT:
         LZPLTData lz_plt;
     case ZLIB_GLZ_RGB:
diff --git a/configure.ac b/configure.ac
index f47ee20..d295c5b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -694,6 +694,24 @@ if test "x$enable_dbus" != "xno"; then
   fi
 fi
 
+AC_ARG_ENABLE([lz4],
+  AS_HELP_STRING([--enable-lz4=@<:@yes/no@:>@],
+                 [Enable lz4 compression algorithm @<:@default=no@:>@]),
+  [],
+  [enable_lz4="no"])
+
+if test "x$enable_lz4" = "xyes"; then
+    AC_CHECK_LIB(lz4, LZ4_compress_limitedOutput, LZ4_LIBS='-llz4', enable_lz4=no)
+    AC_CHECK_HEADER([lz4.h], [], [enable_lz4=no])
+    AC_DEFINE([USE_LZ4], [1], [Define to build with Lz4 support])
+
+    if test "x$enable_lz4" = "xno"; then
+        AC_MSG_ERROR([lz4 not found])
+    fi
+fi
+AC_SUBST(LZ4_LIBS)
+
+
 dnl ===========================================================================
 dnl check compiler flags
 
@@ -765,6 +783,7 @@ AC_MSG_NOTICE([
         USB redirection support:  ${have_usbredir} ${with_usbredir_hotplug}
         DBus:                     ${have_dbus}
         WebDAV support:           ${have_phodav}
+        LZ4 support:              ${enable_lz4}
 
         Now type 'make' to build $PACKAGE
 
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 3d87958..f799543 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -194,6 +194,7 @@ libspice_client_glib_2_0_la_LIBADD =					\
 	$(OPUS_LIBS)							\
 	$(JPEG_LIBS)							\
 	$(Z_LIBS)							\
+	$(LZ4_LIBS)							\
 	$(PIXMAN_LIBS)							\
 	$(SSL_LIBS)							\
 	$(PULSE_LIBS)							\
diff --git a/configure.ac b/configure.ac
index cedeb40..89f36cf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -124,6 +124,12 @@ AC_ARG_ENABLE(opengl,
 AS_IF([test x"$enable_opengl" != "xno"], [enable_opengl="yes"])
 AM_CONDITIONAL(SUPPORT_GL, test "x$enable_opengl" = "xyes")
 
+AC_ARG_ENABLE(lz4,
+[  --enable-lz4         Enable lz4 compression algorithm],,
+[enable_lz4="no"])
+AS_IF([test x"$enable_lz4" != "xno"], [enable_lz4="yes"])
+AM_CONDITIONAL(SUPPORT_LZ4, test "x$enable_lz4" = "xyes")
+
 AC_ARG_ENABLE(smartcard,
 [  --enable-smartcard         Enable network redirection],,
 [enable_smartcard="no"])
@@ -268,6 +274,17 @@ AC_SUBST(GL_CFLAGS)
 AC_SUBST(GL_LIBS)
 SPICE_NONPKGCONFIG_LIBS+=" $GL_LIBS"
 
+if test "x$enable_lz4" = "xyes"; then
+    AC_CHECK_LIB(lz4, LZ4_compress_limitedOutput, LZ4_LIBS='-llz4', enable_lz4=no)
+    AC_CHECK_HEADER([lz4.h], [], [enable_lz4=no])
+    AC_DEFINE([USE_LZ4], [1], [Define to build with Lz4 support])
+
+    if test "x$enable_lz4" = "xno"; then
+        AC_MSG_ERROR([lz4 not found])
+    fi
+fi
+AC_SUBST(LZ4_LIBS)
+
 if test "x$red_target" = "xx11" && test "x$enable_client" = "xyes" ; then
 	if test "$os_linux" = yes; then
 		PKG_CHECK_MODULES(ALSA, alsa)
@@ -535,6 +552,8 @@ echo "
 
         GUI:                      ${enable_gui}
 " ; fi ; echo "\
+        LZ4 support:              ${enable_lz4}
+
         Smartcard:                ${enable_smartcard}
 
         SASL support:             ${enable_sasl}
diff --git a/server/Makefile.am b/server/Makefile.am
index 34219c8..acff1a7 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -35,6 +35,7 @@ libspice_server_la_LIBADD =						\
 	$(GL_LIBS)							\
 	$(GLIB2_LIBS)							\
 	$(JPEG_LIBS)							\
+	$(LZ4_LIBS)							\
 	$(LIBRT)							\
 	$(PIXMAN_LIBS)							\
 	$(SASL_LIBS)							\
@@ -60,6 +61,8 @@ libspice_server_la_SOURCES =			\
 	inputs_channel.h			\
 	jpeg_encoder.c				\
 	jpeg_encoder.h				\
+	lz4_encoder.c				\
+	lz4_encoder.h				\
 	main_channel.c				\
 	main_channel.h				\
 	mjpeg_encoder.c				\
diff --git a/server/lz4_encoder.c b/server/lz4_encoder.c
new file mode 100644
index 0000000..cbcd5df
--- /dev/null
+++ b/server/lz4_encoder.c
@@ -0,0 +1,123 @@
+/*
+   Copyright (C) 2014 Flexible Software Solutions S.L.
+
+   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/>.
+*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef USE_LZ4
+
+#define SPICE_LOG_DOMAIN "SpiceLz4Encoder"
+
+#include <arpa/inet.h>
+#include <lz4.h>
+#include "red_common.h"
+#include "lz4_encoder.h"
+
+typedef struct Lz4Encoder {
+    Lz4EncoderUsrContext *usr;
+} Lz4Encoder;
+
+Lz4EncoderContext* lz4_encoder_create(Lz4EncoderUsrContext *usr)
+{
+    Lz4Encoder *enc;
+    if (!usr->more_space || !usr->more_lines) {
+        return NULL;
+    }
+
+    enc = spice_new0(Lz4Encoder, 1);
+    enc->usr = usr;
+
+    return (Lz4EncoderContext*)enc;
+}
+
+void lz4_encoder_destroy(Lz4EncoderContext* encoder)
+{
+    free(encoder);
+}
+
+int lz4_encode(Lz4EncoderContext *lz4, int height, int stride,
+               uint8_t *io_ptr, unsigned int num_io_bytes)
+{
+    Lz4Encoder *enc = (Lz4Encoder *)lz4;
+    uint8_t *lines;
+    int num_lines = 0;
+    int total_lines = 0;
+    int in_size, enc_size, out_size = 0, already_copied;
+    int stride_abs = abs(stride);
+    uint8_t *in_buf, *compressed_lines;
+    uint8_t *out_buf = io_ptr;
+    LZ4_stream_t *stream = LZ4_createStream();
+
+    // Encode direction
+    *(out_buf++) = stride < 0 ? 1 : 0;
+    num_io_bytes--;
+
+    do {
+        num_lines = enc->usr->more_lines(enc->usr, &lines);
+        if (num_lines <= 0) {
+            spice_error("more lines failed");
+            LZ4_freeStream(stream);
+            return 0;
+        }
+        in_buf = stride < 0 ? lines - (stride_abs * (num_lines - 1)) : lines;
+        lines += stride * num_lines;
+        in_size = stride_abs * num_lines;
+        compressed_lines = (uint8_t *) malloc(LZ4_compressBound(in_size) + 4);
+        enc_size = LZ4_compress_continue(stream, (const char *) in_buf,
+                                         (char *) compressed_lines + 4, in_size);
+        if (enc_size <= 0) {
+            spice_error("compress failed!");
+            free(compressed_lines);
+            LZ4_freeStream(stream);
+            return 0;
+        }
+        *((uint32_t *)compressed_lines) = htonl(enc_size);
+
+        out_size += enc_size += 4;
+        already_copied = 0;
+        while (num_io_bytes < enc_size) {
+            memcpy(out_buf, compressed_lines + already_copied, num_io_bytes);
+            already_copied += num_io_bytes;
+            enc_size -= num_io_bytes;
+            num_io_bytes = enc->usr->more_space(enc->usr, &io_ptr);
+            if (num_io_bytes <= 0) {
+                spice_error("more space failed");
+                free(compressed_lines);
+                LZ4_freeStream(stream);
+                return 0;
+            }
+            out_buf = io_ptr;
+        }
+        memcpy(out_buf, compressed_lines + already_copied, enc_size);
+        out_buf += enc_size;
+        num_io_bytes -= enc_size;
+
+        free(compressed_lines);
+        total_lines += num_lines;
+    } while (total_lines < height);
+
+    LZ4_freeStream(stream);
+    if (total_lines != height) {
+        spice_error("too many lines\n");
+        out_size = 0;
+    }
+
+    return out_size;
+}
+
+#endif // USE_LZ4
+
diff --git a/server/lz4_encoder.h b/server/lz4_encoder.h
new file mode 100644
index 0000000..41e41c5
--- /dev/null
+++ b/server/lz4_encoder.h
@@ -0,0 +1,51 @@
+/*
+   Copyright (C) 2014 Flexible Software Solutions S.L.
+
+   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_LZ4_ENCODER
+#define _H_LZ4_ENCODER
+
+#include <spice/types.h>
+
+typedef void* Lz4EncoderContext;
+typedef struct Lz4EncoderUsrContext Lz4EncoderUsrContext;
+
+struct Lz4EncoderUsrContext {
+    int (*more_space)(Lz4EncoderUsrContext *usr, uint8_t **io_ptr);
+    int (*more_lines)(Lz4EncoderUsrContext *usr, uint8_t **lines);
+};
+
+Lz4EncoderContext* lz4_encoder_create(Lz4EncoderUsrContext *usr);
+void lz4_encoder_destroy(Lz4EncoderContext *encoder);
+
+/* returns the total size of the encoded data. Images must be supplied from the
+   top line to the bottom */
+int lz4_encode(Lz4EncoderContext *lz4, int height, int stride,
+               uint8_t *io_ptr, unsigned int num_io_bytes);
+#endif
+
diff --git a/server/red_worker.c b/server/red_worker.c
index e177b68..e3e0cff 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -74,6 +74,9 @@
 #include "red_memslots.h"
 #include "red_parse_qxl.h"
 #include "jpeg_encoder.h"
+#ifdef USE_LZ4
+#include "lz4_encoder.h"
+#endif
 #include "demarshallers.h"
 #include "zlib_encoder.h"
 #include "red_channel.h"
@@ -237,6 +240,7 @@ static const char *quic_stat_name = "quic";
 static const char *jpeg_stat_name = "jpeg";
 static const char *zlib_stat_name = "zlib_glz";
 static const char *jpeg_alpha_stat_name = "jpeg_alpha";
+static const char *lz4_stat_name = "lz4";
 
 static inline void stat_compress_init(stat_info_t *info, const char *name)
 {
@@ -600,6 +604,11 @@ typedef struct {
 } JpegData;
 
 typedef struct {
+    Lz4EncoderUsrContext usr;
+    EncoderData data;
+} Lz4Data;
+
+typedef struct {
     ZlibEncoderUsrContext usr;
     EncoderData data;
 } ZlibData;
@@ -740,6 +749,7 @@ struct DisplayChannel {
     stat_info_t jpeg_stat;
     stat_info_t zlib_glz_stat;
     stat_info_t jpeg_alpha_stat;
+    stat_info_t lz4_stat;
 #endif
 };
 
@@ -998,6 +1008,11 @@ typedef struct RedWorker {
     JpegData jpeg_data;
     JpegEncoderContext *jpeg;
 
+#ifdef USE_LZ4
+    Lz4Data lz4_data;
+    Lz4EncoderContext *lz4;
+#endif
+
     ZlibData zlib_data;
     ZlibEncoder *zlib;
 
@@ -1190,27 +1205,37 @@ static void print_compress_stats(DisplayChannel *display_channel)
                stat_byte_to_mega(display_channel->jpeg_alpha_stat.comp_size),
                stat_cpu_time_to_sec(display_channel->jpeg_alpha_stat.total)
                );
+    spice_info("LZ4      \t%8d\t%13.2f\t%12.2f\t%12.2f",
+               display_channel->lz4_stat.count,
+               stat_byte_to_mega(display_channel->lz4_stat.orig_size),
+               stat_byte_to_mega(display_channel->lz4_stat.comp_size),
+               stat_cpu_time_to_sec(display_channel->lz4_stat.total)
+               );
     spice_info("-------------------------------------------------------------------");
     spice_info("Total    \t%8d\t%13.2f\t%12.2f\t%12.2f",
                display_channel->lz_stat.count + display_channel->glz_stat.count +
                                                 display_channel->quic_stat.count +
                                                 display_channel->jpeg_stat.count +
+                                                display_channel->lz4_stat.count +
                                                 display_channel->jpeg_alpha_stat.count,
                stat_byte_to_mega(display_channel->lz_stat.orig_size +
                                  display_channel->glz_stat.orig_size +
                                  display_channel->quic_stat.orig_size +
                                  display_channel->jpeg_stat.orig_size +
+                                 display_channel->lz4_stat.orig_size +
                                  display_channel->jpeg_alpha_stat.orig_size),
                stat_byte_to_mega(display_channel->lz_stat.comp_size +
                                  glz_enc_size +
                                  display_channel->quic_stat.comp_size +
                                  display_channel->jpeg_stat.comp_size +
+                                 display_channel->lz4_stat.comp_size +
                                  display_channel->jpeg_alpha_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 +
+                                    display_channel->lz4_stat.total +
                                     display_channel->jpeg_alpha_stat.total)
                );
 }
@@ -5723,6 +5748,14 @@ static int jpeg_usr_more_space(JpegEncoderUsrContext *usr, uint8_t **io_ptr)
     return (encoder_usr_more_space(usr_data, (uint32_t **)io_ptr) << 2);
 }
 
+#ifdef USE_LZ4
+static int lz4_usr_more_space(Lz4EncoderUsrContext *usr, uint8_t **io_ptr)
+{
+    EncoderData *usr_data = &(((Lz4Data *)usr)->data);
+    return (encoder_usr_more_space(usr_data, (uint32_t **)io_ptr) << 2);
+}
+#endif
+
 static int zlib_usr_more_space(ZlibEncoderUsrContext *usr, uint8_t **io_ptr)
 {
     EncoderData *usr_data = &(((ZlibData *)usr)->data);
@@ -5783,6 +5816,14 @@ static int jpeg_usr_more_lines(JpegEncoderUsrContext *usr, uint8_t **lines)
     return encoder_usr_more_lines(usr_data, lines);
 }
 
+#ifdef USE_LZ4
+static int lz4_usr_more_lines(Lz4EncoderUsrContext *usr, uint8_t **lines)
+{
+    EncoderData *usr_data = &(((Lz4Data *)usr)->data);
+    return encoder_usr_more_lines(usr_data, lines);
+}
+#endif
+
 static int zlib_usr_more_input(ZlibEncoderUsrContext *usr, uint8_t** input)
 {
     EncoderData *usr_data = &(((ZlibData *)usr)->data);
@@ -5884,6 +5925,20 @@ static inline void red_init_jpeg(RedWorker *worker)
     }
 }
 
+#ifdef USE_LZ4
+static inline void red_init_lz4(RedWorker *worker)
+{
+    worker->lz4_data.usr.more_space = lz4_usr_more_space;
+    worker->lz4_data.usr.more_lines = lz4_usr_more_lines;
+
+    worker->lz4 = lz4_encoder_create(&worker->lz4_data.usr);
+
+    if (!worker->lz4) {
+        spice_critical("create lz4 encoder failed");
+    }
+}
+#endif
+
 static inline void red_init_zlib(RedWorker *worker)
 {
     worker->zlib_data.usr.more_space = zlib_usr_more_space;
@@ -6353,6 +6408,80 @@ static int red_jpeg_compress_image(DisplayChannelClient *dcc, SpiceImage *dest,
     return TRUE;
 }
 
+#ifdef USE_LZ4
+static int red_lz4_compress_image(DisplayChannelClient *dcc, SpiceImage *dest,
+                                  SpiceBitmap *src, compress_send_data_t* o_comp_data,
+                                  uint32_t group_id)
+{
+    DisplayChannel *display_channel = DCC_TO_DC(dcc);
+    RedWorker *worker = display_channel->common.worker;
+    Lz4Data *lz4_data = &worker->lz4_data;
+    Lz4EncoderContext *lz4 = worker->lz4;
+    int lz4_size = 0;
+    int stride;
+
+#ifdef COMPRESS_STAT
+    stat_time_t start_time = stat_now();
+#endif
+
+    lz4_data->data.bufs_tail = red_display_alloc_compress_buf(dcc);
+    lz4_data->data.bufs_head = lz4_data->data.bufs_tail;
+
+    if (!lz4_data->data.bufs_head) {
+        spice_warning("failed to allocate compress buffer");
+        return FALSE;
+    }
+
+    lz4_data->data.bufs_head->send_next = NULL;
+    lz4_data->data.dcc = dcc;
+
+    if (setjmp(lz4_data->data.jmp_env)) {
+        while (lz4_data->data.bufs_head) {
+            RedCompressBuf *buf = lz4_data->data.bufs_head;
+            lz4_data->data.bufs_head = buf->send_next;
+            red_display_free_compress_buf(dcc, buf);
+        }
+        return FALSE;
+    }
+
+    if (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE) {
+        spice_chunks_linearize(src->data);
+    }
+
+    lz4_data->data.u.lines_data.chunks = src->data;
+    lz4_data->data.u.lines_data.stride = src->stride;
+    lz4_data->usr.more_lines = lz4_usr_more_lines;
+
+    if ((src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) {
+        lz4_data->data.u.lines_data.next = 0;
+        lz4_data->data.u.lines_data.reverse = 0;
+        stride = src->stride;
+    } else {
+        lz4_data->data.u.lines_data.next = src->data->num_chunks - 1;
+        lz4_data->data.u.lines_data.reverse = 1;
+        stride = -src->stride;
+    }
+
+    lz4_size = lz4_encode(lz4, src->y, stride, (uint8_t*)lz4_data->data.bufs_head->buf,
+                          sizeof(lz4_data->data.bufs_head->buf));
+
+    // the compressed buffer is bigger than the original data
+    if (lz4_size > (src->y * src->stride)) {
+        longjmp(lz4_data->data.jmp_env, 1);
+    }
+
+    dest->descriptor.type = SPICE_IMAGE_TYPE_LZ4;
+    dest->u.lz4.data_size = lz4_size;
+
+    o_comp_data->comp_buf = lz4_data->data.bufs_head;
+    o_comp_data->comp_buf_size = lz4_size;
+
+    stat_compress_add(&display_channel->lz4_stat, start_time, src->stride * src->y,
+                      o_comp_data->comp_buf_size);
+    return TRUE;
+}
+#endif
+
 static inline int red_quic_compress_image(DisplayChannelClient *dcc, SpiceImage *dest,
                                           SpiceBitmap *src, compress_send_data_t* o_comp_data,
                                           uint32_t group_id)
@@ -6541,8 +6670,13 @@ static inline int red_compress_image(DisplayChannelClient *dcc,
         }
 
         if (!glz) {
+#ifdef USE_LZ4
+            ret = red_lz4_compress_image(dcc, dest, src, o_comp_data,
+                                         drawable->group_id);
+#else
             ret = red_lz_compress_image(dcc, dest, src, o_comp_data,
                                         drawable->group_id);
+#endif
 #ifdef COMPRESS_DEBUG
             spice_info("LZ LOCAL compress");
 #endif
@@ -8773,9 +8907,15 @@ static void red_marshall_image(RedChannelClient *rcc, SpiceMarshaller *m, ImageI
                                                      &comp_send_data,
                                                      worker->mem_slots.internal_groupslot_id);
         } else {
+#ifdef USE_LZ4
+            comp_succeeded = red_lz4_compress_image(dcc, &red_image, &bitmap,
+                                                    &comp_send_data,
+                                                    worker->mem_slots.internal_groupslot_id);
+#else
             comp_succeeded = red_lz_compress_image(dcc, &red_image, &bitmap,
                                                    &comp_send_data,
                                                    worker->mem_slots.internal_groupslot_id);
+#endif
         }
     }
 
@@ -10565,6 +10705,7 @@ static void display_channel_create(RedWorker *worker, int migrate)
     stat_compress_init(&display_channel->jpeg_stat, jpeg_stat_name);
     stat_compress_init(&display_channel->zlib_glz_stat, zlib_stat_name);
     stat_compress_init(&display_channel->jpeg_alpha_stat, jpeg_alpha_stat_name);
+    stat_compress_init(&display_channel->lz4_stat, lz4_stat_name);
 }
 
 static void guest_set_client_capabilities(RedWorker *worker)
@@ -11610,6 +11751,7 @@ void handle_dev_set_compression(void *opaque, void *payload)
         stat_reset(&worker->display_channel->jpeg_stat);
         stat_reset(&worker->display_channel->zlib_glz_stat);
         stat_reset(&worker->display_channel->jpeg_alpha_stat);
+        stat_reset(&worker->display_channel->lz4_stat);
     }
 #endif
 }
@@ -12013,6 +12155,9 @@ SPICE_GNUC_NORETURN void *red_worker_main(void *arg)
     red_init_quic(worker);
     red_init_lz(worker);
     red_init_jpeg(worker);
+#ifdef USE_LZ4
+    red_init_lz4(worker);
+#endif
     red_init_zlib(worker);
     worker->event_timeout = INF_EVENT_WAIT;
     for (;;) {
_______________________________________________
Spice-devel mailing list
Spice-devel@xxxxxxxxxxxxxxxxxxxxx
http://lists.freedesktop.org/mailman/listinfo/spice-devel

[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]     [Monitors]