[Spice-devel] [PATCH v2 07/18] worker: move compress to dcc_compress_image()

Jonathon Jongsma jjongsma at redhat.com
Wed Nov 18 13:45:50 PST 2015


From: Marc-André Lureau <marcandre.lureau at gmail.com>

---
Changes since v1:
 - rebased to resolve conflicts due to removed "fixme: remove?" lines from
   patch 01/18

 server/dcc.c             | 631 ++++++++++++++++++++++++++++++++++++++++++
 server/dcc.h             |  33 +++
 server/display-channel.h |   4 +
 server/red_parse_qxl.h   |   6 +
 server/red_worker.c      | 695 ++---------------------------------------------
 5 files changed, 695 insertions(+), 674 deletions(-)

diff --git a/server/dcc.c b/server/dcc.c
index 2fb0af6..55a810b 100644
--- a/server/dcc.c
+++ b/server/dcc.c
@@ -305,3 +305,634 @@ void dcc_destroy_surface(DisplayChannelClient *dcc, uint32_t surface_id)
     destroy = surface_destroy_item_new(channel, surface_id);
     red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), &destroy->pipe_item);
 }
+
+/* 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(DisplayChannelClient *dcc, Drawable *drawable)
+{
+    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)
+    DRAWABLE_FOREACH_GLZ_SAFE(drawable, item, next, ret) {
+        if (ret->dcc == dcc) {
+            return ret;
+        }
+    }
+
+    ret = spice_new(RedGlzDrawable, 1);
+
+    ret->dcc = dcc;
+    ret->red_drawable = red_drawable_ref(drawable->red_drawable);
+    ret->drawable = drawable;
+    ret->group_id = drawable->group_id;
+    ret->instances_count = 0;
+    ring_init(&ret->instances);
+
+    ring_item_init(&ret->link);
+    ring_item_init(&ret->drawable_link);
+    ring_add_before(&ret->link, &dcc->glz_drawables);
+    ring_add(&drawable->glz_ring, &ret->drawable_link);
+    dcc->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->glz_instance = NULL;
+    ret->red_glz_drawable = glz_drawable;
+
+    return ret;
+}
+
+#define MIN_GLZ_SIZE_FOR_ZLIB 100
+
+int dcc_compress_image_glz(DisplayChannelClient *dcc,
+                           SpiceImage *dest, SpiceBitmap *src, Drawable *drawable,
+                           compress_send_data_t* o_comp_data)
+{
+    DisplayChannel *display_channel = DCC_TO_DC(dcc);
+#ifdef COMPRESS_STAT
+    stat_time_t start_time = stat_now(display_channel->glz_stat.clock);
+#endif
+    spice_assert(bitmap_fmt_is_rgb(src->format));
+    GlzData *glz_data = &dcc->glz_data;
+    ZlibData *zlib_data;
+    LzImageType type = MAP_BITMAP_FMT_TO_LZ_IMAGE_TYPE[src->format];
+    RedGlzDrawable *glz_drawable;
+    GlzDrawableInstanceItem *glz_drawable_instance;
+    int glz_size;
+    int zlib_size;
+
+    glz_data->data.bufs_tail = compress_buf_new();
+    glz_data->data.bufs_head = glz_data->data.bufs_tail;
+    glz_data->data.dcc = dcc;
+
+    glz_drawable = get_glz_drawable(dcc, drawable);
+    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(dcc->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->glz_instance);
+
+    stat_compress_add(&display_channel->glz_stat, start_time, src->stride * src->y, glz_size);
+
+    if (!display_channel->enable_zlib_glz_wrap || (glz_size < MIN_GLZ_SIZE_FOR_ZLIB)) {
+        goto glz;
+    }
+#ifdef COMPRESS_STAT
+    start_time = stat_now(display_channel->zlib_glz_stat.clock);
+#endif
+    zlib_data = &dcc->zlib_data;
+
+    zlib_data->data.bufs_tail = compress_buf_new();
+    zlib_data->data.bufs_head = zlib_data->data.bufs_tail;
+    zlib_data->data.dcc = dcc;
+
+    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(dcc->zlib, dcc->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) {
+        while (zlib_data->data.bufs_head) {
+            RedCompressBuf *buf = zlib_data->data.bufs_head;
+            zlib_data->data.bufs_head = buf->send_next;
+            compress_buf_free(buf);
+        }
+        goto glz;
+    }
+
+    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(&display_channel->zlib_glz_stat, start_time, glz_size, zlib_size);
+    return TRUE;
+glz:
+    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;
+}
+
+int dcc_compress_image_lz(DisplayChannelClient *dcc,
+                          SpiceImage *dest, SpiceBitmap *src,
+                          compress_send_data_t* o_comp_data, uint32_t group_id)
+{
+    LzData *lz_data = &dcc->lz_data;
+    LzContext *lz = dcc->lz;
+    LzImageType type = MAP_BITMAP_FMT_TO_LZ_IMAGE_TYPE[src->format];
+    int size;            // size of the compressed data
+
+#ifdef COMPRESS_STAT
+    stat_time_t start_time = stat_now(DCC_TO_DC(dcc)->lz_stat.clock);
+#endif
+
+    lz_data->data.bufs_tail = compress_buf_new();
+    lz_data->data.bufs_head = lz_data->data.bufs_tail;
+    lz_data->data.dcc = dcc;
+
+    if (setjmp(lz_data->data.jmp_env)) {
+        while (lz_data->data.bufs_head) {
+            RedCompressBuf *buf = lz_data->data.bufs_head;
+            lz_data->data.bufs_head = buf->send_next;
+            compress_buf_free(buf);
+        }
+        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;
+
+        dcc_palette_cache_palette(dcc, dest->u.lz_plt.palette, &(dest->u.lz_plt.flags));
+        o_comp_data->lzplt_palette = dest->u.lz_plt.palette;
+    }
+
+    stat_compress_add(&DCC_TO_DC(dcc)->lz_stat, start_time, src->stride * src->y,
+                      o_comp_data->comp_buf_size);
+    return TRUE;
+}
+
+int dcc_compress_image_jpeg(DisplayChannelClient *dcc, SpiceImage *dest,
+                            SpiceBitmap *src, compress_send_data_t* o_comp_data,
+                            uint32_t group_id)
+{
+    JpegData *jpeg_data = &dcc->jpeg_data;
+    LzData *lz_data = &dcc->lz_data;
+    JpegEncoderContext *jpeg = dcc->jpeg;
+    LzContext *lz = dcc->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;
+
+#ifdef COMPRESS_STAT
+    stat_time_t start_time = stat_now(DCC_TO_DC(dcc)->jpeg_stat.clock);
+#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;
+    }
+
+    jpeg_data->data.bufs_tail = compress_buf_new();
+    jpeg_data->data.bufs_head = jpeg_data->data.bufs_tail;
+    jpeg_data->data.dcc = dcc;
+
+    if (setjmp(jpeg_data->data.jmp_env)) {
+        while (jpeg_data->data.bufs_head) {
+            RedCompressBuf *buf = jpeg_data->data.bufs_head;
+            jpeg_data->data.bufs_head = buf->send_next;
+            compress_buf_free(buf);
+        }
+        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, dcc->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(&DCC_TO_DC(dcc)->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.dcc = dcc;
+
+    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(&DCC_TO_DC(dcc)->jpeg_alpha_stat, start_time, src->stride * src->y,
+                      o_comp_data->comp_buf_size);
+    return TRUE;
+}
+
+#ifdef USE_LZ4
+int dcc_compress_image_lz4(DisplayChannelClient *dcc, SpiceImage *dest,
+                           SpiceBitmap *src, compress_send_data_t* o_comp_data,
+                           uint32_t group_id)
+{
+    DisplayChannel *display_channel = DCC_TO_DC(dcc);
+    Lz4Data *lz4_data = &dcc->lz4_data;
+    Lz4EncoderContext *lz4 = dcc->lz4;
+    int lz4_size = 0;
+
+#ifdef COMPRESS_STAT
+    stat_time_t start_time = stat_now(display_channel->lz4_stat.clock);
+#endif
+
+    lz4_data->data.bufs_tail = compress_buf_new();
+    lz4_data->data.bufs_head = lz4_data->data.bufs_tail;
+
+    if (!lz4_data->data.bufs_head) {
+        spice_warning("failed to allocate compress buffer");
+        return FALSE;
+    }
+
+    lz4_data->data.bufs_head->send_next = NULL;
+    lz4_data->data.dcc = dcc;
+
+    if (setjmp(lz4_data->data.jmp_env)) {
+        while (lz4_data->data.bufs_head) {
+            RedCompressBuf *buf = lz4_data->data.bufs_head;
+            lz4_data->data.bufs_head = buf->send_next;
+            compress_buf_free(buf);
+        }
+        return FALSE;
+    }
+
+    if (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE) {
+        spice_chunks_linearize(src->data);
+    }
+
+    lz4_data->data.u.lines_data.chunks = src->data;
+    lz4_data->data.u.lines_data.stride = src->stride;
+    lz4_data->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(&display_channel->lz4_stat, start_time, src->stride * src->y,
+                      o_comp_data->comp_buf_size);
+    return TRUE;
+}
+#endif
+
+int dcc_compress_image_quic(DisplayChannelClient *dcc, SpiceImage *dest,
+                            SpiceBitmap *src, compress_send_data_t* o_comp_data,
+                            uint32_t group_id)
+{
+    QuicData *quic_data = &dcc->quic_data;
+    QuicContext *quic = dcc->quic;
+    volatile QuicImageType type;
+    int size, stride;
+
+#ifdef COMPRESS_STAT
+    stat_time_t start_time = stat_now(DCC_TO_DC(dcc)->quic_stat.clock);
+#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;
+    }
+
+    quic_data->data.bufs_tail = compress_buf_new();
+    quic_data->data.bufs_head = quic_data->data.bufs_tail;
+    quic_data->data.dcc = dcc;
+
+    if (setjmp(quic_data->data.jmp_env)) {
+        while (quic_data->data.bufs_head) {
+            RedCompressBuf *buf = quic_data->data.bufs_head;
+            quic_data->data.bufs_head = buf->send_next;
+            compress_buf_free(buf);
+        }
+        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(&DCC_TO_DC(dcc)->quic_stat, start_time, src->stride * src->y,
+                      o_comp_data->comp_buf_size);
+    return TRUE;
+}
+
+#define MIN_SIZE_TO_COMPRESS 54
+#define MIN_DIMENSION_TO_QUIC 3
+int dcc_compress_image(DisplayChannelClient *dcc,
+                       SpiceImage *dest, SpiceBitmap *src, Drawable *drawable,
+                       int can_lossy,
+                       compress_send_data_t* o_comp_data)
+{
+    DisplayChannel *display_channel = DCC_TO_DC(dcc);
+    SpiceImageCompression image_compression = dcc->image_compression;
+    int quic_compress = FALSE;
+
+    if ((image_compression == SPICE_IMAGE_COMPRESSION_OFF) ||
+        ((src->y * src->stride) < MIN_SIZE_TO_COMPRESS)) { // TODO: change the size cond
+        return FALSE;
+    } else if (image_compression == SPICE_IMAGE_COMPRESSION_QUIC) {
+        if (bitmap_fmt_is_plt(src->format)) {
+            return FALSE;
+        } else {
+            quic_compress = TRUE;
+        }
+    } else {
+        /*
+            lz doesn't handle (1) bitmaps with strides that are larger than the width
+            of the image in bytes (2) unstable bitmaps
+        */
+        if (bitmap_has_extra_stride(src) || (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE)) {
+            if ((image_compression == SPICE_IMAGE_COMPRESSION_LZ) ||
+                (image_compression == SPICE_IMAGE_COMPRESSION_GLZ) ||
+                (image_compression == SPICE_IMAGE_COMPRESSION_LZ4) ||
+                bitmap_fmt_is_plt(src->format)) {
+                return FALSE;
+            } else {
+                quic_compress = TRUE;
+            }
+        } else {
+            if ((image_compression == SPICE_IMAGE_COMPRESSION_AUTO_LZ) ||
+                (image_compression == SPICE_IMAGE_COMPRESSION_AUTO_GLZ)) {
+                if ((src->x < MIN_DIMENSION_TO_QUIC) || (src->y < MIN_DIMENSION_TO_QUIC)) {
+                    quic_compress = FALSE;
+                } else {
+                    if (drawable->copy_bitmap_graduality == BITMAP_GRADUAL_INVALID) {
+                        quic_compress = bitmap_fmt_has_graduality(src->format) &&
+                            bitmap_get_graduality_level(src) == BITMAP_GRADUAL_HIGH;
+                    } else {
+                        quic_compress = (drawable->copy_bitmap_graduality == BITMAP_GRADUAL_HIGH);
+                    }
+                }
+            } else {
+                quic_compress = FALSE;
+            }
+        }
+    }
+
+    if (quic_compress) {
+#ifdef COMPRESS_DEBUG
+        spice_info("QUIC compress");
+#endif
+        // if bitmaps is picture-like, compress it using jpeg
+        if (can_lossy && display_channel->enable_jpeg &&
+            ((image_compression == SPICE_IMAGE_COMPRESSION_AUTO_LZ) ||
+            (image_compression == SPICE_IMAGE_COMPRESSION_AUTO_GLZ))) {
+            // if we use lz for alpha, the stride can't be extra
+            if (src->format != SPICE_BITMAP_FMT_RGBA || !bitmap_has_extra_stride(src)) {
+                return dcc_compress_image_jpeg(dcc, dest,
+                                               src, o_comp_data, drawable->group_id);
+            }
+        }
+        return dcc_compress_image_quic(dcc, dest,
+                                       src, o_comp_data, drawable->group_id);
+    } else {
+        int glz;
+        int ret;
+        if ((image_compression == SPICE_IMAGE_COMPRESSION_AUTO_GLZ) ||
+            (image_compression == SPICE_IMAGE_COMPRESSION_GLZ)) {
+            glz = bitmap_fmt_has_graduality(src->format) && (
+                    (src->x * src->y) < glz_enc_dictionary_get_size(
+                        dcc->glz_dict->dict));
+        } else if ((image_compression == SPICE_IMAGE_COMPRESSION_AUTO_LZ) ||
+                   (image_compression == SPICE_IMAGE_COMPRESSION_LZ) ||
+                   (image_compression == SPICE_IMAGE_COMPRESSION_LZ4)) {
+            glz = FALSE;
+        } else {
+            spice_error("invalid image compression type %u", image_compression);
+            return FALSE;
+        }
+
+        if (glz) {
+            /* using the global dictionary only if it is not frozen */
+            pthread_rwlock_rdlock(&dcc->glz_dict->encode_lock);
+            if (!dcc->glz_dict->migrate_freeze) {
+                ret = dcc_compress_image_glz(dcc,
+                                             dest, src,
+                                             drawable, o_comp_data);
+            } else {
+                glz = FALSE;
+            }
+            pthread_rwlock_unlock(&dcc->glz_dict->encode_lock);
+        }
+
+        if (!glz) {
+#ifdef USE_LZ4
+            if (image_compression == SPICE_IMAGE_COMPRESSION_LZ4 &&
+                bitmap_fmt_is_rgb(src->format) &&
+                red_channel_client_test_remote_cap(&dcc->common.base,
+                        SPICE_DISPLAY_CAP_LZ4_COMPRESSION)) {
+                ret = dcc_compress_image_lz4(dcc, dest, src, o_comp_data,
+                                             drawable->group_id);
+            } else
+#endif
+                ret = dcc_compress_image_lz(dcc, dest, src, o_comp_data,
+                                            drawable->group_id);
+#ifdef COMPRESS_DEBUG
+            spice_info("LZ LOCAL compress");
+#endif
+        }
+#ifdef COMPRESS_DEBUG
+        else {
+            spice_info("LZ global compress fmt=%d", src->format);
+        }
+#endif
+        return ret;
+    }
+}
+
+#define CLIENT_PALETTE_CACHE
+#include "cache_item.tmpl.c"
+#undef CLIENT_PALETTE_CACHE
+
+void dcc_palette_cache_palette(DisplayChannelClient *dcc, SpicePalette *palette,
+                               uint8_t *flags)
+{
+    if (palette == NULL) {
+        return;
+    }
+    if (palette->unique) {
+        if (red_palette_cache_find(dcc, palette->unique)) {
+            *flags |= SPICE_BITMAP_FLAGS_PAL_FROM_CACHE;
+            return;
+        }
+        if (red_palette_cache_add(dcc, palette->unique, 1)) {
+            *flags |= SPICE_BITMAP_FLAGS_PAL_CACHE_ME;
+        }
+    }
+}
+
+void dcc_palette_cache_reset(DisplayChannelClient *dcc)
+{
+    red_palette_cache_reset(dcc, CLIENT_PALETTE_CACHE_SIZE);
+}
diff --git a/server/dcc.h b/server/dcc.h
index 94eaab3..1b2c0a9 100644
--- a/server/dcc.h
+++ b/server/dcc.h
@@ -106,6 +106,8 @@ struct DisplayChannelClient {
     int use_mjpeg_encoder_rate_control;
     uint32_t streams_max_latency;
     uint64_t streams_max_bit_rate;
+
+    uint32_t glz_drawable_count;
 };
 
 #define DCC_TO_WORKER(dcc)                                              \
