Message ID | 20170505104101.30589-7-kraxel@redhat.com |
---|---|
State | New |
Headers | show |
Hi, I can not compile git master anymore on my RHEL 7.3 installation ... I think it is due to this patch here. I now get: CC ui/egl-headless.o /home/thuth/devel/qemu/ui/egl-headless.c: In function ‘egl_headless_init’: /home/thuth/devel/qemu/ui/egl-headless.c:142:5: error: implicit declaration of function ‘egl_rendernode_init’ [-Werror=implicit-function-declaration] if (egl_rendernode_init(NULL) < 0) { ^ /home/thuth/devel/qemu/ui/egl-headless.c:142:5: error: nested extern declaration of ‘egl_rendernode_init’ [-Werror=nested-externs] cc1: all warnings being treated as errors make: *** [ui/egl-headless.o] Error 1 The problem is likely that the prototype in egl-helpers.h is protected with some "#ifdef CONFIG_OPENGL_DMABUF" ... so should the code in egl-headless.c put into such conditionals, too? Thomas On 05.05.2017 12:41, Gerd Hoffmann wrote: > Add egl-headless user interface. It doesn't provide a real user > interface, it only provides opengl support using drm render nodes. > It will copy back the bits rendered by the guest using virgl back > to a DisplaySurface and kick the usual display update code paths, > so spice and vnc and screendump can pick it up. > > Use it this way: > qemu -display egl-headless -vnc $display > qemu -display egl-headless -spice gl=off,$args > > Note that you should prefer native spice opengl support (-spice > gl=on) if possible because that delivers better performance. > > Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> > --- > include/ui/console.h | 3 + > ui/egl-headless.c | 158 +++++++++++++++++++++++++++++++++++++++++++++++++++ > vl.c | 16 ++++++ > ui/Makefile.objs | 1 + > 4 files changed, 178 insertions(+) > create mode 100644 ui/egl-headless.c > > diff --git a/include/ui/console.h b/include/ui/console.h > index d759338816..7262bef6d3 100644 > --- a/include/ui/console.h > +++ b/include/ui/console.h > @@ -527,4 +527,7 @@ static inline void early_gtk_display_init(int opengl) > } > #endif > > +/* egl-headless.c */ > +void egl_headless_init(void); > + > #endif > diff --git a/ui/egl-headless.c b/ui/egl-headless.c > new file mode 100644 > index 0000000000..d8d800f8a6 > --- /dev/null > +++ b/ui/egl-headless.c > @@ -0,0 +1,158 @@ > +#include "qemu/osdep.h" > +#include "qemu-common.h" > +#include "sysemu/sysemu.h" > +#include "ui/console.h" > +#include "ui/egl-helpers.h" > +#include "ui/egl-context.h" > + > +typedef struct egl_dpy { > + DisplayChangeListener dcl; > + DisplaySurface *ds; > + int width, height; > + GLuint texture; > + GLuint framebuffer; > + GLuint blit_texture; > + GLuint blit_framebuffer; > + bool y_0_top; > +} egl_dpy; > + > +static void egl_refresh(DisplayChangeListener *dcl) > +{ > + graphic_hw_update(dcl->con); > +} > + > +static void egl_gfx_update(DisplayChangeListener *dcl, > + int x, int y, int w, int h) > +{ > +} > + > +static void egl_gfx_switch(DisplayChangeListener *dcl, > + struct DisplaySurface *new_surface) > +{ > + egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); > + > + edpy->ds = new_surface; > +} > + > +static void egl_scanout_disable(DisplayChangeListener *dcl) > +{ > + egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); > + > + edpy->texture = 0; > + /* XXX: delete framebuffers here ??? */ > +} > + > +static void egl_scanout_texture(DisplayChangeListener *dcl, > + uint32_t backing_id, > + bool backing_y_0_top, > + uint32_t backing_width, > + uint32_t backing_height, > + uint32_t x, uint32_t y, > + uint32_t w, uint32_t h) > +{ > + egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); > + > + edpy->texture = backing_id; > + edpy->y_0_top = backing_y_0_top; > + > + /* source framebuffer */ > + if (!edpy->framebuffer) { > + glGenFramebuffers(1, &edpy->framebuffer); > + } > + glBindFramebuffer(GL_FRAMEBUFFER_EXT, edpy->framebuffer); > + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, > + GL_TEXTURE_2D, edpy->texture, 0); > + > + /* dest framebuffer */ > + if (!edpy->blit_framebuffer) { > + glGenFramebuffers(1, &edpy->blit_framebuffer); > + glGenTextures(1, &edpy->blit_texture); > + edpy->width = 0; > + edpy->height = 0; > + } > + if (edpy->width != backing_width || edpy->height != backing_height) { > + edpy->width = backing_width; > + edpy->height = backing_height; > + glBindTexture(GL_TEXTURE_2D, edpy->blit_texture); > + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, > + edpy->width, edpy->height, > + 0, GL_BGRA, GL_UNSIGNED_BYTE, 0); > + glBindFramebuffer(GL_FRAMEBUFFER_EXT, edpy->blit_framebuffer); > + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, > + GL_TEXTURE_2D, edpy->blit_texture, 0); > + } > +} > + > +static void egl_scanout_flush(DisplayChangeListener *dcl, > + uint32_t x, uint32_t y, > + uint32_t w, uint32_t h) > +{ > + egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); > + GLuint y1, y2; > + > + if (!edpy->texture || !edpy->ds) { > + return; > + } > + assert(surface_width(edpy->ds) == edpy->width); > + assert(surface_height(edpy->ds) == edpy->height); > + assert(surface_format(edpy->ds) == PIXMAN_x8r8g8b8); > + > + /* blit framebuffer, flip if needed */ > + glBindFramebuffer(GL_READ_FRAMEBUFFER, edpy->framebuffer); > + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, edpy->blit_framebuffer); > + glViewport(0, 0, edpy->width, edpy->height); > + y1 = edpy->y_0_top ? edpy->height : 0; > + y2 = edpy->y_0_top ? 0 : edpy->height; > + glBlitFramebuffer(0, y1, edpy->width, y2, > + 0, 0, edpy->width, edpy->height, > + GL_COLOR_BUFFER_BIT, GL_NEAREST); > + > + /* read pixels to surface */ > + glBindFramebuffer(GL_READ_FRAMEBUFFER, edpy->blit_framebuffer); > + glReadBuffer(GL_COLOR_ATTACHMENT0_EXT); > + glReadPixels(0, 0, edpy->width, edpy->height, > + GL_BGRA, GL_UNSIGNED_BYTE, surface_data(edpy->ds)); > + > + /* notify about updates */ > + dpy_gfx_update(edpy->dcl.con, x, y, w, h); > +} > + > +static const DisplayChangeListenerOps egl_ops = { > + .dpy_name = "egl-headless", > + .dpy_refresh = egl_refresh, > + .dpy_gfx_update = egl_gfx_update, > + .dpy_gfx_switch = egl_gfx_switch, > + > + .dpy_gl_ctx_create = qemu_egl_create_context, > + .dpy_gl_ctx_destroy = qemu_egl_destroy_context, > + .dpy_gl_ctx_make_current = qemu_egl_make_context_current, > + .dpy_gl_ctx_get_current = qemu_egl_get_current_context, > + > + .dpy_gl_scanout_disable = egl_scanout_disable, > + .dpy_gl_scanout_texture = egl_scanout_texture, > + .dpy_gl_update = egl_scanout_flush, > +}; > + > +void egl_headless_init(void) > +{ > + QemuConsole *con; > + egl_dpy *edpy; > + int idx; > + > + if (egl_rendernode_init(NULL) < 0) { > + error_report("egl: render node init failed"); > + exit(1); > + } > + > + for (idx = 0;; idx++) { > + con = qemu_console_lookup_by_index(idx); > + if (!con || !qemu_console_is_graphic(con)) { > + break; > + } > + > + edpy = g_new0(egl_dpy, 1); > + edpy->dcl.con = con; > + edpy->dcl.ops = &egl_ops; > + register_displaychangelistener(&edpy->dcl); > + } > +} > diff --git a/vl.c b/vl.c > index f46e070e0d..4e76e0ceb4 100644 > --- a/vl.c > +++ b/vl.c > @@ -2050,6 +2050,7 @@ typedef enum DisplayType { > DT_SDL, > DT_COCOA, > DT_GTK, > + DT_EGL, > DT_NONE, > } DisplayType; > > @@ -2127,6 +2128,15 @@ static DisplayType select_display(const char *p) > error_report("VNC requires a display argument vnc=<display>"); > exit(1); > } > + } else if (strstart(p, "egl-headless", &opts)) { > +#ifdef CONFIG_OPENGL > + request_opengl = 1; > + display_opengl = 1; > + display = DT_EGL; > +#else > + fprintf(stderr, "egl support is disabled\n"); > + exit(1); > +#endif > } else if (strstart(p, "curses", &opts)) { > #ifdef CONFIG_CURSES > display = DT_CURSES; > @@ -4662,6 +4672,12 @@ int main(int argc, char **argv, char **envp) > qemu_spice_display_init(); > } > > +#ifdef CONFIG_OPENGL > + if (display_type == DT_EGL) { > + egl_headless_init(); > + } > +#endif > + > if (foreach_device_config(DEV_GDB, gdbserver_start) < 0) { > exit(1); > } > diff --git a/ui/Makefile.objs b/ui/Makefile.objs > index 27566b32f1..aac6ae8bef 100644 > --- a/ui/Makefile.objs > +++ b/ui/Makefile.objs > @@ -33,6 +33,7 @@ common-obj-y += shader.o > common-obj-y += console-gl.o > common-obj-y += egl-helpers.o > common-obj-y += egl-context.o > +common-obj-y += egl-headless.o > ifeq ($(CONFIG_GTK_GL),y) > common-obj-$(CONFIG_GTK) += gtk-gl-area.o > else >
diff --git a/include/ui/console.h b/include/ui/console.h index d759338816..7262bef6d3 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -527,4 +527,7 @@ static inline void early_gtk_display_init(int opengl) } #endif +/* egl-headless.c */ +void egl_headless_init(void); + #endif diff --git a/ui/egl-headless.c b/ui/egl-headless.c new file mode 100644 index 0000000000..d8d800f8a6 --- /dev/null +++ b/ui/egl-headless.c @@ -0,0 +1,158 @@ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "sysemu/sysemu.h" +#include "ui/console.h" +#include "ui/egl-helpers.h" +#include "ui/egl-context.h" + +typedef struct egl_dpy { + DisplayChangeListener dcl; + DisplaySurface *ds; + int width, height; + GLuint texture; + GLuint framebuffer; + GLuint blit_texture; + GLuint blit_framebuffer; + bool y_0_top; +} egl_dpy; + +static void egl_refresh(DisplayChangeListener *dcl) +{ + graphic_hw_update(dcl->con); +} + +static void egl_gfx_update(DisplayChangeListener *dcl, + int x, int y, int w, int h) +{ +} + +static void egl_gfx_switch(DisplayChangeListener *dcl, + struct DisplaySurface *new_surface) +{ + egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); + + edpy->ds = new_surface; +} + +static void egl_scanout_disable(DisplayChangeListener *dcl) +{ + egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); + + edpy->texture = 0; + /* XXX: delete framebuffers here ??? */ +} + +static void egl_scanout_texture(DisplayChangeListener *dcl, + uint32_t backing_id, + bool backing_y_0_top, + uint32_t backing_width, + uint32_t backing_height, + uint32_t x, uint32_t y, + uint32_t w, uint32_t h) +{ + egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); + + edpy->texture = backing_id; + edpy->y_0_top = backing_y_0_top; + + /* source framebuffer */ + if (!edpy->framebuffer) { + glGenFramebuffers(1, &edpy->framebuffer); + } + glBindFramebuffer(GL_FRAMEBUFFER_EXT, edpy->framebuffer); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, edpy->texture, 0); + + /* dest framebuffer */ + if (!edpy->blit_framebuffer) { + glGenFramebuffers(1, &edpy->blit_framebuffer); + glGenTextures(1, &edpy->blit_texture); + edpy->width = 0; + edpy->height = 0; + } + if (edpy->width != backing_width || edpy->height != backing_height) { + edpy->width = backing_width; + edpy->height = backing_height; + glBindTexture(GL_TEXTURE_2D, edpy->blit_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, + edpy->width, edpy->height, + 0, GL_BGRA, GL_UNSIGNED_BYTE, 0); + glBindFramebuffer(GL_FRAMEBUFFER_EXT, edpy->blit_framebuffer); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, edpy->blit_texture, 0); + } +} + +static void egl_scanout_flush(DisplayChangeListener *dcl, + uint32_t x, uint32_t y, + uint32_t w, uint32_t h) +{ + egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); + GLuint y1, y2; + + if (!edpy->texture || !edpy->ds) { + return; + } + assert(surface_width(edpy->ds) == edpy->width); + assert(surface_height(edpy->ds) == edpy->height); + assert(surface_format(edpy->ds) == PIXMAN_x8r8g8b8); + + /* blit framebuffer, flip if needed */ + glBindFramebuffer(GL_READ_FRAMEBUFFER, edpy->framebuffer); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, edpy->blit_framebuffer); + glViewport(0, 0, edpy->width, edpy->height); + y1 = edpy->y_0_top ? edpy->height : 0; + y2 = edpy->y_0_top ? 0 : edpy->height; + glBlitFramebuffer(0, y1, edpy->width, y2, + 0, 0, edpy->width, edpy->height, + GL_COLOR_BUFFER_BIT, GL_NEAREST); + + /* read pixels to surface */ + glBindFramebuffer(GL_READ_FRAMEBUFFER, edpy->blit_framebuffer); + glReadBuffer(GL_COLOR_ATTACHMENT0_EXT); + glReadPixels(0, 0, edpy->width, edpy->height, + GL_BGRA, GL_UNSIGNED_BYTE, surface_data(edpy->ds)); + + /* notify about updates */ + dpy_gfx_update(edpy->dcl.con, x, y, w, h); +} + +static const DisplayChangeListenerOps egl_ops = { + .dpy_name = "egl-headless", + .dpy_refresh = egl_refresh, + .dpy_gfx_update = egl_gfx_update, + .dpy_gfx_switch = egl_gfx_switch, + + .dpy_gl_ctx_create = qemu_egl_create_context, + .dpy_gl_ctx_destroy = qemu_egl_destroy_context, + .dpy_gl_ctx_make_current = qemu_egl_make_context_current, + .dpy_gl_ctx_get_current = qemu_egl_get_current_context, + + .dpy_gl_scanout_disable = egl_scanout_disable, + .dpy_gl_scanout_texture = egl_scanout_texture, + .dpy_gl_update = egl_scanout_flush, +}; + +void egl_headless_init(void) +{ + QemuConsole *con; + egl_dpy *edpy; + int idx; + + if (egl_rendernode_init(NULL) < 0) { + error_report("egl: render node init failed"); + exit(1); + } + + for (idx = 0;; idx++) { + con = qemu_console_lookup_by_index(idx); + if (!con || !qemu_console_is_graphic(con)) { + break; + } + + edpy = g_new0(egl_dpy, 1); + edpy->dcl.con = con; + edpy->dcl.ops = &egl_ops; + register_displaychangelistener(&edpy->dcl); + } +} diff --git a/vl.c b/vl.c index f46e070e0d..4e76e0ceb4 100644 --- a/vl.c +++ b/vl.c @@ -2050,6 +2050,7 @@ typedef enum DisplayType { DT_SDL, DT_COCOA, DT_GTK, + DT_EGL, DT_NONE, } DisplayType; @@ -2127,6 +2128,15 @@ static DisplayType select_display(const char *p) error_report("VNC requires a display argument vnc=<display>"); exit(1); } + } else if (strstart(p, "egl-headless", &opts)) { +#ifdef CONFIG_OPENGL + request_opengl = 1; + display_opengl = 1; + display = DT_EGL; +#else + fprintf(stderr, "egl support is disabled\n"); + exit(1); +#endif } else if (strstart(p, "curses", &opts)) { #ifdef CONFIG_CURSES display = DT_CURSES; @@ -4662,6 +4672,12 @@ int main(int argc, char **argv, char **envp) qemu_spice_display_init(); } +#ifdef CONFIG_OPENGL + if (display_type == DT_EGL) { + egl_headless_init(); + } +#endif + if (foreach_device_config(DEV_GDB, gdbserver_start) < 0) { exit(1); } diff --git a/ui/Makefile.objs b/ui/Makefile.objs index 27566b32f1..aac6ae8bef 100644 --- a/ui/Makefile.objs +++ b/ui/Makefile.objs @@ -33,6 +33,7 @@ common-obj-y += shader.o common-obj-y += console-gl.o common-obj-y += egl-helpers.o common-obj-y += egl-context.o +common-obj-y += egl-headless.o ifeq ($(CONFIG_GTK_GL),y) common-obj-$(CONFIG_GTK) += gtk-gl-area.o else
Add egl-headless user interface. It doesn't provide a real user interface, it only provides opengl support using drm render nodes. It will copy back the bits rendered by the guest using virgl back to a DisplaySurface and kick the usual display update code paths, so spice and vnc and screendump can pick it up. Use it this way: qemu -display egl-headless -vnc $display qemu -display egl-headless -spice gl=off,$args Note that you should prefer native spice opengl support (-spice gl=on) if possible because that delivers better performance. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- include/ui/console.h | 3 + ui/egl-headless.c | 158 +++++++++++++++++++++++++++++++++++++++++++++++++++ vl.c | 16 ++++++ ui/Makefile.objs | 1 + 4 files changed, 178 insertions(+) create mode 100644 ui/egl-headless.c