Skip to content
Snippets Groups Projects
IntraSearch.h 37.05 KiB
/* The copyright in this software is being made available under the BSD
 * License, included below. This software may be subject to other third party
 * and contributor rights, including patent rights, and no such rights are
 * granted under this license.
 *
 * Copyright (c) 2010-2023, ITU/ISO/IEC
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  * Neither the name of the ITU/ISO/IEC nor the names of its contributors may
 *    be used to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

/** \file     IntraSearch.h
    \brief    intra search class (header)
*/

#ifndef __INTRASEARCH__
#define __INTRASEARCH__

// Include files

#include "CABACWriter.h"
#include "EncCfg.h"

#include "CommonLib/IntraPrediction.h"
#include "CommonLib/TrQuant.h"
#include "CommonLib/Unit.h"
#include "CommonLib/RdCost.h"
#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
#include "CommonLib/BilateralFilter.h"
#endif
#include "EncReshape.h"

//! \ingroup EncoderLib
//! \{

// ====================================================================================================================
// Class definition
// ====================================================================================================================
class EncModeCtrl;

enum PLTScanMode
{
  PLT_SCAN_HORTRAV = 0,
  PLT_SCAN_VERTRAV = 1,
  NUM_PLT_SCAN = 2
};
class SortingElement
{
public:
  SortingElement()
  {
    cnt[0] = cnt[1] = cnt[2] = cnt[3] = 0;
    shift[0] = shift[1] = shift[2] = 0;
    lastCnt[0] = lastCnt[1] = lastCnt[2] = 0;
    data[0] = data[1] = data[2] = 0;
    sumData[0] = sumData[1] = sumData[2] = 0;
  }
  uint32_t  getCnt(int idx) const         { return cnt[idx]; }
  void      setCnt(uint32_t val, int idx) { cnt[idx] = val; }
  int       getSumData (int id) const   { return sumData[id]; }

  void resetAll(ComponentID compBegin, uint32_t numComp)
  {
    shift[0] = shift[1] = shift[2] = 0;
    lastCnt[0] = lastCnt[1] = lastCnt[2] = 0;
    for (int ch = compBegin; ch < (compBegin + numComp); ch++)
    {
      data[ch] = 0;
      sumData[ch] = 0;
    }
  }
  void setAll(uint32_t* ui, ComponentID compBegin, uint32_t numComp)
  {
    for (int ch = compBegin; ch < (compBegin + numComp); ch++)
    {
      data[ch] = ui[ch];
    }
  }
  bool almostEqualData(SortingElement element, int errorLimit, const BitDepths& bitDepths, ComponentID compBegin, uint32_t numComp, bool lossless)
  {
    bool almostEqual = true;
    for (int comp = compBegin; comp < (compBegin + numComp); comp++)
    {
      if (lossless)
      {
        if ((std::abs(data[comp] - element.data[comp])) > errorLimit)
        {
          almostEqual = false;
          break;
        }
      }
      else
      {
      uint32_t absError = 0;
      if (isChroma((ComponentID) comp))
      {
        absError += int(double(std::abs(data[comp] - element.data[comp])) * PLT_CHROMA_WEIGHTING) >> (bitDepths.recon[CHANNEL_TYPE_CHROMA] - PLT_ENCBITDEPTH);
      }
      else
      {
        absError += (std::abs(data[comp] - element.data[comp]))>> (bitDepths.recon[CHANNEL_TYPE_LUMA] - PLT_ENCBITDEPTH);
      }
      if (absError > errorLimit)
      {
        almostEqual = false;
        break;
      }
      }
    }
    return almostEqual;
  }
  uint32_t getSAD(SortingElement element, const BitDepths &bitDepths, ComponentID compBegin, uint32_t numComp, bool lossless)
  {
    uint32_t sumAd = 0;
    for (int comp = compBegin; comp < (compBegin + numComp); comp++)
    {
      ChannelType chType = (comp > 0) ? CHANNEL_TYPE_CHROMA : CHANNEL_TYPE_LUMA;
      if (lossless)
      {
        sumAd += (std::abs(data[comp] - element.data[comp]));
      }
      else
      {
      sumAd += (std::abs(data[comp] - element.data[comp]) >> (bitDepths.recon[chType] - PLT_ENCBITDEPTH));
      }
    }
    return sumAd;
  }
  void copyDataFrom(SortingElement element, ComponentID compBegin, uint32_t numComp)
  {
    for (int comp = compBegin; comp < (compBegin + numComp); comp++)
    {
      data[comp] = element.data[comp];
      sumData[comp] = data[comp];
      shift[comp] = 0;
      lastCnt[comp] = 1;
    }
  }
  void copyAllFrom(SortingElement element, ComponentID compBegin, uint32_t numComp)
  {
    copyDataFrom(element, compBegin, numComp);
    for (int comp = compBegin; comp < (compBegin + numComp); comp++)
    {
      sumData[comp] = element.sumData[comp];
      cnt[comp]     = element.cnt[comp];
      shift[comp]   = element.shift[comp];
      lastCnt[comp] = element.lastCnt[comp];
    }
    cnt[MAX_NUM_COMPONENT] = element.cnt[MAX_NUM_COMPONENT];
  }
  void addElement(const SortingElement& element, ComponentID compBegin, uint32_t numComp)
  {
    for (int i = compBegin; i<(compBegin + numComp); i++)
    {
      sumData[i] += element.data[i];
      cnt[i]++;
      if( cnt[i] > 1 && cnt[i] == 2 * lastCnt[i] )
      {
        uint32_t rnd = 1 << shift[i];
        shift[i]++;
        data[i] = (sumData[i] + rnd) >> shift[i];
        lastCnt[i] = cnt[i];
      }
    }
  }
private:
  uint32_t cnt[MAX_NUM_COMPONENT+1];
  int shift[3], lastCnt[3], data[3], sumData[3];
};
/// encoder search class
class IntraSearch : public IntraPrediction
{
private:
  EncModeCtrl    *m_modeCtrl;
  Pel*            m_pSharedPredTransformSkip[MAX_NUM_TBLOCKS];
#if JVET_W0103_INTRA_MTS
#if JVET_Y0142_ADAPT_INTRA_MTS
  int             m_testAMTForFullRD[6];
  bool            m_validMTSReturn;
#else
  int             m_testAMTForFullRD[4];
#endif
  int             m_numCandAMTForFullRD;
#endif
  XUCache         m_unitCache;

