diff --git a/source/App/EncoderApp/EncApp.cpp b/source/App/EncoderApp/EncApp.cpp index a3f8e4b3f1021e855562c011dd5d3c0c36507d55..f419c4a306a13a6c47503e3766fe5a7365f174f7 100644 --- a/source/App/EncoderApp/EncApp.cpp +++ b/source/App/EncoderApp/EncApp.cpp @@ -297,6 +297,9 @@ void EncApp::xInitLibCfg() m_cEncLib.setQuadtreeTULog2MinSize ( m_quadtreeTULog2MinSize ); m_cEncLib.setQuadtreeTUMaxDepthInter ( m_uiQuadtreeTUMaxDepthInter ); m_cEncLib.setQuadtreeTUMaxDepthIntra ( m_uiQuadtreeTUMaxDepthIntra ); +#if JVET_M0428_ENC_DB_OPT + m_cEncLib.setUseEncDbOpt(m_encDbOpt); +#endif m_cEncLib.setUseFastLCTU ( m_useFastLCTU ); m_cEncLib.setFastInterSearchMode ( m_fastInterSearchMode ); m_cEncLib.setUseEarlyCU ( m_bUseEarlyCU ); diff --git a/source/App/EncoderApp/EncAppCfg.cpp b/source/App/EncoderApp/EncAppCfg.cpp index 733a913d8590e9dea4a5fe9fd1f19aafb0d25746..d0a101fc97c548d6b5a4a0ecfe5d2ef40992f6b8 100644 --- a/source/App/EncoderApp/EncAppCfg.cpp +++ b/source/App/EncoderApp/EncAppCfg.cpp @@ -891,6 +891,9 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] ) ("WrapAroundOffset", m_wrapAroundOffset, 0u, "Offset in luma samples used for computing the horizontal wrap-around position") // ADD_NEW_TOOL : (encoder app) add parsing parameters here +#if JVET_M0428_ENC_DB_OPT + ("EncDbOpt", m_encDbOpt, false, "Encoder optimization with deblocking filter") +#endif #if JVET_M0427_INLOOP_RESHAPER ("LumaReshapeEnable", m_lumaReshapeEnable, false, "Enable Reshaping for Luma Channel") ("ReshapeSignalType", m_reshapeSignalType, 0u, "Input signal type: 0: SDR, 1:PQ, 2:HLG") @@ -3230,6 +3233,9 @@ void EncAppCfg::xPrintParameter() msg(VERBOSE, "(Sigal:%s ", m_reshapeSignalType==0? "SDR" : "HDR-PQ"); msg(VERBOSE, ") "); } +#endif +#if JVET_M0428_ENC_DB_OPT + msg(VERBOSE, "EncDbOpt:%d ", m_encDbOpt); #endif msg( VERBOSE, "\nFAST TOOL CFG: " ); msg( VERBOSE, "LCTUFast:%d ", m_useFastLCTU ); diff --git a/source/App/EncoderApp/EncAppCfg.h b/source/App/EncoderApp/EncAppCfg.h index ad9930adf8e0a0b02a1a1fbf660489b8ccdea19d..14c72592ecb99f112f263b3073d8c6545af38c6e 100644 --- a/source/App/EncoderApp/EncAppCfg.h +++ b/source/App/EncoderApp/EncAppCfg.h @@ -273,6 +273,9 @@ protected: uint32_t m_reshapeSignalType; uint32_t m_intraCMD; ReshapeCW m_reshapeCW; +#endif +#if JVET_M0428_ENC_DB_OPT + bool m_encDbOpt; #endif unsigned m_uiMaxCUWidth; ///< max. CU width in pixel unsigned m_uiMaxCUHeight; ///< max. CU height in pixel diff --git a/source/Lib/CommonLib/CodingStructure.cpp b/source/Lib/CommonLib/CodingStructure.cpp index f52f4205834c8bfafe0841b22e45451cb4462b16..bf711f3ba8f175a93ca30d842a85771171ed226a 100644 --- a/source/Lib/CommonLib/CodingStructure.cpp +++ b/source/Lib/CommonLib/CodingStructure.cpp @@ -751,6 +751,11 @@ void CodingStructure::initSubStructure( CodingStructure& subStruct, const Channe { CHECK( this == &subStruct, "Trying to init self as sub-structure" ); +#if JVET_M0428_ENC_DB_OPT + subStruct.useDbCost = false; + subStruct.costDbOffset = 0; +#endif + for( uint32_t i = 0; i < subStruct.area.blocks.size(); i++ ) { CHECKD( subStruct.area.blocks[i].size() != subArea.blocks[i].size(), "Trying to init sub-structure of incompatible size" ); @@ -852,7 +857,9 @@ void CodingStructure::useSubStructure( const CodingStructure& subStruct, const C fracBits += subStruct.fracBits; dist += subStruct.dist; cost += subStruct.cost; - +#if JVET_M0428_ENC_DB_OPT + costDbOffset += subStruct.costDbOffset; +#endif if( parent ) { // allow this to be false at the top level @@ -916,7 +923,9 @@ void CodingStructure::useSubStructure( const CodingStructure& subStruct, const C fracBits += subStruct.fracBits; dist += subStruct.dist; cost += subStruct.cost; - +#if JVET_M0428_ENC_DB_OPT + costDbOffset += subStruct.costDbOffset; +#endif if( parent ) { // allow this to be false at the top level @@ -978,7 +987,9 @@ void CodingStructure::copyStructure( const CodingStructure& other, const Channel fracBits = other.fracBits; dist = other.dist; cost = other.cost; - +#if JVET_M0428_ENC_DB_OPT + costDbOffset = other.costDbOffset; +#endif CHECKD( area != other.area, "Incompatible sizes" ); const UnitArea dualITreeArea = CS::getArea( *this, this->area, chType ); @@ -1110,6 +1121,10 @@ void CodingStructure::initStructData( const int &QP, const bool &_isLosses, cons cost = MAX_DOUBLE; #if JVET_M0102_INTRA_SUBPARTITIONS lumaCost = MAX_DOUBLE; +#endif +#if JVET_M0428_ENC_DB_OPT + costDbOffset = 0; + useDbCost = false; #endif interHad = std::numeric_limits<Distortion>::max(); } diff --git a/source/Lib/CommonLib/CodingStructure.h b/source/Lib/CommonLib/CodingStructure.h index f28500a798c9a02ea29c2f58e19501540d80f94a..67ca6d94738a476430f8d78f28e61bfc22a143ce 100644 --- a/source/Lib/CommonLib/CodingStructure.h +++ b/source/Lib/CommonLib/CodingStructure.h @@ -179,6 +179,10 @@ public: static_vector<double, NUM_ENC_FEATURES> features; double cost; +#if JVET_M0428_ENC_DB_OPT + bool useDbCost; + double costDbOffset; +#endif #if JVET_M0102_INTRA_SUBPARTITIONS double lumaCost; #endif diff --git a/source/Lib/CommonLib/LoopFilter.cpp b/source/Lib/CommonLib/LoopFilter.cpp index b3362a350a49a9171e438ec58b791a79356ab8a6..8355c91554139ec4a6a80f1a4346c56904e50c1d 100644 --- a/source/Lib/CommonLib/LoopFilter.cpp +++ b/source/Lib/CommonLib/LoopFilter.cpp @@ -126,7 +126,19 @@ void LoopFilter::create( const unsigned uiMaxCUDepth ) m_aapucBS [edgeDir].resize( numPartitions ); m_aapbEdgeFilter[edgeDir].resize( numPartitions ); } +#if JVET_M0428_ENC_DB_OPT + m_enc = false; +#endif +} + +#if JVET_M0428_ENC_DB_OPT +void LoopFilter::initEncPicYuvBuffer(ChromaFormat chromaFormat, int lumaWidth, int lumaHeight) +{ + const UnitArea picArea(chromaFormat, Area(0, 0, lumaWidth, lumaHeight)); + m_encPicYuvBuffer.destroy(); + m_encPicYuvBuffer.create(picArea); } +#endif void LoopFilter::destroy() { @@ -135,6 +147,9 @@ void LoopFilter::destroy() m_aapucBS [edgeDir].clear(); m_aapbEdgeFilter[edgeDir].clear(); } +#if JVET_M0428_ENC_DB_OPT + m_encPicYuvBuffer.destroy(); +#endif } /** @@ -738,7 +753,11 @@ void LoopFilter::xEdgeFilterLuma(const CodingUnit& cu, const DeblockEdgeDir edge const CompArea& lumaArea = cu.block(COMPONENT_Y); const PreCalcValues& pcv = *cu.cs->pcv; +#if JVET_M0428_ENC_DB_OPT + PelBuf picYuvRec = m_enc ? m_encPicYuvBuffer.getBuf( lumaArea ) : cu.cs->getRecoBuf( lumaArea ); +#else PelBuf picYuvRec = cu.cs->getRecoBuf( lumaArea ); +#endif Pel *piSrc = picYuvRec.buf; const int iStride = picYuvRec.stride; Pel *piTmpSrc = piSrc; @@ -1010,9 +1029,13 @@ void LoopFilter::xEdgeFilterChroma(const CodingUnit& cu, const DeblockEdgeDir ed const PreCalcValues& pcv = *cu.cs->pcv; unsigned rasterIdx = getRasterIdx( lumaPos, pcv ); - +#if JVET_M0428_ENC_DB_OPT + PelBuf picYuvRecCb = m_enc ? m_encPicYuvBuffer.getBuf(cu.block(COMPONENT_Cb)) : cu.cs->getRecoBuf(cu.block(COMPONENT_Cb)); + PelBuf picYuvRecCr = m_enc ? m_encPicYuvBuffer.getBuf(cu.block(COMPONENT_Cr)) : cu.cs->getRecoBuf(cu.block(COMPONENT_Cr)); +#else PelBuf picYuvRecCb = cu.cs->getRecoBuf( cu.block(COMPONENT_Cb) ); PelBuf picYuvRecCr = cu.cs->getRecoBuf( cu.block(COMPONENT_Cr) ); +#endif Pel *piSrcCb = picYuvRecCb.buf; Pel *piSrcCr = picYuvRecCr.buf; const int iStride = picYuvRecCb.stride; diff --git a/source/Lib/CommonLib/LoopFilter.h b/source/Lib/CommonLib/LoopFilter.h index 5f2b0bafb0a13b0e004bed96061e9e07790e7529..88907e1d147c725acfe9f66793900e4f7fd5e03e 100644 --- a/source/Lib/CommonLib/LoopFilter.h +++ b/source/Lib/CommonLib/LoopFilter.h @@ -58,11 +58,16 @@ private: static_vector<char, MAX_NUM_PARTS_IN_CTU> m_aapucBS [NUM_EDGE_DIR]; ///< Bs for [Ver/Hor][Y/U/V][Blk_Idx] static_vector<bool, MAX_NUM_PARTS_IN_CTU> m_aapbEdgeFilter[NUM_EDGE_DIR]; LFCUParam m_stLFCUParam; ///< status structure - +#if JVET_M0428_ENC_DB_OPT + PelStorage m_encPicYuvBuffer; + bool m_enc; +#endif private: +#if !JVET_M0428_ENC_DB_OPT /// CU-level deblocking function void xDeblockCU ( CodingUnit& cu, const DeblockEdgeDir edgeDir ); - +#endif + // set / get functions void xSetLoopfilterParam ( const CodingUnit& cu ); @@ -111,6 +116,14 @@ public: LoopFilter(); ~LoopFilter(); +#if JVET_M0428_ENC_DB_OPT + /// CU-level deblocking function + void xDeblockCU(CodingUnit& cu, const DeblockEdgeDir edgeDir); + void initEncPicYuvBuffer(ChromaFormat chromaFormat, int lumaWidth, int lumaHeight); + PelStorage& getDbEncPicYuvBuffer() { return m_encPicYuvBuffer; } + void setEnc(bool b) { m_enc = b; } +#endif + void create ( const unsigned uiMaxCUDepth ); void destroy (); diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h index b313f177c2e13afbcea253cf45ff6b98218425d7..d611c5ad386ee9862e17ce4155a54775712f6d94 100644 --- a/source/Lib/CommonLib/TypeDef.h +++ b/source/Lib/CommonLib/TypeDef.h @@ -54,6 +54,8 @@ #define JVET_M0451_INTEROPERABILITY_POINT_SYNTAX 1 +#define JVET_M0428_ENC_DB_OPT 1 // Encoder optimization with deblocking filter + #define JVET_M0055_DEBUG_CTU 1 // DebugCTU encoder debug option #define JVET_M0297_32PT_MTS_ZERO_OUT 1 // 32 point MTS based on skipping high frequency coefficients diff --git a/source/Lib/EncoderLib/EncCfg.h b/source/Lib/EncoderLib/EncCfg.h index 12a54c46092c5d531f8459e13058d2960db3eb53..f63264bf2ef6082a1d275ff4be12002cb4b40712 100644 --- a/source/Lib/EncoderLib/EncCfg.h +++ b/source/Lib/EncoderLib/EncCfg.h @@ -288,6 +288,9 @@ protected: unsigned m_reshapeSignalType; unsigned m_intraCMD; ReshapeCW m_reshapeCW; +#endif +#if JVET_M0428_ENC_DB_OPT + bool m_encDbOpt; #endif bool m_useFastLCTU; bool m_useFastMrg; @@ -871,6 +874,10 @@ public: void setMaxCodingDepth ( uint32_t u ) { m_maxTotalCUDepth = u; } uint32_t getMaxCodingDepth () const { return m_maxTotalCUDepth; } void setLog2DiffMaxMinCodingBlockSize( uint32_t u ) { m_log2DiffMaxMinCodingBlockSize = u; } +#if JVET_M0428_ENC_DB_OPT + void setUseEncDbOpt ( bool n ) { m_encDbOpt = n; } + bool getUseEncDbOpt () const { return m_encDbOpt; } +#endif void setUseFastLCTU ( bool n ) { m_useFastLCTU = n; } bool getUseFastLCTU () const { return m_useFastLCTU; } diff --git a/source/Lib/EncoderLib/EncCu.cpp b/source/Lib/EncoderLib/EncCu.cpp index 101c0cbe6706e9f99253c0354999a86c0cf6b914..6f461e435fcd5d7d694dd557f6d907786ba31105 100644 --- a/source/Lib/EncoderLib/EncCu.cpp +++ b/source/Lib/EncoderLib/EncCu.cpp @@ -318,6 +318,9 @@ void EncCu::init( EncLib* pcEncLib, const SPS& sps PARL_PARAM( const int tId ) ) m_pcEncLib = pcEncLib; m_dataId = tId; #endif +#if JVET_M0428_ENC_DB_OPT + m_pcLoopFilter = pcEncLib->getLoopFilter(); +#endif #if JVET_M0170_MRG_SHARELIST m_shareState = NO_SHARE; m_pcInterSearch->setShareState(0); @@ -653,6 +656,9 @@ bool EncCu::xCheckBestMode( CodingStructure *&tempCS, CodingStructure *&bestCS, std::swap( tempCS, bestCS ); // store temp best CI for next CU coding m_CurrCtx->best = m_CABACEstimator->getCtx(); +#if JVET_M0428_ENC_DB_OPT + m_bestModeUpdated = true; +#endif bestCSUpdated = true; } } @@ -1232,7 +1238,14 @@ void EncCu::xCheckModeSplit(CodingStructure *&tempCS, CodingStructure *&bestCS, #endif const double factor = ( tempCS->currQP[partitioner.chType] > 30 ? 1.1 : 1.075 ); +#if JVET_M0428_ENC_DB_OPT + tempCS->useDbCost = m_pcEncCfg->getUseEncDbOpt(); + if (!tempCS->useDbCost) + CHECK(bestCS->costDbOffset != 0, "error"); + const double cost = m_pcRdCost->calcRdCost( uint64_t( m_CABACEstimator->getEstFracBits() + ( ( bestCS->fracBits ) / factor ) ), Distortion( bestCS->dist / factor ) ) + bestCS->costDbOffset / factor; +#else const double cost = m_pcRdCost->calcRdCost( uint64_t( m_CABACEstimator->getEstFracBits() + ( ( bestCS->fracBits ) / factor ) ), Distortion( bestCS->dist / factor ) ); +#endif m_CABACEstimator->getCtx() = SubCtx( Ctx::SplitFlag, ctxStartSP ); #if JVET_M0421_SPLIT_SIG @@ -1243,7 +1256,11 @@ void EncCu::xCheckModeSplit(CodingStructure *&tempCS, CodingStructure *&bestCS, m_CABACEstimator->getCtx() = SubCtx( Ctx::BTSplitFlag, ctxStartBT ); #endif +#if JVET_M0428_ENC_DB_OPT + if (cost > bestCS->cost + bestCS->costDbOffset) +#else if( cost > bestCS->cost ) +#endif { xCheckBestMode( tempCS, bestCS, partitioner, encTestMode ); return; @@ -1355,6 +1372,10 @@ void EncCu::xCheckModeSplit(CodingStructure *&tempCS, CodingStructure *&bestCS, { CHECK( split == CU_QUAD_SPLIT, "Split decision reusing cannot skip quad split" ); tempCS->cost = MAX_DOUBLE; +#if JVET_M0428_ENC_DB_OPT + tempCS->costDbOffset = 0; + tempCS->useDbCost = m_pcEncCfg->getUseEncDbOpt(); +#endif m_CurrCtx--; partitioner.exitCurrSplit(); bool bestCSUpdated = @@ -1483,9 +1504,18 @@ void EncCu::xCheckModeSplit(CodingStructure *&tempCS, CodingStructure *&bestCS, #endif { bestCS->cost = MAX_DOUBLE; +#if JVET_M0428_ENC_DB_OPT + bestCS->costDbOffset = 0; +#endif } } - +#if JVET_M0428_ENC_DB_OPT + else + { + bestCS->costDbOffset = 0; + } + tempCS->useDbCost = m_pcEncCfg->getUseEncDbOpt(); +#endif // RD check for sub partitioned coding structure. bool bestCSUpdated = @@ -1585,6 +1615,10 @@ void EncCu::xCheckRDCostIntra( CodingStructure *&tempCS, CodingStructure *&bestC tempCS->interHad = interHad; +#if JVET_M0428_ENC_DB_OPT + m_bestModeUpdated = tempCS->useDbCost = bestCS->useDbCost = false; +#endif + if( isLuma( partitioner.chType ) ) { #if JVET_M0102_INTRA_SUBPARTITIONS @@ -1705,6 +1739,12 @@ void EncCu::xCheckRDCostIntra( CodingStructure *&tempCS, CodingStructure *&bestC m_modeCtrl->setBestCostWithoutSplitFlags( tmpCostWithoutSplitFlags ); } #endif + +#if JVET_M0428_ENC_DB_OPT + xCalDebCost( *tempCS, partitioner ); + tempCS->useDbCost = m_pcEncCfg->getUseEncDbOpt(); +#endif + #if !JVET_M0464_UNI_MTS // we save the cost of the modes for the first EMT pass if( !emtCuFlag ) static_cast< double& >( costSize2Nx2NemtFirstPass ) = tempCS->cost; @@ -1804,6 +1844,11 @@ void EncCu::xCheckIntraPCM(CodingStructure *&tempCS, CodingStructure *&bestCS, P xCheckDQP( *tempCS, partitioner ); +#if JVET_M0428_ENC_DB_OPT + xCalDebCost( *tempCS, partitioner ); + tempCS->useDbCost = m_pcEncCfg->getUseEncDbOpt(); +#endif + #if WCG_EXT DTRACE_MODE_COST( *tempCS, m_pcRdCost->getLambda( true ) ); #else @@ -1936,6 +1981,10 @@ void EncCu::xCheckRDCostHashInter( CodingStructure *&tempCS, CodingStructure *&b const unsigned wIdx = gp_sizeIdxInfo->idxFrom(tempCS->area.lwidth()); double equGBiCost = MAX_DOUBLE; +#if JVET_M0428_ENC_DB_OPT + m_bestModeUpdated = tempCS->useDbCost = bestCS->useDbCost = false; +#endif + #if JVET_M0464_UNI_MTS xEncodeInterResidual(tempCS, bestCS, partitioner, encTestMode, 0 , m_pImvTempCS ? m_pImvTempCS[wIdx] : NULL @@ -1949,6 +1998,13 @@ void EncCu::xCheckRDCostHashInter( CodingStructure *&tempCS, CodingStructure *&b , &equGBiCost #endif ); + +#if JVET_M0428_ENC_DB_OPT + if ( m_bestModeUpdated && bestCS->cost != MAX_DOUBLE ) + { + xCalDebCost( *bestCS, partitioner ); + } +#endif } tempCS->initStructData(encTestMode.qp, encTestMode.lossless); @@ -2458,7 +2514,9 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *& } } } - +#if JVET_M0428_ENC_DB_OPT + m_bestModeUpdated = tempCS->useDbCost = bestCS->useDbCost = false; +#endif #if !JVET_M0253_HASH_ME const uint32_t iteration = encTestMode.lossless ? 1 : 2; @@ -2669,6 +2727,12 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *& } } } +#if JVET_M0428_ENC_DB_OPT + if ( m_bestModeUpdated && bestCS->cost != MAX_DOUBLE ) + { + xCalDebCost( *bestCS, partitioner ); + } +#endif } void EncCu::xCheckRDCostMergeTriangle2Nx2N( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode ) @@ -2868,7 +2932,9 @@ void EncCu::xCheckRDCostMergeTriangle2Nx2N( CodingStructure *&tempCS, CodingStru tempCS->initStructData( encTestMode.qp, encTestMode.lossless ); } - +#if JVET_M0428_ENC_DB_OPT + m_bestModeUpdated = tempCS->useDbCost = bestCS->useDbCost = false; +#endif { #if !JVET_M0253_HASH_ME const uint8_t iteration = encTestMode.lossless ? 1 : 2; @@ -2970,6 +3036,12 @@ void EncCu::xCheckRDCostMergeTriangle2Nx2N( CodingStructure *&tempCS, CodingStru }// end loop mrgHADIdx } } +#if JVET_M0428_ENC_DB_OPT + if ( m_bestModeUpdated && bestCS->cost != MAX_DOUBLE ) + { + xCalDebCost( *bestCS, partitioner ); + } +#endif } void EncCu::xCheckRDCostAffineMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode ) @@ -2983,7 +3055,9 @@ void EncCu::xCheckRDCostAffineMerge2Nx2N( CodingStructure *&tempCS, CodingStruct { return; } - +#if JVET_M0428_ENC_DB_OPT + m_bestModeUpdated = tempCS->useDbCost = bestCS->useDbCost = false; +#endif const Slice &slice = *tempCS->slice; CHECK( slice.getSliceType() == I_SLICE, "Affine Merge modes not available for I-slices" ); @@ -3266,6 +3340,12 @@ void EncCu::xCheckRDCostAffineMerge2Nx2N( CodingStructure *&tempCS, CodingStruct } } } +#if JVET_M0428_ENC_DB_OPT + if ( m_bestModeUpdated && bestCS->cost != MAX_DOUBLE ) + { + xCalDebCost( *bestCS, partitioner ); + } +#endif } ////////////////////////////////////////////////////////////////////////////////////////////// // ibc merge/skip mode check @@ -3450,6 +3530,9 @@ void EncCu::xCheckRDCostIBCModeMerge2Nx2N(CodingStructure *&tempCS, CodingStruct tempCS->dist = 0; tempCS->fracBits = 0; tempCS->cost = MAX_DOUBLE; +#if JVET_M0428_ENC_DB_OPT + tempCS->costDbOffset = 0; +#endif tempCS->initStructData(encTestMode.qp, encTestMode.lossless); return; } @@ -3460,7 +3543,9 @@ void EncCu::xCheckRDCostIBCModeMerge2Nx2N(CodingStructure *&tempCS, CodingStruct const unsigned int iteration = encTestMode.lossless ? 1 : 2; - +#if JVET_M0428_ENC_DB_OPT + m_bestModeUpdated = tempCS->useDbCost = bestCS->useDbCost = false; +#endif // 2. Pass: check candidates using full RD test for (unsigned int numResidualPass = 0; numResidualPass < iteration; numResidualPass++) { @@ -3577,7 +3662,12 @@ void EncCu::xCheckRDCostIBCModeMerge2Nx2N(CodingStructure *&tempCS, CodingStruct } } } - +#if JVET_M0428_ENC_DB_OPT + if ( m_bestModeUpdated && bestCS->cost != MAX_DOUBLE ) + { + xCalDebCost( *bestCS, partitioner ); + } +#endif } void EncCu::xCheckRDCostIBCMode(CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode) @@ -3589,6 +3679,10 @@ void EncCu::xCheckRDCostIBCMode(CodingStructure *&tempCS, CodingStructure *&best tempCS->initStructData(encTestMode.qp, encTestMode.lossless); +#if JVET_M0428_ENC_DB_OPT + m_bestModeUpdated = tempCS->useDbCost = bestCS->useDbCost = false; +#endif + CodingUnit &cu = tempCS->addCU(CS::getArea(*tempCS, tempCS->area, partitioner.chType), partitioner.chType); partitioner.setCUData(cu); @@ -3615,6 +3709,10 @@ void EncCu::xCheckRDCostIBCMode(CodingStructure *&tempCS, CodingStructure *&best CU::addPUs(cu); +#if JVET_M0428_ENC_DB_OPT + m_bestModeUpdated = tempCS->useDbCost = bestCS->useDbCost = false; +#endif + PredictionUnit& pu = *cu.firstPU; cu.mmvdSkip = false; pu.mmvdMergeFlag = false; @@ -3675,6 +3773,14 @@ void EncCu::xCheckRDCostIBCMode(CodingStructure *&tempCS, CodingStructure *&best xCheckDQP(*tempCS, partitioner); } +#if JVET_M0428_ENC_DB_OPT + tempCS->useDbCost = m_pcEncCfg->getUseEncDbOpt(); + if ( m_bestModeUpdated ) + { + xCalDebCost( *tempCS, partitioner ); + } +#endif + DTRACE_MODE_COST(*tempCS, m_pcRdCost->getLambda()); xCheckBestMode(tempCS, bestCS, partitioner, encTestMode); @@ -3705,6 +3811,9 @@ void EncCu::xCheckRDCostIBCMode(CodingStructure *&tempCS, CodingStructure *&best tempCS->dist = 0; tempCS->fracBits = 0; tempCS->cost = MAX_DOUBLE; +#if JVET_M0428_ENC_DB_OPT + tempCS->costDbOffset = 0; +#endif } } #endif @@ -3716,6 +3825,9 @@ void EncCu::xCheckRDCostIBCMode(CodingStructure *&tempCS, CodingStructure *&best tempCS->dist = 0; tempCS->fracBits = 0; tempCS->cost = MAX_DOUBLE; +#if JVET_M0428_ENC_DB_OPT + tempCS->costDbOffset = 0; +#endif } } // chroma CU ibc comp @@ -3766,6 +3878,13 @@ void EncCu::xCheckRDCostIBCMode(CodingStructure *&tempCS, CodingStructure *&best xEncodeDontSplit(*tempCS, partitioner); xCheckDQP(*tempCS, partitioner); +#if JVET_M0428_ENC_DB_OPT + tempCS->useDbCost = m_pcEncCfg->getUseEncDbOpt(); + if ( m_bestModeUpdated ) + { + xCalDebCost( *tempCS, partitioner ); + } +#endif DTRACE_MODE_COST(*tempCS, m_pcRdCost->getLambda()); @@ -3776,6 +3895,9 @@ void EncCu::xCheckRDCostIBCMode(CodingStructure *&tempCS, CodingStructure *&best tempCS->dist = 0; tempCS->fracBits = 0; tempCS->cost = MAX_DOUBLE; +#if JVET_M0428_ENC_DB_OPT + tempCS->costDbOffset = 0; +#endif } } } @@ -3807,6 +3929,10 @@ void EncCu::xCheckRDCostInter( CodingStructure *&tempCS, CodingStructure *&bestC double curBestCost = bestCS->cost; double equGBiCost = MAX_DOUBLE; +#if JVET_M0428_ENC_DB_OPT + m_bestModeUpdated = tempCS->useDbCost = bestCS->useDbCost = false; +#endif + for( int gbiLoopIdx = 0; gbiLoopIdx < gbiLoopNum; gbiLoopIdx++ ) { if( m_pcEncCfg->getUseGBiFast() ) @@ -3911,6 +4037,12 @@ void EncCu::xCheckRDCostInter( CodingStructure *&tempCS, CodingStructure *&bestC break; } } // for( UChar gbiLoopIdx = 0; gbiLoopIdx < gbiLoopNum; gbiLoopIdx++ ) +#if JVET_M0428_ENC_DB_OPT + if ( m_bestModeUpdated && bestCS->cost != MAX_DOUBLE ) + { + xCalDebCost( *bestCS, partitioner ); + } +#endif } @@ -3925,6 +4057,10 @@ bool EncCu::xCheckRDCostInterIMV( CodingStructure *&tempCS, CodingStructure *&be CHECK( iIMV != 1 && iIMV != 2 && iIMV != 3, "Unsupported IMV Mode" ); // Fast 4-Pel Mode +#if JVET_M0428_ENC_DB_OPT + m_bestModeUpdated = tempCS->useDbCost = bestCS->useDbCost = false; +#endif + EncTestMode encTestModeBase = encTestMode; // copy for clearing non-IMV options encTestModeBase.opts = EncTestModeOpts( encTestModeBase.opts & ETO_IMV ); // clear non-IMV options (is that intended?) @@ -4168,6 +4304,13 @@ bool EncCu::xCheckRDCostInterIMV( CodingStructure *&tempCS, CodingStructure *&be #endif } // for( UChar gbiLoopIdx = 0; gbiLoopIdx < gbiLoopNum; gbiLoopIdx++ ) +#if JVET_M0428_ENC_DB_OPT + if ( m_bestModeUpdated && bestCS->cost != MAX_DOUBLE ) + { + xCalDebCost( *bestCS, partitioner ); + } +#endif + #if JVET_M0246_AFFINE_AMVR return tempCS->slice->getSPS()->getAffineAmvrEnabledFlag() ? validMode : true; #else @@ -4175,6 +4318,215 @@ bool EncCu::xCheckRDCostInterIMV( CodingStructure *&tempCS, CodingStructure *&be #endif } +#if JVET_M0428_ENC_DB_OPT +void EncCu::xCalDebCost( CodingStructure &cs, Partitioner &partitioner, bool calDist ) +{ + if ( cs.cost == MAX_DOUBLE ) + { + cs.costDbOffset = 0; + } + + if ( cs.slice->getDeblockingFilterDisable() || ( !m_pcEncCfg->getUseEncDbOpt() && !calDist ) ) + { + return; + } + + m_pcLoopFilter->setEnc(true); + const ChromaFormat format = cs.area.chromaFormat; + CodingUnit* cu = cs.getCU(partitioner.chType); + const Position lumaPos = cu->Y().valid() ? cu->Y().pos() : recalcPosition( format, cu->chType, CHANNEL_TYPE_LUMA, cu->blocks[cu->chType].pos() ); + bool topEdgeAvai = lumaPos.y > 0 && ( ( lumaPos.y % 8 ) == 0 ); + bool leftEdgeAvai = lumaPos.x > 0 && ( ( lumaPos.x % 8 ) == 0 ); + bool anyEdgeAvai = topEdgeAvai || leftEdgeAvai; + cs.costDbOffset = 0; + + if ( calDist ) + { + const UnitArea currCsArea = clipArea( CS::getArea( cs, cs.area, partitioner.chType ), *cs.picture ); + ComponentID compStr = ( CS::isDualITree( cs ) && !isLuma( partitioner.chType ) ) ? COMPONENT_Cb : COMPONENT_Y; + ComponentID compEnd = ( CS::isDualITree( cs ) && isLuma( partitioner.chType ) ) ? COMPONENT_Y : COMPONENT_Cr; + Distortion finalDistortion = 0; + for ( int comp = compStr; comp <= compEnd; comp++ ) + { + const ComponentID compID = ComponentID( comp ); + CPelBuf org = cs.getOrgBuf( compID ); + CPelBuf reco = cs.getRecoBuf( compID ); + finalDistortion += getDistortionDb( cs, org, reco, compID, currCsArea.block( compID ), false ); + } + //updated distortion + cs.dist = finalDistortion; + } + + if ( anyEdgeAvai && m_pcEncCfg->getUseEncDbOpt() ) + { + ComponentID compStr = ( CS::isDualITree( cs ) && !isLuma( partitioner.chType ) ) ? COMPONENT_Cb : COMPONENT_Y; + ComponentID compEnd = ( CS::isDualITree( cs ) && isLuma( partitioner.chType ) ) ? COMPONENT_Y : COMPONENT_Cr; + + const UnitArea currCsArea = clipArea( CS::getArea( cs, cs.area, partitioner.chType ), *cs.picture ); + + PelStorage& picDbBuf = m_pcLoopFilter->getDbEncPicYuvBuffer(); + + //deblock neighbour pixels + const Size lumaSize = cu->Y().valid() ? cu->Y().size() : recalcSize( format, cu->chType, CHANNEL_TYPE_LUMA, cu->blocks[cu->chType].size() ); + +#if JVET_M0471_LONG_DEBLOCKING_FILTERS + const int verOffset = lumaPos.y > 7 ? 8 : 4; + const int horOffset = lumaPos.x > 7 ? 8 : 4; +#else + const int verOffset = 4; + const int horOffset = 4; +#endif + const UnitArea areaTop( format, Area( lumaPos.x, lumaPos.y - verOffset, lumaSize.width, verOffset ) ); + const UnitArea areaLeft( format, Area( lumaPos.x - horOffset, lumaPos.y, horOffset, lumaSize.height ) ); + for ( int compIdx = compStr; compIdx <= compEnd; compIdx++ ) + { + ComponentID compId = (ComponentID)compIdx; + + //Copy current CU's reco to Deblock Pic Buffer + const CompArea& curCompArea = currCsArea.block( compId ); + picDbBuf.getBuf( curCompArea ).copyFrom( cs.getRecoBuf( curCompArea ) ); +#if JVET_M0427_INLOOP_RESHAPER + if ( cs.slice->getReshapeInfo().getUseSliceReshaper() && m_pcReshape->getSliceReshaperInfo().getUseSliceReshaper() && isLuma( compId ) ) + { + picDbBuf.getBuf( curCompArea ).rspSignal( m_pcReshape->getInvLUT() ); + } +#endif + + //left neighbour + if ( leftEdgeAvai ) + { + const CompArea& compArea = areaLeft.block(compId); + picDbBuf.getBuf( compArea ).copyFrom( cs.picture->getRecoBuf( compArea ) ); +#if JVET_M0427_INLOOP_RESHAPER + if ( cs.slice->getReshapeInfo().getUseSliceReshaper() && m_pcReshape->getSliceReshaperInfo().getUseSliceReshaper() && isLuma( compId ) ) + { + picDbBuf.getBuf( compArea ).rspSignal( m_pcReshape->getInvLUT() ); + } +#endif + } + //top neighbour + if ( topEdgeAvai ) + { + const CompArea& compArea = areaTop.block( compId ); + picDbBuf.getBuf( compArea ).copyFrom( cs.picture->getRecoBuf( compArea ) ); +#if JVET_M0427_INLOOP_RESHAPER + if ( cs.slice->getReshapeInfo().getUseSliceReshaper() && m_pcReshape->getSliceReshaperInfo().getUseSliceReshaper() && isLuma( compId ) ) + { + picDbBuf.getBuf( compArea ).rspSignal( m_pcReshape->getInvLUT() ); + } +#endif + } + } + + //deblock + if ( leftEdgeAvai ) + { + m_pcLoopFilter->xDeblockCU( *cu, EDGE_VER ); + } + + if (topEdgeAvai) + { + m_pcLoopFilter->xDeblockCU( *cu, EDGE_HOR ); + } + + //update current CU SSE + Distortion distCur = 0; + for ( int compIdx = compStr; compIdx <= compEnd; compIdx++ ) + { + ComponentID compId = (ComponentID)compIdx; + CPelBuf reco = picDbBuf.getBuf( currCsArea.block( compId ) ); + CPelBuf org = cs.getOrgBuf( compId ); + distCur += getDistortionDb( cs, org, reco, compId, currCsArea.block( compId ), true ); + } + + //calculate difference between DB_before_SSE and DB_after_SSE for neighbouring CUs + Distortion distBeforeDb = 0, distAfterDb = 0; + for (int compIdx = compStr; compIdx <= compEnd; compIdx++) + { + ComponentID compId = (ComponentID)compIdx; + if ( leftEdgeAvai ) + { + const CompArea& compArea = areaLeft.block( compId ); + CPelBuf org = cs.picture->getOrigBuf( compArea ); + CPelBuf reco = cs.picture->getRecoBuf( compArea ); + CPelBuf recoDb = picDbBuf.getBuf( compArea ); + distBeforeDb += getDistortionDb( cs, org, reco, compId, compArea, false ); + distAfterDb += getDistortionDb( cs, org, recoDb, compId, compArea, true ); + } + if ( topEdgeAvai ) + { + const CompArea& compArea = areaTop.block( compId ); + CPelBuf org = cs.picture->getOrigBuf( compArea ); + CPelBuf reco = cs.picture->getRecoBuf( compArea ); + CPelBuf recoDb = picDbBuf.getBuf( compArea ); + distBeforeDb += getDistortionDb( cs, org, reco, compId, compArea, false ); + distAfterDb += getDistortionDb( cs, org, recoDb, compId, compArea, true ); + } + } + + //updated cost + int64_t distTmp = distCur - cs.dist + distAfterDb - distBeforeDb; + int sign = distTmp < 0 ? -1 : 1; + distTmp = distTmp < 0 ? -distTmp : distTmp; + cs.costDbOffset = sign * m_pcRdCost->calcRdCost( 0, distTmp ); + } + + m_pcLoopFilter->setEnc( false ); +} + +Distortion EncCu::getDistortionDb( CodingStructure &cs, CPelBuf org, CPelBuf reco, ComponentID compID, const CompArea& compArea, bool afterDb ) +{ + Distortion dist = 0; +#if WCG_EXT + CPelBuf orgLuma = cs.picture->getOrigBuf( compArea ); +#if JVET_M0427_INLOOP_RESHAPER + if ( m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled() || ( + m_pcEncCfg->getReshaper() && ( cs.slice->getReshapeInfo().getUseSliceReshaper() && m_pcReshape->getCTUFlag() ) ) ) +#else + if ( m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled() ) +#endif + { +#if JVET_M0427_INLOOP_RESHAPER + if ( compID == COMPONENT_Y && !afterDb ) + { + CompArea tmpArea( COMPONENT_Y, cs.area.chromaFormat, Position( 0, 0 ), compArea.size() ); + PelBuf tmpRecLuma = m_tmpStorageLCU->getBuf( tmpArea ); + tmpRecLuma.copyFrom( reco ); + tmpRecLuma.rspSignal( m_pcReshape->getInvLUT() ); + dist += m_pcRdCost->getDistPart( org, tmpRecLuma, cs.sps->getBitDepth( toChannelType( compID ) ), compID, DF_SSE_WTD, &orgLuma ); + } + else +#endif + { + dist += m_pcRdCost->getDistPart( org, reco, cs.sps->getBitDepth( toChannelType( compID ) ), compID, DF_SSE_WTD, &orgLuma ); + } + } +#if JVET_M0427_INLOOP_RESHAPER + else if ( m_pcEncCfg->getReshaper() && cs.slice->getReshapeInfo().getUseSliceReshaper() && cs.slice->isIntra() ) //intra slice + { + if ( compID == COMPONENT_Y && afterDb ) + { + CompArea tmpArea( COMPONENT_Y, cs.area.chromaFormat, Position( 0, 0 ), compArea.size() ); + PelBuf tmpRecLuma = m_tmpStorageLCU->getBuf( tmpArea ); + tmpRecLuma.copyFrom( reco ); + tmpRecLuma.rspSignal( m_pcReshape->getFwdLUT() ); + dist += m_pcRdCost->getDistPart( org, tmpRecLuma, cs.sps->getBitDepth( toChannelType( compID ) ), compID, DF_SSE ); + } + else + { + dist += m_pcRdCost->getDistPart( org, reco, cs.sps->getBitDepth(toChannelType( compID ) ), compID, DF_SSE ); + } + } +#endif + else +#endif + { + dist = m_pcRdCost->getDistPart( org, reco, cs.sps->getBitDepth( toChannelType( compID ) ), compID, DF_SSE ); + } + return dist; +} +#endif + #if JVET_M0464_UNI_MTS void EncCu::xEncodeInterResidual( CodingStructure *&tempCS , CodingStructure *&bestCS @@ -4331,6 +4683,9 @@ void EncCu::xEncodeInterResidual( CodingStructure *&tempCS, CodingStructure *&be tempCS->dist = 0; tempCS->fracBits = 0; tempCS->cost = MAX_DOUBLE; +#if JVET_M0428_ENC_DB_OPT + tempCS->costDbOffset = 0; +#endif } reloadCU = true; // enable cu reloading @@ -4414,6 +4769,9 @@ void EncCu::xEncodeInterResidual( CodingStructure *&tempCS, CodingStructure *&be if (tempCS->getPU(partitioner.chType)->mhIntraFlag) { tempCS->cost = MAX_DOUBLE; +#if JVET_M0428_ENC_DB_OPT + tempCS->costDbOffset = 0; +#endif return; } } @@ -4715,8 +5073,6 @@ void EncCu::xEncodeDontSplit( CodingStructure &cs, Partitioner &partitioner ) #if REUSE_CU_RESULTS void EncCu::xReuseCachedResult( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner ) { - const SPS &sps = *tempCS->sps; - BestEncInfoCache* bestEncCache = dynamic_cast<BestEncInfoCache*>( m_modeCtrl ); CHECK( !bestEncCache, "If this mode is chosen, mode controller has to implement the mode caching capabilities" ); EncTestMode cachedMode; @@ -4740,6 +5096,11 @@ void EncCu::xReuseCachedResult( CodingStructure *&tempCS, CodingStructure *&best xReconInter( cu ); } +#if JVET_M0428_ENC_DB_OPT + tempCS->useDbCost = m_pcEncCfg->getUseEncDbOpt(); + xCalDebCost( *tempCS, partitioner, true ); +#else + const SPS &sps = *tempCS->sps; Distortion finalDistortion = 0; const int numValidComponents = getNumberValidComponents( tempCS->area.chromaFormat ); @@ -4782,6 +5143,7 @@ void EncCu::xReuseCachedResult( CodingStructure *&tempCS, CodingStructure *&best #endif finalDistortion += m_pcRdCost->getDistPart( org, reco, sps.getBitDepth( toChannelType( compID ) ), compID, DF_SSE ); } +#endif m_CABACEstimator->getCtx() = m_CurrCtx->start; m_CABACEstimator->resetBits(); @@ -4791,7 +5153,9 @@ void EncCu::xReuseCachedResult( CodingStructure *&tempCS, CodingStructure *&best cuCtx.isChromaQpAdjCoded = true; m_CABACEstimator->coding_unit( cu, partitioner, cuCtx ); +#if JVET_M0428_ENC_DB_OPT == 0 tempCS->dist = finalDistortion; +#endif tempCS->fracBits = m_CABACEstimator->getEstFracBits(); tempCS->cost = m_pcRdCost->calcRdCost( tempCS->fracBits, tempCS->dist ); @@ -4804,7 +5168,6 @@ void EncCu::xReuseCachedResult( CodingStructure *&tempCS, CodingStructure *&best THROW( "Should never happen!" ); } } - #endif diff --git a/source/Lib/EncoderLib/EncCu.h b/source/Lib/EncoderLib/EncCu.h index 21286871bb0c19299efbb04b73eb1ca1a768c789..31921823e27bda64cb7e5dd0ceba9277e8fc7c08 100644 --- a/source/Lib/EncoderLib/EncCu.h +++ b/source/Lib/EncoderLib/EncCu.h @@ -46,6 +46,9 @@ #include "CommonLib/Unit.h" #include "CommonLib/UnitPartitioner.h" #include "CommonLib/IbcHashMap.h" +#if JVET_M0428_ENC_DB_OPT +#include "CommonLib/LoopFilter.h" +#endif #if REUSE_CU_RESULTS || JVET_M0170_MRG_SHARELIST || JVET_M0427_INLOOP_RESHAPER #include "DecoderLib/DecCu.h" @@ -84,7 +87,9 @@ class EncCu #endif { private: - +#if JVET_M0428_ENC_DB_OPT + bool m_bestModeUpdated; +#endif struct CtxPair { Ctx start; @@ -116,6 +121,9 @@ private: TrQuant* m_pcTrQuant; RdCost* m_pcRdCost; EncSlice* m_pcSliceEncoder; +#if JVET_M0428_ENC_DB_OPT + LoopFilter* m_pcLoopFilter; +#endif CABACWriter* m_CABACEstimator; RateCtrl* m_pcRateCtrl; @@ -191,6 +199,11 @@ public: protected: +#if JVET_M0428_ENC_DB_OPT + void xCalDebCost ( CodingStructure &cs, Partitioner &partitioner, bool calDist = false ); + Distortion getDistortionDb ( CodingStructure &cs, CPelBuf org, CPelBuf reco, ComponentID compID, const CompArea& compArea, bool afterDb ); +#endif + void xCompressCU ( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &pm , LutMotionCand *&tempMotCandLUTs , LutMotionCand *&bestMotCandLUTs diff --git a/source/Lib/EncoderLib/EncLib.cpp b/source/Lib/EncoderLib/EncLib.cpp index e6a11e25e7f39767d88eea7e772209f7263d729d..c975d68d8e9c510d259ed15273a0adfaa4dcb8b6 100644 --- a/source/Lib/EncoderLib/EncLib.cpp +++ b/source/Lib/EncoderLib/EncLib.cpp @@ -130,7 +130,12 @@ void EncLib::create () } m_cLoopFilter.create( m_maxTotalCUDepth ); - +#if JVET_M0428_ENC_DB_OPT + if ( !m_bLoopFilterDisable ) + { + m_cLoopFilter.initEncPicYuvBuffer( m_chromaFormatIDC, getSourceWidth(), getSourceHeight() ); + } +#endif if( m_alf ) { m_cEncALF.create( getSourceWidth(), getSourceHeight(), m_chromaFormatIDC, m_maxCUWidth, m_maxCUHeight, m_maxTotalCUDepth, m_bitDepth, m_inputBitDepth ); diff --git a/source/Lib/EncoderLib/EncModeCtrl.cpp b/source/Lib/EncoderLib/EncModeCtrl.cpp index d95114f9ed816b2895ec4971a27e07b5e4e89d5b..2421dbdb4f6eb4426fb28b50bd23378da97a5688 100644 --- a/source/Lib/EncoderLib/EncModeCtrl.cpp +++ b/source/Lib/EncoderLib/EncModeCtrl.cpp @@ -1964,7 +1964,11 @@ bool EncModeCtrlMTnoRQT::useModeResult( const EncTestMode& encTestmode, CodingSt } // for now just a simple decision based on RD-cost or choose tempCS if bestCS is not yet coded +#if JVET_M0428_ENC_DB_OPT + if( !cuECtx.bestCS || ( ( tempCS->features[ENC_FT_RD_COST] + ( tempCS->useDbCost ? tempCS->costDbOffset : 0 ) ) < ( cuECtx.bestCS->features[ENC_FT_RD_COST] + ( tempCS->useDbCost ? cuECtx.bestCS->costDbOffset : 0 ) ) ) ) +#else if( !cuECtx.bestCS || tempCS->features[ENC_FT_RD_COST] < cuECtx.bestCS->features[ENC_FT_RD_COST] ) +#endif { cuECtx.bestCS = tempCS; cuECtx.bestCU = tempCS->cus[0]; diff --git a/source/Lib/EncoderLib/IntraSearch.cpp b/source/Lib/EncoderLib/IntraSearch.cpp index d4cab58f9f0efe39e92d5b59fcee262e28b6c893..690cc1cc4879a5df41e0be903c8193e98a24843f 100644 --- a/source/Lib/EncoderLib/IntraSearch.cpp +++ b/source/Lib/EncoderLib/IntraSearch.cpp @@ -900,6 +900,9 @@ void IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner ) if( cu.ispMode && !csTemp->cus[0]->firstTU->cbf[COMPONENT_Y] ) { csTemp->cost = MAX_DOUBLE; +#if JVET_M0428_ENC_DB_OPT + csTemp->costDbOffset = 0; +#endif } #else xRecurIntraCodingLumaQT( *csTemp, partitioner );