d3d11videosink: Add a property to support rendering statistics data on window
authorSeungha Yang <seungha@centricular.com>
Sat, 21 Nov 2020 19:39:57 +0000 (04:39 +0900)
committerSeungha Yang <seungha@centricular.com>
Thu, 3 Dec 2020 20:22:14 +0000 (05:22 +0900)
Add a new property "render-stats" to allow rendering statistics
data on window for debugging and/or development purpose.
Text rendering will be accelerated by GPU since this implementation
uses Direct2D/DirectWrite API and Direct3D inter-op for minimal overhead.
Specifically, text data will be rendered on swapchain backbuffer
directly without any copy/allocation of extra texture.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/1830>

sys/d3d11/gstd3d11videosink.c
sys/d3d11/gstd3d11videosinkbin.c
sys/d3d11/gstd3d11window.cpp
sys/d3d11/gstd3d11window.h
sys/d3d11/meson.build

index 8bb5aa0..ed94448 100644 (file)
@@ -44,6 +44,7 @@ enum
   PROP_ENABLE_NAVIGATION_EVENTS,
   PROP_FULLSCREEN_TOGGLE_MODE,
   PROP_FULLSCREEN,
+  PROP_RENDER_STATS,
 };
 
 #define DEFAULT_ADAPTER                   -1
@@ -51,6 +52,7 @@ enum
 #define DEFAULT_ENABLE_NAVIGATION_EVENTS  TRUE
 #define DEFAULT_FULLSCREEN_TOGGLE_MODE    GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE
 #define DEFAULT_FULLSCREEN                FALSE
+#define DEFAULT_RENDER_STATS              FALSE
 
 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
     GST_PAD_SINK,
@@ -84,6 +86,7 @@ struct _GstD3D11VideoSink
   gboolean enable_navigation_events;
   GstD3D11WindowFullscreenToggleMode fullscreen_toggle_mode;
   gboolean fullscreen;
+  gboolean render_stats;
 
   /* saved render rectangle until we have a window */
   GstVideoRectangle render_rect;
@@ -178,6 +181,16 @@ gst_d3d11_video_sink_class_init (GstD3D11VideoSinkClass * klass)
           "Ignored when \"fullscreen-toggle-mode\" does not include \"property\"",
           DEFAULT_FULLSCREEN, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
+#ifdef HAVE_DIRECT_WRITE
+  g_object_class_install_property (gobject_class, PROP_RENDER_STATS,
+      g_param_spec_boolean ("render-stats",
+          "Render Stats",
+          "Render statistics data (e.g., average framerate) on window",
+          DEFAULT_RENDER_STATS,
+          GST_PARAM_CONDITIONALLY_AVAILABLE | GST_PARAM_MUTABLE_READY |
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+#endif
+
   element_class->set_context =
       GST_DEBUG_FUNCPTR (gst_d3d11_video_sink_set_context);
 
@@ -213,6 +226,7 @@ gst_d3d11_video_sink_init (GstD3D11VideoSink * self)
   self->enable_navigation_events = DEFAULT_ENABLE_NAVIGATION_EVENTS;
   self->fullscreen_toggle_mode = DEFAULT_FULLSCREEN_TOGGLE_MODE;
   self->fullscreen = DEFAULT_FULLSCREEN;
+  self->render_stats = DEFAULT_RENDER_STATS;
 }
 
 static void
@@ -252,6 +266,11 @@ gst_d3d11_videosink_set_property (GObject * object, guint prop_id,
         g_object_set (self->window, "fullscreen", self->fullscreen, NULL);
       }
       break;
+#ifdef HAVE_DIRECT_WRITE
+    case PROP_RENDER_STATS:
+      self->render_stats = g_value_get_boolean (value);
+      break;
+#endif
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -285,6 +304,11 @@ gst_d3d11_videosink_get_property (GObject * object, guint prop_id,
         g_value_set_boolean (value, self->fullscreen);
       }
       break;
+#ifdef HAVE_DIRECT_WRITE
+    case PROP_RENDER_STATS:
+      g_value_set_boolean (value, self->render_stats);
+      break;
+#endif
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -592,6 +616,9 @@ gst_d3d11_video_sink_start (GstBaseSink * sink)
       "fullscreen-toggle-mode", self->fullscreen_toggle_mode,
       "fullscreen", self->fullscreen,
       "enable-navigation-events", self->enable_navigation_events, NULL);
