From bb268eafa136146db140672ecb19dbf4bfa2cd38 Mon Sep 17 00:00:00 2001 From: Seungha Yang Date: Mon, 1 Apr 2024 22:19:21 +0900 Subject: [PATCH] webview2: Add support for d3d12 interop Enable shared copy to D3D12 resource Part-of: --- .../sys/webview2/gstwebview2object.cpp | 13 +- .../sys/webview2/gstwebview2object.h | 7 +- .../sys/webview2/gstwebview2src.cpp | 178 ++++++++++++++++-- .../gst-plugins-bad/sys/webview2/meson.build | 4 +- 4 files changed, 183 insertions(+), 19 deletions(-) diff --git a/subprojects/gst-plugins-bad/sys/webview2/gstwebview2object.cpp b/subprojects/gst-plugins-bad/sys/webview2/gstwebview2object.cpp index fc342278fa..90768e1999 100644 --- a/subprojects/gst-plugins-bad/sys/webview2/gstwebview2object.cpp +++ b/subprojects/gst-plugins-bad/sys/webview2/gstwebview2object.cpp @@ -1031,7 +1031,8 @@ gst_webview2_object_send_event (GstWebView2Object * object, GstEvent * event) GstFlowReturn gst_webview2_object_do_capture (GstWebView2Object * object, - ID3D11Texture2D * texture) + ID3D11Texture2D * texture, ID3D11DeviceContext4 * context4, + ID3D11Fence * fence, guint64 * fence_val, gboolean need_signal) { auto priv = object->priv; @@ -1067,6 +1068,16 @@ gst_webview2_object_do_capture (GstWebView2Object * object, context->CopySubresourceRegion (texture, 0, 0, 0, 0, priv->texture.Get (), 0, &box); + if (need_signal) { + auto fence_value = *fence_val + 1; + auto hr = context4->Signal (fence, fence_value); + if (!gst_d3d11_result (hr, priv->device)) { + GST_ERROR_OBJECT (object, "Signal failed"); + return GST_FLOW_ERROR; + } + + *fence_val = fence_value; + } return GST_FLOW_OK; } diff --git a/subprojects/gst-plugins-bad/sys/webview2/gstwebview2object.h b/subprojects/gst-plugins-bad/sys/webview2/gstwebview2object.h index 519dca5cb5..e6685e2b3e 100644 --- a/subprojects/gst-plugins-bad/sys/webview2/gstwebview2object.h +++ b/subprojects/gst-plugins-bad/sys/webview2/gstwebview2object.h @@ -22,6 +22,7 @@ #include #include #include +#include #include G_BEGIN_DECLS @@ -44,7 +45,11 @@ void gst_webview2_object_send_event (GstWebView2Object * client GstEvent * event); GstFlowReturn gst_webview2_object_do_capture (GstWebView2Object * client, - ID3D11Texture2D * texture); + ID3D11Texture2D * texture, + ID3D11DeviceContext4 * context4, + ID3D11Fence * fence, + guint64 * fence_val, + gboolean need_signal); void gst_webview2_object_set_flushing (GstWebView2Object * client, gboolean flushing); diff --git a/subprojects/gst-plugins-bad/sys/webview2/gstwebview2src.cpp b/subprojects/gst-plugins-bad/sys/webview2/gstwebview2src.cpp index 10e831ec96..b1265733de 100644 --- a/subprojects/gst-plugins-bad/sys/webview2/gstwebview2src.cpp +++ b/subprojects/gst-plugins-bad/sys/webview2/gstwebview2src.cpp @@ -32,6 +32,7 @@ #include "gstwebview2src.h" #include "gstwebview2object.h" #include +#include #include #include #include @@ -48,8 +49,10 @@ static GstStaticPadTemplate pad_template = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_ALWAYS, GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY, - "BGRA") ", pixel-aspect-ratio = 1/1;" GST_VIDEO_CAPS_MAKE ("BGRA") - ", pixel-aspect-ratio = 1/1")); + "BGRA") ", pixel-aspect-ratio = 1/1; " + GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_D3D12_MEMORY, + "BGRA") ", pixel-aspect-ratio = 1/1; " + GST_VIDEO_CAPS_MAKE ("BGRA") ", pixel-aspect-ratio = 1/1")); enum { @@ -67,7 +70,43 @@ enum /* *INDENT-OFF* */ struct GstWebView2SrcPrivate { + GstWebView2SrcPrivate () + { + event_handle = CreateEventEx (nullptr, nullptr, 0, EVENT_ALL_ACCESS); + } + + ~GstWebView2SrcPrivate () + { + ClearResource (); + gst_clear_object (&object); + gst_clear_object (&device12); + gst_clear_object (&device); + + CloseHandle (event_handle); + } + + void ClearResource () + { + if (fence12) { + auto completed = fence12->GetCompletedValue (); + if (completed < fence_val) { + auto hr = fence12->SetEventOnCompletion (fence_val, event_handle); + if (SUCCEEDED (hr)) + WaitForSingleObject (event_handle, INFINITE); + } + } + + staging = nullptr; + fence12 = nullptr; + fence11 = nullptr; + fence_val = 0; + context4 = nullptr; + device_5 = nullptr; + can_d3d12_copy = FALSE; + } + GstD3D11Device *device = nullptr; + GstD3D12Device *device12 = nullptr; GstWebView2Object *object = nullptr; @@ -77,6 +116,15 @@ struct GstWebView2SrcPrivate GstClockID clock_id = nullptr; ComPtr staging; + /* D3D12 interop */ + ComPtr device_5; + ComPtr context4; + ComPtr fence11; + ComPtr fence12; + gboolean can_d3d12_copy; + UINT64 fence_val = 0; + HANDLE event_handle; + /* properties */ gint adapter_index = DEFAULT_ADAPTER; std::string location = DEFAULT_LOCATION; @@ -322,15 +370,52 @@ gst_webview2_src_start (GstBaseSrc * src) { auto self = GST_WEBVIEW2_SRC (src); auto priv = self->priv; + auto elem = GST_ELEMENT_CAST (src); + HRESULT hr; GST_DEBUG_OBJECT (self, "Start"); - if (!gst_d3d11_ensure_element_data (GST_ELEMENT_CAST (self), - priv->adapter_index, &priv->device)) { - GST_ERROR_OBJECT (self, "Couldn't get D3D11 context"); + priv->ClearResource (); + + if (!gst_d3d11_ensure_element_data (elem, priv->adapter_index, &priv->device)) { + GST_ERROR_OBJECT (self, "Couldn't get D3D11 device"); return FALSE; } + gint64 luid; + g_object_get (priv->device, "adapter-luid", &luid, nullptr); + if (!gst_d3d12_ensure_element_data_for_adapter_luid (elem, + luid, &priv->device12)) { + GST_ERROR_OBJECT (self, "Couldn't get d3d12 device"); + return FALSE; + } + + auto device = gst_d3d11_device_get_device_handle (priv->device); + device->QueryInterface (IID_PPV_ARGS (&priv->device_5)); + + auto context = gst_d3d11_device_get_device_context_handle (priv->device); + context->QueryInterface (IID_PPV_ARGS (&priv->context4)); + + if (priv->device_5 && priv->context4) { + hr = priv->device_5->CreateFence (0, D3D11_FENCE_FLAG_SHARED, + IID_PPV_ARGS (&priv->fence11)); + if (gst_d3d11_result (hr, priv->device)) { + HANDLE fence_handle; + hr = priv->fence11->CreateSharedHandle (nullptr, GENERIC_ALL, nullptr, + &fence_handle); + if (gst_d3d11_result (hr, priv->device)) { + auto device12 = gst_d3d12_device_get_device_handle (priv->device12); + hr = device12->OpenSharedHandle (fence_handle, + IID_PPV_ARGS (&priv->fence12)); + CloseHandle (fence_handle); + if (gst_d3d12_result (hr, priv->device12)) + priv->can_d3d12_copy = TRUE; + } + } + } + + GST_DEBUG_OBJECT (self, "D3D12 copy support: %d", priv->can_d3d12_copy); + std::lock_guard < std::mutex > lk (priv->lock); priv->object = gst_webview2_object_new (priv->device); if (!priv->object) { @@ -355,10 +440,11 @@ gst_webview2_src_stop (GstBaseSrc * src) GST_DEBUG_OBJECT (self, "Stop"); - priv->staging = nullptr; + priv->ClearResource (); gst_clear_object (&priv->object); gst_clear_object (&priv->device); + gst_clear_object (&priv->device12); return TRUE; } @@ -494,19 +580,33 @@ gst_webview2_decide_allocation (GstBaseSrc * src, GstQuery * query) auto features = gst_caps_get_features (caps, 0); auto is_d3d11 = gst_caps_features_contains (features, GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY); + auto is_d3d12 = gst_caps_features_contains (features, + GST_CAPS_FEATURE_MEMORY_D3D12_MEMORY); if (pool) { - if (!GST_IS_D3D11_BUFFER_POOL (pool)) { - gst_clear_object (&pool); - } else { - auto d3d11_pool = GST_D3D11_BUFFER_POOL (pool); - if (d3d11_pool->device != priv->device) + if (is_d3d11) { + if (!GST_IS_D3D11_BUFFER_POOL (pool)) { gst_clear_object (&pool); + } else { + auto d3d11_pool = GST_D3D11_BUFFER_POOL (pool); + if (d3d11_pool->device != priv->device) + gst_clear_object (&pool); + } + } else if (is_d3d12) { + if (!GST_IS_D3D12_BUFFER_POOL (pool)) { + gst_clear_object (&pool); + } else { + auto d3d12_pool = GST_D3D12_BUFFER_POOL (pool); + if (!gst_d3d12_device_is_equal (d3d12_pool->device, priv->device12)) + gst_clear_object (&pool); + } } } if (!pool) { if (is_d3d11) pool = gst_d3d11_buffer_pool_new (priv->device); + else if (is_d3d12) + pool = gst_d3d12_buffer_pool_new (priv->device12); else pool = gst_video_buffer_pool_new (); } @@ -528,6 +628,23 @@ gst_webview2_decide_allocation (GstBaseSrc * src, GstQuery * query) gst_buffer_pool_config_set_d3d11_allocation_params (config, params); gst_d3d11_allocation_params_free (params); + } else if (is_d3d12) { + auto params = gst_buffer_pool_config_get_d3d12_allocation_params (config); + if (!params) { + params = gst_d3d12_allocation_params_new (priv->device12, &info, + GST_D3D12_ALLOCATION_FLAG_DEFAULT, + D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS | + D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET, D3D12_HEAP_FLAG_SHARED); + } else { + gst_d3d12_allocation_params_set_resource_flags (params, + D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS | + D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET); + gst_d3d12_allocation_params_set_heap_flags (params, + D3D12_HEAP_FLAG_SHARED); + } + + gst_buffer_pool_config_set_d3d12_allocation_params (config, params); + gst_d3d12_allocation_params_free (params); } if (!gst_buffer_pool_set_config (pool, config)) { @@ -642,8 +759,11 @@ gst_webview2_src_create (GstBaseSrc * src, guint64 offset, guint size, if (ret != GST_FLOW_OK) return ret; - ID3D11Texture2D *out_texture = nullptr; + auto device = gst_d3d11_device_get_device_handle (priv->device); + + ComPtr < ID3D11Texture2D > out_texture; gboolean system_copy = TRUE; + gboolean is_d3d12 = FALSE; GstMapInfo out_map; auto mem = gst_buffer_peek_memory (buffer, 0); if (gst_is_d3d11_memory (mem)) { @@ -659,11 +779,27 @@ gst_webview2_src_create (GstBaseSrc * src, guint64 offset, guint size, out_texture = (ID3D11Texture2D *) out_map.data; system_copy = FALSE; } + } else if (priv->can_d3d12_copy && gst_is_d3d12_memory (mem)) { + auto dmem = GST_D3D12_MEMORY_CAST (mem); + if (gst_d3d12_device_is_equal (dmem->device, priv->device12)) { + out_texture = gst_d3d12_memory_get_d3d11_texture (dmem, device); + if (out_texture) { + gst_d3d12_memory_sync (dmem); + + if (!gst_memory_map (mem, &out_map, GST_MAP_WRITE_D3D12)) { + GST_ERROR_OBJECT (self, "Couldn't map output d3d12 memory"); + gst_buffer_unref (buffer); + return GST_FLOW_ERROR; + } + + is_d3d12 = TRUE; + system_copy = FALSE; + } + } } if (!out_texture) { if (!priv->staging) { - auto device = gst_d3d11_device_get_device_handle (priv->device); D3D11_TEXTURE2D_DESC staging_desc = { }; staging_desc.Width = priv->info.width; staging_desc.Height = priv->info.height; @@ -683,13 +819,14 @@ gst_webview2_src_create (GstBaseSrc * src, guint64 offset, guint size, } } - out_texture = priv->staging.Get (); + out_texture = priv->staging; GST_TRACE_OBJECT (self, "Do CPU copy"); } else { GST_TRACE_OBJECT (self, "Do GPU copy"); } - ret = gst_webview2_object_do_capture (priv->object, out_texture); + ret = gst_webview2_object_do_capture (priv->object, out_texture.Get (), + priv->context4.Get (), priv->fence11.Get (), &priv->fence_val, is_d3d12); if (ret != GST_FLOW_OK) { if (!system_copy) gst_memory_unmap (mem, &out_map); @@ -699,6 +836,17 @@ gst_webview2_src_create (GstBaseSrc * src, guint64 offset, guint size, if (!system_copy) { gst_memory_unmap (mem, &out_map); + if (is_d3d12) { + auto cq = gst_d3d12_device_get_command_queue (priv->device12, + D3D12_COMMAND_LIST_TYPE_DIRECT); + gst_d3d12_command_queue_execute_wait (cq, + priv->fence12.Get (), priv->fence_val); + guint64 fence_val_12; + gst_d3d12_command_queue_execute_command_lists (cq, + 0, nullptr, &fence_val_12); + auto dmem = GST_D3D12_MEMORY_CAST (mem); + dmem->fence_value = fence_val_12; + } } else { auto context = gst_d3d11_device_get_device_context_handle (priv->device); D3D11_MAPPED_SUBRESOURCE map; diff --git a/subprojects/gst-plugins-bad/sys/webview2/meson.build b/subprojects/gst-plugins-bad/sys/webview2/meson.build index 4e57bd8460..1986bc6da3 100644 --- a/subprojects/gst-plugins-bad/sys/webview2/meson.build +++ b/subprojects/gst-plugins-bad/sys/webview2/meson.build @@ -11,7 +11,7 @@ if host_system != 'windows' or webview2_option.disabled() subdir_done() endif -if not gstd3d11_dep.found() +if not gstd3d11_dep.found() or not gstd3d12_dep.found() if webview2_option.enabled() error('The webview2 was enabled explicitly, but required dependencies were not found.') endif @@ -101,7 +101,7 @@ gstwebview2 = library('gstwebview2', cpp_args : gst_plugins_bad_args + extra_args, include_directories : [configinc], dependencies : [gstbase_dep, gstvideo_dep, coremessaging_lib, - gstd3d11_dep, runtimeobject_dep, dwmapi_lib, dcomp_lib] + sdk_deps, + gstd3d11_dep, gstd3d12_dep, runtimeobject_dep, dwmapi_lib, dcomp_lib] + sdk_deps, install : true, install_dir : plugins_install_dir, )