From cb670f8110baa99c3d82b7407fc9d6a3f8d065ca Mon Sep 17 00:00:00 2001 From: Seungha Yang Date: Sat, 13 Apr 2024 21:46:32 +0900 Subject: [PATCH] d3d12: Add GstD3D12Frame struct and helper method Adding GstD3D12Frame struct with map, unmap, and copy methods. This new struct is equivalent to GstVideoFrame but gst_d3d12_frame_map() method will extract D3D12 specific resource handles from memory. Part-of: --- .../gst-libs/gst/d3d12/gstd3d12.h | 1 + .../gst-libs/gst/d3d12/gstd3d12_fwd.h | 2 + .../gst-libs/gst/d3d12/gstd3d12frame.cpp | 378 ++++++++++++++++++ .../gst-libs/gst/d3d12/gstd3d12frame.h | 104 +++++ .../gst-libs/gst/d3d12/meson.build | 2 + 5 files changed, 487 insertions(+) create mode 100644 subprojects/gst-plugins-bad/gst-libs/gst/d3d12/gstd3d12frame.cpp create mode 100644 subprojects/gst-plugins-bad/gst-libs/gst/d3d12/gstd3d12frame.h diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/d3d12/gstd3d12.h b/subprojects/gst-plugins-bad/gst-libs/gst/d3d12/gstd3d12.h index 1c0771b087..facb24aec1 100644 --- a/subprojects/gst-plugins-bad/gst-libs/gst/d3d12/gstd3d12.h +++ b/subprojects/gst-plugins-bad/gst-libs/gst/d3d12/gstd3d12.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #include diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/d3d12/gstd3d12_fwd.h b/subprojects/gst-plugins-bad/gst-libs/gst/d3d12/gstd3d12_fwd.h index 5030b38206..12ed95d609 100644 --- a/subprojects/gst-plugins-bad/gst-libs/gst/d3d12/gstd3d12_fwd.h +++ b/subprojects/gst-plugins-bad/gst-libs/gst/d3d12/gstd3d12_fwd.h @@ -88,5 +88,7 @@ typedef struct _GstD3D12FenceDataPoolClass GstD3D12FenceDataPoolClass; typedef struct _GstD3D12FenceDataPoolPrivate GstD3D12FenceDataPoolPrivate; typedef struct _GstD3D12FenceData GstD3D12FenceData; +typedef struct _GstD3D12Frame GstD3D12Frame; + G_END_DECLS diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/d3d12/gstd3d12frame.cpp b/subprojects/gst-plugins-bad/gst-libs/gst/d3d12/gstd3d12frame.cpp new file mode 100644 index 0000000000..1eecd0358f --- /dev/null +++ b/subprojects/gst-plugins-bad/gst-libs/gst/d3d12/gstd3d12frame.cpp @@ -0,0 +1,378 @@ +/* GStreamer + * Copyright (C) 2024 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 "gstd3d12frame.h" +#include "gstd3d12memory.h" +#include "gstd3d12device.h" +#include "gstd3d12-private.h" +#include +#include + +#ifndef GST_DISABLE_GST_DEBUG +#define GST_CAT_DEFAULT ensure_debug_category() +static GstDebugCategory * +ensure_debug_category (void) +{ + static GstDebugCategory *cat = nullptr; + + GST_D3D12_CALL_ONCE_BEGIN { + cat = _gst_debug_category_new ("d3d12frame", 0, "d3d12frame"); + } GST_D3D12_CALL_ONCE_END; + + return cat; +} +#endif + +/** + * gst_d3d12_frame_map: + * @frame: (out caller-allocates): pointer to #GstD3D12Frame + * @info: a #GstVideoInfo + * @buffer: the buffer to map + * @map_flags: #GstMapFlags + * @d3d12_flags: #GstD3D12FrameFlags + * + * Executes memory map operation fills @frame with extracted Direct3D12 resource + * information + * + * Returns: %TRUE on success. + * + * Since: 1.26 + */ +gboolean +gst_d3d12_frame_map (GstD3D12Frame * frame, const GstVideoInfo * info, + GstBuffer * buffer, GstMapFlags map_flags, + GstD3D12FrameMapFlags d3d12_flags) +{ + g_return_val_if_fail (frame, FALSE); + g_return_val_if_fail (info, FALSE); + g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE); + + memset (frame, 0, sizeof (GstD3D12Frame)); + + auto is_write = (map_flags & GST_MAP_WRITE) != 0; + auto is_writable = gst_buffer_is_writable (buffer); + + if (is_write && !is_writable) { + GST_ERROR ("Buffer is not writable"); + return FALSE; + } + + bool need_map = (map_flags & GST_MAP_READWRITE) != 0; + map_flags = (GstMapFlags) (map_flags | GST_MAP_D3D12); + + guint num_mem = gst_buffer_n_memory (buffer); + if (!num_mem) { + GST_ERROR ("Empty buffer"); + return FALSE; + } + + GstD3D12Device *device = nullptr; + for (guint i = 0; i < num_mem; i++) { + auto mem = gst_buffer_peek_memory (buffer, i); + if (!gst_is_d3d12_memory (mem)) { + GST_WARNING ("memory %u is not a d3d12 memory", i); + return FALSE; + } + + auto dmem = GST_D3D12_MEMORY_CAST (mem); + if (!device) { + device = dmem->device; + } else if (!gst_d3d12_device_is_equal (device, dmem->device)) { + GST_ERROR ("memory %u belongs to different device", i); + return FALSE; + } + + auto resource = gst_d3d12_memory_get_resource_handle (dmem); + D3D12_RESOURCE_DESC desc; + desc = GetDesc (resource); + + if ((d3d12_flags & GST_D3D12_FRAME_MAP_FLAG_SRV) != 0) { + if ((desc.Flags & D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE) != 0) { + GST_WARNING ("SRV map is requested but SRV is not allowed"); + return FALSE; + } + + if (!gst_d3d12_memory_get_shader_resource_view_heap (dmem)) { + GST_ERROR ("Couldn't get SRV descriptor heap"); + return FALSE; + } + } + + if ((d3d12_flags & GST_D3D12_FRAME_MAP_FLAG_RTV) != 0) { + if ((desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET) == 0) { + GST_WARNING ("RTV map is requested but RTV is not allowed"); + return FALSE; + } + + if (!gst_d3d12_memory_get_render_target_view_heap (dmem)) { + GST_ERROR ("Couldn't get RTV descriptor heap"); + return FALSE; + } + } + } + + auto device_handle = gst_d3d12_device_get_device_handle (device); + auto srv_inc_size = + device_handle->GetDescriptorHandleIncrementSize + (D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + auto rtv_inc_size = + device_handle->GetDescriptorHandleIncrementSize + (D3D12_DESCRIPTOR_HEAP_TYPE_RTV); + + guint plane_idx = 0; + for (guint i = 0; i < num_mem; i++) { + if (plane_idx >= G_N_ELEMENTS (frame->data)) { + GST_ERROR ("Too many planes"); + gst_d3d12_frame_unmap (frame); + return FALSE; + } + + auto mem = gst_buffer_peek_memory (buffer, i); + GstMemory *new_mem = mem; + if (need_map) { + mem = gst_memory_ref (mem); + + new_mem = gst_memory_make_mapped (mem, &frame->map[i], map_flags); + if (!new_mem) { + GST_ERROR ("Couldn't map memory %u", i); + gst_d3d12_frame_unmap (frame); + return FALSE; + } + + if (mem != new_mem && is_writable) { + gst_buffer_replace_memory_range (buffer, + i, 1, gst_memory_ref (new_mem)); + } + } + + auto dmem = GST_D3D12_MEMORY_CAST (new_mem); + auto num_planes = gst_d3d12_memory_get_plane_count (dmem); + auto resource = gst_d3d12_memory_get_resource_handle (dmem); + D3D12_RESOURCE_DESC desc; + desc = GetDesc (resource); + + ID3D12DescriptorHeap *srv_heap = nullptr; + ID3D12DescriptorHeap *rtv_heap = nullptr; + + if ((d3d12_flags & GST_D3D12_FRAME_MAP_FLAG_SRV) != 0) + srv_heap = gst_d3d12_memory_get_shader_resource_view_heap (dmem); + + if ((d3d12_flags & GST_D3D12_FRAME_MAP_FLAG_RTV) != 0) + rtv_heap = gst_d3d12_memory_get_render_target_view_heap (dmem); + + CD3DX12_CPU_DESCRIPTOR_HANDLE srv_handle; + if (srv_heap) { + srv_handle = + CD3DX12_CPU_DESCRIPTOR_HANDLE + (GetCPUDescriptorHandleForHeapStart (srv_heap)); + } + + CD3DX12_CPU_DESCRIPTOR_HANDLE rtv_handle; + if (rtv_heap) { + rtv_handle = + CD3DX12_CPU_DESCRIPTOR_HANDLE + (GetCPUDescriptorHandleForHeapStart (rtv_heap)); + } + + for (guint j = 0; j < num_planes; j++) { + if (plane_idx >= G_N_ELEMENTS (frame->data)) { + GST_ERROR ("Too many planes"); + gst_d3d12_frame_unmap (frame); + return FALSE; + } + + frame->data[plane_idx] = resource; + gst_d3d12_memory_get_subresource_index (dmem, + j, &frame->subresource_index[plane_idx]); + gst_d3d12_memory_get_plane_rectangle (dmem, j, + &frame->plane_rect[plane_idx]); + + if (srv_heap) { + frame->srv_desc_handle[plane_idx] = srv_handle; + srv_handle.Offset (srv_inc_size); + } + + if (rtv_heap) { + frame->rtv_desc_handle[plane_idx] = rtv_handle; + rtv_handle.Offset (rtv_inc_size); + } + + plane_idx++; + } + } + + guint frame_flags = 0; + if (GST_VIDEO_INFO_IS_INTERLACED (info)) { + if (GST_VIDEO_INFO_INTERLACE_MODE (info) == GST_VIDEO_INTERLACE_MODE_MIXED) { + if (GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_FLAG_INTERLACED)) { + frame_flags |= GST_VIDEO_FRAME_FLAG_INTERLACED; + } + } else { + frame_flags |= GST_VIDEO_FRAME_FLAG_INTERLACED; + } + + if (GST_VIDEO_INFO_FIELD_ORDER (info) == + GST_VIDEO_FIELD_ORDER_TOP_FIELD_FIRST) { + frame_flags |= GST_VIDEO_FRAME_FLAG_TFF; + } else { + if (GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_FLAG_TFF)) + frame_flags |= GST_VIDEO_FRAME_FLAG_TFF; + if (GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_FLAG_RFF)) + frame_flags |= GST_VIDEO_FRAME_FLAG_RFF; + if (GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_FLAG_ONEFIELD)) + frame_flags |= GST_VIDEO_FRAME_FLAG_ONEFIELD; + } + } + + frame->device = device; + frame->info = *info; + frame->buffer = buffer; + frame->frame_flags = (GstVideoFrameFlags) frame_flags; + frame->d3d12_flags = d3d12_flags; + + return TRUE; +} + +/** + * gst_d3d12_frame_unmap: + * @frame: a #GstD3D12Frame + * + * Unmap the memory previously mapped with gst_d3d12_frame_map + * + * Since: 1.26 + */ +void +gst_d3d12_frame_unmap (GstD3D12Frame * frame) +{ + g_return_if_fail (frame); + + for (guint i = 0; i < G_N_ELEMENTS (frame->map); i++) { + auto mem = frame->map[i].memory; + if (!mem) + return; + + gst_memory_unmap (mem, &frame->map[i]); + gst_memory_unref (mem); + } +} + +static void +gst_d3d12_frame_build_copy_args (GstD3D12Frame * dest, + const GstD3D12Frame * src, guint plane, + GstD3D12CopyTextureRegionArgs * args, D3D12_BOX * src_box) +{ + src_box->left = 0; + src_box->top = 0; + src_box->right = MIN (dest->plane_rect[plane].right, + src->plane_rect[plane].right); + src_box->bottom = MIN (dest->plane_rect[plane].bottom, + src->plane_rect[plane].bottom); + src_box->front = 0; + src_box->back = 1; + + args->dst = CD3DX12_TEXTURE_COPY_LOCATION (dest->data[plane], + dest->subresource_index[plane]); + args->src = CD3DX12_TEXTURE_COPY_LOCATION (src->data[plane], + src->subresource_index[plane]); +} + +/** + * gst_d3d12_frame_copy: + * @dest: a #GstD3D12Frame + * @src: a #GstD3D12Frame + * @fence_value: (out): a fence value for the copy operation + * + * Copy the contents from @src to @dest. + * + * Returns: %TRUE on success. + * + * Since: 1.26 + */ +gboolean +gst_d3d12_frame_copy (GstD3D12Frame * dest, const GstD3D12Frame * src, + guint64 * fence_value) +{ + g_return_val_if_fail (dest, FALSE); + g_return_val_if_fail (src, FALSE); + g_return_val_if_fail (dest->device, FALSE); + g_return_val_if_fail (src->device, FALSE); + g_return_val_if_fail (GST_VIDEO_INFO_FORMAT (&dest->info) == + GST_VIDEO_INFO_FORMAT (&src->info), FALSE); + + if (!gst_d3d12_device_is_equal (dest->device, src->device)) { + GST_ERROR ("Cross device copy is not supported"); + return FALSE; + } + + GstD3D12CopyTextureRegionArgs args[GST_VIDEO_MAX_PLANES] = { }; + D3D12_BOX src_box[GST_VIDEO_MAX_PLANES] = { }; + + for (guint i = 0; GST_VIDEO_INFO_N_PLANES (&dest->info); i++) { + gst_d3d12_frame_build_copy_args (dest, src, i, &args[i], &src_box[i]); + args[i].src_box = &src_box[i]; + } + + return gst_d3d12_device_copy_texture_region (dest->device, + GST_VIDEO_INFO_N_PLANES (&dest->info), args, + D3D12_COMMAND_LIST_TYPE_DIRECT, fence_value); +} + +/** + * gst_video_frame_copy_plane: + * @dest: a #GstD3D12Frame + * @src: a #GstD3D12Frame + * @plane: a plane + * @fence_value: (out): a fence value for the copy operation + * + * Copy the plane with index @plane from @src to @dest. + * + * Returns: %TRUE on success. + * + * Since: 1.26 + */ +gboolean +gst_d3d12_frame_copy_plane (GstD3D12Frame * dest, const GstD3D12Frame * src, + guint plane, guint64 * fence_value) +{ + g_return_val_if_fail (dest, FALSE); + g_return_val_if_fail (src, FALSE); + g_return_val_if_fail (dest->device, FALSE); + g_return_val_if_fail (src->device, FALSE); + g_return_val_if_fail (GST_VIDEO_INFO_FORMAT (&dest->info) == + GST_VIDEO_INFO_FORMAT (&src->info), FALSE); + g_return_val_if_fail (plane < GST_VIDEO_INFO_N_PLANES (&dest->info), FALSE); + + if (!gst_d3d12_device_is_equal (dest->device, src->device)) { + GST_ERROR ("Cross device copy is not supported"); + return FALSE; + } + + GstD3D12CopyTextureRegionArgs args = { }; + D3D12_BOX src_box = { }; + + gst_d3d12_frame_build_copy_args (dest, src, plane, &args, &src_box); + args.src_box = &src_box; + + return gst_d3d12_device_copy_texture_region (dest->device, 1, &args, + D3D12_COMMAND_LIST_TYPE_DIRECT, fence_value); +} diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/d3d12/gstd3d12frame.h b/subprojects/gst-plugins-bad/gst-libs/gst/d3d12/gstd3d12frame.h new file mode 100644 index 0000000000..34be950e37 --- /dev/null +++ b/subprojects/gst-plugins-bad/gst-libs/gst/d3d12/gstd3d12frame.h @@ -0,0 +1,104 @@ +/* GStreamer + * Copyright (C) 2024 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 + +G_BEGIN_DECLS + +/** + * GstD3D12FrameMapFlags: + * @GST_D3D12_FRAME_MAP_FLAG_NONE: No flags + * @GST_D3D12_FRAME_MAP_FLAG_SRV: Frame mapping requires shared resource view + * @GST_D3D12_FRAME_MAP_FLAG_RTV: Frame mapping requires render target view + * + * Since: 1.26 + */ +typedef enum +{ + GST_D3D12_FRAME_MAP_FLAG_NONE = 0, + GST_D3D12_FRAME_MAP_FLAG_SRV = (1 << 0), + GST_D3D12_FRAME_MAP_FLAG_RTV = (1 << 1), +} GstD3D12FrameMapFlags; + +DEFINE_ENUM_FLAG_OPERATORS (GstD3D12FrameMapFlags); + +/** + * GstD3D12Frame: + * @info: the #GstVideoInfo + * @frame_flags: #GstVideoFrameFlags for the frame + * @d3d12_flags: #GstD3D12FrameMapFlags for the frame + * @device: a #GstD3D12Device + * @buffer: the mapped buffer + * @map: mappings of the memory objects + * @data: pointers to the plane data + * @subresource_index: subresource index of the plane + * @plane_rect: plane rectangle + * @srv_desc_handle: shader resource view descriptor handle + * @rtb_desc_handle: render target view descriptor handle + * + * A frame obtained from gst_d3d12_frame_map() + * + * Since: 1.26 + */ +struct _GstD3D12Frame +{ + GstVideoInfo info; + GstVideoFrameFlags frame_flags; + GstD3D12FrameMapFlags d3d12_flags; + GstD3D12Device *device; + GstBuffer *buffer; + + GstMapInfo map[GST_VIDEO_MAX_PLANES]; + ID3D12Resource *data[GST_VIDEO_MAX_PLANES]; + guint subresource_index[GST_VIDEO_MAX_PLANES]; + D3D12_RECT plane_rect[GST_VIDEO_MAX_PLANES]; + D3D12_CPU_DESCRIPTOR_HANDLE srv_desc_handle[GST_VIDEO_MAX_PLANES]; + D3D12_CPU_DESCRIPTOR_HANDLE rtv_desc_handle[GST_VIDEO_MAX_PLANES]; + + /*< private >*/ + guint64 _gst_reserved[GST_PADDING_LARGE]; +}; + +GST_D3D12_API +gboolean gst_d3d12_frame_map (GstD3D12Frame * frame, + const GstVideoInfo * info, + GstBuffer * buffer, + GstMapFlags map_flags, + GstD3D12FrameMapFlags d3d12_flags); + +GST_D3D12_API +void gst_d3d12_frame_unmap (GstD3D12Frame * frame); + +GST_D3D12_API +gboolean gst_d3d12_frame_copy (GstD3D12Frame * dest, + const GstD3D12Frame * src, + guint64 * fence_value); + +GST_D3D12_API +gboolean gst_d3d12_frame_copy_plane (GstD3D12Frame * dest, + const GstD3D12Frame * src, + guint plane, + guint64 * fence_value); + +G_END_DECLS + diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/d3d12/meson.build b/subprojects/gst-plugins-bad/gst-libs/gst/d3d12/meson.build index 8779751545..daf65d75a5 100644 --- a/subprojects/gst-plugins-bad/gst-libs/gst/d3d12/meson.build +++ b/subprojects/gst-plugins-bad/gst-libs/gst/d3d12/meson.build @@ -9,6 +9,7 @@ d3d12_sources = [ 'gstd3d12device.cpp', 'gstd3d12fencedatapool.cpp', 'gstd3d12format.cpp', + 'gstd3d12frame.cpp', 'gstd3d12memory.cpp', 'gstd3d12utils.cpp', ] @@ -25,6 +26,7 @@ d3d12_headers = [ 'gstd3d12device.h', 'gstd3d12fencedatapool.h', 'gstd3d12format.h', + 'gstd3d12frame.h', 'gstd3d12memory.h', 'gstd3d12utils.h', ]