qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH 9/9] gtk/opengl: add opengl context and scanout


From: Marc-André Lureau
Subject: Re: [Qemu-devel] [PATCH 9/9] gtk/opengl: add opengl context and scanout support (GtkGLArea)
Date: Fri, 11 Sep 2015 16:44:25 +0200

 Hi

On Wed, Sep 9, 2015 at 1:20 PM, Gerd Hoffmann <address@hidden> wrote:
> This allows virtio-gpu to render in 3d mode.
> Uses native opengl support which is present
> in gtk versions 3.16 and newer.
>
> Signed-off-by: Gerd Hoffmann <address@hidden>

After this patch, when starting qemu I get:

(qemu-system-x86_64:28264): Gtk-CRITICAL **: gtk_gl_area_make_current:
assertion 'gtk_widget_get_realized (widget)' failed
qemu-system-x86_64: Couldn't find current GLX or EGL context.



> ---
>  configure        |   8 ++
>  include/ui/gtk.h |  23 ++++++
>  ui/Makefile.objs |   5 ++
>  ui/gtk-gl-area.c | 219 
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  ui/gtk.c         | 111 +++++++++++++++++++++-------
>  5 files changed, 341 insertions(+), 25 deletions(-)
>  create mode 100644 ui/gtk-gl-area.c
>
> diff --git a/configure b/configure
> index 2d922d1..f7692d2 100755
> --- a/configure
> +++ b/configure
> @@ -329,6 +329,7 @@ glusterfs_zerofill="no"
>  archipelago="no"
>  gtk=""
>  gtkabi=""
> +gtk_gl="no"
>  gnutls=""
>  gnutls_hash=""
>  vte=""
> @@ -3182,6 +3183,9 @@ if test "$opengl" != "no" ; then
>      opengl_cflags="$($pkg_config --cflags $opengl_pkgs) $x11_cflags"
>      opengl_libs="$($pkg_config --libs $opengl_pkgs) $x11_libs"
>      opengl=yes
> +    if test "$gtk" = "yes" && $pkg_config --exists "$gtkpackage >= 3.16"; 
> then
> +        gtk_gl="yes"
> +    fi
>    else
>      if test "$opengl" = "yes" ; then
>        feature_not_found "opengl" "Please install opengl (mesa) devel pkgs: 
> $opengl_pkgs"
> @@ -4541,6 +4545,7 @@ fi
>  echo "pixman            $pixman"
>  echo "SDL support       $sdl"
>  echo "GTK support       $gtk"
> +echo "GTK GL support    $gtk_gl"
>  echo "GNUTLS support    $gnutls"
>  echo "GNUTLS hash       $gnutls_hash"
>  echo "GNUTLS gcrypt     $gnutls_gcrypt"
> @@ -4902,6 +4907,9 @@ if test "$gtk" = "yes" ; then
>    echo "CONFIG_GTK=y" >> $config_host_mak
>    echo "CONFIG_GTKABI=$gtkabi" >> $config_host_mak
>    echo "GTK_CFLAGS=$gtk_cflags" >> $config_host_mak
> +  if test "$gtk_gl" = "yes" ; then
> +    echo "CONFIG_GTK_GL=y" >> $config_host_mak
> +  fi
>  fi
>  if test "$gnutls" = "yes" ; then
>    echo "CONFIG_GNUTLS=y" >> $config_host_mak
> diff --git a/include/ui/gtk.h b/include/ui/gtk.h
> index 1e04c0c..76aabd0 100644
> --- a/include/ui/gtk.h
> +++ b/include/ui/gtk.h
> @@ -112,4 +112,27 @@ void gtk_egl_init(void);
>  int gd_egl_make_current(DisplayChangeListener *dcl,
>                          qemu_gl_context ctx);
>
> +/* ui/gtk-gl-area.c */
> +void gd_gl_area_init(VirtualConsole *vc);
> +void gd_gl_area_draw(VirtualConsole *vc);
> +void gd_gl_area_update(DisplayChangeListener *dcl,
> +                       int x, int y, int w, int h);
> +void gd_gl_area_refresh(DisplayChangeListener *dcl);
> +void gd_gl_area_switch(DisplayChangeListener *dcl,
> +                       DisplaySurface *surface);
> +qemu_gl_context gd_gl_area_create_context(DisplayChangeListener *dcl,
> +                                          struct qemu_gl_params *params);
> +void gd_gl_area_destroy_context(DisplayChangeListener *dcl,
> +                                qemu_gl_context ctx);
> +void gd_gl_area_scanout(DisplayChangeListener *dcl,
> +                        uint32_t backing_id, bool backing_y_0_top,
> +                        uint32_t x, uint32_t y,
> +                        uint32_t w, uint32_t h);
> +void gd_gl_area_scanout_flush(DisplayChangeListener *dcl,
> +                              uint32_t x, uint32_t y, uint32_t w, uint32_t 
> h);
> +void gtk_gl_area_init(void);
> +qemu_gl_context gd_gl_area_get_current_context(DisplayChangeListener *dcl);
> +int gd_gl_area_make_current(DisplayChangeListener *dcl,
> +                            qemu_gl_context ctx);
> +
>  #endif /* UI_GTK_H */
> diff --git a/ui/Makefile.objs b/ui/Makefile.objs
> index 9bc64a0..bc79bdb 100644
> --- a/ui/Makefile.objs
> +++ b/ui/Makefile.objs
> @@ -32,11 +32,16 @@ common-obj-y += shader.o
>  common-obj-y += console-gl.o
>  common-obj-y += egl-helpers.o
>  common-obj-y += egl-context.o
> +ifeq ($(CONFIG_GTK_GL),y)
> +common-obj-$(CONFIG_GTK) += gtk-gl-area.o
> +else
>  common-obj-$(CONFIG_GTK) += gtk-egl.o
>  endif
> +endif
>
>  gtk.o-cflags := $(GTK_CFLAGS) $(VTE_CFLAGS)
>  gtk-egl.o-cflags := $(GTK_CFLAGS) $(VTE_CFLAGS) $(OPENGL_CFLAGS)
> +gtk-gl-area.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)
> diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c
> new file mode 100644
> index 0000000..8973c36
> --- /dev/null
> +++ b/ui/gtk-gl-area.c
> @@ -0,0 +1,219 @@
> +/*
> + * GTK UI -- glarea opengl code.
> + *
> + * Requires 3.16+ (GtkGLArea widget).
> + *
> + * 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"
> +
> +static void gtk_gl_area_set_scanout_mode(VirtualConsole *vc, bool scanout)
> +{
> +    if (vc->gfx.scanout_mode == scanout) {
> +        return;
> +    }
> +
> +    vc->gfx.scanout_mode = scanout;
> +    if (!vc->gfx.scanout_mode) {
> +        if (vc->gfx.fbo_id) {
> +            glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
> +                                      GL_COLOR_ATTACHMENT0_EXT,
> +                                      GL_TEXTURE_2D, 0, 0);
> +            glDeleteFramebuffers(1, &vc->gfx.fbo_id);
> +            vc->gfx.fbo_id = 0;
> +        }
> +        if (vc->gfx.surface) {
> +            surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds);
> +            surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds);
> +        }
> +    }
> +}
> +
> +/** DisplayState Callbacks (opengl version) **/
> +
> +void gd_gl_area_draw(VirtualConsole *vc)
> +{
> +    int ww, wh, y1, y2;
> +
> +    if (!vc->gfx.gls) {
> +        return;
> +    }
> +
> +    gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
> +    ww = gtk_widget_get_allocated_width(vc->gfx.drawing_area);
> +    wh = gtk_widget_get_allocated_height(vc->gfx.drawing_area);
> +
> +    if (vc->gfx.scanout_mode) {
> +        if (!vc->gfx.fbo_id) {
> +            return;
> +        }
> +
> +        glBindFramebuffer(GL_READ_FRAMEBUFFER, vc->gfx.fbo_id);
> +        /* GtkGLArea sets GL_DRAW_FRAMEBUFFER for us */
> +
> +        glViewport(0, 0, ww, wh);
> +        y1 = vc->gfx.y0_top ? 0 : vc->gfx.h;
> +        y2 = vc->gfx.y0_top ? vc->gfx.h : 0;
> +        glBlitFramebuffer(0, y1, vc->gfx.w, y2,
> +                          0, 0, ww, wh,
> +                          GL_COLOR_BUFFER_BIT, GL_NEAREST);
> +    } else {
> +        if (!vc->gfx.ds) {
> +            return;
> +        }
> +        gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
> +
> +        surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, ww, wh);
> +        surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds);
> +    }
> +}
> +
> +void gd_gl_area_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;
> +    }
> +
> +    gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
> +    surface_gl_update_texture(vc->gfx.gls, vc->gfx.ds, x, y, w, h);
> +    vc->gfx.glupdates++;
> +}
> +
> +void gd_gl_area_refresh(DisplayChangeListener *dcl)
> +{
> +    VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
> +
> +    if (!vc->gfx.gls) {
> +        gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
> +        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;
> +        gtk_gl_area_set_scanout_mode(vc, false);
> +        gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area));
> +    }
> +}
> +
> +void gd_gl_area_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;
> +    }
> +
> +    if (vc->gfx.gls) {
> +        gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
> +        surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds);
> +        surface_gl_create_texture(vc->gfx.gls, surface);
> +    }
> +    vc->gfx.ds = surface;
> +
> +    if (resized) {
> +        gd_update_windowsize(vc);
> +    }
> +}
> +
> +qemu_gl_context gd_gl_area_create_context(DisplayChangeListener *dcl,
> +                                          struct qemu_gl_params *params)
> +{
> +    VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
> +    GdkWindow *window;
> +    GdkGLContext *ctx;
> +    GError *err = NULL;
> +
> +    gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
> +    window = gtk_widget_get_window(vc->gfx.drawing_area);
> +    ctx = gdk_window_create_gl_context(window, &err);
> +    gdk_gl_context_set_required_version(ctx,
> +                                        params->major_ver,
> +                                        params->minor_ver);
> +    gdk_gl_context_realize(ctx, &err);
> +    return ctx;
> +}
> +
> +void gd_gl_area_destroy_context(DisplayChangeListener *dcl, qemu_gl_context 
> ctx)
> +{
> +    /* FIXME */
> +}
> +
> +void gd_gl_area_scanout(DisplayChangeListener *dcl,
> +                        uint32_t backing_id, bool backing_y_0_top,
> +                        uint32_t x, uint32_t y,
> +                        uint32_t w, uint32_t h)
> +{
> +    VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
> +
> +    vc->gfx.x = x;
> +    vc->gfx.y = y;
> +    vc->gfx.w = w;
> +    vc->gfx.h = h;
> +    vc->gfx.tex_id = backing_id;
> +    vc->gfx.y0_top = backing_y_0_top;
> +
> +    gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
> +
> +    if (vc->gfx.tex_id == 0 || vc->gfx.w == 0 || vc->gfx.h == 0) {
> +        gtk_gl_area_set_scanout_mode(vc, false);
> +        return;
> +    }
> +
> +    gtk_gl_area_set_scanout_mode(vc, true);
> +    if (!vc->gfx.fbo_id) {
> +        glGenFramebuffers(1, &vc->gfx.fbo_id);
> +    }
> +
> +    glBindFramebuffer(GL_FRAMEBUFFER_EXT, vc->gfx.fbo_id);
> +    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
> +                              GL_TEXTURE_2D, vc->gfx.tex_id, 0);
> +}
> +
> +void gd_gl_area_scanout_flush(DisplayChangeListener *dcl,
> +                          uint32_t x, uint32_t y, uint32_t w, uint32_t h)
> +{
> +    VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
> +
> +    gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area));
> +}
> +
> +void gtk_gl_area_init(void)
> +{
> +    display_opengl = 1;
> +}
> +
> +qemu_gl_context gd_gl_area_get_current_context(DisplayChangeListener *dcl)
> +{
> +    return gdk_gl_context_get_current();
> +}
> +
> +int gd_gl_area_make_current(DisplayChangeListener *dcl,
> +                            qemu_gl_context ctx)
> +{
> +    gdk_gl_context_make_current(ctx);
> +    return 0;
> +}
> diff --git a/ui/gtk.c b/ui/gtk.c
> index 6faf5a5..5cbb389 100644
> --- a/ui/gtk.c
> +++ b/ui/gtk.c
> @@ -356,6 +356,12 @@ static void gd_update_full_redraw(VirtualConsole *vc)
>      GtkWidget *area = vc->gfx.drawing_area;
>      int ww, wh;
>      gdk_drawable_get_size(gtk_widget_get_window(area), &ww, &wh);
> +#if defined(CONFIG_GTK_GL)
> +    if (vc->gfx.gls) {
> +        gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area));
> +        return;
> +    }
> +#endif
>      gtk_widget_queue_draw_area(area, 0, 0, ww, wh);
>  }
>
> @@ -596,6 +602,27 @@ static const DisplayChangeListenerOps dcl_ops = {
>
>  /** DisplayState Callbacks (opengl version) **/
>
> +#if defined(CONFIG_GTK_GL)
> +
> +static const DisplayChangeListenerOps dcl_gl_area_ops = {
> +    .dpy_name             = "gtk-egl",
> +    .dpy_gfx_update       = gd_gl_area_update,
> +    .dpy_gfx_switch       = gd_gl_area_switch,
> +    .dpy_gfx_check_format = console_gl_check_format,
> +    .dpy_refresh          = gd_gl_area_refresh,
> +    .dpy_mouse_set        = gd_mouse_set,
> +    .dpy_cursor_define    = gd_cursor_define,
> +
> +    .dpy_gl_ctx_create       = gd_gl_area_create_context,
> +    .dpy_gl_ctx_destroy      = gd_gl_area_destroy_context,
> +    .dpy_gl_ctx_make_current = gd_gl_area_make_current,
> +    .dpy_gl_ctx_get_current  = gd_gl_area_get_current_context,
> +    .dpy_gl_scanout          = gd_gl_area_scanout,
> +    .dpy_gl_update           = gd_gl_area_scanout_flush,
> +};
> +
> +#else
> +
>  static const DisplayChangeListenerOps dcl_egl_ops = {
>      .dpy_name             = "gtk-egl",
>      .dpy_gfx_update       = gd_egl_update,
> @@ -613,7 +640,8 @@ static const DisplayChangeListenerOps dcl_egl_ops = {
>      .dpy_gl_update           = gd_egl_scanout_flush,
>  };
>
> -#endif
> +#endif /* CONFIG_GTK_GL */
> +#endif /* CONFIG_OPENGL */
>
>  /** QEMU Events **/
>
> @@ -663,6 +691,19 @@ static gboolean gd_window_close(GtkWidget *widget, 
> GdkEvent *event,
>      return TRUE;
>  }
>
> +#if defined(CONFIG_GTK_GL)
> +static gboolean gd_render_event(GtkGLArea *area, GdkGLContext *context,
> +                                void *opaque)
> +{
> +    VirtualConsole *vc = opaque;
> +
> +    if (vc->gfx.gls) {
> +        gd_gl_area_draw(vc);
> +    }
> +    return TRUE;
> +}
> +#endif
> +
>  static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
>  {
>      VirtualConsole *vc = opaque;
> @@ -673,8 +714,13 @@ static gboolean gd_draw_event(GtkWidget *widget, cairo_t 
> *cr, void *opaque)
>
>  #if defined(CONFIG_OPENGL)
>      if (vc->gfx.gls) {
> +#if defined(CONFIG_GTK_GL)
> +        /* invoke render callback please */
> +        return FALSE;
> +#else
>          gd_egl_draw(vc);
>          return TRUE;
> +#endif
>      }
>  #endif
>
> @@ -1618,6 +1664,10 @@ static void gd_connect_vc_gfx_signals(VirtualConsole 
> *vc)
>  #if GTK_CHECK_VERSION(3, 0, 0)
>      g_signal_connect(vc->gfx.drawing_area, "draw",
>                       G_CALLBACK(gd_draw_event), vc);
> +#if defined(CONFIG_GTK_GL)
> +    g_signal_connect(vc->gfx.drawing_area, "render",
> +                     G_CALLBACK(gd_render_event), vc);
> +#endif
>  #else
>      g_signal_connect(vc->gfx.drawing_area, "expose-event",
>                       G_CALLBACK(gd_expose_event), vc);
> @@ -1726,7 +1776,37 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, 
> VirtualConsole *vc,
>      vc->gfx.scale_x = 1.0;
>      vc->gfx.scale_y = 1.0;
>
> -    vc->gfx.drawing_area = gtk_drawing_area_new();
> +#if defined(CONFIG_OPENGL)
> +    if (display_opengl) {
> +#if defined(CONFIG_GTK_GL)
> +        vc->gfx.drawing_area = gtk_gl_area_new();
> +        vc->gfx.dcl.ops = &dcl_gl_area_ops;
> +#else
> +        vc->gfx.drawing_area = gtk_drawing_area_new();
> +        /*
> +         * 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;
> +#endif /* CONFIG_GTK_GL */
> +    } else
> +#endif
> +    {
> +        vc->gfx.drawing_area = gtk_drawing_area_new();
> +        vc->gfx.dcl.ops = &dcl_ops;
> +    }
> +
> +
>      gtk_widget_add_events(vc->gfx.drawing_area,
>                            GDK_POINTER_MOTION_MASK |
>                            GDK_BUTTON_PRESS_MASK |
> @@ -1744,29 +1824,6 @@ 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);
>
> @@ -2029,8 +2086,12 @@ void early_gtk_display_init(int opengl)
>          break;
>      case 1: /* on */
>  #if defined(CONFIG_OPENGL)
> +#if defined(CONFIG_GTK_GL)
> +        gtk_gl_area_init();
> +#else
>          gtk_egl_init();
>  #endif
> +#endif
>          break;
>      default:
>          g_assert_not_reached();
> --
> 1.8.3.1
>
>



-- 
Marc-André Lureau



reply via email to

[Prev in Thread] Current Thread [Next in Thread]