mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-13 20:06:35 +00:00
2b1dbefc42
Running element_vkcolorconver test with Vulkan validation layer this error is raised: Code 0 : Validation Error: [ VUID-VkMappedMemoryRange-size-01390 ] Object 0: handle = 0x100000000010, type = VK_OBJECT_TYPE_DEVICE_MEMORY; | MessageID = 0xdd4e6d8b | vkFlushMappedMemoryRanges: Size in pMemRanges[0] is 0x4, which is not a multiple of VkPhysicalDeviceLimits::nonCoherentAtomSize (0x40) and offset + size (0x0 + 0x4 = 0x4) not equal to the memory size (0xb). The Vulkan spec states: If size is not equal to VK_WHOLE_SIZE, size must either be a multiple of VkPhysicalDeviceLimits::nonCoherentAtomSize, or offset plus size must equal the size of memory The reason of is that the image size used in the test doesn't comply hardware restrictions. In order to avoid juggling with image size and hardware restrictions, this patch proposes to use VK_WHOLE_SIZE macro. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4311>
363 lines
10 KiB
C
363 lines
10 KiB
C
/*
|
|
* GStreamer
|
|
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
|
*
|
|
* 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 <string.h>
|
|
|
|
#include "gstvkmemory.h"
|
|
|
|
#include "gstvkdebug-private.h"
|
|
|
|
/**
|
|
* SECTION:vkmemory
|
|
* @title: GstVulkanMemory
|
|
* @short_description: memory subclass for Vulkan device memory
|
|
* @see_also: #GstVulkanDevice, #GstMemory, #GstAllocator
|
|
*
|
|
* GstVulkanMemory is a #GstMemory subclass providing support for the mapping of
|
|
* Vulkan device memory.
|
|
*/
|
|
|
|
/* WARNING: while suballocation is allowed, nothing prevents aliasing which
|
|
* requires external synchronisation */
|
|
|
|
#define GST_CAT_DEFUALT GST_CAT_VULKAN_MEMORY
|
|
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFUALT);
|
|
|
|
static GstAllocator *_vulkan_memory_allocator;
|
|
|
|
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)
|
|
{
|
|
gsize align = gst_memory_alignment, offset = 0, maxsize = size;
|
|
GstMemoryFlags flags = 0;
|
|
gchar *props_str;
|
|
|
|
if (params) {
|
|
flags = params->flags;
|
|
align |= params->align;
|
|
offset = params->prefix;
|
|
maxsize += params->prefix + params->padding;
|
|
maxsize += align;
|
|
}
|
|
|
|
gst_memory_init (GST_MEMORY_CAST (mem), flags, allocator, parent, maxsize,
|
|
align, offset, size);
|
|
|
|
mem->device = gst_object_ref (device);
|
|
mem->alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
|
mem->alloc_info.pNext = NULL;
|
|
mem->alloc_info.allocationSize = (VkDeviceSize) mem->mem.maxsize;
|
|
mem->alloc_info.memoryTypeIndex = memory_type_index;
|
|
mem->properties = mem_prop_flags;
|
|
mem->notify = notify;
|
|
mem->user_data = user_data;
|
|
mem->vk_offset = 0;
|
|
|
|
g_mutex_init (&mem->lock);
|
|
|
|
props_str = gst_vulkan_memory_property_flags_to_string (mem_prop_flags);
|
|
|
|
GST_CAT_DEBUG (GST_CAT_VULKAN_MEMORY, "new Vulkan memory:%p size:%"
|
|
G_GSIZE_FORMAT " properties:%s", mem, maxsize, props_str);
|
|
|
|
g_free (props_str);
|
|
}
|
|
|
|
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)
|
|
{
|
|
GstVulkanMemory *mem = g_new0 (GstVulkanMemory, 1);
|
|
GError *error = NULL;
|
|
VkResult err;
|
|
|
|
_vk_mem_init (mem, allocator, parent, device, memory_type_index, params,
|
|
size, mem_props_flags, user_data, notify);
|
|
|
|
err =
|
|
vkAllocateMemory (device->device, &mem->alloc_info, NULL, &mem->mem_ptr);
|
|
if (gst_vulkan_error_to_g_error (err, &error, "vkAllocMemory") < 0) {
|
|
GST_CAT_ERROR (GST_CAT_VULKAN_MEMORY, "Failed to allocate device memory %s",
|
|
error->message);
|
|
gst_memory_unref ((GstMemory *) mem);
|
|
g_clear_error (&error);
|
|
return NULL;
|
|
}
|
|
|
|
return mem;
|
|
}
|
|
|
|
static gpointer
|
|
_vk_mem_map_full (GstVulkanMemory * mem, GstMapInfo * info, gsize size)
|
|
{
|
|
gpointer data;
|
|
VkResult err;
|
|
GError *error = NULL;
|
|
|
|
if ((mem->properties & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) {
|
|
GST_CAT_ERROR (GST_CAT_VULKAN_MEMORY, "Cannot map host-invisible memory");
|
|
return NULL;
|
|
}
|
|
|
|
err = vkMapMemory (mem->device->device, mem->mem_ptr, mem->vk_offset,
|
|
size, 0, &data);
|
|
if (gst_vulkan_error_to_g_error (err, &error, "vkMapMemory") < 0) {
|
|
GST_CAT_ERROR (GST_CAT_VULKAN_MEMORY, "Failed to map device memory %s",
|
|
error->message);
|
|
g_clear_error (&error);
|
|
return NULL;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
static void
|
|
_vk_mem_unmap_full (GstVulkanMemory * mem, GstMapInfo * info)
|
|
{
|
|
if ((info->flags & GST_MAP_WRITE)
|
|
&& !(mem->properties & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) {
|
|
GError *error = NULL;
|
|
VkResult err;
|
|
VkMappedMemoryRange range = {
|
|
.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
|
|
/* .pNext = */
|
|
.memory = mem->mem_ptr,
|
|
.offset = mem->vk_offset,
|
|
.size = VK_WHOLE_SIZE,
|
|
};
|
|
|
|
err = vkFlushMappedMemoryRanges (mem->device->device, 1u, &range);
|
|
if (gst_vulkan_error_to_g_error (err, &error,
|
|
"vkFlushMappedMemoryRanges") < 0) {
|
|
GST_CAT_WARNING (GST_CAT_VULKAN_MEMORY, "Failed to flush memory: %s",
|
|
error->message);
|
|
g_clear_error (&error);
|
|
}
|
|
}
|
|
|
|
vkUnmapMemory (mem->device->device, mem->mem_ptr);
|
|
}
|
|
|
|
static GstMemory *
|
|
_vk_mem_copy (GstVulkanMemory * src, gssize offset, gssize size)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static GstMemory *
|
|
_vk_mem_share (GstVulkanMemory * mem, gssize offset, gsize size)
|
|
{
|
|
GstVulkanMemory *shared = g_new0 (GstVulkanMemory, 1);
|
|
GstVulkanMemory *parent = mem;
|
|
GstAllocationParams params = { 0, };
|
|
|
|
if (size == -1)
|
|
size = mem->mem.size - offset;
|
|
|
|
g_return_val_if_fail (size > 0, NULL);
|
|
|
|
while ((parent = (GstVulkanMemory *) (GST_MEMORY_CAST (parent)->parent)));
|
|
|
|
params.flags = GST_MEMORY_FLAGS (mem);
|
|
params.align = GST_MEMORY_CAST (parent)->align;
|
|
|
|
_vk_mem_init (shared, _vulkan_memory_allocator, GST_MEMORY_CAST (mem),
|
|
parent->device, parent->alloc_info.memoryTypeIndex, ¶ms, size,
|
|
parent->properties, NULL, NULL);
|
|
shared->mem_ptr = parent->mem_ptr;
|
|
shared->wrapped = TRUE;
|
|
shared->vk_offset = offset + mem->vk_offset;
|
|
|
|
return GST_MEMORY_CAST (shared);
|
|
}
|
|
|
|
static gboolean
|
|
_vk_mem_is_span (GstVulkanMemory * mem1, GstVulkanMemory * mem2, gsize * offset)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
static GstMemory *
|
|
_vk_mem_alloc (GstAllocator * allocator, gsize size,
|
|
GstAllocationParams * params)
|
|
{
|
|
g_critical ("Subclass should override GstAllocatorClass::alloc() function");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
_vk_mem_free (GstAllocator * allocator, GstMemory * memory)
|
|
{
|
|
GstVulkanMemory *mem = (GstVulkanMemory *) memory;
|
|
|
|
GST_CAT_TRACE (GST_CAT_VULKAN_MEMORY, "freeing buffer memory:%p "
|
|
"id:%" G_GUINT64_FORMAT, mem, (guint64) mem->mem_ptr);
|
|
|
|
g_mutex_clear (&mem->lock);
|
|
|
|
if (mem->notify)
|
|
mem->notify (mem->user_data);
|
|
|
|
if (mem->mem_ptr && !mem->wrapped)
|
|
vkFreeMemory (mem->device->device, mem->mem_ptr, NULL);
|
|
|
|
gst_object_unref (mem->device);
|
|
|
|
g_free (mem);
|
|
}
|
|
|
|
/**
|
|
* gst_vulkan_memory_find_memory_type_index_with_type_properties:
|
|
* @device: a #GstVulkanDevice
|
|
* @type_bits: memory type bits to search for
|
|
* @properties: memory properties to search for
|
|
* @type_index: resulting index of the memory type
|
|
*
|
|
* Returns: whether a valid memory type could be found
|
|
*
|
|
* Since: 1.18
|
|
*/
|
|
gboolean
|
|
gst_vulkan_memory_find_memory_type_index_with_type_properties (GstVulkanDevice *
|
|
device, guint32 type_bits, VkMemoryPropertyFlags properties,
|
|
guint32 * type_index)
|
|
{
|
|
guint32 i;
|
|
|
|
/* Search memtypes to find first index with those properties */
|
|
for (i = 0; i < 32; i++) {
|
|
if ((type_bits & 1) == 1) {
|
|
/* Type is available, does it match user properties? */
|
|
if ((device->physical_device->memory_properties.memoryTypes[i].
|
|
propertyFlags & properties) == properties) {
|
|
*type_index = i;
|
|
return TRUE;
|
|
}
|
|
}
|
|
type_bits >>= 1;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* gst_vulkan_memory_alloc:
|
|
* @device:a #GstVulkanDevice
|
|
* @memory_type_index: the Vulkan memory type index
|
|
* @params: a #GstAllocationParams
|
|
* @size: the size to allocate
|
|
*
|
|
* Allocated a new #GstVulkanMemory.
|
|
*
|
|
* Returns: a #GstMemory object backed by a vulkan device memory
|
|
*
|
|
* Since: 1.18
|
|
*/
|
|
GstMemory *
|
|
gst_vulkan_memory_alloc (GstVulkanDevice * device, guint32 memory_type_index,
|
|
GstAllocationParams * params, gsize size, VkMemoryPropertyFlags mem_flags)
|
|
{
|
|
GstVulkanMemory *mem;
|
|
|
|
mem = _vk_mem_new (_vulkan_memory_allocator, NULL, device, memory_type_index,
|
|
params, size, mem_flags, NULL, NULL);
|
|
|
|
return (GstMemory *) mem;
|
|
}
|
|
|
|
G_DEFINE_TYPE (GstVulkanMemoryAllocator, gst_vulkan_memory_allocator,
|
|
GST_TYPE_ALLOCATOR);
|
|
|
|
static void
|
|
gst_vulkan_memory_allocator_class_init (GstVulkanMemoryAllocatorClass * klass)
|
|
{
|
|
GstAllocatorClass *allocator_class = (GstAllocatorClass *) klass;
|
|
|
|
allocator_class->alloc = _vk_mem_alloc;
|
|
allocator_class->free = _vk_mem_free;
|
|
}
|
|
|
|
static void
|
|
gst_vulkan_memory_allocator_init (GstVulkanMemoryAllocator * allocator)
|
|
{
|
|
GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
|
|
|
|
alloc->mem_type = GST_VULKAN_MEMORY_ALLOCATOR_NAME;
|
|
alloc->mem_map_full = (GstMemoryMapFullFunction) _vk_mem_map_full;
|
|
alloc->mem_unmap_full = (GstMemoryUnmapFullFunction) _vk_mem_unmap_full;
|
|
alloc->mem_copy = (GstMemoryCopyFunction) _vk_mem_copy;
|
|
alloc->mem_share = (GstMemoryShareFunction) _vk_mem_share;
|
|
alloc->mem_is_span = (GstMemoryIsSpanFunction) _vk_mem_is_span;
|
|
}
|
|
|
|
/**
|
|
* gst_vulkan_memory_init_once:
|
|
*
|
|
* Initializes the Vulkan memory allocator. It is safe to call this function
|
|
* multiple times. This must be called before any other #GstVulkanMemory operation.
|
|
*
|
|
* Since: 1.18
|
|
*/
|
|
void
|
|
gst_vulkan_memory_init_once (void)
|
|
{
|
|
static gsize _init = 0;
|
|
|
|
if (g_once_init_enter (&_init)) {
|
|
GST_DEBUG_CATEGORY_INIT (GST_CAT_VULKAN_MEMORY, "vulkanmemory", 0,
|
|
"Vulkan Memory");
|
|
|
|
_vulkan_memory_allocator =
|
|
g_object_new (gst_vulkan_memory_allocator_get_type (), NULL);
|
|
gst_object_ref_sink (_vulkan_memory_allocator);
|
|
|
|
gst_allocator_register (GST_VULKAN_MEMORY_ALLOCATOR_NAME,
|
|
gst_object_ref (_vulkan_memory_allocator));
|
|
g_once_init_leave (&_init, 1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* gst_is_vulkan_memory:
|
|
* @mem:a #GstMemory
|
|
*
|
|
* Returns: whether the memory at @mem is a #GstVulkanMemory
|
|
*
|
|
* Since: 1.18
|
|
*/
|
|
gboolean
|
|
gst_is_vulkan_memory (GstMemory * mem)
|
|
{
|
|
return mem != NULL && mem->allocator != NULL &&
|
|
g_type_is_a (G_OBJECT_TYPE (mem->allocator),
|
|
GST_TYPE_VULKAN_MEMORY_ALLOCATOR);
|
|
}
|