Skip to content
Snippets Groups Projects
EncCu.cpp 150 KiB
Newer Older
  • Learn to ignore specific revisions
  •       if( !jobUsed[jId] || jId == bestJId ) continue;
    
          auto *jobBlkCache = dynamic_cast<BestEncInfoCache*>( m_pcEncLib->getCuEncoder( picture->scheduler.getSplitDataId( jId ) )->m_modeCtrl );
          CHECK( !jobBlkCache, "If own mode controller has blk info cache capability so should all other mode controllers!" );
          blkCache->BestEncInfoCache::copyState( *jobBlkCache, partitioner.currArea() );
        }
    
        blkCache->tick();
      }
    #endif
    
    }
    
    void EncCu::copyState( EncCu* other, Partitioner& partitioner, const UnitArea& currArea, const bool isDist )
    {
      const unsigned wIdx = gp_sizeIdxInfo->idxFrom( partitioner.currArea().lwidth () );
      const unsigned hIdx = gp_sizeIdxInfo->idxFrom( partitioner.currArea().lheight() );
    
      if( isDist )
      {
        other->m_pBestCS[wIdx][hIdx]->initSubStructure( *m_pBestCS[wIdx][hIdx], partitioner.chType, partitioner.currArea(), false );
        other->m_pTempCS[wIdx][hIdx]->initSubStructure( *m_pTempCS[wIdx][hIdx], partitioner.chType, partitioner.currArea(), false );
      }
      else
      {
              CodingStructure* dst =        m_pBestCS[wIdx][hIdx];
    
        const CodingStructure* src = other->m_pBestCS[wIdx][hIdx];
    
        bool keepPred = true;
    
        dst->useSubStructure( *src, partitioner.chType, currArea, keepPred, true, keepResi, keepResi );
    
        dst->cost           =  src->cost;
        dst->dist           =  src->dist;
        dst->fracBits       =  src->fracBits;
        dst->features       =  src->features;
      }
    
      if( isDist )
      {
        m_CurrCtx = m_CtxBuffer.data();
      }
    
      m_pcInterSearch->copyState( *other->m_pcInterSearch );
      m_modeCtrl     ->copyState( *other->m_modeCtrl, partitioner.currArea() );
      m_pcRdCost     ->copyState( *other->m_pcRdCost );
      m_pcTrQuant    ->copyState( *other->m_pcTrQuant );
    
      if( m_pcEncCfg->getReshaper() )
      {
        EncReshape *encReshapeThis  = dynamic_cast<EncReshape*>(       m_pcReshape);
        EncReshape *encReshapeOther = dynamic_cast<EncReshape*>(other->m_pcReshape);
        encReshapeThis->copyState( *encReshapeOther );
      }
    
      m_shareState    = other->m_shareState;
      m_shareBndPosX  = other->m_shareBndPosX;
      m_shareBndPosY  = other->m_shareBndPosY;
      m_shareBndSizeW = other->m_shareBndSizeW;
      m_shareBndSizeH = other->m_shareBndSizeH;
      setShareStateDec( other->getShareStateDec() );
      m_pcInterSearch->setShareState( other->m_pcInterSearch->getShareState() );
    
    
      m_CABACEstimator->getCtx() = other->m_CABACEstimator->getCtx();
    }
    #endif
    
    
    void EncCu::xCheckModeSplit(CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode )
    
    {
      const int qp                = encTestMode.qp;
      const Slice &slice          = *tempCS->slice;
      const bool bIsLosslessMode  = false; // False at this level. Next level down may set it to true.
      const int oldPrevQp         = tempCS->prevQP[partitioner.chType];
    
      const auto oldMotionLut     = tempCS->motionLut;
    
    #if ENABLE_QPA_SUB_CTU
    
      const PPS &pps              = *tempCS->pps;
      const uint32_t currDepth    = partitioner.currDepth;
    #endif
    
      const PartSplit split = getPartSplit( encTestMode );
    
      CHECK( split == CU_DONT_SPLIT, "No proper split provided!" );
    
      tempCS->initStructData( qp, bIsLosslessMode );
    
      m_CABACEstimator->getCtx() = m_CurrCtx->start;
    
    
    Karsten Suehring's avatar
    Karsten Suehring committed
      const TempCtx ctxStartSP( m_CtxCache, SubCtx( Ctx::SplitFlag,   m_CABACEstimator->getCtx() ) );
    
    Adam Wieckowski's avatar
    Adam Wieckowski committed
      const TempCtx ctxStartQt( m_CtxCache, SubCtx( Ctx::SplitQtFlag, m_CABACEstimator->getCtx() ) );
      const TempCtx ctxStartHv( m_CtxCache, SubCtx( Ctx::SplitHvFlag, m_CABACEstimator->getCtx() ) );
      const TempCtx ctxStart12( m_CtxCache, SubCtx( Ctx::Split12Flag, m_CABACEstimator->getCtx() ) );
    
    Karsten Suehring's avatar
    Karsten Suehring committed
      m_CABACEstimator->resetBits();
    
    Adam Wieckowski's avatar
    Adam Wieckowski committed
      m_CABACEstimator->split_cu_mode( split, *tempCS, partitioner );
    
    Karsten Suehring's avatar
    Karsten Suehring committed
      const double factor = ( tempCS->currQP[partitioner.chType] > 30 ? 1.1 : 1.075 );
    
    Nan Hu's avatar
    Nan Hu committed
      tempCS->useDbCost = m_pcEncCfg->getUseEncDbOpt();
      if (!tempCS->useDbCost)
        CHECK(bestCS->costDbOffset != 0, "error");
      const double cost   = m_pcRdCost->calcRdCost( uint64_t( m_CABACEstimator->getEstFracBits() + ( ( bestCS->fracBits ) / factor ) ), Distortion( bestCS->dist / factor ) ) + bestCS->costDbOffset / factor;
    
    Karsten Suehring's avatar
    Karsten Suehring committed
      m_CABACEstimator->getCtx() = SubCtx( Ctx::SplitFlag,   ctxStartSP );
    
    Adam Wieckowski's avatar
    Adam Wieckowski committed
      m_CABACEstimator->getCtx() = SubCtx( Ctx::SplitQtFlag, ctxStartQt );
      m_CABACEstimator->getCtx() = SubCtx( Ctx::SplitHvFlag, ctxStartHv );
      m_CABACEstimator->getCtx() = SubCtx( Ctx::Split12Flag, ctxStart12 );
    
      if (cost > bestCS->cost + bestCS->costDbOffset
    
        || (m_pcEncCfg->getUsePerceptQPA() && !m_pcEncCfg->getUseRateCtrl() && pps.getUseDQP() && (pps.getCuQpDeltaSubdiv() > 0) && (split == CU_HORZ_SPLIT || split == CU_VERT_SPLIT) &&
            (currDepth == 0)) // force quad-split or no split at CTU level
    
    Nan Hu's avatar
    Nan Hu committed
    #endif
    
    Karsten Suehring's avatar
    Karsten Suehring committed
      {
        xCheckBestMode( tempCS, bestCS, partitioner, encTestMode );
        return;
    
      int startShareThisLevel = 0;
      const uint32_t uiLPelX = tempCS->area.Y().lumaPos().x;
      const uint32_t uiTPelY = tempCS->area.Y().lumaPos().y;
    
      int splitRatio = 1;
      CHECK(!(split == CU_QUAD_SPLIT || split == CU_HORZ_SPLIT || split == CU_VERT_SPLIT
        || split == CU_TRIH_SPLIT || split == CU_TRIV_SPLIT), "invalid split type");
      splitRatio = (split == CU_HORZ_SPLIT || split == CU_VERT_SPLIT) ? 1 : 2;
    
      bool isOneChildSmall = ((tempCS->area.lwidth())*(tempCS->area.lheight()) >> splitRatio) < MRG_SHARELIST_SHARSIZE;
    
      if ((((tempCS->area.lwidth())*(tempCS->area.lheight())) > (MRG_SHARELIST_SHARSIZE * 1)))
      {
        m_shareState = NO_SHARE;
      }
    
      if (m_shareState == NO_SHARE)//init state
      {
        if (isOneChildSmall)
        {
          m_shareState = GEN_ON_SHARED_BOUND;//share start state
          startShareThisLevel = 1;
        }
      }
    
    #if JVET_N0266_SMALL_BLOCKS
      if ( m_shareState == GEN_ON_SHARED_BOUND && slice.getSPS()->getIBCFlag() )
    #else
    
      if ((m_shareState == GEN_ON_SHARED_BOUND) && (!slice.isIntra() || slice.getSPS()->getIBCFlag()))
    
        tempCS->motionLut.lutShare = tempCS->motionLut.lut;
    
        tempCS->motionLut.lutShareIbc = tempCS->motionLut.lutIbc;
    
        m_shareBndPosX = uiLPelX;
        m_shareBndPosY = uiTPelY;
        m_shareBndSizeW = tempCS->area.lwidth();
        m_shareBndSizeH = tempCS->area.lheight();
        m_shareState = SHARING;
      }
    
    
      m_pcInterSearch->setShareState(m_shareState);
      setShareStateDec(m_shareState);
    
    
      partitioner.splitCurrArea( split, *tempCS );
    
      bool qgEnableChildren = partitioner.currQgEnable(); // QG possible at children level
    
    Taoran Lu's avatar
    Taoran Lu committed
    
      tempCS->getPredBuf().fill(0);
    
      AffineMVInfo tmpMVInfo;
      bool isAffMVInfoSaved;
      m_pcInterSearch->savePrevAffMVInfo(0, tmpMVInfo, isAffMVInfoSaved);
    
    
      do
      {
        const auto &subCUArea  = partitioner.currArea();
    
        if( tempCS->picture->Y().contains( subCUArea.lumaPos() ) )
        {
          const unsigned wIdx    = gp_sizeIdxInfo->idxFrom( subCUArea.lwidth () );
          const unsigned hIdx    = gp_sizeIdxInfo->idxFrom( subCUArea.lheight() );
    
          CodingStructure *tempSubCS = m_pTempCS[wIdx][hIdx];
          CodingStructure *bestSubCS = m_pBestCS[wIdx][hIdx];
    
          tempCS->initSubStructure( *tempSubCS, partitioner.chType, subCUArea, false );
          tempCS->initSubStructure( *bestSubCS, partitioner.chType, subCUArea, false );
    
          tempSubCS->sharedBndPos.x = (m_shareState == SHARING) ? m_shareBndPosX : tempSubCS->area.Y().lumaPos().x;
          tempSubCS->sharedBndPos.y = (m_shareState == SHARING) ? m_shareBndPosY : tempSubCS->area.Y().lumaPos().y;
          tempSubCS->sharedBndSize.width = (m_shareState == SHARING) ? m_shareBndSizeW : tempSubCS->area.lwidth();
          tempSubCS->sharedBndSize.height = (m_shareState == SHARING) ? m_shareBndSizeH : tempSubCS->area.lheight();
          bestSubCS->sharedBndPos.x = (m_shareState == SHARING) ? m_shareBndPosX : tempSubCS->area.Y().lumaPos().x;
          bestSubCS->sharedBndPos.y = (m_shareState == SHARING) ? m_shareBndPosY : tempSubCS->area.Y().lumaPos().y;
          bestSubCS->sharedBndSize.width = (m_shareState == SHARING) ? m_shareBndSizeW : tempSubCS->area.lwidth();
          bestSubCS->sharedBndSize.height = (m_shareState == SHARING) ? m_shareBndSizeH : tempSubCS->area.lheight();
    
          xCompressCU( tempSubCS, bestSubCS, partitioner );
    
    
          if( bestSubCS->cost == MAX_DOUBLE )
          {
            CHECK( split == CU_QUAD_SPLIT, "Split decision reusing cannot skip quad split" );
            tempCS->cost = MAX_DOUBLE;
    
    Nan Hu's avatar
    Nan Hu committed
            tempCS->costDbOffset = 0;
            tempCS->useDbCost = m_pcEncCfg->getUseEncDbOpt();
    
            m_CurrCtx--;
            partitioner.exitCurrSplit();
            xCheckBestMode( tempCS, bestCS, partitioner, encTestMode );
            return;
          }
    
          bool keepResi = KEEP_PRED_AND_RESI_SIGNALS;
          tempCS->useSubStructure( *bestSubCS, partitioner.chType, CS::getArea( *tempCS, subCUArea, partitioner.chType ), KEEP_PRED_AND_RESI_SIGNALS, true, keepResi, keepResi );
    
    
          if( partitioner.currQgEnable() )
    
          {
            tempCS->prevQP[partitioner.chType] = bestSubCS->prevQP[partitioner.chType];
          }
    
          tempSubCS->releaseIntermediateData();
          bestSubCS->releaseIntermediateData();
        }
      } while( partitioner.nextPart( *tempCS ) );
    
      partitioner.exitCurrSplit();
    
    
      if (startShareThisLevel == 1)
      {
        m_shareState = NO_SHARE;
        m_pcInterSearch->setShareState(m_shareState);
        setShareStateDec(m_shareState);
      }
    
    
      m_CurrCtx--;
    
      // Finally, generate split-signaling bits for RD-cost check
      const PartSplit implicitSplit = partitioner.getImplicitSplit( *tempCS );
    
      {
        bool enforceQT = implicitSplit == CU_QUAD_SPLIT;
    #if HM_QTBT_REPRODUCE_FAST_LCTU_BUG
    
        // LARGE CTU bug
    
    Karsten Suehring's avatar
    Karsten Suehring committed
        if( m_pcEncCfg->getUseFastLCTU() )
    
          unsigned maxDepth = g_aucLog2[tempCS->sps->getCTUSize()] - g_aucLog2[tempCS->sps->getMinQTSize(slice.getSliceType(), partitioner.chType)];
    
    
          if( auto ad = dynamic_cast<AdaptiveDepthPartitioner*>( &partitioner ) )
          {
            ad->setMaxMinDepth( minDepth, maxDepth, *tempCS );
          }
    
          if( minDepth > partitioner.currQtDepth )
          {
            // enforce QT
            enforceQT = true;
          }
        }
    #endif
    
        if( !enforceQT )
        {
          m_CABACEstimator->resetBits();
    
    
    Adam Wieckowski's avatar
    Adam Wieckowski committed
          m_CABACEstimator->split_cu_mode( split, *tempCS, partitioner );
    
    
          tempCS->fracBits += m_CABACEstimator->getEstFracBits(); // split bits
        }
      }
    
      tempCS->cost = m_pcRdCost->calcRdCost( tempCS->fracBits, tempCS->dist );
    
      // Check Delta QP bits for splitted structure
    
      if( !qgEnableChildren ) // check at deepest QG level only
    
      xCheckDQP( *tempCS, partitioner, true );
    
      // If the configuration being tested exceeds the maximum number of bytes for a slice / slice-segment, then
      // a proper RD evaluation cannot be performed. Therefore, termination of the
      // slice/slice-segment must be made prior to this CTU.
      // This can be achieved by forcing the decision to be that of the rpcTempCU.
      // The exception is each slice / slice-segment must have at least one CTU.
      if (bestCS->cost != MAX_DOUBLE)
      {
        const TileMap& tileMap = *tempCS->picture->tileMap;
        const uint32_t CtuAddr             = CU::getCtuAddr( *bestCS->getCU( partitioner.chType ) );
        const bool isEndOfSlice        =    slice.getSliceMode() == FIXED_NUMBER_OF_BYTES
                                          && ((slice.getSliceBits() + CS::getEstBits(*bestCS)) > slice.getSliceArgument() << 3)
                                          && CtuAddr != tileMap.getCtuTsToRsAddrMap(slice.getSliceCurStartCtuTsAddr())
    #if HEVC_DEPENDENT_SLICES
                                          && CtuAddr != tileMap.getCtuTsToRsAddrMap(slice.getSliceSegmentCurStartCtuTsAddr());
    #else
                                          ;
    #endif
    
    #if HEVC_DEPENDENT_SLICES
        const bool isEndOfSliceSegment =    slice.getSliceSegmentMode() == FIXED_NUMBER_OF_BYTES
                                          && ((slice.getSliceSegmentBits() + CS::getEstBits(*bestCS)) > slice.getSliceSegmentArgument() << 3)
                                          && CtuAddr != tileMap.getCtuTsToRsAddrMap(slice.getSliceSegmentCurStartCtuTsAddr());
                                              // Do not need to check slice condition for slice-segment since a slice-segment is a subset of a slice.
        if (isEndOfSlice || isEndOfSliceSegment)
    #else
        if(isEndOfSlice)
    #endif
        {
          bestCS->cost = MAX_DOUBLE;
    
    Nan Hu's avatar
    Nan Hu committed
          bestCS->costDbOffset = 0;
    
    Nan Hu's avatar
    Nan Hu committed
      else
      {
        bestCS->costDbOffset = 0;
      }
      tempCS->useDbCost = m_pcEncCfg->getUseEncDbOpt();
    
    
      // RD check for sub partitioned coding structure.
      xCheckBestMode( tempCS, bestCS, partitioner, encTestMode );
    
    
      if (isAffMVInfoSaved)
        m_pcInterSearch->addAffMVInfo(tmpMVInfo);
    
    
      tempCS->motionLut = oldMotionLut;
    
      tempCS->releaseIntermediateData();
    
      tempCS->prevQP[partitioner.chType] = oldPrevQp;
    }
    
    
    void EncCu::xCheckRDCostIntra( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode )
    {
    
    Tung Nguyen's avatar
    Tung Nguyen committed
      const PPS &pps      = *tempCS->pps;
    
      bool   useIntraSubPartitions   = false;
      double maxCostAllowedForChroma = MAX_DOUBLE;
      const  CodingUnit *bestCU      = bestCS->getCU( partitioner.chType );
    
    Tung Nguyen's avatar
    Tung Nguyen committed
      {
    
    
        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;
        cu.tileIdx          = tempCS->picture->tileMap->getTileIdxMap( tempCS->area.lumaPos() );
        cu.skip             = false;
    
        cu.predMode         = MODE_INTRA;
        cu.transQuantBypass = encTestMode.lossless;
        cu.chromaQpAdj      = cu.transQuantBypass ? 0 : m_cuChromaQpOffsetIdxPlus1;
        cu.qp               = encTestMode.qp;
      //cu.ipcm             = false;
    
        cu.ispMode          = NOT_INTRA_SUBPARTITIONS;
    
    Nan Hu's avatar
    Nan Hu committed
        m_bestModeUpdated = tempCS->useDbCost = bestCS->useDbCost = false;
    
    
          //the Intra SubPartitions mode uses the value of the best cost so far (luma if it is the fast version) to avoid test non-necessary lines
    
          const double bestCostSoFar = CS::isDualITree( *tempCS ) ? m_modeCtrl->getBestCostWithoutSplitFlags() : bestCU && bestCU->predMode == MODE_INTRA ? bestCS->lumaCost : bestCS->cost;
          m_pcIntraSearch->estIntraPredLumaQT( cu, partitioner, bestCostSoFar );
    
          useIntraSubPartitions = cu.ispMode != NOT_INTRA_SUBPARTITIONS;
          if( !CS::isDualITree( *tempCS ) )
          {
            tempCS->lumaCost = m_pcRdCost->calcRdCost( tempCS->fracBits, tempCS->dist );
            if( useIntraSubPartitions )
            {
              //the difference between the best cost so far and the current luma cost is stored to avoid testing the Cr component if the cost of luma + Cb is larger than the best cost
              maxCostAllowedForChroma = bestCS->cost < MAX_DOUBLE ? bestCS->cost - tempCS->lumaCost : MAX_DOUBLE;
            }
          }
    
    
          if (m_pcEncCfg->getUsePbIntraFast() && tempCS->dist == std::numeric_limits<Distortion>::max()
              && tempCS->interHad == 0)
          {
            interHad = 0;
            // JEM assumes only perfect reconstructions can from now on beat the inter mode
            m_modeCtrl->enforceInterHad( 0 );
    
    Tung Nguyen's avatar
    Tung Nguyen committed
            return;
    
          }
    
          if( !CS::isDualITree( *tempCS ) )
          {
            cu.cs->picture->getRecoBuf( cu.Y() ).copyFrom( cu.cs->getRecoBuf( COMPONENT_Y ) );
    
    Taoran Lu's avatar
    Taoran Lu committed
            cu.cs->picture->getPredBuf(cu.Y()).copyFrom(cu.cs->getPredBuf(COMPONENT_Y));
    
          }
        }
    
        if( tempCS->area.chromaFormat != CHROMA_400 && ( partitioner.chType == CHANNEL_TYPE_CHROMA || !CS::isDualITree( *tempCS ) ) )
        {
    
          TUIntraSubPartitioner subTuPartitioner( partitioner );
          m_pcIntraSearch->estIntraPredChromaQT( cu, ( !useIntraSubPartitions || ( CS::isDualITree( *cu.cs ) && !isLuma( CHANNEL_TYPE_CHROMA ) ) ) ? partitioner : subTuPartitioner, maxCostAllowedForChroma );
          if( useIntraSubPartitions && !cu.ispMode )
          {
            //At this point the temp cost is larger than the best cost. Therefore, we can already skip the remaining calculations
            return;
          }
    
        }
    
        cu.rootCbf = false;
    
        for( uint32_t t = 0; t < getNumberValidTBlocks( *cu.cs->pcv ); t++ )
        {
          cu.rootCbf |= cu.firstTU->cbf[t] != 0;
        }
    
        // Get total bits for current mode: encode CU
        m_CABACEstimator->resetBits();
    
        if( pps.getTransquantBypassEnabledFlag() )
        {
          m_CABACEstimator->cu_transquant_bypass_flag( cu );
        }
    
    
    Yu Han's avatar
    Yu Han committed
        if ((!cu.cs->slice->isIntra() || cu.cs->slice->getSPS()->getIBCFlag())
    
    Xiaozhong Xu's avatar
    Xiaozhong Xu committed
          && cu.Y().valid()
    
        {
          m_CABACEstimator->cu_skip_flag ( cu );
        }
        m_CABACEstimator->pred_mode      ( cu );
    
        m_CABACEstimator->pcm_data       ( cu, partitioner );
    
        m_CABACEstimator->extend_ref_line( cu );
    
        m_CABACEstimator->isp_mode       ( cu );
    
        m_CABACEstimator->cu_pred_data   ( cu );
    
        // Encode Coefficients
        CUCtx cuCtx;
        cuCtx.isDQPCoded = true;
        cuCtx.isChromaQpAdjCoded = true;
        m_CABACEstimator->cu_residual( cu, partitioner, cuCtx );
    
        tempCS->fracBits = m_CABACEstimator->getEstFracBits();
        tempCS->cost     = m_pcRdCost->calcRdCost(tempCS->fracBits, tempCS->dist);
    
    
        const double tmpCostWithoutSplitFlags = tempCS->cost;
    
        xEncodeDontSplit( *tempCS, partitioner );
    
        xCheckDQP( *tempCS, partitioner );
    
    
        if( tempCS->cost < bestCS->cost )
        {
          m_modeCtrl->setBestCostWithoutSplitFlags( tmpCostWithoutSplitFlags );
        }
    
    Nan Hu's avatar
    Nan Hu committed
    
    
    Nan Hu's avatar
    Nan Hu committed
        xCalDebCost( *tempCS, partitioner );
    
    Nan Hu's avatar
    Nan Hu committed
        tempCS->useDbCost = m_pcEncCfg->getUseEncDbOpt();
    
    
    
    #if WCG_EXT
        DTRACE_MODE_COST( *tempCS, m_pcRdCost->getLambda( true ) );
    #else
        DTRACE_MODE_COST( *tempCS, m_pcRdCost->getLambda() );
    #endif
        xCheckBestMode( tempCS, bestCS, partitioner, encTestMode );
    
      } //for emtCuFlag
    }
    
    void EncCu::xCheckIntraPCM(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;
      cu.tileIdx          = tempCS->picture->tileMap->getTileIdxMap( tempCS->area.lumaPos() );
      cu.skip             = false;
    
      cu.predMode         = MODE_INTRA;
      cu.transQuantBypass = encTestMode.lossless;
      cu.chromaQpAdj      = cu.transQuantBypass ? 0 : m_cuChromaQpOffsetIdxPlus1;
      cu.qp               = encTestMode.qp;
      cu.ipcm             = true;
    
    
      tempCS->addPU( CS::getArea( *tempCS, tempCS->area, partitioner.chType ), partitioner.chType );
    
      tempCS->addTU( CS::getArea( *tempCS, tempCS->area, partitioner.chType ), partitioner.chType );
    
    
      m_pcIntraSearch->IPCMSearch(*tempCS, partitioner);
    
      m_CABACEstimator->getCtx() = m_CurrCtx->start;
    
      m_CABACEstimator->resetBits();
    
      if( tempCS->pps->getTransquantBypassEnabledFlag() )
      {
        m_CABACEstimator->cu_transquant_bypass_flag( cu );
      }
    
    
    Yu Han's avatar
    Yu Han committed
      if ((!cu.cs->slice->isIntra() || cu.cs->slice->getSPS()->getIBCFlag())
    
    Xiaozhong Xu's avatar
    Xiaozhong Xu committed
        && cu.Y().valid()
    
      {
        m_CABACEstimator->cu_skip_flag ( cu );
      }
      m_CABACEstimator->pred_mode      ( cu );
    
      m_CABACEstimator->pcm_data       ( cu, partitioner );
    
    
    
      tempCS->fracBits = m_CABACEstimator->getEstFracBits();
      tempCS->cost     = m_pcRdCost->calcRdCost(tempCS->fracBits, tempCS->dist);
    
      xEncodeDontSplit( *tempCS, partitioner );
    
      xCheckDQP( *tempCS, partitioner );
    
    
    Nan Hu's avatar
    Nan Hu committed
      xCalDebCost( *tempCS, partitioner );
    
    Nan Hu's avatar
    Nan Hu committed
      tempCS->useDbCost = m_pcEncCfg->getUseEncDbOpt();
    
    
    #if WCG_EXT
      DTRACE_MODE_COST( *tempCS, m_pcRdCost->getLambda( true ) );
    #else
      DTRACE_MODE_COST( *tempCS, m_pcRdCost->getLambda() );
    #endif
      xCheckBestMode( tempCS, bestCS, partitioner, encTestMode );
    }
    
    void EncCu::xCheckDQP( CodingStructure& cs, Partitioner& partitioner, bool bKeepCtx )
    {
      CHECK( bKeepCtx && cs.cus.size() <= 1 && partitioner.getImplicitSplit( cs ) == CU_DONT_SPLIT, "bKeepCtx should only be set in split case" );
      CHECK( !bKeepCtx && cs.cus.size() > 1, "bKeepCtx should never be set for non-split case" );
    
      if( !cs.pps->getUseDQP() )
      {
        return;
      }
    
    
      if (CS::isDualITree(cs) && isChroma(partitioner.chType))
    
      if( !partitioner.currQgEnable() ) // do not consider split or leaf/not leaf QG condition (checked by caller)
    
      {
        return;
      }
    
    
      CodingUnit* cuFirst = cs.getCU( partitioner.chType );
    
      CHECK( !cuFirst, "No CU available" );
    
      bool hasResidual = false;
      for( const auto &cu : cs.cus )
      {
        if( cu->rootCbf )
        {
          hasResidual = true;
          break;
        }
      }
    
      int predQP = CU::predictQP( *cuFirst, cs.prevQP[partitioner.chType] );
    
      if( hasResidual )
      {
        TempCtx ctxTemp( m_CtxCache );
        if( !bKeepCtx ) ctxTemp = SubCtx( Ctx::DeltaQP, m_CABACEstimator->getCtx() );
    
        m_CABACEstimator->resetBits();
        m_CABACEstimator->cu_qp_delta( *cuFirst, predQP, cuFirst->qp );
    
        cs.fracBits += m_CABACEstimator->getEstFracBits(); // dQP bits
        cs.cost      = m_pcRdCost->calcRdCost(cs.fracBits, cs.dist);
    
    
        if( !bKeepCtx ) m_CABACEstimator->getCtx() = SubCtx( Ctx::DeltaQP, ctxTemp );
    
        // NOTE: reset QPs for CUs without residuals up to first coded CU
        for( const auto &cu : cs.cus )
        {
          if( cu->rootCbf )
          {
            break;
          }
          cu->qp = predQP;
        }
      }
      else
      {
        // No residuals: reset CU QP to predicted value
        for( const auto &cu : cs.cus )
        {
          cu->qp = predQP;
        }
      }
    }
    
    void EncCu::xFillPCMBuffer( CodingUnit &cu )
    {
      const ChromaFormat format        = cu.chromaFormat;
      const uint32_t numberValidComponents = getNumberValidComponents(format);
    
      for( auto &tu : CU::traverseTUs( cu ) )
      {
        for( uint32_t ch = 0; ch < numberValidComponents; ch++ )
        {
          const ComponentID compID = ComponentID( ch );
    
          const CompArea &compArea = tu.blocks[ compID ];
    
          const CPelBuf source      = tu.cs->getOrgBuf( compArea );
                 PelBuf destination = tu.getPcmbuf( compID );
    
    
    Taoran Lu's avatar
    Taoran Lu committed
          if (tu.cs->slice->getReshapeInfo().getUseSliceReshaper() && m_pcReshape->getCTUFlag() && compID == COMPONENT_Y)
          {
            CompArea    tmpArea(COMPONENT_Y, compArea.chromaFormat, Position(0, 0), compArea.size());
            PelBuf tempOrgBuf = m_tmpStorageLCU->getBuf(tmpArea);
            tempOrgBuf.copyFrom(source);
            tempOrgBuf.rspSignal(m_pcReshape->getFwdLUT());
            destination.copyFrom(tempOrgBuf);
          }
          else
            destination.copyFrom( source );
    
    void EncCu::xCheckRDCostHashInter( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode )
    {
      bool isPerfectMatch = false;
    
      tempCS->initStructData(encTestMode.qp, encTestMode.lossless);
      m_pcInterSearch->resetBufferedUniMotions();
      m_pcInterSearch->setAffineModeSelected(false);
      CodingUnit &cu = tempCS->addCU(tempCS->area, partitioner.chType);
    
      partitioner.setCUData(cu);
      cu.slice = tempCS->slice;
      cu.skip = false;
      cu.predMode = MODE_INTER;
      cu.transQuantBypass = encTestMode.lossless;
      cu.chromaQpAdj = cu.transQuantBypass ? 0 : m_cuChromaQpOffsetIdxPlus1;
      cu.qp = encTestMode.qp;
      CU::addPUs(cu);
      cu.mmvdSkip = false;
      cu.firstPU->mmvdMergeFlag = false;
    
      if (m_pcInterSearch->predInterHashSearch(cu, partitioner, isPerfectMatch))
      {
        double equGBiCost = MAX_DOUBLE;
    
    
    Nan Hu's avatar
    Nan Hu committed
        m_bestModeUpdated = tempCS->useDbCost = bestCS->useDbCost = false;
    
    
        xEncodeInterResidual(tempCS, bestCS, partitioner, encTestMode, 0
          , 0
          , &equGBiCost
        );
    
    Nan Hu's avatar
    Nan Hu committed
    
    
    Nan Hu's avatar
    Nan Hu committed
        if ( m_bestModeUpdated && bestCS->cost != MAX_DOUBLE )
        {
          xCalDebCost( *bestCS, partitioner );
        }
    
      }
      tempCS->initStructData(encTestMode.qp, encTestMode.lossless);
    
      if (cu.lwidth() != 64)
      {
        isPerfectMatch = false;
      }
      m_modeCtrl->setIsHashPerfectMatch(isPerfectMatch);
    }
    
    
    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 );
    
      MergeCtx mergeCtx;
      const SPS &sps = *tempCS->sps;
    
    
      if( sps.getSBTMVPEnabledFlag() )
    
      {
        Size bufSize = g_miScaling.scale( tempCS->area.lumaSize() );
        mergeCtx.subPuMvpMiBuf    = MotionBuf( m_SubPuMiBuf,    bufSize );
      }
    
    
      Mv   refinedMvdL0[MAX_NUM_PARTS_IN_CTU][MRG_MAX_NUM_CANDS];
    
      setMergeBestSATDCost( MAX_DOUBLE );
    
    
      {
        // first get merge candidates
        CodingUnit cu( tempCS->area );
        cu.cs       = tempCS;
        cu.predMode = MODE_INTER;
        cu.slice    = tempCS->slice;
        cu.tileIdx  = tempCS->picture->tileMap->getTileIdxMap(tempCS->area.lumaPos());
    
        PredictionUnit pu( tempCS->area );
        pu.cu = &cu;
        pu.cs = tempCS;
    
        pu.shareParentPos = tempCS->sharedBndPos;
        pu.shareParentSize = tempCS->sharedBndSize;
    
        PU::getInterMergeCandidates(pu, mergeCtx
          , 0
        );
    
        PU::getInterMMVDMergeCandidates(pu, mergeCtx);
    
    #if JVET_N0324_REGULAR_MRG_FLAG
        pu.regularMergeFlag = true;
    #endif
    
      bool candHasNoResidual[MRG_MAX_NUM_CANDS + MMVD_ADD_NUM];
      for (uint32_t ui = 0; ui < MRG_MAX_NUM_CANDS + MMVD_ADD_NUM; ui++)
      {
        candHasNoResidual[ui] = false;
      }
    
      bool                                        bestIsSkip = false;
      bool                                        bestIsMMVDSkip = true;
    
      PelUnitBuf                                  acMergeBuffer[MRG_MAX_NUM_CANDS];
    
      PelUnitBuf                                  acMergeRealBuffer[MMVD_MRG_MAX_RD_BUF_NUM];
      PelUnitBuf *                                acMergeTempBuffer[MMVD_MRG_MAX_RD_NUM];
      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);
      }
    
      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;
        }
      }
    
    
      static_vector<unsigned, MRG_MAX_NUM_CANDS + MMVD_ADD_NUM>  RdModeList2; // store the Intra mode for Intrainter
      RdModeList2.clear();
    
      bool isIntrainterEnabled = sps.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;
      }
      if( m_pcEncCfg->getUseFastMerge() || isIntrainterEnabled)
    
        if (isIntrainterEnabled)
        {
          uiNumMrgSATDCand += 1;
        }
    
        bestIsSkip       = false;
    
        if( auto blkCache = dynamic_cast< CacheBlkInfoCtrl* >( m_modeCtrl ) )
        {
    
    Yu Han's avatar
    Yu Han committed
          if (slice.getSPS()->getIBCFlag())
    
    Xiaozhong Xu's avatar
    Xiaozhong Xu committed
          {
            ComprCUCtx cuECtx = m_modeCtrl->getComprCUCtx();
            bestIsSkip = blkCache->isSkip(tempCS->area) && cuECtx.bestCU;
          }
          else
    
          bestIsSkip = blkCache->isSkip( tempCS->area );
    
          bestIsMMVDSkip = blkCache->isMMVDSkip(tempCS->area);
    
    
        if (isIntrainterEnabled) // always perform low complexity check
        {
          bestIsSkip = false;
        }
    
    
        static_vector<double, MRG_MAX_NUM_CANDS + MMVD_ADD_NUM> candCostList;
    
        // 1. Pass: get SATD-cost for selected candidates and reduce their count
        if( !bestIsSkip )
        {
          RdModeList.clear();
          mrgTempBufSet       = true;
          const double sqrtLambdaForFirstPass = m_pcRdCost->getMotionLambda( encTestMode.lossless );
    
          CodingUnit &cu      = tempCS->addCU( tempCS->area, partitioner.chType );
    
          const double sqrtLambdaForFirstPassIntra = m_pcRdCost->getMotionLambda(cu.transQuantBypass) / double(1 << SCALE_BITS);
    
    
          partitioner.setCUData( cu );
          cu.slice            = tempCS->slice;
          cu.tileIdx          = tempCS->picture->tileMap->getTileIdxMap( tempCS->area.lumaPos() );
          cu.skip             = false;
    
        //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->addPU( cu, partitioner.chType );
    
          DistParam distParam;
    
    #if JVET_N0329_IBC_SEARCH_IMP
          const bool bUseHadamard = !encTestMode.lossless && !tempCS->slice->getDisableSATDForRD();
    #else
    
          const bool bUseHadamard= !encTestMode.lossless;
    
          m_pcRdCost->setDistParam (distParam, tempCS->getOrgBuf().Y(), m_acMergeBuffer[0].Y(), sps.getBitDepth (CHANNEL_TYPE_LUMA), COMPONENT_Y, bUseHadamard);
    
          const UnitArea localUnitArea( tempCS->area.chromaFormat, Area( 0, 0, tempCS->area.Y().width, tempCS->area.Y().height) );
          for( uint32_t uiMergeCand = 0; uiMergeCand < mergeCtx.numValidMergeCand; uiMergeCand++ )
          {
            mergeCtx.setMergeInfo( pu, uiMergeCand );
    
            PU::spanMotionInfo( pu, mergeCtx );
    
            distParam.cur = singleMergeTempBuffer->Y();
            m_pcInterSearch->motionCompensation(pu, *singleMergeTempBuffer);
    
            acMergeBuffer[uiMergeCand] = m_acRealMergeBuffer[uiMergeCand].getBuf(localUnitArea);
            acMergeBuffer[uiMergeCand].copyFrom(*singleMergeTempBuffer);
    
            if( mergeCtx.interDirNeighbours[uiMergeCand] == 3 && mergeCtx.mrgTypeNeighbours[uiMergeCand] == MRG_TYPE_DEFAULT_N )
            {
              mergeCtx.mvFieldNeighbours[2*uiMergeCand].mv   = pu.mv[0];
              mergeCtx.mvFieldNeighbours[2*uiMergeCand+1].mv = pu.mv[1];
    
              {
                int dx, dy, i, j, num = 0;
                dy = std::min<int>(pu.lumaSize().height, DMVR_SUBCU_HEIGHT);
                dx = std::min<int>(pu.lumaSize().width, DMVR_SUBCU_WIDTH);
                if (PU::checkDMVRCondition(pu))
                {
                  for (i = 0; i < (pu.lumaSize().height); i += dy)
                  {
                    for (j = 0; j < (pu.lumaSize().width); j += dx)
                    {
                      refinedMvdL0[num][uiMergeCand] = pu.mvdL0SubPu[num];
                      num++;
                    }
                  }
                }
              }
    
            }
    
            Distortion uiSad = distParam.distFunc(distParam);
            uint32_t uiBitsCand = uiMergeCand + 1;
            if( uiMergeCand == tempCS->slice->getMaxNumMergeCand() - 1 )
            {
              uiBitsCand--;
            }
    
    Frank Bossen's avatar
    Frank Bossen committed
    #if !JVET_MMVD_OFF_MACRO
    
    Seungsoo Jeong's avatar
    Seungsoo Jeong committed
    #if JVET_N0127_MMVD_SPS_FLAG 
            if ( pu.cs->sps->getUseMMVD() )
    
              uiBitsCand++; // for mmvd_flag
    #else
    
            uiBitsCand++; // for mmvd_flag
    
    Frank Bossen's avatar
    Frank Bossen committed
    #endif
    
            double cost     = (double)uiSad + (double)uiBitsCand * sqrtLambdaForFirstPass;
    
            updateDoubleCandList(uiMergeCand, cost, RdModeList, candCostList, RdModeList2, (uint32_t)NUM_LUMA_MODE, uiNumMrgSATDCand, &insertPos);
    
            if (insertPos != -1)
            {
              if (insertPos == RdModeList.size() - 1)
              {
                swap(singleMergeTempBuffer, acMergeTempBuffer[insertPos]);
              }
              else
              {
                for (uint32_t i = uint32_t(RdModeList.size()) - 1; i > insertPos; i--)
                {
                  swap(acMergeTempBuffer[i - 1], acMergeTempBuffer[i]);
                }
                swap(singleMergeTempBuffer, acMergeTempBuffer[insertPos]);
              }
            }
    
    Yu Han's avatar
    Yu Han committed
            CHECK(std::min(uiMergeCand + 1, uiNumMrgSATDCand) != RdModeList.size(), "");
    
    
          if (isIntrainterEnabled)
          {
            int numTestIntraMode = 4;
            // prepare for Intra bits calculation
            const TempCtx ctxStart(m_CtxCache, m_CABACEstimator->getCtx());
            const TempCtx ctxStartIntraMode(m_CtxCache, SubCtx(Ctx::MHIntraPredMode, m_CABACEstimator->getCtx()));
    
            // for Intrainter fast, recored the best intra mode during the first round for mrege 0
            int bestMHIntraMode = -1;
            double bestMHIntraCost = MAX_DOUBLE;
    
    
            pu.mhIntraFlag = true;
    
    
            // save the to-be-tested merge candidates
            uint32_t MHIntraMergeCand[NUM_MRG_SATD_CAND];
    
            for (uint32_t mergeCnt = 0; mergeCnt < std::min(NUM_MRG_SATD_CAND, (const int)mergeCtx.numValidMergeCand); mergeCnt++)
    
            {
              MHIntraMergeCand[mergeCnt] = RdModeList[mergeCnt];
            }
    
            for (uint32_t mergeCnt = 0; mergeCnt < std::min(std::min(NUM_MRG_SATD_CAND, (const int)mergeCtx.numValidMergeCand), 4); mergeCnt++)
    
            {
              uint32_t mergeCand = MHIntraMergeCand[mergeCnt];
              acMergeBuffer[mergeCand] = m_acRealMergeBuffer[mergeCand].getBuf(localUnitArea);
    
              // estimate merge bits
              uint32_t bitsCand = mergeCand + 1;
              if (mergeCand == pu.cs->slice->getMaxNumMergeCand() - 1)
              {
                bitsCand--;
              }
    
              // first round
              for (uint32_t intraCnt = 0; intraCnt < numTestIntraMode; intraCnt++)
              {
                pu.intraDir[0] = (intraCnt < 2) ? intraCnt : ((intraCnt == 2) ? HOR_IDX : VER_IDX);
    
                // fast 2
                if (mergeCnt > 0 && bestMHIntraMode != pu.intraDir[0])
                {
                  continue;
                }
                int narrowCase = PU::getNarrowShape(pu.lwidth(), pu.lheight());
                if (narrowCase == 1 && pu.intraDir[0] == HOR_IDX)
                {
                  continue;
                }
                if (narrowCase == 2 && pu.intraDir[0] == VER_IDX)
                {
                  continue;
                }
                // generate intrainter Y prediction
                if (mergeCnt == 0)
                {
    
                  m_pcIntraSearch->initIntraPatternChType( *pu.cu, pu.Y());
                  m_pcIntraSearch->predIntraAng(COMPONENT_Y, pu.cs->getPredBuf(pu).Y(), pu);
    
                  m_pcIntraSearch->switchBuffer(pu, COMPONENT_Y, pu.cs->getPredBuf(pu).Y(), m_pcIntraSearch->getPredictorPtr2(COMPONENT_Y, intraCnt));
                }
                pu.cs->getPredBuf(pu).copyFrom(acMergeBuffer[mergeCand]);
    
    Taoran Lu's avatar
    Taoran Lu committed
                if (pu.cs->slice->getReshapeInfo().getUseSliceReshaper() && m_pcReshape->getCTUFlag())
                {
                  pu.cs->getPredBuf(pu).Y().rspSignal(m_pcReshape->getFwdLUT());
                }
    
                m_pcIntraSearch->geneWeightedPred(COMPONENT_Y, pu.cs->getPredBuf(pu).Y(), pu, m_pcIntraSearch->getPredictorPtr2(COMPONENT_Y, intraCnt));
    
                // calculate cost
    
    Taoran Lu's avatar
    Taoran Lu committed
                if (pu.cs->slice->getReshapeInfo().getUseSliceReshaper() && m_pcReshape->getCTUFlag())
                {
                   pu.cs->getPredBuf(pu).Y().rspSignal(m_pcReshape->getInvLUT());
                }
    
                distParam.cur = pu.cs->getPredBuf(pu).Y();
                Distortion sadValue = distParam.distFunc(distParam);
    
    Taoran Lu's avatar
    Taoran Lu committed
                if (pu.cs->slice->getReshapeInfo().getUseSliceReshaper() && m_pcReshape->getCTUFlag())
                {
                  pu.cs->getPredBuf(pu).Y().rspSignal(m_pcReshape->getFwdLUT());
                }
    
                m_CABACEstimator->getCtx() = SubCtx(Ctx::MHIntraPredMode, ctxStartIntraMode);
                uint64_t fracModeBits = m_pcIntraSearch->xFracModeBitsIntra(pu, pu.intraDir[0], CHANNEL_TYPE_LUMA);
    
    #if JVET_N0324_REGULAR_MRG_FLAG
                double cost = (double)sadValue + (double)(bitsCand + 9) * sqrtLambdaForFirstPass + (double)fracModeBits * sqrtLambdaForFirstPassIntra;
    #else
    
                double cost = (double)sadValue + (double)(bitsCand + 1) * sqrtLambdaForFirstPass + (double)fracModeBits * sqrtLambdaForFirstPassIntra;
    
                insertPos = -1;
                updateDoubleCandList(mergeCand + MRG_MAX_NUM_CANDS + MMVD_ADD_NUM, cost, RdModeList, candCostList, RdModeList2, pu.intraDir[0], uiNumMrgSATDCand, &insertPos);
                if (insertPos != -1)
                {
                  for (int i = int(RdModeList.size()) - 1; i > insertPos; i--)
                  {
                    swap(acMergeTempBuffer[i - 1], acMergeTempBuffer[i]);
                  }
                  swap(singleMergeTempBuffer, acMergeTempBuffer[insertPos]);
                }
                // fast 2
                if (mergeCnt == 0 && cost < bestMHIntraCost)
                {
                  bestMHIntraMode = pu.intraDir[0];
                  bestMHIntraCost = cost;
                }
              }
            }
    
            pu.mhIntraFlag = false;
    
            m_CABACEstimator->getCtx() = ctxStart;
          }
    
    Frank Bossen's avatar
    Frank Bossen committed
    #if !JVET_MMVD_OFF_MACRO
    
    Seungsoo Jeong's avatar
    Seungsoo Jeong committed
    #if JVET_N0127_MMVD_SPS_FLAG 
          if ( pu.cs->sps->getUseMMVD() )
    
    #endif 
            cu.mmvdSkip = true;
    
    #if JVET_N0448_N0380