From: Seungha Yang Date: Fri, 22 Jul 2022 15:11:18 +0000 (+0900) Subject: d3d11: Implement fence abstraction X-Git-Tag: 1.22.0~1228 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=ba259111d58e3e25ea26fbdd136801d779187b31;p=platform%2Fupstream%2Fgstreamer.git d3d11: Implement fence abstraction Depending on device feature level, d3d11 runtime can support ID3D11Fence which is equivalent to ID3D12Fence. Waiting using fence has performance-wise benefit over pulling ID3D11Query status. If ID3D11Fence is not supported by device, then ID3D11Query will be used instead. Part-of: --- diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/d3d11/gstd3d11_fwd.h b/subprojects/gst-plugins-bad/gst-libs/gst/d3d11/gstd3d11_fwd.h index e4601ae..1589fc8 100644 --- a/subprojects/gst-plugins-bad/gst-libs/gst/d3d11/gstd3d11_fwd.h +++ b/subprojects/gst-plugins-bad/gst-libs/gst/d3d11/gstd3d11_fwd.h @@ -63,5 +63,8 @@ typedef struct _GstD3D11Converter GstD3D11Converter; typedef struct _GstD3D11ConverterClass GstD3D11ConverterClass; typedef struct _GstD3D11ConverterPrivate GstD3D11ConverterPrivate; +typedef struct _GstD3D11Fence GstD3D11Fence; +typedef struct _GstD3D11FencePrivate GstD3D11FencePrivate; + G_END_DECLS diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/d3d11/gstd3d11device.cpp b/subprojects/gst-plugins-bad/gst-libs/gst/d3d11/gstd3d11device.cpp index a36f284..41d4f0e 100644 --- a/subprojects/gst-plugins-bad/gst-libs/gst/d3d11/gstd3d11device.cpp +++ b/subprojects/gst-plugins-bad/gst-libs/gst/d3d11/gstd3d11device.cpp @@ -105,7 +105,9 @@ struct _GstD3D11DevicePrivate gint64 adapter_luid; ID3D11Device *device; + ID3D11Device5 *device5; ID3D11DeviceContext *device_context; + ID3D11DeviceContext4 *device_context4; ID3D11VideoDevice *video_device; ID3D11VideoContext *video_context; @@ -116,6 +118,8 @@ struct _GstD3D11DevicePrivate GRecMutex extern_lock; GMutex resource_lock; + LARGE_INTEGER frequency; + #if HAVE_D3D11SDKLAYERS_H ID3D11Debug *d3d11_debug; ID3D11InfoQueue *d3d11_info_queue; @@ -703,6 +707,8 @@ gst_d3d11_device_dispose (GObject * object) GST_LOG_OBJECT (self, "dispose"); + GST_D3D11_CLEAR_COM (priv->device5); + GST_D3D11_CLEAR_COM (priv->device_context4); GST_D3D11_CLEAR_COM (priv->video_device); GST_D3D11_CLEAR_COM (priv->video_context); GST_D3D11_CLEAR_COM (priv->device); @@ -951,7 +957,9 @@ gst_d3d11_device_new_internal (const GstD3D11DeviceConstructData * data) ComPtr < IDXGIAdapter1 > adapter; ComPtr < IDXGIFactory1 > factory; ComPtr < ID3D11Device > device; + ComPtr < ID3D11Device5 > device5; ComPtr < ID3D11DeviceContext > device_context; + ComPtr < ID3D11DeviceContext4 > device_context4; HRESULT hr; UINT create_flags; guint adapter_index = 0; @@ -1077,6 +1085,14 @@ gst_d3d11_device_new_internal (const GstD3D11DeviceConstructData * data) priv = self->priv; + hr = device.As (&device5); + if (SUCCEEDED (hr)) + hr = device_context.As (&device_context4); + if (SUCCEEDED (hr)) { + priv->device5 = device5.Detach (); + priv->device_context4 = device_context4.Detach (); + } + priv->adapter = adapter_index; priv->device = device.Detach (); priv->device_context = device_context.Detach (); @@ -1099,6 +1115,9 @@ gst_d3d11_device_new_internal (const GstD3D11DeviceConstructData * data) gst_d3d11_device_setup_format_table (self); gst_d3d11_device_setup_debug_layer (self); + BOOL ret = QueryPerformanceFrequency (&priv->frequency); + g_assert (ret); + return self; } @@ -1382,3 +1401,258 @@ gst_d3d11_device_get_format (GstD3D11Device * device, GstVideoFormat format, return FALSE; } + +GST_DEFINE_MINI_OBJECT_TYPE (GstD3D11Fence, gst_d3d11_fence); + +struct _GstD3D11FencePrivate +{ + UINT64 fence_value; + ID3D11Fence *fence; + ID3D11Query *query; + HANDLE event_handle; + gboolean signalled; + gboolean synced; +}; + +static void +_gst_d3d11_fence_free (GstD3D11Fence * fence) +{ + GstD3D11FencePrivate *priv = fence->priv; + + GST_D3D11_CLEAR_COM (priv->fence); + GST_D3D11_CLEAR_COM (priv->query); + if (priv->event_handle) + CloseHandle (priv->event_handle); + + gst_clear_object (&fence->device); + + g_free (priv); + g_free (fence); +} + +/** + * gst_d3d11_device_create_fence: + * @device: a #GstD3D11Device + * + * Creates fence object (i.e., ID3D11Fence) if available, otherwise + * ID3D11Query with D3D11_QUERY_EVENT is created. + * + * Returns: a #GstD3D11Fence object + * + * Since: 1.22 + */ +GstD3D11Fence * +gst_d3d11_device_create_fence (GstD3D11Device * device) +{ + GstD3D11DevicePrivate *priv; + ID3D11Fence *fence = nullptr; + HRESULT hr = S_OK; + GstD3D11Fence *self; + + g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), nullptr); + + priv = device->priv; + + if (priv->device5 && priv->device_context4) { + hr = priv->device5->CreateFence (0, D3D11_FENCE_FLAG_NONE, + IID_PPV_ARGS (&fence)); + + if (!gst_d3d11_result (hr, device)) + GST_WARNING_OBJECT (device, "Failed to create fence object"); + } + + self = g_new0 (GstD3D11Fence, 1); + self->device = (GstD3D11Device *) gst_object_ref (device); + self->priv = g_new0 (GstD3D11FencePrivate, 1); + self->priv->fence = fence; + if (fence) { + self->priv->event_handle = CreateEventEx (nullptr, nullptr, + 0, EVENT_ALL_ACCESS); + } + + gst_mini_object_init (GST_MINI_OBJECT_CAST (self), 0, + GST_TYPE_D3D11_FENCE, nullptr, nullptr, + (GstMiniObjectFreeFunction) _gst_d3d11_fence_free); + + return self; +} + +/** + * gst_d3d11_fence_signal: + * @fence: a #GstD3D11Fence + * + * Sets sync point to fence for waiting. + * Must be called with gst_d3d11_device_lock() held + * + * Returns: %TRUE if successful + * + * Since: 1.22 + */ +gboolean +gst_d3d11_fence_signal (GstD3D11Fence * fence) +{ + HRESULT hr = S_OK; + GstD3D11Device *device; + GstD3D11DevicePrivate *device_priv; + GstD3D11FencePrivate *priv; + + g_return_val_if_fail (GST_IS_D3D11_FENCE (fence), FALSE); + + device = fence->device; + device_priv = device->priv; + priv = fence->priv; + + priv->signalled = FALSE; + priv->synced = FALSE; + + if (priv->fence) { + priv->fence_value++; + + GST_LOG_OBJECT (device, "Signals with fence value %" G_GUINT64_FORMAT, + priv->fence_value); + + hr = device_priv->device_context4->Signal (priv->fence, priv->fence_value); + if (!gst_d3d11_result (hr, device)) { + GST_ERROR_OBJECT (device, "Failed to signal fence value %" + G_GUINT64_FORMAT, fence->priv->fence_value); + return FALSE; + } + } else { + D3D11_QUERY_DESC desc; + + GST_D3D11_CLEAR_COM (priv->query); + + desc.Query = D3D11_QUERY_EVENT; + desc.MiscFlags = 0; + + GST_LOG_OBJECT (device, "Creating query object"); + + hr = device_priv->device->CreateQuery (&desc, &priv->query); + if (!gst_d3d11_result (hr, device)) { + GST_ERROR_OBJECT (device, "Failed to create query object"); + return FALSE; + } + + device_priv->device_context->End (priv->query); + } + + priv->signalled = TRUE; + + return TRUE; +} + +/** + * gst_d3d11_fence_wait: + * @fence: a #GstD3D11Fence + * + * Waits until previously issued GPU commands have been completed + * Must be called with gst_d3d11_device_lock() held + * + * Returns: %TRUE if successful + * + * Since: 1.22 + */ +gboolean +gst_d3d11_fence_wait (GstD3D11Fence * fence) +{ + HRESULT hr = S_OK; + GstD3D11Device *device; + GstD3D11DevicePrivate *device_priv; + GstD3D11FencePrivate *priv; + BOOL timer_ret; + LARGE_INTEGER current_time, now; + + g_return_val_if_fail (GST_IS_D3D11_FENCE (fence), FALSE); + + device = fence->device; + device_priv = device->priv; + priv = fence->priv; + + if (!priv->signalled) { + GST_DEBUG_OBJECT (device, "Fence is not signalled, nothing to wait"); + return TRUE; + } + + if (priv->synced) { + GST_DEBUG_OBJECT (device, "Already synced"); + return TRUE; + } + + timer_ret = QueryPerformanceCounter (¤t_time); + g_assert (timer_ret); + + now = current_time; + + if (priv->fence) { + GST_LOG_OBJECT (device, "Waiting fence value %" G_GUINT64_FORMAT, + priv->fence_value); + + if (fence->priv->fence->GetCompletedValue () < fence->priv->fence_value) { + hr = fence->priv->fence->SetEventOnCompletion (fence->priv->fence_value, + fence->priv->event_handle); + if (!gst_d3d11_result (hr, device)) { + GST_WARNING_OBJECT (device, "Failed set event handle"); + return FALSE; + } + + /* 20 seconds should be sufficient time */ + DWORD ret = WaitForSingleObject (priv->event_handle, 20000); + if (ret != WAIT_OBJECT_0) { + GST_WARNING_OBJECT (device, + "Failed to wait object, ret 0x%x", (guint) ret); + return FALSE; + } + } + } else { + LONGLONG timeout; + BOOL sync_done = FALSE; + + g_assert (priv->query != nullptr); + + /* 20 sec timeout */ + timeout = now.QuadPart + 20 * device_priv->frequency.QuadPart; + + GST_LOG_OBJECT (device, "Waiting event"); + + while (now.QuadPart < timeout && !sync_done) { + hr = device_priv->device_context->GetData (priv->query, + &sync_done, sizeof (BOOL), 0); + if (FAILED (hr)) { + GST_WARNING_OBJECT (device, "Failed to get event data"); + return FALSE; + } + + if (sync_done) + break; + + g_thread_yield (); + timer_ret = QueryPerformanceCounter (&now); + g_assert (timer_ret); + } + + if (!sync_done) { + GST_WARNING_OBJECT (device, "Timeout"); + return FALSE; + } + + GST_D3D11_CLEAR_COM (priv->query); + } + +#ifndef GST_DISABLE_GST_DEBUG + if (gst_debug_category_get_threshold (GST_CAT_DEFAULT) >= GST_LEVEL_LOG) { + GstClockTime elapsed; + + QueryPerformanceCounter (&now); + elapsed = gst_util_uint64_scale (now.QuadPart - current_time.QuadPart, + GST_SECOND, device_priv->frequency.QuadPart); + + GST_LOG_OBJECT (device, "Wait done, elapsed %" GST_TIME_FORMAT, + GST_TIME_ARGS (elapsed)); + } +#endif + + priv->signalled = FALSE; + priv->synced = TRUE; + + return TRUE; +} diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/d3d11/gstd3d11device.h b/subprojects/gst-plugins-bad/gst-libs/gst/d3d11/gstd3d11device.h index 970d334..01b2728 100644 --- a/subprojects/gst-plugins-bad/gst-libs/gst/d3d11/gstd3d11device.h +++ b/subprojects/gst-plugins-bad/gst-libs/gst/d3d11/gstd3d11device.h @@ -34,15 +34,19 @@ G_BEGIN_DECLS #define GST_IS_D3D11_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_D3D11_DEVICE)) #define GST_D3D11_DEVICE_CAST(obj) ((GstD3D11Device*)(obj)) +#define GST_TYPE_D3D11_FENCE (gst_d3d11_fence_get_type()) +#define GST_IS_D3D11_FENCE(obj) (GST_IS_MINI_OBJECT_TYPE(obj, GST_TYPE_D3D11_FENCE)) +#define GST_D3D11_FENCE(obj) ((GstD3D11Fence *)obj) +#define GST_D3D11_FENCE_CAST(obj) (GST_D3D11_FENCE(obj)) + #define GST_D3D11_DEVICE_HANDLE_CONTEXT_TYPE "gst.d3d11.device.handle" struct _GstD3D11Device { GstObject parent; - GstD3D11DevicePrivate *priv; - /*< private >*/ + GstD3D11DevicePrivate *priv; gpointer _gst_reserved[GST_PADDING]; }; @@ -94,5 +98,43 @@ gboolean gst_d3d11_device_get_format (GstD3D11Device * devi GstVideoFormat format, GstD3D11Format * device_format); +struct _GstD3D11Fence +{ + GstMiniObject parent; + + GstD3D11Device *device; + + /*< private >*/ + GstD3D11FencePrivate *priv; + gpointer _gst_reserved[GST_PADDING]; +}; + +GST_D3D11_API +GType gst_d3d11_fence_get_type (void); + +GST_D3D11_API +GstD3D11Fence * gst_d3d11_device_create_fence (GstD3D11Device * device); + +GST_D3D11_API +gboolean gst_d3d11_fence_signal (GstD3D11Fence * fence); + +GST_D3D11_API +gboolean gst_d3d11_fence_wait (GstD3D11Fence * fence); + +static inline void +gst_d3d11_fence_unref (GstD3D11Fence * fence) +{ + gst_mini_object_unref (GST_MINI_OBJECT_CAST (fence)); +} + +static inline void +gst_clear_d3d11_fence (GstD3D11Fence ** fence) +{ + if (fence && *fence) { + gst_d3d11_fence_unref (*fence); + *fence = NULL; + } +} + G_END_DECLS