d3d11videosink: Add present signal

The "present" signal will be emitted just before the
IDXGISwapChain::Present() call. The client can perform additional
GPU operation with given GstD3D11Device object and
ID3D11RenderTargetView handle. Or, the client can read back
the scene to be displayed on window using the signal.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2923>
This commit is contained in:
Seungha Yang 2022-08-19 20:25:31 +09:00 committed by GStreamer Marge Bot
parent 625e74100d
commit 470436d7e6
6 changed files with 86 additions and 9 deletions

View file

@ -78,6 +78,7 @@ enum
{ {
/* signals */ /* signals */
SIGNAL_BEGIN_DRAW, SIGNAL_BEGIN_DRAW,
SIGNAL_PRESENT,
/* actions */ /* actions */
SIGNAL_DRAW, SIGNAL_DRAW,
@ -351,6 +352,31 @@ gst_d3d11_video_sink_class_init (GstD3D11VideoSinkClass * klass)
G_TYPE_BOOLEAN, 4, G_TYPE_POINTER, G_TYPE_UINT, G_TYPE_UINT64, G_TYPE_BOOLEAN, 4, G_TYPE_POINTER, G_TYPE_UINT, G_TYPE_UINT64,
G_TYPE_UINT64); G_TYPE_UINT64);
/**
* GstD3D11VideoSink::present
* @videosink: the #GstD3D11VideoSink
* @device: a #GstD3D11Device handle
* @render_target: a ID3D11RenderTargetView handle of swapchain's backbuffer
*
* Emitted just before presenting a texture via the IDXGISwapChain::Present.
* The client can perform additional rendering on the given @render_target,
* or can read the content already rendered on the swapchain's backbuffer.
*
* This signal will be emitted with gst_d3d11_device_lock() taken and
* client should perform GPU operation from the thread where this signal
* emitted.
*
* If a client wants to listen this signal, the client must connect this
* signal before the first present. Otherwise this signal will not be
* emitted.
*
* Since: 1.22
*/
gst_d3d11_video_sink_signals[SIGNAL_PRESENT] =
g_signal_new ("present", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, 0, nullptr, nullptr, nullptr,
G_TYPE_NONE, 2, GST_TYPE_D3D11_DEVICE, G_TYPE_POINTER);
element_class->set_context = element_class->set_context =
GST_DEBUG_FUNCPTR (gst_d3d11_video_sink_set_context); GST_DEBUG_FUNCPTR (gst_d3d11_video_sink_set_context);
@ -763,6 +789,14 @@ gst_d3d11_video_mouse_key_event (GstD3D11Window * window, const gchar * event,
gst_navigation_send_event_simple (GST_NAVIGATION (self), mouse_event); gst_navigation_send_event_simple (GST_NAVIGATION (self), mouse_event);
} }
static void
gst_d3d11_video_sink_present (GstD3D11Window * window, GstD3D11Device * device,
ID3D11RenderTargetView * rtv, GstD3D11VideoSink * self)
{
g_signal_emit (self, gst_d3d11_video_sink_signals[SIGNAL_PRESENT], 0,
device, rtv);
}
static gboolean static gboolean
gst_d3d11_video_sink_start (GstBaseSink * sink) gst_d3d11_video_sink_start (GstBaseSink * sink)
{ {
@ -784,6 +818,7 @@ static gboolean
gst_d3d11_video_sink_prepare_window (GstD3D11VideoSink * self) gst_d3d11_video_sink_prepare_window (GstD3D11VideoSink * self)
{ {
GstD3D11WindowNativeType window_type = GST_D3D11_WINDOW_NATIVE_TYPE_HWND; GstD3D11WindowNativeType window_type = GST_D3D11_WINDOW_NATIVE_TYPE_HWND;
gboolean emit_present = FALSE;
if (self->window) if (self->window)
return TRUE; return TRUE;
@ -847,11 +882,18 @@ done:
return FALSE; return FALSE;
} }
/* Emits present signal only if signal is connected for performance reason */
if (g_signal_has_handler_pending (self,
gst_d3d11_video_sink_signals[SIGNAL_PRESENT], 0, FALSE)) {
emit_present = TRUE;
}
g_object_set (self->window, g_object_set (self->window,
"force-aspect-ratio", self->force_aspect_ratio, "force-aspect-ratio", self->force_aspect_ratio,
"fullscreen-toggle-mode", self->fullscreen_toggle_mode, "fullscreen-toggle-mode", self->fullscreen_toggle_mode,
"fullscreen", self->fullscreen, "fullscreen", self->fullscreen,
"enable-navigation-events", self->enable_navigation_events, NULL); "enable-navigation-events", self->enable_navigation_events,
"emit-present", emit_present, nullptr);
gst_d3d11_window_set_orientation (self->window, self->selected_method); gst_d3d11_window_set_orientation (self->window, self->selected_method);
@ -859,6 +901,8 @@ done:
G_CALLBACK (gst_d3d11_video_sink_key_event), self); G_CALLBACK (gst_d3d11_video_sink_key_event), self);
g_signal_connect (self->window, "mouse-event", g_signal_connect (self->window, "mouse-event",
G_CALLBACK (gst_d3d11_video_mouse_key_event), self); G_CALLBACK (gst_d3d11_video_mouse_key_event), self);
g_signal_connect (self->window, "present",
G_CALLBACK (gst_d3d11_video_sink_present), self);
return TRUE; return TRUE;
} }

