Coverage Report

Created: 2026-10-28 06:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/dav1d/src/refmvs.c
Line
Count
Source
1
/*
2
 * Copyright © 2020, VideoLAN and dav1d authors
3
 * Copyright © 2020, Two Orioles, LLC
4
 * All rights reserved.
5
 *
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions are met:
8
 *
9
 * 1. Redistributions of source code must retain the above copyright notice, this
10
 *    list of conditions and the following disclaimer.
11
 *
12
 * 2. Redistributions in binary form must reproduce the above copyright notice,
13
 *    this list of conditions and the following disclaimer in the documentation
14
 *    and/or other materials provided with the distribution.
15
 *
16
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
 */
27
28
#include "config.h"
29
30
#include <limits.h>
31
#include <stdlib.h>
32
#include <string.h>
33
34
#include "dav1d/common.h"
35
36
#include "common/intops.h"
37
38
#include "src/env.h"
39
#include "src/mem.h"
40
#include "src/refmvs.h"
41
42
static void add_spatial_candidate(refmvs_candidate *const mvstack, int *const cnt,
43
                                  const int weight, const refmvs_block *const b,
44
                                  const union refmvs_refpair ref, const mv gmv[2],
45
                                  int *const have_newmv_match,
46
                                  int *const have_refmv_match)
47
1.96M
{
48
1.96M
    if (b->mv.mv[0].n == INVALID_MV) return; // intra block, no intrabc
49
50
1.86M
    if (ref.ref[1] == -1) {
51
2.11M
        for (int n = 0; n < 2; n++) {
52
1.92M
            if (b->ref.ref[n] == ref.ref[0]) {
53
1.51M
                const mv cand_mv = ((b->mf & 1) && gmv[0].n != INVALID_MV) ?
54
1.46M
                                   gmv[0] : b->mv.mv[n];
55
56
1.51M
                *have_refmv_match = 1;
57
1.51M
                *have_newmv_match |= b->mf >> 1;
58
59
1.51M
                const int last = *cnt;
60
2.86M
                for (int m = 0; m < last; m++)
61
1.70M
                    if (mvstack[m].mv.mv[0].n == cand_mv.n) {
62
351k
                        mvstack[m].weight += weight;
63
351k
                        return;
64
351k
                    }
65
66
1.16M
                if (last < 8) {
67
1.16M
                    mvstack[last].mv.mv[0] = cand_mv;
68
1.16M
                    mvstack[last].weight = weight;
69
1.16M
                    *cnt = last + 1;
70
1.16M
                }
71
1.16M
                return;
72
1.51M
            }
73
1.92M
        }
74
1.70M
    } else if (b->ref.pair == ref.pair) {
75
55.2k
        const refmvs_mvpair cand_mv = { .mv = {
76
55.2k
            [0] = ((b->mf & 1) && gmv[0].n != INVALID_MV) ? gmv[0] : b->mv.mv[0],
77
55.2k
            [1] = ((b->mf & 1) && gmv[1].n != INVALID_MV) ? gmv[1] : b->mv.mv[1],
78
55.2k
        }};
79
80
55.2k
        *have_refmv_match = 1;
81
55.2k
        *have_newmv_match |= b->mf >> 1;
82
83
55.2k
        const int last = *cnt;
84
76.7k
        for (int n = 0; n < last; n++)
85
39.4k
            if (mvstack[n].mv.n == cand_mv.n) {
86
17.8k
                mvstack[n].weight += weight;
87
17.8k
                return;
88
17.8k
            }
89
90
37.3k
        if (last < 8) {
91
37.3k
            mvstack[last].mv = cand_mv;
92
37.3k
            mvstack[last].weight = weight;
93
37.3k
            *cnt = last + 1;
94
37.3k
        }
95
37.3k
    }
96
1.86M
}
97
98
static int scan_row(refmvs_candidate *const mvstack, int *const cnt,
99
                    const union refmvs_refpair ref, const mv gmv[2],
100
                    const refmvs_block *b, const int bw4, const int w4,
101
                    const int max_rows, const int step,
102
                    int *const have_newmv_match, int *const have_refmv_match)
103
500k
{
104
500k
    const refmvs_block *cand_b = b;
105
500k
    const enum BlockSize first_cand_bs = cand_b->bs;
106
500k
    const uint8_t *const first_cand_b_dim = dav1d_block_dimensions[first_cand_bs];
107
500k
    int cand_bw4 = first_cand_b_dim[0];
108
500k
    int len = imax(step, imin(bw4, cand_bw4));
109
110
500k
    if (bw4 <= cand_bw4) {
111
        // FIXME weight can be higher for odd blocks (bx4 & 1), but then the
112
        // position of the first block has to be odd already, i.e. not just
113
        // for row_offset=-3/-5
114
        // FIXME why can this not be cand_bw4?
115
450k
        const int weight = bw4 == 1 ? 2 :
116
450k
                           imax(2, imin(2 * max_rows, first_cand_b_dim[1]));
117
450k
        add_spatial_candidate(mvstack, cnt, len * weight, cand_b, ref, gmv,
118
450k
                              have_newmv_match, have_refmv_match);
119
450k
        return weight >> 1;
120
450k
    }
121
122
86.7k
    for (int x = 0;;) {
123
        // FIXME if we overhang above, we could fill a bitmask so we don't have
124
        // to repeat the add_spatial_candidate() for the next row, but just increase
125
        // the weight here
126
86.7k
        add_spatial_candidate(mvstack, cnt, len * 2, cand_b, ref, gmv,
127
86.7k
                              have_newmv_match, have_refmv_match);
128
86.7k
        x += len;
129
86.7k
        if (x >= w4) return 1;
130
37.1k
        cand_b = &b[x];
131
37.1k
        cand_bw4 = dav1d_block_dimensions[cand_b->bs][0];
132
37.1k
        assert(cand_bw4 < bw4);
133
37.1k
        len = imax(step, cand_bw4);
134
37.1k
    }
135
49.9k
}
136
137
static int scan_col(refmvs_candidate *const mvstack, int *const cnt,
138
                    const union refmvs_refpair ref, const mv gmv[2],
139
                    /*const*/ refmvs_block *const *b, const int bh4, const int h4,
140
                    const int bx4, const int max_cols, const int step,
141
                    int *const have_newmv_match, int *const have_refmv_match)
