]> git.sesse.net Git - movit/blob - deinterlace_effect.cpp
Release Movit 1.7.1.
[movit] / deinterlace_effect.cpp
1 #include <epoxy/gl.h>
2
3 #include "deinterlace_effect.h"
4 #include "effect_chain.h"
5 #include "init.h"
6 #include "util.h"
7
8 using namespace std;
9
10 namespace movit {
11
12 DeinterlaceEffect::DeinterlaceEffect()
13         : enable_spatial_interlacing_check(true),
14           current_field_position(TOP),
15           num_lines(1080)
16 {
17         if (movit_compute_shaders_supported) {
18                 compute_effect_owner.reset(new DeinterlaceComputeEffect);
19                 compute_effect = compute_effect_owner.get();
20         } else {
21                 register_int("enable_spatial_interlacing_check", (int *)&enable_spatial_interlacing_check);
22                 register_int("current_field_position", (int *)&current_field_position);
23                 register_uniform_float("num_lines", &num_lines);
24                 register_uniform_float("inv_width", &inv_width);
25                 register_uniform_float("self_offset", &self_offset);
26                 register_uniform_float_array("current_offset", current_offset, 2);
27                 register_uniform_float_array("other_offset", other_offset, 3);
28         }
29 }
30
31 string DeinterlaceEffect::output_fragment_shader()
32 {
33         char buf[256];
34         snprintf(buf, sizeof(buf), "#define YADIF_ENABLE_SPATIAL_INTERLACING_CHECK %d\n",
35                 enable_spatial_interlacing_check);
36         string frag_shader = buf;
37
38         frag_shader += read_file("deinterlace_effect.frag");
39         return frag_shader;
40 }
41
42 void DeinterlaceEffect::rewrite_graph(EffectChain *graph, Node *self)
43 {
44         if (compute_effect != nullptr) {
45                 Node *compute_node = graph->add_node(compute_effect_owner.release());
46                 graph->replace_receiver(self, compute_node);
47                 graph->replace_sender(self, compute_node);
48                 self->disabled = true;
49         }
50 }
51
52 bool DeinterlaceEffect::set_int(const std::string &key, int value)
53 {
54         if (compute_effect != nullptr) {
55                 return compute_effect->set_int(key, value);
56         } else {
57                 return Effect::set_int(key, value);
58         }
59 }
60
61 void DeinterlaceEffect::inform_input_size(unsigned input_num, unsigned width, unsigned height)
62 {
63         assert(input_num >= 0 && input_num < 5);
64         widths[input_num] = width;
65         heights[input_num] = height;
66         num_lines = height * 2;
67 }
68
69 void DeinterlaceEffect::get_output_size(unsigned *width, unsigned *height,
70                                         unsigned *virtual_width, unsigned *virtual_height) const
71 {
72         assert(widths[0] == widths[1]);
73         assert(widths[1] == widths[2]);
74         assert(widths[2] == widths[3]);
75         assert(widths[3] == widths[4]);
76         assert(heights[0] == heights[1]);
77         assert(heights[1] == heights[2]);
78         assert(heights[2] == heights[3]);
79         assert(heights[3] == heights[4]);
80         *width = *virtual_width = widths[0];
81         *height = *virtual_height = heights[0] * 2;
82 }
83
84 void DeinterlaceEffect::set_gl_state(GLuint glsl_program_num, const string &prefix, unsigned *sampler_num)
85 {
86         Effect::set_gl_state(glsl_program_num, prefix, sampler_num);
87
88         inv_width = 1.0 / widths[0];
89
90         // Texel centers: t = output texel center for top field, b = for bottom field,
91         // x = the input texel. (The same area is two pixels for output, one for input;
92         // thus the stippled line in the middle.)
93         //
94         // +---------+
95         // |         |
96         // |    t    |
97         // |         |
98         // | - -x- - |
99         // |         |
100         // |    b    |
101         // |         |
102         // +---------+
103         //
104         // Note as usual OpenGL's bottom-left convention.
105         if (current_field_position == 0) {
106                 // Top.
107                 self_offset = -0.5 / num_lines;
108         } else {
109                 // Bottom.
110                 assert(current_field_position == 1);
111                 self_offset = 0.5 / num_lines;
112         }
113
114         // Having now established where the texels lie for the uninterpolated samples,
115         // we can use that to figure out where to sample for the interpolation. Drawing
116         // the fields as what lines they represent, here for three-pixel high fields
117         // with current_field_position == 0 (plus an “o” to mark the pixel we're trying
118         // to interpolate, and “c” for corresponding texel in the other field):
119         //
120         // Prev Cur Next
121         //       x
122         //   x       x
123         //       x
124         //   c   o   c
125         //       x
126         //   x       x
127         //
128         // Obviously, for sampling in the current field, we are one half-texel off
129         // compared to <self_offset>, so sampling in the current field is easy:
130         current_offset[0] = self_offset - 0.5 / heights[0];
131         current_offset[1] = self_offset + 0.5 / heights[0];
132
133         // Now to find the texel in the other fields corresponding to the pixel
134         // we're trying to interpolate, let's realign the diagram above:
135         //
136         // Prev Cur Next
137         //   x   x   x
138         //
139         //   c   x   c
140         //       o
141         //   x   x   x
142         //
143         // So obviously for this case, we need to center on the same place as
144         // current_offset[1] (the texel directly above the o; note again the
145         // bottom-left convention). For the case of current_field_position == 1,
146         // the shift in the alignment goes the other way, and what we want
147         // is current_offset[0] (the texel directly below the o).
148         float center_offset = current_offset[1 - current_field_position];
149         other_offset[0] = center_offset - 1.0 / heights[0];
150         other_offset[1] = center_offset;
151         other_offset[2] = center_offset + 1.0 / heights[0];
152 }
153
154 // Implementation of DeinterlaceComputeEffect.
155
156 DeinterlaceComputeEffect::DeinterlaceComputeEffect()
157         : enable_spatial_interlacing_check(true),
158           current_field_position(TOP)
159 {
160         register_int("enable_spatial_interlacing_check", (int *)&enable_spatial_interlacing_check);
161         register_int("current_field_position", (int *)&current_field_position);
162         register_uniform_float("inv_width", &inv_width);
163         register_uniform_float("inv_height", &inv_height);
164         register_uniform_float("current_field_vertical_offset", &current_field_vertical_offset);
165 }
166
167 string DeinterlaceComputeEffect::output_fragment_shader()
168 {
169         char buf[256];
170         snprintf(buf, sizeof(buf), "#define YADIF_ENABLE_SPATIAL_INTERLACING_CHECK %d\n",
171                 enable_spatial_interlacing_check);
172         string frag_shader = buf;
173
174         frag_shader += read_file("deinterlace_effect.comp");
175         return frag_shader;
176 }
177
178 void DeinterlaceComputeEffect::inform_input_size(unsigned input_num, unsigned width, unsigned height)
179 {
180         assert(input_num >= 0 && input_num < 5);
181         widths[input_num] = width;
182         heights[input_num] = height;
183 }
184
185 void DeinterlaceComputeEffect::get_output_size(unsigned *width, unsigned *height,
186                                         unsigned *virtual_width, unsigned *virtual_height) const
187 {
188         assert(widths[0] == widths[1]);
189         assert(widths[1] == widths[2]);
190         assert(widths[2] == widths[3]);
191         assert(widths[3] == widths[4]);
192         assert(heights[0] == heights[1]);
193         assert(heights[1] == heights[2]);
194         assert(heights[2] == heights[3]);
195         assert(heights[3] == heights[4]);
196         *width = *virtual_width = widths[0];
197         *height = *virtual_height = heights[0] * 2;
198 }
199
200 void DeinterlaceComputeEffect::set_gl_state(GLuint glsl_program_num, const string &prefix, unsigned *sampler_num)
201 {
202         Effect::set_gl_state(glsl_program_num, prefix, sampler_num);
203
204         inv_width = 1.0 / widths[0];
205         inv_height = 1.0 / heights[0];
206
207         // For the compute shader, we need to load a block of pixels. Marking off the
208         // ones we are supposed to interpolate (looking only at one column):
209         //
210         //  field_pos==0            field_pos==1
211         //
212         //  6     x      ↑          6     .      ↑
213         //  6     .      |          6     x      |
214         //  5     x      |          5     .      |
215         //  5     .      |          5     x      |
216         //  4     x      |          4     .      |
217         //  4     .      |          4     x      |
218         //  3     x      | y        3     o      | y
219         //  3     o      |          3     x      |
220         //  2     x      |          2     o      |
221         //  2     o      |          2     x      |
222         //  1     x      |          1     .      |
223         //  1     .      |          1     x      |
224         //  0     x      |          0     .      |
225         //  0     .      |          0     x      |
226         //
227         // So if we are to compute e.g. output samples [2,4), we load input samples
228         // [1,3] for TFF and samples [2,4] for BFF.
229         if (current_field_position == 0) {
230                 current_field_vertical_offset = -1.0 / heights[0];
231         } else {
232                 current_field_vertical_offset =  0.0 / heights[0];
233         }
234 }
235
236 void DeinterlaceComputeEffect::get_compute_dimensions(unsigned output_width, unsigned output_height,
237                                                unsigned *x, unsigned *y, unsigned *z) const
238 {
239         // Each workgroup outputs 8x32 pixels (see GROUP_W and GROUP_H in the shader),
240         // so figure out the number of groups by simply rounding up.
241         *x = (output_width + 7) / 8;
242         *y = (output_height + 31) / 32;
243         *z = 1;
244 }
245
246 }  // namespace movit