| /* |
| * Copyright © 2018, VideoLAN and dav1d authors |
| * Copyright © 2018, Two Orioles, LLC |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright notice, this |
| * list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR |
| * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| |
| #include "src/thread_task.h" |
| |
| void *dav1d_frame_task(void *const data) { |
| Dav1dFrameContext *const f = data; |
| |
| dav1d_set_thread_name("dav1d-frame"); |
| dav1d_pthread_mutex_lock(&f->frame_thread.td.lock); |
| for (;;) { |
| while (!f->n_tile_data && !f->frame_thread.die) { |
| dav1d_pthread_cond_wait(&f->frame_thread.td.cond, |
| &f->frame_thread.td.lock); |
| } |
| if (f->frame_thread.die) break; |
| dav1d_pthread_mutex_unlock(&f->frame_thread.td.lock); |
| |
| const int res = dav1d_decode_frame(f); |
| if (res) |
| memset(f->frame_thread.cf, 0, |
| (size_t)f->frame_thread.cf_sz * 128 * 128 / 2); |
| |
| dav1d_pthread_mutex_lock(&f->frame_thread.td.lock); |
| f->n_tile_data = 0; |
| dav1d_pthread_cond_signal(&f->frame_thread.td.cond); |
| } |
| dav1d_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 (;;) { |
| dav1d_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; |
| dav1d_pthread_cond_signal(&fttd->icond); |
| } |
| dav1d_pthread_cond_wait(&fttd->cond, &fttd->lock); |
| } |
| if (t->tile_thread.die) { |
| dav1d_pthread_cond_signal(&fttd->icond); |
| dav1d_pthread_mutex_unlock(&fttd->lock); |
| break; |
| } |
| fttd->available &= ~mask; |
| const int task_idx = fttd->num_tasks - fttd->tasks_left--; |
| dav1d_pthread_mutex_unlock(&fttd->lock); |
| |
| 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) |
| { |
| int error = dav1d_decode_tile_sbrow(t); |
| int progress = error ? TILE_ERROR : 1 + (t->by >> f->sb_shift); |
| |
| // signal progress |
| dav1d_pthread_mutex_lock(&ts->tile_thread.lock); |
| atomic_store(&ts->progress, progress); |
| dav1d_pthread_cond_signal(&ts->tile_thread.cond); |
| dav1d_pthread_mutex_unlock(&ts->tile_thread.lock); |
| if (error) break; |
| } |
| } 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]; |
| 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) { |
| dav1d_pthread_mutex_lock(&ts->tile_thread.lock); |
| while ((progress = atomic_load(&ts->progress)) < sby) |
| dav1d_pthread_cond_wait(&ts->tile_thread.cond, |
| &ts->tile_thread.lock); |
| dav1d_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; |
| int error = dav1d_decode_tile_sbrow(t); |
| progress = error ? TILE_ERROR : 1 + sby; |
| |
| // signal progress |
| dav1d_pthread_mutex_lock(&ts->tile_thread.lock); |
| atomic_store(&ts->progress, progress); |
| dav1d_pthread_cond_broadcast(&ts->tile_thread.cond); |
| dav1d_pthread_mutex_unlock(&ts->tile_thread.lock); |
| } |
| } |
| |
| return NULL; |
| } |