On 06/16/2010 01:44 PM, Stefano Stabellini wrote: > > the patch still doesn't use the display allocator interface, but it > shouldn't be difficult to implement support for it on top of this patch, > so it is fine by me. >
This patch adds display allocator support in the fbdev driver. This way we avoid memcpying when the guest surface is compatible with the physical surface. diff --git a/fbdev.c b/fbdev.c index 54f2381..83104c0 100644 --- a/fbdev.c +++ b/fbdev.c @@ -69,11 +69,10 @@ static int fb_switch_state = FB_ACTIVE; static DisplayChangeListener *dcl; static QemuPfConv *conv; static PixelFormat fbpf; -static int resize_screen; -static int redraw_screen; static int cx, cy, cw, ch; static int debug = 0; static Notifier exit_notifier; +uint8_t *guest_surface; /* fwd decls */ static int fbdev_activate_vt(int tty, int vtno, bool wait); @@ -786,10 +785,10 @@ static void fbdev_render(DisplayState *ds, int x, int y, int w, int h) uint8_t *src; int line; - if (!conv) + if (!conv || !guest_surface) return; - src = ds_get_data(ds) + y * ds_get_linesize(ds) + src = guest_surface + y * ds_get_linesize(ds) + x * ds_get_bytes_per_pixel(ds); dst = fb_mem + y * fb_fix.line_length + x * fbpf.bytes_per_pixel; @@ -819,46 +818,50 @@ static void fbdev_update(DisplayState *ds, int x, int y, int w, int h) if (fb_switch_state != FB_ACTIVE) return; - if (resize_screen) { - if (debug) - fprintf(stderr, "%s: handle resize\n", __FUNCTION__); - resize_screen = 0; - cx = 0; cy = 0; - cw = ds_get_width(ds); - ch = ds_get_height(ds); - if (ds_get_width(ds) < fb_var.xres) { - cx = (fb_var.xres - ds_get_width(ds)) / 2; - } - if (ds_get_height(ds) < fb_var.yres) { - cy = (fb_var.yres - ds_get_height(ds)) / 2; - } + if (guest_surface != NULL) { + fbdev_render(ds, x, y, w, h); + } +} - if (conv) { - qemu_pf_conv_put(conv); - } - conv = qemu_pf_conv_get(&fbpf, &ds->surface->pf); - if (conv == NULL) { - fprintf(stderr, "fbdev: unsupported PixelFormat conversion\n"); - } +static void fbdev_setdata(DisplayState *ds) +{ + if (conv) { + qemu_pf_conv_put(conv); } - if (redraw_screen) { - if (debug) - fprintf(stderr, "%s: handle redraw\n", __FUNCTION__); - redraw_screen = 0; - fbdev_cls(); - x = 0; y = 0; w = ds_get_width(ds); h = ds_get_height(ds); + conv = qemu_pf_conv_get(&fbpf, &ds->surface->pf); + if (conv == NULL) { + fprintf(stderr, "fbdev: unsupported PixelFormat conversion\n"); } - fbdev_render(ds, x, y, w, h); + guest_surface = ds_get_data(ds); } static void fbdev_resize(DisplayState *ds) { - if (debug) - fprintf(stderr, "%s: request resize+redraw\n", __FUNCTION__); - resize_screen++; - redraw_screen++; + int is_video_ptr; + + if (fb_switch_state == FB_ACTIVE) { + fbdev_cls(); + } + + is_video_ptr = ds_get_data(ds) >= fb_mem + fb_mem_offset && + ds_get_data(ds) < fb_mem + fb_fix.smem_len + fb_mem_offset; + + if (ds_get_bits_per_pixel(ds) != fbpf.bits_per_pixel || + ds_get_linesize(ds) != fb_fix.line_length || + !is_video_ptr) { + cx = 0; cy = 0; + if (ds_get_width(ds) < fb_var.xres) { + cx = (fb_var.xres - ds_get_width(ds)) / 2; + } + if (ds_get_height(ds) < fb_var.yres) { + cy = (fb_var.yres - ds_get_height(ds)) / 2; + } + fbdev_setdata(ds); + } else { + guest_surface = NULL; + } } static void fbdev_refresh(DisplayState *ds) @@ -866,21 +869,17 @@ static void fbdev_refresh(DisplayState *ds) switch (fb_switch_state) { case FB_REL_REQ: fbdev_switch_release(); + vga_hw_invalidate(); case FB_INACTIVE: return; case FB_ACQ_REQ: fbdev_switch_acquire(); - redraw_screen++; - if (debug) - fprintf(stderr, "%s: request redraw\n", __FUNCTION__); + vga_hw_invalidate(); case FB_ACTIVE: break; } vga_hw_update(); - if (redraw_screen) { - fbdev_update(ds, 0, 0, 0, 0); - } } static void fbdev_exit_notifier(Notifier *notifier) @@ -888,8 +887,58 @@ static void fbdev_exit_notifier(Notifier *notifier) fbdev_cleanup(); } +static DisplaySurface *fbdev_create_displaysurface(int width, int height) +{ + DisplaySurface *surface = qemu_mallocz(sizeof (DisplaySurface)); + + surface->width = width; + surface->height = height; + + surface->pf = fbpf; + surface->linesize = fb_fix.line_length; + + if (fb_switch_state == FB_INACTIVE) { + surface->flags = QEMU_ALLOCATED_FLAG; + surface->data = qemu_mallocz(surface->linesize * surface->height); + } else { + surface->flags = QEMU_REALPIXELS_FLAG; + surface->data = fb_mem; + + if (width < fb_var.xres) + surface->data += ((fb_var.xres - width) / 2) * fbpf.bytes_per_pixel; + if (height < fb_var.yres) + surface->data += ((fb_var.yres - height) / 2) * fb_fix.line_length; + } + + return surface; +} + +static void fbdev_free_displaysurface(DisplaySurface *surface) +{ + if (surface == NULL) + return; + + if (surface->flags & QEMU_ALLOCATED_FLAG) { + qemu_free(surface->data); + } + + surface->data = NULL; + + qemu_free(surface); +} + +static DisplaySurface *fbdev_resize_displaysurface(DisplaySurface *surface, + int width, + int height) +{ + fbdev_free_displaysurface(surface); + return fbdev_create_displaysurface(width, height); +} + void fbdev_display_init(DisplayState *ds, const char *device) { + DisplayAllocator *da; + if (dcl != NULL) { if (debug) fprintf(stderr, "%s: already active\n", __FUNCTION__); @@ -910,7 +959,17 @@ void fbdev_display_init(DisplayState *ds, const char *device) dcl->dpy_update = fbdev_update; dcl->dpy_resize = fbdev_resize; dcl->dpy_refresh = fbdev_refresh; + dcl->dpy_setdata = fbdev_setdata; register_displaychangelistener(ds, dcl); + + da = qemu_mallocz(sizeof (DisplayAllocator)); + da->create_displaysurface = fbdev_create_displaysurface; + da->resize_displaysurface = fbdev_resize_displaysurface; + da->free_displaysurface = fbdev_free_displaysurface; + + if (register_displayallocator(ds, da) == da) { + dpy_resize(ds); + } } void fbdev_display_uninit(void) -- Julian Pidancet <julian.pidan...@citrix.com>