@@ -162,5 +164,36 @@ ImageItem *                dcc_add_surface_area_image                (DisplayCha
                                                                       SpiceRect *area,
                                                                       PipeItem *pos,
                                                                       int can_lossy);
+void                       dcc_palette_cache_reset                   (DisplayChannelClient *dcc);
+void                       dcc_palette_cache_palette                 (DisplayChannelClient *dcc,
+                                                                      SpicePalette *palette,
+                                                                      uint8_t *flags);
+
+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                        dcc_compress_image                        (DisplayChannelClient *dcc,
+                                                                      SpiceImage *dest, SpiceBitmap *src, Drawable *drawable,
+                                                                      int can_lossy,
+                                                                      compress_send_data_t* o_comp_data);
+int                        dcc_compress_image_glz                    (DisplayChannelClient *dcc,
+                                                                      SpiceImage *dest, SpiceBitmap *src, Drawable *drawable,
+                                                                      compress_send_data_t* o_comp_data);
+int                        dcc_compress_image_lz                     (DisplayChannelClient *dcc,
+                                                                      SpiceImage *dest, SpiceBitmap *src,
+                                                                      compress_send_data_t* o_comp_data, uint32_t group_id);
+int                        dcc_compress_image_jpeg                   (DisplayChannelClient *dcc, SpiceImage *dest,
+                                                                      SpiceBitmap *src, compress_send_data_t* o_comp_data,
+                                                                      uint32_t group_id);
+int                        dcc_compress_image_quic                   (DisplayChannelClient *dcc, SpiceImage *dest,
+                                                                      SpiceBitmap *src, compress_send_data_t* o_comp_data,
+                                                                      uint32_t group_id);
+int                        dcc_compress_image_lz4                    (DisplayChannelClient *dcc, SpiceImage *dest,
+                                                                      SpiceBitmap *src, compress_send_data_t* o_comp_data,
+                                                                      uint32_t group_id);
 
 #endif /* DCC_H_ */
diff --git a/server/display-channel.h b/server/display-channel.h
index 5f5bc51..595ced8 100644
--- a/server/display-channel.h
+++ b/server/display-channel.h
@@ -90,6 +90,10 @@ struct Drawable {
 #define DRAWABLE_FOREACH_DPI_SAFE(drawable, link, next, dpi)            \
     SAFE_FOREACH(link, next, drawable,  &(drawable)->pipes, dpi, LINK_TO_DPI(link))
 
+#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_ring, glz, LINK_TO_GLZ(link))
 
 enum {
     PIPE_ITEM_TYPE_DRAW = PIPE_ITEM_TYPE_COMMON_LAST,
diff --git a/server/red_parse_qxl.h b/server/red_parse_qxl.h
index 87862fa..b3b28e1 100644
--- a/server/red_parse_qxl.h
+++ b/server/red_parse_qxl.h
@@ -57,6 +57,12 @@ typedef struct RedDrawable {
     } u;
 } RedDrawable;
 
+static inline RedDrawable *red_drawable_ref(RedDrawable *drawable)
+{
+    drawable->refs++;
+    return drawable;
+}
+
 typedef struct RedUpdateCmd {
     QXLReleaseInfo *release_info;
     SpiceRect area;
diff --git a/server/red_worker.c b/server/red_worker.c
index 28a9ca3..de9117a 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -73,12 +73,6 @@
 #define CMD_RING_POLL_RETRIES 200
 
 #define DISPLAY_CLIENT_SHORT_TIMEOUT 15000000000ULL //nano
-#define DISPLAY_CLIENT_MIGRATE_DATA_TIMEOUT 10000000000ULL //nano, 10 sec
-#define DISPLAY_CLIENT_RETRY_INTERVAL 10000 //micro
-
-#define DISPLAY_FREE_LIST_DEFAULT_SIZE 128
-
-#define MIN_GLZ_SIZE_FOR_ZLIB 100
 
 #define VALIDATE_SURFACE_RET(worker, surface_id) \
     if (!validate_surface(worker, surface_id)) { \
@@ -146,7 +140,6 @@ struct RedWorker {
     uint32_t cursor_poll_tries;
 
     uint32_t red_drawable_count;
-    uint32_t glz_drawable_count;
     uint32_t bits_unique;
 
     RedMemSlotInfo mem_slots;
@@ -203,13 +196,6 @@ static void red_create_surface(DisplayChannel *display, uint32_t surface_id, uin
                                uint32_t height, int32_t stride, uint32_t format,
                                void *line_0, int data_is_valid, int send_client);
 
-
-#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_ring, glz, LINK_TO_GLZ(link))
-
-
 static void display_stream_clip_unref(DisplayChannel *display, StreamClipItem *item)
 {
     if (--item->refs != 0)
@@ -546,14 +532,6 @@ static void common_release_recv_buf(RedChannelClient *rcc, uint16_t type, uint32
     }
 }
 
-#define CLIENT_PALETTE_CACHE
-#include "cache_item.tmpl.c"
-#undef CLIENT_PALETTE_CACHE
-
-static void red_reset_palette_cache(DisplayChannelClient *dcc)
-{
-    red_palette_cache_reset(dcc, CLIENT_PALETTE_CACHE_SIZE);
-}
 
 static Drawable* drawable_try_new(DisplayChannel *display)
 {
@@ -593,13 +571,6 @@ static inline void set_surface_release_info(QXLReleaseInfoExt *release_info_ext,
     release_info_ext->group_id = group_id;
 }
 
-static RedDrawable *red_drawable_ref(RedDrawable *drawable)
-{
-    drawable->refs++;
-    return drawable;
-}
-
-
 static void red_drawable_unref(RedWorker *worker, RedDrawable *red_drawable,
                                uint32_t group_id)
 {
@@ -1924,8 +1895,10 @@ static void red_free_some(RedWorker *worker)
     DisplayChannelClient *dcc;
     RingItem *item, *next;
 
+#if FIXME
     spice_debug("#draw=%d, #red_draw=%d, #glz_draw=%d", display->drawable_count,
                 worker->red_drawable_count, worker->glz_drawable_count);
+#endif
     FOREACH_DCC(worker->display_channel, item, next, dcc) {
         GlzSharedDictionary *glz_dict = dcc ? dcc->glz_dict : NULL;
 
@@ -2033,79 +2006,6 @@ static void fill_base(SpiceMarshaller *base_marshaller, Drawable *drawable)
     spice_marshall_DisplayBase(base_marshaller, &base);
 }
 
-static inline void fill_palette(DisplayChannelClient *dcc,
-                                SpicePalette *palette,
-                                uint8_t *flags)
-{
-    if (palette == NULL) {
-        return;
-    }
-    if (palette->unique) {
-        if (red_palette_cache_find(dcc, palette->unique)) {
-            *flags |= SPICE_BITMAP_FLAGS_PAL_FROM_CACHE;
-            return;
-        }
-        if (red_palette_cache_add(dcc, palette->unique, 1)) {
-            *flags |= SPICE_BITMAP_FLAGS_PAL_CACHE_ME;
-        }
-    }
-}
-
-/******************************************************
- *      Global lz red drawables routines
-*******************************************************/
-
-/* if already exists, returns it. Otherwise allocates and adds it (1) to the ring tail
-   in the channel (2) to the Drawable*/
-static RedGlzDrawable *red_display_get_glz_drawable(DisplayChannelClient *dcc, Drawable *drawable)
-{
-    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)
-    DRAWABLE_FOREACH_GLZ_SAFE(drawable, item, next, ret) {
-        if (ret->dcc == dcc) {
-            return ret;
-        }
-    }
-
-    ret = spice_new(RedGlzDrawable, 1);
-
-    ret->dcc = dcc;
-    ret->red_drawable = red_drawable_ref(drawable->red_drawable);
-    ret->drawable = drawable;
-    ret->group_id = drawable->group_id;
-    ret->instances_count = 0;
-    ring_init(&ret->instances);
-
-    ring_item_init(&ret->link);
-    ring_item_init(&ret->drawable_link);
-    ring_add_before(&ret->link, &dcc->glz_drawables);
-    ring_add(&drawable->glz_ring, &ret->drawable_link);
-    DCC_TO_WORKER(dcc)->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 *red_display_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->glz_instance = NULL;
-    ret->red_glz_drawable = glz_drawable;
-
-    return ret;
-}
-
 /* 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).
@@ -2144,7 +2044,7 @@ void dcc_free_glz_drawable_instance(DisplayChannelClient *dcc,
         }
         red_drawable_unref(worker, glz_drawable->red_drawable,
                            glz_drawable->group_id);
-        worker->glz_drawable_count--;
+        dcc->glz_drawable_count--;
         if (ring_item_is_linked(&glz_drawable->link)) {
             ring_remove(&glz_drawable->link);
         }
@@ -2262,565 +2162,6 @@ static int red_display_free_some_independent_glz_drawables(DisplayChannelClient
     return n;
 }
 
-typedef struct compress_send_data_t {
-    void*    comp_buf;
-    uint32_t comp_buf_size;
-    SpicePalette *lzplt_palette;
-    int is_lossy;
-} compress_send_data_t;
-
-static inline int red_glz_compress_image(DisplayChannelClient *dcc,
-                                         SpiceImage *dest, SpiceBitmap *src, Drawable *drawable,
-                                         compress_send_data_t* o_comp_data)
-{
-    DisplayChannel *display_channel = DCC_TO_DC(dcc);
-#ifdef COMPRESS_STAT
-    stat_time_t start_time = stat_now(display_channel->zlib_glz_stat.clock);
-#endif
-    spice_assert(bitmap_fmt_is_rgb(src->format));
-    GlzData *glz_data = &dcc->glz_data;
-    ZlibData *zlib_data;
-    LzImageType type = MAP_BITMAP_FMT_TO_LZ_IMAGE_TYPE[src->format];
-    RedGlzDrawable *glz_drawable;
-    GlzDrawableInstanceItem *glz_drawable_instance;
-    int glz_size;
-    int zlib_size;
-
-    glz_data->data.bufs_tail = compress_buf_new();
-    glz_data->data.bufs_head = glz_data->data.bufs_tail;
-    glz_data->data.dcc = dcc;
-
-    glz_drawable = red_display_get_glz_drawable(dcc, drawable);
-    glz_drawable_instance = red_display_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(dcc->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->glz_instance);
-
-    stat_compress_add(&display_channel->glz_stat, start_time, src->stride * src->y, glz_size);
-
-    if (!display_channel->enable_zlib_glz_wrap || (glz_size < MIN_GLZ_SIZE_FOR_ZLIB)) {
-        goto glz;
-    }
-#ifdef COMPRESS_STAT
-    start_time = stat_now(display_channel->zlib_glz_stat.clock);
-#endif
-    zlib_data = &dcc->zlib_data;
-
-    zlib_data->data.bufs_tail = compress_buf_new();
-    zlib_data->data.bufs_head = zlib_data->data.bufs_tail;
-    zlib_data->data.dcc = dcc;
-
-    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(dcc->zlib, dcc->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) {
-        while (zlib_data->data.bufs_head) {
-            RedCompressBuf *buf = zlib_data->data.bufs_head;
-            zlib_data->data.bufs_head = buf->send_next;
-            compress_buf_free(buf);
-        }
-        goto glz;
-    }
-
-    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(&display_channel->zlib_glz_stat, start_time, glz_size, zlib_size);
-    return TRUE;
-glz:
-    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;
-}
-
-static inline int red_lz_compress_image(DisplayChannelClient *dcc,
-                                        SpiceImage *dest, SpiceBitmap *src,
-                                        compress_send_data_t* o_comp_data, uint32_t group_id)
-{
-    LzData *lz_data = &dcc->lz_data;
-    LzContext *lz = dcc->lz;
-    LzImageType type = MAP_BITMAP_FMT_TO_LZ_IMAGE_TYPE[src->format];
-    int size;            // size of the compressed data
-
-#ifdef COMPRESS_STAT
-    stat_time_t start_time = stat_now(DCC_TO_DC(dcc)->lz_stat.clock);
-#endif
-
-    lz_data->data.bufs_tail = compress_buf_new();
-    lz_data->data.bufs_head = lz_data->data.bufs_tail;
-    lz_data->data.dcc = dcc;
-
-    if (setjmp(lz_data->data.jmp_env)) {
-        while (lz_data->data.bufs_head) {
-            RedCompressBuf *buf = lz_data->data.bufs_head;
-            lz_data->data.bufs_head = buf->send_next;
-            compress_buf_free(buf);
-        }
-        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;
-
-        fill_palette(dcc, dest->u.lz_plt.palette, &(dest->u.lz_plt.flags));
-        o_comp_data->lzplt_palette = dest->u.lz_plt.palette;
-    }
-
-    stat_compress_add(&DCC_TO_DC(dcc)->lz_stat, start_time, src->stride * src->y,
-                      o_comp_data->comp_buf_size);
-    return TRUE;
-}
-
-static int red_jpeg_compress_image(DisplayChannelClient *dcc, SpiceImage *dest,
-                                   SpiceBitmap *src, compress_send_data_t* o_comp_data,
-                                   uint32_t group_id)
-{
-    JpegData *jpeg_data = &dcc->jpeg_data;
-    LzData *lz_data = &dcc->lz_data;
-    JpegEncoderContext *jpeg = dcc->jpeg;
-    LzContext *lz = dcc->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;
-
-#ifdef COMPRESS_STAT
-    stat_time_t start_time = stat_now(DCC_TO_DC(dcc)->jpeg_alpha_stat.clock);
-#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;
-    }
-
-    jpeg_data->data.bufs_tail = compress_buf_new();
-    jpeg_data->data.bufs_head = jpeg_data->data.bufs_tail;
-    jpeg_data->data.dcc = dcc;
-
-    if (setjmp(jpeg_data->data.jmp_env)) {
-        while (jpeg_data->data.bufs_head) {
-            RedCompressBuf *buf = jpeg_data->data.bufs_head;
-            jpeg_data->data.bufs_head = buf->send_next;
-            compress_buf_free(buf);
-        }
-        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, dcc->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(&DCC_TO_DC(dcc)->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.dcc = dcc;
-
-    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(&DCC_TO_DC(dcc)->jpeg_alpha_stat, start_time, src->stride * src->y,
-                      o_comp_data->comp_buf_size);
-    return TRUE;
-}
-
-#ifdef USE_LZ4
-static int red_lz4_compress_image(DisplayChannelClient *dcc, SpiceImage *dest,
-                                  SpiceBitmap *src, compress_send_data_t* o_comp_data,
-                                  uint32_t group_id)
-{
-    Lz4Data *lz4_data = &dcc->lz4_data;
-    Lz4EncoderContext *lz4 = dcc->lz4;
-    int lz4_size = 0;
-
-#ifdef COMPRESS_STAT
-    stat_time_t start_time = stat_now(DCC_TO_DC(dcc)->lz4_stat.clock);
-#endif
-
-    lz4_data->data.bufs_tail = compress_buf_new();
-    lz4_data->data.bufs_head = lz4_data->data.bufs_tail;
-
-    if (!lz4_data->data.bufs_head) {
-        spice_warning("failed to allocate compress buffer");
-        return FALSE;
-    }
-
-    lz4_data->data.bufs_head->send_next = NULL;
-    lz4_data->data.dcc = dcc;
-
-    if (setjmp(lz4_data->data.jmp_env)) {
-        while (lz4_data->data.bufs_head) {
-            RedCompressBuf *buf = lz4_data->data.bufs_head;
-            lz4_data->data.bufs_head = buf->send_next;
-            compress_buf_free(buf);
-        }
-        return FALSE;
-    }
-
-    if (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE) {
-        spice_chunks_linearize(src->data);
-    }
-
-    lz4_data->data.u.lines_data.chunks = src->data;
-    lz4_data->data.u.lines_data.stride = src->stride;
-    lz4_data->data.u.lines_data.next = 0;
-    lz4_data->data.u.lines_data.reverse = 0;
-    /* fixme remove? lz4_data->usr.more_lines = lz4_usr_more_lines; */
-
-    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(&DCC_TO_DC(dcc)->lz4_stat, start_time, src->stride * src->y,
-                      o_comp_data->comp_buf_size);
-    return TRUE;
-}
-#endif
-
-static inline int red_quic_compress_image(DisplayChannelClient *dcc, SpiceImage *dest,
-                                          SpiceBitmap *src, compress_send_data_t* o_comp_data,
-                                          uint32_t group_id)
-{
-    QuicData *quic_data = &dcc->quic_data;
-    QuicContext *quic = dcc->quic;
-    volatile QuicImageType type;
-    int size, stride;
-
-#ifdef COMPRESS_STAT
-    stat_time_t start_time = stat_now(DCC_TO_DC(dcc)->quic_stat.clock);
-#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;
-    }
-
-    quic_data->data.bufs_tail = compress_buf_new();
-    quic_data->data.bufs_head = quic_data->data.bufs_tail;
-    quic_data->data.dcc = dcc;
-
-    if (setjmp(quic_data->data.jmp_env)) {
-        while (quic_data->data.bufs_head) {
-            RedCompressBuf *buf = quic_data->data.bufs_head;
-            quic_data->data.bufs_head = buf->send_next;
-            compress_buf_free(buf);
-        }
-        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(&DCC_TO_DC(dcc)->quic_stat, start_time, src->stride * src->y,
-                      o_comp_data->comp_buf_size);
-    return TRUE;
-}
-
-#define MIN_SIZE_TO_COMPRESS 54
-#define MIN_DIMENSION_TO_QUIC 3
-static inline int red_compress_image(DisplayChannelClient *dcc,
-                                     SpiceImage *dest, SpiceBitmap *src, Drawable *drawable,
-                                     int can_lossy,
-                                     compress_send_data_t* o_comp_data)
-{
-    DisplayChannel *display_channel = DCC_TO_DC(dcc);
-    SpiceImageCompression image_compression = dcc->image_compression;
-    int quic_compress = FALSE;
-
-    if ((image_compression == SPICE_IMAGE_COMPRESSION_OFF) ||
-        ((src->y * src->stride) < MIN_SIZE_TO_COMPRESS)) { // TODO: change the size cond
-        return FALSE;
-    } else if (image_compression == SPICE_IMAGE_COMPRESSION_QUIC) {
-        if (bitmap_fmt_is_plt(src->format)) {
-            return FALSE;
-        } else {
-            quic_compress = TRUE;
-        }
-    } else {
-        /*
-            lz doesn't handle (1) bitmaps with strides that are larger than the width
-            of the image in bytes (2) unstable bitmaps
-        */
-        if (bitmap_has_extra_stride(src) || (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE)) {
-            if ((image_compression == SPICE_IMAGE_COMPRESSION_LZ) ||
-                (image_compression == SPICE_IMAGE_COMPRESSION_GLZ) ||
-                (image_compression == SPICE_IMAGE_COMPRESSION_LZ4) ||
-                bitmap_fmt_is_plt(src->format)) {
-                return FALSE;
-            } else {
-                quic_compress = TRUE;
-            }
-        } else {
-            if ((image_compression == SPICE_IMAGE_COMPRESSION_AUTO_LZ) ||
-                (image_compression == SPICE_IMAGE_COMPRESSION_AUTO_GLZ)) {
-                if ((src->x < MIN_DIMENSION_TO_QUIC) || (src->y < MIN_DIMENSION_TO_QUIC)) {
-                    quic_compress = FALSE;
-                } else {
-                    if (drawable->copy_bitmap_graduality == BITMAP_GRADUAL_INVALID) {
-                        quic_compress = bitmap_fmt_has_graduality(src->format) &&
-                            bitmap_get_graduality_level(src) == BITMAP_GRADUAL_HIGH;
-                    } else {
-                        quic_compress = (drawable->copy_bitmap_graduality == BITMAP_GRADUAL_HIGH);
-                    }
-                }
-            } else {
-                quic_compress = FALSE;
-            }
-        }
-    }
-
-    if (quic_compress) {
-#ifdef COMPRESS_DEBUG
-        spice_info("QUIC compress");
-#endif
-        // if bitmaps is picture-like, compress it using jpeg
-        if (can_lossy && display_channel->enable_jpeg &&
-            ((image_compression == SPICE_IMAGE_COMPRESSION_AUTO_LZ) ||
-            (image_compression == SPICE_IMAGE_COMPRESSION_AUTO_GLZ))) {
-            // if we use lz for alpha, the stride can't be extra
-            if (src->format != SPICE_BITMAP_FMT_RGBA || !bitmap_has_extra_stride(src)) {
-                return red_jpeg_compress_image(dcc, dest,
-                                               src, o_comp_data, drawable->group_id);
-            }
-        }
-        return red_quic_compress_image(dcc, dest,
-                                       src, o_comp_data, drawable->group_id);
-    } else {
-        int glz;
-        int ret;
-        if ((image_compression == SPICE_IMAGE_COMPRESSION_AUTO_GLZ) ||
-            (image_compression == SPICE_IMAGE_COMPRESSION_GLZ)) {
-            glz = bitmap_fmt_has_graduality(src->format) && (
-                    (src->x * src->y) < glz_enc_dictionary_get_size(
-                        dcc->glz_dict->dict));
-        } else if ((image_compression == SPICE_IMAGE_COMPRESSION_AUTO_LZ) ||
-                   (image_compression == SPICE_IMAGE_COMPRESSION_LZ) ||
-                   (image_compression == SPICE_IMAGE_COMPRESSION_LZ4)) {
-            glz = FALSE;
-        } else {
-            spice_error("invalid image compression type %u", image_compression);
-            return FALSE;
-        }
-
-        if (glz) {
-            /* using the global dictionary only if it is not frozen */
-            pthread_rwlock_rdlock(&dcc->glz_dict->encode_lock);
-            if (!dcc->glz_dict->migrate_freeze) {
-                ret = red_glz_compress_image(dcc,
-                                             dest, src,
-                                             drawable, o_comp_data);
-            } else {
-                glz = FALSE;
-            }
-            pthread_rwlock_unlock(&dcc->glz_dict->encode_lock);
-        }
-
-        if (!glz) {
-#ifdef USE_LZ4
-            if (image_compression == SPICE_IMAGE_COMPRESSION_LZ4 &&
-                bitmap_fmt_is_rgb(src->format) &&
-                red_channel_client_test_remote_cap(&dcc->common.base,
-                        SPICE_DISPLAY_CAP_LZ4_COMPRESSION)) {
-                ret = red_lz4_compress_image(dcc, dest, src, o_comp_data,
-                                             drawable->group_id);
-            } else
-#endif
-                ret = red_lz_compress_image(dcc, dest, src, o_comp_data,
-                                            drawable->group_id);
-#ifdef COMPRESS_DEBUG
-            spice_info("LZ LOCAL compress");
-#endif
-        }
-#ifdef COMPRESS_DEBUG
-        else {
-            spice_info("LZ global compress fmt=%d", src->format);
-        }
-#endif
-        return ret;
-    }
-}
-
 int dcc_pixmap_cache_unlocked_add(DisplayChannelClient *dcc, uint64_t id, uint32_t size, int lossy)
 {
     PixmapCache *cache = dcc->pixmap_cache;
@@ -3044,7 +2385,7 @@ static FillBitsType fill_bits(DisplayChannelClient *dcc, SpiceMarshaller *m,
            in order to prevent starvation in the client between pixmap_cache and
            global dictionary (in cases of multiple monitors) */
         if (reds_stream_get_family(rcc->stream) == AF_UNIX ||
-            !red_compress_image(dcc, &image, &simage->u.bitmap,
+            !dcc_compress_image(dcc, &image, &simage->u.bitmap,
                                 drawable, can_lossy, &comp_send_data)) {
             SpicePalette *palette;
 
@@ -3054,7 +2395,7 @@ static FillBitsType fill_bits(DisplayChannelClient *dcc, SpiceMarshaller *m,
             bitmap->flags = bitmap->flags & SPICE_BITMAP_FLAGS_TOP_DOWN;
 
             palette = bitmap->palette;
-            fill_palette(dcc, palette, &bitmap->flags);
+            dcc_palette_cache_palette(dcc, palette, &bitmap->flags);
             spice_marshall_Image(m, &image,
                                  &bitmap_palette_out, &lzplt_palette_out);
             spice_assert(lzplt_palette_out == NULL);
@@ -5022,11 +4363,11 @@ static void red_marshall_image(RedChannelClient *rcc, SpiceMarshaller *m, ImageI
     }
 
     if (lossy_comp) {
-        comp_succeeded = red_jpeg_compress_image(dcc, &red_image,
+        comp_succeeded = dcc_compress_image_jpeg(dcc, &red_image,
                                                  &bitmap, &comp_send_data,
                                                  worker->mem_slots.internal_groupslot_id);
     } else if (quic_comp) {
-        comp_succeeded = red_quic_compress_image(dcc, &red_image, &bitmap,
+        comp_succeeded = dcc_compress_image_quic(dcc, &red_image, &bitmap,
                                                  &comp_send_data,
                                                  worker->mem_slots.internal_groupslot_id);
 #ifdef USE_LZ4
@@ -5034,12 +4375,12 @@ static void red_marshall_image(RedChannelClient *rcc, SpiceMarshaller *m, ImageI
                bitmap_fmt_is_rgb(bitmap.format) &&
                red_channel_client_test_remote_cap(&dcc->common.base,
                                                   SPICE_DISPLAY_CAP_LZ4_COMPRESSION)) {
-        comp_succeeded = red_lz4_compress_image(dcc, &red_image, &bitmap,
+        comp_succeeded = dcc_compress_image_lz4(dcc, &red_image, &bitmap,
                                                 &comp_send_data,
                                                 worker->mem_slots.internal_groupslot_id);
 #endif
     } else if (comp_mode != SPICE_IMAGE_COMPRESSION_OFF) {
-        comp_succeeded = red_lz_compress_image(dcc, &red_image, &bitmap,
+        comp_succeeded = dcc_compress_image_lz(dcc, &red_image, &bitmap,
                                                &comp_send_data,
                                                worker->mem_slots.internal_groupslot_id);
     }
@@ -5287,7 +4628,7 @@ static void display_channel_send_item(RedChannelClient *rcc, PipeItem *pipe_item
         display_channel_marshall_reset_cache(rcc, m);
         break;
     case PIPE_ITEM_TYPE_INVAL_PALETTE_CACHE:
-        red_reset_palette_cache(dcc);
+        dcc_palette_cache_reset(dcc);
         red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES, NULL);
         break;
     case PIPE_ITEM_TYPE_CREATE_SURFACE: {
@@ -5356,15 +4697,17 @@ static void display_channel_client_on_disconnect(RedChannelClient *rcc)
     pixmap_cache_unref(dcc->pixmap_cache);
     dcc->pixmap_cache = NULL;
     red_release_glz(dcc);
-    red_reset_palette_cache(dcc);
+    dcc_palette_cache_reset(dcc);
     free(dcc->send_data.stream_outbuf);
     free(dcc->send_data.free_list.res);
     dcc_destroy_stream_agents(dcc);
 
     // this was the last channel client
+#if FIXME
     spice_debug("#draw=%d, #red_draw=%d, #glz_draw=%d",
                 display->drawable_count, worker->red_drawable_count,
                 worker->glz_drawable_count);
+#endif
 }
 
 void red_disconnect_all_display_TODO_remove_me(RedChannel *channel)
@@ -6940,13 +6283,14 @@ static void handle_dev_wakeup(void *opaque, void *payload)
 static void handle_dev_oom(void *opaque, void *payload)
 {
     RedWorker *worker = opaque;
-    DisplayChannel *display = worker->display_channel;
-
-    RedChannel *display_red_channel = &worker->display_channel->common.base;
     int ring_is_empty;
 
-    spice_assert(worker->running);
+    spice_return_if_fail(worker->running);
     // streams? but without streams also leak
+
+#if FIXME
+    DisplayChannel *display = worker->display_channel;
+    RedChannel *display_red_channel = &worker->display_channel->common.base;
     spice_debug("OOM1 #draw=%u, #red_draw=%u, #glz_draw=%u current %u pipes %u",
                 display->drawable_count,
                 worker->red_drawable_count,
@@ -6954,6 +6298,7 @@ static void handle_dev_oom(void *opaque, void *payload)
                 display->current_size,
                 worker->display_channel ?
                 red_channel_sum_pipes_size(display_red_channel) : 0);
+#endif
     while (red_process_commands(worker, MAX_PIPE_SIZE, &ring_is_empty)) {
         red_channel_push(&worker->display_channel->common.base);
     }
@@ -6961,6 +6306,7 @@ static void handle_dev_oom(void *opaque, void *payload)
         red_free_some(worker);
         worker->qxl->st->qif->flush_resources(worker->qxl);
     }
+#if FIXME
     spice_debug("OOM2 #draw=%u, #red_draw=%u, #glz_draw=%u current %u pipes %u",
                 display->drawable_count,
                 worker->red_drawable_count,
@@ -6968,6 +6314,7 @@ static void handle_dev_oom(void *opaque, void *payload)
                 display->current_size,
                 worker->display_channel ?
                 red_channel_sum_pipes_size(display_red_channel) : 0);
+#endif
     red_dispatcher_clear_pending(worker->red_dispatcher, RED_DISPATCHER_PENDING_OOM);
 }
 
-- 
2.4.3



More information about the Spice-devel mailing list