gstreamer/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11window_dummy.cpp
2022-07-09 03:36:17 +09:00

337 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;
};
#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, GError ** error);
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->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, GError ** error)
{
GstD3D11ConverterMethod method = GST_D3D11_CONVERTER_METHOD_SHADER;
if (!window->allocator) {
window->allocator =
(GstD3D11Allocator *) gst_allocator_find (GST_D3D11_MEMORY_NAME);
if (!window->allocator) {
GST_ERROR_OBJECT (window, "Allocator is unavailable");
return FALSE;
}
}
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;
GstD3D11DeviceLockGuard lk (window->device);
window->converter = gst_d3d11_converter_new (window->device, &window->info,
&window->render_info, &method);
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_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 (window->allocator,
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 {
/* *INDENT-OFF* */
ComPtr<ID3D11Query> query;
/* *INDENT-ON* */
D3D11_QUERY_DESC query_desc;
ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device);
ID3D11DeviceContext *context_handle =
gst_d3d11_device_get_device_context_handle (device);
BOOL sync_done = FALSE;
/* If keyed mutex is not used, let's handle sync manually by using
* ID3D11Query. Issued GPU commands might not be finished yet */
query_desc.Query = D3D11_QUERY_EVENT;
query_desc.MiscFlags = 0;
hr = device_handle->CreateQuery (&query_desc, &query);
if (!gst_d3d11_result (hr, device)) {
GST_ERROR_OBJECT (self, "Couldn't Create event query");
return FALSE;
}
context_handle->End (query.Get ());
/* Wait until all issued GPU commands are finished */
do {
hr = context_handle->GetData (query.Get (), &sync_done, sizeof (BOOL), 0);
} while (!sync_done && (hr == S_OK || hr == S_FALSE));
if (!gst_d3d11_result (hr, device)) {
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;
}