1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
|
/*
* Copyright (C) 2018 Igalia S.L
* Copyright (C) 2018 Metrological Group B.V.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "config.h"
#include "GstAllocatorFastMalloc.h"
#include <gst/gst.h>
#include <wtf/FastMalloc.h>
#include <wtf/MallocSpan.h>
#include <wtf/StdLibExtras.h>
#include <wtf/glib/WTFGType.h>
typedef struct {
// Instance of the base GstMemory class (we're effectively subclassing it, not unlike GstMemorySystem).
GstMemory base;
// Span to the tail of an GstMemoryFastMalloc allocation block where the user memory contents will be stored.
// gstMemoryFastMallocNew() will allocate enough space to fit such tail after GstMemoryFastMalloc.
// gstAllocatorFastMallocMemShare() only allocates enough space for the GstMemoryFastMalloc struct and
// instead shares the `data` span with the original (parent) GstMemoryFastMalloc.
std::span<uint8_t> data;
} GstMemoryFastMalloc;
//
// Note: when allocating GstMemoryFastMalloc we also allocate a long enough tail to hold the contents
// the user will store. That tail is what will be returned by our map() function.
typedef struct {
} GstAllocatorFastMallocPrivate;
typedef struct {
GstAllocator parent;
GstAllocatorFastMallocPrivate* priv;
} GstAllocatorFastMalloc;
typedef struct {
GstAllocatorClass parent;
} GstAllocatorFastMallocClass;
WEBKIT_DEFINE_TYPE(GstAllocatorFastMalloc, gst_allocator_fast_malloc, GST_TYPE_ALLOCATOR)
#define WEBKIT_GST_ALLOCATOR_FAST_MALLOC_CAST(p) reinterpret_cast<GstAllocatorFastMalloc*>(p)
#define GST_MEMORY_FAST_MALLOC_CAST(p) reinterpret_cast<GstMemoryFastMalloc*>(p)
static GstMemoryFastMalloc* gstMemoryFastMallocNew(GstAllocator* allocator, gsize size, gsize alignment, gsize offset, gsize padding, GstMemoryFlags flags)
{
ASSERT(G_TYPE_CHECK_INSTANCE_TYPE(allocator, gst_allocator_fast_malloc_get_type()));
// Alignment should be a (power-of-two - 1). That is GStreamer's convention.
alignment |= gst_memory_alignment;
ASSERT(!((alignment + 1) & alignment));
// GStreamer's allocator requires heap allocations.
DisableMallocRestrictionsForCurrentThreadScope disableMallocRestrictions;
// The block we get from FastMalloc consists of:
// (1) *Header*: GstMemoryFastMalloc struct, plus any extra space needed to satisfy alignment.
gsize headerSize = (sizeof(GstMemoryFastMalloc) + alignment) & ~alignment; // Rounds up to the nearest alignment unit.
// (2) *Tail*: the memory that is accessible to the user via GstMemory.map().
gsize tailSize = offset + size + padding;
auto totalSize = headerSize + tailSize;
auto storage = MallocSpan<uint8_t, FastAlignedMalloc>::tryAlignedMalloc(alignment + 1 /* Power of 2 */, totalSize);
if (!storage)
return nullptr;
auto wholeAllocation = storage.leakSpan();
auto& header = WTF::reinterpretCastSpanStartTo<GstMemoryFastMalloc>(wholeAllocation);
header.data = wholeAllocation.subspan(headerSize);
if (offset && (flags & GST_MEMORY_FLAG_ZERO_PREFIXED))
memsetSpan(header.data.subspan(0, offset), 0);
if (padding && (flags & GST_MEMORY_FLAG_ZERO_PADDED))
memsetSpan(header.data.subspan(offset + size, padding), 0);
gst_memory_init(&header.base, flags, allocator, nullptr, tailSize, alignment, offset, size);
return &header;
}
static GstMemory* gstAllocatorFastMallocAlloc(GstAllocator* allocator, gsize size, GstAllocationParams* params)
{
return GST_MEMORY_CAST(gstMemoryFastMallocNew(allocator, size, params->align, params->prefix, params->padding, params->flags));
}
static void gstAllocatorFastMallocFree(GstAllocator* allocator, GstMemory* memory)
{
#if ASSERT_ENABLED
ASSERT(G_TYPE_CHECK_INSTANCE_TYPE(allocator, gst_allocator_fast_malloc_get_type()));
#else
UNUSED_PARAM(allocator);
#endif
fastAlignedFree(memory);
}
static gpointer gstAllocatorFastMallocMemMap(GstMemory* baseMemory, gsize, GstMapFlags)
{
auto memory = GST_MEMORY_FAST_MALLOC_CAST(baseMemory);
return memory->data.data();
}
static void gstAllocatorFastMallocMemUnmap(GstMemory*)
{
}
static GstMemory* gstAllocatorFastMallocMemCopy(GstMemory* baseMemory, gssize offset, gssize size)
{
auto memory = GST_MEMORY_FAST_MALLOC_CAST(baseMemory);
if (size == static_cast<gssize>(-1))
size = memory->base.size > static_cast<gsize>(offset) ? memory->base.size - offset : 0;
auto* copy = gstMemoryFastMallocNew(memory->base.allocator, size, memory->base.align, 0, 0, static_cast<GstMemoryFlags>(0));
if (!copy)
return nullptr;
auto destinationSpan = copy->data.subspan(0, size);
memcpySpan(destinationSpan, memory->data.subspan(memory->base.offset + offset, size));
return GST_MEMORY_CAST(copy);
}
static GstMemory* gstAllocatorFastMallocMemShare(GstMemory* baseMemory, gssize offset, gssize size)
{
auto memory = GST_MEMORY_FAST_MALLOC_CAST(baseMemory);
GstMemoryFastMalloc* sharedMemory;
if (!tryFastMalloc(sizeof(GstMemoryFastMalloc)).getValue(sharedMemory))
return nullptr;
sharedMemory->data = memory->data;
if (size == static_cast<gssize>(-1))
size = memory->base.size - offset;
auto* parent = memory->base.parent ? memory->base.parent : GST_MEMORY_CAST(memory);
gst_memory_init(GST_MEMORY_CAST(sharedMemory),
static_cast<GstMemoryFlags>(GST_MINI_OBJECT_FLAGS(parent) | GST_MINI_OBJECT_FLAG_LOCK_READONLY),
memory->base.allocator, parent, memory->base.maxsize, memory->base.align,
memory->base.offset + offset, size);
return GST_MEMORY_CAST(sharedMemory);
}
// Will be called by gst_memory_is_span() after checking that both GstMemories have
// the same allocators and they both share the same non-null parent.
//
// Returns whether the visible end of the first memory coincides with the visible
// start of the second memory and fills `offset` with the offset of the first memory
// within the visible span of the parent memory.
//
// This is used to avoid expensive concatenation: If the function returns true, it
// means you can use gst_memory_share() on the parent memory with the filled offset
// and the combined size of both memories to get a GstMemory that spans both previous
// memories without doing any copies. See _sysmem_is_span() in GStreamer for reference.
static gboolean gstAllocatorFastMallocMemIsSpan(GstMemory* _memory1, GstMemory* _memory2, gsize* offset)
{
auto memory1 = GST_MEMORY_FAST_MALLOC_CAST(_memory1);
auto memory2 = GST_MEMORY_FAST_MALLOC_CAST(_memory2);
if (offset) {
auto parent = GST_MEMORY_FAST_MALLOC_CAST(memory1->base.parent);
ASSERT(parent);
*offset = memory1->base.offset - parent->base.offset;
}
auto visibleSpan1 = memory1->data.subspan(memory1->base.offset, memory1->base.size);
auto visibleSpan2 = memory2->data.subspan(memory2->base.offset, memory2->base.size);
return visibleSpan1.end() == visibleSpan2.begin();
}
static void gstAllocatorFastMallocConstructed(GObject* object)
{
G_OBJECT_CLASS(gst_allocator_fast_malloc_parent_class)->constructed(object);
GST_OBJECT_FLAG_SET(GST_OBJECT_CAST(object), GST_OBJECT_FLAG_MAY_BE_LEAKED);
auto allocator = GST_ALLOCATOR_CAST(object);
allocator->mem_type = "FastMalloc";
allocator->mem_map = gstAllocatorFastMallocMemMap;
allocator->mem_unmap = gstAllocatorFastMallocMemUnmap;
allocator->mem_copy = gstAllocatorFastMallocMemCopy;
allocator->mem_share = gstAllocatorFastMallocMemShare;
allocator->mem_is_span = gstAllocatorFastMallocMemIsSpan;
}
static void gst_allocator_fast_malloc_class_init(GstAllocatorFastMallocClass* klass)
{
auto gobjectClass = G_OBJECT_CLASS(klass);
gobjectClass->constructed = gstAllocatorFastMallocConstructed;
auto gstAllocatorClass = GST_ALLOCATOR_CLASS(klass);
gstAllocatorClass->alloc = gstAllocatorFastMallocAlloc;
gstAllocatorClass->free = gstAllocatorFastMallocFree;
}
|