static av_always_inline int decode_mb_row_no_filter(AVCodecContext *avctx, void *tdata,
                                        int jobnr, int threadnr, int is_vp7)
{
    VP8Context *s = avctx->priv_data;
    VP8ThreadData *prev_td, *next_td, *td = &s->thread_data[threadnr];
    int mb_y = atomic_load(&td->thread_mb_pos) >> 16;
    int mb_x, mb_xy = mb_y * s->mb_width;
    int num_jobs = s->num_jobs;
    VP8Frame *curframe = s->curframe, *prev_frame = s->prev_frame;
    VP56RangeCoder *c  = &s->coeff_partition[mb_y & (s->num_coeff_partitions - 1)];
    VP8Macroblock *mb;
    uint8_t *dst[3] = {
        curframe->tf.f->data[0] + 16 * mb_y * s->linesize,
        curframe->tf.f->data[1] +  8 * mb_y * s->uvlinesize,
        curframe->tf.f->data[2] +  8 * mb_y * s->uvlinesize
    };

    if (c->end <= c->buffer && c->bits >= 0)
         return AVERROR_INVALIDDATA;

    if (mb_y == 0)
        prev_td = td;
    else
        prev_td = &s->thread_data[(jobnr + num_jobs - 1) % num_jobs];
    if (mb_y == s->mb_height - 1)
        next_td = td;
    else
        next_td = &s->thread_data[(jobnr + 1) % num_jobs];
    if (s->mb_layout == 1)
        mb = s->macroblocks_base + ((s->mb_width + 1) * (mb_y + 1) + 1);
    else {
        // Make sure the previous frame has read its segmentation map,
        // if we re-use the same map.
        if (prev_frame && s->segmentation.enabled &&
            !s->segmentation.update_map)
            ff_thread_await_progress(&prev_frame->tf, mb_y, 0);
        mb = s->macroblocks + (s->mb_height - mb_y - 1) * 2;
        memset(mb - 1, 0, sizeof(*mb)); // zero left macroblock
        AV_WN32A(s->intra4x4_pred_mode_left, DC_PRED * 0x01010101);
    }

    if (!is_vp7 || mb_y == 0)
        memset(td->left_nnz, 0, sizeof(td->left_nnz));

    td->mv_bounds.mv_min.x = -MARGIN;
    td->mv_bounds.mv_max.x = ((s->mb_width - 1) << 6) + MARGIN;

    for (mb_x = 0; mb_x < s->mb_width; mb_x++, mb_xy++, mb++) {
        if (c->end <= c->buffer && c->bits >= 0)
            return AVERROR_INVALIDDATA;
        // Wait for previous thread to read mb_x+2, and reach mb_y-1.
        if (prev_td != td) {
            if (threadnr != 0) {
                check_thread_pos(td, prev_td,
                                 mb_x + (is_vp7 ? 2 : 1),
                                 mb_y - (is_vp7 ? 2 : 1));
            } else {
                check_thread_pos(td, prev_td,
                                 mb_x + (is_vp7 ? 2 : 1) + s->mb_width + 3,
                                 mb_y - (is_vp7 ? 2 : 1));
            }
        }

        s->vdsp.prefetch(dst[0] + (mb_x & 3) * 4 * s->linesize + 64,
                         s->linesize, 4);
        s->vdsp.prefetch(dst[1] + (mb_x & 7) * s->uvlinesize + 64,
                         dst[2] - dst[1], 2);

        if (!s->mb_layout)
            decode_mb_mode(s, &td->mv_bounds, mb, mb_x, mb_y, curframe->seg_map->data + mb_xy,
                           prev_frame && prev_frame->seg_map ?
                           prev_frame->seg_map->data + mb_xy : NULL, 0, is_vp7);

        prefetch_motion(s, mb, mb_x, mb_y, mb_xy, VP56_FRAME_PREVIOUS);

        if (!mb->skip)
            decode_mb_coeffs(s, td, c, mb, s->top_nnz[mb_x], td->left_nnz, is_vp7);

        if (mb->mode <= MODE_I4x4)
            intra_predict(s, td, dst, mb, mb_x, mb_y, is_vp7);
        else
            inter_predict(s, td, dst, mb, mb_x, mb_y);

        prefetch_motion(s, mb, mb_x, mb_y, mb_xy, VP56_FRAME_GOLDEN);

        if (!mb->skip) {
            idct_mb(s, td, dst, mb);
        } else {
            AV_ZERO64(td->left_nnz);
            AV_WN64(s->top_nnz[mb_x], 0);   // array of 9, so unaligned

            /* Reset DC block predictors if they would exist
             * if the mb had coefficients */
            if (mb->mode != MODE_I4x4 && mb->mode != VP8_MVMODE_SPLIT) {
                td->left_nnz[8]     = 0;
                s->top_nnz[mb_x][8] = 0;
            }
        }

        if (s->deblock_filter)
            filter_level_for_mb(s, mb, &td->filter_strength[mb_x], is_vp7);

        if (s->deblock_filter && num_jobs != 1 && threadnr == num_jobs - 1) {
            if (s->filter.simple)
                backup_mb_border(s->top_border[mb_x + 1], dst[0],
                                 NULL, NULL, s->linesize, 0, 1);
            else
                backup_mb_border(s->top_border[mb_x + 1], dst[0],
                                 dst[1], dst[2], s->linesize, s->uvlinesize, 0);
        }

        prefetch_motion(s, mb, mb_x, mb_y, mb_xy, VP56_FRAME_GOLDEN2);

        dst[0]      += 16;
        dst[1]      += 8;
        dst[2]      += 8;
        td->mv_bounds.mv_min.x -= 64;
        td->mv_bounds.mv_max.x -= 64;

        if (mb_x == s->mb_width + 1) {
            update_pos(td, mb_y, s->mb_width + 3);
        } else {
            update_pos(td, mb_y, mb_x);
        }
    }
    return 0;
}