d3d11videosink: Add present signal
authorSeungha Yang <seungha@centricular.com>
Fri, 19 Aug 2022 11:25:31 +0000 (20:25 +0900)
committerGStreamer Marge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Wed, 24 Aug 2022 17:44:49 +0000 (17:44 +0000)
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>

subprojects/gst-plugins-bad/sys/d3d11/gstd3d11videosink.cpp
subprojects/gst-plugins-bad/sys/d3d11/gstd3d11window.cpp
subprojects/gst-plugins-bad/sys/d3d11/gstd3d11window.h
subprojects/gst-plugins-bad/sys/d3d11/gstd3d11window_corewindow.cpp
subprojects/gst-plugins-bad/sys/d3d11/gstd3d11window_swapchainpanel.cpp
subprojects/gst-plugins-bad/sys/d3d11/gstd3d11window_win32.cpp

index ff0695f073a198c74c85f7f4d742ff5776658f64..7140b30ac0eb303f9a26a42e758c314068c41e7c 100644 (file)
@@ -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;
 }
index 58063d37042ec98ee7fb18d8ae7505d79ec4fb41..95276981e42d25be9a46d39d533b76559499f9c8 100644 (file)
@@ -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;
 
index eb787aaaa995fd2a1c5beae29657b7e6ca7e06dd..f7d0213c99bb9cc9258014a9eaefe351fac0c5c4 100644 (file)
@@ -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;
index 2da1433a68f4ed8c326e2179a273166a02e90424..2ff60abfe72db350476d0876bda6fe592026f9f7 100644 (file)
@@ -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;
   }
index 26ad2eb7a96fe7a25ca7eb6c5580803f2dcc6174..4574aab8d7be26b54959d673b01a20ac0a8de81f 100644 (file)
@@ -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;
   }
index 58437780a858cd85918e32e701b266dbab838aa0..6ef2623406fcedc3d319c4347c9764a610e53746 100644 (file)
@@ -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;
     }