mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-19 06:46:38 +00:00
0788492461
In some cases, rendering and dxgi (e.g., swapchain) APIs should be called from window message pump thread, but current design (dedicated d3d11 thread) make it impossible. To solve it, change concurrency model to locking based one from single-thread model.
931 lines
26 KiB
C
931 lines
26 KiB
C
/* GStreamer
|
|
* Copyright (C) <2019> Seungha Yang <seungha.yang@navercorp.com>
|
|
* Copyright (C) <2019> Jeongki Kim <jeongki.kim@jeongki.kim>
|
|
*
|
|
* 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 "gstd3d11colorconverter.h"
|
|
#include "gstd3d11utils.h"
|
|
#include "gstd3d11device.h"
|
|
#include "gstd3d11shader.h"
|
|
#include "gstd3d11format.h"
|
|
|
|
#include <string.h>
|
|
|
|
GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_colorconverter_debug);
|
|
#define GST_CAT_DEFAULT gst_d3d11_colorconverter_debug
|
|
|
|
/* *INDENT-OFF* */
|
|
typedef struct
|
|
{
|
|
FLOAT trans_matrix[12];
|
|
FLOAT padding[4];
|
|
} PixelShaderColorTransform;
|
|
|
|
typedef struct
|
|
{
|
|
struct {
|
|
FLOAT x;
|
|
FLOAT y;
|
|
FLOAT z;
|
|
} position;
|
|
struct {
|
|
FLOAT x;
|
|
FLOAT y;
|
|
} texture;
|
|
} VertexData;
|
|
|
|
typedef struct
|
|
{
|
|
const gchar *constant_buffer;
|
|
const gchar *func;
|
|
} PixelShaderTemplate;
|
|
|
|
#define COLOR_TRANSFORM_COEFF \
|
|
"cbuffer PixelShaderColorTransform : register(b0)\n" \
|
|
"{\n" \
|
|
" float3x4 trans_matrix;\n" \
|
|
" float3 padding;\n" \
|
|
"};\n"
|
|
|
|
#define HLSL_FUNC_YUV_TO_RGB \
|
|
"float3 yuv_to_rgb (float3 yuv)\n" \
|
|
"{\n" \
|
|
" yuv += float3(-0.062745f, -0.501960f, -0.501960f);\n" \
|
|
" yuv = mul(yuv, trans_matrix);\n" \
|
|
" return saturate(yuv);\n" \
|
|
"}\n"
|
|
|
|
#define HLSL_FUNC_RGB_TO_YUV \
|
|
"float3 rgb_to_yuv (float3 rgb)\n" \
|
|
"{\n" \
|
|
" float3 yuv;\n" \
|
|
" yuv = mul(rgb, trans_matrix);\n" \
|
|
" yuv += float3(0.062745f, 0.501960f, 0.501960f);\n" \
|
|
" return saturate(yuv);\n" \
|
|
"}\n"
|
|
|
|
static const PixelShaderTemplate templ_REORDER =
|
|
{ NULL, NULL };
|
|
|
|
static const PixelShaderTemplate templ_YUV_to_RGB =
|
|
{ COLOR_TRANSFORM_COEFF, HLSL_FUNC_YUV_TO_RGB };
|
|
|
|
#if 0
|
|
static const PixelShaderTemplate templ_RGB_to_YUV =
|
|
{ COLOR_TRANSFORM_COEFF, HLSL_FUNC_RGB_TO_YUV };
|
|
#endif
|
|
|
|
static const gchar templ_REORDER_BODY[] =
|
|
" float4 sample;\n"
|
|
" sample = shaderTexture[0].Sample(samplerState, input.Texture);\n"
|
|
/* alpha channel */
|
|
" %s\n"
|
|
" return sample;\n";
|
|
|
|
static const gchar templ_VUYA_to_RGB_BODY[] =
|
|
" float4 sample, rgba;\n"
|
|
" sample.x = shaderTexture[0].Sample(samplerState, input.Texture).z;\n"
|
|
" sample.y = shaderTexture[0].Sample(samplerState, input.Texture).y;\n"
|
|
" sample.z = shaderTexture[0].Sample(samplerState, input.Texture).x;\n"
|
|
" sample.a = shaderTexture[0].Sample(samplerState, input.Texture).a;\n"
|
|
" rgba.rgb = yuv_to_rgb (sample.xyz);\n"
|
|
" rgba.a = sample.a;\n"
|
|
" return rgba;\n";
|
|
|
|
#if 0
|
|
static const gchar templ_RGB_to_VUYA_BODY[] =
|
|
" float4 sample, vuya;\n"
|
|
" sample = shaderTexture[0].Sample(samplerState, input.Texture);\n"
|
|
" vuya.zyx = rgb_to_yuv (sample.rgb);\n"
|
|
" vuya.a = %s;\n"
|
|
" return vuya;\n";
|
|
#endif
|
|
|
|
/* YUV to RGB conversion */
|
|
static const gchar templ_PLANAR_YUV_to_RGB_BODY[] =
|
|
" float4 sample, rgba;\n"
|
|
" sample.x = shaderTexture[0].Sample(samplerState, input.Texture).x;\n"
|
|
" sample.y = shaderTexture[1].Sample(samplerState, input.Texture).x;\n"
|
|
" sample.z = shaderTexture[2].Sample(samplerState, input.Texture).x;\n"
|
|
" rgba.rgb = yuv_to_rgb (sample.xyz);\n"
|
|
" rgba.a = 1.0;\n"
|
|
" return rgba;\n";
|
|
|
|
static const gchar templ_PLANAR_YUV_HIGH_to_RGB_BODY[] =
|
|
" float4 sample, rgba;\n"
|
|
" sample.x = shaderTexture[0].Sample(samplerState, input.Texture).x * %d;\n"
|
|
" sample.y = shaderTexture[1].Sample(samplerState, input.Texture).x * %d;\n"
|
|
" sample.z = shaderTexture[2].Sample(samplerState, input.Texture).x * %d;\n"
|
|
" rgba.rgb = yuv_to_rgb (sample.xyz);\n"
|
|
" rgba.a = 1.0;\n"
|
|
" return rgba;\n";
|
|
|
|
/* FIXME: add RGB to planar */
|
|
|
|
static const gchar templ_SEMI_PLANAR_to_RGB_BODY[] =
|
|
" float4 sample, rgba;\n"
|
|
" sample.x = shaderTexture[0].Sample(samplerState, input.Texture).x;\n"
|
|
" sample.yz = shaderTexture[1].Sample(samplerState, input.Texture).xy;\n"
|
|
" rgba.rgb = yuv_to_rgb (sample.xyz);\n"
|
|
" rgba.a = 1.0;\n"
|
|
" return rgba;\n";
|
|
|
|
/* FIXME: add RGB to semi-planar */
|
|
|
|
static const gchar templ_pixel_shader[] =
|
|
/* constant buffer */
|
|
"%s\n"
|
|
"Texture2D shaderTexture[4];\n"
|
|
"SamplerState samplerState;\n"
|
|
"\n"
|
|
"struct PS_INPUT\n"
|
|
"{\n"
|
|
" float4 Position: SV_POSITION;\n"
|
|
" float3 Texture: TEXCOORD0;\n"
|
|
"};\n"
|
|
"\n"
|
|
/* rgb <-> yuv function */
|
|
"%s\n"
|
|
"float4 main(PS_INPUT input): SV_TARGET\n"
|
|
"{\n"
|
|
"%s"
|
|
"}\n";
|
|
|
|
static const gchar templ_vertex_shader[] =
|
|
"struct VS_INPUT\n"
|
|
"{\n"
|
|
" float4 Position : POSITION;\n"
|
|
" float4 Texture : TEXCOORD0;\n"
|
|
"};\n"
|
|
"\n"
|
|
"struct VS_OUTPUT\n"
|
|
"{\n"
|
|
" float4 Position: SV_POSITION;\n"
|
|
" float4 Texture: TEXCOORD0;\n"
|
|
"};\n"
|
|
"\n"
|
|
"VS_OUTPUT main(VS_INPUT input)\n"
|
|
"{\n"
|
|
" return input;\n"
|
|
"}\n";
|
|
|
|
/* *INDENT-ON* */
|
|
|
|
typedef struct
|
|
{
|
|
const PixelShaderTemplate *templ;
|
|
gchar *ps_body;
|
|
PixelShaderColorTransform transform;
|
|
} ConvertInfo;
|
|
|
|
struct _GstD3D11ColorConverter
|
|
{
|
|
GstD3D11Device *device;
|
|
GstVideoInfo in_info;
|
|
GstVideoInfo out_info;
|
|
|
|
const GstD3D11Format *in_d3d11_format;
|
|
const GstD3D11Format *out_d3d11_format;
|
|
|
|
guint num_input_view;
|
|
guint num_output_view;
|
|
|
|
GstD3D11Quad *quad;
|
|
|
|
D3D11_VIEWPORT viewport;
|
|
|
|
ConvertInfo convert_info;
|
|
};
|
|
|
|
/* from video-converter.c */
|
|
typedef struct
|
|
{
|
|
gfloat dm[4][4];
|
|
} MatrixData;
|
|
|
|
static void
|
|
color_matrix_set_identity (MatrixData * m)
|
|
{
|
|
gint i, j;
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
for (j = 0; j < 4; j++) {
|
|
m->dm[i][j] = (i == j);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
color_matrix_copy (MatrixData * d, const MatrixData * s)
|
|
{
|
|
gint i, j;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
for (j = 0; j < 4; j++)
|
|
d->dm[i][j] = s->dm[i][j];
|
|
}
|
|
|
|
/* Perform 4x4 matrix multiplication:
|
|
* - @dst@ = @a@ * @b@
|
|
* - @dst@ may be a pointer to @a@ andor @b@
|
|
*/
|
|
static void
|
|
color_matrix_multiply (MatrixData * dst, MatrixData * a, MatrixData * b)
|
|
{
|
|
MatrixData tmp;
|
|
gint i, j, k;
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
for (j = 0; j < 4; j++) {
|
|
gfloat x = 0;
|
|
for (k = 0; k < 4; k++) {
|
|
x += a->dm[i][k] * b->dm[k][j];
|
|
}
|
|
tmp.dm[i][j] = x;
|
|
}
|
|
}
|
|
color_matrix_copy (dst, &tmp);
|
|
}
|
|
|
|
static void
|
|
color_matrix_offset_components (MatrixData * m, gfloat a1, gfloat a2, gfloat a3)
|
|
{
|
|
MatrixData a;
|
|
|
|
color_matrix_set_identity (&a);
|
|
a.dm[0][3] = a1;
|
|
a.dm[1][3] = a2;
|
|
a.dm[2][3] = a3;
|
|
color_matrix_multiply (m, &a, m);
|
|
}
|
|
|
|
static void
|
|
color_matrix_scale_components (MatrixData * m, gfloat a1, gfloat a2, gfloat a3)
|
|
{
|
|
MatrixData a;
|
|
|
|
color_matrix_set_identity (&a);
|
|
a.dm[0][0] = a1;
|
|
a.dm[1][1] = a2;
|
|
a.dm[2][2] = a3;
|
|
color_matrix_multiply (m, &a, m);
|
|
}
|
|
|
|
static void
|
|
color_matrix_debug (GstD3D11ColorConverter * self, const MatrixData * s)
|
|
{
|
|
GST_DEBUG ("[%f %f %f %f]",
|
|
s->dm[0][0], s->dm[0][1], s->dm[0][2], s->dm[0][3]);
|
|
GST_DEBUG ("[%f %f %f %f]",
|
|
s->dm[1][0], s->dm[1][1], s->dm[1][2], s->dm[1][3]);
|
|
GST_DEBUG ("[%f %f %f %f]",
|
|
s->dm[2][0], s->dm[2][1], s->dm[2][2], s->dm[2][3]);
|
|
GST_DEBUG ("[%f %f %f %f]",
|
|
s->dm[3][0], s->dm[3][1], s->dm[3][2], s->dm[3][3]);
|
|
}
|
|
|
|
static void
|
|
color_matrix_YCbCr_to_RGB (MatrixData * m, gfloat Kr, gfloat Kb)
|
|
{
|
|
gfloat Kg = 1.0 - Kr - Kb;
|
|
MatrixData k = {
|
|
{
|
|
{1., 0., 2 * (1 - Kr), 0.},
|
|
{1., -2 * Kb * (1 - Kb) / Kg, -2 * Kr * (1 - Kr) / Kg, 0.},
|
|
{1., 2 * (1 - Kb), 0., 0.},
|
|
{0., 0., 0., 1.},
|
|
}
|
|
};
|
|
|
|
color_matrix_multiply (m, &k, m);
|
|
}
|
|
|
|
static void
|
|
color_matrix_RGB_to_YCbCr (MatrixData * m, gfloat Kr, gfloat Kb)
|
|
{
|
|
gfloat Kg = 1.0 - Kr - Kb;
|
|
MatrixData k;
|
|
gfloat x;
|
|
|
|
k.dm[0][0] = Kr;
|
|
k.dm[0][1] = Kg;
|
|
k.dm[0][2] = Kb;
|
|
k.dm[0][3] = 0;
|
|
|
|
x = 1 / (2 * (1 - Kb));
|
|
k.dm[1][0] = -x * Kr;
|
|
k.dm[1][1] = -x * Kg;
|
|
k.dm[1][2] = x * (1 - Kb);
|
|
k.dm[1][3] = 0;
|
|
|
|
x = 1 / (2 * (1 - Kr));
|
|
k.dm[2][0] = x * (1 - Kr);
|
|
k.dm[2][1] = -x * Kg;
|
|
k.dm[2][2] = -x * Kb;
|
|
k.dm[2][3] = 0;
|
|
|
|
k.dm[3][0] = 0;
|
|
k.dm[3][1] = 0;
|
|
k.dm[3][2] = 0;
|
|
k.dm[3][3] = 1;
|
|
|
|
color_matrix_multiply (m, &k, m);
|
|
}
|
|
|
|
static void
|
|
compute_matrix_to_RGB (GstD3D11ColorConverter * self, MatrixData * data,
|
|
GstVideoInfo * info)
|
|
{
|
|
gdouble Kr = 0, Kb = 0;
|
|
gint offset[4], scale[4];
|
|
|
|
/* bring color components to [0..1.0] range */
|
|
gst_video_color_range_offsets (info->colorimetry.range, info->finfo, offset,
|
|
scale);
|
|
|
|
color_matrix_offset_components (data, -offset[0], -offset[1], -offset[2]);
|
|
color_matrix_scale_components (data, 1 / ((float) scale[0]),
|
|
1 / ((float) scale[1]), 1 / ((float) scale[2]));
|
|
|
|
if (!GST_VIDEO_INFO_IS_RGB (info)) {
|
|
/* bring components to R'G'B' space */
|
|
if (gst_video_color_matrix_get_Kr_Kb (info->colorimetry.matrix, &Kr, &Kb))
|
|
color_matrix_YCbCr_to_RGB (data, Kr, Kb);
|
|
}
|
|
color_matrix_debug (self, data);
|
|
}
|
|
|
|
static void
|
|
compute_matrix_to_YUV (GstD3D11ColorConverter * self, MatrixData * data,
|
|
GstVideoInfo * info)
|
|
{
|
|
gdouble Kr = 0, Kb = 0;
|
|
gint offset[4], scale[4];
|
|
|
|
if (!GST_VIDEO_INFO_IS_RGB (info)) {
|
|
/* bring components to YCbCr space */
|
|
if (gst_video_color_matrix_get_Kr_Kb (info->colorimetry.matrix, &Kr, &Kb))
|
|
color_matrix_RGB_to_YCbCr (data, Kr, Kb);
|
|
}
|
|
|
|
/* bring color components to nominal range */
|
|
gst_video_color_range_offsets (info->colorimetry.range, info->finfo, offset,
|
|
scale);
|
|
|
|
color_matrix_scale_components (data, (float) scale[0], (float) scale[1],
|
|
(float) scale[2]);
|
|
color_matrix_offset_components (data, offset[0], offset[1], offset[2]);
|
|
|
|
color_matrix_debug (self, data);
|
|
}
|
|
|
|
static gboolean
|
|
converter_get_matrix (GstD3D11ColorConverter * self, MatrixData * matrix,
|
|
GstVideoInfo * in_info, GstVideoInfo * out_info)
|
|
{
|
|
gboolean same_matrix;
|
|
guint in_bits, out_bits;
|
|
|
|
in_bits = GST_VIDEO_INFO_COMP_DEPTH (in_info, 0);
|
|
out_bits = GST_VIDEO_INFO_COMP_DEPTH (out_info, 0);
|
|
|
|
same_matrix = in_info->colorimetry.matrix == out_info->colorimetry.matrix;
|
|
|
|
GST_DEBUG ("matrix %d -> %d (%d)", in_info->colorimetry.matrix,
|
|
out_info->colorimetry.matrix, same_matrix);
|
|
|
|
color_matrix_set_identity (matrix);
|
|
|
|
if (same_matrix) {
|
|
GST_DEBUG ("conversion matrix is not required");
|
|
return FALSE;
|
|
}
|
|
|
|
if (in_bits < out_bits) {
|
|
gint scale = 1 << (out_bits - in_bits);
|
|
color_matrix_scale_components (matrix,
|
|
1 / (float) scale, 1 / (float) scale, 1 / (float) scale);
|
|
}
|
|
|
|
GST_DEBUG ("to RGB matrix");
|
|
compute_matrix_to_RGB (self, matrix, in_info);
|
|
GST_DEBUG ("current matrix");
|
|
color_matrix_debug (self, matrix);
|
|
|
|
GST_DEBUG ("to YUV matrix");
|
|
compute_matrix_to_YUV (self, matrix, out_info);
|
|
GST_DEBUG ("current matrix");
|
|
color_matrix_debug (self, matrix);
|
|
|
|
if (in_bits > out_bits) {
|
|
gint scale = 1 << (in_bits - out_bits);
|
|
color_matrix_scale_components (matrix,
|
|
(float) scale, (float) scale, (float) scale);
|
|
}
|
|
|
|
GST_DEBUG ("final matrix");
|
|
color_matrix_debug (self, matrix);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
setup_convert_info_rgb_to_rgb (GstD3D11ColorConverter * self,
|
|
const GstVideoInfo * in_info, const GstVideoInfo * out_info)
|
|
{
|
|
ConvertInfo *convert_info = &self->convert_info;
|
|
GstVideoFormat in_format = GST_VIDEO_INFO_FORMAT (in_info);
|
|
|
|
#define IS_RGBX_FORMAT(f) \
|
|
((f) == GST_VIDEO_FORMAT_RGBx || \
|
|
(f) == GST_VIDEO_FORMAT_xRGB || \
|
|
(f) == GST_VIDEO_FORMAT_BGRx || \
|
|
(f) == GST_VIDEO_FORMAT_xBGR)
|
|
|
|
convert_info->templ = &templ_REORDER;
|
|
convert_info->ps_body = g_strdup_printf (templ_REORDER_BODY,
|
|
IS_RGBX_FORMAT (in_format) ? "sample.a = 1.0f;" : "");
|
|
|
|
#undef IS_RGBX_FORMAT
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
setup_convert_info_yuv_to_rgb (GstD3D11ColorConverter * self,
|
|
const GstVideoInfo * in_info, const GstVideoInfo * out_info)
|
|
{
|
|
ConvertInfo *info = &self->convert_info;
|
|
|
|
info->templ = &templ_YUV_to_RGB;
|
|
|
|
switch (GST_VIDEO_INFO_FORMAT (in_info)) {
|
|
case GST_VIDEO_FORMAT_VUYA:
|
|
info->ps_body = g_strdup_printf (templ_VUYA_to_RGB_BODY);
|
|
break;
|
|
case GST_VIDEO_FORMAT_I420:
|
|
info->ps_body = g_strdup_printf (templ_PLANAR_YUV_to_RGB_BODY);
|
|
break;
|
|
case GST_VIDEO_FORMAT_I420_10LE:
|
|
info->ps_body =
|
|
g_strdup_printf (templ_PLANAR_YUV_HIGH_to_RGB_BODY, 64, 64, 64);
|
|
break;
|
|
case GST_VIDEO_FORMAT_NV12:
|
|
case GST_VIDEO_FORMAT_P010_10LE:
|
|
info->ps_body = g_strdup_printf (templ_SEMI_PLANAR_to_RGB_BODY);
|
|
break;
|
|
default:
|
|
GST_FIXME_OBJECT (self,
|
|
"Unhandled input format %s",
|
|
gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (in_info)));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
setup_convert_info_rgb_to_yuv (GstD3D11ColorConverter * self,
|
|
const GstVideoInfo * in_info, const GstVideoInfo * out_info)
|
|
{
|
|
GST_FIXME ("Implement RGB to YUV format conversion");
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
setup_convert_info_yuv_to_yuv (GstD3D11ColorConverter * self,
|
|
const GstVideoInfo * in_info, const GstVideoInfo * out_info)
|
|
{
|
|
GST_FIXME ("Implement YUV to YUV format conversion");
|
|
return FALSE;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
GstD3D11ColorConverter *self;
|
|
GstVideoInfo *in_info;
|
|
GstVideoInfo *out_info;
|
|
gboolean ret;
|
|
} SetupShaderData;
|
|
|
|
static void
|
|
gst_d3d11_color_convert_setup_shader (GstD3D11Device * device,
|
|
SetupShaderData * data)
|
|
{
|
|
GstD3D11ColorConverter *self = data->self;
|
|
HRESULT hr;
|
|
D3D11_SAMPLER_DESC sampler_desc = { 0, };
|
|
D3D11_INPUT_ELEMENT_DESC input_desc[2] = { 0, };
|
|
D3D11_BUFFER_DESC buffer_desc = { 0, };
|
|
D3D11_MAPPED_SUBRESOURCE map;
|
|
VertexData *vertex_data;
|
|
WORD *indices;
|
|
ID3D11Device *device_handle;
|
|
ID3D11DeviceContext *context_handle;
|
|
gchar *shader_code = NULL;
|
|
ConvertInfo *convert_info = &self->convert_info;
|
|
ID3D11PixelShader *ps = NULL;
|
|
ID3D11VertexShader *vs = NULL;
|
|
ID3D11InputLayout *layout = NULL;
|
|
ID3D11SamplerState *sampler = NULL;
|
|
ID3D11Buffer *const_buffer = NULL;
|
|
ID3D11Buffer *vertex_buffer = NULL;
|
|
ID3D11Buffer *index_buffer = NULL;
|
|
const guint index_count = 2 * 3;
|
|
|
|
data->ret = TRUE;
|
|
|
|
device_handle = gst_d3d11_device_get_device_handle (device);
|
|
context_handle = gst_d3d11_device_get_device_context_handle (device);
|
|
|
|
/* bilinear filtering */
|
|
sampler_desc.Filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT;
|
|
sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
|
|
sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
|
|
sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
|
|
sampler_desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
|
|
sampler_desc.MinLOD = 0;
|
|
sampler_desc.MaxLOD = D3D11_FLOAT32_MAX;
|
|
|
|
hr = ID3D11Device_CreateSamplerState (device_handle, &sampler_desc, &sampler);
|
|
if (!gst_d3d11_result (hr)) {
|
|
GST_ERROR ("Couldn't create sampler state, hr: 0x%x", (guint) hr);
|
|
data->ret = FALSE;
|
|
goto clear;
|
|
}
|
|
|
|
shader_code = g_strdup_printf (templ_pixel_shader,
|
|
convert_info->templ->constant_buffer ?
|
|
convert_info->templ->constant_buffer : "",
|
|
convert_info->templ->func ? convert_info->templ->func : "",
|
|
convert_info->ps_body);
|
|
|
|
GST_LOG ("Create Pixel Shader \n%s", shader_code);
|
|
|
|
if (!gst_d3d11_create_pixel_shader (device, shader_code, &ps)) {
|
|
GST_ERROR ("Couldn't create pixel shader");
|
|
|
|
g_free (shader_code);
|
|
data->ret = FALSE;
|
|
goto clear;
|
|
}
|
|
|
|
g_free (shader_code);
|
|
|
|
if (convert_info->templ->constant_buffer) {
|
|
D3D11_BUFFER_DESC const_buffer_desc = { 0, };
|
|
|
|
const_buffer_desc.Usage = D3D11_USAGE_DYNAMIC;
|
|
const_buffer_desc.ByteWidth = sizeof (PixelShaderColorTransform);
|
|
const_buffer_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
|
|
const_buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
|
const_buffer_desc.MiscFlags = 0;
|
|
const_buffer_desc.StructureByteStride = 0;
|
|
|
|
hr = ID3D11Device_CreateBuffer (device_handle, &const_buffer_desc, NULL,
|
|
&const_buffer);
|
|
|
|
if (!gst_d3d11_result (hr)) {
|
|
GST_ERROR ("Couldn't create constant buffer, hr: 0x%x", (guint) hr);
|
|
data->ret = FALSE;
|
|
goto clear;
|
|
}
|
|
|
|
gst_d3d11_device_lock (device);
|
|
hr = ID3D11DeviceContext_Map (context_handle,
|
|
(ID3D11Resource *) const_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
|
|
|
|
if (!gst_d3d11_result (hr)) {
|
|
GST_ERROR ("Couldn't map constant buffer, hr: 0x%x", (guint) hr);
|
|
data->ret = FALSE;
|
|
gst_d3d11_device_unlock (device);
|
|
goto clear;
|
|
}
|
|
|
|
memcpy (map.pData, &convert_info->transform,
|
|
sizeof (PixelShaderColorTransform));
|
|
|
|
ID3D11DeviceContext_Unmap (context_handle,
|
|
(ID3D11Resource *) const_buffer, 0);
|
|
gst_d3d11_device_unlock (device);
|
|
}
|
|
|
|
input_desc[0].SemanticName = "POSITION";
|
|
input_desc[0].SemanticIndex = 0;
|
|
input_desc[0].Format = DXGI_FORMAT_R32G32B32_FLOAT;
|
|
input_desc[0].InputSlot = 0;
|
|
input_desc[0].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
|
|
input_desc[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
|
|
input_desc[0].InstanceDataStepRate = 0;
|
|
|
|
input_desc[1].SemanticName = "TEXCOORD";
|
|
input_desc[1].SemanticIndex = 0;
|
|
input_desc[1].Format = DXGI_FORMAT_R32G32_FLOAT;
|
|
input_desc[1].InputSlot = 0;
|
|
input_desc[1].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
|
|
input_desc[1].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
|
|
input_desc[1].InstanceDataStepRate = 0;
|
|
|
|
if (!gst_d3d11_create_vertex_shader (device, templ_vertex_shader,
|
|
input_desc, G_N_ELEMENTS (input_desc), &vs, &layout)) {
|
|
GST_ERROR ("Couldn't vertex pixel shader");
|
|
data->ret = FALSE;
|
|
goto clear;
|
|
}
|
|
|
|
/* setup vertext buffer and index buffer */
|
|
buffer_desc.Usage = D3D11_USAGE_DYNAMIC;
|
|
buffer_desc.ByteWidth = sizeof (VertexData) * 4;
|
|
buffer_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
|
|
buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
|
|
|
hr = ID3D11Device_CreateBuffer (device_handle, &buffer_desc, NULL,
|
|
&vertex_buffer);
|
|
|
|
if (!gst_d3d11_result (hr)) {
|
|
GST_ERROR ("Couldn't create vertex buffer, hr: 0x%x", (guint) hr);
|
|
data->ret = FALSE;
|
|
goto clear;
|
|
}
|
|
|
|
buffer_desc.Usage = D3D11_USAGE_DYNAMIC;
|
|
buffer_desc.ByteWidth = sizeof (WORD) * index_count;
|
|
buffer_desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
|
|
buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
|
|
|
hr = ID3D11Device_CreateBuffer (device_handle, &buffer_desc, NULL,
|
|
&index_buffer);
|
|
|
|
if (!gst_d3d11_result (hr)) {
|
|
GST_ERROR ("Couldn't create index buffer, hr: 0x%x", (guint) hr);
|
|
data->ret = FALSE;
|
|
goto clear;
|
|
}
|
|
|
|
gst_d3d11_device_lock (device);
|
|
hr = ID3D11DeviceContext_Map (context_handle,
|
|
(ID3D11Resource *) vertex_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
|
|
|
|
if (!gst_d3d11_result (hr)) {
|
|
GST_ERROR ("Couldn't map vertex buffer, hr: 0x%x", (guint) hr);
|
|
data->ret = FALSE;
|
|
gst_d3d11_device_unlock (device);
|
|
goto clear;
|
|
}
|
|
|
|
vertex_data = (VertexData *) map.pData;
|
|
|
|
hr = ID3D11DeviceContext_Map (context_handle,
|
|
(ID3D11Resource *) index_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
|
|
|
|
if (!gst_d3d11_result (hr)) {
|
|
GST_ERROR ("Couldn't map index buffer, hr: 0x%x", (guint) hr);
|
|
ID3D11DeviceContext_Unmap (context_handle,
|
|
(ID3D11Resource *) vertex_buffer, 0);
|
|
gst_d3d11_device_unlock (device);
|
|
data->ret = FALSE;
|
|
goto clear;
|
|
}
|
|
|
|
indices = (WORD *) map.pData;
|
|
|
|
/* 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.x = 0.0f;
|
|
vertex_data[0].texture.y = 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.x = 0.0f;
|
|
vertex_data[1].texture.y = 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.x = 1.0f;
|
|
vertex_data[2].texture.y = 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.x = 1.0f;
|
|
vertex_data[3].texture.y = 1.0f;
|
|
|
|
/* clockwise indexing */
|
|
indices[0] = 0; /* bottom left */
|
|
indices[1] = 1; /* top left */
|
|
indices[2] = 2; /* top right */
|
|
|
|
indices[3] = 3; /* bottom right */
|
|
indices[4] = 0; /* bottom left */
|
|
indices[5] = 2; /* top right */
|
|
|
|
ID3D11DeviceContext_Unmap (context_handle,
|
|
(ID3D11Resource *) vertex_buffer, 0);
|
|
ID3D11DeviceContext_Unmap (context_handle,
|
|
(ID3D11Resource *) index_buffer, 0);
|
|
gst_d3d11_device_unlock (device);
|
|
|
|
self->quad = gst_d3d11_quad_new (device,
|
|
ps, vs, layout, sampler, const_buffer, vertex_buffer, sizeof (VertexData),
|
|
index_buffer, DXGI_FORMAT_R16_UINT, index_count);
|
|
|
|
self->num_input_view = GST_VIDEO_INFO_N_PLANES (data->in_info);
|
|
self->num_output_view = GST_VIDEO_INFO_N_PLANES (data->out_info);
|
|
|
|
clear:
|
|
if (ps)
|
|
ID3D11PixelShader_Release (ps);
|
|
if (vs)
|
|
ID3D11VertexShader_Release (vs);
|
|
if (layout)
|
|
ID3D11InputLayout_Release (layout);
|
|
if (sampler)
|
|
ID3D11SamplerState_Release (sampler);
|
|
if (const_buffer)
|
|
ID3D11Buffer_Release (const_buffer);
|
|
if (vertex_buffer)
|
|
ID3D11Buffer_Release (vertex_buffer);
|
|
if (index_buffer)
|
|
ID3D11Buffer_Release (index_buffer);
|
|
|
|
return;
|
|
}
|
|
|
|
GstD3D11ColorConverter *
|
|
gst_d3d11_color_converter_new (GstD3D11Device * device,
|
|
GstVideoInfo * in_info, GstVideoInfo * out_info)
|
|
{
|
|
SetupShaderData data;
|
|
const GstVideoInfo *unknown_info;
|
|
const GstD3D11Format *in_d3d11_format;
|
|
const GstD3D11Format *out_d3d11_format;
|
|
gboolean is_supported = FALSE;
|
|
MatrixData matrix;
|
|
GstD3D11ColorConverter *converter = NULL;
|
|
|
|
g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
|
|
g_return_val_if_fail (in_info != NULL, NULL);
|
|
g_return_val_if_fail (out_info != NULL, NULL);
|
|
|
|
GST_DEBUG ("Setup convert with format %s -> %s",
|
|
gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (in_info)),
|
|
gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (out_info)));
|
|
|
|
in_d3d11_format = gst_d3d11_format_from_gst (GST_VIDEO_INFO_FORMAT (in_info));
|
|
if (!in_d3d11_format) {
|
|
unknown_info = in_info;
|
|
goto format_unknown;
|
|
}
|
|
|
|
out_d3d11_format =
|
|
gst_d3d11_format_from_gst (GST_VIDEO_INFO_FORMAT (out_info));
|
|
if (!out_d3d11_format) {
|
|
unknown_info = out_info;
|
|
goto format_unknown;
|
|
}
|
|
|
|
converter = g_new0 (GstD3D11ColorConverter, 1);
|
|
converter->device = gst_object_ref (device);
|
|
|
|
if (GST_VIDEO_INFO_IS_RGB (in_info)) {
|
|
if (GST_VIDEO_INFO_IS_RGB (out_info)) {
|
|
is_supported =
|
|
setup_convert_info_rgb_to_rgb (converter, in_info, out_info);
|
|
} else if (GST_VIDEO_INFO_IS_YUV (out_info)) {
|
|
is_supported =
|
|
setup_convert_info_rgb_to_yuv (converter, in_info, out_info);
|
|
}
|
|
} else if (GST_VIDEO_INFO_IS_YUV (in_info)) {
|
|
if (GST_VIDEO_INFO_IS_RGB (out_info)) {
|
|
is_supported =
|
|
setup_convert_info_yuv_to_rgb (converter, in_info, out_info);
|
|
} else if (GST_VIDEO_INFO_IS_YUV (out_info)) {
|
|
is_supported =
|
|
setup_convert_info_yuv_to_yuv (converter, in_info, out_info);
|
|
}
|
|
}
|
|
|
|
if (!is_supported) {
|
|
goto conversion_not_supported;
|
|
}
|
|
|
|
if (converter_get_matrix (converter, &matrix, in_info, out_info)) {
|
|
PixelShaderColorTransform *transform = &converter->convert_info.transform;
|
|
|
|
/* padding the last column for 16bytes alignment */
|
|
transform->trans_matrix[0] = matrix.dm[0][0];
|
|
transform->trans_matrix[1] = matrix.dm[0][1];
|
|
transform->trans_matrix[2] = matrix.dm[0][2];
|
|
transform->trans_matrix[3] = 0;
|
|
transform->trans_matrix[4] = matrix.dm[1][0];
|
|
transform->trans_matrix[5] = matrix.dm[1][1];
|
|
transform->trans_matrix[6] = matrix.dm[1][2];
|
|
transform->trans_matrix[7] = 0;
|
|
transform->trans_matrix[8] = matrix.dm[2][0];
|
|
transform->trans_matrix[9] = matrix.dm[2][1];
|
|
transform->trans_matrix[10] = matrix.dm[2][2];
|
|
transform->trans_matrix[11] = 0;
|
|
}
|
|
|
|
converter->viewport.TopLeftX = 0;
|
|
converter->viewport.TopLeftY = 0;
|
|
converter->viewport.Width = GST_VIDEO_INFO_WIDTH (out_info);
|
|
converter->viewport.Height = GST_VIDEO_INFO_HEIGHT (out_info);
|
|
converter->viewport.MinDepth = 0.0f;
|
|
converter->viewport.MaxDepth = 1.0f;
|
|
|
|
data.self = converter;
|
|
data.in_info = in_info;
|
|
data.out_info = out_info;
|
|
gst_d3d11_color_convert_setup_shader (device, &data);
|
|
|
|
if (!data.ret || !converter->quad) {
|
|
GST_ERROR ("Couldn't setup shader");
|
|
gst_d3d11_color_converter_free (converter);
|
|
converter = NULL;
|
|
}
|
|
|
|
return converter;
|
|
|
|
/* ERRORS */
|
|
format_unknown:
|
|
{
|
|
GST_ERROR ("%s couldn't be converted to d3d11 format",
|
|
gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (unknown_info)));
|
|
return NULL;
|
|
}
|
|
conversion_not_supported:
|
|
{
|
|
GST_ERROR ("Conversion %s to %s not supported",
|
|
gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (in_info)),
|
|
gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (out_info)));
|
|
gst_d3d11_color_converter_free (converter);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
void
|
|
gst_d3d11_color_converter_free (GstD3D11ColorConverter * converter)
|
|
{
|
|
g_return_if_fail (converter != NULL);
|
|
|
|
if (converter->quad)
|
|
gst_d3d11_quad_free (converter->quad);
|
|
|
|
gst_clear_object (&converter->device);
|
|
g_free (converter->convert_info.ps_body);
|
|
g_free (converter);
|
|
}
|
|
|
|
gboolean
|
|
gst_d3d11_color_converter_convert (GstD3D11ColorConverter * converter,
|
|
ID3D11ShaderResourceView * srv[GST_VIDEO_MAX_PLANES],
|
|
ID3D11RenderTargetView * rtv[GST_VIDEO_MAX_PLANES])
|
|
{
|
|
g_return_val_if_fail (converter != NULL, FALSE);
|
|
g_return_val_if_fail (srv != NULL, FALSE);
|
|
g_return_val_if_fail (rtv != NULL, FALSE);
|
|
|
|
return gst_d3d11_draw_quad (converter->quad, &converter->viewport, 1,
|
|
srv, converter->num_input_view, rtv, converter->num_output_view);
|
|
}
|
|
|
|
gboolean
|
|
gst_d3d11_color_converter_update_rect (GstD3D11ColorConverter * converter,
|
|
RECT * rect)
|
|
{
|
|
g_return_val_if_fail (converter != NULL, FALSE);
|
|
|
|
converter->viewport.TopLeftX = rect->left;
|
|
converter->viewport.TopLeftY = rect->top;
|
|
converter->viewport.Width = rect->right - rect->left;
|
|
converter->viewport.Height = rect->bottom - rect->top;
|
|
|
|
return TRUE;
|
|
}
|