qemu/ui/egl-headless.c
Erik Skultety 91e61947eb ui: Allow specifying 'rendernode' display option for egl-headless
As libvirt can't predict which rendernode QEMU would pick, it
won't adjust the permissions on the device, hence QEMU getting
"Permission denied" when opening the DRI device. Therefore, enable
'rendernode' option for egl-headless display type.

Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1648236

Signed-off-by: Erik Skultety <eskultet@redhat.com>
Message-id: 27f4617f19aa1072114f10f1aa9dd199735ef982.1542362949.git.eskultet@redhat.com
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2018-11-16 11:44:22 +01:00

210 lines
6.1 KiB
C

#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"
#include "ui/shader.h"
typedef struct egl_dpy {
DisplayChangeListener dcl;
DisplaySurface *ds;
QemuGLShader *gls;
egl_fb guest_fb;
egl_fb cursor_fb;
egl_fb blit_fb;
bool y_0_top;
uint32_t pos_x;
uint32_t pos_y;
} 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);
egl_fb_destroy(&edpy->guest_fb);
egl_fb_destroy(&edpy->blit_fb);
}
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->y_0_top = backing_y_0_top;
/* source framebuffer */
egl_fb_setup_for_tex(&edpy->guest_fb,
backing_width, backing_height, backing_id, false);
/* dest framebuffer */
if (edpy->blit_fb.width != backing_width ||
edpy->blit_fb.height != backing_height) {
egl_fb_destroy(&edpy->blit_fb);
egl_fb_setup_new_tex(&edpy->blit_fb, backing_width, backing_height);
}
}
static void egl_scanout_dmabuf(DisplayChangeListener *dcl,
QemuDmaBuf *dmabuf)
{
egl_dmabuf_import_texture(dmabuf);
if (!dmabuf->texture) {
return;
}
egl_scanout_texture(dcl, dmabuf->texture,
false, dmabuf->width, dmabuf->height,
0, 0, dmabuf->width, dmabuf->height);
}
static void egl_cursor_dmabuf(DisplayChangeListener *dcl,
QemuDmaBuf *dmabuf, bool have_hot,
uint32_t hot_x, uint32_t hot_y)
{
egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
if (dmabuf) {
egl_dmabuf_import_texture(dmabuf);
if (!dmabuf->texture) {
return;
}
egl_fb_setup_for_tex(&edpy->cursor_fb, dmabuf->width, dmabuf->height,
dmabuf->texture, false);
} else {
egl_fb_destroy(&edpy->cursor_fb);
}
}
static void egl_cursor_position(DisplayChangeListener *dcl,
uint32_t pos_x, uint32_t pos_y)
{
egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
edpy->pos_x = pos_x;
edpy->pos_y = pos_y;
}
static void egl_release_dmabuf(DisplayChangeListener *dcl,
QemuDmaBuf *dmabuf)
{
egl_dmabuf_release_texture(dmabuf);
}
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);
if (!edpy->guest_fb.texture || !edpy->ds) {
return;
}
assert(surface_width(edpy->ds) == edpy->guest_fb.width);
assert(surface_height(edpy->ds) == edpy->guest_fb.height);
assert(surface_format(edpy->ds) == PIXMAN_x8r8g8b8);
if (edpy->cursor_fb.texture) {
/* have cursor -> render using textures */
egl_texture_blit(edpy->gls, &edpy->blit_fb, &edpy->guest_fb,
!edpy->y_0_top);
egl_texture_blend(edpy->gls, &edpy->blit_fb, &edpy->cursor_fb,
!edpy->y_0_top, edpy->pos_x, edpy->pos_y);
} else {
/* no cursor -> use simple framebuffer blit */
egl_fb_blit(&edpy->blit_fb, &edpy->guest_fb, edpy->y_0_top);
}
egl_fb_read(surface_data(edpy->ds), &edpy->blit_fb);
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_scanout_dmabuf = egl_scanout_dmabuf,
.dpy_gl_cursor_dmabuf = egl_cursor_dmabuf,
.dpy_gl_cursor_position = egl_cursor_position,
.dpy_gl_release_dmabuf = egl_release_dmabuf,
.dpy_gl_update = egl_scanout_flush,
};
static void early_egl_headless_init(DisplayOptions *opts)
{
display_opengl = 1;
}
static void egl_headless_init(DisplayState *ds, DisplayOptions *opts)
{
DisplayGLMode mode = opts->has_gl ? opts->gl : DISPLAYGL_MODE_ON;
QemuConsole *con;
egl_dpy *edpy;
int idx;
if (egl_rendernode_init(opts->u.egl_headless.rendernode, mode) < 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;
edpy->gls = qemu_gl_init_shader();
register_displaychangelistener(&edpy->dcl);
}
}
static QemuDisplay qemu_display_egl = {
.type = DISPLAY_TYPE_EGL_HEADLESS,
.early_init = early_egl_headless_init,
.init = egl_headless_init,
};
static void register_egl(void)
{
qemu_display_register(&qemu_display_egl);
}
type_init(register_egl);