d3d12swapchainsink: Add uv-remap and redraw action signal

New uv-remap signal can be used for UV coordinate remap operation
in videosink, and redraw signal can allow updating view even in paused
state

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9209>
This commit is contained in:
Seungha Yang 2025-06-11 22:42:22 +09:00 committed by GStreamer Marge Bot
parent ad6ce32a74
commit c5fd61c908

View File

@ -41,6 +41,7 @@
#include <wrl.h>
#include <vector>
#include <memory>
#include <math.h>
/* *INDENT-OFF* */
using namespace Microsoft::WRL;
@ -83,6 +84,8 @@ enum
enum
{
SIGNAL_RESIZE,
SIGNAL_UV_REMAP,
SIGNAL_REDRAW,
SIGNAL_LAST
};
@ -225,6 +228,11 @@ struct GstD3D12SwapChainSinkPrivate
FLOAT border_color_val[4];
GstVideoRectangle viewport = { };
gboolean auto_resize = FALSE;
gboolean did_redraw = FALSE;
std::vector<ComPtr<ID3D12Resource>> uv_remap;
std::vector<D3D12_VIEWPORT> uv_remap_viewport_origin;
std::vector<GstVideoRectangle> uv_remap_viewport;
gint adapter = DEFAULT_ADAPTER;
gint force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
@ -272,6 +280,9 @@ static GstFlowReturn gst_d3d12_swapchain_sink_show_frame (GstVideoSink * sink,
GstBuffer * buf);
static void gst_d3d12_swapchain_sink_resize (GstD3D12SwapChainSink * self,
guint width, guint height);
static void gst_d3d12_swapchain_sink_uv_remap (GstD3D12SwapChainSink * self,
guint num_lut, ID3D12Resource ** lut, D3D12_VIEWPORT * viewport);
static void gst_d3d12_swapchain_sink_redraw (GstD3D12SwapChainSink * self);
static void
gst_d3d12_swapchain_sink_resize_internal (GstD3D12SwapChainSink * self,
guint width, guint height);
@ -388,6 +399,44 @@ gst_d3d12_swapchain_sink_class_init (GstD3D12SwapChainSinkClass * klass)
G_CALLBACK (gst_d3d12_swapchain_sink_resize), nullptr, nullptr, nullptr,
G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
/**
* GstD3D12SwapChainSink::uv-remap
* @videosink: the #GstD3D12SwapChainSink
* @num_lut: LUT resource array length
* @lut: Array of ID3D12Resource used for UV remap operation
* @viewport: Array of D3D12_VIEWPORT
*
* Sets list of ID3D12Resource for UV coordinates remapping.
* Valid formats are R8G8B8A8_UNORM and R16G16B16A16_UNORM.
* R -> U, G -> U, B -> unused, and A -> mask where A >= 0.5
* applies remapping, otherwise fill background color"
*
* TopLeftX, TopLeftY, Width, and Height values are used to calculate
* final viewport size. The coordinates must be normalized value in [0, 1]
* range instead of real viewport size.
*
* Since: 1.28
*/
d3d12_swapchain_sink_signals[SIGNAL_UV_REMAP] =
g_signal_new_class_handler ("uv-remap", G_TYPE_FROM_CLASS (klass),
(GSignalFlags) (G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
G_CALLBACK (gst_d3d12_swapchain_sink_uv_remap), nullptr, nullptr, nullptr,
G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_POINTER, G_TYPE_POINTER);
/**
* GstD3D12SwapChainSink::redraw
* @videosink: the #GstD3D12SwapChainSink
*
* Redraw last buffer and present it
*
* Since: 1.28
*/
d3d12_swapchain_sink_signals[SIGNAL_REDRAW] =
g_signal_new_class_handler ("redraw", G_TYPE_FROM_CLASS (klass),
(GSignalFlags) (G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
G_CALLBACK (gst_d3d12_swapchain_sink_redraw), nullptr, nullptr, nullptr,
G_TYPE_NONE, 0);
element_class->set_context =
GST_DEBUG_FUNCPTR (gst_d3d12_swapchain_sink_set_context);
@ -855,6 +904,53 @@ gst_d3d12_swapchain_sink_foreach_meta (GstBuffer * buffer, GstMeta ** meta,
return TRUE;
}
static void
calculate_remap_viewport (GstD3D12SwapChainSink * self,
const D3D12_VIEWPORT * d3d12_viewport, GstVideoRectangle * viewport)
{
auto priv = self->priv;
if (priv->viewport.w > 0 && priv->viewport.h > 0) {
double x = d3d12_viewport->TopLeftX;
double y = d3d12_viewport->TopLeftY;
double w = d3d12_viewport->Width;
double h = d3d12_viewport->Height;
/* Ensure normalized coordinate */
x = CLAMP (x, 0.0, 1.0);
y = CLAMP (y, 0.0, 1.0);
w = CLAMP (w, 0.0, 1.0);
h = CLAMP (h, 0.0, 1.0);
/* Scale to real viewport size */
gint xi = (gint) round ((double) priv->viewport.w * x) + priv->viewport.x;
gint yi = (gint) round ((double) priv->viewport.h * y) + priv->viewport.y;
gint wi = (gint) round ((double) priv->viewport.w * w);
gint hi = (gint) round ((double) priv->viewport.h * h);
/* clamp */
auto r = xi + wi;
auto rr = priv->viewport.x + priv->viewport.w;
if (rr < r)
wi = rr - xi;
auto b = yi + hi;
auto bb = priv->viewport.y + priv->viewport.h;
if (bb < b)
hi = bb - hi;
viewport->x = xi;
viewport->y = yi;
viewport->w = wi;
viewport->h = hi;
} else {
viewport->x = 0;
viewport->y = 0;
viewport->w = 0;
viewport->h = 0;
}
}
static gboolean
gst_d3d12_swapchain_sink_render (GstD3D12SwapChainSink * self)
{
@ -879,7 +975,7 @@ gst_d3d12_swapchain_sink_render (GstD3D12SwapChainSink * self)
priv->prev_crop_rect = crop_rect;
}
priv->lock.lock ();
std::lock_guard < std::recursive_mutex > lk (priv->lock);
if (priv->first_present || priv->output_updated) {
GstVideoRectangle dst_rect = { };
dst_rect.w = priv->width;
@ -895,6 +991,14 @@ gst_d3d12_swapchain_sink_render (GstD3D12SwapChainSink * self)
priv->viewport = dst_rect;
}
priv->uv_remap_viewport.clear ();
for (size_t i = 0; i < priv->uv_remap_viewport_origin.size (); i++) {
GstVideoRectangle uv_viewport = { };
calculate_remap_viewport (self, &priv->uv_remap_viewport_origin[i],
&uv_viewport);
priv->uv_remap_viewport.push_back (uv_viewport);
}
g_object_set (priv->conv, "dest-x", priv->viewport.x,
"dest-y", priv->viewport.y, "dest-width", priv->viewport.w,
"dest-height", priv->viewport.h, "hue", priv->hue,
@ -906,7 +1010,6 @@ gst_d3d12_swapchain_sink_render (GstD3D12SwapChainSink * self)
priv->first_present = false;
priv->output_updated = false;
}
priv->lock.unlock ();
gst_d3d12_overlay_compositor_upload (priv->comp, priv->cached_buf);
@ -974,17 +1077,37 @@ gst_d3d12_swapchain_sink_render (GstD3D12SwapChainSink * self)
if (priv->viewport.x != 0 || priv->viewport.y != 0 ||
(guint) priv->viewport.w != priv->width ||
(guint) priv->viewport.h != priv->height) {
(guint) priv->viewport.h != priv->height || !priv->uv_remap.empty ()) {
auto rtv_heap = gst_d3d12_memory_get_render_target_view_heap (mem);
auto cpu_handle = GetCPUDescriptorHandleForHeapStart (rtv_heap);
cl->ClearRenderTargetView (cpu_handle, priv->border_color_val, 0, nullptr);
}
if (!gst_d3d12_converter_convert_buffer (priv->conv,
priv->cached_buf, conv_outbuf, fence_data, cl.Get (), TRUE)) {
GST_ERROR_OBJECT (self, "Couldn't build convert command");
gst_d3d12_fence_data_unref (fence_data);
return FALSE;
if (!priv->uv_remap.empty ()) {
std::vector < ID3D12Resource * >uv_remap;
for (size_t i = 0; i < priv->uv_remap.size (); i++)
uv_remap.push_back (priv->uv_remap[i].Get ());
if (!gst_d3d12_converter_convert_buffer_for_uv_remap (priv->conv,
priv->cached_buf, conv_outbuf, fence_data, cl.Get (), TRUE,
(guint) priv->uv_remap.size (), uv_remap.data (),
priv->uv_remap_viewport.data ())) {
GST_ERROR_OBJECT (self, "Couldn't build convert command");
gst_d3d12_fence_data_unref (fence_data);
return FALSE;
}
} else {
gst_d3d12_converter_update_viewport (priv->conv, priv->viewport.x,
priv->viewport.y, priv->viewport.w, priv->viewport.h);
gst_d3d12_converter_set_remap (priv->conv, nullptr);
if (!gst_d3d12_converter_convert_buffer (priv->conv,
priv->cached_buf, conv_outbuf, fence_data, cl.Get (), TRUE)) {
GST_ERROR_OBJECT (self, "Couldn't build convert command");
gst_d3d12_fence_data_unref (fence_data);
return FALSE;
}
}
if (!gst_d3d12_overlay_compositor_draw (priv->comp,
@ -1074,7 +1197,12 @@ gst_d3d12_swapchain_sink_set_buffer (GstD3D12SwapChainSink * self,
GST_VIDEO_SINK_WIDTH (self), GST_VIDEO_SINK_HEIGHT (self));
}
} else {
need_render = false;
if (priv->did_redraw) {
need_render = true;
} else {
need_render = false;
}
update_converter = false;
}
}
@ -1135,6 +1263,7 @@ gst_d3d12_swapchain_sink_set_buffer (GstD3D12SwapChainSink * self,
if (!need_render)
return TRUE;
priv->did_redraw = FALSE;
auto mem = gst_buffer_peek_memory (buffer, 0);
if (!gst_is_d3d12_memory (mem)) {
GstBuffer *upload = nullptr;
@ -1204,6 +1333,29 @@ gst_d3d12_swapchain_sink_resize_internal (GstD3D12SwapChainSink * self,
}
}
static void
gst_d3d12_swapchain_sink_redraw (GstD3D12SwapChainSink * self)
{
auto priv = self->priv;
std::lock_guard < std::recursive_mutex > lk (priv->lock);
GST_DEBUG_OBJECT (self, "Redraw");
if (priv->swapchain && priv->cached_buf &&
gst_d3d12_swapchain_sink_render (self)) {
GST_DEBUG_OBJECT (self, "Presenting redraw frame");
auto hr = priv->swapchain->Present (0, 0);
if (!gst_d3d12_result (hr, self->device))
GST_ERROR_OBJECT (self, "Present failed");
gst_d3d12_cmd_queue_execute_command_lists (priv->cq,
0, nullptr, &priv->fence_val);
priv->did_redraw = TRUE;
}
}
static void
gst_d3d12_swapchain_sink_resize (GstD3D12SwapChainSink * self, guint width,
guint height)
@ -1238,6 +1390,31 @@ gst_d3d12_swapchain_sink_resize (GstD3D12SwapChainSink * self, guint width,
gst_d3d12_swapchain_sink_resize_internal (self, width, height);
}
static void
gst_d3d12_swapchain_sink_uv_remap (GstD3D12SwapChainSink * self, guint num_lut,
ID3D12Resource ** lut, D3D12_VIEWPORT * viewport)
{
auto priv = self->priv;
std::lock_guard < std::recursive_mutex > lk (priv->lock);
priv->uv_remap.clear ();
priv->uv_remap_viewport.clear ();
priv->uv_remap_viewport_origin.clear ();
for (guint i = 0; i < num_lut; i++) {
ComPtr < ID3D12Resource > remap = lut[i];
priv->uv_remap.push_back (remap);
priv->uv_remap_viewport_origin.push_back (viewport[i]);
GstVideoRectangle rect = { };
calculate_remap_viewport (self, &viewport[i], &rect);
GST_DEBUG_OBJECT (self,
"Calculated viewport %d (x, y, w, h): %d, %d, %d, %d", i,
rect.x, rect.y, rect.w, rect.h);
priv->uv_remap_viewport.push_back (rect);
}
}
static gboolean
gst_d3d12_swapchain_sink_start (GstBaseSink * sink)
{
@ -1375,6 +1552,8 @@ gst_d3d12_swapchain_sink_prepare (GstBaseSink * sink, GstBuffer * buffer)
auto self = GST_D3D12_SWAPCHAIN_SINK (sink);
auto priv = self->priv;
GST_TRACE_OBJECT (self, "Prepare");
auto pts = GST_BUFFER_PTS (buffer);
if (GST_CLOCK_TIME_IS_VALID (pts)) {
auto stream_time = gst_segment_to_stream_time (&sink->segment,
@ -1398,6 +1577,8 @@ gst_d3d12_swapchain_sink_show_frame (GstVideoSink * sink, GstBuffer * buf)
auto self = GST_D3D12_SWAPCHAIN_SINK (sink);
auto priv = self->priv;
GST_TRACE_OBJECT (self, "Show frame");
std::lock_guard < std::recursive_mutex > lk (priv->lock);
if (!gst_d3d12_swapchain_sink_set_buffer (self, buf, FALSE)) {
GST_ERROR_OBJECT (self, "Set buffer failed");