1980 lines
		
	
	
		
			54 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1980 lines
		
	
	
		
			54 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:
 | |
|       {
 | |
|         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:
 | |
|         /* 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;
 | |
|   }
 | |
| 
 | |
|   /* FIXME: d3d11 sampler doesn't support packed-and-subsampled formats
 | |
|    * very well (and it's really poorly documented).
 | |
|    * As per observation, d3d11 samplers seems to be dropping the second
 | |
|    * Y componet from "Y0-U0-Y1-V0" pair which results in bad visual quality
 | |
|    * than 4:2:0 subsampled formats. We should revisit this later */
 | |
| 
 | |
|   /* TODO: The best would be using d3d11 compute shader to handle this kinds of
 | |
|    * samples but comute shader is not implemented yet by us.
 | |
|    *
 | |
|    * Another simple approach is using d3d11 video processor,
 | |
|    * but capability will be very device dependent because it depends on
 | |
|    * GPU vendor's driver implementation, moreover, software fallback does
 | |
|    * not support d3d11 video processor. So it's not reliable in this case */
 | |
| #if 0
 | |
|   /* NOTE: packted yuv 4:2:2 YUY2, UYVY, and VYUY formats are not natively
 | |
|    * supported render target view formats
 | |
|    * (i.e., cannot be output format of shader pipeline) */
 | |
|   priv->format_table[n_formats].format = GST_VIDEO_FORMAT_YUY2;
 | |
|   if (can_support_format (self, DXGI_FORMAT_YUY2,
 | |
|           D3D11_FORMAT_SUPPORT_SHADER_SAMPLE)) {
 | |
|     priv->format_table[n_formats].resource_format[0] =
 | |
|         DXGI_FORMAT_R8G8B8A8_UNORM;
 | |
|     priv->format_table[n_formats].dxgi_format = DXGI_FORMAT_YUY2;
 | |
|   } else {
 | |
|     /* If DXGI_FORMAT_YUY2 format is not supported, use this format,
 | |
|      * it's analogous to YUY2 */
 | |
|     priv->format_table[n_formats].resource_format[0] =
 | |
|         DXGI_FORMAT_G8R8_G8B8_UNORM;
 | |
|   }
 | |
|   n_formats++;
 | |
| 
 | |
|   /* No native DXGI format available for UYVY */
 | |
|   priv->format_table[n_formats].format = GST_VIDEO_FORMAT_UYVY;
 | |
|   priv->format_table[n_formats].resource_format[0] =
 | |
|       DXGI_FORMAT_R8G8_B8G8_UNORM;
 | |
|   n_formats++;
 | |
| 
 | |
|   /* No native DXGI format available for VYUY */
 | |
|   priv->format_table[n_formats].format = GST_VIDEO_FORMAT_VYUY;
 | |
|   priv->format_table[n_formats].resource_format[0] =
 | |
|       DXGI_FORMAT_R8G8_B8G8_UNORM;
 | |
|   n_formats++;
 | |
| 
 | |
|   /* Y210 and Y410 formats cannot support rtv */
 | |
|   priv->format_table[n_formats].format = GST_VIDEO_FORMAT_Y210;
 | |
|   priv->format_table[n_formats].resource_format[0] =
 | |
|       DXGI_FORMAT_R16G16B16A16_UNORM;
 | |
|   if (can_support_format (self, DXGI_FORMAT_Y210,
 | |
|           D3D11_FORMAT_SUPPORT_SHADER_SAMPLE))
 | |
|     priv->format_table[n_formats].dxgi_format = DXGI_FORMAT_Y210;
 | |
|   else
 | |
|     priv->format_table[n_formats].dxgi_format = DXGI_FORMAT_UNKNOWN;
 | |
|   n_formats++;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| 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 void
 | |
| gst_d3d11_device_dispose (GObject * object)
 | |
| {
 | |
|   GstD3D11Device *self = GST_D3D11_DEVICE (object);
 | |
|   GstD3D11DevicePrivate *priv = self->priv;
 | |
| 
 | |
|   GST_LOG_OBJECT (self, "dispose");
 | |
| 
 | |
|   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
 | |
| 
 | |
|   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;
 | |
|       guint luid;
 | |
| 
 | |
|       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;
 | |
| 
 | |
|       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_9_3,
 | |
|     D3D_FEATURE_LEVEL_9_2,
 | |
|     D3D_FEATURE_LEVEL_9_1
 | |
|   };
 | |
|   D3D_FEATURE_LEVEL selected_level;
 | |
| 
 | |
|   debug_init_once ();
 | |
| 
 | |
|   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;
 | |
|     }
 | |
| 
 | |
|     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;
 | |
| 
 | |
|   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");
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * 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);
 | |
| }
 | |
| 
 | |
| 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 (priv->feature_level >= D3D_FEATURE_LEVEL_11_0) {
 | |
|     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 ();
 | |
|     }
 | |
|   } else {
 | |
|     const gchar *target;
 | |
|     if (priv->feature_level >= D3D_FEATURE_LEVEL_10_0)
 | |
|       target = "ps_4_0";
 | |
|     else if (priv->feature_level >= D3D_FEATURE_LEVEL_9_3)
 | |
|       target = "ps_4_0_level_9_3";
 | |
|     else
 | |
|       target = "ps_4_0_level_9_1";
 | |
| 
 | |
|     hr = gst_d3d11_compile (source, source_size, nullptr, defines,
 | |
|         nullptr, entry_point, target, 0, 0, &blob, nullptr);
 | |
|     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;
 | |
| 
 | |
|   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 (priv->feature_level >= D3D_FEATURE_LEVEL_11_0) {
 | |
|     ComPtr < ID3DBlob > blob;
 | |
|     const void *data;
 | |
|     gsize size;
 | |
| 
 | |
|     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;
 | |
|   } else {
 | |
|     hr = gst_d3d11_create_vertex_shader_simple (device, source, entry_point,
 | |
|         input_desc, desc_len, &shader, &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;
 | |
| }
 |