Skip to content
Snippets Groups Projects
EncCu.cpp 136 KiB
Newer Older
  • Learn to ignore specific revisions
  •         }
            else if ( m_pcEncCfg->getMotionEstimationSearchMethod() != MESEARCH_SELECTIVE )
            {
              int absolute_MV = 0;
    
              for ( uint32_t uiRefListIdx = 0; uiRefListIdx < 2; uiRefListIdx++ )
              {
                if ( slice.getNumRefIdx( RefPicList( uiRefListIdx ) ) > 0 )
                {
                  absolute_MV += bestPU.mvd[uiRefListIdx].getAbsHor() + bestPU.mvd[uiRefListIdx].getAbsVer();
                }
              }
    
              if ( absolute_MV == 0 )
              {
                m_modeCtrl->setEarlySkipDetected();
              }
            }
          }
        }
      }
    #else
    
      MvField       affineMvField[2][3];
      unsigned char interDirNeighbours;
      int           numValidMergeCand;
      bool          hasNoResidual = false;
    
    #if JVET_L0646_GBI
      uint8_t       gbiIdx = GBI_DEFAULT;
    #endif
    
    
    
      tempCS->initStructData( encTestMode.qp, encTestMode.lossless );
    
      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;
    
    #if JVET_L0054_MMVD
      cu.mmvdSkip = false;
    #endif
    
      cu.partSize         = encTestMode.partSize;
      cu.affine           = true;
      cu.predMode         = MODE_INTER;
      cu.transQuantBypass = encTestMode.lossless;
      cu.chromaQpAdj      = cu.transQuantBypass ? 0 : m_cuChromaQpOffsetIdxPlus1;
      cu.qp               = encTestMode.qp;
    
      CU::addPUs( cu );
    
      cu.firstPU->mergeFlag = true;
      cu.firstPU->mergeIdx  = 0;
    
    #if JVET_L0646_GBI
      PU::getAffineMergeCand( *cu.firstPU, affineMvField, interDirNeighbours, gbiIdx, numValidMergeCand );
    #else
    
      PU::getAffineMergeCand( *cu.firstPU, affineMvField, interDirNeighbours, numValidMergeCand );
    
      if( numValidMergeCand == -1 )
      {
        return;
      }
    
      cu.firstPU->interDir = interDirNeighbours;
      PU::setAllAffineMvField( *cu.firstPU, affineMvField[REF_PIC_LIST_0], REF_PIC_LIST_0 );
      PU::setAllAffineMvField( *cu.firstPU, affineMvField[REF_PIC_LIST_1], REF_PIC_LIST_1 );
    
    #if JVET_L0646_GBI
      cu.GBiIdx = gbiIdx;
    #endif
    
    
      PU::spanMotionInfo( *cu.firstPU );
    
      m_pcInterSearch->motionCompensation( cu );
    
    
      xEncodeInterResidual(tempCS, bestCS, partitioner, encTestMode, 0
        , NULL
        , 1
        , &hasNoResidual);
    
    
      if( ! (encTestMode.lossless || hasNoResidual) )
      {
        tempCS->initStructData( encTestMode.qp, encTestMode.lossless );
        tempCS->copyStructure( *bestCS, partitioner.chType );
        tempCS->getPredBuf().copyFrom( bestCS->getPredBuf() );
    
    
        xEncodeInterResidual(tempCS, bestCS, partitioner, encTestMode, 1
          , NULL
          , 1
          , &hasNoResidual);
    
    Xiaozhong Xu's avatar
    Xiaozhong Xu committed
    3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 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 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560
    #if JVET_L0293_CPR
    //////////////////////////////////////////////////////////////////////////////////////////////
    // cpr merge/skip mode check
    void EncCu::xCheckRDCostCPRModeMerge2Nx2N(CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode)
    {
      assert(tempCS->chType != CHANNEL_TYPE_CHROMA); // chroma CPR is derived
    
      if (tempCS->area.lwidth() > CPR_MAX_CAND_SIZE || tempCS->area.lheight() > CPR_MAX_CAND_SIZE) // currently only check 32x32 and below block for cpr merge/skip
      {
        return;
      }
      const SPS &sps = *tempCS->sps;
    
      tempCS->initStructData(encTestMode.qp, encTestMode.lossless);
      MergeCtx mergeCtx;
    
    
      if (sps.getSpsNext().getUseSubPuMvp())
      {
        Size bufSize = g_miScaling.scale(tempCS->area.lumaSize());
        mergeCtx.subPuMvpMiBuf = MotionBuf(m_SubPuMiBuf, bufSize);
      }
    
      {
        // first get merge candidates
        CodingUnit cu(tempCS->area);
        cu.cs = tempCS;
        cu.predMode = MODE_INTER;
        cu.cpr = true;
        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;
    #if JVET_L0054_MMVD
        cu.mmvdSkip = false;
        pu.mmvdMergeFlag = false;
    #endif
    #if JVET_L0124_L0208_TRIANGLE
        cu.triangle = false;
    #endif
        PU::getInterMergeCandidates(pu, mergeCtx
    #if JVET_L0054_MMVD
          , 0
    #endif
        );
      }
    
      int candHasNoResidual[MRG_MAX_NUM_CANDS];
      for (unsigned int ui = 0; ui < mergeCtx.numValidMergeCand; ui++)
      {
        candHasNoResidual[ui] = 0;
      }
    
      bool                                        bestIsSkip = false;
      unsigned                                    numMrgSATDCand = mergeCtx.numValidMergeCand;
      static_vector<unsigned, MRG_MAX_NUM_CANDS>  RdModeList(MRG_MAX_NUM_CANDS);
      for (unsigned i = 0; i < MRG_MAX_NUM_CANDS; i++)
      {
        RdModeList[i] = i;
      }
    
      //{
        static_vector<double, MRG_MAX_NUM_CANDS>  candCostList(MRG_MAX_NUM_CANDS, MAX_DOUBLE);
        // 1. Pass: get SATD-cost for selected candidates and reduce their count
        {
          const double sqrtLambdaForFirstPass = m_pcRdCost->getMotionLambda(encTestMode.lossless);
    
          CodingUnit &cu = tempCS->addCU(CS::getArea(*tempCS, tempCS->area, (const ChannelType)partitioner.chType), (const ChannelType)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.cpr = true;
          cu.transQuantBypass = encTestMode.lossless;
          cu.chromaQpAdj = cu.transQuantBypass ? 0 : m_cuChromaQpOffsetIdxPlus1;
          cu.qp = encTestMode.qp;
    #if JVET_L0054_MMVD
          cu.mmvdSkip = false;
    #endif
    #if JVET_L0124_L0208_TRIANGLE
          cu.triangle = false;
    #endif
          DistParam distParam;
          const bool bUseHadamard = !encTestMode.lossless;
          PredictionUnit &pu = tempCS->addPU(cu, partitioner.chType); //tempCS->addPU(cu);
    #if JVET_L0054_MMVD
          pu.mmvdMergeFlag = false;
    #endif
          Picture* refPic = pu.cu->slice->getPic();
          const CPelBuf refBuf = refPic->getRecoBuf(pu.blocks[COMPONENT_Y]);
          const Pel*        piRefSrch = refBuf.buf;
          m_pcRdCost->setDistParam(distParam, tempCS->getOrgBuf().Y(), refBuf, sps.getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y, bUseHadamard);
          int refStride = refBuf.stride;
          const UnitArea localUnitArea(tempCS->area.chromaFormat, Area(0, 0, tempCS->area.Y().width, tempCS->area.Y().height));
          int numValidBv = mergeCtx.numValidMergeCand;
          for (unsigned int mergeCand = 0; mergeCand < mergeCtx.numValidMergeCand; mergeCand++)
          {
            if (mergeCtx.interDirNeighbours[mergeCand] != 1)
            {
              numValidBv--;
              continue;
            }
            if (tempCS->slice->getRefPic(REF_PIC_LIST_0, mergeCtx.mvFieldNeighbours[mergeCand << 1].refIdx)->getPOC() != tempCS->slice->getPOC())
            {
              numValidBv--;
              continue;
            }
            mergeCtx.setMergeInfo(pu, mergeCand); // set bv info in merge mode
            const int cuPelX = pu.Y().x;
            const int cuPelY = pu.Y().y;
            int roiWidth = pu.lwidth();
            int roiHeight = pu.lheight();
            const int picWidth = pu.cs->slice->getSPS()->getPicWidthInLumaSamples();
            const int picHeight = pu.cs->slice->getSPS()->getPicHeightInLumaSamples();
            const unsigned int  lcuWidth = pu.cs->slice->getSPS()->getMaxCUWidth();
            int xPred = pu.bv.getHor();
            int yPred = pu.bv.getVer();
    
            if (!PU::isBlockVectorValid(pu, cuPelX, cuPelY, roiWidth, roiHeight, picWidth, picHeight, 0, 0, xPred, yPred, lcuWidth)) // not valid bv derived
            {
              numValidBv--;
              continue;
            }
            PU::spanMotionInfo(pu, mergeCtx);
    
            distParam.cur.buf = piRefSrch + refStride * yPred + xPred;
    
            Distortion sad = distParam.distFunc(distParam);
            unsigned int bitsCand = mergeCand + 1;
            if (mergeCand == tempCS->slice->getMaxNumMergeCand() - 1)
            {
              bitsCand--;
            }
            double cost = (double)sad + (double)bitsCand * sqrtLambdaForFirstPass;
    #if JVET_L0283_MULTI_REF_LINE
            static_vector<int, MRG_MAX_NUM_CANDS> * nullList = nullptr;
    #endif
    
            updateCandList(mergeCand, cost, RdModeList, candCostList
    #if JVET_L0283_MULTI_REF_LINE
              , *nullList, -1
    #endif 
             , numMrgSATDCand);
          }
    
          // Try to limit number of candidates using SATD-costs
          if (numValidBv)
          {
            numMrgSATDCand = numValidBv;
            for (unsigned int i = 1; i < numValidBv; i++)
            {
              if (candCostList[i] > MRG_FAST_RATIO*candCostList[0])
              {
                numMrgSATDCand = i;
                break;
              }
            }
          }
          else
          {
            tempCS->dist = 0;
            tempCS->fracBits = 0;
            tempCS->cost = MAX_DOUBLE;
            tempCS->initStructData(encTestMode.qp, encTestMode.lossless);
            return;
          }
    
          tempCS->initStructData(encTestMode.qp, encTestMode.lossless);
        }
      //}
    
    
      const unsigned int iteration = encTestMode.lossless ? 1 : 2;
    
      // 2. Pass: check candidates using full RD test
      for (unsigned int numResidualPass = 0; numResidualPass < iteration; numResidualPass++)
      {
        for (unsigned int mrgHADIdx = 0; mrgHADIdx < numMrgSATDCand; mrgHADIdx++)
        {
          unsigned int mergeCand = RdModeList[mrgHADIdx];
          if (mergeCtx.interDirNeighbours[mergeCand] != 1)
          {
            continue;
          }
          if (tempCS->slice->getRefPic(REF_PIC_LIST_0, mergeCtx.mvFieldNeighbours[mergeCand << 1].refIdx)->getPOC() != tempCS->slice->getPOC())
          {
            continue;
          }
          if (!(numResidualPass == 1 && candHasNoResidual[mergeCand] == 1))
          {
            if (!(bestIsSkip && (numResidualPass == 0)))
            {
              unsigned char considerEmtSecondPass = 0;
              bool skipSecondEmtPass = true;
              bool hasResidual[2] = { false, false };
              double emtCost[2] = { MAX_DOUBLE, MAX_DOUBLE };
    
              // CU-level optimization
              for (unsigned char emtCuFlag = 0; emtCuFlag <= considerEmtSecondPass; emtCuFlag++)
              {
                if (m_pcEncCfg->getFastInterEMT() && emtCuFlag && skipSecondEmtPass)
                {
                  continue;
                }
    
                // first get merge candidates
                CodingUnit &cu = tempCS->addCU(CS::getArea(*tempCS, tempCS->area, (const ChannelType)partitioner.chType), (const ChannelType)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.cpr = true;
                cu.transQuantBypass = encTestMode.lossless;
                cu.chromaQpAdj = cu.transQuantBypass ? 0 : m_cuChromaQpOffsetIdxPlus1;
                cu.qp = encTestMode.qp;
    
                cu.emtFlag = false;
    
                PredictionUnit &pu = tempCS->addPU(cu, partitioner.chType);// tempCS->addPU(cu);
                pu.intraDir[0] = DC_IDX; // set intra pred for cpr block
                pu.intraDir[1] = PLANAR_IDX; // set intra pred for cpr block
    #if JVET_L0054_MMVD
                cu.mmvdSkip = false;
                pu.mmvdMergeFlag = false;
    #endif
    #if JVET_L0124_L0208_TRIANGLE
                cu.triangle = false;
    #endif
                mergeCtx.setMergeInfo(pu, mergeCand);
                PU::spanMotionInfo(pu, mergeCtx);
    
                assert(mergeCtx.mrgTypeNeighbours[mergeCand] == MRG_TYPE_CPR); //  should be CPR candidate at this round
                const bool chroma = !(CS::isDualITree(*tempCS));
    
                //  MC
                m_pcInterSearch->motionCompensation(pu,REF_PIC_LIST_0, true, chroma);
                m_CABACEstimator->getCtx() = m_CurrCtx->start;
    
                m_pcInterSearch->encodeResAndCalcRdInterCU(*tempCS, partitioner, (numResidualPass != 0), true, chroma);
                xEncodeDontSplit(*tempCS, partitioner);
    
                if (tempCS->pps->getUseDQP() && (partitioner.currDepth) <= tempCS->pps->getMaxCuDQPDepth())
                {
                  xCheckDQP(*tempCS, partitioner);
                }
    
                hasResidual[emtCuFlag] = cu.rootCbf;
                emtCost[emtCuFlag] = tempCS->cost;
    
                DTRACE_MODE_COST(*tempCS, m_pcRdCost->getLambda());
                xCheckBestMode(tempCS, bestCS, partitioner, encTestMode);
    
                tempCS->initStructData(encTestMode.qp, encTestMode.lossless);
              }
              if (numResidualPass == 0 && (emtCost[0] <= emtCost[1] ? !hasResidual[0] : !hasResidual[1]))
    
                {
                  // If no residual when allowing for one, then set mark to not try case where residual is forced to 0
                  candHasNoResidual[mergeCand] = 1;
                }
    
                if (m_pcEncCfg->getUseFastDecisionForMerge() && !bestIsSkip)
                {
                  if (bestCS->getCU(partitioner.chType) == NULL)
                    bestIsSkip = 0;
                  else
                  bestIsSkip = bestCS->getCU(partitioner.chType)->rootCbf == 0;
                }
            }
          }
        }
      }
    
    }
    
    void EncCu::xCheckRDCostCPRMode(CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode)
    {
        tempCS->initStructData(encTestMode.qp, encTestMode.lossless);
    
        CodingUnit &cu = tempCS->addCU(CS::getArea(*tempCS, tempCS->area, partitioner.chType), 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.cpr = true;
        cu.imv = 0;
    
        CU::addPUs(cu);
    
        PredictionUnit& pu = *cu.firstPU;
    #if JVET_L0054_MMVD
        cu.mmvdSkip = false;
        pu.mmvdMergeFlag = false;
    #endif
    
        pu.intraDir[0] = DC_IDX; // set intra pred for cpr block
        pu.intraDir[1] = PLANAR_IDX; // set intra pred for cpr block
    
        pu.interDir = 1; // use list 0 for CPR mode
        pu.refIdx[REF_PIC_LIST_0] = pu.cs->slice->getNumRefIdx(REF_PIC_LIST_0) - 1; // last idx in the list
    
    
        if (partitioner.chType == CHANNEL_TYPE_LUMA)
        {
          bool bValid = m_pcInterSearch->predCPRSearch(cu, partitioner, m_ctuCprSearchRangeX, m_ctuCprSearchRangeY, m_cprHashMap);
    
          if (bValid)
          {
            PU::spanMotionInfo(pu);
            const bool chroma = !(CS::isDualITree(*tempCS));
            //  MC
            m_pcInterSearch->motionCompensation(pu, REF_PIC_LIST_0, true, chroma);
    
            double    bestCost = bestCS->cost;
            unsigned char    considerEmtSecondPass = 0;
            bool      skipSecondEmtPass = true;
            double    emtFirstPassCost = MAX_DOUBLE;
    
            // CU-level optimization
    
            for (unsigned char emtCuFlag = 0; emtCuFlag <= considerEmtSecondPass; emtCuFlag++)
            {
              if (m_pcEncCfg->getFastInterEMT() && emtCuFlag && skipSecondEmtPass)
              {
                continue;
              }
    
              tempCS->getCU(tempCS->chType)->emtFlag = emtCuFlag;
    
              m_pcInterSearch->encodeResAndCalcRdInterCU(*tempCS, partitioner, false, true, chroma);
    
              if (m_pcEncCfg->getFastInterEMT())
              {
                emtFirstPassCost = (!emtCuFlag) ? tempCS->cost : emtFirstPassCost;
              }
              xEncodeDontSplit(*tempCS, partitioner);
    
              if (tempCS->pps->getUseDQP() && (partitioner.currDepth) <= tempCS->pps->getMaxCuDQPDepth())
              {
                xCheckDQP(*tempCS, partitioner);
              }
    
              DTRACE_MODE_COST(*tempCS, m_pcRdCost->getLambda());
              xCheckBestMode(tempCS, bestCS, partitioner, encTestMode);
    
              //now we check whether the second pass should be skipped or not
              if (!emtCuFlag && considerEmtSecondPass)
              {
                static const double thresholdToSkipEmtSecondPass = 1.1; // Skip checking EMT transforms
                if (m_pcEncCfg->getFastInterEMT() && (!cu.firstTU->cbf[COMPONENT_Y] || emtFirstPassCost > bestCost * thresholdToSkipEmtSecondPass))
                {
                  skipSecondEmtPass = true;
                }
                else //EMT will be checked
                {
                  if (bestCost == bestCS->cost) //The first EMT pass didn't become the bestCS, so we clear the TUs generated
                  {
                    tempCS->clearTUs();
                  }
                  else
                  {
                    tempCS->initStructData(bestCS->currQP[bestCS->chType], bestCS->isLossless);
    
                    tempCS->copyStructure(*bestCS, partitioner.chType);
                    tempCS->getPredBuf().copyFrom(bestCS->getPredBuf());
                  }
    
                  //we need to restart the distortion for the new tempCS, the bit count and the cost
                  tempCS->dist = 0;
                  tempCS->fracBits = 0;
                  tempCS->cost = MAX_DOUBLE;
                }
              }
            }
    
          } // bValid
          else
          {
            tempCS->dist = 0;
            tempCS->fracBits = 0;
            tempCS->cost = MAX_DOUBLE;
          }
        }
     // chroma CU cpr comp
        else
        {
          bool success = true;
          // chroma tree, reuse luma bv at minimal block level
          // enabled search only when each chroma sub-block has a BV from its luma sub-block
          assert(tempCS->getCprLumaCoverage(pu.Cb()) == CPR_LUMA_COVERAGE_FULL);
          // check if each BV for the chroma sub-block is valid
          //static const UInt unitArea = MIN_PU_SIZE * MIN_PU_SIZE;
          const CompArea lumaArea = CompArea(COMPONENT_Y, pu.chromaFormat, pu.Cb().lumaPos(), recalcSize(pu.chromaFormat, CHANNEL_TYPE_CHROMA, CHANNEL_TYPE_LUMA, pu.Cb().size()));
          PredictionUnit subPu;
          subPu.cs = pu.cs;
          subPu.cu = pu.cu;
          const ComponentID compID = COMPONENT_Cb; // use Cb to represent both Cb and CR, as their structures are the same
          int shiftHor = ::getComponentScaleX(compID, pu.chromaFormat);
          int shiftVer = ::getComponentScaleY(compID, pu.chromaFormat);
          //const ChromaFormat  chFmt = pu.chromaFormat;
    
          for (int y = lumaArea.y; y < lumaArea.y + lumaArea.height; y += MIN_PU_SIZE)
          {
            for (int x = lumaArea.x; x < lumaArea.x + lumaArea.width; x += MIN_PU_SIZE)
            {
              const MotionInfo &curMi = pu.cs->picture->cs->getMotionInfo(Position{ x, y });
    
              subPu.UnitArea::operator=(UnitArea(pu.chromaFormat, Area(x, y, MIN_PU_SIZE, MIN_PU_SIZE)));
              Position offsetRef = subPu.blocks[compID].pos().offset((curMi.bv.getHor() >> shiftHor), (curMi.bv.getVer() >> shiftVer));
              Position refEndPos(offsetRef.x + subPu.blocks[compID].size().width - 1, offsetRef.y + subPu.blocks[compID].size().height - 1 );
    
              if (!subPu.cs->isDecomp(refEndPos, toChannelType(compID)) || !subPu.cs->isDecomp(offsetRef, toChannelType(compID))) // ref block is not yet available for this chroma sub-block
              {
                success = false;
                break;
              }
            }
            if (!success)
              break;
          }
          ////////////////////////////////////////////////////////////////////////////
    
          if (success)
          {
            //pu.mergeType = MRG_TYPE_CPR;
            m_pcInterSearch->motionCompensation(pu, REF_PIC_LIST_0, false, true); // luma=0, chroma=1
            m_pcInterSearch->encodeResAndCalcRdInterCU(*tempCS, partitioner, false, false, true);
    
            xEncodeDontSplit(*tempCS, partitioner);
    
            xCheckDQP(*tempCS, partitioner);
    
            DTRACE_MODE_COST(*tempCS, m_pcRdCost->getLambda());
    
            xCheckBestMode(tempCS, bestCS, partitioner, encTestMode);
          }
          else
          {
            tempCS->dist = 0;
            tempCS->fracBits = 0;
            tempCS->cost = MAX_DOUBLE;
          }
        }
      }
      // check cpr mode in encoder RD
      //////////////////////////////////////////////////////////////////////////////////////////////
    #endif // CPR
    
    
    void EncCu::xCheckRDCostInter( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode )
    {
      tempCS->initStructData( encTestMode.qp, encTestMode.lossless );
    
    
    #if JVET_L0646_GBI
      
      m_pcInterSearch->setAffineModeSelected(false);
    
      if( tempCS->slice->getCheckLDC() )
      {
        m_bestGbiCost[0] = m_bestGbiCost[1] = std::numeric_limits<double>::max();
        m_bestGbiIdx[0] = m_bestGbiIdx[1] = -1;
      }
    
      m_pcInterSearch->resetBufferedUniMotions();
      int gbiLoopNum = (tempCS->slice->isInterB() ? GBI_NUM : 1);
      gbiLoopNum = (tempCS->sps->getSpsNext().getUseGBi() ? gbiLoopNum : 1);
    
      if( tempCS->area.lwidth() * tempCS->area.lheight() < GBI_SIZE_CONSTRAINT )
      {
        gbiLoopNum = 1;
      }
    
      double curBestCost = bestCS->cost;
      double equGBiCost = MAX_DOUBLE;
    
      for( int gbiLoopIdx = 0; gbiLoopIdx < gbiLoopNum; gbiLoopIdx++ )
      {
        if( m_pcEncCfg->getUseGBiFast() )
        {
          auto blkCache = dynamic_cast< CacheBlkInfoCtrl* >(m_modeCtrl);
    
          if( blkCache )
          {
            bool isBestInter = blkCache->getInter(bestCS->area);
            uint8_t bestGBiIdx = blkCache->getGbiIdx(bestCS->area);
    
            if( isBestInter && g_GbiSearchOrder[gbiLoopIdx] != GBI_DEFAULT && g_GbiSearchOrder[gbiLoopIdx] != bestGBiIdx )
            {
              continue;
            }
          }
        }
        if( !tempCS->slice->getCheckLDC() )
        {
          if( gbiLoopIdx != 0 && gbiLoopIdx != 3 && gbiLoopIdx != 4 )
          {
            continue;
          }
        }
    #endif
    
      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;
    
    #if JVET_L0054_MMVD
      cu.mmvdSkip = false;
    #endif
    
    //cu.affine
      cu.predMode         = MODE_INTER;
      cu.transQuantBypass = encTestMode.lossless;
      cu.chromaQpAdj      = cu.transQuantBypass ? 0 : m_cuChromaQpOffsetIdxPlus1;
      cu.qp               = encTestMode.qp;
      CU::addPUs( cu );
    
    
    #if JVET_L0646_GBI
      cu.GBiIdx = g_GbiSearchOrder[gbiLoopIdx];
      uint8_t gbiIdx = cu.GBiIdx;
      bool  testGbi = (gbiIdx != GBI_DEFAULT);
    #endif
    
      m_pcInterSearch->predInterSearch( cu, partitioner );
    
      const unsigned wIdx = gp_sizeIdxInfo->idxFrom( tempCS->area.lwidth () );
    
    #if JVET_L0646_GBI
      gbiIdx = CU::getValidGbiIdx(cu);
      if( testGbi && gbiIdx == GBI_DEFAULT ) // Enabled GBi but the search results is uni.
      {
        tempCS->initStructData(encTestMode.qp, encTestMode.lossless);
        continue;
      }
      CHECK(!(testGbi || (!testGbi && gbiIdx == GBI_DEFAULT)), " !( bTestGbi || (!bTestGbi && gbiIdx == GBI_DEFAULT ) )");
    
      bool isEqualUni = false;
      if( m_pcEncCfg->getUseGBiFast() )
      {
        if( cu.firstPU->interDir != 3 && testGbi == 0 )
        {
          isEqualUni = true;
        }
      }
    #endif
    
    
      xEncodeInterResidual( tempCS, bestCS, partitioner, encTestMode, 0
    
    Karsten Suehring's avatar
    Karsten Suehring committed
        , m_pImvTempCS ? m_pImvTempCS[wIdx] : NULL
    
    #if JVET_L0646_GBI
        , &equGBiCost
    #endif
    
    #if JVET_L0646_GBI
      if( g_GbiSearchOrder[gbiLoopIdx] == GBI_DEFAULT )
        m_pcInterSearch->setAffineModeSelected((bestCS->cus.front()->affine && !(bestCS->cus.front()->firstPU->mergeFlag)));
    
      tempCS->initStructData(encTestMode.qp, encTestMode.lossless);
    
      double skipTH = MAX_DOUBLE;
      skipTH = (m_pcEncCfg->getUseGBiFast() ? 1.05 : MAX_DOUBLE);
      if( equGBiCost > curBestCost * skipTH )
      {
        break;
      }
    
      if( m_pcEncCfg->getUseGBiFast() )
      {
        if( isEqualUni == true && m_pcEncCfg->getIntraPeriod() == -1 )
        {
          break;
        }
      }
      if( g_GbiSearchOrder[gbiLoopIdx] == GBI_DEFAULT && xIsGBiSkip(cu) && m_pcEncCfg->getUseGBiFast() )
      {
        break;
      }
     }  // for( UChar gbiLoopIdx = 0; gbiLoopIdx < gbiLoopNum; gbiLoopIdx++ )
    #endif
    
    }
    
    
    
    
    
    bool EncCu::xCheckRDCostInterIMV( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode )
    {
      int iIMV = int( ( encTestMode.opts & ETO_IMV ) >> ETO_IMV_SHIFT );
    
    #if JVET_L0646_GBI
      m_pcInterSearch->setAffineModeSelected(false);
    #endif
    
      // Only int-Pel, 4-Pel and fast 4-Pel allowed
      CHECK( iIMV != 1 && iIMV != 2 && iIMV != 3, "Unsupported IMV Mode" );
      // Fast 4-Pel Mode
    
      EncTestMode encTestModeBase = encTestMode;                                        // copy for clearing non-IMV options
      encTestModeBase.opts        = EncTestModeOpts( encTestModeBase.opts & ETO_IMV );  // clear non-IMV options (is that intended?)
    
      tempCS->initStructData( encTestMode.qp, encTestMode.lossless );
    
      CodingStructure* pcCUInfo2Reuse = nullptr;
    
    
    #if JVET_L0646_GBI
      m_pcInterSearch->resetBufferedUniMotions();
      int gbiLoopNum = (tempCS->slice->isInterB() ? GBI_NUM : 1);
      gbiLoopNum = (pcCUInfo2Reuse != NULL ? 1 : gbiLoopNum);
      gbiLoopNum = (tempCS->slice->getSPS()->getSpsNext().getUseGBi() ? gbiLoopNum : 1);
    
      if( tempCS->area.lwidth() * tempCS->area.lheight() < GBI_SIZE_CONSTRAINT )
      {
        gbiLoopNum = 1;
      }
    
      double curBestCost = bestCS->cost;
      double equGBiCost = MAX_DOUBLE;
    
      for( int gbiLoopIdx = 0; gbiLoopIdx < gbiLoopNum; gbiLoopIdx++ )
      {
        if( m_pcEncCfg->getUseGBiFast() )
        {
          auto blkCache = dynamic_cast< CacheBlkInfoCtrl* >(m_modeCtrl);
    
          if( blkCache )
          {
            bool isBestInter = blkCache->getInter(bestCS->area);
            uint8_t bestGBiIdx = blkCache->getGbiIdx(bestCS->area);
    
            if( isBestInter && g_GbiSearchOrder[gbiLoopIdx] != GBI_DEFAULT && g_GbiSearchOrder[gbiLoopIdx] != bestGBiIdx )
            {
              continue;
            }
          }
        }
    
        if( !tempCS->slice->getCheckLDC() )
        {
          if( gbiLoopIdx != 0 && gbiLoopIdx != 3 && gbiLoopIdx != 4 )
          {
            continue;
          }
        }
    
        if( m_pcEncCfg->getUseGBiFast() && tempCS->slice->getCheckLDC() && g_GbiSearchOrder[gbiLoopIdx] != GBI_DEFAULT
          && (m_bestGbiIdx[0] >= 0 && g_GbiSearchOrder[gbiLoopIdx] != m_bestGbiIdx[0])
          && (m_bestGbiIdx[1] >= 0 && g_GbiSearchOrder[gbiLoopIdx] != m_bestGbiIdx[1]))
        {
          continue;
        }
    #endif
    
      CodingUnit &cu = ( pcCUInfo2Reuse != nullptr ) ? *tempCS->getCU( partitioner.chType ) : tempCS->addCU( tempCS->area, partitioner.chType );
    
      if( pcCUInfo2Reuse == nullptr )
      {
        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
    
      //cu.affine
        cu.predMode         = MODE_INTER;
        cu.transQuantBypass = encTestMode.lossless;
        cu.chromaQpAdj      = cu.transQuantBypass ? 0 : m_cuChromaQpOffsetIdxPlus1;
        cu.qp               = encTestMode.qp;
    
        CU::addPUs( cu );
      }
      else
      {
        CHECK( cu.skip,                                "Mismatch" );
        CHECK( cu.qtDepth  != partitioner.currQtDepth, "Mismatch" );
        CHECK( cu.btDepth  != partitioner.currBtDepth, "Mismatch" );
        CHECK( cu.mtDepth  != partitioner.currMtDepth, "Mismatch" );
        CHECK( cu.depth    != partitioner.currDepth,   "Mismatch" );
      }
    
      cu.imv      = iIMV > 1 ? 2 : 1;
      cu.emtFlag  = false;
    
    
    #if JVET_L0646_GBI
      bool testGbi;
      uint8_t gbiIdx;
    #endif
    
      if( pcCUInfo2Reuse != nullptr )
      {
        // reuse the motion info from pcCUInfo2Reuse
        CU::resetMVDandMV2Int( cu, m_pcInterSearch );
    
    
    #if JVET_L0646_GBI
        CHECK(cu.GBiIdx < 0 || cu.GBiIdx >= GBI_NUM, "cu.GBiIdx < 0 || cu.GBiIdx >= GBI_NUM");
        gbiIdx = CU::getValidGbiIdx(cu);
        testGbi = (gbiIdx != GBI_DEFAULT);
    #endif
    
    Xiaozhong Xu's avatar
    Xiaozhong Xu committed
    #if JVET_L0293_CPR
          if (m_modeCtrl->useModeResult(encTestModeBase, tempCS, partitioner))
          {
            std::swap(tempCS, bestCS);
            // store temp best CI for next CU coding
            m_CurrCtx->best = m_CABACEstimator->getCtx();
          }
    #else
    
          m_modeCtrl->useModeResult( encTestModeBase, tempCS, partitioner );
    
    Xiaozhong Xu's avatar
    Xiaozhong Xu committed
    #endif
    
    #if JVET_L0646_GBI 
        cu.GBiIdx = g_GbiSearchOrder[gbiLoopIdx];
        gbiIdx = cu.GBiIdx;
        testGbi = (gbiIdx != GBI_DEFAULT);
    #endif
    
        m_pcInterSearch->predInterSearch( cu, partitioner );
    
    #if JVET_L0646_GBI
        gbiIdx = CU::getValidGbiIdx(cu);
    #endif
    
    #if JVET_L0646_GBI
      if( testGbi && gbiIdx == GBI_DEFAULT ) // Enabled GBi but the search results is uni.
      {
        tempCS->initStructData(encTestMode.qp, encTestMode.lossless);
        continue;
      }
      CHECK(!(testGbi || (!testGbi && gbiIdx == GBI_DEFAULT)), " !( bTestGbi || (!bTestGbi && gbiIdx == GBI_DEFAULT ) )");
    
      bool isEqualUni = false;
      if( m_pcEncCfg->getUseGBiFast() )
      {
        if( cu.firstPU->interDir != 3 && testGbi == 0 )
        {
          isEqualUni = true;
        }
      }
    #endif
    
    Xiaozhong Xu's avatar
    Xiaozhong Xu committed
    #if JVET_L0293_CPR
        if (m_modeCtrl->useModeResult(encTestModeBase, tempCS, partitioner))
        {
          std::swap(tempCS, bestCS);
          // store temp best CI for next CU coding
          m_CurrCtx->best = m_CABACEstimator->getCtx();
        }
    #else
    
        m_modeCtrl->useModeResult( encTestModeBase, tempCS, partitioner );
    
    Xiaozhong Xu's avatar
    Xiaozhong Xu committed
    #endif
    
      xEncodeInterResidual( tempCS, bestCS, partitioner, encTestModeBase, 0
        , NULL
        , true
        , 0
    
    #if JVET_L0646_GBI
        , &equGBiCost
    #endif
    
    #if JVET_L0646_GBI
      tempCS->initStructData(encTestMode.qp, encTestMode.lossless);
    
      double skipTH = MAX_DOUBLE;
      skipTH = (m_pcEncCfg->getUseGBiFast() ? 1.05 : MAX_DOUBLE);
      if( equGBiCost > curBestCost * skipTH )
      {
        break;
      }
    
      if( m_pcEncCfg->getUseGBiFast() )
      {
        if( isEqualUni == true && m_pcEncCfg->getIntraPeriod() == -1 )
        {
          break;
        }
      }
      if( g_GbiSearchOrder[gbiLoopIdx] == GBI_DEFAULT && xIsGBiSkip(cu) && m_pcEncCfg->getUseGBiFast() )
      {
        break;
      }
     } // for( UChar gbiLoopIdx = 0; gbiLoopIdx < gbiLoopNum; gbiLoopIdx++ )
    #endif
    
    
    void EncCu::xEncodeInterResidual( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode, int residualPass
      , CodingStructure* imvCS
      , int emtMode
      , bool* bestHasNonResi
    
    #if JVET_L0646_GBI
      , double* equGBiCost
    #endif
    
    {
      if( residualPass == 1 && encTestMode.lossless )
      {
        return;
      }
    
      CodingUnit*            cu        = tempCS->getCU( partitioner.chType );
      double   bestCostInternal        = MAX_DOUBLE;
    
      double           bestCost        = bestCS->cost;
      const SPS&            sps        = *tempCS->sps;
    
    Karsten Suehring's avatar
    Karsten Suehring committed
      const int      maxSizeEMT        = EMT_INTER_MAX_CU_WITH_QTBT;
    
      bool              swapped        = false; // avoid unwanted data copy
      bool             reloadCU        = false;
      const bool considerEmtSecondPass = emtMode && sps.getSpsNext().getUseInterEMT() && partitioner.currArea().lwidth() <= maxSizeEMT && partitioner.currArea().lheight() <= maxSizeEMT;
    
      int minEMTMode = 0;
      int maxEMTMode = (considerEmtSecondPass?1:0);
    
    Xiang Li's avatar
    Xiang Li committed
      // Not allow very big |MVd| to avoid CABAC crash caused by too large MVd. Normally no impact on coding performance.
      const int maxMvd = 1 << 15;
      const PredictionUnit& pu = *cu->firstPU;
      if (!cu->affine)
      {
        if ((pu.refIdx[0] >= 0 && (pu.mvd[0].getAbsHor() >= maxMvd || pu.mvd[0].getAbsVer() >= maxMvd))
          || (pu.refIdx[1] >= 0 && (pu.mvd[1].getAbsHor() >= maxMvd || pu.mvd[1].getAbsVer() >= maxMvd)))
        {
          return;
        }
      }
    
    
      if( emtMode == 2 )
      {
        minEMTMode = maxEMTMode = (cu->emtFlag?1:0);
      }
    
      for( int curEmtMode = minEMTMode; curEmtMode <= maxEMTMode; curEmtMode++ )
    
        if( reloadCU )
        {
          if( bestCost == bestCS->cost ) //The first EMT pass didn't become the bestCS, so we clear the TUs generated
          {
            tempCS->clearTUs();
          }
          else if( false == swapped )
          {
            tempCS->initStructData( encTestMode.qp, encTestMode.lossless );
            tempCS->copyStructure( *bestCS, partitioner.chType );
            tempCS->getPredBuf().copyFrom( bestCS->getPredBuf() );
            bestCost = bestCS->cost;
            cu       = tempCS->getCU( partitioner.chType );
            swapped = true;
          }
          else
          {
            tempCS->clearTUs();
            bestCost = bestCS->cost;
            cu       = tempCS->getCU( partitioner.chType );
          }
    
          //we need to restart the distortion for the new tempCS, the bit count and the cost
          tempCS->dist     = 0;
          tempCS->fracBits = 0;
          tempCS->cost     = MAX_DOUBLE;
        }
    
        reloadCU    = true; // enable cu reloading
    
    
        const bool skipResidual = residualPass == 1;
        m_pcInterSearch->encodeResAndCalcRdInterCU( *tempCS, partitioner, skipResidual );
    
        xEncodeDontSplit( *tempCS, partitioner );
    
        xCheckDQP( *tempCS, partitioner );