| /* |
| * Copyright (c) 2021, Alliance for Open Media. All rights reserved |
| * |
| * This source code is subject to the terms of the BSD 3-Clause Clear License |
| * and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause |
| * Clear License was not distributed with this source code in the LICENSE file, |
| * you can obtain it at aomedia.org/license/software-license/bsd-3-c-c/. |
| * If the Alliance for Open Media Patent License 1.0 was not distributed with |
| * this source code in the PATENTS file, you can obtain it at |
| * aomedia.org/license/patent-license/. |
| */ |
| |
| #include "av1/common/tip.h" |
| #include "config/aom_scale_rtcd.h" |
| #if CONFIG_OPTFLOW_ON_TIP |
| #include "config/aom_dsp_rtcd.h" |
| #endif // CONFIG_OPTFLOW_ON_TIP |
| |
| // CHROMA_MI_SIZE is the block size in luma unit for Chroma TIP interpolation |
| #define CHROMA_MI_SIZE (TMVP_MI_SIZE) |
| // Maximum block size is allowed to combine the blocks with same MV |
| #define MAX_BLOCK_SIZE_WITH_SAME_MV \ |
| 8 // Needs to be 8 when across scale |
| // prediction is needed due to use of |
| // superres or resize. A higher value |
| // such as 128 could be used if |
| // across scale prediction is not |
| // invoked. |
| // Percentage threshold of number of blocks with available motion |
| // projection in a frame to allow TIP mode |
| #define TIP_ENABLE_COUNT_THRESHOLD 60 |
| |
| static void tip_find_closest_bi_dir_ref_frames(AV1_COMMON *cm, |
| int ref_order_hints[2], |
| MV_REFERENCE_FRAME rf[2]) { |
| const OrderHintInfo *const order_hint_info = &cm->seq_params.order_hint_info; |
| |
| if (!order_hint_info->enable_order_hint || frame_is_intra_only(cm)) return; |
| |
| const int cur_order_hint = cm->current_frame.order_hint; |
| |
| // Identify the nearest forward and backward references. |
| for (int i = 0; i < INTER_REFS_PER_FRAME; i++) { |
| const RefCntBuffer *const buf = get_ref_frame_buf(cm, i); |
| if (buf == NULL) continue; |
| |
| const int ref_order_hint = buf->order_hint; |
| const int ref_to_cur_dist = |
| get_relative_dist(order_hint_info, ref_order_hint, cur_order_hint); |
| if (ref_to_cur_dist < 0) { |
| // Forward reference |
| if (ref_order_hints[0] == -1 || |
| get_relative_dist(order_hint_info, ref_order_hint, |
| ref_order_hints[0]) > 0) { |
| ref_order_hints[0] = ref_order_hint; |
| rf[0] = i; |
| } |
| } else if (ref_to_cur_dist > 0) { |
| // Backward reference |
| if (ref_order_hints[1] == INT_MAX || |
| get_relative_dist(order_hint_info, ref_order_hint, |
| ref_order_hints[1]) < 0) { |
| ref_order_hints[1] = ref_order_hint; |
| rf[1] = i; |
| } |
| } |
| } |
| } |
| |
| static AOM_INLINE int tip_find_reference_frame(AV1_COMMON *cm, int start_frame, |
| int target_frame_order) { |
| const RefCntBuffer *const start_frame_buf = |
| get_ref_frame_buf(cm, start_frame); |
| |
| const int *const ref_order_hints = &start_frame_buf->ref_order_hints[0]; |
| for (MV_REFERENCE_FRAME rf = 0; rf < INTER_REFS_PER_FRAME; ++rf) { |
| if (ref_order_hints[rf] == target_frame_order) { |
| return 1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int tip_motion_field_projection(AV1_COMMON *cm, |
| MV_REFERENCE_FRAME nearest_ref[2], |
| int nearest_ref_order_hint[2]) { |
| int ref_frame_offset = 0; |
| int target_order_hint = 0; |
| OrderHintInfo *order_hint_info = &cm->seq_params.order_hint_info; |
| |
| MV_REFERENCE_FRAME start_frame = NONE_FRAME; |
| int find_ref = |
| tip_find_reference_frame(cm, nearest_ref[0], nearest_ref_order_hint[1]); |
| if (find_ref) { |
| ref_frame_offset = get_relative_dist( |
| order_hint_info, nearest_ref_order_hint[0], nearest_ref_order_hint[1]); |
| start_frame = nearest_ref[0]; |
| target_order_hint = nearest_ref_order_hint[1]; |
| } else { |
| find_ref = |
| tip_find_reference_frame(cm, nearest_ref[1], nearest_ref_order_hint[0]); |
| if (!find_ref) return 0; |
| ref_frame_offset = get_relative_dist( |
| order_hint_info, nearest_ref_order_hint[1], nearest_ref_order_hint[0]); |
| start_frame = nearest_ref[1]; |
| target_order_hint = nearest_ref_order_hint[0]; |
| } |
| |
| const RefCntBuffer *const start_frame_buf = |
| get_ref_frame_buf(cm, start_frame); |
| if (!is_ref_motion_field_eligible(cm, start_frame_buf)) return 0; |
| |
| const int start_frame_order_hint = start_frame_buf->order_hint; |
| |
| assert(start_frame_buf->width == cm->width && |
| start_frame_buf->height == cm->height); |
| |
| const int *const ref_order_hints = start_frame_buf->ref_order_hints; |
| const int cur_order_hint = cm->cur_frame->order_hint; |
| int start_to_current_frame_offset = get_relative_dist( |
| order_hint_info, start_frame_order_hint, cur_order_hint); |
| |
| const int is_backward = ref_frame_offset < 0; |
| if (is_backward) { |
| ref_frame_offset = -ref_frame_offset; |
| start_to_current_frame_offset = -start_to_current_frame_offset; |
| } |
| |
| const int temporal_scale_factor = |
| tip_derive_scale_factor(start_to_current_frame_offset, ref_frame_offset); |
| |
| TPL_MV_REF *tpl_mvs_base = cm->tpl_mvs; |
| const MV_REF *mv_ref_base = start_frame_buf->mvs; |
| const int mvs_rows = |
| ROUND_POWER_OF_TWO(cm->mi_params.mi_rows, TMVP_SHIFT_BITS); |
| const int mvs_cols = |
| ROUND_POWER_OF_TWO(cm->mi_params.mi_cols, TMVP_SHIFT_BITS); |
| const int mvs_stride = mvs_cols; |
| for (int blk_row = 0; blk_row < mvs_rows; ++blk_row) { |
| for (int blk_col = 0; blk_col < mvs_cols; ++blk_col) { |
| const MV_REF *mv_ref = &mv_ref_base[blk_row * mvs_stride + blk_col]; |
| MV_REFERENCE_FRAME ref_frame[2] = { mv_ref->ref_frame[0], |
| mv_ref->ref_frame[1] }; |
| for (int idx = 0; idx < 2; ++idx) { |
| if (is_inter_ref_frame(ref_frame[idx])) { |
| const int ref_frame_order_hint = ref_order_hints[ref_frame[idx]]; |
| if (ref_frame_order_hint == target_order_hint) { |
| MV ref_mv = mv_ref->mv[idx].as_mv; |
| int_mv this_mv; |
| int mi_r = 0; |
| int mi_c = 0; |
| tip_get_mv_projection(&this_mv.as_mv, ref_mv, |
| temporal_scale_factor); |
| const int pos_valid = get_block_position(cm, &mi_r, &mi_c, blk_row, |
| blk_col, this_mv.as_mv, 0); |
| if (pos_valid) { |
| if (is_backward) { |
| ref_mv.row = -ref_mv.row; |
| ref_mv.col = -ref_mv.col; |
| } |
| |
| const int mi_offset = mi_r * mvs_stride + mi_c; |
| if (tpl_mvs_base[mi_offset].mfmv0.as_int == INVALID_MV) { |
| tpl_mvs_base[mi_offset].mfmv0.as_mv.row = ref_mv.row; |
| tpl_mvs_base[mi_offset].mfmv0.as_mv.col = ref_mv.col; |
| tpl_mvs_base[mi_offset].ref_frame_offset = ref_frame_offset; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| return 1; |
| } |
| |
| void av1_derive_tip_nearest_ref_frames_motion_projection(AV1_COMMON *cm) { |
| int nearest_ref_order_hints[2] = { -1, INT_MAX }; |
| MV_REFERENCE_FRAME nearest_rf[2] = { NONE_FRAME, NONE_FRAME }; |
| tip_find_closest_bi_dir_ref_frames(cm, nearest_ref_order_hints, nearest_rf); |
| if (nearest_rf[0] != NONE_FRAME && nearest_rf[1] != NONE_FRAME) { |
| cm->tip_ref.ref_frame[0] = nearest_rf[0]; |
| cm->tip_ref.ref_frame[1] = nearest_rf[1]; |
| tip_motion_field_projection(cm, nearest_rf, nearest_ref_order_hints); |
| } else { |
| cm->tip_ref.ref_frame[0] = NONE_FRAME; |
| cm->tip_ref.ref_frame[1] = NONE_FRAME; |
| } |
| } |
| |
| static void tip_temporal_scale_motion_field(AV1_COMMON *cm, |
| const int ref_frames_offset) { |
| TPL_MV_REF *tpl_mvs_base = cm->tpl_mvs; |
| const int mvs_rows = |
| ROUND_POWER_OF_TWO(cm->mi_params.mi_rows, TMVP_SHIFT_BITS); |
| const int mvs_cols = |
| ROUND_POWER_OF_TWO(cm->mi_params.mi_cols, TMVP_SHIFT_BITS); |
| const int mvs_stride = mvs_cols; |
| for (int blk_row = 0; blk_row < mvs_rows; ++blk_row) { |
| for (int blk_col = 0; blk_col < mvs_cols; ++blk_col) { |
| const int tpl_offset = blk_row * mvs_stride + blk_col; |
| TPL_MV_REF *tpl_mvs = tpl_mvs_base + tpl_offset; |
| if (tpl_mvs->mfmv0.as_int != INVALID_MV) { |
| int_mv this_refmv; |
| get_mv_projection(&this_refmv.as_mv, tpl_mvs->mfmv0.as_mv, |
| ref_frames_offset, tpl_mvs->ref_frame_offset); |
| tpl_mvs->mfmv0.as_int = this_refmv.as_int; |
| tpl_mvs->ref_frame_offset = ref_frames_offset; |
| } |
| } |
| } |
| } |
| |
| static void tip_fill_motion_field_holes(AV1_COMMON *cm) { |
| TPL_MV_REF *tpl_mvs_base = cm->tpl_mvs; |
| const int mvs_rows = |
| ROUND_POWER_OF_TWO(cm->mi_params.mi_rows, TMVP_SHIFT_BITS); |
| const int mvs_cols = |
| ROUND_POWER_OF_TWO(cm->mi_params.mi_cols, TMVP_SHIFT_BITS); |
| const int mvs_stride = mvs_cols; |
| const int total_units = mvs_rows * mvs_cols; |
| |
| MV_REF *tmvp_mvs = cm->cur_frame->mvs; |
| int write_pos = 0; |
| for (int blk_row = 0; blk_row < mvs_rows; ++blk_row) { |
| for (int blk_col = 0; blk_col < mvs_cols; ++blk_col) { |
| const int tpl_offset = blk_row * mvs_stride + blk_col; |
| TPL_MV_REF *tpl_mvs = tpl_mvs_base + tpl_offset; |
| if (tpl_mvs->mfmv0.as_int != INVALID_MV) { |
| tmvp_mvs[write_pos].mv[0].as_mv.row = blk_row; |
| tmvp_mvs[write_pos].mv[0].as_mv.col = blk_col; |
| write_pos++; |
| } |
| } |
| } |
| |
| #define ITER_DIR 4 |
| const int dirs[ITER_DIR][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } }; |
| |
| int read_pos = 0; |
| while (read_pos < write_pos && write_pos < total_units) { |
| const int start = read_pos; |
| const int end = write_pos; |
| for (int i = start; i < end; ++i) { |
| const int cur_row = tmvp_mvs[i].mv[0].as_mv.row; |
| const int cur_col = tmvp_mvs[i].mv[0].as_mv.col; |
| const int cur_tpl_offset = cur_row * mvs_stride + cur_col; |
| for (int dir = 0; dir < ITER_DIR; ++dir) { |
| const int next_row = cur_row + dirs[dir][0]; |
| const int next_col = cur_col + dirs[dir][1]; |
| const int next_tpl_offset = next_row * mvs_stride + next_col; |
| if (next_row < 0 || next_row >= mvs_rows || next_col < 0 || |
| next_col >= mvs_cols || |
| tpl_mvs_base[next_tpl_offset].mfmv0.as_int != INVALID_MV) { |
| continue; |
| } |
| tpl_mvs_base[next_tpl_offset].mfmv0.as_int = |
| tpl_mvs_base[cur_tpl_offset].mfmv0.as_int; |
| tmvp_mvs[write_pos].mv[0].as_mv.row = next_row; |
| tmvp_mvs[write_pos].mv[0].as_mv.col = next_col; |
| write_pos++; |
| } |
| } |
| read_pos = end; |
| } |
| } |
| |
| static void tip_blk_average_filter_mv(AV1_COMMON *cm) { |
| TPL_MV_REF *tpl_mvs_base = cm->tpl_mvs; |
| const int mvs_rows = |
| ROUND_POWER_OF_TWO(cm->mi_params.mi_rows, TMVP_SHIFT_BITS); |
| const int mvs_cols = |
| ROUND_POWER_OF_TWO(cm->mi_params.mi_cols, TMVP_SHIFT_BITS); |
| const int mvs_stride = mvs_cols; |
| |
| MV_REF *avg_mvs = cm->cur_frame->mvs; |
| for (int i = 0; i < mvs_rows; i++) { |
| const int i0 = i - 1; |
| const int i1 = i + 1; |
| for (int j = 0; j < mvs_cols; j++) { |
| const int j0 = j - 1; |
| const int j1 = j + 1; |
| int count = 1; |
| MV this_mv; |
| const int cur_pos = i * mvs_stride + j; |
| this_mv.row = tpl_mvs_base[cur_pos].mfmv0.as_mv.row; |
| this_mv.col = tpl_mvs_base[cur_pos].mfmv0.as_mv.col; |
| if (i0 >= 0) { |
| count++; |
| const int top_pos = i0 * mvs_stride + j; |
| this_mv.row += tpl_mvs_base[top_pos].mfmv0.as_mv.row; |
| this_mv.col += tpl_mvs_base[top_pos].mfmv0.as_mv.col; |
| } |
| |
| if (i1 < mvs_rows) { |
| count++; |
| const int bottom_pos = i1 * mvs_stride + j; |
| this_mv.row += tpl_mvs_base[bottom_pos].mfmv0.as_mv.row; |
| this_mv.col += tpl_mvs_base[bottom_pos].mfmv0.as_mv.col; |
| } |
| |
| if (j0 >= 0) { |
| count++; |
| const int left_pos = i * mvs_stride + j0; |
| this_mv.row += tpl_mvs_base[left_pos].mfmv0.as_mv.row; |
| this_mv.col += tpl_mvs_base[left_pos].mfmv0.as_mv.col; |
| } |
| |
| if (j1 < mvs_cols) { |
| count++; |
| const int right_pos = i * mvs_stride + j1; |
| this_mv.row += tpl_mvs_base[right_pos].mfmv0.as_mv.row; |
| this_mv.col += tpl_mvs_base[right_pos].mfmv0.as_mv.col; |
| } |
| |
| avg_mvs[cur_pos].mv[0].as_mv.row = this_mv.row / count; |
| avg_mvs[cur_pos].mv[0].as_mv.col = this_mv.col / count; |
| } |
| } |
| |
| for (int i = 0; i < mvs_rows; i++) { |
| for (int j = 0; j < mvs_cols; j++) { |
| const int tpl_offset = i * mvs_stride + j; |
| tpl_mvs_base[tpl_offset].mfmv0.as_int = avg_mvs[tpl_offset].mv[0].as_int; |
| } |
| } |
| } |
| |
| static INLINE MV tip_clamp_tip_mv_to_umv_border_sb( |
| const MV *src_mv, int bw, int bh, int ss_x, int ss_y, int dist_to_left_edge, |
| int dist_to_right_edge, int dist_to_top_edge, int dist_to_bottom_edge) { |
| // If the MV points so far into the UMV border that no visible pixels |
| // are used for reconstruction, the subpel part of the MV can be |
| // discarded and the MV limited to 16 pixels with equivalent results. |
| const int spel_left = (AOM_INTERP_EXTEND + bw) << SUBPEL_BITS; |
| const int spel_right = spel_left - SUBPEL_SHIFTS; |
| const int spel_top = (AOM_INTERP_EXTEND + bh) << SUBPEL_BITS; |
| const int spel_bottom = spel_top - SUBPEL_SHIFTS; |
| MV clamped_mv = { (int16_t)(src_mv->row * (1 << (1 - ss_y))), |
| (int16_t)(src_mv->col * (1 << (1 - ss_x))) }; |
| assert(ss_x <= 1); |
| assert(ss_y <= 1); |
| const SubpelMvLimits mv_limits = { |
| dist_to_left_edge * (1 << (1 - ss_x)) - spel_left, |
| dist_to_right_edge * (1 << (1 - ss_x)) + spel_right, |
| dist_to_top_edge * (1 << (1 - ss_y)) - spel_top, |
| dist_to_bottom_edge * (1 << (1 - ss_y)) + spel_bottom |
| }; |
| |
| clamp_mv(&clamped_mv, &mv_limits); |
| |
| return clamped_mv; |
| } |
| |
| static INLINE int tip_check_motion_field(AV1_COMMON *cm, const MV *mv, int mi_x, |
| int mi_y, int bw, int bh, int frame_w, |
| int frame_h) { |
| TIP *tip_ref = &cm->tip_ref; |
| MV this_mv[2]; |
| tip_get_mv_projection(&this_mv[0], *mv, tip_ref->ref_frames_offset_sf[0]); |
| tip_get_mv_projection(&this_mv[1], *mv, tip_ref->ref_frames_offset_sf[1]); |
| |
| const int dist_to_top_edge = -GET_MV_SUBPEL(mi_y); |
| const int dist_to_bottom_edge = GET_MV_SUBPEL(frame_h - bh - mi_y); |
| const int dist_to_left_edge = -GET_MV_SUBPEL(mi_x); |
| const int dist_to_right_edge = GET_MV_SUBPEL(frame_w - bw - mi_x); |
| |
| MV temp_mv; |
| temp_mv = tip_clamp_tip_mv_to_umv_border_sb( |
| &this_mv[0], bw, bh, 0, 0, dist_to_left_edge, dist_to_right_edge, |
| dist_to_top_edge, dist_to_bottom_edge); |
| if (temp_mv.row != this_mv[0].row || temp_mv.col != this_mv[0].col) { |
| return 1; |
| } |
| |
| temp_mv = tip_clamp_tip_mv_to_umv_border_sb( |
| &this_mv[1], bw, bh, 0, 0, dist_to_left_edge, dist_to_right_edge, |
| dist_to_top_edge, dist_to_bottom_edge); |
| if (temp_mv.row != this_mv[1].row || temp_mv.col != this_mv[1].col) { |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| static void tip_motion_field_within_frame(AV1_COMMON *cm) { |
| TPL_MV_REF *tpl_mvs_base = cm->tpl_mvs; |
| const int mvs_rows = |
| ROUND_POWER_OF_TWO(cm->mi_params.mi_rows, TMVP_SHIFT_BITS); |
| const int mvs_cols = |
| ROUND_POWER_OF_TWO(cm->mi_params.mi_cols, TMVP_SHIFT_BITS); |
| const int mvs_stride = mvs_cols; |
| assert(mvs_rows * mvs_stride <= cm->tpl_mvs_mem_size); |
| av1_zero_array(cm->tip_ref.mf_need_clamp, mvs_rows * mvs_stride); |
| |
| const int width = (mvs_cols << TMVP_MI_SZ_LOG2); |
| const int height = (mvs_rows << TMVP_MI_SZ_LOG2); |
| |
| int *mf_need_clamp = cm->tip_ref.mf_need_clamp; |
| for (int i = 0; i < mvs_rows; i++) { |
| for (int j = 0; j < mvs_cols; j++) { |
| const int cur_pos = i * mvs_stride + j; |
| if (tpl_mvs_base[cur_pos].mfmv0.as_int != INVALID_MV && |
| tpl_mvs_base[cur_pos].mfmv0.as_int != 0) { |
| MV this_mv; |
| this_mv.row = tpl_mvs_base[cur_pos].mfmv0.as_mv.row; |
| this_mv.col = tpl_mvs_base[cur_pos].mfmv0.as_mv.col; |
| |
| const int tpl_row = i << TMVP_MI_SZ_LOG2; |
| const int tpl_col = j << TMVP_MI_SZ_LOG2; |
| |
| mf_need_clamp[cur_pos] = |
| tip_check_motion_field(cm, &this_mv, tpl_col, tpl_row, TMVP_MI_SIZE, |
| TMVP_MI_SIZE, width, height); |
| } else { |
| tpl_mvs_base[cur_pos].mfmv0.as_int = 0; |
| } |
| } |
| } |
| } |
| |
| static void tip_check_enable_tip_mode(AV1_COMMON *cm) { |
| const TPL_MV_REF *tpl_mvs_base = cm->tpl_mvs; |
| const int mvs_rows = |
| ROUND_POWER_OF_TWO(cm->mi_params.mi_rows, TMVP_SHIFT_BITS); |
| const int mvs_cols = |
| ROUND_POWER_OF_TWO(cm->mi_params.mi_cols, TMVP_SHIFT_BITS); |
| const int mvs_stride = mvs_cols; |
| |
| int count = 0; |
| for (int blk_row = 0; blk_row < mvs_rows; ++blk_row) { |
| for (int blk_col = 0; blk_col < mvs_cols; ++blk_col) { |
| const int tpl_offset = blk_row * mvs_stride + blk_col; |
| const TPL_MV_REF *tpl_mvs = tpl_mvs_base + tpl_offset; |
| if (tpl_mvs->mfmv0.as_int != INVALID_MV) { |
| ++count; |
| } |
| } |
| } |
| |
| // Percentage of number of blocks with available motion field |
| const int percent = (count * 100) / (mvs_rows * mvs_cols); |
| if (percent < TIP_ENABLE_COUNT_THRESHOLD) { |
| cm->features.tip_frame_mode = TIP_FRAME_DISABLED; |
| } else { |
| cm->features.tip_frame_mode = TIP_FRAME_AS_REF; |
| } |
| } |
| |
| static void tip_config_tip_parameter(AV1_COMMON *cm, int check_tip_threshold) { |
| TIP *tip_ref = &cm->tip_ref; |
| if (cm->current_frame.frame_type == KEY_FRAME || |
| cm->current_frame.frame_type == INTRA_ONLY_FRAME || |
| cm->current_frame.frame_type == S_FRAME) { |
| cm->features.tip_frame_mode = TIP_FRAME_DISABLED; |
| tip_ref->ref_frame[0] = NONE_FRAME; |
| tip_ref->ref_frame[1] = NONE_FRAME; |
| return; |
| } |
| |
| const OrderHintInfo *const order_hint_info = &cm->seq_params.order_hint_info; |
| const int cur_order_hint = cm->cur_frame->order_hint; |
| |
| MV_REFERENCE_FRAME nearest_rf[2] = { tip_ref->ref_frame[0], |
| tip_ref->ref_frame[1] }; |
| |
| if (nearest_rf[0] != NONE_FRAME && nearest_rf[1] != NONE_FRAME && |
| (is_ref_motion_field_eligible(cm, get_ref_frame_buf(cm, nearest_rf[0])) || |
| is_ref_motion_field_eligible(cm, |
| get_ref_frame_buf(cm, nearest_rf[1])))) { |
| if (check_tip_threshold) { |
| tip_check_enable_tip_mode(cm); |
| } |
| |
| if (cm->features.tip_frame_mode) { |
| cm->features.allow_tip_hole_fill = cm->seq_params.enable_tip_hole_fill; |
| RefCntBuffer *ref0_frame_buf = get_ref_frame_buf(cm, nearest_rf[0]); |
| const int ref0_frame_order_hint = ref0_frame_buf->order_hint; |
| const int cur_to_ref0_offset = get_relative_dist( |
| order_hint_info, cur_order_hint, ref0_frame_order_hint); |
| |
| RefCntBuffer *ref1_frame_buf = get_ref_frame_buf(cm, nearest_rf[1]); |
| const int ref1_frame_order_hint = ref1_frame_buf->order_hint; |
| const int cur_to_ref1_offset = get_relative_dist( |
| order_hint_info, cur_order_hint, ref1_frame_order_hint); |
| |
| const int ref_frames_offset = get_relative_dist( |
| order_hint_info, ref1_frame_order_hint, ref0_frame_order_hint); |
| tip_ref->ref_frame_buffer[0] = ref0_frame_buf; |
| tip_ref->ref_frame_buffer[1] = ref1_frame_buf; |
| tip_ref->ref_scale_factor[0] = |
| get_ref_scale_factors_const(cm, nearest_rf[0]); |
| tip_ref->ref_scale_factor[1] = |
| get_ref_scale_factors_const(cm, nearest_rf[1]); |
| tip_ref->ref_frames_offset_sf[0] = |
| tip_derive_scale_factor(cur_to_ref0_offset, ref_frames_offset); |
| tip_ref->ref_frames_offset_sf[1] = |
| tip_derive_scale_factor(cur_to_ref1_offset, ref_frames_offset); |
| tip_ref->ref_frames_offset = ref_frames_offset; |
| tip_ref->ref_offset[0] = cur_to_ref0_offset; |
| tip_ref->ref_offset[1] = cur_to_ref1_offset; |
| tip_ref->ref_order_hint[0] = ref0_frame_order_hint; |
| tip_ref->ref_order_hint[1] = ref1_frame_order_hint; |
| } |
| } else { |
| cm->features.tip_frame_mode = TIP_FRAME_DISABLED; |
| cm->features.allow_tip_hole_fill = false; |
| tip_ref->ref_frame[0] = NONE_FRAME; |
| tip_ref->ref_frame[1] = NONE_FRAME; |
| } |
| } |
| |
| void av1_setup_tip_motion_field(AV1_COMMON *cm, int check_tip_threshold) { |
| tip_config_tip_parameter(cm, check_tip_threshold); |
| if (cm->features.tip_frame_mode) { |
| tip_temporal_scale_motion_field(cm, cm->tip_ref.ref_frames_offset); |
| if (cm->features.allow_tip_hole_fill) { |
| tip_fill_motion_field_holes(cm); |
| tip_blk_average_filter_mv(cm); |
| } |
| tip_motion_field_within_frame(cm); |
| } |
| } |
| |
| static AOM_INLINE void tip_highbd_convolve_2d_facade_compound( |
| const uint16_t *src, int src_stride, uint16_t *dst, int dst_stride, |
| const int w, const int h, const InterpFilterParams *interp_filters[2], |
| SubpelParams *subpel_params, ConvolveParams *conv_params, int bd) { |
| const int subpel_x_qn = subpel_params->subpel_x; |
| const int subpel_y_qn = subpel_params->subpel_y; |
| if (subpel_x_qn && subpel_y_qn) { |
| assert(subpel_x_qn && subpel_y_qn); |
| av1_highbd_dist_wtd_convolve_2d(src, src_stride, dst, dst_stride, w, h, |
| interp_filters[0], interp_filters[1], |
| subpel_x_qn, subpel_y_qn, conv_params, bd); |
| } else if (subpel_x_qn && !subpel_y_qn) { |
| av1_highbd_dist_wtd_convolve_x(src, src_stride, dst, dst_stride, w, h, |
| interp_filters[0], subpel_x_qn, conv_params, |
| bd); |
| } else if (!subpel_x_qn && subpel_y_qn) { |
| av1_highbd_dist_wtd_convolve_y(src, src_stride, dst, dst_stride, w, h, |
| interp_filters[1], subpel_y_qn, conv_params, |
| bd); |
| } else { |
| av1_highbd_dist_wtd_convolve_2d_copy(src, src_stride, dst, dst_stride, w, h, |
| conv_params, bd); |
| } |
| } |
| |
| static AOM_INLINE void tip_highbd_inter_predictor( |
| const uint16_t *src, int src_stride, uint16_t *dst, int dst_stride, |
| SubpelParams *subpel_params, int w, int h, ConvolveParams *conv_params, |
| const InterpFilterParams *interp_filters[2], int bd) { |
| assert(conv_params->do_average == 0 || conv_params->do_average == 1); |
| const int is_scaled = has_scale(subpel_params->xs, subpel_params->ys); |
| assert(conv_params->dst != NULL); |
| if (is_scaled) { |
| av1_highbd_convolve_2d_scale( |
| src, src_stride, dst, dst_stride, w, h, interp_filters[0], |
| interp_filters[1], subpel_params->subpel_x, subpel_params->xs, |
| subpel_params->subpel_y, subpel_params->ys, conv_params, bd); |
| } else { |
| revert_scale_extra_bits(subpel_params); |
| tip_highbd_convolve_2d_facade_compound(src, src_stride, dst, dst_stride, w, |
| h, interp_filters, subpel_params, |
| conv_params, bd); |
| } |
| } |
| |
| static AOM_INLINE void tip_build_one_inter_predictor( |
| uint16_t *dst, int dst_stride, const MV *const src_mv, |
| InterPredParams *inter_pred_params, MACROBLOCKD *xd, int mi_x, int mi_y, |
| int ref, uint16_t **mc_buf, CalcSubpelParamsFunc calc_subpel_params_func) { |
| SubpelParams subpel_params; |
| uint16_t *src; |
| int src_stride; |
| calc_subpel_params_func(src_mv, inter_pred_params, xd, mi_x, mi_y, ref, |
| #if CONFIG_OPTFLOW_REFINEMENT |
| 0, |
| #endif // CONFIG_OPTFLOW_REFINEMENT |
| mc_buf, &src, &subpel_params, &src_stride); |
| |
| tip_highbd_inter_predictor( |
| src, src_stride, dst, dst_stride, &subpel_params, |
| inter_pred_params->block_width, inter_pred_params->block_height, |
| &inter_pred_params->conv_params, inter_pred_params->interp_filter_params, |
| inter_pred_params->bit_depth); |
| } |
| |
| #if CONFIG_OPTFLOW_ON_TIP |
| #define MAKE_BFP_SAD_WRAPPER_COMMON(fnname) \ |
| static unsigned int fnname##_8(const uint16_t *src_ptr, int source_stride, \ |
| const uint16_t *ref_ptr, int ref_stride) { \ |
| return fnname(src_ptr, source_stride, ref_ptr, ref_stride); \ |
| } \ |
| static unsigned int fnname##_10(const uint16_t *src_ptr, int source_stride, \ |
| const uint16_t *ref_ptr, int ref_stride) { \ |
| return fnname(src_ptr, source_stride, ref_ptr, ref_stride) >> 2; \ |
| } \ |
| static unsigned int fnname##_12(const uint16_t *src_ptr, int source_stride, \ |
| const uint16_t *ref_ptr, int ref_stride) { \ |
| return fnname(src_ptr, source_stride, ref_ptr, ref_stride) >> 4; \ |
| } |
| |
| MAKE_BFP_SAD_WRAPPER_COMMON(aom_highbd_sad8x8) |
| |
| // Get the proper sad calculation function for an 8x8 block |
| static unsigned int get_highbd_sad_8X8(const uint16_t *src_ptr, |
| int source_stride, |
| const uint16_t *ref_ptr, int ref_stride, |
| int bd) { |
| if (bd == 8) { |
| return aom_highbd_sad8x8_8(src_ptr, source_stride, ref_ptr, ref_stride); |
| } else if (bd == 10) { |
| return aom_highbd_sad8x8_10(src_ptr, source_stride, ref_ptr, ref_stride); |
| } else if (bd == 12) { |
| return aom_highbd_sad8x8_12(src_ptr, source_stride, ref_ptr, ref_stride); |
| } else { |
| assert(0); |
| return 0; |
| } |
| } |
| |
| // Build an 8x8 block in the TIP frame |
| static AOM_INLINE void tip_build_inter_predictors_8x8( |
| const AV1_COMMON *cm, MACROBLOCKD *xd, int plane, TIP_PLANE *tip_plane, |
| const MV mv[2], int mi_x, int mi_y, uint16_t **mc_buf, |
| CONV_BUF_TYPE *tmp_conv_dst, CalcSubpelParamsFunc calc_subpel_params_func, |
| uint16_t *dst, int dst_stride) { |
| // TODO(any): currently this only works for y plane |
| assert(plane == 0); |
| |
| int bw = 8; |
| int bh = 8; |
| |
| TIP_PLANE *const tip = &tip_plane[plane]; |
| |
| const int bd = cm->seq_params.bit_depth; |
| |
| const int ss_x = plane ? cm->seq_params.subsampling_x : 0; |
| const int ss_y = plane ? cm->seq_params.subsampling_y : 0; |
| const int comp_pixel_x = (mi_x >> ss_x); |
| const int comp_pixel_y = (mi_y >> ss_y); |
| const int comp_bw = bw >> ss_x; |
| const int comp_bh = bh >> ss_y; |
| |
| MB_MODE_INFO *mbmi = aom_calloc(1, sizeof(*mbmi)); |
| |
| int_mv mv_refined[2 * 4]; |
| |
| CONV_BUF_TYPE *org_buf = xd->tmp_conv_dst; |
| xd->tmp_conv_dst = tmp_conv_dst; |
| |
| mbmi->mv[0].as_mv = mv[0]; |
| mbmi->mv[1].as_mv = mv[1]; |
| mbmi->ref_frame[0] = TIP_FRAME; |
| mbmi->ref_frame[1] = NONE_FRAME; |
| mbmi->interp_fltr = EIGHTTAP_REGULAR; |
| mbmi->use_intrabc[xd->tree_type == CHROMA_PART] = 0; |
| mbmi->use_intrabc[0] = 0; |
| mbmi->motion_mode = SIMPLE_TRANSLATION; |
| mbmi->sb_type[PLANE_TYPE_Y] = BLOCK_8X8; |
| mbmi->interinter_comp.type = COMPOUND_AVERAGE; |
| |
| // Arrays to hold optical flow offsets. |
| int vx0[4] = { 0 }; |
| int vx1[4] = { 0 }; |
| int vy0[4] = { 0 }; |
| int vy1[4] = { 0 }; |
| |
| // Pointers to gradient and dst buffers |
| int16_t *gx0 = cm->gx0, *gy0 = cm->gy0, *gx1 = cm->gx1, *gy1 = cm->gy1; |
| uint16_t *dst0 = NULL, *dst1 = NULL; |
| |
| dst0 = cm->dst0_16_tip; |
| dst1 = cm->dst1_16_tip; |
| |
| int do_opfl = (is_opfl_refine_allowed(cm, mbmi) && plane == 0); |
| |
| const unsigned int sad_thres = |
| cm->features.tip_frame_mode == TIP_FRAME_AS_OUTPUT ? 15 : 6; |
| |
| const int use_4x4 = 0; |
| if (do_opfl) { |
| InterPredParams params0, params1; |
| av1_opfl_build_inter_predictor(cm, xd, plane, mbmi, bw, bh, mi_x, mi_y, |
| mc_buf, ¶ms0, calc_subpel_params_func, 0, |
| dst0); |
| av1_opfl_build_inter_predictor(cm, xd, plane, mbmi, bw, bh, mi_x, mi_y, |
| mc_buf, ¶ms1, calc_subpel_params_func, 1, |
| dst1); |
| const unsigned int sad = get_highbd_sad_8X8(dst0, bw, dst1, bw, bd); |
| |
| if (sad < sad_thres) { |
| do_opfl = 0; |
| } |
| } |
| |
| if (do_opfl) { |
| // Initialize refined mv |
| const MV mv0 = mv[0]; |
| const MV mv1 = mv[1]; |
| for (int mvi = 0; mvi < 4; mvi++) { |
| mv_refined[mvi * 2].as_mv = mv0; |
| mv_refined[mvi * 2 + 1].as_mv = mv1; |
| } |
| // Refine MV using optical flow. The final output MV will be in 1/16 |
| // precision. |
| av1_get_optflow_based_mv_highbd(cm, xd, plane, mbmi, mv_refined, bw, bh, |
| mi_x, mi_y, mc_buf, calc_subpel_params_func, |
| gx0, gy0, gx1, gy1, vx0, vy0, vx1, vy1, |
| dst0, dst1, 0, use_4x4); |
| } |
| |
| for (int ref = 0; ref < 2; ++ref) { |
| const struct scale_factors *const sf = cm->tip_ref.ref_scale_factor[ref]; |
| struct buf_2d *const pred_buf = &tip->pred[ref]; |
| |
| InterPredParams inter_pred_params; |
| av1_init_inter_params(&inter_pred_params, comp_bw, comp_bh, comp_pixel_y, |
| comp_pixel_x, ss_x, ss_y, bd, 0, sf, pred_buf, |
| MULTITAP_SHARP); |
| |
| inter_pred_params.comp_mode = UNIFORM_COMP; |
| |
| const int width = (cm->mi_params.mi_cols << MI_SIZE_LOG2); |
| const int height = (cm->mi_params.mi_rows << MI_SIZE_LOG2); |
| inter_pred_params.dist_to_top_edge = -GET_MV_SUBPEL(mi_y); |
| inter_pred_params.dist_to_bottom_edge = GET_MV_SUBPEL(height - bh - mi_y); |
| inter_pred_params.dist_to_left_edge = -GET_MV_SUBPEL(mi_x); |
| inter_pred_params.dist_to_right_edge = GET_MV_SUBPEL(width - bw - mi_x); |
| |
| inter_pred_params.conv_params = |
| get_conv_params_no_round(ref, plane, tmp_conv_dst, MAX_SB_SIZE, 1, bd); |
| |
| if (do_opfl) { |
| av1_opfl_rebuild_inter_predictor( |
| dst, dst_stride, plane, mv_refined, &inter_pred_params, xd, mi_x, |
| mi_y, ref, mc_buf, calc_subpel_params_func, use_4x4); |
| } else { |
| tip_build_one_inter_predictor(dst, dst_stride, &mv[ref], |
| &inter_pred_params, xd, mi_x, mi_y, ref, |
| mc_buf, calc_subpel_params_func); |
| } |
| } |
| |
| xd->tmp_conv_dst = org_buf; |
| aom_free(mbmi); |
| } |
| #endif // CONFIG_OPTFLOW_ON_TIP |
| |
| static AOM_INLINE void tip_build_inter_predictors_8x8_and_bigger( |
| const AV1_COMMON *cm, MACROBLOCKD *xd, int plane, TIP_PLANE *tip_plane, |
| const MV mv[2], int bw, int bh, int mi_x, int mi_y, uint16_t **mc_buf, |
| CONV_BUF_TYPE *tmp_conv_dst, CalcSubpelParamsFunc calc_subpel_params_func) { |
| TIP_PLANE *const tip = &tip_plane[plane]; |
| struct buf_2d *const dst_buf = &tip->dst; |
| uint16_t *const dst = dst_buf->buf; |
| |
| #if CONFIG_OPTFLOW_ON_TIP |
| int dst_stride = dst_buf->stride; |
| if (plane == 0 && cm->features.use_optflow_tip) { |
| if (bw != 8 || bh != 8) { |
| for (int h = 0; h < bh; h += 8) { |
| for (int w = 0; w < bw; w += 8) { |
| dst_buf->buf = dst + h * dst_stride + w; |
| tip_build_inter_predictors_8x8_and_bigger( |
| cm, xd, plane, tip_plane, mv, 8, 8, mi_x + w, mi_y + h, mc_buf, |
| tmp_conv_dst, calc_subpel_params_func); |
| } |
| } |
| dst_buf->buf = dst; |
| return; |
| } |
| tip_build_inter_predictors_8x8(cm, xd, plane, tip_plane, mv, mi_x, mi_y, |
| mc_buf, tmp_conv_dst, |
| calc_subpel_params_func, dst, dst_stride); |
| return; |
| } |
| #endif // CONFIG_OPTFLOW_ON_TIP |
| |
| const int bd = cm->seq_params.bit_depth; |
| |
| const int ss_x = plane ? cm->seq_params.subsampling_x : 0; |
| const int ss_y = plane ? cm->seq_params.subsampling_y : 0; |
| const int comp_pixel_x = (mi_x >> ss_x); |
| const int comp_pixel_y = (mi_y >> ss_y); |
| const int comp_bw = bw >> ss_x; |
| const int comp_bh = bh >> ss_y; |
| for (int ref = 0; ref < 2; ++ref) { |
| const struct scale_factors *const sf = cm->tip_ref.ref_scale_factor[ref]; |
| struct buf_2d *const pred_buf = &tip->pred[ref]; |
| |
| InterPredParams inter_pred_params; |
| av1_init_inter_params(&inter_pred_params, comp_bw, comp_bh, comp_pixel_y, |
| comp_pixel_x, ss_x, ss_y, bd, 0, sf, pred_buf, |
| MULTITAP_SHARP); |
| |
| inter_pred_params.comp_mode = UNIFORM_COMP; |
| |
| const int width = (cm->mi_params.mi_cols << MI_SIZE_LOG2); |
| const int height = (cm->mi_params.mi_rows << MI_SIZE_LOG2); |
| inter_pred_params.dist_to_top_edge = -GET_MV_SUBPEL(mi_y); |
| inter_pred_params.dist_to_bottom_edge = GET_MV_SUBPEL(height - bh - mi_y); |
| inter_pred_params.dist_to_left_edge = -GET_MV_SUBPEL(mi_x); |
| inter_pred_params.dist_to_right_edge = GET_MV_SUBPEL(width - bw - mi_x); |
| |
| inter_pred_params.conv_params = |
| get_conv_params_no_round(ref, plane, tmp_conv_dst, MAX_SB_SIZE, 1, bd); |
| |
| tip_build_one_inter_predictor(dst, dst_buf->stride, &mv[ref], |
| &inter_pred_params, xd, mi_x, mi_y, ref, |
| mc_buf, calc_subpel_params_func); |
| } |
| } |
| |
| static AOM_INLINE void tip_component_build_inter_predictors( |
| const AV1_COMMON *cm, MACROBLOCKD *xd, int plane, TIP_PLANE *tip_plane, |
| const MV mv[2], int bw, int bh, int mi_x, int mi_y, uint16_t **mc_buf, |
| CONV_BUF_TYPE *tmp_conv_dst, CalcSubpelParamsFunc calc_subpel_params_func) { |
| tip_build_inter_predictors_8x8_and_bigger( |
| cm, xd, plane, tip_plane, mv, bw, bh, mi_x, mi_y, mc_buf, tmp_conv_dst, |
| calc_subpel_params_func); |
| } |
| |
| static INLINE void tip_setup_pred_plane(struct buf_2d *dst, uint16_t *src, |
| int width, int height, int stride, |
| int tpl_row, int tpl_col, |
| const struct scale_factors *scale, |
| int subsampling_x, int subsampling_y) { |
| const int x = tpl_col >> subsampling_x; |
| const int y = tpl_row >> subsampling_y; |
| dst->buf = src + scaled_buffer_offset(x, y, stride, scale); |
| dst->buf0 = src; |
| dst->width = width; |
| dst->height = height; |
| dst->stride = stride; |
| } |
| |
| static AOM_INLINE void tip_component_setup_pred_planes(AV1_COMMON *const cm, |
| const int plane, |
| const int tpl_row, |
| const int tpl_col) { |
| TIP *tip_ref = &cm->tip_ref; |
| for (int ref = 0; ref < 2; ++ref) { |
| const YV12_BUFFER_CONFIG *ref_buf = &tip_ref->ref_frame_buffer[ref]->buf; |
| TIP_PLANE *const pd = &tip_ref->tip_plane[plane]; |
| int is_uv = 0; |
| int subsampling_x = 0; |
| int subsampling_y = 0; |
| if (plane > 0) { |
| is_uv = 1; |
| subsampling_x = cm->seq_params.subsampling_x; |
| subsampling_y = cm->seq_params.subsampling_y; |
| } |
| tip_setup_pred_plane( |
| &pd->pred[ref], ref_buf->buffers[plane], ref_buf->crop_widths[is_uv], |
| ref_buf->crop_heights[is_uv], ref_buf->strides[is_uv], tpl_row, tpl_col, |
| tip_ref->ref_scale_factor[ref], subsampling_x, subsampling_y); |
| } |
| } |
| |
| static AOM_INLINE void tip_component_setup_dst_planes(AV1_COMMON *const cm, |
| const int plane, |
| const int tpl_row, |
| const int tpl_col) { |
| const YV12_BUFFER_CONFIG *src = &cm->tip_ref.tip_frame->buf; |
| TIP_PLANE *const pd = &cm->tip_ref.tip_plane[plane]; |
| int is_uv = 0; |
| int subsampling_x = 0; |
| int subsampling_y = 0; |
| if (plane > 0) { |
| is_uv = 1; |
| subsampling_x = cm->seq_params.subsampling_x; |
| subsampling_y = cm->seq_params.subsampling_y; |
| } |
| tip_setup_pred_plane(&pd->dst, src->buffers[plane], src->crop_widths[is_uv], |
| src->crop_heights[is_uv], src->strides[is_uv], tpl_row, |
| tpl_col, NULL, subsampling_x, subsampling_y); |
| } |
| |
| static void tip_setup_tip_frame_plane( |
| AV1_COMMON *cm, MACROBLOCKD *xd, int plane, int blk_row_start, |
| int blk_col_start, int blk_row_end, int blk_col_end, int mvs_stride, |
| int unit_blk_size, int max_allow_blk_size, uint16_t **mc_buf, |
| CONV_BUF_TYPE *tmp_conv_dst, CalcSubpelParamsFunc calc_subpel_params_func) { |
| TIP *tip_ref = &cm->tip_ref; |
| const TPL_MV_REF *tpl_mvs_base = cm->tpl_mvs; |
| |
| MV zero_mv[2]; |
| memset(zero_mv, 0, sizeof(zero_mv)); |
| |
| const int step = (unit_blk_size >> TMVP_MI_SZ_LOG2); |
| for (int blk_row = blk_row_start; blk_row < blk_row_end; blk_row += step) { |
| for (int blk_col = blk_col_start; blk_col < blk_col_end; blk_col += step) { |
| const int tpl_offset = blk_row * mvs_stride + blk_col; |
| const TPL_MV_REF *tpl_mvs = tpl_mvs_base + tpl_offset; |
| const int tpl_row = blk_row << TMVP_MI_SZ_LOG2; |
| const int tpl_col = blk_col << TMVP_MI_SZ_LOG2; |
| |
| int blk_width = unit_blk_size; |
| int blk_height = unit_blk_size; |
| int offset = step; |
| while (blk_col + offset < blk_col_end && blk_width < max_allow_blk_size && |
| tip_ref->mf_need_clamp[tpl_offset] == |
| tip_ref->mf_need_clamp[tpl_offset + offset] && |
| tpl_mvs->mfmv0.as_int == |
| tpl_mvs_base[tpl_offset + offset].mfmv0.as_int) { |
| blk_width += unit_blk_size; |
| offset += step; |
| } |
| blk_col += (offset - step); |
| |
| MV mv[2]; |
| if (tpl_mvs->mfmv0.as_int != 0) { |
| tip_get_mv_projection(&mv[0], tpl_mvs->mfmv0.as_mv, |
| tip_ref->ref_frames_offset_sf[0]); |
| tip_get_mv_projection(&mv[1], tpl_mvs->mfmv0.as_mv, |
| tip_ref->ref_frames_offset_sf[1]); |
| } else { |
| mv[0] = zero_mv[0]; |
| mv[1] = zero_mv[1]; |
| } |
| |
| tip_component_setup_pred_planes(cm, plane, tpl_row, tpl_col); |
| tip_component_setup_dst_planes(cm, plane, tpl_row, tpl_col); |
| tip_component_build_inter_predictors( |
| cm, xd, plane, tip_ref->tip_plane, mv, blk_width, blk_height, tpl_col, |
| tpl_row, mc_buf, tmp_conv_dst, calc_subpel_params_func); |
| } |
| } |
| } |
| |
| static AOM_INLINE void tip_setup_tip_frame_planes( |
| AV1_COMMON *cm, MACROBLOCKD *xd, int blk_row_start, int blk_col_start, |
| int blk_row_end, int blk_col_end, int mvs_stride, uint16_t **mc_buf, |
| CONV_BUF_TYPE *tmp_conv_dst, CalcSubpelParamsFunc calc_subpel_params_func) { |
| const int num_planes = av1_num_planes(cm); |
| for (int plane = 0; plane < num_planes; ++plane) { |
| if (plane == 0) { |
| tip_setup_tip_frame_plane(cm, xd, plane, blk_row_start, blk_col_start, |
| blk_row_end, blk_col_end, mvs_stride, |
| TMVP_MI_SIZE, MAX_BLOCK_SIZE_WITH_SAME_MV, |
| mc_buf, tmp_conv_dst, calc_subpel_params_func); |
| } else { |
| // CHROMA_MI_SIZE is the block size in luma unit for Chroma |
| // TIP interpolation, will convert to the step size in TMVP 8x8 unit |
| tip_setup_tip_frame_plane(cm, xd, plane, blk_row_start, blk_col_start, |
| blk_row_end, blk_col_end, mvs_stride, |
| CHROMA_MI_SIZE, MAX_BLOCK_SIZE_WITH_SAME_MV, |
| mc_buf, tmp_conv_dst, calc_subpel_params_func); |
| } |
| } |
| |
| aom_extend_frame_borders(&cm->tip_ref.tip_frame->buf, av1_num_planes(cm)); |
| } |
| |
| void av1_setup_tip_frame(AV1_COMMON *cm, MACROBLOCKD *xd, uint16_t **mc_buf, |
| CONV_BUF_TYPE *tmp_conv_dst, |
| CalcSubpelParamsFunc calc_subpel_params_func) { |
| const int mvs_rows = |
| ROUND_POWER_OF_TWO(cm->mi_params.mi_rows, TMVP_SHIFT_BITS); |
| const int mvs_cols = |
| ROUND_POWER_OF_TWO(cm->mi_params.mi_cols, TMVP_SHIFT_BITS); |
| tip_setup_tip_frame_planes(cm, xd, 0, 0, mvs_rows, mvs_cols, mvs_cols, mc_buf, |
| tmp_conv_dst, calc_subpel_params_func); |
| } |
| |
| static void tip_extend_plane_block_based_highbd( |
| uint16_t *const src, int src_stride, int width, int height, int extend_top, |
| int extend_left, int extend_bottom, int extend_right, int start_w, |
| int start_h, int blk_w, int blk_h) { |
| assert(src != NULL); |
| int i = 0; |
| |
| if (extend_left) { |
| // copy the left most columns out |
| uint16_t *src_ptr = src + start_h * src_stride; |
| uint16_t *dst_ptr = src_ptr - extend_left; |
| for (i = 0; i < blk_h; ++i) { |
| aom_memset16(dst_ptr, src_ptr[0], extend_left); |
| src_ptr += src_stride; |
| dst_ptr += src_stride; |
| } |
| } |
| |
| if (extend_right) { |
| // copy the right most columns out |
| uint16_t *src_ptr = src + start_h * src_stride + width - 1; |
| uint16_t *dst_ptr = src_ptr + 1; |
| for (i = 0; i < blk_h; ++i) { |
| aom_memset16(dst_ptr, src_ptr[0], extend_right); |
| src_ptr += src_stride; |
| dst_ptr += src_stride; |
| } |
| } |
| |
| if (extend_top) { |
| // copy the top lines into each line of the respective borders |
| uint16_t *src_ptr = src + start_w - extend_left; |
| uint16_t *dst_ptr = src_ptr - src_stride * extend_top; |
| const int extend_size = extend_left + extend_right + blk_w; |
| for (i = 0; i < extend_top; ++i) { |
| memcpy(dst_ptr, src_ptr, extend_size * sizeof(uint16_t)); |
| dst_ptr += src_stride; |
| } |
| } |
| |
| if (extend_bottom) { |
| // copy the bottom lines into each line of the respective borders |
| uint16_t *src_ptr = src + src_stride * (height - 1) + start_w - extend_left; |
| uint16_t *dst_ptr = src_ptr + src_stride; |
| const int extend_size = extend_left + extend_right + blk_w; |
| for (i = 0; i < extend_bottom; ++i) { |
| memcpy(dst_ptr, src_ptr, extend_size * sizeof(uint16_t)); |
| dst_ptr += src_stride; |
| } |
| } |
| } |
| |
| static void tip_extend_plane_border(AV1_COMMON *cm, int blk_row_start, |
| int blk_col_start, int blk_height, |
| int blk_width) { |
| YV12_BUFFER_CONFIG *tip_buf = &cm->tip_ref.tip_frame->buf; |
| const int width = tip_buf->y_width; |
| const int height = tip_buf->y_height; |
| |
| int top_border = 0; |
| int bottom_border = 0; |
| int left_border = 0; |
| int right_border = 0; |
| if (blk_row_start == 0) { |
| top_border = 1; |
| } |
| |
| if (blk_row_start + blk_height >= height) { |
| bottom_border = 1; |
| blk_height = height - blk_row_start; |
| } |
| |
| if (blk_col_start == 0) { |
| left_border = 1; |
| } |
| |
| if (blk_col_start + blk_width >= width) { |
| right_border = 1; |
| blk_width = width - blk_col_start; |
| } |
| |
| if (top_border || bottom_border || left_border || right_border) { |
| const int subsampling_x = cm->seq_params.subsampling_x; |
| const int subsampling_y = cm->seq_params.subsampling_y; |
| const int y_stride = tip_buf->y_stride; |
| const int uv_stride = tip_buf->uv_stride; |
| const int extend_border = tip_buf->border; |
| const int y_width = tip_buf->y_crop_width; |
| const int y_height = tip_buf->y_crop_height; |
| const int uv_width = tip_buf->uv_crop_width; |
| const int uv_height = tip_buf->uv_crop_height; |
| uint16_t *y_dst = tip_buf->y_buffer; |
| uint16_t *u_dst = tip_buf->u_buffer; |
| uint16_t *v_dst = tip_buf->v_buffer; |
| |
| const int extend_top = top_border ? extend_border : 0; |
| const int extend_bottom = bottom_border ? extend_border : 0; |
| const int extend_left = left_border ? extend_border : 0; |
| const int extend_right = right_border ? extend_border : 0; |
| |
| const int uv_extend_top = extend_top >> subsampling_y; |
| const int uv_extend_bottom = extend_bottom >> subsampling_y; |
| const int uv_extend_left = extend_left >> subsampling_x; |
| const int uv_extend_right = extend_right >> subsampling_x; |
| |
| tip_extend_plane_block_based_highbd(y_dst, y_stride, y_width, y_height, |
| extend_top, extend_left, extend_bottom, |
| extend_right, blk_col_start, |
| blk_row_start, blk_width, blk_height); |
| |
| blk_col_start >>= subsampling_x; |
| blk_row_start >>= subsampling_y; |
| blk_width >>= subsampling_x; |
| blk_height >>= subsampling_y; |
| tip_extend_plane_block_based_highbd( |
| u_dst, uv_stride, uv_width, uv_height, uv_extend_top, uv_extend_left, |
| uv_extend_bottom, uv_extend_right, blk_col_start, blk_row_start, |
| blk_width, blk_height); |
| tip_extend_plane_block_based_highbd( |
| v_dst, uv_stride, uv_width, uv_height, uv_extend_top, uv_extend_left, |
| uv_extend_bottom, uv_extend_right, blk_col_start, blk_row_start, |
| blk_width, blk_height); |
| } |
| } |
| |
| static void tip_setup_tip_plane_blocks( |
| AV1_COMMON *cm, MACROBLOCKD *xd, int plane, int blk_row_start, |
| int blk_col_start, int blk_row_end, int blk_col_end, int mvs_stride, |
| int unit_blk_size, int max_allow_blk_size, uint16_t **mc_buf, |
| CONV_BUF_TYPE *tmp_conv_dst, CalcSubpelParamsFunc calc_subpel_params_func) { |
| TIP *tip_ref = &cm->tip_ref; |
| const TPL_MV_REF *tpl_mvs_base = cm->tpl_mvs; |
| |
| MV zero_mv[2]; |
| memset(zero_mv, 0, sizeof(zero_mv)); |
| |
| const int step = (unit_blk_size >> TMVP_MI_SZ_LOG2); |
| for (int blk_row = blk_row_start; blk_row < blk_row_end; blk_row += step) { |
| for (int blk_col = blk_col_start; blk_col < blk_col_end; blk_col += step) { |
| const int tpl_offset = blk_row * mvs_stride + blk_col; |
| if (tip_ref->available_flag[tpl_offset]) continue; |
| const TPL_MV_REF *tpl_mvs = tpl_mvs_base + tpl_offset; |
| const int tpl_row = blk_row << TMVP_MI_SZ_LOG2; |
| const int tpl_col = blk_col << TMVP_MI_SZ_LOG2; |
| |
| int blk_width = unit_blk_size; |
| int blk_height = unit_blk_size; |
| int offset = step; |
| while (blk_col + offset < blk_col_end && blk_width < max_allow_blk_size && |
| !tip_ref->available_flag[tpl_offset + offset] && |
| tip_ref->mf_need_clamp[tpl_offset] == |
| tip_ref->mf_need_clamp[tpl_offset + offset] && |
| tpl_mvs->mfmv0.as_int == |
| tpl_mvs_base[tpl_offset + offset].mfmv0.as_int) { |
| blk_width += unit_blk_size; |
| offset += step; |
| } |
| blk_col += (offset - step); |
| |
| MV mv[2]; |
| if (tpl_mvs->mfmv0.as_int != 0) { |
| tip_get_mv_projection(&mv[0], tpl_mvs->mfmv0.as_mv, |
| tip_ref->ref_frames_offset_sf[0]); |
| tip_get_mv_projection(&mv[1], tpl_mvs->mfmv0.as_mv, |
| tip_ref->ref_frames_offset_sf[1]); |
| } else { |
| mv[0] = zero_mv[0]; |
| mv[1] = zero_mv[1]; |
| } |
| |
| tip_component_setup_pred_planes(cm, plane, tpl_row, tpl_col); |
| tip_component_setup_dst_planes(cm, plane, tpl_row, tpl_col); |
| tip_component_build_inter_predictors( |
| cm, xd, plane, tip_ref->tip_plane, mv, blk_width, blk_height, tpl_col, |
| tpl_row, mc_buf, tmp_conv_dst, calc_subpel_params_func); |
| } |
| } |
| } |
| |
| static AOM_INLINE void tip_setup_tip_planes_blocks( |
| AV1_COMMON *cm, MACROBLOCKD *xd, int blk_row_start, int blk_col_start, |
| int blk_row_end, int blk_col_end, int mvs_stride, uint16_t **mc_buf, |
| CONV_BUF_TYPE *tmp_conv_dst, CalcSubpelParamsFunc calc_subpel_params_func) { |
| const int num_planes = av1_num_planes(cm); |
| for (int plane = 0; plane < num_planes; ++plane) { |
| if (plane == 0) { |
| tip_setup_tip_plane_blocks(cm, xd, plane, blk_row_start, blk_col_start, |
| blk_row_end, blk_col_end, mvs_stride, |
| TMVP_MI_SIZE, MAX_BLOCK_SIZE_WITH_SAME_MV, |
| mc_buf, tmp_conv_dst, calc_subpel_params_func); |
| } else { |
| // CHROMA_MI_SIZE is the block size in luma unit for Chroma |
| // TIP interpolation, will convert to the step size in TMVP 8x8 unit |
| tip_setup_tip_plane_blocks(cm, xd, plane, blk_row_start, blk_col_start, |
| blk_row_end, blk_col_end, mvs_stride, |
| CHROMA_MI_SIZE, MAX_BLOCK_SIZE_WITH_SAME_MV, |
| mc_buf, tmp_conv_dst, calc_subpel_params_func); |
| } |
| } |
| |
| const int step = (TMVP_MI_SIZE >> TMVP_MI_SZ_LOG2); |
| for (int blk_row = blk_row_start; blk_row < blk_row_end; blk_row += step) { |
| for (int blk_col = blk_col_start; blk_col < blk_col_end; blk_col += step) { |
| const int tpl_offset = blk_row * mvs_stride + blk_col; |
| cm->tip_ref.available_flag[tpl_offset] = 1; |
| } |
| } |
| } |
| |
| void av1_setup_tip_on_the_fly(AV1_COMMON *cm, MACROBLOCKD *xd, |
| int blk_row_start, int blk_col_start, |
| int blk_row_end, int blk_col_end, int mvs_stride, |
| uint16_t **mc_buf, CONV_BUF_TYPE *tmp_conv_dst, |
| CalcSubpelParamsFunc calc_subpel_params_func) { |
| tip_setup_tip_planes_blocks(cm, xd, blk_row_start, blk_col_start, blk_row_end, |
| blk_col_end, mvs_stride, mc_buf, tmp_conv_dst, |
| calc_subpel_params_func); |
| tip_extend_plane_border(cm, blk_row_start << TMVP_MI_SZ_LOG2, |
| blk_col_start << TMVP_MI_SZ_LOG2, |
| (blk_row_end - blk_row_start) << TMVP_MI_SZ_LOG2, |
| (blk_col_end - blk_col_start) << TMVP_MI_SZ_LOG2); |
| } |
| |
| void av1_copy_tip_frame_tmvp_mvs(const AV1_COMMON *const cm) { |
| MV_REF *frame_mvs = cm->cur_frame->mvs; |
| const TPL_MV_REF *tpl_mvs = cm->tpl_mvs; |
| const TIP *tip_ref = &cm->tip_ref; |
| const int mvs_rows = |
| ROUND_POWER_OF_TWO(cm->mi_params.mi_rows, TMVP_SHIFT_BITS); |
| const int mvs_cols = |
| ROUND_POWER_OF_TWO(cm->mi_params.mi_cols, TMVP_SHIFT_BITS); |
| const int mvs_stride = mvs_cols; |
| |
| for (int h = 0; h < mvs_rows; h++) { |
| MV_REF *mv = frame_mvs; |
| const TPL_MV_REF *tpl_mv = tpl_mvs; |
| for (int w = 0; w < mvs_cols; w++) { |
| mv->ref_frame[0] = NONE_FRAME; |
| mv->ref_frame[1] = NONE_FRAME; |
| mv->mv[0].as_int = 0; |
| mv->mv[1].as_int = 0; |
| if (tpl_mv->mfmv0.as_int != INVALID_MV) { |
| int_mv this_mv[2] = { { 0 } }; |
| tip_get_mv_projection(&this_mv[0].as_mv, tpl_mv->mfmv0.as_mv, |
| tip_ref->ref_frames_offset_sf[0]); |
| tip_get_mv_projection(&this_mv[1].as_mv, tpl_mv->mfmv0.as_mv, |
| tip_ref->ref_frames_offset_sf[1]); |
| |
| if ((abs(this_mv[0].as_mv.row) <= REFMVS_LIMIT) && |
| (abs(this_mv[0].as_mv.col) <= REFMVS_LIMIT)) { |
| mv->ref_frame[0] = tip_ref->ref_frame[0]; |
| mv->mv[0].as_int = this_mv[0].as_int; |
| } |
| |
| if ((abs(this_mv[1].as_mv.row) <= REFMVS_LIMIT) && |
| (abs(this_mv[1].as_mv.col) <= REFMVS_LIMIT)) { |
| mv->ref_frame[1] = tip_ref->ref_frame[1]; |
| mv->mv[1].as_int = this_mv[1].as_int; |
| } |
| } |
| mv++; |
| tpl_mv++; |
| } |
| frame_mvs += mvs_stride; |
| tpl_mvs += mvs_stride; |
| } |
| } |