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 "vkbuffermemory.h"
26 
27 /**
28  * SECTION:vkbuffermemory
29  * @title: vkbuffermemory
30  * @short_description: memory subclass for Vulkan buffer memory
31  * @see_also: #GstMemory, #GstAllocator
32  *
33  * GstVulkanBufferMemory is a #GstMemory subclass providing support for the
34  * mapping of Vulkan device memory.
35  */
36 
37 #define GST_CAT_DEFUALT GST_CAT_VULKAN_BUFFER_MEMORY
38 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFUALT);
39 
40 static GstAllocator *_vulkan_buffer_memory_allocator;
41 
42 #define GST_VK_BUFFER_CREATE_INFO_INIT GST_VK_STRUCT_8
43 #define GST_VK_BUFFER_CREATE_INFO(info, pNext, flags, size, usage, sharingMode, queueFamilyIndexCount, pQueueFamilyIndices ) \
44   G_STMT_START { \
45     VkBufferCreateInfo tmp = GST_VK_BUFFER_CREATE_INFO_INIT (VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, pNext, flags, size, usage, sharingMode, queueFamilyIndexCount, pQueueFamilyIndices); \
46     (info) = tmp; \
47   } G_STMT_END
48 
49 static gboolean
_create_info_from_args(VkBufferCreateInfo * info,gsize size,VkBufferUsageFlags usage)50 _create_info_from_args (VkBufferCreateInfo * info, gsize size,
51     VkBufferUsageFlags usage)
52 {
53   /* FIXME: validate these */
54   GST_VK_BUFFER_CREATE_INFO (*info, NULL, 0, size, usage,
55       VK_SHARING_MODE_EXCLUSIVE, 0, NULL);
56 
57   return TRUE;
58 }
59 
60 #define GST_VK_BUFFER_VIEW_CREATE_INFO_INIT GST_VK_STRUCT_7
61 #define GST_VK_BUFFER_VIEW_CREATE_INFO(info, pNext, flags, buffer, format, offset, range) \
62   G_STMT_START { \
63     VkBufferViewCreateInfo tmp = GST_VK_BUFFER_VIEW_CREATE_INFO_INIT (VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO, pNext, flags, buffer, format, offset, range); \
64     (info) = tmp; \
65   } G_STMT_END
66 
67 static gboolean
_create_view_from_args(VkBufferViewCreateInfo * info,VkBuffer buffer,VkFormat format,gsize offset,gsize range)68 _create_view_from_args (VkBufferViewCreateInfo * info, VkBuffer buffer,
69     VkFormat format, gsize offset, gsize range)
70 {
71   /* FIXME: validate these */
72   g_assert (format != VK_FORMAT_UNDEFINED);
73 
74   GST_VK_BUFFER_VIEW_CREATE_INFO (*info, NULL, 0, buffer, format, offset,
75       range);
76 
77   return TRUE;
78 }
79 
80 static void
_vk_buffer_mem_init(GstVulkanBufferMemory * mem,GstAllocator * allocator,GstMemory * parent,GstVulkanDevice * device,VkBufferUsageFlags usage,GstAllocationParams * params,gsize size,gpointer user_data,GDestroyNotify notify)81 _vk_buffer_mem_init (GstVulkanBufferMemory * mem, GstAllocator * allocator,
82     GstMemory * parent, GstVulkanDevice * device, VkBufferUsageFlags usage,
83     GstAllocationParams * params, gsize size, gpointer user_data,
84     GDestroyNotify notify)
85 {
86   gsize align = gst_memory_alignment, offset = 0, maxsize = size;
87   GstMemoryFlags flags = 0;
88 
89   if (params) {
90     flags = params->flags;
91     align |= params->align;
92     offset = params->prefix;
93     maxsize += params->prefix + params->padding + align;
94   }
95 
96   gst_memory_init (GST_MEMORY_CAST (mem), flags, allocator, parent, maxsize,
97       align, offset, size);
98 
99   mem->device = gst_object_ref (device);
100   mem->wrapped = FALSE;
101   mem->notify = notify;
102   mem->user_data = user_data;
103 
104   g_mutex_init (&mem->lock);
105 
106   GST_CAT_DEBUG (GST_CAT_VULKAN_BUFFER_MEMORY,
107       "new Vulkan Buffer memory:%p size:%" G_GSIZE_FORMAT, mem, maxsize);
108 }
109 
110 static GstVulkanBufferMemory *
_vk_buffer_mem_new_alloc(GstAllocator * allocator,GstMemory * parent,GstVulkanDevice * device,VkFormat format,gsize size,VkBufferUsageFlags usage,VkMemoryPropertyFlags mem_prop_flags,gpointer user_data,GDestroyNotify notify)111 _vk_buffer_mem_new_alloc (GstAllocator * allocator, GstMemory * parent,
112     GstVulkanDevice * device, VkFormat format, gsize size,
113     VkBufferUsageFlags usage, VkMemoryPropertyFlags mem_prop_flags,
114     gpointer user_data, GDestroyNotify notify)
115 {
116   GstVulkanBufferMemory *mem = NULL;
117   GstAllocationParams params = { 0, };
118   VkBufferCreateInfo buffer_info;
119   GError *error = NULL;
120   guint32 type_idx;
121   VkBuffer buffer;
122   VkResult err;
123 
124   if (!_create_info_from_args (&buffer_info, size, usage)) {
125     GST_CAT_ERROR (GST_CAT_VULKAN_BUFFER_MEMORY, "Incorrect buffer parameters");
126     goto error;
127   }
128 
129   err = vkCreateBuffer (device->device, &buffer_info, NULL, &buffer);
130   if (gst_vulkan_error_to_g_error (err, &error, "vkCreateBuffer") < 0)
131     goto vk_error;
132 
133   mem = g_new0 (GstVulkanBufferMemory, 1);
134   vkGetBufferMemoryRequirements (device->device, buffer, &mem->requirements);
135 
136   /* XXX: assumes alignment is a power of 2 */
137   params.align = mem->requirements.alignment - 1;
138   _vk_buffer_mem_init (mem, allocator, parent, device, usage, &params,
139       mem->requirements.size, user_data, notify);
140   mem->buffer = buffer;
141 
142   if (!gst_vulkan_memory_find_memory_type_index_with_type_properties (device,
143           mem->requirements.memoryTypeBits, mem_prop_flags, &type_idx))
144     goto error;
145 
146   mem->vk_mem = (GstVulkanMemory *) gst_vulkan_memory_alloc (device, type_idx,
147       &params, mem->requirements.size, mem_prop_flags);
148   if (!mem->vk_mem)
149     goto error;
150 
151   err = vkBindBufferMemory (device->device, buffer, mem->vk_mem->mem_ptr, 0);
152   if (gst_vulkan_error_to_g_error (err, &error, "vkBindBufferMemory") < 0)
153     goto vk_error;
154 
155   if (usage & (VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT |
156           VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT |
157           VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
158           VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
159           VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
160           VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
161           VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT)) {
162     VkBufferViewCreateInfo view_info;
163 
164     _create_view_from_args (&view_info, mem->buffer, format, 0,
165         mem->requirements.size);
166     err = vkCreateBufferView (device->device, &view_info, NULL, &mem->view);
167     if (gst_vulkan_error_to_g_error (err, &error, "vkCreateBufferView") < 0)
168       goto vk_error;
169   }
170 
171   return mem;
172 
173 vk_error:
174   {
175     GST_CAT_ERROR (GST_CAT_VULKAN_BUFFER_MEMORY,
176         "Failed to allocate buffer memory %s", error->message);
177     g_clear_error (&error);
178     goto error;
179   }
180 
181 error:
182   {
183     if (mem)
184       gst_memory_unref ((GstMemory *) mem);
185     return NULL;
186   }
187 }
188 
189 static GstVulkanBufferMemory *
_vk_buffer_mem_new_wrapped(GstAllocator * allocator,GstMemory * parent,GstVulkanDevice * device,VkBuffer buffer,VkFormat format,VkBufferUsageFlags usage,gpointer user_data,GDestroyNotify notify)190 _vk_buffer_mem_new_wrapped (GstAllocator * allocator, GstMemory * parent,
191     GstVulkanDevice * device, VkBuffer buffer, VkFormat format,
192     VkBufferUsageFlags usage, gpointer user_data, GDestroyNotify notify)
193 {
194   GstVulkanBufferMemory *mem = g_new0 (GstVulkanBufferMemory, 1);
195   GstAllocationParams params = { 0, };
196   GError *error = NULL;
197   VkResult err;
198 
199   mem->buffer = buffer;
200 
201   vkGetBufferMemoryRequirements (device->device, mem->buffer,
202       &mem->requirements);
203 
204   params.align = mem->requirements.alignment - 1;
205   params.flags = GST_MEMORY_FLAG_NOT_MAPPABLE;
206   _vk_buffer_mem_init (mem, allocator, parent, device, usage, &params,
207       mem->requirements.size, user_data, notify);
208   mem->wrapped = TRUE;
209 
210   /* XXX: we don't actually if the buffer has a vkDeviceMemory bound so
211    * this may fail */
212   if (usage & (VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT |
213           VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT |
214           VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
215           VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
216           VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
217           VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
218           VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT)) {
219     VkBufferViewCreateInfo view_info;
220 
221     _create_view_from_args (&view_info, mem->buffer, format, 0,
222         mem->requirements.size);
223     err = vkCreateBufferView (device->device, &view_info, NULL, &mem->view);
224     if (gst_vulkan_error_to_g_error (err, &error, "vkCreateBufferView") < 0)
225       goto vk_error;
226   }
227 
228   return mem;
229 
230 vk_error:
231   {
232     GST_CAT_ERROR (GST_CAT_VULKAN_BUFFER_MEMORY,
233         "Failed to allocate buffer memory %s", error->message);
234     g_clear_error (&error);
235     goto error;
236   }
237 
238 error:
239   {
240     gst_memory_unref ((GstMemory *) mem);
241     return NULL;
242   }
243 }
244 
245 static gpointer
_vk_buffer_mem_map_full(GstVulkanBufferMemory * mem,GstMapInfo * info,gsize size)246 _vk_buffer_mem_map_full (GstVulkanBufferMemory * mem, GstMapInfo * info,
247     gsize size)
248 {
249   GstMapInfo *vk_map_info;
250 
251   /* FIXME: possible barrier needed */
252   g_mutex_lock (&mem->lock);
253 
254   if (!mem->vk_mem) {
255     g_mutex_unlock (&mem->lock);
256     return NULL;
257   }
258 
259   vk_map_info = g_new0 (GstMapInfo, 1);
260   info->user_data[0] = vk_map_info;
261   if (!gst_memory_map ((GstMemory *) mem->vk_mem, vk_map_info, info->flags)) {
262     g_free (vk_map_info);
263     g_mutex_unlock (&mem->lock);
264     return NULL;
265   }
266   g_mutex_unlock (&mem->lock);
267 
268   return vk_map_info->data;
269 }
270 
271 static void
_vk_buffer_mem_unmap_full(GstVulkanBufferMemory * mem,GstMapInfo * info)272 _vk_buffer_mem_unmap_full (GstVulkanBufferMemory * mem, GstMapInfo * info)
273 {
274   g_mutex_lock (&mem->lock);
275   gst_memory_unmap ((GstMemory *) mem->vk_mem, info->user_data[0]);
276   g_mutex_unlock (&mem->lock);
277 
278   g_free (info->user_data[0]);
279 }
280 
281 static GstMemory *
_vk_buffer_mem_copy(GstVulkanBufferMemory * src,gssize offset,gssize size)282 _vk_buffer_mem_copy (GstVulkanBufferMemory * src, gssize offset, gssize size)
283 {
284   return NULL;
285 }
286 
287 static GstMemory *
_vk_buffer_mem_share(GstVulkanBufferMemory * mem,gssize offset,gssize size)288 _vk_buffer_mem_share (GstVulkanBufferMemory * mem, gssize offset, gssize size)
289 {
290   return NULL;
291 }
292 
293 static gboolean
_vk_buffer_mem_is_span(GstVulkanBufferMemory * mem1,GstVulkanBufferMemory * mem2,gsize * offset)294 _vk_buffer_mem_is_span (GstVulkanBufferMemory * mem1,
295     GstVulkanBufferMemory * mem2, gsize * offset)
296 {
297   return FALSE;
298 }
299 
300 static GstMemory *
_vk_buffer_mem_alloc(GstAllocator * allocator,gsize size,GstAllocationParams * params)301 _vk_buffer_mem_alloc (GstAllocator * allocator, gsize size,
302     GstAllocationParams * params)
303 {
304   g_critical ("Subclass should override GstAllocatorClass::alloc() function");
305 
306   return NULL;
307 }
308 
309 static void
_vk_buffer_mem_free(GstAllocator * allocator,GstMemory * memory)310 _vk_buffer_mem_free (GstAllocator * allocator, GstMemory * memory)
311 {
312   GstVulkanBufferMemory *mem = (GstVulkanBufferMemory *) memory;
313 
314   GST_CAT_TRACE (GST_CAT_VULKAN_BUFFER_MEMORY, "freeing buffer memory:%p "
315       "id:%" G_GUINT64_FORMAT, mem, (guint64) mem->buffer);
316 
317   if (mem->buffer && !mem->wrapped)
318     vkDestroyBuffer (mem->device->device, mem->buffer, NULL);
319 
320   if (mem->view)
321     vkDestroyBufferView (mem->device->device, mem->view, NULL);
322 
323   if (mem->vk_mem)
324     gst_memory_unref ((GstMemory *) mem->vk_mem);
325 
326   if (mem->notify)
327     mem->notify (mem->user_data);
328 
329   gst_object_unref (mem->device);
330 }
331 
332 /**
333  * gst_vulkan_buffer_memory_alloc:
334  * @device:a #GstVulkanDevice
335  * @memory_type_index: the Vulkan memory type index
336  * @params: a #GstAllocationParams
337  * @size: the size to allocate
338  *
339  * Allocated a new #GstVulkanBufferMemory.
340  *
341  * Returns: a #GstMemory object backed by a vulkan device memory
342  */
343 GstMemory *
gst_vulkan_buffer_memory_alloc(GstVulkanDevice * device,VkFormat format,gsize size,VkBufferUsageFlags usage,VkMemoryPropertyFlags mem_prop_flags)344 gst_vulkan_buffer_memory_alloc (GstVulkanDevice * device, VkFormat format,
345     gsize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags mem_prop_flags)
346 {
347   GstVulkanBufferMemory *mem;
348 
349   mem = _vk_buffer_mem_new_alloc (_vulkan_buffer_memory_allocator, NULL, device,
350       format, size, usage, mem_prop_flags, NULL, NULL);
351 
352   return (GstMemory *) mem;
353 }
354 
355 GstMemory *
gst_vulkan_buffer_memory_wrapped(GstVulkanDevice * device,VkBuffer buffer,VkFormat format,VkBufferUsageFlags usage,gpointer user_data,GDestroyNotify notify)356 gst_vulkan_buffer_memory_wrapped (GstVulkanDevice * device, VkBuffer buffer,
357     VkFormat format, VkBufferUsageFlags usage, gpointer user_data,
358     GDestroyNotify notify)
359 {
360   GstVulkanBufferMemory *mem;
361 
362   mem =
363       _vk_buffer_mem_new_wrapped (_vulkan_buffer_memory_allocator, NULL, device,
364       buffer, format, usage, user_data, notify);
365 
366   return (GstMemory *) mem;
367 }
368 
369 G_DEFINE_TYPE (GstVulkanBufferMemoryAllocator,
370     gst_vulkan_buffer_memory_allocator, GST_TYPE_ALLOCATOR);
371 
372 static void
gst_vulkan_buffer_memory_allocator_class_init(GstVulkanBufferMemoryAllocatorClass * klass)373     gst_vulkan_buffer_memory_allocator_class_init
374     (GstVulkanBufferMemoryAllocatorClass * klass)
375 {
376   GstAllocatorClass *allocator_class = (GstAllocatorClass *) klass;
377 
378   allocator_class->alloc = _vk_buffer_mem_alloc;
379   allocator_class->free = _vk_buffer_mem_free;
380 }
381 
382 static void
gst_vulkan_buffer_memory_allocator_init(GstVulkanBufferMemoryAllocator * allocator)383 gst_vulkan_buffer_memory_allocator_init (GstVulkanBufferMemoryAllocator *
384     allocator)
385 {
386   GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
387 
388   alloc->mem_type = GST_VULKAN_BUFFER_MEMORY_ALLOCATOR_NAME;
389   alloc->mem_map_full = (GstMemoryMapFullFunction) _vk_buffer_mem_map_full;
390   alloc->mem_unmap_full =
391       (GstMemoryUnmapFullFunction) _vk_buffer_mem_unmap_full;
392   alloc->mem_copy = (GstMemoryCopyFunction) _vk_buffer_mem_copy;
393   alloc->mem_share = (GstMemoryShareFunction) _vk_buffer_mem_share;
394   alloc->mem_is_span = (GstMemoryIsSpanFunction) _vk_buffer_mem_is_span;
395 }
396 
397 /**
398  * gst_vulkan_buffer_memory_init_once:
399  *
400  * Initializes the Vulkan memory allocator. It is safe to call this function
401  * multiple times.  This must be called before any other #GstVulkanBufferMemory operation.
402  */
403 void
gst_vulkan_buffer_memory_init_once(void)404 gst_vulkan_buffer_memory_init_once (void)
405 {
406   static volatile gsize _init = 0;
407 
408   if (g_once_init_enter (&_init)) {
409     GST_DEBUG_CATEGORY_INIT (GST_CAT_VULKAN_BUFFER_MEMORY, "vulkanbuffermemory",
410         0, "Vulkan Buffer Memory");
411 
412     _vulkan_buffer_memory_allocator =
413         g_object_new (gst_vulkan_buffer_memory_allocator_get_type (), NULL);
414     gst_object_ref_sink (_vulkan_buffer_memory_allocator);
415 
416     gst_allocator_register (GST_VULKAN_BUFFER_MEMORY_ALLOCATOR_NAME,
417         gst_object_ref (_vulkan_buffer_memory_allocator));
418     g_once_init_leave (&_init, 1);
419   }
420 }
421 
422 /**
423  * gst_is_vulkan_buffer_memory:
424  * @mem:a #GstMemory
425  *
426  * Returns: whether the memory at @mem is a #GstVulkanBufferMemory
427  */
428 gboolean
gst_is_vulkan_buffer_memory(GstMemory * mem)429 gst_is_vulkan_buffer_memory (GstMemory * mem)
430 {
431   return mem != NULL && mem->allocator != NULL &&
432       g_type_is_a (G_OBJECT_TYPE (mem->allocator),
433       GST_TYPE_VULKAN_BUFFER_MEMORY_ALLOCATOR);
434 }
435