Coverage Report

Created: 2026-10-28 07:26

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
0
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
0
  if (width < 640)
39
0
    return 1;
40
0
  else if (width <= 1280)
41
0
    return 2;
42
0
  else if (width <= 4096)
43
0
    return 4;
44
0
  else
45
0
    return 8;
46
0
}
47
48
#if !CONFIG_REALTIME_ONLY || CONFIG_AV1_DECODER
49
0
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
0
  (void)width;
63
0
  return 1;
64
0
#endif
65
0
}
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
0
                           int width, int num_workers) {
71
0
  lf_sync->rows = rows;
72
0
#if CONFIG_MULTITHREAD
73
0
  {
74
0
    int i, j;
75
76
0
    for (j = 0; j < MAX_MB_PLANE; j++) {
77
0
      CHECK_MEM_ERROR(cm, lf_sync->mutex_[j],
78
0
                      aom_malloc(sizeof(*(lf_sync->mutex_[j])) * rows));
79
0
      if (lf_sync->mutex_[j]) {
80
0
        for (i = 0; i < rows; ++i) {
81
0
          pthread_mutex_init(&lf_sync->mutex_[j][i], NULL);
82
0
        }
83
0
      }
84
85
0
      CHECK_MEM_ERROR(cm, lf_sync->cond_[j],
86
0
                      aom_malloc(sizeof(*(lf_sync->cond_[j])) * rows));
87
0
      if (lf_sync->cond_[j]) {
88
0
        for (i = 0; i < rows; ++i) {
89
0
          pthread_cond_init(&lf_sync->cond_[j][i], NULL);
90
0
        }
91
0
      }
92
0
    }
93
94
0
    CHECK_MEM_ERROR(cm, lf_sync->job_mutex,
95
0
                    aom_malloc(sizeof(*(lf_sync->job_mutex))));
96
0
    if (lf_sync->job_mutex) {
97
0
      pthread_mutex_init(lf_sync->job_mutex, NULL);
98
0
    }
99
0
  }
100
0
#endif  // CONFIG_MULTITHREAD
101
0
  CHECK_MEM_ERROR(cm, lf_sync->lfdata,
102
0
                  aom_malloc(num_workers * sizeof(*(lf_sync->lfdata))));
103
0
  lf_sync->num_workers = num_workers;
104
105
0
  for (int j = 0; j < MAX_MB_PLANE; j++) {
106
0
    CHECK_MEM_ERROR(cm, lf_sync->cur_sb_col[j],
107
0
                    aom_malloc(sizeof(*(lf_sync->cur_sb_col[j])) * rows));
108
0
  }
109
0
  CHECK_MEM_ERROR(
110
0
      cm, lf_sync->job_queue,
111
0
      aom_malloc(sizeof(*(lf_sync->job_queue)) * rows * MAX_MB_PLANE * 2));
112
  // Set up nsync.
113
0
  lf_sync->sync_range = get_sync_range(width);
114
0
}
115
116
// Deallocate lf synchronization related mutex and data
117
0
void av1_loop_filter_dealloc(AV1LfSync *lf_sync) {
118
0
  if (lf_sync != NULL) {
119
0
    int j;
120
0
#if CONFIG_MULTITHREAD
121
0
    int i;
122
0
    for (j = 0; j < MAX_MB_PLANE; j++) {
123
0
      if (lf_sync->mutex_[j] != NULL) {
124
0
        for (i = 0; i < lf_sync->rows; ++i) {
125
0
          pthread_mutex_destroy(&lf_sync->mutex_[j][i]);
126
0
        }
127
0
        aom_free(lf_sync->mutex_[j]);
128
0
      }
129
0
      if (lf_sync->cond_[j] != NULL) {
130
0
        for (i = 0; i < lf_sync->rows; ++i) {
131
0
          pthread_cond_destroy(&lf_sync->cond_[j][i]);
132
0
        }
133
0
        aom_free(lf_sync->cond_[j]);
134
0
      }
135
0
    }
136
0
    if (lf_sync->job_mutex != NULL) {
137
0
      pthread_mutex_destroy(lf_sync->job_mutex);
138
0
      aom_free(lf_sync->job_mutex);
139
0
    }
140
0
#endif  // CONFIG_MULTITHREAD
141
0
    aom_free(lf_sync->lfdata);
142
0
    for (j = 0; j < MAX_MB_PLANE; j++) {
143
0
      aom_free(lf_sync->cur_sb_col[j]);
144
0
    }
145
146
0
    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
0
    av1_zero(*lf_sync);
150
0
  }
151
0
}
152
153
void av1_alloc_cdef_sync(AV1_COMMON *const cm, AV1CdefSync *cdef_sync,
154
0
                         int num_workers) {
155
0
  if (num_workers < 1) return;
156
0
#if CONFIG_MULTITHREAD
157
0
  if (cdef_sync->mutex_ == NULL) {
158
0
    CHECK_MEM_ERROR(cm, cdef_sync->mutex_,
159
0
                    aom_malloc(sizeof(*(cdef_sync->mutex_))));
160
0
    if (cdef_sync->mutex_) pthread_mutex_init(cdef_sync->mutex_, NULL);
161
0
  }
162
#else
163
  (void)cm;
164
  (void)cdef_sync;
165
#endif  // CONFIG_MULTITHREAD
166
0
}
167
168
0
void av1_free_cdef_sync(AV1CdefSync *cdef_sync) {
169
0
  if (cdef_sync == NULL) return;
170
0
#if CONFIG_MULTITHREAD
171
0
  if (cdef_sync->mutex_ != NULL) {
172
0
    pthread_mutex_destroy(cdef_sync->mutex_);
173
0
    aom_free(cdef_sync->mutex_);
174
0
  }
175
0
#endif  // CONFIG_MULTITHREAD
176
0
}
177
178
static inline void cdef_row_mt_sync_read(AV1CdefSync *const cdef_sync,
179
0
                                         int row) {
180
0
  if (!row) return;
181
0
#if CONFIG_MULTITHREAD
182
0
  AV1CdefRowSync *const cdef_row_mt = cdef_sync->cdef_row_mt;
183
0
  pthread_mutex_lock(cdef_row_mt[row - 1].row_mutex_);
184
0
  while (cdef_row_mt[row - 1].is_row_done != 1)
185
0
    pthread_cond_wait(cdef_row_mt[row - 1].row_cond_,
186
0
                      cdef_row_mt[row - 1].row_mutex_);
187
0
  cdef_row_mt[row - 1].is_row_done = 0;
188
0
  pthread_mutex_unlock(cdef_row_mt[row - 1].row_mutex_);
189
#else
190
  (void)cdef_sync;
191
#endif  // CONFIG_MULTITHREAD
192
0
}
193
194
static inline void cdef_row_mt_sync_write(AV1CdefSync *const cdef_sync,
195
0
                                          int row) {
196
0
#if CONFIG_MULTITHREAD
197
0
  AV1CdefRowSync *const cdef_row_mt = cdef_sync->cdef_row_mt;
198
0
  pthread_mutex_lock(cdef_row_mt[row].row_mutex_);
199
0
  pthread_cond_signal(cdef_row_mt[row].row_cond_);
200
0
  cdef_row_mt[row].is_row_done = 1;
201
0
  pthread_mutex_unlock(cdef_row_mt[row].row_mutex_);
202
#else
203
  (void)cdef_sync;
204
  (void)row;
205
#endif  // CONFIG_MULTITHREAD
206
0
}
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
0
#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
0
                             int plane) {
217
0
  bool lf_mt_continue = true;
218
0
#if CONFIG_MULTITHREAD
219
0
  const int nsync = lf_sync->sync_range;
220
221
0
  if (r && !(c & (nsync - 1))) {
222
0
    pthread_mutex_t *const mutex = &lf_sync->mutex_[plane][r - 1];
223
0
    pthread_mutex_lock(mutex);
224
225
0
    while (c > lf_sync->cur_sb_col[plane][r - 1] - nsync) {
226
0
      pthread_cond_wait(&lf_sync->cond_[plane][r - 1], mutex);
227
0
    }
228
0
    lf_mt_continue = lf_sync->cur_sb_col[plane][r - 1] != LF_SYNC_EXIT;
229
0
    pthread_mutex_unlock(mutex);
230
0
  }
231
#else
232
  (void)lf_sync;
233
  (void)r;
234
  (void)c;
235
  (void)plane;
236
#endif  // CONFIG_MULTITHREAD
237
0
  return lf_mt_continue;
238
0
}
239
240
static inline void sync_write(AV1LfSync *const lf_sync, int r, int c,
241
0
                              const int sb_cols, int plane) {
242
0
#if CONFIG_MULTITHREAD
243
0
  const int nsync = lf_sync->sync_range;
244
0
  int cur;
245
  // Only signal when there are enough filtered SB for next row to run.
246
0
  int sig = 1;
247
248
0
  if (c < sb_cols - 1) {
249
0
    cur = c;
250
0
    if (c % nsync) sig = 0;
251
0
  } else {
252
0
    cur = sb_cols + nsync;
253
0
  }
254
255
0
  if (sig) {
256
0
    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
0
    lf_sync->cur_sb_col[plane][r] = AOMMAX(lf_sync->cur_sb_col[plane][r], cur);
262
263
0
    pthread_cond_broadcast(&lf_sync->cond_[plane][r]);
264
0
    pthread_mutex_unlock(&lf_sync->mutex_[plane][r]);
265
0
  }
266
#else
267
  (void)lf_sync;
268
  (void)r;
269
  (void)c;
270
  (void)sb_cols;
271
  (void)plane;
272
#endif  // CONFIG_MULTITHREAD
273
0
}
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
0
    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
