[Spice-devel] [RFCv5 30/47] server/red_worker: introduce RedSurfaceReleaseInfo

Alon Levy alevy at redhat.com
Sun May 8 06:11:26 PDT 2011


with multiple clients any surface creation or deletion cleanup needs to
wait until all the clients have been sent the command, so add reference
counting to the create and destroy release calls.

Also introduces the SURFACES_FOREACH macro, a bit ugly due to the need
to update multiple variables and trying to do it without introducing any
function, so the macro can be used as a for loop head.
---
 server/red_worker.c |  216 ++++++++++++++++++++++++++++++++++++++------------
 1 files changed, 164 insertions(+), 52 deletions(-)

diff --git a/server/red_worker.c b/server/red_worker.c
index 1bbd455..db8d9d9 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -808,6 +808,11 @@ typedef struct DrawContext {
     void *line_0;
 } DrawContext;
 
+typedef struct RedSurfaceReleaseInfo {
+    int refs;
+    QXLReleaseInfoExt create, destroy;
+} RedSurfaceReleaseInfo;
+
 typedef struct RedSurface {
     uint32_t refs;
     Ring current;
@@ -820,8 +825,8 @@ typedef struct RedSurface {
     Ring depend_on_me;
     QRegion draw_dirty_region;
 
-    //fix me - better handling here
-    QXLReleaseInfoExt create, destroy;
+    RedSurfaceReleaseInfo *release_info;
+    void *backend;
 } RedSurface;
 
 typedef struct ItemTrace {
@@ -995,12 +1000,46 @@ static void red_wait_pipe_item_sent(RedChannelClient *rcc, PipeItem *item);
 static void dump_bitmap(RedWorker *worker, SpiceBitmap *bitmap, uint32_t group_id);
 #endif
 
+static RingItem *red_worker_get_clients_tail(RedWorker *worker)
+{
+    if (!worker->display_channel) {
+        return NULL;
+    }
+    return ring_get_tail(&(worker)->display_channel->common.base.clients);
+}
+
 /*
  * Macros to make iterating over stuff easier
  * The two collections we iterate over:
  *  given a channel, iterate over it's clients
  */
 
+/* RENDER_FOREACH has been called obfuscated. Here is what it tries to do:
+ * We have a single RedRender that is embedded in the RedWorker. The rest are
+ * embedded in the DisplayClientChannel that are held in the client
+ * ring. This macro goes over all of them. It uses the fact that the first
+ * client on the ring also references the Surfaces on the worker.
+ * So: (call the return value surfaces to write out invariants)
+ *  0 clients:
+ *   return [&worker->surfaces]
+ *  1 client:
+ *   return [worker->display_channel->clients[0]->surfaces]
+ *   invariant: surfaces[0] == &worker->surfaces
+ *  n clients:
+ *   return [worker->display_channel->clients[i]->surfaces for i in xrange(num_clients)]
+ *   invariant: surfaces[0] == &worker->surfaces
+ */
+#define RENDER_FOREACH(var, render, worker)                                        \
+    for ((var) = red_worker_get_clients_tail(worker),                               \
+         (render) = (!(var) ? &(worker)->render :                                  \
+                    SPICE_CONTAINEROF((var), CommonChannelClient,                   \
+                                      base.channel_link)->render);                \
+            (var) || (render);                                                       \
+            (var) = (var) ? ring_prev(                                              \
+                &(worker)->display_channel->common.base.clients, (var)) : NULL,     \
+            (render) = (var) ? SPICE_CONTAINEROF((var), CommonChannelClient,         \
+                                         base.channel_link)->render : NULL)
+
 #define RCC_FOREACH(link, rcc, channel) \
     for (link = ring_get_head(&(channel)->clients),\
          rcc = SPICE_CONTAINEROF(link, RedChannelClient, channel_link);\
@@ -1479,6 +1518,27 @@ static inline void red_destroy_surface_item(RedWorker *worker,
     red_channel_client_pipe_add(&dcc->common.base, &destroy->pipe_item);
 }
 
+static inline void red_release_surface(RedWorker *worker, RedSurface *surface)
+{
+    RedSurfaceReleaseInfo *release_info = surface->release_info;
+
+    if (surface->backend) {
+        free(surface->backend);
+        surface->backend = NULL;
+    }
+    if (!release_info || --release_info->refs) {
+        return;
+    }
+    if (release_info->create.info) {
+        worker->qxl->st->qif->release_resource(worker->qxl, release_info->create);
+    }
+    if (release_info->destroy.info) {
+        worker->qxl->st->qif->release_resource(worker->qxl, release_info->destroy);
+    }
+    free(release_info);
+    surface->release_info = NULL;
+}
+
 static inline void red_destroy_surface(RedRender *render, uint32_t surface_id)
 {
     RedWorker *worker = render->worker;
@@ -1492,12 +1552,7 @@ static inline void red_destroy_surface(RedRender *render, uint32_t surface_id)
         ASSERT(surface->context.canvas);
 
         surface->context.canvas->ops->destroy(surface->context.canvas);
-        if (surface->create.info) {
-            worker->qxl->st->qif->release_resource(worker->qxl, surface->create);
-        }
-        if (surface->destroy.info) {
-            worker->qxl->st->qif->release_resource(worker->qxl, surface->destroy);
-        }
+        red_release_surface(worker, surface);
 
         region_destroy(&surface->draw_dirty_region);
         surface->context.canvas = NULL;
@@ -1507,19 +1562,16 @@ static inline void red_destroy_surface(RedRender *render, uint32_t surface_id)
     }
 }
 
-static inline void set_surface_release_info(RedRender *render, uint32_t surface_id, int is_create,
-                                            QXLReleaseInfo *release_info, uint32_t group_id)
+static inline void set_surface_release_info(RedSurfaceReleaseInfo *surface_release_info,
+                            int is_create, QXLReleaseInfo *release_info, uint32_t group_id)
 {
-    RedSurface *surface;
-
-    surface = &render->surfaces[surface_id];
-
+    ASSERT(release_info);
     if (is_create) {
-        surface->create.info = release_info;
-        surface->create.group_id = group_id;
+        surface_release_info->create.info = release_info;
+        surface_release_info->create.group_id = group_id;
     } else {
-        surface->destroy.info = release_info;
-        surface->destroy.group_id = group_id;
+        surface_release_info->destroy.info = release_info;
+        surface_release_info->destroy.group_id = group_id;
     }
 }
 
@@ -3577,45 +3629,100 @@ static inline void red_process_drawable(RedWorker *worker, RedDrawable *drawable
 
 static inline void red_create_surface(RedRender *render, uint32_t surface_id, uint32_t width,
                                       uint32_t height, int32_t stride, uint32_t format,
-                                      void *line_0, int data_is_valid);
+                                      void *line_0, int data_is_valid,
+                                      RedSurfaceReleaseInfo *release_info);
 
-static inline void red_process_surface(RedWorker *worker, RedSurfaceCmd *surface, uint32_t group_id, int data_is_valid)
+static RedSurfaceReleaseInfo *red_create_surface_release_info(
+    int is_create, QXLReleaseInfo *qxl_release_info, uint32_t group_id)
 {
-    int surface_id;
-    RedSurface *red_surface;
-    uint8_t *data;
-    RedRender *render = &worker->render;
+    RedSurfaceReleaseInfo *release_info = spice_malloc0(sizeof(RedSurfaceReleaseInfo));
 
-    surface_id = surface->surface_id;
-    __validate_surface(render, surface_id);
+    if (qxl_release_info) {
+        set_surface_release_info(release_info, is_create, qxl_release_info, group_id);
+    }
+    return release_info;
+}
 
-    red_surface = &render->surfaces[surface_id];
+static inline void red_create_surface_for_all_clients(RedWorker *worker,
+      uint32_t surface_id, uint32_t width, uint32_t height, int32_t stride,
+      uint32_t format, void *line_0, int data_is_valid,
+      QXLReleaseInfo *qxl_release_info, uint32_t group_id)
+{
+    RingItem *link;
+    RedRender *render;
+    RedSurfaceReleaseInfo *release_info =
+        qxl_release_info == NULL ? NULL :
+        red_create_surface_release_info(1, qxl_release_info, group_id);
 
-    switch (surface->type) {
-    case QXL_SURFACE_CMD_CREATE: {
-        uint32_t height = surface->u.surface_create.height;
-        int32_t stride = surface->u.surface_create.stride;
+    RENDER_FOREACH(link, render, worker) {
+        red_create_surface(render, surface_id,
+                           width, height, stride, format, line_0,
+                           data_is_valid, release_info);
+    }
+}
 
-        data = surface->u.surface_create.data;
-        if (stride < 0) {
-            data -= (int32_t)(stride * (height - 1));
-        }
-        red_create_surface(render, surface_id, surface->u.surface_create.width,
-                           height, stride, surface->u.surface_create.format, data,
-                           data_is_valid);
-        set_surface_release_info(&worker->render, surface_id, 1, surface->release_info, group_id);
-        break;
+static inline void red_create_surface_from_cmd_for_all_clients(RedWorker *worker,
+    RedSurfaceCmd *surface, uint32_t group_id, int data_is_valid)
+{
+    uint8_t *line_0;
+    uint32_t height = surface->u.surface_create.height;
+    int32_t stride = surface->u.surface_create.stride;
+
+    line_0 = surface->u.surface_create.data;
+    if (stride < 0) {
+        line_0 -= (int32_t)(stride * (height - 1));
     }
-    case QXL_SURFACE_CMD_DESTROY:
-        PANIC_ON(!red_surface->context.canvas);
-        set_surface_release_info(render, surface_id, 0, surface->release_info, group_id);
+    return red_create_surface_for_all_clients(worker, surface->surface_id,
+        surface->u.surface_create.width, height, stride, surface->u.surface_create.format,
+        line_0, data_is_valid, surface->release_info, group_id);
+}
+
+static inline void red_destroy_surface_for_all_clients(RedWorker *worker,
+    RedSurfaceCmd *surface, uint32_t group_id, int data_is_valid)
+{
+    int surface_id;
+    RingItem *link;
+    RedRender *render;
+    ASSERT(surface->release_info); // we assume primary doesn't reach here.
+
+    surface_id = surface->surface_id;
+    RENDER_FOREACH(link, render, worker) {
+        PANIC_ON(!render->surfaces[surface_id].context.canvas);
+        // this is actually redundant - but since it is possible for the
+        // surfaces to be freed in any order (TODO: is it really? could all of
+        // this be much simpler then I'm making it up to be?) just set the
+        // release info for all
+        set_surface_release_info(render->surfaces[surface_id].release_info,
+                                         0, surface->release_info, group_id);
         red_handle_depends_on_target_surface(render, surface_id);
-        /* note that red_handle_depends_on_target_surface must be called before red_current_clear.
-           otherwise "current" will hold items that other drawables may depend on, and then
-           red_current_clear will remove them from the pipe. */
+        /* note that red_handle_depends_on_target_surface must be called before
+         * red_current_clear. otherwise "current" will hold items that other
+         * drawables may depend on, and then red_current_clear will remove them
+         * from the pipe. */
         red_current_clear(render, surface_id);
-        red_clear_surface_drawables_from_pipe(render->dcc, surface_id, FALSE);
+        if (render->dcc) {
+            red_clear_surface_drawables_from_pipe(render->dcc,
+                                                  surface_id, FALSE);
+        }
         red_destroy_surface(render, surface_id);
+    }
+}
+
+static inline void red_process_surface(RedWorker *worker, RedSurfaceCmd *surface, uint32_t group_id, int data_is_valid)
+{
+    int surface_id;
+
+    surface_id = surface->surface_id;
+    __validate_surface(&worker->render, surface_id);
+
+    switch (surface->type) {
+    case QXL_SURFACE_CMD_CREATE:
+        red_create_surface_from_cmd_for_all_clients(worker, surface, group_id,
+                                           data_is_valid);
+        break;
+    case QXL_SURFACE_CMD_DESTROY:
+        red_destroy_surface_for_all_clients(worker, surface, group_id,
+                                            data_is_valid);
         break;
     default:
             red_error("unknown surface command");
@@ -8690,7 +8797,8 @@ static inline void red_create_surface_item(DisplayChannelClient *dcc, int surfac
 
 static inline void red_create_surface(RedRender *render, uint32_t surface_id, uint32_t width,
                                       uint32_t height, int32_t stride, uint32_t format,
-                                      void *line_0, int data_is_valid)
+                                      void *line_0, int data_is_valid,
+                                      RedSurfaceReleaseInfo *release_info)
 {
     RedWorker *worker = render->worker;
     RedSurface *surface = &render->surfaces[surface_id];
@@ -8700,8 +8808,13 @@ static inline void red_create_surface(RedRender *render, uint32_t surface_id, ui
     if (stride >= 0) {
         PANIC("Untested path stride >= 0");
     }
+    surface->backend = NULL;
     PANIC_ON(surface->context.canvas);
 
+    if (release_info) {
+        release_info->refs++;
+    }
+    surface->release_info = release_info;
     surface->context.canvas_draws_on_surface = FALSE;
     surface->context.width = width;
     surface->context.height = height;
@@ -8711,8 +8824,6 @@ static inline void red_create_surface(RedRender *render, uint32_t surface_id, ui
     if (!data_is_valid) {
         memset((char *)line_0 + (int32_t)(stride * (height - 1)), 0, height*abs(stride));
     }
-    surface->create.info = NULL;
-    surface->destroy.info = NULL;
     ring_init(&surface->current);
     ring_init(&surface->current_list);
     ring_init(&surface->depend_on_me);
@@ -10062,9 +10173,10 @@ static inline void handle_dev_create_primary_surface(RedWorker *worker)
         line_0 -= (int32_t)(surface.stride * (surface.height -1));
     }
 
-    red_create_surface(&worker->render, 0, surface.width,
+    red_create_surface_for_all_clients(worker, 0, surface.width,
                        surface.height, surface.stride, surface.format,
-                       line_0, surface.flags & QXL_SURF_FLAG_KEEP_DATA);
+                       line_0, surface.flags & QXL_SURF_FLAG_KEEP_DATA,
+                       NULL /* release_info */, surface.group_id);
 
     if (display_is_connected(worker)) {
         red_pipes_add_verb(&worker->display_channel->common.base,
-- 
1.7.5.1



More information about the Spice-devel mailing list