rgba 转 yv420p x86 极速转换,1080p 800 fps

    最近项目中涉及到颜色控件转换,开始使用ffmpeg的 scale 功能,感觉方便快捷。 不过 测试发现 1080p图像在 i5 2400 上只有150-160fps。具体到实时24fps的视频中,颜色转换,就消耗了一个核的1/6的性能(i54核,windows统计是4%,)。从这个数据预估仅仅是颜色转换 这个使用率太高,应该有优化空间。从网上查询研究的结果看,有yv420转rgb的,能到600fps(http://blog.csdn.net/mikedai/article/details/79073530)。虽然过程相反,从复杂度看应该差不多。 实际项目中会多路并行使用,所以是cpu越低越好。考虑优化可以x86汇编优化,和gpu加速两种。x86相对容易,gpu复杂但是能消耗更少的cpu。不过从该文章的测试结果看,gpu方案综合 并不比cpu高多少。在600 fps下,cpu使用率已经极少到可以忽略。所以以下是基于x86汇编的方案,gpu方案目前没有时间尝试了,。

     根据目前经验,转换优化:

    1、可以从数学计算转化为查表方式,能提高不少性能。

    2、c++调用sse指令函数。

    3、直接汇编。

    直接上结果吧(x64平台,汇编需要单独成文件):

   方法1、发现效果并不好,大约和ffmpeg的方法持平。

   方法2、能提高到200+fps。

               通过对应汇编代码发现,sse指令函数,每一条都额外消耗了3-5条汇编指令。所以直接使用汇编预计可以提高3-5倍。

  方法3、汇编提高到630-900 fps,计算机环境有波动 所以每次测试结果有些差别。(ps 经过查看原始公式,应该还有优化空间,特别是 UV 的转换。)

  未使用查表方式:因为多次查表,涉及到内存多次访问,效率上预计有很大影响。而直接寄存器计算,可以避免内存频繁交互。同时,汇编指令(_mm_maddubs_epi16 / pmaddubsw)能一次完成  2乘法和1次加法操作,所以能比查表节省指令。

第一步:公式转化为 整数计算:

      

y = (unsigned char)( ( 66 * r + 129 * g +  25 * b + 128) >> 8) + 16  ;            
u = (unsigned char)( ( -38 * r -  74 * g + 112 * b + 128) >> 8) + 128 ;            
v = (unsigned char)( ( 112 * r -  94 * g -  18 * b + 128) >> 8) + 128 ;

第二步:将公式再转化为可直接乘法和加法完成的方式:

  y=(unsigned char)(  66 * r + 129 * g +  25 * b + 16*255)>>8 ;

  u=(unsigned char)(-38*r - 74*g + 112*b + 127*255)>>8;

  v=(unsigned char)(112*r - 94*g - 18*b + 127*255)>>8;   

这样变换之后,一次y或者u或者v转换只需要 括号内计算一次乘法加操作 和一次水平相加,加上一次右移操作3条指令完成。

       一次pmaddubsw 指令执行 66 * r + 129 * g,和25 * b + 16*255,需要将这两个结果再相加,所以需要一次水平相加操作(_mm_hadd_epi16)

这样一次计算占据4字节xmm寄存器,xmm可以计算16字节,所以一次可以同时进行4像素rgba计算。

c 调用接口(图像宽高为16字节整数倍图像): 

void rgba2yv420_asm(unsigned char *pRgb, unsigned char *pYv420 , int nWidth, int nHeight);

汇编.asm文件:


.data
    ycof db 66, 127, 25,  16, 66, 127, 25,  16, 66, 127, 25,  16, 66, 127, 25,  16
    ucof db -19, -37, 56, 64, -19, -37, 56,  64, -19, -37, 56,  64, -19, -37, 56, 64
    vcof db 56, -47, -9,  64, 56, -47, -9,  64, 56, -47, -9,  64, 56, -47, -9,  64
    uv_stuff_mask_low db 0, 1, 2, 3, 8, 9, 10, 11, -1,-1,-1,-1, -1,-1,-1,-1
    uv_stuff_mask_high db -1,-1,-1,-1, -1,-1,-1,-1, 0, 1, 2, 3, 8, 9, 10, 11
;    nStep db 8
.code

rgba2yv420_asm PROC
    
    mov rax, -1    ;return error
        
    ;step
    cmp r8, 1920
    je step128
    cmp r8, 1080
    je step8
    cmp r8, 720
    je step16
    jp finish    ;return error
    
step128:
    mov rsi, 256
    jp start
step16:
    mov rsi, 64
    jp start
step8:
    mov rsi, 32
start:    
    push r12
    push r13
    push r14
    push r15
    
    ;init regs
    pxor xmm0, xmm0
    pxor xmm1, xmm1
    pxor xmm2, xmm2        ;mask
    pxor xmm3, xmm3        ;mask
    pxor xmm4, xmm4
    pxor xmm5, xmm5        ;shift    
    pxor xmm6, xmm6        ;shift
    
    pxor xmm7, xmm7
    
    pxor xmm8, xmm8
    pxor xmm9, xmm9
    
    pxor xmm10, xmm10
    
    movdqa xmm13, xmmword ptr [ycof];
    movdqa xmm14, xmmword ptr [ucof];
    movdqa xmm15, xmmword ptr [vcof];
    
    mov r10, rcx        ; rgba input
    mov r13, rdx        ; yv420 output
    mov eax, 8
    mov ebx, 7
    movd xmm5, eax        ; >>8
    movd xmm6, ebx        ; >>7
    
    movdqa xmm2, xmmword ptr [uv_stuff_mask_low]        ; mask low
    movdqa xmm3, xmmword ptr [uv_stuff_mask_high]        ; mask high
    

    mov r11, r8    ; w
    mov r14, r13        ;output u

    imul r11, r9   ; h
    add r14, r11        ;U OUT = Y + w*h
    
    mov r15, r14        ;output v
    shr r11, 2
    add r15, r11        ;V OUT = U + w*h/4    
;input params

    xor rax, rax
    mov r11, r9            ;h,  R9

hloop:    
    xor rdi, rdi        ;w = 0,  R8
    
prefetch_Y:
    prefetchnta [rsi][r10]    ;prefetch 128 pixels
    xor r12, r12
    
wloop:
    movdqa xmm0, [r10+r12]        
    movdqa xmm1, [r10+r12+16]
        
    pmaddubsw xmm0, xmm13        ;_mm_maddubs_epi16  
    pmaddubsw xmm1, xmm13        ;_mm_maddubs_epi16
    phaddw    xmm0, xmm1        ;_mm_hadd_epi16
    PSRAW      xmm0, xmm5        ;_mm_sra_epi16        >>8
    PACKSSWB  xmm0, xmm0        ;_mm_packs_epi16
    ;PACKUSWB  xmm0, xmm0        ;_mm_packs_epi16

    movq    qword ptr [r13+rdi], xmm0    ;output low 64 bit
    add rdi, 8            ; w++
    
    cmp rax, 1
    je Endof_uv        
    
; UV LOOPS
    movdqa xmm0, [r10+r12]        ; reload 16 bytes 4 pixel
    movdqa xmm1, [r10+r12+16]    ; reload 16 bytes
    
    pshufb xmm0, xmm2    ;_mm_shuffle_epi8  skip 1 pixel  4->2 pixel
    pshufb xmm1, xmm3    ;_mm_shuffle_epi8  ;
    
    paddw xmm0, xmm1    ;_mm_add_epi16
    movdqa xmm1, xmm0
    ;u
    pmaddubsw xmm0, xmm14        ;_mm_maddubs_epi16
    phaddw    xmm0, xmm7            ;_mm_hadd_epi16 0
    PSRAW      xmm0, xmm6        ;_mm_sra_epi16        >>7
    PACKUSWB  xmm0, xmm0        ;_mm_packs_epi16
    ;v    
    pmaddubsw xmm1, xmm15        ;_mm_maddubs_epi16
    phaddw    xmm1, xmm7            ;_mm_hadd_epi16 0
    PSRAW      xmm1, xmm6        ;_mm_sra_epi16        >>7
    PACKUSWB  xmm1, xmm1        ;_mm_packs_epi16
    
    movd    dword ptr [r14], xmm0    ;output low 32 bit  U
    movd    dword ptr [r15], xmm1    ;output low 32 bit  V
    add r14, 4            ;U += 4
    add r15, 4            ;V += 4
    
Endof_uv:    
    add r12, 32
    cmp r12, rsi
    jne wloop
    
    add r10, rsi
    cmp rdi, r8
    jne prefetch_Y
    
    xor rax, 1
    add r13, r8
    dec r11                ; h++
    jne  hloop

    xor rax, rax        ;return 0
    
    pop r15
    pop r14
    pop r13
    pop r12
    emms        ;reset float register
finish:

    ret
 
rgba2yv420_asm ENDP


END

     

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值