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