diff --git a/CMakeLists.txt b/CMakeLists.txt index 78fe2a4a..19b5a7b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,7 @@ endif() # project name project( vvdec VERSION 3.2.0 ) # set alternative version numbering for release candidates -set( PROJECT_VERSION_RC rc1 ) +set( PROJECT_VERSION_RC rc2 ) if( PROJECT_VERSION_RC ) set( PROJECT_VERSION "${PROJECT_VERSION}-${PROJECT_VERSION_RC}" ) endif() @@ -264,6 +264,8 @@ set( VVDEC_ENABLE_WERROR ON CACHE BOOL "Treat warnings as set( VVDEC_USE_ADDRESS_SANITIZER OFF CACHE BOOL "Enable or disable address sanitizer" ) +set( VVDEC_FUZZING_BUILD OFF CACHE BOOL "Build fuzz-testing binaries using clang -fsanitize=fuzzer" ) + if( CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang" ) @@ -387,6 +389,19 @@ if( VVDEC_USE_THREAD_SANITIZER ) add_link_options( "-fsanitize=thread" ) endif() +if( VVDEC_FUZZING_BUILD ) + add_compile_definitions( FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1 ) + + if( CMAKE_CXX_COMPILER_ID STREQUAL "Clang" ) + message( STATUS "enabled GNU/Clang fuzzing sanitizer" ) + + add_compile_options( -fsanitize=fuzzer -fno-omit-frame-pointer ) + add_link_options( "-fsanitize=fuzzer-no-link" ) + else() + message( WARNING "You probably want to build with clang, when enabling VVDEC_FUZZING_BUILD" ) + endif() +endif() + if( VVDEC_ENABLE_WERROR ) add_compile_options( "$<$,$,$>:-Werror>" ) add_compile_options( "$<$:/WX>" ) @@ -486,6 +501,9 @@ enable_testing() if( NOT BUILD_SHARED_LIBS ) add_subdirectory( "tests/vvdec_unit_test" ) endif() +if( VVDEC_FUZZING_BUILD ) + add_subdirectory( "tests/fuzzing" ) +endif() # create a list of all test bitstreams in variable BITSTREAM_FILES # also defines BITSTREAM_URL_BASE diff --git a/source/Lib/CommonLib/CodingStructure.cpp b/source/Lib/CommonLib/CodingStructure.cpp index 7df1f1db..d5dc0ae9 100644 --- a/source/Lib/CommonLib/CodingStructure.cpp +++ b/source/Lib/CommonLib/CodingStructure.cpp @@ -542,9 +542,10 @@ void CodingStructure::initVIbcBuf( int numCtuLines, ChromaFormat chromaFormatIDC m_virtualIBCbuf.resize( numCtuLines ); for( auto& buf: m_virtualIBCbuf ) { - if( buf.bufs.empty() ) + if( buf.bufs.empty() || m_IBCBufferWidth != g_IBCBufferSize / ctuSize ) { m_IBCBufferWidth = g_IBCBufferSize / ctuSize; + buf.destroy(); buf.create( UnitArea( chromaFormatIDC, Area( 0, 0, m_IBCBufferWidth, ctuSize ) ) ); } } diff --git a/source/Lib/CommonLib/ContextModelling.cpp b/source/Lib/CommonLib/ContextModelling.cpp index 9f85dcf9..9ddadd52 100644 --- a/source/Lib/CommonLib/ContextModelling.cpp +++ b/source/Lib/CommonLib/ContextModelling.cpp @@ -466,6 +466,10 @@ void MergeCtx::setMmvdMergeCandiInfo( CodingUnit& cu, int candIdx ) cu.mv [REF_PIC_LIST_1][0] = mmvdBaseMv[fPosBaseIdx][1].mv + tempMv[1]; cu.refIdx[REF_PIC_LIST_1] = refList1; } + else + { + CHECK( refList0 < 0 && refList1 < 0, "MMVD base merge candidate for the signalled base index is not available" ); + } //cu.setMmvdFlag ( true ); cu.mmvdIdx = candIdx; diff --git a/source/Lib/CommonLib/ContextModelling.h b/source/Lib/CommonLib/ContextModelling.h index 1fd8dbf9..9251e407 100644 --- a/source/Lib/CommonLib/ContextModelling.h +++ b/source/Lib/CommonLib/ContextModelling.h @@ -461,40 +461,33 @@ class CUCtx class MergeCtx { -public: - MergeCtx() : numValidMergeCand( 0 ) { memset( mrgTypeNeighbours, 0, sizeof( mrgTypeNeighbours ) ); } - ~MergeCtx() {} public: MvField mvFieldNeighbours [ MRG_MAX_NUM_CANDS << 1 ]; // double length for mv of both lists - uint8_t BcwIdx [ MRG_MAX_NUM_CANDS ]; - unsigned char interDirNeighbours[ MRG_MAX_NUM_CANDS ]; - MergeType mrgTypeNeighbours [ MRG_MAX_NUM_CANDS ]; - int numValidMergeCand; + uint8_t BcwIdx [ MRG_MAX_NUM_CANDS ] = { 0 }; + unsigned char interDirNeighbours[ MRG_MAX_NUM_CANDS ] = { 0 }; + MergeType mrgTypeNeighbours [ MRG_MAX_NUM_CANDS ] = { MergeType(0) }; + int numValidMergeCand = 0; - MvField mmvdBaseMv [MMVD_BASE_MV_NUM ][2]; + MvField mmvdBaseMv [ MMVD_BASE_MV_NUM ][2]; + bool mmvdUseAltHpelIf [ MMVD_BASE_MV_NUM ] = { false }; + bool useAltHpelIf [ MRG_MAX_NUM_CANDS ] = { false }; void setMmvdMergeCandiInfo( CodingUnit& cu, int candIdx ); - bool mmvdUseAltHpelIf [ MMVD_BASE_MV_NUM ]; - bool useAltHpelIf [ MRG_MAX_NUM_CANDS ]; void setMergeInfo ( CodingUnit& cu, int candIdx ); - void init() { numValidMergeCand = 0; memset( mrgTypeNeighbours, 0, sizeof( mrgTypeNeighbours ) ); } }; class AffineMergeCtx { -public: - AffineMergeCtx() : numValidMergeCand( 0 ) { for ( unsigned i = 0; i < AFFINE_MRG_MAX_NUM_CANDS; i++ ) affineType[i] = AFFINEMODEL_4PARAM; } - ~AffineMergeCtx() {} public: MvField mvFieldNeighbours [AFFINE_MRG_MAX_NUM_CANDS << 1][3]; // double length for mv of both lists - unsigned char interDirNeighbours[AFFINE_MRG_MAX_NUM_CANDS ]; - AffineModel affineType [AFFINE_MRG_MAX_NUM_CANDS ]; - uint8_t BcwIdx [AFFINE_MRG_MAX_NUM_CANDS ]; - int numValidMergeCand; - int maxNumMergeCand; + unsigned char interDirNeighbours[AFFINE_MRG_MAX_NUM_CANDS ] = { 0 }; + AffineModel affineType [AFFINE_MRG_MAX_NUM_CANDS ] = { AffineModel( 0 ) }; // AFFINEMODEL_4PARAM + uint8_t BcwIdx [AFFINE_MRG_MAX_NUM_CANDS ] = { 0 }; + int numValidMergeCand = 0; + int maxNumMergeCand = 0; MotionBuf subPuMvpMiBuf; - MergeType mergeType [AFFINE_MRG_MAX_NUM_CANDS ]; + MergeType mergeType [AFFINE_MRG_MAX_NUM_CANDS ] = { MergeType(0) }; }; diff --git a/source/Lib/CommonLib/InterPrediction.cpp b/source/Lib/CommonLib/InterPrediction.cpp index d62f6258..29321548 100644 --- a/source/Lib/CommonLib/InterPrediction.cpp +++ b/source/Lib/CommonLib/InterPrediction.cpp @@ -268,7 +268,7 @@ void gradFilterCore(Pel* pSrc, ptrdiff_t srcStride, int width, int height, ptrdi void PaddBIOCore(const Pel* refPel,Pel* dstPel,unsigned width,const int shift) { -#define LFTSHFT(y,shift) y<> shiftSampleHor); refy = cu.Cb().y + (bv.ver >> shiftSampleVer); } - refx &= ((m_IBCBufferWidth >> shiftSampleHor) - 1); + const int ibcBufferWidth = cu.cs->m_IBCBufferWidth >> shiftSampleHor; + refx &= ( ibcBufferWidth - 1 ); refy &= ((1 << ctuSizeVerLog2) - 1); const int lineIdx = cu.lumaPos().y / cu.slice->getSPS()->getMaxCUHeight(); CHECK( refy + predBuf.bufs[compID].height > ( 1 << ctuSizeVerLog2 ), "IBC access out of bounds." ); - if (refx + (int)predBuf.bufs[compID].width <= (m_IBCBufferWidth >> shiftSampleHor)) + if (refx + (int)predBuf.bufs[compID].width <= ibcBufferWidth) { const CompArea srcArea = CompArea( compID, Position( refx, refy ), Size( predBuf.bufs[compID] ) ); - const CPelBuf refBuf = cu.cs->m_virtualIBCbuf[lineIdx].getBuf( srcArea ); //m_IBCBuffer.getBuf(srcArea); + const CPelBuf refBuf = cu.cs->m_virtualIBCbuf[lineIdx].getBuf( srcArea ); predBuf.bufs[compID].copyFrom( refBuf ); } else { // wrap around - CHECK( (int) predBuf.bufs[compID].width > ( m_IBCBufferWidth >> shiftSampleHor ), "IBC access out of bounds." ); + CHECK( (int) predBuf.bufs[compID].width > ibcBufferWidth, "IBC access out of bounds." ); - const int width = ( m_IBCBufferWidth >> shiftSampleHor ) - refx; + const int width = ibcBufferWidth - refx; CompArea srcArea = CompArea( compID, Position( refx, refy ), Size( width, predBuf.bufs[compID].height ) ); - CPelBuf srcBuf = cu.cs->m_virtualIBCbuf[lineIdx].getBuf( srcArea ); // m_IBCBuffer.getBuf(srcArea); + CPelBuf srcBuf = cu.cs->m_virtualIBCbuf[lineIdx].getBuf( srcArea ); PelBuf dstBuf = predBuf.bufs[compID].subBuf( 0, 0, width, predBuf.bufs[compID].height ); dstBuf.copyFrom( srcBuf ); const int remWidth = predBuf.bufs[compID].width - width; srcArea = CompArea( compID, Position( 0, refy ), Size( remWidth, predBuf.bufs[compID].height ) ); - srcBuf = cu.cs->m_virtualIBCbuf[lineIdx].getBuf( srcArea ); // m_IBCBuffer.getBuf(srcArea); + srcBuf = cu.cs->m_virtualIBCbuf[lineIdx].getBuf( srcArea ); dstBuf = predBuf.bufs[compID].subBuf( width, 0, remWidth, predBuf.bufs[compID].height ); dstBuf.copyFrom( srcBuf ); } diff --git a/source/Lib/CommonLib/InterPrediction.h b/source/Lib/CommonLib/InterPrediction.h index 737fa137..b7b3cea0 100644 --- a/source/Lib/CommonLib/InterPrediction.h +++ b/source/Lib/CommonLib/InterPrediction.h @@ -116,8 +116,6 @@ class InterPrediction : public WeightPrediction bool m_subPuMC = false; UnitArea m_currCuArea; - int m_IBCBufferWidth; - PelStorage m_IBCBuffer; void xIntraBlockCopy (CodingUnit &cu, PelUnitBuf &predBuf, const ComponentID compID); diff --git a/source/Lib/CommonLib/InterpolationFilter.cpp b/source/Lib/CommonLib/InterpolationFilter.cpp index fbe97350..6883e947 100644 --- a/source/Lib/CommonLib/InterpolationFilter.cpp +++ b/source/Lib/CommonLib/InterpolationFilter.cpp @@ -467,7 +467,7 @@ void InterpolationFilter::filterCopy( const ClpRng& clpRng, const Pel* src, cons { for( col = 0; col < width; col++ ) { - dst[col] = src[col] << shift10BitOut; + dst[col] = src[col] * ( 1 << shift10BitOut ); } INCY( src, srcStride ); INCY( dst, dstStride ); @@ -482,7 +482,7 @@ void InterpolationFilter::filterCopy( const ClpRng& clpRng, const Pel* src, cons { for( col = 0; col < width; col++ ) { - Pel val = src[col] << shift; + Pel val = src[col] * ( 1 << shift ); dst[col] = val - ( Pel ) IF_INTERNAL_OFFS; } diff --git a/source/Lib/CommonLib/Picture.cpp b/source/Lib/CommonLib/Picture.cpp index 8e03e515..a0e60217 100644 --- a/source/Lib/CommonLib/Picture.cpp +++ b/source/Lib/CommonLib/Picture.cpp @@ -54,7 +54,7 @@ POSSIBILITY OF SUCH DAMAGE. namespace vvdec { -void paddPicBorderTopCore(Pel *pi, ptrdiff_t stride,int width,int xmargin,int ymargin) +static void paddPicBorderTopCore(Pel *pi, ptrdiff_t stride,int width,int xmargin,int ymargin) { for( int x = 0; x < xmargin; x++ ) { @@ -69,7 +69,7 @@ void paddPicBorderTopCore(Pel *pi, ptrdiff_t stride,int width,int xmargin,int ym } } -void paddPicBorderBotCore(Pel *pi, ptrdiff_t stride,int width,int xmargin,int ymargin) +static void paddPicBorderBotCore(Pel *pi, ptrdiff_t stride,int width,int xmargin,int ymargin) { for( int x = 0; x < xmargin; x++ ) { @@ -84,7 +84,7 @@ void paddPicBorderBotCore(Pel *pi, ptrdiff_t stride,int width,int xmargin,int ym } } -void paddPicBorderLeftRightCore(Pel *pi, ptrdiff_t stride,int width,int xmargin,int height) +static void paddPicBorderLeftRightCore(Pel *pi, ptrdiff_t stride,int width,int xmargin,int height) { for( int y = 1; y < ( height - 1 ); y++ ) { @@ -263,8 +263,9 @@ void Picture::finalInit( CUChunkCache* cuChunkCache, TUChunkCache* tuChunkCache, parseDone . lock(); cs->picture = this; cs->picHeader = ph; - cs->pps = pps ? pps->getSharedPtr() : nullptr; - cs->sps = sps ? sps->getSharedPtr() : nullptr; + cs->pps = pps->getSharedPtr(); + cs->sps = sps->getSharedPtr(); + cs->pcv = pps->pcv.get(); if( phPSupdate ) { @@ -284,10 +285,14 @@ void Picture::finalInit( CUChunkCache* cuChunkCache, TUChunkCache* tuChunkCache, cs->lmcsAps = lmcsAps ? lmcsAps->getSharedPtr() : nullptr; } - cs->pcv = pps->pcv.get(); cs->allocTempInternals(); cs->rebindPicBufs(); + if( sps->getIBCFlag() ) + { + cs->initVIbcBuf( cs->pcv->heightInCtus, sps->getChromaFormatIdc(), sps->getMaxCUHeight() ); + } + resetProcessingTime(); } @@ -373,11 +378,14 @@ void Picture::ensureUsableAsRef() CHECK_FATAL( reconDone.hasException(), "to be usable as reference the picture should not have an Exception reconDone barrier" ); } -void Picture::fillGrey( const SPS* sps ) +void Picture::fillGrey( const SPS* fallbackSPS ) { + CHECK( !cs && !cs->sps && !fallbackSPS, "No SPS accessible" ); + + const int bitDepth = cs && cs->sps ? cs->sps->getBitDepth() : fallbackSPS->getBitDepth(); // fill in grey buffer for missing reference pictures (GDR or broken bitstream) - const uint32_t yFill = 1 << ( sps->getBitDepth() - 1 ); - const uint32_t cFill = 1 << ( sps->getBitDepth() - 1 ); + const uint32_t yFill = 1 << ( bitDepth - 1 ); + const uint32_t cFill = 1 << ( bitDepth - 1 ); getRecoBuf().Y().fill( yFill ); getRecoBuf().Cb().fill( cFill ); getRecoBuf().Cr().fill( cFill ); diff --git a/source/Lib/CommonLib/Picture.h b/source/Lib/CommonLib/Picture.h index 924f52b1..919c8646 100644 --- a/source/Lib/CommonLib/Picture.h +++ b/source/Lib/CommonLib/Picture.h @@ -274,7 +274,7 @@ struct Picture : public UnitArea void waitForAllTasks(); void ensureUsableAsRef(); - void fillGrey( const SPS* sps ); + void fillGrey( const SPS* fallbackSPS = nullptr ); #if TRACE_ENABLE_ITT __itt_domain* m_itt_decLibInst; diff --git a/source/Lib/CommonLib/Quant.cpp b/source/Lib/CommonLib/Quant.cpp index 1397049b..3682e6a9 100644 --- a/source/Lib/CommonLib/Quant.cpp +++ b/source/Lib/CommonLib/Quant.cpp @@ -343,7 +343,8 @@ void Quant::dequant( const TransformUnit& tu, CoeffBuf& dstCoeff, const Componen // from the dequantisation equation: // iCoeffQ = Intermediate_Int((int64_t(clipQCoef) * scale + iAdd) >> rightShift); //(sizeof(Intermediate_Int) * 8) = inputBitDepth + scaleBits - rightShift - const uint32_t targetInputBitDepth = std::min( maxLog2TrDynamicRange + 1, sizeof( Intermediate_Int ) * 8 + rightShift - scaleBits ); + const uint32_t targetInputBitDepth = std::min( maxLog2TrDynamicRange + 1, (int) sizeof( Intermediate_Int ) * 8 + rightShift - scaleBits ); + CHECK( targetInputBitDepth < 8, "Invalid bit depth" ); const Intermediate_Int inputMaximum = ( 1 << ( targetInputBitDepth - 1 ) ) - 1; if( !enableScalingLists ) diff --git a/source/Lib/CommonLib/Reshape.cpp b/source/Lib/CommonLib/Reshape.cpp index 129bac39..4a02e695 100644 --- a/source/Lib/CommonLib/Reshape.cpp +++ b/source/Lib/CommonLib/Reshape.cpp @@ -327,6 +327,18 @@ void Reshape::constructReshaper() for (int i = m_sliceReshapeInfo.reshaperModelMinBinIdx; i <= m_sliceReshapeInfo.reshaperModelMaxBinIdx; i++) m_binCW[i] = (uint16_t)(m_sliceReshapeInfo.reshaperModelBinCWDelta[i] + (int)m_initCW); + unsigned int sumCW = 0; + for (int i = m_sliceReshapeInfo.reshaperModelMinBinIdx; i <= m_sliceReshapeInfo.reshaperModelMaxBinIdx; i++) + { + CHECK_READ_RANGE( m_binCW[i], m_initCW >> 3, ( m_initCW << 3 ) - 1, // + "The value of lmcsCW[ i ] shall be in the range of OrgCW >> 3 to ( OrgCW << 3 ) - 1, inclusive." ); + CHECK_READ_RANGE( m_binCW[i] + m_sliceReshapeInfo.chrResScalingOffset, ( m_initCW >> 3 ), ( m_initCW << 3 ) - 1, + "lmcsCW[ i ] + lmcsDeltaCrs shall be in the range of OrgCW >> 3 to ( OrgCW << 3 ) - 1, inclusive." ); + sumCW += m_binCW[i]; + } + CHECK( sumCW > ( 1 << m_lumaBD ) - 1, + "It is a requirement of bitstream conformance that the following condition is true: sum( lmcsCW[0..15] ) <= ( 1 << BitDepth ) − 1" ); + for (int i = 0; i < pwlFwdLUTsize; i++) { m_reshapePivot[i + 1] = m_reshapePivot[i] + m_binCW[i]; @@ -344,6 +356,16 @@ void Reshape::constructReshaper() m_chromaAdjHelpLUT[i] = (int32_t)(m_initCW * (1 << FP_PREC) / ( m_binCW[i] + m_sliceReshapeInfo.chrResScalingOffset ) ); } } + for (int i = m_sliceReshapeInfo.reshaperModelMinBinIdx; i <= m_sliceReshapeInfo.reshaperModelMaxBinIdx; i++) + { + if( m_reshapePivot[i] % ( 1 << ( m_lumaBD - 5 ) ) != 0 ) + { + CHECK( ( m_reshapePivot[i] >> ( m_lumaBD - 5 ) ) == ( m_reshapePivot[i + 1] >> ( m_lumaBD - 5 ) ), + "When the value of LmcsPivot[i] is not a multiple of 1 << ( BitDepth - 5 ), " + "the value of( LmcsPivot[i] >> ( BitDepth - 5 ) ) shall not be equal to the value of( LmcsPivot[i + 1] >> ( BitDepth - 5 ) )." ); + } + } + for (int lumaSample = 0; lumaSample < m_reshapeLUTSize; lumaSample++) { int idxYInv = getPWLIdxInv(lumaSample); diff --git a/source/Lib/CommonLib/Slice.cpp b/source/Lib/CommonLib/Slice.cpp index 33a9f34c..acf258c7 100644 --- a/source/Lib/CommonLib/Slice.cpp +++ b/source/Lib/CommonLib/Slice.cpp @@ -482,6 +482,10 @@ void Slice::constructSingleRefPicList( const PicList& rcPicList, RefPicList list if( !rRPL.isRefPicLongterm( ii ) ) { refPOC = getPOC() + rRPL.getRefPicIdentifier( ii ); + // A same-layer STRP entry must not resolve to the current POC: that is either the current + // picture or a stale POC-colliding picture, neither valid for inter prediction (only the + // separate inter-layer path may reference the current POC, in a different layer). + CHECK( refPOC == getPOC(), "An STRP entry must not refer to a picture with the current POC" ); pcRefPic = xGetRefPic( rcPicList, refPOC, m_pcPic->layerId ); CHECK( !pcRefPic, "Picture pointer missing from ref pic list" ); diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h index fb5baf7f..429b7fe4 100644 --- a/source/Lib/CommonLib/TypeDef.h +++ b/source/Lib/CommonLib/TypeDef.h @@ -830,7 +830,7 @@ class UnsupportedFeatureException : public Exception #define CHECK( cond, msg ) { if UNLIKELY( cond ) { THROW_RECOVERABLE( msg << "\nERROR CONDITION: " << #cond ); } } #define CHECK_UNSUPPORTED( cond, msg ) { if UNLIKELY( cond ) { THROW_UNSUPPORTED( msg << "\nERROR CONDITION: " << #cond ); } } -#if defined( FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION ) // CHECKD should not abort, when running fuzzing builds, to allow fuzzing to continue +#if defined( _DEBUG ) && defined( FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION ) // CHECKD should not abort, when running fuzzing builds, to allow fuzzing to continue # define CHECKD( cond, msg ) CHECK_WARN( cond, msg ) #elif defined( _DEBUG ) # define CHECKD( cond, msg ) { if UNLIKELY( cond ) { ABORT ( msg << "\nERROR CONDITION: " << #cond ); } } diff --git a/source/Lib/CommonLib/UnitTools.cpp b/source/Lib/CommonLib/UnitTools.cpp index 38cebcb8..bbb7cb4c 100644 --- a/source/Lib/CommonLib/UnitTools.cpp +++ b/source/Lib/CommonLib/UnitTools.cpp @@ -697,6 +697,7 @@ bool PU::addMergeHMVPCand(const CodingStructure &cs, MergeCtx& mrgCtx, MotionHis { const HPMVInfo &miNeighbor = lut[(num_avai_candInLUT - mrgIdx)]; mrgCtx.interDirNeighbours[cnt] = miNeighbor.interDir(); + CHECK(mrgCtx.interDirNeighbours[cnt] == 0, "miNeighbor.interDir() shouldn't be zero"); mrgCtx.mvFieldNeighbours[cnt << 1].setMvField(miNeighbor.mv[0], miNeighbor.mhRefIdx[0]); mrgCtx.useAltHpelIf[cnt] = !ibcFlag && miNeighbor.useAltHpelIf; if (isInterB) diff --git a/source/Lib/CommonLib/arm/neon/AdaptiveLoopFilter_neon.cpp b/source/Lib/CommonLib/arm/neon/AdaptiveLoopFilter_neon.cpp index 6c518c88..ae9b4857 100644 --- a/source/Lib/CommonLib/arm/neon/AdaptiveLoopFilter_neon.cpp +++ b/source/Lib/CommonLib/arm/neon/AdaptiveLoopFilter_neon.cpp @@ -1013,6 +1013,49 @@ void DeriveClassificationBlk_neon( AlfClassifier* classifier, const CPelBuf& src } while( i < blk.height ); } +static inline void processCcAlfRow4_neon( const Pel* srcCross, Pel* srcSelf, const ptrdiff_t offset1, + const ptrdiff_t offset2, const ptrdiff_t offset3, const int16x8_t coeff, + const uint16x4_t offset, const uint16x4_t maxVal ) +{ + static constexpr int scaleBits = 7; + + const int16x8_t src0 = vld1q_s16( srcCross + offset2 ); + const int16x8_t src1 = vld1q_s16( srcCross - 1 ); + const int16x8_t src2 = vld1q_s16( srcCross ); + const int16x8_t src3 = vld1q_s16( srcCross + offset1 - 1 ); + const int16x8_t src45 = vld1q_s16( srcCross + offset1 ); + const int16x8_t src6 = vld1q_s16( srcCross + offset3 ); + + const int16x8_t srcCurr = vuzpq_s16( src2, src2 ).val[0]; + const int16x8_t srcDiff01 = vuzpq_s16( src0, src1 ).val[0]; + const int16x8_t srcDiff25 = vuzpq_s16( src2, src45 ).val[1]; + const int16x8_t srcDiff34 = vuzpq_s16( src3, src45 ).val[0]; + const int16x8_t srcDiff6 = vuzpq_s16( src6, src6 ).val[0]; + + const int16x8_t diff01 = vsubq_s16( srcDiff01, srcCurr ); + const int16x8_t diff25 = vsubq_s16( srcDiff25, srcCurr ); + const int16x8_t diff34 = vsubq_s16( srcDiff34, srcCurr ); + const int16x8_t diff6 = vsubq_s16( srcDiff6, srcCurr ); + + int32x4_t sum = vdupq_n_s32( 0 ); + sum = vmlal_lane_s16( sum, vget_low_s16 ( diff01 ), vget_low_s16 ( coeff ), 0 ); + sum = vmlal_lane_s16( sum, vget_high_s16( diff01 ), vget_low_s16 ( coeff ), 1 ); + sum = vmlal_lane_s16( sum, vget_low_s16 ( diff25 ), vget_low_s16 ( coeff ), 2 ); + sum = vmlal_lane_s16( sum, vget_low_s16 ( diff34 ), vget_low_s16 ( coeff ), 3 ); + sum = vmlal_lane_s16( sum, vget_high_s16( diff34 ), vget_high_s16( coeff ), 0 ); + sum = vmlal_lane_s16( sum, vget_high_s16( diff25 ), vget_high_s16( coeff ), 1 ); + sum = vmlal_lane_s16( sum, vget_low_s16 ( diff6 ), vget_high_s16( coeff ), 2 ); + + int16x4_t delta = vrshrn_n_s32( sum, scaleBits ); + delta = vreinterpret_s16_u16( vsub_u16( vmin_u16( vvdec_vsqadd_u16( offset, delta ), maxVal ), offset ) ); + + const uint16x4_t srcSelfU = vreinterpret_u16_s16( vld1_s16( srcSelf ) ); + + const uint16x4_t out = vmin_u16( vvdec_vsqadd_u16( srcSelfU, delta ), maxVal ); + + vst1_s16( srcSelf, vreinterpret_s16_u16( out ) ); +} + static inline void processCcAlfBothRow4_neon( const Pel* srcCross, Pel* srcSelfCb, Pel* srcSelfCr, const ptrdiff_t offset1, const ptrdiff_t offset2, const ptrdiff_t offset3, const int16x8_t coeffCb, const int16x8_t coeffCr, const uint16x8_t offset, @@ -1074,6 +1117,125 @@ static inline void processCcAlfBothRow4_neon( const Pel* srcCross, Pel* srcSelfC vst1_s16( srcSelfCr, vreinterpret_s16_u16( vget_high_u16( outCbCr ) ) ); } +void FilterBlkCcAlf_neon( const PelBuf& dstBuf, const CPelUnitBuf& recSrc, const Area& blkDst, const Area& blkSrc, + const ComponentID compId, const int16_t filterCoeff[8], const ClpRngs& clpRngs, + int vbCTUHeight, int vbPos ) +{ + CHECKD( 1 << getLog2( vbCTUHeight ) != vbCTUHeight, "Not a power of 2" ); + CHECKD( vbPos != vbCTUHeight - ALF_VB_POS_ABOVE_CTUROW_LUMA, "Unexpected VB position" ); + CHECKD( !isChroma( compId ), "Must be chroma" ); + + const ChromaFormat nChromaFormat = recSrc.chromaFormat; + CHECKD( nChromaFormat == CHROMA_400, "Wrong chroma format" ); + + const int startHeight = blkDst.y; + const int endHeight = blkDst.y + blkDst.height; + const int startWidth = blkDst.x; + const int endWidth = blkDst.x + blkDst.width; + const int scaleX = getComponentScaleX( compId, nChromaFormat ); // 420: 1; 422: 1; 444: 0 + const int scaleY = getComponentScaleY( compId, nChromaFormat ); // 420: 1; 422: 0; 444: 0 + + CHECKD( startHeight % 4, "Wrong startHeight in filtering" ); + CHECKD( startWidth % 4, "Wrong startWidth in filtering" ); + CHECKD( ( endHeight - startHeight ) % 4, "Wrong endHeight in filtering" ); + CHECKD( ( endWidth - startWidth ) % 4, "Wrong endWidth in filtering" ); + + if( scaleX != 1 ) + { + AdaptiveLoopFilter::filterBlkCcAlf( dstBuf, recSrc, blkDst, blkSrc, compId, filterCoeff, clpRngs, vbCTUHeight, + vbPos ); + return; + } + + const CPelBuf& srcBuf = recSrc.get( COMPONENT_Y ); + const ptrdiff_t lumaStride = srcBuf.stride; + const Pel* lumaPtr = srcBuf.buf + blkSrc.y * lumaStride + blkSrc.x; + + const ptrdiff_t chromaStride = dstBuf.stride; + Pel* chromaPtr = dstBuf.buf + blkDst.y * chromaStride + blkDst.x; + + const uint16x4_t offset = vdup_n_u16( 1 << ( clpRngs.bd - 1 ) ); + const uint16x4_t maxVal = vdup_n_u16( clpRngs.max() ); + + const int16x8_t coeff = vld1q_s16( filterCoeff ); + + const int width = endWidth - startWidth; + const int height = endHeight - startHeight; + const ptrdiff_t lumaStepY = ( lumaStride * 4 ) << scaleY; + + CHECKD( height <= 0, "Wrong height in filtering" ); + CHECKD( width <= 0, "Wrong width in filtering" ); + + int i = 0; + do + { + const int pos = ( ( startHeight + i ) << scaleY ) & ( vbCTUHeight - 1 ); + const int firstDistance = vbPos - pos; + const int lastDistance = firstDistance - ( 3 << scaleY ); + + if( lastDistance > 2 ) + { + const ptrdiff_t offset1 = lumaStride; + const ptrdiff_t offset2 = -lumaStride; + const ptrdiff_t offset3 = 2 * lumaStride; + + for( int ii = 0; ii < 4; ii++ ) + { + const Pel* srcCrossRow = lumaPtr + ( ii << scaleY ) * lumaStride; + Pel* srcSelfRow = chromaPtr + ii * chromaStride; + + int j = 0; + do + { + processCcAlfRow4_neon( srcCrossRow + ( j << 1 ), srcSelfRow + j, offset1, offset2, offset3, coeff, offset, + maxVal ); + j += 4; + } while( j < width ); + } + } + else + { + for( int ii = 0; ii < 4; ii++ ) + { + ptrdiff_t offset1 = lumaStride; + ptrdiff_t offset2 = -lumaStride; + ptrdiff_t offset3 = 2 * lumaStride; + + const int distance = firstDistance - ( ii << scaleY ); + if( scaleY == 0 && ( distance == 0 || distance == -1 ) ) + { + continue; + } + if( distance == 2 || distance == -1 ) + { + offset3 = offset1; + } + else if( distance == 1 || distance == 0 ) + { + offset1 = 0; + offset2 = 0; + offset3 = 0; + } + + const Pel* srcCrossRow = lumaPtr + ( ii << scaleY ) * lumaStride; + Pel* srcSelfRow = chromaPtr + ii * chromaStride; + + int j = 0; + do + { + processCcAlfRow4_neon( srcCrossRow + ( j << 1 ), srcSelfRow + j, offset1, offset2, offset3, coeff, offset, + maxVal ); + j += 4; + } while( j < width ); + } + } + + chromaPtr += chromaStride * 4; + lumaPtr += lumaStepY; + i += 4; + } while( i < height ); +} + void FilterBlkCcAlfBoth_neon( const PelBuf& dstBufCb, const PelBuf& dstBufCr, const CPelUnitBuf& recSrc, const Area& blkDst, const Area& blkSrc, const int16_t filterCoeffCb[8], const int16_t filterCoeffCr[8], const ClpRngs& clpRngs, int vbCTUHeight, int vbPos ) @@ -1200,6 +1362,7 @@ void AdaptiveLoopFilter::_initAdaptiveLoopFilterARM() m_deriveClassificationBlk = DeriveClassificationBlk_neon; m_filter7x7Blk = Filter7x7Blk_neon; m_filter5x5Blk = Filter5x5Blk_neon; + m_filterCcAlf = FilterBlkCcAlf_neon; m_filterCcAlfBoth = FilterBlkCcAlfBoth_neon; } diff --git a/source/Lib/CommonLib/arm/neon/sum_neon.h b/source/Lib/CommonLib/arm/neon/sum_neon.h index 43b0178d..f1e7aa7f 100644 --- a/source/Lib/CommonLib/arm/neon/sum_neon.h +++ b/source/Lib/CommonLib/arm/neon/sum_neon.h @@ -60,6 +60,16 @@ static inline uint16x8_t vvdec_vabaq_s16( uint16x8_t acc, int16x8_t x, int16x8_t return vreinterpretq_u16_s16( vabaq_s16( vreinterpretq_s16_u16( acc ), x, y ) ); } +static inline uint16x4_t vvdec_vsqadd_u16( uint16x4_t a, int16x4_t b ) +{ +#if REAL_TARGET_AARCH64 + return vsqadd_u16( a, b ); +#else + const int16x4_t sum = vqadd_s16( vreinterpret_s16_u16( a ), b ); + return vreinterpret_u16_s16( vmax_s16( vdup_n_s16( 0 ), sum ) ); +#endif +} + static inline uint16x8_t vvdec_vsqaddq_u16( uint16x8_t a, int16x8_t b ) { #if REAL_TARGET_AARCH64 diff --git a/source/Lib/CommonLib/x86/AdaptiveLoopFilterX86.h b/source/Lib/CommonLib/x86/AdaptiveLoopFilterX86.h index 5ced5340..35a24b89 100644 --- a/source/Lib/CommonLib/x86/AdaptiveLoopFilterX86.h +++ b/source/Lib/CommonLib/x86/AdaptiveLoopFilterX86.h @@ -57,8 +57,8 @@ namespace vvdec template void simdDeriveClassificationBlk(AlfClassifier *classifier, const CPelBuf &srcLuma, const Area& blk, const int shift, int vbCTUHeight, int vbPos) { - const size_t imgStride = srcLuma.stride; - const Pel * srcExt = srcLuma.buf; + const ptrdiff_t imgStride = srcLuma.stride; + const Pel* srcExt = srcLuma.buf; const int imgHExtended = blk.height + 4; const int imgWExtended = blk.width + 4; @@ -72,7 +72,7 @@ void simdDeriveClassificationBlk(AlfClassifier *classifier, const CPelBuf &srcLu for (int i = 0; i < imgHExtended; i += 2) { - const size_t offset = (i + posY - 3) * imgStride + posX - 3; + const ptrdiff_t offset = ( i + posY - 3 ) * imgStride + posX - 3; const Pel *imgY0 = &srcExt[offset]; const Pel *imgY1 = &srcExt[offset + imgStride]; @@ -302,7 +302,7 @@ void simdDeriveClassificationBlk(AlfClassifier *classifier, const CPelBuf &srcLu template<> void simdDeriveClassificationBlk(AlfClassifier *classifier, const CPelBuf &srcLuma, const Area& blk, const int shift, int vbCTUHeight, int vbPos) { - const size_t imgStride = srcLuma.stride; + const ptrdiff_t imgStride = srcLuma.stride; const Pel * srcExt = srcLuma.buf; const int imgHExtended = blk.height + 4; @@ -336,7 +336,7 @@ void simdDeriveClassificationBlk(AlfClassifier *classifier, const CPelBuf for (int i = 0; i < imgHExtended; i += 2) { - const size_t offset = (i + posY - 3) * imgStride + posX - 3; + const ptrdiff_t offset = (i + posY - 3) * imgStride + posX - 3; _mm_prefetch( ( const char* ) &srcExt[offset + 4 * imgStride], _MM_HINT_T0 ); _mm_prefetch( ( const char* ) &srcExt[offset + 5 * imgStride], _MM_HINT_T0 ); @@ -594,8 +594,8 @@ static void simdFilter5x5Blk( const AlfClassifier*, const CPelBuf srcBuffer = recSrc.get(compId); PelBuf dstBuffer = recDst.get(compId); - const size_t srcStride = srcBuffer.stride; - const size_t dstStride = dstBuffer.stride; + const ptrdiff_t srcStride = srcBuffer.stride; + const ptrdiff_t dstStride = dstBuffer.stride; constexpr int SHIFT = AdaptiveLoopFilter::m_NUM_BITS - 1; constexpr int ROUND = 1 << (SHIFT - 1); @@ -785,8 +785,8 @@ void simdFilter5x5Blk( const AlfClassifier*, const CPelBuf srcBuffer = recSrc.get(compId); PelBuf dstBuffer = recDst.get(compId); - const size_t srcStride = srcBuffer.stride; - const size_t dstStride = dstBuffer.stride; + const ptrdiff_t srcStride = srcBuffer.stride; + const ptrdiff_t dstStride = dstBuffer.stride; constexpr int SHIFT = AdaptiveLoopFilter::m_NUM_BITS - 1; constexpr int ROUND = 1 << (SHIFT - 1); @@ -1010,8 +1010,8 @@ static void simdFilter7x7Blk( const AlfClassifier* classifier, const CPelBuf srcBuffer = recSrc.get(compId); PelBuf dstBuffer = recDst.get(compId); - const size_t srcStride = srcBuffer.stride; - const size_t dstStride = dstBuffer.stride; + const ptrdiff_t srcStride = srcBuffer.stride; + const ptrdiff_t dstStride = dstBuffer.stride; constexpr int SHIFT = AdaptiveLoopFilter::m_NUM_BITS - 1; constexpr int ROUND = 1 << (SHIFT - 1); @@ -1234,8 +1234,8 @@ void simdFilter7x7Blk( const AlfClassifier* classifier, const CPelBuf srcBuffer = recSrc.get(compId); PelBuf dstBuffer = recDst.get(compId); - const size_t srcStride = srcBuffer.stride; - const size_t dstStride = dstBuffer.stride; + const ptrdiff_t srcStride = srcBuffer.stride; + const ptrdiff_t dstStride = dstBuffer.stride; constexpr int SHIFT = AdaptiveLoopFilter::m_NUM_BITS - 1; constexpr int ROUND = 1 << (SHIFT - 1); diff --git a/source/Lib/CommonLib/x86/LoopFilterX86.h b/source/Lib/CommonLib/x86/LoopFilterX86.h index 41a05d32..7336c4fe 100644 --- a/source/Lib/CommonLib/x86/LoopFilterX86.h +++ b/source/Lib/CommonLib/x86/LoopFilterX86.h @@ -692,7 +692,7 @@ static inline void xFilteringPandQX86Ver( Pel* src, ptrdiff_t step, const ptrdif _mm_unpacklo_epi16( dbParr, dbParr ) ), _mm_unpackhi_epi16( dbParr, dbParr ), 1 ); ydbp = _mm256_abs_epi16 ( _mm256_sub_epi16( _mm256_set1_epi32( 64 ), ytmp ) ); - ytmp = _mm256_set1_epi32 ( refP | ( refMiddle << 16 ) ); + ytmp = _mm256_set1_epi32 ( refP | ( refMiddle * ( 1 << 16 ) ) ); ydst = _mm256_madd_epi16 ( ydbp, ytmp ); ydst = _mm256_add_epi32 ( ydst, _mm256_set1_epi32( 32 ) ); ydst = _mm256_srli_epi32 ( ydst, 6 ); @@ -725,7 +725,7 @@ static inline void xFilteringPandQX86Ver( Pel* src, ptrdiff_t step, const ptrdif _mm_unpacklo_epi16( dbQarr, dbQarr ) ), _mm_unpackhi_epi16( dbQarr, dbQarr ), 1 ); ydbp = _mm256_abs_epi16 ( _mm256_sub_epi16( _mm256_set1_epi32( 64 ), ytmp ) ); - ytmp = _mm256_set1_epi32 ( refQ | ( refMiddle << 16 ) ); + ytmp = _mm256_set1_epi32 ( refQ | ( refMiddle * ( 1 << 16 ) ) ); ydst = _mm256_madd_epi16 ( ydbp, ytmp ); ydst = _mm256_add_epi32 ( ydst, _mm256_set1_epi32( 32 ) ); ydst = _mm256_srli_epi32 ( ydst, 6 ); diff --git a/source/Lib/CommonLib/x86/TrafoX86.h b/source/Lib/CommonLib/x86/TrafoX86.h index 35021f16..0116d7e8 100644 --- a/source/Lib/CommonLib/x86/TrafoX86.h +++ b/source/Lib/CommonLib/x86/TrafoX86.h @@ -105,10 +105,10 @@ void fastInv_SSE( const TMatrixCoeff* it, const TCoeff* src, TCoeff* dst, unsign __m128i xscale = maxLoopL == 4 ? _mm_packs_epi32( _mm_load_si128( ( const __m128i* )srcPtr0 ), _mm_load_si128( ( const __m128i* )srcPtr1 ) ) : _mm_packs_epi32( _mm_loadu_si64( ( const __m128i* )srcPtr0 ), _mm_loadu_si64( ( const __m128i* )srcPtr1 ) ); - xscale = _mm_shuffle_epi8( xscale, _mm_setr_epi8( 0, 1, 8, 9, 2, 3, 10, 11, 4, 5, 12, 13, 6, 7, 14, 15 ) ); if( _mm_test_all_zeros( xscale, xscale ) ) { dstPtr += ( trSize * maxLoopL ); continue; } + xscale = _mm_shuffle_epi8( xscale, _mm_setr_epi8( 0, 1, 8, 9, 2, 3, 10, 11, 4, 5, 12, 13, 6, 7, 14, 15 ) ); for( int l = 0; l < maxLoopL; l++ ) { __m256i @@ -172,10 +172,10 @@ void fastInv_SSE( const TMatrixCoeff* it, const TCoeff* src, TCoeff* dst, unsign __m128i xscale = maxLoopL == 4 ? _mm_packs_epi32( _mm_load_si128( ( const __m128i* )srcPtr0 ), _mm_load_si128( ( const __m128i* )srcPtr1 ) ) : _mm_packs_epi32( _mm_loadu_si64( ( const __m128i* )srcPtr0 ), _mm_loadu_si64( ( const __m128i* )srcPtr1 ) ); - xscale = _mm_shuffle_epi8( xscale, _mm_setr_epi8( 0, 1, 8, 9, 2, 3, 10, 11, 4, 5, 12, 13, 6, 7, 14, 15 ) ); if( _mm_test_all_zeros( xscale, xscale ) ) { dstPtr += ( trSize * maxLoopL ); continue; } + xscale = _mm_shuffle_epi8( xscale, _mm_setr_epi8( 0, 1, 8, 9, 2, 3, 10, 11, 4, 5, 12, 13, 6, 7, 14, 15 ) ); for( int l = 0; l < maxLoopL; l++ ) { __m256i @@ -212,13 +212,13 @@ void fastInv_SSE( const TMatrixCoeff* it, const TCoeff* src, TCoeff* dst, unsign __m128i xscale = maxLoopL == 4 ? _mm_packs_epi32( _mm_load_si128( ( const __m128i* )srcPtr0 ), _mm_load_si128( ( const __m128i* )srcPtr1 ) ) : _mm_packs_epi32( _mm_loadu_si64( ( const __m128i* )srcPtr0 ), _mm_loadu_si64( ( const __m128i* )srcPtr1 ) ); - xscale = _mm_shuffle_epi8( xscale, _mm_setr_epi8( 0, 1, 8, 9, 2, 3, 10, 11, 4, 5, 12, 13, 6, 7, 14, 15 ) ); if( _mm_test_all_zeros( xscale, xscale ) ) { dstPtr += ( trSize * maxLoopL ); continue; } + xscale = _mm_shuffle_epi8( xscale, _mm_setr_epi8( 0, 1, 8, 9, 2, 3, 10, 11, 4, 5, 12, 13, 6, 7, 14, 15 ) ); for( int l = 0; l < maxLoopL; l++ ) { - const TMatrixCoeff* itPtr0 = &it[k * trSize]; + const TMatrixCoeff* itPtr0 = &it[k * trSize]; const TMatrixCoeff* itPtr1 = &it[( k + 1 ) * trSize]; __m128i @@ -257,10 +257,8 @@ void fastInv_SSE( const TMatrixCoeff* it, const TCoeff* src, TCoeff* dst, unsign } } #endif - else if( trSize >= 4 ) + else if( trSize == 4 ) { - CHECKD( trSize != 4, "trSize needs to be '4'!" ); - for( int k = 0; k < rows; k += 2 ) { TCoeff* dstPtr = dst; @@ -278,10 +276,10 @@ void fastInv_SSE( const TMatrixCoeff* it, const TCoeff* src, TCoeff* dst, unsign __m128i xscale = maxLoopL == 4 ? _mm_packs_epi32( _mm_load_si128( ( const __m128i* )srcPtr0 ), _mm_load_si128( ( const __m128i* )srcPtr1 ) ) : _mm_packs_epi32( _mm_loadu_si64( ( const __m128i* )srcPtr0 ), _mm_loadu_si64( ( const __m128i* )srcPtr1 ) ); - xscale = _mm_shuffle_epi8( xscale, _mm_setr_epi8( 0, 1, 8, 9, 2, 3, 10, 11, 4, 5, 12, 13, 6, 7, 14, 15 ) ); if( _mm_test_all_zeros( xscale, xscale ) ) { dstPtr += ( trSize * maxLoopL ); continue; } + xscale = _mm_shuffle_epi8( xscale, _mm_setr_epi8( 0, 1, 8, 9, 2, 3, 10, 11, 4, 5, 12, 13, 6, 7, 14, 15 ) ); for( int l = 0; l < maxLoopL; l++ ) { __m128i diff --git a/source/Lib/DecoderLib/CABACReader.cpp b/source/Lib/DecoderLib/CABACReader.cpp index d43095f6..904a4a9a 100644 --- a/source/Lib/DecoderLib/CABACReader.cpp +++ b/source/Lib/DecoderLib/CABACReader.cpp @@ -2473,7 +2473,7 @@ void CABACReader::residual_coding( TransformUnit& tu, ComponentID compID, CUCtx& const int posX = cctx.posX( blkPos ); const int posY = cctx.posY( blkPos ); - int AbsCoeff = depQuant ? ( coeff[blkPos] << 1 ) - ( ( ( int ) sub1Pattern ) & 1 ) : coeff[blkPos]; + int AbsCoeff = depQuant ? ( coeff[blkPos] * (1 << 1) ) - ( ( ( int ) sub1Pattern ) & 1 ) : coeff[blkPos]; dstCff.at( posX, posY ) = ( signPattern & 1u ? -AbsCoeff : AbsCoeff ); } } @@ -2970,7 +2970,7 @@ void CABACReader::residual_coding_subblockTS( CoeffCodingContext& cctx, TCoeffSi DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_par_flag() bin=%d ctx=%d\n", parFlag, cctx.parityCtxIdAbsTS() ); cctx.decNumCtxBins(1); } - coeff[ blkPos ] = (sign ? -1 : 1 ) * (1 + parFlag + gt1Flag); + coeff[ blkPos ] = (sign ? -1 : 1 ) * int(1 + parFlag + gt1Flag); DTRACE( g_trace_ctx, D_SYNTAX_RESI, "coeff[ blkPos ]=%d blkPos=%d\n", coeff[ blkPos ], blkPos ); } lastScanPosPass1 = nextSigPos; @@ -3096,6 +3096,7 @@ unsigned CABACReader::exp_golomb_eqprob( unsigned count ) { bit = m_BinDecoder.decodeBinEP( ); symbol += bit << count++; + CHECK( count >= 31, "exp_golomb_eqprob count overflow" ); } if( --count ) { diff --git a/source/Lib/DecoderLib/DecLib.cpp b/source/Lib/DecoderLib/DecLib.cpp index 9e7a4cbc..34289f9c 100644 --- a/source/Lib/DecoderLib/DecLib.cpp +++ b/source/Lib/DecoderLib/DecLib.cpp @@ -192,44 +192,19 @@ Picture* DecLib::decode( InputNALUnit& nalu ) if( newPic ) { Picture* pcParsedPic = m_decLibParser.getNextDecodablePicture(); - if( pcParsedPic ) - { - while( pcParsedPic->error || pcParsedPic->wasLost || pcParsedPic->parseDone.hasException() ) - { - CHECK_FATAL( pcParsedPic->progress >= Picture::reconstructing, "The error picture shouldn't be in reconstructing state yet." ); - - std::exception_ptr parsing_exception = pcParsedPic->parseDone.hasException() ? pcParsedPic->parseDone.getException() : nullptr; - if( parsing_exception ) - { // the exception has not been thrown out of the library. Do that after preparing this picture for referencing - pcParsedPic->error = true; - pcParsedPic->waitForAllTasks(); - pcParsedPic->parseDone.clearException(); - } - pcParsedPic->waitForAllTasks(); - - if( pcParsedPic->progress < Picture::parsing ) - { - // we don't know if all structures are there yet, so we init them - pcParsedPic->ensureUsableAsRef(); - } - pcParsedPic->fillGrey( m_decLibParser.getParameterSetManager().getFirstSPS() ); + while( pcParsedPic && ( pcParsedPic->error || pcParsedPic->wasLost || pcParsedPic->parseDone.hasException() ) ) + { + CHECK_FATAL( pcParsedPic->progress >= Picture::reconstructing, "The error picture shouldn't be in reconstructing state yet." ); - // need to finish picture here, because it won't go through declibRecon - finishPicture( pcParsedPic ); + sanitizeBrokenPicture( pcParsedPic ); - // this exception has not been thrown outside (error must have happened in slice parsing task) - if( parsing_exception ) - { - CHECK_FATAL( pcParsedPic->exceptionThrownOut, "The exception shouldn't have been thrown out already." ); - pcParsedPic->exceptionThrownOut = true; - std::rethrow_exception( parsing_exception ); - } - - // try again to get a picture, that we can reconstruct now - pcParsedPic = m_decLibParser.getNextDecodablePicture(); - } + // try again to get a picture, that we can reconstruct now + pcParsedPic = m_decLibParser.getNextDecodablePicture(); + } + if( pcParsedPic ) + { reconPicture( pcParsedPic ); } } @@ -260,12 +235,22 @@ Picture* DecLib::decode( InputNALUnit& nalu ) Picture* DecLib::flushPic() { - Picture* outPic = getNextOutputPic( false ); + Picture* outPic = nullptr; try { // at end of file, fill the decompression queue and decode pictures until the next output-picture is finished while( Picture* pcParsedPic = m_decLibParser.getNextDecodablePicture() ) { + if( pcParsedPic->error || pcParsedPic->wasLost || pcParsedPic->parseDone.hasException() ) + { + CHECK_FATAL( pcParsedPic->progress >= Picture::reconstructing, "The error picture shouldn't be in reconstructing state yet." ); + + sanitizeBrokenPicture( pcParsedPic ); + + // try again to get a picture, that we can reconstruct now + continue; + } + // reconPicture() blocks and finishes one picture on each call reconPicture( pcParsedPic ); @@ -279,6 +264,11 @@ Picture* DecLib::flushPic() } } + // no more pictures to finish decoding, but we might still have finished pictures in the recon queue + if( !outPic ) + { + outPic = getNextOutputPic( false ); + } if( outPic && outPic->progress == Picture::finished ) { return outPic; @@ -307,7 +297,7 @@ Picture* DecLib::flushPic() } catch( ... ) { - m_picListManager.releasePicture(outPic); + m_picListManager.releasePicture( outPic ); throw; } @@ -320,6 +310,37 @@ Picture* DecLib::flushPic() return nullptr; } +void DecLib::sanitizeBrokenPicture( Picture* pcParsedPic ) +{ + std::exception_ptr parsing_exception = pcParsedPic->parseDone.hasException() ? pcParsedPic->parseDone.getException() : nullptr; + if( parsing_exception ) + { // the exception has not been thrown out of the library. Do that after preparing this picture for referencing + pcParsedPic->error = true; + pcParsedPic->waitForAllTasks(); + pcParsedPic->parseDone.clearException(); + } + + pcParsedPic->waitForAllTasks(); + + if( pcParsedPic->progress < Picture::parsing ) + { + // we don't know if all structures are there yet, so we init them + pcParsedPic->ensureUsableAsRef(); + } + pcParsedPic->fillGrey( m_decLibParser.getParameterSetManager().getFirstSPS() ); + + // need to finish picture here, because it won't go through declibRecon + finishPicture( pcParsedPic ); + + // this exception has not been thrown outside (error must have happened in slice parsing task) + if( parsing_exception ) + { + CHECK_FATAL( pcParsedPic->exceptionThrownOut, "The exception shouldn't have been thrown out already." ); + pcParsedPic->exceptionThrownOut = true; + std::rethrow_exception( parsing_exception ); + } +} + #if JVET_R0270 int DecLib::finishPicture( Picture* pcPic, MsgLevel msgl, bool associatedWithNewClvs ) #else @@ -335,6 +356,7 @@ int DecLib::finishPicture( Picture* pcPic, MsgLevel msgl ) if( pcPic->wasLost || pcPic->error || pcPic->reconDone.hasException() || pcPic->parseDone.hasException() ) { msg( msgl, "POC %4d LId: %2d TId: %1d %s\n", pcPic->poc, pcPic->layerId, pcSlice->getTLayer(), pcPic->wasLost ? "LOST" : "ERROR" ); + pcPic->fillGrey( pcPic->cs->sps.get() ); pcPic->progress = Picture::finished; // if the picture has an exception set (originating from thread-pool tasks), don't return here, but rethrow the exception diff --git a/source/Lib/DecoderLib/DecLib.h b/source/Lib/DecoderLib/DecLib.h index 232dba1f..37802c18 100644 --- a/source/Lib/DecoderLib/DecLib.h +++ b/source/Lib/DecoderLib/DecLib.h @@ -121,6 +121,7 @@ class DecLib #endif Picture* getNextOutputPic ( bool bFlush = false ); void blockAndFinishPictures( Picture* pcPic = nullptr ); // iterate over DecLibRecon instances and wait to finish picture(s) + void sanitizeBrokenPicture( Picture* pcParsedPic ); void xCheckNalUnitConstraintFlags( const ConstraintInfo *cInfo, uint32_t naluType ); }; diff --git a/source/Lib/DecoderLib/DecLibParser.cpp b/source/Lib/DecoderLib/DecLibParser.cpp index 41d8c6ed..254eb369 100644 --- a/source/Lib/DecoderLib/DecLibParser.cpp +++ b/source/Lib/DecoderLib/DecLibParser.cpp @@ -254,7 +254,10 @@ bool DecLibParser::parse( InputNALUnit& nalu ) { m_threadPool->processTasksOnMainThread(); } - m_pcParsePic->reconDone.wait(); + else + { + m_pcParsePic->reconDone.wait(); + } m_decLib.checkPictureHashSEI( m_pcParsePic ); } } @@ -1199,12 +1202,13 @@ void DecLibParser::xActivateParameterSets( const int layerId ) CHECK( sps->getProfileTierLevel()->getConstraintInfo()->getNoLumaTransformSize64ConstraintFlag() && sps->getLog2MaxTbSize() != 5, "When gci_no_luma_transform_size_64_constraint_flag is equal to 1, the value of sps_max_luma_transform_size_64_flag shall be equal to 0" ); - // TODO: fix MT static maps - static std::unordered_map m_layerChromaFormat; - static std::unordered_map m_layerBitDepth; - +#if 0 if( vps && vps->getMaxLayers() > 1 ) { + // TODO: fix MT static maps + static std::unordered_map m_layerChromaFormat; + static std::unordered_map m_layerBitDepth; + int curLayerIdx = vps->getGeneralLayerIdx(layerId); int curLayerChromaFormat = sps->getChromaFormatIdc(); int curLayerBitDepth = sps->getBitDepth(); @@ -1231,6 +1235,7 @@ void DecLibParser::xActivateParameterSets( const int layerId ) } } } +#endif const int minCuSize = 1 << sps->getLog2MinCodingBlockSize(); CHECK( ( pps->getPicWidthInLumaSamples() % ( std::max( 8, minCuSize) ) ) != 0, "Coded frame width must be a multiple of Max(8, the minimum unit size)" ); diff --git a/source/Lib/DecoderLib/DecLibRecon.cpp b/source/Lib/DecoderLib/DecLibRecon.cpp index 73a61962..7f3c7855 100644 --- a/source/Lib/DecoderLib/DecLibRecon.cpp +++ b/source/Lib/DecoderLib/DecLibRecon.cpp @@ -520,10 +520,6 @@ void DecLibRecon::decompressPicture( Picture* pcPic ) const int widthInCtus = cs.pcv->widthInCtus; const int heightInCtus = cs.pcv->heightInCtus; - if( sps->getIBCFlag() ) - { - cs.initVIbcBuf( heightInCtus, sps->getChromaFormatIdc(), sps->getMaxCUHeight() ); - } pcPic->startProcessingTimer(); if( m_decodeThreadPool->numThreads() > 0 ) @@ -691,26 +687,31 @@ Picture* DecLibRecon::waitForPrevDecompressedPic() return nullptr; ITT_TASKSTART( itt_domain_dec, itt_handle_waitTasks ); - if( m_decodeThreadPool->numThreads() == 0 ) - { - m_decodeThreadPool->processTasksOnMainThread(); - CHECK_FATAL( m_currDecompPic->reconDone.isBlocked(), "can't make progress. some dependecy has not been finished" ); - } - try { + if( m_decodeThreadPool->numThreads() == 0 ) + { + /*bool stillTasksWaiting = */ m_decodeThreadPool->processTasksOnMainThread(); + CHECK_FATAL( m_currDecompPic->reconDone.isBlocked(), "can't make progress. some dependecy has not been finished" ); + } + + const Slice* lastSlice = m_currDecompPic->slices.back(); + const unsigned lastSliceLastCtuIdx = lastSlice->getCtuAddrInSlice( lastSlice->getNumCtuInSlice() - 1 ); + CHECK( lastSliceLastCtuIdx != m_currDecompPic->cs->pcv->sizeInCtus - 1, "Picture incomplete. A slice was probably lost." ); + m_currDecompPic->reconDone.wait(); } catch( ... ) { m_currDecompPic->error = true; + m_currDecompPic->reconDone.setException( std::current_exception() ); } // also check error flag, which can have been set earlier (e.g., when trying to use the picture as reference) if( m_currDecompPic->error || m_currDecompPic->reconDone.hasException() ) { // ensure all tasks are cleared from declibRecon - cleanupOnException( std::current_exception() ); + cleanupOnException(); } ITT_TASKEND( itt_domain_dec, itt_handle_waitTasks ); @@ -718,7 +719,7 @@ Picture* DecLibRecon::waitForPrevDecompressedPic() return std::exchange( m_currDecompPic, nullptr ); } -void DecLibRecon::cleanupOnException( std::exception_ptr exception ) +void DecLibRecon::cleanupOnException() { // there was an exception anywhere in m_currDecompPic // => we need to wait for all tasks to be cleared from the thread pool @@ -749,369 +750,362 @@ bool DecLibRecon::ctuTask( int tid, void* task_param ) const int ctuStart = param->ctuStart; const int ctuEnd = param->ctuEnd; - try + if( cs.picture->m_ctuTaskCounter.hasException() ) { - if( cs.picture->m_ctuTaskCounter.hasException() ) - { - std::rethrow_exception( cs.picture->m_ctuTaskCounter.getException() ); - } + std::rethrow_exception( cs.picture->m_ctuTaskCounter.getException() ); + } - switch( thisCtuState.load() ) - { - // all case statements fall through to continue with next task, unless they return false due to unsatisfied preconditions + switch( thisCtuState.load() ) + { + // all case statements fall through to continue with next task, unless they return false due to unsatisfied preconditions - case MIDER: + case MIDER: + { + if( col > 0 && thisLine[col - 1] <= MIDER_cont ) + return false; + if( line > 0 ) { - if( col > 0 && thisLine[col - 1] <= MIDER_cont ) - return false; - if( line > 0 ) + if( col + 1 < tasksPerLine ) { - if( col + 1 < tasksPerLine ) - { - if( lineAbove[col + 1] <= MIDER ) - return false; - } - else - { - if( lineAbove[col] <= MIDER_cont ) - return false; - } + if( lineAbove[col + 1] <= MIDER ) + return false; } - if( onlyCheckReadyState ) - return true; + else + { + if( lineAbove[col] <= MIDER_cont ) + return false; + } + } + if( onlyCheckReadyState ) + return true; - ITT_TASKSTART( itt_domain_dec, itt_handle_mider ); + ITT_TASKSTART( itt_domain_dec, itt_handle_mider ); - for( int ctu = ctuStart; ctu < ctuEnd; ctu++ ) + for( int ctu = ctuStart; ctu < ctuEnd; ctu++ ) + { + const int ctuRsAddr = ctu + line * cs.pcv->widthInCtus; + CtuData& ctuData = cs.getCtuData( ctuRsAddr ); + ctuData.motion = &decLib.m_motionInfo[cs.pcv->num4x4CtuBlks * ctuRsAddr]; + + if( !ctuData.slice->isIntra() || cs.sps->getIBCFlag() ) + { + const UnitArea ctuArea = getCtuArea( cs, ctu, line, true ); + decLib.m_pcThreadResource[tid]->m_cCuDecoder.TaskDeriveCtuMotionInfo( cs, ctuRsAddr, ctuArea, param->common.perLineMiHist[line] ); + } + else { - const int ctuRsAddr = ctu + line * cs.pcv->widthInCtus; - CtuData& ctuData = cs.getCtuData( ctuRsAddr ); - ctuData.motion = &decLib.m_motionInfo[cs.pcv->num4x4CtuBlks * ctuRsAddr]; + memset( NO_WARNING_class_memaccess( ctuData.motion ), MI_NOT_VALID, sizeof( MotionInfo ) * cs.pcv->num4x4CtuBlks ); + } - if( !ctuData.slice->isIntra() || cs.sps->getIBCFlag() ) - { - const UnitArea ctuArea = getCtuArea( cs, ctu, line, true ); - decLib.m_pcThreadResource[tid]->m_cCuDecoder.TaskDeriveCtuMotionInfo( cs, ctuRsAddr, ctuArea, param->common.perLineMiHist[line] ); - } - else - { - memset( NO_WARNING_class_memaccess( ctuData.motion ), MI_NOT_VALID, sizeof( MotionInfo ) * cs.pcv->num4x4CtuBlks ); - } + thisCtuState = MIDER_cont; + } - thisCtuState = MIDER_cont; - } + thisCtuState = LF_INIT; - thisCtuState = LF_INIT; + ITT_TASKEND( itt_domain_dec, itt_handle_mider ); + } - ITT_TASKEND( itt_domain_dec, itt_handle_mider ); - } + case LF_INIT: + { + if( onlyCheckReadyState ) + return true; - case LF_INIT: - { - if( onlyCheckReadyState ) - return true; + ITT_TASKSTART( itt_domain_dec, itt_handle_lfcl ); - ITT_TASKSTART( itt_domain_dec, itt_handle_lfcl ); + for( int ctu = ctuStart; ctu < ctuEnd; ctu++ ) + { + const int ctuRsAddr = ctu + line * cs.pcv->widthInCtus; + CtuData& ctuData = cs.getCtuData( ctuRsAddr ); + ctuData.lfParam[0] = &decLib.m_loopFilterParam[cs.pcv->num4x4CtuBlks * ( 2 * ctuRsAddr + 0 )]; + ctuData.lfParam[1] = &decLib.m_loopFilterParam[cs.pcv->num4x4CtuBlks * ( 2 * ctuRsAddr + 1 )]; + memset( ctuData.lfParam[0], 0, sizeof( LoopFilterParam ) * 2 * cs.pcv->num4x4CtuBlks ); - for( int ctu = ctuStart; ctu < ctuEnd; ctu++ ) - { - const int ctuRsAddr = ctu + line * cs.pcv->widthInCtus; - CtuData& ctuData = cs.getCtuData( ctuRsAddr ); - ctuData.lfParam[0] = &decLib.m_loopFilterParam[cs.pcv->num4x4CtuBlks * ( 2 * ctuRsAddr + 0 )]; - ctuData.lfParam[1] = &decLib.m_loopFilterParam[cs.pcv->num4x4CtuBlks * ( 2 * ctuRsAddr + 1 )]; - memset( ctuData.lfParam[0], 0, sizeof( LoopFilterParam ) * 2 * cs.pcv->num4x4CtuBlks ); + decLib.m_cLoopFilter.calcFilterStrengthsCTU( cs, ctuRsAddr ); + } - decLib.m_cLoopFilter.calcFilterStrengthsCTU( cs, ctuRsAddr ); - } + thisCtuState = INTER; - thisCtuState = INTER; + ITT_TASKEND( itt_domain_dec, itt_handle_lfcl ); + } - ITT_TASKEND( itt_domain_dec, itt_handle_lfcl ); + case INTER: + { + if( std::all_of( cs.picture->slices.begin(), cs.picture->slices.end(), []( const Slice* pcSlice ) { return pcSlice->isIntra(); } ) ) + { + // not really necessary, but only for optimizing the wave-fronts + if( col > 1 && thisLine[col - 2] <= INTER ) + return false; + if( line > 0 && lineAbove[col] <= INTER ) + return false; } - case INTER: + if( std::any_of( cs.picture->refPicExtDepBarriers.cbegin(), cs.picture->refPicExtDepBarriers.cend(), []( const Barrier* b ) { return b->isBlocked(); } ) ) { - if( std::all_of( cs.picture->slices.begin(), cs.picture->slices.end(), []( const Slice* pcSlice ) { return pcSlice->isIntra(); } ) ) - { - // not really necessary, but only for optimizing the wave-fronts - if( col > 1 && thisLine[col - 2] <= INTER ) - return false; - if( line > 0 && lineAbove[col] <= INTER ) - return false; - } + return false; + } - if( std::any_of( cs.picture->refPicExtDepBarriers.cbegin(), cs.picture->refPicExtDepBarriers.cend(), []( const Barrier* b ) { return b->isBlocked(); } ) ) - { - return false; - } + if( onlyCheckReadyState ) + return true; - if( onlyCheckReadyState ) - return true; + ITT_TASKSTART( itt_domain_dec, itt_handle_inter ); - ITT_TASKSTART( itt_domain_dec, itt_handle_inter ); + for( int ctu = ctuStart; ctu < ctuEnd; ctu++ ) + { + const int ctuRsAddr = ctu + line * cs.pcv->widthInCtus; + const UnitArea ctuArea = getCtuArea( cs, ctu, line, true ); + const CtuData& ctuData = cs.getCtuData( ctuRsAddr ); - for( int ctu = ctuStart; ctu < ctuEnd; ctu++ ) - { - const int ctuRsAddr = ctu + line * cs.pcv->widthInCtus; - const UnitArea ctuArea = getCtuArea( cs, ctu, line, true ); - const CtuData& ctuData = cs.getCtuData( ctuRsAddr ); + decLib.m_pcThreadResource[tid]->m_cCuDecoder.TaskTrafoCtu( cs, ctuRsAddr, ctuArea ); - decLib.m_pcThreadResource[tid]->m_cCuDecoder.TaskTrafoCtu( cs, ctuRsAddr, ctuArea ); + if( !ctuData.slice->isIntra() ) + { + decLib.m_pcThreadResource[tid]->m_cCuDecoder.TaskInterCtu( cs, ctuRsAddr, ctuArea ); - if( !ctuData.slice->isIntra() ) + if( cs.picture->stillReferenced ) { - decLib.m_pcThreadResource[tid]->m_cCuDecoder.TaskInterCtu( cs, ctuRsAddr, ctuArea ); - - if( cs.picture->stillReferenced ) - { - decLib.m_pcThreadResource[tid]->m_cCuDecoder.TaskFinishMotionInfo( cs, ctuRsAddr, ctu, line ); - } + decLib.m_pcThreadResource[tid]->m_cCuDecoder.TaskFinishMotionInfo( cs, ctuRsAddr, ctu, line ); } } + } - thisCtuState = INTRA; + thisCtuState = INTRA; - ITT_TASKEND( itt_domain_dec, itt_handle_inter ); - } + ITT_TASKEND( itt_domain_dec, itt_handle_inter ); + } - case INTRA: - { - if( col > 0 && thisLine[col - 1] <= INTRA_cont ) - return false; + case INTRA: + { + if( col > 0 && thisLine[col - 1] <= INTRA_cont ) + return false; - if( line > 0 ) + if( line > 0 ) + { + if( col + 1 < tasksPerLine ) { - if( col + 1 < tasksPerLine ) - { - if( lineAbove[col + 1] <= INTRA ) - return false; - } - else - { - if( lineAbove[col] <= INTRA_cont ) - return false; - } + if( lineAbove[col + 1] <= INTRA ) + return false; } - if( onlyCheckReadyState ) - return true; - - ITT_TASKSTART( itt_domain_dec, itt_handle_intra ); - - for( int ctu = ctuStart; ctu < ctuEnd; ctu++ ) + else { - const int ctuRsAddr = ctu + line * cs.pcv->widthInCtus; - const UnitArea ctuArea = getCtuArea( cs, ctu, line, true ); - decLib.m_pcThreadResource[tid]->m_cCuDecoder.TaskCriticalIntraKernel( cs, ctuRsAddr, ctuArea ); - - thisCtuState = INTRA_cont; + if( lineAbove[col] <= INTRA_cont ) + return false; } + } + if( onlyCheckReadyState ) + return true; + + ITT_TASKSTART( itt_domain_dec, itt_handle_intra ); - thisCtuState = RSP; + for( int ctu = ctuStart; ctu < ctuEnd; ctu++ ) + { + const int ctuRsAddr = ctu + line * cs.pcv->widthInCtus; + const UnitArea ctuArea = getCtuArea( cs, ctu, line, true ); + decLib.m_pcThreadResource[tid]->m_cCuDecoder.TaskCriticalIntraKernel( cs, ctuRsAddr, ctuArea ); - ITT_TASKEND( itt_domain_dec, itt_handle_intra ); + thisCtuState = INTRA_cont; } - case RSP: - { - // RIRZIIIII - // IIIIIXXXX - // - // - Z can be reshaped when it is no more an intra prediction source for X in the next line + thisCtuState = RSP; + ITT_TASKEND( itt_domain_dec, itt_handle_intra ); + } - if ( line + 1 < heightInCtus && col + 1 < tasksPerLine && lineBelow[col + 1] < INTRA_cont ) - return false; - else if( line + 1 < heightInCtus && lineBelow[col] < RSP ) - return false; - else if( col + 1 < tasksPerLine && thisLine [col + 1] < INTRA_cont ) // need this for the last line - return false; + case RSP: + { + // RIRZIIIII + // IIIIIXXXX + // + // - Z can be reshaped when it is no more an intra prediction source for X in the next line - if( onlyCheckReadyState ) - return true; - ITT_TASKSTART( itt_domain_dec, itt_handle_rsp ); + if ( line + 1 < heightInCtus && col + 1 < tasksPerLine && lineBelow[col + 1] < INTRA_cont ) + return false; + else if( line + 1 < heightInCtus && lineBelow[col] < RSP ) + return false; + else if( col + 1 < tasksPerLine && thisLine [col + 1] < INTRA_cont ) // need this for the last line + return false; - for( int ctu = ctuStart; ctu < ctuEnd; ctu++ ) - { - decLib.m_pcThreadResource[tid]->m_cReshaper.rspCtuBcw( cs, ctu, line ); - } + if( onlyCheckReadyState ) + return true; - ITT_TASKEND( itt_domain_dec, itt_handle_rsp ); + ITT_TASKSTART( itt_domain_dec, itt_handle_rsp ); - thisCtuState = LF_V; + for( int ctu = ctuStart; ctu < ctuEnd; ctu++ ) + { + decLib.m_pcThreadResource[tid]->m_cReshaper.rspCtuBcw( cs, ctu, line ); } - case LF_V: - { - if( col > 0 && thisLine[col - 1] < LF_V ) - return false; - if( onlyCheckReadyState ) - return true; + ITT_TASKEND( itt_domain_dec, itt_handle_rsp ); - ITT_TASKSTART( itt_domain_dec, itt_handle_lfl ); + thisCtuState = LF_V; + } - for( int ctu = ctuStart; ctu < ctuEnd; ctu++ ) - { - decLib.m_cLoopFilter.loopFilterCTU( cs, MAX_NUM_CHANNEL_TYPE, ctu, line, EDGE_VER ); + case LF_V: + { + if( col > 0 && thisLine[col - 1] < LF_V ) + return false; + if( onlyCheckReadyState ) + return true; - thisCtuState = LF_V_cont; - } + ITT_TASKSTART( itt_domain_dec, itt_handle_lfl ); - thisCtuState = LF_H; + for( int ctu = ctuStart; ctu < ctuEnd; ctu++ ) + { + decLib.m_cLoopFilter.loopFilterCTU( cs, MAX_NUM_CHANNEL_TYPE, ctu, line, EDGE_VER ); - ITT_TASKEND( itt_domain_dec, itt_handle_lfl ); + thisCtuState = LF_V_cont; } - case LF_H: - { - if( line > 0 && lineAbove[col] < LF_H ) - return false; - - if( line > 0 && col + 1 < tasksPerLine && lineAbove[col + 1] < LF_V_cont ) - return false; + thisCtuState = LF_H; - if( col + 1 < tasksPerLine && thisLine[col + 1] < LF_V_cont ) - return false; + ITT_TASKEND( itt_domain_dec, itt_handle_lfl ); + } - if( onlyCheckReadyState ) - return true; + case LF_H: + { + if( line > 0 && lineAbove[col] < LF_H ) + return false; - ITT_TASKSTART( itt_domain_dec, itt_handle_lfl ); + if( line > 0 && col + 1 < tasksPerLine && lineAbove[col + 1] < LF_V_cont ) + return false; - for( int ctu = ctuStart; ctu < ctuEnd; ctu++ ) - { - decLib.m_cLoopFilter.loopFilterCTU( cs, MAX_NUM_CHANNEL_TYPE, ctu, line, EDGE_HOR ); - } + if( col + 1 < tasksPerLine && thisLine[col + 1] < LF_V_cont ) + return false; - thisCtuState = PRESAO; + if( onlyCheckReadyState ) + return true; - ITT_TASKEND( itt_domain_dec, itt_handle_lfl ); - } + ITT_TASKSTART( itt_domain_dec, itt_handle_lfl ); - case PRESAO: + for( int ctu = ctuStart; ctu < ctuEnd; ctu++ ) { - // only last CTU processes full line - if( col == tasksPerLine - 1 ) - { - if( line > 0 && lineAbove[col] <= PRESAO ) - return false; - - for( int c = 0; c < tasksPerLine; ++c ) - { - if( thisLine[c] < PRESAO ) - return false; + decLib.m_cLoopFilter.loopFilterCTU( cs, MAX_NUM_CHANNEL_TYPE, ctu, line, EDGE_HOR ); + } - if( line + 1 < heightInCtus && lineBelow[c] < PRESAO ) - return false; - } - if( onlyCheckReadyState ) - return true; + thisCtuState = PRESAO; - ITT_TASKSTART( itt_domain_dec, itt_handle_presao ); + ITT_TASKEND( itt_domain_dec, itt_handle_lfl ); + } - if( cs.sps->getUseSAO() ) - { - decLib.m_cSAO.SAOPrepareCTULine( cs, getLineArea( cs, line, true ) ); - } + case PRESAO: + { + // only last CTU processes full line + if( col == tasksPerLine - 1 ) + { + if( line > 0 && lineAbove[col] <= PRESAO ) + return false; - ITT_TASKEND( itt_domain_dec, itt_handle_presao ); - } - else if( thisLine[tasksPerLine - 1] <= PRESAO ) // wait for last CTU to finish PRESAO + for( int c = 0; c < tasksPerLine; ++c ) { - return false; + if( thisLine[c] < PRESAO ) + return false; + + if( line + 1 < heightInCtus && lineBelow[c] < PRESAO ) + return false; } if( onlyCheckReadyState ) return true; - thisCtuState = SAO; - } + ITT_TASKSTART( itt_domain_dec, itt_handle_presao ); - case SAO: - { - if( onlyCheckReadyState ) - return true; - - // only last CTU processes full line if( cs.sps->getUseSAO() ) { - ITT_TASKSTART( itt_domain_dec, itt_handle_sao ); + decLib.m_cSAO.SAOPrepareCTULine( cs, getLineArea( cs, line, true ) ); + } - for( int ctu = ctuStart; ctu < ctuEnd; ctu++ ) - { - const UnitArea ctuArea = getCtuArea( cs, ctu, line, true ); - decLib.m_cSAO.SAOProcessCTU( cs, ctuArea ); - } + ITT_TASKEND( itt_domain_dec, itt_handle_presao ); + } + else if( thisLine[tasksPerLine - 1] <= PRESAO ) // wait for last CTU to finish PRESAO + { + return false; + } + if( onlyCheckReadyState ) + return true; - ITT_TASKEND( itt_domain_dec, itt_handle_sao ); - } - if( param->common.doALF ) - { - ITT_TASKSTART( itt_domain_dec, itt_handle_alf ); + thisCtuState = SAO; + } - for( int ctu = ctuStart; ctu < ctuEnd; ctu++ ) - { - AdaptiveLoopFilter::prepareCTU( cs, ctu, line ); + case SAO: + { + if( onlyCheckReadyState ) + return true; - thisCtuState = SAO_cont; - } + // only last CTU processes full line + if( cs.sps->getUseSAO() ) + { + ITT_TASKSTART( itt_domain_dec, itt_handle_sao ); - ITT_TASKEND( itt_domain_dec, itt_handle_alf ); + for( int ctu = ctuStart; ctu < ctuEnd; ctu++ ) + { + const UnitArea ctuArea = getCtuArea( cs, ctu, line, true ); + decLib.m_cSAO.SAOProcessCTU( cs, ctuArea ); } - thisCtuState = ALF; + ITT_TASKEND( itt_domain_dec, itt_handle_sao ); } - - case ALF: + if( param->common.doALF ) { - if( param->common.doALF ) + ITT_TASKSTART( itt_domain_dec, itt_handle_alf ); + + for( int ctu = ctuStart; ctu < ctuEnd; ctu++ ) { - const bool a = line > 0; - const bool b = line + 1 < heightInCtus; - const bool c = col > 0; - const bool d = col + 1 < tasksPerLine; + AdaptiveLoopFilter::prepareCTU( cs, ctu, line ); - if( a ) - { - if( c && lineAbove[col - 1] < ALF ) return false; - if( lineAbove[col ] < ALF ) return false; - if( d && lineAbove[col + 1] < SAO_cont ) return false; - } + thisCtuState = SAO_cont; + } - if( b ) - { - if( c && lineBelow[col - 1] < ALF ) return false; - if( lineBelow[col ] < ALF ) return false; - if( d && lineBelow[col + 1] < SAO_cont ) return false; - } + ITT_TASKEND( itt_domain_dec, itt_handle_alf ); + } - if( c && thisLine[col - 1] < ALF ) return false; - if( d && thisLine[col + 1] < SAO_cont ) return false; + thisCtuState = ALF; + } - if( onlyCheckReadyState ) - return true; + case ALF: + { + if( param->common.doALF ) + { + const bool a = line > 0; + const bool b = line + 1 < heightInCtus; + const bool c = col > 0; + const bool d = col + 1 < tasksPerLine; - ITT_TASKSTART( itt_domain_dec, itt_handle_alf ); - for( int ctu = ctuStart; ctu < ctuEnd; ctu++ ) - { - decLib.m_cALF.processCTU( cs, ctu, line, tid ); - } - ITT_TASKEND( itt_domain_dec, itt_handle_alf ); + if( a ) + { + if( c && lineAbove[col - 1] < ALF ) return false; + if( lineAbove[col ] < ALF ) return false; + if( d && lineAbove[col + 1] < SAO_cont ) return false; + } + + if( b ) + { + if( c && lineBelow[col - 1] < ALF ) return false; + if( lineBelow[col ] < ALF ) return false; + if( d && lineBelow[col + 1] < SAO_cont ) return false; } - else if( onlyCheckReadyState ) + + if( c && thisLine[col - 1] < ALF ) return false; + if( d && thisLine[col + 1] < SAO_cont ) return false; + + if( onlyCheckReadyState ) return true; - thisCtuState = DONE; + ITT_TASKSTART( itt_domain_dec, itt_handle_alf ); + for( int ctu = ctuStart; ctu < ctuEnd; ctu++ ) + { + decLib.m_cALF.processCTU( cs, ctu, line, tid ); + } + ITT_TASKEND( itt_domain_dec, itt_handle_alf ); } + else if( onlyCheckReadyState ) + return true; - default: - CHECKD( thisCtuState != DONE, "Wrong CTU state" ); - } // end switch - } - catch( ... ) - { - std::rethrow_exception( std::current_exception() ); + thisCtuState = DONE; } + default: + CHECKD( thisCtuState != DONE, "Wrong CTU state" ); + } // end switch + return true; } diff --git a/source/Lib/DecoderLib/DecLibRecon.h b/source/Lib/DecoderLib/DecLibRecon.h index 7095e3ed..edd852c6 100644 --- a/source/Lib/DecoderLib/DecLibRecon.h +++ b/source/Lib/DecoderLib/DecLibRecon.h @@ -186,7 +186,7 @@ class DecLibRecon void decompressPicture( Picture* pcPic ); Picture* waitForPrevDecompressedPic(); - void cleanupOnException( std::exception_ptr exception ); + void cleanupOnException(); Picture* getCurrPic() const { return m_currDecompPic; } void swapBufs( CodingStructure& cs ); diff --git a/source/Lib/DecoderLib/HLSyntaxReader.cpp b/source/Lib/DecoderLib/HLSyntaxReader.cpp index cd569024..8290553a 100644 --- a/source/Lib/DecoderLib/HLSyntaxReader.cpp +++ b/source/Lib/DecoderLib/HLSyntaxReader.cpp @@ -1037,13 +1037,7 @@ void HLSyntaxReader::parseLmcsAps( APS* aps ) X_READ_FLAG_idx( lmcs_delta_sign_cw_flag, "[ i ]" ); info.reshaperModelBinCWDelta[i] *= 1 - 2 * lmcs_delta_sign_cw_flag; } -#if 0 - // TODO: needs bitdepth from SPS - int OrgCW = ( 1 << BitDepth ) / 16; - int lmcsCW = OrgCW + info.reshaperModelBinCWDelta[i]; - CHECK_READ_RANGE( lmcsCW, OrgCW >> 3, ( OrgCW << 3 ) - 1, "lmcsCW[i]" ); - // TODO: also CHECK( sum(lmcsCW[0..15]) > ( 1 << BitDepth ) − 1 ); -#endif + // lmcsCW[i] range is checked in Reshape::constructReshaper() } if( aps->chromaPresentFlag ) @@ -1055,10 +1049,8 @@ void HLSyntaxReader::parseLmcsAps( APS* aps ) X_READ_FLAG( lmcs_delta_sign_crs_flag ); info.chrResScalingOffset *= ( 1 - 2 * lmcs_delta_sign_crs_flag ); } + // lmcsDeltaCrs range is checked in Reshape::constructReshaper() } - // TODO: - // It is a requirement of bitstream conformance that, when lmcsCW[ i ] is not equal to 0, ( lmcsCW[ i ] + lmcsDeltaCrs ) - // shall be in the range of ( OrgCW >> 3 ) to ( ( OrgCW << 3 ) - 1 ), inclusive } void HLSyntaxReader::parseScalingListAps( APS* aps ) diff --git a/source/Lib/DecoderLib/SEIread.cpp b/source/Lib/DecoderLib/SEIread.cpp index 41fa1f42..44a0ba4c 100644 --- a/source/Lib/DecoderLib/SEIread.cpp +++ b/source/Lib/DecoderLib/SEIread.cpp @@ -452,6 +452,7 @@ void SEIReader::xParseSEIDecodedPictureHash(vvdecSEI* s, uint32_t payloadSize, s uint32_t val; sei_read_code( pDecodedMessageOutputStream, 8, val, "dph_sei_hash_type" ); + CHECK_READ_RANGE( val, 0, VVDEC_HASHTYPE_NONE, "dph_sei_hash_type" ); sei->method = static_cast(val); bytesRead++; sei_read_code( pDecodedMessageOutputStream, 1, val, "dph_sei_single_component_flag"); sei->singleCompFlag = val; @@ -506,8 +507,8 @@ void SEIReader::xParseSEIScalableNesting(vvdecSEI* s, const NalUnitType nalUnitT sei_read_flag(decodedMessageOutputStream, symbol, "sn_subpic_flag"); sei->snSubpicFlag = symbol; if (sei->snOlsFlag) { - sei_read_uvlc(decodedMessageOutputStream, symbol, "sn_nuolss_minus1"); sei->snNumOlss = symbol+1; - CHECK( sei->snNumOlss > 64, "sn_nuolss_minus1 must be < 64 in vvdecSEIScalableNesting" ); + sei_read_uvlc(decodedMessageOutputStream, symbol, "sn_num_olss_minus1"); sei->snNumOlss = symbol+1; + CHECK( sei->snNumOlss > 64, "sn_num_olss_minus1 must be < 64 in vvdecSEIScalableNesting" ); for (uint32_t i = 0; i < sei->snNumOlss; i++) { @@ -546,7 +547,8 @@ void SEIReader::xParseSEIScalableNesting(vvdecSEI* s, const NalUnitType nalUnitT sei_read_flag(decodedMessageOutputStream, symbol, "sn_all_layers_flag"); sei->snAllLayersFlag = symbol; if (!sei->snAllLayersFlag) { - sei_read_uvlc(decodedMessageOutputStream, symbol, "sn_nulayers_minus1"); sei->snNumLayers = symbol+1; + sei_read_uvlc(decodedMessageOutputStream, symbol, "sn_num_layers_minus1"); sei->snNumLayers = symbol+1; + CHECK( sei->snNumLayers > 64, "sn_num_layers_minus1 must be < 64 in vvdecSEIScalableNesting" ); sei->snLayerId[0] = nuhLayerId; for (uint32_t i = 1; i < sei->snNumLayers; i++) { @@ -556,16 +558,18 @@ void SEIReader::xParseSEIScalableNesting(vvdecSEI* s, const NalUnitType nalUnitT } if (sei->snSubpicFlag) { - sei_read_uvlc(decodedMessageOutputStream, symbol, "sn_nusubpics_minus1"); sei->snNumSubpics = symbol + 1; + sei_read_uvlc(decodedMessageOutputStream, symbol, "sn_num_subpics_minus1"); sei->snNumSubpics = symbol + 1; + CHECK( sei->snNumSubpics > 64, "sn_num_subpics_minus1 must be < 64 in vvdecSEIScalableNesting" ); sei_read_uvlc(decodedMessageOutputStream, symbol, "sn_subpic_id_len_minus1"); sei->snSubpicIdLen = symbol + 1; + CHECK( sei->snSubpicIdLen > 16, "sn_subpic_id_len_minus1 must be < 16 in vvdecSEIScalableNesting" ); for (uint32_t i = 0; i < sei->snNumSubpics; i++) { sei_read_code(decodedMessageOutputStream, sei->snSubpicIdLen, symbol, "sn_subpic_id[i]"); sei->snSubpicId[i] = symbol; } } - sei_read_uvlc(decodedMessageOutputStream, symbol, "sn_nuseis_minus1"); sei->snNumSEIs = symbol + 1; - CHECK (sei->snNumSEIs > 64, "The value of sn_nuseis_minus1 shall be in the range of 0 to 63"); + sei_read_uvlc(decodedMessageOutputStream, symbol, "sn_num_seis_minus1"); sei->snNumSEIs = symbol + 1; + CHECK (sei->snNumSEIs > 64, "The value of sn_num_seis_minus1 shall be in the range of 0 to 63"); // byte alignment while( m_pcBitstream->getNumBitsUntilByteAligned() ) @@ -749,6 +753,7 @@ void SEIReader::xParseSEIBufferingPeriod(vvdecSEI* s, uint32_t payloadSize, std: sei_read_code(pDecodedMessageOutputStream, 3, code, "bp_max_sub_layers_minus1"); sei->bpMaxSubLayers = code + 1; + CHECK( sei->bpMaxSubLayers > 7, "bp_max_sub_layers_minus1 must be <= 6 in vvdecSEIBufferingPeriod" ); if (sei->bpMaxSubLayers - 1 > 0) { sei_read_flag(pDecodedMessageOutputStream, code, "cpb_removal_delay_deltas_present_flag"); @@ -760,8 +765,8 @@ void SEIReader::xParseSEIBufferingPeriod(vvdecSEI* s, uint32_t payloadSize, std: } if (sei->cpbRemovalDelayDeltasPresentFlag) { - sei_read_uvlc( pDecodedMessageOutputStream, code, "nucpb_removal_delay_deltas_minus1" ); sei->numCpbRemovalDelayDeltas = code + 1; - CHECK( sei->numCpbRemovalDelayDeltas > 14, "nucpb_removal_delay_deltas_minus1 must be <= 13 in vvdecSEIBufferingPeriod" ); + sei_read_uvlc( pDecodedMessageOutputStream, code, "num_cpb_removal_delay_deltas_minus1" ); sei->numCpbRemovalDelayDeltas = code + 1; + CHECK( sei->numCpbRemovalDelayDeltas > 14, "num_cpb_removal_delay_deltas_minus1 must be <= 13 in vvdecSEIBufferingPeriod" ); for( i = 0; i < sei->numCpbRemovalDelayDeltas; i ++ ) { @@ -770,6 +775,7 @@ void SEIReader::xParseSEIBufferingPeriod(vvdecSEI* s, uint32_t payloadSize, std: } } sei_read_uvlc( pDecodedMessageOutputStream, code, "bp_cpb_cnt_minus1" ); sei->bpCpbCnt = code + 1; + CHECK( sei->bpCpbCnt > 32, "bp_cpb_cnt_minus1 must be <= 31 in vvdecSEIBufferingPeriod" ); if (sei->bpMaxSubLayers - 1 > 0) { sei_read_flag(pDecodedMessageOutputStream, code, "bp_sublayer_initial_cpb_removal_delay_present_flag"); @@ -932,8 +938,9 @@ void SEIReader::xParseSEIPictureTiming(vvdecSEI* s, uint32_t payloadSize, const } if( bp.bpDecodingUnitHrdParamsPresentFlag && bp.decodingUnitCpbParamsInPicTimingSeiFlag ) { - sei_read_uvlc( pDecodedMessageOutputStream, symbol, "nudecoding_units_minus1" ); + sei_read_uvlc( pDecodedMessageOutputStream, symbol, "num_decoding_units_minus1" ); sei->numDecodingUnits = symbol+1; + CHECK( sei->numDecodingUnits > 32, "num_decoding_units_minus1 must be <= 31 in vvdecSEIPictureTiming" ); if (sei->numDecodingUnits > 1) { @@ -952,7 +959,7 @@ void SEIReader::xParseSEIPictureTiming(vvdecSEI* s, uint32_t payloadSize, const } for( uint32_t i = 0; i < sei->numDecodingUnits; i ++ ) { - sei_read_uvlc( pDecodedMessageOutputStream, symbol, "nunalus_in_du_minus1[i]" ); + sei_read_uvlc( pDecodedMessageOutputStream, symbol, "num_nalus_in_du_minus1[i]" ); sei->numNalusInDu[i] = symbol+1; if( !sei->duCommonCpbRemovalDelayFlag && i < (sei->numDecodingUnits-1) ) { @@ -1253,11 +1260,11 @@ void SEIReader::xParseSEIFilmGrainCharacteristics(vvdecSEI* s, uint32_t payloadS vvdecCompModel &cm = sei->compModel[c]; if (cm.presentFlag) { - sei_read_code(pDecodedMessageOutputStream, 8, code, "nuintensity_intervals_minus1[c]"); cm.numIntensityIntervals = code + 1; - sei_read_code(pDecodedMessageOutputStream, 3, code, "numodel_values_minus1[c]"); cm.numModelValues = code + 1; + sei_read_code(pDecodedMessageOutputStream, 8, code, "num_intensity_intervals_minus1[c]"); cm.numIntensityIntervals = code + 1; + sei_read_code(pDecodedMessageOutputStream, 3, code, "num_model_values_minus1[c]"); cm.numModelValues = code + 1; - CHECK ( cm.numIntensityIntervals > 256, "nuintensity_intervals_minus1[c] out of range" ); - CHECK ( cm.numModelValues > 6, "numodel_values_minus1[c] out of range" ); + CHECK ( cm.numIntensityIntervals > 256, "num_intensity_intervals_minus1[c] out of range" ); + CHECK ( cm.numModelValues > 6, "num_model_values_minus1[c] out of range" ); for (uint32_t interval = 0; interval < cm.numIntensityIntervals; interval++) { @@ -1410,7 +1417,7 @@ void SEIReader::xParseSEIOmniViewport(vvdecSEI* s, uint32_t payloadSize, std::os { sei_read_flag( pDecodedMessageOutputStream, code, "omni_viewport_persistence_flag" ); sei->omniViewportPersistenceFlag = code; sei_read_code( pDecodedMessageOutputStream, 4, code, "omni_viewport_cnt_minus1" ); sei->omniViewportCnt = code+1; - CHECK( sei->omniViewportCnt > 16, "omni_viewport_cnt_minus1 must be < 16 in vvdecSEIDecodedPictureHash" ); + CHECK( sei->omniViewportCnt > 16, "omni_viewport_cnt_minus1 must be < 16 in vvdecSEIOmniViewport" ); for(uint32_t region=0; region < sei->omniViewportCnt; region++) { @@ -1442,7 +1449,7 @@ void SEIReader::xParseSEIRegionWisePacking(vvdecSEI* s, uint32_t payloadSize, st sei_read_flag( pDecodedMessageOutputStream, val, "rwp_persistence_flag" ); sei->rwpPersistenceFlag = val; sei_read_flag( pDecodedMessageOutputStream, val, "constituent_picture_matching_flag" ); sei->constituentPictureMatchingFlag = val; sei_read_code( pDecodedMessageOutputStream, 5, val, "rwp_reserved_zero_5bits" ); - sei_read_code( pDecodedMessageOutputStream, 8, val, "nupacked_regions" ); sei->numPackedRegions = val; + sei_read_code( pDecodedMessageOutputStream, 8, val, "num_packed_regions" ); sei->numPackedRegions = val; sei_read_code( pDecodedMessageOutputStream, 32, val, "proj_picture_width" ); sei->projPictureWidth = val; sei_read_code( pDecodedMessageOutputStream, 32, val, "proj_picture_height" ); sei->projPictureHeight = val; sei_read_code( pDecodedMessageOutputStream, 16, val, "packed_picture_width" ); sei->packedPictureWidth = val; @@ -1536,14 +1543,14 @@ void SEIReader::xParseSEISubpictureLevelInfo(vvdecSEI* s, uint32_t payloadSize, { sei_read_uvlc(pDecodedMessageOutputStream, val, "sli_nusubpics_minus1"); sei->numSubpics = val + 1; CHECK( sei->numSubpics > 64, "sli_nusubpics_minus1 must be < 64 in vvdecSEISubpictureLevelInfo" ); - sei_read_code(pDecodedMessageOutputStream, 3, val, "sli_max_sublayers_minus1" ); sei->sliMaxSublayers = val + 1; - CHECK( sei->sliMaxSublayers > 6, "sli_max_sublayers_minus1 must be < 6 in vvdecSEISubpictureLevelInfo" ); + } + sei_read_code(pDecodedMessageOutputStream, 3, val, "sli_max_sublayers_minus1" ); sei->sliMaxSublayers = val + 1; + CHECK( sei->sliMaxSublayers > 6, "sli_max_sublayers_minus1 must be < 6 in vvdecSEISubpictureLevelInfo" ); - sei_read_flag(pDecodedMessageOutputStream, val, "sli_sublayer_info_present_flag"); sei->sliSublayerInfoPresentFlag = val; - while (!isByteAligned()) - { - sei_read_flag( pDecodedMessageOutputStream, val, "sli_alignment_zero_bit" ); CHECK (val != 0, "sli_alignment_zero_bit not equal to zero" ); - } + sei_read_flag(pDecodedMessageOutputStream, val, "sli_sublayer_info_present_flag"); sei->sliSublayerInfoPresentFlag = val; + while (!isByteAligned()) + { + sei_read_flag( pDecodedMessageOutputStream, val, "sli_alignment_zero_bit" ); CHECK (val != 0, "sli_alignment_zero_bit not equal to zero" ); } // sei parameters initialization diff --git a/source/Lib/Utilities/ThreadPool.cpp b/source/Lib/Utilities/ThreadPool.cpp index a9021c6e..f0867473 100644 --- a/source/Lib/Utilities/ThreadPool.cpp +++ b/source/Lib/Utilities/ThreadPool.cpp @@ -357,7 +357,7 @@ bool ThreadPool::processTask( int threadId, ThreadPool::Slot& task ) } if( task.counter != nullptr ) { - --(*task.counter); + task.counter->decrement_nothrow(); } } catch( ... ) @@ -420,14 +420,7 @@ void ThreadPool::handleTaskException( const std::exception_ptr e, Barrier* done, if( counter != nullptr ) { counter->setException( e ); - // Barrier::unlock() in the decrement operator throws, when the counter reaches zero, so we catch it here - try - { - --( *counter ); - } - catch( ... ) - { - } + counter->decrement_nothrow(); } if( slot_state != nullptr ) diff --git a/source/Lib/Utilities/ThreadPool.h b/source/Lib/Utilities/ThreadPool.h index 46367422..026d108d 100644 --- a/source/Lib/Utilities/ThreadPool.h +++ b/source/Lib/Utilities/ThreadPool.h @@ -87,6 +87,11 @@ struct Barrier m_lockState.store( false ); } + virtual void unlock_nothrow() + { + m_lockState.store( false ); + } + virtual void lock() { checkAndRethrowException(); @@ -240,9 +245,10 @@ struct WaitCounter int operator--() { std::unique_lock l( m_lock ); - const unsigned int new_count = --m_count; - if( new_count == 0 ) + const int new_count = --m_count; + if( new_count <= 0 ) { + CHECK( new_count < 0, "WaitCounter is negative, this is probably a bug." ); m_cond.notify_all(); // we can notify before unlocking the barrier, because wait() and wait_nothrow() wait for m_count and not for m_done m_done.unlock(); } @@ -250,24 +256,38 @@ struct WaitCounter return new_count; } + int decrement_nothrow() + { + std::unique_lock l( m_lock ); + const int new_count = --m_count; + if( new_count <= 0 ) + { + CHECK_WARN( new_count < 0, "WaitCounter is negative, this is probably a bug." ); + m_cond.notify_all(); // we can notify before unlocking the barrier, because wait() and wait_nothrow() wait for m_count and not for m_done + m_done.unlock_nothrow(); + } + l.unlock(); // unlock mutex after done-barrier to prevent race between barrier and counter + return new_count; + } + bool isBlocked() const { std::lock_guard l( m_lock ); m_done.checkAndRethrowException(); - return 0 != m_count; + return m_count > 0; } void wait() const { std::unique_lock l( m_lock ); - m_cond.wait( l, [this] { return m_count == 0 || m_done.hasException(); } ); + m_cond.wait( l, [this] { return m_count <= 0 || m_done.hasException(); } ); m_done.checkAndRethrowException(); } void wait_nothrow() const { std::unique_lock l( m_lock ); - m_cond.wait( l, [this] { return m_count == 0; } ); + m_cond.wait( l, [this] { return m_count <= 0; } ); } void setException( std::exception_ptr e ) @@ -293,9 +313,11 @@ struct WaitCounter const Barrier* donePtr() const { return &m_done; } private: - mutable std::condition_variable m_cond; + mutable std::condition_variable m_cond; // We can't use a BlockingBarrier but need a separate condition variable here, + // so we can wait for m_count to reach zero (all tasks have been cleared from + // the thread pool) even if the exception flag is set. mutable std::mutex m_lock; - unsigned int m_count = 0; + int m_count = 0; Barrier m_done{ false }; }; diff --git a/tests/fuzzing/CMakeLists.txt b/tests/fuzzing/CMakeLists.txt new file mode 100644 index 00000000..8b85b76d --- /dev/null +++ b/tests/fuzzing/CMakeLists.txt @@ -0,0 +1,42 @@ +# executable +set( EXE_NAME vvdec_fuzzer ) + +# get source files +file( GLOB SRC_FILES CONFIGURE_DEPENDS "*.cpp" ) + +# get include files +file( GLOB INC_FILES CONFIGURE_DEPENDS "*.h" ) + +if( VVDEC_ENABLE_TRACING ) + add_compile_definitions( ENABLE_TRACING ) +endif() + +# add executable +add_executable( ${EXE_NAME} ${SRC_FILES} ${INC_FILES} ${RESOURCE_FILE} ) +set_target_properties( ${EXE_NAME} PROPERTIES RELEASE_POSTFIX "${CMAKE_RELEASE_POSTFIX}" ) +set_target_properties( ${EXE_NAME} PROPERTIES DEBUG_POSTFIX "${CMAKE_DEBUG_POSTFIX}" ) +set_target_properties( ${EXE_NAME} PROPERTIES RELWITHDEBINFO_POSTFIX "${CMAKE_RELWITHDEBINFO_POSTFIX}" ) +set_target_properties( ${EXE_NAME} PROPERTIES MINSIZEREL_POSTFIX "${CMAKE_MINSIZEREL_POSTFIX}" ) +if( VVDEC_INSTALL_RPATH ) + set_target_properties( ${EXE_NAME} PROPERTIES INSTALL_RPATH "${VVDEC_INSTALL_RPATH}" ) +endif() +if( VVDEC_LIBRARY_ONLY ) + set_target_properties( ${EXE_NAME} PROPERTIES EXCLUDE_FROM_ALL TRUE ) +endif() + + +target_compile_options( ${EXE_NAME} PRIVATE -fsanitize=fuzzer -fno-omit-frame-pointer ) +target_link_options( ${EXE_NAME} PRIVATE -fsanitize=fuzzer ) + +target_link_libraries( ${EXE_NAME} vvdec ) + +target_compile_options( ${EXE_NAME} PRIVATE $<$,$>:-Wall -Wextra> + $<$:-Wall -Wextra -fdiagnostics-show-option -Wno-ignored-attributes> + $<$:/W4 /WX>) + +# example: place header files in different folders +source_group( "Header Files" FILES ${INC_FILES} ) +source_group( "Resource Files" FILES ${RESOURCE_FILE} ) + +# set the folder where to place the projects +set_target_properties( ${EXE_NAME} PROPERTIES FOLDER tests ) diff --git a/tests/fuzzing/README_Fuzzing.md b/tests/fuzzing/README_Fuzzing.md new file mode 100644 index 00000000..90bda693 --- /dev/null +++ b/tests/fuzzing/README_Fuzzing.md @@ -0,0 +1,38 @@ +# VVdeC fuzzing environment + +This is a fuzzing environment for the VVdeC video decoder. It is based on clang's libFuzzer (https://llvm.org/docs/LibFuzzer.html). + +## Building the fuzzing target + +Build the fuzzing target using clang. + +```bash +CC=clang CXX=clang++ cmake -S . -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DVVDEC_TOPLEVEL_OUTPUT_DIRS=0 -DVVDEC_FUZZING_BUILD=1 -DVVDEC_USE_ADDRESS_SANITIZER=1 +cmake --build build +``` + +If you get linker errors about missing libclang_rt.*-*.a, you may need to install the clang runtime libraries. On Debian/Ubuntu install the `libclang-rt-dev` package. + +## Prepare the seed corpus + +You need a seed corpus of (preferably short) but differing bitstreams to use as input for the fuzzer. You can create an initial corpus by using some of the bitstreams from the VVdeC test suite. + +For example, you can create a corpus directory and copy the 20 smallest bitstreams from the test suite into it: + +```bash +mkdir fuzz_corpus +ls -S ext/bitstreams/*/*.bit | tail -n 20 | xargs -I {} cp {} fuzz_corpus/ +``` + +## Run the fuzzer + +Run the fuzzer with the corpus and an output directory for crashes: + +This will run the fuzzer with a timeout of 20 seconds per input until it finds a crash. The crashes will be saved in the `fuzz_output` directory. + +```bash +mkdir -p fuzz_output +UBSAN_OPTIONS=halt_on_error=1:abort_on_error=1 ./build/bin/vvdec_fuzzer -timeout=20 -artifact_prefix=fuzz_output/ fuzz_corpus/ +``` + +You can pass some additional VVdeC options to the fuzzer using --option=value similar to the ones used by vvdecapp. Currently supported are `--t=, --p=, --simd=, --v=, --eh=`. \ No newline at end of file diff --git a/tests/fuzzing/vvdec_fuzzer.cpp b/tests/fuzzing/vvdec_fuzzer.cpp new file mode 100644 index 00000000..cf7d4abb --- /dev/null +++ b/tests/fuzzing/vvdec_fuzzer.cpp @@ -0,0 +1,153 @@ +/* ----------------------------------------------------------------------------- +The copyright in this software is being made available under the Clear BSD +License, included below. No patent rights, trademark rights and/or +other Intellectual Property Rights other than the copyrights concerning +the Software are granted under this license. + +The Clear BSD License + +Copyright (c) 2018-2026, Fraunhofer-Gesellschaft zur Förderung der angewandten Forschung e.V. & The VVdeC Authors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted (subject to the limitations in the disclaimer below) 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 copyright holder nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY +THIS LICENSE. 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. + + +------------------------------------------------------------------------------------------- */ + +#include +#include +#include +#include +#include +#include + +#include + +static vvdecParams g_params; + +#define COMPARE_ARG( ARG_STR ) ( currArg.compare( 0, sizeof( ARG_STR ) - 1, ARG_STR ) == 0 && currArg[sizeof( ARG_STR ) - 1] == '=' ) + +extern "C" int LLVMFuzzerInitialize( int* argc, char*** argv ) +{ + vvdec_params_default( &g_params ); + g_params.threads = std::min( 8u, std::thread::hardware_concurrency() ); // limit default to 8 threads + g_params.logLevel = VVDEC_SILENT; + + if( argc && argv ) + { + for( int i = 1; i < *argc; ++i ) + { + std::string currArg( ( *argv )[i] ); + + if( COMPARE_ARG( "--t" ) ) + { + g_params.threads = std::atoi( currArg.c_str() + 4 ); + } + else if( COMPARE_ARG( "--p" ) ) + { + g_params.parseDelay = std::atoi( currArg.c_str() + 4 ); + } + else if( COMPARE_ARG( "--simd" ) ) + { + g_params.simd = static_cast( std::atoi( currArg.c_str() + 7 ) + 1 ); + } + else if( COMPARE_ARG( "--v" ) ) + { + g_params.logLevel = static_cast( std::atoi( currArg.c_str() + 4 ) ); + } + else if( COMPARE_ARG( "--eh" ) ) + { + g_params.errHandlingFlags = static_cast( std::atoi( currArg.c_str() + 5 ) ); + } + else if( currArg.compare( 0, 2, "--" ) == 0 ) + { + std::cerr << "Unknown VVdeC argument: " << currArg << std::endl; + return -1; + } + } + } + return 0; +} + +extern "C" int LLVMFuzzerTestOneInput( const uint8_t* data, size_t size ) +{ + auto* dec = vvdec_decoder_open( &g_params ); + if( !dec ) + { + return -1; + } + if( g_params.logLevel >= VVDEC_INFO ) + { + std::cerr << vvdec_get_dec_information( dec ) << std::endl; + } + + vvdecAccessUnit au; + vvdec_accessUnit_default( &au ); + + // we can pass the whole bitstream to the decoder, since the decoder will process the NAL units individually anyways + std::vector payload( data, data + size ); + au.payload = payload.data(); + au.payloadSize = static_cast( size ); + au.payloadUsedSize = static_cast( size ); + + vvdecFrame* frame = nullptr; + int numFailed = 0; + int ret = vvdec_decode( dec, &au, &frame ); + while( ret == VVDEC_OK || ret == VVDEC_TRY_AGAIN || ret == VVDEC_ERR_DEC_INPUT ) + { + if( frame ) + { + vvdec_frame_unref( dec, frame ); + } + + frame = nullptr; + ret = vvdec_flush( dec, &frame ); + if( ret == VVDEC_OK || frame ) + { + numFailed = 0; + } + else + { + ++numFailed; + if( numFailed > 32 + std::max( g_params.threads * 1.5, g_params.parseDelay ) ) + { + std::cerr << "ERROR: probably infinite loop of vvdec_flush() calls. " << numFailed << " successive flush calles returned an error." << std::endl; + abort(); + } + } + } + if( frame ) + { + vvdec_frame_unref( dec, frame ); + } + + vvdec_decoder_close( dec ); + + return 0; // Values other than 0 and -1 are reserved for future use. +}