[Spice-devel] [video-qxl 4/6] Add a deferred frames mode.
Jeremy White
jwhite at codeweavers.com
Thu Jan 10 07:44:45 PST 2013
This renders all operations to a frame buffer,
and sends updates periodically.
Signed-off-by: Jeremy White <jwhite at codeweavers.com>
---
src/Makefile.am | 1 +
src/dfps.c | 290 +++++++++++++++++++++++++++++++++++++++++++++++++++++
src/dfps.h | 42 ++++++++
src/qxl.h | 5 +
src/qxl_driver.c | 77 ++++++++------
src/qxl_surface.c | 52 ++++++++++
6 files changed, 438 insertions(+), 29 deletions(-)
create mode 100644 src/dfps.c
create mode 100644 src/dfps.h
diff --git a/src/Makefile.am b/src/Makefile.am
index 0d70c83..6950ba5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -90,5 +90,6 @@ spiceqxl_drv_la_SOURCES = \
murmurhash3.h \
qxl_cursor.c \
qxl_edid.c \
+ dfps.c \
compat-api.h
endif
diff --git a/src/dfps.c b/src/dfps.c
new file mode 100644
index 0000000..fca51ff
--- /dev/null
+++ b/src/dfps.c
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2012 CodeWeavers, 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 (including the next
+ * paragraph) 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.
+ *
+ * Authors:
+ * Jeremy White <jwhite at codeweavers.com>
+ */
+
+/*----------------------------------------------------------------------------
+ Deferred Frames Per Second (dfps) Support File
+
+ By default, The xorg-video-qxl driver transmits all of the video
+ operation across the wire. While this has the greatest fidelity, and
+ lends itself well to a variety of optimizations and behavior tuning,
+ it does not always use bandwidth efficiently.
+
+ This file implements a 'deferred frames' mode which instead renders
+ everything to a frame buffer, and then periodically sends only updated
+ portions of the screen.
+
+ For some use cases, this proves to be far more efficient with network
+ resources.
+----------------------------------------------------------------------------*/
+
+#include <xorg-server.h>
+#include "qxl.h"
+#include "dfps.h"
+
+struct dfps_info_t
+{
+ RegionRec updated_region;
+
+ PixmapPtr copy_src;
+ Pixel solid_pixel;
+};
+
+void dfps_ticker(void *opaque)
+{
+ qxl_screen_t *qxl = (qxl_screen_t *) opaque;
+ dfps_info_t *info = NULL;
+ PixmapPtr pixmap;
+
+ pixmap = qxl->pScrn->pScreen->GetScreenPixmap(qxl->pScrn->pScreen);
+ if (pixmap)
+ info = dfps_get_info(pixmap);
+ if (info)
+ {
+ qxl_surface_upload_primary_regions(qxl, pixmap, &info->updated_region);
+ RegionUninit(&info->updated_region);
+ RegionInit(&info->updated_region, NULL, 0);
+ }
+ qxl->core->timer_start(qxl->frames_timer, 1000 / qxl->deferred_fps);
+}
+
+
+static Bool unaccel (void)
+{
+ return FALSE;
+}
+
+static Bool dfps_prepare_solid (PixmapPtr pixmap, int alu, Pixel planemask, Pixel fg)
+{
+ dfps_info_t *info;
+
+ if (!(info = dfps_get_info (pixmap)))
+ return FALSE;
+
+ info->solid_pixel = fg;
+
+ return TRUE;
+}
+
+static void dfps_solid (PixmapPtr pixmap, int x_1, int y_1, int x_2, int y_2)
+{
+ FbBits *bits;
+ int stride;
+ int bpp;
+ int xoff, yoff;
+ struct pixman_box16 box;
+ RegionPtr region;
+ Bool throwaway_bool;
+ dfps_info_t *info;
+
+ if (!(info = dfps_get_info (pixmap)))
+ return;
+
+ /* Draw to the frame buffer */
+ fbGetDrawable((DrawablePtr)pixmap, bits, stride, bpp, xoff, yoff);
+ pixman_fill((uint32_t *) bits, stride, bpp, x_1 + xoff, y_1 + yoff, x_2 - x_1, y_2 - y_1, info->solid_pixel);
+ fbValidateDrawable(pixmap);
+ fbFinishAccess(pixmap);
+
+ /* Track the updated region */
+ box.x1 = x_1; box.x2 = x_2; box.y1 = y_1; box.y2 = y_2;
+ region = RegionCreate(&box, 1);
+ RegionAppend(&info->updated_region, region);
+ RegionValidate(&info->updated_region, &throwaway_bool);
+ RegionUninit(region);
+ return;
+}
+
+static void dfps_done_solid (PixmapPtr pixmap)
+{
+}
+
+static Bool dfps_prepare_copy (PixmapPtr source, PixmapPtr dest,
+ int xdir, int ydir, int alu,
+ Pixel planemask)
+{
+ dfps_info_t *info;
+
+ if (!(info = dfps_get_info (dest)))
+ return FALSE;
+
+ info->copy_src = source;
+ return TRUE;
+}
+
+static void dfps_copy (PixmapPtr dest,
+ int src_x1, int src_y1,
+ int dest_x1, int dest_y1,
+ int width, int height)
+{
+ struct pixman_box16 box;
+ RegionPtr region;
+ Bool throwaway_bool;
+ GCPtr pgc;
+
+ dfps_info_t *info;
+
+ if (!(info = dfps_get_info (dest)))
+ return;
+
+ /* Render into to the frame buffer */
+ pgc = GetScratchGC(dest->drawable.depth, dest->drawable.pScreen);
+ ValidateGC(&dest->drawable, pgc);
+ fbCopyArea(&info->copy_src->drawable, &dest->drawable, pgc, src_x1, src_y1, width, height, dest_x1, dest_y1);
+ FreeScratchGC(pgc);
+
+ /* Update the tracking region */
+ box.x1 = dest_x1; box.x2 = dest_x1 + width; box.y1 = dest_y1; box.y2 = dest_y1 + height;
+ region = RegionCreate(&box, 1);
+ RegionAppend(&info->updated_region, region);
+ RegionValidate(&info->updated_region, &throwaway_bool);
+ RegionUninit(region);
+}
+
+static void dfps_done_copy (PixmapPtr dest)
+{
+}
+
+static Bool dfps_put_image (PixmapPtr dest, int x, int y, int w, int h,
+ char *src, int src_pitch)
+{
+ struct pixman_box16 box;
+ RegionPtr region;
+ Bool throwaway_bool;
+ dfps_info_t *info;
+
+ if (!(info = dfps_get_info (dest)))
+ return FALSE;
+
+ box.x1 = x; box.x2 = x + w; box.y1 = y; box.y2 = y + h;
+ region = RegionCreate(&box, 1);
+ RegionAppend(&info->updated_region, region);
+ RegionValidate(&info->updated_region, &throwaway_bool);
+ RegionUninit(region);
+
+ /* We can avoid doing the put image ourselves, as the uxa driver
+ will fall back and do it for us if we return false */
+ return FALSE;
+}
+
+
+static Bool dfps_prepare_access (PixmapPtr pixmap, RegionPtr region, uxa_access_t requested_access)
+{
+ fbPrepareAccess(pixmap);
+ if (requested_access == UXA_ACCESS_RW)
+ {
+ dfps_info_t *info;
+ Bool throwaway_bool;
+
+ if (!(info = dfps_get_info (pixmap)))
+ return FALSE;
+ RegionAppend(&info->updated_region, region);
+ RegionValidate(&info->updated_region, &throwaway_bool);
+ }
+ return TRUE;
+}
+
+static void dfps_finish_access (PixmapPtr pixmap)
+{
+ fbFinishAccess(pixmap);
+}
+
+static Bool dfps_pixmap_is_offscreen (PixmapPtr pixmap)
+{
+ return !!dfps_get_info(pixmap);
+}
+
+static void dfps_set_screen_pixmap (PixmapPtr pixmap)
+{
+ pixmap->drawable.pScreen->devPrivate = pixmap;
+}
+
+static PixmapPtr dfps_create_pixmap (ScreenPtr screen, int w, int h, int depth, unsigned usage)
+{
+ PixmapPtr pixmap;
+ dfps_info_t *info;
+
+ info = calloc(1, sizeof(*info));
+ if (!info)
+ return FALSE;
+
+ pixmap = fbCreatePixmap (screen, w, h, depth, usage);
+ if (pixmap)
+ dfps_set_info(pixmap, info);
+ else
+ free(info);
+
+ return pixmap;
+}
+
+static Bool dfps_destroy_pixmap (PixmapPtr pixmap)
+{
+ if (pixmap->refcnt == 1)
+ {
+ dfps_info_t *info = dfps_get_info (pixmap);
+ if (info)
+ free(info);
+ dfps_set_info(pixmap, NULL);
+ }
+
+ return fbDestroyPixmap (pixmap);
+}
+
+void dfps_set_uxa_functions(qxl_screen_t *qxl, ScreenPtr screen)
+{
+ /* Solid fill */
+ //qxl->uxa->check_solid = dfps_check_solid;
+ qxl->uxa->prepare_solid = dfps_prepare_solid;
+ qxl->uxa->solid = dfps_solid;
+ qxl->uxa->done_solid = dfps_done_solid;
+
+ /* Copy */
+ //qxl->uxa->check_copy = qxl_check_copy;
+ qxl->uxa->prepare_copy = dfps_prepare_copy;
+ qxl->uxa->copy = dfps_copy;
+ qxl->uxa->done_copy = dfps_done_copy;
+
+ /* Composite */
+ qxl->uxa->check_composite = (typeof(qxl->uxa->check_composite))unaccel;
+ qxl->uxa->check_composite_target = (typeof(qxl->uxa->check_composite_target))unaccel;
+ qxl->uxa->check_composite_texture = (typeof(qxl->uxa->check_composite_texture))unaccel;
+ qxl->uxa->prepare_composite = (typeof(qxl->uxa->prepare_composite))unaccel;
+ qxl->uxa->composite = (typeof(qxl->uxa->composite))unaccel;
+ qxl->uxa->done_composite = (typeof(qxl->uxa->done_composite))unaccel;
+
+ /* PutImage */
+ qxl->uxa->put_image = dfps_put_image;
+
+ /* Prepare access */
+ qxl->uxa->prepare_access = dfps_prepare_access;
+ qxl->uxa->finish_access = dfps_finish_access;
+
+ /* General screen information */
+ qxl->uxa->pixmap_is_offscreen = dfps_pixmap_is_offscreen;
+
+ screen->SetScreenPixmap = dfps_set_screen_pixmap;
+ screen->CreatePixmap = dfps_create_pixmap;
+ screen->DestroyPixmap = dfps_destroy_pixmap;
+}
diff --git a/src/dfps.h b/src/dfps.h
new file mode 100644
index 0000000..ea38a46
--- /dev/null
+++ b/src/dfps.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2012 CodeWeavers, 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 (including the next
+ * paragraph) 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.
+ *
+ */
+
+typedef struct dfps_info_t dfps_info_t;
+
+void dfps_ticker(void *opaque);
+void dfps_set_uxa_functions(qxl_screen_t *qxl, ScreenPtr screen);
+
+static inline dfps_info_t *dfps_get_info (PixmapPtr pixmap)
+{
+#if HAS_DEVPRIVATEKEYREC
+ return dixGetPrivate(&pixmap->devPrivates, &uxa_pixmap_index);
+#else
+ return dixLookupPrivate(&pixmap->devPrivates, &uxa_pixmap_index);
+#endif
+}
+
+static inline void dfps_set_info (PixmapPtr pixmap, dfps_info_t *info)
+{
+ dixSetPrivate(&pixmap->devPrivates, &uxa_pixmap_index, info);
+}
diff --git a/src/qxl.h b/src/qxl.h
index 1389658..427bb55 100644
--- a/src/qxl.h
+++ b/src/qxl.h
@@ -240,6 +240,9 @@ struct _qxl_screen_t
/* XSpice specific */
struct QXLRom shadow_rom; /* Parameter RAM */
SpiceServer * spice_server;
+ SpiceCoreInterface *core;
+ SpiceTimer * frames_timer;
+
QXLWorker * worker;
int worker_running;
QXLInstance display_sin;
@@ -439,6 +442,8 @@ get_ram_header (qxl_screen_t *qxl)
((uint8_t *)qxl->ram + qxl->rom->ram_header_offset);
}
+void qxl_surface_upload_primary_regions(qxl_screen_t *qxl, PixmapPtr pixmap, RegionRec *r);
+
/*
* Images
*/
diff --git a/src/qxl_driver.c b/src/qxl_driver.c
index 72beb0f..8ba2efa 100644
--- a/src/qxl_driver.c
+++ b/src/qxl_driver.c
@@ -56,6 +56,7 @@
#include "spiceqxl_inputs.h"
#include "spiceqxl_io_port.h"
#include "spiceqxl_spice_server.h"
+#include "dfps.h"
#endif /* XSPICE */
extern void compat_init_scrn (ScrnInfoPtr);
@@ -916,14 +917,10 @@ set_screen_pixmap_header (ScreenPtr pScreen)
if (pPixmap)
{
- ErrorF ("new stride: %d (display width: %d, bpp: %d)\n",
- qxl->pScrn->displayWidth * qxl->bytes_per_pixel,
- qxl->pScrn->displayWidth, qxl->bytes_per_pixel);
-
pScreen->ModifyPixmapHeader (pPixmap,
qxl->primary_mode.x_res, qxl->primary_mode.y_res,
-1, -1,
- qxl->pScrn->displayWidth * qxl->bytes_per_pixel,
+ qxl->primary_mode.x_res * qxl->bytes_per_pixel,
qxl_surface_get_host_bits(qxl->primary));
}
else
@@ -953,7 +950,7 @@ qxl_resize_primary_to_virtual (qxl_screen_t *qxl)
{
ScreenPtr pScreen;
long new_surface0_size;
-
+
if ((qxl->primary_mode.x_res == qxl->virtual_x &&
qxl->primary_mode.y_res == qxl->virtual_y) &&
qxl->device_primary == QXL_DEVICE_PRIMARY_CREATED)
@@ -989,12 +986,18 @@ qxl_resize_primary_to_virtual (qxl_screen_t *qxl)
if (pScreen)
{
PixmapPtr root = pScreen->GetScreenPixmap (pScreen);
- qxl_surface_t *surf;
-
- if ((surf = get_surface (root)))
- qxl_surface_kill (surf);
-
- set_surface (root, qxl->primary);
+
+#ifdef XSPICE
+ if (qxl->deferred_fps <= 0)
+#endif
+ {
+ qxl_surface_t *surf;
+
+ if ((surf = get_surface (root)))
+ qxl_surface_kill (surf);
+
+ set_surface (root, qxl->primary);
+ }
set_screen_pixmap_header (pScreen);
}
@@ -1208,14 +1211,19 @@ qxl_create_screen_resources (ScreenPtr pScreen)
return FALSE;
pPixmap = pScreen->GetScreenPixmap (pScreen);
-
- set_screen_pixmap_header (pScreen);
-
- if ((surf = get_surface (pPixmap)))
- qxl_surface_kill (surf);
-
- set_surface (pPixmap, qxl->primary);
-
+
+#ifdef XSPICE
+ if (qxl->deferred_fps <= 0)
+#endif
+ {
+ set_screen_pixmap_header (pScreen);
+
+ if ((surf = get_surface (pPixmap)))
+ qxl_surface_kill (surf);
+
+ set_surface (pPixmap, qxl->primary);
+ }
+
/* HACK - I don't want to enable any crtcs other then the first at the beginning */
for (i = 1; i < qxl->num_heads; ++i)
{
@@ -1681,7 +1689,12 @@ setup_uxa (qxl_screen_t *qxl, ScreenPtr screen)
qxl->uxa->uxa_major = 1;
qxl->uxa->uxa_minor = 0;
- set_uxa_functions(qxl, screen);
+#ifdef XSPICE
+ if (qxl->deferred_fps)
+ dfps_set_uxa_functions(qxl, screen);
+ else
+#endif
+ set_uxa_functions(qxl, screen);
if (!uxa_driver_init (screen, qxl->uxa))
{
@@ -1708,18 +1721,21 @@ setup_uxa (qxl_screen_t *qxl, ScreenPtr screen)
static void
spiceqxl_screen_init (ScrnInfoPtr pScrn, qxl_screen_t *qxl)
{
- SpiceCoreInterface *core;
-
// Init spice
if (!qxl->spice_server)
{
qxl->spice_server = xspice_get_spice_server ();
xspice_set_spice_server_options (qxl->options);
- core = basic_event_loop_init ();
- spice_server_init (qxl->spice_server, core);
+ qxl->core = basic_event_loop_init ();
+ spice_server_init (qxl->spice_server, qxl->core);
qxl_add_spice_display_interface (qxl);
qxl->worker->start (qxl->worker);
qxl->worker_running = TRUE;
+ if (qxl->deferred_fps)
+ {
+ qxl->frames_timer = qxl->core->timer_add(dfps_ticker, qxl);
+ qxl->core->timer_start(qxl->frames_timer, 1000 / qxl->deferred_fps);
+ }
}
qxl->spice_server = qxl->spice_server;
}
@@ -1930,11 +1946,14 @@ qxl_leave_vt (VT_FUNC_ARGS_DECL)
qxl_screen_t *qxl = pScrn->driverPrivate;
xf86_hide_cursors (pScrn);
-
+
pScrn->EnableDisableFBAccess (XF86_SCRN_ARG (pScrn), FALSE);
-
- qxl->vt_surfaces = qxl_surface_cache_evacuate_all (qxl->surface_cache);
-
+
+#ifdef XSPICE
+ if (qxl->deferred_fps <= 0)
+#endif
+ qxl->vt_surfaces = qxl_surface_cache_evacuate_all (qxl->surface_cache);
+
ioport_write (qxl, QXL_IO_RESET, 0);
qxl_restore_state (pScrn);
diff --git a/src/qxl_surface.c b/src/qxl_surface.c
index 8c89eb6..3da9079 100644
--- a/src/qxl_surface.c
+++ b/src/qxl_surface.c
@@ -438,6 +438,7 @@ qxl_surface_get_host_bits(qxl_surface_t *surface)
{
return (void *) pixman_image_get_data(surface->host_image);
}
+
static struct QXLSurfaceCmd *
make_surface_cmd (surface_cache_t *cache, uint32_t id, QXLSurfaceCmdType type)
{
@@ -1090,6 +1091,57 @@ upload_box (qxl_surface_t *surface, int x1, int y1, int x2, int y2)
}
}
+static void
+upload_one_primary_region(qxl_screen_t *qxl, PixmapPtr pixmap, BoxPtr b)
+{
+ struct QXLRect rect;
+ struct QXLDrawable *drawable;
+ struct QXLImage *image;
+ FbBits *data;
+ int stride;
+ int bpp;
+
+ rect.left = b->x1;
+ rect.right = b->x2;
+ rect.top = b->y1;
+ rect.bottom = b->y2;
+
+ drawable = make_drawable (qxl, 0, QXL_DRAW_COPY, &rect);
+ drawable->u.copy.src_area = rect;
+ translate_rect (&drawable->u.copy.src_area);
+ drawable->u.copy.rop_descriptor = ROPD_OP_PUT;
+ drawable->u.copy.scale_mode = 0;
+ drawable->u.copy.mask.flags = 0;
+ drawable->u.copy.mask.pos.x = 0;
+ drawable->u.copy.mask.pos.y = 0;
+ drawable->u.copy.mask.bitmap = 0;
+
+ fbGetPixmapBitsData(pixmap, data, stride, bpp);
+ image = qxl_image_create (
+ qxl, (const uint8_t *)data, b->x1, b->y1, b->x2 - b->x1, b->y2 - b->y1, stride * sizeof(*data),
+ bpp == 24 ? 4 : bpp / 8, TRUE);
+ drawable->u.copy.src_bitmap =
+ physical_address (qxl, image, qxl->main_mem_slot);
+
+ push_drawable (qxl, drawable);
+}
+
+void
+qxl_surface_upload_primary_regions(qxl_screen_t *qxl, PixmapPtr pixmap, RegionRec *r)
+{
+ int n_boxes;
+ BoxPtr boxes;
+
+ n_boxes = RegionNumRects(r);
+ boxes = RegionRects(r);
+
+ while (n_boxes--)
+ {
+ upload_one_primary_region(qxl, pixmap, boxes);
+ boxes++;
+ }
+}
+
void
qxl_surface_finish_access (qxl_surface_t *surface, PixmapPtr pixmap)
{
--
1.7.10.4
More information about the Spice-devel
mailing list