Coverage Report

Created: 2026-08-31 06:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/svt-av1/Source/Lib/Codec/mcomp.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2016, Alliance for Open Media. All rights reserved
3
 *
4
 * This source code is subject to the terms of the BSD 2 Clause License and
5
 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6
 * was not distributed with this source code in the LICENSE file, you can
7
 * obtain it at www.aomedia.org/license/software. If the Alliance for Open
8
 * Media Patent License 1.0 was not distributed with this source code in the
9
 * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
10
 */
11
12
#include <limits.h>
13
#include <math.h>
14
#include <stdio.h>
15
#include "mcomp.h"
16
#include "mv.h"
17
#include "av1_common.h"
18
#include "coding_unit.h"
19
#include "block_structures.h"
20
#include "av1me.h"
21
#include "aom_dsp_rtcd.h"
22
#include "rd_cost.h"
23
24
// =============================================================================
25
//  Subpixel Motion Search: Translational
26
// =============================================================================
27
0
#define INIT_SUBPEL_STEP_SIZE (4)
28
29
/*
30
 * To avoid the penalty for crossing cache-line read, preload the reference
31
 * area in a small buffer, which is aligned to make sure there won't be crossing
32
 * cache-line read while reading from this buffer. This reduced the cpu
33
 * cycles spent on reading ref data in sub-pixel filter functions.
34
 * TODO: Currently, since sub-pixel search range here is -3 ~ 3, copy 22 rows x
35
 * 32 cols area that is enough for 16x16 macroblock. Later, for SPLITMV, we
36
 * could reduce the area.
37
 */