142
985k
{
143
985k
    const refmvs_block *cand_b = &b[0][bx4];
144
985k
    const enum BlockSize first_cand_bs = cand_b->bs;
145
985k
    const uint8_t *const first_cand_b_dim = dav1d_block_dimensions[first_cand_bs];
146
985k
    int cand_bh4 = first_cand_b_dim[1];
147
985k
    int len = imax(step, imin(bh4, cand_bh4));
148
149
985k
    if (bh4 <= cand_bh4) {
150
        // FIXME weight can be higher for odd blocks (by4 & 1), but then the
151
        // position of the first block has to be odd already, i.e. not just
152
        // for col_offset=-3/-5
153
        // FIXME why can this not be cand_bh4?
154
924k
        const int weight = bh4 == 1 ? 2 :
155
924k
                           imax(2, imin(2 * max_cols, first_cand_b_dim[0]));
156
924k
        add_spatial_candidate(mvstack, cnt, len * weight, cand_b, ref, gmv,
157
924k
                            have_newmv_match, have_refmv_match);
158
924k
        return weight >> 1;
159
924k
    }
160
161
96.1k
    for (int y = 0;;) {
162
        // FIXME if we overhang above, we could fill a bitmask so we don't have
163
        // to repeat the add_spatial_candidate() for the next row, but just increase
164
        // the weight here
165
96.1k
        add_spatial_candidate(mvstack, cnt, len * 2, cand_b, ref, gmv,
166
96.1k
                              have_newmv_match, have_refmv_match);
167
96.1k
        y += len;
168
96.1k
        if (y >= h4) return 1;
169
37.6k
        cand_b = &b[y][bx4];
170
37.6k
        cand_bh4 = dav1d_block_dimensions[cand_b->bs][1];
171
37.6k
        assert(cand_bh4 < bh4);
172
37.6k
        len = imax(step, cand_bh4);
173
37.6k
    }
174
61.4k
}
175
176
18.0k
static inline union mv mv_projection(const union mv mv, const int num, const int den) {
177
18.0k
    static const uint16_t div_mult[32] = {
178
18.0k
           0, 16384, 8192, 5461, 4096, 3276, 2730, 2340,
179
18.0k
        2048,  1820, 1638, 1489, 1365, 1260, 1170, 1092,
180
18.0k
        1024,   963,  910,  862,  819,  780,  744,  712,
181
18.0k
         682,   655,  630,  606,  585,  564,  546,  528
182
18.0k
    };
183
18.0k
    assert(den > 0 && den < 32);
184
18.0k
    assert(num > -32 && num < 32);
185
18.0k
    const int frac = num * div_mult[den];
186
18.0k
    const int y = mv.y * frac, x = mv.x * frac;
187
    // Round and clip according to AV1 spec section 7.9.3
188
18.0k
    return (union mv) { // 0x3fff == (1 << 14) - 1
189
18.0k
        .y = iclip((y + 8192 + (y >> 31)) >> 14, -0x3fff, 0x3fff),
190
18.0k
        .x = iclip((x + 8192 + (x >> 31)) >> 14, -0x3fff, 0x3fff)
191
18.0k
    };
192
18.0k
}
193
194
static void add_temporal_candidate(const refmvs_frame *const rf,
195
                                   refmvs_candidate *const mvstack, int *const cnt,
196
                                   const refmvs_temporal_block *const rb,
197
                                   const union refmvs_refpair ref, int *const globalmv_ctx,
198
                                   const union mv gmv[], const int weight)
199
11.6k
{
200
11.6k
    if (rb->mv.n == INVALID_MV) return;
201
202
9.14k
    union mv mv = mv_projection(rb->mv, rf->pocdiff[ref.ref[0] - 1], rb->ref);
203
9.14k
    fix_mv_precision(rf->frm_hdr, &mv);
204
205
9.14k
    const int last = *cnt;
206
9.14k
    if (ref.ref[1] == -1) {
207
6.03k
        if (globalmv_ctx)
208
2.98k
            *globalmv_ctx = (abs(mv.x - gmv[0].x) | abs(mv.y - gmv[0].y)) >= 16;
209
210
9.86k
        for (int n = 0; n < last; n++)
211
7.97k
            if (mvstack[n].mv.mv[0].n == mv.n) {
212
4.14k
                mvstack[n].weight += weight;
213
4.14k
                return;
214
4.14k
            }
215
1.89k
        if (last < 8) {
216
1.89k
            mvstack[last].mv.mv[0] = mv;
217
1.89k
            mvstack[last].weight = weight;
218
1.89k
            *cnt = last + 1;
219
1.89k
        }
220
3.11k
    } else {
221
3.11k
        refmvs_mvpair mvp = { .mv = {
222
3.11k
            [0] = mv,
223
3.11k
            [1] = mv_projection(rb->mv, rf->pocdiff[ref.ref[1] - 1], rb->ref),
224
3.11k
        }};
225
3.11k
        fix_mv_precision(rf->frm_hdr, &mvp.mv[1]);
226
227
4.12k
        for (int n = 0; n < last; n++)
228
3.20k
            if (mvstack[n].mv.n == mvp.n) {
229
2.18k
                mvstack[n].weight += weight;
230
2.18k
                return;
231
2.18k
            }
232
926
        if (last < 8) {
233
926
            mvstack[last].mv = mvp;
234
926
            mvstack[last].weight = weight;
235
926
            *cnt = last + 1;
236
926
        }
237
926
    }
238
9.14k
}
239
240
static void add_compound_extended_candidate(refmvs_candidate *const same,
241
                                            int *const same_count,
242
                                            const refmvs_block *const cand_b,
243
                                            const int sign0, const int sign1,
244
                                            const union refmvs_refpair ref,
245
                                            const uint8_t *const sign_bias)
246
49.2k
{
247
49.2k
    refmvs_candidate *const diff = &same[2];
248
49.2k
    int *const diff_count = &same_count[2];
249
250
125k
    for (int n = 0; n < 2; n++) {
251
97.1k
        const int cand_ref = cand_b->ref.ref[n];
252
253
97.1k
        if (cand_ref <= 0) break;
254
255
76.7k
        mv cand_mv = cand_b->mv.mv[n];
256
76.7k
        if (cand_ref == ref.ref[0]) {
257
28.6k
            if (same_count[0] < 2)
258
28.0k
                same[same_count[0]++].mv.mv[0] = cand_mv;
259
28.6k
            if (diff_count[1] < 2) {
260
25.9k
                if (sign1 ^ sign_bias[cand_ref - 1]) {
261
625
                    cand_mv.y = -cand_mv.y;
262
625
                    cand_mv.x = -cand_mv.x;
263
625
                }
264
25.9k
                diff[diff_count[1]++].mv.mv[1] = cand_mv;
265
25.9k
            }
266
48.1k
        } else if (cand_ref == ref.ref[1]) {
267
28.0k
            if (same_count[1] < 2)
268
27.6k
                same[same_count[1]++].mv.mv[1] = cand_mv;
269
28.0k
            if (diff_count[0] < 2) {
270
24.5k
                if (sign0 ^ sign_bias[cand_ref - 1]) {
271
634
                    cand_mv.y = -cand_mv.y;
272
634
                    cand_mv.x = -cand_mv.x;
273
634
                }
274
24.5k
                diff[diff_count[0]++].mv.mv[0] = cand_mv;
275
24.5k
            }
276
28.0k
        } else {
277
20.0k
            mv i_cand_mv = (union mv) {
278
20.0k
                .x = -cand_mv.x,
279
20.0k
                .y = -cand_mv.y
280
20.0k
            };
281
282
20.0k
            if (diff_count[0] < 2) {
283
16.7k
                diff[diff_count[0]++].mv.mv[0] =
284
16.7k
                    sign0 ^ sign_bias[cand_ref - 1] ?
285
16.6k
                    i_cand_mv : cand_mv;
286
16.7k
            }
287
288
20.0k
            if (diff_count[1] < 2) {
289
16.0k
                diff[diff_count[1]++].mv.mv[1] =
290
16.0k
                    sign1 ^ sign_bias[cand_ref - 1] ?
291
15.8k
                    i_cand_mv : cand_mv;
292
16.0k
            }
293
20.0k
        }
294
76.7k
    }
295
49.2k
}
296
297
static void add_single_extended_candidate(refmvs_candidate mvstack[8], int *const cnt,
298
                                          const refmvs_block *const cand_b,
299
                                          const int sign, const uint8_t *const sign_bias)
