abce93c1f9ba859cb20672d6005104d83dcfa5ca
[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 GST_DEBUG_CATEGORY (gst_debug_vulkan_trash);
29 #define GST_CAT_DEFAULT gst_debug_vulkan_trash
30
31 #define gst_vulkan_trash_release(c,t) \
32     gst_vulkan_handle_pool_release (GST_VULKAN_HANDLE_POOL_CAST (c), t);
33
34 static void
35 _init_debug (void)
36 {
37   static volatile gsize init;
38
39   if (g_once_init_enter (&init)) {
40     GST_DEBUG_CATEGORY_INIT (gst_debug_vulkan_trash,
41         "vulkantrash", 0, "Vulkan Trash");
42     g_once_init_leave (&init, 1);
43   }
44 }
45
46 static gboolean
47 gst_vulkan_trash_dispose (GstVulkanTrash * trash)
48 {
49   GstVulkanTrashList *cache;
50
51   /* no pool, do free */
52   if ((cache = trash->cache) == NULL)
53     return TRUE;
54
55   /* keep the buffer alive */
56   gst_vulkan_trash_ref (trash);
57   /* return the buffer to the pool */
58   gst_vulkan_trash_release (cache, trash);
59
60   return FALSE;
61 }
62
63 static void
64 gst_vulkan_trash_deinit (GstVulkanTrash * trash)
65 {
66   if (trash->fence) {
67     g_warn_if_fail (gst_vulkan_fence_is_signaled (trash->fence));
68     gst_vulkan_fence_unref (trash->fence);
69     trash->fence = NULL;
70   }
71
72   trash->notify = NULL;
73   trash->user_data = NULL;
74 }
75
76 static void
77 gst_vulkan_trash_free (GstMiniObject * object)
78 {
79   GstVulkanTrash *trash = (GstVulkanTrash *) object;
80
81   GST_TRACE ("Freeing trash object %p with fence %" GST_PTR_FORMAT, trash,
82       trash->fence);
83
84   gst_vulkan_trash_deinit (trash);
85
86   g_free (trash);
87 }
88
89 static void
90 gst_vulkan_trash_init (GstVulkanTrash * trash, GstVulkanFence * fence,
91     GstVulkanTrashNotify notify, gpointer user_data)
92 {
93   g_return_if_fail (fence != NULL);
94   g_return_if_fail (GST_IS_VULKAN_DEVICE (fence->device));
95   g_return_if_fail (notify != NULL);
96
97   gst_mini_object_init ((GstMiniObject *) trash, 0,
98       gst_vulkan_trash_get_type (), NULL,
99       (GstMiniObjectDisposeFunction) gst_vulkan_trash_dispose,
100       (GstMiniObjectFreeFunction) gst_vulkan_trash_free);
101   GST_TRACE ("Initializing trash object %p with fence %" GST_PTR_FORMAT
102       " on device %" GST_PTR_FORMAT, trash, fence, fence->device);
103   trash->fence = gst_vulkan_fence_ref (fence);
104   trash->notify = notify;
105   trash->user_data = user_data;
106 }
107
108 /**
109  * gst_vulkan_trash_new:
110  * @fence: a #GstVulkanFence
111  * @notify: (scope async): a #GstVulkanTrashNotify
112  * @user_data: (closure notify): user data for @notify
113  *
114  * Create and return a new #GstVulkanTrash object that will stores a callback
115  * to call when @fence is signalled.
116  *
117  * Returns: (transfer full): a new #GstVulkanTrash
118  */
119 GstVulkanTrash *
120 gst_vulkan_trash_new (GstVulkanFence * fence, GstVulkanTrashNotify notify,
121     gpointer user_data)
122 {
123   GstVulkanTrash *ret = NULL;
124
125   g_return_val_if_fail (fence != NULL, NULL);
126   g_return_val_if_fail (GST_IS_VULKAN_DEVICE (fence->device), NULL);
127   g_return_val_if_fail (notify != NULL, NULL);
128
129   _init_debug ();
130
131   ret = g_new0 (GstVulkanTrash, 1);
132   GST_TRACE ("Creating new trash object %p with fence %" GST_PTR_FORMAT
133       " on device %" GST_PTR_FORMAT, ret, fence, fence->device);
134   gst_vulkan_trash_init (ret, fence, notify, user_data);
135
136   return ret;
137 }
138
139 #if GLIB_SIZEOF_VOID_P == 8
140 # define PUSH_NON_DISPATCHABLE_HANDLE_TO_GPOINTER(pointer, handle) pointer = (gpointer) handle
141 # define TAKE_NON_DISPATCHABLE_HANDLE_FROM_GPOINTER(handle, type, pointer) handle = (type) pointer
142 #else
143 # define PUSH_NON_DISPATCHABLE_HANDLE_TO_GPOINTER(pointer, handle) \
144     G_STMT_START { \
145       pointer = g_new0(guint64, 1); \
146       *((GstVulkanHandleTypedef *) pointer) = (GstVulkanHandleTypedef) handle; \
147     } G_STMT_END
148 # define TAKE_NON_DISPATCHABLE_HANDLE_FROM_GPOINTER(handle, type, pointer) \
149     G_STMT_START { \
150       handle = *((type *) pointer); \
151       g_free (pointer); \
152     } G_STMT_END
153 #endif
154
155 #define FREE_DESTROY_FUNC(func, type, type_name) \
156 static void \
157 G_PASTE(_free_,type_name) (GstVulkanDevice * device, gpointer resource_handle) \
158 { \
159   type resource; \
160   TAKE_NON_DISPATCHABLE_HANDLE_FROM_GPOINTER(resource, type, resource_handle); \
161   GST_TRACE_OBJECT (device, "Freeing vulkan " G_STRINGIFY (type) \
162       " %" GST_VULKAN_NON_DISPATCHABLE_HANDLE_FORMAT, resource); \
163   func (device->device, resource, NULL); \
164 } \
165 GstVulkanTrash * \
166 G_PASTE(gst_vulkan_trash_new_free_,type_name) (GstVulkanFence * fence, \
167     type type_name) \
168 { \
169   GstVulkanTrash *trash; \
170   gpointer handle_data; \
171   PUSH_NON_DISPATCHABLE_HANDLE_TO_GPOINTER(handle_data, type_name); \
172   g_return_val_if_fail (type_name != VK_NULL_HANDLE, VK_NULL_HANDLE); \
173   trash = gst_vulkan_trash_new (fence, \
174       (GstVulkanTrashNotify) G_PASTE(_free_,type_name), handle_data); \
175   return trash; \
176 }
177
178 FREE_DESTROY_FUNC (vkDestroySemaphore, VkSemaphore, semaphore);
179 #define FREE_WITH_VK_PARENT(func, type, type_name, parent_type) \
180 struct G_PASTE(free_parent_info_,type_name) \
181 { \
182   parent_type parent; \
183   type resource; \
184 }; \
185 static void \
186 G_PASTE(_free_,type_name) (GstVulkanDevice * device, struct G_PASTE(free_parent_info_,type_name) *info) \
187 { \
188   GST_TRACE_OBJECT (device, "Freeing vulkan " G_STRINGIFY (type) \
189       " %" GST_VULKAN_NON_DISPATCHABLE_HANDLE_FORMAT, info->resource); \
190   func (device->device, info->parent, 1, &info->resource); \
191   g_free (info); \
192 } \
193 GstVulkanTrash * \
194 G_PASTE(gst_vulkan_trash_new_free_,type_name) (GstVulkanFence * fence, \
195     parent_type parent, type type_name) \
196 { \
197   struct G_PASTE(free_parent_info_,type_name) *info; \
198   GstVulkanTrash *trash; \
199   g_return_val_if_fail (type_name != VK_NULL_HANDLE, VK_NULL_HANDLE); \
200   info = g_new0 (struct G_PASTE(free_parent_info_,type_name), 1); \
201   /* FIXME: keep parent alive ? */\
202   info->parent = parent; \
203   info->resource = type_name; \
204   trash = gst_vulkan_trash_new (fence, \
205       (GstVulkanTrashNotify) G_PASTE(_free_,type_name), info); \
206   return trash; \
207 }
208
209 void
210 gst_vulkan_trash_object_unref (GstVulkanDevice * device, gpointer user_data)
211 {
212   gst_object_unref ((GstObject *) user_data);
213 }
214
215 void
216 gst_vulkan_trash_mini_object_unref (GstVulkanDevice * device,
217     gpointer user_data)
218 {
219   gst_mini_object_unref ((GstMiniObject *) user_data);
220 }
221
222 G_DEFINE_TYPE_WITH_CODE (GstVulkanTrashList, gst_vulkan_trash_list,
223     GST_TYPE_VULKAN_HANDLE_POOL, _init_debug ());
224
225 void
226 gst_vulkan_trash_list_gc (GstVulkanTrashList * trash_list)
227 {
228   GstVulkanTrashListClass *trash_class;
229   g_return_if_fail (GST_IS_VULKAN_TRASH_LIST (trash_list));
230   trash_class = GST_VULKAN_TRASH_LIST_GET_CLASS (trash_list);
231   g_return_if_fail (trash_class->gc_func != NULL);
232
233   trash_class->gc_func (trash_list);
234 }
235
236 gboolean
237 gst_vulkan_trash_list_add (GstVulkanTrashList * trash_list,
238     GstVulkanTrash * trash)
239 {
240   GstVulkanTrashListClass *trash_class;
241   g_return_val_if_fail (GST_IS_VULKAN_TRASH_LIST (trash_list), FALSE);
242   trash_class = GST_VULKAN_TRASH_LIST_GET_CLASS (trash_list);
243   g_return_val_if_fail (trash_class->add_func != NULL, FALSE);
244
245   return trash_class->add_func (trash_list, trash);
246 }
247
248 gboolean
249 gst_vulkan_trash_list_wait (GstVulkanTrashList * trash_list, guint64 timeout)
250 {
251   GstVulkanTrashListClass *trash_class;
252   g_return_val_if_fail (GST_IS_VULKAN_TRASH_LIST (trash_list), FALSE);
253   trash_class = GST_VULKAN_TRASH_LIST_GET_CLASS (trash_list);
254   g_return_val_if_fail (trash_class->wait_func != NULL, FALSE);
255
256   return trash_class->wait_func (trash_list, timeout);
257 }
258
259 static gpointer
260 gst_vulkan_trash_list_alloc_impl (GstVulkanHandlePool * pool, GError ** error)
261 {
262   return g_new0 (GstVulkanTrash, 1);
263 }
264
265 static void
266 gst_vulkan_trash_list_release_impl (GstVulkanHandlePool * pool, gpointer handle)
267 {
268   GstVulkanTrash *trash = handle;
269
270   GST_TRACE_OBJECT (pool, "reset trash object %p", trash);
271
272   gst_vulkan_trash_deinit (trash);
273   gst_clear_object (&trash->cache);
274
275   GST_VULKAN_HANDLE_POOL_CLASS (gst_vulkan_trash_list_parent_class)->release
276       (pool, handle);
277 }
278
279 static void
280 gst_vulkan_trash_list_free_impl (GstVulkanHandlePool * pool, gpointer handle)
281 {
282   GstVulkanTrash *trash = handle;
283
284   gst_vulkan_trash_unref (trash);
285 }
286
287 static void
288 gst_vulkan_trash_list_class_init (GstVulkanTrashListClass * klass)
289 {
290   GstVulkanHandlePoolClass *pool_class = (GstVulkanHandlePoolClass *) klass;
291
292   pool_class->alloc = gst_vulkan_trash_list_alloc_impl;
293   pool_class->release = gst_vulkan_trash_list_release_impl;
294   pool_class->free = gst_vulkan_trash_list_free_impl;
295 }
296
297 static void
298 gst_vulkan_trash_list_init (GstVulkanTrashList * trash_list)
299 {
300 }
301
302 GstVulkanTrash *
303 gst_vulkan_trash_list_acquire (GstVulkanTrashList * trash_list,
304     GstVulkanFence * fence, GstVulkanTrashNotify notify, gpointer user_data)
305 {
306   GstVulkanHandlePool *pool = GST_VULKAN_HANDLE_POOL (trash_list);
307   GstVulkanHandlePoolClass *pool_class;
308   GstVulkanTrash *trash;
309
310   g_return_val_if_fail (GST_IS_VULKAN_TRASH_LIST (trash_list), NULL);
311
312   pool_class = GST_VULKAN_HANDLE_POOL_GET_CLASS (trash_list);
313
314   trash = pool_class->acquire (pool, NULL);
315   gst_vulkan_trash_init (trash, fence, notify, user_data);
316   trash->cache = gst_object_ref (trash_list);
317
318   GST_TRACE_OBJECT (trash_list, "acquired trash object %p", trash);
319
320   return trash;
321 }
322
323 typedef struct _GstVulkanTrashFenceList GstVulkanTrashFenceList;
324
325 struct _GstVulkanTrashFenceList
326 {
327   GstVulkanTrashList parent;
328
329   GList *list;
330 };
331
332 G_DEFINE_TYPE (GstVulkanTrashFenceList, gst_vulkan_trash_fence_list,
333     GST_TYPE_VULKAN_TRASH_LIST);
334
335 static void
336 gst_vulkan_trash_fence_list_gc (GstVulkanTrashList * trash_list)
337 {
338   GstVulkanTrashFenceList *fence_list = (GstVulkanTrashFenceList *) trash_list;
339   GList *l = fence_list->list;
340
341   while (l) {
342     GstVulkanTrash *trash = l->data;
343
344     if (gst_vulkan_fence_is_signaled (trash->fence)) {
345       GList *next = g_list_next (l);
346       GST_TRACE_OBJECT (fence_list, "fence %" GST_PTR_FORMAT " has been "
347           "signalled, notifying", trash->fence);
348       trash->notify (trash->fence->device, trash->user_data);
349       gst_vulkan_trash_unref (trash);
350       fence_list->list = g_list_delete_link (fence_list->list, l);
351       l = next;
352     } else {
353       l = g_list_next (l);
354     }
355   }
356 }
357
358 static gboolean
359 gst_vulkan_trash_fence_list_wait (GstVulkanTrashList * trash_list,
360     guint64 timeout)
361 {
362   GstVulkanTrashFenceList *fence_list = (GstVulkanTrashFenceList *) trash_list;
363   VkResult err = VK_SUCCESS;
364   guint i, n;
365
366   /* remove all the previously signaled fences */
367   gst_vulkan_trash_fence_list_gc (trash_list);
368
369   n = g_list_length (fence_list->list);
370   if (n > 0) {
371     VkFence *fences;
372     GstVulkanDevice *device = NULL;
373     GList *l = NULL;
374
375     fences = g_new0 (VkFence, n);
376     for (i = 0, l = fence_list->list; i < n; i++, l = g_list_next (l)) {
377       GstVulkanTrash *trash = l->data;
378
379       if (device == NULL)
380         device = trash->fence->device;
381
382       fences[i] = trash->fence->fence;
383
384       /* only support waiting on fences from the same device */
385       g_assert (device == trash->fence->device);
386     }
387
388     GST_TRACE_OBJECT (trash_list, "Waiting on %d fences with timeout %"
389         GST_TIME_FORMAT, n, GST_TIME_ARGS (timeout));
390     err = vkWaitForFences (device->device, n, fences, TRUE, timeout);
391     g_free (fences);
392
393     gst_vulkan_trash_fence_list_gc (trash_list);
394   }
395
396   return err == VK_SUCCESS;
397 }
398
399 static gboolean
400 gst_vulkan_trash_fence_list_add (GstVulkanTrashList * trash_list,
401     GstVulkanTrash * trash)
402 {
403   GstVulkanTrashFenceList *fence_list = (GstVulkanTrashFenceList *) trash_list;
404
405   g_return_val_if_fail (GST_MINI_OBJECT_TYPE (trash) == GST_TYPE_VULKAN_TRASH,
406       FALSE);
407
408   /* XXX: do something better based on the actual fence */
409   fence_list->list = g_list_prepend (fence_list->list, trash);
410
411   return TRUE;
412 }
413
414 static void
415 gst_vulkan_trash_fence_list_finalize (GObject * object)
416 {
417   GstVulkanTrashList *trash_list = (GstVulkanTrashList *) object;
418   GstVulkanTrashFenceList *fence_list = (GstVulkanTrashFenceList *) object;
419
420   gst_vulkan_trash_fence_list_gc (trash_list);
421   g_warn_if_fail (fence_list->list == NULL);
422
423   G_OBJECT_CLASS (gst_vulkan_trash_fence_list_parent_class)->finalize (object);
424 }
425
426 static void
427 gst_vulkan_trash_fence_list_class_init (GstVulkanTrashFenceListClass * klass)
428 {
429   GstVulkanTrashListClass *trash_class = (GstVulkanTrashListClass *) klass;
430   GObjectClass *object_class = (GObjectClass *) klass;
431
432   trash_class->add_func = gst_vulkan_trash_fence_list_add;
433   trash_class->gc_func = gst_vulkan_trash_fence_list_gc;
434   trash_class->wait_func = gst_vulkan_trash_fence_list_wait;
435
436   object_class->finalize = gst_vulkan_trash_fence_list_finalize;
437 }
438
439 static void
440 gst_vulkan_trash_fence_list_init (GstVulkanTrashFenceList * trash_list)
441 {
442 }
443
444 GstVulkanTrashList *
445 gst_vulkan_trash_fence_list_new (void)
446 {
447   return g_object_new (gst_vulkan_trash_fence_list_get_type (), NULL);
448 }
449
450 GST_DEFINE_MINI_OBJECT_TYPE (GstVulkanTrash, gst_vulkan_trash);