Skip to content
Snippets Groups Projects
IntraSearch.cpp 483 KiB
Newer Older
  • Learn to ignore specific revisions
  •   }
    
      if ((pu.lwidth() > 8 && pu.lheight() > 8))
      {
        // Sort MIP candidates by Hadamard cost
    
        const int transpOff = getNumModesMip( pu.Y() );
    
    Philipp Merkle's avatar
    Philipp Merkle committed
        static_vector<uint8_t, FAST_UDI_MAX_RDMODE_NUM> sortedMipModes(0);
        static_vector<double, FAST_UDI_MAX_RDMODE_NUM> sortedMipCost(0);
    
        for( uint8_t mode : { 0, 1, 2 } )
    
        {
          uint8_t candMode = mode + uint8_t((mipHadCost[mode + transpOff] < mipHadCost[mode]) ? transpOff : 0);
          updateCandList(candMode, mipHadCost[candMode], sortedMipModes, sortedMipCost, 3);
        }
    
        // Append MIP mode to RD mode list
    
        const int modeListSize = int(tempRdModeList.size());
    
        for (int idx = 0; idx < 3; idx++)
        {
    
          const bool     isTransposed = (sortedMipModes[idx] >= transpOff ? true : false);
          const uint32_t mipIdx       = (isTransposed ? sortedMipModes[idx] - transpOff : sortedMipModes[idx]);
          const ModeInfo mipMode( true, isTransposed, 0, NOT_INTRA_SUBPARTITIONS, mipIdx );
    
          bool alreadyIncluded = false;
    
          for (int modeListIdx = 0; modeListIdx < modeListSize; modeListIdx++)
    
          {
            if (tempRdModeList[modeListIdx] == mipMode)
            {
              alreadyIncluded = true;
              break;
            }
          }
    
          if (!alreadyIncluded)
          {
    
    fan wang's avatar
    fan wang committed
    #if JVET_AB0155_SGPM
            updateCandList(mipMode, sortedMipCost[idx], tempRdModeList, tempCandCostList, tempRdModeList.size() + 1);
    #else
    
            tempRdModeList.push_back(mipMode);
            tempCandCostList.push_back(0);
    
    fan wang's avatar
    fan wang committed
    #endif
    
    #if JVET_AB0157_TMRL
    
      if (pu.lwidth() > 8 && pu.lheight() > 8 && CU::allowTmrl(*pu.cu))
    
      {
        // Sort TMRL candidates by cost.
        static_vector<uint8_t, FAST_UDI_MAX_RDMODE_NUM> sortedTmrlModes(0);
    
        static_vector<double, FAST_UDI_MAX_RDMODE_NUM>  sortedTmrlCost(0);
    
        for (uint8_t tmrlListIdx = 0; tmrlListIdx < MRL_LIST_SIZE; tmrlListIdx++)
        {
          CHECK(tmrlCostList[tmrlListIdx] == MAX_DOUBLE, "tmrlCostList is not filled.");
          updateCandList(tmrlListIdx, tmrlCostList[tmrlListIdx], sortedTmrlModes, sortedTmrlCost, 3);
        }
    
        // Append TMRL mode to RD mode list
        const int modeListSize = int(tempRdModeList.size());
        for (int idx = 0; idx < 3; idx++)
        {
    
          const uint8_t  tmrlListIdx = sortedTmrlModes[idx];
    
          const ModeInfo tmrlMode(false, false, tmrlListIdx + MAX_REF_LINE_IDX, NOT_INTRA_SUBPARTITIONS, 0);
    
          bool           alreadyIncluded = false;
    
          for (int modeListIdx = 0; modeListIdx < modeListSize; modeListIdx++)
          {
            if (tempRdModeList[modeListIdx] == tmrlMode)
            {
              alreadyIncluded = true;
              break;
            }
          }
    
          if (!alreadyIncluded)
          {
    
            const auto numRd = tempRdModeList.size() + 1;
            updateCandList(tmrlMode, sortedTmrlCost[idx], tempRdModeList, tempCandCostList, numRd);
    
    #if JVET_AC0105_DIRECTIONAL_PLANAR
    
      static_vector<uint8_t, 2> sortedDirPlanarModes(2);
      static_vector<double, 2>  sortedDirPlanarCost(2);
    
      for(int i = 0; i < 2; i++ )
    
      {
        sortedDirPlanarModes[i] = 0;
    
        sortedDirPlanarCost[i]  = MAX_DOUBLE;
    
      for (uint8_t idx = 0; idx < 2; idx++)
    
        CHECK(dirPlanarCostList[idx] == MAX_DOUBLE, "dirPlanarCostList is not filled.");
        updateCandList(idx, dirPlanarCostList[idx], sortedDirPlanarModes, sortedDirPlanarCost, 2);
    
      }
    
      const int modeListSize = int(tempRdModeList.size());
      for (int idx = 0; idx < 2; idx++)
      {
        const uint8_t  dirPlanarListIdx = sortedDirPlanarModes[idx];
        const ModeInfo dirPlanarMode(false, false, 0, NOT_INTRA_SUBPARTITIONS,
                                      dirPlanarListIdx == 0 ? PL_HOR_IDX : PL_VER_IDX);
        bool alreadyIncluded = false;
        for (int modeListIdx = 0; modeListIdx < modeListSize; modeListIdx++)
        {
          if (tempRdModeList[modeListIdx] == dirPlanarMode)
          {
            alreadyIncluded = true;
            break;
          }
        }
    
        if (!alreadyIncluded)
        {
          const auto numRd = tempRdModeList.size() + 1;
          updateCandList(dirPlanarMode, sortedDirPlanarCost[idx], tempRdModeList, tempCandCostList, numRd);
          break;
        }
      }
    #endif
    
    
      candModeList = tempRdModeList;
      candCostList = tempCandCostList;
      numModesForFullRD = int(candModeList.size());
    }
    
    // It decides which modes from the ISP lists can be full RD tested
    void IntraSearch::xGetNextISPMode(ModeInfo& modeInfo, const ModeInfo* lastMode, const Size cuSize)
    {
      static_vector<ModeInfo, FAST_UDI_MAX_RDMODE_NUM>* rdModeLists[2] = { &m_ispCandListHor, &m_ispCandListVer };
    
    
      const int curIspLfnstIdx = m_curIspLfnstIdx;
      if (curIspLfnstIdx >= NUM_LFNST_NUM_PER_SET)
      {
        //All lfnst indices have been checked
        return;
      }
    
    
      ISPType nextISPcandSplitType;
    
      auto& ispTestedModes = m_ispTestedModes[curIspLfnstIdx];
    
      const bool horSplitIsTerminated = ispTestedModes.splitIsFinished[HOR_INTRA_SUBPARTITIONS - 1];
      const bool verSplitIsTerminated = ispTestedModes.splitIsFinished[VER_INTRA_SUBPARTITIONS - 1];
      if (!horSplitIsTerminated && !verSplitIsTerminated)
    
      {
        nextISPcandSplitType = !lastMode ? HOR_INTRA_SUBPARTITIONS : lastMode->ispMod == HOR_INTRA_SUBPARTITIONS ? VER_INTRA_SUBPARTITIONS : HOR_INTRA_SUBPARTITIONS;
      }
    
      else if (!horSplitIsTerminated && verSplitIsTerminated)
    
      {
        nextISPcandSplitType = HOR_INTRA_SUBPARTITIONS;
      }
    
      else if (horSplitIsTerminated && !verSplitIsTerminated)
    
      {
        nextISPcandSplitType = VER_INTRA_SUBPARTITIONS;
      }
      else
      {
    
        xFinishISPModes();
    
        return;   // no more modes will be tested
      }
    
    
      int maxNumSubPartitions = ispTestedModes.numTotalParts[nextISPcandSplitType - 1];
    
      // We try to break the split here for lfnst > 0 according to the first mode
    
      if (curIspLfnstIdx > 0 && ispTestedModes.numTestedModes[nextISPcandSplitType - 1] == 1)
      {
        int firstModeThisSplit = ispTestedModes.getTestedIntraMode(nextISPcandSplitType, 0);
        int numSubPartsFirstModeThisSplit = ispTestedModes.getNumCompletedSubParts(nextISPcandSplitType, firstModeThisSplit);
        CHECK(numSubPartsFirstModeThisSplit < 0, "wrong number of subpartitions!");
        bool stopThisSplit = false;
        bool stopThisSplitAllLfnsts = false;
        if (numSubPartsFirstModeThisSplit < maxNumSubPartitions)
        {
          stopThisSplit = true;
          if (m_pcEncCfg->getUseFastISP() && curIspLfnstIdx == 1 && numSubPartsFirstModeThisSplit < maxNumSubPartitions - 1)
          {
            stopThisSplitAllLfnsts = true;
          }
        }
    
        if (stopThisSplit)
        {
          ispTestedModes.splitIsFinished[nextISPcandSplitType - 1] = true;
          if (curIspLfnstIdx == 1 && stopThisSplitAllLfnsts)
          {
            m_ispTestedModes[2].splitIsFinished[nextISPcandSplitType - 1] = true;
          }
          return;
        }
      }
    
    
      // We try to break the split here for lfnst = 0 or all lfnst indices according to the first two modes
    
      if (curIspLfnstIdx == 0 && ispTestedModes.numTestedModes[nextISPcandSplitType - 1] == 2)
    
      {
        // Split stop criteria after checking the performance of previously tested intra modes
        const int thresholdSplit1 = maxNumSubPartitions;
    
        bool stopThisSplitForAllLFNSTs = false;
        const int thresholdSplit1ForAllLFNSTs = maxNumSubPartitions - 1;
    
        int mode1 = ispTestedModes.getTestedIntraMode((ISPType)nextISPcandSplitType, 0);
    
    Jie's avatar
    Jie committed
    #if ENABLE_DIMD && !JVET_V0087_DIMD_NO_ISP
    
    Vadim Seregin's avatar
    Vadim Seregin committed
        mode1 = ( mode1 == DC_IDX || mode1 == DIMD_IDX ) ? -1 : mode1;
    #else
    
        mode1 = mode1 == DC_IDX ? -1 : mode1;
    
    Vadim Seregin's avatar
    Vadim Seregin committed
    #endif
    
        int numSubPartsBestMode1 = mode1 != -1 ? ispTestedModes.getNumCompletedSubParts((ISPType)nextISPcandSplitType, mode1) : -1;
        int mode2 = ispTestedModes.getTestedIntraMode((ISPType)nextISPcandSplitType, 1);
    
    Jie's avatar
    Jie committed
    #if ENABLE_DIMD && !JVET_V0087_DIMD_NO_ISP
    
    Vadim Seregin's avatar
    Vadim Seregin committed
        mode2 = ( mode2 == DC_IDX || mode2 == DIMD_IDX ) ? -1 : mode2;
    #else
    
        mode2 = mode2 == DC_IDX ? -1 : mode2;
    
    Vadim Seregin's avatar
    Vadim Seregin committed
    #endif
    
        int numSubPartsBestMode2 = mode2 != -1 ? ispTestedModes.getNumCompletedSubParts((ISPType)nextISPcandSplitType, mode2) : -1;
    
    
        // 1) The 2 most promising modes do not reach a certain number of sub-partitions
        if (numSubPartsBestMode1 != -1 && numSubPartsBestMode2 != -1)
        {
          if (numSubPartsBestMode1 < thresholdSplit1 && numSubPartsBestMode2 < thresholdSplit1)
          {
    
            if (curIspLfnstIdx == 0 && numSubPartsBestMode1 < thresholdSplit1ForAllLFNSTs && numSubPartsBestMode2 < thresholdSplit1ForAllLFNSTs)
            {
              stopThisSplitForAllLFNSTs = true;
            }
    
          else
          {
            //we stop also if the cost is MAX_DOUBLE for both modes
            double mode1Cost = ispTestedModes.getRDCost(nextISPcandSplitType, mode1);
            double mode2Cost = ispTestedModes.getRDCost(nextISPcandSplitType, mode2);
            if (!(mode1Cost < MAX_DOUBLE || mode2Cost < MAX_DOUBLE))
            {
              stopThisSplit = true;
            }
          }
    
          // 2) One split type may be discarded by comparing the number of sub-partitions of the best angle modes of both splits
    
          ISPType otherSplit = nextISPcandSplitType == HOR_INTRA_SUBPARTITIONS ? VER_INTRA_SUBPARTITIONS : HOR_INTRA_SUBPARTITIONS;
          int  numSubPartsBestMode2OtherSplit = mode2 != -1 ? ispTestedModes.getNumCompletedSubParts(otherSplit, mode2) : -1;
    
          if (numSubPartsBestMode2OtherSplit != -1 && numSubPartsBestMode2 != -1 && ispTestedModes.bestSplitSoFar != nextISPcandSplitType)
    
            if (numSubPartsBestMode2OtherSplit > numSubPartsBestMode2)
    
            // both have the same number of subpartitions
            else if (numSubPartsBestMode2OtherSplit == numSubPartsBestMode2)
    
              // both have the maximum number of subpartitions, so it compares RD costs to decide
              if (numSubPartsBestMode2OtherSplit == maxNumSubPartitions)
    
                double rdCostBestMode2ThisSplit = ispTestedModes.getRDCost(nextISPcandSplitType, mode2);
                double rdCostBestMode2OtherSplit = ispTestedModes.getRDCost(otherSplit, mode2);
                double threshold = 1.3;
                if (rdCostBestMode2ThisSplit == MAX_DOUBLE || rdCostBestMode2OtherSplit < rdCostBestMode2ThisSplit * threshold)
                {
                  stopThisSplit = true;
                }
              }
              else // none of them reached the maximum number of subpartitions with the best angle modes, so it compares the results with the the planar mode
              {
                int  numSubPartsBestMode1OtherSplit = mode1 != -1 ? ispTestedModes.getNumCompletedSubParts(otherSplit, mode1) : -1;
                if (numSubPartsBestMode1OtherSplit != -1 && numSubPartsBestMode1 != -1 && numSubPartsBestMode1OtherSplit > numSubPartsBestMode1)
                {
                  stopThisSplit = true;
                }
    
          ispTestedModes.splitIsFinished[nextISPcandSplitType - 1] = true;
    
          if (stopThisSplitForAllLFNSTs)
          {
            for (int lfnstIdx = 1; lfnstIdx < NUM_LFNST_NUM_PER_SET; lfnstIdx++)
            {
              m_ispTestedModes[lfnstIdx].splitIsFinished[nextISPcandSplitType - 1] = true;
            }
          }
    
          return;
        }
      }
    
      // Now a new mode is retrieved from the list and it has to be decided whether it should be tested or not
    
      if (ispTestedModes.candIndexInList[nextISPcandSplitType - 1] < rdModeLists[nextISPcandSplitType - 1]->size())
    
        ModeInfo candidate = rdModeLists[nextISPcandSplitType - 1]->at(ispTestedModes.candIndexInList[nextISPcandSplitType - 1]);
        ispTestedModes.candIndexInList[nextISPcandSplitType - 1]++;
    
    
        // extra modes are only tested if ISP has won so far
    
        if (ispTestedModes.candIndexInList[nextISPcandSplitType - 1] > ispTestedModes.numOrigModesToTest)
    
          if (ispTestedModes.bestSplitSoFar != candidate.ispMod || ispTestedModes.bestModeSoFar == PLANAR_IDX)
    
            ispTestedModes.splitIsFinished[nextISPcandSplitType - 1] = true;
    
            return;
          }
        }
    
        bool testCandidate = true;
    
        // we look for a reference mode that has already been tested within the window and decide to test the new one according to the reference mode costs
    
    Vadim Seregin's avatar
    Vadim Seregin committed
        if (
    
    Jie's avatar
    Jie committed
    #if ENABLE_DIMD && !JVET_V0087_DIMD_NO_ISP
    
    Vadim Seregin's avatar
    Vadim Seregin committed
          candidate.modeId != DIMD_IDX &&
    
    Keming Cao's avatar
    Keming Cao committed
    #endif
    #if JVET_W0123_TIMD_FUSION
          candidate.modeId != TIMD_IDX &&
    
    #endif
    #if JVET_AC0105_DIRECTIONAL_PLANAR
          candidate.modeId != PL_HOR_IDX && candidate.modeId != PL_VER_IDX &&
    
    Vadim Seregin's avatar
    Vadim Seregin committed
    #endif
          maxNumSubPartitions > 2 && (curIspLfnstIdx > 0 || (candidate.modeId >= DC_IDX && ispTestedModes.numTestedModes[nextISPcandSplitType - 1] >= 2)))
    
          int       refLfnstIdx = -1;
    
          const int angWindowSize = 5;
          int       numSubPartsLeftMode, numSubPartsRightMode, numSubPartsRefMode, leftIntraMode = -1, rightIntraMode = -1;
          int       windowSize = candidate.modeId > DC_IDX ? angWindowSize : 1;
    
          int       numSamples = cuSize.width << floorLog2(cuSize.height);
    
          int       numSubPartsLimit = numSamples >= 256 ? maxNumSubPartitions - 1 : 2;
    
    
          xFindAlreadyTestedNearbyIntraModes(curIspLfnstIdx, (int)candidate.modeId, &refLfnstIdx, &leftIntraMode, &rightIntraMode, (ISPType)candidate.ispMod, windowSize);
    
          if (refLfnstIdx != -1 && refLfnstIdx != curIspLfnstIdx)
          {
            CHECK(leftIntraMode != candidate.modeId || rightIntraMode != candidate.modeId, "wrong intra mode and lfnstIdx values!");
            numSubPartsRefMode = m_ispTestedModes[refLfnstIdx].getNumCompletedSubParts((ISPType)candidate.ispMod, candidate.modeId);
            CHECK(numSubPartsRefMode <= 0, "Wrong value of the number of subpartitions completed!");
          }
          else
          {
            numSubPartsLeftMode = leftIntraMode != -1 ? ispTestedModes.getNumCompletedSubParts((ISPType)candidate.ispMod, leftIntraMode) : -1;
            numSubPartsRightMode = rightIntraMode != -1 ? ispTestedModes.getNumCompletedSubParts((ISPType)candidate.ispMod, rightIntraMode) : -1;
    
            numSubPartsRefMode = std::max(numSubPartsLeftMode, numSubPartsRightMode);
          }
    
    
          if (numSubPartsRefMode > 0)
          {
            // The mode was found. Now we check the condition
            testCandidate = numSubPartsRefMode > numSubPartsLimit;
          }
        }
    
        if (testCandidate)
        {
          modeInfo = candidate;
        }
      }
    
      else
      {
        //the end of the list was reached, so the split is invalidated
        ispTestedModes.splitIsFinished[nextISPcandSplitType - 1] = true;
      }
    
    void IntraSearch::xFindAlreadyTestedNearbyIntraModes(int lfnstIdx, int currentIntraMode, int* refLfnstIdx, int* leftIntraMode, int* rightIntraMode, ISPType ispOption, int windowSize)
    
    {
      bool leftModeFound = false, rightModeFound = false;
      *leftIntraMode = -1;
      *rightIntraMode = -1;
    
      *refLfnstIdx = -1;
    
      const unsigned st = ispOption - 1;
    
    
      //first we check if the exact intra mode was already tested for another lfnstIdx value
      if (lfnstIdx > 0)
      {
        bool sameIntraModeFound = false;
    
    Vadim Seregin's avatar
    Vadim Seregin committed
        if (lfnstIdx == 2 && m_ispTestedModes[1].modeHasBeenTested[st].count(currentIntraMode) )
    
        {
          sameIntraModeFound = true;
          *refLfnstIdx = 1;
        }
    
    Vadim Seregin's avatar
    Vadim Seregin committed
        else if (m_ispTestedModes[0].modeHasBeenTested[st].count(currentIntraMode) )
    
        {
          sameIntraModeFound = true;
          *refLfnstIdx = 0;
        }
    
        if (sameIntraModeFound)
        {
          *leftIntraMode = currentIntraMode;
          *rightIntraMode = currentIntraMode;
          return;
        }
      }
    
    
      //The mode has not been checked for another lfnstIdx value, so now we look for a similar mode within a window using the same lfnstIdx
    
      for (int k = 1; k <= windowSize; k++)
      {
        int off = currentIntraMode - 2 - k;
        int leftMode = (off < 0) ? NUM_LUMA_MODE + off : currentIntraMode - k;
        int rightMode = currentIntraMode > DC_IDX ? (((int)currentIntraMode - 2 + k) % 65) + 2 : PLANAR_IDX;
    
    
    Vadim Seregin's avatar
    Vadim Seregin committed
        leftModeFound  = leftMode  != (int)currentIntraMode ? m_ispTestedModes[lfnstIdx].modeHasBeenTested[st].count( leftMode ) > 0 : false;
        rightModeFound = rightMode != (int)currentIntraMode ? m_ispTestedModes[lfnstIdx].modeHasBeenTested[st].count( rightMode ) > 0 : false;
    
        if (leftModeFound || rightModeFound)
        {
          *leftIntraMode = leftModeFound ? leftMode : -1;
          *rightIntraMode = rightModeFound ? rightMode : -1;
    
          *refLfnstIdx = lfnstIdx;
    
    //It prepares the list of potential intra modes candidates that will be tested using RD costs
    bool IntraSearch::xSortISPCandList(double bestCostSoFar, double bestNonISPCost, ModeInfo bestNonISPMode)
    
      int bestISPModeInRelCU = -1;
      m_modeCtrl->setStopNonDCT2Transforms(false);
    
      if (m_pcEncCfg->getUseFastISP())
      {
        //we check if the ISP tests can be cancelled
        double thSkipISP = 1.4;
        if (bestNonISPCost > bestCostSoFar * thSkipISP)
        {
          for (int splitIdx = 0; splitIdx < NUM_INTRA_SUBPARTITIONS_MODES - 1; splitIdx++)
          {
            for (int j = 0; j < NUM_LFNST_NUM_PER_SET; j++)
            {
              m_ispTestedModes[j].splitIsFinished[splitIdx] = true;
            }
          }
          return false;
        }
        if (!updateISPStatusFromRelCU(bestNonISPCost, bestNonISPMode, bestISPModeInRelCU))
        {
          return false;
        }
      }
    
    
      for (int k = 0; k < m_ispCandListHor.size(); k++)
      {
        m_ispCandListHor.at(k).ispMod = HOR_INTRA_SUBPARTITIONS; //we set the correct ISP split type value
      }
    
      auto origHadList = m_ispCandListHor;   // save the original hadamard list of regular intra
      bool modeIsInList[NUM_LUMA_MODE] = { false };
    
      m_ispCandListHor.clear();
      m_ispCandListVer.clear();
    
      // we sort the normal intra modes according to their full RD costs
    
    Vadim Seregin's avatar
    Vadim Seregin committed
      std::stable_sort(m_regIntraRDListWithCosts.begin(), m_regIntraRDListWithCosts.end(), ModeInfoWithCost::compareModeInfoWithCost);
    
    
      // we get the best angle from the regular intra list
      int bestNormalIntraAngle = -1;
      for (int modeIdx = 0; modeIdx < m_regIntraRDListWithCosts.size(); modeIdx++)
      {
        if (bestNormalIntraAngle == -1 && m_regIntraRDListWithCosts.at(modeIdx).modeId > DC_IDX)
        {
          bestNormalIntraAngle = m_regIntraRDListWithCosts.at(modeIdx).modeId;
          break;
        }
      }
    
      int mode1 = PLANAR_IDX;
      int mode2 = bestNormalIntraAngle;
    
      ModeInfo refMode = origHadList.at(0);
      auto* destListPtr = &m_ispCandListHor;
    
    #if JVET_AG0058_EIP
      bool isAeip = bestISPModeInRelCU >= EIP_IDX && bestISPModeInRelCU < (EIP_IDX + std::max(NUM_DERIVED_EIP, MAX_MERGE_EIP));
      if (m_pcEncCfg->getUseFastISP() && bestISPModeInRelCU != -1 && !isAeip) //RelCU intra mode
    #else
    
      if (m_pcEncCfg->getUseFastISP() && bestISPModeInRelCU != -1) //RelCU intra mode
    
        destListPtr->push_back(
          ModeInfo(refMode.mipFlg, refMode.mipTrFlg, refMode.mRefId, refMode.ispMod, bestISPModeInRelCU));
    
        modeIsInList[bestISPModeInRelCU] = true;
      }
      // Planar
    
    #if JVET_W0103_INTRA_MTS
      // push planar later when FastISP is on.
      if (!m_pcEncCfg->getUseFastISP() && !modeIsInList[mode1])
    #else
    
      if (!modeIsInList[mode1])
    
      {
        destListPtr->push_back(ModeInfo(refMode.mipFlg, refMode.mipTrFlg, refMode.mRefId, refMode.ispMod, mode1));
        modeIsInList[mode1] = true;
      }
    
      // Best angle in regular intra
      if (mode2 != -1 && !modeIsInList[mode2])
      {
        destListPtr->push_back(ModeInfo(refMode.mipFlg, refMode.mipTrFlg, refMode.mRefId, refMode.ispMod, mode2));
        modeIsInList[mode2] = true;
      }
      // Remaining regular intra modes that were full RD tested (except DC, which is added after the angles from regular intra)
      int dcModeIndex = -1;
      for (int remModeIdx = 0; remModeIdx < m_regIntraRDListWithCosts.size(); remModeIdx++)
      {
        int currentMode = m_regIntraRDListWithCosts.at(remModeIdx).modeId;
        if (currentMode != mode1 && currentMode != mode2 && !modeIsInList[currentMode])
        {
          if (currentMode > DC_IDX)
          {
            destListPtr->push_back(ModeInfo(refMode.mipFlg, refMode.mipTrFlg, refMode.mRefId, refMode.ispMod, currentMode));
            modeIsInList[currentMode] = true;
          }
          else if (currentMode == DC_IDX)
          {
            dcModeIndex = remModeIdx;
          }
        }
      }
    
    #if JVET_W0103_INTRA_MTS
      // Planar (after angular modes when FastISP is on)
      if (!modeIsInList[mode1])
      {
        destListPtr->push_back(ModeInfo(refMode.mipFlg, refMode.mipTrFlg, refMode.mRefId, refMode.ispMod, mode1));
        modeIsInList[mode1] = true;
      }
    #endif
    
      // DC is added after the angles from regular intra
      if (dcModeIndex != -1 && !modeIsInList[DC_IDX])
      {
        destListPtr->push_back(ModeInfo(refMode.mipFlg, refMode.mipTrFlg, refMode.mRefId, refMode.ispMod, DC_IDX));
        modeIsInList[DC_IDX] = true;
      }
    
      // We add extra candidates to the list that will only be tested if ISP is likely to win
      for (int j = 0; j < NUM_LFNST_NUM_PER_SET; j++)
      {
        m_ispTestedModes[j].numOrigModesToTest = (int)destListPtr->size();
    
    #if JVET_W0103_INTRA_MTS
        if (m_pcEncCfg->getUseFastISP() && m_numModesISPRDO != -1 && destListPtr->size() > m_numModesISPRDO)
        {
          m_ispTestedModes[j].numOrigModesToTest = m_numModesISPRDO;
        }
    #endif
    
      const int addedModesFromHadList = 3;
      int       newModesAdded = 0;
    
      for (int k = 0; k < origHadList.size(); k++)
      {
        if (newModesAdded == addedModesFromHadList)
        {
          break;
        }
    
    Vadim Seregin's avatar
    Vadim Seregin committed
        if (
    
    Jie's avatar
    Jie committed
    #if ENABLE_DIMD && !JVET_V0087_DIMD_NO_ISP
    
    Vadim Seregin's avatar
    Vadim Seregin committed
          origHadList.at(k).modeId == DIMD_IDX ||
    
    Keming Cao's avatar
    Keming Cao committed
    #endif
    #if JVET_W0123_TIMD_FUSION
          origHadList.at(k).modeId == TIMD_IDX ||
    
    #endif
    #if JVET_AC0105_DIRECTIONAL_PLANAR
          origHadList.at(k).modeId == PL_HOR_IDX || origHadList.at(k).modeId == PL_VER_IDX ||
    
    Vadim Seregin's avatar
    Vadim Seregin committed
    #endif
    	!modeIsInList[origHadList.at(k).modeId])
    
          destListPtr->push_back( ModeInfo( refMode.mipFlg, refMode.mipTrFlg, refMode.mRefId, refMode.ispMod, origHadList.at(k).modeId ) );
    
      if (m_pcEncCfg->getUseFastISP() && bestISPModeInRelCU != -1)
      {
        destListPtr->resize(1);
      }
    
    
      // Copy modes to other split-type list
      m_ispCandListVer = m_ispCandListHor;
      for (int i = 0; i < m_ispCandListVer.size(); i++)
      {
        m_ispCandListVer[i].ispMod = VER_INTRA_SUBPARTITIONS;
      }
    
      // Reset the tested modes information to 0
    
      for (int j = 0; j < NUM_LFNST_NUM_PER_SET; j++)
      {
        for (int i = 0; i < m_ispCandListHor.size(); i++)
        {
          m_ispTestedModes[j].clearISPModeInfo(m_ispCandListHor[i].modeId);
        }
      }
      return true;
    }
    
    void IntraSearch::xSortISPCandListLFNST()
    {
      //It resorts the list of intra mode candidates for lfnstIdx > 0 by checking the RD costs for lfnstIdx = 0
      ISPTestedModesInfo& ispTestedModesRef = m_ispTestedModes[0];
      for (int splitIdx = 0; splitIdx < NUM_INTRA_SUBPARTITIONS_MODES - 1; splitIdx++)
      {
        ISPType ispMode = splitIdx ? VER_INTRA_SUBPARTITIONS : HOR_INTRA_SUBPARTITIONS;
        if (!m_ispTestedModes[m_curIspLfnstIdx].splitIsFinished[splitIdx] && ispTestedModesRef.testedModes[splitIdx].size() > 1)
        {
          auto& candList   = ispMode == HOR_INTRA_SUBPARTITIONS ? m_ispCandListHor : m_ispCandListVer;
          int bestModeId   = candList[1].modeId > DC_IDX ? candList[1].modeId : -1;
          int bestSubParts = candList[1].modeId > DC_IDX ? ispTestedModesRef.getNumCompletedSubParts(ispMode, bestModeId) : -1;
          double bestCost  = candList[1].modeId > DC_IDX ? ispTestedModesRef.getRDCost(ispMode, bestModeId) : MAX_DOUBLE;
          for (int i = 0; i < candList.size(); i++)
          {
    
    Jie's avatar
    Jie committed
    #if ENABLE_DIMD && !JVET_V0087_DIMD_NO_ISP
    
    Vadim Seregin's avatar
    Vadim Seregin committed
            if( candList[i].modeId == DIMD_IDX )
            {
              continue;
            }
    
    Keming Cao's avatar
    Keming Cao committed
    #endif
    #if JVET_W0123_TIMD_FUSION
            if( candList[i].modeId == TIMD_IDX )
            {
              continue;
            }
    
    #endif
    #if JVET_AC0105_DIRECTIONAL_PLANAR
            if (candList[i].modeId == PL_HOR_IDX || candList[i].modeId == PL_VER_IDX)
            {
              continue;
            }
    
    Vadim Seregin's avatar
    Vadim Seregin committed
    #endif
    
            const int candSubParts = ispTestedModesRef.getNumCompletedSubParts(ispMode, candList[i].modeId);
            const double candCost = ispTestedModesRef.getRDCost(ispMode, candList[i].modeId);
            if (candSubParts > bestSubParts || candCost < bestCost)
            {
              bestModeId = candList[i].modeId;
              bestCost = candCost;
              bestSubParts = candSubParts;
            }
          }
    
          if (bestModeId != -1)
          {
            if (bestModeId != candList[0].modeId)
            {
              auto prevMode = candList[0];
              candList[0].modeId = bestModeId;
              for (int i = 1; i < candList.size(); i++)
              {
                auto nextMode = candList[i];
                candList[i] = prevMode;
                if (nextMode.modeId == bestModeId)
                {
                  break;
                }
                prevMode = nextMode;
              }
            }
          }
        }
      }
    
    bool IntraSearch::updateISPStatusFromRelCU( double bestNonISPCostCurrCu, ModeInfo bestNonISPModeCurrCu, int& bestISPModeInRelCU )
    {
      //It compares the data of a related CU with the current CU to cancel or reduce the ISP tests
      bestISPModeInRelCU = -1;
      if (m_modeCtrl->getRelatedCuIsValid())
      {
        double bestNonISPCostRelCU = m_modeCtrl->getBestDCT2NonISPCostRelCU();
        double costRatio           = bestNonISPCostCurrCu / bestNonISPCostRelCU;
        bool   bestModeRelCuIsMip  = (m_modeCtrl->getIspPredModeValRelCU() >> 5) & 0x1;
        bool   bestModeCurrCuIsMip = bestNonISPModeCurrCu.mipFlg;
        int    relatedCuIntraMode  = m_modeCtrl->getIspPredModeValRelCU() >> 9;
    
    #if JVET_AG0058_EIP
        bool   bestModeRelCuIsAeip = (m_modeCtrl->getIspPredModeValRelCU() >> 8) & 0x1;
        bool   bestModeCurrCuIsAeip = (bestNonISPModeCurrCu.modeId >= EIP_IDX) && (bestNonISPModeCurrCu.modeId < EIP_IDX + std::max(NUM_DERIVED_EIP, MAX_MERGE_EIP));
        if (bestModeRelCuIsAeip)
        {
          relatedCuIntraMode += EIP_IDX;
        }
        bool   isSameTypeOfMode = (bestModeRelCuIsMip && bestModeCurrCuIsMip) // Mip
          || (bestModeRelCuIsAeip && bestModeCurrCuIsAeip) // aeip
          || (!bestModeRelCuIsAeip && !bestModeCurrCuIsAeip && !bestModeRelCuIsMip && !bestModeCurrCuIsMip);
        bool   bothModesAreAngular = bestNonISPModeCurrCu.modeId > DC_IDX && relatedCuIntraMode > DC_IDX
          && bestNonISPModeCurrCu.modeId < EIP_IDX&& relatedCuIntraMode < EIP_IDX;
        bool   modesAreComparable = isSameTypeOfMode && (bestModeCurrCuIsMip || bestModeCurrCuIsAeip ||
          bestNonISPModeCurrCu.modeId == relatedCuIntraMode || (bothModesAreAngular && abs(relatedCuIntraMode - (int)bestNonISPModeCurrCu.modeId) <= 5));
    #else
        bool   isSameTypeOfMode = (bestModeRelCuIsMip && bestModeCurrCuIsMip) || (!bestModeRelCuIsMip && !bestModeCurrCuIsMip);
    
        bool   bothModesAreAngular = bestNonISPModeCurrCu.modeId > DC_IDX && relatedCuIntraMode > DC_IDX;
    
        bool   modesAreComparable = isSameTypeOfMode && (bestModeCurrCuIsMip || bestNonISPModeCurrCu.modeId == relatedCuIntraMode || (bothModesAreAngular && abs(relatedCuIntraMode - (int)bestNonISPModeCurrCu.modeId) <= 5));
    #endif
    
        int    status              = m_modeCtrl->getIspPredModeValRelCU();
    
        if ((status & 0x3) == 0x3) //ISP was not selected in the relCU
        {
          double bestNonDCT2Cost = m_modeCtrl->getBestNonDCT2Cost();
          double ratioWithNonDCT2 = bestNonDCT2Cost / bestNonISPCostRelCU;
          double margin = ratioWithNonDCT2 < 0.95 ? 0.2 : 0.1;
    
          if (costRatio > 1 - margin && costRatio < 1 + margin && modesAreComparable)
          {
            for (int lfnstVal = 0; lfnstVal < NUM_LFNST_NUM_PER_SET; lfnstVal++)
            {
              m_ispTestedModes[lfnstVal].splitIsFinished[HOR_INTRA_SUBPARTITIONS - 1] = true;
              m_ispTestedModes[lfnstVal].splitIsFinished[VER_INTRA_SUBPARTITIONS - 1] = true;
            }
            return false;
          }
        }
        else if ((status & 0x3) == 0x1) //ISP was selected in the relCU
        {
          double margin = 0.05;
    
          if (costRatio > 1 - margin && costRatio < 1 + margin && modesAreComparable)
          {
            int  ispSplitIdx = (m_modeCtrl->getIspPredModeValRelCU() >> 2) & 0x1;
            bool lfnstIdxIsNot0 = (bool)((m_modeCtrl->getIspPredModeValRelCU() >> 3) & 0x1);
            bool lfnstIdxIs2 = (bool)((m_modeCtrl->getIspPredModeValRelCU() >> 4) & 0x1);
            int  lfnstIdx = !lfnstIdxIsNot0 ? 0 : lfnstIdxIs2 ? 2 : 1;
            bestISPModeInRelCU = (int)m_modeCtrl->getBestISPIntraModeRelCU();
    
            for (int splitIdx = 0; splitIdx < NUM_INTRA_SUBPARTITIONS_MODES - 1; splitIdx++)
            {
              for (int lfnstVal = 0; lfnstVal < NUM_LFNST_NUM_PER_SET; lfnstVal++)
              {
                if (lfnstVal == lfnstIdx && splitIdx == ispSplitIdx)
                {
                  continue;
                }
                m_ispTestedModes[lfnstVal].splitIsFinished[splitIdx] = true;
              }
            }
    
            bool stopNonDCT2Transforms = (bool)((m_modeCtrl->getIspPredModeValRelCU() >> 6) & 0x1);
            m_modeCtrl->setStopNonDCT2Transforms(stopNonDCT2Transforms);
          }
        }
        else
        {
          THROW("Wrong ISP relCU status");
        }
      }
    
      return true;
    }
    
    void IntraSearch::xFinishISPModes()
    {
    
      //Continue to the next lfnst index
    
      m_curIspLfnstIdx++;
    
      if (m_curIspLfnstIdx < NUM_LFNST_NUM_PER_SET)
      {
        //Check if LFNST is applicable
        if (m_curIspLfnstIdx == 1)
        {
          bool canTestLFNST = false;
          for (int lfnstIdx = 1; lfnstIdx < NUM_LFNST_NUM_PER_SET; lfnstIdx++)
          {
            canTestLFNST |= !m_ispTestedModes[lfnstIdx].splitIsFinished[HOR_INTRA_SUBPARTITIONS - 1] || !m_ispTestedModes[lfnstIdx].splitIsFinished[VER_INTRA_SUBPARTITIONS - 1];
          }
          if (canTestLFNST)
          {
            //Construct the intra modes candidates list for the lfnst > 0 cases
            xSortISPCandListLFNST();
          }
        }
      }
    }
    
    void IntraSearch::setLumaIntraPredIdx(PredictionUnit& pu)
    {
    #if SECONDARY_MPM
      const int numMPMs = NUM_PRIMARY_MOST_PROBABLE_MODES + NUM_SECONDARY_MOST_PROBABLE_MODES;
    #else
      const int numMPMs = NUM_MOST_PROBABLE_MODES;
    #endif
      int pred_idx = numMPMs;
      for (int idx = 0; idx < numMPMs; idx++)
      {
        if (pu.intraDir[0] == m_intraMPM[idx])
        {
          pred_idx = idx;
          break;
        }
      }
    #if SECONDARY_MPM
      if (pred_idx < NUM_PRIMARY_MOST_PROBABLE_MODES)
      {
        pu.mpmFlag = true;
        pu.secondMpmFlag = false;
      }
      else if (pred_idx < numMPMs)
      {
        pu.mpmFlag = false;
        pu.secondMpmFlag = true;
      }
      else
      {
        pu.mpmFlag = false;
        pu.secondMpmFlag = false;
        pred_idx = NUM_NON_MPM_MODES;
        for (int idx = 0; idx < NUM_NON_MPM_MODES; idx++)
        {
          if (pu.intraDir[0] == m_intraNonMPM[idx])
          {
            pred_idx = idx;
            break;
          }
        }
    
      }
    #else
      if (mpm_idx < NUM_MOST_PROBABLE_MODES)
      {
        pu.mpmFlag = true;
      }
      else
      {
        std::sort(mpmPred, mpmPred + numMPMs);
        int pred_idx = pu.intraDir[0];
        for (int idx = numMPMs - 1; idx >= 0; idx--)
        {
          if (pred_idx > mpmPred[idx])
          {
            pred_idx--;
          }
        }
        CHECK(pred_idx >= 64, "Incorrect mode");
      }
    #endif
      pu.ipredIdx = pred_idx;
    }