  CodingStructure ****m_pSplitCS;
  CodingStructure ****m_pFullCS;

  CodingStructure ***m_pTempCS;
  CodingStructure ***m_pBestCS;

  CodingStructure **m_pSaveCS;
#if !INTRA_RM_SMALL_BLOCK_SIZE_CONSTRAINTS
  bool            m_saveCuCostInSCIPU;
  uint8_t         m_numCuInSCIPU;
  Area            m_cuAreaInSCIPU[NUM_INTER_CU_INFO_SAVE];
  double          m_cuCostInSCIPU[NUM_INTER_CU_INFO_SAVE];
#endif
  struct ModeInfo
  {
    bool     mipFlg; // CU::mipFlag
    bool     mipTrFlg; // PU::mipTransposedFlag
    int      mRefId; // PU::multiRefIdx
    uint8_t  ispMod; // CU::ispMode
    uint32_t modeId; // PU::intraDir[CHANNEL_TYPE_LUMA]
#if JVET_V0130_INTRA_TMP
    bool     tmpFlag; // CU::tmpFlag
#if JVET_AD0086_ENHANCED_INTRA_TMP
    int      tmpIdx;
    bool     tmpFusionFlag;
    bool     tmpFlmFlag;
#if JVET_AG0136_INTRA_TMP_LIC
    bool tmpLicFlag;
    uint8_t tmpLicIdc;
#endif
    int      tmpIsSubPel;
    int      tmpSubPelIdx;
#if JVET_AH0200_INTRA_TMP_BV_REORDER
    int      tmpFracIdx;
#endif
#endif
#endif
#if JVET_AB0155_SGPM
    bool     sgpmFlag;   // CU::sgpmFlag
    int      sgpmSplitDir;
    int      sgpmMode0;
    int      sgpmMode1;
    int      sgpmIdx;
#if JVET_AG0152_SGPM_ITMP_IBC
    Mv       sgpmBv0;
    Mv       sgpmBv1;
#endif
#endif
#if JVET_AB0155_SGPM
#if JVET_V0130_INTRA_TMP
    ModeInfo() : mipFlg( false ), mipTrFlg( false ), mRefId( 0 ), ispMod( NOT_INTRA_SUBPARTITIONS ), modeId( 0 ), tmpFlag( 0 )
#if JVET_AD0086_ENHANCED_INTRA_TMP
#if JVET_AG0136_INTRA_TMP_LIC
      , tmpIdx(0) , tmpFusionFlag(false) , tmpFlmFlag(false) , tmpLicFlag(false) , tmpLicIdc(0), tmpIsSubPel(0), tmpSubPelIdx(0)
#if JVET_AH0200_INTRA_TMP_BV_REORDER
      , tmpFracIdx(-1)
#endif
#else
      , tmpIdx(0) , tmpFusionFlag(false) , tmpFlmFlag(false) , tmpIsSubPel(0), tmpSubPelIdx(0)
#endif
#endif
	, sgpmFlag( 0 ), sgpmSplitDir( 0 ), sgpmMode0( 0 ), sgpmMode1( 0 ), sgpmIdx( 0 ) 
#if JVET_AG0152_SGPM_ITMP_IBC
  , sgpmBv0(0, 0), sgpmBv1(0, 0)
#endif
{}
    ModeInfo(const bool mipf, const bool miptf, const int mrid, const uint8_t ispm, const uint32_t mode,
             const bool tmpf = 0
#if JVET_AD0086_ENHANCED_INTRA_TMP
#if JVET_AG0136_INTRA_TMP_LIC
      , const int tmpi = 0 , const bool tmpff = 0  , const int tmpflmf = 0 , const int tmpLic = 0 , const int tmpLicIdc = 0 , const int tmpsp = 0, const int tmpspi = 0
#if JVET_AH0200_INTRA_TMP_BV_REORDER
    , const int tmpfi = -1
#endif
#else
      , const int tmpi = 0 , const bool tmpff = 0  , const int tmpflmf = 0 , const int tmpsp = 0, const int tmpspi = 0
#endif
#endif
	  , const bool sf = 0, const int sd = 0, const int sm0 = 0, const int sm1 = 0, const int si = 0 
#if JVET_AG0152_SGPM_ITMP_IBC
    , const Mv sbv0 = Mv(0, 0), const Mv sbv1 = Mv(0, 0)
#endif
)
#else
    ModeInfo() : mipFlg(false), mipTrFlg(false), mRefId(0), ispMod(NOT_INTRA_SUBPARTITIONS), modeId(0), sgpmFlag(0), sgpmSplitDir(0), sgpmMode0(0), sgpmMode1(0), sgpmIdx(0){}
    ModeInfo(const bool mipf, const bool miptf, const int mrid, const uint8_t ispm, const uint32_t mode,
             const bool sf = 0, const int sd = 0, const int sm0 = 0, const int sm1 = 0, const int si = 0)
#endif
      : mipFlg(mipf)
      , mipTrFlg(miptf)
      , mRefId(mrid)
      , ispMod(ispm)
      , modeId(mode)
#if JVET_V0130_INTRA_TMP
      , tmpFlag(tmpf)
#endif
#if JVET_AD0086_ENHANCED_INTRA_TMP
#if JVET_AG0136_INTRA_TMP_LIC
      , tmpIdx(tmpi) , tmpFusionFlag(tmpff) , tmpFlmFlag(tmpflmf) , tmpLicFlag(tmpLic) , tmpLicIdc(tmpLicIdc) ,tmpIsSubPel(tmpsp) , tmpSubPelIdx(tmpspi)
#if JVET_AH0200_INTRA_TMP_BV_REORDER
      , tmpFracIdx(tmpfi)
#endif
#else
      , tmpIdx(tmpi) , tmpFusionFlag(tmpff) , tmpFlmFlag(tmpflmf) , tmpIsSubPel(tmpsp) , tmpSubPelIdx(tmpspi)
#endif
#endif
      , sgpmFlag(sf)
      , sgpmSplitDir(sd)
      , sgpmMode0(sm0)
      , sgpmMode1(sm1)
      , sgpmIdx(si)
#if JVET_AG0152_SGPM_ITMP_IBC
      , sgpmBv0(sbv0)
      , sgpmBv1(sbv1)
#endif
    {
    }
    ModeInfo &operator=(const ModeInfo &other)
    {
      mipFlg       = other.mipFlg;     // CU::mipFlag
      mipTrFlg     = other.mipTrFlg;   // PU::mipTransposedFlag
      mRefId       = other.mRefId;     // PU::multiRefIdx
      ispMod       = other.ispMod;     // CU::ispMode
      modeId       = other.modeId;     // PU::intraDir[CHANNEL_TYPE_LUMA]
#if JVET_V0130_INTRA_TMP
      tmpFlag      = other.tmpFlag;    // CU::tmpFlag
#endif
#if JVET_AD0086_ENHANCED_INTRA_TMP
      tmpIdx        = other.tmpIdx;
      tmpFusionFlag = other.tmpFusionFlag;
      tmpFlmFlag    = other.tmpFlmFlag;
#if JVET_AG0136_INTRA_TMP_LIC
      tmpLicFlag    = other.tmpLicFlag;
      tmpLicIdc     = other.tmpLicIdc;
#endif
      tmpIsSubPel   = other.tmpIsSubPel;    // CU::tmpIsSubPel
      tmpSubPelIdx  = other.tmpSubPelIdx;   // CU::tmpSubPelIdx
#if JVET_AH0200_INTRA_TMP_BV_REORDER
      tmpFracIdx   = other.tmpFracIdx;
#endif
#endif
      sgpmFlag     = other.sgpmFlag;   // CU::sgpmFlag
      sgpmSplitDir = other.sgpmSplitDir;
      sgpmMode0    = other.sgpmMode0;
      sgpmMode1    = other.sgpmMode1;
      sgpmIdx      = other.sgpmIdx;
#if JVET_AG0152_SGPM_ITMP_IBC
      sgpmBv0 = other.sgpmBv0;
      sgpmBv1 = other.sgpmBv1;
#endif
      return *this;
    }
    bool operator==(const ModeInfo cmp) const
    {
      return (mipFlg == cmp.mipFlg && mipTrFlg == cmp.mipTrFlg && mRefId == cmp.mRefId && ispMod == cmp.ispMod
                && modeId == cmp.modeId 
#if JVET_V0130_INTRA_TMP
                && tmpFlag == cmp.tmpFlag
#endif
#if JVET_AD0086_ENHANCED_INTRA_TMP
                && tmpIdx == cmp.tmpIdx
                && tmpFusionFlag == cmp.tmpFusionFlag
                && tmpFlmFlag == cmp.tmpFlmFlag
#if JVET_AG0136_INTRA_TMP_LIC
                && tmpLicFlag == cmp.tmpLicFlag
                && tmpLicIdc == cmp.tmpLicIdc
#endif
                && tmpIsSubPel == cmp.tmpIsSubPel
                && tmpSubPelIdx == cmp.tmpSubPelIdx
#if JVET_AH0200_INTRA_TMP_BV_REORDER
                && tmpFracIdx == cmp.tmpFracIdx
#endif
#endif
                && sgpmFlag == cmp.sgpmFlag
                && sgpmSplitDir == cmp.sgpmSplitDir); // sgpmMode0 and sgpmMode1 seems no need
    }
#elif JVET_V0130_INTRA_TMP
	  ModeInfo() : mipFlg(false), mipTrFlg(false), mRefId(0), ispMod(NOT_INTRA_SUBPARTITIONS), modeId(0), tmpFlag(0) {}
	  ModeInfo(const bool mipf, const bool miptf, const int mrid, const uint8_t ispm, const uint32_t mode, const bool tpmf = 0) : mipFlg(mipf), mipTrFlg(miptf), mRefId(mrid), ispMod(ispm), modeId(mode), tmpFlag(tpmf) {}
	  bool operator==(const ModeInfo cmp) const { return (mipFlg == cmp.mipFlg && mipTrFlg == cmp.mipTrFlg && mRefId == cmp.mRefId && ispMod == cmp.ispMod && modeId == cmp.modeId && tmpFlag == cmp.tmpFlag); }
#else
    ModeInfo() : mipFlg(false), mipTrFlg(false), mRefId(0), ispMod(NOT_INTRA_SUBPARTITIONS), modeId(0) {}
    ModeInfo(const bool mipf, const bool miptf, const int mrid, const uint8_t ispm, const uint32_t mode) : mipFlg(mipf), mipTrFlg(miptf), mRefId(mrid), ispMod(ispm), modeId(mode) {}
    bool operator==(const ModeInfo cmp) const { return (mipFlg == cmp.mipFlg && mipTrFlg == cmp.mipTrFlg && mRefId == cmp.mRefId && ispMod == cmp.ispMod && modeId == cmp.modeId); }
#endif
  };
  struct ModeInfoWithCost : public ModeInfo
  {
    double rdCost;
    ModeInfoWithCost() : ModeInfo(), rdCost(MAX_DOUBLE) {}
#if JVET_AB0155_SGPM && JVET_V0130_INTRA_TMP
    ModeInfoWithCost(const bool mipf, const bool miptf, const int mrid, const uint8_t ispm, const uint32_t mode, const bool tpmf, 
#if JVET_AD0086_ENHANCED_INTRA_TMP
#if JVET_AG0136_INTRA_TMP_LIC
      const int tmpi, const bool tmpff, const int tmpflmf,  const int tmpLicItmp, const int tmpLicIdc, const int tmpsp, const int tmpspi,
#if JVET_AH0200_INTRA_TMP_BV_REORDER
                     const int tmpfi,
#endif
#else
                     const int tmpi, const bool tmpff, const int tmpflmf,  const int tmpsp, const int tmpspi,
#endif
#endif
					 double cost, const bool sf = 0, const int sd = 0, const int sm0 = 0, const int sm1 = 0)
      : ModeInfo(mipf, miptf, mrid, ispm, mode, tpmf
#if JVET_AD0086_ENHANCED_INTRA_TMP
#if JVET_AG0136_INTRA_TMP_LIC
        ,tmpi ,tmpff ,tmpflmf , tmpLicItmp, tmpLicIdc, tmpsp, tmpspi
#if JVET_AH0200_INTRA_TMP_BV_REORDER
        ,tmpfi
#endif
#else
        ,tmpi ,tmpff ,tmpflmf , tmpsp, tmpspi
#endif
#endif
	  , sf, sd, sm0, sm1), rdCost(cost)
    {
    }
    bool operator==(const ModeInfoWithCost cmp) const
    {
      return (mipFlg == cmp.mipFlg && mipTrFlg == cmp.mipTrFlg && mRefId == cmp.mRefId && ispMod == cmp.ispMod
              && modeId == cmp.modeId && tmpFlag == cmp.tmpFlag
#if JVET_AD0086_ENHANCED_INTRA_TMP
      && tmpIdx == cmp.tmpIdx
      && tmpFusionFlag == cmp.tmpFusionFlag
      && tmpFlmFlag == cmp.tmpFlmFlag
#if JVET_AG0136_INTRA_TMP_LIC
      && tmpLicFlag == cmp.tmpLicFlag
      && tmpLicIdc == cmp.tmpLicIdc
#endif
      && tmpIsSubPel == cmp.tmpIsSubPel
      && tmpSubPelIdx == cmp.tmpSubPelIdx
#if JVET_AH0200_INTRA_TMP_BV_REORDER
      && tmpFracIdx == cmp.tmpFracIdx
#endif
#endif
			   && rdCost == cmp.rdCost && sgpmFlag == cmp.sgpmFlag
              && sgpmSplitDir == cmp.sgpmSplitDir);   // sgpmMode0 and sgpmMode1 seems no need
    }
#elif JVET_V0130_INTRA_TMP
	  ModeInfoWithCost(const bool mipf, const bool miptf, const int mrid, const uint8_t ispm, const uint32_t mode, const bool tpmf, double cost) : ModeInfo(mipf, miptf, mrid, ispm, mode, tpmf), rdCost(cost) {}
	  bool operator==(const ModeInfoWithCost cmp) const { return (mipFlg == cmp.mipFlg && mipTrFlg == cmp.mipTrFlg && mRefId == cmp.mRefId && ispMod == cmp.ispMod && modeId == cmp.modeId && tmpFlag == cmp.tmpFlag && rdCost == cmp.rdCost); }
#else
    ModeInfoWithCost(const bool mipf, const bool miptf, const int mrid, const uint8_t ispm, const uint32_t mode, double cost) : ModeInfo(mipf, miptf, mrid, ispm, mode), rdCost(cost) {}
    bool operator==(const ModeInfoWithCost cmp) const { return (mipFlg == cmp.mipFlg && mipTrFlg == cmp.mipTrFlg && mRefId == cmp.mRefId && ispMod == cmp.ispMod && modeId == cmp.modeId && rdCost == cmp.rdCost); }
#endif
    static bool compareModeInfoWithCost(ModeInfoWithCost a, ModeInfoWithCost b) { return a.rdCost < b.rdCost; }
  };

