gstreamer/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12window.cpp
Seungha Yang dc414415a5 d3d12videosink: Add support for GstColorBalance interface
... and adding hue, saturation, brightness, and contrast properties

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7817>
2024-11-03 21:33:37 +00:00

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;
}