[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