0
  (void)error_info;
286
0
  const int sb_cols =
287
0
      CEIL_POWER_OF_TWO(cm->mi_params.mi_cols, MAX_MIB_SIZE_LOG2);
288
0
  const int r = mi_row >> num_mis_in_lpf_unit_height_log2;
289
0
  int mi_col, c;
290
291
0
  const bool joint_filter_chroma = (lpf_opt_level == 2) && plane > AOM_PLANE_Y;
292
0
  const int num_planes = joint_filter_chroma ? 2 : 1;
293
0
  assert(IMPLIES(joint_filter_chroma, plane == AOM_PLANE_U));
294
295
0
  if (dir == 0) {
296
0
    for (mi_col = 0; mi_col < cm->mi_params.mi_cols; mi_col += MAX_MIB_SIZE) {
297
0
      c = mi_col >> MAX_MIB_SIZE_LOG2;
298
299
0
      av1_setup_dst_planes(planes, cm->seq_params->sb_size, frame_buffer,
300
0
                           mi_row, mi_col, plane, plane + num_planes);
301
0
      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
0
      } else {
312
0
        av1_filter_block_plane_vert(cm, xd, plane, &planes[plane], mi_row,
313
0
                                    mi_col);
314
0
      }
315
0
      if (lf_sync != NULL) {
316
0
        sync_write(lf_sync, r, c, sb_cols, plane);
317
0
      }
318
0
    }
319
0
  } else if (dir == 1) {
320
0
    for (mi_col = 0; mi_col < cm->mi_params.mi_cols; mi_col += MAX_MIB_SIZE) {
321
0
      c = mi_col >> MAX_MIB_SIZE_LOG2;
322
323
0
      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
0
        if (!sync_read(lf_sync, r, c, plane)) return;
327
328
        // Wait for vertical edge filtering of the right block to be completed
329
0
        if (!sync_read(lf_sync, r + 1, c, plane)) return;
330
0
      }
331
332
0
      av1_setup_dst_planes(planes, cm->seq_params->sb_size, frame_buffer,
333
0
                           mi_row, mi_col, plane, plane + num_planes);
334
0
      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
0
      } else {
345
0
        av1_filter_block_plane_horz(cm, xd, plane, &planes[plane], mi_row,
346
0
                                    mi_col);
347
0
      }
