On Fri, 2016-06-17 at 14:58 +0100, Frediano Ziglio wrote: > Signed-off-by: Frediano Ziglio <fziglio@xxxxxxxxxx> > --- > server/Makefile.am | 4 +- > server/dcc-encoders.c | 1388 ---------------------------------------------- > - > server/dcc-encoders.h | 217 -------- > server/dcc.h | 2 +- > server/image-encoders.c | 1388 > +++++++++++++++++++++++++++++++++++++++++++++++ > server/image-encoders.h | 217 ++++++++ > 6 files changed, 1608 insertions(+), 1608 deletions(-) > delete mode 100644 server/dcc-encoders.c > delete mode 100644 server/dcc-encoders.h > create mode 100644 server/image-encoders.c > create mode 100644 server/image-encoders.h > > diff --git a/server/Makefile.am b/server/Makefile.am > index 0af8a1b..921b082 100644 > --- a/server/Makefile.am > +++ b/server/Makefile.am > @@ -148,8 +148,8 @@ libserver_la_SOURCES = \ > dcc-send.c \ > dcc.h \ > display-limits.h \ > - dcc-encoders.c \ > - dcc-encoders.h \ > + image-encoders.c \ > + image-encoders.h \ > $(NULL) > > if HAVE_LZ4 > diff --git a/server/dcc-encoders.c b/server/dcc-encoders.c > deleted file mode 100644 > index 984f2d4..0000000 > --- a/server/dcc-encoders.c > +++ /dev/null > @@ -1,1388 +0,0 @@ > -/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ > -/* > - Copyright (C) 2009-2015 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/ > >. > -*/ > -#ifdef HAVE_CONFIG_H > -#include <config.h> > -#endif > - > -#include <glib.h> > - > -#include "dcc-encoders.h" > -#include "spice-bitmap-utils.h" > -#include "red-worker.h" // red_drawable_unref > -#include "pixmap-cache.h" // MAX_CACHE_CLIENTS > - > -#define ZLIB_DEFAULT_COMPRESSION_LEVEL 3 > - > -#define ENCODER_MESSAGE_SIZE 512 > - > -#define MAX_GLZ_DRAWABLE_INSTANCES 2 > - > -typedef struct GlzDrawableInstanceItem GlzDrawableInstanceItem; > - > -struct GlzSharedDictionary { > - RingItem base; > - GlzEncDictContext *dict; > - uint32_t refs; > - uint8_t id; > - pthread_rwlock_t encode_lock; > - int migrate_freeze; > - RedClient *client; // channel clients of the same client share the dict > -}; > - > -/* for each qxl drawable, there may be several instances of lz drawables */ > -/* TODO - reuse this stuff for the top level. I just added a second level of > multiplicity > - * at the Drawable by keeping a ring, so: > - * Drawable -> (ring of) RedGlzDrawable -> (up to 2) GlzDrawableInstanceItem > - * and it should probably (but need to be sure...) be > - * Drawable -> ring of GlzDrawableInstanceItem. > - */ > -struct GlzDrawableInstanceItem { > - RingItem glz_link; > - RingItem free_link; > - GlzEncDictImageContext *context; > - RedGlzDrawable *glz_drawable; > -}; > - > -struct RedGlzDrawable { > - RingItem link; // ordered by the time it was encoded > - RingItem drawable_link; > - RedDrawable *red_drawable; > - GlzDrawableInstanceItem instances_pool[MAX_GLZ_DRAWABLE_INSTANCES]; > - Ring instances; > - uint8_t instances_count; > - gboolean has_drawable; > - ImageEncoders *encoders; > -}; > - > -#define LINK_TO_GLZ(ptr) SPICE_CONTAINEROF((ptr), RedGlzDrawable, \ > - drawable_link) > -#define DRAWABLE_FOREACH_GLZ_SAFE(drawable, link, next, glz) \ > - SAFE_FOREACH(link, next, drawable, &(drawable)->glz_retention.ring, glz, > LINK_TO_GLZ(link)) > - > -static void glz_drawable_instance_item_free(GlzDrawableInstanceItem > *instance); > -static void encoder_data_init(EncoderData *data); > -static void encoder_data_reset(EncoderData *data); > -static void image_encoders_release_glz(ImageEncoders *enc); > - > - > -static SPICE_GNUC_NORETURN SPICE_GNUC_PRINTF(2, 3) void > -quic_usr_error(QuicUsrContext *usr, const char *fmt, ...) > -{ > - EncoderData *usr_data = &(((QuicData *)usr)->data); > - va_list ap; > - char message_buf[ENCODER_MESSAGE_SIZE]; > - > - va_start(ap, fmt); > - vsnprintf(message_buf, sizeof(message_buf), fmt, ap); > - va_end(ap); > - spice_critical("%s", message_buf); > - > - longjmp(usr_data->jmp_env, 1); > -} > - > -static SPICE_GNUC_NORETURN SPICE_GNUC_PRINTF(2, 3) void > -lz_usr_error(LzUsrContext *usr, const char *fmt, ...) > -{ > - EncoderData *usr_data = &(((LzData *)usr)->data); > - va_list ap; > - char message_buf[ENCODER_MESSAGE_SIZE]; > - > - va_start(ap, fmt); > - vsnprintf(message_buf, sizeof(message_buf), fmt, ap); > - va_end(ap); > - spice_critical("%s", message_buf); > - > - longjmp(usr_data->jmp_env, 1); > -} > - > -static SPICE_GNUC_PRINTF(2, 3) void > -glz_usr_error(GlzEncoderUsrContext *usr, const char *fmt, ...) > -{ > - va_list ap; > - char message_buf[ENCODER_MESSAGE_SIZE]; > - > - va_start(ap, fmt); > - vsnprintf(message_buf, sizeof(message_buf), fmt, ap); > - va_end(ap); > - > - spice_critical("%s", message_buf); // if global lz fails in the middle > - // the consequences are not > predictable since the window > - // can turn to be unsynchronized > between the server and > - // and the client > -} > - > -static SPICE_GNUC_PRINTF(2, 3) void > -quic_usr_warn(QuicUsrContext *usr, const char *fmt, ...) > -{ > - va_list ap; > - char message_buf[ENCODER_MESSAGE_SIZE]; > - > - va_start(ap, fmt); > - vsnprintf(message_buf, sizeof(message_buf), fmt, ap); > - va_end(ap); > - spice_warning("%s", message_buf); > -} > - > -static SPICE_GNUC_PRINTF(2, 3) void > -lz_usr_warn(LzUsrContext *usr, const char *fmt, ...) > -{ > - va_list ap; > - char message_buf[ENCODER_MESSAGE_SIZE]; > - > - va_start(ap, fmt); > - vsnprintf(message_buf, sizeof(message_buf), fmt, ap); > - va_end(ap); > - spice_warning("%s", message_buf); > -} > - > -static SPICE_GNUC_PRINTF(2, 3) void > -glz_usr_warn(GlzEncoderUsrContext *usr, const char *fmt, ...) > -{ > - va_list ap; > - char message_buf[ENCODER_MESSAGE_SIZE]; > - > - va_start(ap, fmt); > - vsnprintf(message_buf, sizeof(message_buf), fmt, ap); > - va_end(ap); > - spice_warning("%s", message_buf); > -} > - > -static void *quic_usr_malloc(QuicUsrContext *usr, int size) > -{ > - return spice_malloc(size); > -} > - > -static void *lz_usr_malloc(LzUsrContext *usr, int size) > -{ > - return spice_malloc(size); > -} > - > -static void *glz_usr_malloc(GlzEncoderUsrContext *usr, int size) > -{ > - return spice_malloc(size); > -} > - > -static void quic_usr_free(QuicUsrContext *usr, void *ptr) > -{ > - free(ptr); > -} > - > -static void lz_usr_free(LzUsrContext *usr, void *ptr) > -{ > - free(ptr); > -} > - > -static void glz_usr_free(GlzEncoderUsrContext *usr, void *ptr) > -{ > - free(ptr); > -} > - > -static void encoder_data_init(EncoderData *data) > -{ > - data->bufs_tail = g_new(RedCompressBuf, 1); > - data->bufs_head = data->bufs_tail; > - data->bufs_head->send_next = NULL; > -} > - > -static void encoder_data_reset(EncoderData *data) > -{ > - RedCompressBuf *buf = data->bufs_head; > - while (buf) { > - RedCompressBuf *next = buf->send_next; > - g_free(buf); > - buf = next; > - } > - data->bufs_head = data->bufs_tail = NULL; > -} > - > -/* Allocate more space for compressed buffer. > - * The pointer returned in io_ptr is garanteed to be aligned to 4 bytes. > - */ > -static int encoder_usr_more_space(EncoderData *enc_data, uint8_t **io_ptr) > -{ > - RedCompressBuf *buf; > - > - buf = g_new(RedCompressBuf, 1); > - enc_data->bufs_tail->send_next = buf; > - enc_data->bufs_tail = buf; > - buf->send_next = NULL; > - *io_ptr = buf->buf.bytes; > - return sizeof(buf->buf); > -} > - > -static int quic_usr_more_space(QuicUsrContext *usr, uint32_t **io_ptr, int > rows_completed) > -{ > - EncoderData *usr_data = &(((QuicData *)usr)->data); > - return encoder_usr_more_space(usr_data, (uint8_t **)io_ptr) / > sizeof(uint32_t); > -} > - > -static int lz_usr_more_space(LzUsrContext *usr, uint8_t **io_ptr) > -{ > - EncoderData *usr_data = &(((LzData *)usr)->data); > - return encoder_usr_more_space(usr_data, io_ptr); > -} > - > -static int glz_usr_more_space(GlzEncoderUsrContext *usr, uint8_t **io_ptr) > -{ > - EncoderData *usr_data = &(((GlzData *)usr)->data); > - return encoder_usr_more_space(usr_data, io_ptr); > -} > - > -static int jpeg_usr_more_space(JpegEncoderUsrContext *usr, uint8_t **io_ptr) > -{ > - EncoderData *usr_data = &(((JpegData *)usr)->data); > - return encoder_usr_more_space(usr_data, io_ptr); > -} > - > -#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, io_ptr); > -} > -#endif > - > -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, io_ptr); > -} > - > -static inline int encoder_usr_more_lines(EncoderData *enc_data, uint8_t > **lines) > -{ > - struct SpiceChunk *chunk; > - > - if (enc_data->u.lines_data.reverse) { > - if (!(enc_data->u.lines_data.next >= 0)) { > - return 0; > - } > - } else { > - if (!(enc_data->u.lines_data.next < enc_data->u.lines_data.chunks- > >num_chunks)) { > - return 0; > - } > - } > - > - chunk = &enc_data->u.lines_data.chunks->chunk[enc_data- > >u.lines_data.next]; > - if (chunk->len % enc_data->u.lines_data.stride) { > - return 0; > - } > - > - if (enc_data->u.lines_data.reverse) { > - enc_data->u.lines_data.next--; > - *lines = chunk->data + chunk->len - enc_data->u.lines_data.stride; > - } else { > - enc_data->u.lines_data.next++; > - *lines = chunk->data; > - } > - > - return chunk->len / enc_data->u.lines_data.stride; > -} > - > -static int quic_usr_more_lines(QuicUsrContext *usr, uint8_t **lines) > -{ > - EncoderData *usr_data = &(((QuicData *)usr)->data); > - return encoder_usr_more_lines(usr_data, lines); > -} > - > -static int lz_usr_more_lines(LzUsrContext *usr, uint8_t **lines) > -{ > - EncoderData *usr_data = &(((LzData *)usr)->data); > - return encoder_usr_more_lines(usr_data, lines); > -} > - > -static int glz_usr_more_lines(GlzEncoderUsrContext *usr, uint8_t **lines) > -{ > - EncoderData *usr_data = &(((GlzData *)usr)->data); > - return encoder_usr_more_lines(usr_data, lines); > -} > - > -static int jpeg_usr_more_lines(JpegEncoderUsrContext *usr, uint8_t **lines) > -{ > - EncoderData *usr_data = &(((JpegData *)usr)->data); > - 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); > - int buf_size; > - > - if (!usr_data->u.compressed_data.next) { > - spice_assert(usr_data->u.compressed_data.size_left == 0); > - return 0; > - } > - > - *input = usr_data->u.compressed_data.next->buf.bytes; > - 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 image_encoders_init_quic(ImageEncoders *enc) > -{ > - enc->quic_data.usr.error = quic_usr_error; > - enc->quic_data.usr.warn = quic_usr_warn; > - enc->quic_data.usr.info = quic_usr_warn; > - enc->quic_data.usr.malloc = quic_usr_malloc; > - enc->quic_data.usr.free = quic_usr_free; > - enc->quic_data.usr.more_space = quic_usr_more_space; > - enc->quic_data.usr.more_lines = quic_usr_more_lines; > - > - enc->quic = quic_create(&enc->quic_data.usr); > - > - if (!enc->quic) { > - spice_critical("create quic failed"); > - } > -} > - > -static void image_encoders_init_lz(ImageEncoders *enc) > -{ > - enc->lz_data.usr.error = lz_usr_error; > - enc->lz_data.usr.warn = lz_usr_warn; > - enc->lz_data.usr.info = lz_usr_warn; > - enc->lz_data.usr.malloc = lz_usr_malloc; > - enc->lz_data.usr.free = lz_usr_free; > - enc->lz_data.usr.more_space = lz_usr_more_space; > - enc->lz_data.usr.more_lines = lz_usr_more_lines; > - > - enc->lz = lz_create(&enc->lz_data.usr); > - > - if (!enc->lz) { > - spice_critical("create lz failed"); > - } > -} > - > -static void glz_usr_free_image(GlzEncoderUsrContext *usr, GlzUsrImageContext > *image) > -{ > - GlzData *lz_data = (GlzData *)usr; > - GlzDrawableInstanceItem *glz_drawable_instance = (GlzDrawableInstanceItem > *)image; > - ImageEncoders *drawable_enc = glz_drawable_instance->glz_drawable- > >encoders; > - ImageEncoders *this_enc = SPICE_CONTAINEROF(lz_data, ImageEncoders, > glz_data); > - if (this_enc == drawable_enc) { > - glz_drawable_instance_item_free(glz_drawable_instance); > - } else { > - /* The glz dictionary is shared between all DisplayChannelClient > - * instances that belong to the same client, and glz_usr_free_image > - * can be called by the dictionary code > - * (glz_dictionary_window_remove_head). Thus this function can be > - * called from any DisplayChannelClient thread, hence the need for > - * this check. > - */ > - pthread_mutex_lock(&drawable_enc->glz_drawables_inst_to_free_lock); > - ring_add_before(&glz_drawable_instance->free_link, > - &drawable_enc->glz_drawables_inst_to_free); > - pthread_mutex_unlock(&drawable_enc->glz_drawables_inst_to_free_lock); > - } > -} > - > -static void image_encoders_init_glz_data(ImageEncoders *enc) > -{ > - enc->glz_data.usr.error = glz_usr_error; > - enc->glz_data.usr.warn = glz_usr_warn; > - enc->glz_data.usr.info = glz_usr_warn; > - enc->glz_data.usr.malloc = glz_usr_malloc; > - enc->glz_data.usr.free = glz_usr_free; > - enc->glz_data.usr.more_space = glz_usr_more_space; > - enc->glz_data.usr.more_lines = glz_usr_more_lines; > - enc->glz_data.usr.free_image = glz_usr_free_image; > -} > - > -static void image_encoders_init_jpeg(ImageEncoders *enc) > -{ > - enc->jpeg_data.usr.more_space = jpeg_usr_more_space; > - enc->jpeg_data.usr.more_lines = jpeg_usr_more_lines; > - > - enc->jpeg = jpeg_encoder_create(&enc->jpeg_data.usr); > - > - if (!enc->jpeg) { > - spice_critical("create jpeg encoder failed"); > - } > -} > - > -#ifdef USE_LZ4 > -static inline void image_encoders_init_lz4(ImageEncoders *enc) > -{ > - enc->lz4_data.usr.more_space = lz4_usr_more_space; > - enc->lz4_data.usr.more_lines = lz4_usr_more_lines; > - > - enc->lz4 = lz4_encoder_create(&enc->lz4_data.usr); > - > - if (!enc->lz4) { > - spice_critical("create lz4 encoder failed"); > - } > -} > -#endif > - > -static void image_encoders_init_zlib(ImageEncoders *enc) > -{ > - enc->zlib_data.usr.more_space = zlib_usr_more_space; > - enc->zlib_data.usr.more_input = zlib_usr_more_input; > - > - enc->zlib = zlib_encoder_create(&enc->zlib_data.usr, > ZLIB_DEFAULT_COMPRESSION_LEVEL); > - > - if (!enc->zlib) { > - spice_critical("create zlib encoder failed"); > - } > -} > - > -void image_encoders_init(ImageEncoders *enc, ImageEncoderSharedData > *shared_data) > -{ > - spice_assert(shared_data); > - enc->shared_data = shared_data; > - > - ring_init(&enc->glz_drawables); > - ring_init(&enc->glz_drawables_inst_to_free); > - pthread_mutex_init(&enc->glz_drawables_inst_to_free_lock, NULL); > - > - image_encoders_init_glz_data(enc); > - image_encoders_init_quic(enc); > - image_encoders_init_lz(enc); > - image_encoders_init_jpeg(enc); > -#ifdef USE_LZ4 > - image_encoders_init_lz4(enc); > -#endif > - image_encoders_init_zlib(enc); > - > - // todo: tune level according to bandwidth > - enc->zlib_level = ZLIB_DEFAULT_COMPRESSION_LEVEL; > -} > - > -void image_encoders_free(ImageEncoders *enc) > -{ > - image_encoders_release_glz(enc); > - quic_destroy(enc->quic); > - enc->quic = NULL; > - lz_destroy(enc->lz); > - enc->lz = NULL; > - jpeg_encoder_destroy(enc->jpeg); > - enc->jpeg = NULL; > -#ifdef USE_LZ4 > - lz4_encoder_destroy(enc->lz4); > - enc->lz4 = NULL; > -#endif > - zlib_encoder_destroy(enc->zlib); > - enc->zlib = NULL; > -} > - > -/* Remove from the to_free list and the instances_list. > - When no instance is left - the RedGlzDrawable is released too. (and the > qxl drawable too, if > - it is not used by Drawable). > - NOTE - 1) can be called only by the display channel that created the > drawable > - 2) it is assumed that the instance was already removed from the > dictionary*/ > -static void glz_drawable_instance_item_free(GlzDrawableInstanceItem > *instance) > -{ > - RedGlzDrawable *glz_drawable; > - > - spice_assert(instance); > - spice_assert(instance->glz_drawable); > - > - glz_drawable = instance->glz_drawable; > - > - spice_assert(glz_drawable->instances_count > 0); > - > - ring_remove(&instance->glz_link); > - glz_drawable->instances_count--; > - > - // when the remove callback is performed from the channel that the > - // drawable belongs to, the instance is not added to the 'to_free' list > - if (ring_item_is_linked(&instance->free_link)) { > - ring_remove(&instance->free_link); > - } > - > - if (ring_is_empty(&glz_drawable->instances)) { > - spice_assert(glz_drawable->instances_count == 0); > - > - if (glz_drawable->has_drawable) { > - ring_remove(&glz_drawable->drawable_link); > - } > - red_drawable_unref(glz_drawable->red_drawable); > - glz_drawable->encoders->shared_data->glz_drawable_count--; > - if (ring_item_is_linked(&glz_drawable->link)) { > - ring_remove(&glz_drawable->link); > - } > - free(glz_drawable); > - } > -} > - > -/* > - * Releases all the instances of the drawable from the dictionary and the > display channel client. > - * The release of the last instance will also release the drawable itself and > the qxl drawable > - * if possible. > - * NOTE - the caller should prevent encoding using the dictionary during this > operation > - */ > -static void red_glz_drawable_free(RedGlzDrawable *glz_drawable) > -{ > - ImageEncoders *enc = glz_drawable->encoders; > - RingItem *head_instance = ring_get_head(&glz_drawable->instances); > - int cont = (head_instance != NULL); > - > - while (cont) { > - if (glz_drawable->instances_count == 1) { > - /* Last instance: glz_drawable_instance_item_free will free the > glz_drawable */ > - cont = FALSE; > - } > - GlzDrawableInstanceItem *instance = SPICE_CONTAINEROF(head_instance, > - GlzDrawableInstanceIt > em, > - glz_link); > - if (!ring_item_is_linked(&instance->free_link)) { > - // the instance didn't get out from window yet > - glz_enc_dictionary_remove_image(enc->glz_dict->dict, > - instance->context, > - &enc->glz_data.usr); > - } > - glz_drawable_instance_item_free(instance); > - > - if (cont) { > - head_instance = ring_get_head(&glz_drawable->instances); > - } > - } > -} > - > -gboolean image_encoders_glz_encode_lock(ImageEncoders *enc) > -{ > - if (enc->glz_dict) { > - pthread_rwlock_wrlock(&enc->glz_dict->encode_lock); > - return TRUE; > - } > - return FALSE; > -} > - > -void image_encoders_glz_encode_unlock(ImageEncoders *enc) > -{ > - if (enc->glz_dict) { > - pthread_rwlock_unlock(&enc->glz_dict->encode_lock); > - } > -} > - > -/* > - * Remove from the global lz dictionary some glz_drawables that have no > reference to > - * Drawable (their qxl drawables are released too). > - * NOTE - the caller should prevent encoding using the dictionary during the > operation > - */ > -int image_encoders_free_some_independent_glz_drawables(ImageEncoders *enc) > -{ > - RingItem *ring_link; > - int n = 0; > - > - if (!enc) { > - return 0; > - } > - ring_link = ring_get_head(&enc->glz_drawables); > - while ((n < RED_RELEASE_BUNCH_SIZE) && (ring_link != NULL)) { > - RedGlzDrawable *glz_drawable = SPICE_CONTAINEROF(ring_link, > RedGlzDrawable, link); > - ring_link = ring_next(&enc->glz_drawables, ring_link); > - if (!glz_drawable->has_drawable) { > - red_glz_drawable_free(glz_drawable); > - n++; > - } > - } > - return n; > -} > - > -void image_encoders_free_glz_drawables_to_free(ImageEncoders* enc) > -{ > - RingItem *ring_link; > - > - if (!enc->glz_dict) { > - return; > - } > - pthread_mutex_lock(&enc->glz_drawables_inst_to_free_lock); > - while ((ring_link = ring_get_head(&enc->glz_drawables_inst_to_free))) { > - GlzDrawableInstanceItem *drawable_instance = > SPICE_CONTAINEROF(ring_link, > - GlzDrawableI > nstanceItem, > - free_link); > - glz_drawable_instance_item_free(drawable_instance); > - } > - pthread_mutex_unlock(&enc->glz_drawables_inst_to_free_lock); > -} > - > -/* Clear all lz drawables - enforce their removal from the global dictionary. > - NOTE - prevents encoding using the dictionary during the operation*/ > -void image_encoders_free_glz_drawables(ImageEncoders *enc) > -{ > - RingItem *ring_link; > - GlzSharedDictionary *glz_dict = enc ? enc->glz_dict : NULL; > - > - if (!glz_dict) { > - return; > - } > - > - // assure no display channel is during global lz encoding > - pthread_rwlock_wrlock(&glz_dict->encode_lock); > - while ((ring_link = ring_get_head(&enc->glz_drawables))) { > - RedGlzDrawable *drawable = SPICE_CONTAINEROF(ring_link, > RedGlzDrawable, link); > - // no need to lock the to_free list, since we assured no other thread > is encoding and > - // thus not other thread access the to_free list of the channel > - red_glz_drawable_free(drawable); > - } > - pthread_rwlock_unlock(&glz_dict->encode_lock); > -} > - > -void glz_retention_free_drawables(GlzImageRetention *ret) > -{ > - RingItem *glz_item, *next_item; > - RedGlzDrawable *glz; > - SAFE_FOREACH(glz_item, next_item, TRUE, &ret->ring, glz, > LINK_TO_GLZ(glz_item)) { > - red_glz_drawable_free(glz); > - } > -} > - > -void glz_retention_detach_drawables(GlzImageRetention *ret) > -{ > - RingItem *item, *next; > - > - RING_FOREACH_SAFE(item, next, &ret->ring) { > - SPICE_CONTAINEROF(item, RedGlzDrawable, drawable_link)->has_drawable > = FALSE; > - ring_remove(item); > - } > -} > - > -static void image_encoders_freeze_glz(ImageEncoders *enc) > -{ > - pthread_rwlock_wrlock(&enc->glz_dict->encode_lock); > - enc->glz_dict->migrate_freeze = TRUE; > - pthread_rwlock_unlock(&enc->glz_dict->encode_lock); > -} > - > -void image_encoders_glz_get_restore_data(ImageEncoders *enc, > - uint8_t *out_id, > GlzEncDictRestoreData *out_data) > -{ > - spice_assert(enc->glz_dict); > - image_encoders_freeze_glz(enc); > - *out_id = enc->glz_dict->id; > - glz_enc_dictionary_get_restore_data(enc->glz_dict->dict, out_data, > - &enc->glz_data.usr); > -} > - > -static GlzSharedDictionary *glz_shared_dictionary_new(RedClient *client, > uint8_t id, > - GlzEncDictContext > *dict) > -{ > - spice_return_val_if_fail(dict != NULL, NULL); > - > - GlzSharedDictionary *shared_dict = spice_new0(GlzSharedDictionary, 1); > - > - shared_dict->dict = dict; > - shared_dict->id = id; > - shared_dict->refs = 1; > - shared_dict->migrate_freeze = FALSE; > - shared_dict->client = client; > - ring_item_init(&shared_dict->base); > - pthread_rwlock_init(&shared_dict->encode_lock, NULL); > - > - return shared_dict; > -} > - > -static pthread_mutex_t glz_dictionary_list_lock = PTHREAD_MUTEX_INITIALIZER; > -static Ring glz_dictionary_list = {&glz_dictionary_list, > &glz_dictionary_list}; > - > -static GlzSharedDictionary *find_glz_dictionary(RedClient *client, uint8_t > dict_id) > -{ > - RingItem *now; > - GlzSharedDictionary *ret = NULL; > - > - now = &glz_dictionary_list; > - while ((now = ring_next(&glz_dictionary_list, now))) { > - GlzSharedDictionary *dict = SPICE_UPCAST(GlzSharedDictionary, now); > - if ((dict->client == client) && (dict->id == dict_id)) { > - ret = dict; > - break; > - } > - } > - > - return ret; > -} > - > -#define MAX_LZ_ENCODERS MAX_CACHE_CLIENTS > - > -static GlzSharedDictionary *create_glz_dictionary(ImageEncoders *enc, > - RedClient *client, > - uint8_t id, int > window_size) > -{ > - spice_info("Lz Window %d Size=%d", id, window_size); > - > - GlzEncDictContext *glz_dict = > - glz_enc_dictionary_create(window_size, MAX_LZ_ENCODERS, &enc- > >glz_data.usr); > - > - return glz_shared_dictionary_new(client, id, glz_dict); > -} > - > -gboolean image_encoders_get_glz_dictionary(ImageEncoders *enc, > - RedClient *client, > - uint8_t id, int window_size) > -{ > - GlzSharedDictionary *shared_dict; > - > - spice_return_val_if_fail(!enc->glz_dict, FALSE); > - > - pthread_mutex_lock(&glz_dictionary_list_lock); > - > - shared_dict = find_glz_dictionary(client, id); > - if (shared_dict) { > - shared_dict->refs++; > - } else { > - shared_dict = create_glz_dictionary(enc, client, id, window_size); > - ring_add(&glz_dictionary_list, &shared_dict->base); > - } > - > - pthread_mutex_unlock(&glz_dictionary_list_lock); > - enc->glz_dict = shared_dict; > - return shared_dict != NULL; > -} > - > -static GlzSharedDictionary *restore_glz_dictionary(ImageEncoders *enc, > - RedClient *client, > - uint8_t id, > - GlzEncDictRestoreData > *restore_data) > -{ > - GlzEncDictContext *glz_dict = > - glz_enc_dictionary_restore(restore_data, &enc->glz_data.usr); > - > - return glz_shared_dictionary_new(client, id, glz_dict); > -} > - > -gboolean image_encoders_restore_glz_dictionary(ImageEncoders *enc, > - RedClient *client, > - uint8_t id, > - GlzEncDictRestoreData > *restore_data) > -{ > - GlzSharedDictionary *shared_dict = NULL; > - > - spice_return_val_if_fail(!enc->glz_dict, FALSE); > - > - pthread_mutex_lock(&glz_dictionary_list_lock); > - > - shared_dict = find_glz_dictionary(client, id); > - > - if (shared_dict) { > - shared_dict->refs++; > - } else { > - shared_dict = restore_glz_dictionary(enc, client, id, restore_data); > - ring_add(&glz_dictionary_list, &shared_dict->base); > - } > - > - pthread_mutex_unlock(&glz_dictionary_list_lock); > - enc->glz_dict = shared_dict; > - return shared_dict != NULL; > -} > - > -gboolean image_encoders_glz_create(ImageEncoders *enc, uint8_t id) > -{ > - enc->glz = glz_encoder_create(id, enc->glz_dict->dict, &enc- > >glz_data.usr); > - return enc->glz != NULL; > -} > - > -/* destroy encoder, and dictionary if no one uses it*/ > -static void image_encoders_release_glz(ImageEncoders *enc) > -{ > - GlzSharedDictionary *shared_dict; > - > - image_encoders_free_glz_drawables(enc); > - > - glz_encoder_destroy(enc->glz); > - enc->glz = NULL; > - > - if (!(shared_dict = enc->glz_dict)) { > - return; > - } > - > - enc->glz_dict = NULL; > - pthread_mutex_lock(&glz_dictionary_list_lock); > - if (--shared_dict->refs != 0) { > - pthread_mutex_unlock(&glz_dictionary_list_lock); > - return; > - } > - ring_remove(&shared_dict->base); > - pthread_mutex_unlock(&glz_dictionary_list_lock); > - glz_enc_dictionary_destroy(shared_dict->dict, &enc->glz_data.usr); > - free(shared_dict); > -} > - > -int image_encoders_compress_quic(ImageEncoders *enc, SpiceImage *dest, > - SpiceBitmap *src, compress_send_data_t* > o_comp_data) > -{ > - QuicData *quic_data = &enc->quic_data; > - QuicContext *quic = enc->quic; > - volatile QuicImageType type; > - int size, stride; > - stat_start_time_t start_time; > - stat_start_time_init(&start_time, &enc->shared_data->quic_stat); > - > -#ifdef COMPRESS_DEBUG > - spice_info("QUIC compress"); > -#endif > - > - switch (src->format) { > - case SPICE_BITMAP_FMT_32BIT: > - type = QUIC_IMAGE_TYPE_RGB32; > - break; > - case SPICE_BITMAP_FMT_RGBA: > - type = QUIC_IMAGE_TYPE_RGBA; > - break; > - case SPICE_BITMAP_FMT_16BIT: > - type = QUIC_IMAGE_TYPE_RGB16; > - break; > - case SPICE_BITMAP_FMT_24BIT: > - type = QUIC_IMAGE_TYPE_RGB24; > - break; > - default: > - return FALSE; > - } > - > - encoder_data_init(&quic_data->data); > - > - if (setjmp(quic_data->data.jmp_env)) { > - encoder_data_reset(&quic_data->data); > - return FALSE; > - } > - > - if (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE) { > - spice_chunks_linearize(src->data); > - } > - > - quic_data->data.u.lines_data.chunks = src->data; > - quic_data->data.u.lines_data.stride = src->stride; > - if ((src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) { > - quic_data->data.u.lines_data.next = 0; > - quic_data->data.u.lines_data.reverse = 0; > - stride = src->stride; > - } else { > - quic_data->data.u.lines_data.next = src->data->num_chunks - 1; > - quic_data->data.u.lines_data.reverse = 1; > - stride = -src->stride; > - } > - size = quic_encode(quic, type, src->x, src->y, NULL, 0, stride, > - quic_data->data.bufs_head->buf.words, > - G_N_ELEMENTS(quic_data->data.bufs_head->buf.words)); > - > - // the compressed buffer is bigger than the original data > - if ((size << 2) > (src->y * src->stride)) { > - longjmp(quic_data->data.jmp_env, 1); > - } > - > - dest->descriptor.type = SPICE_IMAGE_TYPE_QUIC; > - dest->u.quic.data_size = size << 2; > - > - o_comp_data->comp_buf = quic_data->data.bufs_head; > - o_comp_data->comp_buf_size = size << 2; > - > - stat_compress_add(&enc->shared_data->quic_stat, start_time, src->stride * > src->y, > - o_comp_data->comp_buf_size); > - return TRUE; > -} > - > -static const LzImageType bitmap_fmt_to_lz_image_type[] = { > - LZ_IMAGE_TYPE_INVALID, > - LZ_IMAGE_TYPE_PLT1_LE, > - LZ_IMAGE_TYPE_PLT1_BE, > - LZ_IMAGE_TYPE_PLT4_LE, > - LZ_IMAGE_TYPE_PLT4_BE, > - LZ_IMAGE_TYPE_PLT8, > - LZ_IMAGE_TYPE_RGB16, > - LZ_IMAGE_TYPE_RGB24, > - LZ_IMAGE_TYPE_RGB32, > - LZ_IMAGE_TYPE_RGBA, > - LZ_IMAGE_TYPE_A8 > -}; > - > -int image_encoders_compress_lz(ImageEncoders *enc, > - SpiceImage *dest, SpiceBitmap *src, > - compress_send_data_t* o_comp_data) > -{ > - LzData *lz_data = &enc->lz_data; > - LzContext *lz = enc->lz; > - LzImageType type = bitmap_fmt_to_lz_image_type[src->format]; > - int size; // size of the compressed data > - > - stat_start_time_t start_time; > - stat_start_time_init(&start_time, &enc->shared_data->lz_stat); > - > -#ifdef COMPRESS_DEBUG > - spice_info("LZ LOCAL compress"); > -#endif > - > - encoder_data_init(&lz_data->data); > - > - if (setjmp(lz_data->data.jmp_env)) { > - encoder_data_reset(&lz_data->data); > - return FALSE; > - } > - > - lz_data->data.u.lines_data.chunks = src->data; > - lz_data->data.u.lines_data.stride = src->stride; > - lz_data->data.u.lines_data.next = 0; > - lz_data->data.u.lines_data.reverse = 0; > - > - size = lz_encode(lz, type, src->x, src->y, > - !!(src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN), > - NULL, 0, src->stride, > - lz_data->data.bufs_head->buf.bytes, > - sizeof(lz_data->data.bufs_head->buf)); > - > - // the compressed buffer is bigger than the original data > - if (size > (src->y * src->stride)) { > - longjmp(lz_data->data.jmp_env, 1); > - } > - > - if (bitmap_fmt_is_rgb(src->format)) { > - dest->descriptor.type = SPICE_IMAGE_TYPE_LZ_RGB; > - dest->u.lz_rgb.data_size = size; > - > - o_comp_data->comp_buf = lz_data->data.bufs_head; > - o_comp_data->comp_buf_size = size; > - } else { > - /* masks are 1BIT bitmaps without palettes, but they are not > compressed > - * (see fill_mask) */ > - spice_assert(src->palette); > - dest->descriptor.type = SPICE_IMAGE_TYPE_LZ_PLT; > - dest->u.lz_plt.data_size = size; > - dest->u.lz_plt.flags = src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN; > - dest->u.lz_plt.palette = src->palette; > - dest->u.lz_plt.palette_id = src->palette->unique; > - o_comp_data->comp_buf = lz_data->data.bufs_head; > - o_comp_data->comp_buf_size = size; > - > - o_comp_data->lzplt_palette = dest->u.lz_plt.palette; > - } > - > - stat_compress_add(&enc->shared_data->lz_stat, start_time, src->stride * > src->y, > - o_comp_data->comp_buf_size); > - return TRUE; > -} > - > -int image_encoders_compress_jpeg(ImageEncoders *enc, SpiceImage *dest, > - SpiceBitmap *src, compress_send_data_t* > o_comp_data) > -{ > - JpegData *jpeg_data = &enc->jpeg_data; > - LzData *lz_data = &enc->lz_data; > - JpegEncoderContext *jpeg = enc->jpeg; > - LzContext *lz = enc->lz; > - volatile JpegEncoderImageType jpeg_in_type; > - int jpeg_size = 0; > - volatile int has_alpha = FALSE; > - int alpha_lz_size = 0; > - int comp_head_filled; > - int comp_head_left; > - int stride; > - uint8_t *lz_out_start_byte; > - stat_start_time_t start_time; > - stat_start_time_init(&start_time, &enc->shared_data->jpeg_alpha_stat); > - > -#ifdef COMPRESS_DEBUG > - spice_info("JPEG compress"); > -#endif > - > - switch (src->format) { > - case SPICE_BITMAP_FMT_16BIT: > - jpeg_in_type = JPEG_IMAGE_TYPE_RGB16; > - break; > - case SPICE_BITMAP_FMT_24BIT: > - jpeg_in_type = JPEG_IMAGE_TYPE_BGR24; > - break; > - case SPICE_BITMAP_FMT_32BIT: > - jpeg_in_type = JPEG_IMAGE_TYPE_BGRX32; > - break; > - case SPICE_BITMAP_FMT_RGBA: > - jpeg_in_type = JPEG_IMAGE_TYPE_BGRX32; > - has_alpha = TRUE; > - break; > - default: > - return FALSE; > - } > - > - encoder_data_init(&jpeg_data->data); > - > - if (setjmp(jpeg_data->data.jmp_env)) { > - encoder_data_reset(&jpeg_data->data); > - return FALSE; > - } > - > - if (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE) { > - spice_chunks_linearize(src->data); > - } > - > - jpeg_data->data.u.lines_data.chunks = src->data; > - jpeg_data->data.u.lines_data.stride = src->stride; > - if ((src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) { > - jpeg_data->data.u.lines_data.next = 0; > - jpeg_data->data.u.lines_data.reverse = 0; > - stride = src->stride; > - } else { > - jpeg_data->data.u.lines_data.next = src->data->num_chunks - 1; > - jpeg_data->data.u.lines_data.reverse = 1; > - stride = -src->stride; > - } > - jpeg_size = jpeg_encode(jpeg, enc->jpeg_quality, jpeg_in_type, > - src->x, src->y, NULL, > - 0, stride, jpeg_data->data.bufs_head->buf.bytes, > - sizeof(jpeg_data->data.bufs_head->buf)); > - > - // the compressed buffer is bigger than the original data > - if (jpeg_size > (src->y * src->stride)) { > - longjmp(jpeg_data->data.jmp_env, 1); > - } > - > - if (!has_alpha) { > - dest->descriptor.type = SPICE_IMAGE_TYPE_JPEG; > - dest->u.jpeg.data_size = jpeg_size; > - > - o_comp_data->comp_buf = jpeg_data->data.bufs_head; > - o_comp_data->comp_buf_size = jpeg_size; > - o_comp_data->is_lossy = TRUE; > - > - stat_compress_add(&enc->shared_data->jpeg_stat, start_time, src- > >stride * src->y, > - o_comp_data->comp_buf_size); > - return TRUE; > - } > - > - lz_data->data.bufs_head = jpeg_data->data.bufs_tail; > - lz_data->data.bufs_tail = lz_data->data.bufs_head; > - > - comp_head_filled = jpeg_size % sizeof(lz_data->data.bufs_head->buf); > - comp_head_left = sizeof(lz_data->data.bufs_head->buf) - comp_head_filled; > - lz_out_start_byte = lz_data->data.bufs_head->buf.bytes + > comp_head_filled; > - > - lz_data->data.u.lines_data.chunks = src->data; > - lz_data->data.u.lines_data.stride = src->stride; > - lz_data->data.u.lines_data.next = 0; > - lz_data->data.u.lines_data.reverse = 0; > - > - alpha_lz_size = lz_encode(lz, LZ_IMAGE_TYPE_XXXA, src->x, src->y, > - !!(src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN), > - NULL, 0, src->stride, > - lz_out_start_byte, > - comp_head_left); > - > - // the compressed buffer is bigger than the original data > - if ((jpeg_size + alpha_lz_size) > (src->y * src->stride)) { > - longjmp(jpeg_data->data.jmp_env, 1); > - } > - > - dest->descriptor.type = SPICE_IMAGE_TYPE_JPEG_ALPHA; > - dest->u.jpeg_alpha.flags = 0; > - if (src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN) { > - dest->u.jpeg_alpha.flags |= SPICE_JPEG_ALPHA_FLAGS_TOP_DOWN; > - } > - > - dest->u.jpeg_alpha.jpeg_size = jpeg_size; > - dest->u.jpeg_alpha.data_size = jpeg_size + alpha_lz_size; > - > - o_comp_data->comp_buf = jpeg_data->data.bufs_head; > - o_comp_data->comp_buf_size = jpeg_size + alpha_lz_size; > - o_comp_data->is_lossy = TRUE; > - stat_compress_add(&enc->shared_data->jpeg_alpha_stat, start_time, src- > >stride * src->y, > - o_comp_data->comp_buf_size); > - return TRUE; > -} > - > -#ifdef USE_LZ4 > -int image_encoders_compress_lz4(ImageEncoders *enc, SpiceImage *dest, > - SpiceBitmap *src, compress_send_data_t* > o_comp_data) > -{ > - Lz4Data *lz4_data = &enc->lz4_data; > - Lz4EncoderContext *lz4 = enc->lz4; > - int lz4_size = 0; > - stat_start_time_t start_time; > - stat_start_time_init(&start_time, &enc->shared_data->lz4_stat); > - > -#ifdef COMPRESS_DEBUG > - spice_info("LZ4 compress"); > -#endif > - > - encoder_data_init(&lz4_data->data); > - > - if (setjmp(lz4_data->data.jmp_env)) { > - encoder_data_reset(&lz4_data->data); > - 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->data.u.lines_data.next = 0; > - lz4_data->data.u.lines_data.reverse = 0; > - > - lz4_size = lz4_encode(lz4, src->y, src->stride, lz4_data->data.bufs_head- > >buf.bytes, > - sizeof(lz4_data->data.bufs_head->buf), > - src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN, src- > >format); > - > - // 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(&enc->shared_data->lz4_stat, start_time, src->stride * > src->y, > - o_comp_data->comp_buf_size); > - return TRUE; > -} > -#endif > - > -/* if already exists, returns it. Otherwise allocates and adds it (1) to the > ring tail > - in the channel (2) to the Drawable*/ > -static RedGlzDrawable *get_glz_drawable(ImageEncoders *enc, RedDrawable > *red_drawable, > - GlzImageRetention *glz_retention) > -{ > - RedGlzDrawable *ret; > - RingItem *item, *next; > - > - // TODO - I don't really understand what's going on here, so doing the > technical equivalent > - // now that we have multiple glz_dicts, so the only way to go from dcc to > drawable glz is to go > - // over the glz_ring (unless adding some better data structure then a > ring) > - SAFE_FOREACH(item, next, TRUE, &glz_retention->ring, ret, > LINK_TO_GLZ(item)) { > - if (ret->encoders == enc) { > - return ret; > - } > - } > - > - ret = spice_new(RedGlzDrawable, 1); > - > - ret->encoders = enc; > - ret->red_drawable = red_drawable_ref(red_drawable); > - ret->has_drawable = TRUE; > - ret->instances_count = 0; > - ring_init(&ret->instances); > - > - ring_item_init(&ret->link); > - ring_item_init(&ret->drawable_link); > - ring_add_before(&ret->link, &enc->glz_drawables); > - ring_add(&glz_retention->ring, &ret->drawable_link); > - enc->shared_data->glz_drawable_count++; > - return ret; > -} > - > -/* allocates new instance and adds it to instances in the given drawable. > - NOTE - the caller should set the glz_instance returned by the encoder by > itself.*/ > -static GlzDrawableInstanceItem *add_glz_drawable_instance(RedGlzDrawable > *glz_drawable) > -{ > - spice_assert(glz_drawable->instances_count < MAX_GLZ_DRAWABLE_INSTANCES); > - // NOTE: We assume the additions are performed consecutively, without > removals in the middle > - GlzDrawableInstanceItem *ret = glz_drawable->instances_pool + > glz_drawable->instances_count; > - glz_drawable->instances_count++; > - > - ring_item_init(&ret->free_link); > - ring_item_init(&ret->glz_link); > - ring_add(&glz_drawable->instances, &ret->glz_link); > - ret->context = NULL; > - ret->glz_drawable = glz_drawable; > - > - return ret; > -} > - > -#define MIN_GLZ_SIZE_FOR_ZLIB 100 > - > -int image_encoders_compress_glz(ImageEncoders *enc, > - SpiceImage *dest, SpiceBitmap *src, > - RedDrawable *red_drawable, > - GlzImageRetention *glz_retention, > - compress_send_data_t* o_comp_data, > - gboolean enable_zlib_glz_wrap) > -{ > - stat_start_time_t start_time; > - stat_start_time_init(&start_time, &enc->shared_data->zlib_glz_stat); > - spice_assert(bitmap_fmt_is_rgb(src->format)); > - GlzData *glz_data = &enc->glz_data; > - ZlibData *zlib_data; > - LzImageType type = bitmap_fmt_to_lz_image_type[src->format]; > - RedGlzDrawable *glz_drawable; > - GlzDrawableInstanceItem *glz_drawable_instance; > - int glz_size; > - int zlib_size; > - > -#ifdef COMPRESS_DEBUG > - spice_info("LZ global compress fmt=%d", src->format); > -#endif > - > - if ((src->x * src->y) >= glz_enc_dictionary_get_size(enc->glz_dict- > >dict)) { > - return FALSE; > - } > - > - pthread_rwlock_rdlock(&enc->glz_dict->encode_lock); > - /* using the global dictionary only if it is not frozen */ > - if (enc->glz_dict->migrate_freeze) { > - pthread_rwlock_unlock(&enc->glz_dict->encode_lock); > - return FALSE; > - } > - > - encoder_data_init(&glz_data->data); > - > - glz_drawable = get_glz_drawable(enc, red_drawable, glz_retention); > - glz_drawable_instance = add_glz_drawable_instance(glz_drawable); > - > - glz_data->data.u.lines_data.chunks = src->data; > - glz_data->data.u.lines_data.stride = src->stride; > - glz_data->data.u.lines_data.next = 0; > - glz_data->data.u.lines_data.reverse = 0; > - > - glz_size = glz_encode(enc->glz, type, src->x, src->y, > - (src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN), NULL, > 0, > - src->stride, glz_data->data.bufs_head->buf.bytes, > - sizeof(glz_data->data.bufs_head->buf), > - glz_drawable_instance, > - &glz_drawable_instance->context); > - > - stat_compress_add(&enc->shared_data->glz_stat, start_time, src->stride * > src->y, glz_size); > - > - if (!enable_zlib_glz_wrap || (glz_size < MIN_GLZ_SIZE_FOR_ZLIB)) { > - goto glz; > - } > - stat_start_time_init(&start_time, &enc->shared_data->zlib_glz_stat); > - zlib_data = &enc->zlib_data; > - > - encoder_data_init(&zlib_data->data); > - > - 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(enc->zlib, enc->zlib_level, > - glz_size, zlib_data->data.bufs_head->buf.bytes, > - sizeof(zlib_data->data.bufs_head->buf)); > - > - // the compressed buffer is bigger than the original data > - if (zlib_size >= glz_size) { > - encoder_data_reset(&zlib_data->data); > - goto glz; > - } else { > - encoder_data_reset(&glz_data->data); > - } > - > - dest->descriptor.type = SPICE_IMAGE_TYPE_ZLIB_GLZ_RGB; > - dest->u.zlib_glz.glz_data_size = glz_size; > - dest->u.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(&enc->shared_data->zlib_glz_stat, start_time, glz_size, > zlib_size); > - pthread_rwlock_unlock(&enc->glz_dict->encode_lock); > - return TRUE; > - > -glz: > - pthread_rwlock_unlock(&enc->glz_dict->encode_lock); > - > - dest->descriptor.type = SPICE_IMAGE_TYPE_GLZ_RGB; > - dest->u.lz_rgb.data_size = glz_size; > - > - o_comp_data->comp_buf = glz_data->data.bufs_head; > - o_comp_data->comp_buf_size = glz_size; > - > - return TRUE; > -} > - > -void image_encoder_shared_init(ImageEncoderSharedData *shared_data) > -{ > - clockid_t stat_clock = CLOCK_THREAD_CPUTIME_ID; > - > - stat_compress_init(&shared_data->off_stat, "off", stat_clock); > - stat_compress_init(&shared_data->lz_stat, "lz", stat_clock); > - stat_compress_init(&shared_data->glz_stat, "glz", stat_clock); > - stat_compress_init(&shared_data->quic_stat, "quic", stat_clock); > - stat_compress_init(&shared_data->jpeg_stat, "jpeg", stat_clock); > - stat_compress_init(&shared_data->zlib_glz_stat, "zlib", stat_clock); > - stat_compress_init(&shared_data->jpeg_alpha_stat, "jpeg_alpha", > stat_clock); > - stat_compress_init(&shared_data->lz4_stat, "lz4", stat_clock); > -} > - > -void image_encoder_shared_stat_reset(ImageEncoderSharedData *shared_data) > -{ > - stat_reset(&shared_data->off_stat); > - stat_reset(&shared_data->quic_stat); > - stat_reset(&shared_data->lz_stat); > - stat_reset(&shared_data->glz_stat); > - stat_reset(&shared_data->jpeg_stat); > - stat_reset(&shared_data->zlib_glz_stat); > - stat_reset(&shared_data->jpeg_alpha_stat); > - stat_reset(&shared_data->lz4_stat); > -} > - > -#define STAT_FMT "%s\t%8u\t%13.8g\t%12.8g\t%12.8g" > - > -#ifdef COMPRESS_STAT > -static void stat_print_one(const char *name, const stat_info_t *stat) > -{ > - spice_info(STAT_FMT, name, stat->count, > - stat_byte_to_mega(stat->orig_size), > - stat_byte_to_mega(stat->comp_size), > - stat_cpu_time_to_sec(stat->total)); > -} > - > -static void stat_sum(stat_info_t *total, const stat_info_t *stat) > -{ > - total->count += stat->count; > - total->orig_size += stat->orig_size; > - total->comp_size += stat->comp_size; > - total->total += stat->total; > -} > -#endif > - > -void image_encoder_shared_stat_print(const ImageEncoderSharedData > *shared_data) > -{ > -#ifdef COMPRESS_STAT > - /* sum all statistics */ > - stat_info_t total = { > - .count = 0, > - .orig_size = 0, > - .comp_size = 0, > - .total = 0 > - }; > - stat_sum(&total, &shared_data->off_stat); > - stat_sum(&total, &shared_data->quic_stat); > - stat_sum(&total, &shared_data->glz_stat); > - stat_sum(&total, &shared_data->lz_stat); > - stat_sum(&total, &shared_data->jpeg_stat); > - stat_sum(&total, &shared_data->jpeg_alpha_stat); > - stat_sum(&total, &shared_data->lz4_stat); > - > - /* fix for zlib glz */ > - total.total += shared_data->zlib_glz_stat.total; > - if (shared_data->zlib_glz_stat.count) { > - total.comp_size = total.comp_size - shared_data->glz_stat.comp_size + > - shared_data->zlib_glz_stat.comp_size; > - } > - > - spice_info("Method \t count \torig_size(MB)\tenc_size(MB)\tenc_time(s > )"); > - stat_print_one("OFF ", &shared_data->off_stat); > - stat_print_one("QUIC ", &shared_data->quic_stat); > - stat_print_one("GLZ ", &shared_data->glz_stat); > - stat_print_one("ZLIB GLZ ", &shared_data->zlib_glz_stat); > - stat_print_one("LZ ", &shared_data->lz_stat); > - stat_print_one("JPEG ", &shared_data->jpeg_stat); > - stat_print_one("JPEG-RGBA", &shared_data->jpeg_alpha_stat); > - stat_print_one("LZ4 ", &shared_data->lz4_stat); > - spice_info("------------------------------------------------------------- > ------"); > - stat_print_one("Total ", &total); > -#endif > -} > diff --git a/server/dcc-encoders.h b/server/dcc-encoders.h > deleted file mode 100644 > index 9286970..0000000 > --- a/server/dcc-encoders.h > +++ /dev/null > @@ -1,217 +0,0 @@ > -/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ > -/* > - Copyright (C) 2009-2015 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 DCC_ENCODERS_H_ > -#define DCC_ENCODERS_H_ > - > -#include <setjmp.h> > -#include <common/quic.h> > -#include <common/lz.h> > - > -#include "stat.h" > -#include "red-parse-qxl.h" > -#include "glz-encoder.h" > -#include "jpeg-encoder.h" > -#ifdef USE_LZ4 > -#include "lz4-encoder.h" > -#endif > -#include "zlib-encoder.h" > - > -struct RedClient; > - > -typedef struct RedCompressBuf RedCompressBuf; > -typedef struct RedGlzDrawable RedGlzDrawable; > -typedef struct ImageEncoders ImageEncoders; > -typedef struct ImageEncoderSharedData ImageEncoderSharedData; > -typedef struct GlzSharedDictionary GlzSharedDictionary; > -typedef struct GlzImageRetention GlzImageRetention; > - > -void image_encoder_shared_init(ImageEncoderSharedData *shared_data); > -void image_encoder_shared_stat_reset(ImageEncoderSharedData *shared_data); > -void image_encoder_shared_stat_print(const ImageEncoderSharedData > *shared_data); > - > -void image_encoders_init(ImageEncoders *enc, ImageEncoderSharedData > *shared_data); > -void image_encoders_free(ImageEncoders *enc); > -int image_encoders_free_some_independent_glz_drawables(ImageEncoders *enc); > -void image_encoders_free_glz_drawables(ImageEncoders *enc); > -void image_encoders_free_glz_drawables_to_free(ImageEncoders* enc); > -gboolean image_encoders_glz_create(ImageEncoders *enc, uint8_t id); > -void image_encoders_glz_get_restore_data(ImageEncoders *enc, > - uint8_t *out_id, > GlzEncDictRestoreData *out_data); > -gboolean image_encoders_glz_encode_lock(ImageEncoders *enc); > -void image_encoders_glz_encode_unlock(ImageEncoders *enc); > -void glz_retention_free_drawables(GlzImageRetention *ret); > -void glz_retention_detach_drawables(GlzImageRetention *ret); > - > -#define RED_COMPRESS_BUF_SIZE (1024 * 64) > -struct RedCompressBuf { > - /* This buffer provide space for compression algorithms. > - * Some algorithms access the buffer as an array of 32 bit words > - * so is defined to make sure is always aligned that way. > - */ > - union { > - uint8_t bytes[RED_COMPRESS_BUF_SIZE]; > - uint32_t words[RED_COMPRESS_BUF_SIZE / 4]; > - } buf; > - RedCompressBuf *send_next; > -}; > - > -static inline void compress_buf_free(RedCompressBuf *buf) > -{ > - g_free(buf); > -} > - > -gboolean image_encoders_get_glz_dictionary(ImageEncoders *enc, > - struct RedClient *client, > - uint8_t id, int window_size); > -gboolean image_encoders_restore_glz_dictionary(ImageEncoders *enc, > - struct RedClient *client, > - uint8_t id, > - GlzEncDictRestoreData > *restore_data); > - > -typedef struct { > - RedCompressBuf *bufs_head; > - RedCompressBuf *bufs_tail; > - jmp_buf jmp_env; > - union { > - struct { > - SpiceChunks *chunks; > - int next; > - int stride; > - int reverse; > - } lines_data; > - struct { > - RedCompressBuf* next; > - int size_left; > - } compressed_data; // for encoding data that was already compressed > by another method > - } u; > -} EncoderData; > - > -typedef struct { > - QuicUsrContext usr; > - EncoderData data; > -} QuicData; > - > -typedef struct { > - LzUsrContext usr; > - EncoderData data; > -} LzData; > - > -typedef struct { > - JpegEncoderUsrContext usr; > - EncoderData data; > -} JpegData; > - > -#ifdef USE_LZ4 > -typedef struct { > - Lz4EncoderUsrContext usr; > - EncoderData data; > -} Lz4Data; > -#endif > - > -typedef struct { > - ZlibEncoderUsrContext usr; > - EncoderData data; > -} ZlibData; > - > -typedef struct { > - GlzEncoderUsrContext usr; > - EncoderData data; > -} GlzData; > - > -struct GlzImageRetention { > - Ring ring; > -}; > - > -static inline void glz_retention_init(GlzImageRetention *ret) > -{ > - ring_init(&ret->ring); > -} > - > -struct ImageEncoderSharedData { > - uint32_t glz_drawable_count; > - > - stat_info_t off_stat; > - stat_info_t lz_stat; > - stat_info_t glz_stat; > - stat_info_t quic_stat; > - stat_info_t jpeg_stat; > - stat_info_t zlib_glz_stat; > - stat_info_t jpeg_alpha_stat; > - stat_info_t lz4_stat; > -}; > - > -struct ImageEncoders { > - ImageEncoderSharedData *shared_data; > - > - QuicData quic_data; > - QuicContext *quic; > - > - LzData lz_data; > - LzContext *lz; > - > - int jpeg_quality; > - > - JpegData jpeg_data; > - JpegEncoderContext *jpeg; > - > -#ifdef USE_LZ4 > - Lz4Data lz4_data; > - Lz4EncoderContext *lz4; > -#endif > - > - int zlib_level; > - > - ZlibData zlib_data; > - ZlibEncoder *zlib; > - > - /* global lz encoding entities */ > - GlzSharedDictionary *glz_dict; > - GlzEncoderContext *glz; > - GlzData glz_data; > - > - Ring glz_drawables; // all the living lz drawable, ordered > by encoding time > - Ring glz_drawables_inst_to_free; // list of instances to be > freed > - pthread_mutex_t glz_drawables_inst_to_free_lock; > -}; > - > -typedef struct compress_send_data_t { > - void* comp_buf; > - uint32_t comp_buf_size; > - SpicePalette *lzplt_palette; > - int is_lossy; > -} compress_send_data_t; > - > -int image_encoders_compress_quic(ImageEncoders *enc, SpiceImage *dest, > - SpiceBitmap *src, compress_send_data_t* > o_comp_data); > -int image_encoders_compress_lz(ImageEncoders *enc, > - SpiceImage *dest, SpiceBitmap *src, > - compress_send_data_t* o_comp_data); > -int image_encoders_compress_jpeg(ImageEncoders *enc, SpiceImage *dest, > - SpiceBitmap *src, compress_send_data_t* > o_comp_data); > -int image_encoders_compress_lz4(ImageEncoders *enc, SpiceImage *dest, > - SpiceBitmap *src, compress_send_data_t* > o_comp_data); > -int image_encoders_compress_glz(ImageEncoders *enc, > - SpiceImage *dest, SpiceBitmap *src, > - RedDrawable *red_drawable, > - GlzImageRetention *glz_retention, > - compress_send_data_t* o_comp_data, > - gboolean enable_zlib_glz_wrap); > - > -#define RED_RELEASE_BUNCH_SIZE 64 > - > -#endif /* DCC_ENCODERS_H_ */ > diff --git a/server/dcc.h b/server/dcc.h > index 0c19c02..5f7b16f 100644 > --- a/server/dcc.h > +++ b/server/dcc.h > @@ -21,7 +21,7 @@ > #include "red-worker.h" > #include "pixmap-cache.h" > #include "cache-item.h" > -#include "dcc-encoders.h" > +#include "image-encoders.h" > #include "stream.h" > #include "display-limits.h" > > diff --git a/server/image-encoders.c b/server/image-encoders.c > new file mode 100644 > index 0000000..b47ae35 > --- /dev/null > +++ b/server/image-encoders.c > @@ -0,0 +1,1388 @@ > +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ > +/* > + Copyright (C) 2009-2015 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/ > >. > +*/ > +#ifdef HAVE_CONFIG_H > +#include <config.h> > +#endif > + > +#include <glib.h> > + > +#include "image-encoders.h" > +#include "spice-bitmap-utils.h" > +#include "red-worker.h" // red_drawable_unref > +#include "pixmap-cache.h" // MAX_CACHE_CLIENTS > + > +#define ZLIB_DEFAULT_COMPRESSION_LEVEL 3 > + > +#define ENCODER_MESSAGE_SIZE 512 > + > +#define MAX_GLZ_DRAWABLE_INSTANCES 2 > + > +typedef struct GlzDrawableInstanceItem GlzDrawableInstanceItem; > + > +struct GlzSharedDictionary { > + RingItem base; > + GlzEncDictContext *dict; > + uint32_t refs; > + uint8_t id; > + pthread_rwlock_t encode_lock; > + int migrate_freeze; > + RedClient *client; // channel clients of the same client share the dict > +}; > + > +/* for each qxl drawable, there may be several instances of lz drawables */ > +/* TODO - reuse this stuff for the top level. I just added a second level of > multiplicity > + * at the Drawable by keeping a ring, so: > + * Drawable -> (ring of) RedGlzDrawable -> (up to 2) GlzDrawableInstanceItem > + * and it should probably (but need to be sure...) be > + * Drawable -> ring of GlzDrawableInstanceItem. > + */ > +struct GlzDrawableInstanceItem { > + RingItem glz_link; > + RingItem free_link; > + GlzEncDictImageContext *context; > + RedGlzDrawable *glz_drawable; > +}; > + > +struct RedGlzDrawable { > + RingItem link; // ordered by the time it was encoded > + RingItem drawable_link; > + RedDrawable *red_drawable; > + GlzDrawableInstanceItem instances_pool[MAX_GLZ_DRAWABLE_INSTANCES]; > + Ring instances; > + uint8_t instances_count; > + gboolean has_drawable; > + ImageEncoders *encoders; > +}; > + > +#define LINK_TO_GLZ(ptr) SPICE_CONTAINEROF((ptr), RedGlzDrawable, \ > + drawable_link) > +#define DRAWABLE_FOREACH_GLZ_SAFE(drawable, link, next, glz) \ > + SAFE_FOREACH(link, next, drawable, &(drawable)->glz_retention.ring, glz, > LINK_TO_GLZ(link)) > + > +static void glz_drawable_instance_item_free(GlzDrawableInstanceItem > *instance); > +static void encoder_data_init(EncoderData *data); > +static void encoder_data_reset(EncoderData *data); > +static void image_encoders_release_glz(ImageEncoders *enc); > + > + > +static SPICE_GNUC_NORETURN SPICE_GNUC_PRINTF(2, 3) void > +quic_usr_error(QuicUsrContext *usr, const char *fmt, ...) > +{ > + EncoderData *usr_data = &(((QuicData *)usr)->data); > + va_list ap; > + char message_buf[ENCODER_MESSAGE_SIZE]; > + > + va_start(ap, fmt); > + vsnprintf(message_buf, sizeof(message_buf), fmt, ap); > + va_end(ap); > + spice_critical("%s", message_buf); > + > + longjmp(usr_data->jmp_env, 1); > +} > + > +static SPICE_GNUC_NORETURN SPICE_GNUC_PRINTF(2, 3) void > +lz_usr_error(LzUsrContext *usr, const char *fmt, ...) > +{ > + EncoderData *usr_data = &(((LzData *)usr)->data); > + va_list ap; > + char message_buf[ENCODER_MESSAGE_SIZE]; > + > + va_start(ap, fmt); > + vsnprintf(message_buf, sizeof(message_buf), fmt, ap); > + va_end(ap); > + spice_critical("%s", message_buf); > + > + longjmp(usr_data->jmp_env, 1); > +} > + > +static SPICE_GNUC_PRINTF(2, 3) void > +glz_usr_error(GlzEncoderUsrContext *usr, const char *fmt, ...) > +{ > + va_list ap; > + char message_buf[ENCODER_MESSAGE_SIZE]; > + > + va_start(ap, fmt); > + vsnprintf(message_buf, sizeof(message_buf), fmt, ap); > + va_end(ap); > + > + spice_critical("%s", message_buf); // if global lz fails in the middle > + // the consequences are not > predictable since the window > + // can turn to be unsynchronized > between the server and > + // and the client > +} > + > +static SPICE_GNUC_PRINTF(2, 3) void > +quic_usr_warn(QuicUsrContext *usr, const char *fmt, ...) > +{ > + va_list ap; > + char message_buf[ENCODER_MESSAGE_SIZE]; > + > + va_start(ap, fmt); > + vsnprintf(message_buf, sizeof(message_buf), fmt, ap); > + va_end(ap); > + spice_warning("%s", message_buf); > +} > + > +static SPICE_GNUC_PRINTF(2, 3) void > +lz_usr_warn(LzUsrContext *usr, const char *fmt, ...) > +{ > + va_list ap; > + char message_buf[ENCODER_MESSAGE_SIZE]; > + > + va_start(ap, fmt); > + vsnprintf(message_buf, sizeof(message_buf), fmt, ap); > + va_end(ap); > + spice_warning("%s", message_buf); > +} > + > +static SPICE_GNUC_PRINTF(2, 3) void > +glz_usr_warn(GlzEncoderUsrContext *usr, const char *fmt, ...) > +{ > + va_list ap; > + char message_buf[ENCODER_MESSAGE_SIZE]; > + > + va_start(ap, fmt); > + vsnprintf(message_buf, sizeof(message_buf), fmt, ap); > + va_end(ap); > + spice_warning("%s", message_buf); > +} > + > +static void *quic_usr_malloc(QuicUsrContext *usr, int size) > +{ > + return spice_malloc(size); > +} > + > +static void *lz_usr_malloc(LzUsrContext *usr, int size) > +{ > + return spice_malloc(size); > +} > + > +static void *glz_usr_malloc(GlzEncoderUsrContext *usr, int size) > +{ > + return spice_malloc(size); > +} > + > +static void quic_usr_free(QuicUsrContext *usr, void *ptr) > +{ > + free(ptr); > +} > + > +static void lz_usr_free(LzUsrContext *usr, void *ptr) > +{ > + free(ptr); > +} > + > +static void glz_usr_free(GlzEncoderUsrContext *usr, void *ptr) > +{ > + free(ptr); > +} > + > +static void encoder_data_init(EncoderData *data) > +{ > + data->bufs_tail = g_new(RedCompressBuf, 1); > + data->bufs_head = data->bufs_tail; > + data->bufs_head->send_next = NULL; > +} > + > +static void encoder_data_reset(EncoderData *data) > +{ > + RedCompressBuf *buf = data->bufs_head; > + while (buf) { > + RedCompressBuf *next = buf->send_next; > + g_free(buf); > + buf = next; > + } > + data->bufs_head = data->bufs_tail = NULL; > +} > + > +/* Allocate more space for compressed buffer. > + * The pointer returned in io_ptr is garanteed to be aligned to 4 bytes. > + */ > +static int encoder_usr_more_space(EncoderData *enc_data, uint8_t **io_ptr) > +{ > + RedCompressBuf *buf; > + > + buf = g_new(RedCompressBuf, 1); > + enc_data->bufs_tail->send_next = buf; > + enc_data->bufs_tail = buf; > + buf->send_next = NULL; > + *io_ptr = buf->buf.bytes; > + return sizeof(buf->buf); > +} > + > +static int quic_usr_more_space(QuicUsrContext *usr, uint32_t **io_ptr, int > rows_completed) > +{ > + EncoderData *usr_data = &(((QuicData *)usr)->data); > + return encoder_usr_more_space(usr_data, (uint8_t **)io_ptr) / > sizeof(uint32_t); > +} > + > +static int lz_usr_more_space(LzUsrContext *usr, uint8_t **io_ptr) > +{ > + EncoderData *usr_data = &(((LzData *)usr)->data); > + return encoder_usr_more_space(usr_data, io_ptr); > +} > + > +static int glz_usr_more_space(GlzEncoderUsrContext *usr, uint8_t **io_ptr) > +{ > + EncoderData *usr_data = &(((GlzData *)usr)->data); > + return encoder_usr_more_space(usr_data, io_ptr); > +} > + > +static int jpeg_usr_more_space(JpegEncoderUsrContext *usr, uint8_t **io_ptr) > +{ > + EncoderData *usr_data = &(((JpegData *)usr)->data); > + return encoder_usr_more_space(usr_data, io_ptr); > +} > + > +#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, io_ptr); > +} > +#endif > + > +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, io_ptr); > +} > + > +static inline int encoder_usr_more_lines(EncoderData *enc_data, uint8_t > **lines) > +{ > + struct SpiceChunk *chunk; > + > + if (enc_data->u.lines_data.reverse) { > + if (!(enc_data->u.lines_data.next >= 0)) { > + return 0; > + } > + } else { > + if (!(enc_data->u.lines_data.next < enc_data->u.lines_data.chunks- > >num_chunks)) { > + return 0; > + } > + } > + > + chunk = &enc_data->u.lines_data.chunks->chunk[enc_data- > >u.lines_data.next]; > + if (chunk->len % enc_data->u.lines_data.stride) { > + return 0; > + } > + > + if (enc_data->u.lines_data.reverse) { > + enc_data->u.lines_data.next--; > + *lines = chunk->data + chunk->len - enc_data->u.lines_data.stride; > + } else { > + enc_data->u.lines_data.next++; > + *lines = chunk->data; > + } > + > + return chunk->len / enc_data->u.lines_data.stride; > +} > + > +static int quic_usr_more_lines(QuicUsrContext *usr, uint8_t **lines) > +{ > + EncoderData *usr_data = &(((QuicData *)usr)->data); > + return encoder_usr_more_lines(usr_data, lines); > +} > + > +static int lz_usr_more_lines(LzUsrContext *usr, uint8_t **lines) > +{ > + EncoderData *usr_data = &(((LzData *)usr)->data); > + return encoder_usr_more_lines(usr_data, lines); > +} > + > +static int glz_usr_more_lines(GlzEncoderUsrContext *usr, uint8_t **lines) > +{ > + EncoderData *usr_data = &(((GlzData *)usr)->data); > + return encoder_usr_more_lines(usr_data, lines); > +} > + > +static int jpeg_usr_more_lines(JpegEncoderUsrContext *usr, uint8_t **lines) > +{ > + EncoderData *usr_data = &(((JpegData *)usr)->data); > + 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); > + int buf_size; > + > + if (!usr_data->u.compressed_data.next) { > + spice_assert(usr_data->u.compressed_data.size_left == 0); > + return 0; > + } > + > + *input = usr_data->u.compressed_data.next->buf.bytes; > + 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 image_encoders_init_quic(ImageEncoders *enc) > +{ > + enc->quic_data.usr.error = quic_usr_error; > + enc->quic_data.usr.warn = quic_usr_warn; > + enc->quic_data.usr.info = quic_usr_warn; > + enc->quic_data.usr.malloc = quic_usr_malloc; > + enc->quic_data.usr.free = quic_usr_free; > + enc->quic_data.usr.more_space = quic_usr_more_space; > + enc->quic_data.usr.more_lines = quic_usr_more_lines; > + > + enc->quic = quic_create(&enc->quic_data.usr); > + > + if (!enc->quic) { > + spice_critical("create quic failed"); > + } > +} > + > +static void image_encoders_init_lz(ImageEncoders *enc) > +{ > + enc->lz_data.usr.error = lz_usr_error; > + enc->lz_data.usr.warn = lz_usr_warn; > + enc->lz_data.usr.info = lz_usr_warn; > + enc->lz_data.usr.malloc = lz_usr_malloc; > + enc->lz_data.usr.free = lz_usr_free; > + enc->lz_data.usr.more_space = lz_usr_more_space; > + enc->lz_data.usr.more_lines = lz_usr_more_lines; > + > + enc->lz = lz_create(&enc->lz_data.usr); > + > + if (!enc->lz) { > + spice_critical("create lz failed"); > + } > +} > + > +static void glz_usr_free_image(GlzEncoderUsrContext *usr, GlzUsrImageContext > *image) > +{ > + GlzData *lz_data = (GlzData *)usr; > + GlzDrawableInstanceItem *glz_drawable_instance = (GlzDrawableInstanceItem > *)image; > + ImageEncoders *drawable_enc = glz_drawable_instance->glz_drawable- > >encoders; > + ImageEncoders *this_enc = SPICE_CONTAINEROF(lz_data, ImageEncoders, > glz_data); > + if (this_enc == drawable_enc) { > + glz_drawable_instance_item_free(glz_drawable_instance); > + } else { > + /* The glz dictionary is shared between all DisplayChannelClient > + * instances that belong to the same client, and glz_usr_free_image > + * can be called by the dictionary code > + * (glz_dictionary_window_remove_head). Thus this function can be > + * called from any DisplayChannelClient thread, hence the need for > + * this check. > + */ > + pthread_mutex_lock(&drawable_enc->glz_drawables_inst_to_free_lock); > + ring_add_before(&glz_drawable_instance->free_link, > + &drawable_enc->glz_drawables_inst_to_free); > + pthread_mutex_unlock(&drawable_enc->glz_drawables_inst_to_free_lock); > + } > +} > + > +static void image_encoders_init_glz_data(ImageEncoders *enc) > +{ > + enc->glz_data.usr.error = glz_usr_error; > + enc->glz_data.usr.warn = glz_usr_warn; > + enc->glz_data.usr.info = glz_usr_warn; > + enc->glz_data.usr.malloc = glz_usr_malloc; > + enc->glz_data.usr.free = glz_usr_free; > + enc->glz_data.usr.more_space = glz_usr_more_space; > + enc->glz_data.usr.more_lines = glz_usr_more_lines; > + enc->glz_data.usr.free_image = glz_usr_free_image; > +} > + > +static void image_encoders_init_jpeg(ImageEncoders *enc) > +{ > + enc->jpeg_data.usr.more_space = jpeg_usr_more_space; > + enc->jpeg_data.usr.more_lines = jpeg_usr_more_lines; > + > + enc->jpeg = jpeg_encoder_create(&enc->jpeg_data.usr); > + > + if (!enc->jpeg) { > + spice_critical("create jpeg encoder failed"); > + } > +} > + > +#ifdef USE_LZ4 > +static inline void image_encoders_init_lz4(ImageEncoders *enc) > +{ > + enc->lz4_data.usr.more_space = lz4_usr_more_space; > + enc->lz4_data.usr.more_lines = lz4_usr_more_lines; > + > + enc->lz4 = lz4_encoder_create(&enc->lz4_data.usr); > + > + if (!enc->lz4) { > + spice_critical("create lz4 encoder failed"); > + } > +} > +#endif > + > +static void image_encoders_init_zlib(ImageEncoders *enc) > +{ > + enc->zlib_data.usr.more_space = zlib_usr_more_space; > + enc->zlib_data.usr.more_input = zlib_usr_more_input; > + > + enc->zlib = zlib_encoder_create(&enc->zlib_data.usr, > ZLIB_DEFAULT_COMPRESSION_LEVEL); > + > + if (!enc->zlib) { > + spice_critical("create zlib encoder failed"); > + } > +} > + > +void image_encoders_init(ImageEncoders *enc, ImageEncoderSharedData > *shared_data) > +{ > + spice_assert(shared_data); > + enc->shared_data = shared_data; > + > + ring_init(&enc->glz_drawables); > + ring_init(&enc->glz_drawables_inst_to_free); > + pthread_mutex_init(&enc->glz_drawables_inst_to_free_lock, NULL); > + > + image_encoders_init_glz_data(enc); > + image_encoders_init_quic(enc); > + image_encoders_init_lz(enc); > + image_encoders_init_jpeg(enc); > +#ifdef USE_LZ4 > + image_encoders_init_lz4(enc); > +#endif > + image_encoders_init_zlib(enc); > + > + // todo: tune level according to bandwidth > + enc->zlib_level = ZLIB_DEFAULT_COMPRESSION_LEVEL; > +} > + > +void image_encoders_free(ImageEncoders *enc) > +{ > + image_encoders_release_glz(enc); > + quic_destroy(enc->quic); > + enc->quic = NULL; > + lz_destroy(enc->lz); > + enc->lz = NULL; > + jpeg_encoder_destroy(enc->jpeg); > + enc->jpeg = NULL; > +#ifdef USE_LZ4 > + lz4_encoder_destroy(enc->lz4); > + enc->lz4 = NULL; > +#endif > + zlib_encoder_destroy(enc->zlib); > + enc->zlib = NULL; > +} > + > +/* Remove from the to_free list and the instances_list. > + When no instance is left - the RedGlzDrawable is released too. (and the > qxl drawable too, if > + it is not used by Drawable). > + NOTE - 1) can be called only by the display channel that created the > drawable > + 2) it is assumed that the instance was already removed from the > dictionary*/ > +static void glz_drawable_instance_item_free(GlzDrawableInstanceItem > *instance) > +{ > + RedGlzDrawable *glz_drawable; > + > + spice_assert(instance); > + spice_assert(instance->glz_drawable); > + > + glz_drawable = instance->glz_drawable; > + > + spice_assert(glz_drawable->instances_count > 0); > + > + ring_remove(&instance->glz_link); > + glz_drawable->instances_count--; > + > + // when the remove callback is performed from the channel that the > + // drawable belongs to, the instance is not added to the 'to_free' list > + if (ring_item_is_linked(&instance->free_link)) { > + ring_remove(&instance->free_link); > + } > + > + if (ring_is_empty(&glz_drawable->instances)) { > + spice_assert(glz_drawable->instances_count == 0); > + > + if (glz_drawable->has_drawable) { > + ring_remove(&glz_drawable->drawable_link); > + } > + red_drawable_unref(glz_drawable->red_drawable); > + glz_drawable->encoders->shared_data->glz_drawable_count--; > + if (ring_item_is_linked(&glz_drawable->link)) { > + ring_remove(&glz_drawable->link); > + } > + free(glz_drawable); > + } > +} > + > +/* > + * Releases all the instances of the drawable from the dictionary and the > display channel client. > + * The release of the last instance will also release the drawable itself and > the qxl drawable > + * if possible. > + * NOTE - the caller should prevent encoding using the dictionary during this > operation > + */ > +static void red_glz_drawable_free(RedGlzDrawable *glz_drawable) > +{ > + ImageEncoders *enc = glz_drawable->encoders; > + RingItem *head_instance = ring_get_head(&glz_drawable->instances); > + int cont = (head_instance != NULL); > + > + while (cont) { > + if (glz_drawable->instances_count == 1) { > + /* Last instance: glz_drawable_instance_item_free will free the > glz_drawable */ > + cont = FALSE; > + } > + GlzDrawableInstanceItem *instance = SPICE_CONTAINEROF(head_instance, > + GlzDrawableInstanceIt > em, > + glz_link); > + if (!ring_item_is_linked(&instance->free_link)) { > + // the instance didn't get out from window yet > + glz_enc_dictionary_remove_image(enc->glz_dict->dict, > + instance->context, > + &enc->glz_data.usr); > + } > + glz_drawable_instance_item_free(instance); > + > + if (cont) { > + head_instance = ring_get_head(&glz_drawable->instances); > + } > + } > +} > + > +gboolean image_encoders_glz_encode_lock(ImageEncoders *enc) > +{ > + if (enc->glz_dict) { > + pthread_rwlock_wrlock(&enc->glz_dict->encode_lock); > + return TRUE; > + } > + return FALSE; > +} > + > +void image_encoders_glz_encode_unlock(ImageEncoders *enc) > +{ > + if (enc->glz_dict) { > + pthread_rwlock_unlock(&enc->glz_dict->encode_lock); > + } > +} > + > +/* > + * Remove from the global lz dictionary some glz_drawables that have no > reference to > + * Drawable (their qxl drawables are released too). > + * NOTE - the caller should prevent encoding using the dictionary during the > operation > + */ > +int image_encoders_free_some_independent_glz_drawables(ImageEncoders *enc) > +{ > + RingItem *ring_link; > + int n = 0; > + > + if (!enc) { > + return 0; > + } > + ring_link = ring_get_head(&enc->glz_drawables); > + while ((n < RED_RELEASE_BUNCH_SIZE) && (ring_link != NULL)) { > + RedGlzDrawable *glz_drawable = SPICE_CONTAINEROF(ring_link, > RedGlzDrawable, link); > + ring_link = ring_next(&enc->glz_drawables, ring_link); > + if (!glz_drawable->has_drawable) { > + red_glz_drawable_free(glz_drawable); > + n++; > + } > + } > + return n; > +} > + > +void image_encoders_free_glz_drawables_to_free(ImageEncoders* enc) > +{ > + RingItem *ring_link; > + > + if (!enc->glz_dict) { > + return; > + } > + pthread_mutex_lock(&enc->glz_drawables_inst_to_free_lock); > + while ((ring_link = ring_get_head(&enc->glz_drawables_inst_to_free))) { > + GlzDrawableInstanceItem *drawable_instance = > SPICE_CONTAINEROF(ring_link, > + GlzDrawableI > nstanceItem, > + free_link); > + glz_drawable_instance_item_free(drawable_instance); > + } > + pthread_mutex_unlock(&enc->glz_drawables_inst_to_free_lock); > +} > + > +/* Clear all lz drawables - enforce their removal from the global dictionary. > + NOTE - prevents encoding using the dictionary during the operation*/ > +void image_encoders_free_glz_drawables(ImageEncoders *enc) > +{ > + RingItem *ring_link; > + GlzSharedDictionary *glz_dict = enc ? enc->glz_dict : NULL; > + > + if (!glz_dict) { > + return; > + } > + > + // assure no display channel is during global lz encoding > + pthread_rwlock_wrlock(&glz_dict->encode_lock); > + while ((ring_link = ring_get_head(&enc->glz_drawables))) { > + RedGlzDrawable *drawable = SPICE_CONTAINEROF(ring_link, > RedGlzDrawable, link); > + // no need to lock the to_free list, since we assured no other thread > is encoding and > + // thus not other thread access the to_free list of the channel > + red_glz_drawable_free(drawable); > + } > + pthread_rwlock_unlock(&glz_dict->encode_lock); > +} > + > +void glz_retention_free_drawables(GlzImageRetention *ret) > +{ > + RingItem *glz_item, *next_item; > + RedGlzDrawable *glz; > + SAFE_FOREACH(glz_item, next_item, TRUE, &ret->ring, glz, > LINK_TO_GLZ(glz_item)) { > + red_glz_drawable_free(glz); > + } > +} > + > +void glz_retention_detach_drawables(GlzImageRetention *ret) > +{ > + RingItem *item, *next; > + > + RING_FOREACH_SAFE(item, next, &ret->ring) { > + SPICE_CONTAINEROF(item, RedGlzDrawable, drawable_link)->has_drawable > = FALSE; > + ring_remove(item); > + } > +} > + > +static void image_encoders_freeze_glz(ImageEncoders *enc) > +{ > + pthread_rwlock_wrlock(&enc->glz_dict->encode_lock); > + enc->glz_dict->migrate_freeze = TRUE; > + pthread_rwlock_unlock(&enc->glz_dict->encode_lock); > +} > + > +void image_encoders_glz_get_restore_data(ImageEncoders *enc, > + uint8_t *out_id, > GlzEncDictRestoreData *out_data) > +{ > + spice_assert(enc->glz_dict); > + image_encoders_freeze_glz(enc); > + *out_id = enc->glz_dict->id; > + glz_enc_dictionary_get_restore_data(enc->glz_dict->dict, out_data, > + &enc->glz_data.usr); > +} > + > +static GlzSharedDictionary *glz_shared_dictionary_new(RedClient *client, > uint8_t id, > + GlzEncDictContext > *dict) > +{ > + spice_return_val_if_fail(dict != NULL, NULL); > + > + GlzSharedDictionary *shared_dict = spice_new0(GlzSharedDictionary, 1); > + > + shared_dict->dict = dict; > + shared_dict->id = id; > + shared_dict->refs = 1; > + shared_dict->migrate_freeze = FALSE; > + shared_dict->client = client; > + ring_item_init(&shared_dict->base); > + pthread_rwlock_init(&shared_dict->encode_lock, NULL); > + > + return shared_dict; > +} > + > +static pthread_mutex_t glz_dictionary_list_lock = PTHREAD_MUTEX_INITIALIZER; > +static Ring glz_dictionary_list = {&glz_dictionary_list, > &glz_dictionary_list}; > + > +static GlzSharedDictionary *find_glz_dictionary(RedClient *client, uint8_t > dict_id) > +{ > + RingItem *now; > + GlzSharedDictionary *ret = NULL; > + > + now = &glz_dictionary_list; > + while ((now = ring_next(&glz_dictionary_list, now))) { > + GlzSharedDictionary *dict = SPICE_UPCAST(GlzSharedDictionary, now); > + if ((dict->client == client) && (dict->id == dict_id)) { > + ret = dict; > + break; > + } > + } > + > + return ret; > +} > + > +#define MAX_LZ_ENCODERS MAX_CACHE_CLIENTS > + > +static GlzSharedDictionary *create_glz_dictionary(ImageEncoders *enc, > + RedClient *client, > + uint8_t id, int > window_size) > +{ > + spice_info("Lz Window %d Size=%d", id, window_size); > + > + GlzEncDictContext *glz_dict = > + glz_enc_dictionary_create(window_size, MAX_LZ_ENCODERS, &enc- > >glz_data.usr); > + > + return glz_shared_dictionary_new(client, id, glz_dict); > +} > + > +gboolean image_encoders_get_glz_dictionary(ImageEncoders *enc, > + RedClient *client, > + uint8_t id, int window_size) > +{ > + GlzSharedDictionary *shared_dict; > + > + spice_return_val_if_fail(!enc->glz_dict, FALSE); > + > + pthread_mutex_lock(&glz_dictionary_list_lock); > + > + shared_dict = find_glz_dictionary(client, id); > + if (shared_dict) { > + shared_dict->refs++; > + } else { > + shared_dict = create_glz_dictionary(enc, client, id, window_size); > + ring_add(&glz_dictionary_list, &shared_dict->base); > + } > + > + pthread_mutex_unlock(&glz_dictionary_list_lock); > + enc->glz_dict = shared_dict; > + return shared_dict != NULL; > +} > + > +static GlzSharedDictionary *restore_glz_dictionary(ImageEncoders *enc, > + RedClient *client, > + uint8_t id, > + GlzEncDictRestoreData > *restore_data) > +{ > + GlzEncDictContext *glz_dict = > + glz_enc_dictionary_restore(restore_data, &enc->glz_data.usr); > + > + return glz_shared_dictionary_new(client, id, glz_dict); > +} > + > +gboolean image_encoders_restore_glz_dictionary(ImageEncoders *enc, > + RedClient *client, > + uint8_t id, > + GlzEncDictRestoreData > *restore_data) > +{ > + GlzSharedDictionary *shared_dict = NULL; > + > + spice_return_val_if_fail(!enc->glz_dict, FALSE); > + > + pthread_mutex_lock(&glz_dictionary_list_lock); > + > + shared_dict = find_glz_dictionary(client, id); > + > + if (shared_dict) { > + shared_dict->refs++; > + } else { > + shared_dict = restore_glz_dictionary(enc, client, id, restore_data); > + ring_add(&glz_dictionary_list, &shared_dict->base); > + } > + > + pthread_mutex_unlock(&glz_dictionary_list_lock); > + enc->glz_dict = shared_dict; > + return shared_dict != NULL; > +} > + > +gboolean image_encoders_glz_create(ImageEncoders *enc, uint8_t id) > +{ > + enc->glz = glz_encoder_create(id, enc->glz_dict->dict, &enc- > >glz_data.usr); > + return enc->glz != NULL; > +} > + > +/* destroy encoder, and dictionary if no one uses it*/ > +static void image_encoders_release_glz(ImageEncoders *enc) > +{ > + GlzSharedDictionary *shared_dict; > + > + image_encoders_free_glz_drawables(enc); > + > + glz_encoder_destroy(enc->glz); > + enc->glz = NULL; > + > + if (!(shared_dict = enc->glz_dict)) { > + return; > + } > + > + enc->glz_dict = NULL; > + pthread_mutex_lock(&glz_dictionary_list_lock); > + if (--shared_dict->refs != 0) { > + pthread_mutex_unlock(&glz_dictionary_list_lock); > + return; > + } > + ring_remove(&shared_dict->base); > + pthread_mutex_unlock(&glz_dictionary_list_lock); > + glz_enc_dictionary_destroy(shared_dict->dict, &enc->glz_data.usr); > + free(shared_dict); > +} > + > +int image_encoders_compress_quic(ImageEncoders *enc, SpiceImage *dest, > + SpiceBitmap *src, compress_send_data_t* > o_comp_data) > +{ > + QuicData *quic_data = &enc->quic_data; > + QuicContext *quic = enc->quic; > + volatile QuicImageType type; > + int size, stride; > + stat_start_time_t start_time; > + stat_start_time_init(&start_time, &enc->shared_data->quic_stat); > + > +#ifdef COMPRESS_DEBUG > + spice_info("QUIC compress"); > +#endif > + > + switch (src->format) { > + case SPICE_BITMAP_FMT_32BIT: > + type = QUIC_IMAGE_TYPE_RGB32; > + break; > + case SPICE_BITMAP_FMT_RGBA: > + type = QUIC_IMAGE_TYPE_RGBA; > + break; > + case SPICE_BITMAP_FMT_16BIT: > + type = QUIC_IMAGE_TYPE_RGB16; > + break; > + case SPICE_BITMAP_FMT_24BIT: > + type = QUIC_IMAGE_TYPE_RGB24; > + break; > + default: > + return FALSE; > + } > + > + encoder_data_init(&quic_data->data); > + > + if (setjmp(quic_data->data.jmp_env)) { > + encoder_data_reset(&quic_data->data); > + return FALSE; > + } > + > + if (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE) { > + spice_chunks_linearize(src->data); > + } > + > + quic_data->data.u.lines_data.chunks = src->data; > + quic_data->data.u.lines_data.stride = src->stride; > + if ((src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) { > + quic_data->data.u.lines_data.next = 0; > + quic_data->data.u.lines_data.reverse = 0; > + stride = src->stride; > + } else { > + quic_data->data.u.lines_data.next = src->data->num_chunks - 1; > + quic_data->data.u.lines_data.reverse = 1; > + stride = -src->stride; > + } > + size = quic_encode(quic, type, src->x, src->y, NULL, 0, stride, > + quic_data->data.bufs_head->buf.words, > + G_N_ELEMENTS(quic_data->data.bufs_head->buf.words)); > + > + // the compressed buffer is bigger than the original data > + if ((size << 2) > (src->y * src->stride)) { > + longjmp(quic_data->data.jmp_env, 1); > + } > + > + dest->descriptor.type = SPICE_IMAGE_TYPE_QUIC; > + dest->u.quic.data_size = size << 2; > + > + o_comp_data->comp_buf = quic_data->data.bufs_head; > + o_comp_data->comp_buf_size = size << 2; > + > + stat_compress_add(&enc->shared_data->quic_stat, start_time, src->stride * > src->y, > + o_comp_data->comp_buf_size); > + return TRUE; > +} > + > +static const LzImageType bitmap_fmt_to_lz_image_type[] = { > + LZ_IMAGE_TYPE_INVALID, > + LZ_IMAGE_TYPE_PLT1_LE, > + LZ_IMAGE_TYPE_PLT1_BE, > + LZ_IMAGE_TYPE_PLT4_LE, > + LZ_IMAGE_TYPE_PLT4_BE, > + LZ_IMAGE_TYPE_PLT8, > + LZ_IMAGE_TYPE_RGB16, > + LZ_IMAGE_TYPE_RGB24, > + LZ_IMAGE_TYPE_RGB32, > + LZ_IMAGE_TYPE_RGBA, > + LZ_IMAGE_TYPE_A8 > +}; > + > +int image_encoders_compress_lz(ImageEncoders *enc, > + SpiceImage *dest, SpiceBitmap *src, > + compress_send_data_t* o_comp_data) > +{ > + LzData *lz_data = &enc->lz_data; > + LzContext *lz = enc->lz; > + LzImageType type = bitmap_fmt_to_lz_image_type[src->format]; > + int size; // size of the compressed data > + > + stat_start_time_t start_time; > + stat_start_time_init(&start_time, &enc->shared_data->lz_stat); > + > +#ifdef COMPRESS_DEBUG > + spice_info("LZ LOCAL compress"); > +#endif > + > + encoder_data_init(&lz_data->data); > + > + if (setjmp(lz_data->data.jmp_env)) { > + encoder_data_reset(&lz_data->data); > + return FALSE; > + } > + > + lz_data->data.u.lines_data.chunks = src->data; > + lz_data->data.u.lines_data.stride = src->stride; > + lz_data->data.u.lines_data.next = 0; > + lz_data->data.u.lines_data.reverse = 0; > + > + size = lz_encode(lz, type, src->x, src->y, > + !!(src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN), > + NULL, 0, src->stride, > + lz_data->data.bufs_head->buf.bytes, > + sizeof(lz_data->data.bufs_head->buf)); > + > + // the compressed buffer is bigger than the original data > + if (size > (src->y * src->stride)) { > + longjmp(lz_data->data.jmp_env, 1); > + } > + > + if (bitmap_fmt_is_rgb(src->format)) { > + dest->descriptor.type = SPICE_IMAGE_TYPE_LZ_RGB; > + dest->u.lz_rgb.data_size = size; > + > + o_comp_data->comp_buf = lz_data->data.bufs_head; > + o_comp_data->comp_buf_size = size; > + } else { > + /* masks are 1BIT bitmaps without palettes, but they are not > compressed > + * (see fill_mask) */ > + spice_assert(src->palette); > + dest->descriptor.type = SPICE_IMAGE_TYPE_LZ_PLT; > + dest->u.lz_plt.data_size = size; > + dest->u.lz_plt.flags = src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN; > + dest->u.lz_plt.palette = src->palette; > + dest->u.lz_plt.palette_id = src->palette->unique; > + o_comp_data->comp_buf = lz_data->data.bufs_head; > + o_comp_data->comp_buf_size = size; > + > + o_comp_data->lzplt_palette = dest->u.lz_plt.palette; > + } > + > + stat_compress_add(&enc->shared_data->lz_stat, start_time, src->stride * > src->y, > + o_comp_data->comp_buf_size); > + return TRUE; > +} > + > +int image_encoders_compress_jpeg(ImageEncoders *enc, SpiceImage *dest, > + SpiceBitmap *src, compress_send_data_t* > o_comp_data) > +{ > + JpegData *jpeg_data = &enc->jpeg_data; > + LzData *lz_data = &enc->lz_data; > + JpegEncoderContext *jpeg = enc->jpeg; > + LzContext *lz = enc->lz; > + volatile JpegEncoderImageType jpeg_in_type; > + int jpeg_size = 0; > + volatile int has_alpha = FALSE; > + int alpha_lz_size = 0; > + int comp_head_filled; > + int comp_head_left; > + int stride; > + uint8_t *lz_out_start_byte; > + stat_start_time_t start_time; > + stat_start_time_init(&start_time, &enc->shared_data->jpeg_alpha_stat); > + > +#ifdef COMPRESS_DEBUG > + spice_info("JPEG compress"); > +#endif > + > + switch (src->format) { > + case SPICE_BITMAP_FMT_16BIT: > + jpeg_in_type = JPEG_IMAGE_TYPE_RGB16; > + break; > + case SPICE_BITMAP_FMT_24BIT: > + jpeg_in_type = JPEG_IMAGE_TYPE_BGR24; > + break; > + case SPICE_BITMAP_FMT_32BIT: > + jpeg_in_type = JPEG_IMAGE_TYPE_BGRX32; > + break; > + case SPICE_BITMAP_FMT_RGBA: > + jpeg_in_type = JPEG_IMAGE_TYPE_BGRX32; > + has_alpha = TRUE; > + break; > + default: > + return FALSE; > + } > + > + encoder_data_init(&jpeg_data->data); > + > + if (setjmp(jpeg_data->data.jmp_env)) { > + encoder_data_reset(&jpeg_data->data); > + return FALSE; > + } > + > + if (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE) { > + spice_chunks_linearize(src->data); > + } > + > + jpeg_data->data.u.lines_data.chunks = src->data; > + jpeg_data->data.u.lines_data.stride = src->stride; > + if ((src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) { > + jpeg_data->data.u.lines_data.next = 0; > + jpeg_data->data.u.lines_data.reverse = 0; > + stride = src->stride; > + } else { > + jpeg_data->data.u.lines_data.next = src->data->num_chunks - 1; > + jpeg_data->data.u.lines_data.reverse = 1; > + stride = -src->stride; > + } > + jpeg_size = jpeg_encode(jpeg, enc->jpeg_quality, jpeg_in_type, > + src->x, src->y, NULL, > + 0, stride, jpeg_data->data.bufs_head->buf.bytes, > + sizeof(jpeg_data->data.bufs_head->buf)); > + > + // the compressed buffer is bigger than the original data > + if (jpeg_size > (src->y * src->stride)) { > + longjmp(jpeg_data->data.jmp_env, 1); > + } > + > + if (!has_alpha) { > + dest->descriptor.type = SPICE_IMAGE_TYPE_JPEG; > + dest->u.jpeg.data_size = jpeg_size; > + > + o_comp_data->comp_buf = jpeg_data->data.bufs_head; > + o_comp_data->comp_buf_size = jpeg_size; > + o_comp_data->is_lossy = TRUE; > + > + stat_compress_add(&enc->shared_data->jpeg_stat, start_time, src- > >stride * src->y, > + o_comp_data->comp_buf_size); > + return TRUE; > + } > + > + lz_data->data.bufs_head = jpeg_data->data.bufs_tail; > + lz_data->data.bufs_tail = lz_data->data.bufs_head; > + > + comp_head_filled = jpeg_size % sizeof(lz_data->data.bufs_head->buf); > + comp_head_left = sizeof(lz_data->data.bufs_head->buf) - comp_head_filled; > + lz_out_start_byte = lz_data->data.bufs_head->buf.bytes + > comp_head_filled; > + > + lz_data->data.u.lines_data.chunks = src->data; > + lz_data->data.u.lines_data.stride = src->stride; > + lz_data->data.u.lines_data.next = 0; > + lz_data->data.u.lines_data.reverse = 0; > + > + alpha_lz_size = lz_encode(lz, LZ_IMAGE_TYPE_XXXA, src->x, src->y, > + !!(src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN), > + NULL, 0, src->stride, > + lz_out_start_byte, > + comp_head_left); > + > + // the compressed buffer is bigger than the original data > + if ((jpeg_size + alpha_lz_size) > (src->y * src->stride)) { > + longjmp(jpeg_data->data.jmp_env, 1); > + } > + > + dest->descriptor.type = SPICE_IMAGE_TYPE_JPEG_ALPHA; > + dest->u.jpeg_alpha.flags = 0; > + if (src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN) { > + dest->u.jpeg_alpha.flags |= SPICE_JPEG_ALPHA_FLAGS_TOP_DOWN; > + } > + > + dest->u.jpeg_alpha.jpeg_size = jpeg_size; > + dest->u.jpeg_alpha.data_size = jpeg_size + alpha_lz_size; > + > + o_comp_data->comp_buf = jpeg_data->data.bufs_head; > + o_comp_data->comp_buf_size = jpeg_size + alpha_lz_size; > + o_comp_data->is_lossy = TRUE; > + stat_compress_add(&enc->shared_data->jpeg_alpha_stat, start_time, src- > >stride * src->y, > + o_comp_data->comp_buf_size); > + return TRUE; > +} > + > +#ifdef USE_LZ4 > +int image_encoders_compress_lz4(ImageEncoders *enc, SpiceImage *dest, > + SpiceBitmap *src, compress_send_data_t* > o_comp_data) > +{ > + Lz4Data *lz4_data = &enc->lz4_data; > + Lz4EncoderContext *lz4 = enc->lz4; > + int lz4_size = 0; > + stat_start_time_t start_time; > + stat_start_time_init(&start_time, &enc->shared_data->lz4_stat); > + > +#ifdef COMPRESS_DEBUG > + spice_info("LZ4 compress"); > +#endif > + > + encoder_data_init(&lz4_data->data); > + > + if (setjmp(lz4_data->data.jmp_env)) { > + encoder_data_reset(&lz4_data->data); > + 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->data.u.lines_data.next = 0; > + lz4_data->data.u.lines_data.reverse = 0; > + > + lz4_size = lz4_encode(lz4, src->y, src->stride, lz4_data->data.bufs_head- > >buf.bytes, > + sizeof(lz4_data->data.bufs_head->buf), > + src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN, src- > >format); > + > + // 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(&enc->shared_data->lz4_stat, start_time, src->stride * > src->y, > + o_comp_data->comp_buf_size); > + return TRUE; > +} > +#endif > + > +/* if already exists, returns it. Otherwise allocates and adds it (1) to the > ring tail > + in the channel (2) to the Drawable*/ > +static RedGlzDrawable *get_glz_drawable(ImageEncoders *enc, RedDrawable > *red_drawable, > + GlzImageRetention *glz_retention) > +{ > + RedGlzDrawable *ret; > + RingItem *item, *next; > + > + // TODO - I don't really understand what's going on here, so doing the > technical equivalent > + // now that we have multiple glz_dicts, so the only way to go from dcc to > drawable glz is to go > + // over the glz_ring (unless adding some better data structure then a > ring) > + SAFE_FOREACH(item, next, TRUE, &glz_retention->ring, ret, > LINK_TO_GLZ(item)) { > + if (ret->encoders == enc) { > + return ret; > + } > + } > + > + ret = spice_new(RedGlzDrawable, 1); > + > + ret->encoders = enc; > + ret->red_drawable = red_drawable_ref(red_drawable); > + ret->has_drawable = TRUE; > + ret->instances_count = 0; > + ring_init(&ret->instances); > + > + ring_item_init(&ret->link); > + ring_item_init(&ret->drawable_link); > + ring_add_before(&ret->link, &enc->glz_drawables); > + ring_add(&glz_retention->ring, &ret->drawable_link); > + enc->shared_data->glz_drawable_count++; > + return ret; > +} > + > +/* allocates new instance and adds it to instances in the given drawable. > + NOTE - the caller should set the glz_instance returned by the encoder by > itself.*/ > +static GlzDrawableInstanceItem *add_glz_drawable_instance(RedGlzDrawable > *glz_drawable) > +{ > + spice_assert(glz_drawable->instances_count < MAX_GLZ_DRAWABLE_INSTANCES); > + // NOTE: We assume the additions are performed consecutively, without > removals in the middle > + GlzDrawableInstanceItem *ret = glz_drawable->instances_pool + > glz_drawable->instances_count; > + glz_drawable->instances_count++; > + > + ring_item_init(&ret->free_link); > + ring_item_init(&ret->glz_link); > + ring_add(&glz_drawable->instances, &ret->glz_link); > + ret->context = NULL; > + ret->glz_drawable = glz_drawable; > + > + return ret; > +} > + > +#define MIN_GLZ_SIZE_FOR_ZLIB 100 > + > +int image_encoders_compress_glz(ImageEncoders *enc, > + SpiceImage *dest, SpiceBitmap *src, > + RedDrawable *red_drawable, > + GlzImageRetention *glz_retention, > + compress_send_data_t* o_comp_data, > + gboolean enable_zlib_glz_wrap) > +{ > + stat_start_time_t start_time; > + stat_start_time_init(&start_time, &enc->shared_data->zlib_glz_stat); > + spice_assert(bitmap_fmt_is_rgb(src->format)); > + GlzData *glz_data = &enc->glz_data; > + ZlibData *zlib_data; > + LzImageType type = bitmap_fmt_to_lz_image_type[src->format]; > + RedGlzDrawable *glz_drawable; > + GlzDrawableInstanceItem *glz_drawable_instance; > + int glz_size; > + int zlib_size; > + > +#ifdef COMPRESS_DEBUG > + spice_info("LZ global compress fmt=%d", src->format); > +#endif > + > + if ((src->x * src->y) >= glz_enc_dictionary_get_size(enc->glz_dict- > >dict)) { > + return FALSE; > + } > + > + pthread_rwlock_rdlock(&enc->glz_dict->encode_lock); > + /* using the global dictionary only if it is not frozen */ > + if (enc->glz_dict->migrate_freeze) { > + pthread_rwlock_unlock(&enc->glz_dict->encode_lock); > + return FALSE; > + } > + > + encoder_data_init(&glz_data->data); > + > + glz_drawable = get_glz_drawable(enc, red_drawable, glz_retention); > + glz_drawable_instance = add_glz_drawable_instance(glz_drawable); > + > + glz_data->data.u.lines_data.chunks = src->data; > + glz_data->data.u.lines_data.stride = src->stride; > + glz_data->data.u.lines_data.next = 0; > + glz_data->data.u.lines_data.reverse = 0; > + > + glz_size = glz_encode(enc->glz, type, src->x, src->y, > + (src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN), NULL, > 0, > + src->stride, glz_data->data.bufs_head->buf.bytes, > + sizeof(glz_data->data.bufs_head->buf), > + glz_drawable_instance, > + &glz_drawable_instance->context); > + > + stat_compress_add(&enc->shared_data->glz_stat, start_time, src->stride * > src->y, glz_size); > + > + if (!enable_zlib_glz_wrap || (glz_size < MIN_GLZ_SIZE_FOR_ZLIB)) { > + goto glz; > + } > + stat_start_time_init(&start_time, &enc->shared_data->zlib_glz_stat); > + zlib_data = &enc->zlib_data; > + > + encoder_data_init(&zlib_data->data); > + > + 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(enc->zlib, enc->zlib_level, > + glz_size, zlib_data->data.bufs_head->buf.bytes, > + sizeof(zlib_data->data.bufs_head->buf)); > + > + // the compressed buffer is bigger than the original data > + if (zlib_size >= glz_size) { > + encoder_data_reset(&zlib_data->data); > + goto glz; > + } else { > + encoder_data_reset(&glz_data->data); > + } > + > + dest->descriptor.type = SPICE_IMAGE_TYPE_ZLIB_GLZ_RGB; > + dest->u.zlib_glz.glz_data_size = glz_size; > + dest->u.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(&enc->shared_data->zlib_glz_stat, start_time, glz_size, > zlib_size); > + pthread_rwlock_unlock(&enc->glz_dict->encode_lock); > + return TRUE; > + > +glz: > + pthread_rwlock_unlock(&enc->glz_dict->encode_lock); > + > + dest->descriptor.type = SPICE_IMAGE_TYPE_GLZ_RGB; > + dest->u.lz_rgb.data_size = glz_size; > + > + o_comp_data->comp_buf = glz_data->data.bufs_head; > + o_comp_data->comp_buf_size = glz_size; > + > + return TRUE; > +} > + > +void image_encoder_shared_init(ImageEncoderSharedData *shared_data) > +{ > + clockid_t stat_clock = CLOCK_THREAD_CPUTIME_ID; > + > + stat_compress_init(&shared_data->off_stat, "off", stat_clock); > + stat_compress_init(&shared_data->lz_stat, "lz", stat_clock); > + stat_compress_init(&shared_data->glz_stat, "glz", stat_clock); > + stat_compress_init(&shared_data->quic_stat, "quic", stat_clock); > + stat_compress_init(&shared_data->jpeg_stat, "jpeg", stat_clock); > + stat_compress_init(&shared_data->zlib_glz_stat, "zlib", stat_clock); > + stat_compress_init(&shared_data->jpeg_alpha_stat, "jpeg_alpha", > stat_clock); > + stat_compress_init(&shared_data->lz4_stat, "lz4", stat_clock); > +} > + > +void image_encoder_shared_stat_reset(ImageEncoderSharedData *shared_data) > +{ > + stat_reset(&shared_data->off_stat); > + stat_reset(&shared_data->quic_stat); > + stat_reset(&shared_data->lz_stat); > + stat_reset(&shared_data->glz_stat); > + stat_reset(&shared_data->jpeg_stat); > + stat_reset(&shared_data->zlib_glz_stat); > + stat_reset(&shared_data->jpeg_alpha_stat); > + stat_reset(&shared_data->lz4_stat); > +} > + > +#define STAT_FMT "%s\t%8u\t%13.8g\t%12.8g\t%12.8g" > + > +#ifdef COMPRESS_STAT > +static void stat_print_one(const char *name, const stat_info_t *stat) > +{ > + spice_info(STAT_FMT, name, stat->count, > + stat_byte_to_mega(stat->orig_size), > + stat_byte_to_mega(stat->comp_size), > + stat_cpu_time_to_sec(stat->total)); > +} > + > +static void stat_sum(stat_info_t *total, const stat_info_t *stat) > +{ > + total->count += stat->count; > + total->orig_size += stat->orig_size; > + total->comp_size += stat->comp_size; > + total->total += stat->total; > +} > +#endif > + > +void image_encoder_shared_stat_print(const ImageEncoderSharedData > *shared_data) > +{ > +#ifdef COMPRESS_STAT > + /* sum all statistics */ > + stat_info_t total = { > + .count = 0, > + .orig_size = 0, > + .comp_size = 0, > + .total = 0 > + }; > + stat_sum(&total, &shared_data->off_stat); > + stat_sum(&total, &shared_data->quic_stat); > + stat_sum(&total, &shared_data->glz_stat); > + stat_sum(&total, &shared_data->lz_stat); > + stat_sum(&total, &shared_data->jpeg_stat); > + stat_sum(&total, &shared_data->jpeg_alpha_stat); > + stat_sum(&total, &shared_data->lz4_stat); > + > + /* fix for zlib glz */ > + total.total += shared_data->zlib_glz_stat.total; > + if (shared_data->zlib_glz_stat.count) { > + total.comp_size = total.comp_size - shared_data->glz_stat.comp_size + > + shared_data->zlib_glz_stat.comp_size; > + } > + > + spice_info("Method \t count \torig_size(MB)\tenc_size(MB)\tenc_time(s > )"); > + stat_print_one("OFF ", &shared_data->off_stat); > + stat_print_one("QUIC ", &shared_data->quic_stat); > + stat_print_one("GLZ ", &shared_data->glz_stat); > + stat_print_one("ZLIB GLZ ", &shared_data->zlib_glz_stat); > + stat_print_one("LZ ", &shared_data->lz_stat); > + stat_print_one("JPEG ", &shared_data->jpeg_stat); > + stat_print_one("JPEG-RGBA", &shared_data->jpeg_alpha_stat); > + stat_print_one("LZ4 ", &shared_data->lz4_stat); > + spice_info("------------------------------------------------------------- > ------"); > + stat_print_one("Total ", &total); > +#endif > +} > diff --git a/server/image-encoders.h b/server/image-encoders.h > new file mode 100644 > index 0000000..9286970 > --- /dev/null > +++ b/server/image-encoders.h > @@ -0,0 +1,217 @@ > +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ > +/* > + Copyright (C) 2009-2015 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 DCC_ENCODERS_H_ > +#define DCC_ENCODERS_H_ Change this to IMAGE_ENCODERS_H_? > + > +#include <setjmp.h> > +#include <common/quic.h> > +#include <common/lz.h> > + > +#include "stat.h" > +#include "red-parse-qxl.h" > +#include "glz-encoder.h" > +#include "jpeg-encoder.h" > +#ifdef USE_LZ4 > +#include "lz4-encoder.h" > +#endif > +#include "zlib-encoder.h" > + > +struct RedClient; > + > +typedef struct RedCompressBuf RedCompressBuf; > +typedef struct RedGlzDrawable RedGlzDrawable; > +typedef struct ImageEncoders ImageEncoders; > +typedef struct ImageEncoderSharedData ImageEncoderSharedData; > +typedef struct GlzSharedDictionary GlzSharedDictionary; > +typedef struct GlzImageRetention GlzImageRetention; > + > +void image_encoder_shared_init(ImageEncoderSharedData *shared_data); > +void image_encoder_shared_stat_reset(ImageEncoderSharedData *shared_data); > +void image_encoder_shared_stat_print(const ImageEncoderSharedData > *shared_data); > + > +void image_encoders_init(ImageEncoders *enc, ImageEncoderSharedData > *shared_data); > +void image_encoders_free(ImageEncoders *enc); > +int image_encoders_free_some_independent_glz_drawables(ImageEncoders *enc); > +void image_encoders_free_glz_drawables(ImageEncoders *enc); > +void image_encoders_free_glz_drawables_to_free(ImageEncoders* enc); > +gboolean image_encoders_glz_create(ImageEncoders *enc, uint8_t id); > +void image_encoders_glz_get_restore_data(ImageEncoders *enc, > + uint8_t *out_id, > GlzEncDictRestoreData *out_data); > +gboolean image_encoders_glz_encode_lock(ImageEncoders *enc); > +void image_encoders_glz_encode_unlock(ImageEncoders *enc); > +void glz_retention_free_drawables(GlzImageRetention *ret); > +void glz_retention_detach_drawables(GlzImageRetention *ret); > + > +#define RED_COMPRESS_BUF_SIZE (1024 * 64) > +struct RedCompressBuf { > + /* This buffer provide space for compression algorithms. > + * Some algorithms access the buffer as an array of 32 bit words > + * so is defined to make sure is always aligned that way. > + */ > + union { > + uint8_t bytes[RED_COMPRESS_BUF_SIZE]; > + uint32_t words[RED_COMPRESS_BUF_SIZE / 4]; > + } buf; > + RedCompressBuf *send_next; > +}; > + > +static inline void compress_buf_free(RedCompressBuf *buf) > +{ > + g_free(buf); > +} > + > +gboolean image_encoders_get_glz_dictionary(ImageEncoders *enc, > + struct RedClient *client, > + uint8_t id, int window_size); > +gboolean image_encoders_restore_glz_dictionary(ImageEncoders *enc, > + struct RedClient *client, > + uint8_t id, > + GlzEncDictRestoreData > *restore_data); > + > +typedef struct { > + RedCompressBuf *bufs_head; > + RedCompressBuf *bufs_tail; > + jmp_buf jmp_env; > + union { > + struct { > + SpiceChunks *chunks; > + int next; > + int stride; > + int reverse; > + } lines_data; > + struct { > + RedCompressBuf* next; > + int size_left; > + } compressed_data; // for encoding data that was already compressed > by another method > + } u; > +} EncoderData; > + > +typedef struct { > + QuicUsrContext usr; > + EncoderData data; > +} QuicData; > + > +typedef struct { > + LzUsrContext usr; > + EncoderData data; > +} LzData; > + > +typedef struct { > + JpegEncoderUsrContext usr; > + EncoderData data; > +} JpegData; > + > +#ifdef USE_LZ4 > +typedef struct { > + Lz4EncoderUsrContext usr; > + EncoderData data; > +} Lz4Data; > +#endif > + > +typedef struct { > + ZlibEncoderUsrContext usr; > + EncoderData data; > +} ZlibData; > + > +typedef struct { > + GlzEncoderUsrContext usr; > + EncoderData data; > +} GlzData; > + > +struct GlzImageRetention { > + Ring ring; > +}; > + > +static inline void glz_retention_init(GlzImageRetention *ret) > +{ > + ring_init(&ret->ring); > +} > + > +struct ImageEncoderSharedData { > + uint32_t glz_drawable_count; > + > + stat_info_t off_stat; > + stat_info_t lz_stat; > + stat_info_t glz_stat; > + stat_info_t quic_stat; > + stat_info_t jpeg_stat; > + stat_info_t zlib_glz_stat; > + stat_info_t jpeg_alpha_stat; > + stat_info_t lz4_stat; > +}; > + > +struct ImageEncoders { > + ImageEncoderSharedData *shared_data; > + > + QuicData quic_data; > + QuicContext *quic; > + > + LzData lz_data; > + LzContext *lz; > + > + int jpeg_quality; > + > + JpegData jpeg_data; > + JpegEncoderContext *jpeg; > + > +#ifdef USE_LZ4 > + Lz4Data lz4_data; > + Lz4EncoderContext *lz4; > +#endif > + > + int zlib_level; > + > + ZlibData zlib_data; > + ZlibEncoder *zlib; > + > + /* global lz encoding entities */ > + GlzSharedDictionary *glz_dict; > + GlzEncoderContext *glz; > + GlzData glz_data; > + > + Ring glz_drawables; // all the living lz drawable, ordered > by encoding time > + Ring glz_drawables_inst_to_free; // list of instances to be > freed > + pthread_mutex_t glz_drawables_inst_to_free_lock; > +}; > + > +typedef struct compress_send_data_t { > + void* comp_buf; > + uint32_t comp_buf_size; > + SpicePalette *lzplt_palette; > + int is_lossy; > +} compress_send_data_t; > + > +int image_encoders_compress_quic(ImageEncoders *enc, SpiceImage *dest, > + SpiceBitmap *src, compress_send_data_t* > o_comp_data); > +int image_encoders_compress_lz(ImageEncoders *enc, > + SpiceImage *dest, SpiceBitmap *src, > + compress_send_data_t* o_comp_data); > +int image_encoders_compress_jpeg(ImageEncoders *enc, SpiceImage *dest, > + SpiceBitmap *src, compress_send_data_t* > o_comp_data); > +int image_encoders_compress_lz4(ImageEncoders *enc, SpiceImage *dest, > + SpiceBitmap *src, compress_send_data_t* > o_comp_data); > +int image_encoders_compress_glz(ImageEncoders *enc, > + SpiceImage *dest, SpiceBitmap *src, > + RedDrawable *red_drawable, > + GlzImageRetention *glz_retention, > + compress_send_data_t* o_comp_data, > + gboolean enable_zlib_glz_wrap); > + > +#define RED_RELEASE_BUNCH_SIZE 64 > + > +#endif /* DCC_ENCODERS_H_ */ Aside from the comment about the header guards, looks good. Acked-by: Jonathon Jongsma <jjongsma@xxxxxxxxxx> _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel