FFmpeg H264/H265边界填充二--videodsp.asm

来源:互联网 发布:数据库结构的数据模型 编辑:程序博客网 时间:2024/06/05 07:04

   在前面已经详细的分析了边界填充算法的C语言优化实现,在C语言的实现中主要是用memcpy来实现,为了提高效率FFmpeg利用了mmx、xmm和ymm等寄存器进行指令集优化。FFmpeg中和边界填充相关的优化主要在 “videodsp.asm”中实现,在下面的源代码中已经详细的分析了边界填充汇编优化代码的实现。

;******************************************************************************;* Core video DSP functions;* Copyright (c) 2012 Ronald S. Bultje <rsbultje@gmail.com>;*;* This file is part of FFmpeg.;*;* FFmpeg is free software; you can redistribute it and/or;* modify it under the terms of the GNU Lesser General Public;* License as published by the Free Software Foundation; either;* version 2.1 of the License, or (at your option) any later version.;*;* FFmpeg is distributed in the hope that it will be useful,;* but WITHOUT ANY WARRANTY; without even the implied warranty of;* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU;* Lesser General Public License for more details.;*;* You should have received a copy of the GNU Lesser General Public;* License along with FFmpeg; if not, write to the Free Software;* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA;******************************************************************************%include "libavutil/x86/x86util.asm"SECTION .text; slow vertical extension loop function. Works with variable-width, and; does per-line reading/writing of source data%macro V_COPY_ROW 2 ; type (top/body/bottom), h.%1_y_loop:                                     ; do {    mov              wq, r7mp                   ;   initialize w (r7mp = wmp) ;r7mp在前面已经取值为负数.%1_x_loop:                                     ;   do {    movu             m0, [srcq+wq]              ;     m0 = read($mmsize)      ;    movu      [dstq+wq], m0                     ;     write(m0, $mmsize)    add              wq, mmsize                 ;     w -= $mmsize    cmp              wq, -mmsize                ;   } while (w > $mmsize);      ;最后剩余wq- (-mmsize)个像素未拷贝    jl .%1_x_loop    movu             m0, [srcq-mmsize]          ;     m0 = read($mmsize)        ;剩余不足mmsize的像素,按照mmsize大小读取    movu  [dstq-mmsize], m0                     ;     write(m0, $mmsize)%ifidn %1, body                                 ;   if ($type == body) {    add            srcq, src_strideq            ;     src += src_stride%endif                                          ;   }    add            dstq, dst_strideq            ;   dst += dst_stride    dec              %2                         ; } while (--$h);    jnz .%1_y_loop%endmacro%macro vvar_fn 0; .----. <- zero; |    |    <- top is copied from first line in body of source; |----| <- start_y; |    |    <- body is copied verbatim (line-by-line) from source; |----| <- end_y; |    |    <- bottom is copied from last line in body of source; '----' <- bh%if ARCH_X86_64cglobal emu_edge_vvar, 7, 8, 1, dst, dst_stride, src, src_stride, \                                start_y, end_y, bh, w%else ; x86-32cglobal emu_edge_vvar, 1, 6, 1, dst, src, start_y, end_y, bh, w%define src_strideq r3mp%define dst_strideq r1mp    mov            srcq, r2mp    mov        start_yq, r4mp    mov          end_yq, r5mp    mov             bhq, r6mp%endif    sub             bhq, end_yq                 ; bh    -= end_q    ;bottom部分的高度    sub          end_yq, start_yq               ; end_q -= start_q  ;body部分高度    add            srcq, r7mp                   ; (r7mp = wmp)      ;拷贝行时,从右往左拷贝    add            dstq, r7mp                   ; (r7mp = wmp)    neg            r7mp                         ; (r7mp = wmp)      ;宽度取反    test       start_yq, start_yq               ; if (start_q) {    jz .body    V_COPY_ROW      top, start_yq               ;   v_copy_row(top, start_yq).body:                                          ; }    V_COPY_ROW     body, end_yq                 ; v_copy_row(body, end_yq)    test            bhq, bhq                    ; if (bh) {    jz .end    sub            srcq, src_strideq            ;   src -= src_stride    V_COPY_ROW   bottom, bhq                    ;   v_copy_row(bottom, bh).end:                                           ; }    RET%endmacro%if ARCH_X86_32INIT_MMX mmxvvar_fn%endifINIT_XMM ssevvar_fn%macro hvar_fn 0cglobal emu_edge_hvar, 5, 6, 1, dst, dst_stride, start_x, n_words, h, w    lea            dstq, [dstq+n_wordsq*2]                  ;拷贝n_words个字    neg        n_wordsq                                     ;取反    lea        start_xq, [start_xq+n_wordsq*2]              ;start_x+=2*n_words.y_loop:                                        ; do {%if cpuflag(avx2)    vpbroadcastb     m0, [dstq+start_xq]    mov              wq, n_wordsq               ;   initialize w%else    movzx            wd, byte [dstq+start_xq]   ;   w = read(1)         ;读取1字节,这里其实就是原始的dst+start_x处的像素值    imul             wd, 0x01010101             ;   w *= 0x01010101     ;扩展为4个相同的字节    movd             m0, wd    mov              wq, n_wordsq               ;   initialize w%if cpuflag(sse2)    pshufd           m0, m0, q0000              ;   splat               ;扩展%else ; mmx    punpckldq        m0, m0                     ;   splat               ;扩展%endif ; mmx/sse%endif ; avx2.x_loop:                                        ;   do {    movu    [dstq+wq*2], m0                     ;     write($reg, $mmsize)  ;从左往右拷贝    add              wq, mmsize/2               ;     w -= $mmsize/2    cmp              wq, -mmsize/2              ;   } while (w > $mmsize/2)    jl .x_loop    movu  [dstq-mmsize], m0                     ;   write($reg, $mmsize)    ;拷贝该行剩余像素    add            dstq, dst_strideq            ;   dst += dst_stride    dec              hq                         ; } while (h--)    jnz .y_loop    RET%endmacro%if ARCH_X86_32INIT_MMX mmxhvar_fn%endifINIT_XMM sse2hvar_fn%if HAVE_AVX2_EXTERNALINIT_XMM avx2hvar_fn%endif; macro to read/write a horizontal number of pixels (%2) to/from registers; on sse, - fills xmm0-15 for consecutive sets of 16 pixels;         - if (%2 & 8)  fills 8 bytes into xmm$next;         - if (%2 & 4)  fills 4 bytes into xmm$next;         - if (%2 & 3)  fills 1, 2 or 4 bytes in eax; on mmx, - fills mm0-7 for consecutive sets of 8 pixels;         - if (%2 & 4)  fills 4 bytes into mm$next;         - if (%2 & 3)  fills 1, 2 or 4 bytes in eax; writing data out is in the same way%macro READ_NUM_BYTES 2                         ;读取连续的%2个像素值%assign %%off 0     ; offset in source buffer%assign %%mmx_idx 0 ; mmx register index%assign %%xmm_idx 0 ; xmm register index%rep %2/mmsize                                  ;计算需要读取多少次mmsize个像素%if mmsize == 16                                ;为sse    movu   xmm %+ %%xmm_idx, [srcq+%%off]%assign %%xmm_idx %%xmm_idx+1%else ; mmx                                     ;为mmx    movu    mm %+ %%mmx_idx, [srcq+%%off]%assign %%mmx_idx %%mmx_idx+1%endif%assign %%off %%off+mmsize%endrep ; %2/mmsize                                                %if mmsize == 16                                ;sse,剩余像素数在[8, 16)之间%if (%2-%%off) >= 8                             ;剩下大于8个像素%if %2 > 16 && (%2-%%off) > 8                   ;初始需要拷贝的像素数大于16个,且剩下的像素数在(8, 15)之间    movu   xmm %+ %%xmm_idx, [srcq+%2-16]       ;将最后的16个像素拷贝到xmm寄存器中,这里拷贝的16个像素会和上次拷贝的部分元素重叠,但他们的值相同,并不会造成错误%assign %%xmm_idx %%xmm_idx+1%assign %%off %2%else                                           ;((%2 <= 16) || (%2 - %%off) <= 8),由于mmsize==16,所以%2>=16,同时还要满足(%2 - %%off) >= 8,满足以上条件的只有剩下8个像素的情况,拷贝到mmx寄存器中    movq    mm %+ %%mmx_idx, [srcq+%%off]       ;这里也可以像if语句里那样一次性拷贝16个像素,但并没有这个必要。%assign %%mmx_idx %%mmx_idx+1%assign %%off %%off+8%endif ;%2 > 16 && (%2-%%off) > 8%endif ; (%2-%%off) >= 8%endif ;mmsize == 16%if (%2-%%off) >= 4                             ;剩余的像素数在[4, 8)之间%if %2 > 8 && (%2-%%off) > 4                    ;如果初始需要拷贝的像素个数大于8个,而且现在剩余的像素个数在[4,8)之间,所以可以直接拷贝8个像素,    movq    mm %+ %%mmx_idx, [srcq+%2-8]        ;本次拷贝的8个像素中会有8 - (%2 - %%off) 个像素和上一次拷贝的元素重叠,但不会造成影响%assign %%off %2%else                                           ;(%2 <= 8 || (%2 - %%off) <= 4), 同时还要满足 (%2 - %%off) >= 4, 拷贝到mmx寄存器中    movd    mm %+ %%mmx_idx, [srcq+%%off]       ;进入该分支的情况有两种,第一是初始需要拷贝的总像素数小于等于8;第二种是当前待拷贝的像素数为4个。%assign %%off %%off+4                           ;当初始需要拷贝的总像素数个数小于等于8时,不能使用if条件中的[srcq+%2-8]这种方式进行拷贝,因为%endif                                          ;地址[srcq+%2-8]不属于当前需要拷贝的地址范围,这样会篡改其他的内存,当待拷贝的像素个数为4时,%assign %%mmx_idx %%mmx_idx+1                   ;可以按照如此正常进行拷贝%endif ; (%2-%%off) >= 4%if (%2-%%off) >= 1                             ;剩余像素在[1, 4)之间%if %2 >= 4                                     ;初始需要拷贝的所有像素个数大于等于4,拷贝到mmx寄存器    movd mm %+ %%mmx_idx, [srcq+%2-4]%elif (%2-%%off) == 1                           ;只需要拷贝一个像素,val对应的是eax寄存器    mov            valb, [srcq+%2-1]%elif (%2-%%off) == 2                           ;拷贝两个像素    mov            valw, [srcq+%2-2]%else                                           ;拷贝三个像素    mov            valb, [srcq+%2-1]            ;拷贝一个像素    ror            vald, 16    mov            valw, [srcq+%2-3]            ;拷贝两个像素%endif%endif ; (%2-%%off) >= 1%endmacro ; READ_NUM_BYTES%macro WRITE_NUM_BYTES 2%assign %%off 0     ; offset in destination buffer%assign %%mmx_idx 0 ; mmx register index%assign %%xmm_idx 0 ; xmm register index%rep %2/mmsize                                  ;计算需要写多少次mmsize个像素%if mmsize == 16                                ;sse    movu   [dstq+%%off], xmm %+ %%xmm_idx       %assign %%xmm_idx %%xmm_idx+1%else ; mmx    movu   [dstq+%%off], mm %+ %%mmx_idx%assign %%mmx_idx %%mmx_idx+1%endif%assign %%off %%off+mmsize%endrep ; %2/mmsize%if mmsize == 16                                ;sse%if (%2-%%off) >= 8                             ;sse,剩余像素个数[8, 16)%if %2 > 16 && (%2-%%off) > 8                   ;如果初始需要写入的像素个数大于16,且剩余的像素数大于8个    movu   [dstq+%2-16], xmm %+ %%xmm_idx       ;此时剩余需要写入的元素个数实际上是小于16个的,但这里仍然写入16个元素,并不会有任何的问题,%assign %%xmm_idx %%xmm_idx+1                   ;因为在%2>16时,上一次写入的16个像素和这里写入的16个像素中有16-(%2-%%off)个像素是相同的,并不会造成错误%assign %%off %2%else                                           ;(%2 <= 16 || (%2-%%off) <= 8),在sse条件下%2>=16,而且这里(%2-%%off) >= 8,但是当%2为16的时候不会进入该分支,    movq   [dstq+%%off], mm %+ %%mmx_idx        ;所以能进入该分支的就是%2-%%off==8,此时也可以使用if语句中的方式,也并无影响。%assign %%mmx_idx %%mmx_idx+1%assign %%off %%off+8%endif%endif ; (%2-%%off) >= 8%endif%if (%2-%%off) >= 4                             ;剩余像素个数[4, 8)%if %2 > 8 && (%2-%%off) > 4                    ;如果初始需要写入的像素个数大于8个(也就是%2>8),且剩余的像素个数大于4,此时需要写入的有效像素个数为%2-%%off,    movq    [dstq+%2-8], mm %+ %%mmx_idx        ;但这里将mmx写入到dstq+%2-8,会覆盖上次写入的8-(%2-%%off),但对结果并不会有影响,因为重叠的这几个元素是相同的%assign %%off %2%else                                           ;%2 <= 8 || (%2-%%off) <= 4,同时还需要满足(%2-%%off) >= 4,所以这里的条件相当于%2 <= 8 || (%2-%%off) == 4,    movd   [dstq+%%off], mm %+ %%mmx_idx        ;当%2<=8时(这里肯定是%2<8,如果等于会在上面写入,所以能进入这个分支,一定是%2<8),不能像上面if分支那样使用[dstq+%2-8]的方式进行写入,因为这样会造成8-(%2-%%off)个元素被覆写,此处被覆写%assign %%off %%off+4                           ;会对内存造成影响,因为当%2<8是,地址[dstq+%2-8]是属于其他模块的内存;当%2-%%off==4时可正常写入。%endif%assign %%mmx_idx %%mmx_idx+1%endif ; (%2-%%off) >= 4%if (%2-%%off) >= 1%if %2 >= 4    movd    [dstq+%2-4], mm %+ %%mmx_idx        ;这里写入4个像素虽然有重叠部分,但不会有内存越界的问题%elif (%2-%%off) == 1    mov     [dstq+%2-1], valb                   ;写一个字节,val对应的是eax寄存器%elif (%2-%%off) == 2    mov     [dstq+%2-2], valw                   ;写两个字节%else    mov     [dstq+%2-3], valw                   ;写两个字节    ror            vald, 16    mov     [dstq+%2-1], valb                   ;写一个字节,一共写入三个字节%ifnidn %1, body    ror            vald, 16%endif%endif%endif ; (%2-%%off) >= 1%endmacro ; WRITE_NUM_BYTES; vertical top/bottom extend and body copy fast loops; these are function pointers to set-width line copy functions, i.e.; they read a fixed number of pixels into set registers, and write; those out into the destination buffer%macro VERTICAL_EXTEND 2%assign %%n %1%rep 1+%2-%1%if %%n <= 3%if ARCH_X86_64;当需要拷贝的像素个数少于4个的时候,需要将像素值拷贝到eax中,在64位系统下eax对应第7个寄存器,所以此处val放在第7个元素的位置cglobal emu_edge_vfix %+ %%n, 6, 8, 0, dst, dst_stride, src, src_stride, \                                       start_y, end_y, val, bh;由于val处于第7个位置,所以只能自动加载前6个元素到对应寄存器中,后面的参数手动加载    mov             bhq, r6mp                   ; r6mp = bhmp%else ; x86-32;在32位系统下,eax对应第1个寄存器,所以这里val要放在第一个位置,所以这里不能自动加载参数,32位能够自由使用的寄存器只有6个cglobal emu_edge_vfix %+ %%n, 0, 6, 0, val, dst, src, start_y, end_y, bh    mov            dstq, r0mp    mov            srcq, r2mp    mov        start_yq, r4mp    mov          end_yq, r5mp    mov             bhq, r6mp%define dst_strideq r1mp    ;32位能够自由使用的寄存器只有6个,val使用了一个,这里其他参数使用了5个,已经被使用完,所以这两个参数放在内存中%define src_strideq r3mp%endif ; x86-64/32%else%if ARCH_X86_64cglobal emu_edge_vfix %+ %%n, 7, 7, 1, dst, dst_stride, src, src_stride, \                                       start_y, end_y, bh%else ; x86-32cglobal emu_edge_vfix %+ %%n, 1, 5, 1, dst, src, start_y, end_y, bh    mov            srcq, r2mp    mov        start_yq, r4mp    mov          end_yq, r5mp    mov             bhq, r6mp%define dst_strideq r1mp%define src_strideq r3mp%endif ; x86-64/32%endif    ; FIXME move this to c wrapper?    sub             bhq, end_yq                 ; bh    -= end_y    sub          end_yq, start_yq               ; end_y -= start_y    ; extend pixels above body    test       start_yq, start_yq               ; if (start_y) {    jz .body_loop    READ_NUM_BYTES  top, %%n                    ;   $variable_regs = read($n)   ;将上边界第一行拷贝到寄存器中.top_loop:                                      ;   do {    WRITE_NUM_BYTES top, %%n                    ;     write($variable_regs, $n) ;将寄存器中的像素值写入到内存    add            dstq, dst_strideq            ;     dst += linesize    dec        start_yq                         ;   } while (--start_y)    jnz .top_loop                               ; }    ; copy body pixels.body_loop:                                     ; do {                          ;正常拷贝中间未越出边界的像素值    READ_NUM_BYTES  body, %%n                   ;   $variable_regs = read($n)    WRITE_NUM_BYTES body, %%n                   ;   write($variable_regs, $n)    add            dstq, dst_strideq            ;   dst += dst_stride    add            srcq, src_strideq            ;   src += src_stride    dec          end_yq                         ; } while (--end_y)    jnz .body_loop    ; copy bottom pixels    test            bhq, bhq                    ; if (block_h) {    jz .end    sub            srcq, src_strideq            ;   src -= linesize    READ_NUM_BYTES  bottom, %%n                 ;   $variable_regs = read($n)   ;将下边界的像素值拷贝到寄存器中.bottom_loop:                                   ;   do {                        ;将寄存器中的值写入到越出下边界的内存中    WRITE_NUM_BYTES bottom, %%n                 ;     write($variable_regs, $n)    add            dstq, dst_strideq            ;     dst += linesize    dec             bhq                         ;   } while (--bh)    jnz .bottom_loop                            ; }.end:    RET%assign %%n %%n+1%endrep ; 1+%2-%1%endmacro ; VERTICAL_EXTENDINIT_MMX mmxVERTICAL_EXTEND 1, 15%if ARCH_X86_32VERTICAL_EXTEND 16, 22%endifINIT_XMM sseVERTICAL_EXTEND 16, 22; left/right (horizontal) fast extend functions; these are essentially identical to the vertical extend ones above,; just left/right separated because number of pixels to extend is; obviously not the same on both sides.%macro READ_V_PIXEL 2%if cpuflag(avx2)    vpbroadcastb     m0, %2%else    movzx          vald, byte %2        ;从[dstq+start_xq]读取一个字节    imul           vald, 0x01010101     ;将该字节扩展为4个相同的字节%if %1 >= 8    movd             m0, vald           ;放入到m0指令集寄存器中(mmx,xmm等)%if mmsize == 16    pshufd           m0, m0, q0000      ;扩展为16字节%else    punpckldq        m0, m0             ;扩展%endif ; mmsize == 16%endif ; %1 > 16%endif ; avx2%endmacro ; READ_V_PIXEL%macro WRITE_V_PIXEL 2%assign %%off 0%if %1 >= 8%rep %1/mmsize    movu     [%2+%%off], m0     ;拷贝%assign %%off %%off+mmsize%endrep ; %1/mmsize%if mmsize == 16%if %1-%%off >= 8               ;剩余待拷贝像素数[8, 16)%if %1 > 16 && %1-%%off > 8     ;拷贝到[%2+%1-16],虽然会和上一次的拷贝有重叠的部分,但并不会造成问题    movu     [%2+%1-16], m0%assign %%off %1%else    movq     [%2+%%off], m0     ;剩余8个像素%assign %%off %%off+8%endif%endif ; %1-%%off >= 8%endif ; mmsize == 16%if %1-%%off >= 4               ;剩余[4,8)%if %1 > 8 && %1-%%off > 4    movq      [%2+%1-8], m0%assign %%off %1%else                           ;剩余4个像素    movd     [%2+%%off], m0%assign %%off %%off+4%endif%endif ; %1-%%off >= 4%else ; %1 < 8%rep %1/4    mov      [%2+%%off], vald%assign %%off %%off+4%endrep ; %1/4%endif ; %1 >=/< 8%if %1-%%off == 2           ;剩余2个像素%if cpuflag(avx2)    movd     [%2+%%off-2], m0%else    mov      [%2+%%off], valw%endif ; avx2%endif ; (%1-%%off)/2%endmacro ; WRITE_V_PIXEL%macro H_EXTEND 2%assign %%n %1%rep 1+(%2-%1)/2%if cpuflag(avx2)cglobal emu_edge_hfix %+ %%n, 4, 4, 1, dst, dst_stride, start_x, bh%elsecglobal emu_edge_hfix %+ %%n, 4, 5, 1, dst, dst_stride, start_x, bh, val%endif.loop_y:                                        ; do {    READ_V_PIXEL    %%n, [dstq+start_xq]        ;   $variable_regs = read($n)    WRITE_V_PIXEL   %%n, dstq                   ;   write($variable_regs, $n)    add            dstq, dst_strideq            ;   dst += dst_stride    dec             bhq                         ; } while (--bh)    jnz .loop_y    RET%assign %%n %%n+2       ;2个像素为单位进行拷贝%endrep ; 1+(%2-%1)/2%endmacro ; H_EXTENDINIT_MMX mmxH_EXTEND 2, 14%if ARCH_X86_32H_EXTEND 16, 22%endifINIT_XMM sse2H_EXTEND 16, 22%if HAVE_AVX2_EXTERNALINIT_XMM avx2H_EXTEND 8, 22%endif%macro PREFETCH_FN 1cglobal prefetch, 3, 3, 0, buf, stride, h.loop:    %1      [bufq]    add      bufq, strideq    dec        hd    jg .loop    REP_RET%endmacroINIT_MMX mmxextPREFETCH_FN prefetcht0%if ARCH_X86_32INIT_MMX 3dnowPREFETCH_FN prefetch%endif


0 0