summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Levy <alevy@redhat.com>2013-08-06 18:01:59 +0200
committerAlon Levy <alevy@redhat.com>2013-08-12 22:17:09 +0300
commitd770c8a5927ca25fcab7311ee6414f08d47d8505 (patch)
treecf2aeab9101ba8cc542cbd6cda155a48d47e3d12
parent294a96f390f679c6e76e40d8d7e90b308d1843ca (diff)
shared memory support for display channelwip/shared-memory
See complete description in spice-protocol commit f24154b87f92ae65b1aa83b97378f9c687d09017
-rw-r--r--gtk/channel-display-priv.h2
-rw-r--r--gtk/channel-display.c195
-rw-r--r--gtk/channel-display.h1
-rw-r--r--gtk/spice-widget-cairo.c22
4 files changed, 207 insertions, 13 deletions
diff --git a/gtk/channel-display-priv.h b/gtk/channel-display-priv.h
index 09683be..2fe889e 100644
--- a/gtk/channel-display-priv.h
+++ b/gtk/channel-display-priv.h
@@ -38,11 +38,13 @@ G_BEGIN_DECLS
typedef struct display_surface {
RingItem link;
+ guint64 shm_offset;
guint32 surface_id;
bool primary;
enum SpiceSurfaceFmt format;
int width, height, stride, size;
int shmid;
+ int top_down;
uint8_t *data;
SpiceCanvas *canvas;
SpiceGlzDecoder *glz_decoder;
diff --git a/gtk/channel-display.c b/gtk/channel-display.c
index 75fa8c2..26eb6b9 100644
--- a/gtk/channel-display.c
+++ b/gtk/channel-display.c
@@ -31,6 +31,9 @@
#include <sys/ipc.h>
#endif
+#include <fcntl.h>
+#include <sys/mman.h>
+
#include "glib-compat.h"
#include "spice-client.h"
#include "spice-common.h"
@@ -65,6 +68,10 @@
#define MONITORS_MAX 256
+/* Must be != -1 (used for invalid shm_id but no shm_open) and negative, so in
+ * [MIN_INT, -2] */
+#define SHM_ID_USING_SHM_OPEN -2
+
struct _SpiceDisplayChannelPrivate {
Ring surfaces;
display_cache *images;
@@ -80,6 +87,12 @@ struct _SpiceDisplayChannelPrivate {
GArray *monitors;
guint monitors_max;
gboolean enable_adaptive_streaming;
+ struct {
+ gboolean enable; /* do we provide capability */
+ gboolean use; /* is it on. !enable => !use */
+ int fd;
+ void *mem;
+ } shm;
#ifdef WIN32
HDC dc;
#endif
@@ -421,6 +434,7 @@ gboolean spice_display_get_primary(SpiceChannel *channel, guint32 surface_id,
primary->shmid = surface->shmid;
primary->data = surface->data;
primary->marked = c->mark;
+ primary->top_down = surface->top_down;
CHANNEL_DEBUG(channel, "get primary %p", primary->data);
return TRUE;
@@ -697,6 +711,9 @@ static void spice_display_channel_reset_capabilities(SpiceChannel *channel)
if (SPICE_DISPLAY_CHANNEL(channel)->priv->enable_adaptive_streaming) {
spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_DISPLAY_CAP_STREAM_REPORT);
}
+ if (SPICE_DISPLAY_CHANNEL(channel)->priv->shm.enable) {
+ spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_DISPLAY_CAP_SHARED_MEMORY);
+ }
}
static void spice_display_channel_init(SpiceDisplayChannel *channel)
@@ -713,6 +730,9 @@ static void spice_display_channel_init(SpiceDisplayChannel *channel)
c->dc = create_compatible_dc();
#endif
c->monitors_max = 1;
+ if (g_getenv("SPICE_DISABLE_SHARED_MEMORY") == NULL) {
+ c->shm.enable = TRUE;
+ }
if (g_getenv("SPICE_DISABLE_ADAPTIVE_STREAMING")) {
SPICE_DEBUG("adaptive video disabled");
@@ -763,7 +783,23 @@ static int create_canvas(SpiceChannel *channel, display_surface *surface)
}
if (surface->shmid == -1) {
- surface->data = spice_malloc(surface->size);
+ if (c->shm.fd && surface->primary) {
+ /* surface->shm_offset */;
+ //uint64_t offset = 0;
+ uint64_t offset = surface->shm_offset;
+ spice_info("shared memory mapping surface %d at offset %zd (%zd)\n",
+ surface->surface_id, offset, surface->shm_offset);
+ surface->data = mmap(0, surface->size, PROT_READ, MAP_SHARED,
+ c->shm.fd, offset);
+ c->shm.mem = surface->data;
+ surface->shmid = SHM_ID_USING_SHM_OPEN;
+ if (!surface->data) {
+ g_warning("failed to mmap shared memory from server");
+ }
+ }
+ if (surface->data == NULL) {
+ surface->data = spice_malloc(surface->size);
+ }
}
g_return_val_if_fail(c->glz_window, 0);
@@ -821,15 +857,19 @@ static void destroy_canvas(display_surface *surface)
zlib_decoder_destroy(surface->zlib_decoder);
jpeg_decoder_destroy(surface->jpeg_decoder);
- if (surface->shmid == -1) {
+ switch (surface->shmid) {
+ case -1:
free(surface->data);
- }
-#ifdef HAVE_SYS_SHM_H
- else {
+ break;
+ case SHM_ID_USING_SHM_OPEN:
+ /* we unmap on the next create */
+ break;
+ default:
+#if defined(HAVE_X11) && defined(HAVE_SYS_SHM_H)
shmdt(surface->data);
shmctl(surface->shmid, IPC_RMID, 0);
- }
#endif
+ }
surface->shmid = -1;
surface->data = NULL;
@@ -1728,10 +1768,10 @@ static void display_handle_draw_composite(SpiceChannel *channel, SpiceMsgIn *in)
}
/* coroutine context */
-static void display_handle_surface_create(SpiceChannel *channel, SpiceMsgIn *in)
+static void display_handle_surface_create_helper(SpiceChannel *channel,
+ SpiceMsgSurfaceCreateShm *create)
{
SpiceDisplayChannelPrivate *c = SPICE_DISPLAY_CHANNEL(channel)->priv;
- SpiceMsgSurfaceCreate *create = spice_msg_in_parsed(in);
display_surface *surface = spice_new0(display_surface, 1);
surface->surface_id = create->surface_id;
@@ -1740,8 +1780,10 @@ static void display_handle_surface_create(SpiceChannel *channel, SpiceMsgIn *in)
surface->height = create->height;
surface->stride = create->width * 4;
surface->size = surface->height * surface->stride;
+ surface->shm_offset = create->shm_offset;
+ surface->top_down = c->shm.use && !!(create->flags & SPICE_SURFACE_FLAGS_TOP_DOWN);
- if (create->flags == SPICE_SURFACE_FLAGS_PRIMARY) {
+ if (create->flags & SPICE_SURFACE_FLAGS_PRIMARY) {
surface->primary = true;
create_canvas(channel, surface);
if (c->mark_false_event_id != 0) {
@@ -1754,6 +1796,29 @@ static void display_handle_surface_create(SpiceChannel *channel, SpiceMsgIn *in)
}
}
+/* coroutine context */
+static void display_handle_surface_create(SpiceChannel *channel, SpiceMsgIn *in)
+{
+ SpiceMsgSurfaceCreate *create_no_shm = spice_msg_in_parsed(in);
+ SpiceMsgSurfaceCreateShm create;
+
+ create.surface_id = create_no_shm->surface_id;
+ create.format = create_no_shm->format;
+ create.width = create_no_shm->width;
+ create.height = create_no_shm->height;
+ create.flags = create_no_shm->flags;
+ create.shm_offset = 0;
+ display_handle_surface_create_helper(channel, &create);
+}
+
+/* coroutine context */
+static void display_handle_surface_create_shm(SpiceChannel *channel, SpiceMsgIn *in)
+{
+ SpiceMsgSurfaceCreateShm *create = spice_msg_in_parsed(in);
+
+ display_handle_surface_create_helper(channel, create);
+}
+
static gboolean display_mark_false(gpointer data)
{
SpiceChannel *channel = data;
@@ -1840,6 +1905,114 @@ static void display_handle_monitors_config(SpiceChannel *channel, SpiceMsgIn *in
g_object_notify_main_context(G_OBJECT(channel), "monitors");
}
+
+/* coroutine context */
+static void display_handle_shm_offer(SpiceChannel *channel, SpiceMsgIn *in)
+{
+ int success = 0;
+ SpiceMsgDisplayShmOffer *offer = spice_msg_in_parsed(in);
+ SpiceDisplayChannelPrivate *c = SPICE_DISPLAY_CHANNEL(channel)->priv;
+
+ CHANNEL_DEBUG(channel, "SHM offer %s", offer->name);
+
+ if (c->shm.enable) {
+ offer->name[sizeof(offer->name) - 1] = 0;
+ c->shm.fd = shm_open((const char *)offer->name, O_RDONLY, 0);
+ if (c->shm.fd <= 0) {
+ g_warning("shm_open failed");
+ } else {
+ success = 1;
+ }
+ }
+
+ c->shm.use = success;
+ /* reply to shared memory offer */
+ {
+ SpiceMsgcDisplayShmReply reply;
+ SpiceMsgOut *out;
+ reply.accepted = success;
+ out = spice_msg_out_new(channel, SPICE_MSGC_DISPLAY_SHM_REPLY);
+ out->marshallers->msgc_display_shm_reply(out->marshaller, &reply);
+ spice_msg_out_send_internal(out);
+ }
+}
+
+static int clip_num_rects(SpiceClip *clip)
+{
+ switch (clip->type) {
+ case SPICE_CLIP_TYPE_RECTS:
+ return clip->rects->num_rects;
+ case SPICE_CLIP_TYPE_NONE:
+ default:
+ return 0;
+ }
+}
+
+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;
+}
+
+/* coroutine context */
+static void display_handle_shm_damage(SpiceChannel *channel, SpiceMsgIn *in)
+{
+ SpiceDisplayChannelPrivate *c = SPICE_DISPLAY_CHANNEL(channel)->priv;
+ display_surface *surface;
+ SpiceMsgDisplayShmDamage *damage = spice_msg_in_parsed(in);
+ SpiceRect *box = &damage->box;
+
+ surface = find_surface(c, 0);
+ CHANNEL_DEBUG(channel, "SHM damage: [%d,%d,%d,%d], clip #%d",
+ box->left, box->top, box->right, box->bottom,
+ clip_num_rects(&damage->clip));
+#if 0
+#if 1
+ box->left = 0;
+ box->right = surface->width;
+ box->top = 0;
+ box->bottom = surface->height;
+#else
+ {
+ int tmp = surface->height - box->top;
+ box->top = surface->height - box->bottom;
+ box->bottom = tmp;
+ }
+#endif
+#endif
+ emit_invalidate(channel, box);
+ spice_return_if_fail(surface != NULL);
+#if 0
+ write_ppm_32(c->shm.mem, surface->width, surface->height);
+#else
+ (void)write_ppm_32;
+#endif
+}
+
static const spice_msg_handler display_handlers[] = {
[ SPICE_MSG_DISPLAY_MODE ] = display_handle_mode,
[ SPICE_MSG_DISPLAY_MARK ] = display_handle_mark,
@@ -1873,9 +2046,13 @@ static const spice_msg_handler display_handlers[] = {
[ SPICE_MSG_DISPLAY_DRAW_COMPOSITE ] = display_handle_draw_composite,
[ SPICE_MSG_DISPLAY_SURFACE_CREATE ] = display_handle_surface_create,
+ [ SPICE_MSG_DISPLAY_SURFACE_CREATE_SHM ] = display_handle_surface_create_shm,
[ SPICE_MSG_DISPLAY_SURFACE_DESTROY ] = display_handle_surface_destroy,
[ SPICE_MSG_DISPLAY_MONITORS_CONFIG ] = display_handle_monitors_config,
+
+ [ SPICE_MSG_DISPLAY_SHM_OFFER ] = display_handle_shm_offer,
+ [ SPICE_MSG_DISPLAY_SHM_DAMAGE ] = display_handle_shm_damage
};
/* coroutine context */
diff --git a/gtk/channel-display.h b/gtk/channel-display.h
index 88e60d9..815cdbb 100644
--- a/gtk/channel-display.h
+++ b/gtk/channel-display.h
@@ -52,6 +52,7 @@ struct _SpiceDisplayPrimary {
gint shmid;
guint8 *data;
gboolean marked;
+ gboolean top_down;
};
/**
diff --git a/gtk/spice-widget-cairo.c b/gtk/spice-widget-cairo.c
index 5e2b944..6e7bf59 100644
--- a/gtk/spice-widget-cairo.c
+++ b/gtk/spice-widget-cairo.c
@@ -36,6 +36,7 @@ G_GNUC_INTERNAL
int spicex_image_create(SpiceDisplay *display)
{
SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
+ uint8_t *data;
if (d->ximage != NULL)
return 0;
@@ -50,9 +51,13 @@ int spicex_image_create(SpiceDisplay *display)
} else {
d->convert = FALSE;
-
- d->ximage = cairo_image_surface_create_for_data
- (d->data, CAIRO_FORMAT_RGB24, d->width, d->height, d->stride);
+ if (d->stride < 0) {
+ data = ((uint8_t *)d->data) + ((-d->stride) * (d->height - 1));
+ } else {
+ data = d->data;
+ }
+ d->ximage = cairo_image_surface_create_for_data(
+ data, CAIRO_FORMAT_RGB24, d->width, d->height, d->stride);
}
return 0;
@@ -92,8 +97,9 @@ void spicex_draw_event(SpiceDisplay *display, cairo_t *cr)
int x, y;
int ww, wh;
int w, h;
+ int top_down;
- spice_display_get_scaling(display, &s, &x, &y, &w, &h, NULL);
+ spice_display_get_scaling(display, &s, &x, &y, &w, &h, &top_down);
gdk_drawable_get_size(gtk_widget_get_window(GTK_WIDGET(display)), &ww, &wh);
@@ -128,11 +134,19 @@ void spicex_draw_event(SpiceDisplay *display, cairo_t *cr)
if (d->ximage) {
cairo_translate(cr, x, y);
cairo_rectangle(cr, 0, 0, w, h);
+ if (top_down) {
+ cairo_save(cr);
+ cairo_scale(cr, 1, -1);
+ cairo_translate(cr, 0, -h);
+ }
cairo_scale(cr, s, s);
if (!d->convert)
cairo_translate(cr, -d->area.x, -d->area.y);
cairo_set_source_surface(cr, d->ximage, 0, 0);
cairo_fill(cr);
+ if (top_down) {
+ cairo_restore(cr);
+ }
if (d->mouse_mode == SPICE_MOUSE_MODE_SERVER &&
d->mouse_guest_x != -1 && d->mouse_guest_y != -1 &&