YUV視頻格式到RGB32格式轉換的速度優化 中篇
[email protected] 2007.11.05
tag: YUV,YCbCr,YUV到RGB顏色轉換,YUV解碼,VFW,視頻,MMX,SSE,多核優化
摘要: 我們得到的很多視頻數據(一些解碼器的輸出或者攝像頭的輸出等)都使用了一種
叫YUV的顏色格式;本文介紹了常見的YUV視頻格式(YUY2/YVYU/UYVY/I420/YV12等)到
RGB顏色格式的轉換,並嘗試對轉化的速度進行優化;
全文 分爲:
《上篇》文章首先介紹了YUV顏色格式,並介紹了YUV顏色格式和RGB顏色格式之
間的相互轉換;然後重點介紹了YUYV視頻格式到RGB32格式的轉化,並嘗試進行了一
些速度優化;
《中篇》嘗試使用MMX/SSE指令對前面實現的解碼器核心進行速度優化;然
後簡要介紹了一個使用這類CPU特殊指令時的代碼框架,使得解碼程序能夠根據運行時
的CPU指令支持情況動態調用最佳的實現代碼;並最終提供一個多核並行的優化版本;
《下篇》介紹YUV類型的其他種類繁多的視頻數據編碼格式;並將前面實現的解碼
器核心(在不損失代碼速度的前提下)進行必要的修改,使之適用於這些YUV視頻格式
的解碼;
(2007.11.25 優化了一下DECODE_YUYV_SSE,使用預讀優化;調整了一下MMX指令的定義方式,結構更好一些)
(2007.11.13 修正了一下顏色轉換公式中的係數)
(2007.11.05 修改函數DECODE_YUYV_AutoEx中的一個bug)
正文:
代碼使用C++,編譯器:VC2005
涉及到彙編的時候假定爲x86平臺;
現在的高清視頻幀尺寸越來越大,所以本文測試的圖片大小將使用1024x576和
1920x1080兩種常見的幀尺寸來測試解碼器速度;
測試平臺:(CPU:AMD64x2 4200+(2.37G); 內存:DDR2 677(雙通道); 編譯器:VC2005)
測試平臺:(CPU:Intel Core2 4400(2.00G);內存:DDR2 667(雙通道); 編譯器:VC2005)
請先參看《YUV視頻格式到RGB32格式轉換的速度優化 上篇》,
本文章將繼續成倍的提高其速度!
A:使用MMX指令來繼續優化YUYV視頻格式到RGB32格式的轉換函數
現在絕大多數的x86CPU都支持MMX指令,它是一種單指令多數據流的指令集(SIMD);
MMX指令能夠同時操作8個byte或者4個short等; 在YUV轉換到RGB的運算中,爲了保持精
度選用一次運算4個short數據類型;那外考慮如果在一個寄存器中保存YUYV四個數據,
整個運算寫起來會較麻煩,且算法受到Y、U、V三個顏色存放位置的影響嚴重;而且考
慮到除了packed模式外很多YUV視頻數據都爲planar模式,所以想把Y、U、V先分離到
各自的寄存器再運算,看起來舒服得多,那麼運算核心將用一個寄存器保存4個U,一
個寄存器保存4個V,對應8個Y,也就是說核心轉換代碼運行一遍可以輸出8個RGB32比
特顏色;
所以我們先來實現一個通用的MMX實現的轉換核心:
我們約定輸入:
mm0 = Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0
mm1 = 00 u3 00 u2 00 u1 00 u0
mm2 = 00 v3 00 v2 00 v1 00 v0
通過edx指向的內存輸出:
[edx -- edx+8*4]
由於主要的計算使用short精度,那些係數就不能使用16位的定點數了;爲了不超出short的
範圍可以使用13位的定點數(再大就會溢出了);
MMX實現的轉換核心(使用了宏來實現):YUV422ToRGB32_MMX:
(係數的由來/數據在MMX寄存器的大致流動都有較詳細的註釋;
如果有人進一步改進了這個核心,請告訴我:)
const UInt64 csMMX_16_b = 0x1010101010101010; // byte{16,16,16,16,16,16,16,16}
const UInt64 csMMX_128_w = 0x0080008000800080; //short{ 128, 128, 128, 128}
const UInt64 csMMX_0x00FF_w = 0x00FF00FF00FF00FF; //掩碼
const UInt64 csMMX_Y_coeff_w = 0x2543254325432543; //short{ 9539, 9539, 9539, 9539} =1.164383*(1<<13)
const UInt64 csMMX_U_blue_w = 0x408D408D408D408D; //short{16525,16525,16525,16525} =2.017232*(1<<13)
const UInt64 csMMX_U_green_w = 0xF377F377F377F377; //short{-3209,-3209,-3209,-3209} =(-0.391762)*(1<<13)
const UInt64 csMMX_V_green_w = 0xE5FCE5FCE5FCE5FC; //short{-6660,-6660,-6660,-6660} =(-0.812968)*(1<<13)
const UInt64 csMMX_V_red_w = 0x3313331333133313; //short{13075,13075,13075,13075} =1.596027*(1<<13)
//一次處理8個顏色輸出
#define YUV422ToRGB32_MMX(out_RGB_reg,WriteCode) /
/*input : mm0 = Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 */ /
/* mm1 = 00 u3 00 u2 00 u1 00 u0 */ /
/* mm2 = 00 v3 00 v2 00 v1 00 v0 */ /
/*output : [out_RGB_reg -- out_RGB_reg+8*4] */ /
/
asm psubusb mm0,csMMX_16_b /* mm0 : Y -= 16 */ /
asm psubsw mm1,csMMX_128_w /* mm1 : u -= 128 */ /
asm movq mm7,mm0 /
asm psubsw mm2,csMMX_128_w /* mm2 : v -= 128 */ /
asm pand mm0,csMMX_0x00FF_w /* mm0 = 00 Y6 00 Y4 00 Y2 00 Y0 */ /
asm psllw mm1,3 /* mm1 : u *= 8 */ /
asm psllw mm2,3 /* mm2 : v *= 8 */ /
asm psrlw mm7,8 /* mm7 = 00 Y7 00 Y5 00 Y3 00 Y1 */ /
asm movq mm3,mm1 /
asm movq mm4,mm2 /
/
asm pmulhw mm1,csMMX_U_green_w /* mm1 = u * U_green */ /
asm psllw mm0,3 /* y*=8 */ /
asm pmulhw mm2,csMMX_V_green_w /* mm2 = v * V_green */ /
asm psllw mm7,3 /* y*=8 */ /
asm pmulhw mm3,csMMX_U_blue_w /
asm paddsw mm1,mm2 /
asm pmulhw mm4,csMMX_V_red_w /
asm movq mm2,mm3 /
asm pmulhw mm0,csMMX_Y_coeff_w /
asm movq mm6,mm4 /
asm pmulhw mm7,csMMX_Y_coeff_w /
asm movq mm5,mm1 /
asm paddsw mm3,mm0 /* mm3 = B6 B4 B2 B0 */ /
asm paddsw mm2,mm7 /* mm2 = B7 B5 B3 B1 */ /
asm paddsw mm4,mm0 /* mm4 = R6 R4 R2 R0 */ /
asm paddsw mm6,mm7 /* mm6 = R7 R5 R3 R1 */ /
asm paddsw mm1,mm0 /* mm1 = G6 G4 G2 G0 */ /
asm paddsw mm5,mm7 /* mm5 = G7 G5 G3 G1 */ /
/
asm packuswb mm3,mm4 /* mm3 = R6 R4 R2 R0 B6 B4 B2 B0 to [0-255] */ /
asm packuswb mm2,mm6 /* mm2 = R7 R5 R3 R1 B7 B5 B3 B1 to [0-255] */ /
asm packuswb mm5,mm1 /* mm5 = G6 G4 G2 G0 G7 G5 G3 G1 to [0-255] */ /
asm movq mm4,mm3 /
asm punpcklbw mm3,mm2 /* mm3 = B7 B6 B5 B4 B3 B2 B1 B0 */ /
asm punpckldq mm1,mm5 /* mm1 = G7 G5 G3 G1 xx xx xx xx */ /
asm punpckhbw mm4,mm2 /* mm4 = R7 R6 R5 R4 R3 R2 R1 R0 */ /
asm punpckhbw mm5,mm1 /* mm5 = G7 G6 G5 G4 G3 G2 G1 G0 */ /
/
/*out*/ /
asm pcmpeqb mm2,mm2 /* mm2 = FF FF FF FF FF FF FF FF */ /
/
asm movq mm0,mm3 /
asm movq mm7,mm4 /
asm punpcklbw mm0,mm5 /* mm0 = G3 B3 G2 B2 G1 B1 G0 B0 */ /
asm punpcklbw mm7,mm2 /* mm7 = FF R3 FF R2 FF R1 FF R0 */ /
asm movq mm1,mm0 /
asm movq mm6,mm3 /
asm punpcklwd mm0,mm7 /* mm0 = FF R1 G1 B1 FF R0 G0 B0 */ /
asm punpckhwd mm1,mm7 /* mm1 = FF R3 G3 B3 FF R2 G2 B2 */ /
asm WriteCode [out_RGB_reg],mm0 /
asm movq mm7,mm4 /
asm punpckhbw mm6,mm5 /* mm6 = G7 B7 G6 B6 G5 B5 G4 B4 */ /
asm WriteCode [out_RGB_reg+8],mm1 /
asm punpckhbw mm7,mm2 /* mm7 = FF R7 FF R6 FF R5 FF R4 */ /
asm movq mm0,mm6 /
asm punpcklwd mm6,mm7 /* mm6 = FF R5 G5 B5 FF R4 G4 B4 */ /
asm punpckhwd mm0,mm7 /* mm0 = FF R7 G7 B7 FF R6 G6 B6 */ /
asm WriteCode [out_RGB_reg+8*2],mm6 /
asm WriteCode [out_RGB_reg+8*3],mm0
YUV視頻格式到RGB32格式的轉換函數,MMX指令實現版本
asm movq mm0,[in_yuv_reg ] /*mm0=V1 Y3 U1 Y2 V0 Y1 U0 Y0 */ /
asm movq mm4,[in_yuv_reg+8] /*mm4=V3 Y7 U3 Y6 V2 Y5 U2 Y4 */ /
asm movq mm1,mm0 /
asm movq mm5,mm4 /
asm psrlw mm1,8 /*mm1=00 V1 00 U1 00 V0 00 U0 */ /
asm psrlw mm5,8 /*mm5=00 V3 00 U3 00 V2 00 U2 */ /
asm pand mm0,csMMX_0x00FF_w /*mm0=00 Y3 00 Y2 00 Y1 00 Y0 */ /
asm pand mm4,csMMX_0x00FF_w /*mm4=00 Y7 00 Y6 00 Y5 00 Y4 */ /
asm packuswb mm1,mm5 /*mm1=V3 U3 V2 U2 V1 U1 V0 U0 */ /
asm movq mm2,mm1 /
asm packuswb mm0,mm4 /*mm0=Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 */ /
asm psllw mm1,8 /*mm1=U3 00 U2 00 U1 00 U0 00 */ /
asm psrlw mm2,8 /*mm2=00 V3 00 V2 00 V1 00 V0 */ /
asm psrlw mm1,8 /*mm1=00 U3 00 U2 00 U1 00 U0 */
void DECODE_YUYV_MMX_line(TARGB32* pDstLine,const TUInt8* pYUYV,long width)
{
long expand8_width=(width>>3)<<3;
if (expand8_width>0)
{
asm
{
mov ecx,expand8_width
mov eax,pYUYV
mov edx,pDstLine
lea eax,[eax+ecx*2]
lea edx,[edx+ecx*4]
neg ecx
loop_beign:
YUYV_Loader_MMX(eax+ecx*2)
YUV422ToRGB32_MMX(edx+ecx*4,movq)
add ecx,8
jnz loop_beign
mov pYUYV,eax
mov pDstLine,edx
}
}
//處理邊界
DECODE_YUYV_Common_line(pDstLine,pYUYV,width-expand8_width);
}
void DECODE_YUYV_MMX(const TUInt8* pYUYV,const TPicRegion& DstPic)
{
assert((DstPic.width & 1)==0);
long YUV_byte_width=(DstPic.width>>1)<<2;
TARGB32* pDstLine=DstPic.pdata;
for (long y=0;y<DstPic.height;++y)
{
DECODE_YUYV_MMX_line(pDstLine,pYUYV,DstPic.width);
pYUYV+=YUV_byte_width;
((TUInt8*&)pDstLine)+=DstPic.byte_width;
}
asm emms
}
速度測試:
////////////////////////////////////////////////////////////////////////////////
//==============================================================================
// | 1024x576 | 1920x1080 |
//------------------------------------------------------------------------------
// | AMD64x2 | Core2 | AMD64x2 | Core2 |
//------------------------------------------------------------------------------
//DECODE_YUYV_MMX 585.4 FPS 569.8 FPS 169.8 FPS 160.4 FPS
////////////////////////////////////////////////////////////////////////////////
B.使用SSE中的軟件預取和禁止寫緩存來改進MMX版本
這裏的改動其實很小,只是把YUV422ToRGB32_MMX中顏色數據保存操作
movq [mem],mmx_reg 修改成 movntq [mem],mmx_reg
然後再處理完成後調用sfence緩存刷新指令。
完整代碼如下:
#define YUV422ToRGB32_SSE(out_RGB_reg) YUV422ToRGB32_MMX(out_RGB_reg,movntq)
void DECODE_YUYV_SSE_line(TARGB32* pDstLine,const TUInt8* pYUYV,long width)
{
long expand8_width=(width>>3)<<3;
if (expand8_width>0)
{
asm
{
mov ecx,expand8_width
mov eax,pYUYV
mov edx,pDstLine
lea eax,[eax+ecx*2]
lea edx,[edx+ecx*4]
neg ecx
loop_beign:
YUYV_Loader_MMX(eax+ecx*2)
prefetchnta [eax+ecx*2+64*4] //預讀
YUV422ToRGB32_SSE(edx+ecx*4)
add ecx,8
jnz loop_beign
mov pYUYV,eax
mov pDstLine,edx
}
}
//處理邊界
DECODE_YUYV_Common_line(pDstLine,pYUYV,width-expand8_width);
}
void DECODE_YUYV_SSE(const TUInt8* pYUYV,const TPicRegion& DstPic)
{
assert((DstPic.width & 1)==0);
long YUV_byte_width=(DstPic.width>>1)<<2;
TARGB32* pDstLine=DstPic.pdata;
for (long y=0;y<DstPic.height;++y)
{
DECODE_YUYV_SSE_line(pDstLine,pYUYV,DstPic.width);
pYUYV+=YUV_byte_width;
((TUInt8*&)pDstLine)+=DstPic.byte_width;
}
asm sfence
asm emms
}
速度測試:
////////////////////////////////////////////////////////////////////////////////
//==============================================================================
// | 1024x576 | 1920x1080 |
//------------------------------------------------------------------------------
// | AMD64x2 | Core2 | AMD64x2 | Core2 |
//------------------------------------------------------------------------------
//DECODE_YUYV_SSE 770.3 FPS 741.9 FPS 220.0 FPS 209.7 FPS
////////////////////////////////////////////////////////////////////////////////
C.使用CPU特殊指令的一般框架
我的blog文章中,經常使用MMX/SSE等特殊指令,都是隻給出代碼實現;它們離實際項目代
碼還有一點距離;在實際的項目中需要一種機制使得開發的軟件能夠根據運行的CPU的特性動
態的決定調用最優化的實現版本;
在x86CPU上可以使用CPUID指令來得到各種關於當前CPU的特性,包括製造商、CPU家族號、
緩存信息、是否支持MMX/SSE/SSE2指令集 等等;
要使用CPUID指令,首先應該判斷CPU是否支持該指令;方法是判斷EFLAGS寄存器的第21位
是否可以改寫;如果可以改寫,那麼說明這塊CPU支持CPUID指令; 函數實現如下:
{
long int CPUIDInfOld=0;
long int CPUIDInfNew=0;
try
{
asm
{
pushfd // 保存原 EFLAGS
pop eax
mov edx,eax
mov CPUIDInfOld,eax //
xor eax,00200000h // 改寫 第21位
push eax
popfd // 改寫 EFLAGS
pushfd // 保存新 EFLAGS
pop eax
mov CPUIDInfNew,eax
push edx // 恢復原 EFLAGS
popfd
}
return (CPUIDInfOld!=CPUIDInfNew); // EFLAGS 第21位 可以改寫
}
catch(...)
{
return false;
}
}
//那麼判斷CPU是否支持MMX指令的函數如下:
{
if (!_CPUSupportCPUID())
return false;
long int MMXInf=0;
try
{
asm
{
push ebx
mov eax,1
cpuid
mov MMXInf,edx
pop ebx
}
MMXInf=MMXInf & (1 << 23); //檢測edx第23位
return (MMXInf==(1 << 23));
}
catch(...)
{
return false;
}
}
//判斷CPU是否支持SSE指令的函數如下:
{
if (!_CPUSupportCPUID())
return false;
long int SSEInf=0;
try
{
asm
{
push ebx
mov eax,1
cpuid
mov SSEInf,edx
pop ebx
}
SSEInf=SSEInf & (1 << 25); //檢測edx第25位
return (SSEInf==(1 << 25));
}
catch(...)
{
return false;
}
}
// 由於SSE的寄存器是比較後期加入的,某些較老的操作系統可能不支持這些寄存器
//的任務切換保存;可以用觸發異常的方式來判斷操作系統是否支持SSE;
{
//觸發異常來判斷
try
{
asm
{
//movups xmm0,xmm0
asm _emit 0x0F asm _emit 0x10 asm _emit 0xC0
}
return true;
}
catch(...)
{
return false;
}
}
//定義常量,用以在程序作爲分支條件
const bool _IS_SSE_ACTIVE=_CPUSupportSSE() && _SystemSupportSSE();
D.根據運行的CPU支持的指令集來動態調用不同的解碼器實現
const TDECODE_YUYV_line_proc DECODE_YUYV_Auto_line=
( _IS_MMX_ACTIVE ? (_IS_SSE_ACTIVE ? DECODE_YUYV_SSE_line : DECODE_YUYV_MMX_line) : DECODE_YUYV_Common_line );
__forceinline void DECODE_filish()
{
if (_IS_MMX_ACTIVE)
{
if (_IS_SSE_ACTIVE) { asm sfence }
asm emms
}
}
void DECODE_YUYV_Auto(const TUInt8* pYUYV,const TPicRegion& DstPic)
{
assert((DstPic.width & 1)==0);
long YUV_byte_width=(DstPic.width>>1)<<2;
TARGB32* pDstLine=DstPic.pdata;
for (long y=0;y<DstPic.height;++y)
{
DECODE_YUYV_Auto_line(pDstLine,pYUYV,DstPic.width);
pYUYV+=YUV_byte_width;
((TUInt8*&)pDstLine)+=DstPic.byte_width;
}
DECODE_filish();
}
在我的兩臺測試電腦上速度同DECODE_YUYV_SSE,因爲它們都支持MMX和SSE;
E.YUYV視頻格式解碼器的並行化實現
這個比較簡單,將圖像分爲多個塊交給多個CPU同時執行就可以了;代碼如下:
( 這裏利用CWorkThreadPool類來並行執行任務; 參見我的Blog文
章《並行計算簡介和多核CPU編程Demo》,裏面有CWorkThreadPool類的完整源代碼)
struct TDECODE_YUYV_Parallel_WorkData
{
const TUInt8* pYUYV;
TPicRegion DstPic;
};
void DECODE_YUYV_Parallel_callback(void* wd)
{
TDECODE_YUYV_Parallel_WorkData* WorkData=(TDECODE_YUYV_Parallel_WorkData*)wd;
DECODE_YUYV_Auto(WorkData->pYUYV,WorkData->DstPic);
}
void DECODE_YUYV_Parallel(const TUInt8* pYUYV,const TPicRegion& DstPic)
{
long work_count=CWorkThreadPool::best_work_count();
std::vector<TDECODE_YUYV_Parallel_WorkData> work_list(work_count);
std::vector<TDECODE_YUYV_Parallel_WorkData*> pwork_list(work_count);
long cheight=DstPic.height / work_count;
for (long i=0;i<work_count;++i)
{
work_list[i].pYUYV=pYUYV+i*cheight*(DstPic.width*2);
work_list[i].DstPic.pdata=DstPic.pixel_pos(0,cheight*i);
work_list[i].DstPic.byte_width=DstPic.byte_width;
work_list[i].DstPic.width=DstPic.width;
work_list[i].DstPic.height=cheight;
pwork_list[i]=&work_list[i];
}
work_list[work_count-1].DstPic.height=DstPic.height-cheight*(work_count-1);
CWorkThreadPool::work_execute(DECODE_YUYV_Parallel_callback,(void**)&pwork_list[0],work_count);
}
速度測試:
////////////////////////////////////////////////////////////////////////////////
//==============================================================================
// | 1024x576 | 1920x1080 |
//------------------------------------------------------------------------------
// | AMD64x2 | Core2 | AMD64x2 | Core2 |
//------------------------------------------------------------------------------
//DECODE_YUYV_Parallel 1433.9 FPS 1417.1 FPS 414.1 FPS 286.3 FPS
////////////////////////////////////////////////////////////////////////////////
F.另一種更靈活的任務分配方案
我的Blog文章中,涉及到並行的時候,一般都是前面那種簡單的平均任務分配模式;
這裏再實現一種複雜一點的分配方案:線程執行完自己分配的任務後,嘗試幫助其它
線程執行沒有完成的任務;(以單行爲最小可分配任務粒度); 這有一個優點,就是
在多任務環境下,能夠更好地利用全部的CPU資源; 實現如下:
{
//任務領取
if ((*Lock)!=0) return;
long lock_value=InterlockedIncrement(Lock);//也可以用帶lock前綴的inc指令來代替這個windows調用
//警告: 在以後更多個核的電腦上,這裏的lock造成的潛在衝突沒有測試過
if (lock_value>=2) return;
//lock_value==1時,任務領取成功
//執行任務
DECODE_YUYV_Auto_line(pDstLine,pYUYV,width);
}
__forceinline void DECODE_YUYV_AutoEx(const TUInt8* pYUYV,const TPicRegion& DstPic,volatile long* LockList,long begin_y0)
{
assert((DstPic.width & 1)==0);
long YUV_byte_width=(DstPic.width>>1)<<2;
TARGB32* pDstLine=DstPic.pdata;
long y;
const TUInt8* pYUYV_b=pYUYV+(YUV_byte_width*begin_y0);
TARGB32* pDstLine_b=(TARGB32*)(((TUInt8*)DstPic.pdata)+(DstPic.byte_width*begin_y0));
for (y=begin_y0;y<DstPic.height;++y)
{
DECODE_YUYV_AutoLock_line(pDstLine_b,pYUYV_b,DstPic.width,&LockList[y]);
pYUYV_b+=YUV_byte_width;
((TUInt8*&)pDstLine_b)+=DstPic.byte_width;
}
for (y=0;y<begin_y0;++y)
{
DECODE_YUYV_AutoLock_line(pDstLine,pYUYV,DstPic.width,&LockList[y]);
pYUYV+=YUV_byte_width;
((TUInt8*&)pDstLine)+=DstPic.byte_width;
}
DECODE_filish();
}
struct TDECODE_YUYV_ParallelEx_WorkData
{
const TUInt8* pYUYV;
TPicRegion DstPic;
long* LockList;
long begin_y0;
};
void DECODE_YUYV_ParallelEx_callback(void* wd)
{
TDECODE_YUYV_ParallelEx_WorkData* WorkData=(TDECODE_YUYV_ParallelEx_WorkData*)wd;
DECODE_YUYV_AutoEx(WorkData->pYUYV,WorkData->DstPic,(volatile long*)WorkData->LockList,WorkData->begin_y0);
}
void DECODE_YUYV_ParallelEx(const TUInt8* pYUYV,const TPicRegion& DstPic)
{
long work_count=CWorkThreadPool::best_work_count();
std::vector<TDECODE_YUYV_ParallelEx_WorkData> work_list(work_count);
std::vector<TDECODE_YUYV_ParallelEx_WorkData*> pwork_list(work_count);
std::vector<long> lock_list(DstPic.height);
for (long y=0;y<DstPic.height;++y)
lock_list[y]=0;
long cheight=DstPic.height / work_count;
for (long i=0;i<work_count;++i)
{
work_list[i].pYUYV=pYUYV;
work_list[i].DstPic=DstPic;
work_list[i].begin_y0=i*cheight;
work_list[i].LockList=&lock_list[0];
pwork_list[i]=&work_list[i];
}
CWorkThreadPool::work_execute(DECODE_YUYV_ParallelEx_callback,(void**)&pwork_list[0],work_count);
}
速度測試:
////////////////////////////////////////////////////////////////////////////////
//==============================================================================
// | 1024x576 | 1920x1080 |
//------------------------------------------------------------------------------
// | AMD64x2 | Core2 | AMD64x2 | Core2 |
//------------------------------------------------------------------------------
//DECODE_YUYV_ParallelEx 1387.5 FPS 1359.2 FPS 409.9 FPS 287.4 FPS
////////////////////////////////////////////////////////////////////////////////
G:把測試成績放在一起
////////////////////////////////////////////////////////////////////////////////
//測試平臺:(CPU:AMD64x2 4200+(2.37G); 內存:DDR2 677(雙通道); 編譯器:VC2005)
//測試平臺:(CPU:Intel Core2 4400(2.00G);內存:DDR2 667(雙通道); 編譯器:VC2005)
////////////////////////////////////////////////////////////////////////////////
//==============================================================================
// | 1024x576 | 1920x1080 |
//------------------------------------------------------------------------------
// | AMD64x2 | Core2 | AMD64x2 | Core2 |
//------------------------------------------------------------------------------
//DECODE_YUYV_Float 55.0 FPS 63.7 FPS 15.6 FPS 18.0 FPS
//DECODE_YUYV_Int 137.1 FPS 131.9 FPS 39.0 FPS 37.1 FPS
//DECODE_YUYV_RGBTable 164.8 FPS 152.9 FPS 47.1 FPS 43.7 FPS
//DECODE_YUYV_Table 146.1 FPS 151.3 FPS 41.8 FPS 43.5 FPS
//DECODE_YUYV_TableEx 236.5 FPS 300.5 FPS 68.1 FPS 85.0 FPS
//DECODE_YUYV_Common 250.7 FPS 287.1 FPS 71.9 FPS 80.7 FPS
//DECODE_YUYV_MMX 585.4 FPS 569.8 FPS 169.8 FPS 160.4 FPS
//DECODE_YUYV_SSE 770.3 FPS 741.9 FPS 220.0 FPS 209.7 FPS
//DECODE_YUYV_Auto (同DECODE_YUYV_SSE)
//DECODE_YUYV_Parallel 1433.9 FPS 1417.1 FPS 414.1 FPS 286.3 FPS
//DECODE_YUYV_ParallelEx 1387.5 FPS 1359.2 FPS 409.9 FPS 287.4 FPS
////////////////////////////////////////////////////////////////////////////////
(歡迎提出不足和改進意見;文章下篇將開始支持更多類型的YUV視頻數據格式)