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
_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)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 *
_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)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
_vk_mem_map_full(GstVulkanMemory * mem,GstMapInfo * info,gsize size)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
_vk_mem_unmap_full(GstVulkanMemory * mem,GstMapInfo * info)143 _vk_mem_unmap_full (GstVulkanMemory * mem, GstMapInfo * info)
144 {
145 vkUnmapMemory (mem->device->device, mem->mem_ptr);
146 }
147
148 static GstMemory *
_vk_mem_copy(GstVulkanMemory * src,gssize offset,gssize size)149 _vk_mem_copy (GstVulkanMemory * src, gssize offset, gssize size)
150 {
151 return NULL;
152 }
153
154 static GstMemory *
_vk_mem_share(GstVulkanMemory * mem,gssize offset,gsize size)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, ¶ms, 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
_vk_mem_is_span(GstVulkanMemory * mem1,GstVulkanMemory * mem2,gsize * offset)182 _vk_mem_is_span (GstVulkanMemory * mem1, GstVulkanMemory * mem2, gsize * offset)
183 {
184 return FALSE;
185 }
186
187 static GstMemory *
_vk_mem_alloc(GstAllocator * allocator,gsize size,GstAllocationParams * params)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
_vk_mem_free(GstAllocator * allocator,GstMemory * memory)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
gst_vulkan_memory_find_memory_type_index_with_type_properties(GstVulkanDevice * device,guint32 type_bits,VkMemoryPropertyFlags properties,guint32 * type_index)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 *
gst_vulkan_memory_alloc(GstVulkanDevice * device,guint32 memory_type_index,GstAllocationParams * params,gsize size,VkMemoryPropertyFlags mem_flags)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
gst_vulkan_memory_allocator_class_init(GstVulkanMemoryAllocatorClass * klass)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
gst_vulkan_memory_allocator_init(GstVulkanMemoryAllocator * allocator)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
gst_vulkan_memory_init_once(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
gst_is_vulkan_memory(GstMemory * mem)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 }
343