Merging gst-plugins-bad
[platform/upstream/gstreamer.git] / gst-libs / gst / d3d11 / gstd3d11utils.cpp
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 *
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
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  * @device: (inout) (transfer full): location of a #GstD3D11Device
70  *
71  * Helper function for implementing #GstElementClass.set_context() in
72  * D3D11 capable elements.
73  *
74  * Retrieve's the #GstD3D11Device in @context and places the result in @device.
75  *
76  * Returns: whether the @device could be set successfully
77  *
78  * Since: 1.20
79  */
80 gboolean
81 gst_d3d11_handle_set_context (GstElement * element, GstContext * context,
82     gint adapter, GstD3D11Device ** device)
83 {
84   const gchar *context_type;
85
86   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
87   g_return_val_if_fail (device != NULL, FALSE);
88
89   _init_context_debug ();
90
91   if (!context)
92     return FALSE;
93
94   context_type = gst_context_get_context_type (context);
95   if (g_strcmp0 (context_type, GST_D3D11_DEVICE_HANDLE_CONTEXT_TYPE) == 0) {
96     const GstStructure *str;
97     GstD3D11Device *other_device = NULL;
98     guint other_adapter = 0;
99
100     /* If we had device already, will not replace it */
101     if (*device)
102       return TRUE;
103
104     str = gst_context_get_structure (context);
105
106     if (gst_structure_get (str, "device", GST_TYPE_D3D11_DEVICE,
107             &other_device, "adapter", G_TYPE_UINT, &other_adapter, NULL)) {
108       if (adapter == -1 || (guint) adapter == other_adapter) {
109         GST_CAT_DEBUG_OBJECT (GST_CAT_CONTEXT,
110             element, "Found D3D11 device context");
111         *device = other_device;
112
113         return TRUE;
114       }
115
116       gst_object_unref (other_device);
117     }
118   }
119
120   return FALSE;
121 }
122
123 static void
124 context_set_d3d11_device (GstContext * context, GstD3D11Device * device)
125 {
126   GstStructure *s;
127   guint adapter = 0;
128   guint device_id = 0;
129   guint vendor_id = 0;
130   gboolean hardware = FALSE;
131   gchar *desc = NULL;
132   gint64 adapter_luid = 0;
133
134   g_return_if_fail (context != NULL);
135
136   g_object_get (G_OBJECT (device), "adapter", &adapter, "device-id", &device_id,
137       "vendor-id", &vendor_id, "hardware", &hardware, "description", &desc,
138       "adapter-luid", &adapter_luid, NULL);
139
140   GST_CAT_LOG (GST_CAT_CONTEXT,
141       "setting GstD3D11Device(%" GST_PTR_FORMAT
142       ") with adapter %d on context(%" GST_PTR_FORMAT ")",
143       device, adapter, context);
144
145   s = gst_context_writable_structure (context);
146   gst_structure_set (s, "device", GST_TYPE_D3D11_DEVICE, device,
147       "adapter", G_TYPE_UINT, adapter,
148       "adapter-luid", G_TYPE_INT64, adapter_luid,
149       "device-id", G_TYPE_UINT, device_id,
150       "vendor-id", G_TYPE_UINT, vendor_id,
151       "hardware", G_TYPE_BOOLEAN, hardware,
152       "description", G_TYPE_STRING, GST_STR_NULL (desc), NULL);
153   g_free (desc);
154 }
155
156 /**
157  * gst_d3d11_handle_context_query:
158  * @element: a #GstElement
159  * @query: a #GstQuery of type %GST_QUERY_CONTEXT
160  * @device: (transfer none) (nullable): a #GstD3D11Device
161  *
162  * Returns: Whether the @query was successfully responded to from the passed
163  *          @device.
164  *
165  * Since: 1.20
166  */
167 gboolean
168 gst_d3d11_handle_context_query (GstElement * element, GstQuery * query,
169     GstD3D11Device * device)
170 {
171   const gchar *context_type;
172   GstContext *context, *old_context;
173
174   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
175   g_return_val_if_fail (GST_IS_QUERY (query), FALSE);
176
177   _init_context_debug ();
178
179   GST_LOG_OBJECT (element, "handle context query %" GST_PTR_FORMAT, query);
180
181   if (!device)
182     return FALSE;
183
184   gst_query_parse_context_type (query, &context_type);
185   if (g_strcmp0 (context_type, GST_D3D11_DEVICE_HANDLE_CONTEXT_TYPE) != 0)
186     return FALSE;
187
188   gst_query_parse_context (query, &old_context);
189   if (old_context)
190     context = gst_context_copy (old_context);
191   else
192     context = gst_context_new (GST_D3D11_DEVICE_HANDLE_CONTEXT_TYPE, TRUE);
193
194   context_set_d3d11_device (context, device);
195   gst_query_set_context (query, context);
196   gst_context_unref (context);
197
198   GST_DEBUG_OBJECT (element, "successfully set %" GST_PTR_FORMAT
199       " on %" GST_PTR_FORMAT, device, query);
200
201   return TRUE;
202 }
203
204 static gboolean
205 pad_query (const GValue * item, GValue * value, gpointer user_data)
206 {
207   GstPad *pad = (GstPad *) g_value_get_object (item);
208   GstQuery *query = (GstQuery *) user_data;
209   gboolean res;
210
211   res = gst_pad_peer_query (pad, query);
212
213   if (res) {
214     g_value_set_boolean (value, TRUE);
215     return FALSE;
216   }
217
218   GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, pad, "pad peer query failed");
219   return TRUE;
220 }
221
222 static gboolean
223 run_query (GstElement * element, GstQuery * query, GstPadDirection direction)
224 {
225   GstIterator *it;
226   GstIteratorFoldFunction func = pad_query;
227   GValue res = { 0 };
228
229   g_value_init (&res, G_TYPE_BOOLEAN);
230   g_value_set_boolean (&res, FALSE);
231
232   /* Ask neighbor */
233   if (direction == GST_PAD_SRC)
234     it = gst_element_iterate_src_pads (element);
235   else
236     it = gst_element_iterate_sink_pads (element);
237
238   while (gst_iterator_fold (it, func, &res, query) == GST_ITERATOR_RESYNC)
239     gst_iterator_resync (it);
240
241   gst_iterator_free (it);
242
243   return g_value_get_boolean (&res);
244 }
245
246 static void
247 run_d3d11_context_query (GstElement * element, GstD3D11Device ** device)
248 {
249   GstQuery *query;
250   GstContext *ctxt = NULL;
251
252   /* 1) Query downstream with GST_QUERY_CONTEXT for the context and
253    *    check if downstream already has a context of the specific type
254    */
255   query = gst_query_new_context (GST_D3D11_DEVICE_HANDLE_CONTEXT_TYPE);
256   if (run_query (element, query, GST_PAD_SRC)) {
257     gst_query_parse_context (query, &ctxt);
258     if (ctxt) {
259       GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
260           "found context (%" GST_PTR_FORMAT ") in downstream query", ctxt);
261       gst_element_set_context (element, ctxt);
262     }
263   }
264
265   /* 2) although we found d3d11 device context above, the element does not want
266    *    to use the context. Then try to find from the other direction */
267   if (*device == NULL && run_query (element, query, GST_PAD_SINK)) {
268     gst_query_parse_context (query, &ctxt);
269     if (ctxt) {
270       GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
271           "found context (%" GST_PTR_FORMAT ") in upstream query", ctxt);
272       gst_element_set_context (element, ctxt);
273     }
274   }
275
276   if (*device == NULL) {
277     /* 3) Post a GST_MESSAGE_NEED_CONTEXT message on the bus with
278      *    the required context type and afterwards check if a
279      *    usable context was set now as in 1). The message could
280      *    be handled by the parent bins of the element and the
281      *    application.
282      */
283     GstMessage *msg;
284
285     GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
286         "posting need context message");
287     msg = gst_message_new_need_context (GST_OBJECT_CAST (element),
288         GST_D3D11_DEVICE_HANDLE_CONTEXT_TYPE);
289     gst_element_post_message (element, msg);
290   }
291
292   /*
293    * Whomever responds to the need-context message performs a
294    * GstElement::set_context() with the required context in which the element
295    * is required to update the display_ptr or call gst_gl_handle_set_context().
296    */
297
298   gst_query_unref (query);
299 }
300
301 /**
302  * gst_d3d11_ensure_element_data:
303  * @element: the #GstElement running the query
304  * @adapter: preferred adapter index, pass adapter >=0 when
305  *           the adapter explicitly required. Otherwise, set -1.
306  * @device: (inout): the resulting #GstD3D11Device
307  *
308  * Perform the steps necessary for retrieving a #GstD3D11Device
309  * from the surrounding elements or from the application using the #GstContext mechanism.
310  *
311  * If the contents of @device is not %NULL, then no #GstContext query is
312  * necessary for #GstD3D11Device retrieval is performed.
313  *
314  * Returns: whether a #GstD3D11Device exists in @device
315  *
316  * Since: 1.20
317  */
318 gboolean
319 gst_d3d11_ensure_element_data (GstElement * element, gint adapter,
320     GstD3D11Device ** device)
321 {
322   guint target_adapter = 0;
323
324   g_return_val_if_fail (element != NULL, FALSE);
325   g_return_val_if_fail (device != NULL, FALSE);
326
327   _init_context_debug ();
328
329   if (*device) {
330     GST_LOG_OBJECT (element, "already have a device %" GST_PTR_FORMAT, *device);
331     return TRUE;
332   }
333
334   run_d3d11_context_query (element, device);
335   if (*device)
336     return TRUE;
337
338   if (adapter > 0)
339     target_adapter = adapter;
340
341   /* Needs D3D11_CREATE_DEVICE_BGRA_SUPPORT flag for Direct2D interop */
342   *device = gst_d3d11_device_new (target_adapter,
343       D3D11_CREATE_DEVICE_BGRA_SUPPORT);
344
345   if (*device == NULL) {
346     GST_ERROR_OBJECT (element,
347         "Couldn't create new device with adapter index %d", target_adapter);
348     return FALSE;
349   } else {
350     GstContext *context;
351     GstMessage *msg;
352
353     /* Propagate new D3D11 device context */
354
355     context = gst_context_new (GST_D3D11_DEVICE_HANDLE_CONTEXT_TYPE, TRUE);
356     context_set_d3d11_device (context, *device);
357
358     gst_element_set_context (element, context);
359
360     GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
361         "posting have context (%p) message with D3D11 device context (%p)",
362         context, *device);
363     msg = gst_message_new_have_context (GST_OBJECT_CAST (element), context);
364     gst_element_post_message (GST_ELEMENT_CAST (element), msg);
365   }
366
367   return TRUE;
368 }
369
370 gboolean
371 _gst_d3d11_result (HRESULT hr, GstD3D11Device * device, GstDebugCategory * cat,
372     const gchar * file, const gchar * function, gint line)
373 {
374 #ifndef GST_DISABLE_GST_DEBUG
375   gboolean ret = TRUE;
376
377   if (FAILED (hr)) {
378     gchar *error_text = NULL;
379
380     error_text = g_win32_error_message ((guint) hr);
381     /* g_win32_error_message() doesn't cover all HERESULT return code,
382      * so it could be empty string, or null if there was an error
383      * in g_utf16_to_utf8() */
384     gst_debug_log (cat, GST_LEVEL_WARNING, file, function, line,
385         NULL, "D3D11 call failed: 0x%x, %s", (guint) hr,
386         GST_STR_NULL (error_text));
387     g_free (error_text);
388
389     ret = FALSE;
390   }
391 #if (HAVE_D3D11SDKLAYERS_H || HAVE_DXGIDEBUG_H)
392   if (device) {
393     gst_d3d11_device_d3d11_debug (device, file, function, line);
394     gst_d3d11_device_dxgi_debug (device, file, function, line);
395   }
396 #endif
397
398   return ret;
399 #else
400   return SUCCEEDED (hr);
401 #endif
402 }