  struct ISPTestedModeInfo
  {
    int    numCompSubParts;
    double rdCost;

    ISPTestedModeInfo() {}

    void setMode(int numParts, double cost)
    {
      numCompSubParts = numParts;
      rdCost = cost;
    }
    void clear()
    {
      numCompSubParts = -1;
      rdCost = MAX_DOUBLE;
    }
  };
  struct ISPTestedModesInfo
  {
    std::map<int, ISPTestedModeInfo>            intraMode[2];
    std::map<int, bool>                         modeHasBeenTested[2];
    int                                         numTotalParts[2];
    static_vector<int, FAST_UDI_MAX_RDMODE_NUM> testedModes[2];
    int                                         bestModeSoFar;
    ISPType                                     bestSplitSoFar;
    int                                         bestMode[2];
    double                                      bestCost[2];
    int                                         numTestedModes[2];
    int                                         candIndexInList[2];
    bool                                        splitIsFinished[2];
    int                                         numOrigModesToTest;

    // set a tested mode results
    void setModeResults(ISPType splitType, int iModeIdx, int numCompletedParts, double rdCost, double currentBestCost)
    {
      const unsigned st = splitType - 1;
      CHECKD(st > 1, "The split type is invalid!");
      CHECK( iModeIdx < 0, "The modeIdx is invalid" );
      const int maxNumParts = numTotalParts[st];
      intraMode[st][iModeIdx].setMode(numCompletedParts, numCompletedParts == maxNumParts ? rdCost : MAX_DOUBLE);
      testedModes[st].push_back(iModeIdx);
      numTestedModes[st]++;
      modeHasBeenTested[st][iModeIdx] = true;
      if (numCompletedParts == maxNumParts && rdCost < bestCost[st])   // best mode update
      {
        bestMode[st] = iModeIdx;
        bestCost[st] = rdCost;
      }
      if (numCompletedParts == maxNumParts && rdCost < currentBestCost)   // best mode update
      {
        bestModeSoFar = iModeIdx;
        bestSplitSoFar = splitType;
      }
    }

