diff --git a/.indent_cpp_list b/.indent_cpp_list index d591601048..418ad6495e 100644 --- a/.indent_cpp_list +++ b/.indent_cpp_list @@ -6,6 +6,7 @@ subprojects/gst-plugins-bad/gst-libs/gst/va subprojects/gst-plugins-bad/gst-libs/gst/winrt subprojects/gst-plugins-bad/sys/amfcodec subprojects/gst-plugins-bad/sys/d3d11 +subprojects/gst-plugins-bad/sys/d3d12 subprojects/gst-plugins-bad/sys/dwrite subprojects/gst-plugins-bad/sys/mediafoundation subprojects/gst-plugins-bad/sys/nvcodec diff --git a/subprojects/gst-plugins-bad/meson_options.txt b/subprojects/gst-plugins-bad/meson_options.txt index 62ee2c6097..402e098277 100644 --- a/subprojects/gst-plugins-bad/meson_options.txt +++ b/subprojects/gst-plugins-bad/meson_options.txt @@ -100,6 +100,7 @@ option('curl-ssh2', type : 'feature', value : 'auto', description : 'cURL networ option('d3dvideosink', type : 'feature', value : 'auto', description : 'Direct3D video sink plugin') option('d3d11', type : 'feature', value : 'auto', description : 'Direct3D11 plugin') option('d3d11-wgc', type : 'feature', value : 'auto', description : 'Windows Graphics Capture API support in d3d11 plugin') +option('d3d12', type : 'feature', value : 'auto', description : 'Direct3D12 plugin') option('dash', type : 'feature', value : 'auto', description : 'DASH demuxer plugin') option('dc1394', type : 'feature', value : 'auto', description : 'libdc1394 IIDC camera source plugin') option('decklink', type : 'feature', value : 'auto', description : 'DeckLink audio/video source/sink plugin') diff --git a/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12_fwd.h b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12_fwd.h new file mode 100644 index 0000000000..e3488d6a9f --- /dev/null +++ b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12_fwd.h @@ -0,0 +1,55 @@ +/* GStreamer + * Copyright (C) 2023 Seungha Yang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include + +#ifndef INITGUID +#include +#endif + +#include +#include +#include + +G_BEGIN_DECLS + +typedef struct _GstD3D12Device GstD3D12Device; +typedef struct _GstD3D12DeviceClass GstD3D12DeviceClass; +typedef struct _GstD3D12DevicePrivate GstD3D12DevicePrivate; + +typedef struct _GstD3D12Fence GstD3D12Fence; +typedef struct _GstD3D12FencePrivate GstD3D12FencePrivate; + +typedef struct _GstD3D12Memory GstD3D12Memory; +typedef struct _GstD3D12MemoryPrivate GstD3D12MemoryPrivate; + +typedef struct _GstD3D12Allocator GstD3D12Allocator; +typedef struct _GstD3D12AllocatorClass GstD3D12AllocatorClass; +typedef struct _GstD3D12AllocatorPrivate GstD3D12AllocatorPrivate; + +typedef struct _GstD3D12PoolAllocator GstD3D12PoolAllocator; +typedef struct _GstD3D12PoolAllocatorClass GstD3D12PoolAllocatorClass; +typedef struct _GstD3D12PoolAllocatorPrivate GstD3D12PoolAllocatorPrivate; + +typedef struct _GstD3D12Format GstD3D12Format; + +G_END_DECLS + diff --git a/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12decoder.cpp b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12decoder.cpp new file mode 100644 index 0000000000..d3022815d3 --- /dev/null +++ b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12decoder.cpp @@ -0,0 +1,1637 @@ +/* GStreamer + * Copyright (C) 2023 Seungha Yang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "gstd3d12decoder.h" +#include "gstd3d12device.h" +#include "gstd3d12utils.h" +#include "gstd3d12format.h" +#include "gstd3d12memory.h" +#include "gstd3d12fence.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GST_DEBUG_CATEGORY_EXTERN (gst_d3d12_decoder_debug); +#define GST_CAT_DEFAULT gst_d3d12_decoder_debug + +struct DecoderFormat +{ + GstDxvaCodec codec; + const GUID decode_profile; + DXGI_FORMAT format; +}; + +static const DecoderFormat format_list[] = { + {GST_DXVA_CODEC_H264, D3D12_VIDEO_DECODE_PROFILE_H264, DXGI_FORMAT_NV12} +}; + +/* *INDENT-OFF* */ +using namespace Microsoft::WRL; + +class GstD3D12Dpb +{ +public: + GstD3D12Dpb (guint8 size) : size_(size) + { + textures_.resize (size); + subresources_.resize (size); + heaps_.resize (size); + + for (guint i = 0; i < size; i++) { + dxva_id_.push (i); + textures_[i] = nullptr; + subresources_[i] = 0; + heaps_[i] = nullptr; + } + } + + guint8 Acquire (GstD3D12Memory * mem, ID3D12VideoDecoderHeap * heap) + { + std::lock_guard lk (lock_); + if (dxva_id_.empty ()) + return 0xff; + + guint8 ret = dxva_id_.front (); + dxva_id_.pop (); + + GstD3D12Memory *dmem; + ID3D12Resource *resource; + UINT subresource = 0; + + dmem = GST_D3D12_MEMORY_CAST (mem); + resource = gst_d3d12_memory_get_resource_handle (dmem); + gst_d3d12_memory_get_subresource_index (dmem, 0, &subresource); + + textures_[ret] = resource; + subresources_[ret] = subresource; + heaps_[ret] = heap; + + return ret; + } + + void Release (guint8 id) + { + std::lock_guard lk (lock_); + if (id == 0xff || id >= size_) + return; + + dxva_id_.push (id); + + textures_[id] = nullptr; + subresources_[id] = 0; + heaps_[id] = nullptr; + } + + guint8 GetSize () + { + return size_; + } + + ID3D12Resource ** GetTextures () + { + return &textures_[0]; + } + + UINT * GetSubresources () + { + return &subresources_[0]; + } + + ID3D12VideoDecoderHeap ** GetHeaps () + { + return &heaps_[0]; + } + +private: + std::queue dxva_id_; + std::mutex lock_; + guint size_; + std::vector textures_; + std::vector subresources_; + std::vector heaps_; +}; + +struct GstD3D12DecoderPicture : public GstMiniObject +{ + GstD3D12DecoderPicture (GstMemory * dpb_mem, GstMemory * out_mem, + std::shared_ptr d3d12_dpb, + ID3D12VideoDecoderHeap * decoder_heap, guint8 dxva_id) + : mem(dpb_mem), output_mem(out_mem), dpb(d3d12_dpb), heap(decoder_heap) + , view_id(dxva_id) + { + } + + ~GstD3D12DecoderPicture () + { + auto d3d12_dpb = dpb.lock (); + if (d3d12_dpb) + d3d12_dpb->Release (view_id); + + if (mem) + gst_memory_unref (mem); + if (output_mem) + gst_memory_unref (output_mem); + } + + GstMemory *mem; + GstMemory *output_mem; + ComPtr heap; + std::weak_ptr dpb; + + guint8 view_id; +}; + +#define GST_TYPE_D3D12_DECODER_PICTURE (gst_d3d12_decoder_picture_get_type ()) +GST_DEFINE_MINI_OBJECT_TYPE (GstD3D12DecoderPicture, gst_d3d12_decoder_picture); + +struct GstD3D12DecoderPrivate +{ + GstD3D12DecoderPrivate () + { + fence_value = 1; + } + + ~GstD3D12DecoderPrivate() + { + if (input_state) + gst_video_codec_state_unref (input_state); + + if (output_state) + gst_video_codec_state_unref (output_state); + + if (fence) { + gst_d3d12_fence_wait (fence); + gst_d3d12_fence_unref (fence); + } + + if (allocator) { + gst_d3d12_allocator_set_active (allocator, FALSE); + gst_object_unref (allocator); + } + + if (output_allocator) { + gst_d3d12_allocator_set_active (output_allocator, FALSE); + gst_object_unref (output_allocator); + } + + if (device) + gst_object_unref (device); + } + + GstD3D12Device *device = nullptr; + + GstDxvaCodec codec = GST_DXVA_CODEC_NONE; + + /* Allocator for dpb textures */ + GstD3D12Allocator *allocator = nullptr; + + /* Used for output */ + GstD3D12Allocator *output_allocator = nullptr; + + gboolean configured = FALSE; + gboolean opened = FALSE; + + GstVideoInfo info; + GstVideoInfo output_info; + gint crop_x = 0; + gint crop_y = 0; + gint coded_width = 0; + gint coded_height = 0; + DXGI_FORMAT decoder_format = DXGI_FORMAT_UNKNOWN; + gboolean reference_only = FALSE; + + D3D12_FEATURE_DATA_VIDEO_DECODE_SUPPORT support = { 0, }; + + /* For staging */ + D3D12_PLACED_SUBRESOURCE_FOOTPRINT layout[GST_VIDEO_MAX_PLANES] = { 0, }; + + GstVideoCodecState *input_state = nullptr; + GstVideoCodecState *output_state = nullptr; + + /* Internal pool params */ + gint aligned_width = 0; + gint aligned_height = 0; + guint dpb_size = 0; + + ComPtr video_device; + + ComPtr decoder; + ComPtr heap; + ComPtr ca; + ComPtr cl; + ComPtr cq; + ComPtr bitstream; + gsize bitstream_size = 0; + + std::shared_ptr dpb; + + /* Used for download decoded picture to staging */ + ComPtr copy_ca; + ComPtr copy_cl; + ComPtr copy_cq; + + ComPtr staging; + + GstD3D12Fence *fence = nullptr; + std::atomic fence_value; + + gint64 luid; + + std::mutex lock; +}; +/* *INDENT-ON* */ + +struct _GstD3D12Decoder +{ + GstObject parent; + + GstD3D12DecoderPrivate *priv; +}; + +static void gst_d3d12_decoder_finalize (GObject * object); + +#define parent_class gst_d3d12_decoder_parent_class +G_DEFINE_TYPE (GstD3D12Decoder, gst_d3d12_decoder, GST_TYPE_OBJECT); + +static void +gst_d3d12_decoder_class_init (GstD3D12DecoderClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = gst_d3d12_decoder_finalize; +} + +static void +gst_d3d12_decoder_init (GstD3D12Decoder * self) +{ + GstD3D12DecoderPrivate *priv; + + self->priv = priv = new GstD3D12DecoderPrivate (); +} + +static void +gst_d3d12_decoder_finalize (GObject * object) +{ + GstD3D12Decoder *self = GST_D3D12_DECODER (object); + + delete self->priv; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_d3d12_decoder_clear_resource (GstD3D12Decoder * self) +{ + GstD3D12DecoderPrivate *priv = self->priv; + std::lock_guard < std::mutex > lk (priv->lock); + + gst_d3d12_fence_wait (priv->fence); + + if (priv->allocator) { + gst_d3d12_allocator_set_active (priv->allocator, FALSE); + gst_clear_object (&priv->allocator); + } + + if (priv->output_allocator) { + gst_d3d12_allocator_set_active (priv->output_allocator, FALSE); + gst_clear_object (&priv->output_allocator); + } + + priv->heap = nullptr; + priv->staging = nullptr; +} + +static void +gst_d3d12_decoder_reset (GstD3D12Decoder * self) +{ + GstD3D12DecoderPrivate *priv = self->priv; + + gst_d3d12_decoder_clear_resource (self); + priv->dpb_size = 0; + + priv->configured = FALSE; + priv->opened = FALSE; + + g_clear_pointer (&priv->output_state, gst_video_codec_state_unref); + g_clear_pointer (&priv->input_state, gst_video_codec_state_unref); +} + +GstD3D12Decoder * +gst_d3d12_decoder_new (GstD3D12Device * device, GstDxvaCodec codec) +{ + GstD3D12Decoder *self; + GstD3D12DecoderPrivate *priv; + ComPtr < ID3D12VideoDevice > video_device; + ComPtr < ID3D12CommandAllocator > copy_ca; + ComPtr < ID3D12GraphicsCommandList > copy_cl; + ComPtr < ID3D12CommandQueue > copy_cq; + ComPtr < ID3D12CommandAllocator > ca; + ComPtr < ID3D12VideoDecodeCommandList > cl; + ComPtr < ID3D12CommandQueue > cq; + ID3D12Device *device_handle; + D3D12_COMMAND_QUEUE_DESC desc = { }; + HRESULT hr; + + g_return_val_if_fail (GST_IS_D3D12_DEVICE (device), nullptr); + g_return_val_if_fail (codec > GST_DXVA_CODEC_NONE, nullptr); + g_return_val_if_fail (codec < GST_DXVA_CODEC_LAST, nullptr); + + device_handle = gst_d3d12_device_get_device_handle (device); + hr = device_handle->QueryInterface (IID_PPV_ARGS (&video_device)); + if (!gst_d3d12_result (hr, device)) + return nullptr; + + hr = device_handle->CreateCommandAllocator (D3D12_COMMAND_LIST_TYPE_COPY, + IID_PPV_ARGS (©_ca)); + if (!gst_d3d12_result (hr, device)) + return nullptr; + + hr = device_handle->CreateCommandList (0, D3D12_COMMAND_LIST_TYPE_COPY, + copy_ca.Get (), nullptr, IID_PPV_ARGS (©_cl)); + if (!gst_d3d12_result (hr, device)) + return nullptr; + + hr = copy_cl->Close (); + if (!gst_d3d12_result (hr, device)) + return nullptr; + + desc.Type = D3D12_COMMAND_LIST_TYPE_COPY; + desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; + hr = device_handle->CreateCommandQueue (&desc, IID_PPV_ARGS (©_cq)); + if (!gst_d3d12_result (hr, device)) + return nullptr; + + hr = device_handle->CreateCommandAllocator + (D3D12_COMMAND_LIST_TYPE_VIDEO_DECODE, IID_PPV_ARGS (&ca)); + if (!gst_d3d12_result (hr, device)) + return nullptr; + + hr = device_handle->CreateCommandList (0, + D3D12_COMMAND_LIST_TYPE_VIDEO_DECODE, ca.Get (), nullptr, + IID_PPV_ARGS (&cl)); + if (!gst_d3d12_result (hr, device)) + return nullptr; + + hr = cl->Close (); + if (!gst_d3d12_result (hr, device)) + return nullptr; + + desc.Type = D3D12_COMMAND_LIST_TYPE_VIDEO_DECODE; + desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; + hr = device_handle->CreateCommandQueue (&desc, IID_PPV_ARGS (&cq)); + if (!gst_d3d12_result (hr, device)) + return nullptr; + + self = (GstD3D12Decoder *) g_object_new (GST_TYPE_D3D12_DECODER, nullptr); + priv = self->priv; + priv->codec = codec; + priv->device = (GstD3D12Device *) gst_object_ref (device); + priv->fence = gst_d3d12_fence_new (device); + priv->video_device = video_device; + priv->copy_ca = copy_ca; + priv->copy_cl = copy_cl; + priv->copy_cq = copy_cq; + priv->ca = ca; + priv->cl = cl; + priv->cq = cq; + g_object_get (priv->device, "adapter-luid", &priv->luid, nullptr); + + gst_object_ref_sink (self); + + return self; +} + +GstFlowReturn +gst_d3d12_decoder_configure (GstD3D12Decoder * decoder, + GstVideoCodecState * input_state, const GstVideoInfo * info, + gint crop_x, gint crop_y, gint coded_width, + gint coded_height, guint dpb_size) +{ + g_return_val_if_fail (GST_IS_D3D12_DECODER (decoder), GST_FLOW_ERROR); + g_return_val_if_fail (info, GST_FLOW_ERROR); + g_return_val_if_fail (input_state, GST_FLOW_ERROR); + g_return_val_if_fail (coded_width >= GST_VIDEO_INFO_WIDTH (info), + GST_FLOW_ERROR); + g_return_val_if_fail (coded_height >= GST_VIDEO_INFO_HEIGHT (info), + GST_FLOW_ERROR); + g_return_val_if_fail (dpb_size > 0, GST_FLOW_ERROR); + + GstD3D12DecoderPrivate *priv = decoder->priv; + GstD3D12Format device_format; + + gst_d3d12_decoder_reset (decoder); + + if (!gst_d3d12_device_get_device_format (priv->device, + GST_VIDEO_INFO_FORMAT (info), &device_format) || + device_format.dxgi_format == DXGI_FORMAT_UNKNOWN) { + GST_ERROR_OBJECT (decoder, "Could not determine dxgi format from %s", + gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (info))); + return GST_FLOW_ERROR; + } + + if (priv->codec == GST_DXVA_CODEC_H264) + dpb_size += 1; + + priv->input_state = gst_video_codec_state_ref (input_state); + priv->info = priv->output_info = *info; + priv->crop_x = crop_x; + priv->crop_y = crop_y; + priv->coded_width = coded_width; + priv->coded_height = coded_height; + priv->dpb_size = dpb_size; + priv->decoder_format = device_format.dxgi_format; + + priv->configured = TRUE; + + return GST_FLOW_OK; +} + +static gboolean +gst_d3d12_decoder_prepare_allocator (GstD3D12Decoder * self) +{ + GstD3D12DecoderPrivate *priv = self->priv; + + GST_DEBUG_OBJECT (self, "Preparing allocator"); + + std::lock_guard < std::mutex > lk (priv->lock); + if (priv->allocator) { + gst_d3d12_allocator_set_active (priv->allocator, FALSE); + gst_clear_object (&priv->allocator); + } + + if (priv->output_allocator) { + gst_d3d12_allocator_set_active (priv->output_allocator, FALSE); + gst_clear_object (&priv->output_allocator); + } + + D3D12_RESOURCE_FLAGS resource_flags = D3D12_RESOURCE_FLAG_NONE; + if ((priv->support.ConfigurationFlags & + D3D12_VIDEO_DECODE_CONFIGURATION_FLAG_REFERENCE_ONLY_ALLOCATIONS_REQUIRED) + != 0) { + resource_flags = + D3D12_RESOURCE_FLAG_VIDEO_DECODE_REFERENCE_ONLY | + D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE; + priv->reference_only = TRUE; + } else { + priv->reference_only = FALSE; + } + + UINT16 array_size; + /* Tier 1 decoder requires array */ + if (priv->support.DecodeTier == D3D12_VIDEO_DECODE_TIER_1) { + array_size = priv->dpb_size; + } else { + array_size = 1; + } + + D3D12_HEAP_PROPERTIES heap_prop = + CD3D12_HEAP_PROPERTIES (D3D12_HEAP_TYPE_DEFAULT); + D3D12_RESOURCE_DESC desc = CD3D12_RESOURCE_DESC::Tex2D (priv->decoder_format, + priv->aligned_width, + priv->aligned_height, + array_size, + 1, + 1, + 0, + resource_flags); + + priv->allocator = (GstD3D12Allocator *) + gst_d3d12_pool_allocator_new (priv->device, + &heap_prop, D3D12_HEAP_FLAG_NONE, + &desc, D3D12_RESOURCE_STATE_COMMON, nullptr); + gst_d3d12_allocator_set_active (priv->allocator, TRUE); + + /* In case that device requires reference only dpb texture, we need another + * texture pool for outputting without VIDEO_DECODE_REFERENCE_ONLY flag */ + if (priv->reference_only) { + GST_DEBUG_OBJECT (self, "Creating output only allocator"); + D3D12_RESOURCE_DESC ref_desc = + CD3D12_RESOURCE_DESC::Tex2D (priv->decoder_format, + priv->aligned_width, + priv->aligned_height, + 1, + 1); + + priv->output_allocator = (GstD3D12Allocator *) + gst_d3d12_pool_allocator_new (priv->device, &heap_prop, + D3D12_HEAP_FLAG_NONE, &ref_desc, D3D12_RESOURCE_STATE_COMMON, nullptr); + gst_d3d12_allocator_set_active (priv->output_allocator, TRUE); + } + + priv->dpb = std::make_shared < GstD3D12Dpb > ((guint8) priv->dpb_size); + + return TRUE; +} + +static void +gst_d3d12_decoder_picture_free (GstD3D12DecoderPicture * self) +{ + delete self; +} + +static GstD3D12DecoderPicture * +gst_d3d12_decoder_picture_new (GstD3D12Decoder * self, GstMemory * mem, + GstMemory * output_mem, ID3D12VideoDecoderHeap * heap) +{ + GstD3D12DecoderPrivate *priv = self->priv; + GstD3D12DecoderPicture *picture; + + auto view_id = priv->dpb->Acquire (GST_D3D12_MEMORY_CAST (mem), heap); + if (view_id == 0xff) { + GST_WARNING_OBJECT (self, "No empty picture"); + if (mem) + gst_memory_unref (mem); + if (output_mem) + gst_memory_unref (output_mem); + return nullptr; + } + + picture = new GstD3D12DecoderPicture (mem, output_mem, priv->dpb, heap, + view_id); + + gst_mini_object_init (picture, 0, GST_TYPE_D3D12_DECODER_PICTURE, + nullptr, nullptr, + (GstMiniObjectFreeFunction) gst_d3d12_decoder_picture_free); + + return picture; +} + +GstFlowReturn +gst_d3d12_decoder_new_picture (GstD3D12Decoder * decoder, + GstVideoDecoder * videodec, GstCodecPicture * picture) +{ + GstD3D12DecoderPrivate *priv; + GstD3D12DecoderPicture *decoder_pic = nullptr; + GstMemory *mem; + GstMemory *output_mem = nullptr; + GstFlowReturn ret; + + g_return_val_if_fail (GST_IS_D3D12_DECODER (decoder), GST_FLOW_ERROR); + g_return_val_if_fail (GST_IS_VIDEO_DECODER (videodec), GST_FLOW_ERROR); + g_return_val_if_fail (picture != nullptr, GST_FLOW_ERROR); + + priv = decoder->priv; + + if (!priv->allocator) { + /* Try negotiate again whatever the previous negotiation result was. + * There could be updated field(s) in sinkpad caps after we negotiated with + * downstream on new_sequence() call. For example, h264/h265 parse + * will be able to update HDR10 related caps field after parsing + * corresponding SEI messages which are usually placed after the essential + * headers */ + gst_video_decoder_negotiate (videodec); + + if (!gst_d3d12_decoder_prepare_allocator (decoder)) { + GST_ERROR_OBJECT (videodec, "Failed to setup dpb pool"); + return GST_FLOW_ERROR; + } + } + + ret = + gst_d3d12_pool_allocator_acquire_memory (GST_D3D12_POOL_ALLOCATOR + (priv->allocator), &mem); + if (ret != GST_FLOW_OK) { + GST_WARNING_OBJECT (videodec, "Couldn't acquire memory"); + return ret; + } + + if (priv->reference_only) { + ret = + gst_d3d12_pool_allocator_acquire_memory (GST_D3D12_POOL_ALLOCATOR + (priv->output_allocator), &output_mem); + if (ret != GST_FLOW_OK) { + GST_WARNING_OBJECT (videodec, "Couldn't acquire output memory"); + gst_memory_unref (mem); + return ret; + } + } + + decoder_pic = gst_d3d12_decoder_picture_new (decoder, mem, output_mem, + priv->heap.Get ()); + if (!decoder_pic) { + GST_ERROR_OBJECT (videodec, "Couldn't create new picture"); + return GST_FLOW_ERROR; + } + + gst_codec_picture_set_user_data (picture, decoder_pic, + (GDestroyNotify) gst_mini_object_unref); + + return GST_FLOW_OK; +} + +GstFlowReturn +gst_d3d12_decoder_duplicate_picture (GstD3D12Decoder * decoder, + GstCodecPicture * src, GstCodecPicture * dst) +{ + GstD3D12DecoderPicture *decoder_pic = + (GstD3D12DecoderPicture *) gst_codec_picture_get_user_data (src); + + if (!decoder_pic) + return GST_FLOW_ERROR; + + gst_codec_picture_set_user_data (dst, gst_mini_object_ref (decoder_pic), + (GDestroyNotify) gst_mini_object_unref); + + return GST_FLOW_OK; +} + +guint8 +gst_d3d12_decoder_get_picture_id (GstD3D12Decoder * decoder, + GstCodecPicture * picture) +{ + GstD3D12DecoderPicture *decoder_pic = + (GstD3D12DecoderPicture *) gst_codec_picture_get_user_data (picture); + + if (!picture) + return 0xff; + + return decoder_pic->view_id; +} + +GstFlowReturn +gst_d3d12_decoder_start_picture (GstD3D12Decoder * decoder, + GstCodecPicture * picture, guint8 * picture_id) +{ + GstD3D12DecoderPicture *decoder_pic = + (GstD3D12DecoderPicture *) gst_codec_picture_get_user_data (picture); + + if (picture_id) + *picture_id = 0xff; + + if (!decoder_pic) + return GST_FLOW_ERROR; + + if (picture_id) + *picture_id = decoder_pic->view_id; + + return GST_FLOW_OK; +} + +static gboolean +gst_d3d12_decoder_upload_bitstream (GstD3D12Decoder * self, gpointer data, + gsize size) +{ + GstD3D12DecoderPrivate *priv = self->priv; + gpointer map_data; + HRESULT hr; + + if (priv->bitstream && priv->bitstream_size < size) + priv->bitstream = nullptr; + + if (!priv->bitstream) { + ID3D12Device *device_handle = + gst_d3d12_device_get_device_handle (priv->device); + ComPtr < ID3D12Resource > bitstream; + size_t alloc_size = GST_ROUND_UP_128 (size) + 1024; + + D3D12_HEAP_PROPERTIES heap_prop = + CD3D12_HEAP_PROPERTIES (D3D12_HEAP_TYPE_UPLOAD); + D3D12_RESOURCE_DESC desc = CD3D12_RESOURCE_DESC::Buffer (alloc_size); + hr = device_handle->CreateCommittedResource (&heap_prop, + D3D12_HEAP_FLAG_NONE, &desc, + D3D12_RESOURCE_STATE_COMMON, nullptr, IID_PPV_ARGS (&bitstream)); + if (!gst_d3d12_result (hr, priv->device)) { + GST_ERROR_OBJECT (self, "Failed to create bitstream buffer"); + return FALSE; + } + + GST_LOG_OBJECT (self, "Allocated new bitstream buffer with size %" + G_GSIZE_FORMAT, size); + + priv->bitstream = bitstream; + priv->bitstream_size = alloc_size; + } + + hr = priv->bitstream->Map (0, nullptr, &map_data); + if (!gst_d3d12_result (hr, priv->device)) { + GST_ERROR_OBJECT (self, "Couldn't map bitstream buffer"); + return FALSE; + } + + memcpy (map_data, data, size); + priv->bitstream->Unmap (0, nullptr); + + return TRUE; +} + +GstFlowReturn +gst_d3d12_decoder_end_picture (GstD3D12Decoder * decoder, + GstCodecPicture * picture, GPtrArray * ref_pics, + const GstDxvaDecodingArgs * args) +{ + GstD3D12DecoderPrivate *priv = decoder->priv; + HRESULT hr; + D3D12_VIDEO_DECODE_OUTPUT_STREAM_ARGUMENTS out_args; + D3D12_VIDEO_DECODE_INPUT_STREAM_ARGUMENTS in_args; + ID3D12Resource *resource; + ID3D12Resource *out_resource = nullptr; + GstD3D12Memory *dmem; + UINT subresource[2]; + GstD3D12DecoderPicture *decoder_pic = (GstD3D12DecoderPicture *) + gst_codec_picture_get_user_data (picture); + UINT64 fence_value; + ID3D12Fence *fence_handle; + std::vector < D3D12_RESOURCE_BARRIER > pre_barriers; + std::vector < D3D12_RESOURCE_BARRIER > post_barriers; + + if (!decoder_pic) { + GST_ERROR_OBJECT (decoder, "No attached decoder picture"); + return GST_FLOW_ERROR; + } + + if (!args->bitstream || args->bitstream_size == 0) { + GST_ERROR_OBJECT (decoder, "No bitstream buffer passed"); + return GST_FLOW_ERROR; + } + + /* Wait for previous fence if needed */ + gst_d3d12_fence_wait (priv->fence); + + if (!gst_d3d12_decoder_upload_bitstream (decoder, args->bitstream, + args->bitstream_size)) { + return GST_FLOW_ERROR; + } + + memset (&in_args, 0, sizeof (D3D12_VIDEO_DECODE_INPUT_STREAM_ARGUMENTS)); + memset (&out_args, 0, sizeof (D3D12_VIDEO_DECODE_OUTPUT_STREAM_ARGUMENTS)); + + hr = priv->ca->Reset (); + if (!gst_d3d12_result (hr, priv->device)) { + GST_ERROR_OBJECT (decoder, "Couldn't reset command allocator"); + return GST_FLOW_ERROR; + } + + hr = priv->cl->Reset (priv->ca.Get ()); + if (!gst_d3d12_result (hr, priv->device)) { + GST_ERROR_OBJECT (decoder, "Couldn't reset command list"); + return GST_FLOW_ERROR; + } + + for (guint i = 0; i < ref_pics->len; i++) { + GstCodecPicture *ref_pic = + (GstCodecPicture *) g_ptr_array_index (ref_pics, i); + GstD3D12DecoderPicture *ref_dec_pic = + (GstD3D12DecoderPicture *) gst_codec_picture_get_user_data (ref_pic); + + if (!ref_dec_pic || ref_dec_pic == decoder_pic) + continue; + + dmem = (GstD3D12Memory *) ref_dec_pic->mem; + + resource = gst_d3d12_memory_get_resource_handle (dmem); + gst_d3d12_memory_get_subresource_index (dmem, 0, &subresource[0]); + gst_d3d12_memory_get_subresource_index (dmem, 1, &subresource[1]); + + pre_barriers.push_back (CD3D12_RESOURCE_BARRIER::Transition (resource, + D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_VIDEO_DECODE_READ, + subresource[0])); + pre_barriers.push_back (CD3D12_RESOURCE_BARRIER::Transition (resource, + D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_VIDEO_DECODE_READ, + subresource[1])); + + post_barriers.push_back (CD3D12_RESOURCE_BARRIER::Transition (resource, + D3D12_RESOURCE_STATE_VIDEO_DECODE_READ, D3D12_RESOURCE_STATE_COMMON, + subresource[0])); + post_barriers.push_back (CD3D12_RESOURCE_BARRIER::Transition (resource, + D3D12_RESOURCE_STATE_VIDEO_DECODE_READ, D3D12_RESOURCE_STATE_COMMON, + subresource[1])); + } + + dmem = (GstD3D12Memory *) decoder_pic->output_mem; + if (dmem) { + out_resource = gst_d3d12_memory_get_resource_handle (dmem); + + pre_barriers.push_back (CD3D12_RESOURCE_BARRIER::Transition (out_resource, + D3D12_RESOURCE_STATE_COMMON, + D3D12_RESOURCE_STATE_VIDEO_DECODE_WRITE)); + post_barriers.push_back (CD3D12_RESOURCE_BARRIER::Transition (out_resource, + D3D12_RESOURCE_STATE_VIDEO_DECODE_WRITE, + D3D12_RESOURCE_STATE_COMMON)); + } + + dmem = (GstD3D12Memory *) decoder_pic->mem; + resource = gst_d3d12_memory_get_resource_handle (dmem); + gst_d3d12_memory_get_subresource_index (GST_D3D12_MEMORY_CAST (dmem), 0, + &subresource[0]); + gst_d3d12_memory_get_subresource_index (GST_D3D12_MEMORY_CAST (dmem), 1, + &subresource[1]); + + pre_barriers.push_back (CD3D12_RESOURCE_BARRIER::Transition (resource, + D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_VIDEO_DECODE_WRITE, + subresource[0])); + pre_barriers.push_back (CD3D12_RESOURCE_BARRIER::Transition (resource, + D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_VIDEO_DECODE_WRITE, + subresource[1])); + + post_barriers.push_back (CD3D12_RESOURCE_BARRIER::Transition (resource, + D3D12_RESOURCE_STATE_VIDEO_DECODE_WRITE, D3D12_RESOURCE_STATE_COMMON, + subresource[0])); + post_barriers.push_back (CD3D12_RESOURCE_BARRIER::Transition (resource, + D3D12_RESOURCE_STATE_VIDEO_DECODE_WRITE, D3D12_RESOURCE_STATE_COMMON, + subresource[1])); + + priv->cl->ResourceBarrier (pre_barriers.size (), &pre_barriers[0]); + + if (out_resource) { + out_args.pOutputTexture2D = out_resource; + out_args.OutputSubresource = 0; + out_args.ConversionArguments.Enable = TRUE; + out_args.ConversionArguments.pReferenceTexture2D = resource; + out_args.ConversionArguments.ReferenceSubresource = subresource[0]; + } else { + out_args.pOutputTexture2D = resource; + out_args.OutputSubresource = subresource[0]; + out_args.ConversionArguments.Enable = FALSE; + } + + if (args->picture_params) { + in_args.FrameArguments[in_args.NumFrameArguments].Type = + D3D12_VIDEO_DECODE_ARGUMENT_TYPE_PICTURE_PARAMETERS; + in_args.FrameArguments[in_args.NumFrameArguments].Size = + args->picture_params_size; + in_args.FrameArguments[in_args.NumFrameArguments].pData = + args->picture_params; + in_args.NumFrameArguments++; + } + + if (args->slice_control) { + in_args.FrameArguments[in_args.NumFrameArguments].Type = + D3D12_VIDEO_DECODE_ARGUMENT_TYPE_SLICE_CONTROL; + in_args.FrameArguments[in_args.NumFrameArguments].Size = + args->slice_control_size; + in_args.FrameArguments[in_args.NumFrameArguments].pData = + args->slice_control; + in_args.NumFrameArguments++; + } + + if (args->inverse_quantization_matrix) { + in_args.FrameArguments[in_args.NumFrameArguments].Type = + D3D12_VIDEO_DECODE_ARGUMENT_TYPE_INVERSE_QUANTIZATION_MATRIX; + in_args.FrameArguments[in_args.NumFrameArguments].Size = + args->inverse_quantization_matrix_size; + in_args.FrameArguments[in_args.NumFrameArguments].pData = + args->inverse_quantization_matrix; + in_args.NumFrameArguments++; + } + + in_args.CompressedBitstream.pBuffer = priv->bitstream.Get (); + in_args.CompressedBitstream.Offset = 0; + in_args.CompressedBitstream.Size = args->bitstream_size; + in_args.pHeap = decoder_pic->heap.Get (); + + in_args.ReferenceFrames.NumTexture2Ds = priv->dpb->GetSize (); + in_args.ReferenceFrames.ppHeaps = priv->dpb->GetHeaps (); + in_args.ReferenceFrames.ppTexture2Ds = priv->dpb->GetTextures (); + in_args.ReferenceFrames.pSubresources = priv->dpb->GetSubresources (); + + priv->cl->DecodeFrame (priv->decoder.Get (), &out_args, &in_args); + priv->cl->ResourceBarrier (post_barriers.size (), &post_barriers[0]); + + hr = priv->cl->Close (); + if (!gst_d3d12_result (hr, priv->device)) { + GST_ERROR_OBJECT (decoder, "Couldn't record decoding command"); + return GST_FLOW_ERROR; + } + + ID3D12CommandList *cl[] = { priv->cl.Get () }; + priv->cq->ExecuteCommandLists (1, cl); + + fence_handle = gst_d3d12_fence_get_handle (priv->fence); + fence_value = priv->fence_value.fetch_add (1); + hr = priv->cq->Signal (fence_handle, fence_value); + if (!gst_d3d12_result (hr, priv->device)) { + GST_DEBUG_OBJECT (decoder, "Couldn't signal fence value"); + return GST_FLOW_ERROR; + } + + gst_d3d12_fence_set_event_on_completion_value (priv->fence, fence_value); + /* Will wait on output picture or start of next decoding loop */ + + return GST_FLOW_OK; +} + +static gboolean +gst_d3d12_decoder_ensure_staging_texture (GstD3D12Decoder * self) +{ + GstD3D12DecoderPrivate *priv = self->priv; + + if (priv->staging) + return TRUE; + + ComPtr < ID3D12Resource > staging; + HRESULT hr; + UINT64 size; + ID3D12Device *device = gst_d3d12_device_get_device_handle (priv->device); + D3D12_RESOURCE_DESC tex_desc = + CD3D12_RESOURCE_DESC::Tex2D (priv->decoder_format, + priv->aligned_width, + priv->aligned_height, + 1, + 1); + + device->GetCopyableFootprints (&tex_desc, 0, 2, 0, priv->layout, nullptr, + nullptr, &size); + + D3D12_HEAP_PROPERTIES heap_prop = CD3D12_HEAP_PROPERTIES + (D3D12_HEAP_TYPE_READBACK); + D3D12_RESOURCE_DESC desc = CD3D12_RESOURCE_DESC::Buffer (size); + + hr = device->CreateCommittedResource (&heap_prop, D3D12_HEAP_FLAG_NONE, + &desc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS (&staging)); + if (!gst_d3d12_result (hr, priv->device)) + return FALSE; + + priv->staging = staging; + + return TRUE; +} + +GstFlowReturn +gst_d3d12_decoder_output_picture (GstD3D12Decoder * decoder, + GstVideoDecoder * videodec, GstVideoCodecFrame * frame, + GstCodecPicture * picture, GstVideoBufferFlags buffer_flags, + gint display_width, gint display_height) +{ + GstD3D12DecoderPrivate *priv = decoder->priv; + GstD3D12DecoderPicture *decoder_pic; + std::vector < D3D12_RESOURCE_BARRIER > barriers; + GstFlowReturn ret = GST_FLOW_ERROR; + ID3D12CommandList *list[1]; + UINT64 fence_value; + GstMemory *mem; + GstD3D12Memory *dmem; + ID3D12Resource *resource; + UINT subresource[2]; + void *map_data; + HRESULT hr; + GstVideoFrame vframe; + + g_return_val_if_fail (GST_IS_D3D12_DECODER (decoder), GST_FLOW_ERROR); + g_return_val_if_fail (GST_IS_VIDEO_DECODER (videodec), GST_FLOW_ERROR); + g_return_val_if_fail (picture != nullptr, GST_FLOW_ERROR); + + priv = decoder->priv; + + decoder_pic = (GstD3D12DecoderPicture *) + gst_codec_picture_get_user_data (picture); + if (!decoder_pic) { + ret = GST_FLOW_ERROR; + goto error; + } + + if (picture->discont_state) { + g_clear_pointer (&priv->input_state, gst_video_codec_state_unref); + priv->input_state = gst_video_codec_state_ref (picture->discont_state); + + if (!gst_video_decoder_negotiate (videodec)) { + GST_ERROR_OBJECT (videodec, "Failed to re-negotiate with new frame size"); + ret = GST_FLOW_NOT_NEGOTIATED; + goto error; + } + } + + ret = gst_video_decoder_allocate_output_frame (videodec, frame); + if (ret != GST_FLOW_OK) { + GST_ERROR_OBJECT (videodec, "Couldn't allocate output buffer"); + goto error; + } + + if (!gst_d3d12_decoder_ensure_staging_texture (decoder)) { + GST_ERROR_OBJECT (videodec, "Couldn't allocate staging texture"); + ret = GST_FLOW_ERROR; + goto error; + } + + /* Wait for pending decoding operation before copying */ + gst_d3d12_fence_wait (priv->fence); + + mem = decoder_pic->output_mem ? decoder_pic->output_mem : decoder_pic->mem; + dmem = GST_D3D12_MEMORY_CAST (mem); + resource = gst_d3d12_memory_get_resource_handle (dmem); + + gst_d3d12_memory_get_subresource_index (dmem, 0, &subresource[0]); + gst_d3d12_memory_get_subresource_index (dmem, 1, &subresource[1]); + + /* Copy texture to staging */ + hr = priv->copy_ca->Reset (); + if (!gst_d3d12_result (hr, priv->device)) { + ret = GST_FLOW_ERROR; + goto error; + } + + hr = priv->copy_cl->Reset (priv->copy_ca.Get (), nullptr); + if (!gst_d3d12_result (hr, priv->device)) { + ret = GST_FLOW_ERROR; + goto error; + } + + barriers.push_back (CD3D12_RESOURCE_BARRIER::Transition (resource, + D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_SOURCE, + subresource[0])); + barriers.push_back (CD3D12_RESOURCE_BARRIER::Transition (resource, + D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_SOURCE, + subresource[1])); + + priv->copy_cl->ResourceBarrier (barriers.size (), &barriers[0]); + + for (guint i = 0; i < 2; i++) { + D3D12_TEXTURE_COPY_LOCATION src = + CD3D12_TEXTURE_COPY_LOCATION (resource, subresource[i]); + D3D12_TEXTURE_COPY_LOCATION dst = + CD3D12_TEXTURE_COPY_LOCATION (priv->staging.Get (), priv->layout[i]); + D3D12_BOX src_box = { 0, }; + + /* FIXME: only 4:2:0 */ + if (i == 0) { + src_box.left = GST_ROUND_UP_2 (priv->crop_x); + src_box.top = GST_ROUND_UP_2 (priv->crop_y); + src_box.right = GST_ROUND_UP_2 (priv->crop_x + priv->output_info.width); + src_box.bottom = GST_ROUND_UP_2 (priv->crop_y + priv->output_info.height); + } else { + src_box.left = GST_ROUND_UP_2 (priv->crop_x) / 2; + src_box.top = GST_ROUND_UP_2 (priv->crop_y) / 2; + src_box.right = + GST_ROUND_UP_2 (priv->crop_x + priv->output_info.width) / 2; + src_box.bottom = + GST_ROUND_UP_2 (priv->crop_y + priv->output_info.height) / 2; + } + + src_box.front = 0; + src_box.back = 1; + + priv->copy_cl->CopyTextureRegion (&dst, 0, 0, 0, &src, &src_box); + } + + hr = priv->copy_cl->Close (); + if (!gst_d3d12_result (hr, priv->device)) { + GST_ERROR_OBJECT (videodec, "Couldn't record copy command"); + ret = GST_FLOW_ERROR; + goto error; + } + + list[0] = priv->copy_cl.Get (); + priv->copy_cq->ExecuteCommandLists (1, list); + + fence_value = priv->fence_value.fetch_add (1); + hr = priv->copy_cq->Signal (gst_d3d12_fence_get_handle (priv->fence), + fence_value); + if (!gst_d3d12_result (hr, priv->device)) { + ret = GST_FLOW_ERROR; + goto error; + } + + gst_d3d12_fence_set_event_on_completion_value (priv->fence, fence_value); + gst_d3d12_fence_wait (priv->fence); + + hr = priv->staging->Map (0, nullptr, &map_data); + if (!gst_d3d12_result (hr, priv->device)) { + ret = GST_FLOW_ERROR; + goto error; + } + + gst_video_frame_map (&vframe, + &priv->output_info, frame->output_buffer, GST_MAP_WRITE); + + for (guint i = 0; i < GST_VIDEO_FRAME_N_PLANES (&vframe); i++) { + guint8 *src = (guint8 *) map_data + priv->layout[i].Offset; + guint8 *dst = (guint8 *) GST_VIDEO_FRAME_PLANE_DATA (&vframe, i); + gint width = GST_VIDEO_FRAME_COMP_WIDTH (&vframe, i) * + GST_VIDEO_FRAME_COMP_PSTRIDE (&vframe, i); + + for (guint j = 0; j < GST_VIDEO_FRAME_COMP_HEIGHT (&vframe, i); j++) { + memcpy (dst, src, width); + dst += GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, i); + src += priv->layout[i].Footprint.RowPitch; + } + } + + priv->staging->Unmap (0, nullptr); + gst_video_frame_unmap (&vframe); + + gst_codec_picture_unref (picture); + return gst_video_decoder_finish_frame (videodec, frame); + +error: + gst_codec_picture_unref (picture); + gst_video_decoder_release_frame (videodec, frame); + + return ret; +} + +static gboolean +gst_d3d12_decoder_open (GstD3D12Decoder * self) +{ + GstD3D12DecoderPrivate *priv = self->priv; + HRESULT hr; + gint aligned_width, aligned_height; + ID3D12VideoDevice *video_device; + GstVideoInfo *info = &priv->info; + D3D12_FEATURE_DATA_VIDEO_DECODE_SUPPORT support; + ComPtr < ID3D12VideoDecoderHeap > heap; + + if (priv->opened) + return TRUE; + + if (!priv->configured) { + GST_ERROR_OBJECT (self, "Should configure first"); + return FALSE; + } + + video_device = priv->video_device.Get (); + + const DecoderFormat *decoder_foramt = nullptr; + for (guint i = 0; i < G_N_ELEMENTS (format_list); i++) { + if (format_list[i].codec != priv->codec || + format_list[i].format != priv->decoder_format) { + continue; + } + + D3D12_FEATURE_DATA_VIDEO_DECODE_SUPPORT s; + s.NodeIndex = 0; + + s.Configuration.DecodeProfile = format_list[i].decode_profile; + s.Configuration.BitstreamEncryption = D3D12_BITSTREAM_ENCRYPTION_TYPE_NONE; + if (GST_VIDEO_INFO_IS_INTERLACED (info) && + GST_VIDEO_INFO_INTERLACE_MODE (info) != + GST_VIDEO_INTERLACE_MODE_ALTERNATE) { + s.Configuration.InterlaceType = + D3D12_VIDEO_FRAME_CODED_INTERLACE_TYPE_FIELD_BASED; + } else { + s.Configuration.InterlaceType = + D3D12_VIDEO_FRAME_CODED_INTERLACE_TYPE_NONE; + } + s.DecodeFormat = priv->decoder_format; + s.FrameRate = { 0, 1 }; + s.BitRate = 0; + s.Width = priv->coded_width; + s.Height = priv->coded_height; + + hr = video_device->CheckFeatureSupport (D3D12_FEATURE_VIDEO_DECODE_SUPPORT, + &s, sizeof (D3D12_FEATURE_DATA_VIDEO_DECODE_SUPPORT)); + if (FAILED (hr)) + continue; + + if ((s.SupportFlags & D3D12_VIDEO_DECODE_SUPPORT_FLAG_SUPPORTED) == 0) { + GST_INFO_OBJECT (self, "Device doesn't support current configuration"); + continue; + } + + decoder_foramt = &format_list[i]; + support = s; + + GST_INFO_OBJECT (self, + "Created decoder support tier %d with configuration flags 0x%x", + support.DecodeTier, support.ConfigurationFlags); + break; + } + + if (!decoder_foramt) { + GST_WARNING_OBJECT (self, "Failed to find supported configuration"); + return FALSE; + } + + guint alignment = 16; + if ((support.ConfigurationFlags & + D3D12_VIDEO_DECODE_CONFIGURATION_FLAG_HEIGHT_ALIGNMENT_MULTIPLE_32_REQUIRED) + != 0) { + alignment = 32; + } + + aligned_width = GST_ROUND_UP_N (priv->coded_width, alignment); + aligned_height = GST_ROUND_UP_N (priv->coded_height, alignment); + + priv->aligned_width = aligned_width; + priv->aligned_height = aligned_height; + + /* We can reuse decoder object, which is resolution independent */ + if (!priv->decoder || + priv->support.Configuration.DecodeProfile != + support.Configuration.DecodeProfile + || priv->support.Configuration.InterlaceType != + support.Configuration.InterlaceType) { + ComPtr < ID3D12VideoDecoder > decoder; + + priv->decoder = nullptr; + + D3D12_VIDEO_DECODER_DESC desc; + desc.NodeMask = 0; + desc.Configuration = support.Configuration; + hr = video_device->CreateVideoDecoder (&desc, IID_PPV_ARGS (&decoder)); + if (!gst_d3d12_result (hr, priv->device)) { + GST_ERROR_OBJECT (self, "Failed to create decoder"); + return FALSE; + } + + priv->decoder = decoder; + } + + D3D12_VIDEO_DECODER_HEAP_DESC heap_desc; + heap_desc.NodeMask = 0; + heap_desc.Configuration = support.Configuration; + heap_desc.DecodeWidth = priv->aligned_width; + heap_desc.DecodeHeight = priv->aligned_height; + heap_desc.Format = priv->decoder_format; + heap_desc.FrameRate = { 0, 1 }; + heap_desc.BitRate = 0; + heap_desc.MaxDecodePictureBufferCount = priv->dpb_size; + hr = video_device->CreateVideoDecoderHeap (&heap_desc, IID_PPV_ARGS (&heap)); + if (!gst_d3d12_result (hr, priv->device)) { + GST_ERROR_OBJECT (self, "Failed to create decoder heap"); + return FALSE; + } + + priv->heap = heap; + priv->support = support; + + priv->opened = TRUE; + + return TRUE; +} + +gboolean +gst_d3d12_decoder_negotiate (GstD3D12Decoder * decoder, + GstVideoDecoder * videodec) +{ + g_return_val_if_fail (GST_IS_D3D12_DECODER (decoder), FALSE); + g_return_val_if_fail (GST_IS_VIDEO_DECODER (videodec), FALSE); + + GstD3D12DecoderPrivate *priv = decoder->priv; + GstVideoInfo *info = &priv->output_info; + GstVideoCodecState *state = nullptr; + GstVideoCodecState *input_state = priv->input_state; + GstStructure *s; + const gchar *str; + + /* TODO: add support alternate interlace */ + state = gst_video_decoder_set_interlaced_output_state (videodec, + GST_VIDEO_INFO_FORMAT (info), GST_VIDEO_INFO_INTERLACE_MODE (info), + GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info), input_state); + + if (!state) { + GST_ERROR_OBJECT (decoder, "Couldn't set output state"); + return FALSE; + } + + state->caps = gst_video_info_to_caps (&state->info); + + s = gst_caps_get_structure (input_state->caps, 0); + str = gst_structure_get_string (s, "mastering-display-info"); + if (str) { + gst_caps_set_simple (state->caps, + "mastering-display-info", G_TYPE_STRING, str, nullptr); + } + + str = gst_structure_get_string (s, "content-light-level"); + if (str) { + gst_caps_set_simple (state->caps, + "content-light-level", G_TYPE_STRING, str, nullptr); + } + + g_clear_pointer (&priv->output_state, gst_video_codec_state_unref); + priv->output_state = state; + + return gst_d3d12_decoder_open (decoder); +} + +gboolean +gst_d3d12_decoder_decide_allocation (GstD3D12Decoder * decoder, + GstVideoDecoder * videodec, GstQuery * query) +{ + GstD3D12DecoderPrivate *priv = decoder->priv; + GstCaps *outcaps; + GstBufferPool *pool = nullptr; + guint n, size, min = 0, max = 0; + GstVideoInfo vinfo = { 0, }; + GstStructure *config; + + g_return_val_if_fail (GST_IS_D3D12_DECODER (decoder), FALSE); + g_return_val_if_fail (GST_IS_VIDEO_DECODER (videodec), FALSE); + g_return_val_if_fail (query != nullptr, FALSE); + + if (!priv->opened) { + GST_ERROR_OBJECT (videodec, "Should open decoder first"); + return FALSE; + } + + gst_query_parse_allocation (query, &outcaps, nullptr); + + if (!outcaps) { + GST_DEBUG_OBJECT (decoder, "No output caps"); + return FALSE; + } + + gst_video_info_from_caps (&vinfo, outcaps); + n = gst_query_get_n_allocation_pools (query); + if (n > 0) + gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max); + + if (!pool) { + pool = gst_video_buffer_pool_new (); + size = (guint) vinfo.size; + } + + config = gst_buffer_pool_get_config (pool); + gst_buffer_pool_config_set_params (config, outcaps, size, min, max); + gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META); + + gst_buffer_pool_set_config (pool, config); + + if (n > 0) + gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max); + else + gst_query_add_allocation_pool (query, pool, size, min, max); + gst_object_unref (pool); + + return TRUE; +} + +enum +{ + PROP_DECODER_ADAPTER_LUID = 1, + PROP_DECODER_DEVICE_ID, + PROP_DECODER_VENDOR_ID, +}; + +struct _GstD3D12DecoderClassData +{ + GstD3D12DecoderSubClassData subclass_data; + GstCaps *sink_caps; + GstCaps *src_caps; + gchar *description; +}; + +GstD3D12DecoderClassData * +gst_d3d12_decoder_check_feature_support (GstD3D12Device * device, + ID3D12VideoDevice * video_device, GstDxvaCodec codec) +{ + HRESULT hr; + GstDxvaResolution max_resolution = { 0, 0 }; + D3D12_VIDEO_DECODE_CONFIGURATION_FLAGS config_flags = + D3D12_VIDEO_DECODE_CONFIGURATION_FLAG_NONE; + D3D12_VIDEO_DECODE_TIER tier = D3D12_VIDEO_DECODE_TIER_NOT_SUPPORTED; + std::set < DXGI_FORMAT > supported_formats; + + g_return_val_if_fail (GST_IS_D3D12_DEVICE (device), nullptr); + g_return_val_if_fail (video_device != nullptr, nullptr); + + for (guint i = 0; i < G_N_ELEMENTS (format_list); i++) { + if (format_list[i].codec != codec) + continue; + + D3D12_FEATURE_DATA_VIDEO_DECODE_SUPPORT s; + s.NodeIndex = 0; + s.Configuration.DecodeProfile = format_list[i].decode_profile; + s.Configuration.BitstreamEncryption = D3D12_BITSTREAM_ENCRYPTION_TYPE_NONE; + s.Configuration.InterlaceType = D3D12_VIDEO_FRAME_CODED_INTERLACE_TYPE_NONE; + s.DecodeFormat = format_list[i].format; + s.FrameRate = { 0, 1 }; + s.BitRate = 0; + + for (guint j = 0; j < G_N_ELEMENTS (gst_dxva_resolutions); j++) { + s.Width = gst_dxva_resolutions[j].width; + s.Height = gst_dxva_resolutions[j].height; + + hr = video_device->CheckFeatureSupport + (D3D12_FEATURE_VIDEO_DECODE_SUPPORT, &s, + sizeof (D3D12_FEATURE_DATA_VIDEO_DECODE_SUPPORT)); + if (FAILED (hr)) + break; + + if ((s.SupportFlags & D3D12_VIDEO_DECODE_SUPPORT_FLAG_SUPPORTED) == 0) + break; + + if (max_resolution.width < gst_dxva_resolutions[j].width) + max_resolution.width = gst_dxva_resolutions[j].width; + if (max_resolution.height < gst_dxva_resolutions[j].height) + max_resolution.height = gst_dxva_resolutions[j].height; + + supported_formats.insert (format_list[i].format); + config_flags = s.ConfigurationFlags; + tier = s.DecodeTier; + } + } + + if (supported_formats.empty ()) { + GST_DEBUG_OBJECT (device, "Device doesn't support %s", + gst_dxva_codec_to_string (codec)); + return nullptr; + } + + GST_DEBUG_OBJECT (device, + "Device supports codec %s (%dx%d), configuration flags 0x%x, tier: %d", + gst_dxva_codec_to_string (codec), + max_resolution.width, max_resolution.height, config_flags, tier); + + std::string format_string; + + /* *INDENT-OFF* */ + for (const auto &iter : supported_formats) { + GstVideoFormat format = gst_d3d12_dxgi_format_to_gst (iter); + + if (format == GST_VIDEO_FORMAT_UNKNOWN) { + GST_ERROR_OBJECT (device, + "Failed to get video format from dxgi format %d", iter); + } + + if (!format_string.empty ()) + format_string += ", "; + + format_string += gst_video_format_to_string (format); + } + /* *INDENT-ON* */ + + std::string src_caps_string; + /* TODO: support d3d12 memory */ + src_caps_string = "video/x-raw, format = (string) "; + if (supported_formats.size () > 1) { + src_caps_string += "{ " + format_string + " }"; + } else { + src_caps_string += format_string; + } + + GstCaps *src_caps = gst_caps_from_string (src_caps_string.c_str ()); + GstCaps *sink_caps; + + switch (codec) { + case GST_DXVA_CODEC_H264: + sink_caps = gst_caps_from_string ("video/x-h264, " + "stream-format= (string) { avc, avc3, byte-stream }, " + "alignment= (string) au, " + "profile = (string) { high, progressive-high, constrained-high, main, " + "constrained-baseline, baseline }"); + break; + default: + g_assert_not_reached (); + return nullptr; + } + + gint max_res = MAX (max_resolution.width, max_resolution.height); + gst_caps_set_simple (sink_caps, + "width", GST_TYPE_INT_RANGE, 1, max_res, + "height", GST_TYPE_INT_RANGE, 1, max_res, nullptr); + gst_caps_set_simple (src_caps, + "width", GST_TYPE_INT_RANGE, 1, max_res, + "height", GST_TYPE_INT_RANGE, 1, max_res, nullptr); + + GstD3D12DecoderClassData *ret = g_new0 (GstD3D12DecoderClassData, 1); + GstD3D12DecoderSubClassData *subclass_data = &ret->subclass_data; + + ret->sink_caps = sink_caps; + ret->src_caps = src_caps; + + subclass_data->codec = codec; + + /* class data will be leaked if the element never gets instantiated */ + GST_MINI_OBJECT_FLAG_SET (sink_caps, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED); + GST_MINI_OBJECT_FLAG_SET (src_caps, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED); + + g_object_get (device, "adapter-luid", &subclass_data->adapter_luid, + "device-id", &subclass_data->device_id, "vendor-id", + &subclass_data->vendor_id, "description", &ret->description, nullptr); + + GST_DEBUG_OBJECT (device, + "Configured sink caps: %" GST_PTR_FORMAT ", src caps: %" GST_PTR_FORMAT, + sink_caps, src_caps); + + return ret; +} + +static void +gst_d3d12_decoder_class_data_free (GstD3D12DecoderClassData * data) +{ + if (!data) + return; + + gst_clear_caps (&data->sink_caps); + gst_clear_caps (&data->src_caps); + g_free (data->description); + g_free (data); +} + +void +gst_d3d12_decoder_class_data_fill_subclass_data (GstD3D12DecoderClassData * + data, GstD3D12DecoderSubClassData * subclass_data) +{ + g_return_if_fail (data != nullptr); + g_return_if_fail (subclass_data != nullptr); + + *subclass_data = data->subclass_data; +} + +void +gst_d3d12_decoder_proxy_class_init (GstElementClass * klass, + GstD3D12DecoderClassData * data, const gchar * author) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstD3D12DecoderSubClassData *cdata = &data->subclass_data; + std::string long_name; + std::string description; + const gchar *codec_name; + GParamFlags param_flags = (GParamFlags) (GST_PARAM_DOC_SHOW_DEFAULT | + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + GstPadTemplate *pad_templ; + + g_object_class_install_property (gobject_class, PROP_DECODER_ADAPTER_LUID, + g_param_spec_int64 ("adapter-luid", "Adapter LUID", + "DXGI Adapter LUID (Locally Unique Identifier) of created device", + G_MININT64, G_MAXINT64, 0, param_flags)); + + g_object_class_install_property (gobject_class, PROP_DECODER_DEVICE_ID, + g_param_spec_uint ("device-id", "Device Id", + "DXGI Device ID", 0, G_MAXUINT32, 0, param_flags)); + + g_object_class_install_property (gobject_class, PROP_DECODER_VENDOR_ID, + g_param_spec_uint ("vendor-id", "Vendor Id", + "DXGI Vendor ID", 0, G_MAXUINT32, 0, param_flags)); + + codec_name = gst_dxva_codec_to_string (cdata->codec); + long_name = "Direct3D12/DXVA " + std::string (codec_name) + " " + + std::string (data->description) + " Decoder"; + description = "Direct3D12/DXVA based " + std::string (codec_name) + + " video decoder"; + + gst_element_class_set_metadata (klass, long_name.c_str (), + "Codec/Decoder/Video/Hardware", description.c_str (), author); + + pad_templ = gst_pad_template_new ("sink", + GST_PAD_SINK, GST_PAD_ALWAYS, data->sink_caps); + gst_element_class_add_pad_template (klass, pad_templ); + + pad_templ = gst_pad_template_new ("src", + GST_PAD_SRC, GST_PAD_ALWAYS, data->src_caps); + gst_element_class_add_pad_template (klass, pad_templ); + + gst_d3d12_decoder_class_data_free (data); +} + +void +gst_d3d12_decoder_proxy_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec, + GstD3D12DecoderSubClassData * subclass_data) +{ + switch (prop_id) { + case PROP_DECODER_ADAPTER_LUID: + g_value_set_int64 (value, subclass_data->adapter_luid); + break; + case PROP_DECODER_DEVICE_ID: + g_value_set_uint (value, subclass_data->device_id); + break; + case PROP_DECODER_VENDOR_ID: + g_value_set_uint (value, subclass_data->vendor_id); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +gboolean +gst_d3d12_decoder_proxy_open (GstVideoDecoder * videodec, + GstD3D12DecoderSubClassData * subclass_data, GstD3D12Device ** device, + GstD3D12Decoder ** decoder) +{ + GstElement *elem = GST_ELEMENT (videodec); + + if (!gst_d3d12_ensure_element_data_for_adapter_luid (elem, + subclass_data->adapter_luid, device)) { + GST_ERROR_OBJECT (elem, "Cannot create d3d12device"); + return FALSE; + } + + *decoder = gst_d3d12_decoder_new (*device, subclass_data->codec); + + if (*decoder == nullptr) { + GST_ERROR_OBJECT (elem, "Cannot create d3d12 decoder"); + gst_clear_object (device); + return FALSE; + } + + return TRUE; +} diff --git a/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12decoder.h b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12decoder.h new file mode 100644 index 0000000000..7210062cc4 --- /dev/null +++ b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12decoder.h @@ -0,0 +1,167 @@ +/* GStreamer + * Copyright (C) 2023 Seungha Yang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include +#include +#include +#include +#include "gstd3d12_fwd.h" + +G_BEGIN_DECLS + +#define GST_TYPE_D3D12_DECODER (gst_d3d12_decoder_get_type()) +G_DECLARE_FINAL_TYPE (GstD3D12Decoder, + gst_d3d12_decoder, GST, D3D12_DECODER, GstObject); + +typedef struct _GstD3D12DecoderClassData GstD3D12DecoderClassData; + +struct GstD3D12DecoderSubClassData +{ + GstDxvaCodec codec; + gint64 adapter_luid; + guint device_id; + guint vendor_id; +}; + +#define GST_D3D12_DECODER_DEFINE_TYPE(ModuleObjName,module_obj_name,MODULE,OBJ_NAME,ParentName) \ + static GstElementClass *parent_class = NULL; \ + typedef struct _##ModuleObjName { \ + ParentName parent; \ + GstD3D12Device *device; \ + GstD3D12Decoder *decoder; \ + } ModuleObjName;\ + typedef struct _##ModuleObjName##Class { \ + ParentName##Class parent_class; \ + GstD3D12DecoderSubClassData class_data; \ + } ModuleObjName##Class; \ + static inline ModuleObjName * MODULE##_##OBJ_NAME (gpointer ptr) { \ + return (ModuleObjName *) (ptr); \ + } \ + static inline ModuleObjName##Class * MODULE##_##OBJ_NAME##_GET_CLASS (gpointer ptr) { \ + return G_TYPE_INSTANCE_GET_CLASS ((ptr),G_TYPE_FROM_INSTANCE(ptr),ModuleObjName##Class); \ + } \ + static void module_obj_name##_get_property (GObject * object, \ + guint prop_id, GValue * value, GParamSpec * pspec); \ + static void module_obj_name##_set_context (GstElement * element, \ + GstContext * context); \ + static gboolean module_obj_name##_open (GstVideoDecoder * decoder); \ + static gboolean module_obj_name##_close (GstVideoDecoder * decoder); \ + static gboolean module_obj_name##_negotiate (GstVideoDecoder * decoder); \ + static gboolean module_obj_name##_decide_allocation (GstVideoDecoder * decoder, \ + GstQuery * query); \ + static gboolean module_obj_name##_sink_query (GstVideoDecoder * decoder, \ + GstQuery * query); \ + static gboolean module_obj_name##_src_query (GstVideoDecoder * decoder, \ + GstQuery * query); \ + static GstFlowReturn module_obj_name##_configure (ParentName * decoder, \ + GstVideoCodecState * input_state, const GstVideoInfo * info, \ + gint crop_x, gint crop_y, \ + gint coded_width, gint coded_height, gint max_dpb_size); \ + static GstFlowReturn module_obj_name##_new_picture (ParentName * decoder, \ + GstCodecPicture * picture); \ + static guint8 module_obj_name##_get_picture_id (ParentName * decoder, \ + GstCodecPicture * picture); \ + static GstFlowReturn module_obj_name##_start_picture (ParentName * decoder, \ + GstCodecPicture * picture, guint8 * picture_id); \ + static GstFlowReturn module_obj_name##_end_picture (ParentName * decoder, \ + GstCodecPicture * picture, GPtrArray * ref_pics, \ + const GstDxvaDecodingArgs * args); \ + static GstFlowReturn module_obj_name##_output_picture (ParentName * decoder, \ + GstVideoCodecFrame * frame, GstCodecPicture * picture, \ + GstVideoBufferFlags buffer_flags, \ + gint display_width, gint display_height); + +#define GST_D3D12_DECODER_DEFINE_TYPE_FULL(ModuleObjName,module_obj_name,MODULE,OBJ_NAME,ParentName) \ + GST_D3D12_DECODER_DEFINE_TYPE(ModuleObjName,module_obj_name,MODULE,OBJ_NAME,ParentName); \ + static GstFlowReturn module_obj_name##_duplicate_picture (ParentName * decoder, \ + GstCodecPicture * src, GstCodecPicture * dst); + +GstD3D12Decoder * gst_d3d12_decoder_new (GstD3D12Device * device, + GstDxvaCodec codec); + +GstFlowReturn gst_d3d12_decoder_configure (GstD3D12Decoder * decoder, + GstVideoCodecState * input_state, + const GstVideoInfo * info, + gint crop_x, + gint crop_y, + gint coded_width, + gint coded_height, + guint dpb_size); + +GstFlowReturn gst_d3d12_decoder_new_picture (GstD3D12Decoder * decoder, + GstVideoDecoder * videodec, + GstCodecPicture * picture); + +GstFlowReturn gst_d3d12_decoder_duplicate_picture (GstD3D12Decoder * decoder, + GstCodecPicture * src, + GstCodecPicture * dst); + +guint8 gst_d3d12_decoder_get_picture_id (GstD3D12Decoder * decoder, + GstCodecPicture * picture); + +GstFlowReturn gst_d3d12_decoder_start_picture (GstD3D12Decoder * decoder, + GstCodecPicture * picture, + guint8 * picture_id); + +GstFlowReturn gst_d3d12_decoder_end_picture (GstD3D12Decoder * decoder, + GstCodecPicture * picture, + GPtrArray * ref_pics, + const GstDxvaDecodingArgs * args); + +GstFlowReturn gst_d3d12_decoder_output_picture (GstD3D12Decoder * decoder, + GstVideoDecoder * videodec, + GstVideoCodecFrame * frame, + GstCodecPicture * picture, + GstVideoBufferFlags buffer_flags, + gint display_width, + gint display_height); + +gboolean gst_d3d12_decoder_negotiate (GstD3D12Decoder * decoder, + GstVideoDecoder * videodec); + +gboolean gst_d3d12_decoder_decide_allocation (GstD3D12Decoder * decoder, + GstVideoDecoder * videodec, + GstQuery * query); + +/* Utils for element registration */ +GstD3D12DecoderClassData * gst_d3d12_decoder_check_feature_support (GstD3D12Device * device, + ID3D12VideoDevice * video_device, + GstDxvaCodec codec); + +void gst_d3d12_decoder_class_data_fill_subclass_data (GstD3D12DecoderClassData * data, + GstD3D12DecoderSubClassData * subclass_data); + +void gst_d3d12_decoder_proxy_class_init (GstElementClass * klass, + GstD3D12DecoderClassData * data, + const gchar * author); + +void gst_d3d12_decoder_proxy_get_property (GObject * object, + guint prop_id, + GValue * value, + GParamSpec * pspec, + GstD3D12DecoderSubClassData * subclass_data); + +gboolean gst_d3d12_decoder_proxy_open (GstVideoDecoder * videodec, + GstD3D12DecoderSubClassData * subclass_data, + GstD3D12Device ** device, + GstD3D12Decoder ** decoder); + +G_END_DECLS diff --git a/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12device.cpp b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12device.cpp new file mode 100644 index 0000000000..486f9c7d5f --- /dev/null +++ b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12device.cpp @@ -0,0 +1,802 @@ +/* GStreamer + * Copyright (C) 2023 Seungha Yang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstd3d12device.h" +#include "gstd3d12utils.h" +#include "gstd3d12format.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_D3D12_SDKLAYERS_H +#include +#endif + +GST_DEBUG_CATEGORY_STATIC (gst_d3d12_device_debug); +GST_DEBUG_CATEGORY_STATIC (gst_d3d12_sdk_debug); +#define GST_CAT_DEFAULT gst_d3d12_device_debug + +#define MAKE_FORMAT_MAP_YUV(g,d,r0,r1,r2,r3) \ + { GST_VIDEO_FORMAT_ ##g, DXGI_FORMAT_ ##d, \ + { DXGI_FORMAT_ ##r0, DXGI_FORMAT_ ##r1, DXGI_FORMAT_ ##r2, DXGI_FORMAT_ ##r3 }, \ + { DXGI_FORMAT_ ##r0, DXGI_FORMAT_ ##r1, DXGI_FORMAT_ ##r2, DXGI_FORMAT_ ##r3 }, \ + (D3D12_FORMAT_SUPPORT1) (D3D12_FORMAT_SUPPORT1_TEXTURE2D | \ + D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE | D3D12_FORMAT_SUPPORT1_RENDER_TARGET) } + +#define MAKE_FORMAT_MAP_YUV_FULL(g,d,r0,r1,r2,r3,f) \ + { GST_VIDEO_FORMAT_ ##g, DXGI_FORMAT_ ##d, \ + { DXGI_FORMAT_ ##r0, DXGI_FORMAT_ ##r1, DXGI_FORMAT_ ##r2, DXGI_FORMAT_ ##r3 }, \ + { DXGI_FORMAT_ ##r0, DXGI_FORMAT_ ##r1, DXGI_FORMAT_ ##r2, DXGI_FORMAT_ ##r3 }, \ + (D3D12_FORMAT_SUPPORT1) (f) } + +#define MAKE_FORMAT_MAP_RGB(g,d) \ + { GST_VIDEO_FORMAT_ ##g, DXGI_FORMAT_ ##d, \ + { DXGI_FORMAT_ ##d, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN }, \ + { DXGI_FORMAT_ ##d, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN }, \ + (D3D12_FORMAT_SUPPORT1) (D3D12_FORMAT_SUPPORT1_TEXTURE2D | \ + D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE | D3D12_FORMAT_SUPPORT1_RENDER_TARGET) } + +#define MAKE_FORMAT_MAP_RGBP(g,d,a) \ + { GST_VIDEO_FORMAT_ ##g, DXGI_FORMAT_UNKNOWN, \ + { DXGI_FORMAT_ ##d, DXGI_FORMAT_ ##d, DXGI_FORMAT_ ##d, DXGI_FORMAT_ ##a }, \ + { DXGI_FORMAT_ ##d, DXGI_FORMAT_ ##d, DXGI_FORMAT_ ##d, DXGI_FORMAT_ ##a }, \ + (D3D12_FORMAT_SUPPORT1) (D3D12_FORMAT_SUPPORT1_TEXTURE2D | \ + D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE | D3D12_FORMAT_SUPPORT1_RENDER_TARGET) } + +static const GstD3D12Format _gst_d3d12_default_format_map[] = { + MAKE_FORMAT_MAP_RGB (BGRA, B8G8R8A8_UNORM), + MAKE_FORMAT_MAP_RGB (RGBA, R8G8B8A8_UNORM), + MAKE_FORMAT_MAP_RGB (BGRx, B8G8R8A8_UNORM), + MAKE_FORMAT_MAP_RGB (RGBx, R8G8B8A8_UNORM), + MAKE_FORMAT_MAP_RGB (RGB10A2_LE, R10G10B10A2_UNORM), + MAKE_FORMAT_MAP_RGB (RGBA64_LE, R16G16B16A16_UNORM), + MAKE_FORMAT_MAP_YUV (AYUV, UNKNOWN, R8G8B8A8_UNORM, UNKNOWN, UNKNOWN, + UNKNOWN), + MAKE_FORMAT_MAP_YUV (AYUV64, UNKNOWN, R16G16B16A16_UNORM, UNKNOWN, UNKNOWN, + UNKNOWN), + MAKE_FORMAT_MAP_YUV (VUYA, AYUV, R8G8B8A8_UNORM, UNKNOWN, UNKNOWN, UNKNOWN), + MAKE_FORMAT_MAP_YUV (NV12, NV12, R8_UNORM, R8G8_UNORM, UNKNOWN, UNKNOWN), + MAKE_FORMAT_MAP_YUV (NV21, UNKNOWN, R8_UNORM, R8G8_UNORM, UNKNOWN, UNKNOWN), + MAKE_FORMAT_MAP_YUV (P010_10LE, P010, R16_UNORM, R16G16_UNORM, UNKNOWN, + UNKNOWN), + MAKE_FORMAT_MAP_YUV (P012_LE, P016, R16_UNORM, R16G16_UNORM, UNKNOWN, + UNKNOWN), + MAKE_FORMAT_MAP_YUV (P016_LE, P016, R16_UNORM, R16G16_UNORM, UNKNOWN, + UNKNOWN), + MAKE_FORMAT_MAP_YUV (I420, UNKNOWN, R8_UNORM, R8_UNORM, R8_UNORM, UNKNOWN), + MAKE_FORMAT_MAP_YUV (YV12, UNKNOWN, R8_UNORM, R8_UNORM, R8_UNORM, UNKNOWN), + MAKE_FORMAT_MAP_YUV (I420_10LE, UNKNOWN, R16_UNORM, R16_UNORM, R16_UNORM, + UNKNOWN), + MAKE_FORMAT_MAP_YUV (I420_12LE, UNKNOWN, R16_UNORM, R16_UNORM, R16_UNORM, + UNKNOWN), + MAKE_FORMAT_MAP_YUV (Y42B, UNKNOWN, R8_UNORM, R8_UNORM, R8_UNORM, UNKNOWN), + MAKE_FORMAT_MAP_YUV (I422_10LE, UNKNOWN, R16_UNORM, R16_UNORM, R16_UNORM, + UNKNOWN), + MAKE_FORMAT_MAP_YUV (I422_12LE, UNKNOWN, R16_UNORM, R16_UNORM, R16_UNORM, + UNKNOWN), + MAKE_FORMAT_MAP_YUV (Y444, UNKNOWN, R8_UNORM, R8_UNORM, R8_UNORM, UNKNOWN), + MAKE_FORMAT_MAP_YUV (Y444_10LE, UNKNOWN, R16_UNORM, R16_UNORM, R16_UNORM, + UNKNOWN), + MAKE_FORMAT_MAP_YUV (Y444_12LE, UNKNOWN, R16_UNORM, R16_UNORM, R16_UNORM, + UNKNOWN), + MAKE_FORMAT_MAP_YUV (Y444_16LE, UNKNOWN, R16_UNORM, R16_UNORM, R16_UNORM, + UNKNOWN), + /* GRAY */ + /* NOTE: To support conversion by using video processor, + * mark DXGI_FORMAT_{R8,R16}_UNORM formats as known dxgi_format. + * Otherwise, d3d12 elements will not try to use video processor for + * those formats */ + MAKE_FORMAT_MAP_RGB (GRAY8, R8_UNORM), + MAKE_FORMAT_MAP_RGB (GRAY16_LE, R16_UNORM), + MAKE_FORMAT_MAP_YUV_FULL (Y410, Y410, R10G10B10A2_UNORM, UNKNOWN, UNKNOWN, + UNKNOWN, + D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE), + MAKE_FORMAT_MAP_YUV_FULL (YUY2, YUY2, R8G8B8A8_UNORM, UNKNOWN, UNKNOWN, + UNKNOWN, + D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE), + MAKE_FORMAT_MAP_RGBP (RGBP, R8_UNORM, UNKNOWN), + MAKE_FORMAT_MAP_RGBP (BGRP, R8_UNORM, UNKNOWN), + MAKE_FORMAT_MAP_RGBP (GBR, R8_UNORM, UNKNOWN), + MAKE_FORMAT_MAP_RGBP (GBR_10LE, R16_UNORM, UNKNOWN), + MAKE_FORMAT_MAP_RGBP (GBR_12LE, R16_UNORM, UNKNOWN), + MAKE_FORMAT_MAP_RGBP (GBRA, R8_UNORM, R8_UNORM), + MAKE_FORMAT_MAP_RGBP (GBRA_10LE, R16_UNORM, R16_UNORM), + MAKE_FORMAT_MAP_RGBP (GBRA_12LE, R16_UNORM, R16_UNORM), +}; + +#undef MAKE_FORMAT_MAP_YUV +#undef MAKE_FORMAT_MAP_YUV_FULL +#undef MAKE_FORMAT_MAP_RGB + +#define GST_D3D12_N_FORMATS G_N_ELEMENTS(_gst_d3d12_default_format_map) + +enum +{ + PROP_0, + PROP_ADAPTER_INDEX, + PROP_ADAPTER_LUID, + PROP_DEVICE_ID, + PROP_VENDOR_ID, + PROP_HARDWARE, + PROP_DESCRIPTION, +}; + +/* d3d12 devices are singtones per adapter. Keep track of live objects and + * reuse already created object if possible */ +/* *INDENT-OFF* */ +std::mutex device_list_lock_; +std::vector live_devices_; + +using namespace Microsoft::WRL; + +struct _GstD3D12DevicePrivate +{ + ComPtr device; + ComPtr adapter; + ComPtr factory; + std::vector < GstD3D12Format> formats; + std::mutex lock; + std::recursive_mutex extern_lock; + std::atomic fence_value; + +#ifdef HAVE_D3D12_SDKLAYERS_H + ComPtr info_queue; +#endif + + guint adapter_index = 0; + guint device_id = 0; + guint vendor_id = 0; + std::string description; + gint64 adapter_luid = 0; +}; +/* *INDENT-ON* */ + +#ifdef HAVE_D3D12_SDKLAYERS_H +static gboolean +gst_d3d12_device_enable_debug (void) +{ + static gboolean enabled = FALSE; + + GST_D3D12_CALL_ONCE_BEGIN { + GST_DEBUG_CATEGORY_INIT (gst_d3d12_device_debug, + "d3d12device", 0, "d3d12 device object"); + GST_DEBUG_CATEGORY_INIT (gst_d3d12_sdk_debug, + "d3d12debuglayer", 0, "d3d12 SDK layer debug"); + + /* Enables debug layer only if it's requested, otherwise + * already configured d3d12 devices (e.g., owned by application) + * will be invalidated by ID3D12Debug::EnableDebugLayer() */ + if (!g_getenv ("GST_ENABLE_D3D12_DEBUG")) + return; + + HRESULT hr; + ComPtr < ID3D12Debug > d3d12_debug; + hr = D3D12GetDebugInterface (IID_PPV_ARGS (&d3d12_debug)); + if (FAILED (hr)) + return; + + d3d12_debug->EnableDebugLayer (); + enabled = TRUE; + + GST_INFO ("D3D12 debug layer is enabled"); + + ComPtr < ID3D12Debug5 > d3d12_debug5; + hr = d3d12_debug.As (&d3d12_debug5); + if (SUCCEEDED (hr)) + d3d12_debug5->SetEnableAutoName (TRUE); + + ComPtr < ID3D12Debug1 > d3d12_debug1; + hr = d3d12_debug.As (&d3d12_debug1); + if (FAILED (hr)) + return; + + d3d12_debug1->SetEnableSynchronizedCommandQueueValidation (TRUE); + + GST_INFO ("Enabled synchronized command queue validation"); + + if (!g_getenv ("GST_ENABLE_D3D12_DEBUG_GPU_VALIDATION")) + return; + + d3d12_debug1->SetEnableGPUBasedValidation (TRUE); + + GST_INFO ("Enabled GPU based validation"); + } + GST_D3D12_CALL_ONCE_END; + + return enabled; +} +#endif + +#define gst_d3d12_device_parent_class parent_class +G_DEFINE_TYPE (GstD3D12Device, gst_d3d12_device, GST_TYPE_OBJECT); + +static void gst_d3d12_device_finalize (GObject * object); +static void gst_d3d12_device_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void gst_d3d12_device_setup_format_table (GstD3D12Device * self); + +static void +gst_d3d12_device_class_init (GstD3D12DeviceClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GParamFlags readable_flags = + (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + gobject_class->finalize = gst_d3d12_device_finalize; + gobject_class->get_property = gst_d3d12_device_get_property; + + g_object_class_install_property (gobject_class, PROP_ADAPTER_INDEX, + g_param_spec_uint ("adapter-index", "Adapter Index", + "DXGI Adapter index for creating device", + 0, G_MAXUINT32, 0, readable_flags)); + + g_object_class_install_property (gobject_class, PROP_ADAPTER_LUID, + g_param_spec_int64 ("adapter-luid", "Adapter LUID", + "DXGI Adapter LUID (Locally Unique Identifier) of created device", + 0, G_MAXINT64, 0, readable_flags)); + + g_object_class_install_property (gobject_class, PROP_DEVICE_ID, + g_param_spec_uint ("device-id", "Device Id", + "DXGI Device ID", 0, G_MAXUINT32, 0, readable_flags)); + + g_object_class_install_property (gobject_class, PROP_VENDOR_ID, + g_param_spec_uint ("vendor-id", "Vendor Id", + "DXGI Vendor ID", 0, G_MAXUINT32, 0, readable_flags)); + + g_object_class_install_property (gobject_class, PROP_DESCRIPTION, + g_param_spec_string ("description", "Description", + "Human readable device description", nullptr, readable_flags)); +} + +static void +gst_d3d12_device_init (GstD3D12Device * self) +{ + self->priv = new GstD3D12DevicePrivate (); +} + +static void +gst_d3d12_device_finalize (GObject * object) +{ + GstD3D12Device *self = GST_D3D12_DEVICE (object); + + GST_DEBUG_OBJECT (self, "Finalize"); + + delete self->priv; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_d3d12_device_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstD3D12Device *self = GST_D3D12_DEVICE (object); + GstD3D12DevicePrivate *priv = self->priv; + + switch (prop_id) { + case PROP_ADAPTER_INDEX: + g_value_set_uint (value, priv->adapter_index); + break; + case PROP_ADAPTER_LUID: + g_value_set_int64 (value, priv->adapter_luid); + break; + case PROP_DEVICE_ID: + g_value_set_uint (value, priv->device_id); + break; + case PROP_VENDOR_ID: + g_value_set_uint (value, priv->vendor_id); + break; + case PROP_DESCRIPTION: + g_value_set_string (value, priv->description.c_str ()); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +check_format_support (GstD3D12Device * self, DXGI_FORMAT format, + guint flags, D3D12_FEATURE_DATA_FORMAT_SUPPORT * support) +{ + ID3D12Device *device = self->priv->device.Get (); + HRESULT hr; + + support->Format = format; + hr = device->CheckFeatureSupport (D3D12_FEATURE_FORMAT_SUPPORT, support, + sizeof (D3D12_FEATURE_DATA_FORMAT_SUPPORT)); + if (FAILED (hr)) { + GST_INFO_OBJECT (self, + "Failed to check feature support for DXGI format %d", format); + return FALSE; + } + + if (((guint) support->Support1 & flags) != flags) { + GST_INFO_OBJECT (self, + "DXGI format %d supports1 flag 0x%x, required 0x%x", format, + support->Support1, flags); + + return FALSE; + } + + return TRUE; +} + +static void +gst_d3d12_device_setup_format_table (GstD3D12Device * self) +{ + GstD3D12DevicePrivate *priv = self->priv; + + for (guint i = 0; i < G_N_ELEMENTS (_gst_d3d12_default_format_map); i++) { + const GstD3D12Format *iter = &_gst_d3d12_default_format_map[i]; + GstD3D12Format format; + D3D12_FEATURE_DATA_FORMAT_SUPPORT support[GST_VIDEO_MAX_PLANES]; + + memset (support, 0, sizeof (support)); + + switch (iter->format) { + /* RGB/GRAY */ + case GST_VIDEO_FORMAT_BGRA: + case GST_VIDEO_FORMAT_BGRx: + case GST_VIDEO_FORMAT_RGBA: + case GST_VIDEO_FORMAT_RGBx: + case GST_VIDEO_FORMAT_RGB10A2_LE: + case GST_VIDEO_FORMAT_RGBA64_LE: + case GST_VIDEO_FORMAT_GRAY8: + case GST_VIDEO_FORMAT_GRAY16_LE: + if (!check_format_support (self, iter->dxgi_format, + iter->format_support1[0], &support[0])) { + continue; + } + break; + /* YUV DXGI native formats */ + case GST_VIDEO_FORMAT_VUYA: + case GST_VIDEO_FORMAT_Y410: + case GST_VIDEO_FORMAT_NV12: + case GST_VIDEO_FORMAT_P010_10LE: + case GST_VIDEO_FORMAT_P012_LE: + case GST_VIDEO_FORMAT_P016_LE: + case GST_VIDEO_FORMAT_YUY2: + if (!check_format_support (self, iter->dxgi_format, + iter->format_support1[0], &support[0])) { + continue; + } + break; + /* non-DXGI native formats */ + case GST_VIDEO_FORMAT_NV21: + case GST_VIDEO_FORMAT_I420: + case GST_VIDEO_FORMAT_YV12: + case GST_VIDEO_FORMAT_I420_10LE: + case GST_VIDEO_FORMAT_I420_12LE: + case GST_VIDEO_FORMAT_Y42B: + case GST_VIDEO_FORMAT_I422_10LE: + case GST_VIDEO_FORMAT_I422_12LE: + case GST_VIDEO_FORMAT_Y444: + case GST_VIDEO_FORMAT_Y444_10LE: + case GST_VIDEO_FORMAT_Y444_12LE: + case GST_VIDEO_FORMAT_Y444_16LE: + case GST_VIDEO_FORMAT_AYUV: + case GST_VIDEO_FORMAT_AYUV64: + /* RGB planar formats */ + case GST_VIDEO_FORMAT_RGBP: + case GST_VIDEO_FORMAT_BGRP: + case GST_VIDEO_FORMAT_GBR: + case GST_VIDEO_FORMAT_GBR_10LE: + case GST_VIDEO_FORMAT_GBR_12LE: + case GST_VIDEO_FORMAT_GBRA: + case GST_VIDEO_FORMAT_GBRA_10LE: + case GST_VIDEO_FORMAT_GBRA_12LE: + { + bool supported = true; + for (guint j = 0; j < GST_VIDEO_MAX_PLANES; j++) { + if (iter->resource_format[j] == DXGI_FORMAT_UNKNOWN) + break; + + if (!check_format_support (self, iter->resource_format[j], + iter->format_support1[0], &support[j])) { + supported = false; + break; + } + } + + if (!supported) { + GST_INFO_OBJECT (self, "%s is not supported", + gst_video_format_to_string (iter->format)); + continue; + } + + break; + } + default: + g_assert_not_reached (); + return; + } + + format = *iter; + + for (guint j = 0; j < GST_VIDEO_MAX_PLANES; j++) { + format.format_support1[j] = support[j].Support1; + format.format_support2[j] = support[j].Support2; + } + + priv->formats.push_back (format); + } +} + +static void +gst_d3d12_device_weak_ref_notify (gpointer data, GstD3D12Device * device) +{ + std::lock_guard < std::mutex > lk (device_list_lock_); + auto it = std::find (live_devices_.begin (), live_devices_.end (), device); + if (it != live_devices_.end ()) + live_devices_.erase (it); + else + GST_WARNING ("Could not find %p from list", device); +} + +typedef enum +{ + GST_D3D12_DEVICE_CONSTRUCT_FOR_INDEX, + GST_D3D12_DEVICE_CONSTRUCT_FOR_LUID, +} GstD3D12DeviceConstructType; + +typedef struct +{ + union + { + guint index; + gint64 luid; + } data; + GstD3D12DeviceConstructType type; +} GstD3D12DeviceConstructData; + +static HRESULT +gst_d3d12_device_find_adapter (const GstD3D12DeviceConstructData * data, + IDXGIFactory2 * factory, guint * index, IDXGIAdapter1 ** rst) +{ + HRESULT hr; + UINT factory_flags = 0; + +#ifdef HAVE_D3D12_SDKLAYERS_H + if (gst_d3d12_device_enable_debug ()) + factory_flags |= DXGI_CREATE_FACTORY_DEBUG; +#endif + + hr = CreateDXGIFactory2 (factory_flags, IID_PPV_ARGS (&factory)); + if (FAILED (hr)) { + GST_WARNING ("cannot create dxgi factory, hr: 0x%x", (guint) hr); + return hr; + } + + switch (data->type) { + case GST_D3D12_DEVICE_CONSTRUCT_FOR_INDEX:{ + ComPtr < IDXGIAdapter1 > adapter; + hr = factory->EnumAdapters1 (data->data.index, &adapter); + if (FAILED (hr)) + return hr; + + *index = data->data.index; + *rst = adapter.Detach (); + return S_OK; + } + case GST_D3D12_DEVICE_CONSTRUCT_FOR_LUID: + for (UINT i = 0;; i++) { + ComPtr < IDXGIAdapter1 > adapter; + DXGI_ADAPTER_DESC1 desc; + + hr = factory->EnumAdapters1 (i, &adapter); + if (FAILED (hr)) + return hr; + + hr = adapter->GetDesc1 (&desc); + if (FAILED (hr)) + return hr; + + if (gst_d3d12_luid_to_int64 (&desc.AdapterLuid) != data->data.luid) { + continue; + } + + *index = i; + *rst = adapter.Detach (); + + return S_OK; + } + default: + g_assert_not_reached (); + break; + } + + return E_FAIL; +} + +static GstD3D12Device * +gst_d3d12_device_new_internal (const GstD3D12DeviceConstructData * data) +{ + ComPtr < IDXGIFactory2 > factory; + ComPtr < IDXGIAdapter1 > adapter; + ComPtr < ID3D12Device > device; + HRESULT hr; + UINT factory_flags = 0; + guint index = 0; + +#ifdef HAVE_D3D12_SDKLAYERS_H + if (gst_d3d12_device_enable_debug ()) + factory_flags |= DXGI_CREATE_FACTORY_DEBUG; +#endif + + hr = CreateDXGIFactory2 (factory_flags, IID_PPV_ARGS (&factory)); + if (FAILED (hr)) { + GST_WARNING ("Could create dxgi factory, hr: 0x%x", (guint) hr); + return nullptr; + } + + hr = gst_d3d12_device_find_adapter (data, factory.Get (), &index, &adapter); + if (FAILED (hr)) { + GST_WARNING ("Could not find adapter, hr: 0x%x", (guint) hr); + return nullptr; + } + + DXGI_ADAPTER_DESC1 desc; + hr = adapter->GetDesc1 (&desc); + if (FAILED (hr)) { + GST_WARNING ("Could not get adapter desc, hr: 0x%x", (guint) hr); + return nullptr; + } + + hr = D3D12CreateDevice (adapter.Get (), D3D_FEATURE_LEVEL_12_0, + IID_PPV_ARGS (&device)); + if (FAILED (hr)) { + GST_WARNING ("Could not create device, hr: 0x%x", (guint) hr); + return nullptr; + } + + GstD3D12Device *self = (GstD3D12Device *) + g_object_new (GST_TYPE_D3D12_DEVICE, nullptr); + GstD3D12DevicePrivate *priv = self->priv; + + priv->factory = factory; + priv->adapter = adapter; + priv->device = device; + priv->adapter_luid = gst_d3d12_luid_to_int64 (&desc.AdapterLuid); + priv->vendor_id = desc.VendorId; + priv->device_id = desc.DeviceId; + + std::wstring_convert < std::codecvt_utf8 < wchar_t >, wchar_t >converter; + priv->description = converter.to_bytes (desc.Description); + + GST_INFO_OBJECT (self, + "adapter index %d: D3D12 device vendor-id: 0x%04x, device-id: 0x%04x, " + "Flags: 0x%x, adapter-luid: %" G_GINT64_FORMAT ", %s", + priv->adapter_index, desc.VendorId, desc.DeviceId, desc.Flags, + priv->adapter_luid, priv->description.c_str ()); + + gst_d3d12_device_setup_format_table (self); + +#ifdef HAVE_D3D12_SDKLAYERS_H + if (gst_d3d12_device_enable_debug ()) { + ComPtr < ID3D12InfoQueue > info_queue; + device.As (&info_queue); + priv->info_queue = info_queue; + } +#endif + + return self; +} + +GstD3D12Device * +gst_d3d12_device_new (guint adapter_index) +{ + GstD3D12Device *self = nullptr; + std::lock_guard < std::mutex > lk (device_list_lock_); + + /* *INDENT-OFF* */ + for (auto iter: live_devices_) { + if (iter->priv->adapter_index == adapter_index) { + self = (GstD3D12Device *) gst_object_ref (iter); + break; + } + } + /* *INDENT-ON* */ + + if (!self) { + GstD3D12DeviceConstructData data; + data.data.index = adapter_index; + data.type = GST_D3D12_DEVICE_CONSTRUCT_FOR_INDEX; + + self = gst_d3d12_device_new_internal (&data); + if (!self) { + GST_INFO ("Could not create device for index %d", adapter_index); + return nullptr; + } + + gst_object_ref_sink (self); + g_object_weak_ref (G_OBJECT (self), + (GWeakNotify) gst_d3d12_device_weak_ref_notify, nullptr); + live_devices_.push_back (self); + } + + return self; +} + +GstD3D12Device * +gst_d3d12_device_new_for_adapter_luid (gint64 adapter_luid) +{ + GstD3D12Device *self = nullptr; + std::lock_guard < std::mutex > lk (device_list_lock_); + + /* *INDENT-OFF* */ + for (auto iter: live_devices_) { + if (iter->priv->adapter_luid == adapter_luid) { + self = (GstD3D12Device *) gst_object_ref (iter); + break; + } + } + /* *INDENT-ON* */ + + if (!self) { + GstD3D12DeviceConstructData data; + data.data.luid = adapter_luid; + data.type = GST_D3D12_DEVICE_CONSTRUCT_FOR_LUID; + + self = gst_d3d12_device_new_internal (&data); + if (!self) { + GST_INFO ("Could not create device for LUID %" G_GINT64_FORMAT, + adapter_luid); + return nullptr; + } + + gst_object_ref_sink (self); + g_object_weak_ref (G_OBJECT (self), + (GWeakNotify) gst_d3d12_device_weak_ref_notify, nullptr); + live_devices_.push_back (self); + } + + return self; +} + +ID3D12Device * +gst_d3d12_device_get_device_handle (GstD3D12Device * device) +{ + g_return_val_if_fail (GST_IS_D3D12_DEVICE (device), nullptr); + + return device->priv->device.Get (); +} + +IDXGIAdapter1 * +gst_d3d12_device_get_adapter_handle (GstD3D12Device * device) +{ + g_return_val_if_fail (GST_IS_D3D12_DEVICE (device), nullptr); + + return device->priv->adapter.Get (); +} + +IDXGIFactory2 * +gst_d3d12_device_get_factory_handle (GstD3D12Device * device) +{ + g_return_val_if_fail (GST_IS_D3D12_DEVICE (device), nullptr); + + return device->priv->factory.Get (); +} + +gboolean +gst_d3d12_device_get_device_format (GstD3D12Device * device, + GstVideoFormat format, GstD3D12Format * device_format) +{ + g_return_val_if_fail (GST_IS_D3D12_DEVICE (device), FALSE); + g_return_val_if_fail (device_format != nullptr, FALSE); + + /* *INDENT-OFF* */ + for (const auto &iter : device->priv->formats) { + if (iter.format == format) { + *device_format = iter; + return TRUE; + } + } + /* *INDENT-ON* */ + + return FALSE; +} + +static inline GstDebugLevel +d3d12_message_severity_to_gst (D3D12_MESSAGE_SEVERITY level) +{ + switch (level) { + case D3D12_MESSAGE_SEVERITY_CORRUPTION: + case D3D12_MESSAGE_SEVERITY_ERROR: + return GST_LEVEL_ERROR; + case D3D12_MESSAGE_SEVERITY_WARNING: + return GST_LEVEL_WARNING; + case D3D12_MESSAGE_SEVERITY_INFO: + return GST_LEVEL_INFO; + case D3D12_MESSAGE_SEVERITY_MESSAGE: + return GST_LEVEL_DEBUG; + default: + break; + } + + return GST_LEVEL_LOG; +} + +#ifdef HAVE_D3D12_SDKLAYERS_H +void +gst_d3d12_device_d3d12_debug (GstD3D12Device * device, const gchar * file, + const gchar * function, gint line) +{ + g_return_if_fail (GST_IS_D3D12_DEVICE (device)); + + if (!device->priv->info_queue) + return; + + GstD3D12DevicePrivate *priv = device->priv; + + std::lock_guard < std::recursive_mutex > lk (priv->extern_lock); + ID3D12InfoQueue *info_queue = priv->info_queue.Get (); + UINT64 num_msg = info_queue->GetNumStoredMessages (); + for (guint64 i = 0; i < num_msg; i++) { + HRESULT hr; + SIZE_T msg_len; + D3D12_MESSAGE *msg; + GstDebugLevel msg_level; + GstDebugLevel selected_level; + + hr = info_queue->GetMessage (i, nullptr, &msg_len); + if (FAILED (hr) || msg_len == 0) + continue; + + msg = (D3D12_MESSAGE *) g_malloc0 (msg_len); + hr = info_queue->GetMessage (i, msg, &msg_len); + if (FAILED (hr) || msg_len == 0) { + g_free (msg); + continue; + } + + msg_level = d3d12_message_severity_to_gst (msg->Severity); + if (msg->Category == D3D12_MESSAGE_CATEGORY_STATE_CREATION && + msg_level > GST_LEVEL_ERROR) { + /* Do not warn for live object, since there would be live object + * when ReportLiveDeviceObjects was called */ + selected_level = GST_LEVEL_INFO; + } else { + selected_level = msg_level; + } + + gst_debug_log (gst_d3d12_sdk_debug, selected_level, file, function, line, + G_OBJECT (device), "D3D12InfoQueue: %s", msg->pDescription); + g_free (msg); + } + + info_queue->ClearStoredMessages (); +} +#else +void +gst_d3d12_device_d3d12_debug (GstD3D12Device * device, const gchar * file, + const gchar * function, gint line) +{ +} +#endif diff --git a/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12device.h b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12device.h new file mode 100644 index 0000000000..d416af224f --- /dev/null +++ b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12device.h @@ -0,0 +1,77 @@ +/* GStreamer + * Copyright (C) 2023 Seungha Yang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include +#include +#include "gstd3d12_fwd.h" + +G_BEGIN_DECLS + +#define GST_TYPE_D3D12_DEVICE (gst_d3d12_device_get_type()) +#define GST_D3D12_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_D3D12_DEVICE,GstD3D12Device)) +#define GST_D3D12_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_D3D12_DEVICE,GstD3D12DeviceClass)) +#define GST_D3D12_DEVICE_GET_CLASS(obj) (GST_D3D12_DEVICE_CLASS(G_OBJECT_GET_CLASS(obj))) +#define GST_IS_D3D12_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_D3D12_DEVICE)) +#define GST_IS_D3D12_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_D3D12_DEVICE)) +#define GST_D3D12_DEVICE_CAST(obj) ((GstD3D12Device*)(obj)) + +#define GST_D3D12_DEVICE_HANDLE_CONTEXT_TYPE "gst.d3d12.device.handle" + +struct _GstD3D12Device +{ + GstObject parent; + + /*< private >*/ + GstD3D12DevicePrivate *priv; + gpointer _gst_reserved[GST_PADDING]; +}; + +struct _GstD3D12DeviceClass +{ + GstObjectClass parent_class; + + /*< private >*/ + gpointer _gst_reserved[GST_PADDING]; +}; + +GType gst_d3d12_device_get_type (void); + +GstD3D12Device * gst_d3d12_device_new (guint adapter_index); + +GstD3D12Device * gst_d3d12_device_new_for_adapter_luid (gint64 adapter_luid); + +ID3D12Device * gst_d3d12_device_get_device_handle (GstD3D12Device * device); + +IDXGIAdapter1 * gst_d3d12_device_get_adapter_handle (GstD3D12Device * device); + +IDXGIFactory2 * gst_d3d12_device_get_factory_handle (GstD3D12Device * device); + +gboolean gst_d3d12_device_get_device_format (GstD3D12Device * device, + GstVideoFormat format, + GstD3D12Format * device_format); + +void gst_d3d12_device_d3d12_debug (GstD3D12Device * device, + const gchar * file, + const gchar * function, + gint line); + +G_END_DECLS + diff --git a/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12fence.cpp b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12fence.cpp new file mode 100644 index 0000000000..418da3f62b --- /dev/null +++ b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12fence.cpp @@ -0,0 +1,173 @@ +/* GStreamer + * Copyright (C) 2023 Seungha Yang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstd3d12fence.h" +#include "gstd3d12device.h" +#include "gstd3d12utils.h" +#include +#include + +GST_DEBUG_CATEGORY_EXTERN (gst_d3d12_fence_debug); +#define GST_CAT_DEFAULT gst_d3d12_fence_debug + +GST_DEFINE_MINI_OBJECT_TYPE (GstD3D12Fence, gst_d3d12_fence); + +/* *INDENT-OFF* */ +using namespace Microsoft::WRL; + +struct _GstD3D12FencePrivate +{ + _GstD3D12FencePrivate() + { + event_handle = CreateEventEx (nullptr, nullptr, 0, EVENT_ALL_ACCESS); + } + + ~_GstD3D12FencePrivate() + { + CloseHandle (event_handle); + } + + ComPtr fence; + HANDLE event_handle; + std::mutex lock; + guint64 value = 0; + gboolean can_wait = FALSE; +}; +/* *INDENT-ON* */ + +static void +gst_d3d12_fence_free (GstD3D12Fence * self) +{ + if (!self) + return; + + GST_TRACE ("Freeing fence %p", self); + + gst_clear_object (&self->device); + delete self->priv; + + g_free (self); +} + +GstD3D12Fence * +gst_d3d12_fence_new (GstD3D12Device * device) +{ + GstD3D12Fence *self; + GstD3D12FencePrivate *priv; + ID3D12Device *device_handle; + HRESULT hr; + ComPtr < ID3D12Fence > fence; + + g_return_val_if_fail (GST_IS_D3D12_DEVICE (device), nullptr); + + device_handle = gst_d3d12_device_get_device_handle (device); + hr = device_handle->CreateFence (0, + D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS (&fence)); + if (!gst_d3d12_result (hr, device)) { + GST_ERROR_OBJECT (device, "Failed to create fence, hr: 0x%x", hr); + return nullptr; + } + + priv = new GstD3D12FencePrivate (); + priv->fence = fence; + + self = g_new0 (GstD3D12Fence, 1); + GST_TRACE_OBJECT (device, "Creating fence %p", self); + + self->device = (GstD3D12Device *) gst_object_ref (device); + self->priv = priv; + + gst_mini_object_init (GST_MINI_OBJECT_CAST (self), 0, GST_TYPE_D3D12_FENCE, + nullptr, nullptr, (GstMiniObjectFreeFunction) gst_d3d12_fence_free); + + return self; +} + +gboolean +gst_d3d12_fence_set_event_on_completion_value (GstD3D12Fence * fence, + guint64 value) +{ + GstD3D12FencePrivate *priv; + HRESULT hr; + + g_return_val_if_fail (fence != nullptr, FALSE); + + priv = fence->priv; + + std::lock_guard < std::mutex > lk (priv->lock); + hr = priv->fence->SetEventOnCompletion (value, priv->event_handle); + if (!gst_d3d12_result (hr, fence->device)) { + GST_ERROR_OBJECT (fence->device, "Failed to set completion event"); + return FALSE; + } + + priv->value = value; + priv->can_wait = TRUE; + + return TRUE; +} + +ID3D12Fence * +gst_d3d12_fence_get_handle (GstD3D12Fence * fence) +{ + g_return_val_if_fail (fence != nullptr, nullptr); + + return fence->priv->fence.Get (); +} + +void +gst_d3d12_fence_wait_for (GstD3D12Fence * fence, guint timeout_ms) +{ + g_return_if_fail (fence != nullptr); + + GstD3D12FencePrivate *priv = fence->priv; + + std::lock_guard < std::mutex > lk (priv->lock); + if (!priv->can_wait) + return; + + GST_TRACE ("Waiting for fence to be signalled with value %" G_GUINT64_FORMAT, + priv->value); + WaitForSingleObjectEx (priv->event_handle, timeout_ms, FALSE); + GST_TRACE ("Signalled with value %" G_GUINT64_FORMAT, priv->value); + + priv->can_wait = FALSE; +} + +void +gst_d3d12_fence_wait (GstD3D12Fence * fence) +{ + gst_d3d12_fence_wait_for (fence, INFINITE); +} + +GstD3D12Fence * +gst_d3d12_fence_ref (GstD3D12Fence * fence) +{ + return (GstD3D12Fence *) gst_mini_object_ref (GST_MINI_OBJECT_CAST (fence)); +} + +void +gst_d3d12_fence_unref (GstD3D12Fence * fence) +{ + gst_mini_object_unref (GST_MINI_OBJECT_CAST (fence)); +} diff --git a/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12fence.h b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12fence.h new file mode 100644 index 0000000000..cdf2daee49 --- /dev/null +++ b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12fence.h @@ -0,0 +1,60 @@ +/* GStreamer + * Copyright (C) 2023 Seungha Yang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include +#include "gstd3d12_fwd.h" + +G_BEGIN_DECLS + +#define GST_TYPE_D3D12_FENCE (gst_d3d12_fence_get_type()) +#define GST_D3D12_FENCE_CAST(f) ((GstD3D12Fence *)f) + +struct _GstD3D12Fence +{ + GstMiniObject parent; + + GstD3D12Device *device; + + /*< private >*/ + GstD3D12FencePrivate *priv; + gpointer _gst_reserved[GST_PADDING]; +}; + +GType gst_d3d12_fence_get_type (void); + +GstD3D12Fence * gst_d3d12_fence_new (GstD3D12Device * device); + +ID3D12Fence * gst_d3d12_fence_get_handle (GstD3D12Fence * fence); + +gboolean gst_d3d12_fence_set_event_on_completion_value (GstD3D12Fence * fence, + guint64 value); + +void gst_d3d12_fence_wait_for (GstD3D12Fence * fence, + guint timeout_ms); + +void gst_d3d12_fence_wait (GstD3D12Fence * fence); + +GstD3D12Fence * gst_d3d12_fence_ref (GstD3D12Fence * fence); + +void gst_d3d12_fence_unref (GstD3D12Fence * fence); + +G_END_DECLS + diff --git a/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12format.cpp b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12format.cpp new file mode 100644 index 0000000000..477b1e0cc4 --- /dev/null +++ b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12format.cpp @@ -0,0 +1,127 @@ +/* GStreamer + * Copyright (C) 2023 Seungha Yang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstd3d12format.h" +#include "gstd3d12utils.h" +#include "gstd3d12device.h" +#include + +GST_DEBUG_CATEGORY_EXTERN (gst_d3d12_format_debug); +#define GST_CAT_DEFAULT gst_d3d12_format_debug + +guint +gst_d3d12_get_format_plane_count (GstD3D12Device * device, DXGI_FORMAT format) +{ + ID3D12Device *device_handle; + HRESULT hr; + D3D12_FEATURE_DATA_FORMAT_INFO format_info = { format, 0 }; + + g_return_val_if_fail (GST_IS_D3D12_DEVICE (device), 0); + + device_handle = gst_d3d12_device_get_device_handle (device); + hr = device_handle->CheckFeatureSupport (D3D12_FEATURE_FORMAT_INFO, + &format_info, sizeof (D3D12_FEATURE_DATA_FORMAT_INFO)); + if (!gst_d3d12_result (hr, device)) + return 0; + + return format_info.PlaneCount; +} + +GstVideoFormat +gst_d3d12_dxgi_format_to_gst (DXGI_FORMAT format) +{ + switch (format) { + case DXGI_FORMAT_B8G8R8A8_UNORM: + return GST_VIDEO_FORMAT_BGRA; + case DXGI_FORMAT_R8G8B8A8_UNORM: + return GST_VIDEO_FORMAT_RGBA; + case DXGI_FORMAT_R10G10B10A2_UNORM: + return GST_VIDEO_FORMAT_RGB10A2_LE; + case DXGI_FORMAT_AYUV: + return GST_VIDEO_FORMAT_VUYA; + case DXGI_FORMAT_YUY2: + return GST_VIDEO_FORMAT_YUY2; + case DXGI_FORMAT_Y210: + return GST_VIDEO_FORMAT_Y210; + case DXGI_FORMAT_Y410: + return GST_VIDEO_FORMAT_Y410; + case DXGI_FORMAT_NV12: + return GST_VIDEO_FORMAT_NV12; + case DXGI_FORMAT_P010: + return GST_VIDEO_FORMAT_P010_10LE; + case DXGI_FORMAT_P016: + return GST_VIDEO_FORMAT_P016_LE; + default: + break; + } + + return GST_VIDEO_FORMAT_UNKNOWN; +} + +gboolean +gst_d3d12_dxgi_format_to_resource_formats (DXGI_FORMAT format, + DXGI_FORMAT resource_format[GST_VIDEO_MAX_PLANES]) +{ + g_return_val_if_fail (resource_format != nullptr, FALSE); + + for (guint i = 0; i < GST_VIDEO_MAX_PLANES; i++) + resource_format[i] = DXGI_FORMAT_UNKNOWN; + + switch (format) { + case DXGI_FORMAT_B8G8R8A8_UNORM: + case DXGI_FORMAT_R8G8B8A8_UNORM: + case DXGI_FORMAT_R10G10B10A2_UNORM: + case DXGI_FORMAT_R8_UNORM: + case DXGI_FORMAT_R8G8_UNORM: + case DXGI_FORMAT_R16_UNORM: + case DXGI_FORMAT_R16G16_UNORM: + case DXGI_FORMAT_G8R8_G8B8_UNORM: + case DXGI_FORMAT_R8G8_B8G8_UNORM: + case DXGI_FORMAT_R16G16B16A16_UNORM: + resource_format[0] = format; + break; + case DXGI_FORMAT_AYUV: + case DXGI_FORMAT_YUY2: + resource_format[0] = DXGI_FORMAT_R8G8B8A8_UNORM; + break; + case DXGI_FORMAT_NV12: + resource_format[0] = DXGI_FORMAT_R8_UNORM; + resource_format[1] = DXGI_FORMAT_R8G8_UNORM; + break; + case DXGI_FORMAT_P010: + case DXGI_FORMAT_P016: + resource_format[0] = DXGI_FORMAT_R16_UNORM; + resource_format[1] = DXGI_FORMAT_R16G16_UNORM; + break; + case DXGI_FORMAT_Y210: + resource_format[0] = DXGI_FORMAT_R16G16B16A16_UNORM; + break; + case DXGI_FORMAT_Y410: + resource_format[0] = DXGI_FORMAT_R10G10B10A2_UNORM; + break; + default: + return FALSE; + } + + return TRUE; +} diff --git a/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12format.h b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12format.h new file mode 100644 index 0000000000..68994d9499 --- /dev/null +++ b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12format.h @@ -0,0 +1,72 @@ +/* GStreamer + * Copyright (C) 2023 Seungha Yang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include +#include +#include "gstd3d12_fwd.h" + +G_BEGIN_DECLS + +#define GST_D3D12_COMMON_FORMATS \ + "NV12, P010_10LE, P016_LE " + +#define GST_D3D12_SINK_FORMATS \ + "{ " GST_D3D12_COMMON_FORMATS " }" + +#define GST_D3D12_SRC_FORMATS \ + "{ " GST_D3D12_COMMON_FORMATS " }" + +#define GST_D3D12_FORMATS_ALL \ + "{ " GST_D3D12_COMMON_FORMATS " }" + +struct _GstD3D12Format +{ + GstVideoFormat format; + + /* direct mapping to dxgi format if applicable */ + DXGI_FORMAT dxgi_format; + + /* formats for texture processing */ + DXGI_FORMAT resource_format[GST_VIDEO_MAX_PLANES]; + + /* extra format used for unordered access view (unused) */ + DXGI_FORMAT uav_format[GST_VIDEO_MAX_PLANES]; + + /* D3D12_FORMAT_SUPPORT1 flags */ + guint format_support1[GST_VIDEO_MAX_PLANES]; + + /* D3D12_FORMAT_SUPPORT2 flags (unused) */ + guint format_support2[GST_VIDEO_MAX_PLANES]; + + /*< private >*/ + guint padding[GST_PADDING_LARGE]; +}; + +guint gst_d3d12_get_format_plane_count (GstD3D12Device * device, + DXGI_FORMAT format); + +GstVideoFormat gst_d3d12_dxgi_format_to_gst (DXGI_FORMAT format); + +gboolean gst_d3d12_dxgi_format_to_resource_formats (DXGI_FORMAT format, + DXGI_FORMAT resource_format[GST_VIDEO_MAX_PLANES]); + +G_END_DECLS + diff --git a/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12h264dec.cpp b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12h264dec.cpp new file mode 100644 index 0000000000..097bf484e5 --- /dev/null +++ b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12h264dec.cpp @@ -0,0 +1,327 @@ +/* GStreamer + * Copyright (C) 2023 Seungha Yang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02120-1301, USA. + */ + +/** + * SECTION:element-d3d12h264dec + * @title: d3d12h264dec + * + * A Direct3D12 based H.264 video decoder + * + * ## Example launch line + * ``` + * gst-launch-1.0 filesrc location=/path/to/h264/file ! parsebin ! d3d12h264dec ! videoconvert ! autovideosink + * ``` + * + * Since: 1.24 + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "gstd3d12h264dec.h" +#include "gstd3d12device.h" +#include "gstd3d12utils.h" +#include + +GST_DEBUG_CATEGORY_STATIC (gst_d3d12_h264_dec_debug); +#define GST_CAT_DEFAULT gst_d3d12_h264_dec_debug + +GST_D3D12_DECODER_DEFINE_TYPE_FULL (GstD3D12H264Dec, gst_d3d12_h264_dec, + GST, D3D12_H264_DEC, GstDxvaH264Decoder); + +static void +gst_d3d12_h264_dec_class_init (GstD3D12H264DecClass * klass, gpointer data) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + GstVideoDecoderClass *decoder_class = GST_VIDEO_DECODER_CLASS (klass); + GstDxvaH264DecoderClass *dxva_class = GST_DXVA_H264_DECODER_CLASS (klass); + GstD3D12DecoderClassData *cdata = (GstD3D12DecoderClassData *) data; + + gobject_class->get_property = gst_d3d12_h264_dec_get_property; + + element_class->set_context = + GST_DEBUG_FUNCPTR (gst_d3d12_h264_dec_set_context); + + parent_class = (GstElementClass *) g_type_class_peek_parent (klass); + gst_d3d12_decoder_class_data_fill_subclass_data (cdata, &klass->class_data); + + gst_d3d12_decoder_proxy_class_init (element_class, cdata, + "Seungha Yang "); + + decoder_class->open = GST_DEBUG_FUNCPTR (gst_d3d12_h264_dec_open); + decoder_class->close = GST_DEBUG_FUNCPTR (gst_d3d12_h264_dec_close); + decoder_class->negotiate = GST_DEBUG_FUNCPTR (gst_d3d12_h264_dec_negotiate); + decoder_class->decide_allocation = + GST_DEBUG_FUNCPTR (gst_d3d12_h264_dec_decide_allocation); + decoder_class->sink_query = GST_DEBUG_FUNCPTR (gst_d3d12_h264_dec_sink_query); + decoder_class->src_query = GST_DEBUG_FUNCPTR (gst_d3d12_h264_dec_src_query); + + dxva_class->configure = GST_DEBUG_FUNCPTR (gst_d3d12_h264_dec_configure); + dxva_class->new_picture = GST_DEBUG_FUNCPTR (gst_d3d12_h264_dec_new_picture); + dxva_class->duplicate_picture = + GST_DEBUG_FUNCPTR (gst_d3d12_h264_dec_duplicate_picture); + dxva_class->get_picture_id = + GST_DEBUG_FUNCPTR (gst_d3d12_h264_dec_get_picture_id); + dxva_class->start_picture = + GST_DEBUG_FUNCPTR (gst_d3d12_h264_dec_start_picture); + dxva_class->end_picture = GST_DEBUG_FUNCPTR (gst_d3d12_h264_dec_end_picture); + dxva_class->output_picture = + GST_DEBUG_FUNCPTR (gst_d3d12_h264_dec_output_picture); +} + +static void +gst_d3d12_h264_dec_init (GstD3D12H264Dec * self) +{ +} + +static void +gst_d3d12_h264_dec_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstD3D12H264DecClass *klass = GST_D3D12_H264_DEC_GET_CLASS (object); + GstD3D12DecoderSubClassData *cdata = &klass->class_data; + + gst_d3d12_decoder_proxy_get_property (object, prop_id, value, pspec, cdata); +} + +static void +gst_d3d12_h264_dec_set_context (GstElement * element, GstContext * context) +{ + GstD3D12H264Dec *self = GST_D3D12_H264_DEC (element); + GstD3D12H264DecClass *klass = GST_D3D12_H264_DEC_GET_CLASS (self); + GstD3D12DecoderSubClassData *cdata = &klass->class_data; + + gst_d3d12_handle_set_context_for_adapter_luid (element, + context, cdata->adapter_luid, &self->device); + + GST_ELEMENT_CLASS (parent_class)->set_context (element, context); +} + +static gboolean +gst_d3d12_h264_dec_open (GstVideoDecoder * decoder) +{ + GstD3D12H264Dec *self = GST_D3D12_H264_DEC (decoder); + GstD3D12H264DecClass *klass = GST_D3D12_H264_DEC_GET_CLASS (self); + GstD3D12DecoderSubClassData *cdata = &klass->class_data; + + return gst_d3d12_decoder_proxy_open (decoder, cdata, &self->device, + &self->decoder); +} + +static gboolean +gst_d3d12_h264_dec_close (GstVideoDecoder * decoder) +{ + GstD3D12H264Dec *self = GST_D3D12_H264_DEC (decoder); + + gst_clear_object (&self->decoder); + gst_clear_object (&self->device); + + return TRUE; +} + +static gboolean +gst_d3d12_h264_dec_negotiate (GstVideoDecoder * decoder) +{ + GstD3D12H264Dec *self = GST_D3D12_H264_DEC (decoder); + + if (!gst_d3d12_decoder_negotiate (self->decoder, decoder)) + return FALSE; + + return GST_VIDEO_DECODER_CLASS (parent_class)->negotiate (decoder); +} + +static gboolean +gst_d3d12_h264_dec_decide_allocation (GstVideoDecoder * decoder, + GstQuery * query) +{ + GstD3D12H264Dec *self = GST_D3D12_H264_DEC (decoder); + + if (!gst_d3d12_decoder_decide_allocation (self->decoder, decoder, query)) { + return FALSE; + } + + return GST_VIDEO_DECODER_CLASS (parent_class)->decide_allocation + (decoder, query); +} + +static gboolean +gst_d3d12_h264_dec_sink_query (GstVideoDecoder * decoder, GstQuery * query) +{ + GstD3D12H264Dec *self = GST_D3D12_H264_DEC (decoder); + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_CONTEXT: + if (gst_d3d12_handle_context_query (GST_ELEMENT (decoder), + query, self->device)) { + return TRUE; + } + break; + default: + break; + } + + return GST_VIDEO_DECODER_CLASS (parent_class)->sink_query (decoder, query); +} + +static gboolean +gst_d3d12_h264_dec_src_query (GstVideoDecoder * decoder, GstQuery * query) +{ + GstD3D12H264Dec *self = GST_D3D12_H264_DEC (decoder); + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_CONTEXT: + if (gst_d3d12_handle_context_query (GST_ELEMENT (decoder), + query, self->device)) { + return TRUE; + } + break; + default: + break; + } + + return GST_VIDEO_DECODER_CLASS (parent_class)->src_query (decoder, query); +} + +static GstFlowReturn +gst_d3d12_h264_dec_configure (GstDxvaH264Decoder * decoder, + GstVideoCodecState * input_state, const GstVideoInfo * info, + gint crop_x, gint crop_y, gint coded_width, gint coded_height, + gint max_dpb_size) +{ + GstD3D12H264Dec *self = GST_D3D12_H264_DEC (decoder); + + return gst_d3d12_decoder_configure (self->decoder, input_state, info, + crop_x, crop_y, coded_width, coded_height, max_dpb_size); +} + +static GstFlowReturn +gst_d3d12_h264_dec_new_picture (GstDxvaH264Decoder * decoder, + GstCodecPicture * picture) +{ + GstD3D12H264Dec *self = GST_D3D12_H264_DEC (decoder); + + return gst_d3d12_decoder_new_picture (self->decoder, + GST_VIDEO_DECODER (decoder), picture); +} + +static GstFlowReturn +gst_d3d12_h264_dec_duplicate_picture (GstDxvaH264Decoder * decoder, + GstCodecPicture * src, GstCodecPicture * dst) +{ + GstD3D12H264Dec *self = GST_D3D12_H264_DEC (decoder); + + return gst_d3d12_decoder_duplicate_picture (self->decoder, src, dst); +} + +static guint8 +gst_d3d12_h264_dec_get_picture_id (GstDxvaH264Decoder * decoder, + GstCodecPicture * picture) +{ + GstD3D12H264Dec *self = GST_D3D12_H264_DEC (decoder); + + return gst_d3d12_decoder_get_picture_id (self->decoder, picture); +} + +static GstFlowReturn +gst_d3d12_h264_dec_start_picture (GstDxvaH264Decoder * decoder, + GstCodecPicture * picture, guint8 * picture_id) +{ + GstD3D12H264Dec *self = GST_D3D12_H264_DEC (decoder); + + return gst_d3d12_decoder_start_picture (self->decoder, picture, picture_id); +} + +static GstFlowReturn +gst_d3d12_h264_dec_end_picture (GstDxvaH264Decoder * decoder, + GstCodecPicture * picture, GPtrArray * ref_pics, + const GstDxvaDecodingArgs * args) +{ + GstD3D12H264Dec *self = GST_D3D12_H264_DEC (decoder); + + return gst_d3d12_decoder_end_picture (self->decoder, picture, ref_pics, args); +} + +static GstFlowReturn +gst_d3d12_h264_dec_output_picture (GstDxvaH264Decoder * decoder, + GstVideoCodecFrame * frame, GstCodecPicture * picture, + GstVideoBufferFlags buffer_flags, gint display_width, gint display_height) +{ + GstD3D12H264Dec *self = GST_D3D12_H264_DEC (decoder); + + return gst_d3d12_decoder_output_picture (self->decoder, + GST_VIDEO_DECODER (decoder), frame, picture, + buffer_flags, display_width, display_height); +} + +void +gst_d3d12_h264_dec_register (GstPlugin * plugin, GstD3D12Device * device, + ID3D12VideoDevice * video_device, guint rank) +{ + GType type; + gchar *type_name; + gchar *feature_name; + guint index = 0; + GTypeInfo type_info = { + sizeof (GstD3D12H264DecClass), + nullptr, + nullptr, + (GClassInitFunc) gst_d3d12_h264_dec_class_init, + nullptr, + nullptr, + sizeof (GstD3D12H264Dec), + 0, + (GInstanceInitFunc) gst_d3d12_h264_dec_init, + }; + + GST_DEBUG_CATEGORY_INIT (gst_d3d12_h264_dec_debug, "d3d12h264dec", 0, + "d3d12h264dec"); + + type_info.class_data = + gst_d3d12_decoder_check_feature_support (device, video_device, + GST_DXVA_CODEC_H264); + if (!type_info.class_data) + return; + + type_name = g_strdup ("GstD3D12H264Dec"); + feature_name = g_strdup ("d3d12h264dec"); + + while (g_type_from_name (type_name)) { + index++; + g_free (type_name); + g_free (feature_name); + type_name = g_strdup_printf ("GstD3D12H264Device%dDec", index); + feature_name = g_strdup_printf ("d3d12h264device%ddec", index); + } + + type = g_type_register_static (GST_TYPE_DXVA_H264_DECODER, + type_name, &type_info, (GTypeFlags) 0); + + /* make lower rank than default device */ + if (rank > 0 && index != 0) + rank--; + + if (!gst_element_register (plugin, feature_name, rank, type)) + GST_WARNING ("Failed to register plugin '%s'", type_name); + + g_free (type_name); + g_free (feature_name); +} diff --git a/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12h264dec.h b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12h264dec.h new file mode 100644 index 0000000000..afd8994c13 --- /dev/null +++ b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12h264dec.h @@ -0,0 +1,32 @@ +/* GStreamer + * Copyright (C) 2023 Seungha Yang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include "gstd3d12decoder.h" + +G_BEGIN_DECLS + +void gst_d3d12_h264_dec_register (GstPlugin * plugin, + GstD3D12Device * device, + ID3D12VideoDevice * video_device, + guint rank); + +G_END_DECLS + diff --git a/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12memory.cpp b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12memory.cpp new file mode 100644 index 0000000000..ef17fad966 --- /dev/null +++ b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12memory.cpp @@ -0,0 +1,1068 @@ +/* GStreamer + * Copyright (C) 2023 Seungha Yang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstd3d12memory.h" +#include "gstd3d12device.h" +#include "gstd3d12utils.h" +#include "gstd3d12format.h" +#include +#include +#include +#include +#include +#include + +GST_DEBUG_CATEGORY_EXTERN (gst_d3d12_allocator_debug); +#define GST_CAT_DEFAULT gst_d3d12_allocator_debug + +static GstD3D12Allocator *_d3d12_memory_allocator = nullptr; + +/* *INDENT-OFF* */ +using namespace Microsoft::WRL; + +struct _GstD3D12MemoryPrivate +{ + ComPtr resource; + + ComPtr srv_heap; + ComPtr rtv_heap; + + guint srv_increment_size = 0; + guint rtv_increment_size = 0; + + guint num_srv = 0; + guint num_rtv = 0; + + D3D12_RESOURCE_DESC desc; + D3D12_RESOURCE_STATES state; + + /* Queryied via ID3D12Device::GetCopyableFootprints */ + D3D12_PLACED_SUBRESOURCE_FOOTPRINT layout[GST_VIDEO_MAX_PLANES]; + guint64 size; + guint num_subresources; + guint subresource_index[GST_VIDEO_MAX_PLANES]; + + std::recursive_mutex lock; +}; +/* *INDENT-ON* */ + +GST_DEFINE_MINI_OBJECT_TYPE (GstD3D12Memory, gst_d3d12_memory); + +static gpointer +gst_d3d12_memory_map_full (GstMemory * mem, GstMapInfo * info, gsize maxsize) +{ + GstD3D12Memory *dmem = GST_D3D12_MEMORY_CAST (mem); + GstD3D12MemoryPrivate *priv = dmem->priv; + GstMapFlags flags = info->flags; + + if ((flags & GST_MAP_D3D12) == 0) { + GST_ERROR ("CPU access to d3d12 resource is not allowed yet"); + return nullptr; + } + + /* Holds lock while we are being mapped. We will not allow concurrent + * access to the texture unlike D3D11 for now + * (unless we use D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS flag) */ + priv->lock.lock (); + + return priv->resource.Get (); +} + +static void +gst_d3d12_memory_unmap_full (GstMemory * mem, GstMapInfo * info) +{ + GstD3D12Memory *dmem = GST_D3D12_MEMORY_CAST (mem); + GstD3D12MemoryPrivate *priv = dmem->priv; + + if ((info->flags & GST_MAP_D3D12) == 0) { + GST_ERROR ("CPU access to d3d12 resource is not allowed yet"); + return; + } + + priv->lock.unlock (); +} + +static GstMemory * +gst_d3d12_memory_share (GstMemory * mem, gssize offset, gssize size) +{ + /* TODO: impl. */ + return nullptr; +} + +gboolean +gst_is_d3d12_memory (GstMemory * mem) +{ + return mem != nullptr && mem->allocator != nullptr && + (GST_IS_D3D12_ALLOCATOR (mem->allocator) || + GST_IS_D3D12_POOL_ALLOCATOR (mem->allocator)); +} + +void +gst_d3d12_memory_init_once (void) +{ + GST_D3D12_CALL_ONCE_BEGIN { + _d3d12_memory_allocator = + (GstD3D12Allocator *) g_object_new (GST_TYPE_D3D12_ALLOCATOR, nullptr); + gst_object_ref_sink (_d3d12_memory_allocator); + gst_object_ref (_d3d12_memory_allocator); + + gst_allocator_register (GST_D3D12_MEMORY_NAME, + GST_ALLOCATOR_CAST (_d3d12_memory_allocator)); + } GST_D3D12_CALL_ONCE_END; +} + +ID3D12Resource * +gst_d3d12_memory_get_resource_handle (GstD3D12Memory * mem) +{ + g_return_val_if_fail (gst_is_d3d12_memory (GST_MEMORY_CAST (mem)), FALSE); + + return mem->priv->resource.Get (); +} + +gboolean +gst_d3d12_memory_get_state (GstD3D12Memory * mem, D3D12_RESOURCE_STATES * state) +{ + g_return_val_if_fail (gst_is_d3d12_memory (GST_MEMORY_CAST (mem)), FALSE); + + if (!mem->priv->lock.try_lock ()) { + GST_WARNING ("Resource %p is owned by other thread, try map first", mem); + return FALSE; + } + + if (state) + *state = mem->priv->state; + + mem->priv->lock.unlock (); + + return TRUE; +} + +gboolean +gst_d3d12_memory_set_state (GstD3D12Memory * mem, D3D12_RESOURCE_STATES state) +{ + g_return_val_if_fail (gst_is_d3d12_memory (GST_MEMORY_CAST (mem)), FALSE); + + if (!mem->priv->lock.try_lock ()) { + GST_WARNING ("Resource %p is owned by other thread, try map first", mem); + return FALSE; + } + + mem->priv->state = state; + mem->priv->lock.unlock (); + + /* XXX: This might not be sufficient. We should know the type of command list + * (queue) where the resource was used in for the later use. + * Probably we can infer it by using state though. + */ + + return TRUE; +} + +gboolean +gst_d3d12_memory_get_subresource_index (GstD3D12Memory * mem, guint plane, + guint * index) +{ + g_return_val_if_fail (gst_is_d3d12_memory (GST_MEMORY_CAST (mem)), FALSE); + g_return_val_if_fail (index != nullptr, FALSE); + + if (plane >= mem->priv->num_subresources) { + GST_WARNING_OBJECT (GST_MEMORY_CAST (mem)->allocator, "Invalid plane %d", + plane); + return FALSE; + } + + *index = mem->priv->subresource_index[plane]; + + return TRUE; +} + +gboolean +gst_d3d12_memory_get_plane_count (GstD3D12Memory * mem, guint * count) +{ + g_return_val_if_fail (gst_is_d3d12_memory (GST_MEMORY_CAST (mem)), FALSE); + g_return_val_if_fail (count != nullptr, FALSE); + + *count = mem->priv->num_subresources; + + return TRUE; +} + +gboolean +gst_d3d12_memory_get_plane_size (GstD3D12Memory * mem, guint plane, + gint * width, gint * height, gint * stride, gsize * offset) +{ + GstD3D12MemoryPrivate *priv; + + g_return_val_if_fail (gst_is_d3d12_memory (GST_MEMORY_CAST (mem)), FALSE); + + priv = mem->priv; + + if (plane >= priv->num_subresources) { + GST_WARNING_OBJECT (GST_MEMORY_CAST (mem)->allocator, "Invalid plane %d", + plane); + return FALSE; + } + + if (width) + *width = (gint) priv->layout[plane].Footprint.Width; + if (height) + *height = (gint) priv->layout[plane].Footprint.Height; + if (stride) + *stride = (gint) priv->layout[plane].Footprint.RowPitch; + if (offset) + *offset = (gsize) priv->layout[plane].Offset; + + return TRUE; +} + +static gboolean +create_shader_resource_views (GstD3D12Memory * mem) +{ + GstD3D12MemoryPrivate *priv = mem->priv; + HRESULT hr; + guint num_formats = 0; + ID3D12Device *device; + DXGI_FORMAT formats[GST_VIDEO_MAX_PLANES]; + + if (!gst_d3d12_dxgi_format_to_resource_formats (priv->desc.Format, formats)) { + GST_ERROR_OBJECT (GST_MEMORY_CAST (mem)->allocator, + "Failed to get resource formats for DXGI format %d", priv->desc.Format); + return FALSE; + } + + for (guint i = 0; i < G_N_ELEMENTS (formats); i++) { + if (formats[i] == DXGI_FORMAT_UNKNOWN) + break; + + num_formats++; + } + + g_assert (priv->srv_heap == nullptr); + device = gst_d3d12_device_get_device_handle (mem->device); + + priv->srv_increment_size = + device->GetDescriptorHandleIncrementSize + (D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + + D3D12_DESCRIPTOR_HEAP_DESC heap_desc = { }; + heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; + heap_desc.NumDescriptors = num_formats; + heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; + heap_desc.NodeMask = 0; + + ComPtr < ID3D12DescriptorHeap > srv_heap; + hr = device->CreateDescriptorHeap (&heap_desc, IID_PPV_ARGS (&srv_heap)); + if (!gst_d3d12_result (hr, mem->device)) { + GST_ERROR_OBJECT (mem->device, "Failed to create SRV descriptor heap"); + return FALSE; + } + + auto srv_handle = srv_heap->GetCPUDescriptorHandleForHeapStart (); + for (guint i = 0; i < num_formats; i++) { + D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc = { }; + srv_desc.Format = formats[i]; + srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; + srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + srv_desc.Texture2D.MostDetailedMip = 0; + srv_desc.Texture2D.MipLevels = 1; + srv_desc.Texture2D.PlaneSlice = i; + srv_desc.Texture2D.ResourceMinLODClamp = 0xf; + + device->CreateShaderResourceView (priv->resource.Get (), &srv_desc, + srv_handle); + srv_handle.ptr += priv->srv_increment_size; + } + + priv->srv_heap = srv_heap; + priv->num_srv = num_formats; + + return TRUE; +} + +static gboolean +gst_d3d12_memory_ensure_shader_resource_view (GstD3D12Memory * mem) +{ + GstD3D12MemoryPrivate *priv = mem->priv; + + if ((priv->desc.Flags & D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE) != 0) { + GST_LOG_OBJECT (GST_MEMORY_CAST (mem)->allocator, + "Shader resource was denied"); + return FALSE; + } + + std::lock_guard < std::recursive_mutex > lk (priv->lock); + if (priv->num_srv) + return TRUE; + + return create_shader_resource_views (mem); +} + +guint +gst_d3d12_memory_get_shader_resource_view_size (GstD3D12Memory * mem) +{ + g_return_val_if_fail (gst_is_d3d12_memory (GST_MEMORY_CAST (mem)), 0); + + if (!gst_d3d12_memory_ensure_shader_resource_view (mem)) + return 0; + + return mem->priv->num_srv; +} + +gboolean +gst_d3d12_memory_get_shader_resource_view (GstD3D12Memory * mem, guint index, + D3D12_CPU_DESCRIPTOR_HANDLE * srv) +{ + GstD3D12MemoryPrivate *priv; + + g_return_val_if_fail (gst_is_d3d12_memory (GST_MEMORY_CAST (mem)), FALSE); + g_return_val_if_fail (srv != nullptr, FALSE); + + if (!gst_d3d12_memory_ensure_shader_resource_view (mem)) + return FALSE; + + priv = mem->priv; + + if (index >= priv->num_srv) { + GST_ERROR ("Invalid SRV index %d", index); + return FALSE; + } + + g_assert (priv->srv_heap != nullptr); + + auto srv_handle = priv->srv_heap->GetCPUDescriptorHandleForHeapStart (); + srv_handle.ptr += ((gsize) index * priv->srv_increment_size); + + *srv = srv_handle; + return TRUE; +} + +static gboolean +create_render_target_views (GstD3D12Memory * mem) +{ + GstD3D12MemoryPrivate *priv = mem->priv; + HRESULT hr; + guint num_formats = 0; + ID3D12Device *device; + DXGI_FORMAT formats[GST_VIDEO_MAX_PLANES]; + + if (!gst_d3d12_dxgi_format_to_resource_formats (priv->desc.Format, formats)) { + GST_ERROR_OBJECT (GST_MEMORY_CAST (mem)->allocator, + "Failed to get resource formats for DXGI format %d", priv->desc.Format); + return FALSE; + } + + for (guint i = 0; i < G_N_ELEMENTS (formats); i++) { + if (formats[i] == DXGI_FORMAT_UNKNOWN) + break; + + num_formats++; + } + + g_assert (priv->rtv_heap == nullptr); + device = gst_d3d12_device_get_device_handle (mem->device); + + priv->rtv_increment_size = + device->GetDescriptorHandleIncrementSize (D3D12_DESCRIPTOR_HEAP_TYPE_RTV); + + D3D12_DESCRIPTOR_HEAP_DESC heap_desc = { }; + heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; + heap_desc.NumDescriptors = num_formats; + heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; + heap_desc.NodeMask = 0; + + ComPtr < ID3D12DescriptorHeap > rtv_heap; + hr = device->CreateDescriptorHeap (&heap_desc, IID_PPV_ARGS (&rtv_heap)); + if (!gst_d3d12_result (hr, mem->device)) { + GST_ERROR_OBJECT (mem->device, "Failed to create SRV descriptor heap"); + return FALSE; + } + + auto rtv_handle = rtv_heap->GetCPUDescriptorHandleForHeapStart (); + for (guint i = 0; i < num_formats; i++) { + D3D12_RENDER_TARGET_VIEW_DESC rtv_desc = { }; + rtv_desc.Format = formats[i]; + rtv_desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; + rtv_desc.Texture2D.MipSlice = 0; + rtv_desc.Texture2D.PlaneSlice = i; + + device->CreateRenderTargetView (priv->resource.Get (), &rtv_desc, + rtv_handle); + rtv_handle.ptr += priv->rtv_increment_size; + } + + priv->rtv_heap = rtv_heap; + priv->num_rtv = num_formats; + + return TRUE; +} + +static gboolean +gst_d3d12_memory_ensure_render_target_view (GstD3D12Memory * mem) +{ + GstD3D12MemoryPrivate *priv = mem->priv; + + if ((priv->desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET) == 0) { + GST_LOG_OBJECT (GST_MEMORY_CAST (mem)->allocator, + "Render target is not allowed"); + return FALSE; + } + + std::lock_guard < std::recursive_mutex > lk (priv->lock); + if (priv->num_rtv) + return TRUE; + + return create_render_target_views (mem); +} + +guint +gst_d3d12_memory_get_render_target_view_size (GstD3D12Memory * mem) +{ + g_return_val_if_fail (gst_is_d3d12_memory (GST_MEMORY_CAST (mem)), 0); + + if (!gst_d3d12_memory_ensure_render_target_view (mem)) + return 0; + + return mem->priv->num_rtv; +} + +gboolean +gst_d3d12_memory_get_render_target_view (GstD3D12Memory * mem, guint index, + D3D12_CPU_DESCRIPTOR_HANDLE * rtv) +{ + GstD3D12MemoryPrivate *priv; + + g_return_val_if_fail (gst_is_d3d12_memory (GST_MEMORY_CAST (mem)), FALSE); + g_return_val_if_fail (rtv != nullptr, FALSE); + + if (!gst_d3d12_memory_ensure_render_target_view (mem)) + return FALSE; + + priv = mem->priv; + + if (index >= priv->num_rtv) { + GST_ERROR ("Invalid RTV index %d", index); + return FALSE; + } + + g_assert (priv->rtv_heap != nullptr); + + auto rtv_handle = priv->rtv_heap->GetCPUDescriptorHandleForHeapStart (); + rtv_handle.ptr += ((gsize) index * priv->rtv_increment_size); + + *rtv = rtv_handle; + + return TRUE; +} + +/* GstD3D12Allocator */ +#define gst_d3d12_allocator_parent_class alloc_parent_class +G_DEFINE_TYPE (GstD3D12Allocator, gst_d3d12_allocator, GST_TYPE_ALLOCATOR); + +static GstMemory *gst_d3d12_allocator_dummy_alloc (GstAllocator * allocator, + gsize size, GstAllocationParams * params); +static GstMemory *gst_d3d12_allocator_alloc_internal (GstD3D12Allocator * self, + GstD3D12Device * device, const D3D12_HEAP_PROPERTIES * heap_props, + D3D12_HEAP_FLAGS heap_flags, const D3D12_RESOURCE_DESC * desc, + D3D12_RESOURCE_STATES initial_state, + const D3D12_CLEAR_VALUE * optimized_clear_value); +static void gst_d3d12_allocator_free (GstAllocator * allocator, + GstMemory * mem); + +static void +gst_d3d12_allocator_class_init (GstD3D12AllocatorClass * klass) +{ + GstAllocatorClass *allocator_class = GST_ALLOCATOR_CLASS (klass); + + allocator_class->alloc = gst_d3d12_allocator_dummy_alloc; + allocator_class->free = gst_d3d12_allocator_free; +} + +static void +gst_d3d12_allocator_init (GstD3D12Allocator * allocator) +{ + GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator); + + alloc->mem_type = GST_D3D12_MEMORY_NAME; + alloc->mem_map_full = gst_d3d12_memory_map_full; + alloc->mem_unmap_full = gst_d3d12_memory_unmap_full; + alloc->mem_share = gst_d3d12_memory_share; + + GST_OBJECT_FLAG_SET (alloc, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC); +} + +static GstMemory * +gst_d3d12_allocator_dummy_alloc (GstAllocator * allocator, gsize size, + GstAllocationParams * params) +{ + g_return_val_if_reached (nullptr); +} + +static void +gst_d3d12_allocator_free (GstAllocator * allocator, GstMemory * mem) +{ + GstD3D12Memory *dmem = GST_D3D12_MEMORY_CAST (mem); + + GST_LOG_OBJECT (allocator, "Free memory %p", mem); + + /* XXX: probably we might want to check whether this resource is still + * being used by GPU or not, via fence or something */ + + gst_clear_object (&dmem->device); + delete dmem->priv; + + g_free (dmem); +} + +static GstMemory * +gst_d3d12_allocator_alloc_wrapped (GstD3D12Allocator * self, + GstD3D12Device * device, const D3D12_RESOURCE_DESC * desc, + D3D12_RESOURCE_STATES initial_state, ID3D12Resource * resource, + guint array_slice) +{ + GstD3D12Memory *mem; + GstD3D12MemoryPrivate *priv; + ID3D12Device *device_handle = gst_d3d12_device_get_device_handle (device); + guint8 num_subresources = + gst_d3d12_get_format_plane_count (device, desc->Format); + + if (num_subresources == 0) { + GST_ERROR_OBJECT (self, "Couldn't get format info"); + return nullptr; + } + + mem = g_new0 (GstD3D12Memory, 1); + mem->priv = priv = new GstD3D12MemoryPrivate (); + + priv->desc = *desc; + priv->num_subresources = num_subresources; + priv->resource = resource; + priv->state = initial_state; + + mem->device = (GstD3D12Device *) gst_object_ref (device); + + mem->priv->size = 0; + for (guint i = 0; i < mem->priv->num_subresources; i++) { + UINT64 size; + + /* One notable difference between D3D12/D3D11 is that, D3D12 introduced + * *PLANE* slice concept. That means, Each plane of YUV format + * (e.g, DXGI_FORMAT_NV12) can be accessible in D3D12 but that wasn't + * allowed in D3D11. As a result, the way for calculating subresource index + * is changed. This is an example of subresource indexing + * for array size == 3 with NV12 format. + * + * Array 0 Array 1 Array 2 + * +-------------+-------------+-------------+ + * | Y plane : 0 | Y plane : 1 | Y plane : 2 | + * +-------------+-------------+-------------+ + * | UV plane: 3 | UV plane: 4 | UV plane: 5 | + * +-------------+-------------+-------------+ + */ + mem->priv->subresource_index[i] = gst_d3d12_calculate_subresource (0, + array_slice, i, 1, desc->DepthOrArraySize); + + device_handle->GetCopyableFootprints (&priv->desc, + priv->subresource_index[i], 1, 0, &priv->layout[i], nullptr, nullptr, + &size); + + /* Update offset manually */ + if (i > 1) { + priv->layout[i].Offset = priv->layout[i - 1].Offset + + priv->layout[i - 1].Footprint.RowPitch * + priv->layout[i - 1].Footprint.Height; + } + + priv->size += size; + } + + gst_memory_init (GST_MEMORY_CAST (mem), + (GstMemoryFlags) 0, GST_ALLOCATOR_CAST (self), nullptr, + mem->priv->size, 0, 0, mem->priv->size); + + + GST_LOG_OBJECT (self, "Allocated new memory %p with size %" G_GUINT64_FORMAT, + mem, priv->size); + + return GST_MEMORY_CAST (mem); +} + +static GstMemory * +gst_d3d12_allocator_alloc_internal (GstD3D12Allocator * self, + GstD3D12Device * device, const D3D12_HEAP_PROPERTIES * heap_props, + D3D12_HEAP_FLAGS heap_flags, const D3D12_RESOURCE_DESC * desc, + D3D12_RESOURCE_STATES initial_state, + const D3D12_CLEAR_VALUE * optimized_clear_value) +{ + ID3D12Device *device_handle; + HRESULT hr; + ComPtr < ID3D12Resource > resource; + + device_handle = gst_d3d12_device_get_device_handle (device); + hr = device_handle->CreateCommittedResource (heap_props, heap_flags, + desc, initial_state, optimized_clear_value, IID_PPV_ARGS (&resource)); + if (!gst_d3d12_result (hr, device)) { + GST_ERROR_OBJECT (self, "Couldn't create texture"); + return nullptr; + } + + return gst_d3d12_allocator_alloc_wrapped (self, device, desc, + initial_state, resource.Get (), 0); +} + +GstMemory * +gst_d3d12_allocator_alloc (GstD3D12Allocator * allocator, + GstD3D12Device * device, const D3D12_HEAP_PROPERTIES * heap_props, + D3D12_HEAP_FLAGS heap_flags, const D3D12_RESOURCE_DESC * desc, + D3D12_RESOURCE_STATES initial_state, + const D3D12_CLEAR_VALUE * optimized_clear_value) +{ + g_return_val_if_fail (GST_IS_D3D12_ALLOCATOR (allocator), nullptr); + g_return_val_if_fail (GST_IS_D3D12_DEVICE (device), nullptr); + g_return_val_if_fail (heap_props != nullptr, nullptr); + g_return_val_if_fail (desc != nullptr, nullptr); + + if (desc->DepthOrArraySize > 1) { + GST_ERROR_OBJECT (allocator, "Array is not supported, use pool allocator"); + return nullptr; + } + + return gst_d3d12_allocator_alloc_internal (allocator, device, heap_props, + heap_flags, desc, initial_state, optimized_clear_value); +} + +gboolean +gst_d3d12_allocator_set_active (GstD3D12Allocator * allocator, gboolean active) +{ + GstD3D12AllocatorClass *klass; + + g_return_val_if_fail (GST_IS_D3D12_ALLOCATOR (allocator), FALSE); + + klass = GST_D3D12_ALLOCATOR_GET_CLASS (allocator); + if (klass->set_actvie) + return klass->set_actvie (allocator, active); + + return TRUE; +} + +/* GstD3D12PoolAllocator */ + +/* *INDENT-OFF* */ +struct _GstD3D12PoolAllocatorPrivate +{ + _GstD3D12PoolAllocatorPrivate() + { + outstanding = 0; + } + + /* For the case where DepthOrArraySize > 1 */ + ComPtr resource; + + D3D12_HEAP_PROPERTIES heap_props; + D3D12_HEAP_FLAGS heap_flags; + D3D12_RESOURCE_DESC desc; + D3D12_RESOURCE_STATES initial_state; + D3D12_CLEAR_VALUE clear_value; + gboolean clear_value_is_valid = FALSE; + + std::queue queue; + + std::mutex lock; + std::condition_variable cond; + gboolean started = FALSE; + gboolean active = FALSE; + + std::atomic outstanding; + guint cur_mems = 0; + gboolean flushing = FALSE; +}; +/* *INDENT-ON* */ + +static void gst_d3d12_pool_allocator_finalize (GObject * object); + +static gboolean +gst_d3d12_pool_allocator_set_active (GstD3D12Allocator * allocator, + gboolean active); + +static gboolean gst_d3d12_pool_allocator_start (GstD3D12PoolAllocator * self); +static gboolean gst_d3d12_pool_allocator_stop (GstD3D12PoolAllocator * self); +static gboolean gst_d3d12_memory_release (GstMiniObject * mini_object); + +#define gst_d3d12_pool_allocator_parent_class pool_alloc_parent_class +G_DEFINE_TYPE (GstD3D12PoolAllocator, gst_d3d12_pool_allocator, + GST_TYPE_D3D12_ALLOCATOR); + +static void +gst_d3d12_pool_allocator_class_init (GstD3D12PoolAllocatorClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstD3D12AllocatorClass *d3d12alloc_class = GST_D3D12_ALLOCATOR_CLASS (klass); + + gobject_class->finalize = gst_d3d12_pool_allocator_finalize; + + d3d12alloc_class->set_actvie = gst_d3d12_pool_allocator_set_active; +} + +static void +gst_d3d12_pool_allocator_init (GstD3D12PoolAllocator * self) +{ + self->priv = new GstD3D12PoolAllocatorPrivate (); +} + +static void +gst_d3d12_pool_allocator_finalize (GObject * object) +{ + GstD3D12PoolAllocator *self = GST_D3D12_POOL_ALLOCATOR (object); + + GST_DEBUG_OBJECT (self, "Finalize"); + + gst_d3d12_pool_allocator_stop (self); + delete self->priv; + + g_clear_object (&self->device); + + G_OBJECT_CLASS (pool_alloc_parent_class)->finalize (object); +} + +/* must be called with the lock */ +static gboolean +gst_d3d12_pool_allocator_start (GstD3D12PoolAllocator * self) +{ + GstD3D12PoolAllocatorPrivate *priv = self->priv; + ID3D12Device *device_handle; + HRESULT hr; + + if (priv->started) + return TRUE; + + /* Nothing to do */ + if (priv->desc.DepthOrArraySize == 1) { + priv->started = TRUE; + return TRUE; + } + + device_handle = gst_d3d12_device_get_device_handle (self->device); + if (!priv->resource) { + ComPtr < ID3D12Resource > resource; + hr = device_handle->CreateCommittedResource (&priv->heap_props, + priv->heap_flags, &priv->desc, priv->initial_state, + priv->clear_value_is_valid ? &priv->clear_value : nullptr, + IID_PPV_ARGS (&resource)); + if (!gst_d3d12_result (hr, self->device)) { + GST_ERROR_OBJECT (self, "Failed to allocate texture"); + return FALSE; + } + + priv->resource = resource; + } + + for (guint i = 0; i < priv->desc.DepthOrArraySize; i++) { + GstMemory *mem; + + mem = gst_d3d12_allocator_alloc_wrapped (_d3d12_memory_allocator, + self->device, &priv->desc, + priv->initial_state, priv->resource.Get (), i); + + priv->cur_mems++; + priv->queue.push (mem); + } + + priv->started = TRUE; + + return TRUE; +} + +static gboolean +gst_d3d12_pool_allocator_set_active (GstD3D12Allocator * allocator, + gboolean active) +{ + GstD3D12PoolAllocator *self = GST_D3D12_POOL_ALLOCATOR (allocator); + GstD3D12PoolAllocatorPrivate *priv = self->priv; + + GST_LOG_OBJECT (self, "active %d", active); + + std::unique_lock < std::mutex > lk (priv->lock); + /* just return if we are already in the right state */ + if (priv->active == active) { + GST_LOG_OBJECT (self, "allocator was in the right state"); + return TRUE; + } + + if (active) { + if (!gst_d3d12_pool_allocator_start (self)) { + GST_ERROR_OBJECT (self, "start failed"); + return FALSE; + } + + priv->active = TRUE; + priv->flushing = FALSE; + } else { + priv->flushing = TRUE; + priv->active = FALSE; + + priv->cond.notify_all (); + + /* when all memory objects are in the pool, free them. Else they will be + * freed when they are released */ + GST_LOG_OBJECT (self, "outstanding memories %d, (in queue %u)", + priv->outstanding.load (), (guint) priv->queue.size ()); + if (priv->outstanding == 0) { + if (!gst_d3d12_pool_allocator_stop (self)) { + GST_ERROR_OBJECT (self, "stop failed"); + return FALSE; + } + } + } + + return TRUE; +} + +static void +gst_d3d12_pool_allocator_free_memory (GstD3D12PoolAllocator * self, + GstMemory * mem) +{ + GstD3D12PoolAllocatorPrivate *priv = self->priv; + + priv->cur_mems--; + GST_LOG_OBJECT (self, "freeing memory %p (%u left)", mem, priv->cur_mems); + + GST_MINI_OBJECT_CAST (mem)->dispose = nullptr; + gst_memory_unref (mem); +} + +/* must be called with the lock */ +static void +gst_d3d12_pool_allocator_clear_queue (GstD3D12PoolAllocator * self) +{ + GstD3D12PoolAllocatorPrivate *priv = self->priv; + + GST_LOG_OBJECT (self, "Clearing queue"); + + while (!priv->queue.empty ()) { + GstMemory *mem = priv->queue.front (); + priv->queue.pop (); + gst_d3d12_pool_allocator_free_memory (self, mem); + } + + GST_LOG_OBJECT (self, "Clear done"); +} + +/* must be called with the lock */ +static gboolean +gst_d3d12_pool_allocator_stop (GstD3D12PoolAllocator * self) +{ + GstD3D12PoolAllocatorPrivate *priv = self->priv; + + GST_DEBUG_OBJECT (self, "Stop"); + + if (priv->started) { + gst_d3d12_pool_allocator_clear_queue (self); + + priv->started = FALSE; + } else { + GST_DEBUG_OBJECT (self, "Wasn't started"); + } + + return TRUE; +} + +static void +gst_d3d12_pool_allocator_release_memory (GstD3D12PoolAllocator * self, + GstMemory * mem) +{ + GstD3D12PoolAllocatorPrivate *priv = self->priv; + + GST_LOG_OBJECT (self, "Released memory %p", mem); + + GST_MINI_OBJECT_CAST (mem)->dispose = nullptr; + mem->allocator = (GstAllocator *) gst_object_ref (_d3d12_memory_allocator); + + /* keep it around in our queue */ + priv->queue.push (mem); + priv->outstanding--; + priv->cond.notify_all (); + priv->lock.unlock (); + + gst_object_unref (self); +} + +static gboolean +gst_d3d12_memory_release (GstMiniObject * mini_object) +{ + GstMemory *mem = GST_MEMORY_CAST (mini_object); + GstD3D12PoolAllocator *alloc; + GstD3D12PoolAllocatorPrivate *priv; + + g_assert (mem->allocator != nullptr); + + if (!GST_IS_D3D12_POOL_ALLOCATOR (mem->allocator)) { + GST_LOG_OBJECT (mem->allocator, "Not our memory, free"); + return TRUE; + } + + alloc = GST_D3D12_POOL_ALLOCATOR (mem->allocator); + priv = alloc->priv; + + priv->lock.lock (); + /* if flushing, free this memory */ + if (alloc->priv->flushing) { + priv->lock.unlock (); + GST_LOG_OBJECT (alloc, "allocator is flushing, free %p", mem); + return TRUE; + } + + /* return the memory to the allocator */ + gst_memory_ref (mem); + gst_d3d12_pool_allocator_release_memory (alloc, mem); + + return FALSE; +} + +/* must be called with the lock */ +static GstFlowReturn +gst_d3d12_pool_allocator_alloc (GstD3D12PoolAllocator * self, GstMemory ** mem) +{ + GstD3D12PoolAllocatorPrivate *priv = self->priv; + GstMemory *new_mem; + + /* we allcates texture array during start */ + if (priv->desc.DepthOrArraySize > 1) + return GST_FLOW_EOS; + + /* increment the allocation counter */ + new_mem = + gst_d3d12_allocator_alloc_internal (_d3d12_memory_allocator, + self->device, &priv->heap_props, + priv->heap_flags, &priv->desc, priv->initial_state, + priv->clear_value_is_valid ? &priv->clear_value : nullptr); + + if (!new_mem) { + GST_ERROR_OBJECT (self, "Failed to allocate new memory"); + return GST_FLOW_ERROR; + } + + priv->cur_mems++; + + *mem = new_mem; + + return GST_FLOW_OK; +} + +/* must be called with the lock */ +static GstFlowReturn +gst_d3d12_pool_allocator_acquire_memory_internal (GstD3D12PoolAllocator * self, + GstMemory ** memory, std::unique_lock < std::mutex > &lk) +{ + GstD3D12PoolAllocatorPrivate *priv = self->priv; + GstFlowReturn ret = GST_FLOW_ERROR; + + do { + if (priv->flushing) { + GST_DEBUG_OBJECT (self, "we are flushing"); + return GST_FLOW_FLUSHING; + } + + if (!priv->queue.empty ()) { + *memory = priv->queue.front (); + priv->queue.pop (); + GST_LOG_OBJECT (self, "acquired memory %p", *memory); + return GST_FLOW_OK; + } + + /* no memory, try to allocate some more */ + GST_LOG_OBJECT (self, "no memory, trying to allocate"); + ret = gst_d3d12_pool_allocator_alloc (self, memory); + if (ret == GST_FLOW_OK) + return ret; + + /* something went wrong, return error */ + if (ret != GST_FLOW_EOS) + break; + + GST_LOG_OBJECT (self, "waiting for free memory or flushing"); + priv->cond.wait (lk); + } while (TRUE); + + return ret; +} + +GstD3D12PoolAllocator * +gst_d3d12_pool_allocator_new (GstD3D12Device * device, + const D3D12_HEAP_PROPERTIES * heap_props, D3D12_HEAP_FLAGS heap_flags, + const D3D12_RESOURCE_DESC * desc, D3D12_RESOURCE_STATES initial_state, + const D3D12_CLEAR_VALUE * optimized_clear_value) +{ + GstD3D12PoolAllocator *self; + + g_return_val_if_fail (GST_IS_D3D12_DEVICE (device), nullptr); + g_return_val_if_fail (heap_props != nullptr, nullptr); + g_return_val_if_fail (desc != nullptr, nullptr); + + gst_d3d12_memory_init_once (); + + self = (GstD3D12PoolAllocator *) + g_object_new (GST_TYPE_D3D12_POOL_ALLOCATOR, nullptr); + gst_object_ref_sink (self); + + self->device = (GstD3D12Device *) gst_object_ref (device); + self->priv->heap_props = *heap_props; + self->priv->heap_flags = heap_flags; + self->priv->desc = *desc; + self->priv->initial_state = initial_state; + if (optimized_clear_value) { + self->priv->clear_value = *optimized_clear_value; + self->priv->clear_value_is_valid = TRUE; + } else { + self->priv->clear_value_is_valid = FALSE; + } + + return self; +} + +GstFlowReturn +gst_d3d12_pool_allocator_acquire_memory (GstD3D12PoolAllocator * allocator, + GstMemory ** memory) +{ + GstFlowReturn ret; + GstD3D12PoolAllocatorPrivate *priv; + + g_return_val_if_fail (GST_IS_D3D12_POOL_ALLOCATOR (allocator), + GST_FLOW_ERROR); + g_return_val_if_fail (memory != nullptr, GST_FLOW_ERROR); + + priv = allocator->priv; + + std::unique_lock < std::mutex > lk (priv->lock); + ret = gst_d3d12_pool_allocator_acquire_memory_internal (allocator, + memory, lk); + + if (ret == GST_FLOW_OK) { + GstMemory *mem = *memory; + /* Replace default allocator with ours */ + gst_object_unref (mem->allocator); + mem->allocator = (GstAllocator *) gst_object_ref (allocator); + GST_MINI_OBJECT_CAST (mem)->dispose = gst_d3d12_memory_release; + priv->outstanding++; + } + + return ret; +} diff --git a/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12memory.h b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12memory.h new file mode 100644 index 0000000000..1459dc7411 --- /dev/null +++ b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12memory.h @@ -0,0 +1,190 @@ +/* GStreamer + * Copyright (C) 2023 Seungha Yang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include +#include +#include "gstd3d12_fwd.h" + +G_BEGIN_DECLS + +#define GST_TYPE_D3D12_ALLOCATION_PARAMS (gst_d3d12_allocation_params_get_type()) + +#define GST_TYPE_D3D12_MEMORY (gst_d3d12_memory_get_type()) +#define GST_D3D12_MEMORY_CAST(obj) ((GstD3D12Memory *)obj) + +#define GST_TYPE_D3D12_ALLOCATOR (gst_d3d12_allocator_get_type()) +#define GST_D3D12_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_D3D12_ALLOCATOR, GstD3D12Allocator)) +#define GST_D3D12_ALLOCATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_D3D12_ALLOCATOR, GstD3D12AllocatorClass)) +#define GST_IS_D3D12_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_D3D12_ALLOCATOR)) +#define GST_IS_D3D12_ALLOCATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_D3D12_ALLOCATOR)) +#define GST_D3D12_ALLOCATOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_D3D12_ALLOCATOR, GstD3D12AllocatorClass)) +#define GST_D3D12_ALLOCATOR_CAST(obj) ((GstD3D12Allocator *)obj) + +#define GST_TYPE_D3D12_POOL_ALLOCATOR (gst_d3d12_pool_allocator_get_type()) +#define GST_D3D12_POOL_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_D3D12_POOL_ALLOCATOR, GstD3D12PoolAllocator)) +#define GST_D3D12_POOL_ALLOCATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_D3D12_POOL_ALLOCATOR, GstD3D12PoolAllocatorClass)) +#define GST_IS_D3D12_POOL_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_D3D12_POOL_ALLOCATOR)) +#define GST_IS_D3D12_POOL_ALLOCATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_D3D12_POOL_ALLOCATOR)) +#define GST_D3D12_POOL_ALLOCATOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_D3D12_POOL_ALLOCATOR, GstD3D12PoolAllocatorClass)) +#define GST_D3D12_POOL_ALLOCATOR_CAST(obj) ((GstD3D12PoolAllocator *)obj) + +/** + * GST_D3D12_MEMORY_NAME: + * + * The name of the Direct3D12 memory + */ +#define GST_D3D12_MEMORY_NAME "D3D12Memory" + +/** + * GST_CAPS_FEATURE_MEMORY_D3D12_MEMORY: + * + * Name of the caps feature for indicating the use of #GstD3D12Memory + */ +#define GST_CAPS_FEATURE_MEMORY_D3D12_MEMORY "memory:D3D12Memory" + +/** + * GST_MAP_D3D12: + * + * Flag indicating that we should map the D3D12 resource instead of to system memory. + */ +#define GST_MAP_D3D12 (GST_MAP_FLAG_LAST << 1) + +struct _GstD3D12Memory +{ + GstMemory mem; + + /*< public >*/ + GstD3D12Device *device; + + /*< private >*/ + GstD3D12MemoryPrivate *priv; + gpointer _gst_reserved[GST_PADDING]; +}; + +GType gst_d3d12_memory_get_type (void); + +void gst_d3d12_memory_init_once (void); + +gboolean gst_is_d3d12_memory (GstMemory * mem); + +ID3D12Resource * gst_d3d12_memory_get_resource_handle (GstD3D12Memory * mem); + +gboolean gst_d3d12_memory_get_state (GstD3D12Memory * mem, + D3D12_RESOURCE_STATES * state); + +gboolean gst_d3d12_memory_set_state (GstD3D12Memory * mem, + D3D12_RESOURCE_STATES state); + +gboolean gst_d3d12_memory_get_subresource_index (GstD3D12Memory * mem, + guint plane, + guint * index); + +gboolean gst_d3d12_memory_get_plane_count (GstD3D12Memory * mem, + guint * count); + +gboolean gst_d3d12_memory_get_plane_size (GstD3D12Memory * mem, + guint plane, + gint * width, + gint * height, + gint * stride, + gsize * offset); + +guint gst_d3d12_memory_get_shader_resource_view_size (GstD3D12Memory * mem); + +gboolean gst_d3d12_memory_get_shader_resource_view (GstD3D12Memory * mem, + guint index, + D3D12_CPU_DESCRIPTOR_HANDLE * srv); + +guint gst_d3d12_memory_get_render_target_view_size (GstD3D12Memory * mem); + +gboolean gst_d3d12_memory_get_render_target_view (GstD3D12Memory * mem, + guint index, + D3D12_CPU_DESCRIPTOR_HANDLE * rtv); + +struct _GstD3D12Allocator +{ + GstAllocator allocator; + + /*< private >*/ + GstD3D12AllocatorPrivate *priv; + + gpointer _gst_reserved[GST_PADDING]; +}; + +struct _GstD3D12AllocatorClass +{ + GstAllocatorClass allocator_class; + + gboolean (*set_actvie) (GstD3D12Allocator * allocator, + gboolean active); + + /*< private >*/ + gpointer _gst_reserved[GST_PADDING_LARGE]; +}; + +GType gst_d3d12_allocator_get_type (void); + +GstMemory * gst_d3d12_allocator_alloc (GstD3D12Allocator * allocator, + GstD3D12Device * device, + const D3D12_HEAP_PROPERTIES * heap_props, + D3D12_HEAP_FLAGS heap_flags, + const D3D12_RESOURCE_DESC * desc, + D3D12_RESOURCE_STATES initial_state, + const D3D12_CLEAR_VALUE * optimized_clear_value); + +gboolean gst_d3d12_allocator_set_active (GstD3D12Allocator * allocator, + gboolean active); + +struct _GstD3D12PoolAllocator +{ + GstD3D12Allocator allocator; + + /*< public >*/ + GstD3D12Device *device; + + /*< private >*/ + GstD3D12PoolAllocatorPrivate *priv; + + gpointer _gst_reserved[GST_PADDING]; +}; + +struct _GstD3D12PoolAllocatorClass +{ + GstD3D12AllocatorClass allocator_class; + + /*< private >*/ + gpointer _gst_reserved[GST_PADDING]; +}; + +GType gst_d3d12_pool_allocator_get_type (void); + +GstD3D12PoolAllocator * gst_d3d12_pool_allocator_new (GstD3D12Device * device, + const D3D12_HEAP_PROPERTIES * heap_props, + D3D12_HEAP_FLAGS heap_flags, + const D3D12_RESOURCE_DESC * desc, + D3D12_RESOURCE_STATES initial_state, + const D3D12_CLEAR_VALUE * optimized_clear_value); + +GstFlowReturn gst_d3d12_pool_allocator_acquire_memory (GstD3D12PoolAllocator * allocator, + GstMemory ** memory); + +G_END_DECLS + diff --git a/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12utils.cpp b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12utils.cpp new file mode 100644 index 0000000000..edb4243526 --- /dev/null +++ b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12utils.cpp @@ -0,0 +1,442 @@ +/* GStreamer + * Copyright (C) 2023 Seungha Yang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstd3d12utils.h" +#include "gstd3d12device.h" +#include + +/* *INDENT-OFF* */ +static std::recursive_mutex context_lock_; +/* *INDENT-ON* */ + +GST_DEBUG_CATEGORY_STATIC (GST_CAT_CONTEXT); +GST_DEBUG_CATEGORY_EXTERN (gst_d3d12_utils_debug); +#define GST_CAT_DEFAULT gst_d3d12_utils_debug + +static void +init_context_debug (void) +{ + GST_D3D12_CALL_ONCE_BEGIN { + GST_DEBUG_CATEGORY_GET (GST_CAT_CONTEXT, "GST_CONTEXT"); + } GST_D3D12_CALL_ONCE_END; +} + +gboolean +gst_d3d12_handle_set_context (GstElement * element, GstContext * context, + gint adapter_index, GstD3D12Device ** device) +{ + const gchar *context_type; + + g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); + g_return_val_if_fail (device != nullptr, FALSE); + + init_context_debug (); + + if (!context) + return FALSE; + + context_type = gst_context_get_context_type (context); + if (g_strcmp0 (context_type, GST_D3D12_DEVICE_HANDLE_CONTEXT_TYPE) == 0) { + const GstStructure *str; + GstD3D12Device *other_device = nullptr; + guint other_adapter = 0; + + /* If we had device already, will not replace it */ + if (*device) + return TRUE; + + str = gst_context_get_structure (context); + + if (gst_structure_get (str, "device", GST_TYPE_D3D12_DEVICE, + &other_device, "adapter-index", G_TYPE_UINT, &other_adapter, + nullptr)) { + if (adapter_index == -1 || (guint) adapter_index == other_adapter) { + GST_CAT_DEBUG_OBJECT (GST_CAT_CONTEXT, + element, "Found D3D12 device context"); + *device = other_device; + + return TRUE; + } + + gst_object_unref (other_device); + } + } + + return FALSE; +} + +gboolean +gst_d3d12_handle_set_context_for_adapter_luid (GstElement * element, + GstContext * context, gint64 adapter_luid, GstD3D12Device ** device) +{ + const gchar *context_type; + + g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); + g_return_val_if_fail (device != nullptr, FALSE); + + init_context_debug (); + + if (!context) + return FALSE; + + context_type = gst_context_get_context_type (context); + if (g_strcmp0 (context_type, GST_D3D12_DEVICE_HANDLE_CONTEXT_TYPE) == 0) { + const GstStructure *str; + GstD3D12Device *other_device = nullptr; + gint64 other_adapter = 0; + + /* If we had device already, will not replace it */ + if (*device) + return TRUE; + + str = gst_context_get_structure (context); + + if (gst_structure_get (str, "device", GST_TYPE_D3D12_DEVICE, + &other_device, "adapter-luid", G_TYPE_INT64, &other_adapter, + nullptr)) { + if (adapter_luid == other_adapter) { + GST_CAT_DEBUG_OBJECT (GST_CAT_CONTEXT, + element, "Found D3D12 device context"); + *device = other_device; + + return TRUE; + } + + gst_object_unref (other_device); + } + } + + return FALSE; +} + +static void +context_set_d3d12_device (GstContext * context, GstD3D12Device * device) +{ + GstStructure *s; + guint adapter_index = 0; + guint device_id = 0; + guint vendor_id = 0; + gchar *desc = nullptr; + gint64 adapter_luid = 0; + + g_return_if_fail (context != nullptr); + + g_object_get (G_OBJECT (device), "adapter-index", &adapter_index, + "device-id", &device_id, "vendor-id", &vendor_id, "description", &desc, + "adapter-luid", &adapter_luid, nullptr); + + GST_CAT_LOG (GST_CAT_CONTEXT, + "setting GstD3D12Device(%" GST_PTR_FORMAT + ") with adapter index %d on context(%" GST_PTR_FORMAT ")", + device, adapter_index, context); + + s = gst_context_writable_structure (context); + gst_structure_set (s, "device", GST_TYPE_D3D12_DEVICE, device, + "adapter-index", G_TYPE_UINT, adapter_index, + "adapter-luid", G_TYPE_INT64, adapter_luid, + "device-id", G_TYPE_UINT, device_id, + "vendor-id", G_TYPE_UINT, vendor_id, + "description", G_TYPE_STRING, GST_STR_NULL (desc), nullptr); + g_free (desc); +} + +gboolean +gst_d3d12_handle_context_query (GstElement * element, GstQuery * query, + GstD3D12Device * device) +{ + const gchar *context_type; + GstContext *context, *old_context; + + g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); + g_return_val_if_fail (GST_IS_QUERY (query), FALSE); + + init_context_debug (); + + GST_LOG_OBJECT (element, "handle context query %" GST_PTR_FORMAT, query); + + if (!device) + return FALSE; + + gst_query_parse_context_type (query, &context_type); + if (g_strcmp0 (context_type, GST_D3D12_DEVICE_HANDLE_CONTEXT_TYPE) != 0) + return FALSE; + + gst_query_parse_context (query, &old_context); + if (old_context) + context = gst_context_copy (old_context); + else + context = gst_context_new (GST_D3D12_DEVICE_HANDLE_CONTEXT_TYPE, TRUE); + + context_set_d3d12_device (context, device); + gst_query_set_context (query, context); + gst_context_unref (context); + + GST_DEBUG_OBJECT (element, "successfully set %" GST_PTR_FORMAT + " on %" GST_PTR_FORMAT, device, query); + + return TRUE; +} + +static gboolean +pad_query (const GValue * item, GValue * value, gpointer user_data) +{ + GstPad *pad = (GstPad *) g_value_get_object (item); + GstQuery *query = (GstQuery *) user_data; + gboolean res; + + res = gst_pad_peer_query (pad, query); + + if (res) { + g_value_set_boolean (value, TRUE); + return FALSE; + } + + GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, pad, "pad peer query failed"); + return TRUE; +} + +static gboolean +run_query (GstElement * element, GstQuery * query, GstPadDirection direction) +{ + GstIterator *it; + GstIteratorFoldFunction func = pad_query; + GValue res = G_VALUE_INIT; + + g_value_init (&res, G_TYPE_BOOLEAN); + g_value_set_boolean (&res, FALSE); + + /* Ask neighbor */ + if (direction == GST_PAD_SRC) + it = gst_element_iterate_src_pads (element); + else + it = gst_element_iterate_sink_pads (element); + + while (gst_iterator_fold (it, func, &res, query) == GST_ITERATOR_RESYNC) + gst_iterator_resync (it); + + gst_iterator_free (it); + + return g_value_get_boolean (&res); +} + +static void +run_d3d12_context_query (GstElement * element, GstD3D12Device ** device) +{ + GstQuery *query; + GstContext *ctxt = nullptr; + + query = gst_query_new_context (GST_D3D12_DEVICE_HANDLE_CONTEXT_TYPE); + if (run_query (element, query, GST_PAD_SRC)) { + gst_query_parse_context (query, &ctxt); + if (ctxt) { + GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element, + "found context (%" GST_PTR_FORMAT ") in downstream query", ctxt); + gst_element_set_context (element, ctxt); + } + } + + if (*device == nullptr && run_query (element, query, GST_PAD_SINK)) { + gst_query_parse_context (query, &ctxt); + if (ctxt) { + GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element, + "found context (%" GST_PTR_FORMAT ") in upstream query", ctxt); + gst_element_set_context (element, ctxt); + } + } + + if (*device == nullptr) { + GstMessage *msg; + + GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element, + "posting need context message"); + msg = gst_message_new_need_context (GST_OBJECT_CAST (element), + GST_D3D12_DEVICE_HANDLE_CONTEXT_TYPE); + gst_element_post_message (element, msg); + } + + gst_query_unref (query); +} + +gboolean +gst_d3d12_ensure_element_data (GstElement * element, gint adapter_index, + GstD3D12Device ** device) +{ + guint target_adapter = 0; + std::lock_guard < std::recursive_mutex > lk (context_lock_); + + g_return_val_if_fail (element != nullptr, FALSE); + g_return_val_if_fail (device != nullptr, FALSE); + + init_context_debug (); + + if (*device) { + GST_LOG_OBJECT (element, "already have a device %" GST_PTR_FORMAT, *device); + return TRUE; + } + + run_d3d12_context_query (element, device); + if (*device) + return TRUE; + + if (adapter_index > 0) + target_adapter = adapter_index; + + *device = gst_d3d12_device_new (target_adapter); + + if (*device == nullptr) { + GST_ERROR_OBJECT (element, + "Couldn't create new device with adapter index %d", target_adapter); + return FALSE; + } else { + GstContext *context; + GstMessage *msg; + + /* Propagate new D3D12 device context */ + + context = gst_context_new (GST_D3D12_DEVICE_HANDLE_CONTEXT_TYPE, TRUE); + context_set_d3d12_device (context, *device); + + gst_element_set_context (element, context); + + GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element, + "posting have context (%p) message with D3D12 device context (%p)", + context, *device); + msg = gst_message_new_have_context (GST_OBJECT_CAST (element), context); + gst_element_post_message (GST_ELEMENT_CAST (element), msg); + } + + return TRUE; +} + +gboolean +gst_d3d12_ensure_element_data_for_adapter_luid (GstElement * element, + gint64 adapter_luid, GstD3D12Device ** device) +{ + std::lock_guard < std::recursive_mutex > lk (context_lock_); + + g_return_val_if_fail (element != nullptr, FALSE); + g_return_val_if_fail (device != nullptr, FALSE); + + init_context_debug (); + + if (*device) { + GST_LOG_OBJECT (element, "already have a device %" GST_PTR_FORMAT, *device); + return TRUE; + } + + run_d3d12_context_query (element, device); + if (*device) + return TRUE; + + *device = gst_d3d12_device_new_for_adapter_luid (adapter_luid); + + if (*device == nullptr) { + GST_ERROR_OBJECT (element, + "Couldn't create new device with adapter luid %" G_GINT64_FORMAT, + adapter_luid); + return FALSE; + } else { + GstContext *context; + GstMessage *msg; + + /* Propagate new D3D12 device context */ + context = gst_context_new (GST_D3D12_DEVICE_HANDLE_CONTEXT_TYPE, TRUE); + context_set_d3d12_device (context, *device); + + gst_element_set_context (element, context); + + GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element, + "posting have context (%p) message with D3D12 device context (%p)", + context, *device); + msg = gst_message_new_have_context (GST_OBJECT_CAST (element), context); + gst_element_post_message (GST_ELEMENT_CAST (element), msg); + } + + return TRUE; +} + +gint64 +gst_d3d12_luid_to_int64 (const LUID * luid) +{ + LARGE_INTEGER val; + + g_return_val_if_fail (luid != nullptr, 0); + + val.LowPart = luid->LowPart; + val.HighPart = luid->HighPart; + + return val.QuadPart; +} + +GstContext * +gst_d3d12_context_new (GstD3D12Device * device) +{ + GstContext *context; + + g_return_val_if_fail (GST_IS_D3D12_DEVICE (device), nullptr); + + context = gst_context_new (GST_D3D12_DEVICE_HANDLE_CONTEXT_TYPE, TRUE); + context_set_d3d12_device (context, device); + + return context; + +} + +gboolean +_gst_d3d12_result (HRESULT hr, GstD3D12Device * device, GstDebugCategory * cat, + const gchar * file, const gchar * function, gint line, GstDebugLevel level) +{ +#ifndef GST_DISABLE_GST_DEBUG + gboolean ret = TRUE; + + if (device) + gst_d3d12_device_d3d12_debug (device, file, function, line); + + if (FAILED (hr)) { + gchar *error_text = nullptr; + + error_text = g_win32_error_message ((guint) hr); + /* g_win32_error_message() doesn't cover all HERESULT return code, + * so it could be empty string, or nullptr if there was an error + * in g_utf16_to_utf8() */ + gst_debug_log (cat, level, file, function, line, + nullptr, "D3D12 call failed: 0x%x, %s", (guint) hr, + GST_STR_NULL (error_text)); + g_free (error_text); + + ret = FALSE; + } + + return ret; +#else + return SUCCEEDED (hr); +#endif +} + +guint +gst_d3d12_calculate_subresource (guint mip_slice, guint array_slice, + guint plane_slice, guint mip_level, guint array_size) +{ + return mip_slice + array_slice * mip_level + + plane_slice * mip_level * array_size; +} diff --git a/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12utils.h b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12utils.h new file mode 100644 index 0000000000..f393ba6b79 --- /dev/null +++ b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12utils.h @@ -0,0 +1,217 @@ +/* GStreamer + * Copyright (C) 2023 Seungha Yang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include +#include "gstd3d12_fwd.h" +#include "gstd3d12device.h" + +G_BEGIN_DECLS + +gboolean gst_d3d12_handle_set_context (GstElement * element, + GstContext * context, + gint adapter_index, + GstD3D12Device ** device); + +gboolean gst_d3d12_handle_set_context_for_adapter_luid (GstElement * element, + GstContext * context, + gint64 adapter_luid, + GstD3D12Device ** device); + +gboolean gst_d3d12_handle_context_query (GstElement * element, + GstQuery * query, + GstD3D12Device * device); + +gboolean gst_d3d12_ensure_element_data (GstElement * element, + gint adapter_index, + GstD3D12Device ** device); + +gboolean gst_d3d12_ensure_element_data_for_adapter_luid (GstElement * element, + gint64 adapter_luid, + GstD3D12Device ** device); + +gint64 gst_d3d12_luid_to_int64 (const LUID * luid); + +GstContext * gst_d3d12_context_new (GstD3D12Device * device); + +gboolean _gst_d3d12_result (HRESULT hr, + GstD3D12Device * device, + GstDebugCategory * cat, + const gchar * file, + const gchar * function, + gint line, + GstDebugLevel level); + +/** + * gst_d3d12_result: + * @result: HRESULT D3D12 API return code + * @device: (nullable): Associated #GstD3D12Device + * + * Returns: %TRUE if D3D12 API call result is SUCCESS + */ +#define gst_d3d12_result(result,device) \ + _gst_d3d12_result (result, device, GST_CAT_DEFAULT, __FILE__, GST_FUNCTION, __LINE__, GST_LEVEL_ERROR) + +guint gst_d3d12_calculate_subresource (guint mip_slice, + guint array_slice, + guint plane_slice, + guint mip_level, + guint array_size); + +#define GST_D3D12_CLEAR_COM(obj) G_STMT_START { \ + if (obj) { \ + (obj)->Release (); \ + (obj) = NULL; \ + } \ + } G_STMT_END + +G_END_DECLS + +struct CD3D12_HEAP_PROPERTIES : public D3D12_HEAP_PROPERTIES +{ + CD3D12_HEAP_PROPERTIES ( + D3D12_HEAP_TYPE type, + D3D12_CPU_PAGE_PROPERTY cpu_page_property = D3D12_CPU_PAGE_PROPERTY_UNKNOWN, + D3D12_MEMORY_POOL memory_pool_preference = D3D12_MEMORY_POOL_UNKNOWN, + UINT creation_node_mask = 1, + UINT visible_node_mask = 1) + { + Type = type; + CPUPageProperty = cpu_page_property; + MemoryPoolPreference = memory_pool_preference; + CreationNodeMask = creation_node_mask; + VisibleNodeMask = visible_node_mask; + } +}; + +struct CD3D12_RESOURCE_DESC : public D3D12_RESOURCE_DESC +{ + CD3D12_RESOURCE_DESC( + D3D12_RESOURCE_DIMENSION dimension, + UINT64 alignment, + UINT64 width, + UINT height, + UINT16 depth_or_array_size, + UINT16 mip_levels, + DXGI_FORMAT format, + UINT sample_count, + UINT sample_quality, + D3D12_TEXTURE_LAYOUT layout, + D3D12_RESOURCE_FLAGS flags) + { + Dimension = dimension; + Alignment = alignment; + Width = width; + Height = height; + DepthOrArraySize = depth_or_array_size; + MipLevels = mip_levels; + Format = format; + SampleDesc.Count = sample_count; + SampleDesc.Quality = sample_quality; + Layout = layout; + Flags = flags; + } + + static inline CD3D12_RESOURCE_DESC Buffer ( + UINT64 width, + D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE, + UINT64 alignment = 0) + { + return CD3D12_RESOURCE_DESC (D3D12_RESOURCE_DIMENSION_BUFFER, alignment, + width, 1, 1, 1, DXGI_FORMAT_UNKNOWN, 1, 0, + D3D12_TEXTURE_LAYOUT_ROW_MAJOR, flags); + } + + static inline CD3D12_RESOURCE_DESC Tex2D ( + DXGI_FORMAT format, + UINT64 width, + UINT height, + UINT16 array_size = 1, + UINT16 mip_levels = 0, + UINT sample_count = 1, + UINT sample_quality = 0, + D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE, + D3D12_TEXTURE_LAYOUT layout = D3D12_TEXTURE_LAYOUT_UNKNOWN, + UINT64 alignment = 0) + { + return CD3D12_RESOURCE_DESC(D3D12_RESOURCE_DIMENSION_TEXTURE2D, alignment, + width, height, array_size, mip_levels, format, sample_count, + sample_quality, layout, flags); + } +}; + +struct CD3D12_RESOURCE_BARRIER : public D3D12_RESOURCE_BARRIER +{ + static inline CD3D12_RESOURCE_BARRIER Transition ( + ID3D12Resource * resource, + D3D12_RESOURCE_STATES before, + D3D12_RESOURCE_STATES after, + UINT subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, + D3D12_RESOURCE_BARRIER_FLAGS flags = D3D12_RESOURCE_BARRIER_FLAG_NONE) + { + CD3D12_RESOURCE_BARRIER result; + D3D12_RESOURCE_BARRIER &barrier = result; + result.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + result.Flags = flags; + barrier.Transition.pResource = resource; + barrier.Transition.StateBefore = before; + barrier.Transition.StateAfter = after; + barrier.Transition.Subresource = subresource; + + return result; + } +}; + +struct CD3D12_TEXTURE_COPY_LOCATION : public D3D12_TEXTURE_COPY_LOCATION +{ + CD3D12_TEXTURE_COPY_LOCATION (ID3D12Resource * resource) + { + pResource = resource; + Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + PlacedFootprint = {}; + } + + CD3D12_TEXTURE_COPY_LOCATION ( + ID3D12Resource * resource, + D3D12_PLACED_SUBRESOURCE_FOOTPRINT const & foot_print) + { + pResource = resource; + Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + PlacedFootprint = foot_print; + } + + CD3D12_TEXTURE_COPY_LOCATION ( + ID3D12Resource * resource, + UINT subresource) + { + pResource = resource; + Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + PlacedFootprint = {}; + SubresourceIndex = subresource; + } +}; + +#include + +#define GST_D3D12_CALL_ONCE_BEGIN \ + static std::once_flag __once_flag; \ + std::call_once (__once_flag, [&]() + +#define GST_D3D12_CALL_ONCE_END ) diff --git a/subprojects/gst-plugins-bad/sys/d3d12/meson.build b/subprojects/gst-plugins-bad/sys/d3d12/meson.build new file mode 100644 index 0000000000..e9e48a9a73 --- /dev/null +++ b/subprojects/gst-plugins-bad/sys/d3d12/meson.build @@ -0,0 +1,75 @@ +d3d12_sources = [ + 'gstd3d12decoder.cpp', + 'gstd3d12device.cpp', + 'gstd3d12fence.cpp', + 'gstd3d12format.cpp', + 'gstd3d12h264dec.cpp', + 'gstd3d12memory.cpp', + 'gstd3d12utils.cpp', + 'plugin.cpp', +] + +extra_args = ['-DGST_USE_UNSTABLE_API'] +extra_dep = [] + +d3d12_option = get_option('d3d12') +if host_system != 'windows' or d3d12_option.disabled() + subdir_done() +endif + +d3d12_lib = cc.find_library('d3d12', required : d3d12_option) +dxgi_lib = cc.find_library('dxgi', required : d3d12_option) + +if not gstdxva_dep.found() or not d3d12_lib.found() or not dxgi_lib.found() + if d3d12_option.enabled() + error('The d3d12 was enabled explicitly, but required GstD3D11 dependencies were not found.') + endif + subdir_done() +endif + +d3d12_headers = [ + 'd3d12.h', + 'd3d12video.h', + 'dxgi1_6.h', + 'wrl.h', +] + +have_d3d12_headers = true +foreach h: d3d12_headers + if not cc.has_header(h) + have_d3d12_headers = false + endif +endforeach + +if not have_d3d12_headers + if d3d12_option.enabled() + error('The d3d12 plugin was enabled explicitly, but required dependencies were not found.') + endif + subdir_done() +endif + +if cc.has_header('d3d12sdklayers.h') + extra_dep += ['-DHAVE_D3D12_SDKLAYERS_H'] +endif + +# MinGW 32bits compiler seems to be complaining about redundant-decls +# when ComPtr is in use. Let's just disable the warning +if cc.get_id() != 'msvc' + extra_mingw_args = cc.get_supported_arguments([ + '-Wno-redundant-decls', + ]) + + extra_args += extra_mingw_args +endif + +gstd3d12 = library('gstd3d12', + d3d12_sources, + c_args : gst_plugins_bad_args + extra_args, + cpp_args: gst_plugins_bad_args + extra_args, + include_directories : [configinc], + dependencies : [gstbase_dep, gstvideo_dep, gstcodecs_dep, + gstdxva_dep, d3d12_lib, dxgi_lib], + install : true, + install_dir : plugins_install_dir, +) +plugins += [gstd3d12] diff --git a/subprojects/gst-plugins-bad/sys/d3d12/plugin.cpp b/subprojects/gst-plugins-bad/sys/d3d12/plugin.cpp new file mode 100644 index 0000000000..d3432a5dae --- /dev/null +++ b/subprojects/gst-plugins-bad/sys/d3d12/plugin.cpp @@ -0,0 +1,94 @@ +/* GStreamer + * Copyright (C) 2023 Seungha Yang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include "gstd3d12device.h" +#include "gstd3d12h264dec.h" + +#include + +/* *INDENT-OFF* */ +using namespace Microsoft::WRL; +/* *INDENT-ON* */ + +GST_DEBUG_CATEGORY (gst_d3d12_debug); +GST_DEBUG_CATEGORY (gst_d3d12_allocator_debug); +GST_DEBUG_CATEGORY (gst_d3d12_command_list_debug); +GST_DEBUG_CATEGORY (gst_d3d12_decoder_debug); +GST_DEBUG_CATEGORY (gst_d3d12_fence_debug); +GST_DEBUG_CATEGORY (gst_d3d12_format_debug); +GST_DEBUG_CATEGORY (gst_d3d12_utils_debug); + +#define GST_CAT_DEFAULT gst_d3d12_debug + +static gboolean +plugin_init (GstPlugin * plugin) +{ + GST_DEBUG_CATEGORY_INIT (gst_d3d12_debug, "d3d12", 0, "d3d12"); + GST_DEBUG_CATEGORY_INIT (gst_d3d12_allocator_debug, "d3d12allocator", 0, + "d3d12allocator"); + GST_DEBUG_CATEGORY_INIT (gst_d3d12_command_list_debug, "d3d12commandlist", 0, + "d3d12commandlist"); + GST_DEBUG_CATEGORY_INIT (gst_d3d12_decoder_debug, "d3d12decoder", 0, + "d3d12decoder"); + GST_DEBUG_CATEGORY_INIT (gst_d3d12_fence_debug, "d3d12fence", 0, + "d3d12fence"); + GST_DEBUG_CATEGORY_INIT (gst_d3d12_format_debug, "d3d12format", 0, + "d3d12format"); + GST_DEBUG_CATEGORY_INIT (gst_d3d12_utils_debug, + "d3d12utils", 0, "d3d12utils"); + + /* Enumerate devices to register decoders per device and to get the highest + * feature level */ + /* AMD seems to be supporting up to 12 cards, and 8 for NVIDIA */ + for (guint i = 0; i < 12; i++) { + GstD3D12Device *device = nullptr; + ID3D12Device *device_handle; + ComPtr < ID3D12VideoDevice > video_device; + HRESULT hr; + + device = gst_d3d12_device_new (i); + if (!device) + break; + + device_handle = gst_d3d12_device_get_device_handle (device); + hr = device_handle->QueryInterface (IID_PPV_ARGS (&video_device)); + if (FAILED (hr)) { + gst_object_unref (device); + continue; + } + + gst_d3d12_h264_dec_register (plugin, device, video_device.Get (), + GST_RANK_NONE); + + gst_object_unref (device); + } + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + d3d12, + "Direct3D12 plugin", + plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/subprojects/gst-plugins-bad/sys/meson.build b/subprojects/gst-plugins-bad/sys/meson.build index f554617957..8285f07fa5 100644 --- a/subprojects/gst-plugins-bad/sys/meson.build +++ b/subprojects/gst-plugins-bad/sys/meson.build @@ -4,6 +4,7 @@ subdir('applemedia') subdir('asio') subdir('bluez') subdir('d3d11') +subdir('d3d12') subdir('d3dvideosink') subdir('decklink') subdir('directsound')