d3d11: Reorganize format mapping table
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / gst-libs / gst / d3d11 / gstd3d11memory.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 <string.h>
26 #include "gstd3d11memory.h"
27 #include "gstd3d11device.h"
28 #include "gstd3d11utils.h"
29 #include "gstd3d11_private.h"
30
31 GST_DEBUG_CATEGORY_STATIC (gst_d3d11_allocator_debug);
32 #define GST_CAT_DEFAULT gst_d3d11_allocator_debug
33
34 static GstAllocator *_d3d11_memory_allocator;
35
36 /* GstD3D11AllocationParams */
37 static void gst_d3d11_allocation_params_init (GType type);
38 G_DEFINE_BOXED_TYPE_WITH_CODE (GstD3D11AllocationParams,
39     gst_d3d11_allocation_params,
40     (GBoxedCopyFunc) gst_d3d11_allocation_params_copy,
41     (GBoxedFreeFunc) gst_d3d11_allocation_params_free,
42     gst_d3d11_allocation_params_init (g_define_type_id));
43
44 /**
45  * gst_d3d11_allocation_params_new:
46  * @device: a #GstD3D11Device
47  * @info: a #GstVideoInfo
48  * @flags: a #GstD3D11AllocationFlags
49  * @bind_flags: D3D11_BIND_FLAG value used for creating Direct3D11 texture
50  *
51  * Create #GstD3D11AllocationParams object which is used by #GstD3D11BufferPool
52  * and #GstD3D11Allocator in order to allocate new ID3D11Texture2D
53  * object with given configuration
54  *
55  * Returns: a #GstD3D11AllocationParams or %NULL if @info is not supported
56  *
57  * Since: 1.20
58  */
59 GstD3D11AllocationParams *
60 gst_d3d11_allocation_params_new (GstD3D11Device * device, GstVideoInfo * info,
61     GstD3D11AllocationFlags flags, guint bind_flags)
62 {
63   GstD3D11AllocationParams *ret;
64   GstD3D11Format d3d11_format;
65   guint i;
66
67   g_return_val_if_fail (info != NULL, NULL);
68
69   if (!gst_d3d11_device_get_format (device, GST_VIDEO_INFO_FORMAT (info),
70           &d3d11_format)) {
71     GST_WARNING ("Couldn't get d3d11 format");
72     return NULL;
73   }
74
75   ret = g_new0 (GstD3D11AllocationParams, 1);
76
77   ret->info = *info;
78   ret->aligned_info = *info;
79   ret->d3d11_format = d3d11_format;
80
81   /* Usage Flag
82    * https://docs.microsoft.com/en-us/windows/win32/api/d3d11/ne-d3d11-d3d11_usage
83    *
84    * +----------------------------------------------------------+
85    * | Resource Usage | Default | Dynamic | Immutable | Staging |
86    * +----------------+---------+---------+-----------+---------+
87    * | GPU-Read       | Yes     | Yes     | Yes       | Yes     |
88    * | GPU-Write      | Yes     |         |           | Yes     |
89    * | CPU-Read       |         |         |           | Yes     |
90    * | CPU-Write      |         | Yes     |           | Yes     |
91    * +----------------------------------------------------------+
92    */
93
94   /* If corresponding dxgi format is undefined, use resource format instead */
95   if (d3d11_format.dxgi_format == DXGI_FORMAT_UNKNOWN) {
96     for (i = 0; i < GST_VIDEO_INFO_N_PLANES (info); i++) {
97       g_assert (d3d11_format.resource_format[i] != DXGI_FORMAT_UNKNOWN);
98
99       ret->desc[i].Width = GST_VIDEO_INFO_COMP_WIDTH (info, i);
100       ret->desc[i].Height = GST_VIDEO_INFO_COMP_HEIGHT (info, i);
101       ret->desc[i].MipLevels = 1;
102       ret->desc[i].ArraySize = 1;
103       ret->desc[i].Format = d3d11_format.resource_format[i];
104       ret->desc[i].SampleDesc.Count = 1;
105       ret->desc[i].SampleDesc.Quality = 0;
106       ret->desc[i].Usage = D3D11_USAGE_DEFAULT;
107       ret->desc[i].BindFlags = bind_flags;
108     }
109   } else {
110     ret->desc[0].Width = GST_VIDEO_INFO_WIDTH (info);
111     ret->desc[0].Height = GST_VIDEO_INFO_HEIGHT (info);
112     ret->desc[0].MipLevels = 1;
113     ret->desc[0].ArraySize = 1;
114     ret->desc[0].Format = d3d11_format.dxgi_format;
115     ret->desc[0].SampleDesc.Count = 1;
116     ret->desc[0].SampleDesc.Quality = 0;
117     ret->desc[0].Usage = D3D11_USAGE_DEFAULT;
118     ret->desc[0].BindFlags = bind_flags;
119   }
120
121   ret->flags = flags;
122
123   return ret;
124 }
125
126 /**
127  * gst_d3d11_allocation_params_alignment:
128  * @params: a #GstD3D11AllocationParams
129  * @align: a #GstVideoAlignment
130  *
131  * Adjust Width and Height fields of D3D11_TEXTURE2D_DESC with given
132  * @align
133  *
134  * Returns: %TRUE if alignment could be applied
135  *
136  * Since: 1.20
137  */
138 gboolean
139 gst_d3d11_allocation_params_alignment (GstD3D11AllocationParams * params,
140     GstVideoAlignment * align)
141 {
142   guint i;
143   guint padding_width, padding_height;
144   GstVideoInfo *info;
145   GstVideoInfo new_info;
146
147   g_return_val_if_fail (params != NULL, FALSE);
148   g_return_val_if_fail (align != NULL, FALSE);
149
150   /* d3d11 does not support stride align. Consider padding only */
151   padding_width = align->padding_left + align->padding_right;
152   padding_height = align->padding_top + align->padding_bottom;
153
154   info = &params->info;
155
156   if (!gst_video_info_set_format (&new_info, GST_VIDEO_INFO_FORMAT (info),
157           GST_VIDEO_INFO_WIDTH (info) + padding_width,
158           GST_VIDEO_INFO_HEIGHT (info) + padding_height)) {
159     GST_WARNING ("Set format fail");
160     return FALSE;
161   }
162
163   params->aligned_info = new_info;
164
165   for (i = 0; i < GST_VIDEO_INFO_N_PLANES (info); i++) {
166     params->desc[i].Width = GST_VIDEO_INFO_COMP_WIDTH (&new_info, i);
167     params->desc[i].Height = GST_VIDEO_INFO_COMP_HEIGHT (&new_info, i);
168   }
169
170   return TRUE;
171 }
172
173 /**
174  * gst_d3d11_allocation_params_copy:
175  * @src: a #GstD3D11AllocationParams
176  *
177  * Returns: a copy of @src
178  *
179  * Since: 1.20
180  */
181 GstD3D11AllocationParams *
182 gst_d3d11_allocation_params_copy (GstD3D11AllocationParams * src)
183 {
184   GstD3D11AllocationParams *dst;
185
186   g_return_val_if_fail (src != NULL, NULL);
187
188   dst = g_new0 (GstD3D11AllocationParams, 1);
189   memcpy (dst, src, sizeof (GstD3D11AllocationParams));
190
191   return dst;
192 }
193
194 /**
195  * gst_d3d11_allocation_params_free:
196  * @params: a #GstD3D11AllocationParams
197  *
198  * Free @params
199  *
200  * Since: 1.20
201  */
202 void
203 gst_d3d11_allocation_params_free (GstD3D11AllocationParams * params)
204 {
205   g_free (params);
206 }
207
208 static gint
209 gst_d3d11_allocation_params_compare (const GstD3D11AllocationParams * p1,
210     const GstD3D11AllocationParams * p2)
211 {
212   g_return_val_if_fail (p1 != NULL, -1);
213   g_return_val_if_fail (p2 != NULL, -1);
214
215   if (p1 == p2)
216     return 0;
217
218   return -1;
219 }
220
221 static void
222 gst_d3d11_allocation_params_init (GType type)
223 {
224   static GstValueTable table = {
225     0, (GstValueCompareFunc) gst_d3d11_allocation_params_compare,
226     NULL, NULL
227   };
228
229   table.type = type;
230   gst_value_register (&table);
231 }
232
233 /* GstD3D11Memory */
234 #define GST_D3D11_MEMORY_GET_LOCK(m) (&(GST_D3D11_MEMORY_CAST(m)->priv->lock))
235 #define GST_D3D11_MEMORY_LOCK(m) G_STMT_START { \
236   GST_TRACE("Locking %p from thread %p", (m), g_thread_self()); \
237   g_mutex_lock(GST_D3D11_MEMORY_GET_LOCK(m)); \
238   GST_TRACE("Locked %p from thread %p", (m), g_thread_self()); \
239 } G_STMT_END
240
241 #define GST_D3D11_MEMORY_UNLOCK(m) G_STMT_START { \
242   GST_TRACE("Unlocking %p from thread %p", (m), g_thread_self()); \
243   g_mutex_unlock(GST_D3D11_MEMORY_GET_LOCK(m)); \
244 } G_STMT_END
245
246 struct _GstD3D11MemoryPrivate
247 {
248   ID3D11Texture2D *texture;
249   ID3D11Buffer *buffer;
250
251   ID3D11Resource *staging;
252
253   D3D11_TEXTURE2D_DESC desc;
254   D3D11_BUFFER_DESC buffer_desc;
255
256   guint subresource_index;
257
258   ID3D11ShaderResourceView *shader_resource_view[GST_VIDEO_MAX_PLANES];
259   guint num_shader_resource_views;
260
261   ID3D11RenderTargetView *render_target_view[GST_VIDEO_MAX_PLANES];
262   guint num_render_target_views;
263
264   ID3D11VideoDecoderOutputView *decoder_output_view;
265   ID3D11VideoProcessorInputView *processor_input_view;
266   ID3D11VideoProcessorOutputView *processor_output_view;
267
268   D3D11_MAPPED_SUBRESOURCE map;
269
270   GstD3D11MemoryNativeType native_type;
271
272   GMutex lock;
273   gint cpu_map_count;
274 };
275
276 GST_DEFINE_MINI_OBJECT_TYPE (GstD3D11Memory, gst_d3d11_memory);
277
278 static inline D3D11_MAP
279 gst_d3d11_map_flags_to_d3d11 (GstMapFlags flags)
280 {
281   if ((flags & GST_MAP_READWRITE) == GST_MAP_READWRITE)
282     return D3D11_MAP_READ_WRITE;
283   else if ((flags & GST_MAP_WRITE) == GST_MAP_WRITE)
284     return D3D11_MAP_WRITE;
285   else if ((flags & GST_MAP_READ) == GST_MAP_READ)
286     return D3D11_MAP_READ;
287   else
288     g_assert_not_reached ();
289
290   return D3D11_MAP_READ;
291 }
292
293 static ID3D11Texture2D *
294 gst_d3d11_allocate_staging_texture (GstD3D11Device * device,
295     const D3D11_TEXTURE2D_DESC * ref)
296 {
297   D3D11_TEXTURE2D_DESC desc = { 0, };
298   ID3D11Texture2D *texture = NULL;
299   ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device);
300   HRESULT hr;
301
302   desc.Width = ref->Width;
303   desc.Height = ref->Height;
304   desc.MipLevels = 1;
305   desc.Format = ref->Format;
306   desc.SampleDesc.Count = 1;
307   desc.ArraySize = 1;
308   desc.Usage = D3D11_USAGE_STAGING;
309   desc.CPUAccessFlags = (D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE);
310
311   hr = device_handle->CreateTexture2D (&desc, NULL, &texture);
312   if (!gst_d3d11_result (hr, device)) {
313     GST_ERROR_OBJECT (device, "Failed to create texture");
314     return NULL;
315   }
316
317   return texture;
318 }
319
320 /* Must be called with d3d11 device lock */
321 static gboolean
322 gst_d3d11_memory_map_cpu_access (GstD3D11Memory * dmem, D3D11_MAP map_type)
323 {
324   GstD3D11MemoryPrivate *priv = dmem->priv;
325   HRESULT hr;
326   ID3D11DeviceContext *device_context =
327       gst_d3d11_device_get_device_context_handle (dmem->device);
328
329   hr = device_context->Map (priv->staging, 0, map_type, 0, &priv->map);
330
331   if (!gst_d3d11_result (hr, dmem->device)) {
332     GST_ERROR_OBJECT (GST_MEMORY_CAST (dmem)->allocator,
333         "Failed to map staging texture (0x%x)", (guint) hr);
334     return FALSE;
335   }
336
337   return TRUE;
338 }
339
340 /* Must be called with d3d11 device lock */
341 static void
342 gst_d3d11_memory_upload (GstD3D11Memory * dmem)
343 {
344   GstD3D11MemoryPrivate *priv = dmem->priv;
345   ID3D11DeviceContext *device_context;
346
347   if (!priv->staging || priv->staging == priv->texture ||
348       !GST_MEMORY_FLAG_IS_SET (dmem, GST_D3D11_MEMORY_TRANSFER_NEED_UPLOAD))
349     return;
350
351   device_context = gst_d3d11_device_get_device_context_handle (dmem->device);
352   device_context->CopySubresourceRegion (priv->texture, priv->subresource_index,
353       0, 0, 0, priv->staging, 0, NULL);
354 }
355
356 /* Must be called with d3d11 device lock */
357 static void
358 gst_d3d11_memory_download (GstD3D11Memory * dmem)
359 {
360   GstD3D11MemoryPrivate *priv = dmem->priv;
361   ID3D11DeviceContext *device_context;
362
363   if (!priv->staging || priv->staging == priv->texture ||
364       !GST_MEMORY_FLAG_IS_SET (dmem, GST_D3D11_MEMORY_TRANSFER_NEED_DOWNLOAD))
365     return;
366
367   device_context = gst_d3d11_device_get_device_context_handle (dmem->device);
368   device_context->CopySubresourceRegion (priv->staging, 0, 0, 0, 0,
369       priv->texture, priv->subresource_index, NULL);
370 }
371
372 static gpointer
373 gst_d3d11_memory_map_full (GstMemory * mem, GstMapInfo * info, gsize maxsize)
374 {
375   GstD3D11Memory *dmem = GST_D3D11_MEMORY_CAST (mem);
376   GstD3D11MemoryPrivate *priv = dmem->priv;
377   GstMapFlags flags = info->flags;
378   gpointer ret = NULL;
379
380   gst_d3d11_device_lock (dmem->device);
381   GST_D3D11_MEMORY_LOCK (dmem);
382
383   memset (info->user_data, 0, sizeof (info->user_data));
384   info->user_data[0] = GUINT_TO_POINTER (dmem->priv->subresource_index);
385
386   if ((flags & GST_MAP_D3D11) == GST_MAP_D3D11) {
387     if (priv->native_type == GST_D3D11_MEMORY_NATIVE_TYPE_BUFFER) {
388       /* FIXME: handle non-staging buffer */
389       g_assert (priv->buffer != nullptr);
390       ret = priv->buffer;
391     } else {
392       gst_d3d11_memory_upload (dmem);
393       GST_MEMORY_FLAG_UNSET (dmem, GST_D3D11_MEMORY_TRANSFER_NEED_UPLOAD);
394
395       if ((flags & GST_MAP_WRITE) == GST_MAP_WRITE)
396         GST_MINI_OBJECT_FLAG_SET (dmem,
397             GST_D3D11_MEMORY_TRANSFER_NEED_DOWNLOAD);
398
399       g_assert (priv->texture != NULL);
400       ret = priv->texture;
401       goto out;
402     }
403   }
404
405   if (priv->cpu_map_count == 0) {
406     D3D11_MAP map_type;
407
408     /* FIXME: handle non-staging buffer */
409     if (priv->native_type == GST_D3D11_MEMORY_NATIVE_TYPE_TEXTURE_2D) {
410       /* Allocate staging texture for CPU access */
411       if (!priv->staging) {
412         priv->staging = gst_d3d11_allocate_staging_texture (dmem->device,
413             &priv->desc);
414         if (!priv->staging) {
415           GST_ERROR_OBJECT (mem->allocator, "Couldn't create staging texture");
416           goto out;
417         }
418
419         /* first memory, always need download to staging */
420         GST_MINI_OBJECT_FLAG_SET (mem, GST_D3D11_MEMORY_TRANSFER_NEED_DOWNLOAD);
421       }
422
423       gst_d3d11_memory_download (dmem);
424     }
425
426     map_type = gst_d3d11_map_flags_to_d3d11 (flags);
427     if (!gst_d3d11_memory_map_cpu_access (dmem, map_type)) {
428       GST_ERROR_OBJECT (mem->allocator, "Couldn't map staging texture");
429       goto out;
430     }
431   }
432
433   if ((flags & GST_MAP_WRITE) == GST_MAP_WRITE) {
434     GST_MINI_OBJECT_FLAG_SET (mem, GST_D3D11_MEMORY_TRANSFER_NEED_UPLOAD);
435   }
436
437   GST_MEMORY_FLAG_UNSET (mem, GST_D3D11_MEMORY_TRANSFER_NEED_DOWNLOAD);
438
439   priv->cpu_map_count++;
440   ret = dmem->priv->map.pData;
441
442 out:
443   GST_D3D11_MEMORY_UNLOCK (dmem);
444   gst_d3d11_device_unlock (dmem->device);
445
446   return ret;
447 }
448
449 /* Must be called with d3d11 device lock */
450 static void
451 gst_d3d11_memory_unmap_cpu_access (GstD3D11Memory * dmem)
452 {
453   GstD3D11MemoryPrivate *priv = dmem->priv;
454   ID3D11DeviceContext *device_context =
455       gst_d3d11_device_get_device_context_handle (dmem->device);
456
457   device_context->Unmap (priv->staging, 0);
458 }
459
460 static void
461 gst_d3d11_memory_unmap_full (GstMemory * mem, GstMapInfo * info)
462 {
463   GstD3D11Memory *dmem = GST_D3D11_MEMORY_CAST (mem);
464   GstD3D11MemoryPrivate *priv = dmem->priv;
465
466   gst_d3d11_device_lock (dmem->device);
467   GST_D3D11_MEMORY_LOCK (dmem);
468
469   if ((info->flags & GST_MAP_D3D11) == GST_MAP_D3D11) {
470     if ((info->flags & GST_MAP_WRITE) == GST_MAP_WRITE)
471       GST_MINI_OBJECT_FLAG_SET (mem, GST_D3D11_MEMORY_TRANSFER_NEED_DOWNLOAD);
472
473     goto out;
474   }
475
476   if ((info->flags & GST_MAP_WRITE) == GST_MAP_WRITE)
477     GST_MINI_OBJECT_FLAG_SET (mem, GST_D3D11_MEMORY_TRANSFER_NEED_UPLOAD);
478
479   priv->cpu_map_count--;
480   if (priv->cpu_map_count > 0)
481     goto out;
482
483   gst_d3d11_memory_unmap_cpu_access (dmem);
484
485 out:
486   GST_D3D11_MEMORY_UNLOCK (dmem);
487   gst_d3d11_device_unlock (dmem->device);
488 }
489
490 static GstMemory *
491 gst_d3d11_memory_share (GstMemory * mem, gssize offset, gssize size)
492 {
493   /* TODO: impl. */
494   return NULL;
495 }
496
497 static gboolean
498 gst_d3d11_memory_update_size (GstMemory * mem)
499 {
500   GstD3D11Memory *dmem = GST_D3D11_MEMORY_CAST (mem);
501   GstD3D11MemoryPrivate *priv = dmem->priv;
502   gsize offset[GST_VIDEO_MAX_PLANES];
503   gint stride[GST_VIDEO_MAX_PLANES];
504   gsize size;
505   D3D11_TEXTURE2D_DESC *desc = &priv->desc;
506   gboolean ret = FALSE;
507
508   if (!priv->staging) {
509     priv->staging = gst_d3d11_allocate_staging_texture (dmem->device,
510         &priv->desc);
511     if (!priv->staging) {
512       GST_ERROR_OBJECT (mem->allocator, "Couldn't create staging texture");
513       return FALSE;
514     }
515
516     GST_MINI_OBJECT_FLAG_SET (mem, GST_D3D11_MEMORY_TRANSFER_NEED_DOWNLOAD);
517   }
518
519   gst_d3d11_device_lock (dmem->device);
520   if (!gst_d3d11_memory_map_cpu_access (dmem, D3D11_MAP_READ_WRITE)) {
521     GST_ERROR_OBJECT (mem->allocator, "Couldn't map staging texture");
522     return FALSE;
523   }
524
525   gst_d3d11_memory_unmap_cpu_access (dmem);
526
527   if (!gst_d3d11_dxgi_format_get_size (desc->Format, desc->Width, desc->Height,
528           priv->map.RowPitch, offset, stride, &size)) {
529     GST_ERROR_OBJECT (mem->allocator, "Couldn't calculate memory size");
530     goto out;
531   }
532
533   mem->maxsize = mem->size = size;
534   ret = TRUE;
535
536 out:
537   gst_d3d11_device_unlock (dmem->device);
538   return ret;
539 }
540
541 /**
542  * gst_is_d3d11_memory:
543  * @mem: a #GstMemory
544  *
545  * Returns: whether @mem is a #GstD3D11Memory
546  *
547  * Since: 1.20
548  */
549 gboolean
550 gst_is_d3d11_memory (GstMemory * mem)
551 {
552   return mem != NULL && mem->allocator != NULL &&
553       (GST_IS_D3D11_ALLOCATOR (mem->allocator) ||
554       GST_IS_D3D11_POOL_ALLOCATOR (mem->allocator));
555 }
556
557 /**
558  * gst_d3d11_memory_get_native_type:
559  * @mem: a #GstD3D11Memory
560  *
561  * Returns: a #GstD3D11MemoryNativeType
562  *
563  * Since: 1.22
564  */
565 GstD3D11MemoryNativeType
566 gst_d3d11_memory_get_native_type (GstD3D11Memory * mem)
567 {
568   if (!gst_is_d3d11_memory (GST_MEMORY_CAST (mem)))
569     return GST_D3D11_MEMORY_NATIVE_TYPE_INVALID;
570
571   return mem->priv->native_type;
572 }
573
574 /**
575  * gst_d3d11_memory_init_once:
576  *
577  * Initializes the Direct3D11 Texture allocator. It is safe to call
578  * this function multiple times. This must be called before any other
579  * GstD3D11Memory operation.
580  *
581  * Since: 1.20
582  */
583 void
584 gst_d3d11_memory_init_once (void)
585 {
586   static gsize _init = 0;
587
588   if (g_once_init_enter (&_init)) {
589
590     GST_DEBUG_CATEGORY_INIT (gst_d3d11_allocator_debug, "d3d11allocator", 0,
591         "Direct3D11 Texture Allocator");
592
593     _d3d11_memory_allocator =
594         (GstAllocator *) g_object_new (GST_TYPE_D3D11_ALLOCATOR, NULL);
595     gst_object_ref_sink (_d3d11_memory_allocator);
596
597     gst_allocator_register (GST_D3D11_MEMORY_NAME, _d3d11_memory_allocator);
598     g_once_init_leave (&_init, 1);
599   }
600 }
601
602 /**
603  * gst_d3d11_memory_get_resource_handle:
604  * @mem: a #GstD3D11Memory
605  *
606  * Returns: (transfer none): a ID3D11Resource handle. Caller must not release
607  * returned handle.
608  *
609  * Since: 1.22
610  */
611 ID3D11Resource *
612 gst_d3d11_memory_get_resource_handle (GstD3D11Memory * mem)
613 {
614   g_return_val_if_fail (gst_is_d3d11_memory (GST_MEMORY_CAST (mem)), NULL);
615
616   switch (mem->priv->native_type) {
617     case GST_D3D11_MEMORY_NATIVE_TYPE_BUFFER:
618       return mem->priv->buffer;
619     case GST_D3D11_MEMORY_NATIVE_TYPE_TEXTURE_2D:
620       return mem->priv->texture;
621     default:
622       break;
623   }
624
625   return nullptr;
626 }
627
628 /**
629  * gst_d3d11_memory_get_subresource_index:
630  * @mem: a #GstD3D11Memory
631  *
632  * Returns: subresource index corresponding to @mem.
633  *
634  * Since: 1.20
635  */
636 guint
637 gst_d3d11_memory_get_subresource_index (GstD3D11Memory * mem)
638 {
639   g_return_val_if_fail (gst_is_d3d11_memory (GST_MEMORY_CAST (mem)), 0);
640
641   if (mem->priv->native_type != GST_D3D11_MEMORY_NATIVE_TYPE_TEXTURE_2D)
642     return 0;
643
644   return mem->priv->subresource_index;
645 }
646
647 /**
648  * gst_d3d11_memory_get_texture_desc:
649  * @mem: a #GstD3D11Memory
650  * @desc: (out): a D3D11_TEXTURE2D_DESC
651  *
652  * Fill @desc with D3D11_TEXTURE2D_DESC for ID3D11Texture2D
653  *
654  * Returns: %TRUE if successeed
655  *
656  * Since: 1.20
657  */
658 gboolean
659 gst_d3d11_memory_get_texture_desc (GstD3D11Memory * mem,
660     D3D11_TEXTURE2D_DESC * desc)
661 {
662   g_return_val_if_fail (gst_is_d3d11_memory (GST_MEMORY_CAST (mem)), FALSE);
663   g_return_val_if_fail (desc != NULL, FALSE);
664
665   if (mem->priv->native_type != GST_D3D11_MEMORY_NATIVE_TYPE_TEXTURE_2D)
666     return FALSE;
667
668   *desc = mem->priv->desc;
669
670   return TRUE;
671 }
672
673 /**
674  * gst_d3d11_memory_get_buffer_desc:
675  * @mem: a #GstD3D11Memory
676  * @desc: (out): a D3D11_BUFFER_DESC
677  *
678  * Fill @desc with D3D11_BUFFER_DESC for ID3D11Buffer
679  *
680  * Returns: %TRUE if successeed
681  *
682  * Since: 1.22
683  */
684 gboolean
685 gst_d3d11_memory_get_buffer_desc (GstD3D11Memory * mem,
686     D3D11_BUFFER_DESC * desc)
687 {
688   g_return_val_if_fail (gst_is_d3d11_memory (GST_MEMORY_CAST (mem)), FALSE);
689   g_return_val_if_fail (desc != NULL, FALSE);
690
691   if (mem->priv->native_type != GST_D3D11_MEMORY_NATIVE_TYPE_BUFFER)
692     return FALSE;
693
694   *desc = mem->priv->buffer_desc;
695
696   return TRUE;
697 }
698
699 /**
700  * gst_d3d11_memory_get_resource_stride:
701  * @mem: a #GstD3D11Memory
702  * @stride: (out): stride of resource
703  *
704  * Returns: %TRUE if successeed
705  *
706  * Since: 1.22
707  */
708 gboolean
709 gst_d3d11_memory_get_resource_stride (GstD3D11Memory * mem, guint * stride)
710 {
711   g_return_val_if_fail (gst_is_d3d11_memory (GST_MEMORY_CAST (mem)), FALSE);
712   g_return_val_if_fail (stride != NULL, FALSE);
713
714   *stride = mem->priv->map.RowPitch;
715
716   return TRUE;
717 }
718
719 static gboolean
720 create_shader_resource_views (GstD3D11Memory * mem)
721 {
722   GstD3D11MemoryPrivate *priv = mem->priv;
723   guint i;
724   HRESULT hr;
725   guint num_views = 0;
726   ID3D11Device *device_handle;
727   D3D11_SHADER_RESOURCE_VIEW_DESC resource_desc;
728   DXGI_FORMAT formats[GST_VIDEO_MAX_PLANES] = { DXGI_FORMAT_UNKNOWN, };
729
730   memset (&resource_desc, 0, sizeof (D3D11_SHADER_RESOURCE_VIEW_DESC));
731
732   device_handle = gst_d3d11_device_get_device_handle (mem->device);
733
734   num_views = gst_d3d11_dxgi_format_get_resource_format (priv->desc.Format,
735       formats);
736   if (!num_views) {
737     GST_ERROR_OBJECT (GST_MEMORY_CAST (mem)->allocator,
738         "Unknown resource formats for DXGI format %s (%d)",
739         gst_d3d11_dxgi_format_to_string (priv->desc.Format), priv->desc.Format);
740     return FALSE;
741   }
742
743   if ((priv->desc.BindFlags & D3D11_BIND_SHADER_RESOURCE) != 0) {
744     resource_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
745     resource_desc.Texture2D.MipLevels = 1;
746
747     for (i = 0; i < num_views; i++) {
748       resource_desc.Format = formats[i];
749       hr = device_handle->CreateShaderResourceView (priv->texture,
750           &resource_desc, &priv->shader_resource_view[i]);
751
752       if (!gst_d3d11_result (hr, mem->device)) {
753         GST_ERROR_OBJECT (GST_MEMORY_CAST (mem)->allocator,
754             "Failed to create resource DXGI format %s (%d) for plane %d"
755             " view (0x%x)", gst_d3d11_dxgi_format_to_string (formats[i]),
756             formats[i], i, (guint) hr);
757         goto error;
758       }
759     }
760
761     priv->num_shader_resource_views = num_views;
762
763     return TRUE;
764   }
765
766   return FALSE;
767
768 error:
769   for (i = 0; i < num_views; i++)
770     GST_D3D11_CLEAR_COM (priv->shader_resource_view[i]);
771
772   priv->num_shader_resource_views = 0;
773
774   return FALSE;
775 }
776
777 static gboolean
778 gst_d3d11_memory_ensure_shader_resource_view (GstD3D11Memory * mem)
779 {
780   GstD3D11MemoryPrivate *priv = mem->priv;
781   gboolean ret = FALSE;
782
783   if (mem->priv->native_type != GST_D3D11_MEMORY_NATIVE_TYPE_TEXTURE_2D)
784     return FALSE;
785
786   if (!(priv->desc.BindFlags & D3D11_BIND_SHADER_RESOURCE)) {
787     GST_LOG_OBJECT (GST_MEMORY_CAST (mem)->allocator,
788         "Need BindFlags, current flag 0x%x", priv->desc.BindFlags);
789     return FALSE;
790   }
791
792   GST_D3D11_MEMORY_LOCK (mem);
793   if (priv->num_shader_resource_views) {
794     ret = TRUE;
795     goto done;
796   }
797
798   ret = create_shader_resource_views (mem);
799
800 done:
801   GST_D3D11_MEMORY_UNLOCK (mem);
802
803   return ret;
804 }
805
806 /**
807  * gst_d3d11_memory_get_shader_resource_view_size:
808  * @mem: a #GstD3D11Memory
809  *
810  * Returns: the number of ID3D11ShaderResourceView that can be used
811  * for processing GPU operation with @mem
812  *
813  * Since: 1.20
814  */
815 guint
816 gst_d3d11_memory_get_shader_resource_view_size (GstD3D11Memory * mem)
817 {
818   g_return_val_if_fail (gst_is_d3d11_memory (GST_MEMORY_CAST (mem)), 0);
819
820   if (!gst_d3d11_memory_ensure_shader_resource_view (mem))
821     return 0;
822
823   return mem->priv->num_shader_resource_views;
824 }
825
826 /**
827  * gst_d3d11_memory_get_shader_resource_view:
828  * @mem: a #GstD3D11Memory
829  * @index: the index of the ID3D11ShaderResourceView
830  *
831  * Returns: (transfer none) (nullable): a pointer to the
832  * ID3D11ShaderResourceView or %NULL if ID3D11ShaderResourceView is unavailable
833  * for @index
834  *
835  * Since: 1.20
836  */
837 ID3D11ShaderResourceView *
838 gst_d3d11_memory_get_shader_resource_view (GstD3D11Memory * mem, guint index)
839 {
840   GstD3D11MemoryPrivate *priv;
841
842   g_return_val_if_fail (gst_is_d3d11_memory (GST_MEMORY_CAST (mem)), NULL);
843
844   if (!gst_d3d11_memory_ensure_shader_resource_view (mem))
845     return NULL;
846
847   priv = mem->priv;
848
849   if (index >= priv->num_shader_resource_views) {
850     GST_ERROR ("Invalid SRV index %d", index);
851     return NULL;
852   }
853
854   return priv->shader_resource_view[index];
855 }
856
857 static gboolean
858 create_render_target_views (GstD3D11Memory * mem)
859 {
860   GstD3D11MemoryPrivate *priv = mem->priv;
861   guint i;
862   HRESULT hr;
863   guint num_views = 0;
864   ID3D11Device *device_handle;
865   D3D11_RENDER_TARGET_VIEW_DESC render_desc;
866   DXGI_FORMAT formats[GST_VIDEO_MAX_PLANES] = { DXGI_FORMAT_UNKNOWN, };
867
868   memset (&render_desc, 0, sizeof (D3D11_RENDER_TARGET_VIEW_DESC));
869
870   device_handle = gst_d3d11_device_get_device_handle (mem->device);
871
872   num_views = gst_d3d11_dxgi_format_get_resource_format (priv->desc.Format,
873       formats);
874   if (!num_views) {
875     GST_ERROR_OBJECT (GST_MEMORY_CAST (mem)->allocator,
876         "Unknown resource formats for DXGI format %s (%d)",
877         gst_d3d11_dxgi_format_to_string (priv->desc.Format), priv->desc.Format);
878     return FALSE;
879   }
880
881   if ((priv->desc.BindFlags & D3D11_BIND_RENDER_TARGET) != 0) {
882     render_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
883     render_desc.Texture2D.MipSlice = 0;
884
885     for (i = 0; i < num_views; i++) {
886       render_desc.Format = formats[i];
887
888       hr = device_handle->CreateRenderTargetView (priv->texture, &render_desc,
889           &priv->render_target_view[i]);
890       if (!gst_d3d11_result (hr, mem->device)) {
891         GST_ERROR_OBJECT (GST_MEMORY_CAST (mem)->allocator,
892             "Failed to create resource DXGI format %s (%d) for plane %d"
893             " view (0x%x)", gst_d3d11_dxgi_format_to_string (formats[i]),
894             formats[i], i, (guint) hr);
895         goto error;
896       }
897     }
898
899     priv->num_render_target_views = num_views;
900
901     return TRUE;
902   }
903
904   return FALSE;
905
906 error:
907   for (i = 0; i < num_views; i++)
908     GST_D3D11_CLEAR_COM (priv->render_target_view[i]);
909
910   priv->num_render_target_views = 0;
911
912   return FALSE;
913 }
914
915 static gboolean
916 gst_d3d11_memory_ensure_render_target_view (GstD3D11Memory * mem)
917 {
918   GstD3D11MemoryPrivate *priv = mem->priv;
919   gboolean ret = FALSE;
920
921   if (mem->priv->native_type != GST_D3D11_MEMORY_NATIVE_TYPE_TEXTURE_2D)
922     return FALSE;
923
924   if (!(priv->desc.BindFlags & D3D11_BIND_RENDER_TARGET)) {
925     GST_WARNING_OBJECT (GST_MEMORY_CAST (mem)->allocator,
926         "Need BindFlags, current flag 0x%x", priv->desc.BindFlags);
927     return FALSE;
928   }
929
930   GST_D3D11_MEMORY_LOCK (mem);
931   if (priv->num_render_target_views) {
932     ret = TRUE;
933     goto done;
934   }
935
936   ret = create_render_target_views (mem);
937
938 done:
939   GST_D3D11_MEMORY_UNLOCK (mem);
940
941   return ret;
942 }
943
944 /**
945  * gst_d3d11_memory_get_render_target_view_size:
946  * @mem: a #GstD3D11Memory
947  *
948  * Returns: the number of ID3D11RenderTargetView that can be used
949  * for processing GPU operation with @mem
950  *
951  * Since: 1.20
952  */
953 guint
954 gst_d3d11_memory_get_render_target_view_size (GstD3D11Memory * mem)
955 {
956   g_return_val_if_fail (gst_is_d3d11_memory (GST_MEMORY_CAST (mem)), 0);
957
958   if (!gst_d3d11_memory_ensure_render_target_view (mem))
959     return 0;
960
961   return mem->priv->num_render_target_views;
962 }
963
964 /**
965  * gst_d3d11_memory_get_render_target_view:
966  * @mem: a #GstD3D11Memory
967  * @index: the index of the ID3D11RenderTargetView
968  *
969  * Returns: (transfer none) (nullable): a pointer to the
970  * ID3D11RenderTargetView or %NULL if ID3D11RenderTargetView is unavailable
971  * for @index
972  *
973  * Since: 1.20
974  */
975 ID3D11RenderTargetView *
976 gst_d3d11_memory_get_render_target_view (GstD3D11Memory * mem, guint index)
977 {
978   GstD3D11MemoryPrivate *priv;
979
980   g_return_val_if_fail (gst_is_d3d11_memory (GST_MEMORY_CAST (mem)), NULL);
981
982   if (!gst_d3d11_memory_ensure_render_target_view (mem))
983     return NULL;
984
985   priv = mem->priv;
986
987   if (index >= priv->num_render_target_views) {
988     GST_ERROR ("Invalid RTV index %d", index);
989     return NULL;
990   }
991
992   return priv->render_target_view[index];
993 }
994
995 static gboolean
996 gst_d3d11_memory_ensure_decoder_output_view (GstD3D11Memory * mem,
997     ID3D11VideoDevice * video_device, GUID * decoder_profile)
998 {
999   GstD3D11MemoryPrivate *dmem_priv = mem->priv;
1000   GstD3D11Allocator *allocator;
1001   D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC desc;
1002   HRESULT hr;
1003   gboolean ret = FALSE;
1004
1005   if (mem->priv->native_type != GST_D3D11_MEMORY_NATIVE_TYPE_TEXTURE_2D)
1006     return FALSE;
1007
1008   allocator = GST_D3D11_ALLOCATOR (GST_MEMORY_CAST (mem)->allocator);
1009
1010   if (!(dmem_priv->desc.BindFlags & D3D11_BIND_DECODER)) {
1011     GST_LOG_OBJECT (allocator,
1012         "Need BindFlags, current flag 0x%x", dmem_priv->desc.BindFlags);
1013     return FALSE;
1014   }
1015
1016   GST_D3D11_MEMORY_LOCK (mem);
1017   if (dmem_priv->decoder_output_view) {
1018     dmem_priv->decoder_output_view->GetDesc (&desc);
1019     if (IsEqualGUID (desc.DecodeProfile, *decoder_profile)) {
1020       goto succeeded;
1021     } else {
1022       /* Shouldn't happen, but try again anyway */
1023       GST_WARNING_OBJECT (allocator,
1024           "Existing view has different decoder profile");
1025       GST_D3D11_CLEAR_COM (dmem_priv->decoder_output_view);
1026     }
1027   }
1028
1029   if (dmem_priv->decoder_output_view)
1030     goto succeeded;
1031
1032   desc.DecodeProfile = *decoder_profile;
1033   desc.ViewDimension = D3D11_VDOV_DIMENSION_TEXTURE2D;
1034   desc.Texture2D.ArraySlice = dmem_priv->subresource_index;
1035
1036   hr = video_device->CreateVideoDecoderOutputView (dmem_priv->texture, &desc,
1037       &dmem_priv->decoder_output_view);
1038   if (!gst_d3d11_result (hr, mem->device)) {
1039     GST_ERROR_OBJECT (allocator,
1040         "Could not create decoder output view, hr: 0x%x", (guint) hr);
1041     goto done;
1042   }
1043
1044 succeeded:
1045   ret = TRUE;
1046
1047 done:
1048   GST_D3D11_MEMORY_UNLOCK (mem);
1049
1050   return ret;
1051 }
1052
1053 /**
1054  * gst_d3d11_memory_get_decoder_output_view:
1055  * @mem: a #GstD3D11Memory
1056  *
1057  * Returns: (transfer none) (nullable): a pointer to the
1058  * ID3D11VideoDecoderOutputView or %NULL if ID3D11VideoDecoderOutputView is
1059  * unavailable
1060  *
1061  * Since: 1.20
1062  */
1063 ID3D11VideoDecoderOutputView *
1064 gst_d3d11_memory_get_decoder_output_view (GstD3D11Memory * mem,
1065     ID3D11VideoDevice * video_device, GUID * decoder_profile)
1066 {
1067   g_return_val_if_fail (gst_is_d3d11_memory (GST_MEMORY_CAST (mem)), NULL);
1068   g_return_val_if_fail (video_device != NULL, NULL);
1069   g_return_val_if_fail (decoder_profile != NULL, NULL);
1070
1071   if (!gst_d3d11_memory_ensure_decoder_output_view (mem,
1072           video_device, decoder_profile))
1073     return NULL;
1074
1075   return mem->priv->decoder_output_view;
1076 }
1077
1078 static gboolean
1079 check_bind_flags_for_processor_input_view (guint bind_flags)
1080 {
1081   static const guint compatible_flags = (D3D11_BIND_DECODER |
1082       D3D11_BIND_VIDEO_ENCODER | D3D11_BIND_RENDER_TARGET |
1083       D3D11_BIND_UNORDERED_ACCESS);
1084
1085   if (bind_flags == 0)
1086     return TRUE;
1087
1088   if ((bind_flags & compatible_flags) != 0)
1089     return TRUE;
1090
1091   return FALSE;
1092 }
1093
1094 static gboolean
1095 gst_d3d11_memory_ensure_processor_input_view (GstD3D11Memory * mem,
1096     ID3D11VideoDevice * video_device,
1097     ID3D11VideoProcessorEnumerator * enumerator)
1098 {
1099   GstD3D11MemoryPrivate *dmem_priv = mem->priv;
1100   GstD3D11Allocator *allocator;
1101   D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC desc;
1102   HRESULT hr;
1103   gboolean ret = FALSE;
1104
1105   if (mem->priv->native_type != GST_D3D11_MEMORY_NATIVE_TYPE_TEXTURE_2D)
1106     return FALSE;
1107
1108   allocator = GST_D3D11_ALLOCATOR (GST_MEMORY_CAST (mem)->allocator);
1109
1110   if (!check_bind_flags_for_processor_input_view (dmem_priv->desc.BindFlags)) {
1111     GST_LOG_OBJECT (allocator,
1112         "Need BindFlags, current flag 0x%x", dmem_priv->desc.BindFlags);
1113     return FALSE;
1114   }
1115
1116   GST_D3D11_MEMORY_LOCK (mem);
1117   if (dmem_priv->processor_input_view)
1118     goto succeeded;
1119
1120   desc.FourCC = 0;
1121   desc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D;
1122   desc.Texture2D.MipSlice = 0;
1123   desc.Texture2D.ArraySlice = dmem_priv->subresource_index;
1124
1125   hr = video_device->CreateVideoProcessorInputView (dmem_priv->texture,
1126       enumerator, &desc, &dmem_priv->processor_input_view);
1127   if (!gst_d3d11_result (hr, mem->device)) {
1128     GST_ERROR_OBJECT (allocator,
1129         "Could not create processor input view, hr: 0x%x", (guint) hr);
1130     goto done;
1131   }
1132
1133 succeeded:
1134   ret = TRUE;
1135
1136 done:
1137   GST_D3D11_MEMORY_UNLOCK (mem);
1138
1139   return ret;
1140 }
1141
1142 /**
1143  * gst_d3d11_memory_get_processor_input_view:
1144  * @mem: a #GstD3D11Memory
1145  * @video_device: a #ID3D11VideoDevice
1146  * @enumerator: a #ID3D11VideoProcessorEnumerator
1147  *
1148  * Returns: (transfer none) (nullable): a pointer to the
1149  * ID3D11VideoProcessorInputView or %NULL if ID3D11VideoProcessorInputView is
1150  * unavailable
1151  *
1152  * Since: 1.20
1153  */
1154 ID3D11VideoProcessorInputView *
1155 gst_d3d11_memory_get_processor_input_view (GstD3D11Memory * mem,
1156     ID3D11VideoDevice * video_device,
1157     ID3D11VideoProcessorEnumerator * enumerator)
1158 {
1159   g_return_val_if_fail (gst_is_d3d11_memory (GST_MEMORY_CAST (mem)), NULL);
1160   g_return_val_if_fail (video_device != NULL, NULL);
1161   g_return_val_if_fail (enumerator != NULL, NULL);
1162
1163   if (!gst_d3d11_memory_ensure_processor_input_view (mem, video_device,
1164           enumerator))
1165     return NULL;
1166
1167   return mem->priv->processor_input_view;
1168 }
1169
1170 static gboolean
1171 gst_d3d11_memory_ensure_processor_output_view (GstD3D11Memory * mem,
1172     ID3D11VideoDevice * video_device,
1173     ID3D11VideoProcessorEnumerator * enumerator)
1174 {
1175   GstD3D11MemoryPrivate *priv = mem->priv;
1176   GstD3D11Allocator *allocator;
1177   D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC desc;
1178   HRESULT hr;
1179   gboolean ret;
1180
1181   if (mem->priv->native_type != GST_D3D11_MEMORY_NATIVE_TYPE_TEXTURE_2D)
1182     return FALSE;
1183
1184   memset (&desc, 0, sizeof (D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC));
1185
1186   allocator = GST_D3D11_ALLOCATOR (GST_MEMORY_CAST (mem)->allocator);
1187
1188   if (!(priv->desc.BindFlags & D3D11_BIND_RENDER_TARGET)) {
1189     GST_LOG_OBJECT (allocator,
1190         "Need BindFlags, current flag 0x%x", priv->desc.BindFlags);
1191     return FALSE;
1192   }
1193
1194   /* FIXME: texture array should be supported at some point */
1195   if (priv->subresource_index != 0) {
1196     GST_FIXME_OBJECT (allocator,
1197         "Texture array is not suppoted for processor output view");
1198     return FALSE;
1199   }
1200
1201   GST_D3D11_MEMORY_LOCK (mem);
1202   if (priv->processor_output_view)
1203     goto succeeded;
1204
1205   desc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D;
1206   desc.Texture2D.MipSlice = 0;
1207
1208   hr = video_device->CreateVideoProcessorOutputView (priv->texture,
1209       enumerator, &desc, &priv->processor_output_view);
1210   if (!gst_d3d11_result (hr, mem->device)) {
1211     GST_ERROR_OBJECT (allocator,
1212         "Could not create processor input view, hr: 0x%x", (guint) hr);
1213     goto done;
1214   }
1215
1216 succeeded:
1217   ret = TRUE;
1218
1219 done:
1220   GST_D3D11_MEMORY_UNLOCK (mem);
1221
1222   return ret;
1223 }
1224
1225 /**
1226  * gst_d3d11_memory_get_processor_output_view:
1227  * @mem: a #GstD3D11Memory
1228  * @video_device: a #ID3D11VideoDevice
1229  * @enumerator: a #ID3D11VideoProcessorEnumerator
1230  *
1231  * Returns: (transfer none) (nullable): a pointer to the
1232  * ID3D11VideoProcessorOutputView or %NULL if ID3D11VideoProcessorOutputView is
1233  * unavailable
1234  *
1235  * Since: 1.20
1236  */
1237 ID3D11VideoProcessorOutputView *
1238 gst_d3d11_memory_get_processor_output_view (GstD3D11Memory * mem,
1239     ID3D11VideoDevice * video_device,
1240     ID3D11VideoProcessorEnumerator * enumerator)
1241 {
1242   g_return_val_if_fail (gst_is_d3d11_memory (GST_MEMORY_CAST (mem)), NULL);
1243   g_return_val_if_fail (video_device != NULL, NULL);
1244   g_return_val_if_fail (enumerator != NULL, NULL);
1245
1246   if (!gst_d3d11_memory_ensure_processor_output_view (mem, video_device,
1247           enumerator))
1248     return NULL;
1249
1250   return mem->priv->processor_output_view;
1251 }
1252
1253 /* GstD3D11Allocator */
1254 struct _GstD3D11AllocatorPrivate
1255 {
1256   GstMemoryCopyFunction fallback_copy;
1257 };
1258
1259 #define gst_d3d11_allocator_parent_class alloc_parent_class
1260 G_DEFINE_TYPE_WITH_PRIVATE (GstD3D11Allocator,
1261     gst_d3d11_allocator, GST_TYPE_ALLOCATOR);
1262
1263 static GstMemory *gst_d3d11_allocator_dummy_alloc (GstAllocator * allocator,
1264     gsize size, GstAllocationParams * params);
1265 static GstMemory *gst_d3d11_allocator_alloc_internal (GstD3D11Allocator * self,
1266     GstD3D11Device * device, const D3D11_TEXTURE2D_DESC * desc);
1267 static void gst_d3d11_allocator_free (GstAllocator * allocator,
1268     GstMemory * mem);
1269
1270 static void
1271 gst_d3d11_allocator_class_init (GstD3D11AllocatorClass * klass)
1272 {
1273   GstAllocatorClass *allocator_class = GST_ALLOCATOR_CLASS (klass);
1274
1275   allocator_class->alloc = gst_d3d11_allocator_dummy_alloc;
1276   allocator_class->free = gst_d3d11_allocator_free;
1277 }
1278
1279 static GstMemory *
1280 gst_d3d11_memory_copy (GstMemory * mem, gssize offset, gssize size)
1281 {
1282   GstD3D11Allocator *alloc = GST_D3D11_ALLOCATOR (mem->allocator);
1283   GstD3D11AllocatorPrivate *priv = alloc->priv;
1284   GstD3D11Memory *dmem = GST_D3D11_MEMORY_CAST (mem);
1285   GstD3D11Memory *copy_dmem;
1286   GstD3D11Device *device = dmem->device;
1287   ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device);
1288   ID3D11DeviceContext *device_context =
1289       gst_d3d11_device_get_device_context_handle (device);
1290   D3D11_TEXTURE2D_DESC dst_desc = { 0, };
1291   D3D11_TEXTURE2D_DESC src_desc = { 0, };
1292   GstMemory *copy = NULL;
1293   GstMapInfo info;
1294   HRESULT hr;
1295   UINT bind_flags = 0;
1296   UINT supported_flags = 0;
1297
1298   if (dmem->priv->native_type != GST_D3D11_MEMORY_NATIVE_TYPE_TEXTURE_2D)
1299     return priv->fallback_copy (mem, offset, size);
1300
1301   /* non-zero offset or different size is not supported */
1302   if (offset != 0 || (size != -1 && (gsize) size != mem->size)) {
1303     GST_DEBUG_OBJECT (alloc, "Different size/offset, try fallback copy");
1304     return priv->fallback_copy (mem, offset, size);
1305   }
1306
1307   gst_d3d11_device_lock (device);
1308   if (!gst_memory_map (mem, &info,
1309           (GstMapFlags) (GST_MAP_READ | GST_MAP_D3D11))) {
1310     gst_d3d11_device_unlock (device);
1311
1312     GST_WARNING_OBJECT (alloc, "Failed to map memory, try fallback copy");
1313
1314     return priv->fallback_copy (mem, offset, size);
1315   }
1316
1317   dmem->priv->texture->GetDesc (&src_desc);
1318   dst_desc.Width = src_desc.Width;
1319   dst_desc.Height = src_desc.Height;
1320   dst_desc.MipLevels = 1;
1321   dst_desc.Format = src_desc.Format;
1322   dst_desc.SampleDesc.Count = 1;
1323   dst_desc.ArraySize = 1;
1324   dst_desc.Usage = D3D11_USAGE_DEFAULT;
1325
1326   /* If supported, use bind flags for SRV/RTV */
1327   hr = device_handle->CheckFormatSupport (src_desc.Format, &supported_flags);
1328   if (gst_d3d11_result (hr, device)) {
1329     if ((supported_flags & D3D11_FORMAT_SUPPORT_SHADER_SAMPLE) ==
1330         D3D11_FORMAT_SUPPORT_SHADER_SAMPLE) {
1331       bind_flags |= D3D11_BIND_SHADER_RESOURCE;
1332     }
1333
1334     if ((supported_flags & D3D11_FORMAT_SUPPORT_RENDER_TARGET) ==
1335         D3D11_FORMAT_SUPPORT_RENDER_TARGET) {
1336       bind_flags |= D3D11_BIND_RENDER_TARGET;
1337     }
1338   }
1339
1340   copy = gst_d3d11_allocator_alloc_internal (alloc, device, &dst_desc);
1341   if (!copy) {
1342     gst_memory_unmap (mem, &info);
1343     gst_d3d11_device_unlock (device);
1344
1345     GST_WARNING_OBJECT (alloc,
1346         "Failed to allocate new d3d11 map memory, try fallback copy");
1347
1348     return priv->fallback_copy (mem, offset, size);
1349   }
1350
1351   copy_dmem = GST_D3D11_MEMORY_CAST (copy);
1352   device_context->CopySubresourceRegion (copy_dmem->priv->texture, 0, 0, 0, 0,
1353       dmem->priv->texture, dmem->priv->subresource_index, NULL);
1354   copy->maxsize = copy->size = mem->maxsize;
1355   gst_memory_unmap (mem, &info);
1356   gst_d3d11_device_unlock (device);
1357
1358   /* newly allocated memory holds valid image data. We need download this
1359    * pixel data into staging memory for CPU access */
1360   GST_MINI_OBJECT_FLAG_SET (mem, GST_D3D11_MEMORY_TRANSFER_NEED_DOWNLOAD);
1361
1362   return copy;
1363 }
1364
1365 static void
1366 gst_d3d11_allocator_init (GstD3D11Allocator * allocator)
1367 {
1368   GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
1369   GstD3D11AllocatorPrivate *priv;
1370
1371   priv = allocator->priv = (GstD3D11AllocatorPrivate *)
1372       gst_d3d11_allocator_get_instance_private (allocator);
1373
1374   alloc->mem_type = GST_D3D11_MEMORY_NAME;
1375   alloc->mem_map_full = gst_d3d11_memory_map_full;
1376   alloc->mem_unmap_full = gst_d3d11_memory_unmap_full;
1377   alloc->mem_share = gst_d3d11_memory_share;
1378
1379   /* Store pointer to default mem_copy method for fallback copy */
1380   priv->fallback_copy = alloc->mem_copy;
1381   alloc->mem_copy = gst_d3d11_memory_copy;
1382
1383   GST_OBJECT_FLAG_SET (alloc, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC);
1384 }
1385
1386 static GstMemory *
1387 gst_d3d11_allocator_dummy_alloc (GstAllocator * allocator, gsize size,
1388     GstAllocationParams * params)
1389 {
1390   g_return_val_if_reached (NULL);
1391 }
1392
1393 static void
1394 gst_d3d11_allocator_free (GstAllocator * allocator, GstMemory * mem)
1395 {
1396   GstD3D11Memory *dmem = GST_D3D11_MEMORY_CAST (mem);
1397   GstD3D11MemoryPrivate *dmem_priv = dmem->priv;
1398   gint i;
1399
1400   GST_LOG_OBJECT (allocator, "Free memory %p", mem);
1401
1402   for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) {
1403     GST_D3D11_CLEAR_COM (dmem_priv->render_target_view[i]);
1404     GST_D3D11_CLEAR_COM (dmem_priv->shader_resource_view[i]);
1405   }
1406
1407   GST_D3D11_CLEAR_COM (dmem_priv->decoder_output_view);
1408   GST_D3D11_CLEAR_COM (dmem_priv->processor_input_view);
1409   GST_D3D11_CLEAR_COM (dmem_priv->processor_output_view);
1410   GST_D3D11_CLEAR_COM (dmem_priv->texture);
1411   GST_D3D11_CLEAR_COM (dmem_priv->staging);
1412   GST_D3D11_CLEAR_COM (dmem_priv->buffer);
1413
1414   gst_clear_object (&dmem->device);
1415   g_mutex_clear (&dmem_priv->lock);
1416   g_free (dmem->priv);
1417   g_free (dmem);
1418 }
1419
1420 static GstMemory *
1421 gst_d3d11_allocator_alloc_wrapped (GstD3D11Allocator * self,
1422     GstD3D11Device * device, const D3D11_TEXTURE2D_DESC * desc,
1423     ID3D11Texture2D * texture)
1424 {
1425   GstD3D11Memory *mem;
1426
1427   mem = g_new0 (GstD3D11Memory, 1);
1428   mem->priv = g_new0 (GstD3D11MemoryPrivate, 1);
1429
1430   gst_memory_init (GST_MEMORY_CAST (mem),
1431       (GstMemoryFlags) 0, GST_ALLOCATOR_CAST (self), NULL, 0, 0, 0, 0);
1432   g_mutex_init (&mem->priv->lock);
1433   mem->priv->texture = texture;
1434   mem->priv->desc = *desc;
1435   mem->priv->native_type = GST_D3D11_MEMORY_NATIVE_TYPE_TEXTURE_2D;
1436   mem->device = (GstD3D11Device *) gst_object_ref (device);
1437
1438   /* This is staging texture as well */
1439   if (desc->Usage == D3D11_USAGE_STAGING) {
1440     mem->priv->staging = texture;
1441     texture->AddRef ();
1442   }
1443
1444   return GST_MEMORY_CAST (mem);
1445 }
1446
1447 static GstMemory *
1448 gst_d3d11_allocator_alloc_internal (GstD3D11Allocator * self,
1449     GstD3D11Device * device, const D3D11_TEXTURE2D_DESC * desc)
1450 {
1451   ID3D11Texture2D *texture = NULL;
1452   ID3D11Device *device_handle;
1453   HRESULT hr;
1454
1455   device_handle = gst_d3d11_device_get_device_handle (device);
1456
1457   hr = device_handle->CreateTexture2D (desc, NULL, &texture);
1458   if (!gst_d3d11_result (hr, device)) {
1459     GST_ERROR_OBJECT (self, "Couldn't create texture");
1460     return NULL;
1461   }
1462
1463   return gst_d3d11_allocator_alloc_wrapped (self, device, desc, texture);
1464 }
1465
1466 /**
1467  * gst_d3d11_allocator_alloc:
1468  * @allocator: a #GstD3D11Allocator
1469  * @device: a #GstD3D11Device
1470  * @desc: a D3D11_TEXTURE2D_DESC struct
1471  *
1472  * Returns: a newly allocated #GstD3D11Memory with given parameters.
1473  *
1474  * Since: 1.20
1475  */
1476 GstMemory *
1477 gst_d3d11_allocator_alloc (GstD3D11Allocator * allocator,
1478     GstD3D11Device * device, const D3D11_TEXTURE2D_DESC * desc)
1479 {
1480   GstMemory *mem;
1481
1482   g_return_val_if_fail (GST_IS_D3D11_ALLOCATOR (allocator), NULL);
1483   g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
1484   g_return_val_if_fail (desc != NULL, NULL);
1485
1486   mem = gst_d3d11_allocator_alloc_internal (allocator, device, desc);
1487   if (!mem)
1488     return NULL;
1489
1490   if (!gst_d3d11_memory_update_size (mem)) {
1491     GST_ERROR_OBJECT (allocator, "Failed to calculate size");
1492     gst_memory_unref (mem);
1493     return NULL;
1494   }
1495
1496   return mem;
1497 }
1498
1499 /**
1500  * gst_d3d11_allocator_alloc_buffer:
1501  * @allocator: a #GstD3D11Allocator
1502  * @device: a #GstD3D11Device
1503  * @desc: a D3D11_BUFFER_DESC struct
1504  *
1505  * Returns: a newly allocated #GstD3D11Memory with given parameters.
1506  *
1507  * Since: 1.22
1508  */
1509 GstMemory *
1510 gst_d3d11_allocator_alloc_buffer (GstD3D11Allocator * allocator,
1511     GstD3D11Device * device, const D3D11_BUFFER_DESC * desc)
1512 {
1513   GstD3D11Memory *mem;
1514   ID3D11Buffer *buffer;
1515   ID3D11Device *device_handle;
1516   HRESULT hr;
1517
1518   g_return_val_if_fail (GST_IS_D3D11_ALLOCATOR (allocator), nullptr);
1519   g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), nullptr);
1520   g_return_val_if_fail (desc != nullptr, nullptr);
1521
1522   if (desc->Usage != D3D11_USAGE_STAGING) {
1523     GST_FIXME_OBJECT (allocator, "Non staging buffer is not supported");
1524     return nullptr;
1525   }
1526
1527   device_handle = gst_d3d11_device_get_device_handle (device);
1528
1529   hr = device_handle->CreateBuffer (desc, nullptr, &buffer);
1530   if (!gst_d3d11_result (hr, device)) {
1531     GST_ERROR_OBJECT (allocator, "Couldn't create buffer");
1532     return nullptr;
1533   }
1534
1535   mem = g_new0 (GstD3D11Memory, 1);
1536   mem->priv = g_new0 (GstD3D11MemoryPrivate, 1);
1537
1538   gst_memory_init (GST_MEMORY_CAST (mem),
1539       (GstMemoryFlags) 0, GST_ALLOCATOR_CAST (allocator), nullptr, 0, 0, 0, 0);
1540   g_mutex_init (&mem->priv->lock);
1541   mem->priv->buffer = buffer;
1542   mem->priv->buffer_desc = *desc;
1543   mem->priv->native_type = GST_D3D11_MEMORY_NATIVE_TYPE_BUFFER;
1544   mem->device = (GstD3D11Device *) gst_object_ref (device);
1545
1546   GST_MEMORY_CAST (mem)->maxsize = GST_MEMORY_CAST (mem)->size =
1547       desc->ByteWidth;
1548
1549   return GST_MEMORY_CAST (mem);
1550 }
1551
1552 gboolean
1553 gst_d3d11_allocator_set_active (GstD3D11Allocator * allocator, gboolean active)
1554 {
1555   GstD3D11AllocatorClass *klass;
1556
1557   g_return_val_if_fail (GST_IS_D3D11_ALLOCATOR (allocator), FALSE);
1558
1559   klass = GST_D3D11_ALLOCATOR_GET_CLASS (allocator);
1560   if (klass->set_actvie)
1561     return klass->set_actvie (allocator, active);
1562
1563   return TRUE;
1564 }
1565
1566 /* GstD3D11PoolAllocator */
1567 #define GST_D3D11_POOL_ALLOCATOR_LOCK(alloc)   (g_rec_mutex_lock(&alloc->priv->lock))
1568 #define GST_D3D11_POOL_ALLOCATOR_UNLOCK(alloc) (g_rec_mutex_unlock(&alloc->priv->lock))
1569 #define GST_D3D11_POOL_ALLOCATOR_IS_FLUSHING(alloc)  (g_atomic_int_get (&alloc->priv->flushing))
1570
1571 struct _GstD3D11PoolAllocatorPrivate
1572 {
1573   /* parent texture when array typed memory is used */
1574   ID3D11Texture2D *texture;
1575   D3D11_TEXTURE2D_DESC desc;
1576
1577   /* All below member variables are analogous to that of GstBufferPool */
1578   GstAtomicQueue *queue;
1579   GstPoll *poll;
1580
1581   /* This lock will protect all below variables apart from atomic ones
1582    * (identical to GstBufferPool::priv::rec_lock) */
1583   GRecMutex lock;
1584   gboolean started;
1585   gboolean active;
1586
1587   /* atomic */
1588   gint outstanding;
1589   guint max_mems;
1590   guint cur_mems;
1591   gboolean flushing;
1592
1593   /* Calculated memory size, based on Direct3D11 staging texture map.
1594    * Note that, we cannot know the actually staging texture memory size prior
1595    * to map the staging texture because driver will likely require padding */
1596   gsize mem_size;
1597 };
1598
1599 static void gst_d3d11_pool_allocator_dispose (GObject * object);
1600 static void gst_d3d11_pool_allocator_finalize (GObject * object);
1601
1602 static gboolean
1603 gst_d3d11_pool_allocator_set_active (GstD3D11Allocator * allocator,
1604     gboolean active);
1605
1606 static gboolean gst_d3d11_pool_allocator_start (GstD3D11PoolAllocator * self);
1607 static gboolean gst_d3d11_pool_allocator_stop (GstD3D11PoolAllocator * self);
1608 static gboolean gst_d3d11_memory_release (GstMiniObject * mini_object);
1609
1610 #define gst_d3d11_pool_allocator_parent_class pool_alloc_parent_class
1611 G_DEFINE_TYPE_WITH_PRIVATE (GstD3D11PoolAllocator,
1612     gst_d3d11_pool_allocator, GST_TYPE_D3D11_ALLOCATOR);
1613
1614 static void
1615 gst_d3d11_pool_allocator_class_init (GstD3D11PoolAllocatorClass * klass)
1616 {
1617   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1618   GstD3D11AllocatorClass *d3d11alloc_class = GST_D3D11_ALLOCATOR_CLASS (klass);
1619
1620   gobject_class->dispose = gst_d3d11_pool_allocator_dispose;
1621   gobject_class->finalize = gst_d3d11_pool_allocator_finalize;
1622
1623   d3d11alloc_class->set_actvie = gst_d3d11_pool_allocator_set_active;
1624 }
1625
1626 static void
1627 gst_d3d11_pool_allocator_init (GstD3D11PoolAllocator * allocator)
1628 {
1629   GstD3D11PoolAllocatorPrivate *priv;
1630
1631   priv = allocator->priv = (GstD3D11PoolAllocatorPrivate *)
1632       gst_d3d11_pool_allocator_get_instance_private (allocator);
1633   g_rec_mutex_init (&priv->lock);
1634
1635   priv->poll = gst_poll_new_timer ();
1636   priv->queue = gst_atomic_queue_new (16);
1637   priv->flushing = 1;
1638   priv->active = FALSE;
1639   priv->started = FALSE;
1640
1641   /* 1 control write for flushing - the flush token */
1642   gst_poll_write_control (priv->poll);
1643   /* 1 control write for marking that we are not waiting for poll - the wait token */
1644   gst_poll_write_control (priv->poll);
1645 }
1646
1647 static void
1648 gst_d3d11_pool_allocator_dispose (GObject * object)
1649 {
1650   GstD3D11PoolAllocator *self = GST_D3D11_POOL_ALLOCATOR (object);
1651
1652   gst_clear_object (&self->device);
1653
1654   G_OBJECT_CLASS (pool_alloc_parent_class)->dispose (object);
1655 }
1656
1657 static void
1658 gst_d3d11_pool_allocator_finalize (GObject * object)
1659 {
1660   GstD3D11PoolAllocator *self = GST_D3D11_POOL_ALLOCATOR (object);
1661   GstD3D11PoolAllocatorPrivate *priv = self->priv;
1662
1663   GST_DEBUG_OBJECT (self, "Finalize");
1664
1665   gst_d3d11_pool_allocator_stop (self);
1666   gst_atomic_queue_unref (priv->queue);
1667   gst_poll_free (priv->poll);
1668   g_rec_mutex_clear (&priv->lock);
1669
1670   GST_D3D11_CLEAR_COM (priv->texture);
1671
1672   G_OBJECT_CLASS (pool_alloc_parent_class)->finalize (object);
1673 }
1674
1675 static gboolean
1676 gst_d3d11_pool_allocator_start (GstD3D11PoolAllocator * self)
1677 {
1678   GstD3D11PoolAllocatorPrivate *priv = self->priv;
1679   ID3D11Device *device_handle;
1680   HRESULT hr;
1681   guint i;
1682
1683   if (priv->started)
1684     return TRUE;
1685
1686   /* Nothing to do */
1687   if (priv->desc.ArraySize == 1) {
1688     priv->started = TRUE;
1689     return TRUE;
1690   }
1691
1692   device_handle = gst_d3d11_device_get_device_handle (self->device);
1693
1694   if (!priv->texture) {
1695     hr = device_handle->CreateTexture2D (&priv->desc, NULL, &priv->texture);
1696     if (!gst_d3d11_result (hr, self->device)) {
1697       GST_ERROR_OBJECT (self, "Failed to allocate texture");
1698       return FALSE;
1699     }
1700   }
1701
1702   /* Pre-allocate memory objects */
1703   for (i = 0; i < priv->desc.ArraySize; i++) {
1704     GstMemory *mem;
1705
1706     priv->texture->AddRef ();
1707     mem =
1708         gst_d3d11_allocator_alloc_wrapped (GST_D3D11_ALLOCATOR_CAST
1709         (_d3d11_memory_allocator), self->device, &priv->desc, priv->texture);
1710
1711     if (i == 0) {
1712       if (!gst_d3d11_memory_update_size (mem)) {
1713         GST_ERROR_OBJECT (self, "Failed to calculate memory size");
1714         gst_memory_unref (mem);
1715         return FALSE;
1716       }
1717
1718       priv->mem_size = mem->size;
1719     } else {
1720       mem->size = mem->maxsize = priv->mem_size;
1721     }
1722
1723     GST_D3D11_MEMORY_CAST (mem)->priv->subresource_index = i;
1724
1725     g_atomic_int_add (&priv->cur_mems, 1);
1726     gst_atomic_queue_push (priv->queue, mem);
1727     gst_poll_write_control (priv->poll);
1728   }
1729
1730   priv->started = TRUE;
1731
1732   return TRUE;
1733 }
1734
1735 static void
1736 gst_d3d11_pool_allocator_do_set_flushing (GstD3D11PoolAllocator * self,
1737     gboolean flushing)
1738 {
1739   GstD3D11PoolAllocatorPrivate *priv = self->priv;
1740
1741   if (GST_D3D11_POOL_ALLOCATOR_IS_FLUSHING (self) == flushing)
1742     return;
1743
1744   if (flushing) {
1745     g_atomic_int_set (&priv->flushing, 1);
1746     /* Write the flush token to wake up any waiters */
1747     gst_poll_write_control (priv->poll);
1748   } else {
1749     while (!gst_poll_read_control (priv->poll)) {
1750       if (errno == EWOULDBLOCK) {
1751         /* This should not really happen unless flushing and unflushing
1752          * happens on different threads. Let's wait a bit to get back flush
1753          * token from the thread that was setting it to flushing */
1754         g_thread_yield ();
1755         continue;
1756       } else {
1757         /* Critical error but GstPoll already complained */
1758         break;
1759       }
1760     }
1761
1762     g_atomic_int_set (&priv->flushing, 0);
1763   }
1764 }
1765
1766 static gboolean
1767 gst_d3d11_pool_allocator_set_active (GstD3D11Allocator * allocator,
1768     gboolean active)
1769 {
1770   GstD3D11PoolAllocator *self = GST_D3D11_POOL_ALLOCATOR (allocator);
1771   GstD3D11PoolAllocatorPrivate *priv = self->priv;
1772
1773   GST_LOG_OBJECT (self, "active %d", active);
1774
1775   GST_D3D11_POOL_ALLOCATOR_LOCK (self);
1776   /* just return if we are already in the right state */
1777   if (priv->active == active)
1778     goto was_ok;
1779
1780   if (active) {
1781     if (!gst_d3d11_pool_allocator_start (self))
1782       goto start_failed;
1783
1784     /* flush_stop may release memory objects, setting to active to avoid running
1785      * do_stop while activating the pool */
1786     priv->active = TRUE;
1787
1788     gst_d3d11_pool_allocator_do_set_flushing (self, FALSE);
1789   } else {
1790     gint outstanding;
1791
1792     /* set to flushing first */
1793     gst_d3d11_pool_allocator_do_set_flushing (self, TRUE);
1794
1795     /* when all memory objects are in the pool, free them. Else they will be
1796      * freed when they are released */
1797     outstanding = g_atomic_int_get (&priv->outstanding);
1798     GST_LOG_OBJECT (self, "outstanding memories %d, (in queue %d)",
1799         outstanding, gst_atomic_queue_length (priv->queue));
1800     if (outstanding == 0) {
1801       if (!gst_d3d11_pool_allocator_stop (self))
1802         goto stop_failed;
1803     }
1804
1805     priv->active = FALSE;
1806   }
1807
1808   GST_D3D11_POOL_ALLOCATOR_UNLOCK (self);
1809
1810   return TRUE;
1811
1812 was_ok:
1813   {
1814     GST_DEBUG_OBJECT (self, "allocator was in the right state");
1815     GST_D3D11_POOL_ALLOCATOR_UNLOCK (self);
1816     return TRUE;
1817   }
1818 start_failed:
1819   {
1820     GST_ERROR_OBJECT (self, "start failed");
1821     GST_D3D11_POOL_ALLOCATOR_UNLOCK (self);
1822     return FALSE;
1823   }
1824 stop_failed:
1825   {
1826     GST_ERROR_OBJECT (self, "stop failed");
1827     GST_D3D11_POOL_ALLOCATOR_UNLOCK (self);
1828     return FALSE;
1829   }
1830 }
1831
1832 static void
1833 gst_d3d11_pool_allocator_free_memory (GstD3D11PoolAllocator * self,
1834     GstMemory * mem)
1835 {
1836   GstD3D11PoolAllocatorPrivate *priv = self->priv;
1837
1838   g_atomic_int_add (&priv->cur_mems, -1);
1839   GST_LOG_OBJECT (self, "freeing memory %p (%u left)", mem, priv->cur_mems);
1840
1841   GST_MINI_OBJECT_CAST (mem)->dispose = NULL;
1842   gst_memory_unref (mem);
1843 }
1844
1845 /* must be called with the lock */
1846 static gboolean
1847 gst_d3d11_pool_allocator_clear_queue (GstD3D11PoolAllocator * self)
1848 {
1849   GstD3D11PoolAllocatorPrivate *priv = self->priv;
1850   GstMemory *memory;
1851
1852   GST_LOG_OBJECT (self, "Clearing queue");
1853
1854   /* clear the pool */
1855   while ((memory = (GstMemory *) gst_atomic_queue_pop (priv->queue))) {
1856     while (!gst_poll_read_control (priv->poll)) {
1857       if (errno == EWOULDBLOCK) {
1858         /* We put the memory into the queue but did not finish writing control
1859          * yet, let's wait a bit and retry */
1860         g_thread_yield ();
1861         continue;
1862       } else {
1863         /* Critical error but GstPoll already complained */
1864         break;
1865       }
1866     }
1867     gst_d3d11_pool_allocator_free_memory (self, memory);
1868   }
1869
1870   GST_LOG_OBJECT (self, "Clear done");
1871
1872   return priv->cur_mems == 0;
1873 }
1874
1875 /* must be called with the lock */
1876 static gboolean
1877 gst_d3d11_pool_allocator_stop (GstD3D11PoolAllocator * self)
1878 {
1879   GstD3D11PoolAllocatorPrivate *priv = self->priv;
1880
1881   GST_DEBUG_OBJECT (self, "Stop");
1882
1883   if (priv->started) {
1884     if (!gst_d3d11_pool_allocator_clear_queue (self))
1885       return FALSE;
1886
1887     priv->started = FALSE;
1888   } else {
1889     GST_DEBUG_OBJECT (self, "Wasn't started");
1890   }
1891
1892   return TRUE;
1893 }
1894
1895 static inline void
1896 dec_outstanding (GstD3D11PoolAllocator * self)
1897 {
1898   if (g_atomic_int_dec_and_test (&self->priv->outstanding)) {
1899     /* all memory objects are returned to the pool, see if we need to free them */
1900     if (GST_D3D11_POOL_ALLOCATOR_IS_FLUSHING (self)) {
1901       /* take the lock so that set_active is not run concurrently */
1902       GST_D3D11_POOL_ALLOCATOR_LOCK (self);
1903       /* now that we have the lock, check if we have been de-activated with
1904        * outstanding buffers */
1905       if (!self->priv->active)
1906         gst_d3d11_pool_allocator_stop (self);
1907
1908       GST_D3D11_POOL_ALLOCATOR_UNLOCK (self);
1909     }
1910   }
1911 }
1912
1913 static void
1914 gst_d3d11_pool_allocator_release_memory (GstD3D11PoolAllocator * self,
1915     GstMemory * mem)
1916 {
1917   GST_LOG_OBJECT (self, "Released memory %p", mem);
1918
1919   GST_MINI_OBJECT_CAST (mem)->dispose = NULL;
1920   mem->allocator = (GstAllocator *) gst_object_ref (_d3d11_memory_allocator);
1921   gst_object_unref (self);
1922
1923   /* keep it around in our queue */
1924   gst_atomic_queue_push (self->priv->queue, mem);
1925   gst_poll_write_control (self->priv->poll);
1926   dec_outstanding (self);
1927 }
1928
1929 static gboolean
1930 gst_d3d11_memory_release (GstMiniObject * mini_object)
1931 {
1932   GstMemory *mem = GST_MEMORY_CAST (mini_object);
1933   GstD3D11PoolAllocator *alloc;
1934
1935   g_assert (mem->allocator != NULL);
1936
1937   if (!GST_IS_D3D11_POOL_ALLOCATOR (mem->allocator)) {
1938     GST_LOG_OBJECT (mem->allocator, "Not our memory, free");
1939     return TRUE;
1940   }
1941
1942   alloc = GST_D3D11_POOL_ALLOCATOR (mem->allocator);
1943   /* if flushing, free this memory */
1944   if (GST_D3D11_POOL_ALLOCATOR_IS_FLUSHING (alloc)) {
1945     GST_LOG_OBJECT (alloc, "allocator is flushing, free %p", mem);
1946     return TRUE;
1947   }
1948
1949   /* return the memory to the allocator */
1950   gst_memory_ref (mem);
1951   gst_d3d11_pool_allocator_release_memory (alloc, mem);
1952
1953   return FALSE;
1954 }
1955
1956 static GstFlowReturn
1957 gst_d3d11_pool_allocator_alloc (GstD3D11PoolAllocator * self, GstMemory ** mem)
1958 {
1959   GstD3D11PoolAllocatorPrivate *priv = self->priv;
1960   GstMemory *new_mem;
1961
1962   /* we allcates texture array during start */
1963   if (priv->desc.ArraySize > 1)
1964     return GST_FLOW_EOS;
1965
1966   /* increment the allocation counter */
1967   g_atomic_int_add (&priv->cur_mems, 1);
1968   new_mem =
1969       gst_d3d11_allocator_alloc_internal (GST_D3D11_ALLOCATOR_CAST
1970       (_d3d11_memory_allocator), self->device, &priv->desc);
1971   if (!new_mem) {
1972     GST_ERROR_OBJECT (self, "Failed to allocate new memory");
1973     g_atomic_int_add (&priv->cur_mems, -1);
1974     return GST_FLOW_ERROR;
1975   }
1976
1977   if (!priv->mem_size) {
1978     if (!gst_d3d11_memory_update_size (new_mem)) {
1979       GST_ERROR_OBJECT (self, "Failed to calculate size");
1980       gst_memory_unref (new_mem);
1981       g_atomic_int_add (&priv->cur_mems, -1);
1982
1983       return GST_FLOW_ERROR;
1984     }
1985
1986     priv->mem_size = new_mem->size;
1987   }
1988
1989   new_mem->size = new_mem->maxsize = priv->mem_size;
1990
1991   *mem = new_mem;
1992
1993   return GST_FLOW_OK;
1994 }
1995
1996 static GstFlowReturn
1997 gst_d3d11_pool_allocator_acquire_memory_internal (GstD3D11PoolAllocator * self,
1998     GstMemory ** memory)
1999 {
2000   GstFlowReturn result;
2001   GstD3D11PoolAllocatorPrivate *priv = self->priv;
2002
2003   while (TRUE) {
2004     if (G_UNLIKELY (GST_D3D11_POOL_ALLOCATOR_IS_FLUSHING (self)))
2005       goto flushing;
2006
2007     /* try to get a memory from the queue */
2008     *memory = (GstMemory *) gst_atomic_queue_pop (priv->queue);
2009     if (G_LIKELY (*memory)) {
2010       while (!gst_poll_read_control (priv->poll)) {
2011         if (errno == EWOULDBLOCK) {
2012           /* We put the memory into the queue but did not finish writing control
2013            * yet, let's wait a bit and retry */
2014           g_thread_yield ();
2015           continue;
2016         } else {
2017           /* Critical error but GstPoll already complained */
2018           break;
2019         }
2020       }
2021       result = GST_FLOW_OK;
2022       GST_LOG_OBJECT (self, "acquired memory %p", *memory);
2023       break;
2024     }
2025
2026     /* no memory, try to allocate some more */
2027     GST_LOG_OBJECT (self, "no memory, trying to allocate");
2028     result = gst_d3d11_pool_allocator_alloc (self, memory);
2029     if (G_LIKELY (result == GST_FLOW_OK))
2030       /* we have a memory, return it */
2031       break;
2032
2033     if (G_UNLIKELY (result != GST_FLOW_EOS))
2034       /* something went wrong, return error */
2035       break;
2036
2037     /* now we release the control socket, we wait for a memory release or
2038      * flushing */
2039     if (!gst_poll_read_control (priv->poll)) {
2040       if (errno == EWOULDBLOCK) {
2041         /* This means that we have two threads trying to allocate memory
2042          * already, and the other one already got the wait token. This
2043          * means that we only have to wait for the poll now and not write the
2044          * token afterwards: we will be woken up once the other thread is
2045          * woken up and that one will write the wait token it removed */
2046         GST_LOG_OBJECT (self, "waiting for free memory or flushing");
2047         gst_poll_wait (priv->poll, GST_CLOCK_TIME_NONE);
2048       } else {
2049         /* This is a critical error, GstPoll already gave a warning */
2050         result = GST_FLOW_ERROR;
2051         break;
2052       }
2053     } else {
2054       /* We're the first thread waiting, we got the wait token and have to
2055        * write it again later
2056        * OR
2057        * We're a second thread and just consumed the flush token and block all
2058        * other threads, in which case we must not wait and give it back
2059        * immediately */
2060       if (!GST_D3D11_POOL_ALLOCATOR_IS_FLUSHING (self)) {
2061         GST_LOG_OBJECT (self, "waiting for free memory or flushing");
2062         gst_poll_wait (priv->poll, GST_CLOCK_TIME_NONE);
2063       }
2064       gst_poll_write_control (priv->poll);
2065     }
2066   }
2067
2068   return result;
2069
2070   /* ERRORS */
2071 flushing:
2072   {
2073     GST_DEBUG_OBJECT (self, "we are flushing");
2074     return GST_FLOW_FLUSHING;
2075   }
2076 }
2077
2078 /**
2079  * gst_d3d11_pool_allocator_new:
2080  * @device: a #GstD3D11Device
2081  * @desc: a D3D11_TEXTURE2D_DESC for texture allocation
2082  *
2083  * Creates a new #GstD3D11PoolAllocator instance.
2084  *
2085  * Returns: (transfer full): a new #GstD3D11PoolAllocator instance
2086  */
2087 GstD3D11PoolAllocator *
2088 gst_d3d11_pool_allocator_new (GstD3D11Device * device,
2089     const D3D11_TEXTURE2D_DESC * desc)
2090 {
2091   GstD3D11PoolAllocator *self;
2092
2093   g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
2094   g_return_val_if_fail (desc != NULL, NULL);
2095
2096   gst_d3d11_memory_init_once ();
2097
2098   self = (GstD3D11PoolAllocator *)
2099       g_object_new (GST_TYPE_D3D11_POOL_ALLOCATOR, NULL);
2100   gst_object_ref_sink (self);
2101
2102   self->device = (GstD3D11Device *) gst_object_ref (device);
2103   self->priv->desc = *desc;
2104
2105   return self;
2106 }
2107
2108 /**
2109  * gst_d3d11_pool_allocator_acquire_memory:
2110  * @allocator: a #GstD3D11PoolAllocator
2111  * @memory: (transfer full): a #GstMemory
2112  *
2113  * Acquires a #GstMemory from @allocator. @memory should point to a memory
2114  * location that can hold a pointer to the new #GstMemory.
2115  *
2116  * Returns: a #GstFlowReturn such as %GST_FLOW_FLUSHING when the allocator is
2117  * inactive.
2118  */
2119 GstFlowReturn
2120 gst_d3d11_pool_allocator_acquire_memory (GstD3D11PoolAllocator * allocator,
2121     GstMemory ** memory)
2122 {
2123   GstD3D11PoolAllocatorPrivate *priv;
2124   GstFlowReturn result;
2125
2126   g_return_val_if_fail (GST_IS_D3D11_POOL_ALLOCATOR (allocator),
2127       GST_FLOW_ERROR);
2128   g_return_val_if_fail (memory != NULL, GST_FLOW_ERROR);
2129
2130   priv = allocator->priv;
2131
2132   /* assume we'll have one more outstanding buffer we need to do that so
2133    * that concurrent set_active doesn't clear the buffers */
2134   g_atomic_int_inc (&priv->outstanding);
2135   result = gst_d3d11_pool_allocator_acquire_memory_internal (allocator, memory);
2136
2137   if (result == GST_FLOW_OK) {
2138     GstMemory *mem = *memory;
2139     /* Replace default allocator with ours */
2140     gst_object_unref (mem->allocator);
2141     mem->allocator = (GstAllocator *) gst_object_ref (allocator);
2142     GST_MINI_OBJECT_CAST (mem)->dispose = gst_d3d11_memory_release;
2143   } else {
2144     dec_outstanding (allocator);
2145   }
2146
2147   return result;
2148 }
2149
2150 /**
2151  * gst_d3d11_pool_allocator_get_pool_size:
2152  * @allocator: a #GstD3D11PoolAllocator
2153  * @max_size: (out) (optional): the max size of pool
2154  * @outstanding_size: (out) (optional): the number of outstanding memory
2155  *
2156  * Returns: %TRUE if the size of memory pool is known
2157  *
2158  * Since: 1.20
2159  */
2160 gboolean
2161 gst_d3d11_pool_allocator_get_pool_size (GstD3D11PoolAllocator * allocator,
2162     guint * max_size, guint * outstanding_size)
2163 {
2164   GstD3D11PoolAllocatorPrivate *priv;
2165
2166   g_return_val_if_fail (GST_IS_D3D11_POOL_ALLOCATOR (allocator), FALSE);
2167
2168   priv = allocator->priv;
2169
2170   if (max_size) {
2171     if (priv->desc.ArraySize > 1) {
2172       *max_size = priv->desc.ArraySize;
2173     } else {
2174       /* For non-texture-array memory, we don't have any limit yet */
2175       *max_size = 0;
2176     }
2177   }
2178
2179   if (outstanding_size)
2180     *outstanding_size = g_atomic_int_get (&priv->outstanding);
2181
2182   return TRUE;
2183 }