[Spice-devel] [PATCH 5/9] infrastructure routines for resending to the client part of surfaces that have been sent lossy.
Yonit Halperin
yhalperi at redhat.com
Tue Jun 1 00:30:54 PDT 2010
The code also handles cases in which the server doesn't hold anymore these surfaces parts, i.e., when
it holds a more updated version of them. This scenario is handled by replacing commands that were rendered, with images.
---
server/red_worker.c | 334 ++++++++++++++++++++++++++++++++++++++++++++++-----
1 files changed, 306 insertions(+), 28 deletions(-)
diff --git a/server/red_worker.c b/server/red_worker.c
index a787751..feb8c8a 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -49,6 +49,7 @@
#include "mjpeg_encoder.h"
#include "red_memslots.h"
#include "jpeg_encoder.h"
+#include "rect.h"
//#define COMPRESS_STAT
//#define DUMP_BITMAP
@@ -1337,6 +1338,15 @@ static inline void red_pipe_add_drawable(RedWorker *worker, Drawable *drawable)
red_pipe_add(&worker->display_channel->base, &drawable->pipe_item);
}
+static inline void red_pipe_add_drawable_to_tail(RedWorker *worker, Drawable *drawable)
+{
+ if (!worker->display_channel) {
+ return;
+ }
+ drawable->refs++;
+ red_pipe_add_tail(&worker->display_channel->base, &drawable->pipe_item);
+}
+
static inline void red_pipe_add_drawable_after(RedWorker *worker, Drawable *drawable,
Drawable *pos_after)
{
@@ -1352,6 +1362,15 @@ static inline void red_pipe_add_drawable_after(RedWorker *worker, Drawable *draw
red_pipe_add_after(&worker->display_channel->base, &drawable->pipe_item, &pos_after->pipe_item);
}
+static inline PipeItem *red_pipe_get_tail(RedWorker *worker)
+{
+ if (!worker->display_channel) {
+ return NULL;
+ }
+
+ return (PipeItem*)ring_get_tail(&worker->display_channel->base.pipe);
+}
+
static inline void red_destroy_surface(RedWorker *worker, uint32_t surface_id);
static inline void red_pipe_remove_drawable(RedWorker *worker, Drawable *drawable)
@@ -1372,6 +1391,16 @@ static inline void red_pipe_add_image_item(RedWorker *worker, ImageItem *item)
red_pipe_add(&worker->display_channel->base, &item->link);
}
+static inline void red_pipe_add_image_item_after(RedWorker *worker, ImageItem *item,
+ PipeItem *pos)
+{
+ if (!worker->display_channel) {
+ return;
+ }
+ item->refs++;
+ red_pipe_add_after(&worker->display_channel->base, &item->link, pos);
+}
+
static inline uint64_t channel_message_serial(RedChannel *channel)
{
return channel->send_data.header.serial;
@@ -2337,12 +2366,6 @@ static int is_equal_line_attr(SpiceLineAttr *a1, SpiceLineAttr *a2)
a1->style_nseg == 0;
}
-static inline int rect_is_equal(const SpiceRect *r1, const SpiceRect *r2)
-{
- return r1->top == r2->top && r1->left == r2->left &&
- r1->bottom == r2->bottom && r1->right == r2->right;
-}
-
// partial imp
static int is_same_geometry(RedWorker *worker, Drawable *d1, Drawable *d2)
{
@@ -4696,6 +4719,91 @@ static void red_update_area(RedWorker *worker, const SpiceRect *area, int surfac
#else
+/*
+ Renders drawables for updating the requested area, but only drawables that are older
+ than 'last' (exclusive).
+*/
+static void red_update_area_till(RedWorker *worker, const SpiceRect *area, int surface_id,
+ Drawable *last)
+{
+ // TODO: if we use UPDATE_AREA_BY_TREE, a corresponding red_update_area_till
+ // should be implemented
+
+ RedSurface *surface;
+ Drawable *surface_last = NULL;
+ Ring *ring;
+ RingItem *ring_item;
+ Drawable *now;
+ QRegion rgn;
+
+ ASSERT(last);
+ ASSERT(ring_item_is_linked(&last->list_link));
+
+ surface = &worker->surfaces[surface_id];
+
+ if (surface_id != last->surface_id) {
+ // find the nearest older drawable from the appropriate surface
+ ring = &worker->current_list;
+ ring_item = &last->list_link;
+ while ((ring_item = ring_next(ring, ring_item))) {
+ now = SPICE_CONTAINEROF(ring_item, Drawable, list_link);
+ if (now->surface_id == surface_id) {
+ surface_last = now;
+ break;
+ }
+ }
+ } else {
+ ring_item = ring_next(&surface->current_list, &last->surface_list_link);
+ if (ring_item) {
+ surface_last = SPICE_CONTAINEROF(ring_item, Drawable, surface_list_link);
+ }
+ }
+
+ if (!surface_last) {
+ return;
+ }
+
+ ring = &surface->current_list;
+ ring_item = &surface_last->surface_list_link;
+
+ region_init(&rgn);
+ region_add(&rgn, area);
+
+ // find the first older drawable that intersects with the area
+ do {
+ now = SPICE_CONTAINEROF(ring_item, Drawable, surface_list_link);
+ if (region_intersects(&rgn, &now->tree_item.base.rgn)) {
+ surface_last = now;
+ break;
+ }
+ } while ((ring_item = ring_next(ring, ring_item)));
+
+ region_destroy(&rgn);
+
+ if (!surface_last) {
+ return;
+ }
+
+ do {
+ Container *container;
+
+ ring_item = ring_get_tail(&surface->current_list);
+ now = SPICE_CONTAINEROF(ring_item, Drawable, surface_list_link);
+ now->refs++;
+ container = now->tree_item.base.container;
+ current_remove_drawable(worker, now);
+ container_cleanup(worker, container);
+ /* red_draw_drawable may call red_update_area for the surfaces 'now' depends on. Notice,
+ that it is valid to call red_update_area in this case and not red_update_area_till:
+ It is impossible that there was newer item then 'last' in one of the surfaces
+ that red_update_area is called for, Otherwise, 'now' would have already been rendered.
+ See the call for red_handle_depends_on_target_surface in red_process_drawable */
+ red_draw_drawable(worker, now);
+ release_drawable(worker, now);
+ } while (now != surface_last);
+ validate_area(worker, area, surface_id);
+}
+
static void red_update_area(RedWorker *worker, const SpiceRect *area, int surface_id)
{
RedSurface *surface;
@@ -5012,25 +5120,27 @@ static void red_current_flush(RedWorker *worker, int surface_id)
red_current_clear(worker, surface_id);
}
-static void red_add_surface_image(RedWorker *worker, int surface_id)
+// adding the pipe item after pos. If pos == NULL, adding to head.
+static ImageItem *red_add_surface_area_image(RedWorker *worker, int surface_id, SpiceRect *area,
+ PipeItem *pos, int can_lossy)
{
ImageItem *item;
int stride;
- SpiceRect area;
- SpiceCanvas *canvas = worker->surfaces[surface_id].context.canvas;
- RedSurface *surface;
+ int width;
+ int height;
+ RedSurface *surface = &worker->surfaces[surface_id];
+ SpiceCanvas *canvas = surface->context.canvas;
+ int bpp;
int all_set;
- surface = &worker->surfaces[surface_id];
+ ASSERT(area);
- if (!worker->display_channel || !surface->context.canvas) {
- return;
- }
-
- stride = abs(surface->context.stride);
-
- item = (ImageItem *)spice_malloc_n_m(surface->context.height, stride,
- sizeof(ImageItem));
+ width = area->right - area->left;
+ height = area->bottom - area->top;
+ bpp = SPICE_SURFACE_FMT_DEPTH(surface->context.format) / 8;
+ stride = SPICE_ALIGN(width * bpp, 4);
+
+ item = (ImageItem *)spice_malloc_n_m(height, stride, sizeof(ImageItem));
red_pipe_item_init(&item->link, PIPE_ITEM_TYPE_IMAGE);
@@ -5039,17 +5149,15 @@ static void red_add_surface_image(RedWorker *worker, int surface_id)
item->image_format =
surface_format_to_image_type(surface->context.format);
item->image_flags = 0;
- item->pos.x = item->pos.y = 0;
- item->width = surface->context.width;
- item->height = surface->context.height;
+ item->pos.x = area->left;
+ item->pos.y = area->top;
+ item->width = width;
+ item->height = height;
item->stride = stride;
item->top_down = surface->context.top_down;
- item->can_lossy = TRUE;
+ item->can_lossy = can_lossy;
- area.top = area.left = 0;
- area.right = surface->context.width;
- area.bottom = surface->context.height;
- canvas->ops->read_bits(canvas, item->data, stride, &area);
+ canvas->ops->read_bits(canvas, item->data, stride, area);
/* For 32bit non-primary surfaces we need to keep any non-zero
high bytes as the surface may be used as source to an alpha_blend */
@@ -5063,8 +5171,34 @@ static void red_add_surface_image(RedWorker *worker, int surface_id)
}
}
- red_pipe_add_image_item(worker, item);
+ if (!pos) {
+ red_pipe_add_image_item(worker, item);
+ } else {
+ red_pipe_add_image_item_after(worker, item, pos);
+ }
+
release_image_item(item);
+
+ return item;
+}
+
+static void red_add_surface_image(RedWorker *worker, int surface_id)
+{
+ SpiceRect area;
+ RedSurface *surface;
+ ImageItem *item;
+
+ surface = &worker->surfaces[surface_id];
+
+ if (!worker->display_channel || !surface->context.canvas) {
+ return;
+ }
+
+ area.top = area.left = 0;
+ area.right = surface->context.width;
+ area.bottom = surface->context.height;
+
+ item = red_add_surface_area_image(worker, surface_id, &area, NULL, TRUE);
display_channel_push(worker);
}
@@ -6950,6 +7084,150 @@ SPICE_GNUC_UNUSED static void surface_lossy_region_update(RedWorker *worker, Dis
} // else SPICE_CLIP_TYPE_PATH and lossless: leave the area as is
}
+static inline int drawable_intersects_with_areas(Drawable *drawable, int surface_ids[],
+ SpiceRect *surface_areas[],
+ int num_surfaces)
+{
+ int i;
+ for (i = 0; i < num_surfaces; i++) {
+ if (surface_ids[i] == drawable->qxl_drawable->surface_id) {
+ if (rect_intersects(surface_areas[i], &drawable->qxl_drawable->bbox)) {
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+static int pipe_rendered_drawables_intersect_with_areas(RedWorker *worker,
+ DisplayChannel *display_channel,
+ int surface_ids[],
+ SpiceRect *surface_areas[],
+ int num_surfaces)
+{
+ PipeItem *pipe_item;
+ Ring *pipe;
+
+ ASSERT(num_surfaces);
+ pipe = &display_channel->base.pipe;
+
+ for (pipe_item = (PipeItem *)ring_get_head(pipe);
+ pipe_item;
+ pipe_item = (PipeItem *)ring_next(pipe, &pipe_item->link))
+ {
+ Drawable *drawable;
+
+ if (pipe_item->type != PIPE_ITEM_TYPE_DRAW)
+ continue;
+ drawable = SPICE_CONTAINEROF(pipe_item, Drawable, pipe_item);
+
+ if (ring_item_is_linked(&drawable->list_link))
+ continue; // item hasn't been rendered
+
+ if (drawable_intersects_with_areas(drawable, surface_ids, surface_areas, num_surfaces)) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static void red_pipe_replace_rendered_drawables_with_images(RedWorker *worker,
+ DisplayChannel *display_channel)
+{
+ PipeItem *pipe_item;
+ Ring *pipe;
+
+ pipe = &display_channel->base.pipe;
+ // going from the newest to oldest
+ for (pipe_item = (PipeItem *)ring_get_head(pipe);
+ pipe_item;
+ pipe_item = (PipeItem *)ring_next(pipe, &pipe_item->link)) {
+
+ Drawable *drawable;
+ ImageItem *image;
+ if (pipe_item->type != PIPE_ITEM_TYPE_DRAW)
+ continue;
+ drawable = SPICE_CONTAINEROF(pipe_item, Drawable, pipe_item);
+ if (ring_item_is_linked(&drawable->list_link))
+ continue; // item hasn't been rendered
+ image = red_add_surface_area_image(worker, drawable->qxl_drawable->surface_id,
+ &drawable->qxl_drawable->bbox, pipe_item, TRUE);
+
+ ASSERT(image);
+ red_pipe_remove_drawable(worker, drawable);
+ pipe_item = &image->link;
+ }
+}
+
+SPICE_GNUC_UNUSED static void red_add_lossless_drawable_dependencies(RedWorker *worker,
+ DisplayChannel *display_channel,
+ Drawable *item,
+ int deps_surfaces_ids[],
+ SpiceRect *deps_areas[],
+ int num_deps)
+{
+ QXLDrawable *drawable = item->qxl_drawable;
+ int sync_rendered = FALSE;
+ int i;
+
+
+ if (!ring_item_is_linked(&item->list_link)) {
+ /* drawable was already rendered, we may not be able to retrieve the lossless data
+ for the lossy areas */
+ sync_rendered = TRUE;
+
+ // checking if the drawable itself or one of the other commands
+ // that were rendered, affected the areas that need to be resent
+ if (!drawable_intersects_with_areas(item, deps_surfaces_ids,
+ deps_areas, num_deps)) {
+ if (pipe_rendered_drawables_intersect_with_areas(worker, display_channel,
+ deps_surfaces_ids,
+ deps_areas,
+ num_deps)) {
+ sync_rendered = TRUE;
+ }
+ } else {
+ sync_rendered = TRUE;
+ }
+ } else {
+ sync_rendered = FALSE;
+ for (i = 0; i < num_deps; i++) {
+ red_update_area_till(worker, deps_areas[i], deps_surfaces_ids[i], item);
+ }
+ }
+
+ if (!sync_rendered) {
+ // pushing the pipe item back to the pipe
+ red_pipe_add_drawable_to_tail(worker, item);
+ // the surfaces areas will be sent as DRAW_COPY commands, that
+ // will be executed before the current drawable
+ for (i = 0; i < num_deps; i++) {
+ red_add_surface_area_image(worker, deps_surfaces_ids[i], deps_areas[i],
+ red_pipe_get_tail(worker), FALSE);
+
+ }
+ } else {
+ int drawable_surface_id[1];
+ SpiceRect *drawable_bbox[1];
+
+ drawable_surface_id[0] = drawable->surface_id;
+ drawable_bbox[0] = &drawable->bbox;
+
+ // check if the other rendered images in the pipe have updated the drawable bbox
+ if (pipe_rendered_drawables_intersect_with_areas(worker, display_channel,
+ drawable_surface_id,
+ drawable_bbox,
+ 1)) {
+ red_pipe_replace_rendered_drawables_with_images(worker, display_channel);
+ }
+
+
+ red_add_surface_area_image(worker, drawable->surface_id, &drawable->bbox,
+ red_pipe_get_tail(worker), TRUE);
+ }
+}
+
static inline void red_send_qxl_drawable(RedWorker *worker, DisplayChannel *display_channel,
Drawable *item)
{
--
1.6.6.1
More information about the Spice-devel
mailing list