Coverage Report

Created: 2026-07-16 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/svt-av1/Source/Lib/Codec/enc_cdef.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2016, Alliance for Open Media. All rights reserved
3
 *
4
 * This source code is subject to the terms of the BSD 3-Clause Clear License and
5
 * the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear License
6
 * was not distributed with this source code in the LICENSE file, you can
7
 * obtain it at https://www.aomedia.org/license. If the Alliance for Open
8
 * Media Patent License 1.0 was not distributed with this source code in the
9
 * PATENTS file, you can obtain it at https://www.aomedia.org/license/patent-license.
10
 */
11
#include <stdio.h>
12
#include <stdlib.h>
13
#include <math.h>
14
#include <string.h>
15
16
#include "enc_cdef.h"
17
#include <stdint.h>
18
#include "aom_dsp_rtcd.h"
19
#include "cdef_copy.h"
20
#include "svt_log.h"
21
#include "rd_cost.h"
22
#include "rc_process.h"
23
24
static INLINE uint64_t mse_8xn_16bit_c(const uint16_t* src, const uint16_t* dst, const int32_t dstride,
25
0
                                       const int32_t height, uint8_t subsampling_factor) {
26
0
    uint64_t sum = 0;
27
0
    int32_t  i, j;
28
0
    for (i = 0; i < height; i += subsampling_factor) {
29
0
        for (j = 0; j < 8; j++) {
30
0
            int32_t e = dst[i * dstride + j] - src[8 * i + j];
31
0
            sum += e * e;
32
0
        }
33
0
    }
34
0
    return sum;
35
0
}
36
37
static INLINE uint64_t mse_4xn_16bit_c(const uint16_t* src, const uint16_t* dst, const int32_t dstride,
38
0
                                       const int32_t height, uint8_t subsampling_factor) {
39
0
    uint64_t sum = 0;
40
0
    int32_t  i, j;
41
0
    for (i = 0; i < height; i += subsampling_factor) {
42
0
        for (j = 0; j < 4; j++) {
43
0
            int32_t e = dst[i * dstride + j] - src[4 * i + j];
44
0
            sum += e * e;
45
0
        }
46
0
    }
47
0
    return sum;
48
0
}
49
50
static INLINE uint64_t mse_8xn_8bit_c(const uint8_t* src, const uint8_t* dst, const int32_t dstride,
51
0
                                      const int32_t height, uint8_t subsampling_factor) {
52
0
    uint64_t sum = 0;
53
0
    int32_t  i, j;
54
0
    for (i = 0; i < height; i += subsampling_factor) {
55
0
        for (j = 0; j < 8; j++) {
56
0
            int32_t e = dst[i * dstride + j] - src[8 * i + j];
57
0
            sum += e * e;
58
0
        }
59
0
    }
60
0
    return sum;
61
0
}
62
63
static INLINE uint64_t mse_4xn_8bit_c(const uint8_t* src, const uint8_t* dst, const int32_t dstride,
64
0
                                      const int32_t height, uint8_t subsampling_factor) {
65
0
    uint64_t sum = 0;
66
0
    int32_t  i, j;
67
0
    for (i = 0; i < height; i += subsampling_factor) {
68
0
        for (j = 0; j < 4; j++) {
69
0
            int32_t e = dst[i * dstride + j] - src[4 * i + j];
70
0
            sum += e * e;
71
0
        }
72
0
    }
73
0
    return sum;
74
0
}
75
76
/* Compute MSE only on the blocks we filtered. */
77
uint64_t svt_aom_compute_cdef_dist_16bit_c(const uint16_t* dst, int32_t dstride, const uint16_t* src,
78
                                           const CdefList* dlist, int32_t cdef_count, BlockSize bsize,
79
0
                                           int32_t coeff_shift, uint8_t subsampling_factor) {
80
0
    uint64_t sum = 0;
81
0
    int32_t  bi, bx, by;
82
0
    if (bsize == BLOCK_8X8) {
83
0
        for (bi = 0; bi < cdef_count; bi++) {
84
0
            by = dlist[bi].by;
85
0
            bx = dlist[bi].bx;
86
0
            sum += mse_8xn_16bit_c(
87
0
                &src[bi << (3 + 3)], &dst[(by << 3) * dstride + (bx << 3)], dstride, 8, subsampling_factor);
88
0
        }
89
0
    } else if (bsize == BLOCK_4X8) {
90
0
        for (bi = 0; bi < cdef_count; bi++) {
91
0
            by = dlist[bi].by;
92
0
            bx = dlist[bi].bx;
93
0
            sum += mse_4xn_16bit_c(
94
0
                &src[bi << (3 + 2)], &dst[(by << 3) * dstride + (bx << 2)], dstride, 8, subsampling_factor);
95
0
        }
96
0
    } else if (bsize == BLOCK_8X4) {
97
0
        for (bi = 0; bi < cdef_count; bi++) {
98
0
            by = dlist[bi].by;
99
0
            bx = dlist[bi].bx;
100
0
            sum += mse_8xn_16bit_c(
101
0
                &src[bi << (2 + 3)], &dst[(by << 2) * dstride + (bx << 3)], dstride, 4, subsampling_factor);
102
0
        }
103
0
    } else {
104
0
        assert(bsize == BLOCK_4X4);
105
0
        for (bi = 0; bi < cdef_count; bi++) {
106
0
            by = dlist[bi].by;
107
0
            bx = dlist[bi].bx;
108
0
            sum += mse_4xn_16bit_c(
109
0
                &src[bi << (2 + 2)], &dst[(by << 2) * dstride + (bx << 2)], dstride, 4, subsampling_factor);
110
0
        }
111
0
    }
112
0
    return sum >> 2 * coeff_shift;
113
0
}
114
115
uint64_t svt_aom_compute_cdef_dist_8bit_c(const uint8_t* dst8, int32_t dstride, const uint8_t* src8,
116
                                          const CdefList* dlist, int32_t cdef_count, BlockSize bsize,
117
0
                                          int32_t coeff_shift, uint8_t subsampling_factor) {
118
0
    uint64_t sum = 0;
119
0
    int32_t  bi, bx, by;
120
0
    if (bsize == BLOCK_8X8) {
121
0
        for (bi = 0; bi < cdef_count; bi++) {
122
0
            by = dlist[bi].by;
123
0
            bx = dlist[bi].bx;
124
0
            sum += mse_8xn_8bit_c(
125
0
                &src8[bi << (3 + 3)], &dst8[(by << 3) * dstride + (bx << 3)], dstride, 8, subsampling_factor);
126
0
        }
127
0
    } else if (bsize == BLOCK_4X8) {
128
0
        for (bi = 0; bi < cdef_count; bi++) {
129
0
            by = dlist[bi].by;
130
0
            bx = dlist[bi].bx;
131
0
            sum += mse_4xn_8bit_c(
132
0
                &src8[bi << (3 + 2)], &dst8[(by << 3) * dstride + (bx << 2)], dstride, 8, subsampling_factor);
133
0
        }
134
0
    } else if (bsize == BLOCK_8X4) {
135
0
        for (bi = 0; bi < cdef_count; bi++) {
136
0
            by = dlist[bi].by;
137
0
            bx = dlist[bi].bx;
138
0
            sum += mse_8xn_8bit_c(
139
0
                &src8[bi << (2 + 3)], &dst8[(by << 2) * dstride + (bx << 3)], dstride, 4, subsampling_factor);
140
0
        }
141
0
    } else {
142
0
        assert(bsize == BLOCK_4X4);
143
0
        for (bi = 0; bi < cdef_count; bi++) {
144
0
            by = dlist[bi].by;
145
0
            bx = dlist[bi].bx;
146
0
            sum += mse_4xn_8bit_c(
147
0
                &src8[bi << (2 + 2)], &dst8[(by << 2) * dstride + (bx << 2)], dstride, 4, subsampling_factor);
148
0
        }
149
0
    }
150
0
    return sum >> 2 * coeff_shift;
151
0
}
152
153
int32_t svt_sb_compute_cdef_list(PictureControlSet* pcs, const Av1Common* const cm, int32_t mi_row, int32_t mi_col,
154
0
                                 CdefList* dlist, BlockSize bs) {
155
0
    int32_t maxc = cm->mi_cols - mi_col;
156
0
    int32_t maxr = cm->mi_rows - mi_row;
157
158
0
    if (bs == BLOCK_128X128 || bs == BLOCK_128X64) {
159
0
        maxc = AOMMIN(maxc, MI_SIZE_128X128);
160
0
    } else {
161
0
        maxc = AOMMIN(maxc, MI_SIZE_64X64);
162
0
    }
163
0
    if (bs == BLOCK_128X128 || bs == BLOCK_64X128) {
164
0
        maxr = AOMMIN(maxr, MI_SIZE_128X128);
165
0
    } else {
166
0
        maxr = AOMMIN(maxr, MI_SIZE_64X64);
167
0
    }
168
169
    // CDEF lists 8x8 luma units; one unit = 2x2 mi blocks (MI_SIZE = 4px), so the mi step is 2 and
170
    // the unit index is the mi index >> 1. (mi_size_wide/high[BLOCK_8X8] == 2, fixed.)
171
0
    int32_t count = 0;
172
0
    for (int32_t r = 0; r < maxr; r += 2) {
173
0
        MbModeInfo** const row0 = pcs->mi_grid_base + (mi_row + r) * pcs->mi_stride + mi_col;
174
0
        MbModeInfo** const row1 = row0 + pcs->mi_stride;
175
0
        for (int32_t c = 0; c < maxc; c += 2) {
176
0
            uint8_t all_skip = row0[c]->block_mi.skip & row0[c + 1]->block_mi.skip & row1[c]->block_mi.skip &
177
0
                row1[c + 1]->block_mi.skip;
178
0
            dlist[count].by = (uint8_t)(r >> 1);
179
0
            dlist[count].bx = (uint8_t)(c >> 1);
180
0
            count += 1 - all_skip;
181
0
        }
182
0
    }
183
0
    return count;
184
0
}
185
186
/*
187
Loop over all 64x64 filter blocks and perform the CDEF filtering for each block, using
188
the filter strength pairs chosen in finish_cdef_search().
189
*/
190
#if CDEF_8BITS_PATH
191
// Native 8-bit src8 builder for both interior and frame-edge fbs: builds the 8-bit src8 buffer
192
// DIRECTLY from recon + the 8-bit line/col halo buffers (plain 8->8 copies, no widen/narrow), then
193
// filters all blocks via svt_cdef_filter_fb_lbd. For frame-edge fbs the off-frame halo is left as
194
// garbage and masked geometrically by the boundary-aware kernel (no 16-bit sentinel buffer).
195
// linebuf/colbuf are viewed as uint8 here (8-bit pixel halos); the persistent buffers are over-
196
// allocated as uint16 for HBD, but the 8-bit path only touches the uint8 half (bit depth is fixed
197
// per pipeline, so no path mixes element sizes within a run).
198
// Off-frame recon reads are avoided: top/tl/tr route to linebuf when prev_row_cdef==1 (true for the
199
// first fb row), left routes to colbuf for fbc==0, and the body copy clamps its bottom/right extent.
200
static inline void cdef_apply_fb(uint8_t* rec_buff, uint32_t rec_stride, uint8_t* src8, uint8_t* linebuf_pli,
201
                                 uint8_t* colbuf_pli, const uint8_t* prev_row_cdef, int fbr, int fbc, int nvfb,
202
                                 int nhfb, int coffset, int mhl2, int mwl2, int nhb, int nvb, int stride, int rend,
203
                                 int cend, int cstart, int cdef_left, uint8_t dir[CDEF_NBLOCKS][CDEF_NBLOCKS],
204
                                 int* dirinit, int32_t var[CDEF_NBLOCKS][CDEF_NBLOCKS], int pli, CdefList* dlist,
205
                                 int cdef_count, int cdef_strength, int damping, int coeff_shift, int xdec, int ydec,
206
                                 int frame_top, int frame_left, int frame_bottom, int frame_right) {
207
    const int vsz = nvb << mhl2;
208
    const int hsz = nhb << mwl2;
209
    // CDEF taps reach only +-CDEF_HALO px, so the recon copies need only a CDEF_HALO-wide halo even
210
    // though the buffer is padded to HBORDER/VBORDER for aligned SIMD loads (the extra border is
211
    // never read into any output). Name the halo geometry inside the padded buffer.
212
    const int halo_col = CDEF_HBORDER - CDEF_HALO; // left-halo column
213
    const int halo_row = CDEF_VBORDER - CDEF_HALO; // top-halo row
214
    const int top_off  = halo_row * CDEF_BSTRIDE + CDEF_HBORDER; // src8 (top halo, body col 0)
215
    const int tl_off   = halo_row * CDEF_BSTRIDE + halo_col; // src8 (top halo, left halo col)
216
    const int tr_off   = halo_row * CDEF_BSTRIDE + CDEF_HBORDER + hsz; // src8 (top halo, right body edge)
217
    const int lrow     = halo_row * stride; // linebuf row offset of the halo
218
    const int cs       = cstart < -CDEF_HALO ? -CDEF_HALO : cstart;
219
    // Off-frame bottom/right have no in-frame halo: clamp the recon body copy so it never reads past
220
    // the frame edge (those taps are masked geometrically by the bounded kernel).
221
    const int body_rows = frame_bottom ? vsz : vsz + CDEF_HALO;
222
    const int cright    = frame_right ? hsz : cend - (CDEF_HBORDER - CDEF_HALO);
223
    // Body + top halo in ONE copy from recon (also brings the left/right and bottom halo). The top
224
    // halo is overwritten from the pre-filter linebuf below wherever the top neighbour fb was already
225
    // CDEF-filtered (its current recon is post-filter and unusable as halo).
226
    svt_cdef_copy_rect8(&src8[(CDEF_VBORDER - CDEF_HALO) * CDEF_BSTRIDE + CDEF_HBORDER + cs],
227
                        CDEF_BSTRIDE,
228
                        rec_buff,
229
                        (MI_SIZE_64X64 << mhl2) * fbr - CDEF_HALO,
230
                        coffset + cs,
231
                        rec_stride,
232
                        CDEF_HALO + body_rows,
233
                        cright - cs);
234
    // Overwrite top center / top-left / top-right from the pre-filter linebuf where that neighbour
235
    // was filtered (else the recon copied above is correct).
236
    if (prev_row_cdef[fbc]) {
237
        svt_cdef_copy_rect8(&src8[top_off], CDEF_BSTRIDE, &linebuf_pli[lrow + coffset], 0, 0, stride, CDEF_HALO, hsz);
238
    }
239
    if (prev_row_cdef[fbc - 1]) {
240
        svt_cdef_copy_rect8(
241
            &src8[tl_off], CDEF_BSTRIDE, &linebuf_pli[lrow + coffset - CDEF_HALO], 0, 0, stride, CDEF_HALO, CDEF_HALO);
242
    }
243
    if (prev_row_cdef[fbc + 1]) {
244
        svt_cdef_copy_rect8(
245
            &src8[tr_off], CDEF_BSTRIDE, &linebuf_pli[lrow + coffset + hsz], 0, 0, stride, CDEF_HALO, CDEF_HALO);
246
    }
247
    // Left halo (from colbuf, if the left fb was filtered).
248
    if (cdef_left) {
249
        svt_cdef_copy_rect8(
250
            &src8[halo_col], CDEF_BSTRIDE, &colbuf_pli[halo_col], 0, 0, CDEF_HBORDER, rend + CDEF_VBORDER, CDEF_HALO);
251
    }
252
    // Save right halo edge to colbuf for the fb to the right.
253
    if (fbc < nhfb - 1) {
254
        svt_cdef_copy_rect8(&colbuf_pli[halo_col],
255
                            CDEF_HBORDER,
256
                            &src8[hsz + halo_col],
257
                            0,
258
                            0,
259
                            CDEF_BSTRIDE,
260
                            rend + CDEF_VBORDER,
261
                            CDEF_HALO);
262
    }
263
    // Save bottom edge to linebuf for the fb below.
264
    if (fbr < nvfb - 1) {
265
        svt_cdef_copy_rect8(&linebuf_pli[coffset],
266
                            stride,
267
                            &rec_buff[((MI_SIZE_64X64 << mhl2) * (fbr + 1) - CDEF_VBORDER) * rec_stride + coffset],
268
                            0,
269
                            0,
270
                            rec_stride,
271
                            CDEF_VBORDER,
272
                            hsz);
273
    }
274
    if (cdef_strength || !*dirinit) {
275
        svt_cdef_filter_fb_lbd(&rec_buff[rec_stride * (MI_SIZE_64X64 * fbr << mhl2) + (fbc * MI_SIZE_64X64 << mwl2)],
276
                               rec_stride,
277
                               &src8[CDEF_VBORDER * CDEF_BSTRIDE + CDEF_HBORDER],
278
                               frame_top,
279
                               frame_left,
280
                               frame_bottom,
281
                               frame_right,
282
                               vsz,
283
                               hsz,
284
                               xdec,
285
                               ydec,
286
                               dir,
287
                               dirinit,
288
                               var,
289
                               pli,
290
                               dlist,
291
                               cdef_count,
292
                               cdef_strength,
293
                               damping,
294
                               coeff_shift,
295
                               1);
296
    }
297
}
298
#endif
299
300
0
void svt_av1_cdef_frame(SequenceControlSet* scs, PictureControlSet* pcs) {
301
0
    PictureParentControlSet* ppcs    = pcs->ppcs;
302
0
    Av1Common*               cm      = ppcs->av1_cm;
303
0
    FrameHeader*             frm_hdr = &ppcs->frm_hdr;
304
0
#if CONFIG_ENABLE_HIGH_BIT_DEPTH
305
0
    const bool is_16bit = scs->is_16bit_pipeline;
306
#else
307
    const bool is_16bit = false;
308
#endif
309
310
0
    EbPictureBufferDesc* recon_pic;
311
0
    svt_aom_get_recon_pic(pcs, &recon_pic, is_16bit);
312
313
0
    const int32_t num_planes = av1_num_planes(&scs->seq_header.color_config);
314
0
#if !CDEF_8BITS_PATH || CONFIG_ENABLE_HIGH_BIT_DEPTH
315
0
    DECLARE_ALIGNED(16, uint16_t, src[CDEF_INBUF_SIZE]); // 16-bit sentinel buffer (HBD / non-8bit path only)
316
0
#endif
317
#if CDEF_8BITS_PATH
318
    // 8-bit mirror of `src` for the native interior filter (no 16-bit-lane work).
319
    DECLARE_ALIGNED(16, uint8_t, src8[CDEF_INBUF_SIZE]);
320
#endif
321
0
    uint16_t*      linebuf[3];
322
0
    uint16_t*      colbuf[3];
323
0
    CdefList       dlist_local[MI_SIZE_64X64 * MI_SIZE_64X64];
324
0
    CdefList*      dlist;
325
0
    uint8_t *      row_cdef, *prev_row_cdef, *curr_row_cdef;
326
0
    int32_t        cdef_count;
327
0
    const uint32_t sb_size = scs->super_block_size;
328
    // Reuse the dlist/count computed by the search (SB=64 and the frame was actually searched).
329
0
    const bool use_dlist_cache = sb_size == 64 && !ppcs->cdef_search_ctrls.use_reference_cdef_fs &&
330
0
        !ppcs->cdef_search_ctrls.use_qp_strength;
331
0
    int32_t        mi_wide_l2[3];
332
0
    int32_t        mi_high_l2[3];
333
0
    int32_t        xdec[3];
334
0
    int32_t        ydec[3];
335
0
    int32_t        coeff_shift = AOMMAX(scs->static_config.encoder_bit_depth - 8, 0);
336
0
    const int32_t  nvfb        = (cm->mi_rows + MI_SIZE_64X64 - 1) / MI_SIZE_64X64;
337
0
    const int32_t  nhfb        = (cm->mi_cols + MI_SIZE_64X64 - 1) / MI_SIZE_64X64;
338
0
    const uint32_t cdef_size   = sizeof(*row_cdef) * (nhfb + 2) * 2;
339
340
    // Persistent scratch: (re)allocate only when it must grow, reuse across frames.
341
0
    if (pcs->cdef_row_cdef_sz < cdef_size) {
342
0
        svt_aom_free(pcs->cdef_row_cdef);
343
0
        pcs->cdef_row_cdef    = (uint8_t*)svt_aom_malloc(cdef_size);
344
0
        pcs->cdef_row_cdef_sz = cdef_size;
345
0
    }
346
0
    row_cdef = pcs->cdef_row_cdef;
347
0
    assert(row_cdef != NULL);
348
0
    memset(row_cdef, 1, cdef_size);
349
0
    prev_row_cdef = row_cdef + 1;
350
0
    curr_row_cdef = prev_row_cdef + nhfb + 2;
351
0
    for (int32_t pli = 0; pli < num_planes; pli++) {
352
0
        int32_t subsampling_x = (pli == 0) ? 0 : 1;
353
0
        int32_t subsampling_y = (pli == 0) ? 0 : 1;
354
0
        xdec[pli]             = subsampling_x; //CHKN xd->plane[pli].subsampling_x;
355
0
        ydec[pli]             = subsampling_y; //CHKN  xd->plane[pli].subsampling_y;
356
0
        mi_wide_l2[pli]       = MI_SIZE_LOG2 - subsampling_x; //CHKN xd->plane[pli].subsampling_x;
357
0
        mi_high_l2[pli]       = MI_SIZE_LOG2 - subsampling_y; //CHKN xd->plane[pli].subsampling_y;
358
0
    }
359
360
0
    const int32_t stride = (cm->mi_cols << MI_SIZE_LOG2) + 2 * CDEF_HBORDER;
361
0
    for (int32_t pli = 0; pli < num_planes; pli++) {
362
0
        const uint32_t lb_sz = sizeof(*linebuf) * CDEF_VBORDER * stride;
363
0
        const uint32_t cb_sz = sizeof(*colbuf) * ((CDEF_BLOCKSIZE << mi_high_l2[pli]) + 2 * CDEF_VBORDER) *
364
0
            CDEF_HBORDER;
365
0
        if (pcs->cdef_linebuf_sz[pli] < lb_sz) {
366
0
            svt_aom_free(pcs->cdef_linebuf[pli]);
367
0
            pcs->cdef_linebuf[pli]    = (uint16_t*)svt_aom_malloc(lb_sz);
368
0
            pcs->cdef_linebuf_sz[pli] = lb_sz;
369
0
        }
370
0
        if (pcs->cdef_colbuf_sz[pli] < cb_sz) {
371
0
            svt_aom_free(pcs->cdef_colbuf[pli]);
372
0
            pcs->cdef_colbuf[pli]    = (uint16_t*)svt_aom_malloc(cb_sz);
373
0
            pcs->cdef_colbuf_sz[pli] = cb_sz;
374
0
        }
375
0
        linebuf[pli] = pcs->cdef_linebuf[pli];
376
0
        colbuf[pli]  = pcs->cdef_colbuf[pli];
377
0
    }
378
    // Frame-level check: if every UV strength entry is 0, no chroma block
379
    // will ever be filtered.  In that case skip all chroma border copies
380
    // (including linebuf/colbuf saves) for the entire frame
381
0
    bool chroma_filter_off = (num_planes > 1);
382
0
    if (chroma_filter_off) {
383
0
        for (int32_t i = 0; i < ppcs->nb_cdef_strengths; i++) {
384
0
            if (frm_hdr->cdef_params.cdef_uv_strength[i] != 0) {
385
0
                chroma_filter_off = false;
386
0
                break;
387
0
            }
388
0
        }
389
0
    }
390
0
    const int32_t active_planes      = chroma_filter_off ? 1 : num_planes;
391
0
    int32_t       mbmi_cdef_strength = 0;
392
0
    for (int32_t fbr = 0; fbr < nvfb; fbr++) {
393
0
        int32_t cdef_left = 1;
394
0
        for (int32_t fbc = 0; fbc < nhfb; fbc++) {
395
            // per-fb state read by the row below; must be written even for skipped fbs
396
0
            curr_row_cdef[fbc] = 0;
397
398
0
            const MbModeInfo* mbmi = pcs->mi_grid_base[MI_SIZE_64X64 * fbr * cm->mi_stride + MI_SIZE_64X64 * fbc];
399
400
            // Find the index of the CDEF strength for the filter block. With a single frame strength
401
            // (nb_cdef_strengths==1, e.g. non-searched frames) every SB uses index 0; only multi-strength
402
            // (searched) frames carry a per-SB index, so the per-SB read is only needed then.
403
0
            if (ppcs->nb_cdef_strengths > 1) {
404
0
                mbmi_cdef_strength = mbmi->cdef_strength;
405
0
            }
406
0
            uint8_t cdef_y_strength  = frm_hdr->cdef_params.cdef_y_strength[mbmi_cdef_strength];
407
0
            uint8_t cdef_uv_strength = frm_hdr->cdef_params.cdef_uv_strength[mbmi_cdef_strength];
408
0
            if (cdef_y_strength == 0 && cdef_uv_strength == 0) {
409
0
                cdef_left = 0;
410
0
                continue;
411
0
            }
412
0
            if (use_dlist_cache) {
413
0
                cdef_count = pcs->cdef_fb_list[fbr * nhfb + fbc].cdef_count;
414
0
                dlist      = pcs->cdef_fb_list[fbr * nhfb + fbc].dlist;
415
0
            } else {
416
0
                cdef_count = svt_sb_compute_cdef_list(
417
0
                    pcs, cm, fbr * MI_SIZE_64X64, fbc * MI_SIZE_64X64, dlist_local, BLOCK_64X64);
418
0
                dlist = dlist_local;
419
0
            }
420
0
            if (cdef_count == 0) {
421
0
                cdef_left = 0;
422
0
                continue;
423
0
            }
424
425
0
            curr_row_cdef[fbc] = 1;
426
427
0
            int32_t nhb, nvb;
428
0
            int32_t cstart = 0;
429
0
            assert(mbmi != NULL && "CDEF ERROR: Skipping Current FB");
430
0
            assert((ppcs->nb_cdef_strengths == 1 || mbmi->cdef_strength != -1) && "CDEF ERROR: Skipping Current FB");
431
0
            if (!cdef_left) {
432
                //CHKN if the left block has not been filtered, then we can use samples on the left as input.
433
0
                cstart = -CDEF_HBORDER;
434
0
            }
435
436
0
            nhb = AOMMIN(MI_SIZE_64X64, cm->mi_cols - MI_SIZE_64X64 * fbc);
437
0
            nvb = AOMMIN(MI_SIZE_64X64, cm->mi_rows - MI_SIZE_64X64 * fbr);
438
            // A fb edge is a frame boundary iff the fb sits in the first/last fb row or column.
439
            // nvfb/nhfb = ceil(mi_rows|mi_cols / 64), so only the last fb row/col can reach the frame
440
            // edge -> the per-mi cm->mi_* compare the original used is always equivalent to this.
441
0
            const int32_t frame_top    = (fbr == 0);
442
0
            const int32_t frame_left   = (fbc == 0);
443
0
            const int32_t frame_bottom = (fbr == nvfb - 1);
444
0
            const int32_t frame_right  = (fbc == nhfb - 1);
445
446
0
            int dirinit = !(ppcs->cdef_search_ctrls.use_reference_cdef_fs || ppcs->cdef_search_ctrls.use_qp_strength);
447
            // When SB 128 is used, the search for certain blocks is skipped, so dir/var info is not generated
448
            // In those cases, must generate info here
449
0
            if (sb_size == 128) {
450
0
                const BlockSize bsize = mbmi->bsize;
451
0
                if (((fbc & 1) && (bsize == BLOCK_128X128 || bsize == BLOCK_128X64)) ||
452
0
                    ((fbr & 1) && (bsize == BLOCK_128X128 || bsize == BLOCK_64X128))) {
453
0
                    dirinit = 0;
454
0
                }
455
0
            }
456
0
            uint8_t (*dir)[CDEF_NBLOCKS][CDEF_NBLOCKS] = &pcs->cdef_dir_data[fbr * nhfb + fbc].dir;
457
0
            int32_t (*var)[CDEF_NBLOCKS][CDEF_NBLOCKS] = &pcs->cdef_dir_data[fbr * nhfb + fbc].var;
458
0
            for (int32_t pli = 0; pli < active_planes; pli++) {
459
                // Strength index for this plane: luma uses Y, chroma planes both use UV. The packed
460
                // value is passed to the leaf filters, which decode primary/secondary internally.
461
0
                const int32_t cdef_strength = pli ? cdef_uv_strength : cdef_y_strength;
462
0
                const int32_t damping       = frm_hdr->cdef_params.cdef_damping;
463
464
0
                int32_t hsize = nhb << mi_wide_l2[pli];
465
0
                int32_t vsize = nvb << mi_high_l2[pli];
466
0
                int32_t rend  = (fbr == nvfb - 1) ? vsize : vsize + CDEF_VBORDER;
467
0
                int32_t cend  = (fbc == nhfb - 1) ? hsize : hsize + CDEF_HBORDER;
468
469
0
                int32_t  coffset    = fbc * MI_SIZE_64X64 << mi_wide_l2[pli];
470
0
                EbByte   rec_buff   = recon_pic->buffer[pli];
471
0
                uint32_t rec_stride = recon_pic->stride[pli];
472
473
#if CDEF_8BITS_PATH
474
                // 8-bit pipeline: build src8 directly from recon (interior AND frame-edge fbs) and
475
                // filter via the boundary-aware hybrid -- no 16-bit sentinel buffer, no widen/narrow.
476
                if (!is_16bit) {
477
                    cdef_apply_fb(rec_buff,
478
                                  rec_stride,
479
                                  src8,
480
                                  (uint8_t*)linebuf[pli],
481
                                  (uint8_t*)colbuf[pli],
482
                                  prev_row_cdef,
483
                                  fbr,
484
                                  fbc,
485
                                  nvfb,
486
                                  nhfb,
487
                                  coffset,
488
                                  mi_high_l2[pli],
489
                                  mi_wide_l2[pli],
490
                                  nhb,
491
                                  nvb,
492
                                  stride,
493
                                  rend,
494
                                  cend,
495
                                  cstart,
496
                                  cdef_left,
497
                                  *dir,
498
                                  &dirinit,
499
                                  *var,
500
                                  pli,
501
                                  dlist,
502
                                  cdef_count,
503
                                  cdef_strength,
504
                                  damping,
505
                                  coeff_shift,
506
                                  xdec[pli],
507
                                  ydec[pli],
508
                                  frame_top,
509
                                  frame_left,
510
                                  frame_bottom,
511
                                  frame_right);
512
                    continue;
513
                }
514
#endif
515
516
0
#if !CDEF_8BITS_PATH || CONFIG_ENABLE_HIGH_BIT_DEPTH
517
                /* Copy in the pixels we need from the current superblock for
518
                   deringing. CDEF taps reach only +-CDEF_HALO px, so a 2-px halo into the
519
                   padded src buffer suffices (ring + interior blocks both read only +-2). */
520
0
                const int halo_col = CDEF_HBORDER - CDEF_HALO; // left/right halo column
521
0
                const int halo_row = CDEF_VBORDER - CDEF_HALO; // top halo row
522
0
                const int top_off  = halo_row * CDEF_BSTRIDE + CDEF_HBORDER; // src (top halo, body col 0)
523
0
                const int tl_off   = halo_row * CDEF_BSTRIDE + halo_col; // src (top halo, left halo col)
524
0
                const int tr_off   = halo_row * CDEF_BSTRIDE + CDEF_HBORDER + hsize; // src (top halo, right body edge)
525
0
                const int lrow     = halo_row * stride; // linebuf row offset of the halo
526
0
                const int e_cs     = cstart < -CDEF_HALO ? -CDEF_HALO : cstart;
527
0
                const int e_cright = cend > hsize + CDEF_HALO ? hsize + CDEF_HALO : cend;
528
0
                const int e_rbot   = rend > vsize + CDEF_HALO ? vsize + CDEF_HALO : rend;
529
0
                svt_aom_copy_sb8_16(&src[CDEF_VBORDER * CDEF_BSTRIDE + CDEF_HBORDER + e_cs],
530
0
                                    CDEF_BSTRIDE,
531
0
                                    rec_buff,
532
0
                                    (MI_SIZE_64X64 << mi_high_l2[pli]) * fbr,
533
0
                                    coffset + e_cs,
534
0
                                    rec_stride,
535
0
                                    e_rbot,
536
0
                                    e_cright - e_cs,
537
0
                                    is_16bit);
538
0
                if (!prev_row_cdef[fbc]) {
539
0
                    svt_aom_copy_sb8_16(&src[top_off],
540
0
                                        CDEF_BSTRIDE,
541
0
                                        rec_buff,
542
0
                                        (MI_SIZE_64X64 << mi_high_l2[pli]) * fbr - CDEF_HALO,
543
0
                                        coffset,
544
0
                                        rec_stride,
545
0
                                        CDEF_HALO,
546
0
                                        hsize,
547
0
                                        is_16bit);
548
0
                } else if (fbr > 0) {
549
0
                    svt_aom_copy_rect(
550
0
                        &src[top_off], CDEF_BSTRIDE, &linebuf[pli][lrow + coffset], stride, CDEF_HALO, hsize);
551
0
                } else {
552
0
                    svt_aom_fill_rect(&src[top_off], CDEF_BSTRIDE, CDEF_HALO, hsize, CDEF_VERY_LARGE);
553
0
                }
554
555
0
                if (!prev_row_cdef[fbc - 1]) {
556
0
                    svt_aom_copy_sb8_16(&src[tl_off],
557
0
                                        CDEF_BSTRIDE,
558
0
                                        rec_buff,
559
0
                                        (MI_SIZE_64X64 << mi_high_l2[pli]) * fbr - CDEF_HALO,
560
0
                                        coffset - CDEF_HALO,
561
0
                                        rec_stride,
562
0
                                        CDEF_HALO,
563
0
                                        CDEF_HALO,
564
0
                                        is_16bit);
565
0
                } else if (fbr > 0 && fbc > 0) {
566
0
                    svt_aom_copy_rect(&src[tl_off],
567
0
                                      CDEF_BSTRIDE,
568
0
                                      &linebuf[pli][lrow + coffset - CDEF_HALO],
569
0
                                      stride,
570
0
                                      CDEF_HALO,
571
0
                                      CDEF_HALO);
572
0
                } else {
573
0
                    svt_aom_fill_rect(&src[tl_off], CDEF_BSTRIDE, CDEF_HALO, CDEF_HALO, CDEF_VERY_LARGE);
574
0
                }
575
576
0
                if (!prev_row_cdef[fbc + 1]) {
577
0
                    svt_aom_copy_sb8_16(&src[tr_off],
578
0
                                        CDEF_BSTRIDE,
579
0
                                        rec_buff,
580
0
                                        (MI_SIZE_64X64 << mi_high_l2[pli]) * fbr - CDEF_HALO,
581
0
                                        coffset + hsize,
582
0
                                        rec_stride,
583
0
                                        CDEF_HALO,
584
0
                                        CDEF_HALO,
585
0
                                        is_16bit);
586
0
                } else if (fbr > 0 && fbc < nhfb - 1) {
587
0
                    svt_aom_copy_rect(&src[tr_off],
588
0
                                      CDEF_BSTRIDE,
589
0
                                      &linebuf[pli][lrow + coffset + hsize],
590
0
                                      stride,
591
0
                                      CDEF_HALO,
592
0
                                      CDEF_HALO);
593
0
                } else {
594
0
                    svt_aom_fill_rect(&src[tr_off], CDEF_BSTRIDE, CDEF_HALO, CDEF_HALO, CDEF_VERY_LARGE);
595
0
                }
596
597
0
                if (cdef_left) {
598
                    /* If we deringed the superblock on the left then we need to copy in
599
                       saved pixels. */
600
0
                    svt_aom_copy_rect(&src[halo_col],
601
0
                                      CDEF_BSTRIDE,
602
0
                                      &colbuf[pli][halo_col],
603
0
                                      CDEF_HBORDER,
604
0
                                      rend + CDEF_VBORDER,
605
0
                                      CDEF_HALO);
606
0
                }
607
608
                /* Saving pixels in case we need to dering the superblock on the
609
                    right. */
610
0
                if (fbc < nhfb - 1) {
611
0
                    svt_aom_copy_rect(&colbuf[pli][halo_col],
612
0
                                      CDEF_HBORDER,
613
0
                                      &src[hsize + halo_col],
614
0
                                      CDEF_BSTRIDE,
615
0
                                      rend + CDEF_VBORDER,
616
0
                                      CDEF_HALO);
617
0
                }
618
619
0
                if (fbr < nvfb - 1) {
620
0
                    svt_aom_copy_sb8_16(&linebuf[pli][coffset],
621
0
                                        stride,
622
0
                                        rec_buff,
623
0
                                        (MI_SIZE_64X64 << mi_high_l2[pli]) * (fbr + 1) - CDEF_VBORDER,
624
0
                                        coffset,
625
0
                                        rec_stride,
626
0
                                        CDEF_VBORDER,
627
0
                                        hsize,
628
0
                                        is_16bit);
629
0
                }
630
631
0
                if (frame_top) {
632
0
                    svt_aom_fill_rect(&src[halo_row * CDEF_BSTRIDE + halo_col],
633
0
                                      CDEF_BSTRIDE,
634
0
                                      CDEF_HALO,
635
0
                                      hsize + 2 * CDEF_HALO,
636
0
                                      CDEF_VERY_LARGE);
637
0
                }
638
0
                if (frame_left) {
639
0
                    svt_aom_fill_rect(&src[halo_row * CDEF_BSTRIDE + halo_col],
640
0
                                      CDEF_BSTRIDE,
641
0
                                      vsize + 2 * CDEF_HALO,
642
0
                                      CDEF_HALO,
643
0
                                      CDEF_VERY_LARGE);
644
0
                }
645
0
                if (frame_bottom) {
646
0
                    svt_aom_fill_rect(&src[(vsize + CDEF_VBORDER) * CDEF_BSTRIDE + halo_col],
647
0
                                      CDEF_BSTRIDE,
648
0
                                      CDEF_HALO,
649
0
                                      hsize + 2 * CDEF_HALO,
650
0
                                      CDEF_VERY_LARGE);
651
0
                }
652
0
                if (frame_right) {
653
0
                    svt_aom_fill_rect(&src[halo_row * CDEF_BSTRIDE + CDEF_HBORDER + hsize],
654
0
                                      CDEF_BSTRIDE,
655
0
                                      vsize + 2 * CDEF_HALO,
656
0
                                      CDEF_HALO,
657
0
                                      CDEF_VERY_LARGE);
658
0
                }
659
                // if ppcs->cdef_ctrls.use_reference_cdef_fs is true, then search was not performed
660
                // Therefore, need to make sure dir and var are initialized
661
0
                if (cdef_strength || !dirinit) {
662
                    // 8-bit content is handled above by cdef_apply_fb (which continues); this
663
                    // 16-bit sentinel path now serves only the HBD (is_16bit) pipeline (and the
664
                    // whole-frame fallback when CDEF_8BITS_PATH is disabled).
665
0
                    svt_cdef_filter_fb(
666
0
                        is_16bit ? NULL
667
0
                                 : &rec_buff[rec_stride * (MI_SIZE_64X64 * fbr << mi_high_l2[pli]) +
668
0
                                             (fbc * MI_SIZE_64X64 << mi_wide_l2[pli])],
669
0
                        is_16bit ? &((uint16_t*)rec_buff)[rec_stride * (MI_SIZE_64X64 * fbr << mi_high_l2[pli]) +
670
0
                                                          (fbc * MI_SIZE_64X64 << mi_wide_l2[pli])]
671
0
                                 : NULL,
672
0
                        rec_stride,
673
0
                        &src[CDEF_VBORDER * CDEF_BSTRIDE + CDEF_HBORDER],
674
0
                        xdec[pli],
675
0
                        ydec[pli],
676
0
                        *dir,
677
0
                        &dirinit,
678
0
                        *var,
679
0
                        pli,
680
0
                        dlist,
681
0
                        cdef_count,
682
0
                        cdef_strength,
683
0
                        damping,
684
0
                        coeff_shift,
685
0
                        1); // no subsampling
686
0
                }
687
0
#endif
688
0
            }
689
0
            cdef_left = 1; //CHKN filtered data is written back directy to recFrame.
690
0
        }
691
0
        {
692
0
            uint8_t* tmp  = prev_row_cdef;
693
0
            prev_row_cdef = curr_row_cdef;
694
0
            curr_row_cdef = tmp;
695
0
        }
696
0
    }
697
0
}
698
699
///-------search
700
/*
701
 * Search for the best luma+chroma strength to add as an option, knowing we
702
 * already selected nb_strengths options
703
 *
704
 * Params:
705
 *
706
 * lev0 : Array of indices of selected luma strengths.
707
 * lev1 : Array of indices of selected chroma strengths.
708
 * nb_strengths : Number of selected (Luma_strength, Chroma_strength) pairs.
709
 * mse : Array of luma and chroma filtering mse values.
710
 * sb_count : Number of filter blocks in the frame.
711
 * start_gi : starting strength index for the search of the additional strengths.
712
 * end_gi : End index for the for the search of the additional strengths.
713
*/
714
uint64_t svt_search_one_dual_c(int* lev0, int* lev1, int nb_strengths, uint64_t** mse[2], int sb_count, int start_gi,
715
0
                               int end_gi) {
716
0
    uint64_t      tot_mse[TOTAL_STRENGTHS][TOTAL_STRENGTHS];
717
0
    int32_t       i, j;
718
0
    uint64_t      best_tot_mse    = (uint64_t)1 << 63;
719
0
    int32_t       best_id0        = 0;
720
0
    int32_t       best_id1        = 0;
721
0
    const int32_t total_strengths = end_gi;
722
0
    memset(tot_mse, 0, sizeof(tot_mse));
723
    /* Loop over the filter blocks in the frame */
724
0
    for (i = 0; i < sb_count; i++) {
725
0
        int32_t  gi;
726
0
        uint64_t best_mse = (uint64_t)1 << 63;
727
        /* Loop over the already selected nb_strengths (Luma_strength,
728
           Chroma_strength) pairs, and find the pair that has the smallest mse
729
           (best_mse) for the current filter block.*/
730
        /* Find best mse among already selected options. */
731
0
        for (gi = 0; gi < nb_strengths; gi++) {
732
0
            uint64_t curr = mse[0][i][lev0[gi]];
733
0
            curr += mse[1][i][lev1[gi]];
734
0
            if (curr < best_mse) {
735
0
                best_mse = curr;
736
0
            }
737
0
        }
738
        /* Loop over the set of available (Luma_strength, Chroma_strength)
739
           pairs, identify any that provide an mse better than best_mse from the
740
           step above for the current filter block, and update any corresponding
741
           total mse (tot_mse[j][k]). */
742
        /* Find best mse when adding each possible new option. */
743
0
        for (j = start_gi; j < total_strengths; j++) {
744
0
            int32_t k;
745
0
            for (k = start_gi; k < total_strengths; k++) {
746
0
                uint64_t best = best_mse;
747
0
                uint64_t curr = mse[0][i][j];
748
0
                curr += mse[1][i][k];
749
0
                if (curr < best) {
750
0
                    best = curr;
751
0
                }
752
0
                tot_mse[j][k] += best;
753
0
            }
754
0
        }
755
0
    }
756
    /* Loop over the additionally searched (Luma_strength, Chroma_strength) pairs
757
       from the step above, and identify any such pair that provided the best mse for
758
       the whole frame. The identified pair would be added to the set of already selected pairs. */
759
0
    for (j = start_gi; j < total_strengths; j++) { // Loop over the additionally searched luma strengths
760
0
        int32_t k;
761
0
        for (k = start_gi; k < total_strengths; k++) { // Loop over the additionally searched chroma strengths
762
0
            if (tot_mse[j][k] < best_tot_mse) {
763
0
                best_tot_mse = tot_mse[j][k];
764
0
                best_id0     = j; // index for the best luma strength
765
0
                best_id1     = k; // index for the best chroma strength
766
0
            }
767
0
        }
768
0
    }
769
0
    lev0[nb_strengths] = best_id0; // Add the identified luma strength to the list of selected luma strengths
770
0
    lev1[nb_strengths] = best_id1; // Add the identified chroma strength to the list of selected chroma strengths
771
0
    return best_tot_mse;
772
0
}
773
774
/*
775
 * Search for the set of luma+chroma strengths that minimizes mse.
776
 *
777
 * Params:
778
 *
779
 * best_lev0 : Array of indices of selected luma strengths.
780
 * best_lev1 : Array of indices of selected chroma strengths.
781
 * nb_strengths : Number of selected (Luma_strength, Chroma_strength) pairs.
782
 * mse : Array of luma and chroma filtering mse values.
783
 * sb_count : Number of filter blocks in the frame.
784
 * start_gi : starting strength index for the search of the additional strengths.
785
 * end_gi : End index for the for the search of the additional strengths.
786
*/
787
static uint64_t joint_strength_search_dual(int32_t* best_lev0, int32_t* best_lev1, int32_t nb_strengths,
788
0
                                           uint64_t** mse[2], int32_t sb_count, int32_t start_gi, int32_t end_gi) {
789
0
    uint64_t best_tot_mse;
790
0
    int32_t  i;
791
0
    best_tot_mse = (uint64_t)1 << 63;
792
    /* Greedy search: add one strength options at a time.
793
794
    Determine nb_strengths (Luma_strength, Chroma_strength) pairs.
795
    The list of nb_strengths pairs is determined by adding one such pair at
796
    a time through the call to the function search_one_dual. When the
797
    function search_one_dual is called, the search accounts for the
798
    strength pairs that have already been added in the previous iteration of
799
    the loop below. The loop below returns in the end best_tot_mse
800
    representing the best filtering mse for the whole frame based on the
801
    selected list of best (Luma_strength, Chroma_strength) pairs.
802
    */
803
0
    for (i = 0; i < nb_strengths; i++) {
804
0
        best_tot_mse = svt_search_one_dual(best_lev0, best_lev1, i, mse, sb_count, start_gi, end_gi);
805
0
    }
806
    /* Performing further refinements on the search based on the results
807
    from the step above. Trying to refine the greedy search by reconsidering each
808
    already-selected option. */
809
0
    for (i = 0; i < 4 * nb_strengths; i++) {
810
0
        int32_t j;
811
0
        for (j = 0; j < nb_strengths - 1; j++) {
812
0
            best_lev0[j] = best_lev0[j + 1];
813
0
            best_lev1[j] = best_lev1[j + 1];
814
0
        }
815
0
        best_tot_mse = svt_search_one_dual(best_lev0, best_lev1, nb_strengths - 1, mse, sb_count, start_gi, end_gi);
816
0
    }
817
0
    return best_tot_mse;
818
0
}
819
820
// This kernel is ported/adapted from libaom (AV1 reference implementation).
821
// Original logic inspired by aom_pick_cdef_from_qp().
822
// Adjusted to match SVT-AV1 data structures and pipeline integration.
823
static void svt_pick_cdef_from_qp(PictureParentControlSet* ppcs, int32_t is_screen_content, int32_t* pred_y_strength,
824
243
                                  int32_t* pred_uv_strength) {
825
243
    FrameHeader*  frm_hdr    = &ppcs->frm_hdr;
826
243
    const uint8_t bit_depth  = ppcs->enhanced_pic->bit_depth;
827
243
    const int32_t base_q_idx = frm_hdr->quantization_params.base_q_idx;
828
829
243
    int32_t q = svt_aom_ac_quant_qtx(base_q_idx, 0, bit_depth);
830
243
    q >>= (bit_depth - 8);
831
832
243
    int32_t y_f1 = 0, y_f2 = 0;
833
243
    int32_t uv_f1 = 0, uv_f2 = 0;
834
835
243
    const int32_t is_intra = (frm_hdr->frame_type == KEY_FRAME || frm_hdr->frame_type == INTRA_ONLY_FRAME);
836
837
243
    if (is_screen_content) {
838
0
        y_f1 = (int32_t)(5.88217781e-06 * q * q + 6.10391455e-03 * q + 9.95043102e-02);
839
840
0
        y_f2 = (int32_t)(-7.79934857e-06 * q * q + 6.58957830e-03 * q + 8.81045025e-01);
841
842
0
        uv_f1 = (int32_t)(-6.79500136e-06 * q * q + 1.02695586e-02 * q + 1.36126802e-01);
843
844
0
        uv_f2 = (int32_t)(-9.99613695e-08 * q * q - 1.79361339e-05 * q + 1.17022324e+0);
845
243
    } else if (!is_intra) {
846
0
        y_f1 = (int32_t)roundf(q * q * -0.0000023593946f + q * 0.0068615186f + 0.02709886f);
847
848
0
        y_f2 = (int32_t)roundf(q * q * -0.00000057629734f + q * 0.0013993345f + 0.03831067f);
849
850
0
        uv_f1 = (int32_t)roundf(q * q * -0.0000007095069f + q * 0.0034628846f + 0.00887099f);
851
852
0
        uv_f2 = (int32_t)roundf(q * q * 0.00000023874085f + q * 0.00028223585f + 0.05576307f);
853
243
    } else { // Intra
854
243
        y_f1 = (int32_t)roundf(q * q * 0.0000033731974f + q * 0.008070594f + 0.0187634f);
855
856
243
        y_f2 = (int32_t)roundf(q * q * 0.0000029167343f + q * 0.0027798624f + 0.0079405f);
857
858
243
        uv_f1 = (int32_t)roundf(q * q * -0.0000130790995f + q * 0.012892405f - 0.00748388f);
859
860
243
        uv_f2 = (int32_t)roundf(q * q * 0.0000032651783f + q * 0.00035520183f + 0.00228092f);
861
243
    }
862
863
    // Clamp to AV1 limits
864
243
    y_f1  = clamp(y_f1, 0, 15);
865
243
    y_f2  = clamp(y_f2, 0, 3);
866
243
    uv_f1 = clamp(uv_f1, 0, 15);
867
243
    uv_f2 = clamp(uv_f2, 0, 3);
868
869
    // Pack primary + secondary
870
243
    *pred_y_strength  = y_f1 * CDEF_SEC_STRENGTHS + y_f2;
871
243
    *pred_uv_strength = uv_f1 * CDEF_SEC_STRENGTHS + uv_f2;
872
243
}
873
874
// Propagate cdef_strength to all 64x64 mi
875
0
static INLINE void propagate_cdef_strength(PictureControlSet* pcs, int32_t sb_index, int8_t strength) {
876
0
    MbModeInfo* mbmi    = pcs->mi_grid_base[sb_index];
877
0
    mbmi->cdef_strength = strength;
878
0
    switch (mbmi->bsize) {
879
0
    case BLOCK_128X128:
880
0
        pcs->mi_grid_base[sb_index + MI_SIZE_64X64]->cdef_strength                                  = strength;
881
0
        pcs->mi_grid_base[sb_index + MI_SIZE_64X64 * pcs->mi_stride]->cdef_strength                 = strength;
882
0
        pcs->mi_grid_base[sb_index + MI_SIZE_64X64 * pcs->mi_stride + MI_SIZE_64X64]->cdef_strength = strength;
883
0
        break;
884
0
    case BLOCK_128X64:
885
0
        pcs->mi_grid_base[sb_index + MI_SIZE_64X64]->cdef_strength = strength;
886
0
        break;
887
0
    case BLOCK_64X128:
888
0
        pcs->mi_grid_base[sb_index + MI_SIZE_64X64 * pcs->mi_stride]->cdef_strength = strength;
889
0
        break;
890
0
    default:
891
0
        break;
892
0
    }
893
0
}
894
895
243
#define CDEF_DAMPING_FROM_QP(base_q_idx) (3 + ((base_q_idx) >> 6))
896
897
243
void finish_cdef_search(PictureControlSet* pcs) {
898
243
    PictureParentControlSet* ppcs    = pcs->ppcs;
899
243
    FrameHeader*             frm_hdr = &ppcs->frm_hdr;
900
243
    Av1Common*               cm      = ppcs->av1_cm;
901
243
    int32_t                  mi_rows = ppcs->av1_cm->mi_rows;
902
243
    int32_t                  mi_cols = ppcs->av1_cm->mi_cols;
903
904
243
    int32_t  fbr, fbc;
905
243
    uint64_t best_tot_mse = (uint64_t)1 << 63;
906
243
    int32_t  sb_count;
907
243
    int32_t  nvfb = (mi_rows + MI_SIZE_64X64 - 1) / MI_SIZE_64X64;
908
243
    int32_t  nhfb = (mi_cols + MI_SIZE_64X64 - 1) / MI_SIZE_64X64;
909
910
243
    CdefSearchControls* cdef_search_ctrls = &pcs->ppcs->cdef_search_ctrls;
911
912
243
    if (cdef_search_ctrls->use_qp_strength) {
913
243
        const bool    allintra  = ppcs->scs->allintra;
914
243
        const uint8_t sc_class1 = ppcs->sc_class1;
915
243
        const uint8_t sc_class5 = ppcs->sc_class5;
916
243
        const uint8_t sc        = allintra ? sc_class5 : sc_class1;
917
243
        int           pred_y, pred_uv;
918
243
        svt_pick_cdef_from_qp(ppcs, sc, &pred_y, &pred_uv);
919
243
        frm_hdr->cdef_params.cdef_bits           = 0;
920
243
        ppcs->nb_cdef_strengths                  = 1;
921
243
        frm_hdr->cdef_params.cdef_y_strength[0]  = pred_y;
922
243
        frm_hdr->cdef_params.cdef_uv_strength[0] = pred_uv;
923
243
        frm_hdr->cdef_params.cdef_damping        = CDEF_DAMPING_FROM_QP(frm_hdr->quantization_params.base_q_idx);
924
        // nb_cdef_strengths==1 -> apply uses strength index 0 for every SB; no per-SB cdef_strength
925
        // propagation needed.
926
243
        return;
927
243
    }
928
929
0
    CdefReconControls* cdef_recon_ctrls           = &pcs->ppcs->cdef_recon_ctrls;
930
0
    const int          first_pass_fs_num          = cdef_search_ctrls->first_pass_fs_num;
931
0
    const int          default_second_pass_fs_num = cdef_search_ctrls->default_second_pass_fs_num;
932
933
0
    frm_hdr->cdef_params.cdef_bits           = 0;
934
0
    ppcs->nb_cdef_strengths                  = 1;
935
0
    frm_hdr->cdef_params.cdef_y_strength[0]  = cdef_search_ctrls->pred_y_f;
936
0
    frm_hdr->cdef_params.cdef_uv_strength[0] = cdef_search_ctrls->pred_uv_f;
937
0
    frm_hdr->cdef_params.cdef_damping        = CDEF_DAMPING_FROM_QP(frm_hdr->quantization_params.base_q_idx);
938
939
0
    if (cdef_search_ctrls->use_reference_cdef_fs) {
940
        // nb_cdef_strengths==1 -> apply uses strength index 0 for every SB; no per-SB cdef_strength
941
        // propagation needed.
942
0
        return;
943
0
    }
944
945
    // Persistent per-pcs scratch (allocated once with the pcs, sized b64_total_count) instead of a
946
    // per-frame malloc/free. mse[] are pointer arrays into the persistent mse_seg.
947
0
    int32_t*   sb_index = pcs->cdef_sb_index;
948
0
    uint64_t** mse[2]   = {pcs->cdef_mse_ptr[0], pcs->cdef_mse_ptr[1]};
949
950
0
    const int32_t start_gi = 0;
951
0
    const int32_t end_gi   = first_pass_fs_num + default_second_pass_fs_num;
952
0
    int32_t       i;
953
0
    int32_t       nb_strengths;
954
0
    int32_t       nb_strength_bits = 0;
955
0
    uint64_t      lambda;
956
0
    uint32_t      fast_lambda, full_lambda = 0;
957
958
0
    svt_aom_lambda_assign(pcs,
959
0
                          &fast_lambda,
960
0
                          &full_lambda,
961
0
                          pcs->ppcs->enhanced_pic->bit_depth,
962
0
                          pcs->ppcs->frm_hdr.quantization_params.base_q_idx,
963
0
                          false);
964
0
    lambda   = full_lambda;
965
0
    sb_count = 0;
966
967
0
    for (fbr = 0; fbr < nvfb; ++fbr) {
968
0
        for (fbc = 0; fbc < nhfb; ++fbc) {
969
0
            const MbModeInfo* mbmi = pcs->mi_grid_base[MI_SIZE_64X64 * fbr * cm->mi_stride + MI_SIZE_64X64 * fbc];
970
0
            if (((fbc & 1) && (mbmi->bsize == BLOCK_128X128 || mbmi->bsize == BLOCK_128X64)) ||
971
0
                ((fbr & 1) && (mbmi->bsize == BLOCK_128X128 || mbmi->bsize == BLOCK_64X128))) {
972
0
                continue;
973
0
            }
974
0
            if (pcs->skip_cdef_seg[fbr * nhfb + fbc]) {
975
0
                continue;
976
0
            }
977
978
0
            mse[0][sb_count]   = pcs->mse_seg[0][fbr * nhfb + fbc];
979
0
            mse[1][sb_count]   = pcs->mse_seg[1][fbr * nhfb + fbc];
980
0
            sb_index[sb_count] = MI_SIZE_64X64 * fbr * pcs->mi_stride + MI_SIZE_64X64 * fbc;
981
0
            sb_count++;
982
0
        }
983
0
    }
984
985
    // Scale down the cost of the (0,0) filter strength to bias selection towards off.  When off, we can save the cost of the application
986
0
    if (cdef_recon_ctrls->zero_fs_cost_bias) {
987
0
        const bool is_16bit = (pcs->scs->static_config.encoder_bit_depth > EB_EIGHT_BIT);
988
0
        uint16_t   factor;
989
0
        for (i = 0; i < sb_count; i++) {
990
0
            if (is_16bit) {
991
0
                factor = cdef_recon_ctrls->zero_fs_cost_bias;
992
0
                if (mse[0][i][0] < 5000) {
993
0
                    factor = MIN(factor - 10, 64);
994
0
                } else if (mse[0][i][0] < 10000) {
995
0
                    factor = MIN(factor - 5, 64);
996
0
                } else if (mse[0][i][0] > 25000) {
997
0
                    factor = MIN(factor + 1, 64);
998
0
                }
999
0
                mse[0][i][0] = (factor * mse[0][i][0]) >> 6;
1000
1001
0
                factor = cdef_recon_ctrls->zero_fs_cost_bias;
1002
0
                if (mse[1][i][0] < 5000) {
1003
0
                    factor = MIN(factor - 10, 64);
1004
0
                } else if (mse[1][i][0] < 10000) {
1005
0
                    factor = MIN(factor - 5, 64);
1006
0
                } else if (mse[1][i][0] > 25000) {
1007
0
                    factor = MIN(factor + 1, 64);
1008
0
                }
1009
0
                mse[1][i][0] = (factor * mse[1][i][0]) >> 6;
1010
0
            } else {
1011
0
                factor = cdef_recon_ctrls->zero_fs_cost_bias;
1012
0
                if (mse[0][i][0] > 25000) {
1013
0
                    factor = MIN(factor + 2, 64);
1014
0
                } else if (mse[0][i][0] > 10000) {
1015
0
                    factor = MIN(factor + 1, 64);
1016
0
                }
1017
0
                mse[0][i][0] = (factor * mse[0][i][0]) >> 6;
1018
1019
0
                factor = cdef_recon_ctrls->zero_fs_cost_bias;
1020
0
                if (mse[1][i][0] > 25000) {
1021
0
                    factor = MIN(factor + 2, 64);
1022
0
                } else if (mse[1][i][0] > 10000) {
1023
0
                    factor = MIN(factor + 1, 64);
1024
0
                }
1025
0
                mse[1][i][0] = (factor * mse[1][i][0]) >> 6;
1026
0
            }
1027
0
        }
1028
0
    }
1029
1030
    // Compute cost of (strength=0) to derive pcs->cdef_dist_dev
1031
0
    int64_t zero_dist = 0;
1032
0
    for (i = 0; i < sb_count; i++) {
1033
0
        zero_dist += mse[0][i][0] + mse[1][i][0];
1034
0
    }
1035
0
    uint64_t zero_cost = RDCOST(lambda, av1_cost_literal(CDEF_STRENGTH_BITS * 2), zero_dist << 4);
1036
1037
    // Search for different numbers of signalling bits
1038
0
    for (i = 0; i <= 3; i++) {
1039
0
        int32_t best_lev0[CDEF_MAX_STRENGTHS] = {0};
1040
0
        int32_t best_lev1[CDEF_MAX_STRENGTHS] = {0};
1041
0
        nb_strengths                          = 1 << i;
1042
0
        uint64_t tot_mse                      = joint_strength_search_dual(
1043
0
            best_lev0, best_lev1, nb_strengths, mse, sb_count, start_gi, end_gi);
1044
1045
0
        const int      total_bits = sb_count * i + nb_strengths * CDEF_STRENGTH_BITS * 2;
1046
0
        const uint64_t cost       = RDCOST(lambda, av1_cost_literal(total_bits), tot_mse * 16);
1047
0
        if (cost < best_tot_mse) {
1048
0
            best_tot_mse     = cost;
1049
0
            nb_strength_bits = i;
1050
0
            for (int32_t j = 0; j < 1 << nb_strength_bits; j++) {
1051
0
                frm_hdr->cdef_params.cdef_y_strength[j]  = best_lev0[j];
1052
0
                frm_hdr->cdef_params.cdef_uv_strength[j] = cdef_search_ctrls->uv_from_y ? best_lev0[j] : best_lev1[j];
1053
0
            }
1054
0
        }
1055
0
    }
1056
1057
0
    pcs->cdef_dist_dev = zero_cost == 0 ? 0 : (int32_t)(1000 - ((1000 * best_tot_mse) / zero_cost));
1058
0
    nb_strengths       = 1 << nb_strength_bits;
1059
1060
0
    frm_hdr->cdef_params.cdef_bits = nb_strength_bits;
1061
0
    ppcs->nb_cdef_strengths        = nb_strengths;
1062
1063
    // Assign each filter block its best strength index
1064
0
    for (i = 0; i < sb_count; i++) {
1065
0
        int32_t  gi;
1066
0
        int32_t  best_gi  = 0;
1067
0
        uint64_t best_mse = (uint64_t)1 << 63;
1068
0
        for (gi = 0; gi < ppcs->nb_cdef_strengths; gi++) {
1069
0
            uint64_t curr = mse[0][i][frm_hdr->cdef_params.cdef_y_strength[gi]] +
1070
0
                mse[1][i][frm_hdr->cdef_params.cdef_uv_strength[gi]];
1071
0
            if (curr < best_mse) {
1072
0
                best_gi  = gi;
1073
0
                best_mse = curr;
1074
0
            }
1075
0
        }
1076
0
        propagate_cdef_strength(pcs, sb_index[i], (int8_t)best_gi);
1077
0
    }
1078
1079
    // Map search indices back to actual filter strengths
1080
0
    int filter_map[TOTAL_STRENGTHS] = {0};
1081
0
    for (i = 0; i < first_pass_fs_num; i++) {
1082
0
        filter_map[i] = cdef_search_ctrls->default_first_pass_fs[i];
1083
0
    }
1084
0
    for (i = 0; i < default_second_pass_fs_num; i++) {
1085
0
        filter_map[first_pass_fs_num + i] = cdef_search_ctrls->default_second_pass_fs[i];
1086
0
    }
1087
1088
0
    for (i = 0; i < ppcs->nb_cdef_strengths; i++) {
1089
0
        frm_hdr->cdef_params.cdef_y_strength[i]  = filter_map[frm_hdr->cdef_params.cdef_y_strength[i]];
1090
0
        frm_hdr->cdef_params.cdef_uv_strength[i] = filter_map[frm_hdr->cdef_params.cdef_uv_strength[i]];
1091
0
    }
1092
1093
0
    frm_hdr->cdef_params.cdef_damping = CDEF_DAMPING_FROM_QP(frm_hdr->quantization_params.base_q_idx);
1094
0
}