View Full Version : [New patch] Improved multi-hexagon search
Dark Shikari
12th September 2007, 00:04
http://i14.tinypic.com/4vrbc6w.png
(Note the FPS values are singlethreaded on 720p footage on a Core 2 2Ghz)
IMH: its a new motion search algorithm aimed to sit between ESA and UMH in terms of speed and quality. Its basically what I came up with after trashing simulated annealing for not really working that well.
Its main advantage other than its speed and quality fitting that niche between UMH and ESA is that unlike ESA it can be multithreaded, which means it is much more practical for everyday encoding.
The algorithm is somewhat complex, but not overly so:
1. Run the regular UMH search, but instead of ending with HEX, go to Improved Multi-Hex instead.
2. If the SAD cost of the current MV is below 100, terminate and jump to ordinary HEX instead. This increases speed at little quality loss.
3. Do an ordinary hex search.
4. Create a radius-8 hexagon around the current MV. Run short-range hex searches on all those. Note that throughout this we never move the current MV; the best that's found is saved once we're done, so all the searches are centered around the same point.
Also note that when the modified HEX search finds a new MV better than all previous ones and terminates due to maxing its range rather than hitting a dead end (i.e. better MVs could be around the corner), it will continue for another full range. This allows shorter ranges to be specified in the IMH algorithm while ensuring that if a good candidate is found, it isn't ignored.
5. For every 8 MERange starting with 16, do another hexagon around the current MV. The range of these gets longer the farther out you go, as the hexagons get farther and farther apart. However, unlike the earlier hexagons, these are adaptively chosen--if the SAD cost of the center of the starting hexagon is over twice that of the best SAD cost so far, the search is skipped. This loses some quality, but vastly increases speed, especially at high MEranges.
6. Finally, save the best result found so far and end the motion search.
Patch link (http://tjhsst.edu/~jgarrett/IMH.diff) (Works with Hadamard patch only)
Patch link 2 (http://tjhsst.edu/~jgarrett/IHM noHadamard.diff) (Works without Hadamard patch only)
The difference between the two patches is only 7 characters; its a single line that conflicted, so no big deal.
(Edit: Minor typo fixed in both patches. It would have kept the code from compiling though.)
akupenguin
12th September 2007, 00:16
Also note that when the modified HEX search finds a new MV better than all previous ones and terminates due to maxing its range rather than hitting a dead end (i.e. better MVs could be around the corner), it will continue for another full range. This allows shorter ranges to be specified in the IMH algorithm while ensuring that if a good candidate is found, it isn't ignored.
Might it be better to just remove the range limitation on HEX?
If the SAD cost of the current MV is below 100, terminate and jump to ordinary HEX instead.
Is 100 supposed to be independent of block size? 16x16 and 4x4 blocks each with SAD=100 are very different match qualities.
Dark Shikari
12th September 2007, 00:23
Might it be better to just remove the range limitation on HEX?
No, that makes it far too slow for use in IMH.
When running a plain HEX search, --merange 16 is plenty to reach the optimal result 99% of the time.
In IMH, I use ranges like 2, 4, and 6--much lower numbers. I can't afford to run the full HEX search as it takes too long, since I'm running so many HEX searches. Its better to do a bunch of short ones, and run it longer on the best ones.
Also, I found that if I made it so that when a HEX found a new best MV and ran out of range, the range was eliminated, the benefit was basically zero over my current method.
Is 100 supposed to be independent of block size? 16x16 and 4x4 blocks each with SAD=100 are very different match qualities.That's a good idea; I overlooked that, since --me imh is indeed doing the PIXEL_4x4 motion searches.
RaynQuist
12th September 2007, 04:53
How are you computing the percentages?
Are you still planning on running exhaustive RDO?
Dark Shikari
12th September 2007, 05:04
How are you computing the percentages?
Its % SSIM increase over --me hex --merange 16.
To be exact, its 1 - (1/(1-NewSSIM)) / (1/(1-OldSSIM)).
Are you still planning on running exhaustive RDO?An exhaustive RDO search would require me understanding how to call Qpel RDO in an arbitrary manner on any block type, and I don't know the code well enough to do that.
Dark Shikari
12th September 2007, 06:25
Is 100 supposed to be independent of block size? 16x16 and 4x4 blocks each with SAD=100 are very different match qualities.
Changing the code here to SAD_THRESH gave a small quality boost per bitrate. Measurable, at least. Higher or lower doesn't seem to help though, so I had the number nearly right it seems.
Sharktooth
12th September 2007, 16:32
http://tjhsst.edu/~jgarrett/IMH%20noHadamard.diff ---> 404
Dark Shikari
12th September 2007, 16:35
http://tjhsst.edu/~jgarrett/IMH%20noHadamard.diff ---> 404
I mispelled the filename, but their SSH server is down so I can't fix it. So I've fixed the link here instead.
Sagittaire
12th September 2007, 18:12
I mispelled the filename, but their SSH server is down so I can't fix it. So I've fixed the link here instead.
Where is the correct hadamard patch. x264_satd_fpel.07.diff from aku seem not work with IMH.diff
Dark Shikari
12th September 2007, 18:22
Where is the correct hadamard patch. x264_satd_fpel.07.diff from aku seem not work with IMH.diffI was basing it on my own --hadamard patch--it should take very little modification to make it work with Aku's though; no algorithms have to be changed, just the fact that they probably modify one or two lines of the same code in non-conflicting ways.
Romario
13th September 2007, 00:01
When we can expect build, based on these patches? Sharktooth?
Sharktooth
13th September 2007, 00:02
:search:
there's already one...
Dark Shikari
13th September 2007, 00:39
Also, this is still in the pipeline (I'll post it later once its more finalized, like IMH is now), but I have an experimental --subme 8 algorithm that gives 0.6% better quality per bitrate than --subme 7 but slows encoding down by a mere 2.2%. Not surprisingly, it consists of even more hexagons, replacing the diamond RD searches with a set of much more accurate HEX algorithms.
Sharktooth
13th September 2007, 00:51
i think there's no reason to add subme 8. you can replace subme 7 with the new algo.
Dark Shikari
13th September 2007, 01:00
i think there's no reason to add subme 8. you can replace subme 7 with the new algo.
True--the difference between 7 and 8 in terms of speed is minute (now down to 1.8% with a small optimization).
#define COST_MV_RD2( mx, my, satd) \
{ \
if( satd <= bsatd * SATD_THRESH)\
{ \
int cost; \
cache_mv[0] = cache_mv2[0] = mx; \
cache_mv[1] = cache_mv2[1] = my; \
cost = x264_rd_cost_part( h, i_lambda2, i8, m->i_pixel ); \
COPY3_IF_LT( bcost, cost, bmx, mx, bmy, my ); \
} \
}
#define COST_MV_RD_X3_DIR( m0x, m0y, m1x, m1y, m2x, m2y, costs) \
{ \
COST_MV_SATD(m0x+omx,m0y+omy, satds[0]);\
COST_MV_SATD(m1x+omx,m1y+omy, satds[1]);\
COST_MV_SATD(m2x+omx,m2y+omy, satds[2]);\
if( satds[0] <= bsatd * SATD_THRESH) \
{ \
cache_mv[0] = cache_mv2[0] = m0x+omx; \
cache_mv[1] = cache_mv2[1] = m0y+omy; \
int cost = x264_rd_cost_part( h, i_lambda2, i8, m->i_pixel ); \
(costs)[0] = cost;\
COPY3_IF_LT( bcost, cost, bmx, m0x+omx, bmy, m0y+omy ); \
} \
else (costs)[0] = COST_MAX;\
if( satds[1] <= bsatd * SATD_THRESH) \
{ \
cache_mv[0] = cache_mv2[0] = m1x+omx; \
cache_mv[1] = cache_mv2[1] = m1y+omy; \
int cost = x264_rd_cost_part( h, i_lambda2, i8, m->i_pixel ); \
(costs)[1] = cost;\
COPY3_IF_LT( bcost, cost, bmx, m1x+omx, bmy, m1y+omy ); \
} \
else (costs)[1] = COST_MAX;\
if( satds[2] <= bsatd * SATD_THRESH) \
{ \
cache_mv[0] = cache_mv2[0] = m2x+omx; \
cache_mv[1] = cache_mv2[1] = m2y+omy; \
int cost = x264_rd_cost_part( h, i_lambda2, i8, m->i_pixel ); \
(costs)[2] = cost;\
COPY3_IF_LT( bcost, cost, bmx, m2x+omx, bmy, m2y+omy ); \
} \
else (costs)[2] = COST_MAX;\
}
void x264_me_refine_qpel_rd2( x264_t *h, x264_me_t *m, int i_lambda2, int i8 )
{
// don't have to fill the whole mv cache rectangle
static const int pixel_mv_offs[] = { 0, 4, 4*8, 0 };
int16_t *cache_mv = h->mb.cache.mv[0][x264_scan8[i8*4]];
int16_t *cache_mv2 = cache_mv + pixel_mv_offs[m->i_pixel];
const int16_t *p_cost_mvx, *p_cost_mvy;
const int bw = x264_pixel_size[m->i_pixel].w>>2;
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;
int bmx = m->mv[0];
int bmy = m->mv[1];
int pmx, pmy, omx, omy, i;
unsigned bsatd, satds[6];
if( m->i_pixel != PIXEL_16x16 && i8 != 0 )
x264_mb_predict_mv( h, 0, i8*4, bw, m->mvp );
pmx = m->mvp[0];
pmy = m->mvp[1];
p_cost_mvx = m->p_cost_mv - pmx;
p_cost_mvy = m->p_cost_mv - pmy;
COST_MV_SATD( bmx, bmy, bsatd );
COST_MV_RD2( bmx, bmy, 0);
/* check the predicted mv */
if( (bmx != pmx || bmy != pmy)
&& pmx >= h->mb.mv_min_spel[0] && pmx <= h->mb.mv_max_spel[0]
&& pmy >= h->mb.mv_min_spel[1] && pmy <= h->mb.mv_max_spel[1] )
{
int satd;
COST_MV_SATD( pmx, pmy, satd );
COST_MV_RD2( pmx, pmy, satd);
}
omx = bmx;
omy = bmy;
/*wide subpel hex search */
for( i = 2; i < 10; i+=2 )
{
if( bmy > h->mb.mv_max_spel[1] - 2 )
break;
omx = bmx;
omy = bmy;
COST_MV_SATD( omx, omy - 2*i, satds[0] );
COST_MV_SATD( omx, omy + 2*i, satds[1] );
COST_MV_SATD( omx - 2*i, omy + 1*i, satds[2] );
COST_MV_SATD( omx - 2*i, omy - 1*i, satds[3] );
COST_MV_SATD( omx + 2*i, omy + 1*i, satds[4] );
COST_MV_SATD( omx + 2*i, omy - 1*i, satds[5] );
COST_MV_RD2( omx, omy - 2*i, satds[0]);
COST_MV_RD2( omx, omy + 2*i, satds[1]);
COST_MV_RD2( omx - 2*i, omy + 1*i, satds[2]);
COST_MV_RD2( omx - 2*i, omy - 1*i, satds[3]);
COST_MV_RD2( omx + 2*i, omy + 1*i, satds[4]);
COST_MV_RD2( omx + 2*i, omy - 1*i, satds[5]);
}
/* subpel hex search */
int dir = -2;
int costs[6];
COST_MV_RD_X3_DIR( -2,0, -1, 2, 1, 2, costs );
COST_MV_RD_X3_DIR( 2,0, 1,-2, -1,-2, costs+3 );
if( dir != -2 )
{
static const int hex2[8][2] = {{-1,-2}, {-2,0}, {-1,2}, {1,2}, {2,0}, {1,-2}, {-1,-2}, {-2,0}};
bmx += hex2[dir+1][0];
bmy += hex2[dir+1][1];
/* half hexagon, not overlapping the previous iteration */
for( i = 1; i < 10; i++ )
{
if( bmy > h->mb.mv_max_spel[1] - 1 )
break;
static const int mod6[8] = {5,0,1,2,3,4,5,0};
const int odir = mod6[dir+1];
COST_MV_RD_X3_DIR( hex2[odir+0][0], hex2[odir+0][1],
hex2[odir+1][0], hex2[odir+1][1],
hex2[odir+2][0], hex2[odir+2][1],
costs );
dir = -2;
COPY2_IF_LT( bcost, costs[0], dir, odir-1 );
COPY2_IF_LT( bcost, costs[1], dir, odir );
COPY2_IF_LT( bcost, costs[2], dir, odir+1 );
if( dir == -2 )
break;
bmx += hex2[dir+1][0];
bmy += hex2[dir+1][1];
}
}
//square refine
omx = bmx;
omy = bmy;
COST_MV_SATD( omx, omy - 1, satds[0] );
COST_MV_SATD( omx, omy + 1, satds[1] );
COST_MV_SATD( omx - 1, omy, satds[2] );
COST_MV_SATD( omx + 1, omy, satds[3] );
COST_MV_RD2( omx, omy - 1, satds[0]);
COST_MV_RD2( omx, omy + 1, satds[1]);
COST_MV_RD2( omx - 1, omy, satds[2]);
COST_MV_RD2( omx + 1, omy, satds[3]);
COST_MV_SATD( omx - 1, omy - 1, satds[0] );
COST_MV_SATD( omx + 1, omy + 1, satds[1] );
COST_MV_SATD( omx - 1, omy + 1, satds[2] );
COST_MV_SATD( omx + 1, omy - 1, satds[3] );
COST_MV_RD2( omx - 1, omy - 1, satds[0]);
COST_MV_RD2( omx + 1, omy + 1, satds[1]);
COST_MV_RD2( omx - 1, omy + 1, satds[2]);
COST_MV_RD2( omx + 1, omy - 1, satds[3]);
if( bmy > h->mb.mv_max_spel[1] )
bmy = h->mb.mv_max_spel[1];
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 );
}
Algorithm:
1. Do the normal initialization and other such stuff done in the regular algorithm. Check the predicted MV, etc.
2. Do a wide hexagon search starting at radius 4 and going to radius 16 (quarterpel numbers, so that's actually 2 to 4). Note that in each pass the center of the hexagon resets to the best vector so far. This might not seem optimal, but testing shows it is considerably better than not doing so, and that the hexagon search here actually improves quality by 0.2% and increases speed because it reduces the number of iterations in step 3.
3. Do an ordinary HEX search starting at the best vector so far, just like in ME HEX.
4. Do a square refine.
5. Finish up.
If there's anything potentially unstable about the algorithm (going out of MVrange, array out of bounds, etc), say so.
Also note that another small boost comes from a change to this matrix:
/* presets selected from good points on the speed-vs-quality curve of several test videos
* subpel_iters[i_subpel_refine] = { refine_hpel, refine_qpel, me_hpel, me_qpel }
* where me_* are the number of EPZS iterations run on all candidate block types,
* and refine_* are run only on the winner. */
static const int subpel_iterations[][4] =
{{1,0,0,0},
{1,1,0,0},
{0,1,1,0},
{0,2,1,0},
{0,2,1,1},
{0,2,1,2},
{0,0,2,2},
{0,0,2,2},
{0,0,4,10}};
Note the extremely high Hpel/Qpel values for --subme 8 (which would become --subme 7). Compared to the cost of the RD search, these take very little time. Also note that the searches called for here are only run until they stop giving any benefit, just like ME HEX, so high values merely serve to help in the rare cases when they would create benefit, and create no speed penalty in any other situation. Also note that these iterations may actually be speeding up the RD algorithm I'm using because they get us closer to the ideal value, meaning fewer HEX iterations are required.
Terranigma
13th September 2007, 01:23
I so can't wait to try this. :)
Dark Shikari
13th September 2007, 01:31
I so can't wait to try this. :)I might even be able to hex-optimize the ordinary Qpel search :p.
The reason this works is because since the qpel resizing filter does modify the image, the best qpel location is not necessarily right next to the best fullpel location. This means that a plain old diamond search to find the best nearby qpel location might not be the best idea, since the best qpel location might be 1 or 2 pixels away.
Whoever coded the radius-2 hexagon algorithm used in ME_HEX (aku?) did a very good job; its an extremely efficient algorithm and though its not the best quality for a wide search since its only a gradient descent algorithm, it makes up for that with its speed and as such can be used, well, all over the place. I might even create a macro that allows me to specify the comparison function for the hex algorithm to use, so I can call it arbitrarily without even copy-pasting the code.
I've found that in some cases HEX is actually faster than DIA search, because Hex jumps two spaces in each iteration with 3 block comparisons per iteration, while DIA jumps one space with 2 block comparisons per iteration, so HEX actually moves 33% faster. Most of the reason HEX can be slower is because it finds better results than DIA, and so has to continue for a longer time in order to find the best result.
Sagekilla
13th September 2007, 04:27
Wow Shikari, have you got enough hexagon searches yet? :p Oh well, if works, keep doing it!
Dark Shikari
13th September 2007, 05:24
Wow Shikari, have you got enough hexagon searches yet? :p Oh well, if works, keep doing it!
Well after adding --b-rdo to my commandline x264 crashes about 200 frames in with the above code.
So I'm guessing there's some corner case that causes a segfault or such, probably due to my complete laziness in (not) checking MVranges. It'll probably take me a while to find it :p
And no, there is no such thing as too many hexagons. Its pretty much the optimal search shape for speed and quality in my (limited) experience.
Dark Shikari
13th September 2007, 07:17
Bug fixed, it was a variable initialization bug :rolleyes:
Code updated.
Dark Shikari
13th September 2007, 08:58
New subme code updated with a new fix that gave another 0.15% quality for about 0.6% speed loss.
Current gain over --subme 7 is about 0.75% quality for the cost of 2.5% speed.
DeathTheSheep
13th September 2007, 17:11
That is quite random. And unexpected. And interesting... :cool:
What, is there suddenly a subme 8?
You realize you are truly pleasing (and to no small degree) those folks interested in what are termed "insane" settings. Nice. ;)
Dark Shikari
13th September 2007, 17:15
That is quite random. And unexpected. And interesting... :cool:
What, is there suddenly a subme 8?
You realize you are truly pleasing (and to no small degree) those folks interested in what are termed "insane" settings. Nice. ;)On sharktooth's advice I'm just going to have it replace --subme 7 since its only marginally slower and offers 50% more quality boost over --subme 6 than normal --subme 7 does.
And yes, I know I'm making people with OCD (Obsessive Compressive Disorder) happy ;)
Sharktooth
13th September 2007, 17:15
well i think the new algo can replace subme 7... there's no reason to use a new subme mode since the speed difference is really low.
Terranigma
13th September 2007, 17:30
wizboy11, interested in compiling a build with this patch and hadamard? ;)
RaynQuist
13th September 2007, 18:54
That is quite random. And unexpected. And interesting... :cool:
What, is there suddenly a subme 8?
You realize you are truly pleasing (and to no small degree) those folks interested in what are termed "insane" settings. Nice. ;)
Actually it's not pleasing me at all since I can no longer blindly use the slowest options and must find a good trade-off point.
Dark Shikari
13th September 2007, 18:58
Actually it's not pleasing me at all since I can no longer blindly use the slowest options and must find a good trade-off point.
Well --me imh --hadamard --threads 2 will give you better speed on a dual core proc than --me esa with somewhat better results, since --me esa doesn't allow threads.
All this means is that --me esa --hadamard --ref 16 --merange 64 is somehow, somehow, not recommended ;)
akupenguin
13th September 2007, 19:02
Actually it's not pleasing me at all since I can no longer blindly use the slowest options and must find a good trade-off point.
That was always the case. Or did you blindly use --me esa --merange 512 ?
TheRyuu
13th September 2007, 20:03
wizboy11, interested in compiling a build with this patch and hadamard? ;)
Calling me out eh?
I'm getting a "malformed patch" error when I use the hadamard compatible one (and hadamard patch was already installed)
Is that ok?
And it won't compile either. :p
gcc -O4 -ffast-math -Wall -I. -DHAVE_MMX -DARCH_X86 -DSYS_MINGW -DHAVE_PTHREAD -s -fomit-frame-pointer -march=pentium2 -m32 -mfpmath=387 -c -o encoder/me.o encoder/me.c
encoder/me.c: In function 'x264_me_search_ref':
encoder/me.c:366: error: 'X264_ME_IMH' undeclared (first use in this function)
encoder/me.c:366: error: (Each undeclared identifier is reported only once
encoder/me.c:366: error: for each function it appears in.)
make: *** [encoder/me.o] Error 1
This is with the 667b source.
AGDenton
13th September 2007, 22:07
Here (http://agerschenfeld.free.fr/x264/x264_satdcmp_imh_aq.tbz)'s a SVN-675 version with IMH, akupenguin's satd_fpel.07, the IMH patch and the AQ patch thrown in for good measure.
It compiles fine, at least on Mac OS X.
I'd also like to add the new --subme 7, but I don't know where to inject the code...
AG
Dark Shikari
14th September 2007, 00:05
Here (http://agerschenfeld.free.fr/x264/x264_satdcmp_imh_aq.tbz)'s a SVN-675 version with IMH, akupenguin's satd_fpel.07, the IMH patch and the AQ patch thrown in for good measure.
It compiles fine, at least on Mac OS X.
I'd also like to add the new --subme 7, but I don't know where to inject the code...
AGI'll modify the patch in a bit to make it a --subme 7 instead of --subme 8, then you should be able to combine it easily.
DeathTheSheep
14th September 2007, 00:19
Great, please do. :) Your old patches didn't work as well, though (as wizboy and I can both attest to) with MingW's patch system. So we end up doing the whole thing by hand. :p
Akupenguin's patches work like a dream though...how does he do it?!
Sagekilla
14th September 2007, 00:25
That is quite random. And unexpected. And interesting... :cool:
What, is there suddenly a subme 8?
You realize you are truly pleasing (and to no small degree) those folks interested in what are termed "insane" settings. Nice. ;)
Oh yes he is, especially come christmas when I may upgrade to a quad core to get practically a 3x boost in raw power.
Edit: At this point, I've done nearly everything short of increasing the reference frames from 4 (which really makes -no- difference from my tests) and enabling ESA. Plus, with all the ME optimizations that Shikari has been up to lately, I'm starting to think using the (new) ESA and leaving refs at 4 would probably give me more quality and less speed loss then actually using the full 16 reference frames!
akupenguin
14th September 2007, 00:30
Akupenguin's patches work like a dream though...how does he do it?!
svn diff
DeathTheSheep
14th September 2007, 00:34
That's it??! Then why do the other patches behave differently? :eek:
akupenguin
14th September 2007, 00:48
Shikari ran instead
diff -r -u5 x264/ x264.edit/
which should also be valid, but isn't identical. I can apply his patches...
Unearthly
14th September 2007, 00:59
I managed to almost build it with VS, but unfortunately the SATD esa algorithm uses some C stuff that MS's compiler does not support (array lengths not defined at compile). I'll try to get around to installing Cygwin this weekend.
I tried using IMH with SATD with the ESA commented out for now, and I must say I like it so far. Although for sure you can say the benefit is not worth the speed, it still runs fast enough to run my encode overnight. :p
Dark Shikari
14th September 2007, 01:12
Oh yes he is, especially come christmas when I may upgrade to a quad core to get practically a 3x boost in raw power.
Edit: At this point, I've done nearly everything short of increasing the reference frames from 4 (which really makes -no- difference from my tests) and enabling ESA. Plus, with all the ME optimizations that Shikari has been up to lately, I'm starting to think using the (new) ESA and leaving refs at 4 would probably give me more quality and less speed loss then actually using the full 16 reference frames!Yup, that's the point of many of my low-speed-but-higher-quality patches; currently the only thing you can do to sacrifice speed for quality beyond a certain point is to enable more reference frames, which is usually just completely useless.
--me imh is mildly slower than --me umh for a much better gain than you'd get from going from --ref 4 to --ref 7, for example (probably a similar speed loss). --hadamard slows things down by about 50% for UMH and 60-70% for IMH, but is still better than the benefit from --ref 4 to --ref 16.
--me esa --hadamard is still crazy slow though :p
Dark Shikari
14th September 2007, 01:24
Here's your --subme 7 diff... it only affects one C file and it doesn't conflict at all with --hadamard or other patches as far as I can tell.
--- x264/encoder/me.c 2007-07-17 04:13:25.937500000 -0700
+++ x264/encoder/me.c 2007-09-13 17:24:40.390625000 -0700
@@ -27,19 +27,22 @@
/* presets selected from good points on the speed-vs-quality curve of several test videos
* subpel_iters[i_subpel_refine] = { refine_hpel, refine_qpel, me_hpel, me_qpel }
* where me_* are the number of EPZS iterations run on all candidate block types,
* and refine_* are run only on the winner. */
+ //The --subme 7 values are much higher because since they get the motion search
+ //closer to the optimal value, they actually tend to save time in the more intensive
+ //RD search that follows.
static const int subpel_iterations[][4] =
{{1,0,0,0},
{1,1,0,0},
{0,1,1,0},
{0,2,1,0},
{0,2,1,1},
{0,2,1,2},
{0,0,2,2},
- {0,0,2,2}};
+ {0,0,4,10}};
static void refine_subpel( x264_t *h, x264_me_t *m, int hpel_iters, int qpel_iters, int *p_halfpel_thresh, int b_refine_qpel );
#define BITS_MVD( mx, my )\
(p_cost_mvx[(mx)<<2] + p_cost_mvy[(my)<<2])
@@ -535,11 +538,11 @@
int hpel = subpel_iterations[h->mb.i_subpel_refine][0];
int qpel = subpel_iterations[h->mb.i_subpel_refine][1];
if( m->i_pixel <= PIXEL_8x8 && h->sh.i_type == SLICE_TYPE_P )
m->cost -= m->i_ref_cost;
-
+
refine_subpel( h, m, hpel, qpel, NULL, 1 );
}
#define COST_MV_SAD( mx, my ) \
{ \
@@ -799,25 +802,56 @@
dst = h->pixf.mbcmp[i_pixel]( m->p_fenc[0], FENC_STRIDE, src, stride ) \
+ p_cost_mvx[mx] + p_cost_mvy[my]; \
COPY1_IF_LT( bsatd, dst ); \
}
-#define COST_MV_RD( mx, my, satd, dir ) \
+#define COST_MV_RD( mx, my, satd) \
{ \
- if( satd <= bsatd * SATD_THRESH \
- && (dir^1) != odir \
- && (dir<0 || !p_visited[(mx)+(my)*16]) ) \
+ if( satd <= bsatd * SATD_THRESH)\
{ \
int cost; \
cache_mv[0] = cache_mv2[0] = mx; \
cache_mv[1] = cache_mv2[1] = my; \
cost = x264_rd_cost_part( h, i_lambda2, i8, m->i_pixel ); \
COPY3_IF_LT( bcost, cost, bmx, mx, bmy, my ); \
- if(dir>=0) p_visited[(mx)+(my)*16] = 1; \
} \
}
+#define COST_MV_RD_X3_DIR( m0x, m0y, m1x, m1y, m2x, m2y, costs) \
+{ \
+ COST_MV_SATD(m0x+omx,m0y+omy, satds[0]);\
+ COST_MV_SATD(m1x+omx,m1y+omy, satds[1]);\
+ COST_MV_SATD(m2x+omx,m2y+omy, satds[2]);\
+ if( satds[0] <= bsatd * SATD_THRESH) \
+ { \
+ cache_mv[0] = cache_mv2[0] = m0x+omx; \
+ cache_mv[1] = cache_mv2[1] = m0y+omy; \
+ int cost = x264_rd_cost_part( h, i_lambda2, i8, m->i_pixel ); \
+ (costs)[0] = cost;\
+ COPY3_IF_LT( bcost, cost, bmx, m0x+omx, bmy, m0y+omy ); \
+ } \
+ else (costs)[0] = COST_MAX;\
+ if( satds[1] <= bsatd * SATD_THRESH) \
+ { \
+ cache_mv[0] = cache_mv2[0] = m1x+omx; \
+ cache_mv[1] = cache_mv2[1] = m1y+omy; \
+ int cost = x264_rd_cost_part( h, i_lambda2, i8, m->i_pixel ); \
+ (costs)[1] = cost;\
+ COPY3_IF_LT( bcost, cost, bmx, m1x+omx, bmy, m1y+omy ); \
+ } \
+ else (costs)[1] = COST_MAX;\
+ if( satds[2] <= bsatd * SATD_THRESH) \
+ { \
+ cache_mv[0] = cache_mv2[0] = m2x+omx; \
+ cache_mv[1] = cache_mv2[1] = m2y+omy; \
+ int cost = x264_rd_cost_part( h, i_lambda2, i8, m->i_pixel ); \
+ (costs)[2] = cost;\
+ COPY3_IF_LT( bcost, cost, bmx, m2x+omx, bmy, m2y+omy ); \
+ } \
+ else (costs)[2] = COST_MAX;\
+}
+
#define SATD_THRESH 17/16
void x264_me_refine_qpel_rd( x264_t *h, x264_me_t *m, int i_lambda2, int i8 )
{
// don't have to fill the whole mv cache rectangle
@@ -826,100 +860,114 @@
int16_t *cache_mv2 = cache_mv + pixel_mv_offs[m->i_pixel];
const int16_t *p_cost_mvx, *p_cost_mvy;
const int bw = x264_pixel_size[m->i_pixel].w>>2;
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;
int bmx = m->mv[0];
int bmy = m->mv[1];
int pmx, pmy, omx, omy, i;
- int odir = -1, bdir;
- unsigned bsatd, satds[4];
-
- int visited[16*13] = {0}; // only need 13x13, but 16 is more convenient
- int *p_visited = &visited[6+6*16];
-
+ unsigned bsatd, satds[6];
+ omx = bmx;
+ omy = bmy;
if( m->i_pixel != PIXEL_16x16 && i8 != 0 )
x264_mb_predict_mv( h, 0, i8*4, bw, m->mvp );
pmx = m->mvp[0];
pmy = m->mvp[1];
p_cost_mvx = m->p_cost_mv - pmx;
p_cost_mvy = m->p_cost_mv - pmy;
COST_MV_SATD( bmx, bmy, bsatd );
- if( m->i_pixel != PIXEL_16x16 )
- COST_MV_RD( bmx, bmy, 0, -1 );
-
+ COST_MV_RD( bmx, bmy, 0);
+
/* check the predicted mv */
if( (bmx != pmx || bmy != pmy)
&& pmx >= h->mb.mv_min_spel[0] && pmx <= h->mb.mv_max_spel[0]
&& pmy >= h->mb.mv_min_spel[1] && pmy <= h->mb.mv_max_spel[1] )
{
int satd;
COST_MV_SATD( pmx, pmy, satd );
- COST_MV_RD( pmx, pmy, satd, -1 );
+ COST_MV_RD( pmx, pmy, satd);
}
-
- /* mark mv and mvp as visited */
- p_visited[0] = 1;
- p_visited -= bmx + bmy*16;
- {
- int mx = bmx ^ m->mv[0] ^ pmx;
- int my = bmy ^ m->mv[1] ^ pmy;
- if( abs(mx-bmx) < 7 && abs(my-bmy) < 7 )
- p_visited[mx + my*16] = 1;
- }
-
- /* hpel diamond */
- bdir = -1;
- for( i = 0; i < 2; i++ )
+
+ // Wide subpel hexagon search. The time it takes is basically negated
+ // by the time it saves in the hex search that follows, so its basically a free
+ // quality boost.
+ for( i = 2; i < 10; i+=2 )
{
if( bmy > h->mb.mv_max_spel[1] - 2 )
break;
omx = bmx;
omy = bmy;
- odir = bdir;
- COST_MV_SATD( omx, omy - 2, satds[0] );
- COST_MV_SATD( omx, omy + 2, satds[1] );
- COST_MV_SATD( omx - 2, omy, satds[2] );
- COST_MV_SATD( omx + 2, omy, satds[3] );
- COST_MV_RD( omx, omy - 2, satds[0], 0 );
- COST_MV_RD( omx, omy + 2, satds[1], 1 );
- COST_MV_RD( omx - 2, omy, satds[2], 2 );
- COST_MV_RD( omx + 2, omy, satds[3], 3 );
- if( bmx == omx && bmy == omy )
- break;
+ COST_MV_SATD( omx, omy - 2*i, satds[0] );
+ COST_MV_SATD( omx, omy + 2*i, satds[1] );
+ COST_MV_SATD( omx - 2*i, omy + 1*i, satds[2] );
+ COST_MV_SATD( omx - 2*i, omy - 1*i, satds[3] );
+ COST_MV_SATD( omx + 2*i, omy + 1*i, satds[4] );
+ COST_MV_SATD( omx + 2*i, omy - 1*i, satds[5] );
+ COST_MV_RD( omx, omy - 2*i, satds[0]);
+ COST_MV_RD( omx, omy + 2*i, satds[1]);
+ COST_MV_RD( omx - 2*i, omy + 1*i, satds[2]);
+ COST_MV_RD( omx - 2*i, omy - 1*i, satds[3]);
+ COST_MV_RD( omx + 2*i, omy + 1*i, satds[4]);
+ COST_MV_RD( omx + 2*i, omy - 1*i, satds[5]);
}
-
- /* qpel diamond */
- bdir = -1;
- for( i = 0; i < 2; i++ )
- {
- if( bmy > h->mb.mv_max_spel[1] - 1 )
- break;
- omx = bmx;
- omy = bmy;
- odir = bdir;
- COST_MV_SATD( omx, omy - 1, satds[0] );
- COST_MV_SATD( omx, omy + 1, satds[1] );
- COST_MV_SATD( omx - 1, omy, satds[2] );
- COST_MV_SATD( omx + 1, omy, satds[3] );
- COST_MV_RD( omx, omy - 1, satds[0], 0 );
- COST_MV_RD( omx, omy + 1, satds[1], 1 );
- COST_MV_RD( omx - 1, omy, satds[2], 2 );
- COST_MV_RD( omx + 1, omy, satds[3], 3 );
- if( bmx == omx && bmy == omy )
- break;
+ //Subpel hex search. Same as ME HEX except with RDO.
+ int dir = -2;
+ int costs[6];
+ COST_MV_RD_X3_DIR( -2,0, -1, 2, 1, 2, costs );
+ COST_MV_RD_X3_DIR( 2,0, 1,-2, -1,-2, costs+3 );
+ if( dir != -2 )
+ {
+ static const int hex2[8][2] = {{-1,-2}, {-2,0}, {-1,2}, {1,2}, {2,0}, {1,-2}, {-1,-2}, {-2,0}};
+ bmx += hex2[dir+1][0];
+ bmy += hex2[dir+1][1];
+ /* half hexagon, not overlapping the previous iteration */
+ for( i = 1; i < 10; i++ )
+ {
+ if( bmy > h->mb.mv_max_spel[1] - 1 )
+ break;
+ static const int mod6[8] = {5,0,1,2,3,4,5,0};
+ const int odir = mod6[dir+1];
+ COST_MV_RD_X3_DIR( hex2[odir+0][0], hex2[odir+0][1],
+ hex2[odir+1][0], hex2[odir+1][1],
+ hex2[odir+2][0], hex2[odir+2][1],
+ costs );
+ dir = -2;
+ COPY2_IF_LT( bcost, costs[0], dir, odir-1 );
+ COPY2_IF_LT( bcost, costs[1], dir, odir );
+ COPY2_IF_LT( bcost, costs[2], dir, odir+1 );
+ if( dir == -2 )
+ break;
+ bmx += hex2[dir+1][0];
+ bmy += hex2[dir+1][1];
+ }
}
-
+ //Square refine. Same as in ME HEX.
+ omx = bmx;
+ omy = bmy;
+ COST_MV_SATD( omx, omy - 1, satds[0] );
+ COST_MV_SATD( omx, omy + 1, satds[1] );
+ COST_MV_SATD( omx - 1, omy, satds[2] );
+ COST_MV_SATD( omx + 1, omy, satds[3] );
+ COST_MV_RD( omx, omy - 1, satds[0]);
+ COST_MV_RD( omx, omy + 1, satds[1]);
+ COST_MV_RD( omx - 1, omy, satds[2]);
+ COST_MV_RD( omx + 1, omy, satds[3]);
+ COST_MV_SATD( omx - 1, omy - 1, satds[0] );
+ COST_MV_SATD( omx + 1, omy + 1, satds[1] );
+ COST_MV_SATD( omx - 1, omy + 1, satds[2] );
+ COST_MV_SATD( omx + 1, omy - 1, satds[3] );
+ COST_MV_RD( omx - 1, omy - 1, satds[0]);
+ COST_MV_RD( omx + 1, omy + 1, satds[1]);
+ COST_MV_RD( omx - 1, omy + 1, satds[2]);
+ COST_MV_RD( omx + 1, omy - 1, satds[3]);
if( bmy > h->mb.mv_max_spel[1] )
bmy = h->mb.mv_max_spel[1];
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 );
}
-
foxyshadis
14th September 2007, 02:48
I managed to almost build it with VS, but unfortunately the SATD esa algorithm uses some C stuff that MS's compiler does not support (array lengths not defined at compile). I'll try to get around to installing Cygwin this weekend.
I tried using IMH with SATD with the ESA commented out for now, and I must say I like it so far. Although for sure you can say the benefit is not worth the speed, it still runs fast enough to run my encode overnight. :p
Shikari's C99isms are all of the random declaration variety, here's a patch to fix that in VC8: http://foxyshadis.slightlydark.com/random/subme7_vc8-2.diff The IMH patch has a couple instances of this, but all you need to do is move several declarations before "if(bcost < 100) goto me_hex2;" and pull "static const int mod6[8] = {5,0,1,2,3,4,5,0};\" to the opening of its scope.
It's the hadamard that has the var-arrays, for that one: http://foxyshadis.slightlydark.com/random/x264_satd_fpel.07-vc8.diff
I'll likely take them down once this is over.
DeathTheSheep
14th September 2007, 05:17
@AGDenton: maybe you didn't notice, but that's pretty broken. Not even one mention of IMH in x264.c, and that's as far as I looked. Mac OS X... :rolleyes:
@Dark Shikari: Every time I tried to stick that in a .diff, I get patch unexpectedly ends in middle of line
patch: **** malformed patch at line 274:
But I patched it by hand and all is well (but it took a while).
Dark Shikari
14th September 2007, 05:24
@AGDenton: maybe you didn't notice, but that's pretty broken. Not even one mention of IMH in x264.c, and that's as far as I looked. Mac OS X... :rolleyes:
@Dark Shikari: Every time I tried to stick that in a .diff, I get patch unexpectedly ends in middle of line
patch: **** malformed patch at line 274:
But I patched it by hand and all is well (but it took a while).I fixed it, I think its because I truncated a - off the end :p
Also, by the way, I'm not done yet--my next area of the code I'm looking to improve is the analysis of predictors before the main motion estimation function--the algorithm used here is relatively naive, so after all these patches go through, or perhaps even before, I'll have something fancier out :)
AGDenton
14th September 2007, 07:03
@DeathTheSheep : that would be because it's in x264.h... Since I don't use the CLI (only libx264.a), I didn't notice. The rest works fine, though.
Here (http://agerschenfeld.free.fr/x264/x264_satdcmp_aq_subme_imh.tbz)'s the corrected source, updated with the new subme -7.
Dark Shikari
14th September 2007, 08:08
A new patch for you folks... this is quite beta and I'm not sure if it'll even patch properly, but as long as it can eventually get integrated into the "Big bunch of Dark Shikari patches" things should be fine.
diff -u5 -r x264/common/common.c x264/common/common.c
--- x264/common/common.c 2007-07-19 03:14:52.387375000 -0700
+++ x264/common/common.c 2007-09-13 23:47:49.296875000 -0700
@@ -449,10 +449,12 @@
p->analyse.i_mv_range = atoi(value);
OPT2("mvrange-thread", "mv-range-thread")
p->analyse.i_mv_range_thread = atoi(value);
OPT2("subme", "subq")
p->analyse.i_subpel_refine = atoi(value);
+ OPT2("me-prepass", "meprepass")
+ p->analyse.i_me_prepass = atobool(value);
OPT("bime")
p->analyse.b_bidir_me = atobool(value);
OPT("chroma-me")
p->analyse.b_chroma_me = atobool(value);
OPT2("b-rdo", "brdo")
@@ -900,10 +902,11 @@
s += sprintf( s, " deblock=%d:%d:%d", p->b_deblocking_filter,
p->i_deblocking_filter_alphac0, p->i_deblocking_filter_beta );
s += sprintf( s, " analyse=%#x:%#x", p->analyse.intra, p->analyse.inter );
s += sprintf( s, " me=%s", x264_motion_est_names[ p->analyse.i_me_method ] );
s += sprintf( s, " subme=%d", p->analyse.i_subpel_refine );
+ s += sprintf( s, " me-prepass=%d", p->analyse.i_me_prepass );
s += sprintf( s, " brdo=%d", p->analyse.b_bframe_rdo );
s += sprintf( s, " mixed_ref=%d", p->analyse.b_mixed_references );
s += sprintf( s, " me_range=%d", p->analyse.i_me_range );
s += sprintf( s, " chroma_me=%d", p->analyse.b_chroma_me );
s += sprintf( s, " trellis=%d", p->analyse.i_trellis );
diff -u5 -r x264/encoder/me.c x264/encoder/me.c
--- x264/encoder/me.c 2007-07-17 04:13:25.937500000 -0700
+++ x264/encoder/me.c 2007-09-14 00:01:47.140625000 -0700
@@ -59,10 +59,27 @@
int cost = h->pixf.fpelcmp[i_pixel]( m->p_fenc[0], FENC_STRIDE, src, stride ) \
+ p_cost_mvx[ mx ] + p_cost_mvy[ my ]; \
COPY3_IF_LT( bpred_cost, cost, bpred_mx, mx, bpred_my, my ); \
}
+#define COST_MV_HPEL2( mx, my, cost ) \
+{ \
+ int stride = 16; \
+ uint8_t *src = h->mc.get_ref( m->p_fref, m->i_stride[0], pix, &stride, mx, my, bw, bh ); \
+ cost = h->pixf.fpelcmp[i_pixel]( m->p_fenc[0], FENC_STRIDE, src, stride ) \
+ + p_cost_mvx[ mx ] + p_cost_mvy[ my ]; \
+}
+
+#define COST_MV_HPEL3( mx, my) \
+{ \
+ int stride = 16; \
+ uint8_t *src = h->mc.get_ref( m->p_fref, m->i_stride[0], pix, &stride, mx, my, bw, bh ); \
+ int cost = h->pixf.fpelcmp[i_pixel]( m->p_fenc[0], FENC_STRIDE, src, stride ) \
+ + p_cost_mvx[ mx ] + p_cost_mvy[ my ]; \
+ COPY3_IF_LT( bestcost, cost, bestx, mx, besty, my ); \
+}
+
#define COST_MV_X3_DIR( m0x, m0y, m1x, m1y, m2x, m2y, costs )\
{\
uint8_t *pix_base = p_fref + bmx + bmy*m->i_stride[0];\
h->pixf.fpelcmp_x3[i_pixel]( m->p_fenc[0],\
pix_base + (m0x) + (m0y)*m->i_stride[0],\
@@ -175,25 +192,92 @@
bmx = x264_clip3( m->mvp[0], mv_x_min*4, mv_x_max*4 );
bmy = x264_clip3( m->mvp[1], mv_y_min*4, mv_y_max*4 );
pmx = ( bmx + 2 ) >> 2;
pmy = ( bmy + 2 ) >> 2;
bcost = COST_MAX;
-
+
/* try extra predictors if provided */
if( h->mb.i_subpel_refine >= 3 )
{
- COST_MV_HPEL( bmx, bmy );
- for( i = 0; i < i_mvc; i++ )
- {
- const int mx = x264_clip3( mvc[i][0], mv_x_min*4, mv_x_max*4 );
- const int my = x264_clip3( mvc[i][1], mv_y_min*4, mv_y_max*4 );
- if( mx != bpred_mx || my != bpred_my )
- COST_MV_HPEL( mx, my );
- }
- bmx = ( bpred_mx + 2 ) >> 2;
- bmy = ( bpred_my + 2 ) >> 2;
- COST_MV( bmx, bmy );
+ COST_MV_HPEL( bmx, bmy );
+ if(!h->param.analyse.i_me_prepass)
+ {
+ for( i = 0; i < i_mvc; i++ )
+ {
+ const int mx = x264_clip3( mvc[i][0], mv_x_min*4, mv_x_max*4 );
+ const int my = x264_clip3( mvc[i][1], mv_y_min*4, mv_y_max*4 );
+ if( mx != bpred_mx || my != bpred_my )
+ COST_MV_HPEL( mx, my );
+ }
+ }
+ else
+ {
+ for( i = 0; i < i_mvc; i++ )
+ {
+ const int mx = x264_clip3( mvc[i][0], mv_x_min*4, mv_x_max*4 );
+ const int my = x264_clip3( mvc[i][1], mv_y_min*4, mv_y_max*4 );
+ if( mx != bpred_mx || my != bpred_my )
+ {
+ int bestcost;
+ int bestx = mx;
+ int besty = my;
+ COST_MV_HPEL2( mx, my, bestcost );
+ COPY3_IF_LT( bpred_cost, bestcost, bpred_mx, bestx, bpred_my, besty );
+ if(bestcost < 2*bpred_cost)
+ {
+ int n;
+ int dir = -2;
+ COST_MV_HPEL2(bestx-2,besty,costs[0]);
+ COST_MV_HPEL2(bestx-1,besty+2,costs[1]);
+ COST_MV_HPEL2(bestx+1,besty+2,costs[2]);
+ COST_MV_HPEL2(bestx+2,besty,costs[3]);
+ COST_MV_HPEL2(bestx+1,besty-2,costs[4]);
+ COST_MV_HPEL2(bestx-1,besty-2,costs[5]);
+ COPY2_IF_LT( bestcost, costs[0], dir, 0 );
+ COPY2_IF_LT( bestcost, costs[1], dir, 1 );
+ COPY2_IF_LT( bestcost, costs[2], dir, 2 );
+ COPY2_IF_LT( bestcost, costs[3], dir, 3 );
+ COPY2_IF_LT( bestcost, costs[4], dir, 4 );
+ COPY2_IF_LT( bestcost, costs[5], dir, 5 );
+ if( dir != -2 )
+ {
+ static const int hex2[8][2] = {{-1,-2}, {-2,0}, {-1,2}, {1,2}, {2,0}, {1,-2}, {-1,-2}, {-2,0}};
+ bestx += hex2[dir+1][0];
+ besty += hex2[dir+1][1];
+ for( n = 1; n < i_me_range && CHECK_MVRANGE(bestx, besty); n++ )
+ {
+ static const int mod6[8] = {5,0,1,2,3,4,5,0};
+ const int odir = mod6[dir+1];
+ COST_MV_HPEL2(hex2[odir+0][0]+bestx,hex2[odir+0][1]+besty,costs[0]);
+ COST_MV_HPEL2(hex2[odir+1][0]+bestx,hex2[odir+1][1]+besty,costs[1]);
+ COST_MV_HPEL2(hex2[odir+2][0]+bestx,hex2[odir+2][1]+besty,costs[2]);
+ dir = -2;
+ COPY2_IF_LT( bestcost, costs[0], dir, odir-1 );
+ COPY2_IF_LT( bestcost, costs[1], dir, odir );
+ COPY2_IF_LT( bestcost, costs[2], dir, odir+1 );
+ if( dir == -2 )
+ break;
+ bestx += hex2[dir+1][0];
+ besty += hex2[dir+1][1];
+ }
+ }
+ COST_MV_HPEL3(bestx+1,besty-1);
+ COST_MV_HPEL3(bestx+1,besty);
+ COST_MV_HPEL3(bestx+1,besty+1);
+ COST_MV_HPEL3(bestx,besty-1);
+ COST_MV_HPEL3(bestx,besty+1);
+ COST_MV_HPEL3(bestx-1,besty-1);
+ COST_MV_HPEL3(bestx-1,besty);
+ COST_MV_HPEL3(bestx-1,besty+1);
+ COPY3_IF_LT(bpred_cost,bestcost,bpred_mx,bestx,bpred_my,besty);
+ }
+ }
+ }
+ }
+ bmx = ( bpred_mx + 2 ) >> 2;
+ bmy = ( bpred_my + 2 ) >> 2;
+ COST_MV( bmx, bmy );
}
else
{
/* check the MVP */
COST_MV( pmx, pmy );
diff -u5 -r x264/x264.c x264/x264.c
--- x264/x264.c 2007-07-17 04:13:26.437500000 -0700
+++ x264/x264.c 2007-09-13 23:46:01.937500000 -0700
@@ -230,11 +230,12 @@
H0( " --merange <integer> Maximum motion vector search range [%d]\n", defaults->analyse.i_me_range );
H1( " --mvrange <integer> Maximum motion vector length [-1 (auto)]\n" );
H1( " --mvrange-thread <int> Minimum buffer between threads [-1 (auto)]\n" );
H0( " -m, --subme <integer> Subpixel motion estimation and partition\n"
" decision quality: 1=fast, 7=best. [%d]\n", defaults->analyse.i_subpel_refine );
- H0( " --b-rdo RD based mode decision for B-frames. Requires subme 6.\n" );
+ H0( " --me-prepass Run an ME prepass on predictors. Requires subme 3 or higher.\n");
+ H0( " --b-rdo RD based mode decision for B-frames. Requires subme 6 or higher.\n" );
H0( " --mixed-refs Decide references on a per partition basis\n" );
H1( " --no-chroma-me Ignore chroma in motion estimation\n" );
H1( " --bime Jointly optimize both MVs in B-frames\n" );
H0( " -8, --8x8dct Adaptive spatial transform size\n" );
H0( " -t, --trellis <integer> Trellis RD quantization. Requires CABAC. [%d]\n"
@@ -403,10 +404,11 @@
{ "me", required_argument, NULL, 0 },
{ "merange", required_argument, NULL, 0 },
{ "mvrange", required_argument, NULL, 0 },
{ "mvrange-thread", required_argument, NULL, 0 },
{ "subme", required_argument, NULL, 'm' },
+ { "me-prepass", no_argument, NULL, 0 },
{ "b-rdo", no_argument, NULL, 0 },
{ "mixed-refs", no_argument, NULL, 0 },
{ "no-chroma-me", no_argument, NULL, 0 },
{ "bime", no_argument, NULL, 0 },
{ "8x8dct", no_argument, NULL, '8' },
diff -u5 -r x264/x264.h x264/x264.h
--- x264/x264.h 2007-07-12 22:46:06.718500000 -0700
+++ x264/x264.h 2007-09-13 23:46:32.562500000 -0700
@@ -219,10 +219,11 @@
int i_me_method; /* motion estimation algorithm to use (X264_ME_*) */
int i_me_range; /* integer pixel motion estimation search range (from predicted mv) */
int i_mv_range; /* maximum length of a mv (in pixels). -1 = auto, based on level */
int i_mv_range_thread; /* minimum space between threads. -1 = auto, based on number of threads. */
int i_subpel_refine; /* subpixel motion estimation quality */
+ int i_me_prepass; /* run an ME prepass on predictors */
int b_bidir_me; /* jointly optimize both MVs in B-frames */
int b_chroma_me; /* chroma ME for subpel and mode decision in P-frames */
int b_bframe_rdo; /* RD based mode decision for B-frames */
int b_mixed_references; /* allow each mb partition in P-frames to have it's own reference number */
int i_trellis; /* trellis RD quantization */
What it does: Runs an ME prepass on all predictors. The prepass is in the form of a hex search.
Effect: at pretty high settings (--me umh, --subme 7, --no-fast-pskip, etc), a 16% drop in speed and a 1% increase in quality. This means its more effective at boosting quality than --me imh relative to the speed cost. Note also the speed cost is independent of the ME algorithm chosen; so if you're doing say --merange 24 --me imh, you'll hardly notice the speed loss from this algorithm. That also means, of course, that if you use --me-prepass on very low encoding settings you'll notice a huge speed drop.
Commandline: --me-prepass
Combining my improved --subme 7 (+0.75% quality), --me imh (+1% quality) and --me-prepass (+1% quality) will get you roughly the same boost you'd get going from --subme 5 to --subme 7 all over again; probably a much better choice than going from --ref 8 to --ref 16.
Terranigma
14th September 2007, 14:40
Can anyone compile a working build with the patches for x86 & x64, or link all that's required so that I can do this myself?
Terranigma
14th September 2007, 15:40
Sorry to double post, But I wanted to say that Cef (http://mirror05.x264.nl/Cef/) has an experimental build up with imh and the improved subme7. The only thing that's missing is --me-prepass command.
Cef
14th September 2007, 16:52
Can anyone compile a working build with the patches for x86 & x64, or link all that's required so that I can do this myself?
He (http://mirror05.x264.nl/Cef/force.php?file=./x264_x86_dark.7z)re (http://mirror05.x264.nl/Cef/force.php?file=./x264_x64_dark.7z) you go.
x264_xxx_exp corresponds to this (http://forum.doom9.org/showthread.php?p=1044198#post1044198) request. It has hrd_pulldown669h.diff, some Bframe changes, and some patches from Dark_Shikari, and some other stuff. Check the .rar in Sagittaire's post to see exactly.
Terranigma
14th September 2007, 17:27
Thanks a lot. Everything is running smoothly. :)
Terranigma
14th September 2007, 19:36
cef, no hadamard?
vortex_hl
14th September 2007, 20:09
cef, no hadamard?
imho it's now --fpel-cmp
vBulletin® v3.8.11, Copyright ©2000-2026, vBulletin Solutions Inc.