gstreamer/subprojects/gst-plugins-bad/tests/check/libs/d3d11device.cpp
Alexander Slobodeniuk 926d5366b9 d3d11device: protect device_lock vs device_new
It seems that when D3D11CreateDevice collides in time
with other D3D11 calls, in particular the proccess of
creating a shader, it can corrupt the memory in the driver.
D3D11 spec doesn't seem to require any thread safety from
D3D11CreateDevice. Following MSDN, it is supposed to be called
in the beginning of the proccess, while GStreamer calls it with each
new pipeline.
Such crashes in the driver were frequently reproducing on the
Intel UHD 630 machine.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6686>
2024-04-20 11:22:41 +00:00

280 lines
7.6 KiB
C++

/* GStreamer
* Copyright (C) 2021 Seungha Yang <seungha@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/gst.h>
#include <gst/check/gstcheck.h>
#include <gst/d3d11/gstd3d11.h>
#include <wrl.h>
/* *INDENT-OFF* */
using namespace Microsoft::WRL;
/* *INDENT-ON* */
static gboolean have_multiple_adapters = FALSE;
GST_START_TEST (test_device_new)
{
GstD3D11Device *device = nullptr;
guint adapter_index = G_MAXINT;
device = gst_d3d11_device_new (0, 0);
fail_unless (GST_IS_D3D11_DEVICE (device));
g_object_get (device, "adapter", &adapter_index, nullptr);
fail_unless_equals_int (adapter_index, 0);
gst_clear_object (&device);
if (have_multiple_adapters) {
device = gst_d3d11_device_new (1, 0);
fail_unless (GST_IS_D3D11_DEVICE (device));
g_object_get (device, "adapter", &adapter_index, nullptr);
fail_unless_equals_int (adapter_index, 1);
}
gst_clear_object (&device);
}
GST_END_TEST;
GST_START_TEST (test_device_for_adapter_luid)
{
GstD3D11Device *device = nullptr;
HRESULT hr;
ComPtr < IDXGIAdapter1 > adapter;
ComPtr < IDXGIFactory1 > factory;
DXGI_ADAPTER_DESC desc;
guint adapter_index = G_MAXINT;
gint64 adapter_luid = 0;
gint64 luid;
hr = CreateDXGIFactory1 (IID_PPV_ARGS (&factory));
if (SUCCEEDED (hr))
hr = factory->EnumAdapters1 (0, &adapter);
if (SUCCEEDED (hr))
hr = adapter->GetDesc (&desc);
if (SUCCEEDED (hr)) {
luid = gst_d3d11_luid_to_int64 (&desc.AdapterLuid);
device = gst_d3d11_device_new_for_adapter_luid (luid, 0);
fail_unless (GST_IS_D3D11_DEVICE (device));
g_object_get (device, "adapter", &adapter_index, "adapter-luid",
&adapter_luid, nullptr);
/* adapter_luid is corresponding to the first enumerated adapter,
* so adapter index should be zero here */
fail_unless_equals_int (adapter_index, 0);
fail_unless_equals_int64 (adapter_luid, luid);
}
gst_clear_object (&device);
adapter = nullptr;
if (have_multiple_adapters) {
if (SUCCEEDED (hr))
hr = factory->EnumAdapters1 (1, &adapter);
if (SUCCEEDED (hr))
hr = adapter->GetDesc (&desc);
if (SUCCEEDED (hr)) {
luid = gst_d3d11_luid_to_int64 (&desc.AdapterLuid);
device = gst_d3d11_device_new_for_adapter_luid (luid, 0);
fail_unless (GST_IS_D3D11_DEVICE (device));
g_object_get (device, "adapter", &adapter_index, "adapter-luid",
&adapter_luid, nullptr);
fail_unless_equals_int (adapter_index, 1);
fail_unless_equals_int64 (adapter_luid, luid);
}
}
gst_clear_object (&device);
}
GST_END_TEST;
GST_START_TEST (test_device_new_wrapped)
{
GstD3D11Device *device = nullptr;
GstD3D11Device *device_clone = nullptr;
ID3D11Device *device_handle, *device_handle_clone;
ID3D11DeviceContext *context_handle, *context_handle_clone;
guint adapter_index = 0;
guint index;
gint64 luid, luid_clone;
if (have_multiple_adapters)
adapter_index = 1;
device = gst_d3d11_device_new (adapter_index, 0);
fail_unless (GST_IS_D3D11_DEVICE (device));
device_handle = gst_d3d11_device_get_device_handle (device);
fail_unless (device_handle != nullptr);
context_handle = gst_d3d11_device_get_device_context_handle (device);
fail_unless (context_handle != nullptr);
g_object_get (device, "adapter", &index, "adapter-luid", &luid, nullptr);
fail_unless_equals_int (index, adapter_index);
device_clone = gst_d3d11_device_new_wrapped (device_handle);
fail_unless (GST_IS_D3D11_DEVICE (device_clone));
device_handle_clone = gst_d3d11_device_get_device_handle (device_clone);
fail_unless_equals_pointer (device_handle, device_handle_clone);
context_handle_clone =
gst_d3d11_device_get_device_context_handle (device_clone);
fail_unless_equals_pointer (context_handle, context_handle_clone);
g_object_get (device_clone,
"adapter", &index, "adapter-luid", &luid_clone, nullptr);
fail_unless_equals_int (index, adapter_index);
fail_unless_equals_int64 (luid, luid_clone);
gst_clear_object (&device);
gst_clear_object (&device_clone);
}
GST_END_TEST;
static gboolean
check_d3d11_available (void)
{
HRESULT hr;
ComPtr < IDXGIAdapter1 > adapter;
ComPtr < IDXGIFactory1 > factory;
hr = CreateDXGIFactory1 (IID_PPV_ARGS (&factory));
if (FAILED (hr))
return FALSE;
hr = factory->EnumAdapters1 (0, &adapter);
if (FAILED (hr))
return FALSE;
adapter = nullptr;
hr = factory->EnumAdapters1 (1, &adapter);
if (SUCCEEDED (hr))
have_multiple_adapters = TRUE;
return TRUE;
}
static gboolean stopping = FALSE;
static gpointer
test_device_new_concurrency_thread (gpointer)
{
GstVideoInfo in_info, out_info;
gst_video_info_set_format (&in_info, GST_VIDEO_FORMAT_I420, 320, 240);
gst_video_info_set_format (&out_info, GST_VIDEO_FORMAT_RGBx, 1920, 1080);
while (!g_atomic_int_get (&stopping)) {
GstD3D11Converter *converter;
GstD3D11Device *device = gst_d3d11_device_new (0, D3D11_CREATE_DEVICE_BGRA_SUPPORT);
gst_d3d11_device_lock (device);
converter = gst_d3d11_converter_new (device, &in_info, &out_info,
gst_structure_new ("converter-config",
GST_D3D11_CONVERTER_OPT_BACKEND, GST_TYPE_D3D11_CONVERTER_BACKEND,
GST_D3D11_CONVERTER_BACKEND_SHADER,
GST_D3D11_CONVERTER_OPT_GAMMA_MODE,
GST_TYPE_VIDEO_GAMMA_MODE, GST_VIDEO_GAMMA_MODE_NONE,
GST_D3D11_CONVERTER_OPT_PRIMARIES_MODE,
GST_TYPE_VIDEO_PRIMARIES_MODE, GST_VIDEO_PRIMARIES_MODE_NONE,
nullptr));
gst_d3d11_device_unlock (device);
g_usleep (g_random_int_range (10, 1000));
gst_d3d11_device_lock (device);
gst_object_unref (converter);
gst_d3d11_device_unlock (device);
gst_object_unref (device);
}
return NULL;
}
GST_START_TEST (test_device_new_concurrency)
{
#define NUM_THREADS 32
GThread *threads[NUM_THREADS];
for (int t = 0; t < NUM_THREADS; t++)
threads[t] = g_thread_new (NULL, test_device_new_concurrency_thread, NULL);
g_usleep (20 * G_TIME_SPAN_SECOND);
g_atomic_int_set (&stopping, TRUE);
for (int t = 0; t < NUM_THREADS; t++)
g_thread_join(threads[t]);
}
GST_END_TEST;
GST_START_TEST (test_device_lock_recursiveness)
{
GstD3D11Device *device = gst_d3d11_device_new (0, 0);
gst_d3d11_device_lock (device);
gst_d3d11_device_lock (device);
gst_d3d11_device_unlock (device);
gst_d3d11_device_unlock (device);
gst_object_unref (device);
}
GST_END_TEST;
static Suite *
d3d11device_suite (void)
{
Suite *s = suite_create ("d3d11device");
TCase *tc_basic = tcase_create ("general");
suite_add_tcase (s, tc_basic);
if (!check_d3d11_available ())
goto out;
tcase_add_test (tc_basic, test_device_new);
tcase_add_test (tc_basic, test_device_for_adapter_luid);
tcase_add_test (tc_basic, test_device_new_wrapped);
tcase_add_test (tc_basic, test_device_new_concurrency);
tcase_add_test (tc_basic, test_device_lock_recursiveness);
out:
return s;
}
GST_CHECK_MAIN (d3d11device);