Introduce CONFIG_CROSS_CHROMA_TX experiment STATS_CHANGED Change-Id: Id649b41d3d3ddccd6e594d31999c695622d576e2
diff --git a/av1/common/av1_txfm.c b/av1/common/av1_txfm.c index 0fe1e7f..a38b11d 100644 --- a/av1/common/av1_txfm.c +++ b/av1/common/av1_txfm.c
@@ -196,6 +196,11 @@ }; #endif // CONFIG_DST_32X32 +#if CONFIG_CROSS_CHROMA_TX +// Haar transform [1, 1; 1, -1] * 1/sqrt(2) * (1<<CCTX_PREC_BITS) +const int32_t cctx_mtx[4] = { 181, 181, 181, -181 }; +#endif // CONFIG_CROSS_CHROMA_TX + // av1_sinpi_arr_data[i][j] = (int)round((sqrt(2) * sin(j*Pi/9) * 2 / 3) * (1 // << (cos_bit_min + i))) modified so that elements j=1,2 sum to element j=4. const int32_t av1_sinpi_arr_data[7][5] = {
diff --git a/av1/common/av1_txfm.h b/av1/common/av1_txfm.h index 5958434..f68cf5e 100644 --- a/av1/common/av1_txfm.h +++ b/av1/common/av1_txfm.h
@@ -49,6 +49,12 @@ #define KLT_PREC_BITS 10 #endif // CONFIG_DDT_INTER +#if CONFIG_CROSS_CHROMA_TX +#define CCTX_DC_ONLY 0 +#define CCTX_PREC_BITS 8 +extern const int32_t cctx_mtx[4]; +#endif // CONFIG_CROSS_CHROMA_TX + #define MAX_TXFM_STAGE_NUM 12 static const int cos_bit_min = 10;
diff --git a/av1/common/idct.c b/av1/common/idct.c index 7f97bf6..7e56a68 100644 --- a/av1/common/idct.c +++ b/av1/common/idct.c
@@ -320,6 +320,29 @@ } } +#if CONFIG_CROSS_CHROMA_TX +void av1_inv_cross_chroma_tx_block(tran_low_t *dqcoeff_u, tran_low_t *dqcoeff_v, + TX_SIZE tx_size) { +#if CCTX_DC_ONLY + const int ncoeffs = 1; +#else + const int ncoeffs = av1_get_max_eob(tx_size); +#endif + // TODO(kslu): check if there is any overflow issue + // TODO(kslu): keep track of the EOB before fwd and after inv cctx + int32_t *src_u = (int32_t *)dqcoeff_u; + int32_t *src_v = (int32_t *)dqcoeff_v; + int32_t tmp[2] = { 0, 0 }; + + for (int i = 0; i < ncoeffs; i++) { + tmp[0] = cctx_mtx[0] * src_u[i] + cctx_mtx[2] * src_v[i]; + tmp[1] = cctx_mtx[1] * src_u[i] + cctx_mtx[3] * src_v[i]; + src_u[i] = ROUND_POWER_OF_TWO_SIGNED(tmp[0], CCTX_PREC_BITS); + src_v[i] = ROUND_POWER_OF_TWO_SIGNED(tmp[1], CCTX_PREC_BITS); + } +} +#endif // CONFIG_CROSS_CHROMA_TX + void av1_inverse_transform_block(const MACROBLOCKD *xd, #if CONFIG_IST tran_low_t *dqcoeff,
diff --git a/av1/common/idct.h b/av1/common/idct.h index 216f5ca..62b5c62 100644 --- a/av1/common/idct.h +++ b/av1/common/idct.h
@@ -33,6 +33,11 @@ #define MAX_TX_SCALE 1 int av1_get_tx_scale(const TX_SIZE tx_size); +#if CONFIG_CROSS_CHROMA_TX +void av1_inv_cross_chroma_tx_block(tran_low_t *dqcoeff_u, tran_low_t *dqcoeff_v, + TX_SIZE tx_size); +#endif // CONFIG_CROSS_CHROMA_TX + void av1_inverse_transform_block(const MACROBLOCKD *xd, #if CONFIG_IST tran_low_t *dqcoeff,
diff --git a/av1/decoder/decodeframe.c b/av1/decoder/decodeframe.c index 5ddf573..1c4664d 100644 --- a/av1/decoder/decodeframe.c +++ b/av1/decoder/decodeframe.c
@@ -158,8 +158,24 @@ tran_low_t *const dqcoeff = dcb->dqcoeff_block[plane] + dcb->cb_offset[plane]; #endif eob_info *eob_data = dcb->eob_data[plane] + dcb->txb_offset[plane]; +#if CONFIG_CROSS_CHROMA_TX + // TODO(kslu): keep track of transform domain eobs of U and V + eob_info *eob_data_u = + dcb->eob_data[AOM_PLANE_U] + dcb->txb_offset[AOM_PLANE_U]; + eob_info *eob_data_v = + dcb->eob_data[AOM_PLANE_V] + dcb->txb_offset[AOM_PLANE_V]; + uint16_t scan_line = (plane == 0) ? eob_data->max_scan_line + : AOMMIN(av1_get_max_eob(tx_size), + AOMMAX(eob_data_u->max_scan_line, + eob_data_v->max_scan_line)); + uint16_t eob = (plane == 0) + ? eob_data->eob + : AOMMIN(av1_get_max_eob(tx_size), + AOMMAX(eob_data_u->eob, eob_data_v->eob)); +#else uint16_t scan_line = eob_data->max_scan_line; uint16_t eob = eob_data->eob; +#endif // CONFIG_CROSS_CHROMA_TX av1_inverse_transform_block(&dcb->xd, dqcoeff, plane, tx_type, tx_size, dst, stride, eob, reduced_tx_set); #if CONFIG_IST @@ -249,6 +265,24 @@ } } +#if CONFIG_CROSS_CHROMA_TX +static AOM_INLINE void inverse_cross_chroma_transform_block( + const AV1_COMMON *const cm, DecoderCodingBlock *dcb, aom_reader *const r, + const int plane, const int blk_row, const int blk_col, + const TX_SIZE tx_size) { + (void)cm; + (void)r; + (void)plane; + (void)blk_row; + (void)blk_col; + tran_low_t *dqcoeff_u = + dcb->dqcoeff_block[AOM_PLANE_U] + dcb->cb_offset[AOM_PLANE_U]; + tran_low_t *dqcoeff_v = + dcb->dqcoeff_block[AOM_PLANE_V] + dcb->cb_offset[AOM_PLANE_V]; + av1_inv_cross_chroma_tx_block(dqcoeff_u, dqcoeff_v, tx_size); +} +#endif // CONFIG_CROSS_CHROMA_TX + static AOM_INLINE void inverse_transform_inter_block( const AV1_COMMON *const cm, DecoderCodingBlock *dcb, aom_reader *const r, const int plane, const int blk_row, const int blk_col, @@ -291,6 +325,9 @@ AV1_COMMON *cm, ThreadData *const td, aom_reader *r, MB_MODE_INFO *const mbmi, int plane, BLOCK_SIZE plane_bsize, int blk_row, int blk_col, int block, TX_SIZE tx_size, int *eob_total) { +#if CONFIG_CROSS_CHROMA_TX + if (plane == AOM_PLANE_U) return; +#endif // CONFIG_CROSS_CHROMA_TX DecoderCodingBlock *const dcb = &td->dcb; MACROBLOCKD *const xd = &dcb->xd; const struct macroblockd_plane *const pd = &xd->plane[plane]; @@ -308,6 +345,39 @@ if (blk_row >= max_blocks_high || blk_col >= max_blocks_wide) return; if (tx_size == plane_tx_size || plane) { +#if CONFIG_CROSS_CHROMA_TX + switch (plane) { + case AOM_PLANE_Y: + td->read_coeffs_tx_inter_block_visit(cm, dcb, r, plane, blk_row, + blk_col, tx_size); + + td->inverse_tx_inter_block_visit(cm, dcb, r, plane, blk_row, blk_col, + tx_size); + eob_info *eob_data = dcb->eob_data[plane] + dcb->txb_offset[plane]; + *eob_total += eob_data->eob; + set_cb_buffer_offsets(dcb, tx_size, plane); + break; + case AOM_PLANE_V: + td->read_coeffs_tx_inter_block_visit(cm, dcb, r, AOM_PLANE_U, blk_row, + blk_col, tx_size); + td->read_coeffs_tx_inter_block_visit(cm, dcb, r, AOM_PLANE_V, blk_row, + blk_col, tx_size); + td->inverse_cctx_block_visit(cm, dcb, r, -1, blk_row, blk_col, tx_size); + td->inverse_tx_inter_block_visit(cm, dcb, r, AOM_PLANE_U, blk_row, + blk_col, tx_size); + td->inverse_tx_inter_block_visit(cm, dcb, r, AOM_PLANE_V, blk_row, + blk_col, tx_size); + eob_info *eob_data_u = + dcb->eob_data[AOM_PLANE_U] + dcb->txb_offset[AOM_PLANE_U]; + eob_info *eob_data_v = + dcb->eob_data[AOM_PLANE_V] + dcb->txb_offset[AOM_PLANE_V]; + *eob_total += eob_data_u->eob + eob_data_v->eob; + set_cb_buffer_offsets(dcb, tx_size, AOM_PLANE_U); + set_cb_buffer_offsets(dcb, tx_size, AOM_PLANE_V); + break; + case AOM_PLANE_U: assert(0); break; + } +#else td->read_coeffs_tx_inter_block_visit(cm, dcb, r, plane, blk_row, blk_col, tx_size); @@ -316,6 +386,7 @@ eob_info *eob_data = dcb->eob_data[plane] + dcb->txb_offset[plane]; *eob_total += eob_data->eob; set_cb_buffer_offsets(dcb, tx_size, plane); +#endif // CONFIG_CROSS_CHROMA_TX } else { #if CONFIG_NEW_TX_PARTITION TX_SIZE sub_txs[MAX_TX_PARTITIONS] = { 0 }; @@ -1161,104 +1232,94 @@ const int plane_start = get_partition_plane_start(xd->tree_type); const int plane_end = get_partition_plane_end(xd->tree_type, av1_num_planes(cm)); - if (!is_inter_block(mbmi, xd->tree_type)) { - int row, col; + + int row, col; + const int max_blocks_wide = max_block_wide(xd, bsize, 0); + const int max_blocks_high = max_block_high(xd, bsize, 0); + const BLOCK_SIZE max_unit_bsize = BLOCK_64X64; + int mu_blocks_wide = mi_size_wide[max_unit_bsize]; + int mu_blocks_high = mi_size_high[max_unit_bsize]; + mu_blocks_wide = AOMMIN(max_blocks_wide, mu_blocks_wide); + mu_blocks_high = AOMMIN(max_blocks_high, mu_blocks_high); + + const int is_inter = is_inter_block(mbmi, xd->tree_type); + if (!is_inter) { assert(bsize == get_plane_block_size(bsize, xd->plane[0].subsampling_x, xd->plane[0].subsampling_y)); - const int max_blocks_wide = max_block_wide(xd, bsize, 0); - const int max_blocks_high = max_block_high(xd, bsize, 0); - const BLOCK_SIZE max_unit_bsize = BLOCK_64X64; - int mu_blocks_wide = mi_size_wide[max_unit_bsize]; - int mu_blocks_high = mi_size_high[max_unit_bsize]; - mu_blocks_wide = AOMMIN(max_blocks_wide, mu_blocks_wide); - mu_blocks_high = AOMMIN(max_blocks_high, mu_blocks_high); - - for (row = 0; row < max_blocks_high; row += mu_blocks_high) { - for (col = 0; col < max_blocks_wide; col += mu_blocks_wide) { - for (int plane = plane_start; plane < plane_end; ++plane) { - if (plane && !xd->is_chroma_ref) break; - const struct macroblockd_plane *const pd = &xd->plane[plane]; - const TX_SIZE tx_size = av1_get_tx_size(plane, xd); - const int stepr = tx_size_high_unit[tx_size]; - const int stepc = tx_size_wide_unit[tx_size]; - - const int unit_height = ROUND_POWER_OF_TWO( - AOMMIN(mu_blocks_high + row, max_blocks_high), pd->subsampling_y); - const int unit_width = ROUND_POWER_OF_TWO( - AOMMIN(mu_blocks_wide + col, max_blocks_wide), pd->subsampling_x); - - for (int blk_row = row >> pd->subsampling_y; blk_row < unit_height; - blk_row += stepr) { - for (int blk_col = col >> pd->subsampling_x; blk_col < unit_width; - blk_col += stepc) { - td->read_coeffs_tx_intra_block_visit(cm, dcb, r, plane, blk_row, - blk_col, tx_size); - td->predict_and_recon_intra_block_visit( - cm, dcb, r, plane, blk_row, blk_col, tx_size); - set_cb_buffer_offsets(dcb, tx_size, plane); - } - } - } - } - } } else { td->predict_inter_block_visit(cm, dcb, bsize); - // Reconstruction - if (!mbmi->skip_txfm[xd->tree_type == CHROMA_PART]) { - int eobtotal = 0; - - const int max_blocks_wide = max_block_wide(xd, bsize, 0); - const int max_blocks_high = max_block_high(xd, bsize, 0); - int row, col; - - const BLOCK_SIZE max_unit_bsize = BLOCK_64X64; + if (!mbmi->skip_txfm[xd->tree_type == CHROMA_PART]) assert(max_unit_bsize == get_plane_block_size(BLOCK_64X64, xd->plane[0].subsampling_x, xd->plane[0].subsampling_y)); - int mu_blocks_wide = mi_size_wide[max_unit_bsize]; - int mu_blocks_high = mi_size_high[max_unit_bsize]; + } - mu_blocks_wide = AOMMIN(max_blocks_wide, mu_blocks_wide); - mu_blocks_high = AOMMIN(max_blocks_high, mu_blocks_high); + for (row = 0; row < max_blocks_high; row += mu_blocks_high) { + for (col = 0; col < max_blocks_wide; col += mu_blocks_wide) { + for (int plane = plane_start; plane < plane_end; ++plane) { + if (plane && !xd->is_chroma_ref) break; + const struct macroblockd_plane *const pd = &xd->plane[plane]; + const TX_SIZE tx_size = av1_get_tx_size(plane, xd); + const int ss_x = pd->subsampling_x; + const int ss_y = pd->subsampling_y; + const int unit_height = ROUND_POWER_OF_TWO( + AOMMIN(mu_blocks_high + row, max_blocks_high), ss_y); + const int unit_width = ROUND_POWER_OF_TWO( + AOMMIN(mu_blocks_wide + col, max_blocks_wide), ss_x); - for (row = 0; row < max_blocks_high; row += mu_blocks_high) { - for (col = 0; col < max_blocks_wide; col += mu_blocks_wide) { - for (int plane = plane_start; plane < plane_end; ++plane) { - if (plane && !xd->is_chroma_ref) break; - const struct macroblockd_plane *const pd = &xd->plane[plane]; - const int ss_x = pd->subsampling_x; - const int ss_y = pd->subsampling_y; - const BLOCK_SIZE plane_bsize = - get_plane_block_size(bsize, ss_x, ss_y); - const TX_SIZE max_tx_size = - get_vartx_max_txsize(xd, plane_bsize, plane); - const int bh_var_tx = tx_size_high_unit[max_tx_size]; - const int bw_var_tx = tx_size_wide_unit[max_tx_size]; - int block = 0; - int step = - tx_size_wide_unit[max_tx_size] * tx_size_high_unit[max_tx_size]; - int blk_row, blk_col; - const int unit_height = ROUND_POWER_OF_TWO( - AOMMIN(mu_blocks_high + row, max_blocks_high), ss_y); - const int unit_width = ROUND_POWER_OF_TWO( - AOMMIN(mu_blocks_wide + col, max_blocks_wide), ss_x); - - for (blk_row = row >> ss_y; blk_row < unit_height; - blk_row += bh_var_tx) { - for (blk_col = col >> ss_x; blk_col < unit_width; - blk_col += bw_var_tx) { + const BLOCK_SIZE plane_bsize = get_plane_block_size(bsize, ss_x, ss_y); + const TX_SIZE max_tx_size = + get_vartx_max_txsize(xd, plane_bsize, plane); + const int stepr = is_inter ? tx_size_high_unit[max_tx_size] + : tx_size_high_unit[tx_size]; + const int stepc = is_inter ? tx_size_wide_unit[max_tx_size] + : tx_size_wide_unit[tx_size]; + int eobtotal = 0; + int block = 0; + for (int blk_row = row >> ss_y; blk_row < unit_height; + blk_row += stepr) { + for (int blk_col = col >> ss_x; blk_col < unit_width; + blk_col += stepc) { + if (!is_inter) { + td->read_coeffs_tx_intra_block_visit(cm, dcb, r, plane, blk_row, + blk_col, tx_size); +#if CONFIG_CROSS_CHROMA_TX + switch (plane) { + case AOM_PLANE_Y: + td->predict_and_recon_intra_block_visit( + cm, dcb, r, AOM_PLANE_Y, blk_row, blk_col, tx_size); + set_cb_buffer_offsets(dcb, tx_size, AOM_PLANE_Y); + break; + case AOM_PLANE_U: break; + case AOM_PLANE_V: + td->predict_and_recon_intra_block_visit( + cm, dcb, r, AOM_PLANE_U, blk_row, blk_col, tx_size); + set_cb_buffer_offsets(dcb, tx_size, AOM_PLANE_U); + td->predict_and_recon_intra_block_visit( + cm, dcb, r, AOM_PLANE_V, blk_row, blk_col, tx_size); + set_cb_buffer_offsets(dcb, tx_size, AOM_PLANE_V); + break; + } +#else + td->predict_and_recon_intra_block_visit( + cm, dcb, r, plane, blk_row, blk_col, tx_size); + set_cb_buffer_offsets(dcb, tx_size, plane); +#endif // CONFIG_CROSS_CHROMA_TX + } else { + // Reconstruction + if (!mbmi->skip_txfm[xd->tree_type == CHROMA_PART]) { decode_reconstruct_tx(cm, td, r, mbmi, plane, plane_bsize, blk_row, blk_col, block, max_tx_size, &eobtotal); - block += step; + block += stepr * stepc; } } } } } } - td->cfl_store_inter_block_visit(cm, xd); } + if (is_inter) td->cfl_store_inter_block_visit(cm, xd); av1_visit_palette(pbi, xd, r, set_color_index_map_offset); } @@ -3417,6 +3478,9 @@ td->predict_and_recon_intra_block_visit = decode_block_void; td->read_coeffs_tx_inter_block_visit = decode_block_void; td->inverse_tx_inter_block_visit = decode_block_void; +#if CONFIG_CROSS_CHROMA_TX + td->inverse_cctx_block_visit = decode_block_void; +#endif // CONFIG_CROSS_CHROMA_TX td->predict_inter_block_visit = predict_inter_block_void; td->cfl_store_inter_block_visit = cfl_store_inter_block_void; @@ -3428,6 +3492,9 @@ td->predict_and_recon_intra_block_visit = predict_and_reconstruct_intra_block; td->inverse_tx_inter_block_visit = inverse_transform_inter_block; +#if CONFIG_CROSS_CHROMA_TX + td->inverse_cctx_block_visit = inverse_cross_chroma_transform_block; +#endif // CONFIG_CROSS_CHROMA_TX td->predict_inter_block_visit = predict_inter_block; td->cfl_store_inter_block_visit = cfl_store_inter_block; }
diff --git a/av1/decoder/decoder.h b/av1/decoder/decoder.h index 78b6fc9..5f151cd 100644 --- a/av1/decoder/decoder.h +++ b/av1/decoder/decoder.h
@@ -125,6 +125,9 @@ decode_block_visitor_fn_t read_coeffs_tx_intra_block_visit; decode_block_visitor_fn_t predict_and_recon_intra_block_visit; decode_block_visitor_fn_t read_coeffs_tx_inter_block_visit; +#if CONFIG_CROSS_CHROMA_TX + decode_block_visitor_fn_t inverse_cctx_block_visit; +#endif // CONFIG_CROSS_CHROMA_TX decode_block_visitor_fn_t inverse_tx_inter_block_visit; predict_inter_block_visitor_fn_t predict_inter_block_visit; cfl_store_inter_block_visitor_fn_t cfl_store_inter_block_visit;
diff --git a/av1/encoder/encodemb.c b/av1/encoder/encodemb.c index 003be64..3cc29b1 100644 --- a/av1/encoder/encodemb.c +++ b/av1/encoder/encodemb.c
@@ -273,11 +273,45 @@ const struct macroblock_plane *const p = &x->plane[plane]; const int is_inter = is_inter_block(mbmi, xd->tree_type); #endif // CONFIG_FORWARDSKIP +#if CONFIG_CROSS_CHROMA_TX + if (is_inter_block(x->e_mbd.mi[0], x->e_mbd.tree_type)) { + switch (plane) { + case AOM_PLANE_Y: #if CONFIG_IST - av1_xform(x, plane, block, blk_row, blk_col, plane_bsize, txfm_param, 0); + av1_xform(x, AOM_PLANE_Y, block, blk_row, blk_col, plane_bsize, + txfm_param, 0); +#else + av1_xform(x, AOM_PLANE_Y, block, blk_row, blk_col, plane_bsize, + txfm_param); +#endif + break; + case AOM_PLANE_U: +#if CONFIG_IST + av1_xform(x, AOM_PLANE_U, block, blk_row, blk_col, plane_bsize, + txfm_param, 0); + av1_xform(x, AOM_PLANE_V, block, blk_row, blk_col, plane_bsize, + txfm_param, 0); +#else + av1_xform(x, AOM_PLANE_U, block, blk_row, blk_col, plane_bsize, + txfm_param); + av1_xform(x, AOM_PLANE_V, block, blk_row, blk_col, plane_bsize, + txfm_param); +#endif + forward_cross_chroma_transform(x, block, txfm_param->tx_size); + // TODO(kslu): maybe skip av1_setup_xform for V + break; + case AOM_PLANE_V: break; + } + } else { +#endif // CONFIG_CROSS_CHROMA_TX +#if CONFIG_IST + av1_xform(x, plane, block, blk_row, blk_col, plane_bsize, txfm_param, 0); #else av1_xform(x, plane, block, blk_row, blk_col, plane_bsize, txfm_param); #endif +#if CONFIG_CROSS_CHROMA_TX + } +#endif // CONFIG_CROSS_CHROMA_TX #if CONFIG_FORWARDSKIP const uint8_t fsc_mode = (mbmi->fsc_mode[xd->tree_type == CHROMA_PART] && plane == PLANE_TYPE_Y) || @@ -355,6 +389,17 @@ #endif } +#if CONFIG_CROSS_CHROMA_TX +void forward_cross_chroma_transform(MACROBLOCK *x, int block, TX_SIZE tx_size) { + struct macroblock_plane *const p_u = &x->plane[AOM_PLANE_U]; + struct macroblock_plane *const p_v = &x->plane[AOM_PLANE_V]; + const int block_offset = BLOCK_OFFSET(block); + tran_low_t *coeff_u = p_u->coeff + block_offset; + tran_low_t *coeff_v = p_v->coeff + block_offset; + av1_fwd_cross_chroma_tx_block(coeff_u, coeff_v, tx_size); +} +#endif // CONFIG_CROSS_CHROMA_TX + void av1_quant(MACROBLOCK *x, int plane, int block, TxfmParam *txfm_param, QUANT_PARAM *qparam) { const struct macroblock_plane *const p = &x->plane[plane]; @@ -483,7 +528,7 @@ MB_MODE_INFO *mbmi = xd->mi[0]; struct macroblock_plane *const p = &x->plane[plane]; struct macroblockd_plane *const pd = &xd->plane[plane]; -#if CONFIG_IST +#if CONFIG_IST || CONFIG_CROSS_CHROMA_TX tran_low_t *dqcoeff = p->dqcoeff + BLOCK_OFFSET(block); #else tran_low_t *const dqcoeff = p->dqcoeff + BLOCK_OFFSET(block); @@ -575,13 +620,45 @@ av1_set_txb_context(x, plane, block, tx_size, a, l); - if (p->eobs[block]) { - *(args->skip) = 0; - av1_inverse_transform_block(xd, dqcoeff, plane, tx_type, tx_size, dst, - pd->dst.stride, p->eobs[block], +#if CONFIG_CROSS_CHROMA_TX + // In CONFIG_CROSS_CHROMA_TX, reconstruction for U plane relies on dqcoeffs of + // V plane, so the below operations for U are performed together with V once + // dqcoeffs of V are obtained. + if (is_inter_block(mbmi, xd->tree_type) && plane == AOM_PLANE_U) { + if (p->eobs[block]) *(args->skip) = 0; + return; + } else if (is_inter_block(mbmi, xd->tree_type) && plane == AOM_PLANE_V) { + struct macroblock_plane *const p_u = &x->plane[AOM_PLANE_U]; + tran_low_t *dqcoeff_u = x->plane[AOM_PLANE_U].dqcoeff + BLOCK_OFFSET(block); + struct macroblockd_plane *const pd_u = &xd->plane[AOM_PLANE_U]; + uint8_t *dst_u = + &pd_u->dst.buf[(blk_row * pd_u->dst.stride + blk_col) << MI_SIZE_LOG2]; + av1_inv_cross_chroma_tx_block(dqcoeff_u, dqcoeff, tx_size); + av1_inverse_transform_block(xd, dqcoeff_u, AOM_PLANE_U, tx_type, tx_size, + dst_u, pd_u->dst.stride, + AOMMAX(p_u->eobs[block], p->eobs[block]), cm->features.reduced_tx_set_used); } + // TODO(kslu): keep track of transform domain eobs for U and V + if (p->eobs[block] || (plane && (x->plane[AOM_PLANE_U].eobs[block] || + x->plane[AOM_PLANE_V].eobs[block]))) { +#else + if (p->eobs[block]) { +#endif // CONFIG_CROSS_CHROMA_TX + *(args->skip) = 0; + av1_inverse_transform_block( + xd, dqcoeff, plane, tx_type, tx_size, dst, pd->dst.stride, +#if CONFIG_CROSS_CHROMA_TX + (plane == 0) ? p->eobs[block] + : AOMMAX(x->plane[AOM_PLANE_U].eobs[block], + x->plane[AOM_PLANE_V].eobs[block]), +#else + p->eobs[block], +#endif + cm->features.reduced_tx_set_used); + } + // TODO(debargha, jingning): Temporarily disable txk_type check for eob=0 // case. It is possible that certain collision in hash index would cause // the assertion failure. To further optimize the rate-distortion @@ -611,6 +688,18 @@ int blk_h = block_size_high[bsize]; mi_to_pixel_loc(&pixel_c, &pixel_r, xd->mi_col, xd->mi_row, blk_col, blk_row, pd->subsampling_x, pd->subsampling_y); +#if CONFIG_CROSS_CHROMA_TX + if (plane == AOM_PLANE_V) { + struct macroblockd_plane *const pd_u = &xd->plane[AOM_PLANE_U]; + uint8_t *dst_u = + &pd_u->dst + .buf[(blk_row * pd_u->dst.stride + blk_col) << MI_SIZE_LOG2]; + mismatch_record_block_tx(dst_u, pd_u->dst.stride, + cm->current_frame.order_hint, AOM_PLANE_U, + pixel_c, pixel_r, blk_w, blk_h, + xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH); + } +#endif // CONFIG_CROSS_CHROMA_TX mismatch_record_block_tx(dst, pd->dst.stride, cm->current_frame.order_hint, plane, pixel_c, pixel_r, blk_w, blk_h); } @@ -808,6 +897,17 @@ cpi, x, &ctx, &mbmi->skip_txfm[xd->tree_type == CHROMA_PART], NULL, NULL, dry_run, cpi->optimize_seg_arr[mbmi->segment_id] }; +#if CONFIG_CROSS_CHROMA_TX + // Subtract first, so both U and V residues will be available when U component + // is being transformed and quantized. + for (int plane = plane_start; plane < plane_end; ++plane) { + const struct macroblockd_plane *const pd = &xd->plane[plane]; + if (plane && !xd->is_chroma_ref) break; + const BLOCK_SIZE plane_bsize = + get_plane_block_size(bsize, pd->subsampling_x, pd->subsampling_y); + av1_subtract_plane(x, plane_bsize, plane); + } +#endif // CONFIG_CROSS_CHROMA_TX for (int plane = plane_start; plane < plane_end; ++plane) { const struct macroblockd_plane *const pd = &xd->plane[plane]; const int subsampling_x = pd->subsampling_x; @@ -826,7 +926,10 @@ const int step = tx_size_wide_unit[max_tx_size] * tx_size_high_unit[max_tx_size]; av1_get_entropy_contexts(plane_bsize, pd, ctx.ta[plane], ctx.tl[plane]); +#if !CONFIG_CROSS_CHROMA_TX av1_subtract_plane(x, plane_bsize, plane); +#endif // !CONFIG_CROSS_CHROMA_TX + arg.ta = ctx.ta[plane]; arg.tl = ctx.tl[plane]; const BLOCK_SIZE max_unit_bsize = @@ -1005,6 +1108,7 @@ } if (*eob) { + // TODO(kslu) apply inv cctx for u plane once it is needed for intra av1_inverse_transform_block(xd, dqcoeff, plane, tx_type, tx_size, dst, dst_stride, *eob, cm->features.reduced_tx_set_used);
diff --git a/av1/encoder/encodemb.h b/av1/encoder/encodemb.h index c289094..47f0b36 100644 --- a/av1/encoder/encodemb.h +++ b/av1/encoder/encodemb.h
@@ -109,6 +109,10 @@ #endif ); +#if CONFIG_CROSS_CHROMA_TX +void forward_cross_chroma_transform(MACROBLOCK *x, int block, TX_SIZE tx_size); +#endif // CONFIG_CROSS_CHROMA_TX + void av1_quant(MACROBLOCK *x, int plane, int block, TxfmParam *txfm_param, QUANT_PARAM *qparam);
diff --git a/av1/encoder/hybrid_fwd_txfm.c b/av1/encoder/hybrid_fwd_txfm.c index 5ef2ff8..001f672 100644 --- a/av1/encoder/hybrid_fwd_txfm.c +++ b/av1/encoder/hybrid_fwd_txfm.c
@@ -499,6 +499,27 @@ } } +#if CONFIG_CROSS_CHROMA_TX +void av1_fwd_cross_chroma_tx_block(tran_low_t *coeff_u, tran_low_t *coeff_v, + TX_SIZE tx_size) { +#if CCTX_DC_ONLY + const int ncoeffs = 1; +#else + const int ncoeffs = av1_get_max_eob(tx_size); +#endif + int32_t *src_u = (int32_t *)coeff_u; + int32_t *src_v = (int32_t *)coeff_v; + int32_t tmp[2] = { 0, 0 }; + + for (int i = 0; i < ncoeffs; i++) { + tmp[0] = cctx_mtx[0] * src_u[i] + cctx_mtx[1] * src_v[i]; + tmp[1] = cctx_mtx[2] * src_u[i] + cctx_mtx[3] * src_v[i]; + src_u[i] = ROUND_POWER_OF_TWO_SIGNED(tmp[0], CCTX_PREC_BITS); + src_v[i] = ROUND_POWER_OF_TWO_SIGNED(tmp[1], CCTX_PREC_BITS); + } +} +#endif // CONFIG_CROSS_CHROMA_TX + #if CONFIG_IST void av1_fwd_stxfm(tran_low_t *coeff, TxfmParam *txfm_param) { const TX_TYPE stx_type = txfm_param->sec_tx_type;
diff --git a/av1/encoder/hybrid_fwd_txfm.h b/av1/encoder/hybrid_fwd_txfm.h index e171f67..6bc7ffb 100644 --- a/av1/encoder/hybrid_fwd_txfm.h +++ b/av1/encoder/hybrid_fwd_txfm.h
@@ -25,6 +25,11 @@ void av1_highbd_fwd_txfm(const int16_t *src_diff, tran_low_t *coeff, int diff_stride, TxfmParam *txfm_param); +#if CONFIG_CROSS_CHROMA_TX +void av1_fwd_cross_chroma_tx_block(tran_low_t *dqcoeff_u, tran_low_t *dqcoeff_v, + TX_SIZE tx_size); +#endif // CONFIG_CROSS_CHROMA_TX + #if CONFIG_IST void av1_fwd_stxfm(tran_low_t *coeff, TxfmParam *txfm_param); #endif
diff --git a/av1/encoder/tx_search.c b/av1/encoder/tx_search.c index 3216e4a..4db1c8c 100644 --- a/av1/encoder/tx_search.c +++ b/av1/encoder/tx_search.c
@@ -1142,6 +1142,7 @@ } } + // TODO(kslu) apply inv cctx for u plane once it is needed for intra inverse_transform_block_facade(x, plane, block, blk_row, blk_col, x->plane[plane].eobs[block], cm->features.reduced_tx_set_used); @@ -1217,7 +1218,7 @@ const int dst_idx = (blk_row * dst_stride + blk_col) << MI_SIZE_LOG2; const uint8_t *src = &x->plane[plane].src.buf[src_idx]; const uint8_t *dst = &xd->plane[plane].dst.buf[dst_idx]; -#if CONFIG_IST +#if CONFIG_IST || CONFIG_CROSS_CHROMA_TX tran_low_t *dqcoeff = p->dqcoeff + BLOCK_OFFSET(block); #else const tran_low_t *dqcoeff = p->dqcoeff + BLOCK_OFFSET(block); @@ -1236,6 +1237,12 @@ const PLANE_TYPE plane_type = get_plane_type(plane); TX_TYPE tx_type = av1_get_tx_type(xd, plane_type, blk_row, blk_col, tx_size, cpi->common.features.reduced_tx_set_used); +#if CONFIG_CROSS_CHROMA_TX + if (is_inter_block(xd->mi[0], xd->tree_type) && plane == AOM_PLANE_U) { + tran_low_t *dqcoeff_v = x->plane[AOM_PLANE_V].dqcoeff + BLOCK_OFFSET(block); + av1_inv_cross_chroma_tx_block(dqcoeff, dqcoeff_v, tx_size); + } +#endif // CONFIG_CROSS_CHROMA_TX av1_inverse_transform_block(xd, dqcoeff, plane, tx_type, tx_size, recon, MAX_TX_SIZE, eob, cpi->common.features.reduced_tx_set_used); @@ -2601,15 +2608,60 @@ RD_STATS this_rd_stats; av1_invalid_rd_stats(&this_rd_stats); - if (!dc_only_blk) +#if CONFIG_CROSS_CHROMA_TX + if (is_inter_block(mbmi, xd->tree_type)) { + switch (plane) { + case AOM_PLANE_Y: + if (!dc_only_blk) { #if CONFIG_IST - av1_xform(x, plane, block, blk_row, blk_col, plane_bsize, &txfm_param, - 1); + av1_xform(x, AOM_PLANE_Y, block, blk_row, blk_col, plane_bsize, + &txfm_param, 1); +#else + av1_xform(x, AOM_PLANE_Y, block, blk_row, blk_col, plane_bsize, + &txfm_param); +#endif + } else { + av1_xform_dc_only(x, AOM_PLANE_Y, block, &txfm_param, + per_px_mean); + } + break; + case AOM_PLANE_U: + if (!dc_only_blk) { +#if CONFIG_IST + av1_xform(x, AOM_PLANE_U, block, blk_row, blk_col, plane_bsize, + &txfm_param, 1); + av1_xform(x, AOM_PLANE_V, block, blk_row, blk_col, plane_bsize, + &txfm_param, 1); +#else + av1_xform(x, AOM_PLANE_U, block, blk_row, blk_col, plane_bsize, + &txfm_param); + av1_xform(x, AOM_PLANE_V, block, blk_row, blk_col, plane_bsize, + &txfm_param); +#endif + } else { + av1_xform_dc_only(x, AOM_PLANE_U, block, &txfm_param, + per_px_mean); + av1_xform_dc_only(x, AOM_PLANE_V, block, &txfm_param, + per_px_mean); + } + forward_cross_chroma_transform(x, block, txfm_param.tx_size); + break; + case AOM_PLANE_V: break; + } + } else { +#endif + if (!dc_only_blk) +#if CONFIG_IST + av1_xform(x, plane, block, blk_row, blk_col, plane_bsize, &txfm_param, + 1); #else av1_xform(x, plane, block, blk_row, blk_col, plane_bsize, &txfm_param); #endif - else - av1_xform_dc_only(x, plane, block, &txfm_param, per_px_mean); + else + av1_xform_dc_only(x, plane, block, &txfm_param, per_px_mean); +#if CONFIG_CROSS_CHROMA_TX + } +#endif // CONFIG_CROSS_CHROMA_TX #if CONFIG_IST skip_trellis_based_on_satd[txfm_param.tx_type] =
diff --git a/build/cmake/aom_config_defaults.cmake b/build/cmake/aom_config_defaults.cmake index 46c4535..14e2a17 100644 --- a/build/cmake/aom_config_defaults.cmake +++ b/build/cmake/aom_config_defaults.cmake
@@ -208,6 +208,8 @@ set_aom_config_var(CONFIG_DST_32X32 0 NUMBER "AV2 DST7 32x32 experiment flag.") set_aom_config_var(CONFIG_DDT_INTER 0 NUMBER "AV2 data-driven inter transform experiment flag.") +set_aom_config_var(CONFIG_CROSS_CHROMA_TX 0 + "AV2 cross chroma component transform experiment flag.") # # Variables in this section control optional features of the build system. #