Newer
Older
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
{
for( int i = 0; i < 2; i++ )
{
if( trModes[ i ].second )
{
lastCheckId = trModes[ i ].first;
}
}
}
}
else
{
xIntraCodingTUBlock( tu, COMPONENT_Y, false, singleDistTmpLuma, default0Save1Load2, &numSig );
}
}
else
{
if( nNumTransformCands > 1 )
{
xIntraCodingTUBlock( tu, COMPONENT_Y, false, singleDistTmpLuma, default0Save1Load2, &numSig, modeId == 0 ? &trModes : nullptr, true );
if( modeId == 0 )
{
for( int i = 0; i < nNumTransformCands; i++ )
{
if( trModes[ i ].second )
{
lastCheckId = trModes[ i ].first;
}
}
}
}
else
{
xIntraCodingTUBlock( tu, COMPONENT_Y, false, singleDistTmpLuma, default0Save1Load2, &numSig );
}
}

Karsten Suehring
committed
//----- determine rate and r-d cost -----
if( ( sps.getUseLFNST() ? ( modeId == lastCheckId && modeId != 0 && checkTransformSkip ) : ( trModes[ modeId ].first != 0 ) ) && !TU::getCbfAtDepth( tu, COMPONENT_Y, currDepth ) )

Karsten Suehring
committed
{
//In order not to code TS flag when cbf is zero, the case for TS with cbf being zero is forbidden.
singleCostTmp = MAX_DOUBLE;
}
else
{
if( cu.ispMode && m_pcRdCost->calcRdCost( csFull->fracBits, csFull->dist + singleDistTmpLuma ) > bestCostSoFar )
{
earlySkipISP = true;
}
else
{
singleTmpFracBits = xGetIntraFracBitsQT( *csFull, partitioner, true, false, subTuCounter, ispType );
}

Karsten Suehring
committed
singleCostTmp = m_pcRdCost->calcRdCost( singleTmpFracBits, singleDistTmpLuma );
}
Santiago de Luxán Hernández
committed
if ( !cu.ispMode && nNumTransformCands > 1 && modeId == firstCheckId )
{
bestDCT2cost = singleCostTmp;
}

Karsten Suehring
committed
if (singleCostTmp < dSingleCost)
{
dSingleCost = singleCostTmp;
uiSingleDistLuma = singleDistTmpLuma;
singleFracBits = singleTmpFracBits;
if( sps.getUseLFNST() )
bestModeId[ COMPONENT_Y ] = modeId;
cbfBestMode = TU::getCbfAtDepth( tu, COMPONENT_Y, currDepth );
cbfBestModeValid = true;
validReturnFull = true;
else
{
bestModeId[ COMPONENT_Y ] = trModes[ modeId ].first;
if( trModes[ modeId ].first == 0 )
{
cbfDCT2 = TU::getCbfAtDepth( tu, COMPONENT_Y, currDepth );
}
}

Karsten Suehring
committed
if( bestModeId[COMPONENT_Y] != lastCheckId )
{
saveCS.getPredBuf( tu.Y() ).copyFrom( csFull->getPredBuf( tu.Y() ) );
saveCS.getRecoBuf( tu.Y() ).copyFrom( csFull->getRecoBuf( tu.Y() ) );
if( keepResi )
{
saveCS.getResiBuf ( tu.Y() ).copyFrom( csFull->getResiBuf ( tu.Y() ) );
saveCS.getOrgResiBuf( tu.Y() ).copyFrom( csFull->getOrgResiBuf( tu.Y() ) );
}
tmpTU->copyComponentFrom( tu, COMPONENT_Y );
ctxBest = m_CABACEstimator->getCtx();
}
}
}
if( sps.getUseLFNST() && !validReturnFull )

Karsten Suehring
committed
{
csFull->cost = MAX_DOUBLE;

Karsten Suehring
committed
if( bCheckSplit )

Karsten Suehring
committed
{
ctxBest = m_CABACEstimator->getCtx();

Karsten Suehring
committed
}
}
else
{
if( bestModeId[COMPONENT_Y] != lastCheckId )
{
csFull->getPredBuf( tu.Y() ).copyFrom( saveCS.getPredBuf( tu.Y() ) );
csFull->getRecoBuf( tu.Y() ).copyFrom( saveCS.getRecoBuf( tu.Y() ) );

Karsten Suehring
committed
if( keepResi )
{
csFull->getResiBuf ( tu.Y() ).copyFrom( saveCS.getResiBuf ( tu.Y() ) );
csFull->getOrgResiBuf( tu.Y() ).copyFrom( saveCS.getOrgResiBuf( tu.Y() ) );
}

Karsten Suehring
committed
tu.copyComponentFrom( *tmpTU, COMPONENT_Y );
if( !bCheckSplit )
{
m_CABACEstimator->getCtx() = ctxBest;
}
}
else if( bCheckSplit )

Karsten Suehring
committed
{
ctxBest = m_CABACEstimator->getCtx();

Karsten Suehring
committed
}
csFull->cost += dSingleCost;
csFull->dist += uiSingleDistLuma;
csFull->fracBits += singleFracBits;
}

Karsten Suehring
committed
}
bool validReturnSplit = false;

