Merge the 3 threading models into a single one

Merges the 3 threading parameters into a single `--threads=` argument.
Frame threading can still be controlled via the `--framedelay=` argument.
Internally, the threading model is now a global thread/task pool design.

Co-authored-by: Ronald S. Bultje <rsbultje@gmail.com>
This commit is contained in:
Victorien Le Couviour--Tuffet
2021-07-26 17:02:02 +02:00
parent 7b433e0772
commit 753eef833b
23 changed files with 1429 additions and 1186 deletions
+7 -10
View File
@@ -459,8 +459,8 @@ test-debian:
- ninja coverage-xml
- grep -Eo 'line-rate="[^"]+"' meson-logs/coverage.xml | head -n 1 |
grep -Eo '[0-9.]+' | awk '{ print "coverage:", $1 * 100 } '
- time meson test -v --suite testdata_seek-stress --test-args "--tilethreads 2 --framethreads 1 --pfthreads=2"
- time meson test -v --suite testdata_seek-stress --test-args "--tilethreads 2 --framethreads 2 --pfthreads=2"
- time meson test -v --suite testdata_seek-stress --test-args "--threads 2 --framedelay 1"
- time meson test -v --suite testdata_seek-stress --test-args "--threads 2 --framedelay 2"
coverage: '/^coverage: (\d+.\d+)$/'
artifacts:
expose_as: 'Coverage HTML report'
@@ -570,14 +570,11 @@ test-debian-tsan:
- ninja -C build
- cd build
- exit_code=0
- time meson test -v --setup=sanitizer --suite testdata-8 --suite testdata-10 --suite testdata-12 --test-args "--tilethreads 1 --framethreads 2 --pfthreads 1" || exit_code=$((exit_code + $?))
- time meson test -v --setup=sanitizer --suite testdata-8 --suite testdata-10 --suite testdata-12 --test-args "--tilethreads 2 --framethreads 1 --pfthreads 1" || exit_code=$((exit_code + $?))
- time meson test -v --setup=sanitizer --suite testdata-8 --suite testdata-10 --suite testdata-12 --test-args "--tilethreads 2 --framethreads 2 --pfthreads 1" || exit_code=$((exit_code + $?))
- time meson test -v --setup=sanitizer --suite testdata-8 --suite testdata-10 --suite testdata-12 --test-args "--tilethreads 2 --framethreads 1 --pfthreads 2" || exit_code=$((exit_code + $?))
- time meson test -v --setup=sanitizer --suite testdata-8 --suite testdata-10 --suite testdata-12 --test-args "--tilethreads 2 --framethreads 2 --pfthreads 2" || exit_code=$((exit_code + $?))
- time meson test -v --setup=sanitizer --suite testdata_seek-stress --test-args "--tilethreads 2 --framethreads 1 --pfthreads 2" || exit_code=$((exit_code + $?))
- time meson test -v --setup=sanitizer --suite testdata_seek-stress --test-args "--tilethreads 2 --framethreads 2 --pfthreads 2" || exit_code=$((exit_code + $?))
- time meson test -v --setup=sanitizer --suite oss-fuzz-asan --suite oss-fuzz-msan --suite oss-fuzz-ubsan || exit_code=$((exit_code + $?))
- time meson test -v --setup=sanitizer --suite testdata-8 --suite testdata-10 --suite testdata-12 --test-args "--threads 2 --framedelay 1" || exit_code=$((exit_code + $?))
- time meson test -v --setup=sanitizer --suite testdata-8 --suite testdata-10 --suite testdata-12 --test-args "--threads 2 --framedelay 2" || exit_code=$((exit_code + $?))
- time meson test -v --setup=sanitizer --suite testdata_seek-stress --test-args "--threads 2 --framedelay 1" || exit_code=$((exit_code + $?))
- time meson test -v --setup=sanitizer --suite testdata_seek-stress --test-args "--threads 2 --framedelay 2" || exit_code=$((exit_code + $?))
- time meson test -v --setup=sanitizer --suite oss-fuzz-asan --suite oss-fuzz-msan --suite oss-fuzz-ubsan || exit_code=$((exit_code + $?))
- if [ $exit_code -ne 0 ]; then exit $exit_code; fi
test-win64:
+10 -16
View File
@@ -147,9 +147,8 @@ static void dp_rd_ctx_parse_args(Dav1dPlayRenderContext *rd_ctx,
static const char short_opts[] = "i:vuzgr:";
enum {
ARG_FRAME_THREADS = 256,
ARG_TILE_THREADS,
ARG_POSTFILTER_THREADS,
ARG_THREADS = 256,
ARG_FRAME_DELAY,
ARG_HIGH_QUALITY,
};
@@ -158,9 +157,8 @@ static void dp_rd_ctx_parse_args(Dav1dPlayRenderContext *rd_ctx,
{ "input", 1, NULL, 'i' },
{ "version", 0, NULL, 'v' },
{ "untimed", 0, NULL, 'u' },
{ "framethreads", 1, NULL, ARG_FRAME_THREADS },
{ "tilethreads", 1, NULL, ARG_TILE_THREADS },
{ "pfthreads", 1, NULL, ARG_POSTFILTER_THREADS },
{ "threads", 1, NULL, ARG_THREADS },
{ "framedelay", 1, NULL, ARG_FRAME_DELAY },
{ "highquality", 0, NULL, ARG_HIGH_QUALITY },
{ "zerocopy", 0, NULL, 'z' },
{ "gpugrain", 0, NULL, 'g' },
@@ -191,17 +189,13 @@ static void dp_rd_ctx_parse_args(Dav1dPlayRenderContext *rd_ctx,
case 'r':
settings->renderer_name = optarg;
break;
case ARG_FRAME_THREADS:
lib_settings->n_frame_threads =
parse_unsigned(optarg, ARG_FRAME_THREADS, argv[0]);
case ARG_THREADS:
lib_settings->n_threads =
parse_unsigned(optarg, ARG_THREADS, argv[0]);
break;
case ARG_TILE_THREADS:
lib_settings->n_tile_threads =
parse_unsigned(optarg, ARG_TILE_THREADS, argv[0]);
break;
case ARG_POSTFILTER_THREADS:
lib_settings->n_postfilter_threads =
parse_unsigned(optarg, ARG_POSTFILTER_THREADS, argv[0]);
case ARG_FRAME_DELAY:
lib_settings->max_frame_delay =
parse_unsigned(optarg, ARG_FRAME_DELAY, argv[0]);
break;
default:
dp_settings_print_usage(argv[0], NULL);
+5 -7
View File
@@ -43,9 +43,8 @@ extern "C" {
typedef struct Dav1dContext Dav1dContext;
typedef struct Dav1dRef Dav1dRef;
#define DAV1D_MAX_FRAME_THREADS 256
#define DAV1D_MAX_TILE_THREADS 64
#define DAV1D_MAX_POSTFILTER_THREADS 256
#define DAV1D_MAX_THREADS 256
#define DAV1D_MAX_FRAME_DELAY 256
typedef struct Dav1dLogger {
void *cookie; ///< Custom data to pass to the callback.
@@ -60,16 +59,15 @@ typedef struct Dav1dLogger {
} Dav1dLogger;
typedef struct Dav1dSettings {
int n_frame_threads;
int n_tile_threads;
int n_threads;
int max_frame_delay; ///< internally clipped to $n_threads. Set to 1 for low-latency decoding.
int apply_grain;
int operating_point; ///< select an operating point for scalable AV1 bitstreams (0 - 31)
int all_layers; ///< output all spatial layers of a scalable AV1 biststream
unsigned frame_size_limit; ///< maximum frame size, in pixels (0 = unlimited)
Dav1dPicAllocator allocator; ///< Picture allocator callback.
Dav1dLogger logger; ///< Logger callback.
int n_postfilter_threads;
uint8_t reserved[28]; ///< reserved for future use
uint8_t reserved[32]; ///< reserved for future use
} Dav1dSettings;
/**
+1 -1
View File
@@ -30,7 +30,7 @@ project('dav1d', ['c'],
'b_ndebug=if-release'],
meson_version: '>= 0.49.0')
dav1d_soname_version = '5.1.1'
dav1d_soname_version = '6.0.0'
dav1d_api_version_array = dav1d_soname_version.split('.')
dav1d_api_version_major = dav1d_api_version_array[0]
dav1d_api_version_minor = dav1d_api_version_array[1]
+2 -22
View File
@@ -4096,16 +4096,15 @@ void dav1d_cdf_thread_copy(CdfContext *const dst, const CdfThreadContext *const
}
int dav1d_cdf_thread_alloc(Dav1dContext *const c, CdfThreadContext *const cdf,
struct thread_data *const t)
const int have_frame_mt)
{
cdf->ref = dav1d_ref_create_using_pool(c->cdf_pool,
sizeof(CdfContext) + sizeof(atomic_uint));
if (!cdf->ref) return DAV1D_ERR(ENOMEM);
cdf->data.cdf = cdf->ref->data;
if (t) {
if (have_frame_mt) {
cdf->progress = (atomic_uint *) &cdf->data.cdf[1];
atomic_init(cdf->progress, 0);
cdf->t = t;
}
return 0;
}
@@ -4123,22 +4122,3 @@ void dav1d_cdf_thread_unref(CdfThreadContext *const cdf) {
dav1d_ref_dec(&cdf->ref);
memset(cdf, 0, sizeof(*cdf));
}
void dav1d_cdf_thread_wait(CdfThreadContext *const cdf) {
if (!cdf->t) return;
if (atomic_load(cdf->progress)) return;
pthread_mutex_lock(&cdf->t->lock);
while (!atomic_load(cdf->progress))
pthread_cond_wait(&cdf->t->cond, &cdf->t->lock);
pthread_mutex_unlock(&cdf->t->lock);
}
void dav1d_cdf_thread_signal(CdfThreadContext *const cdf) {
if (!cdf->t) return;
pthread_mutex_lock(&cdf->t->lock);
atomic_store(cdf->progress, 1);
pthread_cond_broadcast(&cdf->t->cond);
pthread_mutex_unlock(&cdf->t->lock);
}
+1 -8
View File
@@ -135,23 +135,16 @@ typedef struct CdfThreadContext {
CdfContext *cdf; // if ref != NULL
unsigned qcat; // if ref == NULL, from static CDF tables
} data;
struct thread_data *t;
atomic_uint *progress;
} CdfThreadContext;
void dav1d_cdf_thread_init_static(CdfThreadContext *cdf, int qidx);
int dav1d_cdf_thread_alloc(Dav1dContext *c, CdfThreadContext *cdf,
struct thread_data *t);
const int have_frame_mt);
void dav1d_cdf_thread_copy(CdfContext *dst, const CdfThreadContext *src);
void dav1d_cdf_thread_ref(CdfThreadContext *dst, CdfThreadContext *src);
void dav1d_cdf_thread_unref(CdfThreadContext *cdf);
void dav1d_cdf_thread_update(const Dav1dFrameHeader *hdr, CdfContext *dst,
const CdfContext *src);
/*
* These are binary signals (so a signal is either "done" or "not done").
*/
void dav1d_cdf_thread_wait(CdfThreadContext *cdf);
void dav1d_cdf_thread_signal(CdfThreadContext *cdf);
#endif /* DAV1D_SRC_CDF_H */
+422 -351
View File
File diff suppressed because it is too large Load Diff
+78 -48
View File
@@ -34,8 +34,7 @@
typedef struct Dav1dFrameContext Dav1dFrameContext;
typedef struct Dav1dTileState Dav1dTileState;
typedef struct Dav1dTileContext Dav1dTileContext;
typedef struct Dav1dPostFilterContext Dav1dPostFilterContext;
typedef struct Dav1dTaskContext Dav1dTaskContext;
typedef struct Dav1dTask Dav1dTask;
#include "common/attributes.h"
@@ -78,8 +77,8 @@ struct Dav1dContext {
Dav1dFrameContext *fc;
unsigned n_fc;
Dav1dPostFilterContext *pfc;
unsigned n_pfc;
Dav1dTaskContext *tc;
unsigned n_tc;
// cache of OBUs that make up a single frame before we submit them
// to a frame worker to be decoded
@@ -112,14 +111,20 @@ struct Dav1dContext {
unsigned next;
} frame_thread;
// postfilter threading (refer to pfc[] for per_thread thingies)
struct PostFilterThreadData {
// task threading (refer to tc[] for per_thread thingies)
struct TaskThreadData {
pthread_mutex_t lock;
pthread_cond_t cond;
struct Dav1dTask *tasks;
int frame_cnt;
atomic_uint first;
unsigned cur;
// This is used for delayed reset of the task cur pointer when
// such operation is needed but the thread doesn't enter a critical
// section (typically when executing the next sbrow task locklessly).
// See src/thread_task.c:reset_task_cur().
atomic_uint reset_task_cur;
atomic_int cond_signaled;
int inited;
} postfilter_thread;
} task_thread;
// reference/entropy state
Dav1dMemPool *segmap_pool;
@@ -160,6 +165,29 @@ struct Dav1dContext {
Dav1dMemPool *picture_pool;
};
enum TaskType {
DAV1D_TASK_TYPE_INIT,
DAV1D_TASK_TYPE_TILE_ENTROPY,
DAV1D_TASK_TYPE_TILE_RECONSTRUCTION,
DAV1D_TASK_TYPE_DEBLOCK_COLS,
DAV1D_TASK_TYPE_DEBLOCK_ROWS,
DAV1D_TASK_TYPE_CDEF,
DAV1D_TASK_TYPE_SUPER_RESOLUTION,
DAV1D_TASK_TYPE_LOOP_RESTORATION,
DAV1D_TASK_TYPE_ENTROPY_PROGRESS,
};
struct Dav1dTask {
unsigned frame_idx; // frame thread id
enum TaskType type; // task work
int sby; // sbrow
// task dependencies
int recon_progress, deblock_progress, cdef_progress, lr_progress;
int deps_skip;
struct Dav1dTask *next; // only used in task queue
};
struct Dav1dFrameContext {
Dav1dRef *seq_hdr_ref;
Dav1dSequenceHeader *seq_hdr;
@@ -189,8 +217,6 @@ struct Dav1dFrameContext {
int resize_step[2 /* y, uv */], resize_start[2 /* y, uv */];
const Dav1dContext *c;
Dav1dTileContext *tc;
int n_tc;
Dav1dTileState *ts;
int n_ts;
const Dav1dDSPContext *dsp;
@@ -198,7 +224,8 @@ struct Dav1dFrameContext {
recon_b_intra_fn recon_b_intra;
recon_b_inter_fn recon_b_inter;
filter_sbrow_fn filter_sbrow;
filter_sbrow_fn filter_sbrow_deblock;
filter_sbrow_fn filter_sbrow_deblock_cols;
filter_sbrow_fn filter_sbrow_deblock_rows;
filter_sbrow_fn filter_sbrow_cdef;
filter_sbrow_fn filter_sbrow_resize;
filter_sbrow_fn filter_sbrow_lr;
@@ -219,8 +246,9 @@ struct Dav1dFrameContext {
int bitdepth_max;
struct {
struct thread_data td;
int pass, die;
int next_tile_row[2 /* 0: reconstruction, 1: entropy */];
int entropy_progress;
atomic_int deblock_progress, cdef_progress, lr_progress; // in sby units
// indexed using t->by * f->b4_stride + t->bx
Av1Block *b;
struct CodedBlockInfo {
@@ -254,32 +282,34 @@ struct Dav1dFrameContext {
pixel *lr_lpf_line[3 /* plane */];
// in-loop filter per-frame state keeping
int tile_row; // for carry-over at tile row edges
uint8_t *start_of_tile_row;
int start_of_tile_row_sz;
pixel *p[3], *sr_p[3];
Av1Filter *mask_ptr, *prev_mask_ptr;
int restore_planes; // enum LrRestorePlanes
struct {
pthread_cond_t cond;
struct PostFilterThreadData *pftd;
struct Dav1dTask *tasks;
int num_tasks;
int npf;
int done;
int inited;
} thread;
} lf;
struct {
pthread_cond_t cond;
struct TaskThreadData *ttd;
struct Dav1dTask *tasks, *tile_tasks[2], init_task;
int num_tasks, num_tile_tasks;
int done[2];
int update_set; // whether we need to update CDF reference
atomic_int error;
int task_counter;
struct Dav1dTask *task_head, *task_tail;
// Points to the task directly before the cur pointer in the queue.
// This cur pointer is theoretical here, we actually keep track of the
// "prev_t" variable. This is needed to not loose the tasks in
// [head;cur-1] when picking one for execution.
struct Dav1dTask *task_cur_prev;
} task_thread;
// threading (refer to tc[] for per-thread things)
struct FrameTileThreadData {
uint64_t available;
pthread_mutex_t lock;
pthread_cond_t cond, icond;
int tasks_left, num_tasks;
int (*task_idx_to_sby_and_tile_idx)[2];
int titsati_sz, titsati_init[2];
uint16_t titsati_index_rows[1 + DAV1D_MAX_TILE_ROWS];
int inited;
int (*lowest_pixel_mem)[7][2];
int lowest_pixel_mem_sz;
} tile_thread;
};
@@ -292,15 +322,16 @@ struct Dav1dTileState {
int col, row; // in tile units
} tiling;
atomic_int progress; // in sby units, TILE_ERROR after a decoding error
struct {
pthread_mutex_t lock;
pthread_cond_t cond;
} tile_thread;
// in sby units, TILE_ERROR after a decoding error
atomic_int progress[2 /* 0: reconstruction, 1: entropy */];
struct {
uint8_t *pal_idx;
coef *cf;
} frame_thread;
} frame_thread[2 /* 0: reconstruction, 1: entropy */];
// in fullpel units, [0] = Y, [1] = UV, used for progress requirements
// each entry is one tile-sbrow; middle index is refidx
int (*lowest_pixel)[7][2];
uint16_t dqmem[DAV1D_MAX_SEGMENTS][3 /* plane */][2 /* dc/ac */];
const uint16_t (*dq)[3][2];
@@ -313,7 +344,8 @@ struct Dav1dTileState {
Av1RestorationUnit *lr_ref[3];
};
struct Dav1dTileContext {
struct Dav1dTaskContext {
const Dav1dContext *c;
const Dav1dFrameContext *f;
Dav1dTileState *ts;
int bx, by;
@@ -376,18 +408,16 @@ struct Dav1dTileContext {
// keeps it accessible
enum Filter2d tl_4x4_filter;
struct {
int pass;
} frame_thread;
struct {
struct thread_data td;
struct TaskThreadData *ttd;
struct FrameTileThreadData *fttd;
int flushed;
int die;
} tile_thread;
};
struct Dav1dPostFilterContext {
Dav1dContext *c;
struct thread_data td;
int flushed;
int die;
} task_thread;
};
#endif /* DAV1D_SRC_INTERNAL_H */
+6 -3
View File
@@ -35,8 +35,11 @@
#include "src/internal.h"
#include "src/levels.h"
void bytefn(dav1d_loopfilter_sbrow)(const Dav1dFrameContext *f,
pixel *const p[3], Av1Filter *lflvl,
int sby, int start_of_tile_row);
void bytefn(dav1d_loopfilter_sbrow_cols)(const Dav1dFrameContext *f,
pixel *const p[3], Av1Filter *lflvl,
int sby, int start_of_tile_row);
void bytefn(dav1d_loopfilter_sbrow_rows)(const Dav1dFrameContext *f,
pixel *const p[3], Av1Filter *lflvl,
int sby);
#endif /* DAV1D_SRC_LF_APPLY_H */
+31 -11
View File
@@ -170,13 +170,12 @@ static inline void filter_plane_rows_uv(const Dav1dFrameContext *const f,
}
}
void bytefn(dav1d_loopfilter_sbrow)(const Dav1dFrameContext *const f,
pixel *const p[3], Av1Filter *const lflvl,
int sby, const int start_of_tile_row)
void bytefn(dav1d_loopfilter_sbrow_cols)(const Dav1dFrameContext *const f,
pixel *const p[3], Av1Filter *const lflvl,
int sby, const int start_of_tile_row)
{
int x, have_left;
// Don't filter outside the frame
const int have_top = sby > 0;
const int is_sb64 = !f->seq_hdr->sb128;
const int starty4 = (sby & is_sb64) << 4;
const int sbsz = 32 >> is_sb64;
@@ -271,13 +270,6 @@ void bytefn(dav1d_loopfilter_sbrow)(const Dav1dFrameContext *const f,
imin(32, f->w4 - x * 32), starty4, endy4);
}
level_ptr = f->lf.level + f->b4_stride * sby * sbsz;
for (ptr = p[0], x = 0; x < f->sb128w; x++, ptr += 128, level_ptr += 32) {
filter_plane_rows_y(f, have_top, level_ptr, f->b4_stride,
lflvl[x].filter_y[1], ptr, f->cur.stride[0],
imin(32, f->w4 - x * 32), starty4, endy4);
}
if (!f->frame_hdr->loopfilter.level_u && !f->frame_hdr->loopfilter.level_v)
return;
@@ -292,7 +284,35 @@ void bytefn(dav1d_loopfilter_sbrow)(const Dav1dFrameContext *const f,
(imin(32, f->w4 - x * 32) + ss_hor) >> ss_hor,
starty4 >> ss_ver, uv_endy4, ss_ver);
}
}
void bytefn(dav1d_loopfilter_sbrow_rows)(const Dav1dFrameContext *const f,
pixel *const p[3], Av1Filter *const lflvl,
int sby)
{
int x;
// Don't filter outside the frame
const int have_top = sby > 0;
const int is_sb64 = !f->seq_hdr->sb128;
const int starty4 = (sby & is_sb64) << 4;
const int sbsz = 32 >> is_sb64;
const int ss_ver = f->cur.p.layout == DAV1D_PIXEL_LAYOUT_I420;
const int ss_hor = f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I444;
const unsigned endy4 = starty4 + imin(f->h4 - sby * sbsz, sbsz);
const unsigned uv_endy4 = (endy4 + ss_ver) >> ss_ver;
pixel *ptr;
uint8_t (*level_ptr)[4] = f->lf.level + f->b4_stride * sby * sbsz;
for (ptr = p[0], x = 0; x < f->sb128w; x++, ptr += 128, level_ptr += 32) {
filter_plane_rows_y(f, have_top, level_ptr, f->b4_stride,
lflvl[x].filter_y[1], ptr, f->cur.stride[0],
imin(32, f->w4 - x * 32), starty4, endy4);
}
if (!f->frame_hdr->loopfilter.level_u && !f->frame_hdr->loopfilter.level_v)
return;
ptrdiff_t uv_off;
level_ptr = f->lf.level + f->b4_stride * (sby * sbsz >> ss_ver);
for (uv_off = 0, x = 0; x < f->sb128w;
x++, uv_off += 128 >> ss_hor, level_ptr += 32 >> ss_hor)
+108 -194
View File
@@ -63,9 +63,8 @@ COLD const char *dav1d_version(void) {
}
COLD void dav1d_default_settings(Dav1dSettings *const s) {
s->n_frame_threads = 1;
s->n_tile_threads = 1;
s->n_postfilter_threads = 1;
s->max_frame_delay = 8; // set to 1 to do low-latency decoding
s->n_threads = 1;
s->apply_grain = 1;
s->allocator.cookie = NULL;
s->allocator.alloc_picture_callback = dav1d_default_picture_alloc;
@@ -101,12 +100,10 @@ COLD int dav1d_open(Dav1dContext **const c_out, const Dav1dSettings *const s) {
validate_input_or_ret(c_out != NULL, DAV1D_ERR(EINVAL));
validate_input_or_ret(s != NULL, DAV1D_ERR(EINVAL));
validate_input_or_ret(s->n_postfilter_threads >= 1 &&
s->n_postfilter_threads <= DAV1D_MAX_POSTFILTER_THREADS, DAV1D_ERR(EINVAL));
validate_input_or_ret(s->n_tile_threads >= 1 &&
s->n_tile_threads <= DAV1D_MAX_TILE_THREADS, DAV1D_ERR(EINVAL));
validate_input_or_ret(s->n_frame_threads >= 1 &&
s->n_frame_threads <= DAV1D_MAX_FRAME_THREADS, DAV1D_ERR(EINVAL));
validate_input_or_ret(s->n_threads >= 1 &&
s->n_threads <= DAV1D_MAX_THREADS, DAV1D_ERR(EINVAL));
validate_input_or_ret(s->max_frame_delay >= 1 &&
s->max_frame_delay <= DAV1D_MAX_FRAME_DELAY, DAV1D_ERR(EINVAL));
validate_input_or_ret(s->allocator.alloc_picture_callback != NULL,
DAV1D_ERR(EINVAL));
validate_input_or_ret(s->allocator.release_picture_callback != NULL,
@@ -166,44 +163,25 @@ COLD int dav1d_open(Dav1dContext **const c_out, const Dav1dSettings *const s) {
c->flush = &c->flush_mem;
atomic_init(c->flush, 0);
c->n_pfc = s->n_postfilter_threads;
c->n_fc = s->n_frame_threads;
c->fc = dav1d_alloc_aligned(sizeof(*c->fc) * s->n_frame_threads, 32);
c->n_tc = s->n_threads;
c->n_fc = imin(s->n_threads, s->max_frame_delay);
c->fc = dav1d_alloc_aligned(sizeof(*c->fc) * c->n_fc, 32);
if (!c->fc) goto error;
memset(c->fc, 0, sizeof(*c->fc) * s->n_frame_threads);
memset(c->fc, 0, sizeof(*c->fc) * c->n_fc);
if (c->n_pfc > 1) {
c->pfc = dav1d_alloc_aligned(sizeof(*c->pfc) * s->n_postfilter_threads, 32);
if (!c->pfc) goto error;
memset(c->pfc, 0, sizeof(*c->pfc) * s->n_postfilter_threads);
if (pthread_mutex_init(&c->postfilter_thread.lock, NULL)) goto error;
if (pthread_cond_init(&c->postfilter_thread.cond, NULL)) {
pthread_mutex_destroy(&c->postfilter_thread.lock);
c->tc = dav1d_alloc_aligned(sizeof(*c->tc) * s->n_threads, 64);
if (!c->tc) goto error;
memset(c->tc, 0, sizeof(*c->tc) * s->n_threads);
if (c->n_tc > 1) {
if (pthread_mutex_init(&c->task_thread.lock, NULL)) goto error;
if (pthread_cond_init(&c->task_thread.cond, NULL)) {
pthread_mutex_destroy(&c->task_thread.lock);
goto error;
}
c->postfilter_thread.inited = 1;
for (int n = 0; n < s->n_frame_threads; n++) {
Dav1dFrameContext *const f = &c->fc[n];
if (pthread_cond_init(&f->lf.thread.cond, NULL)) goto error;
f->lf.thread.pftd = &c->postfilter_thread;
f->lf.thread.done = 1;
f->lf.thread.inited = 1;
}
for (int n = 0; n < s->n_postfilter_threads; ++n) {
Dav1dPostFilterContext *const pf = &c->pfc[n];
pf->c = c;
if (pthread_mutex_init(&pf->td.lock, NULL)) goto error;
if (pthread_cond_init(&pf->td.cond, NULL)) {
pthread_mutex_destroy(&pf->td.lock);
goto error;
}
if (pthread_create(&pf->td.thread, &thread_attr, dav1d_postfilter_task, pf)) {
pthread_cond_destroy(&c->postfilter_thread.cond);
pthread_mutex_destroy(&c->postfilter_thread.lock);
goto error;
}
pf->td.inited = 1;
}
c->task_thread.cur = c->n_fc;
atomic_init(&c->task_thread.reset_task_cur, UINT_MAX);
atomic_init(&c->task_thread.cond_signaled, 0);
c->task_thread.inited = 1;
}
if (c->n_fc > 1) {
@@ -211,59 +189,34 @@ COLD int dav1d_open(Dav1dContext **const c_out, const Dav1dSettings *const s) {
calloc(c->n_fc, sizeof(*c->frame_thread.out_delayed));
if (!c->frame_thread.out_delayed) goto error;
}
for (int n = 0; n < s->n_frame_threads; n++) {
for (unsigned n = 0; n < c->n_fc; n++) {
Dav1dFrameContext *const f = &c->fc[n];
if (c->n_tc > 1)
if (pthread_cond_init(&f->task_thread.cond, NULL)) goto error;
f->c = c;
f->task_thread.ttd = &c->task_thread;
f->lf.last_sharpness = -1;
f->n_tc = s->n_tile_threads;
f->tc = dav1d_alloc_aligned(sizeof(*f->tc) * s->n_tile_threads, 64);
if (!f->tc) goto error;
memset(f->tc, 0, sizeof(*f->tc) * s->n_tile_threads);
if (f->n_tc > 1) {
if (pthread_mutex_init(&f->tile_thread.lock, NULL)) goto error;
if (pthread_cond_init(&f->tile_thread.cond, NULL)) {
pthread_mutex_destroy(&f->tile_thread.lock);
goto error;
}
if (pthread_cond_init(&f->tile_thread.icond, NULL)) {
pthread_mutex_destroy(&f->tile_thread.lock);
pthread_cond_destroy(&f->tile_thread.cond);
goto error;
}
f->tile_thread.inited = 1;
}
for (int m = 0; m < s->n_tile_threads; m++) {
Dav1dTileContext *const t = &f->tc[m];
t->f = f;
memset(t->cf_16bpc, 0, sizeof(t->cf_16bpc));
if (f->n_tc > 1) {
if (pthread_mutex_init(&t->tile_thread.td.lock, NULL)) goto error;
if (pthread_cond_init(&t->tile_thread.td.cond, NULL)) {
pthread_mutex_destroy(&t->tile_thread.td.lock);
goto error;
}
t->tile_thread.fttd = &f->tile_thread;
if (pthread_create(&t->tile_thread.td.thread, &thread_attr, dav1d_tile_task, t)) {
pthread_cond_destroy(&t->tile_thread.td.cond);
pthread_mutex_destroy(&t->tile_thread.td.lock);
goto error;
}
t->tile_thread.td.inited = 1;
}
}
dav1d_refmvs_init(&f->rf);
if (c->n_fc > 1) {
if (pthread_mutex_init(&f->frame_thread.td.lock, NULL)) goto error;
if (pthread_cond_init(&f->frame_thread.td.cond, NULL)) {
pthread_mutex_destroy(&f->frame_thread.td.lock);
}
for (int m = 0; m < s->n_threads; m++) {
Dav1dTaskContext *const t = &c->tc[m];
t->f = &c->fc[0];
t->task_thread.ttd = &c->task_thread;
t->c = c;
memset(t->cf_16bpc, 0, sizeof(t->cf_16bpc));
if (c->n_tc > 1) {
if (pthread_mutex_init(&t->task_thread.td.lock, NULL)) goto error;
if (pthread_cond_init(&t->task_thread.td.cond, NULL)) {
pthread_mutex_destroy(&t->task_thread.td.lock);
goto error;
}
if (pthread_create(&f->frame_thread.td.thread, &thread_attr, dav1d_frame_task, f)) {
pthread_cond_destroy(&f->frame_thread.td.cond);
pthread_mutex_destroy(&f->frame_thread.td.lock);
if (pthread_create(&t->task_thread.td.thread, &thread_attr, dav1d_worker_task, t)) {
pthread_cond_destroy(&t->task_thread.td.cond);
pthread_mutex_destroy(&t->task_thread.td.lock);
goto error;
}
f->frame_thread.td.inited = 1;
t->task_thread.td.inited = 1;
}
}
dav1d_refmvs_dsp_init(&c->refmvs_dsp);
@@ -395,15 +348,23 @@ static int drain_picture(Dav1dContext *const c, Dav1dPicture *const out) {
do {
const unsigned next = c->frame_thread.next;
Dav1dFrameContext *const f = &c->fc[next];
pthread_mutex_lock(&f->frame_thread.td.lock);
pthread_mutex_lock(&c->task_thread.lock);
while (f->n_tile_data > 0)
pthread_cond_wait(&f->frame_thread.td.cond,
&f->frame_thread.td.lock);
pthread_mutex_unlock(&f->frame_thread.td.lock);
pthread_cond_wait(&f->task_thread.cond,
&f->task_thread.ttd->lock);
Dav1dThreadPicture *const out_delayed =
&c->frame_thread.out_delayed[next];
if (out_delayed->p.data[0]) {
if (atomic_load(&c->task_thread.first) + 1 < c->n_fc)
atomic_fetch_add(&c->task_thread.first, 1);
else
atomic_store(&c->task_thread.first, 0);
if (c->task_thread.cur && c->task_thread.cur < c->n_fc)
c->task_thread.cur--;
}
if (++c->frame_thread.next == c->n_fc)
c->frame_thread.next = 0;
pthread_mutex_unlock(&c->task_thread.lock);
if (out_delayed->p.data[0]) {
const unsigned progress =
atomic_load_explicit(&out_delayed->progress[1],
@@ -510,51 +471,43 @@ void dav1d_flush(Dav1dContext *const c) {
dav1d_ref_dec(&c->content_light_ref);
dav1d_ref_dec(&c->itut_t35_ref);
if (c->n_fc == 1 && c->n_pfc == 1) return;
if (c->n_fc == 1 && c->n_tc == 1) return;
atomic_store(c->flush, 1);
// stop running tasks in worker threads
if (c->n_tc > 1) {
pthread_mutex_lock(&c->task_thread.lock);
for (unsigned i = 0; i < c->n_tc; i++) {
Dav1dTaskContext *const pf = &c->tc[i];
while (!pf->task_thread.flushed) {
pthread_cond_wait(&pf->task_thread.td.cond, &c->task_thread.lock);
}
}
for (unsigned i = 0; i < c->n_fc; i++) {
c->fc[i].task_thread.task_head = NULL;
c->fc[i].task_thread.task_tail = NULL;
c->fc[i].task_thread.task_cur_prev = NULL;
}
atomic_init(&c->task_thread.first, 0);
c->task_thread.cur = c->n_fc;
atomic_store(&c->task_thread.reset_task_cur, INT_MAX);
atomic_store(&c->task_thread.cond_signaled, 0);
pthread_mutex_unlock(&c->task_thread.lock);
}
// wait for threads to complete flushing
if (c->n_pfc > 1)
pthread_mutex_lock(&c->postfilter_thread.lock);
atomic_store(c->flush, 1);
if (c->n_pfc > 1) {
pthread_cond_broadcast(&c->postfilter_thread.cond);
pthread_mutex_unlock(&c->postfilter_thread.lock);
}
if (c->n_fc == 1) goto skip_ft_flush;
for (unsigned n = 0, next = c->frame_thread.next; n < c->n_fc; n++, next++) {
if (next == c->n_fc) next = 0;
Dav1dFrameContext *const f = &c->fc[next];
pthread_mutex_lock(&f->frame_thread.td.lock);
if (f->n_tile_data > 0) {
while (f->n_tile_data > 0)
pthread_cond_wait(&f->frame_thread.td.cond,
&f->frame_thread.td.lock);
assert(!f->cur.data[0]);
}
pthread_mutex_unlock(&f->frame_thread.td.lock);
Dav1dThreadPicture *const out_delayed =
&c->frame_thread.out_delayed[next];
if (out_delayed->p.data[0])
dav1d_thread_picture_unref(out_delayed);
}
c->frame_thread.next = 0;
skip_ft_flush:
if (c->n_pfc > 1) {
for (unsigned i = 0; i < c->n_pfc; ++i) {
Dav1dPostFilterContext *const pf = &c->pfc[i];
pthread_mutex_lock(&pf->td.lock);
if (!pf->flushed)
pthread_cond_wait(&pf->td.cond, &pf->td.lock);
pf->flushed = 0;
pthread_mutex_unlock(&pf->td.lock);
}
pthread_mutex_lock(&c->postfilter_thread.lock);
c->postfilter_thread.tasks = NULL;
pthread_mutex_unlock(&c->postfilter_thread.lock);
for (unsigned i = 0; i < c->n_fc; ++i) {
freep(&c->fc[i].lf.thread.tasks);
c->fc[i].lf.thread.num_tasks = 0;
if (c->n_fc > 1) {
for (unsigned n = 0, next = c->frame_thread.next; n < c->n_fc; n++, next++) {
if (next == c->n_fc) next = 0;
Dav1dFrameContext *const f = &c->fc[next];
dav1d_decode_frame_exit(f, 0);
f->n_tile_data = 0;
Dav1dThreadPicture *out_delayed = &c->frame_thread.out_delayed[next];
if (out_delayed->p.data[0]) {
dav1d_thread_picture_unref(out_delayed);
}
}
c->frame_thread.next = 0;
}
atomic_store(c->flush, 0);
}
@@ -570,82 +523,42 @@ static COLD void close_internal(Dav1dContext **const c_out, int flush) {
if (flush) dav1d_flush(c);
if (c->pfc) {
struct PostFilterThreadData *pftd = &c->postfilter_thread;
if (pftd->inited) {
pthread_mutex_lock(&pftd->lock);
for (unsigned n = 0; n < c->n_pfc && c->pfc[n].td.inited; n++)
c->pfc[n].die = 1;
pthread_cond_broadcast(&pftd->cond);
pthread_mutex_unlock(&pftd->lock);
for (unsigned n = 0; n < c->n_pfc && c->pfc[n].td.inited; n++) {
pthread_join(c->pfc[n].td.thread, NULL);
pthread_cond_destroy(&c->pfc[n].td.cond);
pthread_mutex_destroy(&c->pfc[n].td.lock);
if (c->tc) {
struct TaskThreadData *ttd = &c->task_thread;
if (ttd->inited) {
pthread_mutex_lock(&ttd->lock);
for (unsigned n = 0; n < c->n_tc && c->tc[n].task_thread.td.inited; n++)
c->tc[n].task_thread.die = 1;
pthread_cond_broadcast(&ttd->cond);
pthread_mutex_unlock(&ttd->lock);
for (unsigned n = 0; n < c->n_tc; n++) {
Dav1dTaskContext *const pf = &c->tc[n];
if (!pf->task_thread.td.inited) break;
pthread_join(pf->task_thread.td.thread, NULL);
pthread_cond_destroy(&pf->task_thread.td.cond);
pthread_mutex_destroy(&pf->task_thread.td.lock);
}
pthread_cond_destroy(&pftd->cond);
pthread_mutex_destroy(&pftd->lock);
pthread_cond_destroy(&ttd->cond);
pthread_mutex_destroy(&ttd->lock);
}
dav1d_free_aligned(c->pfc);
dav1d_free_aligned(c->tc);
}
for (unsigned n = 0; c->fc && n < c->n_fc; n++) {
Dav1dFrameContext *const f = &c->fc[n];
// clean-up threading stuff
if (c->n_fc > 1 && f->frame_thread.td.inited) {
pthread_mutex_lock(&f->frame_thread.td.lock);
f->frame_thread.die = 1;
pthread_cond_signal(&f->frame_thread.td.cond);
pthread_mutex_unlock(&f->frame_thread.td.lock);
pthread_join(f->frame_thread.td.thread, NULL);
if (c->n_fc > 1) {
freep(&f->frame_thread.b);
dav1d_freep_aligned(&f->frame_thread.pal_idx);
dav1d_freep_aligned(&f->frame_thread.cf);
freep(&f->frame_thread.tile_start_off);
dav1d_freep_aligned(&f->frame_thread.pal);
freep(&f->frame_thread.cbi);
pthread_mutex_destroy(&f->frame_thread.td.lock);
pthread_cond_destroy(&f->frame_thread.td.cond);
}
if (f->n_tc > 1 && f->tc && f->tile_thread.inited) {
pthread_mutex_lock(&f->tile_thread.lock);
for (int m = 0; m < f->n_tc; m++) {
Dav1dTileContext *const t = &f->tc[m];
t->tile_thread.die = 1;
// mark not created tile threads as available
if (!t->tile_thread.td.inited)
f->tile_thread.available |= 1ULL<<m;
}
pthread_cond_broadcast(&f->tile_thread.cond);
while (f->tile_thread.available != ~0ULL >> (64 - f->n_tc))
pthread_cond_wait(&f->tile_thread.icond,
&f->tile_thread.lock);
pthread_mutex_unlock(&f->tile_thread.lock);
for (int m = 0; m < f->n_tc; m++) {
Dav1dTileContext *const t = &f->tc[m];
if (f->n_tc > 1 && t->tile_thread.td.inited) {
pthread_join(t->tile_thread.td.thread, NULL);
pthread_mutex_destroy(&t->tile_thread.td.lock);
pthread_cond_destroy(&t->tile_thread.td.cond);
}
}
pthread_mutex_destroy(&f->tile_thread.lock);
pthread_cond_destroy(&f->tile_thread.cond);
pthread_cond_destroy(&f->tile_thread.icond);
freep(&f->tile_thread.task_idx_to_sby_and_tile_idx);
}
for (int m = 0; f->ts && m < f->n_ts; m++) {
Dav1dTileState *const ts = &f->ts[m];
pthread_cond_destroy(&ts->tile_thread.cond);
pthread_mutex_destroy(&ts->tile_thread.lock);
}
if (f->lf.thread.inited) {
freep(&f->lf.thread.tasks);
pthread_cond_destroy(&f->lf.thread.cond);
pthread_cond_destroy(&f->task_thread.cond);
}
freep(&f->task_thread.tasks);
dav1d_free_aligned(f->ts);
dav1d_free_aligned(f->tc);
dav1d_free_aligned(f->ipred_edge[0]);
free(f->a);
free(f->tile);
@@ -653,6 +566,7 @@ static COLD void close_internal(Dav1dContext **const c_out, int flush) {
free(f->lf.lr_mask);
free(f->lf.level);
free(f->lf.tx_lpf_right_edge[0]);
free(f->lf.start_of_tile_row);
dav1d_refmvs_clear(&f->rf);
dav1d_free_aligned(f->lf.cdef_line_buf);
dav1d_free_aligned(f->lf.lr_lpf_line[0]);
+13 -10
View File
@@ -48,7 +48,7 @@ static void backup_lpf(const Dav1dFrameContext *const f,
const pixel *src, const ptrdiff_t src_stride,
const int ss_ver, const int sb128,
int row, const int row_h, const int src_w,
const int h, const int ss_hor, const int pft)
const int h, const int ss_hor)
{
const int dst_w = f->frame_hdr->super_res.enabled ?
(f->frame_hdr->width[1] + ss_hor) >> ss_hor : src_w;
@@ -57,7 +57,7 @@ static void backup_lpf(const Dav1dFrameContext *const f,
int stripe_h = (64 - 8 * !row) >> ss_ver;
src += (stripe_h - 2) * PXSTRIDE(src_stride);
if (!pft) {
if (f->c->n_tc == 1) {
if (row) {
const int top = 4 << sb128;
// Copy the top part of the stored loop filtered pixels from the
@@ -108,14 +108,15 @@ static void backup_lpf(const Dav1dFrameContext *const f,
void bytefn(dav1d_lr_copy_lpf)(Dav1dFrameContext *const f,
/*const*/ pixel *const src[3], const int sby)
{
const int pft = f->c->n_pfc > 1;
const int have_tt = f->c->n_tc > 1;
const int offset = 8 * !!sby;
const ptrdiff_t *const src_stride = f->cur.stride;
const ptrdiff_t lr_stride = ((f->sr_cur.p.p.w + 31) & ~31) * sizeof(pixel);
const ptrdiff_t tt_off = have_tt * sby * (4 << f->seq_hdr->sb128) * PXSTRIDE(lr_stride);
pixel *const dst[3] = {
f->lf.lr_lpf_line[0] + pft * sby * (4 << f->seq_hdr->sb128) * PXSTRIDE(lr_stride),
f->lf.lr_lpf_line[1] + pft * sby * (4 << f->seq_hdr->sb128) * PXSTRIDE(lr_stride),
f->lf.lr_lpf_line[2] + pft * sby * (4 << f->seq_hdr->sb128) * PXSTRIDE(lr_stride)
f->lf.lr_lpf_line[0] + tt_off,
f->lf.lr_lpf_line[1] + tt_off,
f->lf.lr_lpf_line[2] + tt_off
};
// TODO Also check block level restore type to reduce copying.
@@ -128,7 +129,7 @@ void bytefn(dav1d_lr_copy_lpf)(Dav1dFrameContext *const f,
const int y_stripe = (sby << (6 + f->seq_hdr->sb128)) - offset;
backup_lpf(f, dst[0], lr_stride,
src[0] - offset * PXSTRIDE(src_stride[0]), src_stride[0],
0, f->seq_hdr->sb128, y_stripe, row_h, w, h, 0, pft);
0, f->seq_hdr->sb128, y_stripe, row_h, w, h, 0);
}
if (restore_planes & (LR_RESTORE_U | LR_RESTORE_V)) {
const int ss_ver = f->sr_cur.p.p.layout == DAV1D_PIXEL_LAYOUT_I420;
@@ -141,12 +142,12 @@ void bytefn(dav1d_lr_copy_lpf)(Dav1dFrameContext *const f,
if (restore_planes & LR_RESTORE_U) {
backup_lpf(f, dst[1], lr_stride,
src[1] - offset_uv * PXSTRIDE(src_stride[1]), src_stride[1],
ss_ver, f->seq_hdr->sb128, y_stripe, row_h, w, h, ss_hor, pft);
ss_ver, f->seq_hdr->sb128, y_stripe, row_h, w, h, ss_hor);
}
if (restore_planes & LR_RESTORE_V) {
backup_lpf(f, dst[2], lr_stride,
src[2] - offset_uv * PXSTRIDE(src_stride[1]), src_stride[1],
ss_ver, f->seq_hdr->sb128, y_stripe, row_h, w, h, ss_hor, pft);
ss_ver, f->seq_hdr->sb128, y_stripe, row_h, w, h, ss_hor);
}
}
}
@@ -162,7 +163,9 @@ static void lr_stripe(const Dav1dFrameContext *const f, pixel *p,
const ptrdiff_t p_stride = f->sr_cur.p.stride[chroma];
const ptrdiff_t lpf_stride = sizeof(pixel) * ((f->sr_cur.p.p.w + 31) & ~31);
const int sby = (y + (y ? 8 << ss_ver : 0)) >> (6 - ss_ver + f->seq_hdr->sb128);
const pixel *lpf = f->lf.lr_lpf_line[plane] + (f->c->n_pfc > 1) * (sby * (4 << f->seq_hdr->sb128) - 4) * PXSTRIDE(lpf_stride) + x;
const int have_tt = f->c->n_tc > 1;
const pixel *lpf = f->lf.lr_lpf_line[plane] +
have_tt * (sby * (4 << f->seq_hdr->sb128) - 4) * PXSTRIDE(lpf_stride) + x;
// The first stripe of the frame is shorter by 8 luma pixel rows.
int stripe_h = imin((64 - 8 * !y) >> ss_ver, row_h - y);
+10 -4
View File
@@ -1547,19 +1547,25 @@ int dav1d_parse_obus(Dav1dContext *const c, Dav1dData *const in, const int globa
dav1d_data_props_copy(&c->out.m, &in->m);
c->event_flags |= dav1d_picture_get_event_flags(&c->refs[c->frame_hdr->existing_frame_idx].p);
} else {
pthread_mutex_lock(&c->task_thread.lock);
// need to append this to the frame output queue
const unsigned next = c->frame_thread.next++;
if (c->frame_thread.next == c->n_fc)
c->frame_thread.next = 0;
Dav1dFrameContext *const f = &c->fc[next];
pthread_mutex_lock(&f->frame_thread.td.lock);
while (f->n_tile_data > 0)
pthread_cond_wait(&f->frame_thread.td.cond,
&f->frame_thread.td.lock);
pthread_cond_wait(&f->task_thread.cond,
&f->task_thread.ttd->lock);
Dav1dThreadPicture *const out_delayed =
&c->frame_thread.out_delayed[next];
if (out_delayed->p.data[0]) {
if (atomic_load(&c->task_thread.first) + 1 < c->n_fc)
atomic_fetch_add(&c->task_thread.first, 1);
else
atomic_store(&c->task_thread.first, 0);
if (c->task_thread.cur < c->n_fc)
c->task_thread.cur--;
const unsigned progress = atomic_load_explicit(&out_delayed->progress[1],
memory_order_relaxed);
if (out_delayed->visible && progress != FRAME_ERROR) {
@@ -1572,7 +1578,7 @@ int dav1d_parse_obus(Dav1dContext *const c, Dav1dData *const in, const int globa
&c->refs[c->frame_hdr->existing_frame_idx].p);
out_delayed->visible = 1;
dav1d_data_props_copy(&out_delayed->p.m, &in->m);
pthread_mutex_unlock(&f->frame_thread.td.lock);
pthread_mutex_unlock(&c->task_thread.lock);
}
if (c->refs[c->frame_hdr->existing_frame_idx].p.p.frame_hdr->frame_type == DAV1D_FRAME_TYPE_KEY) {
const int r = c->frame_hdr->existing_frame_idx;
+3 -49
View File
@@ -176,7 +176,7 @@ int dav1d_thread_picture_alloc(Dav1dContext *const c, Dav1dFrameContext *const f
const int bpc)
{
Dav1dThreadPicture *const p = &f->sr_cur;
p->t = c->n_fc > 1 ? &f->frame_thread.td : NULL;
const int have_frame_mt = c->n_fc > 1;
const int res =
picture_alloc_with_edges(c, &p->p, f->frame_hdr->width[1], f->frame_hdr->height,
@@ -186,7 +186,7 @@ int dav1d_thread_picture_alloc(Dav1dContext *const c, Dav1dFrameContext *const f
c->mastering_display, c->mastering_display_ref,
c->itut_t35, c->itut_t35_ref,
bpc, &f->tile[0].data.m, &c->allocator,
p->t != NULL ? sizeof(atomic_int) * 2 : 0,
have_frame_mt ? sizeof(atomic_int) * 2 : 0,
(void **) &p->progress);
if (res) return res;
@@ -198,7 +198,7 @@ int dav1d_thread_picture_alloc(Dav1dContext *const c, Dav1dFrameContext *const f
c->frame_flags = 0;
p->visible = f->frame_hdr->show_frame;
if (p->t) {
if (have_frame_mt) {
atomic_init(&p->progress[0], 0);
atomic_init(&p->progress[1], 0);
}
@@ -254,7 +254,6 @@ void dav1d_thread_picture_ref(Dav1dThreadPicture *const dst,
const Dav1dThreadPicture *const src)
{
dav1d_picture_ref(&dst->p, &src->p);
dst->t = src->t;
dst->visible = src->visible;
dst->progress = src->progress;
dst->flags = src->flags;
@@ -279,54 +278,9 @@ void dav1d_picture_unref_internal(Dav1dPicture *const p) {
void dav1d_thread_picture_unref(Dav1dThreadPicture *const p) {
dav1d_picture_unref_internal(&p->p);
p->t = NULL;
p->progress = NULL;
}
int dav1d_thread_picture_wait(const Dav1dThreadPicture *const p,
int y_unclipped, const enum PlaneType plane_type)
{
assert(plane_type != PLANE_TYPE_ALL);
if (!p->t)
return 0;
// convert to luma units; include plane delay from loopfilters; clip
const int ss_ver = p->p.p.layout == DAV1D_PIXEL_LAYOUT_I420;
y_unclipped *= 1 << (plane_type & ss_ver); // we rely here on PLANE_TYPE_UV being 1
y_unclipped += (plane_type != PLANE_TYPE_BLOCK) * 8; // delay imposed by loopfilter
const unsigned y = iclip(y_unclipped, 1, p->p.p.h);
atomic_uint *const progress = &p->progress[plane_type != PLANE_TYPE_BLOCK];
unsigned state;
if ((state = atomic_load_explicit(progress, memory_order_acquire)) >= y)
return state == FRAME_ERROR;
pthread_mutex_lock(&p->t->lock);
while ((state = atomic_load_explicit(progress, memory_order_relaxed)) < y)
pthread_cond_wait(&p->t->cond, &p->t->lock);
pthread_mutex_unlock(&p->t->lock);
return state == FRAME_ERROR;
}
void dav1d_thread_picture_signal(const Dav1dThreadPicture *const p,
const int y, // in pixel units
const enum PlaneType plane_type)
{
assert(plane_type != PLANE_TYPE_UV);
if (!p->t)
return;
pthread_mutex_lock(&p->t->lock);
if (plane_type != PLANE_TYPE_Y)
atomic_store(&p->progress[0], y);
if (plane_type != PLANE_TYPE_BLOCK)
atomic_store(&p->progress[1], y);
pthread_cond_broadcast(&p->t->cond);
pthread_mutex_unlock(&p->t->lock);
}
enum Dav1dEventFlags dav1d_picture_get_event_flags(const Dav1dThreadPicture *const p) {
if (!p->flags)
return 0;
-26
View File
@@ -52,7 +52,6 @@ typedef struct Dav1dThreadPicture {
Dav1dPicture p;
int visible;
enum PictureFlags flags;
struct thread_data *t;
// [0] block data (including segmentation map and motion vectors)
// [1] pixel data
atomic_uint *progress;
@@ -91,31 +90,6 @@ void dav1d_thread_picture_unref(Dav1dThreadPicture *p);
*/
void dav1d_picture_move_ref(Dav1dPicture *dst, Dav1dPicture *src);
/**
* Wait for picture to reach a certain stage.
*
* y is in full-pixel units. If pt is not UV, this is in luma
* units, else it is in chroma units.
* plane_type is used to determine how many pixels delay are
* introduced by loopfilter processes.
*
* Returns 0 on success, and 1 if there was an error while decoding p
*/
int dav1d_thread_picture_wait(const Dav1dThreadPicture *p, int y,
enum PlaneType plane_type);
/**
* Signal decoding progress.
*
* y is in full-pixel luma units. FRAME_ERROR is used to signal a decoding
* error to frames using this frame as reference frame.
* plane_type denotes whether we have completed block data (pass 1;
* PLANE_TYPE_BLOCK), pixel data (pass 2, PLANE_TYPE_Y) or both (no
* 2-pass decoding; PLANE_TYPE_ALL).
*/
void dav1d_thread_picture_signal(const Dav1dThreadPicture *p, int y,
enum PlaneType plane_type);
int dav1d_default_picture_alloc(Dav1dPicture *p, void *cookie);
void dav1d_default_picture_release(Dav1dPicture *p, void *cookie);
void dav1d_picture_unref_internal(Dav1dPicture *p);
+8 -6
View File
@@ -37,12 +37,12 @@
#define DEBUG_B_PIXELS 0
#define decl_recon_b_intra_fn(name) \
void (name)(Dav1dTileContext *t, enum BlockSize bs, \
void (name)(Dav1dTaskContext *t, enum BlockSize bs, \
enum EdgeFlags intra_edge_flags, const Av1Block *b)
typedef decl_recon_b_intra_fn(*recon_b_intra_fn);
#define decl_recon_b_inter_fn(name) \
int (name)(Dav1dTileContext *t, enum BlockSize bs, const Av1Block *b)
int (name)(Dav1dTaskContext *t, enum BlockSize bs, const Av1Block *b)
typedef decl_recon_b_inter_fn(*recon_b_inter_fn);
#define decl_filter_sbrow_fn(name) \
@@ -50,11 +50,11 @@ void (name)(Dav1dFrameContext *f, int sby)
typedef decl_filter_sbrow_fn(*filter_sbrow_fn);
#define decl_backup_ipred_edge_fn(name) \
void (name)(Dav1dTileContext *t)
void (name)(Dav1dTaskContext *t)
typedef decl_backup_ipred_edge_fn(*backup_ipred_edge_fn);
#define decl_read_coef_blocks_fn(name) \
void (name)(Dav1dTileContext *t, enum BlockSize bs, const Av1Block *b)
void (name)(Dav1dTaskContext *t, enum BlockSize bs, const Av1Block *b)
typedef decl_read_coef_blocks_fn(*read_coef_blocks_fn);
decl_recon_b_intra_fn(dav1d_recon_b_intra_8bpc);
@@ -65,8 +65,10 @@ decl_recon_b_inter_fn(dav1d_recon_b_inter_16bpc);
decl_filter_sbrow_fn(dav1d_filter_sbrow_8bpc);
decl_filter_sbrow_fn(dav1d_filter_sbrow_16bpc);
decl_filter_sbrow_fn(dav1d_filter_sbrow_deblock_8bpc);
decl_filter_sbrow_fn(dav1d_filter_sbrow_deblock_16bpc);
decl_filter_sbrow_fn(dav1d_filter_sbrow_deblock_cols_8bpc);
decl_filter_sbrow_fn(dav1d_filter_sbrow_deblock_cols_16bpc);
decl_filter_sbrow_fn(dav1d_filter_sbrow_deblock_rows_8bpc);
decl_filter_sbrow_fn(dav1d_filter_sbrow_deblock_rows_16bpc);
decl_filter_sbrow_fn(dav1d_filter_sbrow_cdef_8bpc);
decl_filter_sbrow_fn(dav1d_filter_sbrow_cdef_16bpc);
decl_filter_sbrow_fn(dav1d_filter_sbrow_resize_8bpc);
+68 -62
View File
@@ -318,7 +318,7 @@ static inline unsigned get_lo_ctx(const uint8_t *const levels,
return offset + (mag > 512 ? 4 : (mag + 64) >> 7);
}
static int decode_coefs(Dav1dTileContext *const t,
static int decode_coefs(Dav1dTaskContext *const t,
uint8_t *const a, uint8_t *const l,
const enum RectTxfmSize tx, const enum BlockSize bs,
const Av1Block *const b, const int intra,
@@ -719,7 +719,7 @@ static int decode_coefs(Dav1dTileContext *const t,
return eob;
}
static void read_coef_tree(Dav1dTileContext *const t,
static void read_coef_tree(Dav1dTaskContext *const t,
const enum BlockSize bs, const Av1Block *const b,
const enum RectTxfmSize ytx, const int depth,
const uint16_t *const tx_split,
@@ -768,15 +768,16 @@ static void read_coef_tree(Dav1dTileContext *const t,
coef *cf;
struct CodedBlockInfo *cbi;
if (f->frame_thread.pass) {
assert(ts->frame_thread.cf);
cf = ts->frame_thread.cf;
ts->frame_thread.cf += imin(t_dim->w, 8) * imin(t_dim->h, 8) * 16;
if (t->frame_thread.pass) {
const int p = t->frame_thread.pass & 1;
assert(ts->frame_thread[p].cf);
cf = ts->frame_thread[p].cf;
ts->frame_thread[p].cf += imin(t_dim->w, 8) * imin(t_dim->h, 8) * 16;
cbi = &f->frame_thread.cbi[t->by * f->b4_stride + t->bx];
} else {
cf = bitfn(t->cf);
}
if (f->frame_thread.pass != 2) {
if (t->frame_thread.pass != 2) {
eob = decode_coefs(t, &t->a->lcoef[bx4], &t->l.lcoef[by4],
ytx, bs, b, 0, 0, cf, &txtp, &cf_ctx);
if (DEBUG_BLOCK_INFO)
@@ -798,7 +799,7 @@ static void read_coef_tree(Dav1dTileContext *const t,
uint8_t *txtp_map = &t->txtp_map[by4 * 32 + bx4];
case_set_upto16(txw,,,);
#undef set_ctx
if (f->frame_thread.pass == 1) {
if (t->frame_thread.pass == 1) {
cbi->eob[0] = eob;
cbi->txtp[0] = txtp;
}
@@ -806,7 +807,7 @@ static void read_coef_tree(Dav1dTileContext *const t,
eob = cbi->eob[0];
txtp = cbi->txtp[0];
}
if (!(f->frame_thread.pass & 1)) {
if (!(t->frame_thread.pass & 1)) {
assert(dst);
if (eob >= 0) {
if (DEBUG_BLOCK_INFO && DEBUG_B_PIXELS)
@@ -820,7 +821,7 @@ static void read_coef_tree(Dav1dTileContext *const t,
}
}
void bytefn(dav1d_read_coef_blocks)(Dav1dTileContext *const t,
void bytefn(dav1d_read_coef_blocks)(Dav1dTaskContext *const t,
const enum BlockSize bs, const Av1Block *const b)
{
const Dav1dFrameContext *const f = t->f;
@@ -855,7 +856,7 @@ void bytefn(dav1d_read_coef_blocks)(Dav1dTileContext *const t,
Dav1dTileState *const ts = t->ts;
const int w4 = imin(bw4, f->bw - t->bx), h4 = imin(bh4, f->bh - t->by);
const int cw4 = (w4 + ss_hor) >> ss_hor, ch4 = (h4 + ss_ver) >> ss_ver;
assert(f->frame_thread.pass == 1);
assert(t->frame_thread.pass == 1);
assert(!b->skip);
const TxfmInfo *const uv_t_dim = &dav1d_txfm_dimensions[b->uvtx];
const TxfmInfo *const t_dim = &dav1d_txfm_dimensions[b->intra ? b->tx : b->max_ytx];
@@ -884,12 +885,12 @@ void bytefn(dav1d_read_coef_blocks)(Dav1dTileContext *const t,
const int eob = cbi[t->bx].eob[0] =
decode_coefs(t, &t->a->lcoef[bx4 + x],
&t->l.lcoef[by4 + y], b->tx, bs, b, 1,
0, ts->frame_thread.cf, &txtp, &cf_ctx);
0, ts->frame_thread[1].cf, &txtp, &cf_ctx);
if (DEBUG_BLOCK_INFO)
printf("Post-y-cf-blk[tx=%d,txtp=%d,eob=%d]: r=%d\n",
b->tx, txtp, eob, ts->msac.rng);
cbi[t->bx].txtp[0] = txtp;
ts->frame_thread.cf += imin(t_dim->w, 8) * imin(t_dim->h, 8) * 16;
ts->frame_thread[1].cf += imin(t_dim->w, 8) * imin(t_dim->h, 8) * 16;
#define set_ctx(type, dir, diridx, off, mul, rep_macro) \
rep_macro(type, t->dir lcoef, off, mul * cf_ctx)
#define default_memset(dir, diridx, off, sz) \
@@ -927,14 +928,14 @@ void bytefn(dav1d_read_coef_blocks)(Dav1dTileContext *const t,
const int eob = cbi[t->bx].eob[1 + pl] =
decode_coefs(t, &t->a->ccoef[pl][cbx4 + x],
&t->l.ccoef[pl][cby4 + y], b->uvtx, bs,
b, b->intra, 1 + pl, ts->frame_thread.cf,
b, b->intra, 1 + pl, ts->frame_thread[1].cf,
&txtp, &cf_ctx);
if (DEBUG_BLOCK_INFO)
printf("Post-uv-cf-blk[pl=%d,tx=%d,"
"txtp=%d,eob=%d]: r=%d\n",
pl, b->uvtx, txtp, eob, ts->msac.rng);
cbi[t->bx].txtp[1 + pl] = txtp;
ts->frame_thread.cf += uv_t_dim->w * uv_t_dim->h * 16;
ts->frame_thread[1].cf += uv_t_dim->w * uv_t_dim->h * 16;
#define set_ctx(type, dir, diridx, off, mul, rep_macro) \
rep_macro(type, t->dir ccoef[pl], off, mul * cf_ctx)
#define default_memset(dir, diridx, off, sz) \
@@ -956,7 +957,7 @@ void bytefn(dav1d_read_coef_blocks)(Dav1dTileContext *const t,
}
}
static int mc(Dav1dTileContext *const t,
static int mc(Dav1dTaskContext *const t,
pixel *const dst8, int16_t *const dst16, const ptrdiff_t dst_stride,
const int bw4, const int bh4,
const int bx, const int by, const int pl,
@@ -979,11 +980,6 @@ static int mc(Dav1dTileContext *const t,
int w, h;
if (refp->p.data[0] != f->cur.data[0]) { // i.e. not for intrabc
if (dav1d_thread_picture_wait(refp, dy + bh4 * v_mul + !!my * 4,
PLANE_TYPE_Y + !!pl))
{
return -1;
}
w = (f->cur.p.w + ss_hor) >> ss_hor;
h = (f->cur.p.h + ss_ver) >> ss_ver;
} else {
@@ -1034,8 +1030,6 @@ static int mc(Dav1dTileContext *const t,
const int bottom =
((pos_y + (bh4 * v_mul - 1) * f->svc[refidx][1].step) >> 10) + 1;
if (dav1d_thread_picture_wait(refp, bottom + 4, PLANE_TYPE_Y + !!pl))
return -1;
if (DEBUG_BLOCK_INFO)
printf("Off %dx%d [%d,%d,%d], size %dx%d [%d,%d]\n",
left, top, orig_pos_x, f->svc[refidx][0].scale, refidx,
@@ -1077,7 +1071,7 @@ static int mc(Dav1dTileContext *const t,
return 0;
}
static int obmc(Dav1dTileContext *const t,
static int obmc(Dav1dTaskContext *const t,
pixel *const dst, const ptrdiff_t dst_stride,
const uint8_t *const b_dim, const int pl,
const int bx4, const int by4, const int w4, const int h4)
@@ -1138,7 +1132,7 @@ static int obmc(Dav1dTileContext *const t,
return 0;
}
static int warp_affine(Dav1dTileContext *const t,
static int warp_affine(Dav1dTaskContext *const t,
pixel *dst8, int16_t *dst16, const ptrdiff_t dstride,
const uint8_t *const b_dim, const int pl,
const Dav1dThreadPicture *const refp,
@@ -1176,11 +1170,6 @@ static int warp_affine(Dav1dTileContext *const t,
const pixel *ref_ptr;
ptrdiff_t ref_stride = refp->p.stride[!!pl];
if (dav1d_thread_picture_wait(refp, dy + 4 + 8,
PLANE_TYPE_Y + !!pl))
{
return -1;
}
if (dx < 3 || dx + 8 + 4 > width || dy < 3 || dy + 8 + 4 > height) {
pixel *const emu_edge_buf = bitfn(t->scratch.emu_edge);
f->dsp->mc.emu_edge(15, 15, width, height, dx - 3, dy - 3,
@@ -1204,7 +1193,7 @@ static int warp_affine(Dav1dTileContext *const t,
return 0;
}
void bytefn(dav1d_recon_b_intra)(Dav1dTileContext *const t, const enum BlockSize bs,
void bytefn(dav1d_recon_b_intra)(Dav1dTaskContext *const t, const enum BlockSize bs,
const enum EdgeFlags intra_edge_flags,
const Av1Block *const b)
{
@@ -1239,14 +1228,15 @@ void bytefn(dav1d_recon_b_intra)(Dav1dTileContext *const t, const enum BlockSize
pixel *dst = ((pixel *) f->cur.data[0]) +
4 * (t->by * PXSTRIDE(f->cur.stride[0]) + t->bx);
const uint8_t *pal_idx;
if (f->frame_thread.pass) {
assert(ts->frame_thread.pal_idx);
pal_idx = ts->frame_thread.pal_idx;
ts->frame_thread.pal_idx += bw4 * bh4 * 16;
if (t->frame_thread.pass) {
const int p = t->frame_thread.pass & 1;
assert(ts->frame_thread[p].pal_idx);
pal_idx = ts->frame_thread[p].pal_idx;
ts->frame_thread[p].pal_idx += bw4 * bh4 * 16;
} else {
pal_idx = t->scratch.pal_idx;
}
const uint16_t *const pal = f->frame_thread.pass ?
const uint16_t *const pal = t->frame_thread.pass ?
f->frame_thread.pal[((t->by >> 1) + (t->bx & 1)) * (f->b4_stride >> 1) +
((t->bx >> 1) + (t->by & 1))][0] : t->scratch.pal[0];
f->dsp->ipred.pal_pred(dst, f->cur.stride[0], pal,
@@ -1323,9 +1313,10 @@ void bytefn(dav1d_recon_b_intra)(Dav1dTileContext *const t, const enum BlockSize
coef *cf;
int eob;
enum TxfmType txtp;
if (f->frame_thread.pass) {
cf = ts->frame_thread.cf;
ts->frame_thread.cf += imin(t_dim->w, 8) * imin(t_dim->h, 8) * 16;
if (t->frame_thread.pass) {
const int p = t->frame_thread.pass & 1;
cf = ts->frame_thread[p].cf;
ts->frame_thread[p].cf += imin(t_dim->w, 8) * imin(t_dim->h, 8) * 16;
const struct CodedBlockInfo *const cbi =
&f->frame_thread.cbi[t->by * f->b4_stride + t->bx];
eob = cbi->eob[0];
@@ -1362,7 +1353,7 @@ void bytefn(dav1d_recon_b_intra)(Dav1dTileContext *const t, const enum BlockSize
hex_dump(dst, f->cur.stride[0],
t_dim->w * 4, t_dim->h * 4, "recon");
}
} else if (!f->frame_thread.pass) {
} else if (!t->frame_thread.pass) {
#define set_ctx(type, dir, diridx, off, mul, rep_macro) \
rep_macro(type, t->dir lcoef, off, mul * 0x40)
case_set_upto16(t_dim->h, l., 1, by4 + y);
@@ -1435,12 +1426,13 @@ void bytefn(dav1d_recon_b_intra)(Dav1dTileContext *const t, const enum BlockSize
(t->by >> ss_ver) * PXSTRIDE(f->cur.stride[1]));
const uint16_t (*pal)[8];
const uint8_t *pal_idx;
if (f->frame_thread.pass) {
assert(ts->frame_thread.pal_idx);
if (t->frame_thread.pass) {
const int p = t->frame_thread.pass & 1;
assert(ts->frame_thread[p].pal_idx);
pal = f->frame_thread.pal[((t->by >> 1) + (t->bx & 1)) * (f->b4_stride >> 1) +
((t->bx >> 1) + (t->by & 1))];
pal_idx = ts->frame_thread.pal_idx;
ts->frame_thread.pal_idx += cbw4 * cbh4 * 16;
pal_idx = ts->frame_thread[p].pal_idx;
ts->frame_thread[p].pal_idx += cbw4 * cbh4 * 16;
} else {
pal = t->scratch.pal;
pal_idx = &t->scratch.pal_idx[bw4 * bh4 * 16];
@@ -1545,9 +1537,10 @@ void bytefn(dav1d_recon_b_intra)(Dav1dTileContext *const t, const enum BlockSize
enum TxfmType txtp;
int eob;
coef *cf;
if (f->frame_thread.pass) {
cf = ts->frame_thread.cf;
ts->frame_thread.cf += uv_t_dim->w * uv_t_dim->h * 16;
if (t->frame_thread.pass) {
const int p = t->frame_thread.pass & 1;
cf = ts->frame_thread[p].cf;
ts->frame_thread[p].cf += uv_t_dim->w * uv_t_dim->h * 16;
const struct CodedBlockInfo *const cbi =
&f->frame_thread.cbi[t->by * f->b4_stride + t->bx];
eob = cbi->eob[pl + 1];
@@ -1587,7 +1580,7 @@ void bytefn(dav1d_recon_b_intra)(Dav1dTileContext *const t, const enum BlockSize
hex_dump(dst, stride, uv_t_dim->w * 4,
uv_t_dim->h * 4, "recon");
}
} else if (!f->frame_thread.pass) {
} else if (!t->frame_thread.pass) {
#define set_ctx(type, dir, diridx, off, mul, rep_macro) \
rep_macro(type, t->dir ccoef[pl], off, mul * 0x40)
case_set_upto16(uv_t_dim->h, l., 1, cby4 + y);
@@ -1604,7 +1597,7 @@ void bytefn(dav1d_recon_b_intra)(Dav1dTileContext *const t, const enum BlockSize
}
}
int bytefn(dav1d_recon_b_inter)(Dav1dTileContext *const t, const enum BlockSize bs,
int bytefn(dav1d_recon_b_inter)(Dav1dTaskContext *const t, const enum BlockSize bs,
const Av1Block *const b)
{
Dav1dTileState *const ts = t->ts;
@@ -1719,7 +1712,7 @@ int bytefn(dav1d_recon_b_inter)(Dav1dTileContext *const t, const enum BlockSize
r[-1][t->bx - 1].mv.mv[0],
&f->refp[r[-1][t->bx - 1].ref.ref[0] - 1],
r[-1][t->bx - 1].ref.ref[0] - 1,
f->frame_thread.pass != 2 ? t->tl_4x4_filter :
t->frame_thread.pass != 2 ? t->tl_4x4_filter :
f->frame_thread.b[((t->by - 1) * f->b4_stride) + t->bx - 1].filter2d);
if (res) return res;
}
@@ -1735,7 +1728,7 @@ int bytefn(dav1d_recon_b_inter)(Dav1dTileContext *const t, const enum BlockSize
t->by, 1 + pl, r[0][t->bx - 1].mv.mv[0],
&f->refp[r[0][t->bx - 1].ref.ref[0] - 1],
r[0][t->bx - 1].ref.ref[0] - 1,
f->frame_thread.pass != 2 ? left_filter_2d :
t->frame_thread.pass != 2 ? left_filter_2d :
f->frame_thread.b[(t->by * f->b4_stride) + t->bx - 1].filter2d);
if (res) return res;
}
@@ -1750,7 +1743,7 @@ int bytefn(dav1d_recon_b_inter)(Dav1dTileContext *const t, const enum BlockSize
1 + pl, r[-1][t->bx].mv.mv[0],
&f->refp[r[-1][t->bx].ref.ref[0] - 1],
r[-1][t->bx].ref.ref[0] - 1,
f->frame_thread.pass != 2 ? top_filter_2d :
t->frame_thread.pass != 2 ? top_filter_2d :
f->frame_thread.b[((t->by - 1) * f->b4_stride) + t->bx].filter2d);
if (res) return res;
}
@@ -1994,9 +1987,10 @@ int bytefn(dav1d_recon_b_inter)(Dav1dTileContext *const t, const enum BlockSize
coef *cf;
int eob;
enum TxfmType txtp;
if (f->frame_thread.pass) {
cf = ts->frame_thread.cf;
ts->frame_thread.cf += uvtx->w * uvtx->h * 16;
if (t->frame_thread.pass) {
const int p = t->frame_thread.pass & 1;
cf = ts->frame_thread[p].cf;
ts->frame_thread[p].cf += uvtx->w * uvtx->h * 16;
const struct CodedBlockInfo *const cbi =
&f->frame_thread.cbi[t->by * f->b4_stride + t->bx];
eob = cbi->eob[1 + pl];
@@ -2051,7 +2045,21 @@ int bytefn(dav1d_recon_b_inter)(Dav1dTileContext *const t, const enum BlockSize
return 0;
}
void bytefn(dav1d_filter_sbrow_deblock)(Dav1dFrameContext*const f, const int sby) {
void bytefn(dav1d_filter_sbrow_deblock_cols)(Dav1dFrameContext*const f, const int sby) {
const int y = sby * f->sb_step * 4;
const int ss_ver = f->cur.p.layout == DAV1D_PIXEL_LAYOUT_I420;
pixel *const p[3] = {
f->lf.p[0] + y * PXSTRIDE(f->cur.stride[0]),
f->lf.p[1] + (y * PXSTRIDE(f->cur.stride[1]) >> ss_ver),
f->lf.p[2] + (y * PXSTRIDE(f->cur.stride[1]) >> ss_ver)
};
Av1Filter *mask = f->lf.mask + (sby >> !f->seq_hdr->sb128) * f->sb128w;
if (f->frame_hdr->loopfilter.level_y[0] || f->frame_hdr->loopfilter.level_y[1])
bytefn(dav1d_loopfilter_sbrow_cols)(f, p, mask, sby,
f->lf.start_of_tile_row[sby]);
}
void bytefn(dav1d_filter_sbrow_deblock_rows)(Dav1dFrameContext*const f, const int sby) {
const int y = sby * f->sb_step * 4;
const int ss_ver = f->cur.p.layout == DAV1D_PIXEL_LAYOUT_I420;
pixel *const p[3] = {
@@ -2061,10 +2069,7 @@ void bytefn(dav1d_filter_sbrow_deblock)(Dav1dFrameContext*const f, const int sby
};
Av1Filter *mask = f->lf.mask + (sby >> !f->seq_hdr->sb128) * f->sb128w;
if (f->frame_hdr->loopfilter.level_y[0] || f->frame_hdr->loopfilter.level_y[1]) {
int start_of_tile_row = 0;
if (f->frame_hdr->tiling.row_start_sb[f->lf.tile_row] == sby)
start_of_tile_row = f->lf.tile_row++;
bytefn(dav1d_loopfilter_sbrow)(f, p, mask, sby, start_of_tile_row);
bytefn(dav1d_loopfilter_sbrow_rows)(f, p, mask, sby);
}
if (f->lf.restore_planes) {
// Store loop filtered pixels required by loop restoration
@@ -2145,7 +2150,8 @@ void bytefn(dav1d_filter_sbrow_lr)(Dav1dFrameContext *const f, const int sby) {
}
void bytefn(dav1d_filter_sbrow)(Dav1dFrameContext *const f, const int sby) {
bytefn(dav1d_filter_sbrow_deblock)(f, sby);
bytefn(dav1d_filter_sbrow_deblock_cols)(f, sby);
bytefn(dav1d_filter_sbrow_deblock_rows)(f, sby);
if (f->seq_hdr->cdef)
bytefn(dav1d_filter_sbrow_cdef)(f, sby);
if (f->frame_hdr->width[0] != f->frame_hdr->width[1])
@@ -2154,7 +2160,7 @@ void bytefn(dav1d_filter_sbrow)(Dav1dFrameContext *const f, const int sby) {
bytefn(dav1d_filter_sbrow_lr)(f, sby);
}
void bytefn(dav1d_backup_ipred_edge)(Dav1dTileContext *const t) {
void bytefn(dav1d_backup_ipred_edge)(Dav1dTaskContext *const t) {
const Dav1dFrameContext *const f = t->f;
Dav1dTileState *const ts = t->ts;
const int sby = t->by >> f->sb_shift;
+9 -4
View File
@@ -653,11 +653,14 @@ void dav1d_refmvs_find(const refmvs_tile *const rt,
void dav1d_refmvs_tile_sbrow_init(refmvs_tile *const rt, const refmvs_frame *const rf,
const int tile_col_start4, const int tile_col_end4,
const int tile_row_start4, const int tile_row_end4,
const int sby, int tile_row_idx)
const int sby, int tile_row_idx, const int pass)
{
if (rf->n_tile_threads == 1) tile_row_idx = 0;
rt->rp_proj = &rf->rp_proj[16 * rf->rp_stride * tile_row_idx];
refmvs_block *r = &rf->r[35 * rf->r_stride * tile_row_idx];
const int uses_2pass = rf->n_tile_threads > 1 && rf->n_frame_threads > 1;
const ptrdiff_t pass_off = (uses_2pass && pass == 2) ?
35 * rf->r_stride * rf->n_tile_rows : 0;
refmvs_block *r = &rf->r[35 * rf->r_stride * tile_row_idx + pass_off];
const int sbsz = rf->sbsz;
const int off = (sbsz * sby) & 16;
for (int i = 0; i < sbsz; i++, r += rf->r_stride)
@@ -806,7 +809,7 @@ int dav1d_refmvs_init_frame(refmvs_frame *const rf,
refmvs_temporal_block *const rp,
const unsigned ref_ref_poc[7][7],
/*const*/ refmvs_temporal_block *const rp_ref[7],
const int n_tile_threads)
const int n_tile_threads, const int n_frame_threads)
{
rf->sbsz = 16 << seq_hdr->sb128;
rf->frm_hdr = frm_hdr;
@@ -819,7 +822,8 @@ int dav1d_refmvs_init_frame(refmvs_frame *const rf,
const int n_tile_rows = n_tile_threads > 1 ? frm_hdr->tiling.rows : 1;
if (r_stride != rf->r_stride || n_tile_rows != rf->n_tile_rows) {
if (rf->r) dav1d_freep_aligned(&rf->r);
rf->r = dav1d_alloc_aligned(sizeof(*rf->r) * 35 * r_stride * n_tile_rows, 64);
const int uses_2pass = n_tile_threads > 1 && n_frame_threads > 1;
rf->r = dav1d_alloc_aligned(sizeof(*rf->r) * 35 * r_stride * n_tile_rows * (1 + uses_2pass), 64);
if (!rf->r) return DAV1D_ERR(ENOMEM);
rf->r_stride = r_stride;
}
@@ -833,6 +837,7 @@ int dav1d_refmvs_init_frame(refmvs_frame *const rf,
}
rf->n_tile_rows = n_tile_rows;
rf->n_tile_threads = n_tile_threads;
rf->n_frame_threads = n_frame_threads;
rf->rp = rp;
rf->rp_ref = rp_ref;
const unsigned poc = frm_hdr->frame_offset;
+3 -3
View File
@@ -79,7 +79,7 @@ typedef struct refmvs_frame {
refmvs_block *r; // 35 x r_stride memory
ptrdiff_t r_stride;
int n_tile_rows, n_tile_threads;
int n_tile_rows, n_tile_threads, n_frame_threads;
} refmvs_frame;
typedef struct refmvs_tile {
@@ -116,7 +116,7 @@ int dav1d_refmvs_init_frame(refmvs_frame *rf,
refmvs_temporal_block *rp,
const unsigned ref_ref_poc[7][7],
/*const*/ refmvs_temporal_block *const rp_ref[7],
int n_tile_threads);
int n_tile_threads, int n_frame_threads);
// initialize temporal MVs; this can be done in any configuration, e.g. one
// tile/sbrow at a time, where col_{start,end}8 are the tile boundaries; or
@@ -136,7 +136,7 @@ void dav1d_refmvs_save_tmvs(const refmvs_tile *rt,
void dav1d_refmvs_tile_sbrow_init(refmvs_tile *rt, const refmvs_frame *rf,
int tile_col_start4, int tile_col_end4,
int tile_row_start4, int tile_row_end4,
int sby, int tile_row_idx);
int sby, int tile_row_idx, int pass);
// call for each block
void dav1d_refmvs_find(const refmvs_tile *rt,
+615 -299
View File
@@ -27,345 +27,661 @@
#include "config.h"
#include "common/frame.h"
#include "src/thread_task.h"
int dav1d_task_create_filter_sbrow(Dav1dFrameContext *const f) {
struct PostFilterThreadData *const pftd = f->lf.thread.pftd;
const int frame_idx = (int)(f - f->c->fc);
// This function resets the cur pointer to the first frame theoretically
// executable after a task completed (ie. each time we update some progress or
// insert some tasks in the queue).
// When frame_idx is set, it can be either from a completed task, or from tasks
// inserted in the queue, in which case we have to make sure the cur pointer
// isn't past this insert.
// The special case where frame_idx is UINT_MAX is to handle the reset after
// completing a task and locklessly signaling progress. In this case we don't
// enter a critical section, which is needed for this function, so we set an
// atomic for a delayed handling, happening here. Meaning we can call this
// function without any actual update other than what's in the atomic, hence
// this special case.
static inline int reset_task_cur(const Dav1dContext *const c,
struct TaskThreadData *const ttd,
unsigned frame_idx)
{
const unsigned first = atomic_load(&ttd->first);
if (!ttd->cur && c->fc[first].task_thread.task_cur_prev == NULL)
return 0;
unsigned reset_frame_idx = atomic_exchange(&ttd->reset_task_cur, INT_MAX);
if (reset_frame_idx != INT_MAX) {
if (frame_idx == UINT_MAX) {
if (reset_frame_idx > first + ttd->cur)
return 0;
ttd->cur = reset_frame_idx - first;
goto cur_found;
}
} else if (frame_idx == UINT_MAX)
return 0;
if (frame_idx < first) frame_idx += c->n_fc;
const unsigned min_frame_idx = umin(reset_frame_idx, frame_idx);
const unsigned cur_frame_idx = first + ttd->cur;
if (ttd->cur < c->n_fc && cur_frame_idx < min_frame_idx)
return 0;
for (ttd->cur = min_frame_idx - first; ttd->cur < c->n_fc; ttd->cur++)
if (c->fc[(first + ttd->cur) % c->n_fc].task_thread.task_head)
break;
cur_found:
for (unsigned i = ttd->cur; i < c->n_fc; i++)
c->fc[(first + i) % c->n_fc].task_thread.task_cur_prev = NULL;
return 1;
}
static inline void reset_task_cur_async(struct TaskThreadData *const ttd,
unsigned frame_idx, unsigned n_frames)
{
if (frame_idx < atomic_load(&ttd->first)) frame_idx += n_frames;
unsigned last_idx = frame_idx;
do {
frame_idx = last_idx;
last_idx = atomic_exchange(&ttd->reset_task_cur, frame_idx);
} while (last_idx < frame_idx);
}
static void insert_tasks_between(Dav1dFrameContext *const f,
Dav1dTask *const first, Dav1dTask *const last,
Dav1dTask *const a, Dav1dTask *const b,
const int cond_signal)
{
struct TaskThreadData *const ttd = f->task_thread.ttd;
if (atomic_load(f->c->flush)) return;
assert(!a || a->next == b);
if (!a) f->task_thread.task_head = first;
else a->next = first;
if (!b) f->task_thread.task_tail = last;
last->next = b;
reset_task_cur(f->c, ttd, first->frame_idx);
if (cond_signal && !atomic_fetch_or(&ttd->cond_signaled, 1))
pthread_cond_signal(&ttd->cond);
}
static void insert_tasks(Dav1dFrameContext *const f,
Dav1dTask *const first, Dav1dTask *const last,
const int cond_signal)
{
// insert task back into task queue
Dav1dTask *t_ptr, *prev_t = NULL;
for (t_ptr = f->task_thread.task_head;
t_ptr; prev_t = t_ptr, t_ptr = t_ptr->next)
{
// entropy coding precedes other steps
if (t_ptr->type == DAV1D_TASK_TYPE_TILE_ENTROPY) {
if (first->type > DAV1D_TASK_TYPE_TILE_ENTROPY) continue;
// both are entropy
if (first->sby > t_ptr->sby) continue;
if (first->sby < t_ptr->sby) {
insert_tasks_between(f, first, last, prev_t, t_ptr, cond_signal);
return;
}
// same sby
} else {
if (first->type == DAV1D_TASK_TYPE_TILE_ENTROPY) {
insert_tasks_between(f, first, last, prev_t, t_ptr, cond_signal);
return;
}
if (first->sby > t_ptr->sby) continue;
if (first->sby < t_ptr->sby) {
insert_tasks_between(f, first, last, prev_t, t_ptr, cond_signal);
return;
}
// same sby
if (first->type > t_ptr->type) continue;
if (first->type < t_ptr->type) {
insert_tasks_between(f, first, last, prev_t, t_ptr, cond_signal);
return;
}
// same task type
}
// sort by tile-id
assert(first->type <= DAV1D_TASK_TYPE_TILE_RECONSTRUCTION);
assert(first->type == t_ptr->type);
assert(t_ptr->sby == first->sby);
const int p = first->type == DAV1D_TASK_TYPE_TILE_ENTROPY;
const int t_tile_idx = (int) (first - f->task_thread.tile_tasks[p]);
const int p_tile_idx = (int) (t_ptr - f->task_thread.tile_tasks[p]);
assert(t_tile_idx != p_tile_idx);
if (t_tile_idx > p_tile_idx) continue;
insert_tasks_between(f, first, last, prev_t, t_ptr, cond_signal);
return;
}
// append at the end
insert_tasks_between(f, first, last, prev_t, NULL, cond_signal);
}
static inline void insert_task(Dav1dFrameContext *const f,
Dav1dTask *const t, const int cond_signal)
{
insert_tasks(f, t, t, cond_signal);
}
static int create_filter_sbrow(Dav1dFrameContext *const f,
const int pass, Dav1dTask **res_t)
{
const int has_deblock = f->frame_hdr->loopfilter.level_y[0] ||
f->frame_hdr->loopfilter.level_y[1] ||
f->lf.restore_planes;
f->frame_hdr->loopfilter.level_y[1];
const int has_cdef = f->seq_hdr->cdef;
const int has_resize = f->frame_hdr->width[0] != f->frame_hdr->width[1];
const int has_lr = !!f->lf.restore_planes;
f->lf.thread.npf = has_deblock + has_cdef + has_resize + has_lr;
if (f->lf.thread.npf == 0) return 0;
const int has_lr = f->lf.restore_planes;
pthread_mutex_lock(&pftd->lock);
Dav1dTask *tasks = f->lf.thread.tasks;
int num_tasks = f->sbh * f->lf.thread.npf;
if (num_tasks > f->lf.thread.num_tasks) {
Dav1dTask *tasks = f->task_thread.tasks;
const int uses_2pass = f->c->n_fc > 1;
int num_tasks = f->sbh * (1 + uses_2pass);
if (num_tasks > f->task_thread.num_tasks) {
const size_t size = sizeof(Dav1dTask) * num_tasks;
tasks = realloc(f->lf.thread.tasks, size);
if (!tasks) {
pthread_mutex_unlock(&pftd->lock);
return -1;
}
tasks = realloc(f->task_thread.tasks, size);
if (!tasks) return -1;
memset(tasks, 0, size);
f->lf.thread.tasks = tasks;
f->lf.thread.num_tasks = num_tasks;
f->task_thread.tasks = tasks;
f->task_thread.num_tasks = num_tasks;
}
tasks += f->sbh * (pass & 1);
#define create_task(task, ready_cond, start_cond) \
do { \
t = &tasks[num_tasks++]; \
t->status = ready_cond ? DAV1D_TASK_READY : DAV1D_TASK_DEFAULT; \
t->start = start_cond; \
t->frame_id = frame_cnt; \
t->frame_idx = frame_idx; \
t->sby = sby; \
t->fn = f->bd_fn.filter_sbrow_##task; \
t->last_deps[0] = NULL; \
t->last_deps[1] = NULL; \
t->next_deps[0] = NULL; \
t->next_deps[1] = NULL; \
t->next_exec = NULL; \
} while (0)
Dav1dTask *last_sbrow_deblock = NULL;
Dav1dTask *last_sbrow_cdef = NULL;
Dav1dTask *last_sbrow_resize = NULL;
Dav1dTask *last_sbrow_lr = NULL;
num_tasks = 0;
const int frame_cnt = pftd->frame_cnt++;
for (int sby = 0; sby < f->sbh; ++sby) {
Dav1dTask *t;
Dav1dTask *last = NULL;
if (has_deblock) {
create_task(deblock, sby == 0, 0);
if (sby) {
t->last_deps[1] = last_sbrow_deblock;
last_sbrow_deblock->next_deps[1] = t;
}
last = t;
last_sbrow_deblock = t;
}
if (has_cdef) {
create_task(cdef, sby == 0 && !has_deblock, has_deblock);
if (has_deblock) {
t->last_deps[0] = last;
last->next_deps[0] = t;
}
if (sby) {
t->last_deps[1] = last_sbrow_cdef;
last_sbrow_cdef->next_deps[1] = t;
}
last = t;
last_sbrow_cdef = t;
};
if (has_resize) {
create_task(resize, sby == 0 && !last, !!last);
if (last) {
t->last_deps[0] = last;
last->next_deps[0] = t;
}
if (sby) {
t->last_deps[1] = last_sbrow_resize;
last_sbrow_resize->next_deps[1] = t;
}
last = t;
last_sbrow_resize = t;
}
if (has_lr) {
create_task(lr, sby == 0 && !last, !!last);
if (last) {
t->last_deps[0] = last;
last->next_deps[0] = t;
}
if (sby) {
t->last_deps[1] = last_sbrow_lr;
last_sbrow_lr->next_deps[1] = t;
}
last_sbrow_lr = t;
}
if (pass & 1) {
f->frame_thread.entropy_progress = 0;
} else {
atomic_store(&f->frame_thread.deblock_progress, 0);
atomic_store(&f->frame_thread.cdef_progress, 0);
atomic_store(&f->frame_thread.lr_progress, 0);
}
f->lf.thread.done = 0;
pthread_mutex_unlock(&pftd->lock);
f->frame_thread.next_tile_row[pass & 1] = 0;
Dav1dTask *t = &tasks[0];
t->sby = 0;
t->recon_progress = 1;
t->deblock_progress = 0;
t->cdef_progress = 0;
t->lr_progress = 0;
t->type = pass == 1 ? DAV1D_TASK_TYPE_ENTROPY_PROGRESS :
has_deblock ? DAV1D_TASK_TYPE_DEBLOCK_COLS :
has_lr /* i.e. LR backup */ ? DAV1D_TASK_TYPE_DEBLOCK_ROWS :
has_cdef ? DAV1D_TASK_TYPE_CDEF :
has_resize ? DAV1D_TASK_TYPE_SUPER_RESOLUTION :
DAV1D_TASK_TYPE_LOOP_RESTORATION;
t->frame_idx = (int)(f - f->c->fc);
*res_t = t;
return 0;
}
int dav1d_task_create_tile_sbrow(Dav1dFrameContext *const f, const int pass,
const int cond_signal)
{
Dav1dTask *tasks = f->task_thread.tile_tasks[0];
const int uses_2pass = f->c->n_fc > 1;
const int num_tasks = f->frame_hdr->tiling.cols * f->frame_hdr->tiling.rows;
int alloc_num_tasks = num_tasks * (1 + uses_2pass);
if (alloc_num_tasks > f->task_thread.num_tile_tasks) {
const size_t size = sizeof(Dav1dTask) * alloc_num_tasks;
tasks = realloc(f->task_thread.tile_tasks[0], size);
if (!tasks) return -1;
memset(tasks, 0, size);
f->task_thread.tile_tasks[0] = tasks;
f->task_thread.num_tile_tasks = alloc_num_tasks;
}
f->task_thread.tile_tasks[1] = tasks + num_tasks;
tasks += num_tasks * (pass & 1);
Dav1dTask *pf_t;
if (create_filter_sbrow(f, pass, &pf_t))
return -1;
Dav1dTask *prev_t = NULL;
for (int tile_idx = 0; tile_idx < num_tasks; tile_idx++) {
Dav1dTileState *const ts = &f->ts[tile_idx];
Dav1dTask *t = &tasks[tile_idx];
t->sby = ts->tiling.row_start >> f->sb_shift;
if (pf_t && t->sby) {
prev_t->next = pf_t;
prev_t = pf_t;
pf_t = NULL;
}
t->recon_progress = 0;
t->deblock_progress = 0;
t->cdef_progress = 0;
t->lr_progress = 0;
t->deps_skip = 0;
t->type = pass != 1 ? DAV1D_TASK_TYPE_TILE_RECONSTRUCTION :
DAV1D_TASK_TYPE_TILE_ENTROPY;
t->frame_idx = (int)(f - f->c->fc);
if (prev_t) prev_t->next = t;
prev_t = t;
}
if (pf_t) {
prev_t->next = pf_t;
prev_t = pf_t;
}
insert_tasks(f, &tasks[0], prev_t, cond_signal);
f->task_thread.done[pass & 1] = 0;
return 0;
}
void dav1d_task_schedule(struct PostFilterThreadData *const pftd,
Dav1dTask *const t)
void dav1d_task_frame_init(Dav1dFrameContext *const f) {
const Dav1dContext *const c = f->c;
// schedule init task, which will schedule the remaining tasks
Dav1dTask *const t = &f->task_thread.init_task;
t->type = DAV1D_TASK_TYPE_INIT;
t->frame_idx = (int)(f - c->fc);
t->sby = 0;
t->recon_progress = t->deblock_progress = 0;
t->cdef_progress = t->lr_progress = 0;
insert_task(f, t, 1);
}
static inline int ensure_progress(struct TaskThreadData *const ttd,
Dav1dFrameContext *const f,
Dav1dTask *const t, const enum TaskType type,
atomic_int *const state, int *const target)
{
Dav1dTask **pt = &pftd->tasks;
while (*pt &&
((*pt)->sby < t->sby ||
((*pt)->sby == t->sby && (*pt)->frame_id <= t->frame_id)))
pt = &(*pt)->next_exec;
t->next_exec = *pt;
*pt = t;
pthread_cond_signal(&pftd->cond);
// deblock_rows (non-LR portion) depends on deblock of previous sbrow,
// so ensure that completed. if not, re-add to task-queue; else, fall-through
int p1 = atomic_load(state);
if (p1 < t->sby) {
pthread_mutex_lock(&ttd->lock);
p1 = atomic_load(state);
if (p1 < t->sby) {
t->type = type;
t->deblock_progress = t->recon_progress = 0;
t->cdef_progress = t->lr_progress = 0;
*target = t->sby;
insert_task(f, t, 0);
return 1;
}
pthread_mutex_unlock(&ttd->lock);
}
return 0;
}
static inline void update_task(Dav1dTask *const t, const int dep_type,
Dav1dFrameContext *const f)
static inline int check_tile(Dav1dTask *const t, Dav1dFrameContext *const f,
const int frame_mt)
{
if (!t->last_deps[!dep_type] ||
t->last_deps[!dep_type]->status == DAV1D_TASK_DONE)
{
t->status = DAV1D_TASK_READY;
if (t->start)
dav1d_task_schedule(f->lf.thread.pftd, t);
const int tp = t->type == DAV1D_TASK_TYPE_TILE_ENTROPY;
const int tile_idx = (int)(t - f->task_thread.tile_tasks[tp]);
Dav1dTileState *const ts = &f->ts[tile_idx];
const int p1 = atomic_load(&ts->progress[tp]);
if (p1 < t->sby) return 1;
int error = p1 == TILE_ERROR;
error |= atomic_fetch_or(&f->task_thread.error, error);
if (!error && frame_mt && !tp) {
const int p2 = atomic_load(&ts->progress[1]);
if (p2 <= t->sby) return 1;
error = p2 == TILE_ERROR;
error |= atomic_fetch_or(&f->task_thread.error, error);
}
if (!error && frame_mt && !IS_KEY_OR_INTRA(f->frame_hdr)) {
// check reference state
const Dav1dThreadPicture *p = &f->sr_cur;
const int ss_ver = p->p.p.layout == DAV1D_PIXEL_LAYOUT_I420;
const unsigned p_b = (t->sby + 1) << (f->sb_shift + 2);
const int tile_sby = t->sby - (ts->tiling.row_start >> f->sb_shift);
const int (*const lowest_px)[2] = ts->lowest_pixel[tile_sby];
for (int n = t->deps_skip; n < 7; n++, t->deps_skip++) {
unsigned lowest;
if (tp) {
// if temporal mv refs are disabled, we only need this
// for the primary ref; if segmentation is disabled, we
// don't even need that
lowest = p_b;
} else {
// +8 is postfilter-induced delay
const int y = lowest_px[n][0] == INT_MIN ? INT_MIN :
lowest_px[n][0] + 8;
const int uv = lowest_px[n][1] == INT_MIN ? INT_MIN :
(lowest_px[n][1] << ss_ver) + 8;
const int max = imax(y, uv);
if (max == INT_MIN) continue;
lowest = iclip(max, 1, f->refp[n].p.p.h);
}
const unsigned p3 = atomic_load(&f->refp[n].progress[!tp]);
if (p3 < lowest) return 1;
atomic_fetch_or(&f->task_thread.error, p3 == FRAME_ERROR);
}
}
return 0;
}
void *dav1d_frame_task(void *const data) {
Dav1dFrameContext *const f = data;
void *dav1d_worker_task(void *data) {
Dav1dTaskContext *const tc = data;
const Dav1dContext *const c = tc->c;
struct TaskThreadData *const ttd = tc->task_thread.ttd;
dav1d_set_thread_name("dav1d-frame");
pthread_mutex_lock(&f->frame_thread.td.lock);
dav1d_set_thread_name("dav1d-worker");
pthread_mutex_lock(&ttd->lock);
for (;;) {
while (!f->n_tile_data && !f->frame_thread.die) {
pthread_cond_wait(&f->frame_thread.td.cond,
&f->frame_thread.td.lock);
}
if (f->frame_thread.die) break;
pthread_mutex_unlock(&f->frame_thread.td.lock);
if (dav1d_decode_frame(f))
memset(f->frame_thread.cf, 0,
(size_t)f->frame_thread.cf_sz * 128 * 128 / 2);
pthread_mutex_lock(&f->frame_thread.td.lock);
f->n_tile_data = 0;
pthread_cond_signal(&f->frame_thread.td.cond);
}
pthread_mutex_unlock(&f->frame_thread.td.lock);
return NULL;
}
void *dav1d_tile_task(void *const data) {
Dav1dTileContext *const t = data;
struct FrameTileThreadData *const fttd = t->tile_thread.fttd;
const Dav1dFrameContext *const f = t->f;
const int tile_thread_idx = (int) (t - f->tc);
const uint64_t mask = 1ULL << tile_thread_idx;
dav1d_set_thread_name("dav1d-tile");
for (;;) {
pthread_mutex_lock(&fttd->lock);
fttd->available |= mask;
int did_signal = 0;
while (!fttd->tasks_left && !t->tile_thread.die) {
if (!did_signal) {
did_signal = 1;
pthread_cond_signal(&fttd->icond);
Dav1dFrameContext *f;
Dav1dTask *t, *prev_t;
if (tc->task_thread.die) break;
if (atomic_load(c->flush)) goto park;
while (ttd->cur < c->n_fc) {
const unsigned first = atomic_load(&ttd->first);
f = &c->fc[(first + ttd->cur) % c->n_fc];
prev_t = f->task_thread.task_cur_prev;
t = prev_t ? prev_t->next : f->task_thread.task_head;
while (t) {
if (t->type == DAV1D_TASK_TYPE_INIT) {
const int p1 = f->in_cdf.progress ?
atomic_load(f->in_cdf.progress) : 1;
if (p1) {
atomic_fetch_or(&f->task_thread.error, p1 == TILE_ERROR);
goto found;
}
} else if (t->type <= DAV1D_TASK_TYPE_TILE_RECONSTRUCTION) {
// if not bottom sbrow of tile, this task will be re-added
// after it's finished
if (!check_tile(t, f, c->n_fc > 1))
goto found;
} else if (t->recon_progress) {
const int p = t->type == DAV1D_TASK_TYPE_ENTROPY_PROGRESS;
int error = atomic_load(&f->task_thread.error);
assert(!f->task_thread.done[p] || error);
const int tile_row_base = f->frame_hdr->tiling.cols *
f->frame_thread.next_tile_row[p];
if (p) {
const int p1 = f->frame_thread.entropy_progress;
if (p1 < t->sby) goto next;
atomic_fetch_or(&f->task_thread.error, p1 == TILE_ERROR);
}
for (int tc = 0; tc < f->frame_hdr->tiling.cols; tc++) {
Dav1dTileState *const ts = &f->ts[tile_row_base + tc];
const int p2 = atomic_load(&ts->progress[p]);
if (p2 < t->recon_progress) goto next;
atomic_fetch_or(&f->task_thread.error, p2 == TILE_ERROR);
}
if (!p) {
atomic_int *state = NULL;
int needed;
if (t->cdef_progress) {
state = &f->frame_thread.cdef_progress;
needed = t->cdef_progress;
} else if (t->lr_progress) {
state = &f->frame_thread.lr_progress;
needed = t->lr_progress;
}
if (state) {
const int p3 = atomic_load(state);
if (p3 < needed) goto next;
atomic_fetch_or(&f->task_thread.error, p3 == TILE_ERROR);
}
}
if (t->sby + 1 < f->sbh) {
// add sby+1 to list to replace this one
Dav1dTask *next_t = &t[1];
*next_t = *t;
next_t->sby++;
const int ntr = f->frame_thread.next_tile_row[p] + 1;
const int start = f->frame_hdr->tiling.row_start_sb[ntr];
if (next_t->sby == start)
f->frame_thread.next_tile_row[p] = ntr;
next_t->recon_progress = next_t->sby + 1;
if (t->type == DAV1D_TASK_TYPE_CDEF)
next_t->cdef_progress = next_t->sby;
else if (t->type == DAV1D_TASK_TYPE_LOOP_RESTORATION)
next_t->lr_progress = next_t->sby;
insert_task(f, next_t, 0);
}
goto found;
} else {
assert(!!t->deblock_progress + !!t->cdef_progress + !!t->lr_progress == 1);
atomic_int *state;
int needed;
if (t->deblock_progress) {
needed = t->deblock_progress;
state = &f->frame_thread.deblock_progress;
} else if (t->cdef_progress) {
needed = t->cdef_progress;
state = &f->frame_thread.cdef_progress;
} else {
assert(t->lr_progress);
needed = t->lr_progress;
state = &f->frame_thread.lr_progress;
}
const int p1 = atomic_load(state);
if (p1 >= needed) {
atomic_fetch_or(&f->task_thread.error, p1 == TILE_ERROR);
goto found;
}
}
next:
prev_t = t;
t = t->next;
f->task_thread.task_cur_prev = prev_t;
}
pthread_cond_wait(&fttd->cond, &fttd->lock);
ttd->cur++;
}
if (t->tile_thread.die) {
pthread_cond_signal(&fttd->icond);
pthread_mutex_unlock(&fttd->lock);
break;
}
fttd->available &= ~mask;
const int task_idx = fttd->num_tasks - fttd->tasks_left--;
pthread_mutex_unlock(&fttd->lock);
if (reset_task_cur(c, ttd, UINT_MAX)) continue;
park:
tc->task_thread.flushed = 1;
pthread_cond_signal(&tc->task_thread.td.cond);
// we want to be woken up next time progress is signaled
atomic_store(&ttd->cond_signaled, 0);
pthread_cond_wait(&ttd->cond, &ttd->lock);
tc->task_thread.flushed = 0;
reset_task_cur(c, ttd, UINT_MAX);
continue;
if (f->frame_thread.pass == 1 || f->n_tc >= f->frame_hdr->tiling.cols) {
// we can (or in fact, if >, we need to) do full tile decoding.
// loopfilter happens in the main thread
Dav1dTileState *const ts = t->ts = &f->ts[task_idx];
for (t->by = ts->tiling.row_start; t->by < ts->tiling.row_end;
t->by += f->sb_step)
{
const int error = dav1d_decode_tile_sbrow(t);
const int progress = error ? TILE_ERROR : 1 + (t->by >> f->sb_shift);
found:
// remove t from list
if (prev_t) prev_t->next = t->next;
else f->task_thread.task_head = t->next;
if (!t->next) f->task_thread.task_tail = prev_t;
if (!f->task_thread.task_head) ttd->cur++;
// we don't need to check cond_signaled here, since we found a task
// after the last signal so we want to re-signal the next waiting thread
// and again won't need to signal after that
atomic_store(&ttd->cond_signaled, 1);
pthread_cond_signal(&ttd->cond);
pthread_mutex_unlock(&ttd->lock);
found_unlocked:;
const int flush = atomic_load(c->flush);
int error = atomic_fetch_or(&f->task_thread.error, flush) | flush;
// signal progress
pthread_mutex_lock(&ts->tile_thread.lock);
atomic_store(&ts->progress, progress);
pthread_cond_signal(&ts->tile_thread.cond);
pthread_mutex_unlock(&ts->tile_thread.lock);
if (error) break;
// run it
tc->f = f;
int sby = t->sby;
switch (t->type) {
case DAV1D_TASK_TYPE_INIT: {
assert(c->n_fc > 1);
int res = -1;
if (!atomic_load(&f->task_thread.error))
res = dav1d_decode_frame_init(f);
pthread_mutex_lock(&ttd->lock);
if (f->frame_hdr->refresh_context && !f->task_thread.update_set) {
atomic_store(f->out_cdf.progress, res < 0 ? TILE_ERROR : 1);
}
} else {
const int sby = f->tile_thread.task_idx_to_sby_and_tile_idx[task_idx][0];
const int tile_idx = f->tile_thread.task_idx_to_sby_and_tile_idx[task_idx][1];
if (!res) {
assert(c->n_fc > 1);
for (int p = 1; p <= 2; p++) {
const int res = dav1d_task_create_tile_sbrow(f, p, 0);
if (res) {
// memory allocation failed
f->task_thread.done[2 - p] = 1;
atomic_store(&f->task_thread.error, 1);
f->task_thread.task_counter -= f->sbh +
f->frame_hdr->tiling.cols * f->frame_hdr->tiling.rows;
atomic_store(&f->sr_cur.progress[p - 1], FRAME_ERROR);
if (p == 2 && f->task_thread.done[1]) {
assert(!f->task_thread.task_counter);
dav1d_decode_frame_exit(f, -1);
f->n_tile_data = 0;
pthread_cond_signal(&f->task_thread.cond);
}
}
}
} else {
// init failed, signal completion
atomic_store(&f->task_thread.error, 1);
f->task_thread.task_counter = 0;
f->task_thread.done[0] = 1;
f->task_thread.done[1] = 1;
atomic_store(&f->sr_cur.progress[0], FRAME_ERROR);
atomic_store(&f->sr_cur.progress[1], FRAME_ERROR);
dav1d_decode_frame_exit(f, -1);
f->n_tile_data = 0;
pthread_cond_signal(&f->task_thread.cond);
}
reset_task_cur(c, ttd, t->frame_idx);
continue;
}
case DAV1D_TASK_TYPE_TILE_ENTROPY:
case DAV1D_TASK_TYPE_TILE_RECONSTRUCTION: {
const int p = t->type == DAV1D_TASK_TYPE_TILE_ENTROPY;
const int tile_idx = (int)(t - f->task_thread.tile_tasks[p]);
Dav1dTileState *const ts = &f->ts[tile_idx];
int progress;
// the interleaved decoding can sometimes cause dependency issues
// if one part of the frame decodes signifcantly faster than others.
// Ideally, we'd "skip" tile_sbrows where dependencies are missing,
// and resume them later as dependencies are met. This also would
// solve the broadcast() below and allow us to use signal(). However,
// for now, we use linear dependency tracking because it's simpler.
if ((progress = atomic_load(&ts->progress)) < sby) {
pthread_mutex_lock(&ts->tile_thread.lock);
while ((progress = atomic_load(&ts->progress)) < sby)
pthread_cond_wait(&ts->tile_thread.cond,
&ts->tile_thread.lock);
pthread_mutex_unlock(&ts->tile_thread.lock);
}
if (progress == TILE_ERROR) continue;
// we need to interleave sbrow decoding for all tile cols in a
// tile row, since otherwise subsequent threads will be blocked
// waiting for the post-filter to complete
t->ts = ts;
t->by = sby << f->sb_shift;
const int error = dav1d_decode_tile_sbrow(t);
progress = error ? TILE_ERROR : 1 + sby;
tc->ts = ts;
tc->by = sby << f->sb_shift;
const int uses_2pass = c->n_fc > 1;
tc->frame_thread.pass = !uses_2pass ? 0 :
1 + (t->type == DAV1D_TASK_TYPE_TILE_RECONSTRUCTION);
if (!error) error = dav1d_decode_tile_sbrow(tc);
const int progress = error ? TILE_ERROR : 1 + sby;
// signal progress
pthread_mutex_lock(&ts->tile_thread.lock);
atomic_store(&ts->progress, progress);
pthread_cond_broadcast(&ts->tile_thread.cond);
pthread_mutex_unlock(&ts->tile_thread.lock);
}
}
return NULL;
}
static inline int handle_abortion(Dav1dPostFilterContext *const pf,
Dav1dContext *const c,
struct PostFilterThreadData *const pftd)
{
const int flush = atomic_load_explicit(c->flush, memory_order_acquire);
if (flush) {
pthread_mutex_lock(&pf->td.lock);
pf->flushed = 0;
pthread_mutex_unlock(&pf->td.lock);
}
for (unsigned i = 0; i < c->n_fc; i++) {
Dav1dFrameContext *const f = &c->fc[i];
int send_signal;
if (flush) // TODO before merge, see if this can be safely merged
send_signal = f->lf.thread.done != 1 && f->lf.thread.num_tasks != 0;
else
send_signal = f->lf.thread.done == -1;
for (int j = 0; send_signal && j < f->lf.thread.num_tasks; j++) {
Dav1dTask *const t = &f->lf.thread.tasks[j];
if (t->status == DAV1D_TASK_RUNNING ||
(t->status == DAV1D_TASK_DONE && t->start != -1))
send_signal = 0;
}
if (send_signal) {
if (!flush) {
Dav1dTask **pt = &pftd->tasks;
while (*pt) {
if ((*pt)->frame_idx == i)
*pt = (*pt)->next_exec;
else
pt = &(*pt)->next_exec;
atomic_fetch_or(&f->task_thread.error, error);
if (((sby + 1) << f->sb_shift) < ts->tiling.row_end) {
t->sby++;
t->deps_skip = 0;
if (!check_tile(t, f, uses_2pass)) {
atomic_store(&ts->progress[p], progress);
reset_task_cur_async(ttd, t->frame_idx, c->n_fc);
if (!atomic_fetch_or(&ttd->cond_signaled, 1))
pthread_cond_signal(&ttd->cond);
goto found_unlocked;
}
pthread_mutex_lock(&ttd->lock);
atomic_store(&ts->progress[p], progress);
reset_task_cur(c, ttd, t->frame_idx);
insert_task(f, t, 0);
} else {
pthread_mutex_lock(&ttd->lock);
atomic_store(&ts->progress[p], progress);
reset_task_cur(c, ttd, t->frame_idx);
error = atomic_load(&f->task_thread.error);
if (f->frame_hdr->refresh_context &&
tc->frame_thread.pass <= 1 && f->task_thread.update_set &&
f->frame_hdr->tiling.update == tile_idx)
{
if (!error)
dav1d_cdf_thread_update(f->frame_hdr, f->out_cdf.data.cdf,
&f->ts[f->frame_hdr->tiling.update].cdf);
if (c->n_fc > 1)
atomic_store(f->out_cdf.progress, error ? TILE_ERROR : 1);
}
if (!--f->task_thread.task_counter && f->task_thread.done[0] &&
(!uses_2pass || f->task_thread.done[1]))
{
dav1d_decode_frame_exit(f, error ? -1 : 0);
f->n_tile_data = 0;
pthread_cond_signal(&f->task_thread.cond);
}
assert(f->task_thread.task_counter >= 0);
if (!atomic_fetch_or(&ttd->cond_signaled, 1))
pthread_cond_signal(&ttd->cond);
}
f->lf.thread.done = 1;
pthread_cond_signal(&f->lf.thread.cond);
continue;
}
}
if (flush) {
pthread_mutex_lock(&pf->td.lock);
pf->flushed = 1;
pthread_cond_signal(&pf->td.cond);
pthread_mutex_unlock(&pf->td.lock);
}
return !flush;
}
void *dav1d_postfilter_task(void *data) {
Dav1dPostFilterContext *const pf = data;
Dav1dContext *const c = pf->c;
struct PostFilterThreadData *pftd = &c->postfilter_thread;
dav1d_set_thread_name("dav1d-postfilter");
int exec = 1;
pthread_mutex_lock(&pftd->lock);
for (;;) {
if (!exec && !pf->die)
pthread_cond_wait(&pftd->cond, &pftd->lock);
if (!(exec = handle_abortion(pf, c, pftd))) continue;
if (pf->die) break;
Dav1dTask *const t = pftd->tasks;
if (!t) { exec = 0; continue; }
pftd->tasks = t->next_exec;
t->status = DAV1D_TASK_RUNNING;
pthread_mutex_unlock(&pftd->lock);
Dav1dFrameContext *const f = &c->fc[t->frame_idx];
t->fn(f, t->sby);
exec = 1;
pthread_mutex_lock(&pftd->lock);
if (t->next_deps[0])
update_task(t->next_deps[0], 0, f);
if (t->next_deps[1])
update_task(t->next_deps[1], 1, f);
t->status = DAV1D_TASK_DONE;
if (!t->next_deps[0]) {
const enum PlaneType progress_plane_type =
c->n_fc > 1 && f->frame_hdr->refresh_context ?
PLANE_TYPE_Y : PLANE_TYPE_ALL;
const int y = (t->sby + 1) * f->sb_step * 4;
dav1d_thread_picture_signal(&f->sr_cur, y, progress_plane_type);
if (t->sby + 1 == f->sbh) {
f->lf.thread.done = 1;
pthread_cond_signal(&f->lf.thread.cond);
case DAV1D_TASK_TYPE_DEBLOCK_COLS:
if (!atomic_load(&f->task_thread.error))
f->bd_fn.filter_sbrow_deblock_cols(f, sby);
if (ensure_progress(ttd, f, t, DAV1D_TASK_TYPE_DEBLOCK_ROWS,
&f->frame_thread.deblock_progress,
&t->deblock_progress)) continue;
// fall-through
case DAV1D_TASK_TYPE_DEBLOCK_ROWS:
if (!atomic_load(&f->task_thread.error))
f->bd_fn.filter_sbrow_deblock_rows(f, sby);
// signal deblock progress
if (f->frame_hdr->loopfilter.level_y[0] ||
f->frame_hdr->loopfilter.level_y[1])
{
error = atomic_load(&f->task_thread.error);
atomic_store(&f->frame_thread.deblock_progress,
error ? TILE_ERROR : sby + 1);
reset_task_cur_async(ttd, t->frame_idx, c->n_fc);
if (!atomic_fetch_or(&ttd->cond_signaled, 1))
pthread_cond_signal(&ttd->cond);
}
// fall-through
case DAV1D_TASK_TYPE_CDEF:
if (f->seq_hdr->cdef) {
// cdef caches top (pre-cdef) buffers internally and therefore
// needs to be vertically linear
if (ensure_progress(ttd, f, t, DAV1D_TASK_TYPE_CDEF,
&f->frame_thread.cdef_progress,
&t->cdef_progress)) continue;
if (!atomic_load(&f->task_thread.error))
f->bd_fn.filter_sbrow_cdef(f, sby);
// signal cdef progress
error = atomic_load(&f->task_thread.error);
atomic_store(&f->frame_thread.cdef_progress,
error ? TILE_ERROR : sby + 1);
reset_task_cur_async(ttd, t->frame_idx, c->n_fc);
if (!atomic_fetch_or(&ttd->cond_signaled, 1))
pthread_cond_signal(&ttd->cond);
}
// fall-through
case DAV1D_TASK_TYPE_SUPER_RESOLUTION:
if (f->frame_hdr->width[0] != f->frame_hdr->width[1])
if (!atomic_load(&f->task_thread.error))
f->bd_fn.filter_sbrow_resize(f, sby);
// fall-through
case DAV1D_TASK_TYPE_LOOP_RESTORATION:
// lr is the last step before signaling frame completion, and
// therefore needs to be done vertically linear
if (ensure_progress(ttd, f, t, DAV1D_TASK_TYPE_LOOP_RESTORATION,
&f->frame_thread.lr_progress,
&t->lr_progress)) continue;
if (!atomic_load(&f->task_thread.error) && f->lf.restore_planes)
f->bd_fn.filter_sbrow_lr(f, sby);
// fall-through
case DAV1D_TASK_TYPE_ENTROPY_PROGRESS:
// dummy to convert tile to frame
break;
default: abort();
}
t->start = -1;
// if task completed [typically LR], signal picture progress as per below
const int uses_2pass = c->n_fc > 1;
const enum PlaneType progress_plane_type =
t->type == DAV1D_TASK_TYPE_ENTROPY_PROGRESS ? PLANE_TYPE_BLOCK :
c->n_fc > 1 ? PLANE_TYPE_Y : PLANE_TYPE_ALL;
const int sbh = f->sbh;
pthread_mutex_lock(&ttd->lock);
error = atomic_load(&f->task_thread.error);
const unsigned y = error ? FRAME_ERROR :
sby + 1 == sbh ? UINT_MAX : (unsigned)(sby + 1) * f->sb_step * 4;
if (c->n_fc > 1 && f->sr_cur.p.data[0] /* upon flush, this can be free'ed already */) {
if (!uses_2pass || t->type == DAV1D_TASK_TYPE_ENTROPY_PROGRESS)
atomic_store(&f->sr_cur.progress[0], y);
if (!uses_2pass || t->type != DAV1D_TASK_TYPE_ENTROPY_PROGRESS)
atomic_store(&f->sr_cur.progress[1], y);
}
const int progress = error ? TILE_ERROR : sby + 1;
if (progress_plane_type == PLANE_TYPE_BLOCK)
f->frame_thread.entropy_progress = progress;
else
atomic_store(&f->frame_thread.lr_progress, progress);
if (sby + 1 == sbh)
f->task_thread.done[progress_plane_type == PLANE_TYPE_BLOCK] = 1;
if (!--f->task_thread.task_counter &&
f->task_thread.done[0] && (!uses_2pass || f->task_thread.done[1]))
{
dav1d_decode_frame_exit(f, error ? -1 : 0);
f->n_tile_data = 0;
pthread_cond_signal(&f->task_thread.cond);
}
reset_task_cur(c, ttd, t->frame_idx);
}
pthread_mutex_unlock(&pftd->lock);
pthread_mutex_unlock(&ttd->lock);
return NULL;
}
+8 -25
View File
@@ -35,33 +35,16 @@
#define FRAME_ERROR (UINT_MAX - 1)
#define TILE_ERROR (INT_MAX - 1)
enum TaskStatus {
DAV1D_TASK_DEFAULT,
DAV1D_TASK_READY,
DAV1D_TASK_RUNNING,
DAV1D_TASK_DONE,
};
// these functions assume the task scheduling lock is already taken
int dav1d_task_create_tile_sbrow(Dav1dFrameContext *f, int pass, int cond_signal);
void dav1d_task_frame_init(Dav1dFrameContext *f);
struct Dav1dTask {
enum TaskStatus status; // task status
int start; // frame thread start flag
unsigned frame_idx; // frame thread id
int frame_id; // frame ordering
int sby; // sbrow
filter_sbrow_fn fn; // task work
Dav1dTask *last_deps[2]; // dependencies
Dav1dTask *next_deps[2]; // dependant tasks
Dav1dTask *next_exec; // tasks scheduling
};
int dav1d_task_create_filter_sbrow(Dav1dFrameContext *f);
void dav1d_task_schedule(struct PostFilterThreadData *pftd, Dav1dTask *t);
void *dav1d_frame_task(void *data);
void *dav1d_tile_task(void *data);
void *dav1d_postfilter_task(void *data);
void *dav1d_worker_task(void *data);
int dav1d_decode_frame_init(Dav1dFrameContext *f);
int dav1d_decode_frame_main(Dav1dFrameContext *f);
void dav1d_decode_frame_exit(Dav1dFrameContext *f, int retval);
int dav1d_decode_frame(Dav1dFrameContext *f);
int dav1d_decode_tile_sbrow(Dav1dTileContext *t);
int dav1d_decode_tile_sbrow(Dav1dTaskContext *t);
#endif /* DAV1D_SRC_THREAD_TASK_H */
+8 -8
View File
@@ -106,23 +106,23 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
unsigned h = djb_xor(ptr, 32);
unsigned seed = h;
unsigned probability = h > (RAND_MAX >> 5) ? RAND_MAX >> 5 : h;
int n_frame_threads = (h & 0xf) + 1;
int n_tile_threads = ((h >> 4) & 0x7) + 1;
if (n_frame_threads > 5) n_frame_threads = 1;
if (n_tile_threads > 3) n_tile_threads = 1;
int max_frame_delay = (h & 0xf) + 1;
int n_threads = ((h >> 4) & 0x7) + 1;
if (max_frame_delay > 5) max_frame_delay = 1;
if (n_threads > 3) n_threads = 1;
#endif
ptr += 32; // skip ivf header
dav1d_default_settings(&settings);
#ifdef DAV1D_MT_FUZZING
settings.n_frame_threads = settings.n_tile_threads = 2;
settings.max_frame_delay = settings.n_threads = 4;
#elif defined(DAV1D_ALLOC_FAIL)
settings.n_frame_threads = n_frame_threads;
settings.n_tile_threads = n_tile_threads;
settings.max_frame_delay = max_frame_delay;
settings.n_threads = n_threads;
dav1d_setup_alloc_fail(seed, probability);
#else
settings.n_frame_threads = settings.n_tile_threads = 1;
settings.max_frame_delay = settings.n_threads = 1;
#endif
#if defined(DAV1D_FUZZ_MAX_SIZE)
settings.frame_size_limit = DAV1D_FUZZ_MAX_SIZE;
+13 -19
View File
@@ -50,9 +50,8 @@ enum {
ARG_FRAME_TIMES,
ARG_REALTIME,
ARG_REALTIME_CACHE,
ARG_FRAME_THREADS,
ARG_TILE_THREADS,
ARG_POSTFILTER_THREADS,
ARG_THREADS,
ARG_FRAME_DELAY,
ARG_VERIFY,
ARG_FILM_GRAIN,
ARG_OPPOINT,
@@ -73,9 +72,8 @@ static const struct option long_opts[] = {
{ "skip", 1, NULL, 's' },
{ "realtime", 2, NULL, ARG_REALTIME },
{ "realtimecache", 1, NULL, ARG_REALTIME_CACHE },
{ "framethreads", 1, NULL, ARG_FRAME_THREADS },
{ "tilethreads", 1, NULL, ARG_TILE_THREADS },
{ "pfthreads", 1, NULL, ARG_POSTFILTER_THREADS },
{ "threads", 1, NULL, ARG_THREADS },
{ "framedelay", 1, NULL, ARG_FRAME_DELAY },
{ "verify", 1, NULL, ARG_VERIFY },
{ "filmgrain", 1, NULL, ARG_FILM_GRAIN },
{ "oppoint", 1, NULL, ARG_OPPOINT },
@@ -124,9 +122,9 @@ static void usage(const char *const app, const char *const reason, ...) {
" --realtime [$fract]: limit framerate, optional argument to override input framerate\n"
" --realtimecache $num: set the size of the cache in realtime mode (default: 0)\n"
" --version/-v: print version and exit\n"
" --framethreads $num: number of frame threads (default: 1)\n"
" --tilethreads $num: number of tile threads (default: 1)\n"
" --pfthreads $num: number of postfilter threads (default: 1)\n"
" --threads $num: number of threads (default: 1)\n"
" --framedelay $num: maximum frame delay, capped at $threads (default: 8);\n"
" set to 1 for low-latency decoding\n"
" --filmgrain $num: enable film grain application (default: 1, except if muxer is md5 or xxh3)\n"
" --oppoint $num: select an operating point of a scalable AV1 bitstream (0 - 31)\n"
" --alllayers $num: output all spatial layers of a scalable AV1 bitstream (default: 1)\n"
@@ -299,17 +297,13 @@ void parse(const int argc, char *const *const argv,
cli_settings->realtime_cache =
parse_unsigned(optarg, ARG_REALTIME_CACHE, argv[0]);
break;
case ARG_FRAME_THREADS:
lib_settings->n_frame_threads =
parse_unsigned(optarg, ARG_FRAME_THREADS, argv[0]);
case ARG_FRAME_DELAY:
lib_settings->max_frame_delay =
parse_unsigned(optarg, ARG_FRAME_DELAY, argv[0]);
break;
case ARG_TILE_THREADS:
lib_settings->n_tile_threads =
parse_unsigned(optarg, ARG_TILE_THREADS, argv[0]);
break;
case ARG_POSTFILTER_THREADS:
lib_settings->n_postfilter_threads =
parse_unsigned(optarg, ARG_POSTFILTER_THREADS, argv[0]);
case ARG_THREADS:
lib_settings->n_threads =
parse_unsigned(optarg, ARG_THREADS, argv[0]);
break;
case ARG_VERIFY:
cli_settings->verify = optarg;