/*****************************************************************************
* 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
*
* 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"
* 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},
}\
}
-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;
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];
/* 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 );
{
/* 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;
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:
+ 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
* 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},
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;
/* 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];
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 );
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 )
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;
}
// 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 );
}
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;
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 );
}
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;
}
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 \
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 );
{ \
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 ); \
} \
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;
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)
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) );
}