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
_init_debug(void)45 _init_debug (void)
46 {
47 static 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
gst_vulkan_trash_dispose(GstVulkanTrash * trash)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
gst_vulkan_trash_deinit(GstVulkanTrash * trash)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
gst_vulkan_trash_free(GstMiniObject * object)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
gst_vulkan_trash_init(GstVulkanTrash * trash,GstVulkanFence * fence,GstVulkanTrashNotify notify,gpointer user_data)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 *
gst_vulkan_trash_new(GstVulkanFence * fence,GstVulkanTrashNotify notify,gpointer user_data)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
gst_vulkan_trash_object_unref(GstVulkanDevice * device,gpointer user_data)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
gst_vulkan_trash_mini_object_unref(GstVulkanDevice * device,gpointer user_data)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
gst_vulkan_trash_list_gc(GstVulkanTrashList * trash_list)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
gst_vulkan_trash_list_add(GstVulkanTrashList * trash_list,GstVulkanTrash * trash)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
gst_vulkan_trash_list_wait(GstVulkanTrashList * trash_list,guint64 timeout)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
gst_vulkan_trash_list_alloc_impl(GstVulkanHandlePool * pool,GError ** error)329 gst_vulkan_trash_list_alloc_impl (GstVulkanHandlePool * pool, GError ** error)
330 {
331 return g_new0 (GstVulkanTrash, 1);
332 }
333
334 static void
gst_vulkan_trash_list_release_impl(GstVulkanHandlePool * pool,gpointer handle)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
gst_vulkan_trash_list_free_impl(GstVulkanHandlePool * pool,gpointer handle)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
gst_vulkan_trash_list_class_init(GstVulkanTrashListClass * klass)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
gst_vulkan_trash_list_init(GstVulkanTrashList * trash_list)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 *
gst_vulkan_trash_list_acquire(GstVulkanTrashList * trash_list,GstVulkanFence * fence,GstVulkanTrashNotify notify,gpointer user_data)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
gst_vulkan_trash_fence_list_gc(GstVulkanTrashList * trash_list)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
gst_vulkan_trash_fence_list_wait(GstVulkanTrashList * trash_list,guint64 timeout)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
gst_vulkan_trash_fence_list_add(GstVulkanTrashList * trash_list,GstVulkanTrash * trash)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
gst_vulkan_trash_fence_list_finalize(GObject * object)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
gst_vulkan_trash_fence_list_class_init(GstVulkanTrashFenceListClass * klass)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
gst_vulkan_trash_fence_list_init(GstVulkanTrashFenceList * trash_list)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 *
gst_vulkan_trash_fence_list_new(void)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);
539