    int getNumCompletedSubParts(ISPType splitType, int iModeIdx)
    {
      const unsigned st = splitType - 1;
      CHECK(st < 0 || st > 1, "The split type is invalid!");
      CHECK(iModeIdx < 0, "The modeIdx is invalid");
      return modeHasBeenTested[st].count( iModeIdx ) > 0 ? intraMode[st][iModeIdx].numCompSubParts : -1;
    }

    double getRDCost(ISPType splitType, int iModeIdx)
    {
      const unsigned st = splitType - 1;
      CHECKD(st > 1, "The split type is invalid!");
      return modeHasBeenTested[st].count( iModeIdx ) > 0 ? intraMode[st][iModeIdx].rdCost : MAX_DOUBLE;
    }

    // get a tested intra mode index
    int getTestedIntraMode(ISPType splitType, int pos)
    {
      const unsigned st = splitType - 1;
      CHECKD(st > 1, "The split type is invalid!");
      return pos < testedModes[st].size() ? testedModes[st].at(pos) : -1;
    }

    // set everything to default values
    void clear()
    {
      for (int splitIdx = 0; splitIdx < NUM_INTRA_SUBPARTITIONS_MODES - 1; splitIdx++)
      {
        numTestedModes [splitIdx] = 0;
        candIndexInList[splitIdx] = 0;
        numTotalParts  [splitIdx] = 0;
        splitIsFinished[splitIdx] = false;
        testedModes    [splitIdx].clear();
        bestCost       [splitIdx] = MAX_DOUBLE;
        bestMode       [splitIdx] = -1;
      }
      bestModeSoFar = -1;
      bestSplitSoFar = NOT_INTRA_SUBPARTITIONS;
      numOrigModesToTest = -1;
      modeHasBeenTested[0].clear();
      modeHasBeenTested[1].clear();
    }
    void clearISPModeInfo(int idx)
    {
      intraMode[0].clear();
      intraMode[1].clear();
    }
    void init(const int numTotalPartsHor, const int numTotalPartsVer)
    {
      clear();
      const int horSplit = HOR_INTRA_SUBPARTITIONS - 1, verSplit = VER_INTRA_SUBPARTITIONS - 1;
      numTotalParts  [horSplit] = numTotalPartsHor;
      numTotalParts  [verSplit] = numTotalPartsVer;
      splitIsFinished[horSplit] = (numTotalParts[horSplit] == 0);
      splitIsFinished[verSplit] = (numTotalParts[verSplit] == 0);
    }
  };

