Skip to content

Commit

Permalink
spice-display: initial support for IOSurface rendering
Browse files Browse the repository at this point in the history
In both surface and scanout mode, we blit the texture to a ANGLE texture
backed pbuffer using IOSurface. This IOSurface can be shared with a
separate process.

What needs to be done:
1) Handing of IOSurfaceRef through mach ports (Unix sockets are not
   supported. This implementation hacks in by passing the pointer through
   a file descriptor but this will fail if client is running in a separate
   process).
2) Cursor handling through QEMU (currently, the SPICE client will handle
   it).
  • Loading branch information
osy committed Aug 2, 2021
1 parent dc386f9 commit f384e2c
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 0 deletions.
8 changes: 8 additions & 0 deletions include/ui/spice-display.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
#include "ui/qemu-pixman.h"
#include "ui/console.h"

#if defined(CONFIG_IOSURFACE)
#include <CoreFoundation/CoreFoundation.h>
#include <IOSurface/IOSurfaceRef.h>
#endif

#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
# if SPICE_SERVER_VERSION >= 0x000d01 /* release 0.13.1 */
# define HAVE_SPICE_GL 1
Expand Down Expand Up @@ -130,6 +135,9 @@ struct SimpleSpiceDisplay {
QemuDmaBuf *guest_dmabuf;
bool guest_dmabuf_refresh;
#endif
#if defined(CONFIG_IOSURFACE)
IOSurfaceRef iosurface;
#endif
#if defined(CONFIG_ANGLE)
EGLSurface esurface;
egl_fb iosurface_fb;
Expand Down
3 changes: 3 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ version_res = []
coref = []
iokit = []
vmnet = not_found
iosurface = not_found
emulator_link_args = []
nvmm =not_found
hvf = not_found
Expand All @@ -201,6 +202,7 @@ elif targetos == 'darwin'
coref = dependency('appleframeworks', modules: 'CoreFoundation')
iokit = dependency('appleframeworks', modules: 'IOKit', required: false)
vmnet = dependency('appleframeworks', modules: 'vmnet', required: false)
iosurface = dependency('appleframeworks', modules: 'IOSurface', required: false)
elif targetos == 'sunos'
socket = [cc.find_library('socket'),
cc.find_library('nsl'),
Expand Down Expand Up @@ -1322,6 +1324,7 @@ config_host_data.set('CONFIG_FUSE_LSEEK', fuse_lseek.found())
config_host_data.set('CONFIG_X11', x11.found())
config_host_data.set('CONFIG_CFI', get_option('cfi'))
config_host_data.set('CONFIG_VMNET', vmnet.found())
config_host_data.set('CONFIG_IOSURFACE', iosurface.found())
config_host_data.set('QEMU_VERSION', '"@0@"'.format(meson.project_version()))
config_host_data.set('QEMU_VERSION_MAJOR', meson.project_version().split('.')[0])
config_host_data.set('QEMU_VERSION_MINOR', meson.project_version().split('.')[1])
Expand Down
3 changes: 3 additions & 0 deletions ui/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ if config_host.has_key('CONFIG_SPICE')
'spice-input.c',
'spice-display.c'
))
if iosurface.found()
spice_core_ss.add(iosurface)
endif
ui_modules += {'spice-core' : spice_core_ss}
endif

Expand Down
170 changes: 170 additions & 0 deletions ui/spice-display.c
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,124 @@ static const DisplayChangeListenerOps display_listener_ops = {

#ifdef HAVE_SPICE_GL

#if defined(CONFIG_IOSURFACE)

static void AddIntegerValue(CFMutableDictionaryRef dictionary, const CFStringRef key, int32_t value)
{
CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value);
CFDictionaryAddValue(dictionary, key, number);
CFRelease(number);
}

