• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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