Re: [PATCH] faster yv12 to yuy2 color conversion

Roland Scheidegger <[email protected]>
Newsgroups gmane.comp.video.xine.devel
Message-ID <[email protected]>
Am 10.04.2012 15:57, schrieb Petri Hintukainen:
> Could the interlaced loops (y) be combined ? It could make the code
> simpler. It could also reduce cache misses and even improve streaming
> stores. The loop is already over 200 bytes long.
>
> Have you tried if using prefetch for YUV planes has any effect ?

Hmm I ran some numbers with somewhat interesting results.
(I did not do that on a per-loop base but per-line base, mostly because
it is probably better to assume cpus don't have at least 4 (might even
need more actually) write buffers - I'm not even sure all have 2 which
the current version needs. There's also lots of arguments to pass around
otherwise.)
The K8 is rather indifferent to both of these, performance barely moves
at all (note unfortunately adding prefetch can't be done in a optimal
way neither, it's either tons of unnecessary ones (which shouldn't hurt
much) or another branch in the innermost loop because for luma values
only one prefetch per 8 loop iteration is useful and for chroma values
it's only one per 16 loop iterations even due to 64byte line size plus
it's also ugly due to the argument swapping in the assembly). If
anything both of these changes might have made it very slightly faster
but the difference was really too small to tell.

On a Core2-class chip however things are a bit different: adding
prefetch still didn't do much. However, doing both odd/even lines at
once gets a noticeable boost (10% or so), adding prefetch on top of that
does nothing at all.