300
208k
{
301
414k
    for (int n = 0; n < 2; n++) {
302
404k
        const int cand_ref = cand_b->ref.ref[n];
303
304
404k
        if (cand_ref <= 0) break;
305
        // we need to continue even if cand_ref == ref.ref[0], since
306
        // the candidate could have been added as a globalmv variant,
307
        // which changes the value
308
        // FIXME if scan_{row,col}() returned a mask for the nearest
309
        // edge, we could skip the appropriate ones here
310
311
206k
        mv cand_mv = cand_b->mv.mv[n];
312
206k
        if (sign ^ sign_bias[cand_ref - 1]) {
313
492
            cand_mv.y = -cand_mv.y;
314
492
            cand_mv.x = -cand_mv.x;
315
492
        }
316
317
206k
        int m;
318
206k
        const int last = *cnt;
319
231k
        for (m = 0; m < last; m++)
320
187k
            if (cand_mv.n == mvstack[m].mv.mv[0].n)
321
162k
                break;
322
206k
        if (m == last) {
323
43.5k
            mvstack[m].mv.mv[0] = cand_mv;
324
43.5k
            mvstack[m].weight = 2; // "minimal"
325
43.5k
            *cnt = last + 1;
326
43.5k
        }
327
206k
    }
328
208k
}
329
330
/*
331
 * refmvs_frame allocates memory for one sbrow (32 blocks high, whole frame
332
 * wide) of 4x4-resolution refmvs_block entries for spatial MV referencing.
333
 * mvrefs_tile[] keeps a list of 35 (32 + 3 above) pointers into this memory,
334
 * and each sbrow, the bottom entries (y=27/29/31) are exchanged with the top
335
 * (-5/-3/-1) pointers by calling dav1d_refmvs_tile_sbrow_init() at the start
336
 * of each tile/sbrow.
337
 *
338
 * For temporal MV referencing, we call dav1d_refmvs_save_tmvs() at the end of
339
 * each tile/sbrow (when tile column threading is enabled), or at the start of
340
 * each interleaved sbrow (i.e. once for all tile columns together, when tile
341
 * column threading is disabled). This will copy the 4x4-resolution spatial MVs
342
 * into 8x8-resolution refmvs_temporal_block structures. Then, for subsequent
343
 * frames, at the start of each tile/sbrow (when tile column threading is
344
 * enabled) or at the start of each interleaved sbrow (when tile column
345
 * threading is disabled), we call load_tmvs(), which will project the MVs to
346
 * their respective position in the current frame.
347
 */
348
349
void dav1d_refmvs_find(const refmvs_tile *const rt,
350
                       refmvs_candidate mvstack[8], int *const cnt,
351
                       int *const ctx,
352
                       const union refmvs_refpair ref, const enum BlockSize bs,
353
                       const enum EdgeFlags edge_flags,
354
                       const int by4, const int bx4)
