[Spice-devel] [PATCH 10/10] shared memory support for display channel.
Alon Levy
alevy at redhat.com
Mon Aug 12 05:49:19 PDT 2013
See spice-protocol commit f24154b87f92ae65b1aa83b97378f9c687d09017 for
complete details.
---
configure.ac | 4 +-
server/red_dispatcher.c | 50 ++++-
server/red_dispatcher.h | 4 +
server/red_worker.c | 413 +++++++++++++++++++++++++++++++++----
server/spice-server.syms | 5 +
server/spice.h | 11 +-
server/tests/test_display_base.c | 82 +++++++-
server/tests/test_display_base.h | 4 +-
server/tests/test_display_no_ssl.c | 3 +-
9 files changed, 519 insertions(+), 57 deletions(-)
diff --git a/configure.ac b/configure.ac
index fa1ba31..21831ee 100644
--- a/configure.ac
+++ b/configure.ac
@@ -13,9 +13,9 @@ AC_PREREQ([2.57])
# 4. Follow the libtool manual for the so version:
# http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
-m4_define([SPICE_CURRENT], [9])
+m4_define([SPICE_CURRENT], [10])
m4_define([SPICE_REVISION], [0])
-m4_define([SPICE_AGE], [8])
+m4_define([SPICE_AGE], [9])
# Note on the library name on linux (SONAME) produced by libtool (for reference, gleaned
# from looking at libtool 2.4.2)
diff --git a/server/red_dispatcher.c b/server/red_dispatcher.c
index 03a4c4a..5ab5d26 100644
--- a/server/red_dispatcher.c
+++ b/server/red_dispatcher.c
@@ -508,7 +508,8 @@ static void red_dispatcher_create_primary_surface_complete(RedDispatcher *dispat
static void
red_dispatcher_create_primary_surface_async(RedDispatcher *dispatcher, uint32_t surface_id,
- QXLDevSurfaceCreate *surface, uint64_t cookie)
+ QXLDevSurfaceCreate *surface, uint64_t cookie,
+ uint64_t shm_offset)
{
RedWorkerMessageCreatePrimarySurfaceAsync payload;
RedWorkerMessage message = RED_WORKER_MESSAGE_CREATE_PRIMARY_SURFACE_ASYNC;
@@ -517,18 +518,20 @@ red_dispatcher_create_primary_surface_async(RedDispatcher *dispatcher, uint32_t
payload.base.cmd = async_command_alloc(dispatcher, message, cookie);
payload.surface_id = surface_id;
payload.surface = *surface;
+ payload.shm_offset = shm_offset;
dispatcher_send_message(&dispatcher->dispatcher, message, &payload);
}
static void
red_dispatcher_create_primary_surface_sync(RedDispatcher *dispatcher, uint32_t surface_id,
- QXLDevSurfaceCreate *surface)
+ QXLDevSurfaceCreate *surface, uint64_t shm_offset)
{
RedWorkerMessageCreatePrimarySurface payload = {0,};
dispatcher->surface_create = *surface;
payload.surface_id = surface_id;
payload.surface = *surface;
+ payload.shm_offset = shm_offset;
dispatcher_send_message(&dispatcher->dispatcher,
RED_WORKER_MESSAGE_CREATE_PRIMARY_SURFACE,
&payload);
@@ -537,19 +540,23 @@ red_dispatcher_create_primary_surface_sync(RedDispatcher *dispatcher, uint32_t s
static void
red_dispatcher_create_primary_surface(RedDispatcher *dispatcher, uint32_t surface_id,
- QXLDevSurfaceCreate *surface, int async, uint64_t cookie)
+ QXLDevSurfaceCreate *surface, int async,
+ uint64_t cookie, uint64_t shm_offset)
{
if (async) {
- red_dispatcher_create_primary_surface_async(dispatcher, surface_id, surface, cookie);
+ red_dispatcher_create_primary_surface_async(dispatcher, surface_id,
+ surface, cookie, shm_offset);
} else {
- red_dispatcher_create_primary_surface_sync(dispatcher, surface_id, surface);
+ red_dispatcher_create_primary_surface_sync(dispatcher, surface_id,
+ surface, shm_offset);
}
}
static void qxl_worker_create_primary_surface(QXLWorker *qxl_worker, uint32_t surface_id,
QXLDevSurfaceCreate *surface)
{
- red_dispatcher_create_primary_surface((RedDispatcher*)qxl_worker, surface_id, surface, 0, 0);
+ red_dispatcher_create_primary_surface((RedDispatcher*)qxl_worker,
+ surface_id, surface, 0, 0, 0);
}
static void red_dispatcher_reset_image_cache(RedDispatcher *dispatcher)
@@ -926,7 +933,8 @@ SPICE_GNUC_VISIBLE
void spice_qxl_create_primary_surface(QXLInstance *instance, uint32_t surface_id,
QXLDevSurfaceCreate *surface)
{
- red_dispatcher_create_primary_surface(instance->st->dispatcher, surface_id, surface, 0, 0);
+ red_dispatcher_create_primary_surface(instance->st->dispatcher, surface_id,
+ surface, 0, 0, 0);
}
SPICE_GNUC_VISIBLE
@@ -983,7 +991,18 @@ SPICE_GNUC_VISIBLE
void spice_qxl_create_primary_surface_async(QXLInstance *instance, uint32_t surface_id,
QXLDevSurfaceCreate *surface, uint64_t cookie)
{
- red_dispatcher_create_primary_surface(instance->st->dispatcher, surface_id, surface, 1, cookie);
+ red_dispatcher_create_primary_surface(instance->st->dispatcher, surface_id,
+ surface, 1, cookie, 0);
+}
+
+SPICE_GNUC_VISIBLE
+void spice_qxl_create_primary_surface_shm(QXLInstance *instance, uint32_t surface_id,
+ QXLDevSurfaceCreate *surface, uint64_t cookie,
+ uint64_t shm_offset)
+{
+ red_dispatcher_create_primary_surface(instance->st->dispatcher,
+ surface_id, surface, !!cookie,
+ cookie, shm_offset);
}
SPICE_GNUC_VISIBLE
@@ -1072,6 +1091,17 @@ static RedChannel *red_dispatcher_cursor_channel_create(RedDispatcher *dispatche
return cursor_channel;
}
+int qxl_interface_shared_memory_available(QXLInstance *qin)
+{
+ if (qin->st->qif->base.major_version < 3 ||
+ (qin->st->qif->base.major_version == 3 &&
+ qin->st->qif->base.minor_version < 4) ||
+ !qin->st->qif->shared_memory_file_name) {
+ return 0;
+ }
+ return qin->st->qif->shared_memory_file_name(qin) != NULL;
+}
+
RedDispatcher *red_dispatcher_init(QXLInstance *qxl)
{
RedDispatcher *red_dispatcher;
@@ -1161,6 +1191,10 @@ RedDispatcher *red_dispatcher_init(QXLInstance *qxl)
red_channel_set_data(display_channel, red_dispatcher);
red_channel_set_cap(display_channel, SPICE_DISPLAY_CAP_MONITORS_CONFIG);
red_channel_set_cap(display_channel, SPICE_DISPLAY_CAP_STREAM_REPORT);
+ if (qxl_interface_shared_memory_available(qxl)) {
+ red_channel_set_cap(display_channel,
+ SPICE_DISPLAY_CAP_SHARED_MEMORY);
+ }
reds_register_channel(display_channel);
}
diff --git a/server/red_dispatcher.h b/server/red_dispatcher.h
index 7d23b11..eece032 100644
--- a/server/red_dispatcher.h
+++ b/server/red_dispatcher.h
@@ -25,6 +25,8 @@ typedef struct AsyncCommand AsyncCommand;
struct RedDispatcher *red_dispatcher_init(QXLInstance *qxl);
+int qxl_interface_shared_memory_available(QXLInstance *qxl);
+
void red_dispatcher_set_mm_time(uint32_t);
void red_dispatcher_on_ic_change(void);
void red_dispatcher_on_sv_change(void);
@@ -130,11 +132,13 @@ typedef struct RedWorkerMessageCreatePrimarySurfaceAsync {
RedWorkerMessageAsync base;
uint32_t surface_id;
QXLDevSurfaceCreate surface;
+ uint64_t shm_offset;
} RedWorkerMessageCreatePrimarySurfaceAsync;
typedef struct RedWorkerMessageCreatePrimarySurface {
uint32_t surface_id;
QXLDevSurfaceCreate surface;
+ uint64_t shm_offset;
} RedWorkerMessageCreatePrimarySurface;
typedef struct RedWorkerMessageResetImageCache {
diff --git a/server/red_worker.c b/server/red_worker.c
index 396be4a..c4ffbe0 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -44,8 +44,10 @@
#include <pthread.h>
#include <netinet/tcp.h>
#include <setjmp.h>
-#include <openssl/ssl.h>
#include <inttypes.h>
+#include <sys/mman.h>
+
+#include <openssl/ssl.h>
#include <spice/protocol.h>
#include <spice/qxl_dev.h>
@@ -298,6 +300,8 @@ enum {
PIPE_ITEM_TYPE_DESTROY_SURFACE,
PIPE_ITEM_TYPE_MONITORS_CONFIG,
PIPE_ITEM_TYPE_STREAM_ACTIVATE_REPORT,
+ PIPE_ITEM_TYPE_SHM_OFFER,
+ PIPE_ITEM_TYPE_SHM_DAMAGE,
};
typedef struct VerbItem {
@@ -336,6 +340,7 @@ struct CacheItem {
typedef struct SurfaceCreateItem {
SpiceMsgSurfaceCreate surface_create;
+ uint64_t shm_offset;
PipeItem pipe_item;
} SurfaceCreateItem;
@@ -362,6 +367,17 @@ typedef struct StreamActivateReportItem {
uint32_t stream_id;
} StreamActivateReportItem;
+typedef struct ShmOfferItem {
+ PipeItem pipe_item;
+ uint8_t name[128];
+} ShmOfferItem;
+
+typedef struct ShmDamageItem {
+ PipeItem pipe_item;
+ SpiceRect box;
+ SpiceClip *clip;
+} ShmDamageItem;
+
typedef struct CursorItem {
uint32_t group_id;
int refs;
@@ -677,6 +693,11 @@ struct DisplayChannelClient {
int expect_init;
+ struct {
+ int use; /* if 0 shared memory is unused */
+ int got_reply; /* 0/1 */
+ } shm;
+
PixmapCache *pixmap_cache;
uint32_t pixmap_cache_generation;
int pending_pixmaps_sync;
@@ -882,6 +903,7 @@ typedef struct DrawContext {
int32_t stride;
uint32_t format;
void *line_0;
+ uint64_t shm_offset;
} DrawContext;
typedef struct RedSurface {
@@ -1077,6 +1099,11 @@ static void cursor_channel_client_release_item_after_push(CursorChannelClient *c
static void red_push_monitors_config(DisplayChannelClient *dcc);
+static void push_shm_damage_from_drawable(DisplayChannelClient *dcc,
+ Drawable *drawable);
+static void push_shm_damage(DisplayChannelClient *dcc, SpiceRect *box,
+ SpiceClip *clip);
+
/*
* Macros to make iterating over stuff easier
* The two collections we iterate over:
@@ -1243,9 +1270,11 @@ static inline int is_primary_surface(RedWorker *worker, uint32_t surface_id)
return FALSE;
}
-static inline void __validate_surface(RedWorker *worker, uint32_t surface_id)
+static inline int __validate_surface(RedWorker *worker, uint32_t surface_id)
{
- spice_warn_if(surface_id >= worker->n_surfaces);
+ spice_return_val_if_fail(surface_id < worker->n_surfaces, 0);
+ spice_return_val_if_fail(surface_id >= 1, 0);
+ return 1;
}
static inline int validate_surface(RedWorker *worker, uint32_t surface_id)
@@ -1379,6 +1408,10 @@ static inline void red_handle_drawable_surfaces_client_synced(
RedWorker *worker = DCC_TO_WORKER(dcc);
int x;
+ if (dcc->shm.use) {
+ return;
+ }
+
for (x = 0; x < 3; ++x) {
int surface_id;
@@ -1433,6 +1466,7 @@ static inline DrawablePipeItem *get_drawable_pipe_item(DisplayChannelClient *dcc
{
DrawablePipeItem *dpi;
+ spice_assert(!dcc->shm.use);
dpi = spice_malloc0(sizeof(*dpi));
dpi->drawable = drawable;
dpi->dcc = dcc;
@@ -1455,6 +1489,10 @@ static inline void red_pipe_add_drawable(DisplayChannelClient *dcc, Drawable *dr
{
DrawablePipeItem *dpi;
+ if (dcc->shm.use) {
+ /* we get here via red_current_add */
+ return;
+ }
red_handle_drawable_surfaces_client_synced(dcc, drawable);
dpi = get_drawable_pipe_item(dcc, drawable);
red_channel_client_pipe_add(&dcc->common.base, &dpi->dpi_pipe_item);
@@ -1464,10 +1502,28 @@ static inline void red_pipes_add_drawable(RedWorker *worker, Drawable *drawable)
{
DisplayChannelClient *dcc;
RingItem *dcc_ring_item, *next;
+ int did_draw = 0;
spice_warn_if(!ring_is_empty(&drawable->pipes));
WORKER_FOREACH_DCC_SAFE(worker, dcc_ring_item, next, dcc) {
- red_pipe_add_drawable(dcc, drawable);
+ if (dcc->shm.use) {
+ if (drawable->red_drawable->surface_id != 0) {
+ continue;
+ }
+ if (!dcc->shm.got_reply) {
+ spice_debug("read a drawable before client replied to shm offer");
+ continue;
+ }
+ /* TODO: rate limit */
+ if (!did_draw) {
+ red_update_area(worker, &drawable->red_drawable->bbox, 0);
+ //red_draw_drawable(worker, drawable);
+ did_draw = 1;
+ }
+ push_shm_damage_from_drawable(dcc, drawable);
+ } else {
+ red_pipe_add_drawable(dcc, drawable);
+ }
}
}
@@ -1679,6 +1735,7 @@ static inline void red_destroy_surface_item(RedWorker *worker,
SurfaceDestroyItem *destroy;
RedChannel *channel;
+ spice_assert(!dcc->shm.use || surface_id == 0);
if (!dcc || worker->display_channel->common.during_target_migrate ||
!dcc->surface_client_created[surface_id]) {
return;
@@ -1713,7 +1770,9 @@ static inline void red_destroy_surface(RedWorker *worker, uint32_t surface_id)
region_destroy(&surface->draw_dirty_region);
surface->context.canvas = NULL;
WORKER_FOREACH_DCC_SAFE(worker, link, next, dcc) {
- red_destroy_surface_item(worker, dcc, surface_id);
+ if (surface_id == 0 || !dcc->shm.use) {
+ red_destroy_surface_item(worker, dcc, surface_id);
+ }
}
spice_warn_if(!ring_is_empty(&surface->depend_on_me));
@@ -2501,6 +2560,8 @@ static inline void red_detach_stream(RedWorker *worker, Stream *stream, int deta
static StreamClipItem *__new_stream_clip(DisplayChannelClient* dcc, StreamAgent *agent)
{
StreamClipItem *item = spice_new(StreamClipItem, 1);
+
+ spice_assert(!dcc->shm.use);
red_channel_pipe_item_init(dcc->common.base.channel,
(PipeItem *)item, PIPE_ITEM_TYPE_STREAM_CLIP);
@@ -3017,6 +3078,7 @@ static void red_display_create_stream(DisplayChannelClient *dcc, Stream *stream)
{
StreamAgent *agent = &dcc->stream_agents[get_stream_id(dcc->common.worker, stream)];
+ spice_assert(!dcc->shm.use);
stream->refs++;
spice_assert(region_is_empty(&agent->vis_region));
if (stream->current) {
@@ -3140,6 +3202,7 @@ static void red_display_client_init_streams(DisplayChannelClient *dcc)
RedWorker *worker = dcc->common.worker;
RedChannel *channel = dcc->common.base.channel;
+ spice_assert(!dcc->shm.use);
for (i = 0; i < NUM_STREAMS; i++) {
StreamAgent *agent = &dcc->stream_agents[i];
agent->stream = &worker->streams_buf[i];
@@ -4218,7 +4281,8 @@ cleanup:
static inline void red_create_surface(RedWorker *worker, uint32_t surface_id,uint32_t width,
uint32_t height, int32_t stride, uint32_t format,
- void *line_0, int data_is_valid, int send_client);
+ void *line_0, int data_is_valid, int send_client,
+ uint64_t shm_offset);
static inline void red_process_surface(RedWorker *worker, RedSurfaceCmd *surface,
uint32_t group_id, int loadvm)
@@ -4228,7 +4292,10 @@ static inline void red_process_surface(RedWorker *worker, RedSurfaceCmd *surface
uint8_t *data;
surface_id = surface->surface_id;
- __validate_surface(worker, surface_id);
+ if (!__validate_surface(worker, surface_id)) {
+ exit(-1);
+ return;
+ }
red_surface = &worker->surfaces[surface_id];
@@ -4246,7 +4313,8 @@ static inline void red_process_surface(RedWorker *worker, RedSurfaceCmd *surface
height, stride, surface->u.surface_create.format, data,
reloaded_surface,
// reloaded surfaces will be sent on demand
- !reloaded_surface);
+ !reloaded_surface,
+ 0 /* shm_offset for reloaded surface TODO */);
set_surface_release_info(worker, surface_id, 1, surface->release_info, group_id);
break;
}
@@ -4345,6 +4413,56 @@ static void localize_mask(RedWorker *worker, SpiceQMask *mask, SpiceImage *image
}
}
+int write_ppm_32(uint8_t *d_data, int d_width, int d_height)
+{
+ FILE *fp;
+ uint8_t *p;
+ int n;
+ static int i;
+ char outf[128];
+
+ snprintf(outf, sizeof(outf), "dump.%010d.ppm", i++);
+
+ fp = fopen(outf,"w");
+ if (NULL == fp) {
+ fprintf(stderr, "can't open %s: %s\n", outf, strerror(errno));
+ return -1;
+ }
+ fprintf(fp, "P6\n%d %d\n255\n",
+ d_width, d_height);
+ n = d_width * d_height;
+ p = d_data;
+ while (n > 0) {
+ fputc(p[2], fp);
+ fputc(p[1], fp);
+ fputc(p[0], fp);
+ p += 4;
+ n--;
+ }
+ fclose(fp);
+ return 0;
+}
+
+uint8_t *worker_surface_start(RedWorker *worker, RedSurface *in_surface,
+ int surface_id)
+{
+ RedSurface *surface = in_surface ? in_surface
+ : &worker->surfaces[surface_id];
+ DrawContext *context = &surface->context;
+ uint8_t *data;
+
+ if (!context->canvas) {
+ return NULL;
+ }
+
+ if (context->stride < 0) {
+ data = (uint8_t *)context->line_0 - (context->height - 1) * (-context->stride);
+ } else {
+ data = context->line_0;
+ }
+ return data;
+}
+
static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable)
{
RedSurface *surface;
@@ -4482,6 +4600,15 @@ static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable)
default:
spice_warning("invalid type");
}
+ {
+#if 0
+ DrawContext *context = &surface->context;
+ write_ppm_32(worker_surface_start(worker, NULL, 0),
+ context->width, context->width);
+#else
+ (void)write_ppm_32;
+#endif
+ }
}
#ifndef DRAW_ALL
@@ -5130,6 +5257,7 @@ static ImageItem *red_add_surface_area_image(DisplayChannelClient *dcc, int surf
int all_set;
spice_assert(area);
+ spice_assert(!dcc->shm.use);
width = area->right - area->left;
height = area->bottom - area->top;
@@ -5196,10 +5324,14 @@ static void red_push_surface_image(DisplayChannelClient *dcc, int surface_id)
area.right = surface->context.width;
area.bottom = surface->context.height;
- /* not allowing lossy compression because probably, especially if it is a primary surface,
- it combines both "picture-like" areas with areas that are more "artificial"*/
- red_add_surface_area_image(dcc, surface_id, &area, NULL, FALSE);
- red_channel_client_push(&dcc->common.base);
+ if (surface_id == 0 && dcc->shm.use) {
+ push_shm_damage(dcc, &area, NULL);
+ } else {
+ /* not allowing lossy compression because probably, especially if it is a primary surface,
+ it combines both "picture-like" areas with areas that are more "artificial"*/
+ red_add_surface_area_image(dcc, surface_id, &area, NULL, FALSE);
+ red_channel_client_push(&dcc->common.base);
+ }
}
typedef struct {
@@ -8962,14 +9094,28 @@ static void red_marshall_cursor(RedChannelClient *rcc,
}
static void red_marshall_surface_create(RedChannelClient *rcc,
- SpiceMarshaller *base_marshaller, SpiceMsgSurfaceCreate *surface_create)
+ SpiceMarshaller *base_marshaller, SpiceMsgSurfaceCreate *surface_create,
+ uint64_t shm_offset)
{
DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
region_init(&dcc->surface_client_lossy_region[surface_create->surface_id]);
- red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_SURFACE_CREATE, NULL);
- spice_marshall_msg_display_surface_create(base_marshaller, surface_create);
+ if (dcc->shm.use) {
+ SpiceMsgSurfaceCreateShm create_shm;
+ create_shm.shm_offset = shm_offset;
+ create_shm.surface_id = surface_create->surface_id;
+ create_shm.width = surface_create->width;
+ create_shm.height = surface_create->height;
+ create_shm.format = surface_create->format;
+ create_shm.flags = surface_create->flags;
+ /* newer versions support a message containing shared memory offsets */
+ red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_SURFACE_CREATE_SHM, NULL);
+ spice_marshall_msg_display_surface_create_shm(base_marshaller, &create_shm);
+ } else {
+ red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_SURFACE_CREATE, NULL);
+ spice_marshall_msg_display_surface_create(base_marshaller, surface_create);
+ }
}
static void red_marshall_surface_destroy(RedChannelClient *rcc,
@@ -9029,12 +9175,37 @@ static void red_marshall_stream_activate_report(RedChannelClient *rcc,
spice_marshall_msg_display_stream_activate_report(base_marshaller, &msg);
}
+static void red_marshall_shm_offer(RedChannelClient *rcc,
+ SpiceMarshaller *base_marshaller,
+ ShmOfferItem *offer)
+{
+ SpiceMsgDisplayShmOffer msg;
+
+ red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_SHM_OFFER, NULL);
+ memcpy(msg.name, offer->name, sizeof(msg.name));
+ spice_marshall_msg_display_shm_offer(base_marshaller, &msg);
+}
+
+static void red_marshall_shm_damage(RedChannelClient *rcc,
+ SpiceMarshaller *base_marshaller,
+ ShmDamageItem *damage)
+{
+ SpiceMsgDisplayShmDamage msg;
+
+ red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_SHM_DAMAGE, NULL);
+ msg.box = damage->box;
+ msg.clip.type = damage->clip->type;
+ msg.clip.rects = damage->clip->rects;
+ spice_marshall_msg_display_shm_damage(base_marshaller, &msg);
+}
+
static void display_channel_send_item(RedChannelClient *rcc, PipeItem *pipe_item)
{
SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
red_display_reset_send_data(dcc);
+
switch (pipe_item->type) {
case PIPE_ITEM_TYPE_DRAW: {
DrawablePipeItem *dpi = SPICE_CONTAINEROF(pipe_item, DrawablePipeItem, dpi_pipe_item);
@@ -9084,7 +9255,9 @@ static void display_channel_send_item(RedChannelClient *rcc, PipeItem *pipe_item
case PIPE_ITEM_TYPE_CREATE_SURFACE: {
SurfaceCreateItem *surface_create = SPICE_CONTAINEROF(pipe_item, SurfaceCreateItem,
pipe_item);
- red_marshall_surface_create(rcc, m, &surface_create->surface_create);
+ spice_assert(!dcc->shm.use || surface_create->surface_create.surface_id == 0);
+ red_marshall_surface_create(rcc, m, &surface_create->surface_create,
+ surface_create->shm_offset);
break;
}
case PIPE_ITEM_TYPE_DESTROY_SURFACE: {
@@ -9106,6 +9279,14 @@ static void display_channel_send_item(RedChannelClient *rcc, PipeItem *pipe_item
red_marshall_stream_activate_report(rcc, m, report_item->stream_id);
break;
}
+ case PIPE_ITEM_TYPE_SHM_OFFER:
+ red_marshall_shm_offer(rcc, m, SPICE_CONTAINEROF(pipe_item,
+ ShmOfferItem, pipe_item));
+ break;
+ case PIPE_ITEM_TYPE_SHM_DAMAGE:
+ red_marshall_shm_damage(rcc, m, SPICE_CONTAINEROF(pipe_item,
+ ShmDamageItem, pipe_item));
+ break;
default:
spice_error("invalid pipe item type");
}
@@ -9395,7 +9576,8 @@ static inline void *create_canvas_for_surface(RedWorker *worker, RedSurface *sur
static SurfaceCreateItem *get_surface_create_item(
RedChannel* channel,
uint32_t surface_id, uint32_t width,
- uint32_t height, uint32_t format, uint32_t flags)
+ uint32_t height, uint32_t format, uint32_t flags,
+ uint64_t shm_offset)
{
SurfaceCreateItem *create;
@@ -9407,6 +9589,7 @@ static SurfaceCreateItem *get_surface_create_item(
create->surface_create.height = height;
create->surface_create.flags = flags;
create->surface_create.format = format;
+ create->shm_offset = shm_offset;
red_channel_pipe_item_init(channel,
&create->pipe_item, PIPE_ITEM_TYPE_CREATE_SURFACE);
@@ -9418,17 +9601,24 @@ static inline void red_create_surface_item(DisplayChannelClient *dcc, int surfac
RedSurface *surface;
SurfaceCreateItem *create;
RedWorker *worker = dcc ? dcc->common.worker : NULL;
- uint32_t flags = is_primary_surface(worker, surface_id) ? SPICE_SURFACE_FLAGS_PRIMARY : 0;
+ uint32_t is_primary = is_primary_surface(worker, surface_id) ?
+ SPICE_SURFACE_FLAGS_PRIMARY : 0;
+ uint32_t is_upside_down;
+ uint32_t flags;
+ spice_assert(!dcc->shm.use || surface_id == 0);
/* don't send redundant create surface commands to client */
if (!dcc || worker->display_channel->common.during_target_migrate ||
dcc->surface_client_created[surface_id]) {
return;
}
surface = &worker->surfaces[surface_id];
+ is_upside_down = surface->context.top_down ?
+ SPICE_SURFACE_FLAGS_TOP_DOWN : 0;
+ flags = is_primary | is_upside_down;
create = get_surface_create_item(dcc->common.base.channel,
surface_id, surface->context.width, surface->context.height,
- surface->context.format, flags);
+ surface->context.format, flags, surface->context.shm_offset);
dcc->surface_client_created[surface_id] = TRUE;
red_channel_client_pipe_add(&dcc->common.base, &create->pipe_item);
}
@@ -9439,7 +9629,9 @@ static void red_worker_create_surface_item(RedWorker *worker, int surface_id)
RingItem *item, *next;
WORKER_FOREACH_DCC_SAFE(worker, item, next, dcc) {
- red_create_surface_item(dcc, surface_id);
+ if (surface_id == 0 || !dcc->shm.use) {
+ red_create_surface_item(dcc, surface_id);
+ }
}
}
@@ -9450,15 +9642,19 @@ static void red_worker_push_surface_image(RedWorker *worker, int surface_id)
RingItem *item, *next;
WORKER_FOREACH_DCC_SAFE(worker, item, next, dcc) {
- red_push_surface_image(dcc, surface_id);
+ if (!dcc->shm.use) {
+ red_push_surface_image(dcc, surface_id);
+ }
}
}
static inline void red_create_surface(RedWorker *worker, uint32_t surface_id, uint32_t width,
uint32_t height, int32_t stride, uint32_t format,
- void *line_0, int data_is_valid, int send_client)
+ void *line_0, int data_is_valid, int send_client,
+ uint64_t shm_offset)
{
RedSurface *surface = &worker->surfaces[surface_id];
+ uint8_t *surface_start;
uint32_t i;
spice_warn_if(surface->context.canvas);
@@ -9469,8 +9665,14 @@ static inline void red_create_surface(RedWorker *worker, uint32_t surface_id, ui
surface->context.format = format;
surface->context.stride = stride;
surface->context.line_0 = line_0;
+ surface->context.shm_offset = shm_offset;
if (!data_is_valid) {
- memset((char *)line_0 + (int32_t)(stride * (height - 1)), 0, height*abs(stride));
+ if (stride < 0) {
+ surface_start = (uint8_t *)line_0 + (int32_t)(stride * (height - 1));
+ } else {
+ surface_start = line_0;
+ }
+ memset(surface_start, 0, height * abs(stride));
}
surface->create.info = NULL;
surface->destroy.info = NULL;
@@ -9614,7 +9816,9 @@ static void push_new_primary_surface(DisplayChannelClient *dcc)
{
RedChannelClient *rcc = &dcc->common.base;
- red_channel_client_pipe_add_type(rcc, PIPE_ITEM_TYPE_INVAL_PALLET_CACHE);
+ if (!dcc->shm.use) {
+ red_channel_client_pipe_add_type(rcc, PIPE_ITEM_TYPE_INVAL_PALLET_CACHE);
+ }
red_create_surface_item(dcc, 0);
red_channel_client_push(rcc);
}
@@ -9649,22 +9853,97 @@ static int display_channel_client_wait_for_init(DisplayChannelClient *dcc)
return FALSE;
}
-static void on_new_display_channel_client(DisplayChannelClient *dcc)
+static SpiceClip *clip_copy(const SpiceClip *clip)
+{
+ SpiceClipRects *rects = clip && clip->rects ? clip->rects : NULL;
+ int num_rects = rects ? rects->num_rects : 0;
+ SpiceClip *ret = spice_malloc(sizeof(*ret) +
+ sizeof(*ret->rects) + num_rects * sizeof(SpiceRect));
+
+ ret->type = clip ? clip->type : 0;
+ ret->rects = (SpiceClipRects *)&ret[1];
+ ret->rects->num_rects = num_rects;
+ if (clip && clip->rects) {
+ memcpy(ret->rects->rects, clip->rects->rects,
+ sizeof(SpiceRect) * clip->rects->num_rects);
+ }
+ return ret;
+}
+
+static void push_shm_damage(DisplayChannelClient *dcc, SpiceRect *box,
+ SpiceClip *clip)
{
- DisplayChannel *display_channel = DCC_TO_DC(dcc);
- RedWorker *worker = display_channel->common.worker;
RedChannelClient *rcc = &dcc->common.base;
+ ShmDamageItem *shm_damage_item;
- red_channel_client_push_set_ack(&dcc->common.base);
+ shm_damage_item = spice_malloc0(sizeof(*shm_damage_item));
+ /* TODO: not really really required, but if the drawable is destroyed
+ * before the pipe item is marshalled, we are sending a redundant damage
+ * message - so keeping a drawable here and checking during marshalling
+ * time makes sense. Also avoids the copy of the clips. (so should do this)
+ */
+ shm_damage_item->box = *box;
+ shm_damage_item->clip = clip_copy(clip);
+ spice_debug("pushing shm_damage");
+ red_channel_pipe_item_init(dcc->common.base.channel,
+ &shm_damage_item->pipe_item,
+ PIPE_ITEM_TYPE_SHM_DAMAGE);
+ red_channel_client_pipe_add(&dcc->common.base,
+ &shm_damage_item->pipe_item);
+ red_channel_client_push(rcc);
+}
- if (red_channel_client_waits_for_migrate_data(rcc)) {
+static void push_shm_damage_from_drawable(DisplayChannelClient *dcc,
+ Drawable *drawable)
+{
+ push_shm_damage(dcc, &drawable->red_drawable->bbox,
+ &drawable->red_drawable->clip);
+}
+
+static void push_shm_offer(DisplayChannelClient *dcc)
+{
+ RedChannelClient *rcc = &dcc->common.base;
+ ShmOfferItem *shm_offer_item;
+ RedWorker *worker = DCC_TO_WORKER(RCC_TO_DCC(rcc));
+ const char *qxl_name;
+ int qxl_name_length;
+
+ if (!dcc->shm.use) {
return;
}
- if (!display_channel_client_wait_for_init(dcc)) {
+ if (!qxl_interface_shared_memory_available(worker->qxl)) {
+ spice_info("shared memory not supported by qxl.");
return;
}
- red_channel_client_ack_zero_messages_window(&dcc->common.base);
+ qxl_name = worker->qxl->st->qif->shared_memory_file_name(worker->qxl);
+ if (!qxl_name) {
+ spice_info("shared memory supported by qxl but not available.");
+ return;
+ }
+ qxl_name_length = strlen(qxl_name);
+ if (qxl_name_length >= sizeof(shm_offer_item->name)) {
+ spice_warning("qxl interface shared memory file name length %d > %zd",
+ qxl_name_length, sizeof(shm_offer_item->name));
+ return;
+ }
+ spice_info("pushing shm offer for %s", qxl_name);
+ shm_offer_item = spice_malloc0(sizeof(*shm_offer_item));
+ memcpy(shm_offer_item->name, qxl_name, qxl_name_length);
+ red_channel_pipe_item_init(dcc->common.base.channel,
+ &shm_offer_item->pipe_item,
+ PIPE_ITEM_TYPE_SHM_OFFER);
+ red_channel_client_pipe_add(&dcc->common.base,
+ &shm_offer_item->pipe_item);
+ red_channel_client_push(rcc);
+}
+
+static void on_new_display_channel_client_after_shm_offer(
+ DisplayChannelClient *dcc)
+{
+ RedChannelClient *rcc = &dcc->common.base;
+ RedWorker *worker = DCC_TO_WORKER(dcc);
+
if (worker->surfaces[0].context.canvas) {
red_current_flush(worker, 0);
push_new_primary_surface(dcc);
@@ -9675,6 +9954,35 @@ static void on_new_display_channel_client(DisplayChannelClient *dcc)
}
}
+static void on_new_display_channel_client(DisplayChannelClient *dcc)
+{
+ RedChannelClient *rcc = &dcc->common.base;
+
+ red_channel_client_push_set_ack(&dcc->common.base);
+
+ if (red_channel_client_waits_for_migrate_data(rcc)) {
+ return;
+ }
+
+ if (!display_channel_client_wait_for_init(dcc)) {
+ return;
+ }
+ red_channel_client_ack_zero_messages_window(&dcc->common.base);
+
+ if (red_channel_client_test_remote_cap(rcc, SPICE_DISPLAY_CAP_SHARED_MEMORY)) {
+ RedWorker *worker = DCC_TO_WORKER(dcc);
+ if (qxl_interface_shared_memory_available(worker->qxl)) {
+ dcc->shm.use = 1;
+ push_shm_offer(dcc);
+ } else {
+ dcc->shm.use = 0;
+ on_new_display_channel_client_after_shm_offer(dcc);
+ }
+ } else {
+ on_new_display_channel_client_after_shm_offer(dcc);
+ }
+}
+
static GlzSharedDictionary *_red_find_glz_dictionary(RedClient *client, uint8_t dict_id)
{
RingItem *now;
@@ -10094,6 +10402,20 @@ static int display_channel_handle_stream_report(DisplayChannelClient *dcc,
return TRUE;
}
+static int display_channel_handle_shm_reply(DisplayChannelClient *dcc,
+ SpiceMsgcDisplayShmReply *shm_reply)
+{
+ dcc->shm.got_reply = 1;
+ dcc->shm.use = !!shm_reply->accepted;
+ if (!dcc->shm.use) {
+ spice_info("client doesn't want to use shared memory");
+ } else {
+ spice_info("got reply from client, using shared memory!");
+ on_new_display_channel_client_after_shm_offer(dcc);
+ }
+ return TRUE;
+}
+
static int display_channel_handle_message(RedChannelClient *rcc, uint32_t size, uint16_t type,
void *message)
{
@@ -10110,6 +10432,9 @@ static int display_channel_handle_message(RedChannelClient *rcc, uint32_t size,
case SPICE_MSGC_DISPLAY_STREAM_REPORT:
return display_channel_handle_stream_report(dcc,
(SpiceMsgcDisplayStreamReport *)message);
+ case SPICE_MSGC_DISPLAY_SHM_REPLY:
+ return display_channel_handle_shm_reply(
+ dcc, (SpiceMsgcDisplayShmReply *)message);
default:
return red_channel_client_handle_message(rcc, size, type, message);
}
@@ -10460,6 +10785,13 @@ static void display_channel_client_release_item_before_push(DisplayChannelClient
free(item);
break;
}
+ case PIPE_ITEM_TYPE_SHM_DAMAGE: {
+ ShmDamageItem *damage = SPICE_CONTAINEROF(item, ShmDamageItem, pipe_item);
+
+ free(damage->clip);
+ free(damage);
+ break;
+ }
case PIPE_ITEM_TYPE_INVAL_ONE:
case PIPE_ITEM_TYPE_VERB:
case PIPE_ITEM_TYPE_MIGRATE_DATA:
@@ -10467,6 +10799,7 @@ static void display_channel_client_release_item_before_push(DisplayChannelClient
case PIPE_ITEM_TYPE_PIXMAP_RESET:
case PIPE_ITEM_TYPE_INVAL_PALLET_CACHE:
case PIPE_ITEM_TYPE_STREAM_ACTIVATE_REPORT:
+ case PIPE_ITEM_TYPE_SHM_OFFER:
free(item);
break;
default:
@@ -10544,6 +10877,7 @@ static void guest_set_client_capabilities(RedWorker *worker)
SPICE_DISPLAY_CAP_MONITORS_CONFIG,
SPICE_DISPLAY_CAP_COMPOSITE,
SPICE_DISPLAY_CAP_A8_SURFACE,
+ SPICE_DISPLAY_CAP_SHARED_MEMORY,
};
if (worker->qxl->st->qif->base.major_version < 3 ||
@@ -11120,21 +11454,24 @@ static void set_monitors_config_to_primary(RedWorker *worker)
}
static void dev_create_primary_surface(RedWorker *worker, uint32_t surface_id,
- QXLDevSurfaceCreate surface)
+ QXLDevSurfaceCreate surface,
+ uint64_t shm_offset)
{
uint8_t *line_0;
int error;
+ int surface_size;
spice_debug(NULL);
spice_warn_if(surface_id != 0);
spice_warn_if(surface.height == 0);
spice_warn_if(((uint64_t)abs(surface.stride) * (uint64_t)surface.height) !=
abs(surface.stride) * surface.height);
+ surface_size = surface.height * abs(surface.stride);
line_0 = (uint8_t*)get_virt(&worker->mem_slots, surface.mem,
- surface.height * abs(surface.stride),
- surface.group_id, &error);
+ surface_size, surface.group_id, &error);
if (error) {
+ spice_warning("error getting memory address for primary surface");
return;
}
if (surface.stride < 0) {
@@ -11142,7 +11479,7 @@ static void dev_create_primary_surface(RedWorker *worker, uint32_t surface_id,
}
red_create_surface(worker, 0, surface.width, surface.height, surface.stride, surface.format,
- line_0, surface.flags & QXL_SURF_FLAG_KEEP_DATA, TRUE);
+ line_0, surface.flags & QXL_SURF_FLAG_KEEP_DATA, TRUE, shm_offset);
set_monitors_config_to_primary(worker);
if (display_is_connected(worker) && !worker->display_channel->common.during_target_migrate) {
@@ -11167,7 +11504,8 @@ void handle_dev_create_primary_surface(void *opaque, void *payload)
RedWorkerMessageCreatePrimarySurface *msg = payload;
RedWorker *worker = opaque;
- dev_create_primary_surface(worker, msg->surface_id, msg->surface);
+ dev_create_primary_surface(worker, msg->surface_id, msg->surface,
+ msg->shm_offset);
}
static void dev_destroy_primary_surface(RedWorker *worker, uint32_t surface_id)
@@ -11377,7 +11715,8 @@ void handle_dev_create_primary_surface_async(void *opaque, void *payload)
RedWorkerMessageCreatePrimarySurfaceAsync *msg = payload;
RedWorker *worker = opaque;
- dev_create_primary_surface(worker, msg->surface_id, msg->surface);
+ dev_create_primary_surface(worker, msg->surface_id, msg->surface,
+ msg->shm_offset);
}
/* exception for Dispatcher, data going from red_worker to main thread,
diff --git a/server/spice-server.syms b/server/spice-server.syms
index 4f2dc37..27baff2 100644
--- a/server/spice-server.syms
+++ b/server/spice-server.syms
@@ -145,3 +145,8 @@ SPICE_SERVER_0.12.4 {
global:
spice_server_set_agent_file_xfer;
} SPICE_SERVER_0.12.3;
+
+SPICE_SERVER_0.12.5 {
+global:
+ spice_qxl_create_primary_surface_shm;
+} SPICE_SERVER_0.12.4;
diff --git a/server/spice.h b/server/spice.h
index 6fbb7b2..5c4676e 100644
--- a/server/spice.h
+++ b/server/spice.h
@@ -23,7 +23,7 @@
#include <spice/qxl_dev.h>
#include <spice/vd_agent.h>
-#define SPICE_SERVER_VERSION 0x000c04 /* release 0.12.4 */
+#define SPICE_SERVER_VERSION 0x000c05 /* release 0.12.5 */
/* interface base type */
@@ -97,7 +97,7 @@ struct SpiceCoreInterface {
#define SPICE_INTERFACE_QXL "qxl"
#define SPICE_INTERFACE_QXL_MAJOR 3
-#define SPICE_INTERFACE_QXL_MINOR 3
+#define SPICE_INTERFACE_QXL_MINOR 4
typedef struct QXLInterface QXLInterface;
typedef struct QXLInstance QXLInstance;
typedef struct QXLState QXLState;
@@ -169,6 +169,10 @@ void spice_qxl_monitors_config_async(QXLInstance *instance, QXLPHYSICAL monitors
int group_id, uint64_t cookie);
/* since spice 0.12.3 */
void spice_qxl_driver_unload(QXLInstance *instance);
+/* since spice 0.12.5 */
+void spice_qxl_create_primary_surface_shm(QXLInstance *instance, uint32_t surface_id,
+ QXLDevSurfaceCreate *surface, uint64_t cookie,
+ uint64_t shm_offset);
typedef struct QXLDrawArea {
uint8_t *buf;
@@ -250,6 +254,9 @@ struct QXLInterface {
* return code. */
int (*client_monitors_config)(QXLInstance *qin,
VDAgentMonitorsConfig *monitors_config);
+
+ /* Returns NULL if no file is avilable, or file if it is available */
+ const char *(*shared_memory_file_name)(QXLInstance *qin);
};
struct QXLInstance {
diff --git a/server/tests/test_display_base.c b/server/tests/test_display_base.c
index 20c0e47..4359414 100644
--- a/server/tests/test_display_base.c
+++ b/server/tests/test_display_base.c
@@ -9,6 +9,8 @@
#include <sys/select.h>
#include <sys/types.h>
#include <getopt.h>
+#include <fcntl.h>
+#include <sys/mman.h>
#include "spice.h"
#include <spice/qxl_dev.h>
@@ -61,7 +63,18 @@ static int c_i = 0;
/* Used for automated tests */
static int control = 3; //used to know when we can take a screenshot
static int rects = 16; //number of rects that will be draw
-static int has_automated_tests = 0; //automated test flag
+static int has_automated_tests; //automated test flag
+
+/* Shared memory for primary surface */
+static int has_shared_memory;
+static struct {
+ char name[128];
+ int fd;
+ void *ptr;
+} shm;
+
+/* primary surface stride control */
+static int g_primary_stride = 1;
__attribute__((noreturn))
static void sigchld_handler(int signal_num) // wait for the child process and exit
@@ -337,8 +350,11 @@ static SimpleSurfaceCmd *create_surface(int surface_id, int format, int width, i
surface_cmd->u.surface_create.format = format;
surface_cmd->u.surface_create.width = width;
surface_cmd->u.surface_create.height = height;
- surface_cmd->u.surface_create.stride = -width * bpp;
+ surface_cmd->u.surface_create.stride = width * bpp;
surface_cmd->u.surface_create.data = (intptr_t)data;
+ printf("creating surface with %s stride\n", surface_cmd->u.surface_create.stride > 0 ?
+ "positive" : "negative");
+
return simple_cmd;
}
@@ -369,17 +385,28 @@ static void create_primary_surface(Test *test, uint32_t width,
surface.format = SPICE_SURFACE_FMT_32_xRGB;
surface.width = test->primary_width = width;
surface.height = test->primary_height = height;
- surface.stride = -width * 4; /* negative? */
+ printf("g_primary_stride = %d\n", g_primary_stride);
+ surface.stride = g_primary_stride * width * 4; /* negative? */
surface.mouse_mode = TRUE; /* unused by red_worker */
surface.flags = 0;
surface.type = 0; /* unused by red_worker */
surface.position = 0; /* unused by red_worker */
- surface.mem = (uint64_t)&test->primary_surface;
+ if (has_shared_memory) {
+ surface.mem = (uint64_t)shm.ptr;
+ } else {
+ surface.mem = (uint64_t)test->primary_surface;
+ }
+ if (g_primary_stride < 0) {
+ surface.mem += (g_primary_stride > 0 ? (width * 4 * (height - 1)) : 0);
+ }
surface.group_id = MEM_SLOT_GROUP_ID;
test->width = width;
test->height = height;
+ printf("creating primary with %s stride\n", surface.stride > 0 ?
+ "positive" : "negative");
+
qxl_worker->create_primary_surface(qxl_worker, 0, &surface);
}
@@ -756,6 +783,14 @@ static void set_client_capabilities(QXLInstance *qin,
}
}
+const char *interface_shared_memory_file_name(QXLInstance *sin)
+{
+ if (has_shared_memory) {
+ return shm.name;
+ }
+ return 0;
+}
+
QXLInterface display_sif = {
.base = {
.type = SPICE_INTERFACE_QXL,
@@ -778,6 +813,7 @@ QXLInterface display_sif = {
.flush_resources = flush_resources,
.client_monitors_config = client_monitors_config,
.set_client_capabilities = set_client_capabilities,
+ .shared_memory_file_name = interface_shared_memory_file_name,
};
/* interface for tests */
@@ -844,6 +880,30 @@ void test_set_command_list(Test *test, Command *commands, int num_commands)
test->num_commands = num_commands;
}
+void init_shared_memory(void)
+{
+ int size = MAX_HEIGHT * MAX_WIDTH * 4 + SURF_WIDTH * SURF_HEIGHT * 4;
+
+ snprintf((char *)shm.name, sizeof(shm.name), "spice.primary.debug");
+ printf("setting up shared memory using %s\n", shm.name);
+ /* O_EXCL removed for easier testing - unsecure */
+ shm.fd = shm_open((char *)shm.name, O_CREAT|O_RDWR,
+ S_IRWXU|S_IRWXG|S_IRWXO);
+ if (shm.fd <= 0) {
+ fprintf(stderr, "test: failed to allocate shared memory\n");
+ exit(-1);
+ }
+ if (ftruncate(shm.fd, size) != 0) {
+ fprintf(stderr, "test: failed to ftruncate shared memory to %d\n",
+ size);
+ exit(-1);
+ }
+ shm.ptr = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, shm.fd, 0);
+ if (!shm.ptr) {
+ fprintf(stderr, "test: failed to mmap the shared memory file\n");
+ exit(-1);
+ }
+}
Test *test_new(SpiceCoreInterface *core)
{
@@ -868,10 +928,19 @@ Test *test_new(SpiceCoreInterface *core)
path_init(&path, 0, angle_parts);
test->has_secondary = 0;
test->wakeup_timer = core->timer_add(do_wakeup, test);
+
+ if (has_shared_memory) {
+ init_shared_memory();
+ test->primary_surface = shm.ptr;
+ test->secondary_surface = shm.ptr + MAX_HEIGHT * MAX_WIDTH * 4;
+ } else {
+ test->primary_surface = spice_malloc(MAX_HEIGHT * MAX_WIDTH * 4);
+ test->secondary_surface = spice_malloc(SURF_HEIGHT * SURF_WIDTH * 4);
+ }
return test;
}
-void init_automated()
+void init_automated(void)
{
struct sigaction sa;
@@ -899,6 +968,9 @@ void spice_test_config_parse_args(int argc, char **argv)
#ifdef AUTOMATED_TESTS
{"automated-tests", no_argument, &has_automated_tests, 1},
#endif
+ {"shared-memory", no_argument, &has_shared_memory, 1},
+ {"primary-stride-positive", no_argument, &g_primary_stride, 1},
+ {"primary-stride-negative", no_argument, &g_primary_stride, -1},
{NULL, 0, NULL, 0},
};
int option_index;
diff --git a/server/tests/test_display_base.h b/server/tests/test_display_base.h
index d2823a7..6d22ea7 100644
--- a/server/tests/test_display_base.h
+++ b/server/tests/test_display_base.h
@@ -87,7 +87,7 @@ struct Test {
QXLInstance qxl_instance;
QXLWorker *qxl_worker;
- uint8_t primary_surface[MAX_HEIGHT * MAX_WIDTH * 4];
+ uint8_t *primary_surface;
int primary_height;
int primary_width;
@@ -96,7 +96,7 @@ struct Test {
int cursor_notify;
- uint8_t secondary_surface[SURF_WIDTH * SURF_HEIGHT * 4];
+ uint8_t *secondary_surface;
int has_secondary;
// Current mode (set by create_primary)
diff --git a/server/tests/test_display_no_ssl.c b/server/tests/test_display_no_ssl.c
index 83ab3dc..a5351bb 100644
--- a/server/tests/test_display_no_ssl.c
+++ b/server/tests/test_display_no_ssl.c
@@ -35,10 +35,11 @@ int simple_commands[] = {
SIMPLE_UPDATE,
};
-int main(void)
+int main(int argc, char **argv)
{
Test *test;
+ spice_test_config_parse_args(argc, argv);
core = basic_event_loop_init();
test = test_new(core);
//spice_server_set_image_compression(server, SPICE_IMAGE_COMPRESS_OFF);
--
1.8.3.1
More information about the Spice-devel
mailing list