/** * Copy all non-reference CU data from depth+1 to depth. */ static void work_tree_copy_up(int x_px, int y_px, int depth, lcu_t work_tree[MAX_PU_DEPTH + 1]) { assert(depth >= 0 && depth < MAX_PU_DEPTH); // Copy non-reference CUs. { const int x_orig = SUB_SCU(x_px); const int y_orig = SUB_SCU(y_px); const int width_cu = LCU_WIDTH >> depth; for (int y = y_orig; y < y_orig + width_cu; y += SCU_WIDTH) { for (int x = x_orig; x < x_orig + width_cu; x += SCU_WIDTH) { const cu_info_t *from_cu = LCU_GET_CU_AT_PX(&work_tree[depth + 1], x, y); cu_info_t *to_cu = LCU_GET_CU_AT_PX(&work_tree[depth], x, y); memcpy(to_cu, from_cu, sizeof(*to_cu)); } } } // Copy reconstructed pixels. { const int x = SUB_SCU(x_px); const int y = SUB_SCU(y_px); const int width_px = LCU_WIDTH >> depth; const int luma_index = x + y * LCU_WIDTH; const int chroma_index = (x / 2) + (y / 2) * (LCU_WIDTH / 2); const lcu_yuv_t *from = &work_tree[depth + 1].rec; lcu_yuv_t *to = &work_tree[depth].rec; const lcu_coeff_t *from_coeff = &work_tree[depth + 1].coeff; lcu_coeff_t *to_coeff = &work_tree[depth].coeff; kvz_pixels_blit(&from->y[luma_index], &to->y[luma_index], width_px, width_px, LCU_WIDTH, LCU_WIDTH); if (from->chroma_format != KVZ_CSP_400) { kvz_pixels_blit(&from->u[chroma_index], &to->u[chroma_index], width_px / 2, width_px / 2, LCU_WIDTH / 2, LCU_WIDTH / 2); kvz_pixels_blit(&from->v[chroma_index], &to->v[chroma_index], width_px / 2, width_px / 2, LCU_WIDTH / 2, LCU_WIDTH / 2); } // Copy coefficients up. They do not have to be copied down because they // are not used for the search. kvz_coefficients_blit(&from_coeff->y[luma_index], &to_coeff->y[luma_index], width_px, width_px, LCU_WIDTH, LCU_WIDTH); if (from->chroma_format != KVZ_CSP_400) { kvz_coefficients_blit(&from_coeff->u[chroma_index], &to_coeff->u[chroma_index], width_px / 2, width_px / 2, LCU_WIDTH / 2, LCU_WIDTH / 2); kvz_coefficients_blit(&from_coeff->v[chroma_index], &to_coeff->v[chroma_index], width_px / 2, width_px / 2, LCU_WIDTH / 2, LCU_WIDTH / 2); } } }
/** * Copy all non-reference CU data from depth to depth+1..MAX_PU_DEPTH. */ static void work_tree_copy_down(int x_px, int y_px, int depth, lcu_t work_tree[MAX_PU_DEPTH + 1]) { assert(depth >= 0 && depth < MAX_PU_DEPTH); // TODO: clean up to remove the copy pasta const int width_px = LCU_WIDTH >> depth; int d; for (d = depth + 1; d < MAX_PU_DEPTH + 1; ++d) { const int x_orig = SUB_SCU(x_px); const int y_orig = SUB_SCU(y_px); for (int y = y_orig; y < y_orig + width_px; y += SCU_WIDTH) { for (int x = x_orig; x < x_orig + width_px; x += SCU_WIDTH) { const cu_info_t *from_cu = LCU_GET_CU_AT_PX(&work_tree[depth], x, y); cu_info_t *to_cu = LCU_GET_CU_AT_PX(&work_tree[d], x, y); memcpy(to_cu, from_cu, sizeof(*to_cu)); } } } // Copy reconstructed pixels. for (d = depth + 1; d < MAX_PU_DEPTH + 1; ++d) { const int x = SUB_SCU(x_px); const int y = SUB_SCU(y_px); const int luma_index = x + y * LCU_WIDTH; const int chroma_index = (x / 2) + (y / 2) * (LCU_WIDTH / 2); lcu_yuv_t *from = &work_tree[depth].rec; lcu_yuv_t *to = &work_tree[d].rec; kvz_pixels_blit(&from->y[luma_index], &to->y[luma_index], width_px, width_px, LCU_WIDTH, LCU_WIDTH); if (from->chroma_format != KVZ_CSP_400) { kvz_pixels_blit(&from->u[chroma_index], &to->u[chroma_index], width_px / 2, width_px / 2, LCU_WIDTH / 2, LCU_WIDTH / 2); kvz_pixels_blit(&from->v[chroma_index], &to->v[chroma_index], width_px / 2, width_px / 2, LCU_WIDTH / 2, LCU_WIDTH / 2); } } }
static void encoder_state_recdata_to_bufs(encoder_state_t * const state, const lcu_order_element_t * const lcu, yuv_t * const hor_buf, yuv_t * const ver_buf) { videoframe_t* const frame = state->tile->frame; if (hor_buf) { //Copy the bottom row of this LCU to the horizontal buffer vector2d_t bottom = { lcu->position_px.x, lcu->position_px.y + lcu->size.y - 1 }; const int lcu_row = lcu->position.y; unsigned from_index = bottom.y * frame->rec->stride + bottom.x; unsigned to_index = lcu->position_px.x + lcu_row * frame->width; kvz_pixels_blit(&frame->rec->y[from_index], &hor_buf->y[to_index], lcu->size.x, 1, frame->rec->stride, frame->width); if (state->encoder_control->chroma_format != KVZ_CSP_400) { unsigned from_index_c = (bottom.y / 2) * frame->rec->stride / 2 + (bottom.x / 2); unsigned to_index_c = lcu->position_px.x / 2 + lcu_row * frame->width / 2; kvz_pixels_blit(&frame->rec->u[from_index_c], &hor_buf->u[to_index_c], lcu->size.x / 2, 1, frame->rec->stride / 2, frame->width / 2); kvz_pixels_blit(&frame->rec->v[from_index_c], &hor_buf->v[to_index_c], lcu->size.x / 2, 1, frame->rec->stride / 2, frame->width / 2); } } if (ver_buf) { //Copy the right row of this LCU to the vertical buffer. const int lcu_col = lcu->position.x; vector2d_t left = { lcu->position_px.x + lcu->size.x - 1, lcu->position_px.y }; kvz_pixels_blit(&frame->rec->y[left.y * frame->rec->stride + left.x], &ver_buf->y[lcu->position_px.y + lcu_col * frame->height], 1, lcu->size.y, frame->rec->stride, 1); if (state->encoder_control->chroma_format != KVZ_CSP_400) { unsigned from_index = (left.y / 2) * frame->rec->stride / 2 + (left.x / 2); unsigned to_index = lcu->position_px.y / 2 + lcu_col * frame->height / 2; kvz_pixels_blit(&frame->rec->u[from_index], &ver_buf->u[to_index], 1, lcu->size.y / 2, frame->rec->stride / 2, 1); kvz_pixels_blit(&frame->rec->v[from_index], &ver_buf->v[to_index], 1, lcu->size.y / 2, frame->rec->stride / 2, 1); } } }
/** * \brief Like kvz_quantize_residual except that this uses trskip if that is better. * * Using this function saves one step of quantization and inverse quantization * compared to doing the decision separately from the actual operation. * * \param width Transform width. * \param color Color. * \param scan_order Coefficient scan order. * \param trskip_out Whether transform skip is used. * \param stride Stride for ref_in, pred_in rec_out and coeff_out. * \param ref_in Reference pixels. * \param pred_in Predicted pixels. * \param rec_out Reconstructed pixels. * \param coeff_out Coefficients used for reconstruction of rec_out. * * \returns Whether coeff_out contains any non-zero coefficients. */ int kvz_quantize_residual_trskip( encoder_state_t *const state, const cu_info_t *const cur_cu, const int width, const color_t color, const coeff_scan_order_t scan_order, int8_t *trskip_out, const int in_stride, const int out_stride, const kvz_pixel *const ref_in, const kvz_pixel *const pred_in, kvz_pixel *rec_out, coeff_t *coeff_out) { struct { kvz_pixel rec[4*4]; coeff_t coeff[4*4]; uint32_t cost; int has_coeffs; } skip, noskip, *best; const int bit_cost = (int)(state->global->cur_lambda_cost+0.5); noskip.has_coeffs = kvz_quantize_residual( state, cur_cu, width, color, scan_order, 0, in_stride, 4, ref_in, pred_in, noskip.rec, noskip.coeff); noskip.cost = kvz_pixels_calc_ssd(ref_in, noskip.rec, in_stride, 4, 4); noskip.cost += kvz_get_coeff_cost(state, noskip.coeff, 4, 0, scan_order) * bit_cost; skip.has_coeffs = kvz_quantize_residual( state, cur_cu, width, color, scan_order, 1, in_stride, 4, ref_in, pred_in, skip.rec, skip.coeff); skip.cost = kvz_pixels_calc_ssd(ref_in, skip.rec, in_stride, 4, 4); skip.cost += kvz_get_coeff_cost(state, skip.coeff, 4, 0, scan_order) * bit_cost; if (noskip.cost <= skip.cost) { *trskip_out = 0; best = &noskip; } else { *trskip_out = 1; best = &skip; } if (best->has_coeffs || rec_out != pred_in) { // If there is no residual and reconstruction is already in rec_out, // we can skip this. kvz_pixels_blit(best->rec, rec_out, width, width, 4, out_stride); } kvz_coefficients_blit(best->coeff, coeff_out, width, width, 4, out_stride); return best->has_coeffs; }