Skip to content

Commit

Permalink
gtk: add opengl support, using egl
Browse files Browse the repository at this point in the history
This adds opengl rendering support to the gtk ui, using egl.
It's off by default for now, use 'qemu -display gtk,gl=on'
to play with this.

Note that gtk got native opengl support with release 3.16.
There most likely will be a separate implementation for 3.16+,
using the native gtk opengl support.  This patch covers older
versions (and for the time being 3.16 too, hopefully without
rendering quirks).

Signed-off-by: Gerd Hoffmann <[email protected]>
  • Loading branch information
kraxel committed May 29, 2015
1 parent 7ced9e9 commit 97edf3b
Show file tree
Hide file tree
Showing 6 changed files with 253 additions and 17 deletions.
2 changes: 1 addition & 1 deletion include/ui/console.h
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ void curses_display_init(DisplayState *ds, int full_screen);
int index_from_key(const char *key);

/* gtk.c */
void early_gtk_display_init(void);
void early_gtk_display_init(int opengl);
void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover);

#endif
23 changes: 23 additions & 0 deletions include/ui/gtk.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
#include <X11/XKBlib.h>
#endif

#if defined(CONFIG_OPENGL)
#include "ui/egl-helpers.h"
#endif

/* Compatibility define to let us build on both Gtk2 and Gtk3 */
#if GTK_CHECK_VERSION(3, 0, 0)
static inline void gdk_drawable_get_size(GdkWindow *w, gint *ww, gint *wh)
Expand All @@ -41,6 +45,12 @@ typedef struct VirtualGfxConsole {
cairo_surface_t *surface;
double scale_x;
double scale_y;
#if defined(CONFIG_OPENGL)
ConsoleGLState *gls;
EGLContext ectx;
EGLSurface esurface;
int glupdates;
#endif
} VirtualGfxConsole;

#if defined(CONFIG_VTE)
Expand Down Expand Up @@ -73,4 +83,17 @@ typedef struct VirtualConsole {
};
} VirtualConsole;

/* ui/gtk.c */
void gd_update_windowsize(VirtualConsole *vc);

/* ui/gtk-egl.c */
void gd_egl_init(VirtualConsole *vc);
void gd_egl_draw(VirtualConsole *vc);
void gd_egl_update(DisplayChangeListener *dcl,
int x, int y, int w, int h);
void gd_egl_refresh(DisplayChangeListener *dcl);
void gd_egl_switch(DisplayChangeListener *dcl,
DisplaySurface *surface);
void gtk_egl_init(void);

#endif /* UI_GTK_H */
3 changes: 3 additions & 0 deletions ui/Makefile.objs
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,16 @@ ifeq ($(CONFIG_OPENGL),y)
common-obj-y += shader.o
common-obj-y += console-gl.o
common-obj-y += egl-helpers.o
common-obj-$(CONFIG_GTK) += gtk-egl.o
endif

gtk.o-cflags := $(GTK_CFLAGS) $(VTE_CFLAGS)
gtk-egl.o-cflags := $(GTK_CFLAGS) $(VTE_CFLAGS) $(OPENGL_CFLAGS)
shader.o-cflags += $(OPENGL_CFLAGS)
console-gl.o-cflags += $(OPENGL_CFLAGS)
egl-helpers.o-cflags += $(OPENGL_CFLAGS)

gtk-egl.o-libs += $(OPENGL_LIBS)
shader.o-libs += $(OPENGL_LIBS)
console-gl.o-libs += $(OPENGL_LIBS)
egl-helpers.o-libs += $(OPENGL_LIBS)
141 changes: 141 additions & 0 deletions ui/gtk-egl.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
* GTK UI -- egl opengl code.
*
* Note that gtk 3.16+ (released 2015-03-23) has a GtkGLArea widget,
* which is GtkDrawingArea like widget with opengl rendering support.
*
* This code handles opengl support on older gtk versions, using egl
* to get a opengl context for the X11 window.
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/

#include "qemu-common.h"

#include "trace.h"

#include "ui/console.h"
#include "ui/gtk.h"
#include "ui/egl-helpers.h"

#include "sysemu/sysemu.h"

