Skip to content
Snippets Groups Projects
Select Git revision
  • 129ea76985783a9f88b4a6a2f326abb28ecf7551
  • master default protected
  • N0867-Temp-Scal-HRD
  • VTM-6.1
  • VTM-6.0
  • VTM-6.0rc1
  • VTM-5.2
  • VTM-5.1
  • VTM-5.1rc1
  • VTM-5.0
  • VTM-5.0rc1
  • VTM-4.2
  • VTM-4.1
  • VTM-4.0.1
  • VTM-4.0.1rc1
  • VTM-4.0
  • VTM-4.0rc1
  • VTM-3.2
  • VTM-3.1
  • VTM-3.1rc1
  • VTM-3.0
  • VTM-3.0rc1
  • VTM-2.2
23 results

EncModeCtrl.cpp

Blame
  • Forked from jvet / VVCSoftware_VTM
    Source project has a limited visibility.
    EncModeCtrl.cpp 52.92 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-2018, 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     EncModeCtrl.cpp
        \brief    Encoder controller for trying out specific modes
    */
    
    #include "EncModeCtrl.h"
    
    #include "AQp.h"
    #include "RateCtrl.h"
    
    #include "CommonLib/RdCost.h"
    #include "CommonLib/CodingStructure.h"
    #include "CommonLib/Picture.h"
    #include "CommonLib/UnitTools.h"
    
    #include "CommonLib/dtrace_next.h"
    
    #include <cmath>
    
    void EncModeCtrl::init( EncCfg *pCfg, RateCtrl *pRateCtrl, RdCost* pRdCost )
    {
      m_pcEncCfg      = pCfg;
      m_pcRateCtrl    = pRateCtrl;
      m_pcRdCost      = pRdCost;
      m_fastDeltaQP   = false;
    #if SHARP_LUMA_DELTA_QP
      m_lumaQPOffset  = 0;
    
      initLumaDeltaQpLUT();
    #endif
    }
    
    bool EncModeCtrl::tryModeMaster( const EncTestMode& encTestmode, const CodingStructure &cs, Partitioner& partitioner )
    {
    #if ENABLE_SPLIT_PARALLELISM
      if( m_ComprCUCtxList.back().isLevelSplitParallel )
      {
        if( !parallelJobSelector( encTestmode, cs, partitioner ) )
        {
          return false;
        }
      }
    #endif
      return tryMode( encTestmode, cs, partitioner );
    }
    
    void EncModeCtrl::setEarlySkipDetected()
    {
      m_ComprCUCtxList.back().earlySkip = true;
    }
    
    void EncModeCtrl::xExtractFeatures( const EncTestMode encTestmode, CodingStructure& cs )
    {
      CHECK( cs.features.size() < NUM_ENC_FEATURES, "Features vector is not initialized" );
    
      cs.features[ENC_FT_DISTORTION     ] = double( cs.dist              );
      cs.features[ENC_FT_FRAC_BITS      ] = double( cs.fracBits          );
      cs.features[ENC_FT_RD_COST        ] = double( cs.cost              );
      cs.features[ENC_FT_ENC_MODE_TYPE  ] = double( encTestmode.type     );
      cs.features[ENC_FT_ENC_MODE_OPTS  ] = double( encTestmode.opts     );
      cs.features[ENC_FT_ENC_MODE_PART  ] = double( encTestmode.partSize );
    }
    
    bool EncModeCtrl::nextMode( const CodingStructure &cs, Partitioner &partitioner )
    {
      m_ComprCUCtxList.back().lastTestMode = m_ComprCUCtxList.back().testModes.back();
    
      m_ComprCUCtxList.back().testModes.pop_back();
    
      while( !m_ComprCUCtxList.back().testModes.empty() && !tryModeMaster( currTestMode(), cs, partitioner ) )
      {
        m_ComprCUCtxList.back().testModes.pop_back();
      }
    
      return !m_ComprCUCtxList.back().testModes.empty();
    }
    
    EncTestMode EncModeCtrl::currTestMode() const
    {
      return m_ComprCUCtxList.back().testModes.back();
    }
    
    EncTestMode EncModeCtrl::lastTestMode() const
    {
      return m_ComprCUCtxList.back().lastTestMode;
    }
    
    bool EncModeCtrl::anyMode() const
    {
      return !m_ComprCUCtxList.back().testModes.empty();
    }
    
    void EncModeCtrl::setBest( CodingStructure& cs )
    {
      if( cs.cost != MAX_DOUBLE && !cs.cus.empty() )
      {
        m_ComprCUCtxList.back().bestCS = &cs;
        m_ComprCUCtxList.back().bestCU = cs.cus[0];
        m_ComprCUCtxList.back().bestTU = cs.cus[0]->firstTU;
        m_ComprCUCtxList.back().lastTestMode = getCSEncMode( cs );
      }
    }
    
    void EncModeCtrl::xGetMinMaxQP( int& minQP, int& maxQP, const CodingStructure& cs, const Partitioner &partitioner, const int baseQP, const SPS& sps, const PPS& pps, const bool splitMode )
    {
      if( m_pcEncCfg->getUseRateCtrl() )
      {
        minQP = m_pcRateCtrl->getRCQP();
        maxQP = m_pcRateCtrl->getRCQP();
        return;
      }
    
      const uint32_t currDepth = partitioner.currDepth;
    
      if( !splitMode )
      {
        if( currDepth <= pps.getMaxCuDQPDepth() )
        {
          int deltaQP = m_pcEncCfg->getMaxDeltaQP();
          minQP = Clip3( -sps.getQpBDOffset( CHANNEL_TYPE_LUMA ), MAX_QP, baseQP - deltaQP );
          maxQP = Clip3( -sps.getQpBDOffset( CHANNEL_TYPE_LUMA ), MAX_QP, baseQP + deltaQP );
        }
        else
        {
          minQP = cs.currQP[partitioner.chType];
          maxQP = cs.currQP[partitioner.chType];
        }
    
    #if SHARP_LUMA_DELTA_QP
        if( m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled() )
        {
          minQP = Clip3( -sps.getQpBDOffset( CHANNEL_TYPE_LUMA ), MAX_QP, baseQP - m_lumaQPOffset );
          maxQP = minQP; // force encode choose the modified QO
        }
    #endif
      }
      else
      {
        if( currDepth == pps.getMaxCuDQPDepth() )
        {
          int deltaQP = m_pcEncCfg->getMaxDeltaQP();
          minQP = Clip3( -sps.getQpBDOffset( CHANNEL_TYPE_LUMA ), MAX_QP, baseQP - deltaQP );
          maxQP = Clip3( -sps.getQpBDOffset( CHANNEL_TYPE_LUMA ), MAX_QP, baseQP + deltaQP );
        }
        else if( currDepth < pps.getMaxCuDQPDepth() )
        {
          minQP = baseQP;
          maxQP = baseQP;
        }
        else
        {
          minQP = cs.currQP[partitioner.chType];
          maxQP = cs.currQP[partitioner.chType];
        }
    
    #if SHARP_LUMA_DELTA_QP
        if( m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled() )
        {
          minQP = Clip3( -sps.getQpBDOffset( CHANNEL_TYPE_LUMA ), MAX_QP, baseQP - m_lumaQPOffset );
          maxQP = minQP;
        }
    #endif
      }
    }
    
    
    int EncModeCtrl::xComputeDQP( const CodingStructure &cs, const Partitioner &partitioner )
    {
      Picture* picture    = cs.picture;
      unsigned uiAQDepth  = std::min( partitioner.currDepth, ( uint32_t ) picture->aqlayer.size() - 1 );
      AQpLayer* pcAQLayer = picture->aqlayer[uiAQDepth];
    
      double dMaxQScale   = pow( 2.0, m_pcEncCfg->getQPAdaptationRange() / 6.0 );
      double dAvgAct      = pcAQLayer->getAvgActivity();
      double dCUAct       = pcAQLayer->getActivity( cs.area.Y().topLeft() );
      double dNormAct     = ( dMaxQScale*dCUAct + dAvgAct ) / ( dCUAct + dMaxQScale*dAvgAct );
      double dQpOffset    = log( dNormAct ) / log( 2.0 ) * 6.0;
      int    iQpOffset    = int( floor( dQpOffset + 0.49999 ) );
      return iQpOffset;
    }
    
    
    #if SHARP_LUMA_DELTA_QP
    void EncModeCtrl::initLumaDeltaQpLUT()
    {
      const LumaLevelToDeltaQPMapping &mapping = m_pcEncCfg->getLumaLevelToDeltaQPMapping();
    
      if( !mapping.isEnabled() )
      {
        return;
      }
    
      // map the sparse LumaLevelToDeltaQPMapping.mapping to a fully populated linear table.
    
      int         lastDeltaQPValue = 0;
      std::size_t nextSparseIndex = 0;
      for( int index = 0; index < LUMA_LEVEL_TO_DQP_LUT_MAXSIZE; index++ )
      {
        while( nextSparseIndex < mapping.mapping.size() && index >= mapping.mapping[nextSparseIndex].first )
        {
          lastDeltaQPValue = mapping.mapping[nextSparseIndex].second;
          nextSparseIndex++;
        }
        m_lumaLevelToDeltaQPLUT[index] = lastDeltaQPValue;
      }
    }
    
    int EncModeCtrl::calculateLumaDQP( const CPelBuf& rcOrg )
    {
      double avg = 0;
    
      // Get QP offset derived from Luma level
    #if !WCG_EXT
      if( m_pcEncCfg->getLumaLevelToDeltaQPMapping().mode == LUMALVL_TO_DQP_AVG_METHOD )
    #else
      CHECK( m_pcEncCfg->getLumaLevelToDeltaQPMapping().mode != LUMALVL_TO_DQP_AVG_METHOD, "invalid delta qp mode" );
    #endif
      {
        // Use avg method
        int sum = 0;
        for( uint32_t y = 0; y < rcOrg.height; y++ )
        {
          for( uint32_t x = 0; x < rcOrg.width; x++ )
          {
            sum += rcOrg.at( x, y );
          }
        }
        avg = ( double ) sum / rcOrg.area();
      }
    #if !WCG_EXT
      else
      {
        // Use maximum luma value
        int maxVal = 0;
        for( uint32_t y = 0; y < rcOrg.height; y++ )
        {
          for( uint32_t x = 0; x < rcOrg.width; x++ )
          {
            const Pel& v = rcOrg.at( x, y );
            if( v > maxVal )
            {
              maxVal = v;
            }
          }
        }
        // use a percentage of the maxVal
        avg = ( double ) maxVal * m_pcEncCfg->getLumaLevelToDeltaQPMapping().maxMethodWeight;
      }
    #endif
      int lumaIdx = Clip3<int>( 0, int( LUMA_LEVEL_TO_DQP_LUT_MAXSIZE ) - 1, int( avg + 0.5 ) );
      int QP = m_lumaLevelToDeltaQPLUT[lumaIdx];
      return QP;
    }
    #endif
    
    #if ENABLE_SPLIT_PARALLELISM
    void EncModeCtrl::copyState( const EncModeCtrl& other, const UnitArea& area )
    {
      m_slice          = other.m_slice;
      m_fastDeltaQP    = other.m_fastDeltaQP;
      m_lumaQPOffset   = other.m_lumaQPOffset;
      m_runNextInParallel
                       = other.m_runNextInParallel;
      m_ComprCUCtxList = other.m_ComprCUCtxList;
    }
    
    #endif
    void CacheBlkInfoCtrl::create()
    {
      const unsigned numPos = MAX_CU_SIZE >> MIN_CU_LOG2;
    
      m_numWidths  = gp_sizeIdxInfo->numWidths();
      m_numHeights = gp_sizeIdxInfo->numHeights();
    
      for( unsigned x = 0; x < numPos; x++ )
      {
        for( unsigned y = 0; y < numPos; y++ )
        {
          m_codedCUInfo[x][y] = new CodedCUInfo**[m_numWidths];
    
          for( int wIdx = 0; wIdx < gp_sizeIdxInfo->numWidths(); wIdx++ )
          {
            if( gp_sizeIdxInfo->isCuSize( gp_sizeIdxInfo->sizeFrom( wIdx ) ) && x + ( gp_sizeIdxInfo->sizeFrom( wIdx ) >> MIN_CU_LOG2 ) <= ( MAX_CU_SIZE >> MIN_CU_LOG2 ) )
            {
              m_codedCUInfo[x][y][wIdx] = new CodedCUInfo*[gp_sizeIdxInfo->numHeights()];
    
              for( int hIdx = 0; hIdx < gp_sizeIdxInfo->numHeights(); hIdx++ )
              {
                if( gp_sizeIdxInfo->isCuSize( gp_sizeIdxInfo->sizeFrom( hIdx ) ) && y + ( gp_sizeIdxInfo->sizeFrom( hIdx ) >> MIN_CU_LOG2 ) <= ( MAX_CU_SIZE >> MIN_CU_LOG2 ) )
                {
                  m_codedCUInfo[x][y][wIdx][hIdx] = new CodedCUInfo;
                }
                else
                {
                  m_codedCUInfo[x][y][wIdx][hIdx] = nullptr;
                }
              }
            }
            else
            {
              m_codedCUInfo[x][y][wIdx] = nullptr;
            }
          }
        }
      }
    }
    
    void CacheBlkInfoCtrl::destroy()
    {
      const unsigned numPos = MAX_CU_SIZE >> MIN_CU_LOG2;
    
      for( unsigned x = 0; x < numPos; x++ )
      {
        for( unsigned y = 0; y < numPos; y++ )
        {
          for( int wIdx = 0; wIdx < gp_sizeIdxInfo->numWidths(); wIdx++ )
          {
            if( m_codedCUInfo[x][y][wIdx] )
            {
              for( int hIdx = 0; hIdx < gp_sizeIdxInfo->numHeights(); hIdx++ )
              {
                if( m_codedCUInfo[x][y][wIdx][hIdx] )
                {
                  delete m_codedCUInfo[x][y][wIdx][hIdx];
                }
              }
    
              delete[] m_codedCUInfo[x][y][wIdx];
            }
          }
    
          delete[] m_codedCUInfo[x][y];
        }
      }
    }
    
    void CacheBlkInfoCtrl::init( const Slice &slice )
    {
      const unsigned numPos = MAX_CU_SIZE >> MIN_CU_LOG2;
    
      for( unsigned x = 0; x < numPos; x++ )
      {
        for( unsigned y = 0; y < numPos; y++ )
        {
          for( int wIdx = 0; wIdx < gp_sizeIdxInfo->numWidths(); wIdx++ )
          {
            if( m_codedCUInfo[x][y][wIdx] )
            {
              for( int hIdx = 0; hIdx < gp_sizeIdxInfo->numHeights(); hIdx++ )
              {
                if( m_codedCUInfo[x][y][wIdx][hIdx] )
                {
                  memset( m_codedCUInfo[x][y][wIdx][hIdx], 0, sizeof( CodedCUInfo ) );
                }
              }
            }
          }
        }
      }
    
      m_slice_chblk = &slice;
    #if ENABLE_SPLIT_PARALLELISM
    
      m_currTemporalId = 0;
    #endif
    }
    #if ENABLE_SPLIT_PARALLELISM
    
    void CacheBlkInfoCtrl::touch( const UnitArea& area )
    {
      CodedCUInfo& cuInfo = getBlkInfo( area );
      cuInfo.temporalId = m_currTemporalId;
    }
    
    void CacheBlkInfoCtrl::copyState( const CacheBlkInfoCtrl &other, const UnitArea& area )
    {
      m_slice_chblk = other.m_slice_chblk;
    
      m_currTemporalId = other.m_currTemporalId;
    
      if( m_slice_chblk->isIntra() ) return;
    
      const int cuSizeMask = m_slice_chblk->getSPS()->getMaxCUWidth() - 1;
    
      const int minPosX = ( area.lx() & cuSizeMask ) >> MIN_CU_LOG2;
      const int minPosY = ( area.ly() & cuSizeMask ) >> MIN_CU_LOG2;
      const int maxPosX = ( area.Y().bottomRight().x & cuSizeMask ) >> MIN_CU_LOG2;
      const int maxPosY = ( area.Y().bottomRight().y & cuSizeMask ) >> MIN_CU_LOG2;
    
      for( unsigned x = minPosX; x <= maxPosX; x++ )
      {
        for( unsigned y = minPosY; y <= maxPosY; y++ )
        {
          for( int wIdx = 0; wIdx < gp_sizeIdxInfo->numWidths(); wIdx++ )
          {
            const int width = gp_sizeIdxInfo->sizeFrom( wIdx );
    
            if( m_codedCUInfo[x][y][wIdx] && width <= area.lwidth() && x + ( width >> MIN_CU_LOG2 ) <= ( maxPosX + 1 ) )
            {
              for( int hIdx = 0; hIdx < gp_sizeIdxInfo->numHeights(); hIdx++ )
              {
                const int height = gp_sizeIdxInfo->sizeFrom( hIdx );
    
                if( gp_sizeIdxInfo->isCuSize( height ) && height <= area.lheight() && y + ( height >> MIN_CU_LOG2 ) <= ( maxPosY + 1 ) )
                {
                  if( other.m_codedCUInfo[x][y][wIdx][hIdx]->temporalId > m_codedCUInfo[x][y][wIdx][hIdx]->temporalId )
                  {
                    *m_codedCUInfo[x][y][wIdx][hIdx] = *other.m_codedCUInfo[x][y][wIdx][hIdx];
                    m_codedCUInfo[x][y][wIdx][hIdx]->temporalId = m_currTemporalId;
                  }
                }
                else if( y + ( height >> MIN_CU_LOG2 ) > maxPosY + 1 )
                {
                  break;;
                }
              }
            }
            else if( x + ( width >> MIN_CU_LOG2 ) > maxPosX + 1 )
            {
              break;
            }
          }
        }
      }
    }
    #endif
    
    CodedCUInfo& CacheBlkInfoCtrl::getBlkInfo( const UnitArea& area )
    {
      unsigned idx1, idx2, idx3, idx4;
      getAreaIdx( area.Y(), *m_slice_chblk->getPPS()->pcv, idx1, idx2, idx3, idx4 );
    
      return *m_codedCUInfo[idx1][idx2][idx3][idx4];
    }
    
    bool CacheBlkInfoCtrl::isSkip( const UnitArea& area )
    {
      unsigned idx1, idx2, idx3, idx4;
      getAreaIdx( area.Y(), *m_slice_chblk->getPPS()->pcv, idx1, idx2, idx3, idx4 );
    
      return m_codedCUInfo[idx1][idx2][idx3][idx4]->isSkip;
    }
    
    void CacheBlkInfoCtrl::setMv( const UnitArea& area, const RefPicList refPicList, const int iRefIdx, const Mv& rMv )
    {
      if( iRefIdx >= MAX_STORED_CU_INFO_REFS ) return;
    
      unsigned idx1, idx2, idx3, idx4;
      getAreaIdx( area.Y(), *m_slice_chblk->getPPS()->pcv, idx1, idx2, idx3, idx4 );
    
      m_codedCUInfo[idx1][idx2][idx3][idx4]->saveMv [refPicList][iRefIdx] = rMv;
      m_codedCUInfo[idx1][idx2][idx3][idx4]->validMv[refPicList][iRefIdx] = true;
    #if ENABLE_SPLIT_PARALLELISM
    
      touch( area );
    #endif
    }
    
    bool CacheBlkInfoCtrl::getMv( const UnitArea& area, const RefPicList refPicList, const int iRefIdx, Mv& rMv ) const
    {
      unsigned idx1, idx2, idx3, idx4;
      getAreaIdx( area.Y(), *m_slice_chblk->getPPS()->pcv, idx1, idx2, idx3, idx4 );
    
      if( iRefIdx >= MAX_STORED_CU_INFO_REFS )
      {
        rMv = m_codedCUInfo[idx1][idx2][idx3][idx4]->saveMv[refPicList][0];
        return false;
      }
    
      rMv = m_codedCUInfo[idx1][idx2][idx3][idx4]->saveMv[refPicList][iRefIdx];
      return m_codedCUInfo[idx1][idx2][idx3][idx4]->validMv[refPicList][iRefIdx];
    }
    
    #if JVET_L0646_GBI 
    bool CacheBlkInfoCtrl::getInter(const UnitArea& area)
    {
      unsigned idx1, idx2, idx3, idx4;
      getAreaIdx(area.Y(), *m_slice_chblk->getPPS()->pcv, idx1, idx2, idx3, idx4);
    
      return m_codedCUInfo[idx1][idx2][idx3][idx4]->isInter;
    }
    void CacheBlkInfoCtrl::setGbiIdx(const UnitArea& area, uint8_t gBiIdx)
    {
      unsigned idx1, idx2, idx3, idx4;
      getAreaIdx(area.Y(), *m_slice_chblk->getPPS()->pcv, idx1, idx2, idx3, idx4);
    
      m_codedCUInfo[idx1][idx2][idx3][idx4]->GBiIdx = gBiIdx;
    }
    uint8_t CacheBlkInfoCtrl::getGbiIdx(const UnitArea& area)
    {
      unsigned idx1, idx2, idx3, idx4;
      getAreaIdx(area.Y(), *m_slice_chblk->getPPS()->pcv, idx1, idx2, idx3, idx4);
    
      return m_codedCUInfo[idx1][idx2][idx3][idx4]->GBiIdx;
    }
    #endif
    
    #if REUSE_CU_RESULTS
    static bool isTheSameNbHood( const CodingUnit &cu, const Partitioner &partitioner )
    {
      if( cu.chType != partitioner.chType )
      {
        return false;
      }
    
      const PartitioningStack &ps = partitioner.getPartStack();
    
      int i = 1;
    
      for( ; i < ps.size(); i++ )
      {
        if( ps[i].split != CU::getSplitAtDepth( cu, i - 1 ) )
        {
          break;
        }
      }
    
      const UnitArea &cmnAnc = ps[i - 1].parts[ps[i - 1].idx];
    
      for( int i = 0; i < cmnAnc.blocks.size(); i++ )
      {
        if( i < cu.blocks.size() && cu.blocks[i].valid() && cu.blocks[i].pos() != cmnAnc.blocks[i].pos() )
        {
          return false;
        }
      }
    
      return true;
    }
    
    void BestEncInfoCache::create( const ChromaFormat chFmt )
    {
      const unsigned numPos = MAX_CU_SIZE >> MIN_CU_LOG2;
    
      m_numWidths  = gp_sizeIdxInfo->numWidths();
      m_numHeights = gp_sizeIdxInfo->numHeights();
    
      for( unsigned x = 0; x < numPos; x++ )
      {
        for( unsigned y = 0; y < numPos; y++ )
        {
          m_bestEncInfo[x][y] = new BestEncodingInfo**[m_numWidths];
    
          for( int wIdx = 0; wIdx < gp_sizeIdxInfo->numWidths(); wIdx++ )
          {
            if( gp_sizeIdxInfo->isCuSize( gp_sizeIdxInfo->sizeFrom( wIdx ) ) && x + ( gp_sizeIdxInfo->sizeFrom( wIdx ) >> MIN_CU_LOG2 ) <= ( MAX_CU_SIZE >> MIN_CU_LOG2 ) )
            {
              m_bestEncInfo[x][y][wIdx] = new BestEncodingInfo*[gp_sizeIdxInfo->numHeights()];
    
              for( int hIdx = 0; hIdx < gp_sizeIdxInfo->numHeights(); hIdx++ )
              {
                if( gp_sizeIdxInfo->isCuSize( gp_sizeIdxInfo->sizeFrom( hIdx ) ) && y + ( gp_sizeIdxInfo->sizeFrom( hIdx ) >> MIN_CU_LOG2 ) <= ( MAX_CU_SIZE >> MIN_CU_LOG2 ) )
                {
                  m_bestEncInfo[x][y][wIdx][hIdx] = new BestEncodingInfo;
    
                  int w = gp_sizeIdxInfo->sizeFrom( wIdx );
                  int h = gp_sizeIdxInfo->sizeFrom( hIdx );
    
                  const UnitArea area( chFmt, Area( 0, 0, w, h ) );
    
                  m_bestEncInfo[x][y][wIdx][hIdx]->cu.UnitArea::operator=( area );
                  m_bestEncInfo[x][y][wIdx][hIdx]->pu.UnitArea::operator=( area );
                  m_bestEncInfo[x][y][wIdx][hIdx]->tu.UnitArea::operator=( area );
    
                  m_bestEncInfo[x][y][wIdx][hIdx]->poc      = -1;
                  m_bestEncInfo[x][y][wIdx][hIdx]->testMode = EncTestMode();
                }
                else
                {
                  m_bestEncInfo[x][y][wIdx][hIdx] = nullptr;
                }
              }
            }
            else
            {
              m_bestEncInfo[x][y][wIdx] = nullptr;
            }
          }
        }
      }
    }
    
    void BestEncInfoCache::destroy()
    {
      const unsigned numPos = MAX_CU_SIZE >> MIN_CU_LOG2;
    
      for( unsigned x = 0; x < numPos; x++ )
      {
        for( unsigned y = 0; y < numPos; y++ )
        {
          for( int wIdx = 0; wIdx < gp_sizeIdxInfo->numWidths(); wIdx++ )
          {
            if( m_bestEncInfo[x][y][wIdx] )
            {
              for( int hIdx = 0; hIdx < gp_sizeIdxInfo->numHeights(); hIdx++ )
              {
                if( m_bestEncInfo[x][y][wIdx][hIdx] )
                {
                  delete m_bestEncInfo[x][y][wIdx][hIdx];
                }
              }
    
              delete[] m_bestEncInfo[x][y][wIdx];
            }
          }
    
          delete[] m_bestEncInfo[x][y];
        }
      }
    
      delete[] m_pCoeff;
      delete[] m_pPcmBuf;
    }
    
    void BestEncInfoCache::init( const Slice &slice )
    {
      bool isInitialized = m_slice_bencinf;
    
      m_slice_bencinf = &slice;
    
      if( isInitialized ) return;
      
      const unsigned numPos = MAX_CU_SIZE >> MIN_CU_LOG2;
    
      m_numWidths  = gp_sizeIdxInfo->numWidths();
      m_numHeights = gp_sizeIdxInfo->numHeights();
    
      size_t numCoeff = 0;
      
      for( unsigned x = 0; x < numPos; x++ )
      {
        for( unsigned y = 0; y < numPos; y++ )
        {
          for( int wIdx = 0; wIdx < gp_sizeIdxInfo->numWidths(); wIdx++ )
          {
            if( m_bestEncInfo[x][y][wIdx] ) for( int hIdx = 0; hIdx < gp_sizeIdxInfo->numHeights(); hIdx++ )
            {
              if( m_bestEncInfo[x][y][wIdx][hIdx] )
              {
                for( const CompArea& blk : m_bestEncInfo[x][y][wIdx][hIdx]->cu.blocks )
                {
                  numCoeff += blk.area();
                }
              }
            }
          }
        }
      }
    
      m_pCoeff  = new TCoeff[numCoeff];
      m_pPcmBuf = new Pel   [numCoeff];
    
      TCoeff *coeffPtr = m_pCoeff;
      Pel    *pcmPtr   = m_pPcmBuf;
    
      m_dummyCS.pcv = m_slice_bencinf->getPPS()->pcv;
    
      for( unsigned x = 0; x < numPos; x++ )
      {
        for( unsigned y = 0; y < numPos; y++ )
        {
          for( int wIdx = 0; wIdx < gp_sizeIdxInfo->numWidths(); wIdx++ )
          {
            if( m_bestEncInfo[x][y][wIdx] ) for( int hIdx = 0; hIdx < gp_sizeIdxInfo->numHeights(); hIdx++ )
            {
              if( m_bestEncInfo[x][y][wIdx][hIdx] )
              {
                TCoeff *coeff[MAX_NUM_TBLOCKS] = { 0, };
                Pel    *pcmbf[MAX_NUM_TBLOCKS] = { 0, };
    
                const UnitArea &area = m_bestEncInfo[x][y][wIdx][hIdx]->tu;
    
                for( int i = 0; i < area.blocks.size(); i++ )
                {
                  coeff[i] = coeffPtr; coeffPtr += area.blocks[i].area();
                  pcmbf[i] =   pcmPtr;   pcmPtr += area.blocks[i].area();
                }
    
                m_bestEncInfo[x][y][wIdx][hIdx]->tu.cs = &m_dummyCS;
                m_bestEncInfo[x][y][wIdx][hIdx]->tu.init( coeff, pcmbf );
              }
            }
          }
        }
      }
    }
    
    bool BestEncInfoCache::setFromCs( const CodingStructure& cs, const Partitioner& partitioner )
    {
      if( cs.cus.size() != 1 || cs.tus.size() != 1 || cs.pus.size() != 1 )
      {
        return false;
      }
    
      unsigned idx1, idx2, idx3, idx4;
      getAreaIdx( cs.area.Y(), *m_slice_bencinf->getPPS()->pcv, idx1, idx2, idx3, idx4 );
    
      BestEncodingInfo& encInfo = *m_bestEncInfo[idx1][idx2][idx3][idx4];
    
      encInfo.poc            =  cs.picture->poc;
      encInfo.cu.repositionTo( *cs.cus.front() );
      encInfo.pu.repositionTo( *cs.pus.front() );
      encInfo.tu.repositionTo( *cs.tus.front() );
      encInfo.cu             = *cs.cus.front();
      encInfo.pu             = *cs.pus.front();
      for( auto &blk : cs.tus.front()->blocks )
      {
        if( blk.valid() ) encInfo.tu.copyComponentFrom( *cs.tus.front(), blk.compID );
      }
      encInfo.testMode       = getCSEncMode( cs );
    
      return true;
    }
    
    bool BestEncInfoCache::isValid( const CodingStructure& cs, const Partitioner& partitioner )
    {
      unsigned idx1, idx2, idx3, idx4;
      getAreaIdx( cs.area.Y(), *m_slice_bencinf->getPPS()->pcv, idx1, idx2, idx3, idx4 );
    
      BestEncodingInfo& encInfo = *m_bestEncInfo[idx1][idx2][idx3][idx4];
    
      if( cs.picture->poc != encInfo.poc || CS::getArea( cs, cs.area, partitioner.chType ) != encInfo.cu || !isTheSameNbHood( encInfo.cu, partitioner ) 
        )
      {
        return false;
      }
      else
      {
        return true;
      }
    }
    
    bool BestEncInfoCache::setCsFrom( CodingStructure& cs, EncTestMode& testMode, const Partitioner& partitioner ) const
    {
      unsigned idx1, idx2, idx3, idx4;
      getAreaIdx( cs.area.Y(), *m_slice_bencinf->getPPS()->pcv, idx1, idx2, idx3, idx4 );
    
      BestEncodingInfo& encInfo = *m_bestEncInfo[idx1][idx2][idx3][idx4];
    
      if( cs.picture->poc != encInfo.poc || CS::getArea( cs, cs.area, partitioner.chType ) != encInfo.cu || !isTheSameNbHood( encInfo.cu, partitioner ) )
      {
        return false;
      }
    
      CodingUnit     &cu = cs.addCU( CS::getArea( cs, cs.area, partitioner.chType ), partitioner.chType );
      PredictionUnit &pu = cs.addPU( CS::getArea( cs, cs.area, partitioner.chType ), partitioner.chType );
      TransformUnit  &tu = cs.addTU( CS::getArea( cs, cs.area, partitioner.chType ), partitioner.chType );
    
      cu          .repositionTo( encInfo.cu );
      pu          .repositionTo( encInfo.pu );
      tu          .repositionTo( encInfo.tu );
    
      cu          = encInfo.cu;
      pu          = encInfo.pu;
      for( auto &blk : tu.blocks )
      {
        if( blk.valid() ) tu.copyComponentFrom( encInfo.tu, blk.compID );
      }
    
      testMode    = encInfo.testMode;
    
      return true;
    }
    
    #endif
    
    static bool interHadActive( const ComprCUCtx& ctx )
    {
      return ctx.interHad != 0;
    }
    
    //////////////////////////////////////////////////////////////////////////
    // EncModeCtrlQTBT
    //////////////////////////////////////////////////////////////////////////
    
    EncModeCtrlMTnoRQT::EncModeCtrlMTnoRQT()
    {
    #if !REUSE_CU_RESULTS
      CacheBlkInfoCtrl::create();
    #endif
    }
    
    EncModeCtrlMTnoRQT::~EncModeCtrlMTnoRQT()
    {
    #if !REUSE_CU_RESULTS
      CacheBlkInfoCtrl::destroy();
    #endif
    }
    
    #if REUSE_CU_RESULTS
    void EncModeCtrlMTnoRQT::create( const EncCfg& cfg )
    {
      CacheBlkInfoCtrl::create();
      BestEncInfoCache::create( cfg.getChromaFormatIdc() );
    }
    
    void EncModeCtrlMTnoRQT::destroy()
    {
      CacheBlkInfoCtrl::destroy();
      BestEncInfoCache::destroy();
    }
    
    #endif
    void EncModeCtrlMTnoRQT::initCTUEncoding( const Slice &slice )
    {
      CacheBlkInfoCtrl::init( slice );
    #if REUSE_CU_RESULTS
      BestEncInfoCache::init( slice );
    #endif
    
      CHECK( !m_ComprCUCtxList.empty(), "Mode list is not empty at the beginning of a CTU" );
    
      m_slice             = &slice;
    #if ENABLE_SPLIT_PARALLELISM
      m_runNextInParallel      = false;
    #endif
    
      if( m_pcEncCfg->getUseE0023FastEnc() )
      {
        if (m_pcEncCfg->getUseCompositeRef())
          m_skipThreshold = ( ( slice.getMinPictureDistance() <= PICTURE_DISTANCE_TH * 2 ) ? FAST_SKIP_DEPTH : SKIP_DEPTH );
        else
          m_skipThreshold = ((slice.getMinPictureDistance() <= PICTURE_DISTANCE_TH) ? FAST_SKIP_DEPTH : SKIP_DEPTH);
    
      }
      else
      {
        m_skipThreshold = SKIP_DEPTH;
      }
    }
    
    
    void EncModeCtrlMTnoRQT::initCULevel( Partitioner &partitioner, const CodingStructure& cs )
    {
      // Min/max depth
      unsigned minDepth = 0;
      unsigned maxDepth = g_aucLog2[cs.sps->getSpsNext().getCTUSize()] - g_aucLog2[cs.sps->getSpsNext().getMinQTSize( m_slice->getSliceType(), partitioner.chType )];
      if( m_pcEncCfg->getUseFastLCTU() )
      {
        if( auto adPartitioner = dynamic_cast<AdaptiveDepthPartitioner*>( &partitioner ) )
        {
          // LARGE CTU
          adPartitioner->setMaxMinDepth( minDepth, maxDepth, cs );
        }
      }
    
      m_ComprCUCtxList.push_back( ComprCUCtx( cs, minDepth, maxDepth, NUM_EXTRA_FEATURES ) );
    
    #if ENABLE_SPLIT_PARALLELISM
      if( m_runNextInParallel )
      {
        for( auto &level : m_ComprCUCtxList )
        {
          CHECK( level.isLevelSplitParallel, "Tring to parallelize a level within parallel execution!" );
        }
        CHECK( cs.picture->scheduler.getSplitJobId() == 0, "Trying to run a parallel level although jobId is 0!" );
        m_runNextInParallel                          = false;
        m_ComprCUCtxList.back().isLevelSplitParallel = true;
      }
    
    #endif
      const CodingUnit* cuLeft  = cs.getCU( cs.area.blocks[partitioner.chType].pos().offset( -1, 0 ), partitioner.chType );
      const CodingUnit* cuAbove = cs.getCU( cs.area.blocks[partitioner.chType].pos().offset( 0, -1 ), partitioner.chType );
    
      const bool qtBeforeBt = ( (  cuLeft  &&  cuAbove  && cuLeft ->qtDepth > partitioner.currQtDepth && cuAbove->qtDepth > partitioner.currQtDepth )
                             || (  cuLeft  && !cuAbove  && cuLeft ->qtDepth > partitioner.currQtDepth )
                             || ( !cuLeft  &&  cuAbove  && cuAbove->qtDepth > partitioner.currQtDepth )
                             || ( !cuAbove && !cuLeft   && cs.area.lwidth() >= ( 32 << cs.slice->getDepth() ) ) )
                             && ( cs.area.lwidth() > ( cs.pcv->getMinQtSize( *cs.slice, partitioner.chType ) << 1 ) );
    
      // set features
      ComprCUCtx &cuECtx  = m_ComprCUCtxList.back();
      cuECtx.set( BEST_NON_SPLIT_COST,  MAX_DOUBLE );
      cuECtx.set( BEST_VERT_SPLIT_COST, MAX_DOUBLE );
      cuECtx.set( BEST_HORZ_SPLIT_COST, MAX_DOUBLE );
      cuECtx.set( BEST_TRIH_SPLIT_COST, MAX_DOUBLE );
      cuECtx.set( BEST_TRIV_SPLIT_COST, MAX_DOUBLE );
      cuECtx.set( DO_TRIH_SPLIT,        cs.sps->getSpsNext().getMTTMode() & 1 );
      cuECtx.set( DO_TRIV_SPLIT,        cs.sps->getSpsNext().getMTTMode() & 1 );
      cuECtx.set( BEST_IMV_COST,        MAX_DOUBLE * .5 );
      cuECtx.set( BEST_NO_IMV_COST,     MAX_DOUBLE * .5 );
      cuECtx.set( QT_BEFORE_BT,         qtBeforeBt );
      cuECtx.set( DID_QUAD_SPLIT,       false );
      cuECtx.set( IS_BEST_NOSPLIT_SKIP, false );
      cuECtx.set( MAX_QT_SUB_DEPTH,     0 );
    #if REUSE_CU_RESULTS
      const bool isReusingCu = isValid( cs, partitioner );
      cuECtx.set( IS_REUSING_CU,        isReusingCu );
    #endif
    
      // QP
      int baseQP = cs.baseQP;
      if( m_pcEncCfg->getUseAdaptiveQP() )
      {
        baseQP = Clip3( -cs.sps->getQpBDOffset( CHANNEL_TYPE_LUMA ), MAX_QP, baseQP + xComputeDQP( cs, partitioner ) );
      }
      int minQP = baseQP;
      int maxQP = baseQP;
    
    #if SHARP_LUMA_DELTA_QP
      if( m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled() )
      {
        if( partitioner.currDepth <= cs.pps->getMaxCuDQPDepth() )
        {
          CompArea clipedArea = clipArea( cs.area.Y(), cs.picture->Y() );
          // keep using the same m_QP_LUMA_OFFSET in the same CTU
          m_lumaQPOffset = calculateLumaDQP( cs.getOrgBuf( clipedArea ) );
        }
      }
    #endif
    
      xGetMinMaxQP( minQP, maxQP, cs, partitioner, baseQP, *cs.sps, *cs.pps, true );
      // Add coding modes here
      // NOTE: Working back to front, as a stack, which is more efficient with the container
      // NOTE: First added modes will be processed at the end.
    
      //////////////////////////////////////////////////////////////////////////
      // Add unit split modes
    
      if( !cuECtx.get<bool>( QT_BEFORE_BT ) )
      {
        for( int qp = maxQP; qp >= minQP; qp-- )
        {
          m_ComprCUCtxList.back().testModes.push_back( { ETM_SPLIT_QT, SIZE_2Nx2N, ETO_STANDARD, qp, false } );
        }
      }
    
      if( partitioner.canSplit( CU_TRIV_SPLIT, cs ) )
      {
        // add split modes
        for( int qp = maxQP; qp >= minQP; qp-- )
        {
          m_ComprCUCtxList.back().testModes.push_back( { ETM_SPLIT_TT_V, SIZE_2Nx2N, ETO_STANDARD, qp, false } );
        }
      }
    
      if( partitioner.canSplit( CU_TRIH_SPLIT, cs ) )
      {
        // add split modes
        for( int qp = maxQP; qp >= minQP; qp-- )
        {
          m_ComprCUCtxList.back().testModes.push_back( { ETM_SPLIT_TT_H, SIZE_2Nx2N, ETO_STANDARD, qp, false } );
        }
      }
    
      if( partitioner.canSplit( CU_VERT_SPLIT, cs ) )
      {
        // add split modes
        for( int qp = maxQP; qp >= minQP; qp-- )
        {
          m_ComprCUCtxList.back().testModes.push_back( { ETM_SPLIT_BT_V, SIZE_2Nx2N, ETO_STANDARD, qp, false } );
        }
        m_ComprCUCtxList.back().set( DID_VERT_SPLIT, true );
      }
      else
      {
        m_ComprCUCtxList.back().set( DID_VERT_SPLIT, false );
      }
    
      if( partitioner.canSplit( CU_HORZ_SPLIT, cs ) )
      {
        // add split modes
        for( int qp = maxQP; qp >= minQP; qp-- )
        {
          m_ComprCUCtxList.back().testModes.push_back( { ETM_SPLIT_BT_H, SIZE_2Nx2N, ETO_STANDARD, qp, false } );
        }
        m_ComprCUCtxList.back().set( DID_HORZ_SPLIT, true );
      }
      else
      {
        m_ComprCUCtxList.back().set( DID_HORZ_SPLIT, false );
      }
    
      if( cuECtx.get<bool>( QT_BEFORE_BT ) )
      {
        for( int qp = maxQP; qp >= minQP; qp-- )
        {
          m_ComprCUCtxList.back().testModes.push_back( { ETM_SPLIT_QT, SIZE_2Nx2N, ETO_STANDARD, qp, false } );
        }
      }
    
      m_ComprCUCtxList.back().testModes.push_back( { ETM_POST_DONT_SPLIT } );
    
    #if REUSE_CU_RESULTS
      if( isReusingCu )
      {
        m_ComprCUCtxList.back().testModes.push_back( { ETM_RECO_CACHED } );
      }
    
    #endif
      xGetMinMaxQP( minQP, maxQP, cs, partitioner, baseQP, *cs.sps, *cs.pps, false );
    
      bool useLossless = false;
      int  lowestQP = minQP;
      if( cs.pps->getTransquantBypassEnabledFlag() )
      {
        useLossless = true; // mark that the first iteration is to cost TQB mode.
        minQP = minQP - 1;  // increase loop variable range by 1, to allow testing of TQB mode along with other QPs
    
        if( m_pcEncCfg->getCUTransquantBypassFlagForceValue() )
        {
          maxQP = minQP;
        }
      }
    
      //////////////////////////////////////////////////////////////////////////
      // Add unit coding modes: Intra, InterME, InterMerge ...
    
      for( int qpLoop = maxQP; qpLoop >= minQP; qpLoop-- )
      {
        const int  qp       = std::max( qpLoop, lowestQP );
        const bool lossless = useLossless && qpLoop == minQP;
        // add intra modes
        m_ComprCUCtxList.back().testModes.push_back( { ETM_IPCM,  SIZE_2Nx2N, ETO_STANDARD, qp, lossless } );
        m_ComprCUCtxList.back().testModes.push_back( { ETM_INTRA, SIZE_2Nx2N, ETO_STANDARD, qp, lossless } );
      }
    
      // add first pass modes
      if( !m_slice->isIRAP() )
      {
        for( int qpLoop = maxQP; qpLoop >= minQP; qpLoop-- )
        {
          const int  qp       = std::max( qpLoop, lowestQP );
          const bool lossless = useLossless && qpLoop == minQP;
    
          if( m_pcEncCfg->getIMV() )
          {
            if( m_pcEncCfg->getIMV() == IMV_4PEL )
            {
              int imv = m_pcEncCfg->getIMV4PelFast() ? 3 : 2;
              m_ComprCUCtxList.back().testModes.push_back( { ETM_INTER_ME, SIZE_2Nx2N, EncTestModeOpts( imv << ETO_IMV_SHIFT ), qp, lossless } );
            }
            m_ComprCUCtxList.back().testModes.push_back( { ETM_INTER_ME, SIZE_2Nx2N, EncTestModeOpts( 1 << ETO_IMV_SHIFT ), qp, lossless } );
          }
          // add inter modes
          if( m_pcEncCfg->getUseEarlySkipDetection() )
          {
            m_ComprCUCtxList.back().testModes.push_back( { ETM_MERGE_SKIP,  SIZE_2Nx2N, ETO_STANDARD, qp, lossless } );
            if( cs.sps->getSpsNext().getUseAffine() )
            {
              m_ComprCUCtxList.back().testModes.push_back( { ETM_AFFINE,      SIZE_2Nx2N, ETO_STANDARD, qp, lossless } );
            }
            m_ComprCUCtxList.back().testModes.push_back( { ETM_INTER_ME,    SIZE_2Nx2N, ETO_STANDARD, qp, lossless } );
          }
          else
          {
            m_ComprCUCtxList.back().testModes.push_back( { ETM_INTER_ME,    SIZE_2Nx2N, ETO_STANDARD, qp, lossless } );
    
            m_ComprCUCtxList.back().testModes.push_back( { ETM_MERGE_SKIP,  SIZE_2Nx2N, ETO_STANDARD, qp, lossless } );
            if( cs.sps->getSpsNext().getUseAffine() )
            {
              m_ComprCUCtxList.back().testModes.push_back( { ETM_AFFINE,      SIZE_2Nx2N, ETO_STANDARD, qp, lossless } );
            }
          }
        }
      }
    
      // ensure to skip unprobable modes
      if( !tryModeMaster( m_ComprCUCtxList.back().testModes.back(), cs, partitioner ) )
      {
        nextMode( cs, partitioner );
      }
    
      m_ComprCUCtxList.back().lastTestMode = EncTestMode();
    }
    
    void EncModeCtrlMTnoRQT::finishCULevel( Partitioner &partitioner )
    {
      m_ComprCUCtxList.pop_back();
    }
    
    
    bool EncModeCtrlMTnoRQT::tryMode( const EncTestMode& encTestmode, const CodingStructure &cs, Partitioner& partitioner )
    {
      CHECK( encTestmode.partSize != SIZE_2Nx2N, "Only 2Nx2N supported with QTBT" );
    
      ComprCUCtx& cuECtx = m_ComprCUCtxList.back();
    
      // Fast checks, partitioning depended
    
      // if early skip detected, skip all modes checking but the splits
      if( cuECtx.earlySkip && m_pcEncCfg->getUseEarlySkipDetection() && !isModeSplit( encTestmode ) && !( isModeInter( encTestmode ) && encTestmode.partSize == SIZE_2Nx2N ) )
      {
        return false;
      }
    
      const PartSplit implicitSplit = partitioner.getImplicitSplit( cs );
      const bool isBoundary         = implicitSplit != CU_DONT_SPLIT;
    
      if( isBoundary && encTestmode.type != ETM_SPLIT_QT )
      {
        return getPartSplit( encTestmode ) == implicitSplit;
      }
      else if( isBoundary && encTestmode.type == ETM_SPLIT_QT )
      {
        return partitioner.canSplit( CU_QUAD_SPLIT, cs );
      }
    
    #if !ENABLE_BMS
      if( partitioner.currArea().lwidth() > m_slice->getSPS()->getMaxTrSize() && encTestmode.type != ETM_SPLIT_QT )
      {
        return false;
      }
    
    #endif
    #if REUSE_CU_RESULTS
      if( cuECtx.get<bool>( IS_REUSING_CU ) )
      {
        if( encTestmode.type == ETM_RECO_CACHED )
        {
          return true;
        }
    
        if( isModeNoSplit( encTestmode ) )
        {
          return false;
        }
      }
    
    #endif
      const Slice&           slice       = *m_slice;
      const SPS&             sps         = *slice.getSPS();
      const uint32_t             numComp     = getNumberValidComponents( slice.getSPS()->getChromaFormatIdc() );
      const uint32_t             width       = partitioner.currArea().lumaSize().width;
      const CodingStructure *bestCS      = cuECtx.bestCS;
      const CodingUnit      *bestCU      = cuECtx.bestCU;
      const EncTestMode      bestMode    = bestCS ? getCSEncMode( *bestCS ) : EncTestMode();
    
      CodedCUInfo    &relatedCU          = getBlkInfo( partitioner.currArea() );
    
      if( cuECtx.minDepth > partitioner.currQtDepth && partitioner.canSplit( CU_QUAD_SPLIT, cs ) )
      {
        // enforce QT
        return encTestmode.type == ETM_SPLIT_QT;
      }
      else if( encTestmode.type == ETM_SPLIT_QT && cuECtx.maxDepth <= partitioner.currQtDepth )
      {
        // don't check this QT depth
        return false;
      }
    
      if( bestCS && bestCS->cus.size() == 1 )
      {
        // update the best non-split cost
        cuECtx.set( BEST_NON_SPLIT_COST, bestCS->cost );
      }
    
      if( encTestmode.type == ETM_INTRA )
      {
        if( getFastDeltaQp() )
        {
          if( cs.area.lumaSize().width > cs.pcv->fastDeltaQPCuMaxSize )
          {
            return false; // only check necessary 2Nx2N Intra in fast delta-QP mode
          }
        }
    
        if( m_pcEncCfg->getUseFastLCTU() && partitioner.currArea().lumaSize().area() > 4096 )
        {
          return false;
        }
    
        if (CS::isDualITree(cs) && (partitioner.currArea().lumaSize().width > 64 || partitioner.currArea().lumaSize().height > 64))
        {
          return false;
        }
    
        if( m_pcEncCfg->getUsePbIntraFast() && !cs.slice->isIntra() && !interHadActive( cuECtx ) && cuECtx.bestCU && CU::isInter( *cuECtx.bestCU ) )
        {
          return false;
        }
    
        // INTRA MODES
        CHECK( !slice.isIntra() && !cuECtx.bestTU, "No possible non-intra encoding for a P- or B-slice found" );
    
        if( !( slice.isIRAP() || bestMode.type == ETM_INTRA || 
    	  ( ( !m_pcEncCfg->getDisableIntraPUsInInterSlices() ) && !relatedCU.isInter && (
                                             ( cuECtx.bestTU->cbf[0] != 0 ) ||
               ( ( numComp > COMPONENT_Cb ) && cuECtx.bestTU->cbf[1] != 0 ) ||
               ( ( numComp > COMPONENT_Cr ) && cuECtx.bestTU->cbf[2] != 0 )  // avoid very complex intra if it is unlikely
             ) ) ) )
        {
          return false;
        }
        if( lastTestMode().type != ETM_INTRA && cuECtx.bestCS && cuECtx.bestCU && interHadActive( cuECtx ) )
        {
          // Get SATD threshold from best Inter-CU
          if( !cs.slice->isIRAP() && m_pcEncCfg->getUsePbIntraFast() )
          {
            CodingUnit* bestCU = cuECtx.bestCU;
            if( bestCU && CU::isInter( *bestCU ) )
            {
              DistParam distParam;
              const bool useHad = !bestCU->transQuantBypass;
              m_pcRdCost->setDistParam( distParam, cs.getOrgBuf( COMPONENT_Y ), cuECtx.bestCS->getPredBuf( COMPONENT_Y ), cs.sps->getBitDepth( CHANNEL_TYPE_LUMA ), COMPONENT_Y, useHad );
              cuECtx.interHad = distParam.distFunc( distParam );
            }
          }
        }
    
        return true;
      }
      else if( encTestmode.type == ETM_IPCM )
      {
        if( getFastDeltaQp() )
        {
          const SPS &sps = *cs.sps;
          const uint32_t fastDeltaQPCuMaxPCMSize = Clip3( ( uint32_t ) 1 << sps.getPCMLog2MinSize(), ( uint32_t ) 1 << sps.getPCMLog2MaxSize(), 32u );
    
          if( cs.area.lumaSize().width > fastDeltaQPCuMaxPCMSize )
          {
            return false;   // only check necessary PCM in fast deltaqp mode
          }
        }
    
        // PCM MODES
        return sps.getUsePCM() && width <= ( 1 << sps.getPCMLog2MaxSize() ) && width >= ( 1 << sps.getPCMLog2MinSize() );
      }
      else if( isModeInter( encTestmode ) )
      {
        // INTER MODES (ME + MERGE/SKIP)
        CHECK( slice.isIntra(), "Inter-mode should not be in the I-Slice mode list!" );
    
        if( getFastDeltaQp() )
        {
          if( encTestmode.type == ETM_MERGE_SKIP )
          {
            return false;
          }
          if( encTestmode.partSize != SIZE_2Nx2N || cs.area.lumaSize().width > cs.pcv->fastDeltaQPCuMaxSize )
          {
            return false; // only check necessary 2Nx2N Inter in fast deltaqp mode
          }
        }
    
        // --- Check if we can quit current mode using SAVE/LOAD coding history
    
        if( encTestmode.type == ETM_INTER_ME )
        {
          if( encTestmode.opts == ETO_STANDARD )
          {
            // NOTE: ETO_STANDARD is always done when early SKIP mode detection is enabled
            if( !m_pcEncCfg->getUseEarlySkipDetection() )
            {
              if( relatedCU.isSkip || relatedCU.isIntra )
              {
                return false;
              }
            }
          }
          else if ((encTestmode.opts & ETO_IMV) != 0)
          {
            int imvOpt = (encTestmode.opts & ETO_IMV) >> ETO_IMV_SHIFT;
    
            if (imvOpt == 3 && cuECtx.get<double>(BEST_NO_IMV_COST) * 1.06 < cuECtx.get<double>(BEST_IMV_COST))
            {
              return false;
            }
          }
        }
    
        if ( encTestmode.type == ETM_AFFINE && relatedCU.isIntra )
        {
          return false;
        }
        return true;
      }
      else if( isModeSplit( encTestmode ) )
      {
        //////////////////////////////////////////////////////////////////////////
        // skip-history rule - don't split further if at least for three past levels
        //                     in the split tree it was found that skip is the best mode
        //////////////////////////////////////////////////////////////////////////
        int skipScore = 0;
    
        if( !slice.isIntra() && cuECtx.get<bool>( IS_BEST_NOSPLIT_SKIP ) )
        {
          for( int i = 2; i < m_ComprCUCtxList.size(); i++ )
          {
            if( ( m_ComprCUCtxList.end() - i )->get<bool>( IS_BEST_NOSPLIT_SKIP ) )
            {
              skipScore += 1;
            }
            else
            {
              break;
            }
          }
        }
    
        const PartSplit split = getPartSplit( encTestmode );
        if( !partitioner.canSplit( split, cs ) || skipScore >= 2 )
        {
          if( split == CU_HORZ_SPLIT ) cuECtx.set( DID_HORZ_SPLIT, false );
          if( split == CU_VERT_SPLIT ) cuECtx.set( DID_VERT_SPLIT, false );
          if( split == CU_QUAD_SPLIT ) cuECtx.set( DID_QUAD_SPLIT, false );
    
          return false;
        }
    
        if( m_pcEncCfg->getUseContentBasedFastQtbt() )
        {
          const CompArea& currArea = partitioner.currArea().Y();
          int cuHeight  = currArea.height;
          int cuWidth   = currArea.width;
    
          const bool condIntraInter = m_pcEncCfg->getIntraPeriod() == 1 ? ( partitioner.currBtDepth == 0 ) : ( cuHeight > 32 && cuWidth > 32 );
    
          if( cuWidth == cuHeight && condIntraInter && getPartSplit( encTestmode ) != CU_QUAD_SPLIT )
          {
            const CPelBuf bufCurrArea = cs.getOrgBuf( partitioner.currArea().block( COMPONENT_Y ) );
    
            double horVal = 0;
            double verVal = 0;
            double dupVal = 0;
            double dowVal = 0;
    
            const double th = m_pcEncCfg->getIntraPeriod() == 1 ? 1.2 : 1.0;
    
            unsigned j, k;
    
            for( j = 0; j < cuWidth - 1; j++ )
            {
              for( k = 0; k < cuHeight - 1; k++ )
              {
                horVal += abs( bufCurrArea.at( j + 1, k     ) - bufCurrArea.at( j, k ) );
                verVal += abs( bufCurrArea.at( j    , k + 1 ) - bufCurrArea.at( j, k ) );
                dowVal += abs( bufCurrArea.at( j + 1, k )     - bufCurrArea.at( j, k + 1 ) );
                dupVal += abs( bufCurrArea.at( j + 1, k + 1 ) - bufCurrArea.at( j, k ) );
              }
            }
            if( horVal > th * verVal && sqrt( 2 ) * horVal > th * dowVal && sqrt( 2 ) * horVal > th * dupVal && ( getPartSplit( encTestmode ) == CU_HORZ_SPLIT || getPartSplit( encTestmode ) == CU_TRIH_SPLIT ) )
            {
              return false;
            }
            if( th * dupVal < sqrt( 2 ) * verVal && th * dowVal < sqrt( 2 ) * verVal && th * horVal < verVal && ( getPartSplit( encTestmode ) == CU_VERT_SPLIT || getPartSplit( encTestmode ) == CU_TRIV_SPLIT ) )
            {
              return false;
            }
          }
    
          if( m_pcEncCfg->getIntraPeriod() == 1 && cuWidth <= 32 && cuHeight <= 32 && bestCS && bestCS->tus.size() == 1 && bestCU && bestCU->depth == partitioner.currDepth && partitioner.currBtDepth > 1 && isLuma( partitioner.chType ) )
          {
            if( !bestCU->rootCbf )
            {
              return false;
            }
          }
        }
    
        if( bestCU && bestCU->skip && bestCU->mtDepth >= m_skipThreshold && !isModeSplit( cuECtx.lastTestMode ) )
        {
          return false;
        }
    
        int featureToSet = -1;
    
        switch( getPartSplit( encTestmode ) )
        {
          case CU_QUAD_SPLIT:
            {
    #if ENABLE_SPLIT_PARALLELISM
              if( !cuECtx.isLevelSplitParallel )
    #endif
              if( !cuECtx.get<bool>( QT_BEFORE_BT ) && bestCU )
              {
                unsigned maxBTD        = cs.pcv->getMaxBtDepth( slice, partitioner.chType );
                const CodingUnit *cuBR = bestCS->cus.back();
    #if JVET_L0081_VPDU_SPLIT_CONSTRAINTS
                unsigned height        = partitioner.currArea().lumaSize().height;
    #endif
    
                if( bestCU && ( ( bestCU->btDepth == 0 &&                               maxBTD >= ( slice.isIntra() ? 3 : 2 ) )
                             || ( bestCU->btDepth == 1 && cuBR && cuBR->btDepth == 1 && maxBTD >= ( slice.isIntra() ? 4 : 3 ) ) )
    #if JVET_L0081_VPDU_SPLIT_CONSTRAINTS
                           && ( width <= JVET_L0081_VPDU_SPLIT_CONSTRAINTS && height <= JVET_L0081_VPDU_SPLIT_CONSTRAINTS )
    #endif
                           && cuECtx.get<bool>( DID_HORZ_SPLIT ) && cuECtx.get<bool>( DID_VERT_SPLIT ) )
                {
                  return false;
                }
              }
              if( m_pcEncCfg->getUseEarlyCU() && bestCS->cost != MAX_DOUBLE && bestCU && bestCU->skip )
              {
                return false;
              }
              if( getFastDeltaQp() && width <= slice.getPPS()->pcv->fastDeltaQPCuMaxSize )
              {
                return false;
              }
            }
            break;
          case CU_HORZ_SPLIT:
            featureToSet = DID_HORZ_SPLIT;
            break;
          case CU_VERT_SPLIT:
            featureToSet = DID_VERT_SPLIT;
            break;
          case CU_TRIH_SPLIT:
            if( cuECtx.get<bool>( DID_HORZ_SPLIT ) && bestCU && bestCU->btDepth == partitioner.currBtDepth && !bestCU->rootCbf )
            {
              return false;
            }
    
            if( !cuECtx.get<bool>( DO_TRIH_SPLIT ) )
            {
              return false;
            }
            break;
          case CU_TRIV_SPLIT:
            if( cuECtx.get<bool>( DID_VERT_SPLIT ) && bestCU && bestCU->btDepth == partitioner.currBtDepth && !bestCU->rootCbf )
            {
              return false;
            }
    
            if( !cuECtx.get<bool>( DO_TRIV_SPLIT ) )
            {
              return false;
            }
            break;
          default:
            THROW( "Only CU split modes are governed by the EncModeCtrl" );
            return false;
            break;
        }
    
        switch( split )
        {
          case CU_HORZ_SPLIT:
          case CU_TRIH_SPLIT:
            if( cuECtx.get<bool>( QT_BEFORE_BT ) && cuECtx.get<bool>( DID_QUAD_SPLIT ) )
            {
              if( cuECtx.get<int>( MAX_QT_SUB_DEPTH ) > partitioner.currQtDepth + 1 )
              {
                if( featureToSet >= 0 ) cuECtx.set( featureToSet, false );
                return false;
              }
            }
            break;
          case CU_VERT_SPLIT:
          case CU_TRIV_SPLIT:
            if( cuECtx.get<bool>( QT_BEFORE_BT ) && cuECtx.get<bool>( DID_QUAD_SPLIT ) )
            {
              if( cuECtx.get<int>( MAX_QT_SUB_DEPTH ) > partitioner.currQtDepth + 1 )
              {
                if( featureToSet >= 0 ) cuECtx.set( featureToSet, false );
                return false;
              }
            }
            break;
          default:
            break;
        }
    
        if( split == CU_QUAD_SPLIT ) cuECtx.set( DID_QUAD_SPLIT, true );
        return true;
      }
      else
      {
        CHECK( encTestmode.type != ETM_POST_DONT_SPLIT, "Unknown mode" );
    
        if( !bestCS || ( bestCS && isModeSplit( bestMode ) ) )
        {
          return false;
        }
        else
        {
    #if REUSE_CU_RESULTS
          setFromCs( *bestCS, partitioner );
    
    #endif
          // assume the non-split modes are done and set the marks for the best found mode
          if( bestCS && bestCU )
          {
            if( CU::isInter( *bestCU ) )
            {
              relatedCU.isInter   = true;
    #if HM_CODED_CU_INFO
              relatedCU.isSkip   |= bestCU->skip;
    #else
              relatedCU.isSkip    = bestCU->skip;
    #endif
    #if JVET_L0646_GBI
              relatedCU.GBiIdx    = bestCU->GBiIdx;
    #endif
            }
            else if( CU::isIntra( *bestCU ) )
            {
              relatedCU.isIntra   = true;
            }
    #if ENABLE_SPLIT_PARALLELISM
            touch( partitioner.currArea() );
    #endif
            cuECtx.set( IS_BEST_NOSPLIT_SKIP, bestCU->skip );
          }
        }
    
        return false;
      }
    }
    
    bool EncModeCtrlMTnoRQT::useModeResult( const EncTestMode& encTestmode, CodingStructure*& tempCS, Partitioner& partitioner )
    {
      xExtractFeatures( encTestmode, *tempCS );
    
      ComprCUCtx& cuECtx = m_ComprCUCtxList.back();
    
    
      if(      encTestmode.type == ETM_SPLIT_BT_H )
      {
        cuECtx.set( BEST_HORZ_SPLIT_COST, tempCS->cost );
      }
      else if( encTestmode.type == ETM_SPLIT_BT_V )
      {
        cuECtx.set( BEST_VERT_SPLIT_COST, tempCS->cost );
      }
      else if( encTestmode.type == ETM_SPLIT_TT_H )
      {
        cuECtx.set( BEST_TRIH_SPLIT_COST, tempCS->cost );
      }
      else if( encTestmode.type == ETM_SPLIT_TT_V )
      {
        cuECtx.set( BEST_TRIV_SPLIT_COST, tempCS->cost );
      }
      else if( encTestmode.type == ETM_INTRA && encTestmode.partSize == SIZE_2Nx2N )
      {
        const CodingUnit cu = *tempCS->getCU( partitioner.chType );
    
        if( !cu.emtFlag )
        {
          cuECtx.bestEmtSize2Nx2N1stPass = tempCS->cost;
        }
      }
    
      if( m_pcEncCfg->getIMV4PelFast() && m_pcEncCfg->getIMV() && encTestmode.type == ETM_INTER_ME )
      {
        int imvMode = ( encTestmode.opts & ETO_IMV ) >> ETO_IMV_SHIFT;
    
        if( imvMode == 1 )
        {
          if( tempCS->cost < cuECtx.get<double>( BEST_IMV_COST ) )
          {
            cuECtx.set( BEST_IMV_COST, tempCS->cost );
          }
        }
        else if( imvMode == 0 )
        {
          if( tempCS->cost < cuECtx.get<double>( BEST_NO_IMV_COST ) )
          {
            cuECtx.set( BEST_NO_IMV_COST, tempCS->cost );
          }
        }
      }
    
      if( encTestmode.type == ETM_SPLIT_QT )
      {
        int maxQtD = 0;
        for( const auto& cu : tempCS->cus )
        {
          maxQtD = std::max<int>( maxQtD, cu->qtDepth );
        }
        cuECtx.set( MAX_QT_SUB_DEPTH, maxQtD );
      }
    
      if( ( tempCS->sps->getSpsNext().getMTTMode() & 1 ) == 1 )
      {
        int maxMtD = tempCS->pcv->getMaxBtDepth( *tempCS->slice, partitioner.chType ) + partitioner.currImplicitBtDepth;
    
        if( encTestmode.type == ETM_SPLIT_BT_H )
        {
          if( tempCS->cus.size() > 2 )
          {
            int h_2   = tempCS->area.blocks[partitioner.chType].height / 2;
            int cu1_h = tempCS->cus.front()->blocks[partitioner.chType].height;
            int cu2_h = tempCS->cus.back() ->blocks[partitioner.chType].height;
    
            cuECtx.set( DO_TRIH_SPLIT, cu1_h < h_2 || cu2_h < h_2 || partitioner.currMtDepth + 1 == maxMtD );
          }
        }
        else if( encTestmode.type == ETM_SPLIT_BT_V )
        {
          if( tempCS->cus.size() > 2 )
          {
            int w_2   = tempCS->area.blocks[partitioner.chType].width / 2;
            int cu1_w = tempCS->cus.front()->blocks[partitioner.chType].width;
            int cu2_w = tempCS->cus.back() ->blocks[partitioner.chType].width;
    
            cuECtx.set( DO_TRIV_SPLIT, cu1_w < w_2 || cu2_w < w_2 || partitioner.currMtDepth + 1 == maxMtD );
          }
        }
      }
    
      // for now just a simple decision based on RD-cost or choose tempCS if bestCS is not yet coded
      if( !cuECtx.bestCS || tempCS->features[ENC_FT_RD_COST] < cuECtx.bestCS->features[ENC_FT_RD_COST] )
      {
        cuECtx.bestCS = tempCS;
        cuECtx.bestCU = tempCS->cus[0];
        cuECtx.bestTU = cuECtx.bestCU->firstTU;
    
        if( isModeInter( encTestmode ) )
        {
          //Here we take the best cost of both inter modes. We are assuming only the inter modes (and all of them) have come before the intra modes!!!
          cuECtx.bestInterCost = cuECtx.bestCS->cost;
        }
    
        return true;
      }
      else
      {
        return false;
      }
    }
    
    #if ENABLE_SPLIT_PARALLELISM
    void EncModeCtrlMTnoRQT::copyState( const EncModeCtrl& other, const UnitArea& area )
    {
      const EncModeCtrlMTnoRQT* pOther = dynamic_cast<const EncModeCtrlMTnoRQT*>( &other );
    
      CHECK( !pOther, "Trying to copy state from a different type of controller" );
    
      this->EncModeCtrl        ::copyState( *pOther, area );
      this->CacheBlkInfoCtrl   ::copyState( *pOther, area );
    
      m_skipThreshold = pOther->m_skipThreshold;
    }
    
    int EncModeCtrlMTnoRQT::getNumParallelJobs( const CodingStructure &cs, Partitioner& partitioner ) const
    {
      int numJobs = 1; // for no-split coding
    
      if( partitioner.canSplit( CU_QUAD_SPLIT, cs ) )
      {
        numJobs = 2;
      }
    
      if( partitioner.canSplit( CU_VERT_SPLIT, cs ) )
      {
        numJobs = 3;
      }
    
      if( partitioner.canSplit( CU_HORZ_SPLIT, cs ) )
      {
        numJobs = 4;
      }
    
      if( partitioner.canSplit( CU_TRIV_SPLIT, cs ) )
      {
        numJobs = 5;
      }
    
      if( partitioner.canSplit( CU_TRIH_SPLIT, cs ) )
      {
        numJobs = 6;
      }
    
      CHECK( numJobs >= NUM_RESERVERD_SPLIT_JOBS, "More jobs specified than allowed" );
    
      return numJobs;
    }
    
    bool EncModeCtrlMTnoRQT::isParallelSplit( const CodingStructure &cs, Partitioner& partitioner ) const
    {
      if( partitioner.getImplicitSplit( cs ) != CU_DONT_SPLIT || cs.picture->scheduler.getSplitJobId() != 0 ) return false;
      const int numJobs = getNumParallelJobs( cs, partitioner );
      const int numPxl  = partitioner.currArea().Y().area();
      const int parlAt  = m_pcEncCfg->getNumSplitThreads() <= 3 ? 1024 : 256;
      if(  cs.slice->isIntra() && numJobs > 2 && ( numPxl == parlAt || !partitioner.canSplit( CU_QUAD_SPLIT, cs ) ) ) return true;
      if( !cs.slice->isIntra() && numJobs > 1 && ( numPxl == parlAt || !partitioner.canSplit( CU_QUAD_SPLIT, cs ) ) ) return true;
      return false;
    }
    
    bool EncModeCtrlMTnoRQT::parallelJobSelector( const EncTestMode& encTestmode, const CodingStructure &cs, Partitioner& partitioner ) const
    {
      // Job descriptors
      //  - 1: all non-split modes
      //  - 2: QT-split
      //  - 3: all vertical modes but TT_V
      //  - 4: all horizontal modes but TT_H
      //  - 5: TT_V
      //  - 6: TT_H
      switch( cs.picture->scheduler.getSplitJobId() )
      {
      case 1:
        // be sure to execute post dont split
        return !isModeSplit( encTestmode );
        break;
      case 2:
        return encTestmode.type == ETM_SPLIT_QT;
        break;
      case 3:
        switch( encTestmode.type )
        {
        case ETM_SPLIT_BT_V:
          return true;
          break;
        default:
          return false;
          break;
        }
        break;
      case 4:
        switch( encTestmode.type )
        {
        case ETM_SPLIT_BT_H:
          return true;
          break;
        default:
          return false;
          break;
        }
        break;
      case 5:
        return encTestmode.type == ETM_SPLIT_TT_V;
        break;
      case 6:
        return encTestmode.type == ETM_SPLIT_TT_H;
        break;
      default:
        THROW( "Unknown job-ID for parallelization of EncModeCtrlMTnoRQT: " << cs.picture->scheduler.getSplitJobId() );
        break;
      }
    }
    
    #endif