Hello Marc-André, I added the changes you asked for. A new compression type SPICE_IMAGE_COMPRESS_LZ4 has been added, so that either LZ or LZ4 can be selected. Then, when LZ4 is selected, it will gracely fallback to LZ if: - LZ4 support was not enabled in the server during compilation. - The client does not support a new capablity SPICE_DISPLAY_CAP_LZ4_COMPRESSION. The new capability is added to spice-protocol, so I send another patch for that repository. El Miércoles, 5 de noviembre de 2014 15:11:01 Javier Celaya escribió: > Hello > > El Miércoles, 5 de noviembre de 2014 08:58:25 Marc-André Lureau escribió: > > Hi > > > > ----- Original Message ----- > > > > > 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. > > > > Great! > > > > > 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. > > > > Sounds reasonable > > > > > 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. > > > > The patches look nice. However, I am not sure I understand why you chose > > not to modify the protocol to declare the new image type. We also need a > > new display capability before applying this patch, as the server needs to > > remain compatible with older clients no matter before accepting this. > > > > Would you be willing to do those changes to get your patch accepted? > > Yes, I will do those changes and send the patches again. > > Thanks > > > thanks > > _______________________________________________ > Spice-devel mailing list > Spice-devel@xxxxxxxxxxxxxxxxxxxxx > http://lists.freedesktop.org/mailman/listinfo/spice-devel
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 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..f3580a3 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) { @@ -599,6 +603,13 @@ typedef struct { EncoderData data; } JpegData; +#ifdef USE_LZ4 +typedef struct { + Lz4EncoderUsrContext usr; + EncoderData data; +} Lz4Data; +#endif + typedef struct { ZlibEncoderUsrContext usr; EncoderData data; @@ -740,6 +751,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 +1010,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 +1207,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 +5750,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 +5818,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 +5927,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 +6410,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) @@ -6469,6 +6600,7 @@ static inline int red_compress_image(DisplayChannelClient *dcc, if (_stride_is_extra(src) || (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE)) { if ((image_compression == SPICE_IMAGE_COMPRESS_LZ) || (image_compression == SPICE_IMAGE_COMPRESS_GLZ) || + (image_compression == SPICE_IMAGE_COMPRESS_LZ4) || BITMAP_FMT_IS_PLT[src->format]) { return FALSE; } else { @@ -6520,7 +6652,8 @@ static inline int red_compress_image(DisplayChannelClient *dcc, (src->x * src->y) < glz_enc_dictionary_get_size( dcc->glz_dict->dict)); } else if ((image_compression == SPICE_IMAGE_COMPRESS_AUTO_LZ) || - (image_compression == SPICE_IMAGE_COMPRESS_LZ)) { + (image_compression == SPICE_IMAGE_COMPRESS_LZ) || + (image_compression == SPICE_IMAGE_COMPRESS_LZ4)) { glz = FALSE; } else { spice_error("invalid image compression type %u", image_compression); @@ -6541,8 +6674,16 @@ static inline int red_compress_image(DisplayChannelClient *dcc, } if (!glz) { - ret = red_lz_compress_image(dcc, dest, src, o_comp_data, - drawable->group_id); +#ifdef USE_LZ4 + if (image_compression == SPICE_IMAGE_COMPRESS_LZ4 && + red_channel_client_test_remote_cap(&dcc->common.base, + SPICE_DISPLAY_CAP_LZ4_COMPRESSION)) { + ret = red_lz4_compress_image(dcc, dest, src, o_comp_data, + drawable->group_id); + } else +#endif + ret = red_lz_compress_image(dcc, dest, src, o_comp_data, + drawable->group_id); #ifdef COMPRESS_DEBUG spice_info("LZ LOCAL compress"); #endif @@ -8773,9 +8914,16 @@ static void red_marshall_image(RedChannelClient *rcc, SpiceMarshaller *m, ImageI &comp_send_data, worker->mem_slots.internal_groupslot_id); } else { + spice_warning("Do we ever get here??"); +#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 +10713,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) @@ -11589,6 +11738,11 @@ void handle_dev_set_compression(void *opaque, void *payload) case SPICE_IMAGE_COMPRESS_QUIC: spice_info("ic quic"); break; +#ifdef USE_LZ4 + case SPICE_IMAGE_COMPRESS_LZ4: + spice_info("ic lz4"); + break; +#endif case SPICE_IMAGE_COMPRESS_LZ: spice_info("ic lz"); break; @@ -11610,6 +11764,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 +12168,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 (;;) { diff --git a/server/spice.h b/server/spice.h index 58700d1..b38ca62 100644 --- a/server/spice.h +++ b/server/spice.h @@ -479,6 +479,7 @@ typedef enum { SPICE_IMAGE_COMPRESS_QUIC = 4, SPICE_IMAGE_COMPRESS_GLZ = 5, SPICE_IMAGE_COMPRESS_LZ = 6, + SPICE_IMAGE_COMPRESS_LZ4 = 7, } spice_image_compression_t; int spice_server_set_image_compression(SpiceServer *s,
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/gtk/channel-display.c b/gtk/channel-display.c index d9f8d45..dfebbb2 100644 --- a/gtk/channel-display.c +++ b/gtk/channel-display.c @@ -590,6 +590,9 @@ static void spice_display_channel_reset_capabilities(SpiceChannel *channel) spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_DISPLAY_CAP_MONITORS_CONFIG); spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_DISPLAY_CAP_COMPOSITE); spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_DISPLAY_CAP_A8_SURFACE); +#ifdef USE_LZ4 + spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_DISPLAY_CAP_LZ4_COMPRESSION); +#endif if (SPICE_DISPLAY_CHANNEL(channel)->priv->enable_adaptive_streaming) { spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_DISPLAY_CAP_STREAM_REPORT); }
diff --git a/spice/protocol.h b/spice/protocol.h index 961a303..bea376c 100644 --- a/spice/protocol.h +++ b/spice/protocol.h @@ -133,6 +133,7 @@ enum { SPICE_DISPLAY_CAP_COMPOSITE, SPICE_DISPLAY_CAP_A8_SURFACE, SPICE_DISPLAY_CAP_STREAM_REPORT, + SPICE_DISPLAY_CAP_LZ4_COMPRESSION, }; enum {
_______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel