From 422d67dde4bb9d55362dee5db2237ef373448dbd Mon Sep 17 00:00:00 2001 From: Frediano Ziglio Date: Thu, 19 Nov 2015 13:55:39 +0000 Subject: worker: move compress to dcc_compress_image() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Signed-off-by: Frediano Ziglio Acked-by: Jonathon Jongsma --- server/dcc.c | 631 +++++++++++++++++++++++++++++++++++++++++++ server/dcc.h | 31 +++ server/display-channel.h | 4 + server/red_parse_qxl.h | 6 + server/red_worker.c | 678 +---------------------------------------------- 5 files changed, 681 insertions(+), 669 deletions(-) diff --git a/server/dcc.c b/server/dcc.c index a14247c7..1e188140 100644 --- a/server/dcc.c +++ b/server/dcc.c @@ -309,3 +309,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_TO_DC(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->context = NULL; + ret->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->context); + + 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 a6ca9be5..7827f72a 100644 --- a/server/dcc.h +++ b/server/dcc.h @@ -163,5 +163,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 7f3949f3..111e0146 100644 --- a/server/display-channel.h +++ b/server/display-channel.h @@ -91,6 +91,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 87862fa2..b3b28e12 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 eab00ff4..d744f483 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)) { \ @@ -202,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) @@ -545,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) { @@ -592,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) { @@ -2032,79 +2004,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_DC(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->context = NULL; - ret->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). @@ -2261,565 +2160,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->context); - - 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; @@ -3043,7 +2383,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; @@ -3053,7 +2393,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); @@ -5021,11 +4361,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 @@ -5033,12 +4373,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); } @@ -5286,7 +4626,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: { @@ -5355,7 +4695,7 @@ 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); @@ -6944,7 +6284,7 @@ static void handle_dev_oom(void *opaque, void *payload) 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 spice_debug("OOM1 #draw=%u, #red_draw=%u, #glz_draw=%u current %u pipes %u", display->drawable_count, -- cgit