michael@0: /******************************************************************** michael@0: * * michael@0: * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * michael@0: * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * michael@0: * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * michael@0: * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * michael@0: * * michael@0: * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * michael@0: * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * michael@0: * * michael@0: ******************************************************************** michael@0: michael@0: function: michael@0: last mod: $Id: mmxfrag.c 17410 2010-09-21 21:53:48Z tterribe $ michael@0: michael@0: ********************************************************************/ michael@0: michael@0: /*MMX acceleration of fragment reconstruction for motion compensation. michael@0: Originally written by Rudolf Marek. michael@0: Additional optimization by Nils Pipenbrinck. michael@0: Note: Loops are unrolled for best performance. michael@0: The iteration each instruction belongs to is marked in the comments as #i.*/ michael@0: #include michael@0: #include "x86int.h" michael@0: michael@0: #if defined(OC_X86_ASM) michael@0: michael@0: /*Copies an 8x8 block of pixels from _src to _dst, assuming _ystride bytes michael@0: between rows.*/ michael@0: # define OC_FRAG_COPY_MMX(_dst,_src,_ystride) \ michael@0: do{ \ michael@0: const unsigned char *src; \ michael@0: unsigned char *dst; \ michael@0: ptrdiff_t ystride3; \ michael@0: src=(_src); \ michael@0: dst=(_dst); \ michael@0: __asm__ __volatile__( \ michael@0: /*src+0*ystride*/ \ michael@0: "movq (%[src]),%%mm0\n\t" \ michael@0: /*src+1*ystride*/ \ michael@0: "movq (%[src],%[ystride]),%%mm1\n\t" \ michael@0: /*ystride3=ystride*3*/ \ michael@0: "lea (%[ystride],%[ystride],2),%[ystride3]\n\t" \ michael@0: /*src+2*ystride*/ \ michael@0: "movq (%[src],%[ystride],2),%%mm2\n\t" \ michael@0: /*src+3*ystride*/ \ michael@0: "movq (%[src],%[ystride3]),%%mm3\n\t" \ michael@0: /*dst+0*ystride*/ \ michael@0: "movq %%mm0,(%[dst])\n\t" \ michael@0: /*dst+1*ystride*/ \ michael@0: "movq %%mm1,(%[dst],%[ystride])\n\t" \ michael@0: /*Pointer to next 4.*/ \ michael@0: "lea (%[src],%[ystride],4),%[src]\n\t" \ michael@0: /*dst+2*ystride*/ \ michael@0: "movq %%mm2,(%[dst],%[ystride],2)\n\t" \ michael@0: /*dst+3*ystride*/ \ michael@0: "movq %%mm3,(%[dst],%[ystride3])\n\t" \ michael@0: /*Pointer to next 4.*/ \ michael@0: "lea (%[dst],%[ystride],4),%[dst]\n\t" \ michael@0: /*src+0*ystride*/ \ michael@0: "movq (%[src]),%%mm0\n\t" \ michael@0: /*src+1*ystride*/ \ michael@0: "movq (%[src],%[ystride]),%%mm1\n\t" \ michael@0: /*src+2*ystride*/ \ michael@0: "movq (%[src],%[ystride],2),%%mm2\n\t" \ michael@0: /*src+3*ystride*/ \ michael@0: "movq (%[src],%[ystride3]),%%mm3\n\t" \ michael@0: /*dst+0*ystride*/ \ michael@0: "movq %%mm0,(%[dst])\n\t" \ michael@0: /*dst+1*ystride*/ \ michael@0: "movq %%mm1,(%[dst],%[ystride])\n\t" \ michael@0: /*dst+2*ystride*/ \ michael@0: "movq %%mm2,(%[dst],%[ystride],2)\n\t" \ michael@0: /*dst+3*ystride*/ \ michael@0: "movq %%mm3,(%[dst],%[ystride3])\n\t" \ michael@0: :[dst]"+r"(dst),[src]"+r"(src),[ystride3]"=&r"(ystride3) \ michael@0: :[ystride]"r"((ptrdiff_t)(_ystride)) \ michael@0: :"memory" \ michael@0: ); \ michael@0: } \ michael@0: while(0) michael@0: michael@0: /*Copies an 8x8 block of pixels from _src to _dst, assuming _ystride bytes michael@0: between rows.*/ michael@0: void oc_frag_copy_mmx(unsigned char *_dst, michael@0: const unsigned char *_src,int _ystride){ michael@0: OC_FRAG_COPY_MMX(_dst,_src,_ystride); michael@0: } michael@0: michael@0: /*Copies the fragments specified by the lists of fragment indices from one michael@0: frame to another. michael@0: _dst_frame: The reference frame to copy to. michael@0: _src_frame: The reference frame to copy from. michael@0: _ystride: The row stride of the reference frames. michael@0: _fragis: A pointer to a list of fragment indices. michael@0: _nfragis: The number of fragment indices to copy. michael@0: _frag_buf_offs: The offsets of fragments in the reference frames.*/ michael@0: void oc_frag_copy_list_mmx(unsigned char *_dst_frame, michael@0: const unsigned char *_src_frame,int _ystride, michael@0: const ptrdiff_t *_fragis,ptrdiff_t _nfragis,const ptrdiff_t *_frag_buf_offs){ michael@0: ptrdiff_t fragii; michael@0: for(fragii=0;fragii<_nfragis;fragii++){ michael@0: ptrdiff_t frag_buf_off; michael@0: frag_buf_off=_frag_buf_offs[_fragis[fragii]]; michael@0: OC_FRAG_COPY_MMX(_dst_frame+frag_buf_off, michael@0: _src_frame+frag_buf_off,_ystride); michael@0: } michael@0: } michael@0: michael@0: michael@0: void oc_frag_recon_intra_mmx(unsigned char *_dst,int _ystride, michael@0: const ogg_int16_t *_residue){ michael@0: __asm__ __volatile__( michael@0: /*Set mm0 to 0xFFFFFFFFFFFFFFFF.*/ michael@0: "pcmpeqw %%mm0,%%mm0\n\t" michael@0: /*#0 Load low residue.*/ michael@0: "movq 0*8(%[residue]),%%mm1\n\t" michael@0: /*#0 Load high residue.*/ michael@0: "movq 1*8(%[residue]),%%mm2\n\t" michael@0: /*Set mm0 to 0x8000800080008000.*/ michael@0: "psllw $15,%%mm0\n\t" michael@0: /*#1 Load low residue.*/ michael@0: "movq 2*8(%[residue]),%%mm3\n\t" michael@0: /*#1 Load high residue.*/ michael@0: "movq 3*8(%[residue]),%%mm4\n\t" michael@0: /*Set mm0 to 0x0080008000800080.*/ michael@0: "psrlw $8,%%mm0\n\t" michael@0: /*#2 Load low residue.*/ michael@0: "movq 4*8(%[residue]),%%mm5\n\t" michael@0: /*#2 Load high residue.*/ michael@0: "movq 5*8(%[residue]),%%mm6\n\t" michael@0: /*#0 Bias low residue.*/ michael@0: "paddsw %%mm0,%%mm1\n\t" michael@0: /*#0 Bias high residue.*/ michael@0: "paddsw %%mm0,%%mm2\n\t" michael@0: /*#0 Pack to byte.*/ michael@0: "packuswb %%mm2,%%mm1\n\t" michael@0: /*#1 Bias low residue.*/ michael@0: "paddsw %%mm0,%%mm3\n\t" michael@0: /*#1 Bias high residue.*/ michael@0: "paddsw %%mm0,%%mm4\n\t" michael@0: /*#1 Pack to byte.*/ michael@0: "packuswb %%mm4,%%mm3\n\t" michael@0: /*#2 Bias low residue.*/ michael@0: "paddsw %%mm0,%%mm5\n\t" michael@0: /*#2 Bias high residue.*/ michael@0: "paddsw %%mm0,%%mm6\n\t" michael@0: /*#2 Pack to byte.*/ michael@0: "packuswb %%mm6,%%mm5\n\t" michael@0: /*#0 Write row.*/ michael@0: "movq %%mm1,(%[dst])\n\t" michael@0: /*#1 Write row.*/ michael@0: "movq %%mm3,(%[dst],%[ystride])\n\t" michael@0: /*#2 Write row.*/ michael@0: "movq %%mm5,(%[dst],%[ystride],2)\n\t" michael@0: /*#3 Load low residue.*/ michael@0: "movq 6*8(%[residue]),%%mm1\n\t" michael@0: /*#3 Load high residue.*/ michael@0: "movq 7*8(%[residue]),%%mm2\n\t" michael@0: /*#4 Load high residue.*/ michael@0: "movq 8*8(%[residue]),%%mm3\n\t" michael@0: /*#4 Load high residue.*/ michael@0: "movq 9*8(%[residue]),%%mm4\n\t" michael@0: /*#5 Load high residue.*/ michael@0: "movq 10*8(%[residue]),%%mm5\n\t" michael@0: /*#5 Load high residue.*/ michael@0: "movq 11*8(%[residue]),%%mm6\n\t" michael@0: /*#3 Bias low residue.*/ michael@0: "paddsw %%mm0,%%mm1\n\t" michael@0: /*#3 Bias high residue.*/ michael@0: "paddsw %%mm0,%%mm2\n\t" michael@0: /*#3 Pack to byte.*/ michael@0: "packuswb %%mm2,%%mm1\n\t" michael@0: /*#4 Bias low residue.*/ michael@0: "paddsw %%mm0,%%mm3\n\t" michael@0: /*#4 Bias high residue.*/ michael@0: "paddsw %%mm0,%%mm4\n\t" michael@0: /*#4 Pack to byte.*/ michael@0: "packuswb %%mm4,%%mm3\n\t" michael@0: /*#5 Bias low residue.*/ michael@0: "paddsw %%mm0,%%mm5\n\t" michael@0: /*#5 Bias high residue.*/ michael@0: "paddsw %%mm0,%%mm6\n\t" michael@0: /*#5 Pack to byte.*/ michael@0: "packuswb %%mm6,%%mm5\n\t" michael@0: /*#3 Write row.*/ michael@0: "movq %%mm1,(%[dst],%[ystride3])\n\t" michael@0: /*#4 Write row.*/ michael@0: "movq %%mm3,(%[dst4])\n\t" michael@0: /*#5 Write row.*/ michael@0: "movq %%mm5,(%[dst4],%[ystride])\n\t" michael@0: /*#6 Load low residue.*/ michael@0: "movq 12*8(%[residue]),%%mm1\n\t" michael@0: /*#6 Load high residue.*/ michael@0: "movq 13*8(%[residue]),%%mm2\n\t" michael@0: /*#7 Load low residue.*/ michael@0: "movq 14*8(%[residue]),%%mm3\n\t" michael@0: /*#7 Load high residue.*/ michael@0: "movq 15*8(%[residue]),%%mm4\n\t" michael@0: /*#6 Bias low residue.*/ michael@0: "paddsw %%mm0,%%mm1\n\t" michael@0: /*#6 Bias high residue.*/ michael@0: "paddsw %%mm0,%%mm2\n\t" michael@0: /*#6 Pack to byte.*/ michael@0: "packuswb %%mm2,%%mm1\n\t" michael@0: /*#7 Bias low residue.*/ michael@0: "paddsw %%mm0,%%mm3\n\t" michael@0: /*#7 Bias high residue.*/ michael@0: "paddsw %%mm0,%%mm4\n\t" michael@0: /*#7 Pack to byte.*/ michael@0: "packuswb %%mm4,%%mm3\n\t" michael@0: /*#6 Write row.*/ michael@0: "movq %%mm1,(%[dst4],%[ystride],2)\n\t" michael@0: /*#7 Write row.*/ michael@0: "movq %%mm3,(%[dst4],%[ystride3])\n\t" michael@0: : michael@0: :[residue]"r"(_residue), michael@0: [dst]"r"(_dst), michael@0: [dst4]"r"(_dst+(_ystride<<2)), michael@0: [ystride]"r"((ptrdiff_t)_ystride), michael@0: [ystride3]"r"((ptrdiff_t)_ystride*3) michael@0: :"memory" michael@0: ); michael@0: } michael@0: michael@0: void oc_frag_recon_inter_mmx(unsigned char *_dst,const unsigned char *_src, michael@0: int _ystride,const ogg_int16_t *_residue){ michael@0: int i; michael@0: /*Zero mm0.*/ michael@0: __asm__ __volatile__("pxor %%mm0,%%mm0\n\t"::); michael@0: for(i=4;i-->0;){ michael@0: __asm__ __volatile__( michael@0: /*#0 Load source.*/ michael@0: "movq (%[src]),%%mm3\n\t" michael@0: /*#1 Load source.*/ michael@0: "movq (%[src],%[ystride]),%%mm7\n\t" michael@0: /*#0 Get copy of src.*/ michael@0: "movq %%mm3,%%mm4\n\t" michael@0: /*#0 Expand high source.*/ michael@0: "punpckhbw %%mm0,%%mm4\n\t" michael@0: /*#0 Expand low source.*/ michael@0: "punpcklbw %%mm0,%%mm3\n\t" michael@0: /*#0 Add residue high.*/ michael@0: "paddsw 8(%[residue]),%%mm4\n\t" michael@0: /*#1 Get copy of src.*/ michael@0: "movq %%mm7,%%mm2\n\t" michael@0: /*#0 Add residue low.*/ michael@0: "paddsw (%[residue]), %%mm3\n\t" michael@0: /*#1 Expand high source.*/ michael@0: "punpckhbw %%mm0,%%mm2\n\t" michael@0: /*#0 Pack final row pixels.*/ michael@0: "packuswb %%mm4,%%mm3\n\t" michael@0: /*#1 Expand low source.*/ michael@0: "punpcklbw %%mm0,%%mm7\n\t" michael@0: /*#1 Add residue low.*/ michael@0: "paddsw 16(%[residue]),%%mm7\n\t" michael@0: /*#1 Add residue high.*/ michael@0: "paddsw 24(%[residue]),%%mm2\n\t" michael@0: /*Advance residue.*/ michael@0: "lea 32(%[residue]),%[residue]\n\t" michael@0: /*#1 Pack final row pixels.*/ michael@0: "packuswb %%mm2,%%mm7\n\t" michael@0: /*Advance src.*/ michael@0: "lea (%[src],%[ystride],2),%[src]\n\t" michael@0: /*#0 Write row.*/ michael@0: "movq %%mm3,(%[dst])\n\t" michael@0: /*#1 Write row.*/ michael@0: "movq %%mm7,(%[dst],%[ystride])\n\t" michael@0: /*Advance dst.*/ michael@0: "lea (%[dst],%[ystride],2),%[dst]\n\t" michael@0: :[residue]"+r"(_residue),[dst]"+r"(_dst),[src]"+r"(_src) michael@0: :[ystride]"r"((ptrdiff_t)_ystride) michael@0: :"memory" michael@0: ); michael@0: } michael@0: } michael@0: michael@0: void oc_frag_recon_inter2_mmx(unsigned char *_dst,const unsigned char *_src1, michael@0: const unsigned char *_src2,int _ystride,const ogg_int16_t *_residue){ michael@0: int i; michael@0: /*Zero mm7.*/ michael@0: __asm__ __volatile__("pxor %%mm7,%%mm7\n\t"::); michael@0: for(i=4;i-->0;){ michael@0: __asm__ __volatile__( michael@0: /*#0 Load src1.*/ michael@0: "movq (%[src1]),%%mm0\n\t" michael@0: /*#0 Load src2.*/ michael@0: "movq (%[src2]),%%mm2\n\t" michael@0: /*#0 Copy src1.*/ michael@0: "movq %%mm0,%%mm1\n\t" michael@0: /*#0 Copy src2.*/ michael@0: "movq %%mm2,%%mm3\n\t" michael@0: /*#1 Load src1.*/ michael@0: "movq (%[src1],%[ystride]),%%mm4\n\t" michael@0: /*#0 Unpack lower src1.*/ michael@0: "punpcklbw %%mm7,%%mm0\n\t" michael@0: /*#1 Load src2.*/ michael@0: "movq (%[src2],%[ystride]),%%mm5\n\t" michael@0: /*#0 Unpack higher src1.*/ michael@0: "punpckhbw %%mm7,%%mm1\n\t" michael@0: /*#0 Unpack lower src2.*/ michael@0: "punpcklbw %%mm7,%%mm2\n\t" michael@0: /*#0 Unpack higher src2.*/ michael@0: "punpckhbw %%mm7,%%mm3\n\t" michael@0: /*Advance src1 ptr.*/ michael@0: "lea (%[src1],%[ystride],2),%[src1]\n\t" michael@0: /*Advance src2 ptr.*/ michael@0: "lea (%[src2],%[ystride],2),%[src2]\n\t" michael@0: /*#0 Lower src1+src2.*/ michael@0: "paddsw %%mm2,%%mm0\n\t" michael@0: /*#0 Higher src1+src2.*/ michael@0: "paddsw %%mm3,%%mm1\n\t" michael@0: /*#1 Copy src1.*/ michael@0: "movq %%mm4,%%mm2\n\t" michael@0: /*#0 Build lo average.*/ michael@0: "psraw $1,%%mm0\n\t" michael@0: /*#1 Copy src2.*/ michael@0: "movq %%mm5,%%mm3\n\t" michael@0: /*#1 Unpack lower src1.*/ michael@0: "punpcklbw %%mm7,%%mm4\n\t" michael@0: /*#0 Build hi average.*/ michael@0: "psraw $1,%%mm1\n\t" michael@0: /*#1 Unpack higher src1.*/ michael@0: "punpckhbw %%mm7,%%mm2\n\t" michael@0: /*#0 low+=residue.*/ michael@0: "paddsw (%[residue]),%%mm0\n\t" michael@0: /*#1 Unpack lower src2.*/ michael@0: "punpcklbw %%mm7,%%mm5\n\t" michael@0: /*#0 high+=residue.*/ michael@0: "paddsw 8(%[residue]),%%mm1\n\t" michael@0: /*#1 Unpack higher src2.*/ michael@0: "punpckhbw %%mm7,%%mm3\n\t" michael@0: /*#1 Lower src1+src2.*/ michael@0: "paddsw %%mm4,%%mm5\n\t" michael@0: /*#0 Pack and saturate.*/ michael@0: "packuswb %%mm1,%%mm0\n\t" michael@0: /*#1 Higher src1+src2.*/ michael@0: "paddsw %%mm2,%%mm3\n\t" michael@0: /*#0 Write row.*/ michael@0: "movq %%mm0,(%[dst])\n\t" michael@0: /*#1 Build lo average.*/ michael@0: "psraw $1,%%mm5\n\t" michael@0: /*#1 Build hi average.*/ michael@0: "psraw $1,%%mm3\n\t" michael@0: /*#1 low+=residue.*/ michael@0: "paddsw 16(%[residue]),%%mm5\n\t" michael@0: /*#1 high+=residue.*/ michael@0: "paddsw 24(%[residue]),%%mm3\n\t" michael@0: /*#1 Pack and saturate.*/ michael@0: "packuswb %%mm3,%%mm5\n\t" michael@0: /*#1 Write row ptr.*/ michael@0: "movq %%mm5,(%[dst],%[ystride])\n\t" michael@0: /*Advance residue ptr.*/ michael@0: "add $32,%[residue]\n\t" michael@0: /*Advance dest ptr.*/ michael@0: "lea (%[dst],%[ystride],2),%[dst]\n\t" michael@0: :[dst]"+r"(_dst),[residue]"+r"(_residue), michael@0: [src1]"+%r"(_src1),[src2]"+r"(_src2) michael@0: :[ystride]"r"((ptrdiff_t)_ystride) michael@0: :"memory" michael@0: ); michael@0: } michael@0: } michael@0: michael@0: void oc_restore_fpu_mmx(void){ michael@0: __asm__ __volatile__("emms\n\t"); michael@0: } michael@0: #endif