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