From 73713f837b4c0ceda7f33476586931c04a0ec4b9 Mon Sep 17 00:00:00 2001 From: "Agate, Jesse" Date: Tue, 7 Nov 2023 11:36:42 -0500 Subject: [PATCH] amd/vpelib: Studio Range Handling The purpose of this change is to support bg color fill + studio range output. Now, studio range conversion will be done in the gamma block before bg color fill. That is for all studio range outputs adn SDR outputs we will be in NL before blend block. For full range HDR (full range PQ of FP16 only), we will be linear before blend block. - Added linear_0_1 gamma function - Consolidated x_scale and y_scale - Refactor Regam/Degam Funcs - Add y bias to regam and degam - Pull memory allocation for cm into a different function to make it more readable - Added logic to degamma/regamma to re-calculate instead of using table if the scale factors are not one and the bias is not zero. - Changing output gamma programming sequence in sw layer - Fixes in hw layer for interpolation error assocaites with studio range transformation in gamma. - Disabling ocsc - PQ studio will still be 'blended' in NL. But A check was added to reject the job if it is not opaque Reviewed-by: Roy Chan Acked-by: Alan Liu Signed-off-by: Jesse Agate Part-of: --- src/amd/vpelib/src/chip/vpe10/inc/vpe10_mpc.h | 2 +- .../vpelib/src/chip/vpe10/vpe10_cm_common.c | 44 ++ src/amd/vpelib/src/chip/vpe10/vpe10_mpc.c | 68 ++- src/amd/vpelib/src/chip/vpe10/vpe10_opp.c | 13 +- .../vpelib/src/chip/vpe10/vpe10_resource.c | 1 - src/amd/vpelib/src/core/color.c | 437 +++++++++++------- src/amd/vpelib/src/core/color_bg.c | 69 ++- src/amd/vpelib/src/core/color_gamma.c | 240 ++++++---- src/amd/vpelib/src/core/inc/color.h | 32 +- src/amd/vpelib/src/core/inc/color_bg.h | 5 +- src/amd/vpelib/src/core/inc/color_gamma.h | 7 +- src/amd/vpelib/src/core/inc/hw_shared.h | 5 - src/amd/vpelib/src/core/inc/mpc.h | 2 +- src/amd/vpelib/src/core/vpelib.c | 30 +- 14 files changed, 597 insertions(+), 358 deletions(-) diff --git a/src/amd/vpelib/src/chip/vpe10/inc/vpe10_mpc.h b/src/amd/vpelib/src/chip/vpe10/inc/vpe10_mpc.h index 3a4416c6c5c..c523e50eac2 100644 --- a/src/amd/vpelib/src/chip/vpe10/inc/vpe10_mpc.h +++ b/src/amd/vpelib/src/chip/vpe10/inc/vpe10_mpc.h @@ -1433,7 +1433,7 @@ bool vpe10_mpc_program_3dlut_indirect(struct mpc *mpc, bool use_tetrahedral_9, bool use_12bits); // Blend-gamma control. -void vpe10_mpc_program_1dlut(struct mpc *mpc, const struct pwl_params *params); +void vpe10_mpc_program_1dlut(struct mpc *mpc, const struct pwl_params *params, enum cm_type gamma_type); void vpe10_mpc_program_cm_location(struct mpc *mpc, uint8_t location); diff --git a/src/amd/vpelib/src/chip/vpe10/vpe10_cm_common.c b/src/amd/vpelib/src/chip/vpe10/vpe10_cm_common.c index f892d2152de..109de042cc8 100644 --- a/src/amd/vpelib/src/chip/vpe10/vpe10_cm_common.c +++ b/src/amd/vpelib/src/chip/vpe10/vpe10_cm_common.c @@ -62,6 +62,22 @@ static bool cm_helper_convert_to_custom_float(struct pwl_result_data *rgb_result return false; } + if (!vpe_convert_to_custom_float_format( + corner_points[0].red.y, &fmt, &corner_points[0].red.custom_float_y)) { + VPE_ASSERT(0); + return false; + } + if (!vpe_convert_to_custom_float_format( + corner_points[0].green.y, &fmt, &corner_points[0].green.custom_float_y)) { + VPE_ASSERT(0); + return false; + } + if (!vpe_convert_to_custom_float_format( + corner_points[0].blue.y, &fmt, &corner_points[0].blue.custom_float_y)) { + VPE_ASSERT(0); + return false; + } + if (!vpe_convert_to_custom_float_format( corner_points[0].red.offset, &fmt, &corner_points[0].red.custom_float_offset)) { VPE_ASSERT(0); @@ -369,6 +385,17 @@ bool vpe10_cm_helper_translate_curve_to_hw_format( ++rgb; ++i; } + + corner_points[0].red.y = output_tf->start_base; + corner_points[0].green.y = output_tf->start_base; + corner_points[0].blue.y = output_tf->start_base; + + if (output_tf->start_base.value != vpe_fixpt_zero.value) { + corner_points[0].red.slope = vpe_fixpt_zero; + corner_points[0].green.slope = vpe_fixpt_zero; + corner_points[0].blue.slope = vpe_fixpt_zero; + } + cm_helper_convert_to_custom_float(rgb_resulted, lut_params->corner_points, hw_points, fixpoint); return true; @@ -480,6 +507,16 @@ bool vpe10_cm_helper_translate_curve_to_degamma_hw_format( ++i; } + corner_points[0].red.y = output_tf->start_base; + corner_points[0].green.y = output_tf->start_base; + corner_points[0].blue.y = output_tf->start_base; + + if (output_tf->start_base.value != vpe_fixpt_zero.value) { + corner_points[0].red.slope = vpe_fixpt_zero; + corner_points[0].green.slope = vpe_fixpt_zero; + corner_points[0].blue.slope = vpe_fixpt_zero; + } + cm_helper_convert_to_custom_float(rgb_resulted, lut_params->corner_points, hw_points, false); return true; @@ -561,6 +598,13 @@ void vpe10_cm_helper_program_gamcor_xfer_func(struct config_writer *config_write REG_SET_2_CM(reg->start_cntl_r, 0, exp_region_start, params->corner_points[0].red.custom_float_x, exp_region_start_segment, 0); + REG_SET_CM(reg->start_base_cntl_r, 0, field_region_start_base, + params->corner_points[0].red.custom_float_y); + REG_SET_CM(reg->start_base_cntl_g, 0, field_region_start_base, + params->corner_points[0].green.custom_float_y); + REG_SET_CM(reg->start_base_cntl_b, 0, field_region_start_base, + params->corner_points[0].blue.custom_float_y); + REG_SET_CM(reg->start_slope_cntl_b, 0, // linear slope at start of curve field_region_linear_slope, params->corner_points[0].blue.custom_float_slope); REG_SET_CM(reg->start_slope_cntl_g, 0, field_region_linear_slope, diff --git a/src/amd/vpelib/src/chip/vpe10/vpe10_mpc.c b/src/amd/vpelib/src/chip/vpe10/vpe10_mpc.c index a103027e06e..894a397ed2d 100644 --- a/src/amd/vpelib/src/chip/vpe10/vpe10_mpc.c +++ b/src/amd/vpelib/src/chip/vpe10/vpe10_mpc.c @@ -275,16 +275,17 @@ void vpe10_program_output_csc(struct mpc *mpc, enum vpe_surface_pixel_format pix enum mpc_output_csc_mode ocsc_mode = MPC_OUTPUT_CSC_COEF_A; - if (mpc->funcs->power_on_ogam_lut) - mpc->funcs->power_on_ogam_lut(mpc, true); + if (mpc->funcs->power_on_ogam_lut) + mpc->funcs->power_on_ogam_lut(mpc, true); - if (matrix != NULL) { - if (mpc->funcs->set_output_csc != NULL) - mpc->funcs->set_output_csc(mpc, matrix, ocsc_mode); - } else { - if (mpc->funcs->set_ocsc_default != NULL) - mpc->funcs->set_ocsc_default(mpc, pixel_format, colorspace, ocsc_mode); - } + if (matrix != NULL) { + if (mpc->funcs->set_output_csc != NULL) + mpc->funcs->set_output_csc(mpc, matrix, ocsc_mode); + } + else { + if (mpc->funcs->set_ocsc_default != NULL) + mpc->funcs->set_ocsc_default(mpc, pixel_format, colorspace, ocsc_mode); + } } enum vpmpcc_ogam_mode { @@ -1091,13 +1092,23 @@ static void vpe10_mpc_program_1dlut_luta_settings(struct mpc *mpc, const struct } static void vpe10_mpc_program_1dlut_pwl( - struct mpc *mpc, const struct pwl_result_data *rgb, uint32_t num) + struct mpc *mpc, const struct pwl_result_data *rgb, uint32_t num, enum cm_type gamma_type) { PROGRAM_ENTRY(); - uint32_t last_base_value_red = rgb[num].red_reg; - uint32_t last_base_value_green = rgb[num].blue_reg; - uint32_t last_base_value_blue = rgb[num].green_reg; + uint32_t last_base_value_red; + uint32_t last_base_value_green; + uint32_t last_base_value_blue; + + if (gamma_type == CM_DEGAM) { + last_base_value_red = rgb[num].red_reg; + last_base_value_green = rgb[num].blue_reg; + last_base_value_blue = rgb[num].green_reg; + } else { + last_base_value_red = rgb[num - 1].red_reg + rgb[num - 1].delta_red_reg; + last_base_value_green = rgb[num - 1].green_reg + rgb[num - 1].delta_green_reg; + last_base_value_blue = rgb[num - 1].blue_reg + rgb[num - 1].delta_blue_reg; + } if (vpe_is_rgb_equal(rgb, num)) { vpe10_cm_helper_program_pwl(config_writer, rgb, last_base_value_red, num, @@ -1128,7 +1139,7 @@ static void vpe10_mpc_program_1dlut_pwl( } // Blend-gamma control. -void vpe10_mpc_program_1dlut(struct mpc *mpc, const struct pwl_params *params) +void vpe10_mpc_program_1dlut(struct mpc *mpc, const struct pwl_params *params, enum cm_type gamma_type) { PROGRAM_ENTRY(); @@ -1138,6 +1149,7 @@ void vpe10_mpc_program_1dlut(struct mpc *mpc, const struct pwl_params *params) if (vpe_priv->init.debug.enable_mem_low_power.bits.mpc) vpe10_mpc_power_on_1dlut_shaper_3dlut(mpc, false); + return; } @@ -1145,7 +1157,7 @@ void vpe10_mpc_program_1dlut(struct mpc *mpc, const struct pwl_params *params) vpe10_mpc_configure_1dlut(mpc, true); vpe10_mpc_program_1dlut_luta_settings(mpc, params); - vpe10_mpc_program_1dlut_pwl(mpc, params->rgb_resulted, params->hw_points_num); + vpe10_mpc_program_1dlut_pwl(mpc, params->rgb_resulted, params->hw_points_num, gamma_type); REG_SET( VPMPCC_MCM_1DLUT_CONTROL, REG_DEFAULT(VPMPCC_MCM_1DLUT_CONTROL), VPMPCC_MCM_1DLUT_MODE, 2); @@ -1286,17 +1298,31 @@ void vpe10_mpc_set_output_transfer_func(struct mpc *mpc, struct output_ctx *outp void vpe10_mpc_set_blend_lut(struct mpc *mpc, const struct transfer_func *blend_tf) { struct pwl_params *blend_lut = NULL; + enum cm_type gamma_type = CM_DEGAM; + + if (blend_tf && blend_tf->type == TF_TYPE_DISTRIBUTED_POINTS) { + + gamma_type = blend_tf->cm_gamma_type; + + if (blend_tf->use_pre_calculated_table) { + vpe10_cm_get_tf_pwl_params(blend_tf, &blend_lut, gamma_type); + + VPE_ASSERT(blend_lut != NULL); + if (blend_lut == NULL) + return; + } else { + if (gamma_type == CM_DEGAM) + vpe10_cm_helper_translate_curve_to_degamma_hw_format( + blend_tf, &mpc->blender_params); // TODO should init regamma_params first + else + vpe10_cm_helper_translate_curve_to_hw_format( + blend_tf, &mpc->blender_params, false); - if (blend_tf) { - if (blend_tf->type == TF_TYPE_DISTRIBUTED_POINTS) { - vpe10_cm_helper_translate_curve_to_degamma_hw_format( - blend_tf, &mpc->blender_params); // TODO should init regamma_params first blend_lut = &mpc->blender_params; } } - mpc->funcs->program_1dlut(mpc, blend_lut); - return; + mpc->funcs->program_1dlut(mpc, blend_lut, gamma_type); } bool vpe10_mpc_program_movable_cm(struct mpc *mpc, const struct transfer_func *func_shaper, diff --git a/src/amd/vpelib/src/chip/vpe10/vpe10_opp.c b/src/amd/vpelib/src/chip/vpe10/vpe10_opp.c index 71a68604f53..d8f92c0e5f2 100644 --- a/src/amd/vpelib/src/chip/vpe10/vpe10_opp.c +++ b/src/amd/vpelib/src/chip/vpe10/vpe10_opp.c @@ -56,22 +56,13 @@ void vpe10_opp_set_clamping( { PROGRAM_ENTRY(); + //OCSC operations are handled in output gamma sequence to allow + // full range bg color fill. Hence, no clamping should be done on the output. switch (params->clamping_level) { case CLAMPING_LIMITED_RANGE_8BPC: case CLAMPING_LIMITED_RANGE_10BPC: case CLAMPING_LIMITED_RANGE_12BPC: - REG_SET_2(VPFMT_CLAMP_CNTL, 0, VPFMT_CLAMP_DATA_EN, 1, VPFMT_CLAMP_COLOR_FORMAT, - params->clamping_level); - break; case CLAMPING_LIMITED_RANGE_PROGRAMMABLE: - REG_SET_2(VPFMT_CLAMP_CNTL, 0, VPFMT_CLAMP_DATA_EN, 1, VPFMT_CLAMP_COLOR_FORMAT, 7); - REG_SET_2(VPFMT_CLAMP_COMPONENT_R, 0, VPFMT_CLAMP_LOWER_R, params->r_clamp_component_lower, - VPFMT_CLAMP_UPPER_R, params->r_clamp_component_upper); - REG_SET_2(VPFMT_CLAMP_COMPONENT_G, 0, VPFMT_CLAMP_LOWER_G, params->g_clamp_component_lower, - VPFMT_CLAMP_UPPER_G, params->g_clamp_component_upper); - REG_SET_2(VPFMT_CLAMP_COMPONENT_B, 0, VPFMT_CLAMP_LOWER_B, params->b_clamp_component_lower, - VPFMT_CLAMP_UPPER_B, params->b_clamp_component_upper); - break; case CLAMPING_FULL_RANGE: default: REG_SET_2(VPFMT_CLAMP_CNTL, 0, VPFMT_CLAMP_DATA_EN, 0, VPFMT_CLAMP_COLOR_FORMAT, 0); diff --git a/src/amd/vpelib/src/chip/vpe10/vpe10_resource.c b/src/amd/vpelib/src/chip/vpe10/vpe10_resource.c index 368b41c454a..98a414f4e20 100644 --- a/src/amd/vpelib/src/chip/vpe10/vpe10_resource.c +++ b/src/amd/vpelib/src/chip/vpe10/vpe10_resource.c @@ -830,7 +830,6 @@ int32_t vpe10_program_backend( cdc->funcs->program_global_sync(cdc, VPE10_CDC_VUPDATE_OFFSET_DEFAULT, VPE10_CDC_VUPDATE_WIDTH_DEFAULT, VPE10_CDC_VREADY_OFFSET_DEFAULT); - mpc->funcs->program_output_csc(mpc, surface_info->format, output_ctx->cs, NULL); mpc->funcs->set_output_transfer_func(mpc, output_ctx); // program shaper, 3dlut and 1dlut in MPC for after blend // Note: cannot program both before and after blend CM diff --git a/src/amd/vpelib/src/core/color.c b/src/amd/vpelib/src/core/color.c index bd1ad3181b4..fdf95ccae10 100644 --- a/src/amd/vpelib/src/core/color.c +++ b/src/amd/vpelib/src/core/color.c @@ -43,15 +43,15 @@ static void color_check_input_cm_update(struct vpe_priv *vpe_priv, struct stream static void color_check_output_cm_update( struct vpe_priv *vpe_priv, const struct vpe_color_space *vcs); -static bool color_check_bypass_cm(struct vpe_priv *vpe_priv, const struct vpe_build_param *param); +static bool color_update_regamma_tf(struct vpe_priv *vpe_priv, + enum color_transfer_func output_transfer_function, struct fixed31_32 x_scale, + struct fixed31_32 y_scale, struct fixed31_32 y_bias, bool can_bypass, + struct transfer_func *output_tf); -static bool color_update_output_tf(struct vpe_priv *vpe_priv, - enum color_transfer_func output_transfer_function, struct transfer_func *output_tf, - bool can_bypass); - -static bool color_update_input_tf(struct vpe_priv *vpe_priv, - enum color_transfer_func input_transfer_function, struct transfer_func *input_tf, - bool can_bypass, bool force_tf_calculation); +static bool color_update_degamma_tf(struct vpe_priv *vpe_priv, + enum color_transfer_func color_input_tf, struct fixed31_32 x_scale, + struct fixed31_32 y_scale, struct fixed31_32 y_bias, bool can_bypass, + struct transfer_func *input_tf); static bool color_update_input_cs(struct vpe_priv *vpe_priv, enum color_space in_cs, const struct vpe_color_adjust *adjustments, struct vpe_csc_matrix *input_cs, @@ -136,19 +136,22 @@ static void color_check_input_cm_update(struct vpe_priv *vpe_priv, struct stream stream_ctx->enable_3dlut = enable_3dlut; } -static bool color_update_output_tf(struct vpe_priv *vpe_priv, - enum color_transfer_func output_transfer_function, struct transfer_func *output_tf, - bool can_bypass) +static bool color_update_regamma_tf(struct vpe_priv *vpe_priv, + enum color_transfer_func output_transfer_function, struct fixed31_32 x_scale, + struct fixed31_32 y_scale, struct fixed31_32 y_bias, bool can_bypass, + struct transfer_func* output_tf) { struct pwl_params *params = NULL; - output_tf->sdr_ref_white_level = 80; - + if (can_bypass) { output_tf->type = TF_TYPE_BYPASS; return true; } + output_tf->sdr_ref_white_level = 80; + output_tf->cm_gamma_type = CM_REGAM; output_tf->type = TF_TYPE_DISTRIBUTED_POINTS; + output_tf->start_base = y_bias; switch (output_transfer_function) { case TRANSFER_FUNC_SRGB: @@ -156,6 +159,7 @@ static bool color_update_output_tf(struct vpe_priv *vpe_priv, case TRANSFER_FUNC_BT1886: case TRANSFER_FUNC_PQ2084: case TRANSFER_FUNC_LINEAR_0_125: + case TRANSFER_FUNC_LINEAR_0_1: output_tf->tf = output_transfer_function; break; default: @@ -163,7 +167,10 @@ static bool color_update_output_tf(struct vpe_priv *vpe_priv, break; } - if (!vpe_priv->init.debug.force_tf_calculation) + if (!vpe_priv->init.debug.force_tf_calculation && + x_scale.value == vpe_fixpt_one.value && + y_scale.value == vpe_fixpt_one.value && + y_bias.value == vpe_fixpt_zero.value) vpe_priv->resource.get_tf_pwl_params(output_tf, ¶ms, CM_REGAM); if (params) @@ -172,14 +179,20 @@ static bool color_update_output_tf(struct vpe_priv *vpe_priv, output_tf->use_pre_calculated_table = false; if (!output_tf->use_pre_calculated_table) - vpe_color_calculate_regamma_params(vpe_priv, output_tf, &vpe_priv->cal_buffer); + vpe_color_calculate_regamma_params(vpe_priv, + x_scale, + y_scale, + y_bias, + &vpe_priv->cal_buffer, + output_tf); return true; } -static bool color_update_input_tf(struct vpe_priv *vpe_priv, - const enum color_transfer_func color_input_tf, struct transfer_func *input_tf, bool can_bypass, - bool force_tf_calculation) +static bool color_update_degamma_tf(struct vpe_priv *vpe_priv, + enum color_transfer_func color_input_tf, struct fixed31_32 x_scale, + struct fixed31_32 y_scale, struct fixed31_32 y_bias, bool can_bypass, + struct transfer_func *input_tf) { bool ret = true; struct pwl_params *params = NULL; @@ -189,15 +202,18 @@ static bool color_update_input_tf(struct vpe_priv *vpe_priv, return true; } + input_tf->cm_gamma_type = CM_DEGAM; input_tf->type = TF_TYPE_DISTRIBUTED_POINTS; + input_tf->start_base = y_bias; switch (color_input_tf) { case TRANSFER_FUNC_SRGB: case TRANSFER_FUNC_BT709: case TRANSFER_FUNC_BT1886: case TRANSFER_FUNC_PQ2084: - case TRANSFER_FUNC_LINEAR_0_125: case TRANSFER_FUNC_NORMALIZED_PQ: + case TRANSFER_FUNC_LINEAR_0_1: + case TRANSFER_FUNC_LINEAR_0_125: input_tf->tf = color_input_tf; break; default: @@ -205,7 +221,10 @@ static bool color_update_input_tf(struct vpe_priv *vpe_priv, break; } - if (!vpe_priv->init.debug.force_tf_calculation) + if (!vpe_priv->init.debug.force_tf_calculation && + x_scale.value == vpe_fixpt_one.value && + y_scale.value == vpe_fixpt_one.value && + y_bias.value == vpe_fixpt_zero.value) vpe_priv->resource.get_tf_pwl_params(input_tf, ¶ms, CM_DEGAM); if (params) @@ -213,31 +232,80 @@ static bool color_update_input_tf(struct vpe_priv *vpe_priv, else input_tf->use_pre_calculated_table = false; - if ((!input_tf->use_pre_calculated_table) || (force_tf_calculation)) { - input_tf->use_pre_calculated_table = false; - vpe_color_calculate_degamma_params(vpe_priv, vpe_priv->stream_ctx->tf_scaling_factor, - vpe_fixpt_from_int(1), + if (!input_tf->use_pre_calculated_table) { + vpe_color_calculate_degamma_params(vpe_priv, + x_scale, + y_scale, + y_bias, input_tf); } return ret; } -// return true if bypass can be done -static bool color_check_bypass_cm(struct vpe_priv *vpe_priv, const struct vpe_build_param *param) -{ - uint32_t i; - struct stream_ctx *stream_ctx; +static enum vpe_status vpe_allocate_cm_memory(struct vpe_priv *vpe_priv, const struct vpe_build_param *param) { - // TODO: revisit the TM case - for (i = 0; i < param->num_streams; i++) { - stream_ctx = &vpe_priv->stream_ctx[i]; - if (stream_ctx->cs != vpe_priv->output_ctx.cs || - stream_ctx->tf != vpe_priv->output_ctx.tf) { - return false; + struct stream_ctx *stream_ctx; + struct output_ctx *output_ctx; + enum vpe_status status = VPE_STATUS_OK; + + for (uint32_t stream_idx = 0; stream_idx < param->num_streams; stream_idx++) { + stream_ctx = &vpe_priv->stream_ctx[stream_idx]; + + if (!stream_ctx->input_cs) { + stream_ctx->input_cs = + (struct vpe_csc_matrix *)vpe_zalloc(sizeof(struct vpe_csc_matrix)); + if (!stream_ctx->input_cs) { + vpe_log("err: out of memory for input cs!"); + return VPE_STATUS_NO_MEMORY; + } + } + + if (!stream_ctx->input_tf) { + stream_ctx->input_tf = + (struct transfer_func *)vpe_zalloc(sizeof(struct transfer_func)); + if (!stream_ctx->input_tf) { + vpe_log("err: out of memory for input tf!"); + return VPE_STATUS_NO_MEMORY; + } + } + + if (!stream_ctx->bias_scale) { + stream_ctx->bias_scale = + (struct bias_and_scale *)vpe_zalloc(sizeof(struct bias_and_scale)); + if (!stream_ctx->bias_scale) { + vpe_log("err: out of memory for bias and scale!"); + return VPE_STATUS_NO_MEMORY; + } + } + + if (!stream_ctx->gamut_remap) { + stream_ctx->gamut_remap = vpe_zalloc(sizeof(struct colorspace_transform)); + if (!stream_ctx->gamut_remap) { + vpe_log("err: out of memory for gamut_remap!"); + return VPE_STATUS_NO_MEMORY; + } + } + if (!stream_ctx->blend_tf) { + stream_ctx->blend_tf = vpe_zalloc(sizeof(struct transfer_func)); + if (!stream_ctx->blend_tf) { + vpe_log("err: out of memory for blend tf!"); + return VPE_STATUS_NO_MEMORY; + } } } - return true; + + output_ctx = &vpe_priv->output_ctx; + if (!output_ctx->output_tf) { + output_ctx->output_tf = + (struct transfer_func *)vpe_zalloc(sizeof(struct transfer_func)); + if (!output_ctx->output_tf) { + vpe_log("err: out of memory for output tf!"); + return VPE_STATUS_NO_MEMORY; + } + } + + return VPE_STATUS_OK; } static enum color_space color_get_icsc_cs(enum color_space ics) @@ -302,6 +370,126 @@ static bool color_update_input_cs(struct vpe_priv *vpe_priv, enum color_space in return true; } +/* This function generates software points for the blnd gam and ogam programming blocks. + The logic for the blndgam/ogam programming sequence is a function of: + 1. Output Range (Studio Full) + 2. 3DLUT usage + 3. Output format (HDR SDR) + + SDR Out or studio range out + TM Case + BLNDGAM : NL -> NL*S + B + OGAM : Bypass + Non TM Case + BLNDGAM : L -> NL*S + B + OGAM : Bypass + Full range HDR Out + TM Case + BLNDGAM : NL -> L + OGAM : L -> NL + Non TM Case + BLNDGAM : Bypass + OGAM : L -> NL + +*/ + +static bool vpe_update_output_gamma_sequence(struct vpe_priv *vpe_priv, + const struct vpe_build_param *param) +{ + + struct stream_ctx *stream_ctx; + struct output_ctx *output_ctx; + struct vpe_color_space tm_out_cs; + struct fixed31_32 x_scale; + struct fixed31_32 y_scale; + struct fixed31_32 y_bias; + enum vpe_status status; + bool is_studio; + bool can_bypass = false; + bool lut3d_enabled = false; + + + status = VPE_STATUS_OK; + x_scale = vpe_fixpt_one; + y_bias = vpe_fixpt_zero; + y_scale = vpe_fixpt_one; + is_studio = (param->dst_surface.cs.range == VPE_COLOR_RANGE_STUDIO); + + if (is_studio) { + + if (vpe_is_rgb8(param->dst_surface.format)) { + y_scale = STUDIO_RANGE_SCALE_8_BIT; + y_bias = STUDIO_RANGE_FOOT_ROOM_8_BIT; + } else { + y_scale = STUDIO_RANGE_SCALE_10_BIT; + y_bias = STUDIO_RANGE_FOOT_ROOM_10_BIT; + } + } + + output_ctx = &vpe_priv->output_ctx; + + for (uint32_t stream_idx = 0; stream_idx < param->num_streams; stream_idx++) { + + enum color_space cs; + enum color_transfer_func tf = TRANSFER_FUNC_LINEAR_0_1; + + stream_ctx = &vpe_priv->stream_ctx[stream_idx]; + lut3d_enabled = stream_ctx->enable_3dlut; + + //If SDR out -> Blend should be NL + //If studio out -> No choice but to blend in NL + if (!vpe_is_HDR(output_ctx->tf) || is_studio) { + if (lut3d_enabled) { + tf = TRANSFER_FUNC_LINEAR_0_1; + } + else { + tf = output_ctx->tf; + } + + color_update_regamma_tf(vpe_priv, + tf, + x_scale, + y_scale, + y_bias, + can_bypass, + stream_ctx->blend_tf); + } + else { + + if (lut3d_enabled) { + vpe_color_build_tm_cs(&stream_ctx->stream.tm_params, param->dst_surface, &tm_out_cs); + vpe_color_get_color_space_and_tf(&tm_out_cs, &cs, &tf); + } + else { + can_bypass = true; + } + + color_update_degamma_tf(vpe_priv, + tf, + x_scale, + y_scale, + y_bias, + can_bypass, + stream_ctx->blend_tf); + } + } + + if (vpe_is_HDR(output_ctx->tf) && !is_studio) + can_bypass = false; //Blending is done in linear light so ogam needs to handle the regam + else + can_bypass = true; + + color_update_regamma_tf(vpe_priv, + output_ctx->tf, + vpe_fixpt_one, + vpe_fixpt_one, + vpe_fixpt_zero, + can_bypass, + output_ctx->output_tf); + + return status; +} + bool vpe_use_csc_adjust(const struct vpe_color_adjust *adjustments) { float epsilon = 0.001f; // steps are 1.0f or 0.01f, so should be plenty @@ -444,110 +632,68 @@ enum vpe_status vpe_color_update_color_space_and_tf( { uint32_t stream_idx; struct stream_ctx *stream_ctx; - struct output_ctx *output_ctx; - enum vpe_status status = VPE_STATUS_OK; + struct fixed31_32 new_matrix_scaling_factor; + struct output_ctx *output_ctx = &vpe_priv->output_ctx; + enum vpe_status status = VPE_STATUS_OK; - color_check_output_cm_update(vpe_priv, ¶m->dst_surface.cs); + status = vpe_allocate_cm_memory(vpe_priv, param); + if (status == VPE_STATUS_OK) { - for (stream_idx = 0; stream_idx < param->num_streams; stream_idx++) { - stream_ctx = &vpe_priv->stream_ctx[stream_idx]; + color_check_output_cm_update(vpe_priv, ¶m->dst_surface.cs); - color_check_input_cm_update(vpe_priv, stream_ctx, - ¶m->streams[stream_idx].surface_info.cs, ¶m->streams[stream_idx].color_adj, - param->streams[stream_idx].tm_params.enable_3dlut); - } + for (stream_idx = 0; stream_idx < param->num_streams; stream_idx++) { - output_ctx = &vpe_priv->output_ctx; - if (output_ctx->dirty_bits.transfer_function) { - if (!output_ctx->output_tf) { - output_ctx->output_tf = - (struct transfer_func *)vpe_zalloc(sizeof(struct transfer_func)); - if (!output_ctx->output_tf) { - vpe_log("err: out of memory for output tf!"); - return VPE_STATUS_NO_MEMORY; - } - } + new_matrix_scaling_factor = vpe_fixpt_one; + stream_ctx = &vpe_priv->stream_ctx[stream_idx]; - color_update_output_tf(vpe_priv, output_ctx->tf, output_ctx->output_tf, - false); // No bypass, always do regam/degam - } - - for (stream_idx = 0; stream_idx < param->num_streams; stream_idx++) { - stream_ctx = &vpe_priv->stream_ctx[stream_idx]; - struct fixed31_32 new_matrix_scaling_factor = vpe_fixpt_one; - - if (stream_ctx->dirty_bits.color_space) { - if (!stream_ctx->input_cs) { - stream_ctx->input_cs = - (struct vpe_csc_matrix *)vpe_zalloc(sizeof(struct vpe_csc_matrix)); - if (!stream_ctx->input_cs) { - vpe_log("err: out of memory for input cs!"); - return VPE_STATUS_NO_MEMORY; - } - } - - if (!color_update_input_cs(vpe_priv, stream_ctx->cs, - ¶m->streams[stream_idx].color_adj, stream_ctx->input_cs, - &stream_ctx->color_adjustments, &new_matrix_scaling_factor)) { - vpe_log("err: input cs not being programmed!"); - } else { - if ((vpe_priv->scale_yuv_matrix) && // the option to scale the matrix yuv to rgb is - // on - (new_matrix_scaling_factor.value != - vpe_priv->stream_ctx->tf_scaling_factor.value)) { - vpe_priv->stream_ctx->tf_scaling_factor = new_matrix_scaling_factor; - stream_ctx->dirty_bits.transfer_function = 1; // force tf recalculation - } - } - } - - if (stream_ctx->dirty_bits.transfer_function) { - if (!stream_ctx->input_tf) { - stream_ctx->input_tf = - (struct transfer_func *)vpe_zalloc(sizeof(struct transfer_func)); - if (!stream_ctx->input_tf) { - vpe_log("err: out of memory for input tf!"); - return VPE_STATUS_NO_MEMORY; - } - } - - color_update_input_tf(vpe_priv, stream_ctx->tf, stream_ctx->input_tf, - stream_ctx->stream.tm_params.enable_3dlut, // By Pass regamma if 3DLUT is enabled - false); - } - - if (!stream_ctx->bias_scale) { - stream_ctx->bias_scale = - (struct bias_and_scale *)vpe_zalloc(sizeof(struct bias_and_scale)); - if (!stream_ctx->bias_scale) { - vpe_log("err: out of memory for bias and scale!"); - return VPE_STATUS_NO_MEMORY; - } + color_check_input_cm_update(vpe_priv, stream_ctx, + ¶m->streams[stream_idx].surface_info.cs, ¶m->streams[stream_idx].color_adj, + param->streams[stream_idx].tm_params.enable_3dlut); build_scale_and_bias(stream_ctx->bias_scale, ¶m->streams[stream_idx].surface_info.cs, param->streams[stream_idx].surface_info.format); - } - if (stream_ctx->dirty_bits.color_space || output_ctx->dirty_bits.color_space) { - if (!stream_ctx->gamut_remap) { - stream_ctx->gamut_remap = vpe_zalloc(sizeof(struct colorspace_transform)); - if (!stream_ctx->gamut_remap) { - vpe_log("err: out of memory for gamut_remap!"); - return VPE_STATUS_NO_MEMORY; + if (stream_ctx->dirty_bits.color_space) { + if (!color_update_input_cs(vpe_priv, stream_ctx->cs, + ¶m->streams[stream_idx].color_adj, stream_ctx->input_cs, + &stream_ctx->color_adjustments, &new_matrix_scaling_factor)) { + vpe_log("err: input cs not being programmed!"); + } + else { + if ((vpe_priv->scale_yuv_matrix) && // the option to scale the matrix yuv to rgb is + // on + (new_matrix_scaling_factor.value != + vpe_priv->stream_ctx->tf_scaling_factor.value)) { + vpe_priv->stream_ctx->tf_scaling_factor = new_matrix_scaling_factor; + stream_ctx->dirty_bits.transfer_function = 1; // force tf recalculation + } } } - status = vpe_color_update_gamut(vpe_priv, stream_ctx->cs, output_ctx->cs, - stream_ctx->gamut_remap, stream_ctx->stream.tm_params.enable_3dlut); + + if (stream_ctx->dirty_bits.transfer_function) { + color_update_degamma_tf(vpe_priv, stream_ctx->tf, + vpe_priv->stream_ctx->tf_scaling_factor, + vpe_fixpt_one, + vpe_fixpt_zero, + stream_ctx->stream.tm_params.enable_3dlut, // By Pass degamma if 3DLUT is enabled + stream_ctx->input_tf); + } + + if (stream_ctx->dirty_bits.color_space || output_ctx->dirty_bits.color_space) { + status = vpe_color_update_gamut(vpe_priv, stream_ctx->cs, output_ctx->cs, + stream_ctx->gamut_remap, stream_ctx->stream.tm_params.enable_3dlut); + } } - } - if (status != VPE_STATUS_OK) { - vpe_log("failed in updating gamut %d\n", (int)status); - return status; - } + if (status == VPE_STATUS_OK) { + if (output_ctx->dirty_bits.transfer_function || + output_ctx->dirty_bits.color_space) + vpe_update_output_gamma_sequence(vpe_priv, param); + } - return VPE_STATUS_OK; + } + return status; } enum vpe_status vpe_color_tm_update_hdr_mult(uint16_t shaper_in_exp_max, uint32_t peak_white, @@ -583,44 +729,10 @@ enum vpe_status vpe_color_update_shaper( shaper_in.use_const_hdr_mult = false; // can't be true. Fix is required. shaper_func->type = TF_TYPE_HWPWL; - shaper_func->tf = TRANSFER_FUNC_LINEAR_0_125; + shaper_func->tf = TRANSFER_FUNC_LINEAR_0_1; return vpe_build_shaper(&shaper_in, &shaper_func->pwl); } -enum vpe_status vpe_color_update_blnd_gam(struct vpe_priv *vpe_priv, - const struct vpe_build_param *param, const struct vpe_tonemap_params *tm_params, - struct transfer_func *blnd_tf_func, bool enable_3dlut) -{ - - if (!enable_3dlut) { - blnd_tf_func->type = TF_TYPE_BYPASS; - return VPE_STATUS_OK; - } - - enum vpe_status ret = VPE_STATUS_OK; - struct vpe_color_space tm_out_cs; - enum color_space cs; - enum color_transfer_func tf; - struct fixed31_32 tf_norm_gain; - - vpe_color_build_tm_cs(tm_params, param->dst_surface, &tm_out_cs); - vpe_color_get_color_space_and_tf(&tm_out_cs, &cs, &tf); - - if (tf == TRANSFER_FUNC_NORMALIZED_PQ) { - uint32_t outLuminance = vpe_priv->output_ctx.hdr_metadata.max_mastering; - vpe_compute_pq(vpe_fixpt_from_fraction((long long)outLuminance, 10000), &tf_norm_gain); - } else { - tf_norm_gain = vpe_fixpt_from_int(1); - } - - blnd_tf_func->type = TF_TYPE_DISTRIBUTED_POINTS; - blnd_tf_func->tf = tf; - blnd_tf_func->use_pre_calculated_table = false; - - vpe_color_calculate_degamma_params(vpe_priv, tf_norm_gain, vpe_fixpt_from_int(1), blnd_tf_func); - return VPE_STATUS_OK; -} - enum vpe_status vpe_color_update_movable_cm( struct vpe_priv *vpe_priv, const struct vpe_build_param *param) { @@ -679,6 +791,8 @@ enum vpe_status vpe_color_update_movable_cm( } } + //Blendgam is updated by output vpe_update_output_gamma_sequence + if (param->streams[stream_idx].tm_params.shaper_tf == VPE_TF_PQ_NORMALIZED) pqNormFactor = stream_ctx->stream.hdr_metadata.max_mastering; else @@ -689,9 +803,6 @@ enum vpe_status vpe_color_update_movable_cm( vpe_color_update_shaper(SHAPER_EXP_MAX_IN, stream_ctx->in_shaper_func, enable_3dlut); - vpe_color_update_blnd_gam( - vpe_priv, param, &stream_ctx->stream.tm_params, stream_ctx->blend_tf, enable_3dlut); - vpe_color_build_tm_cs(&stream_ctx->stream.tm_params, param->dst_surface, &tm_out_cs); vpe_color_get_color_space_and_tf(&tm_out_cs, &out_lut_cs, &tf); diff --git a/src/amd/vpelib/src/core/color_bg.c b/src/amd/vpelib/src/core/color_bg.c index ce919dd8b5c..7a2dafc664a 100644 --- a/src/amd/vpelib/src/core/color_bg.c +++ b/src/amd/vpelib/src/core/color_bg.c @@ -1,6 +1,7 @@ #include #include #include "color_bg.h" +#include "vpe_priv.h" struct csc_vector { float x; @@ -134,6 +135,35 @@ static bool bg_csc(struct vpe_color *bg_color, enum color_space cs) return output_is_clipped; } +static inline bool is_global_bg_blend_applied(struct stream_ctx *stream_ctx) { + + return (stream_ctx->stream.blend_info.blending) && + (stream_ctx->stream.blend_info.global_alpha) && + (stream_ctx->stream.blend_info.global_alpha_value != 1.0); +} + +/* + In order to support background color fill correctly, we need to do studio -> full range conversion + before the blend block. However, there is also a requirement for HDR output to be blended in linear space. + Hence, if we have PQ out and studio range, we need to make sure no blenidng will occur. Othewise the job + is invalid. + +*/ +static enum vpe_status is_valid_blend(const struct vpe_priv *vpe_priv, struct vpe_color *bg_color) { + + enum vpe_status status = VPE_STATUS_OK; + const struct vpe_color_space *vcs = &vpe_priv->output_ctx.surface.cs; + struct stream_ctx *stream_ctx = vpe_priv->stream_ctx; //Only need to check the first stream. + + if ((vcs->range == VPE_COLOR_RANGE_STUDIO) && + (vcs->tf == VPE_TF_PQ) && + ((stream_ctx->stream.surface_info.cs.encoding == VPE_PIXEL_ENCODING_RGB) || + is_global_bg_blend_applied(stream_ctx))) + status = VPE_STATUS_BG_COLOR_OUT_OF_RANGE; + + return status; +} + struct gamma_coefs { float a0; float a1; @@ -257,6 +287,8 @@ static bool is_rgb_limited(enum color_space cs) return (cs == COLOR_SPACE_SRGB_LIMITED || cs == COLOR_SPACE_2020_RGB_LIMITEDRANGE); } +// To understand the logic for background color conversion, +// please refer to vpe_update_output_gamma_sequence in color.c void vpe_bg_color_convert( enum color_space output_cs, struct transfer_func *output_tf, struct vpe_color *bg_color) { @@ -305,7 +337,7 @@ void vpe_bg_color_convert( // input is [0-0xffff] // convert bg color to RGB full range for use inside pipe - if (bg_color->is_ycbcr || is_rgb_limited(bgcolor_cs)) + if (bg_color->is_ycbcr) bg_csc(bg_color, bgcolor_cs); if (output_tf->type == TF_TYPE_DISTRIBUTED_POINTS) { @@ -315,16 +347,7 @@ void vpe_bg_color_convert( // de-gam switch (output_tf->tf) { - case TRANSFER_FUNC_SRGB: - case TRANSFER_FUNC_BT709: - case TRANSFER_FUNC_BT1886: - compute_degam(output_tf->tf, (double)bg_color->rgba.r, °am_r, true); - compute_degam(output_tf->tf, (double)bg_color->rgba.g, °am_g, true); - compute_degam(output_tf->tf, (double)bg_color->rgba.b, °am_b, true); - bg_color->rgba.r = (float)degam_r; - bg_color->rgba.g = (float)degam_g; - bg_color->rgba.b = (float)degam_b; - break; + case TRANSFER_FUNC_PQ2084: compute_depq((double)bg_color->rgba.r, °am_r, true); compute_depq((double)bg_color->rgba.g, °am_g, true); @@ -333,7 +356,11 @@ void vpe_bg_color_convert( bg_color->rgba.g = (float)degam_g; bg_color->rgba.b = (float)degam_b; break; + case TRANSFER_FUNC_SRGB: + case TRANSFER_FUNC_BT709: + case TRANSFER_FUNC_BT1886: case TRANSFER_FUNC_LINEAR_0_125: + case TRANSFER_FUNC_LINEAR_0_1: break; default: VPE_ASSERT(0); @@ -343,15 +370,18 @@ void vpe_bg_color_convert( // for TF_TYPE_BYPASS, bg color should be programmed to mpc as linear } + enum vpe_status vpe_bg_color_outside_cs_gamut( - const struct vpe_color_space *vcs, struct vpe_color *bg_color) + const struct vpe_priv *vpe_priv, struct vpe_color *bg_color) { enum color_space cs; enum color_transfer_func tf; struct vpe_color bg_color_copy = *bg_color; + const struct vpe_color_space *vcs = &vpe_priv->output_ctx.surface.cs; + vpe_color_get_color_space_and_tf(vcs, &cs, &tf); - if (is_rgb_limited(cs) || (bg_color->is_ycbcr)) { + if ((bg_color->is_ycbcr)) { // using the bg_color_copy instead as bg_csc will modify it // we should not do modification in checking stage // otherwise validate_cached_param() will fail @@ -361,3 +391,16 @@ enum vpe_status vpe_bg_color_outside_cs_gamut( } return VPE_STATUS_OK; } + +// These two checks are only neccessary for VPE1.0 and contain alot of quirks to work around VPE 1.0 limitations. +enum vpe_status vpe_is_valid_bg_color(const struct vpe_priv *vpe_priv, struct vpe_color *bg_color) { + + enum vpe_status status = VPE_STATUS_OK; + + status = is_valid_blend(vpe_priv, bg_color); + + if (status == VPE_STATUS_OK) + status = vpe_bg_color_outside_cs_gamut(vpe_priv, bg_color); + + return status; +} diff --git a/src/amd/vpelib/src/core/color_gamma.c b/src/amd/vpelib/src/core/color_gamma.c index 399f56cf96f..254631f3681 100644 --- a/src/amd/vpelib/src/core/color_gamma.c +++ b/src/amd/vpelib/src/core/color_gamma.c @@ -336,33 +336,30 @@ static struct fixed31_32 translate_from_linear_space_ex(struct fixed31_32 arg, return translate_from_linear_space(&scratch_gamma_args); } -static void build_pq(struct pwl_float_data_ex *rgb_regamma, uint32_t hw_points_num, - const struct hw_x_point *coordinate_x, uint32_t hdr_normalization) +static void build_pq(uint32_t hw_points_num, + const struct hw_x_point *coordinate_x, + struct fixed31_32 x_scale, struct fixed31_32 y_scale, struct fixed31_32 y_bias, + struct pwl_float_data_ex *rgb_regamma) { - uint32_t i, start_index; + uint32_t i, curve_start_index; struct pwl_float_data_ex *rgb = rgb_regamma; const struct hw_x_point *coord_x = coordinate_x; struct fixed31_32 output; - struct fixed31_32 scaling_factor = vpe_fixpt_from_fraction(1, hdr_normalization); + struct fixed31_32 slope; - /* TODO: start index is from segment 2^-24, skipping first segment - * due to x values too small for power calculations + /* Curve Start index is from segment 2^-24, the first segment must be + * treated as a linear interpolation due to numbers being to small for power + * operations */ - start_index = 32; - rgb += start_index; - coord_x += start_index; - - for (i = start_index; i <= hw_points_num; i++) { - - vpe_compute_pq(vpe_fixpt_mul(coord_x->x, scaling_factor), &output); - - /* should really not happen? */ - if (vpe_fixpt_lt(output, vpe_fixpt_zero)) - output = vpe_fixpt_zero; - else if (vpe_fixpt_lt(vpe_fixpt_one, output)) - output = vpe_fixpt_one; + curve_start_index = 32; + vpe_compute_pq(vpe_fixpt_mul(coord_x[curve_start_index].x, x_scale), &output); + output = vpe_fixpt_mul(output, y_scale); + slope = vpe_fixpt_div(output, coord_x[curve_start_index].x); + for (i = 0; i < curve_start_index; i++) { + output = vpe_fixpt_mul(coord_x->x, slope); + output = vpe_fixpt_add(output, y_bias); rgb->r = output; rgb->g = output; rgb->b = output; @@ -370,11 +367,12 @@ static void build_pq(struct pwl_float_data_ex *rgb_regamma, uint32_t hw_points_n ++coord_x; ++rgb; } - coord_x = coordinates_x; - rgb = rgb_regamma; - struct fixed31_32 slope = vpe_fixpt_div(rgb[start_index].r, coord_x[start_index].x); - for (i = 0; i < start_index; i++) { - output = vpe_fixpt_mul(coord_x->x, slope); + + for (i = curve_start_index; i <= hw_points_num; i++) { + + vpe_compute_pq(vpe_fixpt_mul(coord_x->x, x_scale), &output); + output = vpe_fixpt_mul(output, y_scale); + output = vpe_fixpt_add(output, y_bias); rgb->r = output; rgb->g = output; rgb->b = output; @@ -384,36 +382,30 @@ static void build_pq(struct pwl_float_data_ex *rgb_regamma, uint32_t hw_points_n } } -static void build_de_pq(struct transfer_func_distributed_points *de_pq, uint32_t hw_points_num, +static void build_de_pq(uint32_t hw_points_num, const struct hw_x_point *coordinate_x_degamma, struct fixed31_32 x_scale, - struct fixed31_32 y_scale) + struct fixed31_32 y_scale, struct fixed31_32 y_bias, struct transfer_func_distributed_points *de_pq) { - uint32_t i; struct fixed31_32 output; - struct fixed31_32 *de_pq_table = vpe_color_get_table(type_de_pq_table); - - precompute_de_pq(x_scale, y_scale); - - for (i = 0; i < hw_points_num; i++) { - output = de_pq_table[i]; - /* should really not happen? */ - if (vpe_fixpt_lt(output, vpe_fixpt_zero)) - output = vpe_fixpt_zero; + for (uint32_t i = 0; i < hw_points_num; i++) { + compute_de_pq(vpe_fixpt_mul(coordinates_x_degamma[i].x, x_scale), &output); + output = vpe_fixpt_mul(output, y_scale); + output = vpe_fixpt_add(output, y_bias); de_pq->red[i] = output; de_pq->green[i] = output; de_pq->blue[i] = output; } } -static bool build_degamma(struct transfer_func_distributed_points *curve, uint32_t hw_points_num, +static bool build_degamma(uint32_t hw_points_num, const struct hw_x_point *coordinate_x_degamma, enum color_transfer_func type, - struct fixed31_32 yuv2rgbScaling) + struct fixed31_32 x_scale, struct fixed31_32 y_scale, struct fixed31_32 y_bias, + struct transfer_func_distributed_points *curve) { uint32_t i; struct gamma_coefficients coeff; - struct fixed31_32 scaledX; - struct fixed31_32 scaledY; + struct fixed31_32 output; bool ret = false; if (!build_coefficients(&coeff, type)) @@ -424,11 +416,15 @@ static bool build_degamma(struct transfer_func_distributed_points *curve, uint32 i = 0; while (i != MAX_HW_POINTS_DEGAMMA) { - scaledX = vpe_fixpt_mul(coordinate_x_degamma[i].x, yuv2rgbScaling); - scaledY = translate_to_linear_space_ex(scaledX, &coeff, 0); - curve->red[i] = scaledY; - curve->green[i] = scaledY; - curve->blue[i] = scaledY; + output = vpe_fixpt_mul(coordinate_x_degamma[i].x, x_scale); + + output = translate_to_linear_space_ex(output, &coeff, 0); + output = vpe_fixpt_mul(output, y_scale); + output = vpe_fixpt_add(output, y_bias); + + curve->red[i] = output; + curve->green[i] = output; + curve->blue[i] = output; i++; } ret = true; @@ -436,9 +432,10 @@ release: return ret; } -static bool build_regamma(struct vpe_priv *vpe_priv, struct pwl_float_data_ex *rgb_regamma, - uint32_t hw_points_num, const struct hw_x_point *coordinate_x, enum color_transfer_func type, - struct calculate_buffer *cal_buffer) +static bool build_regamma(struct vpe_priv *vpe_priv, uint32_t hw_points_num, + const struct hw_x_point *coordinate_x, enum color_transfer_func type, + struct fixed31_32 x_scale, struct fixed31_32 y_scale, struct fixed31_32 y_bias, + struct calculate_buffer *cal_buffer, struct pwl_float_data_ex *rgb_regamma) { uint32_t i; bool ret = false; @@ -460,7 +457,9 @@ static bool build_regamma(struct vpe_priv *vpe_priv, struct pwl_float_data_ex *r i = 0; while (i <= hw_points_num) { /* TODO use y vs r,g,b */ - rgb->r = translate_from_linear_space_ex(coord_x->x, coeff, 0, cal_buffer); + rgb->r = vpe_fixpt_mul(coord_x->x, x_scale); + rgb->r = translate_from_linear_space_ex(rgb->r, coeff, 0, cal_buffer); + rgb->r = vpe_fixpt_add(vpe_fixpt_mul(rgb->r, y_scale), y_bias); rgb->g = rgb->r; rgb->b = rgb->r; ++coord_x; @@ -488,8 +487,7 @@ static void build_new_custom_resulted_curve( } } -static bool map_regamma_hw_to_x_user(struct pixel_gamma_point *coeff128, - struct hw_x_point *coords_x, const struct pwl_float_data_ex *rgb_regamma, +static bool map_regamma_hw_to_x_user(struct hw_x_point *coords_x, const struct pwl_float_data_ex *rgb_regamma, uint32_t hw_points_num, struct transfer_func_distributed_points *tf_pts, bool doClamping) { /* setup to spare calculated ideal regamma values */ @@ -517,78 +515,114 @@ static bool map_regamma_hw_to_x_user(struct pixel_gamma_point *coeff128, } static bool calculate_curve(struct vpe_priv *vpe_priv, enum color_transfer_func trans, + struct fixed31_32 x_scale, struct fixed31_32 y_scale, struct fixed31_32 y_bias, struct transfer_func_distributed_points *points, struct pwl_float_data_ex *rgb_regamma, - uint32_t hdr_normalization, struct calculate_buffer *cal_buffer) + struct calculate_buffer *cal_buffer) { + int hdr_norm = vpe_priv->resource.internal_hdr_normalization; + int y_norm; + struct fixed31_32 combined_scale; + bool ret = false; - - if (trans == TRANSFER_FUNC_PQ2084) { - points->end_exponent = 0; - points->x_point_at_y1_red = 1; - points->x_point_at_y1_green = 1; - points->x_point_at_y1_blue = 1; - - build_pq(rgb_regamma, MAX_HW_POINTS, coordinates_x, hdr_normalization); + switch (trans) + { + case TRANSFER_FUNC_SRGB: + case TRANSFER_FUNC_BT709: + case TRANSFER_FUNC_BT1886: + build_regamma(vpe_priv, MAX_HW_POINTS, coordinates_x, trans, x_scale, y_scale, y_bias, cal_buffer, rgb_regamma); ret = true; - } else if (trans == TRANSFER_FUNC_LINEAR_0_125) { + break; + case TRANSFER_FUNC_PQ2084: + build_pq(MAX_HW_POINTS, coordinates_x, x_scale, y_scale, y_bias, rgb_regamma); + ret = true; + break; + case TRANSFER_FUNC_LINEAR_0_125: + case TRANSFER_FUNC_LINEAR_0_1: + if (trans == TRANSFER_FUNC_LINEAR_0_1) + y_norm = 1; + else + y_norm = CCCS_NORM; + + combined_scale = vpe_fixpt_div_int(vpe_fixpt_from_int(y_norm), hdr_norm); + combined_scale = vpe_fixpt_mul(combined_scale, y_scale); + combined_scale = vpe_fixpt_mul(combined_scale, x_scale); for (int i = 0; i < MAX_HW_POINTS; i++) { - rgb_regamma[i].r = - vpe_fixpt_mul(coordinates_x[i].x, vpe_fixpt_from_fraction(125, hdr_normalization)); + rgb_regamma[i].r = vpe_fixpt_mul(coordinates_x[i].x, combined_scale); + rgb_regamma[i].r = vpe_fixpt_add(rgb_regamma[i].r, y_bias); rgb_regamma[i].g = rgb_regamma[i].r; rgb_regamma[i].b = rgb_regamma[i].r; } + ret = true; - } else { - // trans == TRANSFER_FUNC_SRGB - // trans == TRANSFER_FUNC_BT709 - // trans == TRANSFER_FUNCTION_GAMMA22 - // trans == TRANSFER_FUNCTION_GAMMA24 - // trans == TRANSFER_FUNCTION_GAMMA26 - points->end_exponent = 0; - points->x_point_at_y1_red = 1; - points->x_point_at_y1_green = 1; - points->x_point_at_y1_blue = 1; - - build_regamma(vpe_priv, rgb_regamma, MAX_HW_POINTS, coordinates_x, trans, cal_buffer); - - ret = true; + break; + case TRANSFER_FUNC_NORMALIZED_PQ: + case TRANSFER_FUNC_UNKNOWN: + break; + default: + break; } + return ret; } #define _EXTRA_POINTS 3 bool vpe_color_calculate_degamma_params(struct vpe_priv *vpe_priv, struct fixed31_32 x_scale, - struct fixed31_32 y_scale, struct transfer_func *input_tf) + struct fixed31_32 y_scale, struct fixed31_32 y_bias, struct transfer_func *input_tf) { - struct transfer_func_distributed_points *tf_pts = &input_tf->tf_pts; - enum color_transfer_func tf; - uint32_t i; - bool ret = true; + struct transfer_func_distributed_points *tf_pts = &input_tf->tf_pts; + enum color_transfer_func tf = input_tf->tf; + int hdr_norm = vpe_priv->resource.internal_hdr_normalization; + bool ret = false; + int x_norm; + struct fixed31_32 scale_combined; + struct fixed31_32 output; - tf = input_tf->tf; - if (tf == TRANSFER_FUNC_PQ2084 || tf == TRANSFER_FUNC_NORMALIZED_PQ) - build_de_pq(tf_pts, MAX_HW_POINTS_DEGAMMA, coordinates_x_degamma, x_scale, y_scale); - else if (tf == TRANSFER_FUNC_SRGB || tf == TRANSFER_FUNC_BT709 || tf == TRANSFER_FUNC_BT1886) - build_degamma(tf_pts, MAX_HW_POINTS_DEGAMMA, coordinates_x_degamma, tf, x_scale); - else if (tf == TRANSFER_FUNC_LINEAR_0_125) { - // just copy coordinates_x_degamma into curve - i = 0; - while (i != MAX_HW_POINTS_DEGAMMA) { - tf_pts->red[i] = vpe_fixpt_mul(coordinates_x[i].x, y_scale); - tf_pts->red[i] = tf_pts->red[i]; - tf_pts->red[i] = tf_pts->red[i]; - i++; + + switch (tf) + { + case TRANSFER_FUNC_PQ2084: + case TRANSFER_FUNC_NORMALIZED_PQ: + build_de_pq(MAX_HW_POINTS_DEGAMMA, coordinates_x_degamma, x_scale, y_scale, y_bias, tf_pts); + ret = true; + break; + case TRANSFER_FUNC_SRGB: + case TRANSFER_FUNC_BT709: + case TRANSFER_FUNC_BT1886: + build_degamma(MAX_HW_POINTS_DEGAMMA, coordinates_x_degamma, tf, x_scale, y_scale, y_bias, tf_pts); + ret = true; + break; + case TRANSFER_FUNC_LINEAR_0_1: + case TRANSFER_FUNC_LINEAR_0_125: + if (tf == TRANSFER_FUNC_LINEAR_0_1) + x_norm = 1; + else + x_norm = CCCS_NORM; + + scale_combined = vpe_fixpt_div_int(vpe_fixpt_from_int(hdr_norm), x_norm); + scale_combined = vpe_fixpt_mul(scale_combined, x_scale); + scale_combined = vpe_fixpt_mul(scale_combined, y_scale); + + for (int i = 0; i < MAX_HW_POINTS_DEGAMMA; i ++) { + output = vpe_fixpt_mul(coordinates_x_degamma[i].x, scale_combined); + output = vpe_fixpt_add(output, y_bias); + tf_pts->red[i] = output; + tf_pts->green[i] = output; + tf_pts->blue[i] = output; } - } else - ret = false; - + ret = true; + break; + default: + break; + } return ret; } bool vpe_color_calculate_regamma_params( - struct vpe_priv *vpe_priv, struct transfer_func *output_tf, struct calculate_buffer *cal_buffer) + struct vpe_priv* vpe_priv, struct fixed31_32 x_scale, + struct fixed31_32 y_scale, struct fixed31_32 y_bias, + struct calculate_buffer *cal_buffer, struct transfer_func *output_tf) { struct transfer_func_distributed_points *tf_pts = &output_tf->tf_pts; struct pwl_float_data_ex *rgb_regamma = NULL; @@ -608,11 +642,13 @@ bool vpe_color_calculate_regamma_params( tf = output_tf->tf; - ret = calculate_curve(vpe_priv, tf, tf_pts, rgb_regamma, - vpe_priv->resource.internal_hdr_normalization, cal_buffer); + ret = calculate_curve(vpe_priv, tf, + x_scale, y_scale, y_bias, + tf_pts, rgb_regamma, + cal_buffer); if (ret) { - map_regamma_hw_to_x_user(coeff, coordinates_x, rgb_regamma, MAX_HW_POINTS, tf_pts, false); + map_regamma_hw_to_x_user(coordinates_x, rgb_regamma, MAX_HW_POINTS, tf_pts, false); } vpe_free(coeff); diff --git a/src/amd/vpelib/src/core/inc/color.h b/src/amd/vpelib/src/core/inc/color.h index 231ef6a737c..4e6db7cb894 100644 --- a/src/amd/vpelib/src/core/inc/color.h +++ b/src/amd/vpelib/src/core/inc/color.h @@ -32,9 +32,14 @@ extern "C" { #endif -#define SDR_VIDEO_WHITE_POINT 100 // nits -#define SDR_WHITE_POINT 80 // nits -#define HDR_PEAK_WHITE 10000 +#define SDR_VIDEO_WHITE_POINT 100 // nits +#define SDR_WHITE_POINT 80 // nits +#define HDR_PEAK_WHITE 10000 +#define CCCS_NORM HDR_PEAK_WHITE/SDR_WHITE_POINT +#define STUDIO_RANGE_FOOT_ROOM_10_BIT vpe_fixpt_from_fraction(64, 1023) +#define STUDIO_RANGE_SCALE_10_BIT vpe_fixpt_from_fraction(940 - 64, 1023) +#define STUDIO_RANGE_FOOT_ROOM_8_BIT vpe_fixpt_from_fraction(16, 255) +#define STUDIO_RANGE_SCALE_8_BIT vpe_fixpt_from_fraction(235 - 16, 255) struct vpe_priv; struct stream_ctx; @@ -52,13 +57,19 @@ enum color_depth { COLOR_DEPTH_COUNT }; -enum color_transfer_func { +/* +* Special comment for TRANSFER_FUNC_LINEAR_0_125, + Inside of the pipeline, vpe operates in 0 - 1 nominal range. + For degamma operations, 0_125 refers to input normalization and for regam, 0_125 refers to output normalization. + */ + enum color_transfer_func { TRANSFER_FUNC_UNKNOWN, TRANSFER_FUNC_SRGB, TRANSFER_FUNC_BT709, TRANSFER_FUNC_BT1886, TRANSFER_FUNC_PQ2084, - TRANSFER_FUNC_LINEAR_0_125, + TRANSFER_FUNC_LINEAR_0_1, + TRANSFER_FUNC_LINEAR_0_125, TRANSFER_FUNC_NORMALIZED_PQ }; @@ -115,6 +126,11 @@ enum transfer_func_type { TF_TYPE_HWPWL }; +enum cm_type { + CM_DEGAM, + CM_REGAM, +}; + enum { TRANSFER_FUNC_POINTS = 1025 }; @@ -135,6 +151,8 @@ struct transfer_func_distributed_points { struct transfer_func { enum transfer_func_type type; enum color_transfer_func tf; + enum cm_type cm_gamma_type; + struct fixed31_32 start_base; //Used to clamp curve start /* FP16 1.0 reference level in nits, default is 80 nits, only for PQ*/ uint32_t sdr_ref_white_level; @@ -235,10 +253,6 @@ enum vpe_status vpe_color_tm_update_hdr_mult(uint16_t shaper_in_exp_max, uint32_ enum vpe_status vpe_color_update_shaper( uint16_t shaper_in_exp_max, struct transfer_func *shaper_func, bool enable_3dlut); -enum vpe_status vpe_color_update_blnd_gam(struct vpe_priv *vpe_priv, - const struct vpe_build_param *param, const struct vpe_tonemap_params *tm_params, - struct transfer_func *blnd_tf_func, bool enable_3dlut); - enum vpe_status vpe_color_build_tm_cs(const struct vpe_tonemap_params *tm_params, struct vpe_surface_info surface_info, struct vpe_color_space *vcs); diff --git a/src/amd/vpelib/src/core/inc/color_bg.h b/src/amd/vpelib/src/core/inc/color_bg.h index 8821e9e6948..fbc47695a35 100644 --- a/src/amd/vpelib/src/core/inc/color_bg.h +++ b/src/amd/vpelib/src/core/inc/color_bg.h @@ -30,4 +30,7 @@ void vpe_bg_color_convert( enum color_space cs, struct transfer_func *output_tf, struct vpe_color *bg_color); enum vpe_status vpe_bg_color_outside_cs_gamut( - const struct vpe_color_space *vcs, struct vpe_color *bg_color); + const struct vpe_priv *vpe_priv, struct vpe_color *bg_color); + +enum vpe_status vpe_is_valid_bg_color(const struct vpe_priv *vpe_priv, struct vpe_color *bg_color); + diff --git a/src/amd/vpelib/src/core/inc/color_gamma.h b/src/amd/vpelib/src/core/inc/color_gamma.h index a870697947f..02c42e36eaa 100644 --- a/src/amd/vpelib/src/core/inc/color_gamma.h +++ b/src/amd/vpelib/src/core/inc/color_gamma.h @@ -51,11 +51,12 @@ void vpe_color_setup_x_points_distribution(void); void vpe_color_setup_x_points_distribution_degamma(void); -bool vpe_color_calculate_regamma_params(struct vpe_priv *vpe_priv, struct transfer_func *output_tf, - struct calculate_buffer *cal_buffer); +bool vpe_color_calculate_regamma_params(struct vpe_priv *vpe_priv, struct fixed31_32 x_scale, + struct fixed31_32 y_scale, struct fixed31_32 y_bias, + struct calculate_buffer *cal_buffer, struct transfer_func* output_tf); bool vpe_color_calculate_degamma_params(struct vpe_priv *vpe_priv, struct fixed31_32 x_scale, - struct fixed31_32 y_scale, struct transfer_func *input_tf); + struct fixed31_32 y_scale, struct fixed31_32 y_bias, struct transfer_func *input_tf); void vpe_compute_pq(struct fixed31_32 in_x, struct fixed31_32 *out_y); diff --git a/src/amd/vpelib/src/core/inc/hw_shared.h b/src/amd/vpelib/src/core/inc/hw_shared.h index 43e477fb367..464358dd7ec 100644 --- a/src/amd/vpelib/src/core/inc/hw_shared.h +++ b/src/amd/vpelib/src/core/inc/hw_shared.h @@ -30,11 +30,6 @@ extern "C" { #endif -enum cm_type { - CM_DEGAM, - CM_REGAM, -}; - struct bias_and_scale { uint32_t scale_red; uint32_t bias_red; diff --git a/src/amd/vpelib/src/core/inc/mpc.h b/src/amd/vpelib/src/core/inc/mpc.h index 7ac9fac4e56..3a4162cbbf1 100644 --- a/src/amd/vpelib/src/core/inc/mpc.h +++ b/src/amd/vpelib/src/core/inc/mpc.h @@ -143,7 +143,7 @@ struct mpc_funcs { bool use_tetrahedral_9, bool use_12bits); // Blend-gamma control. - void (*program_1dlut)(struct mpc *mpc, const struct pwl_params *params); + void (*program_1dlut)(struct mpc *mpc, const struct pwl_params *params, enum cm_type gamma_type); void (*program_cm_location)(struct mpc *mpc, uint8_t location); diff --git a/src/amd/vpelib/src/core/vpelib.c b/src/amd/vpelib/src/core/vpelib.c index 06cd5af9aab..224d4c1abd2 100644 --- a/src/amd/vpelib/src/core/vpelib.c +++ b/src/amd/vpelib/src/core/vpelib.c @@ -343,7 +343,7 @@ enum vpe_status vpe_check_support( vpe_cache_tone_map_params(&vpe_priv->stream_ctx[i], param); } - if (status == VPE_STATUS_OK) { + if (status == VPE_STATUS_OK) { // output checking - check per asic support status = vpe_check_output_support(vpe, param); if (status != VPE_STATUS_OK) { @@ -430,7 +430,7 @@ enum vpe_status vpe_check_support( // if the bg_color support is false, there is a flag to verify if the bg_color falls in the // output gamut if (!vpe_priv->pub.caps->bg_color_check_support) { - status = vpe_bg_color_outside_cs_gamut(&output_ctx->surface.cs, &output_ctx->bg_color); + status = vpe_is_valid_bg_color(vpe_priv, &output_ctx->bg_color); if (status != VPE_STATUS_OK) { vpe_log( "failed in checking the background color versus the output color space %d\n", @@ -504,28 +504,6 @@ static bool validate_cached_param(struct vpe_priv *vpe_priv, const struct vpe_bu return true; } -static bool validate_color_pipeline(struct vpe_priv *vpe_priv, const struct vpe_build_param *param) -{ - uint32_t stream_idx; - struct stream_ctx *stream_ctx; - struct output_ctx *output_ctx; - - output_ctx = &vpe_priv->output_ctx; - - /* For BG color, we need to make sure degamm / regamm is not bypass, - * as we want to have input in the range of 0-1 in mpc, - * since mpc only allows 0-1 range for BG color - */ - for (stream_idx = 0; stream_idx < param->num_streams; stream_idx++) { - stream_ctx = &vpe_priv->stream_ctx[stream_idx]; - if (output_ctx->output_tf->type == TF_TYPE_BYPASS && - stream_ctx->input_tf->type == TF_TYPE_BYPASS) - return false; - } - - return true; -} - enum vpe_status vpe_build_commands( struct vpe *vpe, const struct vpe_build_param *param, struct vpe_build_bufs *bufs) { @@ -622,9 +600,7 @@ enum vpe_status vpe_build_commands( vpe_log("failed updating whitepoint gain %d\n", (int)status); } } - if (status == VPE_STATUS_OK) { - VPE_ASSERT(validate_color_pipeline(vpe_priv, param)); - } + if (status == VPE_STATUS_OK) { vpe_bg_color_convert(vpe_priv->output_ctx.cs, vpe_priv->output_ctx.output_tf, &vpe_priv->output_ctx.bg_color);