]> git.sesse.net Git - x264/blobdiff - encoder/me.c
use permute macros in satd
[x264] / encoder / me.c
index 61c6db2ad3ff84c3482bf3289a970a16e58f9a71..44f6f6db227fd3674faa47bbbcbf0cfbc8b52249 100644 (file)
@@ -1,11 +1,11 @@
 /*****************************************************************************
  * me.c: h264 encoder library (Motion Estimation)
  *****************************************************************************
- * Copyright (C) 2003 Laurent Aimar
- * $Id: me.c,v 1.1 2004/06/03 19:27:08 fenrir Exp $
+ * Copyright (C) 2003-2008 x264 project
  *
- * Authors: Laurent Aimar <fenrir@via.ecp.fr>
- *          Loren Merritt <lorenm@u.washington.edu>
+ * Authors: Loren Merritt <lorenm@u.washington.edu>
+ *          Laurent Aimar <fenrir@via.ecp.fr>
+ *          Fiona Glaser <fiona@x264.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -19,7 +19,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111, USA.
  *****************************************************************************/
 
 #include "common/common.h"
@@ -31,7 +31,7 @@
  * and refine_* are run only on the winner.
  * the subme=7 values are much higher because any amount of satd search makes
  * up its time by reducing the number of rd iterations. */
-static const int subpel_iterations[][4] = 
+static const int subpel_iterations[][4] =
    {{1,0,0,0},
     {1,1,0,0},
     {0,1,1,0},
@@ -151,7 +151,7 @@ static void refine_subpel( x264_t *h, x264_me_t *m, int hpel_iters, int qpel_ite
     }\
 }
 
