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@xxxxxxxxx> wrote: > From: Marc-André Lureau <mlureau@xxxxxxxxxx> > > 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@xxxxxxxxxx> > --- > 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@xxxxxxxxxxxxxxxxxxxxx > http://lists.freedesktop.org/mailman/listinfo/spice-devel -- Marc-André Lureau _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel