FFmpeg
thread.c
Go to the documentation of this file.
1 /*
2  * VVC thread logic
3  *
4  * Copyright (C) 2023 Nuo Mi
5  *
6  * This file is part of FFmpeg.
7  *
8  * FFmpeg is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * FFmpeg is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with FFmpeg; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 #include <stdatomic.h>
24 
25 #include "libavutil/executor.h"
26 #include "libavutil/mem.h"
27 #include "libavutil/thread.h"
28 
29 #include "thread.h"
30 #include "ctu.h"
31 #include "filter.h"
32 #include "inter.h"
33 #include "intra.h"
34 #include "refs.h"
35 
36 typedef struct ProgressListener {
38  struct VVCTask *task;
41 
42 typedef enum VVCTaskStage {
43  VVC_TASK_STAGE_INIT, // for CTU(0, 0) only
53 } VVCTaskStage;
54 
55 typedef struct VVCTask {
56  union {
57  struct VVCTask *next; //for executor debug only
59  } u;
60 
62 
63  // ctu x, y, and raster scan order
64  int rx, ry, rs;
66 
69 
70  // for parse task only
73  int ctu_idx; //ctu idx in the current slice
74 
75  // tasks with target scores met are ready for scheduling
78 } VVCTask;
79 
80 typedef struct VVCRowThread {
82 } VVCRowThread;
83 
84 typedef struct VVCFrameThread {
85  // error return for tasks
87 
90 
91  int ctu_size;
92  int ctu_width;
94  int ctu_count;
95 
96  //protected by lock
99 
101 
105 
106 static void add_task(VVCContext *s, VVCTask *t)
107 {
108  VVCFrameThread *ft = t->fc->ft;
109 
111 
112  av_executor_execute(s->executor, &t->u.task);
113 }
114 
115 static void task_init(VVCTask *t, VVCTaskStage stage, VVCFrameContext *fc, const int rx, const int ry)
116 {
117  memset(t, 0, sizeof(*t));
118  t->stage = stage;
119  t->fc = fc;
120  t->rx = rx;
121  t->ry = ry;
122  t->rs = ry * fc->ft->ctu_width + rx;
123  for (int i = 0; i < FF_ARRAY_ELEMS(t->score); i++)
124  atomic_store(t->score + i, 0);
126 }
127 
128 static int task_init_parse(VVCTask *t, SliceContext *sc, EntryPoint *ep, const int ctu_idx)
129 {
130  if (t->sc) {
131  // the task already inited, error bitstream
132  return AVERROR_INVALIDDATA;
133  }
134  t->sc = sc;
135  t->ep = ep;
136  t->ctu_idx = ctu_idx;
137 
138  return 0;
139 }
140 
141 static uint8_t task_add_score(VVCTask *t, const VVCTaskStage stage)
142 {
143  return atomic_fetch_add(&t->score[stage], 1) + 1;
144 }
145 
146 static uint8_t task_get_score(VVCTask *t, const VVCTaskStage stage)
147 {
148  return atomic_load(&t->score[stage]);
149 }
150 
151 //first row in tile or slice
152 static int is_first_row(const VVCFrameContext *fc, const int rx, const int ry)
153 {
154  const VVCFrameThread *ft = fc->ft;
155  const VVCPPS *pps = fc->ps.pps;
156 
157  if (ry != pps->ctb_to_row_bd[ry]) {
158  const int rs = ry * ft->ctu_width + rx;
159  return fc->tab.slice_idx[rs] != fc->tab.slice_idx[rs - ft->ctu_width];
160  }
161  return 1;
162 }
163 
164 static int task_has_target_score(VVCTask *t, const VVCTaskStage stage, const uint8_t score)
165 {
166  // l:left, r:right, t: top, b: bottom
167  static const uint8_t target_score[] =
168  {
169  2, //VVC_TASK_STAGE_RECON, need l + rt recon
170  3, //VVC_TASK_STAGE_LMCS, need r + b + rb recon
171  1, //VVC_TASK_STAGE_DEBLOCK_V, need l deblock v
172  2, //VVC_TASK_STAGE_DEBLOCK_H, need r deblock v + t deblock h
173  5, //VVC_TASK_STAGE_SAO, need l + r + lb + b + rb deblock h
174  8, //VVC_TASK_STAGE_ALF, need sao around the ctu
175  };
176  uint8_t target = 0;
177  VVCFrameContext *fc = t->fc;
178 
179  if (stage == VVC_TASK_STAGE_INIT)
180  return 1;
181 
182  if (stage == VVC_TASK_STAGE_PARSE) {
183  const H266RawSPS *rsps = fc->ps.sps->r;
184  const int wpp = rsps->sps_entropy_coding_sync_enabled_flag && !is_first_row(fc, t->rx, t->ry);
185  const int no_prev_stage = t->rs > 0;
186  target = 2 + wpp - no_prev_stage; //left parse + colocation + wpp - no_prev_stage
187  } else if (stage == VVC_TASK_STAGE_INTER) {
188  target = atomic_load(&t->target_inter_score);
189  } else {
190  target = target_score[stage - VVC_TASK_STAGE_RECON];
191  }
192 
193  //+1 for previous stage
194  av_assert0(score <= target + 1);
195  return score == target + 1;
196 }
197 
199  const int rx, const int ry, const VVCTaskStage stage)
200 {
201  VVCTask *t = ft->tasks + ft->ctu_width * ry + rx;
202  uint8_t score;
203 
204  if (rx < 0 || rx >= ft->ctu_width || ry < 0 || ry >= ft->ctu_height)
205  return;
206 
207  score = task_add_score(t, stage);
208  if (task_has_target_score(t, stage, score)) {
209  av_assert0(s);
210  av_assert0(stage == t->stage);
211  add_task(s, t);
212  }
213 }
214 
215 static void sheduled_done(VVCFrameThread *ft, atomic_int *scheduled)
216 {
217  if (atomic_fetch_sub(scheduled, 1) == 1) {
218  ff_mutex_lock(&ft->lock);
219  ff_cond_signal(&ft->cond);
220  ff_mutex_unlock(&ft->lock);
221  }
222 }
223 
224 static void progress_done(VVCProgressListener *_l, const int type)
225 {
226  const ProgressListener *l = (ProgressListener *)_l;
227  const VVCTask *t = l->task;
228  VVCFrameThread *ft = t->fc->ft;
229 
230  frame_thread_add_score(l->s, ft, t->rx, t->ry, type);
232 }
233 
235 {
237 }
238 
240 {
242 }
243 
244 static void listener_init(ProgressListener *l, VVCTask *t, VVCContext *s, const VVCProgress vp, const int y)
245 {
246  const int is_inter = vp == VVC_PROGRESS_PIXEL;
247 
248  l->task = t;
249  l->s = s;
250  l->l.vp = vp;
251  l->l.y = y;
252  l->l.progress_done = is_inter ? pixel_done : mv_done;
253  if (is_inter)
255 }
256 
258  VVCTask *t, VVCContext *s, const VVCProgress vp, const int y)
259 {
260  VVCFrameThread *ft = t->fc->ft;
261 
263  listener_init(l, t, s, vp, y);
265 }
266 
268 {
269  VVCFrameThread *ft = fc->ft;
270  EntryPoint *ep = t->ep;
271  const VVCSPS *sps = fc->ps.sps;
272 
273  if (sps->r->sps_entropy_coding_sync_enabled_flag) {
274  if (t->rx == fc->ps.pps->ctb_to_col_bd[t->rx]) {
275  EntryPoint *next = ep + 1;
276  if (next < sc->eps + sc->nb_eps && !is_first_row(fc, t->rx, t->ry + 1)) {
277  memcpy(next->cabac_state, ep->cabac_state, sizeof(next->cabac_state));
278  ff_vvc_ep_init_stat_coeff(next, sps->bit_depth, sps->r->sps_persistent_rice_adaptation_enabled_flag);
279  }
280  }
281  if (t->ry + 1 < ft->ctu_height && !is_first_row(fc, t->rx, t->ry + 1))
283  }
284 
285  if (t->ctu_idx + 1 < t->ep->ctu_end) {
286  const int next_rs = sc->sh.ctb_addr_in_curr_slice[t->ctu_idx + 1];
287  const int next_rx = next_rs % ft->ctu_width;
288  const int next_ry = next_rs / ft->ctu_width;
289  frame_thread_add_score(s, ft, next_rx, next_ry, VVC_TASK_STAGE_PARSE);
290  }
291 }
292 
293 static void schedule_inter(VVCContext *s, VVCFrameContext *fc, const SliceContext *sc, VVCTask *t, const int rs)
294 {
295  const VVCSH *sh = &sc->sh;
296 
297  if (!IS_I(sh->r)) {
298  CTU *ctu = fc->tab.ctus + rs;
299  for (int lx = 0; lx < 2; lx++) {
300  for (int i = 0; i < sh->r->num_ref_idx_active[lx]; i++) {
301  int y = ctu->max_y[lx][i];
302  VVCRefPic *refp = sc->rpl[lx].refs + i;
303  VVCFrame *ref = refp->ref;
304  if (ref && y >= 0) {
305  if (refp->is_scaled)
306  y = y * refp->scale[1] >> 14;
308  }
309  }
310  }
311  }
312 }
313 
314 static void parse_task_done(VVCContext *s, VVCFrameContext *fc, const int rx, const int ry)
315 {
316  VVCFrameThread *ft = fc->ft;
317  const int rs = ry * ft->ctu_width + rx;
318  const int slice_idx = fc->tab.slice_idx[rs];
319  VVCTask *t = ft->tasks + rs;
320  const SliceContext *sc = fc->slices[slice_idx];
321 
322  schedule_next_parse(s, fc, sc, t);
323  schedule_inter(s, fc, sc, t, rs);
324 }
325 
326 static void task_stage_done(const VVCTask *t, VVCContext *s)
327 {
328  VVCFrameContext *fc = t->fc;
329  VVCFrameThread *ft = fc->ft;
330  const VVCTaskStage stage = t->stage;
331 
332 #define ADD(dx, dy, stage) frame_thread_add_score(s, ft, t->rx + (dx), t->ry + (dy), stage)
333 
334  //this is a reserve map of ready_score, ordered by zigzag
335  if (stage == VVC_TASK_STAGE_PARSE) {
336  parse_task_done(s, fc, t->rx, t->ry);
337  } else if (stage == VVC_TASK_STAGE_RECON) {
338  ADD(-1, 1, VVC_TASK_STAGE_RECON);
339  ADD( 1, 0, VVC_TASK_STAGE_RECON);
340  ADD(-1, -1, VVC_TASK_STAGE_LMCS);
341  ADD( 0, -1, VVC_TASK_STAGE_LMCS);
342  ADD(-1, 0, VVC_TASK_STAGE_LMCS);
343  } else if (stage == VVC_TASK_STAGE_DEBLOCK_V) {
346  } else if (stage == VVC_TASK_STAGE_DEBLOCK_H) {
348  ADD(-1, -1, VVC_TASK_STAGE_SAO);
349  ADD( 0, -1, VVC_TASK_STAGE_SAO);
350  ADD(-1, 0, VVC_TASK_STAGE_SAO);
351  ADD( 1, -1, VVC_TASK_STAGE_SAO);
352  ADD( 1, 0, VVC_TASK_STAGE_SAO);
353  } else if (stage == VVC_TASK_STAGE_SAO) {
354  ADD(-1, -1, VVC_TASK_STAGE_ALF);
355  ADD( 0, -1, VVC_TASK_STAGE_ALF);
356  ADD(-1, 0, VVC_TASK_STAGE_ALF);
357  ADD( 1, -1, VVC_TASK_STAGE_ALF);
358  ADD(-1, 1, VVC_TASK_STAGE_ALF);
359  ADD( 1, 0, VVC_TASK_STAGE_ALF);
360  ADD( 0, 1, VVC_TASK_STAGE_ALF);
361  ADD( 1, 1, VVC_TASK_STAGE_ALF);
362  }
363 }
364 
365 static int task_is_stage_ready(VVCTask *t, int add)
366 {
367  const VVCTaskStage stage = t->stage;
368  uint8_t score;
369  if (stage > VVC_TASK_STAGE_ALF)
370  return 0;
371  score = task_get_score(t, stage) + add;
372  return task_has_target_score(t, stage, score);
373 }
374 
375 static int task_ready(const AVTask *_t, void *user_data)
376 {
377  VVCTask *t = (VVCTask*)_t;
378 
379  return task_is_stage_ready(t, 0);
380 }
381 
382 #define CHECK(a, b) \
383  do { \
384  if ((a) != (b)) \
385  return (a) < (b); \
386  } while (0)
387 
388 static int task_priority_higher(const AVTask *_a, const AVTask *_b)
389 {
390  const VVCTask *a = (const VVCTask*)_a;
391  const VVCTask *b = (const VVCTask*)_b;
392 
393 
394  if (a->stage <= VVC_TASK_STAGE_PARSE || b->stage <= VVC_TASK_STAGE_PARSE) {
395  CHECK(a->stage, b->stage);
396  CHECK(a->fc->decode_order, b->fc->decode_order); //decode order
397  CHECK(a->ry, b->ry);
398  return a->rx < b->rx;
399  }
400 
401  CHECK(a->fc->decode_order, b->fc->decode_order); //decode order
402  CHECK(a->rx + a->ry + a->stage, b->rx + b->ry + b->stage); //zigzag with type
403  CHECK(a->rx + a->ry, b->rx + b->ry); //zigzag
404  return a->ry < b->ry;
405 }
406 
408 {
409  const VVCFrameContext *fc = t->fc;
410 
411  if (fc->ps.ph.r->ph_temporal_mvp_enabled_flag || fc->ps.sps->r->sps_sbtmvp_enabled_flag) {
412  VVCFrame *col = fc->ref->collocated_ref;
413  const int first_col = t->rx == fc->ps.pps->ctb_to_col_bd[t->rx];
414  if (col && first_col) {
415  //we depend on bottom and right boundary, do not - 1 for y
416  const int y = (t->ry << fc->ps.sps->ctb_log2_size_y);
418  return;
419  }
420  }
422 }
423 
425 {
426  const int rs = sc->sh.ctb_addr_in_curr_slice[ep->ctu_start];
427  VVCTask *t = ft->tasks + rs;
428 
430 }
431 
433 {
434  VVCFrameContext *fc = lc->fc;
435  VVCFrameThread *ft = fc->ft;
436  const int ret = ff_vvc_per_frame_init(fc);
437 
438  if (ret < 0)
439  return ret;
440 
441  for (int i = 0; i < fc->nb_slices; i++) {
442  SliceContext *sc = fc->slices[i];
443  for (int j = 0; j < sc->nb_eps; j++) {
444  EntryPoint *ep = sc->eps + j;
445  for (int k = ep->ctu_start; k < ep->ctu_end; k++) {
446  const int rs = sc->sh.ctb_addr_in_curr_slice[k];
447  VVCTask *t = ft->tasks + rs;
448  check_colocation(s, t);
449  }
450  submit_entry_point(s, ft, sc, ep);
451  }
452  }
453  return 0;
454 }
455 
457  const int ry, const VVCProgress idx)
458 {
459  VVCFrameThread *ft = fc->ft;
460  const int ctu_size = ft->ctu_size;
461  int old;
462 
463  if (atomic_fetch_add(&ft->rows[ry].col_progress[idx], 1) == ft->ctu_width - 1) {
464  int y;
465  ff_mutex_lock(&ft->lock);
466  y = old = ft->row_progress[idx];
467  while (y < ft->ctu_height && atomic_load(&ft->rows[y].col_progress[idx]) == ft->ctu_width)
468  y++;
469  if (old != y)
470  ft->row_progress[idx] = y;
471  // ff_vvc_report_progress will acquire other frames' locks, which could lead to a deadlock
472  // We need to unlock ft->lock first
473  ff_mutex_unlock(&ft->lock);
474 
475  if (old != y) {
476  const int progress = y == ft->ctu_height ? INT_MAX : y * ctu_size;
477  ff_vvc_report_progress(fc->ref, idx, progress);
478  }
479  }
480 }
481 
483 {
484  int ret;
485  VVCFrameContext *fc = lc->fc;
486  const int rs = t->rs;
487  const CTU *ctu = fc->tab.ctus + rs;
488 
489  lc->ep = t->ep;
490 
491  ret = ff_vvc_coding_tree_unit(lc, t->ctu_idx, rs, t->rx, t->ry);
492  if (ret < 0)
493  return ret;
494 
495  if (!ctu->has_dmvr)
497 
498  return 0;
499 }
500 
502 {
503  VVCFrameContext *fc = lc->fc;
504  const CTU *ctu = fc->tab.ctus + t->rs;
505  int ret;
506 
507  ret = ff_vvc_predict_inter(lc, t->rs);
508  if (ret < 0)
509  return ret;
510 
511  if (ctu->has_dmvr)
513 
514  return 0;
515 }
516 
518 {
519  return ff_vvc_reconstruct(lc, t->rs, t->rx, t->ry);
520 }
521 
523 {
524  VVCFrameContext *fc = lc->fc;
525  VVCFrameThread *ft = fc->ft;
526  const int ctu_size = ft->ctu_size;
527  const int x0 = t->rx * ctu_size;
528  const int y0 = t->ry * ctu_size;
529 
530  ff_vvc_lmcs_filter(lc, x0, y0);
531 
532  return 0;
533 }
534 
536 {
537  VVCFrameContext *fc = lc->fc;
538  VVCFrameThread *ft = fc->ft;
539  const int ctb_size = ft->ctu_size;
540  const int x0 = t->rx * ctb_size;
541  const int y0 = t->ry * ctb_size;
542 
544  ff_vvc_decode_neighbour(lc, x0, y0, t->rx, t->ry, t->rs);
545  ff_vvc_deblock_vertical(lc, x0, y0, t->rs);
546  }
547 
548  return 0;
549 }
550 
552 {
553  VVCFrameContext *fc = lc->fc;
554  VVCFrameThread *ft = fc->ft;
555  const int ctb_size = ft->ctu_size;
556  const int x0 = t->rx * ctb_size;
557  const int y0 = t->ry * ctb_size;
558 
560  ff_vvc_decode_neighbour(lc, x0, y0, t->rx, t->ry, t->rs);
561  ff_vvc_deblock_horizontal(lc, x0, y0, t->rs);
562  }
563  if (fc->ps.sps->r->sps_sao_enabled_flag)
564  ff_vvc_sao_copy_ctb_to_hv(lc, t->rx, t->ry, t->ry == ft->ctu_height - 1);
565 
566  return 0;
567 }
568 
570 {
571  VVCFrameContext *fc = lc->fc;
572  VVCFrameThread *ft = fc->ft;
573  const int ctb_size = ft->ctu_size;
574  const int x0 = t->rx * ctb_size;
575  const int y0 = t->ry * ctb_size;
576 
577  if (fc->ps.sps->r->sps_sao_enabled_flag) {
578  ff_vvc_decode_neighbour(lc, x0, y0, t->rx, t->ry, t->rs);
579  ff_vvc_sao_filter(lc, x0, y0);
580  }
581 
582  if (fc->ps.sps->r->sps_alf_enabled_flag)
583  ff_vvc_alf_copy_ctu_to_hv(lc, x0, y0);
584 
585  return 0;
586 }
587 
589 {
590  VVCFrameContext *fc = lc->fc;
591  VVCFrameThread *ft = fc->ft;
592  const int ctu_size = ft->ctu_size;
593  const int x0 = t->rx * ctu_size;
594  const int y0 = t->ry * ctu_size;
595 
596  if (fc->ps.sps->r->sps_alf_enabled_flag) {
597  ff_vvc_decode_neighbour(lc, x0, y0, t->rx, t->ry, t->rs);
598  ff_vvc_alf_filter(lc, x0, y0);
599  }
601 
602  return 0;
603 }
604 
605 #define VVC_THREAD_DEBUG
606 #ifdef VVC_THREAD_DEBUG
607 const static char* task_name[] = {
608  "INIT",
609  "P",
610  "I",
611  "R",
612  "L",
613  "V",
614  "H",
615  "S",
616  "A"
617 };
618 #endif
619 
620 typedef int (*run_func)(VVCContext *s, VVCLocalContext *lc, VVCTask *t);
621 
623 {
624  int ret;
625  VVCFrameContext *fc = t->fc;
626  VVCFrameThread *ft = fc->ft;
627  const VVCTaskStage stage = t->stage;
628  static const run_func run[] = {
629  run_init,
630  run_parse,
631  run_inter,
632  run_recon,
633  run_lmcs,
636  run_sao,
637  run_alf,
638  };
639 
640 #ifdef VVC_THREAD_DEBUG
641  av_log(s->avctx, AV_LOG_DEBUG, "frame %5d, %s(%3d, %3d)\r\n", (int)t->fc->decode_order, task_name[stage], t->rx, t->ry);
642 #endif
643 
644  lc->sc = t->sc;
645 
646  if (!atomic_load(&ft->ret)) {
647  if ((ret = run[stage](s, lc, t)) < 0) {
648 #ifdef COMPAT_ATOMICS_WIN32_STDATOMIC_H
649  intptr_t zero = 0;
650 #else
651  int zero = 0;
652 #endif
654  av_log(s->avctx, AV_LOG_ERROR,
655  "frame %5d, %s(%3d, %3d) failed with %d\r\n",
656  (int)fc->decode_order, task_name[stage], t->rx, t->ry, ret);
657  }
658  }
659 
660  task_stage_done(t, s);
661  return;
662 }
663 
664 static int task_run(AVTask *_t, void *local_context, void *user_data)
665 {
666  VVCTask *t = (VVCTask*)_t;
668  VVCLocalContext *lc = local_context;
669  VVCFrameThread *ft = t->fc->ft;
670 
671  lc->fc = t->fc;
672 
673  do {
674  task_run_stage(t, s, lc);
675  t->stage++;
676  } while (task_is_stage_ready(t, 1));
677 
678  if (t->stage != VVC_TASK_STAGE_LAST)
679  frame_thread_add_score(s, ft, t->rx, t->ry, t->stage);
680 
682 
683  return 0;
684 }
685 
686 AVExecutor* ff_vvc_executor_alloc(VVCContext *s, const int thread_count)
687 {
689  s,
690  sizeof(VVCLocalContext),
692  task_ready,
693  task_run,
694  };
695  return av_executor_alloc(&callbacks, thread_count);
696 }
697 
699 {
700  av_executor_free(e);
701 }
702 
704 {
705  VVCFrameThread *ft = fc->ft;
706 
707  if (!ft)
708  return;
709 
710  ff_mutex_destroy(&ft->lock);
711  ff_cond_destroy(&ft->cond);
712  av_freep(&ft->rows);
713  av_freep(&ft->tasks);
714  av_freep(&ft);
715 }
716 
718 {
719  const VVCFrameThread *ft = fc->ft;
720  VVCTask task;
721 
722  task_init(&task, VVC_TASK_STAGE_RECON, fc, 0, 0);
723 
724  for (int i = VVC_TASK_STAGE_RECON; i < VVC_TASK_STAGE_LAST; i++) {
725  task.stage = i;
726 
727  for (task.rx = -1; task.rx <= ft->ctu_width; task.rx++) {
728  task.ry = -1; //top
729  task_stage_done(&task, NULL);
730  task.ry = ft->ctu_height; //bottom
731  task_stage_done(&task, NULL);
732  }
733 
734  for (task.ry = 0; task.ry < ft->ctu_height; task.ry++) {
735  task.rx = -1; //left
736  task_stage_done(&task, NULL);
737  task.rx = ft->ctu_width; //right
738  task_stage_done(&task, NULL);
739  }
740  }
741 }
742 
744 {
745  const VVCSPS *sps = fc->ps.sps;
746  const VVCPPS *pps = fc->ps.pps;
747  VVCFrameThread *ft = fc->ft;
748  int ret;
749 
750  if (!ft || ft->ctu_width != pps->ctb_width ||
751  ft->ctu_height != pps->ctb_height ||
752  ft->ctu_size != sps->ctb_size_y) {
753 
755  ft = av_calloc(1, sizeof(*fc->ft));
756  if (!ft)
757  return AVERROR(ENOMEM);
758 
759  ft->ctu_width = fc->ps.pps->ctb_width;
760  ft->ctu_height = fc->ps.pps->ctb_height;
761  ft->ctu_count = fc->ps.pps->ctb_count;
762  ft->ctu_size = fc->ps.sps->ctb_size_y;
763 
764  ft->rows = av_calloc(ft->ctu_height, sizeof(*ft->rows));
765  if (!ft->rows)
766  goto fail;
767 
768  ft->tasks = av_malloc(ft->ctu_count * sizeof(*ft->tasks));
769  if (!ft->tasks)
770  goto fail;
771 
772  if ((ret = ff_cond_init(&ft->cond, NULL)))
773  goto fail;
774 
775  if ((ret = ff_mutex_init(&ft->lock, NULL))) {
776  ff_cond_destroy(&ft->cond);
777  goto fail;
778  }
779  }
780  fc->ft = ft;
781  ft->ret = 0;
782  for (int y = 0; y < ft->ctu_height; y++) {
783  VVCRowThread *row = ft->rows + y;
784  memset(row->col_progress, 0, sizeof(row->col_progress));
785  }
786 
787  for (int rs = 0; rs < ft->ctu_count; rs++) {
788  VVCTask *t = ft->tasks + rs;
790  }
791 
792  memset(&ft->row_progress[0], 0, sizeof(ft->row_progress));
793 
795 
796  return 0;
797 
798 fail:
799  if (ft) {
800  av_freep(&ft->rows);
801  av_freep(&ft->tasks);
802  av_freep(&ft);
803  }
804 
805  return AVERROR(ENOMEM);
806 }
807 
809 {
810  VVCFrameThread *ft = fc->ft;
811 
812  for (int i = 0; i < fc->nb_slices; i++) {
813  SliceContext *sc = fc->slices[i];
814  for (int j = 0; j < sc->nb_eps; j++) {
815  EntryPoint *ep = sc->eps + j;
816  for (int k = ep->ctu_start; k < ep->ctu_end; k++) {
817  const int rs = sc->sh.ctb_addr_in_curr_slice[k];
818  VVCTask *t = ft->tasks + rs;
819  const int ret = task_init_parse(t, sc, ep, k);
820  if (ret < 0)
821  return ret;
822  }
823  }
824  }
826 
827  return 0;
828 }
829 
831 {
832  VVCFrameThread *ft = fc->ft;
833 
834  ff_mutex_lock(&ft->lock);
835 
837  ff_cond_wait(&ft->cond, &ft->lock);
838 
839  ff_mutex_unlock(&ft->lock);
841 
842 #ifdef VVC_THREAD_DEBUG
843  av_log(s->avctx, AV_LOG_DEBUG, "frame %5d done\r\n", (int)fc->decode_order);
844 #endif
845  return ft->ret;
846 }
ff_vvc_reconstruct
int ff_vvc_reconstruct(VVCLocalContext *lc, const int rs, const int rx, const int ry)
reconstruct a CTU
Definition: intra.c:661
VVCSPS
Definition: ps.h:58
run_deblock_h
static int run_deblock_h(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: thread.c:551
ff_mutex_init
static int ff_mutex_init(AVMutex *mutex, const void *attr)
Definition: thread.h:187
VVCPPS
Definition: ps.h:92
parse_task_done
static void parse_task_done(VVCContext *s, VVCFrameContext *fc, const int rx, const int ry)
Definition: thread.c:314
VVCFrameContext::decode_order
uint64_t decode_order
Definition: dec.h:137
atomic_store
#define atomic_store(object, desired)
Definition: stdatomic.h:85
VVC_TASK_STAGE_LAST
@ VVC_TASK_STAGE_LAST
Definition: thread.c:52
ff_vvc_sao_filter
void ff_vvc_sao_filter(VVCLocalContext *lc, int x0, int y0)
sao filter for the CTU
Definition: filter.c:314
VVC_PROGRESS_PIXEL
@ VVC_PROGRESS_PIXEL
Definition: refs.h:40
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
filter.h
run_sao
static int run_sao(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: thread.c:569
thread.h
ff_vvc_report_frame_finished
void ff_vvc_report_frame_finished(VVCFrame *frame)
Definition: refs.c:545
VVC_MAX_REF_ENTRIES
@ VVC_MAX_REF_ENTRIES
Definition: vvc.h:115
VVCProgressListener::vp
VVCProgress vp
Definition: refs.h:48
listener_init
static void listener_init(ProgressListener *l, VVCTask *t, VVCContext *s, const VVCProgress vp, const int y)
Definition: thread.c:244
frame_thread_init_score
static void frame_thread_init_score(VVCFrameContext *fc)
Definition: thread.c:717
ff_vvc_predict_inter
int ff_vvc_predict_inter(VVCLocalContext *lc, const int rs)
Loop entire CTU to predict all inter coding blocks.
Definition: inter.c:1003
callbacks
static const OMX_CALLBACKTYPE callbacks
Definition: omx.c:340
VVCRefPic
Definition: dec.h:45
VVC_TASK_STAGE_DEBLOCK_V
@ VVC_TASK_STAGE_DEBLOCK_V
Definition: thread.c:48
ff_vvc_deblock_vertical
void ff_vvc_deblock_vertical(const VVCLocalContext *lc, const int x0, const int y0, const int rs)
vertical deblock filter for the CTU
Definition: filter.c:849
VVCLocalContext::sc
SliceContext * sc
Definition: ctu.h:431
run_lmcs
static int run_lmcs(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: thread.c:522
task_init_parse
static int task_init_parse(VVCTask *t, SliceContext *sc, EntryPoint *ep, const int ctu_idx)
Definition: thread.c:128
ff_vvc_alf_copy_ctu_to_hv
void ff_vvc_alf_copy_ctu_to_hv(VVCLocalContext *lc, const int x0, const int y0)
Definition: filter.c:1084
b
#define b
Definition: input.c:41
VVCTask::u
union VVCTask::@286 u
VVCFrameThread::ctu_size
int ctu_size
Definition: thread.c:91
atomic_int
intptr_t atomic_int
Definition: stdatomic.h:55
VVCSH::r
const H266RawSliceHeader * r
RefStruct reference.
Definition: ps.h:238
fc
#define fc(width, name, range_min, range_max)
Definition: cbs_av1.c:472
task_run_stage
static void task_run_stage(VVCTask *t, VVCContext *s, VVCLocalContext *lc)
Definition: thread.c:622
task_ready
static int task_ready(const AVTask *_t, void *user_data)
Definition: thread.c:375
run_parse
static int run_parse(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: thread.c:482
ff_vvc_deblock_horizontal
void ff_vvc_deblock_horizontal(const VVCLocalContext *lc, const int x0, const int y0, const int rs)
horizontal deblock filter for the CTU
Definition: filter.c:854
ff_vvc_report_progress
void ff_vvc_report_progress(VVCFrame *frame, const VVCProgress vp, const int y)
Definition: refs.c:585
add_progress_listener
static void add_progress_listener(VVCFrame *ref, ProgressListener *l, VVCTask *t, VVCContext *s, const VVCProgress vp, const int y)
Definition: thread.c:257
VVCProgress
VVCProgress
Definition: refs.h:38
progress_done
static void progress_done(VVCProgressListener *_l, const int type)
Definition: thread.c:224
VVCRefPic::ref
struct VVCFrame * ref
Definition: dec.h:46
av_malloc
#define av_malloc(s)
Definition: tableprint_vlc.h:30
ff_mutex_unlock
static int ff_mutex_unlock(AVMutex *mutex)
Definition: thread.h:189
AVTaskCallbacks
Definition: executor.h:31
ProgressListener::s
VVCContext * s
Definition: thread.c:39
ff_vvc_coding_tree_unit
int ff_vvc_coding_tree_unit(VVCLocalContext *lc, const int ctu_idx, const int rs, const int rx, const int ry)
parse a CTU
Definition: ctu.c:2455
VVCLocalContext::fc
VVCFrameContext * fc
Definition: ctu.h:432
fail
#define fail()
Definition: checkasm.h:188
VVCRowThread
Definition: thread.c:80
VVCTask::stage
VVCTaskStage stage
Definition: thread.c:61
type
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf type
Definition: writing_filters.txt:86
task_name
const static char * task_name[]
Definition: thread.c:607
SliceContext::rpl
RefPicList * rpl
Definition: dec.h:111
atomic_fetch_sub
#define atomic_fetch_sub(object, operand)
Definition: stdatomic.h:137
ADD
#define ADD(dx, dy, stage)
ff_vvc_frame_submit
int ff_vvc_frame_submit(VVCContext *s, VVCFrameContext *fc)
Definition: thread.c:808
VVCFrameThread::cond
AVCond cond
Definition: thread.c:103
VVCFrameThread
Definition: thread.c:84
H266RawSliceHeader::num_ref_idx_active
uint8_t num_ref_idx_active[2]
NumRefIdxActive[].
Definition: cbs_h266.h:837
av_executor_alloc
AVExecutor * av_executor_alloc(const AVTaskCallbacks *cb, int thread_count)
Alloc executor.
Definition: executor.c:142
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:180
FF_ARRAY_ELEMS
#define FF_ARRAY_ELEMS(a)
Definition: sinewin_tablegen.c:29
VVCFrameThread::nb_scheduled_tasks
atomic_int nb_scheduled_tasks
Definition: thread.c:97
RefPicList::refs
VVCRefPic refs[VVC_MAX_REF_ENTRIES]
Definition: dec.h:56
AVMutex
#define AVMutex
Definition: thread.h:184
ff_vvc_frame_wait
int ff_vvc_frame_wait(VVCContext *s, VVCFrameContext *fc)
Definition: thread.c:830
ff_vvc_executor_free
void ff_vvc_executor_free(AVExecutor **e)
Definition: thread.c:698
s
#define s(width, name)
Definition: cbs_vp9.c:198
EntryPoint::cabac_state
VVCCabacState cabac_state[VVC_CONTEXTS]
Definition: ctu.h:356
LUMA_EXTRA_AFTER
#define LUMA_EXTRA_AFTER
Definition: ctu.h:56
ff_cond_wait
static int ff_cond_wait(AVCond *cond, AVMutex *mutex)
Definition: thread.h:198
av_assert0
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition: avassert.h:40
VVCSH
Definition: ps.h:237
AV_LOG_DEBUG
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition: log.h:201
AVCond
#define AVCond
Definition: thread.h:192
VVCFrameContext::ft
struct VVCFrameThread * ft
Definition: dec.h:135
VVCRowThread::col_progress
atomic_int col_progress[VVC_PROGRESS_LAST]
Definition: thread.c:81
atomic_load
#define atomic_load(object)
Definition: stdatomic.h:93
sheduled_done
static void sheduled_done(VVCFrameThread *ft, atomic_int *scheduled)
Definition: thread.c:215
AVTask
Definition: executor.h:27
H266RawSPS
Definition: cbs_h266.h:308
VVCTask::sc
SliceContext * sc
Definition: thread.c:71
CTU
Definition: ctu.h:331
inter.h
task_run
static int task_run(AVTask *_t, void *local_context, void *user_data)
Definition: thread.c:664
NULL
#define NULL
Definition: coverity.c:32
AVExecutor
Definition: executor.c:51
run_func
int(* run_func)(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: thread.c:620
ff_vvc_frame_thread_init
int ff_vvc_frame_thread_init(VVCFrameContext *fc)
Definition: thread.c:743
task_has_target_score
static int task_has_target_score(VVCTask *t, const VVCTaskStage stage, const uint8_t score)
Definition: thread.c:164
run
uint8_t run
Definition: svq3.c:204
VVCLocalContext
Definition: ctu.h:370
task_priority_higher
static int task_priority_higher(const AVTask *_a, const AVTask *_b)
Definition: thread.c:388
SliceContext::eps
struct EntryPoint * eps
Definition: dec.h:109
VVCTask::rx
int rx
Definition: thread.c:64
VVC_TASK_STAGE_DEBLOCK_H
@ VVC_TASK_STAGE_DEBLOCK_H
Definition: thread.c:49
VVCRefPic::is_scaled
int is_scaled
RprConstraintsActiveFlag.
Definition: dec.h:51
VVCFrameThread::ret
atomic_int ret
Definition: thread.c:86
ProgressListener
Definition: thread.c:36
VVCFrameThread::nb_scheduled_listeners
atomic_int nb_scheduled_listeners
Definition: thread.c:98
ff_vvc_alf_filter
void ff_vvc_alf_filter(VVCLocalContext *lc, const int x0, const int y0)
alf filter for the CTU
Definition: filter.c:1188
SliceContext
Definition: mss12.h:70
ff_mutex_destroy
static int ff_mutex_destroy(AVMutex *mutex)
Definition: thread.h:190
atomic_compare_exchange_strong
#define atomic_compare_exchange_strong(object, expected, desired)
Definition: stdatomic.h:114
VVCFrameThread::rows
VVCRowThread * rows
Definition: thread.c:88
VVCTask::col_listener
ProgressListener col_listener
Definition: thread.c:67
ff_vvc_decode_neighbour
void ff_vvc_decode_neighbour(VVCLocalContext *lc, const int x_ctb, const int y_ctb, const int rx, const int ry, const int rs)
Definition: ctu.c:2486
VVCTask::listener
ProgressListener listener[2][VVC_MAX_REF_ENTRIES]
Definition: thread.c:68
run_init
static int run_init(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: thread.c:432
ff_vvc_lmcs_filter
void ff_vvc_lmcs_filter(const VVCLocalContext *lc, const int x, const int y)
lmcs filter for the CTU
Definition: filter.c:1243
pixel_done
static void pixel_done(VVCProgressListener *l)
Definition: thread.c:234
VVCProgressListener::y
int y
Definition: refs.h:49
ff_vvc_frame_thread_free
void ff_vvc_frame_thread_free(VVCFrameContext *fc)
Definition: thread.c:703
EntryPoint::ctu_end
int ctu_end
Definition: ctu.h:360
VVC_TASK_STAGE_INTER
@ VVC_TASK_STAGE_INTER
Definition: thread.c:45
executor.h
schedule_inter
static void schedule_inter(VVCContext *s, VVCFrameContext *fc, const SliceContext *sc, VVCTask *t, const int rs)
Definition: thread.c:293
user_data
static int FUNC() user_data(CodedBitstreamContext *ctx, RWContext *rw, MPEG2RawUserData *current)
Definition: cbs_mpeg2_syntax_template.c:59
intra.h
run_alf
static int run_alf(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: thread.c:588
refs.h
VVC_PROGRESS_LAST
@ VVC_PROGRESS_LAST
Definition: refs.h:41
VVCFrame
Definition: dec.h:71
a
The reader does not expect b to be semantically here and if the code is changed by maybe adding a a division or other the signedness will almost certainly be mistaken To avoid this confusion a new type was SUINT is the C unsigned type but it holds a signed int to use the same example SUINT a
Definition: undefined.txt:41
CTU::has_dmvr
int has_dmvr
Definition: ctu.h:334
VVCSH::ctb_addr_in_curr_slice
const uint32_t * ctb_addr_in_curr_slice
CtbAddrInCurrSlice.
Definition: ps.h:243
ff_mutex_lock
static int ff_mutex_lock(AVMutex *mutex)
Definition: thread.h:188
VVC_TASK_STAGE_LMCS
@ VVC_TASK_STAGE_LMCS
Definition: thread.c:47
zero
static int zero(InterplayACMContext *s, unsigned ind, unsigned col)
Definition: interplayacm.c:121
VVCTask::task
AVTask task
Definition: thread.c:58
VVCTask::ctu_idx
int ctu_idx
Definition: thread.c:73
task_add_score
static uint8_t task_add_score(VVCTask *t, const VVCTaskStage stage)
Definition: thread.c:141
atomic_uchar
intptr_t atomic_uchar
Definition: stdatomic.h:52
VVC_PROGRESS_MV
@ VVC_PROGRESS_MV
Definition: refs.h:39
CTU::max_y
int max_y[2][VVC_MAX_REF_ENTRIES]
Definition: ctu.h:332
task_get_score
static uint8_t task_get_score(VVCTask *t, const VVCTaskStage stage)
Definition: thread.c:146
task_init
static void task_init(VVCTask *t, VVCTaskStage stage, VVCFrameContext *fc, const int rx, const int ry)
Definition: thread.c:115
SliceContext::nb_eps
int nb_eps
Definition: dec.h:110
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:256
VVCFrameThread::lock
AVMutex lock
Definition: thread.c:102
submit_entry_point
static void submit_entry_point(VVCContext *s, VVCFrameThread *ft, SliceContext *sc, EntryPoint *ep)
Definition: thread.c:424
VVCTask::next
struct VVCTask * next
Definition: thread.c:57
task_is_stage_ready
static int task_is_stage_ready(VVCTask *t, int add)
Definition: thread.c:365
report_frame_progress
static void report_frame_progress(VVCFrameContext *fc, const int ry, const VVCProgress idx)
Definition: thread.c:456
EntryPoint
Definition: ctu.h:351
av_calloc
void * av_calloc(size_t nmemb, size_t size)
Definition: mem.c:264
VVCTask
Definition: thread.c:55
VVC_TASK_STAGE_RECON
@ VVC_TASK_STAGE_RECON
Definition: thread.c:46
ret
ret
Definition: filter_design.txt:187
sps
static int FUNC() sps(CodedBitstreamContext *ctx, RWContext *rw, H264RawSPS *current)
Definition: cbs_h264_syntax_template.c:260
run_deblock_v
static int run_deblock_v(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: thread.c:535
ff_vvc_executor_alloc
AVExecutor * ff_vvc_executor_alloc(VVCContext *s, const int thread_count)
Definition: thread.c:686
VVCProgressListener
Definition: refs.h:47
task_stage_done
static void task_stage_done(const VVCTask *t, VVCContext *s)
Definition: thread.c:326
CHECK
#define CHECK(a, b)
Definition: thread.c:382
run_recon
static int run_recon(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: thread.c:517
av_executor_free
void av_executor_free(AVExecutor **executor)
Free executor.
Definition: executor.c:184
VVCFrameThread::row_progress
int row_progress[VVC_PROGRESS_LAST]
Definition: thread.c:100
frame_thread_add_score
static void frame_thread_add_score(VVCContext *s, VVCFrameThread *ft, const int rx, const int ry, const VVCTaskStage stage)
Definition: thread.c:198
mv_done
static void mv_done(VVCProgressListener *l)
Definition: thread.c:239
VVC_TASK_STAGE_SAO
@ VVC_TASK_STAGE_SAO
Definition: thread.c:50
VVCFrameThread::tasks
VVCTask * tasks
Definition: thread.c:89
VVCFrameThread::ctu_count
int ctu_count
Definition: thread.c:94
av_executor_execute
void av_executor_execute(AVExecutor *e, AVTask *t)
Add task to executor.
Definition: executor.c:195
atomic_fetch_add
#define atomic_fetch_add(object, operand)
Definition: stdatomic.h:131
ProgressListener::task
struct VVCTask * task
Definition: thread.c:38
ref
static int ref[MAX_W *MAX_W]
Definition: jpeg2000dwt.c:112
ff_cond_signal
static int ff_cond_signal(AVCond *cond)
Definition: thread.h:196
pps
uint64_t pps
Definition: dovi_rpuenc.c:35
H266RawSPS::sps_entropy_coding_sync_enabled_flag
uint8_t sps_entropy_coding_sync_enabled_flag
Definition: cbs_h266.h:348
VVCTask::score
atomic_uchar score[VVC_TASK_STAGE_LAST]
Definition: thread.c:76
VVCTask::ep
EntryPoint * ep
Definition: thread.c:72
ProgressListener::l
VVCProgressListener l
Definition: thread.c:37
VVCTask::target_inter_score
atomic_uchar target_inter_score
Definition: thread.c:77
VVCTask::fc
VVCFrameContext * fc
Definition: thread.c:65
mem.h
VVC_TASK_STAGE_PARSE
@ VVC_TASK_STAGE_PARSE
Definition: thread.c:44
ff_cond_destroy
static int ff_cond_destroy(AVCond *cond)
Definition: thread.h:195
VVCProgressListener::progress_done
progress_done_fn progress_done
Definition: refs.h:50
SliceContext::sh
VVCSH sh
Definition: dec.h:108
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:34
add_task
static void add_task(VVCContext *s, VVCTask *t)
Definition: thread.c:106
VVCFrameContext
Definition: dec.h:115
VVC_TASK_STAGE_INIT
@ VVC_TASK_STAGE_INIT
Definition: thread.c:43
EntryPoint::ctu_start
int ctu_start
Definition: ctu.h:359
run_inter
static int run_inter(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: thread.c:501
VVCTask::ry
int ry
Definition: thread.c:64
IS_I
#define IS_I(rsh)
Definition: ps.h:38
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
thread.h
VVCFrameThread::ctu_width
int ctu_width
Definition: thread.c:92
AVERROR_INVALIDDATA
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:61
ff_vvc_sao_copy_ctb_to_hv
void ff_vvc_sao_copy_ctb_to_hv(VVCLocalContext *lc, const int rx, const int ry, const int last_row)
Definition: filter.c:174
ff_cond_init
static int ff_cond_init(AVCond *cond, const void *attr)
Definition: thread.h:194
ctu.h
ff_vvc_per_frame_init
int ff_vvc_per_frame_init(VVCFrameContext *fc)
Definition: dec.c:413
VVCLocalContext::ep
EntryPoint * ep
Definition: ctu.h:433
VVCTask::rs
int rs
Definition: thread.c:64
H266RawSliceHeader::sh_deblocking_filter_disabled_flag
uint8_t sh_deblocking_filter_disabled_flag
Definition: cbs_h266.h:815
ff_vvc_ep_init_stat_coeff
void ff_vvc_ep_init_stat_coeff(EntryPoint *ep, const int bit_depth, const int persistent_rice_adaptation_enabled_flag)
Definition: ctu.c:2561
is_first_row
static int is_first_row(const VVCFrameContext *fc, const int rx, const int ry)
Definition: thread.c:152
check_colocation
static void check_colocation(VVCContext *s, VVCTask *t)
Definition: thread.c:407
ff_vvc_add_progress_listener
void ff_vvc_add_progress_listener(VVCFrame *frame, VVCProgressListener *l)
Definition: refs.c:606
VVCFrameThread::ctu_height
int ctu_height
Definition: thread.c:93
VVCRefPic::scale
int scale[2]
RefPicScale[].
Definition: dec.h:52
VVCContext
Definition: dec.h:214
schedule_next_parse
static void schedule_next_parse(VVCContext *s, VVCFrameContext *fc, const SliceContext *sc, const VVCTask *t)
Definition: thread.c:267
VVCTaskStage
VVCTaskStage
Definition: thread.c:42
VVC_TASK_STAGE_ALF
@ VVC_TASK_STAGE_ALF
Definition: thread.c:51