From 470436d7e6f7c2427e68e5e2baa07a4bae70d0d7 Mon Sep 17 00:00:00 2001 From: Seungha Yang Date: Fri, 19 Aug 2022 20:25:31 +0900 Subject: [PATCH] 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: --- .../sys/d3d11/gstd3d11videosink.cpp | 46 ++++++++++++++++++- .../sys/d3d11/gstd3d11window.cpp | 40 ++++++++++++++-- .../sys/d3d11/gstd3d11window.h | 2 +- .../sys/d3d11/gstd3d11window_corewindow.cpp | 2 +- .../d3d11/gstd3d11window_swapchainpanel.cpp | 2 +- .../sys/d3d11/gstd3d11window_win32.cpp | 3 +- 6 files changed, 86 insertions(+), 9 deletions(-) diff --git a/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11videosink.cpp b/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11videosink.cpp index ff0695f073..7140b30ac0 100644 --- a/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11videosink.cpp +++ b/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11videosink.cpp @@ -78,6 +78,7 @@ enum { /* signals */ SIGNAL_BEGIN_DRAW, + SIGNAL_PRESENT, /* actions */ 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_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 = 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); } +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 gst_d3d11_video_sink_start (GstBaseSink * sink) { @@ -784,6 +818,7 @@ static gboolean gst_d3d11_video_sink_prepare_window (GstD3D11VideoSink * self) { GstD3D11WindowNativeType window_type = GST_D3D11_WINDOW_NATIVE_TYPE_HWND; + gboolean emit_present = FALSE; if (self->window) return TRUE; @@ -847,11 +882,18 @@ done: 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, "force-aspect-ratio", self->force_aspect_ratio, "fullscreen-toggle-mode", self->fullscreen_toggle_mode, "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); @@ -859,6 +901,8 @@ done: G_CALLBACK (gst_d3d11_video_sink_key_event), self); g_signal_connect (self->window, "mouse-event", 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; } diff --git a/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11window.cpp b/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11window.cpp index 58063d3704..95276981e4 100644 --- a/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11window.cpp +++ b/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11window.cpp @@ -54,18 +54,20 @@ enum PROP_FULLSCREEN, PROP_WINDOW_HANDLE, PROP_RENDER_STATS, + PROP_EMIT_PRESENT, }; #define DEFAULT_ENABLE_NAVIGATION_EVENTS TRUE #define DEFAULT_FORCE_ASPECT_RATIO TRUE #define DEFAULT_FULLSCREEN_TOGGLE_MODE GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE #define DEFAULT_FULLSCREEN FALSE -#define DEFAULT_RENDER_STATS FALSE +#define DEFAULT_EMIT_PRESENT FALSE enum { SIGNAL_KEY_EVENT, SIGNAL_MOUSE_EVENT, + SIGNAL_PRESENT, SIGNAL_LAST }; @@ -163,6 +165,11 @@ gst_d3d11_window_class_init (GstD3D11WindowClass * klass) (GParamFlags) (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | 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] = g_signal_new ("key-event", G_TYPE_FROM_CLASS (klass), 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_RUN_LAST, 0, NULL, NULL, NULL, 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 @@ -181,7 +193,7 @@ gst_d3d11_window_init (GstD3D11Window * self) self->enable_navigation_events = DEFAULT_ENABLE_NAVIGATION_EVENTS; self->fullscreen_toggle_mode = GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE; self->fullscreen = DEFAULT_FULLSCREEN; - self->render_stats = DEFAULT_RENDER_STATS; + self->emit_present = DEFAULT_EMIT_PRESENT; } static void @@ -219,6 +231,9 @@ gst_d3d11_window_set_property (GObject * object, guint prop_id, case PROP_WINDOW_HANDLE: self->external_handle = (guintptr) g_value_get_pointer (value); break; + case PROP_EMIT_PRESENT: + self->emit_present = g_value_get_boolean (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -244,6 +259,9 @@ gst_d3d11_window_get_property (GObject * object, guint prop_id, case PROP_FULLSCREEN: g_value_set_boolean (value, self->fullscreen); break; + case PROP_EMIT_PRESENT: + g_value_set_boolean (value, self->emit_present); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -844,6 +862,17 @@ gst_d3d111_window_present (GstD3D11Window * self, GstBuffer * buffer, 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); if (crop_meta) { input_rect.left = crop_meta->x; @@ -895,8 +924,13 @@ gst_d3d111_window_present (GstD3D11Window * self, GstBuffer * buffer, if (self->allow_tearing && self->fullscreen) 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); + } self->first_present = FALSE; diff --git a/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11window.h b/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11window.h index eb787aaaa9..f7d0213c99 100644 --- a/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11window.h +++ b/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11window.h @@ -86,7 +86,7 @@ struct _GstD3D11Window GstD3D11WindowFullscreenToggleMode fullscreen_toggle_mode; gboolean requested_fullscreen; gboolean fullscreen; - gboolean render_stats; + gboolean emit_present; GstVideoInfo info; GstVideoInfo render_info; diff --git a/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11window_corewindow.cpp b/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11window_corewindow.cpp index 2da1433a68..2ff60abfe7 100644 --- a/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11window_corewindow.cpp +++ b/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11window_corewindow.cpp @@ -452,7 +452,7 @@ gst_d3d11_window_core_window_present (GstD3D11Window * window, IDXGISwapChain1 *swap_chain = (IDXGISwapChain1 *) window->swap_chain; /* 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.pDirtyRects = &window->render_rect; } diff --git a/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11window_swapchainpanel.cpp b/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11window_swapchainpanel.cpp index 26ad2eb7a9..4574aab8d7 100644 --- a/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11window_swapchainpanel.cpp +++ b/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11window_swapchainpanel.cpp @@ -442,7 +442,7 @@ gst_d3d11_window_swap_chain_panel_present (GstD3D11Window * window, IDXGISwapChain1 *swap_chain = (IDXGISwapChain1 *) window->swap_chain; /* 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.pDirtyRects = &window->render_rect; } diff --git a/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11window_win32.cpp b/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11window_win32.cpp index 58437780a8..6ef2623406 100644 --- a/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11window_win32.cpp +++ b/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11window_win32.cpp @@ -80,7 +80,6 @@ struct _GstD3D11WindowWin32 GstD3D11WindowWin32OverlayState overlay_state; HDC device_handle; - gboolean first_present; gboolean have_swapchain1; /* atomic */ @@ -1067,7 +1066,7 @@ gst_d3d11_window_win32_present (GstD3D11Window * window, guint present_flags) DXGI_PRESENT_PARAMETERS present_params = { 0, }; /* 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.pDirtyRects = &window->render_rect; }