Coverage Report

Created: 2026-10-17 06:50

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.02M
{
48
1.02M
    if (b->mv.mv[0].n == INVALID_MV) return; // intra block, no intrabc
49
50
944k
    if (ref.ref[1] == -1) {
51
1.25M
        for (int n = 0; n < 2; n++) {
52
1.02M
            if (b->ref.ref[n] == ref.ref[0]) {
53
562k
                const mv cand_mv = ((b->mf & 1) && gmv[0].n != INVALID_MV) ?
54
545k
                                   gmv[0] : b->mv.mv[n];
55
56
562k
                *have_refmv_match = 1;
57
562k
                *have_newmv_match |= b->mf >> 1;
58
59
562k
                const int last = *cnt;
60
810k
                for (int m = 0; m < last; m++)
61
561k
                    if (mvstack[m].mv.mv[0].n == cand_mv.n) {
62
313k
                        mvstack[m].weight += weight;
63
313k
                        return;
64
313k
                    }
65
66
248k
                if (last < 8) {
67
248k
                    mvstack[last].mv.mv[0] = cand_mv;
68
248k
                    mvstack[last].weight = weight;
69
248k
                    *cnt = last + 1;
70
248k
                }
71
248k
                return;
72
562k
            }
73
1.02M
        }
74
782k
    } else if (b->ref.pair == ref.pair) {
75
52.9k
        const refmvs_mvpair cand_mv = { .mv = {
76
52.9k
            [0] = ((b->mf & 1) && gmv[0].n != INVALID_MV) ? gmv[0] : b->mv.mv[0],
77
52.9k
            [1] = ((b->mf & 1) && gmv[1].n != INVALID_MV) ? gmv[1] : b->mv.mv[1],
78
52.9k
        }};
79
80
52.9k
        *have_refmv_match = 1;
81
52.9k
        *have_newmv_match |= b->mf >> 1;
82
83
52.9k
        const int last = *cnt;
84
79.0k
        for (int n = 0; n < last; n++)
85
45.7k
            if (mvstack[n].mv.n == cand_mv.n) {
86
19.5k
                mvstack[n].weight += weight;
87
19.5k
                return;
88
19.5k
            }
89
90
33.3k
        if (last < 8) {
91
33.3k
            mvstack[last].mv = cand_mv;
92
33.3k
            mvstack[last].weight = weight;
93
33.3k
            *cnt = last + 1;
94
33.3k
        }
95
33.3k
    }
96
944k
}
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
325k
{
104
325k
    const refmvs_block *cand_b = b;
105
325k
    const enum BlockSize first_cand_bs = cand_b->bs;
106
325k
    const uint8_t *const first_cand_b_dim = dav1d_block_dimensions[first_cand_bs];
107
325k
    int cand_bw4 = first_cand_b_dim[0];
108
325k
    int len = imax(step, imin(bw4, cand_bw4));
109
110
325k
    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
281k
        const int weight = bw4 == 1 ? 2 :
116
281k
                           imax(2, imin(2 * max_rows, first_cand_b_dim[1]));
117
281k
        add_spatial_candidate(mvstack, cnt, len * weight, cand_b, ref, gmv,
118
281k
                              have_newmv_match, have_refmv_match);
119
281k
        return weight >> 1;
120
281k
    }
121
122
89.3k
    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
89.3k
        add_spatial_candidate(mvstack, cnt, len * 2, cand_b, ref, gmv,
127
89.3k
                              have_newmv_match, have_refmv_match);
128
89.3k
        x += len;
129
89.3k
        if (x >= w4) return 1;
130
44.8k
        cand_b = &b[x];
131
44.8k
        cand_bw4 = dav1d_block_dimensions[cand_b->bs][0];
132
44.8k
        assert(cand_bw4 < bw4);
133
44.8k
        len = imax(step, cand_bw4);
134
44.8k
    }
135
44.4k
}
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
377k
{
143
377k
    const refmvs_block *cand_b = &b[0][bx4];
144
377k
    const enum BlockSize first_cand_bs = cand_b->bs;
145
377k
    const uint8_t *const first_cand_b_dim = dav1d_block_dimensions[first_cand_bs];
146
377k
    int cand_bh4 = first_cand_b_dim[1];
147
377k
    int len = imax(step, imin(bh4, cand_bh4));
148
149
377k
    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
319k
        const int weight = bh4 == 1 ? 2 :
155
319k
                           imax(2, imin(2 * max_cols, first_cand_b_dim[0]));
156
319k
        add_spatial_candidate(mvstack, cnt, len * weight, cand_b, ref, gmv,
157
319k
                            have_newmv_match, have_refmv_match);
158
319k
        return weight >> 1;
159
319k
    }
160
161
116k
    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
116k
        add_spatial_candidate(mvstack, cnt, len * 2, cand_b, ref, gmv,
166
116k
                              have_newmv_match, have_refmv_match);
167
116k
        y += len;
168
116k
        if (y >= h4) return 1;
169
57.4k
        cand_b = &b[y][bx4];
170
57.4k
        cand_bh4 = dav1d_block_dimensions[cand_b->bs][1];
171
57.4k
        assert(cand_bh4 < bh4);
172
57.4k
        len = imax(step, cand_bh4);
173
57.4k
    }
