thread_task.c 5.83 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
/*
 * 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;

35
    pthread_mutex_lock(&f->frame_thread.td.lock);
36 37 38 39 40
    for (;;) {
        while (!f->n_tile_data && !f->frame_thread.die) {
            pthread_cond_wait(&f->frame_thread.td.cond,
                              &f->frame_thread.td.lock);
        }
41
        if (f->frame_thread.die) break;
42 43
        pthread_mutex_unlock(&f->frame_thread.td.lock);

44 45 46 47
        const int res = dav1d_decode_frame(f);
        if (res)
            memset(f->frame_thread.cf, 0,
                   sizeof(int32_t) * 3 * f->lf.mask_sz * 128 * 128);
48 49 50 51

        pthread_mutex_lock(&f->frame_thread.td.lock);
        f->n_tile_data = 0;
        pthread_cond_signal(&f->frame_thread.td.cond);
52
    }
53
    pthread_mutex_unlock(&f->frame_thread.td.lock);
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76

    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 = t - f->tc;
    const uint64_t mask = 1ULL << tile_thread_idx;

    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);
            }
            pthread_cond_wait(&fttd->cond, &fttd->lock);
        }
        if (t->tile_thread.die) {
77
            pthread_cond_signal(&fttd->icond);
78 79 80 81 82 83 84
            pthread_mutex_unlock(&fttd->lock);
            break;
        }
        fttd->available &= ~mask;
        const int task_idx = fttd->num_tasks - fttd->tasks_left--;
        pthread_mutex_unlock(&fttd->lock);

85
        if (f->frame_thread.pass == 1 || f->n_tc >= f->frame_hdr->tiling.cols) {
86 87 88 89 90 91
            // 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)
            {
92 93
                int error = dav1d_decode_tile_sbrow(t);
                int progress = error ? TILE_ERROR : 1 + (t->by >> f->sb_shift);
94 95 96

                // signal progress
                pthread_mutex_lock(&ts->tile_thread.lock);
97
                atomic_store(&ts->progress, progress);
98 99
                pthread_cond_signal(&ts->tile_thread.cond);
                pthread_mutex_unlock(&ts->tile_thread.lock);
100
                if (error) break;
101 102 103 104 105
            }
        } 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];
106
            int progress;
107 108 109 110 111 112 113

            // 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.
114
            if ((progress = atomic_load(&ts->progress)) < sby) {
115
                pthread_mutex_lock(&ts->tile_thread.lock);
116
                while ((progress = atomic_load(&ts->progress)) < sby)
117 118 119 120
                    pthread_cond_wait(&ts->tile_thread.cond,
                                      &ts->tile_thread.lock);
                pthread_mutex_unlock(&ts->tile_thread.lock);
            }
121
            if (progress == TILE_ERROR) continue;
122 123 124 125 126 127

            // 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;
128 129
            int error = dav1d_decode_tile_sbrow(t);
            progress = error ? TILE_ERROR : 1 + sby;
130 131 132

            // signal progress
            pthread_mutex_lock(&ts->tile_thread.lock);
133
            atomic_store(&ts->progress, progress);
134 135 136 137 138 139 140
            pthread_cond_broadcast(&ts->tile_thread.cond);
            pthread_mutex_unlock(&ts->tile_thread.lock);
        }
    }

    return NULL;
}