d3d11: Update build-time dependency
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / gst-libs / gst / d3d11 / gstd3d11bufferpool.cpp
1 /* GStreamer
2  * Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
3  * Copyright (C) 2020 Seungha Yang <seungha@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 "gstd3d11bufferpool.h"
26 #include "gstd3d11memory.h"
27 #include "gstd3d11device.h"
28 #include "gstd3d11utils.h"
29 #include "gstd3d11_private.h"
30
31 #include <string.h>
32
33 /**
34  * SECTION:gstd3d11bufferpool
35  * @title: GstD3D11BufferPool
36  * @short_description: buffer pool for #GstD3D11Memory objects
37  * @see_also: #GstBufferPool, #GstGLMemory
38  *
39  * a #GstD3D11BufferPool is an object that allocates buffers with #GstD3D11Memory
40  *
41  * A #GstGLBufferPool is created with gst_d3d11_buffer_pool_new()
42  */
43
44 GST_DEBUG_CATEGORY_STATIC (gst_d3d11_buffer_pool_debug);
45 #define GST_CAT_DEFAULT gst_d3d11_buffer_pool_debug
46
47 struct _GstD3D11BufferPoolPrivate
48 {
49   GstD3D11Allocator *alloc[GST_VIDEO_MAX_PLANES];
50
51   GstD3D11AllocationParams *d3d11_params;
52   gboolean texture_array_pool;
53
54   gint stride[GST_VIDEO_MAX_PLANES];
55   gsize offset[GST_VIDEO_MAX_PLANES];
56 };
57
58 #define gst_d3d11_buffer_pool_parent_class parent_class
59 G_DEFINE_TYPE_WITH_PRIVATE (GstD3D11BufferPool,
60     gst_d3d11_buffer_pool, GST_TYPE_BUFFER_POOL);
61
62 static void gst_d3d11_buffer_pool_dispose (GObject * object);
63 static const gchar **gst_d3d11_buffer_pool_get_options (GstBufferPool * pool);
64 static gboolean gst_d3d11_buffer_pool_set_config (GstBufferPool * pool,
65     GstStructure * config);
66 static GstFlowReturn gst_d3d11_buffer_pool_alloc_buffer (GstBufferPool * pool,
67     GstBuffer ** buffer, GstBufferPoolAcquireParams * params);
68 static GstFlowReturn gst_d3d11_buffer_pool_acquire_buffer (GstBufferPool * pool,
69     GstBuffer ** buffer, GstBufferPoolAcquireParams * params);
70 static void gst_d3d11_buffer_pool_reset_buffer (GstBufferPool * pool,
71     GstBuffer * buffer);
72 static gboolean gst_d3d11_buffer_pool_start (GstBufferPool * pool);
73 static gboolean gst_d3d11_buffer_pool_stop (GstBufferPool * pool);
74
75 static void
76 gst_d3d11_buffer_pool_class_init (GstD3D11BufferPoolClass * klass)
77 {
78   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
79   GstBufferPoolClass *bufferpool_class = GST_BUFFER_POOL_CLASS (klass);
80
81   gobject_class->dispose = gst_d3d11_buffer_pool_dispose;
82
83   bufferpool_class->get_options = gst_d3d11_buffer_pool_get_options;
84   bufferpool_class->set_config = gst_d3d11_buffer_pool_set_config;
85   bufferpool_class->alloc_buffer = gst_d3d11_buffer_pool_alloc_buffer;
86   bufferpool_class->acquire_buffer = gst_d3d11_buffer_pool_acquire_buffer;
87   bufferpool_class->reset_buffer = gst_d3d11_buffer_pool_reset_buffer;
88   bufferpool_class->start = gst_d3d11_buffer_pool_start;
89   bufferpool_class->stop = gst_d3d11_buffer_pool_stop;
90
91   GST_DEBUG_CATEGORY_INIT (gst_d3d11_buffer_pool_debug, "d3d11bufferpool", 0,
92       "d3d11bufferpool object");
93 }
94
95 static void
96 gst_d3d11_buffer_pool_init (GstD3D11BufferPool * self)
97 {
98   self->priv = (GstD3D11BufferPoolPrivate *)
99       gst_d3d11_buffer_pool_get_instance_private (self);
100 }
101
102 static void
103 gst_d3d11_buffer_pool_clear_allocator (GstD3D11BufferPool * self)
104 {
105   GstD3D11BufferPoolPrivate *priv = self->priv;
106   guint i;
107
108   for (i = 0; i < G_N_ELEMENTS (priv->alloc); i++) {
109     if (priv->alloc[i]) {
110       gst_d3d11_allocator_set_active (priv->alloc[i], FALSE);
111       gst_clear_object (&priv->alloc[i]);
112     }
113   }
114 }
115
116 static void
117 gst_d3d11_buffer_pool_dispose (GObject * object)
118 {
119   GstD3D11BufferPool *self = GST_D3D11_BUFFER_POOL (object);
120   GstD3D11BufferPoolPrivate *priv = self->priv;
121
122   g_clear_pointer (&priv->d3d11_params, gst_d3d11_allocation_params_free);
123   gst_clear_object (&self->device);
124   gst_d3d11_buffer_pool_clear_allocator (self);
125
126   G_OBJECT_CLASS (parent_class)->dispose (object);
127 }
128
129 static const gchar **
130 gst_d3d11_buffer_pool_get_options (GstBufferPool * pool)
131 {
132   /* NOTE: d3d11 memory does not support alignment */
133   static const gchar *options[] = { GST_BUFFER_POOL_OPTION_VIDEO_META, NULL };
134
135   return options;
136 }
137
138 static gboolean
139 gst_d3d11_buffer_pool_set_config (GstBufferPool * pool, GstStructure * config)
140 {
141   GstD3D11BufferPool *self = GST_D3D11_BUFFER_POOL (pool);
142   GstD3D11BufferPoolPrivate *priv = self->priv;
143   GstVideoInfo info;
144   GstCaps *caps = NULL;
145   guint min_buffers, max_buffers;
146   gboolean ret = TRUE;
147   D3D11_TEXTURE2D_DESC *desc;
148   const GstD3D11Format *format;
149   gsize offset = 0;
150   gint i;
151
152   if (!gst_buffer_pool_config_get_params (config, &caps, NULL, &min_buffers,
153           &max_buffers))
154     goto wrong_config;
155
156   if (caps == NULL)
157     goto no_caps;
158
159   /* now parse the caps from the config */
160   if (!gst_video_info_from_caps (&info, caps))
161     goto wrong_caps;
162
163   GST_LOG_OBJECT (pool, "%dx%d, caps %" GST_PTR_FORMAT, info.width, info.height,
164       caps);
165
166   gst_d3d11_buffer_pool_clear_allocator (self);
167
168   memset (priv->stride, 0, sizeof (priv->stride));
169   memset (priv->offset, 0, sizeof (priv->offset));
170
171   if (priv->d3d11_params)
172     gst_d3d11_allocation_params_free (priv->d3d11_params);
173   priv->d3d11_params =
174       gst_buffer_pool_config_get_d3d11_allocation_params (config);
175   if (!priv->d3d11_params) {
176     /* allocate memory with resource format by default */
177     priv->d3d11_params =
178         gst_d3d11_allocation_params_new (self->device,
179         &info, (GstD3D11AllocationFlags) 0, 0);
180   }
181
182   desc = priv->d3d11_params->desc;
183
184   /* resolution of semi-planar formats must be multiple of 2 */
185   if (desc[0].Format == DXGI_FORMAT_NV12 || desc[0].Format == DXGI_FORMAT_P010
186       || desc[0].Format == DXGI_FORMAT_P016) {
187     if (desc[0].Width % 2 || desc[0].Height % 2) {
188       gint width, height;
189       GstVideoAlignment align;
190
191       GST_WARNING_OBJECT (self, "Resolution %dx%d is not mutiple of 2, fixing",
192           desc[0].Width, desc[0].Height);
193
194       width = GST_ROUND_UP_2 (desc[0].Width);
195       height = GST_ROUND_UP_2 (desc[0].Height);
196
197       gst_video_alignment_reset (&align);
198       align.padding_right = width - desc[0].Width;
199       align.padding_bottom = height - desc[0].Height;
200
201       gst_d3d11_allocation_params_alignment (priv->d3d11_params, &align);
202     }
203   }
204 #ifndef GST_DISABLE_GST_DEBUG
205   {
206     GST_LOG_OBJECT (self, "Direct3D11 Allocation params");
207     GST_LOG_OBJECT (self, "\tD3D11AllocationFlags: 0x%x",
208         priv->d3d11_params->flags);
209     for (i = 0; GST_VIDEO_MAX_PLANES; i++) {
210       if (desc[i].Format == DXGI_FORMAT_UNKNOWN)
211         break;
212       GST_LOG_OBJECT (self, "\t[plane %d] %dx%d, DXGI format %d",
213           i, desc[i].Width, desc[i].Height, desc[i].Format);
214       GST_LOG_OBJECT (self, "\t[plane %d] MipLevel %d, ArraySize %d",
215           i, desc[i].MipLevels, desc[i].ArraySize);
216       GST_LOG_OBJECT (self,
217           "\t[plane %d] SampleDesc.Count %d, SampleDesc.Quality %d",
218           i, desc[i].SampleDesc.Count, desc[i].SampleDesc.Quality);
219       GST_LOG_OBJECT (self, "\t[plane %d] Usage %d", i, desc[i].Usage);
220       GST_LOG_OBJECT (self,
221           "\t[plane %d] BindFlags 0x%x", i, desc[i].BindFlags);
222       GST_LOG_OBJECT (self,
223           "\t[plane %d] CPUAccessFlags 0x%x", i, desc[i].CPUAccessFlags);
224       GST_LOG_OBJECT (self,
225           "\t[plane %d] MiscFlags 0x%x", i, desc[i].MiscFlags);
226     }
227   }
228 #endif
229
230   if ((priv->d3d11_params->flags & GST_D3D11_ALLOCATION_FLAG_TEXTURE_ARRAY)) {
231     guint max_array_size = 0;
232
233     for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) {
234       if (desc[i].Format == DXGI_FORMAT_UNKNOWN)
235         break;
236
237       if (desc[i].ArraySize > max_array_size)
238         max_array_size = desc[i].ArraySize;
239     }
240
241     if (max_buffers == 0 || max_buffers > max_array_size) {
242       GST_WARNING_OBJECT (pool,
243           "Array pool is requested but allowed pool size %d > ArraySize %d",
244           max_buffers, max_array_size);
245       max_buffers = max_array_size;
246     }
247
248     priv->texture_array_pool = TRUE;
249   } else {
250     priv->texture_array_pool = FALSE;
251   }
252
253   offset = 0;
254   for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) {
255     GstD3D11Allocator *alloc;
256     GstD3D11PoolAllocator *pool_alloc;
257     GstFlowReturn flow_ret;
258     GstMemory *mem = NULL;
259     guint stride = 0;
260
261     if (desc[i].Format == DXGI_FORMAT_UNKNOWN)
262       break;
263
264     alloc =
265         (GstD3D11Allocator *) gst_d3d11_pool_allocator_new (self->device,
266         &desc[i]);
267     if (!gst_d3d11_allocator_set_active (alloc, TRUE)) {
268       GST_ERROR_OBJECT (self, "Failed to activate allocator");
269       gst_object_unref (alloc);
270       return FALSE;
271     }
272
273     pool_alloc = GST_D3D11_POOL_ALLOCATOR (alloc);
274     flow_ret = gst_d3d11_pool_allocator_acquire_memory (pool_alloc, &mem);
275     if (flow_ret != GST_FLOW_OK) {
276       GST_ERROR_OBJECT (self, "Failed to allocate initial memory");
277       gst_d3d11_allocator_set_active (alloc, FALSE);
278       gst_object_unref (alloc);
279       return FALSE;
280     }
281
282     if (!gst_d3d11_memory_get_resource_stride (GST_D3D11_MEMORY_CAST (mem),
283             &stride) || stride < desc[i].Width) {
284       GST_ERROR_OBJECT (self, "Failed to calculate stride");
285
286       gst_d3d11_allocator_set_active (alloc, FALSE);
287       gst_object_unref (alloc);
288       gst_memory_unref (mem);
289
290       return FALSE;
291     }
292
293     priv->stride[i] = stride;
294     priv->offset[i] = offset;
295     offset += mem->size;
296
297     priv->alloc[i] = alloc;
298
299     gst_memory_unref (mem);
300   }
301
302   g_assert (priv->d3d11_params->d3d11_format != NULL);
303   format = priv->d3d11_params->d3d11_format;
304   /* single texture semi-planar formats */
305   if (format->dxgi_format != DXGI_FORMAT_UNKNOWN &&
306       GST_VIDEO_INFO_N_PLANES (&info) == 2) {
307     priv->stride[1] = priv->stride[0];
308     priv->offset[1] = priv->stride[0] * desc[0].Height;
309   }
310
311   gst_buffer_pool_config_set_params (config,
312       caps, offset, min_buffers, max_buffers);
313
314   return GST_BUFFER_POOL_CLASS (parent_class)->set_config (pool, config) && ret;
315
316   /* ERRORS */
317 wrong_config:
318   {
319     GST_WARNING_OBJECT (pool, "invalid config");
320     return FALSE;
321   }
322 no_caps:
323   {
324     GST_WARNING_OBJECT (pool, "no caps in config");
325     return FALSE;
326   }
327 wrong_caps:
328   {
329     GST_WARNING_OBJECT (pool,
330         "failed getting geometry from caps %" GST_PTR_FORMAT, caps);
331     return FALSE;
332   }
333 }
334
335 static GstFlowReturn
336 gst_d3d11_buffer_pool_fill_buffer (GstD3D11BufferPool * self, GstBuffer * buf)
337 {
338   GstD3D11BufferPoolPrivate *priv = self->priv;
339   GstFlowReturn ret = GST_FLOW_OK;
340   guint i;
341
342   for (i = 0; i < G_N_ELEMENTS (priv->alloc); i++) {
343     GstMemory *mem = NULL;
344     GstD3D11PoolAllocator *alloc = GST_D3D11_POOL_ALLOCATOR (priv->alloc[i]);
345
346     if (!alloc)
347       break;
348
349     ret = gst_d3d11_pool_allocator_acquire_memory (alloc, &mem);
350     if (ret != GST_FLOW_OK) {
351       GST_WARNING_OBJECT (self, "Failed to acquire memory, ret %s",
352           gst_flow_get_name (ret));
353       return ret;
354     }
355
356     gst_buffer_append_memory (buf, mem);
357   }
358
359   return GST_FLOW_OK;
360 }
361
362 static GstFlowReturn
363 gst_d3d11_buffer_pool_alloc_buffer (GstBufferPool * pool, GstBuffer ** buffer,
364     GstBufferPoolAcquireParams * params)
365 {
366   GstD3D11BufferPool *self = GST_D3D11_BUFFER_POOL (pool);
367   GstD3D11BufferPoolPrivate *priv = self->priv;
368   GstD3D11AllocationParams *d3d11_params = priv->d3d11_params;
369   GstVideoInfo *info = &d3d11_params->info;
370   GstBuffer *buf;
371   GstFlowReturn ret = GST_FLOW_OK;
372
373   buf = gst_buffer_new ();
374   /* In case of texture-array, we are releasing memory objects in
375    * the GstBufferPool::reset_buffer() so that GstD3D11Memory objects can be
376    * returned to the GstD3D11PoolAllocator. So, underlying GstD3D11Memory
377    * will be filled in the later GstBufferPool::acquire_buffer() call.
378    * Don't fill memory here for non-texture-array therefore */
379   if (!priv->texture_array_pool) {
380     ret = gst_d3d11_buffer_pool_fill_buffer (self, buf);
381     if (ret != GST_FLOW_OK) {
382       gst_buffer_unref (buf);
383       return ret;
384     }
385   }
386
387   gst_buffer_add_video_meta_full (buf, GST_VIDEO_FRAME_FLAG_NONE,
388       GST_VIDEO_INFO_FORMAT (info), GST_VIDEO_INFO_WIDTH (info),
389       GST_VIDEO_INFO_HEIGHT (info), GST_VIDEO_INFO_N_PLANES (info),
390       priv->offset, priv->stride);
391
392   *buffer = buf;
393
394   return GST_FLOW_OK;
395 }
396
397 static GstFlowReturn
398 gst_d3d11_buffer_pool_acquire_buffer (GstBufferPool * pool,
399     GstBuffer ** buffer, GstBufferPoolAcquireParams * params)
400 {
401   GstD3D11BufferPool *self = GST_D3D11_BUFFER_POOL (pool);
402   GstD3D11BufferPoolPrivate *priv = self->priv;
403   GstFlowReturn ret;
404
405   ret = GST_BUFFER_POOL_CLASS (parent_class)->acquire_buffer (pool,
406       buffer, params);
407
408   if (ret != GST_FLOW_OK)
409     return ret;
410
411   /* Don't need special handling for non-texture-array case */
412   if (!priv->texture_array_pool)
413     return ret;
414
415   /* Baseclass will hold empty buffer in this case, fill GstMemory */
416   g_assert (gst_buffer_n_memory (*buffer) == 0);
417
418   return gst_d3d11_buffer_pool_fill_buffer (self, *buffer);
419 }
420
421 static void
422 gst_d3d11_buffer_pool_reset_buffer (GstBufferPool * pool, GstBuffer * buffer)
423 {
424   GstD3D11BufferPool *self = GST_D3D11_BUFFER_POOL (pool);
425   GstD3D11BufferPoolPrivate *priv = self->priv;
426
427   /* If we are using texture array, we should return GstD3D11Memory to
428    * to the GstD3D11PoolAllocator, so that the allocator can wake up
429    * if it's waiting for available memory object */
430   if (priv->texture_array_pool) {
431     GST_LOG_OBJECT (self, "Returning memory to allocator");
432     gst_buffer_remove_all_memory (buffer);
433   }
434
435   GST_BUFFER_POOL_CLASS (parent_class)->reset_buffer (pool, buffer);
436   GST_BUFFER_FLAGS (buffer) = 0;
437 }
438
439 static gboolean
440 gst_d3d11_buffer_pool_start (GstBufferPool * pool)
441 {
442   GstD3D11BufferPool *self = GST_D3D11_BUFFER_POOL (pool);
443   GstD3D11BufferPoolPrivate *priv = self->priv;
444   guint i;
445   gboolean ret;
446
447   GST_DEBUG_OBJECT (self, "Start");
448
449   for (i = 0; i < G_N_ELEMENTS (priv->alloc); i++) {
450     GstD3D11Allocator *alloc = priv->alloc[i];
451
452     if (!alloc)
453       break;
454
455     if (!gst_d3d11_allocator_set_active (alloc, TRUE)) {
456       GST_ERROR_OBJECT (self, "Failed to activate allocator");
457       return FALSE;
458     }
459   }
460
461   ret = GST_BUFFER_POOL_CLASS (parent_class)->start (pool);
462   if (!ret) {
463     GST_ERROR_OBJECT (self, "Failed to start");
464
465     for (i = 0; i < G_N_ELEMENTS (priv->alloc); i++) {
466       GstD3D11Allocator *alloc = priv->alloc[i];
467
468       if (!alloc)
469         break;
470
471       gst_d3d11_allocator_set_active (alloc, FALSE);
472     }
473
474     return FALSE;
475   }
476
477   return TRUE;
478 }
479
480 static gboolean
481 gst_d3d11_buffer_pool_stop (GstBufferPool * pool)
482 {
483   GstD3D11BufferPool *self = GST_D3D11_BUFFER_POOL (pool);
484   GstD3D11BufferPoolPrivate *priv = self->priv;
485   guint i;
486
487   GST_DEBUG_OBJECT (self, "Stop");
488
489   for (i = 0; i < G_N_ELEMENTS (priv->alloc); i++) {
490     GstD3D11Allocator *alloc = priv->alloc[i];
491
492     if (!alloc)
493       break;
494
495     if (!gst_d3d11_allocator_set_active (alloc, FALSE)) {
496       GST_ERROR_OBJECT (self, "Failed to deactivate allocator");
497       return FALSE;
498     }
499   }
500
501   return GST_BUFFER_POOL_CLASS (parent_class)->stop (pool);
502 }
503
504 /**
505  * gst_d3d11_buffer_pool_new:
506  * @device: a #GstD3D11Device to use
507  *
508  * Returns: a #GstBufferPool that allocates buffers with #GstD3D11Memory
509  *
510  * Since: 1.20
511  */
512 GstBufferPool *
513 gst_d3d11_buffer_pool_new (GstD3D11Device * device)
514 {
515   GstD3D11BufferPool *pool;
516
517   g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
518
519   pool = (GstD3D11BufferPool *) g_object_new (GST_TYPE_D3D11_BUFFER_POOL, NULL);
520   gst_object_ref_sink (pool);
521
522   pool->device = (GstD3D11Device *) gst_object_ref (device);
523
524   return GST_BUFFER_POOL_CAST (pool);
525 }
526
527 /**
528  * gst_buffer_pool_config_get_d3d11_allocation_params:
529  * @config: a buffer pool config
530  *
531  * Returns: (transfer full) (nullable): the currently configured
532  * #GstD3D11AllocationParams on @config or %NULL if @config doesn't contain
533  * #GstD3D11AllocationParams
534  *
535  * Since: 1.20
536  */
537 GstD3D11AllocationParams *
538 gst_buffer_pool_config_get_d3d11_allocation_params (GstStructure * config)
539 {
540   GstD3D11AllocationParams *ret;
541
542   if (!gst_structure_get (config, "d3d11-allocation-params",
543           GST_TYPE_D3D11_ALLOCATION_PARAMS, &ret, NULL))
544     ret = NULL;
545
546   return ret;
547 }
548
549 /**
550  * gst_buffer_pool_config_set_d3d11_allocation_params:
551  * @config: a buffer pool config
552  * @params: (transfer none): a #GstD3D11AllocationParams
553  *
554  * Sets @params on @config
555  *
556  * Since: 1.20
557  */
558 void
559 gst_buffer_pool_config_set_d3d11_allocation_params (GstStructure * config,
560     GstD3D11AllocationParams * params)
561 {
562   g_return_if_fail (config != NULL);
563   g_return_if_fail (params != NULL);
564
565   gst_structure_set (config, "d3d11-allocation-params",
566       GST_TYPE_D3D11_ALLOCATION_PARAMS, params, NULL);
567 }