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