webview2: Add support for d3d12 interop

Enable shared copy to D3D12 resource

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6499>
This commit is contained in:
Seungha Yang 2024-04-01 22:19:21 +09:00
parent d00c26cdc8
commit bb268eafa1
4 changed files with 183 additions and 19 deletions

View file

@ -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;
}

View file

@ -22,6 +22,7 @@
#include <gst/gst.h>
#include <gst/video/video.h>
#include <gst/d3d11/gstd3d11.h>
#include <d3d11_4.h>
#include <string>
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);

View file

@ -32,6 +32,7 @@
#include "gstwebview2src.h"
#include "gstwebview2object.h"
#include <gst/d3d11/gstd3d11-private.h>
#include <gst/d3d12/gstd3d12.h>
#include <mutex>
#include <string>
#include <wrl.h>
@ -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<ID3D11Texture2D> staging;
/* D3D12 interop */
ComPtr<ID3D11Device5> device_5;
ComPtr<ID3D11DeviceContext4> context4;
ComPtr<ID3D11Fence> fence11;
ComPtr<ID3D12Fence> 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;

View file

@ -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,
)