348
0
    }
349
0
  }
350
0
}
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
0
                                   AV1_COMMON *const cm, int num_workers) {
380
0
  const AVxWorkerInterface *const winterface = aom_get_worker_interface();
381
0
  int had_error = workers[0].had_error;
382
0
  struct aom_internal_error_info error_info;
383
384
  // Read the error_info of main thread.
385
0
  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
0
  for (int i = num_workers - 1; i > 0; --i) {
392
0
    AVxWorker *const worker = &workers[i];
393
0
    if (!winterface->sync(worker)) {
394
0
      had_error = 1;
395
0
      error_info = ((LFWorkerData *)worker->data2)->error_info;
396
0
    }
397
0
  }
398
0
  if (had_error) aom_internal_error_copy(cm->error, &error_info);
399
0
}
400
401
// Row-based multi-threaded loopfilter hook
402
0
static int loop_filter_row_worker(void *arg1, void *arg2) {
403
0
  AV1LfSync *const lf_sync = (AV1LfSync *)arg1;
404
0
  LFWorkerData *const lf_data = (LFWorkerData *)arg2;
405
0
  AV1LfMTInfo *cur_job_info;
406
407
0
#if CONFIG_MULTITHREAD
408
0
  pthread_mutex_t *job_mutex_ = lf_sync->job_mutex;
409
0
#endif
410
411
0
  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
0
  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
0
  error_info->setjmp = 1;
427
428
0
  while ((cur_job_info = get_lf_job_info(lf_sync)) != NULL) {
429
0
    const int lpf_opt_level = cur_job_info->lpf_opt_level;
430
0
    av1_thread_loop_filter_rows(
431
0
        lf_data->frame_buffer, lf_data->cm, lf_data->planes, lf_data->xd,
432
0
        cur_job_info->mi_row, cur_job_info->plane, cur_job_info->dir,
433
0
        lpf_opt_level, lf_sync, error_info, lf_data->params_buf,
434
0
        lf_data->tx_buf, MAX_MIB_SIZE_LOG2);
435
0
  }
436
0
  error_info->setjmp = 0;
437
0
  return 1;
438
0
}
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
0
                                AV1LfSync *lf_sync, int lpf_opt_level) {
445
0
  const AVxWorkerInterface *const winterface = aom_get_worker_interface();
446
0
  int i;
447
0
  loop_filter_frame_mt_init(cm, start, stop, planes_to_lf, num_workers, lf_sync,
448
0
                            lpf_opt_level, MAX_MIB_SIZE_LOG2);
449
450
  // Set up loopfilter thread data.
451
0
  for (i = num_workers - 1; i >= 0; --i) {
452
0
    AVxWorker *const worker = &workers[i];
453
0
    LFWorkerData *const lf_data = &lf_sync->lfdata[i];
454
455
0
    worker->hook = loop_filter_row_worker;
456
0
    worker->data1 = lf_sync;
457
0
    worker->data2 = lf_data;
458
459
    // Loopfilter data
460
0
    loop_filter_data_reset(lf_data, frame, cm, xd);
461
462
    // Start loopfiltering
463
0
    worker->had_error = 0;
464
0
    if (i == 0) {
465
0
      winterface->execute(worker);
466
0
    } else {
467
0
      winterface->launch(worker);
468
0
    }
469
0
  }
470
471
0
  sync_lf_workers(workers, cm, num_workers);
472
0
}
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
0
                             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
0
  int mi_row, plane, dir;
481
482
0
  AV1_DEBLOCKING_PARAMETERS params_buf[MAX_MIB_SIZE];
483
0
  TX_SIZE tx_buf[MAX_MIB_SIZE];
484
0
  for (mi_row = start; mi_row < stop; mi_row += MAX_MIB_SIZE) {
485
0
    for (plane = 0; plane < MAX_MB_PLANE; ++plane) {
486
0
      if (skip_loop_filter_plane(planes_to_lf, plane, lpf_opt_level)) {
487
0
        continue;
488
0
      }
489
490
0
      for (dir = 0; dir < 2; ++dir) {
491
0
        av1_thread_loop_filter_rows(frame, cm, xd->plane, xd, mi_row, plane,
492
0
                                    dir, lpf_opt_level, /*lf_sync=*/NULL,
493
0
                                    xd->error_info, params_buf, tx_buf,
494
0
                                    MAX_MIB_SIZE_LOG2);
495
0
      }
496
0
    }
497
0
  }