355
564k
{
356
564k
    const refmvs_frame *const rf = rt->rf;
357
564k
    const uint8_t *const b_dim = dav1d_block_dimensions[bs];
358
564k
    const int bw4 = b_dim[0], w4 = imin(imin(bw4, 16), rt->tile_col.end - bx4);
359
564k
    const int bh4 = b_dim[1], h4 = imin(imin(bh4, 16), rt->tile_row.end - by4);
360
564k
    mv gmv[2], tgmv[2];
361
362
564k
    *cnt = 0;
363
564k
    assert(ref.ref[0] >=  0 && ref.ref[0] <= 8 &&
364
564k
           ref.ref[1] >= -1 && ref.ref[1] <= 8);
365
564k
    if (ref.ref[0] > 0) {
366
270k
        tgmv[0] = get_gmv_2d(&rf->frm_hdr->gmv[ref.ref[0] - 1],
367
270k
                             bx4, by4, bw4, bh4, rf->frm_hdr);
368
270k
        gmv[0] = rf->frm_hdr->gmv[ref.ref[0] - 1].type > DAV1D_WM_TYPE_TRANSLATION ?
369
198k
                 tgmv[0] : (mv) { .n = INVALID_MV };
370
293k
    } else {
371
293k
        tgmv[0] = (mv) { .n = 0 };
372
293k
        gmv[0] = (mv) { .n = INVALID_MV };
373
293k
    }
374
564k
    if (ref.ref[1] > 0) {
375
42.7k
        tgmv[1] = get_gmv_2d(&rf->frm_hdr->gmv[ref.ref[1] - 1],
376
42.7k
                             bx4, by4, bw4, bh4, rf->frm_hdr);
377
42.7k
        gmv[1] = rf->frm_hdr->gmv[ref.ref[1] - 1].type > DAV1D_WM_TYPE_TRANSLATION ?
378
30.8k
                 tgmv[1] : (mv) { .n = INVALID_MV };
379
42.7k
    }
380
381
    // top
382
564k
    int have_newmv = 0, have_col_mvs = 0, have_row_mvs = 0;
383
564k
    unsigned max_rows = 0, n_rows = ~0;
384
564k
    const refmvs_block *b_top;
385
564k
    if (by4 > rt->tile_row.start) {
386
327k
        max_rows = imin((by4 - rt->tile_row.start + 1) >> 1, 2 + (bh4 > 1));
387
327k
        b_top = &rt->r[(by4 & 31) - 1 + 5][bx4];
388
327k
        n_rows = scan_row(mvstack, cnt, ref, gmv, b_top,
389
327k
                          bw4, w4, max_rows, bw4 >= 16 ? 4 : 1,
390
327k
                          &have_newmv, &have_row_mvs);
391
327k
    }
392
393
    // left
394
564k
    unsigned max_cols = 0, n_cols = ~0U;
395
564k
    refmvs_block *const *b_left;
396
564k
    if (bx4 > rt->tile_col.start) {
397
494k
        max_cols = imin((bx4 - rt->tile_col.start + 1) >> 1, 2 + (bw4 > 1));
398
494k
        b_left = &rt->r[(by4 & 31) + 5];
399
494k
        n_cols = scan_col(mvstack, cnt, ref, gmv, b_left,
400
494k
                          bh4, h4, bx4 - 1, max_cols, bh4 >= 16 ? 4 : 1,
401
494k
                          &have_newmv, &have_col_mvs);
402
494k
    }
403
404
    // top/right
405
564k
    if (n_rows != ~0U && edge_flags & EDGE_I444_TOP_HAS_RIGHT &&
406
196k
        imax(bw4, bh4) <= 16 && bw4 + bx4 < rt->tile_col.end)
407
147k
    {
408
147k
        add_spatial_candidate(mvstack, cnt, 4, &b_top[bw4], ref, gmv,
409
147k
                              &have_newmv, &have_row_mvs);
410
147k
    }
411
412
564k
    const int nearest_match = have_col_mvs + have_row_mvs;
413
564k
    const int nearest_cnt = *cnt;
414
1.29M
    for (int n = 0; n < nearest_cnt; n++)
415
731k
        mvstack[n].weight += 640;
416
417
    // temporal
418
564k
    int globalmv_ctx = rf->frm_hdr->use_ref_frame_mvs;
419
564k
    if (rf->use_ref_frame_mvs) {
420
5.72k
        const ptrdiff_t stride = rf->rp_stride;
421
5.72k
        const int by8 = by4 >> 1, bx8 = bx4 >> 1;
422
5.72k
        const refmvs_temporal_block *const rbi = &rt->rp_proj[(by8 & 15) * stride + bx8];
423
5.72k
        const refmvs_temporal_block *rb = rbi;
424
5.72k
        const int step_h = bw4 >= 16 ? 2 : 1, step_v = bh4 >= 16 ? 2 : 1;
425
5.72k
        const int w8 = imin((w4 + 1) >> 1, 8), h8 = imin((h4 + 1) >> 1, 8);
426
        // save_tmvs() writes one record per 8x8 cell of a coded block, so the
427
        // scan walks runs of bit-identical records. add_temporal_candidate()
428
        // is a pure function of (rb->mv, rb->ref) plus loop invariants, and on
429
        // a repeat its only effect is `weight += 2` on the same stack entry
430
        // the first call of the run inserted or merged into -- the first match
431
        // is stable because a repeat never inserts. So a run of `n` identical
432
        // records is exactly one call carrying weight 2*n.
433
        //
434
        // Two records with mv.n == INVALID_MV belong to the same run whatever
435
        // their .ref holds: add_temporal_candidate() returns before reading
436
        // .ref, so both are no-ops. The check must be written that way round
437
        // rather than merely being allowed to be, because load_tmvs() marks a
438
        // cell unprojected by writing .mv.n alone and leaves .ref uninitialized.
439
5.72k
        const refmvs_temporal_block *run = &rb[0];
440
5.72k
        int run_len = 1;
441
5.72k
        int *run_gmv_ctx = &globalmv_ctx;
442
17.5k
        for (int y = 0; y < h8; y += step_v) {
443
24.4k
            for (int x = y ? 0 : step_h; x < w8; x += step_h) {
444
12.5k
                const unsigned mvn = rb[x].mv.n;
445
12.5k
                if (mvn == run->mv.n &&
446
12.1k
                    (mvn == INVALID_MV || rb[x].ref == run->ref))
447
12.1k
                {
448
12.1k
                    run_len++;
449
12.1k
                    continue;
450
12.1k
                }
451
396
                add_temporal_candidate(rf, mvstack, cnt, run, ref,
452
396
                                       run_gmv_ctx, tgmv, 2 * run_len);
453
396
                run_gmv_ctx = NULL;
454
396
                run = &rb[x];
455
396
                run_len = 1;
456
396
            }
457
11.8k
            rb += stride * step_v;
458
11.8k
        }
459
5.72k
        add_temporal_candidate(rf, mvstack, cnt, run, ref,
460
5.72k
                               run_gmv_ctx, tgmv, 2 * run_len);
461
5.72k
        if (imin(bw4, bh4) >= 2 && imax(bw4, bh4) < 16) {
462
3.93k
            const int bh8 = bh4 >> 1, bw8 = bw4 >> 1;
463
3.93k
            rb = &rbi[bh8 * stride];
464
3.93k
            const int has_bottom = by8 + bh8 < imin(rt->tile_row.end >> 1,
465
3.93k
                                                    (by8 & ~7) + 8);
466
3.93k
            if (has_bottom && bx8 - 1 >= imax(rt->tile_col.start >> 1, bx8 & ~7)) {
467
1.63k
                add_temporal_candidate(rf, mvstack, cnt, &rb[-1], ref,
468
1.63k
                                       NULL, NULL, 2);
469
1.63k
            }
470
3.93k
            if (bx8 + bw8 < imin(rt->tile_col.end >> 1, (bx8 & ~7) + 8)) {
471
2.49k
                if (has_bottom) {
472
1.59k
                    add_temporal_candidate(rf, mvstack, cnt, &rb[bw8], ref,
473
1.59k
                                           NULL, NULL, 2);
474
1.59k
                }
475
2.49k
                if (by8 + bh8 - 1 < imin(rt->tile_row.end >> 1, (by8 & ~7) + 8)) {
476
2.27k
                    add_temporal_candidate(rf, mvstack, cnt, &rb[bw8 - stride],
477
2.27k
                                           ref, NULL, NULL, 2);
478
2.27k
                }
479
2.49k
            }
480
3.93k
        }
481
5.72k
    }
482
564k
    assert(*cnt <= 8);
483
484
    // top/left (which, confusingly, is part of "secondary" references)
485
564k
    int have_dummy_newmv_match;
486
564k
    if ((n_rows | n_cols) != ~0U) {
487
266k
        add_spatial_candidate(mvstack, cnt, 4, &b_top[-1], ref, gmv,
488
266k
                              &have_dummy_newmv_match, &have_row_mvs);
489
266k
    }
490
491
    // "secondary" (non-direct neighbour) top & left edges
492
    // what is different about secondary is that everything is now in 8x8 resolution
493
1.69M
    for (int n = 2; n <= 3; n++) {
494
1.12M
        if ((unsigned) n > n_rows && (unsigned) n <= max_rows) {
495
172k
            n_rows += scan_row(mvstack, cnt, ref, gmv,
496
172k
                               &rt->r[(((by4 & 31) - 2 * n + 1) | 1) + 5][bx4 | 1],
497
172k
                               bw4, w4, 1 + max_rows - n, bw4 >= 16 ? 4 : 2,
498
172k
                               &have_dummy_newmv_match, &have_row_mvs);
499
172k
        }
500
501
1.12M
        if ((unsigned) n > n_cols && (unsigned) n <= max_cols) {
502
492k
            n_cols += scan_col(mvstack, cnt, ref, gmv, &rt->r[((by4 & 31) | 1) + 5],
503
492k
                               bh4, h4, (bx4 - n * 2 + 1) | 1,
504
492k
                               1 + max_cols - n, bh4 >= 16 ? 4 : 2,
505
492k
                               &have_dummy_newmv_match, &have_col_mvs);
506
492k
        }
507
1.12M
    }
508
564k
    assert(*cnt <= 8);
509
510
564k
    const int ref_match_count = have_col_mvs + have_row_mvs;
511
512
    // context build-up
513
564k
    int refmv_ctx, newmv_ctx;
514
564k
    switch (nearest_match) {
515
73.0k
    case 0:
516
73.0k
        refmv_ctx = imin(2, ref_match_count);
517
73.0k
        newmv_ctx = ref_match_count > 0;
518
73.0k
        break;
519
283k
    case 1:
520
283k
        refmv_ctx = imin(ref_match_count * 3, 4);
521
283k
        newmv_ctx = 3 - have_newmv;
522
283k
        break;
523
208k
    case 2:
524
208k
        refmv_ctx = 5;
525
208k
        newmv_ctx = 5 - have_newmv;
526
208k
        break;
527
564k
    }
528
529
    // sorting (nearest, then "secondary")
530
564k
    int len = nearest_cnt;
531
1.19M
    while (len) {
532
629k
        int last = 0;
533
932k
        for (int n = 1; n < len; n++) {
534
302k
            if (mvstack[n - 1].weight < mvstack[n].weight) {
535
143k
#define EXCHANGE(a, b) do { refmvs_candidate tmp = a; a = b; b = tmp; } while (0)
536
140k
                EXCHANGE(mvstack[n - 1], mvstack[n]);
537
140k
                last = n;
538
140k
            }
539
302k
        }
540
629k
        len = last;
541
629k
    }
542
564k
    len = *cnt;
543
906k
    while (len > nearest_cnt) {
544
342k
        int last = nearest_cnt;
545
486k
        for (int n = nearest_cnt + 1; n < len; n++) {
546
144k
            if (mvstack[n - 1].weight < mvstack[n].weight) {
547
3.35k
                EXCHANGE(mvstack[n - 1], mvstack[n]);
548
3.35k
#undef EXCHANGE
549
3.35k
                last = n;
550
3.35k
            }
551
144k
        }
552
342k
        len = last;
553
342k
    }
554
555
564k
    if (ref.ref[1] > 0) {
556
42.7k
        if (*cnt < 2) {
557
33.3k
            const int sign0 = rf->sign_bias[ref.ref[0] - 1];
558
33.3k
            const int sign1 = rf->sign_bias[ref.ref[1] - 1];
559
33.3k
            const int sz4 = imin(w4, h4);
560
33.3k
            refmvs_candidate *const same = &mvstack[*cnt];
561
33.3k
            int same_count[4] = { 0 };
562
563
            // non-self references in top
564
40.0k
            if (n_rows != ~0U) for (int x = 0; x < sz4;) {
565
20.8k
                const refmvs_block *const cand_b = &b_top[x];
566
20.8k
                add_compound_extended_candidate(same, same_count, cand_b,
567
20.8k
                                                sign0, sign1, ref, rf->sign_bias);
568
20.8k
                x += dav1d_block_dimensions[cand_b->bs][0];
569
20.8k
            }
570
571
            // non-self references in left
572
54.3k
            if (n_cols != ~0U) for (int y = 0; y < sz4;) {
573
28.4k
                const refmvs_block *const cand_b = &b_left[y][bx4 - 1];
574
28.4k
                add_compound_extended_candidate(same, same_count, cand_b,
575
28.4k
                                                sign0, sign1, ref, rf->sign_bias);
576
28.4k
                y += dav1d_block_dimensions[cand_b->bs][1];
577
28.4k
            }
578
579
33.3k
            refmvs_candidate *const diff = &same[2];
580
33.3k
            const int *const diff_count = &same_count[2];
581
582
            // merge together
583
99.9k
            for (int n = 0; n < 2; n++) {
584
66.6k
                int m = same_count[n];
585
586
66.6k
                if (m >= 2) continue;
587
588
55.7k
                const int l = diff_count[n];
589
55.7k
                if (l) {
590
46.6k
                    same[m].mv.mv[n] = diff[0].mv.mv[n];
591
46.6k
                    if (++m == 2) continue;
592
17.3k
                    if (l == 2) {
593
10.7k
                        same[1].mv.mv[n] = diff[1].mv.mv[n];
594
10.7k
                        continue;
595
10.7k
                    }
596
17.3k
                }
597
20.3k
                do {
598
20.3k
                    same[m].mv.mv[n] = tgmv[n];
599
20.3k
                } while (++m < 2);
600
15.8k
            }
601
602
            // if the first extended was the same as the non-extended one,
603
            // then replace it with the second extended one
604
33.3k
            int n = *cnt;
605
33.3k
            if (n == 1 && mvstack[0].mv.n == same[0].mv.n)
606
12.3k
                mvstack[1].mv = mvstack[2].mv;
607
51.0k
            do {
608
51.0k
                mvstack[n].weight = 2;
609
51.0k
            } while (++n < 2);
610
33.3k
            *cnt = 2;
611
33.3k
        }
612
613
        // clamping
614
42.7k
        const int left = -(bx4 + bw4 + 4) * 4 * 8;
615
42.7k
        const int right = (rf->iw4 - bx4 + 4) * 4 * 8;
616
42.7k
        const int top = -(by4 + bh4 + 4) * 4 * 8;
617
42.7k
        const int bottom = (rf->ih4 - by4 + 4) * 4 * 8;
618
619
42.7k
        const int n_refmvs = *cnt;
620
42.7k
        int n = 0;
621
89.3k
        do {
622
89.3k
            mvstack[n].mv.mv[0].x = iclip(mvstack[n].mv.mv[0].x, left, right);
623
89.3k
            mvstack[n].mv.mv[0].y = iclip(mvstack[n].mv.mv[0].y, top, bottom);
624
89.3k
            mvstack[n].mv.mv[1].x = iclip(mvstack[n].mv.mv[1].x, left, right);
625
89.3k
            mvstack[n].mv.mv[1].y = iclip(mvstack[n].mv.mv[1].y, top, bottom);
626
89.3k
        } while (++n < n_refmvs);
627
628
42.7k
        switch (refmv_ctx >> 1) {
629
19.4k
        case 0:
630
19.4k
            *ctx = imin(newmv_ctx, 1);
631
19.4k
            break;
632
17.7k
        case 1:
633
17.7k
            *ctx = 1 + imin(newmv_ctx, 3);
634
17.7k
            break;
635
5.57k
        case 2:
636
5.57k
            *ctx = iclip(3 + newmv_ctx, 4, 7);
637
5.57k
            break;
638
42.7k
        }
639
640
42.7k
        return;
641
521k
    } else if (*cnt < 2 && ref.ref[0] > 0) {
642
156k
        const int sign = rf->sign_bias[ref.ref[0] - 1];
643
156k
        const int sz4 = imin(w4, h4);
644
645
        // non-self references in top
646
180k
        if (n_rows != ~0U) for (int x = 0; x < sz4 && *cnt < 2;) {
647
91.6k
            const refmvs_block *const cand_b = &b_top[x];
648
91.6k
            add_single_extended_candidate(mvstack, cnt, cand_b, sign, rf->sign_bias);
649
91.6k
            x += dav1d_block_dimensions[cand_b->bs][0];
650
91.6k
        }
651
652
        // non-self references in left
653
237k
        if (n_cols != ~0U) for (int y = 0; y < sz4 && *cnt < 2;) {
654
117k
            const refmvs_block *const cand_b = &b_left[y][bx4 - 1];
655
117k
            add_single_extended_candidate(mvstack, cnt, cand_b, sign, rf->sign_bias);
656
117k
            y += dav1d_block_dimensions[cand_b->bs][1];
657
117k
        }
658
156k
    }
659
521k
    assert(*cnt <= 8);
660
661
    // clamping
662
521k
    int n_refmvs = *cnt;
663
521k
    if (n_refmvs) {
664
498k
        const int left = -(bx4 + bw4 + 4) * 4 * 8;
665
498k
        const int right = (rf->iw4 - bx4 + 4) * 4 * 8;
666
498k
        const int top = -(by4 + bh4 + 4) * 4 * 8;
667
498k
        const int bottom = (rf->ih4 - by4 + 4) * 4 * 8;
668
669
498k
        int n = 0;
670
1.21M
        do {
671
1.21M
            mvstack[n].mv.mv[0].x = iclip(mvstack[n].mv.mv[0].x, left, right);
672
1.21M
            mvstack[n].mv.mv[0].y = iclip(mvstack[n].mv.mv[0].y, top, bottom);
673
1.21M
        } while (++n < n_refmvs);
674
498k
    }
675
676
699k
    for (int n = *cnt; n < 2; n++)
677
178k
        mvstack[n].mv.mv[0] = tgmv[0];
678
679
521k
    *ctx = (refmv_ctx << 4) | (globalmv_ctx << 3) | newmv_ctx;
680
521k
}
681
682
void dav1d_refmvs_tile_sbrow_init(refmvs_tile *const rt, const refmvs_frame *const rf,
683
                                  const int tile_col_start4, const int tile_col_end4,
684
                                  const int tile_row_start4, const int tile_row_end4,
685
                                  const int sby, int tile_row_idx, const int pass)