  static_vector<ModeInfo, FAST_UDI_MAX_RDMODE_NUM> m_ispCandListHor, m_ispCandListVer;
  static_vector<ModeInfoWithCost, FAST_UDI_MAX_RDMODE_NUM> m_regIntraRDListWithCosts;

  ISPTestedModesInfo m_ispTestedModes[NUM_LFNST_NUM_PER_SET];
  int m_curIspLfnstIdx;

  //cost variables for the EMT algorithm and new modes list
  double     m_bestModeCostStore[ NUM_LFNST_NUM_PER_SET ];                                    // RD cost of the best mode for each PU using DCT2
  bool       m_bestModeCostValid[ NUM_LFNST_NUM_PER_SET ];
  double     m_modeCostStore[ NUM_LFNST_NUM_PER_SET ][ NUM_LUMA_MODE ];                   // RD cost of each mode for each PU using DCT2
  ModeInfo   m_savedRdModeList[ NUM_LFNST_NUM_PER_SET ][ NUM_LUMA_MODE ];
  int32_t    m_savedNumRdModes[ NUM_LFNST_NUM_PER_SET ];
#if JVET_W0103_INTRA_MTS
  double     m_globalBestCostStore;
  bool       m_globalBestCostValid;
  int        m_numModesISPRDO; //full modes for ISP testing.
#if JVET_Y0142_ADAPT_INTRA_MTS
  static_vector<ModeInfo, NUM_LUMA_MODE> m_modesForMTS;
  static_vector<int64_t, NUM_LUMA_MODE> m_modesCoeffAbsSumDCT2;
  int64_t m_coeffAbsSumDCT2;
#endif
#endif
#if JVET_AE0169_BIPREDICTIVE_IBC
  double     m_bestIntraSADHADCost;
#endif
#if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
  double     m_bestIntraSADCost;
#endif
  ModeInfo                                           m_savedRdModeFirstColorSpace[4 * NUM_LFNST_NUM_PER_SET * 2][FAST_UDI_MAX_RDMODE_NUM];
  char                                               m_savedBDPCMModeFirstColorSpace[4 * NUM_LFNST_NUM_PER_SET * 2][FAST_UDI_MAX_RDMODE_NUM];
  double                                             m_savedRdCostFirstColorSpace[4 * NUM_LFNST_NUM_PER_SET * 2][FAST_UDI_MAX_RDMODE_NUM];
  int                                                m_numSavedRdModeFirstColorSpace[4 * NUM_LFNST_NUM_PER_SET * 2];
  int                                                m_savedRdModeIdx;

#if SECONDARY_MPM
  int m_mpmListSize;
#endif

