Skip to content
Snippets Groups Projects
IntraSearch.cpp 238 KiB
Newer Older
  • Learn to ignore specific revisions
  •   if (jointCbCr)
      {
        ruiDist += m_pcRdCost->getDistPart(crOrgResi, crResi, sps.getBitDepth(toChannelType(COMPONENT_Cr)), COMPONENT_Cr, DF_SSE);
      }
    }
    
    
    bool IntraSearch::xIntraCodingLumaISP(CodingStructure& cs, Partitioner& partitioner, const double bestCostSoFar)
    {
      int               subTuCounter = 0;
      const CodingUnit& cu = *cs.getCU(partitioner.currArea().lumaPos(), partitioner.chType);
      bool              earlySkipISP = false;
    
    Santiago de Luxán Hernández's avatar
    Santiago de Luxán Hernández committed
      bool              splitCbfLuma = false;
    
      const PartSplit   ispType = CU::getISPType(cu, COMPONENT_Y);
    
      cs.cost = 0;
    
      partitioner.splitCurrArea(ispType, cs);
    
    
      CUCtx cuCtx;
      cuCtx.isDQPCoded = true;
      cuCtx.isChromaQpAdjCoded = true;
    
    
      do   // subpartitions loop
      {
        uint32_t   numSig = 0;
        Distortion singleDistTmpLuma = 0;
        uint64_t   singleTmpFracBits = 0;
        double     singleCostTmp = 0;
    
        TransformUnit& tu = cs.addTU(CS::getArea(cs, partitioner.currArea(), partitioner.chType), partitioner.chType);
        tu.depth = partitioner.currTrDepth;
    
        // Encode TU
    
        xIntraCodingTUBlock(tu, COMPONENT_Y, singleDistTmpLuma, 0, &numSig);
    
    Vadim Seregin's avatar
    Vadim Seregin committed
    
    #if SIGN_PREDICTION
        cs.picture->getRecoBuf( partitioner.currArea() ).copyFrom( cs.getRecoBuf( partitioner.currArea() ) );
    #endif
    
    
        if (singleDistTmpLuma == MAX_INT)   // all zero CBF skip
        {
          earlySkipISP = true;
          partitioner.exitCurrSplit();
          cs.cost = MAX_DOUBLE;
          return false;
        }
    
    
        if (m_pcRdCost->calcRdCost(cs.fracBits, cs.dist + singleDistTmpLuma) > bestCostSoFar)
    
          // The accumulated cost + distortion is already larger than the best cost so far, so it is not necessary to
          // calculate the rate
          earlySkipISP = true;
        }
        else
        {
          singleTmpFracBits = xGetIntraFracBitsQT(cs, partitioner, true, false, subTuCounter, ispType, &cuCtx);
    
        singleCostTmp = m_pcRdCost->calcRdCost(singleTmpFracBits, singleDistTmpLuma);
    
    
        cs.cost += singleCostTmp;
        cs.dist += singleDistTmpLuma;
        cs.fracBits += singleTmpFracBits;
    
        subTuCounter++;
    
    
    Santiago de Luxán Hernández's avatar
    Santiago de Luxán Hernández committed
        splitCbfLuma |= TU::getCbfAtDepth(*cs.getTU(partitioner.currArea().lumaPos(), partitioner.chType, subTuCounter - 1), COMPONENT_Y, partitioner.currTrDepth);
    
        int nSubPartitions = m_ispTestedModes[cu.lfnstIdx].numTotalParts[cu.ispMode - 1];
    
        if (subTuCounter < nSubPartitions)
        {
          // exit condition if the accumulated cost is already larger than the best cost so far (no impact in RD performance)
          if (cs.cost > bestCostSoFar)
          {
            earlySkipISP = true;
            break;
          }
          else if (subTuCounter < nSubPartitions)
          {
            // more restrictive exit condition
            double threshold = nSubPartitions == 2 ? 0.95 : subTuCounter == 1 ? 0.83 : 0.91;
            if (subTuCounter < nSubPartitions && cs.cost > bestCostSoFar * threshold)
            {
              earlySkipISP = true;
              break;
            }
          }
        }
      } while (partitioner.nextPart(cs));   // subpartitions loop
    
      partitioner.exitCurrSplit();
      const UnitArea& currArea = partitioner.currArea();
      const uint32_t  currDepth = partitioner.currTrDepth;
    
      if (earlySkipISP)
      {
        cs.cost = MAX_DOUBLE;
      }
      else
      {
        cs.cost = m_pcRdCost->calcRdCost(cs.fracBits, cs.dist);
        // The cost check is necessary here again to avoid superfluous operations if the maximum number of coded subpartitions was reached and yet ISP did not win
        if (cs.cost < bestCostSoFar)
        {
          cs.setDecomp(cu.Y());
          cs.picture->getRecoBuf(currArea.Y()).copyFrom(cs.getRecoBuf(currArea.Y()));
    
          for (auto& ptu : cs.tus)
          {
            if (currArea.Y().contains(ptu->Y()))
            {
    
    Santiago de Luxán Hernández's avatar
    Santiago de Luxán Hernández committed
              TU::setCbfAtDepth(*ptu, COMPONENT_Y, currDepth, splitCbfLuma ? 1 : 0);
    
    bool IntraSearch::xRecurIntraCodingLumaQT( CodingStructure &cs, Partitioner &partitioner, const double bestCostSoFar, const int subTuIdx, const PartSplit ispType, const bool ispIsCurrentWinner, bool mtsCheckRangeFlag, int mtsFirstCheckId, int mtsLastCheckId, bool moreProbMTSIdxFirst )
    
    {
            int   subTuCounter = subTuIdx;
      const UnitArea &currArea = partitioner.currArea();
      const CodingUnit     &cu = *cs.getCU( currArea.lumaPos(), partitioner.chType );
            bool  earlySkipISP = false;
    
    Tung Nguyen's avatar
    Tung Nguyen committed
      uint32_t currDepth       = partitioner.currTrDepth;
    
      const SPS &sps           = *cs.sps;
    
      bool bCheckFull          = true;
      bool bCheckSplit         = false;
    
    Karsten Suehring's avatar
    Karsten Suehring committed
      bCheckFull               = !partitioner.canSplit( TU_MAX_TR_SPLIT, cs );
      bCheckSplit              = partitioner.canSplit( TU_MAX_TR_SPLIT, cs );
    
      const Slice           &slice = *cs.slice;
    
      if( cu.ispMode )
      {
        bCheckSplit = partitioner.canSplit( ispType, cs );
        bCheckFull = !bCheckSplit;
      }
    
      double     dSingleCost                        = MAX_DOUBLE;
      Distortion uiSingleDistLuma                   = 0;
      uint64_t   singleFracBits                     = 0;
    
      bool       checkTransformSkip                 = sps.getTransformSkipEnabledFlag();
    
      int        bestModeId[ MAX_NUM_COMPONENT ]    = { 0, 0, 0 };
      uint8_t    nNumTransformCands                 = cu.mtsFlag ? 4 : 1;
      uint8_t    numTransformIndexCands             = nNumTransformCands;
    
    
      const TempCtx ctxStart  ( m_CtxCache, m_CABACEstimator->getCtx() );
      TempCtx       ctxBest   ( m_CtxCache );
    
      CodingStructure *csSplit = nullptr;
      CodingStructure *csFull  = nullptr;
    
    
      CUCtx cuCtx;
      cuCtx.isDQPCoded = true;
      cuCtx.isChromaQpAdjCoded = true;
    
    
      bool validReturnFull = false;
    
    
      if( bCheckFull )
      {
        csFull->cost = 0.0;
    
        TransformUnit &tu = csFull->addTU( CS::getArea( *csFull, currArea, partitioner.chType ), partitioner.chType );
        tu.depth = currDepth;
    
    
        const bool tsAllowed  = TU::isTSAllowed( tu, COMPONENT_Y );
    
        const bool mtsAllowed = CU::isMTSAllowed( cu, COMPONENT_Y );
    
        std::vector<TrMode> trModes;
    
        if( sps.getUseLFNST() )
        {
          checkTransformSkip &= tsAllowed;
          checkTransformSkip &= !cu.mtsFlag;
          checkTransformSkip &= !cu.lfnstIdx;
    
          if( !cu.mtsFlag && checkTransformSkip )
          {
            trModes.push_back( TrMode( 0, true ) ); //DCT2
            trModes.push_back( TrMode( 1, true ) ); //TS
          }
        }
        else
        {
          nNumTransformCands = 1 + ( tsAllowed ? 1 : 0 ) + ( mtsAllowed ? 4 : 0 ); // DCT + TS + 4 MTS = 6 tests
    
          if (m_pcEncCfg->getCostMode() == COST_LOSSLESS_CODING && slice.isLossless())
    
          {
            nNumTransformCands = 1;
            CHECK(!tsAllowed && !cu.bdpcmMode, "transform skip should be enabled for LS");
            if (cu.bdpcmMode)
            {
              trModes.push_back(TrMode(0, true));
            }
            else
            {
              trModes.push_back(TrMode(1, true));
            }
          }
          else
          {
    
            trModes.push_back(TrMode(0, true));   // DCT2
            if (tsAllowed)
    
              trModes.push_back(TrMode(1, true));
            }
            if (mtsAllowed)
            {
              for (int i = 2; i < 6; i++)
              {
                trModes.push_back(TrMode(i, true));
              }
    
        }
    
        CHECK( !tu.Y().valid(), "Invalid TU" );
    
    
        CodingStructure &saveCS = *m_pSaveCS[0];
    
        TransformUnit *tmpTU = nullptr;
    
        Distortion singleDistTmpLuma = 0;
        uint64_t     singleTmpFracBits = 0;
        double     singleCostTmp     = 0;
    
        int        firstCheckId      = ( sps.getUseLFNST() && mtsCheckRangeFlag && cu.mtsFlag ) ? mtsFirstCheckId : 0;
    
        //we add the MTS candidates to the loop. TransformSkip will still be the last one to be checked (when modeId == lastCheckId) as long as checkTransformSkip is true
        int        lastCheckId       = sps.getUseLFNST() ? ( ( mtsCheckRangeFlag && cu.mtsFlag ) ? ( mtsLastCheckId + ( int ) checkTransformSkip ) : ( numTransformIndexCands - ( firstCheckId + 1 ) + ( int ) checkTransformSkip ) ) :
                                       trModes[ nNumTransformCands - 1 ].first;
        bool isNotOnlyOneMode        = sps.getUseLFNST() ? lastCheckId != firstCheckId : nNumTransformCands != 1;
    
    
        if( isNotOnlyOneMode )
        {
          saveCS.pcv     = cs.pcv;
          saveCS.picture = cs.picture;
          saveCS.area.repositionTo(cs.area);
          saveCS.clearTUs();
          tmpTU = &saveCS.addTU(currArea, partitioner.chType);
        }
    
    
        bool    cbfBestMode      = false;
        bool    cbfBestModeValid = false;
    
    Tung Nguyen's avatar
    Tung Nguyen committed
        bool    cbfDCT2  = true;
    
        double threshold = m_pcEncCfg->getUseFastISP() && !cu.ispMode && ispIsCurrentWinner && nNumTransformCands > 1 ? 1 + 1.4 / sqrt( cu.lwidth() * cu.lheight() ) : 1;
    
        for( int modeId = firstCheckId; modeId <= ( sps.getUseLFNST() ? lastCheckId : ( nNumTransformCands - 1 ) ); modeId++ )
        {
          uint8_t transformIndex = modeId;
    
          if( sps.getUseLFNST() )
          {
            if( ( transformIndex < lastCheckId ) || ( ( transformIndex == lastCheckId ) && !checkTransformSkip ) ) //we avoid this if the mode is transformSkip
            {
              // Skip checking other transform candidates if zero CBF is encountered and it is the best transform so far
              if( m_pcEncCfg->getUseFastLFNST() && transformIndex && !cbfBestMode && cbfBestModeValid )
              {
                continue;
              }
            }
          }
          else
          {
    
            if (!(m_pcEncCfg->getCostMode() == COST_LOSSLESS_CODING && slice.isLossless()))
    
              if (!cbfDCT2 || (m_pcEncCfg->getUseTransformSkipFast() && bestModeId[COMPONENT_Y] == MTS_SKIP))
              {
                break;
              }
              if (!trModes[modeId].second)
              {
                continue;
              }
              // we compare the DCT-II cost against the best ISP cost so far (except for TS)
              if (m_pcEncCfg->getUseFastISP() && !cu.ispMode && ispIsCurrentWinner && trModes[modeId].first != MTS_DCT2_DCT2
                  && (trModes[modeId].first != MTS_SKIP || !tsAllowed) && bestDCT2cost > bestCostSoFar * threshold)
              {
                continue;
              }
    
            tu.mtsIdx[COMPONENT_Y] = trModes[modeId].first;
    
          if ((modeId != firstCheckId) && isNotOnlyOneMode)
          {
            m_CABACEstimator->getCtx() = ctxStart;
          }
    
          int default0Save1Load2 = 0;
          singleDistTmpLuma = 0;
    
    
          if( modeId == firstCheckId && ( sps.getUseLFNST() ? ( modeId != lastCheckId ) : ( nNumTransformCands > 1 ) ) )
    
            if( sps.getUseLFNST() && !cbfBestModeValid )
            {
              default0Save1Load2 = 1;
            }
            else
            {
              default0Save1Load2 = 2;
            }
    
          if( sps.getUseLFNST() )
          {
            if( cu.mtsFlag )
            {
              if( moreProbMTSIdxFirst )
              {
                const ChannelType     chType      = toChannelType( COMPONENT_Y );
                const CompArea&       area        = tu.blocks[ COMPONENT_Y ];
                const PredictionUnit& pu          = *cs.getPU( area.pos(), chType );
                uint32_t              uiIntraMode = pu.intraDir[ chType ];
    
                if( transformIndex == 1 )
                {
    
                  tu.mtsIdx[COMPONENT_Y] = (uiIntraMode < 34) ? MTS_DST7_DCT8 : MTS_DCT8_DST7;
    
                else if( transformIndex == 2 )
    
                  tu.mtsIdx[COMPONENT_Y] = (uiIntraMode < 34) ? MTS_DCT8_DST7 : MTS_DST7_DCT8;
    
                  tu.mtsIdx[COMPONENT_Y] = MTS_DST7_DST7 + transformIndex;
    
                tu.mtsIdx[COMPONENT_Y] = MTS_DST7_DST7 + transformIndex;
    
              tu.mtsIdx[COMPONENT_Y] = transformIndex;
    
            }
    
            if( !cu.mtsFlag && checkTransformSkip )
            {
    
              xIntraCodingTUBlock( tu, COMPONENT_Y, singleDistTmpLuma, default0Save1Load2, &numSig, modeId == 0 ? &trModes : nullptr, true );
    
              if( modeId == 0 )
              {
                for( int i = 0; i < 2; i++ )
                {
                  if( trModes[ i ].second )
                  {
                    lastCheckId = trModes[ i ].first;
                  }
                }
              }
            }
            else
            {
    
              xIntraCodingTUBlock( tu, COMPONENT_Y, singleDistTmpLuma, default0Save1Load2, &numSig );
    
              xIntraCodingTUBlock( tu, COMPONENT_Y, 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, singleDistTmpLuma, default0Save1Load2, &numSig );
    
          cuCtx.mtsLastScanPos = false;
    
          if( ( sps.getUseLFNST() ? ( modeId == lastCheckId && modeId != 0 && checkTransformSkip ) : ( trModes[ modeId ].first != 0 ) ) && !TU::getCbfAtDepth( tu, COMPONENT_Y, currDepth ) )
    
          {
            //In order not to code TS flag when cbf is zero, the case for TS with cbf being zero is forbidden.
    
            if (m_pcEncCfg->getCostMode() != COST_LOSSLESS_CODING || !slice.isLossless())
    
            {
              singleCostTmp = MAX_DOUBLE;
            }
    
            else
            {
              singleTmpFracBits = xGetIntraFracBitsQT(*csFull, partitioner, true, false, subTuCounter, ispType, &cuCtx);
              singleCostTmp = m_pcRdCost->calcRdCost(singleTmpFracBits, singleDistTmpLuma);
            }
    
            if( cu.ispMode && m_pcRdCost->calcRdCost( csFull->fracBits, csFull->dist + singleDistTmpLuma ) > bestCostSoFar )
            {
              earlySkipISP = true;
            }
            else
            {
    
              singleTmpFracBits = xGetIntraFracBitsQT( *csFull, partitioner, true, false, subTuCounter, ispType, &cuCtx );
    
            if (tu.mtsIdx[COMPONENT_Y] > MTS_SKIP)
            {
              if (!cuCtx.mtsLastScanPos)
              {
                singleCostTmp = MAX_DOUBLE;
              }
              else
              {
                singleCostTmp = m_pcRdCost->calcRdCost(singleTmpFracBits, singleDistTmpLuma);
              }
            }
            else
    
            {
              singleCostTmp = m_pcRdCost->calcRdCost(singleTmpFracBits, singleDistTmpLuma);
            }
    
          if ( !cu.ispMode && nNumTransformCands > 1 && modeId == firstCheckId )
          {
            bestDCT2cost = singleCostTmp;
          }
    
    
          if (singleCostTmp < dSingleCost)
          {
            dSingleCost       = singleCostTmp;
            uiSingleDistLuma  = singleDistTmpLuma;
            singleFracBits    = singleTmpFracBits;
    
    
    Tung Nguyen's avatar
    Tung Nguyen committed
            {
    
              bestModeId[ COMPONENT_Y ] = modeId;
              cbfBestMode = TU::getCbfAtDepth( tu, COMPONENT_Y, currDepth );
              cbfBestModeValid = true;
              validReturnFull = true;
    
    Tung Nguyen's avatar
    Tung Nguyen committed
            }
    
            else
            {
              bestModeId[ COMPONENT_Y ] = trModes[ modeId ].first;
              if( trModes[ modeId ].first == 0 )
              {
                cbfDCT2 = TU::getCbfAtDepth( tu, COMPONENT_Y, currDepth );
              }
            }
    
    
            if( bestModeId[COMPONENT_Y] != lastCheckId )
            {
              saveCS.getPredBuf( tu.Y() ).copyFrom( csFull->getPredBuf( tu.Y() ) );
              saveCS.getRecoBuf( tu.Y() ).copyFrom( csFull->getRecoBuf( tu.Y() ) );
    
    
              {
                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 )
    
          csFull->cost = MAX_DOUBLE;
    
            ctxBest = m_CABACEstimator->getCtx();
    
        }
        else
        {
          if( bestModeId[COMPONENT_Y] != lastCheckId )
          {
            csFull->getPredBuf( tu.Y() ).copyFrom( saveCS.getPredBuf( tu.Y() ) );
            csFull->getRecoBuf( tu.Y() ).copyFrom( saveCS.getRecoBuf( tu.Y() ) );
    
            {
              csFull->getResiBuf   ( tu.Y() ).copyFrom( saveCS.getResiBuf   ( tu.Y() ) );
              csFull->getOrgResiBuf( tu.Y() ).copyFrom( saveCS.getOrgResiBuf( tu.Y() ) );
            }
    
            tu.copyComponentFrom( *tmpTU, COMPONENT_Y );
    
            if( !bCheckSplit )
            {
              m_CABACEstimator->getCtx() = ctxBest;
            }
          }
          else if( bCheckSplit )
    
            ctxBest = m_CABACEstimator->getCtx();
    
          csFull->cost     += dSingleCost;
          csFull->dist     += uiSingleDistLuma;
          csFull->fracBits += singleFracBits;
        }
    
      bool validReturnSplit = false;
    
      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's avatar
    Karsten Suehring committed
        if( partitioner.canSplit( TU_MAX_TR_SPLIT, cs ) )
    
        if( cu.ispMode )
        {
          partitioner.splitCurrArea( ispType, *csSplit );
        }
    
          bool tmpValidReturnSplit = xRecurIntraCodingLumaQT( *csSplit, partitioner, bestCostSoFar, subTuCounter, ispType, false, mtsCheckRangeFlag, mtsFirstCheckId, mtsLastCheckId );
    
    Vadim Seregin's avatar
    Vadim Seregin committed
    #if SIGN_PREDICTION
          cs.picture->getRecoBuf(  partitioner.currArea()  ).copyFrom( cs.getRecoBuf( partitioner.currArea() ) );
    #endif
    
          subTuCounter += subTuCounter != -1 ? 1 : 0;
          if( sps.getUseLFNST() && !tmpValidReturnSplit )
          {
            splitIsSelected = false;
            break;
          }
    
          if( !cu.ispMode )
          {
            csSplit->setDecomp( partitioner.currArea().Y() );
          }
          else if( CU::isISPFirst( cu, partitioner.currArea().Y(), COMPONENT_Y ) )
          {
            csSplit->setDecomp( cu.Y() );
          }
    
          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());
    
              double threshold = nSubPartitions == 2 ? 0.95 : subTuCounter == 1 ? 0.83 : 0.91;
    
              if( subTuCounter < nSubPartitions && csSplit->cost > bestCostSoFar*threshold )
              {
                earlySkipISP    = true;
                splitIsSelected = false;
                break;
              }
            }
          }
    
        } 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;
    
    
          cuCtx.violatesLfnstConstrained[CHANNEL_TYPE_LUMA] = false;
          cuCtx.violatesLfnstConstrained[CHANNEL_TYPE_CHROMA] = false;
          cuCtx.lfnstLastScanPos = false;
          cuCtx.violatesMtsCoeffConstraint = false;
    
          cuCtx.mtsLastScanPos = false;
    
          csSplit->fracBits = xGetIntraFracBitsQT( *csSplit, partitioner, true, false, cu.ispMode ? 0 : -1, ispType, &cuCtx );
    
    
          //--- update cost ---
          csSplit->cost     = m_pcRdCost->calcRdCost(csSplit->fracBits, csSplit->dist);
    
        if( !sps.getUseLFNST() || validReturnFull || validReturnSplit )
    
          // 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()));
    
          if( cu.ispMode && earlySkipISP )
          {
            cs.cost = MAX_DOUBLE;
          }
          else
          {
            cs.cost = m_pcRdCost->calcRdCost( cs.fracBits, cs.dist );
            retVal = true;
          }
    
    bool IntraSearch::xRecurIntraCodingACTQT(CodingStructure &cs, Partitioner &partitioner, bool mtsCheckRangeFlag, int mtsFirstCheckId, int mtsLastCheckId, bool moreProbMTSIdxFirst)
    {
      const UnitArea &currArea = partitioner.currArea();
      uint32_t       currDepth = partitioner.currTrDepth;
      const Slice    &slice = *cs.slice;
      const SPS      &sps = *cs.sps;
    
      bool bCheckFull = !partitioner.canSplit(TU_MAX_TR_SPLIT, cs);
      bool bCheckSplit = !bCheckFull;
    
      TempCtx ctxStart(m_CtxCache, m_CABACEstimator->getCtx());
      TempCtx ctxBest(m_CtxCache);
    
      CodingStructure *csSplit = nullptr;
      CodingStructure *csFull = nullptr;
      if (bCheckSplit)
      {
        csSplit = &cs;
      }
      else if (bCheckFull)
      {
        csFull = &cs;
      }
    
      bool validReturnFull = false;
    
      if (bCheckFull)
      {
        TransformUnit        &tu = csFull->addTU(CS::getArea(*csFull, currArea, partitioner.chType), partitioner.chType);
        tu.depth = currDepth;
        const CodingUnit     &cu = *csFull->getCU(tu.Y().pos(), CHANNEL_TYPE_LUMA);
        const PredictionUnit &pu = *csFull->getPU(tu.Y().pos(), CHANNEL_TYPE_LUMA);
        CHECK(!tu.Y().valid() || !tu.Cb().valid() || !tu.Cr().valid(), "Invalid TU");
        CHECK(tu.cu != &cu, "wrong CU fetch");
        CHECK(cu.ispMode, "adaptive color transform cannot be applied to ISP");
        CHECK(pu.intraDir[CHANNEL_TYPE_CHROMA] != DM_CHROMA_IDX, "chroma should use DM mode for adaptive color transform");
    
        // 1. intra prediction and forward color transform
    
        PelUnitBuf orgBuf = csFull->getOrgBuf(tu);
        PelUnitBuf predBuf = csFull->getPredBuf(tu);
        PelUnitBuf resiBuf = csFull->getResiBuf(tu);
        PelUnitBuf orgResiBuf = csFull->getOrgResiBuf(tu);
    
        bool doReshaping = (slice.getLmcsEnabledFlag() && slice.getPicHeader()->getLmcsChromaResidualScaleFlag() && (slice.isIntra() || m_pcReshape->getCTUFlag()) && (tu.blocks[COMPONENT_Cb].width * tu.blocks[COMPONENT_Cb].height > 4));
    
        if (doReshaping)
        {
          const Area      area = tu.Y().valid() ? tu.Y() : Area(recalcPosition(tu.chromaFormat, tu.chType, CHANNEL_TYPE_LUMA, tu.blocks[tu.chType].pos()), recalcSize(tu.chromaFormat, tu.chType, CHANNEL_TYPE_LUMA, tu.blocks[tu.chType].size()));
          const CompArea &areaY = CompArea(COMPONENT_Y, tu.chromaFormat, area);
          int             adj = m_pcReshape->calculateChromaAdjVpduNei(tu, areaY);
          tu.setChromaAdj(adj);
        }
    #endif
    
    
        for (int i = 0; i < getNumberValidComponents(tu.chromaFormat); i++)
        {
          ComponentID          compID = (ComponentID)i;
          const CompArea       &area = tu.blocks[compID];
          const ChannelType    chType = toChannelType(compID);
    
          PelBuf         piOrg = orgBuf.bufs[compID];
          PelBuf         piPred = predBuf.bufs[compID];
          PelBuf         piResi = resiBuf.bufs[compID];
    
          initIntraPatternChType(*tu.cu, area);
    
    Vadim Seregin's avatar
    Vadim Seregin committed
    #if JVET_V0130_INTRA_TMP
          if( PU::isTmp( pu, chType ) )
          {
            int foundCandiNum;
            m_pcTrQuant->getTargetTemplate( pu.cu, pu.lwidth(), pu.lheight() );
            m_pcTrQuant->candidateSearchIntra( pu.cu, pu.lwidth(), pu.lheight() );
            m_pcTrQuant->generateTMPrediction( piPred.buf, piPred.stride, pu.lwidth(), pu.lheight(), foundCandiNum );
            CHECK( foundCandiNum < 1, "" );
    
          }
          else if( PU::isMIP( pu, chType ) )
    
    Vadim Seregin's avatar
    Vadim Seregin committed
    #else
    
          if (PU::isMIP(pu, chType))
    
    Vadim Seregin's avatar
    Vadim Seregin committed
    #endif
    
            predIntraMip(compID, piPred, pu);
          }
          else
          {
            predIntraAng(compID, piPred, pu);
          }
    
          piResi.copyFrom(piOrg);
    
          if (slice.getLmcsEnabledFlag() && m_pcReshape->getCTUFlag() && compID == COMPONENT_Y)
    
          {
            CompArea tmpArea(COMPONENT_Y, area.chromaFormat, Position(0, 0), area.size());
            PelBuf   tmpPred = m_tmpStorageLCU.getBuf(tmpArea);
    
    Vadim Seregin's avatar
    Vadim Seregin committed
            piResi.rspSignal( piPred, m_pcReshape->getFwdLUT() );
    
            piResi.subtract(tmpPred);
          }
    
    #if JVET_S0234_ACT_CRS_FIX
          else if (doReshaping && (compID != COMPONENT_Y))
          {
            piResi.subtract(piPred);
            int cResScaleInv = tu.getChromaAdj();
            piResi.scaleSignal(cResScaleInv, 1, slice.clpRng(compID));
          }
    #endif
    
            piResi.subtract(piPred);
    
        resiBuf.colorSpaceConvert(orgResiBuf, true, cs.slice->clpRng(COMPONENT_Y));
    
        // 2. luma residual optimization
    
        double     dSingleCostLuma = MAX_DOUBLE;
        bool       checkTransformSkip = sps.getTransformSkipEnabledFlag();
        int        bestLumaModeId = 0;
        uint8_t    nNumTransformCands = cu.mtsFlag ? 4 : 1;
        uint8_t    numTransformIndexCands = nNumTransformCands;
    
        const bool tsAllowed = TU::isTSAllowed(tu, COMPONENT_Y);
    
        const bool mtsAllowed = CU::isMTSAllowed(cu, COMPONENT_Y);
    
        std::vector<TrMode> trModes;
    
        if (sps.getUseLFNST())
        {
          checkTransformSkip &= tsAllowed;
          checkTransformSkip &= !cu.mtsFlag;
          checkTransformSkip &= !cu.lfnstIdx;
    
          if (!cu.mtsFlag && checkTransformSkip)
          {
            trModes.push_back(TrMode(0, true)); //DCT2
            trModes.push_back(TrMode(1, true)); //TS
          }
        }
        else
        {
    
          if (m_pcEncCfg->getCostMode() == COST_LOSSLESS_CODING && slice.isLossless())
    
          {
            nNumTransformCands = 1;
            CHECK(!tsAllowed && !cu.bdpcmMode, "transform skip should be enabled for LS");
            if (cu.bdpcmMode)
            {
              trModes.push_back(TrMode(0, true));
            }
            else
            {
              trModes.push_back(TrMode(1, true));
            }
          }
          else
          {
    
            nNumTransformCands = 1 + (tsAllowed ? 1 : 0) + (mtsAllowed ? 4 : 0);   // DCT + TS + 4 MTS = 6 tests
    
            trModes.push_back(TrMode(0, true));   // DCT2
            if (tsAllowed)
    
              trModes.push_back(TrMode(1, true));
            }
            if (mtsAllowed)
            {
              for (int i = 2; i < 6; i++)
              {
                trModes.push_back(TrMode(i, true));
              }
    
        }
    
        CodingStructure &saveLumaCS = *m_pSaveCS[0];
        TransformUnit   *tmpTU = nullptr;
        Distortion      singleDistTmpLuma = 0;
        uint64_t        singleTmpFracBits = 0;
        double          singleCostTmp = 0;
        int             firstCheckId = (sps.getUseLFNST() && mtsCheckRangeFlag && cu.mtsFlag) ? mtsFirstCheckId : 0;
        int             lastCheckId = sps.getUseLFNST() ? ((mtsCheckRangeFlag && cu.mtsFlag) ? (mtsLastCheckId + (int)checkTransformSkip) : (numTransformIndexCands - (firstCheckId + 1) + (int)checkTransformSkip)) : trModes[nNumTransformCands - 1].first;
        bool            isNotOnlyOneMode = sps.getUseLFNST() ? lastCheckId != firstCheckId : nNumTransformCands != 1;
    
        if (isNotOnlyOneMode)
        {
          saveLumaCS.pcv = csFull->pcv;
          saveLumaCS.picture = csFull->picture;
          saveLumaCS.area.repositionTo(csFull->area);
          saveLumaCS.clearTUs();
          tmpTU = &saveLumaCS.addTU(currArea, partitioner.chType);
        }
    
        bool    cbfBestMode = false;
        bool    cbfBestModeValid = false;
        bool    cbfDCT2 = true;
    
        if (m_pcEncCfg->getCostMode() != COST_LOSSLESS_CODING || !slice.isLossless())
    
        m_pcRdCost->lambdaAdjustColorTrans(true, COMPONENT_Y);
    
        for (int modeId = firstCheckId; modeId <= ((m_pcEncCfg->getCostMode() == COST_LOSSLESS_CODING && slice.isLossless()) ? (nNumTransformCands - 1) : lastCheckId); modeId++)
    
        {
          uint8_t transformIndex = modeId;
          csFull->getResiBuf(tu.Y()).copyFrom(csFull->getOrgResiBuf(tu.Y()));
    
          m_CABACEstimator->getCtx() = ctxStart;
          m_CABACEstimator->resetBits();
    
          if (sps.getUseLFNST())
          {
            if ((transformIndex < lastCheckId) || ((transformIndex == lastCheckId) && !checkTransformSkip)) //we avoid this if the mode is transformSkip
            {
              // Skip checking other transform candidates if zero CBF is encountered and it is the best transform so far
              if (m_pcEncCfg->getUseFastLFNST() && transformIndex && !cbfBestMode && cbfBestModeValid)
              {
                continue;
              }
            }
          }
          else
          {
    
            if (!(m_pcEncCfg->getCostMode() == COST_LOSSLESS_CODING && slice.isLossless()))
    
              if (!cbfDCT2 || (m_pcEncCfg->getUseTransformSkipFast() && bestLumaModeId == 1))
              {
                break;
              }
              if (!trModes[modeId].second)
              {
                continue;
              }
    
            tu.mtsIdx[COMPONENT_Y] = trModes[modeId].first;
          }
    
          singleDistTmpLuma = 0;
          if (sps.getUseLFNST())
          {
            if (cu.mtsFlag)
            {
              if (moreProbMTSIdxFirst)
              {
                uint32_t uiIntraMode = pu.intraDir[CHANNEL_TYPE_LUMA];
    
                if (transformIndex == 1)
                {
                  tu.mtsIdx[COMPONENT_Y] = (uiIntraMode < 34) ? MTS_DST7_DCT8 : MTS_DCT8_DST7;
                }
                else if (transformIndex == 2)
                {
                  tu.mtsIdx[COMPONENT_Y] = (uiIntraMode < 34) ? MTS_DCT8_DST7 : MTS_DST7_DCT8;
                }
                else
                {
                  tu.mtsIdx[COMPONENT_Y] = MTS_DST7_DST7 + transformIndex;
                }
              }
              else
              {
                tu.mtsIdx[COMPONENT_Y] = MTS_DST7_DST7 + transformIndex;
              }
            }
            else
            {
              tu.mtsIdx[COMPONENT_Y] = transformIndex;
            }
    
            if (!cu.mtsFlag && checkTransformSkip)
            {
              xIntraCodingACTTUBlock(tu, COMPONENT_Y, singleDistTmpLuma, modeId == 0 ? &trModes : nullptr, true);
              if (modeId == 0)
              {
                for (int i = 0; i < 2; i++)
                {
                  if (trModes[i].second)
                  {
                    lastCheckId = trModes[i].first;
                  }
                }
              }
            }
            else
            {
              xIntraCodingACTTUBlock(tu, COMPONENT_Y, singleDistTmpLuma);
            }
          }
          else
          {
            if (nNumTransformCands > 1)
            {
              xIntraCodingACTTUBlock(tu, COMPONENT_Y, singleDistTmpLuma, modeId == 0 ? &trModes : nullptr, true);
              if (modeId == 0)
              {
                for (int i = 0; i < nNumTransformCands; i++)
                {
                  if (trModes[i].second)
                  {
                    lastCheckId = trModes[i].first;
                  }
                }
              }
            }
            else
            {
              xIntraCodingACTTUBlock(tu, COMPONENT_Y, singleDistTmpLuma);
            }
          }
    
    
          CUCtx cuCtx;
          cuCtx.isDQPCoded = true;
          cuCtx.isChromaQpAdjCoded = true;
    
          //----- determine rate and r-d cost -----
          if ((sps.getUseLFNST() ? (modeId == lastCheckId && modeId != 0 && checkTransformSkip) : (trModes[modeId].first != 0)) && !TU::getCbfAtDepth(tu, COMPONENT_Y, currDepth))
          {
            //In order not to code TS flag when cbf is zero, the case for TS with cbf being zero is forbidden.
    
            if (m_pcEncCfg->getCostMode() != COST_LOSSLESS_CODING || !slice.isLossless())
    
            singleCostTmp = MAX_DOUBLE;
    
            else
            {
              singleTmpFracBits = xGetIntraFracBitsQT(*csFull, partitioner, true, false, -1, TU_NO_ISP);
              singleCostTmp = m_pcRdCost->calcRdCost(singleTmpFracBits, singleDistTmpLuma, false);
            }
    
            singleTmpFracBits = xGetIntraFracBitsQT(*csFull, partitioner, true, false, -1, TU_NO_ISP, &cuCtx);
    
            if (tu.mtsIdx[COMPONENT_Y] > MTS_SKIP)
            {
              if (!cuCtx.mtsLastScanPos)
              {
                singleCostTmp = MAX_DOUBLE;
              }
              else
              {
    
                singleCostTmp = m_pcRdCost->calcRdCost(singleTmpFracBits, singleDistTmpLuma, false);
    
            {
              singleCostTmp = m_pcRdCost->calcRdCost(singleTmpFracBits, singleDistTmpLuma, false);
            }
    
          }
    
          if (singleCostTmp < dSingleCostLuma)
          {
            dSingleCostLuma = singleCostTmp;
            validReturnFull = true;
    
            if (sps.getUseLFNST())
            {
              bestLumaModeId = modeId;
              cbfBestMode = TU::getCbfAtDepth(tu, COMPONENT_Y, currDepth);
              cbfBestModeValid = true;
            }
            else
            {
              bestLumaModeId = trModes[modeId].first;