686
86.4k
{
687
86.4k
    if (rf->n_tile_threads == 1) tile_row_idx = 0;
688
86.4k
    rt->rp_proj = &rf->rp_proj[16 * rf->rp_stride * tile_row_idx];
689
86.4k
    const ptrdiff_t r_stride = rf->rp_stride * 2;
690
86.4k
    const ptrdiff_t pass_off = (rf->n_frame_threads > 1 && pass == 2) ?
691
46.1k
        35 * 2 * rf->n_blocks : 0;
692
86.4k
    refmvs_block *r = &rf->r[35 * r_stride * tile_row_idx + pass_off];
693
86.4k
    const int sbsz = rf->sbsz;
694
86.4k
    const int off = (sbsz * sby) & 16;
695
1.87M
    for (int i = 0; i < sbsz; i++, r += r_stride)
696
1.78M
        rt->r[off + 5 + i] = r;
697
86.4k
    rt->r[off + 0] = r;
698
86.4k
    r += r_stride;
699
86.4k
    rt->r[off + 1] = NULL;
700
86.4k
    rt->r[off + 2] = r;
701
86.4k
    r += r_stride;
702
86.4k
    rt->r[off + 3] = NULL;
703
86.4k
    rt->r[off + 4] = r;
704
86.4k
    if (sby & 1) {
705
102k
#define EXCHANGE(a, b) do { void *const tmp = a; a = b; b = tmp; } while (0)
706
34.1k
        EXCHANGE(rt->r[off + 0], rt->r[off + sbsz + 0]);
707
34.1k
        EXCHANGE(rt->r[off + 2], rt->r[off + sbsz + 2]);
708
34.1k
        EXCHANGE(rt->r[off + 4], rt->r[off + sbsz + 4]);
709
34.1k
#undef EXCHANGE
710
34.1k
    }
711
712
86.4k
    rt->rf = rf;
713
86.4k
    rt->tile_row.start = tile_row_start4;
714
86.4k
    rt->tile_row.end = imin(tile_row_end4, rf->ih4);
715
86.4k
    rt->tile_col.start = tile_col_start4;
716
86.4k
    rt->tile_col.end = imin(tile_col_end4, rf->iw4);
717
86.4k
}
718
719
static void load_tmvs_c(const refmvs_frame *const rf, int tile_row_idx,
720
                        const int col_start8, const int col_end8,
721
                        const int row_start8, int row_end8)
