mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-26 02:00:33 +00:00
103112725c
clamp-to-border will return the border color which is typically black, white or transparent. When linear filtering the edge pixels will typeically be combined with the border color which is not typically what we want. Especially when color converting, this removes a green box around the edge when converting YUV->RGB.
1958 lines
61 KiB
C
1958 lines
61 KiB
C
/*
|
|
* GStreamer
|
|
* Copyright (C) 2019 Matthew Waters <matthew@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.
|
|
*/
|
|
|
|
/**
|
|
* SECTION:element-vulkanimageidentity
|
|
* @title: vulkanimgeidentity
|
|
*
|
|
* vulkanimageidentity produces a vulkan image that is a copy of the input image.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <string.h>
|
|
|
|
#include "vkcolorconvert.h"
|
|
#include "vkshader.h"
|
|
#include "vkelementutils.h"
|
|
|
|
#include "shaders/identity.vert.h"
|
|
#include "shaders/swizzle.frag.h"
|
|
#include "shaders/swizzle_and_clobber_alpha.frag.h"
|
|
#include "shaders/yuy2_to_rgb.frag.h"
|
|
#include "shaders/ayuv_to_rgb.frag.h"
|
|
#include "shaders/nv12_to_rgb.frag.h"
|
|
#include "shaders/rgb_to_ayuv.frag.h"
|
|
#include "shaders/rgb_to_yuy2.frag.h"
|
|
#include "shaders/rgb_to_nv12.frag.h"
|
|
|
|
GST_DEBUG_CATEGORY (gst_debug_vulkan_color_convert);
|
|
#define GST_CAT_DEFAULT gst_debug_vulkan_color_convert
|
|
|
|
#define N_SHADER_INFO (8*16)
|
|
static shader_info shader_infos[N_SHADER_INFO];
|
|
|
|
#define PUSH_CONSTANT_RANGE_NULL_INIT (VkPushConstantRange) { \
|
|
.stageFlags = 0, \
|
|
.offset = 0, \
|
|
.size = 0, \
|
|
}
|
|
|
|
static void
|
|
get_rgb_format_swizzle_order (GstVideoFormat format,
|
|
gint swizzle[GST_VIDEO_MAX_COMPONENTS])
|
|
{
|
|
const GstVideoFormatInfo *finfo = gst_video_format_get_info (format);
|
|
int c_i = 0, i;
|
|
|
|
g_return_if_fail (finfo->flags & GST_VIDEO_FORMAT_FLAG_RGB
|
|
|| format == GST_VIDEO_FORMAT_AYUV);
|
|
|
|
for (i = 0; i < finfo->n_components; i++) {
|
|
swizzle[c_i++] = finfo->poffset[i];
|
|
}
|
|
|
|
/* special case spaced RGB formats as the space does not contain a poffset
|
|
* value and we need all four components to be valid in order to swizzle
|
|
* correctly */
|
|
if (format == GST_VIDEO_FORMAT_xRGB || format == GST_VIDEO_FORMAT_xBGR) {
|
|
swizzle[c_i++] = 0;
|
|
} else if (format == GST_VIDEO_FORMAT_RGBx || format == GST_VIDEO_FORMAT_BGRx) {
|
|
swizzle[c_i++] = 3;
|
|
} else {
|
|
for (i = finfo->n_components; i < GST_VIDEO_MAX_COMPONENTS; i++) {
|
|
swizzle[c_i++] = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
get_vulkan_rgb_format_swizzle_order (VkFormat format, gint * swizzle,
|
|
guint swizzle_count, guint offset)
|
|
{
|
|
const GstVulkanFormatInfo *finfo = gst_vulkan_format_get_info (format);
|
|
int i;
|
|
|
|
g_return_if_fail (finfo->flags & GST_VULKAN_FORMAT_FLAG_RGB);
|
|
g_return_if_fail (finfo->n_components <= swizzle_count);
|
|
|
|
for (i = 0; i < finfo->n_components; i++) {
|
|
swizzle[i] = offset + finfo->poffset[i];
|
|
}
|
|
for (i = finfo->n_components; i < swizzle_count; i++) {
|
|
swizzle[i] = -1;
|
|
}
|
|
}
|
|
|
|
/* given a swizzle index, produce an index such that:
|
|
*
|
|
* swizzle[idx[i]] == identity[i] where:
|
|
* - swizzle is the original swizzle
|
|
* - idx is the result
|
|
* - identity = {0, 1, 2,...}
|
|
* - unset fields are marked by -1
|
|
*/
|
|
static void
|
|
swizzle_identity_order (gint * swizzle, gint * idx)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < GST_VIDEO_MAX_COMPONENTS; i++) {
|
|
idx[i] = -1;
|
|
}
|
|
|
|
for (i = 0; i < GST_VIDEO_MAX_COMPONENTS; i++) {
|
|
if (swizzle[i] >= 0 && swizzle[i] < 4 && idx[swizzle[i]] == -1) {
|
|
idx[swizzle[i]] = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
double dm[4][4];
|
|
} Matrix4;
|
|
|
|
static void
|
|
matrix_debug (const Matrix4 * 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
|
|
matrix_to_float (const Matrix4 * m, float *ret)
|
|
{
|
|
int i, j;
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
for (j = 0; j < 4; j++) {
|
|
ret[j * 4 + i] = m->dm[i][j];
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
matrix_set_identity (Matrix4 * m)
|
|
{
|
|
int i, j;
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
for (j = 0; j < 4; j++) {
|
|
m->dm[i][j] = (i == j);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
matrix_copy (Matrix4 * d, const Matrix4 * 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
|
|
matrix_multiply (Matrix4 * dst, Matrix4 * a, Matrix4 * b)
|
|
{
|
|
Matrix4 tmp;
|
|
int i, j, k;
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
for (j = 0; j < 4; j++) {
|
|
double x = 0;
|
|
for (k = 0; k < 4; k++) {
|
|
x += a->dm[i][k] * b->dm[k][j];
|
|
}
|
|
tmp.dm[i][j] = x;
|
|
}
|
|
}
|
|
matrix_copy (dst, &tmp);
|
|
}
|
|
|
|
#if 0
|
|
static void
|
|
matrix_invert (Matrix4 * d, Matrix4 * s)
|
|
{
|
|
Matrix4 tmp;
|
|
int i, j;
|
|
double det;
|
|
|
|
matrix_set_identity (&tmp);
|
|
for (j = 0; j < 3; j++) {
|
|
for (i = 0; i < 3; i++) {
|
|
tmp.dm[j][i] =
|
|
s->dm[(i + 1) % 3][(j + 1) % 3] * s->dm[(i + 2) % 3][(j + 2) % 3] -
|
|
s->dm[(i + 1) % 3][(j + 2) % 3] * s->dm[(i + 2) % 3][(j + 1) % 3];
|
|
}
|
|
}
|
|
det =
|
|
tmp.dm[0][0] * s->dm[0][0] + tmp.dm[0][1] * s->dm[1][0] +
|
|
tmp.dm[0][2] * s->dm[2][0];
|
|
for (j = 0; j < 3; j++) {
|
|
for (i = 0; i < 3; i++) {
|
|
tmp.dm[i][j] /= det;
|
|
}
|
|
}
|
|
matrix_copy (d, &tmp);
|
|
}
|
|
#endif
|
|
static void
|
|
matrix_offset_components (Matrix4 * m, double a1, double a2, double a3)
|
|
{
|
|
Matrix4 a;
|
|
|
|
matrix_set_identity (&a);
|
|
a.dm[0][3] = a1;
|
|
a.dm[1][3] = a2;
|
|
a.dm[2][3] = a3;
|
|
matrix_debug (&a);
|
|
matrix_multiply (m, &a, m);
|
|
}
|
|
|
|
static void
|
|
matrix_scale_components (Matrix4 * m, double a1, double a2, double a3)
|
|
{
|
|
Matrix4 a;
|
|
|
|
matrix_set_identity (&a);
|
|
a.dm[0][0] = a1;
|
|
a.dm[1][1] = a2;
|
|
a.dm[2][2] = a3;
|
|
matrix_multiply (m, &a, m);
|
|
}
|
|
|
|
static void
|
|
matrix_YCbCr_to_RGB (Matrix4 * m, double Kr, double Kb)
|
|
{
|
|
double Kg = 1.0 - Kr - Kb;
|
|
Matrix4 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.},
|
|
}
|
|
};
|
|
|
|
matrix_multiply (m, &k, m);
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
GstVideoInfo in_info;
|
|
GstVideoInfo out_info;
|
|
|
|
Matrix4 to_RGB_matrix;
|
|
Matrix4 to_YUV_matrix;
|
|
Matrix4 convert_matrix;
|
|
} ConvertInfo;
|
|
|
|
static void
|
|
convert_to_RGB (ConvertInfo * conv, Matrix4 * m)
|
|
{
|
|
GstVideoInfo *info = &conv->in_info;
|
|
|
|
{
|
|
const GstVideoFormatInfo *uinfo;
|
|
gint offset[4], scale[4], depth[4];
|
|
int i;
|
|
|
|
uinfo = gst_video_format_get_info (GST_VIDEO_INFO_FORMAT (info));
|
|
|
|
/* bring color components to [0..1.0] range */
|
|
gst_video_color_range_offsets (info->colorimetry.range, uinfo, offset,
|
|
scale);
|
|
|
|
for (i = 0; i < uinfo->n_components; i++)
|
|
depth[i] = (1 << uinfo->depth[i]) - 1;
|
|
|
|
matrix_offset_components (m, -offset[0] / (float) depth[0],
|
|
-offset[1] / (float) depth[1], -offset[2] / (float) depth[2]);
|
|
matrix_scale_components (m, depth[0] / ((float) scale[0]),
|
|
depth[1] / ((float) scale[1]), depth[2] / ((float) scale[2]));
|
|
GST_DEBUG ("to RGB scale/offset matrix");
|
|
matrix_debug (m);
|
|
}
|
|
|
|
if (GST_VIDEO_INFO_IS_YUV (info)) {
|
|
gdouble Kr, Kb;
|
|
|
|
if (gst_video_color_matrix_get_Kr_Kb (info->colorimetry.matrix, &Kr, &Kb))
|
|
matrix_YCbCr_to_RGB (m, Kr, Kb);
|
|
GST_DEBUG ("to RGB matrix");
|
|
matrix_debug (m);
|
|
}
|
|
}
|
|
|
|
static void
|
|
matrix_RGB_to_YCbCr (Matrix4 * m, double Kr, double Kb)
|
|
{
|
|
double Kg = 1.0 - Kr - Kb;
|
|
Matrix4 k;
|
|
double 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;
|
|
|
|
matrix_multiply (m, &k, m);
|
|
}
|
|
|
|
static void
|
|
convert_to_YUV (ConvertInfo * conv, Matrix4 * m)
|
|
{
|
|
GstVideoInfo *info = &conv->out_info;
|
|
|
|
if (GST_VIDEO_INFO_IS_YUV (info)) {
|
|
gdouble Kr, Kb;
|
|
|
|
if (gst_video_color_matrix_get_Kr_Kb (info->colorimetry.matrix, &Kr, &Kb))
|
|
matrix_RGB_to_YCbCr (m, Kr, Kb);
|
|
GST_DEBUG ("to YUV matrix");
|
|
matrix_debug (m);
|
|
}
|
|
|
|
{
|
|
const GstVideoFormatInfo *uinfo;
|
|
gint offset[4], scale[4], depth[4];
|
|
int i;
|
|
|
|
uinfo = gst_video_format_get_info (GST_VIDEO_INFO_FORMAT (info));
|
|
|
|
/* bring color components to nominal range */
|
|
gst_video_color_range_offsets (info->colorimetry.range, uinfo, offset,
|
|
scale);
|
|
|
|
for (i = 0; i < uinfo->n_components; i++)
|
|
depth[i] = (1 << uinfo->depth[i]) - 1;
|
|
|
|
matrix_scale_components (m, scale[0] / (float) depth[0],
|
|
scale[1] / (float) depth[1], scale[2] / (float) depth[2]);
|
|
matrix_offset_components (m, offset[0] / (float) depth[0],
|
|
offset[1] / (float) depth[1], offset[2] / (float) depth[2]);
|
|
GST_DEBUG ("to YUV scale/offset matrix");
|
|
matrix_debug (m);
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
static void
|
|
matrix_RGB_to_XYZ (Matrix4 * dst, double Rx, double Ry, double Gx,
|
|
double Gy, double Bx, double By, double Wx, double Wy)
|
|
{
|
|
Matrix4 m, im;
|
|
double sx, sy, sz;
|
|
double wx, wy, wz;
|
|
|
|
matrix_set_identity (&m);
|
|
|
|
m.dm[0][0] = Rx;
|
|
m.dm[1][0] = Ry;
|
|
m.dm[2][0] = (1.0 - Rx - Ry);
|
|
m.dm[0][1] = Gx;
|
|
m.dm[1][1] = Gy;
|
|
m.dm[2][1] = (1.0 - Gx - Gy);
|
|
m.dm[0][2] = Bx;
|
|
m.dm[1][2] = By;
|
|
m.dm[2][2] = (1.0 - Bx - By);
|
|
|
|
matrix_invert (&im, &m);
|
|
|
|
wx = Wx / Wy;
|
|
wy = 1.0;
|
|
wz = (1.0 - Wx - Wy) / Wy;
|
|
|
|
sx = im.dm[0][0] * wx + im.dm[0][1] * wy + im.dm[0][2] * wz;
|
|
sy = im.dm[1][0] * wx + im.dm[1][1] * wy + im.dm[1][2] * wz;
|
|
sz = im.dm[2][0] * wx + im.dm[2][1] * wy + im.dm[2][2] * wz;
|
|
|
|
m.dm[0][0] *= sx;
|
|
m.dm[1][0] *= sx;
|
|
m.dm[2][0] *= sx;
|
|
m.dm[0][1] *= sy;
|
|
m.dm[1][1] *= sy;
|
|
m.dm[2][1] *= sy;
|
|
m.dm[0][2] *= sz;
|
|
m.dm[1][2] *= sz;
|
|
m.dm[2][2] *= sz;
|
|
|
|
matrix_copy (dst, &m);
|
|
}
|
|
|
|
static void
|
|
convert_primaries (ConvertInfo * conv)
|
|
{
|
|
gboolean same_matrix, same_primaries;
|
|
Matrix4 p1, p2;
|
|
|
|
same_matrix =
|
|
conv->in_info.colorimetry.matrix == conv->out_info.colorimetry.matrix;
|
|
same_primaries =
|
|
conv->in_info.colorimetry.primaries ==
|
|
conv->out_info.colorimetry.primaries;
|
|
|
|
GST_DEBUG ("matrix %d -> %d (%d)", conv->in_info.colorimetry.matrix,
|
|
conv->out_info.colorimetry.matrix, same_matrix);
|
|
GST_DEBUG ("primaries %d -> %d (%d)", conv->in_info.colorimetry.primaries,
|
|
conv->out_info.colorimetry.primaries, same_primaries);
|
|
|
|
matrix_set_identity (&conv->convert_matrix);
|
|
|
|
if (!same_primaries) {
|
|
const GstVideoColorPrimariesInfo *pi;
|
|
|
|
pi = gst_video_color_primaries_get_info (conv->in_info.colorimetry.
|
|
primaries);
|
|
matrix_RGB_to_XYZ (&p1, pi->Rx, pi->Ry, pi->Gx, pi->Gy, pi->Bx, pi->By,
|
|
pi->Wx, pi->Wy);
|
|
GST_DEBUG ("to XYZ matrix");
|
|
matrix_debug (&p1);
|
|
GST_DEBUG ("current matrix");
|
|
matrix_multiply (&conv->convert_matrix, &conv->convert_matrix, &p1);
|
|
matrix_debug (&conv->convert_matrix);
|
|
|
|
pi = gst_video_color_primaries_get_info (conv->out_info.colorimetry.
|
|
primaries);
|
|
matrix_RGB_to_XYZ (&p2, pi->Rx, pi->Ry, pi->Gx, pi->Gy, pi->Bx, pi->By,
|
|
pi->Wx, pi->Wy);
|
|
matrix_invert (&p2, &p2);
|
|
GST_DEBUG ("to RGB matrix");
|
|
matrix_debug (&p2);
|
|
matrix_multiply (&conv->convert_matrix, &conv->convert_matrix, &p2);
|
|
GST_DEBUG ("current matrix");
|
|
matrix_debug (&conv->convert_matrix);
|
|
}
|
|
}
|
|
#endif
|
|
static ConvertInfo *
|
|
convert_info_new (GstVideoInfo * in_info, GstVideoInfo * out_info)
|
|
{
|
|
ConvertInfo *conv = g_new0 (ConvertInfo, 1);
|
|
|
|
matrix_set_identity (&conv->to_RGB_matrix);
|
|
matrix_set_identity (&conv->convert_matrix);
|
|
matrix_set_identity (&conv->to_YUV_matrix);
|
|
|
|
memcpy (&conv->in_info, in_info, sizeof (*in_info));
|
|
memcpy (&conv->out_info, out_info, sizeof (*out_info));
|
|
|
|
convert_to_RGB (conv, &conv->to_RGB_matrix);
|
|
/* by default videoconvert does not convert primaries
|
|
convert_primaries (conv); */
|
|
convert_to_YUV (conv, &conv->to_YUV_matrix);
|
|
|
|
return conv;
|
|
}
|
|
|
|
static void
|
|
update_descriptor_set (GstVulkanColorConvert * conv, VkImageView * views,
|
|
guint n_views)
|
|
{
|
|
GstVulkanFullScreenRender *render = GST_VULKAN_FULL_SCREEN_RENDER (conv);
|
|
VkDescriptorBufferInfo buffer_info;
|
|
VkDescriptorImageInfo image_info[GST_VIDEO_MAX_PLANES];
|
|
VkWriteDescriptorSet writes[5];
|
|
guint i = 0;
|
|
|
|
for (; i < GST_VIDEO_INFO_N_PLANES (&render->in_info); i++) {
|
|
/* *INDENT-OFF* */
|
|
image_info[i] = (VkDescriptorImageInfo) {
|
|
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
|
.imageView = views[i],
|
|
.sampler = conv->sampler
|
|
};
|
|
|
|
g_assert (i < n_views);
|
|
g_assert (i < GST_VIDEO_MAX_PLANES);
|
|
|
|
writes[i] = (VkWriteDescriptorSet) {
|
|
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
|
.pNext = NULL,
|
|
.dstSet = conv->descriptor_set,
|
|
.dstBinding = i,
|
|
.dstArrayElement = 0,
|
|
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
|
.descriptorCount = 1,
|
|
.pImageInfo = &image_info[i]
|
|
};
|
|
/* *INDENT-ON* */
|
|
}
|
|
if (conv->uniform && conv->current_shader->uniform_size) {
|
|
/* *INDENT-OFF* */
|
|
buffer_info = (VkDescriptorBufferInfo) {
|
|
.buffer = ((GstVulkanBufferMemory *) conv->uniform)->buffer,
|
|
.offset = 0,
|
|
.range = conv->current_shader->uniform_size
|
|
};
|
|
writes[i] = (VkWriteDescriptorSet) {
|
|
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
|
.pNext = NULL,
|
|
.dstSet = conv->descriptor_set,
|
|
.dstBinding = i,
|
|
.dstArrayElement = 0,
|
|
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
|
.descriptorCount = 1,
|
|
.pBufferInfo = &buffer_info
|
|
};
|
|
/* *INDENT-ON* */
|
|
i++;
|
|
};
|
|
g_assert (i <= G_N_ELEMENTS (writes));
|
|
|
|
vkUpdateDescriptorSets (render->device->device, i, writes, 0, NULL);
|
|
}
|
|
|
|
static void
|
|
video_format_to_reorder (GstVideoFormat v_format, gint * reorder,
|
|
gboolean input)
|
|
{
|
|
switch (v_format) {
|
|
case GST_VIDEO_FORMAT_RGBA:
|
|
case GST_VIDEO_FORMAT_RGBx:
|
|
case GST_VIDEO_FORMAT_BGRA:
|
|
case GST_VIDEO_FORMAT_BGRx:
|
|
case GST_VIDEO_FORMAT_ARGB:
|
|
case GST_VIDEO_FORMAT_xRGB:
|
|
case GST_VIDEO_FORMAT_ABGR:
|
|
case GST_VIDEO_FORMAT_xBGR:
|
|
case GST_VIDEO_FORMAT_AYUV:
|
|
get_rgb_format_swizzle_order (v_format, reorder);
|
|
break;
|
|
case GST_VIDEO_FORMAT_UYVY:
|
|
reorder[0] = 1;
|
|
reorder[1] = 0;
|
|
reorder[2] = input ? 3 : 2;
|
|
reorder[3] = 0;
|
|
break;
|
|
case GST_VIDEO_FORMAT_YUY2:
|
|
reorder[0] = 0;
|
|
reorder[1] = 1;
|
|
reorder[2] = 0;
|
|
reorder[3] = input ? 3 : 2;
|
|
break;
|
|
case GST_VIDEO_FORMAT_NV12:
|
|
reorder[0] = 0;
|
|
reorder[1] = 1;
|
|
reorder[2] = 2;
|
|
reorder[3] = 0;
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
break;
|
|
}
|
|
|
|
GST_TRACE ("swizzle: %u, %u, %u, %u", reorder[0], reorder[1], reorder[2],
|
|
reorder[3]);
|
|
}
|
|
|
|
static guint
|
|
finfo_get_plane_n_components (const GstVideoFormatInfo * finfo, guint plane)
|
|
{
|
|
guint n_components = 0, i;
|
|
|
|
switch (finfo->format) {
|
|
case GST_VIDEO_FORMAT_RGBx:
|
|
case GST_VIDEO_FORMAT_xRGB:
|
|
case GST_VIDEO_FORMAT_BGRx:
|
|
case GST_VIDEO_FORMAT_xBGR:
|
|
/* fixup spaced RGB formats as we treat the space as a normal alpha
|
|
* component */
|
|
return plane == 0 ? 4 : 0;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
for (i = 0; i < finfo->n_components; i++) {
|
|
if (finfo->plane[i] == plane)
|
|
n_components++;
|
|
}
|
|
|
|
return n_components;
|
|
}
|
|
|
|
static void
|
|
get_vulkan_format_swizzle_order (GstVideoFormat v_format,
|
|
VkFormat vk_format[GST_VIDEO_MAX_PLANES],
|
|
gint swizzle[GST_VIDEO_MAX_COMPONENTS])
|
|
{
|
|
const GstVideoFormatInfo *finfo;
|
|
int i, prev_in_i = 0;
|
|
|
|
finfo = gst_video_format_get_info (v_format);
|
|
for (i = 0; i < finfo->n_planes; i++) {
|
|
guint plane_components = finfo_get_plane_n_components (finfo, i);
|
|
get_vulkan_rgb_format_swizzle_order (vk_format[i],
|
|
&swizzle[prev_in_i], plane_components, prev_in_i);
|
|
prev_in_i += plane_components;
|
|
}
|
|
|
|
if (v_format == GST_VIDEO_FORMAT_YUY2 || v_format == GST_VIDEO_FORMAT_UYVY) {
|
|
/* Fixup these packed YUV formats as we use a two component format for
|
|
* a 4-component pixel and access two samples in the shader */
|
|
g_assert (swizzle[0] == 0);
|
|
g_assert (swizzle[1] == 1);
|
|
swizzle[2] = 2;
|
|
swizzle[3] = 3;
|
|
}
|
|
|
|
GST_TRACE ("%s: %i, %i, %i, %i", finfo->name, swizzle[0], swizzle[1],
|
|
swizzle[2], swizzle[3]);
|
|
}
|
|
|
|
static void
|
|
calculate_reorder_indexes (GstVideoFormat in_format,
|
|
GstVulkanImageView * in_views[GST_VIDEO_MAX_COMPONENTS],
|
|
GstVideoFormat out_format,
|
|
GstVulkanImageView * out_views[GST_VIDEO_MAX_COMPONENTS],
|
|
int ret_in[GST_VIDEO_MAX_COMPONENTS], int ret_out[GST_VIDEO_MAX_COMPONENTS])
|
|
{
|
|
const GstVideoFormatInfo *in_finfo, *out_finfo;
|
|
VkFormat in_vk_formats[GST_VIDEO_MAX_COMPONENTS];
|
|
VkFormat out_vk_formats[GST_VIDEO_MAX_COMPONENTS];
|
|
int in_vk_order[GST_VIDEO_MAX_COMPONENTS] = { 0, };
|
|
int in_reorder[GST_VIDEO_MAX_COMPONENTS] = { 0, };
|
|
int out_vk_order[GST_VIDEO_MAX_COMPONENTS] = { 0, };
|
|
int out_reorder[GST_VIDEO_MAX_COMPONENTS] = { 0, };
|
|
int tmp[GST_VIDEO_MAX_PLANES] = { 0, };
|
|
int i;
|
|
|
|
in_finfo = gst_video_format_get_info (in_format);
|
|
out_finfo = gst_video_format_get_info (out_format);
|
|
|
|
for (i = 0; i < in_finfo->n_planes; i++)
|
|
in_vk_formats[i] = in_views[i]->image->create_info.format;
|
|
for (i = 0; i < out_finfo->n_planes; i++)
|
|
out_vk_formats[i] = out_views[i]->image->create_info.format;
|
|
|
|
get_vulkan_format_swizzle_order (in_format, in_vk_formats, in_vk_order);
|
|
video_format_to_reorder (in_format, in_reorder, TRUE);
|
|
|
|
video_format_to_reorder (out_format, out_reorder, FALSE);
|
|
get_vulkan_format_swizzle_order (out_format, out_vk_formats, out_vk_order);
|
|
|
|
for (i = 0; i < GST_VIDEO_MAX_COMPONENTS; i++)
|
|
tmp[i] = out_vk_order[out_reorder[i]];
|
|
/* find the identity order for RGBA->$format */
|
|
GST_TRACE ("pre-invert: %u, %u, %u, %u", tmp[0], tmp[1], tmp[2], tmp[3]);
|
|
if (out_format == GST_VIDEO_FORMAT_YUY2
|
|
|| out_format == GST_VIDEO_FORMAT_UYVY) {
|
|
for (i = 0; i < GST_VIDEO_MAX_COMPONENTS; i++)
|
|
ret_out[i] = tmp[i];
|
|
} else {
|
|
swizzle_identity_order (tmp, ret_out);
|
|
}
|
|
|
|
for (i = 0; i < GST_VIDEO_MAX_COMPONENTS; i++)
|
|
ret_in[i] = in_reorder[in_vk_order[i]];
|
|
GST_TRACE ("in reorder: %u, %u, %u, %u", ret_in[0], ret_in[1], ret_in[2],
|
|
ret_in[3]);
|
|
GST_TRACE ("out reorder: %u, %u, %u, %u", ret_out[0], ret_out[1], ret_out[2],
|
|
ret_out[3]);
|
|
}
|
|
|
|
static gboolean
|
|
swizzle_rgb_update_command_state (GstVulkanColorConvert * conv,
|
|
VkCommandBuffer cmd, shader_info * sinfo,
|
|
GstVulkanImageView ** in_views, GstVulkanImageView ** out_views)
|
|
{
|
|
GstVulkanFullScreenRender *render = GST_VULKAN_FULL_SCREEN_RENDER (conv);
|
|
gint reorder[8];
|
|
|
|
calculate_reorder_indexes (GST_VIDEO_INFO_FORMAT (&render->in_info),
|
|
in_views, GST_VIDEO_INFO_FORMAT (&render->out_info),
|
|
out_views, reorder, &reorder[4]);
|
|
|
|
vkCmdPushConstants (cmd, render->pipeline_layout,
|
|
VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof (reorder),
|
|
(const void *) reorder);
|
|
update_descriptor_set (conv, &in_views[0]->view, 1);
|
|
vkCmdBindDescriptorSets (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
|
|
render->pipeline_layout, 0, 1, &conv->descriptor_set, 0, NULL);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
struct ColorMatrices
|
|
{
|
|
float to_RGB[16];
|
|
float primaries[16];
|
|
float to_YUV[16];
|
|
};
|
|
|
|
struct YUVUpdateData
|
|
{
|
|
int in_reorder[4];
|
|
int out_reorder[4];
|
|
int tex_size[2];
|
|
/* each member is aligned on 4x previous component size boundaries */
|
|
int _padding[2];
|
|
struct ColorMatrices matrices;
|
|
};
|
|
|
|
static gboolean
|
|
yuv_to_rgb_update_command_state (GstVulkanColorConvert * conv,
|
|
VkCommandBuffer cmd, shader_info * sinfo, GstVulkanImageView ** in_views,
|
|
GstVulkanImageView ** out_views)
|
|
{
|
|
GstVulkanFullScreenRender *render = GST_VULKAN_FULL_SCREEN_RENDER (conv);
|
|
|
|
if (!GPOINTER_TO_INT (sinfo->user_data)) {
|
|
struct YUVUpdateData data;
|
|
ConvertInfo *conv_info;
|
|
GstMapInfo map_info;
|
|
VkImageView views[GST_VIDEO_MAX_PLANES];
|
|
int i;
|
|
|
|
calculate_reorder_indexes (GST_VIDEO_INFO_FORMAT (&render->in_info),
|
|
in_views, GST_VIDEO_INFO_FORMAT (&render->out_info),
|
|
out_views, data.in_reorder, data.out_reorder);
|
|
|
|
conv_info = convert_info_new (&render->in_info, &render->out_info);
|
|
matrix_to_float (&conv_info->to_RGB_matrix, data.matrices.to_RGB);
|
|
matrix_to_float (&conv_info->convert_matrix, data.matrices.primaries);
|
|
matrix_to_float (&conv_info->to_YUV_matrix, data.matrices.to_YUV);
|
|
/* FIXME: keep this around */
|
|
g_free (conv_info);
|
|
|
|
data.tex_size[0] = GST_VIDEO_INFO_WIDTH (&render->in_info);
|
|
data.tex_size[1] = GST_VIDEO_INFO_HEIGHT (&render->in_info);
|
|
|
|
if (!gst_memory_map (conv->uniform, &map_info, GST_MAP_WRITE)) {
|
|
return FALSE;
|
|
}
|
|
memcpy (map_info.data, &data, sizeof (data));
|
|
gst_memory_unmap (conv->uniform, &map_info);
|
|
|
|
for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&render->in_info); i++)
|
|
views[i] = in_views[i]->view;
|
|
|
|
update_descriptor_set (conv, views,
|
|
GST_VIDEO_INFO_N_PLANES (&render->in_info));
|
|
sinfo->user_data = GINT_TO_POINTER (1);
|
|
}
|
|
vkCmdBindDescriptorSets (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
|
|
render->pipeline_layout, 0, 1, &conv->descriptor_set, 0, NULL);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
clear_user_data_flag (shader_info * sinfo)
|
|
{
|
|
sinfo->user_data = NULL;
|
|
}
|
|
|
|
static gboolean gst_vulkan_color_convert_start (GstBaseTransform * bt);
|
|
static gboolean gst_vulkan_color_convert_stop (GstBaseTransform * bt);
|
|
|
|
static GstCaps *gst_vulkan_color_convert_transform_caps (GstBaseTransform * bt,
|
|
GstPadDirection direction, GstCaps * caps, GstCaps * filter);
|
|
static GstFlowReturn gst_vulkan_color_convert_transform (GstBaseTransform * bt,
|
|
GstBuffer * inbuf, GstBuffer * outbuf);
|
|
static gboolean gst_vulkan_color_convert_set_caps (GstBaseTransform * bt,
|
|
GstCaps * in_caps, GstCaps * out_caps);
|
|
|
|
static VkAttachmentReference
|
|
* gst_vulkan_color_convert_render_pass_attachment_references
|
|
(GstVulkanFullScreenRender * render, guint * n_attachments);
|
|
static VkAttachmentDescription
|
|
* gst_vulkan_color_convert_render_pass_attachment_descriptions
|
|
(GstVulkanFullScreenRender * render, guint * n_descriptions);
|
|
static VkDescriptorSetLayoutBinding
|
|
* gst_vulkan_color_convert_descriptor_set_layout_bindings
|
|
(GstVulkanFullScreenRender * render, guint * n_bindings);
|
|
static void
|
|
gst_vulkan_color_convert_shader_create_info (GstVulkanFullScreenRender *
|
|
render);
|
|
static VkPushConstantRange
|
|
* gst_vulkan_color_convert_push_constant_ranges (GstVulkanFullScreenRender *
|
|
render, guint * n_constants);
|
|
|
|
static GstStaticPadTemplate gst_vulkan_sink_template =
|
|
GST_STATIC_PAD_TEMPLATE ("sink",
|
|
GST_PAD_SINK,
|
|
GST_PAD_ALWAYS,
|
|
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
|
|
(GST_CAPS_FEATURE_MEMORY_VULKAN_IMAGE,
|
|
"{ BGRA, RGBA, ABGR, ARGB, BGRx, RGBx, xBGR, xRGB, AYUV, YUY2, UYVY, NV12 }")));
|
|
|
|
static GstStaticPadTemplate gst_vulkan_src_template =
|
|
GST_STATIC_PAD_TEMPLATE ("src",
|
|
GST_PAD_SRC,
|
|
GST_PAD_ALWAYS,
|
|
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
|
|
(GST_CAPS_FEATURE_MEMORY_VULKAN_IMAGE,
|
|
"{ BGRA, RGBA, ABGR, ARGB, BGRx, RGBx, xBGR, xRGB, AYUV, YUY2, UYVY, NV12 }")));
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
};
|
|
|
|
enum
|
|
{
|
|
SIGNAL_0,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
/* static guint gst_vulkan_color_convert_signals[LAST_SIGNAL] = { 0 }; */
|
|
|
|
#define gst_vulkan_color_convert_parent_class parent_class
|
|
G_DEFINE_TYPE_WITH_CODE (GstVulkanColorConvert, gst_vulkan_color_convert,
|
|
GST_TYPE_VULKAN_FULL_SCREEN_RENDER,
|
|
GST_DEBUG_CATEGORY_INIT (gst_debug_vulkan_color_convert,
|
|
"vulkancolorconvert", 0, "Vulkan Color Convert"));
|
|
|
|
struct yuv_info
|
|
{
|
|
GstVideoFormat format;
|
|
gchar *from_frag;
|
|
gsize from_frag_size;
|
|
gchar *to_frag;
|
|
gsize to_frag_size;
|
|
};
|
|
|
|
static void
|
|
fill_shader_info (void)
|
|
{
|
|
GstVideoFormat rgbs[] = { GST_VIDEO_FORMAT_RGBA, GST_VIDEO_FORMAT_ARGB,
|
|
GST_VIDEO_FORMAT_BGRA, GST_VIDEO_FORMAT_ABGR, GST_VIDEO_FORMAT_RGBx,
|
|
GST_VIDEO_FORMAT_xRGB, GST_VIDEO_FORMAT_BGRx, GST_VIDEO_FORMAT_xBGR
|
|
};
|
|
struct yuv_info yuvs[] = {
|
|
{GST_VIDEO_FORMAT_AYUV, ayuv_to_rgb_frag, ayuv_to_rgb_frag_size,
|
|
rgb_to_ayuv_frag, rgb_to_ayuv_frag_size},
|
|
{GST_VIDEO_FORMAT_YUY2, yuy2_to_rgb_frag, yuy2_to_rgb_frag_size,
|
|
rgb_to_yuy2_frag, rgb_to_yuy2_frag_size},
|
|
{GST_VIDEO_FORMAT_UYVY, yuy2_to_rgb_frag, yuy2_to_rgb_frag_size,
|
|
rgb_to_yuy2_frag, rgb_to_yuy2_frag_size},
|
|
{GST_VIDEO_FORMAT_NV12, nv12_to_rgb_frag, nv12_to_rgb_frag_size,
|
|
rgb_to_nv12_frag, rgb_to_nv12_frag_size},
|
|
};
|
|
guint info_i = 0;
|
|
guint i, j;
|
|
|
|
/* standard RGB with alpha conversion all components are copied */
|
|
/* *INDENT-OFF* */
|
|
for (i = 0; i < G_N_ELEMENTS (rgbs); i++) {
|
|
const GstVideoFormatInfo *from_finfo = gst_video_format_get_info (rgbs[i]);
|
|
|
|
for (j = 0; j < G_N_ELEMENTS (rgbs); j++) {
|
|
const GstVideoFormatInfo *to_finfo = gst_video_format_get_info (rgbs[j]);
|
|
gboolean clobber_alpha = FALSE;
|
|
|
|
GST_TRACE ("Initializing info for %s -> %s", from_finfo->name, to_finfo->name);
|
|
|
|
/* copying to an RGBx variant means we can store whatever we like in the 'x'
|
|
* component we choose to copy the alpha component like a standard RGBA->RGBA
|
|
* swizzle.
|
|
* Copying from an rgbx to a rgba format means we need to reset the
|
|
* alpha value */
|
|
clobber_alpha = !GST_VIDEO_FORMAT_INFO_HAS_ALPHA (from_finfo) && GST_VIDEO_FORMAT_INFO_HAS_ALPHA (to_finfo);
|
|
shader_infos[info_i++] = (shader_info) {
|
|
.from = rgbs[i],
|
|
.to = rgbs[j],
|
|
.cmd_state_update = swizzle_rgb_update_command_state,
|
|
.frag_code = clobber_alpha ? swizzle_and_clobber_alpha_frag : swizzle_frag,
|
|
.frag_size = clobber_alpha ? swizzle_and_clobber_alpha_frag_size : swizzle_frag_size,
|
|
.push_constant_ranges = {
|
|
{
|
|
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
|
|
.offset = 0,
|
|
.size = 8 * sizeof(gint32),
|
|
}, PUSH_CONSTANT_RANGE_NULL_INIT,
|
|
},
|
|
.notify = (GDestroyNotify) clear_user_data_flag,
|
|
.user_data = NULL,
|
|
};
|
|
}
|
|
|
|
for (j = 0; j < G_N_ELEMENTS (yuvs); j++) {
|
|
const GstVideoFormatInfo *to_finfo = gst_video_format_get_info (yuvs[j].format);
|
|
GST_TRACE ("Initializing info for %s -> %s", from_finfo->name, to_finfo->name);
|
|
shader_infos[info_i++] = (shader_info) {
|
|
.from = rgbs[i],
|
|
.to = yuvs[j].format,
|
|
.cmd_state_update = yuv_to_rgb_update_command_state,
|
|
.frag_code = yuvs[j].to_frag,
|
|
.frag_size = yuvs[j].to_frag_size,
|
|
.push_constant_ranges = { PUSH_CONSTANT_RANGE_NULL_INIT },
|
|
.uniform_size = sizeof(struct YUVUpdateData),
|
|
.notify = (GDestroyNotify) clear_user_data_flag,
|
|
.user_data = NULL,
|
|
};
|
|
GST_TRACE ("Initializing info for %s -> %s", to_finfo->name, from_finfo->name);
|
|
shader_infos[info_i++] = (shader_info) {
|
|
.from = yuvs[j].format,
|
|
.to = rgbs[i],
|
|
.cmd_state_update = yuv_to_rgb_update_command_state,
|
|
.frag_code = yuvs[j].from_frag,
|
|
.frag_size = yuvs[j].from_frag_size,
|
|
.push_constant_ranges = { PUSH_CONSTANT_RANGE_NULL_INIT },
|
|
.uniform_size = sizeof(struct YUVUpdateData),
|
|
.notify = (GDestroyNotify) clear_user_data_flag,
|
|
.user_data = NULL,
|
|
};
|
|
}
|
|
}
|
|
/* *INDENT-ON* */
|
|
GST_TRACE ("initialized %u formats", info_i);
|
|
|
|
g_assert (info_i == N_SHADER_INFO);
|
|
}
|
|
|
|
static void
|
|
gst_vulkan_color_convert_class_init (GstVulkanColorConvertClass * klass)
|
|
{
|
|
GstElementClass *gstelement_class;
|
|
GstBaseTransformClass *gstbasetransform_class;
|
|
GstVulkanFullScreenRenderClass *fullscreenrender_class;
|
|
|
|
gstelement_class = (GstElementClass *) klass;
|
|
gstbasetransform_class = (GstBaseTransformClass *) klass;
|
|
fullscreenrender_class = (GstVulkanFullScreenRenderClass *) klass;
|
|
|
|
gst_element_class_set_metadata (gstelement_class, "Vulkan Uploader",
|
|
"Filter/Video/Convert", "A Vulkan Color Convert",
|
|
"Matthew Waters <matthew@centricular.com>");
|
|
|
|
gst_element_class_add_static_pad_template (gstelement_class,
|
|
&gst_vulkan_sink_template);
|
|
gst_element_class_add_static_pad_template (gstelement_class,
|
|
&gst_vulkan_src_template);
|
|
|
|
gstbasetransform_class->start =
|
|
GST_DEBUG_FUNCPTR (gst_vulkan_color_convert_start);
|
|
gstbasetransform_class->stop =
|
|
GST_DEBUG_FUNCPTR (gst_vulkan_color_convert_stop);
|
|
gstbasetransform_class->transform_caps =
|
|
gst_vulkan_color_convert_transform_caps;
|
|
gstbasetransform_class->set_caps = gst_vulkan_color_convert_set_caps;
|
|
gstbasetransform_class->transform = gst_vulkan_color_convert_transform;
|
|
|
|
fullscreenrender_class->render_pass_attachment_references =
|
|
gst_vulkan_color_convert_render_pass_attachment_references;
|
|
fullscreenrender_class->render_pass_attachment_descriptions =
|
|
gst_vulkan_color_convert_render_pass_attachment_descriptions;
|
|
fullscreenrender_class->descriptor_set_layout_bindings =
|
|
gst_vulkan_color_convert_descriptor_set_layout_bindings;
|
|
fullscreenrender_class->shader_create_info =
|
|
gst_vulkan_color_convert_shader_create_info;
|
|
fullscreenrender_class->push_constant_ranges =
|
|
gst_vulkan_color_convert_push_constant_ranges;
|
|
|
|
fill_shader_info ();
|
|
}
|
|
|
|
static void
|
|
gst_vulkan_color_convert_init (GstVulkanColorConvert * conv)
|
|
{
|
|
}
|
|
|
|
static void
|
|
_init_value_string_list (GValue * list, ...)
|
|
{
|
|
GValue item = G_VALUE_INIT;
|
|
gchar *str;
|
|
va_list args;
|
|
|
|
g_value_init (list, GST_TYPE_LIST);
|
|
|
|
va_start (args, list);
|
|
while ((str = va_arg (args, gchar *))) {
|
|
g_value_init (&item, G_TYPE_STRING);
|
|
g_value_set_string (&item, str);
|
|
|
|
gst_value_list_append_value (list, &item);
|
|
g_value_unset (&item);
|
|
}
|
|
va_end (args);
|
|
}
|
|
|
|
static void
|
|
_append_value_string_list (GValue * list, ...)
|
|
{
|
|
GValue item = G_VALUE_INIT;
|
|
gchar *str;
|
|
va_list args;
|
|
|
|
va_start (args, list);
|
|
while ((str = va_arg (args, gchar *))) {
|
|
g_value_init (&item, G_TYPE_STRING);
|
|
g_value_set_string (&item, str);
|
|
|
|
gst_value_list_append_value (list, &item);
|
|
g_value_unset (&item);
|
|
}
|
|
va_end (args);
|
|
}
|
|
|
|
static void
|
|
_init_supported_formats (GstVulkanDevice * device, gboolean output,
|
|
GValue * supported_formats)
|
|
{
|
|
/* Assume if device == NULL that we don't have a Vulkan device and can
|
|
* do the conversion */
|
|
|
|
/* Always supported input and output formats */
|
|
_init_value_string_list (supported_formats, "RGBA", "RGB", "RGBx", "BGR",
|
|
"BGRx", "BGRA", "xRGB", "xBGR", "ARGB", "ABGR", NULL);
|
|
|
|
_append_value_string_list (supported_formats, "AYUV", "YUY2", "UYVY", "NV12",
|
|
NULL);
|
|
}
|
|
|
|
/* copies the given caps */
|
|
static GstCaps *
|
|
gst_vulkan_color_convert_transform_format_info (GstVulkanDevice * device,
|
|
gboolean output, GstCaps * caps)
|
|
{
|
|
GstStructure *st;
|
|
GstCapsFeatures *f;
|
|
gint i, n;
|
|
GstCaps *res;
|
|
GValue supported_formats = G_VALUE_INIT;
|
|
GValue rgb_formats = G_VALUE_INIT;
|
|
GValue supported_rgb_formats = G_VALUE_INIT;
|
|
|
|
/* There are effectively two modes here with the RGB/YUV transition:
|
|
* 1. There is a RGB-like format as input and we can transform to YUV or,
|
|
* 2. No RGB-like format as input so we can only transform to RGB-like formats
|
|
*
|
|
* We also filter down the list of formats depending on what the OpenGL
|
|
* device supports (when provided).
|
|
*/
|
|
|
|
_init_value_string_list (&rgb_formats, "RGBA", "ARGB", "BGRA", "ABGR", "RGBx",
|
|
"xRGB", "BGRx", "xBGR", "RGB", "BGR", "ARGB64", NULL);
|
|
_init_supported_formats (device, output, &supported_formats);
|
|
gst_value_intersect (&supported_rgb_formats, &rgb_formats,
|
|
&supported_formats);
|
|
|
|
res = gst_caps_new_empty ();
|
|
|
|
n = gst_caps_get_size (caps);
|
|
for (i = 0; i < n; i++) {
|
|
const GValue *format;
|
|
|
|
st = gst_caps_get_structure (caps, i);
|
|
f = gst_caps_get_features (caps, i);
|
|
|
|
format = gst_structure_get_value (st, "format");
|
|
st = gst_structure_copy (st);
|
|
if (GST_VALUE_HOLDS_LIST (format)) {
|
|
gboolean have_rgb_formats = FALSE;
|
|
GValue passthrough_formats = G_VALUE_INIT;
|
|
gint j, len;
|
|
|
|
g_value_init (&passthrough_formats, GST_TYPE_LIST);
|
|
len = gst_value_list_get_size (format);
|
|
for (j = 0; j < len; j++) {
|
|
const GValue *val;
|
|
|
|
val = gst_value_list_get_value (format, j);
|
|
if (G_VALUE_HOLDS_STRING (val)) {
|
|
const gchar *format_str = g_value_get_string (val);
|
|
GstVideoFormat v_format = gst_video_format_from_string (format_str);
|
|
const GstVideoFormatInfo *t_info =
|
|
gst_video_format_get_info (v_format);
|
|
if (GST_VIDEO_FORMAT_INFO_FLAGS (t_info) & (GST_VIDEO_FORMAT_FLAG_YUV
|
|
| GST_VIDEO_FORMAT_FLAG_GRAY)) {
|
|
gst_value_list_append_value (&passthrough_formats, val);
|
|
} else if (GST_VIDEO_FORMAT_INFO_FLAGS (t_info) &
|
|
GST_VIDEO_FORMAT_FLAG_RGB) {
|
|
have_rgb_formats = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (have_rgb_formats) {
|
|
gst_structure_set_value (st, "format", &supported_formats);
|
|
} else {
|
|
/* add passthrough structure, then the rgb conversion structure */
|
|
gst_structure_set_value (st, "format", &passthrough_formats);
|
|
gst_caps_append_structure_full (res, gst_structure_copy (st),
|
|
gst_caps_features_copy (f));
|
|
gst_structure_set_value (st, "format", &supported_rgb_formats);
|
|
}
|
|
g_value_unset (&passthrough_formats);
|
|
} else if (G_VALUE_HOLDS_STRING (format)) {
|
|
const gchar *format_str = g_value_get_string (format);
|
|
GstVideoFormat v_format = gst_video_format_from_string (format_str);
|
|
const GstVideoFormatInfo *t_info = gst_video_format_get_info (v_format);
|
|
if (GST_VIDEO_FORMAT_INFO_FLAGS (t_info) & (GST_VIDEO_FORMAT_FLAG_YUV |
|
|
GST_VIDEO_FORMAT_FLAG_GRAY)) {
|
|
/* add passthrough structure, then the rgb conversion structure */
|
|
gst_structure_set_value (st, "format", format);
|
|
gst_caps_append_structure_full (res, gst_structure_copy (st),
|
|
gst_caps_features_copy (f));
|
|
gst_structure_set_value (st, "format", &supported_rgb_formats);
|
|
} else { /* RGB */
|
|
gst_structure_set_value (st, "format", &supported_formats);
|
|
}
|
|
}
|
|
gst_structure_remove_fields (st, "colorimetry", "chroma-site", NULL);
|
|
|
|
gst_caps_append_structure_full (res, st, gst_caps_features_copy (f));
|
|
}
|
|
|
|
g_value_unset (&supported_formats);
|
|
g_value_unset (&rgb_formats);
|
|
g_value_unset (&supported_rgb_formats);
|
|
|
|
return res;
|
|
}
|
|
|
|
static GstCaps *
|
|
gst_vulkan_color_convert_transform_caps (GstBaseTransform * bt,
|
|
GstPadDirection direction, GstCaps * caps, GstCaps * filter)
|
|
{
|
|
GstVulkanFullScreenRender *render = GST_VULKAN_FULL_SCREEN_RENDER (bt);
|
|
|
|
caps = gst_vulkan_color_convert_transform_format_info (render->device,
|
|
direction == GST_PAD_SRC, caps);
|
|
|
|
if (filter) {
|
|
GstCaps *tmp;
|
|
|
|
tmp = gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
|
|
gst_caps_unref (caps);
|
|
caps = tmp;
|
|
}
|
|
|
|
return caps;
|
|
}
|
|
|
|
static void
|
|
destroy_shader_create_info (GstVulkanFullScreenRender * render, gpointer data)
|
|
{
|
|
VkPipelineShaderStageCreateInfo *info = data;
|
|
int i;
|
|
|
|
for (i = 0; i < render->n_shader_stages; i++) {
|
|
vkDestroyShaderModule (render->device->device, info[i].module, NULL);
|
|
}
|
|
|
|
g_free (info);
|
|
}
|
|
|
|
static void
|
|
gst_vulkan_color_convert_shader_create_info (GstVulkanFullScreenRender * render)
|
|
{
|
|
GstVulkanColorConvert *conv = GST_VULKAN_COLOR_CONVERT (render);
|
|
VkShaderModule vert_module, frag_module;
|
|
|
|
vert_module =
|
|
_vk_create_shader (render->device, identity_vert, identity_vert_size,
|
|
NULL);
|
|
frag_module =
|
|
_vk_create_shader (render->device, conv->current_shader->frag_code,
|
|
conv->current_shader->frag_size, NULL);
|
|
|
|
render->n_shader_stages = 2;
|
|
render->shader_create_info =
|
|
g_new0 (VkPipelineShaderStageCreateInfo, render->n_shader_stages);
|
|
render->destroy_shader_create_info = destroy_shader_create_info;
|
|
|
|
/* *INDENT-OFF* */
|
|
render->shader_create_info[0] = (VkPipelineShaderStageCreateInfo) {
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
|
.pNext = NULL,
|
|
.stage = VK_SHADER_STAGE_VERTEX_BIT,
|
|
.module = vert_module,
|
|
.pName = "main"
|
|
};
|
|
|
|
render->shader_create_info[1] = (VkPipelineShaderStageCreateInfo) {
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
|
.pNext = NULL,
|
|
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
|
|
.module = frag_module,
|
|
.pName = "main"
|
|
};
|
|
/* *INDENT-ON* */
|
|
}
|
|
|
|
static VkPushConstantRange *
|
|
gst_vulkan_color_convert_push_constant_ranges (GstVulkanFullScreenRender *
|
|
render, guint * n_constants)
|
|
{
|
|
GstVulkanColorConvert *conv = GST_VULKAN_COLOR_CONVERT (render);
|
|
VkPushConstantRange *ranges;
|
|
int i;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (conv->current_shader->push_constant_ranges);
|
|
i++) {
|
|
if (conv->current_shader->push_constant_ranges[i].stageFlags == 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
*n_constants = i;
|
|
GST_DEBUG_OBJECT (conv, "%s->%s has %u push constants",
|
|
gst_video_format_to_string (conv->current_shader->from),
|
|
gst_video_format_to_string (conv->current_shader->to), *n_constants);
|
|
if (*n_constants <= 0)
|
|
ranges = NULL;
|
|
else
|
|
ranges =
|
|
g_memdup (conv->current_shader->push_constant_ranges,
|
|
sizeof (VkPushConstantRange) * *n_constants);
|
|
|
|
return ranges;
|
|
}
|
|
|
|
static VkDescriptorSetLayoutBinding
|
|
* gst_vulkan_color_convert_descriptor_set_layout_bindings
|
|
(GstVulkanFullScreenRender * render, guint * n_bindings)
|
|
{
|
|
GstVulkanColorConvert *conv = GST_VULKAN_COLOR_CONVERT (render);
|
|
VkDescriptorSetLayoutBinding *bindings;
|
|
guint i;
|
|
|
|
*n_bindings = 0;
|
|
*n_bindings += GST_VIDEO_INFO_N_PLANES (&render->in_info);
|
|
if (conv->current_shader->uniform_size)
|
|
*n_bindings += 1;
|
|
bindings = g_new0 (VkDescriptorSetLayoutBinding, *n_bindings);
|
|
|
|
GST_DEBUG_OBJECT (conv, "%s->%s has %u descriptor set layout bindings",
|
|
gst_video_format_to_string (conv->current_shader->from),
|
|
gst_video_format_to_string (conv->current_shader->to), *n_bindings);
|
|
|
|
for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&render->in_info); i++) {
|
|
/* *INDENT-OFF* */
|
|
bindings[i] = (VkDescriptorSetLayoutBinding) {
|
|
.binding = i,
|
|
.descriptorCount = 1,
|
|
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
|
.pImmutableSamplers = NULL,
|
|
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT
|
|
};
|
|
/* *INDENT-ON* */
|
|
}
|
|
if (conv->current_shader->uniform_size) {
|
|
/* *INDENT-OFF* */
|
|
bindings[i] = (VkDescriptorSetLayoutBinding) {
|
|
.binding = i,
|
|
.descriptorCount = 1,
|
|
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
|
.pImmutableSamplers = NULL,
|
|
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT
|
|
};
|
|
/* *INDENT-ON* */
|
|
i++;
|
|
}
|
|
|
|
g_assert (i == *n_bindings);
|
|
|
|
return bindings;
|
|
}
|
|
|
|
static VkAttachmentReference
|
|
* gst_vulkan_color_convert_render_pass_attachment_references
|
|
(GstVulkanFullScreenRender * render, guint * n_attachments)
|
|
{
|
|
VkAttachmentReference *attachments;
|
|
int i;
|
|
|
|
*n_attachments = GST_VIDEO_INFO_N_PLANES (&render->out_info);
|
|
attachments = g_new0 (VkAttachmentReference, *n_attachments);
|
|
|
|
for (i = 0; i < *n_attachments; i++) {
|
|
/* *INDENT-OFF* */
|
|
attachments[i] = (VkAttachmentReference) {
|
|
.attachment = i,
|
|
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
|
|
};
|
|
/* *INDENT-ON* */
|
|
}
|
|
|
|
return attachments;
|
|
}
|
|
|
|
static VkAttachmentDescription
|
|
* gst_vulkan_color_convert_render_pass_attachment_descriptions
|
|
(GstVulkanFullScreenRender * render, guint * n_descriptions)
|
|
{
|
|
VkAttachmentDescription *color_attachments;
|
|
int i;
|
|
|
|
*n_descriptions = GST_VIDEO_INFO_N_PLANES (&render->out_info);
|
|
color_attachments = g_new0 (VkAttachmentDescription, *n_descriptions);
|
|
for (i = 0; i < *n_descriptions; i++) {
|
|
/* *INDENT-OFF* */
|
|
color_attachments[i] = (VkAttachmentDescription) {
|
|
.format = gst_vulkan_format_from_video_format (GST_VIDEO_INFO_FORMAT (&render->out_info), i),
|
|
.samples = VK_SAMPLE_COUNT_1_BIT,
|
|
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
|
|
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
|
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
|
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
|
/* FIXME: share this between elements to avoid pipeline barriers */
|
|
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
|
.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
|
|
};
|
|
/* *INDENT-ON* */
|
|
}
|
|
|
|
return color_attachments;
|
|
}
|
|
|
|
static VkSampler
|
|
_create_sampler (GstVulkanColorConvert * conv)
|
|
{
|
|
GstVulkanFullScreenRender *render = GST_VULKAN_FULL_SCREEN_RENDER (conv);
|
|
|
|
/* *INDENT-OFF* */
|
|
VkSamplerCreateInfo samplerInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
|
.magFilter = VK_FILTER_LINEAR,
|
|
.minFilter = VK_FILTER_LINEAR,
|
|
.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
|
|
.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
|
|
.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
|
|
.anisotropyEnable = VK_FALSE,
|
|
.maxAnisotropy = 1,
|
|
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
|
|
.unnormalizedCoordinates = VK_FALSE,
|
|
.compareEnable = VK_FALSE,
|
|
.compareOp = VK_COMPARE_OP_ALWAYS,
|
|
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
|
|
.mipLodBias = 0.0f,
|
|
.minLod = 0.0f,
|
|
.maxLod = 0.0f
|
|
};
|
|
/* *INDENT-ON* */
|
|
GError *error = NULL;
|
|
VkSampler sampler;
|
|
VkResult err;
|
|
|
|
err = vkCreateSampler (render->device->device, &samplerInfo, NULL, &sampler);
|
|
if (gst_vulkan_error_to_g_error (err, &error, "vkCreateSampler") < 0) {
|
|
GST_ERROR_OBJECT (conv, "Failed to create sampler: %s", error->message);
|
|
g_clear_error (&error);
|
|
return NULL;
|
|
}
|
|
|
|
return sampler;
|
|
}
|
|
|
|
static gboolean
|
|
gst_vulkan_color_convert_start (GstBaseTransform * bt)
|
|
{
|
|
GstVulkanColorConvert *conv = GST_VULKAN_COLOR_CONVERT (bt);
|
|
|
|
if (!GST_BASE_TRANSFORM_CLASS (parent_class)->start (bt))
|
|
return FALSE;
|
|
|
|
if (!(conv->sampler = _create_sampler (conv)))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static VkDescriptorPool
|
|
_create_descriptor_pool (GstVulkanColorConvert * conv)
|
|
{
|
|
GstVulkanFullScreenRender *render = GST_VULKAN_FULL_SCREEN_RENDER (conv);
|
|
|
|
/* *INDENT-OFF* */
|
|
VkDescriptorPoolSize pool_sizes[] = {
|
|
{
|
|
.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
|
.descriptorCount = GST_VIDEO_INFO_N_PLANES (&render->in_info),
|
|
},
|
|
{
|
|
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
|
.descriptorCount = 1
|
|
},
|
|
};
|
|
|
|
VkDescriptorPoolCreateInfo pool_info = {
|
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
|
|
.pNext = NULL,
|
|
.poolSizeCount = G_N_ELEMENTS (pool_sizes),
|
|
.pPoolSizes = pool_sizes,
|
|
.maxSets = 1
|
|
};
|
|
/* *INDENT-ON* */
|
|
VkDescriptorPool pool;
|
|
GError *error = NULL;
|
|
VkResult err;
|
|
|
|
err =
|
|
vkCreateDescriptorPool (render->device->device, &pool_info, NULL, &pool);
|
|
if (gst_vulkan_error_to_g_error (err, &error, "vkCreateDescriptorPool") < 0) {
|
|
GST_ERROR_OBJECT (render, "Failed to create descriptor pool: %s",
|
|
error->message);
|
|
g_clear_error (&error);
|
|
return NULL;
|
|
}
|
|
|
|
return pool;
|
|
}
|
|
|
|
static VkDescriptorSet
|
|
_create_descriptor_set (GstVulkanColorConvert * conv)
|
|
{
|
|
GstVulkanFullScreenRender *render = GST_VULKAN_FULL_SCREEN_RENDER (conv);
|
|
|
|
/* *INDENT-OFF* */
|
|
VkDescriptorSetAllocateInfo alloc_info = {
|
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
|
.pNext = NULL,
|
|
.descriptorPool = conv->descriptor_pool,
|
|
.descriptorSetCount = 1,
|
|
.pSetLayouts = &render->descriptor_set_layout
|
|
};
|
|
/* *INDENT-ON* */
|
|
VkDescriptorSet descriptor;
|
|
GError *error = NULL;
|
|
VkResult err;
|
|
|
|
err =
|
|
vkAllocateDescriptorSets (render->device->device, &alloc_info,
|
|
&descriptor);
|
|
if (gst_vulkan_error_to_g_error (err, &error, "vkAllocateDescriptorSets") < 0) {
|
|
GST_ERROR_OBJECT (conv, "Failed to allocate descriptor: %s",
|
|
error->message);
|
|
g_clear_error (&error);
|
|
return NULL;
|
|
}
|
|
|
|
return descriptor;
|
|
}
|
|
|
|
static gboolean
|
|
_create_uniform_buffer (GstVulkanColorConvert * conv)
|
|
{
|
|
GstVulkanFullScreenRender *render = GST_VULKAN_FULL_SCREEN_RENDER (conv);
|
|
|
|
if (conv->current_shader->uniform_size) {
|
|
conv->uniform =
|
|
gst_vulkan_buffer_memory_alloc (render->device,
|
|
conv->current_shader->uniform_size,
|
|
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
|
|
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
|
|
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_vulkan_color_convert_set_caps (GstBaseTransform * bt, GstCaps * in_caps,
|
|
GstCaps * out_caps)
|
|
{
|
|
GstVulkanColorConvert *conv = GST_VULKAN_COLOR_CONVERT (bt);
|
|
GstVulkanFullScreenRender *render = GST_VULKAN_FULL_SCREEN_RENDER (bt);
|
|
GstVideoInfo in_info, out_info;
|
|
int i;
|
|
|
|
conv->current_shader = NULL;
|
|
|
|
if (!gst_video_info_from_caps (&in_info, in_caps))
|
|
return FALSE;
|
|
if (!gst_video_info_from_caps (&out_info, out_caps))
|
|
return FALSE;
|
|
|
|
if (conv->current_shader) {
|
|
conv->current_shader->notify (conv->current_shader);
|
|
conv->current_shader = NULL;
|
|
}
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (shader_infos); i++) {
|
|
if (shader_infos[i].from != GST_VIDEO_INFO_FORMAT (&in_info))
|
|
continue;
|
|
if (shader_infos[i].to != GST_VIDEO_INFO_FORMAT (&out_info))
|
|
continue;
|
|
|
|
GST_INFO_OBJECT (conv,
|
|
"Found compatible conversion information from %s to %s",
|
|
gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (&in_info)),
|
|
gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (&out_info)));
|
|
conv->current_shader = &shader_infos[i];
|
|
}
|
|
|
|
if (!conv->current_shader) {
|
|
GST_ERROR_OBJECT (conv, "Could not find a conversion info for the "
|
|
"requested formats");
|
|
return FALSE;
|
|
}
|
|
|
|
if (render->last_fence) {
|
|
if (conv->descriptor_pool)
|
|
gst_vulkan_trash_list_add (render->trash_list,
|
|
gst_vulkan_trash_new_free_descriptor_pool (gst_vulkan_fence_ref
|
|
(render->last_fence), conv->descriptor_pool));
|
|
conv->descriptor_set = NULL;
|
|
conv->descriptor_pool = NULL;
|
|
if (conv->uniform)
|
|
gst_vulkan_trash_list_add (render->trash_list,
|
|
gst_vulkan_trash_new_mini_object_unref (gst_vulkan_fence_ref
|
|
(render->last_fence), (GstMiniObject *) conv->uniform));
|
|
conv->uniform = NULL;
|
|
} else {
|
|
if (conv->descriptor_pool)
|
|
vkDestroyDescriptorPool (render->device->device,
|
|
conv->descriptor_pool, NULL);
|
|
conv->descriptor_set = NULL;
|
|
conv->descriptor_pool = NULL;
|
|
if (conv->uniform)
|
|
gst_memory_unref (conv->uniform);
|
|
conv->uniform = NULL;
|
|
}
|
|
|
|
if (!GST_BASE_TRANSFORM_CLASS (parent_class)->set_caps (bt, in_caps,
|
|
out_caps))
|
|
return FALSE;
|
|
|
|
if (!(conv->descriptor_pool = _create_descriptor_pool (conv)))
|
|
return FALSE;
|
|
if (!(conv->descriptor_set = _create_descriptor_set (conv)))
|
|
return FALSE;
|
|
|
|
if (!_create_uniform_buffer (conv))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_vulkan_color_convert_stop (GstBaseTransform * bt)
|
|
{
|
|
GstVulkanColorConvert *conv = GST_VULKAN_COLOR_CONVERT (bt);
|
|
GstVulkanFullScreenRender *render = GST_VULKAN_FULL_SCREEN_RENDER (bt);
|
|
|
|
conv->current_shader = NULL;
|
|
|
|
if (render->device) {
|
|
if (render->last_fence) {
|
|
if (conv->descriptor_pool)
|
|
gst_vulkan_trash_list_add (render->trash_list,
|
|
gst_vulkan_trash_new_free_descriptor_pool (gst_vulkan_fence_ref
|
|
(render->last_fence), conv->descriptor_pool));
|
|
conv->descriptor_set = NULL;
|
|
conv->descriptor_pool = NULL;
|
|
if (conv->sampler)
|
|
gst_vulkan_trash_list_add (render->trash_list,
|
|
gst_vulkan_trash_new_free_sampler (gst_vulkan_fence_ref
|
|
(render->last_fence), conv->sampler));
|
|
conv->sampler = NULL;
|
|
if (conv->uniform)
|
|
gst_vulkan_trash_list_add (render->trash_list,
|
|
gst_vulkan_trash_new_mini_object_unref (gst_vulkan_fence_ref
|
|
(render->last_fence), (GstMiniObject *) conv->uniform));
|
|
conv->uniform = NULL;
|
|
} else {
|
|
if (conv->descriptor_pool)
|
|
vkDestroyDescriptorPool (render->device->device,
|
|
conv->descriptor_pool, NULL);
|
|
conv->descriptor_set = NULL;
|
|
conv->descriptor_pool = NULL;
|
|
if (conv->sampler)
|
|
vkDestroySampler (render->device->device, conv->sampler, NULL);
|
|
conv->sampler = NULL;
|
|
if (conv->uniform)
|
|
gst_memory_unref (conv->uniform);
|
|
conv->uniform = NULL;
|
|
}
|
|
}
|
|
|
|
if (conv->cmd_pool)
|
|
gst_object_unref (conv->cmd_pool);
|
|
conv->cmd_pool = NULL;
|
|
|
|
return GST_BASE_TRANSFORM_CLASS (parent_class)->stop (bt);
|
|
}
|
|
|
|
static VkFramebuffer
|
|
_create_framebuffer (GstVulkanColorConvert * conv, guint n_views,
|
|
VkImageView * views)
|
|
{
|
|
GstVulkanFullScreenRender *render = GST_VULKAN_FULL_SCREEN_RENDER (conv);
|
|
|
|
/* *INDENT-OFF* */
|
|
VkFramebufferCreateInfo framebuffer_info = {
|
|
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
|
.pNext = NULL,
|
|
.renderPass = render->render_pass,
|
|
.attachmentCount = n_views,
|
|
.pAttachments = views,
|
|
.width = GST_VIDEO_INFO_WIDTH (&render->out_info),
|
|
.height = GST_VIDEO_INFO_HEIGHT (&render->out_info),
|
|
.layers = 1
|
|
};
|
|
/* *INDENT-ON* */
|
|
VkFramebuffer framebuffer;
|
|
GError *error = NULL;
|
|
VkResult err;
|
|
|
|
err =
|
|
vkCreateFramebuffer (render->device->device, &framebuffer_info, NULL,
|
|
&framebuffer);
|
|
if (gst_vulkan_error_to_g_error (err, &error, "vkCreateFramebuffer") < 0) {
|
|
GST_ERROR_OBJECT (render, "Failed to create framebuffer: %s",
|
|
error->message);
|
|
g_clear_error (&error);
|
|
return NULL;
|
|
}
|
|
|
|
return framebuffer;
|
|
}
|
|
|
|
static GstFlowReturn
|
|
gst_vulkan_color_convert_transform (GstBaseTransform * bt, GstBuffer * inbuf,
|
|
GstBuffer * outbuf)
|
|
{
|
|
GstVulkanFullScreenRender *render = GST_VULKAN_FULL_SCREEN_RENDER (bt);
|
|
GstVulkanColorConvert *conv = GST_VULKAN_COLOR_CONVERT (bt);
|
|
GstVulkanImageMemory *in_img_mems[GST_VIDEO_MAX_PLANES] = { NULL, };
|
|
GstVulkanImageView *in_img_views[GST_VIDEO_MAX_PLANES] = { NULL, };
|
|
GstVulkanImageMemory *render_img_mems[GST_VIDEO_MAX_PLANES] = { NULL, };
|
|
GstVulkanImageView *render_img_views[GST_VIDEO_MAX_PLANES] = { NULL, };
|
|
GstVulkanImageMemory *out_img_mems[GST_VIDEO_MAX_PLANES] = { NULL, };
|
|
GstVulkanFence *fence = NULL;
|
|
VkFramebuffer framebuffer;
|
|
GstVulkanCommandBuffer *cmd_buf;
|
|
GError *error = NULL;
|
|
VkResult err;
|
|
int i;
|
|
|
|
fence = gst_vulkan_fence_new (render->device, 0, &error);
|
|
if (!fence)
|
|
goto error;
|
|
|
|
for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&render->in_info); i++) {
|
|
GstMemory *mem = gst_buffer_peek_memory (inbuf, i);
|
|
if (!gst_is_vulkan_image_memory (mem)) {
|
|
g_set_error_literal (&error, GST_VULKAN_ERROR, GST_VULKAN_FAILED,
|
|
"Input memory must be a GstVulkanImageMemory");
|
|
goto error;
|
|
}
|
|
in_img_mems[i] = (GstVulkanImageMemory *) mem;
|
|
in_img_views[i] = get_or_create_image_view (in_img_mems[i]);
|
|
gst_vulkan_trash_list_add (render->trash_list,
|
|
gst_vulkan_trash_new_mini_object_unref (gst_vulkan_fence_ref (fence),
|
|
(GstMiniObject *) in_img_views[i]));
|
|
}
|
|
|
|
for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&render->out_info); i++) {
|
|
GstMemory *mem = gst_buffer_peek_memory (outbuf, i);
|
|
if (!gst_is_vulkan_image_memory (mem)) {
|
|
g_set_error_literal (&error, GST_VULKAN_ERROR, GST_VULKAN_FAILED,
|
|
"Output memory must be a GstVulkanImageMemory");
|
|
goto error;
|
|
}
|
|
out_img_mems[i] = (GstVulkanImageMemory *) mem;
|
|
}
|
|
|
|
if (!conv->cmd_pool) {
|
|
if (!(conv->cmd_pool =
|
|
gst_vulkan_queue_create_command_pool (render->queue, &error)))
|
|
goto error;
|
|
}
|
|
|
|
if (!(cmd_buf = gst_vulkan_command_pool_create (conv->cmd_pool, &error)))
|
|
goto error;
|
|
|
|
{
|
|
VkCommandBufferBeginInfo cmd_buf_info = { 0, };
|
|
|
|
/* *INDENT-OFF* */
|
|
cmd_buf_info = (VkCommandBufferBeginInfo) {
|
|
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
|
.pNext = NULL,
|
|
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
|
|
.pInheritanceInfo = NULL
|
|
};
|
|
/* *INDENT-ON* */
|
|
|
|
gst_vulkan_command_buffer_lock (cmd_buf);
|
|
err = vkBeginCommandBuffer (cmd_buf->cmd, &cmd_buf_info);
|
|
if (gst_vulkan_error_to_g_error (err, &error, "vkBeginCommandBuffer") < 0)
|
|
goto error;
|
|
}
|
|
|
|
for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&render->in_info); i++) {
|
|
/* *INDENT-OFF* */
|
|
VkImageMemoryBarrier in_image_memory_barrier = {
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
|
.pNext = NULL,
|
|
.srcAccessMask = in_img_mems[i]->barrier.parent.access_flags,
|
|
.dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT,
|
|
.oldLayout = in_img_mems[i]->barrier.image_layout,
|
|
.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
|
/* FIXME: implement exclusive transfers */
|
|
.srcQueueFamilyIndex = 0,
|
|
.dstQueueFamilyIndex = 0,
|
|
.image = in_img_mems[i]->image,
|
|
.subresourceRange = in_img_mems[i]->barrier.subresource_range
|
|
};
|
|
/* *INDENT-ON* */
|
|
|
|
vkCmdPipelineBarrier (cmd_buf->cmd,
|
|
in_img_mems[i]->barrier.parent.pipeline_stages,
|
|
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0, NULL, 1,
|
|
&in_image_memory_barrier);
|
|
|
|
in_img_mems[i]->barrier.parent.pipeline_stages =
|
|
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
|
in_img_mems[i]->barrier.parent.access_flags =
|
|
in_image_memory_barrier.dstAccessMask;
|
|
in_img_mems[i]->barrier.image_layout = in_image_memory_barrier.newLayout;
|
|
}
|
|
|
|
for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&render->out_info); i++) {
|
|
VkImageMemoryBarrier render_image_memory_barrier;
|
|
|
|
if (GST_VIDEO_INFO_WIDTH (&render->out_info) ==
|
|
GST_VIDEO_INFO_COMP_WIDTH (&render->out_info, i)) {
|
|
render_img_mems[i] = out_img_mems[i];
|
|
} else {
|
|
/* we need a scratch buffer because framebuffers can only output to
|
|
* attachments of at least the same size which means no sub-sampled
|
|
* rendering */
|
|
GstVideoFormat v_format = GST_VIDEO_INFO_FORMAT (&render->out_info);
|
|
VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL;
|
|
VkFormat vk_format;
|
|
GstMemory *mem;
|
|
|
|
vk_format = gst_vulkan_format_from_video_format (v_format, i);
|
|
|
|
mem = gst_vulkan_image_memory_alloc (render->device,
|
|
vk_format, GST_VIDEO_INFO_WIDTH (&render->out_info),
|
|
GST_VIDEO_INFO_HEIGHT (&render->out_info), tiling,
|
|
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
|
|
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
|
render_img_mems[i] = (GstVulkanImageMemory *) mem;
|
|
}
|
|
|
|
/* *INDENT-OFF* */
|
|
render_image_memory_barrier = (VkImageMemoryBarrier) {
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
|
.pNext = NULL,
|
|
.srcAccessMask = render_img_mems[i]->barrier.parent.access_flags,
|
|
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
|
.oldLayout = render_img_mems[i]->barrier.image_layout,
|
|
.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
|
/* FIXME: implement exclusive transfers */
|
|
.srcQueueFamilyIndex = 0,
|
|
.dstQueueFamilyIndex = 0,
|
|
.image = render_img_mems[i]->image,
|
|
.subresourceRange = render_img_mems[i]->barrier.subresource_range
|
|
};
|
|
/* *INDENT-ON* */
|
|
|
|
vkCmdPipelineBarrier (cmd_buf->cmd,
|
|
render_img_mems[i]->barrier.parent.pipeline_stages,
|
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, NULL, 0, NULL, 1,
|
|
&render_image_memory_barrier);
|
|
|
|
render_img_mems[i]->barrier.parent.pipeline_stages =
|
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
|
render_img_mems[i]->barrier.parent.access_flags =
|
|
render_image_memory_barrier.dstAccessMask;
|
|
render_img_mems[i]->barrier.image_layout =
|
|
render_image_memory_barrier.newLayout;
|
|
}
|
|
|
|
{
|
|
VkImageView attachments[4] = { 0, };
|
|
for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&render->out_info); i++) {
|
|
render_img_views[i] = get_or_create_image_view (render_img_mems[i]);
|
|
gst_vulkan_trash_list_add (render->trash_list,
|
|
gst_vulkan_trash_new_mini_object_unref (gst_vulkan_fence_ref (fence),
|
|
(GstMiniObject *) render_img_views[i]));
|
|
attachments[i] = render_img_views[i]->view;
|
|
}
|
|
|
|
if (!(framebuffer = _create_framebuffer (conv,
|
|
GST_VIDEO_INFO_N_PLANES (&render->out_info), attachments))) {
|
|
g_set_error_literal (&error, GST_VULKAN_ERROR, GST_VULKAN_FAILED,
|
|
"Failed to create framebuffer");
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
conv->current_shader->cmd_state_update (conv, cmd_buf->cmd,
|
|
conv->current_shader, in_img_views, render_img_views);
|
|
if (!gst_vulkan_full_screen_render_fill_command_buffer (render, cmd_buf->cmd,
|
|
framebuffer)) {
|
|
g_set_error (&error, GST_VULKAN_ERROR, GST_VULKAN_FAILED,
|
|
"Failed to fill framebuffer");
|
|
goto unlock_error;
|
|
}
|
|
|
|
for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&render->out_info); i++) {
|
|
if (render_img_mems[i] != out_img_mems[i]) {
|
|
VkImageMemoryBarrier out_image_memory_barrier;
|
|
VkImageMemoryBarrier render_image_memory_barrier;
|
|
VkImageBlit blit;
|
|
|
|
/* *INDENT-OFF* */
|
|
render_image_memory_barrier = (VkImageMemoryBarrier) {
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
|
.pNext = NULL,
|
|
.srcAccessMask = render_img_mems[i]->barrier.parent.access_flags,
|
|
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
|
|
.oldLayout = render_img_mems[i]->barrier.image_layout,
|
|
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
|
/* FIXME: implement exclusive transfers */
|
|
.srcQueueFamilyIndex = 0,
|
|
.dstQueueFamilyIndex = 0,
|
|
.image = render_img_mems[i]->image,
|
|
.subresourceRange = render_img_mems[i]->barrier.subresource_range
|
|
};
|
|
out_image_memory_barrier = (VkImageMemoryBarrier) {
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
|
.pNext = NULL,
|
|
.srcAccessMask = out_img_mems[i]->barrier.parent.access_flags,
|
|
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
|
.oldLayout = out_img_mems[i]->barrier.image_layout,
|
|
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
/* FIXME: implement exclusive transfers */
|
|
.srcQueueFamilyIndex = 0,
|
|
.dstQueueFamilyIndex = 0,
|
|
.image = out_img_mems[i]->image,
|
|
.subresourceRange = out_img_mems[i]->barrier.subresource_range
|
|
};
|
|
blit = (VkImageBlit) {
|
|
.srcSubresource = {
|
|
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
|
.mipLevel = 0,
|
|
.baseArrayLayer = 0,
|
|
.layerCount = 1,
|
|
},
|
|
.srcOffsets = {
|
|
{0, 0, 0},
|
|
{GST_VIDEO_INFO_COMP_WIDTH (&render->out_info, i), GST_VIDEO_INFO_COMP_HEIGHT (&render->out_info, i), 1},
|
|
},
|
|
.dstSubresource = {
|
|
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
|
.mipLevel = 0,
|
|
.baseArrayLayer = 0,
|
|
.layerCount = 1,
|
|
},
|
|
.dstOffsets = {
|
|
{0, 0, 0},
|
|
{GST_VIDEO_INFO_COMP_WIDTH (&render->out_info, i), GST_VIDEO_INFO_COMP_HEIGHT (&render->out_info, i), 1},
|
|
},
|
|
};
|
|
/* *INDENT-ON* */
|
|
|
|
vkCmdPipelineBarrier (cmd_buf->cmd,
|
|
render_img_mems[i]->barrier.parent.pipeline_stages,
|
|
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1,
|
|
&render_image_memory_barrier);
|
|
|
|
render_img_mems[i]->barrier.parent.pipeline_stages =
|
|
VK_PIPELINE_STAGE_TRANSFER_BIT;
|
|
render_img_mems[i]->barrier.parent.access_flags =
|
|
render_image_memory_barrier.dstAccessMask;
|
|
render_img_mems[i]->barrier.image_layout =
|
|
render_image_memory_barrier.newLayout;
|
|
|
|
vkCmdPipelineBarrier (cmd_buf->cmd,
|
|
out_img_mems[i]->barrier.parent.pipeline_stages,
|
|
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1,
|
|
&out_image_memory_barrier);
|
|
|
|
out_img_mems[i]->barrier.parent.pipeline_stages =
|
|
VK_PIPELINE_STAGE_TRANSFER_BIT;
|
|
out_img_mems[i]->barrier.parent.access_flags =
|
|
out_image_memory_barrier.dstAccessMask;
|
|
out_img_mems[i]->barrier.image_layout =
|
|
out_image_memory_barrier.newLayout;
|
|
|
|
/* XXX: This is mostly right for a downsampling pass however if
|
|
* anything is more complicated, then we will need a new render pass */
|
|
vkCmdBlitImage (cmd_buf->cmd, render_img_mems[i]->image,
|
|
render_img_mems[i]->barrier.image_layout, out_img_mems[i]->image,
|
|
out_img_mems[i]->barrier.image_layout, 1, &blit, VK_FILTER_LINEAR);
|
|
|
|
/* XXX: try to reuse this image later */
|
|
gst_vulkan_trash_list_add (render->trash_list,
|
|
gst_vulkan_trash_new_mini_object_unref (gst_vulkan_fence_ref (fence),
|
|
(GstMiniObject *) render_img_mems[i]));
|
|
}
|
|
}
|
|
|
|
err = vkEndCommandBuffer (cmd_buf->cmd);
|
|
gst_vulkan_command_buffer_unlock (cmd_buf);
|
|
if (gst_vulkan_error_to_g_error (err, &error, "vkEndCommandBuffer") < 0)
|
|
goto error;
|
|
|
|
gst_vulkan_trash_list_add (render->trash_list,
|
|
gst_vulkan_trash_new_free_framebuffer (gst_vulkan_fence_ref (fence),
|
|
framebuffer));
|
|
gst_vulkan_trash_list_add (render->trash_list,
|
|
gst_vulkan_trash_new_mini_object_unref (gst_vulkan_fence_ref (fence),
|
|
GST_MINI_OBJECT_CAST (cmd_buf)));
|
|
|
|
if (!gst_vulkan_full_screen_render_submit (render, cmd_buf->cmd, fence))
|
|
return GST_FLOW_ERROR;
|
|
|
|
return GST_FLOW_OK;
|
|
|
|
unlock_error:
|
|
if (cmd_buf) {
|
|
gst_vulkan_command_buffer_unlock (cmd_buf);
|
|
gst_vulkan_command_buffer_unref (cmd_buf);
|
|
}
|
|
error:
|
|
GST_ELEMENT_ERROR (bt, LIBRARY, FAILED, ("%s", error->message), (NULL));
|
|
g_clear_error (&error);
|
|
return GST_FLOW_ERROR;
|
|
}
|