mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-24 17:20:36 +00:00
0a65aaa6d2
Make swapchian's display format configurable, since some DXGI formats we can use for swapchain are not API interop compatible. For instance, BGRA format should be used for Direct2D interop. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2923>
336 lines
10 KiB
C++
336 lines
10 KiB
C++
/*
|
|
* GStreamer
|
|
* Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "gstd3d11window_dummy.h"
|
|
#include "gstd3d11pluginutils.h"
|
|
#include <wrl.h>
|
|
|
|
/* *INDENT-OFF* */
|
|
using namespace Microsoft::WRL;
|
|
/* *INDENT-ON* */
|
|
|
|
GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_window_debug);
|
|
#define GST_CAT_DEFAULT gst_d3d11_window_debug
|
|
|
|
struct _GstD3D11WindowDummy
|
|
{
|
|
GstD3D11Window parent;
|
|
|
|
ID3D11Texture2D *fallback_texture;
|
|
ID3D11VideoProcessorOutputView *fallback_pov;
|
|
ID3D11RenderTargetView *fallback_rtv;
|
|
|
|
GstD3D11Fence *fence;
|
|
};
|
|
|
|
#define gst_d3d11_window_dummy_parent_class parent_class
|
|
G_DEFINE_TYPE (GstD3D11WindowDummy, gst_d3d11_window_dummy,
|
|
GST_TYPE_D3D11_WINDOW);
|
|
|
|
static void gst_d3d11_window_dummy_on_resize (GstD3D11Window * window,
|
|
guint width, guint height);
|
|
static gboolean gst_d3d11_window_dummy_prepare (GstD3D11Window * window,
|
|
guint display_width, guint display_height, GstCaps * caps,
|
|
GstStructure * config, DXGI_FORMAT display_format, GError ** error);
|
|
static void gst_d3d11_window_dummy_unprepare (GstD3D11Window * window);
|
|
static gboolean
|
|
gst_d3d11_window_dummy_open_shared_handle (GstD3D11Window * window,
|
|
GstD3D11WindowSharedHandleData * data);
|
|
static gboolean
|
|
gst_d3d11_window_dummy_release_shared_handle (GstD3D11Window * window,
|
|
GstD3D11WindowSharedHandleData * data);
|
|
|
|
static void
|
|
gst_d3d11_window_dummy_class_init (GstD3D11WindowDummyClass * klass)
|
|
{
|
|
GstD3D11WindowClass *window_class = GST_D3D11_WINDOW_CLASS (klass);
|
|
|
|
window_class->on_resize =
|
|
GST_DEBUG_FUNCPTR (gst_d3d11_window_dummy_on_resize);
|
|
window_class->prepare = GST_DEBUG_FUNCPTR (gst_d3d11_window_dummy_prepare);
|
|
window_class->unprepare =
|
|
GST_DEBUG_FUNCPTR (gst_d3d11_window_dummy_unprepare);
|
|
window_class->open_shared_handle =
|
|
GST_DEBUG_FUNCPTR (gst_d3d11_window_dummy_open_shared_handle);
|
|
window_class->release_shared_handle =
|
|
GST_DEBUG_FUNCPTR (gst_d3d11_window_dummy_release_shared_handle);
|
|
}
|
|
|
|
static void
|
|
gst_d3d11_window_dummy_init (GstD3D11WindowDummy * self)
|
|
{
|
|
}
|
|
|
|
static gboolean
|
|
gst_d3d11_window_dummy_prepare (GstD3D11Window * window,
|
|
guint display_width, guint display_height, GstCaps * caps,
|
|
GstStructure * config, DXGI_FORMAT display_format, GError ** error)
|
|
{
|
|
gst_clear_object (&window->compositor);
|
|
gst_clear_object (&window->converter);
|
|
|
|
/* We are supporting only RGBA, BGRA or RGB10A2_LE formats but we don't know
|
|
* which format texture will be used at this moment */
|
|
|
|
gst_video_info_from_caps (&window->info, caps);
|
|
window->render_rect.left = 0;
|
|
window->render_rect.top = 0;
|
|
window->render_rect.right = display_width;
|
|
window->render_rect.bottom = display_height;
|
|
|
|
window->input_rect.left = 0;
|
|
window->input_rect.top = 0;
|
|
window->input_rect.right = GST_VIDEO_INFO_WIDTH (&window->info);
|
|
window->input_rect.bottom = GST_VIDEO_INFO_HEIGHT (&window->info);
|
|
|
|
gst_video_info_set_format (&window->render_info,
|
|
GST_VIDEO_FORMAT_BGRA, display_width, display_height);
|
|
|
|
/* TODO: not sure which colorspace should be used, let's use BT709 since
|
|
* it's default and most common one */
|
|
window->render_info.colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT709;
|
|
window->render_info.colorimetry.transfer = GST_VIDEO_TRANSFER_BT709;
|
|
window->render_info.colorimetry.range = GST_VIDEO_COLOR_RANGE_0_255;
|
|
|
|
if (config) {
|
|
gst_structure_set (config, GST_D3D11_CONVERTER_OPT_BACKEND,
|
|
GST_TYPE_D3D11_CONVERTER_BACKEND, GST_D3D11_CONVERTER_BACKEND_SHADER,
|
|
nullptr);
|
|
} else {
|
|
config = gst_structure_new ("converter-config",
|
|
GST_D3D11_CONVERTER_OPT_BACKEND, GST_TYPE_D3D11_CONVERTER_BACKEND,
|
|
GST_D3D11_CONVERTER_BACKEND_SHADER, nullptr);
|
|
}
|
|
|
|
GstD3D11DeviceLockGuard lk (window->device);
|
|
window->converter = gst_d3d11_converter_new (window->device, &window->info,
|
|
&window->render_info, config);
|
|
|
|
if (!window->converter) {
|
|
GST_ERROR_OBJECT (window, "Cannot create converter");
|
|
g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
|
|
"Cannot create converter");
|
|
return FALSE;
|
|
}
|
|
|
|
window->compositor =
|
|
gst_d3d11_overlay_compositor_new (window->device, &window->render_info);
|
|
if (!window->compositor) {
|
|
GST_ERROR_OBJECT (window, "Cannot create overlay compositor");
|
|
g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
|
|
"Cannot create overlay compositor");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gst_d3d11_window_dummy_unprepare (GstD3D11Window * window)
|
|
{
|
|
GstD3D11WindowDummy *self = GST_D3D11_WINDOW_DUMMY (window);
|
|
|
|
gst_clear_d3d11_fence (&self->fence);
|
|
}
|
|
|
|
static void
|
|
gst_d3d11_window_dummy_on_resize (GstD3D11Window * window,
|
|
guint width, guint height)
|
|
{
|
|
GstVideoRectangle src_rect, dst_rect, rst_rect;
|
|
|
|
dst_rect.x = 0;
|
|
dst_rect.y = 0;
|
|
dst_rect.w = width;
|
|
dst_rect.h = height;
|
|
|
|
if (window->force_aspect_ratio) {
|
|
src_rect.x = 0;
|
|
src_rect.y = 0;
|
|
src_rect.w = GST_VIDEO_INFO_WIDTH (&window->render_info);
|
|
src_rect.h = GST_VIDEO_INFO_HEIGHT (&window->render_info);
|
|
|
|
switch (window->method) {
|
|
case GST_VIDEO_ORIENTATION_90R:
|
|
case GST_VIDEO_ORIENTATION_90L:
|
|
case GST_VIDEO_ORIENTATION_UL_LR:
|
|
case GST_VIDEO_ORIENTATION_UR_LL:
|
|
src_rect.w = GST_VIDEO_INFO_HEIGHT (&window->render_info);
|
|
src_rect.h = GST_VIDEO_INFO_WIDTH (&window->render_info);
|
|
break;
|
|
default:
|
|
src_rect.w = GST_VIDEO_INFO_WIDTH (&window->render_info);
|
|
src_rect.h = GST_VIDEO_INFO_HEIGHT (&window->render_info);
|
|
break;
|
|
}
|
|
|
|
gst_video_sink_center_rect (src_rect, dst_rect, &rst_rect, TRUE);
|
|
} else {
|
|
rst_rect = dst_rect;
|
|
}
|
|
|
|
window->render_rect.left = rst_rect.x;
|
|
window->render_rect.top = rst_rect.y;
|
|
window->render_rect.right = rst_rect.x + rst_rect.w;
|
|
window->render_rect.bottom = rst_rect.y + rst_rect.h;
|
|
|
|
window->first_present = TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_d3d11_window_dummy_open_shared_handle (GstD3D11Window * window,
|
|
GstD3D11WindowSharedHandleData * data)
|
|
{
|
|
GstD3D11Device *device = window->device;
|
|
ID3D11Device *device_handle;
|
|
HRESULT hr;
|
|
ComPtr < ID3D11Texture2D > texture;
|
|
ComPtr < IDXGIKeyedMutex > keyed_mutex;
|
|
ID3D11RenderTargetView *rtv;
|
|
GstMemory *mem;
|
|
GstD3D11Memory *dmem;
|
|
D3D11_TEXTURE2D_DESC desc;
|
|
gboolean use_keyed_mutex = FALSE;
|
|
|
|
device_handle = gst_d3d11_device_get_device_handle (device);
|
|
|
|
if ((data->texture_misc_flags & D3D11_RESOURCE_MISC_SHARED_NTHANDLE) ==
|
|
D3D11_RESOURCE_MISC_SHARED_NTHANDLE) {
|
|
ComPtr < ID3D11Device1 > device1_handle;
|
|
|
|
hr = device_handle->QueryInterface (IID_PPV_ARGS (&device1_handle));
|
|
if (!gst_d3d11_result (hr, device))
|
|
return FALSE;
|
|
|
|
hr = device1_handle->OpenSharedResource1 (data->shared_handle,
|
|
IID_PPV_ARGS (&texture));
|
|
} else {
|
|
hr = device_handle->OpenSharedResource (data->shared_handle,
|
|
IID_PPV_ARGS (&texture));
|
|
}
|
|
|
|
if (!gst_d3d11_result (hr, device))
|
|
return FALSE;
|
|
|
|
texture->GetDesc (&desc);
|
|
use_keyed_mutex = (desc.MiscFlags & D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX) ==
|
|
D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
|
|
|
|
if (use_keyed_mutex) {
|
|
hr = texture->QueryInterface (IID_PPV_ARGS (&keyed_mutex));
|
|
if (!gst_d3d11_result (hr, device)) {
|
|
GST_ERROR_OBJECT (window, "Keyed mutex is unavailable");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
mem = gst_d3d11_allocator_alloc_wrapped (nullptr,
|
|
device, texture.Get (), desc.Width * desc.Height * 4, nullptr, nullptr);
|
|
if (!mem) {
|
|
GST_ERROR_OBJECT (window, "Couldn't allocate memory");
|
|
return FALSE;
|
|
}
|
|
|
|
dmem = GST_D3D11_MEMORY_CAST (mem);
|
|
rtv = gst_d3d11_memory_get_render_target_view (dmem, 0);
|
|
if (!rtv) {
|
|
GST_ERROR_OBJECT (window, "Render target view is unavailable");
|
|
gst_memory_unref (mem);
|
|
return FALSE;
|
|
}
|
|
|
|
if (keyed_mutex) {
|
|
hr = keyed_mutex->AcquireSync (data->acquire_key, INFINITE);
|
|
if (!gst_d3d11_result (hr, device)) {
|
|
GST_ERROR_OBJECT (window, "Couldn't acquire sync");
|
|
gst_memory_unref (mem);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Everything is prepared now */
|
|
gst_d3d11_window_dummy_on_resize (window, desc.Width, desc.Height);
|
|
|
|
/* Move owned resources */
|
|
data->render_target = gst_buffer_new ();
|
|
gst_buffer_append_memory (data->render_target, mem);
|
|
if (keyed_mutex)
|
|
data->keyed_mutex = keyed_mutex.Detach ();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_d3d11_window_dummy_release_shared_handle (GstD3D11Window * window,
|
|
GstD3D11WindowSharedHandleData * data)
|
|
{
|
|
GstD3D11WindowDummy *self = GST_D3D11_WINDOW_DUMMY (window);
|
|
GstD3D11Device *device = window->device;
|
|
HRESULT hr;
|
|
|
|
/* TODO: cache owned resource for the later reuse? */
|
|
if (data->keyed_mutex) {
|
|
hr = data->keyed_mutex->ReleaseSync (data->release_key);
|
|
gst_d3d11_result (hr, device);
|
|
|
|
GST_D3D11_CLEAR_COM (data->keyed_mutex);
|
|
} else {
|
|
/* If keyed mutex is not used, let's handle sync manually by using
|
|
* fence. Issued GPU commands might not be finished yet */
|
|
|
|
if (!self->fence)
|
|
self->fence = gst_d3d11_device_create_fence (device);
|
|
|
|
if (!self->fence) {
|
|
GST_ERROR_OBJECT (self, "Couldn't Create event query");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!gst_d3d11_fence_signal (self->fence) ||
|
|
!gst_d3d11_fence_wait (self->fence)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't sync GPU operation");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
gst_clear_buffer (&data->render_target);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
GstD3D11Window *
|
|
gst_d3d11_window_dummy_new (GstD3D11Device * device)
|
|
{
|
|
GstD3D11Window *window;
|
|
|
|
g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
|
|
|
|
window = (GstD3D11Window *)
|
|
g_object_new (GST_TYPE_D3D11_WINDOW_DUMMY, "d3d11device", device, NULL);
|
|
|
|
window->initialized = TRUE;
|
|
g_object_ref_sink (window);
|
|
|
|
return window;
|
|
}
|