• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_corewindow.h"
26 
27 /* workaround for GetCurrentTime collision */
28 #ifdef GetCurrentTime
29 #undef GetCurrentTime
30 #endif
31 #include <windows.ui.xaml.h>
32 #include <windows.ui.xaml.media.dxinterop.h>
33 #include <windows.applicationmodel.core.h>
34 #include <wrl.h>
35 #include <wrl/wrappers/corewrappers.h>
36 #include <windows.graphics.display.h>
37 
38 /* *INDENT-OFF* */
39 using namespace Microsoft::WRL;
40 using namespace Microsoft::WRL::Wrappers;
41 using namespace ABI::Windows::UI;
42 using namespace ABI::Windows::Foundation;
43 using namespace ABI::Windows::Foundation::Collections;
44 using namespace ABI::Windows::Graphics;
45 
46 typedef ABI::Windows::Foundation::
47     __FITypedEventHandler_2_Windows__CUI__CCore__CCoreWindow_Windows__CUI__CCore__CWindowSizeChangedEventArgs_t
48         IWindowSizeChangedEventHandler;
49 
50 GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_window_debug);
51 #define GST_CAT_DEFAULT gst_d3d11_window_debug
52 
53 /* timeout to wait busy UI thread */
54 #define DEFAULT_ASYNC_TIMEOUT (10 * 1000)
55 
56 typedef struct _CoreWindowWinRTStorage
57 {
58   ComPtr<Core::ICoreWindow> core_window;
59   ComPtr<Core::ICoreDispatcher> dispatcher;
60   HANDLE cancellable;
61   EventRegistrationToken event_token;
62 } CoreWindowWinRTStorage;
63 /* *INDENT-ON* */
64 
65 struct _GstD3D11WindowCoreWindow
66 {
67   GstD3D11Window parent;
68 
69   CoreWindowWinRTStorage *storage;
70 };
71 
72 #define gst_d3d11_window_core_window_parent_class parent_class
73 G_DEFINE_TYPE (GstD3D11WindowCoreWindow, gst_d3d11_window_core_window,
74     GST_TYPE_D3D11_WINDOW);
75 
76 static void gst_d3d11_window_core_window_constructed (GObject * object);
77 static void gst_d3d11_window_core_window_dispose (GObject * object);
78 static void gst_d3d11_window_core_window_update_swap_chain (GstD3D11Window *
79     window);
80 static void gst_d3d11_window_core_window_change_fullscreen_mode (GstD3D11Window
81     * window);
82 static gboolean gst_d3d11_window_core_window_create_swap_chain (GstD3D11Window *
83     window, DXGI_FORMAT format, guint width, guint height,
84     guint swapchain_flags, IDXGISwapChain ** swap_chain);
85 static GstFlowReturn gst_d3d11_window_core_window_present (GstD3D11Window *
86     window, guint present_flags);
87 static gboolean gst_d3d11_window_core_window_unlock (GstD3D11Window * window);
88 static gboolean
89 gst_d3d11_window_core_window_unlock_stop (GstD3D11Window * window);
90 static void
91 gst_d3d11_window_core_window_on_resize (GstD3D11Window * window,
92     guint width, guint height);
93 static void
94 gst_d3d11_window_core_window_on_resize_sync (GstD3D11Window * window);
95 static void gst_d3d11_window_core_window_unprepare (GstD3D11Window * window);
96 
97 static float
get_logical_dpi(void)98 get_logical_dpi (void)
99 {
100   /* *INDENT-OFF* */
101   ComPtr<Display::IDisplayPropertiesStatics> properties;
102   /* *INDENT-ON* */
103   HRESULT hr;
104   HStringReference str_ref =
105       HStringReference
106       (RuntimeClass_Windows_Graphics_Display_DisplayProperties);
107 
108   hr = GetActivationFactory (str_ref.Get (), properties.GetAddressOf ());
109 
110   if (gst_d3d11_result (hr, NULL)) {
111     float dpi = 96.0f;
112 
113     hr = properties->get_LogicalDpi (&dpi);
114     if (gst_d3d11_result (hr, NULL))
115       return dpi;
116   }
117 
118   return 96.0f;
119 }
120 
121 static inline float
dip_to_pixel(float dip)122 dip_to_pixel (float dip)
123 {
124   /* https://docs.microsoft.com/en-us/windows/win32/learnwin32/dpi-and-device-independent-pixels */
125   return dip * get_logical_dpi () / 96.0f;
126 }
127 
128 /* *INDENT-OFF* */
129 class CoreResizeHandler
130     : public RuntimeClass<RuntimeClassFlags<ClassicCom>,
131         IWindowSizeChangedEventHandler>
132 {
133 public:
CoreResizeHandler()134   CoreResizeHandler () {}
RuntimeClassInitialize(GstD3D11Window * listener)135   HRESULT RuntimeClassInitialize (GstD3D11Window * listener)
136   {
137     if (!listener)
138       return E_INVALIDARG;
139 
140     window = listener;
141     return S_OK;
142   }
143 
IFACEMETHOD(Invoke)144   IFACEMETHOD(Invoke)
145   (Core::ICoreWindow * sender, Core::IWindowSizeChangedEventArgs * args)
146   {
147     if (window) {
148       Size new_size;
149       HRESULT hr = args->get_Size(&new_size);
150       if (gst_d3d11_result (hr, NULL)) {
151         guint width, height;
152 
153         width = (guint) dip_to_pixel (new_size.Width);
154         height = (guint) dip_to_pixel (new_size.Height);
155 
156         window->surface_width = width;
157         window->surface_height = height;
158         gst_d3d11_window_core_window_on_resize_sync (window);
159       }
160     }
161 
162     return S_OK;
163   }
164 
165 private:
166   GstD3D11Window * window;
167 };
168 
169 template <typename CB>
170 static HRESULT
run_async(const ComPtr<Core::ICoreDispatcher> & dispatcher,HANDLE cancellable,DWORD timeout,CB && cb)171 run_async (const ComPtr<Core::ICoreDispatcher> &dispatcher, HANDLE cancellable,
172     DWORD timeout, CB &&cb)
173 {
174   ComPtr<IAsyncAction> async_action;
175   HRESULT hr;
176   HRESULT async_hr;
177   boolean can_now;
178   DWORD wait_ret;
179   HANDLE event_handle[2];
180 
181   hr = dispatcher->get_HasThreadAccess (&can_now);
182 
183   if (!gst_d3d11_result (hr, NULL))
184     return hr;
185 
186   if (can_now)
187     return cb ();
188 
189   Event event (CreateEventEx (NULL, NULL, CREATE_EVENT_MANUAL_RESET,
190       EVENT_ALL_ACCESS));
191 
192   if (!event.IsValid())
193     return E_FAIL;
194 
195   auto handler =
196       Callback<Implements<RuntimeClassFlags<ClassicCom>,
197           Core::IDispatchedHandler, FtmBase>>([&async_hr, &cb, &event] {
198         async_hr = cb ();
199         SetEvent (event.Get());
200         return S_OK;
201       });
202 
203   hr = dispatcher->RunAsync (Core::CoreDispatcherPriority_Normal, handler.Get(),
204       async_action.GetAddressOf());
205 
206   if (!gst_d3d11_result (hr, NULL))
207     return hr;
208 
209   event_handle[0] = event.Get();
210   event_handle[1] = cancellable;
211 
212   wait_ret = WaitForMultipleObjects (2, event_handle, FALSE, timeout);
213   if (wait_ret != WAIT_OBJECT_0)
214     return E_FAIL;
215 
216   return async_hr;
217 }
218 
219 static HRESULT
get_window_size(const ComPtr<Core::ICoreDispatcher> & dispatcher,HANDLE cancellable,const ComPtr<Core::ICoreWindow> & window,Size * size)220 get_window_size (const ComPtr<Core::ICoreDispatcher> &dispatcher,
221     HANDLE cancellable,
222     const ComPtr<Core::ICoreWindow> &window, Size *size)
223 {
224   return run_async (dispatcher, cancellable, INFINITE,
225       [window, size] {
226         HRESULT hr;
227         Rect bounds;
228 
229         hr = window->get_Bounds (&bounds);
230         if (gst_d3d11_result (hr, NULL)) {
231           size->Width = dip_to_pixel (bounds.Width);
232           size->Height = dip_to_pixel (bounds.Height);
233         }
234 
235         return hr;
236       });
237 }
238 /* *INDENT-ON* */
239 
240 static void
gst_d3d11_window_core_window_class_init(GstD3D11WindowCoreWindowClass * klass)241 gst_d3d11_window_core_window_class_init (GstD3D11WindowCoreWindowClass * klass)
242 {
243   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
244   GstD3D11WindowClass *window_class = GST_D3D11_WINDOW_CLASS (klass);
245 
246   gobject_class->constructed = gst_d3d11_window_core_window_constructed;
247   gobject_class->dispose = gst_d3d11_window_core_window_dispose;
248 
249   window_class->update_swap_chain =
250       GST_DEBUG_FUNCPTR (gst_d3d11_window_core_window_update_swap_chain);
251   window_class->change_fullscreen_mode =
252       GST_DEBUG_FUNCPTR (gst_d3d11_window_core_window_change_fullscreen_mode);
253   window_class->create_swap_chain =
254       GST_DEBUG_FUNCPTR (gst_d3d11_window_core_window_create_swap_chain);
255   window_class->present =
256       GST_DEBUG_FUNCPTR (gst_d3d11_window_core_window_present);
257   window_class->unlock =
258       GST_DEBUG_FUNCPTR (gst_d3d11_window_core_window_unlock);
259   window_class->unlock_stop =
260       GST_DEBUG_FUNCPTR (gst_d3d11_window_core_window_unlock_stop);
261   window_class->on_resize =
262       GST_DEBUG_FUNCPTR (gst_d3d11_window_core_window_on_resize);
263   window_class->unprepare =
264       GST_DEBUG_FUNCPTR (gst_d3d11_window_core_window_unprepare);
265 }
266 
267 static void
gst_d3d11_window_core_window_init(GstD3D11WindowCoreWindow * self)268 gst_d3d11_window_core_window_init (GstD3D11WindowCoreWindow * self)
269 {
270   self->storage = new CoreWindowWinRTStorage;
271   self->storage->cancellable = CreateEvent (NULL, TRUE, FALSE, NULL);
272 }
273 
274 /* *INDENT-OFF* */
275 static void
gst_d3d11_window_core_window_constructed(GObject * object)276 gst_d3d11_window_core_window_constructed (GObject * object)
277 {
278   GstD3D11Window *window = GST_D3D11_WINDOW (object);
279   GstD3D11WindowCoreWindow *self = GST_D3D11_WINDOW_CORE_WINDOW (object);
280   CoreWindowWinRTStorage *storage = self->storage;
281   HRESULT hr;
282   ComPtr<IInspectable> inspectable;
283   ComPtr<IWindowSizeChangedEventHandler> resize_handler;
284   ComPtr<Core::ICoreWindow> core_window;
285   Size size;
286 
287   if (!window->external_handle) {
288     GST_ERROR_OBJECT (self, "No external window handle");
289     return;
290   }
291 
292   inspectable = reinterpret_cast<IInspectable*> (window->external_handle);
293 
294   hr = inspectable.As (&storage->core_window);
295   if (!gst_d3d11_result (hr, NULL))
296     goto error;
297 
298   hr = storage->core_window->get_Dispatcher (&storage->dispatcher);
299   if (!gst_d3d11_result (hr, NULL))
300     goto error;
301 
302   hr = get_window_size (storage->dispatcher, storage->cancellable,
303       storage->core_window, &size);
304   if (!gst_d3d11_result (hr, NULL))
305     goto error;
306 
307   GST_DEBUG_OBJECT (self,
308       "client size %dx%d", window->surface_width, window->surface_height);
309   window->surface_width = size.Width;
310   window->surface_height = size.Height;
311 
312   hr = MakeAndInitialize<CoreResizeHandler>(&resize_handler, window);
313   if (!gst_d3d11_result (hr, NULL))
314     goto error;
315 
316   hr = storage->core_window.As (&core_window);
317   if (!gst_d3d11_result (hr, NULL))
318     goto error;
319 
320   hr = run_async (storage->dispatcher,
321       storage->cancellable, DEFAULT_ASYNC_TIMEOUT,
322       [self, core_window, resize_handler] {
323         return core_window->add_SizeChanged (resize_handler.Get(),
324             &self->storage->event_token);
325       });
326   if (!gst_d3d11_result (hr, NULL))
327     goto error;
328 
329   window->initialized = TRUE;
330   return;
331 
332 error:
333   GST_ERROR_OBJECT (self, "Invalid window handle");
334   return;
335 }
336 /* *INDENT-ON* */
337 
338 static void
gst_d3d11_window_core_window_dispose(GObject * object)339 gst_d3d11_window_core_window_dispose (GObject * object)
340 {
341   gst_d3d11_window_core_window_unprepare (GST_D3D11_WINDOW (object));
342 
343   G_OBJECT_CLASS (parent_class)->dispose (object);
344 }
345 
346 /* *INDENT-OFF* */
347 static void
gst_d3d11_window_core_window_unprepare(GstD3D11Window * window)348 gst_d3d11_window_core_window_unprepare (GstD3D11Window * window)
349 {
350   GstD3D11WindowCoreWindow *self = GST_D3D11_WINDOW_CORE_WINDOW (window);
351   CoreWindowWinRTStorage *storage = self->storage;
352 
353   if (storage) {
354     if (storage->core_window && storage->dispatcher) {
355       ComPtr<Core::ICoreWindow> window;
356       HRESULT hr;
357 
358       hr = storage->core_window.As (&window);
359       if (SUCCEEDED (hr)) {
360         run_async (storage->dispatcher,
361             storage->cancellable, DEFAULT_ASYNC_TIMEOUT,
362             [self, window] {
363               return window->remove_SizeChanged (self->storage->event_token);
364             });
365       }
366     }
367 
368     CloseHandle (storage->cancellable);
369     delete storage;
370   }
371 
372   self->storage = NULL;
373 }
374 /* *INDENT-ON* */
375 
376 static IDXGISwapChain1 *
create_swap_chain_for_core_window(GstD3D11WindowCoreWindow * self,GstD3D11Device * device,guintptr core_window,DXGI_SWAP_CHAIN_DESC1 * desc,IDXGIOutput * output)377 create_swap_chain_for_core_window (GstD3D11WindowCoreWindow * self,
378     GstD3D11Device * device, guintptr core_window, DXGI_SWAP_CHAIN_DESC1 * desc,
379     IDXGIOutput * output)
380 {
381   HRESULT hr;
382   IDXGISwapChain1 *swap_chain = NULL;
383   ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device);
384   IDXGIFactory1 *factory = gst_d3d11_device_get_dxgi_factory_handle (device);
385   ComPtr < IDXGIFactory2 > factory2;
386 
387   hr = factory->QueryInterface (IID_PPV_ARGS (&factory2));
388   if (!gst_d3d11_result (hr, device)) {
389     GST_WARNING_OBJECT (self, "IDXGIFactory2 interface is unavailable");
390     return NULL;
391   }
392 
393   gst_d3d11_device_lock (device);
394   hr = factory2->CreateSwapChainForCoreWindow (device_handle,
395       (IUnknown *) core_window, desc, output, &swap_chain);
396   gst_d3d11_device_unlock (device);
397 
398   if (!gst_d3d11_result (hr, device)) {
399     GST_WARNING_OBJECT (self, "Cannot create SwapChain Object: 0x%x",
400         (guint) hr);
401     swap_chain = NULL;
402   }
403 
404   return swap_chain;
405 }
406 
407 static gboolean
gst_d3d11_window_core_window_create_swap_chain(GstD3D11Window * window,DXGI_FORMAT format,guint width,guint height,guint swapchain_flags,IDXGISwapChain ** swap_chain)408 gst_d3d11_window_core_window_create_swap_chain (GstD3D11Window * window,
409     DXGI_FORMAT format, guint width, guint height, guint swapchain_flags,
410     IDXGISwapChain ** swap_chain)
411 {
412   GstD3D11WindowCoreWindow *self = GST_D3D11_WINDOW_CORE_WINDOW (window);
413   /* *INDENT-OFF* */
414   ComPtr<IDXGISwapChain1> new_swapchain;
415   /* *INDENT-ON* */
416   GstD3D11Device *device = window->device;
417   DXGI_SWAP_CHAIN_DESC1 desc1 = { 0, };
418 
419   desc1.Width = width;
420   desc1.Height = height;
421   desc1.Format = format;
422   desc1.Stereo = FALSE;
423   desc1.SampleDesc.Count = 1;
424   desc1.SampleDesc.Quality = 0;
425   desc1.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
426   desc1.BufferCount = 2;
427   desc1.Scaling = DXGI_SCALING_NONE;
428   desc1.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
429   desc1.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
430   desc1.Flags = swapchain_flags;
431 
432   new_swapchain =
433       create_swap_chain_for_core_window (self, device,
434       window->external_handle, &desc1, NULL);
435 
436   if (!new_swapchain) {
437     GST_ERROR_OBJECT (self, "Cannot create swapchain");
438     return FALSE;
439   }
440 
441   new_swapchain.CopyTo (swap_chain);
442 
443   return TRUE;
444 }
445 
446 static GstFlowReturn
gst_d3d11_window_core_window_present(GstD3D11Window * window,guint present_flags)447 gst_d3d11_window_core_window_present (GstD3D11Window * window,
448     guint present_flags)
449 {
450   GstD3D11WindowCoreWindow *self = GST_D3D11_WINDOW_CORE_WINDOW (window);
451   HRESULT hr;
452   DXGI_PRESENT_PARAMETERS present_params = { 0, };
453   IDXGISwapChain1 *swap_chain = (IDXGISwapChain1 *) window->swap_chain;
454 
455   /* the first present should not specify dirty-rect */
456   if (!window->first_present) {
457     present_params.DirtyRectsCount = 1;
458     present_params.pDirtyRects = &window->render_rect;
459   }
460 
461   hr = swap_chain->Present1 (0, present_flags, &present_params);
462 
463   if (!gst_d3d11_result (hr, window->device)) {
464     GST_WARNING_OBJECT (self, "Direct3D cannot present texture, hr: 0x%x",
465         (guint) hr);
466   }
467 
468   return GST_FLOW_OK;
469 }
470 
471 static gboolean
gst_d3d11_window_core_window_unlock(GstD3D11Window * window)472 gst_d3d11_window_core_window_unlock (GstD3D11Window * window)
473 {
474   GstD3D11WindowCoreWindow *self = GST_D3D11_WINDOW_CORE_WINDOW (window);
475   CoreWindowWinRTStorage *storage = self->storage;
476 
477   SetEvent (storage->cancellable);
478 
479   return TRUE;
480 }
481 
482 static gboolean
gst_d3d11_window_core_window_unlock_stop(GstD3D11Window * window)483 gst_d3d11_window_core_window_unlock_stop (GstD3D11Window * window)
484 {
485   GstD3D11WindowCoreWindow *self = GST_D3D11_WINDOW_CORE_WINDOW (window);
486   CoreWindowWinRTStorage *storage = self->storage;
487 
488   ResetEvent (storage->cancellable);
489 
490   return TRUE;
491 }
492 
493 static void
gst_d3d11_window_core_window_update_swap_chain(GstD3D11Window * window)494 gst_d3d11_window_core_window_update_swap_chain (GstD3D11Window * window)
495 {
496   gst_d3d11_window_core_window_on_resize (window,
497       window->surface_width, window->surface_height);
498 
499   return;
500 }
501 
502 static void
gst_d3d11_window_core_window_change_fullscreen_mode(GstD3D11Window * window)503 gst_d3d11_window_core_window_change_fullscreen_mode (GstD3D11Window * window)
504 {
505   GST_FIXME_OBJECT (window, "Implement fullscreen mode change");
506 
507   return;
508 }
509 
510 static void
gst_d3d11_window_core_window_on_resize(GstD3D11Window * window,guint width,guint height)511 gst_d3d11_window_core_window_on_resize (GstD3D11Window * window,
512     guint width, guint height)
513 {
514   GstD3D11WindowCoreWindow *self = GST_D3D11_WINDOW_CORE_WINDOW (window);
515   CoreWindowWinRTStorage *storage = self->storage;
516 
517   /* *INDENT-OFF* */
518   run_async (storage->dispatcher, storage->cancellable, INFINITE,
519       [window] {
520         gst_d3d11_window_core_window_on_resize_sync (window);
521         return S_OK;
522       });
523   /* *INDENT-ON* */
524 }
525 
526 static void
gst_d3d11_window_core_window_on_resize_sync(GstD3D11Window * window)527 gst_d3d11_window_core_window_on_resize_sync (GstD3D11Window * window)
528 {
529   GST_LOG_OBJECT (window,
530       "New size %dx%d", window->surface_width, window->surface_height);
531 
532   GST_D3D11_WINDOW_CLASS (parent_class)->on_resize (window,
533       window->surface_width, window->surface_height);
534 }
535 
536 GstD3D11Window *
gst_d3d11_window_core_window_new(GstD3D11Device * device,guintptr handle)537 gst_d3d11_window_core_window_new (GstD3D11Device * device, guintptr handle)
538 {
539   GstD3D11Window *window;
540 
541   g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
542   g_return_val_if_fail (handle, NULL);
543 
544   window = (GstD3D11Window *)
545       g_object_new (GST_TYPE_D3D11_WINDOW_CORE_WINDOW,
546       "d3d11device", device, "window-handle", handle, NULL);
547 
548   if (!window->initialized) {
549     gst_object_unref (window);
550     return NULL;
551   }
552 
553   g_object_ref_sink (window);
554 
555   return window;
556 }
557