xCheckRDCostMerge2Nx2N在xCompressCU函数中调用,用来测试Merge模式、mmvd模式以及CIIP模式。
首先由getInterMergeCandidates获取merge候选模式信息,存储于,mergeCtx。同时由merge列表获取MMVD模式的两个baseMV。
如果不进行fastMerge,则跳过SATD直接进行RDO选最优
fastMerge时,对7个merge候选模式进行SATD,分别进行MC获得pred像素,选取cost最小的4个merge候选;对于MMVD模式分别以这两个mv为中心进行4方向8步长的计算,得到最优的baseMV、方向和步长。CIIP模式则选择经过SATD之后的前4个merge模式,分别于帧内模式结合,选取最优的merge模式和帧内模式。
对RdModeList中的merge候选、MMVD模式以及CIIP模式,统一进行RDO选择最优。
void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode )
{
const Slice &slice = *tempCS->slice;
CHECK( slice.getSliceType() == I_SLICE, "Merge modes not available for I-slices" );
tempCS->initStructData( encTestMode.qp, encTestMode.lossless ); //tempCS清空初始化
MergeCtx mergeCtx;
const SPS &sps = *tempCS->sps;
if( sps.getSpsNext().getUseSubPuMvp() )
{
Size bufSize = g_miScaling.scale( tempCS->area.lumaSize() );
mergeCtx.subPuMvpMiBuf = MotionBuf( m_SubPuMiBuf, bufSize );
}
#if JVET_L0124_L0208_TRIANGLE
setMergeBestSATDCost( MAX_DOUBLE );
#endif
{
// first get merge candidates
CodingUnit cu( tempCS->area ); //创建cu、pu用来获得merge列表
cu.cs = tempCS;
cu.partSize = SIZE_2Nx2N;
cu.predMode = MODE_INTER;
cu.slice = tempCS->slice;
#if HEVC_TILES_WPP
cu.tileIdx = tempCS->picture->tileMap->getTileIdxMap(tempCS->area.lumaPos());
#endif
PredictionUnit pu( tempCS->area );
pu.cu = &cu;
pu.cs = tempCS;
PU::getInterMergeCandidates(pu, mergeCtx //获取merge列表,其中包含7个merge候选信息,存储于mergeCtx
#if JVET_L0054_MMVD
, 0
#endif
);
PU::restrictBiPredMergeCands(pu, mergeCtx);
#if JVET_L0054_MMVD
PU::getInterMMVDMergeCandidates(pu, mergeCtx); //MMVD选择merge列表的前两个候选,作为baseMV
#endif
}
bool candHasNoResidual[MRG_MAX_NUM_CANDS + MMVD_ADD_NUM]; //若merge模式没有resi,则为skip模式
for (uint32_t ui = 0; ui < MRG_MAX_NUM_CANDS + MMVD_ADD_NUM; ui++)
{
candHasNoResidual[ui] = false;
}
bool bestIsSkip = false;
bool bestIsMMVDSkip = true;
#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
PelUnitBuf acMergeBuffer[MRG_MAX_NUM_CANDS]; //里面存储的是:merge列表里面候选模式0~6顺序排列的各模式的pred像素
#endif
PelUnitBuf acMergeRealBuffer[MMVD_MRG_MAX_RD_BUF_NUM];//存储RDModeList前7个mode的pred像素
PelUnitBuf * acMergeTempBuffer[MMVD_MRG_MAX_RD_NUM]; //acMergeTempBuffer中始终存储的是RDModeList前几个模式的pred像素
PelUnitBuf * singleMergeTempBuffer; //这两个是指针
int insertPos;
unsigned uiNumMrgSATDCand = mergeCtx.numValidMergeCand + MMVD_ADD_NUM;
static_vector<unsigned, MRG_MAX_NUM_CANDS + MMVD_ADD_NUM> RdModeList;
bool mrgTempBufSet = false;
for (unsigned i = 0; i < MRG_MAX_NUM_CANDS + MMVD_ADD_NUM; i++)
{
RdModeList.push_back(i); //如果不进行SATD,那么RdModeList中存放7+64个merge候选,直接进行最后的RDO
}
const UnitArea localUnitArea(tempCS->area.chromaFormat, Area(0, 0, tempCS->area.Y().width, tempCS->area.Y().height));
for (unsigned i = 0; i < MMVD_MRG_MAX_RD_BUF_NUM; i++)
{
acMergeRealBuffer[i] = m_acMergeBuffer[i].getBuf(localUnitArea);
if (i < MMVD_MRG_MAX_RD_NUM)
{
acMergeTempBuffer[i] = acMergeRealBuffer + i;
}
else
{
singleMergeTempBuffer = acMergeRealBuffer + i;
}
}/以上为初始化,并获取Merge列表
#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
#if JVET_L0054_MMVD
static_vector<unsigned, MRG_MAX_NUM_CANDS + MMVD_ADD_NUM> RdModeList2; // store the Intra mode for Intrainter
#else
static_vector<unsigned, MRG_MAX_NUM_CANDS> RdModeList2; // store the Intra mode for Intrainter
#endif
RdModeList2.clear();
bool isIntrainterEnabled = sps.getSpsNext().getUseMHIntra();
if (bestCS->area.lwidth() * bestCS->area.lheight() < 64 || bestCS->area.lwidth() >= MAX_CU_SIZE || bestCS->area.lheight() >= MAX_CU_SIZE)
{
isIntrainterEnabled = false;
}
bool isTestSkipMerge[MRG_MAX_NUM_CANDS]; // record if the merge candidate has tried skip mode
for (uint32_t idx = 0; idx < MRG_MAX_NUM_CANDS; idx++)
{
isTestSkipMerge[idx] = false;
}
#endif
#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
if( m_pcEncCfg->getUseFastMerge() || isIntrainterEnabled) // FastMerge || CIIP
#else
if( m_pcEncCfg->getUseFastMerge() )
#endif
{
uiNumMrgSATDCand = NUM_MRG_SATD_CAND; //SATD粗选择后的merge候选数目,4
#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
if (isIntrainterEnabled)
{
uiNumMrgSATDCand += 1; //4加一个CIIP模式
}
#endif
bestIsSkip = false;
if( auto blkCache = dynamic_cast< CacheBlkInfoCtrl* >( m_modeCtrl ) )
{
#if JVET_L0293_CPR
if (slice.getSPS()->getSpsNext().getCPRMode())
{
ComprCUCtx cuECtx = m_modeCtrl->getComprCUCtx();
bestIsSkip = blkCache->isSkip(tempCS->area) && cuECtx.bestCU;
}
else
#endif
bestIsSkip = blkCache->isSkip( tempCS->area );
#if JVET_L0054_MMVD
bestIsMMVDSkip = blkCache->isMMVDSkip(tempCS->area);
#endif
}
#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
if (isIntrainterEnabled) // always perform low complexity check
{
bestIsSkip = false;
}
#endif
#if JVET_L0054_MMVD
static_vector<double, MRG_MAX_NUM_CANDS + MMVD_ADD_NUM> candCostList; //存放SATD时得到的7+64个候选的cost
#else
static_vector<double, MRG_MAX_NUM_CANDS> candCostList;
#endif
// 1. Pass: get SATD-cost for selected candidates and reduce their count
if( !bestIsSkip ) //不是skip模式,对7+64个merge候选模式进行SATD粗选择
{
RdModeList.clear(); //RdModeList前面有压栈操作,这里清空
mrgTempBufSet = true;
const double sqrtLambdaForFirstPass = m_pcRdCost->getMotionLambda( encTestMode.lossless );
CodingUnit &cu = tempCS->addCU( tempCS->area, partitioner.chType ); //tempCS添加cu和pu
#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
const double sqrtLambdaForFirstPassIntra = m_pcRdCost->getMotionLambda(cu.transQuantBypass) / double(1 << SCALE_BITS);
#endif
partitioner.setCUData( cu );
cu.slice = tempCS->slice;
#if HEVC_TILES_WPP
cu.tileIdx = tempCS->picture->tileMap->getTileIdxMap( tempCS->area.lumaPos() );
#endif
cu.skip = false;
#if JVET_L0054_MMVD
cu.mmvdSkip = false;
#endif
#if JVET_L0124_L0208_TRIANGLE
cu.triangle = false;
#endif
cu.partSize = SIZE_2Nx2N;
//cu.affine
cu.predMode = MODE_INTER;
//cu.LICFlag
cu.transQuantBypass = encTestMode.lossless;
cu.chromaQpAdj = cu.transQuantBypass ? 0 : m_cuChromaQpOffsetIdxPlus1;
cu.qp = encTestMode.qp;
//cu.emtFlag is set below
PredictionUnit &pu = tempCS->add