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:
+7
-10
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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]
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+78
-48
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 */
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user