722
3.15k
{
723
3.15k
    if (rf->n_tile_threads == 1) tile_row_idx = 0;
724
3.15k
    assert(row_start8 >= 0);
725
3.15k
    assert((unsigned) (row_end8 - row_start8) <= 16U);
726
3.15k
    row_end8 = imin(row_end8, rf->ih8);
727
3.15k
    const int col_start8i = imax(col_start8 - 8, 0);
728
3.15k
    const int col_end8i = imin(col_end8 + 8, rf->iw8);
729
730
3.15k
    const ptrdiff_t stride = rf->rp_stride;
731
3.15k
    refmvs_temporal_block *rp_proj =
732
3.15k
        &rf->rp_proj[16 * stride * tile_row_idx + (row_start8 & 15) * stride];
733
25.4k
    for (int y = row_start8; y < row_end8; y++) {
734
132k
        for (int x = col_start8; x < col_end8; x++)
735
109k
            rp_proj[x].mv.n = INVALID_MV;
736
22.2k
        rp_proj += stride;
737
22.2k
    }
738
739
3.15k
    rp_proj = &rf->rp_proj[16 * stride * tile_row_idx];
740
4.59k
    for (int n = 0; n < rf->n_mfmvs; n++) {
741
1.44k
        const int ref2cur = rf->mfmv_ref2cur[n];
742
1.44k
        if (ref2cur == INVALID_REF2CUR) continue;
743
744
1.34k
        const int ref = rf->mfmv_ref[n];
745
1.34k
        const int ref_sign = ref - 4;
746
1.34k
        const refmvs_temporal_block *r = &rf->rp_ref[ref][row_start8 * stride];
747
8.31k
        for (int y = row_start8; y < row_end8; y++) {
748
6.97k
            const int y_sb_align = y & ~7;
749
6.97k
            const int y_proj_start = imax(y_sb_align, row_start8);
750
6.97k
            const int y_proj_end = imin(y_sb_align + 8, row_end8);
751
19.1k
            for (int x = col_start8i; x < col_end8i; x++) {
752
12.2k
                const refmvs_temporal_block *rb = &r[x];
753
12.2k
                const int b_ref = rb->ref;
754
12.2k
                if (!b_ref) continue;
755
7.07k
                const int ref2ref = rf->mfmv_ref2ref[n][b_ref - 1];
756
7.07k
                if (!ref2ref) continue;
757
5.78k
                const mv b_mv = rb->mv;
758
5.78k
                const mv offset = mv_projection(b_mv, ref2cur, ref2ref);
759
5.78k
                int pos_x = x + apply_sign(abs(offset.x) >> 6,
760
5.78k
                                           offset.x ^ ref_sign);
761
5.78k
                const int pos_y = y + apply_sign(abs(offset.y) >> 6,
762
5.78k
                                                 offset.y ^ ref_sign);
763
5.78k
                if (pos_y >= y_proj_start && pos_y < y_proj_end) {
764
5.48k
                    const ptrdiff_t pos = (pos_y & 15) * stride;
765
29.0k
                    for (;;) {
766
29.0k
                        const int x_sb_align = x & ~7;
767
29.0k
                        if (pos_x >= imax(x_sb_align - 8, col_start8) &&
768
28.7k
                            pos_x < imin(x_sb_align + 16, col_end8))
769
28.2k
                        {
770
28.2k
                            rp_proj[pos + pos_x].mv = rb->mv;
771
28.2k
                            rp_proj[pos + pos_x].ref = ref2ref;
772
28.2k
                        }
773
29.0k
                        if (++x >= col_end8i) break;
774
24.1k
                        rb++;
775
24.1k
                        if (rb->ref != b_ref || rb->mv.n != b_mv.n) break;
776
23.6k
                        pos_x++;
777
23.6k
                    }
778
5.48k
                } else {
779
767
                    for (;;) {
780
767
                        if (++x >= col_end8i) break;
781
592
                        rb++;
782
592
                        if (rb->ref != b_ref || rb->mv.n != b_mv.n) break;
783
592
                    }
784
296
                }
785
5.78k
                x--;
786
5.78k
            }
787
6.97k
            r += stride;
788
6.97k
        }
789
1.34k
    }
790
3.15k
}
791
792
static void save_tmvs_c(refmvs_temporal_block *rp, const ptrdiff_t stride,
793
                        refmvs_block *const *const rr,
794
                        const uint8_t *const ref_sign,
795
                        const int col_end8, const int row_end8,
796
                        const int col_start8, const int row_start8)
