[Spice-devel] [PATCH 3/9] lossy surface regions in the client: infrastructure for tracking and updating

Yonit Halperin yhalperi at redhat.com
Tue Jun 1 00:30:52 PDT 2010


---
 server/red_worker.c |  160 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 160 insertions(+), 0 deletions(-)

diff --git a/server/red_worker.c b/server/red_worker.c
index 55d181d..357c6e8 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -657,6 +657,7 @@ struct DisplayChannel {
     pthread_mutex_t glz_drawables_inst_to_free_lock;
 
     uint8_t surface_client_created[NUM_SURFACES];
+    QRegion surface_client_lossy_region[NUM_SURFACES];
 
     struct {
         union {
@@ -1021,6 +1022,20 @@ typedef struct RedWorker {
     int jpeg_quality;
 } RedWorker;
 
+typedef enum {
+    BITMAP_DATA_TYPE_INVALID,
+    BITMAP_DATA_TYPE_CACHE,
+    BITMAP_DATA_TYPE_SURFACE,
+    BITMAP_DATA_TYPE_BITMAP,
+    BITMAP_DATA_TYPE_BITMAP_TO_CACHE,
+} BitmapDataType;
+
+typedef struct BitmapData {
+    BitmapDataType type;
+    uint64_t id; // surface id or cache item id
+    SpiceRect lossy_rect;
+} BitmapData;
+
 static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable);
 static void red_current_flush(RedWorker *worker, int surface_id);
 static void display_channel_push(RedWorker *worker);
@@ -6788,6 +6803,151 @@ static inline void red_display_reset_send_data(DisplayChannel *channel)
     memset(channel->send_data.free_list.sync, 0, sizeof(channel->send_data.free_list.sync));
 }
 
+/* set area=NULL for testing the whole surface */
+static int is_surface_area_lossy(DisplayChannel *display_channel, uint32_t surface_id,
+                                 const SpiceRect *area, SpiceRect *out_lossy_area)
+{
+    RedSurface *surface;
+    QRegion *surface_lossy_region;
+    QRegion lossy_region;
+
+    validate_surface(display_channel->base.worker, surface_id);
+    surface = &display_channel->base.worker->surfaces[surface_id];
+    surface_lossy_region = &display_channel->surface_client_lossy_region[surface_id];
+
+    if (!area) {
+        if (region_is_empty(surface_lossy_region)) {
+            return FALSE;
+        } else {
+            out_lossy_area->top = 0;
+            out_lossy_area->left = 0;
+            out_lossy_area->bottom = surface->context.height;
+            out_lossy_area->right = surface->context.width;
+            return TRUE;
+        }
+    }
+
+    region_init(&lossy_region);
+    region_add(&lossy_region, area);
+    region_and(&lossy_region, surface_lossy_region);
+    if (!region_is_empty(&lossy_region)) {
+        out_lossy_area->left = lossy_region.extents.x1;
+        out_lossy_area->top = lossy_region.extents.y1;
+        out_lossy_area->right = lossy_region.extents.x2;
+        out_lossy_area->bottom = lossy_region.extents.y2;
+        region_destroy(&lossy_region);
+        return TRUE;
+    } else {
+        return FALSE;
+    }
+}
+/* returns if the bitmap was already sent lossy to the client. If the bitmap hasn't been sent yet
+   to the client, returns false. "area" is for surfaces. If area = NULL,
+   all the surface is considered. out_lossy_data will hold info about the bitmap, and its lossy
+   area in case it is lossy and part of a surface. */
+static int is_bitmap_lossy(DisplayChannel *display_channel, SPICE_ADDRESS bitmap, SpiceRect *area,
+                           Drawable *drawable, BitmapData *out_data)
+{
+    RedWorker *worker = display_channel->base.worker;
+    QXLImage *qxl_image;
+
+    if (bitmap == 0) {
+        // self bitmap
+        out_data->type = BITMAP_DATA_TYPE_BITMAP;
+        return FALSE;
+    }
+
+    qxl_image = (QXLImage *)get_virt(&worker->mem_slots, bitmap, sizeof(QXLImage),
+                                     drawable->group_id);
+
+    if ((qxl_image->descriptor.flags & QXL_IMAGE_CACHE)) {
+        int is_hit_lossy;
+
+        out_data->id = qxl_image->descriptor.id;
+        if (pixmap_cache_hit(display_channel->pixmap_cache, qxl_image->descriptor.id,
+                             &is_hit_lossy, display_channel)) {
+            out_data->type = BITMAP_DATA_TYPE_CACHE;
+            if (is_hit_lossy) {
+                return TRUE;
+            } else {
+                return FALSE;
+            }
+        } else {
+            out_data->type = BITMAP_DATA_TYPE_BITMAP_TO_CACHE;
+        }
+    } else {
+         out_data->type = BITMAP_DATA_TYPE_BITMAP;
+    }
+
+    if (qxl_image->descriptor.type != SPICE_IMAGE_TYPE_SURFACE) {
+        return FALSE;
+    }
+
+    out_data->type = BITMAP_DATA_TYPE_SURFACE;
+    out_data->id = qxl_image->surface_image.surface_id;
+
+    if (is_surface_area_lossy(display_channel, qxl_image->surface_image.surface_id,
+                              area, &out_data->lossy_rect))
+    {
+        return TRUE;
+    } else {
+        return FALSE;
+    }
+}
+
+SPICE_GNUC_UNUSED static int is_brush_lossy(DisplayChannel *display_channel, SpiceBrush *brush,
+                                            Drawable *drawable, BitmapData *out_data)
+{
+    if (brush->type == SPICE_BRUSH_TYPE_PATTERN) {
+        return is_bitmap_lossy(display_channel, brush->u.pattern.pat, NULL,
+                               drawable, out_data);
+    } else {
+        out_data->type = BITMAP_DATA_TYPE_INVALID;
+        return FALSE;
+    }
+}
+
+SPICE_GNUC_UNUSED static void surface_lossy_region_update(RedWorker *worker, DisplayChannel *display_channel,
+                                                          Drawable *item, int has_mask, int lossy)
+{
+    QRegion *surface_lossy_region;
+    QXLDrawable *drawable;
+
+    if (has_mask && !lossy) {
+        return;
+    }
+
+    surface_lossy_region = &display_channel->surface_client_lossy_region[item->surface_id];
+    drawable = item->qxl_drawable;
+
+    if ((drawable->clip.type == SPICE_CLIP_TYPE_NONE) ||
+        ((drawable->clip.type == SPICE_CLIP_TYPE_PATH) && lossy)) {
+        if (!lossy) {
+            region_remove(surface_lossy_region, &drawable->bbox);
+        } else {
+            region_add(surface_lossy_region, &drawable->bbox);
+        }
+    } else if (drawable->clip.type == SPICE_CLIP_TYPE_RECTS) {
+        QRegion clip_rgn;
+        QRegion draw_region;
+        region_init(&clip_rgn);
+        region_init(&draw_region);
+        region_add(&draw_region, &drawable->bbox);
+        add_clip_rects(worker, &clip_rgn,
+                       drawable->clip.data + SPICE_OFFSETOF(QXLClipRects, chunk),
+                       item->group_id);
+        region_and(&draw_region, &clip_rgn);
+        if (lossy) {
+            region_or(surface_lossy_region, &draw_region);
+        } else {
+            region_exclude(surface_lossy_region, &draw_region);
+        }
+
+        region_destroy(&clip_rgn);
+        region_destroy(&draw_region);
+    } // else SPICE_CLIP_TYPE_PATH and lossless: leave the area as is
+}
+
 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