mirror of
https://github.com/ultravideo/uvg266.git
synced 2024-11-27 19:24:06 +00:00
[mtt] Remove depth from cu_info_t
This commit is contained in:
parent
72196bbadb
commit
b6c36f07ac
|
@ -76,7 +76,6 @@ static const struct option long_options[] = {
|
|||
{ "tr-skip-max-size", required_argument, NULL, 0 },
|
||||
{ "mts", required_argument, NULL, 0 },
|
||||
{ "no-mts", no_argument, NULL, 0 },
|
||||
{ "tr-depth-intra", required_argument, NULL, 0 },
|
||||
{ "me", required_argument, NULL, 0 },
|
||||
{ "subme", required_argument, NULL, 0 },
|
||||
{ "source-scan-type", required_argument, NULL, 0 },
|
||||
|
@ -627,7 +626,6 @@ void print_help(void)
|
|||
" This is mostly for debugging and is not\n"
|
||||
" guaranteed to produce sensible bitstream or\n"
|
||||
" work at all. [disabled]\n"
|
||||
" --tr-depth-intra <int> : Transform split depth for intra blocks [0]\n"
|
||||
" --(no-)bipred : Bi-prediction [disabled]\n"
|
||||
" --cu-split-termination <string> : CU split search termination [zero]\n"
|
||||
" - off: Don't terminate early.\n"
|
||||
|
|
8
src/cu.h
8
src/cu.h
|
@ -117,7 +117,6 @@ typedef struct {
|
|||
typedef struct
|
||||
{
|
||||
uint8_t type : 2; //!< \brief block type, one of cu_type_t values
|
||||
uint8_t depth : 3; //!< \brief depth / size of this block
|
||||
uint8_t skipped : 1; //!< \brief flag to indicate this block is skipped
|
||||
uint8_t merged : 1; //!< \brief flag to indicate this block is merged
|
||||
uint8_t merge_idx : 3; //!< \brief merge index
|
||||
|
@ -198,7 +197,7 @@ void uvg_cu_loc_ctor(cu_loc_t *loc, int x, int y, int width, int height);
|
|||
} \
|
||||
} while (0)
|
||||
|
||||
#define CHECKPOINT_CU(prefix_str, cu) CHECKPOINT(prefix_str " type=%d depth=%d part_size=%d coded=%d " \
|
||||
#define CHECKPOINT_CU(prefix_str, cu) CHECKPOINT(prefix_str " type=%d part_size=%d coded=%d " \
|
||||
"skipped=%d merged=%d merge_idx=%d cbf.y=%d cbf.u=%d cbf.v=%d " \
|
||||
"intra[0].cost=%u intra[0].bitcost=%u intra[0].mode=%d intra[0].mode_chroma=%d intra[0].tr_skip=%d " \
|
||||
"intra[1].cost=%u intra[1].bitcost=%u intra[1].mode=%d intra[1].mode_chroma=%d intra[1].tr_skip=%d " \
|
||||
|
@ -206,7 +205,7 @@ void uvg_cu_loc_ctor(cu_loc_t *loc, int x, int y, int width, int height);
|
|||
"intra[3].cost=%u intra[3].bitcost=%u intra[3].mode=%d intra[3].mode_chroma=%d intra[3].tr_skip=%d " \
|
||||
"inter.cost=%u inter.bitcost=%u inter.mv[0]=%d inter.mv[1]=%d inter.mvd[0]=%d inter.mvd[1]=%d " \
|
||||
"inter.mv_cand=%d inter.mv_ref=%d inter.mv_dir=%d inter.mode=%d" \
|
||||
, (cu).type, (cu).depth, (cu).part_size, (cu).coded, \
|
||||
, (cu).type, (cu).part_size, (cu).coded, \
|
||||
(cu).skipped, (cu).merged, (cu).merge_idx, (cu).cbf.y, (cu).cbf.u, (cu).cbf.v, \
|
||||
(cu).intra[0].cost, (cu).intra[0].bitcost, (cu).intra[0].mode, (cu).intra[0].mode_chroma, (cu).intra[0].tr_skip, \
|
||||
(cu).intra[1].cost, (cu).intra[1].bitcost, (cu).intra[1].mode, (cu).intra[1].mode_chroma, (cu).intra[1].tr_skip, \
|
||||
|
@ -567,7 +566,7 @@ static INLINE void cbf_set(uint16_t *cbf, color_t plane)
|
|||
* Set CBF in a level to true if it is set at a lower level in any of
|
||||
* the child_cbfs.
|
||||
*/
|
||||
static INLINE void cbf_set_conditionally(uint16_t *cbf, uint16_t child_cbfs[3], int depth, color_t plane)
|
||||
static INLINE void cbf_set_conditionally(uint16_t *cbf, uint16_t child_cbfs[3], color_t plane)
|
||||
{
|
||||
bool child_cbf_set = cbf_is_set(child_cbfs[0], plane) ||
|
||||
cbf_is_set(child_cbfs[1], plane) ||
|
||||
|
@ -578,7 +577,6 @@ static INLINE void cbf_set_conditionally(uint16_t *cbf, uint16_t child_cbfs[3],
|
|||
}
|
||||
|
||||
/**
|
||||
* Set CBF in a levels <= depth to false.
|
||||
*/
|
||||
static INLINE void cbf_clear(uint16_t *cbf, color_t plane)
|
||||
{
|
||||
|
|
|
@ -444,7 +444,6 @@ void uvg_encode_last_significant_xy(cabac_data_t * const cabac,
|
|||
static void encode_chroma_tu(
|
||||
encoder_state_t* const state,
|
||||
const cu_loc_t * const cu_loc,
|
||||
int depth,
|
||||
cu_info_t* cur_pu,
|
||||
int8_t* scan_idx,
|
||||
lcu_coeff_t* coeff,
|
||||
|
@ -453,11 +452,11 @@ static void encode_chroma_tu(
|
|||
uvg_tree_type tree_type)
|
||||
{
|
||||
int width_c = cu_loc->chroma_width;
|
||||
//int height_c = cu_loc->chroma_height;
|
||||
int height_c = cu_loc->chroma_height;
|
||||
int x_local = ((cu_loc->x >> (tree_type != UVG_CHROMA_T)) & ~3) % LCU_WIDTH_C;
|
||||
int y_local = ((cu_loc->y >> (tree_type != UVG_CHROMA_T)) & ~3) % LCU_WIDTH_C;
|
||||
cabac_data_t* const cabac = &state->cabac;
|
||||
*scan_idx = uvg_get_scan_order(cur_pu->type, cur_pu->intra.mode_chroma, depth);
|
||||
*scan_idx = SCAN_DIAG;
|
||||
if(!joint_chroma){
|
||||
// const coeff_t *coeff_u = &coeff->u[xy_to_zorder(LCU_WIDTH_C, x_local, y_local)];
|
||||
// const coeff_t *coeff_v = &coeff->v[xy_to_zorder(LCU_WIDTH_C, x_local, y_local)];
|
||||
|
@ -468,7 +467,9 @@ static void encode_chroma_tu(
|
|||
|
||||
if (cbf_is_set(cur_pu->cbf, COLOR_U)) {
|
||||
// TODO: height for this check and the others below
|
||||
if(state->encoder_control->cfg.trskip_enable && width_c <= (1 << state->encoder_control->cfg.trskip_max_size)){
|
||||
if(state->encoder_control->cfg.trskip_enable
|
||||
&& width_c <= (1 << state->encoder_control->cfg.trskip_max_size)
|
||||
&& height_c <= (1 << state->encoder_control->cfg.trskip_max_size)){
|
||||
cabac->cur_ctx = &cabac->ctx.transform_skip_model_chroma;
|
||||
// HEVC only supports transform_skip for Luma
|
||||
// TODO: transform skip for chroma blocks
|
||||
|
@ -478,7 +479,9 @@ static void encode_chroma_tu(
|
|||
}
|
||||
|
||||
if (cbf_is_set(cur_pu->cbf, COLOR_V)) {
|
||||
if (state->encoder_control->cfg.trskip_enable && width_c <= (1 << state->encoder_control->cfg.trskip_max_size)) {
|
||||
if (state->encoder_control->cfg.trskip_enable
|
||||
&& width_c <= (1 << state->encoder_control->cfg.trskip_max_size)
|
||||
&& height_c <= (1 << state->encoder_control->cfg.trskip_max_size)) {
|
||||
cabac->cur_ctx = &cabac->ctx.transform_skip_model_chroma;
|
||||
CABAC_BIN(cabac, (cur_pu->tr_skip >> COLOR_V) & 1, "transform_skip_flag");
|
||||
}
|
||||
|
@ -488,7 +491,9 @@ static void encode_chroma_tu(
|
|||
else {
|
||||
coeff_t coeff_uv[TR_MAX_WIDTH * TR_MAX_WIDTH];
|
||||
uvg_get_sub_coeff(coeff_uv, coeff->joint_uv, x_local, y_local, cu_loc->chroma_width, cu_loc->chroma_height, LCU_WIDTH_C);
|
||||
if (state->encoder_control->cfg.trskip_enable && width_c <= (1 << state->encoder_control->cfg.trskip_max_size)) {
|
||||
if (state->encoder_control->cfg.trskip_enable
|
||||
&& width_c <= (1 << state->encoder_control->cfg.trskip_max_size)
|
||||
&& height_c <= (1 << state->encoder_control->cfg.trskip_max_size)) {
|
||||
cabac->cur_ctx = &cabac->ctx.transform_skip_model_chroma;
|
||||
CABAC_BIN(cabac, 0, "transform_skip_flag");
|
||||
}
|
||||
|
@ -500,15 +505,12 @@ static void encode_chroma_tu(
|
|||
static void encode_transform_unit(
|
||||
encoder_state_t * const state,
|
||||
const cu_loc_t *cu_loc,
|
||||
int depth,
|
||||
bool only_chroma,
|
||||
lcu_coeff_t* coeff,
|
||||
enum uvg_tree_type tree_type,
|
||||
bool last_split,
|
||||
const cu_loc_t *original_loc) // Original cu dimensions, before CU split
|
||||
{
|
||||
assert(depth >= 1 && depth <= MAX_PU_DEPTH);
|
||||
|
||||
const videoframe_t * const frame = state->tile->frame;
|
||||
cabac_data_t* const cabac = &state->cabac;
|
||||
const int x = cu_loc->x;
|
||||
|
@ -524,7 +526,7 @@ static void encode_transform_unit(
|
|||
uvg_get_isp_cu_arr_coords(&isp_x, &isp_y);
|
||||
const cu_info_t *cur_pu = uvg_cu_array_at_const(used_cu_array, isp_x, isp_y);
|
||||
|
||||
int8_t scan_idx = uvg_get_scan_order(cur_pu->type, cur_pu->intra.mode, depth);
|
||||
int8_t scan_idx = SCAN_DIAG;
|
||||
|
||||
int cbf_y = cbf_is_set(cur_pu->cbf, COLOR_Y);
|
||||
|
||||
|
@ -559,7 +561,7 @@ static void encode_transform_unit(
|
|||
}
|
||||
|
||||
bool joint_chroma = cur_pu->joint_cb_cr != 0;
|
||||
if (depth == MAX_DEPTH) {
|
||||
if (cur_pu->log2_height + cur_pu->log2_width < 6) {
|
||||
// For size 4x4 luma transform the corresponding chroma transforms are
|
||||
// also of size 4x4 covering 8x8 luma pixels. The residual is coded in
|
||||
// the last transform unit.
|
||||
|
@ -578,7 +580,7 @@ static void encode_transform_unit(
|
|||
if ((chroma_cbf_set || joint_chroma) && last_split) {
|
||||
//Need to drop const to get lfnst constraints
|
||||
// Use original dimensions instead of ISP split dimensions
|
||||
encode_chroma_tu(state, original_loc, depth, (cu_info_t*)cur_pu, &scan_idx, coeff, joint_chroma, tree_type);
|
||||
encode_chroma_tu(state, original_loc, (cu_info_t*)cur_pu, &scan_idx, coeff, joint_chroma, tree_type);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -593,7 +595,6 @@ static void encode_transform_unit(
|
|||
static void encode_transform_coeff(
|
||||
encoder_state_t * const state,
|
||||
const cu_loc_t * cu_loc,
|
||||
int8_t depth,
|
||||
bool only_chroma,
|
||||
lcu_coeff_t* coeff,
|
||||
enum uvg_tree_type tree_type,
|
||||
|
@ -641,7 +642,7 @@ static void encode_transform_coeff(
|
|||
cu_loc_t loc;
|
||||
uvg_cu_loc_ctor(&loc, (x + i * split_width), (y + j * split_height), width >> 1, height >> 1);
|
||||
|
||||
encode_transform_coeff(state, &loc, depth + 1, only_chroma, coeff, tree_type, true, luma_cbf_ctx, &loc);
|
||||
encode_transform_coeff(state, &loc, only_chroma, coeff, tree_type, true, luma_cbf_ctx, &loc);
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
@ -650,7 +651,10 @@ static void encode_transform_coeff(
|
|||
// No chroma.
|
||||
// Not the last CU for area of 64 pixels cowered by more than one luma CU.
|
||||
// Not the last ISP Split
|
||||
if (state->encoder_control->chroma_format != UVG_CSP_400 && (depth != 4 || only_chroma) && tree_type != UVG_LUMA_T && last_split) {
|
||||
if (state->encoder_control->chroma_format != UVG_CSP_400
|
||||
&& (cur_pu->log2_height + cur_pu->log2_width >= 6 || only_chroma)
|
||||
&& tree_type != UVG_LUMA_T
|
||||
&& last_split) {
|
||||
cabac->cur_ctx = &(cabac->ctx.qt_cbf_model_cb[0]);
|
||||
CABAC_BIN(cabac, cb_flag_u, "cbf_cb");
|
||||
cabac->cur_ctx = &(cabac->ctx.qt_cbf_model_cr[cb_flag_u ? 1 : 0]);
|
||||
|
@ -687,7 +691,7 @@ static void encode_transform_coeff(
|
|||
}
|
||||
|
||||
if (cb_flag_y | cb_flag_u | cb_flag_v) {
|
||||
if (state->must_code_qp_delta && (only_chroma || cb_flag_y || depth != 4) ) {
|
||||
if (state->must_code_qp_delta && (only_chroma || cb_flag_y || cur_pu->log2_height + cur_pu->log2_width >= 6) ) {
|
||||
const int qp_pred = uvg_get_cu_ref_qp(state, x_cu, y_cu, state->last_qp);
|
||||
const int qp_delta = cur_cu->qp - qp_pred;
|
||||
// Possible deltaQP range depends on bit depth as stated in HEVC specification.
|
||||
|
@ -714,7 +718,7 @@ static void encode_transform_coeff(
|
|||
((cb_flag_u || cb_flag_v )
|
||||
&& cur_cu->type == CU_INTRA)
|
||||
|| (cb_flag_u && cb_flag_v))
|
||||
&& (depth != 4 || only_chroma || tree_type == UVG_CHROMA_T)
|
||||
&& (cur_pu->log2_height + cur_pu->log2_width >= 6 || only_chroma || tree_type == UVG_CHROMA_T)
|
||||
&& state->encoder_control->cfg.jccr
|
||||
&& last_split
|
||||
) {
|
||||
|
@ -723,7 +727,7 @@ static void encode_transform_coeff(
|
|||
CABAC_BIN(cabac, cur_pu->joint_cb_cr != 0, "tu_joint_cbcr_residual_flag");
|
||||
}
|
||||
|
||||
encode_transform_unit(state, cu_loc, depth, only_chroma, coeff, tree_type, last_split, original_loc);
|
||||
encode_transform_unit(state, cu_loc, only_chroma, coeff, tree_type, last_split, original_loc);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1194,94 +1198,6 @@ void uvg_encode_intra_luma_coding_unit(
|
|||
if (cabac->only_count && bits_out) *bits_out += bits;
|
||||
}
|
||||
|
||||
/**
|
||||
static void encode_part_mode(encoder_state_t * const state,
|
||||
cabac_data_t * const cabac,
|
||||
const cu_info_t * const cur_cu,
|
||||
int depth)
|
||||
{
|
||||
// Binarization from Table 9-34 of the HEVC spec:
|
||||
//
|
||||
// | log2CbSize > | log2CbSize ==
|
||||
// | MinCbLog2SizeY | MinCbLog2SizeY
|
||||
// -------+-------+----------+---------+-----------+----------
|
||||
// pred | part | AMP | AMP | |
|
||||
// mode | mode | disabled | enabled | size == 8 | size > 8
|
||||
// -------+-------+----------+---------+-----------+----------
|
||||
// intra | 2Nx2N | - - | 1 1
|
||||
// | NxN | - - | 0 0
|
||||
// -------+-------+--------------------+----------------------
|
||||
// inter | 2Nx2N | 1 1 | 1 1
|
||||
// | 2NxN | 01 011 | 01 01
|
||||
// | Nx2N | 00 001 | 00 001
|
||||
// | NxN | - - | - 000
|
||||
// | 2NxnU | - 0100 | - -
|
||||
// | 2NxnD | - 0101 | - -
|
||||
// | nLx2N | - 0000 | - -
|
||||
// | nRx2N | - 0001 | - -
|
||||
// -------+-------+--------------------+----------------------
|
||||
//
|
||||
//
|
||||
// Context indices from Table 9-37 of the HEVC spec:
|
||||
//
|
||||
// binIdx
|
||||
// | 0 1 2 3
|
||||
// ------------------------------+------------------
|
||||
// log2CbSize == MinCbLog2SizeY | 0 1 2 bypass
|
||||
// log2CbSize > MinCbLog2SizeY | 0 1 3 bypass
|
||||
// ------------------------------+------------------
|
||||
double bits = 0;
|
||||
if (cur_cu->type == CU_INTRA) {
|
||||
if (depth == MAX_DEPTH) {
|
||||
cabac->cur_ctx = &(cabac->ctx.part_size_model[0]);
|
||||
if (cur_cu->part_size == SIZE_2Nx2N) {
|
||||
CABAC_FBITS_UPDATE(cabac, &(cabac->ctx.part_size_model[0]), 1, bits, "part_mode 2Nx2N");
|
||||
} else {
|
||||
CABAC_FBITS_UPDATE(cabac, &(cabac->ctx.part_size_model[0]), 0, bits, "part_mode NxN");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
cabac->cur_ctx = &(cabac->ctx.part_size_model[0]);
|
||||
if (cur_cu->part_size == SIZE_2Nx2N) {
|
||||
CABAC_FBITS_UPDATE(cabac, &(cabac->ctx.part_size_model[0]), 1, bits, "part_mode 2Nx2N");
|
||||
return bits;
|
||||
}
|
||||
CABAC_FBITS_UPDATE(cabac, &(cabac->ctx.part_size_model[0]), 0, bits, "part_mode split");
|
||||
|
||||
cabac->cur_ctx = &(cabac->ctx.part_size_model[1]);
|
||||
if (cur_cu->part_size == SIZE_2NxN ||
|
||||
cur_cu->part_size == SIZE_2NxnU ||
|
||||
cur_cu->part_size == SIZE_2NxnD) {
|
||||
CABAC_FBITS_UPDATE(cabac, &(cabac->ctx.part_size_model[1]), 1, bits, "part_mode vertical");
|
||||
} else {
|
||||
CABAC_FBITS_UPDATE(cabac, &(cabac->ctx.part_size_model[1]), 0, bits, "part_mode horizontal");
|
||||
}
|
||||
|
||||
if (state->encoder_control->cfg.amp_enable && depth < MAX_DEPTH) {
|
||||
cabac->cur_ctx = &(cabac->ctx.part_size_model[3]);
|
||||
|
||||
if (cur_cu->part_size == SIZE_2NxN ||
|
||||
cur_cu->part_size == SIZE_Nx2N) {
|
||||
CABAC_FBITS_UPDATE(cabac, &(cabac->ctx.part_size_model[3]), 1, bits, "part_mode SMP");
|
||||
return bits;
|
||||
}
|
||||
CABAC_FBITS_UPDATE(cabac, &(cabac->ctx.part_size_model[3]), 0, bits, "part_mode AMP");
|
||||
|
||||
if (cur_cu->part_size == SIZE_2NxnU ||
|
||||
cur_cu->part_size == SIZE_nLx2N) {
|
||||
CABAC_BINS_EP(cabac, 0, 1, "part_mode AMP");
|
||||
if(cabac->only_count) bits += 1;
|
||||
} else {
|
||||
CABAC_BINS_EP(cabac, 1, 1, "part_mode AMP");
|
||||
if(cabac->only_count) bits += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
**/
|
||||
|
||||
|
||||
bool uvg_write_split_flag(
|
||||
const encoder_state_t * const state,
|
||||
|
@ -1451,7 +1367,7 @@ void uvg_encode_coding_tree(
|
|||
}
|
||||
|
||||
// When not in MAX_DEPTH, insert split flag and split the blocks if needed
|
||||
if (depth != MAX_DEPTH && !(tree_type == UVG_CHROMA_T && depth == MAX_DEPTH -1)) {
|
||||
if (cu_width + cu_height > 8) {
|
||||
|
||||
const int split_flag = uvg_write_split_flag(
|
||||
state,
|
||||
|
@ -1550,11 +1466,7 @@ void uvg_encode_coding_tree(
|
|||
cabac->cur_ctx = &(cabac->ctx.cu_pred_mode_model[ctx_predmode]);
|
||||
CABAC_BIN(cabac, (cur_cu->type == CU_INTRA), "PredMode");
|
||||
}
|
||||
|
||||
// part_mode
|
||||
//encode_part_mode(state, cabac, cur_cu, depth);
|
||||
|
||||
|
||||
|
||||
|
||||
#if ENABLE_PCM
|
||||
// Code IPCM block
|
||||
|
@ -1637,7 +1549,7 @@ void uvg_encode_coding_tree(
|
|||
// Code (possible) coeffs to bitstream
|
||||
if (has_coeffs) {
|
||||
int luma_cbf_ctx = 0;
|
||||
encode_transform_coeff(state, cu_loc, depth, 0, coeff, tree_type, true, false, &luma_cbf_ctx, cu_loc);
|
||||
encode_transform_coeff(state, cu_loc, 0, coeff, tree_type, true, false, &luma_cbf_ctx, cu_loc);
|
||||
}
|
||||
|
||||
encode_mts_idx(state, cabac, cur_cu, cu_loc);
|
||||
|
@ -1651,7 +1563,7 @@ void uvg_encode_coding_tree(
|
|||
const bool is_local_dual_tree = cu_height * cu_width < 64 && tree_type == UVG_BOTH_T;
|
||||
|
||||
// Code chroma prediction mode.
|
||||
if (state->encoder_control->chroma_format != UVG_CSP_400 && depth != 4 && tree_type == UVG_BOTH_T) {
|
||||
if (state->encoder_control->chroma_format != UVG_CSP_400 && cur_cu->log2_height + cur_cu->log2_width >= 6 && tree_type == UVG_BOTH_T) {
|
||||
encode_chroma_intra_cu(cabac, cur_cu, state->encoder_control->cfg.cclm, NULL);
|
||||
}
|
||||
int luma_cbf_ctx = 0;
|
||||
|
@ -1671,7 +1583,7 @@ void uvg_encode_coding_tree(
|
|||
|
||||
// Check if last split to write chroma
|
||||
bool last_split = (i + 1) == split_limit;
|
||||
encode_transform_coeff(state, &split_loc, depth, 0, coeff, tree_type, last_split, can_skip_last_cbf, &luma_cbf_ctx, cu_loc);
|
||||
encode_transform_coeff(state, &split_loc, 0, coeff, tree_type, last_split, can_skip_last_cbf, &luma_cbf_ctx, cu_loc);
|
||||
can_skip_last_cbf &= luma_cbf_ctx == 2;
|
||||
}
|
||||
}
|
||||
|
@ -1691,7 +1603,7 @@ void uvg_encode_coding_tree(
|
|||
tmp->violates_lfnst_constrained_luma = false;
|
||||
tmp->violates_lfnst_constrained_chroma = false;
|
||||
tmp->lfnst_last_scan_pos = false;
|
||||
encode_transform_coeff(state, cu_loc, depth, 1, coeff, tree_type, true, false, &luma_cbf_ctx, cu_loc);
|
||||
encode_transform_coeff(state, cu_loc, 1, coeff, tree_type, true, false, &luma_cbf_ctx, cu_loc);
|
||||
// Write LFNST only once for single tree structure
|
||||
encode_lfnst_idx(state, cabac, tmp, is_local_dual_tree ? UVG_CHROMA_T : tree_type, COLOR_UV, cu_loc);
|
||||
}
|
||||
|
@ -1756,7 +1668,7 @@ double uvg_mock_encode_coding_unit(
|
|||
}
|
||||
|
||||
// When not in MAX_DEPTH, insert split flag and split the blocks if needed
|
||||
if (tree_type != UVG_CHROMA_T ? depth != MAX_DEPTH : depth != MAX_DEPTH - 1) {
|
||||
if (cur_cu->log2_height + cur_cu->log2_width > 4) {
|
||||
uvg_write_split_flag(
|
||||
state,
|
||||
cabac,
|
||||
|
@ -1831,7 +1743,7 @@ double uvg_mock_encode_coding_unit(
|
|||
if(tree_type != UVG_CHROMA_T) {
|
||||
uvg_encode_intra_luma_coding_unit(state, cabac, cur_cu, cu_loc, lcu, &bits);
|
||||
}
|
||||
if((depth != 4 || (x % 8 != 0 && y % 8 != 0)) && state->encoder_control->chroma_format != UVG_CSP_400 && tree_type != UVG_LUMA_T) {
|
||||
if((cur_cu->log2_height + cur_cu->log2_width >= 6 || (x % 8 != 0 && y % 8 != 0)) && state->encoder_control->chroma_format != UVG_CSP_400 && tree_type != UVG_LUMA_T) {
|
||||
encode_chroma_intra_cu(cabac, cur_cu, state->encoder_control->cfg.cclm, &bits);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2100,11 +2100,12 @@ int uvg_get_cu_ref_qp(const encoder_state_t *state, int x, int y, int last_qp)
|
|||
{
|
||||
const cu_array_t *cua = state->tile->frame->cu_array;
|
||||
// Quantization group width
|
||||
const int qg_width = LCU_WIDTH >> MIN(state->frame->max_qp_delta_depth, uvg_cu_array_at_const(cua, x, y)->depth);
|
||||
const int qg_width = 1 << MAX(6 - state->frame->max_qp_delta_depth, uvg_cu_array_at_const(cua, x, y)->log2_width);
|
||||
const int qg_height = 1 << MAX(6 - state->frame->max_qp_delta_depth, uvg_cu_array_at_const(cua, x, y)->log2_height);
|
||||
|
||||
// Coordinates of the top-left corner of the quantization group
|
||||
const int x_qg = x & ~(qg_width - 1);
|
||||
const int y_qg = y & ~(qg_width - 1);
|
||||
const int y_qg = y & ~(qg_height - 1);
|
||||
if(x_qg == 0 && y_qg > 0 && y_qg % LCU_WIDTH == 0) {
|
||||
return uvg_cu_array_at_const(cua, x_qg, y_qg - 1)->qp;
|
||||
}
|
||||
|
|
26
src/filter.c
26
src/filter.c
|
@ -307,32 +307,6 @@ static bool is_pu_boundary(const encoder_state_t *const state,
|
|||
it for now, in case some other tool requires it.
|
||||
*/
|
||||
return false;
|
||||
//const cu_info_t *const scu =
|
||||
// uvg_cu_array_at_const(state->tile->frame->cu_array, x, y);
|
||||
//// Get the containing CU.
|
||||
//const int32_t cu_width = LCU_WIDTH >> scu->depth;
|
||||
//const int32_t x_cu = x & ~(cu_width - 1);
|
||||
//const int32_t y_cu = y & ~(cu_width - 1);
|
||||
//const cu_info_t *const cu =
|
||||
// uvg_cu_array_at_const(state->tile->frame->cu_array, x_cu, y_cu);
|
||||
|
||||
//const int num_pu = uvg_part_mode_num_parts[cu->part_size];
|
||||
//for (int i = 0; i < num_pu; i++) {
|
||||
// if (dir == EDGE_HOR) {
|
||||
// int y_pu = PU_GET_Y(cu->part_size, cu_width, y_cu, i);
|
||||
// if (y_pu == y) {
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// } else {
|
||||
// int x_pu = PU_GET_X(cu->part_size, cu_width, x_cu, i);
|
||||
// if (x_pu == x) {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
//return false;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -565,7 +565,7 @@ static void predict_cclm(
|
|||
y_extension >>= tree_type == UVG_CHROMA_T;
|
||||
const cu_info_t* pu = LCU_GET_CU_AT_PX(lcu, (x_scu >> (tree_type == UVG_CHROMA_T)) - 4, y_extension);
|
||||
if (y_extension >= ctu_size || pu->type == CU_NOTSET || (pu->type == CU_INTRA && pu->intra.mode_chroma == -1)) break;
|
||||
if(x_scu == 32 && y_scu == 0 && pu->depth == 0) break;
|
||||
if(x_scu == 32 && y_scu == 0 && pu->log2_width == 6) break;
|
||||
}
|
||||
for(int i = 0; i < height + available_left_below * 2; i++) {
|
||||
sampled_luma_ref.left[i] = state->tile->frame->cclm_luma_rec[(y0/2 + i) * (stride2/2) + x0 / 2 - 1];
|
||||
|
@ -1866,8 +1866,9 @@ void uvg_intra_recon_cu(
|
|||
// TODO: not necessary to call if only luma and ISP is on
|
||||
uvg_quantize_lcu_residual(state, has_luma, has_chroma && !(search_data->pred_cu.joint_cb_cr & 3),
|
||||
search_data->pred_cu.joint_cb_cr & 3 && state->encoder_control->cfg.jccr && has_chroma,
|
||||
cu_loc, depth, cur_cu, lcu,
|
||||
false, tree_type);
|
||||
cu_loc, cur_cu, lcu,
|
||||
false,
|
||||
tree_type);
|
||||
}
|
||||
|
||||
|
||||
|
|
19
src/search.c
19
src/search.c
|
@ -164,7 +164,6 @@ static void lcu_fill_cu_info(lcu_t *lcu, int x_local, int y_local, int width, in
|
|||
for (int x = x_local; x < x_local + width; x += SCU_WIDTH) {
|
||||
cu_info_t *to = LCU_GET_CU_AT_PX(lcu, x, y);
|
||||
to->type = cu->type;
|
||||
to->depth = cu->depth;
|
||||
to->qp = cu->qp;
|
||||
to->split_tree = cu->split_tree;
|
||||
//to->tr_idx = cu->tr_idx;
|
||||
|
@ -456,7 +455,6 @@ double uvg_cu_rd_cost_chroma(
|
|||
}
|
||||
|
||||
if (!skip_residual_coding) {
|
||||
const int tr_depth = depth - pred_cu->depth;
|
||||
cabac_data_t* cabac = (cabac_data_t*)&state->search_cabac;
|
||||
cabac_ctx_t* ctx = &(cabac->ctx.qt_cbf_model_cb[0]);
|
||||
cabac->cur_ctx = ctx;
|
||||
|
@ -941,7 +939,6 @@ static double search_cu(
|
|||
|
||||
cur_cu = LCU_GET_CU_AT_PX(lcu, x_local, y_local);
|
||||
// Assign correct depth
|
||||
cur_cu->depth = (split_tree.current_depth > MAX_DEPTH) ? MAX_DEPTH : split_tree.current_depth;
|
||||
cur_cu->type = CU_NOTSET;
|
||||
cur_cu->qp = state->qp;
|
||||
cur_cu->bdpcmMode = 0;
|
||||
|
@ -1041,7 +1038,8 @@ static double search_cu(
|
|||
int8_t intra_mode = intra_search.pred_cu.intra.mode;
|
||||
|
||||
// TODO: This heavily relies to square CUs
|
||||
if ((split_tree.current_depth != 4 || (x % 8 && y % 8)) && state->encoder_control->chroma_format != UVG_CSP_400 && tree_type != UVG_LUMA_T) {
|
||||
if ((cur_cu->log2_height + cur_cu->log2_width >= 6 || (x % 8 && y % 8))
|
||||
&& state->encoder_control->chroma_format != UVG_CSP_400 && tree_type != UVG_LUMA_T) {
|
||||
|
||||
intra_search.pred_cu.joint_cb_cr = 0;
|
||||
if(tree_type == UVG_CHROMA_T) {
|
||||
|
@ -1098,7 +1096,7 @@ static double search_cu(
|
|||
|
||||
bool recon_chroma = true;
|
||||
bool recon_luma = tree_type != UVG_CHROMA_T;
|
||||
if ((split_tree.current_depth == 4) || state->encoder_control->chroma_format == UVG_CSP_400 || tree_type == UVG_LUMA_T) {
|
||||
if ((cur_cu->log2_height + cur_cu->log2_width < 6) || state->encoder_control->chroma_format == UVG_CSP_400 || tree_type == UVG_LUMA_T) {
|
||||
recon_chroma = false;
|
||||
}
|
||||
lcu_fill_cu_info(lcu, x_local, y_local, cu_width, cu_width, cur_cu);
|
||||
|
@ -1109,7 +1107,7 @@ static double search_cu(
|
|||
recon_luma, recon_chroma);
|
||||
|
||||
|
||||
if(split_tree.current_depth == 4 && x % 8 && y % 8 && tree_type != UVG_LUMA_T && state->encoder_control->chroma_format != UVG_CSP_400) {
|
||||
if(cur_cu->log2_height + cur_cu->log2_width < 6 && x % 8 && y % 8 && tree_type != UVG_LUMA_T && state->encoder_control->chroma_format != UVG_CSP_400) {
|
||||
intra_search.pred_cu.intra.mode_chroma = cur_cu->intra.mode_chroma;
|
||||
uvg_intra_recon_cu(state,
|
||||
&intra_search, cu_loc,
|
||||
|
@ -1172,11 +1170,10 @@ static double search_cu(
|
|||
uvg_quantize_lcu_residual(state,
|
||||
true, has_chroma && !cur_cu->joint_cb_cr,
|
||||
cur_cu->joint_cb_cr, &loc,
|
||||
depth,
|
||||
NULL,
|
||||
lcu,
|
||||
false,
|
||||
tree_type);
|
||||
tree_type);
|
||||
|
||||
int cbf = cbf_is_set_any(cur_cu->cbf);
|
||||
|
||||
|
@ -1265,7 +1262,7 @@ static double search_cu(
|
|||
|
||||
double split_bits = 0;
|
||||
|
||||
if (split_tree.current_depth < MAX_DEPTH) {
|
||||
if (cur_cu->log2_height + cur_cu->log2_width > 4) {
|
||||
|
||||
state->search_cabac.update = 1;
|
||||
// Add cost of cu_split_flag.
|
||||
|
@ -1343,7 +1340,7 @@ static double search_cu(
|
|||
cu_info_t *cu_d1 = LCU_GET_CU_AT_PX(&work_tree[depth + 1], x_local, y_local);
|
||||
|
||||
// If the best CU in depth+1 is intra and the biggest it can be, try it.
|
||||
if (cu_d1->type == CU_INTRA && cu_d1->depth == depth + 1) {
|
||||
if (cu_d1->type == CU_INTRA && (cu_d1->log2_height + 1 == cur_cu->log2_height || cu_d1->log2_width + 1 == cur_cu->log2_width)) {
|
||||
cabac_data_t temp_cabac;
|
||||
memcpy(&temp_cabac, &state->search_cabac, sizeof(temp_cabac));
|
||||
memcpy(&state->search_cabac, &pre_search_cabac, sizeof(pre_search_cabac));
|
||||
|
@ -1413,7 +1410,7 @@ static double search_cu(
|
|||
state, x, y, cu_width / 2, cu_width / 2, lcu->rec.y, lcu->left_ref.y[64]
|
||||
);
|
||||
}
|
||||
} else if (depth >= 0 && depth < MAX_PU_DEPTH) {
|
||||
} else if (cur_cu->log2_height + cur_cu->log2_width > 4) {
|
||||
// Need to copy modes down since the lower level of the work tree is used
|
||||
// when searching SMP and AMP blocks.
|
||||
work_tree_copy_down(depth, work_tree, tree_type, cu_loc);
|
||||
|
|
|
@ -96,7 +96,5 @@ double uvg_cu_rd_cost_chroma(
|
|||
lcu_t *const lcu,
|
||||
const cu_loc_t * const);
|
||||
|
||||
void uvg_intra_recon_lcu_luma(encoder_state_t * const state, int x, int y, int depth, int8_t intra_mode, cu_info_t *cur_cu, lcu_t *lcu);
|
||||
void uvg_intra_recon_lcu_chroma(encoder_state_t * const state, int x, int y, int depth, int8_t intra_mode, cu_info_t *cur_cu, lcu_t *lcu);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1764,7 +1764,7 @@ static void search_pu_inter(
|
|||
cur_pu->inter.mv[1][1] = info->merge_cand[merge_idx].mv[1][1];
|
||||
uvg_inter_recon_cu(state, lcu, true, false, cu_loc);
|
||||
|
||||
uvg_quantize_lcu_residual(state, true, false, false, cu_loc, depth, cur_pu, lcu, true, UVG_BOTH_T);
|
||||
uvg_quantize_lcu_residual(state, true, false, false, cu_loc, cur_pu, lcu, true, UVG_BOTH_T);
|
||||
|
||||
if (cbf_is_set(cur_pu->cbf, COLOR_Y)) {
|
||||
continue;
|
||||
|
@ -1774,9 +1774,9 @@ static void search_pu_inter(
|
|||
uvg_quantize_lcu_residual(state,
|
||||
false, has_chroma,
|
||||
false, /*we are only checking for lack of coeffs so no need to check jccr*/
|
||||
cu_loc, depth, cur_pu, lcu,
|
||||
cu_loc, cur_pu, lcu,
|
||||
true,
|
||||
UVG_BOTH_T);
|
||||
UVG_BOTH_T);
|
||||
if (!cbf_is_set_any(cur_pu->cbf)) {
|
||||
cur_pu->type = CU_INTER;
|
||||
cur_pu->merge_idx = merge_idx;
|
||||
|
@ -2132,11 +2132,10 @@ void uvg_cu_cost_inter_rd2(
|
|||
false,
|
||||
false,
|
||||
cu_loc,
|
||||
depth,
|
||||
cur_cu,
|
||||
lcu,
|
||||
false,
|
||||
UVG_BOTH_T);
|
||||
false,
|
||||
UVG_BOTH_T);
|
||||
ALIGNED(64) uvg_pixel u_pred[LCU_WIDTH_C * LCU_WIDTH_C];
|
||||
ALIGNED(64) uvg_pixel v_pred[LCU_WIDTH_C * LCU_WIDTH_C];
|
||||
uvg_pixels_blit(&lcu->ref.u[index], u_pred, width, width, LCU_WIDTH_C, width);
|
||||
|
@ -2197,11 +2196,10 @@ void uvg_cu_cost_inter_rd2(
|
|||
true, reconstruct_chroma,
|
||||
reconstruct_chroma && state->encoder_control->cfg.jccr,
|
||||
cu_loc,
|
||||
depth,
|
||||
cur_cu,
|
||||
lcu,
|
||||
false,
|
||||
UVG_BOTH_T);
|
||||
false,
|
||||
UVG_BOTH_T);
|
||||
}
|
||||
|
||||
int cbf = cbf_is_set_any(cur_cu->cbf);
|
||||
|
|
|
@ -271,8 +271,6 @@ static double search_intra_trdepth(
|
|||
lcu_t *const lcu,
|
||||
enum uvg_tree_type tree_type)
|
||||
{
|
||||
|
||||
const uint8_t depth = 6 - uvg_g_convert_to_log2[cu_loc->width];
|
||||
const uint8_t width = cu_loc->width;
|
||||
const uint8_t height = cu_loc->height; // TODO: height for non-square blocks
|
||||
const uint8_t width_c = cu_loc->chroma_width;
|
||||
|
@ -595,7 +593,7 @@ static double search_intra_trdepth(
|
|||
}
|
||||
}
|
||||
|
||||
if (depth == 0 || split_cost < nosplit_cost) {
|
||||
if (!PU_IS_TU(pred_cu) || split_cost < nosplit_cost) {
|
||||
return split_cost;
|
||||
} else {
|
||||
return nosplit_cost;
|
||||
|
@ -1306,7 +1304,6 @@ static int8_t search_intra_rdo(
|
|||
enum uvg_tree_type tree_type,
|
||||
const cu_loc_t* const cu_loc)
|
||||
{
|
||||
const int8_t depth = 6 - uvg_g_convert_to_log2[cu_loc->width];
|
||||
const int width = cu_loc->width;
|
||||
const int height = cu_loc->height; // TODO: height for non-square blocks
|
||||
|
||||
|
|
|
@ -844,16 +844,13 @@ void uvg_fwd_lfnst(
|
|||
const uint16_t lfnst_index = lfnst_idx;
|
||||
int8_t intra_mode = (color == COLOR_Y) ? cur_cu->intra.mode : cur_cu->intra.mode_chroma;
|
||||
bool mts_skip = cur_cu->tr_idx == MTS_SKIP;
|
||||
const int depth = cur_cu->depth;
|
||||
bool is_separate_tree = depth == 4 || tree_type != UVG_BOTH_T;
|
||||
bool is_separate_tree = cur_cu->log2_height + cur_cu->log2_width < 6 || tree_type != UVG_BOTH_T;
|
||||
bool is_cclm_mode = (intra_mode >= 81 && intra_mode <= 83); // CCLM modes are in [81, 83]
|
||||
|
||||
bool is_mip = block_is_mip(cur_cu, color, is_separate_tree);
|
||||
bool is_wide_angle = false; // TODO: get wide angle mode when implemented
|
||||
|
||||
const int cu_type = cur_cu->type;
|
||||
|
||||
const int scan_order = uvg_get_scan_order(cu_type, intra_mode, depth);
|
||||
|
||||
const int scan_order = SCAN_DIAG;
|
||||
|
||||
if (lfnst_index && !mts_skip && (is_separate_tree || color == COLOR_Y))
|
||||
{
|
||||
|
@ -981,16 +978,13 @@ void uvg_inv_lfnst(
|
|||
const uint32_t lfnst_index = lfnst_idx;
|
||||
int8_t intra_mode = (color == COLOR_Y) ? cur_cu->intra.mode : cur_cu->intra.mode_chroma;
|
||||
bool mts_skip = cur_cu->tr_idx == MTS_SKIP;
|
||||
const int depth = cur_cu->depth;
|
||||
bool is_separate_tree = depth == 4 || tree_type != UVG_BOTH_T;
|
||||
bool is_separate_tree = cur_cu->log2_height + cur_cu->log2_width < 6 || tree_type != UVG_BOTH_T;
|
||||
bool is_cclm_mode = (intra_mode >= 81 && intra_mode <= 83); // CCLM modes are in [81, 83]
|
||||
|
||||
bool is_mip = block_is_mip(cur_cu, color, is_separate_tree);
|
||||
bool is_wide_angle = false; // TODO: get wide angle mode when implemented
|
||||
|
||||
const int cu_type = cur_cu->type;
|
||||
|
||||
const int scan_order = uvg_get_scan_order(cu_type, intra_mode, depth);
|
||||
|
||||
const int scan_order = SCAN_DIAG;
|
||||
|
||||
if (lfnst_index && !mts_skip && (is_separate_tree || color == COLOR_Y)) {
|
||||
const uint32_t log2_block_size = uvg_g_convert_to_log2[width];
|
||||
|
@ -1148,7 +1142,6 @@ static void quantize_tr_residual(
|
|||
encoder_state_t * const state,
|
||||
const color_t color,
|
||||
const cu_loc_t *cu_loc,
|
||||
const uint8_t depth,
|
||||
cu_info_t *cur_pu,
|
||||
lcu_t* lcu,
|
||||
bool early_skip,
|
||||
|
@ -1164,7 +1157,7 @@ static void quantize_tr_residual(
|
|||
// If luma is 4x4, do chroma for the 8x8 luma area when handling the top
|
||||
// left PU because the coordinates are correct.
|
||||
bool handled_elsewhere = color != COLOR_Y &&
|
||||
depth == MAX_DEPTH &&
|
||||
cur_pu->log2_width + cur_pu-> log2_height < 6&&
|
||||
(x % 4 != 0 || y % 4 != 0);
|
||||
if (handled_elsewhere) {
|
||||
return;
|
||||
|
@ -1181,8 +1174,7 @@ static void quantize_tr_residual(
|
|||
const int8_t mode =
|
||||
(color == COLOR_Y) ? cur_pu->intra.mode : cur_pu->intra.mode_chroma;
|
||||
|
||||
const coeff_scan_order_t scan_idx =
|
||||
uvg_get_scan_order(cur_pu->type, mode, depth); // Height does not affect this
|
||||
const coeff_scan_order_t scan_idx = SCAN_DIAG;
|
||||
const int offset = lcu_px.x + lcu_px.y * lcu_width;
|
||||
//const int z_index = xy_to_zorder(lcu_width, lcu_px.x, lcu_px.y);
|
||||
|
||||
|
@ -1355,7 +1347,6 @@ void uvg_quantize_lcu_residual(
|
|||
const bool chroma,
|
||||
const bool jccr,
|
||||
const cu_loc_t * cu_loc,
|
||||
const uint8_t depth,
|
||||
cu_info_t *cur_pu,
|
||||
lcu_t* lcu,
|
||||
bool early_skip,
|
||||
|
@ -1402,17 +1393,10 @@ void uvg_quantize_lcu_residual(
|
|||
cu_loc_t loc;
|
||||
uvg_cu_loc_ctor(&loc, (x + i * offset), (y + j * offset), width >> 1, height >> 1);
|
||||
// jccr is currently not supported if transform is split
|
||||
uvg_quantize_lcu_residual(state, luma, chroma, 0, &loc, depth + 1, NULL, lcu, early_skip, tree_type);
|
||||
uvg_quantize_lcu_residual(state, luma, chroma, 0, &loc, NULL, lcu, early_skip, tree_type);
|
||||
}
|
||||
}
|
||||
|
||||
//const int32_t x2 = x + offset;
|
||||
//const int32_t y2 = y + offset;
|
||||
|
||||
//uvg_quantize_lcu_residual(state, luma, chroma, 0, x, y, depth + 1, NULL, lcu, early_skip, tree_type);
|
||||
//uvg_quantize_lcu_residual(state, luma, chroma, 0, x2, y, depth + 1, NULL, lcu, early_skip, tree_type);
|
||||
//uvg_quantize_lcu_residual(state, luma, chroma, 0, x, y2, depth + 1, NULL, lcu, early_skip, tree_type);
|
||||
//uvg_quantize_lcu_residual(state, luma, chroma, 0, x2, y2, depth + 1, NULL, lcu, early_skip, tree_type);
|
||||
|
||||
|
||||
// Propagate coded block flags from child CUs to parent CU.
|
||||
uint16_t child_cbfs[3] = {
|
||||
|
@ -1433,14 +1417,14 @@ void uvg_quantize_lcu_residual(
|
|||
uvg_cu_loc_ctor(&loc, x, y, width, height);
|
||||
|
||||
if (luma) {
|
||||
quantize_tr_residual(state, COLOR_Y, &loc, depth, cur_pu, lcu, early_skip, tree_type);
|
||||
quantize_tr_residual(state, COLOR_Y, &loc, cur_pu, lcu, early_skip, tree_type);
|
||||
}
|
||||
if (chroma) {
|
||||
quantize_tr_residual(state, COLOR_U, &loc, depth, cur_pu, lcu, early_skip, tree_type);
|
||||
quantize_tr_residual(state, COLOR_V, &loc, depth, cur_pu, lcu, early_skip, tree_type);
|
||||
quantize_tr_residual(state, COLOR_U, &loc, cur_pu, lcu, early_skip, tree_type);
|
||||
quantize_tr_residual(state, COLOR_V, &loc, cur_pu, lcu, early_skip, tree_type);
|
||||
}
|
||||
if (jccr && PU_IS_TU(cur_pu)) {
|
||||
quantize_tr_residual(state, COLOR_UV, &loc, depth, cur_pu, lcu, early_skip, tree_type);
|
||||
quantize_tr_residual(state, COLOR_UV, &loc, cur_pu, lcu, early_skip, tree_type);
|
||||
}
|
||||
if(chroma && jccr && PU_IS_TU(cur_pu)) {
|
||||
assert( 0 && "Trying to quantize both jccr and regular at the same time.\n");
|
||||
|
|
|
@ -96,7 +96,6 @@ void uvg_quantize_lcu_residual(
|
|||
bool chroma,
|
||||
const bool jccr,
|
||||
const cu_loc_t* cu_loc,
|
||||
uint8_t depth,
|
||||
cu_info_t *cur_cu,
|
||||
lcu_t* lcu,
|
||||
bool early_skip,
|
||||
|
|
Loading…
Reference in a new issue