gstreamer/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12converter.cpp
Seungha Yang 0577d1c9de d3d12: Reduce shader visible descriptor size
Shader visible descriptors occupy GPU resource and there are hardware
limits. Thus, in order to minimize the amount of shader visible heaps,
only non shader visible descriptor heap (staging) will be held by d3d12memory.
Then converter will copy the staging descriptor to shader visible
descriptor heap per draw.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5875>
2024-01-02 13:02:47 +00:00

2261 lines
72 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 "gstd3d12.h"
#include "gstd3d12converter-builder.h"
#include <directx/d3dx12.h>
#include <wrl.h>
#include <string.h>
#include <math.h>
#include <map>
#include <vector>
#include <memory>
#include <queue>
#ifndef HAVE_DIRECTX_MATH_SIMD
#define _XM_NO_INTRINSICS_
#endif
#include <DirectXMath.h>
GST_DEBUG_CATEGORY (gst_d3d12_converter_debug);
#define GST_CAT_DEFAULT gst_d3d12_converter_debug
GType
gst_d3d12_converter_sampler_filter_get_type (void)
{
static GType filter_type = 0;
static const GEnumValue filter_types[] = {
{D3D12_FILTER_MIN_MAG_MIP_POINT,
"D3D12_FILTER_MIN_MAG_MIP_POINT", "min-mag-mip-point"},
{D3D12_FILTER_MIN_LINEAR_MAG_MIP_POINT,
"D3D12_FILTER_MIN_LINEAR_MAG_MIP_POINT", "min-linear-mag-mip-point"},
{D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT,
"D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT", "min-mag-linear-mip-point"},
{D3D12_FILTER_ANISOTROPIC, "D3D12_FILTER_ANISOTROPIC", "anisotropic"},
{0, nullptr, nullptr},
};
GST_D3D12_CALL_ONCE_BEGIN {
filter_type = g_enum_register_static ("GstD3D12ConverterSamplerFilter",
filter_types);
} GST_D3D12_CALL_ONCE_END;
return filter_type;
}
GType
gst_d3d12_converter_alpha_mode_get_type (void)
{
static GType type = 0;
static const GEnumValue alpha_mode[] = {
{GST_D3D12_CONVERTER_ALPHA_MODE_UNSPECIFIED,
"GST_D3D12_CONVERTER_ALPHA_MODE_UNSPECIFIED", "unspecified"},
{GST_D3D12_CONVERTER_ALPHA_MODE_PREMULTIPLIED,
"GST_D3D12_CONVERTER_ALPHA_MODE_PREMULTIPLIED", "premultiplied"},
{GST_D3D12_CONVERTER_ALPHA_MODE_STRAIGHT,
"GST_D3D12_CONVERTER_ALPHA_MODE_STRAIGHT", "straight"},
{0, nullptr, nullptr},
};
GST_D3D12_CALL_ONCE_BEGIN {
type = g_enum_register_static ("GstD3D12ConverterAlphaMode", alpha_mode);
} GST_D3D12_CALL_ONCE_END;
return type;
}
/* *INDENT-OFF* */
using namespace Microsoft::WRL;
using namespace DirectX;
/* *INDENT-ON* */
#define GAMMA_LUT_SIZE 4096
#define DEFAULT_BUFFER_COUNT 2
static const WORD g_indices[6] = { 0, 1, 2, 3, 0, 2 };
struct PSColorSpace
{
/* + 1 for 16bytes alignment */
FLOAT coeffX[4];
FLOAT coeffY[4];
FLOAT coeffZ[4];
FLOAT offset[4];
FLOAT min[4];
FLOAT max[4];
};
struct PSConstBuffer
{
PSColorSpace preCoeff;
PSColorSpace postCoeff;
PSColorSpace primariesCoeff;
};
struct VertexData
{
struct
{
FLOAT x;
FLOAT y;
FLOAT z;
} position;
struct
{
FLOAT u;
FLOAT v;
} texture;
};
struct GammaLut
{
guint16 lut[GAMMA_LUT_SIZE];
};
/* *INDENT-OFF* */
typedef std::shared_ptr<GammaLut> GammaLutPtr;
static const XMFLOAT4X4A g_matrix_identity = XMFLOAT4X4A (
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
static const XMFLOAT4X4A g_matrix_90r = XMFLOAT4X4A (
0.0f, -1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
static const XMFLOAT4X4A g_matrix_180 = XMFLOAT4X4A (
-1.0f, 0.0f, 0.0f, 0.0f,
0.0f, -1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
static const XMFLOAT4X4A g_matrix_90l = XMFLOAT4X4A (
0.0f, 1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
static const XMFLOAT4X4A g_matrix_horiz = XMFLOAT4X4A (
-1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
static const XMFLOAT4X4A g_matrix_vert = XMFLOAT4X4A (
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, -1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
static const XMFLOAT4X4A g_matrix_ul_lr = XMFLOAT4X4A (
0.0f, -1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
static const XMFLOAT4X4A g_matrix_ur_ll = XMFLOAT4X4A (
0.0f, 1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
/* *INDENT-ON* */
enum
{
PROP_0,
PROP_SRC_X,
PROP_SRC_Y,
PROP_SRC_WIDTH,
PROP_SRC_HEIGHT,
PROP_DEST_X,
PROP_DEST_Y,
PROP_DEST_WIDTH,
PROP_DEST_HEIGHT,
PROP_ALPHA,
PROP_FILL_BORDER,
PROP_BORDER_COLOR,
PROP_VIDEO_DIRECTION,
};
/* *INDENT-OFF* */
struct QuadData
{
D3D12_GRAPHICS_PIPELINE_STATE_DESC desc;
ComPtr<ID3D12PipelineState> pso;
guint num_rtv;
};
#define STATE_VERTEX_AND_INDEX \
(D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER | D3D12_RESOURCE_STATE_INDEX_BUFFER)
struct ConverterUploadData
{
ComPtr<ID3D12Resource> vertex_index_upload;
ComPtr<ID3D12Resource> ps_const_upload;
ComPtr<ID3D12Resource> gamma_dec_lut_upload;
ComPtr<ID3D12Resource> gamma_enc_lut_upload;
};
static void
converter_upload_data_free (ConverterUploadData * data)
{
if (data)
delete data;
}
struct _GstD3D12ConverterPrivate
{
_GstD3D12ConverterPrivate ()
{
transform = g_matrix_identity;
custom_transform = g_matrix_identity;
blend_desc = CD3DX12_BLEND_DESC (D3D12_DEFAULT);
for (guint i = 0; i < 4; i++)
blend_factor[i] = 1.0f;
}
~_GstD3D12ConverterPrivate ()
{
if (fallback_pool) {
gst_buffer_pool_set_active (fallback_pool, FALSE);
gst_clear_object (&fallback_pool);
}
converter_upload_data_free (upload_data);
gst_clear_object (&srv_heap_pool);
}
GstVideoInfo in_info;
GstVideoInfo out_info;
GstD3D12Format in_d3d12_format;
GstD3D12Format out_d3d12_format;
CONVERT_TYPE convert_type = CONVERT_TYPE::IDENTITY;
D3D12_VIEWPORT viewport[GST_VIDEO_MAX_PLANES];
D3D12_RECT scissor_rect[GST_VIDEO_MAX_PLANES];
D3D12_BLEND_DESC blend_desc;
FLOAT blend_factor[4];
gboolean update_pso = FALSE;
GstVideoInfo fallback_pool_info;
GstBufferPool *fallback_pool = nullptr;
ConverterRootSignaturePtr crs;
ComPtr<ID3D12RootSignature> rs;
D3D12_VERTEX_BUFFER_VIEW vbv;
D3D12_INDEX_BUFFER_VIEW idv;
ComPtr<ID3D12Resource> vertex_index_buf;
ComPtr<ID3D12Resource> ps_const_buf;
ComPtr<ID3D12Resource> gamma_dec_lut;
ComPtr<ID3D12Resource> gamma_enc_lut;
D3D12_PLACED_SUBRESOURCE_FOOTPRINT gamma_lut_layout;
ComPtr<ID3D12DescriptorHeap> gamma_lut_heap;
std::vector<QuadData> quad_data;
GstD3D12DescriptorPool *srv_heap_pool = nullptr;
ConverterUploadData *upload_data = nullptr;
bool is_first = true;
guint srv_inc_size;
guint rtv_inc_size;
std::vector<D3D12_RESOURCE_BARRIER> barriers;
std::vector<D3D12_CPU_DESCRIPTOR_HANDLE> rtv_handles;
guint64 input_texture_width;
guint input_texture_height;
gboolean update_src_rect = FALSE;
gboolean update_dest_rect = FALSE;
gboolean update_transform = FALSE;
XMFLOAT4X4A transform;
XMFLOAT4X4A custom_transform;
PSConstBuffer const_data;
gboolean clear_background = FALSE;
FLOAT clear_color[4][4];
GstD3D12ColorMatrix clear_color_matrix;
GstVideoOrientationMethod video_direction;
std::mutex prop_lock;
/* properties */
gint src_x = 0;
gint src_y = 0;
gint src_width = 0;
gint src_height = 0;
gint dest_x = 0;
gint dest_y = 0;
gint dest_width = 0;
gint dest_height = 0;
FLOAT alpha = 1.0;
gboolean fill_border = FALSE;
guint64 border_color = 0;
GstD3D12ConverterAlphaMode src_alpha_mode =
GST_D3D12_CONVERTER_ALPHA_MODE_UNSPECIFIED;
GstD3D12ConverterAlphaMode dst_alpha_mode =
GST_D3D12_CONVERTER_ALPHA_MODE_UNSPECIFIED;
};
/* *INDENT-ON* */
static void gst_d3d12_converter_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_d3d12_converter_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static void gst_d3d12_converter_finalize (GObject * object);
static void
gst_d3d12_converter_calculate_border_color (GstD3D12Converter * self);
#define gst_d3d12_converter_parent_class parent_class
G_DEFINE_TYPE (GstD3D12Converter, gst_d3d12_converter, GST_TYPE_OBJECT);
static void
gst_d3d12_converter_class_init (GstD3D12ConverterClass * klass)
{
auto object_class = G_OBJECT_CLASS (klass);
auto param_flags = (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
object_class->set_property = gst_d3d12_converter_set_property;
object_class->get_property = gst_d3d12_converter_get_property;
object_class->finalize = gst_d3d12_converter_finalize;
g_object_class_install_property (object_class, PROP_SRC_X,
g_param_spec_int ("src-x", "Src-X",
"Source x poisition to start conversion", G_MININT, G_MAXINT, 0,
param_flags));
g_object_class_install_property (object_class, PROP_SRC_Y,
g_param_spec_int ("src-y", "Src-Y",
"Source y poisition to start conversion", G_MININT, G_MAXINT, 0,
param_flags));
g_object_class_install_property (object_class, PROP_SRC_WIDTH,
g_param_spec_int ("src-width", "Src-Width",
"Source width to convert", 0, G_MAXINT, 0, param_flags));
g_object_class_install_property (object_class, PROP_SRC_HEIGHT,
g_param_spec_int ("src-height", "Src-Height",
"Source height to convert", 0, G_MAXINT, 0, param_flags));
g_object_class_install_property (object_class, PROP_DEST_X,
g_param_spec_int ("dest-x", "Dest-X",
"x poisition in the destination frame", G_MININT, G_MAXINT, 0,
param_flags));
g_object_class_install_property (object_class, PROP_DEST_Y,
g_param_spec_int ("dest-y", "Dest-Y",
"y poisition in the destination frame", G_MININT, G_MAXINT, 0,
param_flags));
g_object_class_install_property (object_class, PROP_DEST_WIDTH,
g_param_spec_int ("dest-width", "Dest-Width",
"Width in the destination frame", 0, G_MAXINT, 0, param_flags));
g_object_class_install_property (object_class, PROP_DEST_HEIGHT,
g_param_spec_int ("dest-height", "Dest-Height",
"Height in the destination frame", 0, G_MAXINT, 0, param_flags));
g_object_class_install_property (object_class, PROP_ALPHA,
g_param_spec_double ("alpha", "Alpha",
"The alpha color value to use", 0, 1.0, 1.0, param_flags));
g_object_class_install_property (object_class, PROP_FILL_BORDER,
g_param_spec_boolean ("fill-border", "Fill border",
"Fill border with \"border-color\" if destination rectangle does not "
"fill the complete destination image", FALSE, param_flags));
g_object_class_install_property (object_class, PROP_BORDER_COLOR,
g_param_spec_uint64 ("border-color", "Border Color",
"ARGB representation of the border color to use",
0, G_MAXUINT64, 0xffff000000000000, param_flags));
g_object_class_install_property (object_class, PROP_VIDEO_DIRECTION,
g_param_spec_enum ("video-direction", "Video Direction",
"Video direction", GST_TYPE_VIDEO_ORIENTATION_METHOD,
GST_VIDEO_ORIENTATION_IDENTITY, param_flags));
GST_DEBUG_CATEGORY_INIT (gst_d3d12_converter_debug,
"d3d12converter", 0, "d3d12converter");
}
static void
gst_d3d12_converter_init (GstD3D12Converter * self)
{
self->priv = new GstD3D12ConverterPrivate ();
}
static void
gst_d3d12_converter_finalize (GObject * object)
{
auto self = GST_D3D12_CONVERTER (object);
delete self->priv;
gst_clear_object (&self->device);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
update_src_rect (GstD3D12Converter * self, gint * old_val,
const GValue * new_val)
{
auto priv = self->priv;
gint tmp;
tmp = g_value_get_int (new_val);
if (tmp != *old_val) {
priv->update_src_rect = TRUE;
*old_val = tmp;
}
}
static void
update_dest_rect (GstD3D12Converter * self, gint * old_val,
const GValue * new_val)
{
auto priv = self->priv;
gint tmp;
tmp = g_value_get_int (new_val);
if (tmp != *old_val) {
priv->update_dest_rect = TRUE;
*old_val = tmp;
}
}
static void
gst_d3d12_converter_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
auto self = GST_D3D12_CONVERTER (object);
auto priv = self->priv;
std::lock_guard < std::mutex > lk (priv->prop_lock);
switch (prop_id) {
case PROP_SRC_X:
update_src_rect (self, &priv->src_x, value);
break;
case PROP_SRC_Y:
update_src_rect (self, &priv->src_y, value);
break;
case PROP_SRC_WIDTH:
update_src_rect (self, &priv->src_width, value);
break;
case PROP_SRC_HEIGHT:
update_src_rect (self, &priv->src_height, value);
break;
case PROP_DEST_X:
update_dest_rect (self, &priv->dest_x, value);
break;
case PROP_DEST_Y:
update_dest_rect (self, &priv->dest_y, value);
break;
case PROP_DEST_WIDTH:
update_dest_rect (self, &priv->dest_width, value);
break;
case PROP_DEST_HEIGHT:
update_dest_rect (self, &priv->dest_height, value);
break;
case PROP_ALPHA:
priv->alpha = g_value_get_double (value);
break;
case PROP_FILL_BORDER:{
gboolean fill_border = g_value_get_boolean (value);
if (fill_border != priv->fill_border) {
priv->update_dest_rect = TRUE;
priv->fill_border = fill_border;
}
break;
}
case PROP_BORDER_COLOR:{
guint64 border_color = g_value_get_uint64 (value);
if (border_color != priv->border_color) {
priv->border_color = border_color;
gst_d3d12_converter_calculate_border_color (self);
}
break;
}
case PROP_VIDEO_DIRECTION:{
GstVideoOrientationMethod video_direction =
(GstVideoOrientationMethod) g_value_get_enum (value);
if (video_direction != priv->video_direction) {
priv->video_direction = video_direction;
priv->update_transform = TRUE;
}
break;
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_d3d12_converter_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
auto self = GST_D3D12_CONVERTER (object);
auto priv = self->priv;
std::lock_guard < std::mutex > lk (priv->prop_lock);
switch (prop_id) {
case PROP_SRC_X:
g_value_set_int (value, priv->src_x);
break;
case PROP_SRC_Y:
g_value_set_int (value, priv->src_y);
break;
case PROP_SRC_WIDTH:
g_value_set_int (value, priv->src_width);
break;
case PROP_SRC_HEIGHT:
g_value_set_int (value, priv->src_height);
break;
case PROP_DEST_X:
g_value_set_int (value, priv->dest_x);
break;
case PROP_DEST_Y:
g_value_set_int (value, priv->dest_y);
break;
case PROP_DEST_WIDTH:
g_value_set_int (value, priv->dest_width);
break;
case PROP_DEST_HEIGHT:
g_value_set_int (value, priv->dest_height);
break;
case PROP_ALPHA:
g_value_set_double (value, priv->alpha);
break;
case PROP_FILL_BORDER:
g_value_set_boolean (value, priv->fill_border);
break;
case PROP_BORDER_COLOR:
g_value_set_uint64 (value, priv->border_color);
break;
case PROP_VIDEO_DIRECTION:
g_value_set_enum (value, priv->video_direction);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static GammaLutPtr
gst_d3d12_converter_get_gamma_dec_table (GstVideoTransferFunction func)
{
static std::mutex lut_lock;
static std::map < GstVideoTransferFunction, GammaLutPtr > g_gamma_dec_table;
std::lock_guard < std::mutex > lk (lut_lock);
auto lut = g_gamma_dec_table.find (func);
if (lut != g_gamma_dec_table.end ())
return lut->second;
const gdouble scale = (gdouble) 1 / (GAMMA_LUT_SIZE - 1);
auto table = std::make_shared < GammaLut > ();
for (guint i = 0; i < GAMMA_LUT_SIZE; i++) {
gdouble val = gst_video_transfer_function_decode (func, i * scale);
val = rint (val * 65535);
val = CLAMP (val, 0, 65535);
table->lut[i] = (guint16) val;
}
g_gamma_dec_table[func] = table;
return table;
}
static GammaLutPtr
gst_d3d12_converter_get_gamma_enc_table (GstVideoTransferFunction func)
{
static std::mutex lut_lock;
static std::map < GstVideoTransferFunction, GammaLutPtr > g_gamma_enc_table;
std::lock_guard < std::mutex > lk (lut_lock);
auto lut = g_gamma_enc_table.find (func);
if (lut != g_gamma_enc_table.end ())
return lut->second;
const gdouble scale = (gdouble) 1 / (GAMMA_LUT_SIZE - 1);
auto table = std::make_shared < GammaLut > ();
for (guint i = 0; i < GAMMA_LUT_SIZE; i++) {
gdouble val = gst_video_transfer_function_encode (func, i * scale);
val = rint (val * 65535);
val = CLAMP (val, 0, 65535);
table->lut[i] = (guint16) val;
}
g_gamma_enc_table[func] = table;
return table;
}
static gboolean
gst_d3d12_converter_setup_resource (GstD3D12Converter * self,
const GstVideoInfo * in_info, const GstVideoInfo * out_info,
D3D12_FILTER sampler_filter)
{
auto priv = self->priv;
HRESULT hr;
VertexData vertex_data[4];
auto device = gst_d3d12_device_get_device_handle (self->device);
priv->srv_inc_size = device->GetDescriptorHandleIncrementSize
(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
priv->rtv_inc_size = device->GetDescriptorHandleIncrementSize
(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
ComPtr < ID3DBlob > rs_blob;
priv->crs =
gst_d3d12_get_converter_root_signature (self->device,
GST_VIDEO_INFO_FORMAT (in_info), priv->convert_type, sampler_filter);
if (!priv->crs) {
GST_ERROR_OBJECT (self, "Couldn't get root signature blob");
return FALSE;
}
priv->crs->GetBlob (&rs_blob);
hr = device->CreateRootSignature (0, rs_blob->GetBufferPointer (),
rs_blob->GetBufferSize (), IID_PPV_ARGS (&priv->rs));
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create root signature");
return FALSE;
}
auto psblob_list =
gst_d3d12_get_converter_pixel_shader_blob (GST_VIDEO_INFO_FORMAT
(in_info), GST_VIDEO_INFO_FORMAT (out_info),
priv->src_alpha_mode == GST_D3D12_CONVERTER_ALPHA_MODE_PREMULTIPLIED,
priv->dst_alpha_mode == GST_D3D12_CONVERTER_ALPHA_MODE_PREMULTIPLIED,
priv->convert_type);
if (psblob_list.empty ()) {
GST_ERROR_OBJECT (self, "Couldn't get pixel shader blob");
return FALSE;
}
D3D12_SHADER_BYTECODE vs_blob;
D3D12_INPUT_ELEMENT_DESC input_desc[2];
hr = gst_d3d12_get_converter_vertex_shader_blob (&vs_blob, input_desc);
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't get vertex shader blob");
return FALSE;
}
std::queue < DXGI_FORMAT > rtv_formats;
for (guint i = 0; i < 4; i++) {
auto format = priv->out_d3d12_format.resource_format[i];
if (format == DXGI_FORMAT_UNKNOWN)
break;
rtv_formats.push (format);
}
priv->quad_data.resize (psblob_list.size ());
for (size_t i = 0; i < psblob_list.size (); i++) {
D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc = { };
pso_desc.pRootSignature = priv->rs.Get ();
pso_desc.VS = vs_blob;
pso_desc.PS = psblob_list[i].bytecode;
pso_desc.BlendState = priv->blend_desc;
pso_desc.SampleMask = UINT_MAX;
pso_desc.RasterizerState = CD3DX12_RASTERIZER_DESC (D3D12_DEFAULT);
pso_desc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
pso_desc.DepthStencilState.DepthEnable = FALSE;
pso_desc.DepthStencilState.StencilEnable = FALSE;
pso_desc.InputLayout.pInputElementDescs = input_desc;
pso_desc.InputLayout.NumElements = G_N_ELEMENTS (input_desc);
pso_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
pso_desc.NumRenderTargets = psblob_list[i].num_rtv;
for (UINT j = 0; j < pso_desc.NumRenderTargets; j++) {
pso_desc.RTVFormats[j] = rtv_formats.front ();
rtv_formats.pop ();
}
pso_desc.SampleDesc.Count = 1;
ComPtr < ID3D12PipelineState > pso;
hr = device->CreateGraphicsPipelineState (&pso_desc, IID_PPV_ARGS (&pso));
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create PSO");
return FALSE;
}
priv->quad_data[i].desc = pso_desc;
priv->quad_data[i].pso = pso;
priv->quad_data[i].num_rtv = psblob_list[i].num_rtv;
}
D3D12_DESCRIPTOR_HEAP_DESC srv_heap_desc = { };
srv_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
srv_heap_desc.NumDescriptors = priv->crs->GetNumSrv ();
if (priv->crs->HaveLut ())
srv_heap_desc.NumDescriptors += 2;
srv_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
priv->srv_heap_pool = gst_d3d12_descriptor_pool_new (self->device,
&srv_heap_desc);
priv->upload_data = new ConverterUploadData ();
auto upload_data = priv->upload_data;
/* bottom left */
vertex_data[0].position.x = -1.0f;
vertex_data[0].position.y = -1.0f;
vertex_data[0].position.z = 0.0f;
vertex_data[0].texture.u = 0.0f;
vertex_data[0].texture.v = 1.0f;
/* top left */
vertex_data[1].position.x = -1.0f;
vertex_data[1].position.y = 1.0f;
vertex_data[1].position.z = 0.0f;
vertex_data[1].texture.u = 0.0f;
vertex_data[1].texture.v = 0.0f;
/* top right */
vertex_data[2].position.x = 1.0f;
vertex_data[2].position.y = 1.0f;
vertex_data[2].position.z = 0.0f;
vertex_data[2].texture.u = 1.0f;
vertex_data[2].texture.v = 0.0f;
/* bottom right */
vertex_data[3].position.x = 1.0f;
vertex_data[3].position.y = -1.0f;
vertex_data[3].position.z = 0.0f;
vertex_data[3].texture.u = 1.0f;
vertex_data[3].texture.v = 1.0f;
/* vertex and index buffers */
D3D12_HEAP_PROPERTIES heap_prop;
D3D12_RESOURCE_DESC resource_desc;
CD3DX12_RANGE range (0, 0);
guint8 *data;
{
heap_prop = CD3DX12_HEAP_PROPERTIES (D3D12_HEAP_TYPE_DEFAULT);
resource_desc =
CD3DX12_RESOURCE_DESC::Buffer (sizeof (VertexData) * 4 +
sizeof (g_indices));
hr = device->CreateCommittedResource (&heap_prop,
D3D12_HEAP_FLAG_CREATE_NOT_ZEROED,
&resource_desc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr,
IID_PPV_ARGS (&priv->vertex_index_buf));
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create vertex buffer");
return FALSE;
}
priv->vbv.BufferLocation = priv->vertex_index_buf->GetGPUVirtualAddress ();
priv->vbv.SizeInBytes = sizeof (VertexData) * 4;
priv->vbv.StrideInBytes = sizeof (VertexData);
priv->idv.BufferLocation = priv->vbv.BufferLocation + priv->vbv.SizeInBytes;
priv->idv.SizeInBytes = sizeof (g_indices);
priv->idv.Format = DXGI_FORMAT_R16_UINT;
heap_prop = CD3DX12_HEAP_PROPERTIES (D3D12_HEAP_TYPE_UPLOAD);
hr = device->CreateCommittedResource (&heap_prop,
D3D12_HEAP_FLAG_CREATE_NOT_ZEROED,
&resource_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr,
IID_PPV_ARGS (&upload_data->vertex_index_upload));
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create vertex buffer upload");
return FALSE;
}
hr = upload_data->vertex_index_upload->Map (0, &range, (void **) &data);
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't map vertext buffer");
return FALSE;
}
memcpy (data, vertex_data, sizeof (VertexData) * 4);
memcpy (data + sizeof (VertexData) * 4, g_indices, sizeof (g_indices));
upload_data->vertex_index_upload->Unmap (0, nullptr);
}
/* pixel shader const buffer, static */
{
heap_prop = CD3DX12_HEAP_PROPERTIES (D3D12_HEAP_TYPE_DEFAULT);
resource_desc = CD3DX12_RESOURCE_DESC::Buffer (sizeof (PSConstBuffer));
hr = device->CreateCommittedResource (&heap_prop,
D3D12_HEAP_FLAG_CREATE_NOT_ZEROED,
&resource_desc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr,
IID_PPV_ARGS (&priv->ps_const_buf));
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create const buffer");
return FALSE;
}
heap_prop = CD3DX12_HEAP_PROPERTIES (D3D12_HEAP_TYPE_UPLOAD);
hr = device->CreateCommittedResource (&heap_prop,
D3D12_HEAP_FLAG_CREATE_NOT_ZEROED,
&resource_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr,
IID_PPV_ARGS (&upload_data->ps_const_upload));
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create const buffer upload");
return FALSE;
}
hr = upload_data->ps_const_upload->Map (0, &range, (void **) &data);
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't map index buffer");
return FALSE;
}
memcpy (data, &priv->const_data, sizeof (PSConstBuffer));
upload_data->ps_const_upload->Unmap (0, nullptr);
}
if (priv->crs->HaveLut ()) {
heap_prop = CD3DX12_HEAP_PROPERTIES (D3D12_HEAP_TYPE_DEFAULT);
resource_desc = CD3DX12_RESOURCE_DESC::Tex1D (DXGI_FORMAT_R16_UNORM,
GAMMA_LUT_SIZE, 1, 1);
hr = device->CreateCommittedResource (&heap_prop,
D3D12_HEAP_FLAG_CREATE_NOT_ZEROED,
&resource_desc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr,
IID_PPV_ARGS (&priv->gamma_dec_lut));
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create gamma decoding LUT");
return FALSE;
}
hr = device->CreateCommittedResource (&heap_prop,
D3D12_HEAP_FLAG_CREATE_NOT_ZEROED,
&resource_desc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr,
IID_PPV_ARGS (&priv->gamma_enc_lut));
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create gamma encoding LUT");
return FALSE;
}
UINT64 gamma_lut_size;
device->GetCopyableFootprints (&resource_desc, 0, 1, 0,
&priv->gamma_lut_layout, nullptr, nullptr, &gamma_lut_size);
heap_prop = CD3DX12_HEAP_PROPERTIES (D3D12_HEAP_TYPE_UPLOAD);
resource_desc = CD3DX12_RESOURCE_DESC::Buffer (gamma_lut_size);
hr = device->CreateCommittedResource (&heap_prop,
D3D12_HEAP_FLAG_CREATE_NOT_ZEROED,
&resource_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr,
IID_PPV_ARGS (&upload_data->gamma_dec_lut_upload));
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create gamma decoding LUT upload");
return FALSE;
}
hr = device->CreateCommittedResource (&heap_prop,
D3D12_HEAP_FLAG_CREATE_NOT_ZEROED,
&resource_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr,
IID_PPV_ARGS (&upload_data->gamma_enc_lut_upload));
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create gamma encoding LUT upload");
return FALSE;
}
auto in_trc = in_info->colorimetry.transfer;
auto out_trc = in_info->colorimetry.transfer;
auto gamma_dec_table = gst_d3d12_converter_get_gamma_dec_table (in_trc);
auto gamma_enc_table = gst_d3d12_converter_get_gamma_enc_table (out_trc);
hr = upload_data->gamma_dec_lut_upload->Map (0, &range, (void **) &data);
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't map gamma lut upload buffer");
return FALSE;
}
memcpy (data, gamma_dec_table->lut, GAMMA_LUT_SIZE * sizeof (guint16));
upload_data->gamma_dec_lut_upload->Unmap (0, nullptr);
hr = upload_data->gamma_enc_lut_upload->Map (0, &range, (void **) &data);
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't map gamma lut upload buffer");
return FALSE;
}
memcpy (data, gamma_enc_table->lut, GAMMA_LUT_SIZE * sizeof (guint16));
upload_data->gamma_enc_lut_upload->Unmap (0, nullptr);
D3D12_DESCRIPTOR_HEAP_DESC desc = { };
desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
desc.NumDescriptors = 2;
desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
auto hr = device->CreateDescriptorHeap (&desc,
IID_PPV_ARGS (&priv->gamma_lut_heap));
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't map gamma lut upload buffer");
return FALSE;
}
auto cpu_handle =
CD3DX12_CPU_DESCRIPTOR_HANDLE
(priv->gamma_lut_heap->GetCPUDescriptorHandleForHeapStart ());
D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc = { };
srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1D;
srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
srv_desc.Texture1D.MipLevels = 1;
device->CreateShaderResourceView (priv->gamma_dec_lut.Get (), &srv_desc,
cpu_handle);
cpu_handle.Offset (priv->srv_inc_size);
device->CreateShaderResourceView (priv->gamma_enc_lut.Get (), &srv_desc,
cpu_handle);
}
priv->input_texture_width = GST_VIDEO_INFO_WIDTH (in_info);
priv->input_texture_height = GST_VIDEO_INFO_HEIGHT (in_info);
for (guint i = 0; i < GST_VIDEO_INFO_N_PLANES (out_info); i++) {
priv->viewport[i].TopLeftX = 0;
priv->viewport[i].TopLeftY = 0;
priv->viewport[i].Width = GST_VIDEO_INFO_COMP_WIDTH (out_info, i);
priv->viewport[i].Height = GST_VIDEO_INFO_COMP_HEIGHT (out_info, i);
priv->viewport[i].MinDepth = 0.0f;
priv->viewport[i].MaxDepth = 1.0f;
priv->scissor_rect[i].left = 0;
priv->scissor_rect[i].top = 0;
priv->scissor_rect[i].right = GST_VIDEO_INFO_COMP_WIDTH (out_info, i);
priv->scissor_rect[i].bottom = GST_VIDEO_INFO_COMP_HEIGHT (out_info, i);
}
return TRUE;
}
static void
gst_d3d12_converter_update_clear_background (GstD3D12Converter * self)
{
auto priv = self->priv;
const GstVideoInfo *out_info = &priv->out_info;
if (priv->fill_border && (priv->dest_x != 0 || priv->dest_y != 0 ||
priv->dest_width != out_info->width ||
priv->dest_height != out_info->height ||
priv->video_direction == GST_VIDEO_ORIENTATION_CUSTOM)) {
GST_DEBUG_OBJECT (self, "Enable background color");
priv->clear_background = TRUE;
} else {
GST_DEBUG_OBJECT (self, "Disable background color");
priv->clear_background = FALSE;
}
}
static gboolean
gst_d3d12_converter_apply_orientation (GstD3D12Converter * self)
{
auto priv = self->priv;
switch (priv->video_direction) {
case GST_VIDEO_ORIENTATION_IDENTITY:
case GST_VIDEO_ORIENTATION_AUTO:
case GST_VIDEO_ORIENTATION_CUSTOM:
default:
priv->transform = g_matrix_identity;
break;
case GST_VIDEO_ORIENTATION_90R:
priv->transform = g_matrix_90r;
break;
case GST_VIDEO_ORIENTATION_180:
priv->transform = g_matrix_180;
break;
case GST_VIDEO_ORIENTATION_90L:
priv->transform = g_matrix_90l;
break;
case GST_VIDEO_ORIENTATION_HORIZ:
priv->transform = g_matrix_horiz;
break;
case GST_VIDEO_ORIENTATION_VERT:
priv->transform = g_matrix_vert;
break;
case GST_VIDEO_ORIENTATION_UL_LR:
priv->transform = g_matrix_ul_lr;
break;
case GST_VIDEO_ORIENTATION_UR_LL:
priv->transform = g_matrix_ur_ll;
break;
}
return TRUE;
}
static gboolean
gst_d3d12_converter_update_transform (GstD3D12Converter * self)
{
auto priv = self->priv;
if (!priv->update_transform)
return TRUE;
priv->update_transform = FALSE;
gst_d3d12_converter_update_clear_background (self);
return gst_d3d12_converter_apply_orientation (self);
}
static gboolean
gst_d3d12_converter_update_src_rect (GstD3D12Converter * self)
{
auto priv = self->priv;
VertexData vertex_data[4];
HRESULT hr;
FLOAT u0, u1, v0, v1, off_u, off_v;
gint texture_width = priv->input_texture_width;
gint texture_height = priv->input_texture_height;
if (!priv->update_src_rect)
return TRUE;
priv->update_src_rect = FALSE;
GST_DEBUG_OBJECT (self, "Updating vertex buffer");
if (!priv->upload_data)
priv->upload_data = new ConverterUploadData ();
auto upload_data = priv->upload_data;
if (!upload_data->vertex_index_upload) {
D3D12_HEAP_PROPERTIES heap_prop =
CD3DX12_HEAP_PROPERTIES (D3D12_HEAP_TYPE_UPLOAD);
D3D12_RESOURCE_DESC buffer_desc =
CD3DX12_RESOURCE_DESC::Buffer (sizeof (vertex_data) +
sizeof (g_indices));
auto device = gst_d3d12_device_get_device_handle (self->device);
hr = device->CreateCommittedResource (&heap_prop, D3D12_HEAP_FLAG_NONE,
&buffer_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr,
IID_PPV_ARGS (&upload_data->vertex_index_upload));
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create vertex buffer upload");
return FALSE;
}
}
/*
* (u0, v0) -- (u1, v0)
* | |
* (u0, v1) -- (u1, v1)
*/
off_u = 0.5f / texture_width;
off_v = 0.5f / texture_height;
if (priv->src_x > 0)
u0 = (priv->src_x / (gfloat) texture_width) + off_u;
else
u0 = 0.0f;
if ((priv->src_x + priv->src_width) != texture_width)
u1 = ((priv->src_x + priv->src_width) / (gfloat) texture_width) - off_u;
else
u1 = 1.0f;
if (priv->src_y > 0)
v0 = (priv->src_y / (gfloat) texture_height) + off_v;
else
v0 = 0.0;
if ((priv->src_y + priv->src_height) != texture_height)
v1 = ((priv->src_y + priv->src_height) / (gfloat) texture_height) - off_v;
else
v1 = 1.0f;
/* bottom left */
vertex_data[0].position.x = -1.0f;
vertex_data[0].position.y = -1.0f;
vertex_data[0].position.z = 0.0f;
vertex_data[0].texture.u = u0;
vertex_data[0].texture.v = v1;
/* top left */
vertex_data[1].position.x = -1.0f;
vertex_data[1].position.y = 1.0f;
vertex_data[1].position.z = 0.0f;
vertex_data[1].texture.u = u0;
vertex_data[1].texture.v = v0;
/* top right */
vertex_data[2].position.x = 1.0f;
vertex_data[2].position.y = 1.0f;
vertex_data[2].position.z = 0.0f;
vertex_data[2].texture.u = u1;
vertex_data[2].texture.v = v0;
/* bottom right */
vertex_data[3].position.x = 1.0f;
vertex_data[3].position.y = -1.0f;
vertex_data[3].position.z = 0.0f;
vertex_data[3].texture.u = u1;
vertex_data[3].texture.v = v1;
guint8 *data;
CD3DX12_RANGE range (0, 0);
hr = upload_data->vertex_index_upload->Map (0, &range, (void **) &data);
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't map vertex buffer, hr: 0x%x", (guint) hr);
return FALSE;
}
memcpy (data, vertex_data, sizeof (VertexData) * 4);
memcpy (data + sizeof (VertexData) * 4, g_indices, sizeof (g_indices));
upload_data->vertex_index_upload->Unmap (0, nullptr);
return TRUE;
}
static gboolean
gst_d3d12_converter_update_dest_rect (GstD3D12Converter * self)
{
auto priv = self->priv;
if (!priv->update_dest_rect)
return TRUE;
priv->viewport[0].TopLeftX = priv->dest_x;
priv->viewport[0].TopLeftY = priv->dest_y;
priv->viewport[0].Width = priv->dest_width;
priv->viewport[0].Height = priv->dest_height;
priv->scissor_rect[0].left = priv->dest_x;
priv->scissor_rect[0].top = priv->dest_y;
priv->scissor_rect[0].right = priv->dest_width + priv->dest_x;
priv->scissor_rect[0].bottom = priv->dest_height + priv->dest_y;
GST_DEBUG_OBJECT (self,
"Update viewport, TopLeftX: %f, TopLeftY: %f, Width: %f, Height %f",
priv->viewport[0].TopLeftX, priv->viewport[0].TopLeftY,
priv->viewport[0].Width, priv->viewport[0].Height);
gst_d3d12_converter_update_clear_background (self);
switch (GST_VIDEO_INFO_FORMAT (&priv->out_info)) {
case GST_VIDEO_FORMAT_NV12:
case GST_VIDEO_FORMAT_NV21:
case GST_VIDEO_FORMAT_P010_10LE:
case GST_VIDEO_FORMAT_P012_LE:
case GST_VIDEO_FORMAT_P016_LE:
case GST_VIDEO_FORMAT_I420:
case GST_VIDEO_FORMAT_YV12:
case GST_VIDEO_FORMAT_I420_10LE:
case GST_VIDEO_FORMAT_I420_12LE:
priv->viewport[1].TopLeftX = priv->viewport[0].TopLeftX / 2;
priv->viewport[1].TopLeftY = priv->viewport[0].TopLeftY / 2;
priv->viewport[1].Width = priv->viewport[0].Width / 2;
priv->viewport[1].Height = priv->viewport[0].Height / 2;
priv->scissor_rect[1].left = priv->scissor_rect[0].left / 2;
priv->scissor_rect[1].top = priv->scissor_rect[0].top / 2;
priv->scissor_rect[1].right = priv->scissor_rect[0].right / 2;
priv->scissor_rect[1].bottom = priv->scissor_rect[0].bottom / 2;
for (guint i = 2; i < GST_VIDEO_INFO_N_PLANES (&priv->out_info); i++) {
priv->viewport[i] = priv->viewport[1];
priv->scissor_rect[i] = priv->scissor_rect[1];
}
break;
case GST_VIDEO_FORMAT_Y42B:
case GST_VIDEO_FORMAT_I422_10LE:
case GST_VIDEO_FORMAT_I422_12LE:
priv->viewport[1].TopLeftX = priv->viewport[0].TopLeftX / 2;
priv->viewport[1].TopLeftY = priv->viewport[0].TopLeftY;
priv->viewport[1].Width = priv->viewport[0].Width / 2;
priv->viewport[1].Height = priv->viewport[0].Height;
priv->scissor_rect[1].left = priv->scissor_rect[0].left / 2;
priv->scissor_rect[1].top = priv->scissor_rect[0].top;
priv->scissor_rect[1].right = priv->scissor_rect[0].right / 2;
priv->scissor_rect[1].bottom = priv->scissor_rect[0].bottom;
for (guint i = 2; i < GST_VIDEO_INFO_N_PLANES (&priv->out_info); i++) {
priv->viewport[i] = priv->viewport[1];
priv->scissor_rect[i] = priv->scissor_rect[1];
}
break;
case GST_VIDEO_FORMAT_Y444:
case GST_VIDEO_FORMAT_Y444_10LE:
case GST_VIDEO_FORMAT_Y444_12LE:
case GST_VIDEO_FORMAT_Y444_16LE:
case GST_VIDEO_FORMAT_RGBP:
case GST_VIDEO_FORMAT_BGRP:
case GST_VIDEO_FORMAT_GBR:
case GST_VIDEO_FORMAT_GBR_10LE:
case GST_VIDEO_FORMAT_GBR_12LE:
case GST_VIDEO_FORMAT_GBR_16LE:
case GST_VIDEO_FORMAT_GBRA:
case GST_VIDEO_FORMAT_GBRA_10LE:
case GST_VIDEO_FORMAT_GBRA_12LE:
for (guint i = 1; i < GST_VIDEO_INFO_N_PLANES (&priv->out_info); i++) {
priv->viewport[i] = priv->viewport[0];
priv->scissor_rect[i] = priv->scissor_rect[0];
}
break;
default:
break;
}
priv->update_dest_rect = FALSE;
return TRUE;
}
static void
convert_info_gray_to_yuv (const GstVideoInfo * gray, GstVideoInfo * yuv)
{
GstVideoInfo tmp;
if (GST_VIDEO_INFO_IS_YUV (gray)) {
*yuv = *gray;
return;
}
if (gray->finfo->depth[0] == 8) {
gst_video_info_set_format (&tmp,
GST_VIDEO_FORMAT_Y444, gray->width, gray->height);
} else {
gst_video_info_set_format (&tmp,
GST_VIDEO_FORMAT_Y444_16LE, gray->width, gray->height);
}
tmp.colorimetry.range = gray->colorimetry.range;
if (tmp.colorimetry.range == GST_VIDEO_COLOR_RANGE_UNKNOWN)
tmp.colorimetry.range = GST_VIDEO_COLOR_RANGE_0_255;
tmp.colorimetry.primaries = gray->colorimetry.primaries;
if (tmp.colorimetry.primaries == GST_VIDEO_COLOR_PRIMARIES_UNKNOWN)
tmp.colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT709;
tmp.colorimetry.transfer = gray->colorimetry.transfer;
if (tmp.colorimetry.transfer == GST_VIDEO_TRANSFER_UNKNOWN)
tmp.colorimetry.transfer = GST_VIDEO_TRANSFER_BT709;
tmp.colorimetry.matrix = gray->colorimetry.matrix;
if (tmp.colorimetry.matrix == GST_VIDEO_COLOR_MATRIX_UNKNOWN)
tmp.colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT709;
*yuv = tmp;
}
static gboolean
gst_d3d12_converter_calculate_matrix (GstD3D12Converter * self,
const GstVideoInfo * in_info, const GstVideoInfo * out_info)
{
auto priv = self->priv;
GstD3D12ColorMatrix pre_coeff;
GstD3D12ColorMatrix post_coeff;
GstD3D12ColorMatrix primaries_coeff;
GstVideoInfo rgb_info;
gst_d3d12_color_matrix_init (&pre_coeff);
gst_d3d12_color_matrix_init (&post_coeff);
gst_d3d12_color_matrix_init (&primaries_coeff);
switch (priv->convert_type) {
case CONVERT_TYPE::RANGE:
gst_d3d12_color_range_adjust_matrix_unorm (in_info, out_info,
&post_coeff);
break;
case CONVERT_TYPE::SIMPLE:
if (GST_VIDEO_INFO_IS_RGB (in_info)) {
gst_d3d12_rgb_to_yuv_matrix_unorm (in_info, out_info, &post_coeff);
} else {
gst_d3d12_yuv_to_rgb_matrix_unorm (in_info, out_info, &post_coeff);
}
break;
case CONVERT_TYPE::GAMMA:
case CONVERT_TYPE::PRIMARY:
if (GST_VIDEO_INFO_IS_RGB (in_info)) {
rgb_info = *in_info;
if (in_info->colorimetry.range == GST_VIDEO_COLOR_RANGE_16_235) {
rgb_info.colorimetry.range = GST_VIDEO_COLOR_RANGE_0_255;
gst_d3d12_color_range_adjust_matrix_unorm (in_info,
&rgb_info, &pre_coeff);
}
} else {
gst_video_info_set_format (&rgb_info,
in_info->finfo->depth[0] == 8 ? GST_VIDEO_FORMAT_RGBA :
GST_VIDEO_FORMAT_RGBA64_LE, in_info->width, in_info->height);
rgb_info.colorimetry.range = GST_VIDEO_COLOR_RANGE_0_255;
rgb_info.colorimetry.transfer = in_info->colorimetry.transfer;
rgb_info.colorimetry.primaries = in_info->colorimetry.primaries;
gst_d3d12_yuv_to_rgb_matrix_unorm (in_info, &rgb_info, &pre_coeff);
}
if (priv->convert_type == CONVERT_TYPE::PRIMARY) {
const GstVideoColorPrimariesInfo *in_pinfo;
const GstVideoColorPrimariesInfo *out_pinfo;
in_pinfo =
gst_video_color_primaries_get_info (in_info->colorimetry.primaries);
out_pinfo =
gst_video_color_primaries_get_info (out_info->
colorimetry.primaries);
gst_d3d12_color_primaries_matrix_unorm (in_pinfo, out_pinfo,
&primaries_coeff);
}
if (GST_VIDEO_INFO_IS_RGB (out_info)) {
if (out_info->colorimetry.range == GST_VIDEO_COLOR_RANGE_16_235) {
rgb_info = *out_info;
rgb_info.colorimetry.range = GST_VIDEO_COLOR_RANGE_0_255;
gst_d3d12_color_range_adjust_matrix_unorm (&rgb_info,
out_info, &post_coeff);
}
} else {
gst_d3d12_rgb_to_yuv_matrix_unorm (&rgb_info, out_info, &post_coeff);
}
break;
default:
break;
}
if (gst_debug_category_get_threshold (GST_CAT_DEFAULT) >= GST_LEVEL_DEBUG) {
gchar *matrix_dump;
matrix_dump = gst_d3d12_dump_color_matrix (&pre_coeff);
GST_DEBUG_OBJECT (self, "PreCoeff \n%s", matrix_dump);
g_free (matrix_dump);
matrix_dump = gst_d3d12_dump_color_matrix (&primaries_coeff);
GST_DEBUG_OBJECT (self, "PrimaryCoeff \n%s", matrix_dump);
g_free (matrix_dump);
matrix_dump = gst_d3d12_dump_color_matrix (&post_coeff);
GST_DEBUG_OBJECT (self, "PostCoeff \n%s", matrix_dump);
g_free (matrix_dump);
}
PSColorSpace *preCoeff = &priv->const_data.preCoeff;
PSColorSpace *postCoeff = &priv->const_data.postCoeff;
PSColorSpace *primariesCoeff = &priv->const_data.primariesCoeff;
for (guint i = 0; i < 3; i++) {
preCoeff->coeffX[i] = pre_coeff.matrix[0][i];
preCoeff->coeffY[i] = pre_coeff.matrix[1][i];
preCoeff->coeffZ[i] = pre_coeff.matrix[2][i];
preCoeff->offset[i] = pre_coeff.offset[i];
preCoeff->min[i] = pre_coeff.min[i];
preCoeff->max[i] = pre_coeff.max[i];
postCoeff->coeffX[i] = post_coeff.matrix[0][i];
postCoeff->coeffY[i] = post_coeff.matrix[1][i];
postCoeff->coeffZ[i] = post_coeff.matrix[2][i];
postCoeff->offset[i] = post_coeff.offset[i];
postCoeff->min[i] = post_coeff.min[i];
postCoeff->max[i] = post_coeff.max[i];
primariesCoeff->coeffX[i] = primaries_coeff.matrix[0][i];
primariesCoeff->coeffY[i] = primaries_coeff.matrix[1][i];
primariesCoeff->coeffZ[i] = primaries_coeff.matrix[2][i];
primariesCoeff->offset[i] = primaries_coeff.offset[i];
primariesCoeff->min[i] = primaries_coeff.min[i];
primariesCoeff->max[i] = primaries_coeff.max[i];
}
return TRUE;
}
static gboolean
is_custom_format (GstVideoFormat format)
{
switch (format) {
case GST_VIDEO_FORMAT_xRGB:
case GST_VIDEO_FORMAT_xBGR:
case GST_VIDEO_FORMAT_ARGB:
case GST_VIDEO_FORMAT_ABGR:
case GST_VIDEO_FORMAT_BGRA64_LE:
case GST_VIDEO_FORMAT_BGR10A2_LE:
case GST_VIDEO_FORMAT_RBGA:
return TRUE;
default:
break;
}
return FALSE;
}
static void
gst_d3d12_converter_calculate_border_color (GstD3D12Converter * self)
{
auto priv = self->priv;
GstD3D12ColorMatrix *m = &priv->clear_color_matrix;
const GstVideoInfo *out_info = &priv->out_info;
gdouble a;
gdouble rgb[3];
gdouble converted[3];
GstVideoFormat format = GST_VIDEO_INFO_FORMAT (out_info);
a = ((priv->border_color & 0xffff000000000000) >> 48) / (gdouble) G_MAXUINT16;
rgb[0] =
((priv->border_color & 0x0000ffff00000000) >> 32) / (gdouble) G_MAXUINT16;
rgb[1] =
((priv->border_color & 0x00000000ffff0000) >> 16) / (gdouble) G_MAXUINT16;
rgb[2] = (priv->border_color & 0x000000000000ffff) / (gdouble) G_MAXUINT16;
for (guint i = 0; i < 3; i++) {
converted[i] = 0;
for (guint j = 0; j < 3; j++) {
converted[i] += m->matrix[i][j] * rgb[j];
}
converted[i] += m->offset[i];
converted[i] = CLAMP (converted[i], m->min[i], m->max[i]);
}
GST_DEBUG_OBJECT (self, "Calculated background color ARGB: %f, %f, %f, %f",
a, converted[0], converted[1], converted[2]);
/* scale down if output is planar high bitdepth format */
switch (format) {
case GST_VIDEO_FORMAT_I420_10LE:
case GST_VIDEO_FORMAT_I422_10LE:
case GST_VIDEO_FORMAT_Y444_10LE:
case GST_VIDEO_FORMAT_GBR_10LE:
case GST_VIDEO_FORMAT_GBRA_10LE:
for (guint i = 0; i < 3; i++) {
converted[i] /= 64.0;
}
a /= 64.0;
break;
case GST_VIDEO_FORMAT_I420_12LE:
case GST_VIDEO_FORMAT_I422_12LE:
case GST_VIDEO_FORMAT_Y444_12LE:
case GST_VIDEO_FORMAT_GBR_12LE:
case GST_VIDEO_FORMAT_GBRA_12LE:
for (guint i = 0; i < 3; i++) {
converted[i] /= 16.0;
}
a /= 16.0;
break;
default:
break;
}
if ((GST_VIDEO_INFO_IS_RGB (out_info) &&
GST_VIDEO_INFO_N_PLANES (out_info) == 1 &&
!is_custom_format (format)) || GST_VIDEO_INFO_IS_GRAY (out_info)) {
for (guint i = 0; i < 3; i++)
priv->clear_color[0][i] = converted[i];
priv->clear_color[0][3] = a;
} else {
switch (format) {
case GST_VIDEO_FORMAT_VUYA:
priv->clear_color[0][0] = converted[2];
priv->clear_color[0][1] = converted[1];
priv->clear_color[0][2] = converted[0];
priv->clear_color[0][3] = a;
break;
case GST_VIDEO_FORMAT_AYUV:
case GST_VIDEO_FORMAT_AYUV64:
priv->clear_color[0][0] = a;
priv->clear_color[0][1] = converted[0];
priv->clear_color[0][2] = converted[1];
priv->clear_color[0][3] = converted[2];
break;
case GST_VIDEO_FORMAT_ARGB:
case GST_VIDEO_FORMAT_xRGB:
priv->clear_color[0][0] = a;
priv->clear_color[0][1] = converted[0];
priv->clear_color[0][2] = converted[1];
priv->clear_color[0][3] = converted[2];
break;
case GST_VIDEO_FORMAT_ABGR:
case GST_VIDEO_FORMAT_xBGR:
priv->clear_color[0][0] = a;
priv->clear_color[0][1] = converted[2];
priv->clear_color[0][2] = converted[1];
priv->clear_color[0][3] = converted[0];
break;
case GST_VIDEO_FORMAT_RBGA:
priv->clear_color[0][0] = converted[0];
priv->clear_color[0][1] = converted[2];
priv->clear_color[0][2] = converted[1];
priv->clear_color[0][3] = a;
break;
case GST_VIDEO_FORMAT_NV12:
case GST_VIDEO_FORMAT_NV21:
case GST_VIDEO_FORMAT_P010_10LE:
case GST_VIDEO_FORMAT_P012_LE:
case GST_VIDEO_FORMAT_P016_LE:
priv->clear_color[0][0] = converted[0];
priv->clear_color[0][1] = 0;
priv->clear_color[0][2] = 0;
priv->clear_color[0][3] = 1.0;
if (format == GST_VIDEO_FORMAT_NV21) {
priv->clear_color[1][0] = converted[2];
priv->clear_color[1][1] = converted[1];
} else {
priv->clear_color[1][0] = converted[1];
priv->clear_color[1][1] = converted[2];
}
priv->clear_color[1][2] = 0;
priv->clear_color[1][3] = 1.0;
break;
case GST_VIDEO_FORMAT_I420:
case GST_VIDEO_FORMAT_YV12:
case GST_VIDEO_FORMAT_I420_10LE:
case GST_VIDEO_FORMAT_I420_12LE:
case GST_VIDEO_FORMAT_Y42B:
case GST_VIDEO_FORMAT_I422_10LE:
case GST_VIDEO_FORMAT_I422_12LE:
case GST_VIDEO_FORMAT_Y444:
case GST_VIDEO_FORMAT_Y444_10LE:
case GST_VIDEO_FORMAT_Y444_12LE:
case GST_VIDEO_FORMAT_Y444_16LE:
priv->clear_color[0][0] = converted[0];
priv->clear_color[0][1] = 0;
priv->clear_color[0][2] = 0;
priv->clear_color[0][3] = 1.0;
if (format == GST_VIDEO_FORMAT_YV12) {
priv->clear_color[1][0] = converted[2];
priv->clear_color[2][0] = converted[1];
} else {
priv->clear_color[1][0] = converted[1];
priv->clear_color[2][0] = converted[2];
}
priv->clear_color[1][1] = 0;
priv->clear_color[1][2] = 0;
priv->clear_color[1][3] = 1.0;
priv->clear_color[2][1] = 0;
priv->clear_color[2][2] = 0;
priv->clear_color[2][3] = 1.0;
break;
case GST_VIDEO_FORMAT_RGBP:
priv->clear_color[0][0] = converted[0];
priv->clear_color[1][0] = converted[1];
priv->clear_color[2][0] = converted[2];
break;
case GST_VIDEO_FORMAT_BGRP:
priv->clear_color[0][0] = converted[2];
priv->clear_color[1][0] = converted[1];
priv->clear_color[2][0] = converted[0];
break;
case GST_VIDEO_FORMAT_GBR:
case GST_VIDEO_FORMAT_GBR_10LE:
case GST_VIDEO_FORMAT_GBR_12LE:
case GST_VIDEO_FORMAT_GBR_16LE:
priv->clear_color[0][0] = converted[1];
priv->clear_color[1][0] = converted[2];
priv->clear_color[2][0] = converted[0];
break;
case GST_VIDEO_FORMAT_GBRA:
case GST_VIDEO_FORMAT_GBRA_10LE:
case GST_VIDEO_FORMAT_GBRA_12LE:
priv->clear_color[0][0] = converted[1];
priv->clear_color[1][0] = converted[2];
priv->clear_color[2][0] = converted[0];
priv->clear_color[3][0] = a;
break;
default:
g_assert_not_reached ();
break;
}
}
}
GstD3D12Converter *
gst_d3d12_converter_new (GstD3D12Device * device, const GstVideoInfo * in_info,
const GstVideoInfo * out_info, const D3D12_BLEND_DESC * blend_desc,
const gfloat blend_factor[4], GstStructure * config)
{
GstD3D12Converter *self;
GstD3D12Format in_d3d12_format;
GstD3D12Format out_d3d12_format;
gboolean allow_gamma = FALSE;
gboolean allow_primaries = FALSE;
D3D12_FILTER sampler_filter = D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT;
GstVideoInfo matrix_in_info;
GstVideoInfo matrix_out_info;
g_return_val_if_fail (GST_IS_D3D12_DEVICE (device), nullptr);
g_return_val_if_fail (in_info != nullptr, nullptr);
g_return_val_if_fail (out_info != nullptr, nullptr);
self = (GstD3D12Converter *) g_object_new (GST_TYPE_D3D12_CONVERTER, nullptr);
gst_object_ref_sink (self);
auto priv = self->priv;
if (blend_desc)
priv->blend_desc = *blend_desc;
if (blend_factor) {
for (guint i = 0; i < 4; i++)
priv->blend_factor[i] = blend_factor[i];
}
if (config) {
gint value;
if (gst_structure_get_enum (config, GST_D3D12_CONVERTER_OPT_GAMMA_MODE,
GST_TYPE_VIDEO_GAMMA_MODE, &value) &&
(GstVideoGammaMode) value != GST_VIDEO_GAMMA_MODE_NONE) {
allow_gamma = TRUE;
}
if (gst_structure_get_enum (config, GST_D3D12_CONVERTER_OPT_PRIMARIES_MODE,
GST_TYPE_VIDEO_PRIMARIES_MODE, &value) &&
(GstVideoPrimariesMode) value != GST_VIDEO_PRIMARIES_MODE_NONE) {
allow_primaries = TRUE;
}
gst_structure_get_enum (config, GST_D3D12_CONVERTER_OPT_SAMPLER_FILTER,
GST_TYPE_D3D12_CONVERTER_SAMPLER_FILTER, (int *) &sampler_filter);
gst_structure_get_enum (config, GST_D3D12_CONVERTER_OPT_SRC_ALPHA_MODE,
GST_TYPE_D3D12_CONVERTER_ALPHA_MODE, (int *) &priv->src_alpha_mode);
gst_structure_get_enum (config, GST_D3D12_CONVERTER_OPT_DEST_ALPHA_MODE,
GST_TYPE_D3D12_CONVERTER_ALPHA_MODE, (int *) &priv->dst_alpha_mode);
gst_structure_free (config);
}
GST_DEBUG_OBJECT (self,
"Setup converter with format %s -> %s, "
"allow gamma conversion: %d, allow primaries conversion: %d ",
gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (in_info)),
gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (out_info)),
allow_gamma, allow_primaries);
if (!gst_d3d12_device_get_format (device, GST_VIDEO_INFO_FORMAT (in_info),
&in_d3d12_format)) {
GST_ERROR_OBJECT (self, "%s couldn't be converted to d3d12 format",
gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (in_info)));
gst_object_unref (self);
return nullptr;
}
if (!gst_d3d12_device_get_format (device, GST_VIDEO_INFO_FORMAT (out_info),
&out_d3d12_format)) {
GST_ERROR_OBJECT (self, "%s couldn't be converted to d3d12 format",
gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (in_info)));
gst_object_unref (self);
return nullptr;
}
self->device = (GstD3D12Device *) gst_object_ref (device);
priv->in_info = *in_info;
priv->out_info = *out_info;
priv->in_d3d12_format = in_d3d12_format;
priv->out_d3d12_format = out_d3d12_format;
/* Init properties */
priv->src_width = GST_VIDEO_INFO_WIDTH (in_info);
priv->src_height = GST_VIDEO_INFO_HEIGHT (in_info);
priv->dest_width = GST_VIDEO_INFO_WIDTH (out_info);
priv->dest_height = GST_VIDEO_INFO_HEIGHT (out_info);
priv->alpha = 1.0;
priv->border_color = 0xffff000000000000;
if (GST_VIDEO_INFO_IS_RGB (&priv->out_info)) {
GstVideoInfo rgb_info = priv->out_info;
rgb_info.colorimetry.range = GST_VIDEO_COLOR_RANGE_0_255;
gst_d3d12_color_range_adjust_matrix_unorm (&rgb_info, &priv->out_info,
&priv->clear_color_matrix);
} else {
GstVideoInfo rgb_info;
GstVideoInfo yuv_info;
gst_video_info_set_format (&rgb_info, GST_VIDEO_FORMAT_RGBA64_LE,
priv->out_info.width, priv->out_info.height);
convert_info_gray_to_yuv (&priv->out_info, &yuv_info);
if (yuv_info.colorimetry.matrix == GST_VIDEO_COLOR_MATRIX_UNKNOWN ||
yuv_info.colorimetry.matrix == GST_VIDEO_COLOR_MATRIX_RGB) {
GST_WARNING_OBJECT (self, "Invalid matrix is detected");
yuv_info.colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT709;
}
gst_d3d12_rgb_to_yuv_matrix_unorm (&rgb_info,
&yuv_info, &priv->clear_color_matrix);
}
gst_d3d12_converter_calculate_border_color (self);
priv->convert_type = CONVERT_TYPE::IDENTITY;
if (GST_VIDEO_INFO_IS_RGB (in_info) != GST_VIDEO_INFO_IS_RGB (out_info)) {
priv->convert_type = CONVERT_TYPE::SIMPLE;
} else if (in_info->colorimetry.range != GST_VIDEO_COLOR_RANGE_UNKNOWN &&
out_info->colorimetry.range != GST_VIDEO_COLOR_RANGE_UNKNOWN &&
in_info->colorimetry.range != out_info->colorimetry.range) {
priv->convert_type = CONVERT_TYPE::RANGE;
}
if (allow_gamma &&
in_info->colorimetry.transfer != GST_VIDEO_TRANSFER_UNKNOWN &&
out_info->colorimetry.transfer != GST_VIDEO_TRANSFER_UNKNOWN &&
!gst_video_transfer_function_is_equivalent (in_info->colorimetry.transfer,
GST_VIDEO_INFO_COMP_DEPTH (in_info, 0),
out_info->colorimetry.transfer, GST_VIDEO_INFO_COMP_DEPTH (out_info,
0))) {
GST_DEBUG_OBJECT (self, "Different transfer function %d -> %d",
in_info->colorimetry.transfer, out_info->colorimetry.transfer);
priv->convert_type = CONVERT_TYPE::GAMMA;
}
if (allow_primaries &&
in_info->colorimetry.primaries != GST_VIDEO_COLOR_PRIMARIES_UNKNOWN &&
out_info->colorimetry.primaries != GST_VIDEO_COLOR_PRIMARIES_UNKNOWN &&
in_info->colorimetry.transfer != GST_VIDEO_TRANSFER_UNKNOWN &&
out_info->colorimetry.transfer != GST_VIDEO_TRANSFER_UNKNOWN &&
!gst_video_color_primaries_is_equivalent (in_info->colorimetry.primaries,
out_info->colorimetry.primaries)) {
GST_DEBUG_OBJECT (self, "Different primaries %d -> %d",
in_info->colorimetry.primaries, out_info->colorimetry.primaries);
priv->convert_type = CONVERT_TYPE::PRIMARY;
}
if (GST_VIDEO_INFO_IS_RGB (&priv->in_info)) {
matrix_in_info = priv->in_info;
} else {
convert_info_gray_to_yuv (&priv->in_info, &matrix_in_info);
if (matrix_in_info.colorimetry.matrix == GST_VIDEO_COLOR_MATRIX_UNKNOWN ||
matrix_in_info.colorimetry.matrix == GST_VIDEO_COLOR_MATRIX_RGB) {
matrix_in_info.colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT709;
}
}
if (GST_VIDEO_INFO_IS_RGB (&priv->out_info)) {
matrix_out_info = priv->out_info;
} else {
convert_info_gray_to_yuv (&priv->out_info, &matrix_out_info);
if (matrix_out_info.colorimetry.matrix == GST_VIDEO_COLOR_MATRIX_UNKNOWN ||
matrix_out_info.colorimetry.matrix == GST_VIDEO_COLOR_MATRIX_RGB) {
matrix_out_info.colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT709;
}
}
if (!gst_d3d12_converter_calculate_matrix (self,
&matrix_in_info, &matrix_out_info)) {
gst_object_unref (self);
return nullptr;
}
if (!gst_d3d12_converter_setup_resource (self, &priv->in_info,
&priv->out_info, sampler_filter)) {
gst_object_unref (self);
return nullptr;
}
return self;
}
static gboolean
gst_d3d12_converter_update_pso (GstD3D12Converter * self)
{
auto priv = self->priv;
if (!priv->update_pso)
return TRUE;
std::vector < QuadData > quad_data;
quad_data.resize (priv->quad_data.size ());
auto device = gst_d3d12_device_get_device_handle (self->device);
for (size_t i = 0; i < quad_data.size (); i++) {
D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc = priv->quad_data[i].desc;
pso_desc.BlendState = priv->blend_desc;
ComPtr < ID3D12PipelineState > pso;
auto hr =
device->CreateGraphicsPipelineState (&pso_desc, IID_PPV_ARGS (&pso));
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create pso");
return FALSE;
}
quad_data[i].desc = pso_desc;
quad_data[i].pso = pso;
quad_data[i].num_rtv = priv->quad_data[i].num_rtv;
}
priv->update_pso = FALSE;
priv->quad_data = quad_data;
return TRUE;
}
static void
pso_free_func (ID3D12PipelineState * pso)
{
if (pso)
pso->Release ();
}
static gboolean
gst_d3d12_converter_execute (GstD3D12Converter * self,
GstBuffer * in_buf, GstBuffer * out_buf, GstD3D12FenceData * fence_data,
ID3D12GraphicsCommandList * cl)
{
auto priv = self->priv;
std::lock_guard < std::mutex > lk (priv->prop_lock);
auto mem = (GstD3D12Memory *) gst_buffer_peek_memory (in_buf, 0);
auto resource = gst_d3d12_memory_get_resource_handle (mem);
auto desc = resource->GetDesc ();
if (desc.Width != priv->input_texture_width ||
desc.Height != priv->input_texture_height) {
GST_DEBUG_OBJECT (self, "Texture resolution changed %ux%u -> %ux%u",
(guint) priv->input_texture_width, priv->input_texture_height,
(guint) desc.Width, desc.Height);
priv->input_texture_width = desc.Width;
priv->input_texture_height = desc.Height;
priv->update_src_rect = TRUE;
}
if (!gst_d3d12_converter_update_dest_rect (self)) {
GST_ERROR_OBJECT (self, "Failed to update dest rect");
return FALSE;
}
if (!gst_d3d12_converter_update_src_rect (self)) {
GST_ERROR_OBJECT (self, "Failed to update src rect");
return FALSE;
}
if (!gst_d3d12_converter_update_transform (self)) {
GST_ERROR_OBJECT (self, "Failed to update transform matrix");
return FALSE;
}
if (!gst_d3d12_converter_update_pso (self)) {
GST_ERROR_OBJECT (self, "Failed to update pso");
return FALSE;
}
auto & barriers = priv->barriers;
auto & rtv_handles = priv->rtv_handles;
barriers.clear ();
rtv_handles.clear ();
auto upload_data = priv->upload_data;
if (priv->is_first) {
g_assert (upload_data);
GST_DEBUG_OBJECT (self, "First loop, uploading data");
cl->CopyResource (priv->vertex_index_buf.Get (),
upload_data->vertex_index_upload.Get ());
barriers.
push_back (CD3DX12_RESOURCE_BARRIER::Transition (priv->vertex_index_buf.
Get (), D3D12_RESOURCE_STATE_COPY_DEST, STATE_VERTEX_AND_INDEX));
cl->CopyResource (priv->ps_const_buf.Get (),
upload_data->ps_const_upload.Get ());
barriers.
push_back (CD3DX12_RESOURCE_BARRIER::Transition (priv->ps_const_buf.
Get (), D3D12_RESOURCE_STATE_COPY_DEST,
D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER));
if (priv->crs->HaveLut ()) {
D3D12_TEXTURE_COPY_LOCATION src;
D3D12_TEXTURE_COPY_LOCATION dst;
g_assert (upload_data->gamma_dec_lut_upload);
g_assert (upload_data->gamma_enc_lut_upload);
g_assert (priv->gamma_dec_lut);
g_assert (priv->gamma_enc_lut);
src =
CD3DX12_TEXTURE_COPY_LOCATION (upload_data->
gamma_dec_lut_upload.Get (), priv->gamma_lut_layout);
dst = CD3DX12_TEXTURE_COPY_LOCATION (priv->gamma_dec_lut.Get ());
cl->CopyTextureRegion (&dst, 0, 0, 0, &src, nullptr);
barriers.
push_back (CD3DX12_RESOURCE_BARRIER::Transition (priv->gamma_dec_lut.
Get (), D3D12_RESOURCE_STATE_COPY_DEST,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE));
src =
CD3DX12_TEXTURE_COPY_LOCATION (upload_data->
gamma_enc_lut_upload.Get (), priv->gamma_lut_layout);
dst = CD3DX12_TEXTURE_COPY_LOCATION (priv->gamma_enc_lut.Get ());
cl->CopyTextureRegion (&dst, 0, 0, 0, &src, nullptr);
barriers.
push_back (CD3DX12_RESOURCE_BARRIER::Transition (priv->gamma_enc_lut.
Get (), D3D12_RESOURCE_STATE_COPY_DEST,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE));
}
} else if (upload_data) {
std::vector < D3D12_RESOURCE_BARRIER > pre_copy_barriers;
if (upload_data->vertex_index_upload) {
pre_copy_barriers.
push_back (CD3DX12_RESOURCE_BARRIER::
Transition (priv->vertex_index_buf.Get (), STATE_VERTEX_AND_INDEX,
D3D12_RESOURCE_STATE_COPY_DEST));
barriers.
push_back (CD3DX12_RESOURCE_BARRIER::
Transition (priv->vertex_index_buf.Get (),
D3D12_RESOURCE_STATE_COPY_DEST, STATE_VERTEX_AND_INDEX));
}
if (!pre_copy_barriers.empty ()) {
cl->ResourceBarrier (pre_copy_barriers.size (),
pre_copy_barriers.data ());
}
if (upload_data->vertex_index_upload) {
GST_DEBUG_OBJECT (self, "Vertex updated");
cl->CopyResource (priv->vertex_index_buf.Get (),
upload_data->vertex_index_upload.Get ());
}
}
auto device = gst_d3d12_device_get_device_handle (self->device);
ComPtr < ID3D12DescriptorHeap > srv_heap;
GstD3D12Descriptor *descriptor;
if (!gst_d3d12_descriptor_pool_acquire (priv->srv_heap_pool, &descriptor)) {
GST_ERROR_OBJECT (self, "Couldn't acquire srv heap");
return FALSE;
}
gst_d3d12_descriptor_get_handle (descriptor, &srv_heap);
gst_d3d12_fence_data_add_notify (fence_data, descriptor,
(GDestroyNotify) gst_d3d12_descriptor_unref);
auto cpu_handle =
CD3DX12_CPU_DESCRIPTOR_HANDLE
(srv_heap->GetCPUDescriptorHandleForHeapStart ());
for (guint i = 0; i < gst_buffer_n_memory (in_buf); i++) {
auto mem = (GstD3D12Memory *) gst_buffer_peek_memory (in_buf, i);
auto num_planes = gst_d3d12_memory_get_plane_count (mem);
ComPtr < ID3D12DescriptorHeap > mem_srv_heap;
if (!gst_d3d12_memory_get_shader_resource_view_heap (mem, &mem_srv_heap)) {
GST_ERROR_OBJECT (self, "Couldn't get SRV");
return FALSE;
}
device->CopyDescriptorsSimple (num_planes, cpu_handle,
mem_srv_heap->GetCPUDescriptorHandleForHeapStart (),
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
cpu_handle.Offset (num_planes, priv->srv_inc_size);
}
if (priv->crs->HaveLut ()) {
device->CopyDescriptorsSimple (2, cpu_handle,
priv->gamma_lut_heap->GetCPUDescriptorHandleForHeapStart (),
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
}
for (guint i = 0; i < gst_buffer_n_memory (out_buf); i++) {
auto mem = (GstD3D12Memory *) gst_buffer_peek_memory (out_buf, i);
auto num_planes = gst_d3d12_memory_get_plane_count (mem);
ComPtr < ID3D12DescriptorHeap > rtv_heap;
if (!gst_d3d12_memory_get_render_target_view_heap (mem, &rtv_heap)) {
GST_ERROR_OBJECT (self, "Couldn't get rtv heap");
return FALSE;
}
auto cpu_handle =
CD3DX12_CPU_DESCRIPTOR_HANDLE
(rtv_heap->GetCPUDescriptorHandleForHeapStart ());
for (guint plane = 0; plane < num_planes; plane++) {
rtv_handles.push_back (cpu_handle);
cpu_handle.Offset (priv->rtv_inc_size);
}
}
if (!barriers.empty ())
cl->ResourceBarrier (barriers.size (), barriers.data ());
if (priv->clear_background) {
for (size_t i = 0; i < rtv_handles.size (); i++) {
cl->ClearRenderTargetView (rtv_handles[i],
priv->clear_color[i], 0, nullptr);
}
}
auto pso = priv->quad_data[0].pso.Get ();
cl->SetGraphicsRootSignature (priv->rs.Get ());
cl->SetPipelineState (pso);
ID3D12DescriptorHeap *heaps[] = { srv_heap.Get () };
cl->SetDescriptorHeaps (1, heaps);
cl->SetGraphicsRootDescriptorTable (priv->crs->GetPsSrvIdx (),
srv_heap->GetGPUDescriptorHandleForHeapStart ());
cl->SetGraphicsRoot32BitConstants (priv->crs->GetVsRootConstIdx (),
16, &priv->transform, 0);
cl->SetGraphicsRoot32BitConstants (priv->crs->GetPsRootConstIdx (),
1, &priv->alpha, 0);
cl->SetGraphicsRootConstantBufferView (priv->crs->GetPsCbvIdx (),
priv->ps_const_buf->GetGPUVirtualAddress ());
cl->IASetIndexBuffer (&priv->idv);
cl->IASetVertexBuffers (0, 1, &priv->vbv);
cl->IASetPrimitiveTopology (D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
cl->RSSetViewports (priv->quad_data[0].num_rtv, priv->viewport);
cl->RSSetScissorRects (priv->quad_data[0].num_rtv, priv->scissor_rect);
cl->OMSetRenderTargets (priv->quad_data[0].num_rtv,
rtv_handles.data (), FALSE, nullptr);
cl->OMSetBlendFactor (priv->blend_factor);
cl->DrawIndexedInstanced (6, 1, 0, 0, 0);
pso->AddRef ();
gst_d3d12_fence_data_add_notify (fence_data, pso,
(GDestroyNotify) pso_free_func);
auto offset = priv->quad_data[0].num_rtv;
if (priv->quad_data.size () == 2) {
pso = priv->quad_data[1].pso.Get ();
cl->SetPipelineState (pso);
cl->RSSetViewports (priv->quad_data[1].num_rtv, &priv->viewport[offset]);
cl->RSSetScissorRects (priv->quad_data[1].num_rtv,
&priv->scissor_rect[offset]);
cl->OMSetRenderTargets (priv->quad_data[1].num_rtv,
rtv_handles.data () + offset, FALSE, nullptr);
cl->DrawIndexedInstanced (6, 1, 0, 0, 0);
pso->AddRef ();
gst_d3d12_fence_data_add_notify (fence_data, pso,
(GDestroyNotify) pso_free_func);
}
gst_d3d12_fence_data_add_notify (fence_data,
gst_buffer_ref (in_buf), (GDestroyNotify) gst_buffer_unref);
if (priv->upload_data) {
gst_d3d12_fence_data_add_notify (fence_data,
priv->upload_data, (GDestroyNotify) converter_upload_data_free);
}
priv->upload_data = nullptr;
priv->is_first = false;
return TRUE;
}
static gboolean
gst_d3d12_converter_map_buffer (GstBuffer * buffer,
GstMapInfo info[GST_VIDEO_MAX_PLANES], GstMapFlags flags)
{
GstMapFlags map_flags;
guint num_mapped = 0;
map_flags = (GstMapFlags) (flags | GST_MAP_D3D12);
for (num_mapped = 0; num_mapped < gst_buffer_n_memory (buffer); num_mapped++) {
auto mem = gst_buffer_peek_memory (buffer, num_mapped);
if (!gst_memory_map (mem, &info[num_mapped], map_flags))
goto error;
}
return TRUE;
error:
for (guint i = 0; i < num_mapped; i++) {
auto mem = gst_buffer_peek_memory (buffer, i);
gst_memory_unmap (mem, &info[i]);
}
return FALSE;
}
static void
gst_d3d12_converter_unmap_buffer (GstBuffer * buffer,
GstMapInfo info[GST_VIDEO_MAX_PLANES])
{
for (guint i = 0; i < gst_buffer_n_memory (buffer); i++) {
auto mem = gst_buffer_peek_memory (buffer, i);
gst_memory_unmap (mem, &info[i]);
}
}
static GstBuffer *
gst_d3d12_converter_upload_buffer (GstD3D12Converter * self, GstBuffer * in_buf)
{
GstVideoFrame in_frame, out_frame;
auto priv = self->priv;
GstBuffer *fallback_buf = nullptr;
if (!gst_video_frame_map (&in_frame, &priv->in_info, in_buf, GST_MAP_READ)) {
GST_ERROR_OBJECT (self, "Couldn't map video frame");
return nullptr;
}
if (priv->fallback_pool) {
if (priv->fallback_pool_info.width != in_frame.info.width ||
priv->fallback_pool_info.height != in_frame.info.height) {
gst_buffer_pool_set_active (priv->fallback_pool, FALSE);
gst_clear_object (&priv->fallback_pool);
}
}
if (!priv->fallback_pool) {
priv->fallback_pool = gst_d3d12_buffer_pool_new (self->device);
priv->fallback_pool_info = in_frame.info;
auto caps = gst_video_info_to_caps (&in_frame.info);
auto config = gst_buffer_pool_get_config (priv->fallback_pool);
auto params = gst_d3d12_allocation_params_new (self->device, &in_frame.info,
GST_D3D12_ALLOCATION_FLAG_DEFAULT,
D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS);
gst_buffer_pool_config_set_d3d12_allocation_params (config, params);
gst_d3d12_allocation_params_free (params);
gst_buffer_pool_config_set_params (config, caps, in_frame.info.size, 0, 0);
gst_caps_unref (caps);
if (!gst_buffer_pool_set_config (priv->fallback_pool, config)) {
GST_ERROR_OBJECT (self, "Couldn't set pool config");
gst_video_frame_unmap (&in_frame);
gst_clear_object (&priv->fallback_pool);
return nullptr;
}
if (!gst_buffer_pool_set_active (priv->fallback_pool, TRUE)) {
GST_ERROR_OBJECT (self, "Failed to set active");
gst_video_frame_unmap (&in_frame);
gst_clear_object (&priv->fallback_pool);
return nullptr;
}
}
gst_buffer_pool_acquire_buffer (priv->fallback_pool, &fallback_buf, nullptr);
if (!fallback_buf) {
GST_ERROR_OBJECT (self, "Couldn't acquire fallback buf");
gst_video_frame_unmap (&in_frame);
return nullptr;
}
if (!gst_video_frame_map (&out_frame, &priv->fallback_pool_info, fallback_buf,
GST_MAP_WRITE)) {
GST_ERROR_OBJECT (self, "Couldn't map output frame");
gst_video_frame_unmap (&in_frame);
gst_buffer_unref (fallback_buf);
return nullptr;
}
auto copy_ret = gst_video_frame_copy (&out_frame, &in_frame);
gst_video_frame_unmap (&out_frame);
gst_video_frame_unmap (&in_frame);
if (!copy_ret) {
GST_ERROR_OBJECT (self, "Couldn't copy to fallback buffer");
gst_buffer_unref (fallback_buf);
return nullptr;
}
return fallback_buf;
}
static gboolean
gst_d3d12_converter_check_needs_upload (GstD3D12Converter * self,
GstBuffer * buf)
{
auto mem = gst_buffer_peek_memory (buf, 0);
if (!gst_is_d3d12_memory (mem))
return TRUE;
auto dmem = GST_D3D12_MEMORY_CAST (mem);
if (dmem->device != self->device)
return TRUE;
auto resource = gst_d3d12_memory_get_resource_handle (dmem);
auto desc = resource->GetDesc ();
if ((desc.Flags & D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE) ==
D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE) {
return TRUE;
}
return FALSE;
}
gboolean
gst_d3d12_converter_convert_buffer (GstD3D12Converter * converter,
GstBuffer * in_buf, GstBuffer * out_buf, GstD3D12FenceData * fence_data,
ID3D12GraphicsCommandList * cl)
{
g_return_val_if_fail (GST_IS_D3D12_CONVERTER (converter), FALSE);
g_return_val_if_fail (GST_IS_BUFFER (in_buf), FALSE);
g_return_val_if_fail (GST_IS_BUFFER (out_buf), FALSE);
g_return_val_if_fail (fence_data, FALSE);
g_return_val_if_fail (cl, FALSE);
GstMapInfo in_info[GST_VIDEO_MAX_PLANES];
gboolean need_upload = gst_d3d12_converter_check_needs_upload (converter,
in_buf);
if (need_upload) {
in_buf = gst_d3d12_converter_upload_buffer (converter, in_buf);
if (!in_buf)
return FALSE;
}
if (!gst_d3d12_converter_map_buffer (in_buf, in_info, GST_MAP_READ)) {
GST_ERROR_OBJECT (converter, "Couldn't map input buffer");
if (need_upload)
gst_buffer_unref (in_buf);
return FALSE;
}
gst_d3d12_converter_unmap_buffer (in_buf, in_info);
auto ret = gst_d3d12_converter_execute (converter,
in_buf, out_buf, fence_data, cl);
/* fence data will hold this buffer */
if (need_upload)
gst_buffer_unref (in_buf);
return ret;
}
gboolean
gst_d3d12_converter_update_blend_state (GstD3D12Converter * converter,
const D3D12_BLEND_DESC * blend_desc, const gfloat blend_factor[4])
{
g_return_val_if_fail (GST_IS_D3D12_CONVERTER (converter), FALSE);
auto priv = converter->priv;
std::lock_guard < std::mutex > lk (priv->prop_lock);
D3D12_BLEND_DESC new_blend = CD3DX12_BLEND_DESC (D3D12_DEFAULT);
if (blend_desc)
new_blend = *blend_desc;
if (memcmp (&priv->blend_desc, &new_blend, sizeof (D3D12_BLEND_DESC)) != 0)
priv->update_pso = TRUE;
if (blend_factor) {
for (guint i = 0; i < 4; i++)
priv->blend_factor[i] = blend_factor[i];
} else {
for (guint i = 0; i < 4; i++)
priv->blend_factor[i] = 1.0f;
}
return TRUE;
}