/** DisplayState Callbacks (opengl version) **/

void gd_egl_init(VirtualConsole *vc)
{
GdkWindow *gdk_window = gtk_widget_get_window(vc->gfx.drawing_area);
if (!gdk_window) {
return;
}

#if GTK_CHECK_VERSION(3, 0, 0)
Window x11_window = gdk_x11_window_get_xid(gdk_window);
#else
Window x11_window = gdk_x11_drawable_get_xid(gdk_window);
#endif
if (!x11_window) {
return;
}

vc->gfx.ectx = qemu_egl_init_ctx();
vc->gfx.esurface = qemu_egl_init_surface_x11(vc->gfx.ectx, x11_window);

assert(vc->gfx.esurface);
}

void gd_egl_draw(VirtualConsole *vc)
{
GdkWindow *window;
int ww, wh;

if (!vc->gfx.gls || !vc->gfx.ds) {
return;
}

eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
vc->gfx.esurface, vc->gfx.ectx);

window = gtk_widget_get_window(vc->gfx.drawing_area);
gdk_drawable_get_size(window, &ww, &wh);
surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, ww, wh);
surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds);

eglSwapBuffers(qemu_egl_display, vc->gfx.esurface);
}

void gd_egl_update(DisplayChangeListener *dcl,
int x, int y, int w, int h)
{
VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);

if (!vc->gfx.gls || !vc->gfx.ds) {
return;
}

eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
vc->gfx.esurface, vc->gfx.ectx);
surface_gl_update_texture(vc->gfx.gls, vc->gfx.ds, x, y, w, h);
vc->gfx.glupdates++;
}

void gd_egl_refresh(DisplayChangeListener *dcl)
{
VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);

if (!vc->gfx.esurface) {
gd_egl_init(vc);
if (!vc->gfx.esurface) {
return;
}
vc->gfx.gls = console_gl_init_context();
if (vc->gfx.ds) {
surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds);
}
}

graphic_hw_update(dcl->con);

if (vc->gfx.glupdates) {
vc->gfx.glupdates = 0;
gd_egl_draw(vc);
}
}

void gd_egl_switch(DisplayChangeListener *dcl,
DisplaySurface *surface)
{
VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
bool resized = true;

trace_gd_switch(vc->label, surface_width(surface), surface_height(surface));

if (vc->gfx.ds &&
surface_width(vc->gfx.ds) == surface_width(surface) &&
surface_height(vc->gfx.ds) == surface_height(surface)) {
resized = false;
}

surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds);
vc->gfx.ds = surface;
if (vc->gfx.gls) {
surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds);
}

if (resized) {
gd_update_windowsize(vc);
}
}

void gtk_egl_init(void)
{
GdkDisplay *gdk_display = gdk_display_get_default();
Display *x11_display = gdk_x11_display_get_xdisplay(gdk_display);

if (qemu_egl_init_dpy(x11_display, false, false) < 0) {
return;
}

display_opengl = 1;
}
90 changes: 75 additions & 15 deletions ui/gtk.c
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ static void gd_update_geometry_hints(VirtualConsole *vc)
gtk_window_set_geometry_hints(geo_window, geo_widget, &geo, mask);
}

static void gd_update_windowsize(VirtualConsole *vc)
void gd_update_windowsize(VirtualConsole *vc)
{
GtkDisplayState *s = vc->s;

Expand Down Expand Up @@ -581,6 +581,33 @@ static void gd_switch(DisplayChangeListener *dcl,
}
}

static const DisplayChangeListenerOps dcl_ops = {
.dpy_name = "gtk",
.dpy_gfx_update = gd_update,
.dpy_gfx_switch = gd_switch,
.dpy_gfx_check_format = qemu_pixman_check_format,
.dpy_refresh = gd_refresh,
.dpy_mouse_set = gd_mouse_set,
.dpy_cursor_define = gd_cursor_define,
};


#if defined(CONFIG_OPENGL)

/** DisplayState Callbacks (opengl version) **/

