gst: don't use volatile to mean atomic
[platform/upstream/gstreamer.git] / sys / d3d11 / gstd3d11window.cpp
1 /*
2  * GStreamer
3  * Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.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 "gstd3d11window.h"
26 #include "gstd3d11pluginutils.h"
27
28 #if GST_D3D11_WINAPI_APP
29 /* workaround for GetCurrentTime collision */
30 #ifdef GetCurrentTime
31 #undef GetCurrentTime
32 #endif
33 #include <windows.ui.xaml.h>
34 #include <windows.applicationmodel.core.h>
35 #endif
36
37 #ifdef HAVE_DIRECT_WRITE
38 #include <dwrite.h>
39 #include <d2d1_1.h>
40 #include <sstream>
41 #endif
42
43 #include <wrl.h>
44 /* *INDENT-OFF* */
45 using namespace Microsoft::WRL;
46
47 G_BEGIN_DECLS
48
49 GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_window_debug);
50 #define GST_CAT_DEFAULT gst_d3d11_window_debug
51
52 G_END_DECLS
53 /* *INDENT-ON* */
54
55 struct _GstD3D11WindowPrivate
56 {
57 #ifdef HAVE_DIRECT_WRITE
58   IDWriteFactory *dwrite_factory;
59   IDWriteTextFormat *dwrite_format;
60
61   ID2D1Factory1 *d2d_factory;
62   ID2D1Device *d2d_device;
63   ID2D1DeviceContext *d2d_device_context;
64   ID2D1SolidColorBrush *d2d_brush;
65 #else
66   gpointer dummy;
67 #endif
68 };
69
70 enum
71 {
72   PROP_0,
73   PROP_D3D11_DEVICE,
74   PROP_FORCE_ASPECT_RATIO,
75   PROP_ENABLE_NAVIGATION_EVENTS,
76   PROP_FULLSCREEN_TOGGLE_MODE,
77   PROP_FULLSCREEN,
78   PROP_WINDOW_HANDLE,
79   PROP_RENDER_STATS,
80 };
81
82 #define DEFAULT_ENABLE_NAVIGATION_EVENTS  TRUE
83 #define DEFAULT_FORCE_ASPECT_RATIO        TRUE
84 #define DEFAULT_FULLSCREEN_TOGGLE_MODE    GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE
85 #define DEFAULT_FULLSCREEN                FALSE
86 #define DEFAULT_RENDER_STATS              FALSE
87
88 enum
89 {
90   SIGNAL_KEY_EVENT,
91   SIGNAL_MOUSE_EVENT,
92   SIGNAL_LAST
93 };
94
95 static guint d3d11_window_signals[SIGNAL_LAST] = { 0, };
96
97 GType
98 gst_d3d11_window_fullscreen_toggle_mode_type (void)
99 {
100   static gsize mode_type = 0;
101
102   if (g_once_init_enter (&mode_type)) {
103     static const GFlagsValue mode_types[] = {
104       {GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE,
105           "GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE", "none"},
106       {GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_ALT_ENTER,
107           "GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_ALT_ENTER", "alt-enter"},
108       {GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_PROPERTY,
109           "GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_PROPERTY", "property"},
110       {0, NULL, NULL},
111     };
112     GType tmp = g_flags_register_static ("GstD3D11WindowFullscreenToggleMode",
113         mode_types);
114     g_once_init_leave (&mode_type, tmp);
115   }
116
117   return (GType) mode_type;
118 }
119
120 #define gst_d3d11_window_parent_class parent_class
121 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GstD3D11Window, gst_d3d11_window,
122     GST_TYPE_OBJECT);
123
124 static void gst_d3d11_window_set_property (GObject * object, guint prop_id,
125     const GValue * value, GParamSpec * pspec);
126 static void gst_d3d11_window_get_property (GObject * object, guint prop_id,
127     GValue * value, GParamSpec * pspec);
128 static void gst_d3d11_window_dispose (GObject * object);
129 static GstFlowReturn gst_d3d111_window_present (GstD3D11Window * self,
130     GstBuffer * buffer, GstStructure * stats,
131     ID3D11VideoProcessorOutputView * pov, ID3D11RenderTargetView * rtv);
132 static void gst_d3d11_window_on_resize_default (GstD3D11Window * window,
133     guint width, guint height);
134 static gboolean gst_d3d11_window_prepare_default (GstD3D11Window * window,
135     guint display_width, guint display_height, GstCaps * caps,
136     gboolean * video_processor_available, GError ** error);
137
138 static void
139 gst_d3d11_window_class_init (GstD3D11WindowClass * klass)
140 {
141   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
142
143   gobject_class->set_property = gst_d3d11_window_set_property;
144   gobject_class->get_property = gst_d3d11_window_get_property;
145   gobject_class->dispose = gst_d3d11_window_dispose;
146
147   klass->on_resize = GST_DEBUG_FUNCPTR (gst_d3d11_window_on_resize_default);
148   klass->prepare = GST_DEBUG_FUNCPTR (gst_d3d11_window_prepare_default);
149
150   g_object_class_install_property (gobject_class, PROP_D3D11_DEVICE,
151       g_param_spec_object ("d3d11device", "D3D11 Device",
152           "GstD3D11Device object for creating swapchain",
153           GST_TYPE_D3D11_DEVICE,
154           (GParamFlags) (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
155               G_PARAM_STATIC_STRINGS)));
156
157   g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
158       g_param_spec_boolean ("force-aspect-ratio",
159           "Force aspect ratio",
160           "When enabled, scaling will respect original aspect ratio",
161           DEFAULT_FORCE_ASPECT_RATIO,
162           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
163
164   g_object_class_install_property (gobject_class, PROP_ENABLE_NAVIGATION_EVENTS,
165       g_param_spec_boolean ("enable-navigation-events",
166           "Enable navigation events",
167           "When enabled, signals for navigation events are emitted",
168           DEFAULT_ENABLE_NAVIGATION_EVENTS,
169           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
170
171   g_object_class_install_property (gobject_class, PROP_FULLSCREEN_TOGGLE_MODE,
172       g_param_spec_flags ("fullscreen-toggle-mode",
173           "Full screen toggle mode",
174           "Full screen toggle mode used to trigger fullscreen mode change",
175           GST_D3D11_WINDOW_TOGGLE_MODE_GET_TYPE, DEFAULT_FULLSCREEN_TOGGLE_MODE,
176           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
177
178   g_object_class_install_property (gobject_class, PROP_FULLSCREEN,
179       g_param_spec_boolean ("fullscreen",
180           "fullscreen",
181           "Ignored when \"fullscreen-toggle-mode\" does not include \"property\"",
182           DEFAULT_FULLSCREEN,
183           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
184
185   g_object_class_install_property (gobject_class, PROP_WINDOW_HANDLE,
186       g_param_spec_pointer ("window-handle",
187           "Window Handle", "External Window Handle",
188           (GParamFlags) (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
189               G_PARAM_STATIC_STRINGS)));
190
191 #ifdef HAVE_DIRECT_WRITE
192   g_object_class_install_property (gobject_class, PROP_RENDER_STATS,
193       g_param_spec_boolean ("render-stats",
194           "Render Stats",
195           "Render statistics data (e.g., average framerate) on window",
196           DEFAULT_RENDER_STATS,
197           (GParamFlags) (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)));
198 #endif
199
200   d3d11_window_signals[SIGNAL_KEY_EVENT] =
201       g_signal_new ("key-event", G_TYPE_FROM_CLASS (klass),
202       G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
203       G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
204
205   d3d11_window_signals[SIGNAL_MOUSE_EVENT] =
206       g_signal_new ("mouse-event", G_TYPE_FROM_CLASS (klass),
207       G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
208       G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_INT, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
209 }
210
211 static void
212 gst_d3d11_window_init (GstD3D11Window * self)
213 {
214   self->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
215   self->enable_navigation_events = DEFAULT_ENABLE_NAVIGATION_EVENTS;
216   self->fullscreen_toggle_mode = GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE;
217   self->fullscreen = DEFAULT_FULLSCREEN;
218   self->render_stats = DEFAULT_RENDER_STATS;
219
220   self->priv =
221       (GstD3D11WindowPrivate *) gst_d3d11_window_get_instance_private (self);
222 }
223
224 static void
225 gst_d3d11_window_set_property (GObject * object, guint prop_id,
226     const GValue * value, GParamSpec * pspec)
227 {
228   GstD3D11Window *self = GST_D3D11_WINDOW (object);
229   GstD3D11WindowClass *klass = GST_D3D11_WINDOW_GET_CLASS (object);
230
231   switch (prop_id) {
232     case PROP_D3D11_DEVICE:
233       self->device = (GstD3D11Device *) g_value_dup_object (value);
234       break;
235     case PROP_FORCE_ASPECT_RATIO:
236     {
237       self->force_aspect_ratio = g_value_get_boolean (value);
238       if (self->swap_chain)
239         klass->update_swap_chain (self);
240       break;
241     }
242     case PROP_ENABLE_NAVIGATION_EVENTS:
243       self->enable_navigation_events = g_value_get_boolean (value);
244       break;
245     case PROP_FULLSCREEN_TOGGLE_MODE:
246       self->fullscreen_toggle_mode =
247           (GstD3D11WindowFullscreenToggleMode) g_value_get_flags (value);
248       break;
249     case PROP_FULLSCREEN:
250     {
251       self->requested_fullscreen = g_value_get_boolean (value);
252       if (self->swap_chain)
253         klass->change_fullscreen_mode (self);
254       break;
255     }
256     case PROP_WINDOW_HANDLE:
257       self->external_handle = (guintptr) g_value_get_pointer (value);
258       break;
259 #ifdef HAVE_DIRECT_WRITE
260     case PROP_RENDER_STATS:
261       self->render_stats = g_value_get_boolean (value);
262       break;
263 #endif
264     default:
265       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
266       break;
267   }
268 }
269
270 static void
271 gst_d3d11_window_get_property (GObject * object, guint prop_id,
272     GValue * value, GParamSpec * pspec)
273 {
274   GstD3D11Window *self = GST_D3D11_WINDOW (object);
275
276   switch (prop_id) {
277     case PROP_ENABLE_NAVIGATION_EVENTS:
278       g_value_set_boolean (value, self->enable_navigation_events);
279       break;
280     case PROP_FORCE_ASPECT_RATIO:
281       g_value_set_boolean (value, self->force_aspect_ratio);
282       break;
283     case PROP_FULLSCREEN_TOGGLE_MODE:
284       g_value_set_flags (value, self->fullscreen_toggle_mode);
285       break;
286     case PROP_FULLSCREEN:
287       g_value_set_boolean (value, self->fullscreen);
288       break;
289     default:
290       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
291       break;
292   }
293 }
294
295 #ifdef HAVE_DIRECT_WRITE
296 static void
297 gst_d3d11_window_release_dwrite_resources (GstD3D11Window * self)
298 {
299   GstD3D11WindowPrivate *priv = self->priv;
300
301   GST_D3D11_CLEAR_COM (priv->d2d_device_context);
302   GST_D3D11_CLEAR_COM (priv->d2d_factory);
303   GST_D3D11_CLEAR_COM (priv->d2d_device);
304   GST_D3D11_CLEAR_COM (priv->d2d_brush);
305   GST_D3D11_CLEAR_COM (priv->dwrite_factory);
306   GST_D3D11_CLEAR_COM (priv->dwrite_format);
307 }
308
309 static void
310 gst_d3d11_window_prepare_dwrite_device (GstD3D11Window * self)
311 {
312   GstD3D11WindowPrivate *priv = self->priv;
313   HRESULT hr;
314   /* *INDENT-OFF* */
315   ComPtr<IDXGIDevice> dxgi_device;
316   /* *INDENT-ON* */
317   ID3D11Device *device_handle;
318
319   if (!self->device) {
320     GST_ERROR_OBJECT (self, "D3D11Device is unavailable");
321     return;
322   }
323
324   /* Already prepared */
325   if (priv->d2d_device)
326     return;
327
328   hr = D2D1CreateFactory (D2D1_FACTORY_TYPE_MULTI_THREADED,
329       IID_PPV_ARGS (&priv->d2d_factory));
330   if (!gst_d3d11_result (hr, self->device))
331     goto error;
332
333   device_handle = gst_d3d11_device_get_device_handle (self->device);
334   hr = device_handle->QueryInterface (IID_PPV_ARGS (&dxgi_device));
335   if (!gst_d3d11_result (hr, self->device))
336     goto error;
337
338   hr = priv->d2d_factory->CreateDevice (dxgi_device.Get (), &priv->d2d_device);
339   if (!gst_d3d11_result (hr, self->device))
340     goto error;
341
342   hr = priv->d2d_device->CreateDeviceContext
343       (D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS,
344       &priv->d2d_device_context);
345   if (!gst_d3d11_result (hr, self->device))
346     goto error;
347
348   hr = priv->
349       d2d_device_context->CreateSolidColorBrush (D2D1::ColorF (D2D1::ColorF::
350           Yellow), &priv->d2d_brush);
351   if (!gst_d3d11_result (hr, self->device))
352     goto error;
353
354   hr = DWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED,
355       __uuidof (IDWriteFactory), (IUnknown **) & priv->dwrite_factory);
356   if (!gst_d3d11_result (hr, self->device))
357     goto error;
358
359   /* Configure font */
360   hr = priv->dwrite_factory->CreateTextFormat (L"Arial", NULL,
361       DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
362       DWRITE_FONT_STRETCH_NORMAL, 12.0f, L"en-US", &priv->dwrite_format);
363   if (!gst_d3d11_result (hr, self->device))
364     goto error;
365
366   GST_DEBUG_OBJECT (self, "Direct2D device is prepared");
367
368   return;
369
370 error:
371   gst_d3d11_window_release_dwrite_resources (self);
372 }
373
374 static void
375 gst_d3d11_window_dwrite_on_resize (GstD3D11Window * self,
376     ID3D11Texture2D * backbuffer)
377 {
378   GstD3D11WindowPrivate *priv = self->priv;
379   /* *INDENT-OFF* */
380   ComPtr<IDXGISurface> dxgi_surface;
381   ComPtr<ID2D1Bitmap1> bitmap;
382   /* *INDENT-ON* */
383   D2D1_BITMAP_PROPERTIES1 prop;
384   HRESULT hr;
385
386   if (!priv->d2d_device)
387     return;
388
389   prop.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
390   prop.pixelFormat.alphaMode = D2D1_ALPHA_MODE_IGNORE;
391   /* default dpi */
392   prop.dpiX = 96.0f;
393   prop.dpiY = 96.0f;
394   prop.bitmapOptions =
395       D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW;
396   prop.colorContext = NULL;
397
398   hr = backbuffer->QueryInterface (IID_PPV_ARGS (&dxgi_surface));
399   if (!gst_d3d11_result (hr, self->device))
400     goto error;
401
402   hr = priv->d2d_device_context->
403       CreateBitmapFromDxgiSurface (dxgi_surface.Get (), &prop, &bitmap);
404   if (!gst_d3d11_result (hr, self->device))
405     goto error;
406
407   priv->d2d_device_context->SetTarget (bitmap.Get ());
408
409   GST_LOG_OBJECT (self, "New D2D bitmap has been configured");
410
411   return;
412
413 error:
414   gst_d3d11_window_release_dwrite_resources (self);
415 }
416
417 #endif
418
419 static void
420 gst_d3d11_window_release_resources (GstD3D11Device * device,
421     GstD3D11Window * window)
422 {
423 #ifdef HAVE_DIRECT_WRITE
424   gst_d3d11_window_release_dwrite_resources (window);
425 #endif
426
427   GST_D3D11_CLEAR_COM (window->rtv);
428   GST_D3D11_CLEAR_COM (window->pov);
429   GST_D3D11_CLEAR_COM (window->swap_chain);
430 }
431
432 static void
433 gst_d3d11_window_dispose (GObject * object)
434 {
435   GstD3D11Window *self = GST_D3D11_WINDOW (object);
436
437   if (self->device) {
438     gst_d3d11_window_release_resources (self->device, self);
439   }
440
441   g_clear_pointer (&self->processor, gst_d3d11_video_processor_free);
442   g_clear_pointer (&self->converter, gst_d3d11_converter_free);
443   g_clear_pointer (&self->compositor, gst_d3d11_overlay_compositor_free);
444
445   gst_clear_buffer (&self->cached_buffer);
446   gst_clear_object (&self->device);
447
448   G_OBJECT_CLASS (parent_class)->dispose (object);
449 }
450
451 static void
452 gst_d3d11_window_on_resize_default (GstD3D11Window * window, guint width,
453     guint height)
454 {
455 #ifdef HAVE_DIRECT_WRITE
456   GstD3D11WindowPrivate *priv = window->priv;
457 #endif
458   HRESULT hr;
459   ID3D11Device *device_handle;
460   D3D11_TEXTURE2D_DESC desc;
461   DXGI_SWAP_CHAIN_DESC swap_desc;
462   ID3D11Texture2D *backbuffer = NULL;
463   GstVideoRectangle src_rect, dst_rect, rst_rect;
464   IDXGISwapChain *swap_chain;
465
466   gst_d3d11_device_lock (window->device);
467   if (!window->swap_chain)
468     goto done;
469
470   device_handle = gst_d3d11_device_get_device_handle (window->device);
471   swap_chain = window->swap_chain;
472
473   GST_D3D11_CLEAR_COM (window->rtv);
474   GST_D3D11_CLEAR_COM (window->pov);
475
476 #ifdef HAVE_DIRECT_WRITE
477   /* D2D bitmap need to be cleared before resizing swapchain buffer */
478   if (priv->d2d_device_context)
479     priv->d2d_device_context->SetTarget (NULL);
480 #endif
481
482   swap_chain->GetDesc (&swap_desc);
483   hr = swap_chain->ResizeBuffers (0, width, height, window->dxgi_format,
484       swap_desc.Flags);
485   if (!gst_d3d11_result (hr, window->device)) {
486     GST_ERROR_OBJECT (window, "Couldn't resize buffers, hr: 0x%x", (guint) hr);
487     goto done;
488   }
489
490   hr = swap_chain->GetBuffer (0, IID_ID3D11Texture2D, (void **) &backbuffer);
491   if (!gst_d3d11_result (hr, window->device)) {
492     GST_ERROR_OBJECT (window,
493         "Cannot get backbuffer from swapchain, hr: 0x%x", (guint) hr);
494     goto done;
495   }
496
497   backbuffer->GetDesc (&desc);
498   window->surface_width = desc.Width;
499   window->surface_height = desc.Height;
500
501   {
502     dst_rect.x = 0;
503     dst_rect.y = 0;
504     dst_rect.w = window->surface_width;
505     dst_rect.h = window->surface_height;
506
507     if (window->force_aspect_ratio) {
508       src_rect.x = 0;
509       src_rect.y = 0;
510       src_rect.w = GST_VIDEO_INFO_WIDTH (&window->render_info);
511       src_rect.h = GST_VIDEO_INFO_HEIGHT (&window->render_info);
512
513       gst_video_sink_center_rect (src_rect, dst_rect, &rst_rect, TRUE);
514     } else {
515       rst_rect = dst_rect;
516     }
517   }
518
519   window->render_rect.left = rst_rect.x;
520   window->render_rect.top = rst_rect.y;
521   window->render_rect.right = rst_rect.x + rst_rect.w;
522   window->render_rect.bottom = rst_rect.y + rst_rect.h;
523
524   GST_LOG_OBJECT (window,
525       "New client area %dx%d, render rect x: %d, y: %d, %dx%d",
526       desc.Width, desc.Height, rst_rect.x, rst_rect.y, rst_rect.w, rst_rect.h);
527
528   hr = device_handle->CreateRenderTargetView ((ID3D11Resource *) backbuffer,
529       NULL, &window->rtv);
530   if (!gst_d3d11_result (hr, window->device)) {
531     GST_ERROR_OBJECT (window, "Cannot create render target view, hr: 0x%x",
532         (guint) hr);
533
534     goto done;
535   }
536
537   if (window->processor) {
538     D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC pov_desc;
539
540     pov_desc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D;
541     pov_desc.Texture2D.MipSlice = 0;
542
543     if (!gst_d3d11_video_processor_create_output_view (window->processor,
544             &pov_desc, (ID3D11Resource *) backbuffer, &window->pov))
545       goto done;
546   }
547 #ifdef HAVE_DIRECT_WRITE
548   if (window->render_stats)
549     gst_d3d11_window_dwrite_on_resize (window, backbuffer);
550 #endif
551
552   window->first_present = TRUE;
553
554   /* redraw the last scene if cached buffer exits */
555   if (window->cached_buffer) {
556     gst_d3d111_window_present (window, window->cached_buffer, NULL,
557         window->pov, window->rtv);
558   }
559
560 done:
561   GST_D3D11_CLEAR_COM (backbuffer);
562
563   gst_d3d11_device_unlock (window->device);
564 }
565
566 void
567 gst_d3d11_window_on_key_event (GstD3D11Window * window, const gchar * event,
568     const gchar * key)
569 {
570   g_return_if_fail (GST_IS_D3D11_WINDOW (window));
571
572   if (!window->enable_navigation_events)
573     return;
574
575   g_signal_emit (window, d3d11_window_signals[SIGNAL_KEY_EVENT], 0, event, key);
576 }
577
578 void
579 gst_d3d11_window_on_mouse_event (GstD3D11Window * window, const gchar * event,
580     gint button, gdouble x, gdouble y)
581 {
582   g_return_if_fail (GST_IS_D3D11_WINDOW (window));
583
584   if (!window->enable_navigation_events)
585     return;
586
587   g_signal_emit (window, d3d11_window_signals[SIGNAL_MOUSE_EVENT], 0,
588       event, button, x, y);
589 }
590
591 typedef struct
592 {
593   DXGI_FORMAT dxgi_format;
594   GstVideoFormat gst_format;
595   gboolean supported;
596 } GstD3D11WindowDisplayFormat;
597
598 gboolean
599 gst_d3d11_window_prepare (GstD3D11Window * window, guint display_width,
600     guint display_height, GstCaps * caps, gboolean * video_processor_available,
601     GError ** error)
602 {
603   GstD3D11WindowClass *klass;
604
605   g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), FALSE);
606
607   klass = GST_D3D11_WINDOW_GET_CLASS (window);
608   g_assert (klass->prepare != NULL);
609
610   GST_DEBUG_OBJECT (window, "Prepare window, display resolution %dx%d, caps %"
611       GST_PTR_FORMAT, display_width, display_height, caps);
612
613   return klass->prepare (window, display_width, display_height, caps,
614       video_processor_available, error);
615 }
616
617 static gboolean
618 gst_d3d11_window_prepare_default (GstD3D11Window * window, guint display_width,
619     guint display_height, GstCaps * caps, gboolean * video_processor_available,
620     GError ** error)
621 {
622   GstD3D11WindowClass *klass;
623   guint swapchain_flags = 0;
624   ID3D11Device *device_handle;
625   guint i;
626   guint num_supported_format = 0;
627   HRESULT hr;
628   UINT display_flags =
629       D3D11_FORMAT_SUPPORT_TEXTURE2D | D3D11_FORMAT_SUPPORT_DISPLAY;
630   UINT supported_flags = 0;
631   GstD3D11WindowDisplayFormat formats[] = {
632     {DXGI_FORMAT_R8G8B8A8_UNORM, GST_VIDEO_FORMAT_RGBA, FALSE},
633     {DXGI_FORMAT_B8G8R8A8_UNORM, GST_VIDEO_FORMAT_BGRA, FALSE},
634     {DXGI_FORMAT_R10G10B10A2_UNORM, GST_VIDEO_FORMAT_RGB10A2_LE, FALSE},
635   };
636   const GstD3D11WindowDisplayFormat *chosen_format = NULL;
637   const GstDxgiColorSpace *chosen_colorspace = NULL;
638 #if (GST_D3D11_DXGI_HEADER_VERSION >= 4)
639   gboolean have_hdr10 = FALSE;
640   DXGI_COLOR_SPACE_TYPE native_colorspace_type =
641       DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
642 #endif
643 #if (GST_D3D11_DXGI_HEADER_VERSION >= 5)
644   DXGI_HDR_METADATA_HDR10 hdr10_metadata = { 0, };
645 #endif
646
647   /* Step 1: Clear old resources and objects */
648   gst_clear_buffer (&window->cached_buffer);
649   g_clear_pointer (&window->processor, gst_d3d11_video_processor_free);
650   g_clear_pointer (&window->converter, gst_d3d11_converter_free);
651   g_clear_pointer (&window->compositor, gst_d3d11_overlay_compositor_free);
652
653   window->processor_in_use = FALSE;
654
655   /* Step 2: Decide display color format
656    * If upstream format is 10bits, try DXGI_FORMAT_R10G10B10A2_UNORM first
657    * Otherwise, use DXGI_FORMAT_B8G8R8A8_UNORM or DXGI_FORMAT_B8G8R8A8_UNORM
658    */
659   gst_video_info_from_caps (&window->info, caps);
660   device_handle = gst_d3d11_device_get_device_handle (window->device);
661   for (i = 0; i < G_N_ELEMENTS (formats); i++) {
662     hr = device_handle->CheckFormatSupport (formats[i].dxgi_format,
663         &supported_flags);
664     if (SUCCEEDED (hr) && (supported_flags & display_flags) == display_flags) {
665       GST_DEBUG_OBJECT (window, "Device supports format %s (DXGI_FORMAT %d)",
666           gst_video_format_to_string (formats[i].gst_format),
667           formats[i].dxgi_format);
668       formats[i].supported = TRUE;
669       num_supported_format++;
670     }
671   }
672
673   if (num_supported_format == 0) {
674     GST_ERROR_OBJECT (window, "Cannot determine render format");
675     g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
676         "Cannot determine render format");
677     return FALSE;
678   }
679 #ifdef HAVE_DIRECT_WRITE
680   if (window->render_stats && formats[1].supported) {
681     /* FIXME: D2D seems to be accepting only DXGI_FORMAT_B8G8R8A8_UNORM */
682     chosen_format = &formats[1];
683   } else
684 #else
685   {
686     for (i = 0; i < GST_VIDEO_INFO_N_COMPONENTS (&window->info); i++) {
687       if (GST_VIDEO_INFO_COMP_DEPTH (&window->info, i) > 8) {
688         if (formats[2].supported) {
689           chosen_format = &formats[2];
690         }
691         break;
692       }
693     }
694   }
695 #endif
696
697   if (!chosen_format) {
698     /* prefer native format over conversion */
699     for (i = 0; i < 2; i++) {
700       if (formats[i].supported &&
701           formats[i].gst_format == GST_VIDEO_INFO_FORMAT (&window->info)) {
702         chosen_format = &formats[i];
703         break;
704       }
705     }
706
707     /* choose any color space then */
708     if (!chosen_format) {
709       for (i = 0; i < G_N_ELEMENTS (formats); i++) {
710         if (formats[i].supported) {
711           chosen_format = &formats[i];
712           break;
713         }
714       }
715     }
716   }
717
718   g_assert (chosen_format != NULL);
719
720   GST_DEBUG_OBJECT (window, "chosen rendero format %s (DXGI_FORMAT %d)",
721       gst_video_format_to_string (chosen_format->gst_format),
722       chosen_format->dxgi_format);
723
724   /* Step 3: Create swapchain
725    * (or reuse old swapchain if the format is not changed) */
726   window->allow_tearing = FALSE;
727   g_object_get (window->device, "allow-tearing", &window->allow_tearing, NULL);
728   if (window->allow_tearing) {
729     GST_DEBUG_OBJECT (window, "device support tearning");
730     swapchain_flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
731   }
732
733   gst_d3d11_device_lock (window->device);
734   window->dxgi_format = chosen_format->dxgi_format;
735
736   klass = GST_D3D11_WINDOW_GET_CLASS (window);
737   if (!window->swap_chain &&
738       !klass->create_swap_chain (window, window->dxgi_format,
739           display_width, display_height, swapchain_flags,
740           &window->swap_chain)) {
741     GST_ERROR_OBJECT (window, "Cannot create swapchain");
742     g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
743         "Cannot create swapchain");
744     goto error;
745   }
746
747   /* this rect struct will be used to calculate render area */
748   window->render_rect.left = 0;
749   window->render_rect.top = 0;
750   window->render_rect.right = display_width;
751   window->render_rect.bottom = display_height;
752
753   window->input_rect.left = 0;
754   window->input_rect.top = 0;
755   window->input_rect.right = GST_VIDEO_INFO_WIDTH (&window->info);
756   window->input_rect.bottom = GST_VIDEO_INFO_HEIGHT (&window->info);
757
758   /* Step 4: Decide render color space and set it on converter/processor */
759
760   /* check HDR10 metadata. If HDR APIs are available, BT2020 primaries colorspcae
761    * will be used.
762    *
763    * FIXME: need to query h/w level support. If that's not the case, tone-mapping
764    * should be placed somewhere.
765    *
766    * FIXME: Non-HDR colorspace with BT2020 primaries will break rendering.
767    * https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/issues/1175
768    * To workaround it, BT709 colorspace will be chosen for non-HDR case.
769    */
770 #if (GST_D3D11_DXGI_HEADER_VERSION >= 5)
771   {
772     GstVideoMasteringDisplayInfo minfo;
773     GstVideoContentLightLevel cll;
774
775     if (gst_video_mastering_display_info_from_caps (&minfo, caps) &&
776         gst_video_content_light_level_from_caps (&cll, caps)) {
777       IDXGISwapChain4 *swapchain4 = NULL;
778       HRESULT hr;
779
780       hr = window->swap_chain->QueryInterface (IID_IDXGISwapChain4,
781           (void **) &swapchain4);
782       if (gst_d3d11_result (hr, window->device)) {
783         GST_DEBUG_OBJECT (window, "Have HDR metadata, set to DXGI swapchain");
784
785         gst_d3d11_hdr_meta_data_to_dxgi (&minfo, &cll, &hdr10_metadata);
786
787         hr = swapchain4->SetHDRMetaData (DXGI_HDR_METADATA_TYPE_HDR10,
788             sizeof (DXGI_HDR_METADATA_HDR10), &hdr10_metadata);
789         if (!gst_d3d11_result (hr, window->device)) {
790           GST_WARNING_OBJECT (window, "Couldn't set HDR metadata, hr 0x%x",
791               (guint) hr);
792         } else {
793           have_hdr10 = TRUE;
794         }
795
796         swapchain4->Release ();
797       }
798     }
799   }
800 #endif
801
802   /* Step 5: Choose display color space */
803   gst_video_info_set_format (&window->render_info,
804       chosen_format->gst_format, display_width, display_height);
805
806   /* preserve upstream colorimetry */
807   window->render_info.colorimetry.primaries =
808       window->info.colorimetry.primaries;
809   window->render_info.colorimetry.transfer = window->info.colorimetry.transfer;
810   /* prefer FULL range RGB. STUDIO range doesn't seem to be well supported
811    * color space by GPUs and we don't need to preserve color range for
812    * target display color space type */
813   window->render_info.colorimetry.range = GST_VIDEO_COLOR_RANGE_0_255;
814
815 #if (GST_D3D11_DXGI_HEADER_VERSION >= 4)
816   {
817     IDXGISwapChain3 *swapchain3 = NULL;
818     HRESULT hr;
819
820     hr = window->swap_chain->QueryInterface (IID_IDXGISwapChain3,
821         (void **) &swapchain3);
822
823     if (gst_d3d11_result (hr, window->device)) {
824       chosen_colorspace =
825           gst_d3d11_find_swap_chain_color_space (&window->render_info,
826           swapchain3, have_hdr10);
827       if (chosen_colorspace) {
828         native_colorspace_type =
829             (DXGI_COLOR_SPACE_TYPE) chosen_colorspace->dxgi_color_space_type;
830         hr = swapchain3->SetColorSpace1 (native_colorspace_type);
831         if (!gst_d3d11_result (hr, window->device)) {
832           GST_WARNING_OBJECT (window, "Failed to set colorspace %d, hr: 0x%x",
833               native_colorspace_type, (guint) hr);
834           chosen_colorspace = NULL;
835           native_colorspace_type = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
836         } else {
837           GST_DEBUG_OBJECT (window,
838               "Set colorspace %d", native_colorspace_type);
839
840           /* update with selected display color space */
841           window->render_info.colorimetry.primaries =
842               chosen_colorspace->primaries;
843           window->render_info.colorimetry.transfer =
844               chosen_colorspace->transfer;
845           window->render_info.colorimetry.range = chosen_colorspace->range;
846           window->render_info.colorimetry.matrix = chosen_colorspace->matrix;
847         }
848       }
849
850       swapchain3->Release ();
851     }
852   }
853 #endif
854
855   /* otherwise, use most common DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709
856    * color space */
857   if (!chosen_colorspace) {
858     GST_DEBUG_OBJECT (window, "No selected render color space, use BT709");
859     window->render_info.colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT709;
860     window->render_info.colorimetry.transfer = GST_VIDEO_TRANSFER_BT709;
861     window->render_info.colorimetry.range = GST_VIDEO_COLOR_RANGE_0_255;
862   }
863 #if (GST_D3D11_DXGI_HEADER_VERSION >= 4)
864   if (chosen_colorspace) {
865     const GstDxgiColorSpace *in_color_space =
866         gst_d3d11_video_info_to_dxgi_color_space (&window->info);
867     const GstD3D11Format *in_format =
868         gst_d3d11_device_format_from_gst (window->device,
869         GST_VIDEO_INFO_FORMAT (&window->info));
870     gboolean hardware = FALSE;
871     GstD3D11VideoProcessor *processor = NULL;
872
873     if (in_color_space && in_format &&
874         in_format->dxgi_format != DXGI_FORMAT_UNKNOWN) {
875       g_object_get (window->device, "hardware", &hardware, NULL);
876     }
877
878     if (hardware) {
879       processor =
880           gst_d3d11_video_processor_new (window->device,
881           GST_VIDEO_INFO_WIDTH (&window->info),
882           GST_VIDEO_INFO_HEIGHT (&window->info), display_width, display_height);
883     }
884
885     if (processor) {
886       DXGI_FORMAT in_dxgi_format = in_format->dxgi_format;
887       DXGI_FORMAT out_dxgi_format = chosen_format->dxgi_format;
888       DXGI_COLOR_SPACE_TYPE in_dxgi_color_space =
889           (DXGI_COLOR_SPACE_TYPE) in_color_space->dxgi_color_space_type;
890       DXGI_COLOR_SPACE_TYPE out_dxgi_color_space = native_colorspace_type;
891
892       if (!gst_d3d11_video_processor_check_format_conversion (processor,
893               in_dxgi_format, in_dxgi_color_space, out_dxgi_format,
894               out_dxgi_color_space)) {
895         GST_DEBUG_OBJECT (window, "Conversion is not supported by device");
896         gst_d3d11_video_processor_free (processor);
897         processor = NULL;
898       } else {
899         GST_DEBUG_OBJECT (window, "video processor supports conversion");
900         gst_d3d11_video_processor_set_input_dxgi_color_space (processor,
901             in_dxgi_color_space);
902         gst_d3d11_video_processor_set_output_dxgi_color_space (processor,
903             out_dxgi_color_space);
904
905 #if (GST_D3D11_DXGI_HEADER_VERSION >= 5)
906         if (have_hdr10) {
907           GST_DEBUG_OBJECT (window, "Set HDR metadata on video processor");
908           gst_d3d11_video_processor_set_input_hdr10_metadata (processor,
909               &hdr10_metadata);
910           gst_d3d11_video_processor_set_output_hdr10_metadata (processor,
911               &hdr10_metadata);
912         }
913 #endif
914       }
915
916       window->processor = processor;
917     }
918   }
919 #endif
920   *video_processor_available = !!window->processor;
921
922   /* configure shader even if video processor is available for fallback */
923   window->converter =
924       gst_d3d11_converter_new (window->device, &window->info,
925       &window->render_info);
926
927   if (!window->converter) {
928     GST_ERROR_OBJECT (window, "Cannot create converter");
929     g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
930         "Cannot create converter");
931     goto error;
932   }
933
934   window->compositor =
935       gst_d3d11_overlay_compositor_new (window->device, &window->render_info);
936   if (!window->compositor) {
937     GST_ERROR_OBJECT (window, "Cannot create overlay compositor");
938     g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
939         "Cannot create overlay compositor");
940     goto error;
941   }
942 #ifdef HAVE_DIRECT_WRITE
943   if (window->render_stats)
944     gst_d3d11_window_prepare_dwrite_device (window);
945 #endif
946   gst_d3d11_device_unlock (window->device);
947
948   /* call resize to allocated resources */
949   klass->on_resize (window, display_width, display_height);
950
951   if (window->requested_fullscreen != window->fullscreen) {
952     klass->change_fullscreen_mode (window);
953   }
954
955   GST_DEBUG_OBJECT (window, "New swap chain 0x%p created", window->swap_chain);
956
957   return TRUE;
958
959 error:
960   gst_d3d11_device_unlock (window->device);
961
962   return FALSE;
963 }
964
965 void
966 gst_d3d11_window_show (GstD3D11Window * window)
967 {
968   GstD3D11WindowClass *klass;
969
970   g_return_if_fail (GST_IS_D3D11_WINDOW (window));
971
972   klass = GST_D3D11_WINDOW_GET_CLASS (window);
973
974   if (klass->show)
975     klass->show (window);
976 }
977
978 void
979 gst_d3d11_window_set_render_rectangle (GstD3D11Window * window, gint x, gint y,
980     gint width, gint height)
981 {
982   g_return_if_fail (GST_IS_D3D11_WINDOW (window));
983
984   /* TODO: resize window and view */
985 }
986
987 static gboolean
988 gst_d3d11_window_buffer_ensure_processor_input (GstD3D11Window * self,
989     GstBuffer * buffer, ID3D11VideoProcessorInputView ** in_view)
990 {
991   GstD3D11Memory *mem;
992   ID3D11VideoProcessorInputView *piv;
993
994   if (!self->processor)
995     return FALSE;
996
997   if (gst_buffer_n_memory (buffer) != 1)
998     return FALSE;
999
1000   mem = (GstD3D11Memory *) gst_buffer_peek_memory (buffer, 0);
1001   piv = gst_d3d11_video_processor_get_input_view (self->processor, mem);
1002   if (!piv) {
1003     GST_LOG_OBJECT (self, "Failed to get processor input view");
1004     return FALSE;
1005   }
1006
1007   *in_view = piv;
1008
1009   return TRUE;
1010 }
1011
1012 #ifdef HAVE_DIRECT_WRITE
1013 static void
1014 gst_d3d11_window_present_d2d (GstD3D11Window * self, GstStructure * stats)
1015 {
1016   GstD3D11WindowPrivate *priv = self->priv;
1017   HRESULT hr;
1018   gdouble framerate = 0.0;
1019   guint64 dropped = 0;
1020   guint64 rendered = 0;
1021   std::wostringstream stats_str;
1022   /* *INDENT-OFF* */
1023   ComPtr<IDWriteTextLayout> layout;
1024   /* *INDENT-ON* */
1025   FLOAT left;
1026   FLOAT top;
1027
1028   if (!priv->d2d_device)
1029     return;
1030
1031   if (!stats)
1032     return;
1033
1034   gst_structure_get_double (stats, "average-rate", &framerate);
1035   gst_structure_get_uint64 (stats, "dropped", &dropped);
1036   gst_structure_get_uint64 (stats, "rendered", &rendered);
1037
1038   stats_str.precision (5);
1039   stats_str << "Average-rate: " << framerate << std::endl;
1040   stats_str << "Dropped: " << dropped << std::endl;
1041   stats_str << "Rendered: " << rendered << std::endl;
1042
1043   hr = priv->dwrite_factory->CreateTextLayout (stats_str.str ().c_str (),
1044       (UINT32) stats_str.str ().size (), priv->dwrite_format,
1045       self->render_rect.right - self->render_rect.left,
1046       self->render_rect.bottom - self->render_rect.top, &layout);
1047   if (!gst_d3d11_result (hr, self->device))
1048     return;
1049
1050   left = self->render_rect.left + 5.0f;
1051   top = self->render_rect.top + 5.0f;
1052
1053   priv->d2d_device_context->BeginDraw ();
1054   priv->d2d_device_context->DrawTextLayout (D2D1::Point2F (left, top),
1055       layout.Get (), priv->d2d_brush);
1056
1057   hr = priv->d2d_device_context->EndDraw ();
1058   gst_d3d11_result (hr, self->device);
1059
1060   return;
1061 }
1062 #endif
1063
1064 static gboolean
1065 gst_d3d11_window_do_processor (GstD3D11Window * self,
1066     ID3D11VideoProcessorInputView * piv, ID3D11VideoProcessorOutputView * pov)
1067 {
1068   gboolean ret;
1069
1070   ret = gst_d3d11_video_processor_render_unlocked (self->processor,
1071       &self->input_rect, piv, &self->render_rect, pov);
1072   if (!ret) {
1073     GST_ERROR_OBJECT (self, "Couldn't render to backbuffer using processor");
1074   } else {
1075     GST_TRACE_OBJECT (self, "Rendered using processor");
1076     self->processor_in_use = TRUE;
1077   }
1078
1079   return ret;
1080 }
1081
1082 static gboolean
1083 gst_d3d11_window_do_convert (GstD3D11Window * self,
1084     ID3D11ShaderResourceView * srv[GST_VIDEO_MAX_PLANES],
1085     ID3D11RenderTargetView * rtv)
1086 {
1087   if (!gst_d3d11_converter_convert_unlocked (self->converter,
1088           srv, &rtv, NULL, NULL)) {
1089     GST_ERROR_OBJECT (self, "Couldn't render to backbuffer using converter");
1090     return FALSE;
1091   } else {
1092     GST_TRACE_OBJECT (self, "Rendered using converter");
1093   }
1094
1095   return TRUE;
1096 }
1097
1098 static GstFlowReturn
1099 gst_d3d111_window_present (GstD3D11Window * self, GstBuffer * buffer,
1100     GstStructure * stats, ID3D11VideoProcessorOutputView * pov,
1101     ID3D11RenderTargetView * rtv)
1102 {
1103   GstD3D11WindowClass *klass = GST_D3D11_WINDOW_GET_CLASS (self);
1104   GstFlowReturn ret = GST_FLOW_OK;
1105   guint present_flags = 0;
1106
1107   if (!buffer)
1108     return GST_FLOW_OK;
1109
1110   {
1111     GstMapInfo infos[GST_VIDEO_MAX_PLANES];
1112     ID3D11ShaderResourceView *srv[GST_VIDEO_MAX_PLANES];
1113     ID3D11VideoProcessorInputView *piv = NULL;
1114     ID3D11Device *device_handle =
1115         gst_d3d11_device_get_device_handle (self->device);
1116     gboolean can_convert = FALSE;
1117     gboolean can_process = FALSE;
1118     gboolean convert_ret = FALSE;
1119
1120     /* Map memory in any case so that we can upload pending stage texture */
1121     if (!gst_d3d11_buffer_map (buffer, device_handle, infos, GST_MAP_READ)) {
1122       GST_ERROR_OBJECT (self, "Couldn't map buffer");
1123       return GST_FLOW_ERROR;
1124     }
1125
1126     can_convert = gst_d3d11_buffer_get_shader_resource_view (buffer, srv);
1127     if (pov) {
1128       can_process = gst_d3d11_window_buffer_ensure_processor_input (self,
1129           buffer, &piv);
1130     }
1131
1132     if (!can_convert && !can_process) {
1133       GST_ERROR_OBJECT (self, "Input texture cannot be used for converter");
1134       return GST_FLOW_ERROR;
1135     }
1136
1137     if (self->first_present) {
1138       D3D11_VIEWPORT viewport;
1139
1140       viewport.TopLeftX = self->render_rect.left;
1141       viewport.TopLeftY = self->render_rect.top;
1142       viewport.Width = self->render_rect.right - self->render_rect.left;
1143       viewport.Height = self->render_rect.bottom - self->render_rect.top;
1144       viewport.MinDepth = 0.0f;
1145       viewport.MaxDepth = 1.0f;
1146       gst_d3d11_converter_update_viewport (self->converter, &viewport);
1147       gst_d3d11_overlay_compositor_update_viewport (self->compositor,
1148           &viewport);
1149     }
1150
1151     /* Converter preference order
1152      * 1) If this texture can be converted via processor, and we used processor
1153      *    previously, use processor
1154      * 2) If SRV is available, use converter
1155      * 3) otherwise, use processor
1156      */
1157     if (can_process && self->processor_in_use) {
1158       convert_ret = gst_d3d11_window_do_processor (self, piv, pov);
1159     } else if (can_convert) {
1160       convert_ret = gst_d3d11_window_do_convert (self, srv, rtv);
1161     } else if (can_process) {
1162       convert_ret = gst_d3d11_window_do_processor (self, piv, pov);
1163     } else {
1164       g_assert_not_reached ();
1165       ret = GST_FLOW_ERROR;
1166       goto unmap_and_out;
1167     }
1168
1169     if (!convert_ret) {
1170       ret = GST_FLOW_ERROR;
1171       goto unmap_and_out;
1172     }
1173
1174     gst_d3d11_overlay_compositor_upload (self->compositor, buffer);
1175     gst_d3d11_overlay_compositor_draw_unlocked (self->compositor, &rtv);
1176
1177 #if (GST_D3D11_DXGI_HEADER_VERSION >= 5)
1178     if (self->allow_tearing && self->fullscreen) {
1179       present_flags |= DXGI_PRESENT_ALLOW_TEARING;
1180     }
1181 #endif
1182
1183 #if HAVE_DIRECT_WRITE
1184     gst_d3d11_window_present_d2d (self, stats);
1185 #endif
1186
1187     if (klass->present)
1188       ret = klass->present (self, present_flags);
1189
1190     self->first_present = FALSE;
1191
1192   unmap_and_out:
1193     gst_d3d11_buffer_unmap (buffer, infos);
1194   }
1195
1196   return ret;
1197 }
1198
1199 GstFlowReturn
1200 gst_d3d11_window_render (GstD3D11Window * window, GstBuffer * buffer,
1201     GstVideoRectangle * rect, GstStructure * stats)
1202 {
1203   GstMemory *mem;
1204   GstFlowReturn ret;
1205
1206   g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), GST_FLOW_ERROR);
1207   g_return_val_if_fail (rect != NULL, GST_FLOW_ERROR);
1208
1209   mem = gst_buffer_peek_memory (buffer, 0);
1210   if (!gst_is_d3d11_memory (mem)) {
1211     GST_ERROR_OBJECT (window, "Invalid buffer");
1212
1213     if (stats)
1214       gst_structure_free (stats);
1215
1216     return GST_FLOW_ERROR;
1217   }
1218
1219   gst_d3d11_device_lock (window->device);
1220   gst_buffer_replace (&window->cached_buffer, buffer);
1221
1222   ret = gst_d3d111_window_present (window, window->cached_buffer, stats,
1223       window->pov, window->rtv);
1224   gst_d3d11_device_unlock (window->device);
1225
1226   if (stats)
1227     gst_structure_free (stats);
1228
1229   return ret;
1230 }
1231
1232 GstFlowReturn
1233 gst_d3d11_window_render_on_shared_handle (GstD3D11Window * window,
1234     GstBuffer * buffer, HANDLE shared_handle, guint texture_misc_flags,
1235     guint64 acquire_key, guint64 release_key)
1236 {
1237   GstD3D11WindowClass *klass;
1238   GstMemory *mem;
1239   GstFlowReturn ret = GST_FLOW_OK;
1240   GstD3D11WindowSharedHandleData data = { NULL, };
1241   ID3D11VideoProcessorOutputView *pov = NULL;
1242   ID3D11RenderTargetView *rtv = NULL;
1243
1244   g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), GST_FLOW_ERROR);
1245
1246   klass = GST_D3D11_WINDOW_GET_CLASS (window);
1247
1248   g_assert (klass->open_shared_handle != NULL);
1249   g_assert (klass->release_shared_handle != NULL);
1250
1251   mem = gst_buffer_peek_memory (buffer, 0);
1252   if (!gst_is_d3d11_memory (mem)) {
1253     GST_ERROR_OBJECT (window, "Invalid buffer");
1254
1255     return GST_FLOW_ERROR;
1256   }
1257
1258   data.shared_handle = shared_handle;
1259   data.texture_misc_flags = texture_misc_flags;
1260   data.acquire_key = acquire_key;
1261   data.release_key = release_key;
1262
1263   gst_d3d11_device_lock (window->device);
1264   if (!klass->open_shared_handle (window, &data)) {
1265     GST_ERROR_OBJECT (window, "Couldn't open shared handle");
1266     gst_d3d11_device_unlock (window->device);
1267     return GST_FLOW_OK;
1268   }
1269
1270   if (data.fallback_rtv) {
1271     rtv = data.fallback_rtv;
1272     pov = data.fallback_pov;
1273   } else {
1274     rtv = data.rtv;
1275     pov = data.pov;
1276   }
1277
1278   ret = gst_d3d111_window_present (window, buffer, NULL, pov, rtv);
1279
1280   klass->release_shared_handle (window, &data);
1281   gst_d3d11_device_unlock (window->device);
1282
1283   return ret;
1284 }
1285
1286 gboolean
1287 gst_d3d11_window_unlock (GstD3D11Window * window)
1288 {
1289   GstD3D11WindowClass *klass;
1290   gboolean ret = TRUE;
1291
1292   g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), FALSE);
1293
1294   klass = GST_D3D11_WINDOW_GET_CLASS (window);
1295
1296   if (klass->unlock)
1297     ret = klass->unlock (window);
1298
1299   return ret;
1300 }
1301
1302 gboolean
1303 gst_d3d11_window_unlock_stop (GstD3D11Window * window)
1304 {
1305   GstD3D11WindowClass *klass;
1306   gboolean ret = TRUE;
1307
1308   g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), FALSE);
1309
1310   klass = GST_D3D11_WINDOW_GET_CLASS (window);
1311
1312   if (klass->unlock_stop)
1313     ret = klass->unlock_stop (window);
1314
1315   gst_d3d11_device_lock (window->device);
1316   gst_clear_buffer (&window->cached_buffer);
1317   gst_d3d11_device_unlock (window->device);
1318
1319   return ret;
1320 }
1321
1322 void
1323 gst_d3d11_window_unprepare (GstD3D11Window * window)
1324 {
1325   GstD3D11WindowClass *klass;
1326
1327   g_return_if_fail (GST_IS_D3D11_WINDOW (window));
1328
1329   klass = GST_D3D11_WINDOW_GET_CLASS (window);
1330
1331   if (klass->unprepare)
1332     klass->unprepare (window);
1333 }
1334
1335 GstD3D11WindowNativeType
1336 gst_d3d11_window_get_native_type_from_handle (guintptr handle)
1337 {
1338   if (!handle)
1339     return GST_D3D11_WINDOW_NATIVE_TYPE_NONE;
1340
1341 #if (!GST_D3D11_WINAPI_ONLY_APP)
1342   if (IsWindow ((HWND) handle))
1343     return GST_D3D11_WINDOW_NATIVE_TYPE_HWND;
1344 #endif
1345 #if GST_D3D11_WINAPI_ONLY_APP
1346   {
1347     /* *INDENT-OFF* */
1348     ComPtr<IInspectable> window = reinterpret_cast<IInspectable*> (handle);
1349     ComPtr<ABI::Windows::UI::Core::ICoreWindow> core_window;
1350     ComPtr<ABI::Windows::UI::Xaml::Controls::ISwapChainPanel> panel;
1351     /* *INDENT-ON* */
1352
1353     if (SUCCEEDED (window.As (&core_window)))
1354       return GST_D3D11_WINDOW_NATIVE_TYPE_CORE_WINDOW;
1355
1356     if (SUCCEEDED (window.As (&panel)))
1357       return GST_D3D11_WINDOW_NATIVE_TYPE_SWAP_CHAIN_PANEL;
1358   }
1359 #endif
1360
1361   return GST_D3D11_WINDOW_NATIVE_TYPE_NONE;
1362 }
1363
1364 const gchar *
1365 gst_d3d11_window_get_native_type_to_string (GstD3D11WindowNativeType type)
1366 {
1367   switch (type) {
1368     case GST_D3D11_WINDOW_NATIVE_TYPE_NONE:
1369       return "none";
1370     case GST_D3D11_WINDOW_NATIVE_TYPE_HWND:
1371       return "hwnd";
1372     case GST_D3D11_WINDOW_NATIVE_TYPE_CORE_WINDOW:
1373       return "core-window";
1374     case GST_D3D11_WINDOW_NATIVE_TYPE_SWAP_CHAIN_PANEL:
1375       return "swap-chain-panel";
1376     default:
1377       break;
1378   }
1379
1380   return "none";
1381 }