+#ifdef HAVE_DIRECT_WRITE
+  g_object_set (self->window, "render-stats", self->render_stats, NULL);
+#endif
   GST_OBJECT_UNLOCK (self);
 
   g_signal_connect (self->window, "key-event",
@@ -864,6 +891,7 @@ gst_d3d11_video_sink_show_frame (GstVideoSink * sink, GstBuffer * buf)
   gboolean need_unref = FALSE;
   gboolean do_device_copy = TRUE;
   gint i;
+  GstStructure *stats = NULL;
 
   render_buf = buf;
 
@@ -955,7 +983,10 @@ gst_d3d11_video_sink_show_frame (GstVideoSink * sink, GstBuffer * buf)
   rect.w = self->video_width;
   rect.h = self->video_height;
 
-  ret = gst_d3d11_window_render (self->window, render_buf, &rect);
+  if (self->render_stats)
+    stats = gst_base_sink_get_stats (GST_BASE_SINK_CAST (self));
+
+  ret = gst_d3d11_window_render (self->window, render_buf, &rect, stats);
   if (need_unref)
     gst_buffer_unref (render_buf);
 
@@ -1013,7 +1044,7 @@ gst_d3d11_video_sink_expose (GstVideoOverlay * overlay)
     rect.w = GST_VIDEO_SINK_WIDTH (self);
     rect.h = GST_VIDEO_SINK_HEIGHT (self);
 
-    gst_d3d11_window_render (self->window, NULL, &rect);
+    gst_d3d11_window_render (self->window, NULL, &rect, NULL);
   }
 }
 
index d6481d7..0ed16ee 100644 (file)
@@ -60,6 +60,7 @@ enum
   PROP_ENABLE_NAVIGATION_EVENTS,
   PROP_FULLSCREEN_TOGGLE_MODE,
   PROP_FULLSCREEN,
+  PROP_RENDER_STATS,
 };
 
 /* basesink */
@@ -85,6 +86,7 @@ enum
 #define DEFAULT_ENABLE_NAVIGATION_EVENTS  TRUE
 #define DEFAULT_FULLSCREEN_TOGGLE_MODE    GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE
 #define DEFAULT_FULLSCREEN                FALSE
+#define DEFAULT_RENDER_STATS              FALSE
 
 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
     GST_PAD_SINK,
@@ -238,6 +240,15 @@ gst_d3d11_video_sink_bin_class_init (GstD3D11VideoSinkBinClass * klass)
           "fullscreen",
           "Ignored when \"fullscreen-toggle-mode\" does not include \"property\"",
           DEFAULT_FULLSCREEN, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+#ifdef HAVE_DIRECT_WRITE