  static_vector<ModeInfo, FAST_UDI_MAX_RDMODE_NUM> m_uiSavedRdModeListLFNST;
  static_vector<ModeInfo, FAST_UDI_MAX_RDMODE_NUM> m_uiSavedHadModeListLFNST;
  uint32_t                                         m_uiSavedNumRdModesLFNST;
  static_vector<double,   FAST_UDI_MAX_RDMODE_NUM> m_dSavedModeCostLFNST;
  static_vector<double,   FAST_UDI_MAX_RDMODE_NUM> m_dSavedHadListLFNST;

#if JVET_AB0155_SGPM
  static_vector<ModeInfo, SGPM_NUM> m_uiSavedRdModeListSGPM;
  static_vector<ModeInfo, SGPM_NUM> m_uiSavedHadModeListSGPM;
  static_vector<double, SGPM_NUM>   m_dSavedModeCostSGPM;
  static_vector<double, SGPM_NUM>   m_dSavedHadListSGPM;

#if JVET_AG0152_SGPM_ITMP_IBC
  Pel* m_intraPredBuf[NUM_LUMA_MODE + SGPM_NUM_BVS];
  Pel* m_sgpmPredBuf[SGPM_NUM];
  uint8_t         m_intraModeReady[NUM_LUMA_MODE + SGPM_NUM_BVS];
#else
  Pel*            m_intraPredBuf[NUM_LUMA_MODE];
  Pel*            m_sgpmPredBuf[SGPM_NUM];
  uint8_t         m_intraModeReady[NUM_LUMA_MODE];
#endif
#endif
#if JVET_AH0209_PDP
  Pel* m_pdpIntraPredBuf[NUM_LUMA_MODE];
#endif
#if JVET_AG0058_EIP
  Pel* m_eipPredBuf[NUM_DERIVED_EIP];
  Pel* m_eipMergePredBuf[MAX_MERGE_EIP];
  static_vector<ModeInfo, NUM_DERIVED_EIP + MAX_MERGE_EIP> m_uiSavedRdModeListEip;
  static_vector<ModeInfo, NUM_DERIVED_EIP + MAX_MERGE_EIP> m_uiSavedHadModeListEip;
  static_vector<double, NUM_DERIVED_EIP + MAX_MERGE_EIP>   m_dSavedModeCostEip;
  static_vector<double, NUM_DERIVED_EIP + MAX_MERGE_EIP>   m_dSavedHadListEip;
#endif
#if JVET_AH0076_OBIC
  Pel* m_dimdPredBuf;
  Pel* m_obicPredBuf;
#endif
  PelStorage      m_tmpStorageLCU;
  PelStorage      m_colorTransResiBuf;
#if JVET_AB0143_CCCM_TS
#if JVET_AC0147_CCCM_NO_SUBSAMPLING
#if JVET_AD0202_CCCM_MDF
  PelStorage      m_cccmStorage[2][TOTAL_NUM_CCCM_MODES];
#else
  PelStorage      m_cccmStorage[2][CCCM_NUM_MODES];
#endif
#else
  PelStorage      m_cccmStorage[CCCM_NUM_MODES];
#endif
#endif

#if JVET_AD0188_CCP_MERGE
#if JVET_AC0147_CCCM_NO_SUBSAMPLING
#if JVET_AD0202_CCCM_MDF
  CCPModelCandidate m_ccmParamsStorage[2][TOTAL_NUM_CCCM_MODES];
#else
  CCPModelCandidate m_ccmParamsStorage[2][CCCM_NUM_MODES];
#endif
#else
  CCPModelCandidate m_ccmParamsStorage[CCCM_NUM_MODES];
#endif
#endif

#if JVET_AC0119_LM_CHROMA_FUSION
  PelStorage      m_predStorage[2];
  PelStorage      m_fusionStorage[6];
#endif

#if JVET_AD0120_LBCCP
  PelStorage      m_lmPredFiltStorage[LBCCP_FILTER_MMLMNUM];
  struct lmPredFiltModeInfo
  {
    int    bufIdx;
    int    isCccm;
    int    isCccmNoSub;
    int    isGlcccm;
    int    cccmMdfIdx;
    double cost;
  };
#endif

#if JVET_AG0059_CCP_MERGE_ENHANCEMENT
  PelStorage      m_predCCPFusionStorage[2];
#endif

protected:
  // interface to option
  EncCfg*         m_pcEncCfg;

  // interface to classes
#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
  BilateralFilter* m_bilateralFilter;
#endif
  TrQuant*        m_pcTrQuant;
  RdCost*         m_pcRdCost;
  EncReshape*     m_pcReshape;

  // RD computation
  CABACWriter*    m_CABACEstimator;
  CtxCache*       m_ctxCache;

  bool            m_isInitialized;
  uint32_t        m_symbolSize;
  uint16_t**      m_truncBinBits;
  uint16_t*       m_escapeNumBins;
  bool            m_bestEscape;
  double*         m_indexError[MAXPLTSIZE + 1];
  uint8_t*        m_minErrorIndexMap; // store the best index in terms of distortion for each pixel
  uint8_t         m_indexMapRDOQ   [2][NUM_TRELLIS_STATE][2 * MAX_CU_BLKSIZE_PLT];
  bool            m_runMapRDOQ     [2][NUM_TRELLIS_STATE][2 * MAX_CU_BLKSIZE_PLT];
  uint8_t*        m_statePtRDOQ    [NUM_TRELLIS_STATE];
  bool            m_prevRunTypeRDOQ[2][NUM_TRELLIS_STATE];
  int             m_prevRunPosRDOQ [2][NUM_TRELLIS_STATE];
  double          m_stateCostRDOQ  [2][NUM_TRELLIS_STATE];
public:
#if INTRA_TRANS_ENC_OPT
  bool            m_skipTimdLfnstMtsPass;
#endif
#if JVET_AH0076_OBIC
  bool            m_skipObicLfnstMtsPass;
  bool            m_skipDimdLfnstMtsPass;
#endif
#if JVET_AC0147_CCCM_NO_SUBSAMPLING
  bool            m_skipCCCMSATD;
  int             m_isCccmNoSubModeEnabledInRdo[MMLM_T_IDX + 1];
#endif 
#if JVET_AD0202_CCCM_MDF
  bool            m_skipCCCMwithMdfSATD;
  int             m_isCccmWithMdfEnabledInRdo[5][MMLM_T_IDX + 1];
#endif
#if JVET_AG0154_DECODER_DERIVED_CCP_FUSION
  bool m_skipDdCcpListConstruction;
  bool firstTransformDdccp;
  PelStorage      m_ddCcpStorage;
  PelUnitBuf      m_ddCcpStorageTemp;
  std::vector<DecoderDerivedCcpCandidate> m_decoderDerivedCcpList;
  bool m_skipDdCcpMergeFusionList;
  int m_numCcpMergefusionRdo;
  double m_ddccpMergeFusionCost[2];
  int m_ddCcpMergeFusionModeIndex[2];
  PelStorage      m_ddCcpFusionStorage[2];
  PelUnitBuf      m_ddCcpFusionStorageTemp[2];
#endif  
  IntraSearch();
  ~IntraSearch();

