diff --git a/gst-libs/gst/d3d11/gstd3d11_fwd.h b/gst-libs/gst/d3d11/gstd3d11_fwd.h index 624cc156eb..8b455d1aab 100644 --- a/gst-libs/gst/d3d11/gstd3d11_fwd.h +++ b/gst-libs/gst/d3d11/gstd3d11_fwd.h @@ -64,9 +64,13 @@ typedef struct _GstD3D11DevicePrivate GstD3D11DevicePrivate; typedef struct _GstD3D11AllocationParams GstD3D11AllocationParams; typedef struct _GstD3D11Memory GstD3D11Memory; typedef struct _GstD3D11MemoryPrivate GstD3D11MemoryPrivate; + typedef struct _GstD3D11Allocator GstD3D11Allocator; typedef struct _GstD3D11AllocatorClass GstD3D11AllocatorClass; -typedef struct _GstD3D11AllocatorPrivate GstD3D11AllocatorPrivate; + +typedef struct _GstD3D11PoolAllocator GstD3D11PoolAllocator; +typedef struct _GstD3D11PoolAllocatorClass GstD3D11PoolAllocatorClass; +typedef struct _GstD3D11PoolAllocatorPrivate GstD3D11PoolAllocatorPrivate; typedef struct _GstD3D11BufferPool GstD3D11BufferPool; typedef struct _GstD3D11BufferPoolClass GstD3D11BufferPoolClass; diff --git a/gst-libs/gst/d3d11/gstd3d11bufferpool.c b/gst-libs/gst/d3d11/gstd3d11bufferpool.c index be0f675aef..f4b2874c71 100644 --- a/gst-libs/gst/d3d11/gstd3d11bufferpool.c +++ b/gst-libs/gst/d3d11/gstd3d11bufferpool.c @@ -45,14 +45,12 @@ GST_DEBUG_CATEGORY_STATIC (gst_d3d11_buffer_pool_debug); struct _GstD3D11BufferPoolPrivate { - GstD3D11Device *device; - GstD3D11Allocator *allocator; + GstD3D11Allocator *alloc[GST_VIDEO_MAX_PLANES]; - gboolean add_videometa; GstD3D11AllocationParams *d3d11_params; + gboolean texture_array_pool; gint stride[GST_VIDEO_MAX_PLANES]; - gsize size[GST_VIDEO_MAX_PLANES]; gsize offset[GST_VIDEO_MAX_PLANES]; }; @@ -64,10 +62,14 @@ static void gst_d3d11_buffer_pool_dispose (GObject * object); static const gchar **gst_d3d11_buffer_pool_get_options (GstBufferPool * pool); static gboolean gst_d3d11_buffer_pool_set_config (GstBufferPool * pool, GstStructure * config); -static GstFlowReturn gst_d3d11_buffer_pool_alloc (GstBufferPool * pool, +static GstFlowReturn gst_d3d11_buffer_pool_alloc_buffer (GstBufferPool * pool, GstBuffer ** buffer, GstBufferPoolAcquireParams * params); -static void gst_d3d11_buffer_pool_flush_start (GstBufferPool * pool); -static void gst_d3d11_buffer_pool_flush_stop (GstBufferPool * pool); +static GstFlowReturn gst_d3d11_buffer_pool_acquire_buffer (GstBufferPool * pool, + GstBuffer ** buffer, GstBufferPoolAcquireParams * params); +static void gst_d3d11_buffer_pool_reset_buffer (GstBufferPool * pool, + GstBuffer * buffer); +static gboolean gst_d3d11_buffer_pool_start (GstBufferPool * pool); +static gboolean gst_d3d11_buffer_pool_stop (GstBufferPool * pool); static void gst_d3d11_buffer_pool_class_init (GstD3D11BufferPoolClass * klass) @@ -79,9 +81,11 @@ gst_d3d11_buffer_pool_class_init (GstD3D11BufferPoolClass * klass) bufferpool_class->get_options = gst_d3d11_buffer_pool_get_options; bufferpool_class->set_config = gst_d3d11_buffer_pool_set_config; - bufferpool_class->alloc_buffer = gst_d3d11_buffer_pool_alloc; - bufferpool_class->flush_start = gst_d3d11_buffer_pool_flush_start; - bufferpool_class->flush_stop = gst_d3d11_buffer_pool_flush_stop; + bufferpool_class->alloc_buffer = gst_d3d11_buffer_pool_alloc_buffer; + bufferpool_class->acquire_buffer = gst_d3d11_buffer_pool_acquire_buffer; + bufferpool_class->reset_buffer = gst_d3d11_buffer_pool_reset_buffer; + bufferpool_class->start = gst_d3d11_buffer_pool_start; + bufferpool_class->stop = gst_d3d11_buffer_pool_stop; GST_DEBUG_CATEGORY_INIT (gst_d3d11_buffer_pool_debug, "d3d11bufferpool", 0, "d3d11bufferpool object"); @@ -93,18 +97,29 @@ gst_d3d11_buffer_pool_init (GstD3D11BufferPool * self) self->priv = gst_d3d11_buffer_pool_get_instance_private (self); } +static void +gst_d3d11_buffer_pool_clear_allocator (GstD3D11BufferPool * self) +{ + GstD3D11BufferPoolPrivate *priv = self->priv; + guint i; + + for (i = 0; i < G_N_ELEMENTS (priv->alloc); i++) { + if (priv->alloc[i]) { + gst_d3d11_allocator_set_active (priv->alloc[i], FALSE); + gst_clear_object (&priv->alloc[i]); + } + } +} + static void gst_d3d11_buffer_pool_dispose (GObject * object) { GstD3D11BufferPool *self = GST_D3D11_BUFFER_POOL (object); GstD3D11BufferPoolPrivate *priv = self->priv; - if (priv->d3d11_params) - gst_d3d11_allocation_params_free (priv->d3d11_params); - priv->d3d11_params = NULL; - - gst_clear_object (&priv->device); - gst_clear_object (&priv->allocator); + g_clear_pointer (&priv->d3d11_params, gst_d3d11_allocation_params_free); + gst_clear_object (&self->device); + gst_d3d11_buffer_pool_clear_allocator (self); G_OBJECT_CLASS (parent_class)->dispose (object); } @@ -118,79 +133,6 @@ gst_d3d11_buffer_pool_get_options (GstBufferPool * pool) return options; } -static GstBuffer * -allocate_staging_buffer (GstD3D11Allocator * allocator, - const GstVideoInfo * info, const GstD3D11Format * format, - const D3D11_TEXTURE2D_DESC desc[GST_VIDEO_MAX_PLANES], - gboolean add_videometa) -{ - GstBuffer *buffer; - gint i; - gint stride[GST_VIDEO_MAX_PLANES] = { 0, }; - gsize offset[GST_VIDEO_MAX_PLANES] = { 0, }; - GstMemory *mem; - - g_return_val_if_fail (GST_IS_D3D11_ALLOCATOR (allocator), NULL); - g_return_val_if_fail (info != NULL, NULL); - g_return_val_if_fail (format != NULL, NULL); - g_return_val_if_fail (desc != NULL, NULL); - - buffer = gst_buffer_new (); - - if (format->dxgi_format == DXGI_FORMAT_UNKNOWN) { - gsize size[GST_VIDEO_MAX_PLANES] = { 0, }; - - for (i = 0; i < GST_VIDEO_INFO_N_PLANES (info); i++) { - mem = gst_d3d11_allocator_alloc_staging (allocator, &desc[i], 0, - &stride[i]); - - if (!mem) { - GST_ERROR_OBJECT (allocator, "Couldn't allocate memory for plane %d", - i); - goto error; - } - - size[i] = gst_memory_get_sizes (mem, NULL, NULL); - if (i > 0) - offset[i] = offset[i - 1] + size[i - 1]; - gst_buffer_append_memory (buffer, mem); - } - } else { - /* must be YUV semi-planar or single plane */ - g_assert (GST_VIDEO_INFO_N_PLANES (info) <= 2); - - mem = gst_d3d11_allocator_alloc_staging (allocator, &desc[0], 0, - &stride[0]); - - if (!mem) { - GST_ERROR_OBJECT (allocator, "Couldn't allocate memory"); - goto error; - } - - gst_memory_get_sizes (mem, NULL, NULL); - gst_buffer_append_memory (buffer, mem); - - if (GST_VIDEO_INFO_N_PLANES (info) == 2) { - stride[1] = stride[0]; - offset[1] = stride[0] * desc[0].Height; - } - } - - if (add_videometa) { - gst_buffer_add_video_meta_full (buffer, GST_VIDEO_FRAME_FLAG_NONE, - GST_VIDEO_INFO_FORMAT (info), GST_VIDEO_INFO_WIDTH (info), - GST_VIDEO_INFO_HEIGHT (info), GST_VIDEO_INFO_N_PLANES (info), - offset, stride); - } - - return buffer; - -error: - gst_buffer_unref (buffer); - - return NULL; -} - static gboolean gst_d3d11_buffer_pool_set_config (GstBufferPool * pool, GstStructure * config) { @@ -199,10 +141,10 @@ gst_d3d11_buffer_pool_set_config (GstBufferPool * pool, GstStructure * config) GstVideoInfo info; GstCaps *caps = NULL; guint min_buffers, max_buffers; - GstAllocator *allocator = NULL; gboolean ret = TRUE; D3D11_TEXTURE2D_DESC *desc; - GstBuffer *staging_buffer; + const GstD3D11Format *format; + gsize offset = 0; gint i; if (!gst_buffer_pool_config_get_params (config, &caps, NULL, &min_buffers, @@ -219,24 +161,10 @@ gst_d3d11_buffer_pool_set_config (GstBufferPool * pool, GstStructure * config) GST_LOG_OBJECT (pool, "%dx%d, caps %" GST_PTR_FORMAT, info.width, info.height, caps); - if (!gst_buffer_pool_config_get_allocator (config, &allocator, NULL)) - goto wrong_config; + gst_d3d11_buffer_pool_clear_allocator (self); - gst_clear_object (&priv->allocator); - - if (allocator) { - if (!GST_IS_D3D11_ALLOCATOR (allocator)) { - goto wrong_allocator; - } else { - priv->allocator = gst_object_ref (allocator); - } - } else { - priv->allocator = gst_d3d11_allocator_new (priv->device); - g_assert (priv->allocator); - } - - priv->add_videometa = gst_buffer_pool_config_has_option (config, - GST_BUFFER_POOL_OPTION_VIDEO_META); + memset (priv->stride, 0, sizeof (priv->stride)); + memset (priv->offset, 0, sizeof (priv->offset)); if (priv->d3d11_params) gst_d3d11_allocation_params_free (priv->d3d11_params); @@ -245,7 +173,7 @@ gst_d3d11_buffer_pool_set_config (GstBufferPool * pool, GstStructure * config) if (!priv->d3d11_params) { /* allocate memory with resource format by default */ priv->d3d11_params = - gst_d3d11_allocation_params_new (priv->device, &info, 0, 0); + gst_d3d11_allocation_params_new (self->device, &info, 0, 0); } desc = priv->d3d11_params->desc; @@ -313,35 +241,71 @@ gst_d3d11_buffer_pool_set_config (GstBufferPool * pool, GstStructure * config) max_buffers, max_array_size); max_buffers = max_array_size; } + + priv->texture_array_pool = TRUE; + } else { + priv->texture_array_pool = FALSE; } - staging_buffer = allocate_staging_buffer (priv->allocator, - &info, priv->d3d11_params->d3d11_format, priv->d3d11_params->desc, TRUE); + offset = 0; + for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) { + GstD3D11Allocator *alloc; + GstD3D11PoolAllocator *pool_alloc; + GstFlowReturn flow_ret; + GstMemory *mem = NULL; + guint stride = 0; - if (!staging_buffer) { - GST_ERROR_OBJECT (pool, "Couldn't allocated staging buffer"); - return FALSE; - } else { - GstVideoMeta *meta = gst_buffer_get_video_meta (staging_buffer); + if (desc[i].Format == DXGI_FORMAT_UNKNOWN) + break; - if (!meta) { - GST_ERROR_OBJECT (pool, "Buffer doesn't have video meta"); - gst_buffer_unref (staging_buffer); + alloc = + (GstD3D11Allocator *) gst_d3d11_pool_allocator_new (self->device, + &desc[i]); + if (!gst_d3d11_allocator_set_active (alloc, TRUE)) { + GST_ERROR_OBJECT (self, "Failed to activate allocator"); + gst_object_unref (alloc); return FALSE; } - for (i = 0; i < gst_buffer_n_memory (staging_buffer); i++) { - GstMemory *mem = gst_buffer_peek_memory (staging_buffer, i); - - priv->size[i] = gst_memory_get_sizes (mem, NULL, NULL); + pool_alloc = GST_D3D11_POOL_ALLOCATOR (alloc); + flow_ret = gst_d3d11_pool_allocator_acquire_memory (pool_alloc, &mem); + if (flow_ret != GST_FLOW_OK) { + GST_ERROR_OBJECT (self, "Failed to allocate initial memory"); + gst_d3d11_allocator_set_active (alloc, FALSE); + gst_object_unref (alloc); + return FALSE; } - memcpy (priv->offset, meta->offset, sizeof (priv->offset)); - memcpy (priv->stride, meta->stride, sizeof (priv->stride)); + if (!gst_d3d11_memory_get_texture_stride (GST_D3D11_MEMORY_CAST (mem), + &stride) || stride < desc[i].Width) { + GST_ERROR_OBJECT (self, "Failed to calculate stride"); + + gst_d3d11_allocator_set_active (alloc, FALSE); + gst_object_unref (alloc); + gst_memory_unref (mem); + + return FALSE; + } + + priv->stride[i] = stride; + priv->offset[i] = offset; + offset += mem->size; + + priv->alloc[i] = alloc; + + gst_memory_unref (mem); } - self->buffer_size = gst_buffer_get_size (staging_buffer); - gst_buffer_unref (staging_buffer); + g_assert (priv->d3d11_params->d3d11_format != NULL); + format = priv->d3d11_params->d3d11_format; + /* single texture semi-planar formats */ + if (format->dxgi_format != DXGI_FORMAT_UNKNOWN && + GST_VIDEO_INFO_N_PLANES (&info) == 2) { + priv->stride[1] = priv->stride[0]; + priv->offset[1] = priv->stride[0] * desc[0].Height; + } + + self->buffer_size = offset; gst_buffer_pool_config_set_params (config, caps, self->buffer_size, min_buffers, max_buffers); @@ -365,84 +329,172 @@ wrong_caps: "failed getting geometry from caps %" GST_PTR_FORMAT, caps); return FALSE; } -wrong_allocator: - { - GST_WARNING_OBJECT (pool, "Incorrect allocator type for this pool"); - return FALSE; - } } static GstFlowReturn -gst_d3d11_buffer_pool_alloc (GstBufferPool * pool, GstBuffer ** buffer, - GstBufferPoolAcquireParams * params) +gst_d3d11_buffer_pool_fill_buffer (GstD3D11BufferPool * self, GstBuffer * buf) { - GstD3D11BufferPool *self = GST_D3D11_BUFFER_POOL (pool); GstD3D11BufferPoolPrivate *priv = self->priv; - GstMemory *mem; - GstBuffer *buf; - GstD3D11AllocationParams *d3d11_params = priv->d3d11_params; - GstVideoInfo *info = &d3d11_params->info; + GstFlowReturn ret = GST_FLOW_OK; gint i; - buf = gst_buffer_new (); + for (i = 0; i < G_N_ELEMENTS (priv->alloc); i++) { + GstMemory *mem = NULL; + GstD3D11PoolAllocator *alloc = GST_D3D11_POOL_ALLOCATOR (priv->alloc[i]); - if (d3d11_params->d3d11_format->dxgi_format == DXGI_FORMAT_UNKNOWN) { - for (i = 0; i < GST_VIDEO_INFO_N_PLANES (info); i++) { - mem = gst_d3d11_allocator_alloc (priv->allocator, &d3d11_params->desc[i], - d3d11_params->flags, priv->size[i]); - if (!mem) - goto error; + if (!alloc) + break; - gst_buffer_append_memory (buf, mem); + ret = gst_d3d11_pool_allocator_acquire_memory (alloc, &mem); + if (ret != GST_FLOW_OK) { + GST_WARNING_OBJECT (self, "Failed to acquire memory, ret %s", + gst_flow_get_name (ret)); + return ret; } - } else { - mem = gst_d3d11_allocator_alloc (priv->allocator, &d3d11_params->desc[0], - d3d11_params->flags, priv->size[0]); - - if (!mem) - goto error; gst_buffer_append_memory (buf, mem); } - if (priv->add_videometa) { - GST_DEBUG_OBJECT (self, "adding GstVideoMeta"); - gst_buffer_add_video_meta_full (buf, GST_VIDEO_FRAME_FLAG_NONE, - GST_VIDEO_INFO_FORMAT (info), GST_VIDEO_INFO_WIDTH (info), - GST_VIDEO_INFO_HEIGHT (info), GST_VIDEO_INFO_N_PLANES (info), - priv->offset, priv->stride); + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_d3d11_buffer_pool_alloc_buffer (GstBufferPool * pool, GstBuffer ** buffer, + GstBufferPoolAcquireParams * params) +{ + GstD3D11BufferPool *self = GST_D3D11_BUFFER_POOL (pool); + GstD3D11BufferPoolPrivate *priv = self->priv; + GstD3D11AllocationParams *d3d11_params = priv->d3d11_params; + GstVideoInfo *info = &d3d11_params->info; + GstBuffer *buf; + GstFlowReturn ret = GST_FLOW_OK; + + buf = gst_buffer_new (); + /* For texture-array case, we release memory in reset_buffer() so that it can + * be returned to allocator. So our acquire_buffer() method is expecting + * empty buffer in that case. Don't fill memory here for non-texture-array */ + if (!priv->texture_array_pool) { + ret = gst_d3d11_buffer_pool_fill_buffer (self, buf); + if (ret != GST_FLOW_OK) { + gst_buffer_unref (buf); + return ret; + } } + gst_buffer_add_video_meta_full (buf, GST_VIDEO_FRAME_FLAG_NONE, + GST_VIDEO_INFO_FORMAT (info), GST_VIDEO_INFO_WIDTH (info), + GST_VIDEO_INFO_HEIGHT (info), GST_VIDEO_INFO_N_PLANES (info), + priv->offset, priv->stride); + *buffer = buf; return GST_FLOW_OK; +} -error: - gst_buffer_unref (buf); +static GstFlowReturn +gst_d3d11_buffer_pool_acquire_buffer (GstBufferPool * pool, + GstBuffer ** buffer, GstBufferPoolAcquireParams * params) +{ + GstD3D11BufferPool *self = GST_D3D11_BUFFER_POOL (pool); + GstD3D11BufferPoolPrivate *priv = self->priv; + GstFlowReturn ret; - GST_ERROR_OBJECT (self, "cannot create texture memory"); + ret = GST_BUFFER_POOL_CLASS (parent_class)->acquire_buffer (pool, + buffer, params); - return GST_FLOW_ERROR; + if (ret != GST_FLOW_OK) + return ret; + + /* Don't need special handling for non-texture-array case */ + if (!priv->texture_array_pool) + return ret; + + /* Baseclass will hold empty buffer in this case, fill GstMemory */ + g_assert (gst_buffer_n_memory (*buffer) == 0); + + return gst_d3d11_buffer_pool_fill_buffer (self, *buffer); } static void -gst_d3d11_buffer_pool_flush_start (GstBufferPool * pool) +gst_d3d11_buffer_pool_reset_buffer (GstBufferPool * pool, GstBuffer * buffer) { GstD3D11BufferPool *self = GST_D3D11_BUFFER_POOL (pool); GstD3D11BufferPoolPrivate *priv = self->priv; - if (priv->allocator) - gst_d3d11_allocator_set_flushing (priv->allocator, TRUE); + /* if we are using texture array, return memory to allocator, so that + * memory pool allocator can wake up if it's waiting for available memory */ + if (priv->texture_array_pool) { + GST_LOG_OBJECT (self, "Returning memory to allocator"); + gst_buffer_remove_all_memory (buffer); + } + + GST_BUFFER_POOL_CLASS (parent_class)->reset_buffer (pool, buffer); + GST_BUFFER_FLAGS (buffer) = 0; } -static void -gst_d3d11_buffer_pool_flush_stop (GstBufferPool * pool) +static gboolean +gst_d3d11_buffer_pool_start (GstBufferPool * pool) { GstD3D11BufferPool *self = GST_D3D11_BUFFER_POOL (pool); GstD3D11BufferPoolPrivate *priv = self->priv; + guint i; + gboolean ret; - if (priv->allocator) - gst_d3d11_allocator_set_flushing (priv->allocator, FALSE); + GST_DEBUG_OBJECT (self, "Start"); + + for (i = 0; i < G_N_ELEMENTS (priv->alloc); i++) { + GstD3D11Allocator *alloc = priv->alloc[i]; + + if (!alloc) + break; + + if (!gst_d3d11_allocator_set_active (alloc, TRUE)) { + GST_ERROR_OBJECT (self, "Failed to activate allocator"); + return FALSE; + } + } + + ret = GST_BUFFER_POOL_CLASS (parent_class)->start (pool); + if (!ret) { + GST_ERROR_OBJECT (self, "Failed to start"); + + for (i = 0; i < G_N_ELEMENTS (priv->alloc); i++) { + GstD3D11Allocator *alloc = priv->alloc[i]; + + if (!alloc) + break; + + gst_d3d11_allocator_set_active (alloc, FALSE); + } + + return FALSE; + } + + return TRUE; +} + +static gboolean +gst_d3d11_buffer_pool_stop (GstBufferPool * pool) +{ + GstD3D11BufferPool *self = GST_D3D11_BUFFER_POOL (pool); + GstD3D11BufferPoolPrivate *priv = self->priv; + guint i; + + GST_DEBUG_OBJECT (self, "Stop"); + + for (i = 0; i < G_N_ELEMENTS (priv->alloc); i++) { + GstD3D11Allocator *alloc = priv->alloc[i]; + + if (!alloc) + break; + + if (!gst_d3d11_allocator_set_active (alloc, FALSE)) { + GST_ERROR_OBJECT (self, "Failed to deactivate allocator"); + return FALSE; + } + } + + return GST_BUFFER_POOL_CLASS (parent_class)->stop (pool); } /** @@ -457,15 +509,13 @@ GstBufferPool * gst_d3d11_buffer_pool_new (GstD3D11Device * device) { GstD3D11BufferPool *pool; - GstD3D11Allocator *alloc; g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL); pool = g_object_new (GST_TYPE_D3D11_BUFFER_POOL, NULL); - alloc = gst_d3d11_allocator_new (device); + gst_object_ref_sink (pool); - pool->priv->device = gst_object_ref (device); - pool->priv->allocator = alloc; + pool->device = gst_object_ref (device); return GST_BUFFER_POOL_CAST (pool); } diff --git a/gst-libs/gst/d3d11/gstd3d11bufferpool.h b/gst-libs/gst/d3d11/gstd3d11bufferpool.h index 4335136e83..e809000ea7 100644 --- a/gst-libs/gst/d3d11/gstd3d11bufferpool.h +++ b/gst-libs/gst/d3d11/gstd3d11bufferpool.h @@ -39,6 +39,8 @@ struct _GstD3D11BufferPool { GstBufferPool parent; + GstD3D11Device *device; + /* re-calculated buffer size based on d3d11 pitch and stride */ guint buffer_size; diff --git a/gst-libs/gst/d3d11/gstd3d11device.c b/gst-libs/gst/d3d11/gstd3d11device.c index 94f9667100..946732a256 100644 --- a/gst-libs/gst/d3d11/gstd3d11device.c +++ b/gst-libs/gst/d3d11/gstd3d11device.c @@ -26,6 +26,7 @@ #include "gstd3d11utils.h" #include "gstd3d11format.h" #include "gstd3d11_private.h" +#include "gstd3d11memory.h" #include #include @@ -165,7 +166,10 @@ gst_d3d11_device_enable_d3d11_debug (void) g_once_init_leave (&_init, 1); } - return ! !d3d11_debug_module; + if (d3d11_debug_module) + return TRUE; + + return FALSE; } static inline GstDebugLevel @@ -257,7 +261,8 @@ gst_d3d11_device_enable_dxgi_debug (void) if (dxgi_debug_module) g_module_symbol (dxgi_debug_module, "DXGIGetDebugInterface", (gpointer *) & GstDXGIGetDebugInterface); - ret = ! !GstDXGIGetDebugInterface; + if (GstDXGIGetDebugInterface) + ret = TRUE; #elif (GST_D3D11_DXGI_HEADER_VERSION >= 3) ret = TRUE; #endif @@ -402,6 +407,8 @@ gst_d3d11_device_class_init (GstD3D11DeviceClass * klass) g_param_spec_int64 ("adapter-luid", "Adapter LUID", "DXGI Adapter LUID (Locally Unique Identifier) of created device", 0, G_MAXINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + gst_d3d11_memory_init_once (); } static void diff --git a/gst-libs/gst/d3d11/gstd3d11memory.c b/gst-libs/gst/d3d11/gstd3d11memory.c index 1196a719d8..7629049b70 100644 --- a/gst-libs/gst/d3d11/gstd3d11memory.c +++ b/gst-libs/gst/d3d11/gstd3d11memory.c @@ -30,16 +30,15 @@ GST_DEBUG_CATEGORY_STATIC (gst_d3d11_allocator_debug); #define GST_CAT_DEFAULT gst_d3d11_allocator_debug -#define GST_D3D11_ALLOCATOR_GET_LOCK(a) (&(GST_D3D11_ALLOCATOR_CAST(a)->priv->lock)) -#define GST_D3D11_ALLOCATOR_LOCK(a) g_mutex_lock(GST_D3D11_ALLOCATOR_GET_LOCK(a)) -#define GST_D3D11_ALLOCATOR_UNLOCK(a) g_mutex_unlock(GST_D3D11_ALLOCATOR_GET_LOCK(a)) +static GstAllocator *_d3d11_memory_allocator; -#define GST_D3D11_MEMORY_CAST(m) ((GstD3D11Memory *) m) -#define GST_D3D11_MEMORY_GET_LOCK(m) (&(GST_D3D11_MEMORY_CAST(m)->priv->lock)) -#define GST_D3D11_MEMORY_LOCK(m) g_mutex_lock(GST_D3D11_MEMORY_GET_LOCK(m)) -#define GST_D3D11_MEMORY_UNLOCK(m) g_mutex_unlock(GST_D3D11_MEMORY_GET_LOCK(m)) - -#define GST_D3D11_MEMORY_NAME "D3D11Memory" +/* GstD3D11AllocationParams */ +static void gst_d3d11_allocation_params_init (GType type); +G_DEFINE_BOXED_TYPE_WITH_CODE (GstD3D11AllocationParams, + gst_d3d11_allocation_params, + (GBoxedCopyFunc) gst_d3d11_allocation_params_copy, + (GBoxedFreeFunc) gst_d3d11_allocation_params_free, + gst_d3d11_allocation_params_init (g_define_type_id)); /** * gst_d3d11_allocation_params_new: @@ -220,7 +219,7 @@ gst_d3d11_allocation_params_compare (const GstD3D11AllocationParams * p1, } static void -_init_alloc_params (GType type) +gst_d3d11_allocation_params_init (GType type) { static GstValueTable table = { 0, (GstValueCompareFunc) gst_d3d11_allocation_params_compare, @@ -231,31 +230,20 @@ _init_alloc_params (GType type) gst_value_register (&table); } -G_DEFINE_BOXED_TYPE_WITH_CODE (GstD3D11AllocationParams, - gst_d3d11_allocation_params, - (GBoxedCopyFunc) gst_d3d11_allocation_params_copy, - (GBoxedFreeFunc) gst_d3d11_allocation_params_free, - _init_alloc_params (g_define_type_id)); - -typedef enum -{ - GST_D3D11_MEMORY_TYPE_TEXTURE = 0, - GST_D3D11_MEMORY_TYPE_ARRAY = 1, - GST_D3D11_MEMORY_TYPE_STAGING = 2, -} GstD3D11MemoryType; +/* GstD3D11Memory */ +#define GST_D3D11_MEMORY_GET_LOCK(m) (&(GST_D3D11_MEMORY_CAST(m)->priv->lock)) +#define GST_D3D11_MEMORY_LOCK(m) g_mutex_lock(GST_D3D11_MEMORY_GET_LOCK(m)) +#define GST_D3D11_MEMORY_UNLOCK(m) g_mutex_unlock(GST_D3D11_MEMORY_GET_LOCK(m)) struct _GstD3D11MemoryPrivate { - GstD3D11MemoryType type; - ID3D11Texture2D *texture; + ID3D11Texture2D *staging; + D3D11_TEXTURE2D_DESC desc; - /* valid only for array typed memory */ guint subresource_index; - ID3D11Texture2D *staging; - ID3D11ShaderResourceView *shader_resource_view[GST_VIDEO_MAX_PLANES]; guint num_shader_resource_views; @@ -268,34 +256,15 @@ struct _GstD3D11MemoryPrivate D3D11_MAPPED_SUBRESOURCE map; + GMutex lock; gint cpu_map_count; }; -struct _GstD3D11AllocatorPrivate -{ - /* parent texture when array typed memory is used */ - ID3D11Texture2D *texture; - GArray *array_in_use; - GArray *decoder_output_view_array; - GArray *processor_input_view_array; - - /* Count the number of array textures in use */ - guint num_array_textures_in_use; - guint array_texture_size; - - GMutex lock; - GCond cond; - - gboolean flushing; -}; - -#define gst_d3d11_allocator_parent_class parent_class -G_DEFINE_TYPE_WITH_PRIVATE (GstD3D11Allocator, - gst_d3d11_allocator, GST_TYPE_ALLOCATOR); +GST_DEFINE_MINI_OBJECT_TYPE (GstD3D11Memory, gst_d3d11_memory); static inline D3D11_MAP -gst_map_flags_to_d3d11 (GstMapFlags flags) +gst_d3d11_map_flags_to_d3d11 (GstMapFlags flags) { if ((flags & GST_MAP_READWRITE) == GST_MAP_READWRITE) return D3D11_MAP_READ_WRITE; @@ -310,7 +279,7 @@ gst_map_flags_to_d3d11 (GstMapFlags flags) } static ID3D11Texture2D * -create_staging_texture (GstD3D11Device * device, +gst_d3d11_allocate_staging_texture (GstD3D11Device * device, const D3D11_TEXTURE2D_DESC * ref) { D3D11_TEXTURE2D_DESC desc = { 0, }; @@ -337,101 +306,74 @@ create_staging_texture (GstD3D11Device * device, } static gboolean -map_cpu_access_data (GstD3D11Memory * dmem, D3D11_MAP map_type) +gst_d3d11_memory_map_cpu_access (GstD3D11Memory * dmem, D3D11_MAP map_type) { GstD3D11MemoryPrivate *priv = dmem->priv; HRESULT hr; - gboolean ret = TRUE; - ID3D11Resource *texture = (ID3D11Resource *) priv->texture; ID3D11Resource *staging = (ID3D11Resource *) priv->staging; ID3D11DeviceContext *device_context = gst_d3d11_device_get_device_context_handle (dmem->device); gst_d3d11_device_lock (dmem->device); - if (GST_MEMORY_FLAG_IS_SET (dmem, GST_D3D11_MEMORY_TRANSFER_NEED_DOWNLOAD)) { - ID3D11DeviceContext_CopySubresourceRegion (device_context, - staging, 0, 0, 0, 0, texture, priv->subresource_index, NULL); - } - hr = ID3D11DeviceContext_Map (device_context, staging, 0, map_type, 0, &priv->map); + gst_d3d11_device_unlock (dmem->device); if (!gst_d3d11_result (hr, dmem->device)) { GST_ERROR_OBJECT (GST_MEMORY_CAST (dmem)->allocator, "Failed to map staging texture (0x%x)", (guint) hr); - ret = FALSE; + return FALSE; } + return TRUE; +} + +static void +gst_d3d11_memory_upload (GstD3D11Memory * dmem) +{ + GstD3D11MemoryPrivate *priv = dmem->priv; + ID3D11DeviceContext *device_context; + + if (!priv->staging || priv->staging == priv->texture || + !GST_MEMORY_FLAG_IS_SET (dmem, GST_D3D11_MEMORY_TRANSFER_NEED_UPLOAD)) + return; + + device_context = gst_d3d11_device_get_device_context_handle (dmem->device); + gst_d3d11_device_lock (dmem->device); + ID3D11DeviceContext_CopySubresourceRegion (device_context, + (ID3D11Resource *) priv->texture, priv->subresource_index, 0, 0, 0, + (ID3D11Resource *) priv->staging, 0, NULL); gst_d3d11_device_unlock (dmem->device); +} - return ret; +static void +gst_d3d11_memory_download (GstD3D11Memory * dmem) +{ + GstD3D11MemoryPrivate *priv = dmem->priv; + ID3D11DeviceContext *device_context; + + if (!priv->staging || priv->staging == priv->texture || + !GST_MEMORY_FLAG_IS_SET (dmem, GST_D3D11_MEMORY_TRANSFER_NEED_DOWNLOAD)) + return; + + device_context = gst_d3d11_device_get_device_context_handle (dmem->device); + gst_d3d11_device_lock (dmem->device); + ID3D11DeviceContext_CopySubresourceRegion (device_context, + (ID3D11Resource *) priv->staging, 0, 0, 0, 0, + (ID3D11Resource *) priv->texture, priv->subresource_index, NULL); + gst_d3d11_device_unlock (dmem->device); } static gpointer -gst_d3d11_memory_map_staging (GstMemory * mem, GstMapFlags flags) +gst_d3d11_memory_map_full (GstMemory * mem, GstMapInfo * info, gsize maxsize) { - GstD3D11Memory *dmem = (GstD3D11Memory *) mem; + GstD3D11Memory *dmem = GST_D3D11_MEMORY_CAST (mem); GstD3D11MemoryPrivate *priv = dmem->priv; - - GST_D3D11_MEMORY_LOCK (dmem); - if (priv->cpu_map_count == 0) { - ID3D11DeviceContext *device_context = - gst_d3d11_device_get_device_context_handle (dmem->device); - D3D11_MAP map_type; - HRESULT hr; - gboolean ret = TRUE; - - map_type = gst_map_flags_to_d3d11 (flags); - - gst_d3d11_device_lock (dmem->device); - hr = ID3D11DeviceContext_Map (device_context, - (ID3D11Resource *) priv->texture, 0, map_type, 0, &priv->map); - if (!gst_d3d11_result (hr, dmem->device)) { - GST_ERROR_OBJECT (GST_MEMORY_CAST (dmem)->allocator, - "Failed to map staging texture (0x%x)", (guint) hr); - ret = FALSE; - } - gst_d3d11_device_unlock (dmem->device); - - if (!ret) { - GST_D3D11_MEMORY_UNLOCK (dmem); - return NULL; - } - } - - priv->cpu_map_count++; - GST_D3D11_MEMORY_UNLOCK (dmem); - - return dmem->priv->map.pData; -} - -static gpointer -gst_d3d11_memory_map (GstMemory * mem, gsize maxsize, GstMapFlags flags) -{ - GstD3D11Memory *dmem = (GstD3D11Memory *) mem; - GstD3D11MemoryPrivate *priv = dmem->priv; - - if (priv->type == GST_D3D11_MEMORY_TYPE_STAGING) { - if ((flags & GST_MAP_D3D11) == GST_MAP_D3D11) - return priv->texture; - - return gst_d3d11_memory_map_staging (mem, flags); - } + GstMapFlags flags = info->flags; GST_D3D11_MEMORY_LOCK (dmem); if ((flags & GST_MAP_D3D11) == GST_MAP_D3D11) { - if (priv->staging && - GST_MEMORY_FLAG_IS_SET (dmem, GST_D3D11_MEMORY_TRANSFER_NEED_UPLOAD)) { - ID3D11DeviceContext *device_context = - gst_d3d11_device_get_device_context_handle (dmem->device); - - gst_d3d11_device_lock (dmem->device); - ID3D11DeviceContext_CopySubresourceRegion (device_context, - (ID3D11Resource *) priv->texture, priv->subresource_index, 0, 0, 0, - (ID3D11Resource *) priv->staging, 0, NULL); - gst_d3d11_device_unlock (dmem->device); - } - + gst_d3d11_memory_upload (dmem); GST_MEMORY_FLAG_UNSET (dmem, GST_D3D11_MEMORY_TRANSFER_NEED_UPLOAD); if ((flags & GST_MAP_WRITE) == GST_MAP_WRITE) @@ -448,7 +390,8 @@ gst_d3d11_memory_map (GstMemory * mem, gsize maxsize, GstMapFlags flags) /* Allocate staging texture for CPU access */ if (!priv->staging) { - priv->staging = create_staging_texture (dmem->device, &priv->desc); + priv->staging = gst_d3d11_allocate_staging_texture (dmem->device, + &priv->desc); if (!priv->staging) { GST_ERROR_OBJECT (mem->allocator, "Couldn't create staging texture"); GST_D3D11_MEMORY_UNLOCK (dmem); @@ -460,9 +403,10 @@ gst_d3d11_memory_map (GstMemory * mem, gsize maxsize, GstMapFlags flags) GST_MINI_OBJECT_FLAG_SET (mem, GST_D3D11_MEMORY_TRANSFER_NEED_DOWNLOAD); } - map_type = gst_map_flags_to_d3d11 (flags); + gst_d3d11_memory_download (dmem); + map_type = gst_d3d11_map_flags_to_d3d11 (flags); - if (!map_cpu_access_data (dmem, map_type)) { + if (!gst_d3d11_memory_map_cpu_access (dmem, map_type)) { GST_ERROR_OBJECT (mem->allocator, "Couldn't map staging texture"); GST_D3D11_MEMORY_UNLOCK (dmem); @@ -470,8 +414,9 @@ gst_d3d11_memory_map (GstMemory * mem, gsize maxsize, GstMapFlags flags) } } - if ((flags & GST_MAP_WRITE) == GST_MAP_WRITE) + if ((flags & GST_MAP_WRITE) == GST_MAP_WRITE) { GST_MINI_OBJECT_FLAG_SET (mem, GST_D3D11_MEMORY_TRANSFER_NEED_UPLOAD); + } GST_MEMORY_FLAG_UNSET (mem, GST_D3D11_MEMORY_TRANSFER_NEED_DOWNLOAD); @@ -482,16 +427,13 @@ gst_d3d11_memory_map (GstMemory * mem, gsize maxsize, GstMapFlags flags) } static void -unmap_cpu_access_data (GstD3D11Memory * dmem) +gst_d3d11_memory_unmap_cpu_access (GstD3D11Memory * dmem) { GstD3D11MemoryPrivate *priv = dmem->priv; ID3D11Resource *staging = (ID3D11Resource *) priv->staging; ID3D11DeviceContext *device_context = gst_d3d11_device_get_device_context_handle (dmem->device); - if (priv->type == GST_D3D11_MEMORY_TYPE_STAGING) - staging = (ID3D11Resource *) priv->texture; - gst_d3d11_device_lock (dmem->device); ID3D11DeviceContext_Unmap (device_context, staging, 0); gst_d3d11_device_unlock (dmem->device); @@ -500,21 +442,19 @@ unmap_cpu_access_data (GstD3D11Memory * dmem) static void gst_d3d11_memory_unmap_full (GstMemory * mem, GstMapInfo * info) { - GstD3D11Memory *dmem = (GstD3D11Memory *) mem; + GstD3D11Memory *dmem = GST_D3D11_MEMORY_CAST (mem); GstD3D11MemoryPrivate *priv = dmem->priv; GST_D3D11_MEMORY_LOCK (dmem); if ((info->flags & GST_MAP_D3D11) == GST_MAP_D3D11) { - if (priv->type != GST_D3D11_MEMORY_TYPE_STAGING && - (info->flags & GST_MAP_WRITE) == GST_MAP_WRITE) + if ((info->flags & GST_MAP_WRITE) == GST_MAP_WRITE) GST_MINI_OBJECT_FLAG_SET (mem, GST_D3D11_MEMORY_TRANSFER_NEED_DOWNLOAD); GST_D3D11_MEMORY_UNLOCK (dmem); return; } - if (priv->type != GST_D3D11_MEMORY_TYPE_STAGING && - (info->flags & GST_MAP_WRITE)) + if ((info->flags & GST_MAP_WRITE) == GST_MAP_WRITE) GST_MINI_OBJECT_FLAG_SET (mem, GST_D3D11_MEMORY_TRANSFER_NEED_UPLOAD); priv->cpu_map_count--; @@ -523,8 +463,7 @@ gst_d3d11_memory_unmap_full (GstMemory * mem, GstMapInfo * info) return; } - unmap_cpu_access_data (dmem); - + gst_d3d11_memory_unmap_cpu_access (dmem); GST_D3D11_MEMORY_UNLOCK (dmem); } @@ -535,181 +474,153 @@ gst_d3d11_memory_share (GstMemory * mem, gssize offset, gssize size) return NULL; } -static GstMemory * -gst_d3d11_allocator_dummy_alloc (GstAllocator * allocator, gsize size, - GstAllocationParams * params) -{ - g_return_val_if_reached (NULL); -} - -static void -gst_d3d11_allocator_free (GstAllocator * allocator, GstMemory * mem) -{ - GstD3D11Allocator *self = GST_D3D11_ALLOCATOR (allocator); - GstD3D11AllocatorPrivate *priv = self->priv; - GstD3D11Memory *dmem = (GstD3D11Memory *) mem; - GstD3D11MemoryPrivate *dmem_priv = dmem->priv; - gint i; - - if (priv->array_in_use) { - GST_D3D11_ALLOCATOR_LOCK (self); - g_array_index (priv->array_in_use, - guint8, dmem_priv->subresource_index) = 0; - priv->num_array_textures_in_use--; - g_cond_broadcast (&priv->cond); - GST_D3D11_ALLOCATOR_UNLOCK (self); - } - - for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) { - if (dmem_priv->render_target_view[i]) - ID3D11RenderTargetView_Release (dmem_priv->render_target_view[i]); - - if (dmem_priv->shader_resource_view[i]) - ID3D11ShaderResourceView_Release (dmem_priv->shader_resource_view[i]); - } - - if (dmem_priv->decoder_output_view) - ID3D11VideoDecoderOutputView_Release (dmem_priv->decoder_output_view); - - if (dmem_priv->processor_input_view) - ID3D11VideoProcessorInputView_Release (dmem_priv->processor_input_view); - - if (dmem_priv->processor_output_view) - ID3D11VideoProcessorOutputView_Release (dmem_priv->processor_output_view); - - if (dmem_priv->texture) - ID3D11Texture2D_Release (dmem_priv->texture); - - if (dmem_priv->staging) - ID3D11Texture2D_Release (dmem_priv->staging); - - gst_clear_object (&dmem->device); - g_mutex_clear (&dmem_priv->lock); - g_free (dmem->priv); - g_free (dmem); -} - -static void -gst_d3d11_allocator_dispose (GObject * object) -{ - GstD3D11Allocator *alloc = GST_D3D11_ALLOCATOR (object); - GstD3D11AllocatorPrivate *priv = alloc->priv; - - g_clear_pointer (&priv->decoder_output_view_array, g_array_unref); - g_clear_pointer (&priv->processor_input_view_array, g_array_unref); - - if (priv->texture) { - ID3D11Texture2D_Release (priv->texture); - priv->texture = NULL; - } - - gst_clear_object (&alloc->device); - - G_OBJECT_CLASS (parent_class)->dispose (object); -} - -static void -gst_d3d11_allocator_finalize (GObject * object) -{ - GstD3D11Allocator *alloc = GST_D3D11_ALLOCATOR (object); - GstD3D11AllocatorPrivate *priv = alloc->priv; - - g_mutex_clear (&priv->lock); - g_cond_clear (&priv->cond); - - g_clear_pointer (&priv->array_in_use, g_array_unref); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static void -gst_d3d11_allocator_class_init (GstD3D11AllocatorClass * klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GstAllocatorClass *allocator_class = GST_ALLOCATOR_CLASS (klass); - - gobject_class->dispose = gst_d3d11_allocator_dispose; - gobject_class->finalize = gst_d3d11_allocator_finalize; - - allocator_class->alloc = gst_d3d11_allocator_dummy_alloc; - allocator_class->free = gst_d3d11_allocator_free; - - GST_DEBUG_CATEGORY_INIT (gst_d3d11_allocator_debug, "d3d11allocator", 0, - "d3d11allocator object"); -} - -static void -gst_d3d11_allocator_init (GstD3D11Allocator * allocator) -{ - GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator); - GstD3D11AllocatorPrivate *priv; - - alloc->mem_type = GST_D3D11_MEMORY_NAME; - alloc->mem_map = gst_d3d11_memory_map; - alloc->mem_unmap_full = gst_d3d11_memory_unmap_full; - alloc->mem_share = gst_d3d11_memory_share; - /* fallback copy */ - - GST_OBJECT_FLAG_SET (alloc, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC); - - priv = gst_d3d11_allocator_get_instance_private (allocator); - g_mutex_init (&priv->lock); - g_cond_init (&priv->cond); - priv->array_texture_size = 1; - - allocator->priv = priv; -} - -/** - * gst_d3d11_allocator_new: - * @device: a #GstD3D11Device - * - * Returns: a newly created #GstD3D11Allocator - * - * Since: 1.20 - */ -GstD3D11Allocator * -gst_d3d11_allocator_new (GstD3D11Device * device) -{ - GstD3D11Allocator *allocator; - - g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL); - - allocator = g_object_new (GST_TYPE_D3D11_ALLOCATOR, NULL); - allocator->device = gst_object_ref (device); - - return allocator; -} - static gboolean -calculate_mem_size (GstD3D11Device * device, ID3D11Texture2D * texture, - const D3D11_TEXTURE2D_DESC * desc, D3D11_MAP map_type, - gint stride[GST_VIDEO_MAX_PLANES], gsize * size) +gst_d3d11_memory_update_size (GstMemory * mem) { - HRESULT hr; - gboolean ret = TRUE; - D3D11_MAPPED_SUBRESOURCE map; + GstD3D11Memory *dmem = GST_D3D11_MEMORY_CAST (mem); + GstD3D11MemoryPrivate *priv = dmem->priv; gsize offset[GST_VIDEO_MAX_PLANES]; - ID3D11DeviceContext *device_context = - gst_d3d11_device_get_device_context_handle (device); + gint stride[GST_VIDEO_MAX_PLANES]; + gsize size; + D3D11_TEXTURE2D_DESC *desc = &priv->desc; - gst_d3d11_device_lock (device); - hr = ID3D11DeviceContext_Map (device_context, - (ID3D11Resource *) texture, 0, map_type, 0, &map); + if (!priv->staging) { + priv->staging = gst_d3d11_allocate_staging_texture (dmem->device, + &priv->desc); + if (!priv->staging) { + GST_ERROR_OBJECT (mem->allocator, "Couldn't create staging texture"); + return FALSE; + } - if (!gst_d3d11_result (hr, device)) { - GST_ERROR_OBJECT (device, "Failed to map texture (0x%x)", (guint) hr); - gst_d3d11_device_unlock (device); + GST_MINI_OBJECT_FLAG_SET (mem, GST_D3D11_MEMORY_TRANSFER_NEED_DOWNLOAD); + } + + if (!gst_d3d11_memory_map_cpu_access (dmem, D3D11_MAP_READ_WRITE)) { + GST_ERROR_OBJECT (mem->allocator, "Couldn't map staging texture"); return FALSE; } - ret = gst_d3d11_dxgi_format_get_size (desc->Format, - desc->Width, desc->Height, map.RowPitch, offset, stride, size); + gst_d3d11_memory_unmap_cpu_access (dmem); - ID3D11DeviceContext_Unmap (device_context, (ID3D11Resource *) texture, 0); - gst_d3d11_device_unlock (device); + if (!gst_d3d11_dxgi_format_get_size (desc->Format, desc->Width, desc->Height, + priv->map.RowPitch, offset, stride, &size)) { + GST_ERROR_OBJECT (mem->allocator, "Couldn't calculate memory size"); + return FALSE; + } - return ret; + mem->maxsize = mem->size = size; + + return TRUE; +} + +/** + * gst_is_d3d11_memory: + * @mem: a #GstMemory + * + * Returns: whether @mem is a #GstD3D11Memory + * + * Since: 1.20 + */ +gboolean +gst_is_d3d11_memory (GstMemory * mem) +{ + return mem != NULL && mem->allocator != NULL && + (GST_IS_D3D11_ALLOCATOR (mem->allocator) || + GST_IS_D3D11_POOL_ALLOCATOR (mem->allocator)); +} + +/** + * gst_d3d11_memory_init_once: + * + * Initializes the Direct3D11 Texture allocator. It is safe to call + * this function multiple times. This must be called before any other + * GstD3D11Memory operation. + * + * Since: 1.20 + */ +void +gst_d3d11_memory_init_once (void) +{ + static gsize _init = 0; + + if (g_once_init_enter (&_init)) { + + GST_DEBUG_CATEGORY_INIT (gst_d3d11_allocator_debug, "d3d11allocator", 0, + "Direct3D11 Texture Allocator"); + + _d3d11_memory_allocator = g_object_new (GST_TYPE_D3D11_ALLOCATOR, NULL); + gst_object_ref_sink (_d3d11_memory_allocator); + + gst_allocator_register (GST_D3D11_MEMORY_NAME, _d3d11_memory_allocator); + g_once_init_leave (&_init, 1); + } +} + +/** + * gst_d3d11_memory_get_texture_handle: + * @mem: a #GstD3D11Memory + * + * Returns: (transfer none): a ID3D11Texture2D handle. Caller must not release + * returned handle. + * + * Since: 1.20 + */ +ID3D11Texture2D * +gst_d3d11_memory_get_texture_handle (GstD3D11Memory * mem) +{ + g_return_val_if_fail (gst_is_d3d11_memory (GST_MEMORY_CAST (mem)), NULL); + + return mem->priv->texture; +} + +/** + * gst_d3d11_memory_get_subresource_index: + * @mem: a #GstD3D11Memory + * + * Returns: subresource index corresponding to @mem. + * + * Since: 1.20 + */ +guint +gst_d3d11_memory_get_subresource_index (GstD3D11Memory * mem) +{ + g_return_val_if_fail (gst_is_d3d11_memory (GST_MEMORY_CAST (mem)), 0); + + return mem->priv->subresource_index; +} + +/** + * gst_d3d11_memory_get_texture_desc: + * @mem: a #GstD3D11Memory + * @desc: (out): a D3D11_TEXTURE2D_DESC + * + * Fill @desc with D3D11_TEXTURE2D_DESC for ID3D11Texture2D + * + * Returns: %TRUE if successeed + * + * Since: 1.20 + */ +gboolean +gst_d3d11_memory_get_texture_desc (GstD3D11Memory * mem, + D3D11_TEXTURE2D_DESC * desc) +{ + g_return_val_if_fail (gst_is_d3d11_memory (GST_MEMORY_CAST (mem)), FALSE); + g_return_val_if_fail (desc != NULL, FALSE); + + *desc = mem->priv->desc; + + return TRUE; +} + +gboolean +gst_d3d11_memory_get_texture_stride (GstD3D11Memory * mem, guint * stride) +{ + g_return_val_if_fail (gst_is_d3d11_memory (GST_MEMORY_CAST (mem)), FALSE); + g_return_val_if_fail (stride != NULL, FALSE); + + *stride = mem->priv->map.RowPitch; + + return TRUE; } static gboolean @@ -805,6 +716,83 @@ error: return FALSE; } +static gboolean +gst_d3d11_memory_ensure_shader_resource_view (GstD3D11Memory * mem) +{ + GstD3D11MemoryPrivate *priv = mem->priv; + gboolean ret = FALSE; + + if (!(priv->desc.BindFlags & D3D11_BIND_SHADER_RESOURCE)) { + GST_LOG_OBJECT (GST_MEMORY_CAST (mem)->allocator, + "Need BindFlags, current flag 0x%x", priv->desc.BindFlags); + return FALSE; + } + + GST_D3D11_MEMORY_LOCK (mem); + if (priv->num_shader_resource_views) { + ret = TRUE; + goto done; + } + + ret = create_shader_resource_views (mem); + +done: + GST_D3D11_MEMORY_UNLOCK (mem); + + return ret; +} + +/** + * gst_d3d11_memory_get_shader_resource_view_size: + * @mem: a #GstD3D11Memory + * + * Returns: the number of ID3D11ShaderResourceView that can be used + * for processing GPU operation with @mem + * + * Since: 1.20 + */ +guint +gst_d3d11_memory_get_shader_resource_view_size (GstD3D11Memory * mem) +{ + g_return_val_if_fail (gst_is_d3d11_memory (GST_MEMORY_CAST (mem)), 0); + + if (!gst_d3d11_memory_ensure_shader_resource_view (mem)) + return 0; + + return mem->priv->num_shader_resource_views; +} + +/** + * gst_d3d11_memory_get_shader_resource_view: + * @mem: a #GstD3D11Memory + * @index: the index of the ID3D11ShaderResourceView + * + * Returns: (transfer none) (nullable): a pointer to the + * ID3D11ShaderResourceView or %NULL if ID3D11ShaderResourceView is unavailable + * for @index + * + * Since: 1.20 + */ +ID3D11ShaderResourceView * +gst_d3d11_memory_get_shader_resource_view (GstD3D11Memory * mem, guint index) +{ + GstD3D11MemoryPrivate *priv; + + g_return_val_if_fail (gst_is_d3d11_memory (GST_MEMORY_CAST (mem)), NULL); + + if (!gst_d3d11_memory_ensure_shader_resource_view (mem)) + return NULL; + + priv = mem->priv; + + if (index >= priv->num_shader_resource_views) { + GST_ERROR ("Invalid SRV index %d", index); + return NULL; + } + + return priv->shader_resource_view[index]; +} + static gboolean create_render_target_views (GstD3D11Memory * mem) { @@ -886,410 +874,6 @@ error: return FALSE; } -static void -gst_d3d11_decoder_output_view_clear (ID3D11VideoDecoderOutputView ** view) -{ - if (view && *view) { - ID3D11VideoDecoderOutputView_Release (*view); - *view = NULL; - } -} - -static void -gst_d3d11_processor_input_view_clear (ID3D11VideoProcessorInputView ** view) -{ - if (view && *view) { - ID3D11VideoProcessorInputView_Release (*view); - *view = NULL; - } -} - -static gboolean -check_bind_flags_for_processor_input_view (guint bind_flags) -{ - static const guint compatible_flags = (D3D11_BIND_DECODER | - D3D11_BIND_VIDEO_ENCODER | D3D11_BIND_RENDER_TARGET | - D3D11_BIND_UNORDERED_ACCESS); - - if (bind_flags == 0) - return TRUE; - - if ((bind_flags & compatible_flags) != 0) - return TRUE; - - return FALSE; -} - -/** - * gst_d3d11_allocator_alloc: - * @allocator: a #GstD3D11Allocator - * @desc: a D3D11_TEXTURE2D_DESC struct - * @flags: a #GstD3D11AllocationFlags - * @size: a size of CPU accesible memory - * - * Returns: a newly allocated #GstD3D11Memory with given parameters. - * - * Since: 1.20 - */ -GstMemory * -gst_d3d11_allocator_alloc (GstD3D11Allocator * allocator, - const D3D11_TEXTURE2D_DESC * desc, GstD3D11AllocationFlags flags, - gsize size) -{ - GstD3D11Memory *mem; - GstD3D11Device *device; - ID3D11Texture2D *texture = NULL; - guint index_to_use = 0; - GstD3D11AllocatorPrivate *priv; - GstD3D11MemoryType type = GST_D3D11_MEMORY_TYPE_TEXTURE; - HRESULT hr; - ID3D11Device *device_handle; - - g_return_val_if_fail (GST_IS_D3D11_ALLOCATOR (allocator), NULL); - g_return_val_if_fail (desc != NULL, NULL); - g_return_val_if_fail (size > 0, NULL); - - priv = allocator->priv; - device = allocator->device; - device_handle = gst_d3d11_device_get_device_handle (device); - - if ((flags & GST_D3D11_ALLOCATION_FLAG_TEXTURE_ARRAY)) { - gint i; - - do_again: - GST_D3D11_ALLOCATOR_LOCK (allocator); - if (priv->flushing) { - GST_DEBUG_OBJECT (allocator, "we are flushing"); - GST_D3D11_ALLOCATOR_UNLOCK (allocator); - - return NULL; - } - - if (!priv->array_in_use) { - priv->array_in_use = g_array_sized_new (FALSE, - TRUE, sizeof (guint8), desc->ArraySize); - g_array_set_size (priv->array_in_use, desc->ArraySize); - - priv->array_texture_size = desc->ArraySize; - - if ((desc->BindFlags & D3D11_BIND_DECODER) == D3D11_BIND_DECODER && - !priv->decoder_output_view_array) { - priv->decoder_output_view_array = g_array_sized_new (FALSE, - TRUE, sizeof (ID3D11VideoDecoderOutputView *), desc->ArraySize); - g_array_set_clear_func (priv->decoder_output_view_array, - (GDestroyNotify) gst_d3d11_decoder_output_view_clear); - g_array_set_size (priv->decoder_output_view_array, desc->ArraySize); - } - - if (check_bind_flags_for_processor_input_view (desc->BindFlags)) { - priv->processor_input_view_array = g_array_sized_new (FALSE, - TRUE, sizeof (ID3D11VideoProcessorInputView *), desc->ArraySize); - g_array_set_clear_func (priv->processor_input_view_array, - (GDestroyNotify) gst_d3d11_processor_input_view_clear); - g_array_set_size (priv->processor_input_view_array, desc->ArraySize); - } - } - - for (i = 0; i < desc->ArraySize; i++) { - if (!g_array_index (priv->array_in_use, guint8, i)) { - index_to_use = i; - break; - } - } - - if (i == desc->ArraySize) { - GST_DEBUG_OBJECT (allocator, "All elements in array are used now"); - g_cond_wait (&priv->cond, &priv->lock); - GST_D3D11_ALLOCATOR_UNLOCK (allocator); - goto do_again; - } - - g_array_index (priv->array_in_use, guint8, index_to_use) = 1; - priv->num_array_textures_in_use++; - - GST_D3D11_ALLOCATOR_UNLOCK (allocator); - - if (!priv->texture) { - hr = ID3D11Device_CreateTexture2D (device_handle, desc, NULL, - &priv->texture); - if (!gst_d3d11_result (hr, device)) { - GST_ERROR_OBJECT (allocator, "Couldn't create texture"); - goto error; - } - } - - ID3D11Texture2D_AddRef (priv->texture); - texture = priv->texture; - - type = GST_D3D11_MEMORY_TYPE_ARRAY; - } else { - hr = ID3D11Device_CreateTexture2D (device_handle, desc, NULL, &texture); - if (!gst_d3d11_result (hr, device)) { - GST_ERROR_OBJECT (allocator, "Couldn't create texture"); - goto error; - } - } - - mem = g_new0 (GstD3D11Memory, 1); - mem->priv = g_new0 (GstD3D11MemoryPrivate, 1); - - gst_memory_init (GST_MEMORY_CAST (mem), - 0, GST_ALLOCATOR_CAST (allocator), NULL, size, 0, 0, size); - - g_mutex_init (&mem->priv->lock); - mem->priv->texture = texture; - mem->priv->desc = *desc; - mem->priv->type = type; - mem->priv->subresource_index = index_to_use; - mem->device = gst_object_ref (device); - - return GST_MEMORY_CAST (mem); - -error: - if (texture) - ID3D11Texture2D_Release (texture); - - return NULL; -} - -/** - * gst_d3d11_allocator_alloc_staging: - * @allocator: a #GstD3D11Allocator - * @desc: a D3D11_TEXTURE2D_DESC struct - * @flags: a #GstD3D11AllocationFlags - * @stride: (out): a stride of CPU accesible memory - * - * Returns: a newly allocated #GstD3D11Memory with given parameters. - * Returned #GstD3D11Memory can be used only for staging texture. - * - * Since: 1.20 - */ -GstMemory * -gst_d3d11_allocator_alloc_staging (GstD3D11Allocator * allocator, - const D3D11_TEXTURE2D_DESC * desc, GstD3D11AllocationFlags flags, - gint * stride) -{ - GstD3D11Memory *mem; - GstD3D11Device *device; - ID3D11Texture2D *texture = NULL; - gsize mem_size = 0; - gint mem_stride[GST_VIDEO_MAX_PLANES]; - - g_return_val_if_fail (GST_IS_D3D11_ALLOCATOR (allocator), NULL); - g_return_val_if_fail (desc != NULL, NULL); - - device = allocator->device; - - texture = create_staging_texture (device, desc); - if (!texture) { - GST_ERROR_OBJECT (allocator, "Couldn't create staging texture"); - goto error; - } - - if (!calculate_mem_size (device, - texture, desc, D3D11_MAP_READ, mem_stride, &mem_size)) { - GST_ERROR_OBJECT (allocator, "Couldn't calculate staging texture size"); - goto error; - } - - mem = g_new0 (GstD3D11Memory, 1); - mem->priv = g_new0 (GstD3D11MemoryPrivate, 1); - - gst_memory_init (GST_MEMORY_CAST (mem), - 0, GST_ALLOCATOR_CAST (allocator), NULL, mem_size, 0, 0, mem_size); - - g_mutex_init (&mem->priv->lock); - mem->priv->texture = texture; - mem->priv->desc = *desc; - mem->priv->type = GST_D3D11_MEMORY_TYPE_STAGING; - mem->device = gst_object_ref (device); - - /* every plan will have identical size */ - if (stride) - *stride = mem_stride[0]; - - return GST_MEMORY_CAST (mem); - -error: - if (texture) - ID3D11Texture2D_Release (texture); - - return NULL; -} - -/** - * gst_d3d11_allocator_set_flushing: - * @allocator: a #GstD3D11Allocator - * @flusing: whether to start or stop flusing - * - * Enable or disable the flushing state of @allocator. - * - * Since: 1.20 - */ -void -gst_d3d11_allocator_set_flushing (GstD3D11Allocator * allocator, - gboolean flushing) -{ - GstD3D11AllocatorPrivate *priv; - - g_return_if_fail (GST_IS_D3D11_ALLOCATOR (allocator)); - - priv = allocator->priv; - - GST_D3D11_ALLOCATOR_LOCK (allocator); - priv->flushing = flushing; - g_cond_broadcast (&priv->cond); - GST_D3D11_ALLOCATOR_UNLOCK (allocator); -} - -/** - * gst_is_d3d11_memory: - * @mem: a #GstMemory - * - * Returns: whether @mem is a #GstD3D11Memory - * - * Since: 1.20 - */ -gboolean -gst_is_d3d11_memory (GstMemory * mem) -{ - return mem != NULL && mem->allocator != NULL && - GST_IS_D3D11_ALLOCATOR (mem->allocator); -} - -/** - * gst_d3d11_memory_get_texture_handle: - * @mem: a #GstD3D11Memory - * - * Returns: (transfer none): a ID3D11Texture2D handle. Caller must not release - * returned handle. - * - * Since: 1.20 - */ -ID3D11Texture2D * -gst_d3d11_memory_get_texture_handle (GstD3D11Memory * mem) -{ - g_return_val_if_fail (gst_is_d3d11_memory (GST_MEMORY_CAST (mem)), NULL); - - return mem->priv->texture; -} - -/** - * gst_d3d11_memory_get_subresource_index: - * @mem: a #GstD3D11Memory - * - * Returns: subresource index corresponding to @mem. - * - * Since: 1.20 - */ -guint -gst_d3d11_memory_get_subresource_index (GstD3D11Memory * mem) -{ - g_return_val_if_fail (gst_is_d3d11_memory (GST_MEMORY_CAST (mem)), 0); - - return mem->priv->subresource_index; -} - -/** - * gst_d3d11_memory_get_texture_desc: - * @mem: a #GstD3D11Memory - * @desc: (out): a D3D11_TEXTURE2D_DESC - * - * Fill @desc with D3D11_TEXTURE2D_DESC for ID3D11Texture2D - * - * Returns: %TRUE if successeed - * - * Since: 1.20 - */ -gboolean -gst_d3d11_memory_get_texture_desc (GstD3D11Memory * mem, - D3D11_TEXTURE2D_DESC * desc) -{ - g_return_val_if_fail (gst_is_d3d11_memory (GST_MEMORY_CAST (mem)), FALSE); - g_return_val_if_fail (desc != NULL, FALSE); - - *desc = mem->priv->desc; - - return TRUE; -} - -static gboolean -gst_d3d11_memory_ensure_shader_resource_view (GstD3D11Memory * mem) -{ - GstD3D11MemoryPrivate *priv = mem->priv; - gboolean ret = FALSE; - - if (!(priv->desc.BindFlags & D3D11_BIND_SHADER_RESOURCE)) { - GST_LOG_OBJECT (GST_MEMORY_CAST (mem)->allocator, - "Need BindFlags, current flag 0x%x", priv->desc.BindFlags); - return FALSE; - } - - GST_D3D11_MEMORY_LOCK (mem); - if (priv->num_shader_resource_views) { - ret = TRUE; - goto done; - } - - ret = create_shader_resource_views (mem); - -done: - GST_D3D11_MEMORY_UNLOCK (mem); - - return ret; -} - -/** - * gst_d3d11_memory_get_shader_resource_view_size: - * @mem: a #GstD3D11Memory - * - * Returns: the number of ID3D11ShaderResourceView that can be used - * for processing GPU operation with @mem - * - * Since: 1.20 - */ -guint -gst_d3d11_memory_get_shader_resource_view_size (GstD3D11Memory * mem) -{ - g_return_val_if_fail (gst_is_d3d11_memory (GST_MEMORY_CAST (mem)), 0); - - if (!gst_d3d11_memory_ensure_shader_resource_view (mem)) - return 0; - - return mem->priv->num_shader_resource_views; -} - -/** - * gst_d3d11_memory_get_shader_resource_view: - * @mem: a #GstD3D11Memory - * @index: the index of the ID3D11ShaderResourceView - * - * Returns: (transfer none) (nullable): a pointer to the - * ID3D11ShaderResourceView or %NULL if ID3D11ShaderResourceView is unavailable - * for @index - * - * Since: 1.20 - */ -ID3D11ShaderResourceView * -gst_d3d11_memory_get_shader_resource_view (GstD3D11Memory * mem, guint index) -{ - GstD3D11MemoryPrivate *priv; - - g_return_val_if_fail (gst_is_d3d11_memory (GST_MEMORY_CAST (mem)), NULL); - - if (!gst_d3d11_memory_ensure_shader_resource_view (mem)) - return NULL; - - priv = mem->priv; - - if (index >= priv->num_shader_resource_views) { - GST_ERROR ("Invalid SRV index %d", index); - return NULL; - } - - return priv->shader_resource_view[index]; -} - static gboolean gst_d3d11_memory_ensure_render_target_view (GstD3D11Memory * mem) { @@ -1373,14 +957,11 @@ gst_d3d11_memory_ensure_decoder_output_view (GstD3D11Memory * mem, { GstD3D11MemoryPrivate *dmem_priv = mem->priv; GstD3D11Allocator *allocator; - GstD3D11AllocatorPrivate *priv; D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC desc; - ID3D11VideoDecoderOutputView *view = NULL; HRESULT hr; gboolean ret = FALSE; allocator = GST_D3D11_ALLOCATOR (GST_MEMORY_CAST (mem)->allocator); - priv = allocator->priv; if (!(dmem_priv->desc.BindFlags & D3D11_BIND_DECODER)) { GST_LOG_OBJECT (allocator, @@ -1403,32 +984,6 @@ gst_d3d11_memory_ensure_decoder_output_view (GstD3D11Memory * mem, } } - if (priv->decoder_output_view_array) { - GST_D3D11_ALLOCATOR_LOCK (allocator); - view = g_array_index (priv->decoder_output_view_array, - ID3D11VideoDecoderOutputView *, dmem_priv->subresource_index); - - if (view) { - ID3D11VideoDecoderOutputView_GetDesc (view, &desc); - /* Shouldn't happen because decoder will not reuse this allocator - * over different codec/profiles */ - if (!IsEqualGUID (&desc.DecodeProfile, decoder_profile)) { - GST_WARNING_OBJECT (allocator, - "Existing view has different decoder profile"); - ID3D11VideoDecoderOutputView_Release (view); - view = NULL; - g_array_index (priv->decoder_output_view_array, - ID3D11VideoDecoderOutputView *, - dmem_priv->subresource_index) = NULL; - } else { - /* Increase refcount and reuse existing view */ - dmem_priv->decoder_output_view = view; - ID3D11VideoDecoderOutputView_AddRef (view); - } - } - GST_D3D11_ALLOCATOR_UNLOCK (allocator); - } - if (dmem_priv->decoder_output_view) goto succeeded; @@ -1445,22 +1000,6 @@ gst_d3d11_memory_ensure_decoder_output_view (GstD3D11Memory * mem, goto done; } - /* Store view array for later reuse */ - if (priv->decoder_output_view_array) { - GST_D3D11_ALLOCATOR_LOCK (allocator); - view = g_array_index (priv->decoder_output_view_array, - ID3D11VideoDecoderOutputView *, dmem_priv->subresource_index); - - if (view) - ID3D11VideoDecoderOutputView_Release (view); - - g_array_index (priv->decoder_output_view_array, - ID3D11VideoDecoderOutputView *, dmem_priv->subresource_index) = - dmem_priv->decoder_output_view; - ID3D11VideoDecoderOutputView_AddRef (dmem_priv->decoder_output_view); - GST_D3D11_ALLOCATOR_UNLOCK (allocator); - } - succeeded: ret = TRUE; @@ -1495,6 +1034,22 @@ gst_d3d11_memory_get_decoder_output_view (GstD3D11Memory * mem, return mem->priv->decoder_output_view; } +static gboolean +check_bind_flags_for_processor_input_view (guint bind_flags) +{ + static const guint compatible_flags = (D3D11_BIND_DECODER | + D3D11_BIND_VIDEO_ENCODER | D3D11_BIND_RENDER_TARGET | + D3D11_BIND_UNORDERED_ACCESS); + + if (bind_flags == 0) + return TRUE; + + if ((bind_flags & compatible_flags) != 0) + return TRUE; + + return FALSE; +} + static gboolean gst_d3d11_memory_ensure_processor_input_view (GstD3D11Memory * mem, ID3D11VideoDevice * video_device, @@ -1502,14 +1057,11 @@ gst_d3d11_memory_ensure_processor_input_view (GstD3D11Memory * mem, { GstD3D11MemoryPrivate *dmem_priv = mem->priv; GstD3D11Allocator *allocator; - GstD3D11AllocatorPrivate *priv; D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC desc; - ID3D11VideoProcessorInputView *view = NULL; HRESULT hr; gboolean ret = FALSE; allocator = GST_D3D11_ALLOCATOR (GST_MEMORY_CAST (mem)->allocator); - priv = allocator->priv; if (!check_bind_flags_for_processor_input_view (dmem_priv->desc.BindFlags)) { GST_LOG_OBJECT (allocator, @@ -1518,22 +1070,6 @@ gst_d3d11_memory_ensure_processor_input_view (GstD3D11Memory * mem, } GST_D3D11_MEMORY_LOCK (mem); - if (dmem_priv->processor_input_view) - goto succeeded; - - if (priv->processor_input_view_array) { - GST_D3D11_ALLOCATOR_LOCK (allocator); - view = g_array_index (priv->processor_input_view_array, - ID3D11VideoProcessorInputView *, dmem_priv->subresource_index); - - /* Increase refcount and reuse existing view */ - if (view) { - dmem_priv->processor_input_view = view; - ID3D11VideoProcessorInputView_AddRef (view); - } - GST_D3D11_ALLOCATOR_UNLOCK (allocator); - } - if (dmem_priv->processor_input_view) goto succeeded; @@ -1551,22 +1087,6 @@ gst_d3d11_memory_ensure_processor_input_view (GstD3D11Memory * mem, goto done; } - /* Store view array for later reuse */ - if (priv->processor_input_view_array) { - GST_D3D11_ALLOCATOR_LOCK (allocator); - view = g_array_index (priv->processor_input_view_array, - ID3D11VideoProcessorInputView *, dmem_priv->subresource_index); - - if (view) - ID3D11VideoProcessorInputView_Release (view); - - g_array_index (priv->processor_input_view_array, - ID3D11VideoProcessorInputView *, dmem_priv->subresource_index) = - dmem_priv->processor_input_view; - ID3D11VideoProcessorInputView_AddRef (dmem_priv->processor_input_view); - GST_D3D11_ALLOCATOR_UNLOCK (allocator); - } - succeeded: ret = TRUE; @@ -1682,3 +1202,757 @@ gst_d3d11_memory_get_processor_output_view (GstD3D11Memory * mem, return mem->priv->processor_output_view; } + +/* GstD3D11Allocator */ +#define gst_d3d11_allocator_parent_class alloc_parent_class +G_DEFINE_TYPE (GstD3D11Allocator, gst_d3d11_allocator, GST_TYPE_ALLOCATOR); + +static GstMemory *gst_d3d11_allocator_dummy_alloc (GstAllocator * allocator, + gsize size, GstAllocationParams * params); +static void gst_d3d11_allocator_free (GstAllocator * allocator, + GstMemory * mem); + +static void +gst_d3d11_allocator_class_init (GstD3D11AllocatorClass * klass) +{ + GstAllocatorClass *allocator_class = GST_ALLOCATOR_CLASS (klass); + + allocator_class->alloc = gst_d3d11_allocator_dummy_alloc; + allocator_class->free = gst_d3d11_allocator_free; +} + +static void +gst_d3d11_allocator_init (GstD3D11Allocator * allocator) +{ + GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator); + + alloc->mem_type = GST_D3D11_MEMORY_NAME; + alloc->mem_map_full = gst_d3d11_memory_map_full; + alloc->mem_unmap_full = gst_d3d11_memory_unmap_full; + alloc->mem_share = gst_d3d11_memory_share; + /* fallback copy */ + + GST_OBJECT_FLAG_SET (alloc, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC); +} + +static GstMemory * +gst_d3d11_allocator_dummy_alloc (GstAllocator * allocator, gsize size, + GstAllocationParams * params) +{ + g_return_val_if_reached (NULL); +} + +static void +gst_d3d11_allocator_free (GstAllocator * allocator, GstMemory * mem) +{ + GstD3D11Memory *dmem = GST_D3D11_MEMORY_CAST (mem); + GstD3D11MemoryPrivate *dmem_priv = dmem->priv; + gint i; + + GST_LOG_OBJECT (allocator, "Free memory %p", mem); + + for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) { + if (dmem_priv->render_target_view[i]) + ID3D11RenderTargetView_Release (dmem_priv->render_target_view[i]); + + if (dmem_priv->shader_resource_view[i]) + ID3D11ShaderResourceView_Release (dmem_priv->shader_resource_view[i]); + } + + if (dmem_priv->decoder_output_view) + ID3D11VideoDecoderOutputView_Release (dmem_priv->decoder_output_view); + + if (dmem_priv->processor_input_view) + ID3D11VideoProcessorInputView_Release (dmem_priv->processor_input_view); + + if (dmem_priv->processor_output_view) + ID3D11VideoProcessorOutputView_Release (dmem_priv->processor_output_view); + + if (dmem_priv->texture) + ID3D11Texture2D_Release (dmem_priv->texture); + + if (dmem_priv->staging) + ID3D11Texture2D_Release (dmem_priv->staging); + + gst_clear_object (&dmem->device); + g_mutex_clear (&dmem_priv->lock); + g_free (dmem->priv); + g_free (dmem); +} + +static GstMemory * +gst_d3d11_allocator_alloc_wrapped (GstD3D11Allocator * self, + GstD3D11Device * device, const D3D11_TEXTURE2D_DESC * desc, + ID3D11Texture2D * texture) +{ + GstD3D11Memory *mem; + + mem = g_new0 (GstD3D11Memory, 1); + mem->priv = g_new0 (GstD3D11MemoryPrivate, 1); + + gst_memory_init (GST_MEMORY_CAST (mem), + 0, GST_ALLOCATOR_CAST (self), NULL, 0, 0, 0, 0); + g_mutex_init (&mem->priv->lock); + mem->priv->texture = texture; + mem->priv->desc = *desc; + mem->device = gst_object_ref (device); + + /* This is staging texture as well */ + if (desc->Usage == D3D11_USAGE_STAGING) { + mem->priv->staging = texture; + ID3D11Texture2D_AddRef (texture); + } + + return GST_MEMORY_CAST (mem); +} + +static GstMemory * +gst_d3d11_allocator_alloc_internal (GstD3D11Allocator * self, + GstD3D11Device * device, const D3D11_TEXTURE2D_DESC * desc) +{ + ID3D11Texture2D *texture = NULL; + ID3D11Device *device_handle; + HRESULT hr; + + device_handle = gst_d3d11_device_get_device_handle (device); + + hr = ID3D11Device_CreateTexture2D (device_handle, desc, NULL, &texture); + if (!gst_d3d11_result (hr, device)) { + GST_ERROR_OBJECT (self, "Couldn't create texture"); + return NULL; + } + + return gst_d3d11_allocator_alloc_wrapped (self, device, desc, texture); +} + +/** + * gst_d3d11_allocator_alloc: + * @allocator: a #GstD3D11Allocator + * @device: a #GstD3D11Device + * @desc: a D3D11_TEXTURE2D_DESC struct + * + * Returns: a newly allocated #GstD3D11Memory with given parameters. + * + * Since: 1.20 + */ +GstMemory * +gst_d3d11_allocator_alloc (GstD3D11Allocator * allocator, + GstD3D11Device * device, const D3D11_TEXTURE2D_DESC * desc) +{ + GstMemory *mem; + + g_return_val_if_fail (GST_IS_D3D11_ALLOCATOR (allocator), NULL); + g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL); + g_return_val_if_fail (desc != NULL, NULL); + + mem = gst_d3d11_allocator_alloc_internal (allocator, device, desc); + if (!mem) + return NULL; + + if (!gst_d3d11_memory_update_size (mem)) { + GST_ERROR_OBJECT (allocator, "Failed to calculate size"); + gst_memory_unref (mem); + return NULL; + } + + return mem; +} + +gboolean +gst_d3d11_allocator_set_active (GstD3D11Allocator * allocator, gboolean active) +{ + GstD3D11AllocatorClass *klass; + + g_return_val_if_fail (GST_IS_D3D11_ALLOCATOR (allocator), FALSE); + + klass = GST_D3D11_ALLOCATOR_GET_CLASS (allocator); + if (klass->set_actvie) + return klass->set_actvie (allocator, active); + + return TRUE; +} + +/* GstD3D11PoolAllocator */ +#define GST_D3D11_POOL_ALLOCATOR_LOCK(alloc) (g_rec_mutex_lock(&alloc->priv->lock)) +#define GST_D3D11_POOL_ALLOCATOR_UNLOCK(alloc) (g_rec_mutex_unlock(&alloc->priv->lock)) +#define GST_D3D11_POOL_ALLOCATOR_IS_FLUSHING(alloc) (g_atomic_int_get (&alloc->priv->flushing)) + +struct _GstD3D11PoolAllocatorPrivate +{ + /* parent texture when array typed memory is used */ + ID3D11Texture2D *texture; + D3D11_TEXTURE2D_DESC desc; + + /* All below member variables are analogous to that of GstBufferPool */ + GstAtomicQueue *queue; + GstPoll *poll; + + /* This lock will protect all below variables apart from atomic ones + * (identical to GstBufferPool::priv::rec_lock) */ + GRecMutex lock; + gboolean started; + gboolean active; + + /* atomic */ + gint outstanding; + guint max_mems; + guint cur_mems; + gboolean flushing; + + /* Calculated memory size, based on Direct3D11 staging texture map. + * Note that, we cannot know the actually staging texture memory size prior + * to map the staging texture because driver will likely require padding */ + gsize mem_size; +}; + +static void gst_d3d11_pool_allocator_dispose (GObject * object); +static void gst_d3d11_pool_allocator_finalize (GObject * object); + +static gboolean +gst_d3d11_pool_allocator_set_active (GstD3D11Allocator * allocator, + gboolean active); + +static gboolean gst_d3d11_pool_allocator_start (GstD3D11PoolAllocator * self); +static gboolean gst_d3d11_pool_allocator_stop (GstD3D11PoolAllocator * self); +static gboolean gst_d3d11_memory_release (GstMiniObject * mini_object); + +#define gst_d3d11_pool_allocator_parent_class pool_alloc_parent_class +G_DEFINE_TYPE_WITH_PRIVATE (GstD3D11PoolAllocator, + gst_d3d11_pool_allocator, GST_TYPE_D3D11_ALLOCATOR); + +static void +gst_d3d11_pool_allocator_class_init (GstD3D11PoolAllocatorClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstD3D11AllocatorClass *d3d11alloc_class = GST_D3D11_ALLOCATOR_CLASS (klass); + + gobject_class->dispose = gst_d3d11_pool_allocator_dispose; + gobject_class->finalize = gst_d3d11_pool_allocator_finalize; + + d3d11alloc_class->set_actvie = gst_d3d11_pool_allocator_set_active; +} + +static void +gst_d3d11_pool_allocator_init (GstD3D11PoolAllocator * allocator) +{ + GstD3D11PoolAllocatorPrivate *priv; + + priv = allocator->priv = + gst_d3d11_pool_allocator_get_instance_private (allocator); + g_rec_mutex_init (&priv->lock); + + priv->poll = gst_poll_new_timer (); + priv->queue = gst_atomic_queue_new (16); + priv->flushing = 1; + priv->active = FALSE; + priv->started = FALSE; + + /* 1 control write for flushing - the flush token */ + gst_poll_write_control (priv->poll); + /* 1 control write for marking that we are not waiting for poll - the wait token */ + gst_poll_write_control (priv->poll); +} + +static void +gst_d3d11_pool_allocator_dispose (GObject * object) +{ + GstD3D11PoolAllocator *self = GST_D3D11_POOL_ALLOCATOR (object); + + gst_clear_object (&self->device); + + G_OBJECT_CLASS (pool_alloc_parent_class)->dispose (object); +} + +static void +gst_d3d11_pool_allocator_finalize (GObject * object) +{ + GstD3D11PoolAllocator *self = GST_D3D11_POOL_ALLOCATOR (object); + GstD3D11PoolAllocatorPrivate *priv = self->priv; + + GST_DEBUG_OBJECT (self, "Finalize"); + + gst_d3d11_pool_allocator_stop (self); + gst_atomic_queue_unref (priv->queue); + gst_poll_free (priv->poll); + g_rec_mutex_clear (&priv->lock); + + if (priv->texture) + ID3D11Texture2D_Release (priv->texture); + + G_OBJECT_CLASS (pool_alloc_parent_class)->finalize (object); +} + +static gboolean +gst_d3d11_pool_allocator_start (GstD3D11PoolAllocator * self) +{ + GstD3D11PoolAllocatorPrivate *priv = self->priv; + ID3D11Device *device_handle; + HRESULT hr; + guint i; + + if (priv->started) + return TRUE; + + /* Nothing to do */ + if (priv->desc.ArraySize == 1) { + priv->started = TRUE; + return TRUE; + } + + device_handle = gst_d3d11_device_get_device_handle (self->device); + + if (!priv->texture) { + hr = ID3D11Device_CreateTexture2D (device_handle, &priv->desc, NULL, + &priv->texture); + if (!gst_d3d11_result (hr, self->device)) { + GST_ERROR_OBJECT (self, "Failed to allocate texture"); + return FALSE; + } + } + + /* Pre-allocate memory objects */ + for (i = 0; i < priv->desc.ArraySize; i++) { + GstMemory *mem; + + ID3D11Texture2D_AddRef (priv->texture); + mem = + gst_d3d11_allocator_alloc_wrapped (GST_D3D11_ALLOCATOR_CAST + (_d3d11_memory_allocator), self->device, &priv->desc, priv->texture); + + if (i == 0) { + if (!gst_d3d11_memory_update_size (mem)) { + GST_ERROR_OBJECT (self, "Failed to calculate memory size"); + gst_memory_unref (mem); + return FALSE; + } + + priv->mem_size = mem->size; + } else { + mem->size = mem->maxsize = priv->mem_size; + } + + GST_D3D11_MEMORY_CAST (mem)->priv->subresource_index = i; + + g_atomic_int_add (&priv->cur_mems, 1); + gst_atomic_queue_push (priv->queue, mem); + gst_poll_write_control (priv->poll); + } + + priv->started = TRUE; + + return TRUE; +} + +static void +gst_d3d11_pool_allocator_do_set_flushing (GstD3D11PoolAllocator * self, + gboolean flushing) +{ + GstD3D11PoolAllocatorPrivate *priv = self->priv; + + if (GST_D3D11_POOL_ALLOCATOR_IS_FLUSHING (self) == flushing) + return; + + if (flushing) { + g_atomic_int_set (&priv->flushing, 1); + /* Write the flush token to wake up any waiters */ + gst_poll_write_control (priv->poll); + } else { + while (!gst_poll_read_control (priv->poll)) { + if (errno == EWOULDBLOCK) { + /* This should not really happen unless flushing and unflushing + * happens on different threads. Let's wait a bit to get back flush + * token from the thread that was setting it to flushing */ + g_thread_yield (); + continue; + } else { + /* Critical error but GstPoll already complained */ + break; + } + } + + g_atomic_int_set (&priv->flushing, 0); + } +} + +static gboolean +gst_d3d11_pool_allocator_set_active (GstD3D11Allocator * allocator, + gboolean active) +{ + GstD3D11PoolAllocator *self = GST_D3D11_POOL_ALLOCATOR (allocator); + GstD3D11PoolAllocatorPrivate *priv = self->priv; + + GST_LOG_OBJECT (self, "active %d", active); + + GST_D3D11_POOL_ALLOCATOR_LOCK (self); + /* just return if we are already in the right state */ + if (priv->active == active) + goto was_ok; + + if (active) { + if (!gst_d3d11_pool_allocator_start (self)) + goto start_failed; + + /* flush_stop may release memory objects, setting to active to avoid running + * do_stop while activating the pool */ + priv->active = TRUE; + + gst_d3d11_pool_allocator_do_set_flushing (self, FALSE); + } else { + gint outstanding; + + /* set to flushing first */ + gst_d3d11_pool_allocator_do_set_flushing (self, TRUE); + + /* when all memory objects are in the pool, free them. Else they will be + * freed when they are released */ + outstanding = g_atomic_int_get (&priv->outstanding); + GST_LOG_OBJECT (self, "outstanding memories %d, (in queue %d)", + outstanding, gst_atomic_queue_length (priv->queue)); + if (outstanding == 0) { + if (!gst_d3d11_pool_allocator_stop (self)) + goto stop_failed; + } + + priv->active = FALSE; + } + + GST_D3D11_POOL_ALLOCATOR_UNLOCK (self); + + return TRUE; + +was_ok: + { + GST_DEBUG_OBJECT (self, "allocator was in the right state"); + GST_D3D11_POOL_ALLOCATOR_UNLOCK (self); + return TRUE; + } +start_failed: + { + GST_ERROR_OBJECT (self, "start failed"); + GST_D3D11_POOL_ALLOCATOR_UNLOCK (self); + return FALSE; + } +stop_failed: + { + GST_ERROR_OBJECT (self, "stop failed"); + GST_D3D11_POOL_ALLOCATOR_UNLOCK (self); + return FALSE; + } +} + +static void +gst_d3d11_pool_allocator_free_memory (GstD3D11PoolAllocator * self, + GstMemory * mem) +{ + GstD3D11PoolAllocatorPrivate *priv = self->priv; + + g_atomic_int_add (&priv->cur_mems, -1); + GST_LOG_OBJECT (self, "freeing memory %p (%u left)", mem, priv->cur_mems); + + GST_MINI_OBJECT_CAST (mem)->dispose = NULL; + gst_memory_unref (mem); +} + +/* must be called with the lock */ +static gboolean +gst_d3d11_pool_allocator_clear_queue (GstD3D11PoolAllocator * self) +{ + GstD3D11PoolAllocatorPrivate *priv = self->priv; + GstMemory *memory; + + GST_LOG_OBJECT (self, "Clearing queue"); + + /* clear the pool */ + while ((memory = gst_atomic_queue_pop (priv->queue))) { + while (!gst_poll_read_control (priv->poll)) { + if (errno == EWOULDBLOCK) { + /* We put the memory into the queue but did not finish writing control + * yet, let's wait a bit and retry */ + g_thread_yield (); + continue; + } else { + /* Critical error but GstPoll already complained */ + break; + } + } + gst_d3d11_pool_allocator_free_memory (self, memory); + } + + GST_LOG_OBJECT (self, "Clear done"); + + return priv->cur_mems == 0; +} + +/* must be called with the lock */ +static gboolean +gst_d3d11_pool_allocator_stop (GstD3D11PoolAllocator * self) +{ + GstD3D11PoolAllocatorPrivate *priv = self->priv; + + GST_DEBUG_OBJECT (self, "Stop"); + + if (priv->started) { + if (!gst_d3d11_pool_allocator_clear_queue (self)) + return FALSE; + + priv->started = FALSE; + } else { + GST_DEBUG_OBJECT (self, "Wasn't started"); + } + + return TRUE; +} + +static inline void +dec_outstanding (GstD3D11PoolAllocator * self) +{ + if (g_atomic_int_dec_and_test (&self->priv->outstanding)) { + /* all memory objects are returned to the pool, see if we need to free them */ + if (GST_D3D11_POOL_ALLOCATOR_IS_FLUSHING (self)) { + /* take the lock so that set_active is not run concurrently */ + GST_D3D11_POOL_ALLOCATOR_LOCK (self); + /* now that we have the lock, check if we have been de-activated with + * outstanding buffers */ + if (!self->priv->active) + gst_d3d11_pool_allocator_stop (self); + + GST_D3D11_POOL_ALLOCATOR_UNLOCK (self); + } + } +} + +static void +gst_d3d11_pool_allocator_release_memory (GstD3D11PoolAllocator * self, + GstMemory * mem) +{ + GST_LOG_OBJECT (self, "Released memory %p", mem); + + GST_MINI_OBJECT_CAST (mem)->dispose = NULL; + mem->allocator = gst_object_ref (_d3d11_memory_allocator); + gst_object_unref (self); + + /* keep it around in our queue */ + gst_atomic_queue_push (self->priv->queue, mem); + gst_poll_write_control (self->priv->poll); + dec_outstanding (self); +} + +static gboolean +gst_d3d11_memory_release (GstMiniObject * mini_object) +{ + GstMemory *mem = GST_MEMORY_CAST (mini_object); + GstD3D11PoolAllocator *alloc; + + g_assert (mem->allocator != NULL); + + if (!GST_IS_D3D11_POOL_ALLOCATOR (mem->allocator)) { + GST_LOG_OBJECT (mem->allocator, "Not our memory, free"); + return TRUE; + } + + alloc = GST_D3D11_POOL_ALLOCATOR (mem->allocator); + /* if flushing, free this memory */ + if (GST_D3D11_POOL_ALLOCATOR_IS_FLUSHING (alloc)) { + GST_LOG_OBJECT (alloc, "allocator is flushing, free %p", mem); + return TRUE; + } + + /* return the memory to the allocator */ + gst_memory_ref (mem); + gst_d3d11_pool_allocator_release_memory (alloc, mem); + + return FALSE; +} + +static GstFlowReturn +gst_d3d11_pool_allocator_alloc (GstD3D11PoolAllocator * self, GstMemory ** mem) +{ + GstD3D11PoolAllocatorPrivate *priv = self->priv; + GstMemory *new_mem; + + /* we allcates texture array during start */ + if (priv->desc.ArraySize > 1) + return GST_FLOW_EOS; + + /* increment the allocation counter */ + g_atomic_int_add (&priv->cur_mems, 1); + new_mem = + gst_d3d11_allocator_alloc_internal (GST_D3D11_ALLOCATOR_CAST + (_d3d11_memory_allocator), self->device, &priv->desc); + if (!new_mem) { + GST_ERROR_OBJECT (self, "Failed to allocate new memory"); + g_atomic_int_add (&priv->cur_mems, -1); + return GST_FLOW_ERROR; + } + + if (!priv->mem_size) { + if (!gst_d3d11_memory_update_size (new_mem)) { + GST_ERROR_OBJECT (self, "Failed to calculate size"); + gst_memory_unref (new_mem); + g_atomic_int_add (&priv->cur_mems, -1); + + return GST_FLOW_ERROR; + } + + priv->mem_size = new_mem->size; + } + + new_mem->size = new_mem->maxsize = priv->mem_size; + + *mem = new_mem; + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_d3d11_pool_allocator_acquire_memory_internal (GstD3D11PoolAllocator * self, + GstMemory ** memory) +{ + GstFlowReturn result; + GstD3D11PoolAllocatorPrivate *priv = self->priv; + + while (TRUE) { + if (G_UNLIKELY (GST_D3D11_POOL_ALLOCATOR_IS_FLUSHING (self))) + goto flushing; + + /* try to get a memory from the queue */ + *memory = gst_atomic_queue_pop (priv->queue); + if (G_LIKELY (*memory)) { + while (!gst_poll_read_control (priv->poll)) { + if (errno == EWOULDBLOCK) { + /* We put the memory into the queue but did not finish writing control + * yet, let's wait a bit and retry */ + g_thread_yield (); + continue; + } else { + /* Critical error but GstPoll already complained */ + break; + } + } + result = GST_FLOW_OK; + GST_LOG_OBJECT (self, "acquired memory %p", *memory); + break; + } + + /* no memory, try to allocate some more */ + GST_LOG_OBJECT (self, "no memory, trying to allocate"); + result = gst_d3d11_pool_allocator_alloc (self, memory); + if (G_LIKELY (result == GST_FLOW_OK)) + /* we have a memory, return it */ + break; + + if (G_UNLIKELY (result != GST_FLOW_EOS)) + /* something went wrong, return error */ + break; + + /* now we release the control socket, we wait for a memory release or + * flushing */ + if (!gst_poll_read_control (priv->poll)) { + if (errno == EWOULDBLOCK) { + /* This means that we have two threads trying to allocate memory + * already, and the other one already got the wait token. This + * means that we only have to wait for the poll now and not write the + * token afterwards: we will be woken up once the other thread is + * woken up and that one will write the wait token it removed */ + GST_LOG_OBJECT (self, "waiting for free memory or flushing"); + gst_poll_wait (priv->poll, GST_CLOCK_TIME_NONE); + } else { + /* This is a critical error, GstPoll already gave a warning */ + result = GST_FLOW_ERROR; + break; + } + } else { + /* We're the first thread waiting, we got the wait token and have to + * write it again later + * OR + * We're a second thread and just consumed the flush token and block all + * other threads, in which case we must not wait and give it back + * immediately */ + if (!GST_D3D11_POOL_ALLOCATOR_IS_FLUSHING (self)) { + GST_LOG_OBJECT (self, "waiting for free memory or flushing"); + gst_poll_wait (priv->poll, GST_CLOCK_TIME_NONE); + } + gst_poll_write_control (priv->poll); + } + } + + return result; + + /* ERRORS */ +flushing: + { + GST_DEBUG_OBJECT (self, "we are flushing"); + return GST_FLOW_FLUSHING; + } +} + +/** + * gst_d3d11_pool_allocator_new: + * @device: a #GstD3D11Device + * @desc: a D3D11_TEXTURE2D_DESC for texture allocation + * + * Creates a new #GstD3D11PoolAllocator instance. + * + * Returns: (transfer full): a new #GstD3D11PoolAllocator instance + */ +GstD3D11PoolAllocator * +gst_d3d11_pool_allocator_new (GstD3D11Device * device, + const D3D11_TEXTURE2D_DESC * desc) +{ + GstD3D11PoolAllocator *self; + + g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL); + g_return_val_if_fail (desc != NULL, NULL); + + gst_d3d11_memory_init_once (); + + self = g_object_new (GST_TYPE_D3D11_POOL_ALLOCATOR, NULL); + gst_object_ref_sink (self); + + self->device = gst_object_ref (device); + self->priv->desc = *desc; + + return self; +} + +/** + * gst_d3d11_pool_allocator_acquire_memory: + * @allocator: a #GstD3D11PoolAllocator + * @memory: (transfer full): a #GstMemory + * + * Acquires a #GstMemory from @allocator. @memory should point to a memory + * location that can hold a pointer to the new #GstMemory. + * + * Returns: a #GstFlowReturn such as %GST_FLOW_FLUSHING when the allocator is + * inactive. + */ +GstFlowReturn +gst_d3d11_pool_allocator_acquire_memory (GstD3D11PoolAllocator * allocator, + GstMemory ** memory) +{ + GstD3D11PoolAllocatorPrivate *priv; + GstFlowReturn result; + + g_return_val_if_fail (GST_IS_D3D11_POOL_ALLOCATOR (allocator), + GST_FLOW_ERROR); + g_return_val_if_fail (memory != NULL, FALSE); + + priv = allocator->priv; + + /* assume we'll have one more outstanding buffer we need to do that so + * that concurrent set_active doesn't clear the buffers */ + g_atomic_int_inc (&priv->outstanding); + result = gst_d3d11_pool_allocator_acquire_memory_internal (allocator, memory); + + if (result == GST_FLOW_OK) { + GstMemory *mem = *memory; + /* Replace default allocator with ours */ + gst_object_unref (mem->allocator); + mem->allocator = gst_object_ref (allocator); + GST_MINI_OBJECT_CAST (mem)->dispose = gst_d3d11_memory_release; + } else { + dec_outstanding (allocator); + } + + return result; +} diff --git a/gst-libs/gst/d3d11/gstd3d11memory.h b/gst-libs/gst/d3d11/gstd3d11memory.h index f28d01dec7..40215d9a07 100644 --- a/gst-libs/gst/d3d11/gstd3d11memory.h +++ b/gst-libs/gst/d3d11/gstd3d11memory.h @@ -30,14 +30,35 @@ G_BEGIN_DECLS #define GST_TYPE_D3D11_ALLOCATION_PARAMS (gst_d3d11_allocation_params_get_type()) + +#define GST_TYPE_D3D11_MEMORY (gst_d3d11_memory_get_type()) +#define GST_D3D11_MEMORY_CAST(obj) ((GstD3D11Memory *)obj) + #define GST_TYPE_D3D11_ALLOCATOR (gst_d3d11_allocator_get_type()) #define GST_D3D11_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_D3D11_ALLOCATOR, GstD3D11Allocator)) -#define GST_D3D11_ALLOCATOR_CLASS(klass) (G_TYPE_CHECK_CLASS((klass), GST_TYPE_D3D11_ALLOCATOR, GstD3D11AllocatorClass)) +#define GST_D3D11_ALLOCATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_D3D11_ALLOCATOR, GstD3D11AllocatorClass)) #define GST_IS_D3D11_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_D3D11_ALLOCATOR)) #define GST_IS_D3D11_ALLOCATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_D3D11_ALLOCATOR)) #define GST_D3D11_ALLOCATOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_D3D11_ALLOCATOR, GstD3D11AllocatorClass)) #define GST_D3D11_ALLOCATOR_CAST(obj) ((GstD3D11Allocator *)obj) +#define GST_TYPE_D3D11_POOL_ALLOCATOR (gst_d3d11_pool_allocator_get_type()) +#define GST_D3D11_POOL_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_D3D11_POOL_ALLOCATOR, GstD3D11PoolAllocator)) +#define GST_D3D11_POOL_ALLOCATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_D3D11_POOL_ALLOCATOR, GstD3D11PoolAllocatorClass)) +#define GST_IS_D3D11_POOL_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_D3D11_POOL_ALLOCATOR)) +#define GST_IS_D3D11_POOL_ALLOCATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_D3D11_POOL_ALLOCATOR)) +#define GST_D3D11_POOL_ALLOCATOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_D3D11_POOL_ALLOCATOR, GstD3D11PoolAllocatorClass)) +#define GST_D3D11_POOL_ALLOCATOR_CAST(obj) ((GstD3D11PoolAllocator *)obj) + +/** + * GST_D3D11_MEMORY_NAME: + * + * The name of the Direct3D11 memory + * + * Since: 1.20 + */ +#define GST_D3D11_MEMORY_NAME "D3D11Memory" + /** * GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY: * @@ -100,11 +121,30 @@ struct _GstD3D11AllocationParams gpointer _gst_reserved[GST_PADDING_LARGE]; }; +GST_D3D11_API +GType gst_d3d11_allocation_params_get_type (void); + +GST_D3D11_API +GstD3D11AllocationParams * gst_d3d11_allocation_params_new (GstD3D11Device * device, + GstVideoInfo * info, + GstD3D11AllocationFlags flags, + guint bind_flags); + +GST_D3D11_API +GstD3D11AllocationParams * gst_d3d11_allocation_params_copy (GstD3D11AllocationParams * src); + +GST_D3D11_API +void gst_d3d11_allocation_params_free (GstD3D11AllocationParams * params); + +GST_D3D11_API +gboolean gst_d3d11_allocation_params_alignment (GstD3D11AllocationParams * parms, + GstVideoAlignment * align); + struct _GstD3D11Memory { GstMemory mem; - /*< public > */ + /*< public >*/ GstD3D11Device *device; /*< private >*/ @@ -112,76 +152,26 @@ struct _GstD3D11Memory gpointer _gst_reserved[GST_PADDING]; }; -struct _GstD3D11Allocator -{ - GstAllocator parent; - - GstD3D11Device *device; - - /*< private >*/ - GstD3D11AllocatorPrivate *priv; - gpointer _gst_reserved[GST_PADDING]; -}; - -struct _GstD3D11AllocatorClass -{ - GstAllocatorClass allocator_class; - - /*< private >*/ - gpointer _gst_reserved[GST_PADDING]; -}; +GST_D3D11_API +GType gst_d3d11_memory_get_type (void); GST_D3D11_API -GType gst_d3d11_allocation_params_get_type (void); +void gst_d3d11_memory_init_once (void); GST_D3D11_API -GstD3D11AllocationParams * gst_d3d11_allocation_params_new (GstD3D11Device * device, - GstVideoInfo * info, - GstD3D11AllocationFlags flags, - guint bind_flags); +gboolean gst_is_d3d11_memory (GstMemory * mem); GST_D3D11_API -GstD3D11AllocationParams * gst_d3d11_allocation_params_copy (GstD3D11AllocationParams * src); - -GST_D3D11_API -void gst_d3d11_allocation_params_free (GstD3D11AllocationParams * params); - -GST_D3D11_API -gboolean gst_d3d11_allocation_params_alignment (GstD3D11AllocationParams * parms, - GstVideoAlignment * align); - -GST_D3D11_API -GType gst_d3d11_allocator_get_type (void); - -GST_D3D11_API -GstD3D11Allocator * gst_d3d11_allocator_new (GstD3D11Device *device); - -GST_D3D11_API -GstMemory * gst_d3d11_allocator_alloc (GstD3D11Allocator * allocator, - const D3D11_TEXTURE2D_DESC * desc, - GstD3D11AllocationFlags flags, - gsize size); - -GST_D3D11_API -GstMemory * gst_d3d11_allocator_alloc_staging (GstD3D11Allocator * allocator, - const D3D11_TEXTURE2D_DESC * desc, - GstD3D11AllocationFlags flags, - gint * stride); - -GST_D3D11_API -void gst_d3d11_allocator_set_flushing (GstD3D11Allocator * allocator, - gboolean flushing); - -GST_D3D11_API -gboolean gst_is_d3d11_memory (GstMemory * mem); - -GST_D3D11_API -ID3D11Texture2D * gst_d3d11_memory_get_texture_handle (GstD3D11Memory * mem); +ID3D11Texture2D * gst_d3d11_memory_get_texture_handle (GstD3D11Memory * mem); GST_D3D11_API gboolean gst_d3d11_memory_get_texture_desc (GstD3D11Memory * mem, D3D11_TEXTURE2D_DESC * desc); +GST_D3D11_API +gboolean gst_d3d11_memory_get_texture_stride (GstD3D11Memory * mem, + guint * stride); + GST_D3D11_API guint gst_d3d11_memory_get_subresource_index (GstD3D11Memory * mem); @@ -214,6 +204,69 @@ ID3D11VideoProcessorOutputView * gst_d3d11_memory_get_processor_output_view (Gs ID3D11VideoDevice * video_device, ID3D11VideoProcessorEnumerator * enumerator); +struct _GstD3D11Allocator +{ + GstAllocator allocator; + + /*< private >*/ + gpointer _gst_reserved[GST_PADDING]; +}; + +struct _GstD3D11AllocatorClass +{ + GstAllocatorClass allocator_class; + + gboolean (*set_actvie) (GstD3D11Allocator * allocator, + gboolean active); + + /*< private >*/ + gpointer _gst_reserved[GST_PADDING_LARGE]; +}; + +GST_D3D11_API +GType gst_d3d11_allocator_get_type (void); + +GST_D3D11_API +GstMemory * gst_d3d11_allocator_alloc (GstD3D11Allocator * allocator, + GstD3D11Device * device, + const D3D11_TEXTURE2D_DESC * desc); + +GST_D3D11_API +gboolean gst_d3d11_allocator_set_active (GstD3D11Allocator * allocator, + gboolean active); + +struct _GstD3D11PoolAllocator +{ + GstD3D11Allocator allocator; + + /*< public >*/ + GstD3D11Device *device; + + /*< private >*/ + GstD3D11PoolAllocatorPrivate *priv; + + gpointer _gst_reserved[GST_PADDING]; +}; + +struct _GstD3D11PoolAllocatorClass +{ + GstD3D11AllocatorClass allocator_class; + + /*< private >*/ + gpointer _gst_reserved[GST_PADDING]; +}; + +GST_D3D11_API +GType gst_d3d11_pool_allocator_get_type (void); + +GST_D3D11_API +GstD3D11PoolAllocator * gst_d3d11_pool_allocator_new (GstD3D11Device * device, + const D3D11_TEXTURE2D_DESC * desc); + +GST_D3D11_API +GstFlowReturn gst_d3d11_pool_allocator_acquire_memory (GstD3D11PoolAllocator * allocator, + GstMemory ** memory); + G_END_DECLS #endif /* __GST_D3D11_MEMORY_H__ */ diff --git a/sys/d3d11/gstd3d11compositor.cpp b/sys/d3d11/gstd3d11compositor.cpp index 25f4db277c..affe237f81 100644 --- a/sys/d3d11/gstd3d11compositor.cpp +++ b/sys/d3d11/gstd3d11compositor.cpp @@ -1531,7 +1531,10 @@ gst_d3d11_compositor_release_pad (GstElement * element, GstPad * pad) GST_OBJECT_NAME (pad)); gst_clear_buffer (&cpad->fallback_buf); - gst_clear_object (&cpad->fallback_pool); + if (cpad->fallback_pool) { + gst_buffer_pool_set_active (cpad->fallback_pool, FALSE); + gst_clear_object (&cpad->fallback_pool); + } g_clear_pointer (&cpad->convert, gst_d3d11_converter_free); GST_D3D11_CLEAR_COM (cpad->blend); diff --git a/sys/d3d11/gstd3d11decoder.cpp b/sys/d3d11/gstd3d11decoder.cpp index 5f792b2786..322a7885cf 100644 --- a/sys/d3d11/gstd3d11decoder.cpp +++ b/sys/d3d11/gstd3d11decoder.cpp @@ -270,7 +270,10 @@ gst_d3d11_decoder_get_property (GObject * object, guint prop_id, static void gst_d3d11_decoder_clear_resource (GstD3D11Decoder * self) { - gst_clear_object (&self->internal_pool); + if (self->internal_pool) { + gst_buffer_pool_set_active (self->internal_pool, FALSE); + gst_clear_object (&self->internal_pool); + } GST_D3D11_CLEAR_COM (self->decoder_handle); GST_D3D11_CLEAR_COM (self->staging); @@ -381,7 +384,10 @@ gst_d3d11_decoder_prepare_output_view_pool (GstD3D11Decoder * self) GstVideoInfo *info = &self->info; guint pool_size; - gst_clear_object (&self->internal_pool); + if (self->internal_pool) { + gst_buffer_pool_set_active (self->internal_pool, FALSE); + gst_clear_object (&self->internal_pool); + } if (!self->use_array_of_texture) { alloc_flags = GST_D3D11_ALLOCATION_FLAG_TEXTURE_ARRAY; diff --git a/sys/d3d11/gstd3d11pluginutils.cpp b/sys/d3d11/gstd3d11pluginutils.cpp index 2e6bd9e20e..c96479b1d5 100644 --- a/sys/d3d11/gstd3d11pluginutils.cpp +++ b/sys/d3d11/gstd3d11pluginutils.cpp @@ -571,78 +571,20 @@ gst_d3d11_find_swap_chain_color_space (GstVideoInfo * info, } #endif -GstBuffer * -gst_d3d11_allocate_staging_buffer (GstD3D11Allocator * allocator, - const GstVideoInfo * info, const GstD3D11Format * format, - const D3D11_TEXTURE2D_DESC desc[GST_VIDEO_MAX_PLANES], - gboolean add_videometa) +static void +fill_staging_desc (const D3D11_TEXTURE2D_DESC * ref, + D3D11_TEXTURE2D_DESC * staging) { - GstBuffer *buffer; - guint i; - gint stride[GST_VIDEO_MAX_PLANES] = { 0, }; - gsize offset[GST_VIDEO_MAX_PLANES] = { 0, }; - GstMemory *mem; + memset (staging, 0, sizeof (D3D11_TEXTURE2D_DESC)); - g_return_val_if_fail (GST_IS_D3D11_ALLOCATOR (allocator), NULL); - g_return_val_if_fail (info != NULL, NULL); - g_return_val_if_fail (format != NULL, NULL); - g_return_val_if_fail (desc != NULL, NULL); - - buffer = gst_buffer_new (); - - if (format->dxgi_format == DXGI_FORMAT_UNKNOWN) { - gsize size[GST_VIDEO_MAX_PLANES] = { 0, }; - - for (i = 0; i < GST_VIDEO_INFO_N_PLANES (info); i++) { - mem = - gst_d3d11_allocator_alloc_staging (allocator, &desc[i], - (GstD3D11AllocationFlags) 0, &stride[i]); - - if (!mem) { - GST_ERROR_OBJECT (allocator, "Couldn't allocate memory for plane %d", - i); - goto error; - } - - size[i] = gst_memory_get_sizes (mem, NULL, NULL); - if (i > 0) - offset[i] = offset[i - 1] + size[i - 1]; - gst_buffer_append_memory (buffer, mem); - } - } else { - /* must be YUV semi-planar or single plane */ - g_assert (GST_VIDEO_INFO_N_PLANES (info) <= 2); - - mem = gst_d3d11_allocator_alloc_staging (allocator, &desc[0], - (GstD3D11AllocationFlags) 0, &stride[0]); - - if (!mem) { - GST_ERROR_OBJECT (allocator, "Couldn't allocate memory"); - goto error; - } - - gst_memory_get_sizes (mem, NULL, NULL); - gst_buffer_append_memory (buffer, mem); - - if (GST_VIDEO_INFO_N_PLANES (info) == 2) { - stride[1] = stride[0]; - offset[1] = stride[0] * desc[0].Height; - } - } - - if (add_videometa) { - gst_buffer_add_video_meta_full (buffer, GST_VIDEO_FRAME_FLAG_NONE, - GST_VIDEO_INFO_FORMAT (info), GST_VIDEO_INFO_WIDTH (info), - GST_VIDEO_INFO_HEIGHT (info), GST_VIDEO_INFO_N_PLANES (info), - offset, stride); - } - - return buffer; - -error: - gst_buffer_unref (buffer); - - return NULL; + staging->Width = ref->Width; + staging->Height = ref->Height; + staging->MipLevels = 1; + staging->Format = ref->Format; + staging->SampleDesc.Count = 1; + staging->ArraySize = 1; + staging->Usage = D3D11_USAGE_STAGING; + staging->CPUAccessFlags = (D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE); } GstBuffer * @@ -651,11 +593,14 @@ gst_d3d11_allocate_staging_buffer_for (GstBuffer * buffer, { GstD3D11Memory *dmem; GstD3D11Device *device; - GstD3D11AllocationParams *params = NULL; GstD3D11Allocator *alloc = NULL; GstBuffer *staging_buffer = NULL; - D3D11_TEXTURE2D_DESC *desc; + gint stride[GST_VIDEO_MAX_PLANES] = { 0, }; + gsize offset[GST_VIDEO_MAX_PLANES] = { 0, }; guint i; + gsize size = 0; + const GstD3D11Format *format; + D3D11_TEXTURE2D_DESC desc; for (i = 0; i < gst_buffer_n_memory (buffer); i++) { GstMemory *mem = gst_buffer_peek_memory (buffer, i); @@ -669,54 +614,73 @@ gst_d3d11_allocate_staging_buffer_for (GstBuffer * buffer, dmem = (GstD3D11Memory *) gst_buffer_peek_memory (buffer, 0); device = dmem->device; - - params = gst_d3d11_allocation_params_new (device, (GstVideoInfo *) info, - (GstD3D11AllocationFlags) 0, 0); - - if (!params) { - GST_WARNING ("Couldn't create alloc params"); - goto done; + format = gst_d3d11_device_format_from_gst (device, + GST_VIDEO_INFO_FORMAT (info)); + if (!format) { + GST_ERROR ("Unknown d3d11 format"); + return NULL; } - desc = ¶ms->desc[0]; - /* resolution of semi-planar formats must be multiple of 2 */ - if (desc[0].Format == DXGI_FORMAT_NV12 || desc[0].Format == DXGI_FORMAT_P010 - || desc[0].Format == DXGI_FORMAT_P016) { - if (desc[0].Width % 2 || desc[0].Height % 2) { - gint width, height; - GstVideoAlignment align; - - width = GST_ROUND_UP_2 (desc[0].Width); - height = GST_ROUND_UP_2 (desc[0].Height); - - gst_video_alignment_reset (&align); - align.padding_right = width - desc[0].Width; - align.padding_bottom = height - desc[0].Height; - - gst_d3d11_allocation_params_alignment (params, &align); - } - } - - alloc = gst_d3d11_allocator_new (device); + alloc = (GstD3D11Allocator *) gst_allocator_find (GST_D3D11_MEMORY_NAME); if (!alloc) { - GST_WARNING ("Couldn't create allocator"); - goto done; + GST_ERROR ("D3D11 allocator is not available"); + return NULL; } - staging_buffer = gst_d3d11_allocate_staging_buffer (alloc, - info, params->d3d11_format, params->desc, add_videometa); + staging_buffer = gst_buffer_new (); + for (i = 0; i < gst_buffer_n_memory (buffer); i++) { + D3D11_TEXTURE2D_DESC staging_desc; + GstD3D11Memory *mem = (GstD3D11Memory *) gst_buffer_peek_memory (buffer, i); + GstD3D11Memory *new_mem; - if (!staging_buffer) - GST_WARNING ("Couldn't allocate staging buffer"); + guint cur_stride = 0; -done: - if (params) - gst_d3d11_allocation_params_free (params); + gst_d3d11_memory_get_texture_desc (mem, &desc); + fill_staging_desc (&desc, &staging_desc); + + new_mem = (GstD3D11Memory *) + gst_d3d11_allocator_alloc (alloc, mem->device, &staging_desc); + if (!new_mem) { + GST_ERROR ("Failed to allocate memory"); + goto error; + } + + if (!gst_d3d11_memory_get_texture_stride (new_mem, &cur_stride) || + cur_stride < staging_desc.Width) { + GST_ERROR ("Failed to calculate memory size"); + gst_memory_unref (GST_MEMORY_CAST (mem)); + goto error; + } + + offset[i] = size; + stride[i] = cur_stride; + size += GST_MEMORY_CAST (new_mem)->size; + + gst_buffer_append_memory (staging_buffer, GST_MEMORY_CAST (new_mem)); + } + + /* single texture semi-planar formats */ + if (format->dxgi_format != DXGI_FORMAT_UNKNOWN && + GST_VIDEO_INFO_N_PLANES (info) == 2) { + stride[1] = stride[0]; + offset[1] = stride[0] * desc.Height; + } + + gst_buffer_add_video_meta_full (staging_buffer, GST_VIDEO_FRAME_FLAG_NONE, + GST_VIDEO_INFO_FORMAT (info), GST_VIDEO_INFO_WIDTH (info), + GST_VIDEO_INFO_HEIGHT (info), GST_VIDEO_INFO_N_PLANES (info), + offset, stride); if (alloc) gst_object_unref (alloc); return staging_buffer; + +error: + gst_clear_buffer (&staging_buffer); + gst_clear_object (&alloc); + + return NULL; } static gboolean diff --git a/sys/d3d11/gstd3d11pluginutils.h b/sys/d3d11/gstd3d11pluginutils.h index ab4f6b48f0..de78df4176 100644 --- a/sys/d3d11/gstd3d11pluginutils.h +++ b/sys/d3d11/gstd3d11pluginutils.h @@ -77,12 +77,6 @@ const GstDxgiColorSpace * gst_d3d11_find_swap_chain_color_space (GstVideoInfo * gboolean use_hdr10); #endif -GstBuffer * gst_d3d11_allocate_staging_buffer (GstD3D11Allocator * allocator, - const GstVideoInfo * info, - const GstD3D11Format * format, - const D3D11_TEXTURE2D_DESC desc[GST_VIDEO_MAX_PLANES], - gboolean add_videometa); - GstBuffer * gst_d3d11_allocate_staging_buffer_for (GstBuffer * buffer, const GstVideoInfo * info, gboolean add_videometa);