From a1c8fc61633caeedab7f00a381c8f61b7bd7410e Mon Sep 17 00:00:00 2001 From: Seungha Yang Date: Sat, 16 Mar 2024 20:40:58 +0900 Subject: [PATCH] d3d12device: Keep device object permanently Because ID3D12Device objects are singletons per adapter, GstD3D12Device was following the API design, that is, keep track of global GstD3D12Device objects and reuses it. That means ID3D12Device object can be released at the time when GstD3D12Device is destroyed. But exetrnal APIs such as NVENC does not seem to be happy with the released ID3D12Device, that could be a driver bug though. Let's hold already opened ID3D12Device permanently without releasing it for now. Part-of: --- .../sys/d3d12/gstd3d12-private.h | 4 - .../sys/d3d12/gstd3d12commandqueue.cpp | 1 - .../sys/d3d12/gstd3d12device.cpp | 343 +++++++----------- .../gst-plugins-bad/sys/d3d12/plugin.cpp | 1 - 4 files changed, 130 insertions(+), 219 deletions(-) diff --git a/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12-private.h b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12-private.h index 496b322501..bd86fa2f51 100644 --- a/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12-private.h +++ b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12-private.h @@ -191,7 +191,3 @@ static const GstD3D12Format g_gst_d3d12_default_format_map[] = { void gst_d3d12_device_clear_yuv_texture (GstD3D12Device * device, GstMemory * mem); - -void gst_d3d12_init_background_thread (void); - -void gst_d3d12_sync_background_thread (void); diff --git a/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12commandqueue.cpp b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12commandqueue.cpp index c603d42779..c3a4e381fc 100644 --- a/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12commandqueue.cpp +++ b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12commandqueue.cpp @@ -396,7 +396,6 @@ gst_d3d12_command_queue_set_notify (GstD3D12CommandQueue * queue, std::lock_guard < std::mutex > elk (priv->execute_lock); auto gc_data = std::make_shared < GCData > (fence_data, notify, fence_value); if (!priv->gc_thread) { - gst_d3d12_init_background_thread (); priv->gc_thread = g_thread_new ("GstD3D12Gc", (GThreadFunc) gst_d3d12_command_queue_gc_thread, queue); } diff --git a/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12device.cpp b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12device.cpp index 85c8c7d16e..1a820ffecb 100644 --- a/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12device.cpp +++ b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12device.cpp @@ -40,9 +40,21 @@ #include #include -GST_DEBUG_CATEGORY_STATIC (gst_d3d12_device_debug); GST_DEBUG_CATEGORY_STATIC (gst_d3d12_sdk_debug); -#define GST_CAT_DEFAULT gst_d3d12_device_debug + +#ifndef GST_DISABLE_GST_DEBUG +#define GST_CAT_DEFAULT ensure_debug_category() +static GstDebugCategory * +ensure_debug_category (void) +{ + static GstDebugCategory *cat = nullptr; + GST_D3D12_CALL_ONCE_BEGIN { + cat = _gst_debug_category_new ("d3d12device", 0, "d3d12device"); + } GST_D3D12_CALL_ONCE_END; + + return cat; +} +#endif /* GST_DISABLE_GST_DEBUG */ enum { @@ -58,19 +70,11 @@ enum /* *INDENT-OFF* */ using namespace Microsoft::WRL; -struct _GstD3D12DevicePrivate +struct DeviceInner { - ~_GstD3D12DevicePrivate () + ~DeviceInner () { - auto hr = device->GetDeviceRemovedReason (); - /* If device were not removed, make sure no pending command in queue */ - if (hr == S_OK) { - if (direct_queue) - gst_d3d12_command_queue_fence_wait (direct_queue, G_MAXUINT64, nullptr); - - if (copy_queue) - gst_d3d12_command_queue_fence_wait (copy_queue, G_MAXUINT64, nullptr); - } + Drain (); gst_clear_object (&direct_queue); gst_clear_object (©_queue); @@ -85,39 +89,59 @@ struct _GstD3D12DevicePrivate adapter = nullptr; d3d11on12 = nullptr; - if (info_queue && device) { - ComPtr debug_dev; - device.As (&debug_dev); - if (debug_dev) { - debug_dev->ReportLiveDeviceObjects (D3D12_RLDO_DETAIL | - D3D12_RLDO_IGNORE_INTERNAL); + ReportLiveObjects (); + } - UINT64 num_msg = info_queue->GetNumStoredMessages (); - for (UINT64 i = 0; i < num_msg; i++) { - HRESULT hr; - SIZE_T msg_len; - D3D12_MESSAGE *msg; + void Drain () + { + if (direct_queue) + gst_d3d12_command_queue_drain (direct_queue); - hr = info_queue->GetMessage (i, nullptr, &msg_len); - if (FAILED (hr) || msg_len == 0) - continue; + if (copy_queue) + gst_d3d12_command_queue_drain (copy_queue); + } - msg = (D3D12_MESSAGE *) g_malloc0 (msg_len); - hr = info_queue->GetMessage (i, msg, &msg_len); - if (FAILED (hr) || msg_len == 0) { - g_free (msg); - continue; - } + void ReportLiveObjects () + { + if (!info_queue || !device) + return; - gst_debug_log (gst_d3d12_sdk_debug, GST_LEVEL_INFO, - __FILE__, GST_FUNCTION, __LINE__, nullptr, - "D3D12InfoQueue: %s", msg->pDescription); - g_free (msg); - } + ComPtr debug_dev; + device.As (&debug_dev); + if (!debug_dev) + return; - info_queue->ClearStoredMessages (); + debug_dev->ReportLiveDeviceObjects (D3D12_RLDO_DETAIL | + D3D12_RLDO_IGNORE_INTERNAL); + + GST_DEBUG ("Begin live object report %s", description.c_str ()); + + UINT64 num_msg = info_queue->GetNumStoredMessages (); + for (UINT64 i = 0; i < num_msg; i++) { + HRESULT hr; + SIZE_T msg_len; + D3D12_MESSAGE *msg; + + hr = info_queue->GetMessage (i, nullptr, &msg_len); + if (FAILED (hr) || msg_len == 0) + continue; + + msg = (D3D12_MESSAGE *) g_malloc0 (msg_len); + hr = info_queue->GetMessage (i, msg, &msg_len); + if (FAILED (hr) || msg_len == 0) { + g_free (msg); + continue; } + + gst_debug_log (gst_d3d12_sdk_debug, GST_LEVEL_INFO, + __FILE__, GST_FUNCTION, __LINE__, nullptr, + "D3D12InfoQueue: %s", msg->pDescription); + g_free (msg); } + + GST_DEBUG ("End live object report %s", description.c_str ()); + + info_queue->ClearStoredMessages (); } ComPtr device; @@ -149,6 +173,13 @@ struct _GstD3D12DevicePrivate gint64 adapter_luid = 0; }; +typedef std::shared_ptr DeviceInnerPtr; + +struct _GstD3D12DevicePrivate +{ + DeviceInnerPtr inner; +}; + enum GstD3D12DeviceConstructType { GST_D3D12_DEVICE_CONSTRUCT_FOR_INDEX, @@ -168,30 +199,6 @@ struct GstD3D12DeviceConstructData static GstD3D12Device * gst_d3d12_device_new_internal (const GstD3D12DeviceConstructData * data); -struct DeviceCache -{ - ~DeviceCache() - { - if (priv) - delete priv; - } - - GstD3D12Device *device = nullptr; - GstD3D12DevicePrivate *priv = nullptr; - guint64 token = 0; -}; - -/* Because ID3D12Device instance is a singleton per adapter, - * this DeviceCacheManager object will cache GstD3D12Device object and - * will return the same GstD3D12Device object for create request - * if instanted object exists already. - * - * Another role of this object dtor thread management. - * GstD3D12CommandQueue object held by GstD3D12Device will run background - * garbage collection thread and releasing garbage collection data - * could result in releasing GstD3D12Device object, which can cause self-thread - * joining. This manager will run one background thread to avoid it - */ class DeviceCacheManager { public: @@ -207,155 +214,61 @@ public: return inst; } - void InitThread () - { - std::lock_guard lk (lock_); - if (!thread_) - thread_ = new std::thread (&DeviceCacheManager::threadFunc, this); - } - - void Sync () - { - guint64 to_wait = 0; - - { - std::lock_guard lk (lock_); - if (!thread_) - return; - - token_++; - to_wait = token_; - - auto empty_item = std::make_shared (); - empty_item->token = to_wait; - - std::lock_guard olk (thread_lock_); - to_remove_.push (std::move (empty_item)); - thread_cond_.notify_one (); - } - - std::unique_lock olk (token_lock_); - while (token_synced_ < to_wait) - token_cond_.wait (olk); - } - - GstD3D12Device * Create (const GstD3D12DeviceConstructData * data) + GstD3D12Device * GetDevice (const GstD3D12DeviceConstructData * data) { std::lock_guard lk (lock_); auto it = std::find_if (list_.begin (), list_.end (), - [&] (const auto & cache) { - const auto priv = cache->priv; + [&] (const auto & device) { if (data->type == GST_D3D12_DEVICE_CONSTRUCT_FOR_INDEX) - return priv->adapter_index == data->data.index; + return device->adapter_index == data->data.index; - return priv->adapter_luid == data->data.luid; + return device->adapter_luid == data->data.luid; }); - if (it != list_.end ()) - return (GstD3D12Device *) gst_object_ref ((*it)->device); + if (it != list_.end ()) { + GST_DEBUG ("Reusing created device"); + auto device = (GstD3D12Device *) + g_object_new (GST_TYPE_D3D12_DEVICE, nullptr); + gst_object_ref_sink (device); + device->priv->inner = *it; + return device; + } auto device = gst_d3d12_device_new_internal (data); if (!device) return nullptr; - gst_object_ref_sink (device); + GST_DEBUG ("Created new device"); - auto item = std::make_shared (); - item->device = device; - item->priv = device->priv; - - g_object_weak_ref (G_OBJECT (device), DeviceCacheManager::NotifyCb, this); - list_.push_back (item); + list_.push_back (device->priv->inner); return device; } - static void NotifyCb (gpointer data, GObject * device) + void ReleaseDevice (guint64 luid) { - auto self = (DeviceCacheManager *) data; - self->remove ((GstD3D12Device *) device); + std::lock_guard lk (lock_); + for (const auto & it : list_) { + if (it->adapter_luid == luid) { + if (it.use_count () == 1) { + it->Drain (); + it->ReportLiveObjects (); + } + return; + } + } } private: DeviceCacheManager () {} ~DeviceCacheManager () {} - void threadFunc () - { - while (true) { - std::unique_lock lk (thread_lock_); - while (to_remove_.empty ()) - thread_cond_.wait (lk); - - while (!to_remove_.empty ()) { - guint64 token; - - { - auto item = to_remove_.front (); - to_remove_.pop (); - token = item->token; - } - - std::lock_guard olk (token_lock_); - token_synced_ = token; - token_cond_.notify_all (); - } - } - } - - void remove (GstD3D12Device * device) - { - std::lock_guard lk (lock_); - auto it = std::find_if (list_.begin (), list_.end (), - [&] (const auto & cache) { - return cache->device == device; - }); - - std::shared_ptr cached; - if (it != list_.end ()) { - cached = *it; - list_.erase (it); - } else { - GST_WARNING ("Couldn't find device from cache"); - } - - if (cached && thread_) { - std::lock_guard tlk (thread_lock_); - token_++; - cached->token = token_; - to_remove_.push (std::move (cached)); - thread_cond_.notify_one (); - } - } - private: std::mutex lock_; - std::vector> list_; - std::mutex thread_lock_; - std::condition_variable thread_cond_; - std::thread *thread_ = nullptr; - std::queue> to_remove_; - std::mutex token_lock_; - std::condition_variable token_cond_; - guint64 token_ = 0; - guint64 token_synced_ = 0; + std::vector list_; }; /* *INDENT-ON* */ -void -gst_d3d12_init_background_thread (void) -{ - auto manager = DeviceCacheManager::GetInstance (); - manager->InitThread (); -} - -void -gst_d3d12_sync_background_thread (void) -{ - auto manager = DeviceCacheManager::GetInstance (); - manager->Sync (); -} - static gboolean gst_d3d12_device_enable_debug (void) { @@ -462,7 +375,14 @@ gst_d3d12_device_finalize (GObject * object) GST_DEBUG_OBJECT (self, "Finalize"); - /* Don't delete private struct. DeviceCacheManager will destroy it */ + guint64 luid = 0; + if (self->priv->inner) + luid = self->priv->inner->adapter_luid; + + delete self->priv; + + auto manager = DeviceCacheManager::GetInstance (); + manager->ReleaseDevice (luid); G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -471,8 +391,8 @@ static void gst_d3d12_device_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { - GstD3D12Device *self = GST_D3D12_DEVICE (object); - GstD3D12DevicePrivate *priv = self->priv; + auto self = GST_D3D12_DEVICE (object); + auto priv = self->priv->inner; switch (prop_id) { case PROP_ADAPTER_INDEX: @@ -500,7 +420,7 @@ static gboolean check_format_support (GstD3D12Device * self, DXGI_FORMAT format, guint flags, D3D12_FEATURE_DATA_FORMAT_SUPPORT * support) { - ID3D12Device *device = self->priv->device.Get (); + auto device = self->priv->inner->device; HRESULT hr; support->Format = format; @@ -526,7 +446,7 @@ check_format_support (GstD3D12Device * self, DXGI_FORMAT format, static void gst_d3d12_device_setup_format_table (GstD3D12Device * self) { - auto priv = self->priv; + auto priv = self->priv->inner; for (guint i = 0; i < GST_D3D12_N_FORMATS; i++) { const auto iter = &g_gst_d3d12_default_format_map[i]; @@ -722,9 +642,6 @@ gst_d3d12_device_new_internal (const GstD3D12DeviceConstructData * data) UINT factory_flags = 0; guint index = 0; - GST_DEBUG_CATEGORY_INIT (gst_d3d12_device_debug, - "d3d12device", 0, "d3d12 device object"); - gst_d3d12_device_enable_debug (); hr = CreateDXGIFactory2 (factory_flags, IID_PPV_ARGS (&factory)); @@ -753,9 +670,10 @@ gst_d3d12_device_new_internal (const GstD3D12DeviceConstructData * data) return nullptr; } - GstD3D12Device *self = (GstD3D12Device *) - g_object_new (GST_TYPE_D3D12_DEVICE, nullptr); - GstD3D12DevicePrivate *priv = self->priv; + auto self = (GstD3D12Device *) g_object_new (GST_TYPE_D3D12_DEVICE, nullptr); + gst_object_ref_sink (self); + self->priv->inner = std::make_shared < DeviceInner > (); + auto priv = self->priv->inner; priv->factory = factory; priv->adapter = adapter; @@ -835,7 +753,7 @@ gst_d3d12_device_new (guint adapter_index) data.data.index = adapter_index; data.type = GST_D3D12_DEVICE_CONSTRUCT_FOR_INDEX; - return manager->Create (&data); + return manager->GetDevice (&data); } GstD3D12Device * @@ -846,7 +764,7 @@ gst_d3d12_device_new_for_adapter_luid (gint64 adapter_luid) data.data.luid = adapter_luid; data.type = GST_D3D12_DEVICE_CONSTRUCT_FOR_LUID; - return manager->Create (&data); + return manager->GetDevice (&data); } ID3D12Device * @@ -854,7 +772,7 @@ gst_d3d12_device_get_device_handle (GstD3D12Device * device) { g_return_val_if_fail (GST_IS_D3D12_DEVICE (device), nullptr); - return device->priv->device.Get (); + return device->priv->inner->device.Get (); } IDXGIAdapter1 * @@ -862,7 +780,7 @@ gst_d3d12_device_get_adapter_handle (GstD3D12Device * device) { g_return_val_if_fail (GST_IS_D3D12_DEVICE (device), nullptr); - return device->priv->adapter.Get (); + return device->priv->inner->adapter.Get (); } IDXGIFactory2 * @@ -870,7 +788,7 @@ gst_d3d12_device_get_factory_handle (GstD3D12Device * device) { g_return_val_if_fail (GST_IS_D3D12_DEVICE (device), nullptr); - return device->priv->factory.Get (); + return device->priv->inner->factory.Get (); } gboolean @@ -880,7 +798,7 @@ gst_d3d12_device_get_d3d11on12_device (GstD3D12Device * device, g_return_val_if_fail (GST_IS_D3D12_DEVICE (device), FALSE); g_return_val_if_fail (d3d11on12, FALSE); - auto priv = device->priv; + auto priv = device->priv->inner; std::lock_guard < std::mutex > lk (priv->lock); if (!priv->d3d11on12) { @@ -905,7 +823,7 @@ gst_d3d12_device_lock (GstD3D12Device * device) { g_return_if_fail (GST_IS_D3D12_DEVICE (device)); - auto priv = device->priv; + auto priv = device->priv->inner; priv->extern_lock.lock (); } @@ -914,7 +832,7 @@ gst_d3d12_device_unlock (GstD3D12Device * device) { g_return_if_fail (GST_IS_D3D12_DEVICE (device)); - auto priv = device->priv; + auto priv = device->priv->inner; priv->extern_lock.unlock (); } @@ -925,7 +843,7 @@ gst_d3d12_device_get_format (GstD3D12Device * device, g_return_val_if_fail (GST_IS_D3D12_DEVICE (device), FALSE); g_return_val_if_fail (device_format != nullptr, FALSE); - auto priv = device->priv; + auto priv = device->priv->inner; const auto & target = priv->format_table.find (format); if (target == priv->format_table.end ()) return FALSE; @@ -942,7 +860,7 @@ gst_d3d12_device_get_command_queue (GstD3D12Device * device, { g_return_val_if_fail (GST_IS_D3D12_DEVICE (device), nullptr); - auto priv = device->priv; + auto priv = device->priv->inner; switch (queue_type) { case D3D12_COMMAND_LIST_TYPE_DIRECT: @@ -965,7 +883,7 @@ gst_d3d12_device_execute_command_lists (GstD3D12Device * device, { g_return_val_if_fail (GST_IS_D3D12_DEVICE (device), FALSE); - auto priv = device->priv; + auto priv = device->priv->inner; GstD3D12CommandQueue *queue; switch (queue_type) { @@ -992,7 +910,7 @@ gst_d3d12_device_get_completed_value (GstD3D12Device * device, { g_return_val_if_fail (GST_IS_D3D12_DEVICE (device), G_MAXUINT64); - auto priv = device->priv; + auto priv = device->priv->inner; GstD3D12CommandQueue *queue; switch (queue_type) { @@ -1018,7 +936,7 @@ gst_d3d12_device_set_fence_notify (GstD3D12Device * device, g_return_val_if_fail (GST_IS_D3D12_DEVICE (device), FALSE); g_return_val_if_fail (fence_data, FALSE); - auto priv = device->priv; + auto priv = device->priv->inner; GstD3D12CommandQueue *queue; switch (queue_type) { @@ -1046,7 +964,7 @@ gst_d3d12_device_fence_wait (GstD3D12Device * device, { g_return_val_if_fail (GST_IS_D3D12_DEVICE (device), FALSE); - auto priv = device->priv; + auto priv = device->priv->inner; GstD3D12CommandQueue *queue; switch (queue_type) { @@ -1077,7 +995,7 @@ gst_d3d12_device_copy_texture_region (GstD3D12Device * device, g_return_val_if_fail (args, FALSE); HRESULT hr; - auto priv = device->priv; + auto priv = device->priv->inner; GstD3D12CommandAllocatorPool *ca_pool; GstD3D12CommandAllocator *gst_ca = nullptr; GstD3D12CommandListPool *cl_pool; @@ -1186,11 +1104,10 @@ gst_d3d12_device_d3d12_debug (GstD3D12Device * device, const gchar * file, { g_return_if_fail (GST_IS_D3D12_DEVICE (device)); - if (!device->priv->info_queue) + auto priv = device->priv->inner; + if (!priv->info_queue) return; - GstD3D12DevicePrivate *priv = device->priv; - std::lock_guard < std::recursive_mutex > lk (priv->extern_lock); ID3D12InfoQueue *info_queue = priv->info_queue.Get (); UINT64 num_msg = info_queue->GetNumStoredMessages (); @@ -1233,7 +1150,7 @@ gst_d3d12_device_d3d12_debug (GstD3D12Device * device, const gchar * file, void gst_d3d12_device_clear_yuv_texture (GstD3D12Device * device, GstMemory * mem) { - auto priv = device->priv; + auto priv = device->priv->inner; auto dmem = GST_D3D12_MEMORY_CAST (mem); ComPtr < ID3D12DescriptorHeap > heap; diff --git a/subprojects/gst-plugins-bad/sys/d3d12/plugin.cpp b/subprojects/gst-plugins-bad/sys/d3d12/plugin.cpp index ef42cdb8b7..488635adfb 100644 --- a/subprojects/gst-plugins-bad/sys/d3d12/plugin.cpp +++ b/subprojects/gst-plugins-bad/sys/d3d12/plugin.cpp @@ -64,7 +64,6 @@ GST_DEBUG_CATEGORY (gst_d3d12_utils_debug); static void plugin_deinit (gpointer data) { - gst_d3d12_sync_background_thread (); } static gboolean