[Spice-devel] [PATCH spice-gtk 7/7] gtk: add spice-widget GL scanout support
Marc-André Lureau
marcandre.lureau at gmail.com
Mon Jan 4 09:33:33 PST 2016
Hi
I realize there are still a few missing cleanups here and there.
Please do not review thoroughly, consider it as wip.
thanks
On Mon, Jan 4, 2016 at 3:46 PM, Marc-André Lureau
<marcandre.lureau at gmail.com> wrote:
> From: Marc-André Lureau <mlureau at redhat.com>
>
> Hook to spice-glib events to show the GL scanout.
>
> The opengl context is created with egl, and is currently
> x11-only (supporting wayland with egl doesn't seem trivial). Using
> GtkGlArea is also left for the future, since SpiceDisplay widget is a
> GtkDrawingArea and can't be replaced without breaking ABI.
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau at redhat.com>
> ---
> src/Makefile.am | 9 +
> src/spice-widget-egl.c | 530 ++++++++++++++++++++++++++++++++++++++++++++++++
> src/spice-widget-priv.h | 30 +++
> src/spice-widget.c | 150 ++++++++++++--
> 4 files changed, 699 insertions(+), 20 deletions(-)
> create mode 100644 src/spice-widget-egl.c
>
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 37b89fe..68884e6 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -122,6 +122,7 @@ SPICE_GTK_LIBADD_COMMON = \
> libspice-client-glib-2.0.la \
> $(GTK_LIBS) \
> $(CAIRO_LIBS) \
> + $(EPOXY_LIBS) \
> $(LIBM) \
> $(NULL)
>
> @@ -160,17 +161,25 @@ SPICE_GTK_SOURCES_COMMON += \
> endif
>
> if WITH_GTK
> +if WITH_EPOXY
> +SPICE_GTK_SOURCES_COMMON += \
> + spice-widget-egl.c \
> + $(NULL)
> +endif
> +
> if HAVE_GTK_2
> libspice_client_gtk_2_0_la_DEPEDENCIES = $(GTK_SYMBOLS_FILE)
> libspice_client_gtk_2_0_la_LDFLAGS = $(SPICE_GTK_LDFLAGS_COMMON)
> libspice_client_gtk_2_0_la_LIBADD = $(SPICE_GTK_LIBADD_COMMON)
> libspice_client_gtk_2_0_la_SOURCES = $(SPICE_GTK_SOURCES_COMMON)
> +libspice_client_gtk_2_0_la_CFLAGS = $(EPOXY_CFLAGS)
> nodist_libspice_client_gtk_2_0_la_SOURCES = $(nodist_SPICE_GTK_SOURCES_COMMON)
> else
> libspice_client_gtk_3_0_la_DEPEDENCIES = $(GTK_SYMBOLS_FILE)
> libspice_client_gtk_3_0_la_LDFLAGS = $(SPICE_GTK_LDFLAGS_COMMON)
> libspice_client_gtk_3_0_la_LIBADD = $(SPICE_GTK_LIBADD_COMMON)
> libspice_client_gtk_3_0_la_SOURCES = $(SPICE_GTK_SOURCES_COMMON)
> +libspice_client_gtk_3_0_la_CFLAGS = $(EPOXY_CFLAGS)
> nodist_libspice_client_gtk_3_0_la_SOURCES = $(nodist_SPICE_GTK_SOURCES_COMMON)
> endif
>
> diff --git a/src/spice-widget-egl.c b/src/spice-widget-egl.c
> new file mode 100644
> index 0000000..cafa2b3
> --- /dev/null
> +++ b/src/spice-widget-egl.c
> @@ -0,0 +1,530 @@
> +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> +/*
> + Copyright (C) 2014-2015 Red Hat, Inc.
> +
> + This library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + This library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with this library; if not, see <http://www.gnu.org/licenses/>.
> +*/
> +#include "config.h"
> +
> +#include <math.h>
> +
> +#define EGL_EGLEXT_PROTOTYPES
> +#define GL_GLEXT_PROTOTYPES
> +
> +#include "spice-widget.h"
> +#include "spice-widget-priv.h"
> +#include "spice-gtk-session-priv.h"
> +#include <libdrm/drm_fourcc.h>
> +
> +#include <gdk/gdkx.h>
> +
> +static const char *spice_egl_vertex_src = \
> +" \
> + attribute vec4 position; \
> + attribute vec2 texcoords; \
> + varying vec2 tcoords; \
> + uniform mat4 mproj; \
> + \
> + void main() \
> + { \
> + tcoords = texcoords; \
> + gl_Position = mproj * position; \
> + } \
> +";
> +
> +static const char *spice_egl_fragment_src = \
> +" \
> + varying highp vec2 tcoords; \
> + uniform sampler2D samp; \
> + \
> + void main() \
> + { \
> + gl_FragColor = texture2D(samp, tcoords); \
> + } \
> +";
> +
> +static void apply_ortho(guint mproj, float left, float right,
> + float bottom, float top, float near, float far)
> +
> +{
> + float a = 2.0f / (right - left);
> + float b = 2.0f / (top - bottom);
> + float c = -2.0f / (far - near);
> +
> + float tx = - (right + left) / (right - left);
> + float ty = - (top + bottom) / (top - bottom);
> + float tz = - (far + near) / (far - near);
> +
> + float ortho[16] = {
> + a, 0, 0, 0,
> + 0, b, 0, 0,
> + 0, 0, c, 0,
> + tx, ty, tz, 1
> + };
> +
> + glUniformMatrix4fv(mproj, 1, GL_FALSE, &ortho[0]);
> +}
> +
> +static gboolean spice_egl_init_shaders(SpiceDisplay *display, GError **err)
> +{
> + SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
> + GLuint fs, vs, prog;
> + GLint stat;
> +
> + fs = glCreateShader(GL_FRAGMENT_SHADER);
> + glShaderSource(fs, 1, (const char **)&spice_egl_fragment_src, NULL);
> + glCompileShader(fs);
> + glGetShaderiv(fs, GL_COMPILE_STATUS, &stat);
> + if (!stat) {
> + g_set_error_literal(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
> + "Failed to compile FS");
> + return false;
> + }
> +
> + vs = glCreateShader(GL_VERTEX_SHADER);
> + glShaderSource(vs, 1, (const char **)&spice_egl_vertex_src, NULL);
> + glCompileShader(vs);
> + glGetShaderiv(vs, GL_COMPILE_STATUS, &stat);
> + if (!stat) {
> + g_set_error_literal(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
> + "failed to compile VS");
> + return false;
> + }
> +
> + prog = glCreateProgram();
> + glAttachShader(prog, fs);
> + glAttachShader(prog, vs);
> + glLinkProgram(prog);
> +
> + glGetProgramiv(prog, GL_LINK_STATUS, &stat);
> + if (!stat) {
> + char log[1000];
> + GLsizei len;
> + glGetProgramInfoLog(prog, 1000, &len, log);
> + g_set_error(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
> + "Error linking shaders: %s", log);
> + return false;
> + }
> +
> + glUseProgram(prog);
> +
> + d->egl.attr_pos = glGetAttribLocation(prog, "position");
> + d->egl.attr_tex = glGetAttribLocation(prog, "texcoords");
> + d->egl.tex_loc = glGetUniformLocation(prog, "samp");
> + d->egl.mproj = glGetUniformLocation(prog, "mproj");
> +
> + glUniform1i(d->egl.tex_loc, 0);
> +
> + glGenBuffers(1, &d->egl.vbuf_id);
> + glBindBuffer(GL_ARRAY_BUFFER, d->egl.vbuf_id);
> + glBufferData(GL_ARRAY_BUFFER,
> + (sizeof(GLfloat) * 4 * 4) +
> + (sizeof(GLfloat) * 4 * 2),
> + NULL,
> + GL_STATIC_DRAW);
> +
> + return true;
> +}
> +
> +gboolean spice_egl_init(SpiceDisplay *display, GError **err)
> +{
> + SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
> + static const EGLint conf_att[] = {
> + EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
> + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
> + EGL_RED_SIZE, 8,
> + EGL_GREEN_SIZE, 8,
> + EGL_BLUE_SIZE, 8,
> + EGL_ALPHA_SIZE, 0,
> + EGL_NONE,
> + };
> + static const EGLint ctx_att[] = {
> +#ifdef EGL_CONTEXT_MAJOR_VERSION
> + EGL_CONTEXT_MAJOR_VERSION, 2,
> +#else
> + EGL_CONTEXT_CLIENT_VERSION, 2,
> +#endif
> + EGL_NONE
> + };
> + EGLBoolean b;
> + EGLenum api;
> + EGLint major, minor, n;
> + EGLNativeDisplayType dpy = 0;
> + GdkDisplay *gdk_dpy = gdk_display_get_default();
> +
> +#ifdef GDK_WINDOWING_X11
> + if (GDK_IS_X11_DISPLAY(gdk_dpy)) {
> + dpy = (EGLNativeDisplayType)gdk_x11_display_get_xdisplay(gdk_dpy);
> + }
> +#endif
> +
> + d->egl.display = eglGetDisplay(dpy);
> + if (d->egl.display == EGL_NO_DISPLAY) {
> + g_critical("Failed to get EGL display");
> + return false;
> + }
> +
> + if (!eglInitialize(d->egl.display, &major, &minor)) {
> + g_critical("Failed to init EGL display");
> + return false;
> + }
> +
> + SPICE_DEBUG("EGL major/minor: %d.%d\n", major, minor);
> + SPICE_DEBUG("EGL version: %s\n",
> + eglQueryString(d->egl.display, EGL_VERSION));
> + SPICE_DEBUG("EGL vendor: %s\n",
> + eglQueryString(d->egl.display, EGL_VENDOR));
> + SPICE_DEBUG("EGL extensions: %s\n",
> + eglQueryString(d->egl.display, EGL_EXTENSIONS));
> +
> + api = EGL_OPENGL_ES_API;
> + b = eglBindAPI(api);
> + if (!b) {
> + g_critical("cannot bind OpenGLES API");
> + return false;
> + }
> +
> + b = eglChooseConfig(d->egl.display, conf_att, &d->egl.conf,
> + 1, &n);
> +
> + if (!b || n != 1) {
> + g_critical("cannot find suitable EGL config");
> + return false;
> + }
> +
> + d->egl.ctx = eglCreateContext(d->egl.display,
> + d->egl.conf,
> + EGL_NO_CONTEXT,
> + ctx_att);
> + if (!d->egl.ctx) {
> + g_critical("cannot create EGL context");
> + return false;
> + }
> +
> + eglMakeCurrent(d->egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE,
> + d->egl.ctx);
> +
> + return spice_egl_init_shaders(display, err);
> +}
> +
> +static gboolean spice_widget_init_egl_win(SpiceDisplay *display, GError **err)
> +{
> + GtkWidget *widget = GTK_WIDGET(display);
> + SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
> + GdkWindow *gdk_win;
> + EGLNativeWindowType native = 0;
> + EGLBoolean b;
> +
> + if (d->egl.surface)
> + return true;
> +
> + gdk_win = gtk_widget_get_window(widget);
> + if (!gdk_win)
> + return false;
> +
> +#ifdef GDK_WINDOWING_X11
> + if (GDK_IS_X11_WINDOW(gdk_win)) {
> + native = (EGLNativeWindowType)gdk_x11_window_get_xid(gdk_win);
> + }
> +#endif
> +
> + if (!native) {
> + g_set_error_literal(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
> + "this platform isn't supported");
> + return false;
> + }
> +
> + d->egl.surface = eglCreateWindowSurface(d->egl.display,
> + d->egl.conf,
> + native, NULL);
> +
> + if (!d->egl.surface) {
> + g_set_error_literal(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
> + "failed to init egl surface");
> + return false;
> + }
> +
> + b = eglMakeCurrent(d->egl.display,
> + d->egl.surface,
> + d->egl.surface,
> + d->egl.ctx);
> + if (!b) {
> + g_set_error_literal(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
> + "failed to activate context");
> + return false;
> + }
> +
> + return true;
> +}
> +
> +#if GTK_CHECK_VERSION (2, 91, 0)
> +static inline void gdk_drawable_get_size(GdkWindow *w, gint *ww, gint *wh)
> +{
> + *ww = gdk_window_get_width(w);
> + *wh = gdk_window_get_height(w);
> +}
> +#endif
> +
> +gboolean spice_egl_realize_display(SpiceDisplay *display, GError **err)
> +{
> + SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
> + int ww, wh;
> +
> + SPICE_DEBUG("egl realize");
> + if (!spice_widget_init_egl_win(display, err))
> + return false;
> +
> + glGenTextures(1, &d->egl.tex_id);
> + glGenTextures(1, &d->egl.tex_pointer_id);
> +
> + gdk_drawable_get_size(gtk_widget_get_window(GTK_WIDGET(display)), &ww, &wh);
> + spice_egl_resize_display(display, ww, wh);
> +
> + return true;
> +}
> +
> +void spice_egl_unrealize_display(SpiceDisplay *display)
> +{
> + SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
> +
> + SPICE_DEBUG("egl unrealize %p", d->egl.surface);
> +
> + glDeleteTextures(1, &d->egl.tex_id);
> + glDeleteTextures(1, &d->egl.tex_pointer_id);
> +
> + eglMakeCurrent(d->egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE,
> + EGL_NO_CONTEXT);
> +
> + if (d->egl.surface != EGL_NO_SURFACE) {
> + eglDestroySurface(d->egl.display, d->egl.surface);
> + d->egl.surface = EGL_NO_SURFACE;
> + }
> +}
> +
> +void spice_egl_resize_display(SpiceDisplay *display, int w, int h)
> +{
> + SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
> +
> + if (d->egl.surface != EGL_NO_SURFACE) {
> + apply_ortho(d->egl.mproj, 0, w, 0, h, -1, 1);
> + glViewport(0, 0, w, h);
> +
> + if (d->egl.image) {
> + spice_egl_update_display(display);
> + }
> + }
> +}
> +
> +static void
> +draw_rect_from_arrays(SpiceDisplay *display, const void *verts, const void *tex)
> +{
> + SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
> +
> + glBindBuffer(GL_ARRAY_BUFFER, d->egl.vbuf_id);
> +
> + if (verts) {
> + glBufferSubData(GL_ARRAY_BUFFER,
> + 0,
> + sizeof(GLfloat) * 4 * 4,
> + verts);
> + glVertexAttribPointer(d->egl.attr_pos, 4, GL_FLOAT,
> + GL_FALSE, 0, 0);
> + glEnableVertexAttribArray(d->egl.attr_pos);
> + }
> + if (tex) {
> + glBufferSubData(GL_ARRAY_BUFFER,
> + sizeof(GLfloat) * 4 * 4,
> + sizeof(GLfloat) * 4 * 2,
> + tex);
> + glVertexAttribPointer(d->egl.attr_tex, 2, GL_FLOAT,
> + GL_FALSE, 0,
> + (void *)(sizeof(GLfloat) * 4 * 4));
> + glEnableVertexAttribArray(d->egl.attr_tex);
> + }
> +
> + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
> + if (verts)
> + glDisableVertexAttribArray(d->egl.attr_pos);
> + if (tex)
> + glDisableVertexAttribArray(d->egl.attr_tex);
> +
> + glBindBuffer(GL_ARRAY_BUFFER, 0);
> +}
> +
> +static GLvoid
> +client_draw_rect_tex(SpiceDisplay *display,
> + float x, float y, float w, float h,
> + float tx, float ty, float tw, float th)
> +{
> + float verts[4][4];
> + float tex[4][2];
> +
> + verts[0][0] = x;
> + verts[0][1] = y;
> + verts[0][2] = 0.0;
> + verts[0][3] = 1.0;
> + tex[0][0] = tx;
> + tex[0][1] = ty;
> + verts[1][0] = x + w;
> + verts[1][1] = y;
> + verts[1][2] = 0.0;
> + verts[1][3] = 1.0;
> + tex[1][0] = tx + tw;
> + tex[1][1] = ty;
> + verts[2][0] = x;
> + verts[2][1] = y + h;
> + verts[2][2] = 0.0;
> + verts[2][3] = 1.0;
> + tex[2][0] = tx;
> + tex[2][1] = ty + th;
> + verts[3][0] = x + w;
> + verts[3][1] = y + h;
> + verts[3][2] = 0.0;
> + verts[3][3] = 1.0;
> + tex[3][0] = tx + tw;
> + tex[3][1] = ty + th;
> +
> + draw_rect_from_arrays(display, verts, tex);
> +}
> +
> +G_GNUC_INTERNAL
> +void spice_egl_cursor_set(SpiceDisplay *display)
> +{
> + SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
> + GdkPixbuf *image = d->mouse_pixbuf;
> +
> + if (image == NULL)
> + return;
> +
> + int width = gdk_pixbuf_get_width(image);
> + int height = gdk_pixbuf_get_height(image);
> +
> + glBindTexture(GL_TEXTURE_2D, d->egl.tex_pointer_id);
> + glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
> + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
> + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
> + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
> + width, height, 0,
> + GL_RGBA, GL_UNSIGNED_BYTE,
> + gdk_pixbuf_read_pixels(image));
> +}
> +
> +G_GNUC_INTERNAL
> +void spice_egl_update_display(SpiceDisplay *display)
> +{
> + SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
> + double s;
> + int x, y, w, h;
> + gdouble tx, ty, tw, th;
> +
> + g_return_if_fail(d->egl.image != NULL);
> + g_return_if_fail(d->egl.surface != NULL);
> +
> + spice_display_get_scaling(display, &s, &x, &y, &w, &h);
> +
> + glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
> + glClear(GL_COLOR_BUFFER_BIT);
> +
> + tx = ((float)d->area.x / (float)d->egl.scanout.width);
> + ty = ((float)d->area.y / (float)d->egl.scanout.height);
> + tw = ((float)d->area.width / (float)d->egl.scanout.width);
> + th = ((float)d->area.height / (float)d->egl.scanout.height);
> + ty += 1 - th;
> + if (!d->egl.scanout.y0top) {
> + ty = 1 - ty;
> + th = -1 * th;
> + }
> + SPICE_DEBUG("update %f +%d+%d %dx%d +%f+%f %fx%f", s, x, y, w, h,
> + tx, ty, tw, th);
> +
> + glBindTexture(GL_TEXTURE_2D, d->egl.tex_id);
> + glDisable(GL_BLEND);
> + client_draw_rect_tex(display, x, y, w, h,
> + tx, ty, tw, th);
> +
> + if (d->mouse_mode == SPICE_MOUSE_MODE_SERVER &&
> + d->mouse_guest_x != -1 && d->mouse_guest_y != -1 &&
> + !d->show_cursor &&
> + spice_gtk_session_get_pointer_grabbed(d->gtk_session) &&
> + d->mouse_pixbuf) {
> + GdkPixbuf *image = d->mouse_pixbuf;
> + int width = gdk_pixbuf_get_width(image);
> + int height = gdk_pixbuf_get_height(image);
> +
> + glBindTexture(GL_TEXTURE_2D, d->egl.tex_pointer_id);
> + glEnable(GL_BLEND);
> + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
> + client_draw_rect_tex(display,
> + x + (d->mouse_guest_x - d->mouse_hotspot.x) * s,
> + y + h - (d->mouse_guest_y - d->mouse_hotspot.y) * s,
> + width, -height,
> + 0, 0, 1, 1);
> + }
> +
> + eglSwapBuffers(d->egl.display, d->egl.surface);
> +}
> +
> +
> +G_GNUC_INTERNAL
> +gboolean spice_egl_update_scanout(SpiceDisplay *display,
> + const SpiceGlScanout *scanout,
> + GError **err)
> +{
> + SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
> + EGLint attrs[13];
> + guint32 format;
> +
> + g_return_val_if_fail(scanout != NULL, false);
> + format = scanout->format;
> +
> + if (scanout->fd == -1)
> + return false;
> +
> + if (d->egl.image != NULL) {
> + eglDestroyImageKHR(d->egl.display, d->egl.image);
> + d->egl.image = NULL;
> + }
> +
> + attrs[0] = EGL_DMA_BUF_PLANE0_FD_EXT;
> + attrs[1] = scanout->fd;
> + attrs[2] = EGL_DMA_BUF_PLANE0_PITCH_EXT;
> + attrs[3] = scanout->stride;
> + attrs[4] = EGL_DMA_BUF_PLANE0_OFFSET_EXT;
> + attrs[5] = 0;
> + attrs[6] = EGL_WIDTH;
> + attrs[7] = scanout->width;
> + attrs[8] = EGL_HEIGHT;
> + attrs[9] = scanout->height;
> + attrs[10] = EGL_LINUX_DRM_FOURCC_EXT;
> + attrs[11] = format;
> + attrs[12] = EGL_NONE;
> + SPICE_DEBUG("fd:%d stride:%d y0:%d %dx%d format:0x%x (%c%c%c%c)",
> + scanout->fd, scanout->stride, scanout->y0top,
> + scanout->width, scanout->height, format,
> + format & 0xff, (format >> 8) & 0xff, (format >> 16) & 0xff, format >> 24);
> +
> + d->egl.image = eglCreateImageKHR(d->egl.display,
> + EGL_NO_CONTEXT,
> + EGL_LINUX_DMA_BUF_EXT,
> + (EGLClientBuffer)NULL,
> + attrs);
> +
> + glBindTexture(GL_TEXTURE_2D, d->egl.tex_id);
> + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
> + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
> + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)d->egl.image);
> + d->egl.scanout = *scanout;
> +
> + return true;
> +}
> diff --git a/src/spice-widget-priv.h b/src/spice-widget-priv.h
> index 682e889..39936c3 100644
> --- a/src/spice-widget-priv.h
> +++ b/src/spice-widget-priv.h
> @@ -30,6 +30,10 @@
> #include <windows.h>
> #endif
>
> +#ifdef USE_EPOXY
> +#include <epoxy/egl.h>
> +#endif
> +
> #include "spice-widget.h"
> #include "spice-common.h"
> #include "spice-gtk-session.h"
> @@ -124,6 +128,23 @@ struct _SpiceDisplayPrivate {
> int x11_accel_denominator;
> int x11_threshold;
> #endif
> +#ifdef USE_EPOXY
> + struct {
> + gboolean enabled;
> + EGLSurface surface;
> + EGLDisplay display;
> + EGLConfig conf;
> + EGLContext ctx;
> + guint32 tex_loc;
> + guint32 mproj;
> + guint32 attr_pos, attr_tex;
> + guint32 vbuf_id;
> + guint tex_id;
> + guint tex_pointer_id;
> + EGLImageKHR image;
> + SpiceGlScanout scanout;
> + } egl;
> +#endif
> };
>
> int spicex_image_create (SpiceDisplay *display);
> @@ -135,6 +156,15 @@ void spicex_expose_event (SpiceDisplay *display, GdkEventExp
> #endif
> gboolean spicex_is_scaled (SpiceDisplay *display);
> void spice_display_get_scaling (SpiceDisplay *display, double *s, int *x, int *y, int *w, int *h);
> +gboolean spice_egl_init (SpiceDisplay *display, GError **err);
> +gboolean spice_egl_realize_display (SpiceDisplay *display, GError **err);
> +void spice_egl_unrealize_display (SpiceDisplay *display);
> +void spice_egl_update_display (SpiceDisplay *display);
> +void spice_egl_resize_display (SpiceDisplay *display, int w, int h);
> +gboolean spice_egl_update_scanout (SpiceDisplay *display,
> + const SpiceGlScanout *scanout,
> + GError **err);
> +void spice_egl_cursor_set (SpiceDisplay *display);
>
> G_END_DECLS
>
> diff --git a/src/spice-widget.c b/src/spice-widget.c
> index 77f663f..dc014b7 100644
> --- a/src/spice-widget.c
> +++ b/src/spice-widget.c
> @@ -552,6 +552,7 @@ static void spice_display_init(SpiceDisplay *display)
> GtkWidget *widget = GTK_WIDGET(display);
> SpiceDisplayPrivate *d;
> GtkTargetEntry targets = { "text/uri-list", 0, 0 };
> + G_GNUC_UNUSED GError *err = NULL;
>
> d = display->priv = SPICE_DISPLAY_GET_PRIVATE(display);
>
> @@ -583,6 +584,13 @@ static void spice_display_init(SpiceDisplay *display)
>
> d->mouse_cursor = get_blank_cursor();
> d->have_mitshm = true;
> +
> +#ifdef USE_EPOXY
> + if (!spice_egl_init(display, &err)) {
> + g_critical("egl init failed: %s", err->message);
> + g_clear_error(&err);
> + }
> +#endif
> }
>
> static GObject *
> @@ -1132,6 +1140,20 @@ static gboolean do_color_convert(SpiceDisplay *display, GdkRectangle *r)
> return true;
> }
>
> +static void set_egl_enabled(SpiceDisplay *display, bool enabled)
> +{
> +#ifdef USE_EPOXY
> + SpiceDisplayPrivate *d = display->priv;
> +
> + if (d->egl.enabled != enabled) {
> + d->egl.enabled = enabled;
> + /* even though the function is marked as deprecated, it's the
> + * only way I found to prevent glitches when the window is
> + * resized. */
> + gtk_widget_set_double_buffered(GTK_WIDGET(display), !enabled);
> + }
> +#endif
> +}
>
> #if GTK_CHECK_VERSION (2, 91, 0)
> static gboolean draw_event(GtkWidget *widget, cairo_t *cr)
> @@ -1140,6 +1162,13 @@ static gboolean draw_event(GtkWidget *widget, cairo_t *cr)
> SpiceDisplayPrivate *d = display->priv;
> g_return_val_if_fail(d != NULL, false);
>
> +#ifdef USE_EPOXY
> + if (d->egl.enabled) {
> + spice_egl_update_display(display);
> + return false;
> + }
> +#endif
> +
> if (d->mark == 0 || d->data == NULL ||
> d->area.width == 0 || d->area.height == 0)
> return false;
> @@ -1760,6 +1789,9 @@ static void size_allocate(GtkWidget *widget, GtkAllocation *conf, gpointer data)
> d->ww = conf->width;
> d->wh = conf->height;
> recalc_geometry(widget);
> +#ifdef USE_EPOXY
> + spice_egl_resize_display(display, conf->width, conf->height);
> +#endif
> }
>
> d->mx = conf->x;
> @@ -1786,18 +1818,29 @@ static void realize(GtkWidget *widget)
> {
> SpiceDisplay *display = SPICE_DISPLAY(widget);
> SpiceDisplayPrivate *d = display->priv;
> + G_GNUC_UNUSED GError *err = NULL;
>
> GTK_WIDGET_CLASS(spice_display_parent_class)->realize(widget);
>
> d->keycode_map =
> vnc_display_keymap_gdk2xtkbd_table(gtk_widget_get_window(widget),
> &d->keycode_maplen);
> +
> +#ifdef USE_EPOXY
> + if (!spice_egl_realize_display(display, &err)) {
> + g_critical("egl realize failed: %s", err->message);
> + g_clear_error(&err);
> + }
> +#endif
> update_image(display);
> }
>
> static void unrealize(GtkWidget *widget)
> {
> spicex_image_destroy(SPICE_DISPLAY(widget));
> +#ifdef USE_EPOXY
> + spice_egl_unrealize_display(SPICE_DISPLAY(widget));
> +#endif
>
> GTK_WIDGET_CLASS(spice_display_parent_class)->unrealize(widget);
> }
> @@ -2206,6 +2249,8 @@ static void invalidate(SpiceChannel *channel,
> .height = h
> };
>
> + set_egl_enabled(display, false);
> +
> if (!gtk_widget_get_window(GTK_WIDGET(display)))
> return;
>
> @@ -2269,6 +2314,9 @@ static void cursor_set(SpiceCursorChannel *channel,
> } else
> g_warn_if_reached();
>
> +#ifdef USE_EPOXY
> + spice_egl_cursor_set(display);
> +#endif
> if (d->show_cursor) {
> /* unhide */
> gdk_cursor_unref(d->show_cursor);
> @@ -2416,6 +2464,38 @@ static void cursor_reset(SpiceCursorChannel *channel, gpointer data)
> gdk_window_set_cursor(window, NULL);
> }
>
> +#ifdef USE_EPOXY
> +static void gl_scanout(SpiceDisplay *display)
> +{
> + SpiceDisplayPrivate *d = display->priv;
> + const SpiceGlScanout *scanout;
> + GError *err = NULL;
> +
> + scanout = spice_display_get_gl_scanout(SPICE_DISPLAY_CHANNEL(d->display));
> + g_return_if_fail(scanout != NULL);
> +
> + SPICE_DEBUG("%s: got scanout", __FUNCTION__);
> + set_egl_enabled(display, true);
> +
> + if (!spice_egl_update_scanout(display, scanout, &err)) {
> + g_critical("update scanout failed: %s", err->message);
> + g_clear_error(&err);
> + }
> +}
> +
> +static void gl_draw(SpiceDisplay *display,
> + guint32 x, guint32 y, guint32 w, guint32 h)
> +{
> + SpiceDisplayPrivate *d = display->priv;
> +
> + SPICE_DEBUG("%s", __FUNCTION__);
> + set_egl_enabled(display, true);
> +
> + spice_egl_update_display(display);
> + spice_display_gl_draw_done(SPICE_DISPLAY_CHANNEL(d->display));
> +}
> +#endif
> +
> static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
> {
> SpiceDisplay *display = data;
> @@ -2451,6 +2531,13 @@ static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
> primary.stride, primary.shmid, primary.data, display);
> mark(display, primary.marked);
> }
> +#ifdef USE_EPOXY
> + spice_g_signal_connect_object(channel, "notify::gl-scanout",
> + G_CALLBACK(gl_scanout), display, G_CONNECT_SWAPPED);
> + spice_g_signal_connect_object(channel, "gl-draw",
> + G_CALLBACK(gl_draw), display, G_CONNECT_SWAPPED);
> +#endif
> +
> spice_channel_connect(channel);
> return;
> }
> @@ -2634,34 +2721,57 @@ GdkPixbuf *spice_display_get_pixbuf(SpiceDisplay *display)
> {
> SpiceDisplayPrivate *d;
> GdkPixbuf *pixbuf;
> - int x, y;
> - guchar *src, *data, *dest;
> + guchar *data;
>
> g_return_val_if_fail(SPICE_IS_DISPLAY(display), NULL);
>
> d = display->priv;
>
> g_return_val_if_fail(d != NULL, NULL);
> - /* TODO: ensure d->data has been exposed? */
> - g_return_val_if_fail(d->data != NULL, NULL);
> -
> - data = g_malloc0(d->area.width * d->area.height * 3);
> - src = d->data;
> - dest = data;
> -
> - src += d->area.y * d->stride + d->area.x * 4;
> - for (y = 0; y < d->area.height; ++y) {
> - for (x = 0; x < d->area.width; ++x) {
> - dest[0] = src[x * 4 + 2];
> - dest[1] = src[x * 4 + 1];
> - dest[2] = src[x * 4 + 0];
> - dest += 3;
> + g_return_val_if_fail(d->display != NULL, NULL);
> +
> +#ifdef USE_EPOXY
> + if (d->egl.enabled) {
> + GdkPixbuf *tmp;
> +
> + data = g_malloc0(d->area.width * d->area.height * 4);
> + glReadBuffer(GL_FRONT);
> + glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
> + glReadPixels(0, 0, d->area.width, d->area.height,
> + GL_RGBA, GL_UNSIGNED_BYTE, data);
> + tmp = gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB, true,
> + 8, d->area.width, d->area.height,
> + d->area.width * 4,
> + (GdkPixbufDestroyNotify)g_free, NULL);
> + pixbuf = gdk_pixbuf_flip(tmp, false);
> + g_object_unref(tmp);
> + } else
> +#endif
> + {
> + guchar *src, *dest;
> + int x, y;
> +
> + /* TODO: ensure d->data has been exposed? */
> + g_return_val_if_fail(d->data != NULL, NULL);
> + data = g_malloc0(d->area.width * d->area.height * 3);
> + src = d->data;
> + dest = data;
> +
> + src += d->area.y * d->stride + d->area.x * 4;
> + for (y = 0; y < d->area.height; ++y) {
> + for (x = 0; x < d->area.width; ++x) {
> + dest[0] = src[x * 4 + 2];
> + dest[1] = src[x * 4 + 1];
> + dest[2] = src[x * 4 + 0];
> + dest += 3;
> + }
> + src += d->stride;
> }
> - src += d->stride;
> + pixbuf = gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB, false,
> + 8, d->area.width, d->area.height,
> + d->area.width * 3,
> + (GdkPixbufDestroyNotify)g_free, NULL);
> }
>
> - pixbuf = gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB, false,
> - 8, d->area.width, d->area.height, d->area.width * 3,
> - (GdkPixbufDestroyNotify)g_free, NULL);
> return pixbuf;
> }
> --
> 2.5.0
>
> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/spice-devel
--
Marc-André Lureau
More information about the Spice-devel
mailing list