faster deinterlacing for full frame mode
Roland Scheidegger <[email protected]> Fri, 24 Aug 2012 18:17:19 +0200
| Newsgroups | gmane.comp.video.xine.devel |
|---|---|
| Message-ID | <[email protected]> |
Hi, since some deinterlacers are 100% bound by memory bandwidth, I've tried to reduce the memory bandwidth these are consuming. In particular the greedy2frame one needs to read 4 half-frames to write a frame. However, if it can process both fields at once (for full frame mode) then those 8 half-frames it needs to read get reduced to 5 half-frames needed from memory (the rest will be in cache since the same data is used for outputting both frames). This is a total r/w bandwidth reduction of 25%, which directly translates into a 25% speedup (of the deinterlacer) here (of course what I really want is zero-copy XV which would help even more :-)). Can't say I like those interfaces though, the field/frame stuff is very confusing (especially when switching on/off interlacing), so I'm not sure everything works correctly (especially pulldown which is not really tested since 3:2 pulldown is only ever useful with 60Hz source material). I've slightly refactored the pulldown detection (just to make the mess a bit less confusing). The both-frames-at-once approach is only implemented for greedy2frame though nothing would prevent other deinterlacers from using it, as long as they are not scanline-based (well they could benefit from that too but I was too lazy to adapt the interface to that, those deinterlacers probably aren't all that interesting anyway). Oh and even if that isn't really related actually I've changed the greedy2frame deinterlacer to use some block-based approach instead of per-pixel (just because the code was easier, since the code is 100% limited by memory bandwidth anyway it doesn't actually matter). I guess this isn't quite nice since it differs from the mmxext version now (visually it seems quite similar still). The actual change of the greedy2frame deinterlacer to process both frames at once was pretty much trivial. So I guess this is still WIP but it seems to work well enough for me... Opinions? Roland ------------------------------------------------------------------------------ Live Security Virtual Conference Exclusive live event will cover all the ways today's security and threat landscape has changed and how IT managers can respond. Discussions will include endpoint security, mobile security and the latest in malware threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/ _______________________________________________ xine-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/xine-devel
xine_deint_twoframes.diff
(text/x-patch, 41.8 KB)
diff -r 5111963c9f17 src/post/deinterlace/deinterlace.h
--- a/src/post/deinterlace/deinterlace.h Fri Aug 17 09:06:15 2012 +0300
+++ b/src/post/deinterlace/deinterlace.h Fri Aug 24 17:50:46 2012 +0200
@@ -112,7 +112,7 @@
typedef void (*deinterlace_frame_t)( uint8_t *output, int outstride,
deinterlace_frame_data_t *data,
int bottom_field, int second_field,
- int width, int height );
+ int width, int height, uint8_t *output2 );
/**
@@ -130,6 +130,7 @@
deinterlace_copy_scanline_t copy_scanline;
deinterlace_frame_t deinterlace_frame;
int delaysfield; /* xine: this method delays output by one field relative to input */
+ int twoframes; /* method produces both frames for full frame rate at same time */
const char *description;
};
diff -r 5111963c9f17 src/post/deinterlace/plugins/double.c
--- a/src/post/deinterlace/plugins/double.c Fri Aug 17 09:06:15 2012 +0300
+++ b/src/post/deinterlace/plugins/double.c Fri Aug 24 17:50:46 2012 +0200
@@ -61,6 +61,7 @@
copy_scanline,
0,
0,
+ 0,
NULL
};
diff -r 5111963c9f17 src/post/deinterlace/plugins/greedy.c
--- a/src/post/deinterlace/plugins/greedy.c Fri Aug 17 09:06:15 2012 +0300
+++ b/src/post/deinterlace/plugins/greedy.c Fri Aug 24 17:50:46 2012 +0200
@@ -189,6 +189,7 @@
deinterlace_greedy_packed422_scanline_mmxext,
0,
1,
+ 0,
"Uses heuristics to detect motion in the input frames and reconstruct "
"image detail where possible. Use this for high quality output even "
"on monitors set to an arbitrary refresh rate.\n"
diff -r 5111963c9f17 src/post/deinterlace/plugins/greedy2frame.c
--- a/src/post/deinterlace/plugins/greedy2frame.c Fri Aug 17 09:06:15 2012 +0300
+++ b/src/post/deinterlace/plugins/greedy2frame.c Fri Aug 24 17:50:46 2012 +0200
@@ -56,32 +56,67 @@
static void DeinterlaceGreedy2Frame(uint8_t *output, int outstride,
deinterlace_frame_data_t *data,
- int bottom_field, int second_field, int width, int height )
+ int bottom_field, int second_field,
+ int width, int height, uint8_t *output2 )
{
- if (xine_mm_accel() & MM_ACCEL_X86_SSE2) {
- if (((uintptr_t)output & 15) || (outstride & 15) ||
- width & 7 ||
- ((uintptr_t)data->f0 & 15) || ((uintptr_t)data->f1 & 15)) {
- /*
- * instead of using an unaligned sse2 version just fall back to mmx
- * which has no alignment restriction (though might be slow unaliged,
- * but shouldn't hit this hopefully anyway). Plus in my experiments this
- * was at least as fast as a naive unaligned sse2 version anyway (due to
- * the inability to use streaming stores).
- */
+ if (!output2) {
+ if (xine_mm_accel() & MM_ACCEL_X86_SSE2) {
+ if (((uintptr_t)output & 15) || (outstride & 15) ||
+ width & 7 ||
+ ((uintptr_t)data->f0 & 15) || ((uintptr_t)data->f1 & 15)) {
+ /*
+ * instead of using an unaligned sse2 version just fall back to mmx
+ * which has no alignment restriction (though might be slow unaliged,
+ * but shouldn't hit this hopefully anyway). Plus in my experiments this
+ * was at least as fast as a naive unaligned sse2 version anyway (due to
+ * the inability to use streaming stores).
+ */
+ DeinterlaceGreedy2Frame_MMXEXT(output, outstride, data,
+ bottom_field, second_field, width, height );
+ } else {
+ DeinterlaceGreedy2Frame_SSE2(output, outstride, data,
+ bottom_field, second_field, width, height );
+ }
+ }
+ else {
DeinterlaceGreedy2Frame_MMXEXT(output, outstride, data,
bottom_field, second_field, width, height );
- } else {
- DeinterlaceGreedy2Frame_SSE2(output, outstride, data,
- bottom_field, second_field, width, height );
+ /* could fall back to 3dnow/mmx here too */
}
}
else {
- DeinterlaceGreedy2Frame_MMXEXT(output, outstride, data,
- bottom_field, second_field, width, height );
- /* could fall back to 3dnow/mmx here too */
+ if (xine_mm_accel() & MM_ACCEL_X86_SSE2) {
+ /* should check both outputs probably */
+ if (((uintptr_t)output & 15) || (outstride & 15) ||
+ width & 7 ||
+ ((uintptr_t)data->f0 & 15) || ((uintptr_t)data->f1 & 15)) {
+ /*
+ * instead of using an unaligned sse2 version just fall back to mmx
+ * which has no alignment restriction (though might be slow unaliged,
+ * but shouldn't hit this hopefully anyway). Plus in my experiments this
+ * was at least as fast as a naive unaligned sse2 version anyway (due to
+ * the inability to use streaming stores).
+ */
+ /* TODO */
+ DeinterlaceGreedy2Frame_MMXEXT(output, outstride, data,
+ bottom_field, 0, width, height );
+ DeinterlaceGreedy2Frame_MMXEXT(output2, outstride, data,
+ !bottom_field, 1, width, height );
+ } else {
+ DeinterlaceGreedy2Frame_twoframes_SSE2(output, output2, outstride, data,
+ bottom_field, width, height );
+ }
+ }
+ else {
+ /* TODO */
+ DeinterlaceGreedy2Frame_MMXEXT(output, outstride, data,
+ bottom_field, 0, width, height );
+ DeinterlaceGreedy2Frame_MMXEXT(output2, outstride, data,
+ !bottom_field, 1, width, height );
+ }
}
+
}
@@ -97,6 +132,7 @@
0,
DeinterlaceGreedy2Frame,
1,
+ 1,
NULL
};
diff -r 5111963c9f17 src/post/deinterlace/plugins/greedy2frame_template_sse2.c
--- a/src/post/deinterlace/plugins/greedy2frame_template_sse2.c Fri Aug 17 09:06:15 2012 +0300
+++ b/src/post/deinterlace/plugins/greedy2frame_template_sse2.c Fri Aug 24 17:50:46 2012 +0200
@@ -84,10 +84,11 @@
** B0 | | B1 | |
*/
+
#if defined(ARCH_X86) || defined(ARCH_X86_64)
static const sse_t Mask128 = { uq: { 0x7f7f7f7f7f7f7f7fll, 0x7f7f7f7f7f7f7f7fll} };
-#define TP GREEDYTWOFRAMETHRESHOLD, GREEDYTWOFRAMETHRESHOLD2
-static const sse_t GreedyTwoFrameThreshold128 = { ub: {TP, TP, TP, TP, TP, TP, TP, TP} };
+#define TP GREEDYTWOFRAMETHRESHOLD * 12
+static const sse_t GreedyTwoFrameThreshold128 = { ud: {TP, TP, TP, TP} };
#undef TP
#endif
@@ -155,6 +156,7 @@
Dest += outstride;
Dest2 = Dest;
+#if 0
/* just rely on gcc not using xmm regs... */
do {
asm volatile(
@@ -163,6 +165,7 @@
: /* no output */
: "m" (Mask128) );
} while (0);
+#endif
count = LineLength >> 4;
do {
@@ -176,21 +179,16 @@
"movdqa (%q4,%2), %%xmm3 \n\t" /* xmm3 = B1 */
"movdqa (%q4,%3), %%xmm2 \n\t" /* xmm2 = B0 */
- /* calculate |T1-T0| keep T1 put result in xmm5 */
- "movdqa %%xmm1, %%xmm5 \n\t"
- "psubusb %%xmm0, %%xmm5 \n\t"
- "psubusb %%xmm1, %%xmm0 \n\t"
- "por %%xmm0, %%xmm5 \n\t"
+ /* calculate |T1-T0| keep T1 put result in xmm0 */
+ "psadbw %%xmm1, %%xmm0 \n\t"
/* T1 is data for line to copy */
"movntdq %%xmm1, %1 \n\t"
- /* if |T1-T0| > Threshold we want 0 else dword minus one */
- "psrlw $1, %%xmm5 \n\t"
- "pand %%xmm6, %%xmm5 \n\t"
- "pcmpgtb %0, %%xmm5 \n\t"
- "pcmpeqd %%xmm7, %%xmm5 \n\t"
-
+ /* if |T1-T0| > Threshold we want dword minus one else 0 */
+ "pshufd $0xa0, %%xmm0, %%xmm0 \n\t"
+ "pcmpgtd %0, %%xmm0 \n\t"
+
"prefetcht0 64(%q4,%2) \n\t"
"prefetcht0 64(%q4,%3) \n\t"
:
@@ -198,22 +196,17 @@
"m" (*Destc), "r" (T1), "r" (T0), "r" (Pitch) );
asm volatile (
- /* calculate |B1-B0| keep B1 put result in xmm4 */
- "movdqa %%xmm3, %%xmm4 \n\t"
- "psubusb %%xmm2, %%xmm4 \n\t"
- "psubusb %%xmm3, %%xmm2 \n\t"
- "por %%xmm2, %%xmm4 \n\t"
+ /* calculate |B1-B0| keep B1 put result in xmm2 */
+ "psadbw %%xmm3, %%xmm2 \n\t"
- "movdqa (%0), %%xmm0 \n\t" /* xmm0 = M1 */
- "movdqa (%1), %%xmm2 \n\t" /* xmm2 = M0 */
+ "movdqa (%0), %%xmm5 \n\t" /* xmm5 = M1 */
+ "movdqa (%1), %%xmm4 \n\t" /* xmm4 = M0 */
- /* if |B1-B0| > Threshold we want 0 else dword minus one */
- "psrlw $1, %%xmm4 \n\t"
- "pand %%xmm6, %%xmm4 \n\t"
- "pcmpgtb %2, %%xmm4 \n\t"
- "pcmpeqd %%xmm7, %%xmm4 \n\t"
+ /* if |B1-B0| > Threshold we want dword minus one else 0 */
+ "pshufd $0xa0, %%xmm2, %%xmm2 \n\t"
+ "pcmpgtd %2, %%xmm2 \n\t"
- "por %%xmm4, %%xmm5 \n\t"
+ "pand %%xmm2, %%xmm0 \n\t"
/* Average T1 and B1 so we can do interpolated bobbing if we bob
* onto T1 */
@@ -224,39 +217,33 @@
/* make mm0 the average of M1 and M0 which should make weave
* look better when there is small amounts of movement */
- "movdqa %%xmm2, %%xmm3 \n\t"
- "pavgb %%xmm0, %%xmm3 \n\t" /* xmm3 = avg(M1,M0) */
+ "movdqa %%xmm4, %%xmm3 \n\t"
+ "pavgb %%xmm5, %%xmm3 \n\t" /* xmm3 = avg(M1,M0) */
/* calculate |M1-M0| put result in xmm4 */
- "movdqa %%xmm0, %%xmm4 \n\t"
- "psubusb %%xmm2, %%xmm4 \n\t"
- "psubusb %%xmm0, %%xmm2 \n\t"
- "por %%xmm2, %%xmm4 \n\t"
+ "psadbw %%xmm5, %%xmm4 \n\t"
- /* if |M1-M0| > Threshold we want 0 else dword minus one */
- "psrlw $1, %%xmm4 \n\t"
- "pand %%xmm6, %%xmm4 \n\t"
- "pcmpgtb %2, %%xmm4 \n\t"
- "pcmpeqd %%xmm7, %%xmm4 \n\t" /* do we want to bob */
+ /* if |M1-M0| > Threshold we want dword minus one else 0 */
+ "pshufd $0xa0, %%xmm4, %%xmm4 \n\t"
+ "pcmpgtd %2, %%xmm4 \n\t"
- "pand %%xmm5, %%xmm4 \n\t"
-
+ "por %%xmm0, %%xmm4 \n\t"
/* debugging feature
- * output the value of xmm4 at this point which is pink where we will weave
- * and green where we are going to bob
+ * output the value of xmm4 at this point which is green where we will weave
+ * and pink where we are going to bob
*/
#ifdef CHECK_BOBWEAVE
"movntdq %%xmm4, %3 \n\t"
#else
/* xmm4 now is 1 where we want to weave and 0 where we want to bob */
- "pand %%xmm4, %%xmm3 \n\t"
- "pandn %%xmm1, %%xmm4 \n\t"
- "por %%xmm3, %%xmm4 \n\t"
+ "pand %%xmm4, %%xmm1 \n\t"
+ "pandn %%xmm3, %%xmm4 \n\t"
+ "por %%xmm1, %%xmm4 \n\t"
"movntdq %%xmm4, %3 \n\t"
#endif
:
: "r" (M1), "r" (M0), "m" (GreedyTwoFrameThreshold128),
- "m" (*Dest2));
+ "m" (*Dest2) );
/* Advance to the next set of pixels. */
T1 += 16;
@@ -291,3 +278,293 @@
#endif
}
+
+static void DeinterlaceGreedy2Frame_twoframes_SSE2(uint8_t *output, uint8_t *output2,
+ int outstride,
+ deinterlace_frame_data_t *data,
+ int bottom_field,
+ int width, int height )
+{
+#if defined(ARCH_X86) || defined(ARCH_X86_64)
+ int Line;
+ int stride = width * 2;
+ register uint8_t* M1;
+ register uint8_t* M0;
+ register uint8_t* T1;
+ register uint8_t* T0;
+ register uint8_t* M1f2;
+ register uint8_t* M0f2;
+ register uint8_t* T1f2;
+ register uint8_t* T0f2;
+ uint8_t* Dest = output;
+ register uint8_t* Dest2;
+ register uint8_t* Destc;
+ uint8_t* Destf2 = output2;
+ register uint8_t* Destf22;
+ register uint8_t* Destf2c;
+ register int count;
+ uint32_t Pitch = stride * 2;
+ uint32_t LineLength = stride;
+ uint32_t PitchRest = Pitch - (LineLength >> 4)*16;
+
+ M1 = data->f0;
+ T1 = data->f1;
+ M0 = data->f1;
+ T0 = data->f2;
+ M1f2 = data->f0;
+ T1f2 = data->f0;
+ M0f2 = data->f1;
+ T0f2 = data->f1;
+
+ if( bottom_field ) {
+ M1 += stride;
+ T1 += 0;
+ M0 += stride;
+ T0 += 0;
+ M1f2 += Pitch;
+ T1f2 += stride;
+ M0f2 += Pitch;
+ T0f2 += stride;
+// T1f2 == M1
+// T0f2 == M0
+// M0f2 == T1 + Pitch = B1
+ xine_fast_memcpy(Destf2, M1f2, LineLength);
+ Destf2 += outstride;
+ } else {
+ M1 += Pitch;
+ T1 += stride;
+ M0 += Pitch;
+ T0 += stride;
+ M1f2 += stride;
+ T1f2 += 0;
+ M0f2 += stride;
+ T0f2 += 0;
+// T1 == M0f2
+// M0 == T0f2 + Pitch
+// M1 == T1f2 + Pitch
+ xine_fast_memcpy(Dest, M1, LineLength);
+ Dest += outstride;
+ }
+
+ for (Line = 0; Line < (height / 2) - 1; ++Line)
+ {
+ /* Always use the most recent data verbatim. */
+
+ Destc = Dest;
+ Dest += outstride;
+ Dest2 = Dest;
+ Destf2c = Destf2;
+ Destf2 += outstride;
+ Destf22 = Destf2;
+
+ /* first frame */
+ count = LineLength >> 4;
+ do {
+ asm volatile(
+ /* Figure out what to do with the scanline above the one we copy.
+ * See above for a description of the algorithm.
+ * weave if (weave(M) AND (weave(T) OR weave(B)))
+ */
+ "movdqa (%2), %%xmm1 \n\t" /* xmm1 = T1 */
+ "movdqa (%3), %%xmm0 \n\t" /* xmm0 = T0 */
+ "movdqa (%q4,%2), %%xmm3 \n\t" /* xmm3 = B1 */
+ "movdqa (%q4,%3), %%xmm2 \n\t" /* xmm2 = B0 */
+
+ /* calculate |T1-T0| keep T1 put result in xmm0 */
+ "psadbw %%xmm1, %%xmm0 \n\t"
+
+ /* T1 is data for line to copy */
+ "movntdq %%xmm1, %1 \n\t"
+
+ /* if |T1-T0| > Threshold we want dword minus one else 0 */
+ "pshufd $0xa0, %%xmm0, %%xmm0 \n\t"
+ "pcmpgtd %0, %%xmm0 \n\t"
+
+ "prefetcht0 64(%q4,%2) \n\t"
+ "prefetcht0 64(%q4,%3) \n\t"
+ :
+ : "m" (GreedyTwoFrameThreshold128),
+ "m" (*Destc), "r" (T1), "r" (T0), "r" (Pitch) );
+
+ asm volatile (
+ /* calculate |B1-B0| keep B1 put result in xmm2 */
+ "psadbw %%xmm3, %%xmm2 \n\t"
+
+ "movdqa (%0), %%xmm5 \n\t" /* xmm5 = M1 */
+ "movdqa (%1), %%xmm4 \n\t" /* xmm4 = M0 */
+
+ /* if |B1-B0| > Threshold we want dword minus one else 0 */
+ "pshufd $0xa0, %%xmm2, %%xmm2 \n\t"
+ "pcmpgtd %2, %%xmm2 \n\t"
+
+ "pand %%xmm2, %%xmm0 \n\t"
+
+ /* Average T1 and B1 so we can do interpolated bobbing if we bob
+ * onto T1 */
+ "pavgb %%xmm3, %%xmm1 \n\t" /* xmm1 = avg(T1,B1) */
+
+ "prefetcht0 64(%0) \n\t"
+ "prefetcht0 64(%1) \n\t"
+
+ /* make mm0 the average of M1 and M0 which should make weave
+ * look better when there is small amounts of movement */
+ "movdqa %%xmm4, %%xmm3 \n\t"
+ "pavgb %%xmm5, %%xmm3 \n\t" /* xmm3 = avg(M1,M0) */
+
+ /* calculate |M1-M0| put result in xmm4 */
+ "psadbw %%xmm5, %%xmm4 \n\t"
+
+ /* if |M1-M0| > Threshold we want dword minus one else 0 */
+ "pshufd $0xa0, %%xmm4, %%xmm4 \n\t"
+ "pcmpgtd %2, %%xmm4 \n\t"
+
+ "por %%xmm0, %%xmm4 \n\t"
+/* debugging feature
+ * output the value of xmm4 at this point which is green where we will weave
+ * and pink where we are going to bob
+ */
+#ifdef CHECK_BOBWEAVE
+ "movntdq %%xmm4, %3 \n\t"
+#else
+ /* xmm4 now is 1 where we want to weave and 0 where we want to bob */
+ "pand %%xmm4, %%xmm1 \n\t"
+ "pandn %%xmm3, %%xmm4 \n\t"
+ "por %%xmm1, %%xmm4 \n\t"
+ "movntdq %%xmm4, %3 \n\t"
+#endif
+ :
+ : "r" (M1), "r" (M0), "m" (GreedyTwoFrameThreshold128),
+ "m" (*Dest2) );
+
+ /* Advance to the next set of pixels. */
+ T1 += 16;
+ M1 += 16;
+ M0 += 16;
+ T0 += 16;
+ Dest2 += 16;
+ Destc += 16;
+
+ } while( --count );
+
+ /* second frame */
+ count = LineLength >> 4;
+ do {
+ asm volatile(
+ /* Figure out what to do with the scanline above the one we copy.
+ * See above for a description of the algorithm.
+ * weave if (weave(M) AND (weave(T) OR weave(B)))
+ */
+ "movdqa (%2), %%xmm1 \n\t" /* xmm1 = T1 */
+ "movdqa (%3), %%xmm0 \n\t" /* xmm0 = T0 */
+ "movdqa (%q4,%2), %%xmm3 \n\t" /* xmm3 = B1 */
+ "movdqa (%q4,%3), %%xmm2 \n\t" /* xmm2 = B0 */
+
+ /* calculate |T1-T0| keep T1 put result in xmm0 */
+ "psadbw %%xmm1, %%xmm0 \n\t"
+
+ /* T1 is data for line to copy */
+ "movntdq %%xmm1, %1 \n\t"
+
+ /* if |T1-T0| > Threshold we want dword minus one else 0 */
+ "pshufd $0xa0, %%xmm0, %%xmm0 \n\t"
+ "pcmpgtd %0, %%xmm0 \n\t"
+
+ "prefetcht0 64(%q4,%2) \n\t"
+ "prefetcht0 64(%q4,%3) \n\t"
+ :
+ : "m" (GreedyTwoFrameThreshold128),
+ "m" (*Destf2c), "r" (T1f2), "r" (T0f2), "r" (Pitch) );
+
+ asm volatile (
+ /* calculate |B1-B0| keep B1 put result in xmm2 */
+ "psadbw %%xmm3, %%xmm2 \n\t"
+
+ "movdqa (%0), %%xmm5 \n\t" /* xmm5 = M1 */
+ "movdqa (%1), %%xmm4 \n\t" /* xmm4 = M0 */
+
+ /* if |B1-B0| > Threshold we want dword minus one else 0 */
+ "pshufd $0xa0, %%xmm2, %%xmm2 \n\t"
+ "pcmpgtd %2, %%xmm2 \n\t"
+
+ "pand %%xmm2, %%xmm0 \n\t"
+
+ /* Average T1 and B1 so we can do interpolated bobbing if we bob
+ * onto T1 */
+ "pavgb %%xmm3, %%xmm1 \n\t" /* xmm1 = avg(T1,B1) */
+
+ "prefetcht0 64(%0) \n\t"
+ "prefetcht0 64(%1) \n\t"
+
+ /* make mm0 the average of M1 and M0 which should make weave
+ * look better when there is small amounts of movement */
+ "movdqa %%xmm4, %%xmm3 \n\t"
+ "pavgb %%xmm5, %%xmm3 \n\t" /* xmm3 = avg(M1,M0) */
+
+ /* calculate |M1-M0| put result in xmm4 */
+ "psadbw %%xmm5, %%xmm4 \n\t"
+
+ /* if |M1-M0| > Threshold we want dword minus one else 0 */
+ "pshufd $0xa0, %%xmm4, %%xmm4 \n\t"
+ "pcmpgtd %2, %%xmm4 \n\t"
+
+ "por %%xmm0, %%xmm4 \n\t"
+/* debugging feature
+ * output the value of xmm4 at this point which is green where we will weave
+ * and pink where we are going to bob
+ */
+#ifdef CHECK_BOBWEAVE
+ "movntdq %%xmm4, %3 \n\t"
+#else
+ /* xmm4 now is 1 where we want to weave and 0 where we want to bob */
+ "pand %%xmm4, %%xmm1 \n\t"
+ "pandn %%xmm3, %%xmm4 \n\t"
+ "por %%xmm1, %%xmm4 \n\t"
+ "movntdq %%xmm4, %3 \n\t"
+#endif
+ :
+ : "r" (M1f2), "r" (M0f2), "m" (GreedyTwoFrameThreshold128),
+ "m" (*Destf22) );
+
+ /* Advance to the next set of pixels. */
+ T1f2 += 16;
+ M1f2 += 16;
+ M0f2 += 16;
+ T0f2 += 16;
+ Destf22 += 16;
+ Destf2c += 16;
+
+ } while( --count );
+
+
+ Dest += outstride;
+ Destf2 += outstride;
+
+ M1 += PitchRest;
+ T1 += PitchRest;
+ M0 += PitchRest;
+ T0 += PitchRest;
+ M1f2 += PitchRest;
+ T1f2 += PitchRest;
+ M0f2 += PitchRest;
+ T0f2 += PitchRest;
+ }
+
+ asm("sfence\n\t");
+
+ if( bottom_field )
+ {
+ xine_fast_memcpy(Dest, T1, stride);
+ Dest += outstride;
+ xine_fast_memcpy(Dest, M1, stride);
+ xine_fast_memcpy(Destf2, T1f2, stride);
+ }
+ else
+ {
+ xine_fast_memcpy(Destf2, T1f2, stride);
+ Destf2 += outstride;
+ xine_fast_memcpy(Destf2, M1f2, stride);
+ xine_fast_memcpy(Dest, T1, stride);
+ }
+#endif
+}
+
diff -r 5111963c9f17 src/post/deinterlace/plugins/kdetv_greedyh.c
--- a/src/post/deinterlace/plugins/kdetv_greedyh.c Fri Aug 17 09:06:15 2012 +0300
+++ b/src/post/deinterlace/plugins/kdetv_greedyh.c Fri Aug 24 17:50:46 2012 +0200
@@ -80,7 +80,7 @@
static void deinterlace_frame_di_greedyh( uint8_t *output, int outstride,
deinterlace_frame_data_t *data,
int bottom_field, int second_field,
- int width, int height )
+ int width, int height, uint8_t *output2 )
{
#if defined (ARCH_X86) || defined (ARCH_X86_64)
if( xine_mm_accel() & MM_ACCEL_X86_MMXEXT ) {
@@ -116,6 +116,7 @@
0,
deinterlace_frame_di_greedyh,
0,
+ 0,
"Uses heuristics to detect motion in the input frames and reconstruct "
"image detail where possible. Use this for high quality output even "
"on monitors set to an arbitrary refresh rate.\n"
diff -r 5111963c9f17 src/post/deinterlace/plugins/kdetv_tomsmocomp.c
--- a/src/post/deinterlace/plugins/kdetv_tomsmocomp.c Fri Aug 17 09:06:15 2012 +0300
+++ b/src/post/deinterlace/plugins/kdetv_tomsmocomp.c Fri Aug 24 17:50:46 2012 +0200
@@ -95,7 +95,7 @@
static void deinterlace_frame_di_tomsmocomp( uint8_t *output, int outstride,
deinterlace_frame_data_t *data,
int bottom_field, int second_field,
- int width, int height )
+ int width, int height, uint8_t *output2 )
{
#if defined (ARCH_X86) || defined (ARCH_X86_64)
@@ -132,6 +132,7 @@
0,
deinterlace_frame_di_tomsmocomp,
0,
+ 0,
"Uses heuristics to detect motion in the input frames and reconstruct "
"image detail where possible. Use this for high quality output even "
"on monitors set to an arbitrary refresh rate.\n"
diff -r 5111963c9f17 src/post/deinterlace/plugins/linear.c
--- a/src/post/deinterlace/plugins/linear.c Fri Aug 17 09:06:15 2012 +0300
+++ b/src/post/deinterlace/plugins/linear.c Fri Aug 24 17:50:46 2012 +0200
@@ -64,6 +64,7 @@
copy_scanline,
0,
0,
+ 0,
"Expands each field independently without blurring or copying in time. "
"Use this if you want TV-quality with low CPU, and you have configured "
"your monitor to run at the refresh rate of the video signal.\n"
diff -r 5111963c9f17 src/post/deinterlace/plugins/linearblend.c
--- a/src/post/deinterlace/plugins/linearblend.c Fri Aug 17 09:06:15 2012 +0300
+++ b/src/post/deinterlace/plugins/linearblend.c Fri Aug 24 17:50:46 2012 +0200
@@ -323,6 +323,7 @@
deinterlace_scanline_linear_blend2_mmxext,
0,
0,
+ 0,
linearblendmethod_help
};
@@ -348,6 +349,7 @@
deinterlace_scanline_linear_blend2,
0,
0,
+ 0,
linearblendmethod_help
};
diff -r 5111963c9f17 src/post/deinterlace/plugins/scalerbob.c
--- a/src/post/deinterlace/plugins/scalerbob.c Fri Aug 17 09:06:15 2012 +0300
+++ b/src/post/deinterlace/plugins/scalerbob.c Fri Aug 24 17:50:46 2012 +0200
@@ -52,6 +52,7 @@
0,
0,
0,
+ 0,
"Expands each field independently without blurring or copying in time. "
"Use this if you want TV-quality with low CPU, and you have configured "
"your monitor to run at the refresh rate of the video signal.\n"
diff -r 5111963c9f17 src/post/deinterlace/plugins/vfir.c
--- a/src/post/deinterlace/plugins/vfir.c Fri Aug 17 09:06:15 2012 +0300
+++ b/src/post/deinterlace/plugins/vfir.c Fri Aug 24 17:50:46 2012 +0200
@@ -149,6 +149,7 @@
copy_scanline,
0,
0,
+ 0,
"Avoids flicker by blurring consecutive frames of input. Use this if you "
"want to run your monitor at an arbitrary refresh rate and not use much "
"CPU, and are willing to sacrifice detail.\n"
diff -r 5111963c9f17 src/post/deinterlace/plugins/weave.c
--- a/src/post/deinterlace/plugins/weave.c Fri Aug 17 09:06:15 2012 +0300
+++ b/src/post/deinterlace/plugins/weave.c Fri Aug 24 17:50:46 2012 +0200
@@ -62,6 +62,7 @@
copy_scanline,
0,
0,
+ 0,
"Only updates the most recent field."
};
diff -r 5111963c9f17 src/post/deinterlace/tvtime.c
--- a/src/post/deinterlace/tvtime.c Fri Aug 17 09:06:15 2012 +0300
+++ b/src/post/deinterlace/tvtime.c Fri Aug 24 17:50:46 2012 +0200
@@ -149,6 +149,58 @@
}
+static void tvtime_pulldown_detect( tvtime_t *tvtime,
+ uint8_t *curframe,
+ uint8_t *lastframe,
+ int width,
+ int frame_height,
+ int instride )
+{
+ int predicted;
+
+ predicted = tvtime->pdoffset << 1;
+ if( predicted > PULLDOWN_SEQ_DD ) predicted = PULLDOWN_SEQ_AA;
+
+ calculate_pulldown_score_vektor( tvtime, curframe, lastframe,
+ instride, frame_height, width );
+ tvtime->pdoffset = determine_pulldown_offset_short_history_new( tvtime->last_topdiff,
+ tvtime->last_botdiff,
+ 1, predicted );
+
+ /* 3:2 pulldown state machine. */
+ if( !tvtime->pdoffset ) {
+ /* No pulldown offset applies, drop out of pulldown immediately. */
+ tvtime->pdlastbusted = 0;
+ tvtime->pderror = tvtime->pulldown_error_wait;
+ } else if( tvtime->pdoffset != predicted ) {
+ if( tvtime->pdlastbusted ) {
+ tvtime->pdlastbusted--;
+ tvtime->pdoffset = predicted;
+ } else {
+ tvtime->pderror = tvtime->pulldown_error_wait;
+ }
+ } else {
+ if( tvtime->pderror ) {
+ tvtime->pderror--;
+ }
+
+ if( !tvtime->pderror ) {
+ tvtime->pdlastbusted = PULLDOWN_ERROR_THRESHOLD;
+ }
+ }
+ if( !tvtime->pderror ) {
+ if( !tvtime->filmmode ) {
+ printf( "Film mode enabled.\n" );
+ tvtime->filmmode = 1;
+ }
+ }
+ else if( tvtime->filmmode ) {
+ printf( "Film mode disabled.\n" );
+ tvtime->filmmode = 0;
+ }
+}
+
+
int tvtime_build_deinterlaced_frame( tvtime_t *tvtime, uint8_t *output,
uint8_t *curframe,
uint8_t *lastframe,
@@ -166,72 +218,29 @@
tvtime->filmmode = 0;
}
- if( tvtime->pulldown_alg == PULLDOWN_VEKTOR ) {
- /* Make pulldown phase decisions every top field. */
- if( !bottom_field ) {
- int predicted;
+ /* Make pulldown phase decisions every top field. */
+ else if ( !bottom_field ) {
+ tvtime_pulldown_detect( tvtime, curframe, lastframe, width, frame_height, instride );
+ }
- predicted = tvtime->pdoffset << 1;
- if( predicted > PULLDOWN_SEQ_DD ) predicted = PULLDOWN_SEQ_AA;
+ if( !tvtime->pderror && tvtime->filmmode) {
+ /* We're in pulldown, reverse it. */
+ if ( !bottom_field ) {
- calculate_pulldown_score_vektor( tvtime, curframe, lastframe,
- instride, frame_height, width );
- tvtime->pdoffset = determine_pulldown_offset_short_history_new( tvtime->last_topdiff,
- tvtime->last_botdiff,
- 1, predicted );
+ if( pulldown_drop( tvtime->pdoffset, 0 ) )
+ return 0;
- /* 3:2 pulldown state machine. */
- if( !tvtime->pdoffset ) {
- /* No pulldown offset applies, drop out of pulldown immediately. */
- tvtime->pdlastbusted = 0;
- tvtime->pderror = tvtime->pulldown_error_wait;
- } else if( tvtime->pdoffset != predicted ) {
- if( tvtime->pdlastbusted ) {
- tvtime->pdlastbusted--;
- tvtime->pdoffset = predicted;
- } else {
- tvtime->pderror = tvtime->pulldown_error_wait;
- }
+ else if( pulldown_source( tvtime->pdoffset, 0 ) ) {
+ pulldown_merge_fields( output, lastframe, lastframe + instride,
+ width, frame_height, instride*2, outstride );
} else {
- if( tvtime->pderror ) {
- tvtime->pderror--;
- }
-
- if( !tvtime->pderror ) {
- tvtime->pdlastbusted = PULLDOWN_ERROR_THRESHOLD;
- }
+ pulldown_merge_fields( output, curframe, lastframe + instride,
+ width, frame_height, instride*2, outstride );
}
-
-
- if( !tvtime->pderror ) {
- /* We're in pulldown, reverse it. */
- if( !tvtime->filmmode ) {
- printf( "Film mode enabled.\n" );
- tvtime->filmmode = 1;
- }
-
- if( pulldown_drop( tvtime->pdoffset, 0 ) )
- return 0;
-
- if( pulldown_source( tvtime->pdoffset, 0 ) ) {
- pulldown_merge_fields( output, lastframe, lastframe + instride,
- width, frame_height, instride*2, outstride );
- } else {
- pulldown_merge_fields( output, curframe, lastframe + instride,
- width, frame_height, instride*2, outstride );
- }
-
- return 1;
- } else {
- if( tvtime->filmmode ) {
- printf( "Film mode disabled.\n" );
- tvtime->filmmode = 0;
- }
- }
- } else if( !tvtime->pderror ) {
+ return 1;
+ } else {
if( pulldown_drop( tvtime->pdoffset, 1 ) )
return 0;
-
if( pulldown_source( tvtime->pdoffset, 1 ) ) {
pulldown_merge_fields( output, curframe, lastframe + instride,
width, frame_height, instride*2, outstride );
@@ -239,7 +248,6 @@
pulldown_merge_fields( output, curframe, curframe + instride,
width, frame_height, instride*2, outstride );
}
-
return 1;
}
}
@@ -252,7 +260,7 @@
data.f2 = secondlastframe;
tvtime->curmethod->deinterlace_frame( output, outstride, &data, bottom_field, second_field,
- width, frame_height );
+ width, frame_height, NULL );
} else {
int loop_size;
@@ -361,6 +369,83 @@
return 1;
}
+void tvtime_build_deinterlaced_frames( tvtime_t *tvtime, uint8_t *output[2],
+ uint8_t *curframe,
+ uint8_t *lastframe,
+ uint8_t *secondlastframe,
+ int bottom_field,
+ int width,
+ int frame_height,
+ int instride,
+ int outstride,
+ int bad_frames[2] )
+{
+ if( tvtime->pulldown_alg != PULLDOWN_VEKTOR ) {
+ /* If we leave vektor pulldown mode, lose our state. */
+ tvtime->filmmode = 0;
+ }
+ else {
+ tvtime_pulldown_detect( tvtime, curframe, lastframe, width, frame_height, instride );
+ }
+
+ if( !tvtime->pderror && tvtime->filmmode) {
+ /* We're in pulldown, reverse it. */
+
+ /* top field */
+ if( pulldown_drop( tvtime->pdoffset, 0 ) ) {
+ bad_frames[bottom_field] = 1;
+ }
+
+ if( pulldown_source( tvtime->pdoffset, 0 ) ) {
+ pulldown_merge_fields( output[bottom_field],
+ lastframe, lastframe + instride,
+ width, frame_height, instride*2, outstride );
+ bad_frames[bottom_field] = 0;
+ } else {
+ pulldown_merge_fields( output[bottom_field],
+ curframe, lastframe + instride,
+ width, frame_height, instride*2, outstride );
+ bad_frames[bottom_field] = 0;
+ }
+
+ /* bottom field */
+ if( pulldown_drop( tvtime->pdoffset, 1 ) ) {
+ bad_frames[!bottom_field] = 1;
+ }
+
+ else if( pulldown_source( tvtime->pdoffset, 1 ) ) {
+ pulldown_merge_fields( output[!bottom_field],
+ curframe, lastframe + instride,
+ width, frame_height, instride*2, outstride );
+ bad_frames[!bottom_field] = 0;
+ } else {
+ pulldown_merge_fields( output[!bottom_field],
+ curframe, curframe + instride,
+ width, frame_height, instride*2, outstride );
+ bad_frames[!bottom_field] = 0;
+ }
+ return;
+ }
+
+ /* non-scanlinemode only */
+
+ {
+ deinterlace_frame_data_t data;
+
+ data.f0 = curframe;
+ data.f1 = lastframe;
+ data.f2 = secondlastframe;
+
+ tvtime->curmethod->deinterlace_frame( output[0], outstride, &data, bottom_field, 0,
+ width, frame_height, output[1] );
+
+ bad_frames[0] = 0;
+ bad_frames[1] = 0;
+ }
+}
+
+
+
int tvtime_build_copied_field( tvtime_t *tvtime, uint8_t *output,
uint8_t *curframe,
diff -r 5111963c9f17 src/post/deinterlace/tvtime.h
--- a/src/post/deinterlace/tvtime.h Fri Aug 17 09:06:15 2012 +0300
+++ b/src/post/deinterlace/tvtime.h Fri Aug 24 17:50:46 2012 +0200
@@ -74,7 +74,6 @@
} tvtime_t;
-
int tvtime_build_deinterlaced_frame( tvtime_t *this, uint8_t *output,
uint8_t *curframe,
uint8_t *lastframe,
@@ -85,6 +84,16 @@
int instride,
int outstride );
+void tvtime_build_deinterlaced_frames( tvtime_t *this, uint8_t *output[2],
+ uint8_t *curframe,
+ uint8_t *lastframe,
+ uint8_t *secondlastframe,
+ int bottom_field,
+ int width,
+ int frame_height,
+ int instride,
+ int outstride,
+ int bad_frames[2] );
int tvtime_build_copied_field( tvtime_t *this, uint8_t *output,
uint8_t *curframe,
diff -r 5111963c9f17 src/post/deinterlace/xine_plugin.c
--- a/src/post/deinterlace/xine_plugin.c Fri Aug 17 09:06:15 2012 +0300
+++ b/src/post/deinterlace/xine_plugin.c Fri Aug 24 17:50:46 2012 +0200
@@ -681,6 +681,94 @@
return skip;
}
+/* Build the output frames from the specified fields. */
+static int deinterlace_build_output_frames(
+ post_plugin_deinterlace_t *this, post_video_port_t *port,
+ xine_stream_t *stream,
+ vo_frame_t *frame, vo_frame_t *yuy2_frame,
+ int bottom_field,
+ int64_t pts, int64_t duration)
+{
+ vo_frame_t *deinterlaced_frames[2];
+ int force24fps;
+ int skip = 0;
+ int bad_frames[2];
+ int i;
+ uint8_t *outputs[2];
+
+ /* cheap mode is incompatible with full framerate and pulldown */
+
+ force24fps = this->judder_correction &&
+ ( this->pulldown == PULLDOWN_VEKTOR && this->tvtime->filmmode );
+
+ pthread_mutex_unlock (&this->lock);
+ for (i = 0; i < 2; i++) {
+ deinterlaced_frames[i] = port->original_port->get_frame(port->original_port,
+ frame->width, frame->height, frame->ratio, yuy2_frame->format,
+ frame->flags | VO_BOTH_FIELDS);
+ }
+ pthread_mutex_lock (&this->lock);
+
+ for (i = 0; i < 2; i++) {
+ deinterlaced_frames[i]->crop_left = frame->crop_left;
+ deinterlaced_frames[i]->crop_right = frame->crop_right;
+ deinterlaced_frames[i]->crop_top = frame->crop_top;
+ deinterlaced_frames[i]->crop_bottom = frame->crop_bottom;
+
+ _x_extra_info_merge(deinterlaced_frames[i]->extra_info, frame->extra_info);
+ }
+
+ outputs[0] = deinterlaced_frames[0]->base[0];
+ outputs[1] = deinterlaced_frames[1]->base[0];
+
+ tvtime_build_deinterlaced_frames(this->tvtime,
+ outputs,
+ yuy2_frame->base[0],
+ (this->recent_frame[0])?this->recent_frame[0]->base[0]:yuy2_frame->base[0],
+ (this->recent_frame[1])?this->recent_frame[1]->base[0]:yuy2_frame->base[0],
+ bottom_field, frame->width, frame->height,
+ yuy2_frame->pitches[0], deinterlaced_frames[0]->pitches[0],
+ bad_frames);
+ deinterlaced_frames[0]->bad_frame = bad_frames[0];
+ deinterlaced_frames[1]->bad_frame = bad_frames[1];
+
+ pthread_mutex_unlock (&this->lock);
+
+ for (i = 0; i < 2; i++) {
+ if( force24fps ) {
+ if( !deinterlaced_frames[i]->bad_frame ) {
+ this->framecounter++;
+ if( i == 0 && this->framecounter > FRAMES_TO_SYNC ) {
+ deinterlaced_frames[i]->pts = pts;
+ this->framecounter = 0;
+ } else
+ deinterlaced_frames[i]->pts = 0;
+ deinterlaced_frames[i]->duration = FPS_24_DURATION;
+ if( this->chroma_filter )
+ apply_chroma_filter( deinterlaced_frames[i]->base[0], deinterlaced_frames[i]->pitches[0],
+ frame->width, frame->height );
+ skip = deinterlaced_frames[i]->draw(deinterlaced_frames[i], stream);
+ } else {
+ skip = 0;
+ }
+ } else {
+ deinterlaced_frames[i]->pts = i == 0 ? pts : 0;
+ deinterlaced_frames[i]->duration = duration;
+ if( this->chroma_filter && !deinterlaced_frames[i]->bad_frame )
+ apply_chroma_filter( deinterlaced_frames[i]->base[0], deinterlaced_frames[i]->pitches[0],
+ frame->width, frame->height );
+ skip = deinterlaced_frames[i]->draw(deinterlaced_frames[i], stream);
+ }
+
+ /* _x_post_frame_copy_up(frame, deinterlaced_frames[i]); */
+ deinterlaced_frames[i]->free(deinterlaced_frames[i]);
+ }
+ pthread_mutex_lock (&this->lock);
+
+ return skip;
+}
+
+
static int deinterlace_draw(vo_frame_t *frame, xine_stream_t *stream)
{
post_video_port_t *port = (post_video_port_t *)frame->port;
@@ -865,28 +953,41 @@
* by 1/2 of this frames duration when output is generated
* using the last field of the progressive frame. */
- /* Build the output from the first field. */
+ /* Try building both frames at once */
if ( !(this->recent_frame[0] && this->recent_frame[0]->progressive_frame &&
- this->tvtime->curmethod->delaysfield) ) {
- skip = deinterlace_build_output_field(
+ this->tvtime->curmethod->delaysfield &&
+ framerate_mode == FRAMERATE_FULL && this->tvtime->curmethod->twoframes) ) {
+ skip = deinterlace_build_output_frames(
this, port, stream,
frame, yuy2_frame,
- fields[0], 0,
+ fields[0],
frame->pts,
- (framerate_mode == FRAMERATE_FULL) ? frame->duration/2 : frame->duration,
- 0);
+ frame->duration/2);
}
+ else {
+ /* Build the output from the first field. */
+ if ( !(this->recent_frame[0] && this->recent_frame[0]->progressive_frame &&
+ this->tvtime->curmethod->delaysfield) ) {
+ skip = deinterlace_build_output_field(
+ this, port, stream,
+ frame, yuy2_frame,
+ fields[0], 0,
+ frame->pts,
+ (framerate_mode == FRAMERATE_FULL) ? frame->duration/2 : frame->duration,
+ 0);
+ }
- if( framerate_mode == FRAMERATE_FULL ) {
+ if( framerate_mode == FRAMERATE_FULL ) {
- /* Build the output from the second field. */
- skip = deinterlace_build_output_field(
- this, port, stream,
- frame, yuy2_frame,
- fields[1], 1,
- 0,
- frame->duration/2,
- skip);
+ /* Build the output from the second field. */
+ skip = deinterlace_build_output_field(
+ this, port, stream,
+ frame, yuy2_frame,
+ fields[1], 1,
+ 0,
+ frame->duration/2,
+ skip);
+ }
}
}