797
7.58k
{
798
57.4k
    for (int y = row_start8; y < row_end8; y++) {
799
49.8k
        const refmvs_block *const b = rr[(y & 15) * 2];
800
801
196k
        for (int x = col_start8; x < col_end8;) {
802
146k
            const refmvs_block *const cand_b = &b[x * 2 + 1];
803
146k
            const int bw8 = (dav1d_block_dimensions[cand_b->bs][0] + 1) >> 1;
804
805
146k
            if (cand_b->ref.ref[1] > 0 && ref_sign[cand_b->ref.ref[1] - 1] &&
806
23.9k
                (abs(cand_b->mv.mv[1].y) | abs(cand_b->mv.mv[1].x)) < 4096)
807
21.7k
            {
808
21.7k
                const refmvs_temporal_block tmv = {
809
21.7k
                    .mv = cand_b->mv.mv[1],
810
21.7k
                    .ref = cand_b->ref.ref[1],
811
21.7k
                };
812
75.3k
                for (int n = 0; n < bw8; n++, x++)
813
53.5k
                    rp[x] = tmv;
814
125k
            } else if (cand_b->ref.ref[0] > 0 && ref_sign[cand_b->ref.ref[0] - 1] &&
815
63.9k
                       (abs(cand_b->mv.mv[0].y) | abs(cand_b->mv.mv[0].x)) < 4096)
816
61.1k
            {
817
61.1k
                const refmvs_temporal_block tmv = {
818
61.1k
                    .mv = cand_b->mv.mv[0],
819
61.1k
                    .ref = cand_b->ref.ref[0],
820
61.1k
                };
821
216k
                for (int n = 0; n < bw8; n++, x++)
822
155k
                    rp[x] = tmv;
823
63.9k
            } else {
824
63.9k
                memset(&rp[x], 0, bw8 * sizeof(*rp));
825
63.9k
                x += bw8;
826
63.9k
            }
827
146k
        }
828
49.8k
        rp += stride;
829
49.8k
    }
830
7.58k
}
831
832
int dav1d_refmvs_init_frame(refmvs_frame *const rf,
833
                            const Dav1dSequenceHeader *const seq_hdr,
834
                            const Dav1dFrameHeader *const frm_hdr,
835
                            const uint8_t ref_poc[7],
836
                            refmvs_temporal_block *const rp,
837
                            const uint8_t ref_ref_poc[7][7],
838
                            /*const*/ refmvs_temporal_block *const rp_ref[7],
839
                            const int n_tile_threads, const int n_frame_threads)