  void init                       ( EncCfg*        pcEncCfg,
#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
                                   BilateralFilter* bilateralFilter,
#endif
                                    TrQuant*       pcTrQuant,
                                    RdCost*        pcRdCost,
                                    CABACWriter*   CABACEstimator,
                                    CtxCache*      ctxCache,
                                    const uint32_t     maxCUWidth,
                                    const uint32_t     maxCUHeight,
                                    const uint32_t     maxTotalCUDepth
                                  , EncReshape*   m_pcReshape
                                  , const unsigned bitDepthY
                                  );

  void destroy                    ();

  CodingStructure****getSplitCSBuf() { return m_pSplitCS; }
  CodingStructure****getFullCSBuf () { return m_pFullCS; }
  CodingStructure  **getSaveCSBuf () { return m_pSaveCS; }

  void setModeCtrl                ( EncModeCtrl *modeCtrl ) { m_modeCtrl = modeCtrl; }
#if !INTRA_RM_SMALL_BLOCK_SIZE_CONSTRAINTS
  bool getSaveCuCostInSCIPU       ()               { return m_saveCuCostInSCIPU; }
  void setSaveCuCostInSCIPU       ( bool b )       { m_saveCuCostInSCIPU = b;  }
  void setNumCuInSCIPU            ( uint8_t i )    { m_numCuInSCIPU = i; }
  void saveCuAreaCostInSCIPU      ( Area area, double cost );
  void initCuAreaCostInSCIPU      ();
  double findInterCUCost          ( CodingUnit &cu );
#endif
public:
  bool estIntraPredLumaQT(CodingUnit &cu, Partitioner& pm, const double bestCostSoFar = MAX_DOUBLE, bool mtsCheckRangeFlag = false, int mtsFirstCheckId = 0, int mtsLastCheckId = 0, bool moreProbMTSIdxFirst = false, CodingStructure* bestCS = NULL
#if JVET_AG0136_INTRA_TMP_LIC
    , InterPrediction* pcInterPred = nullptr
#endif
  );
  void estIntraPredChromaQT       ( CodingUnit &cu, Partitioner& pm, const double maxCostAllowed = MAX_DOUBLE 
#if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
                                  , InterPrediction* pcInterPred = nullptr
#endif
  );
  void PLTSearch                  ( CodingStructure &cs, Partitioner& partitioner, ComponentID compBegin, uint32_t numComp);
  uint64_t xFracModeBitsIntra     (PredictionUnit &pu, const uint32_t &uiMode, const ChannelType &compID);
  void invalidateBestModeCost     () { for( int i = 0; i < NUM_LFNST_NUM_PER_SET; i++ ) m_bestModeCostValid[ i ] = false; };

  void sortRdModeListFirstColorSpace(ModeInfo mode, double cost, char bdpcmMode, ModeInfo* rdModeList, double* rdCostList, char* bdpcmModeList, int& candNum);
  void invalidateBestRdModeFirstColorSpace();
  void setSavedRdModeIdx(int idx) { m_savedRdModeIdx = idx; }
#if JVET_AD0120_LBCCP
  void fillLmPredFiltList(PredictionUnit pu, const PelUnitBuf& lmPredFilt, int &lmPredFiltIdx, std::vector<lmPredFiltModeInfo> &miLmPredFiltList);
#endif

#if SECONDARY_MPM
  int& getMpmListSize()           { return m_mpmListSize; }
#endif
#if JVET_AE0169_BIPREDICTIVE_IBC
  double getBestIntraSADHADCost() { return m_bestIntraSADHADCost; }
#endif
#if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
  double getBestIntraSADCost()    { return m_bestIntraSADCost; }
#endif
  void setLumaIntraPredIdx(PredictionUnit& pu);
#if JVET_AG0058_EIP
  EipModelCandidate m_eipModel[NUM_DERIVED_EIP];
  EipModelCandidate m_eipMergeModel[MAX_MERGE_EIP];
#endif
protected:

  // -------------------------------------------------------------------------------------------------------------------
  // T & Q & Q-1 & T-1
  // -------------------------------------------------------------------------------------------------------------------


  // -------------------------------------------------------------------------------------------------------------------
  // Intra search
  // -------------------------------------------------------------------------------------------------------------------

  void     xEncIntraHeader                         ( CodingStructure &cs, Partitioner& pm, const bool &luma, const bool &chroma, const int subTuIdx = -1 );
  void     xEncSubdivCbfQT                         ( CodingStructure &cs, Partitioner& pm, const bool &luma, const bool &chroma, const int subTuIdx = -1, const PartSplit ispType = TU_NO_ISP );
  uint64_t xGetIntraFracBitsQT                     ( CodingStructure &cs, Partitioner& pm, const bool &luma, const bool &chroma, const int subTuIdx = -1, const PartSplit ispType = TU_NO_ISP, CUCtx * cuCtx = nullptr  );
  uint64_t xGetIntraFracBitsQTSingleChromaComponent( CodingStructure &cs, Partitioner& pm, const ComponentID compID );

  uint64_t xGetIntraFracBitsQTChroma(TransformUnit& tu, const ComponentID &compID);
  void xEncCoeffQT                                 ( CodingStructure &cs, Partitioner& pm, const ComponentID compID, const int subTuIdx = -1, const PartSplit ispType = TU_NO_ISP, CUCtx * cuCtx = nullptr );