+  g_object_class_install_property (gobject_class, PROP_RENDER_STATS,
+      g_param_spec_boolean ("render-stats",
+          "Render Stats",
+          "Render statistics data (e.g., average framerate) on window",
+          DEFAULT_RENDER_STATS,
+          GST_PARAM_CONDITIONALLY_AVAILABLE | GST_PARAM_MUTABLE_READY |
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+#endif
 
   gst_element_class_set_static_metadata (element_class,
       "Direct3D11 video sink bin", "Sink/Video",
index e4714a1..77051b7 100644 (file)
 #endif
 #include <windows.ui.xaml.h>
 #include <windows.applicationmodel.core.h>
+#endif
+
+#ifdef HAVE_DIRECT_WRITE
+#include <dwrite.h>
+#include <d2d1_1.h>
+#include <sstream>
+#endif
+
+#if GST_D3D11_WINAPI_ONLY_APP || defined(HAVE_DIRECT_WRITE)
 #include <wrl.h>
 #include <wrl/wrappers/corewrappers.h>
-
 using namespace Microsoft::WRL;
 #endif
 
@@ -45,6 +53,21 @@ GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_window_debug);
 #define GST_CAT_DEFAULT gst_d3d11_window_debug
 }
 
+struct _GstD3D11WindowPrivate
+{
+#ifdef HAVE_DIRECT_WRITE
+  IDWriteFactory *dwrite_factory;
+  IDWriteTextFormat *dwrite_format;
+
+  ID2D1Factory1 *d2d_factory;
+  ID2D1Device *d2d_device;
+  ID2D1DeviceContext *d2d_device_context;
+  ID2D1SolidColorBrush *d2d_brush;
+#else
+  gpointer dummy;
+#endif
+};
+
 enum
 {
   PROP_0,
@@ -54,12 +77,14 @@ enum
   PROP_FULLSCREEN_TOGGLE_MODE,
   PROP_FULLSCREEN,
   PROP_WINDOW_HANDLE,
+  PROP_RENDER_STATS,
 };
 
 #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
 
 enum
 {
@@ -94,7 +119,8 @@ gst_d3d11_window_fullscreen_toggle_mode_type (void)
 }
 
 #define gst_d3d11_window_parent_class parent_class
-G_DEFINE_ABSTRACT_TYPE (GstD3D11Window, gst_d3d11_window, GST_TYPE_OBJECT);
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GstD3D11Window, gst_d3d11_window,
+    GST_TYPE_OBJECT);
 
 static void gst_d3d11_window_set_property (GObject * object, guint prop_id,
     const GValue * value, GParamSpec * pspec);
@@ -102,7 +128,7 @@ static void gst_d3d11_window_get_property (GObject * object, guint prop_id,
     GValue * value, GParamSpec * pspec);
 static void gst_d3d11_window_dispose (GObject * object);
 static GstFlowReturn gst_d3d111_window_present (GstD3D11Window * self,
-    GstBuffer * buffer);
+    GstBuffer * buffer, GstStructure * stats);
 static void gst_d3d11_window_on_resize_default (GstD3D11Window * window,
     guint width, guint height);
 
@@ -158,6 +184,15 @@ gst_d3d11_window_class_init (GstD3D11WindowClass * klass)
           (GParamFlags) (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
               G_PARAM_STATIC_STRINGS)));
 
+#ifdef HAVE_DIRECT_WRITE
+  g_object_class_install_property (gobject_class, PROP_RENDER_STATS,
+      g_param_spec_boolean ("render-stats",
+          "Render Stats",
+          "Render statistics data (e.g., average framerate) on window",
+          DEFAULT_RENDER_STATS,
+          (GParamFlags) (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)));
+#endif
+
   d3d11_window_signals[SIGNAL_KEY_EVENT] =
       g_signal_new ("key-event", G_TYPE_FROM_CLASS (klass),
       G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
@@ -176,6 +211,10 @@ 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->priv =
+      (GstD3D11WindowPrivate *) gst_d3d11_window_get_instance_private (self);
 }
 
 static void
@@ -213,6 +252,11 @@ gst_d3d11_window_set_property (GObject * object, guint prop_id,
     case PROP_WINDOW_HANDLE:
       self->external_handle = (guintptr) g_value_get_pointer (value);
       break;
+#ifdef HAVE_DIRECT_WRITE
+    case PROP_RENDER_STATS:
+      self->render_stats = g_value_get_boolean (value);
+      break;
+#endif
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -244,10 +288,155 @@ gst_d3d11_window_get_property (GObject * object, guint prop_id,
   }
 }
 