Karsten Suehring
committed
if( bCheckSplit )
{
//----- store full entropy coding status, load original entropy coding status -----
if( bCheckFull )
{
m_CABACEstimator->getCtx() = ctxStart;
}
//----- code splitted block -----
csSplit->cost = 0;
bool uiSplitCbfLuma = false;
bool splitIsSelected = true;

Karsten Suehring
committed
{
partitioner.splitCurrArea( TU_MAX_TR_SPLIT, cs );
}
if( cu.ispMode )
{
partitioner.splitCurrArea( ispType, *csSplit );
}

Karsten Suehring
committed
do
{
bool tmpValidReturnSplit = xRecurIntraCodingLumaQT( *csSplit, partitioner, bestCostSoFar, subTuCounter, ispType, false, mtsCheckRangeFlag, mtsFirstCheckId, mtsLastCheckId );
subTuCounter += subTuCounter != -1 ? 1 : 0;
if( sps.getUseLFNST() && !tmpValidReturnSplit )
{
splitIsSelected = false;
break;
}

Karsten Suehring
committed
if( !cu.ispMode )
{
csSplit->setDecomp( partitioner.currArea().Y() );
}
else if( CU::isISPFirst( cu, partitioner.currArea().Y(), COMPONENT_Y ) )
{
csSplit->setDecomp( cu.Y() );
}

Karsten Suehring
committed
uiSplitCbfLuma |= TU::getCbfAtDepth( *csSplit->getTU( partitioner.currArea().lumaPos(), partitioner.chType, subTuCounter - 1 ), COMPONENT_Y, partitioner.currTrDepth );
if( cu.ispMode )
{
//exit condition if the accumulated cost is already larger than the best cost so far (no impact in RD performance)
if( csSplit->cost > bestCostSoFar )
{
earlySkipISP = true;
splitIsSelected = false;
break;
}
else
{
//more restrictive exit condition
bool tuIsDividedInRows = CU::divideTuInRows( cu );
int nSubPartitions = tuIsDividedInRows ? cu.lheight() >> floorLog2(cu.firstTU->lheight()) : cu.lwidth() >> floorLog2(cu.firstTU->lwidth());
Santiago de Luxán Hernández
committed
double threshold = nSubPartitions == 2 ? 0.95 : subTuCounter == 1 ? 0.83 : 0.91;
if( subTuCounter < nSubPartitions && csSplit->cost > bestCostSoFar*threshold )
{
earlySkipISP = true;
splitIsSelected = false;
break;
}
}
}

Karsten Suehring
committed
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
} while( partitioner.nextPart( *csSplit ) );
partitioner.exitCurrSplit();
if( splitIsSelected )
{
for( auto &ptu : csSplit->tus )
{
if( currArea.Y().contains( ptu->Y() ) )
{
TU::setCbfAtDepth( *ptu, COMPONENT_Y, currDepth, uiSplitCbfLuma ? 1 : 0 );
}
}
//----- restore context states -----
m_CABACEstimator->getCtx() = ctxStart;
//----- determine rate and r-d cost -----
csSplit->fracBits = xGetIntraFracBitsQT( *csSplit, partitioner, true, false, cu.ispMode ? 0 : -1, ispType );

Karsten Suehring
committed
//--- update cost ---
csSplit->cost = m_pcRdCost->calcRdCost(csSplit->fracBits, csSplit->dist);
validReturnSplit = true;

Karsten Suehring
committed
}
}
bool retVal = false;

Karsten Suehring
committed
if( csFull || csSplit )
{
if( !sps.getUseLFNST() || validReturnFull || validReturnSplit )

Karsten Suehring
committed
{
{
// otherwise this would've happened in useSubStructure
cs.picture->getRecoBuf( currArea.Y() ).copyFrom( cs.getRecoBuf( currArea.Y() ) );
cs.picture->getPredBuf( currArea.Y() ).copyFrom( cs.getPredBuf( currArea.Y() ) );
}

Karsten Suehring
committed
if( cu.ispMode && earlySkipISP )
{
cs.cost = MAX_DOUBLE;
}
else
{
cs.cost = m_pcRdCost->calcRdCost( cs.fracBits, cs.dist );
retVal = true;
}

Karsten Suehring
committed
}

Karsten Suehring
committed
}
ChromaCbfs IntraSearch::xRecurIntraChromaCodingQT( CodingStructure &cs, Partitioner& partitioner, const double bestCostSoFar, const PartSplit ispType )