840
14.3k
{
841
14.3k
    const int rp_stride = ((frm_hdr->width[0] + 127) & ~127) >> 3;
842
14.3k
    const int n_tile_rows = n_tile_threads > 1 ? frm_hdr->tiling.rows : 1;
843
14.3k
    const int n_blocks = rp_stride * n_tile_rows;
844
845
14.3k
    rf->sbsz = 16 << seq_hdr->sb128;
846
14.3k
    rf->frm_hdr = frm_hdr;
847
14.3k
    rf->iw8 = (frm_hdr->width[0] + 7) >> 3;
848
14.3k
    rf->ih8 = (frm_hdr->height + 7) >> 3;
849
14.3k
    rf->iw4 = rf->iw8 << 1;
850
14.3k
    rf->ih4 = rf->ih8 << 1;
851
14.3k
    rf->rp = rp;
852
14.3k
    rf->rp_stride = rp_stride;
853
14.3k
    rf->n_tile_threads = n_tile_threads;
854
14.3k
    rf->n_frame_threads = n_frame_threads;
855
856
14.3k
    if (n_blocks != rf->n_blocks) {
857
14.3k
        const size_t r_sz = sizeof(*rf->r) * 35 * 2 * n_blocks * (1 + (n_frame_threads > 1));
858
14.3k
        const size_t rp_proj_sz = sizeof(*rf->rp_proj) * 16 * n_blocks;
859
        /* Note that sizeof(*rf->r) == 12, but it's accessed using 16-byte unaligned
860
         * loads in save_tmvs() asm which can overread 4 bytes into rp_proj. */
861
14.3k
        dav1d_free_aligned(rf->r);
862
14.3k
        rf->r = dav1d_alloc_aligned(ALLOC_REFMVS, r_sz + rp_proj_sz, 64);
863
14.3k
        if (!rf->r) {
864
0
            rf->n_blocks = 0;
865
0
            return DAV1D_ERR(ENOMEM);
866
0
        }
867
868
14.3k
        rf->rp_proj = (refmvs_temporal_block*)((uintptr_t)rf->r + r_sz);
869
14.3k
        rf->n_blocks = n_blocks;
870
14.3k
    }
871
872
14.3k
    const int poc = frm_hdr->frame_offset;
873
114k
    for (int i = 0; i < 7; i++) {
874
100k
        const int poc_diff = get_poc_diff(seq_hdr->order_hint_n_bits,
875
100k
                                          ref_poc[i], poc);
876
100k
        rf->sign_bias[i] = poc_diff > 0;
877
100k
        rf->mfmv_sign[i] = poc_diff < 0;
878
100k
        rf->pocdiff[i] = iclip(get_poc_diff(seq_hdr->order_hint_n_bits,
879
100k
                                            poc, ref_poc[i]), -31, 31);
880
100k
    }
881
882
    // temporal MV setup
883
14.3k
    rf->n_mfmvs = 0;
884
14.3k
    rf->rp_ref = rp_ref;
885
14.3k
    if (frm_hdr->use_ref_frame_mvs && seq_hdr->order_hint_n_bits) {
886
2.33k
        int total = 2;
887
2.33k
        if (rp_ref[0] && ref_ref_poc[0][6] != ref_poc[3] /* alt-of-last != gold */) {
888
354
            rf->mfmv_ref[rf->n_mfmvs++] = 0; // last
889
354
            total = 3;
890
354
        }
891
2.33k
        if (rp_ref[4] && get_poc_diff(seq_hdr->order_hint_n_bits, ref_poc[4],
892
511
                                      frm_hdr->frame_offset) > 0)
893
187
        {
894
187
            rf->mfmv_ref[rf->n_mfmvs++] = 4; // bwd
895
187
        }
896
2.33k
        if (rp_ref[5] && get_poc_diff(seq_hdr->order_hint_n_bits, ref_poc[5],
897
622
                                      frm_hdr->frame_offset) > 0)
898
202
        {
899
202
            rf->mfmv_ref[rf->n_mfmvs++] = 5; // altref2
900
202
        }
901
2.33k
        if (rf->n_mfmvs < total && rp_ref[6] &&
902
351
            get_poc_diff(seq_hdr->order_hint_n_bits, ref_poc[6],
903
351
                         frm_hdr->frame_offset) > 0)
904
101
        {
905
101
            rf->mfmv_ref[rf->n_mfmvs++] = 6; // altref
906
101
        }
907
2.33k
        if (rf->n_mfmvs < total && rp_ref[1])
908
442
            rf->mfmv_ref[rf->n_mfmvs++] = 1; // last2
909
910
3.62k
        for (int n = 0; n < rf->n_mfmvs; n++) {
911
1.28k
            const int rpoc = ref_poc[rf->mfmv_ref[n]];
912
1.28k
            const int diff1 = get_poc_diff(seq_hdr->order_hint_n_bits,
913
1.28k
                                           rpoc, frm_hdr->frame_offset);
914
1.28k
            if (abs(diff1) > 31) {
915
115
                rf->mfmv_ref2cur[n] = INVALID_REF2CUR;
916
1.17k
            } else {
917
1.17k
                rf->mfmv_ref2cur[n] = rf->mfmv_ref[n] < 4 ? -diff1 : diff1;
918
9.36k
                for (int m = 0; m < 7; m++) {
919
8.19k
                    const int rrpoc = ref_ref_poc[rf->mfmv_ref[n]][m];
920
8.19k
                    const int diff2 = get_poc_diff(seq_hdr->order_hint_n_bits,
921
8.19k
                                                   rpoc, rrpoc);
922
                    // unsigned comparison also catches the < 0 case
923
8.19k
                    rf->mfmv_ref2ref[n][m] = (unsigned) diff2 > 31U ? 0 : diff2;
924
8.19k
                }
925
1.17k
            }
926
1.28k
        }
927
2.33k
    }
928
14.3k
    rf->use_ref_frame_mvs = rf->n_mfmvs > 0;
929
930
14.3k
    return 0;
931
14.3k
}
932
933
static void splat_mv_c(refmvs_block **rr, const refmvs_block *const rmv,
934
                       const int bx4, const int bw4, int bh4)
935
769k
{
936
2.33M
    do {
937
2.33M
        refmvs_block *const r = *rr++ + bx4;
938
19.0M
        for (int x = 0; x < bw4; x++)
939
16.7M
            r[x] = *rmv;
940
2.33M
    } while (--bh4);
941
769k
}
942
943
#if HAVE_ASM
944
#if ARCH_AARCH64 || ARCH_ARM
945
#include "src/arm/refmvs.h"
946
#elif ARCH_LOONGARCH64
947
#include "src/loongarch/refmvs.h"
948
#elif ARCH_X86
949
#include "src/x86/refmvs.h"
950
#endif
951
#endif
952
953
COLD void dav1d_refmvs_dsp_init(Dav1dRefmvsDSPContext *const c)
954
17.1k
{
955
17.1k
    c->load_tmvs = load_tmvs_c;
956
17.1k
    c->save_tmvs = save_tmvs_c;
957
17.1k
    c->splat_mv = splat_mv_c;
958
959
#if HAVE_ASM
960
#if ARCH_AARCH64 || ARCH_ARM
961
    refmvs_dsp_init_arm(c);
962
#elif ARCH_LOONGARCH64
963
    refmvs_dsp_init_loongarch(c);
964
#elif ARCH_X86
965
    refmvs_dsp_init_x86(c);
966
#endif
967
#endif
968
17.1k
}