Coverage Report

Created: 2026-10-28 06:13

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