mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-18 14:26:43 +00:00
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: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2790>
This commit is contained in:
parent
ccb5d52be5
commit
ba259111d5
3 changed files with 321 additions and 2 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in a new issue