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 "gstvkcommandpool.h"
26 #include "gstvkcommandpool-private.h"
27
28 /**
29 * SECTION:vkcommandpool
30 * @title: GstVulkanCommandPool
31 * @short_description: Vulkan command pool
32 * @see_also: #GstVulkanCommandBuffer, #GstVulkanDevice
33 */
34
35 #define GST_VULKAN_COMMAND_POOL_LARGE_OUTSTANDING 1024
36
37 #define GET_PRIV(pool) gst_vulkan_command_pool_get_instance_private (pool)
38
39 #define GST_CAT_DEFAULT gst_vulkan_command_pool_debug
40 GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
41
42 struct _GstVulkanCommandPoolPrivate
43 {
44 GQueue *available;
45
46 GRecMutex rec_mutex;
47
48 gsize outstanding;
49 };
50
51 #define parent_class gst_vulkan_command_pool_parent_class
52 G_DEFINE_TYPE_WITH_CODE (GstVulkanCommandPool, gst_vulkan_command_pool,
53 GST_TYPE_OBJECT, G_ADD_PRIVATE (GstVulkanCommandPool);
54 GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT,
55 "vulkancommandpool", 0, "Vulkan Command Pool"));
56
57 static void gst_vulkan_command_pool_finalize (GObject * object);
58
59 static void
gst_vulkan_command_pool_init(GstVulkanCommandPool * pool)60 gst_vulkan_command_pool_init (GstVulkanCommandPool * pool)
61 {
62 GstVulkanCommandPoolPrivate *priv = GET_PRIV (pool);
63
64 g_rec_mutex_init (&priv->rec_mutex);
65 priv->available = g_queue_new ();
66 }
67
68 static void
gst_vulkan_command_pool_class_init(GstVulkanCommandPoolClass * device_class)69 gst_vulkan_command_pool_class_init (GstVulkanCommandPoolClass * device_class)
70 {
71 GObjectClass *gobject_class = (GObjectClass *) device_class;
72
73 gobject_class->finalize = gst_vulkan_command_pool_finalize;
74 }
75
76 static void
do_free_buffer(GstVulkanCommandBuffer * cmd_buf)77 do_free_buffer (GstVulkanCommandBuffer * cmd_buf)
78 {
79 gst_vulkan_command_buffer_unref (cmd_buf);
80 }
81
82 static void
gst_vulkan_command_pool_finalize(GObject * object)83 gst_vulkan_command_pool_finalize (GObject * object)
84 {
85 GstVulkanCommandPool *pool = GST_VULKAN_COMMAND_POOL (object);
86 GstVulkanCommandPoolPrivate *priv = GET_PRIV (pool);
87
88 gst_vulkan_command_pool_lock (pool);
89 g_queue_free_full (priv->available, (GDestroyNotify) do_free_buffer);
90 priv->available = NULL;
91 gst_vulkan_command_pool_unlock (pool);
92
93 if (priv->outstanding > 0)
94 g_critical
95 ("Destroying a Vulkan command pool that has outstanding buffers!");
96
97 if (pool->pool)
98 vkDestroyCommandPool (pool->queue->device->device, pool->pool, NULL);
99 pool->pool = VK_NULL_HANDLE;
100
101 gst_clear_object (&pool->queue);
102
103 g_rec_mutex_clear (&priv->rec_mutex);
104
105 G_OBJECT_CLASS (parent_class)->finalize (object);
106 }
107
108 /**
109 * gst_vulkan_command_pool_get_queue
110 * @pool: a #GstVulkanCommandPool
111 *
112 * Returns: (transfer full): the parent #GstVulkanQueue for this command pool
113 *
114 * Since: 1.18
115 */
116 GstVulkanQueue *
gst_vulkan_command_pool_get_queue(GstVulkanCommandPool * pool)117 gst_vulkan_command_pool_get_queue (GstVulkanCommandPool * pool)
118 {
119 g_return_val_if_fail (GST_IS_VULKAN_COMMAND_POOL (pool), NULL);
120
121 return pool->queue ? gst_object_ref (pool->queue) : NULL;
122 }
123
124 static GstVulkanCommandBuffer *
command_alloc(GstVulkanCommandPool * pool,GError ** error)125 command_alloc (GstVulkanCommandPool * pool, GError ** error)
126 {
127 VkResult err;
128 VkCommandBufferAllocateInfo cmd_info = { 0, };
129 GstVulkanCommandBuffer *buf;
130 VkCommandBuffer cmd;
131
132 cmd_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
133 cmd_info.pNext = NULL;
134 cmd_info.commandPool = pool->pool;
135 cmd_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
136 cmd_info.commandBufferCount = 1;
137
138 gst_vulkan_command_pool_lock (pool);
139 err = vkAllocateCommandBuffers (pool->queue->device->device, &cmd_info, &cmd);
140 gst_vulkan_command_pool_unlock (pool);
141 if (gst_vulkan_error_to_g_error (err, error, "vkCreateCommandBuffer") < 0)
142 return NULL;
143
144 buf =
145 gst_vulkan_command_buffer_new_wrapped (cmd,
146 VK_COMMAND_BUFFER_LEVEL_PRIMARY);
147 GST_LOG_OBJECT (pool, "created cmd buffer %p", buf);
148
149 return buf;
150 }
151
152 static gboolean
gst_vulkan_command_pool_can_reset(GstVulkanCommandPool * pool)153 gst_vulkan_command_pool_can_reset (GstVulkanCommandPool * pool)
154 {
155 g_return_val_if_fail (GST_IS_VULKAN_COMMAND_POOL (pool), FALSE);
156
157 /* whether VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT was in flags on
158 * pool creation */
159 return TRUE;
160 }
161
162 /**
163 * gst_vulkan_command_pool_create:
164 * @pool: a #GstVulkanCommandPool
165 * @error: a #GError
166 *
167 * Returns: a new or recycled primary #GstVulkanCommandBuffer
168 *
169 * Since: 1.18
170 */
171 GstVulkanCommandBuffer *
gst_vulkan_command_pool_create(GstVulkanCommandPool * pool,GError ** error)172 gst_vulkan_command_pool_create (GstVulkanCommandPool * pool, GError ** error)
173 {
174 GstVulkanCommandBuffer *cmd = NULL;
175 GstVulkanCommandPoolPrivate *priv;
176
177 g_return_val_if_fail (GST_IS_VULKAN_COMMAND_POOL (pool), NULL);
178
179 priv = GET_PRIV (pool);
180
181 if (gst_vulkan_command_pool_can_reset (pool)) {
182 gst_vulkan_command_pool_lock (pool);
183 cmd = g_queue_pop_head (priv->available);
184 gst_vulkan_command_pool_unlock (pool);
185 }
186 if (!cmd)
187 cmd = command_alloc (pool, error);
188 if (!cmd)
189 return NULL;
190
191 cmd->pool = gst_object_ref (pool);
192
193 gst_vulkan_command_pool_lock (pool);
194 priv->outstanding++;
195 if (priv->outstanding > GST_VULKAN_COMMAND_POOL_LARGE_OUTSTANDING)
196 g_critical ("%s: There are a large number of command buffers outstanding! "
197 "This usually means there is a reference counting issue somewhere.",
198 GST_OBJECT_NAME (pool));
199 gst_vulkan_command_pool_unlock (pool);
200 return cmd;
201 }
202
203 void
gst_vulkan_command_pool_release_buffer(GstVulkanCommandPool * pool,GstVulkanCommandBuffer * buffer)204 gst_vulkan_command_pool_release_buffer (GstVulkanCommandPool * pool,
205 GstVulkanCommandBuffer * buffer)
206 {
207 GstVulkanCommandPoolPrivate *priv;
208 gboolean can_reset;
209
210 g_return_if_fail (GST_IS_VULKAN_COMMAND_POOL (pool));
211 g_return_if_fail (buffer != NULL);
212 g_return_if_fail (buffer->pool == pool);
213
214 priv = GET_PRIV (pool);
215 can_reset = gst_vulkan_command_pool_can_reset (pool);
216
217 gst_vulkan_command_pool_lock (pool);
218 if (can_reset) {
219 vkResetCommandBuffer (buffer->cmd, 0);
220 g_queue_push_tail (priv->available, buffer);
221 GST_TRACE_OBJECT (pool, "reset command buffer %p", buffer);
222 }
223 /* TODO: if this is a secondary command buffer, all primary command buffers
224 * that reference this command buffer will be invalid */
225 priv->outstanding--;
226 gst_vulkan_command_pool_unlock (pool);
227
228 /* decrease the refcount that the buffer had to us */
229 gst_clear_object (&buffer->pool);
230
231 if (!can_reset)
232 gst_vulkan_command_buffer_unref (buffer);
233 }
234
235 /**
236 * gst_vulkan_command_pool_lock:
237 * @pool: a #GstVulkanCommandPool
238 *
239 * This should be called to ensure no other thread will attempt to access
240 * the pool's internal resources. Any modification of any of the allocated
241 * #GstVulkanCommandBuffer's need to be encapsulated in a
242 * gst_vulkan_command_pool_lock()/gst_vulkan_command_pool_unlock() pair to meet
243 * the Vulkan API requirements that host access to the command pool is
244 * externally synchronised.
245 *
246 * Since: 1.18
247 */
248 void
gst_vulkan_command_pool_lock(GstVulkanCommandPool * pool)249 gst_vulkan_command_pool_lock (GstVulkanCommandPool * pool)
250 {
251 GstVulkanCommandPoolPrivate *priv;
252 g_return_if_fail (GST_IS_VULKAN_COMMAND_POOL (pool));
253 priv = GET_PRIV (pool);
254 g_rec_mutex_lock (&priv->rec_mutex);
255 }
256
257 /**
258 * gst_vulkan_command_pool_unlock:
259 * @pool: a #GstVulkanCommandPool
260 *
261 * See the documentation for gst_vulkan_command_pool_lock() for when you would
262 * need to use this function.
263 *
264 * Since: 1.18
265 */
266 void
gst_vulkan_command_pool_unlock(GstVulkanCommandPool * pool)267 gst_vulkan_command_pool_unlock (GstVulkanCommandPool * pool)
268 {
269 GstVulkanCommandPoolPrivate *priv;
270 g_return_if_fail (GST_IS_VULKAN_COMMAND_POOL (pool));
271 priv = GET_PRIV (pool);
272 g_rec_mutex_unlock (&priv->rec_mutex);
273 }
274