glcontext: track sharedness with a cookie
authorMatthew Waters <matthew@centricular.com>
Wed, 15 Jul 2015 14:37:58 +0000 (00:37 +1000)
committerMatthew Waters <matthew@centricular.com>
Sat, 18 Jul 2015 05:34:55 +0000 (15:34 +1000)
The previous approach of traversing the other_context weak ref tree was
1. Less performant
2. Incorrect for context destruction removing a link in the tree

Example of 2:
c1 = context_create (NULL)
c2 = context_create (c1)
c3 = context_create (c2)
context_can_share (c1, c3) == TRUE
context_destroy (c2)
unref (c2)
context_can_share (c1, c3) returns FALSE when it should be TRUE!

This does not remove the restriction that context sharedness can only
be tracked between GstGLContext's.

gst-libs/gst/gl/gstglcontext.c
tests/check/libs/gstglcontext.c

index 12c2206..2a418e1 100644 (file)
@@ -133,6 +133,35 @@ load_self_module (gpointer user_data)
 #error "Add module loading support for GLES3"
 #endif
 
+/* Context sharedness es tracked by a unique id stored in each context object
+ * in order track complex creation/deletion scenarios.  As a result, sharedness
+ * can only be successfully validated between two GstGLContext's where one is
+ * not a wrapped context.
+ *
+ * As there is no API at the winsys level to tell whether two OpenGL contexts
+ * can share GL resources, this is the next best thing.
+ */
+static volatile guint sharegroup_idx;
+
+static guint
+_new_sharegroup_id (void)
+{
+  guint current, ret;
+
+  do {
+    current = g_atomic_int_get (&sharegroup_idx);
+    ret = current + 1;
+
+    /* 0 is special */
+    if (ret == 0)
+      ret++;
+  } while (!g_atomic_int_compare_and_exchange (&sharegroup_idx, current, ret));
+
+  GST_TRACE ("generated new share group id %u", ret);
+
+  return ret;
+}
+
 #define GST_CAT_DEFAULT gst_gl_context_debug
 GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
 
@@ -163,6 +192,7 @@ struct _GstGLContextPrivate
   gboolean alive;
 
   GWeakRef other_context_ref;
+  guint sharegroup_id;
   GError **error;
 
   gint gl_major;
@@ -359,6 +389,7 @@ gst_gl_context_new_wrapped (GstGLDisplay * display, guintptr handle,
   context = (GstGLContext *) context_wrap;
 
   context->display = gst_object_ref (display);
+  context->priv->sharegroup_id = _new_sharegroup_id ();
   context_wrap->handle = handle;
   context_wrap->platform = context_type;
   context_wrap->available_apis = available_apis;
@@ -822,53 +853,13 @@ gst_gl_context_get_window (GstGLContext * context)
   return gst_object_ref (context->window);
 }
 
-static gboolean
-_share_group_descendant (GstGLContext * context, GstGLContext * other_context,
-    GstGLContext ** root)
-{
-  GstGLContext *next = gst_object_ref (context);
-  GstGLContext *prev = NULL;
-
-  /* given a context tree where --> means "has other gl context":
-   *
-   * a-->b-->c-->d
-   *    /   /
-   *   e   /
-   *      /
-   * f-->g
-   *
-   * return TRUE if @other_context is a descendant of @context
-   *
-   * e.g. [a, b], [f, d], [e, c] are all descendants
-   * but [b, a], [d, f], [e, f] are not descendants.  Provide the root node (d)
-   * so that we can check if two chains end up at the end with the same
-   * GstGLContext
-   */
-
-  while (next != NULL) {
-    if (next == other_context) {
-      gst_object_unref (next);
-      if (root)
-        *root = NULL;
-      return TRUE;
-    }
-
-    prev = next;
-    next = g_weak_ref_get (&next->priv->other_context_ref);
-    gst_object_unref (prev);
-  }
-
-  if (root != NULL)
-    *root = prev;
-
-  return FALSE;
-}
-
 /**
  * gst_gl_context_can_share:
  * @context: a #GstGLContext
  * @other_context: another #GstGLContext
  *
+ * Note: This will always fail for two wrapped #GstGLContext's
+ *
  * Returns: whether @context and @other_context are able to share OpenGL
  *      resources.
  *
@@ -877,16 +868,12 @@ _share_group_descendant (GstGLContext * context, GstGLContext * other_context,
 gboolean
 gst_gl_context_can_share (GstGLContext * context, GstGLContext * other_context)
 {
-  GstGLContext *root1, *root2;
-
   g_return_val_if_fail (GST_GL_IS_CONTEXT (context), FALSE);
   g_return_val_if_fail (GST_GL_IS_CONTEXT (other_context), FALSE);
 
   /* check if the contexts are descendants or the root nodes are the same */