View file

@ -54,18 +54,20 @@ enum
PROP_FULLSCREEN, PROP_FULLSCREEN,
PROP_WINDOW_HANDLE, PROP_WINDOW_HANDLE,
PROP_RENDER_STATS, PROP_RENDER_STATS,
PROP_EMIT_PRESENT,
}; };
#define DEFAULT_ENABLE_NAVIGATION_EVENTS TRUE #define DEFAULT_ENABLE_NAVIGATION_EVENTS TRUE
#define DEFAULT_FORCE_ASPECT_RATIO TRUE #define DEFAULT_FORCE_ASPECT_RATIO TRUE
#define DEFAULT_FULLSCREEN_TOGGLE_MODE GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE #define DEFAULT_FULLSCREEN_TOGGLE_MODE GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE
#define DEFAULT_FULLSCREEN FALSE #define DEFAULT_FULLSCREEN FALSE
#define DEFAULT_RENDER_STATS FALSE #define DEFAULT_EMIT_PRESENT FALSE
enum enum
{ {
SIGNAL_KEY_EVENT, SIGNAL_KEY_EVENT,
SIGNAL_MOUSE_EVENT, SIGNAL_MOUSE_EVENT,
SIGNAL_PRESENT,
SIGNAL_LAST SIGNAL_LAST
}; };
@ -163,6 +165,11 @@ gst_d3d11_window_class_init (GstD3D11WindowClass * klass)
(GParamFlags) (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | (GParamFlags) (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS))); G_PARAM_STATIC_STRINGS)));
g_object_class_install_property (gobject_class, PROP_EMIT_PRESENT,
g_param_spec_boolean ("emit-present", "Emit Present",
"Emit present signal", DEFAULT_EMIT_PRESENT,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
d3d11_window_signals[SIGNAL_KEY_EVENT] = d3d11_window_signals[SIGNAL_KEY_EVENT] =
g_signal_new ("key-event", G_TYPE_FROM_CLASS (klass), g_signal_new ("key-event", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
@ -172,6 +179,11 @@ gst_d3d11_window_class_init (GstD3D11WindowClass * klass)
g_signal_new ("mouse-event", G_TYPE_FROM_CLASS (klass), g_signal_new ("mouse-event", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_INT, G_TYPE_DOUBLE, G_TYPE_DOUBLE); G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_INT, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
d3d11_window_signals[SIGNAL_PRESENT] =
g_signal_new ("present", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, 0, nullptr, nullptr, nullptr,
G_TYPE_NONE, 2, GST_TYPE_D3D11_DEVICE, G_TYPE_POINTER);
} }
static void static void
@ -181,7 +193,7 @@ gst_d3d11_window_init (GstD3D11Window * self)
self->enable_navigation_events = DEFAULT_ENABLE_NAVIGATION_EVENTS; self->enable_navigation_events = DEFAULT_ENABLE_NAVIGATION_EVENTS;
self->fullscreen_toggle_mode = GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE; self->fullscreen_toggle_mode = GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE;
self->fullscreen = DEFAULT_FULLSCREEN; self->fullscreen = DEFAULT_FULLSCREEN;
self->render_stats = DEFAULT_RENDER_STATS; self->emit_present = DEFAULT_EMIT_PRESENT;
} }
static void static void
@ -219,6 +231,9 @@ gst_d3d11_window_set_property (GObject * object, guint prop_id,
case PROP_WINDOW_HANDLE: case PROP_WINDOW_HANDLE:
self->external_handle = (guintptr) g_value_get_pointer (value); self->external_handle = (guintptr) g_value_get_pointer (value);
break; break;
case PROP_EMIT_PRESENT:
self->emit_present = g_value_get_boolean (value);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -244,6 +259,9 @@ gst_d3d11_window_get_property (GObject * object, guint prop_id,
case PROP_FULLSCREEN: case PROP_FULLSCREEN:
g_value_set_boolean (value, self->fullscreen); g_value_set_boolean (value, self->fullscreen);
break; break;
case PROP_EMIT_PRESENT:
g_value_set_boolean (value, self->emit_present);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -844,6 +862,17 @@ gst_d3d111_window_present (GstD3D11Window * self, GstBuffer * buffer,
return GST_FLOW_ERROR; return GST_FLOW_ERROR;
} }
/* We use flip mode swapchain and will not redraw borders.
* So backbuffer should be cleared manually in order to remove artifact of
* previous client's rendering on present signal */
if (self->emit_present) {
const FLOAT clear_color[] = { 0.0f, 0.0f, 0.0f, 1.0f };
ID3D11DeviceContext *context =
gst_d3d11_device_get_device_context_handle (self->device);
context->ClearRenderTargetView (rtv, clear_color);
}
crop_meta = gst_buffer_get_video_crop_meta (buffer); crop_meta = gst_buffer_get_video_crop_meta (buffer);
if (crop_meta) { if (crop_meta) {
input_rect.left = crop_meta->x; input_rect.left = crop_meta->x;
@ -895,8 +924,13 @@ gst_d3d111_window_present (GstD3D11Window * self, GstBuffer * buffer,
if (self->allow_tearing && self->fullscreen) if (self->allow_tearing && self->fullscreen)
present_flags |= DXGI_PRESENT_ALLOW_TEARING; present_flags |= DXGI_PRESENT_ALLOW_TEARING;
if (klass->present) if (klass->present) {
if (self->emit_present) {
g_signal_emit (self, d3d11_window_signals[SIGNAL_PRESENT], 0,
self->device, rtv, nullptr);
}
ret = klass->present (self, present_flags); ret = klass->present (self, present_flags);
}
self->first_present = FALSE; self->first_present = FALSE;

View file

@ -86,7 +86,7 @@ struct _GstD3D11Window
GstD3D11WindowFullscreenToggleMode fullscreen_toggle_mode; GstD3D11WindowFullscreenToggleMode fullscreen_toggle_mode;
gboolean requested_fullscreen; gboolean requested_fullscreen;
gboolean fullscreen; gboolean fullscreen;
gboolean render_stats; gboolean emit_present;
GstVideoInfo info; GstVideoInfo info;
GstVideoInfo render_info; GstVideoInfo render_info;

View file

@ -452,7 +452,7 @@ gst_d3d11_window_core_window_present (GstD3D11Window * window,
IDXGISwapChain1 *swap_chain = (IDXGISwapChain1 *) window->swap_chain; IDXGISwapChain1 *swap_chain = (IDXGISwapChain1 *) window->swap_chain;
/* the first present should not specify dirty-rect */ /* the first present should not specify dirty-rect */
if (!window->first_present) { if (!window->first_present && !window->emit_present) {
present_params.DirtyRectsCount = 1; present_params.DirtyRectsCount = 1;
present_params.pDirtyRects = &window->render_rect; present_params.pDirtyRects = &window->render_rect;
} }

View file

@ -442,7 +442,7 @@ gst_d3d11_window_swap_chain_panel_present (GstD3D11Window * window,
IDXGISwapChain1 *swap_chain = (IDXGISwapChain1 *) window->swap_chain; IDXGISwapChain1 *swap_chain = (IDXGISwapChain1 *) window->swap_chain;
/* the first present should not specify dirty-rect */ /* the first present should not specify dirty-rect */
if (!window->first_present) { if (!window->first_present && !window->emit_present) {
present_params.DirtyRectsCount = 1; present_params.DirtyRectsCount = 1;
present_params.pDirtyRects = &window->render_rect; present_params.pDirtyRects = &window->render_rect;
} }

View file

@ -80,7 +80,6 @@ struct _GstD3D11WindowWin32
GstD3D11WindowWin32OverlayState overlay_state; GstD3D11WindowWin32OverlayState overlay_state;
HDC device_handle; HDC device_handle;
gboolean first_present;
gboolean have_swapchain1; gboolean have_swapchain1;
/* atomic */ /* atomic */
@ -1067,7 +1066,7 @@ gst_d3d11_window_win32_present (GstD3D11Window * window, guint present_flags)
DXGI_PRESENT_PARAMETERS present_params = { 0, }; DXGI_PRESENT_PARAMETERS present_params = { 0, };
/* the first present should not specify dirty-rect */ /* the first present should not specify dirty-rect */
if (!window->first_present) { if (!window->first_present && !window->emit_present) {
present_params.DirtyRectsCount = 1; present_params.DirtyRectsCount = 1;
present_params.pDirtyRects = &window->render_rect; present_params.pDirtyRects = &window->render_rect;
} }