vl,frontends/va: Implement gamma and primaries conversion

This enables basic HDR support.

Acked-by: Ruijing Dong <ruijing.dong@amd.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/37058>
This commit is contained in:
David Rosca 2025-10-02 11:15:32 +02:00 committed by Marge Bot
parent 5bc0df5aad
commit 69717c257f
7 changed files with 284 additions and 20 deletions

View file

@ -888,7 +888,7 @@ vl_compositor_init_state(struct vl_compositor_state *s, struct pipe_context *pip
pipe->screen,
PIPE_BIND_CONSTANT_BUFFER,
PIPE_USAGE_DEFAULT,
sizeof(vl_csc_matrix) * 2 + 32 * sizeof(float) + 2 * sizeof(int)
sizeof(vl_csc_matrix) * 3 + 30 * sizeof(float) + 4 * sizeof(int)
);
if (!s->shader_params)

View file

@ -131,8 +131,11 @@ struct vl_compositor_state
struct vl_compositor_layer layers[VL_COMPOSITOR_MAX_LAYERS];
bool interlaced;
unsigned chroma_location;
enum pipe_video_vpp_transfer_characteristic in_transfer_characteristic;
enum pipe_video_vpp_transfer_characteristic out_transfer_characteristic;
vl_csc_matrix yuv2rgb;
vl_csc_matrix rgb2yuv;
vl_csc_matrix primaries;
vl_csc_matrix csc_matrix;
float luma_min, luma_max;

View file