-void x264_me_search_ref( x264_t *h, x264_me_t *m, int (*mvc)[2], int i_mvc, int *p_halfpel_thresh )
+void x264_me_search_ref( x264_t *h, x264_me_t *m, int16_t (*mvc)[2], int i_mvc, int *p_halfpel_thresh )
 {
     const int bw = x264_pixel_size[m->i_pixel].w;
     const int bh = x264_pixel_size[m->i_pixel].h;
@@ -161,9 +161,9 @@ void x264_me_search_ref( x264_t *h, x264_me_t *m, int (*mvc)[2], int i_mvc, int
     int bpred_mx = 0, bpred_my = 0, bpred_cost = COST_MAX;
     int omx, omy, pmx, pmy;
     uint8_t *p_fref = m->p_fref[0];
-    DECLARE_ALIGNED( uint8_t, pix[16*16], 16 );
-    
-    int i, j;
+    DECLARE_ALIGNED_16( uint8_t pix[16*16] );
+
+    int i = 0, j;
     int dir;
     int costs[6];
 
@@ -186,18 +186,17 @@ void x264_me_search_ref( x264_t *h, x264_me_t *m, int (*mvc)[2], int i_mvc, int
     /* try extra predictors if provided */
     if( h->mb.i_subpel_refine >= 3 )
     {
+        uint32_t bmv = pack16to32_mask(bmx,bmy);
         COST_MV_HPEL( bmx, bmy );
-        for( i = 0; i < i_mvc; i++ )
+        do
         {
-            int mx = mvc[i][0];
-            int my = mvc[i][1];
-            if( (mx | my) && ((mx-bmx) | (my-bmy)) )
+            if( *(uint32_t*)mvc[i] && (bmv - *(uint32_t*)mvc[i]) )
             {
-                mx = x264_clip3( mx, mv_x_min*4, mv_x_max*4 );
-                my = x264_clip3( my, mv_y_min*4, mv_y_max*4 );
+                int mx = x264_clip3( mvc[i][0], mv_x_min*4, mv_x_max*4 );
+                int my = x264_clip3( mvc[i][1], mv_y_min*4, mv_y_max*4 );
                 COST_MV_HPEL( mx, my );
             }
-        }
+        } while( ++i < i_mvc );
         bmx = ( bpred_mx + 2 ) >> 2;
         bmy = ( bpred_my + 2 ) >> 2;
         COST_MV( bmx, bmy );
@@ -206,10 +205,14 @@ void x264_me_search_ref( x264_t *h, x264_me_t *m, int (*mvc)[2], int i_mvc, int
     {
         /* check the MVP */
         COST_MV( pmx, pmy );
-        /* I don't know why this helps */
-        bcost -= BITS_MVD(bmx,bmy);
-        
-        for( i = 0; i < i_mvc; i++ )
+        /* Because we are rounding the predicted motion vector to fullpel, there will be
+         * an extra MV cost in 15 out of 16 cases.  However, when the predicted MV is
+         * chosen as the best predictor, it is often the case that the subpel search will
+         * result in a vector at or next to the predicted motion vector.  Therefore, it is
+         * sensible to remove the cost of the MV from the rounded MVP to avoid unfairly
+         * biasing against use of the predicted motion vector. */
+        bcost -= BITS_MVD( pmx, pmy );
+        do
         {
             int mx = (mvc[i][0] + 2) >> 2;
             int my = (mvc[i][1] + 2) >> 2;
@@ -219,23 +222,23 @@ void x264_me_search_ref( x264_t *h, x264_me_t *m, int (*mvc)[2], int i_mvc, int
                 my = x264_clip3( my, mv_y_min, mv_y_max );
                 COST_MV( mx, my );
             }
-        }
+        } while( ++i < i_mvc );
     }
-    
     COST_MV( 0, 0 );
 
     switch( h->mb.i_me_method )
     {
     case X264_ME_DIA:
         /* diamond search, radius 1 */
-        for( i = 0; i < i_me_range; i++ )
+        i = 0;
+        do
         {
             DIA1_ITER( bmx, bmy );
-            if( bmx == omx && bmy == omy )
+            if( (bmx == omx) & (bmy == omy) )
                 break;
             if( !CHECK_MVRANGE(bmx, bmy) )
                 break;
-        }
+        } while( ++i < i_me_range );
         break;
 
     case X264_ME_HEX:
@@ -385,9 +388,7 @@ me_hex2:
                             + abs( m->mvp[1] - mvc[0][1] );
                         denom++;
                     }
-                    for( i = 0; i < i_mvc-1; i++ )
-                        mvd += abs( mvc[i][0] - mvc[i+1][0] )
-                             + abs( mvc[i][1] - mvc[i+1][1] );
+                    mvd += x264_predictor_difference( mvc, i_mvc );
                 }
 
                 sad_ctx = SAD_THRESH(1000) ? 0
@@ -404,19 +405,13 @@ me_hex2:
              * we are still centered on the same place as the DIA2. is this desirable? */
             CROSS( cross_start, i_me_range, i_me_range/2 );
 
-            /* 5x5 ESA */
-            omx = bmx; omy = bmy;
-            if( bcost != ucost2 )
-                COST_MV_X4(  1, 0,  0, 1, -1, 0,  0,-1 );
-            COST_MV_X4(  1, 1, -1, 1, -1,-1,  1,-1 );
-            COST_MV_X4(  2,-1,  2, 0,  2, 1,  2, 2 );
-            COST_MV_X4(  1, 2,  0, 2, -1, 2, -2, 2 );
-            COST_MV_X4( -2, 1, -2, 0, -2,-1, -2,-2 );
-            COST_MV_X4( -1,-2,  0,-2,  1,-2,  2,-2 );
+            COST_MV_X4( -2,-2, -2,2, 2,-2, 2,2 );
 
             /* hexagon grid */
             omx = bmx; omy = bmy;
-            for( i = 1; i <= i_me_range/4; i++ )
+
+            i = 1;
+            do
             {
                 static const int hex4[16][2] = {
                     {-4, 2}, {-4, 1}, {-4, 0}, {-4,-1}, {-4,-2},
@@ -443,7 +438,7 @@ me_hex2:
                     COST_MV_X4(  4*i, 1*i,  4*i, 2*i,  2*i, 3*i,  0*i, 4*i );
                     COST_MV_X4( -2*i, 3*i, -2*i,-3*i,  0*i,-4*i,  2*i,-3*i );
                 }
-            }
+            } while( ++i <= i_me_range/4 );
             if( bmy <= mv_y_max )
                 goto me_hex2;
             break;
