mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-04 23:46:43 +00:00
0cb12db96c
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/6702>
1943 lines
52 KiB
C++
1943 lines
52 KiB
C++
/* GStreamer
|
|
* Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
|
|
* Copyright (C) 2020 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 "gstd3d11device.h"
|
|
#include "gstd3d11device-private.h"
|
|
#include "gstd3d11utils.h"
|
|
#include "gstd3d11format.h"
|
|
#include "gstd3d11-private.h"
|
|
#include "gstd3d11memory.h"
|
|
#include "gstd3d11compile.h"
|
|
#include "gstd3d11shadercache.h"
|
|
#include <gmodule.h>
|
|
#include <wrl.h>
|
|
|
|
#include <windows.h>
|
|
#include <versionhelpers.h>
|
|
#include <map>
|
|
#include <utility>
|
|
#include <atomic>
|
|
#include <mutex>
|
|
#include <string.h>
|
|
#include <unordered_map>
|
|
|
|
/**
|
|
* SECTION:gstd3d11device
|
|
* @title: GstD3D11Device
|
|
* @short_description: Direct3D11 device abstraction
|
|
*
|
|
* #GstD3D11Device wraps ID3D11Device and ID3D11DeviceContext for GPU resources
|
|
* to be able to be shared among various elements. Caller can get native
|
|
* Direct3D11 handles via getter method.
|
|
* Basically Direct3D11 API doesn't require dedicated thread like that of
|
|
* OpenGL context, and ID3D11Device APIs are supposed to be thread-safe.
|
|
* But concurrent call for ID3D11DeviceContext and DXGI API are not allowed.
|
|
* To protect such object, callers need to make use of gst_d3d11_device_lock()
|
|
* and gst_d3d11_device_unlock()
|
|
*/
|
|
|
|
/* *INDENT-OFF* */
|
|
using namespace Microsoft::WRL;
|
|
/* *INDENT-ON* */
|
|
|
|
#if HAVE_D3D11SDKLAYERS_H
|
|
#include <d3d11sdklayers.h>
|
|
|
|
/* mingw header does not define D3D11_RLDO_IGNORE_INTERNAL
|
|
* D3D11_RLDO_SUMMARY = 0x1,
|
|
D3D11_RLDO_DETAIL = 0x2,
|
|
* D3D11_RLDO_IGNORE_INTERNAL = 0x4
|
|
*/
|
|
#define GST_D3D11_RLDO_FLAGS (0x2 | 0x4)
|
|
#endif
|
|
|
|
#if HAVE_DXGIDEBUG_H
|
|
#include <dxgidebug.h>
|
|
typedef HRESULT (WINAPI * DXGIGetDebugInterface_t) (REFIID riid,
|
|
void **ppDebug);
|
|
static DXGIGetDebugInterface_t GstDXGIGetDebugInterface = nullptr;
|
|
|
|
#endif
|
|
|
|
#if (HAVE_D3D11SDKLAYERS_H || HAVE_DXGIDEBUG_H)
|
|
GST_DEBUG_CATEGORY_STATIC (gst_d3d11_debug_layer_debug);
|
|
#endif
|
|
GST_DEBUG_CATEGORY_STATIC (gst_d3d11_device_debug);
|
|
#define GST_CAT_DEFAULT gst_d3d11_device_debug
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_ADAPTER,
|
|
PROP_DEVICE_ID,
|
|
PROP_VENDOR_ID,
|
|
PROP_HARDWARE,
|
|
PROP_DESCRIPTION,
|
|
PROP_CREATE_FLAGS,
|
|
PROP_ADAPTER_LUID,
|
|
};
|
|
|
|
#define DEFAULT_ADAPTER 0
|
|
#define DEFAULT_CREATE_FLAGS 0
|
|
|
|
/* *INDENT-OFF* */
|
|
struct _GstD3D11DevicePrivate
|
|
{
|
|
guint adapter = 0;
|
|
guint device_id = 0;
|
|
guint vendor_id = 0;
|
|
gboolean hardware = 0;
|
|
gchar *description = nullptr;
|
|
guint create_flags = 0;
|
|
gint64 adapter_luid = 0;
|
|
|
|
ID3D11Device *device = nullptr;
|
|
ID3D11Device5 *device5 = nullptr;
|
|
ID3D11DeviceContext *device_context = nullptr;
|
|
ID3D11DeviceContext4 *device_context4 = nullptr;
|
|
|
|
ID3D11VideoDevice *video_device = nullptr;
|
|
ID3D11VideoContext *video_context = nullptr;
|
|
|
|
IDXGIFactory1 *factory = nullptr;
|
|
std::unordered_map<GstVideoFormat, GstD3D11Format> format_table;
|
|
|
|
std::recursive_mutex extern_lock;
|
|
std::mutex resource_lock;
|
|
|
|
LARGE_INTEGER frequency;
|
|
|
|
D3D_FEATURE_LEVEL feature_level;
|
|
std::map <gint64, ComPtr<ID3D11PixelShader>> ps_cache;
|
|
std::map <gint64,
|
|
std::pair<ComPtr<ID3D11VertexShader>, ComPtr<ID3D11InputLayout>>> vs_cache;
|
|
std::map <D3D11_FILTER, ComPtr<ID3D11SamplerState>> sampler_cache;
|
|
|
|
ID3D11RasterizerState *rs = nullptr;
|
|
ID3D11RasterizerState *rs_msaa = nullptr;
|
|
|
|
#if HAVE_D3D11SDKLAYERS_H
|
|
ID3D11Debug *d3d11_debug = nullptr;
|
|
ID3D11InfoQueue *d3d11_info_queue = nullptr;
|
|
#endif
|
|
|
|
#if HAVE_DXGIDEBUG_H
|
|
IDXGIDebug *dxgi_debug = nullptr;
|
|
IDXGIInfoQueue *dxgi_info_queue = nullptr;
|
|
#endif
|
|
};
|
|
/* *INDENT-ON* */
|
|
|
|
static void
|
|
debug_init_once (void)
|
|
{
|
|
GST_D3D11_CALL_ONCE_BEGIN {
|
|
GST_DEBUG_CATEGORY_INIT (gst_d3d11_device_debug,
|
|
"d3d11device", 0, "d3d11 device object");
|
|
#if defined(HAVE_D3D11SDKLAYERS_H) || defined(HAVE_DXGIDEBUG_H)
|
|
GST_DEBUG_CATEGORY_INIT (gst_d3d11_debug_layer_debug,
|
|
"d3d11debuglayer", 0, "native d3d11 and dxgi debug");
|
|
#endif
|
|
} GST_D3D11_CALL_ONCE_END;
|
|
}
|
|
|
|
#define gst_d3d11_device_parent_class parent_class
|
|
G_DEFINE_TYPE_WITH_CODE (GstD3D11Device, gst_d3d11_device, GST_TYPE_OBJECT,
|
|
debug_init_once ());
|
|
|
|
static void gst_d3d11_device_get_property (GObject * object, guint prop_id,
|
|
GValue * value, GParamSpec * pspec);
|
|
static void gst_d3d11_device_dispose (GObject * object);
|
|
static void gst_d3d11_device_finalize (GObject * object);
|
|
|
|
#if HAVE_D3D11SDKLAYERS_H
|
|
static gboolean
|
|
gst_d3d11_device_enable_d3d11_debug (void)
|
|
{
|
|
static GModule *d3d11_debug_module = nullptr;
|
|
/* If all below libraries are unavailable, d3d11 device would fail with
|
|
* D3D11_CREATE_DEVICE_DEBUG flag */
|
|
static const gchar *sdk_dll_names[] = {
|
|
"d3d11sdklayers.dll",
|
|
"d3d11_1sdklayers.dll",
|
|
"d3d11_2sdklayers.dll",
|
|
"d3d11_3sdklayers.dll",
|
|
};
|
|
|
|
GST_D3D11_CALL_ONCE_BEGIN {
|
|
for (guint i = 0; i < G_N_ELEMENTS (sdk_dll_names); i++) {
|
|
d3d11_debug_module = g_module_open (sdk_dll_names[i], G_MODULE_BIND_LAZY);
|
|
if (d3d11_debug_module)
|
|
return;
|
|
}
|
|
}
|
|
GST_D3D11_CALL_ONCE_END;
|
|
|
|
if (d3d11_debug_module)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static inline GstDebugLevel
|
|
d3d11_message_severity_to_gst (D3D11_MESSAGE_SEVERITY level)
|
|
{
|
|
switch (level) {
|
|
case D3D11_MESSAGE_SEVERITY_CORRUPTION:
|
|
case D3D11_MESSAGE_SEVERITY_ERROR:
|
|
return GST_LEVEL_ERROR;
|
|
case D3D11_MESSAGE_SEVERITY_WARNING:
|
|
return GST_LEVEL_WARNING;
|
|
case D3D11_MESSAGE_SEVERITY_INFO:
|
|
return GST_LEVEL_INFO;
|
|
case D3D11_MESSAGE_SEVERITY_MESSAGE:
|
|
return GST_LEVEL_DEBUG;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return GST_LEVEL_LOG;
|
|
}
|
|
|
|
void
|
|
gst_d3d11_device_d3d11_debug (GstD3D11Device * device,
|
|
const gchar * file, const gchar * function, gint line)
|
|
{
|
|
GstD3D11DevicePrivate *priv = device->priv;
|
|
D3D11_MESSAGE *msg;
|
|
SIZE_T msg_len = 0;
|
|
HRESULT hr;
|
|
UINT64 num_msg, i;
|
|
ID3D11InfoQueue *info_queue = priv->d3d11_info_queue;
|
|
|
|
if (!info_queue)
|
|
return;
|
|
|
|
num_msg = info_queue->GetNumStoredMessages ();
|
|
|
|
for (i = 0; i < num_msg; i++) {
|
|
GstDebugLevel level;
|
|
|
|
hr = info_queue->GetMessage (i, NULL, &msg_len);
|
|
|
|
if (FAILED (hr) || msg_len == 0) {
|
|
return;
|
|
}
|
|
|
|
msg = (D3D11_MESSAGE *) g_malloc0 (msg_len);
|
|
hr = info_queue->GetMessage (i, msg, &msg_len);
|
|
|
|
level = d3d11_message_severity_to_gst (msg->Severity);
|
|
if (msg->Category == D3D11_MESSAGE_CATEGORY_STATE_CREATION &&
|
|
level > GST_LEVEL_ERROR) {
|
|
/* Do not warn for live object, since there would be live object
|
|
* when ReportLiveDeviceObjects was called */
|
|
level = GST_LEVEL_INFO;
|
|
}
|
|
|
|
gst_debug_log (gst_d3d11_debug_layer_debug, level, file, function, line,
|
|
G_OBJECT (device), "D3D11InfoQueue: %s", msg->pDescription);
|
|
g_free (msg);
|
|
}
|
|
|
|
info_queue->ClearStoredMessages ();
|
|
|
|
return;
|
|
}
|
|
#else
|
|
void
|
|
gst_d3d11_device_d3d11_debug (GstD3D11Device * device,
|
|
const gchar * file, const gchar * function, gint line)
|
|
{
|
|
/* do nothing */
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#if HAVE_DXGIDEBUG_H
|
|
static gboolean
|
|
gst_d3d11_device_enable_dxgi_debug (void)
|
|
{
|
|
#if (!GST_D3D11_WINAPI_ONLY_APP)
|
|
static GModule *dxgi_debug_module = nullptr;
|
|
|
|
GST_D3D11_CALL_ONCE_BEGIN {
|
|
dxgi_debug_module = g_module_open ("dxgidebug.dll", G_MODULE_BIND_LAZY);
|
|
|
|
if (dxgi_debug_module)
|
|
g_module_symbol (dxgi_debug_module,
|
|
"DXGIGetDebugInterface", (gpointer *) & GstDXGIGetDebugInterface);
|
|
}
|
|
GST_D3D11_CALL_ONCE_END;
|
|
|
|
if (!GstDXGIGetDebugInterface)
|
|
return FALSE;
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static HRESULT
|
|
gst_d3d11_device_dxgi_get_device_interface (REFIID riid, void **debug)
|
|
{
|
|
#if (!GST_D3D11_WINAPI_ONLY_APP)
|
|
if (GstDXGIGetDebugInterface) {
|
|
return GstDXGIGetDebugInterface (riid, debug);
|
|
} else {
|
|
return E_NOINTERFACE;
|
|
}
|
|
#else
|
|
return DXGIGetDebugInterface1 (0, riid, debug);
|
|
#endif
|
|
}
|
|
|
|
static inline GstDebugLevel
|
|
dxgi_info_queue_message_severity_to_gst (DXGI_INFO_QUEUE_MESSAGE_SEVERITY level)
|
|
{
|
|
switch (level) {
|
|
case DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION:
|
|
case DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR:
|
|
return GST_LEVEL_ERROR;
|
|
case DXGI_INFO_QUEUE_MESSAGE_SEVERITY_WARNING:
|
|
return GST_LEVEL_WARNING;
|
|
case DXGI_INFO_QUEUE_MESSAGE_SEVERITY_INFO:
|
|
return GST_LEVEL_INFO;
|
|
case DXGI_INFO_QUEUE_MESSAGE_SEVERITY_MESSAGE:
|
|
return GST_LEVEL_DEBUG;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return GST_LEVEL_LOG;
|
|
}
|
|
|
|
void
|
|
gst_d3d11_device_dxgi_debug (GstD3D11Device * device,
|
|
const gchar * file, const gchar * function, gint line)
|
|
{
|
|
GstD3D11DevicePrivate *priv = device->priv;
|
|
DXGI_INFO_QUEUE_MESSAGE *msg;
|
|
SIZE_T msg_len = 0;
|
|
HRESULT hr;
|
|
UINT64 num_msg, i;
|
|
IDXGIInfoQueue *info_queue = priv->dxgi_info_queue;
|
|
|
|
if (!info_queue)
|
|
return;
|
|
|
|
num_msg = info_queue->GetNumStoredMessages (DXGI_DEBUG_ALL);
|
|
|
|
for (i = 0; i < num_msg; i++) {
|
|
GstDebugLevel level;
|
|
|
|
hr = info_queue->GetMessage (DXGI_DEBUG_ALL, i, NULL, &msg_len);
|
|
|
|
if (FAILED (hr) || msg_len == 0) {
|
|
return;
|
|
}
|
|
|
|
msg = (DXGI_INFO_QUEUE_MESSAGE *) g_malloc0 (msg_len);
|
|
hr = info_queue->GetMessage (DXGI_DEBUG_ALL, i, msg, &msg_len);
|
|
|
|
level = dxgi_info_queue_message_severity_to_gst (msg->Severity);
|
|
gst_debug_log (gst_d3d11_debug_layer_debug, level, file, function, line,
|
|
G_OBJECT (device), "DXGIInfoQueue: %s", msg->pDescription);
|
|
g_free (msg);
|
|
}
|
|
|
|
info_queue->ClearStoredMessages (DXGI_DEBUG_ALL);
|
|
|
|
return;
|
|
}
|
|
#else
|
|
void
|
|
gst_d3d11_device_dxgi_debug (GstD3D11Device * device,
|
|
const gchar * file, const gchar * function, gint line)
|
|
{
|
|
/* do nothing */
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
gst_d3d11_device_class_init (GstD3D11DeviceClass * klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
GParamFlags readable_flags =
|
|
(GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
|
|
|
|
gobject_class->get_property = gst_d3d11_device_get_property;
|
|
gobject_class->dispose = gst_d3d11_device_dispose;
|
|
gobject_class->finalize = gst_d3d11_device_finalize;
|
|
|
|
g_object_class_install_property (gobject_class, PROP_ADAPTER,
|
|
g_param_spec_uint ("adapter", "Adapter",
|
|
"DXGI Adapter index for creating device",
|
|
0, G_MAXUINT32, DEFAULT_ADAPTER, readable_flags));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_DEVICE_ID,
|
|
g_param_spec_uint ("device-id", "Device Id",
|
|
"DXGI Device ID", 0, G_MAXUINT32, 0, readable_flags));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_VENDOR_ID,
|
|
g_param_spec_uint ("vendor-id", "Vendor Id",
|
|
"DXGI Vendor ID", 0, G_MAXUINT32, 0, readable_flags));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_HARDWARE,
|
|
g_param_spec_boolean ("hardware", "Hardware",
|
|
"Whether hardware device or not", TRUE, readable_flags));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_DESCRIPTION,
|
|
g_param_spec_string ("description", "Description",
|
|
"Human readable device description", NULL, readable_flags));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_ADAPTER_LUID,
|
|
g_param_spec_int64 ("adapter-luid", "Adapter LUID",
|
|
"DXGI Adapter LUID (Locally Unique Identifier) of created device",
|
|
G_MININT64, G_MAXINT64, 0, readable_flags));
|
|
|
|
gst_d3d11_memory_init_once ();
|
|
}
|
|
|
|
static void
|
|
gst_d3d11_device_init (GstD3D11Device * self)
|
|
{
|
|
self->priv = new GstD3D11DevicePrivate ();
|
|
}
|
|
|
|
static gboolean
|
|
is_windows_8_or_greater (void)
|
|
{
|
|
static gboolean ret = FALSE;
|
|
|
|
GST_D3D11_CALL_ONCE_BEGIN {
|
|
#if (!GST_D3D11_WINAPI_ONLY_APP)
|
|
if (IsWindows8OrGreater ())
|
|
ret = TRUE;
|
|
#else
|
|
ret = TRUE;
|
|
#endif
|
|
} GST_D3D11_CALL_ONCE_END;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static guint
|
|
check_format_support (GstD3D11Device * self, DXGI_FORMAT format)
|
|
{
|
|
GstD3D11DevicePrivate *priv = self->priv;
|
|
ID3D11Device *handle = priv->device;
|
|
HRESULT hr;
|
|
UINT format_support;
|
|
|
|
hr = handle->CheckFormatSupport (format, &format_support);
|
|
if (FAILED (hr) || format_support == 0)
|
|
return 0;
|
|
|
|
return format_support;
|
|
}
|
|
|
|
static void
|
|
dump_format (GstD3D11Device * self, GstD3D11Format * format)
|
|
{
|
|
gchar *format_support_str = g_flags_to_string (GST_TYPE_D3D11_FORMAT_SUPPORT,
|
|
format->format_support[0]);
|
|
|
|
GST_LOG_OBJECT (self, "%s -> %s (%d), "
|
|
"resource format: %s (%d), %s (%d), %s (%d), %s (%d), flags (0x%x) %s",
|
|
gst_video_format_to_string (format->format),
|
|
gst_d3d11_dxgi_format_to_string (format->dxgi_format),
|
|
format->dxgi_format,
|
|
gst_d3d11_dxgi_format_to_string (format->resource_format[0]),
|
|
format->resource_format[0],
|
|
gst_d3d11_dxgi_format_to_string (format->resource_format[1]),
|
|
format->resource_format[1],
|
|
gst_d3d11_dxgi_format_to_string (format->resource_format[2]),
|
|
format->resource_format[2],
|
|
gst_d3d11_dxgi_format_to_string (format->resource_format[3]),
|
|
format->resource_format[3], format->format_support[0],
|
|
format_support_str);
|
|
|
|
g_free (format_support_str);
|
|
}
|
|
|
|
static void
|
|
gst_d3d11_device_setup_format_table (GstD3D11Device * self)
|
|
{
|
|
GstD3D11DevicePrivate *priv = self->priv;
|
|
|
|
for (guint i = 0; i < G_N_ELEMENTS (_gst_d3d11_default_format_map); i++) {
|
|
const GstD3D11Format *iter = &_gst_d3d11_default_format_map[i];
|
|
GstD3D11Format format;
|
|
guint support[GST_VIDEO_MAX_PLANES] = { 0, };
|
|
gboolean native = TRUE;
|
|
|
|
switch (iter->format) {
|
|
/* RGB/GRAY */
|
|
case GST_VIDEO_FORMAT_BGRA:
|
|
case GST_VIDEO_FORMAT_BGRx:
|
|
case GST_VIDEO_FORMAT_RGBA:
|
|
case GST_VIDEO_FORMAT_RGBx:
|
|
case GST_VIDEO_FORMAT_RGB10A2_LE:
|
|
case GST_VIDEO_FORMAT_RGBA64_LE:
|
|
case GST_VIDEO_FORMAT_GRAY8:
|
|
case GST_VIDEO_FORMAT_GRAY16_LE:
|
|
support[0] = check_format_support (self, iter->dxgi_format);
|
|
if (!support[0]) {
|
|
const gchar *format_name =
|
|
gst_d3d11_dxgi_format_to_string (iter->dxgi_format);
|
|
GST_INFO_OBJECT (self, "DXGI_FORMAT_%s (%d) for %s is not supported",
|
|
format_name, (guint) iter->dxgi_format,
|
|
gst_video_format_to_string (iter->format));
|
|
continue;
|
|
}
|
|
break;
|
|
/* YUV DXGI native formats */
|
|
case GST_VIDEO_FORMAT_VUYA:
|
|
case GST_VIDEO_FORMAT_Y410:
|
|
case GST_VIDEO_FORMAT_NV12:
|
|
case GST_VIDEO_FORMAT_P010_10LE:
|
|
case GST_VIDEO_FORMAT_P012_LE:
|
|
case GST_VIDEO_FORMAT_P016_LE:
|
|
case GST_VIDEO_FORMAT_YUY2:
|
|
case GST_VIDEO_FORMAT_Y210:
|
|
case GST_VIDEO_FORMAT_Y212_LE:
|
|
case GST_VIDEO_FORMAT_Y412_LE:
|
|
case GST_VIDEO_FORMAT_BGRA64_LE:
|
|
case GST_VIDEO_FORMAT_BGR10A2_LE:
|
|
case GST_VIDEO_FORMAT_RBGA:
|
|
{
|
|
gboolean supported = TRUE;
|
|
|
|
if (is_windows_8_or_greater ())
|
|
support[0] = check_format_support (self, iter->dxgi_format);
|
|
|
|
if (!support[0]) {
|
|
GST_DEBUG_OBJECT (self,
|
|
"DXGI_FORMAT_%s (%d) for %s is not supported, "
|
|
"checking resource format",
|
|
gst_d3d11_dxgi_format_to_string (iter->dxgi_format),
|
|
(guint) iter->dxgi_format,
|
|
gst_video_format_to_string (iter->format));
|
|
|
|
native = FALSE;
|
|
for (guint j = 0; j < GST_VIDEO_MAX_PLANES; j++) {
|
|
if (iter->resource_format[j] == DXGI_FORMAT_UNKNOWN)
|
|
break;
|
|
|
|
support[j] = check_format_support (self, iter->resource_format[j]);
|
|
if (support[j] == 0) {
|
|
supported = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!supported) {
|
|
GST_INFO_OBJECT (self, "%s is not supported",
|
|
gst_video_format_to_string (iter->format));
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
/* non-DXGI native formats */
|
|
case GST_VIDEO_FORMAT_NV21:
|
|
case GST_VIDEO_FORMAT_I420:
|
|
case GST_VIDEO_FORMAT_YV12:
|
|
case GST_VIDEO_FORMAT_I420_10LE:
|
|
case GST_VIDEO_FORMAT_I420_12LE:
|
|
case GST_VIDEO_FORMAT_Y42B:
|
|
case GST_VIDEO_FORMAT_I422_10LE:
|
|
case GST_VIDEO_FORMAT_I422_12LE:
|
|
case GST_VIDEO_FORMAT_Y444:
|
|
case GST_VIDEO_FORMAT_Y444_10LE:
|
|
case GST_VIDEO_FORMAT_Y444_12LE:
|
|
case GST_VIDEO_FORMAT_Y444_16LE:
|
|
case GST_VIDEO_FORMAT_AYUV:
|
|
case GST_VIDEO_FORMAT_AYUV64:
|
|
case GST_VIDEO_FORMAT_UYVY:
|
|
case GST_VIDEO_FORMAT_VYUY:
|
|
case GST_VIDEO_FORMAT_YVYU:
|
|
case GST_VIDEO_FORMAT_ARGB:
|
|
case GST_VIDEO_FORMAT_xRGB:
|
|
case GST_VIDEO_FORMAT_ABGR:
|
|
case GST_VIDEO_FORMAT_xBGR:
|
|
case GST_VIDEO_FORMAT_RGB:
|
|
case GST_VIDEO_FORMAT_BGR:
|
|
case GST_VIDEO_FORMAT_v210:
|
|
case GST_VIDEO_FORMAT_v216:
|
|
case GST_VIDEO_FORMAT_v308:
|
|
case GST_VIDEO_FORMAT_IYU2:
|
|
case GST_VIDEO_FORMAT_RGB16:
|
|
case GST_VIDEO_FORMAT_BGR16:
|
|
case GST_VIDEO_FORMAT_RGB15:
|
|
case GST_VIDEO_FORMAT_BGR15:
|
|
case GST_VIDEO_FORMAT_r210:
|
|
/* RGB planar formats */
|
|
case GST_VIDEO_FORMAT_RGBP:
|
|
case GST_VIDEO_FORMAT_BGRP:
|
|
case GST_VIDEO_FORMAT_GBR:
|
|
case GST_VIDEO_FORMAT_GBR_10LE:
|
|
case GST_VIDEO_FORMAT_GBR_12LE:
|
|
case GST_VIDEO_FORMAT_GBR_16LE:
|
|
case GST_VIDEO_FORMAT_GBRA:
|
|
case GST_VIDEO_FORMAT_GBRA_10LE:
|
|
case GST_VIDEO_FORMAT_GBRA_12LE:
|
|
{
|
|
gboolean supported = TRUE;
|
|
|
|
native = FALSE;
|
|
for (guint j = 0; j < GST_VIDEO_MAX_PLANES; j++) {
|
|
if (iter->resource_format[j] == DXGI_FORMAT_UNKNOWN)
|
|
break;
|
|
|
|
support[j] = check_format_support (self, iter->resource_format[j]);
|
|
if (support[j] == 0) {
|
|
supported = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!supported) {
|
|
GST_INFO_OBJECT (self, "%s is not supported",
|
|
gst_video_format_to_string (iter->format));
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
g_assert_not_reached ();
|
|
return;
|
|
}
|
|
|
|
format = *iter;
|
|
|
|
if (!native)
|
|
format.dxgi_format = DXGI_FORMAT_UNKNOWN;
|
|
|
|
for (guint j = 0; j < GST_VIDEO_MAX_PLANES; j++)
|
|
format.format_support[j] = support[j];
|
|
|
|
#ifndef GST_DISABLE_GST_DEBUG
|
|
if (gst_debug_category_get_threshold (GST_CAT_DEFAULT) >= GST_LEVEL_LOG)
|
|
dump_format (self, &format);
|
|
#endif
|
|
|
|
priv->format_table[format.format] = format;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_d3d11_device_get_property (GObject * object, guint prop_id,
|
|
GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstD3D11Device *self = GST_D3D11_DEVICE (object);
|
|
GstD3D11DevicePrivate *priv = self->priv;
|
|
|
|
switch (prop_id) {
|
|
case PROP_ADAPTER:
|
|
g_value_set_uint (value, priv->adapter);
|
|
break;
|
|
case PROP_DEVICE_ID:
|
|
g_value_set_uint (value, priv->device_id);
|
|
break;
|
|
case PROP_VENDOR_ID:
|
|
g_value_set_uint (value, priv->vendor_id);
|
|
break;
|
|
case PROP_HARDWARE:
|
|
g_value_set_boolean (value, priv->hardware);
|
|
break;
|
|
case PROP_DESCRIPTION:
|
|
g_value_set_string (value, priv->description);
|
|
break;
|
|
case PROP_ADAPTER_LUID:
|
|
g_value_set_int64 (value, priv->adapter_luid);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
gst_d3d11_device_log_live_objects (GstD3D11Device * device,
|
|
const gchar * file, const gchar * function, gint line)
|
|
{
|
|
#if HAVE_D3D11SDKLAYERS_H
|
|
if (device->priv->d3d11_debug) {
|
|
device->priv->d3d11_debug->ReportLiveDeviceObjects ((D3D11_RLDO_FLAGS)
|
|
GST_D3D11_RLDO_FLAGS);
|
|
}
|
|
|
|
if (device->priv->d3d11_info_queue)
|
|
gst_d3d11_device_d3d11_debug (device, file, function, line);
|
|
#endif
|
|
|
|
#if HAVE_DXGIDEBUG_H
|
|
if (device->priv->dxgi_debug) {
|
|
device->priv->dxgi_debug->ReportLiveObjects (DXGI_DEBUG_ALL,
|
|
(DXGI_DEBUG_RLO_FLAGS) GST_D3D11_RLDO_FLAGS);
|
|
}
|
|
|
|
if (device->priv->dxgi_info_queue)
|
|
gst_d3d11_device_dxgi_debug (device, file, function, line);
|
|
#endif
|
|
}
|
|
|
|
static SRWLOCK _device_creation_rwlock = SRWLOCK_INIT;
|
|
|
|
static void
|
|
gst_d3d11_device_dispose (GObject * object)
|
|
{
|
|
GstD3D11Device *self = GST_D3D11_DEVICE (object);
|
|
GstD3D11DevicePrivate *priv = self->priv;
|
|
|
|
GST_LOG_OBJECT (self, "dispose");
|
|
|
|
AcquireSRWLockExclusive (&_device_creation_rwlock);
|
|
priv->ps_cache.clear ();
|
|
priv->vs_cache.clear ();
|
|
priv->sampler_cache.clear ();
|
|
|
|
GST_D3D11_CLEAR_COM (priv->rs);
|
|
GST_D3D11_CLEAR_COM (priv->rs_msaa);
|
|
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);
|
|
GST_D3D11_CLEAR_COM (priv->device_context);
|
|
GST_D3D11_CLEAR_COM (priv->factory);
|
|
gst_d3d11_device_log_live_objects (self, __FILE__, GST_FUNCTION, __LINE__);
|
|
|
|
#if HAVE_D3D11SDKLAYERS_H
|
|
GST_D3D11_CLEAR_COM (priv->d3d11_debug);
|
|
GST_D3D11_CLEAR_COM (priv->d3d11_info_queue);
|
|
#endif
|
|
|
|
#if HAVE_DXGIDEBUG_H
|
|
GST_D3D11_CLEAR_COM (priv->dxgi_debug);
|
|
GST_D3D11_CLEAR_COM (priv->dxgi_info_queue);
|
|
#endif
|
|
|
|
ReleaseSRWLockExclusive (&_device_creation_rwlock);
|
|
G_OBJECT_CLASS (parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
gst_d3d11_device_finalize (GObject * object)
|
|
{
|
|
GstD3D11Device *self = GST_D3D11_DEVICE (object);
|
|
GstD3D11DevicePrivate *priv = self->priv;
|
|
|
|
GST_LOG_OBJECT (self, "finalize");
|
|
|
|
g_free (priv->description);
|
|
|
|
delete priv;
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
}
|
|
|
|
typedef enum
|
|
{
|
|
DEVICE_CONSTRUCT_FOR_ADAPTER_INDEX,
|
|
DEVICE_CONSTRUCT_FOR_ADAPTER_LUID,
|
|
DEVICE_CONSTRUCT_WRAPPED,
|
|
} GstD3D11DeviceConstructType;
|
|
|
|
typedef struct _GstD3D11DeviceConstructData
|
|
{
|
|
union
|
|
{
|
|
guint adapter_index;
|
|
gint64 adapter_luid;
|
|
ID3D11Device *device;
|
|
} data;
|
|
GstD3D11DeviceConstructType type;
|
|
UINT create_flags;
|
|
} GstD3D11DeviceConstructData;
|
|
|
|
static HRESULT
|
|
_gst_d3d11_device_get_adapter (const GstD3D11DeviceConstructData * data,
|
|
IDXGIFactory1 * factory, guint * index, DXGI_ADAPTER_DESC * adapter_desc,
|
|
IDXGIAdapter1 ** dxgi_adapter)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ComPtr < IDXGIAdapter1 > adapter1;
|
|
DXGI_ADAPTER_DESC desc;
|
|
|
|
switch (data->type) {
|
|
case DEVICE_CONSTRUCT_FOR_ADAPTER_INDEX:
|
|
{
|
|
hr = factory->EnumAdapters1 (data->data.adapter_index, &adapter1);
|
|
if (FAILED (hr))
|
|
return hr;
|
|
|
|
hr = adapter1->GetDesc (&desc);
|
|
if (FAILED (hr))
|
|
return hr;
|
|
|
|
*index = data->data.adapter_index;
|
|
*adapter_desc = desc;
|
|
*dxgi_adapter = adapter1.Detach ();
|
|
|
|
return S_OK;
|
|
}
|
|
case DEVICE_CONSTRUCT_FOR_ADAPTER_LUID:
|
|
{
|
|
for (guint i = 0;; i++) {
|
|
gint64 luid;
|
|
|
|
adapter1 = nullptr;
|
|
|
|
hr = factory->EnumAdapters1 (i, &adapter1);
|
|
if (FAILED (hr))
|
|
return hr;
|
|
|
|
hr = adapter1->GetDesc (&desc);
|
|
if (FAILED (hr))
|
|
continue;
|
|
|
|
luid = gst_d3d11_luid_to_int64 (&desc.AdapterLuid);
|
|
if (luid != data->data.adapter_luid)
|
|
continue;
|
|
|
|
*index = i;
|
|
*adapter_desc = desc;
|
|
*dxgi_adapter = adapter1.Detach ();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
return E_FAIL;
|
|
}
|
|
case DEVICE_CONSTRUCT_WRAPPED:
|
|
{
|
|
ComPtr < IDXGIDevice > dxgi_device;
|
|
ComPtr < IDXGIAdapter > adapter;
|
|
ID3D11Device *device = data->data.device;
|
|
|
|
hr = device->QueryInterface (IID_PPV_ARGS (&dxgi_device));
|
|
if (FAILED (hr))
|
|
return hr;
|
|
|
|
hr = dxgi_device->GetAdapter (&adapter);
|
|
if (FAILED (hr))
|
|
return hr;
|
|
|
|
hr = adapter.As (&adapter1);
|
|
if (FAILED (hr))
|
|
return hr;
|
|
|
|
hr = adapter1->GetDesc (&desc);
|
|
if (FAILED (hr))
|
|
return hr;
|
|
|
|
auto luid = gst_d3d11_luid_to_int64 (&desc.AdapterLuid);
|
|
|
|
for (guint i = 0;; i++) {
|
|
DXGI_ADAPTER_DESC tmp_desc;
|
|
ComPtr < IDXGIAdapter1 > tmp;
|
|
|
|
hr = factory->EnumAdapters1 (i, &tmp);
|
|
if (FAILED (hr))
|
|
return hr;
|
|
|
|
hr = tmp->GetDesc (&tmp_desc);
|
|
if (FAILED (hr))
|
|
continue;
|
|
|
|
if (luid != gst_d3d11_luid_to_int64 (&tmp_desc.AdapterLuid))
|
|
continue;
|
|
|
|
*index = i;
|
|
*adapter_desc = desc;
|
|
*dxgi_adapter = adapter1.Detach ();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
return E_FAIL;
|
|
}
|
|
default:
|
|
g_assert_not_reached ();
|
|
break;
|
|
}
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
static void
|
|
gst_d3d11_device_setup_debug_layer (GstD3D11Device * self)
|
|
{
|
|
#if HAVE_DXGIDEBUG_H
|
|
if (gst_debug_category_get_threshold (gst_d3d11_debug_layer_debug) >
|
|
GST_LEVEL_ERROR) {
|
|
GstD3D11DevicePrivate *priv = self->priv;
|
|
|
|
if (gst_d3d11_device_enable_dxgi_debug ()) {
|
|
IDXGIDebug *debug = nullptr;
|
|
IDXGIInfoQueue *info_queue = nullptr;
|
|
HRESULT hr;
|
|
|
|
GST_CAT_INFO_OBJECT (gst_d3d11_debug_layer_debug, self,
|
|
"dxgi debug library was loaded");
|
|
hr = gst_d3d11_device_dxgi_get_device_interface (IID_PPV_ARGS (&debug));
|
|
|
|
if (SUCCEEDED (hr)) {
|
|
GST_CAT_INFO_OBJECT (gst_d3d11_debug_layer_debug, self,
|
|
"IDXGIDebug interface available");
|
|
priv->dxgi_debug = debug;
|
|
|
|
hr = gst_d3d11_device_dxgi_get_device_interface (IID_PPV_ARGS
|
|
(&info_queue));
|
|
if (SUCCEEDED (hr)) {
|
|
GST_CAT_INFO_OBJECT (gst_d3d11_debug_layer_debug, self,
|
|
"IDXGIInfoQueue interface available");
|
|
priv->dxgi_info_queue = info_queue;
|
|
}
|
|
}
|
|
} else {
|
|
GST_CAT_INFO_OBJECT (gst_d3d11_debug_layer_debug, self,
|
|
"couldn't load dxgi debug library");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if HAVE_D3D11SDKLAYERS_H
|
|
if ((self->priv->create_flags & D3D11_CREATE_DEVICE_DEBUG) != 0) {
|
|
GstD3D11DevicePrivate *priv = self->priv;
|
|
ID3D11Debug *debug;
|
|
ID3D11InfoQueue *info_queue;
|
|
HRESULT hr;
|
|
|
|
hr = priv->device->QueryInterface (IID_PPV_ARGS (&debug));
|
|
|
|
if (SUCCEEDED (hr)) {
|
|
GST_CAT_INFO_OBJECT (gst_d3d11_debug_layer_debug, self,
|
|
"D3D11Debug interface available");
|
|
priv->d3d11_debug = debug;
|
|
|
|
hr = priv->device->QueryInterface (IID_PPV_ARGS (&info_queue));
|
|
if (SUCCEEDED (hr)) {
|
|
GST_CAT_INFO_OBJECT (gst_d3d11_debug_layer_debug, self,
|
|
"ID3D11InfoQueue interface available");
|
|
priv->d3d11_info_queue = info_queue;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static GstD3D11Device *
|
|
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;
|
|
DXGI_ADAPTER_DESC adapter_desc;
|
|
static const D3D_FEATURE_LEVEL feature_levels[] = {
|
|
D3D_FEATURE_LEVEL_11_1,
|
|
D3D_FEATURE_LEVEL_11_0,
|
|
D3D_FEATURE_LEVEL_10_1,
|
|
D3D_FEATURE_LEVEL_10_0,
|
|
};
|
|
D3D_FEATURE_LEVEL selected_level;
|
|
|
|
debug_init_once ();
|
|
|
|
GstD3D11SRWLockGuard lk (&_device_creation_rwlock);
|
|
hr = CreateDXGIFactory1 (IID_PPV_ARGS (&factory));
|
|
if (!gst_d3d11_result (hr, NULL)) {
|
|
GST_WARNING ("cannot create dxgi factory, hr: 0x%x", (guint) hr);
|
|
return nullptr;
|
|
}
|
|
|
|
create_flags = 0;
|
|
if (data->type != DEVICE_CONSTRUCT_WRAPPED) {
|
|
create_flags = data->create_flags;
|
|
#if HAVE_D3D11SDKLAYERS_H
|
|
if (gst_debug_category_get_threshold (gst_d3d11_debug_layer_debug) >
|
|
GST_LEVEL_ERROR) {
|
|
/* DirectX SDK should be installed on system for this */
|
|
if (gst_d3d11_device_enable_d3d11_debug ()) {
|
|
GST_CAT_INFO (gst_d3d11_debug_layer_debug,
|
|
"d3d11 debug library was loaded");
|
|
create_flags |= D3D11_CREATE_DEVICE_DEBUG;
|
|
} else {
|
|
GST_CAT_INFO (gst_d3d11_debug_layer_debug,
|
|
"couldn't load d3d11 debug library");
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Ensure valid device handle */
|
|
if (data->type == DEVICE_CONSTRUCT_WRAPPED) {
|
|
ID3D11Device *external_device = data->data.device;
|
|
|
|
hr = external_device->QueryInterface (IID_PPV_ARGS (&device));
|
|
if (FAILED (hr)) {
|
|
GST_WARNING ("Not a valid external ID3D11Device handle");
|
|
return nullptr;
|
|
}
|
|
|
|
selected_level = device->GetFeatureLevel ();
|
|
if (selected_level < D3D_FEATURE_LEVEL_10_0) {
|
|
GST_ERROR ("Feature level 0x%x is not supported", (guint) selected_level);
|
|
return nullptr;
|
|
}
|
|
|
|
device->GetImmediateContext (&device_context);
|
|
}
|
|
|
|
hr = _gst_d3d11_device_get_adapter (data, factory.Get (), &adapter_index,
|
|
&adapter_desc, &adapter);
|
|
if (FAILED (hr)) {
|
|
GST_INFO ("Failed to get DXGI adapter");
|
|
return nullptr;
|
|
}
|
|
|
|
if (data->type != DEVICE_CONSTRUCT_WRAPPED) {
|
|
hr = D3D11CreateDevice (adapter.Get (), D3D_DRIVER_TYPE_UNKNOWN,
|
|
NULL, create_flags, feature_levels, G_N_ELEMENTS (feature_levels),
|
|
D3D11_SDK_VERSION, &device, &selected_level, &device_context);
|
|
|
|
if (FAILED (hr)) {
|
|
/* Retry if the system could not recognize D3D_FEATURE_LEVEL_11_1 */
|
|
hr = D3D11CreateDevice (adapter.Get (), D3D_DRIVER_TYPE_UNKNOWN,
|
|
NULL, create_flags, &feature_levels[1],
|
|
G_N_ELEMENTS (feature_levels) - 1, D3D11_SDK_VERSION, &device,
|
|
&selected_level, &device_context);
|
|
}
|
|
|
|
/* if D3D11_CREATE_DEVICE_DEBUG was enabled but couldn't create device,
|
|
* try it without the flag again */
|
|
if (FAILED (hr) && (create_flags & D3D11_CREATE_DEVICE_DEBUG) != 0) {
|
|
create_flags &= ~D3D11_CREATE_DEVICE_DEBUG;
|
|
|
|
hr = D3D11CreateDevice (adapter.Get (), D3D_DRIVER_TYPE_UNKNOWN,
|
|
NULL, create_flags, feature_levels, G_N_ELEMENTS (feature_levels),
|
|
D3D11_SDK_VERSION, &device, &selected_level, &device_context);
|
|
|
|
if (FAILED (hr)) {
|
|
/* Retry if the system could not recognize D3D_FEATURE_LEVEL_11_1 */
|
|
hr = D3D11CreateDevice (adapter.Get (), D3D_DRIVER_TYPE_UNKNOWN,
|
|
NULL, create_flags, &feature_levels[1],
|
|
G_N_ELEMENTS (feature_levels) - 1, D3D11_SDK_VERSION, &device,
|
|
&selected_level, &device_context);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FAILED (hr)) {
|
|
switch (data->type) {
|
|
case DEVICE_CONSTRUCT_FOR_ADAPTER_INDEX:
|
|
{
|
|
GST_INFO ("Failed to create d3d11 device for adapter index %d"
|
|
" with flags 0x%x, hr: 0x%x", data->data.adapter_index,
|
|
create_flags, (guint) hr);
|
|
return nullptr;
|
|
}
|
|
case DEVICE_CONSTRUCT_FOR_ADAPTER_LUID:
|
|
{
|
|
GST_WARNING ("Failed to create d3d11 device for adapter luid %"
|
|
G_GINT64_FORMAT " with flags 0x%x, hr: 0x%x",
|
|
data->data.adapter_luid, create_flags, (guint) hr);
|
|
return nullptr;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
GstD3D11Device *self = nullptr;
|
|
GstD3D11DevicePrivate *priv;
|
|
|
|
self = (GstD3D11Device *) g_object_new (GST_TYPE_D3D11_DEVICE, nullptr);
|
|
gst_object_ref_sink (self);
|
|
|
|
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 ();
|
|
priv->factory = factory.Detach ();
|
|
|
|
priv->vendor_id = adapter_desc.VendorId;
|
|
priv->device_id = adapter_desc.DeviceId;
|
|
priv->description = g_utf16_to_utf8 ((gunichar2 *) adapter_desc.Description,
|
|
-1, nullptr, nullptr, nullptr);
|
|
priv->adapter_luid = gst_d3d11_luid_to_int64 (&adapter_desc.AdapterLuid);
|
|
priv->feature_level = priv->device->GetFeatureLevel ();
|
|
|
|
DXGI_ADAPTER_DESC1 desc1;
|
|
hr = adapter->GetDesc1 (&desc1);
|
|
|
|
/* DXGI_ADAPTER_FLAG_SOFTWARE is missing in dxgi.h of mingw */
|
|
if (SUCCEEDED (hr) && (desc1.Flags & 0x2) != 0x2)
|
|
priv->hardware = TRUE;
|
|
|
|
priv->create_flags = create_flags;
|
|
gst_d3d11_device_setup_format_table (self);
|
|
gst_d3d11_device_setup_debug_layer (self);
|
|
|
|
BOOL ret = QueryPerformanceFrequency (&priv->frequency);
|
|
g_assert (ret);
|
|
|
|
return self;
|
|
}
|
|
|
|
/**
|
|
* gst_d3d11_device_new:
|
|
* @adapter_index: the index of adapter for creating d3d11 device
|
|
* @flags: a D3D11_CREATE_DEVICE_FLAG value used for creating d3d11 device
|
|
*
|
|
* Returns: (transfer full) (nullable): a new #GstD3D11Device for @adapter_index
|
|
* or %NULL when failed to create D3D11 device with given adapter index.
|
|
*
|
|
* Since: 1.22
|
|
*/
|
|
GstD3D11Device *
|
|
gst_d3d11_device_new (guint adapter_index, guint flags)
|
|
{
|
|
GstD3D11DeviceConstructData data;
|
|
|
|
data.data.adapter_index = adapter_index;
|
|
data.type = DEVICE_CONSTRUCT_FOR_ADAPTER_INDEX;
|
|
data.create_flags = flags;
|
|
|
|
return gst_d3d11_device_new_internal (&data);
|
|
}
|
|
|
|
/**
|
|
* gst_d3d11_device_new_for_adapter_luid:
|
|
* @adapter_luid: an int64 representation of the DXGI adapter LUID
|
|
* @flags: a D3D11_CREATE_DEVICE_FLAG value used for creating d3d11 device
|
|
*
|
|
* Returns: (transfer full) (nullable): a new #GstD3D11Device for @adapter_luid
|
|
* or %NULL when failed to create D3D11 device with given adapter luid.
|
|
*
|
|
* Since: 1.22
|
|
*/
|
|
GstD3D11Device *
|
|
gst_d3d11_device_new_for_adapter_luid (gint64 adapter_luid, guint flags)
|
|
{
|
|
GstD3D11DeviceConstructData data;
|
|
|
|
data.data.adapter_luid = adapter_luid;
|
|
data.type = DEVICE_CONSTRUCT_FOR_ADAPTER_LUID;
|
|
data.create_flags = flags;
|
|
|
|
return gst_d3d11_device_new_internal (&data);
|
|
}
|
|
|
|
/**
|
|
* gst_d3d11_device_new_wrapped:
|
|
* @device: (transfer none): an existing ID3D11Device handle
|
|
*
|
|
* Returns: (transfer full) (nullable): a new #GstD3D11Device for @device
|
|
* or %NULL if an error occurred
|
|
*
|
|
* Since: 1.22
|
|
*/
|
|
GstD3D11Device *
|
|
gst_d3d11_device_new_wrapped (ID3D11Device * device)
|
|
{
|
|
GstD3D11DeviceConstructData data;
|
|
|
|
g_return_val_if_fail (device != nullptr, nullptr);
|
|
|
|
data.data.device = device;
|
|
data.type = DEVICE_CONSTRUCT_WRAPPED;
|
|
data.create_flags = 0;
|
|
|
|
return gst_d3d11_device_new_internal (&data);
|
|
}
|
|
|
|
/**
|
|
* gst_d3d11_device_get_device_handle:
|
|
* @device: a #GstD3D11Device
|
|
*
|
|
* Used for various D3D11 APIs directly. Caller must not destroy returned device
|
|
* object.
|
|
*
|
|
* Returns: (transfer none): the ID3D11Device handle
|
|
*
|
|
* Since: 1.22
|
|
*/
|
|
ID3D11Device *
|
|
gst_d3d11_device_get_device_handle (GstD3D11Device * device)
|
|
{
|
|
g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
|
|
|
|
return device->priv->device;
|
|
}
|
|
|
|
/**
|
|
* gst_d3d11_device_get_device_context_handle:
|
|
* @device: a #GstD3D11Device
|
|
*
|
|
* Used for various D3D11 APIs directly. Caller must not destroy returned device
|
|
* object. Any ID3D11DeviceContext call needs to be protected by
|
|
* gst_d3d11_device_lock() and gst_d3d11_device_unlock() method.
|
|
*
|
|
* Returns: (transfer none): the immeidate ID3D11DeviceContext handle
|
|
*
|
|
* Since: 1.22
|
|
*/
|
|
ID3D11DeviceContext *
|
|
gst_d3d11_device_get_device_context_handle (GstD3D11Device * device)
|
|
{
|
|
g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
|
|
|
|
return device->priv->device_context;
|
|
}
|
|
|
|
/**
|
|
* gst_d3d11_device_get_dxgi_factory_handle:
|
|
* @device: a #GstD3D11Device
|
|
*
|
|
* Used for various D3D11 APIs directly. Caller must not destroy returned device
|
|
* object.
|
|
*
|
|
* Returns: (transfer none): the IDXGIFactory1 handle
|
|
*
|
|
* Since: 1.22
|
|
*/
|
|
IDXGIFactory1 *
|
|
gst_d3d11_device_get_dxgi_factory_handle (GstD3D11Device * device)
|
|
{
|
|
g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
|
|
|
|
return device->priv->factory;
|
|
}
|
|
|
|
/**
|
|
* gst_d3d11_device_get_video_device_handle:
|
|
* @device: a #GstD3D11Device
|
|
*
|
|
* Used for various D3D11 APIs directly. Caller must not destroy returned device
|
|
* object.
|
|
*
|
|
* Returns: (nullable) (transfer none) : the ID3D11VideoDevice handle or %NULL
|
|
* if ID3D11VideoDevice is unavailable.
|
|
*
|
|
* Since: 1.22
|
|
*/
|
|
ID3D11VideoDevice *
|
|
gst_d3d11_device_get_video_device_handle (GstD3D11Device * device)
|
|
{
|
|
GstD3D11DevicePrivate *priv;
|
|
|
|
g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
|
|
|
|
priv = device->priv;
|
|
std::lock_guard < std::mutex > lk (priv->resource_lock);
|
|
if (!priv->video_device) {
|
|
HRESULT hr;
|
|
ID3D11VideoDevice *video_device = NULL;
|
|
|
|
hr = priv->device->QueryInterface (IID_PPV_ARGS (&video_device));
|
|
if (gst_d3d11_result (hr, device))
|
|
priv->video_device = video_device;
|
|
}
|
|
|
|
return priv->video_device;
|
|
}
|
|
|
|
/**
|
|
* gst_d3d11_device_get_video_context_handle:
|
|
* @device: a #GstD3D11Device
|
|
*
|
|
* Used for various D3D11 APIs directly. Caller must not destroy returned device
|
|
* object.
|
|
*
|
|
* Returns: (nullable) (transfer none): the ID3D11VideoContext handle or %NULL
|
|
* if ID3D11VideoContext is unavailable.
|
|
*
|
|
* Since: 1.22
|
|
*/
|
|
ID3D11VideoContext *
|
|
gst_d3d11_device_get_video_context_handle (GstD3D11Device * device)
|
|
{
|
|
GstD3D11DevicePrivate *priv;
|
|
|
|
g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
|
|
|
|
priv = device->priv;
|
|
std::lock_guard < std::mutex > lk (priv->resource_lock);
|
|
if (!priv->video_context) {
|
|
HRESULT hr;
|
|
ID3D11VideoContext *video_context = NULL;
|
|
|
|
hr = priv->device_context->QueryInterface (IID_PPV_ARGS (&video_context));
|
|
if (gst_d3d11_result (hr, device))
|
|
priv->video_context = video_context;
|
|
}
|
|
|
|
return priv->video_context;
|
|
}
|
|
|
|
/**
|
|
* gst_d3d11_device_lock:
|
|
* @device: a #GstD3D11Device
|
|
*
|
|
* Take lock for @device. Any thread-unsafe API call needs to be
|
|
* protected by this method. This call must be paired with
|
|
* gst_d3d11_device_unlock()
|
|
*
|
|
* Since: 1.22
|
|
*/
|
|
void
|
|
gst_d3d11_device_lock (GstD3D11Device * device)
|
|
{
|
|
GstD3D11DevicePrivate *priv;
|
|
|
|
g_return_if_fail (GST_IS_D3D11_DEVICE (device));
|
|
|
|
priv = device->priv;
|
|
|
|
AcquireSRWLockShared (&_device_creation_rwlock);
|
|
|
|
GST_TRACE_OBJECT (device, "device locking");
|
|
priv->extern_lock.lock ();
|
|
GST_TRACE_OBJECT (device, "device locked");
|
|
}
|
|
|
|
/**
|
|
* gst_d3d11_device_unlock:
|
|
* @device: a #GstD3D11Device
|
|
*
|
|
* Release lock for @device. This call must be paired with
|
|
* gst_d3d11_device_lock()
|
|
*
|
|
* Since: 1.22
|
|
*/
|
|
void
|
|
gst_d3d11_device_unlock (GstD3D11Device * device)
|
|
{
|
|
GstD3D11DevicePrivate *priv;
|
|
|
|
g_return_if_fail (GST_IS_D3D11_DEVICE (device));
|
|
|
|
priv = device->priv;
|
|
|
|
priv->extern_lock.unlock ();
|
|
GST_TRACE_OBJECT (device, "device unlocked");
|
|
|
|
ReleaseSRWLockShared (&_device_creation_rwlock);
|
|
}
|
|
|
|
/**
|
|
* gst_d3d11_device_get_format:
|
|
* @device: a #GstD3D11Device
|
|
* @format: a #GstVideoFormat
|
|
* @device_format: (out caller-allocates) (nullable): a #GstD3D11Format
|
|
*
|
|
* Converts @format to #GstD3D11Format if the @format is supported
|
|
* by device
|
|
*
|
|
* Returns: %TRUE if @format is supported by @device
|
|
*
|
|
* Since: 1.22
|
|
*/
|
|
gboolean
|
|
gst_d3d11_device_get_format (GstD3D11Device * device, GstVideoFormat format,
|
|
GstD3D11Format * device_format)
|
|
{
|
|
GstD3D11DevicePrivate *priv;
|
|
|
|
g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), FALSE);
|
|
|
|
priv = device->priv;
|
|
|
|
const auto & target = priv->format_table.find (format);
|
|
if (target == priv->format_table.end ()) {
|
|
if (device_format)
|
|
gst_d3d11_format_init (device_format);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if (device_format)
|
|
*device_format = target->second;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
gint64
|
|
gst_d3d11_pixel_shader_token_new (void)
|
|
{
|
|
/* *INDENT-OFF* */
|
|
static std::atomic < gint64 > token_ { 0 };
|
|
/* *INDENT-ON* */
|
|
|
|
return token_.fetch_add (1);
|
|
}
|
|
|
|
gint64
|
|
gst_d3d11_vertex_shader_token_new (void)
|
|
{
|
|
/* *INDENT-OFF* */
|
|
static std::atomic < gint64 > token_ { 0 };
|
|
/* *INDENT-ON* */
|
|
|
|
return token_.fetch_add (1);
|
|
}
|
|
|
|
gint64
|
|
gst_d3d11_compute_shader_token_new (void)
|
|
{
|
|
/* *INDENT-OFF* */
|
|
static std::atomic < gint64 > token_ { 0 };
|
|
/* *INDENT-ON* */
|
|
|
|
return token_.fetch_add (1);
|
|
}
|
|
|
|
HRESULT
|
|
gst_d3d11_device_get_pixel_shader_uncached (GstD3D11Device * device,
|
|
gint64 token, const void *bytecode, gsize bytecode_size,
|
|
const gchar * source, gsize source_size, const gchar * entry_point,
|
|
const D3D_SHADER_MACRO * defines, ID3D11PixelShader ** ps)
|
|
{
|
|
GstD3D11DevicePrivate *priv = device->priv;
|
|
HRESULT hr;
|
|
ComPtr < ID3D11PixelShader > shader;
|
|
ComPtr < ID3DBlob > blob;
|
|
const void *data;
|
|
gsize size;
|
|
|
|
GST_LOG_OBJECT (device,
|
|
"Creating pixel shader for token %" G_GINT64_FORMAT ", source:\n%s",
|
|
token, source);
|
|
|
|
if (bytecode && bytecode_size > 1) {
|
|
data = bytecode;
|
|
size = bytecode_size;
|
|
GST_DEBUG_OBJECT (device,
|
|
"Creating shader \"%s\" using precompiled bytecode", entry_point);
|
|
} else {
|
|
hr = gst_d3d11_shader_cache_get_pixel_shader_blob (token,
|
|
source, source_size, entry_point, defines, &blob);
|
|
if (!gst_d3d11_result (hr, device))
|
|
return hr;
|
|
|
|
data = blob->GetBufferPointer ();
|
|
size = blob->GetBufferSize ();
|
|
}
|
|
|
|
hr = priv->device->CreatePixelShader (data, size, nullptr, &shader);
|
|
if (!gst_d3d11_result (hr, device))
|
|
return hr;
|
|
|
|
GST_DEBUG_OBJECT (device,
|
|
"Created pixel shader \"%s\" for token %" G_GINT64_FORMAT,
|
|
entry_point, token);
|
|
*ps = shader.Detach ();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
gst_d3d11_device_get_pixel_shader (GstD3D11Device * device, gint64 token,
|
|
const void *bytecode, gsize bytecode_size, const gchar * source,
|
|
gsize source_size, const gchar * entry_point,
|
|
const D3D_SHADER_MACRO * defines, ID3D11PixelShader ** ps)
|
|
{
|
|
GstD3D11DevicePrivate *priv = device->priv;
|
|
HRESULT hr;
|
|
ComPtr < ID3D11PixelShader > shader;
|
|
|
|
GST_DEBUG_OBJECT (device, "Getting pixel shader \"%s\" for token %"
|
|
G_GINT64_FORMAT, entry_point, token);
|
|
|
|
std::lock_guard < std::mutex > lk (priv->resource_lock);
|
|
auto cached = priv->ps_cache.find (token);
|
|
if (cached != priv->ps_cache.end ()) {
|
|
GST_DEBUG_OBJECT (device,
|
|
"Found cached pixel shader \"%s\" for token %" G_GINT64_FORMAT,
|
|
entry_point, token);
|
|
*ps = cached->second.Get ();
|
|
(*ps)->AddRef ();
|
|
return S_OK;
|
|
}
|
|
|
|
hr = gst_d3d11_device_get_pixel_shader_uncached (device, token, bytecode,
|
|
bytecode_size, source, source_size, entry_point, defines, &shader);
|
|
if (!gst_d3d11_result (hr, device))
|
|
return hr;
|
|
|
|
priv->ps_cache[token] = shader;
|
|
*ps = shader.Detach ();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
gst_d3d11_device_get_vertex_shader (GstD3D11Device * device, gint64 token,
|
|
const void *bytecode, gsize bytecode_size, const gchar * source,
|
|
gsize source_size, const gchar * entry_point,
|
|
const D3D11_INPUT_ELEMENT_DESC * input_desc, guint desc_len,
|
|
ID3D11VertexShader ** vs, ID3D11InputLayout ** layout)
|
|
{
|
|
GstD3D11DevicePrivate *priv = device->priv;
|
|
HRESULT hr;
|
|
ComPtr < ID3D11VertexShader > shader;
|
|
ComPtr < ID3D11InputLayout > input_layout;
|
|
ComPtr < ID3DBlob > blob;
|
|
const void *data;
|
|
gsize size;
|
|
|
|
GST_DEBUG_OBJECT (device, "Getting vertext shader \"%s\" for token %"
|
|
G_GINT64_FORMAT, entry_point, token);
|
|
|
|
std::lock_guard < std::mutex > lk (priv->resource_lock);
|
|
auto cached = priv->vs_cache.find (token);
|
|
if (cached != priv->vs_cache.end ()) {
|
|
GST_DEBUG_OBJECT (device,
|
|
"Found cached vertex shader \"%s\" for token %" G_GINT64_FORMAT,
|
|
entry_point, token);
|
|
*vs = cached->second.first.Get ();
|
|
*layout = cached->second.second.Get ();
|
|
(*vs)->AddRef ();
|
|
(*layout)->AddRef ();
|
|
return S_OK;
|
|
}
|
|
|
|
GST_LOG_OBJECT (device,
|
|
"Creating vertex shader for token %" G_GINT64_FORMAT ", shader: \n%s",
|
|
token, source);
|
|
|
|
if (bytecode && bytecode_size > 1) {
|
|
data = bytecode;
|
|
size = bytecode_size;
|
|
GST_DEBUG_OBJECT (device,
|
|
"Creating shader \"%s\" using precompiled bytecode", entry_point);
|
|
} else {
|
|
hr = gst_d3d11_shader_cache_get_vertex_shader_blob (token,
|
|
source, source_size, entry_point, &blob);
|
|
if (!gst_d3d11_result (hr, device))
|
|
return hr;
|
|
|
|
data = blob->GetBufferPointer ();
|
|
size = blob->GetBufferSize ();
|
|
}
|
|
|
|
hr = priv->device->CreateVertexShader (data, size, nullptr, &shader);
|
|
if (!gst_d3d11_result (hr, device))
|
|
return hr;
|
|
|
|
hr = priv->device->CreateInputLayout (input_desc, desc_len, data,
|
|
size, &input_layout);
|
|
if (!gst_d3d11_result (hr, device))
|
|
return hr;
|
|
|
|
GST_DEBUG_OBJECT (device, "Created vertex shader \"%s\" for token %"
|
|
G_GINT64_FORMAT, entry_point, token);
|
|
priv->vs_cache[token] = std::make_pair (shader, input_layout);
|
|
|
|
*vs = shader.Detach ();
|
|
*layout = input_layout.Detach ();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
gst_d3d11_device_get_sampler (GstD3D11Device * device, D3D11_FILTER filter,
|
|
ID3D11SamplerState ** sampler)
|
|
{
|
|
GstD3D11DevicePrivate *priv = device->priv;
|
|
ComPtr < ID3D11SamplerState > state;
|
|
D3D11_SAMPLER_DESC desc;
|
|
HRESULT hr;
|
|
|
|
std::lock_guard < std::mutex > lk (priv->resource_lock);
|
|
auto cached = priv->sampler_cache.find (filter);
|
|
if (cached != priv->sampler_cache.end ()) {
|
|
*sampler = cached->second.Get ();
|
|
(*sampler)->AddRef ();
|
|
return S_OK;
|
|
}
|
|
|
|
memset (&desc, 0, sizeof (D3D11_SAMPLER_DESC));
|
|
|
|
desc.Filter = filter;
|
|
desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
|
|
desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
|
|
desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
|
|
desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
|
|
desc.MaxLOD = D3D11_FLOAT32_MAX;
|
|
|
|
if (filter == D3D11_FILTER_ANISOTROPIC) {
|
|
if (priv->feature_level > D3D_FEATURE_LEVEL_9_1)
|
|
desc.MaxAnisotropy = 16;
|
|
else
|
|
desc.MaxAnisotropy = 2;
|
|
}
|
|
|
|
hr = priv->device->CreateSamplerState (&desc, &state);
|
|
if (!gst_d3d11_result (hr, device))
|
|
return hr;
|
|
|
|
priv->sampler_cache[filter] = state;
|
|
*sampler = state.Detach ();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
gst_d3d11_device_get_rasterizer (GstD3D11Device * device,
|
|
ID3D11RasterizerState ** rasterizer)
|
|
{
|
|
GstD3D11DevicePrivate *priv = device->priv;
|
|
D3D11_RASTERIZER_DESC desc;
|
|
HRESULT hr;
|
|
|
|
std::lock_guard < std::mutex > lk (priv->resource_lock);
|
|
if (priv->rs) {
|
|
*rasterizer = priv->rs;
|
|
priv->rs->AddRef ();
|
|
return S_OK;
|
|
}
|
|
|
|
memset (&desc, 0, sizeof (D3D11_RASTERIZER_DESC));
|
|
desc.FillMode = D3D11_FILL_SOLID;
|
|
desc.CullMode = D3D11_CULL_NONE;
|
|
desc.DepthClipEnable = TRUE;
|
|
|
|
hr = priv->device->CreateRasterizerState (&desc, rasterizer);
|
|
if (!gst_d3d11_result (hr, device))
|
|
return hr;
|
|
|
|
priv->rs = *rasterizer;
|
|
priv->rs->AddRef ();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
gst_d3d11_device_get_rasterizer_msaa (GstD3D11Device * device,
|
|
ID3D11RasterizerState ** rasterizer)
|
|
{
|
|
GstD3D11DevicePrivate *priv = device->priv;
|
|
D3D11_RASTERIZER_DESC desc;
|
|
HRESULT hr;
|
|
|
|
std::lock_guard < std::mutex > lk (priv->resource_lock);
|
|
if (priv->rs_msaa) {
|
|
*rasterizer = priv->rs_msaa;
|
|
priv->rs_msaa->AddRef ();
|
|
return S_OK;
|
|
}
|
|
|
|
memset (&desc, 0, sizeof (D3D11_RASTERIZER_DESC));
|
|
desc.FillMode = D3D11_FILL_SOLID;
|
|
desc.CullMode = D3D11_CULL_NONE;
|
|
desc.DepthClipEnable = TRUE;
|
|
desc.MultisampleEnable = TRUE;
|
|
desc.AntialiasedLineEnable = TRUE;
|
|
|
|
hr = priv->device->CreateRasterizerState (&desc, rasterizer);
|
|
if (!gst_d3d11_result (hr, device))
|
|
return hr;
|
|
|
|
priv->rs_msaa = *rasterizer;
|
|
priv->rs_msaa->AddRef ();
|
|
|
|
return S_OK;
|
|
}
|