d3d11: Update build-time dependency
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / gst-libs / gst / d3d11 / gstd3d11stagingbufferpool.cpp
1 /* GStreamer
2  * Copyright (C) 2022 Seungha Yang <seungha@centricular.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "gstd3d11stagingbufferpool.h"
25 #include "gstd3d11memory.h"
26 #include "gstd3d11device.h"
27 #include "gstd3d11utils.h"
28 #include "gstd3d11_private.h"
29
30 #include <string.h>
31
32 GST_DEBUG_CATEGORY_STATIC (gst_d3d11_staging_buffer_pool_debug);
33 #define GST_CAT_DEFAULT gst_d3d11_staging_buffer_pool_debug
34
35 struct _GstD3D11StagingBufferPoolPrivate
36 {
37   GstVideoInfo info;
38
39   D3D11_TEXTURE2D_DESC desc[GST_VIDEO_MAX_PLANES];
40   GstD3D11Allocator *alloc[GST_VIDEO_MAX_PLANES];
41   gint stride[GST_VIDEO_MAX_PLANES];
42   gsize offset[GST_VIDEO_MAX_PLANES];
43 };
44
45 #define gst_d3d11_staging_buffer_pool_parent_class parent_class
46 G_DEFINE_TYPE_WITH_PRIVATE (GstD3D11StagingBufferPool,
47     gst_d3d11_staging_buffer_pool, GST_TYPE_BUFFER_POOL);
48
49 static void gst_d3d11_staging_buffer_pool_dispose (GObject * object);
50 static const gchar **gst_d3d11_staging_buffer_pool_get_options (GstBufferPool *
51     pool);
52 static gboolean gst_d3d11_staging_buffer_pool_set_config (GstBufferPool * pool,
53     GstStructure * config);
54 static GstFlowReturn gst_d3d11_staging_buffer_pool_alloc_buffer (GstBufferPool *
55     pool, GstBuffer ** buffer, GstBufferPoolAcquireParams * params);
56 static gboolean gst_d3d11_staging_buffer_pool_start (GstBufferPool * pool);
57 static gboolean gst_d3d11_staging_buffer_pool_stop (GstBufferPool * pool);
58
59 static void
60 gst_d3d11_staging_buffer_pool_class_init (GstD3D11StagingBufferPoolClass *
61     klass)
62 {
63   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
64   GstBufferPoolClass *bufferpool_class = GST_BUFFER_POOL_CLASS (klass);
65
66   gobject_class->dispose = gst_d3d11_staging_buffer_pool_dispose;
67
68   bufferpool_class->get_options = gst_d3d11_staging_buffer_pool_get_options;
69   bufferpool_class->set_config = gst_d3d11_staging_buffer_pool_set_config;
70   bufferpool_class->alloc_buffer = gst_d3d11_staging_buffer_pool_alloc_buffer;
71   bufferpool_class->start = gst_d3d11_staging_buffer_pool_start;
72   bufferpool_class->stop = gst_d3d11_staging_buffer_pool_stop;
73
74   GST_DEBUG_CATEGORY_INIT (gst_d3d11_staging_buffer_pool_debug,
75       "d3d11stagingbufferpool", 0, "d3d11stagingbufferpool object");
76 }
77
78 static void
79 gst_d3d11_staging_buffer_pool_init (GstD3D11StagingBufferPool * self)
80 {
81   self->priv = (GstD3D11StagingBufferPoolPrivate *)
82       gst_d3d11_staging_buffer_pool_get_instance_private (self);
83 }
84
85 static void
86 gst_d3d11_staging_buffer_pool_clear_allocator (GstD3D11StagingBufferPool * self)
87 {
88   GstD3D11StagingBufferPoolPrivate *priv = self->priv;
89
90   for (guint i = 0; i < G_N_ELEMENTS (priv->alloc); i++) {
91     if (priv->alloc[i]) {
92       gst_d3d11_allocator_set_active (priv->alloc[i], FALSE);
93       gst_clear_object (&priv->alloc[i]);
94     }
95   }
96 }
97
98 static void
99 gst_d3d11_staging_buffer_pool_dispose (GObject * object)
100 {
101   GstD3D11StagingBufferPool *self = GST_D3D11_STAGING_BUFFER_POOL (object);
102
103   gst_clear_object (&self->device);
104   gst_d3d11_staging_buffer_pool_clear_allocator (self);
105
106   G_OBJECT_CLASS (parent_class)->dispose (object);
107 }
108
109 static const gchar **
110 gst_d3d11_staging_buffer_pool_get_options (GstBufferPool * pool)
111 {
112   /* NOTE: d3d11 memory does not support alignment */
113   static const gchar *options[] =
114       { GST_BUFFER_POOL_OPTION_VIDEO_META, nullptr };
115
116   return options;
117 }
118
119 static gboolean
120 gst_d3d11_staging_buffer_pool_set_config (GstBufferPool * pool,
121     GstStructure * config)
122 {
123   GstD3D11StagingBufferPool *self = GST_D3D11_STAGING_BUFFER_POOL (pool);
124   GstD3D11StagingBufferPoolPrivate *priv = self->priv;
125   GstVideoInfo info;
126   GstCaps *caps = nullptr;
127   guint min_buffers, max_buffers;
128   D3D11_TEXTURE2D_DESC *desc;
129   const GstD3D11Format *format;
130   gsize offset = 0;
131
132   if (!gst_buffer_pool_config_get_params (config, &caps, nullptr, &min_buffers,
133           &max_buffers))
134     goto wrong_config;
135
136   if (caps == nullptr)
137     goto no_caps;
138
139   /* now parse the caps from the config */
140   if (!gst_video_info_from_caps (&info, caps))
141     goto wrong_caps;
142
143   format = gst_d3d11_device_format_from_gst (self->device,
144       GST_VIDEO_INFO_FORMAT (&info));
145   if (!format)
146     goto wrong_caps;
147
148   GST_LOG_OBJECT (pool, "%dx%d, caps %" GST_PTR_FORMAT, info.width, info.height,
149       caps);
150
151   gst_d3d11_staging_buffer_pool_clear_allocator (self);
152
153   memset (priv->stride, 0, sizeof (priv->stride));
154   memset (priv->offset, 0, sizeof (priv->offset));
155   memset (priv->desc, 0, sizeof (priv->desc));
156
157   if (format->dxgi_format == DXGI_FORMAT_UNKNOWN) {
158     for (guint i = 0; i < GST_VIDEO_INFO_N_PLANES (&info); i++) {
159       desc = &priv->desc[i];
160
161       desc->Width = GST_VIDEO_INFO_COMP_WIDTH (&info, i);
162       desc->Height = GST_VIDEO_INFO_COMP_HEIGHT (&info, i);
163       desc->MipLevels = 1;
164       desc->ArraySize = 1;
165       desc->Format = format->resource_format[i];
166       desc->SampleDesc.Count = 1;
167       desc->Usage = D3D11_USAGE_STAGING;
168       desc->CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
169     }
170   } else {
171     guint width, height;
172
173     desc = &priv->desc[0];
174
175     width = GST_VIDEO_INFO_WIDTH (&info);
176     height = GST_VIDEO_INFO_HEIGHT (&info);
177
178     /* resolution of semi-planar formats must be multiple of 2 */
179     switch (format->dxgi_format) {
180       case DXGI_FORMAT_NV12:
181       case DXGI_FORMAT_P010:
182       case DXGI_FORMAT_P016:
183         width = GST_ROUND_UP_2 (width);
184         height = GST_ROUND_UP_2 (height);
185         break;
186       default:
187         break;
188     }
189
190     desc->Width = width;
191     desc->Height = height;
192     desc->MipLevels = 1;
193     desc->ArraySize = 1;
194     desc->Format = format->dxgi_format;
195     desc->SampleDesc.Count = 1;
196     desc->Usage = D3D11_USAGE_STAGING;
197     desc->CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
198   }
199
200   offset = 0;
201   for (guint i = 0; i < GST_VIDEO_MAX_PLANES; i++) {
202     GstD3D11Allocator *alloc;
203     GstD3D11PoolAllocator *pool_alloc;
204     GstFlowReturn flow_ret;
205     GstMemory *mem = nullptr;
206     guint stride = 0;
207
208     if (priv->desc[i].Format == DXGI_FORMAT_UNKNOWN)
209       break;
210
211     alloc =
212         (GstD3D11Allocator *) gst_d3d11_pool_allocator_new (self->device,
213         &priv->desc[i]);
214     if (!gst_d3d11_allocator_set_active (alloc, TRUE)) {
215       GST_ERROR_OBJECT (self, "Failed to activate allocator");
216       gst_object_unref (alloc);
217       return FALSE;
218     }
219
220     pool_alloc = GST_D3D11_POOL_ALLOCATOR (alloc);
221     flow_ret = gst_d3d11_pool_allocator_acquire_memory (pool_alloc, &mem);
222     if (flow_ret != GST_FLOW_OK) {
223       GST_ERROR_OBJECT (self, "Failed to allocate initial memory");
224       gst_d3d11_allocator_set_active (alloc, FALSE);
225       gst_object_unref (alloc);
226       return FALSE;
227     }
228
229     if (!gst_d3d11_memory_get_resource_stride (GST_D3D11_MEMORY_CAST (mem),
230             &stride) || stride < priv->desc[i].Width) {
231       GST_ERROR_OBJECT (self, "Failed to calculate stride");
232
233       gst_d3d11_allocator_set_active (alloc, FALSE);
234       gst_object_unref (alloc);
235       gst_memory_unref (mem);
236
237       return FALSE;
238     }
239
240     priv->stride[i] = stride;
241     priv->offset[i] = offset;
242     offset += mem->size;
243
244     priv->alloc[i] = alloc;
245
246     gst_memory_unref (mem);
247   }
248
249   /* single texture semi-planar formats */
250   if (format->dxgi_format != DXGI_FORMAT_UNKNOWN &&
251       GST_VIDEO_INFO_N_PLANES (&info) == 2) {
252     priv->stride[1] = priv->stride[0];
253     priv->offset[1] = priv->stride[0] * priv->desc[0].Height;
254   }
255
256   gst_buffer_pool_config_set_params (config,
257       caps, offset, min_buffers, max_buffers);
258
259   priv->info = info;
260
261   return GST_BUFFER_POOL_CLASS (parent_class)->set_config (pool, config);
262
263   /* ERRORS */
264 wrong_config:
265   {
266     GST_WARNING_OBJECT (pool, "invalid config");
267     return FALSE;
268   }
269 no_caps:
270   {
271     GST_WARNING_OBJECT (pool, "no caps in config");
272     return FALSE;
273   }
274 wrong_caps:
275   {
276     GST_WARNING_OBJECT (pool,
277         "failed getting geometry from caps %" GST_PTR_FORMAT, caps);
278     return FALSE;
279   }
280 }
281
282 static GstFlowReturn
283 gst_d3d11_staging_buffer_pool_fill_buffer (GstD3D11StagingBufferPool * self,
284     GstBuffer * buf)
285 {
286   GstD3D11StagingBufferPoolPrivate *priv = self->priv;
287   GstFlowReturn ret = GST_FLOW_OK;
288
289   for (guint i = 0; i < G_N_ELEMENTS (priv->alloc); i++) {
290     GstMemory *mem = nullptr;
291     GstD3D11PoolAllocator *alloc = GST_D3D11_POOL_ALLOCATOR (priv->alloc[i]);
292
293     if (!alloc)
294       break;
295
296     ret = gst_d3d11_pool_allocator_acquire_memory (alloc, &mem);
297     if (ret != GST_FLOW_OK) {
298       GST_WARNING_OBJECT (self, "Failed to acquire memory, ret %s",
299           gst_flow_get_name (ret));
300       return ret;
301     }
302
303     gst_buffer_append_memory (buf, mem);
304   }
305
306   return GST_FLOW_OK;
307 }
308
309 static GstFlowReturn
310 gst_d3d11_staging_buffer_pool_alloc_buffer (GstBufferPool * pool,
311     GstBuffer ** buffer, GstBufferPoolAcquireParams * params)
312 {
313   GstD3D11StagingBufferPool *self = GST_D3D11_STAGING_BUFFER_POOL (pool);
314   GstD3D11StagingBufferPoolPrivate *priv = self->priv;
315   GstVideoInfo *info = &priv->info;
316   GstBuffer *buf;
317   GstFlowReturn ret = GST_FLOW_OK;
318
319   buf = gst_buffer_new ();
320   ret = gst_d3d11_staging_buffer_pool_fill_buffer (self, buf);
321   if (ret != GST_FLOW_OK) {
322     gst_buffer_unref (buf);
323     return ret;
324   }
325
326   gst_buffer_add_video_meta_full (buf, GST_VIDEO_FRAME_FLAG_NONE,
327       GST_VIDEO_INFO_FORMAT (info), GST_VIDEO_INFO_WIDTH (info),
328       GST_VIDEO_INFO_HEIGHT (info), GST_VIDEO_INFO_N_PLANES (info),
329       priv->offset, priv->stride);
330
331   *buffer = buf;
332
333   return GST_FLOW_OK;
334 }
335
336 static gboolean
337 gst_d3d11_staging_buffer_pool_start (GstBufferPool * pool)
338 {
339   GstD3D11StagingBufferPool *self = GST_D3D11_STAGING_BUFFER_POOL (pool);
340   GstD3D11StagingBufferPoolPrivate *priv = self->priv;
341   gboolean ret;
342
343   GST_DEBUG_OBJECT (self, "Start");
344
345   for (guint i = 0; i < G_N_ELEMENTS (priv->alloc); i++) {
346     GstD3D11Allocator *alloc = priv->alloc[i];
347
348     if (!alloc)
349       break;
350
351     if (!gst_d3d11_allocator_set_active (alloc, TRUE)) {
352       GST_ERROR_OBJECT (self, "Failed to activate allocator");
353       return FALSE;
354     }
355   }
356
357   ret = GST_BUFFER_POOL_CLASS (parent_class)->start (pool);
358   if (!ret) {
359     GST_ERROR_OBJECT (self, "Failed to start");
360
361     for (guint i = 0; i < G_N_ELEMENTS (priv->alloc); i++) {
362       GstD3D11Allocator *alloc = priv->alloc[i];
363
364       if (!alloc)
365         break;
366
367       gst_d3d11_allocator_set_active (alloc, FALSE);
368     }
369
370     return FALSE;
371   }
372
373   return TRUE;
374 }
375
376 static gboolean
377 gst_d3d11_staging_buffer_pool_stop (GstBufferPool * pool)
378 {
379   GstD3D11StagingBufferPool *self = GST_D3D11_STAGING_BUFFER_POOL (pool);
380   GstD3D11StagingBufferPoolPrivate *priv = self->priv;
381
382   GST_DEBUG_OBJECT (self, "Stop");
383
384   for (guint i = 0; i < G_N_ELEMENTS (priv->alloc); i++) {
385     GstD3D11Allocator *alloc = priv->alloc[i];
386
387     if (!alloc)
388       break;
389
390     if (!gst_d3d11_allocator_set_active (alloc, FALSE)) {
391       GST_ERROR_OBJECT (self, "Failed to deactivate allocator");
392       return FALSE;
393     }
394   }
395
396   return GST_BUFFER_POOL_CLASS (parent_class)->stop (pool);
397 }
398
399 /**
400  * gst_d3d11_staging_buffer_pool_new:
401  * @device: a #GstD3D11Device to use
402  *
403  * Returns: a #GstBufferPool that allocates buffers with #GstD3D11Memory
404  * which hold Direct3D11 staging texture allocated with D3D11_USAGE_STAGING
405  * flag, instead of D3D11_USAGE_DEFAULT. The staging texture can be used for
406  * optimized resource upload/download.
407  *
408  * Since: 1.22
409  */
410 GstBufferPool *
411 gst_d3d11_staging_buffer_pool_new (GstD3D11Device * device)
412 {
413   GstD3D11StagingBufferPool *pool;
414
415   g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), nullptr);
416
417   pool = (GstD3D11StagingBufferPool *)
418       g_object_new (GST_TYPE_D3D11_STAGING_BUFFER_POOL, nullptr);
419   gst_object_ref_sink (pool);
420
421   pool->device = (GstD3D11Device *) gst_object_ref (device);
422
423   return GST_BUFFER_POOL_CAST (pool);
424 }