2 // uniform int PREFIX(current_field_position);
3 // uniform float PREFIX(num_lines);
4 // uniform float PREFIX(self_offset);
5 // uniform float PREFIX(inv_width);
6 // uniform float PREFIX(current_offset)[2];
7 // uniform float PREFIX(other_offset)[3];
9 // The best explanation of YADIF that I've seen is actually a pseudocode
10 // reimplementation from the Doom9 forum:
12 // http://forum.doom9.org/showthread.php?p=980375#post980375
14 // We generally follow its terminology instead of the original C source
15 // (which I'll refer to as “C YADIF”), although I've used the C source as a
16 // reference to double-check at times. We're not bit-exact the same as
17 // C YADIF; in particular, we work in linear light, and left/right edge
18 // handling might also be a bit different (for top/bottom edge handling,
19 // C YADIF repeats texels like we do). Also, C YADIF generally works on
20 // Y', Cb and Cr planes separately, while we work on the entire RGBA triplet
21 // and do our spatial interpolation decisions based on the pixel as a whole,
22 // so our decision metric also naturally becomes different.
24 #define DIFF(s1, s2) dot((s1) - (s2), (s1) - (s2))
26 vec4 FUNCNAME(vec2 tc) {
27 int yi = int(round(tc.y * PREFIX(num_lines) - 0.5f));
29 // Figure out if we just want to keep the current line or if
30 // we need to interpolate. This branch is obviously divergent,
31 // but the very nature of deinterlacing would seem to require that.
33 // Note that since we have bottom-left origin, yi % 2 will return 0
34 // for bottom and 1 for top.
35 if ((yi % 2) != PREFIX(current_field_position)) {
36 return INPUT3(vec2(tc.x, tc.y + PREFIX(self_offset)));
39 // First, estimate the current pixel from the neighboring pixels in the
40 // same field (spatial interpolation). We try first 0 degrees (straight
41 // up/down), then ±45 degrees and then finally ±63 degrees. The best of
42 // these, as determined by the “spatial score” (basically sum of squared
43 // differences in three neighboring pixels), is kept.
45 // The C version of YADIF goesn't check +63° unless +45° gave an improvement,
46 // and similarly not -63° unless -45° did. The MMX version goes through pains
47 // to simulate the same, but notes that it “hurts both quality and speed”.
48 // We're not bit-exact the same as the C version anyway, and not sampling
49 // ±63° would probably be a rather divergent branch, so we just always do it.
53 // h i j k l m n +--> x
55 vec2 a_pos = vec2(tc.x - 3.0 * PREFIX(inv_width), tc.y + PREFIX(current_offset)[1]);
56 vec2 b_pos = vec2(tc.x - 2.0 * PREFIX(inv_width), a_pos.y);
57 vec2 c_pos = vec2(tc.x - PREFIX(inv_width), a_pos.y);
58 vec2 d_pos = vec2(tc.x, a_pos.y);
59 vec2 e_pos = vec2(tc.x + PREFIX(inv_width), a_pos.y);
60 vec2 f_pos = vec2(tc.x + 2.0 * PREFIX(inv_width), a_pos.y);
61 vec2 g_pos = vec2(tc.x + 3.0 * PREFIX(inv_width), a_pos.y);
63 vec2 h_pos = vec2(tc.x - 3.0 * PREFIX(inv_width), tc.y + PREFIX(current_offset)[0]);
64 vec2 i_pos = vec2(tc.x - 2.0 * PREFIX(inv_width), h_pos.y);
65 vec2 j_pos = vec2(tc.x - PREFIX(inv_width), h_pos.y);
66 vec2 k_pos = vec2(tc.x, h_pos.y);
67 vec2 l_pos = vec2(tc.x + PREFIX(inv_width), h_pos.y);
68 vec2 m_pos = vec2(tc.x + 2.0 * PREFIX(inv_width), h_pos.y);
69 vec2 n_pos = vec2(tc.x + 3.0 * PREFIX(inv_width), h_pos.y);
71 vec4 a = INPUT3(a_pos);
72 vec4 b = INPUT3(b_pos);
73 vec4 c = INPUT3(c_pos);
74 vec4 d = INPUT3(d_pos);
75 vec4 e = INPUT3(e_pos);
76 vec4 f = INPUT3(f_pos);
77 vec4 g = INPUT3(g_pos);
78 vec4 h = INPUT3(h_pos);
79 vec4 i = INPUT3(i_pos);
80 vec4 j = INPUT3(j_pos);
81 vec4 k = INPUT3(k_pos);
82 vec4 l = INPUT3(l_pos);
83 vec4 m = INPUT3(m_pos);
84 vec4 n = INPUT3(n_pos);
86 // 0 degrees. Note that pred is actually twice the real spatial prediction;
87 // we halve it later to same some arithmetic. Also, our spatial score is not
88 // the same as in C YADIF; we use the total squared sum over all four
89 // channels instead of deinterlacing each channel separately.
91 // Note that there's a small, arbitrary bonus for this first alternative,
92 // so that vertical interpolation wins if everything else is equal.
95 float best_score = DIFF(c, j) + DIFF(d, k) + DIFF(e, l) - 1e-4;
98 score = DIFF(b, k) + DIFF(c, l) + DIFF(d, m);
99 if (score < best_score) {
105 score = DIFF(a, l) + DIFF(b, m) + DIFF(c, n);
106 if (score < best_score) {
112 score = DIFF(d, i) + DIFF(e, j) + DIFF(f, k);
113 if (score < best_score) {
119 score = DIFF(e, h) + DIFF(f, i) + DIFF(g, j);
120 if (score < best_score) {
122 // best_score isn't used anymore.
127 // Now we do a temporal prediction (p2) of this pixel based on the previous
128 // and next fields. The spatial prediction is clamped so that it is not
129 // too far from this temporal prediction, where “too far” is based on
130 // the amount of local temporal change. (In other words, the temporal prediction
131 // is the safe choice, and the question is how far away from that we'll let
132 // our spatial choice run.) Note that here, our difference metric
133 // _is_ the same as C YADIF, namely per-channel abs.
135 // The sample positions look like this; in order to avoid variable name conflicts
136 // with the spatial interpolation, we use uppercase names. x is, again,
137 // the current pixel we're trying to estimate.
145 vec2 AFK_pos = d_pos;
146 vec2 BGL_pos = k_pos;
147 vec4 A = INPUT1(AFK_pos);
148 vec4 B = INPUT1(BGL_pos);
151 vec4 K = INPUT5(AFK_pos);
152 vec4 L = INPUT5(BGL_pos);
154 vec2 CH_pos = vec2(tc.x, tc.y + PREFIX(other_offset)[2]);
155 vec2 DI_pos = vec2(tc.x, tc.y + PREFIX(other_offset)[1]);
156 vec2 EJ_pos = vec2(tc.x, tc.y + PREFIX(other_offset)[0]);
158 vec4 C = INPUT2(CH_pos);
159 vec4 D = INPUT2(DI_pos);
160 vec4 E = INPUT2(EJ_pos);
162 vec4 H = INPUT4(CH_pos);
163 vec4 I = INPUT4(DI_pos);
164 vec4 J = INPUT4(EJ_pos);
166 // Find temporal differences around this line, using all five fields.
167 // tdiff0 is around the current field, tdiff1 is around the previous one,
168 // tdiff2 is around the next one.
169 vec4 tdiff0 = abs(D - I);
170 vec4 tdiff1 = abs(A - F) + abs(B - G); // Actually twice tdiff1.
171 vec4 tdiff2 = abs(K - F) + abs(L - G); // Actually twice tdiff2.
172 vec4 diff = max(tdiff0, 0.5f * max(tdiff1, tdiff2));
174 // The following part is the spatial interlacing check, which loosens up the
175 // allowable temporal change. (See also the comments in the .h file.)
176 // It costs us four extra loads (C, E, H, J) and a few extra ALU ops;
177 // we're already very load-heavy, so the extra ALU is effectively free.
178 // It costs about 18% performance in some benchmarks, which squares
179 // well with going from 20 to 24 loads (a 20% increase), although for
180 // total overall performance in longer chains, the difference is nearly zero.
182 // The basic idea is seemingly to allow more change if there are large spatial
183 // vertical changes, even if there are few temporal changes. These differences
184 // are signed, though, which make it more tricky to follow, although they seem
185 // to reduce into some sort of pseudo-abs. I will not claim to understand them
188 // We start by temporally interpolating the current vertical line (p0–p4):
194 // E p4 J +-----> time
196 // YADIF_ENABLE_SPATIAL_INTERLACING_CHECK will be #defined to 1
197 // if the check is enabled. Otherwise, the compiler should
198 // be able to remove the dependent code quite easily.
199 vec4 p0 = 0.5f * (C + H);
201 vec4 p2 = 0.5f * (D + I);
203 vec4 p4 = 0.5f * (E + J);
205 #if YADIF_ENABLE_SPATIAL_INTERLACING_CHECK
206 vec4 max_ = max(max(p2 - p3, p2 - p1), min(p0 - p1, p4 - p3));
207 vec4 min_ = min(min(p2 - p3, p2 - p1), max(p0 - p1, p4 - p3));
208 diff = max(diff, max(min_, -max_));
211 return clamp(pred, p2 - diff, p2 + diff);
215 #undef YADIF_ENABLE_SPATIAL_INTERLACING_CHECK