@@ -469,9 +464,12 @@ me_hex2:
             /* successive elimination by comparing DC before a full SAD,
              * because sum(abs(diff)) >= abs(diff(sum)). */
             const int stride = m->i_stride[0];
-            static uint8_t zero[16*16] = {0,};
             uint16_t *sums_base = m->integral;
-            DECLARE_ALIGNED( int, enc_dc[4], 16 );
+            /* due to a GCC bug on some platforms (win32?), zero[] may not actually be aligned.
+             * unlike the similar case in ratecontrol.c, this is not a problem because it is not used for any
+             * SSE instructions and the only loss is a tiny bit of performance. */
+            DECLARE_ALIGNED_16( static uint8_t zero[8*FENC_STRIDE] );
+            DECLARE_ALIGNED_16( int enc_dc[4] );
             int sad_size = i_pixel <= PIXEL_8x8 ? PIXEL_8x8 : PIXEL_4x4;
             int delta = x264_pixel_size[sad_size].w;
             int16_t xs_buf[64];
@@ -504,6 +502,8 @@ me_hex2:
                 for( my = min_y; my <= max_y; my++ )
                 {
                     int ycost = p_cost_mvy[my<<2];
+                    if( bsad <= ycost )
+                        continue;
                     bsad -= ycost;
                     xn = h->pixf.ads[i_pixel]( enc_dc, sums_base + min_x + my * stride, delta,
                                                cost_fpel_mvx+min_x, xs, width, bsad*17/16 );
@@ -550,7 +550,13 @@ me_hex2:
                     for( i=0; i<nmvsad && mvsads[i].sad <= bsad; i++ );
                     for( j=i; j<nmvsad; j++ )
                         if( mvsads[j].sad <= bsad )
-                            mvsads[i++] = mvsads[j];
+                        {
+                            /* mvsad_t is not guaranteed to be 8 bytes on all archs, so check before using explicit write-combining */
+                            if( sizeof( mvsad_t ) == sizeof( uint64_t ) )
+                                *(uint64_t*)&mvsads[i++] = *(uint64_t*)&mvsads[j];
+                            else
+                                mvsads[i++] = mvsads[j];
+                        }
                     nmvsad = i;
                 }
                 if( nmvsad > limit )
@@ -562,7 +568,12 @@ me_hex2:
                         for( j=i+1; j<nmvsad; j++ )
                             COPY2_IF_LT( bsad, mvsads[j].sad, bj, j );
                         if( bj > i )
-                            XCHG( mvsad_t, mvsads[i], mvsads[bj] );
+                        {
+                            if( sizeof( mvsad_t ) == sizeof( uint64_t ) )
+                                XCHG( uint64_t, *(uint64_t*)&mvsads[i], *(uint64_t*)&mvsads[bj] );
+                            else
+                                XCHG( mvsad_t, mvsads[i], mvsads[bj] );
+                        }
                     }
                     nmvsad = limit;
                 }
@@ -575,12 +586,15 @@ me_hex2:
                 // just ADS and SAD
                 for( my = min_y; my <= max_y; my++ )
                 {
-                    bcost -= p_cost_mvy[my<<2];
+                    int ycost = p_cost_mvy[my<<2];
+                    if( bcost <= ycost )
+                        continue;
+                    bcost -= ycost;
                     xn = h->pixf.ads[i_pixel]( enc_dc, sums_base + min_x + my * stride, delta,
                                                cost_fpel_mvx+min_x, xs, width, bcost );
                     for( i=0; i<xn-2; i+=3 )
                         COST_MV_X3_ABS( min_x+xs[i],my, min_x+xs[i+1],my, min_x+xs[i+2],my );
-                    bcost += p_cost_mvy[my<<2];
+                    bcost += ycost;
                     for( ; i<xn; i++ )
                         COST_MV( min_x+xs[i], my );
                 }
