Skip to content
Snippets Groups Projects
IntraSearch.cpp 309 KiB
Newer Older
  • Learn to ignore specific revisions
  •               saveCS.getPredBuf(crArea).copyFrom(cs.getPredBuf(crArea));
                  if (keepResi)
                  {
                    saveCS.getResiBuf(cbArea).copyFrom(cs.getResiBuf(cbArea));
                    saveCS.getResiBuf(crArea).copyFrom(cs.getResiBuf(crArea));
                  }
                  saveCS.getRecoBuf(cbArea).copyFrom(cs.getRecoBuf(cbArea));
                  saveCS.getRecoBuf(crArea).copyFrom(cs.getRecoBuf(crArea));
    
                  tmpTU.copyComponentFrom(currTU, COMPONENT_Cb);
                  tmpTU.copyComponentFrom(currTU, COMPONENT_Cr);
    
                  ctxBest = m_CABACEstimator->getCtx();
                }
    
          // Retrieve the best CU data (unless it was the very last one tested)
          {
    #if KEEP_PRED_AND_RESI_SIGNALS
            cs.getPredBuf   (cbArea).copyFrom(saveCS.getPredBuf   (cbArea));
            cs.getOrgResiBuf(cbArea).copyFrom(saveCS.getOrgResiBuf(cbArea));
            cs.getPredBuf   (crArea).copyFrom(saveCS.getPredBuf   (crArea));
            cs.getOrgResiBuf(crArea).copyFrom(saveCS.getOrgResiBuf(crArea));
    #endif
            cs.getPredBuf   (cbArea).copyFrom(saveCS.getPredBuf   (cbArea));
            cs.getPredBuf   (crArea).copyFrom(saveCS.getPredBuf   (crArea));
    
            if( keepResi )
            {
              cs.getResiBuf (cbArea).copyFrom(saveCS.getResiBuf   (cbArea));
              cs.getResiBuf (crArea).copyFrom(saveCS.getResiBuf   (crArea));
            }
            cs.getRecoBuf   (cbArea).copyFrom(saveCS.getRecoBuf   (cbArea));
            cs.getRecoBuf   (crArea).copyFrom(saveCS.getRecoBuf   (crArea));
    
            currTU.copyComponentFrom(tmpTU, COMPONENT_Cb);
            currTU.copyComponentFrom(tmpTU, COMPONENT_Cr);
    
            m_CABACEstimator->getCtx() = ctxBest;
          }
    
          // Copy results to the picture structures
    
    Seungwook Hong's avatar
    Seungwook Hong committed
    #if JVET_Z0118_GDR
          cs.updateReconMotIPM(cbArea);
    #else
    
          cs.picture->getRecoBuf(cbArea).copyFrom(cs.getRecoBuf(cbArea));
    
    Seungwook Hong's avatar
    Seungwook Hong committed
    #endif
    
    #if JVET_Z0118_GDR
          cs.updateReconMotIPM(crArea);
    #else
    
          cs.picture->getRecoBuf(crArea).copyFrom(cs.getRecoBuf(crArea));
    
    Seungwook Hong's avatar
    Seungwook Hong committed
    #endif
    
          cs.picture->getPredBuf(cbArea).copyFrom(cs.getPredBuf(cbArea));
          cs.picture->getPredBuf(crArea).copyFrom(cs.getPredBuf(crArea));
    
          cbfs.cbf(COMPONENT_Cb) = TU::getCbf(currTU, COMPONENT_Cb);
          cbfs.cbf(COMPONENT_Cr) = TU::getCbf(currTU, COMPONENT_Cr);
    
          currTU.jointCbCr = ( (cbfs.cbf(COMPONENT_Cb) + cbfs.cbf(COMPONENT_Cr)) ? bestJointCbCr : 0 );
    
          cs.dist         += bestDistCbCr;
    
        }
      }
      else
      {
        unsigned    numValidTBlocks   = ::getNumberValidTBlocks( *cs.pcv );
        ChromaCbfs  SplitCbfs         ( false );
    
        if( partitioner.canSplit( TU_MAX_TR_SPLIT, cs ) )
        {
          partitioner.splitCurrArea( TU_MAX_TR_SPLIT, cs );
        }
    
        else if( currTU.cu->ispMode )
        {
          partitioner.splitCurrArea( ispType, cs );
        }
    
          ChromaCbfs subCbfs = xRecurIntraChromaCodingQT( cs, partitioner, bestCostSoFar, ispType );
    
    
          for( uint32_t ch = COMPONENT_Cb; ch < numValidTBlocks; ch++ )
          {
            const ComponentID compID = ComponentID( ch );
            SplitCbfs.cbf( compID ) |= subCbfs.cbf( compID );
          }
        } while( partitioner.nextPart( cs ) );
    
        partitioner.exitCurrSplit();
    
    
        if( lumaUsesISP && cs.dist == MAX_UINT )
        {
          return cbfs;
        }
    
        cbfs.Cb |= SplitCbfs.Cb;
        cbfs.Cr |= SplitCbfs.Cr;
    
        if (!lumaUsesISP)
        {
          for (auto &ptu: cs.tus)
    
            if (currArea.Cb().contains(ptu->Cb()) || (!ptu->Cb().valid() && currArea.Y().contains(ptu->Y())))
    
              TU::setCbfAtDepth(*ptu, COMPONENT_Cb, currDepth, SplitCbfs.Cb);
              TU::setCbfAtDepth(*ptu, COMPONENT_Cr, currDepth, SplitCbfs.Cr);
    
        }
      }
    
      return cbfs;
    }
    
    uint64_t IntraSearch::xFracModeBitsIntra(PredictionUnit &pu, const uint32_t &uiMode, const ChannelType &chType)
    {
    
    Vadim Seregin's avatar
    Vadim Seregin committed
      uint8_t orgMode = uiMode;
    
    #if JVET_Y0065_GPM_INTRA
      if (!pu.ciipFlag && !pu.gpmIntraFlag)
    #else
    
      std::swap(orgMode, pu.intraDir[chType]);
    
      m_CABACEstimator->resetBits();
    
      if( isLuma( chType ) )
      {
    
    #if JVET_Y0065_GPM_INTRA
        if (!pu.ciipFlag && !pu.gpmIntraFlag)
    #else
    
        {
          m_CABACEstimator->intra_luma_pred_mode(pu);
        }
    
    #if JVET_Y0065_GPM_INTRA
      if ( !pu.ciipFlag && !pu.gpmIntraFlag )
    #else
    
      std::swap(orgMode, pu.intraDir[chType]);
    
      return m_CABACEstimator->getEstFracBits();
    }
    
    
    void IntraSearch::sortRdModeListFirstColorSpace(ModeInfo mode, double cost, char bdpcmMode, ModeInfo* rdModeList, double* rdCostList, char* bdpcmModeList, int& candNum)
    {
      if (candNum == 0)
      {
        rdModeList[0] = mode;
        rdCostList[0] = cost;
        bdpcmModeList[0] = bdpcmMode;
        candNum++;
        return;
      }
    
      int insertPos = -1;
      for (int pos = candNum - 1; pos >= 0; pos--)
      {
        if (cost < rdCostList[pos])
        {
          insertPos = pos;
        }
      }
    
      if (insertPos >= 0)
      {
        for (int i = candNum - 1; i >= insertPos; i--)
        {
          rdModeList[i + 1] = rdModeList[i];
          rdCostList[i + 1] = rdCostList[i];
          bdpcmModeList[i + 1] = bdpcmModeList[i];
        }
        rdModeList[insertPos] = mode;
        rdCostList[insertPos] = cost;
        bdpcmModeList[insertPos] = bdpcmMode;
        candNum++;
      }
      else
      {
        rdModeList[candNum] = mode;
        rdCostList[candNum] = cost;
        bdpcmModeList[candNum] = bdpcmMode;
        candNum++;
      }
    
      CHECK(candNum > FAST_UDI_MAX_RDMODE_NUM, "exceed intra mode candidate list capacity");
    
      return;
    }
    
    void IntraSearch::invalidateBestRdModeFirstColorSpace()
    {
      int numSaveRdClass = 4 * NUM_LFNST_NUM_PER_SET * 2;
      int savedRdModeListSize = FAST_UDI_MAX_RDMODE_NUM;
    
      for (int i = 0; i < numSaveRdClass; i++)
      {
        m_numSavedRdModeFirstColorSpace[i] = 0;
        for (int j = 0; j < savedRdModeListSize; j++)
        {
    
          m_savedRdModeFirstColorSpace[i][j] = ModeInfo(false, false, 0, NOT_INTRA_SUBPARTITIONS, 0);
    
          m_savedBDPCMModeFirstColorSpace[i][j] = 0;
          m_savedRdCostFirstColorSpace[i][j] = MAX_DOUBLE;
        }
      }
    }
    
    template<typename T, size_t N>
    void IntraSearch::reduceHadCandList(static_vector<T, N>& candModeList, static_vector<double, N>& candCostList, int& numModesForFullRD, const double thresholdHadCost, const double* mipHadCost, const PredictionUnit &pu, const bool fastMip)
    {
      const int maxCandPerType = numModesForFullRD >> 1;
      static_vector<ModeInfo, FAST_UDI_MAX_RDMODE_NUM> tempRdModeList;
      static_vector<double, FAST_UDI_MAX_RDMODE_NUM> tempCandCostList;
      const double minCost = candCostList[0];
      bool keepOneMip = candModeList.size() > numModesForFullRD;
    
      int numConv = 0;
      int numMip = 0;
      for (int idx = 0; idx < candModeList.size() - (keepOneMip?0:1); idx++)
      {
        bool addMode = false;
    
    Philipp Merkle's avatar
    Philipp Merkle committed
        const ModeInfo& orgMode = candModeList[idx];
    
    Philipp Merkle's avatar
    Philipp Merkle committed
        if (!orgMode.mipFlg)
    
          addMode = (numConv < 3);
          numConv += addMode ? 1:0;
    
          addMode = ( numMip < maxCandPerType || (candCostList[idx] < thresholdHadCost * minCost) || keepOneMip );
          keepOneMip = false;
          numMip += addMode ? 1:0;
        }
        if( addMode )
        {
    
    Philipp Merkle's avatar
    Philipp Merkle committed
          tempRdModeList.push_back(orgMode);
    
          tempCandCostList.push_back(candCostList[idx]);
        }
      }
    
      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( fastMip ) break;
          }
        }
      }
    
      candModeList = tempRdModeList;
      candCostList = tempCandCostList;
      numModesForFullRD = int(candModeList.size());
    }
    
    // It decides which modes from the ISP lists can be full RD tested
    void IntraSearch::xGetNextISPMode(ModeInfo& modeInfo, const ModeInfo* lastMode, const Size cuSize)
    {
      static_vector<ModeInfo, FAST_UDI_MAX_RDMODE_NUM>* rdModeLists[2] = { &m_ispCandListHor, &m_ispCandListVer };
    
    
      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)
    
            {
              stopThisSplit = true;
            }
    
            // 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 &&
    
    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;
        if (lfnstIdx == 2 && m_ispTestedModes[1].modeHasBeenTested[currentIntraMode][st])
        {
          sameIntraModeFound = true;
          *refLfnstIdx = 1;
        }
        else if (m_ispTestedModes[0].modeHasBeenTested[currentIntraMode][st])
        {
          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;
    
    
        leftModeFound  = leftMode  != (int)currentIntraMode ? m_ispTestedModes[lfnstIdx].modeHasBeenTested[leftMode][st]  : false;
        rightModeFound = rightMode != (int)currentIntraMode ? m_ispTestedModes[lfnstIdx].modeHasBeenTested[rightMode][st] : 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 (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 ||
    
    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;
            }
    
    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;
        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));
        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();
          }
        }
      }
    }