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