From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from [140.186.70.92] (port=34009 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1OCyLz-00067i-L8 for qemu-devel@nongnu.org; Fri, 14 May 2010 13:07:17 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.69) (envelope-from ) id 1OCyLv-0000ns-Bs for qemu-devel@nongnu.org; Fri, 14 May 2010 13:07:15 -0400 Received: from mail-vw0-f45.google.com ([209.85.212.45]:58189) by eggs.gnu.org with esmtp (Exim 4.69) (envelope-from ) id 1OCyLu-0000nj-Pg for qemu-devel@nongnu.org; Fri, 14 May 2010 13:07:11 -0400 Received: by vws4 with SMTP id 4so1170068vws.4 for ; Fri, 14 May 2010 10:07:10 -0700 (PDT) Message-ID: <4BED8337.2000605@codemonkey.ws> Date: Fri, 14 May 2010 12:07:03 -0500 From: Anthony Liguori MIME-Version: 1.0 Subject: Re: [Qemu-devel] [PATCH] Add QEMU DirectFB display driver References: <1273856330-15161-1-git-send-email-julian.pidancet@citrix.com> In-Reply-To: <1273856330-15161-1-git-send-email-julian.pidancet@citrix.com> Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Julian Pidancet Cc: qemu-devel@nongnu.org On 05/14/2010 11:58 AM, Julian Pidancet wrote: > This patch implements a DirectFB driver for QEMU. It allows Qemu to > draw a VM graphic output directly in the framebuffer of the host, > without having to rely on X11. > DirectFB also provides with a generic interface take advantage of graphic > hardware acceleration for a bunch of different supported cards. > > In this driver, the DirectFB library gives Qemu a pointer to mapped > video memory, which allows Qemu to update the display without extra copy. > In the case where the guest framebuffer is not compatible with the host > framebuffer, DirectFB surface blitting functions are used and can be > accellerated wherever it is possible with the hardware. > > DirectFB is a thin library heavily used in embedded or minimal systems > which don't require X11 overhead. One use case would be to build a > Xen-based client-class hypervisor, with a minimal dom0 running Qemu as > device-model. The dom0 could render the domU graphical outputs on the > physical screen using this patch without having X11 installed. > > The other solution would be to use the DirectFB driver for SDL which > would allow to do slightly the same as this patch. But that would mean > having to deal with an additional layer in the graphical stack, which is > not exactly what one wants from a performance or a complexity point of > view. > As an example, the SDL library gives no garantee that the surface > pointer returned by SDL_SetVideoMode(), if called with the > SDL_HWSURFACE, will be located in video memory [1], especially if the > SDL main surface is not fullscreen. For this reason, you can never > assume that SDL will not perform extra copy operations on your behalf > without notifying you. > But that's a feature. One would assume that SDL tries to allocate a hardware surface whenever it can. Can you provide some performance data to justify this since SDL provides the same ability? Regards, Anthony Liguori > [1] http://www.libsdl.org/cgi/docwiki.cgi/SDL_SetVideoMode > > Signed-off-by: Julian Pidancet > --- > Makefile | 4 + > Makefile.objs | 1 + > configure | 21 +++ > console.h | 3 + > directfb.c | 394 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ > qemu-options.hx | 10 ++ > sysemu.h | 1 + > vl.c | 12 ++ > 8 files changed, 446 insertions(+), 0 deletions(-) > create mode 100644 directfb.c > > diff --git a/Makefile b/Makefile > index eb9e02b..6932c81 100644 > --- a/Makefile > +++ b/Makefile > @@ -106,6 +106,10 @@ sdl.o: sdl.c keymaps.h sdl_keysym.h sdl_zoom.h > > sdl.o audio/sdlaudio.o sdl_zoom.o baum.o: QEMU_CFLAGS += $(SDL_CFLAGS) > > +directfb.o: directfb.c > + > +directfb.o: QEMU_CFLAGS += $(DIRECTFB_CFLAGS) > + > acl.o: acl.h acl.c > > vnc.h: vnc-tls.h vnc-auth-vencrypt.h vnc-auth-sasl.h keymaps.h > diff --git a/Makefile.objs b/Makefile.objs > index ecdd53e..0904b07 100644 > --- a/Makefile.objs > +++ b/Makefile.objs > @@ -102,6 +102,7 @@ common-obj-y += $(addprefix audio/, $(audio-obj-y)) > common-obj-y += keymaps.o > common-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o > common-obj-$(CONFIG_CURSES) += curses.o > +common-obj-$(CONFIG_DIRECTFB) += directfb.o > common-obj-y += vnc.o acl.o d3des.o > common-obj-y += vnc-encoding-zlib.o vnc-encoding-hextile.o > common-obj-y += iov.o > diff --git a/configure b/configure > index 36d028f..eb73415 100755 > --- a/configure > +++ b/configure > @@ -258,6 +258,7 @@ kvm="" > kvm_para="" > nptl="" > sdl="" > +directfb="no" > sparse="no" > uuid="" > vde="" > @@ -502,6 +503,10 @@ for opt do > ;; > --sysconfdir=*) sysconfdir="$optarg" > ;; > + --disable-directfb) directfb="no" > + ;; > + --enable-directfb) directfb="yes" > + ;; > --disable-sdl) sdl="no" > ;; > --enable-sdl) sdl="yes" > @@ -763,6 +768,8 @@ echo " --disable-strip disable stripping binaries" > echo " --disable-werror disable compilation abort on warning" > echo " --disable-sdl disable SDL" > echo " --enable-sdl enable SDL" > +echo " --disable-directfb disable DirectFB" > +echo " --enable-directfb enable DirectFB" > echo " --enable-cocoa enable COCOA (Mac OS X only)" > echo " --audio-drv-list=LIST set audio drivers list:" > echo " Available drivers: $audio_possible_drivers" > @@ -1062,6 +1069,15 @@ if test "$sparse" != "no" ; then > fi > > ########################################## > +# DirectFB probe > + > +if test "$directfb" = "yes" ; then > + directfb_libs=`directfb-config --libs` > + directfb_cflags=`directfb-config --cflags` > + libs_softmmu="$directfb_libs $libs_softmmu" > +fi > + > +########################################## > # SDL probe > > if $pkgconfig sdl --modversion>/dev/null 2>&1; then > @@ -1999,6 +2015,7 @@ if test "$darwin" = "yes" ; then > echo "Cocoa support $cocoa" > fi > echo "SDL support $sdl" > +echo "DirectFB support $directfb" > echo "curses support $curses" > echo "curl support $curl" > echo "check support $check_utests" > @@ -2169,6 +2186,10 @@ fi > if test "$cocoa" = "yes" ; then > echo "CONFIG_COCOA=y">> $config_host_mak > fi > +if test "$directfb" = "yes" ; then > + echo "CONFIG_DIRECTFB=y">> $config_host_mak > + echo "DIRECTFB_CFLAGS=$directfb_cflags">> $config_host_mak > +fi > if test "$curses" = "yes" ; then > echo "CONFIG_CURSES=y">> $config_host_mak > fi > diff --git a/console.h b/console.h > index 6def115..d1dd211 100644 > --- a/console.h > +++ b/console.h > @@ -335,6 +335,9 @@ void qemu_console_resize(DisplayState *ds, int width, int height); > void qemu_console_copy(DisplayState *ds, int src_x, int src_y, > int dst_x, int dst_y, int w, int h); > > +/* directfb.c */ > +void directfb_display_init(DisplayState *ds); > + > /* sdl.c */ > void sdl_display_init(DisplayState *ds, int full_screen, int no_frame); > > diff --git a/directfb.c b/directfb.c > new file mode 100644 > index 0000000..6dea99a > --- /dev/null > +++ b/directfb.c > @@ -0,0 +1,394 @@ > +/* > + * QEMU DirectFB display driver > + * > + * Copyright (c) 2010 Citrix Systems, Inc. > + * > + * Permission is hereby granted, free of charge, to any person obtaining a copy > + * of this software and associated documentation files (the "Software"), to deal > + * in the Software without restriction, including without limitation the rights > + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell > + * copies of the Software, and to permit persons to whom the Software is > + * furnished to do so, subject to the following conditions: > + * > + * The above copyright notice and this permission notice shall be included in > + * all copies or substantial portions of the Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, > + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN > + * THE SOFTWARE. > + */ > + > +#include > +#include > + > +#include > + > +static IDirectFB *dfb = NULL; > +static IDirectFBSurface *primary = NULL; > +static IDirectFBEventBuffer *events = NULL; > +static IDirectFBSurface *guest = NULL; > + > +static void *screen_data = NULL; > +static int screen_pitch = 0; > +static int screen_bpp = 0; > +static int screen_width = 0; > +static int screen_height = 0; > +static int scaling = 0; > + > +#define DIRECTFB_IS_VIDEO_PTR(p) \ > + (p>= (uint8_t *) screen_data&& \ > + p< (uint8_t *) screen_data + screen_height * screen_pitch) > + > +static DFBSurfacePixelFormat directfb_bpp_to_pixelformat(int bpp) > +{ > + switch (bpp) { > + case 16: > + return DSPF_RGB16; > + case 24: > + return DSPF_RGB24; > + case 32: > + return DSPF_RGB32; > + default: > + return DSPF_UNKNOWN; > + } > +} > + > +static void directfb_clearscreen(void) > +{ > + if (screen_data != NULL) { > + /* Surface is locked */ > + memset(screen_data, 0x0, > + screen_pitch * screen_height); > + } else { > + primary->SetColor(primary, 0x0, 0x0, 0x0, 0x0); > + primary->FillRectangle(primary, 0, 0, screen_width, screen_height); > + } > +} > + > +static void directfb_update(struct DisplayState *s, int x, int y, int w, int h) > +{ > + DFBRegion region = {x, y, x + w, y + h}; > + > + if (guest) { > + if (scaling) { > + primary->StretchBlit(primary, guest, NULL, NULL); > + } else { > + int xoff = (screen_width - ds_get_width(s)) / 2; > + int yoff = (screen_height - ds_get_height(s)) / 2; > + > + primary->Blit(primary, guest, NULL, xoff, yoff); > + > + region.x1 += xoff; > + region.y1 += yoff; > + region.x2 += xoff; > + region.x2 += yoff; > + } > + } > + > + primary->Flip(primary,®ion, DSFLIP_NONE); > +} > + > +static void directfb_setdata(DisplayState *s) > +{ > + DFBSurfaceDescription dsc; > + > + if (guest) { > + guest->Release(guest); > + guest = NULL; > + } > + > + dsc.flags = DSDESC_WIDTH | DSDESC_HEIGHT | > + DSDESC_PIXELFORMAT | DSDESC_PREALLOCATED; > + dsc.width = ds_get_width(s); > + dsc.height = ds_get_height(s); > + dsc.pixelformat = directfb_bpp_to_pixelformat(ds_get_bits_per_pixel(s)); > + dsc.preallocated[0].data = ds_get_data(s); > + dsc.preallocated[0].pitch = ds_get_linesize(s); > + > + dfb->CreateSurface(dfb,&dsc,&guest); > +} > + > +static void directfb_resize(struct DisplayState *s) > +{ > + directfb_clearscreen(); > + > + if (scaling || ds_get_bits_per_pixel(s) != screen_bpp || > + ds_get_linesize(s) != screen_pitch || > + !DIRECTFB_IS_VIDEO_PTR(ds_get_data(s))) { > + > + directfb_setdata(s); > + } else { > + if (guest) { > + guest->Release(guest); > + guest = NULL; > + } > + } > +} > + > +static int directfb_buttons_state(DFBInputEvent *ev) > +{ > + int buttons = 0; > + > + if (ev->buttons& DIBM_LEFT) { > + buttons |= MOUSE_EVENT_LBUTTON; > + } > + if (ev->buttons& DIBM_RIGHT) { > + buttons |= MOUSE_EVENT_RBUTTON; > + } > + if (ev->buttons& DIBM_MIDDLE) { > + buttons |= MOUSE_EVENT_MBUTTON; > + } > + > + return buttons; > +} > + > +static void directfb_put_keycode(char keycode, int up) > +{ > + int scancode = keycode; > + > + /* Pause/Break */ > + if (keycode == 119) { > + scancode = 0x45; > + kbd_put_keycode(0xe1); > + kbd_put_keycode(0x1d | up ? 0x80 : 0x0); > + } else { > + /* grey key */ > + if (keycode>= 0x60&& keycode< 0x70) { > + const char esc[16] = {0x1c, 0x1d, 0x35, 0x37, > + 0x38, 0x46, 0x47, 0x48, > + 0x49, 0x4b, 0x4d, 0x4f, > + 0x50, 0x51, 0x52, 0x53}; > + scancode = esc[keycode - 0x60]; > + kbd_put_keycode(0xe0); > + > + /* PrintScreen */ > + if (keycode == 99) { > + scancode = 0x37; > + kbd_put_keycode(0x2a | up ? 0x80 : 0x0); > + kbd_put_keycode(0xe0); > + } > + } > + } > + > + kbd_put_keycode(scancode | (up ? 0x80 : 0x0)); > +} > + > +static void directfb_toggle_fullscreen(struct DisplayState *ds) > +{ > + scaling = !scaling; > + > + vga_hw_invalidate(); > + vga_hw_update(); > +} > + > +static void directfb_refresh(struct DisplayState *s) > +{ > + DFBInputEvent ev; > + > + vga_hw_update(); > + > + while (events->GetEvent(events, DFB_EVENT(&ev)) == DFB_OK) { > + switch (ev.type) { > + case DIET_KEYRELEASE: > + directfb_put_keycode(ev.key_code, 1); > + break; > + case DIET_KEYPRESS: > + /* Toggle centered/fullscreen */ > + if ((ev.modifiers& DIMM_CONTROL)&& > + (ev.modifiers& DIMM_ALT)&& > + (ev.key_id == DIKI_ENTER)) { > + directfb_toggle_fullscreen(s); > + break; > + } > + directfb_put_keycode(ev.key_code, 0); > + break; > + case DIET_BUTTONPRESS: > + case DIET_BUTTONRELEASE: > + case DIET_AXISMOTION: > + { > + int buttons = directfb_buttons_state(&ev); > + int dx = 0; > + int dy = 0; > + int dz = 0; > + > + if (ev.type == DIET_AXISMOTION) { > + if (ev.axis == DIAI_X) { > + dx = ev.axisrel; > + } > + if (ev.axis == DIAI_Y) { > + dy = ev.axisrel; > + } > + if (ev.axis == DIAI_Z) { > + dz = ev.axisrel; > + } > + } > + > + kbd_mouse_event(dx, dy, dz, buttons); > + break; > + } > + case DIET_UNKNOWN: > + default: > + break; > + > + } > + } > +} > + > +static DisplaySurface* directfb_create_displaysurface(int width, int height) > +{ > + DisplaySurface *surface = (DisplaySurface*) qemu_mallocz(sizeof(DisplaySurface)); > + DFBSurfacePixelFormat spf; > + surface->width = width; > + surface->height = height; > + > + primary->GetPixelFormat(primary,&spf); > + > + if (scaling) { > + int bytes_per_pixel = DFB_BYTES_PER_PIXEL(spf); > + > + if (bytes_per_pixel != 2&& bytes_per_pixel != 4) { > + bytes_per_pixel = 4; > + } > + > + surface->pf = qemu_default_pixelformat(8 * bytes_per_pixel); > + surface->linesize = width * bytes_per_pixel; > + > + surface->flags = QEMU_ALLOCATED_FLAG; > + surface->data = qemu_mallocz(surface->linesize * surface->height); > + } else { > + primary->Lock(primary, DSLF_READ | DSLF_WRITE,&screen_data,&screen_pitch); > + surface->pf = qemu_default_pixelformat(screen_bpp); > + surface->flags = QEMU_REALPIXELS_FLAG; > + surface->linesize = screen_pitch; > + surface->data = screen_data + > + ((screen_height - height) / 2) * screen_pitch + > + ((screen_width - width) / 2) * (screen_bpp / 8); > + } > + > + return surface; > +} > + > +static void directfb_free_displaysurface(DisplaySurface *surface) > +{ > + if (surface == NULL) > + return; > + > + if (surface->flags& QEMU_ALLOCATED_FLAG) { > + qemu_free(surface->data); > + } else if (surface->flags& QEMU_REALPIXELS_FLAG) { > + primary->Unlock(primary); > + screen_data = NULL; > + screen_pitch = 0; > + } > + > + surface->data = NULL; > + > + qemu_free(surface); > +} > + > +static DisplaySurface* directfb_resize_displaysurface(DisplaySurface *surface, > + int width, > + int height) > +{ > + directfb_free_displaysurface(surface); > + return directfb_create_displaysurface(width, height); > +} > + > +static DFBEnumerationResult directfb_attach_inputdevice(DFBInputDeviceID device_id, > + DFBInputDeviceDescription desc, > + void *data) > +{ > + if (!strcmp(desc.vendor, "Linux")) { > + return DFENUM_OK; > + } > + > + if (desc.type == DIDID_KEYBOARD || desc.type | DIDTF_MOUSE) { > + IDirectFBInputDevice *device; > + > + dfb->GetInputDevice(dfb, device_id,&device); > + > + if (events == NULL) { > + device->CreateEventBuffer(device,&events); > + } else { > + device->AttachEventBuffer(device, events); > + } > + } > + > + return DFENUM_OK; > +} > + > +void directfb_display_init(DisplayState *ds) > +{ > + DisplayChangeListener *dcl; > + DisplayAllocator *da; > + DFBResult status; > + DFBSurfaceDescription dsc; > + DFBSurfaceCapabilities caps; > + DFBSurfacePixelFormat spf; > + > + /* > + * Prevent DirectFB to read qemu command line argument in procfs and > + * parse it. > + */ > + char prog_name[] = "qemu"; > + char *prog_argv[] = {prog_name}; > + char **dfb_argv = prog_argv; > + int dfb_argc = 1; > + > + status = DirectFBInit(&dfb_argc,&dfb_argv); > + if (status != DFB_OK) { > + fprintf(stderr, "Could not initialize DirectFB(%d) - exiting\n", status); > + exit(1); > + } > + > + DirectFBCreate(&dfb); > + dfb->SetCooperativeLevel(dfb, DFSCL_FULLSCREEN); > + dsc.flags = DSDESC_CAPS; > + dsc.caps = DSCAPS_PRIMARY | DSCAPS_VIDEOONLY | DSCAPS_SHARED; > + status = dfb->CreateSurface(dfb,&dsc,&primary); > + > + if (status != DFB_OK) { > + fprintf(stderr, "Could not create DirectFB surface(%d) - exiting\n", status); > + exit(1); > + } > + > + /* Double check surface capabilities */ > + primary->GetCapabilities(primary,&caps); > + if ((caps& dsc.caps) != dsc.caps || > + caps& DSCAPS_FLIPPING || caps& DSCAPS_INTERLACED || > + caps& DSCAPS_SYSTEMONLY) { > + fprintf(stderr, "Wrong DirectFB surface capabilities - exiting\n"); > + exit(1); > + } > + > + primary->GetSize(primary,&screen_width,&screen_height); > + primary->GetPixelFormat(primary,&spf); > + screen_bpp = DFB_BITS_PER_PIXEL(spf); > + > + dfb->EnumInputDevices(dfb, directfb_attach_inputdevice, NULL); > + > + fprintf(stderr, "Initialized QEMU DirectFB driver. (%dx%d)\n", > + screen_width, screen_height); > + > + dcl = qemu_mallocz(sizeof(DisplayChangeListener)); > + dcl->dpy_update = directfb_update; > + dcl->dpy_resize = directfb_resize; > + dcl->dpy_refresh = directfb_refresh; > + dcl->dpy_setdata = directfb_setdata; > + register_displaychangelistener(ds, dcl); > + > + da = qemu_mallocz(sizeof(DisplayAllocator)); > + da->create_displaysurface = directfb_create_displaysurface; > + da->resize_displaysurface = directfb_resize_displaysurface; > + da->free_displaysurface = directfb_free_displaysurface; > + > + directfb_clearscreen(); > + > + if (register_displayallocator(ds, da) == da) { > + dpy_resize(ds); > + } > +} > diff --git a/qemu-options.hx b/qemu-options.hx > index 12f6b51..a4bdfbe 100644 > --- a/qemu-options.hx > +++ b/qemu-options.hx > @@ -608,6 +608,16 @@ QEMU can display the VGA output when in text mode using a > curses/ncurses interface. Nothing is displayed in graphical mode. > ETEXI > > +#ifdef CONFIG_DIRECTFB > +DEF("directfb", 0, QEMU_OPTION_directfb, > + "-directfb enable DirectFB\n") > +#endif > +STEXI > +@item -directfb > +@findex -directfb > +Enable DirectFB. > +ETEXI > + > #ifdef CONFIG_SDL > DEF("no-frame", 0, QEMU_OPTION_no_frame, > "-no-frame open SDL window without a frame and window decorations\n", > diff --git a/sysemu.h b/sysemu.h > index fa921df..a2cd5b0 100644 > --- a/sysemu.h > +++ b/sysemu.h > @@ -98,6 +98,7 @@ typedef enum DisplayType > DT_CURSES, > DT_SDL, > DT_VNC, > + DT_DIRECTFB, > DT_NOGRAPHIC, > } DisplayType; > > diff --git a/vl.c b/vl.c > index 85bcc84..e6235fa 100644 > --- a/vl.c > +++ b/vl.c > @@ -3199,6 +3199,11 @@ int main(int argc, char **argv, char **envp) > case QEMU_OPTION_full_screen: > full_screen = 1; > break; > +#ifdef CONFIG_DIRECTFB > + case QEMU_OPTION_directfb: > + display_type = DT_DIRECTFB; > + break; > +#endif > #ifdef CONFIG_SDL > case QEMU_OPTION_no_frame: > no_frame = 1; > @@ -3765,6 +3770,8 @@ int main(int argc, char **argv, char **envp) > if (display_type == DT_DEFAULT) { > #if defined(CONFIG_SDL) || defined(CONFIG_COCOA) > display_type = DT_SDL; > +#elif defined(CONFIG_DIRECTFB) > + display_type = DT_DIRECTFB; > #else > display_type = DT_VNC; > vnc_display = "localhost:0,to=99"; > @@ -3781,6 +3788,11 @@ int main(int argc, char **argv, char **envp) > curses_display_init(ds, full_screen); > break; > #endif > +#if defined(CONFIG_DIRECTFB) > + case DT_DIRECTFB: > + directfb_display_init(ds); > + break; > +#endif > #if defined(CONFIG_SDL) > case DT_SDL: > sdl_display_init(ds, full_screen, no_frame); >