@ -55,7 +55,7 @@ struct cs_shader {
unsigned num_samplers;
nir_variable *samplers[3];
nir_variable *image;
nir_def *params[14];
nir_def *params[17];
nir_def *fone;
nir_def *fzero;
};
@ -69,6 +69,7 @@ enum coords_flags {
enum color_conversion {
YUV2RGB,
RGB2YUV,
PRIMARIES,
};
static nir_def *cs_create_shader(struct vl_compositor *c, struct cs_shader *s)
@ -86,6 +87,8 @@ static nir_def *cs_create_shader(struct vl_compositor *c, struct cs_shader *s)
float luma_min; // params[3].x
float luma_max; // params[3].y
vec2 chroma_offset; // params[3].zw
int trc_in; // params[4].x
int trc_out; // params[4].y
ivec2 translate; // params[4].zw
vec2 sampler0_wh; // params[5].xy
vec2 subsample_ratio; // params[5].zw
@ -94,6 +97,7 @@ static nir_def *cs_create_shader(struct vl_compositor *c, struct cs_shader *s)
vec4 proj[3]; // params[7-8]
vec4 chroma_proj[3]; // params[9-10]
vec4 rgb2yuv[3]; // params[11-13]
vec4 primaries[3]; // params[14-16]
};
void main()
@ -252,14 +256,168 @@ static inline nir_def *cs_normalize(struct cs_shader *s, nir_def *src, unsigned
return nir_fdiv(b, src, div);
}
static inline nir_def *cs_color_space_conversion(struct cs_shader *s, nir_def *src,
unsigned comp, enum color_conversion conversion)
static nir_def *cs_color_conversion(struct cs_shader *s, nir_def *src,
unsigned comp, enum color_conversion conversion)
{
/*
return dot(src, params[(conversion == RGB2YUV ? 11 : 0) + comp]);
return dot(src, params[idx + comp]);
*/
nir_builder *b = &s->b;
return nir_fdot4(b, src, s->params[(conversion == RGB2YUV ? 11 : 0) + comp]);
unsigned idx = 0;
if (conversion == RGB2YUV)
idx = 11;
else if (conversion == PRIMARIES)
idx = 14;
return nir_fdot4(b, src, s->params[idx + comp]);
}
#define SDR_WHITE (203.0)
#define PQ_C1 (3424.0 / 4096.0)
#define PQ_C2 (2413.0 / 4096.0 * 32.0)
#define PQ_C3 (2392.0 / 4096.0 * 32.0)
#define PQ_M1 (2610.0 / 4096.0 * 0.25)
#define PQ_M2 (2523.0 / 4096.0 * 128.0)
static void cs_trc_to_linear(struct cs_shader *s, nir_def *src, nir_variable **out, unsigned trc)
{
nir_builder *b = &s->b;
for (unsigned i = 0; i < 3; ++i) {
nir_def *c = nir_fmax(b, nir_channel(b, src, i), s->fzero);
switch (trc) {
case PIPE_VIDEO_VPP_TRC_GAMMA22:
c = nir_fpow_imm(b, c, 2.2f);
break;
case PIPE_VIDEO_VPP_TRC_GAMMA28:
c = nir_fpow_imm(b, c, 2.8f);
break;
case PIPE_VIDEO_VPP_TRC_LINEAR:
break;
case PIPE_VIDEO_VPP_TRC_IEC61966_2_1:
/* sRGB */
nir_push_if(b, nir_fgt_imm(b, c, 0.04045f));
nir_def *p1 = nir_fadd_imm(b, c, 0.055f);
p1 = nir_fpow_imm(b, nir_fdiv_imm(b, p1, 1.055f), 2.4f);
nir_push_else(b, NULL);
nir_def *p2 = nir_fdiv_imm(b, c, 12.92f);
nir_pop_if(b, NULL);
c = nir_if_phi(b, p1, p2);
break;
case PIPE_VIDEO_VPP_TRC_SMPTE2084: {
/* PQ */
c = nir_fpow_imm(b, c, 1.0f / PQ_M2);
nir_def *d1 = nir_fmax(b, nir_fadd_imm(b, c, -PQ_C1), s->fzero);
nir_def *d2 = nir_fsub_imm(b, PQ_C2, nir_fmul_imm(b, c, PQ_C3));
c = nir_fmul_imm(b, nir_fpow_imm(b, nir_fdiv(b, d1, d2), 1.0f / PQ_M1), 10000.0f / SDR_WHITE);
break;
}
case PIPE_VIDEO_VPP_TRC_BT709:
case PIPE_VIDEO_VPP_TRC_SMPTE170M:
case PIPE_VIDEO_VPP_TRC_SMPTE240M:
case PIPE_VIDEO_VPP_TRC_IEC61966_2_4:
case PIPE_VIDEO_VPP_TRC_BT1361_ECG:
case PIPE_VIDEO_VPP_TRC_BT2020_10:
case PIPE_VIDEO_VPP_TRC_BT2020_12:
default:
/* BT.1886 Lb=0 Lw=1 */
c = nir_fpow_imm(b, c, 2.4f);
break;
}
nir_store_var(b, out[i], c, 0x1);
}
}
static void cs_trc_from_linear(struct cs_shader *s, nir_def *src, nir_variable **out, unsigned trc)
{
nir_builder *b = &s->b;
for (unsigned i = 0; i < 3; ++i) {
nir_def *c = nir_fmax(b, nir_channel(b, src, i), s->fzero);
switch (trc) {
case PIPE_VIDEO_VPP_TRC_GAMMA22:
c = nir_fpow_imm(b, c, 1.0f / 2.2f);
break;
case PIPE_VIDEO_VPP_TRC_GAMMA28:
c = nir_fpow_imm(b, c, 1.0f / 2.8f);
break;
case PIPE_VIDEO_VPP_TRC_LINEAR:
break;
case PIPE_VIDEO_VPP_TRC_IEC61966_2_1:
/* sRGB */
nir_push_if(b, nir_fge_imm(b, c, 0.0031308f));
nir_def *p1 = nir_fpow_imm(b, c, 1.0f / 2.4f);
p1 = nir_fadd_imm(b, nir_fmul_imm(b, p1, 1.055f), -0.055f);
nir_push_else(b, NULL);
nir_def *p2 = nir_fmul_imm(b, c, 12.92f);
nir_pop_if(b, NULL);
c = nir_if_phi(b, p1, p2);
break;
case PIPE_VIDEO_VPP_TRC_SMPTE2084: {
/* PQ */
c = nir_fpow_imm(b, nir_fmul_imm(b, c, SDR_WHITE / 10000.0f), PQ_M1);
nir_def *d1 = nir_fadd_imm(b, nir_fmul_imm(b, c, PQ_C2), PQ_C1);
nir_def *d2 = nir_fadd_imm(b, nir_fmul_imm(b, c, PQ_C3), 1.0f);
c = nir_fpow_imm(b, nir_fdiv(b, d1, d2), PQ_M2);
break;
}
case PIPE_VIDEO_VPP_TRC_BT709:
case PIPE_VIDEO_VPP_TRC_SMPTE170M:
case PIPE_VIDEO_VPP_TRC_SMPTE240M:
case PIPE_VIDEO_VPP_TRC_IEC61966_2_4:
case PIPE_VIDEO_VPP_TRC_BT1361_ECG:
case PIPE_VIDEO_VPP_TRC_BT2020_10:
case PIPE_VIDEO_VPP_TRC_BT2020_12:
default:
/* BT.1886 Lb=0 Lw=1 */
c = nir_fpow_imm(b, c, 1.0f / 2.4f);
break;
}
nir_store_var(b, out[i], c, 0x1);
}
}
static nir_def *cs_trc_apply(struct cs_shader *s, nir_def *src, bool in,
void (*trc_func)(struct cs_shader *, nir_def *, nir_variable **, unsigned))
{
nir_builder *b = &s->b;
nir_def *trc = nir_channels(b, s->params[4], in ? 0x1 : 0x2);
nir_variable *col[3] = {
nir_local_variable_create(b->impl, glsl_float_type(), "col0"),
nir_local_variable_create(b->impl, glsl_float_type(), "col1"),
nir_local_variable_create(b->impl, glsl_float_type(), "col2"),
};
enum pipe_video_vpp_transfer_characteristic trcs[] = {
PIPE_VIDEO_VPP_TRC_GAMMA22,
PIPE_VIDEO_VPP_TRC_GAMMA28,
PIPE_VIDEO_VPP_TRC_LINEAR,
PIPE_VIDEO_VPP_TRC_IEC61966_2_1,
PIPE_VIDEO_VPP_TRC_SMPTE2084,
};
for (unsigned i = 0; i < ARRAY_SIZE(trcs); i++) {
nir_push_if(b, nir_ieq_imm(b, trc, trcs[i]));
trc_func(s, src, col, trcs[i]);
nir_push_else(b, NULL);
}
trc_func(s, src, col, PIPE_VIDEO_VPP_TRC_BT709);
for (unsigned i = 0; i < ARRAY_SIZE(trcs); i++)
nir_pop_if(b, NULL);
return nir_vec4(b, nir_load_var(b, col[0]), nir_load_var(b, col[1]),
nir_load_var(b, col[2]), s->fone);
}
static nir_def *cs_prim_trc_conversion(struct cs_shader *s, nir_def *src)
{
nir_def *col[3];
nir_def *color = cs_trc_apply(s, src, true, cs_trc_to_linear);
for (unsigned i = 0; i < 3; i++)
col[i] = cs_color_conversion(s, color, i, PRIMARIES);
color = nir_vec4(&s->b, col[0], col[1], col[2], s->fone);
return cs_trc_apply(s, color, false, cs_trc_from_linear);
}
static inline nir_def *cs_fetch_texel(struct cs_shader *s, nir_def *coords, unsigned sampler)
@ -342,9 +500,10 @@ static void *create_video_buffer_shader(struct vl_compositor *c)
nir_def *color = nir_vec4(b, col[0], col[1], col[2], s.fone);
for (unsigned i = 0; i < 3; ++i)
col[i] = cs_color_space_conversion(&s, color, i, YUV2RGB);
col[i] = cs_color_conversion(&s, color, i, YUV2RGB);
color = nir_vec4(b, col[0], col[1], col[2], alpha);
color = cs_prim_trc_conversion(&s, color);
cs_image_store(&s, cs_translate(&s, ipos), color);
return cs_create_shader_state(c, &s);
@ -370,12 +529,13 @@ static void *create_yuv_progressive_shader(struct vl_compositor *c, enum vl_comp
nir_def *color = nir_vec4(b, col[0], col[1], col[2], s.fone);
for (unsigned i = 0; i < 3; i++)
col[i] = cs_color_space_conversion(&s, color, i, YUV2RGB);
col[i] = cs_color_conversion(&s, color, i, YUV2RGB);
color = nir_vec4(b, col[0], col[1], col[2], s.fone);
color = cs_prim_trc_conversion(&s, color);
for (unsigned i = 0; i < 3; i++)
col[i] = cs_color_space_conversion(&s, color, i, RGB2YUV);
col[i] = cs_color_conversion(&s, color, i, RGB2YUV);
color = nir_vec4(b, col[0], col[1], col[2], s.fone);
@ -456,6 +616,7 @@ static void *create_rgb_yuv_shader(struct vl_compositor *c, enum vl_compositor_p
}
color = nir_vector_insert_imm(b, color, s.fone, 3);
color = cs_prim_trc_conversion(&s, color);
if (plane != VL_COMPOSITOR_PLANE_UV) {
unsigned c = 0;
@ -463,10 +624,10 @@ static void *create_rgb_yuv_shader(struct vl_compositor *c, enum vl_compositor_p
c = 1;
else if (plane == VL_COMPOSITOR_PLANE_V)
c = 2;
color = cs_color_space_conversion(&s, color, c, RGB2YUV);
color = cs_color_conversion(&s, color, c, RGB2YUV);
} else {
nir_def *col1 = cs_color_space_conversion(&s, color, 1, RGB2YUV);
nir_def *col2 = cs_color_space_conversion(&s, color, 2, RGB2YUV);
nir_def *col1 = cs_color_conversion(&s, color, 1, RGB2YUV);
nir_def *col2 = cs_color_conversion(&s, color, 2, RGB2YUV);
color = nir_vec2(b, col1, col2);
}
@ -584,7 +745,7 @@ static nir_def *create_weave_shader(struct vl_compositor *c, bool rgb, bool y)
if (rgb) {
nir_def *alpha = cs_luma_key(&s, nir_channel(b, color, 2));
for (unsigned i = 0; i < 3; ++i)
col[i] = cs_color_space_conversion(&s, color, i, YUV2RGB);
col[i] = cs_color_conversion(&s, color, i, YUV2RGB);
color = nir_vec4(b, col[0], col[1], col[2], alpha);
} else if (y) {
color = nir_channel(b, color, 0);
@ -806,9 +967,10 @@ set_viewport(struct vl_compositor_state *s,
*ptr_float++ = s->luma_max;
*ptr_float++ = drawn->chroma_offset_x;
*ptr_float++ = drawn->chroma_offset_y;
ptr_float += 2; /* pad */
int *ptr_int = (int *)ptr_float;
*ptr_int++ = s->in_transfer_characteristic;
*ptr_int++ = s->out_transfer_characteristic;
*ptr_int++ = drawn->translate_x;
*ptr_int++ = drawn->translate_y;
@ -843,6 +1005,10 @@ set_viewport(struct vl_compositor_state *s,
ptr_float += sizeof(drawn->chroma_proj) / sizeof(float);
memcpy(ptr_float, &s->rgb2yuv, sizeof(vl_csc_matrix));
ptr_float += sizeof(vl_csc_matrix) / sizeof(float);
memcpy(ptr_float, &s->primaries, sizeof(vl_csc_matrix));
ptr_float += sizeof(vl_csc_matrix) / sizeof(float);
pipe_buffer_unmap(s->pipe, buf_transfer);

View file

@ -177,3 +177,84 @@ void vl_csc_get_rgbyuv_matrix(enum pipe_video_vpp_matrix_coefficients coefficien
(*matrix)[i][3] += i == 0 ? r_bias : g_bias;
}
}
void vl_csc_get_primaries_matrix(enum pipe_video_vpp_color_primaries in_color_primaries,
enum pipe_video_vpp_color_primaries out_color_primaries,
vl_csc_matrix *matrix)
{
if (in_color_primaries == out_color_primaries) {
memcpy(matrix, &identity, sizeof(vl_csc_matrix));
return;
}
switch (in_color_primaries) {
case PIPE_VIDEO_VPP_PRI_SMPTE170M:
case PIPE_VIDEO_VPP_PRI_SMPTE240M:
switch (out_color_primaries) {
case PIPE_VIDEO_VPP_PRI_BT2020:
memcpy(matrix, &(vl_csc_matrix){
{ 0.595254, 0.349314, 0.055432, 0.0 },
{ 0.081244, 0.891503, 0.027253, 0.0 },
{ 0.015512, 0.081912, 0.902576, 0.0 },
}, sizeof(vl_csc_matrix));
break;
case PIPE_VIDEO_VPP_PRI_BT709:
default:
memcpy(matrix, &(vl_csc_matrix){
{ 0.939543, 0.050181, 0.010276, 0.0 },
{ 0.017772, 0.965793, 0.016435, 0.0 },
{ -0.001622, -0.004370, 1.005991, 0.0 },
}, sizeof(vl_csc_matrix));
break;
}
break;
case PIPE_VIDEO_VPP_PRI_BT2020:
switch (out_color_primaries) {
case PIPE_VIDEO_VPP_PRI_SMPTE170M:
case PIPE_VIDEO_VPP_PRI_SMPTE240M:
memcpy(matrix, &(vl_csc_matrix){
{ 1.776133, -0.687820, -0.088313, 0.0 },
{ -0.161375, 1.187315, -0.025940, 0.0 },
{ -0.015881, -0.095931, 1.111812, 0.0 },
}, sizeof(vl_csc_matrix));
break;
case PIPE_VIDEO_VPP_PRI_BT709:
default:
memcpy(matrix, &(vl_csc_matrix){
{ 1.660491, -0.587641, -0.072850, 0.0 },
{ -0.124550, 1.132900, -0.008349, 0.0 },
{ -0.018151, -0.100579, 1.118729, 0.0 },
}, sizeof(vl_csc_matrix));
break;
}
break;
case PIPE_VIDEO_VPP_PRI_BT709:
default:
switch (out_color_primaries) {
case PIPE_VIDEO_VPP_PRI_SMPTE170M:
case PIPE_VIDEO_VPP_PRI_SMPTE240M:
memcpy(matrix, &(vl_csc_matrix){
{ 1.065379, -0.055401, -0.009978, 0.0 },
{ -0.019633, 1.036363, -0.016731, 0.0 },
{ 0.001632, 0.004412, 0.993956, 0.0 },
}, sizeof(vl_csc_matrix));
break;
case PIPE_VIDEO_VPP_PRI_BT2020:
memcpy(matrix, &(vl_csc_matrix){
{ 0.627404, 0.329283, 0.043313, 0.0 },
{ 0.069097, 0.919540, 0.011362, 0.0 },
{ 0.016391, 0.088013, 0.895595, 0.0 },
}, sizeof(vl_csc_matrix));
break;
default:
memcpy(matrix, &identity, sizeof(vl_csc_matrix));
}
break;
}
}

View file

@ -39,4 +39,8 @@ void vl_csc_get_rgbyuv_matrix(enum pipe_video_vpp_matrix_coefficients coefficien
enum pipe_video_vpp_color_range out_color_range,
vl_csc_matrix *matrix);
void vl_csc_get_primaries_matrix(enum pipe_video_vpp_color_primaries in_color_primaries,
enum pipe_video_vpp_color_primaries out_color_primaries,
vl_csc_matrix *matrix);
#endif /* vl_csc_h */

View file

@ -105,6 +105,11 @@ vlVaPostProcCompositor(vlVaDriver *drv,
param->in_color_range, param->out_color_range, &drv->cstate.rgb2yuv);
}
vl_csc_get_primaries_matrix(param->in_color_primaries, param->out_color_primaries,
&drv->cstate.primaries);
drv->cstate.in_transfer_characteristic = param->in_transfer_characteristics;
drv->cstate.out_transfer_characteristic = param->out_transfer_characteristics;
if (src_yuv || dst_yuv) {
enum pipe_format format = src_yuv ? src->buffer_format : dst->buffer_format;
@ -173,8 +178,6 @@ vlVaPostProcCompositor(vlVaDriver *drv,
vl_compositor_render(&drv->cstate, &drv->compositor, &surfaces[0], NULL, false);
}
drv->cstate.chroma_location = VL_COMPOSITOR_LOCATION_NONE;
return VA_STATUS_SUCCESS;
}

View file

@ -360,6 +360,7 @@ vlVaPutSurface(VADriverContextP ctx, VASurfaceID surface_id, void* draw, short s
enum pipe_format format;
VAStatus status;
enum pipe_video_vpp_matrix_coefficients coeffs;
enum pipe_video_vpp_color_primaries primaries;
if (!ctx)
return VA_STATUS_ERROR_INVALID_CONTEXT;
@ -393,16 +394,22 @@ vlVaPutSurface(VADriverContextP ctx, VASurfaceID surface_id, void* draw, short s
format = surf->buffer->buffer_format;
if (flags & VA_SRC_BT601)
if (flags & VA_SRC_BT601) {
coeffs = PIPE_VIDEO_VPP_MCF_SMPTE170M;
else if (flags & VA_SRC_SMPTE_240)
coeffs = PIPE_VIDEO_VPP_MCF_SMPTE240M;
else
primaries = PIPE_VIDEO_VPP_PRI_SMPTE170M;
} else {
coeffs = PIPE_VIDEO_VPP_MCF_BT709;
primaries = PIPE_VIDEO_VPP_PRI_BT709;
}
vl_csc_get_rgbyuv_matrix(coeffs, format, surf_templ.format,
PIPE_VIDEO_VPP_CHROMA_COLOR_RANGE_REDUCED,
PIPE_VIDEO_VPP_CHROMA_COLOR_RANGE_FULL, &drv->cstate.yuv2rgb);
vl_csc_get_primaries_matrix(primaries, PIPE_VIDEO_VPP_PRI_BT709, &drv->cstate.primaries);
drv->cstate.in_transfer_characteristic = PIPE_VIDEO_VPP_TRC_BT709;
drv->cstate.out_transfer_characteristic = PIPE_VIDEO_VPP_TRC_BT709;
drv->cstate.chroma_location =
VL_COMPOSITOR_LOCATION_HORIZONTAL_LEFT | VL_COMPOSITOR_LOCATION_VERTICAL_CENTER;
vl_compositor_clear_layers(&drv->cstate);