56c12bafe7bda626e4a7ca4a375f1baf3b4b0e48
[platform/upstream/gstreamer.git] / gst-libs / gst / vulkan / gstvktrash.c
1 /*
2  * GStreamer
3  * Copyright (C) 2016 Matthew Waters <matthew@centricular.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "gstvktrash.h"
26 #include "gstvkhandle.h"
27
28 /**
29  * SECTION:vktrash
30  * @title: GstVulkanTrash
31  * @short_description: Vulkan helper object for freeing resources after a #GstVulkanFence is signalled
32  * @see_also: #GstVulkanFence, #GstVulkanQueue
33  *
34  * #GstVulkanTrash is a helper object for freeing resources after a
35  * #GstVulkanFence is signalled.
36  */
37
38 GST_DEBUG_CATEGORY (gst_debug_vulkan_trash);
39 #define GST_CAT_DEFAULT gst_debug_vulkan_trash
40
41 #define gst_vulkan_trash_release(c,t) \
42     gst_vulkan_handle_pool_release (GST_VULKAN_HANDLE_POOL_CAST (c), t);
43
44 static void
45 _init_debug (void)
46 {
47   static volatile gsize init;
48
49   if (g_once_init_enter (&init)) {
50     GST_DEBUG_CATEGORY_INIT (gst_debug_vulkan_trash,
51         "vulkantrash", 0, "Vulkan Trash");
52     g_once_init_leave (&init, 1);
53   }
54 }
55
56 static gboolean
57 gst_vulkan_trash_dispose (GstVulkanTrash * trash)
58 {
59   GstVulkanTrashList *cache;
60
61   /* no pool, do free */
62   if ((cache = trash->cache) == NULL)
63     return TRUE;
64
65   /* keep the buffer alive */
66   gst_vulkan_trash_ref (trash);
67   /* return the trash object to the pool */
68   gst_vulkan_trash_release (cache, trash);
69
70   return FALSE;
71 }
72
73 static void
74 gst_vulkan_trash_deinit (GstVulkanTrash * trash)
75 {
76   if (trash->fence) {
77     g_warn_if_fail (gst_vulkan_fence_is_signaled (trash->fence));
78     gst_vulkan_fence_unref (trash->fence);
79     trash->fence = NULL;
80   }
81
82   trash->notify = NULL;
83   trash->user_data = NULL;
84 }
85
86 static void
87 gst_vulkan_trash_free (GstMiniObject * object)
88 {
89   GstVulkanTrash *trash = (GstVulkanTrash *) object;
90
91   GST_TRACE ("Freeing trash object %p with fence %" GST_PTR_FORMAT, trash,
92       trash->fence);
93
94   gst_vulkan_trash_deinit (trash);
95
96   g_free (trash);
97 }
98
99 static void
100 gst_vulkan_trash_init (GstVulkanTrash * trash, GstVulkanFence * fence,
101     GstVulkanTrashNotify notify, gpointer user_data)
102 {
103   g_return_if_fail (fence != NULL);
104   g_return_if_fail (GST_IS_VULKAN_DEVICE (fence->device));
105   g_return_if_fail (notify != NULL);
106
107   gst_mini_object_init ((GstMiniObject *) trash, 0,
108       gst_vulkan_trash_get_type (), NULL,
109       (GstMiniObjectDisposeFunction) gst_vulkan_trash_dispose,
110       (GstMiniObjectFreeFunction) gst_vulkan_trash_free);
111   GST_TRACE ("Initializing trash object %p with fence %" GST_PTR_FORMAT
112       " on device %" GST_PTR_FORMAT, trash, fence, fence->device);
113   trash->fence = gst_vulkan_fence_ref (fence);
114   trash->notify = notify;
115   trash->user_data = user_data;
116 }
117
118 /**
119  * gst_vulkan_trash_new:
120  * @fence: a #GstVulkanFence
121  * @notify: (scope async): a #GstVulkanTrashNotify
122  * @user_data: (closure notify): user data for @notify
123  *
124  * Create and return a new #GstVulkanTrash object that will stores a callback
125  * to call when @fence is signalled.
126  *
127  * Returns: (transfer full): a new #GstVulkanTrash
128  *
129  * Since: 1.18
130  */
131 GstVulkanTrash *
132 gst_vulkan_trash_new (GstVulkanFence * fence, GstVulkanTrashNotify notify,
133     gpointer user_data)
134 {
135   GstVulkanTrash *ret = NULL;
136
137   g_return_val_if_fail (fence != NULL, NULL);
138   g_return_val_if_fail (GST_IS_VULKAN_DEVICE (fence->device), NULL);
139   g_return_val_if_fail (notify != NULL, NULL);
140
141   _init_debug ();
142
143   ret = g_new0 (GstVulkanTrash, 1);
144   GST_TRACE ("Creating new trash object %p with fence %" GST_PTR_FORMAT
145       " on device %" GST_PTR_FORMAT, ret, fence, fence->device);
146   gst_vulkan_trash_init (ret, fence, notify, user_data);
147
148   return ret;
149 }
150
151 #if GLIB_SIZEOF_VOID_P == 8
152 # define PUSH_NON_DISPATCHABLE_HANDLE_TO_GPOINTER(pointer, handle) pointer = (gpointer) handle
153 # define TAKE_NON_DISPATCHABLE_HANDLE_FROM_GPOINTER(handle, type, pointer) handle = (type) pointer
154 #else
155 # define PUSH_NON_DISPATCHABLE_HANDLE_TO_GPOINTER(pointer, handle) \
156     G_STMT_START { \
157       pointer = g_new0(guint64, 1); \
158       *((GstVulkanHandleTypedef *) pointer) = (GstVulkanHandleTypedef) handle; \
159     } G_STMT_END
160 # define TAKE_NON_DISPATCHABLE_HANDLE_FROM_GPOINTER(handle, type, pointer) \
161     G_STMT_START { \
162       handle = *((type *) pointer); \
163       g_free (pointer); \
164     } G_STMT_END
165 #endif
166
167 #define FREE_DESTROY_FUNC(func, type, type_name) \
168 static void \
169 G_PASTE(_free_,type_name) (GstVulkanDevice * device, gpointer resource_handle) \
170 { \
171   type resource; \
172   TAKE_NON_DISPATCHABLE_HANDLE_FROM_GPOINTER(resource, type, resource_handle); \
173   GST_TRACE_OBJECT (device, "Freeing vulkan " G_STRINGIFY (type) \
174       " %" GST_VULKAN_NON_DISPATCHABLE_HANDLE_FORMAT, resource); \
175   func (device->device, resource, NULL); \
176 } \
177 GstVulkanTrash * \
178 G_PASTE(gst_vulkan_trash_new_free_,type_name) (GstVulkanFence * fence, \
179     type type_name) \
180 { \
181   GstVulkanTrash *trash; \
182   gpointer handle_data; \
183   PUSH_NON_DISPATCHABLE_HANDLE_TO_GPOINTER(handle_data, type_name); \
184   g_return_val_if_fail (type_name != VK_NULL_HANDLE, VK_NULL_HANDLE); \
185   trash = gst_vulkan_trash_new (fence, \
186       (GstVulkanTrashNotify) G_PASTE(_free_,type_name), handle_data); \
187   return trash; \
188 }
189
190 /**
191  * gst_vulkan_trash_new_free_semaphore:
192  * @fence: the #GstVulkanFence
193  * @semaphore: a `VkSemaphore` to free
194  *
195  * Returns: (transfer full): a new #GstVulkanTrash object that will the free
196  *     @semaphore when @fence is signalled
197  *
198  * Since: 1.18
199  */
200 FREE_DESTROY_FUNC (vkDestroySemaphore, VkSemaphore, semaphore);
201 #define FREE_WITH_VK_PARENT(func, type, type_name, parent_type) \
202 struct G_PASTE(free_parent_info_,type_name) \
203 { \
204   parent_type parent; \
205   type resource; \
206 }; \
207 static void \
208 G_PASTE(_free_,type_name) (GstVulkanDevice * device, struct G_PASTE(free_parent_info_,type_name) *info) \
209 { \
210   GST_TRACE_OBJECT (device, "Freeing vulkan " G_STRINGIFY (type) \
211       " %" GST_VULKAN_NON_DISPATCHABLE_HANDLE_FORMAT, info->resource); \
212   func (device->device, info->parent, 1, &info->resource); \
213   g_free (info); \
214 } \
215 GstVulkanTrash * \
216 G_PASTE(gst_vulkan_trash_new_free_,type_name) (GstVulkanFence * fence, \
217     parent_type parent, type type_name) \
218 { \
219   struct G_PASTE(free_parent_info_,type_name) *info; \
220   GstVulkanTrash *trash; \
221   g_return_val_if_fail (type_name != VK_NULL_HANDLE, VK_NULL_HANDLE); \
222   info = g_new0 (struct G_PASTE(free_parent_info_,type_name), 1); \
223   /* FIXME: keep parent alive ? */\
224   info->parent = parent; \
225   info->resource = type_name; \
226   trash = gst_vulkan_trash_new (fence, \
227       (GstVulkanTrashNotify) G_PASTE(_free_,type_name), info); \
228   return trash; \
229 }
230
231 /**
232  * gst_vulkan_trash_object_unref:
233  * @device: the #GstVulkanDevice
234  * @user_data: the #GstMiniObject
235  *
236  * A #GstVulkanTrashNotify implementation for unreffing a #GstObject when the
237  * associated #GstVulkanFence is signalled
238  *
239  * Since: 1.18
240  */
241 void
242 gst_vulkan_trash_object_unref (GstVulkanDevice * device, gpointer user_data)
243 {
244   gst_object_unref ((GstObject *) user_data);
245 }
246
247 /**
248  * gst_vulkan_trash_mini_object_unref:
249  * @device: the #GstVulkanDevice
250  * @user_data: the #GstMiniObject
251  *
252  * A #GstVulkanTrashNotify implementation for unreffing a #GstMiniObject when the
253  * associated #GstVulkanFence is signalled
254  *
255  * Since: 1.18
256  */
257 void
258 gst_vulkan_trash_mini_object_unref (GstVulkanDevice * device,
259     gpointer user_data)
260 {
261   gst_mini_object_unref ((GstMiniObject *) user_data);
262 }
263
264 G_DEFINE_TYPE_WITH_CODE (GstVulkanTrashList, gst_vulkan_trash_list,
265     GST_TYPE_VULKAN_HANDLE_POOL, _init_debug ());
266
267 /**
268  * gst_vulkan_trash_list_gc:
269  * @trash_list: the #GstVulkanTrashList
270  *
271  * Remove any stored #GstVulkanTrash objects that have had their associated
272  * #GstVulkanFence signalled.
273  *
274  * Since: 1.18
275  */
276 void
277 gst_vulkan_trash_list_gc (GstVulkanTrashList * trash_list)
278 {
279   GstVulkanTrashListClass *trash_class;
280   g_return_if_fail (GST_IS_VULKAN_TRASH_LIST (trash_list));
281   trash_class = GST_VULKAN_TRASH_LIST_GET_CLASS (trash_list);
282   g_return_if_fail (trash_class->gc_func != NULL);
283
284   trash_class->gc_func (trash_list);
285 }
286
287 /**
288  * gst_vulkan_trash_list_add:
289  * @trash_list: the #GstVulkanTrashList
290  * @trash: #GstVulkanTrash object to add to the list
291  *
292  * Returns: whether @trash could be added to @trash_list
293  *
294  * Since: 1.18
295  */
296 gboolean
297 gst_vulkan_trash_list_add (GstVulkanTrashList * trash_list,
298     GstVulkanTrash * trash)
299 {
300   GstVulkanTrashListClass *trash_class;
301   g_return_val_if_fail (GST_IS_VULKAN_TRASH_LIST (trash_list), FALSE);
302   trash_class = GST_VULKAN_TRASH_LIST_GET_CLASS (trash_list);
303   g_return_val_if_fail (trash_class->add_func != NULL, FALSE);
304
305   return trash_class->add_func (trash_list, trash);
306 }
307
308 /**
309  * gst_vulkan_trash_list_wait:
310  * @trash_list: the #GstVulkanTrashList
311  * @timeout: timeout in ns to wait, -1 for indefinite
312  *
313  * Returns: whether the wait succeeded in waiting for all objects to be freed.
314  *
315  * Since: 1.18
316  */
317 gboolean
318 gst_vulkan_trash_list_wait (GstVulkanTrashList * trash_list, guint64 timeout)
319 {
320   GstVulkanTrashListClass *trash_class;
321   g_return_val_if_fail (GST_IS_VULKAN_TRASH_LIST (trash_list), FALSE);
322   trash_class = GST_VULKAN_TRASH_LIST_GET_CLASS (trash_list);
323   g_return_val_if_fail (trash_class->wait_func != NULL, FALSE);
324
325   return trash_class->wait_func (trash_list, timeout);
326 }
327
328 static gpointer
329 gst_vulkan_trash_list_alloc_impl (GstVulkanHandlePool * pool, GError ** error)
330 {
331   return g_new0 (GstVulkanTrash, 1);
332 }
333
334 static void
335 gst_vulkan_trash_list_release_impl (GstVulkanHandlePool * pool, gpointer handle)
336 {
337   GstVulkanTrash *trash = handle;
338
339   GST_TRACE_OBJECT (pool, "reset trash object %p", trash);
340
341   gst_vulkan_trash_deinit (trash);
342   gst_clear_object (&trash->cache);
343
344   GST_VULKAN_HANDLE_POOL_CLASS (gst_vulkan_trash_list_parent_class)->release
345       (pool, handle);
346 }
347
348 static void
349 gst_vulkan_trash_list_free_impl (GstVulkanHandlePool * pool, gpointer handle)
350 {
351   GstVulkanTrash *trash = handle;
352
353   gst_vulkan_trash_unref (trash);
354 }
355
356 static void
357 gst_vulkan_trash_list_class_init (GstVulkanTrashListClass * klass)
358 {
359   GstVulkanHandlePoolClass *pool_class = (GstVulkanHandlePoolClass *) klass;
360
361   pool_class->alloc = gst_vulkan_trash_list_alloc_impl;
362   pool_class->release = gst_vulkan_trash_list_release_impl;
363   pool_class->free = gst_vulkan_trash_list_free_impl;
364 }
365
366 static void
367 gst_vulkan_trash_list_init (GstVulkanTrashList * trash_list)
368 {
369 }
370
371 /**
372  * gst_vulkan_trash_list_acquire:
373  * @trash_list: a #GstVulkanTrashList
374  * @fence: a #GstVulkanFence to wait for signalling
375  * @notify: (scope async): notify function for when @fence is signalled
376  * @user_data: user data for @notify
377  *
378  * Returns: (transfer full): a new or reused #GstVulkanTrash for the provided
379  *          parameters.
380  *
381  * Since: 1.18
382  */
383 GstVulkanTrash *
384 gst_vulkan_trash_list_acquire (GstVulkanTrashList * trash_list,
385     GstVulkanFence * fence, GstVulkanTrashNotify notify, gpointer user_data)
386 {
387   GstVulkanHandlePool *pool = GST_VULKAN_HANDLE_POOL (trash_list);
388   GstVulkanHandlePoolClass *pool_class;
389   GstVulkanTrash *trash;
390
391   g_return_val_if_fail (GST_IS_VULKAN_TRASH_LIST (trash_list), NULL);
392
393   pool_class = GST_VULKAN_HANDLE_POOL_GET_CLASS (trash_list);
394
395   trash = pool_class->acquire (pool, NULL);
396   gst_vulkan_trash_init (trash, fence, notify, user_data);
397   trash->cache = gst_object_ref (trash_list);
398
399   GST_TRACE_OBJECT (trash_list, "acquired trash object %p", trash);
400
401   return trash;
402 }
403
404 typedef struct _GstVulkanTrashFenceList GstVulkanTrashFenceList;
405
406 struct _GstVulkanTrashFenceList
407 {
408   GstVulkanTrashList parent;
409
410   GList *list;
411 };
412
413 G_DEFINE_TYPE (GstVulkanTrashFenceList, gst_vulkan_trash_fence_list,
414     GST_TYPE_VULKAN_TRASH_LIST);
415
416 static void
417 gst_vulkan_trash_fence_list_gc (GstVulkanTrashList * trash_list)
418 {
419   GstVulkanTrashFenceList *fence_list = (GstVulkanTrashFenceList *) trash_list;
420   GList *l = fence_list->list;
421
422   while (l) {
423     GstVulkanTrash *trash = l->data;
424
425     if (gst_vulkan_fence_is_signaled (trash->fence)) {
426       GList *next = g_list_next (l);
427       GST_TRACE_OBJECT (fence_list, "fence %" GST_PTR_FORMAT " has been "
428           "signalled, notifying", trash->fence);
429       trash->notify (trash->fence->device, trash->user_data);
430       gst_vulkan_trash_unref (trash);
431       fence_list->list = g_list_delete_link (fence_list->list, l);
432       l = next;
433     } else {
434       l = g_list_next (l);
435     }
436   }
437 }
438
439 static gboolean
440 gst_vulkan_trash_fence_list_wait (GstVulkanTrashList * trash_list,
441     guint64 timeout)
442 {
443   GstVulkanTrashFenceList *fence_list = (GstVulkanTrashFenceList *) trash_list;
444   VkResult err = VK_SUCCESS;
445   guint i, n;
446
447   /* remove all the previously signaled fences */
448   gst_vulkan_trash_fence_list_gc (trash_list);
449
450   n = g_list_length (fence_list->list);
451   if (n > 0) {
452     VkFence *fences;
453     GstVulkanDevice *device = NULL;
454     GList *l = NULL;
455
456     fences = g_new0 (VkFence, n);
457     for (i = 0, l = fence_list->list; i < n; i++, l = g_list_next (l)) {
458       GstVulkanTrash *trash = l->data;
459
460       if (device == NULL)
461         device = trash->fence->device;
462
463       fences[i] = trash->fence->fence;
464
465       /* only support waiting on fences from the same device */
466       g_assert (device == trash->fence->device);
467     }
468
469     GST_TRACE_OBJECT (trash_list, "Waiting on %d fences with timeout %"
470         GST_TIME_FORMAT, n, GST_TIME_ARGS (timeout));
471     err = vkWaitForFences (device->device, n, fences, TRUE, timeout);
472     g_free (fences);
473
474     gst_vulkan_trash_fence_list_gc (trash_list);
475   }
476
477   return err == VK_SUCCESS;
478 }
479
480 static gboolean
481 gst_vulkan_trash_fence_list_add (GstVulkanTrashList * trash_list,
482     GstVulkanTrash * trash)
483 {
484   GstVulkanTrashFenceList *fence_list = (GstVulkanTrashFenceList *) trash_list;
485
486   g_return_val_if_fail (GST_MINI_OBJECT_TYPE (trash) == GST_TYPE_VULKAN_TRASH,
487       FALSE);
488
489   /* XXX: do something better based on the actual fence */
490   fence_list->list = g_list_prepend (fence_list->list, trash);
491
492   return TRUE;
493 }
494
495 static void
496 gst_vulkan_trash_fence_list_finalize (GObject * object)
497 {
498   GstVulkanTrashList *trash_list = (GstVulkanTrashList *) object;
499   GstVulkanTrashFenceList *fence_list = (GstVulkanTrashFenceList *) object;
500
501   gst_vulkan_trash_fence_list_gc (trash_list);
502   g_warn_if_fail (fence_list->list == NULL);
503
504   G_OBJECT_CLASS (gst_vulkan_trash_fence_list_parent_class)->finalize (object);
505 }
506
507 static void
508 gst_vulkan_trash_fence_list_class_init (GstVulkanTrashFenceListClass * klass)
509 {
510   GstVulkanTrashListClass *trash_class = (GstVulkanTrashListClass *) klass;
511   GObjectClass *object_class = (GObjectClass *) klass;
512
513   trash_class->add_func = gst_vulkan_trash_fence_list_add;
514   trash_class->gc_func = gst_vulkan_trash_fence_list_gc;
515   trash_class->wait_func = gst_vulkan_trash_fence_list_wait;
516
517   object_class->finalize = gst_vulkan_trash_fence_list_finalize;
518 }
519
520 static void
521 gst_vulkan_trash_fence_list_init (GstVulkanTrashFenceList * trash_list)
522 {
523 }
524
525 /**
526  * gst_vulkan_trash_fence_list_new:
527  *
528  * Returns: (transfer full): a new #gst_vulkan_trash_fence_list_new
529  *
530  * Since: a.18
531  */
532 GstVulkanTrashList *
533 gst_vulkan_trash_fence_list_new (void)
534 {
535   return g_object_new (gst_vulkan_trash_fence_list_get_type (), NULL);
536 }
537
538 GST_DEFINE_MINI_OBJECT_TYPE (GstVulkanTrash, gst_vulkan_trash);