似乎vtm比较后期的版本就不支持wpp了,不知道为啥。。并行的算法vvenc实现的已经很好了,但科研需要,还是决定优化vtm的wpp算法
修改配置
首先修改CMakeLists.txt,把ENABLE_WPP_PARALLELISM打开
set( SET_ENABLE_WPP_PARALLELISM ON CACHE BOOL "Set ENABLE_WPP_PARALLELISM as a compiler flag" )
set( ENABLE_WPP_PARALLELISM ON CACHE BOOL "If SET_ENABLE_WPP_PARALLELISM is on, it will be set to this value" )
编码指令:
./bin/EncoderAppStatic -c ./cfg/encoder_intra_vtm.cfg -q 37 -wdt 1280 -hgt 720 -fr 60 -i ~/yuv/KristenAndSara_1280x720_60.yuv -f 32 --InputBitDepth=8 --OutputBitDepth=8 --InputChromaFormat=420 -o /dev/null -b /dev/null --NumWppThreads=5 --AltDQPCoding
代码
和wpp相关的代码搜索ENABLE_WPP_PARALLELISM就可以找到。接下来介绍几块重要的代码
- 分配任务
schedule(static,1)指给每个线程轮流分配固定的一行ctu(比如有三个线程,那么第一个线程会被分配第0,3,6…行的ctu)?setWppThreadId的代码是这样的:void Scheduler::setSplitThreadId( const int tId ) {g_splitThreadId = tId == CURR_THREAD_ID ? omp_get_thread_num() : tId; },所以每个线程的g_splitThreadId就是它的线程号
#if ENABLE_WPP_PARALLELISM
bool bUseThreads = m_pcCfg->getNumWppThreads() > 1;
if( bUseThreads )
{
CHECK( startCtuTsAddr != 0 || boundingCtuTsAddr != pcPic->cs->pcv->sizeInCtus, "not intended" );
pcPic->cs->allocateVectorsAtPicLevel();
omp_set_num_threads( m_pcCfg->getNumWppThreads() + m_pcCfg->getNumWppExtraLines() );
#pragma omp parallel for schedule(static,1) if(bUseThreads)
for( int ctuTsAddr = startCtuTsAddr; ctuTsAddr < boundingCtuTsAddr; ctuTsAddr += widthInCtus )
{
// wpp thread start
pcPic->scheduler.setWppThreadId();
#if ENABLE_SPLIT_PARALLELISM
pcPic->scheduler.setSplitThreadId( 0 );
#endif
encodeCtus( pcPic, bCompressEntireSlice, bFastDeltaQP, ctuTsAddr, ctuTsAddr + widthInCtus, m_pcLib );
// wpp thread stop
}
}
else
#endif
- 两个ctu行之间的同步
在ctu编码前,EncSlice.cpp中有
pcPic->scheduler.wait( ctuXPosInCtus, ctuYPosInCtus );
scheduler.wait函数是这样的,在Picture.cpp中。正如wpp的定义一样,它要等上一行右上方的ctu编码完毕。这里的m_SyncObjs也是在Picture.cpp中用mutex实现的一个东西
void Scheduler::wait( const int ctuPosX, const int ctuPosY )
{
if( m_numWppThreads == m_numWppDataInstances )
{
if( ctuPosY > 0 && ctuPosX+1 < m_ctuXsize)
{
m_SyncObjs[ctuPosY-1]->wait( ctuPosX+1, ctuPosY-1 );
}
return;
}
m_SyncObjs[ctuPosY]->wait( ctuPosX, ctuPosY );
}
那什么时候wait结束呢?EncSlice.cpp中每个ctu编码结束后,都会调用
pcPic->scheduler.setReady( ctuXPosInCtus, ctuYPosInCtus );