Coverage Report

Created: 2026-11-12 08:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/aom/av1/common/thread_common.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2016, Alliance for Open Media. All rights reserved.
3
 *
4
 * This source code is subject to the terms of the BSD 2 Clause License and
5
 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6
 * was not distributed with this source code in the LICENSE file, you can
7
 * obtain it at www.aomedia.org/license/software. If the Alliance for Open
8
 * Media Patent License 1.0 was not distributed with this source code in the
9
 * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
10
 */
11
12
#include <assert.h>
13
#include <limits.h>
14
15
#include "aom/aom_image.h"
16
#include "config/aom_config.h"
17
#include "config/aom_scale_rtcd.h"
18
19
#include "aom_dsp/aom_dsp_common.h"
20
#include "aom_dsp/txfm_common.h"
21
#include "aom_mem/aom_mem.h"
22
#include "aom_util/aom_pthread.h"
23
#include "aom_util/aom_thread.h"
24
#include "av1/common/av1_loopfilter.h"
25
#include "av1/common/blockd.h"
26
#include "av1/common/cdef.h"
27
#include "av1/common/entropymode.h"
28
#include "av1/common/enums.h"
29
#include "av1/common/thread_common.h"
30
#include "av1/common/reconinter.h"
31
#include "av1/common/reconintra.h"
32
#include "av1/common/restoration.h"
33
34
// Set up nsync by width.
35
4.27k
static inline int get_sync_range(int width) {
36
  // nsync numbers are picked by testing. For example, for 4k
37
  // video, using 4 gives best performance.
38
4.27k
  if (width < 640)
39
4.04k
    return 1;
40
229
  else if (width <= 1280)
41
34
    return 2;
42
195
  else if (width <= 4096)
43
160
    return 4;
44
35
  else
45
35
    return 8;
46
4.27k
}
47
48
#if !CONFIG_REALTIME_ONLY || CONFIG_AV1_DECODER
49
1.30k
static inline int get_lr_sync_range(int width) {
50
#if 0
51
  // nsync numbers are picked by testing. For example, for 4k
52
  // video, using 4 gives best performance.
53
  if (width < 640)
54
    return 1;
55
  else if (width <= 1280)
56
    return 2;
57
  else if (width <= 4096)
58
    return 4;
59
  else
60
    return 8;
61
#else
62
1.30k
  (void)width;
63
1.30k
  return 1;
64
1.30k
#endif
65
1.30k
}
66
#endif  // !CONFIG_REALTIME_ONLY || CONFIG_AV1_DECODER
67
68
// Allocate memory for lf row synchronization
69
void av1_loop_filter_alloc(AV1LfSync *lf_sync, AV1_COMMON *cm, int rows,
70
4.27k
                           int width, int num_workers) {
71
4.27k
  lf_sync->rows = rows;
72
4.27k
#if CONFIG_MULTITHREAD
73
4.27k
  {
74
4.27k
    int i, j;
75
76
17.0k
    for (j = 0; j < MAX_MB_PLANE; j++) {
77
12.8k
      CHECK_MEM_ERROR(cm, lf_sync->mutex_[j],
78
12.8k
                      aom_malloc(sizeof(*(lf_sync->mutex_[j])) * rows));
79
12.8k
      if (lf_sync->mutex_[j]) {
80
54.9k
        for (i = 0; i < rows; ++i) {
81
42.1k
          pthread_mutex_init(&lf_sync->mutex_[j][i], NULL);
82
42.1k
        }
83
12.8k
      }
84
85
12.8k
      CHECK_MEM_ERROR(cm, lf_sync->cond_[j],
86
12.8k
                      aom_malloc(sizeof(*(lf_sync->cond_[j])) * rows));
87
12.8k
      if (lf_sync->cond_[j]) {
88
54.9k
        for (i = 0; i < rows; ++i) {
89
42.1k
          pthread_cond_init(&lf_sync->cond_[j][i], NULL);
90
42.1k
        }
91
12.8k
      }
92
12.8k
    }
93
94
4.27k
    CHECK_MEM_ERROR(cm, lf_sync->job_mutex,
95
4.27k
                    aom_malloc(sizeof(*(lf_sync->job_mutex))));
96
4.27k
    if (lf_sync->job_mutex) {
97
4.27k
      pthread_mutex_init(lf_sync->job_mutex, NULL);
98
4.27k
    }
99
4.27k
  }
100
0
#endif  // CONFIG_MULTITHREAD
101
4.27k
  CHECK_MEM_ERROR(cm, lf_sync->lfdata,
102
4.27k
                  aom_malloc(num_workers * sizeof(*(lf_sync->lfdata))));
103
4.27k
  lf_sync->num_workers = num_workers;
104
105
17.0k
  for (int j = 0; j < MAX_MB_PLANE; j++) {
106
12.8k
    CHECK_MEM_ERROR(cm, lf_sync->cur_sb_col[j],
107
12.8k
                    aom_malloc(sizeof(*(lf_sync->cur_sb_col[j])) * rows));
108
12.8k
  }
109
4.27k
  CHECK_MEM_ERROR(
110
4.27k
      cm, lf_sync->job_queue,
111
4.27k
      aom_malloc(sizeof(*(lf_sync->job_queue)) * rows * MAX_MB_PLANE * 2));
112
  // Set up nsync.
113
4.27k
  lf_sync->sync_range = get_sync_range(width);
114
4.27k
}
115
116
// Deallocate lf synchronization related mutex and data
117
7.95k
void av1_loop_filter_dealloc(AV1LfSync *lf_sync) {
118
7.95k
  if (lf_sync != NULL) {
119
7.95k
    int j;
120
7.95k
#if CONFIG_MULTITHREAD
121
7.95k
    int i;
122
31.8k
    for (j = 0; j < MAX_MB_PLANE; j++) {
123
23.8k
      if (lf_sync->mutex_[j] != NULL) {
124
54.9k
        for (i = 0; i < lf_sync->rows; ++i) {
125
42.1k
          pthread_mutex_destroy(&lf_sync->mutex_[j][i]);
126
42.1k
        }
127
12.8k
        aom_free(lf_sync->mutex_[j]);
128
12.8k
      }
129
23.8k
      if (lf_sync->cond_[j] != NULL) {
130
54.9k
        for (i = 0; i < lf_sync->rows; ++i) {
131
42.1k
          pthread_cond_destroy(&lf_sync->cond_[j][i]);
132
42.1k
        }
133
12.8k
        aom_free(lf_sync->cond_[j]);
134
12.8k
      }
135
23.8k
    }
136
7.95k
    if (lf_sync->job_mutex != NULL) {
137
4.27k
      pthread_mutex_destroy(lf_sync->job_mutex);
138
4.27k
      aom_free(lf_sync->job_mutex);
139
4.27k
    }
140
7.95k
#endif  // CONFIG_MULTITHREAD
141
7.95k
    aom_free(lf_sync->lfdata);
142
31.8k
    for (j = 0; j < MAX_MB_PLANE; j++) {
143
23.8k
      aom_free(lf_sync->cur_sb_col[j]);
144
23.8k
    }
145
146
7.95k
    aom_free(lf_sync->job_queue);
147
    // clear the structure as the source of this call may be a resize in which
148
    // case this call will be followed by an _alloc() which may fail.
149
7.95k
    av1_zero(*lf_sync);
150
7.95k
  }
151
7.95k
}
152
153
void av1_alloc_cdef_sync(AV1_COMMON *const cm, AV1CdefSync *cdef_sync,
154
112k
                         int num_workers) {
155
112k
  if (num_workers < 1) return;
156
66.7k
#if CONFIG_MULTITHREAD
157
66.7k
  if (cdef_sync->mutex_ == NULL) {
158
2.86k
    CHECK_MEM_ERROR(cm, cdef_sync->mutex_,
159
2.86k
                    aom_malloc(sizeof(*(cdef_sync->mutex_))));
160
2.86k
    if (cdef_sync->mutex_) pthread_mutex_init(cdef_sync->mutex_, NULL);
161
2.86k
  }
162
#else
163
  (void)cm;
164
  (void)cdef_sync;
165
#endif  // CONFIG_MULTITHREAD
166
66.7k
}
167
168
17.0k
void av1_free_cdef_sync(AV1CdefSync *cdef_sync) {
169
17.0k
  if (cdef_sync == NULL) return;
170
17.0k
#if CONFIG_MULTITHREAD
171
17.0k
  if (cdef_sync->mutex_ != NULL) {
172
2.86k
    pthread_mutex_destroy(cdef_sync->mutex_);
173
2.86k
    aom_free(cdef_sync->mutex_);
174
2.86k
  }
175
17.0k
#endif  // CONFIG_MULTITHREAD
176
17.0k
}
177
178
static inline void cdef_row_mt_sync_read(AV1CdefSync *const cdef_sync,
179
69.2k
                                         int row) {
180
69.2k
  if (!row) return;
181
52.2k
#if CONFIG_MULTITHREAD
182
52.2k
  AV1CdefRowSync *const cdef_row_mt = cdef_sync->cdef_row_mt;
183
52.2k
  pthread_mutex_lock(cdef_row_mt[row - 1].row_mutex_);
184
59.5k
  while (cdef_row_mt[row - 1].is_row_done != 1)
185
7.26k
    pthread_cond_wait(cdef_row_mt[row - 1].row_cond_,
186
7.26k
                      cdef_row_mt[row - 1].row_mutex_);
187
52.2k
  cdef_row_mt[row - 1].is_row_done = 0;
188
52.2k
  pthread_mutex_unlock(cdef_row_mt[row - 1].row_mutex_);
189
#else
190
  (void)cdef_sync;
191
#endif  // CONFIG_MULTITHREAD
192
52.2k
}
193
194
static inline void cdef_row_mt_sync_write(AV1CdefSync *const cdef_sync,
195
67.7k
                                          int row) {
196
67.7k
#if CONFIG_MULTITHREAD
197
67.7k
  AV1CdefRowSync *const cdef_row_mt = cdef_sync->cdef_row_mt;
198
67.7k
  pthread_mutex_lock(cdef_row_mt[row].row_mutex_);
199
67.7k
  pthread_cond_signal(cdef_row_mt[row].row_cond_);
200
67.7k
  cdef_row_mt[row].is_row_done = 1;
201
67.7k
  pthread_mutex_unlock(cdef_row_mt[row].row_mutex_);
202
#else
203
  (void)cdef_sync;
204
  (void)row;
205
#endif  // CONFIG_MULTITHREAD
206
67.7k
}
207
208
// Value of cur_sb_col[plane][r] after a worker has encountered an error. It
209
// wakes up the workers waiting in sync_read() and tells them to exit.
210
559k
#define LF_SYNC_EXIT INT_MAX
211
212
// Returns true if the sync read is successful and the caller can continue.
213
// Returns false if the caller should exit because a worker has encountered an
214
// error.
215
static inline bool sync_read(AV1LfSync *const lf_sync, int r, int c,
216
1.04M
                             int plane) {
217
1.04M
  bool lf_mt_continue = true;
218
1.04M
#if CONFIG_MULTITHREAD
219
1.04M
  const int nsync = lf_sync->sync_range;
220
221
1.04M
  if (r && !(c & (nsync - 1))) {
222
559k
    pthread_mutex_t *const mutex = &lf_sync->mutex_[plane][r - 1];
223
559k
    pthread_mutex_lock(mutex);
224
225
672k
    while (c > lf_sync->cur_sb_col[plane][r - 1] - nsync) {
226
113k
      pthread_cond_wait(&lf_sync->cond_[plane][r - 1], mutex);
227
113k
    }
228
559k
    lf_mt_continue = lf_sync->cur_sb_col[plane][r - 1] != LF_SYNC_EXIT;
229
559k
    pthread_mutex_unlock(mutex);
230
559k
  }
231
#else
232
  (void)lf_sync;
233
  (void)r;
234
  (void)c;
235
  (void)plane;
236
#endif  // CONFIG_MULTITHREAD
237
1.04M
  return lf_mt_continue;
238
1.04M
}
239
240
static inline void sync_write(AV1LfSync *const lf_sync, int r, int c,
241
515k
                              const int sb_cols, int plane) {
242
515k
#if CONFIG_MULTITHREAD
243
515k
  const int nsync = lf_sync->sync_range;
244
515k
  int cur;
245
  // Only signal when there are enough filtered SB for next row to run.
246
515k
  int sig = 1;
247
248
515k
  if (c < sb_cols - 1) {
249
381k
    cur = c;
250
381k
    if (c % nsync) sig = 0;
251
381k
  } else {
252
133k
    cur = sb_cols + nsync;
253
133k
  }
254
255
515k
  if (sig) {
256
330k
    pthread_mutex_lock(&lf_sync->mutex_[plane][r]);
257
258
    // After an error cur_sb_col[plane][r] holds LF_SYNC_EXIT. AOMMAX keeps it
259
    // from being lowered, so workers waiting in sync_read() still see it and
260
    // exit.
261
330k
    lf_sync->cur_sb_col[plane][r] = AOMMAX(lf_sync->cur_sb_col[plane][r], cur);
262
263
330k
    pthread_cond_broadcast(&lf_sync->cond_[plane][r]);
264
330k
    pthread_mutex_unlock(&lf_sync->mutex_[plane][r]);
265
330k
  }
266
#else
267
  (void)lf_sync;
268
  (void)r;
269
  (void)c;
270
  (void)sb_cols;
271
  (void)plane;
272
#endif  // CONFIG_MULTITHREAD
273
515k
}
274
275
// One job of row loopfiltering.
276
void av1_thread_loop_filter_rows(
277
    const YV12_BUFFER_CONFIG *const frame_buffer, AV1_COMMON *const cm,
278
    struct macroblockd_plane *planes, MACROBLOCKD *xd, int mi_row, int plane,
279
    int dir, int lpf_opt_level, AV1LfSync *const lf_sync,
280
    struct aom_internal_error_info *error_info,
281
    AV1_DEBLOCKING_PARAMETERS *params_buf, TX_SIZE *tx_buf,
282
372k
    int num_mis_in_lpf_unit_height_log2) {
283
  // TODO(aomedia:3276): Pass error_info to the low-level functions as required
284
  // in future to handle error propagation.
285
372k
  (void)error_info;
286
372k
  const int sb_cols =
287
372k
      CEIL_POWER_OF_TWO(cm->mi_params.mi_cols, MAX_MIB_SIZE_LOG2);
288
372k
  const int r = mi_row >> num_mis_in_lpf_unit_height_log2;
289
372k
  int mi_col, c;
290
291
372k
  const bool joint_filter_chroma = (lpf_opt_level == 2) && plane > AOM_PLANE_Y;
292
372k
  const int num_planes = joint_filter_chroma ? 2 : 1;
293
372k
  assert(IMPLIES(joint_filter_chroma, plane == AOM_PLANE_U));
294
295
372k
  if (dir == 0) {
296
798k
    for (mi_col = 0; mi_col < cm->mi_params.mi_cols; mi_col += MAX_MIB_SIZE) {
297
612k
      c = mi_col >> MAX_MIB_SIZE_LOG2;
298
299
612k
      av1_setup_dst_planes(planes, cm->seq_params->sb_size, frame_buffer,
300
612k
                           mi_row, mi_col, plane, plane + num_planes);
301
612k
      if (lpf_opt_level) {
302
0
        if (plane == AOM_PLANE_Y) {
303
0
          av1_filter_block_plane_vert_opt(cm, xd, &planes[plane], mi_row,
304
0
                                          mi_col, params_buf, tx_buf,
305
0
                                          num_mis_in_lpf_unit_height_log2);
306
0
        } else {
307
0
          av1_filter_block_plane_vert_opt_chroma(
308
0
              cm, xd, &planes[plane], mi_row, mi_col, params_buf, tx_buf, plane,
309
0
              joint_filter_chroma, num_mis_in_lpf_unit_height_log2);
310
0
        }
311
612k
      } else {
312
612k
        av1_filter_block_plane_vert(cm, xd, plane, &planes[plane], mi_row,
313
612k
                                    mi_col);
314
612k
      }
315
612k
      if (lf_sync != NULL) {
316
515k
        sync_write(lf_sync, r, c, sb_cols, plane);
317
515k
      }
318
612k
    }
319
186k
  } else if (dir == 1) {
320
799k
    for (mi_col = 0; mi_col < cm->mi_params.mi_cols; mi_col += MAX_MIB_SIZE) {
321
613k
      c = mi_col >> MAX_MIB_SIZE_LOG2;
322
323
613k
      if (lf_sync != NULL) {
324
        // Wait for vertical edge filtering of the top-right block to be
325
        // completed. Exit in case any worker has encountered an error.
326
523k
        if (!sync_read(lf_sync, r, c, plane)) return;
327
328
        // Wait for vertical edge filtering of the right block to be completed
329
523k
        if (!sync_read(lf_sync, r + 1, c, plane)) return;
330
523k
      }
331
332
613k
      av1_setup_dst_planes(planes, cm->seq_params->sb_size, frame_buffer,
333
613k
                           mi_row, mi_col, plane, plane + num_planes);
334
613k
      if (lpf_opt_level) {
335
0
        if (plane == AOM_PLANE_Y) {
336
0
          av1_filter_block_plane_horz_opt(cm, xd, &planes[plane], mi_row,
337
0
                                          mi_col, params_buf, tx_buf,
338
0
                                          num_mis_in_lpf_unit_height_log2);
339
0
        } else {
340
0
          av1_filter_block_plane_horz_opt_chroma(
341
0
              cm, xd, &planes[plane], mi_row, mi_col, params_buf, tx_buf, plane,
342
0
              joint_filter_chroma, num_mis_in_lpf_unit_height_log2);
343
0
        }
344
613k
      } else {
345
613k
        av1_filter_block_plane_horz(cm, xd, plane, &planes[plane], mi_row,
346
613k
                                    mi_col);
347
613k
      }
348
613k
    }
349
186k
  }
350
372k
}
351
352
void av1_set_vert_loop_filter_done(AV1_COMMON *cm, AV1LfSync *lf_sync,
353
0
                                   int num_mis_in_lpf_unit_height_log2) {
354
0
#if CONFIG_MULTITHREAD
355
0
  const int sb_rows =
356
0
      CEIL_POWER_OF_TWO(cm->mi_params.mi_rows, num_mis_in_lpf_unit_height_log2);
357
358
  // In case of loopfilter row-multithreading, the worker on an SB row waits for
359
  // the vertical edge filtering of the right and top-right SBs. Hence, in case
360
  // a thread (main/worker) encounters an error, mark every SB row in the frame
361
  // with LF_SYNC_EXIT. This avoids dependent workers waiting indefinitely and
362
  // makes them exit instead of filtering.
363
0
  for (int sb_row = 0; sb_row < sb_rows; ++sb_row) {
364
0
    for (int plane = 0; plane < MAX_MB_PLANE; ++plane) {
365
0
      pthread_mutex_lock(&lf_sync->mutex_[plane][sb_row]);
366
0
      lf_sync->cur_sb_col[plane][sb_row] = LF_SYNC_EXIT;
367
0
      pthread_cond_broadcast(&lf_sync->cond_[plane][sb_row]);
368
0
      pthread_mutex_unlock(&lf_sync->mutex_[plane][sb_row]);
369
0
    }
370
0
  }
371
#else
372
  (void)cm;
373
  (void)lf_sync;
374
  (void)num_mis_in_lpf_unit_height_log2;
375
#endif  // CONFIG_MULTITHREAD
376
0
}
377
378
static inline void sync_lf_workers(AVxWorker *const workers,
379
19.9k
                                   AV1_COMMON *const cm, int num_workers) {
380
19.9k
  const AVxWorkerInterface *const winterface = aom_get_worker_interface();
381
19.9k
  int had_error = workers[0].had_error;
382
19.9k
  struct aom_internal_error_info error_info;
383
384
  // Read the error_info of main thread.
385
19.9k
  if (had_error) {
386
0
    AVxWorker *const worker = &workers[0];
387
0
    error_info = ((LFWorkerData *)worker->data2)->error_info;
388
0
  }
389
390
  // Wait till all rows are finished.
391
776k
  for (int i = num_workers - 1; i > 0; --i) {
392
756k
    AVxWorker *const worker = &workers[i];
393
756k
    if (!winterface->sync(worker)) {
394
0
      had_error = 1;
395
0
      error_info = ((LFWorkerData *)worker->data2)->error_info;
396
0
    }
397
756k
  }
398
19.9k
  if (had_error) aom_internal_error_copy(cm->error, &error_info);
399
19.9k
}
400
401
// Row-based multi-threaded loopfilter hook
402
767k
static int loop_filter_row_worker(void *arg1, void *arg2) {
403
767k
  AV1LfSync *const lf_sync = (AV1LfSync *)arg1;
404
767k
  LFWorkerData *const lf_data = (LFWorkerData *)arg2;
405
767k
  AV1LfMTInfo *cur_job_info;
406
407
767k
#if CONFIG_MULTITHREAD
408
767k
  pthread_mutex_t *job_mutex_ = lf_sync->job_mutex;
409
767k
#endif
410
411
767k
  struct aom_internal_error_info *const error_info = &lf_data->error_info;
412
413
  // The jmp_buf is valid only for the duration of the function that calls
414
  // setjmp(). Therefore, this function must reset the 'setjmp' field to 0
415
  // before it returns.
416
767k
  if (setjmp(error_info->jmp)) {
417
0
    error_info->setjmp = 0;
418
0
#if CONFIG_MULTITHREAD
419
0
    pthread_mutex_lock(job_mutex_);
420
0
    lf_sync->lf_mt_exit = true;
421
0
    pthread_mutex_unlock(job_mutex_);
422
0
#endif
423
0
    av1_set_vert_loop_filter_done(lf_data->cm, lf_sync, MAX_MIB_SIZE_LOG2);
424
0
    return 0;
425
0
  }
426
767k
  error_info->setjmp = 1;
427
428
1.04M
  while ((cur_job_info = get_lf_job_info(lf_sync)) != NULL) {
429
272k
    const int lpf_opt_level = cur_job_info->lpf_opt_level;
430
272k
    av1_thread_loop_filter_rows(
431
272k
        lf_data->frame_buffer, lf_data->cm, lf_data->planes, lf_data->xd,
432
272k
        cur_job_info->mi_row, cur_job_info->plane, cur_job_info->dir,
433
272k
        lpf_opt_level, lf_sync, error_info, lf_data->params_buf,
434
272k
        lf_data->tx_buf, MAX_MIB_SIZE_LOG2);
435
272k
  }
436
767k
  error_info->setjmp = 0;
437
767k
  return 1;
438
767k
}
439
440
static void loop_filter_rows_mt(YV12_BUFFER_CONFIG *frame, AV1_COMMON *cm,
441
                                MACROBLOCKD *xd, int start, int stop,
442
                                const int planes_to_lf[MAX_MB_PLANE],
443
                                AVxWorker *workers, int num_workers,
444
19.9k
                                AV1LfSync *lf_sync, int lpf_opt_level) {
445
19.9k
  const AVxWorkerInterface *const winterface = aom_get_worker_interface();
446
19.9k
  int i;
447
19.9k
  loop_filter_frame_mt_init(cm, start, stop, planes_to_lf, num_workers, lf_sync,
448
19.9k
                            lpf_opt_level, MAX_MIB_SIZE_LOG2);
449
450
  // Set up loopfilter thread data.
451
796k
  for (i = num_workers - 1; i >= 0; --i) {
452
776k
    AVxWorker *const worker = &workers[i];
453
776k
    LFWorkerData *const lf_data = &lf_sync->lfdata[i];
454
455
776k
    worker->hook = loop_filter_row_worker;
456
776k
    worker->data1 = lf_sync;
457
776k
    worker->data2 = lf_data;
458
459
    // Loopfilter data
460
776k
    loop_filter_data_reset(lf_data, frame, cm, xd);
461
462
    // Start loopfiltering
463
776k
    worker->had_error = 0;
464
776k
    if (i == 0) {
465
19.9k
      winterface->execute(worker);
466
756k
    } else {
467
756k
      winterface->launch(worker);
468
756k
    }
469
776k
  }
470
471
19.9k
  sync_lf_workers(workers, cm, num_workers);
472
19.9k
}
473
474
static void loop_filter_rows(YV12_BUFFER_CONFIG *frame, AV1_COMMON *cm,
475
                             MACROBLOCKD *xd, int start, int stop,
476
                             const int planes_to_lf[MAX_MB_PLANE],
477
14.7k
                             int lpf_opt_level) {
478
  // Filter top rows of all planes first, in case the output can be partially
479
  // reconstructed row by row.
480
14.7k
  int mi_row, plane, dir;
481
482
14.7k
  AV1_DEBLOCKING_PARAMETERS params_buf[MAX_MIB_SIZE];
483
14.7k
  TX_SIZE tx_buf[MAX_MIB_SIZE];
484
35.7k
  for (mi_row = start; mi_row < stop; mi_row += MAX_MIB_SIZE) {
485
84.1k
    for (plane = 0; plane < MAX_MB_PLANE; ++plane) {
486
63.0k
      if (skip_loop_filter_plane(planes_to_lf, plane, lpf_opt_level)) {
487
13.2k
        continue;
488
13.2k
      }
489
490
149k
      for (dir = 0; dir < 2; ++dir) {
491
99.5k
        av1_thread_loop_filter_rows(frame, cm, xd->plane, xd, mi_row, plane,
492
99.5k
                                    dir, lpf_opt_level, /*lf_sync=*/NULL,
493
99.5k
                                    xd->error_info, params_buf, tx_buf,
494
99.5k
                                    MAX_MIB_SIZE_LOG2);
495
99.5k
      }
496
49.7k
    }
497
21.0k
  }
498
14.7k
}
499
500
void av1_loop_filter_frame_mt(YV12_BUFFER_CONFIG *frame, AV1_COMMON *cm,
501
                              MACROBLOCKD *xd, int plane_start, int plane_end,
502
                              int partial_frame, AVxWorker *workers,
503
                              int num_workers, AV1LfSync *lf_sync,
504
34.6k
                              int lpf_opt_level) {
505
34.6k
  int start_mi_row, end_mi_row, mi_rows_to_filter;
506
34.6k
  int planes_to_lf[MAX_MB_PLANE];
507
508
34.6k
  if (!check_planes_to_loop_filter(&cm->lf, planes_to_lf, plane_start,
509
34.6k
                                   plane_end))
510
0
    return;
511
512
34.6k
  start_mi_row = 0;
513
34.6k
  mi_rows_to_filter = cm->mi_params.mi_rows;
514
34.6k
  if (partial_frame && cm->mi_params.mi_rows > 8) {
515
0
    start_mi_row = cm->mi_params.mi_rows >> 1;
516
0
    start_mi_row &= 0xfffffff8;
517
0
    mi_rows_to_filter = AOMMAX(cm->mi_params.mi_rows / 8, 8);
518
0
  }
519
34.6k
  end_mi_row = start_mi_row + mi_rows_to_filter;
520
34.6k
  av1_loop_filter_frame_init(cm, plane_start, plane_end);
521
522
34.6k
  if (num_workers > 1) {
523
    // Enqueue and execute loopfiltering jobs.
524
19.9k
    loop_filter_rows_mt(frame, cm, xd, start_mi_row, end_mi_row, planes_to_lf,
525
19.9k
                        workers, num_workers, lf_sync, lpf_opt_level);
526
19.9k
  } else {
527
    // Directly filter in the main thread.
528
14.7k
    loop_filter_rows(frame, cm, xd, start_mi_row, end_mi_row, planes_to_lf,
529
14.7k
                     lpf_opt_level);
530
14.7k
  }
531
34.6k
}
532
533
#if !CONFIG_REALTIME_ONLY || CONFIG_AV1_DECODER
534
39.0k
static inline void lr_sync_read(void *const lr_sync, int r, int c, int plane) {
535
39.0k
#if CONFIG_MULTITHREAD
536
39.0k
  AV1LrSync *const loop_res_sync = (AV1LrSync *)lr_sync;
537
39.0k
  const int nsync = loop_res_sync->sync_range;
538
539
39.0k
  if (r && !(c & (nsync - 1))) {
540
39.0k
    pthread_mutex_t *const mutex = &loop_res_sync->mutex_[plane][r - 1];
541
39.0k
    pthread_mutex_lock(mutex);
542
543
53.4k
    while (c > loop_res_sync->cur_sb_col[plane][r - 1] - nsync) {
544
14.4k
      pthread_cond_wait(&loop_res_sync->cond_[plane][r - 1], mutex);
545
14.4k
    }
546
39.0k
    pthread_mutex_unlock(mutex);
547
39.0k
  }
548
#else
549
  (void)lr_sync;
550
  (void)r;
551
  (void)c;
552
  (void)plane;
553
#endif  // CONFIG_MULTITHREAD
554
39.0k
}
555
556
static inline void lr_sync_write(void *const lr_sync, int r, int c,
557
54.8k
                                 const int sb_cols, int plane) {
558
54.8k
#if CONFIG_MULTITHREAD
559
54.8k
  AV1LrSync *const loop_res_sync = (AV1LrSync *)lr_sync;
560
54.8k
  const int nsync = loop_res_sync->sync_range;
561
54.8k
  int cur;
562
  // Only signal when there are enough filtered SB for next row to run.
563
54.8k
  int sig = 1;
564
565
54.8k
  if (c < sb_cols - 1) {
566
25.2k
    cur = c;
567
25.2k
    if (c % nsync) sig = 0;
568
29.5k
  } else {
569
29.5k
    cur = sb_cols + nsync;
570
29.5k
  }
571
572
54.8k
  if (sig) {
573
54.8k
    pthread_mutex_lock(&loop_res_sync->mutex_[plane][r]);
574
575
    // When a thread encounters an error, cur_sb_col[plane][r] is set to maximum
576
    // column number. In this case, the AOMMAX operation here ensures that
577
    // cur_sb_col[plane][r] is not overwritten with a smaller value thus
578
    // preventing the infinite waiting of threads in the relevant sync_read()
579
    // function.
580
54.8k
    loop_res_sync->cur_sb_col[plane][r] =
581
54.8k
        AOMMAX(loop_res_sync->cur_sb_col[plane][r], cur);
582
583
54.8k
    pthread_cond_broadcast(&loop_res_sync->cond_[plane][r]);
584
54.8k
    pthread_mutex_unlock(&loop_res_sync->mutex_[plane][r]);
585
54.8k
  }
586
#else
587
  (void)lr_sync;
588
  (void)r;
589
  (void)c;
590
  (void)sb_cols;
591
  (void)plane;
592
#endif  // CONFIG_MULTITHREAD
593
54.8k
}
594
595
// Allocate memory for loop restoration row synchronization
596
void av1_loop_restoration_alloc(AV1LrSync *lr_sync, AV1_COMMON *cm,
597
                                int num_workers, int num_rows_lr,
598
1.30k
                                int num_planes, int width) {
599
1.30k
  lr_sync->rows = num_rows_lr;
600
1.30k
  lr_sync->num_planes = num_planes;
601
1.30k
#if CONFIG_MULTITHREAD
602
1.30k
  {
603
1.30k
    int i, j;
604
605
5.07k
    for (j = 0; j < num_planes; j++) {
606
3.76k
      CHECK_MEM_ERROR(cm, lr_sync->mutex_[j],
607
3.76k
                      aom_malloc(sizeof(*(lr_sync->mutex_[j])) * num_rows_lr));
608
3.76k
      if (lr_sync->mutex_[j]) {
609
15.3k
        for (i = 0; i < num_rows_lr; ++i) {
610
11.5k
          pthread_mutex_init(&lr_sync->mutex_[j][i], NULL);
611
11.5k
        }
612
3.76k
      }
613
614
3.76k
      CHECK_MEM_ERROR(cm, lr_sync->cond_[j],
615
3.76k
                      aom_malloc(sizeof(*(lr_sync->cond_[j])) * num_rows_lr));
616
3.76k
      if (lr_sync->cond_[j]) {
617
15.3k
        for (i = 0; i < num_rows_lr; ++i) {
618
11.5k
          pthread_cond_init(&lr_sync->cond_[j][i], NULL);
619
11.5k
        }
620
3.76k
      }
621
3.76k
    }
622
623
1.30k
    CHECK_MEM_ERROR(cm, lr_sync->job_mutex,
624
1.30k
                    aom_malloc(sizeof(*(lr_sync->job_mutex))));
625
1.30k
    if (lr_sync->job_mutex) {
626
1.30k
      pthread_mutex_init(lr_sync->job_mutex, NULL);
627
1.30k
    }
628
1.30k
  }
629
0
#endif  // CONFIG_MULTITHREAD
630
1.30k
  CHECK_MEM_ERROR(cm, lr_sync->lrworkerdata,
631
1.30k
                  aom_calloc(num_workers, sizeof(*(lr_sync->lrworkerdata))));
632
1.30k
  lr_sync->num_workers = num_workers;
633
634
40.5k
  for (int worker_idx = 0; worker_idx < num_workers; ++worker_idx) {
635
39.2k
    if (worker_idx < num_workers - 1) {
636
37.9k
      CHECK_MEM_ERROR(cm, lr_sync->lrworkerdata[worker_idx].rst_tmpbuf,
637
37.9k
                      (int32_t *)aom_memalign(16, RESTORATION_TMPBUF_SIZE));
638
37.9k
      CHECK_MEM_ERROR(cm, lr_sync->lrworkerdata[worker_idx].rlbs,
639
37.9k
                      aom_malloc(sizeof(RestorationLineBuffers)));
640
641
37.9k
    } else {
642
1.30k
      lr_sync->lrworkerdata[worker_idx].rst_tmpbuf = cm->rst_tmpbuf;
643
1.30k
      lr_sync->lrworkerdata[worker_idx].rlbs = cm->rlbs;
644
1.30k
    }
645
39.2k
  }
646
647
5.07k
  for (int j = 0; j < num_planes; j++) {
648
3.76k
    CHECK_MEM_ERROR(
649
3.76k
        cm, lr_sync->cur_sb_col[j],
650
3.76k
        aom_malloc(sizeof(*(lr_sync->cur_sb_col[j])) * num_rows_lr));
651
3.76k
  }
652
1.30k
  CHECK_MEM_ERROR(
653
1.30k
      cm, lr_sync->job_queue,
654
1.30k
      aom_malloc(sizeof(*(lr_sync->job_queue)) * num_rows_lr * num_planes));
655
  // Set up nsync.
656
1.30k
  lr_sync->sync_range = get_lr_sync_range(width);
657
1.30k
}
658
659
// Deallocate loop restoration synchronization related mutex and data
660
4.99k
void av1_loop_restoration_dealloc(AV1LrSync *lr_sync) {
661
4.99k
  if (lr_sync != NULL) {
662
4.99k
    int j;
663
4.99k
#if CONFIG_MULTITHREAD
664
4.99k
    int i;
665
19.9k
    for (j = 0; j < MAX_MB_PLANE; j++) {
666
14.9k
      if (lr_sync->mutex_[j] != NULL) {
667
15.3k
        for (i = 0; i < lr_sync->rows; ++i) {
668
11.5k
          pthread_mutex_destroy(&lr_sync->mutex_[j][i]);
669
11.5k
        }
670
3.76k
        aom_free(lr_sync->mutex_[j]);
671
3.76k
      }
672
14.9k
      if (lr_sync->cond_[j] != NULL) {
673
15.3k
        for (i = 0; i < lr_sync->rows; ++i) {
674
11.5k
          pthread_cond_destroy(&lr_sync->cond_[j][i]);
675
11.5k
        }
676
3.76k
        aom_free(lr_sync->cond_[j]);
677
3.76k
      }
678
14.9k
    }
679
4.99k
    if (lr_sync->job_mutex != NULL) {
680
1.30k
      pthread_mutex_destroy(lr_sync->job_mutex);
681
1.30k
      aom_free(lr_sync->job_mutex);
682
1.30k
    }
683
4.99k
#endif  // CONFIG_MULTITHREAD
684
19.9k
    for (j = 0; j < MAX_MB_PLANE; j++) {
685
14.9k
      aom_free(lr_sync->cur_sb_col[j]);
686
14.9k
    }
687
688
4.99k
    aom_free(lr_sync->job_queue);
689
690
4.99k
    if (lr_sync->lrworkerdata) {
691
39.2k
      for (int worker_idx = 0; worker_idx < lr_sync->num_workers - 1;
692
37.9k
           worker_idx++) {
693
37.9k
        LRWorkerData *const workerdata_data =
694
37.9k
            lr_sync->lrworkerdata + worker_idx;
695
696
37.9k
        aom_free(workerdata_data->rst_tmpbuf);
697
37.9k
        aom_free(workerdata_data->rlbs);
698
37.9k
      }
699
1.30k
      aom_free(lr_sync->lrworkerdata);
700
1.30k
    }
701
702
    // clear the structure as the source of this call may be a resize in which
703
    // case this call will be followed by an _alloc() which may fail.
704
4.99k
    av1_zero(*lr_sync);
705
4.99k
  }
706
4.99k
}
707
708
static void enqueue_lr_jobs(AV1LrSync *lr_sync, AV1LrStruct *lr_ctxt,
709
12.8k
                            AV1_COMMON *cm) {
710
12.8k
  FilterFrameCtxt *ctxt = lr_ctxt->ctxt;
711
712
12.8k
  const int num_planes = av1_num_planes(cm);
713
12.8k
  AV1LrMTInfo *lr_job_queue = lr_sync->job_queue;
714
12.8k
  int32_t lr_job_counter[2], num_even_lr_jobs = 0;
715
12.8k
  lr_sync->jobs_enqueued = 0;
716
12.8k
  lr_sync->jobs_dequeued = 0;
717
718
49.7k
  for (int plane = 0; plane < num_planes; plane++) {
719
36.9k
    if (cm->rst_info[plane].frame_restoration_type == RESTORE_NONE) continue;
720
27.9k
    num_even_lr_jobs =
721
27.9k
        num_even_lr_jobs + ((ctxt[plane].rsi->vert_units + 1) >> 1);
722
27.9k
  }
723
12.8k
  lr_job_counter[0] = 0;
724
12.8k
  lr_job_counter[1] = num_even_lr_jobs;
725
726
49.7k
  for (int plane = 0; plane < num_planes; plane++) {
727
36.9k
    if (cm->rst_info[plane].frame_restoration_type == RESTORE_NONE) continue;
728
27.9k
    const int is_uv = plane > 0;
729
27.9k
    const int ss_y = is_uv && cm->seq_params->subsampling_y;
730
27.9k
    const int unit_size = ctxt[plane].rsi->restoration_unit_size;
731
27.9k
    const int plane_h = ctxt[plane].plane_h;
732
27.9k
    const int ext_size = unit_size * 3 / 2;
733
734
27.9k
    int y0 = 0, i = 0;
735
69.5k
    while (y0 < plane_h) {
736
41.5k
      int remaining_h = plane_h - y0;
737
41.5k
      int h = (remaining_h < ext_size) ? remaining_h : unit_size;
738
739
41.5k
      RestorationTileLimits limits;
740
41.5k
      limits.v_start = y0;
741
41.5k
      limits.v_end = y0 + h;
742
41.5k
      assert(limits.v_end <= plane_h);
743
      // Offset upwards to align with the restoration processing stripe
744
41.5k
      const int voffset = RESTORATION_UNIT_OFFSET >> ss_y;
745
41.5k
      limits.v_start = AOMMAX(0, limits.v_start - voffset);
746
41.5k
      if (limits.v_end < plane_h) limits.v_end -= voffset;
747
748
41.5k
      assert(lr_job_counter[0] <= num_even_lr_jobs);
749
750
41.5k
      lr_job_queue[lr_job_counter[i & 1]].lr_unit_row = i;
751
41.5k
      lr_job_queue[lr_job_counter[i & 1]].plane = plane;
752
41.5k
      lr_job_queue[lr_job_counter[i & 1]].v_start = limits.v_start;
753
41.5k
      lr_job_queue[lr_job_counter[i & 1]].v_end = limits.v_end;
754
41.5k
      lr_job_queue[lr_job_counter[i & 1]].sync_mode = i & 1;
755
41.5k
      if ((i & 1) == 0) {
756
29.7k
        lr_job_queue[lr_job_counter[i & 1]].v_copy_start =
757
29.7k
            limits.v_start + RESTORATION_BORDER;
758
29.7k
        lr_job_queue[lr_job_counter[i & 1]].v_copy_end =
759
29.7k
            limits.v_end - RESTORATION_BORDER;
760
29.7k
        if (i == 0) {
761
27.9k
          assert(limits.v_start == 0);
762
27.9k
          lr_job_queue[lr_job_counter[i & 1]].v_copy_start = 0;
763
27.9k
        }
764
29.7k
        if (i == (ctxt[plane].rsi->vert_units - 1)) {
765
17.9k
          assert(limits.v_end == plane_h);
766
17.9k
          lr_job_queue[lr_job_counter[i & 1]].v_copy_end = plane_h;
767
17.9k
        }
768
29.7k
      } else {
769
11.8k
        lr_job_queue[lr_job_counter[i & 1]].v_copy_start =
770
11.8k
            AOMMAX(limits.v_start - RESTORATION_BORDER, 0);
771
11.8k
        lr_job_queue[lr_job_counter[i & 1]].v_copy_end =
772
11.8k
            AOMMIN(limits.v_end + RESTORATION_BORDER, plane_h);
773
11.8k
      }
774
41.5k
      lr_job_counter[i & 1]++;
775
41.5k
      lr_sync->jobs_enqueued++;
776
777
41.5k
      y0 += h;
778
41.5k
      ++i;
779
41.5k
    }
780
27.9k
  }
781
12.8k
}
782
783
483k
static AV1LrMTInfo *get_lr_job_info(AV1LrSync *lr_sync) {
784
483k
  AV1LrMTInfo *cur_job_info = NULL;
785
786
483k
#if CONFIG_MULTITHREAD
787
483k
  pthread_mutex_lock(lr_sync->job_mutex);
788
789
488k
  if (!lr_sync->lr_mt_exit && lr_sync->jobs_dequeued < lr_sync->jobs_enqueued) {
790
41.5k
    cur_job_info = lr_sync->job_queue + lr_sync->jobs_dequeued;
791
41.5k
    lr_sync->jobs_dequeued++;
792
41.5k
  }
793
794
483k
  pthread_mutex_unlock(lr_sync->job_mutex);
795
#else
796
  (void)lr_sync;
797
#endif
798
799
483k
  return cur_job_info;
800
483k
}
801
802
static void set_loop_restoration_done(AV1LrSync *const lr_sync,
803
0
                                      FilterFrameCtxt *const ctxt) {
804
0
  for (int plane = 0; plane < MAX_MB_PLANE; ++plane) {
805
0
    if (ctxt[plane].rsi->frame_restoration_type == RESTORE_NONE) continue;
806
0
    int y0 = 0, row_number = 0;
807
0
    const int unit_size = ctxt[plane].rsi->restoration_unit_size;
808
0
    const int plane_h = ctxt[plane].plane_h;
809
0
    const int ext_size = unit_size * 3 / 2;
810
0
    const int hnum_rest_units = ctxt[plane].rsi->horz_units;
811
0
    while (y0 < plane_h) {
812
0
      const int remaining_h = plane_h - y0;
813
0
      const int h = (remaining_h < ext_size) ? remaining_h : unit_size;
814
0
      lr_sync_write(lr_sync, row_number, hnum_rest_units - 1, hnum_rest_units,
815
0
                    plane);
816
0
      y0 += h;
817
0
      ++row_number;
818
0
    }
819
0
  }
820
0
}
821
822
// Implement row loop restoration for each thread.
823
442k
static int loop_restoration_row_worker(void *arg1, void *arg2) {
824
442k
  AV1LrSync *const lr_sync = (AV1LrSync *)arg1;
825
442k
  LRWorkerData *lrworkerdata = (LRWorkerData *)arg2;
826
442k
  AV1LrStruct *lr_ctxt = (AV1LrStruct *)lrworkerdata->lr_ctxt;
827
442k
  FilterFrameCtxt *ctxt = lr_ctxt->ctxt;
828
442k
  int lr_unit_row;
829
442k
  int plane;
830
442k
  int plane_w;
831
442k
#if CONFIG_MULTITHREAD
832
442k
  pthread_mutex_t *job_mutex_ = lr_sync->job_mutex;
833
442k
#endif
834
442k
  struct aom_internal_error_info *const error_info = &lrworkerdata->error_info;
835
836
  // The jmp_buf is valid only for the duration of the function that calls
837
  // setjmp(). Therefore, this function must reset the 'setjmp' field to 0
838
  // before it returns.
839
442k
  if (setjmp(error_info->jmp)) {
840
0
    error_info->setjmp = 0;
841
0
#if CONFIG_MULTITHREAD
842
0
    pthread_mutex_lock(job_mutex_);
843
0
    lr_sync->lr_mt_exit = true;
844
0
    pthread_mutex_unlock(job_mutex_);
845
0
#endif
846
    // In case of loop restoration multithreading, the worker on an even lr
847
    // block row waits for the completion of the filtering of the top-right and
848
    // bottom-right blocks. Hence, in case a thread (main/worker) encounters an
849
    // error, update that filtering of every row in the frame is complete in
850
    // order to avoid the dependent workers from waiting indefinitely.
851
0
    set_loop_restoration_done(lr_sync, lr_ctxt->ctxt);
852
0
    return 0;
853
0
  }
854
442k
  error_info->setjmp = 1;
855
856
442k
  typedef void (*copy_fun)(const YV12_BUFFER_CONFIG *src_ybc,
857
442k
                           YV12_BUFFER_CONFIG *dst_ybc, int hstart, int hend,
858
442k
                           int vstart, int vend);
859
442k
  static const copy_fun copy_funs[MAX_MB_PLANE] = {
860
442k
    aom_yv12_partial_coloc_copy_y, aom_yv12_partial_coloc_copy_u,
861
442k
    aom_yv12_partial_coloc_copy_v
862
442k
  };
863
864
484k
  while (1) {
865
484k
    AV1LrMTInfo *cur_job_info = get_lr_job_info(lr_sync);
866
484k
    if (cur_job_info != NULL) {
867
41.5k
      RestorationTileLimits limits;
868
41.5k
      sync_read_fn_t on_sync_read;
869
41.5k
      sync_write_fn_t on_sync_write;
870
41.5k
      limits.v_start = cur_job_info->v_start;
871
41.5k
      limits.v_end = cur_job_info->v_end;
872
41.5k
      lr_unit_row = cur_job_info->lr_unit_row;
873
41.5k
      plane = cur_job_info->plane;
874
41.5k
      plane_w = ctxt[plane].plane_w;
875
876
      // sync_mode == 1 implies only sync read is required in LR Multi-threading
877
      // sync_mode == 0 implies only sync write is required.
878
41.5k
      on_sync_read =
879
41.5k
          cur_job_info->sync_mode == 1 ? lr_sync_read : av1_lr_sync_read_dummy;
880
41.5k
      on_sync_write = cur_job_info->sync_mode == 0 ? lr_sync_write
881
41.5k
                                                   : av1_lr_sync_write_dummy;
882
883
41.5k
      av1_foreach_rest_unit_in_row(
884
41.5k
          &limits, plane_w, lr_ctxt->on_rest_unit, lr_unit_row,
885
41.5k
          ctxt[plane].rsi->restoration_unit_size, ctxt[plane].rsi->horz_units,
886
41.5k
          ctxt[plane].rsi->vert_units, plane, &ctxt[plane],
887
41.5k
          lrworkerdata->rst_tmpbuf, lrworkerdata->rlbs, on_sync_read,
888
41.5k
          on_sync_write, lr_sync, error_info);
889
890
41.5k
      copy_funs[plane](lr_ctxt->dst, lr_ctxt->frame, 0, plane_w,
891
41.5k
                       cur_job_info->v_copy_start, cur_job_info->v_copy_end);
892
893
41.5k
      if (lrworkerdata->do_extend_border) {
894
0
        aom_extend_frame_borders_plane_row(lr_ctxt->frame, plane,
895
0
                                           cur_job_info->v_copy_start,
896
0
                                           cur_job_info->v_copy_end);
897
0
      }
898
442k
    } else {
899
442k
      break;
900
442k
    }
901
484k
  }
902
442k
  error_info->setjmp = 0;
903
442k
  return 1;
904
442k
}
905
906
static inline void sync_lr_workers(AVxWorker *const workers,
907
12.8k
                                   AV1_COMMON *const cm, int num_workers) {
908
12.8k
  const AVxWorkerInterface *const winterface = aom_get_worker_interface();
909
12.8k
  int had_error = workers[0].had_error;
910
12.8k
  struct aom_internal_error_info error_info;
911
912
  // Read the error_info of main thread.
913
12.8k
  if (had_error) {
914
0
    AVxWorker *const worker = &workers[0];
915
0
    error_info = ((LRWorkerData *)worker->data2)->error_info;
916
0
  }
917
918
  // Wait till all rows are finished.
919
447k
  for (int i = num_workers - 1; i > 0; --i) {
920
434k
    AVxWorker *const worker = &workers[i];
921
434k
    if (!winterface->sync(worker)) {
922
0
      had_error = 1;
923
0
      error_info = ((LRWorkerData *)worker->data2)->error_info;
924
0
    }
925
434k
  }
926
12.8k
  if (had_error) aom_internal_error_copy(cm->error, &error_info);
927
12.8k
}
928
929
static void foreach_rest_unit_in_planes_mt(AV1LrStruct *lr_ctxt,
930
                                           AVxWorker *workers, int num_workers,
931
                                           AV1LrSync *lr_sync, AV1_COMMON *cm,
932
12.8k
                                           int do_extend_border) {
933
12.8k
  FilterFrameCtxt *ctxt = lr_ctxt->ctxt;
934
935
12.8k
  const int num_planes = av1_num_planes(cm);
936
937
12.8k
  const AVxWorkerInterface *const winterface = aom_get_worker_interface();
938
12.8k
  int num_rows_lr = 0;
939
940
49.7k
  for (int plane = 0; plane < num_planes; plane++) {
941
36.9k
    if (cm->rst_info[plane].frame_restoration_type == RESTORE_NONE) continue;
942
943
27.9k
    const int plane_h = ctxt[plane].plane_h;
944
27.9k
    const int unit_size = cm->rst_info[plane].restoration_unit_size;
945
946
27.9k
    num_rows_lr = AOMMAX(num_rows_lr, av1_lr_count_units(unit_size, plane_h));
947
27.9k
  }
948
949
12.8k
  int i;
950
12.8k
  static_assert(MAX_MB_PLANE == 3, "");
951
952
12.8k
  if (!lr_sync->sync_range || num_rows_lr > lr_sync->rows ||
953
11.5k
      num_workers > lr_sync->num_workers || num_planes > lr_sync->num_planes) {
954
1.30k
    av1_loop_restoration_dealloc(lr_sync);
955
1.30k
    av1_loop_restoration_alloc(lr_sync, cm, num_workers, num_rows_lr,
956
1.30k
                               num_planes, cm->width);
957
1.30k
  }
958
12.8k
  lr_sync->lr_mt_exit = false;
959
960
  // Initialize cur_sb_col to -1 for all SB rows.
961
49.7k
  for (i = 0; i < num_planes; i++) {
962
36.9k
    memset(lr_sync->cur_sb_col[i], -1,
963
36.9k
           sizeof(*(lr_sync->cur_sb_col[i])) * num_rows_lr);
964
36.9k
  }
965
966
12.8k
  enqueue_lr_jobs(lr_sync, lr_ctxt, cm);
967
968
  // Set up looprestoration thread data.
969
460k
  for (i = num_workers - 1; i >= 0; --i) {
970
447k
    AVxWorker *const worker = &workers[i];
971
447k
    lr_sync->lrworkerdata[i].lr_ctxt = (void *)lr_ctxt;
972
447k
    lr_sync->lrworkerdata[i].do_extend_border = do_extend_border;
973
447k
    worker->hook = loop_restoration_row_worker;
974
447k
    worker->data1 = lr_sync;
975
447k
    worker->data2 = &lr_sync->lrworkerdata[i];
976
977
    // Start loop restoration
978
447k
    worker->had_error = 0;
979
447k
    if (i == 0) {
980
12.8k
      winterface->execute(worker);
981
434k
    } else {
982
434k
      winterface->launch(worker);
983
434k
    }
984
447k
  }
985
986
12.8k
  sync_lr_workers(workers, cm, num_workers);
987
12.8k
}
988
989
void av1_loop_restoration_filter_frame_mt(YV12_BUFFER_CONFIG *frame,
990
                                          AV1_COMMON *cm, int optimized_lr,
991
                                          AVxWorker *workers, int num_workers,
992
                                          AV1LrSync *lr_sync, void *lr_ctxt,
993
12.8k
                                          int do_extend_border) {
994
12.8k
  assert(!cm->features.all_lossless);
995
996
12.8k
  const int num_planes = av1_num_planes(cm);
997
998
12.8k
  AV1LrStruct *loop_rest_ctxt = (AV1LrStruct *)lr_ctxt;
999
1000
12.8k
  av1_loop_restoration_filter_frame_init(loop_rest_ctxt, frame, cm,
1001
12.8k
                                         optimized_lr, num_planes);
1002
1003
12.8k
  foreach_rest_unit_in_planes_mt(loop_rest_ctxt, workers, num_workers, lr_sync,
1004
12.8k
                                 cm, do_extend_border);
1005
12.8k
}
1006
#endif  // !CONFIG_REALTIME_ONLY || CONFIG_AV1_DECODER
1007
1008
// Initializes cdef_sync parameters.
1009
16.9k
static inline void reset_cdef_job_info(AV1CdefSync *const cdef_sync) {
1010
16.9k
  cdef_sync->end_of_frame = 0;
1011
16.9k
  cdef_sync->fbr = 0;
1012
16.9k
  cdef_sync->fbc = 0;
1013
16.9k
  cdef_sync->cdef_mt_exit = false;
1014
16.9k
}
1015
1016
static inline void launch_cdef_workers(AVxWorker *const workers,
1017
16.9k
                                       int num_workers) {
1018
16.9k
  const AVxWorkerInterface *const winterface = aom_get_worker_interface();
1019
594k
  for (int i = num_workers - 1; i >= 0; i--) {
1020
577k
    AVxWorker *const worker = &workers[i];
1021
577k
    worker->had_error = 0;
1022
577k
    if (i == 0)
1023
16.9k
      winterface->execute(worker);
1024
560k
    else
1025
560k
      winterface->launch(worker);
1026
577k
  }
1027
16.9k
}
1028
1029
static inline void sync_cdef_workers(AVxWorker *const workers,
1030
16.9k
                                     AV1_COMMON *const cm, int num_workers) {
1031
16.9k
  const AVxWorkerInterface *const winterface = aom_get_worker_interface();
1032
16.9k
  int had_error = workers[0].had_error;
1033
16.9k
  struct aom_internal_error_info error_info;
1034
1035
  // Read the error_info of main thread.
1036
16.9k
  if (had_error) {
1037
0
    AVxWorker *const worker = &workers[0];
1038
0
    error_info = ((AV1CdefWorkerData *)worker->data2)->error_info;
1039
0
  }
1040
1041
  // Wait till all rows are finished.
1042
577k
  for (int i = num_workers - 1; i > 0; --i) {
1043
560k
    AVxWorker *const worker = &workers[i];
1044
560k
    if (!winterface->sync(worker)) {
1045
0
      had_error = 1;
1046
0
      error_info = ((AV1CdefWorkerData *)worker->data2)->error_info;
1047
0
    }
1048
560k
  }
1049
16.9k
  if (had_error) aom_internal_error_copy(cm->error, &error_info);
1050
16.9k
}
1051
1052
// Updates the row index of the next job to be processed.
1053
// Also updates end_of_frame flag when the processing of all rows is complete.
1054
static void update_cdef_row_next_job_info(AV1CdefSync *const cdef_sync,
1055
69.4k
                                          const int nvfb) {
1056
69.4k
  cdef_sync->fbr++;
1057
69.4k
  if (cdef_sync->fbr == nvfb) {
1058
16.9k
    cdef_sync->end_of_frame = 1;
1059
16.9k
  }
1060
69.4k
}
1061
1062
// Checks if a job is available. If job is available,
1063
// populates next job information and returns 1, else returns 0.
1064
static inline int get_cdef_row_next_job(AV1CdefSync *const cdef_sync,
1065
637k
                                        volatile int *cur_fbr, const int nvfb) {
1066
637k
#if CONFIG_MULTITHREAD
1067
637k
  pthread_mutex_lock(cdef_sync->mutex_);
1068
637k
#endif  // CONFIG_MULTITHREAD
1069
637k
  int do_next_row = 0;
1070
  // Populates information needed for current job and update the row
1071
  // index of the next row to be processed.
1072
646k
  if (!cdef_sync->cdef_mt_exit && cdef_sync->end_of_frame == 0) {
1073
69.4k
    do_next_row = 1;
1074
69.4k
    *cur_fbr = cdef_sync->fbr;
1075
69.4k
    update_cdef_row_next_job_info(cdef_sync, nvfb);
1076
69.4k
  }
1077
637k
#if CONFIG_MULTITHREAD
1078
637k
  pthread_mutex_unlock(cdef_sync->mutex_);
1079
637k
#endif  // CONFIG_MULTITHREAD
1080
637k
  return do_next_row;
1081
637k
}
1082
1083
0
static void set_cdef_init_fb_row_done(AV1CdefSync *const cdef_sync, int nvfb) {
1084
0
  for (int fbr = 0; fbr < nvfb; fbr++) cdef_row_mt_sync_write(cdef_sync, fbr);
1085
0
}
1086
1087
// Hook function for each thread in CDEF multi-threading.
1088
570k
static int cdef_sb_row_worker_hook(void *arg1, void *arg2) {
1089
570k
  AV1CdefSync *const cdef_sync = (AV1CdefSync *)arg1;
1090
570k
  AV1CdefWorkerData *const cdef_worker = (AV1CdefWorkerData *)arg2;
1091
570k
  AV1_COMMON *cm = cdef_worker->cm;
1092
570k
  const int nvfb = (cm->mi_params.mi_rows + MI_SIZE_64X64 - 1) / MI_SIZE_64X64;
1093
1094
570k
#if CONFIG_MULTITHREAD
1095
570k
  pthread_mutex_t *job_mutex_ = cdef_sync->mutex_;
1096
570k
#endif
1097
570k
  struct aom_internal_error_info *const error_info = &cdef_worker->error_info;
1098
1099
  // The jmp_buf is valid only for the duration of the function that calls
1100
  // setjmp(). Therefore, this function must reset the 'setjmp' field to 0
1101
  // before it returns.
1102
570k
  if (setjmp(error_info->jmp)) {
1103
0
    error_info->setjmp = 0;
1104
0
#if CONFIG_MULTITHREAD
1105
0
    pthread_mutex_lock(job_mutex_);
1106
0
    cdef_sync->cdef_mt_exit = true;
1107
0
    pthread_mutex_unlock(job_mutex_);
1108
0
#endif
1109
    // In case of cdef row-multithreading, the worker on a filter block row
1110
    // (fbr) waits for the line buffers (top and bottom) copy of the above row.
1111
    // Hence, in case a thread (main/worker) encounters an error before copying
1112
    // of the line buffers, update that line buffer copy is complete in order to
1113
    // avoid dependent workers waiting indefinitely.
1114
0
    set_cdef_init_fb_row_done(cdef_sync, nvfb);
1115
0
    return 0;
1116
0
  }
1117
570k
  error_info->setjmp = 1;
1118
1119
570k
  volatile int cur_fbr;
1120
570k
  const int num_planes = av1_num_planes(cm);
1121
639k
  while (get_cdef_row_next_job(cdef_sync, &cur_fbr, nvfb)) {
1122
69.3k
    MACROBLOCKD *xd = cdef_worker->xd;
1123
69.3k
    av1_cdef_fb_row(cm, xd, cdef_worker->linebuf, cdef_worker->colbuf,
1124
69.3k
                    cdef_worker->srcbuf, cur_fbr,
1125
69.3k
                    cdef_worker->cdef_init_fb_row_fn, cdef_sync, error_info);
1126
69.3k
    if (cdef_worker->do_extend_border) {
1127
0
      for (int plane = 0; plane < num_planes; ++plane) {
1128
0
        const YV12_BUFFER_CONFIG *ybf = &cm->cur_frame->buf;
1129
0
        const int is_uv = plane > 0;
1130
0
        const int mi_high = MI_SIZE_LOG2 - xd->plane[plane].subsampling_y;
1131
0
        const int unit_height = MI_SIZE_64X64 << mi_high;
1132
0
        const int v_start = cur_fbr * unit_height;
1133
0
        const int v_end =
1134
0
            AOMMIN(v_start + unit_height, ybf->crop_heights[is_uv]);
1135
0
        aom_extend_frame_borders_plane_row(ybf, plane, v_start, v_end);
1136
0
      }
1137
0
    }
1138
69.3k
  }
1139
570k
  error_info->setjmp = 0;
1140
570k
  return 1;
1141
570k
}
1142
1143
// Assigns CDEF hook function and thread data to each worker.
1144
static void prepare_cdef_frame_workers(
1145
    AV1_COMMON *const cm, MACROBLOCKD *xd, AV1CdefWorkerData *const cdef_worker,
1146
    AVxWorkerHook hook, AVxWorker *const workers, AV1CdefSync *const cdef_sync,
1147
    int num_workers, cdef_init_fb_row_t cdef_init_fb_row_fn,
1148
16.9k
    int do_extend_border) {
1149
16.9k
  const int num_planes = av1_num_planes(cm);
1150
1151
16.9k
  cdef_worker[0].srcbuf = cm->cdef_info.srcbuf;
1152
66.4k
  for (int plane = 0; plane < num_planes; plane++)
1153
49.5k
    cdef_worker[0].colbuf[plane] = cm->cdef_info.colbuf[plane];
1154
594k
  for (int i = num_workers - 1; i >= 0; i--) {
1155
577k
    AVxWorker *const worker = &workers[i];
1156
577k
    cdef_worker[i].cm = cm;
1157
577k
    cdef_worker[i].xd = xd;
1158
577k
    cdef_worker[i].cdef_init_fb_row_fn = cdef_init_fb_row_fn;
1159
577k
    cdef_worker[i].do_extend_border = do_extend_border;
1160
2.24M
    for (int plane = 0; plane < num_planes; plane++)
1161
1.67M
      cdef_worker[i].linebuf[plane] = cm->cdef_info.linebuf[plane];
1162
1163
577k
    worker->hook = hook;
1164
577k
    worker->data1 = cdef_sync;
1165
577k
    worker->data2 = &cdef_worker[i];
1166
577k
  }
1167
16.9k
}
1168
1169
// Initializes row-level parameters for CDEF frame.
1170
void av1_cdef_init_fb_row_mt(const AV1_COMMON *const cm,
1171
                             const MACROBLOCKD *const xd,
1172
                             CdefBlockInfo *const fb_info,
1173
                             uint16_t **const linebuf, uint16_t *const src,
1174
69.2k
                             struct AV1CdefSyncData *const cdef_sync, int fbr) {
1175
69.2k
  const int num_planes = av1_num_planes(cm);
1176
69.2k
  const int nvfb = (cm->mi_params.mi_rows + MI_SIZE_64X64 - 1) / MI_SIZE_64X64;
1177
69.2k
  const int luma_stride =
1178
69.2k
      ALIGN_POWER_OF_TWO(cm->mi_params.mi_cols << MI_SIZE_LOG2, 4);
1179
1180
  // for the current filter block, it's top left corner mi structure (mi_tl)
1181
  // is first accessed to check whether the top and left boundaries are
1182
  // frame boundaries. Then bottom-left and top-right mi structures are
1183
  // accessed to check whether the bottom and right boundaries
1184
  // (respectively) are frame boundaries.
1185
  //
1186
  // Note that we can't just check the bottom-right mi structure - eg. if
1187
  // we're at the right-hand edge of the frame but not the bottom, then
1188
  // the bottom-right mi is NULL but the bottom-left is not.
1189
69.2k
  fb_info->frame_boundary[TOP] = (MI_SIZE_64X64 * fbr == 0) ? 1 : 0;
1190
69.2k
  if (fbr != nvfb - 1)
1191
52.3k
    fb_info->frame_boundary[BOTTOM] =
1192
52.3k
        (MI_SIZE_64X64 * (fbr + 1) == cm->mi_params.mi_rows) ? 1 : 0;
1193
16.9k
  else
1194
16.9k
    fb_info->frame_boundary[BOTTOM] = 1;
1195
1196
69.2k
  fb_info->src = src;
1197
69.2k
  fb_info->damping = cm->cdef_info.cdef_damping;
1198
69.2k
  fb_info->coeff_shift = AOMMAX(cm->seq_params->bit_depth - 8, 0);
1199
69.2k
  av1_zero(fb_info->dir);
1200
69.2k
  av1_zero(fb_info->var);
1201
1202
262k
  for (int plane = 0; plane < num_planes; plane++) {
1203
193k
    const int stride = luma_stride >> xd->plane[plane].subsampling_x;
1204
193k
    uint16_t *top_linebuf = &linebuf[plane][0];
1205
193k
    uint16_t *bot_linebuf = &linebuf[plane][nvfb * CDEF_VBORDER * stride];
1206
193k
    {
1207
193k
      const int mi_high_l2 = MI_SIZE_LOG2 - xd->plane[plane].subsampling_y;
1208
193k
      const int top_offset = MI_SIZE_64X64 * (fbr + 1) << mi_high_l2;
1209
193k
      const int bot_offset = MI_SIZE_64X64 * (fbr + 1) << mi_high_l2;
1210
1211
193k
      if (fbr != nvfb - 1)  // if (fbr != 0)  // top line buffer copy
1212
148k
        av1_cdef_copy_sb8_16(
1213
148k
            cm, &top_linebuf[(fbr + 1) * CDEF_VBORDER * stride], stride,
1214
148k
            xd->plane[plane].dst.buf, top_offset - CDEF_VBORDER, 0,
1215
148k
            xd->plane[plane].dst.stride, CDEF_VBORDER, stride);
1216
193k
      if (fbr != nvfb - 1)  // bottom line buffer copy
1217
145k
        av1_cdef_copy_sb8_16(cm, &bot_linebuf[fbr * CDEF_VBORDER * stride],
1218
145k
                             stride, xd->plane[plane].dst.buf, bot_offset, 0,
1219
145k
                             xd->plane[plane].dst.stride, CDEF_VBORDER, stride);
1220
193k
    }
1221
1222
193k
    fb_info->top_linebuf[plane] = &linebuf[plane][fbr * CDEF_VBORDER * stride];
1223
193k
    fb_info->bot_linebuf[plane] =
1224
193k
        &linebuf[plane]
1225
193k
                [nvfb * CDEF_VBORDER * stride + (fbr * CDEF_VBORDER * stride)];
1226
193k
  }
1227
1228
69.2k
  cdef_row_mt_sync_write(cdef_sync, fbr);
1229
69.2k
  cdef_row_mt_sync_read(cdef_sync, fbr);
1230
69.2k
}
1231
1232
// Implements multi-threading for CDEF.
1233
// Perform CDEF on input frame.
1234
// Inputs:
1235
//   frame: Pointer to input frame buffer.
1236
//   cm: Pointer to common structure.
1237
//   xd: Pointer to common current coding block structure.
1238
// Returns:
1239
//   Nothing will be returned.
1240
void av1_cdef_frame_mt(AV1_COMMON *const cm, MACROBLOCKD *const xd,
1241
                       AV1CdefWorkerData *const cdef_worker,
1242
                       AVxWorker *const workers, AV1CdefSync *const cdef_sync,
1243
                       int num_workers, cdef_init_fb_row_t cdef_init_fb_row_fn,
1244
16.9k
                       int do_extend_border) {
1245
16.9k
  YV12_BUFFER_CONFIG *frame = &cm->cur_frame->buf;
1246
16.9k
  const int num_planes = av1_num_planes(cm);
1247
1248
16.9k
  av1_setup_dst_planes(xd->plane, cm->seq_params->sb_size, frame, 0, 0, 0,
1249
16.9k
                       num_planes);
1250
1251
16.9k
  reset_cdef_job_info(cdef_sync);
1252
16.9k
  prepare_cdef_frame_workers(cm, xd, cdef_worker, cdef_sb_row_worker_hook,
1253
16.9k
                             workers, cdef_sync, num_workers,
1254
16.9k
                             cdef_init_fb_row_fn, do_extend_border);
1255
16.9k
  launch_cdef_workers(workers, num_workers);
1256
16.9k
  sync_cdef_workers(workers, cm, num_workers);
1257
16.9k
}
1258
1259
98.4k
int av1_get_intrabc_extra_top_right_sb_delay(const AV1_COMMON *cm) {
1260
  // No additional top-right delay when intraBC tool is not enabled.
1261
98.4k
  if (!av1_allow_intrabc(cm)) return 0;
1262
  // Due to the hardware constraints on processing the intraBC tool with row
1263
  // multithreading, a top-right delay of 3 superblocks of size 128x128 or 5
1264
  // superblocks of size 64x64 is mandated. However, a minimum top-right delay
1265
  // of 1 superblock is assured with 'sync_range'. Hence return only the
1266
  // additional superblock delay when the intraBC tool is enabled.
1267
5.89k
  return cm->seq_params->sb_size == BLOCK_128X128 ? 2 : 4;
1268
98.4k
}