498
0
}
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
0
                              int lpf_opt_level) {
505
0
  int start_mi_row, end_mi_row, mi_rows_to_filter;
506
0
  int planes_to_lf[MAX_MB_PLANE];
507
508
0
  if (!check_planes_to_loop_filter(&cm->lf, planes_to_lf, plane_start,
509
0
                                   plane_end))
510
0
    return;
511
512
0
  start_mi_row = 0;
513
0
  mi_rows_to_filter = cm->mi_params.mi_rows;
514
0
  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
0
  end_mi_row = start_mi_row + mi_rows_to_filter;
520
0
  av1_loop_filter_frame_init(cm, plane_start, plane_end);
521
522
0
  if (num_workers > 1) {
523
    // Enqueue and execute loopfiltering jobs.
524
0
    loop_filter_rows_mt(frame, cm, xd, start_mi_row, end_mi_row, planes_to_lf,
525
0
                        workers, num_workers, lf_sync, lpf_opt_level);
526
0
  } else {
527
    // Directly filter in the main thread.
528
0
    loop_filter_rows(frame, cm, xd, start_mi_row, end_mi_row, planes_to_lf,
529
0
                     lpf_opt_level);
530
0
  }
531
0
}
532
533
#if !CONFIG_REALTIME_ONLY || CONFIG_AV1_DECODER
534
0
static inline void lr_sync_read(void *const lr_sync, int r, int c, int plane) {
535
0
#if CONFIG_MULTITHREAD
536
0
  AV1LrSync *const loop_res_sync = (AV1LrSync *)lr_sync;
537
0
  const int nsync = loop_res_sync->sync_range;
538
539
0
  if (r && !(c & (nsync - 1))) {
540
0
    pthread_mutex_t *const mutex = &loop_res_sync->mutex_[plane][r - 1];
541
0
    pthread_mutex_lock(mutex);
542
543
0
    while (c > loop_res_sync->cur_sb_col[plane][r - 1] - nsync) {
544
0
      pthread_cond_wait(&loop_res_sync->cond_[plane][r - 1], mutex);
545
0
    }
546
0
    pthread_mutex_unlock(mutex);
547
0
  }
548
#else
549
  (void)lr_sync;
550
  (void)r;
551
  (void)c;
552
  (void)plane;
553
#endif  // CONFIG_MULTITHREAD
554
0
}
555
556
static inline void lr_sync_write(void *const lr_sync, int r, int c,
557
0
                                 const int sb_cols, int plane) {
558
0
#if CONFIG_MULTITHREAD
559
0
  AV1LrSync *const loop_res_sync = (AV1LrSync *)lr_sync;
560
0
  const int nsync = loop_res_sync->sync_range;
561
0
  int cur;
562
  // Only signal when there are enough filtered SB for next row to run.
563
0
  int sig = 1;
564
565
0
  if (c < sb_cols - 1) {
566
0
    cur = c;
567
0
    if (c % nsync) sig = 0;
568
0
  } else {
569
0
    cur = sb_cols + nsync;
570
0
  }
571
572
0
  if (sig) {
573
0
    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
0
    loop_res_sync->cur_sb_col[plane][r] =
581
0
        AOMMAX(loop_res_sync->cur_sb_col[plane][r], cur);
582
583
0
    pthread_cond_broadcast(&loop_res_sync->cond_[plane][r]);
584
0
    pthread_mutex_unlock(&loop_res_sync->mutex_[plane][r]);
585
0
  }
586
#else
587
  (void)lr_sync;
588
  (void)r;
589
  (void)c;
590
  (void)sb_cols;
591
  (void)plane;
592
#endif  // CONFIG_MULTITHREAD
593
0
}
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
0
                                int num_planes, int width) {
599
0
  lr_sync->rows = num_rows_lr;
600
0
  lr_sync->num_planes = num_planes;
601
0
#if CONFIG_MULTITHREAD
602
0
  {
603
0
    int i, j;
604
605
0
    for (j = 0; j < num_planes; j++) {
606
0
      CHECK_MEM_ERROR(cm, lr_sync->mutex_[j],
607
0
                      aom_malloc(sizeof(*(lr_sync->mutex_[j])) * num_rows_lr));
608
0
      if (lr_sync->mutex_[j]) {
609
0
        for (i = 0; i < num_rows_lr; ++i) {
610
0
          pthread_mutex_init(&lr_sync->mutex_[j][i], NULL);
611
0
        }
612
0
      }
613
614
0
      CHECK_MEM_ERROR(cm, lr_sync->cond_[j],
615
0
                      aom_malloc(sizeof(*(lr_sync->cond_[j])) * num_rows_lr));
616
0
      if (lr_sync->cond_[j]) {
617
0
        for (i = 0; i < num_rows_lr; ++i) {
618
0
          pthread_cond_init(&lr_sync->cond_[j][i], NULL);
619
0
        }
620
0
      }
621
0
    }
622
623
0
    CHECK_MEM_ERROR(cm, lr_sync->job_mutex,
624
0
                    aom_malloc(sizeof(*(lr_sync->job_mutex))));
625
0
    if (lr_sync->job_mutex) {
626
0
      pthread_mutex_init(lr_sync->job_mutex, NULL);
627
0
    }
628
0
  }
629
0
#endif  // CONFIG_MULTITHREAD
630
0
  CHECK_MEM_ERROR(cm, lr_sync->lrworkerdata,
631
0
                  aom_calloc(num_workers, sizeof(*(lr_sync->lrworkerdata))));
632
0
  lr_sync->num_workers = num_workers;
633
634
0
  for (int worker_idx = 0; worker_idx < num_workers; ++worker_idx) {
635
0
    if (worker_idx < num_workers - 1) {
636
0
      CHECK_MEM_ERROR(cm, lr_sync->lrworkerdata[worker_idx].rst_tmpbuf,
637
0
                      (int32_t *)aom_memalign(16, RESTORATION_TMPBUF_SIZE));
638
0
      CHECK_MEM_ERROR(cm, lr_sync->lrworkerdata[worker_idx].rlbs,
639
0
                      aom_malloc(sizeof(RestorationLineBuffers)));
640
641
0
    } else {
642
0
      lr_sync->lrworkerdata[worker_idx].rst_tmpbuf = cm->rst_tmpbuf;
643
0
      lr_sync->lrworkerdata[worker_idx].rlbs = cm->rlbs;
644
0
    }
645
0
  }
646
647
0
  for (int j = 0; j < num_planes; j++) {
648
0
    CHECK_MEM_ERROR(
649
0
        cm, lr_sync->cur_sb_col[j],
650
0
        aom_malloc(sizeof(*(lr_sync->cur_sb_col[j])) * num_rows_lr));
651
0
  }
652
0
  CHECK_MEM_ERROR(
653
0
      cm, lr_sync->job_queue,
654
0
      aom_malloc(sizeof(*(lr_sync->job_queue)) * num_rows_lr * num_planes));
655
  // Set up nsync.
656
0
  lr_sync->sync_range = get_lr_sync_range(width);
657
0
}
658
659
// Deallocate loop restoration synchronization related mutex and data
660
0
void av1_loop_restoration_dealloc(AV1LrSync *lr_sync) {
661
0
  if (lr_sync != NULL) {
662
0
    int j;
663
0
#if CONFIG_MULTITHREAD
664
0
    int i;
665
0
    for (j = 0; j < MAX_MB_PLANE; j++) {
666
0
      if (lr_sync->mutex_[j] != NULL) {
667
0
        for (i = 0; i < lr_sync->rows; ++i) {
668
0
          pthread_mutex_destroy(&lr_sync->mutex_[j][i]);
669
0
        }
670
0
        aom_free(lr_sync->mutex_[j]);
671
0
      }
672
0
      if (lr_sync->cond_[j] != NULL) {
673
0
        for (i = 0; i < lr_sync->rows; ++i) {
674
0
          pthread_cond_destroy(&lr_sync->cond_[j][i]);
675
0
        }
676
0
        aom_free(lr_sync->cond_[j]);
677
0
      }
678
0
    }
679
0
    if (lr_sync->job_mutex != NULL) {
680
0
      pthread_mutex_destroy(lr_sync->job_mutex);
681
0
      aom_free(lr_sync->job_mutex);
682
0
    }
683
0
#endif  // CONFIG_MULTITHREAD
684
0
    for (j = 0; j < MAX_MB_PLANE; j++) {
685
0
      aom_free(lr_sync->cur_sb_col[j]);
686
0
    }
687
688
0
    aom_free(lr_sync->job_queue);
689
690
0
    if (lr_sync->lrworkerdata) {
691
0
      for (int worker_idx = 0; worker_idx < lr_sync->num_workers - 1;
692
0
           worker_idx++) {
693
0
        LRWorkerData *const workerdata_data =
694
0
            lr_sync->lrworkerdata + worker_idx;
695
696
0
        aom_free(workerdata_data->rst_tmpbuf);
697
0
        aom_free(workerdata_data->rlbs);
698
0
      }
699
0
      aom_free(lr_sync->lrworkerdata);
700
0
    }
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
0
    av1_zero(*lr_sync);
705
0
  }
706
0
}
707
708
static void enqueue_lr_jobs(AV1LrSync *lr_sync, AV1LrStruct *lr_ctxt,
709
0
                            AV1_COMMON *cm) {
710
0
  FilterFrameCtxt *ctxt = lr_ctxt->ctxt;
711
712
0
  const int num_planes = av1_num_planes(cm);
713
0
  AV1LrMTInfo *lr_job_queue = lr_sync->job_queue;
714
0
  int32_t lr_job_counter[2], num_even_lr_jobs = 0;
715
0
  lr_sync->jobs_enqueued = 0;
716
0
  lr_sync->jobs_dequeued = 0;
717
718
0
  for (int plane = 0; plane < num_planes; plane++) {
719
0
    if (cm->rst_info[plane].frame_restoration_type == RESTORE_NONE) continue;
720
0
    num_even_lr_jobs =
721
0
        num_even_lr_jobs + ((ctxt[plane].rsi->vert_units + 1) >> 1);
722
0
  }
723
0
  lr_job_counter[0] = 0;
724
0
  lr_job_counter[1] = num_even_lr_jobs;
725
726
0
  for (int plane = 0; plane < num_planes; plane++) {
727
0
    if (cm->rst_info[plane].frame_restoration_type == RESTORE_NONE) continue;
728
0
    const int is_uv = plane > 0;
729
0
    const int ss_y = is_uv && cm->seq_params->subsampling_y;
730
0
    const int unit_size = ctxt[plane].rsi->restoration_unit_size;
731
0
    const int plane_h = ctxt[plane].plane_h;
732
0
    const int ext_size = unit_size * 3 / 2;
733
734
0
    int y0 = 0, i = 0;
735
0
    while (y0 < plane_h) {
736
0
      int remaining_h = plane_h - y0;
737
0
      int h = (remaining_h < ext_size) ? remaining_h : unit_size;
738
739
0
      RestorationTileLimits limits;
740
0
      limits.v_start = y0;
741
0
      limits.v_end = y0 + h;
742
0
      assert(limits.v_end <= plane_h);
743
      // Offset upwards to align with the restoration processing stripe
744
0
      const int voffset = RESTORATION_UNIT_OFFSET >> ss_y;
745
0
      limits.v_start = AOMMAX(0, limits.v_start - voffset);
746
0
      if (limits.v_end < plane_h) limits.v_end -= voffset;
747
748
0
      assert(lr_job_counter[0] <= num_even_lr_jobs);
749
750
0
      lr_job_queue[lr_job_counter[i & 1]].lr_unit_row = i;
751
0
      lr_job_queue[lr_job_counter[i & 1]].plane = plane;
752
0
      lr_job_queue[lr_job_counter[i & 1]].v_start = limits.v_start;
753
0
      lr_job_queue[lr_job_counter[i & 1]].v_end = limits.v_end;
754
0
      lr_job_queue[lr_job_counter[i & 1]].sync_mode = i & 1;
755
0
      if ((i & 1) == 0) {
756
0
        lr_job_queue[lr_job_counter[i & 1]].v_copy_start =
757
0
            limits.v_start + RESTORATION_BORDER;
758
0
        lr_job_queue[lr_job_counter[i & 1]].v_copy_end =
759
0
            limits.v_end - RESTORATION_BORDER;
760
0
        if (i == 0) {
761
0
          assert(limits.v_start == 0);
762
0
          lr_job_queue[lr_job_counter[i & 1]].v_copy_start = 0;
763
0
        }
764
0
        if (i == (ctxt[plane].rsi->vert_units - 1)) {
765
0
          assert(limits.v_end == plane_h);
766
0
          lr_job_queue[lr_job_counter[i & 1]].v_copy_end = plane_h;
767
0
        }
768
0
      } else {
769
0
        lr_job_queue[lr_job_counter[i & 1]].v_copy_start =
770
0
            AOMMAX(limits.v_start - RESTORATION_BORDER, 0);
771
0
        lr_job_queue[lr_job_counter[i & 1]].v_copy_end =
772
0
            AOMMIN(limits.v_end + RESTORATION_BORDER, plane_h);
773
0
      }
774
0
      lr_job_counter[i & 1]++;
775
0
      lr_sync->jobs_enqueued++;
776
777
0
      y0 += h;
778
0
      ++i;
779
0
    }
780
0
  }
781
0
}
782
783
0
static AV1LrMTInfo *get_lr_job_info(AV1LrSync *lr_sync) {
784
0
  AV1LrMTInfo *cur_job_info = NULL;
785
786
0
#if CONFIG_MULTITHREAD
787
0
  pthread_mutex_lock(lr_sync->job_mutex);
788
789
0
  if (!lr_sync->lr_mt_exit && lr_sync->jobs_dequeued < lr_sync->jobs_enqueued) {
790
0
    cur_job_info = lr_sync->job_queue + lr_sync->jobs_dequeued;
791
0
    lr_sync->jobs_dequeued++;
792
0
  }
793
794
0
  pthread_mutex_unlock(lr_sync->job_mutex);
795
#else
796
  (void)lr_sync;
797
#endif
798
799
0
  return cur_job_info;
800
0
}
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
0
static int loop_restoration_row_worker(void *arg1, void *arg2) {
824
0
  AV1LrSync *const lr_sync = (AV1LrSync *)arg1;
825
0
  LRWorkerData *lrworkerdata = (LRWorkerData *)arg2;
826
0
  AV1LrStruct *lr_ctxt = (AV1LrStruct *)lrworkerdata->lr_ctxt;
827
0
  FilterFrameCtxt *ctxt = lr_ctxt->ctxt;
828
0
  int lr_unit_row;
829
0
  int plane;
830
0
  int plane_w;
831
0
#if CONFIG_MULTITHREAD
832
0
  pthread_mutex_t *job_mutex_ = lr_sync->job_mutex;
833
0
#endif
834
0
  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
0
  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
0
  error_info->setjmp = 1;
855
856
0
  typedef void (*copy_fun)(const YV12_BUFFER_CONFIG *src_ybc,
857
0
                           YV12_BUFFER_CONFIG *dst_ybc, int hstart, int hend,
858
0
                           int vstart, int vend);
859
0
  static const copy_fun copy_funs[MAX_MB_PLANE] = {
860
0
    aom_yv12_partial_coloc_copy_y, aom_yv12_partial_coloc_copy_u,
861
0
    aom_yv12_partial_coloc_copy_v
862
0
  };
863
864
0
  while (1) {
865
0
    AV1LrMTInfo *cur_job_info = get_lr_job_info(lr_sync);
866
0
    if (cur_job_info != NULL) {
867
0
      RestorationTileLimits limits;
868
0
      sync_read_fn_t on_sync_read;
869
0
      sync_write_fn_t on_sync_write;
870
0
      limits.v_start = cur_job_info->v_start;
871
0
      limits.v_end = cur_job_info->v_end;
872
0
      lr_unit_row = cur_job_info->lr_unit_row;
873
0
      plane = cur_job_info->plane;
874
0
      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
0
      on_sync_read =
879
0
          cur_job_info->sync_mode == 1 ? lr_sync_read : av1_lr_sync_read_dummy;
880
0
      on_sync_write = cur_job_info->sync_mode == 0 ? lr_sync_write
881
0
                                                   : av1_lr_sync_write_dummy;
882
883
0
      av1_foreach_rest_unit_in_row(
884
0
          &limits, plane_w, lr_ctxt->on_rest_unit, lr_unit_row,
885
0
          ctxt[plane].rsi->restoration_unit_size, ctxt[plane].rsi->horz_units,
886
0
          ctxt[plane].rsi->vert_units, plane, &ctxt[plane],
887
0
          lrworkerdata->rst_tmpbuf, lrworkerdata->rlbs, on_sync_read,
888
0
          on_sync_write, lr_sync, error_info);
889
890
0
      copy_funs[plane](lr_ctxt->dst, lr_ctxt->frame, 0, plane_w,
891
0
                       cur_job_info->v_copy_start, cur_job_info->v_copy_end);
892
893
0
      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
0
    } else {
899
0
      break;
900
0
    }
901
0
  }
902
0
  error_info->setjmp = 0;
903
0
  return 1;
904
0
}
905
906
static inline void sync_lr_workers(AVxWorker *const workers,
907
0
                                   AV1_COMMON *const cm, int num_workers) {
908
0
  const AVxWorkerInterface *const winterface = aom_get_worker_interface();
909
0
  int had_error = workers[0].had_error;
910
0
  struct aom_internal_error_info error_info;
911
912
  // Read the error_info of main thread.
913
0
  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
0
  for (int i = num_workers - 1; i > 0; --i) {
920
0
    AVxWorker *const worker = &workers[i];
921
0
    if (!winterface->sync(worker)) {
922
0
      had_error = 1;
923
0
      error_info = ((LRWorkerData *)worker->data2)->error_info;
924
0
    }
925
0
  }
926
0
  if (had_error) aom_internal_error_copy(cm->error, &error_info);
927
0
}
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
0
                                           int do_extend_border) {
933
0
  FilterFrameCtxt *ctxt = lr_ctxt->ctxt;
934
935
0
  const int num_planes = av1_num_planes(cm);
936
937
0
  const AVxWorkerInterface *const winterface = aom_get_worker_interface();
938
0
  int num_rows_lr = 0;
939
940
0
  for (int plane = 0; plane < num_planes; plane++) {
941
0
    if (cm->rst_info[plane].frame_restoration_type == RESTORE_NONE) continue;
942
943
0
    const int plane_h = ctxt[plane].plane_h;
944
0
    const int unit_size = cm->rst_info[plane].restoration_unit_size;
945
946
0
    num_rows_lr = AOMMAX(num_rows_lr, av1_lr_count_units(unit_size, plane_h));
947
0
  }
948
949
0
  int i;
950
0
  static_assert(MAX_MB_PLANE == 3, "");
951
952
0
  if (!lr_sync->sync_range || num_rows_lr > lr_sync->rows ||
953
0
      num_workers > lr_sync->num_workers || num_planes > lr_sync->num_planes) {
954
0
    av1_loop_restoration_dealloc(lr_sync);
955
0
    av1_loop_restoration_alloc(lr_sync, cm, num_workers, num_rows_lr,
956
0
                               num_planes, cm->width);
957
0
  }
958
0
  lr_sync->lr_mt_exit = false;
959
960
  // Initialize cur_sb_col to -1 for all SB rows.
961
0
  for (i = 0; i < num_planes; i++) {
962
0
    memset(lr_sync->cur_sb_col[i], -1,
963
0
           sizeof(*(lr_sync->cur_sb_col[i])) * num_rows_lr);
964
0
  }
965
966
0
  enqueue_lr_jobs(lr_sync, lr_ctxt, cm);
967
968
  // Set up looprestoration thread data.
969
0
  for (i = num_workers - 1; i >= 0; --i) {
970
0
    AVxWorker *const worker = &workers[i];
971
0
    lr_sync->lrworkerdata[i].lr_ctxt = (void *)lr_ctxt;
972
0
    lr_sync->lrworkerdata[i].do_extend_border = do_extend_border;
973
0
    worker->hook = loop_restoration_row_worker;
974
0
    worker->data1 = lr_sync;
975
0
    worker->data2 = &lr_sync->lrworkerdata[i];
976
977
    // Start loop restoration
978
0
    worker->had_error = 0;
979
0
    if (i == 0) {
980
0
      winterface->execute(worker);
981
0
    } else {
982
0
      winterface->launch(worker);
983
0
    }
984
0
  }
985
986
0
  sync_lr_workers(workers, cm, num_workers);
987
0
}
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
0
                                          int do_extend_border) {
994
0
  assert(!cm->features.all_lossless);
995
996
0
  const int num_planes = av1_num_planes(cm);
997
998
0
  AV1LrStruct *loop_rest_ctxt = (AV1LrStruct *)lr_ctxt;
999
1000
0
  av1_loop_restoration_filter_frame_init(loop_rest_ctxt, frame, cm,
1001
0
                                         optimized_lr, num_planes);
1002
1003
0
  foreach_rest_unit_in_planes_mt(loop_rest_ctxt, workers, num_workers, lr_sync,
1004
0
                                 cm, do_extend_border);
1005
0
}
1006
#endif  // !CONFIG_REALTIME_ONLY || CONFIG_AV1_DECODER
1007
1008
// Initializes cdef_sync parameters.
1009
0
static inline void reset_cdef_job_info(AV1CdefSync *const cdef_sync) {
1010
0
  cdef_sync->end_of_frame = 0;
1011
0
  cdef_sync->fbr = 0;
1012
0
  cdef_sync->fbc = 0;
1013
0
  cdef_sync->cdef_mt_exit = false;
1014
0
}
1015
1016
static inline void launch_cdef_workers(AVxWorker *const workers,
1017
0
                                       int num_workers) {
1018
0
  const AVxWorkerInterface *const winterface = aom_get_worker_interface();
1019
0
  for (int i = num_workers - 1; i >= 0; i--) {
1020
0
    AVxWorker *const worker = &workers[i];
1021
0
    worker->had_error = 0;
1022
0
    if (i == 0)
1023
0
      winterface->execute(worker);
1024
0
    else
1025
0
      winterface->launch(worker);
1026
0
  }
1027
0
}
1028
1029
static inline void sync_cdef_workers(AVxWorker *const workers,
1030
0
                                     AV1_COMMON *const cm, int num_workers) {
1031
0
  const AVxWorkerInterface *const winterface = aom_get_worker_interface();
1032
0
  int had_error = workers[0].had_error;
1033
0
  struct aom_internal_error_info error_info;
1034
1035
  // Read the error_info of main thread.
1036
0
  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
0
  for (int i = num_workers - 1; i > 0; --i) {
1043
0
    AVxWorker *const worker = &workers[i];
1044
0
    if (!winterface->sync(worker)) {
1045
0
      had_error = 1;
1046
0
      error_info = ((AV1CdefWorkerData *)worker->data2)->error_info;
1047
0
    }
1048
0
  }
1049
0
  if (had_error) aom_internal_error_copy(cm->error, &error_info);
1050
0
}
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
0
                                          const int nvfb) {
1056
0
  cdef_sync->fbr++;
1057
0
  if (cdef_sync->fbr == nvfb) {
1058
0
    cdef_sync->end_of_frame = 1;
1059
0
  }
1060
0
}
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
0
                                        volatile int *cur_fbr, const int nvfb) {
1066
0
#if CONFIG_MULTITHREAD
1067
0
  pthread_mutex_lock(cdef_sync->mutex_);
1068
0
#endif  // CONFIG_MULTITHREAD
1069
0
  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
0
  if (!cdef_sync->cdef_mt_exit && cdef_sync->end_of_frame == 0) {
1073
0
    do_next_row = 1;
1074
0
    *cur_fbr = cdef_sync->fbr;
1075
0
    update_cdef_row_next_job_info(cdef_sync, nvfb);
1076
0
  }
1077
0
#if CONFIG_MULTITHREAD
1078
0
  pthread_mutex_unlock(cdef_sync->mutex_);
1079
0
#endif  // CONFIG_MULTITHREAD
1080
0
  return do_next_row;
1081
0
}
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
0
static int cdef_sb_row_worker_hook(void *arg1, void *arg2) {
1089
0
  AV1CdefSync *const cdef_sync = (AV1CdefSync *)arg1;
1090
0
  AV1CdefWorkerData *const cdef_worker = (AV1CdefWorkerData *)arg2;
1091
0
  AV1_COMMON *cm = cdef_worker->cm;
1092
0
  const int nvfb = (cm->mi_params.mi_rows + MI_SIZE_64X64 - 1) / MI_SIZE_64X64;
1093
1094
0
#if CONFIG_MULTITHREAD
1095
0
  pthread_mutex_t *job_mutex_ = cdef_sync->mutex_;
1096
0
#endif
1097
0
  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
0
  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
0
  error_info->setjmp = 1;
1118
1119
0
  volatile int cur_fbr;
1120
0
  const int num_planes = av1_num_planes(cm);
1121
0
  while (get_cdef_row_next_job(cdef_sync, &cur_fbr, nvfb)) {
1122
0
    MACROBLOCKD *xd = cdef_worker->xd;
1123
0
    av1_cdef_fb_row(cm, xd, cdef_worker->linebuf, cdef_worker->colbuf,
1124
0
                    cdef_worker->srcbuf, cur_fbr,
1125
0
                    cdef_worker->cdef_init_fb_row_fn, cdef_sync, error_info);
1126
0
    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
0
  }
1139
0
  error_info->setjmp = 0;
1140
0
  return 1;
1141
0
}
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
0
    int do_extend_border) {
1149
0
  const int num_planes = av1_num_planes(cm);
1150
1151
0
  cdef_worker[0].srcbuf = cm->cdef_info.srcbuf;
1152
0
  for (int plane = 0; plane < num_planes; plane++)
1153
0
    cdef_worker[0].colbuf[plane] = cm->cdef_info.colbuf[plane];
1154
0
  for (int i = num_workers - 1; i >= 0; i--) {
1155
0
    AVxWorker *const worker = &workers[i];
1156
0
    cdef_worker[i].cm = cm;
1157
0
    cdef_worker[i].xd = xd;
1158
0
    cdef_worker[i].cdef_init_fb_row_fn = cdef_init_fb_row_fn;
1159
0
    cdef_worker[i].do_extend_border = do_extend_border;
1160
0
    for (int plane = 0; plane < num_planes; plane++)
1161
0
      cdef_worker[i].linebuf[plane] = cm->cdef_info.linebuf[plane];
1162
1163
0
    worker->hook = hook;
1164
0
    worker->data1 = cdef_sync;
1165
0
    worker->data2 = &cdef_worker[i];
1166
0
  }
1167
0
}
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
0
                             struct AV1CdefSyncData *const cdef_sync, int fbr) {
1175
0
  const int num_planes = av1_num_planes(cm);
1176
0
  const int nvfb = (cm->mi_params.mi_rows + MI_SIZE_64X64 - 1) / MI_SIZE_64X64;
1177
0
  const int luma_stride =
1178
0
      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
0
  fb_info->frame_boundary[TOP] = (MI_SIZE_64X64 * fbr == 0) ? 1 : 0;
1190
0
  if (fbr != nvfb - 1)
1191
0
    fb_info->frame_boundary[BOTTOM] =
1192
0
        (MI_SIZE_64X64 * (fbr + 1) == cm->mi_params.mi_rows) ? 1 : 0;
1193
0
  else
1194
0
    fb_info->frame_boundary[BOTTOM] = 1;
1195
1196
0
  fb_info->src = src;
1197
0
  fb_info->damping = cm->cdef_info.cdef_damping;
1198
0
  fb_info->coeff_shift = AOMMAX(cm->seq_params->bit_depth - 8, 0);
1199
0
  av1_zero(fb_info->dir);
1200
0
  av1_zero(fb_info->var);
1201
1202
0
  for (int plane = 0; plane < num_planes; plane++) {
1203
0
    const int stride = luma_stride >> xd->plane[plane].subsampling_x;
1204
0
    uint16_t *top_linebuf = &linebuf[plane][0];
1205
0
    uint16_t *bot_linebuf = &linebuf[plane][nvfb * CDEF_VBORDER * stride];
1206
0
    {
1207
0
      const int mi_high_l2 = MI_SIZE_LOG2 - xd->plane[plane].subsampling_y;
1208
0
      const int top_offset = MI_SIZE_64X64 * (fbr + 1) << mi_high_l2;
1209
0
      const int bot_offset = MI_SIZE_64X64 * (fbr + 1) << mi_high_l2;
1210
1211
0
      if (fbr != nvfb - 1)  // if (fbr != 0)  // top line buffer copy
1212
0
        av1_cdef_copy_sb8_16(
1213
0
            cm, &top_linebuf[(fbr + 1) * CDEF_VBORDER * stride], stride,
1214
0
            xd->plane[plane].dst.buf, top_offset - CDEF_VBORDER, 0,
1215
0
            xd->plane[plane].dst.stride, CDEF_VBORDER, stride);
1216
0
      if (fbr != nvfb - 1)  // bottom line buffer copy
1217
0
        av1_cdef_copy_sb8_16(cm, &bot_linebuf[fbr * CDEF_VBORDER * stride],
1218
0
                             stride, xd->plane[plane].dst.buf, bot_offset, 0,
1219
0
                             xd->plane[plane].dst.stride, CDEF_VBORDER, stride);
1220
0
    }
1221
1222
0
    fb_info->top_linebuf[plane] = &linebuf[plane][fbr * CDEF_VBORDER * stride];
1223
0
    fb_info->bot_linebuf[plane] =
1224
0
        &linebuf[plane]
1225
0
                [nvfb * CDEF_VBORDER * stride + (fbr * CDEF_VBORDER * stride)];
1226
0
  }
1227
1228
0
  cdef_row_mt_sync_write(cdef_sync, fbr);
1229
0
  cdef_row_mt_sync_read(cdef_sync, fbr);
1230
0
}
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
0
                       int do_extend_border) {
1245
0
  YV12_BUFFER_CONFIG *frame = &cm->cur_frame->buf;
1246
0
  const int num_planes = av1_num_planes(cm);
1247
1248
0
  av1_setup_dst_planes(xd->plane, cm->seq_params->sb_size, frame, 0, 0, 0,
1249
0
                       num_planes);
1250
1251
0
  reset_cdef_job_info(cdef_sync);
1252
0
  prepare_cdef_frame_workers(cm, xd, cdef_worker, cdef_sb_row_worker_hook,
1253
0
                             workers, cdef_sync, num_workers,
1254
0
                             cdef_init_fb_row_fn, do_extend_border);
1255
0
  launch_cdef_workers(workers, num_workers);
1256
0
  sync_cdef_workers(workers, cm, num_workers);
1257
0
}
1258
1259
0
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
0
  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
0
  return cm->seq_params->sb_size == BLOCK_128X128 ? 2 : 4;
1268
0
}