qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

gtk-egl.c (10722B)


      1 /*
      2  * GTK UI -- egl opengl code.
      3  *
      4  * Note that gtk 3.16+ (released 2015-03-23) has a GtkGLArea widget,
      5  * which is GtkDrawingArea like widget with opengl rendering support.
      6  *
      7  * This code handles opengl support on older gtk versions, using egl
      8  * to get a opengl context for the X11 window.
      9  *
     10  * This work is licensed under the terms of the GNU GPL, version 2 or later.
     11  * See the COPYING file in the top-level directory.
     12  */
     13 
     14 #include "qemu/osdep.h"
     15 #include "qemu/main-loop.h"
     16 
     17 #include "trace.h"
     18 
     19 #include "ui/console.h"
     20 #include "ui/gtk.h"
     21 #include "ui/egl-helpers.h"
     22 #include "ui/shader.h"
     23 
     24 #include "sysemu/sysemu.h"
     25 
     26 static void gtk_egl_set_scanout_mode(VirtualConsole *vc, bool scanout)
     27 {
     28     if (vc->gfx.scanout_mode == scanout) {
     29         return;
     30     }
     31 
     32     vc->gfx.scanout_mode = scanout;
     33     if (!vc->gfx.scanout_mode) {
     34         egl_fb_destroy(&vc->gfx.guest_fb);
     35         if (vc->gfx.surface) {
     36             surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds);
     37             surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds);
     38         }
     39     }
     40 }
     41 
     42 /** DisplayState Callbacks (opengl version) **/
     43 
     44 void gd_egl_init(VirtualConsole *vc)
     45 {
     46     GdkWindow *gdk_window = gtk_widget_get_window(vc->gfx.drawing_area);
     47     if (!gdk_window) {
     48         return;
     49     }
     50 
     51     Window x11_window = gdk_x11_window_get_xid(gdk_window);
     52     if (!x11_window) {
     53         return;
     54     }
     55 
     56     vc->gfx.ectx = qemu_egl_init_ctx();
     57     vc->gfx.esurface = qemu_egl_init_surface_x11
     58         (vc->gfx.ectx, (EGLNativeWindowType)x11_window);
     59 
     60     assert(vc->gfx.esurface);
     61 }
     62 
     63 void gd_egl_draw(VirtualConsole *vc)
     64 {
     65     GdkWindow *window;
     66 #ifdef CONFIG_GBM
     67     QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf;
     68 #endif
     69     int ww, wh;
     70 
     71     if (!vc->gfx.gls) {
     72         return;
     73     }
     74 
     75     window = gtk_widget_get_window(vc->gfx.drawing_area);
     76     ww = gdk_window_get_width(window);
     77     wh = gdk_window_get_height(window);
     78 
     79     if (vc->gfx.scanout_mode) {
     80 #ifdef CONFIG_GBM
     81         if (dmabuf) {
     82             if (!dmabuf->draw_submitted) {
     83                 return;
     84             } else {
     85                 dmabuf->draw_submitted = false;
     86             }
     87         }
     88 #endif
     89         gd_egl_scanout_flush(&vc->gfx.dcl, 0, 0, vc->gfx.w, vc->gfx.h);
     90 
     91         vc->gfx.scale_x = (double)ww / vc->gfx.w;
     92         vc->gfx.scale_y = (double)wh / vc->gfx.h;
     93 
     94         glFlush();
     95 #ifdef CONFIG_GBM
     96         if (dmabuf) {
     97             egl_dmabuf_create_fence(dmabuf);
     98             if (dmabuf->fence_fd > 0) {
     99                 qemu_set_fd_handler(dmabuf->fence_fd, gd_hw_gl_flushed, NULL, vc);
    100                 return;
    101             }
    102             graphic_hw_gl_block(vc->gfx.dcl.con, false);
    103         }
    104 #endif
    105     } else {
    106         if (!vc->gfx.ds) {
    107             return;
    108         }
    109         eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
    110                        vc->gfx.esurface, vc->gfx.ectx);
    111 
    112         surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, ww, wh);
    113         surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds);
    114 
    115         eglSwapBuffers(qemu_egl_display, vc->gfx.esurface);
    116 
    117         vc->gfx.scale_x = (double)ww / surface_width(vc->gfx.ds);
    118         vc->gfx.scale_y = (double)wh / surface_height(vc->gfx.ds);
    119 
    120         glFlush();
    121     }
    122 }
    123 
    124 void gd_egl_update(DisplayChangeListener *dcl,
    125                    int x, int y, int w, int h)
    126 {
    127     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
    128 
    129     if (!vc->gfx.gls || !vc->gfx.ds) {
    130         return;
    131     }
    132 
    133     eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
    134                    vc->gfx.esurface, vc->gfx.ectx);
    135     surface_gl_update_texture(vc->gfx.gls, vc->gfx.ds, x, y, w, h);
    136     vc->gfx.glupdates++;
    137 }
    138 
    139 void gd_egl_refresh(DisplayChangeListener *dcl)
    140 {
    141     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
    142 
    143     gd_update_monitor_refresh_rate(
    144             vc, vc->window ? vc->window : vc->gfx.drawing_area);
    145 
    146     if (!vc->gfx.esurface) {
    147         gd_egl_init(vc);
    148         if (!vc->gfx.esurface) {
    149             return;
    150         }
    151         vc->gfx.gls = qemu_gl_init_shader();
    152         if (vc->gfx.ds) {
    153             surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds);
    154             surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds);
    155         }
    156 #ifdef CONFIG_GBM
    157         if (vc->gfx.guest_fb.dmabuf) {
    158             egl_dmabuf_release_texture(vc->gfx.guest_fb.dmabuf);
    159             gd_egl_scanout_dmabuf(dcl, vc->gfx.guest_fb.dmabuf);
    160         }
    161 #endif
    162     }
    163 
    164     graphic_hw_update(dcl->con);
    165 
    166     if (vc->gfx.glupdates) {
    167         vc->gfx.glupdates = 0;
    168         gtk_egl_set_scanout_mode(vc, false);
    169         gd_egl_draw(vc);
    170     }
    171 }
    172 
    173 void gd_egl_switch(DisplayChangeListener *dcl,
    174                    DisplaySurface *surface)
    175 {
    176     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
    177     bool resized = true;
    178 
    179     trace_gd_switch(vc->label, surface_width(surface), surface_height(surface));
    180 
    181     if (vc->gfx.ds &&
    182         surface_width(vc->gfx.ds) == surface_width(surface) &&
    183         surface_height(vc->gfx.ds) == surface_height(surface)) {
    184         resized = false;
    185     }
    186     eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
    187                    vc->gfx.esurface, vc->gfx.ectx);
    188 
    189     surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds);
    190     vc->gfx.ds = surface;
    191     if (vc->gfx.gls) {
    192         surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds);
    193     }
    194 
    195     if (resized) {
    196         gd_update_windowsize(vc);
    197     }
    198 
    199     eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
    200                    EGL_NO_CONTEXT);
    201 }
    202 
    203 QEMUGLContext gd_egl_create_context(DisplayGLCtx *dgc,
    204                                     QEMUGLParams *params)
    205 {
    206     VirtualConsole *vc = container_of(dgc, VirtualConsole, gfx.dgc);
    207 
    208     eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
    209                    vc->gfx.esurface, vc->gfx.ectx);
    210     return qemu_egl_create_context(dgc, params);
    211 }
    212 
    213 void gd_egl_scanout_disable(DisplayChangeListener *dcl)
    214 {
    215     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
    216 
    217     vc->gfx.w = 0;
    218     vc->gfx.h = 0;
    219     gtk_egl_set_scanout_mode(vc, false);
    220 }
    221 
    222 void gd_egl_scanout_texture(DisplayChangeListener *dcl,
    223                             uint32_t backing_id, bool backing_y_0_top,
    224                             uint32_t backing_width, uint32_t backing_height,
    225                             uint32_t x, uint32_t y,
    226                             uint32_t w, uint32_t h)
    227 {
    228     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
    229 
    230     vc->gfx.x = x;
    231     vc->gfx.y = y;
    232     vc->gfx.w = w;
    233     vc->gfx.h = h;
    234     vc->gfx.y0_top = backing_y_0_top;
    235 
    236     eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
    237                    vc->gfx.esurface, vc->gfx.ectx);
    238 
    239     gtk_egl_set_scanout_mode(vc, true);
    240     egl_fb_setup_for_tex(&vc->gfx.guest_fb, backing_width, backing_height,
    241                          backing_id, false);
    242 }
    243 
    244 void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl,
    245                            QemuDmaBuf *dmabuf)
    246 {
    247 #ifdef CONFIG_GBM
    248     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
    249 
    250     eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
    251                    vc->gfx.esurface, vc->gfx.ectx);
    252 
    253     egl_dmabuf_import_texture(dmabuf);
    254     if (!dmabuf->texture) {
    255         return;
    256     }
    257 
    258     gd_egl_scanout_texture(dcl, dmabuf->texture,
    259                            false, dmabuf->width, dmabuf->height,
    260                            0, 0, dmabuf->width, dmabuf->height);
    261 
    262     if (dmabuf->allow_fences) {
    263         vc->gfx.guest_fb.dmabuf = dmabuf;
    264     }
    265 #endif
    266 }
    267 
    268 void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl,
    269                           QemuDmaBuf *dmabuf, bool have_hot,
    270                           uint32_t hot_x, uint32_t hot_y)
    271 {
    272 #ifdef CONFIG_GBM
    273     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
    274 
    275     if (dmabuf) {
    276         egl_dmabuf_import_texture(dmabuf);
    277         if (!dmabuf->texture) {
    278             return;
    279         }
    280         egl_fb_setup_for_tex(&vc->gfx.cursor_fb, dmabuf->width, dmabuf->height,
    281                              dmabuf->texture, false);
    282     } else {
    283         egl_fb_destroy(&vc->gfx.cursor_fb);
    284     }
    285 #endif
    286 }
    287 
    288 void gd_egl_cursor_position(DisplayChangeListener *dcl,
    289                             uint32_t pos_x, uint32_t pos_y)
    290 {
    291     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
    292 
    293     vc->gfx.cursor_x = pos_x * vc->gfx.scale_x;
    294     vc->gfx.cursor_y = pos_y * vc->gfx.scale_y;
    295 }
    296 
    297 void gd_egl_scanout_flush(DisplayChangeListener *dcl,
    298                           uint32_t x, uint32_t y, uint32_t w, uint32_t h)
    299 {
    300     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
    301     GdkWindow *window;
    302     int ww, wh;
    303 
    304     if (!vc->gfx.scanout_mode) {
    305         return;
    306     }
    307     if (!vc->gfx.guest_fb.framebuffer) {
    308         return;
    309     }
    310 
    311     eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
    312                    vc->gfx.esurface, vc->gfx.ectx);
    313 
    314     window = gtk_widget_get_window(vc->gfx.drawing_area);
    315     ww = gdk_window_get_width(window);
    316     wh = gdk_window_get_height(window);
    317     egl_fb_setup_default(&vc->gfx.win_fb, ww, wh);
    318     if (vc->gfx.cursor_fb.texture) {
    319         egl_texture_blit(vc->gfx.gls, &vc->gfx.win_fb, &vc->gfx.guest_fb,
    320                          vc->gfx.y0_top);
    321         egl_texture_blend(vc->gfx.gls, &vc->gfx.win_fb, &vc->gfx.cursor_fb,
    322                           vc->gfx.y0_top,
    323                           vc->gfx.cursor_x, vc->gfx.cursor_y,
    324                           vc->gfx.scale_x, vc->gfx.scale_y);
    325     } else {
    326         egl_fb_blit(&vc->gfx.win_fb, &vc->gfx.guest_fb, !vc->gfx.y0_top);
    327     }
    328 
    329 #ifdef CONFIG_GBM
    330     if (vc->gfx.guest_fb.dmabuf) {
    331         egl_dmabuf_create_sync(vc->gfx.guest_fb.dmabuf);
    332     }
    333 #endif
    334 
    335     eglSwapBuffers(qemu_egl_display, vc->gfx.esurface);
    336 }
    337 
    338 void gd_egl_flush(DisplayChangeListener *dcl,
    339                   uint32_t x, uint32_t y, uint32_t w, uint32_t h)
    340 {
    341     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
    342     GtkWidget *area = vc->gfx.drawing_area;
    343 
    344     if (vc->gfx.guest_fb.dmabuf && !vc->gfx.guest_fb.dmabuf->draw_submitted) {
    345         graphic_hw_gl_block(vc->gfx.dcl.con, true);
    346         vc->gfx.guest_fb.dmabuf->draw_submitted = true;
    347         gtk_widget_queue_draw_area(area, x, y, w, h);
    348         return;
    349     }
    350 
    351     gd_egl_scanout_flush(&vc->gfx.dcl, x, y, w, h);
    352 }
    353 
    354 void gtk_egl_init(DisplayGLMode mode)
    355 {
    356     GdkDisplay *gdk_display = gdk_display_get_default();
    357     Display *x11_display = gdk_x11_display_get_xdisplay(gdk_display);
    358 
    359     if (qemu_egl_init_dpy_x11(x11_display, mode) < 0) {
    360         return;
    361     }
    362 
    363     display_opengl = 1;
    364 }
    365 
    366 int gd_egl_make_current(DisplayGLCtx *dgc,
    367                         QEMUGLContext ctx)
    368 {
    369     VirtualConsole *vc = container_of(dgc, VirtualConsole, gfx.dgc);
    370 
    371     return eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
    372                           vc->gfx.esurface, ctx);
    373 }