+#ifdef HAVE_DIRECT_WRITE
+static void
+gst_d3d11_window_release_dwrite_resources (GstD3D11Window * self)
+{
+  GstD3D11WindowPrivate *priv = self->priv;
+
+  if (priv->d2d_device_context) {
+    priv->d2d_device_context->Release ();
+    priv->d2d_device_context = NULL;
+  }
+
+  if (priv->d2d_factory) {
+    priv->d2d_factory->Release ();
+    priv->d2d_factory = NULL;
+  }
+
+  if (priv->d2d_device) {
+    priv->d2d_device->Release ();
+    priv->d2d_device = NULL;
+  }
+
+  if (priv->d2d_brush) {
+    priv->d2d_brush->Release ();
+    priv->d2d_brush = NULL;
+  }
+
+  if (priv->dwrite_factory) {
+    priv->dwrite_factory->Release ();
+    priv->dwrite_factory = NULL;
+  }
+
+  if (priv->dwrite_format) {
+    priv->dwrite_format->Release ();
+    priv->dwrite_format = NULL;
+  }
+}
+
+static void
+gst_d3d11_window_prepare_dwrite_device (GstD3D11Window * self)
+{
+  GstD3D11WindowPrivate *priv = self->priv;
+  HRESULT hr;
+  ComPtr<IDXGIDevice> dxgi_device;
+  ID3D11Device *device_handle;
+
+  if (!self->device) {
+    GST_ERROR_OBJECT (self, "D3D11Device is unavailable");
+    return;
+  }
+
+  /* Already prepared */
+  if (priv->d2d_device)
+    return;
+
+  hr = D2D1CreateFactory (D2D1_FACTORY_TYPE_MULTI_THREADED,
+      IID_PPV_ARGS (&priv->d2d_factory));
+  if (!gst_d3d11_result (hr, self->device))
+    goto error;
+
+  device_handle = gst_d3d11_device_get_device_handle (self->device);
+  hr = device_handle->QueryInterface (IID_PPV_ARGS (&dxgi_device));
+  if (!gst_d3d11_result (hr, self->device))
+    goto error;
+
+  hr = priv->d2d_factory->CreateDevice (dxgi_device.Get (), &priv->d2d_device);
+  if (!gst_d3d11_result (hr, self->device))
+    goto error;
+
+  hr = priv->d2d_device->CreateDeviceContext (D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS,
+      &priv->d2d_device_context);
+  if (!gst_d3d11_result (hr, self->device))
+    goto error;
+
+  hr = priv->d2d_device_context->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Yellow),
+      &priv->d2d_brush);
+  if (!gst_d3d11_result (hr, self->device))
+    goto error;
+
+  hr = DWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED,
+      __uuidof (IDWriteFactory), (IUnknown **) &priv->dwrite_factory);
+  if (!gst_d3d11_result (hr, self->device))
+    goto error;
+
+  /* Configure font */
+  hr = priv->dwrite_factory->CreateTextFormat(L"Arial", NULL,
+      DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
+      DWRITE_FONT_STRETCH_NORMAL, 12.0f, L"en-US", &priv->dwrite_format);
+  if (!gst_d3d11_result (hr, self->device))
+    goto error;
+
+  GST_DEBUG_OBJECT (self, "Direct2D device is prepared");
+
+  return;
+
+error:
+  gst_d3d11_window_release_dwrite_resources (self);
+}
+
+static void
+gst_d3d11_window_dwrite_on_resize (GstD3D11Window * self,
+    ID3D11Texture2D * backbuffer)
+{
+  GstD3D11WindowPrivate *priv = self->priv;
+  ComPtr<IDXGISurface> dxgi_surface;
+  ComPtr<ID2D1Bitmap1> bitmap;
+  D2D1_BITMAP_PROPERTIES1 prop;
+  HRESULT hr;
+
+  if (!priv->d2d_device)
+    return;
+
+  prop.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
+  prop.pixelFormat.alphaMode = D2D1_ALPHA_MODE_IGNORE;
+  /* default dpi */
+  prop.dpiX = 96.0f;
+  prop.dpiY = 96.0f;
+  prop.bitmapOptions =
+      D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW;
+  prop.colorContext = NULL;
+
+  hr = backbuffer->QueryInterface (IID_PPV_ARGS (&dxgi_surface));
+  if (!gst_d3d11_result (hr, self->device))
+    goto error;
+
+  hr = priv->d2d_device_context->CreateBitmapFromDxgiSurface (dxgi_surface.Get (),
+      &prop, &bitmap);
+  if (!gst_d3d11_result (hr, self->device))
+    goto error;
+
+  priv->d2d_device_context->SetTarget (bitmap.Get ());
+
+  GST_LOG_OBJECT (self, "New D2D bitmap has been configured");
+
+  return;
+
+error:
+  gst_d3d11_window_release_dwrite_resources (self);
+}
+
+#endif
+
 static void
 gst_d3d11_window_release_resources (GstD3D11Device * device,
     GstD3D11Window * window)
 {
+#ifdef HAVE_DIRECT_WRITE
+  gst_d3d11_window_release_dwrite_resources (window);
+#endif
+
   if (window->rtv) {
     window->rtv->Release ();
     window->rtv = NULL;
@@ -287,6 +476,9 @@ static void
 gst_d3d11_window_on_resize_default (GstD3D11Window * window, guint width,
     guint height)
 {
+#ifdef HAVE_DIRECT_WRITE
+  GstD3D11WindowPrivate *priv = window->priv;
+#endif
   HRESULT hr;
   ID3D11Device *device_handle;
   D3D11_TEXTURE2D_DESC desc;
@@ -312,6 +504,12 @@ gst_d3d11_window_on_resize_default (GstD3D11Window * window, guint width,
     window->pov = NULL;
   }
 
+#ifdef HAVE_DIRECT_WRITE
+  /* D2D bitmap need to be cleared before resizing swapchain buffer */
+  if (priv->d2d_device_context)
+    priv->d2d_device_context->SetTarget (NULL);
+#endif
+
   swap_chain->GetDesc (&swap_desc);
   hr = swap_chain->ResizeBuffers (0, width, height, window->dxgi_format,
       swap_desc.Flags);
@@ -378,10 +576,15 @@ gst_d3d11_window_on_resize_default (GstD3D11Window * window, guint width,
       goto done;
   }
 
+#ifdef HAVE_DIRECT_WRITE
+  if (window->render_stats)
+    gst_d3d11_window_dwrite_on_resize (window, backbuffer);
+#endif
+
   window->first_present = TRUE;
 
   /* redraw the last scene if cached buffer exits */
-  gst_d3d111_window_present (window, NULL);
+  gst_d3d111_window_present (window, NULL, NULL);
 
 done:
   if (backbuffer)
@@ -489,14 +692,23 @@ gst_d3d11_window_prepare (GstD3D11Window * window, guint display_width,
     return FALSE;
   }
 
-  for (i = 0; i < GST_VIDEO_INFO_N_COMPONENTS (&window->info); i++) {
-    if (GST_VIDEO_INFO_COMP_DEPTH (&window->info, i) > 8) {
-      if (formats[2].supported) {
-        chosen_format = &formats[2];
+#ifdef HAVE_DIRECT_WRITE
+  if (window->render_stats && formats[1].supported) {
+    /* FIXME: D2D seems to be accepting only DXGI_FORMAT_B8G8R8A8_UNORM */
+    chosen_format = &formats[1];
+  } else
+#else
+  {
+    for (i = 0; i < GST_VIDEO_INFO_N_COMPONENTS (&window->info); i++) {
+      if (GST_VIDEO_INFO_COMP_DEPTH (&window->info, i) > 8) {
+        if (formats[2].supported) {
+          chosen_format = &formats[2];
+        }
+        break;
       }
-      break;
     }
   }
+#endif
 
   if (!chosen_format) {
     /* prefer native format over conversion */
@@ -745,6 +957,11 @@ gst_d3d11_window_prepare (GstD3D11Window * window, guint display_width,
         "Cannot create overlay compositor");
     goto error;
   }
+
+#ifdef HAVE_DIRECT_WRITE
+  if (window->render_stats)
+    gst_d3d11_window_prepare_dwrite_device (window);
+#endif
   gst_d3d11_device_unlock (window->device);
 
   /* call resize to allocated resources */
@@ -815,8 +1032,60 @@ gst_d3d11_window_buffer_ensure_processor_input (GstD3D11Window * self,
   return TRUE;
 }
 
+#ifdef HAVE_DIRECT_WRITE
+static void
+gst_d3d11_window_present_d2d (GstD3D11Window * self, GstStructure * stats)
+{
+  GstD3D11WindowPrivate *priv = self->priv;
+  HRESULT hr;
+  gdouble framerate = 0.0;
+  guint64 dropped = 0;
+  guint64 rendered = 0;
+  std::wostringstream stats_str;
+  ComPtr<IDWriteTextLayout> layout;
+  FLOAT left;
+  FLOAT top;
+
+  if (!priv->d2d_device)
+    return;
+
+  if (!stats)
+    return;
+
+  gst_structure_get_double (stats, "average-rate", &framerate);
+  gst_structure_get_uint64 (stats, "dropped", &dropped);
+  gst_structure_get_uint64 (stats, "rendered", &rendered);
+
+  stats_str.precision (5);
+  stats_str << "Average-rate: " << framerate << std::endl;
+  stats_str << "Dropped: " << dropped << std::endl;
+  stats_str << "Rendered: " << rendered << std::endl;
+
+  hr = priv->dwrite_factory->CreateTextLayout (stats_str.str().c_str(),
+      (UINT32) stats_str.str().size(), priv->dwrite_format,
+      self->render_rect.right - self->render_rect.left,
+      self->render_rect.bottom - self->render_rect.top,
+      &layout);
+  if (!gst_d3d11_result (hr, self->device))
+    return;
+
+  left = self->render_rect.left + 5.0f;
+  top = self->render_rect.top + 5.0f;
+
+  priv->d2d_device_context->BeginDraw ();
+  priv->d2d_device_context->DrawTextLayout(D2D1::Point2F(left, top),
+      layout.Get(), priv->d2d_brush);
+
+  hr = priv->d2d_device_context->EndDraw ();
+  gst_d3d11_result (hr, self->device);
+
+  return;
+}
+#endif
+
 static GstFlowReturn
-gst_d3d111_window_present (GstD3D11Window * self, GstBuffer * buffer)
+gst_d3d111_window_present (GstD3D11Window * self, GstBuffer * buffer,
+    GstStructure * stats)
 {
   GstD3D11WindowClass *klass = GST_D3D11_WINDOW_GET_CLASS (self);
   GstFlowReturn ret = GST_FLOW_OK;
@@ -885,6 +1154,10 @@ gst_d3d111_window_present (GstD3D11Window * self, GstBuffer * buffer)
     }
 #endif
 
+#if HAVE_DIRECT_WRITE
+    gst_d3d11_window_present_d2d (self, stats);
+#endif
+
     ret = klass->present (self, present_flags);
 
     self->first_present = FALSE;
@@ -895,7 +1168,7 @@ gst_d3d111_window_present (GstD3D11Window * self, GstBuffer * buffer)
 
 GstFlowReturn
 gst_d3d11_window_render (GstD3D11Window * window, GstBuffer * buffer,
-    GstVideoRectangle * rect)
+    GstVideoRectangle * rect, GstStructure * stats)
 {
   GstMemory *mem;
   GstFlowReturn ret;
@@ -907,13 +1180,19 @@ gst_d3d11_window_render (GstD3D11Window * window, GstBuffer * buffer,
   if (!gst_is_d3d11_memory (mem)) {
     GST_ERROR_OBJECT (window, "Invalid buffer");
 
+    if (stats)
+      gst_structure_free (stats);
+
     return GST_FLOW_ERROR;
   }
 
   gst_d3d11_device_lock (window->device);
-  ret = gst_d3d111_window_present (window, buffer);
+  ret = gst_d3d111_window_present (window, buffer, stats);
   gst_d3d11_device_unlock (window->device);
 
+  if (stats)
+    gst_structure_free (stats);
+
   return ret;
 }
 
index 6ca6024..c72d9d7 100644 (file)
@@ -41,6 +41,7 @@ G_BEGIN_DECLS
 
 typedef struct _GstD3D11Window        GstD3D11Window;
 typedef struct _GstD3D11WindowClass   GstD3D11WindowClass;
+typedef struct _GstD3D11WindowPrivate GstD3D11WindowPrivate;
 
 #define GST_D3D11_WINDOW_FLOW_CLOSED GST_FLOW_CUSTOM_ERROR
 
@@ -76,6 +77,7 @@ struct _GstD3D11Window
   GstD3D11WindowFullscreenToggleMode fullscreen_toggle_mode;
   gboolean requested_fullscreen;
   gboolean fullscreen;
+  gboolean render_stats;
 
   GstVideoInfo info;
   GstVideoInfo render_info;
@@ -103,6 +105,8 @@ struct _GstD3D11Window
   GstBuffer *cached_buffer;
   gboolean first_present;
   gboolean allow_tearing;
+
+  GstD3D11WindowPrivate *priv;
 };
 
 struct _GstD3D11WindowClass
@@ -153,7 +157,8 @@ gboolean      gst_d3d11_window_prepare              (GstD3D11Window * window,
 
 GstFlowReturn gst_d3d11_window_render               (GstD3D11Window * window,
                                                      GstBuffer * buffer,
-                                                     GstVideoRectangle * src_rect);
+                                                     GstVideoRectangle * src_rect,
+                                                     GstStructure * stats);
 
 gboolean      gst_d3d11_window_unlock               (GstD3D11Window * window);
 
index c88a4a6..2ba72f3 100644 (file)
@@ -65,6 +65,8 @@ d3d11_lib = cc.find_library('d3d11', required : d3d11_option)
 dxgi_lib = cc.find_library('dxgi', required : d3d11_option)
 d3dcompiler_lib = cc.find_library('d3dcompiler', required: d3d11_option)
 runtimeobject_lib = cc.find_library('runtimeobject', required : false)
+dwrite_lib = cc.find_library('dwrite', required : false)
+d2d1_lib = cc.find_library('d2d1', required : false)
 
 foreach dxgi_h: dxgi_headers
   if not have_dxgi_header and cc.has_header(dxgi_h[0])
@@ -177,6 +179,15 @@ if get_option('debug') and not (winapi_app_only and get_option('b_vscrt') == 'md
       set_variable(f.get(3), true)
     endif
   endforeach
+
+  # Maybe we want to implement DirectWrite based text rendering in the future,
+  # but currently only debugging purpose.
+  # Also, MinGW is not supported for now because of toolchain issue
+  if cc.get_id() == 'msvc' and dwrite_lib.found() and d2d1_lib.found() and cc.has_header('dwrite.h') and cc.has_header('d2d1_1.h')
+    d3d11_conf.set('HAVE_DIRECT_WRITE', 1)
+    extra_dep += [dwrite_lib, d2d1_lib]
+    message('Enable DirectWrite support in D3D11 plugin')
+  endif
 else
   message('Disable D3D11Debug and DXGIDebug layers')
 endif