38
39
// Returns the subpel offset used by various subpel variance functions [m]sv[a]f
40
0
static INLINE int svt_get_subpel_part(int x) {
41
0
    return x & 7;
42
0
}
43
44
// Gets the address of the ref buffer at subpel location (r, c), rounded to the
45
// nearest fullpel precision toward - \infty
46
47
0
static INLINE const uint8_t* svt_get_buf_from_mv(const struct svt_buf_2d* buf, const Mv mv) {
48
0
    const int offset = (mv.y >> 3) * buf->stride + (mv.x >> 3);
49
0
    return &buf->buf[offset];
50
0
}
51
52
// Calculates the variance of prediction residue.
53
static int svt_upsampled_pref_error(MacroBlockD* xd, const struct AV1Common* const cm, Mv this_mv,
54
0
                                    const SUBPEL_SEARCH_VAR_PARAMS* var_params) {
55
0
    const AomVarianceFnPtr*  vfp                = var_params->vfp;
56
0
    const SUBPEL_SEARCH_TYPE subpel_search_type = var_params->subpel_search_type;
57
58
0
    const MSBuffers* ms_buffers  = &var_params->ms_buffers;
59
0
    const uint8_t*   src         = ms_buffers->src->buf;
60
0
    const uint8_t*   ref         = svt_get_buf_from_mv(ms_buffers->ref, this_mv);
61
0
    const int        src_stride  = ms_buffers->src->stride;
62
0
    const int        ref_stride  = ms_buffers->ref->stride;
63
0
    const int        w           = var_params->w;
64
0
    const int        h           = var_params->h;
65
0
    const int        mi_row      = xd->mi_row;
66
0
    const int        mi_col      = xd->mi_col;
67
0
    const int        subpel_x_q3 = svt_get_subpel_part(this_mv.x);
68
0
    const int        subpel_y_q3 = svt_get_subpel_part(this_mv.y);
69
70
0
    unsigned int besterr;
71
0
    {
72
0
        DECLARE_ALIGNED(16, uint8_t, pred[MAX_SB_SQUARE]);
73
74
0
        {
75
0
            svt_aom_upsampled_pred(
76
0
                xd, cm, mi_row, mi_col, pred, w, h, subpel_x_q3, subpel_y_q3, ref, ref_stride, subpel_search_type);
77
0
        }
78
0
        unsigned int sse;
79
0
        besterr = vfp->vf(pred, w, src, src_stride, &sse);
80
0
    }
81
82
0
    return besterr;
83
0
}
84
85
// Estimates the variance of prediction residue using bilinear filter for fast
86
// search.
87
0
static INLINE int svt_estimated_pref_error(Mv this_mv, const SUBPEL_SEARCH_VAR_PARAMS* var_params) {
88
0
    const AomVarianceFnPtr* vfp = var_params->vfp;
89
90
0
    const MSBuffers* ms_buffers = &var_params->ms_buffers;
91
0
    const uint8_t*   src        = ms_buffers->src->buf;
92
0
    const uint8_t*   ref        = svt_get_buf_from_mv(ms_buffers->ref, this_mv);
93
0
    const int        src_stride = ms_buffers->src->stride;
94
0
    const int        ref_stride = ms_buffers->ref->stride;
95
96
0
    const int subpel_x_q3 = svt_get_subpel_part(this_mv.x);
97
0
    const int subpel_y_q3 = svt_get_subpel_part(this_mv.y);
98
99
0
    unsigned int sse;
100
0
    return vfp->svf(ref, ref_stride, subpel_x_q3, subpel_y_q3, src, src_stride, &sse);
101
0
}
102
103
// Estimates whether this_mv is better than best_mv. This function incorporates
104
// both prediction error and residue into account. It is suffixed "fast" because
105
// it uses bilinear filter to estimate the prediction.
106
static INLINE unsigned int svt_check_better_fast(Mv this_mv, Mv* best_mv, const SubpelMvLimits* mv_limits,
107
                                                 const SUBPEL_SEARCH_VAR_PARAMS* var_params,
108
                                                 const svt_mv_cost_param* mv_cost_params, unsigned int* besterr,
109
0
                                                 int* distortion) {
110
0
    if (svt_av1_is_subpelmv_in_range(mv_limits, this_mv)) {
111
0
        unsigned int cost;
112
0
        int          thismse;
113
0
        cost = svt_aom_fp_mv_err_cost(this_mv, mv_cost_params);
114
0
        if (mv_cost_params->mv_cost_type == MV_COST_OPT) {
115
0
            int64_t bestcost = *distortion + cost;
116
0
            if (bestcost > (((int64_t)*besterr * (int64_t)mv_cost_params->early_exit_th) / 1000)) {
117
0
                return (uint32_t)bestcost;
118
0
            }
119
0
        }
120
0
        thismse = svt_estimated_pref_error(this_mv, var_params);
121
0
        cost += thismse;
122
0
        int weight = 100;
123
0
        if (var_params->bias_fp && best_mv->x % 8 == 0 && best_mv->y % 8 == 0) {
124
0
            weight = var_params->bias_fp;
125
0
        }
126
0
        if ((((uint64_t)cost * weight) / 100) < *besterr) {
127
0
            *besterr    = cost;
128
0
            *best_mv    = this_mv;
129
0
            *distortion = thismse;
130
0
        }
131
0
        return cost;
132
0
    }
133
0
    return INT_MAX;
134
0
}
135
136
// Checks whether this_mv is better than best_mv. This function incorporates
137
// both prediction error and residue into account.
138
static AOM_FORCE_INLINE unsigned int svt_check_better(MacroBlockD* xd, const struct AV1Common* const cm, Mv this_mv,
139
                                                      Mv* best_mv, const SubpelMvLimits* mv_limits,
140
                                                      const SUBPEL_SEARCH_VAR_PARAMS* var_params,
141
0
                                                      const svt_mv_cost_param* mv_cost_params, unsigned int* besterr) {
142
0
    unsigned int cost;
143
0
    if (svt_av1_is_subpelmv_in_range(mv_limits, this_mv)) {
144
0
        int thismse;
145
0
        thismse = svt_upsampled_pref_error(xd, cm, this_mv, var_params);
146
0
        cost    = svt_aom_fp_mv_err_cost(this_mv, mv_cost_params);
147
0
        cost += thismse;
148
0
        int weight = 100;
149
0
        if (var_params->bias_fp && best_mv->x % 8 == 0 && best_mv->y % 8 == 0) {
150
0
            weight = var_params->bias_fp;
151
0
        }
152
0
        if ((((uint64_t)cost * weight) / 100) < *besterr) {
153
0
            *besterr = cost;
154
0
            *best_mv = this_mv;
155
0
        }
156
0
    } else {
157
0
        cost = INT_MAX;
158
0
    }
159
0
    return cost;
160
0
}
161
162
static INLINE Mv get_best_diag_step(int step_size, unsigned int left_cost, unsigned int right_cost,
163
                                    unsigned int up_cost, unsigned int down_cost) {
164
    const Mv diag_step = {
165
        {left_cost <= right_cost ? -step_size : step_size, up_cost <= down_cost ? -step_size : step_size}};
166
167
    return diag_step;
168
}
169
170
static AOM_FORCE_INLINE Mv svt_first_level_check(MacroBlockD* xd, const struct AV1Common* const cm, const Mv this_mv,
171
                                                 Mv* best_mv, const int hstep, const SubpelMvLimits* mv_limits,
172
                                                 const SUBPEL_SEARCH_VAR_PARAMS* var_params,
173
0
                                                 const svt_mv_cost_param* mv_cost_params, unsigned int* besterr) {
174
0
    const Mv left_mv   = {{this_mv.x - hstep, this_mv.y}};
175
0
    const Mv right_mv  = {{this_mv.x + hstep, this_mv.y}};
176
0
    const Mv top_mv    = {{this_mv.x, this_mv.y - hstep}};
177
0
    const Mv bottom_mv = {{this_mv.x, this_mv.y + hstep}};
178
179
0
    const unsigned int left = svt_check_better(
180
0
        xd, cm, left_mv, best_mv, mv_limits, var_params, mv_cost_params, besterr);
181
0
    const unsigned int right = svt_check_better(
182
0
        xd, cm, right_mv, best_mv, mv_limits, var_params, mv_cost_params, besterr);
183
0
    const unsigned int up   = svt_check_better(xd, cm, top_mv, best_mv, mv_limits, var_params, mv_cost_params, besterr);
184
0
    const unsigned int down = svt_check_better(
185
0
        xd, cm, bottom_mv, best_mv, mv_limits, var_params, mv_cost_params, besterr);
186
187
0
    const Mv diag_step = get_best_diag_step(hstep, left, right, up, down);
188
0
    const Mv diag_mv   = {{this_mv.x + diag_step.x, this_mv.y + diag_step.y}};
189
190
    // Check the diagonal direction with the best mv
191
0
    svt_check_better(xd, cm, diag_mv, best_mv, mv_limits, var_params, mv_cost_params, besterr);
192
193
0
    return diag_step;
194
0
}
195
196
// A newer version of second level check that gives better quality.
197
// TODO(chiyotsai@google.com): evaluate this on subpel_search_types different
198
// from av1_find_best_sub_pixel_tree
199
static AOM_FORCE_INLINE void svt_second_level_check_v2(MacroBlockD* xd, const struct AV1Common* const cm,
200
                                                       const Mv this_mv, Mv diag_step, Mv* best_mv,
201
                                                       const SubpelMvLimits*           mv_limits,
202
                                                       const SUBPEL_SEARCH_VAR_PARAMS* var_params,
203
0
                                                       const svt_mv_cost_param* mv_cost_params, unsigned int* besterr) {
204
0
    assert(best_mv->y == this_mv.y + diag_step.y || best_mv->x == this_mv.x + diag_step.x);
205
0
    if (CHECK_MV_EQUAL(this_mv, *best_mv)) {
206
0
        return;
207
0
    } else if (this_mv.y == best_mv->y) {
208
        // Search away from diagonal step since diagonal search did not provide any
209
        // improvement
210
0
        diag_step.y *= -1;
211
0
    } else if (this_mv.x == best_mv->x) {
212
0
        diag_step.x *= -1;
213
0
    }
214
215
0
    const Mv row_bias_mv  = {{best_mv->x, best_mv->y + diag_step.y}};
216
0
    const Mv col_bias_mv  = {{best_mv->x + diag_step.x, best_mv->y}};
217
0
    const Mv diag_bias_mv = {{best_mv->x + diag_step.x, best_mv->y + diag_step.y}};
218
219
    // svt_check_better only lowers *besterr on an improvement, so a drop across
220
    // the two probes is equivalent to "a better mv was found".
221
0
    const unsigned int besterr_before = *besterr;
222
0
    svt_check_better(xd, cm, row_bias_mv, best_mv, mv_limits, var_params, mv_cost_params, besterr);
223
0
    svt_check_better(xd, cm, col_bias_mv, best_mv, mv_limits, var_params, mv_cost_params, besterr);
224
225
    // Do an additional search if the second iteration gives a better mv
226
0
    if (*besterr < besterr_before) {
227
0
        svt_check_better(xd, cm, diag_bias_mv, best_mv, mv_limits, var_params, mv_cost_params, besterr);
228
0
    }
229
0
}
230
231
// Gets the error at the beginning when the mv has fullpel precision
232
static unsigned int svt_upsampled_setup_center_error(const Mv bestmv, const SUBPEL_SEARCH_VAR_PARAMS* var_params,
233
                                                     const svt_mv_cost_param* mv_cost_params,
234
0
                                                     unsigned int*            distortion) {
235
0
    const MSBuffers* ms_buffers = &var_params->ms_buffers;
236
0
    const uint8_t*   ref        = svt_get_buf_from_mv(ms_buffers->ref, bestmv);
237
0
    *distortion                 = var_params->vfp->vf(
238
0
        ref, ms_buffers->ref->stride, ms_buffers->src->buf, ms_buffers->src->stride, distortion);
239
0
    return *distortion + svt_aom_fp_mv_err_cost(bestmv, mv_cost_params);
240
0
}
241
242
// Searches the four cardinal direction for a better mv, then follows up with a
243
// search in the best quadrant. This uses bilinear filter to speed up the
244
// calculation.
245
static AOM_FORCE_INLINE Mv first_level_check_fast(const Mv this_mv, Mv* best_mv, int hstep,
246
                                                  const SubpelMvLimits*           mv_limits,
247
                                                  const SUBPEL_SEARCH_VAR_PARAMS* var_params,
248
                                                  const svt_mv_cost_param* mv_cost_params, unsigned int* besterr,
249
                                                  unsigned int orgerr, int* distortion) {
250
    // Check the four cardinal directions
251
    const Mv           left_mv = {{this_mv.x - hstep, this_mv.y}};
252
    const unsigned int left    = svt_check_better_fast(
253
        left_mv, best_mv, mv_limits, var_params, mv_cost_params, besterr, distortion);
254
255
    const Mv           right_mv = {{this_mv.x + hstep, this_mv.y}};
256
    const unsigned int right    = svt_check_better_fast(
257
        right_mv, best_mv, mv_limits, var_params, mv_cost_params, besterr, distortion);
258
259
    const Mv           top_mv = {{this_mv.x, this_mv.y - hstep}};
260
    const unsigned int up     = svt_check_better_fast(
261
        top_mv, best_mv, mv_limits, var_params, mv_cost_params, besterr, distortion);
262
263
    const Mv           bottom_mv = {{this_mv.x, this_mv.y + hstep}};
264
    const unsigned int down      = svt_check_better_fast(
265
        bottom_mv, best_mv, mv_limits, var_params, mv_cost_params, besterr, distortion);
266
267
    const Mv diag_step = get_best_diag_step(hstep, left, right, up, down);
268
    const Mv diag_mv   = {{this_mv.x + diag_step.x, this_mv.y + diag_step.y}};
269
    if (*besterr >= orgerr) {
270
        return diag_step;
271
    }
272
    // Check the diagonal direction with the best mv
273
    svt_check_better_fast(diag_mv, best_mv, mv_limits, var_params, mv_cost_params, besterr, distortion);
274
275
    return diag_step;
276
}
277
278
// Performs a following up search after first_level_check_fast is called. This
279
// performs two extra chess pattern searches in the best quadrant.
280
static AOM_FORCE_INLINE void second_level_check_fast(const Mv this_mv, const Mv diag_step, Mv* best_mv, int hstep,
281
                                                     const SubpelMvLimits*           mv_limits,
282
                                                     const SUBPEL_SEARCH_VAR_PARAMS* var_params,
283
                                                     const svt_mv_cost_param* mv_cost_params, unsigned int* besterr,
284
                                                     int* distortion) {
285
    assert(diag_step.y == hstep || diag_step.y == -hstep);
286
    assert(diag_step.x == hstep || diag_step.x == -hstep);
287
    const int tr = this_mv.y;
288
    const int tc = this_mv.x;
289
    const int br = best_mv->y;
290
    const int bc = best_mv->x;
291
    if (tr != br && tc != bc) {
292
        assert(diag_step.x == bc - tc);
293
        assert(diag_step.y == br - tr);
294
        const Mv chess_mv_1 = {{bc + diag_step.x, br}};
295
        const Mv chess_mv_2 = {{bc, br + diag_step.y}};
296
        svt_check_better_fast(chess_mv_1, best_mv, mv_limits, var_params, mv_cost_params, besterr, distortion);
297
        svt_check_better_fast(chess_mv_2, best_mv, mv_limits, var_params, mv_cost_params, besterr, distortion);
298
    } else if (tr == br && tc != bc) {
299
        assert(diag_step.x == bc - tc);
300
        // Continue searching in the best direction
301
        const Mv bottom_long_mv = {{bc + diag_step.x, br + hstep}};
302
        const Mv top_long_mv    = {{bc + diag_step.x, br - hstep}};
303
        svt_check_better_fast(bottom_long_mv, best_mv, mv_limits, var_params, mv_cost_params, besterr, distortion);
304
        svt_check_better_fast(top_long_mv, best_mv, mv_limits, var_params, mv_cost_params, besterr, distortion);
305
306
        // Search in the direction opposite of the best quadrant
307
        const Mv rev_mv = {{bc, br - diag_step.y}};
308
        svt_check_better_fast(rev_mv, best_mv, mv_limits, var_params, mv_cost_params, besterr, distortion);
309
    } else if (tr != br && tc == bc) {
310
        assert(diag_step.y == br - tr);
311
        // Continue searching in the best direction
312
        const Mv right_long_mv = {{bc + hstep, br + diag_step.y}};
313
        const Mv left_long_mv  = {{bc - hstep, br + diag_step.y}};
314
        svt_check_better_fast(right_long_mv, best_mv, mv_limits, var_params, mv_cost_params, besterr, distortion);
315
        svt_check_better_fast(left_long_mv, best_mv, mv_limits, var_params, mv_cost_params, besterr, distortion);
316
317
        // Search in the direction opposite of the best quadrant
318
        const Mv rev_mv = {{bc - diag_step.x, br}};
319
        svt_check_better_fast(rev_mv, best_mv, mv_limits, var_params, mv_cost_params, besterr, distortion);
320
    }
321
}
322
323
// Combines first level check and second level check when applicable. This first
324
// searches the four cardinal directions, and perform several
325
// diagonal/chess-pattern searches in the best quadrant.
326
static AOM_FORCE_INLINE void two_level_checks_fast(const Mv this_mv, Mv* best_mv, int hstep,
327
                                                   const SubpelMvLimits*           mv_limits,
328
                                                   const SUBPEL_SEARCH_VAR_PARAMS* var_params,
329
                                                   const svt_mv_cost_param* mv_cost_params, unsigned int* besterr,
330
                                                   unsigned int orgerr, int* distortion, int iters) {
331
    const Mv diag_step = first_level_check_fast(
332
        this_mv, best_mv, hstep, mv_limits, var_params, mv_cost_params, besterr, orgerr, distortion);
333
    if (*besterr < orgerr && iters > 1) {
334
        second_level_check_fast(
335
            this_mv, diag_step, best_mv, hstep, mv_limits, var_params, mv_cost_params, besterr, distortion);
336
    }
337
}
338
339
extern const uint8_t svt_aom_eb_av1_var_offs[MAX_SB_SIZE];
340
341
int svt_av1_find_best_sub_pixel_tree_pruned(void* ictx, MacroBlockD* xd, const struct AV1Common* const cm,
342
                                            SUBPEL_MOTION_SEARCH_PARAMS* ms_params, Mv start_mv, Mv* bestmv,
343
0
                                            BlockSize bsize) {
344
0
    const int                       allow_hp       = ms_params->allow_hp;
345
0
    const int                       forced_stop    = ms_params->forced_stop;
346
0
    const int                       iters_per_step = ms_params->iters_per_step;
347
0
    const SubpelMvLimits*           mv_limits      = &ms_params->mv_limits;
348
0
    const svt_mv_cost_param*        mv_cost_params = &ms_params->mv_cost_params;
349
0
    const SUBPEL_SEARCH_VAR_PARAMS* var_params     = &ms_params->var_params;
350
0
    int                             hstep          = INIT_SUBPEL_STEP_SIZE; // Step size, initialized to 4/8=1/2 pel
351
0
    unsigned int                    besterr;
352
0
    unsigned int                    org_error;
353
0
    int                             distortion_storage = 0;
354
0
    int*                            distortion         = &distortion_storage;
355
0
    *bestmv                                            = start_mv;
356
357
0
    (void)xd;
358
0
    (void)cm;
359
0
    besterr = svt_upsampled_setup_center_error(*bestmv, var_params, mv_cost_params, (unsigned int*)distortion);
360
361
0
    if (ictx != NULL && ms_params->search_stage == SPEL_ME) {
362
0
        ModeDecisionContext* ctx                                 = (ModeDecisionContext*)ictx;
363
0
        ctx->fp_me_dist[ms_params->list_idx][ms_params->ref_idx] = besterr;
364
0
    }
365
366
0
    const uint32_t th_normalizer = var_params->w * var_params->h * ms_params->abs_th_mult;
367
0
    if (besterr < th_normalizer) {
368
0
        return besterr;
369
0
    }
370
    // How many steps to take. A round of 0 means fullpel search only, 1 means
371
    // half-pel, and so on.
372
0
    const int round = AOMMIN(FULL_PEL - forced_stop, 3 - !allow_hp);
373
374
    // If forced_stop is FULL_PEL, return.
375
0
    if (!round) {
376
0
        return besterr;
377
0
    }
378
    // Exit subpel search if the variance of the full-pel predicted samples is low (i.e. where likely interpolation will not modify the integer samples)
379
0
    if (ms_params->pred_variance_th) {
380
0
        const MSBuffers*   ms_buffers = &var_params->ms_buffers;
381
0
        const uint8_t*     ref        = svt_get_buf_from_mv(ms_buffers->ref, *bestmv);
382
0
        unsigned int       sse;
383
0
        const unsigned int var = var_params->vfp->vf(ref, ms_buffers->ref->stride, svt_aom_eb_av1_var_offs, 0, &sse);
384
0
        int                block_var = ROUND_POWER_OF_TWO(var, eb_num_pels_log2_lookup[bsize]);
385
386
0
        if (block_var < ms_params->pred_variance_th) {
387
0
            return besterr;
388
0
        }
389
0
    }
390
0
    if (ms_params->skip_diag_refinement >= 4) {
391
0
        org_error = 0;
392
0
    } else {
393
0
        unsigned int demo = ms_params->skip_diag_refinement >= 2
394
0
            ? ((var_params->w >= 64 || var_params->h >= 64) ? 2 : 1)
395
0
            : 1;
396
0
        org_error         = ms_params->skip_diag_refinement ? besterr / demo : INT_MAX;
397
0
    }
398
0
    for (int iter = 0; iter < round; ++iter) {
399
0
        unsigned int prev_besterr = besterr;
400
0
        two_level_checks_fast(start_mv,
401
0
                              bestmv,
402
0
                              hstep,
403
0
                              mv_limits,
404
0
                              var_params,
405
0
                              mv_cost_params,
406
0
                              &besterr,
407
0
                              org_error,
408
0
                              distortion,
409
0
                              iters_per_step);
410
0
        hstep >>= 1;
411
0
        start_mv = *bestmv;
412
0
        if (ms_params->skip_diag_refinement && iter < QUARTER_PEL) {
413
0
            org_error = MIN(org_error, besterr);
414
0
        }
415
0
        int32_t deviation = (((int64_t)MAX(besterr, 1) - (int64_t)MAX(prev_besterr, 1)) * 100) /
416
0
            (int64_t)MAX(prev_besterr, 1);
417
0
        if (deviation >= ms_params->round_dev_th) {
418
0
            return besterr;
419
0
        }
420
0
    }
421
0
    return besterr;
422
0
}
423
424
int svt_av1_find_best_sub_pixel_tree(void* ictx, MacroBlockD* xd, const struct AV1Common* const cm,
425
0
                                     SUBPEL_MOTION_SEARCH_PARAMS* ms_params, Mv start_mv, Mv* bestmv, BlockSize bsize) {
426
0
    ModeDecisionContext* ctx            = (ModeDecisionContext*)ictx;
427
0
    const int            allow_hp       = ms_params->allow_hp;
428
0
    const int            forced_stop    = ms_params->forced_stop;
429
0
    const int            iters_per_step = ms_params->iters_per_step;
430
431
0
    svt_mv_cost_param*              mv_cost_params = &ms_params->mv_cost_params;
432
0
    const SUBPEL_SEARCH_VAR_PARAMS* var_params     = &ms_params->var_params;
433
0
    const SubpelMvLimits*           mv_limits      = &ms_params->mv_limits;
434
435
    // How many steps to take. A round of 0 means fullpel search only, 1 means
436
    // half-pel, and so on.
437
0
    int round = AOMMIN(FULL_PEL - forced_stop, 3 - !allow_hp);
438
0
    int hstep = INIT_SUBPEL_STEP_SIZE; // Step size, initialized to 4/8=1/2 pel
439
440
0
    unsigned int besterr;
441
0
    int          distortion_storage = 0;
442
0
    int*         distortion         = &distortion_storage;
443
444
0
    *bestmv = start_mv;
445
0
    besterr = svt_upsampled_setup_center_error(*bestmv, var_params, mv_cost_params, (unsigned int*)distortion);
446
0
    if (ctx != NULL && ms_params->search_stage == SPEL_ME) {
447
0
        ctx->fp_me_dist[ms_params->list_idx][ms_params->ref_idx] = besterr;
448
0
        if (ctx->pd_pass == PD_PASS_1 && ctx->md_subpel_me_ctrls.mvp_th) {
449
0
            unsigned int  best_mvperr  = ctx->best_fp_mvp_dist[ms_params->list_idx][ms_params->ref_idx];
450
0
            int           best_mvp_idx = ctx->best_fp_mvp_idx[ms_params->list_idx][ms_params->ref_idx];
451
0
            const int     mvp_err      = best_mvperr + 1;
452
0
            const int     me_err       = besterr + 1;
453
0
            const int32_t deviation    = ((me_err - mvp_err) * 100) / me_err;
454
0
            if (deviation >= ctx->md_subpel_me_ctrls.mvp_th) {
455
0
                round = 1;
456
0
            } else if (ABS(bestmv->x - ctx->mvp_array[ms_params->list_idx][ms_params->ref_idx][best_mvp_idx].x) >
457
0
                           ctx->md_subpel_me_ctrls.hp_mv_th ||
458
0
                       ABS(bestmv->y - ctx->mvp_array[ms_params->list_idx][ms_params->ref_idx][best_mvp_idx].y) >
459
0
                           ctx->md_subpel_me_ctrls.hp_mv_th) {
460
0
                round = MIN(round, 2);
461
0
            }
462
0
        }
463
0
    }
464
0
    const uint32_t th_normalizer = var_params->w * var_params->h * ms_params->abs_th_mult;
465
0
    if (besterr < th_normalizer) {
466
0
        return besterr;
467
0
    }
468
469
    // If forced_stop is FULL_PEL, return.
470
0
    if (!round) {
471
0
        return besterr;
472
0
    }
473
    // Exit subpel search if the variance of the full-pel predicted samples is low (i.e. where likely interpolation will not modify the integer samples)
474
0
    if (ms_params->pred_variance_th) {
475
0
        const MSBuffers*   ms_buffers = &var_params->ms_buffers;
476
0
        const uint8_t*     ref        = svt_get_buf_from_mv(ms_buffers->ref, *bestmv);
477
0
        unsigned int       sse;
478
0
        const unsigned int var = var_params->vfp->vf(ref, ms_buffers->ref->stride, svt_aom_eb_av1_var_offs, 0, &sse);
479
0
        int                block_var = ROUND_POWER_OF_TWO(var, eb_num_pels_log2_lookup[bsize]);
480
481
0
        if (block_var < ms_params->pred_variance_th) {
482
0
            return besterr;
483
0
        }
484
0
    }
485
0
    for (int iter = 0; iter < round; ++iter) {
486
0
        Mv iter_center_mv = *bestmv;
487
0
        Mv diag_step;
488
0
        diag_step = svt_first_level_check(
489
0
            xd, cm, iter_center_mv, bestmv, hstep, mv_limits, var_params, mv_cost_params, &besterr);
490
491
        // Check diagonal sub-pixel position
492
0
        if (!CHECK_MV_EQUAL(iter_center_mv, *bestmv) && iters_per_step > 1) {
493
0
            svt_second_level_check_v2(
494
0
                xd, cm, iter_center_mv, diag_step, bestmv, mv_limits, var_params, mv_cost_params, &besterr);
495
0
        }
496
497
0
        hstep >>= 1;
498
0
    }
499
500
0
    return besterr;
501
0
}
502
503
// =============================================================================
504
//  SVT Functions
505
// =============================================================================