static int spice_iosurface_create(SimpleSpiceDisplay *ssd, int width, int height)
{
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(
kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
AddIntegerValue(dict, kIOSurfaceWidth, width);
AddIntegerValue(dict, kIOSurfaceHeight, height);
AddIntegerValue(dict, kIOSurfacePixelFormat, 'BGRA');
AddIntegerValue(dict, kIOSurfaceBytesPerElement, 4);

ssd->iosurface = IOSurfaceCreate(dict);
CFRelease(dict);

if (!ssd->iosurface) {
error_report("spice_iosurface_create: IOSurfaceCreate failed");
return 0;
}

#if defined(CONFIG_ANGLE)
const EGLint attribs[] = {
EGL_WIDTH, width,
EGL_HEIGHT, height,
EGL_IOSURFACE_PLANE_ANGLE, 0,
EGL_TEXTURE_TARGET, EGL_TEXTURE_2D,
EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
EGL_IOSURFACE_USAGE_HINT_ANGLE, EGL_IOSURFACE_WRITE_HINT_ANGLE,
EGL_NONE, EGL_NONE,
};
ssd->esurface = qemu_egl_init_buffer_surface(spice_gl_ctx,
EGL_IOSURFACE_ANGLE,
ssd->iosurface,
attribs);

if (ssd->esurface == NULL) {
goto gl_error;
}

egl_fb_setup_new_tex(&ssd->iosurface_fb, width, height);

return 1;
gl_error:
CFRelease(ssd->iosurface);
ssd->iosurface = NULL;
return 0;
#else
error_report("spice_iosurface_create: ANGLE not found");
return 0;
#endif
}

static void spice_iosurface_destroy(SimpleSpiceDisplay *ssd)
{
if (!ssd->iosurface) {
return;
}
#if defined(CONFIG_ANGLE)
egl_fb_destroy(&ssd->iosurface_fb);
qemu_egl_destroy_surface(ssd->esurface);
ssd->esurface = EGL_NO_SURFACE;
#endif
CFRelease(ssd->iosurface);
ssd->iosurface = NULL;
}

static int spice_iosurface_resize(SimpleSpiceDisplay *ssd, int width, int height)
{
if (ssd->iosurface) {
if (IOSurfaceGetHeight(ssd->iosurface) != width ||
IOSurfaceGetWidth(ssd->iosurface) != height) {
spice_iosurface_destroy(ssd);
return spice_iosurface_create(ssd, width, height);
} else {
return 1;
}
} else {
return spice_iosurface_create(ssd, width, height);
}
}

static void spice_iosurface_blit(SimpleSpiceDisplay *ssd, GLuint src_texture, bool flip)
{
egl_fb tmp_fb = { .texture = src_texture };
if (!ssd->iosurface) {
return;
}

#if defined(CONFIG_ANGLE)
eglMakeCurrent(qemu_egl_display, ssd->esurface, ssd->esurface, spice_gl_ctx);
glBindTexture(GL_TEXTURE_2D, ssd->iosurface_fb.texture);
eglBindTexImage(qemu_egl_display, ssd->esurface, EGL_BACK_BUFFER);
egl_texture_blit(ssd->gls, &ssd->iosurface_fb, &tmp_fb, flip);
#endif
}

static void spice_iosurface_flush(SimpleSpiceDisplay *ssd)
{
if (!ssd->iosurface) {
return;
}

#if defined(CONFIG_ANGLE)
eglMakeCurrent(qemu_egl_display, ssd->esurface, ssd->esurface, spice_gl_ctx);
eglReleaseTexImage(qemu_egl_display, ssd->esurface, EGL_BACK_BUFFER);
#endif
}

#endif

static void qemu_spice_gl_monitor_config(SimpleSpiceDisplay *ssd,
int x, int y, int w, int h)
{
Expand Down Expand Up @@ -854,6 +972,9 @@ static void spice_gl_refresh(DisplayChangeListener *dcl)
graphic_hw_update(dcl->con);
if (ssd->gl_updates && ssd->have_surface) {
qemu_spice_gl_block(ssd, true);
#if defined(CONFIG_IOSURFACE)
spice_iosurface_flush(ssd);
#endif
glFlush();
cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_GL_DRAW_DONE, 0);
spice_qxl_gl_draw_async(&ssd->qxl, 0, 0,
Expand All @@ -870,6 +991,11 @@ static void spice_gl_update(DisplayChangeListener *dcl,
SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);

surface_gl_update_texture(ssd->gls, ssd->ds, x, y, w, h);
#if defined(CONFIG_IOSURFACE)
if (!qemu_console_is_gl_blocked(ssd->dcl.con)) {
spice_iosurface_blit(ssd, ssd->ds->texture, true);
}
#endif
ssd->gl_updates++;
}

Expand Down Expand Up @@ -897,6 +1023,21 @@ static void spice_gl_switch(DisplayChangeListener *dcl,
surface_gl_destroy_texture(ssd->gls, ssd->ds);
return;
}
#elif defined(CONFIG_IOSURFACE)
if (spice_iosurface_resize(ssd, width, height)) {
// FIXME: replace test code with mach ports!
int fds[2];
if (pipe(fds) != 0) {
}
fd = fds[0];
fourcc = 'BGRA';
CFRetain(ssd->iosurface);
write(fds[1], &ssd->iosurface, sizeof(ssd->iosurface));
close(fds[1]);
} else {
error_report("spice_gl_switch: failed to create IOSurface");
return;
}
#endif

trace_qemu_spice_gl_surface(ssd->qxl.id,
Expand Down Expand Up @@ -940,6 +1081,9 @@ static void qemu_spice_gl_scanout_disable(void *dg)
qemu_spice_gl_monitor_config(ssd, 0, 0, 0, 0);
ssd->have_surface = false;
ssd->have_scanout = false;
#if defined(CONFIG_IOSURFACE)
spice_iosurface_destroy(ssd);
#endif
#if defined(CONFIG_ANGLE)
ssd->backing_borrow = NULL;
ssd->backing_id = -1;
Expand Down Expand Up @@ -967,6 +1111,23 @@ static void qemu_spice_gl_scanout_texture(void *dg,

#if defined(CONFIG_GBM)
fd = egl_get_fd_for_texture(tex_id, &stride, &fourcc, NULL);
#elif defined(CONFIG_IOSURFACE)
if (spice_iosurface_resize(ssd, backing_width, backing_height)) {
// FIXME: replace test code with mach ports!
#if defined(CONFIG_ANGLE)
ssd->backing_borrow = backing_borrow;
ssd->backing_id = backing_id;
#endif
int fds[2];
pipe(fds);
fd = fds[0];
fourcc = 'BGRA';
CFRetain(ssd->iosurface);
write(fds[1], &ssd->iosurface, sizeof(ssd->iosurface));
close(fds[1]);
} else {
fd = -1;
}
#endif
if (fd < 0) {
fprintf(stderr, "%s: failed to get fd for texture\n", __func__);
Expand Down Expand Up @@ -1118,6 +1279,12 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl,
!y_0_top, x, y, 1.0, 1.0);
glFlush();
}
#elif defined(CONFIG_ANGLE) && defined(CONFIG_IOSURFACE)
GLuint tex_id = ssd->backing_borrow(ssd->backing_id, &y_0_top,
NULL, NULL);
spice_iosurface_blit(ssd, tex_id, !y_0_top);
spice_iosurface_flush(ssd);
//TODO: cursor stuff
#endif

trace_qemu_spice_gl_update(ssd->qxl.id, w, h, x, y);
Expand Down Expand Up @@ -1171,6 +1338,9 @@ static void qemu_spice_display_init_one(QemuConsole *con)
ssd->gls = qemu_gl_init_shader();
ssd->have_surface = false;
ssd->have_scanout = false;
#if defined(CONFIG_IOSURFACE)
ssd->iosurface = NULL;
#endif
#if defined(CONFIG_ANGLE)
ssd->esurface = EGL_NO_SURFACE;
ssd->backing_borrow = NULL;
Expand Down

0 comments on commit f384e2c

Please sign in to comment.