Karsten Suehring
committed
{
UnitArea currArea = partitioner.currArea();
const bool keepResi = cs.sps->getUseLMChroma() || KEEP_PRED_AND_RESI_SIGNALS;

Karsten Suehring
committed
if( !currArea.Cb().valid() ) return ChromaCbfs( false );
TransformUnit &currTU = *cs.getTU( currArea.chromaPos(), CHANNEL_TYPE_CHROMA );
const PredictionUnit &pu = *cs.getPU( currArea.chromaPos(), CHANNEL_TYPE_CHROMA );
bool lumaUsesISP = false;

Karsten Suehring
committed
uint32_t currDepth = partitioner.currTrDepth;
const PPS &pps = *cs.pps;
ChromaCbfs cbfs ( false );
if (currDepth == currTU.depth)
{
if (!currArea.Cb().valid() || !currArea.Cr().valid())
{
return cbfs;
}
CodingStructure &saveCS = *m_pSaveCS[1];
saveCS.pcv = cs.pcv;
saveCS.picture = cs.picture;
saveCS.area.repositionTo( cs.area );
Adam Wieckowski
committed
saveCS.initStructData( MAX_INT, false, true );

Karsten Suehring
committed
if( !currTU.cu->isSepTree() && currTU.cu->ispMode )
{
saveCS.clearCUs();
CodingUnit& auxCU = saveCS.addCU( *currTU.cu, partitioner.chType );
auxCU.ispMode = currTU.cu->ispMode;
saveCS.sps = currTU.cs->sps;
saveCS.clearPUs();
saveCS.addPU( *currTU.cu->firstPU, partitioner.chType );
}

Karsten Suehring
committed
TransformUnit &tmpTU = saveCS.addTU(currArea, partitioner.chType);
cs.setDecomp(currArea.Cb(), true); // set in advance (required for Cb2/Cr2 in 4:2:2 video)
const unsigned numTBlocks = ::getNumberValidTBlocks( *cs.pcv );
CompArea& cbArea = currTU.blocks[COMPONENT_Cb];
CompArea& crArea = currTU.blocks[COMPONENT_Cr];
double bestCostCb = MAX_DOUBLE;
double bestCostCr = MAX_DOUBLE;
Distortion bestDistCb = 0;
Distortion bestDistCr = 0;
int maxModesTested = 0;
bool earlyExitISP = false;
TempCtx ctxStartTU( m_CtxCache );
TempCtx ctxStart ( m_CtxCache );
TempCtx ctxBest ( m_CtxCache );
ctxStartTU = m_CABACEstimator->getCtx();
currTU.jointCbCr = 0;
// Do predictions here to avoid repeating the "default0Save1Load2" stuff
int predMode = PU::getFinalIntraMode( pu, CHANNEL_TYPE_CHROMA );
PelBuf piPredCb = cs.getPredBuf(cbArea);
PelBuf piPredCr = cs.getPredBuf(crArea);
Alexey Filippov
committed
initIntraPatternChType( *currTU.cu, cbArea);
initIntraPatternChType( *currTU.cu, crArea);
if( PU::isLMCMode( predMode ) )
{
xGetLumaRecPixels( pu, cbArea );
predIntraChromaLM( COMPONENT_Cb, piPredCb, pu, cbArea, predMode );
predIntraChromaLM( COMPONENT_Cr, piPredCr, pu, crArea, predMode );
}
else
{
Alexey Filippov
committed
predIntraAng( COMPONENT_Cb, piPredCb, pu);
predIntraAng( COMPONENT_Cr, piPredCr, pu);

Karsten Suehring
committed
// determination of chroma residuals including reshaping and cross-component prediction
//----- get chroma residuals -----
PelBuf resiCb = cs.getResiBuf(cbArea);
PelBuf resiCr = cs.getResiBuf(crArea);
resiCb.copyFrom( cs.getOrgBuf (cbArea) );
resiCr.copyFrom( cs.getOrgBuf (crArea) );
resiCb.subtract( piPredCb );
resiCr.subtract( piPredCr );
//----- get reshape parameter ----
bool doReshaping = ( cs.slice->getLmcsEnabledFlag() && cs.slice->getLmcsChromaResidualScaleFlag()
&& (cs.slice->isIntra() || m_pcReshape->getCTUFlag()) && (cbArea.width * cbArea.height > 4) );
if( doReshaping )
{
const Area area = currTU.Y().valid() ? currTU.Y() : Area(recalcPosition(currTU.chromaFormat, currTU.chType, CHANNEL_TYPE_LUMA, currTU.blocks[currTU.chType].pos()), recalcSize(currTU.chromaFormat, currTU.chType, CHANNEL_TYPE_LUMA, currTU.blocks[currTU.chType].size()));
const CompArea &areaY = CompArea(COMPONENT_Y, currTU.chromaFormat, area);
int adj = m_pcReshape->calculateChromaAdjVpduNei(currTU, areaY);
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
currTU.setChromaAdj(adj);
}
//----- get cross component prediction parameters -----
bool checkCrossComponentPrediction = PU::isChromaIntraModeCrossCheckMode( pu ) && pps.getPpsRangeExtension().getCrossComponentPredictionEnabledFlag() && TU::getCbf( currTU, COMPONENT_Y );
int compAlpha[MAX_NUM_COMPONENT] = { 0, 0, 0 };
if( checkCrossComponentPrediction )
{
compAlpha[COMPONENT_Cb] = xCalcCrossComponentPredictionAlpha( currTU, COMPONENT_Cb, m_pcEncCfg->getUseReconBasedCrossCPredictionEstimate() );
compAlpha[COMPONENT_Cr] = xCalcCrossComponentPredictionAlpha( currTU, COMPONENT_Cr, m_pcEncCfg->getUseReconBasedCrossCPredictionEstimate() );
if( compAlpha[COMPONENT_Cb] == 0 && compAlpha[COMPONENT_Cr] == 0 )
{
checkCrossComponentPrediction = false;
}
}
//===== store original residual signals (std and crossCompPred) =====
CompStorage orgResiCb[5], orgResiCr[5]; // 0:std, 1-3:jointCbCr (placeholder at this stage), 4:crossComp
for( int k = 0; k < (checkCrossComponentPrediction?5:1); k+=4 )
{
orgResiCb[k].create( cbArea );
orgResiCr[k].create( crArea );
if( k >= 4 ) {
CrossComponentPrediction::crossComponentPrediction( currTU, COMPONENT_Cb, cs.getResiBuf(currTU.Y()), resiCb, orgResiCb[k], false);
CrossComponentPrediction::crossComponentPrediction( currTU, COMPONENT_Cr, cs.getResiBuf(currTU.Y()), resiCr, orgResiCr[k], false);
} else {
orgResiCb[k].copyFrom( resiCb );
orgResiCr[k].copyFrom( resiCr );
}
if( doReshaping )
{
int cResScaleInv = currTU.getChromaAdj();
orgResiCb[k].scaleSignal( cResScaleInv, 1, currTU.cu->cs->slice->clpRng(COMPONENT_Cb) );
orgResiCr[k].scaleSignal( cResScaleInv, 1, currTU.cu->cs->slice->clpRng(COMPONENT_Cr) );
}
}

Karsten Suehring
committed
for( uint32_t c = COMPONENT_Cb; c < numTBlocks; c++)
{
const ComponentID compID = ComponentID(c);
const CompArea& area = currTU.blocks[compID];
double dSingleCost = MAX_DOUBLE;
int bestModeId = 0;
Distortion singleDistCTmp = 0;
double singleCostTmp = 0;
const int crossCPredictionModesToTest = checkCrossComponentPrediction ? 2 : 1;
#if JVET_P0058_CHROMA_TS
const bool tsAllowed = TU::isTSAllowed(currTU, compID) && (m_pcEncCfg->getUseChromaTS());
uint8_t nNumTransformCands = 1 + (tsAllowed ? 1 : 0); // DCT + TS = 2 tests
std::vector<TrMode> trModes;
trModes.push_back(TrMode(0, true)); // DCT2
if (tsAllowed)
{
trModes.push_back(TrMode(1, true));//TS
}
CHECK(!currTU.Cb().valid(), "Invalid TU");
#endif
#if JVET_P0058_CHROMA_TS
const int totalModesToTest = crossCPredictionModesToTest * nNumTransformCands;
bool cbfDCT2 = true;
#else
const bool isOneMode = false;
maxModesTested = totalModesToTest > maxModesTested ? totalModesToTest : maxModesTested;

Karsten Suehring
committed
int currModeId = 0;
int default0Save1Load2 = 0;
if (!isOneMode)
{
ctxStart = m_CABACEstimator->getCtx();
}
#if JVET_P0058_CHROMA_TS
for (int modeId = 0; modeId < nNumTransformCands; modeId++)
#endif

Karsten Suehring
committed
{
for (int crossCPredictionModeId = 0; crossCPredictionModeId < crossCPredictionModesToTest; crossCPredictionModeId++)
{
resiCb.copyFrom( orgResiCb[4*crossCPredictionModeId] );
resiCr.copyFrom( orgResiCr[4*crossCPredictionModeId] );
currTU.compAlpha [compID] = ( crossCPredictionModeId ? compAlpha[compID] : 0 );

Karsten Suehring
committed
#if JVET_P0058_CHROMA_TS
currTU.mtsIdx[compID] = trModes[modeId].first;
#endif

Karsten Suehring
committed
currModeId++;
const bool isFirstMode = (currModeId == 1);
const bool isLastMode = false; // Always store output to saveCS and tmpTU
#if JVET_P0058_CHROMA_TS
//if DCT2's cbf==0, skip ts search
if (!cbfDCT2 && trModes[modeId].first == MTS_SKIP)
{
break;
}
if (!trModes[modeId].second)
{
continue;
}
#endif

Karsten Suehring
committed
if (!isFirstMode) // if not first mode to be tested
{
m_CABACEstimator->getCtx() = ctxStart;
}
singleDistCTmp = 0;
#if JVET_P0058_CHROMA_TS
if (nNumTransformCands > 1)
{
xIntraCodingTUBlock(currTU, compID, crossCPredictionModeId != 0, singleDistCTmp, default0Save1Load2, nullptr, modeId == 0 ? &trModes : nullptr, true);
}
else
{
xIntraCodingTUBlock(currTU, compID, crossCPredictionModeId != 0, singleDistCTmp, default0Save1Load2);
}
#else

Karsten Suehring
committed
xIntraCodingTUBlock( currTU, compID, crossCPredictionModeId != 0, singleDistCTmp, default0Save1Load2 );

Karsten Suehring
committed
#if JVET_P0058_CHROMA_TS
if (((crossCPredictionModeId == 1) && (currTU.compAlpha[compID] == 0)) || ((currTU.mtsIdx[compID] == MTS_SKIP) && !TU::getCbf(currTU, compID))) //In order not to code TS flag when cbf is zero, the case for TS with cbf being zero is forbidden.
#else
if( ( ( crossCPredictionModeId == 1 ) && ( currTU.compAlpha[compID] == 0 ) ) ) //In order not to code TS flag when cbf is zero, the case for TS with cbf being zero is forbidden.

Karsten Suehring
committed
{
singleCostTmp = MAX_DOUBLE;
}
else if( lumaUsesISP && bestCostSoFar != MAX_DOUBLE && c == COMPONENT_Cb )
{
uint64_t fracBitsTmp = xGetIntraFracBitsQTSingleChromaComponent( cs, partitioner, ComponentID( c ) );
singleCostTmp = m_pcRdCost->calcRdCost( fracBitsTmp, singleDistCTmp );
if( isOneMode || ( !isOneMode && !isLastMode ) )
{
m_CABACEstimator->getCtx() = ctxStart;
}
}

Karsten Suehring
committed
else if( !isOneMode )
{
uint64_t fracBitsTmp = xGetIntraFracBitsQTChroma( currTU, compID );
singleCostTmp = m_pcRdCost->calcRdCost( fracBitsTmp, singleDistCTmp );
}
if( singleCostTmp < dSingleCost )
{
dSingleCost = singleCostTmp;
bestModeId = currModeId;
if ( c == COMPONENT_Cb )
{
bestCostCb = singleCostTmp;
bestDistCb = singleDistCTmp;
}
else
{
bestCostCr = singleCostTmp;
bestDistCr = singleDistCTmp;
}
#if JVET_P0058_CHROMA_TS
if (currTU.mtsIdx[compID] == MTS_DCT2_DCT2)
{
cbfDCT2 = TU::getCbfAtDepth(currTU, compID, currDepth);
}
#endif

Karsten Suehring
committed
if( !isLastMode )
{
#if KEEP_PRED_AND_RESI_SIGNALS
saveCS.getPredBuf (area).copyFrom(cs.getPredBuf (area));
saveCS.getOrgResiBuf(area).copyFrom(cs.getOrgResiBuf(area));
#endif
saveCS.getPredBuf (area).copyFrom(cs.getPredBuf (area));

Karsten Suehring
committed
if( keepResi )
{
saveCS.getResiBuf (area).copyFrom(cs.getResiBuf (area));
}
saveCS.getRecoBuf (area).copyFrom(cs.getRecoBuf (area));
tmpTU.copyComponentFrom(currTU, compID);
ctxBest = m_CABACEstimator->getCtx();
}
}
}
}
if( lumaUsesISP && dSingleCost > bestCostSoFar && c == COMPONENT_Cb )
{
//Luma + Cb cost is already larger than the best cost, so we don't need to test Cr
cs.dist = MAX_UINT;
m_CABACEstimator->getCtx() = ctxStart;
break;
//return cbfs;
}
// Done with one component of separate coding of Cr and Cb, just switch to the best Cb contexts if Cr coding is still to be done
if ( c == COMPONENT_Cb && bestModeId < totalModesToTest)
{
m_CABACEstimator->getCtx() = ctxBest;
currTU.copyComponentFrom(tmpTU, COMPONENT_Cb); // Cbf of Cb is needed to estimate cost for Cr Cbf
}
}
if ( !earlyExitISP )
{
// Test using joint chroma residual coding
double bestCostCbCr = bestCostCb + bestCostCr;
Distortion bestDistCbCr = bestDistCb + bestDistCr;
int bestJointCbCr = 0;
bool lastIsBest = false;
std::vector<int> jointCbfMasksToTest;
if ( cs.sps->getJointCbCrEnabledFlag() && (TU::getCbf(tmpTU, COMPONENT_Cb) || TU::getCbf(tmpTU, COMPONENT_Cr)))
jointCbfMasksToTest = m_pcTrQuant->selectICTCandidates(currTU, orgResiCb, orgResiCr);
}
for( int cbfMask : jointCbfMasksToTest )
currTU.jointCbCr = (uint8_t)cbfMask;
currTU.compAlpha[COMPONENT_Cb] = 0;
currTU.compAlpha[COMPONENT_Cr] = 0;
#if JVET_P0058_CHROMA_TS
// encoder bugfix: initialize mtsIdx for chroma under JointCbCrMode.
currTU.mtsIdx[COMPONENT_Cb] = currTU.mtsIdx[COMPONENT_Cr] = MTS_DCT2_DCT2;
#endif
m_CABACEstimator->getCtx() = ctxStartTU;
resiCb.copyFrom( orgResiCb[cbfMask] );
resiCr.copyFrom( orgResiCr[cbfMask] );
xIntraCodingTUBlock( currTU, COMPONENT_Cb, false, distTmp, 0 );
double costTmp = std::numeric_limits<double>::max();
if( distTmp < std::numeric_limits<Distortion>::max() )
{
uint64_t bits = xGetIntraFracBitsQTChroma( currTU, COMPONENT_Cb );
costTmp = m_pcRdCost->calcRdCost( bits, distTmp );
}
if( costTmp < bestCostCbCr )
{
bestCostCbCr = costTmp;
bestDistCbCr = distTmp;
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
bestJointCbCr = currTU.jointCbCr;
// store data
if( cbfMask != jointCbfMasksToTest.back() )
{
#if KEEP_PRED_AND_RESI_SIGNALS
saveCS.getOrgResiBuf(cbArea).copyFrom(cs.getOrgResiBuf(cbArea));
saveCS.getOrgResiBuf(crArea).copyFrom(cs.getOrgResiBuf(crArea));
#endif
saveCS.getPredBuf (cbArea).copyFrom(cs.getPredBuf (cbArea));
saveCS.getPredBuf (crArea).copyFrom(cs.getPredBuf (crArea));
if( keepResi )
{
saveCS.getResiBuf (cbArea).copyFrom(cs.getResiBuf (cbArea));
saveCS.getResiBuf (crArea).copyFrom(cs.getResiBuf (crArea));
}
saveCS.getRecoBuf (cbArea).copyFrom(cs.getRecoBuf (cbArea));
saveCS.getRecoBuf (crArea).copyFrom(cs.getRecoBuf (crArea));
tmpTU.copyComponentFrom(currTU, COMPONENT_Cb);
tmpTU.copyComponentFrom(currTU, COMPONENT_Cr);
ctxBest = m_CABACEstimator->getCtx();
}
else
{
lastIsBest = true;
}
// Retrieve the best CU data (unless it was the very last one tested)
if ( !( maxModesTested == 1 && jointCbfMasksToTest.empty() ) && !lastIsBest )
{
#if KEEP_PRED_AND_RESI_SIGNALS
cs.getPredBuf (cbArea).copyFrom(saveCS.getPredBuf (cbArea));
cs.getOrgResiBuf(cbArea).copyFrom(saveCS.getOrgResiBuf(cbArea));
cs.getPredBuf (crArea).copyFrom(saveCS.getPredBuf (crArea));
cs.getOrgResiBuf(crArea).copyFrom(saveCS.getOrgResiBuf(crArea));
#endif
cs.getPredBuf (cbArea).copyFrom(saveCS.getPredBuf (cbArea));
cs.getPredBuf (crArea).copyFrom(saveCS.getPredBuf (crArea));
if( keepResi )
{
cs.getResiBuf (cbArea).copyFrom(saveCS.getResiBuf (cbArea));
cs.getResiBuf (crArea).copyFrom(saveCS.getResiBuf (crArea));
}
cs.getRecoBuf (cbArea).copyFrom(saveCS.getRecoBuf (cbArea));
cs.getRecoBuf (crArea).copyFrom(saveCS.getRecoBuf (crArea));
currTU.copyComponentFrom(tmpTU, COMPONENT_Cb);
currTU.copyComponentFrom(tmpTU, COMPONENT_Cr);
m_CABACEstimator->getCtx() = ctxBest;
}
// Copy results to the picture structures
cs.picture->getRecoBuf(cbArea).copyFrom(cs.getRecoBuf(cbArea));
cs.picture->getRecoBuf(crArea).copyFrom(cs.getRecoBuf(crArea));
cs.picture->getPredBuf(cbArea).copyFrom(cs.getPredBuf(cbArea));
cs.picture->getPredBuf(crArea).copyFrom(cs.getPredBuf(crArea));
cbfs.cbf(COMPONENT_Cb) = TU::getCbf(currTU, COMPONENT_Cb);
cbfs.cbf(COMPONENT_Cr) = TU::getCbf(currTU, COMPONENT_Cr);
currTU.jointCbCr = ( (cbfs.cbf(COMPONENT_Cb) + cbfs.cbf(COMPONENT_Cr)) ? bestJointCbCr : 0 );

Karsten Suehring
committed
}
}
else
{
unsigned numValidTBlocks = ::getNumberValidTBlocks( *cs.pcv );
ChromaCbfs SplitCbfs ( false );
if( partitioner.canSplit( TU_MAX_TR_SPLIT, cs ) )
{
partitioner.splitCurrArea( TU_MAX_TR_SPLIT, cs );
}
else if( currTU.cu->ispMode )
{
partitioner.splitCurrArea( ispType, cs );
}

Karsten Suehring
committed
else
THROW( "Implicit TU split not available" );
do
{
ChromaCbfs subCbfs = xRecurIntraChromaCodingQT( cs, partitioner, bestCostSoFar, ispType );

Karsten Suehring
committed
for( uint32_t ch = COMPONENT_Cb; ch < numValidTBlocks; ch++ )
{
const ComponentID compID = ComponentID( ch );
SplitCbfs.cbf( compID ) |= subCbfs.cbf( compID );
}
} while( partitioner.nextPart( cs ) );
partitioner.exitCurrSplit();
if( lumaUsesISP && cs.dist == MAX_UINT )
{
return cbfs;
}

Karsten Suehring
committed
{
cbfs.Cb |= SplitCbfs.Cb;
cbfs.Cr |= SplitCbfs.Cr;
if( !lumaUsesISP )
{
for( auto &ptu : cs.tus )
{
if( currArea.Cb().contains( ptu->Cb() ) || ( !ptu->Cb().valid() && currArea.Y().contains( ptu->Y() ) ) )
{
TU::setCbfAtDepth( *ptu, COMPONENT_Cb, currDepth, SplitCbfs.Cb );
TU::setCbfAtDepth( *ptu, COMPONENT_Cr, currDepth, SplitCbfs.Cr );
}
}
}

Karsten Suehring
committed
}
}
return cbfs;
}
uint64_t IntraSearch::xFracModeBitsIntra(PredictionUnit &pu, const uint32_t &uiMode, const ChannelType &chType)
{
uint32_t orgMode = uiMode;

Karsten Suehring
committed
std::swap(orgMode, pu.intraDir[chType]);
m_CABACEstimator->resetBits();
if( isLuma( chType ) )
{
{
m_CABACEstimator->intra_luma_pred_mode(pu);
}

Karsten Suehring
committed
}
else
{
m_CABACEstimator->intra_chroma_pred_mode( pu );
}

Karsten Suehring
committed
std::swap(orgMode, pu.intraDir[chType]);
return m_CABACEstimator->getEstFracBits();
}
void IntraSearch::encPredIntraDPCM( const ComponentID &compID, PelBuf &pOrg, PelBuf &pDst, const uint32_t &uiDirMode )
{
CHECK( pOrg.buf == 0, "Encoder DPCM called without original buffer" );
const int srcStride = m_refBufferStride[compID];

Karsten Suehring
committed
CPelBuf pSrc = CPelBuf(getPredictorPtr(compID), srcStride, m_leftRefLength + 1);
// Sample Adaptive intra-Prediction (SAP)
if( uiDirMode == HOR_IDX )
{
// left column filled with reference samples, remaining columns filled with pOrg data
for( int y = 0; y < pDst.height; y++ )
{
pDst.at(0, y) = pSrc.at(1 + y, 1);

Karsten Suehring
committed
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
}
CPelBuf orgRest = pOrg.subBuf( 0, 0, pOrg.width - 1, pOrg.height );
PelBuf predRest = pDst.subBuf( 1, 0, pDst.width - 1, pDst.height );
predRest.copyFrom( orgRest );
}
else // VER_IDX
{
// top row filled with reference samples, remaining rows filled with pOrg data
for( int x = 0; x < pDst.width; x++ )
{
pDst.at( x, 0 ) = pSrc.at( 1 + x, 0 );
}
CPelBuf orgRest = pOrg.subBuf( 0, 0, pOrg.width, pOrg.height - 1 );
PelBuf predRest = pDst.subBuf( 0, 1, pDst.width, pDst.height - 1 );
predRest.copyFrom( orgRest );
}
}
bool IntraSearch::useDPCMForFirstPassIntraEstimation( const PredictionUnit &pu, const uint32_t &uiDirMode )
{
return CU::isRDPCMEnabled( *pu.cu ) && pu.cu->transQuantBypass && (uiDirMode == HOR_IDX || uiDirMode == VER_IDX);
}
template<typename T, size_t N>
void IntraSearch::reduceHadCandList(static_vector<T, N>& candModeList, static_vector<double, N>& candCostList, int& numModesForFullRD, const double thresholdHadCost, const double* mipHadCost, const PredictionUnit &pu, const bool fastMip)
{
const int maxCandPerType = numModesForFullRD >> 1;
static_vector<ModeInfo, FAST_UDI_MAX_RDMODE_NUM> tempRdModeList;
static_vector<double, FAST_UDI_MAX_RDMODE_NUM> tempCandCostList;
const double minCost = candCostList[0];
bool keepOneMip = candModeList.size() > numModesForFullRD;

Christian Helmrich
committed
int numConv = 0;
int numMip = 0;
for (int idx = 0; idx < candModeList.size() - (keepOneMip?0:1); idx++)
{
bool addMode = false;
addMode = (numConv < 3);
numConv += addMode ? 1:0;
addMode = ( numMip < maxCandPerType || (candCostList[idx] < thresholdHadCost * minCost) || keepOneMip );
keepOneMip = false;
numMip += addMode ? 1:0;
}
if( addMode )
{
tempCandCostList.push_back(candCostList[idx]);
}
}
if ((pu.lwidth() > 8 && pu.lheight() > 8))
{
// Sort MIP candidates by Hadamard cost
const int transpOff = getNumModesMip(pu.Y()) / 2;
static_vector<uint8_t, FAST_UDI_MAX_RDMODE_NUM> sortedMipModes(0);
static_vector<double, FAST_UDI_MAX_RDMODE_NUM> sortedMipCost(0);
for (uint8_t mode : { 3, 4, 5 })
{
uint8_t candMode = mode + uint8_t((mipHadCost[mode + transpOff] < mipHadCost[mode]) ? transpOff : 0);
updateCandList(candMode, mipHadCost[candMode], sortedMipModes, sortedMipCost, 3);
}
// Append MIP mode to RD mode list
const int modeListSize = int(tempRdModeList.size());
for (int idx = 0; idx < 3; idx++)
{
const ModeInfo mipMode(true, 0, NOT_INTRA_SUBPARTITIONS, sortedMipModes[idx]);
bool alreadyIncluded = false;
for (int modeListIdx = 0; modeListIdx < modeListSize; modeListIdx++)
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
{
if (tempRdModeList[modeListIdx] == mipMode)
{
alreadyIncluded = true;
break;
}
}
if (!alreadyIncluded)
{
tempRdModeList.push_back(mipMode);
tempCandCostList.push_back(0);
if( fastMip ) break;
}
}
}
candModeList = tempRdModeList;
candCostList = tempCandCostList;
numModesForFullRD = int(candModeList.size());
}
// It decides which modes from the ISP lists can be full RD tested
void IntraSearch::xGetNextISPMode(ModeInfo& modeInfo, const ModeInfo* lastMode, const Size cuSize)
{
static_vector<ModeInfo, FAST_UDI_MAX_RDMODE_NUM>* rdModeLists[2] = { &m_ispCandListHor, &m_ispCandListVer };
ISPType nextISPcandSplitType;
auto& ispTestedModes = m_ispTestedModes;
const bool horSplitIsTerminated = ispTestedModes.splitIsFinished[HOR_INTRA_SUBPARTITIONS - 1];
const bool verSplitIsTerminated = ispTestedModes.splitIsFinished[VER_INTRA_SUBPARTITIONS - 1];
if (!horSplitIsTerminated && !verSplitIsTerminated)
{
nextISPcandSplitType = !lastMode ? HOR_INTRA_SUBPARTITIONS : lastMode->ispMod == HOR_INTRA_SUBPARTITIONS ? VER_INTRA_SUBPARTITIONS : HOR_INTRA_SUBPARTITIONS;
}
else if (!horSplitIsTerminated && verSplitIsTerminated)
{
nextISPcandSplitType = HOR_INTRA_SUBPARTITIONS;
}
else if (horSplitIsTerminated && !verSplitIsTerminated)
{
nextISPcandSplitType = VER_INTRA_SUBPARTITIONS;
}
else
{
return; // no more modes will be tested
}
int maxNumSubPartitions = ispTestedModes.numTotalParts[nextISPcandSplitType - 1];
if (ispTestedModes.numTestedModes[nextISPcandSplitType - 1] >= 2)
{
// Split stop criteria after checking the performance of previously tested intra modes
const int thresholdSplit1 = maxNumSubPartitions;
bool stopThisSplit = false;
int mode1 = ispTestedModes.getTestedIntraMode((ISPType)nextISPcandSplitType, 0);
mode1 = mode1 == DC_IDX ? -1 : mode1;
int numSubPartsBestMode1 = mode1 != -1 ? ispTestedModes.getNumCompletedSubParts((ISPType)nextISPcandSplitType, mode1) : -1;
int mode2 = ispTestedModes.getTestedIntraMode((ISPType)nextISPcandSplitType, 1);
mode2 = mode2 == DC_IDX ? -1 : mode2;
int numSubPartsBestMode2 = mode2 != -1 ? ispTestedModes.getNumCompletedSubParts((ISPType)nextISPcandSplitType, mode2) : -1;
// 1) The 2 most promising modes do not reach a certain number of sub-partitions
if (numSubPartsBestMode1 != -1 && numSubPartsBestMode2 != -1)
{
if (numSubPartsBestMode1 < thresholdSplit1 && numSubPartsBestMode2 < thresholdSplit1)
{
stopThisSplit = true;
if (!stopThisSplit)
// 2) One split type may be discarded by comparing the number of sub-partitions of the best angle modes of both splits
ISPType otherSplit = nextISPcandSplitType == HOR_INTRA_SUBPARTITIONS ? VER_INTRA_SUBPARTITIONS : HOR_INTRA_SUBPARTITIONS;
int numSubPartsBestMode2OtherSplit = mode2 != -1 ? ispTestedModes.getNumCompletedSubParts(otherSplit, mode2) : -1;
if (numSubPartsBestMode2OtherSplit != -1 && numSubPartsBestMode2 != -1)
if (numSubPartsBestMode2OtherSplit > numSubPartsBestMode2)
{
stopThisSplit = true;
}
else if (numSubPartsBestMode2OtherSplit == numSubPartsBestMode2 && numSubPartsBestMode2OtherSplit == maxNumSubPartitions)
{
double rdCostBestMode2ThisSplit = ispTestedModes.getRDCost(nextISPcandSplitType, mode2);
double rdCostBestMode2OtherSplit = ispTestedModes.getRDCost(otherSplit, mode2);
double threshold = 1.3;
if (rdCostBestMode2ThisSplit == MAX_DOUBLE || rdCostBestMode2OtherSplit < rdCostBestMode2ThisSplit * threshold)
{
stopThisSplit = true;
}
}
}
}
if (stopThisSplit)
{
ispTestedModes.splitIsFinished[nextISPcandSplitType - 1] = true;
return;
}
}
// Now a new mode is retrieved from the list and it has to be decided whether it should be tested or not
if (ispTestedModes.candIndexInList[nextISPcandSplitType - 1] < rdModeLists[nextISPcandSplitType - 1]->size())
ModeInfo candidate = rdModeLists[nextISPcandSplitType - 1]->at(ispTestedModes.candIndexInList[nextISPcandSplitType - 1]);
ispTestedModes.candIndexInList[nextISPcandSplitType - 1]++;
// extra modes are only tested if ISP has won so far
if (ispTestedModes.candIndexInList[nextISPcandSplitType - 1] > ispTestedModes.numOrigModesToTest)
if (ispTestedModes.bestSplitSoFar != candidate.ispMod || ispTestedModes.bestModeSoFar == PLANAR_IDX)
{
return;
}
}
bool testCandidate = true;
// we look for a reference mode that has already been tested within the window and decide to test the new one according to the reference mode costs
if (candidate.modeId >= DC_IDX && maxNumSubPartitions > 2 && ispTestedModes.numTestedModes[nextISPcandSplitType - 1] >= 2)
{
const int angWindowSize = 5;
int numSubPartsLeftMode, numSubPartsRightMode, numSubPartsRefMode, leftIntraMode = -1, rightIntraMode = -1;
int windowSize = candidate.modeId > DC_IDX ? angWindowSize : 1;
int numSamples = cuSize.width << floorLog2(cuSize.height);
int numSubPartsLimit = numSamples >= 256 ? maxNumSubPartitions - 1 : 2;
xFindAlreadyTestedNearbyIntraModes((int)candidate.modeId, &leftIntraMode, &rightIntraMode, (ISPType)candidate.ispMod, windowSize);
numSubPartsLeftMode = leftIntraMode != -1 ? ispTestedModes.getNumCompletedSubParts((ISPType)candidate.ispMod, leftIntraMode) : -1;
numSubPartsRightMode = rightIntraMode != -1 ? ispTestedModes.getNumCompletedSubParts((ISPType)candidate.ispMod, rightIntraMode) : -1;
numSubPartsRefMode = std::max(numSubPartsLeftMode, numSubPartsRightMode);
if (numSubPartsRefMode > 0)
{