memset tables in the main thread can become a bottleneck for the decoder. For example, if it takes 1% of the processing time for one core, the maximum achievable FPS will be 100. Move the memeset to worker threads will fix the issue.
Showing the changes to libavcodec (3 of 3 files). Show all
Unified · Split
| @@ -82,7 +82,13 @@ static int tl_create(TabList *l) · 81 unmodified lines | ||
| 82 | 82 | if (!*t->tab) |
| 83 | 83 | return AVERROR(ENOMEM); |
| 84 | 84 | } |
| 85 | − } else if (l->zero) { | |
| 85 | + } | |
| 86 | + return 0; | |
| 87 | +} | |
| 88 | + | |
| 89 | +static int tl_zero(TabList *l) | |
| 90 | +{ | |
| 91 | + if (l->zero) { | |
| 86 | 92 | for (int i = 0; i < l->nb_tabs; i++) { |
| 87 | 93 | Tab *t = l->tabs + i; |
| 88 | 94 | memset(*t->tab, 0, t->size); |
| @@ -404,6 +410,11 @@ static int pic_arrays_init(VVCContext *s, VVCFrameContext *fc) · 315 unmodified lines | ||
| 404 | 410 | return 0; |
| 405 | 411 | } |
| 406 | 412 | |
| 413 | +int ff_vvc_per_frame_init(VVCFrameContext *fc) | |
| 414 | +{ | |
| 415 | + return frame_context_for_each_tl(fc, tl_zero); | |
| 416 | +} | |
| 417 | + | |
| 407 | 418 | static int min_positive(const int idx, const int diff, const int min_diff) |
| 408 | 419 | { |
| 409 | 420 | return diff > 0 && (idx < 0 || diff < min_diff); |
| @@ -40,6 +40,7 @@ typedef struct ProgressListener { · 39 unmodified lines | ||
| 40 | 40 | } ProgressListener; |
| 41 | 41 | |
| 42 | 42 | typedef enum VVCTaskStage { |
| 43 | + VVC_TASK_STAGE_INIT, // for CTU(0, 0) only | |
| 43 | 44 | VVC_TASK_STAGE_PARSE, |
| 44 | 45 | VVC_TASK_STAGE_INTER, |
| 45 | 46 | VVC_TASK_STAGE_RECON, |
| @@ -175,10 +176,14 @@ static int task_has_target_score(VVCTask *t, const VVCTaskStage stage, const uin · 129 unmodified lines | ||
| 175 | 176 | uint8_t target = 0; |
| 176 | 177 | VVCFrameContext *fc = t->fc; |
| 177 | 178 | |
| 179 | + if (stage == VVC_TASK_STAGE_INIT) | |
| 180 | + return 1; | |
| 181 | + | |
| 178 | 182 | 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 | |
| 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 | |
| 182 | 187 | } else if (stage == VVC_TASK_STAGE_INTER) { |
| 183 | 188 | target = atomic_load(&t->target_inter_score); |
| 184 | 189 | } else { |
| @@ -399,6 +404,55 @@ static int task_priority_higher(const AVTask *_a, const AVTask *_b) · 214 unmodified lines | ||
| 399 | 404 | return a->ry < b->ry; |
| 400 | 405 | } |
| 401 | 406 | |
| 407 | +static void check_colocation(VVCContext *s, VVCTask *t) | |
| 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); | |
| 417 | + add_progress_listener(col, &t->col_listener, t, s, VVC_PROGRESS_MV, y); | |
| 418 | + return; | |
| 419 | + } | |
| 420 | + } | |
| 421 | + frame_thread_add_score(s, fc->ft, t->rx, t->ry, VVC_TASK_STAGE_PARSE); | |
| 422 | +} | |
| 423 | + | |
| 424 | +static void submit_entry_point(VVCContext *s, VVCFrameThread *ft, SliceContext *sc, EntryPoint *ep) | |
| 425 | +{ | |
| 426 | + const int rs = sc->sh.ctb_addr_in_curr_slice[ep->ctu_start]; | |
| 427 | + VVCTask *t = ft->tasks + rs; | |
| 428 | + | |
| 429 | + frame_thread_add_score(s, ft, t->rx, t->ry, VVC_TASK_STAGE_PARSE); | |
| 430 | +} | |
| 431 | + | |
| 432 | +static int run_init(VVCContext *s, VVCLocalContext *lc, VVCTask *t) | |
| 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 | + | |
| 402 | 456 | static void report_frame_progress(VVCFrameContext *fc, |
| 403 | 457 | const int ry, const VVCProgress idx) |
| 404 | 458 | { |
| @@ -547,6 +601,7 @@ static int run_alf(VVCContext *s, VVCLocalContext *lc, VVCTask *t) · 142 unmodified lines | ||
| 547 | 601 | #define VVC_THREAD_DEBUG |
| 548 | 602 | #ifdef VVC_THREAD_DEBUG |
| 549 | 603 | const static char* task_name[] = { |
| 604 | + "INIT", | |
| 550 | 605 | "P", |
| 551 | 606 | "I", |
| 552 | 607 | "R", |
| @@ -567,6 +622,7 @@ static void task_run_stage(VVCTask *t, VVCContext *s, VVCLocalContext *lc) · 14 unmodified lines | ||
| 567 | 622 | VVCFrameThread *ft = fc->ft; |
| 568 | 623 | const VVCTaskStage stage = t->stage; |
| 569 | 624 | static const run_func run[] = { |
| 625 | + run_init, | |
| 570 | 626 | run_parse, |
| 571 | 627 | run_inter, |
| 572 | 628 | run_recon, |
| @@ -726,7 +782,7 @@ int ff_vvc_frame_thread_init(VVCFrameContext *fc) · 153 unmodified lines | ||
| 726 | 782 | |
| 727 | 783 | for (int rs = 0; rs < ft->ctu_count; rs++) { |
| 728 | 784 | VVCTask *t = ft->tasks + rs; |
| 729 | − task_init(t, VVC_TASK_STAGE_PARSE, fc, rs % ft->ctu_width, rs / ft->ctu_width); | |
| 785 | + task_init(t, rs ? VVC_TASK_STAGE_PARSE : VVC_TASK_STAGE_INIT, fc, rs % ft->ctu_width, rs / ft->ctu_width); | |
| 730 | 786 | } |
| 731 | 787 | |
| 732 | 788 | memset(&ft->row_progress[0], 0, sizeof(ft->row_progress)); |
| @@ -745,59 +801,25 @@ fail: · 12 unmodified lines | ||
| 745 | 801 | return AVERROR(ENOMEM); |
| 746 | 802 | } |
| 747 | 803 | |
| 748 | −static void check_colocation(VVCContext *s, VVCTask *t) | |
| 749 | −{ | |
| 750 | − const VVCFrameContext *fc = t->fc; | |
| 751 | − | |
| 752 | − if (fc->ps.ph.r->ph_temporal_mvp_enabled_flag || fc->ps.sps->r->sps_sbtmvp_enabled_flag) { | |
| 753 | − VVCFrame *col = fc->ref->collocated_ref; | |
| 754 | − const int first_col = t->rx == fc->ps.pps->ctb_to_col_bd[t->rx]; | |
| 755 | − if (col && first_col) { | |
| 756 | − //we depend on bottom and right boundary, do not - 1 for y | |
| 757 | − const int y = (t->ry << fc->ps.sps->ctb_log2_size_y); | |
| 758 | − add_progress_listener(col, &t->col_listener, t, s, VVC_PROGRESS_MV, y); | |
| 759 | − return; | |
| 760 | − } | |
| 761 | − } | |
| 762 | − frame_thread_add_score(s, fc->ft, t->rx, t->ry, VVC_TASK_STAGE_PARSE); | |
| 763 | −} | |
| 764 | − | |
| 765 | −static void submit_entry_point(VVCContext *s, VVCFrameThread *ft, SliceContext *sc, EntryPoint *ep) | |
| 766 | −{ | |
| 767 | − const int rs = sc->sh.ctb_addr_in_curr_slice[ep->ctu_start]; | |
| 768 | − VVCTask *t = ft->tasks + rs; | |
| 769 | − | |
| 770 | − frame_thread_add_score(s, ft, t->rx, t->ry, VVC_TASK_STAGE_PARSE); | |
| 771 | −} | |
| 772 | − | |
| 773 | 804 | int ff_vvc_frame_submit(VVCContext *s, VVCFrameContext *fc) |
| 774 | 805 | { |
| 775 | 806 | VVCFrameThread *ft = fc->ft; |
| 776 | 807 | |
| 777 | − // We'll handle this in two passes: | |
| 778 | − // Pass 0 to initialize tasks with parser, this will help detect bit stream error | |
| 779 | − // Pass 1 to shedule location check and submit the entry point | |
| 780 | − for (int pass = 0; pass < 2; pass++) { | |
| 781 | − for (int i = 0; i < fc->nb_slices; i++) { | |
| 782 | − SliceContext *sc = fc->slices[i]; | |
| 783 | − for (int j = 0; j < sc->nb_eps; j++) { | |
| 784 | − EntryPoint *ep = sc->eps + j; | |
| 785 | − for (int k = ep->ctu_start; k < ep->ctu_end; k++) { | |
| 786 | − const int rs = sc->sh.ctb_addr_in_curr_slice[k]; | |
| 787 | − VVCTask *t = ft->tasks + rs; | |
| 788 | − if (pass) { | |
| 789 | − check_colocation(s, t); | |
| 790 | − } else { | |
| 791 | − const int ret = task_init_parse(t, sc, ep, k); | |
| 792 | − if (ret < 0) | |
| 793 | − return ret; | |
| 794 | − } | |
| 795 | − } | |
| 796 | − if (pass) | |
| 797 | − submit_entry_point(s, ft, sc, ep); | |
| 808 | + for (int i = 0; i < fc->nb_slices; i++) { | |
| 809 | + SliceContext *sc = fc->slices[i]; | |
| 810 | + for (int j = 0; j < sc->nb_eps; j++) { | |
| 811 | + EntryPoint *ep = sc->eps + j; | |
| 812 | + for (int k = ep->ctu_start; k < ep->ctu_end; k++) { | |
| 813 | + const int rs = sc->sh.ctb_addr_in_curr_slice[k]; | |
| 814 | + VVCTask *t = ft->tasks + rs; | |
| 815 | + const int ret = task_init_parse(t, sc, ep, k); | |
| 816 | + if (ret < 0) | |
| 817 | + return ret; | |
| 798 | 818 | } |
| 799 | 819 | } |
| 800 | 820 | } |
| 821 | + frame_thread_add_score(s, ft, 0, 0, VVC_TASK_STAGE_INIT); | |
| 822 | + | |
| 801 | 823 | return 0; |
| 802 | 824 | } |
| 803 | 825 | |
| @@ -32,5 +32,6 @@ int ff_vvc_frame_thread_init(VVCFrameContext *fc); · 31 unmodified lines | ||
| 32 | 32 | void ff_vvc_frame_thread_free(VVCFrameContext *fc); |
| 33 | 33 | int ff_vvc_frame_submit(VVCContext *s, VVCFrameContext *fc); |
| 34 | 34 | int ff_vvc_frame_wait(VVCContext *s, VVCFrameContext *fc); |
| 35 | +int ff_vvc_per_frame_init(VVCFrameContext *fc); | |
| 35 | 36 | |
| 36 | 37 | #endif // AVCODEC_VVC_THREAD_H |