  void xIntraCodingTUBlock        (TransformUnit &tu, const ComponentID &compID, Distortion& ruiDist, const int &default0Save1Load2 = 0, uint32_t* numSig = nullptr, std::vector<TrMode>* trModes=nullptr, const bool loadTr=false 
#if JVET_AG0136_INTRA_TMP_LIC
    , InterPrediction* pcInterPred=nullptr
#endif
  );
  void xIntraCodingACTTUBlock(TransformUnit &tu, const ComponentID &compID, Distortion& ruiDist, std::vector<TrMode>* trModes = nullptr, const bool loadTr = false);
#if JVET_W0103_INTRA_MTS
  void xSelectAMTForFullRD(TransformUnit &tu
#if JVET_AG0136_INTRA_TMP_LIC
    , InterPrediction* pcInterPred=nullptr
#endif
  );
  bool testISPforCurrCU(const CodingUnit &cu);
#endif
  ChromaCbfs xRecurIntraChromaCodingQT( CodingStructure &cs, Partitioner& pm, const double bestCostSoFar = MAX_DOUBLE,                          const PartSplit ispType = TU_NO_ISP 
#if JVET_AB0143_CCCM_TS || JVET_AC0119_LM_CHROMA_FUSION
    , const PelUnitBuf& predStorage = UnitBuf<Pel>()
#endif
#if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
    , InterPrediction* pcInterPred = nullptr
#endif
  );
  bool       xRecurIntraCodingLumaQT  ( CodingStructure &cs, Partitioner& pm, const double bestCostSoFar = MAX_DOUBLE, const int subTuIdx = -1, const PartSplit ispType = TU_NO_ISP, const bool ispIsCurrentWinner = false, bool mtsCheckRangeFlag = false, int mtsFirstCheckId = 0, int mtsLastCheckId = 0, bool moreProbMTSIdxFirst = false 
#if JVET_AG0136_INTRA_TMP_LIC
    , InterPrediction* pcInterPred=NULL
#endif
  );
  bool       xRecurIntraCodingACTQT(CodingStructure &cs, Partitioner& pm, bool mtsCheckRangeFlag = false, int mtsFirstCheckId = 0, int mtsLastCheckId = 0, bool moreProbMTSIdxFirst = false);
  bool       xIntraCodingLumaISP      ( CodingStructure& cs, Partitioner& pm, const double bestCostSoFar = MAX_DOUBLE );

  template<typename T, size_t N>
  void 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
#if JVET_AB0157_TMRL
    , const double* tmrlCostList
#endif
#if JVET_AC0105_DIRECTIONAL_PLANAR
    , const double* dirPlanarCostList
#endif
  );
  void   derivePLTLossy  (      CodingStructure& cs, Partitioner& partitioner, ComponentID compBegin, uint32_t numComp);
  void   calcPixelPred   (      CodingStructure& cs, Partitioner& partitioner, uint32_t    yPos,      uint32_t xPos,             ComponentID compBegin, uint32_t  numComp);
  void     preCalcPLTIndexRD      (CodingStructure& cs, Partitioner& partitioner, ComponentID compBegin, uint32_t numComp);
  void     calcPixelPredRD        (CodingStructure& cs, Partitioner& partitioner, Pel* orgBuf, Pel* pixelValue, Pel* recoValue, ComponentID compBegin, uint32_t numComp);
  void     deriveIndexMap         (CodingStructure& cs, Partitioner& partitioner, ComponentID compBegin, uint32_t numComp, PLTScanMode pltScanMode, double& dCost, bool* idxExist);
  bool     deriveSubblockIndexMap(CodingStructure& cs, Partitioner& partitioner, ComponentID compBegin, PLTScanMode pltScanMode, int minSubPos, int maxSubPos, const BinFracBits& fracBitsPltRunType, const BinFracBits* fracBitsPltIndexINDEX, const BinFracBits* fracBitsPltIndexCOPY, const double minCost, bool useRotate);
  double   rateDistOptPLT         (bool RunType, uint8_t RunIndex, bool prevRunType, uint8_t prevRunIndex, uint8_t aboveRunIndex, bool& prevCodedRunType, int& prevCodedRunPos, int scanPos, uint32_t width, int dist, int indexMaxValue, const BinFracBits* IndexfracBits, const BinFracBits& TypefracBits);
  void     initTBCTable           (int bitDepth);
  uint32_t getTruncBinBits        (uint32_t symbol, uint32_t maxSymbol);
  uint32_t getEpExGolombNumBins   (uint32_t symbol, uint32_t count);
  void xGetNextISPMode                    ( ModeInfo& modeInfo, const ModeInfo* lastMode, const Size cuSize );
  bool xSortISPCandList                   ( double bestCostSoFar, double bestNonISPCost, ModeInfo bestNonISPMode );
  void xSortISPCandListLFNST              ( );
  void xFindAlreadyTestedNearbyIntraModes ( int currentLfnstIdx, int currentIntraMode, int* refLfnstIdx, int* leftIntraMode, int* rightIntraMode, ISPType ispOption, int windowSize );
  bool updateISPStatusFromRelCU           ( double bestNonISPCostCurrCu, ModeInfo bestNonISPModeCurrCu, int& bestISPModeInRelCU );
  void xFinishISPModes                    ( );
#if JVET_Z0050_CCLM_SLOPE
  void xFindBestCclmDeltaSlopeSATD        ( PredictionUnit &pu, ComponentID compID, int cclmModel, int &deltaBest, int64_t &satdBest );
#endif
#if JVET_AA0126_GLM
  void xFindBestGlmIdcSATD                ( PredictionUnit &pu, ComponentID compID, int &idcBest, int64_t &satdBest );
#endif
#if JVET_AG0059_CCP_MERGE_ENHANCEMENT
  void getPredForCCPMrgFusion(PredictionUnit& pu, PelBuf& predCb, PelBuf& predCr);
  void xCalcCcpMrgPred(const PredictionUnit& pu, const ComponentID compID, PelBuf& piPredNonLm, PelBuf& piPredLm);
#endif
};// END CLASS DEFINITION EncSearch

//! \}

#endif // __ENCSEARCH__