174
58.5k
}
175
176
144
static inline union mv mv_projection(const union mv mv, const int num, const int den) {
177
144
    static const uint16_t div_mult[32] = {
178
144
           0, 16384, 8192, 5461, 4096, 3276, 2730, 2340,
179
144
        2048,  1820, 1638, 1489, 1365, 1260, 1170, 1092,
180
144
        1024,   963,  910,  862,  819,  780,  744,  712,
181
144
         682,   655,  630,  606,  585,  564,  546,  528
182
144
    };
183
144
    assert(den > 0 && den < 32);
184
144
    assert(num > -32 && num < 32);
185
144
    const int frac = num * div_mult[den];
186
144
    const int y = mv.y * frac, x = mv.x * frac;
187
    // Round and clip according to AV1 spec section 7.9.3
188
144
    return (union mv) { // 0x3fff == (1 << 14) - 1
189
144
        .y = iclip((y + 8192 + (y >> 31)) >> 14, -0x3fff, 0x3fff),
190
144
        .x = iclip((x + 8192 + (x >> 31)) >> 14, -0x3fff, 0x3fff)
191
144
    };
192
144
}
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
117
{
200
117
    if (rb->mv.n == INVALID_MV) return;
201
202
71
    union mv mv = mv_projection(rb->mv, rf->pocdiff[ref.ref[0] - 1], rb->ref);
203
71
    fix_mv_precision(rf->frm_hdr, &mv);
204
205
71
    const int last = *cnt;
206
71
    if (ref.ref[1] == -1) {
207
68
        if (globalmv_ctx)
208
68
            *globalmv_ctx = (abs(mv.x - gmv[0].x) | abs(mv.y - gmv[0].y)) >= 16;
209
210
85
        for (int n = 0; n < last; n++)
211
30
            if (mvstack[n].mv.mv[0].n == mv.n) {
212
13
                mvstack[n].weight += weight;
213
13
                return;
214
13
            }
215
55
        if (last < 8) {
216
55
            mvstack[last].mv.mv[0] = mv;
217
55
            mvstack[last].weight = weight;
218
55
            *cnt = last + 1;
219
55
        }
220
55
    } else {
221
3
        refmvs_mvpair mvp = { .mv = {
222
3
            [0] = mv,
223
3
            [1] = mv_projection(rb->mv, rf->pocdiff[ref.ref[1] - 1], rb->ref),
224
3
        }};
225
3
        fix_mv_precision(rf->frm_hdr, &mvp.mv[1]);
226
227
3
        for (int n = 0; n < last; n++)
228
0
            if (mvstack[n].mv.n == mvp.n) {
229
0
                mvstack[n].weight += weight;
230
0
                return;
231
0
            }
232
3
        if (last < 8) {
233
3
            mvstack[last].mv = mvp;
234
3
            mvstack[last].weight = weight;
235
3
            *cnt = last + 1;
236
3
        }
237
3
    }
238
71
}
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
40.2k
{
247
40.2k
    refmvs_candidate *const diff = &same[2];
248
40.2k
    int *const diff_count = &same_count[2];
249
250
102k
    for (int n = 0; n < 2; n++) {
251
78.7k
        const int cand_ref = cand_b->ref.ref[n];
252
253
78.7k
        if (cand_ref <= 0) break;
254
255
62.0k
        mv cand_mv = cand_b->mv.mv[n];
256
62.0k
        if (cand_ref == ref.ref[0]) {
257
22.0k
            if (same_count[0] < 2)
258
21.3k
                same[same_count[0]++].mv.mv[0] = cand_mv;
259
22.0k
            if (diff_count[1] < 2) {
260
18.8k
                if (sign1 ^ sign_bias[cand_ref - 1]) {
261
32
                    cand_mv.y = -cand_mv.y;
262
32
                    cand_mv.x = -cand_mv.x;
263
32
                }
264
18.8k
                diff[diff_count[1]++].mv.mv[1] = cand_mv;
265
18.8k
            }
266
39.9k
        } else if (cand_ref == ref.ref[1]) {
267
21.0k
            if (same_count[1] < 2)
268
20.5k
                same[same_count[1]++].mv.mv[1] = cand_mv;
269
21.0k
            if (diff_count[0] < 2) {
270
17.0k
                if (sign0 ^ sign_bias[cand_ref - 1]) {
271
31
                    cand_mv.y = -cand_mv.y;
272
31
                    cand_mv.x = -cand_mv.x;
273
31
                }
274
17.0k
                diff[diff_count[0]++].mv.mv[0] = cand_mv;
275
17.0k
            }
276
21.0k
        } else {
277
18.8k
            mv i_cand_mv = (union mv) {
278
18.8k
                .x = -cand_mv.x,
279
18.8k
                .y = -cand_mv.y
280
18.8k
            };
281
282
18.8k
            if (diff_count[0] < 2) {
283
14.5k
                diff[diff_count[0]++].mv.mv[0] =
284
14.5k
                    sign0 ^ sign_bias[cand_ref - 1] ?
285
14.5k
                    i_cand_mv : cand_mv;
286
14.5k
            }
287
288
18.8k
            if (diff_count[1] < 2) {
289
13.5k
                diff[diff_count[1]++].mv.mv[1] =
290
13.5k
                    sign1 ^ sign_bias[cand_ref - 1] ?
291
13.5k
                    i_cand_mv : cand_mv;
292
13.5k
            }
293
18.8k
        }
294
62.0k
    }
295
40.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
148k
{
301
296k
    for (int n = 0; n < 2; n++) {
302
290k
        const int cand_ref = cand_b->ref.ref[n];
303
304
290k
        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
147k
        mv cand_mv = cand_b->mv.mv[n];
312
147k
        if (sign ^ sign_bias[cand_ref - 1]) {
313
59
            cand_mv.y = -cand_mv.y;
314
59
            cand_mv.x = -cand_mv.x;
315
59
        }
316
317
147k
        int m;
318
147k
        const int last = *cnt;
319
170k
        for (m = 0; m < last; m++)
320
136k
            if (cand_mv.n == mvstack[m].mv.mv[0].n)
321
113k
                break;
322
147k
        if (m == last) {
323
34.7k
            mvstack[m].mv.mv[0] = cand_mv;
324
34.7k
            mvstack[m].weight = 2; // "minimal"
325
34.7k
            *cnt = last + 1;
326
34.7k
        }
327
147k
    }
328
148k
}
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
188k
{
356
188k
    const refmvs_frame *const rf = rt->rf;
357
188k
    const uint8_t *const b_dim = dav1d_block_dimensions[bs];
358
188k
    const int bw4 = b_dim[0], w4 = imin(imin(bw4, 16), rt->tile_col.end - bx4);
359
188k
    const int bh4 = b_dim[1], h4 = imin(imin(bh4, 16), rt->tile_row.end - by4);
360
188k
    mv gmv[2], tgmv[2];
361
362
188k
    *cnt = 0;
363
188k
    assert(ref.ref[0] >=  0 && ref.ref[0] <= 8 &&
364
188k
           ref.ref[1] >= -1 && ref.ref[1] <= 8);
365
188k
    if (ref.ref[0] > 0) {
366
165k
        tgmv[0] = get_gmv_2d(&rf->frm_hdr->gmv[ref.ref[0] - 1],
367
165k
                             bx4, by4, bw4, bh4, rf->frm_hdr);
368
165k
        gmv[0] = rf->frm_hdr->gmv[ref.ref[0] - 1].type > DAV1D_WM_TYPE_TRANSLATION ?
369
140k
                 tgmv[0] : (mv) { .n = INVALID_MV };
370
165k
    } else {
371
22.6k
        tgmv[0] = (mv) { .n = 0 };
372
22.6k
        gmv[0] = (mv) { .n = INVALID_MV };
373
22.6k
    }
374
188k
    if (ref.ref[1] > 0) {
375
29.4k
        tgmv[1] = get_gmv_2d(&rf->frm_hdr->gmv[ref.ref[1] - 1],
376
29.4k
                             bx4, by4, bw4, bh4, rf->frm_hdr);
377
29.4k
        gmv[1] = rf->frm_hdr->gmv[ref.ref[1] - 1].type > DAV1D_WM_TYPE_TRANSLATION ?
378
22.1k
                 tgmv[1] : (mv) { .n = INVALID_MV };
379
29.4k
    }
380
381
    // top
382
188k
    int have_newmv = 0, have_col_mvs = 0, have_row_mvs = 0;
383
188k
    unsigned max_rows = 0, n_rows = ~0;
384
188k
    const refmvs_block *b_top;
385
188k
    if (by4 > rt->tile_row.start) {
386
153k
        max_rows = imin((by4 - rt->tile_row.start + 1) >> 1, 2 + (bh4 > 1));
387
153k
        b_top = &rt->r[(by4 & 31) - 1 + 5][bx4];
388
153k
        n_rows = scan_row(mvstack, cnt, ref, gmv, b_top,
389
153k
                          bw4, w4, max_rows, bw4 >= 16 ? 4 : 1,
390
153k
                          &have_newmv, &have_row_mvs);
391
153k
    }
392
393
    // left
394
188k
    unsigned max_cols = 0, n_cols = ~0U;
395
188k
    refmvs_block *const *b_left;
396
188k
    if (bx4 > rt->tile_col.start) {
397
174k
        max_cols = imin((bx4 - rt->tile_col.start + 1) >> 1, 2 + (bw4 > 1));
398
174k
        b_left = &rt->r[(by4 & 31) + 5];
399
174k
        n_cols = scan_col(mvstack, cnt, ref, gmv, b_left,
400
174k
                          bh4, h4, bx4 - 1, max_cols, bh4 >= 16 ? 4 : 1,
401
174k
                          &have_newmv, &have_col_mvs);
402
174k
    }
403
404
    // top/right
405
188k
    if (n_rows != ~0U && edge_flags & EDGE_I444_TOP_HAS_RIGHT &&
406
86.8k
        imax(bw4, bh4) <= 16 && bw4 + bx4 < rt->tile_col.end)
407
79.1k
    {
408
79.1k
        add_spatial_candidate(mvstack, cnt, 4, &b_top[bw4], ref, gmv,
409
79.1k
                              &have_newmv, &have_row_mvs);
410
79.1k
    }
411
412
188k
    const int nearest_match = have_col_mvs + have_row_mvs;
413
188k
    const int nearest_cnt = *cnt;
414
385k
    for (int n = 0; n < nearest_cnt; n++)
415
197k
        mvstack[n].weight += 640;
416
417
    // temporal
418
188k
    int globalmv_ctx = rf->frm_hdr->use_ref_frame_mvs;
419
188k
    if (rf->use_ref_frame_mvs) {
420
117
        const ptrdiff_t stride = rf->rp_stride;
421
117
        const int by8 = by4 >> 1, bx8 = bx4 >> 1;
422
117
        const refmvs_temporal_block *const rbi = &rt->rp_proj[(by8 & 15) * stride + bx8];
423
117
        const refmvs_temporal_block *rb = rbi;
424
117
        const int step_h = bw4 >= 16 ? 2 : 1, step_v = bh4 >= 16 ? 2 : 1;
425
117
        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
117
        const refmvs_temporal_block *run = &rb[0];
440
117
        int run_len = 1;
441
117
        int *run_gmv_ctx = &globalmv_ctx;
442
234
        for (int y = 0; y < h8; y += step_v) {
443
117
            for (int x = y ? 0 : step_h; x < w8; x += step_h) {
444
0
                const unsigned mvn = rb[x].mv.n;
445
0
                if (mvn == run->mv.n &&
446
0
                    (mvn == INVALID_MV || rb[x].ref == run->ref))
447
0
                {
448
0
                    run_len++;
449
0
                    continue;
450
0
                }
451
0
                add_temporal_candidate(rf, mvstack, cnt, run, ref,
452
0
                                       run_gmv_ctx, tgmv, 2 * run_len);
453
0
                run_gmv_ctx = NULL;
454
0
                run = &rb[x];
455
0
                run_len = 1;
456
0
            }
457
117
            rb += stride * step_v;
458
117
        }
459
117
        add_temporal_candidate(rf, mvstack, cnt, run, ref,
460
117
                               run_gmv_ctx, tgmv, 2 * run_len);
461
117
        if (imin(bw4, bh4) >= 2 && imax(bw4, bh4) < 16) {
462
19
            const int bh8 = bh4 >> 1, bw8 = bw4 >> 1;
463
19
            rb = &rbi[bh8 * stride];
464
19
            const int has_bottom = by8 + bh8 < imin(rt->tile_row.end >> 1,
465
19
                                                    (by8 & ~7) + 8);
466
19
            if (has_bottom && bx8 - 1 >= imax(rt->tile_col.start >> 1, bx8 & ~7)) {
467
0
                add_temporal_candidate(rf, mvstack, cnt, &rb[-1], ref,
468
0
                                       NULL, NULL, 2);
469
0
            }
470
19
            if (bx8 + bw8 < imin(rt->tile_col.end >> 1, (bx8 & ~7) + 8)) {
471
0
                if (has_bottom) {
472
0
                    add_temporal_candidate(rf, mvstack, cnt, &rb[bw8], ref,
473
0
                                           NULL, NULL, 2);
474
0
                }
475
0
                if (by8 + bh8 - 1 < imin(rt->tile_row.end >> 1, (by8 & ~7) + 8)) {
476
0
                    add_temporal_candidate(rf, mvstack, cnt, &rb[bw8 - stride],
477
0
                                           ref, NULL, NULL, 2);
478
0
                }
479
0
            }
480
19
        }
481
117
    }
482
188k
    assert(*cnt <= 8);
483
484
    // top/left (which, confusingly, is part of "secondary" references)
485
188k
    int have_dummy_newmv_match;
486
188k
    if ((n_rows | n_cols) != ~0U) {
487
144k
        add_spatial_candidate(mvstack, cnt, 4, &b_top[-1], ref, gmv,
488
144k
                              &have_dummy_newmv_match, &have_row_mvs);
489
144k
    }
490
491
    // "secondary" (non-direct neighbour) top & left edges
492
    // what is different about secondary is that everything is now in 8x8 resolution
493
565k
    for (int n = 2; n <= 3; n++) {
494
377k
        if ((unsigned) n > n_rows && (unsigned) n <= max_rows) {
495
171k
            n_rows += scan_row(mvstack, cnt, ref, gmv,
496
171k
                               &rt->r[(((by4 & 31) - 2 * n + 1) | 1) + 5][bx4 | 1],
497
171k
                               bw4, w4, 1 + max_rows - n, bw4 >= 16 ? 4 : 2,
498
171k
                               &have_dummy_newmv_match, &have_row_mvs);
499
171k
        }
500
501
377k
        if ((unsigned) n > n_cols && (unsigned) n <= max_cols) {
502
203k
            n_cols += scan_col(mvstack, cnt, ref, gmv, &rt->r[((by4 & 31) | 1) + 5],
503
203k
                               bh4, h4, (bx4 - n * 2 + 1) | 1,
504
203k
                               1 + max_cols - n, bh4 >= 16 ? 4 : 2,
505
203k
                               &have_dummy_newmv_match, &have_col_mvs);
506
203k
        }
507
377k
    }
508
188k
    assert(*cnt <= 8);
509
510
188k
    const int ref_match_count = have_col_mvs + have_row_mvs;
511
512
    // context build-up
513
188k
    int refmv_ctx, newmv_ctx;
514
188k
    switch (nearest_match) {
515
39.6k
    case 0:
516
39.6k
        refmv_ctx = imin(2, ref_match_count);
517
39.6k
        newmv_ctx = ref_match_count > 0;
518
39.6k
        break;
519
73.6k
    case 1:
520
73.6k
        refmv_ctx = imin(ref_match_count * 3, 4);
521
73.6k
        newmv_ctx = 3 - have_newmv;
522
73.6k
        break;
523
75.6k
    case 2:
524
75.6k
        refmv_ctx = 5;
525
75.6k
        newmv_ctx = 5 - have_newmv;
526
75.6k
        break;
527
188k
    }
528
529
    // sorting (nearest, then "secondary")
530
188k
    int len = nearest_cnt;
531
356k
    while (len) {
532
167k
        int last = 0;
533
221k
        for (int n = 1; n < len; n++) {
534
53.6k
            if (mvstack[n - 1].weight < mvstack[n].weight) {
535
27.1k
#define EXCHANGE(a, b) do { refmvs_candidate tmp = a; a = b; b = tmp; } while (0)
536
21.0k
                EXCHANGE(mvstack[n - 1], mvstack[n]);
537
21.0k
                last = n;
538
21.0k
            }
539
53.6k
        }
540
167k
        len = last;
541
167k
    }
542
188k
    len = *cnt;
543
253k
    while (len > nearest_cnt) {
544
65.3k
        int last = nearest_cnt;
545
91.9k
        for (int n = nearest_cnt + 1; n < len; n++) {
546
26.5k
            if (mvstack[n - 1].weight < mvstack[n].weight) {
547
6.11k
                EXCHANGE(mvstack[n - 1], mvstack[n]);
548
6.11k
#undef EXCHANGE
549
6.11k
                last = n;
550
6.11k
            }
551
26.5k
        }
552
65.3k
        len = last;
553
65.3k
    }
554
555
188k
    if (ref.ref[1] > 0) {
556
29.4k
        if (*cnt < 2) {
557
20.7k
            const int sign0 = rf->sign_bias[ref.ref[0] - 1];
558
20.7k
            const int sign1 = rf->sign_bias[ref.ref[1] - 1];
559
20.7k
            const int sz4 = imin(w4, h4);
560
20.7k
            refmvs_candidate *const same = &mvstack[*cnt];
561
20.7k
            int same_count[4] = { 0 };
562
563
            // non-self references in top
564
34.8k
            if (n_rows != ~0U) for (int x = 0; x < sz4;) {
565
18.2k
                const refmvs_block *const cand_b = &b_top[x];
566
18.2k
                add_compound_extended_candidate(same, same_count, cand_b,
567
18.2k
                                                sign0, sign1, ref, rf->sign_bias);
568
18.2k
                x += dav1d_block_dimensions[cand_b->bs][0];
569
18.2k
            }
570
571
            // non-self references in left
572
41.2k
            if (n_cols != ~0U) for (int y = 0; y < sz4;) {
573
21.9k
                const refmvs_block *const cand_b = &b_left[y][bx4 - 1];
574
21.9k
                add_compound_extended_candidate(same, same_count, cand_b,
575
21.9k
                                                sign0, sign1, ref, rf->sign_bias);
576
21.9k
                y += dav1d_block_dimensions[cand_b->bs][1];
577
21.9k
            }
578
579
20.7k
            refmvs_candidate *const diff = &same[2];
580
20.7k
            const int *const diff_count = &same_count[2];
581
582
            // merge together
583
62.2k
            for (int n = 0; n < 2; n++) {
584
41.4k
                int m = same_count[n];
585
586
41.4k
                if (m >= 2) continue;
587
588
30.5k
                const int l = diff_count[n];
589
30.5k
                if (l) {
590
28.2k
                    same[m].mv.mv[n] = diff[0].mv.mv[n];
591
28.2k
                    if (++m == 2) continue;
592
9.54k
                    if (l == 2) {
593
7.66k
                        same[1].mv.mv[n] = diff[1].mv.mv[n];
594
7.66k
                        continue;
595
7.66k
                    }
596
9.54k
                }
597
5.12k
                do {
598
5.12k
                    same[m].mv.mv[n] = tgmv[n];
599
5.12k
                } while (++m < 2);
600
4.14k
            }
601
602
            // if the first extended was the same as the non-extended one,
603
            // then replace it with the second extended one
604
20.7k
            int n = *cnt;
605
20.7k
            if (n == 1 && mvstack[0].mv.n == same[0].mv.n)
606
8.02k
                mvstack[1].mv = mvstack[2].mv;
607
30.6k
            do {
608
30.6k
                mvstack[n].weight = 2;
609
30.6k
            } while (++n < 2);
610
20.7k
            *cnt = 2;
611
20.7k
        }
612
613
        // clamping
614
29.4k
        const int left = -(bx4 + bw4 + 4) * 4 * 8;
615
29.4k
        const int right = (rf->iw4 - bx4 + 4) * 4 * 8;
616
29.4k
        const int top = -(by4 + bh4 + 4) * 4 * 8;
617
29.4k
        const int bottom = (rf->ih4 - by4 + 4) * 4 * 8;
618
619
29.4k
        const int n_refmvs = *cnt;
620
29.4k
        int n = 0;
621
64.0k
        do {
622
64.0k
            mvstack[n].mv.mv[0].x = iclip(mvstack[n].mv.mv[0].x, left, right);
623
64.0k
            mvstack[n].mv.mv[0].y = iclip(mvstack[n].mv.mv[0].y, top, bottom);
624
64.0k
            mvstack[n].mv.mv[1].x = iclip(mvstack[n].mv.mv[1].x, left, right);
625
64.0k
            mvstack[n].mv.mv[1].y = iclip(mvstack[n].mv.mv[1].y, top, bottom);
626
64.0k
        } while (++n < n_refmvs);
627
628
29.4k
        switch (refmv_ctx >> 1) {
629
11.0k
        case 0:
630
11.0k
            *ctx = imin(newmv_ctx, 1);
631
11.0k
            break;
632
11.2k
        case 1:
633
11.2k
            *ctx = 1 + imin(newmv_ctx, 3);
634
11.2k
            break;
635
7.15k
        case 2:
636
7.15k
            *ctx = iclip(3 + newmv_ctx, 4, 7);
637
7.15k
            break;
638
29.4k
        }
639
640
29.4k
        return;
641
159k
    } else if (*cnt < 2 && ref.ref[0] > 0) {
642
89.5k
        const int sign = rf->sign_bias[ref.ref[0] - 1];
643
89.5k
        const int sz4 = imin(w4, h4);
644
645
        // non-self references in top
646
143k
        if (n_rows != ~0U) for (int x = 0; x < sz4 && *cnt < 2;) {
647
73.4k
            const refmvs_block *const cand_b = &b_top[x];
648
73.4k
            add_single_extended_candidate(mvstack, cnt, cand_b, sign, rf->sign_bias);
649
73.4k
            x += dav1d_block_dimensions[cand_b->bs][0];
650
73.4k
        }
651
652
        // non-self references in left
653
155k
        if (n_cols != ~0U) for (int y = 0; y < sz4 && *cnt < 2;) {
654
75.5k
            const refmvs_block *const cand_b = &b_left[y][bx4 - 1];
655
75.5k
            add_single_extended_candidate(mvstack, cnt, cand_b, sign, rf->sign_bias);
656
75.5k
            y += dav1d_block_dimensions[cand_b->bs][1];
657
75.5k
        }
658
89.5k
    }
659
159k
    assert(*cnt <= 8);
660
661
    // clamping
662
159k
    int n_refmvs = *cnt;
663
159k
    if (n_refmvs) {
664
149k
        const int left = -(bx4 + bw4 + 4) * 4 * 8;
665
149k
        const int right = (rf->iw4 - bx4 + 4) * 4 * 8;
666
149k
        const int top = -(by4 + bh4 + 4) * 4 * 8;
667
149k
        const int bottom = (rf->ih4 - by4 + 4) * 4 * 8;
668
669
149k
        int n = 0;
670
283k
        do {
671
283k
            mvstack[n].mv.mv[0].x = iclip(mvstack[n].mv.mv[0].x, left, right);
672
283k
            mvstack[n].mv.mv[0].y = iclip(mvstack[n].mv.mv[0].y, top, bottom);
673
283k
        } while (++n < n_refmvs);
674
149k
    }
675
676
244k
    for (int n = *cnt; n < 2; n++)
677
85.5k
        mvstack[n].mv.mv[0] = tgmv[0];
678
679
159k
    *ctx = (refmv_ctx << 4) | (globalmv_ctx << 3) | newmv_ctx;
680
159k
}
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
18.5k
{
687
18.5k
    if (rf->n_tile_threads == 1) tile_row_idx = 0;
688
18.5k
    rt->rp_proj = &rf->rp_proj[16 * rf->rp_stride * tile_row_idx];
689
18.5k
    const ptrdiff_t r_stride = rf->rp_stride * 2;
690
18.5k
    const ptrdiff_t pass_off = (rf->n_frame_threads > 1 && pass == 2) ?
691
10.4k
        35 * 2 * rf->n_blocks : 0;
692
18.5k
    refmvs_block *r = &rf->r[35 * r_stride * tile_row_idx + pass_off];
693
18.5k
    const int sbsz = rf->sbsz;
694
18.5k
    const int off = (sbsz * sby) & 16;
695
435k
    for (int i = 0; i < sbsz; i++, r += r_stride)
696
416k
        rt->r[off + 5 + i] = r;
697
18.5k
    rt->r[off + 0] = r;
698
18.5k
    r += r_stride;
699
18.5k
    rt->r[off + 1] = NULL;
700
18.5k
    rt->r[off + 2] = r;
701
18.5k
    r += r_stride;
702
18.5k
    rt->r[off + 3] = NULL;
703
18.5k
    rt->r[off + 4] = r;
704
18.5k
    if (sby & 1) {
705
11.7k
#define EXCHANGE(a, b) do { void *const tmp = a; a = b; b = tmp; } while (0)
706
3.93k
        EXCHANGE(rt->r[off + 0], rt->r[off + sbsz + 0]);
707
3.93k
        EXCHANGE(rt->r[off + 2], rt->r[off + sbsz + 2]);
708
3.93k
        EXCHANGE(rt->r[off + 4], rt->r[off + sbsz + 4]);
709
3.93k
#undef EXCHANGE
710
3.93k
    }
711
712
18.5k
    rt->rf = rf;
713
18.5k
    rt->tile_row.start = tile_row_start4;
714
18.5k
    rt->tile_row.end = imin(tile_row_end4, rf->ih4);
715
18.5k
    rt->tile_col.start = tile_col_start4;
716
18.5k
    rt->tile_col.end = imin(tile_col_end4, rf->iw4);
717
18.5k
}
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
169
{
723
169
    if (rf->n_tile_threads == 1) tile_row_idx = 0;
724
169
    assert(row_start8 >= 0);
725
169
    assert((unsigned) (row_end8 - row_start8) <= 16U);
726
169
    row_end8 = imin(row_end8, rf->ih8);
727
169
    const int col_start8i = imax(col_start8 - 8, 0);
728
169
    const int col_end8i = imin(col_end8 + 8, rf->iw8);
729
730
169
    const ptrdiff_t stride = rf->rp_stride;
731
169
    refmvs_temporal_block *rp_proj =
732
169
        &rf->rp_proj[16 * stride * tile_row_idx + (row_start8 & 15) * stride];
733
453
    for (int y = row_start8; y < row_end8; y++) {
734
1.18k
        for (int x = col_start8; x < col_end8; x++)
735
905
            rp_proj[x].mv.n = INVALID_MV;
736
284
        rp_proj += stride;
737
284
    }
738
739
169
    rp_proj = &rf->rp_proj[16 * stride * tile_row_idx];
740
302
    for (int n = 0; n < rf->n_mfmvs; n++) {
741
133
        const int ref2cur = rf->mfmv_ref2cur[n];
742
133
        if (ref2cur == INVALID_REF2CUR) continue;
743
744
95
        const int ref = rf->mfmv_ref[n];
745
95
        const int ref_sign = ref - 4;
746
95
        const refmvs_temporal_block *r = &rf->rp_ref[ref][row_start8 * stride];
747
190
        for (int y = row_start8; y < row_end8; y++) {
748
95
            const int y_sb_align = y & ~7;
749
95
            const int y_proj_start = imax(y_sb_align, row_start8);
750
95
            const int y_proj_end = imin(y_sb_align + 8, row_end8);
751
190
            for (int x = col_start8i; x < col_end8i; x++) {
752
95
                const refmvs_temporal_block *rb = &r[x];
753
95
                const int b_ref = rb->ref;
754
95
                if (!b_ref) continue;
755
76
                const int ref2ref = rf->mfmv_ref2ref[n][b_ref - 1];
756
76
                if (!ref2ref) continue;
757
70
                const mv b_mv = rb->mv;
758
70
                const mv offset = mv_projection(b_mv, ref2cur, ref2ref);
759
70
                int pos_x = x + apply_sign(abs(offset.x) >> 6,
760
70
                                           offset.x ^ ref_sign);
761
70
                const int pos_y = y + apply_sign(abs(offset.y) >> 6,
762
70
                                                 offset.y ^ ref_sign);
763
70
                if (pos_y >= y_proj_start && pos_y < y_proj_end) {
764
62
                    const ptrdiff_t pos = (pos_y & 15) * stride;
765
62
                    for (;;) {
766
62
                        const int x_sb_align = x & ~7;
767
62
                        if (pos_x >= imax(x_sb_align - 8, col_start8) &&
768
59
                            pos_x < imin(x_sb_align + 16, col_end8))
769
53
                        {
770
53
                            rp_proj[pos + pos_x].mv = rb->mv;
771
53
                            rp_proj[pos + pos_x].ref = ref2ref;
772
53
                        }
773
62
                        if (++x >= col_end8i) break;
774
0
                        rb++;
775
0
                        if (rb->ref != b_ref || rb->mv.n != b_mv.n) break;
776
0
                        pos_x++;
777
0
                    }
778
62
                } else {
779
8
                    for (;;) {
780
8
                        if (++x >= col_end8i) break;
781
0
                        rb++;
782
0
                        if (rb->ref != b_ref || rb->mv.n != b_mv.n) break;
783
0
                    }
784
8
                }
785
70
                x--;
786
70
            }
787
95
            r += stride;
788
95
        }
789
95
    }
790
169
}
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
345
{
798
1.16k
    for (int y = row_start8; y < row_end8; y++) {
799
817
        const refmvs_block *const b = rr[(y & 15) * 2];
800
801
1.93k
        for (int x = col_start8; x < col_end8;) {
802
1.12k
            const refmvs_block *const cand_b = &b[x * 2 + 1];
803
1.12k
            const int bw8 = (dav1d_block_dimensions[cand_b->bs][0] + 1) >> 1;
804
805
1.12k
            if (cand_b->ref.ref[1] > 0 && ref_sign[cand_b->ref.ref[1] - 1] &&
806
139
                (abs(cand_b->mv.mv[1].y) | abs(cand_b->mv.mv[1].x)) < 4096)
807
139
            {
808
139
                const refmvs_temporal_block tmv = {
809
139
                    .mv = cand_b->mv.mv[1],
810
139
                    .ref = cand_b->ref.ref[1],
811
139
                };
812
529
                for (int n = 0; n < bw8; n++, x++)
813
390
                    rp[x] = tmv;
814
981
            } else if (cand_b->ref.ref[0] > 0 && ref_sign[cand_b->ref.ref[0] - 1] &&
815
469
                       (abs(cand_b->mv.mv[0].y) | abs(cand_b->mv.mv[0].x)) < 4096)
816
469
            {
817
469
                const refmvs_temporal_block tmv = {
818
469
                    .mv = cand_b->mv.mv[0],
819
469
                    .ref = cand_b->ref.ref[0],
820
469
                };
821
1.78k
                for (int n = 0; n < bw8; n++, x++)
822
1.31k
                    rp[x] = tmv;
823
512
            } else {
824
512
                memset(&rp[x], 0, bw8 * sizeof(*rp));
825
512
                x += bw8;
826
512
            }
827
1.12k
        }
828
817
        rp += stride;
829
817
    }
830
345
}
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
6.35k
{
841
6.35k
    const int rp_stride = ((frm_hdr->width[0] + 127) & ~127) >> 3;
842
6.35k
    const int n_tile_rows = n_tile_threads > 1 ? frm_hdr->tiling.rows : 1;
843
6.35k
    const int n_blocks = rp_stride * n_tile_rows;
844
845
6.35k
    rf->sbsz = 16 << seq_hdr->sb128;
846
6.35k
    rf->frm_hdr = frm_hdr;
847
6.35k
    rf->iw8 = (frm_hdr->width[0] + 7) >> 3;
848
6.35k
    rf->ih8 = (frm_hdr->height + 7) >> 3;
849
6.35k
    rf->iw4 = rf->iw8 << 1;
850
6.35k
    rf->ih4 = rf->ih8 << 1;
851
6.35k
    rf->rp = rp;
852
6.35k
    rf->rp_stride = rp_stride;
853
6.35k
    rf->n_tile_threads = n_tile_threads;
854
6.35k
    rf->n_frame_threads = n_frame_threads;
855
856
6.35k
    if (n_blocks != rf->n_blocks) {
857
4.19k
        const size_t r_sz = sizeof(*rf->r) * 35 * 2 * n_blocks * (1 + (n_frame_threads > 1));
858
4.19k
        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
4.19k
        dav1d_free_aligned(rf->r);
862
4.19k
        rf->r = dav1d_alloc_aligned(ALLOC_REFMVS, r_sz + rp_proj_sz, 64);
863
4.19k
        if (!rf->r) {
864
0
            rf->n_blocks = 0;
865
0
            return DAV1D_ERR(ENOMEM);
866
0
        }
867
868
4.19k
        rf->rp_proj = (refmvs_temporal_block*)((uintptr_t)rf->r + r_sz);
869
4.19k
        rf->n_blocks = n_blocks;
870
4.19k
    }
871
872
6.35k
    const int poc = frm_hdr->frame_offset;
873
50.8k
    for (int i = 0; i < 7; i++) {
874
44.4k
        const int poc_diff = get_poc_diff(seq_hdr->order_hint_n_bits,
875
44.4k
                                          ref_poc[i], poc);
876
44.4k
        rf->sign_bias[i] = poc_diff > 0;
877
44.4k
        rf->mfmv_sign[i] = poc_diff < 0;
878
44.4k
        rf->pocdiff[i] = iclip(get_poc_diff(seq_hdr->order_hint_n_bits,
879
44.4k
                                            poc, ref_poc[i]), -31, 31);
880
44.4k
    }
881
882
    // temporal MV setup
883
6.35k
    rf->n_mfmvs = 0;
884
6.35k
    rf->rp_ref = rp_ref;
885
6.35k
    if (frm_hdr->use_ref_frame_mvs && seq_hdr->order_hint_n_bits) {
886
196
        int total = 2;
887
196
        if (rp_ref[0] && ref_ref_poc[0][6] != ref_poc[3] /* alt-of-last != gold */) {
888
37
            rf->mfmv_ref[rf->n_mfmvs++] = 0; // last
889
37
            total = 3;
890
37
        }
891
196
        if (rp_ref[4] && get_poc_diff(seq_hdr->order_hint_n_bits, ref_poc[4],
892
78
                                      frm_hdr->frame_offset) > 0)
893
16
        {
894
16
            rf->mfmv_ref[rf->n_mfmvs++] = 4; // bwd
895
16
        }
896
196
        if (rp_ref[5] && get_poc_diff(seq_hdr->order_hint_n_bits, ref_poc[5],
897
112
                                      frm_hdr->frame_offset) > 0)
898
22
        {
899
22
            rf->mfmv_ref[rf->n_mfmvs++] = 5; // altref2
900
22
        }
901
196
        if (rf->n_mfmvs < total && rp_ref[6] &&
902
65
            get_poc_diff(seq_hdr->order_hint_n_bits, ref_poc[6],
903
65
                         frm_hdr->frame_offset) > 0)
904
12
        {
905
12
            rf->mfmv_ref[rf->n_mfmvs++] = 6; // altref
906
12
        }
907
196
        if (rf->n_mfmvs < total && rp_ref[1])
908
78
            rf->mfmv_ref[rf->n_mfmvs++] = 1; // last2
909
910
361
        for (int n = 0; n < rf->n_mfmvs; n++) {
911
165
            const int rpoc = ref_poc[rf->mfmv_ref[n]];
912
165
            const int diff1 = get_poc_diff(seq_hdr->order_hint_n_bits,
913
165
                                           rpoc, frm_hdr->frame_offset);
914
165
            if (abs(diff1) > 31) {
915
44
                rf->mfmv_ref2cur[n] = INVALID_REF2CUR;
916
121
            } else {
917
121
                rf->mfmv_ref2cur[n] = rf->mfmv_ref[n] < 4 ? -diff1 : diff1;
918
968
                for (int m = 0; m < 7; m++) {
919
847
                    const int rrpoc = ref_ref_poc[rf->mfmv_ref[n]][m];
920
847
                    const int diff2 = get_poc_diff(seq_hdr->order_hint_n_bits,
921
847
                                                   rpoc, rrpoc);
922
                    // unsigned comparison also catches the < 0 case
923
847
                    rf->mfmv_ref2ref[n][m] = (unsigned) diff2 > 31U ? 0 : diff2;
924
847
                }
925
121
            }
926
165
        }
927
196
    }
928
6.35k
    rf->use_ref_frame_mvs = rf->n_mfmvs > 0;
929
930
6.35k
    return 0;
931
6.35k
}
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
270k
{
936
1.15M
    do {
937
1.15M
        refmvs_block *const r = *rr++ + bx4;
938
11.8M
        for (int x = 0; x < bw4; x++)
939
10.6M
            r[x] = *rmv;
940
1.15M
    } while (--bh4);
941
270k
}
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
7.86k
{
955
7.86k
    c->load_tmvs = load_tmvs_c;
956
7.86k
    c->save_tmvs = save_tmvs_c;
957
7.86k
    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
7.86k
}