va: allocator: dmabuf: Use GstVaSurfaceCopy, if possible.
authorVíctor Manuel Jáquez Leal <vjaquez@igalia.com>
Wed, 6 Oct 2021 13:20:50 +0000 (15:20 +0200)
committerGStreamer Marge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Sun, 10 Oct 2021 17:03:29 +0000 (17:03 +0000)
If dmabuf-based buffer to copy contains only one memory, and there are
memories available in the allocator's pool, it's possible a fast
memory copy using GstVaSurfaceCopy, regardless the drm modifier.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1023>

subprojects/gst-plugins-bad/sys/va/gstvaallocator.c

index 84f399f..dff8cfa 100644 (file)
@@ -244,10 +244,13 @@ struct _GstVaDmabufAllocator
   GstVaDisplay *display;
 
   GstMemoryMapFunction parent_map;
+  GstMemoryCopyFunction parent_copy;
 
   GstVideoInfo info;
   guint usage_hint;
 
+  GstVaSurfaceCopy *copy;
+
   GstVaMemoryPool pool;
 };
 
@@ -255,6 +258,84 @@ struct _GstVaDmabufAllocator
 G_DEFINE_TYPE_WITH_CODE (GstVaDmabufAllocator, gst_va_dmabuf_allocator,
     GST_TYPE_DMABUF_ALLOCATOR, _init_debug_category ());
 
+/* If a buffer contains multiple memories (dmabuf objects) its very
+ * difficult to provide a realiable way to fast-copy single memories:
+ * While VA API sees surfaces with dependant dmabufs, GStreamer only
+ * copies dmabufs in isolation; trying to solve it while keeping a
+ * reference of the copied buffer and dmabuf index is very fragile. */
+static GstMemory *
+gst_va_dmabuf_mem_copy (GstMemory * gmem, gssize offset, gssize size)
+{
+  GstVaDmabufAllocator *self = GST_VA_DMABUF_ALLOCATOR (gmem->allocator);
+  GstVaBufferSurface *buf;
+  guint64 *drm_mod;
+  gsize mem_size;
+
+  buf = gst_mini_object_get_qdata (GST_MINI_OBJECT (gmem),
+      gst_va_buffer_surface_quark ());
+
+  drm_mod = gst_mini_object_get_qdata (GST_MINI_OBJECT (gmem),
+      gst_va_drm_mod_quark ());
+
+  /* 0 is DRM_FORMAT_MOD_LINEAR, we do not include its header now. */
+  if (buf->n_mems > 1 && *drm_mod != 0) {
+    GST_ERROR_OBJECT (self, "Failed to copy multi-dmabuf because non-linear "
+        "modifier: %#lx.", *drm_mod);
+    return NULL;
+  }
+
+  /* check if it's full memory copy */
+  mem_size = gst_memory_get_sizes (gmem, NULL, NULL);
+
+  if (size == -1)
+    size = mem_size > offset ? mem_size - offset : 0;
+
+  /* @XXX: if one-memory buffer it's possible to copy */
+  if (offset == 0 && size == mem_size && buf->n_mems == 1) {
+    GstVaBufferSurface *buf_copy;
+    GstMemory *copy;
+
+    GST_VA_MEMORY_POOL_LOCK (&self->pool);
+    copy = gst_va_memory_pool_pop (&self->pool);
+    GST_VA_MEMORY_POOL_UNLOCK (&self->pool);
+
+    if (!copy) {
+      GstBuffer *buffer = gst_buffer_new ();
+      if (!gst_va_dmabuf_allocator_setup_buffer (gmem->allocator, buffer)) {
+        GST_WARNING_OBJECT (self, "Failed to create a new dmabuf memory");
+        return NULL;
+      }
+
+      copy = gst_buffer_get_memory (buffer, 0);
+      gst_buffer_unref (buffer);
+    }
+
+    buf_copy = gst_mini_object_get_qdata (GST_MINI_OBJECT (copy),
+        gst_va_buffer_surface_quark ());
+    g_assert (buf_copy->n_mems == 1);
+
+    if (!self->copy)
+      self->copy = gst_va_surface_copy_new (self->display, &self->info);
+    if (self->copy && gst_va_surface_copy (self->copy, buf_copy->surface,
+            buf->surface))
+      return copy;
+
+    gst_memory_unref (copy);
+
+    /* try system memory */
+  }
+
+  if (*drm_mod != 0) {
+    GST_ERROR_OBJECT (self, "Failed to copy dmabuf because non-linear "
+        "modifier: %#lx.", *drm_mod);
+    return NULL;
+  }
+
+  /* fallback to system memory */
+  return self->parent_copy (gmem, offset, size);
+
+}
+
 static gpointer
 gst_va_dmabuf_mem_map (GstMemory * gmem, gsize maxsize, GstMapFlags flags)
 {
@@ -282,6 +363,7 @@ gst_va_dmabuf_allocator_finalize (GObject * object)
 {
   GstVaDmabufAllocator *self = GST_VA_DMABUF_ALLOCATOR (object);
 
+  g_clear_pointer (&self->copy, gst_va_surface_copy_free);
   gst_va_memory_pool_finalize (&self->pool);
   gst_clear_object (&self->display);
 
@@ -314,9 +396,14 @@ gst_va_dmabuf_allocator_class_init (GstVaDmabufAllocatorClass * klass)
 static void
 gst_va_dmabuf_allocator_init (GstVaDmabufAllocator * self)
 {
+  GstAllocator *allocator = GST_ALLOCATOR (self);
+
+  self->parent_map = allocator->mem_map;
+  allocator->mem_map = gst_va_dmabuf_mem_map;
+  self->parent_copy = allocator->mem_copy;
+  allocator->mem_copy = gst_va_dmabuf_mem_copy;
+
   gst_va_memory_pool_init (&self->pool);
-  self->parent_map = GST_ALLOCATOR (self)->mem_map;
-  GST_ALLOCATOR (self)->mem_map = gst_va_dmabuf_mem_map;
 }
 
 GstAllocator *
@@ -673,6 +760,8 @@ gst_va_dmabuf_allocator_set_format (GstAllocator * allocator,
   self->usage_hint = usage_hint;
   self->info = *info;
 
+  g_clear_pointer (&self->copy, gst_va_surface_copy_free);
+
   ret = gst_va_dmabuf_allocator_try (allocator);
 
   if (ret)