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 "gstd3d11utils.h"
26 #include "gstd3d11device.h"
27 #include "gstd3d11shader.h"
28 #include "gstd3d11format.h"
30 GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_overlay_compositor_debug);
31 #define GST_CAT_DEFAULT gst_d3d11_overlay_compositor_debug
47 static const gchar templ_pixel_shader[] =
48 "Texture2D shaderTexture;\n"
49 "SamplerState samplerState;\n"
53 " float4 Position: SV_POSITION;\n"
54 " float3 Texture: TEXCOORD0;\n"
57 "float4 main(PS_INPUT input): SV_TARGET\n"
59 " return shaderTexture.Sample(samplerState, input.Texture);\n"
62 static const gchar templ_vertex_shader[] =
65 " float4 Position : POSITION;\n"
66 " float4 Texture : TEXCOORD0;\n"
71 " float4 Position: SV_POSITION;\n"
72 " float4 Texture: TEXCOORD0;\n"
75 "VS_OUTPUT main(VS_INPUT input)\n"
81 struct _GstD3D11OverlayCompositor
83 GstD3D11Device *device;
84 GstVideoInfo out_info;
86 D3D11_VIEWPORT viewport;
88 ID3D11PixelShader *ps;
89 ID3D11VertexShader *vs;
90 ID3D11InputLayout *layout;
91 ID3D11SamplerState *sampler;
92 ID3D11BlendState *blend;
93 ID3D11Buffer *index_buffer;
95 /* GstD3D11CompositionOverlay */
101 GstVideoOverlayRectangle *overlay_rect;
102 ID3D11Texture2D *texture;
103 ID3D11ShaderResourceView *srv;
105 } GstD3D11CompositionOverlay;
107 static GstD3D11CompositionOverlay *
108 gst_d3d11_composition_overlay_new (GstD3D11OverlayCompositor * self,
109 GstVideoOverlayRectangle * overlay_rect)
111 GstD3D11CompositionOverlay *overlay = NULL;
114 D3D11_SUBRESOURCE_DATA subresource_data = { 0, };
115 D3D11_TEXTURE2D_DESC texture_desc = { 0, };
116 D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc = { 0, };
117 D3D11_BUFFER_DESC buffer_desc = { 0, };
118 ID3D11Buffer *vertex_buffer = NULL;
119 D3D11_MAPPED_SUBRESOURCE map;
120 VertexData *vertex_data;
126 ID3D11Texture2D *texture = NULL;
127 ID3D11ShaderResourceView *srv = NULL;
129 ID3D11Device *device_handle;
130 ID3D11DeviceContext *context_handle;
131 GstD3D11Device *device = self->device;
132 const guint index_count = 2 * 3;
133 FLOAT x1, y1, x2, y2;
135 g_return_val_if_fail (overlay_rect != NULL, NULL);
137 device_handle = gst_d3d11_device_get_device_handle (device);
138 context_handle = gst_d3d11_device_get_device_context_handle (device);
140 if (!gst_video_overlay_rectangle_get_render_rectangle (overlay_rect, &x, &y,
142 GST_ERROR ("Failed to get render rectangle");
146 buf = gst_video_overlay_rectangle_get_pixels_unscaled_argb (overlay_rect,
147 GST_VIDEO_OVERLAY_FORMAT_FLAG_NONE);
149 GST_ERROR ("Failed to get overlay buffer");
153 vmeta = gst_buffer_get_video_meta (buf);
155 GST_ERROR ("Failed to get video meta");
159 if (!gst_video_meta_map (vmeta,
160 0, &info, (gpointer *) & data, &stride, GST_MAP_READ)) {
161 GST_ERROR ("Failed to map");
165 /* Do create texture and upload data at once, for create immutable texture */
166 subresource_data.pSysMem = data;
167 subresource_data.SysMemPitch = stride;
168 subresource_data.SysMemSlicePitch = 0;
170 texture_desc.Width = width;
171 texture_desc.Height = height;
172 texture_desc.MipLevels = 1;
173 texture_desc.ArraySize = 1;
174 /* FIXME: need to consider non-BGRA ? */
175 texture_desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
176 texture_desc.SampleDesc.Count = 1;
177 texture_desc.SampleDesc.Quality = 0;
178 texture_desc.Usage = D3D11_USAGE_IMMUTABLE;
179 texture_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
180 texture_desc.CPUAccessFlags = 0;
182 texture = gst_d3d11_device_create_texture (device,
183 &texture_desc, &subresource_data);
184 gst_video_meta_unmap (vmeta, 0, &info);
187 GST_ERROR ("Failed to create texture");
191 srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
192 srv_desc.Texture2D.MipLevels = 1;
194 hr = ID3D11Device_CreateShaderResourceView (device_handle,
195 (ID3D11Resource *) texture, &srv_desc, &srv);
196 if (!gst_d3d11_result (hr, device) || !srv) {
197 GST_ERROR ("Failed to create shader resource view");
201 buffer_desc.Usage = D3D11_USAGE_DYNAMIC;
202 buffer_desc.ByteWidth = sizeof (VertexData) * 4;
203 buffer_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
204 buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
206 hr = ID3D11Device_CreateBuffer (device_handle, &buffer_desc, NULL,
208 if (!gst_d3d11_result (hr, device)) {
209 GST_ERROR ("Couldn't create vertex buffer, hr: 0x%x", (guint) hr);
213 gst_d3d11_device_lock (device);
214 hr = ID3D11DeviceContext_Map (context_handle,
215 (ID3D11Resource *) vertex_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
217 if (!gst_d3d11_result (hr, device)) {
218 GST_ERROR ("Couldn't map vertex buffer, hr: 0x%x", (guint) hr);
219 gst_d3d11_device_unlock (device);
223 vertex_data = (VertexData *) map.pData;
224 x1 = (x / (gfloat) GST_VIDEO_INFO_WIDTH (&self->out_info)) * 2.0f - 1.0f;
225 y1 = (y / (gfloat) GST_VIDEO_INFO_HEIGHT (&self->out_info)) * 2.0f - 1.0f;
228 (gfloat) GST_VIDEO_INFO_WIDTH (&self->out_info)) * 2.0f - 1.0f;
230 (gfloat) GST_VIDEO_INFO_HEIGHT (&self->out_info)) * 2.0f - 1.0f;
232 vertex_data[0].position.x = x1;
233 vertex_data[0].position.y = y1;
234 vertex_data[0].position.z = 0.0f;
235 vertex_data[0].texture.x = 0.0f;
236 vertex_data[0].texture.y = 1.0f;
238 vertex_data[1].position.x = x1;
239 vertex_data[1].position.y = y2;
240 vertex_data[1].position.z = 0.0f;
241 vertex_data[1].texture.x = 0.0f;
242 vertex_data[1].texture.y = 0.0f;
244 vertex_data[2].position.x = x2;
245 vertex_data[2].position.y = y2;
246 vertex_data[2].position.z = 0.0f;
247 vertex_data[2].texture.x = 1.0f;
248 vertex_data[2].texture.y = 0.0f;
250 vertex_data[3].position.x = x2;
251 vertex_data[3].position.y = y1;
252 vertex_data[3].position.z = 0.0f;
253 vertex_data[3].texture.x = 1.0f;
254 vertex_data[3].texture.y = 1.0f;
256 ID3D11DeviceContext_Unmap (context_handle,
257 (ID3D11Resource *) vertex_buffer, 0);
258 gst_d3d11_device_unlock (device);
260 overlay = g_new0 (GstD3D11CompositionOverlay, 1);
261 overlay->overlay_rect = gst_video_overlay_rectangle_ref (overlay_rect);
262 overlay->texture = texture;
264 overlay->quad = gst_d3d11_quad_new (device,
265 self->ps, self->vs, self->layout, self->sampler, self->blend, NULL, NULL,
266 vertex_buffer, sizeof (VertexData),
267 self->index_buffer, DXGI_FORMAT_R16_UINT, index_count);
272 ID3D11ShaderResourceView_Release (srv);
274 ID3D11Texture2D_Release (texture);
278 ID3D11Buffer_Release (vertex_buffer);
284 gst_d3d11_composition_overlay_free (GstD3D11CompositionOverlay * overlay)
289 if (overlay->overlay_rect)
290 gst_video_overlay_rectangle_unref (overlay->overlay_rect);
293 ID3D11ShaderResourceView_Release (overlay->srv);
295 if (overlay->texture)
296 ID3D11Texture2D_Release (overlay->texture);
299 gst_d3d11_quad_free (overlay->quad);
305 gst_d3d11_overlay_compositor_setup_shader (GstD3D11OverlayCompositor * self,
306 GstD3D11Device * device)
309 D3D11_SAMPLER_DESC sampler_desc = { 0, };
310 D3D11_INPUT_ELEMENT_DESC input_desc[2] = { 0, };
311 D3D11_BUFFER_DESC buffer_desc = { 0, };
312 D3D11_BLEND_DESC blend_desc = { 0, };
313 D3D11_MAPPED_SUBRESOURCE map;
315 ID3D11Device *device_handle;
316 ID3D11DeviceContext *context_handle;
317 ID3D11PixelShader *ps = NULL;
318 ID3D11VertexShader *vs = NULL;
319 ID3D11InputLayout *layout = NULL;
320 ID3D11SamplerState *sampler = NULL;
321 ID3D11BlendState *blend = NULL;
322 ID3D11Buffer *index_buffer = NULL;
323 const guint index_count = 2 * 3;
326 device_handle = gst_d3d11_device_get_device_handle (device);
327 context_handle = gst_d3d11_device_get_device_context_handle (device);
329 /* bilinear filtering */
330 sampler_desc.Filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT;
331 sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
332 sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
333 sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
334 sampler_desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
335 sampler_desc.MinLOD = 0;
336 sampler_desc.MaxLOD = D3D11_FLOAT32_MAX;
338 hr = ID3D11Device_CreateSamplerState (device_handle, &sampler_desc, &sampler);
339 if (!gst_d3d11_result (hr, device)) {
340 GST_ERROR ("Couldn't create sampler state, hr: 0x%x", (guint) hr);
345 GST_LOG ("Create Pixel Shader \n%s", templ_pixel_shader);
347 if (!gst_d3d11_create_pixel_shader (device, templ_pixel_shader, &ps)) {
348 GST_ERROR ("Couldn't create pixel shader");
353 input_desc[0].SemanticName = "POSITION";
354 input_desc[0].SemanticIndex = 0;
355 input_desc[0].Format = DXGI_FORMAT_R32G32B32_FLOAT;
356 input_desc[0].InputSlot = 0;
357 input_desc[0].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
358 input_desc[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
359 input_desc[0].InstanceDataStepRate = 0;
361 input_desc[1].SemanticName = "TEXCOORD";
362 input_desc[1].SemanticIndex = 0;
363 input_desc[1].Format = DXGI_FORMAT_R32G32_FLOAT;
364 input_desc[1].InputSlot = 0;
365 input_desc[1].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
366 input_desc[1].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
367 input_desc[1].InstanceDataStepRate = 0;
369 if (!gst_d3d11_create_vertex_shader (device, templ_vertex_shader,
370 input_desc, G_N_ELEMENTS (input_desc), &vs, &layout)) {
371 GST_ERROR ("Couldn't vertex pixel shader");
376 blend_desc.AlphaToCoverageEnable = FALSE;
377 blend_desc.IndependentBlendEnable = FALSE;
378 blend_desc.RenderTarget[0].BlendEnable = TRUE;
379 blend_desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
380 blend_desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
381 blend_desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
382 blend_desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
383 blend_desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
384 blend_desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
385 blend_desc.RenderTarget[0].RenderTargetWriteMask =
386 D3D11_COLOR_WRITE_ENABLE_ALL;
388 hr = ID3D11Device_CreateBlendState (device_handle, &blend_desc, &blend);
389 if (!gst_d3d11_result (hr, device)) {
390 GST_ERROR ("Couldn't create blend staten, hr: 0x%x", (guint) hr);
395 buffer_desc.Usage = D3D11_USAGE_DYNAMIC;
396 buffer_desc.ByteWidth = sizeof (WORD) * index_count;
397 buffer_desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
398 buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
400 hr = ID3D11Device_CreateBuffer (device_handle, &buffer_desc, NULL,
402 if (!gst_d3d11_result (hr, device)) {
403 GST_ERROR ("Couldn't create index buffer, hr: 0x%x", (guint) hr);
408 gst_d3d11_device_lock (device);
409 hr = ID3D11DeviceContext_Map (context_handle,
410 (ID3D11Resource *) index_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
412 if (!gst_d3d11_result (hr, device)) {
413 GST_ERROR ("Couldn't map index buffer, hr: 0x%x", (guint) hr);
414 gst_d3d11_device_unlock (device);
419 indices = (WORD *) map.pData;
421 /* clockwise indexing */
422 indices[0] = 0; /* bottom left */
423 indices[1] = 1; /* top left */
424 indices[2] = 2; /* top right */
426 indices[3] = 3; /* bottom right */
427 indices[4] = 0; /* bottom left */
428 indices[5] = 2; /* top right */
430 ID3D11DeviceContext_Unmap (context_handle,
431 (ID3D11Resource *) index_buffer, 0);
432 gst_d3d11_device_unlock (device);
436 self->layout = layout;
437 self->sampler = sampler;
439 self->index_buffer = index_buffer;
446 ID3D11PixelShader_Release (ps);
448 ID3D11VertexShader_Release (vs);
450 ID3D11InputLayout_Release (layout);
452 ID3D11SamplerState_Release (sampler);
454 ID3D11BlendState_Release (blend);
456 ID3D11Buffer_Release (index_buffer);
462 GstD3D11OverlayCompositor *
463 gst_d3d11_overlay_compositor_new (GstD3D11Device * device,
464 GstVideoInfo * out_info)
466 GstD3D11OverlayCompositor *compositor = NULL;
468 g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
469 g_return_val_if_fail (out_info != NULL, NULL);
471 compositor = g_new0 (GstD3D11OverlayCompositor, 1);
473 if (!gst_d3d11_overlay_compositor_setup_shader (compositor, device)) {
474 gst_d3d11_overlay_compositor_free (compositor);
478 compositor->device = gst_object_ref (device);
479 compositor->out_info = *out_info;
481 compositor->viewport.TopLeftX = 0;
482 compositor->viewport.TopLeftY = 0;
483 compositor->viewport.Width = GST_VIDEO_INFO_WIDTH (out_info);
484 compositor->viewport.Height = GST_VIDEO_INFO_HEIGHT (out_info);
485 compositor->viewport.MinDepth = 0.0f;
486 compositor->viewport.MaxDepth = 1.0f;
492 gst_d3d11_overlay_compositor_free (GstD3D11OverlayCompositor * compositor)
494 g_return_if_fail (compositor != NULL);
496 gst_d3d11_overlay_compositor_free_overlays (compositor);
499 ID3D11PixelShader_Release (compositor->ps);
501 ID3D11VertexShader_Release (compositor->vs);
502 if (compositor->layout)
503 ID3D11InputLayout_Release (compositor->layout);
504 if (compositor->sampler)
505 ID3D11SamplerState_Release (compositor->sampler);
506 if (compositor->blend)
507 ID3D11BlendState_Release (compositor->blend);
508 if (compositor->index_buffer)
509 ID3D11Buffer_Release (compositor->index_buffer);
511 gst_clear_object (&compositor->device);
516 find_in_compositor (const GstD3D11CompositionOverlay * overlay,
517 const GstVideoOverlayRectangle * rect)
519 return !(overlay->overlay_rect == rect);
523 is_in_video_overlay_composition (GstVideoOverlayComposition * voc,
524 GstD3D11CompositionOverlay * overlay)
528 for (i = 0; i < gst_video_overlay_composition_n_rectangles (voc); i++) {
529 GstVideoOverlayRectangle *rectangle =
530 gst_video_overlay_composition_get_rectangle (voc, i);
531 if (overlay->overlay_rect == rectangle)
538 gst_d3d11_overlay_compositor_upload (GstD3D11OverlayCompositor * compositor,
541 GstVideoOverlayCompositionMeta *meta;
542 gint i, num_overlays;
545 g_return_val_if_fail (compositor != NULL, FALSE);
546 g_return_val_if_fail (GST_IS_BUFFER (buf), FALSE);
548 meta = gst_buffer_get_video_overlay_composition_meta (buf);
551 gst_d3d11_overlay_compositor_free_overlays (compositor);
555 num_overlays = gst_video_overlay_composition_n_rectangles (meta->overlay);
557 gst_d3d11_overlay_compositor_free_overlays (compositor);
561 GST_LOG ("Upload %d overlay rectangles", num_overlays);
563 /* Upload new overlay */
564 for (i = 0; i < num_overlays; i++) {
565 GstVideoOverlayRectangle *rectangle =
566 gst_video_overlay_composition_get_rectangle (meta->overlay, i);
568 if (!g_list_find_custom (compositor->overlays,
569 rectangle, (GCompareFunc) find_in_compositor)) {
570 GstD3D11CompositionOverlay *overlay = NULL;
572 overlay = gst_d3d11_composition_overlay_new (compositor, rectangle);
577 compositor->overlays = g_list_append (compositor->overlays, overlay);
581 /* Remove old overlay */
582 iter = compositor->overlays;
584 GstD3D11CompositionOverlay *overlay =
585 (GstD3D11CompositionOverlay *) iter->data;
586 GList *next = iter->next;
588 if (!is_in_video_overlay_composition (meta->overlay, overlay)) {
589 compositor->overlays = g_list_delete_link (compositor->overlays, iter);
590 gst_d3d11_composition_overlay_free (overlay);
600 gst_d3d11_overlay_compositor_free_overlays (GstD3D11OverlayCompositor *
603 g_return_if_fail (compositor != NULL);
605 if (compositor->overlays) {
606 g_list_free_full (compositor->overlays,
607 (GDestroyNotify) gst_d3d11_composition_overlay_free);
609 compositor->overlays = NULL;
614 gst_d3d11_overlay_compositor_update_rect (GstD3D11OverlayCompositor *
615 compositor, RECT * rect)
617 g_return_val_if_fail (compositor != NULL, FALSE);
618 g_return_val_if_fail (rect != NULL, FALSE);
620 compositor->viewport.TopLeftX = rect->left;
621 compositor->viewport.TopLeftY = rect->top;
622 compositor->viewport.Width = rect->right - rect->left;
623 compositor->viewport.Height = rect->bottom - rect->top;
629 gst_d3d11_overlay_compositor_draw (GstD3D11OverlayCompositor * compositor,
630 ID3D11RenderTargetView * rtv[GST_VIDEO_MAX_PLANES])
634 g_return_val_if_fail (compositor != NULL, FALSE);
635 g_return_val_if_fail (rtv != NULL, FALSE);
637 gst_d3d11_device_lock (compositor->device);
638 ret = gst_d3d11_overlay_compositor_draw_unlocked (compositor, rtv);
639 gst_d3d11_device_unlock (compositor->device);
645 gst_d3d11_overlay_compositor_draw_unlocked (GstD3D11OverlayCompositor *
646 compositor, ID3D11RenderTargetView * rtv[GST_VIDEO_MAX_PLANES])
651 g_return_val_if_fail (compositor != NULL, FALSE);
652 g_return_val_if_fail (rtv != NULL, FALSE);
654 for (iter = compositor->overlays; iter; iter = g_list_next (iter)) {
655 GstD3D11CompositionOverlay *overlay =
656 (GstD3D11CompositionOverlay *) iter->data;
658 ret = gst_d3d11_draw_quad_unlocked (overlay->quad,
659 &compositor->viewport, 1, &overlay->srv, 1, rtv, 1, NULL);