d3d11: Update library doc
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / gst-libs / gst / d3d11 / gstd3d11device.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 "gstd3d11device.h"
26 #include "gstd3d11utils.h"
27 #include "gstd3d11format.h"
28 #include "gstd3d11-private.h"
29 #include "gstd3d11memory.h"
30 #include <gmodule.h>
31 #include <wrl.h>
32
33 #include <windows.h>
34 #include <versionhelpers.h>
35
36 /**
37  * SECTION:gstd3d11device
38  * @title: GstD3D11Device
39  * @short_description: Direct3D11 device abstraction
40  *
41  * #GstD3D11Device wraps ID3D11Device and ID3D11DeviceContext for GPU resources
42  * to be able to be shared among various elements. Caller can get native
43  * Direct3D11 handles via getter method.
44  * Basically Direct3D11 API doesn't require dedicated thread like that of
45  * OpenGL context, and ID3D11Device APIs are supposed to be thread-safe.
46  * But concurrent call for ID3D11DeviceContext and DXGI API are not allowed.
47  * To protect such object, callers need to make use of gst_d3d11_device_lock()
48  * and gst_d3d11_device_unlock()
49  */
50
51 /* *INDENT-OFF* */
52 using namespace Microsoft::WRL;
53 /* *INDENT-ON* */
54
55 #if HAVE_D3D11SDKLAYERS_H
56 #include <d3d11sdklayers.h>
57
58 /* mingw header does not define D3D11_RLDO_IGNORE_INTERNAL
59  * D3D11_RLDO_SUMMARY = 0x1,
60    D3D11_RLDO_DETAIL = 0x2,
61  * D3D11_RLDO_IGNORE_INTERNAL = 0x4
62  */
63 #define GST_D3D11_RLDO_FLAGS (0x2 | 0x4)
64 #endif
65
66 #if HAVE_DXGIDEBUG_H
67 #include <dxgidebug.h>
68 typedef HRESULT (WINAPI * DXGIGetDebugInterface_t) (REFIID riid,
69     void **ppDebug);
70 static DXGIGetDebugInterface_t GstDXGIGetDebugInterface = nullptr;
71
72 #endif
73
74 #if (HAVE_D3D11SDKLAYERS_H || HAVE_DXGIDEBUG_H)
75 GST_DEBUG_CATEGORY_STATIC (gst_d3d11_debug_layer_debug);
76 #endif
77 GST_DEBUG_CATEGORY_STATIC (gst_d3d11_device_debug);
78 #define GST_CAT_DEFAULT gst_d3d11_device_debug
79
80 enum
81 {
82   PROP_0,
83   PROP_ADAPTER,
84   PROP_DEVICE_ID,
85   PROP_VENDOR_ID,
86   PROP_HARDWARE,
87   PROP_DESCRIPTION,
88   PROP_CREATE_FLAGS,
89   PROP_ADAPTER_LUID,
90 };
91
92 #define DEFAULT_ADAPTER 0
93 #define DEFAULT_CREATE_FLAGS 0
94
95 struct _GstD3D11DevicePrivate
96 {
97   guint adapter;
98   guint device_id;
99   guint vendor_id;
100   gboolean hardware;
101   gchar *description;
102   guint create_flags;
103   gint64 adapter_luid;
104
105   ID3D11Device *device;
106   ID3D11Device5 *device5;
107   ID3D11DeviceContext *device_context;
108   ID3D11DeviceContext4 *device_context4;
109
110   ID3D11VideoDevice *video_device;
111   ID3D11VideoContext *video_context;
112
113   IDXGIFactory1 *factory;
114   GArray *format_table;
115
116   CRITICAL_SECTION extern_lock;
117   SRWLOCK resource_lock;
118
119   LARGE_INTEGER frequency;
120
121 #if HAVE_D3D11SDKLAYERS_H
122   ID3D11Debug *d3d11_debug;
123   ID3D11InfoQueue *d3d11_info_queue;
124 #endif
125
126 #if HAVE_DXGIDEBUG_H
127   IDXGIDebug *dxgi_debug;
128   IDXGIInfoQueue *dxgi_info_queue;
129 #endif
130 };
131
132 static void
133 debug_init_once (void)
134 {
135   GST_D3D11_CALL_ONCE_BEGIN {
136     GST_DEBUG_CATEGORY_INIT (gst_d3d11_device_debug,
137         "d3d11device", 0, "d3d11 device object");
138 #if defined(HAVE_D3D11SDKLAYERS_H) || defined(HAVE_DXGIDEBUG_H)
139     GST_DEBUG_CATEGORY_INIT (gst_d3d11_debug_layer_debug,
140         "d3d11debuglayer", 0, "native d3d11 and dxgi debug");
141 #endif
142   } GST_D3D11_CALL_ONCE_END;
143 }
144
145 #define gst_d3d11_device_parent_class parent_class
146 G_DEFINE_TYPE_WITH_CODE (GstD3D11Device, gst_d3d11_device, GST_TYPE_OBJECT,
147     G_ADD_PRIVATE (GstD3D11Device); debug_init_once ());
148
149 static void gst_d3d11_device_get_property (GObject * object, guint prop_id,
150     GValue * value, GParamSpec * pspec);
151 static void gst_d3d11_device_dispose (GObject * object);
152 static void gst_d3d11_device_finalize (GObject * object);
153
154 #if HAVE_D3D11SDKLAYERS_H
155 static gboolean
156 gst_d3d11_device_enable_d3d11_debug (void)
157 {
158   static GModule *d3d11_debug_module = nullptr;
159   /* If all below libraries are unavailable, d3d11 device would fail with
160    * D3D11_CREATE_DEVICE_DEBUG flag */
161   static const gchar *sdk_dll_names[] = {
162     "d3d11sdklayers.dll",
163     "d3d11_1sdklayers.dll",
164     "d3d11_2sdklayers.dll",
165     "d3d11_3sdklayers.dll",
166   };
167
168   GST_D3D11_CALL_ONCE_BEGIN {
169     for (guint i = 0; i < G_N_ELEMENTS (sdk_dll_names); i++) {
170       d3d11_debug_module = g_module_open (sdk_dll_names[i], G_MODULE_BIND_LAZY);
171       if (d3d11_debug_module)
172         return;
173     }
174   }
175   GST_D3D11_CALL_ONCE_END;
176
177   if (d3d11_debug_module)
178     return TRUE;
179
180   return FALSE;
181 }
182
183 static inline GstDebugLevel
184 d3d11_message_severity_to_gst (D3D11_MESSAGE_SEVERITY level)
185 {
186   switch (level) {
187     case D3D11_MESSAGE_SEVERITY_CORRUPTION:
188     case D3D11_MESSAGE_SEVERITY_ERROR:
189       return GST_LEVEL_ERROR;
190     case D3D11_MESSAGE_SEVERITY_WARNING:
191       return GST_LEVEL_WARNING;
192     case D3D11_MESSAGE_SEVERITY_INFO:
193       return GST_LEVEL_INFO;
194     case D3D11_MESSAGE_SEVERITY_MESSAGE:
195       return GST_LEVEL_DEBUG;
196     default:
197       break;
198   }
199
200   return GST_LEVEL_LOG;
201 }
202
203 void
204 gst_d3d11_device_d3d11_debug (GstD3D11Device * device,
205     const gchar * file, const gchar * function, gint line)
206 {
207   GstD3D11DevicePrivate *priv = device->priv;
208   D3D11_MESSAGE *msg;
209   SIZE_T msg_len = 0;
210   HRESULT hr;
211   UINT64 num_msg, i;
212   ID3D11InfoQueue *info_queue = priv->d3d11_info_queue;
213
214   if (!info_queue)
215     return;
216
217   num_msg = info_queue->GetNumStoredMessages ();
218
219   for (i = 0; i < num_msg; i++) {
220     GstDebugLevel level;
221
222     hr = info_queue->GetMessage (i, NULL, &msg_len);
223
224     if (FAILED (hr) || msg_len == 0) {
225       return;
226     }
227
228     msg = (D3D11_MESSAGE *) g_alloca (msg_len);
229     hr = info_queue->GetMessage (i, msg, &msg_len);
230
231     level = d3d11_message_severity_to_gst (msg->Severity);
232     if (msg->Category == D3D11_MESSAGE_CATEGORY_STATE_CREATION &&
233         level > GST_LEVEL_ERROR) {
234       /* Do not warn for live object, since there would be live object
235        * when ReportLiveDeviceObjects was called */
236       level = GST_LEVEL_INFO;
237     }
238
239     gst_debug_log (gst_d3d11_debug_layer_debug, level, file, function, line,
240         G_OBJECT (device), "D3D11InfoQueue: %s", msg->pDescription);
241   }
242
243   info_queue->ClearStoredMessages ();
244
245   return;
246 }
247 #else
248 void
249 gst_d3d11_device_d3d11_debug (GstD3D11Device * device,
250     const gchar * file, const gchar * function, gint line)
251 {
252   /* do nothing */
253   return;
254 }
255 #endif
256
257 #if HAVE_DXGIDEBUG_H
258 static gboolean
259 gst_d3d11_device_enable_dxgi_debug (void)
260 {
261   static GModule *dxgi_debug_module = nullptr;
262
263   GST_D3D11_CALL_ONCE_BEGIN {
264 #if (!GST_D3D11_WINAPI_ONLY_APP)
265     dxgi_debug_module = g_module_open ("dxgidebug.dll", G_MODULE_BIND_LAZY);
266
267     if (dxgi_debug_module)
268       g_module_symbol (dxgi_debug_module,
269           "DXGIGetDebugInterface", (gpointer *) & GstDXGIGetDebugInterface);
270 #else
271     GstDXGIGetDebugInterface = DXGIGetDebugInterface1;
272 #endif
273   } GST_D3D11_CALL_ONCE_END;
274
275   if (!GstDXGIGetDebugInterface)
276     return FALSE;
277
278   return TRUE;
279 }
280
281 static HRESULT
282 gst_d3d11_device_dxgi_get_device_interface (REFIID riid, void **debug)
283 {
284 #if (!GST_D3D11_WINAPI_ONLY_APP)
285   if (GstDXGIGetDebugInterface) {
286     return GstDXGIGetDebugInterface (riid, debug);
287   } else {
288     return E_NOINTERFACE;
289   }
290 #else
291   return DXGIGetDebugInterface1 (0, riid, debug);
292 #endif
293 }
294
295 static inline GstDebugLevel
296 dxgi_info_queue_message_severity_to_gst (DXGI_INFO_QUEUE_MESSAGE_SEVERITY level)
297 {
298   switch (level) {
299     case DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION:
300     case DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR:
301       return GST_LEVEL_ERROR;
302     case DXGI_INFO_QUEUE_MESSAGE_SEVERITY_WARNING:
303       return GST_LEVEL_WARNING;
304     case DXGI_INFO_QUEUE_MESSAGE_SEVERITY_INFO:
305       return GST_LEVEL_INFO;
306     case DXGI_INFO_QUEUE_MESSAGE_SEVERITY_MESSAGE:
307       return GST_LEVEL_DEBUG;
308     default:
309       break;
310   }
311
312   return GST_LEVEL_LOG;
313 }
314
315 void
316 gst_d3d11_device_dxgi_debug (GstD3D11Device * device,
317     const gchar * file, const gchar * function, gint line)
318 {
319   GstD3D11DevicePrivate *priv = device->priv;
320   DXGI_INFO_QUEUE_MESSAGE *msg;
321   SIZE_T msg_len = 0;
322   HRESULT hr;
323   UINT64 num_msg, i;
324   IDXGIInfoQueue *info_queue = priv->dxgi_info_queue;
325
326   if (!info_queue)
327     return;
328
329   num_msg = info_queue->GetNumStoredMessages (DXGI_DEBUG_ALL);
330
331   for (i = 0; i < num_msg; i++) {
332     GstDebugLevel level;
333
334     hr = info_queue->GetMessage (DXGI_DEBUG_ALL, i, NULL, &msg_len);
335
336     if (FAILED (hr) || msg_len == 0) {
337       return;
338     }
339
340     msg = (DXGI_INFO_QUEUE_MESSAGE *) g_alloca (msg_len);
341     hr = info_queue->GetMessage (DXGI_DEBUG_ALL, i, msg, &msg_len);
342
343     level = dxgi_info_queue_message_severity_to_gst (msg->Severity);
344     gst_debug_log (gst_d3d11_debug_layer_debug, level, file, function, line,
345         G_OBJECT (device), "DXGIInfoQueue: %s", msg->pDescription);
346   }
347
348   info_queue->ClearStoredMessages (DXGI_DEBUG_ALL);
349
350   return;
351 }
352 #else
353 void
354 gst_d3d11_device_dxgi_debug (GstD3D11Device * device,
355     const gchar * file, const gchar * function, gint line)
356 {
357   /* do nothing */
358   return;
359 }
360 #endif
361
362 static void
363 gst_d3d11_device_class_init (GstD3D11DeviceClass * klass)
364 {
365   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
366   GParamFlags readable_flags =
367       (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
368
369   gobject_class->get_property = gst_d3d11_device_get_property;
370   gobject_class->dispose = gst_d3d11_device_dispose;
371   gobject_class->finalize = gst_d3d11_device_finalize;
372
373   g_object_class_install_property (gobject_class, PROP_ADAPTER,
374       g_param_spec_uint ("adapter", "Adapter",
375           "DXGI Adapter index for creating device",
376           0, G_MAXUINT32, DEFAULT_ADAPTER, readable_flags));
377
378   g_object_class_install_property (gobject_class, PROP_DEVICE_ID,
379       g_param_spec_uint ("device-id", "Device Id",
380           "DXGI Device ID", 0, G_MAXUINT32, 0, readable_flags));
381
382   g_object_class_install_property (gobject_class, PROP_VENDOR_ID,
383       g_param_spec_uint ("vendor-id", "Vendor Id",
384           "DXGI Vendor ID", 0, G_MAXUINT32, 0, readable_flags));
385
386   g_object_class_install_property (gobject_class, PROP_HARDWARE,
387       g_param_spec_boolean ("hardware", "Hardware",
388           "Whether hardware device or not", TRUE, readable_flags));
389
390   g_object_class_install_property (gobject_class, PROP_DESCRIPTION,
391       g_param_spec_string ("description", "Description",
392           "Human readable device description", NULL, readable_flags));
393
394   g_object_class_install_property (gobject_class, PROP_ADAPTER_LUID,
395       g_param_spec_int64 ("adapter-luid", "Adapter LUID",
396           "DXGI Adapter LUID (Locally Unique Identifier) of created device",
397           G_MININT64, G_MAXINT64, 0, readable_flags));
398
399   gst_d3d11_memory_init_once ();
400 }
401
402 static void
403 gst_d3d11_device_init (GstD3D11Device * self)
404 {
405   GstD3D11DevicePrivate *priv;
406
407   priv = (GstD3D11DevicePrivate *)
408       gst_d3d11_device_get_instance_private (self);
409   priv->adapter = DEFAULT_ADAPTER;
410   priv->format_table = g_array_sized_new (FALSE, FALSE,
411       sizeof (GstD3D11Format), GST_D3D11_N_FORMATS);
412
413   InitializeCriticalSection (&priv->extern_lock);
414
415   self->priv = priv;
416 }
417
418 static gboolean
419 is_windows_8_or_greater (void)
420 {
421   static gboolean ret = FALSE;
422
423   GST_D3D11_CALL_ONCE_BEGIN {
424 #if (!GST_D3D11_WINAPI_ONLY_APP)
425     if (IsWindows8OrGreater ())
426       ret = TRUE;
427 #else
428     ret = TRUE;
429 #endif
430   } GST_D3D11_CALL_ONCE_END;
431
432   return ret;
433 }
434
435 static guint
436 check_format_support (GstD3D11Device * self, DXGI_FORMAT format)
437 {
438   GstD3D11DevicePrivate *priv = self->priv;
439   ID3D11Device *handle = priv->device;
440   HRESULT hr;
441   UINT format_support;
442
443   hr = handle->CheckFormatSupport (format, &format_support);
444   if (FAILED (hr) || format_support == 0)
445     return 0;
446
447   return format_support;
448 }
449
450 static void
451 dump_format (GstD3D11Device * self, GstD3D11Format * format)
452 {
453   gchar *format_support_str = g_flags_to_string (GST_TYPE_D3D11_FORMAT_SUPPORT,
454       format->format_support[0]);
455
456   GST_LOG_OBJECT (self, "%s -> %s (%d), "
457       "resource format: %s (%d), %s (%d), %s (%d), %s (%d), flags (0x%x) %s",
458       gst_video_format_to_string (format->format),
459       gst_d3d11_dxgi_format_to_string (format->dxgi_format),
460       format->dxgi_format,
461       gst_d3d11_dxgi_format_to_string (format->resource_format[0]),
462       format->resource_format[0],
463       gst_d3d11_dxgi_format_to_string (format->resource_format[1]),
464       format->resource_format[1],
465       gst_d3d11_dxgi_format_to_string (format->resource_format[2]),
466       format->resource_format[2],
467       gst_d3d11_dxgi_format_to_string (format->resource_format[3]),
468       format->resource_format[3], format->format_support[0],
469       format_support_str);
470
471   g_free (format_support_str);
472 }
473
474 static void
475 gst_d3d11_device_setup_format_table (GstD3D11Device * self)
476 {
477   GstD3D11DevicePrivate *priv = self->priv;
478
479   for (guint i = 0; i < G_N_ELEMENTS (_gst_d3d11_default_format_map); i++) {
480     const GstD3D11Format *iter = &_gst_d3d11_default_format_map[i];
481     GstD3D11Format format;
482     guint support[GST_VIDEO_MAX_PLANES] = { 0, };
483     gboolean native = TRUE;
484
485     switch (iter->format) {
486         /* RGB/GRAY */
487       case GST_VIDEO_FORMAT_BGRA:
488       case GST_VIDEO_FORMAT_BGRx:
489       case GST_VIDEO_FORMAT_RGBA:
490       case GST_VIDEO_FORMAT_RGBx:
491       case GST_VIDEO_FORMAT_RGB10A2_LE:
492       case GST_VIDEO_FORMAT_RGBA64_LE:
493       case GST_VIDEO_FORMAT_GRAY8:
494       case GST_VIDEO_FORMAT_GRAY16_LE:
495         support[0] = check_format_support (self, iter->dxgi_format);
496         if (!support[0]) {
497           const gchar *format_name =
498               gst_d3d11_dxgi_format_to_string (iter->dxgi_format);
499           GST_INFO_OBJECT (self, "DXGI_FORMAT_%s (%d) for %s is not supported",
500               format_name, (guint) iter->dxgi_format,
501               gst_video_format_to_string (iter->format));
502           continue;
503         }
504         break;
505         /* YUV DXGI native formats */
506       case GST_VIDEO_FORMAT_VUYA:
507       case GST_VIDEO_FORMAT_Y410:
508       case GST_VIDEO_FORMAT_NV12:
509       case GST_VIDEO_FORMAT_P010_10LE:
510       case GST_VIDEO_FORMAT_P012_LE:
511       case GST_VIDEO_FORMAT_P016_LE:{
512         gboolean supported = TRUE;
513
514         if (is_windows_8_or_greater ())
515           support[0] = check_format_support (self, iter->dxgi_format);
516
517         if (!support[0]) {
518           GST_DEBUG_OBJECT (self,
519               "DXGI_FORMAT_%s (%d) for %s is not supported, "
520               "checking resource format",
521               gst_d3d11_dxgi_format_to_string (iter->dxgi_format),
522               (guint) iter->dxgi_format,
523               gst_video_format_to_string (iter->format));
524
525           native = FALSE;
526           for (guint j = 0; j < GST_VIDEO_MAX_PLANES; j++) {
527             if (iter->resource_format[j] == DXGI_FORMAT_UNKNOWN)
528               break;
529
530             support[j] = check_format_support (self, iter->resource_format[j]);
531             if (support[j] == 0) {
532               supported = FALSE;
533               break;
534             }
535           }
536
537           if (!supported) {
538             GST_INFO_OBJECT (self, "%s is not supported",
539                 gst_video_format_to_string (iter->format));
540             continue;
541           }
542         }
543         break;
544       }
545         /* YUV non-DXGI native formats */
546       case GST_VIDEO_FORMAT_NV21:
547       case GST_VIDEO_FORMAT_I420:
548       case GST_VIDEO_FORMAT_YV12:
549       case GST_VIDEO_FORMAT_I420_10LE:
550       case GST_VIDEO_FORMAT_I420_12LE:
551       case GST_VIDEO_FORMAT_Y42B:
552       case GST_VIDEO_FORMAT_I422_10LE:
553       case GST_VIDEO_FORMAT_I422_12LE:
554       case GST_VIDEO_FORMAT_Y444:
555       case GST_VIDEO_FORMAT_Y444_10LE:
556       case GST_VIDEO_FORMAT_Y444_12LE:
557       case GST_VIDEO_FORMAT_Y444_16LE:
558       case GST_VIDEO_FORMAT_AYUV:
559       case GST_VIDEO_FORMAT_AYUV64:{
560         gboolean supported = TRUE;
561
562         native = FALSE;
563         for (guint j = 0; j < GST_VIDEO_MAX_PLANES; j++) {
564           if (iter->resource_format[j] == DXGI_FORMAT_UNKNOWN)
565             break;
566
567           support[j] = check_format_support (self, iter->resource_format[j]);
568           if (support[j] == 0) {
569             supported = FALSE;
570             break;
571           }
572         }
573
574         if (!supported) {
575           GST_INFO_OBJECT (self, "%s is not supported",
576               gst_video_format_to_string (iter->format));
577           continue;
578         }
579         break;
580       }
581       default:
582         g_assert_not_reached ();
583         return;
584     }
585
586     format = *iter;
587
588     if (!native)
589       format.dxgi_format = DXGI_FORMAT_UNKNOWN;
590
591     for (guint j = 0; j < GST_VIDEO_MAX_PLANES; j++)
592       format.format_support[j] = support[j];
593
594     if (gst_debug_category_get_threshold (GST_CAT_DEFAULT) >= GST_LEVEL_LOG)
595       dump_format (self, &format);
596
597     g_array_append_val (priv->format_table, format);
598   }
599
600   /* FIXME: d3d11 sampler doesn't support packed-and-subsampled formats
601    * very well (and it's really poorly documented).
602    * As per observation, d3d11 samplers seems to be dropping the second
603    * Y componet from "Y0-U0-Y1-V0" pair which results in bad visual quality
604    * than 4:2:0 subsampled formats. We should revisit this later */
605
606   /* TODO: The best would be using d3d11 compute shader to handle this kinds of
607    * samples but comute shader is not implemented yet by us.
608    *
609    * Another simple approach is using d3d11 video processor,
610    * but capability will be very device dependent because it depends on
611    * GPU vendor's driver implementation, moreover, software fallback does
612    * not support d3d11 video processor. So it's not reliable in this case */
613 #if 0
614   /* NOTE: packted yuv 4:2:2 YUY2, UYVY, and VYUY formats are not natively
615    * supported render target view formats
616    * (i.e., cannot be output format of shader pipeline) */
617   priv->format_table[n_formats].format = GST_VIDEO_FORMAT_YUY2;
618   if (can_support_format (self, DXGI_FORMAT_YUY2,
619           D3D11_FORMAT_SUPPORT_SHADER_SAMPLE)) {
620     priv->format_table[n_formats].resource_format[0] =
621         DXGI_FORMAT_R8G8B8A8_UNORM;
622     priv->format_table[n_formats].dxgi_format = DXGI_FORMAT_YUY2;
623   } else {
624     /* If DXGI_FORMAT_YUY2 format is not supported, use this format,
625      * it's analogous to YUY2 */
626     priv->format_table[n_formats].resource_format[0] =
627         DXGI_FORMAT_G8R8_G8B8_UNORM;
628   }
629   n_formats++;
630
631   /* No native DXGI format available for UYVY */
632   priv->format_table[n_formats].format = GST_VIDEO_FORMAT_UYVY;
633   priv->format_table[n_formats].resource_format[0] =
634       DXGI_FORMAT_R8G8_B8G8_UNORM;
635   n_formats++;
636
637   /* No native DXGI format available for VYUY */
638   priv->format_table[n_formats].format = GST_VIDEO_FORMAT_VYUY;
639   priv->format_table[n_formats].resource_format[0] =
640       DXGI_FORMAT_R8G8_B8G8_UNORM;
641   n_formats++;
642
643   /* Y210 and Y410 formats cannot support rtv */
644   priv->format_table[n_formats].format = GST_VIDEO_FORMAT_Y210;
645   priv->format_table[n_formats].resource_format[0] =
646       DXGI_FORMAT_R16G16B16A16_UNORM;
647   if (can_support_format (self, DXGI_FORMAT_Y210,
648           D3D11_FORMAT_SUPPORT_SHADER_SAMPLE))
649     priv->format_table[n_formats].dxgi_format = DXGI_FORMAT_Y210;
650   else
651     priv->format_table[n_formats].dxgi_format = DXGI_FORMAT_UNKNOWN;
652   n_formats++;
653 #endif
654 }
655
656 static void
657 gst_d3d11_device_get_property (GObject * object, guint prop_id,
658     GValue * value, GParamSpec * pspec)
659 {
660   GstD3D11Device *self = GST_D3D11_DEVICE (object);
661   GstD3D11DevicePrivate *priv = self->priv;
662
663   switch (prop_id) {
664     case PROP_ADAPTER:
665       g_value_set_uint (value, priv->adapter);
666       break;
667     case PROP_DEVICE_ID:
668       g_value_set_uint (value, priv->device_id);
669       break;
670     case PROP_VENDOR_ID:
671       g_value_set_uint (value, priv->vendor_id);
672       break;
673     case PROP_HARDWARE:
674       g_value_set_boolean (value, priv->hardware);
675       break;
676     case PROP_DESCRIPTION:
677       g_value_set_string (value, priv->description);
678       break;
679     case PROP_ADAPTER_LUID:
680       g_value_set_int64 (value, priv->adapter_luid);
681       break;
682     default:
683       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
684       break;
685   }
686 }
687
688 static void
689 gst_d3d11_device_dispose (GObject * object)
690 {
691   GstD3D11Device *self = GST_D3D11_DEVICE (object);
692   GstD3D11DevicePrivate *priv = self->priv;
693
694   GST_LOG_OBJECT (self, "dispose");
695
696   GST_D3D11_CLEAR_COM (priv->device5);
697   GST_D3D11_CLEAR_COM (priv->device_context4);
698   GST_D3D11_CLEAR_COM (priv->video_device);
699   GST_D3D11_CLEAR_COM (priv->video_context);
700   GST_D3D11_CLEAR_COM (priv->device);
701   GST_D3D11_CLEAR_COM (priv->device_context);
702   GST_D3D11_CLEAR_COM (priv->factory);
703 #if HAVE_D3D11SDKLAYERS_H
704   if (priv->d3d11_debug) {
705     priv->d3d11_debug->ReportLiveDeviceObjects ((D3D11_RLDO_FLAGS)
706         GST_D3D11_RLDO_FLAGS);
707   }
708   GST_D3D11_CLEAR_COM (priv->d3d11_debug);
709
710   if (priv->d3d11_info_queue)
711     gst_d3d11_device_d3d11_debug (self, __FILE__, GST_FUNCTION, __LINE__);
712
713   GST_D3D11_CLEAR_COM (priv->d3d11_info_queue);
714 #endif
715
716 #if HAVE_DXGIDEBUG_H
717   if (priv->dxgi_debug) {
718     priv->dxgi_debug->ReportLiveObjects (DXGI_DEBUG_ALL,
719         (DXGI_DEBUG_RLO_FLAGS) GST_D3D11_RLDO_FLAGS);
720   }
721   GST_D3D11_CLEAR_COM (priv->dxgi_debug);
722
723   if (priv->dxgi_info_queue)
724     gst_d3d11_device_dxgi_debug (self, __FILE__, GST_FUNCTION, __LINE__);
725
726   GST_D3D11_CLEAR_COM (priv->dxgi_info_queue);
727 #endif
728
729   G_OBJECT_CLASS (parent_class)->dispose (object);
730 }
731
732 static void
733 gst_d3d11_device_finalize (GObject * object)
734 {
735   GstD3D11Device *self = GST_D3D11_DEVICE (object);
736   GstD3D11DevicePrivate *priv = self->priv;
737
738   GST_LOG_OBJECT (self, "finalize");
739
740   g_array_unref (priv->format_table);
741   DeleteCriticalSection (&priv->extern_lock);
742   g_free (priv->description);
743
744   G_OBJECT_CLASS (parent_class)->finalize (object);
745 }
746
747 typedef enum
748 {
749   DEVICE_CONSTRUCT_FOR_ADAPTER_INDEX,
750   DEVICE_CONSTRUCT_FOR_ADAPTER_LUID,
751   DEVICE_CONSTRUCT_WRAPPED,
752 } GstD3D11DeviceConstructType;
753
754 typedef struct _GstD3D11DeviceConstructData
755 {
756   union
757   {
758     guint adapter_index;
759     gint64 adapter_luid;
760     ID3D11Device *device;
761   } data;
762   GstD3D11DeviceConstructType type;
763   UINT create_flags;
764 } GstD3D11DeviceConstructData;
765
766 static HRESULT
767 _gst_d3d11_device_get_adapter (const GstD3D11DeviceConstructData * data,
768     IDXGIFactory1 * factory, guint * index, DXGI_ADAPTER_DESC * adapter_desc,
769     IDXGIAdapter1 ** dxgi_adapter)
770 {
771   HRESULT hr = S_OK;
772   ComPtr < IDXGIAdapter1 > adapter1;
773   DXGI_ADAPTER_DESC desc;
774
775   switch (data->type) {
776     case DEVICE_CONSTRUCT_FOR_ADAPTER_INDEX:
777     {
778       hr = factory->EnumAdapters1 (data->data.adapter_index, &adapter1);
779       if (FAILED (hr))
780         return hr;
781
782       hr = adapter1->GetDesc (&desc);
783       if (FAILED (hr))
784         return hr;
785
786       *index = data->data.adapter_index;
787       *adapter_desc = desc;
788       *dxgi_adapter = adapter1.Detach ();
789
790       return S_OK;
791     }
792     case DEVICE_CONSTRUCT_FOR_ADAPTER_LUID:
793     {
794       for (guint i = 0;; i++) {
795         gint64 luid;
796
797         adapter1 = nullptr;
798
799         hr = factory->EnumAdapters1 (i, &adapter1);
800         if (FAILED (hr))
801           return hr;
802
803         hr = adapter1->GetDesc (&desc);
804         if (FAILED (hr))
805           continue;
806
807         luid = gst_d3d11_luid_to_int64 (&desc.AdapterLuid);
808         if (luid != data->data.adapter_luid)
809           continue;
810
811         *index = i;
812         *adapter_desc = desc;
813         *dxgi_adapter = adapter1.Detach ();
814
815         return S_OK;
816       }
817
818       return E_FAIL;
819     }
820     case DEVICE_CONSTRUCT_WRAPPED:
821     {
822       ComPtr < IDXGIDevice > dxgi_device;
823       ComPtr < IDXGIAdapter > adapter;
824       ID3D11Device *device = data->data.device;
825       guint luid;
826
827       hr = device->QueryInterface (IID_PPV_ARGS (&dxgi_device));
828       if (FAILED (hr))
829         return hr;
830
831       hr = dxgi_device->GetAdapter (&adapter);
832       if (FAILED (hr))
833         return hr;
834
835       hr = adapter.As (&adapter1);
836       if (FAILED (hr))
837         return hr;
838
839       hr = adapter1->GetDesc (&desc);
840       if (FAILED (hr))
841         return hr;
842
843       luid = gst_d3d11_luid_to_int64 (&desc.AdapterLuid);
844
845       for (guint i = 0;; i++) {
846         DXGI_ADAPTER_DESC tmp_desc;
847         ComPtr < IDXGIAdapter1 > tmp;
848
849         hr = factory->EnumAdapters1 (i, &tmp);
850         if (FAILED (hr))
851           return hr;
852
853         hr = tmp->GetDesc (&tmp_desc);
854         if (FAILED (hr))
855           continue;
856
857         if (luid != gst_d3d11_luid_to_int64 (&tmp_desc.AdapterLuid))
858           continue;
859
860         *index = i;
861         *adapter_desc = desc;
862         *dxgi_adapter = adapter1.Detach ();
863
864         return S_OK;
865       }
866
867       return E_FAIL;
868     }
869     default:
870       g_assert_not_reached ();
871       break;
872   }
873
874   return E_FAIL;
875 }
876
877 static void
878 gst_d3d11_device_setup_debug_layer (GstD3D11Device * self)
879 {
880 #if HAVE_DXGIDEBUG_H
881   if (gst_debug_category_get_threshold (gst_d3d11_debug_layer_debug) >
882       GST_LEVEL_ERROR) {
883     GstD3D11DevicePrivate *priv = self->priv;
884
885     if (gst_d3d11_device_enable_dxgi_debug ()) {
886       IDXGIDebug *debug = nullptr;
887       IDXGIInfoQueue *info_queue = nullptr;
888       HRESULT hr;
889
890       GST_CAT_INFO_OBJECT (gst_d3d11_debug_layer_debug, self,
891           "dxgi debug library was loaded");
892       hr = gst_d3d11_device_dxgi_get_device_interface (IID_PPV_ARGS (&debug));
893
894       if (SUCCEEDED (hr)) {
895         GST_CAT_INFO_OBJECT (gst_d3d11_debug_layer_debug, self,
896             "IDXGIDebug interface available");
897         priv->dxgi_debug = debug;
898
899         hr = gst_d3d11_device_dxgi_get_device_interface (IID_PPV_ARGS
900             (&info_queue));
901         if (SUCCEEDED (hr)) {
902           GST_CAT_INFO_OBJECT (gst_d3d11_debug_layer_debug, self,
903               "IDXGIInfoQueue interface available");
904           priv->dxgi_info_queue = info_queue;
905         }
906       }
907     } else {
908       GST_CAT_INFO_OBJECT (gst_d3d11_debug_layer_debug, self,
909           "couldn't load dxgi debug library");
910     }
911   }
912 #endif
913
914 #if HAVE_D3D11SDKLAYERS_H
915   if ((self->priv->create_flags & D3D11_CREATE_DEVICE_DEBUG) != 0) {
916     GstD3D11DevicePrivate *priv = self->priv;
917     ID3D11Debug *debug;
918     ID3D11InfoQueue *info_queue;
919     HRESULT hr;
920
921     hr = priv->device->QueryInterface (IID_PPV_ARGS (&debug));
922
923     if (SUCCEEDED (hr)) {
924       GST_CAT_INFO_OBJECT (gst_d3d11_debug_layer_debug, self,
925           "D3D11Debug interface available");
926       priv->d3d11_debug = debug;
927
928       hr = priv->device->QueryInterface (IID_PPV_ARGS (&info_queue));
929       if (SUCCEEDED (hr)) {
930         GST_CAT_INFO_OBJECT (gst_d3d11_debug_layer_debug, self,
931             "ID3D11InfoQueue interface available");
932         priv->d3d11_info_queue = info_queue;
933       }
934     }
935   }
936 #endif
937 }
938
939 static GstD3D11Device *
940 gst_d3d11_device_new_internal (const GstD3D11DeviceConstructData * data)
941 {
942   ComPtr < IDXGIAdapter1 > adapter;
943   ComPtr < IDXGIFactory1 > factory;
944   ComPtr < ID3D11Device > device;
945   ComPtr < ID3D11Device5 > device5;
946   ComPtr < ID3D11DeviceContext > device_context;
947   ComPtr < ID3D11DeviceContext4 > device_context4;
948   HRESULT hr;
949   UINT create_flags;
950   guint adapter_index = 0;
951   DXGI_ADAPTER_DESC adapter_desc;
952   static const D3D_FEATURE_LEVEL feature_levels[] = {
953     D3D_FEATURE_LEVEL_11_1,
954     D3D_FEATURE_LEVEL_11_0,
955     D3D_FEATURE_LEVEL_10_1,
956     D3D_FEATURE_LEVEL_10_0,
957     D3D_FEATURE_LEVEL_9_3,
958     D3D_FEATURE_LEVEL_9_2,
959     D3D_FEATURE_LEVEL_9_1
960   };
961   D3D_FEATURE_LEVEL selected_level;
962
963   debug_init_once ();
964
965   hr = CreateDXGIFactory1 (IID_PPV_ARGS (&factory));
966   if (!gst_d3d11_result (hr, NULL)) {
967     GST_WARNING ("cannot create dxgi factory, hr: 0x%x", (guint) hr);
968     return nullptr;
969   }
970
971   create_flags = 0;
972   if (data->type != DEVICE_CONSTRUCT_WRAPPED) {
973     create_flags = data->create_flags;
974 #if HAVE_D3D11SDKLAYERS_H
975     if (gst_debug_category_get_threshold (gst_d3d11_debug_layer_debug) >
976         GST_LEVEL_ERROR) {
977       /* DirectX SDK should be installed on system for this */
978       if (gst_d3d11_device_enable_d3d11_debug ()) {
979         GST_CAT_INFO (gst_d3d11_debug_layer_debug,
980             "d3d11 debug library was loaded");
981         create_flags |= D3D11_CREATE_DEVICE_DEBUG;
982       } else {
983         GST_CAT_INFO (gst_d3d11_debug_layer_debug,
984             "couldn't load d3d11 debug library");
985       }
986     }
987 #endif
988   }
989
990   /* Ensure valid device handle */
991   if (data->type == DEVICE_CONSTRUCT_WRAPPED) {
992     ID3D11Device *external_device = data->data.device;
993
994     hr = external_device->QueryInterface (IID_PPV_ARGS (&device));
995     if (FAILED (hr)) {
996       GST_WARNING ("Not a valid external ID3D11Device handle");
997       return nullptr;
998     }
999
1000     device->GetImmediateContext (&device_context);
1001   }
1002
1003   hr = _gst_d3d11_device_get_adapter (data, factory.Get (), &adapter_index,
1004       &adapter_desc, &adapter);
1005   if (FAILED (hr)) {
1006     GST_INFO ("Failed to get DXGI adapter");
1007     return nullptr;
1008   }
1009
1010   if (data->type != DEVICE_CONSTRUCT_WRAPPED) {
1011     hr = D3D11CreateDevice (adapter.Get (), D3D_DRIVER_TYPE_UNKNOWN,
1012         NULL, create_flags, feature_levels, G_N_ELEMENTS (feature_levels),
1013         D3D11_SDK_VERSION, &device, &selected_level, &device_context);
1014
1015     if (FAILED (hr)) {
1016       /* Retry if the system could not recognize D3D_FEATURE_LEVEL_11_1 */
1017       hr = D3D11CreateDevice (adapter.Get (), D3D_DRIVER_TYPE_UNKNOWN,
1018           NULL, create_flags, &feature_levels[1],
1019           G_N_ELEMENTS (feature_levels) - 1, D3D11_SDK_VERSION, &device,
1020           &selected_level, &device_context);
1021     }
1022
1023     /* if D3D11_CREATE_DEVICE_DEBUG was enabled but couldn't create device,
1024      * try it without the flag again */
1025     if (FAILED (hr) && (create_flags & D3D11_CREATE_DEVICE_DEBUG) != 0) {
1026       create_flags &= ~D3D11_CREATE_DEVICE_DEBUG;
1027
1028       hr = D3D11CreateDevice (adapter.Get (), D3D_DRIVER_TYPE_UNKNOWN,
1029           NULL, create_flags, feature_levels, G_N_ELEMENTS (feature_levels),
1030           D3D11_SDK_VERSION, &device, &selected_level, &device_context);
1031
1032       if (FAILED (hr)) {
1033         /* Retry if the system could not recognize D3D_FEATURE_LEVEL_11_1 */
1034         hr = D3D11CreateDevice (adapter.Get (), D3D_DRIVER_TYPE_UNKNOWN,
1035             NULL, create_flags, &feature_levels[1],
1036             G_N_ELEMENTS (feature_levels) - 1, D3D11_SDK_VERSION, &device,
1037             &selected_level, &device_context);
1038       }
1039     }
1040   }
1041
1042   if (FAILED (hr)) {
1043     switch (data->type) {
1044       case DEVICE_CONSTRUCT_FOR_ADAPTER_INDEX:
1045       {
1046         GST_INFO ("Failed to create d3d11 device for adapter index %d"
1047             " with flags 0x%x, hr: 0x%x", data->data.adapter_index,
1048             create_flags, (guint) hr);
1049         return nullptr;
1050       }
1051       case DEVICE_CONSTRUCT_FOR_ADAPTER_LUID:
1052       {
1053         GST_WARNING ("Failed to create d3d11 device for adapter luid %"
1054             G_GINT64_FORMAT " with flags 0x%x, hr: 0x%x",
1055             data->data.adapter_luid, create_flags, (guint) hr);
1056         return nullptr;
1057       }
1058       default:
1059         break;
1060     }
1061
1062     return nullptr;
1063   }
1064
1065   GstD3D11Device *self = nullptr;
1066   GstD3D11DevicePrivate *priv;
1067
1068   self = (GstD3D11Device *) g_object_new (GST_TYPE_D3D11_DEVICE, nullptr);
1069   gst_object_ref_sink (self);
1070
1071   priv = self->priv;
1072
1073   hr = device.As (&device5);
1074   if (SUCCEEDED (hr))
1075     hr = device_context.As (&device_context4);
1076   if (SUCCEEDED (hr)) {
1077     priv->device5 = device5.Detach ();
1078     priv->device_context4 = device_context4.Detach ();
1079   }
1080
1081   priv->adapter = adapter_index;
1082   priv->device = device.Detach ();
1083   priv->device_context = device_context.Detach ();
1084   priv->factory = factory.Detach ();
1085
1086   priv->vendor_id = adapter_desc.VendorId;
1087   priv->device_id = adapter_desc.DeviceId;
1088   priv->description = g_utf16_to_utf8 ((gunichar2 *) adapter_desc.Description,
1089       -1, nullptr, nullptr, nullptr);
1090   priv->adapter_luid = gst_d3d11_luid_to_int64 (&adapter_desc.AdapterLuid);
1091
1092   DXGI_ADAPTER_DESC1 desc1;
1093   hr = adapter->GetDesc1 (&desc1);
1094
1095   /* DXGI_ADAPTER_FLAG_SOFTWARE is missing in dxgi.h of mingw */
1096   if (SUCCEEDED (hr) && (desc1.Flags & 0x2) != 0x2)
1097     priv->hardware = TRUE;
1098
1099   priv->create_flags = create_flags;
1100   gst_d3d11_device_setup_format_table (self);
1101   gst_d3d11_device_setup_debug_layer (self);
1102
1103   BOOL ret = QueryPerformanceFrequency (&priv->frequency);
1104   g_assert (ret);
1105
1106   return self;
1107 }
1108
1109 /**
1110  * gst_d3d11_device_new:
1111  * @adapter_index: the index of adapter for creating d3d11 device
1112  * @flags: a D3D11_CREATE_DEVICE_FLAG value used for creating d3d11 device
1113  *
1114  * Returns: (transfer full) (nullable): a new #GstD3D11Device for @adapter_index
1115  * or %NULL when failed to create D3D11 device with given adapter index.
1116  *
1117  * Since: 1.22
1118  */
1119 GstD3D11Device *
1120 gst_d3d11_device_new (guint adapter_index, guint flags)
1121 {
1122   GstD3D11DeviceConstructData data;
1123
1124   data.data.adapter_index = adapter_index;
1125   data.type = DEVICE_CONSTRUCT_FOR_ADAPTER_INDEX;
1126   data.create_flags = flags;
1127
1128   return gst_d3d11_device_new_internal (&data);
1129 }
1130
1131 /**
1132  * gst_d3d11_device_new_for_adapter_luid:
1133  * @adapter_luid: an int64 representation of the DXGI adapter LUID
1134  * @flags: a D3D11_CREATE_DEVICE_FLAG value used for creating d3d11 device
1135  *
1136  * Returns: (transfer full) (nullable): a new #GstD3D11Device for @adapter_luid
1137  * or %NULL when failed to create D3D11 device with given adapter luid.
1138  *
1139  * Since: 1.22
1140  */
1141 GstD3D11Device *
1142 gst_d3d11_device_new_for_adapter_luid (gint64 adapter_luid, guint flags)
1143 {
1144   GstD3D11DeviceConstructData data;
1145
1146   data.data.adapter_luid = adapter_luid;
1147   data.type = DEVICE_CONSTRUCT_FOR_ADAPTER_LUID;
1148   data.create_flags = flags;
1149
1150   return gst_d3d11_device_new_internal (&data);
1151 }
1152
1153 /**
1154  * gst_d3d11_device_new_wrapped:
1155  * @device: (transfer none): an existing ID3D11Device handle
1156  *
1157  * Returns: (transfer full) (nullable): a new #GstD3D11Device for @device
1158  * or %NULL if an error occurred
1159  *
1160  * Since: 1.22
1161  */
1162 GstD3D11Device *
1163 gst_d3d11_device_new_wrapped (ID3D11Device * device)
1164 {
1165   GstD3D11DeviceConstructData data;
1166
1167   g_return_val_if_fail (device != nullptr, nullptr);
1168
1169   data.data.device = device;
1170   data.type = DEVICE_CONSTRUCT_WRAPPED;
1171   data.create_flags = 0;
1172
1173   return gst_d3d11_device_new_internal (&data);
1174 }
1175
1176 /**
1177  * gst_d3d11_device_get_device_handle:
1178  * @device: a #GstD3D11Device
1179  *
1180  * Used for various D3D11 APIs directly. Caller must not destroy returned device
1181  * object.
1182  *
1183  * Returns: (transfer none): the ID3D11Device handle
1184  *
1185  * Since: 1.22
1186  */
1187 ID3D11Device *
1188 gst_d3d11_device_get_device_handle (GstD3D11Device * device)
1189 {
1190   g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
1191
1192   return device->priv->device;
1193 }
1194
1195 /**
1196  * gst_d3d11_device_get_device_context_handle:
1197  * @device: a #GstD3D11Device
1198  *
1199  * Used for various D3D11 APIs directly. Caller must not destroy returned device
1200  * object. Any ID3D11DeviceContext call needs to be protected by
1201  * gst_d3d11_device_lock() and gst_d3d11_device_unlock() method.
1202  *
1203  * Returns: (transfer none): the immeidate ID3D11DeviceContext handle
1204  *
1205  * Since: 1.22
1206  */
1207 ID3D11DeviceContext *
1208 gst_d3d11_device_get_device_context_handle (GstD3D11Device * device)
1209 {
1210   g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
1211
1212   return device->priv->device_context;
1213 }
1214
1215 /**
1216  * gst_d3d11_device_get_dxgi_factory_handle:
1217  * @device: a #GstD3D11Device
1218  *
1219  * Used for various D3D11 APIs directly. Caller must not destroy returned device
1220  * object.
1221  *
1222  * Returns: (transfer none): the IDXGIFactory1 handle
1223  *
1224  * Since: 1.22
1225  */
1226 IDXGIFactory1 *
1227 gst_d3d11_device_get_dxgi_factory_handle (GstD3D11Device * device)
1228 {
1229   g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
1230
1231   return device->priv->factory;
1232 }
1233
1234 /**
1235  * gst_d3d11_device_get_video_device_handle:
1236  * @device: a #GstD3D11Device
1237  *
1238  * Used for various D3D11 APIs directly. Caller must not destroy returned device
1239  * object.
1240  *
1241  * Returns: (nullable) (transfer none) : the ID3D11VideoDevice handle or %NULL
1242  * if ID3D11VideoDevice is unavailable.
1243  *
1244  * Since: 1.22
1245  */
1246 ID3D11VideoDevice *
1247 gst_d3d11_device_get_video_device_handle (GstD3D11Device * device)
1248 {
1249   GstD3D11DevicePrivate *priv;
1250
1251   g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
1252
1253   priv = device->priv;
1254   GstD3D11SRWLockGuard lk (&priv->resource_lock);
1255   if (!priv->video_device) {
1256     HRESULT hr;
1257     ID3D11VideoDevice *video_device = NULL;
1258
1259     hr = priv->device->QueryInterface (IID_PPV_ARGS (&video_device));
1260     if (gst_d3d11_result (hr, device))
1261       priv->video_device = video_device;
1262   }
1263
1264   return priv->video_device;
1265 }
1266
1267 /**
1268  * gst_d3d11_device_get_video_context_handle:
1269  * @device: a #GstD3D11Device
1270  *
1271  * Used for various D3D11 APIs directly. Caller must not destroy returned device
1272  * object.
1273  *
1274  * Returns: (nullable) (transfer none): the ID3D11VideoContext handle or %NULL
1275  * if ID3D11VideoContext is unavailable.
1276  *
1277  * Since: 1.22
1278  */
1279 ID3D11VideoContext *
1280 gst_d3d11_device_get_video_context_handle (GstD3D11Device * device)
1281 {
1282   GstD3D11DevicePrivate *priv;
1283
1284   g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
1285
1286   priv = device->priv;
1287   GstD3D11SRWLockGuard lk (&priv->resource_lock);
1288   if (!priv->video_context) {
1289     HRESULT hr;
1290     ID3D11VideoContext *video_context = NULL;
1291
1292     hr = priv->device_context->QueryInterface (IID_PPV_ARGS (&video_context));
1293     if (gst_d3d11_result (hr, device))
1294       priv->video_context = video_context;
1295   }
1296
1297   return priv->video_context;
1298 }
1299
1300 /**
1301  * gst_d3d11_device_lock:
1302  * @device: a #GstD3D11Device
1303  *
1304  * Take lock for @device. Any thread-unsafe API call needs to be
1305  * protected by this method. This call must be paired with
1306  * gst_d3d11_device_unlock()
1307  *
1308  * Since: 1.22
1309  */
1310 void
1311 gst_d3d11_device_lock (GstD3D11Device * device)
1312 {
1313   GstD3D11DevicePrivate *priv;
1314
1315   g_return_if_fail (GST_IS_D3D11_DEVICE (device));
1316
1317   priv = device->priv;
1318
1319   GST_TRACE_OBJECT (device, "device locking");
1320   EnterCriticalSection (&priv->extern_lock);
1321   GST_TRACE_OBJECT (device, "device locked");
1322 }
1323
1324 /**
1325  * gst_d3d11_device_unlock:
1326  * @device: a #GstD3D11Device
1327  *
1328  * Release lock for @device. This call must be paired with
1329  * gst_d3d11_device_lock()
1330  *
1331  * Since: 1.22
1332  */
1333 void
1334 gst_d3d11_device_unlock (GstD3D11Device * device)
1335 {
1336   GstD3D11DevicePrivate *priv;
1337
1338   g_return_if_fail (GST_IS_D3D11_DEVICE (device));
1339
1340   priv = device->priv;
1341
1342   LeaveCriticalSection (&priv->extern_lock);
1343   GST_TRACE_OBJECT (device, "device unlocked");
1344 }
1345
1346 /**
1347  * gst_d3d11_device_get_format:
1348  * @device: a #GstD3D11Device
1349  * @format: a #GstVideoFormat
1350  * @device_format: (out caller-allocates) (nullable): a #GstD3D11Format
1351  *
1352  * Converts @format to #GstD3D11Format if the @format is supported
1353  * by device
1354  *
1355  * Returns: %TRUE if @format is supported by @device
1356  *
1357  * Since: 1.22
1358  */
1359 gboolean
1360 gst_d3d11_device_get_format (GstD3D11Device * device, GstVideoFormat format,
1361     GstD3D11Format * device_format)
1362 {
1363   GstD3D11DevicePrivate *priv;
1364
1365   g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), FALSE);
1366
1367   priv = device->priv;
1368
1369   for (guint i = 0; i < priv->format_table->len; i++) {
1370     const GstD3D11Format *d3d11_fmt =
1371         &g_array_index (priv->format_table, GstD3D11Format, i);
1372
1373     if (d3d11_fmt->format != format)
1374       continue;
1375
1376     if (device_format)
1377       *device_format = *d3d11_fmt;
1378
1379     return TRUE;
1380   }
1381
1382   if (device_format)
1383     gst_d3d11_format_init (device_format);
1384
1385   return FALSE;
1386 }
1387
1388 GST_DEFINE_MINI_OBJECT_TYPE (GstD3D11Fence, gst_d3d11_fence);
1389
1390 struct _GstD3D11FencePrivate
1391 {
1392   UINT64 fence_value;
1393   ID3D11Fence *fence;
1394   ID3D11Query *query;
1395   HANDLE event_handle;
1396   gboolean signalled;
1397   gboolean synced;
1398 };
1399
1400 static void
1401 _gst_d3d11_fence_free (GstD3D11Fence * fence)
1402 {
1403   GstD3D11FencePrivate *priv = fence->priv;
1404
1405   GST_D3D11_CLEAR_COM (priv->fence);
1406   GST_D3D11_CLEAR_COM (priv->query);
1407   if (priv->event_handle)
1408     CloseHandle (priv->event_handle);
1409
1410   gst_clear_object (&fence->device);
1411
1412   g_free (priv);
1413   g_free (fence);
1414 }
1415
1416 /**
1417  * gst_d3d11_device_create_fence:
1418  * @device: a #GstD3D11Device
1419  *
1420  * Creates fence object (i.e., ID3D11Fence) if available, otherwise
1421  * ID3D11Query with D3D11_QUERY_EVENT is created.
1422  *
1423  * Returns: a #GstD3D11Fence object
1424  *
1425  * Since: 1.22
1426  */
1427 GstD3D11Fence *
1428 gst_d3d11_device_create_fence (GstD3D11Device * device)
1429 {
1430   GstD3D11DevicePrivate *priv;
1431   ID3D11Fence *fence = nullptr;
1432   HRESULT hr = S_OK;
1433   GstD3D11Fence *self;
1434
1435   g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), nullptr);
1436
1437   priv = device->priv;
1438
1439   if (priv->device5 && priv->device_context4) {
1440     hr = priv->device5->CreateFence (0, D3D11_FENCE_FLAG_NONE,
1441         IID_PPV_ARGS (&fence));
1442
1443     if (!gst_d3d11_result (hr, device))
1444       GST_WARNING_OBJECT (device, "Failed to create fence object");
1445   }
1446
1447   self = g_new0 (GstD3D11Fence, 1);
1448   self->device = (GstD3D11Device *) gst_object_ref (device);
1449   self->priv = g_new0 (GstD3D11FencePrivate, 1);
1450   self->priv->fence = fence;
1451   if (fence) {
1452     self->priv->event_handle = CreateEventEx (nullptr, nullptr,
1453         0, EVENT_ALL_ACCESS);
1454   }
1455
1456   gst_mini_object_init (GST_MINI_OBJECT_CAST (self), 0,
1457       GST_TYPE_D3D11_FENCE, nullptr, nullptr,
1458       (GstMiniObjectFreeFunction) _gst_d3d11_fence_free);
1459
1460   return self;
1461 }
1462
1463 /**
1464  * gst_d3d11_fence_signal:
1465  * @fence: a #GstD3D11Fence
1466  *
1467  * Sets sync point to fence for waiting.
1468  * Must be called with gst_d3d11_device_lock() held
1469  *
1470  * Returns: %TRUE if successful
1471  *
1472  * Since: 1.22
1473  */
1474 gboolean
1475 gst_d3d11_fence_signal (GstD3D11Fence * fence)
1476 {
1477   HRESULT hr = S_OK;
1478   GstD3D11Device *device;
1479   GstD3D11DevicePrivate *device_priv;
1480   GstD3D11FencePrivate *priv;
1481
1482   g_return_val_if_fail (GST_IS_D3D11_FENCE (fence), FALSE);
1483
1484   device = fence->device;
1485   device_priv = device->priv;
1486   priv = fence->priv;
1487
1488   priv->signalled = FALSE;
1489   priv->synced = FALSE;
1490
1491   if (priv->fence) {
1492     priv->fence_value++;
1493
1494     GST_LOG_OBJECT (device, "Signals with fence value %" G_GUINT64_FORMAT,
1495         priv->fence_value);
1496
1497     hr = device_priv->device_context4->Signal (priv->fence, priv->fence_value);
1498     if (!gst_d3d11_result (hr, device)) {
1499       GST_ERROR_OBJECT (device, "Failed to signal fence value %"
1500           G_GUINT64_FORMAT, fence->priv->fence_value);
1501       return FALSE;
1502     }
1503   } else {
1504     D3D11_QUERY_DESC desc;
1505
1506     GST_D3D11_CLEAR_COM (priv->query);
1507
1508     desc.Query = D3D11_QUERY_EVENT;
1509     desc.MiscFlags = 0;
1510
1511     GST_LOG_OBJECT (device, "Creating query object");
1512
1513     hr = device_priv->device->CreateQuery (&desc, &priv->query);
1514     if (!gst_d3d11_result (hr, device)) {
1515       GST_ERROR_OBJECT (device, "Failed to create query object");
1516       return FALSE;
1517     }
1518
1519     device_priv->device_context->End (priv->query);
1520   }
1521
1522   priv->signalled = TRUE;
1523
1524   return TRUE;
1525 }
1526
1527 /**
1528  * gst_d3d11_fence_wait:
1529  * @fence: a #GstD3D11Fence
1530  *
1531  * Waits until previously issued GPU commands have been completed
1532  * Must be called with gst_d3d11_device_lock() held
1533  *
1534  * Returns: %TRUE if successful
1535  *
1536  * Since: 1.22
1537  */
1538 gboolean
1539 gst_d3d11_fence_wait (GstD3D11Fence * fence)
1540 {
1541   HRESULT hr = S_OK;
1542   GstD3D11Device *device;
1543   GstD3D11DevicePrivate *device_priv;
1544   GstD3D11FencePrivate *priv;
1545   BOOL timer_ret;
1546   LARGE_INTEGER current_time, now;
1547
1548   g_return_val_if_fail (GST_IS_D3D11_FENCE (fence), FALSE);
1549
1550   device = fence->device;
1551   device_priv = device->priv;
1552   priv = fence->priv;
1553
1554   if (!priv->signalled) {
1555     GST_DEBUG_OBJECT (device, "Fence is not signalled, nothing to wait");
1556     return TRUE;
1557   }
1558
1559   if (priv->synced) {
1560     GST_DEBUG_OBJECT (device, "Already synced");
1561     return TRUE;
1562   }
1563
1564   timer_ret = QueryPerformanceCounter (&current_time);
1565   g_assert (timer_ret);
1566
1567   now = current_time;
1568
1569   if (priv->fence) {
1570     GST_LOG_OBJECT (device, "Waiting fence value %" G_GUINT64_FORMAT,
1571         priv->fence_value);
1572
1573     if (fence->priv->fence->GetCompletedValue () < fence->priv->fence_value) {
1574       hr = fence->priv->fence->SetEventOnCompletion (fence->priv->fence_value,
1575           fence->priv->event_handle);
1576       if (!gst_d3d11_result (hr, device)) {
1577         GST_WARNING_OBJECT (device, "Failed set event handle");
1578         return FALSE;
1579       }
1580
1581       /* 20 seconds should be sufficient time */
1582       DWORD ret = WaitForSingleObject (priv->event_handle, 20000);
1583       if (ret != WAIT_OBJECT_0) {
1584         GST_WARNING_OBJECT (device,
1585             "Failed to wait object, ret 0x%x", (guint) ret);
1586         return FALSE;
1587       }
1588     }
1589   } else {
1590     LONGLONG timeout;
1591     BOOL sync_done = FALSE;
1592
1593     g_assert (priv->query != nullptr);
1594
1595     /* 20 sec timeout */
1596     timeout = now.QuadPart + 20 * device_priv->frequency.QuadPart;
1597
1598     GST_LOG_OBJECT (device, "Waiting event");
1599
1600     while (now.QuadPart < timeout && !sync_done) {
1601       hr = device_priv->device_context->GetData (priv->query,
1602           &sync_done, sizeof (BOOL), 0);
1603       if (FAILED (hr)) {
1604         GST_WARNING_OBJECT (device, "Failed to get event data");
1605         return FALSE;
1606       }
1607
1608       if (sync_done)
1609         break;
1610
1611       g_thread_yield ();
1612       timer_ret = QueryPerformanceCounter (&now);
1613       g_assert (timer_ret);
1614     }
1615
1616     if (!sync_done) {
1617       GST_WARNING_OBJECT (device, "Timeout");
1618       return FALSE;
1619     }
1620
1621     GST_D3D11_CLEAR_COM (priv->query);
1622   }
1623
1624 #ifndef GST_DISABLE_GST_DEBUG
1625   if (gst_debug_category_get_threshold (GST_CAT_DEFAULT) >= GST_LEVEL_LOG) {
1626     GstClockTime elapsed;
1627
1628     QueryPerformanceCounter (&now);
1629     elapsed = gst_util_uint64_scale (now.QuadPart - current_time.QuadPart,
1630         GST_SECOND, device_priv->frequency.QuadPart);
1631
1632     GST_LOG_OBJECT (device, "Wait done, elapsed %" GST_TIME_FORMAT,
1633         GST_TIME_ARGS (elapsed));
1634   }
1635 #endif
1636
1637   priv->signalled = FALSE;
1638   priv->synced = TRUE;
1639
1640   return TRUE;
1641 }