/* 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 "gstd3d12dpbstorage.h" #include #include #include GST_DEBUG_CATEGORY_STATIC (gst_d3d12_dpb_storage_debug); #define GST_CAT_DEFAULT gst_d3d12_dpb_storage_debug /* *INDENT-OFF* */ using namespace Microsoft::WRL; struct OwnedTexture { ComPtr texture; guint subresource = 0; gboolean is_free = TRUE; }; struct GstD3D12DpbStoragePrivate { gboolean array_of_textures; DXGI_FORMAT format; guint width; guint height; D3D12_RESOURCE_FLAGS resource_flags; std::vector dpb; std::vector dpb_subresource; std::vector pool; ComPtr base_texture; }; /* *INDENT-ON* */ struct _GstD3D12DpbStorage { GstObject parent; GstD3D12Device *device; GstD3D12DpbStoragePrivate *priv; }; static void gst_d3d12_dpb_storage_finalize (GObject * object); #define gst_d3d12_dpb_storage_parent_class parent_class G_DEFINE_TYPE (GstD3D12DpbStorage, gst_d3d12_dpb_storage, GST_TYPE_OBJECT); static void gst_d3d12_dpb_storage_class_init (GstD3D12DpbStorageClass * klass) { auto object_class = G_OBJECT_CLASS (klass); object_class->finalize = gst_d3d12_dpb_storage_finalize; GST_DEBUG_CATEGORY_INIT (gst_d3d12_dpb_storage_debug, "d3d12dpbstorage", 0, "d3d12dpbstorage"); } static void gst_d3d12_dpb_storage_init (GstD3D12DpbStorage * self) { self->priv = new GstD3D12DpbStoragePrivate (); } static void gst_d3d12_dpb_storage_finalize (GObject * object) { auto self = GST_D3D12_DPB_STORAGE (object); delete self->priv; gst_clear_object (&self->device); G_OBJECT_CLASS (parent_class)->finalize (object); } static HRESULT allocate_texture (ID3D12Device * device, DXGI_FORMAT format, guint width, guint height, D3D12_RESOURCE_FLAGS resource_flags, guint array_size, ID3D12Resource ** texture) { D3D12_HEAP_PROPERTIES prop = CD3DX12_HEAP_PROPERTIES (D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC desc = CD3DX12_RESOURCE_DESC::Tex2D (format, width, height, array_size, 1, 1, 0, resource_flags); return device->CreateCommittedResource (&prop, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_COMMON, nullptr, IID_PPV_ARGS (texture)); } gboolean gst_d3d12_dpb_storage_acquire_frame (GstD3D12DpbStorage * storage, D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE * frame) { g_return_val_if_fail (GST_IS_D3D12_DPB_STORAGE (storage), FALSE); g_return_val_if_fail (frame, FALSE); auto priv = storage->priv; for (size_t i = 0; i < priv->pool.size (); i++) { auto & it = priv->pool[i]; if (it.is_free) { frame->pReconstructedPicture = it.texture.Get (); frame->ReconstructedPictureSubresource = it.subresource; it.is_free = FALSE; return TRUE; } } if (!priv->array_of_textures) { GST_ERROR_OBJECT (storage, "No available free texture"); frame->pReconstructedPicture = nullptr; frame->ReconstructedPictureSubresource = 0; return FALSE; } auto device = gst_d3d12_device_get_device_handle (storage->device); OwnedTexture new_texture; new_texture.is_free = FALSE; HRESULT hr = allocate_texture (device, priv->format, priv->width, priv->height, priv->resource_flags, 1, &new_texture.texture); if (!gst_d3d12_result (hr, storage->device)) { GST_ERROR_OBJECT (storage, "Couldn't allocate texture"); frame->pReconstructedPicture = nullptr; frame->ReconstructedPictureSubresource = 0; return FALSE; } frame->pReconstructedPicture = new_texture.texture.Get (); frame->ReconstructedPictureSubresource = 0; priv->pool.push_back (new_texture); return TRUE; } gboolean gst_d3d12_dpb_storage_add_frame (GstD3D12DpbStorage * storage, D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE * frame) { g_return_val_if_fail (GST_IS_D3D12_DPB_STORAGE (storage), FALSE); g_return_val_if_fail (frame, FALSE); g_return_val_if_fail (frame->pReconstructedPicture, FALSE); auto priv = storage->priv; priv->dpb.insert (priv->dpb.begin (), frame->pReconstructedPicture); priv->dpb_subresource.insert (priv->dpb_subresource.begin (), frame->ReconstructedPictureSubresource); return TRUE; } gboolean gst_d3d12_dpb_storage_get_reference_frames (GstD3D12DpbStorage * storage, D3D12_VIDEO_ENCODE_REFERENCE_FRAMES * ref_frames) { g_return_val_if_fail (GST_IS_D3D12_DPB_STORAGE (storage), FALSE); g_return_val_if_fail (ref_frames, FALSE); auto priv = storage->priv; ref_frames->NumTexture2Ds = priv->dpb.size (); ref_frames->ppTexture2Ds = priv->dpb.data (); if (priv->array_of_textures) ref_frames->pSubresources = nullptr; else ref_frames->pSubresources = priv->dpb_subresource.data (); return TRUE; } static void gst_d3d12_dpb_storage_release_frame (GstD3D12DpbStorage * self, ID3D12Resource * texture, guint subresource) { auto priv = self->priv; if (priv->array_of_textures) { for (size_t i = 0; i < priv->pool.size (); i++) { auto & it = priv->pool[i]; if (texture == it.texture.Get () && it.subresource == subresource) { it.is_free = TRUE; return; } } g_assert_not_reached (); } else { g_return_if_fail (subresource < priv->pool.size ()); priv->pool[subresource].is_free = TRUE; } } gboolean gst_d3d12_dpb_storage_remove_oldest_frame (GstD3D12DpbStorage * storage) { g_return_val_if_fail (GST_IS_D3D12_DPB_STORAGE (storage), FALSE); auto priv = storage->priv; if (priv->dpb.empty ()) { GST_WARNING_OBJECT (storage, "DPB is empty now"); return FALSE; } gst_d3d12_dpb_storage_release_frame (storage, priv->dpb.back (), priv->dpb_subresource.back ()); priv->dpb.pop_back (); priv->dpb_subresource.pop_back (); return TRUE; } void gst_d3d12_dpb_storage_clear_dpb (GstD3D12DpbStorage * storage) { g_return_if_fail (GST_IS_D3D12_DPB_STORAGE (storage)); auto priv = storage->priv; g_assert (priv->dpb.size () == priv->dpb_subresource.size ()); for (size_t i = 0; i < priv->dpb.size (); i++) { gst_d3d12_dpb_storage_release_frame (storage, priv->dpb[i], priv->dpb_subresource[i]); } priv->dpb.clear (); priv->dpb_subresource.clear (); } guint gst_d3d12_dpb_storage_get_dpb_size (GstD3D12DpbStorage * storage) { g_return_val_if_fail (GST_IS_D3D12_DPB_STORAGE (storage), 0); auto priv = storage->priv; return priv->dpb.size (); } guint gst_d3d12_dpb_storage_get_pool_size (GstD3D12DpbStorage * storage) { g_return_val_if_fail (GST_IS_D3D12_DPB_STORAGE (storage), 0); auto priv = storage->priv; return priv->pool.size (); } GstD3D12DpbStorage * gst_d3d12_dpb_storage_new (GstD3D12Device * device, guint dpb_size, gboolean use_array_of_textures, DXGI_FORMAT format, guint width, guint height, D3D12_RESOURCE_FLAGS resource_flags) { g_return_val_if_fail (GST_IS_D3D12_DEVICE (device), nullptr); auto self = (GstD3D12DpbStorage *) g_object_new (GST_TYPE_D3D12_DPB_STORAGE, nullptr); gst_object_ref_sink (self); self->device = (GstD3D12Device *) gst_object_ref (device); auto priv = self->priv; auto device_handle = gst_d3d12_device_get_device_handle (device); HRESULT hr; if (use_array_of_textures) { for (guint i = 0; i < dpb_size; i++) { OwnedTexture texture; hr = allocate_texture (device_handle, format, width, height, resource_flags, 1, &texture.texture); if (!gst_d3d12_result (hr, device)) { GST_ERROR_OBJECT (self, "Couldn't allocate initial texture"); gst_object_unref (self); return nullptr; } priv->pool.push_back (texture); } } else { hr = allocate_texture (device_handle, format, width, height, resource_flags, dpb_size, &priv->base_texture); if (!gst_d3d12_result (hr, device)) { GST_ERROR_OBJECT (self, "Couldn't allocate initial texture"); gst_object_unref (self); return nullptr; } priv->pool.resize (dpb_size); for (guint i = 0; i < dpb_size; i++) { priv->pool[i].texture = priv->base_texture; priv->pool[i].subresource = i; } } priv->width = width; priv->height = height; priv->resource_flags = resource_flags; priv->array_of_textures = use_array_of_textures; priv->dpb.reserve (dpb_size); priv->dpb_subresource.reserve (dpb_size); return self; }