xCheckRDCostMergeTriangle2Nx2N()
,进行三角划分模式预测void EncCu::xCheckRDCostMergeTriangle2Nx2N( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode )
{
const Slice &slice = *tempCS->slice;
const SPS &sps = *tempCS->sps;
CHECK( slice.getSliceType() != B_SLICE, "Triangle mode is only applied to B-slices" );
tempCS->initStructData( encTestMode.qp, encTestMode.lossless );
bool trianglecandHasNoResidual[TRIANGLE_MAX_NUM_CANDS];
for( int mergeCand = 0; mergeCand < TRIANGLE_MAX_NUM_CANDS; mergeCand++ )
{
trianglecandHasNoResidual[mergeCand] = false;
}
bool bestIsSkip;
CodingUnit* cuTemp = bestCS->getCU(partitioner.chType);
if (cuTemp)
bestIsSkip = m_pcEncCfg->getUseFastDecisionForMerge() ? bestCS->getCU(partitioner.chType)->rootCbf == 0 : false;
else
bestIsSkip = false;
uint8_t numTriangleCandidate = TRIANGLE_MAX_NUM_CANDS;
uint8_t triangleNumMrgSATDCand = TRIANGLE_MAX_NUM_SATD_CANDS;
PelUnitBuf triangleBuffer[TRIANGLE_MAX_NUM_UNI_CANDS];
PelUnitBuf triangleWeightedBuffer[TRIANGLE_MAX_NUM_CANDS];
static_vector<uint8_t, TRIANGLE_MAX_NUM_CANDS> triangleRdModeList;
static_vector<double, TRIANGLE_MAX_NUM_CANDS> tianglecandCostList;
if( auto blkCache = dynamic_cast< CacheBlkInfoCtrl* >( m_modeCtrl ) )
{
bestIsSkip |= blkCache->isSkip( tempCS->area );
}
DistParam distParam;
const bool useHadamard = !encTestMode.lossless;
m_pcRdCost->setDistParam( distParam, tempCS->getOrgBuf().Y(), m_acMergeBuffer[0].Y(), sps.getBitDepth( CHANNEL_TYPE_LUMA ), COMPONENT_Y, useHadamard );
const UnitArea localUnitArea( tempCS->area.chromaFormat, Area( 0, 0, tempCS->area.Y().width, tempCS->area.Y().height) );
const double sqrtLambdaForFirstPass = m_pcRdCost->getMotionLambda(encTestMode.lossless);
MergeCtx triangleMrgCtx;
{
CodingUnit cu( tempCS->area );
cu.cs = tempCS;
cu.predMode = MODE_INTER;
cu.slice = tempCS->slice;
#if HEVC_TILES_WPP
cu.tileIdx = tempCS->picture->tileMap->getTileIdxMap( tempCS->area.lumaPos() );
#endif
cu.triangle = true;
cu.mmvdSkip = false;
cu.GBiIdx = GBI_DEFAULT;
PredictionUnit pu( tempCS->area );
pu.cu = &cu;
pu.cs = tempCS;
PU::getTriangleMergeCandidates( pu, triangleMrgCtx );//构建MV候选列表,均为单向预测
for( uint8_t mergeCand = 0; mergeCand < TRIANGLE_MAX_NUM_UNI_CANDS; mergeCand++ )//获取不同MV下CU的参考块,便于后续加权组合以及计算RDcost
{
triangleBuffer[mergeCand] = m_acMergeBuffer[mergeCand].getBuf(localUnitArea);
triangleMrgCtx.setMergeInfo( pu, mergeCand );
PU::spanMotionInfo( pu, triangleMrgCtx );
if( m_pcEncCfg->getMCTSEncConstraint() && ( !( MCTSHelper::checkMvBufferForMCTSConstraint( pu ) ) ) )
{
// Do not use this mode
tempCS->initStructData( encTestMode.qp, encTestMode.lossless );
return;
}
m_pcInterSearch->motionCompensation( pu, triangleBuffer[mergeCand] );
}
}
bool tempBufSet = bestIsSkip ? false : true;
triangleNumMrgSATDCand = bestIsSkip ? TRIANGLE_MAX_NUM_CANDS : TRIANGLE_MAX_NUM_SATD_CANDS;
if( bestIsSkip )
{
for( uint8_t i = 0; i < TRIANGLE_MAX_NUM_CANDS; i++ )
{
triangleRdModeList.push_back(i);
}
}
else
{
CodingUnit &cu = tempCS->addCU( tempCS->area, partitioner.chType );
partitioner.setCUData( cu );
cu.slice = tempCS->slice;
#if HEVC_TILES_WPP
cu.tileIdx = tempCS->picture->tileMap->getTileIdxMap( tempCS->area.lumaPos() );
#endif
cu.skip = false;
cu.predMode = MODE_INTER;
cu.transQuantBypass = encTestMode.lossless;
cu.chromaQpAdj = cu.transQuantBypass ? 0 : m_cuChromaQpOffsetIdxPlus1;
cu.qp = encTestMode.qp;
cu.triangle = true;
cu.mmvdSkip = false;
cu.GBiIdx = GBI_DEFAULT;
PredictionUnit &pu = tempCS->addPU( cu, partitioner.chType );
if( abs(g_aucLog2[cu.lwidth()] - g_aucLog2[cu.lheight()]) >= 2 )
{
numTriangleCandidate = 30;
}
else
{
numTriangleCandidate = TRIANGLE_MAX_NUM_CANDS;
}
for( uint8_t mergeCand = 0; mergeCand < numTriangleCandidate; mergeCand++ )//遍历所有
{
bool splitDir = m_triangleModeTest[mergeCand].m_splitDir;
uint8_t candIdx0 = m_triangleModeTest[mergeCand].m_candIdx0;
uint8_t candIdx1 = m_triangleModeTest[mergeCand].m_candIdx1;
pu.triangleSplitDir = splitDir;
pu.triangleMergeIdx0 = candIdx0;
pu.triangleMergeIdx1 = candIdx1;
pu.mergeFlag = true;
triangleWeightedBuffer[mergeCand] = m_acTriangleWeightedBuffer[mergeCand].getBuf( localUnitArea );
triangleBuffer[candIdx0] = m_acMergeBuffer[candIdx0].getBuf( localUnitArea );
triangleBuffer[candIdx1] = m_acMergeBuffer[candIdx1].getBuf( localUnitArea );
m_pcInterSearch->weightedTriangleBlk( pu, splitDir, CHANNEL_TYPE_LUMA, triangleWeightedBuffer[mergeCand], triangleBuffer[candIdx0], triangleBuffer[candIdx1] );
distParam.cur = triangleWeightedBuffer[mergeCand].Y();
Distortion uiSad = distParam.distFunc( distParam );
uint32_t uiBitsCand = m_triangleIdxBins[splitDir][candIdx0][candIdx1];
double cost = (double)uiSad + (double)uiBitsCand * sqrtLambdaForFirstPass;
static_vector<int, TRIANGLE_MAX_NUM_CANDS> * nullList = nullptr;
updateCandList( mergeCand, cost, triangleRdModeList, tianglecandCostList
, *nullList, -1
, triangleNumMrgSATDCand );
}
// limit number of candidates using SATD-costs
for( uint8_t i = 0; i < triangleNumMrgSATDCand; i++ )
{
if( tianglecandCostList[i] > MRG_FAST_RATIO * tianglecandCostList[0] || tianglecandCostList[i] > getMergeBestSATDCost() )
{
triangleNumMrgSATDCand = i;
break;
}
}
// perform chroma weighting process
for( uint8_t i = 0; i < triangleNumMrgSATDCand; i++ )//对样点进行加权组合
{
uint8_t mergeCand = triangleRdModeList[i];
bool splitDir = m_triangleModeTest[mergeCand].m_splitDir;
uint8_t candIdx0 = m_triangleModeTest[mergeCand].m_candIdx0;
uint8_t candIdx1 = m_triangleModeTest[mergeCand].m_candIdx1;
pu.triangleSplitDir = splitDir;
pu.triangleMergeIdx0 = candIdx0;
pu.triangleMergeIdx1 = candIdx1;
pu.mergeFlag = true;
m_pcInterSearch->weightedTriangleBlk( pu, splitDir, CHANNEL_TYPE_CHROMA, triangleWeightedBuffer[mergeCand], triangleBuffer[candIdx0], triangleBuffer[candIdx1] );
}
tempCS->initStructData( encTestMode.qp, encTestMode.lossless );
}
m_bestModeUpdated = tempCS->useDbCost = bestCS->useDbCost = false;
{
uint8_t iteration;
uint8_t iterationBegin = m_modeCtrl->getIsHashPerfectMatch() ? 1 : 0;
if (encTestMode.lossless)
{
iteration = 1;
iterationBegin = 0;
}
else
{
iteration = 2;
}
for (uint8_t noResidualPass = iterationBegin; noResidualPass < iteration; ++noResidualPass)
{
for( uint8_t mrgHADIdx = 0; mrgHADIdx < triangleNumMrgSATDCand; mrgHADIdx++ )
{
uint8_t mergeCand = triangleRdModeList[mrgHADIdx];
if ( ( (noResidualPass != 0) && trianglecandHasNoResidual[mergeCand] )
|| ( (noResidualPass == 0) && bestIsSkip ) )
{
continue;
}
bool splitDir = m_triangleModeTest[mergeCand].m_splitDir;
uint8_t candIdx0 = m_triangleModeTest[mergeCand].m_candIdx0;
uint8_t candIdx1 = m_triangleModeTest[mergeCand].m_candIdx1;
CodingUnit &cu = tempCS->addCU(tempCS->area, partitioner.chType);
partitioner.setCUData(cu);
cu.slice = tempCS->slice;
#if HEVC_TILES_WPP
cu.tileIdx = tempCS->picture->tileMap->getTileIdxMap( tempCS->area.lumaPos() );
#endif
cu.skip = false;
cu.predMode = MODE_INTER;
cu.transQuantBypass = encTestMode.lossless;
cu.chromaQpAdj = cu.transQuantBypass ? 0 : m_cuChromaQpOffsetIdxPlus1;
cu.qp = encTestMode.qp;
cu.triangle = true;
cu.mmvdSkip = false;
cu.GBiIdx = GBI_DEFAULT;
PredictionUnit &pu = tempCS->addPU(cu, partitioner.chType);
pu.triangleSplitDir = splitDir;
pu.triangleMergeIdx0 = candIdx0;
pu.triangleMergeIdx1 = candIdx1;
pu.mergeFlag = true;
PU::spanTriangleMotionInfo(pu, triangleMrgCtx, splitDir, candIdx0, candIdx1 );
if( m_pcEncCfg->getMCTSEncConstraint() && ( !( MCTSHelper::checkMvBufferForMCTSConstraint( *cu.firstPU ) ) ) )
{
// Do not use this mode
tempCS->initStructData( encTestMode.qp, encTestMode.lossless );
return;
}
if( tempBufSet )
{
tempCS->getPredBuf().copyFrom( triangleWeightedBuffer[mergeCand] );
}
else
{
triangleBuffer[candIdx0] = m_acMergeBuffer[candIdx0].getBuf( localUnitArea );
triangleBuffer[candIdx1] = m_acMergeBuffer[candIdx1].getBuf( localUnitArea );
PelUnitBuf predBuf = tempCS->getPredBuf();
m_pcInterSearch->weightedTriangleBlk( pu, splitDir, MAX_NUM_CHANNEL_TYPE, predBuf, triangleBuffer[candIdx0], triangleBuffer[candIdx1] );
}
xEncodeInterResidual( tempCS, bestCS, partitioner, encTestMode, noResidualPass, ( noResidualPass == 0 ? &trianglecandHasNoResidual[mergeCand] : NULL ) );
if (m_pcEncCfg->getUseFastDecisionForMerge() && !bestIsSkip)
{
bestIsSkip = bestCS->getCU(partitioner.chType)->rootCbf == 0;
}
tempCS->initStructData(encTestMode.qp, encTestMode.lossless);
}// end loop mrgHADIdx
}
}
if ( m_bestModeUpdated && bestCS->cost != MAX_DOUBLE )
{
xCalDebCost( *bestCS, partitioner );
}
}
getTriangleMergeCandidates()
函数构建候选列表首先按照与Merge相似的方式建立列表MergeCand List:空域——时域;
然后根据MergeCand List建立Triangle候选列表,顺序如下:MergeCand List中的单向预测——MergeCand List中双向预测块的前向预测——MergeCand List中的后向预测——MergeCand List中双向预测前向和后向预测的均值。
void PU::getTriangleMergeCandidates( const PredictionUnit &pu, MergeCtx& triangleMrgCtx )
{
const CodingStructure &cs = *pu.cs;
const Slice &slice = *pu.cs->slice;
const int32_t maxNumMergeCand = TRIANGLE_MAX_NUM_UNI_CANDS;
triangleMrgCtx.numValidMergeCand = 0;
for( int32_t i = 0; i < maxNumMergeCand; i++ )
{
triangleMrgCtx.interDirNeighbours[i] = 0;
triangleMrgCtx.mrgTypeNeighbours [i] = MRG_TYPE_DEFAULT_N;
triangleMrgCtx.mvFieldNeighbours[(i << 1) ].refIdx = NOT_VALID;
triangleMrgCtx.mvFieldNeighbours[(i << 1) + 1].refIdx = NOT_VALID;
triangleMrgCtx.mvFieldNeighbours[(i << 1) ].mv = Mv();
triangleMrgCtx.mvFieldNeighbours[(i << 1) + 1].mv = Mv();
}
MotionInfo candidate[TRIANGLE_MAX_NUM_CANDS_MEM];
int32_t candCount = 0;
const Position posLT = pu.Y().topLeft();
const Position posRT = pu.Y().topRight();
const Position posLB = pu.Y().bottomLeft();
MotionInfo miAbove, miLeft, miAboveLeft, miAboveRight, miBelowLeft;
//left
const PredictionUnit* puLeft = cs.getPURestricted( posLB.offset( -1, 0 ), pu, pu.chType );
const bool isAvailableA1 = puLeft && isDiffMER( pu, *puLeft ) && pu.cu != puLeft->cu && CU::isInter( *puLeft->cu )
;
if( isAvailableA1 )
{
miLeft = puLeft->getMotionInfo( posLB.offset(-1, 0) );
candidate[candCount].isInter = true;
candidate[candCount].interDir = miLeft.interDir;
candidate[candCount].mv[0] = miLeft.mv[0];
candidate[candCount].mv[1] = miLeft.mv[1];
candidate[candCount].refIdx[0] = miLeft.refIdx[0];
candidate[candCount].refIdx[1] = miLeft.refIdx[1];
candCount++;
}
// above
const PredictionUnit *puAbove = cs.getPURestricted( posRT.offset( 0, -1 ), pu, pu.chType );
bool isAvailableB1 = puAbove && isDiffMER( pu, *puAbove ) && pu.cu != puAbove->cu && CU::isInter( *puAbove->cu )
;
if( isAvailableB1 )
{
miAbove = puAbove->getMotionInfo( posRT.offset( 0, -1 ) );
if( !isAvailableA1 || ( miAbove != miLeft ) )
{
candidate[candCount].isInter = true;
candidate[candCount].interDir = miAbove.interDir;
candidate[candCount].mv[0] = miAbove.mv[0];
candidate[candCount].mv[1] = miAbove.mv[1];
candidate[candCount].refIdx[0] = miAbove.refIdx[0];
candidate[candCount].refIdx[1] = miAbove.refIdx[1];
candCount++;
}
}
// above right
const PredictionUnit *puAboveRight = cs.getPURestricted( posRT.offset( 1, -1 ), pu, pu.chType );
bool isAvailableB0 = puAboveRight && isDiffMER( pu, *puAboveRight ) && CU::isInter( *puAboveRight->cu )
;
if( isAvailableB0 )
{
miAboveRight = puAboveRight->getMotionInfo( posRT.offset( 1, -1 ) );
if( ( !isAvailableB1 || ( miAbove != miAboveRight ) ) && ( !isAvailableA1 || ( miLeft != miAboveRight ) ) )
{
candidate[candCount].isInter = true;
candidate[candCount].interDir = miAboveRight.interDir;
candidate[candCount].mv[0] = miAboveRight.mv[0];
candidate[candCount].mv[1] = miAboveRight.mv[1];
candidate[candCount].refIdx[0] = miAboveRight.refIdx[0];
candidate[candCount].refIdx[1] = miAboveRight.refIdx[1];
candCount++;
}
}
//left bottom
const PredictionUnit *puLeftBottom = cs.getPURestricted( posLB.offset( -1, 1 ), pu, pu.chType );
bool isAvailableA0 = puLeftBottom && isDiffMER( pu, *puLeftBottom ) && CU::isInter( *puLeftBottom->cu )
;
if( isAvailableA0 )
{
miBelowLeft = puLeftBottom->getMotionInfo( posLB.offset( -1, 1 ) );
if( ( !isAvailableA1 || ( miBelowLeft != miLeft ) ) && ( !isAvailableB1 || ( miBelowLeft != miAbove ) ) && ( !isAvailableB0 || ( miBelowLeft != miAboveRight ) ) )
{
candidate[candCount].isInter = true;
candidate[candCount].interDir = miBelowLeft.interDir;
candidate[candCount].mv[0] = miBelowLeft.mv[0];
candidate[candCount].mv[1] = miBelowLeft.mv[1];
candidate[candCount].refIdx[0] = miBelowLeft.refIdx[0];
candidate[candCount].refIdx[1] = miBelowLeft.refIdx[1];
candCount++;
}
}
// above left
const PredictionUnit *puAboveLeft = cs.getPURestricted( posLT.offset( -1, -1 ), pu, pu.chType );
bool isAvailableB2 = puAboveLeft && isDiffMER( pu, *puAboveLeft ) && CU::isInter( *puAboveLeft->cu )
;
if( isAvailableB2 )
{
miAboveLeft = puAboveLeft->getMotionInfo( posLT.offset( -1, -1 ) );
if( ( !isAvailableA1 || ( miLeft != miAboveLeft ) ) && ( !isAvailableB1 || ( miAbove != miAboveLeft ) ) && ( !isAvailableA0 || ( miBelowLeft != miAboveLeft ) ) && ( !isAvailableB0 || ( miAboveRight != miAboveLeft ) ) )
{
candidate[candCount].isInter = true;
candidate[candCount].interDir = miAboveLeft.interDir;
candidate[candCount].mv[0] = miAboveLeft.mv[0];
candidate[candCount].mv[1] = miAboveLeft.mv[1];
candidate[candCount].refIdx[0] = miAboveLeft.refIdx[0];
candidate[candCount].refIdx[1] = miAboveLeft.refIdx[1];
candCount++;
}
}
if( slice.getEnableTMVPFlag() )
{
Position posRB = pu.Y().bottomRight().offset(-3, -3);
const PreCalcValues& pcv = *cs.pcv;
Position posC0;
Position posC1 = pu.Y().center();
bool isAvailableC0 = false;
bool isAvailableC1 = (posC1.x < pcv.lumaWidth) && (posC1.y < pcv.lumaHeight);
if (((posRB.x + pcv.minCUWidth) < pcv.lumaWidth) && ((posRB.y + pcv.minCUHeight) < pcv.lumaHeight))
{
Position posInCtu( posRB.x & pcv.maxCUWidthMask, posRB.y & pcv.maxCUHeightMask );
if( ( posInCtu.x + 4 < pcv.maxCUWidth ) && // is not at the last column of CTU
( posInCtu.y + 4 < pcv.maxCUHeight ) ) // is not at the last row of CTU
{
posC0 = posRB.offset( 4, 4 );
isAvailableC0 = true;
}
else if( posInCtu.x + 4 < pcv.maxCUWidth ) // is not at the last column of CTU But is last row of CTU
{
posC0 = posRB.offset( 4, 4 );
// in the reference the CTU address is not set - thus probably resulting in no using this C0 possibility
}
else if( posInCtu.y + 4 < pcv.maxCUHeight ) // is not at the last row of CTU But is last column of CTU
{
posC0 = posRB.offset( 4, 4 );
isAvailableC0 = true;
}
else //is the right bottom corner of CTU
{
posC0 = posRB.offset( 4, 4 );
// same as for last column but not last row
}
}
// C0
Mv cColMv;
int32_t refIdx = 0;
bool existMV = ( isAvailableC0 && getColocatedMVP( pu, REF_PIC_LIST_0, posC0, cColMv, refIdx ) );
MotionInfo temporalMv;
temporalMv.interDir = 0;
if( existMV )
{
temporalMv.isInter = true;
temporalMv.interDir |= 1;
temporalMv.mv[0] = cColMv;
temporalMv.refIdx[0] = refIdx;
}
existMV = ( isAvailableC0 && getColocatedMVP( pu, REF_PIC_LIST_1, posC0, cColMv, refIdx ) );
if( existMV )
{
temporalMv.interDir |= 2;
temporalMv.mv[1] = cColMv;
temporalMv.refIdx[1] = refIdx;
}
if( temporalMv.interDir != 0 )
{
candidate[candCount].isInter = true;
candidate[candCount].interDir = temporalMv.interDir;
candidate[candCount].mv[0] = temporalMv.mv[0];
candidate[candCount].mv[1] = temporalMv.mv[1];
candidate[candCount].refIdx[0] = temporalMv.refIdx[0];
candidate[candCount].refIdx[1] = temporalMv.refIdx[1];
candCount++;
}
// C1
temporalMv.interDir = 0;
existMV = isAvailableC1 && getColocatedMVP(pu, REF_PIC_LIST_0, posC1, cColMv, refIdx );
if( existMV )
{
temporalMv.isInter = true;
temporalMv.interDir |= 1;
temporalMv.mv[0] = cColMv;
temporalMv.refIdx[0] = refIdx;
}
existMV = isAvailableC1 && getColocatedMVP(pu, REF_PIC_LIST_1, posC1, cColMv, refIdx );
if( existMV )
{
temporalMv.interDir |= 2;
temporalMv.mv[1] = cColMv;
temporalMv.refIdx[1] = refIdx;
}
if( temporalMv.interDir != 0 )
{
candidate[candCount].isInter = true;
candidate[candCount].interDir = temporalMv.interDir;
candidate[candCount].mv[0] = temporalMv.mv[0];
candidate[candCount].mv[1] = temporalMv.mv[1];
candidate[candCount].refIdx[0] = temporalMv.refIdx[0];
candidate[candCount].refIdx[1] = temporalMv.refIdx[1];
candCount++;
}
}
//开建立Triangle 候选列表
// put uni-prediction candidate to the triangle candidate list
for( int32_t i = 0; i < candCount; i++ )
{
if( candidate[i].interDir != 3 )
{
triangleMrgCtx.interDirNeighbours[triangleMrgCtx.numValidMergeCand] = candidate[i].interDir;
triangleMrgCtx.mrgTypeNeighbours [triangleMrgCtx.numValidMergeCand] = MRG_TYPE_DEFAULT_N;
triangleMrgCtx.mvFieldNeighbours [(triangleMrgCtx.numValidMergeCand << 1) ].mv = candidate[i].mv[0];
triangleMrgCtx.mvFieldNeighbours [(triangleMrgCtx.numValidMergeCand << 1) + 1].mv = candidate[i].mv[1];
triangleMrgCtx.mvFieldNeighbours [(triangleMrgCtx.numValidMergeCand << 1) ].refIdx = candidate[i].refIdx[0];
triangleMrgCtx.mvFieldNeighbours [(triangleMrgCtx.numValidMergeCand << 1) + 1].refIdx = candidate[i].refIdx[1];
triangleMrgCtx.numValidMergeCand += isUniqueTriangleCandidates(pu, triangleMrgCtx);
if( triangleMrgCtx.numValidMergeCand == TRIANGLE_MAX_NUM_UNI_CANDS )
{
return;
}
}
}
// put L0 mv of bi-prediction candidate to the triangle candidate list
for( int32_t i = 0; i < candCount; i++ )
{
if( candidate[i].interDir == 3 )
{
triangleMrgCtx.interDirNeighbours[triangleMrgCtx.numValidMergeCand] = 1;
triangleMrgCtx.mrgTypeNeighbours [triangleMrgCtx.numValidMergeCand] = MRG_TYPE_DEFAULT_N;
triangleMrgCtx.mvFieldNeighbours [(triangleMrgCtx.numValidMergeCand << 1) ].mv = candidate[i].mv[0];
triangleMrgCtx.mvFieldNeighbours [(triangleMrgCtx.numValidMergeCand << 1) + 1].mv = Mv(0, 0);
triangleMrgCtx.mvFieldNeighbours [(triangleMrgCtx.numValidMergeCand << 1) ].refIdx = candidate[i].refIdx[0];
triangleMrgCtx.mvFieldNeighbours [(triangleMrgCtx.numValidMergeCand << 1) + 1].refIdx = -1;
triangleMrgCtx.numValidMergeCand += isUniqueTriangleCandidates(pu, triangleMrgCtx);
if( triangleMrgCtx.numValidMergeCand == TRIANGLE_MAX_NUM_UNI_CANDS )
{
return;
}
}
}
// put L1 mv of bi-prediction candidate to the triangle candidate list
for( int32_t i = 0; i < candCount; i++ )
{
if( candidate[i].interDir == 3 )
{
triangleMrgCtx.interDirNeighbours[triangleMrgCtx.numValidMergeCand] = 2;
triangleMrgCtx.mrgTypeNeighbours [triangleMrgCtx.numValidMergeCand] = MRG_TYPE_DEFAULT_N;
triangleMrgCtx.mvFieldNeighbours [(triangleMrgCtx.numValidMergeCand << 1) ].mv = Mv(0, 0);
triangleMrgCtx.mvFieldNeighbours [(triangleMrgCtx.numValidMergeCand << 1) + 1].mv = candidate[i].mv[1];
triangleMrgCtx.mvFieldNeighbours [(triangleMrgCtx.numValidMergeCand << 1) ].refIdx = -1;
triangleMrgCtx.mvFieldNeighbours [(triangleMrgCtx.numValidMergeCand << 1) + 1].refIdx = candidate[i].refIdx[1];
triangleMrgCtx.numValidMergeCand += isUniqueTriangleCandidates(pu, triangleMrgCtx);
if( triangleMrgCtx.numValidMergeCand == TRIANGLE_MAX_NUM_UNI_CANDS )
{
return;
}
}
}
// put average of L0 and L1 mvs of bi-prediction candidate to the triangle candidate list
for( int32_t i = 0; i < candCount; i++ )
{
if( candidate[i].interDir == 3 )
{
int32_t curPicPoc = slice.getPOC();
int32_t refPicPocL0 = slice.getRefPOC(REF_PIC_LIST_0, candidate[i].refIdx[0]);
int32_t refPicPocL1 = slice.getRefPOC(REF_PIC_LIST_1, candidate[i].refIdx[1]);
Mv aveMv = candidate[i].mv[1];
int32_t distscale = xGetDistScaleFactor( curPicPoc, refPicPocL0, curPicPoc, refPicPocL1 );
if( distscale != 4096 )
{
aveMv = aveMv.scaleMv( distscale ); // scaling to L0
}
aveMv = aveMv + candidate[i].mv[0];
roundAffineMv(aveMv.hor, aveMv.ver, 1);
triangleMrgCtx.interDirNeighbours[triangleMrgCtx.numValidMergeCand] = 1;
triangleMrgCtx.mrgTypeNeighbours [triangleMrgCtx.numValidMergeCand] = MRG_TYPE_DEFAULT_N;
triangleMrgCtx.mvFieldNeighbours [(triangleMrgCtx.numValidMergeCand << 1) ].mv = aveMv;
triangleMrgCtx.mvFieldNeighbours [(triangleMrgCtx.numValidMergeCand << 1) + 1].mv = Mv(0, 0);
triangleMrgCtx.mvFieldNeighbours [(triangleMrgCtx.numValidMergeCand << 1) ].refIdx = candidate[i].refIdx[0];
triangleMrgCtx.mvFieldNeighbours [(triangleMrgCtx.numValidMergeCand << 1) + 1].refIdx = -1;
triangleMrgCtx.numValidMergeCand += isUniqueTriangleCandidates(pu, triangleMrgCtx);
if( triangleMrgCtx.numValidMergeCand == TRIANGLE_MAX_NUM_UNI_CANDS )
{
return;
}
}
}
// fill with Mv(0, 0)
int32_t numRefIdx = std::min( slice.getNumRefIdx(REF_PIC_LIST_0), slice.getNumRefIdx(REF_PIC_LIST_1) );
int32_t cnt = 0;
while( triangleMrgCtx.numValidMergeCand < TRIANGLE_MAX_NUM_UNI_CANDS )
{
if( cnt < numRefIdx )
{
triangleMrgCtx.interDirNeighbours[triangleMrgCtx.numValidMergeCand] = 1;
triangleMrgCtx.mvFieldNeighbours[triangleMrgCtx.numValidMergeCand << 1].setMvField(Mv(0, 0), cnt);
triangleMrgCtx.mvFieldNeighbours[(triangleMrgCtx.numValidMergeCand << 1) + 1].refIdx = NOT_VALID;
triangleMrgCtx.mvFieldNeighbours[(triangleMrgCtx.numValidMergeCand << 1) + 1].mv = Mv();
triangleMrgCtx.numValidMergeCand++;
if( triangleMrgCtx.numValidMergeCand == TRIANGLE_MAX_NUM_UNI_CANDS )
{
return;
}
triangleMrgCtx.interDirNeighbours[triangleMrgCtx.numValidMergeCand] = 2;
triangleMrgCtx.mvFieldNeighbours [(triangleMrgCtx.numValidMergeCand << 1) + 1 ].setMvField(Mv(0, 0), cnt);
triangleMrgCtx.mvFieldNeighbours[triangleMrgCtx.numValidMergeCand << 1].refIdx = NOT_VALID;
triangleMrgCtx.mvFieldNeighbours[triangleMrgCtx.numValidMergeCand << 1].mv = Mv();
triangleMrgCtx.numValidMergeCand++;
cnt = (cnt + 1) % numRefIdx;
}
}
}