[Spice-devel] [RFC v4 45/62] server/red_worker: copy and free new surfaces after first client

Alon Levy alevy at redhat.com
Tue Apr 26 03:55:10 PDT 2011


---
 server/red_worker.c |  138 +++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 134 insertions(+), 4 deletions(-)

diff --git a/server/red_worker.c b/server/red_worker.c
index 6014cb3..399ebea 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -8610,6 +8610,103 @@ static void init_surfaces(Surfaces *surfaces)
     image_surface_init(surfaces);
 }
 
+static void free_surfaces(RedWorker *worker, Surfaces *surfaces)
+{
+    RingItem *link;
+    RingItem *next;
+    int count = 0;
+    int i;
+
+    // TODO: this prevents remove_drawable from sending anything. Is that really
+    // required?
+    surfaces->dcc = NULL;
+
+    RING_FOREACH_SAFE(link, next, &surfaces->current_list) {
+        Drawable *drawable = SPICE_CONTAINEROF(link, Drawable, list_link);
+        remove_drawable(worker, surfaces, drawable);
+        count++;
+    }
+    red_printf("released %d drawables", count);
+    // TODO - we should ensure the reference counts on the surfaces
+    // have zeroed, and that there are no contexts
+    for (i = 0 ; i < surfaces->n_surfaces ; ++i) {
+        RedSurface *s = &surfaces->surfaces[i];
+        if (s->refs == 0) {
+            continue;
+        }
+        ASSERT(s->context.canvas);
+        red_destroy_surface(worker, surfaces, i);
+    }
+    free(surfaces);
+}
+
+static Surfaces *copy_surfaces(RedWorker *worker, DisplayChannelClient *dcc,
+                               Surfaces *orig)
+{
+    int i;
+    Surfaces *surfaces = spice_malloc0(sizeof(Surfaces));
+
+    init_surfaces(surfaces);
+    dcc->common.surfaces = surfaces;
+    surfaces->dcc = dcc;
+    surfaces->n_surfaces = orig->n_surfaces;
+    surfaces->image_surfaces = orig->image_surfaces; // just ops table
+
+    // must init streams before rest of copy, since red_add_current tries
+    // to attach to stream.
+    red_init_streams(dcc->common.surfaces); // TODO - move surfaces to dcc
+    red_display_client_init_streams(dcc);
+
+    for (i = 0 ; i < orig->n_surfaces ; ++i) {
+        RedSurface *s = &orig->surfaces[i];
+        if (s->refs == 0) { // stop when we reached the last surface,
+                            // TODO - any better way then refs?
+            continue;
+        }
+        ASSERT(s->context.canvas);
+        red_create_surface(worker, surfaces, i,
+            s->context.width, s->context.height, s->context.stride,
+            s->context.format, s->context.line_0,
+            TRUE /* surface is valid */, s->release_info);
+    }
+#ifdef PIPE_DEBUG
+    surfaces->last_id = 0; // orig->last_id; // TODO: does this make any sense?
+#endif
+    // clone the current_list, adding references to everything
+    //current_list - we update all current_list's (both the main
+    //and the per surface one) in this loop:
+    //TODO: we definitely need to copy all the surfaces, but how do we copy surface trees?
+    // A. replay all current operations (which are by definition ordered from oldest to
+    // newest). This is what we currently do.
+    // B. flatten each tree to a surface image, and send that. That's what we actually
+    // send to the client. Any reason to have the new client's tree equal to the
+    // worker tree?
+    RingItem *link;
+    int count = 0;
+    RING_FOREACH(link, &orig->current_list) {
+        Drawable *orig_drawable = SPICE_CONTAINEROF(link, Drawable, list_link);
+        uint32_t group_id = orig_drawable->group_id;
+        RedDrawable *red_drawable = orig_drawable->red_drawable;
+        red_process_drawable_surfaces(worker, surfaces, red_drawable,
+            group_id);
+        count++;
+    }
+    ASSERT(count == orig->drawable_count);
+    ASSERT(surfaces->current_size <= orig->current_size);
+    ASSERT(surfaces->drawable_count <= orig->drawable_count);
+    ASSERT(surfaces->transparent_count <= orig->transparent_count);
+    ASSERT(surfaces->shadows_count <= orig->shadows_count);
+    ASSERT(surfaces->containers_count <= orig->containers_count);
+    red_printf("current/drawable/transparent/shadows/containers: "
+        "(%d,%d,%d,%d,%d)->(%d,%d,%d,%d,%d)",
+        orig->current_size, orig->drawable_count, orig->transparent_count,
+        orig->shadows_count, orig->containers_count,
+        surfaces->current_size, surfaces->drawable_count,
+        surfaces->transparent_count, surfaces->shadows_count,
+        surfaces->containers_count);
+    return surfaces;
+}
+
 static void display_channel_client_disconnect(RedChannelClient *rcc)
 {
     // TODO: MC: right now we assume single channel
@@ -8641,7 +8738,36 @@ static void display_channel_client_disconnect(RedChannelClient *rcc)
     free(dcc->send_data.free_list.res);
     red_display_destroy_streams(dcc);
     red_channel_client_pipe_clear(rcc); // do this before deleting surfaces
-    worker->surfaces.dcc = NULL;
+    if (dcc->common.surfaces != &worker->surfaces) {
+        free_surfaces(worker, dcc->common.surfaces);
+    } else {
+        // if this isn't the last client, free one client's surfaces
+        // and let it have the worker's surfaces (this makes it the "primary",
+        // and we should (TODO) let him know new channels like sound are
+        // now available. I wonder what the client will do :).
+        if (display_channel->common.base.clients_num > 1) {
+            // since this may have happened already, the client with the
+            // worker surfaces can be anybody - since this is a doubly linked
+            // list - we can check next and prev, one can't be the head,
+            // and so will be our other.
+            red_printf("swapping surfaces");
+            RingItem *dcc_link = &dcc->common.base.channel_link;
+            Ring *clients = &display_channel->common.base.clients;
+            DisplayChannelClient *other = SPICE_CONTAINEROF(
+                ring_next(clients, dcc_link), DisplayChannelClient,
+                common.base.channel_link);
+            if (other == NULL) {
+                other = SPICE_CONTAINEROF(ring_prev(clients, dcc_link),
+                    DisplayChannelClient, common.base.channel_link);
+            }
+            ASSERT(other != dcc && other != NULL);
+            free_surfaces(worker, other->common.surfaces);
+            worker->surfaces.dcc = other;
+            other->common.surfaces = &worker->surfaces;
+        } else {
+            worker->surfaces.dcc = NULL;
+        }
+    }
     red_channel_client_disconnect(rcc);
 }
 
@@ -9746,9 +9872,13 @@ static void handle_new_display_channel(RedWorker *worker, RedClient *client, Red
     if (!listen_to_new_client_channel(&display_channel->common, &dcc->common, stream)) {
         goto error;
     }
-    dcc->common.surfaces = &worker->surfaces;
-    dcc->common.surfaces->dcc = dcc;
-    red_display_client_init_streams(dcc);
+    if (display_channel->common.base.clients_num == 1) {
+        dcc->common.surfaces = &worker->surfaces;
+        dcc->common.surfaces->dcc = dcc;
+        red_display_client_init_streams(dcc);
+    } else {
+        copy_surfaces(worker, dcc, &worker->surfaces);
+    }
     on_new_display_channel_client(dcc);
     return;
 
-- 
1.7.4.4



More information about the Spice-devel mailing list