I believe these results can easily be explained by the much more
sophisticated hw prefetcher of the core2, which you cannot beat for
consecutive accesses. The K8 can't prefetch that deeply but memory
latency is much lower, which probably means the hw prefetcher does its
job (hence sw prefetch doesn't help much) but the occasional miss at
line start doesn't hurt much neither really.

So I guess at least for the mmxext case doing both loops at once is
better (I'm a bit hesitant of doing that for the mmx case the cpus there
could have vastly different performance characteristics - I also tend to
think prefetch would help older cpus more as those had pretty dumb if
any hw prefetchers).

I tried interleaving the code and if anything it got slightly slower
(maybe too many loads without arithmetic at beginning dunno it could
have been just statistical noise). In any case I tried a quick hacked
sse2 version and performance still was exactly the same even on a core2
(which has 128bit simd units and hence really could be twice as fast if
arithmetic throughput were limiting performance). So this code really
has reached the limits wrt memory subsystem (it might be different on
nehalem) and trying to optimize for higher arithmetic throughput seems
to do nothing but make the code unreadable (well on the mmxext case at
least the mmx one probably does have some potential but I don't really
care about that nor do I have a cpu where I could really measure it). On
Nehalem or other similarly fast cpus certainly adding a sse2 version
would have priority over such hacks too (maybe it would not be totally
limited by memory system with mmx but I bet with sse2 it would be back
instantly to being 100% limited by memory - of course nehalem most
likely also means large caches so exploring options of not always using
streaming stores depending on video and cache size might be worthwile
too there).
The generated code actually looks quite bad at least on 32bit, there's
lots of moves (I tried improving that by using memory operands which
lets gcc use more complex address operands, but it does only really do
something for the first asm block not the other ones hence I moved some
stuff slightly which cuts down innermost loop instructions quite a bit,
not that it does anything for performance - using just one asm block
would generate even slightly better code on 64bit but not compile on
32bit unfortunately...).

I've also fixed the trivial bug in the progressive assembly code...

In any case I'm done with this code for now.

Roland

------------------------------------------------------------------------------
Better than sec? Nothing is better than sec when it comes to
monitoring Big Data applications. Try Boundary one-second 
resolution app monitoring today. Free.
http://p.sf.net/sfu/Boundary-dev2dev

_______________________________________________
xine-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/xine-devel
yv12_yuy2_2.diff (text/x-patch, 18.2 KB)
diff -r f931848945c4 src/xine-utils/color.c
--- a/src/xine-utils/color.c	Tue Apr 10 16:16:50 2012 +0300
+++ b/src/xine-utils/color.c	Wed Apr 11 03:08:09 2012 +0200
@@ -802,10 +802,11 @@
 
 
 #if defined(ARCH_X86) || defined(ARCH_X86_64)
+static const int64_t __attribute__((__used__)) byte_one = 0x0101010101010101ll;
 
-#define MMXEXT_YUV420_YUYV_PROGRESSIVE(p_y1,p_y2,p_u,p_ut,p_ub,p_v,p_vt,p_vb,p_line1,p_line2)  \
+#define MMX_YUV420_YUYV_PROGRESSIVE(p_y1,p_y2,p_u,p_ut,p_ub,p_v,p_vt,p_vb,p_line1,p_line2)  \
 do {                                                                               \
-   __asm__ __volatile__(".align 8 \n\t"                                            \
+   __asm__ __volatile__(                                                           \
     "movq       (%0), %%mm0 \n\t"  /* Load 8 Y          y7 y6 y5 y4 y3 y2 y1 y0 */ \
     "movd       (%1), %%mm1 \n\t"  /* Load 4 Cb         00 00 00 00 u3 u2 u1 u0 */ \
     "movd       (%2), %%mm2 \n\t"  /* Load 4 Cr         00 00 00 00 v3 v2 v1 v0 */ \
@@ -847,24 +848,69 @@
     :                                                                              \
     : "r" (p_ut), "r" (p_vt), "r" (p_ub), "r" (p_vb) );                            \
    __asm__ __volatile__(                                                           \
-    "movntq    %%mm1, (%0)  \n\t"  /* Store low YUYV1                           */ \
+    "movq      %%mm1, (%0)  \n\t"  /* Store low YUYV1                           */ \
     "punpckhbw %%mm3, %%mm0 \n\t"  /*                   v3 y7 u3 y6 v2 y5 u2 y4 */ \
-    "movntq    %%mm0, 8(%0) \n\t"  /* Store high YUYV1                          */ \
+    "movq      %%mm0, 8(%0) \n\t"  /* Store high YUYV1                          */ \
     "movq       (%2), %%mm0 \n\t"  /* Load 8 Y          Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 */ \
     "movq      %%mm0, %%mm1 \n\t"  /*                   Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 */ \
     "punpcklbw %%mm2, %%mm1 \n\t"  /*                   v1 Y3 u1 Y2 v0 Y1 u0 Y0 */ \
-    "movntq    %%mm1, (%1)  \n\t"  /* Store low YUYV2                           */ \
+    "movq      %%mm1, (%1)  \n\t"  /* Store low YUYV2                           */ \
     "punpckhbw %%mm2, %%mm0 \n\t"  /*                   v3 Y7 u3 Y6 v2 Y5 u2 Y4 */ \
-    "movntq    %%mm0, 8(%1) \n\t"  /* Store high YUYV2                          */ \
+    "movq      %%mm0, 8(%1) \n\t"  /* Store high YUYV2                          */ \
     :                                                                              \
     : "r" (p_line1),  "r" (p_line2),  "r" (p_y2) );                                \
   p_line1 += 16; p_line2 += 16; p_y1 += 8; p_y2 += 8; p_u += 4; p_v += 4;          \
   p_ub += 4; p_vb += 4; p_ut += 4; p_vt += 4;                                      \
 } while(0)
 
-#define MMXEXT_YUV420_YUYV_INTERLACED(p_y1,p_y2,p_u,p_ut,p_ub,p_v,p_vt,p_vb,p_line1,p_line2)  \
+#define MMXEXT_YUV420_YUYV_PROGRESSIVE(p_y1,p_y2,p_u,p_ut,p_ub,p_v,p_vt,p_vb,p_line1,p_line2)  \
 do {                                                                               \
-   __asm__ __volatile__(".align 8 \n\t"                                            \
+   __asm__ __volatile__(                                                           \
+    "movd         %0, %%mm1 \n\t"  /* Load 4 Cb         00 00 00 00 u3 u2 u1 u0 */ \
+    "movd         %1, %%mm2 \n\t"  /* Load 4 Cr         00 00 00 00 v3 v2 v1 v0 */ \
+    "movd         %2, %%mm3 \n\t"  /* Load 4 Cbt        00 00 00 00 u3 u2 u1 u0 */ \
+    "movd         %3, %%mm4 \n\t"  /* Load 4 Crt        00 00 00 00 v3 v2 v1 v0 */ \
+    "punpcklbw %%mm2, %%mm1 \n\t"  /*                   v3 u3 v2 u2 v1 u1 v0 u0 */ \
+    :                                                                              \
+    : "m" (*p_u), "m" (*p_v), "m" (*p_ut), "m" (*p_vt) );                          \
+   __asm__ __volatile__(                                                           \
+    "movq       (%0), %%mm0 \n\t"  /* Load 8 Y          y7 y6 y5 y4 y3 y2 y1 y0 */ \
+    "punpcklbw %%mm4, %%mm3 \n\t"  /*                   v3 u3 v2 u2 v1 u1 v0 u0 */ \
+    "pavgb     %%mm1, %%mm3 \n\t"  /* CrCb1 = 1/2(CrCbt + CrCb)                 */ \
+    /* for correct rounding                                                     */ \
+    "psubusb   %%mm7, %%mm3 \n\t"                                                  \
+    "pavgb     %%mm1, %%mm3 \n\t"  /* CrCb1 = 1/2(1/2(CrCbt + CrCb) + CrCb)     */ \
+    "movq      %%mm0, %%mm2 \n\t"  /*                   y7 y6 y5 y4 y3 y2 y1 y0 */ \
+    "punpcklbw %%mm3, %%mm2 \n\t"  /*                   v1 y3 u1 y2 v0 y1 u0 y0 */ \
+    "movntq    %%mm2, (%1)  \n\t"  /* Store low YUYV1                           */ \
+    "punpckhbw %%mm3, %%mm0 \n\t"  /*                   v3 y7 u3 y6 v2 y5 u2 y4 */ \
+    "movntq    %%mm0, 8(%1) \n\t"  /* Store high YUYV1                          */ \
+    :                                                                              \
+    : "r" (p_y1), "r" (p_line1) );                                                 \
+   __asm__ __volatile__(                                                           \
+    "movd         %1, %%mm3 \n\t"  /* Load 4 Cbb        00 00 00 00 u3 u2 u1 u0 */ \
+    "movd         %2, %%mm4 \n\t"  /* Load 4 Crb        00 00 00 00 v3 v2 v1 v0 */ \
+    "movq       (%0), %%mm0 \n\t"  /* Load 8 Y          Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 */ \
+    "punpcklbw %%mm4, %%mm3 \n\t"  /*                   v3 u3 v2 u2 v1 u1 v0 u0 */ \
+    "pavgb     %%mm1, %%mm3 \n\t"  /* CrCb2 = 1/2(CrCbb + CrCb)                 */ \
+    /* for correct rounding                                                     */ \
+    "psubusb   %%mm7, %%mm3 \n\t"                                                  \
+    "pavgb     %%mm1, %%mm3 \n\t"  /* CrCb2 = 1/2(1/2(CrCbb + CrCb) + CrCb)     */ \
+    "movq      %%mm0, %%mm2 \n\t"  /*                   Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 */ \
+    "punpcklbw %%mm3, %%mm2 \n\t"  /*                   v1 Y3 u1 Y2 v0 Y1 u0 Y0 */ \
+    "movntq    %%mm2, (%3)  \n\t"  /* Store low YUYV2                           */ \
+    "punpckhbw %%mm3, %%mm0 \n\t"  /*                   v3 Y7 u3 Y6 v2 Y5 u2 Y4 */ \
+    "movntq    %%mm0, 8(%3) \n\t"  /* Store high YUYV2                          */ \
+    :                                                                              \
+    : "r" (p_y2), "m" (*p_ub), "m" (*p_vb), "r" (p_line2) );                       \
+  p_line1 += 16; p_line2 += 16; p_y1 += 8; p_y2 += 8; p_u += 4; p_v += 4;          \
+  p_ub += 4; p_vb += 4; p_ut += 4; p_vt += 4;                                      \
+} while(0)
+
+
+#define MMX_YUV420_YUYV_INTERLACED(p_y1,p_y2,p_u,p_ut,p_ub,p_v,p_vt,p_vb,p_line1,p_line2)  \
+do {                                                                               \
+   __asm__ __volatile__(                                                           \
     "movd       (%0), %%mm1 \n\t"  /* Load 4 Cb         00 00 00 00 u3 u2 u1 u0 */ \
     "movd       (%1), %%mm2 \n\t"  /* Load 4 Cr         00 00 00 00 v3 v2 v1 v0 */ \
     "pxor      %%mm7, %%mm7 \n\t"  /*                   00 00 00 00 00 00 00 00 */ \
@@ -897,9 +943,9 @@
     "por       %%mm5, %%mm6 \n\t"  /* Cr1 Cb1 interl    v3 u3 v2 u2 v1 u1 v0 u0 */ \
     "movq      %%mm0, %%mm3 \n\t"  /*                   y7 y6 y5 y4 y3 y2 y1 y0 */ \
     "punpcklbw %%mm6, %%mm3 \n\t"  /*                   v1 y3 u1 y2 v0 y1 u0 y0 */ \
-    "movntq    %%mm3, (%3)  \n\t"  /* Store low YUYV1                           */ \
+    "movq      %%mm3, (%3)  \n\t"  /* Store low YUYV1                           */ \
     "punpckhbw %%mm6, %%mm0 \n\t"  /*                   v3 y7 u3 y6 v2 y5 u2 y4 */ \
-    "movntq    %%mm0, 8(%3) \n\t"  /* Store high YUYV1                          */ \
+    "movq      %%mm0, 8(%3) \n\t"  /* Store high YUYV1                          */ \
     :                                                                              \
     : "r" (p_y1), "r" (p_ut), "r" (p_vt), "r" (p_line1) );                         \
    __asm__ __volatile__(                                                           \
@@ -923,11 +969,60 @@
     "por       %%mm1, %%mm2 \n\t"  /* Cr2 Cb2 interl    v3 u3 v2 u2 v1 u1 v0 u0 */ \
     "movq      %%mm0, %%mm1 \n\t"  /*                   Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 */ \
     "punpcklbw %%mm2, %%mm1 \n\t"  /*                   v1 Y3 u1 Y2 v0 Y1 u0 Y0 */ \
-    "movntq    %%mm1, (%3)  \n\t"  /* Store low YUYV2                           */ \
+    "movq      %%mm1, (%3)  \n\t"  /* Store low YUYV2                           */ \
     "punpckhbw %%mm2, %%mm0 \n\t"  /*                   v3 Y7 u3 Y6 v2 Y5 u2 Y4 */ \
+    "movq      %%mm0, 8(%3) \n\t"  /* Store high YUYV2                          */ \
+    :                                                                              \
+    : "r" (p_y2),  "r" (p_ub), "r" (p_vb),  "r" (p_line2) );                       \
+  p_line1 += 16; p_line2 += 16; p_y1 += 8; p_y2 += 8; p_u += 4; p_v += 4;          \
+  p_ub += 4; p_vb += 4; p_ut += 4; p_vt += 4;                                      \
+} while(0)
+
+#define MMXEXT_YUV420_YUYV_INTERLACED(p_y1,p_y2,p_u,p_ut,p_ub,p_v,p_vt,p_vb,p_line1,p_line2)  \
+do {                                                                               \
+   __asm__ __volatile__(                                                           \
+    "movd         %0, %%mm1 \n\t"  /* Load 4 Cb         00 00 00 00 u3 u2 u1 u0 */ \
+    "movd         %1, %%mm2 \n\t"  /* Load 4 Cr         00 00 00 00 v3 v2 v1 v0 */ \
+    "movd         %2, %%mm3 \n\t"  /* Load 4 Cbt        00 00 00 00 u3 u2 u1 u0 */ \
+    "movd         %3, %%mm4 \n\t"  /* Load 4 Crt        00 00 00 00 v3 v2 v1 v0 */ \
+    "punpcklbw %%mm2, %%mm1 \n\t"  /*                   v3 u3 v2 u2 v1 u1 v0 u0 */ \
+    :                                                                              \
+    : "m" (*p_u), "m" (*p_v), "m" (*p_ut), "m" (*p_vt) );                          \
+   __asm__ __volatile__(                                                           \
+    "movq       (%0), %%mm0 \n\t"  /* Load 8 Y          y7 y6 y5 y4 y3 y2 y1 y0 */ \
+    "punpcklbw %%mm4, %%mm3 \n\t"  /*                   v3 u3 v2 u2 v1 u1 v0 u0 */ \
+    "pavgb     %%mm1, %%mm3 \n\t"  /* CrCb1 = 1/2(CrCbt + CrCb)                 */ \
+    "pavgb     %%mm1, %%mm3 \n\t"  /* CrCb1 = 1/2(1/2(CrCbt + CrCb) + CrCb)     */ \
+    /* for correct rounding                                                     */ \
+    "psubusb   %%mm7, %%mm3 \n\t"                                                  \
+    "pavgb     %%mm1, %%mm3 \n\t"  /* CrCb1 = 1/8CrCbt + 7/8CrCb                */ \
+    "movq      %%mm0, %%mm2 \n\t"  /*                   y7 y6 y5 y4 y3 y2 y1 y0 */ \
+    "punpcklbw %%mm3, %%mm2 \n\t"  /*                   v1 y3 u1 y2 v0 y1 u0 y0 */ \
+    "movntq    %%mm2, (%1)  \n\t"  /* Store low YUYV1                           */ \
+    "punpckhbw %%mm3, %%mm0 \n\t"  /*                   v3 y7 u3 y6 v2 y5 u2 y4 */ \
+    "movntq    %%mm0, 8(%1) \n\t"  /* Store high YUYV1                          */ \
+    :                                                                              \
+    : "r" (p_y1), "r" (p_line1) );                                                 \
+   __asm__ __volatile__(                                                           \
+    "movd         %1, %%mm3 \n\t"  /* Load 4 Cbb        00 00 00 00 u3 u2 u1 u0 */ \
+    "movd         %2, %%mm4 \n\t"  /* Load 4 Crb        00 00 00 00 v3 v2 v1 v0 */ \
+    "movq       (%0), %%mm0 \n\t"  /* Load 8 Y          Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 */ \
+    "punpcklbw %%mm4, %%mm3 \n\t"  /*                   v3 u3 v2 u2 v1 u1 v0 u0 */ \
+    "pavgb     %%mm1, %%mm3 \n\t"  /* CrCb2 = 1/2(CrCbb + CrCb)                 */ \
+    "pavgb     %%mm3, %%mm1 \n\t"  /* CrCb2 = 1/4CrCbb + 3/4CrCb                */ \
+    /* other cases give error smaller than one with repeated pavgb but here we  */ \
+    /* would get a max error of 1.125. Subtract one to compensate for repeated  */ \
+    /* rounding up (which will give max error of 0.625 which isn't perfect      */ \
+    /* rounding but good enough).                                               */ \
+    "psubusb   %%mm7, %%mm1 \n\t"                                                  \
+    "pavgb     %%mm1, %%mm3 \n\t"  /* CrCb2 = 3/8CrCbb + 5/8CrCb                */ \
+    "movq      %%mm0, %%mm2 \n\t"  /*                   Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 */ \
+    "punpcklbw %%mm3, %%mm2 \n\t"  /*                   v1 Y3 u1 Y2 v0 Y1 u0 Y0 */ \
+    "movntq    %%mm2, (%3)  \n\t"  /* Store low YUYV2                           */ \
+    "punpckhbw %%mm3, %%mm0 \n\t"  /*                   v3 Y7 u3 Y6 v2 Y5 u2 Y4 */ \
     "movntq    %%mm0, 8(%3) \n\t"  /* Store high YUYV2                          */ \
     :                                                                              \
-    : "r" (p_y2),  "r" (p_ub), "r" (p_vb),  "r" (p_line2) );                       \
+    : "r" (p_y2),  "m" (*p_ub), "m" (*p_vb),  "r" (p_line2) );                     \
   p_line1 += 16; p_line2 += 16; p_y1 += 8; p_y2 += 8; p_u += 4; p_v += 4;          \
   p_ub += 4; p_vb += 4; p_ut += 4; p_vt += 4;                                      \
 } while(0)
@@ -957,6 +1052,11 @@
     const int i_source_v_margin = v_src_pitch - width/2;
     const int i_dest_margin = yuy2_pitch - width*2;
 
+    __asm__ __volatile__(
+     "movq %0, %%mm7 \n\t"
+     :
+     : "m" (byte_one) );
+
     if( progressive ) {
 
       for( i_y = height / 2; i_y-- ; )
@@ -1009,10 +1109,133 @@
             p_ub = p_u;
             p_vb = p_v;
           }
+          
+          /* 2 odd lines */
+          for( i_x = width / 8 ; i_x-- ; )
+          {
+              MMXEXT_YUV420_YUYV_INTERLACED(p_y1,p_y2,p_u,p_ut,p_ub,p_v,p_vt,p_vb,p_line1,p_line2);
+          }
+          for( i_x = (width % 8) / 2 ; i_x-- ; )
+          {
+              C_YUV420_YUYV_INTERLACED(p_y1,p_y2,p_u,p_ut,p_ub,p_v,p_vt,p_vb,p_line1,p_line2);
+          }
+          
+          p_y1 += i_source_margin;
+          p_y2 += i_source_margin;
+          p_u += i_source_u_margin;
+          p_v += i_source_v_margin;
+          p_ut += i_source_u_margin;
+          p_vt += i_source_v_margin;
+          p_ub += i_source_u_margin;
+          p_vb += i_source_v_margin;
+          p_line1 += i_dest_margin;
+          p_line2 += i_dest_margin;
+
+          /* 2 even lines - arguments need to be swapped */
+          for( i_x = width / 8 ; i_x-- ; )
+          {
+              MMXEXT_YUV420_YUYV_INTERLACED(p_y2,p_y1,p_u,p_ub,p_ut,p_v,p_vb,p_vt,p_line2,p_line1);
+          }
+          for( i_x = (width % 8) / 2 ; i_x-- ; )
+          {
+              C_YUV420_YUYV_INTERLACED(p_y2,p_y1,p_u,p_ub,p_ut,p_v,p_vb,p_vt,p_line2,p_line1);
+          }
+
+          p_y2 += i_source_margin;
+          p_u += i_source_u_margin;
+          p_v += i_source_v_margin;
+          p_ut = p_u - 2 * u_src_pitch;
+          p_vt = p_v - 2 * v_src_pitch;
+          p_line2 += i_dest_margin;
+      }
+    }
+
+    sfence();
+    emms();
+
+#endif
+}
+
+/* identical to yv12_to_yuy2_mmxext with the obvious exception...*/
+static void yv12_to_yuy2_mmx
+  (const unsigned char *y_src, int y_src_pitch,
+   const unsigned char *u_src, int u_src_pitch,
+   const unsigned char *v_src, int v_src_pitch,
+   unsigned char *yuy2_map, int yuy2_pitch,
+   int width, int height, int progressive ) {
+#if defined(ARCH_X86) || defined(ARCH_X86_64)
+    uint8_t *p_line1, *p_line2 = yuy2_map;
+    const uint8_t *p_y1, *p_y2 = y_src;
+    const uint8_t *p_u = u_src;
+    const uint8_t *p_v = v_src;
+    const uint8_t *p_ub, *p_vb;
+    const uint8_t *p_ut = u_src;
+    const uint8_t *p_vt = v_src;
+
+    int i_x, i_y;
+    int utmp, vtmp;
+
+    const int i_source_margin = y_src_pitch - width;
+    const int i_source_u_margin = u_src_pitch - width/2;
+    const int i_source_v_margin = v_src_pitch - width/2;
+    const int i_dest_margin = yuy2_pitch - width*2;
+
+    if( progressive ) {
+
+      for( i_y = height / 2; i_y-- ; )
+      {
+          p_line1 = p_line2;
+          p_line2 += yuy2_pitch;
+
+          p_y1 = p_y2;
+          p_y2 += y_src_pitch;
+
+          if( i_y > 1 ) {
+            p_ub = p_u + u_src_pitch;
+            p_vb = p_v + v_src_pitch;
+          } else {
+            p_ub = p_u;
+            p_vb = p_v;
+          }
 
           for( i_x = width / 8 ; i_x-- ; )
           {
-              MMXEXT_YUV420_YUYV_INTERLACED(p_y1,p_y2,p_u,p_ut,p_ub,p_v,p_vt,p_vb,p_line1,p_line2);
+              MMX_YUV420_YUYV_PROGRESSIVE(p_y1,p_y2,p_u,p_ut,p_ub,p_v,p_vt,p_vb,p_line1,p_line2);
+          }
+          for( i_x = (width % 8) / 2 ; i_x-- ; )
+          {
+              C_YUV420_YUYV_PROGRESSIVE(p_y1,p_y2,p_u,p_ut,p_ub,p_v,p_vt,p_vb,p_line1,p_line2);
+          }
+
+          p_y2 += i_source_margin;
+          p_u += i_source_u_margin;
+          p_v += i_source_v_margin;
+          p_ut = p_u - u_src_pitch;
+          p_vt = p_v - v_src_pitch;
+          p_line2 += i_dest_margin;
+      }
+
+    } else {
+
+      for( i_y = height / 4 ; i_y-- ; )
+      {
+          p_line1 = p_line2;
+          p_line2 += 2 * yuy2_pitch;
+
+          p_y1 = p_y2;
+          p_y2 += 2 * y_src_pitch;
+
+          if( i_y > 1 ) {
+            p_ub = p_u + 2 * u_src_pitch;
+            p_vb = p_v + 2 * v_src_pitch;
+          } else {
+            p_ub = p_u;
+            p_vb = p_v;
+          }
+
+          for( i_x = width / 8 ; i_x-- ; )
+          {
+              MMX_YUV420_YUYV_INTERLACED(p_y1,p_y2,p_u,p_ut,p_ub,p_v,p_vt,p_vb,p_line1,p_line2);
           }
           for( i_x = (width % 8) / 2 ; i_x-- ; )
           {
@@ -1053,7 +1276,7 @@
           /* swap arguments for even lines */
           for( i_x = width / 8 ; i_x-- ; )
           {
-              MMXEXT_YUV420_YUYV_INTERLACED(p_y2,p_y1,p_u,p_ub,p_ut,p_v,p_vb,p_vt,p_line2,p_line1);
+              MMX_YUV420_YUYV_INTERLACED(p_y2,p_y1,p_u,p_ub,p_ut,p_v,p_vb,p_vt,p_line2,p_line1);
           }
           for( i_x = (width % 8) / 2 ; i_x-- ; )
           {
@@ -1076,6 +1299,7 @@
 #endif
 }
 
+
 #define C_YUYV_YUV420( )                                          \
     *p_y1++ = *p_line1++; *p_y2++ = *p_line2++;                   \
     *p_u++ = (*p_line1++ + *p_line2++)>>1;                        \
@@ -1260,6 +1484,8 @@
   /* determine best YV12 -> YUY2 converter to use */
   if (xine_mm_accel() & MM_ACCEL_X86_MMXEXT)
     yv12_to_yuy2 = yv12_to_yuy2_mmxext;
+  else if (xine_mm_accel() & MM_ACCEL_X86_MMX)
+    yv12_to_yuy2 = yv12_to_yuy2_mmx;
   else
     yv12_to_yuy2 = yv12_to_yuy2_c;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.