1 /*
2 * GStreamer
3 * Copyright (C) 2019 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 "iosurfacevulkanmemory.h"
26 #include "metal-helpers.h"
27
28 GST_DEBUG_CATEGORY_STATIC (GST_CAT_IO_SURFACE_VULKAN_MEMORY);
29 #define GST_CAT_DEFAULT GST_CAT_IO_SURFACE_VULKAN_MEMORY
30
31 G_DEFINE_TYPE (GstIOSurfaceVulkanMemoryAllocator,
32 gst_io_surface_vulkan_memory_allocator,
33 GST_TYPE_VULKAN_IMAGE_MEMORY_ALLOCATOR);
34
35 typedef struct
36 {
37 GstIOSurfaceVulkanMemory *memory;
38 IOSurfaceRef surface;
39 } ContextThreadData;
40
41 static GstAllocator *_io_surface_vulkan_memory_allocator;
42
43 static void
_mem_free(GstAllocator * allocator,GstMemory * mem)44 _mem_free (GstAllocator * allocator, GstMemory * mem)
45 {
46 gst_io_surface_vulkan_memory_set_surface ((GstIOSurfaceVulkanMemory *) mem,
47 NULL);
48
49 GST_ALLOCATOR_CLASS
50 (gst_io_surface_vulkan_memory_allocator_parent_class)->free (allocator,
51 mem);
52 }
53
54 static gpointer
_io_surface_vulkan_memory_allocator_map(GstMemory * bmem,GstMapInfo * info,gsize size)55 _io_surface_vulkan_memory_allocator_map (GstMemory * bmem,
56 GstMapInfo * info, gsize size)
57 {
58 GstIOSurfaceVulkanMemory *mem = (GstIOSurfaceVulkanMemory *) bmem;
59
60 GST_LOG ("mapping surface %p flags %d", mem->surface, info->flags);
61
62 if (!(info->flags & GST_MAP_WRITE)) {
63 IOSurfaceLock (mem->surface, kIOSurfaceLockReadOnly, NULL);
64 return IOSurfaceGetBaseAddressOfPlane (mem->surface, mem->plane);
65 } else {
66 GST_ERROR ("couldn't map IOSurface %p flags %d", mem->surface, info->flags);
67 return NULL;
68 }
69 }
70
71 static void
_io_surface_vulkan_memory_allocator_unmap(GstMemory * bmem,GstMapInfo * info)72 _io_surface_vulkan_memory_allocator_unmap (GstMemory * bmem, GstMapInfo * info)
73 {
74 GstIOSurfaceVulkanMemory *mem = (GstIOSurfaceVulkanMemory *) bmem;
75
76 GST_LOG ("unmapping surface %p flags %d", mem->surface, info->flags);
77
78 IOSurfaceUnlock (mem->surface, kIOSurfaceLockReadOnly, NULL);
79 }
80
81 static GstMemory *
_mem_alloc(GstAllocator * allocator,gsize size,GstAllocationParams * params)82 _mem_alloc (GstAllocator * allocator, gsize size, GstAllocationParams * params)
83 {
84 g_warning
85 ("use gst_io_surface_vulkan_memory_wrapped () to allocate from this "
86 "IOSurface allocator");
87
88 return NULL;
89 }
90
91 static void
gst_io_surface_vulkan_memory_allocator_class_init(GstIOSurfaceVulkanMemoryAllocatorClass * klass)92 gst_io_surface_vulkan_memory_allocator_class_init
93 (GstIOSurfaceVulkanMemoryAllocatorClass * klass)
94 {
95 GstAllocatorClass *allocator_class = (GstAllocatorClass *) klass;
96
97 allocator_class->alloc = _mem_alloc;
98 allocator_class->free = _mem_free;
99 }
100
101 static void
gst_io_surface_vulkan_memory_allocator_init(GstIOSurfaceVulkanMemoryAllocator * allocator)102 gst_io_surface_vulkan_memory_allocator_init (GstIOSurfaceVulkanMemoryAllocator *
103 allocator)
104 {
105 GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
106
107 alloc->mem_type = GST_IO_SURFACE_VULKAN_MEMORY_ALLOCATOR_NAME;
108 GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC);
109
110 alloc->mem_map_full = _io_surface_vulkan_memory_allocator_map;
111 alloc->mem_unmap_full = _io_surface_vulkan_memory_allocator_unmap;
112 }
113
114 void
gst_io_surface_vulkan_memory_init(void)115 gst_io_surface_vulkan_memory_init (void)
116 {
117 static gsize _init = 0;
118
119 if (g_once_init_enter (&_init)) {
120 GST_DEBUG_CATEGORY_INIT (GST_CAT_IO_SURFACE_VULKAN_MEMORY,
121 "iosurfacevulkan", 0, "IOSurface Vulkan Buffer");
122
123 _io_surface_vulkan_memory_allocator =
124 g_object_new (GST_TYPE_IO_SURFACE_VULKAN_MEMORY_ALLOCATOR, NULL);
125 gst_object_ref_sink (_io_surface_vulkan_memory_allocator);
126
127 gst_allocator_register (GST_IO_SURFACE_VULKAN_MEMORY_ALLOCATOR_NAME,
128 gst_object_ref (_io_surface_vulkan_memory_allocator));
129 g_once_init_leave (&_init, 1);
130 }
131 }
132
133 gboolean
gst_is_io_surface_vulkan_memory(GstMemory * mem)134 gst_is_io_surface_vulkan_memory (GstMemory * mem)
135 {
136 return mem != NULL && mem->allocator != NULL &&
137 g_type_is_a (G_OBJECT_TYPE (mem->allocator),
138 GST_TYPE_IO_SURFACE_VULKAN_MEMORY_ALLOCATOR);
139 }
140
141 static GstIOSurfaceVulkanMemory *
_io_surface_vulkan_memory_new(GstVulkanDevice * device,IOSurfaceRef surface,unsigned int fmt,GstVideoInfo * info,guint plane,gpointer user_data,GDestroyNotify notify)142 _io_surface_vulkan_memory_new (GstVulkanDevice * device, IOSurfaceRef surface,
143 unsigned int /* MTLPixelFormat */ fmt, GstVideoInfo * info, guint plane,
144 gpointer user_data, GDestroyNotify notify)
145 {
146 GstIOSurfaceVulkanMemory *mem;
147 GstAllocationParams params = { 0, };
148 VkImageCreateInfo image_info;
149 VkPhysicalDevice gpu;
150 VkImageUsageFlags usage;
151 VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL;
152 VkFormat vk_format;
153 VkImage image;
154 GError *error = NULL;
155 VkResult err;
156
157 mem = g_new0 (GstIOSurfaceVulkanMemory, 1);
158
159 vk_format = metal_format_to_vulkan (fmt);
160 /* FIXME: choose from outside */
161 usage =
162 VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
163 VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
164
165 /* *INDENT-OFF* */
166 image_info = (VkImageCreateInfo) {
167 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
168 .pNext = NULL,
169 .flags = 0,
170 .imageType = VK_IMAGE_TYPE_2D,
171 .format = vk_format,
172 /* MoltenVK double checks these against the IOSurface in vkUseIOSurface()
173 * and will fail if they do not match */
174 .extent = (VkExtent3D) { GST_VIDEO_INFO_COMP_WIDTH (info, plane), GST_VIDEO_INFO_COMP_HEIGHT (info, plane), 1 },
175 .mipLevels = 1,
176 .arrayLayers = 1,
177 .samples = VK_SAMPLE_COUNT_1_BIT,
178 .tiling = tiling,
179 .usage = usage,
180 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
181 .queueFamilyIndexCount = 0,
182 .pQueueFamilyIndices = NULL,
183 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
184 };
185 /* *INDENT-ON* */
186
187 gpu = gst_vulkan_device_get_physical_device (device);
188
189 err = vkCreateImage (device->device, &image_info, NULL, &image);
190 if (gst_vulkan_error_to_g_error (err, &error, "vkCreateImage") < 0)
191 goto vk_error;
192
193 vkGetImageMemoryRequirements (device->device, image,
194 &mem->vulkan_mem.requirements);
195
196 gst_vulkan_image_memory_init (&mem->vulkan_mem,
197 _io_surface_vulkan_memory_allocator, NULL, device, usage, ¶ms,
198 mem->vulkan_mem.requirements.size, user_data, notify);
199 mem->vulkan_mem.create_info = image_info;
200 mem->vulkan_mem.image = image;
201 mem->vulkan_mem.barrier.image_layout = VK_IMAGE_LAYOUT_GENERAL;
202
203 err =
204 vkGetPhysicalDeviceImageFormatProperties (gpu, vk_format,
205 VK_IMAGE_TYPE_2D, tiling, usage, 0, &mem->vulkan_mem.format_properties);
206 if (gst_vulkan_error_to_g_error (err, &error,
207 "vkGetPhysicalDeviceImageFormatProperties") < 0)
208 goto vk_error;
209
210 GST_MINI_OBJECT_FLAG_SET (mem, GST_MEMORY_FLAG_READONLY);
211
212 mem->surface = NULL;
213 mem->plane = plane;
214 gst_io_surface_vulkan_memory_set_surface (mem, surface);
215
216 return mem;
217
218 vk_error:
219 {
220 GST_CAT_ERROR (GST_CAT_IO_SURFACE_VULKAN_MEMORY,
221 "Failed to allocate image memory %s", error->message);
222 g_clear_error (&error);
223 goto error;
224 }
225
226 error:
227 {
228 if (mem)
229 gst_memory_unref ((GstMemory *) mem);
230 return NULL;
231 }
232 }
233
234 GstIOSurfaceVulkanMemory *
gst_io_surface_vulkan_memory_wrapped(GstVulkanDevice * device,IOSurfaceRef surface,unsigned int fmt,GstVideoInfo * info,guint plane,gpointer user_data,GDestroyNotify notify)235 gst_io_surface_vulkan_memory_wrapped (GstVulkanDevice * device,
236 IOSurfaceRef surface, unsigned int /* MTLPixelFormat */ fmt,
237 GstVideoInfo * info, guint plane, gpointer user_data, GDestroyNotify notify)
238 {
239 return _io_surface_vulkan_memory_new (device, surface, fmt, info, plane,
240 user_data, notify);
241 }
242