2 * Copyright (C) <2019> Seungha Yang <seungha.yang@navercorp.com>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
24 #include "gstd3d11overlaycompositor.h"
25 #include "gstd3d11shader.h"
26 #include "gstd3d11pluginutils.h"
29 GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_overlay_compositor_debug);
30 #define GST_CAT_DEFAULT gst_d3d11_overlay_compositor_debug
33 using namespace Microsoft::WRL;
48 static const gchar templ_pixel_shader[] =
49 "Texture2D shaderTexture;\n"
50 "SamplerState samplerState;\n"
53 " float4 Position: SV_POSITION;\n"
54 " float2 Texture: TEXCOORD;\n"
56 "float4 main(PS_INPUT input): SV_TARGET\n"
58 " return shaderTexture.Sample(samplerState, input.Texture);\n"
61 static const gchar templ_vertex_shader[] =
64 " float4 Position : POSITION;\n"
65 " float2 Texture : TEXCOORD;\n"
70 " float4 Position: SV_POSITION;\n"
71 " float2 Texture: TEXCOORD;\n"
74 "VS_OUTPUT main(VS_INPUT input)\n"
80 struct _GstD3D11OverlayCompositor
82 GstD3D11Device *device;
83 GstVideoInfo out_info;
85 D3D11_VIEWPORT viewport;
87 ID3D11PixelShader *ps;
88 ID3D11VertexShader *vs;
89 ID3D11InputLayout *layout;
90 ID3D11SamplerState *sampler;
91 ID3D11BlendState *blend;
92 ID3D11Buffer *index_buffer;
94 /* GstD3D11CompositionOverlay */
100 GstVideoOverlayRectangle *overlay_rect;
101 ID3D11Texture2D *texture;
102 ID3D11ShaderResourceView *srv;
103 ID3D11Buffer *vertex_buffer;
104 } GstD3D11CompositionOverlay;
106 static GstD3D11CompositionOverlay *
107 gst_d3d11_composition_overlay_new (GstD3D11OverlayCompositor * self,
108 GstVideoOverlayRectangle * overlay_rect)
110 GstD3D11CompositionOverlay *overlay = NULL;
113 D3D11_SUBRESOURCE_DATA subresource_data;
114 D3D11_TEXTURE2D_DESC texture_desc;
115 D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc;
116 D3D11_BUFFER_DESC buffer_desc;
117 D3D11_MAPPED_SUBRESOURCE map;
118 VertexData *vertex_data;
125 ID3D11Device *device_handle;
126 ID3D11DeviceContext *context_handle;
127 GstD3D11Device *device = self->device;
128 FLOAT x1, y1, x2, y2;
130 ComPtr < ID3D11Texture2D > texture;
131 ComPtr < ID3D11ShaderResourceView > srv;
132 ComPtr < ID3D11Buffer > vertex_buffer;
134 g_return_val_if_fail (overlay_rect != NULL, NULL);
136 memset (&subresource_data, 0, sizeof (subresource_data));
137 memset (&texture_desc, 0, sizeof (texture_desc));
138 memset (&srv_desc, 0, sizeof (srv_desc));
139 memset (&buffer_desc, 0, sizeof (buffer_desc));
141 device_handle = gst_d3d11_device_get_device_handle (device);
142 context_handle = gst_d3d11_device_get_device_context_handle (device);
144 if (!gst_video_overlay_rectangle_get_render_rectangle (overlay_rect, &x, &y,
146 GST_ERROR ("Failed to get render rectangle");
150 buf = gst_video_overlay_rectangle_get_pixels_unscaled_argb (overlay_rect,
151 GST_VIDEO_OVERLAY_FORMAT_FLAG_NONE);
153 GST_ERROR ("Failed to get overlay buffer");
157 vmeta = gst_buffer_get_video_meta (buf);
159 GST_ERROR ("Failed to get video meta");
163 if (!gst_video_meta_map (vmeta,
164 0, &info, (gpointer *) & data, &stride, GST_MAP_READ)) {
165 GST_ERROR ("Failed to map");
169 /* Do create texture and upload data at once, for create immutable texture */
170 subresource_data.pSysMem = data;
171 subresource_data.SysMemPitch = stride;
172 subresource_data.SysMemSlicePitch = 0;
174 texture_desc.Width = width;
175 texture_desc.Height = height;
176 texture_desc.MipLevels = 1;
177 texture_desc.ArraySize = 1;
178 /* FIXME: need to consider non-BGRA ? */
179 texture_desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
180 texture_desc.SampleDesc.Count = 1;
181 texture_desc.SampleDesc.Quality = 0;
182 texture_desc.Usage = D3D11_USAGE_IMMUTABLE;
183 texture_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
184 texture_desc.CPUAccessFlags = 0;
186 hr = device_handle->CreateTexture2D (&texture_desc,
187 &subresource_data, &texture);
188 gst_video_meta_unmap (vmeta, 0, &info);
190 if (!gst_d3d11_result (hr, device)) {
191 GST_ERROR ("Failed to create texture");
195 srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
196 srv_desc.Texture2D.MipLevels = 1;
198 hr = device_handle->CreateShaderResourceView (texture.Get (), &srv_desc,
200 if (!gst_d3d11_result (hr, device) || !srv) {
201 GST_ERROR ("Failed to create shader resource view");
205 buffer_desc.Usage = D3D11_USAGE_DYNAMIC;
206 buffer_desc.ByteWidth = sizeof (VertexData) * 4;
207 buffer_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
208 buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
210 hr = device_handle->CreateBuffer (&buffer_desc, NULL, &vertex_buffer);
211 if (!gst_d3d11_result (hr, device)) {
212 GST_ERROR ("Couldn't create vertex buffer, hr: 0x%x", (guint) hr);
216 gst_d3d11_device_lock (device);
217 hr = context_handle->Map (vertex_buffer.Get (),
218 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
220 if (!gst_d3d11_result (hr, device)) {
221 GST_ERROR ("Couldn't map vertex buffer, hr: 0x%x", (guint) hr);
222 gst_d3d11_device_unlock (device);
226 vertex_data = (VertexData *) map.pData;
228 gst_util_fraction_to_double (x, GST_VIDEO_INFO_WIDTH (&self->out_info), &val);
229 x1 = (val * 2.0f) - 1.0f;
231 gst_util_fraction_to_double (y + height,
232 GST_VIDEO_INFO_HEIGHT (&self->out_info), &val);
233 y1 = (val * -2.0f) + 1.0f;
236 gst_util_fraction_to_double (x + width,
237 GST_VIDEO_INFO_WIDTH (&self->out_info), &val);
238 x2 = (val * 2.0f) - 1.0f;
240 gst_util_fraction_to_double (y,
241 GST_VIDEO_INFO_HEIGHT (&self->out_info), &val);
242 y2 = (val * -2.0f) + 1.0f;
245 vertex_data[0].position.x = x1;
246 vertex_data[0].position.y = y1;
247 vertex_data[0].position.z = 0.0f;
248 vertex_data[0].texture.u = 0.0f;
249 vertex_data[0].texture.v = 1.0f;
252 vertex_data[1].position.x = x1;
253 vertex_data[1].position.y = y2;
254 vertex_data[1].position.z = 0.0f;
255 vertex_data[1].texture.u = 0.0f;
256 vertex_data[1].texture.v = 0.0f;
259 vertex_data[2].position.x = x2;
260 vertex_data[2].position.y = y2;
261 vertex_data[2].position.z = 0.0f;
262 vertex_data[2].texture.u = 1.0f;
263 vertex_data[2].texture.v = 0.0f;
266 vertex_data[3].position.x = x2;
267 vertex_data[3].position.y = y1;
268 vertex_data[3].position.z = 0.0f;
269 vertex_data[3].texture.u = 1.0f;
270 vertex_data[3].texture.v = 1.0f;
272 context_handle->Unmap (vertex_buffer.Get (), 0);
273 gst_d3d11_device_unlock (device);
275 overlay = g_new0 (GstD3D11CompositionOverlay, 1);
276 overlay->overlay_rect = gst_video_overlay_rectangle_ref (overlay_rect);
277 overlay->texture = texture.Detach ();
278 overlay->srv = srv.Detach ();
279 overlay->vertex_buffer = vertex_buffer.Detach ();
285 gst_d3d11_composition_overlay_free (GstD3D11CompositionOverlay * overlay)
290 if (overlay->overlay_rect)
291 gst_video_overlay_rectangle_unref (overlay->overlay_rect);
293 GST_D3D11_CLEAR_COM (overlay->srv);
294 GST_D3D11_CLEAR_COM (overlay->texture);
295 GST_D3D11_CLEAR_COM (overlay->vertex_buffer);
301 gst_d3d11_overlay_compositor_setup_shader (GstD3D11OverlayCompositor * self,
302 GstD3D11Device * device)
305 D3D11_SAMPLER_DESC sampler_desc;
306 D3D11_INPUT_ELEMENT_DESC input_desc[2];
307 D3D11_BUFFER_DESC buffer_desc;
308 D3D11_BLEND_DESC blend_desc;
309 D3D11_MAPPED_SUBRESOURCE map;
311 ID3D11Device *device_handle;
312 ID3D11DeviceContext *context_handle;
313 ComPtr < ID3D11PixelShader > ps;
314 ComPtr < ID3D11VertexShader > vs;
315 ComPtr < ID3D11InputLayout > layout;
316 ComPtr < ID3D11SamplerState > sampler;
317 ComPtr < ID3D11BlendState > blend;
318 ComPtr < ID3D11Buffer > index_buffer;
320 memset (&sampler_desc, 0, sizeof (sampler_desc));
321 memset (input_desc, 0, sizeof (input_desc));
322 memset (&buffer_desc, 0, sizeof (buffer_desc));
323 memset (&blend_desc, 0, sizeof (blend_desc));
325 device_handle = gst_d3d11_device_get_device_handle (device);
326 context_handle = gst_d3d11_device_get_device_context_handle (device);
328 /* bilinear filtering */
329 sampler_desc.Filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT;
330 sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
331 sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
332 sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
333 sampler_desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
334 sampler_desc.MinLOD = 0;
335 sampler_desc.MaxLOD = D3D11_FLOAT32_MAX;
337 hr = device_handle->CreateSamplerState (&sampler_desc, &sampler);
338 if (!gst_d3d11_result (hr, device)) {
339 GST_ERROR ("Couldn't create sampler state, hr: 0x%x", (guint) hr);
343 GST_LOG ("Create Pixel Shader \n%s", templ_pixel_shader);
345 if (!gst_d3d11_create_pixel_shader (device, templ_pixel_shader, &ps)) {
346 GST_ERROR ("Couldn't create pixel shader");
350 input_desc[0].SemanticName = "POSITION";
351 input_desc[0].SemanticIndex = 0;
352 input_desc[0].Format = DXGI_FORMAT_R32G32B32_FLOAT;
353 input_desc[0].InputSlot = 0;
354 input_desc[0].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
355 input_desc[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
356 input_desc[0].InstanceDataStepRate = 0;
358 input_desc[1].SemanticName = "TEXCOORD";
359 input_desc[1].SemanticIndex = 0;
360 input_desc[1].Format = DXGI_FORMAT_R32G32_FLOAT;
361 input_desc[1].InputSlot = 0;
362 input_desc[1].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
363 input_desc[1].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
364 input_desc[1].InstanceDataStepRate = 0;
366 if (!gst_d3d11_create_vertex_shader (device, templ_vertex_shader,
367 input_desc, G_N_ELEMENTS (input_desc), &vs, &layout)) {
368 GST_ERROR ("Couldn't vertex pixel shader");
372 blend_desc.AlphaToCoverageEnable = FALSE;
373 blend_desc.IndependentBlendEnable = FALSE;
374 blend_desc.RenderTarget[0].BlendEnable = TRUE;
375 blend_desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
376 blend_desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
377 blend_desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
378 blend_desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
379 blend_desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
380 blend_desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
381 blend_desc.RenderTarget[0].RenderTargetWriteMask =
382 D3D11_COLOR_WRITE_ENABLE_ALL;
384 hr = device_handle->CreateBlendState (&blend_desc, &blend);
385 if (!gst_d3d11_result (hr, device)) {
386 GST_ERROR ("Couldn't create blend staten, hr: 0x%x", (guint) hr);
390 buffer_desc.Usage = D3D11_USAGE_DYNAMIC;
391 buffer_desc.ByteWidth = sizeof (WORD) * 6;
392 buffer_desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
393 buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
395 hr = device_handle->CreateBuffer (&buffer_desc, NULL, &index_buffer);
396 if (!gst_d3d11_result (hr, device)) {
397 GST_ERROR ("Couldn't create index buffer, hr: 0x%x", (guint) hr);
401 gst_d3d11_device_lock (device);
402 hr = context_handle->Map (index_buffer.Get (),
403 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
405 if (!gst_d3d11_result (hr, device)) {
406 GST_ERROR ("Couldn't map index buffer, hr: 0x%x", (guint) hr);
407 gst_d3d11_device_unlock (device);
411 indices = (WORD *) map.pData;
413 /* clockwise indexing */
414 indices[0] = 0; /* bottom left */
415 indices[1] = 1; /* top left */
416 indices[2] = 2; /* top right */
418 indices[3] = 3; /* bottom right */
419 indices[4] = 0; /* bottom left */
420 indices[5] = 2; /* top right */
422 context_handle->Unmap (index_buffer.Get (), 0);
423 gst_d3d11_device_unlock (device);
425 self->ps = ps.Detach ();
426 self->vs = vs.Detach ();
427 self->layout = layout.Detach ();
428 self->sampler = sampler.Detach ();
429 self->blend = blend.Detach ();
430 self->index_buffer = index_buffer.Detach ();
435 GstD3D11OverlayCompositor *
436 gst_d3d11_overlay_compositor_new (GstD3D11Device * device,
437 GstVideoInfo * out_info)
439 GstD3D11OverlayCompositor *compositor = NULL;
441 g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
442 g_return_val_if_fail (out_info != NULL, NULL);
444 compositor = g_new0 (GstD3D11OverlayCompositor, 1);
446 if (!gst_d3d11_overlay_compositor_setup_shader (compositor, device)) {
447 gst_d3d11_overlay_compositor_free (compositor);
451 compositor->device = (GstD3D11Device *) gst_object_ref (device);
452 compositor->out_info = *out_info;
454 compositor->viewport.TopLeftX = 0;
455 compositor->viewport.TopLeftY = 0;
456 compositor->viewport.Width = GST_VIDEO_INFO_WIDTH (out_info);
457 compositor->viewport.Height = GST_VIDEO_INFO_HEIGHT (out_info);
458 compositor->viewport.MinDepth = 0.0f;
459 compositor->viewport.MaxDepth = 1.0f;
465 gst_d3d11_overlay_compositor_free (GstD3D11OverlayCompositor * compositor)
467 g_return_if_fail (compositor != NULL);
469 gst_d3d11_overlay_compositor_free_overlays (compositor);
471 GST_D3D11_CLEAR_COM (compositor->ps);
472 GST_D3D11_CLEAR_COM (compositor->vs);
473 GST_D3D11_CLEAR_COM (compositor->layout);
474 GST_D3D11_CLEAR_COM (compositor->sampler);
475 GST_D3D11_CLEAR_COM (compositor->blend);
476 GST_D3D11_CLEAR_COM (compositor->index_buffer);
478 gst_clear_object (&compositor->device);
483 find_in_compositor (const GstD3D11CompositionOverlay * overlay,
484 const GstVideoOverlayRectangle * rect)
486 return !(overlay->overlay_rect == rect);
490 is_in_video_overlay_composition (GstVideoOverlayComposition * voc,
491 GstD3D11CompositionOverlay * overlay)
495 for (i = 0; i < gst_video_overlay_composition_n_rectangles (voc); i++) {
496 GstVideoOverlayRectangle *rectangle =
497 gst_video_overlay_composition_get_rectangle (voc, i);
498 if (overlay->overlay_rect == rectangle)
505 gst_d3d11_overlay_compositor_upload (GstD3D11OverlayCompositor * compositor,
508 GstVideoOverlayCompositionMeta *meta;
509 gint i, num_overlays;
512 g_return_val_if_fail (compositor != NULL, FALSE);
513 g_return_val_if_fail (GST_IS_BUFFER (buf), FALSE);
515 meta = gst_buffer_get_video_overlay_composition_meta (buf);
518 gst_d3d11_overlay_compositor_free_overlays (compositor);
522 num_overlays = gst_video_overlay_composition_n_rectangles (meta->overlay);
524 gst_d3d11_overlay_compositor_free_overlays (compositor);
528 GST_LOG ("Upload %d overlay rectangles", num_overlays);
530 /* Upload new overlay */
531 for (i = 0; i < num_overlays; i++) {
532 GstVideoOverlayRectangle *rectangle =
533 gst_video_overlay_composition_get_rectangle (meta->overlay, i);
535 if (!g_list_find_custom (compositor->overlays,
536 rectangle, (GCompareFunc) find_in_compositor)) {
537 GstD3D11CompositionOverlay *overlay = NULL;
539 overlay = gst_d3d11_composition_overlay_new (compositor, rectangle);
544 compositor->overlays = g_list_append (compositor->overlays, overlay);
548 /* Remove old overlay */
549 iter = compositor->overlays;
551 GstD3D11CompositionOverlay *overlay =
552 (GstD3D11CompositionOverlay *) iter->data;
553 GList *next = iter->next;
555 if (!is_in_video_overlay_composition (meta->overlay, overlay)) {
556 compositor->overlays = g_list_delete_link (compositor->overlays, iter);
557 gst_d3d11_composition_overlay_free (overlay);
567 gst_d3d11_overlay_compositor_free_overlays (GstD3D11OverlayCompositor *
570 g_return_if_fail (compositor != NULL);
572 if (compositor->overlays) {
573 g_list_free_full (compositor->overlays,
574 (GDestroyNotify) gst_d3d11_composition_overlay_free);
576 compositor->overlays = NULL;
581 gst_d3d11_overlay_compositor_update_viewport (GstD3D11OverlayCompositor *
582 compositor, D3D11_VIEWPORT * viewport)
584 g_return_val_if_fail (compositor != NULL, FALSE);
585 g_return_val_if_fail (viewport != NULL, FALSE);
587 compositor->viewport = *viewport;
593 gst_d3d11_overlay_compositor_draw (GstD3D11OverlayCompositor * compositor,
594 ID3D11RenderTargetView * rtv[GST_VIDEO_MAX_PLANES])
598 g_return_val_if_fail (compositor != NULL, FALSE);
599 g_return_val_if_fail (rtv != NULL, FALSE);
601 gst_d3d11_device_lock (compositor->device);
602 ret = gst_d3d11_overlay_compositor_draw_unlocked (compositor, rtv);
603 gst_d3d11_device_unlock (compositor->device);
609 gst_d3d11_overlay_compositor_draw_unlocked (GstD3D11OverlayCompositor *
610 compositor, ID3D11RenderTargetView * rtv[GST_VIDEO_MAX_PLANES])
613 ID3D11DeviceContext *context;
614 ID3D11ShaderResourceView *clear_view[GST_VIDEO_MAX_PLANES] = { nullptr, };
615 UINT strides = sizeof (VertexData);
618 g_return_val_if_fail (compositor != NULL, FALSE);
619 g_return_val_if_fail (rtv != NULL, FALSE);
621 context = gst_d3d11_device_get_device_context_handle (compositor->device);
622 context->IASetPrimitiveTopology (D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
623 context->IASetInputLayout (compositor->layout);
624 context->IASetIndexBuffer (compositor->index_buffer, DXGI_FORMAT_R16_UINT, 0);
625 context->PSSetSamplers (0, 1, &compositor->sampler);
626 context->VSSetShader (compositor->vs, nullptr, 0);
627 context->PSSetShader (compositor->ps, nullptr, 0);
628 context->RSSetViewports (1, &compositor->viewport);
629 context->OMSetRenderTargets (1, rtv, nullptr);
630 context->OMSetBlendState (compositor->blend, nullptr, 0xffffffff);
632 for (iter = compositor->overlays; iter; iter = g_list_next (iter)) {
633 GstD3D11CompositionOverlay *overlay =
634 (GstD3D11CompositionOverlay *) iter->data;
636 context->PSSetShaderResources (0, 1, &overlay->srv);
637 context->IASetVertexBuffers (0,
638 1, &overlay->vertex_buffer, &strides, &offsets);
640 context->DrawIndexed (6, 0, 0);
643 context->PSSetShaderResources (0, 1, clear_view);
644 context->OMSetRenderTargets (0, nullptr, nullptr);