diff options
author | Alon Levy <alevy@redhat.com> | 2013-08-06 18:01:59 +0200 |
---|---|---|
committer | Alon Levy <alevy@redhat.com> | 2013-08-12 22:17:09 +0300 |
commit | d770c8a5927ca25fcab7311ee6414f08d47d8505 (patch) | |
tree | cf2aeab9101ba8cc542cbd6cda155a48d47e3d12 | |
parent | 294a96f390f679c6e76e40d8d7e90b308d1843ca (diff) |
shared memory support for display channelwip/shared-memory
See complete description in spice-protocol commit
f24154b87f92ae65b1aa83b97378f9c687d09017
-rw-r--r-- | gtk/channel-display-priv.h | 2 | ||||
-rw-r--r-- | gtk/channel-display.c | 195 | ||||
-rw-r--r-- | gtk/channel-display.h | 1 | ||||
-rw-r--r-- | gtk/spice-widget-cairo.c | 22 |
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 && |