Revert 0c15af0 and fix svc multi-layer test failures 1. Revert commit 0c15af0. The VBR changes in 2fed9c3 are now gated by mode!=REALTIME in the current code. 2. Fixed svc multi-layer test failures in the bug below. Bug: 491358676 Change-Id: I0da328c56d283f677a066de8e15b85e2ccabd27b
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c index 6182f1d..93d276f 100644 --- a/av1/encoder/encoder.c +++ b/av1/encoder/encoder.c
@@ -1537,6 +1537,7 @@ cm->current_frame_id = -1; cpi->tile_data = NULL; cpi->last_show_frame_buf = NULL; + cpi->ppi->p_rc.rtc_mode = (cpi->oxcf.mode == REALTIME); realloc_segmentation_maps(cpi); cpi->refresh_frame.alt_ref_frame = false;
diff --git a/av1/encoder/pass2_strategy.c b/av1/encoder/pass2_strategy.c index 3fdf9ba..93ca3e4 100644 --- a/av1/encoder/pass2_strategy.c +++ b/av1/encoder/pass2_strategy.c
@@ -604,10 +604,8 @@ // (zoom in). The range for this_frame_mv_in_out is -1.0 to +1.0. if (this_frame_mv_in_out > 0.0) { frame_boost += frame_boost * (this_frame_mv_in_out * 2.0); -#if !CONFIG_REALTIME_ONLY - if (!p_rc->accumulate_stats_stage) + if (!p_rc->accumulate_stats_stage && !p_rc->rtc_mode) max_boost += max_boost * (this_frame_mv_in_out * 2.0); -#endif } // In the extreme case the boost is halved. else { @@ -993,7 +991,6 @@ gf_group->bit_allocation[gf_group_size] = 0; } -#if CONFIG_REALTIME_ONLY // Returns true if KF group and GF group both are almost completely static. static inline int is_almost_static(double gf_zero_motion, int kf_zero_motion, int is_lap_enabled) { @@ -1008,7 +1005,6 @@ (kf_zero_motion >= STATIC_KF_GROUP_THRESH); } } -#endif #define ARF_ABS_ZOOM_THRESH 4.4 static inline int detect_gf_cut(AV1_COMP *cpi, int frame_index, int cur_start, @@ -1049,12 +1045,10 @@ // If almost totally static, we will not use the the max GF length later, // so we can continue for more frames. - if ((frame_index - cur_start) >= active_max_gf_interval + 1 -#if CONFIG_REALTIME_ONLY - && !is_almost_static(gf_stats->zero_motion_accumulator, - twopass->kf_zeromotion_pct, cpi->ppi->lap_enabled) -#endif - ) { + if ((frame_index - cur_start) >= active_max_gf_interval + 1 && + ((cpi->oxcf.mode != REALTIME) || + !is_almost_static(gf_stats->zero_motion_accumulator, + twopass->kf_zeromotion_pct, cpi->ppi->lap_enabled))) { return 1; } return 0; @@ -2421,52 +2415,53 @@ const AV1EncoderConfig *const oxcf = &cpi->oxcf; const RateControlCfg *const rc_cfg = &oxcf->rc_cfg; -#if !CONFIG_REALTIME_ONLY - TWO_PASS_FRAME stats_in_backup = cpi->twopass_frame; - int gfu_boost_sum = 0; - int gfu_count = 0; - int accumulate_i = 0; - p_rc->accumulate_stats_stage = true; - if (rc->frames_since_key == 0) { - for (int k = 0; k < MAX_NUM_GF_INTERVALS; k++) { - if (p_rc->gf_intervals[k] == 0) { - break; - } - - int new_i = p_rc->gf_intervals[k]; - int ext_len_new = new_i - (k == 0 ? is_intra_only : 0); - if (use_alt_ref) { - if (accumulate_i >= rc->frames_to_key) { + if (cpi->oxcf.mode != REALTIME) { + TWO_PASS_FRAME stats_in_backup = cpi->twopass_frame; + int gfu_boost_sum = 0; + int gfu_count = 0; + int accumulate_i = 0; + p_rc->accumulate_stats_stage = true; + if (rc->frames_since_key == 0) { + for (int k = 0; k < MAX_NUM_GF_INTERVALS; k++) { + if (p_rc->gf_intervals[k] == 0) { break; } - const int forward_frames = - (rc->frames_to_key - accumulate_i - new_i >= ext_len_new) - ? ext_len_new - : AOMMAX(0, rc->frames_to_key - accumulate_i - new_i); - if (k) { - cpi->twopass_frame.stats_in += new_i; - if (cpi->twopass_frame.stats_in >= - twopass->stats_buf_ctx->stats_in_end) { - cpi->twopass_frame.stats_in = twopass->stats_buf_ctx->stats_in_end; + + int new_i = p_rc->gf_intervals[k]; + int ext_len_new = new_i - (k == 0 ? is_intra_only : 0); + if (use_alt_ref) { + if (accumulate_i >= rc->frames_to_key) { + break; } + const int forward_frames = + (rc->frames_to_key - accumulate_i - new_i >= ext_len_new) + ? ext_len_new + : AOMMAX(0, rc->frames_to_key - accumulate_i - new_i); + if (k) { + cpi->twopass_frame.stats_in += new_i; + if (cpi->twopass_frame.stats_in >= + twopass->stats_buf_ctx->stats_in_end) { + cpi->twopass_frame.stats_in = + twopass->stats_buf_ctx->stats_in_end; + } + } + reset_fpf_position(&cpi->twopass_frame, cpi->twopass_frame.stats_in); + // Calculate the boost for alt ref. + int gfu_boost_tmp = av1_calc_arf_boost( + twopass, &cpi->twopass_frame, p_rc, frame_info, alt_offset, + forward_frames, ext_len_new, &p_rc->num_stats_used_for_gfu_boost, + &p_rc->num_stats_required_for_gfu_boost, cpi->ppi->lap_enabled); + gfu_boost_sum += gfu_boost_tmp; } - reset_fpf_position(&cpi->twopass_frame, cpi->twopass_frame.stats_in); - // Calculate the boost for alt ref. - int gfu_boost_tmp = av1_calc_arf_boost( - twopass, &cpi->twopass_frame, p_rc, frame_info, alt_offset, - forward_frames, ext_len_new, &p_rc->num_stats_used_for_gfu_boost, - &p_rc->num_stats_required_for_gfu_boost, cpi->ppi->lap_enabled); - gfu_boost_sum += gfu_boost_tmp; + gfu_count++; + accumulate_i += new_i; } - gfu_count++; - accumulate_i += new_i; + assert(gfu_count > 0); + p_rc->gfu_boost_average = gfu_boost_sum / gfu_count; } - assert(gfu_count > 0); - p_rc->gfu_boost_average = gfu_boost_sum / gfu_count; + cpi->twopass_frame = stats_in_backup; + p_rc->accumulate_stats_stage = false; } - cpi->twopass_frame = stats_in_backup; - p_rc->accumulate_stats_stage = false; -#endif int ext_len = i - is_intra_only; if (use_alt_ref) { @@ -2588,9 +2583,7 @@ RATE_CONTROL *const rc = &cpi->rc; PRIMARY_RATE_CONTROL *const p_rc = &cpi->ppi->p_rc; const AV1EncoderConfig *const oxcf = &cpi->oxcf; -#if CONFIG_REALTIME_ONLY TWO_PASS *const twopass = &cpi->ppi->twopass; -#endif FIRSTPASS_STATS next_frame; const FIRSTPASS_STATS *const start_pos = cpi->twopass_frame.stats_in; GF_GROUP *gf_group = &cpi->ppi->gf_group; @@ -2657,12 +2650,10 @@ if (can_disable_arf) { use_alt_ref = p_rc->use_arf_in_this_kf_group && (i < gf_cfg->lag_in_frames) && - (i >= MIN_GF_INTERVAL) -#if CONFIG_REALTIME_ONLY - && !is_almost_static(gf_stats.zero_motion_accumulator, - twopass->kf_zeromotion_pct, cpi->ppi->lap_enabled) -#endif - ; + (i >= MIN_GF_INTERVAL) && + ((cpi->oxcf.mode != REALTIME) || + !is_almost_static(gf_stats.zero_motion_accumulator, + twopass->kf_zeromotion_pct, cpi->ppi->lap_enabled)); } else { use_alt_ref = p_rc->use_arf_in_this_kf_group && (i < gf_cfg->lag_in_frames) && (i > 2);
diff --git a/av1/encoder/ratectrl.c b/av1/encoder/ratectrl.c index 45443d2..c22cfdf 100644 --- a/av1/encoder/ratectrl.c +++ b/av1/encoder/ratectrl.c
@@ -42,6 +42,7 @@ #define USE_UNRESTRICTED_Q_IN_CQ_MODE 0 #define RES_NUM 2 +#define MODE_NUM 2 // Max rate target for 1080P and below encodes under normal circumstances // (1920 * 1080 / (16 * 16)) * MAX_MB_RATE bits per MB @@ -70,42 +71,41 @@ } \ } while (0) -#define ASSIGN_MINQ_TABLE_2(bit_depth, name, res_idx) \ - do { \ - switch (bit_depth) { \ - case AOM_BITS_8: name = name##_8[res_idx]; break; \ - case AOM_BITS_10: name = name##_10[res_idx]; break; \ - case AOM_BITS_12: name = name##_12[res_idx]; break; \ - default: \ - assert(0 && \ - "bit_depth should be AOM_BITS_8, AOM_BITS_10" \ - " or AOM_BITS_12"); \ - name = NULL; \ - } \ +#define ASSIGN_MINQ_TABLE_2(bit_depth, name, res_idx, mode_idx) \ + do { \ + switch (bit_depth) { \ + case AOM_BITS_8: name = name##_8[mode_idx][res_idx]; break; \ + case AOM_BITS_10: name = name##_10[mode_idx][res_idx]; break; \ + case AOM_BITS_12: name = name##_12[mode_idx][res_idx]; break; \ + default: \ + assert(0 && \ + "bit_depth should be AOM_BITS_8, AOM_BITS_10" \ + " or AOM_BITS_12"); \ + name = NULL; \ + } \ } while (0) // Tables relating active max Q to active min Q -static int kf_low_motion_minq_8[RES_NUM][QINDEX_RANGE]; -static int kf_high_motion_minq_8[RES_NUM][QINDEX_RANGE]; -static int arfgf_low_motion_minq_8[RES_NUM][QINDEX_RANGE]; -static int arfgf_high_motion_minq_8[RES_NUM][QINDEX_RANGE]; -static int inter_minq_8[RES_NUM][QINDEX_RANGE]; +static int kf_low_motion_minq_8[MODE_NUM][RES_NUM][QINDEX_RANGE]; +static int kf_high_motion_minq_8[MODE_NUM][RES_NUM][QINDEX_RANGE]; +static int arfgf_low_motion_minq_8[MODE_NUM][RES_NUM][QINDEX_RANGE]; +static int arfgf_high_motion_minq_8[MODE_NUM][RES_NUM][QINDEX_RANGE]; +static int inter_minq_8[MODE_NUM][RES_NUM][QINDEX_RANGE]; static int rtc_minq_8[QINDEX_RANGE]; -static int kf_low_motion_minq_10[RES_NUM][QINDEX_RANGE]; -static int kf_high_motion_minq_10[RES_NUM][QINDEX_RANGE]; -static int arfgf_low_motion_minq_10[RES_NUM][QINDEX_RANGE]; -static int arfgf_high_motion_minq_10[RES_NUM][QINDEX_RANGE]; -static int inter_minq_10[RES_NUM][QINDEX_RANGE]; +static int kf_low_motion_minq_10[MODE_NUM][RES_NUM][QINDEX_RANGE]; +static int kf_high_motion_minq_10[MODE_NUM][RES_NUM][QINDEX_RANGE]; +static int arfgf_low_motion_minq_10[MODE_NUM][RES_NUM][QINDEX_RANGE]; +static int arfgf_high_motion_minq_10[MODE_NUM][RES_NUM][QINDEX_RANGE]; +static int inter_minq_10[MODE_NUM][RES_NUM][QINDEX_RANGE]; static int rtc_minq_10[QINDEX_RANGE]; -static int kf_low_motion_minq_12[RES_NUM][QINDEX_RANGE]; -static int kf_high_motion_minq_12[RES_NUM][QINDEX_RANGE]; -static int arfgf_low_motion_minq_12[RES_NUM][QINDEX_RANGE]; -static int arfgf_high_motion_minq_12[RES_NUM][QINDEX_RANGE]; -static int inter_minq_12[RES_NUM][QINDEX_RANGE]; +static int kf_low_motion_minq_12[MODE_NUM][RES_NUM][QINDEX_RANGE]; +static int kf_high_motion_minq_12[MODE_NUM][RES_NUM][QINDEX_RANGE]; +static int arfgf_low_motion_minq_12[MODE_NUM][RES_NUM][QINDEX_RANGE]; +static int arfgf_high_motion_minq_12[MODE_NUM][RES_NUM][QINDEX_RANGE]; +static int inter_minq_12[MODE_NUM][RES_NUM][QINDEX_RANGE]; static int rtc_minq_12[QINDEX_RANGE]; -#if !CONFIG_REALTIME_ONLY static int gf_high_1 = 2875; static int gf_low_1 = 562; static int gf_high_2 = 4994; @@ -113,16 +113,12 @@ static int kf_high = 8000; static int kf_low = 553; -#else -static int gf_high = 2400; -static int gf_low = 300; -#ifdef STRICT_RC -static int kf_high = 3200; -#else -static int kf_high = 5000; -#endif -static int kf_low = 400; -#endif + +static int gf_high_rtc = 2400; +static int gf_low_rtc = 300; +static int kf_high_rtc = 5000; +static int kf_low_rtc = 400; + // How many times less pixels there are to encode given the current scaling. // Temporary replacement for rcf_mult and rate_thresh_mult. static double resize_rate_factor(const FrameDimensionCfg *const frm_dim_cfg, @@ -145,39 +141,40 @@ return av1_find_qindex(minqtarget, bit_depth, 0, QINDEX_RANGE - 1); } -#if !CONFIG_REALTIME_ONLY -static double x1[RES_NUM][5] = { - { 0.1771, 0.379, 0.3279, 0.6634, 1.385 }, - { 0.1917, 0.3760, 0.34570, 0.6916, 1.14820 }, +static double x1[MODE_NUM][RES_NUM][5] = { + { + { 0.1771, 0.379, 0.3279, 0.6634, 1.385 }, + { 0.1917, 0.3760, 0.34570, 0.6916, 1.14820 }, + }, + { + { 0.15, 0.45, 0.30, 0.55, 0.90 }, + { 0.15, 0.45, 0.30, 0.55, 0.90 }, + }, }; -#else -static double x1[RES_NUM][5] = { - { 0.15, 0.45, 0.30, 0.55, 0.90 }, - { 0.15, 0.45, 0.30, 0.55, 0.90 }, -}; -#endif -static void init_minq_luts(int kf_low_m[RES_NUM][QINDEX_RANGE], - int kf_high_m[RES_NUM][QINDEX_RANGE], - int arfgf_low[RES_NUM][QINDEX_RANGE], - int arfgf_high[RES_NUM][QINDEX_RANGE], - int inter[RES_NUM][QINDEX_RANGE], int *rtc, +static void init_minq_luts(int kf_low_m[MODE_NUM][RES_NUM][QINDEX_RANGE], + int kf_high_m[MODE_NUM][RES_NUM][QINDEX_RANGE], + int arfgf_low[MODE_NUM][RES_NUM][QINDEX_RANGE], + int arfgf_high[MODE_NUM][RES_NUM][QINDEX_RANGE], + int inter[MODE_NUM][RES_NUM][QINDEX_RANGE], int *rtc, aom_bit_depth_t bit_depth) { int i; - for (int res = 0; res < RES_NUM; res++) { - for (i = 0; i < QINDEX_RANGE; i++) { - const double maxq = av1_convert_qindex_to_q(i, bit_depth); - kf_low_m[res][i] = - get_minq_index(maxq, 0.000001, -0.0004, x1[res][0], bit_depth); - kf_high_m[res][i] = - get_minq_index(maxq, 0.0000021, -0.00125, x1[res][1], bit_depth); - arfgf_low[res][i] = - get_minq_index(maxq, 0.0000015, -0.0009, x1[res][2], bit_depth); - arfgf_high[res][i] = - get_minq_index(maxq, 0.0000021, -0.00125, x1[res][3], bit_depth); - inter[res][i] = - get_minq_index(maxq, 0.00000271, -0.00113, x1[res][4], bit_depth); - rtc[i] = get_minq_index(maxq, 0.00000271, -0.00113, 0.70, bit_depth); + for (int mode = 0; mode < MODE_NUM; mode++) { + for (int res = 0; res < RES_NUM; res++) { + for (i = 0; i < QINDEX_RANGE; i++) { + const double maxq = av1_convert_qindex_to_q(i, bit_depth); + kf_low_m[mode][res][i] = get_minq_index(maxq, 0.000001, -0.0004, + x1[mode][res][0], bit_depth); + kf_high_m[mode][res][i] = get_minq_index(maxq, 0.0000021, -0.00125, + x1[mode][res][1], bit_depth); + arfgf_low[mode][res][i] = get_minq_index(maxq, 0.0000015, -0.0009, + x1[mode][res][2], bit_depth); + arfgf_high[mode][res][i] = get_minq_index(maxq, 0.0000021, -0.00125, + x1[mode][res][3], bit_depth); + inter[mode][res][i] = get_minq_index(maxq, 0.00000271, -0.00113, + x1[mode][res][4], bit_depth); + rtc[i] = get_minq_index(maxq, 0.00000271, -0.00113, 0.70, bit_depth); + } } } } @@ -1169,17 +1166,22 @@ return low_motion_minq[q] + adjustment; } } -#if !CONFIG_REALTIME_ONLY + static int gfboost_thresh[3] = { 4000, 4000, 3000 }; -#endif static int get_kf_active_quality(const PRIMARY_RATE_CONTROL *const p_rc, int q, aom_bit_depth_t bit_depth, const int res_idx) { int *kf_low_motion_minq; int *kf_high_motion_minq; - ASSIGN_MINQ_TABLE_2(bit_depth, kf_low_motion_minq, res_idx > 1); - ASSIGN_MINQ_TABLE_2(bit_depth, kf_high_motion_minq, res_idx > 1); - return get_active_quality(q, p_rc->kf_boost, kf_low, kf_high, + ASSIGN_MINQ_TABLE_2(bit_depth, kf_low_motion_minq, res_idx > 1, + p_rc->rtc_mode); + ASSIGN_MINQ_TABLE_2(bit_depth, kf_high_motion_minq, res_idx > 1, + p_rc->rtc_mode); + + int kf_low_local = p_rc->rtc_mode ? kf_low_rtc : kf_low; + int kf_high_local = p_rc->rtc_mode ? kf_high_rtc : kf_high; + + return get_active_quality(q, p_rc->kf_boost, kf_low_local, kf_high_local, kf_low_motion_minq, kf_high_motion_minq); } @@ -1188,19 +1190,23 @@ const int res_idx) { int *arfgf_low_motion_minq; int *arfgf_high_motion_minq; - ASSIGN_MINQ_TABLE_2(bit_depth, arfgf_low_motion_minq, res_idx > 1); - ASSIGN_MINQ_TABLE_2(bit_depth, arfgf_high_motion_minq, res_idx > 1); + ASSIGN_MINQ_TABLE_2(bit_depth, arfgf_low_motion_minq, res_idx > 1, + p_rc->rtc_mode); + ASSIGN_MINQ_TABLE_2(bit_depth, arfgf_high_motion_minq, res_idx > 1, + p_rc->rtc_mode); + int gf_low_local, gf_high_local; -#if !CONFIG_REALTIME_ONLY - int gf_low_local = - (p_rc->gfu_boost_average < gfboost_thresh[res_idx]) ? gf_low_1 : gf_low_2; - int gf_high_local = (p_rc->gfu_boost_average < gfboost_thresh[res_idx]) - ? gf_high_1 - : gf_high_2; -#else - int gf_low_local = gf_low; - int gf_high_local = gf_high; -#endif + if (p_rc->rtc_mode == false) { + gf_low_local = (p_rc->gfu_boost_average < gfboost_thresh[res_idx]) + ? gf_low_1 + : gf_low_2; + gf_high_local = (p_rc->gfu_boost_average < gfboost_thresh[res_idx]) + ? gf_high_1 + : gf_high_2; + } else { + gf_low_local = gf_low_rtc; + gf_high_local = gf_high_rtc; + } return get_active_quality(q, p_rc->gfu_boost, gf_low_local, gf_high_local, arfgf_low_motion_minq, arfgf_high_motion_minq); @@ -1212,9 +1218,9 @@ } static int get_gf_high_motion_quality(int q, aom_bit_depth_t bit_depth, - const int res_idx) { + const int res_idx, bool rtc_mode) { int *arfgf_high_motion_minq; - ASSIGN_MINQ_TABLE_2(bit_depth, arfgf_high_motion_minq, res_idx > 1); + ASSIGN_MINQ_TABLE_2(bit_depth, arfgf_high_motion_minq, res_idx > 1, rtc_mode); return arfgf_high_motion_minq[q]; } @@ -1607,7 +1613,7 @@ int active_worst_quality = calc_active_worst_quality_no_stats_vbr(cpi); int q; int *inter_minq; - ASSIGN_MINQ_TABLE_2(bit_depth, inter_minq, res_idx > 1); + ASSIGN_MINQ_TABLE_2(bit_depth, inter_minq, res_idx > 1, p_rc->rtc_mode); if (frame_is_intra_only(cm)) { if (rc_mode == AOM_Q) { @@ -2064,7 +2070,7 @@ const int res_idx = is_480p_or_larger + is_608p_or_larger; int *inter_minq; - ASSIGN_MINQ_TABLE_2(bit_depth, inter_minq, res_idx > 1); + ASSIGN_MINQ_TABLE_2(bit_depth, inter_minq, res_idx > 1, p_rc->rtc_mode); // TODO(jingning): Consider to rework this hack that covers issues incurred // in lightfield setting. @@ -2100,7 +2106,8 @@ active_best_quality = get_gf_active_quality(p_rc, q, bit_depth, res_idx); // Constrained quality use slightly lower active best. if (rc_mode == AOM_CQ) active_best_quality = active_best_quality * 15 / 16; - const int min_boost = get_gf_high_motion_quality(q, bit_depth, res_idx); + const int min_boost = + get_gf_high_motion_quality(q, bit_depth, res_idx, p_rc->rtc_mode); const int boost = min_boost - active_best_quality; active_best_quality = min_boost - (int)(boost * p_rc->arf_boost_factor); if (!is_intrl_arf_boost) return active_best_quality;
diff --git a/av1/encoder/ratectrl.h b/av1/encoder/ratectrl.h index 212c8ff..a854304 100644 --- a/av1/encoder/ratectrl.h +++ b/av1/encoder/ratectrl.h
@@ -328,7 +328,6 @@ */ int gfu_boost; -#if !CONFIG_REALTIME_ONLY /*! * Average boost factor of ARFs and GFs within a kf interval */ @@ -338,7 +337,12 @@ * Indicate if it is gfu_boost accumulation stage */ bool accumulate_stats_stage; -#endif + + /*! + * Indicate if it is realtime mode or not + */ + bool rtc_mode; + /*! * Stores the determined gf group lengths for a set of gf groups */
diff --git a/av1/encoder/speed_features.c b/av1/encoder/speed_features.c index dc0abdf..97edb28 100644 --- a/av1/encoder/speed_features.c +++ b/av1/encoder/speed_features.c
@@ -1301,9 +1301,6 @@ sf->tpl_sf.prune_intra_modes = 1; sf->tpl_sf.reduce_first_step_size = 6; sf->tpl_sf.subpel_force_stop = QUARTER_PEL; -#if CONFIG_REALTIME_ONLY - sf->tpl_sf.gop_length_decision_method = 1; -#endif sf->tx_sf.adaptive_txb_search_level = boosted ? 2 : 3; sf->tx_sf.tx_type_search.use_skip_flag_prediction = 2; @@ -2193,11 +2190,7 @@ } static inline void init_tpl_sf(TPL_SPEED_FEATURES *tpl_sf) { -#if !CONFIG_REALTIME_ONLY tpl_sf->gop_length_decision_method = 1; -#else - tpl_sf->gop_length_decision_method = 0; -#endif tpl_sf->prune_intra_modes = 0; tpl_sf->prune_starting_mv = 0; tpl_sf->reduce_first_step_size = 0; @@ -2643,6 +2636,8 @@ init_lpf_sf(&sf->lpf_sf); init_rt_sf(&sf->rt_sf); + cpi->ppi->p_rc.rtc_mode = (cpi->oxcf.mode == REALTIME); + switch (oxcf->mode) { case GOOD: set_good_speed_features_framesize_independent(cpi, sf, speed);