gst: don't use volatile to mean atomic
[platform/upstream/gstreamer.git] / gst-libs / gst / vulkan / gstvkmemory.c
1 /*
2  * GStreamer
3  * Copyright (C) 2015 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 <string.h>
26
27 #include "gstvkmemory.h"
28
29 #include "gstvkdebug-private.h"
30
31 /**
32  * SECTION:vkmemory
33  * @title: GstVulkanMemory
34  * @short_description: memory subclass for Vulkan device memory
35  * @see_also: #GstVulkanDevice, #GstMemory, #GstAllocator
36  *
37  * GstVulkanMemory is a #GstMemory subclass providing support for the mapping of
38  * Vulkan device memory.
39  */
40
41 /* WARNING: while suballocation is allowed, nothing prevents aliasing which
42  * requires external synchronisation */
43
44 #define GST_CAT_DEFUALT GST_CAT_VULKAN_MEMORY
45 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFUALT);
46
47 static GstAllocator *_vulkan_memory_allocator;
48
49 static void
50 _vk_mem_init (GstVulkanMemory * mem, GstAllocator * allocator,
51     GstMemory * parent, GstVulkanDevice * device, guint32 memory_type_index,
52     GstAllocationParams * params, gsize size,
53     VkMemoryPropertyFlags mem_prop_flags, gpointer user_data,
54     GDestroyNotify notify)
55 {
56   gsize align = gst_memory_alignment, offset = 0, maxsize = size;
57   GstMemoryFlags flags = 0;
58   gchar *props_str;
59
60   if (params) {
61     flags = params->flags;
62     align |= params->align;
63     offset = params->prefix;
64     maxsize += params->prefix + params->padding;
65     maxsize += align;
66   }
67
68   gst_memory_init (GST_MEMORY_CAST (mem), flags, allocator, parent, maxsize,
69       align, offset, size);
70
71   mem->device = gst_object_ref (device);
72   mem->alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
73   mem->alloc_info.pNext = NULL;
74   mem->alloc_info.allocationSize = (VkDeviceSize) mem->mem.maxsize;
75   mem->alloc_info.memoryTypeIndex = memory_type_index;
76   mem->properties = mem_prop_flags;
77   mem->notify = notify;
78   mem->user_data = user_data;
79   mem->vk_offset = 0;
80
81   g_mutex_init (&mem->lock);
82
83   props_str = gst_vulkan_memory_property_flags_to_string (mem_prop_flags);
84
85   GST_CAT_DEBUG (GST_CAT_VULKAN_MEMORY, "new Vulkan memory:%p size:%"
86       G_GSIZE_FORMAT " properties:%s", mem, maxsize, props_str);
87
88   g_free (props_str);
89 }
90
91 static GstVulkanMemory *
92 _vk_mem_new (GstAllocator * allocator, GstMemory * parent,
93     GstVulkanDevice * device, guint32 memory_type_index,
94     GstAllocationParams * params, gsize size,
95     VkMemoryPropertyFlags mem_props_flags, gpointer user_data,
96     GDestroyNotify notify)
97 {
98   GstVulkanMemory *mem = g_new0 (GstVulkanMemory, 1);
99   GError *error = NULL;
100   VkResult err;
101
102   _vk_mem_init (mem, allocator, parent, device, memory_type_index, params,
103       size, mem_props_flags, user_data, notify);
104
105   err =
106       vkAllocateMemory (device->device, &mem->alloc_info, NULL, &mem->mem_ptr);
107   if (gst_vulkan_error_to_g_error (err, &error, "vkAllocMemory") < 0) {
108     GST_CAT_ERROR (GST_CAT_VULKAN_MEMORY, "Failed to allocate device memory %s",
109         error->message);
110     gst_memory_unref ((GstMemory *) mem);
111     g_clear_error (&error);
112     return NULL;
113   }
114
115   return mem;
116 }
117
118 static gpointer
119 _vk_mem_map_full (GstVulkanMemory * mem, GstMapInfo * info, gsize size)
120 {
121   gpointer data;
122   VkResult err;
123   GError *error = NULL;
124
125   if ((mem->properties & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) {
126     GST_CAT_ERROR (GST_CAT_VULKAN_MEMORY, "Cannot map host-invisible memory");
127     return NULL;
128   }
129
130   err = vkMapMemory (mem->device->device, mem->mem_ptr, mem->vk_offset,
131       size, 0, &data);
132   if (gst_vulkan_error_to_g_error (err, &error, "vkMapMemory") < 0) {
133     GST_CAT_ERROR (GST_CAT_VULKAN_MEMORY, "Failed to map device memory %s",
134         error->message);
135     g_clear_error (&error);
136     return NULL;
137   }
138
139   return data;
140 }
141
142 static void
143 _vk_mem_unmap_full (GstVulkanMemory * mem, GstMapInfo * info)
144 {
145   vkUnmapMemory (mem->device->device, mem->mem_ptr);
146 }
147
148 static GstMemory *
149 _vk_mem_copy (GstVulkanMemory * src, gssize offset, gssize size)
150 {
151   return NULL;
152 }
153
154 static GstMemory *
155 _vk_mem_share (GstVulkanMemory * mem, gssize offset, gsize size)
156 {
157   GstVulkanMemory *shared = g_new0 (GstVulkanMemory, 1);
158   GstVulkanMemory *parent = mem;
159   GstAllocationParams params = { 0, };
160
161   if (size == -1)
162     size = mem->mem.size - offset;
163
164   g_return_val_if_fail (size > 0, NULL);
165
166   while ((parent = (GstVulkanMemory *) (GST_MEMORY_CAST (parent)->parent)));
167
168   params.flags = GST_MEMORY_FLAGS (mem);
169   params.align = GST_MEMORY_CAST (parent)->align;
170
171   _vk_mem_init (shared, _vulkan_memory_allocator, GST_MEMORY_CAST (mem),
172       parent->device, parent->alloc_info.memoryTypeIndex, &params, size,
173       parent->properties, NULL, NULL);
174   shared->mem_ptr = parent->mem_ptr;
175   shared->wrapped = TRUE;
176   shared->vk_offset = offset + mem->vk_offset;
177
178   return GST_MEMORY_CAST (shared);
179 }
180
181 static gboolean
182 _vk_mem_is_span (GstVulkanMemory * mem1, GstVulkanMemory * mem2, gsize * offset)
183 {
184   return FALSE;
185 }
186
187 static GstMemory *
188 _vk_mem_alloc (GstAllocator * allocator, gsize size,
189     GstAllocationParams * params)
190 {
191   g_critical ("Subclass should override GstAllocatorClass::alloc() function");
192
193   return NULL;
194 }
195
196 static void
197 _vk_mem_free (GstAllocator * allocator, GstMemory * memory)
198 {
199   GstVulkanMemory *mem = (GstVulkanMemory *) memory;
200
201   GST_CAT_TRACE (GST_CAT_VULKAN_MEMORY, "freeing buffer memory:%p "
202       "id:%" G_GUINT64_FORMAT, mem, (guint64) mem->mem_ptr);
203
204   g_mutex_clear (&mem->lock);
205
206   if (mem->notify)
207     mem->notify (mem->user_data);
208
209   if (mem->mem_ptr && !mem->wrapped)
210     vkFreeMemory (mem->device->device, mem->mem_ptr, NULL);
211
212   gst_object_unref (mem->device);
213
214   g_free (mem);
215 }
216
217 /**
218  * gst_vulkan_memory_find_memory_type_index_with_type_properties:
219  * @device: a #GstVulkanDevice
220  * @type_bits: memory type bits to search for
221  * @properties: memory properties to search for
222  * @type_index: resulting index of the memory type
223  *
224  * Returns: whether a valid memory type could be found
225  *
226  * Since: 1.18
227  */
228 gboolean
229 gst_vulkan_memory_find_memory_type_index_with_type_properties (GstVulkanDevice *
230     device, guint32 type_bits, VkMemoryPropertyFlags properties,
231     guint32 * type_index)
232 {
233   guint32 i;
234
235   /* Search memtypes to find first index with those properties */
236   for (i = 0; i < 32; i++) {
237     if ((type_bits & 1) == 1) {
238       /* Type is available, does it match user properties? */
239       if ((device->physical_device->memory_properties.memoryTypes[i].
240               propertyFlags & properties) == properties) {
241         *type_index = i;
242         return TRUE;
243       }
244     }
245     type_bits >>= 1;
246   }
247
248   return FALSE;
249 }
250
251 /**
252  * gst_vulkan_memory_alloc:
253  * @device:a #GstVulkanDevice
254  * @memory_type_index: the Vulkan memory type index
255  * @params: a #GstAllocationParams
256  * @size: the size to allocate
257  *
258  * Allocated a new #GstVulkanMemory.
259  *
260  * Returns: a #GstMemory object backed by a vulkan device memory
261  *
262  * Since: 1.18
263  */
264 GstMemory *
265 gst_vulkan_memory_alloc (GstVulkanDevice * device, guint32 memory_type_index,
266     GstAllocationParams * params, gsize size, VkMemoryPropertyFlags mem_flags)
267 {
268   GstVulkanMemory *mem;
269
270   mem = _vk_mem_new (_vulkan_memory_allocator, NULL, device, memory_type_index,
271       params, size, mem_flags, NULL, NULL);
272
273   return (GstMemory *) mem;
274 }
275
276 G_DEFINE_TYPE (GstVulkanMemoryAllocator, gst_vulkan_memory_allocator,
277     GST_TYPE_ALLOCATOR);
278
279 static void
280 gst_vulkan_memory_allocator_class_init (GstVulkanMemoryAllocatorClass * klass)
281 {
282   GstAllocatorClass *allocator_class = (GstAllocatorClass *) klass;
283
284   allocator_class->alloc = _vk_mem_alloc;
285   allocator_class->free = _vk_mem_free;
286 }
287
288 static void
289 gst_vulkan_memory_allocator_init (GstVulkanMemoryAllocator * allocator)
290 {
291   GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
292
293   alloc->mem_type = GST_VULKAN_MEMORY_ALLOCATOR_NAME;
294   alloc->mem_map_full = (GstMemoryMapFullFunction) _vk_mem_map_full;
295   alloc->mem_unmap_full = (GstMemoryUnmapFullFunction) _vk_mem_unmap_full;
296   alloc->mem_copy = (GstMemoryCopyFunction) _vk_mem_copy;
297   alloc->mem_share = (GstMemoryShareFunction) _vk_mem_share;
298   alloc->mem_is_span = (GstMemoryIsSpanFunction) _vk_mem_is_span;
299 }
300
301 /**
302  * gst_vulkan_memory_init_once:
303  *
304  * Initializes the Vulkan memory allocator. It is safe to call this function
305  * multiple times.  This must be called before any other #GstVulkanMemory operation.
306  *
307  * Since: 1.18
308  */
309 void
310 gst_vulkan_memory_init_once (void)
311 {
312   static gsize _init = 0;
313
314   if (g_once_init_enter (&_init)) {
315     GST_DEBUG_CATEGORY_INIT (GST_CAT_VULKAN_MEMORY, "vulkanmemory", 0,
316         "Vulkan Memory");
317
318     _vulkan_memory_allocator =
319         g_object_new (gst_vulkan_memory_allocator_get_type (), NULL);
320     gst_object_ref_sink (_vulkan_memory_allocator);
321
322     gst_allocator_register (GST_VULKAN_MEMORY_ALLOCATOR_NAME,
323         gst_object_ref (_vulkan_memory_allocator));
324     g_once_init_leave (&_init, 1);
325   }
326 }
327
328 /**
329  * gst_is_vulkan_memory:
330  * @mem:a #GstMemory
331  *
332  * Returns: whether the memory at @mem is a #GstVulkanMemory
333  *
334  * Since: 1.18
335  */
336 gboolean
337 gst_is_vulkan_memory (GstMemory * mem)
338 {
339   return mem != NULL && mem->allocator != NULL &&
340       g_type_is_a (G_OBJECT_TYPE (mem->allocator),
341       GST_TYPE_VULKAN_MEMORY_ALLOCATOR);
342 }