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