-  return context == other_context
-      || _share_group_descendant (context, other_context, &root1)
-      || _share_group_descendant (other_context, context, &root2)
-      || ((root1 != NULL || root2 != NULL) && root1 == root2);
+  return context->priv->sharegroup_id != 0
+      && context->priv->sharegroup_id == other_context->priv->sharegroup_id;
 }
 
 /**
@@ -926,6 +913,10 @@ gst_gl_context_create (GstGLContext * context,
   if (!context->priv->created) {
     g_weak_ref_set (&context->priv->other_context_ref, other_context);
     context->priv->error = error;
+    if (other_context == NULL)
+      context->priv->sharegroup_id = _new_sharegroup_id ();
+    else
+      context->priv->sharegroup_id = other_context->priv->sharegroup_id;
 
     context->priv->gl_thread = g_thread_new ("gstglcontext",
         (GThreadFunc) gst_gl_context_create_thread, context);
index 601e673..d83d41d 100644 (file)
@@ -442,6 +442,46 @@ GST_START_TEST (test_current_context)
 
 GST_END_TEST;
 
+GST_START_TEST (test_context_can_share)
+{
+  GstGLContext *c1, *c2, *c3;
+  GError *error = NULL;
+
+  c1 = gst_gl_context_new (display);
+  gst_gl_context_create (c1, NULL, &error);
+  fail_if (error != NULL, "Error creating context %s\n",
+      error ? error->message : "Unknown Error");
+
+  c2 = gst_gl_context_new (display);
+  gst_gl_context_create (c2, c1, &error);
+  fail_if (error != NULL, "Error creating context %s\n",
+      error ? error->message : "Unknown Error");
+
+  fail_unless (gst_gl_context_can_share (c1, c2));
+  fail_unless (gst_gl_context_can_share (c2, c1));
+
+  c3 = gst_gl_context_new (display);
+  gst_gl_context_create (c3, c2, &error);
+  fail_if (error != NULL, "Error creating context %s\n",
+      error ? error->message : "Unknown Error");
+
+  fail_unless (gst_gl_context_can_share (c1, c3));
+  fail_unless (gst_gl_context_can_share (c3, c1));
+  fail_unless (gst_gl_context_can_share (c2, c3));
+  fail_unless (gst_gl_context_can_share (c3, c2));
+
+  /* destroy the middle context */
+  gst_object_unref (c2);
+  c2 = NULL;
+
+  fail_unless (gst_gl_context_can_share (c1, c3));
+  fail_unless (gst_gl_context_can_share (c3, c1));
+
+  gst_object_unref (c1);
+  gst_object_unref (c3);
+}
+
+GST_END_TEST;
 
 static Suite *
 gst_gl_context_suite (void)
@@ -454,6 +494,7 @@ gst_gl_context_suite (void)
   tcase_add_test (tc_chain, test_share);
   tcase_add_test (tc_chain, test_wrapped_context);
   tcase_add_test (tc_chain, test_current_context);
+  tcase_add_test (tc_chain, test_context_can_share);
 
   return s;
 }