Commit 97edf3bd authored by Gerd Hoffmann's avatar Gerd Hoffmann
Browse files

gtk: add opengl support, using egl



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: default avatarGerd Hoffmann <kraxel@redhat.com>
parent 7ced9e9f
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
@@ -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)
@@ -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)
@@ -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 −0
Original line number Diff line number Diff line
@@ -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)

ui/gtk-egl.c

0 → 100644
+141 −0
Original line number Diff line number Diff line
/*
 * 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;
}
+75 −15
Original line number Diff line number Diff line
@@ -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;

@@ -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)
@@ -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;
    }
@@ -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)
@@ -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));

#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);

@@ -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);
@@ -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
Loading