在全志a31s上 使用ffmpeg库中sw_scale转换格式yuv4202rgbx888时,1920x1080的转换耗时有50多ms,渲染50多ms,实际效果无法达到声音视频同步。后在网上找到neon汇编优化代码,移植到sw_scale函数中,效果仍未能改善。
发现主要耗时在加载/存储内存指令上。现将移植部分代码贴上,以备后来再做分析。
.text
.global ImgYUV2RGB24_neon
.global ImgYUV2RGB24_neon1
@void ImgYUV2RGB24_neon(u8 *pu8RgbBuffer, u8 *pu8SrcYUV, l32 l32Width, l32 l32Height)
ImgYUV2RGB24_neon:
@push {r4, r5, r6, r7, r8, r9, r10,r11,r12, lr}
stmfd sp!, {r4-r10,lr}
mov r6, r2
mov r7, r3
ldr r2, [sp,#32]
ldr r3, [sp,#36]
add r4, r2, r2
add r4, r4, r2 @r4 : DstStride =4 * Width
add r4, r4, r2
@lsr r8, r2, #3 @r8 记录了col的循环次数, r2记录了YUV图像宽度
mov r8, r2, lsr #3
@lsr lr, r3, #1 @lr 记录了Row的循环次数, r3记录了YUV图像高度
mov lr, r3, lsr #1
add r3, r1, r2 @r1, pu8Src1@ r3 : pu8Src2, r2 : Width
add r5, r0, r4 @r5 : pu8Dst2 = pu8Dst - l32DstStride
mov r9, #16
vdup.8 d8, r9 @d8: 16
mov r10, #128
vdup.8 d9, r10 @d9: 128
mov r9, #75
vdup.16 q5, r9 @q5: 75
mov r10, #102
vdup.16 q6, r10 @q6: 102
mov r9, #25
vdup.16 q7, r9 @q7: 25
mov r10, #52
vdup.16 q8, r10 @q8: 52
mov r9, #129
vdup.16 q9, r9 @q9: 129
loop_row:
loop_col:
subs r8, r8, #1
vld1.u8 d0, [r1]! @YLine1
vld1.u8 d2, [r3]! @YLine2
vld1.32 {d4[0]}, [r6]! @U
vld1.32 {d4[1]}, [r7]! @V
vsubl.u8 q0, d0, d8 @YLine2 - 16
vsubl.u8 q1, d2, d8 @YLine1 - 16
vsubl.u8 q2, d4, d9
vmov q3, q2
vzip.s16 q2, q3 @q2:U - 128 q3: V-128
@开始计算乘法部分
vmul.s16 q10, q3, q8
vmla.s16 q10, q2, q7 @得到计算G分量所需要的后半部分U、V之和
vmul.s16 q11, q2, q9 @得到计算B分量的后半部分所需要的U
vmul.s16 q2, q3, q6 @得到计算R分量的后半部分所需要的V
@计算Y的部分乘积
vmul.s16 q0, q0, q5 @q0、q1得到第一行Y的共8点乘积
vmul.s16 q1, q1, q5 @q2、q3得到第二行Y的共8点乘积
@得到两行的G分量
vqsub.s16 q13, q0, q10
vqsub.s16 q14, q1, q10
vqrshrun.s16 d25, q13, #6 @@@@@@@@@@@@@@@@@@第一行的G
vqrshrun.s16 d29, q14, #6 @@@@@@@@@@@@@@@@@@第二行的G
@得到两行的B分量
vqadd.s16 q10, q0, q11
vqadd.s16 q11, q1, q11
vqrshrun.s16 d26, q10, #6 @@@@@@@@@@@@@@@@@@第一行的B
vqrshrun.s16 d30, q11, #6 @@@@@@@@@@@@@@@@@@第二行的B
@得到两行的R分量
vqadd.s16 q11, q0, q2
vqadd.s16 q2, q1, q2
vqrshrun.s16 d24, q11, #6 @@@@@@@@@@@@@@@@@@第一行的R
vqrshrun.s16 d28, q2, #6 @@@@@@@@@@@@@@@@@@第二行的R
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@做interleave操作,形成RGBx形式,然后存入目标buffer
vst4.8 {d24,d25,d26,d27}, [r0]!
vst4.8 {d28,d29,d30,d31}, [r5]!
bgt loop_col
subs lr, lr, #1
add r0, r0, r4
add r5, r0, r4
add r1, r1, r2
add r3, r3, r2
@ add r6, r6, r11
@ add r7, r7, r11
mov r8, r2, lsr #3
bgt loop_row
@pop {r4, r5, r6, r7, r8, r9, r10, lr}
ldmfd sp!, {r4-r10,lr}
bx lr
c部分接口
static int yuv420_rgb24_neon(SwsContext *c, const uint8_t *src[], int srcStride[],
int sliceY, int sliceH, uint8_t *dst[], int dstStride[])
{
if(srcStride[0]>c->srcW)
ImgYUV2RGB24_neon1(dst[0],src[0],src[1],src[2],c->srcW,c->srcH);
else
ImgYUV2RGB24_neon(dst[0],src[0],src[1],src[2],c->srcW,c->srcH);
return 0;
}
由于有些视频解码后宽度变大,改进后实现切边
ImgYUV2RGB24_neon1:
stmfd sp!, {r4-r10,lr}
mov r6, r2
mov r7, r3
ldr r2, [sp,#32]
ldr r3, [sp,#36]
add r4, r2, r2
add r4, r4, r2 @r4 : DstStride = 4 * l32Width
add r4, r4, r2
mov r8, r2, lsr #3 @r8 记录了col的循环次数, r2记录了YUV图像宽度
mov lr, r3, lsr #1 @lr 记录了Row的循环次数, r3记录了YUV图像高度
add r3, r1, r2 @r1, pu8Src1@ r3 : pu8Src2, r2 : l32Width
add r3, r3, #16
add r5, r0, r4 @r5 : pu8Dst2 = pu8Dst - l32DstStride
mov r9, #16
vdup.8 d8, r9 @d8: 16
mov r10, #128
vdup.8 d9, r10 @d9: 128
mov r9, #75
vdup.16 q5, r9 @q5: 75
mov r10, #102
vdup.16 q6, r10 @q6: 102
mov r9, #25
vdup.16 q7, r9 @q7: 25
mov r10, #52
vdup.16 q8, r10 @q8: 52
mov r9, #129
vdup.16 q9, r9 @q9: 129
loop_row1:
loop_col1:
subs r8, r8, #1
vld1.u8 d0, [r1]! @YLine1
vld1.u8 d2, [r3]! @YLine2
vld1.32 {d4[0]}, [r6]! @U
vld1.32 {d4[1]}, [r7]! @V
vsubl.u8 q0, d0, d8 @YLine2 - 16
vsubl.u8 q1, d2, d8 @YLine1 - 16
vsubl.u8 q2, d4, d9
vmov q3, q2
vzip.s16 q2, q3 @q2:U - 128 q3: V-128
@开始计算乘法部分
vmul.s16 q10, q3, q8
vmla.s16 q10, q2, q7 @得到计算G分量所需要的后半部分U、V之和
vmul.s16 q11, q2, q9 @得到计算B分量的后半部分所需要的U
vmul.s16 q2, q3, q6 @得到计算R分量的后半部分所需要的V
@计算Y的部分乘积
vmul.s16 q0, q0, q5 @q0、q1得到第一行Y的共8点乘积
vmul.s16 q1, q1, q5 @q2、q3得到第二行Y的共8点乘积
@得到两行的G分量
vqsub.s16 q13, q0, q10
vqsub.s16 q14, q1, q10
vqrshrun.s16 d25, q13, #6 @@@@@@@@@@@@@@@@@@第一行的G
vqrshrun.s16 d29, q14, #6 @@@@@@@@@@@@@@@@@@第二行的G
@得到两行的B分量
vqadd.s16 q10, q0, q11
vqadd.s16 q11, q1, q11
vqrshrun.s16 d26, q10, #6 @@@@@@@@@@@@@@@@@@第一行的B
vqrshrun.s16 d30, q11, #6 @@@@@@@@@@@@@@@@@@第二行的B
@得到两行的R分量
vqadd.s16 q11, q0, q2
vqadd.s16 q2, q1, q2
vqrshrun.s16 d24, q11, #6 @@@@@@@@@@@@@@@@@@第一行的R
vqrshrun.s16 d28, q2, #6 @@@@@@@@@@@@@@@@@@第二行的R
@做interleave操作,形成RGBx形式,然后存入目标buffer
vst4.8 {d24,d25,d26,d27}, [r0]!
vst4.8 {d28,d29,d30,d31}, [r5]!
bgt loop_col1
subs lr, lr, #1
add r0, r0, r4
add r5, r0, r4
add r1, r1, #16
add r3, r3, #16
add r1, r1, r2
add r3, r3, r2
add r1, r1, #16
add r3, r3, #16
add r6, r6, #8
add r7, r7, #8
mov r8, r2, lsr #3
bgt loop_row1
@pop {r4, r5, r6, r7, r8, r9, r10, lr}
ldmfd sp!, {r4-r10,lr}
bx lr
后期可能会尝试进步优化,现在暂时没有好的想法...