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 {
52 } VVCTaskStage;
53 
54 typedef struct VVCTask {
55  union {
56  struct VVCTask *next; //for executor debug only
58  } u;
59 
61 
62  // ctu x, y, and raster scan order
63  int rx, ry, rs;
65 
68 
69  // for parse task only
72  int ctu_idx; //ctu idx in the current slice
73 
74  // tasks with target scores met are ready for scheduling
77 } VVCTask;
78 
79 typedef struct VVCRowThread {
81 } VVCRowThread;
82 
83 typedef struct VVCFrameThread {
84  // error return for tasks
86 
89 
90  int ctu_size;
91  int ctu_width;
93  int ctu_count;
94 
95  //protected by lock
98 
100 
104 
105 static void add_task(VVCContext *s, VVCTask *t)
106 {
107  VVCFrameThread *ft = t->fc->ft;
108 
110 
111  av_executor_execute(s->executor, &t->u.task);
112 }
113 
114 static void task_init(VVCTask *t, VVCTaskStage stage, VVCFrameContext *fc, const int rx, const int ry)
115 {
116  memset(t, 0, sizeof(*t));
117  t->stage = stage;
118  t->fc = fc;
119  t->rx = rx;
120  t->ry = ry;
121  t->rs = ry * fc->ft->ctu_width + rx;
122  for (int i = 0; i < FF_ARRAY_ELEMS(t->score); i++)
123  atomic_store(t->score + i, 0);
125 }
126 
127 static int task_init_parse(VVCTask *t, SliceContext *sc, EntryPoint *ep, const int ctu_idx)
128 {
129  if (t->sc) {
130  // the task already inited, error bitstream
131  return AVERROR_INVALIDDATA;
132  }
133  t->sc = sc;
134  t->ep = ep;
135  t->ctu_idx = ctu_idx;
136 
137  return 0;
138 }
139 
140 static uint8_t task_add_score(VVCTask *t, const VVCTaskStage stage)
141 {
142  return atomic_fetch_add(&t->score[stage], 1) + 1;
143 }
144 
145 static uint8_t task_get_score(VVCTask *t, const VVCTaskStage stage)
146 {
147  return atomic_load(&t->score[stage]);
148 }
149 
150 //first row in tile or slice
151 static int is_first_row(const VVCFrameContext *fc, const int rx, const int ry)
152 {
153  const VVCFrameThread *ft = fc->ft;
154  const VVCPPS *pps = fc->ps.pps;
155 
156  if (ry != pps->ctb_to_row_bd[ry]) {
157  const int rs = ry * ft->ctu_width + rx;
158  return fc->tab.slice_idx[rs] != fc->tab.slice_idx[rs - ft->ctu_width];
159  }
160  return 1;
161 }
162 
163 static int task_has_target_score(VVCTask *t, const VVCTaskStage stage, const uint8_t score)
164 {
165  // l:left, r:right, t: top, b: bottom
166  static const uint8_t target_score[] =
167  {
168  2, //VVC_TASK_STAGE_RECON, need l + rt recon
169  3, //VVC_TASK_STAGE_LMCS, need r + b + rb recon
170  1, //VVC_TASK_STAGE_DEBLOCK_V, need l deblock v
171  2, //VVC_TASK_STAGE_DEBLOCK_H, need r deblock v + t deblock h
172  5, //VVC_TASK_STAGE_SAO, need l + r + lb + b + rb deblock h
173  8, //VVC_TASK_STAGE_ALF, need sao around the ctu
174  };
175  uint8_t target = 0;
176  VVCFrameContext *fc = t->fc;
177 
178  if (stage == VVC_TASK_STAGE_PARSE) {
179  const H266RawSPS *rsps = fc->ps.sps->r;
180  const int wpp = rsps->sps_entropy_coding_sync_enabled_flag && !is_first_row(fc, t->rx, t->ry);
181  target = 2 + wpp - 1; //left parse + colocation + wpp - no previous stage
182  } else if (stage == VVC_TASK_STAGE_INTER) {
183  target = atomic_load(&t->target_inter_score);
184  } else {
185  target = target_score[stage - VVC_TASK_STAGE_RECON];
186  }
187 
188  //+1 for previous stage
189  av_assert0(score <= target + 1);
190  return score == target + 1;
191 }
192 
194  const int rx, const int ry, const VVCTaskStage stage)
195 {
196  VVCTask *t = ft->tasks + ft->ctu_width * ry + rx;
197  uint8_t score;
198 
199  if (rx < 0 || rx >= ft->ctu_width || ry < 0 || ry >= ft->ctu_height)
200  return;
201 
202  score = task_add_score(t, stage);
203  if (task_has_target_score(t, stage, score)) {
204  av_assert0(s);
205  av_assert0(stage == t->stage);
206  add_task(s, t);
207  }
208 }
209 
210 static void sheduled_done(VVCFrameThread *ft, atomic_int *scheduled)
211 {
212  if (atomic_fetch_sub(scheduled, 1) == 1) {
213  ff_mutex_lock(&ft->lock);
214  ff_cond_signal(&ft->cond);
215  ff_mutex_unlock(&ft->lock);
216  }
217 }
218 
219 static void progress_done(VVCProgressListener *_l, const int type)
220 {
221  const ProgressListener *l = (ProgressListener *)_l;
222  const VVCTask *t = l->task;
223  VVCFrameThread *ft = t->fc->ft;
224 
225  frame_thread_add_score(l->s, ft, t->rx, t->ry, type);
227 }
228 
230 {
232 }
233 
235 {
237 }
238 
239 static void listener_init(ProgressListener *l, VVCTask *t, VVCContext *s, const VVCProgress vp, const int y)
240 {
241  const int is_inter = vp == VVC_PROGRESS_PIXEL;
242 
243  l->task = t;
244  l->s = s;
245  l->l.vp = vp;
246  l->l.y = y;
247  l->l.progress_done = is_inter ? pixel_done : mv_done;
248  if (is_inter)
250 }
251 
253  VVCTask *t, VVCContext *s, const VVCProgress vp, const int y)
254 {
255  VVCFrameThread *ft = t->fc->ft;
256 
258  listener_init(l, t, s, vp, y);
260 }
261 
263 {
264  VVCFrameThread *ft = fc->ft;
265  EntryPoint *ep = t->ep;
266  const VVCSPS *sps = fc->ps.sps;
267 
268  if (sps->r->sps_entropy_coding_sync_enabled_flag) {
269  if (t->rx == fc->ps.pps->ctb_to_col_bd[t->rx]) {
270  EntryPoint *next = ep + 1;
271  if (next < sc->eps + sc->nb_eps && !is_first_row(fc, t->rx, t->ry + 1)) {
272  memcpy(next->cabac_state, ep->cabac_state, sizeof(next->cabac_state));
273  ff_vvc_ep_init_stat_coeff(next, sps->bit_depth, sps->r->sps_persistent_rice_adaptation_enabled_flag);
274  }
275  }
276  if (t->ry + 1 < ft->ctu_height && !is_first_row(fc, t->rx, t->ry + 1))
278  }
279 
280  if (t->ctu_idx + 1 < t->ep->ctu_end) {
281  const int next_rs = sc->sh.ctb_addr_in_curr_slice[t->ctu_idx + 1];
282  const int next_rx = next_rs % ft->ctu_width;
283  const int next_ry = next_rs / ft->ctu_width;
284  frame_thread_add_score(s, ft, next_rx, next_ry, VVC_TASK_STAGE_PARSE);
285  }
286 }
287 
288 static void schedule_inter(VVCContext *s, VVCFrameContext *fc, const SliceContext *sc, VVCTask *t, const int rs)
289 {
290  const VVCSH *sh = &sc->sh;
291 
292  if (!IS_I(sh->r)) {
293  CTU *ctu = fc->tab.ctus + rs;
294  for (int lx = 0; lx < 2; lx++) {
295  for (int i = 0; i < sh->r->num_ref_idx_active[lx]; i++) {
296  int y = ctu->max_y[lx][i];
297  VVCRefPic *refp = sc->rpl[lx].refs + i;
298  VVCFrame *ref = refp->ref;
299  if (ref && y >= 0) {
300  if (refp->is_scaled)
301  y = y * refp->scale[1] >> 14;
303  }
304  }
305  }
306  }
307 }
308 
309 static void parse_task_done(VVCContext *s, VVCFrameContext *fc, const int rx, const int ry)
310 {
311  VVCFrameThread *ft = fc->ft;
312  const int rs = ry * ft->ctu_width + rx;
313  const int slice_idx = fc->tab.slice_idx[rs];
314  VVCTask *t = ft->tasks + rs;
315  const SliceContext *sc = fc->slices[slice_idx];
316 
317  schedule_next_parse(s, fc, sc, t);
318  schedule_inter(s, fc, sc, t, rs);
319 }
320 
321 static void task_stage_done(const VVCTask *t, VVCContext *s)
322 {
323  VVCFrameContext *fc = t->fc;
324  VVCFrameThread *ft = fc->ft;
325  const VVCTaskStage stage = t->stage;
326 
327 #define ADD(dx, dy, stage) frame_thread_add_score(s, ft, t->rx + (dx), t->ry + (dy), stage)
328 
329  //this is a reserve map of ready_score, ordered by zigzag
330  if (stage == VVC_TASK_STAGE_PARSE) {
331  parse_task_done(s, fc, t->rx, t->ry);
332  } else if (stage == VVC_TASK_STAGE_RECON) {
333  ADD(-1, 1, VVC_TASK_STAGE_RECON);
334  ADD( 1, 0, VVC_TASK_STAGE_RECON);
335  ADD(-1, -1, VVC_TASK_STAGE_LMCS);
336  ADD( 0, -1, VVC_TASK_STAGE_LMCS);
337  ADD(-1, 0, VVC_TASK_STAGE_LMCS);
338  } else if (stage == VVC_TASK_STAGE_DEBLOCK_V) {
341  } else if (stage == VVC_TASK_STAGE_DEBLOCK_H) {
343  ADD(-1, -1, VVC_TASK_STAGE_SAO);
344  ADD( 0, -1, VVC_TASK_STAGE_SAO);
345  ADD(-1, 0, VVC_TASK_STAGE_SAO);
346  ADD( 1, -1, VVC_TASK_STAGE_SAO);
347  ADD( 1, 0, VVC_TASK_STAGE_SAO);
348  } else if (stage == VVC_TASK_STAGE_SAO) {
349  ADD(-1, -1, VVC_TASK_STAGE_ALF);
350  ADD( 0, -1, VVC_TASK_STAGE_ALF);
351  ADD(-1, 0, VVC_TASK_STAGE_ALF);
352  ADD( 1, -1, VVC_TASK_STAGE_ALF);
353  ADD(-1, 1, VVC_TASK_STAGE_ALF);
354  ADD( 1, 0, VVC_TASK_STAGE_ALF);
355  ADD( 0, 1, VVC_TASK_STAGE_ALF);
356  ADD( 1, 1, VVC_TASK_STAGE_ALF);
357  }
358 }
359 
360 static int task_is_stage_ready(VVCTask *t, int add)
361 {
362  const VVCTaskStage stage = t->stage;
363  uint8_t score;
364  if (stage > VVC_TASK_STAGE_ALF)
365  return 0;
366  score = task_get_score(t, stage) + add;
367  return task_has_target_score(t, stage, score);
368 }
369 
370 static int task_ready(const AVTask *_t, void *user_data)
371 {
372  VVCTask *t = (VVCTask*)_t;
373 
374  return task_is_stage_ready(t, 0);
375 }
376 
377 #define CHECK(a, b) \
378  do { \
379  if ((a) != (b)) \
380  return (a) < (b); \
381  } while (0)
382 
383 static int task_priority_higher(const AVTask *_a, const AVTask *_b)
384 {
385  const VVCTask *a = (const VVCTask*)_a;
386  const VVCTask *b = (const VVCTask*)_b;
387 
388  CHECK(a->fc->decode_order, b->fc->decode_order); //decode order
389 
390  if (a->stage == VVC_TASK_STAGE_PARSE || b->stage == VVC_TASK_STAGE_PARSE) {
391  CHECK(a->stage, b->stage);
392  CHECK(a->ry, b->ry);
393  return a->rx < b->rx;
394  }
395 
396  CHECK(a->rx + a->ry + a->stage, b->rx + b->ry + b->stage); //zigzag with type
397  CHECK(a->rx + a->ry, b->rx + b->ry); //zigzag
398  return a->ry < b->ry;
399 }
400 
402  const int ry, const VVCProgress idx)
403 {
404  VVCFrameThread *ft = fc->ft;
405  const int ctu_size = ft->ctu_size;
406  int old;
407 
408  if (atomic_fetch_add(&ft->rows[ry].col_progress[idx], 1) == ft->ctu_width - 1) {
409  int y;
410  ff_mutex_lock(&ft->lock);
411  y = old = ft->row_progress[idx];
412  while (y < ft->ctu_height && atomic_load(&ft->rows[y].col_progress[idx]) == ft->ctu_width)
413  y++;
414  if (old != y) {
415  const int progress = y == ft->ctu_height ? INT_MAX : y * ctu_size;
416  ft->row_progress[idx] = y;
417  ff_vvc_report_progress(fc->ref, idx, progress);
418  }
419  ff_mutex_unlock(&ft->lock);
420  }
421 }
422 
424 {
425  int ret;
426  VVCFrameContext *fc = lc->fc;
427  const int rs = t->rs;
428  const CTU *ctu = fc->tab.ctus + rs;
429 
430  lc->ep = t->ep;
431 
432  ret = ff_vvc_coding_tree_unit(lc, t->ctu_idx, rs, t->rx, t->ry);
433  if (ret < 0)
434  return ret;
435 
436  if (!ctu->has_dmvr)
438 
439  return 0;
440 }
441 
443 {
444  VVCFrameContext *fc = lc->fc;
445  const CTU *ctu = fc->tab.ctus + t->rs;
446 
447  ff_vvc_predict_inter(lc, t->rs);
448 
449  if (ctu->has_dmvr)
451 
452  return 0;
453 }
454 
456 {
457  ff_vvc_reconstruct(lc, t->rs, t->rx, t->ry);
458 
459  return 0;
460 }
461 
463 {
464  VVCFrameContext *fc = lc->fc;
465  VVCFrameThread *ft = fc->ft;
466  const int ctu_size = ft->ctu_size;
467  const int x0 = t->rx * ctu_size;
468  const int y0 = t->ry * ctu_size;
469 
470  ff_vvc_lmcs_filter(lc, x0, y0);
471 
472  return 0;
473 }
474 
476 {
477  VVCFrameContext *fc = lc->fc;
478  VVCFrameThread *ft = fc->ft;
479  const int ctb_size = ft->ctu_size;
480  const int x0 = t->rx * ctb_size;
481  const int y0 = t->ry * ctb_size;
482 
484  ff_vvc_decode_neighbour(lc, x0, y0, t->rx, t->ry, t->rs);
485  ff_vvc_deblock_vertical(lc, x0, y0, t->rs);
486  }
487 
488  return 0;
489 }
490 
492 {
493  VVCFrameContext *fc = lc->fc;
494  VVCFrameThread *ft = fc->ft;
495  const int ctb_size = ft->ctu_size;
496  const int x0 = t->rx * ctb_size;
497  const int y0 = t->ry * ctb_size;
498 
500  ff_vvc_decode_neighbour(lc, x0, y0, t->rx, t->ry, t->rs);
501  ff_vvc_deblock_horizontal(lc, x0, y0, t->rs);
502  }
503  if (fc->ps.sps->r->sps_sao_enabled_flag)
504  ff_vvc_sao_copy_ctb_to_hv(lc, t->rx, t->ry, t->ry == ft->ctu_height - 1);
505 
506  return 0;
507 }
508 
510 {
511  VVCFrameContext *fc = lc->fc;
512  VVCFrameThread *ft = fc->ft;
513  const int ctb_size = ft->ctu_size;
514  const int x0 = t->rx * ctb_size;
515  const int y0 = t->ry * ctb_size;
516 
517  if (fc->ps.sps->r->sps_sao_enabled_flag) {
518  ff_vvc_decode_neighbour(lc, x0, y0, t->rx, t->ry, t->rs);
519  ff_vvc_sao_filter(lc, x0, y0);
520  }
521 
522  if (fc->ps.sps->r->sps_alf_enabled_flag)
523  ff_vvc_alf_copy_ctu_to_hv(lc, x0, y0);
524 
525  return 0;
526 }
527 
529 {
530  VVCFrameContext *fc = lc->fc;
531  VVCFrameThread *ft = fc->ft;
532  const int ctu_size = ft->ctu_size;
533  const int x0 = t->rx * ctu_size;
534  const int y0 = t->ry * ctu_size;
535 
536  if (fc->ps.sps->r->sps_alf_enabled_flag) {
537  ff_vvc_decode_neighbour(lc, x0, y0, t->rx, t->ry, t->rs);
538  ff_vvc_alf_filter(lc, x0, y0);
539  }
541 
542  return 0;
543 }
544 
545 #define VVC_THREAD_DEBUG
546 #ifdef VVC_THREAD_DEBUG
547 const static char* task_name[] = {
548  "P",
549  "I",
550  "R",
551  "L",
552  "V",
553  "H",
554  "S",
555  "A"
556 };
557 #endif
558 
560 
562 {
563  int ret;
564  VVCFrameContext *fc = t->fc;
565  VVCFrameThread *ft = fc->ft;
566  const VVCTaskStage stage = t->stage;
567  run_func run[] = {
568  run_parse,
569  run_inter,
570  run_recon,
571  run_lmcs,
574  run_sao,
575  run_alf,
576  };
577 
578 #ifdef VVC_THREAD_DEBUG
579  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);
580 #endif
581 
582  lc->sc = t->sc;
583 
584  if (!atomic_load(&ft->ret)) {
585  if ((ret = run[stage](s, lc, t)) < 0) {
586 #ifdef COMPAT_ATOMICS_WIN32_STDATOMIC_H
587  intptr_t zero = 0;
588 #else
589  int zero = 0;
590 #endif
592  av_log(s->avctx, AV_LOG_ERROR,
593  "frame %5d, %s(%3d, %3d) failed with %d\r\n",
594  (int)fc->decode_order, task_name[stage], t->rx, t->ry, ret);
595  }
596  }
597 
598  task_stage_done(t, s);
599  return;
600 }
601 
602 static int task_run(AVTask *_t, void *local_context, void *user_data)
603 {
604  VVCTask *t = (VVCTask*)_t;
606  VVCLocalContext *lc = local_context;
607  VVCFrameThread *ft = t->fc->ft;
608 
609  lc->fc = t->fc;
610 
611  do {
612  task_run_stage(t, s, lc);
613  t->stage++;
614  } while (task_is_stage_ready(t, 1));
615 
616  if (t->stage != VVC_TASK_STAGE_LAST)
617  frame_thread_add_score(s, ft, t->rx, t->ry, t->stage);
618 
620 
621  return 0;
622 }
623 
624 AVExecutor* ff_vvc_executor_alloc(VVCContext *s, const int thread_count)
625 {
627  s,
628  sizeof(VVCLocalContext),
630  task_ready,
631  task_run,
632  };
633  return av_executor_alloc(&callbacks, thread_count);
634 }
635 
637 {
638  av_executor_free(e);
639 }
640 
642 {
643  VVCFrameThread *ft = fc->ft;
644 
645  if (!ft)
646  return;
647 
648  ff_mutex_destroy(&ft->lock);
649  ff_cond_destroy(&ft->cond);
650  av_freep(&ft->rows);
651  av_freep(&ft->tasks);
652  av_freep(&ft);
653 }
654 
656 {
657  const VVCFrameThread *ft = fc->ft;
658  VVCTask task;
659 
660  task_init(&task, VVC_TASK_STAGE_RECON, fc, 0, 0);
661 
662  for (int i = VVC_TASK_STAGE_RECON; i < VVC_TASK_STAGE_LAST; i++) {
663  task.stage = i;
664 
665  for (task.rx = -1; task.rx <= ft->ctu_width; task.rx++) {
666  task.ry = -1; //top
667  task_stage_done(&task, NULL);
668  task.ry = ft->ctu_height; //bottom
669  task_stage_done(&task, NULL);
670  }
671 
672  for (task.ry = 0; task.ry < ft->ctu_height; task.ry++) {
673  task.rx = -1; //left
674  task_stage_done(&task, NULL);
675  task.rx = ft->ctu_width; //right
676  task_stage_done(&task, NULL);
677  }
678  }
679 }
680 
682 {
683  const VVCSPS *sps = fc->ps.sps;
684  const VVCPPS *pps = fc->ps.pps;
685  VVCFrameThread *ft = fc->ft;
686  int ret;
687 
688  if (!ft || ft->ctu_width != pps->ctb_width ||
689  ft->ctu_height != pps->ctb_height ||
690  ft->ctu_size != sps->ctb_size_y) {
691 
693  ft = av_calloc(1, sizeof(*fc->ft));
694  if (!ft)
695  return AVERROR(ENOMEM);
696 
697  ft->ctu_width = fc->ps.pps->ctb_width;
698  ft->ctu_height = fc->ps.pps->ctb_height;
699  ft->ctu_count = fc->ps.pps->ctb_count;
700  ft->ctu_size = fc->ps.sps->ctb_size_y;
701 
702  ft->rows = av_calloc(ft->ctu_height, sizeof(*ft->rows));
703  if (!ft->rows)
704  goto fail;
705 
706  ft->tasks = av_malloc(ft->ctu_count * sizeof(*ft->tasks));
707  if (!ft->tasks)
708  goto fail;
709 
710  if ((ret = ff_cond_init(&ft->cond, NULL)))
711  goto fail;
712 
713  if ((ret = ff_mutex_init(&ft->lock, NULL))) {
714  ff_cond_destroy(&ft->cond);
715  goto fail;
716  }
717  }
718  fc->ft = ft;
719  ft->ret = 0;
720  for (int y = 0; y < ft->ctu_height; y++) {
721  VVCRowThread *row = ft->rows + y;
722  memset(row->col_progress, 0, sizeof(row->col_progress));
723  }
724 
725  for (int rs = 0; rs < ft->ctu_count; rs++) {
726  VVCTask *t = ft->tasks + rs;
727  task_init(t, VVC_TASK_STAGE_PARSE, fc, rs % ft->ctu_width, rs / ft->ctu_width);
728  }
729 
730  memset(&ft->row_progress[0], 0, sizeof(ft->row_progress));
731 
733 
734  return 0;
735 
736 fail:
737  if (ft) {
738  av_freep(&ft->rows);
739  av_freep(&ft->tasks);
740  av_freep(&ft);
741  }
742 
743  return AVERROR(ENOMEM);
744 }
745 
747 {
748  const VVCFrameContext *fc = t->fc;
749 
750  if (fc->ps.ph.r->ph_temporal_mvp_enabled_flag || fc->ps.sps->r->sps_sbtmvp_enabled_flag) {
751  VVCFrame *col = fc->ref->collocated_ref;
752  const int first_col = t->rx == fc->ps.pps->ctb_to_col_bd[t->rx];
753  if (col && first_col) {
754  //we depend on bottom and right boundary, do not - 1 for y
755  const int y = (t->ry << fc->ps.sps->ctb_log2_size_y);
757  return;
758  }
759  }
761 }
762 
764 {
765  const int rs = sc->sh.ctb_addr_in_curr_slice[ep->ctu_start];
766  VVCTask *t = ft->tasks + rs;
767 
769 }
770 
772 {
773  VVCFrameThread *ft = fc->ft;
774 
775  // We'll handle this in two passes:
776  // Pass 0 to initialize tasks with parser, this will help detect bit stream error
777  // Pass 1 to shedule location check and submit the entry point
778  for (int pass = 0; pass < 2; pass++) {
779  for (int i = 0; i < fc->nb_slices; i++) {
780  SliceContext *sc = fc->slices[i];
781  for (int j = 0; j < sc->nb_eps; j++) {
782  EntryPoint *ep = sc->eps + j;
783  for (int k = ep->ctu_start; k < ep->ctu_end; k++) {
784  const int rs = sc->sh.ctb_addr_in_curr_slice[k];
785  VVCTask *t = ft->tasks + rs;
786  if (pass) {
787  check_colocation(s, t);
788  } else {
789  const int ret = task_init_parse(t, sc, ep, k);
790  if (ret < 0)
791  return ret;
792  }
793  }
794  if (pass)
795  submit_entry_point(s, ft, sc, ep);
796  }
797  }
798  }
799  return 0;
800 }
801 
803 {
804  VVCFrameThread *ft = fc->ft;
805 
806  ff_mutex_lock(&ft->lock);
807 
809  ff_cond_wait(&ft->cond, &ft->lock);
810 
811  ff_mutex_unlock(&ft->lock);
813 
814 #ifdef VVC_THREAD_DEBUG
815  av_log(s->avctx, AV_LOG_DEBUG, "frame %5d done\r\n", (int)fc->decode_order);
816 #endif
817  return ft->ret;
818 }
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:491
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:309
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:51
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:509
thread.h
ff_vvc_report_frame_finished
void ff_vvc_report_frame_finished(VVCFrame *frame)
Definition: refs.c:545
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:239
frame_thread_init_score
static void frame_thread_init_score(VVCFrameContext *fc)
Definition: thread.c:655
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:1010
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:47
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:810
VVCLocalContext::sc
SliceContext * sc
Definition: ctu.h:432
run_lmcs
static int run_lmcs(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: thread.c:462
task_init_parse
static int task_init_parse(VVCTask *t, SliceContext *sc, EntryPoint *ep, const int ctu_idx)
Definition: thread.c:127
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:1175
b
#define b
Definition: input.c:41
VVCFrameThread::ctu_size
int ctu_size
Definition: thread.c:90
atomic_int
intptr_t atomic_int
Definition: stdatomic.h:55
VVCSH::r
const H266RawSliceHeader * r
RefStruct reference.
Definition: ps.h:232
fc
#define fc(width, name, range_min, range_max)
Definition: cbs_av1.c:464
task_run_stage
static void task_run_stage(VVCTask *t, VVCContext *s, VVCLocalContext *lc)
Definition: thread.c:561
task_ready
static int task_ready(const AVTask *_t, void *user_data)
Definition: thread.c:370
run_parse
static int run_parse(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: thread.c:423
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:878
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:252
VVCProgress
VVCProgress
Definition: refs.h:38
progress_done
static void progress_done(VVCProgressListener *_l, const int type)
Definition: thread.c:219
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:2443
VVCLocalContext::fc
VVCFrameContext * fc
Definition: ctu.h:433
fail
#define fail()
Definition: checkasm.h:185
VVCRowThread
Definition: thread.c:79
VVCTask::stage
VVCTaskStage stage
Definition: thread.c:60
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:547
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:771
VVCFrameThread::cond
AVCond cond
Definition: thread.c:102
VVCFrameThread
Definition: thread.c:83
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:137
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:96
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:802
ff_vvc_executor_free
void ff_vvc_executor_free(AVExecutor **e)
Definition: thread.c:636
s
#define s(width, name)
Definition: cbs_vp9.c:198
EntryPoint::cabac_state
VVCCabacState cabac_state[VVC_CONTEXTS]
Definition: ctu.h:357
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
VVC_MAX_REF_ENTRIES
@ VVC_MAX_REF_ENTRIES
Definition: vvc.h:115
VVCSH
Definition: ps.h:231
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:80
atomic_load
#define atomic_load(object)
Definition: stdatomic.h:93
sheduled_done
static void sheduled_done(VVCFrameThread *ft, atomic_int *scheduled)
Definition: thread.c:210
AVTask
Definition: executor.h:27
H266RawSPS
Definition: cbs_h266.h:308
VVCTask::sc
SliceContext * sc
Definition: thread.c:70
CTU
Definition: ctu.h:331
inter.h
task_run
static int task_run(AVTask *_t, void *local_context, void *user_data)
Definition: thread.c:602
NULL
#define NULL
Definition: coverity.c:32
AVExecutor
Definition: executor.c:49
run_func
int(* run_func)(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: thread.c:559
ff_vvc_frame_thread_init
int ff_vvc_frame_thread_init(VVCFrameContext *fc)
Definition: thread.c:681
task_has_target_score
static int task_has_target_score(VVCTask *t, const VVCTaskStage stage, const uint8_t score)
Definition: thread.c:163
run
uint8_t run
Definition: svq3.c:204
VVCLocalContext
Definition: ctu.h:371
ff_vvc_sao_filter
void ff_vvc_sao_filter(VVCLocalContext *lc, int x, int y)
sao filter for the CTU
Definition: filter.c:154
task_priority_higher
static int task_priority_higher(const AVTask *_a, const AVTask *_b)
Definition: thread.c:383
SliceContext::eps
struct EntryPoint * eps
Definition: dec.h:109
VVCTask::rx
int rx
Definition: thread.c:63
VVC_TASK_STAGE_DEBLOCK_H
@ VVC_TASK_STAGE_DEBLOCK_H
Definition: thread.c:48
VVCRefPic::is_scaled
int is_scaled
RprConstraintsActiveFlag.
Definition: dec.h:51
VVCFrameThread::ret
atomic_int ret
Definition: thread.c:85
ProgressListener
Definition: thread.c:36
VVCFrameThread::nb_scheduled_listeners
atomic_int nb_scheduled_listeners
Definition: thread.c:97
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:1199
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:87
VVCTask::col_listener
ProgressListener col_listener
Definition: thread.c:66
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:2474
VVCTask::listener
ProgressListener listener[2][VVC_MAX_REF_ENTRIES]
Definition: thread.c:67
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:1276
pixel_done
static void pixel_done(VVCProgressListener *l)
Definition: thread.c:229
VVCProgressListener::y
int y
Definition: refs.h:49
ff_vvc_frame_thread_free
void ff_vvc_frame_thread_free(VVCFrameContext *fc)
Definition: thread.c:641
EntryPoint::ctu_end
int ctu_end
Definition: ctu.h:361
VVC_TASK_STAGE_INTER
@ VVC_TASK_STAGE_INTER
Definition: thread.c:44
executor.h
schedule_inter
static void schedule_inter(VVCContext *s, VVCFrameContext *fc, const SliceContext *sc, VVCTask *t, const int rs)
Definition: thread.c:288
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:528
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:335
VVCSH::ctb_addr_in_curr_slice
const uint32_t * ctb_addr_in_curr_slice
CtbAddrInCurrSlice.
Definition: ps.h:237
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:46
zero
static int zero(InterplayACMContext *s, unsigned ind, unsigned col)
Definition: interplayacm.c:121
VVCTask::task
AVTask task
Definition: thread.c:57
VVCTask::ctu_idx
int ctu_idx
Definition: thread.c:72
task_add_score
static uint8_t task_add_score(VVCTask *t, const VVCTaskStage stage)
Definition: thread.c:140
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:333
task_get_score
static uint8_t task_get_score(VVCTask *t, const VVCTaskStage stage)
Definition: thread.c:145
task_init
static void task_init(VVCTask *t, VVCTaskStage stage, VVCFrameContext *fc, const int rx, const int ry)
Definition: thread.c:114
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:101
submit_entry_point
static void submit_entry_point(VVCContext *s, VVCFrameThread *ft, SliceContext *sc, EntryPoint *ep)
Definition: thread.c:763
VVCTask::next
struct VVCTask * next
Definition: thread.c:56
task_is_stage_ready
static int task_is_stage_ready(VVCTask *t, int add)
Definition: thread.c:360
report_frame_progress
static void report_frame_progress(VVCFrameContext *fc, const int ry, const VVCProgress idx)
Definition: thread.c:401
EntryPoint
Definition: ctu.h:352
av_calloc
void * av_calloc(size_t nmemb, size_t size)
Definition: mem.c:264
VVCTask
Definition: thread.c:54
VVC_TASK_STAGE_RECON
@ VVC_TASK_STAGE_RECON
Definition: thread.c:45
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:475
ff_vvc_executor_alloc
AVExecutor * ff_vvc_executor_alloc(VVCContext *s, const int thread_count)
Definition: thread.c:624
VVCProgressListener
Definition: refs.h:47
task_stage_done
static void task_stage_done(const VVCTask *t, VVCContext *s)
Definition: thread.c:321
CHECK
#define CHECK(a, b)
Definition: thread.c:377
run_recon
static int run_recon(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: thread.c:455
av_executor_free
void av_executor_free(AVExecutor **executor)
Free executor.
Definition: executor.c:176
VVCFrameThread::row_progress
int row_progress[VVC_PROGRESS_LAST]
Definition: thread.c:99
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:193
mv_done
static void mv_done(VVCProgressListener *l)
Definition: thread.c:234
VVC_TASK_STAGE_SAO
@ VVC_TASK_STAGE_SAO
Definition: thread.c:49
VVCFrameThread::tasks
VVCTask * tasks
Definition: thread.c:88
VVCFrameThread::ctu_count
int ctu_count
Definition: thread.c:93
av_executor_execute
void av_executor_execute(AVExecutor *e, AVTask *t)
Add task to executor.
Definition: executor.c:184
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:75
VVCTask::u
union VVCTask::@272 u
VVCTask::ep
EntryPoint * ep
Definition: thread.c:71
ProgressListener::l
VVCProgressListener l
Definition: thread.c:37
VVCTask::target_inter_score
atomic_uchar target_inter_score
Definition: thread.c:76
VVCTask::fc
VVCFrameContext * fc
Definition: thread.c:64
mem.h
VVC_TASK_STAGE_PARSE
@ VVC_TASK_STAGE_PARSE
Definition: thread.c:43
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:105
VVCFrameContext
Definition: dec.h:115
EntryPoint::ctu_start
int ctu_start
Definition: ctu.h:360
run_inter
static int run_inter(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: thread.c:442
VVCTask::ry
int ry
Definition: thread.c:63
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:91
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:143
ff_cond_init
static int ff_cond_init(AVCond *cond, const void *attr)
Definition: thread.h:194
ctu.h
VVCLocalContext::ep
EntryPoint * ep
Definition: ctu.h:434
VVCTask::rs
int rs
Definition: thread.c:63
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:2550
is_first_row
static int is_first_row(const VVCFrameContext *fc, const int rx, const int ry)
Definition: thread.c:151
int
int
Definition: ffmpeg_filter.c:424
check_colocation
static void check_colocation(VVCContext *s, VVCTask *t)
Definition: thread.c:746
ff_vvc_add_progress_listener
void ff_vvc_add_progress_listener(VVCFrame *frame, VVCProgressListener *l)
Definition: refs.c:605
VVCFrameThread::ctu_height
int ctu_height
Definition: thread.c:92
VVCRefPic::scale
int scale[2]
RefPicScale[].
Definition: dec.h:52
VVCContext
Definition: dec.h:218
schedule_next_parse
static void schedule_next_parse(VVCContext *s, VVCFrameContext *fc, const SliceContext *sc, const VVCTask *t)
Definition: thread.c:262
VVCTaskStage
VVCTaskStage
Definition: thread.c:42
VVC_TASK_STAGE_ALF
@ VVC_TASK_STAGE_ALF
Definition: thread.c:50