• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include "gstd3d11utils.h"
25 #include "gstd3d11device.h"
26 #include "gstd3d11_private.h"
27 
28 #include <windows.h>
29 #include <versionhelpers.h>
30 
31 GST_DEBUG_CATEGORY_STATIC (GST_CAT_CONTEXT);
32 #ifndef GST_DISABLE_GST_DEBUG
33 #define GST_CAT_DEFAULT ensure_debug_category()
34 static GstDebugCategory *
ensure_debug_category(void)35 ensure_debug_category (void)
36 {
37   static gsize cat_gonce = 0;
38 
39   if (g_once_init_enter (&cat_gonce)) {
40     gsize cat_done;
41 
42     cat_done = (gsize) _gst_debug_category_new ("d3d11utils", 0,
43         "d3d11 utility functions");
44 
45     g_once_init_leave (&cat_gonce, cat_done);
46   }
47 
48   return (GstDebugCategory *) cat_gonce;
49 }
50 #else
51 #define ensure_debug_category() /* NOOP */
52 #endif /* GST_DISABLE_GST_DEBUG */
53 
54 static void
_init_context_debug(void)55 _init_context_debug (void)
56 {
57   static gsize _init = 0;
58 
59   if (g_once_init_enter (&_init)) {
60     GST_DEBUG_CATEGORY_GET (GST_CAT_CONTEXT, "GST_CONTEXT");
61     g_once_init_leave (&_init, 1);
62   }
63 }
64 
65 /**
66  * gst_d3d11_handle_set_context:
67  * @element: a #GstElement
68  * @context: a #GstContext
69  * @adapter_index: a DXGI adapter index
70  * @device: (inout) (transfer full): location of a #GstD3D11Device
71  *
72  * Helper function for implementing #GstElementClass.set_context() in
73  * D3D11 capable elements.
74  *
75  * Retrieve's the #GstD3D11Device in @context and places the result in @device.
76  * @device is accepted if @adapter_index is equal to -1 (accept any device)
77  * or equal to that of @device
78  *
79  * Returns: whether the @device could be set successfully
80  *
81  * Since: 1.20
82  */
83 gboolean
gst_d3d11_handle_set_context(GstElement * element,GstContext * context,gint adapter_index,GstD3D11Device ** device)84 gst_d3d11_handle_set_context (GstElement * element, GstContext * context,
85     gint adapter_index, GstD3D11Device ** device)
86 {
87   const gchar *context_type;
88 
89   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
90   g_return_val_if_fail (device != NULL, FALSE);
91 
92   _init_context_debug ();
93 
94   if (!context)
95     return FALSE;
96 
97   context_type = gst_context_get_context_type (context);
98   if (g_strcmp0 (context_type, GST_D3D11_DEVICE_HANDLE_CONTEXT_TYPE) == 0) {
99     const GstStructure *str;
100     GstD3D11Device *other_device = NULL;
101     guint other_adapter_index = 0;
102 
103     /* If we had device already, will not replace it */
104     if (*device)
105       return TRUE;
106 
107     str = gst_context_get_structure (context);
108 
109     if (gst_structure_get (str, "device", GST_TYPE_D3D11_DEVICE,
110             &other_device, "adapter", G_TYPE_UINT, &other_adapter_index,
111             NULL)) {
112       if (adapter_index == -1 || (guint) adapter_index == other_adapter_index) {
113         GST_CAT_DEBUG_OBJECT (GST_CAT_CONTEXT,
114             element, "Found D3D11 device context");
115         *device = other_device;
116 
117         return TRUE;
118       }
119 
120       gst_object_unref (other_device);
121     }
122   }
123 
124   return FALSE;
125 }
126 
127 /**
128  * gst_d3d11_handle_set_context_for_adapter_luid:
129  * @element: a #GstElement
130  * @context: a #GstContext
131  * @adapter_luid: an int64 representation of DXGI adapter LUID
132  * @device: (inout) (transfer full): location of a #GstD3D11Device
133  *
134  * Helper function for implementing #GstElementClass.set_context() in
135  * D3D11 capable elements.
136  *
137  * Retrieve's the #GstD3D11Device in @context and places the result in @device.
138  * @device is accepted only when @adapter_index is equal to that of @device
139  *
140  * Returns: whether the @device could be set successfully
141  *
142  * Since: 1.20
143  */
144 gboolean
gst_d3d11_handle_set_context_for_adapter_luid(GstElement * element,GstContext * context,gint64 adapter_luid,GstD3D11Device ** device)145 gst_d3d11_handle_set_context_for_adapter_luid (GstElement * element,
146     GstContext * context, gint64 adapter_luid, GstD3D11Device ** device)
147 {
148   const gchar *context_type;
149 
150   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
151   g_return_val_if_fail (device != NULL, FALSE);
152 
153   _init_context_debug ();
154 
155   if (!context)
156     return FALSE;
157 
158   context_type = gst_context_get_context_type (context);
159   if (g_strcmp0 (context_type, GST_D3D11_DEVICE_HANDLE_CONTEXT_TYPE) == 0) {
160     const GstStructure *str;
161     GstD3D11Device *other_device = NULL;
162     gint64 other_adapter_luid = 0;
163 
164     /* If we had device already, will not replace it */
165     if (*device)
166       return TRUE;
167 
168     str = gst_context_get_structure (context);
169 
170     if (gst_structure_get (str, "device", GST_TYPE_D3D11_DEVICE,
171             &other_device, "adapter-luid", G_TYPE_INT64,
172             &other_adapter_luid, NULL)) {
173       if (adapter_luid == other_adapter_luid) {
174         GST_CAT_DEBUG_OBJECT (GST_CAT_CONTEXT,
175             element, "Found D3D11 device context");
176         *device = other_device;
177 
178         return TRUE;
179       }
180 
181       gst_object_unref (other_device);
182     }
183   }
184 
185   return FALSE;
186 }
187 
188 static void
context_set_d3d11_device(GstContext * context,GstD3D11Device * device)189 context_set_d3d11_device (GstContext * context, GstD3D11Device * device)
190 {
191   GstStructure *s;
192   guint adapter = 0;
193   guint device_id = 0;
194   guint vendor_id = 0;
195   gboolean hardware = FALSE;
196   gchar *desc = NULL;
197   gint64 adapter_luid = 0;
198 
199   g_return_if_fail (context != NULL);
200 
201   g_object_get (G_OBJECT (device), "adapter", &adapter, "device-id", &device_id,
202       "vendor-id", &vendor_id, "hardware", &hardware, "description", &desc,
203       "adapter-luid", &adapter_luid, NULL);
204 
205   GST_CAT_LOG (GST_CAT_CONTEXT,
206       "setting GstD3D11Device(%" GST_PTR_FORMAT
207       ") with adapter %d on context(%" GST_PTR_FORMAT ")",
208       device, adapter, context);
209 
210   s = gst_context_writable_structure (context);
211   gst_structure_set (s, "device", GST_TYPE_D3D11_DEVICE, device,
212       "adapter", G_TYPE_UINT, adapter,
213       "adapter-luid", G_TYPE_INT64, adapter_luid,
214       "device-id", G_TYPE_UINT, device_id,
215       "vendor-id", G_TYPE_UINT, vendor_id,
216       "hardware", G_TYPE_BOOLEAN, hardware,
217       "description", G_TYPE_STRING, GST_STR_NULL (desc), NULL);
218   g_free (desc);
219 }
220 
221 /**
222  * gst_d3d11_handle_context_query:
223  * @element: a #GstElement
224  * @query: a #GstQuery of type %GST_QUERY_CONTEXT
225  * @device: (transfer none) (nullable): a #GstD3D11Device
226  *
227  * Returns: Whether the @query was successfully responded to from the passed
228  *          @device.
229  *
230  * Since: 1.20
231  */
232 gboolean
gst_d3d11_handle_context_query(GstElement * element,GstQuery * query,GstD3D11Device * device)233 gst_d3d11_handle_context_query (GstElement * element, GstQuery * query,
234     GstD3D11Device * device)
235 {
236   const gchar *context_type;
237   GstContext *context, *old_context;
238 
239   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
240   g_return_val_if_fail (GST_IS_QUERY (query), FALSE);
241 
242   _init_context_debug ();
243 
244   GST_LOG_OBJECT (element, "handle context query %" GST_PTR_FORMAT, query);
245 
246   if (!device)
247     return FALSE;
248 
249   gst_query_parse_context_type (query, &context_type);
250   if (g_strcmp0 (context_type, GST_D3D11_DEVICE_HANDLE_CONTEXT_TYPE) != 0)
251     return FALSE;
252 
253   gst_query_parse_context (query, &old_context);
254   if (old_context)
255     context = gst_context_copy (old_context);
256   else
257     context = gst_context_new (GST_D3D11_DEVICE_HANDLE_CONTEXT_TYPE, TRUE);
258 
259   context_set_d3d11_device (context, device);
260   gst_query_set_context (query, context);
261   gst_context_unref (context);
262 
263   GST_DEBUG_OBJECT (element, "successfully set %" GST_PTR_FORMAT
264       " on %" GST_PTR_FORMAT, device, query);
265 
266   return TRUE;
267 }
268 
269 static gboolean
pad_query(const GValue * item,GValue * value,gpointer user_data)270 pad_query (const GValue * item, GValue * value, gpointer user_data)
271 {
272   GstPad *pad = (GstPad *) g_value_get_object (item);
273   GstQuery *query = (GstQuery *) user_data;
274   gboolean res;
275 
276   res = gst_pad_peer_query (pad, query);
277 
278   if (res) {
279     g_value_set_boolean (value, TRUE);
280     return FALSE;
281   }
282 
283   GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, pad, "pad peer query failed");
284   return TRUE;
285 }
286 
287 static gboolean
run_query(GstElement * element,GstQuery * query,GstPadDirection direction)288 run_query (GstElement * element, GstQuery * query, GstPadDirection direction)
289 {
290   GstIterator *it;
291   GstIteratorFoldFunction func = pad_query;
292   GValue res = { 0 };
293 
294   g_value_init (&res, G_TYPE_BOOLEAN);
295   g_value_set_boolean (&res, FALSE);
296 
297   /* Ask neighbor */
298   if (direction == GST_PAD_SRC)
299     it = gst_element_iterate_src_pads (element);
300   else
301     it = gst_element_iterate_sink_pads (element);
302 
303   while (gst_iterator_fold (it, func, &res, query) == GST_ITERATOR_RESYNC)
304     gst_iterator_resync (it);
305 
306   gst_iterator_free (it);
307 
308   return g_value_get_boolean (&res);
309 }
310 
311 static void
run_d3d11_context_query(GstElement * element,GstD3D11Device ** device)312 run_d3d11_context_query (GstElement * element, GstD3D11Device ** device)
313 {
314   GstQuery *query;
315   GstContext *ctxt = NULL;
316 
317   /* 1) Query downstream with GST_QUERY_CONTEXT for the context and
318    *    check if downstream already has a context of the specific type
319    */
320   query = gst_query_new_context (GST_D3D11_DEVICE_HANDLE_CONTEXT_TYPE);
321   if (run_query (element, query, GST_PAD_SRC)) {
322     gst_query_parse_context (query, &ctxt);
323     if (ctxt) {
324       GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
325           "found context (%" GST_PTR_FORMAT ") in downstream query", ctxt);
326       gst_element_set_context (element, ctxt);
327     }
328   }
329 
330   /* 2) although we found d3d11 device context above, the context might not be
331    *    expected/wanted one by the element (e.g., belongs to the other GPU).
332    *    Then try to find it from the other direction */
333   if (*device == NULL && run_query (element, query, GST_PAD_SINK)) {
334     gst_query_parse_context (query, &ctxt);
335     if (ctxt) {
336       GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
337           "found context (%" GST_PTR_FORMAT ") in upstream query", ctxt);
338       gst_element_set_context (element, ctxt);
339     }
340   }
341 
342   if (*device == NULL) {
343     /* 3) Post a GST_MESSAGE_NEED_CONTEXT message on the bus with
344      *    the required context type and afterwards check if a
345      *    usable context was set now as in 1). The message could
346      *    be handled by the parent bins of the element and the
347      *    application.
348      */
349     GstMessage *msg;
350 
351     GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
352         "posting need context message");
353     msg = gst_message_new_need_context (GST_OBJECT_CAST (element),
354         GST_D3D11_DEVICE_HANDLE_CONTEXT_TYPE);
355     gst_element_post_message (element, msg);
356   }
357 
358   gst_query_unref (query);
359 }
360 
361 /**
362  * gst_d3d11_ensure_element_data:
363  * @element: the #GstElement running the query
364  * @adapter: preferred DXGI adapter index, pass adapter >=0 when
365  *           the adapter explicitly required. Otherwise, set -1.
366  * @device: (inout): the resulting #GstD3D11Device
367  *
368  * Perform the steps necessary for retrieving a #GstD3D11Device
369  * from the surrounding elements or from the application using the #GstContext mechanism.
370  *
371  * If the contents of @device is not %NULL, then no #GstContext query is
372  * necessary for #GstD3D11Device retrieval is performed.
373  *
374  * Returns: whether a #GstD3D11Device exists in @device
375  *
376  * Since: 1.20
377  */
378 gboolean
gst_d3d11_ensure_element_data(GstElement * element,gint adapter,GstD3D11Device ** device)379 gst_d3d11_ensure_element_data (GstElement * element, gint adapter,
380     GstD3D11Device ** device)
381 {
382   guint target_adapter = 0;
383 
384   g_return_val_if_fail (element != NULL, FALSE);
385   g_return_val_if_fail (device != NULL, FALSE);
386 
387   _init_context_debug ();
388 
389   if (*device) {
390     GST_LOG_OBJECT (element, "already have a device %" GST_PTR_FORMAT, *device);
391     return TRUE;
392   }
393 
394   run_d3d11_context_query (element, device);
395   if (*device)
396     return TRUE;
397 
398   if (adapter > 0)
399     target_adapter = adapter;
400 
401   /* Needs D3D11_CREATE_DEVICE_BGRA_SUPPORT flag for Direct2D interop */
402   *device = gst_d3d11_device_new (target_adapter,
403       D3D11_CREATE_DEVICE_BGRA_SUPPORT);
404 
405   if (*device == NULL) {
406     GST_ERROR_OBJECT (element,
407         "Couldn't create new device with adapter index %d", target_adapter);
408     return FALSE;
409   } else {
410     GstContext *context;
411     GstMessage *msg;
412 
413     /* Propagate new D3D11 device context */
414 
415     context = gst_context_new (GST_D3D11_DEVICE_HANDLE_CONTEXT_TYPE, TRUE);
416     context_set_d3d11_device (context, *device);
417 
418     gst_element_set_context (element, context);
419 
420     GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
421         "posting have context (%p) message with D3D11 device context (%p)",
422         context, *device);
423     msg = gst_message_new_have_context (GST_OBJECT_CAST (element), context);
424     gst_element_post_message (GST_ELEMENT_CAST (element), msg);
425   }
426 
427   return TRUE;
428 }
429 
430 /**
431  * gst_d3d11_ensure_element_data_for_adapter_luid:
432  * @element: the #GstElement running the query
433  * @adapter_luid: an int64 representation of DXGI adapter LUID
434  * @device: (inout): the resulting #GstD3D11Device
435  *
436  * Perform the steps necessary for retrieving a #GstD3D11Device
437  * from the surrounding elements or from the application using the #GstContext mechanism.
438  *
439  * If the contents of @device is not %NULL, then no #GstContext query is
440  * necessary for #GstD3D11Device retrieval is performed.
441  *
442  * Returns: whether a #GstD3D11Device exists in @device
443  *
444  * Since: 1.20
445  */
446 gboolean
gst_d3d11_ensure_element_data_for_adapter_luid(GstElement * element,gint64 adapter_luid,GstD3D11Device ** device)447 gst_d3d11_ensure_element_data_for_adapter_luid (GstElement * element,
448     gint64 adapter_luid, GstD3D11Device ** device)
449 {
450   g_return_val_if_fail (element != NULL, FALSE);
451   g_return_val_if_fail (device != NULL, FALSE);
452 
453   _init_context_debug ();
454 
455   if (*device) {
456     GST_LOG_OBJECT (element, "already have a device %" GST_PTR_FORMAT, *device);
457     return TRUE;
458   }
459 
460   run_d3d11_context_query (element, device);
461   if (*device)
462     return TRUE;
463 
464   /* Needs D3D11_CREATE_DEVICE_BGRA_SUPPORT flag for Direct2D interop */
465   *device = gst_d3d11_device_new_for_adapter_luid (adapter_luid,
466       D3D11_CREATE_DEVICE_BGRA_SUPPORT);
467 
468   if (*device == NULL) {
469     GST_ERROR_OBJECT (element,
470         "Couldn't create new device with adapter luid %" G_GINT64_FORMAT,
471         adapter_luid);
472     return FALSE;
473   } else {
474     GstContext *context;
475     GstMessage *msg;
476 
477     /* Propagate new D3D11 device context */
478 
479     context = gst_context_new (GST_D3D11_DEVICE_HANDLE_CONTEXT_TYPE, TRUE);
480     context_set_d3d11_device (context, *device);
481 
482     gst_element_set_context (element, context);
483 
484     GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
485         "posting have context (%p) message with D3D11 device context (%p)",
486         context, *device);
487     msg = gst_message_new_have_context (GST_OBJECT_CAST (element), context);
488     gst_element_post_message (GST_ELEMENT_CAST (element), msg);
489   }
490 
491   return TRUE;
492 }
493 
494 /**
495  * gst_d3d11_luid_to_int64:
496  * @luid: A pointer to LUID struct
497  *
498  * Converts from a LUID to a 64-bit signed integer.
499  * See also Int64FromLuid method defined in
500  * windows.devices.display.core.interop.h Windows SDK header
501  *
502  * Since: 1.20
503  */
504 gint64
gst_d3d11_luid_to_int64(const LUID * luid)505 gst_d3d11_luid_to_int64 (const LUID * luid)
506 {
507   LARGE_INTEGER val;
508 
509   g_return_val_if_fail (luid != nullptr, 0);
510 
511   val.LowPart = luid->LowPart;
512   val.HighPart = luid->HighPart;
513 
514   return val.QuadPart;
515 }
516 
517 gboolean
_gst_d3d11_result(HRESULT hr,GstD3D11Device * device,GstDebugCategory * cat,const gchar * file,const gchar * function,gint line)518 _gst_d3d11_result (HRESULT hr, GstD3D11Device * device, GstDebugCategory * cat,
519     const gchar * file, const gchar * function, gint line)
520 {
521 #ifndef GST_DISABLE_GST_DEBUG
522   gboolean ret = TRUE;
523 
524   if (FAILED (hr)) {
525     gchar *error_text = NULL;
526 
527     error_text = g_win32_error_message ((guint) hr);
528     /* g_win32_error_message() doesn't cover all HERESULT return code,
529      * so it could be empty string, or null if there was an error
530      * in g_utf16_to_utf8() */
531     gst_debug_log (cat, GST_LEVEL_WARNING, file, function, line,
532         NULL, "D3D11 call failed: 0x%x, %s", (guint) hr,
533         GST_STR_NULL (error_text));
534     g_free (error_text);
535 
536     ret = FALSE;
537   }
538 #if (HAVE_D3D11SDKLAYERS_H || HAVE_DXGIDEBUG_H)
539   if (device) {
540     gst_d3d11_device_d3d11_debug (device, file, function, line);
541     gst_d3d11_device_dxgi_debug (device, file, function, line);
542   }
543 #endif
544 
545   return ret;
546 #else
547   return SUCCEEDED (hr);
548 #endif
549 }
550