mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-23 16:50:47 +00:00
dc414415a5
... and adding hue, saturation, brightness, and contrast properties Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7817>
1107 lines
32 KiB
C++
1107 lines
32 KiB
C++
/* GStreamer
|
|
* Copyright (C) 2023 Seungha Yang <seungha@centricular.com>
|
|
*
|
|
* 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 "gstd3d12window.h"
|
|
#include "gstd3d12window-win32.h"
|
|
#include <directx/d3dx12.h>
|
|
#include <d3d11on12.h>
|
|
#include <d2d1_3.h>
|
|
#include <mutex>
|
|
#include <condition_variable>
|
|
#include <vector>
|
|
#include <memory>
|
|
#include <atomic>
|
|
#include <wrl.h>
|
|
#include <string>
|
|
|
|
/* *INDENT-OFF* */
|
|
using namespace Microsoft::WRL;
|
|
/* *INDENT-ON* */
|
|
|
|
GST_DEBUG_CATEGORY (gst_d3d12_window_debug);
|
|
#define GST_CAT_DEFAULT gst_d3d12_window_debug
|
|
|
|
enum
|
|
{
|
|
SIGNAL_KEY_EVENT,
|
|
SIGNAL_MOUSE_EVENT,
|
|
SIGNAL_SCROLL_EVENT,
|
|
SIGNAL_FULLSCREEN,
|
|
SIGNAL_OVERLAY,
|
|
SIGNAL_LAST
|
|
};
|
|
|
|
static guint d3d12_window_signals[SIGNAL_LAST] = { 0, };
|
|
|
|
GType
|
|
gst_d3d12_window_overlay_mode_get_type (void)
|
|
{
|
|
static GType mode_type = 0;
|
|
|
|
GST_D3D12_CALL_ONCE_BEGIN {
|
|
static const GFlagsValue mode_types[] = {
|
|
{GST_D3D12_WINDOW_OVERLAY_NONE, "None", "none"},
|
|
{GST_D3D12_WINDOW_OVERLAY_D3D12,
|
|
"Emits present signal with Direct3D12 resources", "d3d12"},
|
|
{GST_D3D12_WINDOW_OVERLAY_D3D11,
|
|
"Emits present signal with Direct3D12/11 resources", "d3d11"},
|
|
{GST_D3D12_WINDOW_OVERLAY_D2D,
|
|
"Emit present signal with Direct3D12/11 and Direct2D resources",
|
|
"d2d"},
|
|
{0, nullptr, nullptr},
|
|
};
|
|
|
|
mode_type = g_flags_register_static ("GstD3D12WindowOverlayMode",
|
|
mode_types);
|
|
} GST_D3D12_CALL_ONCE_END;
|
|
|
|
return mode_type;
|
|
}
|
|
|
|
/* *INDENT-OFF* */
|
|
|
|
struct GstD3D12WindowPrivate
|
|
{
|
|
GstD3D12WindowPrivate ()
|
|
{
|
|
main_context = g_main_context_new ();
|
|
loop = g_main_loop_new (main_context, FALSE);
|
|
|
|
render_rect.w = -1;
|
|
render_rect.h = -1;
|
|
|
|
gst_video_info_init (&input_info);
|
|
gst_video_info_init (&display_info);
|
|
|
|
fence_data_pool = gst_d3d12_fence_data_pool_new ();
|
|
}
|
|
|
|
~GstD3D12WindowPrivate ()
|
|
{
|
|
g_main_loop_unref (loop);
|
|
g_main_context_unref (main_context);
|
|
gst_clear_object (&fence_data_pool);
|
|
}
|
|
|
|
std::recursive_mutex lock;
|
|
DXGI_FORMAT display_format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
|
|
GstVideoOrientationMethod orientation = GST_VIDEO_ORIENTATION_IDENTITY;
|
|
gfloat fov = 90.0f;
|
|
gboolean ortho = FALSE;
|
|
gfloat rotation_x = 0;
|
|
gfloat rotation_y = 0;
|
|
gfloat rotation_z = 0;
|
|
gfloat scale_x = 1.0f;
|
|
gfloat scale_y = 1.0f;
|
|
gdouble hue = 0.0;
|
|
gdouble saturation = 1.0;
|
|
gdouble brightness = 0.0;
|
|
gdouble contrast = 1.0;
|
|
|
|
/* fullscreen related variables */
|
|
std::atomic<gboolean> fullscreen_on_alt_enter = { FALSE };
|
|
std::atomic<gboolean> requested_fullscreen = { FALSE };
|
|
|
|
GstD3D12FenceDataPool *fence_data_pool;
|
|
|
|
/* User specified rect */
|
|
GstVideoRectangle render_rect = { };
|
|
|
|
GstVideoRectangle output_rect = { };
|
|
RECT dirty_rect = { };
|
|
GstVideoInfo input_info;
|
|
GstVideoInfo display_info;
|
|
guint display_width = 8;
|
|
guint display_height = 8;
|
|
|
|
std::atomic<gboolean> enable_navigation = { TRUE };
|
|
gboolean force_aspect_ratio = TRUE;
|
|
gboolean output_updated = FALSE;
|
|
|
|
std::weak_ptr<SwapChainProxy> proxy;
|
|
SIZE_T proxy_id = 0;
|
|
|
|
std::wstring title;
|
|
|
|
std::atomic<GstD3D12MSAAMode> msaa = { GST_D3D12_MSAA_DISABLED };
|
|
std::atomic<GstD3D12WindowOverlayMode> overlay_mode =
|
|
{ GST_D3D12_WINDOW_OVERLAY_NONE };
|
|
|
|
/* Win32 window handles */
|
|
GThread *main_loop_thread = nullptr;
|
|
GMainLoop *loop = nullptr;
|
|
GMainContext *main_context = nullptr;
|
|
std::mutex loop_lock;
|
|
std::condition_variable loop_cond;
|
|
};
|
|
|
|
struct _GstD3D12Window
|
|
{
|
|
GstObject parent;
|
|
|
|
GstD3D12Device *device;
|
|
|
|
GstD3D12WindowPrivate *priv;
|
|
};
|
|
/* *INDENT-ON* */
|
|
|
|
#define gst_d3d12_window_parent_class parent_class
|
|
G_DEFINE_TYPE (GstD3D12Window, gst_d3d12_window, GST_TYPE_OBJECT);
|
|
|
|
static void gst_d3d12_window_finalize (GObject * object);
|
|
|
|
static void
|
|
gst_d3d12_window_class_init (GstD3D12WindowClass * klass)
|
|
{
|
|
auto object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->finalize = gst_d3d12_window_finalize;
|
|
|
|
d3d12_window_signals[SIGNAL_KEY_EVENT] =
|
|
g_signal_new_class_handler ("key-event", G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_LAST, nullptr, nullptr, nullptr, nullptr,
|
|
G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
|
|
|
|
d3d12_window_signals[SIGNAL_MOUSE_EVENT] =
|
|
g_signal_new_class_handler ("mouse-event", G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_LAST, nullptr, nullptr, nullptr, nullptr,
|
|
G_TYPE_NONE, 5, G_TYPE_STRING, G_TYPE_INT, G_TYPE_DOUBLE, G_TYPE_DOUBLE,
|
|
G_TYPE_UINT);
|
|
|
|
d3d12_window_signals[SIGNAL_SCROLL_EVENT] =
|
|
g_signal_new_class_handler ("scroll-event", G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_LAST, nullptr, nullptr, nullptr, nullptr,
|
|
G_TYPE_NONE, 5, G_TYPE_INT, G_TYPE_INT, G_TYPE_DOUBLE, G_TYPE_DOUBLE,
|
|
G_TYPE_UINT);
|
|
|
|
d3d12_window_signals[SIGNAL_FULLSCREEN] =
|
|
g_signal_new_class_handler ("fullscreen", G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_LAST, nullptr, nullptr, nullptr, nullptr,
|
|
G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
|
|
|
|
d3d12_window_signals[SIGNAL_OVERLAY] =
|
|
g_signal_new_class_handler ("overlay", G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_LAST, nullptr, nullptr, nullptr, nullptr,
|
|
G_TYPE_NONE, 6, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER,
|
|
G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER);
|
|
|
|
GST_DEBUG_CATEGORY_INIT (gst_d3d12_window_debug,
|
|
"d3d12window", 0, "d3d12window");
|
|
}
|
|
|
|
static void
|
|
gst_d3d12_window_init (GstD3D12Window * self)
|
|
{
|
|
self->priv = new GstD3D12WindowPrivate ();
|
|
}
|
|
|
|
static void
|
|
gst_d3d12_window_finalize (GObject * object)
|
|
{
|
|
auto self = GST_D3D12_WINDOW (object);
|
|
|
|
delete self->priv;
|
|
|
|
gst_clear_object (&self->device);
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
}
|
|
|
|
void
|
|
gst_d3d12_window_on_key_event (GstD3D12Window * window, const gchar * event,
|
|
const gchar * name)
|
|
{
|
|
g_signal_emit (window, d3d12_window_signals[SIGNAL_KEY_EVENT], 0, event,
|
|
name);
|
|
}
|
|
|
|
void
|
|
gst_d3d12_window_on_mouse_event (GstD3D12Window * window, const gchar * event,
|
|
gint button, double xpos, double ypos, guint modifier)
|
|
{
|
|
g_signal_emit (window, d3d12_window_signals[SIGNAL_MOUSE_EVENT], 0,
|
|
event, button, xpos, ypos, modifier);
|
|
}
|
|
|
|
void
|
|
gst_d3d12_window_on_scroll_event (GstD3D12Window * window, gint delta_x,
|
|
gint delta_y, double xpos, double ypos, guint modifier)
|
|
{
|
|
g_signal_emit (window, d3d12_window_signals[SIGNAL_SCROLL_EVENT], 0,
|
|
delta_x, delta_y, xpos, ypos, modifier);
|
|
}
|
|
|
|
static gboolean
|
|
msg_io_cb (GIOChannel * source, GIOCondition condition, gpointer data)
|
|
{
|
|
MSG msg;
|
|
|
|
if (!PeekMessage (&msg, nullptr, 0, 0, PM_REMOVE))
|
|
return G_SOURCE_CONTINUE;
|
|
|
|
TranslateMessage (&msg);
|
|
DispatchMessage (&msg);
|
|
|
|
return G_SOURCE_CONTINUE;
|
|
}
|
|
|
|
static gpointer
|
|
gst_d3d12_window_hwnd_thread_func (GstD3D12Window * self)
|
|
{
|
|
auto priv = self->priv;
|
|
auto server = HwndServer::get_instance ();
|
|
|
|
g_main_context_push_thread_default (priv->main_context);
|
|
|
|
priv->proxy_id = server->create_internal_window (self);
|
|
auto proxy = server->get_proxy (self, priv->proxy_id);
|
|
priv->proxy = proxy;
|
|
|
|
proxy->set_fullscreen_on_alt_enter (priv->fullscreen_on_alt_enter);
|
|
proxy->toggle_fullscreen (priv->requested_fullscreen);
|
|
|
|
auto msg_io_ch = g_io_channel_win32_new_messages (0);
|
|
auto msg_source = g_io_create_watch (msg_io_ch, G_IO_IN);
|
|
g_source_set_callback (msg_source, (GSourceFunc) msg_io_cb, nullptr, nullptr);
|
|
g_source_attach (msg_source, priv->main_context);
|
|
|
|
auto idle_source = g_idle_source_new ();
|
|
g_source_set_callback (idle_source,[](gpointer data)->gboolean {
|
|
auto self = GST_D3D12_WINDOW (data);
|
|
auto priv = self->priv;
|
|
std::lock_guard < std::mutex > lk (priv->loop_lock);
|
|
priv->loop_cond.notify_all ();
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
, self, nullptr);
|
|
|
|
g_source_attach (idle_source, priv->main_context);
|
|
g_source_unref (idle_source);
|
|
|
|
g_main_loop_run (priv->loop);
|
|
|
|
proxy = nullptr;
|
|
|
|
g_source_destroy (msg_source);
|
|
g_source_unref (msg_source);
|
|
g_io_channel_unref (msg_io_ch);
|
|
|
|
g_main_context_pop_thread_default (priv->main_context);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void
|
|
gst_d3d12_window_unprepare (GstD3D12Window * window)
|
|
{
|
|
GST_DEBUG_OBJECT (window, "Start unprepare");
|
|
|
|
auto priv = window->priv;
|
|
auto server = HwndServer::get_instance ();
|
|
|
|
priv->proxy.reset ();
|
|
server->release_proxy (window, priv->proxy_id);
|
|
|
|
g_main_loop_quit (priv->loop);
|
|
g_clear_pointer (&priv->main_loop_thread, g_thread_join);
|
|
|
|
GST_DEBUG_OBJECT (window, "Unprepare done");
|
|
}
|
|
|
|
void
|
|
gst_d3d12_window_unlock (GstD3D12Window * window)
|
|
{
|
|
GST_DEBUG_OBJECT (window, "Unlock");
|
|
auto server = HwndServer::get_instance ();
|
|
server->unlock_window (window);
|
|
}
|
|
|
|
void
|
|
gst_d3d12_window_unlock_stop (GstD3D12Window * window)
|
|
{
|
|
GST_DEBUG_OBJECT (window, "Unlock stop");
|
|
|
|
auto server = HwndServer::get_instance ();
|
|
server->unlock_stop_window (window);
|
|
}
|
|
|
|
static GstFlowReturn
|
|
gst_d3d12_window_resize_buffer (GstD3D12Window * self)
|
|
{
|
|
auto priv = self->priv;
|
|
auto proxy = priv->proxy.lock ();
|
|
if (!proxy)
|
|
return GST_FLOW_OK;
|
|
|
|
return proxy->resize_buffer (0, 0);
|
|
}
|
|
|
|
GstFlowReturn
|
|
gst_d3d12_window_open (GstD3D12Window * window, GstD3D12Device * device,
|
|
guint display_width, guint display_height, HWND parent_hwnd,
|
|
gboolean direct_swapchain)
|
|
{
|
|
auto priv = window->priv;
|
|
auto server = HwndServer::get_instance ();
|
|
|
|
GST_DEBUG_OBJECT (window, "Opening new window");
|
|
|
|
gst_d3d12_window_unprepare (window);
|
|
|
|
priv->display_width = display_width;
|
|
priv->display_height = display_height;
|
|
|
|
if (!parent_hwnd) {
|
|
priv->main_loop_thread = g_thread_new ("GstD3D12Window",
|
|
(GThreadFunc) gst_d3d12_window_hwnd_thread_func, window);
|
|
|
|
std::unique_lock < std::mutex > lk (priv->loop_lock);
|
|
while (!g_main_loop_is_running (priv->loop))
|
|
priv->loop_cond.wait (lk);
|
|
|
|
return GST_FLOW_OK;
|
|
}
|
|
|
|
auto ret = server->create_child_hwnd (window, parent_hwnd,
|
|
direct_swapchain, priv->proxy_id);
|
|
if (ret == GST_FLOW_OK)
|
|
priv->proxy = server->get_proxy (window, priv->proxy_id);
|
|
|
|
return ret;
|
|
}
|
|
|
|
GstFlowReturn
|
|
gst_d3d12_window_prepare (GstD3D12Window * window, GstD3D12Device * device,
|
|
guint display_width, guint display_height,
|
|
GstCaps * caps, GstStructure * config, DXGI_FORMAT display_format)
|
|
{
|
|
auto priv = window->priv;
|
|
GstVideoInfo in_info;
|
|
GstVideoFormat format = GST_VIDEO_FORMAT_RGBA;
|
|
priv->display_format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
priv->display_width = display_width;
|
|
priv->display_height = display_height;
|
|
|
|
gst_video_info_from_caps (&in_info, caps);
|
|
|
|
if (display_format != DXGI_FORMAT_UNKNOWN) {
|
|
priv->display_format = display_format;
|
|
format = gst_d3d12_dxgi_format_to_gst (display_format);
|
|
} else if (GST_VIDEO_INFO_COMP_DEPTH (&in_info, 0) > 8) {
|
|
auto device_handle = gst_d3d12_device_get_device_handle (device);
|
|
D3D12_FEATURE_DATA_FORMAT_SUPPORT format_support = { };
|
|
const D3D12_FORMAT_SUPPORT1 support_flags =
|
|
D3D12_FORMAT_SUPPORT1_RENDER_TARGET | D3D12_FORMAT_SUPPORT1_DISPLAY;
|
|
format_support.Format = DXGI_FORMAT_R10G10B10A2_UNORM;
|
|
format_support.Support1 = support_flags;
|
|
auto hr = device_handle->CheckFeatureSupport (D3D12_FEATURE_FORMAT_SUPPORT,
|
|
&format_support, sizeof (format_support));
|
|
if (SUCCEEDED (hr) && (format_support.Support1 & support_flags)
|
|
== support_flags) {
|
|
priv->display_format = DXGI_FORMAT_R10G10B10A2_UNORM;
|
|
format = GST_VIDEO_FORMAT_RGB10A2_LE;
|
|
}
|
|
}
|
|
|
|
{
|
|
std::lock_guard < std::recursive_mutex > lk (priv->lock);
|
|
priv->input_info = in_info;
|
|
gst_video_info_set_format (&priv->display_info, format,
|
|
display_width, display_height);
|
|
}
|
|
|
|
if (!gst_d3d12_device_is_equal (window->device, device)) {
|
|
gst_clear_object (&window->device);
|
|
window->device = (GstD3D12Device *) gst_object_ref (device);
|
|
}
|
|
|
|
auto proxy = priv->proxy.lock ();
|
|
if (!proxy) {
|
|
GST_WARNING_OBJECT (window, "Window was closed");
|
|
return GST_D3D12_WINDOW_FLOW_CLOSED;
|
|
}
|
|
|
|
return proxy->setup_swapchain (device, priv->display_format, &in_info,
|
|
&priv->display_info, config);
|
|
}
|
|
|
|
GstFlowReturn
|
|
gst_d3d12_window_render (GstD3D12Window * self, SwapChainResource * resource,
|
|
GstBuffer * buffer, bool is_first, RECT & output_rect)
|
|
{
|
|
auto priv = self->priv;
|
|
auto device = resource->device;
|
|
auto cur_idx = resource->swapchain->GetCurrentBackBufferIndex ();
|
|
auto swapbuf = resource->buffers[cur_idx];
|
|
|
|
{
|
|
std::lock_guard < std::recursive_mutex > lk (priv->lock);
|
|
if (is_first || priv->output_updated) {
|
|
GstVideoRectangle dst_rect = { };
|
|
GstVideoRectangle rst_rect = { };
|
|
dst_rect.w = (gint) resource->buffer_desc.Width;
|
|
dst_rect.h = (gint) resource->buffer_desc.Height;
|
|
|
|
for (size_t i = 0; i < resource->buffers.size (); i++)
|
|
resource->buffers[i]->is_first = true;
|
|
|
|
if (priv->force_aspect_ratio) {
|
|
GstVideoRectangle src_rect = { };
|
|
|
|
switch (priv->orientation) {
|
|
case GST_VIDEO_ORIENTATION_90R:
|
|
case GST_VIDEO_ORIENTATION_90L:
|
|
case GST_VIDEO_ORIENTATION_UL_LR:
|
|
case GST_VIDEO_ORIENTATION_UR_LL:
|
|
src_rect.w = GST_VIDEO_INFO_HEIGHT (&priv->display_info);
|
|
src_rect.h = GST_VIDEO_INFO_WIDTH (&priv->display_info);
|
|
break;
|
|
default:
|
|
src_rect.w = GST_VIDEO_INFO_WIDTH (&priv->display_info);
|
|
src_rect.h = GST_VIDEO_INFO_HEIGHT (&priv->display_info);
|
|
break;
|
|
}
|
|
|
|
gst_video_sink_center_rect (src_rect, dst_rect, &rst_rect, TRUE);
|
|
} else {
|
|
rst_rect = dst_rect;
|
|
}
|
|
|
|
priv->output_rect = rst_rect;
|
|
priv->dirty_rect.left = rst_rect.x;
|
|
priv->dirty_rect.top = rst_rect.y;
|
|
priv->dirty_rect.right = rst_rect.x + rst_rect.w;
|
|
priv->dirty_rect.bottom = rst_rect.y + rst_rect.h;
|
|
|
|
output_rect = priv->dirty_rect;
|
|
|
|
g_object_set (resource->conv, "dest-x", priv->output_rect.x,
|
|
"dest-y", priv->output_rect.y, "dest-width", priv->output_rect.w,
|
|
"dest-height", priv->output_rect.h, "hue", priv->hue,
|
|
"saturation", priv->saturation, "brightness", priv->brightness,
|
|
"contrast", priv->contrast, nullptr);
|
|
|
|
if (gst_d3d12_need_transform (priv->rotation_x, priv->rotation_y,
|
|
priv->rotation_z, priv->scale_x, priv->scale_y)) {
|
|
gst_d3d12_converter_apply_transform (resource->conv, priv->orientation,
|
|
priv->output_rect.w, priv->output_rect.h, priv->fov, priv->ortho,
|
|
priv->rotation_x, priv->rotation_y, priv->rotation_z,
|
|
priv->scale_x, priv->scale_y);
|
|
} else {
|
|
g_object_set (resource->conv,
|
|
"video-direction", priv->orientation, nullptr);
|
|
}
|
|
|
|
gst_d3d12_overlay_compositor_update_viewport (resource->comp,
|
|
&priv->output_rect);
|
|
}
|
|
|
|
priv->output_updated = FALSE;
|
|
}
|
|
|
|
gst_d3d12_overlay_compositor_upload (resource->comp, buffer);
|
|
|
|
GstD3D12CmdAlloc *gst_ca;
|
|
if (!gst_d3d12_cmd_alloc_pool_acquire (resource->ca_pool, &gst_ca)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't acquire command allocator");
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
auto ca = gst_d3d12_cmd_alloc_get_handle (gst_ca);
|
|
auto hr = ca->Reset ();
|
|
if (!gst_d3d12_result (hr, device)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't reset command list");
|
|
gst_d3d12_cmd_alloc_unref (gst_ca);
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
ComPtr < ID3D12GraphicsCommandList > cl;
|
|
if (!resource->cl) {
|
|
auto device_handle = gst_d3d12_device_get_device_handle (device);
|
|
hr = device_handle->CreateCommandList (0, D3D12_COMMAND_LIST_TYPE_DIRECT,
|
|
ca, nullptr, IID_PPV_ARGS (&cl));
|
|
if (!gst_d3d12_result (hr, device)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't create command list");
|
|
gst_d3d12_cmd_alloc_unref (gst_ca);
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
resource->cl = cl;
|
|
} else {
|
|
cl = resource->cl;
|
|
hr = cl->Reset (ca, nullptr);
|
|
if (!gst_d3d12_result (hr, device)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't reset command list");
|
|
gst_d3d12_cmd_alloc_unref (gst_ca);
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
}
|
|
|
|
GstD3D12FenceData *fence_data;
|
|
gst_d3d12_fence_data_pool_acquire (priv->fence_data_pool, &fence_data);
|
|
gst_d3d12_fence_data_push (fence_data, FENCE_NOTIFY_MINI_OBJECT (gst_ca));
|
|
|
|
auto mem = (GstD3D12Memory *) gst_buffer_peek_memory (swapbuf->backbuf, 0);
|
|
auto backbuf_texture = gst_d3d12_memory_get_resource_handle (mem);
|
|
ID3D12Resource *msaa_resource = nullptr;
|
|
GstBuffer *conv_outbuf = swapbuf->backbuf;
|
|
if (resource->msaa_buf) {
|
|
conv_outbuf = resource->msaa_buf;
|
|
mem = (GstD3D12Memory *) gst_buffer_peek_memory (conv_outbuf, 0);
|
|
msaa_resource = gst_d3d12_memory_get_resource_handle (mem);
|
|
/* MSAA resource must be render target state here already */
|
|
} else {
|
|
D3D12_RESOURCE_BARRIER barrier =
|
|
CD3DX12_RESOURCE_BARRIER::Transition (backbuf_texture,
|
|
D3D12_RESOURCE_STATE_COMMON,
|
|
D3D12_RESOURCE_STATE_RENDER_TARGET);
|
|
cl->ResourceBarrier (1, &barrier);
|
|
}
|
|
|
|
if (swapbuf->is_first || priv->overlay_mode != GST_D3D12_WINDOW_OVERLAY_NONE) {
|
|
FLOAT clear_color[4] = { 0, 0, 0, 1 };
|
|
auto rtv_heap = gst_d3d12_memory_get_render_target_view_heap (mem);
|
|
auto cpu_handle = GetCPUDescriptorHandleForHeapStart (rtv_heap);
|
|
cl->ClearRenderTargetView (cpu_handle, clear_color, 0, nullptr);
|
|
}
|
|
|
|
swapbuf->is_first = false;
|
|
|
|
auto cq = gst_d3d12_device_get_cmd_queue (device,
|
|
D3D12_COMMAND_LIST_TYPE_DIRECT);
|
|
auto cq_handle = gst_d3d12_cmd_queue_get_handle (cq);
|
|
if (!gst_d3d12_converter_convert_buffer (resource->conv,
|
|
buffer, conv_outbuf, fence_data, cl.Get (), TRUE)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't build convert command");
|
|
gst_d3d12_fence_data_unref (fence_data);
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
if (!gst_d3d12_overlay_compositor_draw (resource->comp,
|
|
conv_outbuf, fence_data, cl.Get ())) {
|
|
GST_ERROR_OBJECT (self, "Couldn't build overlay command");
|
|
gst_d3d12_fence_data_unref (fence_data);
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
D3D12_RESOURCE_STATES state_after = D3D12_RESOURCE_STATE_COMMON;
|
|
GstD3D12WindowOverlayMode selected_overlay_mode =
|
|
GST_D3D12_WINDOW_OVERLAY_NONE;
|
|
bool signal_with_lock = false;
|
|
bool set_d2d_target = false;
|
|
GstD3D12WindowOverlayMode overlay_mode = priv->overlay_mode;
|
|
if ((overlay_mode & GST_D3D12_WINDOW_OVERLAY_D3D12) != 0) {
|
|
selected_overlay_mode |= GST_D3D12_WINDOW_OVERLAY_D3D12;
|
|
state_after = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
|
}
|
|
|
|
if ((overlay_mode & GST_D3D12_WINDOW_OVERLAY_D3D11) ==
|
|
GST_D3D12_WINDOW_OVERLAY_D3D11 &&
|
|
resource->ensure_d3d11_target (swapbuf.get ())) {
|
|
selected_overlay_mode |= GST_D3D12_WINDOW_OVERLAY_D3D11;
|
|
signal_with_lock = true;
|
|
}
|
|
|
|
if ((overlay_mode & GST_D3D12_WINDOW_OVERLAY_D2D) ==
|
|
GST_D3D12_WINDOW_OVERLAY_D2D &&
|
|
(priv->display_format == DXGI_FORMAT_R8G8B8A8_UNORM ||
|
|
priv->display_format == DXGI_FORMAT_B8G8R8A8_UNORM) &&
|
|
resource->ensure_d2d_target (swapbuf.get ())) {
|
|
selected_overlay_mode |= GST_D3D12_WINDOW_OVERLAY_D2D;
|
|
set_d2d_target = true;
|
|
}
|
|
|
|
if (msaa_resource) {
|
|
std::vector < D3D12_RESOURCE_BARRIER > barriers;
|
|
barriers.push_back (CD3DX12_RESOURCE_BARRIER::Transition (msaa_resource,
|
|
D3D12_RESOURCE_STATE_RENDER_TARGET,
|
|
D3D12_RESOURCE_STATE_RESOLVE_SOURCE));
|
|
barriers.push_back (CD3DX12_RESOURCE_BARRIER::Transition (backbuf_texture,
|
|
D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_RESOLVE_DEST));
|
|
cl->ResourceBarrier (barriers.size (), barriers.data ());
|
|
|
|
cl->ResolveSubresource (backbuf_texture, 0, msaa_resource, 0,
|
|
priv->display_format);
|
|
|
|
barriers.clear ();
|
|
barriers.push_back (CD3DX12_RESOURCE_BARRIER::Transition (msaa_resource,
|
|
D3D12_RESOURCE_STATE_RESOLVE_SOURCE,
|
|
D3D12_RESOURCE_STATE_RENDER_TARGET));
|
|
barriers.push_back (CD3DX12_RESOURCE_BARRIER::Transition (backbuf_texture,
|
|
D3D12_RESOURCE_STATE_RESOLVE_DEST, state_after));
|
|
cl->ResourceBarrier (barriers.size (), barriers.data ());
|
|
} else if (state_after == D3D12_RESOURCE_STATE_COMMON) {
|
|
D3D12_RESOURCE_BARRIER barrier =
|
|
CD3DX12_RESOURCE_BARRIER::Transition (backbuf_texture,
|
|
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COMMON);
|
|
|
|
cl->ResourceBarrier (1, &barrier);
|
|
}
|
|
|
|
hr = cl->Close ();
|
|
if (!gst_d3d12_result (hr, device)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't close command list");
|
|
gst_d3d12_fence_data_unref (fence_data);
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
ID3D12CommandList *cmd_list[] = { cl.Get () };
|
|
hr = gst_d3d12_cmd_queue_execute_command_lists (cq,
|
|
1, cmd_list, &resource->fence_val);
|
|
if (!gst_d3d12_result (hr, device)) {
|
|
GST_ERROR_OBJECT (self, "Signal failed");
|
|
gst_d3d12_fence_data_unref (fence_data);
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
gst_d3d12_cmd_queue_set_notify (cq, resource->fence_val,
|
|
fence_data, (GDestroyNotify) gst_d3d12_fence_data_unref);
|
|
|
|
if (selected_overlay_mode != GST_D3D12_WINDOW_OVERLAY_NONE) {
|
|
D3D12_RECT viewport = priv->dirty_rect;
|
|
if (signal_with_lock)
|
|
gst_d3d12_device_11on12_lock (device);
|
|
|
|
if (set_d2d_target)
|
|
resource->context2d->SetTarget (swapbuf->d2d_target.Get ());
|
|
|
|
g_signal_emit (self, d3d12_window_signals[SIGNAL_OVERLAY], 0,
|
|
cq_handle, backbuf_texture, resource->device11on12.Get (),
|
|
swapbuf->wrapped_resource.Get (), resource->context2d.Get (),
|
|
&viewport);
|
|
|
|
if (signal_with_lock)
|
|
gst_d3d12_device_11on12_unlock (device);
|
|
}
|
|
|
|
if (state_after != D3D12_RESOURCE_STATE_COMMON) {
|
|
if (!gst_d3d12_cmd_alloc_pool_acquire (resource->ca_pool, &gst_ca)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't acquire command allocator");
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
ca = gst_d3d12_cmd_alloc_get_handle (gst_ca);
|
|
hr = ca->Reset ();
|
|
if (!gst_d3d12_result (hr, device)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't reset command allocator");
|
|
gst_d3d12_cmd_alloc_unref (gst_ca);
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
hr = cl->Reset (ca, nullptr);
|
|
if (!gst_d3d12_result (hr, device)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't reset command list");
|
|
gst_d3d12_cmd_alloc_unref (gst_ca);
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
gst_d3d12_fence_data_pool_acquire (priv->fence_data_pool, &fence_data);
|
|
gst_d3d12_fence_data_push (fence_data, FENCE_NOTIFY_MINI_OBJECT (gst_ca));
|
|
|
|
D3D12_RESOURCE_BARRIER barrier =
|
|
CD3DX12_RESOURCE_BARRIER::Transition (backbuf_texture,
|
|
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COMMON);
|
|
cl->ResourceBarrier (1, &barrier);
|
|
hr = cl->Close ();
|
|
if (!gst_d3d12_result (hr, device)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't close command list");
|
|
gst_d3d12_fence_data_unref (fence_data);
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
hr = gst_d3d12_cmd_queue_execute_command_lists (cq,
|
|
1, cmd_list, &resource->fence_val);
|
|
if (!gst_d3d12_result (hr, device)) {
|
|
GST_ERROR_OBJECT (self, "Signal failed");
|
|
gst_d3d12_fence_data_unref (fence_data);
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
gst_d3d12_cmd_queue_set_notify (cq, resource->fence_val,
|
|
fence_data, (GDestroyNotify) gst_d3d12_fence_data_unref);
|
|
}
|
|
|
|
return GST_FLOW_OK;
|
|
}
|
|
|
|
void
|
|
gst_d3d12_window_expose (GstD3D12Window * window)
|
|
{
|
|
auto priv = window->priv;
|
|
auto proxy = priv->proxy.lock ();
|
|
|
|
if (proxy)
|
|
proxy->expose ();
|
|
}
|
|
|
|
GstFlowReturn
|
|
gst_d3d12_window_set_buffer (GstD3D12Window * window, GstBuffer * buffer)
|
|
{
|
|
auto priv = window->priv;
|
|
auto proxy = priv->proxy.lock ();
|
|
|
|
if (!proxy) {
|
|
GST_WARNING_OBJECT (window, "Window was closed");
|
|
return GST_D3D12_WINDOW_FLOW_CLOSED;
|
|
}
|
|
|
|
return proxy->set_buffer (buffer);
|
|
}
|
|
|
|
GstFlowReturn
|
|
gst_d3d12_window_present (GstD3D12Window * window)
|
|
{
|
|
auto priv = window->priv;
|
|
auto proxy = priv->proxy.lock ();
|
|
if (!proxy) {
|
|
GST_WARNING_OBJECT (window, "Window was closed");
|
|
return GST_D3D12_WINDOW_FLOW_CLOSED;
|
|
}
|
|
|
|
return proxy->present ();
|
|
}
|
|
|
|
guintptr
|
|
gst_d3d12_window_get_window_handle (GstD3D12Window * window)
|
|
{
|
|
auto priv = window->priv;
|
|
auto proxy = priv->proxy.lock ();
|
|
if (!proxy)
|
|
return 0;
|
|
|
|
return (guintptr) proxy->get_window_handle ();
|
|
}
|
|
|
|
void
|
|
gst_d3d12_window_set_render_rect (GstD3D12Window * window,
|
|
const GstVideoRectangle * rect)
|
|
{
|
|
auto priv = window->priv;
|
|
|
|
{
|
|
std::lock_guard < std::recursive_mutex > lk (priv->lock);
|
|
priv->render_rect = *rect;
|
|
}
|
|
|
|
auto proxy = priv->proxy.lock ();
|
|
if (proxy)
|
|
proxy->update_render_rect ();
|
|
}
|
|
|
|
void
|
|
gst_d3d12_window_get_render_rect (GstD3D12Window * window,
|
|
GstVideoRectangle * rect)
|
|
{
|
|
auto priv = window->priv;
|
|
|
|
std::lock_guard < std::recursive_mutex > lk (priv->lock);
|
|
*rect = priv->render_rect;
|
|
}
|
|
|
|
void
|
|
gst_d3d12_window_set_force_aspect_ratio (GstD3D12Window * window,
|
|
gboolean force_aspect_ratio)
|
|
{
|
|
auto priv = window->priv;
|
|
bool updated = false;
|
|
|
|
{
|
|
std::lock_guard < std::recursive_mutex > lk (priv->lock);
|
|
if (priv->force_aspect_ratio != force_aspect_ratio) {
|
|
priv->force_aspect_ratio = force_aspect_ratio;
|
|
priv->output_updated = TRUE;
|
|
updated = true;
|
|
}
|
|
}
|
|
|
|
if (updated)
|
|
gst_d3d12_window_set_buffer (window, nullptr);
|
|
}
|
|
|
|
void
|
|
gst_d3d12_window_set_enable_navigation_events (GstD3D12Window * window,
|
|
gboolean enable)
|
|
{
|
|
auto priv = window->priv;
|
|
priv->enable_navigation = enable;
|
|
}
|
|
|
|
gboolean
|
|
gst_d3d12_window_get_navigation_events_enabled (GstD3D12Window * window)
|
|
{
|
|
auto priv = window->priv;
|
|
return priv->enable_navigation;
|
|
}
|
|
|
|
void
|
|
gst_d3d12_window_set_orientation (GstD3D12Window * window, gboolean immediate,
|
|
GstVideoOrientationMethod orientation, gfloat fov, gboolean ortho,
|
|
gfloat rotation_x, gfloat rotation_y, gfloat rotation_z,
|
|
gfloat scale_x, gfloat scale_y)
|
|
{
|
|
auto priv = window->priv;
|
|
bool updated = false;
|
|
|
|
{
|
|
std::lock_guard < std::recursive_mutex > lk (priv->lock);
|
|
if (priv->orientation != orientation || priv->fov != fov
|
|
|| priv->ortho != ortho
|
|
|| priv->rotation_x != rotation_x || priv->rotation_y != rotation_y
|
|
|| priv->rotation_z != rotation_z || priv->scale_x != scale_x
|
|
|| priv->scale_y != scale_y) {
|
|
priv->orientation = orientation;
|
|
priv->fov = fov;
|
|
priv->ortho = ortho;
|
|
priv->rotation_x = rotation_x;
|
|
priv->rotation_y = rotation_y;
|
|
priv->rotation_z = rotation_z;
|
|
priv->scale_x = scale_x;
|
|
priv->scale_y = scale_y;
|
|
priv->output_updated = TRUE;
|
|
updated = true;
|
|
}
|
|
}
|
|
|
|
if (updated && immediate)
|
|
gst_d3d12_window_set_buffer (window, nullptr);
|
|
}
|
|
|
|
void
|
|
gst_d3d12_window_set_title (GstD3D12Window * window, const gchar * title)
|
|
{
|
|
auto priv = window->priv;
|
|
wchar_t *wtitle = nullptr;
|
|
|
|
if (title)
|
|
wtitle = (wchar_t *) g_utf8_to_utf16 (title, -1, nullptr, nullptr, nullptr);
|
|
|
|
std::lock_guard < std::recursive_mutex > lk (priv->lock);
|
|
if (!wtitle) {
|
|
priv->title.clear ();
|
|
} else {
|
|
priv->title = wtitle;
|
|
}
|
|
g_free (wtitle);
|
|
}
|
|
|
|
GstD3D12Window *
|
|
gst_d3d12_window_new (void)
|
|
{
|
|
auto self = (GstD3D12Window *) g_object_new (GST_TYPE_D3D12_WINDOW, nullptr);
|
|
gst_object_ref_sink (self);
|
|
|
|
auto server = HwndServer::get_instance ();
|
|
server->register_window (self);
|
|
|
|
return self;
|
|
}
|
|
|
|
void
|
|
gst_d3d12_window_invalidate (GstD3D12Window * window)
|
|
{
|
|
auto server = HwndServer::get_instance ();
|
|
server->unregister_window (window);
|
|
}
|
|
|
|
gboolean
|
|
gst_d3d12_window_is_closed (GstD3D12Window * window)
|
|
{
|
|
auto priv = window->priv;
|
|
|
|
if (priv->proxy.expired ())
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
gst_d3d12_window_enable_fullscreen_on_alt_enter (GstD3D12Window * window,
|
|
gboolean enable)
|
|
{
|
|
auto priv = window->priv;
|
|
auto proxy = priv->proxy.lock ();
|
|
|
|
priv->fullscreen_on_alt_enter = enable;
|
|
if (proxy)
|
|
proxy->set_fullscreen_on_alt_enter (enable);
|
|
}
|
|
|
|
void
|
|
gst_d3d12_window_set_fullscreen (GstD3D12Window * window, gboolean enable)
|
|
{
|
|
auto priv = window->priv;
|
|
auto proxy = priv->proxy.lock ();
|
|
|
|
priv->requested_fullscreen = enable;
|
|
if (proxy)
|
|
proxy->toggle_fullscreen (enable);
|
|
}
|
|
|
|
void
|
|
gst_d3d12_window_set_msaa (GstD3D12Window * window, GstD3D12MSAAMode msaa)
|
|
{
|
|
auto priv = window->priv;
|
|
auto prev_val = priv->msaa.exchange (msaa);
|
|
if (prev_val != msaa)
|
|
gst_d3d12_window_resize_buffer (window);
|
|
}
|
|
|
|
void
|
|
gst_d3d12_window_get_msaa (GstD3D12Window * window, GstD3D12MSAAMode & msaa)
|
|
{
|
|
auto priv = window->priv;
|
|
msaa = priv->msaa;
|
|
}
|
|
|
|
void
|
|
gst_d3d12_window_set_overlay_mode (GstD3D12Window * window,
|
|
GstD3D12WindowOverlayMode mode)
|
|
{
|
|
auto priv = window->priv;
|
|
priv->overlay_mode = mode;
|
|
}
|
|
|
|
void
|
|
gst_d3d12_window_get_create_params (GstD3D12Window * window,
|
|
std::wstring & title, GstVideoRectangle * rect, int &display_width,
|
|
int &display_height, GstVideoOrientationMethod & orientation)
|
|
{
|
|
auto priv = window->priv;
|
|
|
|
std::lock_guard < std::recursive_mutex > lk (priv->lock);
|
|
if (priv->title.empty ())
|
|
title = L"Direct3D12 Renderer";
|
|
else
|
|
title = priv->title;
|
|
|
|
*rect = priv->render_rect;
|
|
|
|
display_width = priv->display_width;
|
|
display_height = priv->display_height;
|
|
orientation = priv->orientation;
|
|
}
|
|
|
|
void
|
|
gst_d3d12_window_get_mouse_pos_info (GstD3D12Window * window,
|
|
GstVideoRectangle * out_rect, int &input_width, int &input_height,
|
|
GstVideoOrientationMethod & orientation)
|
|
{
|
|
auto priv = window->priv;
|
|
std::lock_guard < std::recursive_mutex > lk (priv->lock);
|
|
|
|
*out_rect = priv->output_rect;
|
|
input_width = priv->input_info.width;
|
|
input_height = priv->input_info.height;
|
|
orientation = priv->orientation;
|
|
}
|
|
|
|
static gboolean
|
|
gst_d3d12_window_update_color_balance (GstD3D12Window * self,
|
|
gboolean immediate, gdouble * prev, gdouble value)
|
|
{
|
|
auto priv = self->priv;
|
|
gboolean updated = FALSE;
|
|
|
|
{
|
|
std::lock_guard < std::recursive_mutex > lk (priv->lock);
|
|
if (*prev != value) {
|
|
*prev = value;
|
|
priv->output_updated = TRUE;
|
|
updated = TRUE;
|
|
}
|
|
}
|
|
|
|
if (updated && immediate)
|
|
gst_d3d12_window_set_buffer (self, nullptr);
|
|
|
|
return updated;
|
|
}
|
|
|
|
gboolean
|
|
gst_d3d12_window_set_hue (GstD3D12Window * window, gboolean immediate,
|
|
gdouble value)
|
|
{
|
|
auto priv = window->priv;
|
|
return gst_d3d12_window_update_color_balance (window,
|
|
immediate, &priv->hue, value);
|
|
}
|
|
|
|
gdouble
|
|
gst_d3d12_window_get_hue (GstD3D12Window * window)
|
|
{
|
|
auto priv = window->priv;
|
|
std::lock_guard < std::recursive_mutex > lk (priv->lock);
|
|
return priv->hue;
|
|
}
|
|
|
|
gboolean
|
|
gst_d3d12_window_set_saturation (GstD3D12Window * window, gboolean immediate,
|
|
gdouble value)
|
|
{
|
|
auto priv = window->priv;
|
|
return gst_d3d12_window_update_color_balance (window,
|
|
immediate, &priv->saturation, value);
|
|
}
|
|
|
|
gdouble
|
|
gst_d3d12_window_get_saturation (GstD3D12Window * window)
|
|
{
|
|
auto priv = window->priv;
|
|
std::lock_guard < std::recursive_mutex > lk (priv->lock);
|
|
return priv->saturation;
|
|
}
|
|
|
|
gboolean
|
|
gst_d3d12_window_set_brightness (GstD3D12Window * window, gboolean immediate,
|
|
gdouble value)
|
|
{
|
|
auto priv = window->priv;
|
|
return gst_d3d12_window_update_color_balance (window,
|
|
immediate, &priv->brightness, value);
|
|
}
|
|
|
|
gdouble
|
|
gst_d3d12_window_get_brightness (GstD3D12Window * window)
|
|
{
|
|
auto priv = window->priv;
|
|
std::lock_guard < std::recursive_mutex > lk (priv->lock);
|
|
return priv->brightness;
|
|
}
|
|
|
|
gboolean
|
|
gst_d3d12_window_set_contrast (GstD3D12Window * window, gboolean immediate,
|
|
gdouble value)
|
|
{
|
|
auto priv = window->priv;
|
|
return gst_d3d12_window_update_color_balance (window,
|
|
immediate, &priv->contrast, value);
|
|
}
|
|
|
|
gdouble
|
|
gst_d3d12_window_get_contrast (GstD3D12Window * window)
|
|
{
|
|
auto priv = window->priv;
|
|
std::lock_guard < std::recursive_mutex > lk (priv->lock);
|
|
return priv->contrast;
|
|
}
|