static const DisplayChangeListenerOps dcl_egl_ops = {
.dpy_name = "gtk-egl",
.dpy_gfx_update = gd_egl_update,
.dpy_gfx_switch = gd_egl_switch,
.dpy_gfx_check_format = console_gl_check_format,
.dpy_refresh = gd_egl_refresh,
.dpy_mouse_set = gd_mouse_set,
.dpy_cursor_define = gd_cursor_define,
};

#endif

/** QEMU Events **/

static void gd_change_runstate(void *opaque, int running, RunState state)
Expand Down Expand Up @@ -637,6 +664,13 @@ static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
int ww, wh;
int fbw, fbh;

#if defined(CONFIG_OPENGL)
if (vc->gfx.gls) {
gd_egl_draw(vc);
return TRUE;
}
#endif

if (!gtk_widget_get_realized(widget)) {
return FALSE;
}
Expand Down Expand Up @@ -1676,16 +1710,6 @@ static GtkWidget *gd_create_menu_machine(GtkDisplayState *s)
return machine_menu;
}

static const DisplayChangeListenerOps dcl_ops = {
.dpy_name = "gtk",
.dpy_gfx_update = gd_update,
.dpy_gfx_switch = gd_switch,
.dpy_gfx_check_format = qemu_pixman_check_format,
.dpy_refresh = gd_refresh,
.dpy_mouse_set = gd_mouse_set,
.dpy_cursor_define = gd_cursor_define,
};

static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
QemuConsole *con, int idx,
GSList *group, GtkWidget *view_menu)
Expand Down Expand Up @@ -1713,7 +1737,29 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook),
vc->tab_item, gtk_label_new(vc->label));

vc->gfx.dcl.ops = &dcl_ops;
#if defined(CONFIG_OPENGL)
if (display_opengl) {
/*
* gtk_widget_set_double_buffered() was deprecated in 3.14.
* It is required for opengl rendering on X11 though. A
* proper replacement (native opengl support) is only
* available in 3.16+. Silence the warning if possible.
*/
#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
gtk_widget_set_double_buffered(vc->gfx.drawing_area, FALSE);
#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
#pragma GCC diagnostic pop
#endif
vc->gfx.dcl.ops = &dcl_egl_ops;
} else
#endif
{
vc->gfx.dcl.ops = &dcl_ops;
}

vc->gfx.dcl.con = con;
register_displaychangelistener(&vc->gfx.dcl);

Expand Down Expand Up @@ -1876,8 +1922,6 @@ void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover)
GtkDisplayState *s = g_malloc0(sizeof(*s));
char *filename;

gtk_init(NULL, NULL);

s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
#if GTK_CHECK_VERSION(3, 2, 0)
s->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
Expand Down Expand Up @@ -1954,8 +1998,24 @@ void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover)
gd_set_keycode_type(s);
}

void early_gtk_display_init(void)
void early_gtk_display_init(int opengl)
{
gtk_init(NULL, NULL);

switch (opengl) {
case -1: /* default */
case 0: /* off */
break;
case 1: /* on */
#if defined(CONFIG_OPENGL)
gtk_egl_init();
#endif
break;
default:
g_assert_not_reached();
break;
}

#if defined(CONFIG_VTE)
register_vc_handler(gd_vc_handler);
#endif
Expand Down
11 changes: 10 additions & 1 deletion vl.c
Original file line number Diff line number Diff line change
Expand Up @@ -2047,6 +2047,15 @@ static DisplayType select_display(const char *p)
} else {
goto invalid_gtk_args;
}
} else if (strstart(opts, ",gl=", &nextopt)) {
opts = nextopt;
if (strstart(opts, "on", &nextopt)) {
request_opengl = 1;
} else if (strstart(opts, "off", &nextopt)) {
request_opengl = 0;
} else {
goto invalid_gtk_args;
}
} else {
invalid_gtk_args:
fprintf(stderr, "Invalid GTK option string: %s\n", p);
Expand Down Expand Up @@ -4012,7 +4021,7 @@ int main(int argc, char **argv, char **envp)

#if defined(CONFIG_GTK)
if (display_type == DT_GTK) {
early_gtk_display_init();
early_gtk_display_init(request_opengl);
}
#endif
#if defined(CONFIG_SDL)
Expand Down

0 comments on commit 97edf3b

Please sign in to comment.