http://blog.csdn.net/housisong/archive/2007/11/05/1866970.aspx HouSisong@GMail.com 2007.11.05 由于主要的计算使用short精度,那些系数就不能使用16位的定点数了;为了不超出short的范围可以使用13位的定点数(再大就会溢出了); MMX实现的转换核心(使用了宏来实现):YUV422ToRGB32_MMX:
typedef unsigned __int64 UInt64;
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) #define YUV422ToRGB32_MMX 续行 /*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 : [edx -- edx+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 movq [edx],mm0 续行 asm movq mm7,mm4 续行 asm punpckhbw mm6,mm5 /* mm6 = G7 B7 G6 B6 G5 B5 G4 B4 */ 续行 asm movq [edx+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 movq [edx+8*2],mm6 续行 asm movq [edx+8*3],mm0
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 loop_beign: movq mm0,[eax ] //mm0=V1 Y3 U1 Y2 V0 Y1 U0 Y0 movq mm4,[eax+8] //mm4=V3 Y7 U3 Y6 V2 Y5 U2 Y4 movq mm1,mm0 movq mm5,mm4 psrlw mm1,8 //mm1=00 V1 00 U1 00 V0 00 U0 psrlw mm5,8 //mm5=00 V3 00 U3 00 V2 00 U2 pand mm0,csMMX_0x00FF_w //mm0=00 Y3 00 Y2 00 Y1 00 Y0 pand mm4,csMMX_0x00FF_w //mm4=00 Y7 00 Y6 00 Y5 00 Y4 packuswb mm1,mm5 //mm1=V3 U3 V2 U2 V1 U1 V0 U0 movq mm2,mm1 packuswb mm0,mm4 //mm0=Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 psllw mm1,8 //mm1=U3 00 U2 00 U1 00 U0 00 psrlw mm2,8 //mm2=00 V3 00 V2 00 V1 00 V0 psrlw mm1,8 //mm1=00 U3 00 U2 00 U1 00 U0 //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 : [edx -- edx+8*4] YUV422ToRGB32_MMX add eax,8*2 add edx,8*4 sub ecx,8 jnz loop_beign // } } //处理边界 pYUYV+=( expand8_width<<1 ); for (long x=expand8_width;x<width;x+=2) { YUVToRGB32_Two(&pDstLine[x],pYUYV[0],pYUYV[2],pYUYV[1],pYUYV[3]); pYUYV+=4; } } 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 }
B.使用SSE中的写缓存优化来改进MMX版本
#define YUV422ToRGB32_SSE 续行
/*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 : [edx -- edx+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 movntq [edx],mm0 续行 asm movq mm7,mm4 续行 asm punpckhbw mm6,mm5 /* mm6 = G7 B7 G6 B6 G5 B5 G4 B4 */ 续行 asm movntq [edx+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 movntq [edx+8*2],mm6 续行 asm movntq [edx+8*3],mm0 //一次处理8个颜色输出 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 loop_beign: movq mm0,[eax ] //mm0=V1 Y3 U1 Y2 V0 Y1 U0 Y0 movq mm4,[eax+8] //mm4=V3 Y7 U3 Y6 V2 Y5 U2 Y4 movq mm1,mm0 movq mm5,mm4 psrlw mm1,8 //mm1=00 V1 00 U1 00 V0 00 U0 psrlw mm5,8 //mm5=00 V3 00 U3 00 V2 00 U2 pand mm0,csMMX_0x00FF_w //mm0=00 Y3 00 Y2 00 Y1 00 Y0 pand mm4,csMMX_0x00FF_w //mm4=00 Y7 00 Y6 00 Y5 00 Y4 packuswb mm1,mm5 //mm1=V3 U3 V2 U2 V1 U1 V0 U0 movq mm2,mm1 packuswb mm0,mm4 //mm0=Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 psllw mm1,8 //mm1=U3 00 U2 00 U1 00 U0 00 psrlw mm2,8 //mm2=00 V3 00 V2 00 V1 00 V0 psrlw mm1,8 //mm1=00 U3 00 U2 00 U1 00 U0 //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 : [edx -- edx+8*4] YUV422ToRGB32_SSE add eax,8*2 add edx,8*4 sub ecx,8 jnz loop_beign // } } //处理边界 pYUYV+=( expand8_width<<1 ); for (long x=expand8_width;x<width;x+=2) { YUVToRGB32_Two(&pDstLine[x],pYUYV[0],pYUYV[2],pYUYV[1],pYUYV[3]); pYUYV+=4; } } 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 } 速度测试:
bool _CPUSupportCPUID()
{ 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指令的函数如下: bool _CPUSupportMMX() //判断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指令的函数如下: bool _CPUSupportSSE() //判断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的寄存器是比较后期加入的,某些较老的操作系统可能不支持这些寄存器 bool _SystemSupportSSE() //判断操作系统是否支持SSE指令
{ //触发异常来判断 try { asm { //movups xmm0,xmm0 asm _emit 0x0F asm _emit 0x10 asm _emit 0xC0 } return true; } catch(...) { return false; } } //定义常量,用以在程序作为分支条件 const bool _IS_MMX_ACTIVE=_CPUSupportMMX();
const bool _IS_SSE_ACTIVE=_CPUSupportSSE() && _SystemSupportSSE();
D.根据运行的CPU支持的指令集来动态调用不同的解码器实现 typedef void (*TDECODE_YUYV_line_proc)
(TARGB32* pDstLine,const TUInt8* pYUYV,long width);
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视频格式解码器的并行化实现 #include "WorkThreadPool.h"
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); } 速度测试:
__forceinline void DECODE_YUYV_AutoLock_line(TARGB32* pDstLine,
const TUInt8* pYUYV,long width,volatile long* Lock)
{ //任务领取 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);
} 速度测试: G:把测试成绩放在一起 ///////////////////////////////// |