d3d11videosink: Use single GstD3D11Converter object
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / sys / d3d11 / gstd3d11window_dummy.cpp
1 /*
2  * GStreamer
3  * Copyright (C) 2020 Seungha Yang <seungha@centricular.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_dummy.h"
26 #include "gstd3d11pluginutils.h"
27 #include <wrl.h>
28
29 /* *INDENT-OFF* */
30 using namespace Microsoft::WRL;
31 /* *INDENT-ON* */
32
33 GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_window_debug);
34 #define GST_CAT_DEFAULT gst_d3d11_window_debug
35
36 struct _GstD3D11WindowDummy
37 {
38   GstD3D11Window parent;
39
40   ID3D11Texture2D *fallback_texture;
41   ID3D11VideoProcessorOutputView *fallback_pov;
42   ID3D11RenderTargetView *fallback_rtv;
43 };
44
45 #define gst_d3d11_window_dummy_parent_class parent_class
46 G_DEFINE_TYPE (GstD3D11WindowDummy, gst_d3d11_window_dummy,
47     GST_TYPE_D3D11_WINDOW);
48
49 static void gst_d3d11_window_dummy_on_resize (GstD3D11Window * window,
50     guint width, guint height);
51 static gboolean gst_d3d11_window_dummy_prepare (GstD3D11Window * window,
52     guint display_width, guint display_height, GstCaps * caps, GError ** error);
53 static gboolean
54 gst_d3d11_window_dummy_open_shared_handle (GstD3D11Window * window,
55     GstD3D11WindowSharedHandleData * data);
56 static gboolean
57 gst_d3d11_window_dummy_release_shared_handle (GstD3D11Window * window,
58     GstD3D11WindowSharedHandleData * data);
59
60 static void
61 gst_d3d11_window_dummy_class_init (GstD3D11WindowDummyClass * klass)
62 {
63   GstD3D11WindowClass *window_class = GST_D3D11_WINDOW_CLASS (klass);
64
65   window_class->on_resize =
66       GST_DEBUG_FUNCPTR (gst_d3d11_window_dummy_on_resize);
67   window_class->prepare = GST_DEBUG_FUNCPTR (gst_d3d11_window_dummy_prepare);
68   window_class->open_shared_handle =
69       GST_DEBUG_FUNCPTR (gst_d3d11_window_dummy_open_shared_handle);
70   window_class->release_shared_handle =
71       GST_DEBUG_FUNCPTR (gst_d3d11_window_dummy_release_shared_handle);
72 }
73
74 static void
75 gst_d3d11_window_dummy_init (GstD3D11WindowDummy * self)
76 {
77 }
78
79 static gboolean
80 gst_d3d11_window_dummy_prepare (GstD3D11Window * window,
81     guint display_width, guint display_height, GstCaps * caps, GError ** error)
82 {
83   GstD3D11ConverterMethod method = GST_D3D11_CONVERTER_METHOD_SHADER;
84
85   if (!window->allocator) {
86     window->allocator =
87         (GstD3D11Allocator *) gst_allocator_find (GST_D3D11_MEMORY_NAME);
88     if (!window->allocator) {
89       GST_ERROR_OBJECT (window, "Allocator is unavailable");
90       return FALSE;
91     }
92   }
93
94   gst_clear_object (&window->compositor);
95   gst_clear_object (&window->converter);
96
97   /* We are supporting only RGBA, BGRA or RGB10A2_LE formats but we don't know
98    * which format texture will be used at this moment */
99
100   gst_video_info_from_caps (&window->info, caps);
101   window->render_rect.left = 0;
102   window->render_rect.top = 0;
103   window->render_rect.right = display_width;
104   window->render_rect.bottom = display_height;
105
106   window->input_rect.left = 0;
107   window->input_rect.top = 0;
108   window->input_rect.right = GST_VIDEO_INFO_WIDTH (&window->info);
109   window->input_rect.bottom = GST_VIDEO_INFO_HEIGHT (&window->info);
110
111   gst_video_info_set_format (&window->render_info,
112       GST_VIDEO_FORMAT_BGRA, display_width, display_height);
113
114   /* TODO: not sure which colorspace should be used, let's use BT709 since
115    * it's default and most common one */
116   window->render_info.colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT709;
117   window->render_info.colorimetry.transfer = GST_VIDEO_TRANSFER_BT709;
118   window->render_info.colorimetry.range = GST_VIDEO_COLOR_RANGE_0_255;
119
120   gst_d3d11_device_lock (window->device);
121   window->converter = gst_d3d11_converter_new (window->device, &window->info,
122       &window->render_info, &method);
123
124   if (!window->converter) {
125     GST_ERROR_OBJECT (window, "Cannot create converter");
126     g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
127         "Cannot create converter");
128     goto error;
129   }
130
131   window->compositor =
132       gst_d3d11_overlay_compositor_new (window->device, &window->render_info);
133   if (!window->compositor) {
134     GST_ERROR_OBJECT (window, "Cannot create overlay compositor");
135     g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
136         "Cannot create overlay compositor");
137     goto error;
138   }
139
140   gst_d3d11_device_unlock (window->device);
141
142   return TRUE;
143
144 error:
145   gst_d3d11_device_unlock (window->device);
146
147   return FALSE;
148 }
149
150 static void
151 gst_d3d11_window_dummy_on_resize (GstD3D11Window * window,
152     guint width, guint height)
153 {
154   GstVideoRectangle src_rect, dst_rect, rst_rect;
155
156   dst_rect.x = 0;
157   dst_rect.y = 0;
158   dst_rect.w = width;
159   dst_rect.h = height;
160
161   if (window->force_aspect_ratio) {
162     src_rect.x = 0;
163     src_rect.y = 0;
164     src_rect.w = GST_VIDEO_INFO_WIDTH (&window->render_info);
165     src_rect.h = GST_VIDEO_INFO_HEIGHT (&window->render_info);
166
167     gst_video_sink_center_rect (src_rect, dst_rect, &rst_rect, TRUE);
168   } else {
169     rst_rect = dst_rect;
170   }
171
172   window->render_rect.left = rst_rect.x;
173   window->render_rect.top = rst_rect.y;
174   window->render_rect.right = rst_rect.x + rst_rect.w;
175   window->render_rect.bottom = rst_rect.y + rst_rect.h;
176
177   window->first_present = TRUE;
178 }
179
180 static gboolean
181 gst_d3d11_window_dummy_open_shared_handle (GstD3D11Window * window,
182     GstD3D11WindowSharedHandleData * data)
183 {
184   GstD3D11Device *device = window->device;
185   ID3D11Device *device_handle;
186   HRESULT hr;
187   ComPtr < ID3D11Texture2D > texture;
188   ComPtr < IDXGIKeyedMutex > keyed_mutex;
189   ID3D11RenderTargetView *rtv;
190   GstMemory *mem;
191   GstD3D11Memory *dmem;
192   D3D11_TEXTURE2D_DESC desc;
193   gboolean use_keyed_mutex = FALSE;
194
195   device_handle = gst_d3d11_device_get_device_handle (device);
196
197   if ((data->texture_misc_flags & D3D11_RESOURCE_MISC_SHARED_NTHANDLE) ==
198       D3D11_RESOURCE_MISC_SHARED_NTHANDLE) {
199     ComPtr < ID3D11Device1 > device1_handle;
200
201     hr = device_handle->QueryInterface (IID_PPV_ARGS (&device1_handle));
202     if (!gst_d3d11_result (hr, device))
203       return FALSE;
204
205     hr = device1_handle->OpenSharedResource1 (data->shared_handle,
206         IID_PPV_ARGS (&texture));
207   } else {
208     hr = device_handle->OpenSharedResource (data->shared_handle,
209         IID_PPV_ARGS (&texture));
210   }
211
212   if (!gst_d3d11_result (hr, device))
213     return FALSE;
214
215   texture->GetDesc (&desc);
216   use_keyed_mutex = (desc.MiscFlags & D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX) ==
217       D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
218
219   if (use_keyed_mutex) {
220     hr = texture->QueryInterface (IID_PPV_ARGS (&keyed_mutex));
221     if (!gst_d3d11_result (hr, device)) {
222       GST_ERROR_OBJECT (window, "Keyed mutex is unavailable");
223       return FALSE;
224     }
225   }
226
227   mem = gst_d3d11_allocator_alloc_wrapped_native_size (window->allocator,
228       device, texture.Get (), nullptr, nullptr);
229   if (!mem) {
230     GST_ERROR_OBJECT (window, "Couldn't allocate memory");
231     return FALSE;
232   }
233
234   dmem = GST_D3D11_MEMORY_CAST (mem);
235   rtv = gst_d3d11_memory_get_render_target_view (dmem, 0);
236   if (!rtv) {
237     GST_ERROR_OBJECT (window, "Render target view is unavailable");
238     gst_memory_unref (mem);
239     return FALSE;
240   }
241
242   if (keyed_mutex) {
243     hr = keyed_mutex->AcquireSync (data->acquire_key, INFINITE);
244     if (!gst_d3d11_result (hr, device)) {
245       GST_ERROR_OBJECT (window, "Couldn't acquire sync");
246       gst_memory_unref (mem);
247       return FALSE;
248     }
249   }
250
251   /* Everything is prepared now */
252   gst_d3d11_window_dummy_on_resize (window, desc.Width, desc.Height);
253
254   /* Move owned resources */
255   data->render_target = gst_buffer_new ();
256   gst_buffer_append_memory (data->render_target, mem);
257   if (keyed_mutex)
258     data->keyed_mutex = keyed_mutex.Detach ();
259
260   return TRUE;
261 }
262
263 static gboolean
264 gst_d3d11_window_dummy_release_shared_handle (GstD3D11Window * window,
265     GstD3D11WindowSharedHandleData * data)
266 {
267   GstD3D11WindowDummy *self = GST_D3D11_WINDOW_DUMMY (window);
268   GstD3D11Device *device = window->device;
269   HRESULT hr;
270
271   /* TODO: cache owned resource for the later reuse? */
272   if (data->keyed_mutex) {
273     hr = data->keyed_mutex->ReleaseSync (data->release_key);
274     gst_d3d11_result (hr, device);
275
276     GST_D3D11_CLEAR_COM (data->keyed_mutex);
277   } else {
278     /* *INDENT-OFF* */
279     ComPtr<ID3D11Query> query;
280     /* *INDENT-ON* */
281     D3D11_QUERY_DESC query_desc;
282     ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device);
283     ID3D11DeviceContext *context_handle =
284         gst_d3d11_device_get_device_context_handle (device);
285     BOOL sync_done = FALSE;
286
287     /* If keyed mutex is not used, let's handle sync manually by using
288      * ID3D11Query. Issued GPU commands might not be finished yet */
289     query_desc.Query = D3D11_QUERY_EVENT;
290     query_desc.MiscFlags = 0;
291
292     hr = device_handle->CreateQuery (&query_desc, &query);
293     if (!gst_d3d11_result (hr, device)) {
294       GST_ERROR_OBJECT (self, "Couldn't Create event query");
295       return FALSE;
296     }
297
298     context_handle->End (query.Get ());
299
300     /* Wait until all issued GPU commands are finished */
301     do {
302       hr = context_handle->GetData (query.Get (), &sync_done, sizeof (BOOL), 0);
303     } while (!sync_done && (hr == S_OK || hr == S_FALSE));
304
305     if (!gst_d3d11_result (hr, device)) {
306       GST_ERROR_OBJECT (self, "Couldn't sync GPU operation");
307       return FALSE;
308     }
309   }
310
311   gst_clear_buffer (&data->render_target);
312
313   return TRUE;
314 }
315
316 GstD3D11Window *
317 gst_d3d11_window_dummy_new (GstD3D11Device * device)
318 {
319   GstD3D11Window *window;
320
321   g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
322
323   window = (GstD3D11Window *)
324       g_object_new (GST_TYPE_D3D11_WINDOW_DUMMY, "d3d11device", device, NULL);
325
326   window->initialized = TRUE;
327   g_object_ref_sink (window);
328
329   return window;
330 }