| /* | |
| * Copyright © 2018-2026, VideoLAN and dav2d authors | |
| * Copyright © 2018-2026, 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 <errno.h> | |
| #include <limits.h> | |
| #include <string.h> | |
| #include <stdio.h> | |
| #include <inttypes.h> | |
| #include "dav2d/data.h" | |
| #include "common/frame.h" | |
| #include "common/intops.h" | |
| #include "src/ctx.h" | |
| #include "src/decode.h" | |
| #include "src/env.h" | |
| #include "src/filmgrain.h" | |
| #include "src/log.h" | |
| #include "src/quantizer.h" | |
| #include "src/recon.h" | |
| #include "src/ref.h" | |
| #include "src/tables.h" | |
| #include "src/thread_task.h" | |
| #include "src/warpmv.h" | |
| #include "src/wedge.h" | |
| #include "src/wiener_tables.h" | |
| static void init_quant_tables(const Dav2dSequenceHeader *const seq_hdr, | |
| const Dav2dFrameHeader *const frame_hdr, | |
| const int n_seg, | |
| const int qidx, uint32_t (*dq)[3][2]) | |
| { | |
| const int qmax = 255 + 48 * seq_hdr->hbd; | |
| // and then ac == dc | |
| for (int i = 0; i < n_seg; i++) { | |
| const int yac = iclip(frame_hdr->segmentation.enabled ? | |
| qidx + frame_hdr->segmentation.d.delta_q[i] : | |
| qidx, 0, qmax); | |
| const int ydc = clip_qidx(yac, frame_hdr->quant.ydc_delta + | |
| seq_hdr->base_ydc_dq, qmax); | |
| const int uac = clip_qidx(yac, frame_hdr->quant.uac_delta + | |
| seq_hdr->base_uvac_dq, qmax); | |
| const int udc = clip_qidx(yac, frame_hdr->quant.udc_delta + | |
| seq_hdr->base_uvdc_dq, qmax); | |
| const int vac = clip_qidx(yac, frame_hdr->quant.vac_delta + | |
| seq_hdr->base_uvac_dq, qmax); | |
| const int vdc = clip_qidx(yac, frame_hdr->quant.vdc_delta + | |
| seq_hdr->base_uvdc_dq, qmax); | |
| dq[i][0][0] = dav2d_dq_lookup(ydc); | |
| dq[i][0][1] = dav2d_dq_lookup(yac); | |
| dq[i][1][0] = dav2d_dq_lookup(udc); | |
| dq[i][1][1] = dav2d_dq_lookup(uac); | |
| dq[i][2][0] = dav2d_dq_lookup(vdc); | |
| dq[i][2][1] = dav2d_dq_lookup(vac); | |
| } | |
| } | |
| static inline void init_wiener(Dav2dFrameContext *const f) { | |
| const enum Dav2dRestorationType type = f->frame_hdr->restoration.p[0].type; | |
| if (type == DAV2D_RESTORATION_NONE) return; | |
| ptrdiff_t class_lut_offset; | |
| const int bitdepth_min_8 = f->seq_hdr->hbd * 2; | |
| const int qmax = 255 + 48 * f->seq_hdr->hbd; | |
| const int qidx = imin(f->frame_hdr->quant.yac, qmax); | |
| const int base_q = dav2d_dq_lookup(qidx); | |
| const int qstep = (base_q + (1 << bitdepth_min_8 >> 1)) >> bitdepth_min_8; | |
| setup_wiener_qval_approx_data(&f->lf.wiener_qval_approx_data, | |
| &class_lut_offset, | |
| qstep); | |
| int idx = 3; | |
| if (qidx < 130) { | |
| idx = 0; | |
| } else if (qidx < 190) { | |
| idx = 1; | |
| } else if (qidx < 220) { | |
| idx = 2; | |
| } | |
| if (type == DAV2D_RESTORATION_NS_WIENER || type == DAV2D_RESTORATION_SWITCHABLE) { | |
| int num_classes_idx = f->frame_hdr->restoration.p[0].ns.num_classes_idx; | |
| if (num_classes_idx) { | |
| f->lf.ns_subclass_lut = dav2d_user_wiener_class[idx][num_classes_idx - 1] + | |
| class_lut_offset; | |
| } | |
| } | |
| if (type == DAV2D_RESTORATION_PC_WIENER || type == DAV2D_RESTORATION_SWITCHABLE) { | |
| f->lf.pc_subclass_lut = dav2d_pretrained_wiener_class[idx] + | |
| class_lut_offset; | |
| f->lf.pc_filters = dav2d_pc_wiener_filters[idx]; | |
| } | |
| } | |
| static inline void read_amvd(Dav2dTileState *const ts, mv *const mv) { | |
| const int joint = dav2d_msac_decode_symbol_adapt4(&ts->msac, | |
| ts->cdf.m.amvd_joint, 3); | |
| if (!joint) { | |
| mv->n = 0; | |
| return; | |
| } | |
| if (joint & 2) { | |
| const int s = dav2d_msac_decode_symbol_adapt8(&ts->msac, | |
| ts->cdf.m.amvd_index[0], 7); | |
| mv->y = s < 3 ? 2 + s * 2 : 1 << s; | |
| } else mv->y = 0; | |
| if (joint & 1) { | |
| const int s = dav2d_msac_decode_symbol_adapt8(&ts->msac, | |
| ts->cdf.m.amvd_index[1], 7); | |
| mv->x = s < 3 ? 2 + s * 2 : 1 << s; | |
| } else mv->x = 0; | |
| } | |
| // mv_prec=0..6 for {8,4,2,f,h,q,e}pel | |
| static inline void read_mv_residual(Dav2dTileState *const ts, | |
| CdfMvContext *const cdf_mv, mv *const mv, | |
| const int mv_prec) | |
| { | |
| int sh_class; | |
| const int n_syms = 9 + mv_prec, h_syms = n_syms >> 1; | |
| if (dav2d_msac_decode_bool_adapt(&ts->msac, cdf_mv->shell_set)) { | |
| const int h_syms2 = n_syms - h_syms; | |
| sh_class = h_syms + 1 + | |
| dav2d_msac_decode_symbol_adapt8(&ts->msac, | |
| cdf_mv->shell_upper[mv_prec], imin(h_syms2, 7)); | |
| if (mv_prec + sh_class == 21) | |
| sh_class += dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.mv.shell_tip); | |
| } else { | |
| sh_class = dav2d_msac_decode_symbol_adapt8(&ts->msac, | |
| cdf_mv->shell_lower[mv_prec], h_syms); | |
| } | |
| int sh_index; | |
| if (sh_class < 2) { | |
| sh_index = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| cdf_mv->shell_offset_low[sh_class]); | |
| } else if (sh_class == 2) { | |
| sh_index = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| cdf_mv->shell_offset_cl2); | |
| if (sh_index) { | |
| sh_index += dav2d_msac_decode_bool_bypass(&ts->msac); | |
| if (sh_index == 2) | |
| sh_index += dav2d_msac_decode_bool_bypass(&ts->msac); | |
| } | |
| } else { | |
| sh_index = 0; | |
| for (int i = 0, m = 1; i < sh_class; i++, m <<= 1) { | |
| sh_index |= m * dav2d_msac_decode_bool_adapt(&ts->msac, | |
| cdf_mv->shell_offset_hi[i]); | |
| } | |
| } | |
| if (sh_class) sh_index += 1 << sh_class; | |
| if (!sh_index) { | |
| mv->n = 0; | |
| return; | |
| } | |
| int pair_index = 0; | |
| if (sh_index >= 2) { | |
| pair_index = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| cdf_mv->col_component[0]); | |
| if (pair_index && sh_index >= 4) { | |
| pair_index += dav2d_msac_decode_bool_adapt(&ts->msac, | |
| cdf_mv->col_component[1]); | |
| if (pair_index == 2 && sh_index >= 6) | |
| pair_index += dav2d_msac_decode_uniform(&ts->msac, | |
| (sh_index >> 1) - 1); | |
| } | |
| } | |
| const int sh = 6 - mv_prec; | |
| if (pair_index * 2 == sh_index) { | |
| mv->x = mv->y = (sh_index >> 1) << sh; | |
| } else { | |
| const int b = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| cdf_mv->col_index[imin(sh_class, 3)]); | |
| if (b) { | |
| mv->y = pair_index << sh; | |
| mv->x = (sh_index - pair_index) << sh; | |
| } else { | |
| mv->x = pair_index << sh; | |
| mv->y = (sh_index - pair_index) << sh; | |
| } | |
| } | |
| } | |
| static int neg_deinterleave(int diff, int ref, int max) { | |
| if (!ref) return diff; | |
| if (ref >= (max - 1)) return max - diff - 1; | |
| if (2 * ref < max) { | |
| if (diff <= 2 * ref) { | |
| if (diff & 1) | |
| return ref + ((diff + 1) >> 1); | |
| else | |
| return ref - (diff >> 1); | |
| } | |
| return diff; | |
| } else { | |
| if (diff <= 2 * (max - ref - 1)) { | |
| if (diff & 1) | |
| return ref + ((diff + 1) >> 1); | |
| else | |
| return ref - (diff >> 1); | |
| } | |
| return max - (diff + 1); | |
| } | |
| } | |
| static void derive_warpmv(const Dav2dTaskContext *const t, | |
| const int have_top, const int have_left, | |
| const int bw4, const int bh4, | |
| const int w4, const int h4, | |
| const int ref, const union mv mv, | |
| Dav2dWarpedMotionParams *const wmp) | |
| { | |
| int pts[8][2 /* in, out */][2 /* x, y */], np = 0; | |
| const refmvs_block *const r = &t->rt.r[(t->by & 63) * 128], *ra; | |
| #define bs(rp) dav2d_block_dimensions[(rp)->bs] | |
| #define add_sample(dx, dy, sx, sy, rp) do { \ | |
| const union mv *const rmv = (rp)->mf & 2 ? (rp)->lmv : (rp)->mv; \ | |
| for (int n = 0; n < 2; n++) { \ | |
| if ((rp)->ref.ref[n] != ref) continue; \ | |
| pts[np][0][0] = 16 * (2 * (dx) + sx * bs(rp)[0]) - 8; \ | |
| pts[np][0][1] = 16 * (2 * dy + sy * bs(rp)[1]) - 8; \ | |
| pts[np][1][0] = pts[np][0][0] + rmv[n].x; \ | |
| pts[np][1][1] = pts[np][0][1] + rmv[n].y; \ | |
| if (++np == 8) break; \ | |
| } \ | |
| } while (0) | |
| assert(bw4 > 1); | |
| const Dav2dFrameContext *const f = t->f; | |
| int have_topleft = 0; | |
| int have_topright = 0; | |
| const int is_not_sb_boundary = t->by & (f->sb_step - 1); | |
| if (have_top) { | |
| if (is_not_sb_boundary) { | |
| ra = &t->rt.r[((t->by - 1) & 63) * 128]; | |
| const refmvs_block *r2 = &ra[(t->bx & 127)]; | |
| int off = r2->bx4 - t->bx; | |
| have_topleft = !off; | |
| do { | |
| add_sample(off, 0, 1, -1, &r2[off]); | |
| off += bs(&r2[off])[0]; | |
| } while (off < w4 && np < 8); | |
| have_topright = off <= bw4; | |
| } else { | |
| ra = t->rt.ra; | |
| const refmvs_block *r2 = &ra[t->bx >> 1]; | |
| have_topleft = 1; | |
| // if the block pointed to by our rounded-down top/left coordinate | |
| // doesn't intersect with us, skip to the next one. This block will | |
| // instead be handled later on as a top/left candidate. | |
| int off = r2->bx4 + bs(r2)[0] <= t->bx; | |
| // at the top/right edge, we want to include blocks if they | |
| // intersect with us. Otherwise, they will be handled as top/right | |
| // candidates further down. | |
| const int tr_ext = (t->bx + bw4) & (f->sb_step - 1) && | |
| t->bx + bw4 < t->ts->tiling.col_end && | |
| ra[(t->bx + bw4) >> 1].bx4 < t->bx + bw4; | |
| do { | |
| const int off8 = (t->bx + off) >> 1; | |
| const int ioff = ra[off8].bx4 - t->bx; | |
| add_sample(ioff, 0, 1, -1, &ra[off8]); | |
| // the +1 prevents us from re-iterating over the same block | |
| // multiple times. Since we round down in the indexing a few | |
| // lines up and offset from the actual candidate position here, | |
| // this happens to work. | |
| off = ioff + bs(&ra[off8])[0] + 1; | |
| } while (off < w4 + tr_ext && np < 8); | |
| have_topright = 1; | |
| } | |
| have_topright &= bw4 <= 16 && | |
| t->bx + bw4 + !is_not_sb_boundary < t->ts->tiling.col_end && | |
| (!(t->by & (f->sb_step - 1)) || // top sb boundary | |
| ((t->bx + bw4) & (f->sb_step - 1) && // right sb boundary | |
| ra[(t->bx + bw4) & 127].mv[0].n != INVALID_MV)); | |
| } | |
| if (np < 8 && have_left) { | |
| const refmvs_block *r2 = &r[(t->bx - 1) & 127]; | |
| int off = r2->by4 - t->by; | |
| have_topleft &= !off; | |
| do { | |
| add_sample(0, off, -1, 1, &r2[off * 128]); | |
| off += bs(&r2[off * 128])[1]; | |
| } while (off < h4 && np < 8); | |
| } else | |
| have_topleft = 0; | |
| if (is_not_sb_boundary) { | |
| if (np < 8 && have_topleft) // top/left | |
| add_sample(0, 0, -1, -1, &ra[((t->bx - 1) & 127)]); | |
| if (np < 8 && have_topright) // top/right | |
| add_sample(bw4, 0, 1, -1, &ra[((t->bx + bw4) & 127)]); | |
| } else { | |
| if (np < 8 && have_topleft) { // top/left | |
| const refmvs_block *const r2 = t->bx & (f->sb_step - 1) ? | |
| &ra[(t->bx - 1) >> 1] : &t->rt.ra_tl; | |
| if (dav2d_block_dimensions[r2->bs][0] + r2->bx4 == t->bx) | |
| add_sample(0, 0, -1, -1, r2); | |
| } | |
| if (np < 8 && have_topright) { // top/right | |
| const refmvs_block *const r2 = &ra[(t->bx + bw4 + 1) >> 1]; | |
| if (r2->bx4 == t->bx + bw4) | |
| add_sample(bw4, 0, 1, -1, r2); | |
| } | |
| } | |
| assert(np > 0 && np <= 8); | |
| #undef bs | |
| if (!dav2d_find_affine_int(pts, np, bw4, bh4, mv, wmp, t->bx, t->by) && | |
| !dav2d_get_shear_params(wmp)) | |
| { | |
| wmp->type = warp_type(wmp->matrix); | |
| } else | |
| wmp->type = DAV2D_WM_TYPE_INVALID; | |
| } | |
| static void extend_warpmv(Dav2dTaskContext *const t, | |
| const int x_off, const int y_off, | |
| const uint8_t *const b_dim, | |
| const Av2Block *const b, | |
| Dav2dWarpedMotionParams *const wmp) | |
| { | |
| const Dav2dFrameContext *const f = t->f; | |
| const refmvs_block *const r = | |
| // did we cross a vertical SB boundary? | |
| y_off == -1 && !(t->by & (f->sb_step - 1)) ? | |
| // did we cross a horizontal SB boundary? | |
| t->bx + x_off < (t->bx & ~(f->sb_step - 1)) ? | |
| &t->rt.ra_tl : &t->rt.ra[(t->bx + x_off) >> 1] : | |
| &t->rt.r[((t->by + y_off) & 63) * 128 + ((t->bx + x_off) & 127)]; | |
| int32_t *const m = wmp->matrix; | |
| if (r->mf & 2) { | |
| memcpy(m, r->m, sizeof(*m) * 6); | |
| } else if (r->mf & 1) { | |
| memcpy(m, t->f->frame_hdr->gmv.m[b->ref.ref[0]].matrix, sizeof(*m) * 6); | |
| } else { | |
| memcpy(&m[2], &dav2d_default_wm_params.matrix[2], sizeof(*m) * 4); | |
| const int ref = r->ref.ref[0] != b->ref.ref[0]; | |
| m[0] = r->mv[ref].x * (1 << 13); | |
| m[1] = r->mv[ref].y * (1 << 13); | |
| } | |
| // extend warpmv using (quasi-)matrix from neighbour | |
| const int bw4 = b_dim[0], bh4 = b_dim[1]; | |
| const int sx = t->bx * 4 + 2 * bw4 - 1, sy = t->by * 4 + 2 * bh4 - 1; | |
| const int64_t px = ((int64_t) sx << 16) + b->mv[0].x * (1 << 13); | |
| const int64_t py = ((int64_t) sy << 16) + b->mv[0].y * (1 << 13); | |
| if (x_off >= 0) { | |
| assert(y_off == -1); | |
| const int ay = t->by * 4 - 1, sh = 1 + b_dim[3]; | |
| const int64_t apx = (int64_t) m[2] * sx + (int64_t) m[3] * ay + m[0]; | |
| const int64_t apy = (int64_t) m[4] * sx + (int64_t) m[5] * ay + m[1]; | |
| const int m3 = (int) ((px - apx + bh4 - (px < apx)) >> sh); | |
| const int m5 = (int) ((py - apy + bh4 - (py < apy)) >> sh); | |
| m[3] = iclip((m3 + 0x20 - (m3 < 0)) & ~0x3f, -0x7fc0, 0x7fc0); | |
| m[5] = iclip((m5 + 0x20 - (m5 < 0x10000)) & ~0x3f, 0x8040, 0x17fc0); | |
| } else { | |
| assert(x_off == -1 || !(t->by & (t->f->sb_step - 1))); | |
| const int ax = t->bx * 4 - 1, sh = 1 + b_dim[2]; | |
| const int64_t lpx = (int64_t) m[2] * ax + (int64_t) m[3] * sy + m[0]; | |
| const int64_t lpy = (int64_t) m[4] * ax + (int64_t) m[5] * sy + m[1]; | |
| const int m2 = (int) ((px - lpx + bw4 - (px < lpx)) >> sh); | |
| const int m4 = (int) ((py - lpy + bw4 - (py < lpy)) >> sh); | |
| m[2] = iclip((m2 + 0x20 - (m2 < 0x10000)) & ~0x3f, 0x8040, 0x17fc0); | |
| m[4] = iclip((m4 + 0x20 - (m4 < 0)) & ~0x3f, -0x7fc0, 0x7fc0); | |
| } | |
| dav2d_set_affine_mv2d(bw4, bh4, b->mv[0], wmp, t->bx, t->by); | |
| wmp->type = dav2d_get_shear_params(wmp) ? DAV2D_WM_TYPE_INVALID : warp_type(m); | |
| } | |
| static int read_pal_indices(Dav2dTaskContext *const t, uint8_t *const pal_out, | |
| const int pal_sz, const int sz[4]) | |
| { | |
| Dav2dTileState *const ts = t->ts; | |
| uint16_t (*const pal_cdf)[8] = ts->cdf.m.pal_idx[pal_sz - 2]; | |
| uint8_t *const pal_idx = t->scratch.pal_idx_y; | |
| const int dir = imax(sz[2], sz[3]) < 64 && | |
| dav2d_msac_decode_bool_bypass(&ts->msac); | |
| const ptrdiff_t strides[2] = { dir ? 1 : sz[2], dir ? sz[2] : 1 }; | |
| const int lim1 = sz[!dir], lim2 = sz[dir]; | |
| int copy = dav2d_msac_decode_symbol_adapt4(&ts->msac, | |
| ts->cdf.m.pal_idx_identity[3], 2); | |
| if (copy == 2) return -1; | |
| int prev_v = pal_idx[0] = dav2d_msac_decode_uniform(&ts->msac, pal_sz); | |
| if (copy == 1) { | |
| // FIXME if dir=0, maybe use memset()? | |
| for (int m = 1; m < lim2; m++) | |
| pal_idx[m * strides[1]] = prev_v; | |
| } else { | |
| int prev_h = prev_v; | |
| for (int m = 1; m < lim2; m++) { | |
| const int v = dav2d_msac_decode_symbol_adapt8(&ts->msac, pal_cdf[0], | |
| pal_sz - 1); | |
| prev_h = pal_idx[m * strides[1]] = !v ? prev_h : v - (v <= prev_h); | |
| } | |
| } | |
| ptrdiff_t off = strides[0]; | |
| for (int n = 1; n < lim1; n++, off += strides[0]) { | |
| copy = dav2d_msac_decode_symbol_adapt4(&ts->msac, | |
| ts->cdf.m.pal_idx_identity[copy], 2); | |
| if (copy == 2) { | |
| // FIXME if dir=0, maybe use memcpy()? | |
| for (int m = 0; m < lim2; m++) | |
| pal_idx[off + m * strides[1]] = | |
| pal_idx[off - strides[0] + m * strides[1]]; | |
| } else { | |
| const int v = dav2d_msac_decode_symbol_adapt8(&ts->msac, pal_cdf[0], | |
| pal_sz - 1); | |
| const int next_v = pal_idx[off] = !v ? prev_v : v - (v <= prev_v); | |
| if (copy == 1) { | |
| // FIXME if dir=0, maybe use memset()? | |
| for (int m = 1; m < lim2; m++) | |
| pal_idx[off + m * strides[1]] = next_v; | |
| } else { | |
| int prev_tl = prev_v, prev_l = next_v; | |
| for (int m = 1; m < lim2; m++) { | |
| int prev_t = pal_idx[off - strides[0] + m * strides[1]]; | |
| int ctx; | |
| if (prev_t == prev_l) { | |
| ctx = 3 + (prev_tl == prev_l); | |
| } else { | |
| ctx = 1 + (prev_t == prev_tl || prev_l == prev_tl); | |
| } | |
| const int v = dav2d_msac_decode_symbol_adapt8(&ts->msac, | |
| pal_cdf[ctx], pal_sz - 1); | |
| int p; | |
| switch (ctx) { | |
| default: dav2d_unreachable(); | |
| case 1: { | |
| switch (v) { | |
| case 0: | |
| case 1: p = v == dir ? prev_l : prev_t; break; | |
| case 2: p = prev_tl; break; | |
| default: { | |
| const int s1 = prev_l < prev_t; | |
| const int s2 = prev_l < prev_tl; | |
| const int s3 = prev_t < prev_tl; | |
| p = v - (v <= prev_l + s1 + s2) - | |
| (v <= prev_t + s3 + !s1) - | |
| (v <= prev_tl + !s2 + !s3); | |
| break; | |
| }} | |
| break; | |
| } | |
| case 2: { | |
| const int prev_l_or_t = prev_l + prev_t - prev_tl; | |
| switch (v) { | |
| case 0: p = prev_tl; break; | |
| case 1: p = prev_l_or_t; break; | |
| default: { | |
| const int s = prev_l_or_t < prev_tl; | |
| p = v - (v <= prev_l_or_t + s) - (v <= prev_tl + !s); | |
| break; | |
| }} | |
| break; | |
| } | |
| case 3: { | |
| switch (v) { | |
| case 0: p = prev_l; break; | |
| case 1: p = prev_tl; break; | |
| default: { | |
| const int s = prev_l < prev_tl; | |
| p = v - (v <= prev_l + s) - (v <= prev_tl + !s); | |
| break; | |
| }} | |
| break; | |
| } | |
| case 4: | |
| p = !v ? prev_l : v - (v <= prev_l); | |
| break; | |
| } | |
| prev_l = pal_idx[off + m * strides[1]] = p; | |
| prev_tl = prev_t; | |
| } | |
| } | |
| prev_v = next_v; | |
| } | |
| } | |
| t->c->pal_dsp.pal_idx_finish(pal_out, pal_idx, sz[2], sz[3], sz[0], sz[1]); | |
| return 0; | |
| } | |
| static inline unsigned get_prev_frame_segid(const Dav2dFrameContext *const f, | |
| const int by, const int bx, | |
| const int w4, int h4, | |
| const uint8_t *ref_seg_map, | |
| const ptrdiff_t stride) | |
| { | |
| assert(f->frame_hdr->primary_ref_frame != DAV2D_PRIMARY_REF_NONE); | |
| unsigned seg_id = f->n_seg; | |
| ref_seg_map += by * stride + bx; | |
| do { | |
| for (int x = 0; x < w4; x++) | |
| seg_id = imin(seg_id, ref_seg_map[x]); | |
| ref_seg_map += stride; | |
| } while (--h4 > 0 && seg_id); | |
| assert(seg_id < (unsigned) f->n_seg); | |
| return seg_id; | |
| } | |
| #if DEBUG_BLOCK_INFO | |
| static void debug_warp_matrix(const int depth, | |
| const Dav2dFrameContext *const f, | |
| const Dav2dTaskContext *const t, | |
| const Av2Block *const b, const int r) | |
| { | |
| #define signabs(v) v < 0 ? '-' : ' ', abs(v) | |
| DEBUG_BLOCK_printf("%*s[ %c%x, %c%x | %c%x, %c%x, %c%x, %c%x ] " | |
| "mv=y:%d,x:%d\n", depth, "", | |
| signabs(t->warpmv[r].matrix[0]), | |
| signabs(t->warpmv[r].matrix[1]), | |
| signabs(t->warpmv[r].matrix[2]), | |
| signabs(t->warpmv[r].matrix[3]), | |
| signabs(t->warpmv[r].matrix[4]), | |
| signabs(t->warpmv[r].matrix[5]), | |
| b->mv[r].y, b->mv[r].x); | |
| #undef signabs | |
| } | |
| #else | |
| #define debug_warp_matrix(...) | |
| #endif | |
| static inline void splat_oneref_mv(DB_ONLY(const int depth) | |
| const Dav2dFrameContext *const f, | |
| Dav2dTaskContext *const t, | |
| const enum BlockSize bs, | |
| const Av2Block *const b, | |
| const int by4, const int bx4, | |
| const int bw4, const int bh4) | |
| { | |
| refmvs_block *const s_dst = &t->rt.r[by4 * 128 + (t->bx & 127)]; | |
| refmvs_block s_src; | |
| const ptrdiff_t t_stride = f->rf.rp_stride; | |
| refmvs_temporal_block *const t_dst = | |
| f->seq_hdr->ref_frame_mvs && b->ref.ref[0] != TIP_FRAME ? | |
| &f->rf.rp[(t->by >> 1) * (t_stride >> 5) + (t->bx >> 6)] : NULL; | |
| s_src.ref.ref[0] = b->ref.ref[0]; | |
| union refpair t_ref = { .pair = 0x101 * b->ref.ref[0] }; | |
| s_src.ref.ref[1] = -1; | |
| s_src.mv[1].n = INVALID_MV; | |
| s_src.bs = bs; | |
| s_src.by4 = t->by; | |
| s_src.bx4 = t->bx; | |
| if (b->motion_mode > MM_INTERINTRA || | |
| (!f->frame_hdr->force_integer_mv && | |
| b->inter_mode == GLOBALMV && imin(bw4, bh4) > 1 && | |
| f->frame_hdr->gmv.m[b->ref.ref[0]].type > DAV2D_WM_TYPE_TRANSLATION)) | |
| { | |
| assert(bw4 > 1 && bh4 > 1); | |
| const int32_t *mat; | |
| if (b->motion_mode > MM_INTERINTRA) { | |
| s_src.lmv[0] = b->mv[0]; | |
| s_src.lmv[1].n = INVALID_MV; | |
| s_src.mf = 2; | |
| memcpy(s_src.m, t->warpmv[0].matrix, sizeof(int32_t) * 6); | |
| mat = f->frame_hdr->force_integer_mv ? | |
| NULL : t->warpmv[0].matrix; | |
| } else { | |
| s_src.mv[0] = b->mv[0]; | |
| s_src.mf = 1; | |
| mat = f->frame_hdr->gmv.m[b->ref.ref[0]].matrix; | |
| } | |
| f->c->refmvs_dsp.splat_warpmv(s_dst, &s_src, t_dst, bx4 >> 1, t_stride, | |
| bw4, bh4, t_ref, mat); | |
| } else { | |
| s_src.mv[0] = b->mv[0]; | |
| s_src.mf = b->inter_mode == GLOBALMV && imin(bw4, bh4) > 1; | |
| // this is invalid for TIP, but that will be overwritten in tip_pred() | |
| union qmvpair t_mv; | |
| t_mv.mv[0] = t_mv.mv[1] = quantize_mv(b->mv[0]); | |
| if (t_mv.mv[0].n == INVALID_TRAJ) t_ref.pair = -1; | |
| f->c->refmvs_dsp.splat_mv(s_dst, &s_src, t_dst, bx4 >> 1, t_stride, | |
| bw4, bh4, t_mv, t_ref); | |
| } | |
| } | |
| static inline void splat_intrabc_mv(DB_ONLY(const int depth) | |
| const Dav2dFrameContext *const f, | |
| Dav2dTaskContext *const t, | |
| const enum BlockSize bs, | |
| const Av2Block *const b, | |
| const int by4, const int bx4, | |
| const int bw4, const int bh4) | |
| { | |
| refmvs_block *const s_dst = &t->rt.r[by4 * 128 + (t->bx & 127)]; | |
| refmvs_block s_src = (refmvs_block) { | |
| .ref.pair = -1, | |
| .mv[0] = b->mv[0], | |
| .mv[1].n = INVALID_MV, | |
| .bs = bs, | |
| .by4 = t->by, | |
| .bx4 = t->bx, | |
| .mf = 0, | |
| }; | |
| const ptrdiff_t t_stride = f->rf.rp_stride; | |
| refmvs_temporal_block *const t_dst = f->seq_hdr->ref_frame_mvs ? | |
| &f->rf.rp[(t->by >> 1) * (t_stride >> 5) + (t->bx >> 6)] : NULL; | |
| f->c->refmvs_dsp.splat_mv(s_dst, &s_src, t_dst, bx4 >> 1, t_stride, bw4, bh4, | |
| (union qmvpair) { .n = INVALID_TRAJ * 0x10001U }, | |
| (union refpair) { .pair = -1 }); | |
| if (t->f->seq_hdr->refmv_bank) | |
| dav2d_refmvs_bank_add(&t->rt, bs, t->by, t->bx, b); | |
| } | |
| static inline void splat_tworef_mv(DB_ONLY(const int depth) | |
| const Dav2dFrameContext *const f, | |
| Dav2dTaskContext *const t, | |
| const enum BlockSize bs, | |
| const Av2Block *const b, | |
| const int by4, const int bx4, | |
| const int bw4, const int bh4) | |
| { | |
| refmvs_block *const s_dst = &t->rt.r[by4 * 128 + (t->bx & 127)]; | |
| refmvs_block s_src; | |
| const int t_swap = !!(f->rf.ref_flip & (1ULL << (b->ref.ref[0] * 8 + b->ref.ref[1]))); | |
| const ptrdiff_t t_stride = f->rf.rp_stride; | |
| const int opfl = b->inter_mode >= OPFL_NEARMV_NEARMV; | |
| const int refinemv = b->refine_mv && b->comp_type == COMP_INTER_AVG; | |
| refmvs_temporal_block *t_dst = | |
| f->seq_hdr->ref_frame_mvs && (!opfl || !refinemv) ? | |
| &f->rf.rp[(t->by >> 1) * (t_stride >> 5) + (t->bx >> 6)] : NULL; | |
| union refpair t_ref; | |
| s_src.ref.ref[0] = t_ref.ref[t_swap] = b->ref.ref[0]; | |
| s_src.ref.ref[1] = t_ref.ref[!t_swap] = b->ref.ref[1]; | |
| s_src.bs = bs; | |
| s_src.by4 = t->by; | |
| s_src.bx4 = t->bx; | |
| s_src.mf = b->cwp_idx * 4; | |
| const uint8_t *const mask = b->comp_type == COMP_INTER_WEDGE ? | |
| WEDGE_TMVP(bs, bw4, bh4, b->wedge_idx) : NULL; | |
| if (b->motion_mode > MM_INTERINTRA || | |
| (!f->frame_hdr->force_integer_mv && | |
| b->inter_mode == GLOBALMV_GLOBALMV && imin(bw4, bh4) > 1 && | |
| (f->frame_hdr->gmv.m[b->ref.ref[0]].type > DAV2D_WM_TYPE_TRANSLATION || | |
| f->frame_hdr->gmv.m[b->ref.ref[1]].type > DAV2D_WM_TYPE_TRANSLATION))) | |
| { | |
| assert(bw4 > 1 && bh4 > 1); | |
| const int32_t *mat1, *mat2; | |
| if (b->motion_mode > MM_INTERINTRA) { | |
| COPY2MV(s_src.lmv, b->mv); | |
| s_src.mf |= 2; | |
| mat1 = t->warpmv[0].matrix; | |
| mat2 = t->warpmv[1].matrix; | |
| memcpy(s_src.m, mat1, sizeof(int32_t) * 6); | |
| } else { | |
| COPY2MV(s_src.mv, b->mv); | |
| s_src.mf |= 1; | |
| mat1 = f->frame_hdr->gmv.m[b->ref.ref[0]].matrix; | |
| mat2 = f->frame_hdr->gmv.m[b->ref.ref[1]].matrix; | |
| } | |
| f->c->refmvs_dsp.splat_comp_warpmv(s_dst, &s_src, t_dst, bx4 >> 1, | |
| t_stride, bw4, bh4, t_ref, | |
| mat1, mat2, t_swap, mask, | |
| mask ? b->wedge_sign ^ t_swap : 0); | |
| } else { | |
| COPY2MV(s_src.mv, b->mv); | |
| s_src.mf |= b->inter_mode == GLOBALMV_GLOBALMV && imin(bw4, bh4) > 1; | |
| union qmvpair t_mv; | |
| t_mv.mv[0] = quantize_mv(b->mv[t_swap]); | |
| t_mv.mv[1] = quantize_mv(b->mv[!t_swap]); | |
| if (!mask) { | |
| if (t_mv.mv[0].n == INVALID_TRAJ) { | |
| if (t_mv.mv[1].n == INVALID_TRAJ) { | |
| t_ref.pair = -1; | |
| } else { | |
| t_mv.mv[0] = t_mv.mv[1]; | |
| t_ref.ref[0] = t_ref.ref[1]; | |
| } | |
| } else if (t_mv.mv[1].n == INVALID_TRAJ) { | |
| t_mv.mv[1] = t_mv.mv[0]; | |
| t_ref.ref[1] = t_ref.ref[0]; | |
| } | |
| f->c->refmvs_dsp.splat_mv(s_dst, &s_src, t_dst, bx4 >> 1, t_stride, | |
| bw4, bh4, t_mv, t_ref); | |
| } else { | |
| f->c->refmvs_dsp.splat_comp_wedgemv(s_dst, &s_src, t_dst, bx4 >> 1, | |
| t_stride, bw4, bh4, t_mv, t_ref, | |
| mask, | |
| mask ? b->wedge_sign ^ t_swap : 0); | |
| } | |
| } | |
| } | |
| static inline void splat_intraref(const Dav2dContext *const c, | |
| const Dav2dFrameContext *const f, | |
| Dav2dTaskContext *const t, | |
| const enum BlockSize bs, | |
| const int by4, const int bx4, | |
| const int bw4, const int bh4) | |
| { | |
| refmvs_block *const s_dst = &t->rt.r[by4 * 128 + (t->bx & 127)]; | |
| refmvs_block s_src = (refmvs_block) { | |
| .ref.pair = -1, | |
| .mv = { { .n = INVALID_MV }, { .n = INVALID_MV } }, | |
| .bs = bs, | |
| .by4 = t->by, | |
| .bx4 = t->bx, | |
| .mf = 0, | |
| }; | |
| const ptrdiff_t t_stride = f->rf.rp_stride; | |
| refmvs_temporal_block *const t_dst = f->seq_hdr->ref_frame_mvs ? | |
| &f->rf.rp[(t->by >> 1) * (t_stride >> 5) + (t->bx >> 6)] : NULL; | |
| c->refmvs_dsp.splat_mv(s_dst, &s_src, t_dst, bx4 >> 1, t_stride, bw4, bh4, | |
| (union qmvpair) { .n = INVALID_TRAJ * 0x10001U }, | |
| (union refpair) { .pair = -1 }); | |
| if (t->f->seq_hdr->refmv_bank) | |
| dav2d_refmvs_bank_update(&t->rt, bs, t->by, t->bx); | |
| } | |
| static void mc_lowest_px(int *const dst, const int by4, const int bh4, | |
| const int mvy, const int ss_ver, | |
| const struct ScalableMotionParams *const smp) | |
| { | |
| const int v_mul = 4 >> ss_ver; | |
| if (!smp->scale) { | |
| const int my = mvy >> (3 + ss_ver), dy = mvy & (15 >> !ss_ver); | |
| *dst = imax(*dst, (by4 + bh4) * v_mul + my + 4 * !!dy); | |
| } else { | |
| int y = (by4 * v_mul << 4) + mvy * (1 << !ss_ver); | |
| const int64_t tmp = (int64_t)(y) * smp->scale + (smp->scale - 0x4000) * 8; | |
| y = apply_sign64((llabs(tmp) + 128) >> 8, tmp) + 32; | |
| const int bottom = ((y + (bh4 * v_mul - 1) * smp->step) >> 10) + 1 + 4; | |
| *dst = imax(*dst, bottom); | |
| } | |
| } | |
| static ALWAYS_INLINE void affine_lowest_px(int *const dst, const uint8_t *const b_dim, | |
| const int by, const int bx, | |
| const Dav2dWarpedMotionParams *const wmp, | |
| const int ss_ver, const int ss_hor) | |
| { | |
| const int h_mul = 4 >> ss_hor, v_mul = 4 >> ss_ver; | |
| const int32_t *const mat = wmp->matrix; | |
| const int y = b_dim[1] * v_mul - 8; // lowest y | |
| const int src_y = by * 4 + ((y + 4) << ss_ver); | |
| const int64_t mat5_y = (int64_t) mat[5] * src_y + mat[1]; | |
| // check left- and right-most blocks | |
| for (int x = 0; x < b_dim[0] * h_mul; x += imax(8, b_dim[0] * h_mul - 8)) { | |
| // calculate transformation relative to center of 8x8 block in | |
| // luma pixel units | |
| const int src_x = bx * 4 + ((x + 4) << ss_hor); | |
| const int64_t mvy = ((int64_t) mat[4] * src_x + mat5_y) >> ss_ver; | |
| const int dy = (int) (mvy >> 16) - 4; | |
| *dst = imax(*dst, dy + 4 + 8); | |
| } | |
| } | |
| static inline void affine_lowest_px_luma(Dav2dTaskContext *const t, int *const dst, | |
| const uint8_t *const b_dim, | |
| const Dav2dWarpedMotionParams *const wmp) | |
| { | |
| affine_lowest_px(dst, b_dim, t->by, t->bx, wmp, 0, 0); | |
| } | |
| static inline void affine_lowest_px_chroma(Dav2dTaskContext *const t, int *const dst, | |
| const uint8_t *const b_dim, | |
| const Dav2dWarpedMotionParams *const wmp) | |
| { | |
| const Dav2dFrameContext *const f = t->f; | |
| affine_lowest_px(dst, b_dim, t->cby, t->cbx, wmp, f->ss_ver, f->ss_hor); | |
| } | |
| static void derive_lowest_px(Dav2dTaskContext *const t, | |
| const enum BlockSize lbs, const enum BlockSize cbs, | |
| Av2Block *const b) | |
| { | |
| const Dav2dFrameContext *const f = t->f; | |
| Dav2dTileState *const ts = t->ts; | |
| const int has_chroma = cbs != BS_INVALID; | |
| // keep track of motion vectors for each reference | |
| const int sby = (t->by - ts->tiling.row_start) >> f->sb_shift; | |
| int (*const lowest_px)[2] = ts->lowest_pixel[sby]; | |
| const uint8_t *const b_dim = dav2d_block_dimensions[lbs]; | |
| const int bw4 = b_dim[0], bh4 = b_dim[1]; | |
| const int has_sub8x8_chroma = has_chroma && cbs != lbs && imin(bw4, bh4) < 16; | |
| const int has_regular_chroma = has_chroma && !has_sub8x8_chroma; | |
| const int ss_ver = f->ss_ver; | |
| int cbw4, cbh4; | |
| const uint8_t *c_b_dim; | |
| if (has_chroma) { | |
| c_b_dim = dav2d_block_dimensions[cbs]; | |
| cbw4 = c_b_dim[0]; | |
| cbh4 = c_b_dim[1]; | |
| } | |
| // y | |
| assert(lbs != BS_INVALID); | |
| if (b->ref.ref[1] == -1 && b->ref.ref[0] != TIP_FRAME) { | |
| if (!f->frame_hdr->force_integer_mv && | |
| ((b->inter_mode == GLOBALMV && imin(bw4, bh4) > 1 && | |
| f->gmv_warp_allowed[b->ref.ref[0]]) || | |
| (b->motion_mode >= MM_WARP_CAUSAL && | |
| t->warpmv[0].type > DAV2D_WM_TYPE_INVALID))) | |
| { | |
| affine_lowest_px_luma(t, &lowest_px[b->ref.ref[0]][0], b_dim, | |
| b->motion_mode >= MM_WARP_CAUSAL ? &t->warpmv[0] : | |
| &f->frame_hdr->gmv.m[b->ref.ref[0]]); | |
| if (has_regular_chroma) | |
| affine_lowest_px_chroma(t, &lowest_px[b->ref.ref[0]][1], c_b_dim, | |
| b->motion_mode >= MM_WARP_CAUSAL ? &t->warpmv[0] : | |
| &f->frame_hdr->gmv.m[b->ref.ref[0]]); | |
| } else { | |
| mc_lowest_px(&lowest_px[b->ref.ref[0]][0], t->by, bh4, b->mv[0].y, | |
| 0, &f->svc[b->ref.ref[0]][1]); | |
| if (has_regular_chroma) | |
| mc_lowest_px(&lowest_px[b->ref.ref[0]][1], t->cby, cbh4, | |
| b->mv[0].y, ss_ver, &f->svc[b->ref.ref[0]][1]); | |
| } | |
| if (b->motion_mode > MM_INTERINTRA) { | |
| // backup warpmatrix | |
| memcpy(b->mtxbak, t->warpmv[0].matrix, sizeof(int32_t) * 6); | |
| } | |
| } else if (b->ref.ref[0] == TIP_FRAME) { | |
| const int h4 = imin(bh4, f->bh - t->cby); | |
| const int w4 = imin(bw4, f->bw - t->cbx); | |
| int opfl = f->seq_hdr->tip_refine_mv && | |
| (f->frame_hdr->tip.frame_mode == 1 || | |
| f->frame_hdr->tip.subpel_filter == DAV2D_FILTER_8TAP_SHARP); | |
| const union refpair ref = f->rf.tip.ref; | |
| const int refine = opfl && f->frame_hdr->tip.frame_mode == 1 && | |
| f->refdist[ref.ref[0]] == -f->refdist[ref.ref[1]]; | |
| const int step = 2 << (f->frame_hdr->tip.frame_mode == 2 /* frame */ ? !opfl : | |
| ((!opfl && imin(bw4, bh4) >= 4) || b->bs == BS_256x256)); | |
| opfl &= !!f->seq_hdr->opfl_refine && f->frame_hdr->has_bothside_refs; | |
| const ptrdiff_t t_stride = f->rf.rp_stride; | |
| const int x4e = t->bx >> 6, bx4 = t->bx & 63; | |
| for (int y = 0; y < h4; y += step) { | |
| const ptrdiff_t off_y8 = | |
| (((t->by + y) & (f->sb_step - 1)) >> 1) * (t_stride >> 5) + x4e; | |
| const union tmv *const mvr = t->rt.rp_proj[off_y8].mv; | |
| for (int x = 0; x < w4; x += step) { | |
| const int off_x8 = (bx4 + x) >> 1; | |
| union tmv tmv = mvr[off_x8]; | |
| if (tmv.n == INVALID_TMV) tmv.n = 0; | |
| for (int i = 0; i < 2; i++) { | |
| const int64_t tmpy = tmv.y * (int64_t) f->rf.tip.sf[i]; | |
| const int tipmvy = iclip((int)((tmpy + 0x2000 - (tmpy < 0)) >> 14), | |
| -0xffff, 0xffff); | |
| const int mvy = iclip(tipmvy + b->mv[0].y, -0xffff, 0xffff); | |
| mc_lowest_px(&lowest_px[ref.ref[i]][0], t->by + y, step, | |
| mvy | opfl | refine, 0, &f->svc[ref.ref[i]][1]); | |
| if (has_regular_chroma) | |
| mc_lowest_px(&lowest_px[ref.ref[i]][1], t->by + y, step, | |
| mvy | opfl | refine, ss_ver, &f->svc[ref.ref[i]][1]); | |
| } | |
| } | |
| } | |
| } else if (b->inter_mode >= OPFL_NEARMV_NEARMV || | |
| (b->refine_mv && b->comp_type == COMP_INTER_AVG)) | |
| { | |
| // the "| 1" here is to force the mv into a subpel-position and ensure | |
| // the 4px subpel filter overhang is applied in mc_lowest_px(). This | |
| // matches the 4px overhang that refinemv and/or opfl can generate, | |
| // regardless of the input MV. | |
| for (int i = 0; i < 2; i++) { | |
| mc_lowest_px(&lowest_px[b->ref.ref[i]][0], t->by, bh4, | |
| b->mv[i].y | 1, 0, &f->svc[b->ref.ref[i]][1]); | |
| if (has_regular_chroma) | |
| mc_lowest_px(&lowest_px[b->ref.ref[i]][1], t->cby, cbh4, | |
| b->mv[i].y | 1, ss_ver, &f->svc[b->ref.ref[i]][1]); | |
| } | |
| } else { | |
| for (int i = 0; i < 2; i++) { | |
| if ((b->inter_mode == GLOBALMV_GLOBALMV && imin(bw4, bh4) > 1 && | |
| f->gmv_warp_allowed[b->ref.ref[i]]) || | |
| (b->motion_mode == MM_WARP_CAUSAL && | |
| t->warpmv[i].type > DAV2D_WM_TYPE_INVALID)) | |
| { | |
| affine_lowest_px_luma(t, &lowest_px[b->ref.ref[i]][0], b_dim, | |
| b->motion_mode == MM_WARP_CAUSAL ? &t->warpmv[i] : | |
| &f->frame_hdr->gmv.m[b->ref.ref[i]]); | |
| if (has_regular_chroma) | |
| affine_lowest_px_chroma(t, &lowest_px[b->ref.ref[i]][1], c_b_dim, | |
| b->motion_mode == MM_WARP_CAUSAL ? | |
| &t->warpmv[i] : | |
| &f->frame_hdr->gmv.m[b->ref.ref[i]]); | |
| } else { | |
| mc_lowest_px(&lowest_px[b->ref.ref[i]][0], t->by, bh4, | |
| b->mv[i].y, 0, &f->svc[b->ref.ref[i]][1]); | |
| if (has_regular_chroma) | |
| mc_lowest_px(&lowest_px[b->ref.ref[i]][1], t->cby, cbh4, | |
| b->mv[i].y, ss_ver, &f->svc[b->ref.ref[i]][1]); | |
| } | |
| if (b->motion_mode > MM_INTERINTRA) { | |
| // backup warpmatrix. We write one of them into our neighbour | |
| // block on the right, which is OK since comp-warpmv blocks are | |
| // always at least 8px wide, so that entry is unused. | |
| assert(bw4 >= 2); | |
| memcpy(b[i].mtxbak, t->warpmv[i].matrix, sizeof(int32_t) * 6); | |
| } | |
| } | |
| } | |
| if (has_sub8x8_chroma) { | |
| const int ch4 = imin(cbh4, f->bh - t->cby); | |
| const int cw4 = imin(cbw4, f->bw - t->cbx); | |
| uint16_t mask[16] = { 0 }; | |
| for (int y = 0; y < ch4; y++) { | |
| for (int x = 0, m = 1; x < cw4; x++, m <<= 1) { | |
| if (mask[y] & m) continue; | |
| const Av2Block *const b2 = | |
| &f->frame_thread.b[(t->cby + y) * f->b4_stride + t->cbx + x]; | |
| const union mv mv = b2->mv[0]; | |
| const uint8_t *const sdim = dav2d_block_dimensions[b2->bs]; | |
| mc_lowest_px(&lowest_px[b2->ref.ref[0]][1], t->cby + y, sdim[1], | |
| mv.y, ss_ver, &f->svc[b2->ref.ref[0]][1]); | |
| const unsigned m2 = ((1 << sdim[0]) - 1) << x; | |
| for (int yy = y; yy < y + sdim[1]; yy++) | |
| mask[yy] |= m2; | |
| } | |
| } | |
| } | |
| } | |
| static int recon_b(Dav2dTaskContext *const t, DB_ONLY(const int depth) | |
| const enum BlockSize lbs, const enum BlockSize cbs, | |
| Av2Block *const b) | |
| { | |
| const Dav2dFrameContext *const f = t->f; | |
| if (t->task_thread.pass == PASS_RECON) { | |
| if (lbs != BS_INVALID && !b->intra && b->motion_mode > MM_INTERINTRA) { | |
| // restore warp matrices | |
| const int comp = b->ref.ref[1] != -1; | |
| for (int n = 0; n <= comp; n++) { | |
| memcpy(t->warpmv[n].matrix, b[n].mtxbak, sizeof(int32_t) * 6); | |
| t->warpmv[n].type = dav2d_get_shear_params(&t->warpmv[n]) ? | |
| DAV2D_WM_TYPE_INVALID : warp_type(t->warpmv[n].matrix); | |
| } | |
| } | |
| return f->bd_fn.recon_b(t, DB_ONLY(depth) lbs, | |
| (const enum BlockSize[2]){ cbs, cbs }, b); | |
| } | |
| const enum BlockSize bs = lbs == BS_INVALID ? cbs : lbs; | |
| assert(bs != BS_INVALID); | |
| Dav2dTileState *const ts = t->ts; | |
| const int have_top = t->by > ts->tiling.row_start; | |
| const int have_left = t->bx > ts->tiling.col_start; | |
| const uint8_t *const b_dim = dav2d_block_dimensions[bs]; | |
| const int bw4 = b_dim[0], bh4 = b_dim[1]; | |
| const int w4 = imin(bw4, f->bw - t->bx), h4 = imin(bh4, f->bh - t->by); | |
| const int by4 = t->by & 63, bx4 = t->bx & 63; | |
| const int has_luma = lbs != BS_INVALID; | |
| // resolve motion vector and/or warp matrix | |
| if (has_luma && b->intrabc) { | |
| refmvs_candidate mvstack[6]; | |
| int n_mvs; | |
| dav2d_refmvs_find(&t->rt, mvstack, NULL, &n_mvs, | |
| (union refpair) { .pair = -1 }, | |
| bs, 0, t->by, t->bx); | |
| #if DEBUG_BLOCK_INFO | |
| if (BLOCK_TO_DEBUG) { | |
| printf("%*sfind_mv_refs(intra)\n", depth, ""); | |
| for (int n = 0; n < n_mvs; n++) | |
| printf("%*smv[%d/%d]: y=%d,x=%d,w=%d\n", | |
| depth + 1, "", n, n_mvs, mvstack[n].mv[0].y, | |
| mvstack[n].mv[0].x, mvstack[n].weight); | |
| } | |
| #endif | |
| const union mv diff = b->mv[0]; | |
| b->mv[0] = mvstack[b->drl_idx[0]].mv[0]; | |
| if (!b->mv[0].n) { | |
| // I don't know if this can actually happen, but AVM has code here | |
| // to force the refmv to a nonzero value | |
| const int sbsz = 64 << f->frame_hdr->sb128; | |
| if (t->by - f->sb_step < ts->tiling.row_start) { | |
| b->mv[0].x = -(8 * (sbsz + 256)); | |
| } else { | |
| b->mv[0].y = -(8 * sbsz); | |
| } | |
| } | |
| if (!b->is_refmv) { | |
| if (!b->is_qpel) fix_int_mv_precision(&b->mv[0]); | |
| b->mv[0].x += diff.x; | |
| b->mv[0].y += diff.y; | |
| } | |
| #if 0 | |
| // clip intrabc motion vector to decoded parts of current tile | |
| int border_left = ts->tiling.col_start * 4; | |
| int border_top = ts->tiling.row_start * 4; | |
| if (has_chroma) { | |
| if (bw4 < 2 && ss_hor) | |
| border_left += 4; | |
| if (bh4 < 2 && ss_ver) | |
| border_top += 4; | |
| } | |
| int src_left = t->bx * 4 + (b->mv[0].x >> 3); | |
| int src_top = t->by * 4 + (b->mv[0].y >> 3); | |
| int src_right = src_left + bw4 * 4; | |
| int src_bottom = src_top + bh4 * 4; | |
| const int border_right = ((ts->tiling.col_end + (bw4 - 1)) & ~(bw4 - 1)) * 4; | |
| // check against left or right tile boundary and adjust if necessary | |
| if (src_left < border_left) { | |
| src_right += border_left - src_left; | |
| src_left += border_left - src_left; | |
| } else if (src_right > border_right) { | |
| src_left -= src_right - border_right; | |
| src_right -= src_right - border_right; | |
| } | |
| // check against top tile boundary and adjust if necessary | |
| if (src_top < border_top) { | |
| src_bottom += border_top - src_top; | |
| src_top += border_top - src_top; | |
| } | |
| const int sbx = (t->bx >> (4 + f->frame_hdr->sb128)) << (6 + f->frame_hdr->sb128); | |
| const int sby = (t->by >> (4 + f->frame_hdr->sb128)) << (6 + f->frame_hdr->sb128); | |
| const int sb_size = 1 << (6 + f->frame_hdr->sb128); | |
| // check for overlap with current superblock | |
| if (src_bottom > sby && src_right > sbx) { | |
| if (src_top - border_top >= src_bottom - sby) { | |
| // if possible move src up into the previous suberblock row | |
| src_top -= src_bottom - sby; | |
| src_bottom -= src_bottom - sby; | |
| } else if (src_left - border_left >= src_right - sbx) { | |
| // if possible move src left into the previous suberblock | |
| src_left -= src_right - sbx; | |
| src_right -= src_right - sbx; | |
| } | |
| } | |
| // move src up if it is below current superblock row | |
| if (src_bottom > sby + sb_size) { | |
| src_top -= src_bottom - (sby + sb_size); | |
| src_bottom -= src_bottom - (sby + sb_size); | |
| } | |
| // error out if mv still overlaps with the current superblock | |
| if (src_bottom > sby && src_right > sbx) | |
| return -1; | |
| b->mv[0].x = (src_left - t->bx * 4) * 8; | |
| b->mv[0].y = (src_top - t->by * 4) * 8; | |
| #endif | |
| DEBUG_BLOCK_printf("%*sfinal 2dmv: y=%d,x=%d\n", | |
| depth, "", b->mv[0].y, b->mv[0].x); | |
| splat_intrabc_mv(DB_ONLY(depth) f, t, bs, b, by4, bx4, bw4, bh4); | |
| } else if (!b->intra) { | |
| assert(has_luma); | |
| if (b->ref.ref[1] == -1) { | |
| if (b->inter_mode == GLOBALMV) { | |
| b->mv[0] = get_gmv_2d(&f->frame_hdr->gmv.m[b->ref.ref[0]], t->bx, t->by, | |
| bw4, bh4, f->bw, f->bh, f->frame_hdr); | |
| } else { | |
| refmvs_candidate mvstack[6]; | |
| int32_t warp[4][6]; | |
| int n_mvs[2]; | |
| dav2d_refmvs_find(&t->rt, mvstack, | |
| b->ref.ref[0] != TIP_FRAME && b->inter_mode > NEWMV ? | |
| warp : NULL, n_mvs, | |
| (union refpair) { .ref = { b->ref.ref[0], -1 }}, | |
| bs, 0, t->by, t->bx); | |
| #if DEBUG_BLOCK_INFO | |
| if (BLOCK_TO_DEBUG) { | |
| printf("%*sfind_mv_refs(%d,-1)\n", depth, "", b->ref.ref[0]); | |
| for (int n = 0; n < n_mvs[0]; n++) | |
| printf("%*smv[%d/%d]: y=%d,x=%d,w=%d,y_off=%d,x_off=%d\n", | |
| depth + 1, "", n, n_mvs[0], mvstack[n].mv[0].y, | |
| mvstack[n].mv[0].x, mvstack[n].weight, | |
| mvstack[n].y_off, mvstack[n].x_off); | |
| if (b->ref.ref[0] != TIP_FRAME && b->inter_mode > NEWMV) | |
| for (int n = 0; n < n_mvs[1]; n++) | |
| printf("%*swarp[%d/%d]: %d, %d, %d, %d, %d, %d\n", | |
| depth + 1, "", n, n_mvs[1], | |
| warp[n][0], warp[n][1], warp[n][2], | |
| warp[n][3], warp[n][4], warp[n][5]); | |
| } | |
| #endif | |
| if (b->motion_mode > MM_INTERINTRA && b->warp_ref_idx >= n_mvs[1]) | |
| return -1; | |
| const union mv diff = b->mv[0]; | |
| b->mv[0] = b->inter_mode == WARPMV ? | |
| get_warpmv_2d(warp[b->warp_ref_idx], t->bx, t->by, | |
| bw4, bh4, f->bw, f->bh, | |
| b->warpmv_with_mvd ? b->mv_prec : 6) : | |
| mvstack[b->drl_idx[0]].mv[0]; | |
| if (b->inter_mode == NEWMV || b->inter_mode == WARPNEWMV || | |
| (b->inter_mode == WARPMV && b->warpmv_with_mvd)) | |
| { | |
| if (!b->amvd && b->mv_prec <= 3) | |
| mv_reduce_prec(&b->mv[0], b->mv_prec); | |
| b->mv[0].x += diff.x; | |
| b->mv[0].y += diff.y; | |
| } | |
| if (b->motion_mode == MM_WARP_DELTA) { | |
| int32_t *const m = t->warpmv[0].matrix; | |
| for (int n = 0; n < 4 && b->matrix[n] != -0x80; n++) { | |
| if (b->matrix[n]) { | |
| const int base = ((n - 1U) >= 2U) * 0x10000; | |
| m[2 + n] = iclip(warp[b->warp_ref_idx][n + 2] + | |
| b->matrix[n] * (1 << 10), | |
| base - 0x7fc0, base + 0x7fc0); | |
| } else { | |
| m[2 + n] = warp[b->warp_ref_idx][n + 2]; | |
| } | |
| } | |
| if (b->matrix[2] == -0x80) { | |
| m[5] = m[2]; | |
| m[4] = -m[3]; | |
| } | |
| dav2d_set_affine_mv2d(bw4, bh4, b->mv[0], &t->warpmv[0], t->bx, t->by); | |
| t->warpmv[0].type = dav2d_get_shear_params(&t->warpmv[0]) ? | |
| DAV2D_WM_TYPE_INVALID : warp_type(t->warpmv[0].matrix); | |
| } else if (b->motion_mode == MM_WARP_CAUSAL) { | |
| derive_warpmv(t, have_top, have_left, bw4, bh4, w4, h4, | |
| b->ref.ref[0], b->mv[0], &t->warpmv[0]); | |
| } else if (b->motion_mode == MM_WARP_EXTEND) { | |
| const int is_sb_boundary = !(t->by & (f->sb_step - 1)); | |
| int y_off = 0, x_off = 0; // invalid | |
| if (mvstack[b->drl_idx[0]].x_off == -1 || | |
| mvstack[b->drl_idx[0]].y_off == -1) | |
| { | |
| y_off = mvstack[b->drl_idx[0]].y_off; | |
| x_off = mvstack[b->drl_idx[0]].x_off; | |
| const refmvs_block *r; | |
| const int sb_mask = f->sb_step - 1; | |
| if (is_sb_boundary && y_off == -1) { | |
| r = t->bx & sb_mask || x_off >= 0 ? | |
| &t->rt.ra[(t->bx + x_off) >> 1] : &t->rt.ra_tl; | |
| } else { | |
| r = &t->rt.r[((t->by + y_off) & 63) * 128 + | |
| ((t->bx + x_off) & 127)]; | |
| } | |
| if (r->ref.ref[0] == TIP_FRAME) | |
| x_off = y_off = 0; | |
| } | |
| const refmvs_block *const tml = !have_left ? NULL : | |
| &t->rt.r[(t->by & 63) * 128 + ((t->bx - 1) & 127)]; | |
| const refmvs_block *const bml = | |
| (!have_left || t->by + bh4 > ts->tiling.row_end) ? NULL : | |
| &t->rt.r[((t->by + bh4 - 1) & 63) * 128 + | |
| ((t->bx - 1) & 127)]; | |
| const refmvs_block *const lmt = !have_top ? NULL : | |
| is_sb_boundary ? &t->rt.ra[(t->bx & ~1) >> 1] : | |
| &t->rt.r[((t->by - 1) & 63) * 128 + ((t->bx) & 127)]; | |
| const refmvs_block *const rmt = | |
| (!have_top || t->bx + bw4 > ts->tiling.col_end) ? NULL : | |
| is_sb_boundary ? &t->rt.ra[((t->bx & ~1) + bw4 - 2) >> 1] : | |
| &t->rt.r[((t->by - 1) & 63) * 128 + | |
| ((t->bx + bw4 - 1) & 127)]; | |
| if (x_off || y_off) { | |
| /* do nothing */ | |
| } else if (bml && (bml->ref.ref[0] == b->ref.ref[0] || | |
| bml->ref.ref[1] == b->ref.ref[0])) | |
| { | |
| y_off = bh4 - 1; | |
| x_off = -1; | |
| } else if (rmt && (rmt->ref.ref[0] == b->ref.ref[0] || | |
| rmt->ref.ref[1] == b->ref.ref[0])) | |
| { | |
| y_off = -1; | |
| x_off = -(t->bx & is_sb_boundary) + bw4 - (1 + is_sb_boundary); | |
| } else if (tml && (tml->ref.ref[0] == b->ref.ref[0] || | |
| tml->ref.ref[1] == b->ref.ref[0])) | |
| { | |
| y_off = 0; | |
| x_off = -1; | |
| } else if (lmt && (lmt->ref.ref[0] == b->ref.ref[0] || | |
| lmt->ref.ref[1] == b->ref.ref[0])) | |
| { | |
| y_off = -1; | |
| x_off = -(t->bx & is_sb_boundary); | |
| } | |
| if (y_off || x_off) | |
| extend_warpmv(t, x_off, y_off, b_dim, b, &t->warpmv[0]); | |
| else | |
| t->warpmv[0].type = DAV2D_WM_TYPE_INVALID; | |
| } | |
| } | |
| } else if (b->skip_mode) { | |
| refmvs_candidate mvstack[6]; | |
| int n_mvs; | |
| dav2d_refmvs_find(&t->rt, mvstack, NULL, &n_mvs, b->ref, | |
| bs, 1, t->by, t->bx); | |
| #if DEBUG_BLOCK_INFO | |
| if (BLOCK_TO_DEBUG) { | |
| printf("%*sfind_mv_refs(%d,%d)\n", depth, "", | |
| b->ref.ref[0], b->ref.ref[1]); | |
| for (int n = 0; n < n_mvs; n++) | |
| printf("%*smv[%d/%d]: y=%d,x=%d,y2=%d,x2=%d,w=%d,cwp=%d\n", | |
| depth + 1, "", n, n_mvs, mvstack[n].mv[0].y, | |
| mvstack[n].mv[0].x, mvstack[n].mv[1].y, | |
| mvstack[n].mv[1].x, mvstack[n].weight, | |
| mvstack[n].cwp_idx); | |
| } | |
| #endif | |
| COPY2MV(b->mv, mvstack[b->drl_idx[0]].mv); | |
| b->cwp_idx = mvstack[b->drl_idx[0]].cwp_idx; | |
| } else { | |
| if (b->inter_mode != GLOBALMV_GLOBALMV) { | |
| refmvs_candidate mvstack[6]; | |
| int n_mvs[2]; | |
| const int shift = 6 * (f->frame_hdr->opfl_refine_type == 2 && | |
| b->inter_mode >= OPFL_NEARMV_NEARMV); | |
| if (b->inter_mode - shift > NEARMV_NEWMV) { | |
| dav2d_refmvs_find(&t->rt, mvstack, NULL, &n_mvs[0], | |
| b->ref, bs, 0, t->by, t->bx); | |
| } else if (b->ref.ref[0] == b->ref.ref[1]) { | |
| dav2d_refmvs_find(&t->rt, mvstack, NULL, &n_mvs[0], | |
| (union refpair) { .ref = { | |
| b->ref.ref[0], -1 } }, bs, 0, t->by, t->bx); | |
| for (int n = 0; n < 6; n++) { | |
| mvstack[n].mv[1] = mvstack[n].mv[0]; | |
| mvstack[n].weight *= 0x101; | |
| } | |
| n_mvs[1] = n_mvs[0]; | |
| } else { | |
| dav2d_refmvs_find(&t->rt, mvstack, NULL, &n_mvs[0], | |
| (union refpair) { .ref = { | |
| b->ref.ref[0], -1 } }, bs, 0, t->by, t->bx); | |
| refmvs_candidate mvstack2[6]; | |
| dav2d_refmvs_find(&t->rt, mvstack2, NULL, &n_mvs[1], | |
| (union refpair) { .ref = { | |
| b->ref.ref[1], -1 } }, bs, 0, t->by, t->bx); | |
| for (int n = 0; n < 6; n++) { | |
| mvstack[n].mv[1] = mvstack2[n].mv[0]; | |
| mvstack[n].weight = (mvstack[n].weight & 0xff) | | |
| mvstack2[n].weight << 8; | |
| } | |
| } | |
| #if DEBUG_BLOCK_INFO | |
| if (BLOCK_TO_DEBUG) { | |
| printf("%*sfind_mv_refs(%d,%d)\n", depth, "", | |
| b->ref.ref[0], b->ref.ref[1]); | |
| if (b->inter_mode - shift <= NEARMV_NEWMV) { | |
| for (int drl = 0; drl < 2; drl++) | |
| for (int n = 0; n < n_mvs[drl]; n++) | |
| printf("%*smv[%d:%d/%d]: y=%d,x=%d,w=%d\n", | |
| depth + 1, "", drl, n, n_mvs[drl], | |
| mvstack[n].mv[drl].y, | |
| mvstack[n].mv[drl].x, | |
| (mvstack[n].weight >> (8 * drl)) & 0xff); | |
| } else { | |
| for (int n = 0; n < n_mvs[0]; n++) | |
| printf("%*smv[%d/%d]: y=%d,x=%d,y2=%d,x2=%d,w=%d\n", | |
| depth + 1, "", n, n_mvs[0], mvstack[n].mv[0].y, | |
| mvstack[n].mv[0].x, mvstack[n].mv[1].y, | |
| mvstack[n].mv[1].x, mvstack[n].weight); | |
| } | |
| } | |
| #endif | |
| for (int n = 0; n < 2; n++) { | |
| const union mv diff = b->mv[n]; | |
| b->mv[n] = mvstack[b->drl_idx[n]].mv[n]; | |
| const enum InterPredMode m = | |
| dav2d_comp_inter_pred_modes[b->inter_mode - NEARMV_NEARMV][n]; | |
| if (m != NEWMV) continue; | |
| const int mv_prec = (b->mv_prec >> (n * 4)) & 0xf; | |
| if (!b->amvd && mv_prec <= 3) | |
| mv_reduce_prec(&b->mv[n], mv_prec); | |
| b->mv[n].x += diff.x; | |
| b->mv[n].y += diff.y; | |
| } | |
| if (b->motion_mode == MM_WARP_CAUSAL) { | |
| for (int i = 0; i < 2; i++) | |
| derive_warpmv(t, have_top, have_left, bw4, bh4, w4, h4, | |
| b->ref.ref[i], b->mv[i], &t->warpmv[i]); | |
| } | |
| } else { | |
| for (int n = 0; n < 2; n++) | |
| b->mv[n] = get_gmv_2d(&f->frame_hdr->gmv.m[b->ref.ref[n]], | |
| t->bx, t->by, bw4, bh4, f->bw, | |
| f->bh, f->frame_hdr); | |
| } | |
| } | |
| const int is_comp = b->ref.ref[1] != -1; | |
| if (b->motion_mode > MM_INTERINTRA) { | |
| for (int i = 0; i <= is_comp; i++) | |
| debug_warp_matrix(depth, f, t, b, i); | |
| } else if (is_comp) { | |
| DEBUG_BLOCK_printf("%*sfinal 2dmv: y=%d,x=%d | y=%d,x=%d\n", | |
| depth, "", b->mv[0].y, b->mv[0].x, | |
| b->mv[1].y, b->mv[1].x); | |
| } else { | |
| DEBUG_BLOCK_printf("%*sfinal 2dmv: y=%d,x=%d\n", | |
| depth, "", b->mv[0].y, b->mv[0].x); | |
| } | |
| if (t->f->seq_hdr->refmv_bank) | |
| dav2d_refmvs_bank_add(&t->rt, bs, t->by, t->bx, b); | |
| if (b->motion_mode > MM_INTERINTRA) { | |
| int res = 0; | |
| if (t->warpmv[0].type != DAV2D_WM_TYPE_INVALID) | |
| res = dav2d_refmvs_warp_add(&t->rt, t->warpmv[0].matrix, | |
| DB_ONLY(t->by, t->bx) b->ref.ref[0]); | |
| if (!res && is_comp && t->warpmv[1].type != DAV2D_WM_TYPE_INVALID) | |
| dav2d_refmvs_warp_add(&t->rt, t->warpmv[1].matrix, | |
| DB_ONLY(t->by, t->bx) b->ref.ref[1]); | |
| } | |
| if (b->ref.ref[1] != -1) { | |
| splat_tworef_mv(DB_ONLY(depth) f, t, bs, b, by4, bx4, bw4, bh4); | |
| } else { | |
| splat_oneref_mv(DB_ONLY(depth) f, t, bs, b, by4, bx4, bw4, bh4); | |
| } | |
| } else if (has_luma && | |
| (IS_INTER_OR_SWITCH(f->frame_hdr) || f->frame_hdr->allow_intrabc)) | |
| { | |
| splat_intraref(f->c, f, t, bs, by4, bx4, bw4, bh4); | |
| } | |
| if (t->task_thread.pass == PASS_MVRES) { | |
| if (!b->intra) derive_lowest_px(t, lbs, cbs, b); | |
| return 0; | |
| } | |
| assert(t->task_thread.pass & PASS_RECON); | |
| return f->bd_fn.recon_b(t, DB_ONLY(depth) lbs, | |
| (const enum BlockSize[2]){ cbs, cbs }, b); | |
| } | |
| static const uint8_t size_group_lookup[] = { | |
| [BS_4x4] = 0, | |
| [BS_4x8] = 0, | |
| [BS_8x4] = 0, | |
| [BS_8x8] = 1, | |
| [BS_8x16] = 1, | |
| [BS_16x8] = 1, | |
| [BS_16x16] = 2, | |
| [BS_16x32] = 2, | |
| [BS_32x16] = 2, | |
| [BS_32x32] = 3, | |
| [BS_32x64] = 3, | |
| [BS_64x32] = 3, | |
| [BS_64x64] = 3, | |
| [BS_64x128] = 3, | |
| [BS_128x64] = 3, | |
| [BS_128x128] = 3, | |
| [BS_128x256] = 3, | |
| [BS_256x128] = 3, | |
| [BS_256x256] = 3, | |
| [BS_4x16] = 0, | |
| [BS_16x4] = 0, | |
| [BS_8x32] = 1, | |
| [BS_32x8] = 1, | |
| [BS_16x64] = 2, | |
| [BS_64x16] = 2, | |
| [BS_4x32] = 1, | |
| [BS_32x4]= 1, | |
| [BS_8x64] = 2, | |
| [BS_64x8] = 2, | |
| [BS_4x64] = 2, | |
| [BS_64x4] = 2, | |
| }; | |
| static int read_tx_part(Dav2dTaskContext *const t, | |
| DB_ONLY(const int depth) Av2Block *const b, | |
| const enum BlockSize bs) | |
| { | |
| Dav2dTileState *const ts = t->ts; | |
| const Dav2dFrameContext *const f = t->f; | |
| const uint8_t *const b_dim = dav2d_block_dimensions[bs]; | |
| const int bw4 = b_dim[0], bh4 = b_dim[1]; | |
| b->tx_part = TX_PARTITION_NONE; | |
| if (f->frame_hdr->segmentation.lossless[b->seg_id]) { | |
| b->tx_size_ll = 0; | |
| if (bs != BS_4x4 && ((b->intra && !b->intrabc) ? b->fsc : !b->skip_txfm)) { | |
| const int szctx = size_group_lookup[bs]; | |
| const int inter = !b->intra || b->intrabc; | |
| b->tx_size_ll = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.txsz_lossless[szctx][inter]); | |
| DEBUG_BLOCK_printf("%*sPost-lltxsz[ctx=%d|%d,%d]: r=%d\n", | |
| depth, "", szctx, inter, | |
| b->tx_size_ll, ts->msac.rng); | |
| } | |
| } else if (!b->skip_txfm) { | |
| if (f->frame_hdr->txfm_mode == DAV2D_TX_SWITCHABLE && | |
| bs != BS_4x4 && imax(bw4, bh4) <= 16) | |
| { | |
| const int inter = !b->intra || b->intrabc; | |
| static const uint8_t size_to_tx_part_group_lookup[] = { | |
| [BS_64x64] = 7, | |
| [BS_64x32] = 6, | |
| [BS_64x16] = 8, | |
| [BS_64x8] = 8, | |
| [BS_64x4] = 8, | |
| [BS_32x64] = 6, | |
| [BS_32x32] = 5, | |
| [BS_32x16] = 4, | |
| [BS_32x8] = 8, | |
| [BS_32x4] = 8, | |
| [BS_16x64] = 8, | |
| [BS_16x32] = 4, | |
| [BS_16x16] = 3, | |
| [BS_16x8] = 2, | |
| [BS_16x4] = 8, | |
| [BS_8x64] = 8, | |
| [BS_8x32] = 8, | |
| [BS_8x16] = 2, | |
| [BS_8x8] = 1, | |
| [BS_8x4] = 0, | |
| [BS_4x64] = 8, | |
| [BS_4x32] = 8, | |
| [BS_4x16] = 8, | |
| [BS_4x8] = 0, | |
| [BS_4x4] = 0, | |
| }; | |
| const int szctx = size_to_tx_part_group_lookup[bs]; | |
| int is_split = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.tx_split[b->fsc][inter][szctx]); | |
| if (is_split) { | |
| if (imin(bw4, bh4) >= 2) { | |
| static const uint8_t size_to_tx_type_group_vh_lookup[] = { | |
| [BS_64x64] = 9, | |
| [BS_64x32] = 8, | |
| [BS_64x16] = 13, | |
| [BS_64x8] = 11, | |
| [BS_32x64] = 7, | |
| [BS_32x32] = 6, | |
| [BS_32x16] = 5, | |
| [BS_32x8] = 11, | |
| [BS_16x64] = 12, | |
| [BS_16x32] = 4, | |
| [BS_16x16] = 3, | |
| [BS_16x8] = 2, | |
| [BS_8x64] = 10, | |
| [BS_8x32] = 10, | |
| [BS_8x16] = 1, | |
| [BS_8x8] = 0, | |
| }; | |
| const int ctx = size_to_tx_type_group_vh_lookup[bs]; | |
| b->tx_part = 1 + | |
| dav2d_msac_decode_symbol_adapt8(&ts->msac, | |
| ts->cdf.tx2d.tx_part_2d[b->fsc][inter][ctx], 6); | |
| } else if (imax(bw4, bh4) >= 4) { | |
| const int ctx = bw4 >= 4; | |
| const int tx_part_4way = f->seq_hdr->reduced_tx_part_set ? 0 : | |
| dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.tx_part_1d[b->fsc][inter][ctx]); | |
| b->tx_part = TX_PARTITION_H + ctx + tx_part_4way * 2; | |
| } else { | |
| assert(bs == BS_4x8 || bs == BS_8x4); | |
| b->tx_part = bs == BS_4x8 ? TX_PARTITION_H : | |
| TX_PARTITION_V; | |
| } | |
| } | |
| DEBUG_BLOCK_printf("%*sPost-txpart[ctx=%d|%d|%d,%d]: r=%d\n", | |
| depth, "", b->fsc, inter, szctx, | |
| b->tx_part, ts->msac.rng); | |
| } | |
| } | |
| // invalid partitions for this block size | |
| return dav2d_tx_part_tbl[bs][b->tx_part] == -1 ? -1 : 0; | |
| } | |
| static int read_wedge_idx(Dav2dTileState *const ts) { | |
| static const int8_t wedge_angle_dist2idx[20][4] = { | |
| { -1, 0, 1, 2 }, // WEDGE_0 | |
| { 3, 4, 5, 6 }, // WEDGE_14 | |
| { 7, 8, 9, 10 }, // WEDGE_27 | |
| { 11, 12, 13, 14 }, // WEDGE_45 | |
| { 15, 16, 17, 18 }, // WEDGE_63 | |
| { -1, 19, 20, 21 }, // WEDGE_90 | |
| { 22, 23, 24, 25 }, // WEDGE_117 | |
| { 26, 27, 28, 29 }, // WEDGE_135 | |
| { 30, 31, 32, 33 }, // WEDGE_153 | |
| { 34, 35, 36, 37 }, // WEDGE_166 | |
| { -1, 38, 39, 40 }, // WEDGE_180 | |
| { -1, 41, 42, 43 }, // WEDGE_194 | |
| { -1, 44, 45, 46 }, // WEDGE_207 | |
| { -1, 47, 48, 49 }, // WEDGE_225 | |
| { -1, 50, 51, 52 }, // WEDGE_243 | |
| { -1, 53, 54, 55 }, // WEDGE_270 | |
| { -1, 56, 57, 58 }, // WEDGE_297 | |
| { -1, 59, 60, 61 }, // WEDGE_315 | |
| { -1, 62, 63, 64 }, // WEDGE_333 | |
| { -1, 65, 66, 67 }, // WEDGE_346 | |
| }; | |
| const int quad = | |
| dav2d_msac_decode_symbol_adapt4(&ts->msac, ts->cdf.m.wedge_quad, 3); | |
| const int angle = 5 * quad + dav2d_msac_decode_symbol_adapt8(&ts->msac, | |
| ts->cdf.m.wedge_angle[quad], 4); | |
| const int dist = (angle - 1U >= 9U || angle == 5) ? | |
| 1 + dav2d_msac_decode_symbol_adapt4(&ts->msac, ts->cdf.m.wedge_dist2, 2) : | |
| dav2d_msac_decode_symbol_adapt4(&ts->msac, ts->cdf.m.wedge_dist, 3); | |
| return wedge_angle_dist2idx[angle][dist]; | |
| } | |
| static inline void jmvd_scale(union mv *const mv, const int amvd, | |
| const int jmvd_scale_mode) | |
| { | |
| if (amvd) { | |
| switch (jmvd_scale_mode) { | |
| default: dav2d_unreachable(); | |
| case 0: break; | |
| case 1: | |
| mv->y *= 2; | |
| mv->x *= 2; | |
| break; | |
| case 2: | |
| mv->y /= 2; | |
| mv->x /= 2; | |
| break; | |
| } | |
| } else { | |
| switch (jmvd_scale_mode) { | |
| default: dav2d_unreachable(); | |
| case 0: break; | |
| case 1: mv->y *= 2; break; | |
| case 2: mv->x *= 2; break; | |
| case 3: mv->y /= 2; break; | |
| case 4: mv->x /= 2; break; | |
| } | |
| } | |
| } | |
| static void splat2d(uint8_t *ptr, const ptrdiff_t stride, | |
| const int log2w, const int h, const int v) | |
| { | |
| #define set_ctx(rep_macro) \ | |
| for (int y = 0; y < h; y++) { \ | |
| rep_macro(ptr, 0, v); \ | |
| ptr += stride; \ | |
| } | |
| case_set(log2w); | |
| #undef set_ctx | |
| } | |
| static int decode_b(Dav2dTaskContext *const t, DB_ONLY(const int depth) | |
| const enum BlockSize lbs, const enum BlockSize cbs_tree) | |
| { | |
| const enum BlockSize bs = lbs == BS_INVALID ? cbs_tree : lbs; | |
| assert(bs != BS_INVALID); | |
| Dav2dTileState *const ts = t->ts; | |
| const Dav2dFrameContext *const f = t->f; | |
| const int is_mono = f->cur.p.p.layout == DAV2D_PIXEL_LAYOUT_I400; | |
| assert(!is_mono || lbs != BS_INVALID); | |
| const enum BlockSize cbs = is_mono ? BS_INVALID : cbs_tree; | |
| Av2Block *const b = f->c->task_thread.n_passes > 1 ? | |
| &f->frame_thread.b[t->by * f->b4_stride + t->bx] : | |
| &f->frame_thread.b[(t->by & 15) * 16 + (t->bx & 15)]; | |
| const uint8_t *const b_dim = dav2d_block_dimensions[bs]; | |
| const int bx4 = t->bx & 63, by4 = t->by & 63; | |
| const int bw4 = b_dim[0], bh4 = b_dim[1]; | |
| const int w4 = imin(bw4, f->bw - t->bx), h4 = imin(bh4, f->bh - t->by); | |
| const int have_left = t->bx > ts->tiling.col_start; | |
| const int have_top = t->by > ts->tiling.row_start; | |
| const int has_luma = lbs != BS_INVALID, has_chroma = cbs != BS_INVALID; | |
| int ss_hor, ss_ver, cbx4, cby4, cbw4, cbh4, cw4, ch4; | |
| if (has_chroma) { | |
| ss_ver = f->ss_ver; | |
| ss_hor = f->ss_hor; | |
| cbx4 = (t->cbx & 63) >> ss_hor; | |
| cby4 = (t->cby & 63) >> ss_ver; | |
| const uint8_t *const cb_dim = dav2d_block_dimensions[cbs]; | |
| cbw4 = cb_dim[0] >> ss_hor; | |
| cbh4 = cb_dim[1] >> ss_ver; | |
| assert(cbw4 >= 1 && cbh4 >= 1); | |
| cw4 = imin(cbw4, (f->bw - t->cbx) >> ss_hor); | |
| ch4 = imin(cbh4, (f->bh - t->cby) >> ss_ver); | |
| assert(cw4 >= 1 && ch4 >= 1); | |
| } | |
| DEBUG_BLOCK_printf("%*sdecode_b[y=%d,x=%d,bs=%dx%d,plane=%s]: r=%d\n", | |
| depth - 1, "", t->by, t->bx, bw4 * 4, bh4 * 4, | |
| !has_chroma ? "y" : !has_luma ? "uv" : "yuv", | |
| ts->msac.rng); | |
| if (!(t->task_thread.pass & PASS_ENTROPY)) { | |
| return recon_b(t, DB_ONLY(depth) lbs, cbs, b); | |
| } | |
| if (has_luma) b->bs = lbs; | |
| if (has_chroma) b->cbs = cbs; | |
| // segment_id (if seg_feature for skip/ref/gmv is enabled) | |
| int seg_pred = b->seg_id = 0, seg_postskip = 0; | |
| if (f->frame_hdr->segmentation.enabled) { | |
| if (!has_luma) { | |
| b->seg_id = f->cur_segmap[t->bx + t->by * f->b4_stride]; | |
| } else if (!f->frame_hdr->segmentation.update_map) { | |
| if (f->prev_segmap) { | |
| unsigned seg_id = get_prev_frame_segid(f, t->by, t->bx, w4, h4, | |
| f->prev_segmap, | |
| f->b4_stride); | |
| if (seg_id >= DAV2D_MAX_SEGMENTS) return -1; | |
| b->seg_id = seg_id; | |
| } | |
| } else if (f->frame_hdr->segmentation.preskip) { | |
| if (f->frame_hdr->segmentation.temporal && | |
| (seg_pred = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.seg_pred[t->a->seg_pred[bx4] + | |
| t->l.seg_pred[by4]]))) | |
| { | |
| // temporal predicted seg_id | |
| if (f->prev_segmap) { | |
| unsigned seg_id = get_prev_frame_segid(f, t->by, t->bx, | |
| w4, h4, | |
| f->prev_segmap, | |
| f->b4_stride); | |
| if (seg_id >= DAV2D_MAX_SEGMENTS) return -1; | |
| b->seg_id = seg_id; | |
| } | |
| } else { | |
| int seg_ctx; | |
| const unsigned pred_seg_id = | |
| get_cur_frame_segid(t->by, t->bx, have_top, have_left, | |
| &seg_ctx, f->cur_segmap, f->b4_stride); | |
| const unsigned ext_flag = f->seq_hdr->segmentation.ext ? | |
| dav2d_msac_decode_bool_adapt(&ts->msac, ts->cdf.m.seg_id_ext[seg_ctx]) : | |
| 0; | |
| const unsigned diff = | |
| dav2d_msac_decode_symbol_adapt8(&ts->msac, | |
| ts->cdf.m.seg_id[ext_flag][seg_ctx], 7) + | |
| (ext_flag << 3); | |
| const unsigned last_active_seg_id = | |
| f->frame_hdr->segmentation.last_active_segid; | |
| b->seg_id = neg_deinterleave(diff, pred_seg_id, | |
| last_active_seg_id + 1); | |
| if (b->seg_id > last_active_seg_id) return -1; | |
| if (b->seg_id >= DAV2D_MAX_SEGMENTS) return -1; | |
| } | |
| if (has_luma) | |
| DEBUG_BLOCK_printf("%*sPost-segid[%d]: r=%d\n", | |
| depth, "", b->seg_id, ts->msac.rng); | |
| } else | |
| seg_postskip = 1; | |
| } | |
| // cross-sb boundary neighbours | |
| const BlockContext *nx[2]; | |
| int xoff[2], idx = 0; | |
| if (have_left && t->by + bh4 <= ts->tiling.row_end) { | |
| nx[0] = &t->l; xoff[0] = by4 + bh4 - 1; idx++; | |
| } | |
| if (have_top && t->bx + bw4 <= ts->tiling.col_end) { | |
| nx[idx] = t->a; xoff[idx] = bx4 + bw4 - 1; idx++; | |
| } | |
| if (idx < 2 && have_left) { | |
| nx[idx] = &t->l; xoff[idx] = by4; idx++; | |
| } | |
| if (idx < 2) { | |
| nx[idx] = t->a; xoff[idx] = bx4; | |
| if (!idx) { | |
| nx[idx + 1] = t->a; xoff[idx + 1] = bx4; | |
| } | |
| idx += have_top; | |
| } | |
| // skip_mode | |
| if ((seg_postskip || | |
| !((f->frame_hdr->segmentation.d.globalmv_mask | | |
| f->frame_hdr->segmentation.d.skip_mask) & (1 << b->seg_id))) && | |
| f->frame_hdr->skip_mode_enabled && bw4 * bh4 > 2 && !t->intra_region) | |
| { | |
| const int ctx = (idx > 0 ? nx[0]->skip_mode[xoff[0]] : 0) + | |
| (idx > 1 ? nx[1]->skip_mode[xoff[1]] : 0); | |
| b->skip_mode = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.skip_mode[ctx]); | |
| DEBUG_BLOCK_printf("%*sPost-skip_mode[ctx=%d,%d]: r=%d\n", | |
| depth, "", ctx, b->skip_mode, ts->msac.rng); | |
| } else { | |
| b->skip_mode = 0; | |
| } | |
| if (b->skip_mode) { | |
| b->intra = 0; | |
| } else if (IS_INTER_OR_SWITCH(f->frame_hdr) && !t->intra_region) { | |
| if (lbs != cbs_tree) { | |
| // mixed-intra/inter regions in inter frames with chroma planes | |
| // of different sizes (in AVM language: with offsets) are inter | |
| b->intra = 0; | |
| } else { | |
| const int ictx = get_intra_ctx(nx, xoff, idx); | |
| b->intra = !dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.intra[ictx]); | |
| DEBUG_BLOCK_printf("%*sPost-is_inter[ctx=%d,%d]: r=%d\n", | |
| depth, "", ictx, !b->intra, ts->msac.rng); | |
| } | |
| } else { | |
| b->intra = 1; | |
| } | |
| const BlockContext *nb[2]; | |
| int boff[2]; | |
| if (has_luma) { | |
| b->intrabc = 0; | |
| // get "spatial neighbours", depending on edge availability; | |
| // do not cross SB boundaries vertically | |
| const int have_top_in_sb = !!(t->by & (f->sb_step - 1)); | |
| int idx = 0; | |
| if (have_left && bh4 == h4) { | |
| nb[0] = &t->l; | |
| boff[0] = by4 + bh4 - 1; | |
| idx++; | |
| } | |
| if (have_top_in_sb && bw4 == w4) { | |
| nb[idx] = t->a; | |
| boff[idx] = bx4 + bw4 - 1; | |
| idx++; | |
| } | |
| if (have_left && idx < 2) { | |
| nb[idx] = &t->l; | |
| boff[idx] = by4; | |
| idx++; | |
| } | |
| if (have_top_in_sb && idx < 2) { | |
| nb[idx] = t->a; | |
| boff[idx] = bx4; | |
| idx++; | |
| } | |
| if (idx < 2) { | |
| boff[idx] = -1; | |
| if (!idx) boff[1] = -1; | |
| } | |
| if (f->frame_hdr->allow_intrabc && imin(bw4, bh4) < 16 && | |
| b->intra && !t->intra_region) | |
| { | |
| const int ctx = (boff[0] == -1 ? 0 : nb[0]->intrabc[boff[0]]) + | |
| (boff[1] == -1 ? 0 : nb[1]->intrabc[boff[1]]); | |
| b->intrabc = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.intrabc[ctx]); | |
| DEBUG_BLOCK_printf("%*sPost-intrabc[ctx=%d,%d]: r=%d\n", | |
| depth, "", ctx, b->intrabc, ts->msac.rng); | |
| } | |
| } | |
| const int intrabc = has_luma && b->intrabc; | |
| // skip_txfm | |
| if (f->frame_hdr->segmentation.d.skip_mask & (1 << b->seg_id)) { | |
| b->skip_txfm = 1; | |
| } else if (b->intra && !intrabc) { | |
| if (has_luma) b->skip_txfm = 0; | |
| } else { | |
| const int ctx = (idx > 0 ? nx[0]->skip_txfm[xoff[0]] : 0) + | |
| (idx > 1 ? nx[1]->skip_txfm[xoff[1]] : 0) + | |
| b->skip_mode * 3; | |
| b->skip_txfm = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.skip_txfm[ctx]); | |
| DEBUG_BLOCK_printf("%*sPost-skip_txfm[ctx=%d,%d]: r=%d\n", | |
| depth, "", ctx, b->skip_txfm, ts->msac.rng); | |
| } | |
| const int skip_txfm = has_luma && b->skip_txfm; | |
| // segment_id | |
| if (seg_postskip) { | |
| if (!b->skip_txfm && f->frame_hdr->segmentation.temporal && | |
| (seg_pred = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.seg_pred[t->a->seg_pred[bx4] + | |
| t->l.seg_pred[by4]]))) | |
| { | |
| // temporal predicted seg_id | |
| if (f->prev_segmap) { | |
| unsigned seg_id = get_prev_frame_segid(f, t->by, t->bx, w4, h4, | |
| f->prev_segmap, | |
| f->b4_stride); | |
| if (seg_id >= DAV2D_MAX_SEGMENTS) return -1; | |
| b->seg_id = seg_id; | |
| } | |
| } else { | |
| int seg_ctx; | |
| const unsigned pred_seg_id = | |
| get_cur_frame_segid(t->by, t->bx, have_top, have_left, | |
| &seg_ctx, f->cur_segmap, f->b4_stride); | |
| if (b->skip_txfm && !f->frame_hdr->any_lossless) { | |
| b->seg_id = pred_seg_id; | |
| } else { | |
| const unsigned ext_flag = f->seq_hdr->segmentation.ext ? | |
| dav2d_msac_decode_bool_adapt(&ts->msac, ts->cdf.m.seg_id_ext[seg_ctx]) : | |
| 0; | |
| const unsigned diff = | |
| dav2d_msac_decode_symbol_adapt8(&ts->msac, | |
| ts->cdf.m.seg_id[ext_flag][seg_ctx], 7) + | |
| (ext_flag << 3); | |
| const unsigned last_active_seg_id = | |
| f->frame_hdr->segmentation.last_active_segid; | |
| b->seg_id = neg_deinterleave(diff, pred_seg_id, | |
| last_active_seg_id + 1); | |
| if (b->seg_id > last_active_seg_id) return -1; | |
| } | |
| if (b->seg_id >= DAV2D_MAX_SEGMENTS) return -1; | |
| } | |
| if (has_luma) | |
| DEBUG_BLOCK_printf("%*sPost-segid[%d]: r=%d\n", | |
| depth, "", b->seg_id, ts->msac.rng); | |
| } | |
| if (has_luma) { | |
| const int gdf_bs = f->frame_hdr->gdf.b64size; | |
| if (!((t->bx | t->by) & (16 * gdf_bs - 1))) { | |
| int idx = ((t->by & 48) >> 2) + ((t->bx & 48) >> 4); | |
| int flag; | |
| if (f->frame_hdr->gdf.enabled == DAV2D_ADAPTIVE && | |
| imax(f->cur.p.p.w, f->cur.p.p.h) > 64 * gdf_bs) | |
| { | |
| flag = dav2d_msac_decode_bool_adapt(&ts->msac, ts->cdf.m.gdf); | |
| DEBUG_BLOCK_printf("%*sPost-gdf[y=%d,x=%d,gdf=%d]: r=%d\n", | |
| depth, "", t->by, t->bx, | |
| flag, ts->msac.rng); | |
| } else | |
| flag = !!f->frame_hdr->gdf.enabled; | |
| splat2d(&t->lf_mask->gdf[idx], 4, ulog2(gdf_bs), gdf_bs, flag); | |
| } | |
| } | |
| // cdef index | |
| if (f->frame_hdr->cdef.enabled && | |
| (!skip_txfm || f->frame_hdr->cdef.on_skiptx)) | |
| { | |
| const int idx = ((t->bx & 0x30) >> 4) + ((t->by & 0x30) >> 2); | |
| int8_t *const cdef_ptr = &t->lf_mask->cdef_idx[idx]; | |
| if (*cdef_ptr == -1) { | |
| int v; | |
| if (f->frame_hdr->cdef.n_strengths == 1) { | |
| v = 0; | |
| } else { | |
| const int left_cdef_idx = | |
| t->bx - 16 < ts->tiling.col_start ? -1 : | |
| idx & 3 ? cdef_ptr[-1] : | |
| t->lf_mask[-1].cdef_idx[idx + 3]; | |
| const int top_cdef_idx = | |
| !((t->by & ~15) & (f->sb_step - 1)) ? -1 : | |
| idx & 0xc ? cdef_ptr[-4] : | |
| t->lf_mask[-f->sb256w].cdef_idx[idx + 12]; | |
| // cdef_idx=-1: --, 0: true, 1-7: false, edge combo -> context | |
| // ctx=0: false/false, false/--, --/false, --/-- | |
| // ctx=1: false/true, true/false | |
| // ctx=2: true/--, --/true, true/true [same coded block] | |
| // ctx=3: true/true [different coded block] | |
| int ctx; | |
| if ((left_cdef_idx | top_cdef_idx) != -1) { | |
| // both edges are available | |
| ctx = !left_cdef_idx + !top_cdef_idx; | |
| // FIXME this should only be done when both edges are *not* | |
| // from the same coded block | |
| ctx += ctx == 2; | |
| } else { | |
| ctx = !(left_cdef_idx & top_cdef_idx) * 2; | |
| } | |
| if (dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.cdef_idx0[ctx])) | |
| { | |
| v = 0; | |
| } else if (f->frame_hdr->cdef.n_strengths == 2) { | |
| v = 1; | |
| } else { | |
| const int rem = f->frame_hdr->cdef.n_strengths - 3; | |
| v = 1 + dav2d_msac_decode_symbol_adapt8(&ts->msac, | |
| ts->cdf.m.cdef_idx[rem], rem + 1); | |
| } | |
| DEBUG_BLOCK_printf("%*sPost-cdef_idx[ctx=%d,%d]: r=%d\n", | |
| depth, "", ctx, v, ts->msac.rng); | |
| } | |
| const int splat_idx = imax(0, b_dim[2] - 4); | |
| dav2d_memset_pow2[splat_idx](cdef_ptr, v); | |
| if (bh4 >= 32) { | |
| dav2d_memset_pow2[splat_idx](&cdef_ptr[4], v); | |
| if (bh4 == 64) { | |
| dav2d_memset_pow2[splat_idx](&cdef_ptr[8], v); | |
| dav2d_memset_pow2[splat_idx](&cdef_ptr[12], v); | |
| } | |
| } | |
| } | |
| } | |
| // ccso | |
| const int ccso_bs = f->frame_hdr->ccso.b64size; | |
| if (has_luma && !((t->bx | t->by) & (16 * ccso_bs - 1))) { | |
| const ptrdiff_t ccso_fidx = (t->bx >> 4) + (t->by >> 4) * f->sb256w * 4; | |
| const int ccso_bidx = ((t->by & 48) >> 2) + ((t->bx & 48) >> 4); | |
| for (int p = 0; p < 3; p++) { | |
| int flag; | |
| if (!f->frame_hdr->ccso.p[p].enabled) continue; | |
| if (f->frame_hdr->ccso.p[p].sb_reuse) { | |
| flag = f->prev_ccsomap[p][ccso_fidx]; | |
| } else { | |
| // for left/left-bottom [if no overhang] context: | |
| // ctx=0: --/--, false/--, --/false, false/false | |
| // ctx=1: false/true, true/false | |
| // ctx=2: true/--, --/true, true/true [same coded block] | |
| // ctx=3: true/true [different coded block] | |
| const int ccso_l = t->bx == ts->tiling.col_start ? 0 : | |
| bx4 > 0 ? t->lf_mask->ccso[p][ccso_bidx - ccso_bs] : | |
| t->lf_mask[-1].ccso[p][(ccso_bidx | 3) & ~(ccso_bs - 1)]; | |
| const int ctx = ccso_l * 2; | |
| flag = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.ccso[p][ctx]); | |
| DEBUG_BLOCK_printf("%*sPost-ccso[pl=%c,ctx=%d,%d]: r=%d\n", | |
| depth, "", "yuv"[p], ctx, flag, ts->msac.rng); | |
| } | |
| const int ccso_bs_log2 = ulog2(ccso_bs); | |
| splat2d(&t->lf_mask->ccso[p][ccso_bidx], 4, | |
| ccso_bs_log2, ccso_bs, flag); | |
| if (f->cur_ccsomap) | |
| splat2d(&f->cur_ccsomap[p * f->sb256w * f->sb256h * 16 + ccso_fidx], | |
| f->sb256w * 4, ccso_bs_log2, ccso_bs, flag); | |
| } | |
| } | |
| // delta-q/lf | |
| if (has_luma && !((t->bx | t->by) & (63 >> (2 - f->frame_hdr->sb128)))) { | |
| const int prev_qidx = ts->last_qidx; | |
| const int have_delta_q = f->frame_hdr->delta.q.present && | |
| (bs != f->root_bs || !b->skip_txfm); | |
| if (have_delta_q) { | |
| int delta_q = dav2d_msac_decode_symbol_adapt8(&ts->msac, | |
| ts->cdf.m.delta_q, 7); | |
| if (delta_q == 7) { | |
| const int n_bits = 1 + dav2d_msac_decode_bools_bypass(&ts->msac, 3); | |
| delta_q = dav2d_msac_decode_bools_bypass(&ts->msac, n_bits) + | |
| 5 + (1 << n_bits); | |
| } | |
| if (delta_q) { | |
| if (dav2d_msac_decode_bool_bypass(&ts->msac)) delta_q = -delta_q; | |
| delta_q *= 1 << f->frame_hdr->delta.q.res_log2; | |
| } | |
| const int qmax = 255 + 48 * f->seq_hdr->hbd; | |
| ts->last_qidx = iclip(ts->last_qidx + delta_q, 1, qmax); | |
| DEBUG_BLOCK_printf("%*sPost-delta_q[%d->%d]: r=%d\n", | |
| depth, "", delta_q >> f->frame_hdr->delta.q.res_log2, | |
| ts->last_qidx, ts->msac.rng); | |
| } | |
| const int new_qidx = ts->last_qidx; | |
| if (new_qidx == f->frame_hdr->quant.yac) { | |
| // assign frame-wide q values to this sb | |
| ts->dq = f->dq; | |
| } else if (new_qidx != prev_qidx) { | |
| // find sb-specific quant parameters | |
| init_quant_tables(f->seq_hdr, f->frame_hdr, f->n_seg, | |
| new_qidx, ts->dqmem); | |
| ts->dq = ts->dqmem; | |
| } | |
| uint16_t *qidx_ptr = &t->lf_mask->qidx[(bx4 >> 4) + ((by4 & 0x30) >> 2)]; | |
| const int sbsz64 = 1 << f->frame_hdr->sb128; | |
| for (int y64 = 0; y64 < sbsz64; y64++) { | |
| for (int x64 = 0; x64 < sbsz64; x64++) | |
| qidx_ptr[x64] = new_qidx; | |
| qidx_ptr += 4; | |
| } | |
| } | |
| b->fsc = 0; | |
| // intra/inter-specific stuff | |
| int midx = 0xff; // intra/luma directional intra prediction index, if set | |
| if (b->intra && !intrabc) { | |
| static const uint8_t reordered_nondir_y_mode[] = { | |
| DC_PRED, SMOOTH_PRED, SMOOTH_V_PRED, SMOOTH_H_PRED, PAETH_PRED, | |
| }; | |
| static const uint8_t reordered_dir_y_mode[] = { | |
| DIAG_DOWN_LEFT_PRED, VERT_LEFT_PRED, VERT_PRED, VERT_RIGHT_PRED, | |
| DIAG_DOWN_RIGHT_PRED, HOR_DOWN_PRED, HOR_PRED, HOR_UP_PRED, | |
| }; | |
| if (has_luma) { | |
| b->dpcm[0] = f->frame_hdr->segmentation.lossless[b->seg_id] && | |
| dav2d_msac_decode_bool_adapt(&ts->msac, ts->cdf.m.dpcm[0]); | |
| if (b->dpcm[0]) { | |
| if (dav2d_msac_decode_bool_adapt(&ts->msac, ts->cdf.m.dpcm_dir[0])) { | |
| b->y_mode = HOR_PRED; | |
| midx = 45; | |
| } else { | |
| b->y_mode = VERT_PRED; | |
| midx = 17; | |
| } | |
| b->mrl_index = 0; | |
| b->multi_mrl = 0; | |
| b->y_angle = 0; | |
| DEBUG_BLOCK_printf("%*sPost-ydpcm[dir=%d]: r=%d\n", | |
| depth, "", b->y_mode == HOR_PRED, ts->msac.rng); | |
| } else { | |
| const int y_set = dav2d_msac_decode_symbol_adapt4(&ts->msac, | |
| ts->cdf.m.intra_y_set, 3); | |
| int y_mode_idx, y_mode_ctx; | |
| if (!y_set) { | |
| y_mode_ctx = (w4 == bw4 && t->a->midx[bx4 + bw4 - 1] != 0xff) + | |
| (h4 == bh4 && t->l.midx[by4 + bh4 - 1] != 0xff); | |
| y_mode_idx = dav2d_msac_decode_symbol_adapt8(&ts->msac, | |
| ts->cdf.m.intra_y_idx0[y_mode_ctx], 7); | |
| if (y_mode_idx == 7) | |
| y_mode_idx += dav2d_msac_decode_symbol_adapt8(&ts->msac, | |
| ts->cdf.m.intra_y_idx1[y_mode_ctx], 5); | |
| } else { | |
| y_mode_idx = y_set * 16 - 3 + | |
| dav2d_msac_decode_bools_bypass(&ts->msac, 4); | |
| } | |
| if (y_mode_idx < 5) { | |
| b->y_mode = reordered_nondir_y_mode[y_mode_idx]; | |
| b->y_angle = 0; | |
| } else { | |
| const int dir_y_mode_idx = y_mode_idx - 5; | |
| static const uint8_t default_mode_list_y[] = { | |
| 17, 45, 3, 10, 24, 31, 38, 52, | |
| // (-2, +2) | |
| 15, 19, 43, 47, 1, 5, 8, 12, 22, 26, 29, 33, 36, 40, 50, 54, | |
| // (-1, +1) | |
| 16, 18, 44, 46, 2, 4, 9, 11, 23, 25, 30, 32, 37, 39, 51, 53, | |
| // (-3, +3) | |
| 14, 20, 42, 48, 0, 6, 7, 13, 21, 27, 28, 34, 35, 41, 49, 55 | |
| }; | |
| uint8_t custom_mode_list_y[56]; | |
| const uint8_t *reorder = default_mode_list_y; | |
| if (bw4 * bh4 > 2) { | |
| // modes are reordered if neighbour (above/left) modes used | |
| // directional intra prediction modes | |
| uint64_t mask = 0; | |
| uint8_t *ptr = custom_mode_list_y; | |
| *ptr = -1; | |
| if (h4 == bh4 && t->l.midx[by4 + bh4 - 1] != 0xff) { | |
| const int lmidx = t->l.midx[by4 + bh4 - 1]; | |
| *ptr++ = lmidx; | |
| mask |= 1ULL << lmidx; | |
| } | |
| if (w4 == bw4 && t->a->midx[bx4 + bw4 - 1] != 0xff) { | |
| const int amidx = t->a->midx[bx4 + bw4 - 1]; | |
| if (amidx != custom_mode_list_y[0]) { | |
| *ptr++ = amidx; | |
| mask |= 1ULL << amidx; | |
| } | |
| } | |
| int n_dirs = (int)(ptr - custom_mode_list_y); | |
| if (n_dirs > 0) { | |
| reorder = custom_mode_list_y; | |
| if (bw4 * bh4 > 4 && dir_y_mode_idx >= n_dirs) { | |
| // add surrounding [-3..+3] angles | |
| for (int i = 1; i < 5; i++) { | |
| for (int n = 0; n < n_dirs; n++) { | |
| const int cmidx = custom_mode_list_y[n]; | |
| for (int delta = -i, j = 0; j < 2; delta = +i, j++) { | |
| // FIXME replace modulo with fastdiv | |
| const int dmidx = (cmidx + delta + 56) % 56; | |
| if (!(mask & (1ULL << dmidx))) { | |
| *ptr++ = dmidx; | |
| mask |= 1ULL << dmidx; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| n_dirs = (int)(ptr - custom_mode_list_y); | |
| if (dir_y_mode_idx >= n_dirs) { | |
| // remainder of modes in default order | |
| for (unsigned long n = 0; | |
| n < ARRAY_SIZE(default_mode_list_y); n++) | |
| { | |
| const int fmidx = default_mode_list_y[n]; | |
| const uint64_t bit = 1ULL << fmidx; | |
| if (!(mask & bit)) *ptr++ = fmidx; | |
| } | |
| } | |
| } | |
| } | |
| const int dir_y_mode_reord = midx = reorder[dir_y_mode_idx]; | |
| // FIXME division/modulo can be replaced with fastdiv | |
| b->y_mode = reordered_dir_y_mode[dir_y_mode_reord / 7]; | |
| b->y_angle = dir_y_mode_reord % 7 - 3; | |
| } | |
| DEBUG_BLOCK_printf("%*sPost-intra_y_mode[set=%d,idx=%d,ctx=%d,mode=%d,angle=%d]: r=%d\n", | |
| depth, "", y_set, y_mode_idx, | |
| y_set > 0 ? -1 : y_mode_ctx, | |
| b->y_mode, b->y_angle, ts->msac.rng); | |
| } | |
| // =min(5,floor(log2(bw4+bh4)*1.99-1.62)) or | |
| // = floor(log2(bw4+bh4)*1.55-0.555) - or anything in between | |
| if (imax(bw4, bh4) <= 8 && f->seq_hdr->idtx_intra) { | |
| static const uint8_t fsc_bsize_groups[N_BS_SIZES] = { | |
| [BS_32x32] = 5, | |
| [BS_32x16] = 5, | |
| [BS_32x8] = 4, | |
| [BS_32x4] = 4, | |
| [BS_16x32] = 5, | |
| [BS_16x16] = 4, | |
| [BS_16x8] = 3, | |
| [BS_16x4] = 3, | |
| [BS_8x32] = 4, | |
| [BS_8x16] = 3, | |
| [BS_8x8] = 2, | |
| [BS_8x4] = 1, | |
| [BS_4x32] = 4, | |
| [BS_4x16] = 3, | |
| [BS_4x8] = 1, | |
| [BS_4x4] = 0, | |
| }; | |
| const int sz_ctx = fsc_bsize_groups[bs]; | |
| const int ctx = (IS_INTER_OR_SWITCH(f->frame_hdr) && | |
| !t->intra_region) ? 3 : | |
| (boff[0] == -1 ? 0 : nb[0]->fsc[boff[0]]) + | |
| (boff[1] == -1 ? 0 : nb[1]->fsc[boff[1]]); | |
| b->fsc = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.fsc[ctx][sz_ctx]); | |
| DEBUG_BLOCK_printf("%*sPost-fsc[ctx=%d|%d,%d]: r=%d\n", | |
| depth, "", ctx, sz_ctx, b->fsc, ts->msac.rng); | |
| } | |
| b->mrl_index = b->multi_mrl = 0; | |
| if (!b->dpcm[0] && midx != 0xff /* directional mode */ && | |
| f->seq_hdr->mrls) | |
| { | |
| const int ctx = (boff[0] == -1 ? 0 : nb[0]->mrl[boff[0]]) + | |
| (boff[1] == -1 ? 0 : nb[1]->mrl[boff[1]]); | |
| b->mrl_index = dav2d_msac_decode_symbol_adapt4(&ts->msac, | |
| ts->cdf.m.mrl_index[ctx], 3); | |
| DEBUG_BLOCK_printf("%*sPost-mrl_index[ctx=%d,%d]: r=%d\n", | |
| depth, "", ctx, b->mrl_index, ts->msac.rng); | |
| if (b->mrl_index > 0) { | |
| const int ctx2 = | |
| (boff[0] == -1 ? 0 : nb[0]->multi_mrl[boff[0]]) + | |
| (boff[1] == -1 ? 0 : nb[1]->multi_mrl[boff[1]]); | |
| b->multi_mrl = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.multi_mrl[ctx2]); | |
| DEBUG_BLOCK_printf("%*sPost-multi_line_mrl[ctx=%d,%d]: r=%d\n", | |
| depth, "", ctx2, b->multi_mrl, ts->msac.rng); | |
| } | |
| } | |
| } | |
| if (has_chroma) { | |
| b->dpcm[1] = f->frame_hdr->segmentation.lossless[b->seg_id] && | |
| dav2d_msac_decode_bool_adapt(&ts->msac, ts->cdf.m.dpcm[1]); | |
| if (b->dpcm[1]) { | |
| b->uv_mode = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.dpcm_dir[1]) ? HOR_PRED : VERT_PRED; | |
| if (lbs == BS_INVALID) | |
| midx = t->luma_intra_dir_mode_map[(t->by & 15) * 16 + | |
| (t->bx & 15)]; | |
| const int uv_mode_idx = b->uv_mode == HOR_PRED ? 45 : 17; | |
| b->uv_angle = abs(midx - uv_mode_idx) >= 4 ? 0 : (midx % 7) - 3; | |
| DEBUG_BLOCK_printf("%*sPost-uvdpcm[dir=%d]: r=%d\n", | |
| depth, "", b->uv_mode == HOR_PRED, ts->msac.rng); | |
| } else { | |
| const int ll = f->frame_hdr->segmentation.lossless[b->seg_id]; | |
| const int mhccp_allowed = f->seq_hdr->mhccp && | |
| imax(cbw4, cbh4) <= (ll ? 1 : 8) && cbw4 * cbh4 >= 2 - ll; | |
| const int cfl_allowed = (f->seq_hdr->cfl || mhccp_allowed) && | |
| (imax(bw4, bh4) > 16 || !t->sdp_cfl_disallowed) && | |
| imax(cbw4, cbh4) <= (ll ? 1 : 16); | |
| int is_cfl = 0, uv_mode_idx, cfl_ctx, uv_mode_ctx; | |
| if (cfl_allowed) { | |
| cfl_ctx = (t->a->uvmode[cbx4] == CFL_PRED) + | |
| (t->l.uvmode[cby4] == CFL_PRED); | |
| is_cfl = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.cfl[cfl_ctx]); | |
| } | |
| if (is_cfl) { | |
| b->uv_mode = CFL_PRED; | |
| b->uv_angle = 0; | |
| } else { | |
| if (lbs == BS_INVALID) | |
| midx = t->luma_intra_dir_mode_map[(t->by & 15) * 16 + | |
| (t->bx & 15)]; | |
| uv_mode_ctx = midx != 0xff; | |
| uv_mode_idx = dav2d_msac_decode_symbol_adapt8(&ts->msac, | |
| ts->cdf.m.intra_uv_mode[uv_mode_ctx], 7); | |
| if (uv_mode_idx == 7) | |
| uv_mode_idx += dav2d_msac_decode_bools_bypass(&ts->msac, 3); | |
| if (uv_mode_idx > 12) return -1; | |
| if (uv_mode_idx < uv_mode_ctx) { | |
| b->uv_mode = reordered_dir_y_mode[midx / 7]; | |
| b->uv_angle = (midx % 7) - 3; | |
| } else { | |
| if (uv_mode_idx - uv_mode_ctx < 5) { | |
| b->uv_mode = reordered_nondir_y_mode[uv_mode_idx - | |
| uv_mode_ctx]; | |
| b->uv_angle = 0; | |
| } else { | |
| static const uint8_t default_mode_list_uv[] = { | |
| VERT_PRED, HOR_PRED, DIAG_DOWN_LEFT_PRED, | |
| DIAG_DOWN_RIGHT_PRED, VERT_LEFT_PRED, | |
| VERT_RIGHT_PRED, HOR_DOWN_PRED, HOR_UP_PRED, | |
| }; | |
| static const uint8_t intra_dir_mode_y_to_uv_idx[] = { | |
| 2, 4, 0, 5, 3, 6, 1, 7 | |
| }; | |
| int idx = uv_mode_idx - 5 - uv_mode_ctx; | |
| idx += uv_mode_ctx && | |
| idx >= intra_dir_mode_y_to_uv_idx[midx / 7]; | |
| b->uv_mode = default_mode_list_uv[idx]; | |
| b->uv_angle = 0; | |
| } | |
| } | |
| } | |
| DEBUG_BLOCK_printf("%*sPost-intra_uv_mode[cfl=%d,idx=%d,ctx=%d|%d,mode=%d,angle=%d]: r=%d\n", | |
| depth, "", is_cfl, is_cfl ? -1 : uv_mode_idx, | |
| cfl_allowed ? cfl_ctx : -1, | |
| is_cfl ? -1 : uv_mode_ctx, b->uv_mode, | |
| b->uv_angle, ts->msac.rng); | |
| if (b->uv_mode == CFL_PRED) { | |
| memset(b->cfl_alpha, 0, sizeof(b->cfl_alpha)); | |
| if (mhccp_allowed && | |
| (!f->seq_hdr->cfl || | |
| dav2d_msac_decode_bool_adapt(&ts->msac, ts->cdf.m.mhccp))) | |
| { | |
| const int sz_ctx = size_group_lookup[bs]; | |
| b->cfl_type = CFL_MHCCP; | |
| b->cfl_mh_dir = | |
| dav2d_msac_decode_symbol_adapt4(&ts->msac, | |
| ts->cdf.m.mhccp_filter_dir[sz_ctx], 2); | |
| } else { | |
| b->cfl_type = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.cfl_type); | |
| if (b->cfl_type == CFL_EXPLICIT) { | |
| const int sign = dav2d_msac_decode_symbol_adapt8(&ts->msac, | |
| ts->cdf.m.cfl_sign, 7) + 1; | |
| const int sign_u = sign * 0x56 >> 8; | |
| const int sign_v = sign - sign_u * 3; | |
| assert(sign_u == sign / 3); | |
| if (sign_u) { | |
| const int ctx = (sign_u == 2) * 3 + sign_v; | |
| b->cfl_alpha[0] = dav2d_msac_decode_symbol_adapt8(&ts->msac, | |
| ts->cdf.m.cfl_alpha[ctx], 7) + 1; | |
| if (sign_u == 1) b->cfl_alpha[0] = -b->cfl_alpha[0]; | |
| } | |
| if (sign_v) { | |
| const int ctx = (sign_v == 2) * 3 + sign_u; | |
| b->cfl_alpha[1] = dav2d_msac_decode_symbol_adapt8(&ts->msac, | |
| ts->cdf.m.cfl_alpha[ctx], 7) + 1; | |
| if (sign_v == 1) b->cfl_alpha[1] = -b->cfl_alpha[1]; | |
| } | |
| } | |
| } | |
| DEBUG_BLOCK_printf("%*sPost-cfl[type=%d,%s=%d|%d]: r=%d\n", | |
| depth, "", b->cfl_type, | |
| b->cfl_type == CFL_MHCCP ? "mhdir" : "alpha", | |
| b->cfl_type == CFL_MHCCP ? b->cfl_mh_dir : | |
| b->cfl_alpha[0], | |
| b->cfl_alpha[1], ts->msac.rng); | |
| } | |
| } | |
| } | |
| if (has_luma) { | |
| b->pal_sz = 0; | |
| if (f->frame_hdr->allow_screen_content_tools && | |
| b->y_mode == DC_PRED && imax(bw4, bh4) <= 16 && bw4 + bh4 >= 4) | |
| { | |
| const int use_y_pal = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.pal_y); | |
| if (use_y_pal) { | |
| f->bd_fn.read_pal_plane(DB_ONLY(depth) t, b, bx4, by4); | |
| } else | |
| DEBUG_BLOCK_printf("%*sPost-ypal[0]: r=%d\n", | |
| depth, "", ts->msac.rng); | |
| } | |
| b->dip = 0; | |
| if (b->y_mode == DC_PRED && f->seq_hdr->intra_dip && | |
| !b->pal_sz && imin(bw4, bh4) >= 2 && bw4 * bh4 >= 8) | |
| { | |
| const int ctx = (boff[0] == -1 ? 0 : nb[0]->dip[boff[0]]) + | |
| (boff[1] == -1 ? 0 : nb[1]->dip[boff[1]]); | |
| b->dip = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.dip[ctx]); | |
| if (b->dip) { | |
| const int tp = dav2d_msac_decode_bool_bypass(&ts->msac); | |
| const int m = | |
| dav2d_msac_decode_symbol_adapt8(&ts->msac, ts->cdf.m.dip_mode, 5); | |
| b->dip = (tp << 4) | (m + 1); | |
| } | |
| DEBUG_BLOCK_printf("%*sPost-dip[ctx=%d,%d,tp=%d,mode=%d]: r=%d\n", | |
| depth, "", ctx, !!b->dip, b->dip >> 4, | |
| (b->dip - !!b->dip) & 7, ts->msac.rng); | |
| } | |
| if (b->pal_sz) { | |
| uint8_t *pal_idx; | |
| if (f->c->task_thread.n_passes > 1) { | |
| const int p = !!(t->task_thread.pass & PASS_ENTROPY); | |
| assert(ts->frame_thread[p].pal_idx); | |
| pal_idx = ts->frame_thread[p].pal_idx; | |
| ts->frame_thread[p].pal_idx += bw4 * bh4 * 8; | |
| } else | |
| pal_idx = t->scratch.pal_idx_y; | |
| read_pal_indices(t, pal_idx, b->pal_sz, | |
| (int[4]) { w4 * 4, h4 * 4, bw4 * 4, bh4 * 4 }); | |
| DEBUG_BLOCK_printf("%*sPost-y-pal-indices: r=%d\n", | |
| depth, "", ts->msac.rng); | |
| } | |
| if (read_tx_part(t, DB_ONLY(depth) b, bs)) return -1; | |
| } | |
| // reconstruction | |
| if (has_luma) { | |
| b->is_sm[0].a = sm_flag(t->a, bx4); | |
| b->is_sm[0].l = sm_flag(&t->l, by4); | |
| } | |
| if (has_chroma) { | |
| b->is_sm[1].a = sm_uv_flag(t->a, cbx4); | |
| b->is_sm[1].l = sm_uv_flag(&t->l, cby4); | |
| } | |
| if (t->task_thread.pass == PASS_ENTROPY) { | |
| const int res = f->bd_fn.read_coef_blocks(t, DB_ONLY(depth) lbs, cbs, b); | |
| if (res < 0) return res; | |
| } else { | |
| const int res = recon_b(t, DB_ONLY(depth) lbs, cbs, b); | |
| if (res < 0) return res; | |
| } | |
| if (has_luma) { | |
| // update contexts | |
| BlockContext *edge = t->a; | |
| for (int i = 0, off = bx4; i < 2; i++, off = by4, edge = &t->l) { | |
| #define set_ctx(rep_macro) \ | |
| rep_macro(edge->fsc, off, b->fsc); \ | |
| rep_macro(edge->mode, off, b->y_mode); \ | |
| rep_macro(edge->midx, off, midx); \ | |
| rep_macro(edge->mrl, off, !!b->mrl_index); \ | |
| rep_macro(edge->multi_mrl, off, b->multi_mrl); \ | |
| rep_macro(edge->dip, off, !!b->dip); \ | |
| rep_macro(edge->pal_sz, off, b->pal_sz); \ | |
| rep_macro(edge->seg_pred, off, seg_pred); \ | |
| rep_macro(edge->skip_mode, off, 0); \ | |
| rep_macro(edge->intra, off, 1); \ | |
| rep_macro(edge->intrabc, off, 0); \ | |
| rep_macro(edge->morph_pred, off, 0); \ | |
| rep_macro(edge->skip_txfm, off, b->skip_txfm); \ | |
| if (IS_INTER_OR_SWITCH(f->frame_hdr)) { \ | |
| rep_macro(edge->amvd, off, 0); \ | |
| rep_macro(edge->mvprec, off, 0); \ | |
| rep_macro(edge->motion_mode, off, 0); \ | |
| rep_macro(edge->comp_type, off, COMP_INTER_NONE); \ | |
| rep_macro(edge->ref[0], off, ((uint8_t) -1)); \ | |
| rep_macro(edge->ref[1], off, ((uint8_t) -1)); \ | |
| } | |
| case_set(b_dim[2 + i]); | |
| #undef set_ctx | |
| } | |
| } | |
| if (has_luma && b->pal_sz) | |
| f->bd_fn.copy_pal_block_y(t, bx4, by4, bw4, bh4); | |
| if (has_chroma) { | |
| uint8_t uv_mode = b->uv_mode; | |
| dav2d_memset_pow2[ulog2(cbw4)](&t->a->uvmode[cbx4], uv_mode); | |
| dav2d_memset_pow2[ulog2(cbh4)](&t->l.uvmode[cby4], uv_mode); | |
| } | |
| } else if (b->intrabc) { | |
| // intra block copy | |
| b->is_refmv = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.intrabc_mode); | |
| for (b->drl_idx[0] = 0; b->drl_idx[0] < f->frame_hdr->max_bvp_drl_bits; | |
| b->drl_idx[0]++) | |
| { | |
| if (!dav2d_msac_decode_bool_bypass(&ts->msac)) break; | |
| } | |
| b->ref.pair = -1; | |
| b->is_qpel = !f->frame_hdr->force_integer_mv; | |
| if (!b->is_refmv && !f->frame_hdr->force_integer_mv) { | |
| b->is_qpel = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.intrabc_precision); | |
| } | |
| if (!b->is_refmv) { | |
| read_mv_residual(ts, &ts->cdf.dmv, &b->mv[0], 3 + 2 * b->is_qpel); | |
| if (b->mv[0].y) { | |
| const int s = dav2d_msac_decode_bool_bypass(&ts->msac); | |
| if (s) b->mv[0].y = -b->mv[0].y; | |
| } | |
| if (b->mv[0].x) { | |
| const int s = dav2d_msac_decode_bool_bypass(&ts->msac); | |
| if (s) b->mv[0].x = -b->mv[0].x; | |
| } | |
| DEBUG_BLOCK_printf("%*sPost-mvdiff[y:%d,x:%d]: r=%d\n", | |
| depth, "", b->mv[0].y, b->mv[0].x, ts->msac.rng); | |
| } | |
| int morphctx = -1; | |
| b->morph_pred = 0; | |
| if (!(f->frame_hdr->frame_type & 1) && f->seq_hdr->bawp && | |
| f->frame_hdr->allow_screen_content_tools) | |
| { | |
| morphctx = (boff[0] == -1 ? 0 : nb[0]->morph_pred[boff[0]]) + | |
| (boff[1] == -1 ? 0 : nb[1]->morph_pred[boff[1]]); | |
| b->morph_pred = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.morph_pred[morphctx]); | |
| } | |
| DEBUG_BLOCK_printf("%*sPost-intrabc_info[mode=%d,drl=%d," | |
| "prec=%d,morphctx=%d,morph=%d]: r=%d\n", | |
| depth, "", b->is_refmv, b->drl_idx[0], | |
| b->is_qpel, morphctx, b->morph_pred, ts->msac.rng); | |
| if (read_tx_part(t, DB_ONLY(depth) b, bs)) return -1; | |
| // reconstruction | |
| if (t->task_thread.pass == PASS_ENTROPY) { | |
| const int res = f->bd_fn.read_coef_blocks(t, DB_ONLY(depth) lbs, cbs, b); | |
| if (res < 0) return res; | |
| b->filter = DAV2D_FILTER_BILINEAR; | |
| } else { | |
| const int res = recon_b(t, DB_ONLY(depth) lbs, cbs, b); | |
| if (res < 0) return res; | |
| } | |
| if (has_luma) { | |
| BlockContext *edge = t->a; | |
| for (int i = 0, off = bx4; i < 2; i++, off = by4, edge = &t->l) { | |
| #define set_ctx(rep_macro) \ | |
| rep_macro(edge->fsc, off, 0); \ | |
| rep_macro(edge->mode, off, DC_PRED); \ | |
| rep_macro(edge->midx, off, 0xff); \ | |
| rep_macro(edge->mrl, off, 0); \ | |
| rep_macro(edge->multi_mrl, off, 0); \ | |
| rep_macro(edge->dip, off, 0); \ | |
| rep_macro(edge->pal_sz, off, 0); \ | |
| rep_macro(edge->seg_pred, off, seg_pred); \ | |
| rep_macro(edge->skip_mode, off, 0); \ | |
| rep_macro(edge->intrabc, off, 1); \ | |
| rep_macro(edge->morph_pred, off, b->morph_pred); \ | |
| rep_macro(edge->intra, off, 1); \ | |
| rep_macro(edge->skip_txfm, off, b->skip_txfm); \ | |
| if (IS_INTER_OR_SWITCH(f->frame_hdr)) { \ | |
| rep_macro(edge->amvd, off, 0); \ | |
| rep_macro(edge->mvprec, off, 0); \ | |
| rep_macro(edge->comp_type, off, COMP_INTER_NONE); \ | |
| rep_macro(edge->motion_mode, off, 0); \ | |
| rep_macro(edge->ref[0], off, ((uint8_t) -1)); \ | |
| rep_macro(edge->ref[1], off, ((uint8_t) -1)); \ | |
| } | |
| case_set(b_dim[2 + i]); | |
| #undef set_ctx | |
| } | |
| } | |
| if (has_chroma) { | |
| dav2d_memset_pow2[ulog2(cbw4)](&t->a->uvmode[cbx4], DC_PRED); | |
| dav2d_memset_pow2[ulog2(cbh4)](&t->l.uvmode[cby4], DC_PRED); | |
| } | |
| } else { | |
| // inter-specific mode/mv coding | |
| int is_comp, has_subpel_filter, is_tip = 0; | |
| if (!b->skip_mode && f->frame_hdr->tip.frame_mode && | |
| lbs == cbs_tree && imax(bw4, bh4) >= 2) | |
| { | |
| const int ctx = (idx < 1 ? 0 : nx[0]->ref[0][xoff[0]] == TIP_FRAME) + | |
| (idx < 2 ? 0 : nx[1]->ref[0][xoff[1]] == TIP_FRAME); | |
| is_tip = dav2d_msac_decode_bool_adapt(&ts->msac, ts->cdf.m.tip[ctx]); | |
| DEBUG_BLOCK_printf("%*sPost-tip[ctx=%d,%d]: r=%d\n", | |
| depth, "", ctx, is_tip, ts->msac.rng); | |
| } | |
| if (b->skip_mode) { | |
| is_comp = 1; | |
| } else if (!is_tip && | |
| !((f->frame_hdr->segmentation.d.globalmv_mask | | |
| f->frame_hdr->segmentation.d.skip_mask) & (1 << b->seg_id)) && | |
| f->frame_hdr->switchable_comp_refs && bw4 * bh4 >= 4) | |
| { | |
| const int ctx = get_comp_ctx(nx, xoff, idx, &f->refdir_with_intra[1]); | |
| is_comp = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.comp[ctx]); | |
| } else { | |
| is_comp = 0; | |
| } | |
| static const uint8_t mv_prec_tbl[][3] = { | |
| { 3, 1, 0 }, | |
| { 4, 3, 1 }, | |
| }; | |
| int mvprec_def = 1; | |
| b->amvd = 0; | |
| b->motion_mode = MM_TRANSLATION; | |
| b->refine_mv = 0; | |
| if (b->skip_mode) { | |
| const int max_drl_bits = f->frame_hdr->max_drl_bits; | |
| b->drl_idx[0] = 0; | |
| for (int ctx = 0; b->drl_idx[0] < max_drl_bits; | |
| b->drl_idx[0]++, ctx += ctx < 2) | |
| { | |
| if (!dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.skip_mode_drl_idx[ctx])) | |
| { | |
| break; | |
| } | |
| } | |
| DEBUG_BLOCK_printf("%*sPost-drl[%d,%d]: r=%d\n", | |
| depth, "", b->drl_idx[0], b->drl_idx[0], ts->msac.rng); | |
| b->ref.pair = f->skip_mode_refs.pair; | |
| for (int n = 0; n < idx; n++) { | |
| if (nx[n]->ref[0][xoff[n]] == TIP_FRAME) { | |
| b->ref.ref[0] = imin(f->rf.tip.ref.ref[0], | |
| f->rf.tip.ref.ref[1]); | |
| b->ref.ref[1] = imax(f->rf.tip.ref.ref[0], | |
| f->rf.tip.ref.ref[1]); | |
| break; | |
| } else if (nx[n]->ref[1][xoff[n]] != -1) { | |
| b->ref.ref[0] = nx[n]->ref[0][xoff[n]]; | |
| b->ref.ref[1] = nx[n]->ref[1][xoff[n]]; | |
| break; | |
| } else if (nx[n]->ref[0][xoff[n]] != -1) break; | |
| } | |
| b->comp_type = COMP_INTER_AVG; | |
| b->inter_mode = NEARMV_NEARMV; | |
| has_subpel_filter = 0; | |
| } else if (is_comp) { | |
| const int n_refs = f->frame_hdr->n_ref_frames; | |
| if (n_refs > 1) { | |
| const int same_refs = f->seq_hdr->num_same_ref_comp; | |
| int n = 0; | |
| uint8_t cnt[9] = { 0 }; | |
| if (idx > 0) { | |
| cnt[nx[0]->ref[0][xoff[0]] + 1]++; | |
| cnt[nx[0]->ref[1][xoff[0]] + 1]++; | |
| if (idx > 1) { | |
| cnt[nx[1]->ref[0][xoff[1]] + 1]++; | |
| cnt[nx[1]->ref[1][xoff[1]] + 1]++; | |
| } | |
| } | |
| int cnt_rem = idx * 2 - cnt[0] - cnt[8]; | |
| for (int i = 0, maybe_same_ref = !!same_refs, dir; | |
| i < n_refs + n - 2 + maybe_same_ref; i++) | |
| { | |
| int bit; | |
| const int cnt_cur = cnt[i + 1]; | |
| cnt_rem -= cnt_cur; | |
| if (!n && (i == 2 || (i >= n_refs - 2 && i + 1 >= same_refs))) { | |
| bit = 1; | |
| } else { | |
| const int ctx = iclip(cnt_cur - cnt_rem + 1, 0, 2); | |
| uint16_t *const cdf = | |
| n == 0 ? ts->cdf.m.comp0_ref[ctx][i] : | |
| ts->cdf.m.comp1_ref[ctx][dir ^ f->refdir[i]][i]; | |
| bit = dav2d_msac_decode_bool_adapt(&ts->msac, cdf); | |
| } | |
| if (bit) { | |
| b->ref.ref[n++] = i; | |
| if (n == 2) break; | |
| dir = f->refdir[i]; | |
| } | |
| if (maybe_same_ref) { | |
| assert(i < same_refs); | |
| maybe_same_ref = !bit && i + 1 < same_refs; | |
| if (bit) { | |
| i--; | |
| cnt_rem += cnt_cur; | |
| } | |
| } | |
| } | |
| if (n < 2) { | |
| b->ref.ref[1] = n_refs - 1; | |
| if (!n) b->ref.ref[0] = n_refs - 1 - (same_refs < n_refs); | |
| } | |
| } else { | |
| b->ref.pair = 0; | |
| } | |
| DEBUG_BLOCK_printf("%*sPost-ref[%d,%d]: r=%d\n", | |
| depth, "", b->ref.ref[0], b->ref.ref[1], ts->msac.rng); | |
| const int have_top_right = t->bx + bw4 <= ts->tiling.col_end; | |
| const int have_bottom_left = t->by + bh4 <= ts->tiling.row_end; | |
| const int comp_ctx = | |
| get_compref_ctx(t->a, &t->l, by4, bx4, have_top, have_left, | |
| have_top_right, have_bottom_left, b_dim, | |
| b->ref, f->rf.tip.ref); | |
| if (b->ref.ref[0] == b->ref.ref[1]) { | |
| b->inter_mode = NEARMV_NEARMV + | |
| dav2d_msac_decode_symbol_adapt4(&ts->msac, | |
| ts->cdf.m.comp_mode_sameref[comp_ctx], 3); | |
| b->inter_mode += b->inter_mode > NEARMV_NEWMV; // skip newmv_nearmv | |
| } else { | |
| const int joint_ctx = | |
| f->refdist[b->ref.ref[0]] != -f->refdist[b->ref.ref[1]]; | |
| if (dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.comp_mode_joint[joint_ctx])) | |
| { | |
| b->inter_mode = JOINT_NEWMV; | |
| } else { | |
| b->inter_mode = NEARMV_NEARMV + | |
| dav2d_msac_decode_symbol_adapt8(&ts->msac, | |
| ts->cdf.m.comp_mode[comp_ctx], 4); | |
| } | |
| } | |
| enum CompInterPredMode coded_mode = b->inter_mode; | |
| if (f->no_switch_opfl_refine_type && | |
| b->inter_mode != GLOBALMV_GLOBALMV && imin(bw4, bh4) >= 2 && | |
| f->refdir[b->ref.ref[0]] != f->refdir[b->ref.ref[1]] && | |
| !f->svc[b->ref.ref[0]][0].scale && !f->svc[b->ref.ref[1]][0].scale) | |
| { | |
| const int ctx = b->inter_mode > NEARMV_NEARMV; | |
| if (f->frame_hdr->opfl_refine_type == 2 /* always */ || | |
| dav2d_msac_decode_bool_adapt(&ts->msac, ts->cdf.m.opfl[ctx])) | |
| { | |
| b->inter_mode += 6 - (b->inter_mode >= GLOBALMV_GLOBALMV); | |
| if (f->frame_hdr->opfl_refine_type == 1 /* switchable */) | |
| coded_mode = b->inter_mode; | |
| } | |
| } | |
| DEBUG_BLOCK_printf("%*sPost-comp_inter_mode[ctx=%d,%d]: r=%d\n", | |
| depth, "", comp_ctx, coded_mode, ts->msac.rng); | |
| #define NEWMV_MASK ((1 << NEARMV_NEWMV) | \ | |
| (1 << NEWMV_NEARMV) | \ | |
| (1 << NEWMV_NEWMV) | \ | |
| (1 << JOINT_NEWMV) | \ | |
| (1 << OPFL_NEARMV_NEWMV) | \ | |
| (1 << OPFL_NEWMV_NEARMV) | \ | |
| (1 << OPFL_NEWMV_NEWMV) | \ | |
| (1 << OPFL_JOINT_NEWMV)) | |
| const int is_newmv_mode = (1 << b->inter_mode) & NEWMV_MASK; | |
| #undef NEWMV_MASK | |
| if (f->seq_hdr->adaptive_mvd && is_newmv_mode) { | |
| static uint8_t amvd_mode_context[] = { | |
| [NEARMV_NEWMV - NEARMV_NEWMV] = 0, | |
| [NEWMV_NEARMV - NEARMV_NEWMV] = 1, | |
| [OPFL_NEARMV_NEWMV - NEARMV_NEWMV] = 2, | |
| [OPFL_NEWMV_NEARMV - NEARMV_NEWMV] = 3, | |
| [JOINT_NEWMV - NEARMV_NEWMV] = 5, | |
| [OPFL_JOINT_NEWMV - NEARMV_NEWMV] = 6, | |
| [NEWMV_NEWMV - NEARMV_NEWMV] = 7, | |
| [OPFL_NEWMV_NEWMV - NEARMV_NEWMV] = 8, | |
| }; | |
| const int mode_ctx = amvd_mode_context[coded_mode - NEARMV_NEWMV]; | |
| const int ctx = | |
| (idx > 0 && nx[0]->ref[0][xoff[0]] == b->ref.ref[0] && | |
| nx[0]->amvd[xoff[0]]) + | |
| (idx > 1 && nx[1]->ref[0][xoff[1]] == b->ref.ref[0] && | |
| nx[1]->amvd[xoff[1]]); | |
| b->amvd = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.amvd[mode_ctx][ctx]); | |
| DEBUG_BLOCK_printf("%*sPost-amvd[ctx=%d|%d,%d]: r=%d\n", | |
| depth, "", mode_ctx, ctx, b->amvd, ts->msac.rng); | |
| } | |
| int jmvd_scale_mode = 0; | |
| if (b->inter_mode == JOINT_NEWMV || b->inter_mode == OPFL_JOINT_NEWMV) { | |
| jmvd_scale_mode = b->amvd ? | |
| dav2d_msac_decode_symbol_adapt4(&ts->msac, | |
| ts->cdf.m.jmvd_amvd_scale_mode, 2) : | |
| dav2d_msac_decode_symbol_adapt8(&ts->msac, | |
| ts->cdf.m.jmvd_scale_mode, 4); | |
| DEBUG_BLOCK_printf("%*sPost-jmvd_scale_mode[%d]: r=%d\n", | |
| depth, "", jmvd_scale_mode, ts->msac.rng); | |
| } | |
| if (coded_mode == NEWMV_NEWMV && imin(bw4, bh4) > 1 && | |
| !f->frame_hdr->force_integer_mv && b->ref.ref[0] != b->ref.ref[1] && | |
| f->frame_hdr->opfl_refine_type != 2 /* always */ && | |
| f->frame_hdr->motion_modes & (1 << MM_WARP_CAUSAL)) | |
| { | |
| const int is_sb_boundary = !(t->by & (f->sb_step - 1)); | |
| const int ref1 = b->ref.ref[0], ref2 = b->ref.ref[1]; | |
| #define match_ref(dir, off, refidx) \ | |
| (t->dir ref[0][off] == refidx || t->dir ref[1][off] == refidx) | |
| #define match_refs(refidx) \ | |
| (match_ref(l., by4, refidx) || \ | |
| (t->by + bh4 <= ts->tiling.row_end && \ | |
| match_ref(l., by4 + bh4 - 1, refidx)) || \ | |
| (is_sb_boundary ? \ | |
| (match_ref(a_sb_cache., bx4 & ~1, refidx) || \ | |
| (((t->bx + bw4 - 2) & ~1) < ts->tiling.col_end && \ | |
| match_ref(a_sb_cache., (bx4 + bw4 - 2) & ~1, refidx))) : \ | |
| (match_ref(a->, bx4, refidx) || \ | |
| (t->bx + bw4 <= ts->tiling.col_end && \ | |
| match_ref(a->, bx4 + bw4 - 1, refidx))))) | |
| if (match_refs(ref1) && match_refs(ref2)) { | |
| #undef match_refs | |
| #undef match_ref | |
| const enum MotionMode x1 = boff[0] == -1 ? MM_TRANSLATION : | |
| nb[0]->motion_mode[boff[0]], | |
| x2 = boff[1] == -1 ? MM_TRANSLATION : | |
| nb[1]->motion_mode[boff[1]]; | |
| const int cs_ctx = | |
| (x1 >= MM_WARP_CAUSAL || x2 >= MM_WARP_CAUSAL) + | |
| (x1 == MM_WARP_CAUSAL) + (x2 == MM_WARP_CAUSAL); | |
| if (dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.warp_causal[cs_ctx])) | |
| { | |
| b->motion_mode = MM_WARP_CAUSAL; | |
| } | |
| DEBUG_BLOCK_printf("%*sPost-comp_newmv_warp[%d]: r=%d\n", | |
| depth, "", b->motion_mode, ts->msac.rng); | |
| } | |
| } | |
| // drl | |
| memset(b->drl_idx, 0, sizeof(b->drl_idx)); | |
| if (b->inter_mode != GLOBALMV_GLOBALMV) { | |
| const int n_drls = 1 + (coded_mode <= NEARMV_NEWMV); | |
| const int max_drl_bits = f->frame_hdr->max_drl_bits; | |
| for (int r = 0, n = 0, ctx = 0; r < n_drls; r++) { | |
| for (; n < max_drl_bits; n++, ctx += ctx < 2) { | |
| if (!dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.drl_idx[ctx][comp_ctx])) | |
| { | |
| break; | |
| } | |
| } | |
| b->drl_idx[r] = n; | |
| n = coded_mode == NEARMV_NEARMV && b->ref.ref[0] == b->ref.ref[1] ? | |
| b->drl_idx[0] + (b->drl_idx[0] < max_drl_bits) : 0; | |
| ctx = imin(n, 2); | |
| } | |
| if (n_drls == 1) b->drl_idx[1] = b->drl_idx[0]; | |
| DEBUG_BLOCK_printf("%*sPost-drl[%d,%d]: r=%d\n", | |
| depth, "", b->drl_idx[0], b->drl_idx[1], ts->msac.rng); | |
| } | |
| // mv precision | |
| b->mv_prec = 3 + f->frame_hdr->mv_precision; | |
| if (b->mv_prec > 3 && !b->amvd && f->seq_hdr->flex_mvres && is_newmv_mode) { | |
| const int mvprec1 = boff[0] == -1 ? 0 : nb[0]->mvprec[boff[0]]; | |
| const int mvprec2 = boff[1] == -1 ? 0 : nb[1]->mvprec[boff[1]]; | |
| const int ctx1 = (mvprec1 & 1) + (mvprec2 & 1); | |
| if (!dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.mvprec_def[ctx1])) | |
| { | |
| const int ctx2 = (mvprec1 | mvprec2) >> 1; | |
| const int idx = | |
| dav2d_msac_decode_symbol_adapt4(&ts->msac, | |
| ts->cdf.m.mvprec_rem[ctx2][b->mv_prec - 4], 2); | |
| b->mv_prec = mv_prec_tbl[b->mv_prec == 6][idx]; | |
| mvprec_def = 2; | |
| } | |
| DEBUG_BLOCK_printf("%*sPost-mv_precision[ctx=%d|%d|%d,%d]: r=%d\n", | |
| depth, "", ctx1, | |
| mvprec_def == 1 ? -1 : (mvprec1 | mvprec2) >> 1, | |
| mvprec_def == 1 ? -1 : | |
| f->frame_hdr->mv_precision - 1, | |
| b->mv_prec, ts->msac.rng); | |
| } | |
| if (b->inter_mode != GLOBALMV_GLOBALMV) { | |
| int start = 0, end = 2; | |
| int refdist[2]; | |
| if (b->inter_mode == JOINT_NEWMV || | |
| b->inter_mode == OPFL_JOINT_NEWMV) | |
| { | |
| refdist[0] = f->absrefdist[b->ref.ref[0]]; | |
| refdist[1] = f->absrefdist[b->ref.ref[1]]; | |
| start = refdist[0] < refdist[1]; | |
| if (f->refdir[b->ref.ref[0]] ^ f->refdir[b->ref.ref[1]]) | |
| refdist[!start] = -refdist[!start]; | |
| end = start + 1; | |
| } | |
| enum InterPredMode m[2]; | |
| int n; | |
| int sum_mvd = 0, nnzc = 0; | |
| ZERO2MV(&b->mv); | |
| for (n = start; n < end; n++) { | |
| m[n] = dav2d_comp_inter_pred_modes[b->inter_mode - | |
| NEARMV_NEARMV][n]; | |
| if (m[n] != NEWMV) continue; | |
| if (b->amvd) { | |
| read_amvd(ts, &b->mv[n]); | |
| // nnzc remains zero if amvd=1, so mvd_sign_derive=>0 | |
| } else { | |
| read_mv_residual(ts, &ts->cdf.mv, &b->mv[n], b->mv_prec); | |
| sum_mvd += b->mv[n].y + b->mv[n].x; | |
| nnzc += !!b->mv[n].y + !!b->mv[n].x; | |
| } | |
| } | |
| if (b->inter_mode != NEARMV_NEARMV && | |
| b->inter_mode != OPFL_NEARMV_NEARMV) | |
| { | |
| #define BIDIR_NEWMV_MASK ((1 << NEWMV_NEWMV) | \ | |
| (1 << OPFL_NEWMV_NEWMV) | \ | |
| (1 << JOINT_NEWMV) | \ | |
| (1 << OPFL_JOINT_NEWMV)) | |
| if (!f->seq_hdr->mvd_sign_derive || b->drl_idx[0] || | |
| b->drl_idx[1] || nnzc < 3 * (end - start) - 2 || | |
| f->frame_hdr->allow_screen_content_tools || | |
| f->frame_hdr->mv_precision == 3 || b->mv_prec >= 5 || | |
| !((1 << b->inter_mode) & BIDIR_NEWMV_MASK) || | |
| b->motion_mode != MM_TRANSLATION) | |
| { | |
| // this means nnzc2 never reaches nnzc below, so the | |
| // sign-derive condition is never invoked | |
| nnzc = 5; | |
| } | |
| #undef BIDIR_NEWMV_MASK | |
| sum_mvd >>= (6 - b->mv_prec); | |
| int nnzc2 = 0; | |
| for (n = start; n < end; n++) { | |
| if (m[n] != NEWMV) continue; | |
| if (b->mv[n].y) { | |
| const int s = ++nnzc2 == nnzc ? sum_mvd & 1 : | |
| dav2d_msac_decode_bool_bypass(&ts->msac); | |
| if (s) b->mv[n].y = -b->mv[n].y; | |
| } | |
| if (b->mv[n].x) { | |
| const int s = ++nnzc2 == nnzc ? sum_mvd & 1 : | |
| dav2d_msac_decode_bool_bypass(&ts->msac); | |
| if (s) b->mv[n].x = -b->mv[n].x; | |
| } | |
| DEBUG_BLOCK_printf("%*sPost-mvdiff[%d,y:%d,x:%d]: r=%d\n", | |
| depth, "", n, b->mv[n].y, b->mv[n].x, | |
| ts->msac.rng); | |
| } | |
| if (b->inter_mode == JOINT_NEWMV || | |
| b->inter_mode == OPFL_JOINT_NEWMV) | |
| { | |
| n &= 1; // "the one not handled above" | |
| b->mv_prec = (6 << (n * 4)) | | |
| (b->mv_prec << (!n * 4)); | |
| b->mv[n] = dav2d_mv_projection(b->mv[!n], | |
| iclip(refdist[n], -31, +31), | |
| imin(refdist[!n], 31), | |
| -0xffff, 0xffff); | |
| jmvd_scale(&b->mv[n], b->amvd, jmvd_scale_mode); | |
| } else { | |
| b->mv_prec *= 0x11; | |
| } | |
| } | |
| } | |
| if (f->no_switch_refine_mv && imin(bw4, bh4) >= 2 && bw4 * bh4 > 4 && | |
| b->inter_mode != GLOBALMV_GLOBALMV && | |
| f->refdist[b->ref.ref[0]] == -f->refdist[b->ref.ref[1]] && | |
| f->refdist[b->ref.ref[0]] && | |
| !f->svc[b->ref.ref[0]][0].scale && !f->svc[b->ref.ref[1]][0].scale && | |
| (f->frame_hdr->opfl_refine_type != 1 /* switchable */ || | |
| !((1 << b->inter_mode) & ((1 << NEARMV_NEWMV) | | |
| (1 << NEWMV_NEARMV) | | |
| (1 << NEWMV_NEWMV) | | |
| (1 << JOINT_NEWMV))))) | |
| { | |
| if ((1 << coded_mode) & ((1 << NEARMV_NEARMV) | | |
| (1 << OPFL_NEARMV_NEARMV) | | |
| (1 << OPFL_JOINT_NEWMV))) | |
| { | |
| b->refine_mv = 2; // implicitly enabled | |
| } else { | |
| const int ctx = coded_mode - NEARMV_NEARMV; | |
| b->refine_mv = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.refine_mv[ctx]); | |
| DEBUG_BLOCK_printf("%*sPost-refinemv[ctx=%d,%d]: r=%d\n", | |
| depth, "", ctx + 1, b->refine_mv, | |
| ts->msac.rng); | |
| } | |
| } | |
| b->comp_type = COMP_INTER_AVG; | |
| if (coded_mode <= JOINT_NEWMV /* no opfl */ && | |
| b->refine_mv != 1 /* disabled, or implicitly enabled */ && | |
| !(coded_mode == JOINT_NEWMV && b->amvd) && | |
| f->seq_hdr->masked_compound && imin(bw4, bh4) >= 2) | |
| { | |
| const int ffr = f->furthest_future_refidx; | |
| #define comptype_ctx(num) \ | |
| num >= idx ? 0 : nx[num]->ref[1][xoff[num]] != -1 ? \ | |
| nx[num]->comp_type[xoff[num]] > COMP_INTER_AVG : \ | |
| (nx[num]->ref[0][xoff[num]] == ffr) * 2 | |
| const int cctx0 = comptype_ctx(0), cctx1 = comptype_ctx(1); | |
| #undef comptype_ctx | |
| const int ctx = cctx0 + cctx1 + (cctx0 && cctx1) + | |
| (f->absrefdist[b->ref.ref[0]] == f->absrefdist[b->ref.ref[1]]) * 6; | |
| const int has_mask = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.comp_type_masked[ctx]); | |
| if (has_mask) { | |
| b->inter_mode = coded_mode; | |
| if (imax(bw4, bh4) <= 16 && | |
| !dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.comp_type_weighted)) | |
| { | |
| b->comp_type = COMP_INTER_WEDGE; | |
| b->wedge_idx = read_wedge_idx(ts); | |
| b->wedge_sign = dav2d_msac_decode_bool_bypass(&ts->msac); | |
| } else { | |
| b->comp_type = COMP_INTER_SEG; | |
| b->mask_sign = dav2d_msac_decode_bool_bypass(&ts->msac); | |
| } | |
| } | |
| DEBUG_BLOCK_printf("%*sPost-comp_inter_type[ctx=%d,%d,%c=%d|%d]: r=%d\n", | |
| depth, "", ctx, b->comp_type - 1, | |
| "?wm"[b->comp_type - 1], | |
| b->comp_type == COMP_INTER_AVG ? -1 : | |
| b->comp_type == COMP_INTER_WEDGE ? | |
| b->wedge_idx : b->mask_sign, | |
| b->comp_type == COMP_INTER_WEDGE ? | |
| b->wedge_sign : -1, ts->msac.rng); | |
| } | |
| b->cwp_idx = 8; | |
| if (!b->refine_mv && !jmvd_scale_mode && | |
| f->seq_hdr->cwp && b->comp_type == COMP_INTER_AVG && | |
| (coded_mode == NEARMV_NEARMV || coded_mode == JOINT_NEWMV)) | |
| { | |
| int n; | |
| for (n = 0; n < 4; n++) { | |
| if (!dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.cwp_idx[n])) | |
| { | |
| break; | |
| } | |
| } | |
| static const int8_t cwp_weighting_factor[2][5] = { | |
| { 8, 12, 4, 10, 6 }, | |
| { 8, 12, 4, 20, -4 }, | |
| }; | |
| b->cwp_idx = cwp_weighting_factor[!(f->refdir[b->ref.ref[0]] ^ | |
| f->refdir[b->ref.ref[1]])][n]; | |
| if (coded_mode != b->inter_mode && b->cwp_idx != 8) { | |
| assert(f->frame_hdr->opfl_refine_type == 2 /* always */); | |
| b->inter_mode = coded_mode; | |
| b->refine_mv = 0; | |
| } | |
| DEBUG_BLOCK_printf("%*sPost-compweightpred_idx[%d]: r=%d\n", | |
| depth, "", b->cwp_idx, ts->msac.rng); | |
| } | |
| has_subpel_filter = b->inter_mode <= JOINT_NEWMV /* no opfl */ && | |
| !b->refine_mv && b->motion_mode == MM_TRANSLATION && | |
| (b->inter_mode != GLOBALMV_GLOBALMV || imin(bw4, bh4) == 1); | |
| } else { | |
| b->comp_type = COMP_INTER_NONE; | |
| // ref | |
| if ((f->frame_hdr->segmentation.d.globalmv_mask | | |
| f->frame_hdr->segmentation.d.skip_mask) & (1 << b->seg_id)) | |
| { | |
| b->ref.ref[0] = 0; | |
| } else { | |
| if (is_tip) { | |
| b->ref.ref[0] = TIP_FRAME; | |
| b->cwp_idx = dav2d_tip_wts[f->frame_hdr->tip.global_wtd_idx]; | |
| } else { | |
| const int n_refs = f->frame_hdr->n_ref_frames; | |
| int i = 0; | |
| if (n_refs > 1) { | |
| uint8_t cnt[9] = { 0 }; | |
| if (idx > 0) { | |
| cnt[nx[0]->ref[0][xoff[0]] + 1]++; | |
| cnt[nx[0]->ref[1][xoff[0]] + 1]++; | |
| if (idx > 1) { | |
| cnt[nx[1]->ref[0][xoff[1]] + 1]++; | |
| cnt[nx[1]->ref[1][xoff[1]] + 1]++; | |
| } | |
| } | |
| int cnt_rem = idx * 2 - cnt[0] - cnt[8]; | |
| do { | |
| const int cnt_cur = cnt[i + 1]; | |
| cnt_rem -= cnt_cur; | |
| const int ctx = iclip(cnt_cur - cnt_rem + 1, 0, 2); | |
| if (dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.single_ref[ctx][i])) | |
| { | |
| break; | |
| } | |
| } while (++i < n_refs - 1); | |
| } | |
| b->ref.ref[0] = i; | |
| } | |
| DEBUG_BLOCK_printf("%*sPost-ref[%d,-1]: r=%d\n", | |
| depth, "", b->ref.ref[0], ts->msac.rng); | |
| } | |
| b->ref.ref[1] = -1; | |
| const int have_top_right = t->bx + bw4 <= ts->tiling.col_end; | |
| const int have_bottom_left = t->by + bh4 <= ts->tiling.row_end; | |
| const int sngl_ctx = | |
| get_snglref_ctx(t->a, &t->l, by4, bx4, have_top, have_left, | |
| have_top_right, have_bottom_left, b_dim, b->ref.ref[0]); | |
| const int is_sb_boundary = !(t->by & (f->sb_step - 1)); | |
| if ((f->frame_hdr->segmentation.d.globalmv_mask | | |
| f->frame_hdr->segmentation.d.skip_mask) & (1 << b->seg_id)) | |
| { | |
| b->inter_mode = GLOBALMV; | |
| } else if (is_tip) { | |
| b->inter_mode = NEARMV + | |
| 2 * dav2d_msac_decode_bool_adapt(&ts->msac, ts->cdf.m.tip_mode); | |
| DEBUG_BLOCK_printf("%*sPost-tip_mode[%d]: r=%d\n", | |
| depth, "", b->inter_mode, ts->msac.rng); | |
| } else { | |
| int allow_warp = 0; | |
| if (imin(bw4, bh4) >= 2 && f->frame_hdr->warp_motion) { | |
| const int ctx = | |
| get_warp_ctx(t->a, &t->a_sb_cache, | |
| &t->l, by4, bx4, have_top, have_left, | |
| is_sb_boundary ? ((t->bx + bw4 - 2) & ~1) < | |
| ts->tiling.col_end : have_top_right, | |
| have_bottom_left, | |
| is_sb_boundary, b_dim, b->ref.ref[0]); | |
| allow_warp = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.warp[ctx]); | |
| } | |
| if (allow_warp) { | |
| b->inter_mode = !f->frame_hdr->force_integer_mv && | |
| !dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.warp_newmv) ? WARPNEWMV : WARPMV; | |
| } else { | |
| b->inter_mode = NEARMV + | |
| dav2d_msac_decode_symbol_adapt4(&ts->msac, | |
| ts->cdf.m.inter_mode[sngl_ctx], 2); | |
| } | |
| DEBUG_BLOCK_printf("%*sPost-single_inter_mode[ctx=%d,%d]: r=%d\n", | |
| depth, "", sngl_ctx, | |
| b->inter_mode, ts->msac.rng); | |
| } | |
| if (f->seq_hdr->adaptive_mvd && b->inter_mode == NEWMV) { | |
| const int ctx = | |
| (idx > 0 && nx[0]->ref[0][xoff[0]] == b->ref.ref[0] && | |
| nx[0]->amvd[xoff[0]]) + | |
| (idx > 1 && nx[1]->ref[0][xoff[1]] == b->ref.ref[0] && | |
| nx[1]->amvd[xoff[1]]); | |
| b->amvd = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.amvd[4][ctx]); | |
| DEBUG_BLOCK_printf("%*sPost-amvd[ctx=4|%d,%d]: r=%d\n", | |
| depth, "", ctx, b->amvd, ts->msac.rng); | |
| } | |
| b->warp_ref_idx = 0; | |
| b->warpmv_with_mvd = 0; | |
| b->bawp[0] = b->bawp[1] = 0; | |
| if (is_tip) { | |
| /* do nothing */ | |
| } else if (b->inter_mode <= NEWMV) { | |
| // block-adaptive weighted prediction | |
| if (f->frame_hdr->bawp && | |
| b->inter_mode != GLOBALMV && | |
| imin(bw4, bh4) >= 2 && !f->svc[b->ref.ref[0]][0].scale) | |
| { | |
| b->bawp[0] = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.bawp[0]); | |
| if (b->bawp[0]) { | |
| const int ctx = b->inter_mode == NEWMV ? 2 - b->amvd : 0; | |
| b->bawp[0] += dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.bawp_explicit[ctx]); | |
| if (b->bawp[0] == 2) { | |
| b->bawp[0] += dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.bawp_explicit_scale); | |
| b->bawp[0] |= ctx << 2; | |
| } | |
| if (has_chroma) | |
| b->bawp[1] = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.bawp[1]); | |
| } | |
| DEBUG_BLOCK_printf("%*sPost-bawp[%d,%d]: r=%d\n", | |
| depth, "", b->bawp[0] & 3, | |
| b->bawp[1], ts->msac.rng); | |
| } | |
| // inter-intra (motion-mode) | |
| if (f->frame_hdr->motion_modes & (1 << MM_INTERINTRA) && | |
| !b->bawp[0] && bw4 * bh4 > 2 && imax(bw4, bh4) <= 16 && | |
| b->inter_mode >= NEARMV && b->inter_mode <= NEWMV) | |
| { | |
| const int ctx = size_group_lookup[bs]; | |
| if (dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.interintra[ctx])) | |
| { | |
| b->motion_mode = MM_INTERINTRA; | |
| b->interintra_mode = dav2d_msac_decode_symbol_adapt4(&ts->msac, | |
| ts->cdf.m.interintra_mode[ctx], 3); | |
| b->wedge_idx = -1; | |
| if (imin(bw4, bh4) > 1 && | |
| dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.interintra_wedge)) | |
| { | |
| b->wedge_idx = read_wedge_idx(ts); | |
| } | |
| } | |
| DEBUG_BLOCK_printf("%*sPost-interintra[%d,%d,%d]: r=%d\n", | |
| depth, "", b->motion_mode, | |
| b->motion_mode ? b->interintra_mode : -1, | |
| b->motion_mode ? b->wedge_idx : -1, | |
| ts->msac.rng); | |
| } | |
| } else { | |
| // motion mode | |
| b->motion_mode = MM_WARP_DELTA; | |
| if (b->inter_mode == WARPNEWMV) { | |
| const int ref = b->ref.ref[0]; | |
| #define match_ref(dir, off) \ | |
| (t->dir ref[0][off] == ref || t->dir ref[1][off] == ref) | |
| const int has_cs_ext = match_ref(l., by4) || | |
| (t->by + bh4 <= ts->tiling.row_end && | |
| match_ref(l., by4 + bh4 - 1)) || | |
| (is_sb_boundary ? | |
| (match_ref(a_sb_cache., bx4 & ~1) || | |
| (((t->bx + bw4 - 2) & ~1) < ts->tiling.col_end && | |
| match_ref(a_sb_cache., (bx4 + bw4 - 2) & ~1))) : | |
| (match_ref(a->, bx4) || | |
| (t->bx + bw4 <= ts->tiling.col_end && | |
| match_ref(a->, bx4 + bw4 - 1)))); | |
| #undef match_ref | |
| b->motion_mode = MM_WARP_DELTA; | |
| if (has_cs_ext) { | |
| const enum MotionMode x1 = boff[0] == -1 ? MM_TRANSLATION : | |
| nb[0]->motion_mode[boff[0]], | |
| x2 = boff[1] == -1 ? MM_TRANSLATION : | |
| nb[1]->motion_mode[boff[1]]; | |
| const int ext_ctx = (x1 >= MM_WARP_CAUSAL) + | |
| (x2 >= MM_WARP_CAUSAL); | |
| const unsigned mm = f->frame_hdr->motion_modes; | |
| if (mm & (1 << MM_WARP_EXTEND) && | |
| dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.warp_extend[ext_ctx])) | |
| { | |
| b->motion_mode = MM_WARP_EXTEND; | |
| } else if ((mm & (3 << MM_WARP_CAUSAL)) == (3 << MM_WARP_CAUSAL)) { | |
| const int cs_ctx = (ext_ctx > 0) + | |
| (x1 == MM_WARP_CAUSAL) + (x2 == MM_WARP_CAUSAL); | |
| b->motion_mode = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.warp_causal[cs_ctx]) ? | |
| MM_WARP_CAUSAL : MM_WARP_DELTA; | |
| } else { | |
| b->motion_mode = mm & (1 << MM_WARP_CAUSAL) ? | |
| MM_WARP_CAUSAL : MM_WARP_DELTA; | |
| } | |
| DEBUG_BLOCK_printf("%*sPost-sngl_newmv_warp[%d]: r=%d\n", | |
| depth, "", b->motion_mode, ts->msac.rng); | |
| } | |
| } | |
| if (b->motion_mode == MM_WARP_DELTA) { | |
| for (; b->warp_ref_idx < 3; b->warp_ref_idx++) { | |
| if (!dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.warp_ref_idx[b->warp_ref_idx])) | |
| { | |
| break; | |
| } | |
| } | |
| DEBUG_BLOCK_printf("%*sPost-warp_ref_idx[%d/%d]: r=%d\n", | |
| depth, "", b->warp_ref_idx, 4, ts->msac.rng); | |
| } | |
| if (b->inter_mode == WARPMV && b->warp_ref_idx < 2) { | |
| b->warpmv_with_mvd = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.warpmv_with_mvd); | |
| DEBUG_BLOCK_printf("%*sPost-warpmv_with_mvd[%d]: r=%d\n", | |
| depth, "", b->warpmv_with_mvd, ts->msac.rng); | |
| } | |
| } | |
| // drl | |
| b->drl_idx[0] = 0; | |
| if (b->inter_mode != WARPMV && b->inter_mode != GLOBALMV) { | |
| const int max_drl_bits = f->frame_hdr->max_drl_bits; | |
| int n = 0; | |
| for (int ctx = 0; n < max_drl_bits; n++, ctx += ctx < 2) { | |
| if (!dav2d_msac_decode_bool_adapt(&ts->msac, | |
| is_tip ? ts->cdf.m.tip_drl_idx[ctx] : | |
| ts->cdf.m.drl_idx[ctx][sngl_ctx])) | |
| { | |
| break; | |
| } | |
| } | |
| b->drl_idx[0] = n; | |
| DEBUG_BLOCK_printf("%*sPost-drl[%d,-1]: r=%d\n", | |
| depth, "", b->drl_idx[0], ts->msac.rng); | |
| } | |
| // mv precision | |
| b->mv_prec = 3 + f->frame_hdr->mv_precision; | |
| if (b->mv_prec > 3 && !b->amvd && f->seq_hdr->flex_mvres && | |
| (b->inter_mode == NEWMV || b->inter_mode == WARPNEWMV)) | |
| { | |
| const int mvprec1 = boff[0] == -1 ? 0 : nb[0]->mvprec[boff[0]]; | |
| const int mvprec2 = boff[1] == -1 ? 0 : nb[1]->mvprec[boff[1]]; | |
| const int ctx1 = (mvprec1 & 1) + (mvprec2 & 1); | |
| if (!dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.mvprec_def[ctx1])) | |
| { | |
| const int ctx2 = (mvprec1 | mvprec2) >> 1; | |
| const int idx = | |
| dav2d_msac_decode_symbol_adapt4(&ts->msac, | |
| ts->cdf.m.mvprec_rem[ctx2][b->mv_prec - 4], 2); | |
| b->mv_prec = mv_prec_tbl[b->mv_prec == 6][idx]; | |
| mvprec_def = 2; | |
| } | |
| DEBUG_BLOCK_printf("%*sPost-mv_precision[ctx=%d|%d|%d,%d]: r=%d\n", | |
| depth, "", ctx1, | |
| mvprec_def == 1 ? -1 : (mvprec1 | mvprec2) >> 1, | |
| mvprec_def == 1 ? -1 : | |
| f->frame_hdr->mv_precision - 1, | |
| b->mv_prec, ts->msac.rng); | |
| } | |
| if (b->inter_mode == NEWMV || b->inter_mode == WARPNEWMV || | |
| (b->inter_mode == WARPMV && b->warpmv_with_mvd)) | |
| { | |
| int nnzc, nnzc2 = 0, sum_mvd; | |
| if (b->amvd) { | |
| read_amvd(ts, &b->mv[0]); | |
| nnzc = 3; // see comment a few lines down | |
| } else { | |
| read_mv_residual(ts, &ts->cdf.mv, &b->mv[0], b->mv_prec); | |
| nnzc = !!b->mv[0].x + !!b->mv[0].y; | |
| sum_mvd = (b->mv[0].x + b->mv[0].y) >> (6 - b->mv_prec); | |
| if (b->inter_mode == WARPMV || !nnzc || | |
| !f->seq_hdr->mvd_sign_derive || | |
| b->motion_mode != MM_TRANSLATION || | |
| f->frame_hdr->allow_screen_content_tools || | |
| f->frame_hdr->mv_precision == 3 || b->mv_prec >= 5) | |
| { | |
| // this means nnzc2 never reaches nnzc below, so the | |
| // sign-derive condition is never invoked | |
| nnzc = 3; | |
| } | |
| } | |
| if (b->mv[0].y) { | |
| const int s = ++nnzc2 == nnzc ? sum_mvd & 1 : | |
| dav2d_msac_decode_bool_bypass(&ts->msac); | |
| if (s) b->mv[0].y = -b->mv[0].y; | |
| } | |
| if (b->mv[0].x) { | |
| const int s = ++nnzc2 == nnzc ? sum_mvd & 1 : | |
| dav2d_msac_decode_bool_bypass(&ts->msac); | |
| if (s) b->mv[0].x = -b->mv[0].x; | |
| } | |
| DEBUG_BLOCK_printf("%*sPost-mvdiff[%d,y:%d,x:%d]: r=%d\n", | |
| depth, "", 0, b->mv[0].y, b->mv[0].x, | |
| ts->msac.rng); | |
| } | |
| if (b->inter_mode == WARPNEWMV && b->motion_mode == MM_WARP_DELTA && | |
| ((f->seq_hdr->six_param_warp_delta && b->warp_ref_idx == 1) || | |
| b->warp_ref_idx == 0)) | |
| { | |
| const int prec = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.warp_delta_prec[bs]); | |
| const int np = f->seq_hdr->six_param_warp_delta && | |
| b->warp_ref_idx == 1 ? 4 : 2; | |
| const int step = 2 >> prec; | |
| for (int n = 0; n < np; n++) { | |
| const int ctx = n - 1U > 1U; | |
| b->matrix[n] = | |
| dav2d_msac_decode_symbol_adapt8(&ts->msac, | |
| ts->cdf.m.warp_delta_param[0][!ctx], 7); | |
| if (b->matrix[n] == 7 && prec) | |
| b->matrix[n] += | |
| dav2d_msac_decode_symbol_adapt8(&ts->msac, | |
| ts->cdf.m.warp_delta_param[1][!ctx], 7); | |
| if (b->matrix[n]) { | |
| const int sign = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.warp_delta_sign); | |
| if (sign) b->matrix[n] = -b->matrix[n]; | |
| b->matrix[n] *= step; | |
| } | |
| } | |
| if (np == 2) b->matrix[2] = -0x80; // "invalid" | |
| DEBUG_BLOCK_printf("%*sPost-warp_param_signal[%d,%d,%d,%d]: r=%d\n", | |
| depth, "", b->matrix[0] >> !prec, | |
| b->matrix[1] >> !prec, | |
| (np == 4) ? b->matrix[2] >> !prec : 0, | |
| (np == 4) ? b->matrix[3] >> !prec : 0, ts->msac.rng); | |
| } else if (b->motion_mode == MM_WARP_DELTA) { | |
| memset(b->matrix, 0, sizeof(b->matrix)); | |
| } | |
| b->warp_ii = 0; | |
| if (b->inter_mode == WARPMV && | |
| imin(bw4, bh4) >= 2 && imax(bw4, bh4) <= 16) | |
| { | |
| const int ctx = size_group_lookup[bs]; | |
| if (dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.warp_interintra[ctx])) | |
| { | |
| b->warp_ii = 1; | |
| b->interintra_mode = dav2d_msac_decode_symbol_adapt4(&ts->msac, | |
| ts->cdf.m.interintra_mode[ctx], 3); | |
| b->wedge_idx = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.interintra_wedge) ? read_wedge_idx(ts) : -1; | |
| } | |
| DEBUG_BLOCK_printf("%*sPost-warp_ii[%d,%d,%d]: r=%d\n", | |
| depth, "", b->warp_ii, | |
| b->warp_ii ? b->interintra_mode : -1, | |
| b->warp_ii ? b->wedge_idx : -1, ts->msac.rng); | |
| } | |
| has_subpel_filter = !is_tip && b->inter_mode <= NEWMV && | |
| (b->inter_mode != GLOBALMV || imin(bw4, bh4) == 1); | |
| } | |
| // subpel filter | |
| if (b->skip_mode || b->ref.ref[0] == TIP_FRAME || b->refine_mv || | |
| b->inter_mode >= OPFL_NEARMV_NEARMV) | |
| { | |
| assert(!has_subpel_filter); | |
| b->filter = DAV2D_FILTER_8TAP_SHARP; | |
| } else if (f->frame_hdr->subpel_filter_mode == DAV2D_FILTER_SWITCHABLE) { | |
| if (has_subpel_filter) { | |
| const int ctx = get_filter_ctx(nb, boff, b->ref); | |
| b->filter = dav2d_msac_decode_symbol_adapt4(&ts->msac, | |
| ts->cdf.m.filter[ctx], | |
| DAV2D_N_SWITCHABLE_FILTERS - 1); | |
| DEBUG_BLOCK_printf("%*sPost-subpelfilter[ctx=%d,%d]: r=%d\n", | |
| depth, "", ctx, b->filter, ts->msac.rng); | |
| } else b->filter = DAV2D_FILTER_8TAP_REGULAR; | |
| } else { | |
| b->filter = f->frame_hdr->subpel_filter_mode; | |
| } | |
| if (read_tx_part(t, DB_ONLY(depth) b, bs)) return -1; | |
| // reconstruction | |
| if (t->task_thread.pass == PASS_ENTROPY) { | |
| const int res = f->bd_fn.read_coef_blocks(t, DB_ONLY(depth) lbs, cbs, b); | |
| if (res < 0) return res; | |
| } else { | |
| const int res = recon_b(t, DB_ONLY(depth) lbs, cbs, b); | |
| if (res < 0) return res; | |
| } | |
| // context updates | |
| BlockContext *edge = t->a; | |
| for (int i = 0, off = bx4; i < 2; i++, off = by4, edge = &t->l) { | |
| #define set_ctx(rep_macro) \ | |
| rep_macro(edge->seg_pred, off, seg_pred); \ | |
| rep_macro(edge->skip_mode, off, b->skip_mode); \ | |
| rep_macro(edge->intra, off, 0); \ | |
| rep_macro(edge->intrabc, off, 0); \ | |
| rep_macro(edge->morph_pred, off, 0); \ | |
| rep_macro(edge->midx, off, 0xff); \ | |
| rep_macro(edge->fsc, off, 0); \ | |
| rep_macro(edge->skip_txfm, off, b->skip_txfm); \ | |
| rep_macro(edge->pal_sz, off, 0); \ | |
| rep_macro(edge->comp_type, off, b->comp_type); \ | |
| rep_macro(edge->filter, off, b->filter); \ | |
| rep_macro(edge->mode, off, b->inter_mode); \ | |
| rep_macro(edge->mrl, off, 0); \ | |
| rep_macro(edge->multi_mrl, off, 0); \ | |
| rep_macro(edge->dip, off, 0); \ | |
| rep_macro(edge->ref[0], off, ((uint8_t) b->ref.ref[0])); \ | |
| rep_macro(edge->ref[1], off, ((uint8_t) b->ref.ref[1])); \ | |
| rep_macro(edge->motion_mode, off, b->motion_mode); \ | |
| rep_macro(edge->amvd, off, b->amvd); \ | |
| rep_macro(edge->mvprec, off, mvprec_def) | |
| case_set(b_dim[2 + i]); | |
| #undef set_ctx | |
| } | |
| if (has_chroma) { | |
| dav2d_memset_pow2[ulog2(cbw4)](&t->a->uvmode[cbx4], DC_PRED); | |
| dav2d_memset_pow2[ulog2(cbh4)](&t->l.uvmode[cby4], DC_PRED); | |
| } | |
| } | |
| // update contexts | |
| if (f->frame_hdr->segmentation.enabled) { | |
| int seg_id = b->seg_id; | |
| if (has_luma) { | |
| const ptrdiff_t seg_stride = f->b4_stride; | |
| splat2d(&f->cur_segmap[t->by * seg_stride + t->bx], seg_stride, | |
| b_dim[2], bh4, seg_id); | |
| } | |
| if (has_chroma && | |
| (f->frame_hdr->deblock.level_u || f->frame_hdr->deblock.level_v)) | |
| { | |
| const ptrdiff_t seg_stride = f->lf.uv_segmap_stride; | |
| splat2d(&f->lf.segmap_uv[(t->cby >> ss_ver) * seg_stride + | |
| (t->cbx >> ss_hor)], seg_stride, | |
| ulog2(cbw4), cbh4, seg_id); | |
| } | |
| if (f->frame_hdr->segmentation.lossless[seg_id]) { | |
| if (has_luma) { | |
| uint16_t (*const lossless)[4] = &t->lf_mask->lossless_mask_y[by4]; | |
| const uint64_t mask = (~0ULL >> (64 - bw4)) << bx4; | |
| const unsigned mask1 = (unsigned) (mask & 0xffff); | |
| const unsigned mask2 = (unsigned) ((mask >> 16) & 0xffff); | |
| const unsigned mask3 = (unsigned) ((mask >> 32) & 0xffff); | |
| const unsigned mask4 = (unsigned) ((mask >> 48)); | |
| for (int y = 0; y < bh4; y++) { | |
| if (mask1) lossless[y][0] |= mask1; | |
| if (mask2) lossless[y][1] |= mask2; | |
| if (mask3) lossless[y][2] |= mask3; | |
| if (mask4) lossless[y][3] |= mask4; | |
| } | |
| } | |
| if (has_chroma) { | |
| uint16_t (*const lossless)[4] = &t->lf_mask->lossless_mask_uv[cby4]; | |
| const uint64_t mask = (~0ULL >> (64 - cbw4)) << cbx4; | |
| const uint64_t ss_mask = ss_hor ? 0xff : 0xffff; | |
| const unsigned mask1 = (unsigned) (mask & ss_mask); | |
| const unsigned mask2 = (unsigned) ((mask >> (16 >> ss_hor)) & ss_mask); | |
| const unsigned mask3 = (unsigned) ((mask >> (32 >> ss_hor)) & ss_mask); | |
| const unsigned mask4 = (unsigned) ((mask >> (48 >> ss_hor)) & ss_mask); | |
| for (int y = 0; y < cbh4; y++) { | |
| if (mask1) lossless[y][0] |= mask1; | |
| if (mask2) lossless[y][1] |= mask2; | |
| if (mask3) lossless[y][2] |= mask3; | |
| if (mask4) lossless[y][3] |= mask4; | |
| } | |
| } | |
| } | |
| } | |
| if (f->frame_hdr->deblock.level_y[0] || f->frame_hdr->deblock.level_y[1]) { | |
| if (has_luma) { | |
| dav2d_create_db_mask(t->lf_mask->filter_y, b, lbs, t->bx, t->by, | |
| f->bw, f->bh, f->cur.p.p.layout, 0, | |
| &t->a->tx_lpf_y[bx4], &t->l.tx_lpf_y[by4], | |
| f->frame_hdr, f->seq_hdr); | |
| } | |
| if (has_chroma && | |
| (f->frame_hdr->deblock.level_u || f->frame_hdr->deblock.level_v)) | |
| { | |
| dav2d_create_db_mask(t->lf_mask->filter_uv, b, cbs, t->cbx, t->cby, | |
| f->bw, f->bh, f->cur.p.p.layout, 1, | |
| &t->a->tx_lpf_uv[cbx4], &t->l.tx_lpf_uv[cby4], | |
| f->frame_hdr, f->seq_hdr); | |
| } | |
| } | |
| if (has_luma && !b->skip_txfm) { | |
| uint16_t (*noskip_mask)[4] = &t->lf_mask->noskip_mask[by4 >> 1]; | |
| const unsigned mask = (~0U >> imax(0, 32 - bw4)) << (bx4 & 15); | |
| const int bx_idx = (bx4 & 0x30) >> 4; | |
| // the "by64 & 1" handles unaligned blocks. For example, if a block | |
| // at by4=1 with bh4=2 has coefficients, it should set "noskip_mask" | |
| // for both the "by8=0" as well as the "by8=1" rows. | |
| for (int y = 0; y < bh4 + (by4 & 1); y += 2, noskip_mask++) { | |
| (*noskip_mask)[bx_idx] |= mask; | |
| if (bw4 >= 32) { | |
| assert(mask == ~0U); | |
| (*noskip_mask)[bx_idx + 1] = mask; | |
| if (bw4 == 64) | |
| (*noskip_mask)[2] = (*noskip_mask)[3] = mask; | |
| } | |
| } | |
| } | |
| if (f->seq_hdr->sdp && f->cur.p.p.layout != DAV2D_PIXEL_LAYOUT_I400 && | |
| cbs == BS_INVALID) | |
| { | |
| const ptrdiff_t off = (t->by & 15) * 16 + (t->bx & 15); | |
| uint8_t *dirmap = &t->luma_intra_dir_mode_map[off]; | |
| uint8_t *fscmap = &t->luma_fsc_map[off]; | |
| const int bh4_max16 = imin(bh4, 16); | |
| #define set_ctx(rep_macro) \ | |
| for (int y = 0; y < bh4_max16; y++) { \ | |
| rep_macro(dirmap, 0, midx); \ | |
| rep_macro(fscmap, 0, b->fsc); \ | |
| dirmap += 16; \ | |
| fscmap += 16; \ | |
| } | |
| case_set(b_dim[2]); | |
| #undef set_ctx | |
| } | |
| return 0; | |
| } | |
| #if __has_feature(memory_sanitizer) | |
| #include <sanitizer/msan_interface.h> | |
| static int checked_decode_b(Dav2dTaskContext *const t, DB_ONLY(const int depth) | |
| const enum BlockSize lbs, const enum BlockSize cbs) | |
| { | |
| const Dav2dFrameContext *const f = t->f; | |
| const int err = decode_b(t, DB_ONLY(depth) lbs, cbs); | |
| enum BlockSize bs[2] = { lbs, cbs }; | |
| if (err == 0 && t->task_thread.pass & PASS_RECON) | |
| for (int i = 0; i < 2; i++) { | |
| if (bs[i] == BS_INVALID) continue; | |
| const uint8_t *const b_dim = dav2d_block_dimensions[bs[i]]; | |
| const int bw4 = b_dim[0], bh4 = b_dim[1]; | |
| const int w4 = imin(bw4, f->bw - t->bx), h4 = imin(bh4, f->bh - t->by); | |
| const ptrdiff_t stride = f->cur.p.stride[i]; | |
| int bx = i ? t->cbx : t->bx, by = i ? t->cby : t->by; | |
| for (int p = i; p < 1 + i * 2; p++) { | |
| const int ss_ver = p && f->cur.p.p.layout == DAV2D_PIXEL_LAYOUT_I420; | |
| const int ss_hor = p && f->cur.p.p.layout != DAV2D_PIXEL_LAYOUT_I444; | |
| const int width = w4 << (2 - ss_hor + (bw4 == ss_hor)); | |
| const int height = h4 << (2 - ss_ver + (bh4 == ss_ver)); | |
| bx &= ~ss_hor; by &= ~ss_ver; | |
| const uint8_t *data = f->cur.p.data[p] + (by << (2 - ss_ver)) * stride + | |
| (bx << (2 - ss_hor + !!f->seq_hdr->hbd)); | |
| for (int y = 0; y < height; data += stride, y++) { | |
| const size_t line_sz = width << !!f->seq_hdr->hbd; | |
| if (__msan_test_shadow(data, line_sz) != -1) { | |
| fprintf(stderr, "B[%d](%d, %d) w4:%d, h4:%d, row:%d\n", | |
| p, bx, by, w4, h4, y); | |
| __msan_check_mem_is_initialized(data, line_sz); | |
| } | |
| } | |
| } | |
| } | |
| return err; | |
| } | |
| #define decode_b checked_decode_b | |
| #endif /* defined(__has_feature) */ | |
| // dir_ptr is a limited recursive partition tree indicator for (ext)sdp as well | |
| // as sdp/cfl delay limits. | |
| // For partitions with luma, the 24th bit indicates whether the parent partition | |
| // prohibits extsdp in child partitions. | |
| // Also for luma, the lower 24 bits are aggregated as the split-direction of | |
| // the child partitions (1: hor, 2: ver, 3: mixed, -1: none] in bit 0-7, the | |
| // next sub-partition (1: hor, 2: ver, 3: mixed, -1: none) in bit 16-23, and | |
| // the child's partition choices in bit 8-15. | |
| // For chroma, these choices can then be used to infer the partition/direction. | |
| // sdp/cfl delay restrictions can also be calculated using these values. | |
| static inline int sdp_root_split(const Dav2dFrameContext *const f, | |
| const enum BlockSize root_bs) | |
| { | |
| return root_bs == BS_64x64 && f->seq_hdr->sdp && | |
| !(f->frame_hdr->frame_type & 1); | |
| } | |
| static inline int decode_4way(MsacContext *const s, const int ref, | |
| uint16_t *const cdf, int n_bits) | |
| { | |
| assert(n_bits >= 4); | |
| const int bin = dav2d_msac_decode_symbol_adapt4(s, cdf, 3); | |
| const int rem = dav2d_msac_decode_bools_bypass(s, n_bits + bin + !bin - 4); | |
| const int v = (bin ? (1 << (n_bits + bin - 4)) : 0) + rem; | |
| const int n = 1 << n_bits; | |
| return ref * 2 <= n ? inv_recenter(ref, v) : | |
| n - 1 - inv_recenter(n - 1 - ref, v); | |
| } | |
| static int read_wiener_ref(Dav2dTileState *const ts, const int bank_size, | |
| const int bank_idx, int *const exact_match) | |
| { | |
| *exact_match = dav2d_msac_decode_bool_bypass(&ts->msac); | |
| int r; | |
| for (r = 0; r < bank_size - 1; r++) { | |
| const int found = dav2d_msac_decode_bool_bypass(&ts->msac); | |
| if (found) break; | |
| } | |
| return (bank_idx - r) & 3; | |
| } | |
| static void read_wiener_filter(Dav2dTileState *const ts, | |
| int8_t *const filter, | |
| const int8_t *const ref_filter, | |
| const int p) | |
| { | |
| const unsigned *const masks = p ? dav2d_subset_masks_uv : dav2d_subset_masks_y; | |
| const int8_t (*const cf_range)[2] = p ? dav2d_ns_wiener_coef_range_uv : | |
| dav2d_ns_wiener_coef_range_y; | |
| int s; | |
| for (s = 0; s < 3 - !!p; s++) { | |
| const int found = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.wiener_ns_len[!!p]); | |
| if (!found) break; | |
| } | |
| const unsigned mask = masks[s]; | |
| const int asym = p && s && | |
| dav2d_msac_decode_bool_adapt(&ts->msac, ts->cdf.m.wiener_ns_sym); | |
| for (int i = 0, m = mask; i < 16 + !!p * 2; i++, m >>= 1) { | |
| if (!(m & 1)) continue; | |
| filter[i] = decode_4way(&ts->msac, ref_filter[i] - cf_range[i][1], | |
| ts->cdf.m.wiener_ns_cf, cf_range[i][0]) + | |
| cf_range[i][1]; | |
| if (asym && i >= 6) { | |
| filter[i + 1] = filter[i]; | |
| i++; | |
| m >>= 1; | |
| } | |
| } | |
| } | |
| static void read_restoration_info(Dav2dTaskContext *const t, | |
| Av2RestorationUnit *const lr, const int p, | |
| const enum Dav2dRestorationType frame_type) | |
| { | |
| const Dav2dFrameContext *const f = t->f; | |
| Dav2dTileState *const ts = t->ts; | |
| if (frame_type == DAV2D_RESTORATION_SWITCHABLE) { | |
| assert(!p); | |
| if (dav2d_msac_decode_bool_adapt(&ts->msac, ts->cdf.m.rst_switchable[0])) { | |
| lr->type = DAV2D_RESTORATION_NONE; | |
| } else { | |
| const int type = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.rst_switchable[1]); | |
| lr->type = type ? DAV2D_RESTORATION_PC_WIENER : | |
| DAV2D_RESTORATION_NS_WIENER; | |
| } | |
| } else { | |
| assert(!p || frame_type == DAV2D_RESTORATION_NS_WIENER); | |
| uint16_t *const cdf = frame_type == DAV2D_RESTORATION_NS_WIENER ? | |
| ts->cdf.m.rst_ns_wiener : ts->cdf.m.rst_pc_wiener; | |
| const int type = dav2d_msac_decode_bool_adapt(&ts->msac, cdf); | |
| lr->type = type ? frame_type : DAV2D_RESTORATION_NONE; | |
| } | |
| const struct Dav2dNSWienerPlane *const pd = &f->frame_hdr->restoration.p[p].ns; | |
| if (lr->type == DAV2D_RESTORATION_NS_WIENER && !pd->frame_filters_on) { | |
| if (p) { | |
| int exact_match; | |
| struct WienerBankUV *const bank = &ts->wiener_bank_uv[p-1]; | |
| const int r = read_wiener_ref(ts, bank->size, bank->idx, | |
| &exact_match); | |
| int8_t *const filter = lr->ns_filter[0]; | |
| const int8_t *ref_filter = bank->filter[r]; | |
| if (exact_match) { | |
| memcpy(filter, ref_filter, 18); | |
| if (!bank->size) | |
| bank->size = 1; | |
| } else { | |
| read_wiener_filter(ts, filter, ref_filter, p); | |
| const int bidx = bank->idx = (1 + bank->idx) & 3; | |
| memcpy(bank->filter[bidx], filter, sizeof(*filter) * 18); | |
| bank->size += bank->size < 4; | |
| } | |
| } else { | |
| struct WienerBankY *const bank = &ts->wiener_bank_y; | |
| const int n_classes = pd->num_classes; | |
| unsigned exact_match_mask = 0; | |
| uint8_t bank_refs[16]; | |
| for (int n = 0, mask = 1; n < n_classes; n++, mask <<= 1) { | |
| int exact_match; | |
| bank_refs[n] = read_wiener_ref(ts, bank->size[n], | |
| bank->idx[n], | |
| &exact_match); | |
| exact_match_mask |= mask * exact_match; | |
| } | |
| for (int n = 0; n < n_classes; n++, exact_match_mask >>= 1) { | |
| const int r = bank_refs[n]; | |
| int8_t *const filter = lr->ns_filter[n]; | |
| const int8_t *ref_filter = bank->filter[r][n]; | |
| if (exact_match_mask & 1) { | |
| memcpy(filter, ref_filter, 16); | |
| if (!bank->size[n]) | |
| bank->size[n] = 1; | |
| continue; | |
| } | |
| read_wiener_filter(ts, filter, ref_filter, p); | |
| const int bidx = bank->idx[n] = (1 + bank->idx[n]) & 3; | |
| memcpy(bank->filter[bidx][n], filter, sizeof(*filter) * 16); | |
| bank->size[n] += bank->size[n] < 4; | |
| } | |
| } | |
| } | |
| } | |
| // Read the loop restoration coefficients of planes [p_start, p_end) for | |
| // the superblock at t->by/t->bx. | |
| static void read_sb_restoration(Dav2dTaskContext *const t, | |
| const int p_start, const int p_end) | |
| { | |
| const Dav2dFrameContext *const f = t->f; | |
| Dav2dTileState *const ts = t->ts; | |
| const int sbsz = f->sb_step * 4; | |
| for (int p = p_start; p < p_end; p++) { | |
| const int ss_ver = p && f->ss_ver, ss_hor = p && f->ss_hor; | |
| if (f->frame_hdr->restoration.p[p].type == DAV2D_RESTORATION_NONE) | |
| continue; | |
| const int tx = 4 * (t->bx - ts->tiling.col_start) >> ss_hor; | |
| const int ty = 4 * (t->by - ts->tiling.row_start) >> ss_ver; | |
| const int unit_sz_log2 = f->frame_hdr->restoration.unit_size[!!p]; | |
| const int unit_sz = 1 << unit_sz_log2; | |
| const unsigned mask = unit_sz - 1; | |
| if ((tx | ty) & mask) continue; | |
| const int tw = ts->tiling.col_end * 4 >> ss_hor; | |
| const int th = ts->tiling.row_end * 4 >> ss_ver; | |
| const int half_unit = unit_sz >> 1; | |
| // Round half up at frame boundaries, if there's more than one | |
| // restoration unit | |
| const int fx = 4 * t->bx >> ss_hor, fy = t->by * 4 >> ss_ver; | |
| if ((ty && fy + half_unit > th) || (tx && fx + half_unit > tw)) | |
| continue; | |
| const enum Dav2dRestorationType frame_type = f->frame_hdr->restoration.p[p].type; | |
| // FIXME many of these values can be pre-calculated at frame-level | |
| const int sbh = sbsz >> ss_ver; | |
| const int lruh = imax(1, imin(th - fy + half_unit, sbh) >> unit_sz_log2); | |
| // the minimum unit size is such that superblocks are never more than | |
| // one unit wide; only 4:2:2 chroma can have two units (vertically), | |
| // e.g. 64x64 chroma units in 128x128 superblocks | |
| assert(lruh == 1 || (lruh == 2 && ss_hor && !ss_ver)); | |
| const int ux = t->bx * 4; | |
| // units are stored by the luma position of their top-left corner, in | |
| // the 256x256 area containing it, as one of its 4x4 64x64 blocks. | |
| // TODO: store restoration data sequentially instead | |
| for (int y = 0; y < lruh; y++) { | |
| const int uy = t->by * 4 + (y << (unit_sz_log2 + ss_ver)); | |
| Av2RestorationUnit *const lr = | |
| &f->lf.lr_mask[(uy >> 8) * f->sb256w + (ux >> 8)] | |
| .lr[p][((uy >> 4) & 0xc) + ((ux >> 6) & 3)]; | |
| read_restoration_info(t, lr, p, frame_type); | |
| DEBUG_BLOCK_printf("Post-restoration[p=%d,type=%d]: r=%d\n", | |
| p, lr->type, ts->msac.rng); | |
| } | |
| } | |
| } | |
| static int decode_sb(Dav2dTaskContext *const t, DB_ONLY(const int depth) | |
| const enum BlockSize lbs, enum BlockSize cbs, | |
| int *const dir_ptr) | |
| { | |
| const enum BlockSize bs = lbs == BS_INVALID ? cbs : lbs; | |
| assert(bs != BS_INVALID); | |
| const Dav2dFrameContext *const f = t->f; | |
| Dav2dTileState *const ts = t->ts; | |
| const uint8_t *const b_dim = dav2d_block_dimensions[bs]; | |
| const int bw4 = b_dim[0], bh4 = b_dim[1]; | |
| const int hw4 = bw4 >> 1, hh4 = bh4 >> 1; | |
| const int qw4 = hw4 >> 1, qh4 = hh4 >> 1; | |
| const int have_h_split = f->bw > t->bx + hw4; | |
| const int have_v_split = f->bh > t->by + hh4; | |
| const enum BlockSize cbs_orig = cbs; | |
| const int ss_hor = f->seq_hdr->ss_hor, ss_ver = f->seq_hdr->ss_ver; | |
| // key/intraonly frames always apply SDP at the 64x64 boundary | |
| if (lbs == BS_64x64 && cbs == BS_64x64 && | |
| f->seq_hdr->sdp && !(f->frame_hdr->frame_type & 1)) | |
| { | |
| int dir = 0; | |
| if (decode_sb(t, DB_ONLY(depth) lbs, BS_INVALID, &dir)) return -1; | |
| if (sdp_root_split(f, f->root_bs) && | |
| (t->task_thread.pass & PASS_ENTROPY)) | |
| { | |
| read_sb_restoration(t, 1, 3); | |
| } | |
| return decode_sb(t, DB_ONLY(depth) BS_INVALID, cbs, &dir); | |
| } | |
| DEBUG_BLOCK_printf("%*sdecode_sb[y=%d,x=%d,bs=%dx%d,plane=%s]: r=%d\n", | |
| depth - 1, "", t->by, t->bx, bw4 * 4, bh4 * 4, | |
| cbs == BS_INVALID ? "y" : lbs == BS_INVALID ? "uv" : "yuv", | |
| ts->msac.rng); | |
| static const struct PartitionConstants { | |
| // FIXME part[0][split] and part[1][split] are identical, maybe | |
| // we can save a byte by merging these together | |
| int8_t part[2 /* h, v */][4 /* half, quarter, eighth, split */]; | |
| int8_t ctx[2 /* _, direction */]; | |
| } subb[] = { | |
| [BS_256x256] = { | |
| { { BS_256x128, -1, -1, BS_128x128 }, | |
| { BS_128x256, -1, -1, BS_128x128 } }, | |
| { 9, 12 }, | |
| }, [BS_256x128] = { | |
| { { -1, -1, -1, -1 }, | |
| { BS_128x128, -1, -1, -1 } }, | |
| { 8, -1 /* 11 */ }, | |
| }, [BS_128x256] = { | |
| { { BS_128x128, -1, -1, -1 }, | |
| { -1, -1, -1, -1 } }, | |
| { 7, -1 /* 10 */ }, | |
| }, [BS_128x128] = { | |
| { { BS_128x64, -1, -1, BS_64x64 }, | |
| { BS_64x128, -1, -1, BS_64x64 } }, | |
| { 6, 9 }, | |
| }, [BS_128x64] = { | |
| { { -1, -1, -1, -1 }, | |
| { BS_64x64, -1, -1, -1 } }, | |
| { 5, -1 /* 8 */ }, | |
| }, [BS_64x128] = { | |
| { { BS_64x64, -1, -1, -1 }, | |
| { -1, -1, -1, -1 } }, | |
| { 4, -1 /* 7 */ }, | |
| }, [BS_64x64] = { | |
| { { BS_64x32, BS_64x16, BS_64x8, BS_32x32 }, | |
| { BS_32x64, BS_16x64, BS_8x64, BS_32x32 } }, | |
| { 3, 6 }, | |
| }, [BS_64x32] = { | |
| { { BS_64x16, BS_64x8, BS_64x4, BS_32x16 }, | |
| { BS_32x32, BS_16x32, BS_8x32, BS_32x16 } }, | |
| { 3, 5 }, | |
| }, [BS_64x16] = { | |
| { { BS_64x8, BS_64x4, -1, BS_32x8 }, | |
| { BS_32x16, BS_16x16, BS_8x16, BS_32x8 } }, | |
| { 15, 14 }, | |
| }, [BS_64x8] = { | |
| { { -1, -1, -1, -1 }, | |
| { -1, -1, -1, -1 } }, | |
| { 0, 0 }, | |
| }, [BS_64x4] = { | |
| { { -1, -1, -1, -1 }, | |
| { -1, -1, -1, -1 } }, | |
| { 0, -1 }, | |
| }, [BS_32x64] = { | |
| { { BS_32x32, BS_32x16, BS_32x8, BS_16x32 }, | |
| { BS_16x64, BS_8x64, BS_4x64, BS_16x32 } }, | |
| { 3, 4 }, | |
| }, [BS_32x32] = { | |
| { { BS_32x16, BS_32x8, BS_32x4, BS_16x16 }, | |
| { BS_16x32, BS_8x32, BS_4x32, BS_16x16 } }, | |
| { 2, 3 }, | |
| }, [BS_32x16] = { | |
| { { BS_32x8, BS_32x4, -1, BS_16x8 }, | |
| { BS_16x16, BS_8x16, BS_4x16, BS_16x8 } }, | |
| { 2, 2 }, | |
| }, [BS_32x8] = { | |
| { { BS_32x4, -1, -1, -1 }, | |
| { BS_16x8, BS_8x8, BS_4x8, BS_16x4 } }, | |
| { 13, 14 }, | |
| }, [BS_32x4] = { | |
| { { -1, -1, -1, -1 }, | |
| { -1, -1, -1, -1 } }, | |
| { 0, -1 }, | |
| }, [BS_16x64] = { | |
| { { BS_16x32, BS_16x16, BS_16x8, BS_8x32 }, | |
| { BS_8x64, BS_4x64, -1, BS_8x32 } }, | |
| { 14, 13 }, | |
| }, [BS_16x32] = { | |
| { { BS_16x16, BS_16x8, BS_16x4, BS_8x16 }, | |
| { BS_8x32, BS_4x32, -1, BS_8x16 } }, | |
| { 2, 1 }, | |
| }, [BS_16x16] = { | |
| { { BS_16x8, BS_16x4, -1, BS_8x8 }, | |
| { BS_8x16, BS_4x16, -1, BS_8x8 } }, | |
| { 1, 0 }, | |
| }, [BS_16x8] = { | |
| { { BS_16x4, -1, -1, -1 }, | |
| { BS_8x8, BS_4x8, -1, BS_8x4 } }, | |
| { 1, 2 }, | |
| }, [BS_16x4] = { | |
| { { -1, -1, -1, -1 }, | |
| { BS_8x4, -1, -1, -1 } }, | |
| { 11, -1 }, | |
| }, [BS_8x64] = { | |
| { { -1, -1, -1, -1 }, | |
| { -1, -1, -1, -1 } }, | |
| { 0, 0 }, | |
| }, [BS_8x32] = { | |
| { { BS_8x16, BS_8x8, BS_8x4, BS_4x16 }, | |
| { BS_4x32, -1, -1, -1 } }, | |
| { 12, 13 }, | |
| }, [BS_8x16] = { | |
| { { BS_8x8, BS_8x4, -1, BS_4x8 }, | |
| { BS_4x16, -1, -1, -1 } }, | |
| { 1, 1 }, | |
| }, [BS_8x8] = { | |
| { { BS_8x4, -1, -1, -1 }, | |
| { BS_4x8, -1, -1, -1 } }, | |
| { 0, 0 }, | |
| }, [BS_8x4] = { | |
| { { -1, -1, -1, -1 }, | |
| { BS_4x4, -1, -1, -1 } }, | |
| { 0, -1 }, | |
| }, [BS_4x64] = { | |
| { { -1, -1, -1, -1 }, | |
| { -1, -1, -1, -1 } }, | |
| { 0, -1 }, | |
| }, [BS_4x32] = { | |
| { { -1, -1, -1, -1 }, | |
| { -1, -1, -1, -1 } }, | |
| { 0, -1 }, | |
| }, [BS_4x16] = { | |
| { { BS_4x8, -1, -1, -1 }, | |
| { -1, -1, -1, -1 } }, | |
| { 10, -1 }, | |
| }, [BS_4x8] = { | |
| { { BS_4x4, -1, -1, -1 }, | |
| { -1, -1, -1, -1 } }, | |
| { 0, -1 }, | |
| }, [BS_4x4] = { | |
| { { -1, -1, -1, -1 }, | |
| { -1, -1, -1, -1 } }, | |
| { -1, -1 }, | |
| }, | |
| }; | |
| const int pl = lbs == BS_INVALID; | |
| const struct PartitionConstants *const pcc = &subb[bs]; | |
| enum BlockPartition bp = PARTITION_INVALID; | |
| int bx4, by4; | |
| if (t->task_thread.pass & PASS_ENTROPY) { | |
| bx4 = t->bx & 63; | |
| by4 = t->by & 63; | |
| // FIXME some of the code below needs to be tested for 4:2:2 w/ SDP=1 | |
| const int eff_ss_ver = ss_ver & (lbs == BS_INVALID); | |
| const int eff_ss_hor = ss_hor & (lbs == BS_INVALID); | |
| const int bwh4ss[2] = { bw4 >> eff_ss_hor, bh4 >> eff_ss_ver }; | |
| assert(bwh4ss[0] >= 1 && bwh4ss[1] >= 1); | |
| int dir = -1; | |
| const int mix_inter = IS_INTER_OR_SWITCH(f->frame_hdr) && | |
| !t->intra_region; | |
| if (imax(bwh4ss[0], bwh4ss[1]) == 1 || | |
| // 1:8/1:16 partitions don't recursive (normatively) | |
| (pcc->part[0][0] & pcc->part[1][0]) == -1 || | |
| // cannot split 4x8/8x4 blocks in mixed-intra/inter regions of | |
| // inter frames, since 4x4 block sizes in such regions are invalid | |
| (mix_inter && b_dim[2] + b_dim[3] == 1)) | |
| { | |
| bp = PARTITION_NONE; | |
| } else if (!have_h_split || !have_v_split) { | |
| if (bw4 == bh4) { | |
| dir = have_v_split; | |
| bp = !have_v_split ? PARTITION_H : PARTITION_V; | |
| } else if (bw4 > bh4) { | |
| if (!have_h_split || f->bh <= t->by + qh4) { | |
| dir = 1; | |
| bp = PARTITION_V; | |
| } | |
| } else if (bh4 > bw4) { | |
| if (!have_v_split || f->bw <= t->bx + qw4) { | |
| dir = 0; | |
| bp = PARTITION_H; | |
| } | |
| } | |
| } | |
| if (bp == PARTITION_INVALID) { | |
| #if DEBUG_BLOCK_INFO | |
| if (0 && bs == f->root_bs) | |
| printf("poc=%d,y=%d,x=%d,bs=%d,r=%d\n", | |
| f->frame_hdr->frame_offset, t->by, t->bx, bs, ts->msac.rng); | |
| #endif | |
| if (cbs == BS_64x64 && lbs == BS_INVALID && | |
| ((*dir_ptr & 0xff) == 0xff || (*dir_ptr & 0x30003) == 0x10002 || | |
| (*dir_ptr & 0x30003) == 0x20001)) | |
| { | |
| // F164: infer SDP chroma partitioning at 64x64 level | |
| if ((*dir_ptr & 0xff) == 0xff) { | |
| bp = PARTITION_NONE; // if luma did not split, don't split chroma | |
| } else { | |
| // if luma split one way, and all children split another way, | |
| // then copy the initial first-way split for chroma | |
| dir = (*dir_ptr & 0x30003) == 0x10002; | |
| bp = (*dir_ptr >> 8) & 0xff; | |
| } | |
| } else { | |
| const int aspect = 1 << f->seq_hdr->max_pb_aspect_ratio_log2; | |
| const int max_bwh4 = imax(bw4, bh4), min_bwh4 = imin(bw4, bh4); | |
| const int aspect_req_split = max_bwh4 > aspect * min_bwh4; | |
| const int ctx1 = get_partition_ctx(t->a, &t->l, b_dim, pl, by4, bx4); | |
| const int ctx2 = ctx1 + pcc->ctx[0] * 4; | |
| const int is_split = | |
| !have_h_split || !have_v_split || aspect_req_split || | |
| dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.part_split[pl][ctx2]); | |
| if (!is_split) { | |
| bp = PARTITION_NONE; | |
| } else { | |
| if ((bs == BS_128x128 || bs == BS_256x256) && | |
| have_v_split && have_h_split) | |
| { | |
| assert(lbs == cbs || f->cur.p.p.layout == DAV2D_PIXEL_LAYOUT_I400); | |
| const int ctx3 = ctx1 + (bs == BS_256x256) * 4; | |
| const int is_square = | |
| dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.part_square[ctx3]); | |
| if (is_square) | |
| bp = PARTITION_SPLIT; | |
| } else if (imax(bw4, bh4) >= 32) { | |
| assert(lbs == cbs || f->cur.p.p.layout == DAV2D_PIXEL_LAYOUT_I400); | |
| assert(bw4 != bh4); | |
| bp = bw4 > bh4 ? PARTITION_V : PARTITION_H; | |
| } | |
| if (bp == PARTITION_INVALID) { | |
| // split - find direction | |
| const int aspect_isn8 = aspect != 8; | |
| if (imin(bwh4ss[0], bwh4ss[1]) == 1) { | |
| dir = bwh4ss[0] > bwh4ss[1]; | |
| } else if (aspect_req_split) { | |
| assert(bw4 != bh4); | |
| dir = bw4 > bh4; | |
| } else { | |
| const int aspect_req_dir = | |
| min_bwh4 * 8 < max_bwh4 * 2 + aspect_isn8; | |
| if (aspect_req_dir) { | |
| dir = bw4 > bh4; | |
| } else { | |
| const int ctx4 = ctx1 + pcc->ctx[1] * 4; | |
| dir = dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.part_dir[pl][ctx4]); | |
| } | |
| } | |
| assert(pcc->part[dir][0] != -1); | |
| bp = dir ? PARTITION_V : PARTITION_H; | |
| if (imax(bw4, bh4) <= 16) { | |
| // v3/h3 [ext-partition] | |
| // FIXME do we need to keep track of mix_inter and | |
| // resulting block sizes here to ensure we don't | |
| // get 4x4 blocks? | |
| const int bwh4ss2[2] = { bw4 >> f->ss_hor, | |
| bh4 >> f->ss_ver }; | |
| const int has_hv3 = f->seq_hdr->ext_partitions && | |
| bwh4ss[!dir] >= 4 && bwh4ss[dir] >= 2 && | |
| b_dim[!dir] * 8 >= b_dim[dir] * 4 + aspect_isn8 && | |
| (cbs != lbs || (bwh4ss2[!dir] >= 4 && | |
| bwh4ss2[dir] >= 2) || (dir ? | |
| (lbs == BS_32x8 ? have_v_split : | |
| t->bx + qw4 * 3 < f->bw) : | |
| (lbs == BS_8x32 ? have_h_split : | |
| t->by + qh4 * 3 < f->bh))); | |
| const int has_hv4ab = bwh4ss[!dir] >= 8 && | |
| f->seq_hdr->uneven_4way_partitions && | |
| b_dim[!dir] * 8 >= b_dim[dir] * 8 + aspect_isn8 && | |
| (cbs != lbs || bwh4ss2[!dir] >= 8 || (dir ? | |
| (t->bx + (qw4 >> 1) * 7 < f->bw) : | |
| (t->by + (qh4 >> 1) * 7 < f->bh))); | |
| if (has_hv3 || has_hv4ab) { | |
| assert(pcc->part[dir][1] != -1); | |
| const int ctx5 = | |
| get_partition2_ctx(t->a, &t->l, b_dim, | |
| pl, dir, by4, bx4); | |
| const int ctx6 = ctx5 + pcc->ctx[0] * 4; | |
| const int is_ext = | |
| dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.part_ext[pl][ctx6]); | |
| if (is_ext) { | |
| bp = dir ? PARTITION_V3 : PARTITION_H3; | |
| if (has_hv4ab) { | |
| assert(pcc->part[dir][2] != -1); | |
| const int is_4way = !has_hv3 || | |
| dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.part_4way[pl][ctx6]); | |
| if (is_4way) { | |
| const int is_a_or_b = | |
| dav2d_msac_decode_bool_bypass(&ts->msac); | |
| bp = PARTITION_H4A + dir * 2 + is_a_or_b; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| #if DEBUG_BLOCK_INFO | |
| static const char *const names[] = { | |
| [PARTITION_NONE]="none", | |
| [PARTITION_H4A]="h4a", | |
| [PARTITION_H4B]="h4b", | |
| [PARTITION_V4A]="v4a", | |
| [PARTITION_V4B]="v4b", | |
| [PARTITION_SPLIT]="s", | |
| [PARTITION_H]="h", | |
| [PARTITION_V]="v", | |
| [PARTITION_H3]="h3", | |
| [PARTITION_V3]="v3", | |
| }; | |
| DEBUG_BLOCK_printf("%*sread_partition[y=%d,x=%d,bs=%dx%d,bp=%d|%s]: r=%d\n", | |
| depth, "", t->by, t->bx, 4 * bw4, 4 * bh4, bp, | |
| names[bp], ts->msac.rng); | |
| #endif | |
| dir += dir != -1; // -1 for PARTITION_NONE, 1 or 2 for hor/ver | |
| // F157 "limit SDP-imposed CfL delay" | |
| if (lbs == BS_INVALID && cbs == BS_64x64) | |
| t->sdp_cfl_disallowed = dir != -1 && dir != (*dir_ptr & 0x3); | |
| *dir_ptr |= (uint8_t) dir | (bp << 8); | |
| int unmix_bit = 0; | |
| if (IS_INTER_OR_SWITCH(f->frame_hdr) && f->seq_hdr->ext_sdp && | |
| (cbs | lbs) != BS_INVALID && bp != PARTITION_NONE && | |
| !(*dir_ptr & (1 << 24)) && // parent partition limits recursive extsdp | |
| bp < PARTITION_H4A && imin(bw4, bh4) >= 2 && | |
| bs != f->root_bs && imax(bw4, bh4) <= 16) | |
| { | |
| const int sz = b_dim[2] + b_dim[3]; | |
| const int ctx = iclip(sz - 4, 0, 3) + (sz == 4); | |
| t->intra_region = unmix_bit = | |
| !dav2d_msac_decode_bool_adapt(&ts->msac, | |
| ts->cdf.m.region_type[ctx]); | |
| DEBUG_BLOCK_printf("%*sis_mixed_region[ctx=%d,%d]: r=%d\n", | |
| depth, "", ctx, !t->intra_region, ts->msac.rng); | |
| if (t->intra_region) cbs = BS_INVALID; | |
| } | |
| if (f->c->task_thread.n_passes > 1) | |
| *ts->frame_thread[0].partition[0]++ = bp | (unmix_bit << 7); | |
| } else { | |
| bp = *ts->frame_thread[1].partition[t->task_thread.pass == PASS_MVRES]++; | |
| if (bp & 0x80) { | |
| assert(!t->intra_region); | |
| t->intra_region = 1; | |
| cbs = BS_INVALID; | |
| bp &= 0x7f; | |
| } | |
| } | |
| if (bs == cbs) { | |
| t->cbx = t->bx; | |
| t->cby = t->by; | |
| } | |
| // can child partitions split? | |
| static const uint8_t lim[][2] = { | |
| [PARTITION_NONE] = { 1, 1 }, | |
| [PARTITION_H] = { 1, 2 }, | |
| [PARTITION_V] = { 2, 1 }, | |
| [PARTITION_H3] = { 2, 4 }, | |
| [PARTITION_V3] = { 4, 2 }, | |
| [PARTITION_H4A] = { 1, 8 }, | |
| [PARTITION_H4B] = { 1, 8 }, | |
| [PARTITION_V4A] = { 8, 1 }, | |
| [PARTITION_V4B] = { 8, 1 }, | |
| [PARTITION_SPLIT] = { 2, 2 }, | |
| }; | |
| const uint8_t *const l = lim[bp]; | |
| int child_dir = (bw4 <= l[0] || bh4 <= l[1]) << 24; | |
| switch (bp) { | |
| case PARTITION_NONE: | |
| if (decode_b(t, DB_ONLY(depth + 1) lbs, cbs)) return -1; | |
| if (t->task_thread.pass & PASS_ENTROPY) { | |
| if ((cbs | lbs) != BS_INVALID) { | |
| BlockContext *edge = t->a; | |
| #define set_ctx(rep_macro) \ | |
| rep_macro(edge->partition[0], off, (uint8_t) ~(b_dim[i] - 1)); \ | |
| rep_macro(edge->partition[1], off, (uint8_t) ~(b_dim[i] - 1)) | |
| for (int i = 0, off = bx4; i < 2; i++, off = by4, edge = &t->l) { | |
| case_set(b_dim[2 + i]); | |
| } | |
| #undef set_ctx | |
| } else { | |
| dav2d_memset_pow2[b_dim[2]](&t->a->partition[pl][bx4], | |
| (uint8_t) ~(b_dim[0] - 1)); | |
| dav2d_memset_pow2[b_dim[3]](&t->l.partition[pl][by4], | |
| (uint8_t) ~(b_dim[1] - 1)); | |
| } | |
| } | |
| break; | |
| case PARTITION_V: { | |
| assert(hw4 > 0); | |
| const int sub4 = bs == cbs && (hw4 >> ss_hor) > 0; | |
| assert(sub4 || !pl); | |
| if (decode_sb(t, DB_ONLY(depth + 1) | |
| pl ? BS_INVALID : pcc->part[1][0], | |
| sub4 ? pcc->part[1][0] : BS_INVALID, &child_dir)) | |
| { | |
| return -1; | |
| } | |
| if (t->bx + hw4 >= f->bw) break; | |
| t->bx += hw4; | |
| if (decode_sb(t, DB_ONLY(depth + 1) | |
| pl ? BS_INVALID : pcc->part[1][0], | |
| sub4 ? pcc->part[1][0] : cbs, &child_dir)) | |
| { | |
| return -1; | |
| } | |
| t->bx -= hw4; | |
| break; | |
| } | |
| case PARTITION_H: { | |
| assert(hh4 > 0); | |
| const int sub4 = bs == cbs && (hh4 >> ss_ver) > 0; | |
| assert(sub4 || !pl); | |
| if (decode_sb(t, DB_ONLY(depth + 1) | |
| pl ? BS_INVALID : pcc->part[0][0], | |
| sub4 ? pcc->part[0][0] : BS_INVALID, &child_dir)) | |
| { | |
| return -1; | |
| } | |
| if (t->by + hh4 >= f->bh) break; | |
| t->by += hh4; | |
| if (decode_sb(t, DB_ONLY(depth + 1) | |
| pl ? BS_INVALID : pcc->part[0][0], | |
| sub4 ? pcc->part[0][0] : cbs, &child_dir)) | |
| { | |
| return -1; | |
| } | |
| t->by -= hh4; | |
| break; | |
| } | |
| case PARTITION_SPLIT: { | |
| assert(have_v_split && have_h_split && cbs == lbs); | |
| const enum BlockSize sbs = pcc->part[0][3]; | |
| if (decode_sb(t, DB_ONLY(depth + 1) sbs, sbs, &child_dir)) return -1; | |
| t->bx += hw4; | |
| if (decode_sb(t, DB_ONLY(depth + 1) sbs, sbs, &child_dir)) return -1; | |
| t->bx -= hw4; | |
| t->by += hh4; | |
| if (decode_sb(t, DB_ONLY(depth + 1) sbs, sbs, &child_dir)) return -1; | |
| t->bx += hw4; | |
| if (decode_sb(t, DB_ONLY(depth + 1) sbs, sbs, &child_dir)) return -1; | |
| t->bx -= hw4; | |
| t->by -= hh4; | |
| break; | |
| } | |
| case PARTITION_V3: { | |
| assert(qw4 > 0 && hh4 > 0); | |
| const int sub4 = bs == cbs && (qw4 >> ss_hor) > 0 && | |
| (hh4 >> ss_ver) > 0; | |
| assert(sub4 || !pl); | |
| const int i_3only = cbs == BS_INVALID || (!sub4 && bs != BS_32x8); | |
| if (decode_sb(t, DB_ONLY(depth + 1) | |
| pl ? BS_INVALID : pcc->part[1][1], | |
| i_3only ? BS_INVALID : pcc->part[1][1], &child_dir)) | |
| { | |
| return -1; | |
| } | |
| if (t->bx + qw4 >= f->bw) break; | |
| t->bx += qw4; | |
| if (!i_3only) t->cbx = t->bx; | |
| if (decode_sb(t, DB_ONLY(depth + 1) | |
| pl ? BS_INVALID : pcc->part[1][3], | |
| sub4 ? pcc->part[1][3] : BS_INVALID, &child_dir)) | |
| { | |
| return -1; | |
| } | |
| if (t->by + hh4 < f->bh) { | |
| t->by += hh4; | |
| if (decode_sb(t, DB_ONLY(depth + 1) | |
| pl ? BS_INVALID : pcc->part[1][3], | |
| i_3only ? BS_INVALID : pcc->part[1][sub4 * 3], &child_dir)) | |
| { | |
| return -1; | |
| } | |
| t->by -= hh4; | |
| } | |
| if (t->bx + hw4 >= f->bw) { t->bx -= qw4; break; } | |
| t->bx += hw4; | |
| if (decode_sb(t, DB_ONLY(depth + 1) | |
| pl ? BS_INVALID : pcc->part[1][1], | |
| i_3only ? cbs : pcc->part[1][1], &child_dir)) | |
| { | |
| return -1; | |
| } | |
| t->bx -= 3 * qw4; | |
| break; | |
| } | |
| case PARTITION_H3: { | |
| assert(qh4 > 0 && hw4 > 0); | |
| const int sub4 = bs == cbs && (qh4 >> ss_ver) > 0 && | |
| (hw4 >> ss_hor) > 0; | |
| assert(sub4 || !pl); | |
| const int i_3only = cbs == BS_INVALID || (!sub4 && bs != BS_8x32); | |
| if (decode_sb(t, DB_ONLY(depth + 1) | |
| pl ? BS_INVALID : pcc->part[0][1], | |
| i_3only ? BS_INVALID : pcc->part[0][1], &child_dir)) | |
| { | |
| return -1; | |
| } | |
| if (t->by + qh4 >= f->bh) break; | |
| t->by += qh4; | |
| if (!i_3only) t->cby = t->by; | |
| if (decode_sb(t, DB_ONLY(depth + 1) | |
| pl ? BS_INVALID : pcc->part[0][3], | |
| sub4 ? pcc->part[0][3] : BS_INVALID, &child_dir)) | |
| { | |
| return -1; | |
| } | |
| if (t->bx + hw4 < f->bw) { | |
| t->bx += hw4; | |
| if (decode_sb(t, DB_ONLY(depth + 1) | |
| pl ? BS_INVALID : pcc->part[0][3], | |
| i_3only ? BS_INVALID : pcc->part[0][sub4 * 3], &child_dir)) | |
| { | |
| return -1; | |
| } | |
| t->bx -= hw4; | |
| } | |
| if (t->by + hh4 >= f->bh) { t->by -= qh4; break; } | |
| t->by += hh4; | |
| if (decode_sb(t, DB_ONLY(depth + 1) | |
| pl ? BS_INVALID : pcc->part[0][1], | |
| i_3only ? cbs : pcc->part[0][1], &child_dir)) | |
| { | |
| return -1; | |
| } | |
| t->by -= 3 * qh4; | |
| break; | |
| } | |
| case PARTITION_V4A: | |
| case PARTITION_V4B: { | |
| const int ew4 = qw4 >> 1; | |
| assert(ew4 > 0); | |
| const int sub4 = bs == cbs && (ew4 >> ss_hor) > 0; | |
| assert(sub4 || !pl); | |
| if (decode_sb(t, DB_ONLY(depth + 1) | |
| pl ? BS_INVALID : pcc->part[1][2], | |
| sub4 ? pcc->part[1][2] : BS_INVALID, &child_dir)) | |
| { | |
| return -1; | |
| } | |
| if (t->bx + ew4 >= f->bw) break; | |
| t->bx += ew4; | |
| const int var = bp - PARTITION_V4A; // v4b: 1, v4a: 0 | |
| if (decode_sb(t, DB_ONLY(depth + 1) | |
| pl ? BS_INVALID : pcc->part[1][!var], | |
| sub4 ? pcc->part[1][!var] : -1, &child_dir)) | |
| { | |
| return -1; | |
| } | |
| const int w4a = qw4 << var, w4b = hw4 >> var; | |
| if (t->bx + w4a >= f->bw) { t->bx -= ew4; break; } | |
| t->bx += w4a; | |
| if (decode_sb(t, DB_ONLY(depth + 1) | |
| pl ? BS_INVALID : pcc->part[1][var], | |
| sub4 ? pcc->part[1][var] : -1, &child_dir)) | |
| { | |
| return -1; | |
| } | |
| if (t->bx + w4b >= f->bw) { t->bx -= ew4 + w4a; break; } | |
| t->bx += w4b; | |
| if (decode_sb(t, DB_ONLY(depth + 1) | |
| pl ? BS_INVALID : pcc->part[1][2], | |
| sub4 ? pcc->part[1][2] : cbs, &child_dir)) | |
| { | |
| return -1; | |
| } | |
| t->bx -= 7 * ew4; | |
| break; | |
| } | |
| case PARTITION_H4A: | |
| case PARTITION_H4B: { | |
| const int eh4 = qh4 >> 1; | |
| assert(eh4 > 0); | |
| const int sub4 = bs == cbs && (eh4 >> ss_ver) > 0; | |
| assert(sub4 || !pl); | |
| if (decode_sb(t, DB_ONLY(depth + 1) | |
| pl ? BS_INVALID : pcc->part[0][2], | |
| sub4 ? pcc->part[0][2] : BS_INVALID, &child_dir)) | |
| { | |
| return -1; | |
| } | |
| if (t->by + eh4 >= f->bh) break; | |
| t->by += eh4; | |
| const int var = bp - PARTITION_H4A; // h4b: 1, h4a: 0 | |
| if (decode_sb(t, DB_ONLY(depth + 1) | |
| pl ? BS_INVALID : pcc->part[0][!var], | |
| sub4 ? pcc->part[0][!var] : -1, &child_dir)) | |
| { | |
| return -1; | |
| } | |
| const int h4a = qh4 << var, h4b = hh4 >> var; | |
| if (t->by + h4a >= f->bh) { t->by -= eh4; break; } | |
| t->by += h4a; | |
| if (decode_sb(t, DB_ONLY(depth + 1) | |
| pl ? BS_INVALID : pcc->part[0][var], | |
| sub4 ? pcc->part[0][var] : -1, &child_dir)) | |
| { | |
| return -1; | |
| } | |
| if (t->by + h4b >= f->bh) { t->by -= eh4 + h4a; break; } | |
| t->by += h4b; | |
| if (decode_sb(t, DB_ONLY(depth + 1) | |
| pl ? BS_INVALID : pcc->part[0][2], | |
| sub4 ? pcc->part[0][2] : cbs, &child_dir)) | |
| { | |
| return -1; | |
| } | |
| t->by -= 7 * eh4; | |
| break; | |
| } | |
| default: | |
| dav2d_unreachable(); | |
| } | |
| *dir_ptr |= (child_dir & 0xff) << 16; | |
| if (t->intra_region && cbs_orig != BS_INVALID) { | |
| t->cbx = t->bx; | |
| t->cby = t->by; | |
| if (decode_b(t, DB_ONLY(depth + 1) BS_INVALID, cbs_orig)) return -1; | |
| t->intra_region = 0; | |
| } | |
| return 0; | |
| } | |
| static void reset_context(BlockContext *const ctx, const int keyframe, | |
| const int is_tip_frame) | |
| { | |
| memset(ctx->tx_lpf_y, 3, sizeof(ctx->tx_lpf_y)); | |
| memset(ctx->tx_lpf_uv, 2, sizeof(ctx->tx_lpf_uv)); | |
| if (is_tip_frame) return; | |
| memset(ctx->midx, 0xff, sizeof(ctx->midx)); | |
| memset(ctx->intra, keyframe, sizeof(ctx->intra)); | |
| memset(ctx->uvmode, DC_PRED, sizeof(ctx->uvmode)); | |
| if (keyframe) | |
| memset(ctx->mode, DC_PRED, sizeof(ctx->mode)); | |
| memset(ctx->partition, 0, sizeof(ctx->partition)); | |
| memset(ctx->skip_txfm, 0, sizeof(ctx->skip_txfm)); | |
| memset(ctx->skip_mode, 0, sizeof(ctx->skip_mode)); | |
| if (!keyframe) { | |
| memset(ctx->ref, -1, sizeof(ctx->ref)); | |
| memset(ctx->comp_type, 0, sizeof(ctx->comp_type)); | |
| memset(ctx->mode, NEARMV, sizeof(ctx->mode)); | |
| } | |
| memset(ctx->mrl, 0, sizeof(ctx->mrl)); | |
| memset(ctx->lcoef, 0x40, sizeof(ctx->lcoef)); | |
| memset(ctx->ccoef, 0x40, sizeof(ctx->ccoef)); | |
| memset(ctx->filter, DAV2D_N_SWITCHABLE_FILTERS, sizeof(ctx->filter)); | |
| memset(ctx->seg_pred, 0, sizeof(ctx->seg_pred)); | |
| memset(ctx->pal_sz, 0, sizeof(ctx->pal_sz)); | |
| } | |
| // { Y+U+V, Y+U } * 4 | |
| static const uint8_t ss_size_mul[4][2] = { | |
| [DAV2D_PIXEL_LAYOUT_I400] = { 4, 4 }, | |
| [DAV2D_PIXEL_LAYOUT_I420] = { 6, 5 }, | |
| [DAV2D_PIXEL_LAYOUT_I422] = { 8, 6 }, | |
| [DAV2D_PIXEL_LAYOUT_I444] = { 12, 8 }, | |
| }; | |
| static void setup_tile(Dav2dTileState *const ts, | |
| const Dav2dFrameContext *const f, | |
| const uint8_t *const data, const size_t sz, | |
| const int tile_row, const int tile_col, | |
| const unsigned tile_start_off) | |
| { | |
| const int col_sb_start = f->frame_hdr->tiling.t.col_start_sb[tile_col]; | |
| const int col_sb_end = f->frame_hdr->tiling.t.col_start_sb[tile_col + 1]; | |
| const int row_sb_start = f->frame_hdr->tiling.t.row_start_sb[tile_row]; | |
| const int row_sb_end = f->frame_hdr->tiling.t.row_start_sb[tile_row + 1]; | |
| const int sb_shift = f->sb_shift; | |
| const uint8_t *const size_mul = ss_size_mul[f->cur.p.p.layout]; | |
| for (int p = 0; p < 2; p++) { | |
| ts->frame_thread[p].pal_idx = f->frame_thread.pal_idx ? | |
| &f->frame_thread.pal_idx[(size_t)tile_start_off * size_mul[1] >> 3] : | |
| NULL; | |
| ts->frame_thread[p].pal = f->frame_thread.pal ? | |
| &f->frame_thread.pal[(size_t)tile_start_off >> 6] : | |
| NULL; | |
| ts->frame_thread[p].cbi = f->frame_thread.cbi ? | |
| &f->frame_thread.cbi[(size_t)tile_start_off * size_mul[0] >> 6] : | |
| NULL; | |
| ts->frame_thread[p].cf = f->frame_thread.cf ? | |
| (uint8_t*)f->frame_thread.cf + | |
| (((size_t)tile_start_off * size_mul[0]) >> !f->seq_hdr->hbd) : | |
| NULL; | |
| ts->frame_thread[p].partition[0] = | |
| ts->frame_thread[p].partition[1] = f->frame_thread.partition ? | |
| (uint8_t*)f->frame_thread.partition + | |
| ((size_t)tile_start_off >> (4 - f->seq_hdr->sdp)) : | |
| NULL; | |
| } | |
| dav2d_cdf_thread_copy(&ts->cdf, &f->in_cdf); | |
| ts->last_qidx = f->frame_hdr->quant.yac; | |
| dav2d_msac_init(&ts->msac, data, sz, f->frame_hdr->disable_cdf_update); | |
| #if DEBUG_BLOCK_INFO | |
| struct { int by, bx; } tmem = { | |
| .by = row_sb_start << sb_shift, .bx = col_sb_start << sb_shift, | |
| }, *const t = &tmem; | |
| DEBUG_BLOCK_printf("decode_tile[tilerow=%d/col=%d,y=%d-%d,x=%d-%d,size=%td]: r=%d\n", | |
| tile_row, tile_col, | |
| row_sb_start << sb_shift, | |
| imin(row_sb_end << sb_shift, f->bh), | |
| col_sb_start << sb_shift, | |
| imin(col_sb_end << sb_shift, f->bw), | |
| sz, ts->msac.rng); | |
| #endif | |
| ts->tiling.row = tile_row; | |
| ts->tiling.col = tile_col; | |
| ts->tiling.col_start = col_sb_start << sb_shift; | |
| ts->tiling.col_end = imin(col_sb_end << sb_shift, f->bw); | |
| ts->tiling.row_start = row_sb_start << sb_shift; | |
| ts->tiling.row_end = imin(row_sb_end << sb_shift, f->bh); | |
| for (int pl = 0; pl < 3; pl++) { | |
| if (f->frame_hdr->restoration.p[pl].type == DAV2D_RESTORATION_NS_WIENER || | |
| f->frame_hdr->restoration.p[pl].type == DAV2D_RESTORATION_SWITCHABLE) | |
| { | |
| if (pl) { | |
| struct WienerBankUV *const bank = &ts->wiener_bank_uv[pl-1]; | |
| const int8_t (*const cf_range)[2] = dav2d_ns_wiener_coef_range_uv; | |
| bank->idx = 0; | |
| bank->size = 0; | |
| for (int m = 0; m < 18; m++) { | |
| bank->filter[0][m] = cf_range[m][1] + | |
| ((1 << cf_range[m][0]) >> 1); | |
| } | |
| } else { | |
| struct WienerBankY *const bank = &ts->wiener_bank_y; | |
| const int8_t (*const cf_range)[2] = dav2d_ns_wiener_coef_range_y; | |
| memset(bank->size, 0, sizeof(bank->size)); | |
| memset(bank->idx, 0, sizeof(bank->idx)); | |
| const int n_classes = f->frame_hdr->restoration.p[pl].ns.num_classes; | |
| for (int n = 0; n < n_classes; n++) { | |
| for (int m = 0; m < 16; m++) { | |
| bank->filter[0][n][m] = cf_range[m][1] + | |
| ((1 << cf_range[m][0]) >> 1); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if (f->c->n_tc > 1) { | |
| for (int p = 0; p < 3; p++) | |
| atomic_init(&ts->progress[p], ts->tiling.row_start >> 4); | |
| } | |
| } | |
| // modeled after the equivalent function in aomdec:decodeframe.c | |
| static int check_trailing_bits_after_symbol_coder(const MsacContext *const msac) { | |
| // check marker bit (single 1), followed by zeroes | |
| const int n_bits = -(msac->cnt + 14); | |
| assert(n_bits <= 0); // this assumes we errored out when cnt <= -15 in caller | |
| const int n_bytes = (n_bits + 7) >> 3; | |
| const uint8_t *p = &msac->buf_pos[n_bytes]; | |
| const int pattern = 128 >> ((n_bits - 1) & 7); | |
| if ((p[-1] & (2 * pattern - 1)) != pattern) | |
| return 1; | |
| // check remainder zero bytes | |
| for (; p < msac->buf_end; p++) | |
| if (*p) | |
| return 1; | |
| return 0; | |
| } | |
| static int tip_frame_recon_sb(Dav2dTaskContext *const t, | |
| const enum BlockSize bs, const enum BlockSize cbs) | |
| { | |
| const Dav2dFrameContext *const f = t->f; | |
| Av2Block b = { | |
| .bs = bs, | |
| .cbs = cbs, | |
| .intra = 0, | |
| .intrabc = 0, | |
| .seg_id = 0, | |
| .skip_mode = 0, | |
| .skip_txfm = 1, | |
| .tx_part = TX_PARTITION_NONE, | |
| .mv[0].y = f->frame_hdr->tip.gmv.y, | |
| .mv[0].x = f->frame_hdr->tip.gmv.x, | |
| .inter_mode = NEARMV, | |
| .ref.ref[0] = TIP_FRAME, | |
| .ref.ref[1] = -1, | |
| .motion_mode = MM_TRANSLATION, | |
| .filter = f->frame_hdr->tip.subpel_filter, | |
| .cwp_idx = dav2d_tip_wts[f->frame_hdr->tip.global_wtd_idx], | |
| }; | |
| const int by4 = t->by & 63, bx4 = t->bx & 63; | |
| t->cbx = t->bx; | |
| t->cby = t->by; | |
| if (t->task_thread.pass == PASS_MVRES) { | |
| derive_lowest_px(t, bs, cbs, &b); | |
| return 0; | |
| } | |
| assert(t->task_thread.pass & PASS_RECON); | |
| if (f->frame_hdr->tip.apply_filter) { | |
| dav2d_create_db_mask(t->lf_mask->filter_y, &b, bs, | |
| t->bx, t->by, f->bw, f->bh, f->cur.p.p.layout, 0, | |
| &t->a->tx_lpf_y[bx4], &t->l.tx_lpf_y[by4], | |
| f->frame_hdr, f->seq_hdr); | |
| if (cbs != BS_INVALID) | |
| dav2d_create_db_mask(t->lf_mask->filter_uv, &b, bs, t->bx, t->by, | |
| f->bw, f->bh, f->cur.p.p.layout, 1, | |
| &t->a->tx_lpf_uv[bx4 >> f->ss_hor], | |
| &t->l.tx_lpf_uv[by4 >> f->ss_ver], | |
| f->frame_hdr, f->seq_hdr); | |
| const int qidx = f->frame_hdr->quant.yac; | |
| uint16_t *qidx_ptr = &t->lf_mask->qidx[(bx4 >> 4) + ((by4 & 0x30) >> 2)]; | |
| const int sbsz64 = f->sb_step >> 4; | |
| for (int y64 = 0; y64 < sbsz64; y64++) { | |
| for (int x64 = 0; x64 < sbsz64; x64++) | |
| qidx_ptr[x64] = qidx; | |
| qidx_ptr += 4; | |
| } | |
| } | |
| return f->bd_fn.recon_b(t, DB_ONLY(0) bs, | |
| (const enum BlockSize[2]) { cbs, cbs }, &b); | |
| } | |
| int dav2d_decode_tile_sbrow(Dav2dTaskContext *const t) { | |
| const Dav2dFrameContext *const f = t->f; | |
| const enum BlockSize root_bs = f->root_bs; | |
| const enum BlockSize c_root_bs = | |
| f->cur.p.p.layout == DAV2D_PIXEL_LAYOUT_I400 ? BS_INVALID : root_bs; | |
| Dav2dTileState *const ts = t->ts; | |
| const Dav2dContext *const c = f->c; | |
| const int sb_step = f->sb_step; | |
| const int tile_row = ts->tiling.row, tile_col = ts->tiling.col; | |
| // this can be left set if decoding a previous intra region failed | |
| t->intra_region = 0; | |
| // FIXME turn into assert by not scheduling tip frame entropy parsing tasks | |
| if (f->frame_hdr->tip.frame_mode == 2 && t->task_thread.pass == PASS_ENTROPY) | |
| return 0; | |
| if (IS_INTER_OR_SWITCH(f->frame_hdr) || f->frame_hdr->allow_intrabc) { | |
| if (t->task_thread.pass & PASS_MVRES) { | |
| dav2d_refmvs_tile_sbrow_init(&t->rt, &f->rf, | |
| ts->tiling.col_start, ts->tiling.col_end, | |
| ts->tiling.row_start, ts->tiling.row_end, | |
| t->by >> f->sb_shift, ts->tiling.row); | |
| } else if (t->task_thread.pass == PASS_RECON) { | |
| t->rt.rp_proj = &f->rf.rp_proj[(t->by >> 1) * f->rf.rp_stride >> 5]; | |
| } | |
| } | |
| if (t->task_thread.pass == PASS_MVRES && | |
| IS_INTER_OR_SWITCH(f->frame_hdr) && c->n_fc > 1) | |
| { | |
| const int sby = (t->by - ts->tiling.row_start) >> f->sb_shift; | |
| int (*const lowest_px)[2] = ts->lowest_pixel[sby]; | |
| for (int n = 0; n < 7; n++) | |
| for (int m = 0; m < 2; m++) | |
| lowest_px[n][m] = INT_MIN; | |
| } | |
| if (t->task_thread.pass & PASS_MVRES && | |
| f->c->n_tc > 1 && f->frame_hdr->use_ref_frame_mvs) | |
| { | |
| dav2d_refmvs_load_tmvs(&f->c->refmvs_dsp, &f->rf, ts->tiling.row, | |
| ts->tiling.col_start >> 1, ts->tiling.col_end >> 1, | |
| t->by >> 1, (t->by + sb_step) >> 1); | |
| } | |
| const int sb256y = t->by >> 6; | |
| if (!(t->task_thread.pass & PASS_ENTROPY) || f->frame_hdr->tip.frame_mode == 2) { | |
| if (f->frame_hdr->tip.frame_mode == 2) | |
| reset_context(&t->l, IS_KEY_OR_INTRA(f->frame_hdr), 1); | |
| for (t->bx = ts->tiling.col_start; | |
| t->bx < ts->tiling.col_end; t->bx += sb_step) | |
| { | |
| if (t->task_thread.pass & PASS_RECON) { | |
| memset(t->is_coded, 0, sizeof(t->is_coded)); | |
| t->lf_mask = f->lf.mask + (t->bx >> 6) + sb256y * f->sb256w; | |
| t->a = f->a + tile_row * f->sb256w + (t->bx >> 6); | |
| } | |
| if (t->task_thread.pass & PASS_MVRES && | |
| (IS_INTER_OR_SWITCH(f->frame_hdr) || f->frame_hdr->allow_intrabc)) | |
| { | |
| dav2d_refmvs_reset_sb(&t->rt, t->by, t->bx); | |
| } | |
| if (atomic_load_explicit(c->flush, memory_order_acquire)) | |
| return 1; | |
| int dir = 0; | |
| if (f->frame_hdr->tip.frame_mode == 2) { | |
| tip_frame_recon_sb(t, root_bs, c_root_bs); | |
| } else { | |
| if (decode_sb(t, DB_ONLY(1) root_bs, root_bs, &dir)) | |
| return 1; | |
| } | |
| if (t->task_thread.pass & PASS_MVRES && | |
| (IS_INTER_OR_SWITCH(f->frame_hdr) || f->frame_hdr->allow_intrabc)) | |
| { | |
| dav2d_refmvs_save_tmvs(&t->rt, t->bx >> 1, (t->bx + sb_step) >> 1, | |
| t->by >> 1, (t->by + sb_step) >> 1); | |
| } | |
| } | |
| if (t->task_thread.pass & PASS_RECON && c->n_tc > 1) | |
| f->bd_fn.backup_prefilter_data(t); | |
| return 0; | |
| } | |
| reset_context(&t->l, IS_KEY_OR_INTRA(f->frame_hdr), 0); | |
| for (t->bx = ts->tiling.col_start; | |
| t->bx < ts->tiling.col_end; t->bx += sb_step) | |
| { | |
| memset(t->is_coded, 0, sizeof(t->is_coded)); | |
| t->lf_mask = f->lf.mask + (t->bx >> 6) + sb256y * f->sb256w; | |
| t->a = f->a + tile_row * f->sb256w + (t->bx >> 6); | |
| if (atomic_load_explicit(c->flush, memory_order_acquire)) | |
| return 1; | |
| switch (root_bs) { | |
| default: dav2d_unreachable(); | |
| case BS_64x64: { | |
| const int idx = ((t->bx & 0x30) >> 4) + ((t->by & 0x30) >> 2); | |
| t->lf_mask->cdef_idx[idx] = -1; | |
| break; | |
| } | |
| case BS_128x128: { | |
| const int idx = ((t->bx & 32) >> 4) + ((t->by & 32) >> 2); | |
| memset(&t->lf_mask->cdef_idx[idx + 0], -1, 2); | |
| memset(&t->lf_mask->cdef_idx[idx + 4], -1, 2); | |
| break; | |
| } | |
| case BS_256x256: | |
| memset(t->lf_mask->cdef_idx, -1, 16); | |
| break; | |
| } | |
| if (t->task_thread.pass & PASS_MVRES && | |
| (IS_INTER_OR_SWITCH(f->frame_hdr) || f->frame_hdr->allow_intrabc)) | |
| { | |
| dav2d_refmvs_reset_sb(&t->rt, t->by, t->bx); | |
| } | |
| // Restoration filter. In intra frames with SDP and 64x64 | |
| // superblocks, the luma and chroma trees split at the superblock | |
| // root; the chroma coefficients are then read in decode_sb(), | |
| // between the two trees (as AVM does). | |
| read_sb_restoration(t, 0, sdp_root_split(f, root_bs) ? 1 : 3); | |
| int dir = 0; | |
| t->sdp_cfl_disallowed = 0; | |
| if (IS_INTER_OR_SWITCH(f->frame_hdr)) { | |
| // for some contexts related to warp-motion, AVM uses 8x8 (instead | |
| // of 4x4) context resolution when we cross SB boundaries. However, | |
| // the way this is implemented means we sometimes go outside the | |
| // bounds of our own block into data that has already been written | |
| // into by our neighbour blocks. For example, if we access "top" at | |
| // 8x8 resolution for x=25, this may round to x=24 (which our left- | |
| // neighbour just overwrote). To workaround this, we keep a copy of | |
| // all affected context bits at SB boundaries. See AVM #1091. | |
| memcpy(t->a_sb_cache.ref[0], t->a->ref[0], 64); | |
| memcpy(t->a_sb_cache.ref[1], t->a->ref[1], 64); | |
| if (t->by > ts->tiling.row_start) | |
| memcpy(t->a_sb_cache.motion_mode, t->a->motion_mode, 64); | |
| } | |
| if (decode_sb(t, DB_ONLY(1) root_bs, root_bs, &dir)) | |
| return 1; | |
| if (t->task_thread.pass & PASS_MVRES && | |
| (IS_INTER_OR_SWITCH(f->frame_hdr) || f->frame_hdr->allow_intrabc)) | |
| { | |
| dav2d_refmvs_save_tmvs(&t->rt, t->bx >> 1, (t->bx + sb_step) >> 1, | |
| t->by >> 1, (t->by + sb_step) >> 1); | |
| } | |
| } | |
| // backup pre-loopfilter pixels for intra prediction of the next sbrow | |
| if (t->task_thread.pass & PASS_RECON && c->n_tc > 1) | |
| f->bd_fn.backup_prefilter_data(t); | |
| // backup t->a/l.tx_lpf_y/uv at tile boundaries to use them to "fix" | |
| // up the initial value in neighbour tiles when running the loopfilter | |
| int align_h = (f->bh + 63) & ~63; | |
| memcpy(&f->lf.tx_db_right_edge[0][align_h * tile_col + t->by], | |
| &t->l.tx_lpf_y[t->by & 0x30], sb_step); | |
| const int ss_ver = f->cur.p.p.layout == DAV2D_PIXEL_LAYOUT_I420; | |
| align_h >>= ss_ver; | |
| memcpy(&f->lf.tx_db_right_edge[1][align_h * tile_col + (t->by >> ss_ver)], | |
| &t->l.tx_lpf_uv[(t->by & 0x30) >> ss_ver], sb_step >> ss_ver); | |
| // error out on symbol decoder overread | |
| if (ts->msac.cnt <= -15) return 1; | |
| return c->strict_std_compliance && f->frame_hdr->tip.frame_mode != 2 && | |
| (t->by >> f->sb_shift) + 1 >= f->frame_hdr->tiling.t.row_start_sb[tile_row + 1] && | |
| check_trailing_bits_after_symbol_coder(&ts->msac); | |
| } | |
| int dav2d_decode_frame_init(Dav2dFrameContext *const f) { | |
| const Dav2dContext *const c = f->c; | |
| int retval = DAV2D_ERR(ENOMEM); | |
| if (f->sbh > f->lf.start_of_tile_row_sz) { | |
| dav2d_free(f->lf.start_of_tile_row); | |
| f->lf.start_of_tile_row = dav2d_malloc(ALLOC_TILE, f->sbh * sizeof(uint8_t)); | |
| if (!f->lf.start_of_tile_row) { | |
| f->lf.start_of_tile_row_sz = 0; | |
| goto error; | |
| } | |
| f->lf.start_of_tile_row_sz = f->sbh; | |
| } | |
| for (int tile_row = 0, sby = 0; tile_row < f->frame_hdr->tiling.t.rows; tile_row++) { | |
| f->lf.start_of_tile_row[sby++] = (tile_row << 1) | 1; | |
| while (sby < f->frame_hdr->tiling.t.row_start_sb[tile_row + 1]) | |
| f->lf.start_of_tile_row[sby++] = tile_row << 1; | |
| } | |
| const int n_ts = f->frame_hdr->tiling.t.cols * f->frame_hdr->tiling.t.rows; | |
| if (n_ts != f->n_ts) { | |
| if (c->task_thread.n_passes > 1) { | |
| dav2d_free(f->frame_thread.tile_start_off); | |
| f->frame_thread.tile_start_off = | |
| dav2d_malloc(ALLOC_TILE, sizeof(*f->frame_thread.tile_start_off) * n_ts); | |
| if (!f->frame_thread.tile_start_off) { | |
| f->n_ts = 0; | |
| goto error; | |
| } | |
| } | |
| dav2d_free_aligned(f->ts); | |
| f->ts = dav2d_alloc_aligned(ALLOC_TILE, sizeof(*f->ts) * n_ts, 32); | |
| if (!f->ts) goto error; | |
| f->n_ts = n_ts; | |
| } | |
| const int a_sz = f->sb256w * f->frame_hdr->tiling.t.rows; | |
| if (a_sz != f->a_sz) { | |
| dav2d_free(f->a); | |
| f->a = dav2d_malloc(ALLOC_TILE, sizeof(*f->a) * a_sz); | |
| if (!f->a) { | |
| f->a_sz = 0; | |
| goto error; | |
| } | |
| f->a_sz = a_sz; | |
| } | |
| const int num_sb256 = f->sb256w * f->sb256h; | |
| const uint8_t *const size_mul = ss_size_mul[f->cur.p.p.layout]; | |
| const int hbd = !!f->seq_hdr->hbd; | |
| if (c->n_fc > 1) { | |
| const int lowest_pixel_mem_sz = f->frame_hdr->tiling.t.cols * f->sbh; | |
| if (lowest_pixel_mem_sz != f->tile_thread.lowest_pixel_mem_sz) { | |
| dav2d_free(f->tile_thread.lowest_pixel_mem); | |
| f->tile_thread.lowest_pixel_mem = | |
| dav2d_malloc(ALLOC_TILE, lowest_pixel_mem_sz * | |
| sizeof(*f->tile_thread.lowest_pixel_mem)); | |
| if (!f->tile_thread.lowest_pixel_mem) { | |
| f->tile_thread.lowest_pixel_mem_sz = 0; | |
| goto error; | |
| } | |
| f->tile_thread.lowest_pixel_mem_sz = lowest_pixel_mem_sz; | |
| } | |
| int (*lowest_pixel_ptr)[7][2] = f->tile_thread.lowest_pixel_mem; | |
| for (int tile_row = 0, tile_row_base = 0; tile_row < f->frame_hdr->tiling.t.rows; | |
| tile_row++, tile_row_base += f->frame_hdr->tiling.t.cols) | |
| { | |
| const int tile_row_sb_h = f->frame_hdr->tiling.t.row_start_sb[tile_row + 1] - | |
| f->frame_hdr->tiling.t.row_start_sb[tile_row]; | |
| for (int tile_col = 0; tile_col < f->frame_hdr->tiling.t.cols; tile_col++) { | |
| f->ts[tile_row_base + tile_col].lowest_pixel = lowest_pixel_ptr; | |
| lowest_pixel_ptr += tile_row_sb_h; | |
| } | |
| } | |
| } | |
| if (c->task_thread.n_passes > 1) { | |
| const unsigned sb_step4 = f->sb_step * 4; | |
| int tile_idx = 0; | |
| for (int tile_row = 0; tile_row < f->frame_hdr->tiling.t.rows; tile_row++) { | |
| const unsigned row_off = f->frame_hdr->tiling.t.row_start_sb[tile_row] * | |
| sb_step4 * f->sb256w * 256; | |
| const unsigned b_diff = (f->frame_hdr->tiling.t.row_start_sb[tile_row + 1] - | |
| f->frame_hdr->tiling.t.row_start_sb[tile_row]) * sb_step4; | |
| for (int tile_col = 0; tile_col < f->frame_hdr->tiling.t.cols; tile_col++) { | |
| f->frame_thread.tile_start_off[tile_idx++] = row_off + b_diff * | |
| f->frame_hdr->tiling.t.col_start_sb[tile_col] * sb_step4; | |
| } | |
| } | |
| const int cbi_sz = num_sb256 * size_mul[0]; | |
| if (cbi_sz != f->frame_thread.cbi_sz) { | |
| dav2d_free_aligned(f->frame_thread.cbi); | |
| f->frame_thread.cbi = | |
| dav2d_alloc_aligned(ALLOC_BLOCK, sizeof(*f->frame_thread.cbi) * | |
| cbi_sz * 64 * 64 / 4, 64); | |
| if (!f->frame_thread.cbi) { | |
| f->frame_thread.cbi_sz = 0; | |
| goto error; | |
| } | |
| f->frame_thread.cbi_sz = cbi_sz; | |
| } | |
| const int cf_sz = (num_sb256 * size_mul[0]) << hbd; | |
| if (cf_sz != f->frame_thread.cf_sz) { | |
| dav2d_free_aligned(f->frame_thread.cf); | |
| f->frame_thread.cf = | |
| dav2d_alloc_aligned(ALLOC_COEF, (size_t)cf_sz * 256 * 256 / 2, 64); | |
| if (!f->frame_thread.cf) { | |
| f->frame_thread.cf_sz = 0; | |
| goto error; | |
| } | |
| memset(f->frame_thread.cf, 0, (size_t)cf_sz * 256 * 256 / 2); | |
| f->frame_thread.cf_sz = cf_sz; | |
| } | |
| const int part_sz = num_sb256 * 4096 << f->seq_hdr->sdp; | |
| if (part_sz != f->frame_thread.part_sz) { | |
| dav2d_free(f->frame_thread.partition); | |
| f->frame_thread.partition = dav2d_malloc(ALLOC_BLOCK, (size_t)part_sz); | |
| if (!f->frame_thread.partition) { | |
| f->frame_thread.part_sz = 0; | |
| goto error; | |
| } | |
| f->frame_thread.part_sz = part_sz; | |
| } | |
| if (f->frame_hdr->allow_screen_content_tools) { | |
| const int pal_sz = num_sb256 << hbd; | |
| if (pal_sz != f->frame_thread.pal_sz) { | |
| dav2d_free_aligned(f->frame_thread.pal); | |
| f->frame_thread.pal = | |
| dav2d_alloc_aligned(ALLOC_PAL, sizeof(*f->frame_thread.pal) * | |
| pal_sz * 32 * 32, 64); | |
| if (!f->frame_thread.pal) { | |
| f->frame_thread.pal_sz = 0; | |
| goto error; | |
| } | |
| f->frame_thread.pal_sz = pal_sz; | |
| } | |
| const int pal_idx_sz = num_sb256 * size_mul[1]; | |
| if (pal_idx_sz != f->frame_thread.pal_idx_sz) { | |
| dav2d_free_aligned(f->frame_thread.pal_idx); | |
| f->frame_thread.pal_idx = | |
| dav2d_alloc_aligned(ALLOC_PAL, sizeof(*f->frame_thread.pal_idx) * | |
| pal_idx_sz * 256 * 256 / 8, 64); | |
| if (!f->frame_thread.pal_idx) { | |
| f->frame_thread.pal_idx_sz = 0; | |
| goto error; | |
| } | |
| f->frame_thread.pal_idx_sz = pal_idx_sz; | |
| } | |
| } else if (f->frame_thread.pal) { | |
| dav2d_freep_aligned(&f->frame_thread.pal); | |
| dav2d_freep_aligned(&f->frame_thread.pal_idx); | |
| f->frame_thread.pal_sz = f->frame_thread.pal_idx_sz = 0; | |
| } | |
| } | |
| // update allocation of block contexts for above | |
| ptrdiff_t y_stride = f->cur.p.stride[0], uv_stride = f->cur.p.stride[1]; | |
| const int num_lines = c->n_tc > 1 ? f->sb64h * 4 : 8; | |
| // FIXME double this with threading enabled | |
| const int n_tile_rows_m1 = f->frame_hdr->tiling.t.rows - 1; | |
| y_stride = f->cur.p.stride[0], uv_stride = f->cur.p.stride[1]; | |
| if (y_stride * num_lines != f->lf.lr_buf_plane_sz[0] || | |
| uv_stride * num_lines * 2 != f->lf.lr_buf_plane_sz[1] || | |
| y_stride * 6 * n_tile_rows_m1 != f->lf.lr_buf_plane_sz[2] || | |
| uv_stride * 4 * n_tile_rows_m1 != f->lf.lr_buf_plane_sz[3]) | |
| { | |
| dav2d_free_aligned(f->lf.lr_line_buf); | |
| // lr simd may overread the input, so slightly over-allocate the db buffer | |
| size_t alloc_sz = 128; | |
| alloc_sz += (size_t)llabs(y_stride) * num_lines; | |
| alloc_sz += (size_t)llabs(uv_stride) * num_lines * 2; | |
| alloc_sz += (size_t)llabs(y_stride) * n_tile_rows_m1 * 6; | |
| alloc_sz += (size_t)llabs(uv_stride) * n_tile_rows_m1 * 4; | |
| uint8_t *ptr = f->lf.lr_line_buf = dav2d_alloc_aligned(ALLOC_LR, alloc_sz, 64); | |
| if (!ptr) { | |
| f->lf.lr_buf_plane_sz[0] = f->lf.lr_buf_plane_sz[1] = 0; | |
| goto error; | |
| } | |
| ptr += 64; | |
| if (y_stride < 0) | |
| f->lf.lr_db_line[0] = ptr - y_stride * (num_lines - 1); | |
| else | |
| f->lf.lr_db_line[0] = ptr; | |
| ptr += llabs(y_stride) * num_lines; | |
| if (uv_stride < 0) { | |
| f->lf.lr_db_line[1] = ptr - uv_stride * (num_lines * 1 - 1); | |
| f->lf.lr_db_line[2] = ptr - uv_stride * (num_lines * 2 - 1); | |
| } else { | |
| f->lf.lr_db_line[1] = ptr; | |
| f->lf.lr_db_line[2] = ptr + uv_stride * num_lines; | |
| } | |
| ptr += llabs(uv_stride) * num_lines * 2; | |
| // FIXME make the below work with negative stride | |
| f->lf.lr_cdef_line[0] = ptr; | |
| ptr += llabs(y_stride) * n_tile_rows_m1 * 6; | |
| f->lf.lr_cdef_line[1] = ptr; | |
| f->lf.lr_cdef_line[2] = ptr + llabs(uv_stride) * n_tile_rows_m1 * 2; | |
| f->lf.lr_buf_plane_sz[0] = (int) y_stride * num_lines; | |
| f->lf.lr_buf_plane_sz[1] = (int) uv_stride * num_lines * 2; | |
| f->lf.lr_buf_plane_sz[2] = (int) y_stride * n_tile_rows_m1 * 6; | |
| f->lf.lr_buf_plane_sz[3] = (int) uv_stride * n_tile_rows_m1 * 4; | |
| } | |
| // update allocation for loopfilter masks | |
| if (num_sb256 != f->lf.mask_sz) { | |
| dav2d_free(f->lf.mask); | |
| f->lf.mask = dav2d_malloc(ALLOC_LF, sizeof(*f->lf.mask) * num_sb256); | |
| if (!f->lf.mask) { | |
| f->lf.mask_sz = 0; | |
| goto error; | |
| } | |
| dav2d_free(f->frame_thread.b); | |
| f->frame_thread.b = dav2d_malloc(ALLOC_BLOCK, sizeof(*f->frame_thread.b) * | |
| (c->task_thread.n_passes > 1 ? | |
| num_sb256 * 64 * 64 : 16 * 16)); | |
| if (!f->frame_thread.b) { | |
| f->lf.mask_sz = 0; | |
| goto error; | |
| } | |
| dav2d_free(f->lf.lr_mask); | |
| f->lf.lr_mask = dav2d_malloc(ALLOC_LR, sizeof(*f->lf.lr_mask) * num_sb256); | |
| if (!f->lf.lr_mask) { | |
| f->lf.mask_sz = 0; | |
| goto error; | |
| } | |
| f->lf.mask_sz = num_sb256; | |
| } | |
| memset(f->lf.mask, 0, sizeof(*f->lf.mask) * num_sb256); | |
| memset(f->lf.lr_mask, 0, sizeof(*f->lf.lr_mask) * num_sb256); | |
| init_wiener(f); | |
| f->lf.restore_planes = | |
| (((f->frame_hdr->restoration.p[0].type != DAV2D_RESTORATION_NONE && | |
| c->inloop_filters & DAV2D_INLOOPFILTER_WIENER) || | |
| (f->frame_hdr->gdf.enabled && | |
| c->inloop_filters & DAV2D_INLOOPFILTER_GDF)) << 0) | | |
| // chroma wiener requires luma input as well | |
| ((f->frame_hdr->restoration.p[1].type != DAV2D_RESTORATION_NONE && | |
| c->inloop_filters & DAV2D_INLOOPFILTER_WIENER) * 3) | | |
| ((f->frame_hdr->restoration.p[2].type != DAV2D_RESTORATION_NONE && | |
| c->inloop_filters & DAV2D_INLOOPFILTER_WIENER) * 5); | |
| f->lf.cdef_planes = | |
| (f->frame_hdr->cdef.enabled && | |
| c->inloop_filters & DAV2D_INLOOPFILTER_CDEF) ? | |
| f->seq_hdr->layout == DAV2D_PIXEL_LAYOUT_I400 ? 1 : 7 : | |
| // ccso_prep relies on luma input/edges for all planes | |
| ((f->frame_hdr->ccso.p[0].enabled || | |
| f->frame_hdr->ccso.p[1].enabled || | |
| f->frame_hdr->ccso.p[2].enabled) && | |
| c->inloop_filters & DAV2D_INLOOPFILTER_CCSO); | |
| if (f->frame_hdr->gdf.enabled) { | |
| const int qp_idx = f->frame_hdr->gdf.qp_idx; | |
| if (IS_INTER_OR_SWITCH(f->frame_hdr)) { | |
| int max_dist = 0; | |
| for (int i = 0; i < imin(f->frame_hdr->n_ref_frames, 2); i++) | |
| max_dist = imax(max_dist, f->absrefdist[i]); | |
| static const uint8_t ref_dst_idx_tbl[12] = { 4, 0, 1, 2, 2, 2, 3, 3, 3, 3, 3, 4 }; | |
| const int ref_dst_idx = ref_dst_idx_tbl[imin(max_dist, 11)]; | |
| f->lf.gdf_params = &dav2d_gdf_inter_params[ref_dst_idx][qp_idx].p; | |
| } else { | |
| f->lf.gdf_params = &dav2d_gdf_intra_params[qp_idx].p; | |
| } | |
| } | |
| size_t prefilter_data_size_y, prefilter_data_size_uv; | |
| if (c->n_tc == 1) { | |
| prefilter_data_size_y = prefilter_data_size_uv = 0; | |
| f->prefilter_data_full_frame = 1; | |
| } else if (f->frame_hdr->allow_intrabc) { | |
| const int bh_align7 = (f->bh + 15) & ~15; | |
| prefilter_data_size_y = f->cur.p.stride[0] * bh_align7 * 4; | |
| const int bh_align15 = (f->bh + 31) & ~31; | |
| prefilter_data_size_uv = f->cur.p.stride[1] * bh_align15 * 4 >> f->ss_ver; | |
| f->prefilter_data_full_frame = 1; | |
| } else { | |
| prefilter_data_size_y = f->cur.p.stride[0] * f->frame_hdr->tiling.t.rows; | |
| prefilter_data_size_uv = f->cur.p.stride[1] * f->frame_hdr->tiling.t.rows; | |
| f->prefilter_data_full_frame = 0; | |
| } | |
| const size_t prefilter_data_sz = prefilter_data_size_y + 2 * prefilter_data_size_uv; | |
| if (prefilter_data_sz > f->prefilter_data_sz) { | |
| if (f->prefilter_data_sz) // otherwise it's a pointer into f->cur | |
| dav2d_free_aligned(f->prefilter_data[0]); | |
| if (prefilter_data_sz) { | |
| uint8_t *ptr = f->prefilter_data[0] = | |
| dav2d_alloc_aligned(ALLOC_IPRED, prefilter_data_sz, 64); | |
| if (!ptr) { | |
| f->prefilter_data_sz = 0; | |
| goto error; | |
| } | |
| if (f->cur.p.p.layout != DAV2D_PIXEL_LAYOUT_I400) { | |
| ptr += prefilter_data_size_y; | |
| f->prefilter_data[1] = ptr; | |
| ptr += prefilter_data_size_uv; | |
| f->prefilter_data[2] = ptr; | |
| } | |
| } | |
| f->prefilter_data_sz = prefilter_data_sz; | |
| } | |
| const int re_sz = f->sb256h * f->frame_hdr->tiling.t.cols; | |
| if (re_sz != f->lf.re_sz) { | |
| dav2d_free(f->lf.tx_db_right_edge[0]); | |
| f->lf.tx_db_right_edge[0] = dav2d_malloc(ALLOC_LF, re_sz * 64 * 2); | |
| if (!f->lf.tx_db_right_edge[0]) { | |
| f->lf.re_sz = 0; | |
| goto error; | |
| } | |
| f->lf.tx_db_right_edge[1] = f->lf.tx_db_right_edge[0] + re_sz * 64; | |
| f->lf.re_sz = re_sz; | |
| } | |
| // init ref mvs | |
| if (IS_INTER_OR_SWITCH(f->frame_hdr) || f->frame_hdr->allow_intrabc) { | |
| const int ret = | |
| dav2d_refmvs_init_frame(&f->rf, f->seq_hdr, f->frame_hdr, | |
| f->refpoc, f->mvs, f->refrefpoc, f->refcnt, | |
| f->ref_mvs, f->c->n_tc > 1, f->c->n_fc > 1); | |
| if (ret < 0) goto error; | |
| } | |
| // setup dequant tables | |
| init_quant_tables(f->seq_hdr, f->frame_hdr, f->n_seg, | |
| f->frame_hdr->quant.yac, f->dq); | |
| if (f->frame_hdr->quant.qm.enabled) | |
| for (int i = 0; i < N_RECT_TX_SIZES; i++) { | |
| f->qm[i][0] = dav2d_qm_tbl[f->frame_hdr->quant.qm.y[0]][0][i]; | |
| f->qm[i][1] = dav2d_qm_tbl[f->frame_hdr->quant.qm.u[0]][1][i]; | |
| f->qm[i][2] = dav2d_qm_tbl[f->frame_hdr->quant.qm.v[0]][1][i]; | |
| } | |
| else | |
| memset(f->qm, 0, sizeof(f->qm)); | |
| /* Init loopfilter pointers. Increasing NULL pointers is technically UB, | |
| * so just point the chroma pointers in 4:0:0 to the luma plane here to | |
| * avoid having additional in-loop branches in various places. We never | |
| * dereference those pointers so it doesn't really matter what they | |
| * point at, as long as the pointers are valid. */ | |
| const int has_chroma = f->cur.p.p.layout != DAV2D_PIXEL_LAYOUT_I400; | |
| f->lf.p[0] = f->cur.p.data[0]; | |
| f->lf.p[1] = f->cur.p.data[has_chroma ? 1 : 0]; | |
| f->lf.p[2] = f->cur.p.data[has_chroma ? 2 : 0]; | |
| if (c->n_tc == 1) { | |
| f->prefilter_data[0] = f->cur.p.data[0]; | |
| f->prefilter_data[1] = f->cur.p.data[1]; | |
| f->prefilter_data[2] = f->cur.p.data[2]; | |
| } else if (f->frame_hdr->allow_intrabc) { | |
| f->cur.p.data[0] = f->prefilter_data[0]; | |
| f->cur.p.data[1] = f->prefilter_data[1]; | |
| f->cur.p.data[2] = f->prefilter_data[2]; | |
| } | |
| if (c->n_tc > 1) { | |
| for (int n = 0; n < f->sb256w * f->frame_hdr->tiling.t.rows; n++) | |
| reset_context(&f->a[n], IS_KEY_OR_INTRA(f->frame_hdr), | |
| f->frame_hdr->tip.frame_mode == 2); | |
| } | |
| retval = 0; | |
| error: | |
| return retval; | |
| } | |
| // Derive the CDFs stored for use by subsequent frames from the tile CDFs. | |
| // This is also done if disable_cdf_update is set: the tile CDFs are then | |
| // unadapted copies of in_cdf, but averaging and count reset still apply. | |
| void dav2d_decode_frame_update_cdf(Dav2dFrameContext *const f) { | |
| const int n_tiles = f->frame_hdr->tiling.t.cols * | |
| f->frame_hdr->tiling.t.rows; | |
| if (n_tiles > 1 && f->seq_hdr->avg_cdf_type) { | |
| const int shift = imin(ulog2(n_tiles), 3); | |
| const int n_tiles_sh = 1 << shift; | |
| assert(n_tiles_sh <= n_tiles); | |
| dav2d_cdf_shift(f->out_cdf.data.cdf, &f->ts[0].cdf, shift); | |
| for (int n = 1; n < n_tiles_sh; n++) { | |
| dav2d_cdf_shift_accumulate(f->out_cdf.data.cdf, | |
| &f->ts[n].cdf, shift); | |
| } | |
| } else { | |
| memcpy(f->out_cdf.data.cdf, | |
| &f->ts[f->frame_hdr->tiling.update].cdf, | |
| sizeof(CdfContext)); | |
| } | |
| dav2d_cdf_reset_count(f->frame_hdr, f->out_cdf.data.cdf); | |
| } | |
| int dav2d_decode_frame_init_cdf(Dav2dFrameContext *const f) { | |
| const Dav2dContext *const c = f->c; | |
| int retval = DAV2D_ERR(EINVAL); | |
| if (f->use_pri_sec_cdf) { | |
| dav2d_cdf_pri_sec_average(f->in_cdf.data.cdf, | |
| &f->src_cdf[0], &f->src_cdf[1]); | |
| } | |
| if (!f->frame_hdr->disable_cdf_update) | |
| dav2d_cdf_thread_copy(f->out_cdf.data.cdf, &f->in_cdf); | |
| // parse individual tiles per tile group | |
| int tile_row = 0, tile_col = 0; | |
| f->task_thread.update_set = 0; | |
| for (int i = 0; i < f->n_tile_data; i++) { | |
| const uint8_t *data = f->tile[i].data.data; | |
| size_t size = f->tile[i].data.sz; | |
| for (int j = f->tile[i].start; j <= f->tile[i].end; j++) { | |
| size_t tile_sz; | |
| if (j == f->tile[i].end) { | |
| tile_sz = size; | |
| } else { | |
| if (f->frame_hdr->tiling.n_bytes > size) goto error; | |
| tile_sz = 0; | |
| for (unsigned k = 0; k < f->frame_hdr->tiling.n_bytes; k++) | |
| tile_sz |= (unsigned)*data++ << (k * 8); | |
| tile_sz++; | |
| size -= f->frame_hdr->tiling.n_bytes; | |
| if (tile_sz > size) goto error; | |
| } | |
| setup_tile(&f->ts[j], f, data, tile_sz, tile_row, tile_col++, | |
| c->task_thread.n_passes > 1 ? | |
| f->frame_thread.tile_start_off[j] : 0); | |
| if (tile_col == f->frame_hdr->tiling.t.cols) { | |
| tile_col = 0; | |
| tile_row++; | |
| } | |
| if (j == f->frame_hdr->tiling.update && !f->frame_hdr->disable_cdf_update && | |
| !(f->seq_hdr->avg_cdf_type && f->frame_hdr->tiling.t.log2_cols + | |
| f->frame_hdr->tiling.t.log2_rows)) | |
| { | |
| f->task_thread.update_set = 1; | |
| } | |
| data += tile_sz; | |
| size -= tile_sz; | |
| } | |
| } | |
| // tile CDFs won't adapt, so the output CDFs can be derived right away | |
| if (f->frame_hdr->disable_cdf_update) | |
| dav2d_decode_frame_update_cdf(f); | |
| atomic_store(&f->task_thread.entropy_task_counter, | |
| f->frame_hdr->tiling.t.cols * f->frame_hdr->tiling.t.rows); | |
| retval = 0; | |
| error: | |
| return retval; | |
| } | |
| void dav2d_decode_tip_frame_init(Dav2dFrameContext *const f) { | |
| const Dav2dTileInfo *const ti = &f->frame_hdr->tiling.t; | |
| const int sb_shift = f->sb_shift; | |
| for (int tile_row = 0, tile = 0; tile_row < ti->rows; tile_row++) { | |
| for (int tile_col = 0; tile_col < ti->cols; tile_col++, tile++) { | |
| const int col_sb_start = ti->col_start_sb[tile_col]; | |
| const int col_sb_end = ti->col_start_sb[tile_col + 1]; | |
| const int row_sb_start = ti->row_start_sb[tile_row]; | |
| const int row_sb_end = ti->row_start_sb[tile_row + 1]; | |
| Dav2dTileState *const ts = &f->ts[tile]; | |
| ts->tiling.row = tile_row; | |
| ts->tiling.col = tile_col; | |
| ts->tiling.col_start = col_sb_start << sb_shift; | |
| ts->tiling.col_end = imin(col_sb_end << sb_shift, f->bw); | |
| ts->tiling.row_start = row_sb_start << sb_shift; | |
| ts->tiling.row_end = imin(row_sb_end << sb_shift, f->bh); | |
| if (f->c->n_tc > 1) | |
| for (int p = 0; p < 3; p++) | |
| atomic_init(&ts->progress[p], ts->tiling.row_start >> 4); | |
| } | |
| } | |
| atomic_store(&f->task_thread.entropy_task_counter, | |
| f->frame_hdr->tiling.t.cols * f->frame_hdr->tiling.t.rows); | |
| } | |
| int dav2d_decode_frame_main(Dav2dFrameContext *const f) { | |
| const Dav2dContext *const c = f->c; | |
| int retval = DAV2D_ERR(EINVAL); | |
| assert(f->c->n_tc == 1); | |
| Dav2dTaskContext *const t = &c->tc[f - c->fc]; | |
| t->f = f; | |
| t->task_thread.pass = PASS_ALL; | |
| for (int n = 0; n < f->sb256w * f->frame_hdr->tiling.t.rows; n++) | |
| reset_context(&f->a[n], IS_KEY_OR_INTRA(f->frame_hdr), | |
| f->frame_hdr->tip.frame_mode == 2); | |
| const int sb128 = f->frame_hdr->sb128; | |
| for (int tile_row = 0; tile_row < f->frame_hdr->tiling.t.rows; tile_row++) { | |
| const int sby_start = f->frame_hdr->tiling.t.row_start_sb[tile_row]; | |
| const int sbh_end = | |
| imin(f->frame_hdr->tiling.t.row_start_sb[tile_row + 1], f->sbh); | |
| for (int sby = sby_start; sby < sbh_end; sby++) { | |
| t->by = sby << (4 + f->frame_hdr->sb128); | |
| const int by_end = (t->by + f->sb_step) >> 1; | |
| if (f->frame_hdr->use_ref_frame_mvs) { | |
| dav2d_refmvs_load_tmvs(&f->c->refmvs_dsp, &f->rf, tile_row, | |
| 0, f->bw >> 1, t->by >> 1, by_end); | |
| } | |
| for (int tile_col = 0; tile_col < f->frame_hdr->tiling.t.cols; tile_col++) { | |
| t->ts = &f->ts[tile_row * f->frame_hdr->tiling.t.cols + tile_col]; | |
| if (dav2d_decode_tile_sbrow(t)) goto error; | |
| } | |
| } | |
| // post filters (deblock + cdef + ccso + ...) | |
| // do this after completing full tiles, so that intra bc works correctly | |
| const int sb64h_end = | |
| imin(f->frame_hdr->tiling.t.row_start_sb[tile_row + 1] << sb128, f->sb64h); | |
| for (int by64 = sby_start << sb128; by64 < sb64h_end; by64++) { | |
| f->bd_fn.filter_slice(f, by64); | |
| } | |
| } | |
| retval = 0; | |
| error: | |
| return retval; | |
| } | |
| void dav2d_decode_frame_exit(Dav2dFrameContext *const f, int retval) { | |
| const Dav2dContext *const c = f->c; | |
| if (f->cur.p.data[0]) | |
| atomic_init(&f->task_thread.error, 0); | |
| if (c->task_thread.n_passes > 1 && retval && f->frame_thread.cf) { | |
| memset(f->frame_thread.cf, 0, | |
| (size_t)f->frame_thread.cf_sz * 256 * 256 / 2); | |
| } | |
| for (int i = 0; i < 7; i++) { | |
| if (f->refp[i].p.frame_hdr) { | |
| if (!retval && c->n_fc > 1 && c->strict_std_compliance && | |
| atomic_load(&f->refp[i].progress[2]) == FRAME_ERROR) | |
| { | |
| retval = DAV2D_ERR(EINVAL); | |
| atomic_store(&f->task_thread.error, 1); | |
| atomic_store(&f->cur.progress[2], FRAME_ERROR); | |
| } | |
| dav2d_thread_picture_unref(&f->refp[i]); | |
| } | |
| dav2d_ref_dec(&f->ref_mvs_ref[i]); | |
| } | |
| dav2d_thread_picture_unref(&f->cur); | |
| dav2d_cdf_thread_unref(&f->in_cdf); | |
| if (f->frame_hdr && f->use_pri_sec_cdf) { | |
| dav2d_cdf_thread_unref(&f->src_cdf[0]); | |
| dav2d_cdf_thread_unref(&f->src_cdf[1]); | |
| } | |
| if (f->frame_hdr && f->frame_hdr->tip.frame_mode != 2) { | |
| if (f->out_cdf.progress) | |
| atomic_store(f->out_cdf.progress, retval == 0 ? 1 : TILE_ERROR); | |
| dav2d_cdf_thread_unref(&f->out_cdf); | |
| } | |
| dav2d_ref_dec(&f->cur_segmap_ref); | |
| dav2d_ref_dec(&f->prev_segmap_ref); | |
| dav2d_mem_pool_push(f->c->segmap_uv_pool, f->lf.segmap_uv); | |
| f->lf.segmap_uv = NULL; | |
| dav2d_ref_dec(&f->mvs_ref); | |
| dav2d_ref_dec(&f->cur_ccsomap_ref); | |
| for (int p = 0; p < 3; p++) | |
| dav2d_ref_dec(&f->prev_ccsomap_ref[p]); | |
| dav2d_ref_dec(&f->seq_hdr_ref); | |
| dav2d_ref_dec(&f->frame_hdr_ref); | |
| for (int i = 0; i < f->n_tile_data; i++) | |
| dav2d_data_unref_internal(&f->tile[i].data); | |
| f->task_thread.retval = retval; | |
| } | |
| int dav2d_decode_frame(Dav2dFrameContext *const f) { | |
| const Dav2dContext *const c = f->c; | |
| assert(c->n_fc == 1); | |
| // if n_tc > 1 (but n_fc == 1), we could run init/exit in the task | |
| // threads also. Not sure it makes a measurable difference. | |
| int res = dav2d_decode_frame_init(f); | |
| if (!res) { | |
| if (f->frame_hdr->tip.frame_mode != 2) { | |
| res = dav2d_decode_frame_init_cdf(f); | |
| } else { | |
| dav2d_decode_tip_frame_init(f); | |
| } | |
| } | |
| // wait until all threads have completed | |
| if (!res) { | |
| if (c->n_tc > 1) { | |
| const int n_passes = c->task_thread.n_passes; | |
| for (int p = 0; p < n_passes && !res; p++) | |
| res = dav2d_task_create_tile_sbrow(f, p, 1); | |
| pthread_mutex_lock(&f->task_thread.ttd->lock); | |
| pthread_cond_signal(&f->task_thread.ttd->cond); | |
| if (!res) { | |
| while (!f->task_thread.done[0] || | |
| atomic_load(&f->task_thread.task_counter) > 0) | |
| { | |
| pthread_cond_wait(&f->task_thread.cond, | |
| &f->task_thread.ttd->lock); | |
| } | |
| } | |
| pthread_mutex_unlock(&f->task_thread.ttd->lock); | |
| res = f->task_thread.retval; | |
| } else { | |
| res = dav2d_decode_frame_main(f); | |
| if (!res && !f->frame_hdr->disable_cdf_update && | |
| (f->task_thread.update_set || f->seq_hdr->avg_cdf_type)) | |
| { | |
| dav2d_decode_frame_update_cdf(f); | |
| } | |
| } | |
| } | |
| dav2d_decode_frame_exit(f, res); | |
| res = f->task_thread.retval; | |
| f->n_tile_data = 0; | |
| return res; | |
| } | |
| int dav2d_submit_frame(Dav2dContext *const c) { | |
| Dav2dFrameContext *f; | |
| int res = -1; | |
| if (c->n_fc > 1) { | |
| pthread_mutex_lock(&c->task_thread.lock); | |
| const unsigned next = c->frame_thread.next++; | |
| if (c->frame_thread.next == c->n_fc) | |
| c->frame_thread.next = 0; | |
| f = &c->fc[next]; | |
| while (f->n_tile_data != 0) | |
| pthread_cond_wait(&f->task_thread.cond, | |
| &c->task_thread.lock); | |
| if (f->frame_thread.scheduled) { | |
| unsigned first = atomic_load(&c->task_thread.first); | |
| if (first + 1U < c->n_fc) | |
| atomic_fetch_add(&c->task_thread.first, 1U); | |
| else | |
| atomic_store(&c->task_thread.first, 0); | |
| atomic_compare_exchange_strong(&c->task_thread.reset_task_cur, | |
| &first, UINT_MAX); | |
| if (c->task_thread.cur && c->task_thread.cur < c->n_fc) | |
| c->task_thread.cur--; | |
| } | |
| f->frame_thread.scheduled = 1; | |
| // FIXME forward error code for frame to dpb->res | |
| #if 0 | |
| const int error = f->task_thread.retval; | |
| if (error) { | |
| f->task_thread.retval = 0; | |
| c->cached_error = error; | |
| } | |
| #endif | |
| } else { | |
| f = c->fc; | |
| } | |
| struct OutputQueue *q = NULL; | |
| f->seq_hdr = c->seq_hdr; | |
| f->seq_hdr_ref = c->seq_hdr_ref; | |
| dav2d_ref_inc(f->seq_hdr_ref); | |
| f->frame_hdr = c->frame_hdr; | |
| f->frame_hdr_ref = c->frame_hdr_ref; | |
| c->frame_hdr = NULL; | |
| c->frame_hdr_ref = NULL; | |
| f->dsp = &c->dsp[f->seq_hdr->hbd]; | |
| const int bpc = 8 + 2 * f->seq_hdr->hbd; | |
| if (!f->dsp->ipred.intra_pred[DC_PRED]) { | |
| Dav2dDSPContext *const dsp = &c->dsp[f->seq_hdr->hbd]; | |
| switch (bpc) { | |
| #define assign_bitdepth_case(bd) \ | |
| dav2d_ccso_dsp_init_##bd##bpc(&dsp->ccso); \ | |
| dav2d_cdef_dsp_init_##bd##bpc(&dsp->cdef); \ | |
| dav2d_intra_pred_dsp_init_##bd##bpc(&dsp->ipred); \ | |
| dav2d_itx_dsp_init_##bd##bpc(&dsp->itx); \ | |
| dav2d_stx_dsp_init_##bd##bpc(&dsp->stx); \ | |
| dav2d_deblock_dsp_init_##bd##bpc(&dsp->lf); \ | |
| dav2d_loop_restoration_dsp_init_##bd##bpc(&dsp->lr); \ | |
| dav2d_mc_dsp_init_##bd##bpc(&dsp->mc); \ | |
| dav2d_film_grain_dsp_init_##bd##bpc(&dsp->fg); \ | |
| break | |
| #if CONFIG_8BPC | |
| case 8: | |
| assign_bitdepth_case(8); | |
| #endif | |
| #if CONFIG_16BPC | |
| case 10: | |
| case 12: | |
| assign_bitdepth_case(16); | |
| #endif | |
| #undef assign_bitdepth_case | |
| default: | |
| dav2d_log(c, "Compiled without support for %d-bit decoding\n", | |
| 8 + 2 * f->seq_hdr->hbd); | |
| res = DAV2D_ERR(ENOPROTOOPT); | |
| goto error; | |
| } | |
| } | |
| #define assign_bitdepth_case(bd) \ | |
| f->bd_fn.recon_b = dav2d_recon_b_##bd##bpc; \ | |
| f->bd_fn.filter_slice = dav2d_filter_slice_##bd##bpc; \ | |
| f->bd_fn.filter_slice_deblock_cols = dav2d_filter_slice_deblock_cols_##bd##bpc; \ | |
| f->bd_fn.filter_slice_deblock_rows = dav2d_filter_slice_deblock_rows_##bd##bpc; \ | |
| f->bd_fn.filter_slice_cdef = dav2d_filter_slice_cdef_##bd##bpc; \ | |
| f->bd_fn.filter_slice_lr = dav2d_filter_slice_lr_##bd##bpc; \ | |
| f->bd_fn.backup_prefilter_data = dav2d_backup_prefilter_data_##bd##bpc; \ | |
| f->bd_fn.read_coef_blocks = dav2d_read_coef_blocks_##bd##bpc; \ | |
| f->bd_fn.copy_pal_block_y = dav2d_copy_pal_block_y_##bd##bpc; \ | |
| f->bd_fn.read_pal_plane = dav2d_read_pal_plane_##bd##bpc | |
| if (!f->seq_hdr->hbd) { | |
| #if CONFIG_8BPC | |
| assign_bitdepth_case(8); | |
| #endif | |
| } else { | |
| #if CONFIG_16BPC | |
| assign_bitdepth_case(16); | |
| #endif | |
| } | |
| #undef assign_bitdepth_case | |
| int ref_coded_width[7]; | |
| if (IS_INTER_OR_SWITCH(f->frame_hdr)) { | |
| for (int i = 0; i < 7; i++) { | |
| const int refidx = f->frame_hdr->refidx[i]; | |
| if (!c->refs[refidx].p.p.data[0] || | |
| f->frame_hdr->width * 2 < c->refs[refidx].p.p.p.w || | |
| f->frame_hdr->height * 2 < c->refs[refidx].p.p.p.h || | |
| f->frame_hdr->width > c->refs[refidx].p.p.p.w * 16 || | |
| f->frame_hdr->height > c->refs[refidx].p.p.p.h * 16 || | |
| f->seq_hdr->layout != c->refs[refidx].p.p.p.layout || | |
| bpc != c->refs[refidx].p.p.p.bpc) | |
| { | |
| for (int j = 0; j < i; j++) | |
| dav2d_thread_picture_unref(&f->refp[j]); | |
| res = DAV2D_ERR(EINVAL); | |
| goto error; | |
| } | |
| dav2d_thread_picture_ref(&f->refp[i], &c->refs[refidx].p); | |
| ref_coded_width[i] = c->refs[refidx].p.p.frame_hdr->width; | |
| if (f->frame_hdr->width != c->refs[refidx].p.p.p.w || | |
| f->frame_hdr->height != c->refs[refidx].p.p.p.h) | |
| { | |
| #define scale_fac(ref_sz, this_sz) \ | |
| ((((ref_sz) << 14) + ((this_sz) >> 1)) / (this_sz)) | |
| f->svc[i][0].scale = scale_fac(c->refs[refidx].p.p.p.w, | |
| f->frame_hdr->width); | |
| f->svc[i][1].scale = scale_fac(c->refs[refidx].p.p.p.h, | |
| f->frame_hdr->height); | |
| f->svc[i][0].step = (f->svc[i][0].scale + 8) >> 4; | |
| f->svc[i][1].step = (f->svc[i][1].scale + 8) >> 4; | |
| } else { | |
| f->svc[i][0].scale = f->svc[i][1].scale = 0; | |
| } | |
| f->gmv_warp_allowed[i] = f->frame_hdr->gmv.m[i].type > DAV2D_WM_TYPE_TRANSLATION && | |
| !f->frame_hdr->force_integer_mv && | |
| !dav2d_get_shear_params(&f->frame_hdr->gmv.m[i]) && | |
| !f->svc[i][0].scale; | |
| } | |
| // Combine opfl/smvr header conditions with S-frame check, because | |
| // these features are disabled in S-frames, but the header bits exist | |
| if (f->frame_hdr->frame_type == DAV2D_FRAME_TYPE_SWITCH) { | |
| f->no_switch_opfl_refine_type = f->no_switch_refine_mv = 0; | |
| } else { | |
| f->no_switch_opfl_refine_type = f->frame_hdr->opfl_refine_type; | |
| f->no_switch_refine_mv = f->seq_hdr->refine_mv; | |
| } | |
| } | |
| // setup entropy | |
| const int p_ref_idx = f->frame_hdr->primary_ref_frame; | |
| if (p_ref_idx == DAV2D_PRIMARY_REF_NONE || | |
| f->frame_hdr->no_cross_frame_context) | |
| { | |
| dav2d_cdf_thread_init_static(&f->in_cdf, f->frame_hdr->quant.yac, | |
| f->seq_hdr->reduced_tx_part_set); | |
| f->use_pri_sec_cdf = 0; | |
| } else { | |
| const int s_ref_idx = f->frame_hdr->secondary_ref_frame; | |
| const int pri_ref = f->frame_hdr->refidx[p_ref_idx]; | |
| f->use_pri_sec_cdf = s_ref_idx != DAV2D_PRIMARY_REF_NONE && | |
| f->frame_hdr->frame_type == DAV2D_FRAME_TYPE_INTER && | |
| f->seq_hdr->avg_cdf && !f->seq_hdr->avg_cdf_type && | |
| f->frame_hdr->tip.frame_mode != 2; | |
| if (!f->use_pri_sec_cdf) { | |
| dav2d_cdf_thread_ref(&f->in_cdf, &c->cdf[pri_ref]); | |
| } else { | |
| const int sec_ref = f->frame_hdr->refidx[s_ref_idx]; | |
| res = dav2d_cdf_thread_alloc(c, &f->in_cdf, c->n_fc > 1); | |
| if (res < 0) goto error; | |
| dav2d_cdf_thread_ref(&f->src_cdf[0], &c->cdf[pri_ref]); | |
| dav2d_cdf_thread_ref(&f->src_cdf[1], &c->cdf[sec_ref]); | |
| } | |
| } | |
| if (f->frame_hdr->tip.frame_mode != 2) { | |
| res = dav2d_cdf_thread_alloc(c, &f->out_cdf, c->n_fc > 1); | |
| if (res < 0) goto error; | |
| } | |
| // FIXME qsort so tiles are in order (for frame threading) | |
| if (f->n_tile_data_alloc < c->n_tile_data) { | |
| dav2d_free(f->tile); | |
| assert(c->n_tile_data < INT_MAX / (int)sizeof(*f->tile)); | |
| f->tile = dav2d_malloc(ALLOC_TILE, c->n_tile_data * sizeof(*f->tile)); | |
| if (!f->tile) { | |
| f->n_tile_data_alloc = f->n_tile_data = 0; | |
| res = DAV2D_ERR(ENOMEM); | |
| goto error; | |
| } | |
| f->n_tile_data_alloc = c->n_tile_data; | |
| } | |
| if (f->frame_hdr->tip.frame_mode == 2) { | |
| assert(!c->n_tile_data); | |
| f->n_tile_data = -1; // any non-zero value for threading | |
| } else { | |
| memcpy(f->tile, c->tile, c->n_tile_data * sizeof(*f->tile)); | |
| memset(c->tile, 0, c->n_tile_data * sizeof(*c->tile)); | |
| f->n_tile_data = c->n_tile_data; | |
| c->n_tile_data = 0; | |
| } | |
| // allocate frame | |
| res = dav2d_thread_picture_alloc(c, f, bpc); | |
| if (res < 0) goto error; | |
| if (f->frame_hdr->film_grain.present && c->fgm[f->frame_hdr->film_grain.id]) { | |
| f->cur.p.fgm_ref = c->fgm[f->frame_hdr->film_grain.id]; | |
| dav2d_ref_inc(f->cur.p.fgm_ref); | |
| f->cur.p.fgm = f->cur.p.fgm_ref->data; | |
| } | |
| if (c->ci_ref) { | |
| f->cur.p.ci_ref = c->ci_ref; | |
| dav2d_ref_inc(f->cur.p.ci_ref); | |
| f->cur.p.ci = f->cur.p.ci_ref->data; | |
| } | |
| // move f->cur into output queue | |
| if (f->frame_hdr->show_immediate || c->output_invisible_frames) { | |
| q = dav2d_queue_output(c, &f->cur, 0); | |
| #if 0 | |
| c->event_flags |= dav2d_picture_get_event_flags(&f->cur); | |
| #endif | |
| } | |
| // ss_ver is set for 4:2:0, and ss_hor for 4:2:0 & 4:2:2 | |
| f->ss_ver = f->cur.p.p.layout == DAV2D_PIXEL_LAYOUT_I420; | |
| f->ss_hor = f->cur.p.p.layout - 1 < (unsigned) DAV2D_PIXEL_LAYOUT_I444 - 1; | |
| f->root_bs = (const uint8_t[]) { BS_64x64, BS_128x128, | |
| BS_256x256 }[f->frame_hdr->sb128]; | |
| f->bw = ((f->frame_hdr->width + 7) >> 3) << 1; | |
| f->bh = ((f->frame_hdr->height + 7) >> 3) << 1; | |
| f->sb256w = (f->bw + 63) >> 6; | |
| f->sb256h = (f->bh + 63) >> 6; | |
| f->sb64h = (f->bh + 15) >> 4; | |
| f->sb_shift = 4 + f->frame_hdr->sb128; | |
| f->sb_step = 16 << f->frame_hdr->sb128; | |
| f->sbh = (f->bh + f->sb_step - 1) >> f->sb_shift; | |
| f->b4_stride = (f->bw + 63) & ~63; | |
| f->bitdepth_max = (1 << f->cur.p.p.bpc) - 1; | |
| f->n_seg = f->frame_hdr->segmentation.enabled ? | |
| 8 << f->seq_hdr->segmentation.ext : 1; | |
| atomic_init(&f->task_thread.error, 0); | |
| const int n_passes = c->task_thread.n_passes; | |
| const int cols = f->frame_hdr->tiling.t.cols; | |
| const int rows = f->frame_hdr->tiling.t.rows; | |
| atomic_store(&f->task_thread.task_counter, | |
| cols * rows * n_passes + f->sb64h + f->sbh * (n_passes > 1)); | |
| // ref_mvs | |
| if (IS_INTER_OR_SWITCH(f->frame_hdr) || f->frame_hdr->allow_intrabc) { | |
| if (f->seq_hdr->ref_frame_mvs) { | |
| f->mvs_ref = dav2d_ref_create_using_pool(c->refmvs_pool, | |
| sizeof(*f->mvs) * f->sb256h * 32 * (f->b4_stride >> 6)); | |
| if (!f->mvs_ref) { | |
| res = DAV2D_ERR(ENOMEM); | |
| goto error; | |
| } | |
| f->mvs = f->mvs_ref->data; | |
| } else { | |
| f->mvs_ref = NULL; | |
| } | |
| if (IS_INTER_OR_SWITCH(f->frame_hdr)) { | |
| const int poc = f->cur.p.frame_hdr->frame_offset; | |
| // we use -2 here so it doesn't match b->ref==-1, which means intra | |
| int furthest_future_refidx = -2; | |
| for (int i = 0; i < 7; i++) { | |
| f->refpoc[i] = f->refp[i].p.frame_hdr->frame_offset; | |
| const int delta = f->refdist[i] = | |
| get_poc_diff(f->seq_hdr->order_hint_n_bits, f->refpoc[i], poc); | |
| f->absrefdist[i] = abs(delta); | |
| f->refdir[i] = delta > 0; | |
| if (delta > 0 && (furthest_future_refidx < 0 || | |
| f->refdist[furthest_future_refidx] < delta)) | |
| { | |
| furthest_future_refidx = i; | |
| } | |
| } | |
| f->furthest_future_refidx = furthest_future_refidx; | |
| } else { | |
| memset(f->refpoc, 0, sizeof(f->refpoc)); | |
| } | |
| if (f->frame_hdr->use_ref_frame_mvs) { | |
| for (int i = 0; i < 7; i++) { | |
| const int refidx = f->frame_hdr->refidx[i]; | |
| const int ref_w = ((ref_coded_width[i] + 7) >> 3) << 1; | |
| const int ref_h = ((f->refp[i].p.p.h + 7) >> 3) << 1; | |
| if (c->refs[refidx].refmvs != NULL && | |
| ref_w == f->bw && ref_h == f->bh) | |
| { | |
| f->ref_mvs_ref[i] = c->refs[refidx].refmvs; | |
| dav2d_ref_inc(f->ref_mvs_ref[i]); | |
| f->ref_mvs[i] = c->refs[refidx].refmvs->data; | |
| } else { | |
| f->ref_mvs[i] = NULL; | |
| f->ref_mvs_ref[i] = NULL; | |
| } | |
| memcpy(f->refrefpoc[i], c->refs[refidx].refpoc, | |
| sizeof(*f->refrefpoc)); | |
| f->refcnt[i] = f->refp[i].p.frame_hdr->n_ref_frames; | |
| } | |
| } else { | |
| memset(f->ref_mvs_ref, 0, sizeof(f->ref_mvs_ref)); | |
| } | |
| } else { | |
| f->mvs_ref = NULL; | |
| memset(f->ref_mvs_ref, 0, sizeof(f->ref_mvs_ref)); | |
| } | |
| // segmap | |
| f->entropyref_mask = 0; | |
| if (f->frame_hdr->segmentation.enabled) { | |
| // By default, the previous segmentation map is not initialised. | |
| f->prev_segmap_ref = NULL; | |
| f->prev_segmap = NULL; | |
| // We might need a previous frame's segmentation map. This | |
| // happens if there is either no update or a temporal update. | |
| if (f->frame_hdr->segmentation.temporal || | |
| !f->frame_hdr->segmentation.update_map) | |
| { | |
| const int pri_ref = f->frame_hdr->primary_ref_frame; | |
| assert(pri_ref != DAV2D_PRIMARY_REF_NONE); | |
| const int ref_w = ((ref_coded_width[pri_ref] + 7) >> 3) << 1; | |
| const int ref_h = ((f->refp[pri_ref].p.p.h + 7) >> 3) << 1; | |
| if (ref_w == f->bw && ref_h == f->bh) { | |
| f->prev_segmap_ref = c->refs[f->frame_hdr->refidx[pri_ref]].segmap; | |
| if (f->prev_segmap_ref) { | |
| dav2d_ref_inc(f->prev_segmap_ref); | |
| f->prev_segmap = f->prev_segmap_ref->data; | |
| f->entropyref_mask |= 1 << pri_ref; | |
| } | |
| } | |
| } | |
| const size_t segmap_size = sizeof(*f->cur_segmap) * f->b4_stride * 64 * f->sb256h; | |
| f->cur_segmap_ref = dav2d_ref_create_using_pool(c->segmap_pool, segmap_size); | |
| if (!f->cur_segmap_ref) { | |
| if (f->prev_segmap_ref) | |
| dav2d_ref_dec(&f->prev_segmap_ref); | |
| res = DAV2D_ERR(ENOMEM); | |
| goto error; | |
| } | |
| f->cur_segmap = f->cur_segmap_ref->data; | |
| if (!f->frame_hdr->segmentation.update_map && !f->prev_segmap_ref) { | |
| // We need a fresh segmentation map, zero out the segmentation map | |
| memset(f->cur_segmap, 0, segmap_size); | |
| } | |
| f->lf.uv_segmap_stride = f->sb256w * (64 >> f->ss_hor); | |
| const size_t segmap_uv_size = sizeof(*f->lf.segmap_uv) * f->lf.uv_segmap_stride * | |
| f->sb256h * (64 >> f->ss_ver); | |
| void *buf = dav2d_mem_pool_pop(c->segmap_uv_pool, segmap_uv_size); | |
| if (!buf) { | |
| res = DAV2D_ERR(ENOMEM); | |
| goto error; | |
| } | |
| f->lf.segmap_uv = buf; | |
| } else { | |
| f->cur_segmap = NULL; | |
| f->cur_segmap_ref = NULL; | |
| f->prev_segmap_ref = NULL; | |
| f->lf.segmap_uv = NULL; | |
| f->lf.uv_segmap_stride = 0; | |
| } | |
| // CCSO map | |
| for (int p = 0; p < 3; p++) | |
| f->prev_ccsomap_ref[p] = NULL; | |
| if (f->frame_hdr->ccso.enabled) { | |
| const int n_planes = f->seq_hdr->layout == DAV2D_PIXEL_LAYOUT_I400 ? 1 : 3; | |
| for (int p = 0; p < n_planes; p++) { | |
| if (!f->frame_hdr->ccso.p[p].sb_reuse) continue; | |
| const int ref = f->frame_hdr->ccso.p[p].refidx; | |
| f->entropyref_mask |= 1 << ref; | |
| f->prev_ccsomap_ref[p] = c->refs[f->frame_hdr->refidx[ref]].ccsomap; | |
| dav2d_ref_inc(f->prev_ccsomap_ref[p]); | |
| f->prev_ccsomap[p] = f->prev_ccsomap_ref[p]->data; | |
| f->prev_ccsomap[p] += p * f->sb256w * f->sb256h * 16; | |
| } | |
| if (f->frame_hdr->ccso.p[0].sb_reuse && | |
| (n_planes == 1 || | |
| (f->frame_hdr->ccso.p[1].sb_reuse && f->frame_hdr->ccso.p[2].sb_reuse && | |
| f->frame_hdr->ccso.p[0].refidx == f->frame_hdr->ccso.p[1].refidx && | |
| f->frame_hdr->ccso.p[0].refidx == f->frame_hdr->ccso.p[2].refidx))) | |
| { | |
| f->cur_ccsomap_ref = f->prev_ccsomap_ref[0]; | |
| dav2d_ref_inc(f->cur_ccsomap_ref); | |
| f->cur_ccsomap = NULL; | |
| } else { | |
| const size_t ccsomap_size = sizeof(*f->cur_ccsomap) * 3 * f->sb256w * f->sb256h * 4 * 4; | |
| f->cur_ccsomap_ref = dav2d_ref_create_using_pool(c->ccsomap_pool, ccsomap_size); | |
| if (!f->cur_ccsomap_ref) { | |
| res = DAV2D_ERR(ENOMEM); | |
| goto error; | |
| } | |
| f->cur_ccsomap = f->cur_ccsomap_ref->data; | |
| } | |
| } else { | |
| f->cur_ccsomap = NULL; | |
| f->cur_ccsomap_ref = NULL; | |
| } | |
| // skipmode | |
| f->skip_mode_refs.ref[0] = 0; | |
| f->skip_mode_refs.ref[1] = f->frame_hdr->skip_mode_enabled && | |
| f->frame_hdr->n_ref_frames > 1 && | |
| abs(f->absrefdist[0] - f->absrefdist[1]) <= 1; | |
| // update references etc. | |
| const unsigned refresh_frame_flags = f->frame_hdr->refresh_frame_flags; | |
| for (int i = 0; i < DAV2D_NUM_REF_FRAMES; i++) { | |
| if (refresh_frame_flags & (1 << i)) { | |
| if (c->refs[i].p.p.frame_hdr) | |
| dav2d_thread_picture_unref(&c->refs[i].p); | |
| dav2d_thread_picture_ref(&c->refs[i].p, &f->cur); | |
| dav2d_cdf_thread_unref(&c->cdf[i]); | |
| if (f->frame_hdr->tip.frame_mode != 2) { | |
| dav2d_cdf_thread_ref(&c->cdf[i], &f->out_cdf); | |
| } else { | |
| dav2d_cdf_thread_ref(&c->cdf[i], &f->in_cdf); | |
| } | |
| dav2d_ref_dec(&c->refs[i].segmap); | |
| c->refs[i].segmap = f->frame_hdr->segmentation.update_map ? | |
| f->cur_segmap_ref : f->prev_segmap_ref; | |
| if (c->refs[i].segmap) | |
| dav2d_ref_inc(c->refs[i].segmap); | |
| dav2d_ref_dec(&c->refs[i].refmvs); | |
| if (IS_INTER_OR_SWITCH(f->frame_hdr)) { | |
| c->refs[i].refmvs = f->mvs_ref; | |
| if (f->mvs_ref) | |
| dav2d_ref_inc(f->mvs_ref); | |
| } | |
| dav2d_ref_dec(&c->refs[i].ccsomap); | |
| c->refs[i].ccsomap = f->cur_ccsomap_ref; | |
| if (f->cur_ccsomap_ref) | |
| dav2d_ref_inc(f->cur_ccsomap_ref); | |
| memcpy(c->refs[i].refpoc, f->refpoc, sizeof(f->refpoc)); | |
| } | |
| } | |
| if (c->n_fc == 1) { | |
| if ((res = dav2d_decode_frame(f)) < 0) { | |
| for (int i = 0; i < DAV2D_NUM_REF_FRAMES; i++) { | |
| if (refresh_frame_flags & (1 << i)) { | |
| if (c->refs[i].p.p.frame_hdr) | |
| dav2d_thread_picture_unref(&c->refs[i].p); | |
| dav2d_cdf_thread_unref(&c->cdf[i]); | |
| dav2d_ref_dec(&c->refs[i].segmap); | |
| dav2d_ref_dec(&c->refs[i].refmvs); | |
| dav2d_ref_dec(&c->refs[i].ccsomap); | |
| } | |
| } | |
| goto error; | |
| } | |
| } else { | |
| dav2d_task_frame_init(f); | |
| pthread_mutex_unlock(&c->task_thread.lock); | |
| } | |
| return 0; | |
| error: | |
| atomic_init(&f->task_thread.error, 1); | |
| dav2d_cdf_thread_unref(&f->in_cdf); | |
| if (f->use_pri_sec_cdf) { | |
| dav2d_cdf_thread_unref(&f->src_cdf[0]); | |
| dav2d_cdf_thread_unref(&f->src_cdf[1]); | |
| } | |
| if (f->frame_hdr->tip.frame_mode != 2) | |
| dav2d_cdf_thread_unref(&f->out_cdf); | |
| for (int i = 0; i < 7; i++) { | |
| if (f->refp[i].p.frame_hdr) | |
| dav2d_thread_picture_unref(&f->refp[i]); | |
| dav2d_ref_dec(&f->ref_mvs_ref[i]); | |
| } | |
| // FIXME is it correct to re-report error messages during picture output | |
| // if we already reported them during picture input? | |
| if (q) q->res = res; | |
| dav2d_thread_picture_unref(&f->cur); | |
| dav2d_ref_dec(&f->cur_segmap_ref); | |
| dav2d_ref_dec(&f->prev_segmap_ref); | |
| dav2d_ref_dec(&f->mvs_ref); | |
| dav2d_ref_dec(&f->cur_ccsomap_ref); | |
| for (int p = 0; p < 3; p++) | |
| dav2d_ref_dec(&f->prev_ccsomap_ref[p]); | |
| dav2d_ref_dec(&f->seq_hdr_ref); | |
| dav2d_ref_dec(&f->frame_hdr_ref); | |
| #if 0 | |
| dav2d_data_props_copy(&c->cached_error_props, &c->in.m); | |
| #endif | |
| for (int i = 0; i < f->n_tile_data; i++) | |
| dav2d_data_unref_internal(&f->tile[i].data); | |
| f->n_tile_data = 0; | |
| if (c->n_fc > 1) | |
| pthread_mutex_unlock(&c->task_thread.lock); | |
| return res; | |
| } |