diff --git a/ext/vulkan/Makefile.am b/ext/vulkan/Makefile.am index 57b1af0081..2b28b3db03 100644 --- a/ext/vulkan/Makefile.am +++ b/ext/vulkan/Makefile.am @@ -9,6 +9,7 @@ libgstvulkan_la_SOURCES = \ vkdevice.c \ vkdisplay.c \ vkerror.c \ + vkbuffermemory.c \ vkimagememory.c \ vkinstance.c \ vkmemory.c \ @@ -24,6 +25,7 @@ noinst_HEADERS = \ vkdevice.h \ vkdisplay.h \ vkerror.h \ + vkbuffermemory.h \ vkimagememory.h \ vkinstance.h \ vkmemory.h \ diff --git a/ext/vulkan/vk.h b/ext/vulkan/vk.h index a31121502f..b7f22aed81 100644 --- a/ext/vulkan/vk.h +++ b/ext/vulkan/vk.h @@ -33,6 +33,7 @@ #include "vkwindow.h" #include "vkswapper.h" #include "vkmemory.h" +#include "vkbuffermemory.h" #include "vkimagememory.h" #include "vkutils.h" diff --git a/ext/vulkan/vk_fwd.h b/ext/vulkan/vk_fwd.h index 547e5a2cbd..470f0dd0f8 100644 --- a/ext/vulkan/vk_fwd.h +++ b/ext/vulkan/vk_fwd.h @@ -54,6 +54,10 @@ typedef struct _GstVulkanMemory GstVulkanMemory; typedef struct _GstVulkanMemoryAllocator GstVulkanMemoryAllocator; typedef struct _GstVulkanMemoryAllocatorClass GstVulkanMemoryAllocatorClass; +typedef struct _GstVulkanBufferMemory GstVulkanBufferMemory; +typedef struct _GstVulkanBufferMemoryAllocator GstVulkanBufferMemoryAllocator; +typedef struct _GstVulkanBufferMemoryAllocatorClass GstVulkanBufferMemoryAllocatorClass; + typedef struct _GstVulkanImageMemory GstVulkanImageMemory; typedef struct _GstVulkanImageMemoryAllocator GstVulkanImageMemoryAllocator; typedef struct _GstVulkanImageMemoryAllocatorClass GstVulkanImageMemoryAllocatorClass; diff --git a/ext/vulkan/vkbuffermemory.c b/ext/vulkan/vkbuffermemory.c new file mode 100644 index 0000000000..8229b49b03 --- /dev/null +++ b/ext/vulkan/vkbuffermemory.c @@ -0,0 +1,461 @@ +/* + * GStreamer + * Copyright (C) 2016 Matthew Waters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "vkbuffermemory.h" + +/** + * SECTION:vkbuffermemory + * @short_description: memory subclass for Vulkan buffer memory + * @see_also: #GstMemory, #GstAllocator + * + * GstVulkanBufferMemory is a #GstMemory subclass providing support for the + * mapping of Vulkan device memory. + */ + +#define GST_CAT_DEFUALT GST_CAT_VULKAN_BUFFER_MEMORY +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFUALT); + +static GstAllocator *_vulkan_buffer_memory_allocator; + +static gboolean +_find_memory_type_index_with_type_properties (GstVulkanDevice * device, + guint32 typeBits, VkFlags properties, guint32 * typeIndex) +{ + guint32 i; + + /* Search memtypes to find first index with those properties */ + for (i = 0; i < 32; i++) { + if ((typeBits & 1) == 1) { + /* Type is available, does it match user properties? */ + if ((device->memory_properties.memoryTypes[i]. + propertyFlags & properties) == properties) { + *typeIndex = i; + return TRUE; + } + } + typeBits >>= 1; + } + + return FALSE; +} + +#define GST_VK_CREATE_BUFFER_INFO(info,pNext_,flags_,size_,usage_,sharingMode_,queueFamilyIndexCount_,pQueueFamilyIndices_) \ + G_STMT_START { \ + (info).sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; \ + (info).pNext = pNext_; \ + (info).flags = flags_; \ + (info).size = size_; \ + (info).usage = usage_; \ + (info).sharingMode = sharingMode_; \ + (info).queueFamilyIndexCount = queueFamilyIndexCount_; \ + (info).pQueueFamilyIndices = pQueueFamilyIndices_; \ + } G_STMT_END + +static gboolean +_create_info_from_args (VkBufferCreateInfo * info, gsize size, + VkBufferUsageFlags usage) +{ + /* FIXME: validate these */ + GST_VK_CREATE_BUFFER_INFO (*info, NULL, 0, size, usage, + VK_SHARING_MODE_EXCLUSIVE, 0, NULL); + + return TRUE; +} + +#define GST_VK_CREATE_BUFFER_VIEW_INFO(info,pNext_,flags_,buffer_,format_,offset_,range_) \ + G_STMT_START { \ + (info).sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO; \ + (info).pNext = pNext_; \ + (info).buffer = buffer_; \ + (info).format = format_; \ + (info).offset = offset_; \ + (info).range = range_; \ + } G_STMT_END + +static gboolean +_create_view_from_args (VkBufferViewCreateInfo * info, VkBuffer buffer, + VkFormat format, gsize offset, gsize range) +{ + /* FIXME: validate these */ + g_assert (format != VK_FORMAT_UNDEFINED); + + GST_VK_CREATE_BUFFER_VIEW_INFO (*info, NULL, 0, buffer, format, offset, + range); + + return TRUE; +} + +static void +_vk_buffer_mem_init (GstVulkanBufferMemory * mem, GstAllocator * allocator, + GstMemory * parent, GstVulkanDevice * device, GstAllocationParams * params, + gsize size, gpointer user_data, GDestroyNotify notify) +{ + gsize align = gst_memory_alignment, offset = 0, maxsize = size; + GstMemoryFlags flags = 0; + + if (params) { + flags = params->flags; + align |= params->align; + offset = params->prefix; + maxsize += params->prefix + params->padding + align; + } + + gst_memory_init (GST_MEMORY_CAST (mem), flags, allocator, parent, maxsize, + align, offset, size); + + mem->device = gst_object_ref (device); + mem->wrapped = FALSE; + mem->notify = notify; + mem->user_data = user_data; + + g_mutex_init (&mem->lock); + + GST_CAT_DEBUG (GST_CAT_VULKAN_BUFFER_MEMORY, + "new Vulkan Buffer memory:%p size:%" G_GSIZE_FORMAT, mem, maxsize); +} + +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) +{ + GstVulkanBufferMemory *mem = NULL; + GstAllocationParams params = { 0, }; + VkBufferCreateInfo buffer_info; + guint32 memory_type_index; + GError *error = NULL; + VkBuffer buffer; + VkResult err; + + if (!_create_info_from_args (&buffer_info, size, usage)) { + GST_CAT_ERROR (GST_CAT_VULKAN_BUFFER_MEMORY, "Incorrect buffer parameters"); + goto error; + } + + err = vkCreateBuffer (device->device, &buffer_info, NULL, &buffer); + if (gst_vulkan_error_to_g_error (err, &error, "vkCreateBuffer") < 0) + goto vk_error; + + mem = g_new0 (GstVulkanBufferMemory, 1); + vkGetBufferMemoryRequirements (device->device, buffer, &mem->requirements); + + params.align = mem->requirements.alignment; + _vk_buffer_mem_init (mem, allocator, parent, device, ¶ms, + mem->requirements.size, user_data, notify); + mem->buffer = buffer; + + if (!_find_memory_type_index_with_type_properties (device, + mem->requirements.memoryTypeBits, mem_prop_flags, + &memory_type_index)) { + GST_CAT_ERROR (GST_CAT_VULKAN_BUFFER_MEMORY, + "Could not find suitable memory type"); + goto error; + } + + mem->vk_mem = (GstVulkanMemory *) + gst_vulkan_memory_alloc (device, memory_type_index, ¶ms, + mem->requirements.size, mem_prop_flags); + if (!mem->vk_mem) { + GST_CAT_ERROR (GST_CAT_VULKAN_BUFFER_MEMORY, + "Failed to allocate device memory"); + goto error; + } + + err = + vkBindBufferMemory (device->device, mem->buffer, mem->vk_mem->mem_ptr, + 0 /* offset */ ); + if (gst_vulkan_error_to_g_error (err, &error, "vkBindBufferMemory") < 0) + goto vk_error; + + if (usage & (VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | + VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT | + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | + VK_BUFFER_USAGE_INDEX_BUFFER_BIT | + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | + VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT)) { + VkBufferViewCreateInfo view_info; + + _create_view_from_args (&view_info, mem->buffer, format, 0, + mem->requirements.size); + err = vkCreateBufferView (device->device, &view_info, NULL, &mem->view); + if (gst_vulkan_error_to_g_error (err, &error, "vkCreateBufferView") < 0) + goto vk_error; + } + + return mem; + +vk_error: + { + GST_CAT_ERROR (GST_CAT_VULKAN_BUFFER_MEMORY, + "Failed to allocate buffer memory %s", error->message); + g_clear_error (&error); + goto error; + } + +error: + { + if (mem) + gst_memory_unref ((GstMemory *) mem); + return NULL; + } +} + +static GstVulkanBufferMemory * +_vk_buffer_mem_new_wrapped (GstAllocator * allocator, GstMemory * parent, + GstVulkanDevice * device, VkBuffer buffer, VkFormat format, + VkBufferUsageFlags usage, gpointer user_data, GDestroyNotify notify) +{ + GstVulkanBufferMemory *mem = g_new0 (GstVulkanBufferMemory, 1); + GstAllocationParams params = { 0, }; + GError *error = NULL; + VkResult err; + + mem->buffer = buffer; + + vkGetBufferMemoryRequirements (device->device, mem->buffer, + &mem->requirements); + + /* no device memory so no mapping */ + params.flags = GST_MEMORY_FLAG_NOT_MAPPABLE | GST_MEMORY_FLAG_READONLY; + _vk_buffer_mem_init (mem, allocator, parent, device, ¶ms, + mem->requirements.size, user_data, notify); + mem->wrapped = TRUE; + + if (usage & (VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | + VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT | + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | + VK_BUFFER_USAGE_INDEX_BUFFER_BIT | + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | + VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT)) { + VkBufferViewCreateInfo view_info; + + _create_view_from_args (&view_info, mem->buffer, format, 0, + mem->requirements.size); + err = vkCreateBufferView (device->device, &view_info, NULL, &mem->view); + if (gst_vulkan_error_to_g_error (err, &error, "vkCreateBufferView") < 0) + goto vk_error; + } + + return mem; + +vk_error: + { + GST_CAT_ERROR (GST_CAT_VULKAN_BUFFER_MEMORY, + "Failed to allocate buffer memory %s", error->message); + g_clear_error (&error); + goto error; + } + +error: + { + gst_memory_unref ((GstMemory *) mem); + return NULL; + } +} + +static gpointer +_vk_buffer_mem_map_full (GstVulkanBufferMemory * mem, GstMapInfo * info, + gsize size) +{ + GstMapInfo *vk_map_info; + + /* FIXME: possible barrier needed */ + + if (!mem->vk_mem) + return NULL; + + vk_map_info = g_new0 (GstMapInfo, 1); + info->user_data[0] = vk_map_info; + if (!gst_memory_map ((GstMemory *) mem->vk_mem, vk_map_info, info->flags)) { + g_free (vk_map_info); + return NULL; + } + + return vk_map_info->data; +} + +static void +_vk_buffer_mem_unmap_full (GstVulkanBufferMemory * mem, GstMapInfo * info) +{ + gst_memory_unmap ((GstMemory *) mem->vk_mem, info->user_data[0]); + + g_free (info->user_data[0]); +} + +static GstMemory * +_vk_buffer_mem_copy (GstVulkanBufferMemory * src, gssize offset, gssize size) +{ + return NULL; +} + +static GstMemory * +_vk_buffer_mem_share (GstVulkanBufferMemory * mem, gssize offset, gssize size) +{ + return NULL; +} + +static gboolean +_vk_buffer_mem_is_span (GstVulkanBufferMemory * mem1, + GstVulkanBufferMemory * mem2, gsize * offset) +{ + return FALSE; +} + +static GstMemory * +_vk_buffer_mem_alloc (GstAllocator * allocator, gsize size, + GstAllocationParams * params) +{ + g_critical ("Subclass should override GstAllocatorClass::alloc() function"); + + return NULL; +} + +static void +_vk_buffer_mem_free (GstAllocator * allocator, GstMemory * memory) +{ + GstVulkanBufferMemory *mem = (GstVulkanBufferMemory *) memory; + + GST_CAT_TRACE (GST_CAT_VULKAN_BUFFER_MEMORY, "freeing buffer memory:%p " + "id:%" G_GUINT64_FORMAT, mem, (guint64) mem->buffer); + + if (mem->buffer && !mem->wrapped) + vkDestroyBuffer (mem->device->device, mem->buffer, NULL); + + if (mem->view) + vkDestroyBufferView (mem->device->device, mem->view, NULL); + + if (mem->vk_mem) + gst_memory_unref ((GstMemory *) mem->vk_mem); + + if (mem->notify) + mem->notify (mem->user_data); + + gst_object_unref (mem->device); +} + +/** + * gst_vulkan_buffer_memory_alloc: + * @device:a #GstVulkanDevice + * @memory_type_index: the Vulkan memory type index + * @params: a #GstAllocationParams + * @size: the size to allocate + * + * Allocated a new #GstVulkanBufferMemory. + * + * Returns: a #GstMemory object backed by a vulkan device memory + */ +GstMemory * +gst_vulkan_buffer_memory_alloc (GstVulkanDevice * device, VkFormat format, + gsize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags mem_prop_flags) +{ + GstVulkanBufferMemory *mem; + + mem = _vk_buffer_mem_new_alloc (_vulkan_buffer_memory_allocator, NULL, device, + format, size, usage, mem_prop_flags, NULL, NULL); + + return (GstMemory *) mem; +} + +GstMemory * +gst_vulkan_buffer_memory_wrapped (GstVulkanDevice * device, VkBuffer buffer, + VkFormat format, VkBufferUsageFlags usage, gpointer user_data, + GDestroyNotify notify) +{ + GstVulkanBufferMemory *mem; + + mem = + _vk_buffer_mem_new_wrapped (_vulkan_buffer_memory_allocator, NULL, device, + buffer, format, usage, user_data, notify); + + return (GstMemory *) mem; +} + +G_DEFINE_TYPE (GstVulkanBufferMemoryAllocator, + gst_vulkan_buffer_memory_allocator, GST_TYPE_ALLOCATOR); + +static void + gst_vulkan_buffer_memory_allocator_class_init + (GstVulkanBufferMemoryAllocatorClass * klass) +{ + GstAllocatorClass *allocator_class = (GstAllocatorClass *) klass; + + allocator_class->alloc = _vk_buffer_mem_alloc; + allocator_class->free = _vk_buffer_mem_free; +} + +static void +gst_vulkan_buffer_memory_allocator_init (GstVulkanBufferMemoryAllocator * + allocator) +{ + GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator); + + alloc->mem_type = GST_VULKAN_BUFFER_MEMORY_ALLOCATOR_NAME; + alloc->mem_map_full = (GstMemoryMapFullFunction) _vk_buffer_mem_map_full; + alloc->mem_unmap_full = + (GstMemoryUnmapFullFunction) _vk_buffer_mem_unmap_full; + alloc->mem_copy = (GstMemoryCopyFunction) _vk_buffer_mem_copy; + alloc->mem_share = (GstMemoryShareFunction) _vk_buffer_mem_share; + alloc->mem_is_span = (GstMemoryIsSpanFunction) _vk_buffer_mem_is_span; +} + +/** + * gst_vulkan_buffer_memory_init_once: + * + * Initializes the Vulkan memory allocator. It is safe to call this function + * multiple times. This must be called before any other #GstVulkanBufferMemory operation. + */ +void +gst_vulkan_buffer_memory_init_once (void) +{ + static volatile gsize _init = 0; + + if (g_once_init_enter (&_init)) { + GST_DEBUG_CATEGORY_INIT (GST_CAT_VULKAN_BUFFER_MEMORY, "vulkanbuffermemory", + 0, "Vulkan Buffer Memory"); + + _vulkan_buffer_memory_allocator = + g_object_new (gst_vulkan_buffer_memory_allocator_get_type (), NULL); + + gst_allocator_register (GST_VULKAN_BUFFER_MEMORY_ALLOCATOR_NAME, + gst_object_ref (_vulkan_buffer_memory_allocator)); + g_once_init_leave (&_init, 1); + } +} + +/** + * gst_is_vulkan_buffer_memory: + * @mem:a #GstMemory + * + * Returns: whether the memory at @mem is a #GstVulkanBufferMemory + */ +gboolean +gst_is_vulkan_buffer_memory (GstMemory * mem) +{ + return mem != NULL && mem->allocator != NULL && + g_type_is_a (G_OBJECT_TYPE (mem->allocator), + GST_TYPE_VULKAN_BUFFER_MEMORY_ALLOCATOR); +} diff --git a/ext/vulkan/vkbuffermemory.h b/ext/vulkan/vkbuffermemory.h new file mode 100644 index 0000000000..7992630787 --- /dev/null +++ b/ext/vulkan/vkbuffermemory.h @@ -0,0 +1,100 @@ +/* + * GStreamer + * Copyright (C) 2015 Matthew Waters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _VK_BUFFER_MEMORY_H_ +#define _VK_BUFFER_MEMORY_H_ + +#include +#include +#include + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_VULKAN_BUFFER_MEMORY_ALLOCATOR (gst_vulkan_buffer_memory_allocator_get_type()) +GType gst_vulkan_buffer_memory_allocator_get_type(void); + +#define GST_IS_VULKAN_BUFFER_MEMORY_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_VULKAN_BUFFER_MEMORY_ALLOCATOR)) +#define GST_IS_VULKAN_BUFFER_MEMORY_ALLOCATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_VULKAN_BUFFER_MEMORY_ALLOCATOR)) +#define GST_VULKAN_BUFFER_MEMORY_ALLOCATOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_VULKAN_MEMORY_ALLOCATOR, GstVulkanBufferMemoryAllocatorClass)) +#define GST_VULKAN_BUFFER_MEMORY_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_VULKAN_MEMORY_ALLOCATOR, GstVulkanBufferMemoryAllocator)) +#define GST_VULKAN_BUFFER_MEMORY_ALLOCATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_VULKAN_MEMORY_ALLOCATOR, GstVulkanBufferMemoryAllocatorClass)) +#define GST_VULKAN_BUFFER_MEMORY_ALLOCATOR_CAST(obj) ((GstVulkanBufferMemoryAllocator *)(obj)) + +#define GST_VULKAN_BUFFER_MEMORY_ALLOCATOR_NAME "VulkanBuffer" + +struct _GstVulkanBufferMemory +{ + GstMemory parent; + + GstVulkanDevice * device; + + VkBuffer buffer; + VkBufferView view; + GstVulkanMemory *vk_mem; + + VkMemoryRequirements requirements; + + GMutex lock; + gboolean wrapped; + GDestroyNotify notify; + gpointer user_data; +}; + +/** + * GstVulkanBufferMemoryAllocator + * + * Opaque #GstVulkanBufferMemoryAllocator struct + */ +struct _GstVulkanBufferMemoryAllocator +{ + GstAllocator parent; +}; + +/** + * GstVulkanBufferMemoryAllocatorClass: + * + * The #GstVulkanBufferMemoryAllocatorClass only contains private data + */ +struct _GstVulkanBufferMemoryAllocatorClass +{ + GstAllocatorClass parent_class; +}; + +void gst_vulkan_buffer_memory_init_once (void); +gboolean gst_is_vulkan_buffer_memory (GstMemory * mem); + +GstMemory * gst_vulkan_buffer_memory_alloc (GstVulkanDevice * device, + VkFormat format, + gsize size, + VkBufferUsageFlags usage, + VkMemoryPropertyFlags mem_prop_flags); + +GstMemory * gst_vulkan_buffer_memory_wrapped (GstVulkanDevice * device, + VkBuffer buffer, + VkFormat format, + VkBufferUsageFlags usage, + gpointer user_data, + GDestroyNotify notify); + +G_END_DECLS + +#endif /* _VK_BUFFER_MEMORY_H_ */ diff --git a/ext/vulkan/vkinstance.c b/ext/vulkan/vkinstance.c index 09c3b99d53..61e71bec7b 100644 --- a/ext/vulkan/vkinstance.c +++ b/ext/vulkan/vkinstance.c @@ -78,6 +78,7 @@ gst_vulkan_instance_class_init (GstVulkanInstanceClass * instance_class) { gst_vulkan_memory_init_once (); gst_vulkan_image_memory_init_once (); + gst_vulkan_buffer_memory_init_once (); g_type_class_add_private (instance_class, sizeof (GstVulkanInstancePrivate));