@@ -679,7 +693,7 @@ static void refine_subpel( x264_t *h, x264_me_t *m, int hpel_iters, int qpel_ite
     const int i_pixel = m->i_pixel;
     const int b_chroma_me = h->mb.b_chroma_me && i_pixel <= PIXEL_8x8;
 
-    DECLARE_ALIGNED( uint8_t, pix[2][32*18], 16 ); // really 17x17, but round up for alignment
+    DECLARE_ALIGNED_16( uint8_t pix[2][32*18] ); // really 17x17, but round up for alignment
     int omx, omy;
     int i;
 
@@ -688,13 +702,12 @@ static void refine_subpel( x264_t *h, x264_me_t *m, int hpel_iters, int qpel_ite
     int bcost = m->cost;
     int odir = -1, bdir;
 
-
     /* try the subpel component of the predicted mv */
     if( hpel_iters && h->mb.i_subpel_refine < 3 )
     {
         int mx = x264_clip3( m->mvp[0], h->mb.mv_min_spel[0], h->mb.mv_max_spel[0] );
         int my = x264_clip3( m->mvp[1], h->mb.mv_min_spel[1], h->mb.mv_max_spel[1] );
-        if( mx != bmx || my != bmy )
+        if( (mx-bmx)|(my-bmy) )
             COST_MV_SAD( mx, my );
     }
 
@@ -714,7 +727,7 @@ static void refine_subpel( x264_t *h, x264_me_t *m, int hpel_iters, int qpel_ite
         COPY2_IF_LT( bcost, costs[1] + p_cost_mvx[omx  ] + p_cost_mvy[omy+2], bmy, omy+2 );
         COPY3_IF_LT( bcost, costs[2] + p_cost_mvx[omx-2] + p_cost_mvy[omy  ], bmx, omx-2, bmy, omy );
         COPY3_IF_LT( bcost, costs[3] + p_cost_mvx[omx+2] + p_cost_mvy[omy  ], bmx, omx+2, bmy, omy );
-        if( bmx == omx && bmy == omy )
+        if( (bmx == omx) & (bmy == omy) )
             break;
     }
 
@@ -783,13 +796,13 @@ static void refine_subpel( x264_t *h, x264_me_t *m, int hpel_iters, int qpel_ite
     BIME_CACHE(-(a),-(b))
 
 #define COST_BIMV_SATD( m0x, m0y, m1x, m1y ) \
-if( pass == 0 || !visited[(m0x)&7][(m0y)&7][(m1x)&7][(m1y)&7] ) \
+if( pass == 0 || !((visited[(m0x)&7][(m0y)&7][(m1x)&7] & (1<<((m1y)&7)))) ) \
 { \
     int cost; \
     int i0 = 4 + 3*(m0x-om0x) + (m0y-om0y); \
     int i1 = 4 + 3*(m1x-om1x) + (m1y-om1y); \
-    visited[(m0x)&7][(m0y)&7][(m1x)&7][(m1y)&7] = 1; \
-    memcpy( pix, pix0[i0], bs ); \
+    visited[(m0x)&7][(m0y)&7][(m1x)&7] |= (1<<((m1y)&7));\
+    h->mc.memcpy_aligned( pix, pix0[i0], bs ); \
     if( i_weight == 32 ) \
         h->mc.avg[i_pixel]( pix, bw, pix1[i1], bw ); \
     else \
@@ -830,17 +843,18 @@ int x264_me_refine_bidir( x264_t *h, x264_me_t *m0, x264_me_t *m1, int i_weight
     const int16_t *p_cost_m0y = m0->p_cost_mv - x264_clip3( m0->mvp[1], h->mb.mv_min_spel[0], h->mb.mv_max_spel[0] );
     const int16_t *p_cost_m1x = m1->p_cost_mv - x264_clip3( m1->mvp[0], h->mb.mv_min_spel[0], h->mb.mv_max_spel[0] );
     const int16_t *p_cost_m1y = m1->p_cost_mv - x264_clip3( m1->mvp[1], h->mb.mv_min_spel[0], h->mb.mv_max_spel[0] );
-    DECLARE_ALIGNED( uint8_t, pix0[9][16*16], 16 );
-    DECLARE_ALIGNED( uint8_t, pix1[9][16*16], 16 );
-    DECLARE_ALIGNED( uint8_t, pix[16*16], 16 );
+    DECLARE_ALIGNED_16( uint8_t pix0[9][16*16] );
+    DECLARE_ALIGNED_16( uint8_t pix1[9][16*16] );
+    DECLARE_ALIGNED_16( uint8_t pix[16*16] );
     int bm0x = m0->mv[0], om0x = bm0x;
     int bm0y = m0->mv[1], om0y = bm0y;
     int bm1x = m1->mv[0], om1x = bm1x;
     int bm1y = m1->mv[1], om1y = bm1y;
     int bcost = COST_MAX;
     int pass = 0;
-    uint8_t visited[8][8][8][8];
-    memset( visited, 0, sizeof(visited) );
+    /* each byte of visited represents 8 possible m1y positions, so a 4D array isn't needed */
+    uint8_t visited[8][8][8];
+    h->mc.memzero_aligned( visited, sizeof(visited) );
 
     BIME_CACHE( 0, 0 );
     CHECK_BIDIR( 0, 0, 0, 0 );
@@ -899,9 +913,8 @@ int x264_me_refine_bidir( x264_t *h, x264_me_t *m0, x264_me_t *m1, int i_weight
 { \
     if( satd <= bsatd * SATD_THRESH )\
     { \
-        int cost; \
-        cache_mv[0] = cache_mv2[0] = mx; \
-        cache_mv[1] = cache_mv2[1] = my; \
+        uint64_t cost; \
+        *(uint32_t*)cache_mv = *(uint32_t*)cache_mv2 = pack16to32_mask(mx,my); \
         cost = x264_rd_cost_part( h, i_lambda2, i8, m->i_pixel ); \
         COPY4_IF_LT( bcost, cost, bmx, mx, bmy, my, dir, do_dir?mdir:dir ); \
     } \
@@ -920,8 +933,8 @@ void x264_me_refine_qpel_rd( x264_t *h, x264_me_t *m, int i_lambda2, int i8 )
     const int bh = x264_pixel_size[m->i_pixel].h>>2;
     const int i_pixel = m->i_pixel;
 
-    DECLARE_ALIGNED( uint8_t, pix[16*16], 16 );
-    int bcost = m->i_pixel == PIXEL_16x16 ? m->cost : COST_MAX;
+    DECLARE_ALIGNED_16( uint8_t pix[16*16] );
+    uint64_t bcost = m->i_pixel == PIXEL_16x16 ? m->cost : COST_MAX64;
     int bmx = m->mv[0];
     int bmy = m->mv[1];
     int omx = bmx;
@@ -939,7 +952,7 @@ void x264_me_refine_qpel_rd( x264_t *h, x264_me_t *m, int i_lambda2, int i8 )
     p_cost_mvx = m->p_cost_mv - pmx;
     p_cost_mvy = m->p_cost_mv - pmy;
     COST_MV_SATD( bmx, bmy, bsatd );
-    COST_MV_RD( bmx, bmy, 0, 0, 0);
+    COST_MV_RD( bmx, bmy, 0, 0, 0 );
 
     /* check the predicted mv */
     if( (bmx != pmx || bmy != pmy)
@@ -985,7 +998,7 @@ void x264_me_refine_qpel_rd( x264_t *h, x264_me_t *m, int i_lambda2, int i8 )
     m->cost = bcost;
     m->mv[0] = bmx;
     m->mv[1] = bmy;
-    x264_macroblock_cache_mv ( h, 2*(i8&1), i8&2, bw, bh, 0, bmx, bmy );
-    x264_macroblock_cache_mvd( h, 2*(i8&1), i8&2, bw, bh, 0, bmx - pmx, bmy - pmy );
+    x264_macroblock_cache_mv ( h, 2*(i8&1), i8&2, bw, bh, 0, pack16to32_mask(bmx, bmy) );
+    x264_macroblock_cache_mvd( h, 2*(i8&1), i8&2, bw, bh, 0, pack16to32_mask(bmx - pmx, bmy - pmy) );
 }