Skip to content
Snippets Groups Projects
EncCu.cpp 173 KiB
Newer Older
  • Learn to ignore specific revisions
  • /* 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-2025, 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     EncCu.cpp
        \brief    Coding Unit (CU) encoder class
    */
    
    #include "EncCu.h"
    
    #include "EncLib.h"
    #include "Analyze.h"
    #include "AQp.h"
    
    #include "CommonLib/dtrace_codingstruct.h"
    #include "CommonLib/Picture.h"
    #include "CommonLib/UnitTools.h"
    
    Valeri George's avatar
    Valeri George committed
    #include "MCTS.h"
    
    
    
    #include "CommonLib/dtrace_buffer.h"
    
    #include <stdio.h>
    #include <cmath>
    #include <algorithm>
    
    //! \ingroup EncoderLib
    //! \{
    
    // ====================================================================================================================
    
    const MergeIdxPair EncCu::m_geoModeTest[GEO_MAX_NUM_CANDS] = {
      MergeIdxPair{ 0, 1 }, MergeIdxPair{ 1, 0 }, MergeIdxPair{ 0, 2 }, MergeIdxPair{ 1, 2 }, MergeIdxPair{ 2, 0 },
      MergeIdxPair{ 2, 1 }, MergeIdxPair{ 0, 3 }, MergeIdxPair{ 1, 3 }, MergeIdxPair{ 2, 3 }, MergeIdxPair{ 3, 0 },
      MergeIdxPair{ 3, 1 }, MergeIdxPair{ 3, 2 }, MergeIdxPair{ 0, 4 }, MergeIdxPair{ 1, 4 }, MergeIdxPair{ 2, 4 },
      MergeIdxPair{ 3, 4 }, MergeIdxPair{ 4, 0 }, MergeIdxPair{ 4, 1 }, MergeIdxPair{ 4, 2 }, MergeIdxPair{ 4, 3 },
      MergeIdxPair{ 0, 5 }, MergeIdxPair{ 1, 5 }, MergeIdxPair{ 2, 5 }, MergeIdxPair{ 3, 5 }, MergeIdxPair{ 4, 5 },
      MergeIdxPair{ 5, 0 }, MergeIdxPair{ 5, 1 }, MergeIdxPair{ 5, 2 }, MergeIdxPair{ 5, 3 }, MergeIdxPair{ 5, 4 }
    
    Frank Bossen's avatar
    Frank Bossen committed
    };
    
    EncCu::EncCu() {}
    
    
    void EncCu::create( EncCfg* encCfg )
    {
      unsigned      uiMaxWidth    = encCfg->getMaxCUWidth();
      unsigned      uiMaxHeight   = encCfg->getMaxCUHeight();
      ChromaFormat  chromaFormat  = encCfg->getChromaFormatIdc();
    
      unsigned      numWidths     = gp_sizeIdxInfo->numWidths();
      unsigned      numHeights    = gp_sizeIdxInfo->numHeights();
      m_pTempCS = new CodingStructure**  [numWidths];
      m_pBestCS = new CodingStructure**  [numWidths];
    
      m_pTempCS2 = new CodingStructure** [numWidths];
      m_pBestCS2 = new CodingStructure** [numWidths];
    
      m_pelUnitBufPool.initPelUnitBufPool(chromaFormat, uiMaxWidth, uiMaxHeight);
    
      m_mergeItemList.init(encCfg->getMaxMergeRdCandNumTotal(), chromaFormat, uiMaxWidth, uiMaxHeight);
    
      for( unsigned w = 0; w < numWidths; w++ )
      {
        m_pTempCS[w] = new CodingStructure*  [numHeights];
        m_pBestCS[w] = new CodingStructure*  [numHeights];
    
        m_pTempCS2[w] = new CodingStructure* [numHeights];
        m_pBestCS2[w] = new CodingStructure* [numHeights];
    
    
        for( unsigned h = 0; h < numHeights; h++ )
        {
          unsigned width  = gp_sizeIdxInfo->sizeFrom( w );
          unsigned height = gp_sizeIdxInfo->sizeFrom( h );
    
    
          if( gp_sizeIdxInfo->isCuSize( width ) && gp_sizeIdxInfo->isCuSize( height ) 
            && width <= uiMaxWidth && height <= uiMaxHeight)
    
            m_pTempCS[w][h] = new CodingStructure(m_unitPool);
            m_pBestCS[w][h] = new CodingStructure(m_unitPool);
    
            m_pTempCS[w][h]->create(chromaFormat, Area(0, 0, width, height), false, (bool)encCfg->getPLTMode());
            m_pBestCS[w][h]->create(chromaFormat, Area(0, 0, width, height), false, (bool)encCfg->getPLTMode());
    
            m_pTempCS2[w][h] = new CodingStructure(m_unitPool);
            m_pBestCS2[w][h] = new CodingStructure(m_unitPool);
    
            m_pTempCS2[w][h]->create(chromaFormat, Area(0, 0, width, height), false, (bool)encCfg->getPLTMode());
            m_pBestCS2[w][h]->create(chromaFormat, Area(0, 0, width, height), false, (bool)encCfg->getPLTMode());
    
          }
          else
          {
            m_pTempCS[w][h] = nullptr;
            m_pBestCS[w][h] = nullptr;
    
            m_pTempCS2[w][h] = nullptr;
            m_pBestCS2[w][h] = nullptr;
    
          }
        }
      }
    
      m_cuChromaQpOffsetIdxPlus1 = 0;
    
      unsigned maxDepth = numWidths + numHeights;
    
    
    Karsten Suehring's avatar
    Karsten Suehring committed
      m_modeCtrl = new EncModeCtrlMTnoRQT();
    
    Frank Bossen's avatar
    Frank Bossen committed
      m_ctxBuffer.resize(maxDepth);
    
      m_CurrCtx = 0;
    }
    
    
    void EncCu::destroy()
    {
      unsigned numWidths  = gp_sizeIdxInfo->numWidths();
      unsigned numHeights = gp_sizeIdxInfo->numHeights();
    
      for( unsigned w = 0; w < numWidths; w++ )
      {
        for( unsigned h = 0; h < numHeights; h++ )
        {
    
          if (m_pBestCS[w][h])
          {
            m_pBestCS[w][h]->destroy();
          }
          if (m_pTempCS[w][h])
          {
            m_pTempCS[w][h]->destroy();
          }
    
    Karsten Suehring's avatar
    Karsten Suehring committed
          delete m_pBestCS[w][h];
          delete m_pTempCS[w][h];
    
          if (m_pBestCS2[w][h])
          {
            m_pBestCS2[w][h]->destroy();
          }
          if (m_pTempCS2[w][h])
          {
            m_pTempCS2[w][h]->destroy();
          }
    
    
          delete m_pBestCS2[w][h];
          delete m_pTempCS2[w][h];
    
        delete[] m_pTempCS2[w];
        delete[] m_pBestCS2[w];
    
      }
    
      delete[] m_pBestCS; m_pBestCS = nullptr;
      delete[] m_pTempCS; m_pTempCS = nullptr;
    
      delete[] m_pBestCS2; m_pBestCS2 = nullptr;
      delete[] m_pTempCS2; m_pTempCS2 = nullptr;
    
    #if REUSE_CU_RESULTS
    
      if (m_tmpStorageCtu)
    
        m_tmpStorageCtu->destroy();
        delete m_tmpStorageCtu;
        m_tmpStorageCtu = nullptr;
    
    #if REUSE_CU_RESULTS
      m_modeCtrl->destroy();
    
    #endif
      delete m_modeCtrl;
      m_modeCtrl = nullptr;
    
    }
    
    EncCu::~EncCu()
    {
    }
    
    /** \param    pcEncLib      pointer of encoder class
     */
    
    void EncCu::init( EncLib* pcEncLib, const SPS& sps )
    
      m_pcIntraSearch      = pcEncLib->getIntraSearch();
      m_pcInterSearch      = pcEncLib->getInterSearch();
      m_pcTrQuant          = pcEncLib->getTrQuant();
      m_pcRdCost           = pcEncLib->getRdCost ();
      m_CABACEstimator     = pcEncLib->getCABACEncoder()->getCABACEstimator( &sps );
    
      m_ctxPool            = pcEncLib->getCtxCache();
    
      m_pcRateCtrl         = pcEncLib->getRateCtrl();
      m_pcSliceEncoder     = pcEncLib->getSliceEncoder();
    
      m_deblockingFilter   = pcEncLib->getDeblockingFilter();
    
    Frank Bossen's avatar
    Frank Bossen committed
      m_geoCostList.init(m_pcEncCfg->getMaxNumGeoCand());
    
    
      DecCu::init( m_pcTrQuant, m_pcIntraSearch, m_pcInterSearch );
    
      m_modeCtrl->init( m_pcEncCfg, m_pcRateCtrl, m_pcRdCost );
    
      m_modeCtrl->setBIMQPMap( m_pcEncCfg->getAdaptQPmap() );
    
      m_modeCtrl->setInterSearch(m_pcInterSearch);
    
      m_pcIntraSearch->setModeCtrl( m_modeCtrl );
    
    
      m_pcGOPEncoder = pcEncLib->getGOPEncoder();
      m_pcGOPEncoder->setModeCtrl( m_modeCtrl );
    
    }
    
    // ====================================================================================================================
    // Public member functions
    // ====================================================================================================================
    
    
    void EncCu::compressCtu(CodingStructure &cs, const UnitArea &area, const unsigned ctuRsAddr,
                            const EnumArray<int, ChannelType> &prevQP, const EnumArray<int, ChannelType> &currQP)
    
      cs.treeType = TREE_D;
    
      cs.slice->m_mapPltCost[0].clear();
      cs.slice->m_mapPltCost[1].clear();
    
      QTBTPartitioner partitioner;
    
      partitioner.initCtu(area, ChannelType::LUMA, *cs.slice);
    
    Yu Han's avatar
    Yu Han committed
      if (m_pcEncCfg->getIBCMode())
    
    Xiaozhong Xu's avatar
    Xiaozhong Xu committed
      {
    
        if (area.lx() == 0 && area.ly() == 0)
        {
          m_pcInterSearch->resetIbcSearch();
        }
    
    Xiaozhong Xu's avatar
    Xiaozhong Xu committed
        m_pcInterSearch->resetCtuRecord();
    
    Yu Han's avatar
    Yu Han committed
        m_ctuIbcSearchRangeX = m_pcEncCfg->getIBCLocalSearchRangeX();
        m_ctuIbcSearchRangeY = m_pcEncCfg->getIBCLocalSearchRangeY();
    
    Xiaozhong Xu's avatar
    Xiaozhong Xu committed
      }
    
    Yu Han's avatar
    Yu Han committed
      if (m_pcEncCfg->getIBCMode() && m_pcEncCfg->getIBCHashSearch() && (m_pcEncCfg->getIBCFastMethod() & IBC_FAST_METHOD_ADAPTIVE_SEARCHRANGE))
    
    Xiaozhong Xu's avatar
    Xiaozhong Xu committed
      {
    
    Yu Han's avatar
    Yu Han committed
        const int hashHitRatio = m_ibcHashMap.getHashHitRatio(area.Y()); // in percent
    
    Xiaozhong Xu's avatar
    Xiaozhong Xu committed
        if (hashHitRatio < 5) // 5%
        {
    
    Yu Han's avatar
    Yu Han committed
          m_ctuIbcSearchRangeX >>= 1;
          m_ctuIbcSearchRangeY >>= 1;
    
    Xiaozhong Xu's avatar
    Xiaozhong Xu committed
        }
    
    Yu Han's avatar
    Yu Han committed
        if (cs.slice->getNumRefIdx(REF_PIC_LIST_0) > 0)
    
    Xiaozhong Xu's avatar
    Xiaozhong Xu committed
        {
    
    Yu Han's avatar
    Yu Han committed
          m_ctuIbcSearchRangeX >>= 1;
          m_ctuIbcSearchRangeY >>= 1;
    
    Xiaozhong Xu's avatar
    Xiaozhong Xu committed
        }
      }
    
    Frank Bossen's avatar
    Frank Bossen committed
      m_CurrCtx = m_ctxBuffer.data();
    
    
      CodingStructure *tempCS = m_pTempCS[gp_sizeIdxInfo->idxFrom( area.lumaSize().width )][gp_sizeIdxInfo->idxFrom( area.lumaSize().height )];
      CodingStructure *bestCS = m_pBestCS[gp_sizeIdxInfo->idxFrom( area.lumaSize().width )][gp_sizeIdxInfo->idxFrom( area.lumaSize().height )];
    
    
      cs.initSubStructure(*tempCS, partitioner.chType, partitioner.currArea(), false);
      cs.initSubStructure(*bestCS, partitioner.chType, partitioner.currArea(), false);
    
      tempCS->currQP[ChannelType::LUMA] = bestCS->currQP[ChannelType::LUMA] = tempCS->baseQP = bestCS->baseQP =
        currQP[ChannelType::LUMA];
      tempCS->prevQP[ChannelType::LUMA] = bestCS->prevQP[ChannelType::LUMA] = prevQP[ChannelType::LUMA];
    
      xCompressCU(tempCS, bestCS, partitioner);
    
      cs.slice->m_mapPltCost[0].clear();
      cs.slice->m_mapPltCost[1].clear();
    
      // all signals were already copied during compression if the CTU was split - at this point only the structures are copied to the top level CS
    
    Taoran Lu's avatar
    Taoran Lu committed
      const bool copyUnsplitCTUSignals = bestCS->cus.size() == 1;
    
      cs.useSubStructure(*bestCS, partitioner.chType, CS::getArea(*bestCS, area, partitioner.chType), copyUnsplitCTUSignals,
    
                         false, false, copyUnsplitCTUSignals, true);
    
      if (CS::isDualITree (cs) && isChromaEnabled (cs.pcv->chrFormat))
    
        partitioner.initCtu(area, ChannelType::CHROMA, *cs.slice);
    
        cs.initSubStructure(*tempCS, partitioner.chType, partitioner.currArea(), false);
        cs.initSubStructure(*bestCS, partitioner.chType, partitioner.currArea(), false);
    
        tempCS->currQP[ChannelType::CHROMA] = bestCS->currQP[ChannelType::CHROMA] = tempCS->baseQP = bestCS->baseQP =
          currQP[ChannelType::CHROMA];
        tempCS->prevQP[ChannelType::CHROMA] = bestCS->prevQP[ChannelType::CHROMA] = prevQP[ChannelType::CHROMA];
    
        xCompressCU(tempCS, bestCS, partitioner);
    
    Taoran Lu's avatar
    Taoran Lu committed
        const bool copyUnsplitCTUSignals = bestCS->cus.size() == 1;
    
        cs.useSubStructure(*bestCS, partitioner.chType, CS::getArea(*bestCS, area, partitioner.chType),
    
                           copyUnsplitCTUSignals, false, false, copyUnsplitCTUSignals, true);
    
      if (m_pcEncCfg->getUseRateCtrl())
      {
        (m_pcRateCtrl->getRCPic()->getLCU(ctuRsAddr)).m_actualMSE = (double)bestCS->dist / (double)m_pcRateCtrl->getRCPic()->getLCU(ctuRsAddr).m_numberOfPixel;
      }
    
      // reset context states and uninit context pointer
      m_CABACEstimator->getCtx() = m_CurrCtx->start;
      m_CurrCtx                  = 0;
    
    
      // Ensure that a coding was found
      // Selected mode's RD-cost must be not MAX_DOUBLE.
      CHECK( bestCS->cus.empty()                                   , "No possible encoding found" );
      CHECK( bestCS->cus[0]->predMode == NUMBER_OF_PREDICTION_MODES, "No possible encoding found" );
      CHECK( bestCS->cost             == MAX_DOUBLE                , "No possible encoding found" );
    }
    
    // ====================================================================================================================
    // Protected member functions
    // ====================================================================================================================
    
    
    Frank Bossen's avatar
    Frank Bossen committed
    static int xCalcHADs8x8_ISlice(const Pel *piOrg, const ptrdiff_t strideOrg)
    
    {
      int k, i, j, jj;
      int diff[64], m1[8][8], m2[8][8], m3[8][8], iSumHad = 0;
    
      for (k = 0; k < 64; k += 8)
      {
        diff[k + 0] = piOrg[0];
        diff[k + 1] = piOrg[1];
        diff[k + 2] = piOrg[2];
        diff[k + 3] = piOrg[3];
        diff[k + 4] = piOrg[4];
        diff[k + 5] = piOrg[5];
        diff[k + 6] = piOrg[6];
        diff[k + 7] = piOrg[7];
    
    
    Frank Bossen's avatar
    Frank Bossen committed
        piOrg += strideOrg;
    
      }
    
      //horizontal
      for (j = 0; j < 8; j++)
      {
        jj = j << 3;
        m2[j][0] = diff[jj    ] + diff[jj + 4];
        m2[j][1] = diff[jj + 1] + diff[jj + 5];
        m2[j][2] = diff[jj + 2] + diff[jj + 6];
        m2[j][3] = diff[jj + 3] + diff[jj + 7];
        m2[j][4] = diff[jj    ] - diff[jj + 4];
        m2[j][5] = diff[jj + 1] - diff[jj + 5];
        m2[j][6] = diff[jj + 2] - diff[jj + 6];
        m2[j][7] = diff[jj + 3] - diff[jj + 7];
    
        m1[j][0] = m2[j][0] + m2[j][2];
        m1[j][1] = m2[j][1] + m2[j][3];
        m1[j][2] = m2[j][0] - m2[j][2];
        m1[j][3] = m2[j][1] - m2[j][3];
        m1[j][4] = m2[j][4] + m2[j][6];
        m1[j][5] = m2[j][5] + m2[j][7];
        m1[j][6] = m2[j][4] - m2[j][6];
        m1[j][7] = m2[j][5] - m2[j][7];
    
        m2[j][0] = m1[j][0] + m1[j][1];
        m2[j][1] = m1[j][0] - m1[j][1];
        m2[j][2] = m1[j][2] + m1[j][3];
        m2[j][3] = m1[j][2] - m1[j][3];
        m2[j][4] = m1[j][4] + m1[j][5];
        m2[j][5] = m1[j][4] - m1[j][5];
        m2[j][6] = m1[j][6] + m1[j][7];
        m2[j][7] = m1[j][6] - m1[j][7];
      }
    
      //vertical
      for (i = 0; i < 8; i++)
      {
        m3[0][i] = m2[0][i] + m2[4][i];
        m3[1][i] = m2[1][i] + m2[5][i];
        m3[2][i] = m2[2][i] + m2[6][i];
        m3[3][i] = m2[3][i] + m2[7][i];
        m3[4][i] = m2[0][i] - m2[4][i];
        m3[5][i] = m2[1][i] - m2[5][i];
        m3[6][i] = m2[2][i] - m2[6][i];
        m3[7][i] = m2[3][i] - m2[7][i];
    
        m1[0][i] = m3[0][i] + m3[2][i];
        m1[1][i] = m3[1][i] + m3[3][i];
        m1[2][i] = m3[0][i] - m3[2][i];
        m1[3][i] = m3[1][i] - m3[3][i];
        m1[4][i] = m3[4][i] + m3[6][i];
        m1[5][i] = m3[5][i] + m3[7][i];
        m1[6][i] = m3[4][i] - m3[6][i];
        m1[7][i] = m3[5][i] - m3[7][i];
    
        m2[0][i] = m1[0][i] + m1[1][i];
        m2[1][i] = m1[0][i] - m1[1][i];
        m2[2][i] = m1[2][i] + m1[3][i];
        m2[3][i] = m1[2][i] - m1[3][i];
        m2[4][i] = m1[4][i] + m1[5][i];
        m2[5][i] = m1[4][i] - m1[5][i];
        m2[6][i] = m1[6][i] + m1[7][i];
        m2[7][i] = m1[6][i] - m1[7][i];
      }
    
      for (i = 0; i < 8; i++)
      {
        for (j = 0; j < 8; j++)
        {
          iSumHad += abs(m2[i][j]);
        }
      }
      iSumHad -= abs(m2[0][0]);
      iSumHad = (iSumHad + 2) >> 2;
      return(iSumHad);
    }
    
    int  EncCu::updateCtuDataISlice(const CPelBuf buf)
    {
      int  xBl, yBl;
      const int iBlkSize = 8;
      const Pel* pOrgInit = buf.buf;
    
    Frank Bossen's avatar
    Frank Bossen committed
      ptrdiff_t  iStrideOrig = buf.stride;
    
    
      int iSumHad = 0;
      for( yBl = 0; ( yBl + iBlkSize ) <= buf.height; yBl += iBlkSize )
      {
        for( xBl = 0; ( xBl + iBlkSize ) <= buf.width; xBl += iBlkSize )
        {
          const Pel* pOrg = pOrgInit + iStrideOrig*yBl + xBl;
          iSumHad += xCalcHADs8x8_ISlice( pOrg, iStrideOrig );
        }
      }
      return( iSumHad );
    }
    
    
    bool EncCu::xCheckBestMode( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode )
    
      bool bestCSUpdated = false;
    
      if( !tempCS->cus.empty() )
      {
        if( tempCS->cus.size() == 1 )
        {
          const CodingUnit& cu = *tempCS->cus.front();
          CHECK( cu.skip && !cu.firstPU->mergeFlag, "Skip flag without a merge flag is not allowed!" );
        }
    
    #if WCG_EXT
        DTRACE_BEST_MODE( tempCS, bestCS, m_pcRdCost->getLambda( true ) );
    #else
        DTRACE_BEST_MODE( tempCS, bestCS, m_pcRdCost->getLambda() );
    #endif
    
        if( m_modeCtrl->useModeResult( encTestMode, tempCS, partitioner ) )
        {
          std::swap( tempCS, bestCS );
          // store temp best CI for next CU coding
          m_CurrCtx->best = m_CABACEstimator->getCtx();
    
    Nan Hu's avatar
    Nan Hu committed
          m_bestModeUpdated = true;
    
          bestCSUpdated = true;
    
        }
      }
    
      // reset context states
      m_CABACEstimator->getCtx() = m_CurrCtx->start;
    
      return bestCSUpdated;
    
    void EncCu::xCompressCU( CodingStructure*& tempCS, CodingStructure*& bestCS, Partitioner& partitioner, double maxCostAllowed )
    
      CHECK(maxCostAllowed < 0, "Wrong value of maxCostAllowed!");
    
      if (partitioner.isSepTree( *tempCS ))
    
        if( !CS::isDualITree(*tempCS) && partitioner.treeType != TREE_D )
        {
          compBegin = COMPONENT_Y;
    
          numComp   = getNumberValidComponents(tempCS->area.chromaFormat);
    
          if (isLuma(partitioner.chType))
          {
            compBegin = COMPONENT_Y;
            numComp   = 1;
          }
          else
          {
            compBegin = COMPONENT_Cb;
            numComp   = 2;
          }
    
        numComp   = getNumberValidComponents(tempCS->area.chromaFormat);
    
      uint8_t   bestLastPLTSize[MAX_NUM_CHANNEL_TYPE];
    
      Pel       bestLastPLT[MAX_NUM_COMPONENT][MAXPLTPREDSIZE]; // store LastPLT for
    
      uint8_t   curLastPLTSize[MAX_NUM_CHANNEL_TYPE];
    
      Pel       curLastPLT[MAX_NUM_COMPONENT][MAXPLTPREDSIZE]; // store LastPLT if no partition
    
      for (int i = compBegin; i < (compBegin + numComp); i++)
    
        ComponentID comID = jointPLT ? (ComponentID)compBegin : ((i > 0) ? COMPONENT_Cb : COMPONENT_Y);
        bestLastPLTSize[comID] = 0;
        curLastPLTSize[comID] = tempCS->prevPLT.curPLTSize[comID];
        memcpy(curLastPLT[i], tempCS->prevPLT.curPLT[i], tempCS->prevPLT.curPLTSize[comID] * sizeof(Pel));
    
    
      Slice&   slice      = *tempCS->slice;
      const PPS &pps      = *tempCS->pps;
      const SPS &sps      = *tempCS->sps;
      const uint32_t uiLPelX  = tempCS->area.Y().lumaPos().x;
      const uint32_t uiTPelY  = tempCS->area.Y().lumaPos().y;
    
    
      const ModeType modeTypeParent  = partitioner.modeType;
      const TreeType treeTypeParent  = partitioner.treeType;
      const ChannelType chTypeParent = partitioner.chType;
    
      const UnitArea currCsArea = clipArea( CS::getArea( *bestCS, bestCS->area, partitioner.chType ), *tempCS->picture );
    
    
      tempCS->splitRdCostBest = nullptr;
    
    Junjie Chen's avatar
    Junjie Chen committed
      if (m_pcEncCfg->getDPF())
      {
        m_modeCtrl->setCurrCsArea(currCsArea);
        m_modeCtrl->setQpCtu(m_pcSliceEncoder->getQpCtu());
      }
    
      m_modeCtrl->initCULevel( partitioner, *tempCS );
    
    Frank Bossen's avatar
    Frank Bossen committed
    #if GDR_ENABLED
    
        bool isInGdrInterval = slice.getPic()->gdrParam.inGdrInterval;
    
    Frank Bossen's avatar
    Frank Bossen committed
        // 1.0 applicable to inter picture only
    
        {
          int gdrPocStart = m_pcEncCfg->getGdrPocStart();
          int gdrInterval = m_pcEncCfg->getGdrInterval();
    
          int gdrPeriod = m_pcEncCfg->getGdrPeriod();
    
          int picWidth = slice.getPPS()->getPicWidthInLumaSamples();
          int m1, m2, n1;
    
          int gdrPoc = (curPoc - gdrPocStart) % gdrPeriod;
    
          double dd = (picWidth / (double)gdrInterval);
          int mm = (int)((picWidth / (double)gdrInterval) + 0.49999);
          m1 = ((mm + 7) >> 3) << 3;
          m2 = ((mm + 0) >> 3) << 3;
    
          if (gdrPoc < n1)
          {
            begGdrX = m1 * gdrPoc;
            endGdrX = begGdrX + m1;
          }
          else
          {
            begGdrX = m1 * n1 + m2 * (gdrPoc - n1);
            endGdrX = begGdrX + m2;
            if (picWidth <= endGdrX)
    
          bool isInRefreshArea = tempCS->withinRefresh(begGdrX, endGdrX);
    
          if (isInRefreshArea)
    
          else if (tempCS->containRefresh(begGdrX, endGdrX) || tempCS->overlapRefresh(begGdrX, endGdrX))
          {
    
    Frank Bossen's avatar
    Frank Bossen committed
            // 1.3.1 enable only vertical splits (QT, BT_V, TT_V)
    
            // 1.3.2 remove TT_V if it does not satisfy the condition
            if (tempCS->refreshCrossTTV(begGdrX, endGdrX))
            {
              m_modeCtrl->forceRemoveTTV();
            }
          }
    
          if (tempCS->area.lwidth() != tempCS->area.lheight())
          {
            m_modeCtrl->forceRemoveQT();
          }
    
          if (!m_modeCtrl->anyPredModeLeft())
          {
            m_modeCtrl->forceRemoveDontSplit();
          }
    
          if (isInRefreshArea && !m_modeCtrl->anyIntraIBCMode() && (tempCS->area.lwidth() == 4 || tempCS->area.lheight() == 4))
          {
            m_modeCtrl->finishCULevel(partitioner);
            return;
          }
    
      if (partitioner.currQtDepth == 0 && partitioner.currMtDepth == 0 && !tempCS->slice->isIntra()
          && (sps.getUseSBT() || sps.getExplicitMtsInterEnabled()))
    
      {
        auto slsSbt = dynamic_cast<SaveLoadEncInfoSbt*>( m_modeCtrl );
    
        int maxSLSize = sps.getUseSBT() ? tempCS->slice->getSPS()->getMaxTbSize() : MTS_INTER_MAX_CU_SIZE;
    
        slsSbt->resetSaveloadSbt( maxSLSize );
      }
      m_sbtCostSave[0] = m_sbtCostSave[1] = MAX_DOUBLE;
    
    
      m_CurrCtx->start = m_CABACEstimator->getCtx();
    
      if( slice.getUseChromaQpAdj() )
      {
    
        // TODO M0133 : double check encoder decisions with respect to chroma QG detection and actual encode
        int lgMinCuSize = sps.getLog2MinCodingBlockSize() +
    
          std::max<int>(0, floorLog2(sps.getCTUSize()) - sps.getLog2MinCodingBlockSize() - int((slice.getCuChromaQpOffsetSubdiv()+1) / 2));
    
        if( partitioner.currQgChromaEnable() )
    
        {
          m_cuChromaQpOffsetIdxPlus1 = ( ( uiLPelX >> lgMinCuSize ) + ( uiTPelY >> lgMinCuSize ) ) % ( pps.getChromaQpOffsetListLen() + 1 );
        }
    
    
      if( !m_modeCtrl->anyMode() )
      {
        m_modeCtrl->finishCULevel( partitioner );
        return;
      }
    
      DTRACE_UPDATE( g_trace_ctx, std::make_pair( "cux", uiLPelX ) );
      DTRACE_UPDATE( g_trace_ctx, std::make_pair( "cuy", uiTPelY ) );
      DTRACE_UPDATE( g_trace_ctx, std::make_pair( "cuw", tempCS->area.lwidth() ) );
      DTRACE_UPDATE( g_trace_ctx, std::make_pair( "cuh", tempCS->area.lheight() ) );
      DTRACE( g_trace_ctx, D_COMMON, "@(%4d,%4d) [%2dx%2d]\n", tempCS->area.lx(), tempCS->area.ly(), tempCS->area.lwidth(), tempCS->area.lheight() );
    
    
      m_pcInterSearch->resetSavedAffineMotion();
    
      double bestIntPelCost = MAX_DOUBLE;
    
    
      if (tempCS->slice->getSPS()->getUseColorTrans())
      {
        tempCS->tmpColorSpaceCost = MAX_DOUBLE;
        bestCS->tmpColorSpaceCost = MAX_DOUBLE;
        tempCS->firstColorSpaceSelected = true;
        bestCS->firstColorSpaceSelected = true;
      }
    
      if (tempCS->slice->getSPS()->getUseColorTrans() && !CS::isDualITree(*tempCS))
      {
        tempCS->firstColorSpaceTestOnly = false;
        bestCS->firstColorSpaceTestOnly = false;
        tempCS->tmpColorSpaceIntraCost[0] = MAX_DOUBLE;
        tempCS->tmpColorSpaceIntraCost[1] = MAX_DOUBLE;
        bestCS->tmpColorSpaceIntraCost[0] = MAX_DOUBLE;
        bestCS->tmpColorSpaceIntraCost[1] = MAX_DOUBLE;
    
        if (tempCS->bestParent && tempCS->bestParent->firstColorSpaceTestOnly)
        {
          tempCS->firstColorSpaceTestOnly = bestCS->firstColorSpaceTestOnly = true;
        }
      }
    
    
      double splitRdCostBest[NUM_PART_SPLIT];
      std::fill(std::begin(splitRdCostBest), std::end(splitRdCostBest), MAX_DOUBLE);
    
      if (tempCS->slice->getCheckLDC())
      {
    
        m_bestBcwCost.fill(std::numeric_limits<double>::max());
        m_bestBcwIdx.fill(BCW_NUM);
    
        for (int i = compBegin; i < (compBegin + numComp); i++)
        {
          ComponentID comID = jointPLT ? (ComponentID)compBegin : ((i > 0) ? COMPONENT_Cb : COMPONENT_Y);
          tempCS->prevPLT.curPLTSize[comID] = curLastPLTSize[comID];
          memcpy(tempCS->prevPLT.curPLT[i], curLastPLT[i], curLastPLTSize[comID] * sizeof(Pel));
        }
    
        EncTestMode currTestMode = m_modeCtrl->currTestMode();
    
        currTestMode.maxCostAllowed = maxCostAllowed;
    
        if (pps.getUseDQP() && partitioner.isSepTree(*tempCS) && isChroma( partitioner.chType ))
    
        {
          const Position chromaCentral(tempCS->area.Cb().chromaPos().offset(tempCS->area.Cb().chromaSize().width >> 1, tempCS->area.Cb().chromaSize().height >> 1));
          const Position lumaRefPos(chromaCentral.x << getComponentScaleX(COMPONENT_Cb, tempCS->area.chromaFormat), chromaCentral.y << getComponentScaleY(COMPONENT_Cb, tempCS->area.chromaFormat));
          const CodingStructure* baseCS = bestCS->picture->cs;
    
          const CodingUnit      *colLumaCu = baseCS->getCU(lumaRefPos, ChannelType::LUMA);
    
    #if SHARP_LUMA_DELTA_QP || ENABLE_QPA_SUB_CTU
    
        if (partitioner.currQgEnable() && (
    
            (m_pcEncCfg->getBIM()) ||
    
            (m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled()) ||
    #endif
    
            (m_pcEncCfg->getSmoothQPReductionEnable()) ||
    
    #if ENABLE_QPA_SUB_CTU
            (m_pcEncCfg->getUsePerceptQPA() && !m_pcEncCfg->getUseRateCtrl() && pps.getUseDQP())
    #else
            false
    #endif
          ))
    
            updateLambda (&slice, currTestMode.qp,
     #if WCG_EXT && ER_CHROMA_QP_WCG_PPS
                          m_pcEncCfg->getWCGChromaQPControl().isEnabled(),
     #endif
                          CS::isDualITree (*tempCS) || (partitioner.currDepth == 0));
    
          }
        }
    #endif
    
        if( currTestMode.type == ETM_INTER_ME )
        {
          if( ( currTestMode.opts & ETO_IMV ) != 0 )
          {
    
            const bool skipAltHpelIF = (currTestMode.getAmvrSearchMode() == EncTestMode::AmvrSearchMode::HALF_PEL)
                                       && (bestIntPelCost > 1.25 * bestCS->cost);
    
            if (!skipAltHpelIF)
    
              tempCS->bestCS = bestCS;
    
              xCheckRDCostInterAmvr(tempCS, bestCS, partitioner, currTestMode, bestIntPelCost);
    
              tempCS->bestCS = nullptr;
    
              splitRdCostBest[CTU_LEVEL] = bestCS->cost;
              tempCS->splitRdCostBest = splitRdCostBest;
    
            tempCS->bestCS = bestCS;
    
            xCheckRDCostInter( tempCS, bestCS, partitioner, currTestMode );
    
            tempCS->bestCS = nullptr;
    
            splitRdCostBest[CTU_LEVEL] = bestCS->cost;
            tempCS->splitRdCostBest = splitRdCostBest;
    
        else if (currTestMode.type == ETM_HASH_INTER)
        {
          xCheckRDCostHashInter( tempCS, bestCS, partitioner, currTestMode );
    
          splitRdCostBest[CTU_LEVEL] = bestCS->cost;
          tempCS->splitRdCostBest = splitRdCostBest;
    
    #if REUSE_CU_RESULTS
        else if( currTestMode.type == ETM_RECO_CACHED )
        {
          xReuseCachedResult( tempCS, bestCS, partitioner );
    
          splitRdCostBest[CTU_LEVEL] = bestCS->cost;
          tempCS->splitRdCostBest = splitRdCostBest;
    
        }
    #endif
        else if( currTestMode.type == ETM_MERGE_SKIP )
        {
    
          xCheckRDCostUnifiedMerge(tempCS, bestCS, partitioner, currTestMode);
    
          CodingUnit* cu = bestCS->getCU(partitioner.chType);
    
    Xiaozhong Xu's avatar
    Xiaozhong Xu committed
          if (cu)
    
          {
            cu->mmvdSkip = cu->skip == false ? false : cu->mmvdSkip;
          }
    
          splitRdCostBest[CTU_LEVEL] = bestCS->cost;
          tempCS->splitRdCostBest = splitRdCostBest;
    
          if (slice.getSPS()->getUseColorTrans() && !CS::isDualITree(*tempCS))
          {
    
            bool skipSecColorSpace = false;
            skipSecColorSpace = xCheckRDCostIntra(tempCS, bestCS, partitioner, currTestMode, (m_pcEncCfg->getRGBFormatFlag() ? true : false));
    
            if ((m_pcEncCfg->getCostMode() == COST_LOSSLESS_CODING && slice.isLossless()) && !m_pcEncCfg->getRGBFormatFlag())
    
            if (!skipSecColorSpace && !tempCS->firstColorSpaceTestOnly)
    
            {
              xCheckRDCostIntra(tempCS, bestCS, partitioner, currTestMode, (m_pcEncCfg->getRGBFormatFlag() ? false : true));
            }
    
            if (!tempCS->firstColorSpaceTestOnly)
            {
              if (tempCS->tmpColorSpaceIntraCost[0] != MAX_DOUBLE && tempCS->tmpColorSpaceIntraCost[1] != MAX_DOUBLE)
              {
                double skipCostRatio = m_pcEncCfg->getRGBFormatFlag() ? 1.1 : 1.0;
                if (tempCS->tmpColorSpaceIntraCost[1] > (skipCostRatio*tempCS->tmpColorSpaceIntraCost[0]))
                {
                  tempCS->firstColorSpaceTestOnly = bestCS->firstColorSpaceTestOnly = true;
                }
              }
            }
            else
            {
              CHECK(tempCS->tmpColorSpaceIntraCost[1] != MAX_DOUBLE, "the RD test of the second color space should be skipped");
            }
          }
          else
          {
            xCheckRDCostIntra(tempCS, bestCS, partitioner, currTestMode, false);
          }
    
          if (partitioner.currQtDepth == 1 && partitioner.currBtDepth == 0 && partitioner.currArea().lwidth() == 64
              && partitioner.currArea().lheight() == 64)
          {
    
            if ((partitioner.chType == ChannelType::LUMA)
    
                && ((partitioner.currArea().Y().x + 63 < bestCS->picture->lwidth())
                    && (partitioner.currArea().Y().y + 63 < bestCS->picture->lheight())))
            {      
              m_modeCtrl->setNoSplitIntraCost(bestCS->cost);
            }
          }
    
    
          splitRdCostBest[CTU_LEVEL] = bestCS->cost;
          tempCS->splitRdCostBest = splitRdCostBest;
    
        else if (currTestMode.type == ETM_PALETTE)
        {
          xCheckPLT( tempCS, bestCS, partitioner, currTestMode );
    
          splitRdCostBest[CTU_LEVEL] = bestCS->cost;
          tempCS->splitRdCostBest = splitRdCostBest;
    
    Yu Han's avatar
    Yu Han committed
        else if (currTestMode.type == ETM_IBC)
    
    Xiaozhong Xu's avatar
    Xiaozhong Xu committed
        {
    
    Yu Han's avatar
    Yu Han committed
          xCheckRDCostIBCMode(tempCS, bestCS, partitioner, currTestMode);
    
          splitRdCostBest[CTU_LEVEL] = bestCS->cost;
          tempCS->splitRdCostBest = splitRdCostBest;
    
    Xiaozhong Xu's avatar
    Xiaozhong Xu committed
        }
    
    Yu Han's avatar
    Yu Han committed
        else if (currTestMode.type == ETM_IBC_MERGE)
    
    Xiaozhong Xu's avatar
    Xiaozhong Xu committed
        {
    
    Yu Han's avatar
    Yu Han committed
          xCheckRDCostIBCModeMerge2Nx2N(tempCS, bestCS, partitioner, currTestMode);
    
          splitRdCostBest[CTU_LEVEL] = bestCS->cost;
          tempCS->splitRdCostBest = splitRdCostBest;
    
    Xiaozhong Xu's avatar
    Xiaozhong Xu committed
        }
    
          if (bestCS->cus.size() != 0)
          {
            splitmode = bestCS->cus[0]->splitSeries;
          }
    
          assert( partitioner.modeType == tempCS->modeType );
          int signalModeConsVal = tempCS->signalModeCons( getPartSplit( currTestMode ), partitioner, modeTypeParent );
    
          int numRoundRdo = signalModeConsVal == LDT_MODE_TYPE_SIGNAL ? 2 : 1;
    
          bool skipInterPass = false;
    
          for( int i = 0; i < numRoundRdo; i++ )
    
            if( signalModeConsVal == LDT_MODE_TYPE_SIGNAL )
    
              CHECK( numRoundRdo != 2, "numRoundRdo shall be 2 - [LDT_MODE_TYPE_SIGNAL]" );
    
              tempCS->modeType = partitioner.modeType = (i == 0) ? MODE_TYPE_INTER : MODE_TYPE_INTRA;
            }
    
            else if( signalModeConsVal == LDT_MODE_TYPE_INFER )
    
              CHECK( numRoundRdo != 1, "numRoundRdo shall be 1 - [LDT_MODE_TYPE_INFER]" );
    
              tempCS->modeType = partitioner.modeType = MODE_TYPE_INTRA;
            }
    
            else if( signalModeConsVal == LDT_MODE_TYPE_INHERIT )
    
              CHECK( numRoundRdo != 1, "numRoundRdo shall be 1 - [LDT_MODE_TYPE_INHERIT]" );
    
              tempCS->modeType = partitioner.modeType = modeTypeParent;
            }
    
            //for lite intra encoding fast algorithm, set the status to save inter coding info
            if( modeTypeParent == MODE_TYPE_ALL && tempCS->modeType == MODE_TYPE_INTER )
            {
              m_pcIntraSearch->setSaveCuCostInSCIPU( true );
              m_pcIntraSearch->setNumCuInSCIPU( 0 );
            }
            else if( modeTypeParent == MODE_TYPE_ALL && tempCS->modeType != MODE_TYPE_INTER )
            {
              m_pcIntraSearch->setSaveCuCostInSCIPU( false );
              if( tempCS->modeType == MODE_TYPE_ALL )
              {
                m_pcIntraSearch->setNumCuInSCIPU( 0 );
              }
            }
    
    
            xCheckModeSplit( tempCS, bestCS, partitioner, currTestMode, modeTypeParent, skipInterPass, splitRdCostBest );
            tempCS->splitRdCostBest = splitRdCostBest;
    
            //recover cons modes
            tempCS->modeType = partitioner.modeType = modeTypeParent;
            tempCS->treeType = partitioner.treeType = treeTypeParent;
            partitioner.chType = chTypeParent;
            if( modeTypeParent == MODE_TYPE_ALL )
            {
              m_pcIntraSearch->setSaveCuCostInSCIPU( false );
    
              if( numRoundRdo == 2 && tempCS->modeType == MODE_TYPE_INTRA )
    
              {
                m_pcIntraSearch->initCuAreaCostInSCIPU();
              }
            }
            if( skipInterPass )
            {
              break;
            }
          }
    
    #if GDR_ENABLED
          if (bestCS->cus.size() > 0 && splitmode != bestCS->cus[0]->splitSeries)
    #else
    
          if (splitmode != bestCS->cus[0]->splitSeries)
    
    #endif
    
            splitmode = bestCS->cus[0]->splitSeries;
            const CodingUnit&     cu = *bestCS->cus.front();
            cu.cs->prevPLT = bestCS->prevPLT;
            for (int i = compBegin; i < (compBegin + numComp); i++)
            {
              ComponentID comID = jointPLT ? (ComponentID)compBegin : ((i > 0) ? COMPONENT_Cb : COMPONENT_Y);
              bestLastPLTSize[comID] = bestCS->cus[0]->cs->prevPLT.curPLTSize[comID];
              memcpy(bestLastPLT[i], bestCS->cus[0]->cs->prevPLT.curPLT[i], bestCS->cus[0]->cs->prevPLT.curPLTSize[comID] * sizeof(Pel));
            }
    
    Karsten Suehring's avatar
    Karsten Suehring committed
          THROW( "Don't know how to handle mode: type = " << currTestMode.type << ", options = " << currTestMode.opts );
    
        }
      } while( m_modeCtrl->nextMode( *tempCS, partitioner ) );
    
    
      //////////////////////////////////////////////////////////////////////////
      // Finishing CU
    
      if( tempCS->cost == MAX_DOUBLE && bestCS->cost == MAX_DOUBLE )
      {
        //although some coding modes were planned to be tried in RDO, no coding mode actually finished encoding due to early termination
        //thus tempCS->cost and bestCS->cost are both MAX_DOUBLE; in this case, skip the following process for normal case
        m_modeCtrl->finishCULevel( partitioner );
        return;
      }
    
    
      // set context states
      m_CABACEstimator->getCtx() = m_CurrCtx->best;
    
      // QP from last processed CU for further processing
    
    Yin Zhao's avatar
    Yin Zhao committed
      //copy the qp of the last non-chroma CU
      int numCUInThisNode = (int)bestCS->cus.size();
    
      if (numCUInThisNode > 1 && bestCS->cus.back()->chType == ChannelType::CHROMA && !CS::isDualITree(*bestCS))
    
        CHECK(bestCS->cus[numCUInThisNode - 2]->chType != ChannelType::LUMA, "wrong chType");
        bestCS->prevQP[partitioner.chType] = bestCS->cus[numCUInThisNode - 2]->qp;
    
        bestCS->prevQP[partitioner.chType] = bestCS->cus.back()->qp;
    
      if ((!slice.isIntra() || slice.getSPS()->getIBCFlag()) && isLuma(partitioner.chType) && bestCS->cus.size() == 1
          && (CU::isInter(*bestCS->cus.back()) || CU::isIBC(*bestCS->cus.back()))
    
          && bestCS->area.Y() == (*bestCS->cus.back()).Y())
    
        const CodingUnit&     cu = *bestCS->cus.front();
    
    
        CU::saveMotionForHmvp(cu);
    
    Taoran Lu's avatar
    Taoran Lu committed
      bestCS->picture->getPredBuf(currCsArea).copyFrom(bestCS->getPredBuf(currCsArea));
    
      bestCS->picture->getRecoBuf( currCsArea ).copyFrom( bestCS->getRecoBuf( currCsArea ) );
      m_modeCtrl->finishCULevel( partitioner );
    
      if( m_pcIntraSearch->getSaveCuCostInSCIPU() && bestCS->cus.size() == 1 )
      {
        m_pcIntraSearch->saveCuAreaCostInSCIPU( Area( partitioner.currArea().lumaPos(), partitioner.currArea().lumaSize() ), bestCS->cost );
      }