Reduce the amount of arithmetic in the BlurEffect shader a bit.
[movit] / fft_input.cpp
1 #include <string.h>
2 #include <assert.h>
3 #include <GL/glew.h>
4 #include <fftw3.h>
5
6 #include "effect_util.h"
7 #include "fp16.h"
8 #include "fft_input.h"
9 #include "resource_pool.h"
10 #include "util.h"
11
12 using namespace std;
13
14 namespace movit {
15
16 FFTInput::FFTInput(unsigned width, unsigned height)
17         : texture_num(0),
18           fft_width(width),
19           fft_height(height),
20           convolve_width(width),
21           convolve_height(height),
22           pixel_data(NULL)
23 {
24         register_int("fft_width", &fft_width);
25         register_int("fft_height", &fft_height);
26 }
27
28 FFTInput::~FFTInput()
29 {
30         if (texture_num != 0) {
31                 resource_pool->release_2d_texture(texture_num);
32         }
33 }
34
35 void FFTInput::set_gl_state(GLuint glsl_program_num, const string& prefix, unsigned *sampler_num)
36 {
37         glActiveTexture(GL_TEXTURE0 + *sampler_num);
38         check_error();
39
40         if (texture_num == 0) {
41                 assert(pixel_data != NULL);
42
43                 // Do the FFT. Our FFTs should typically be small enough and
44                 // the data changed often enough that FFTW_ESTIMATE should be
45                 // quite OK. Otherwise, we'd need to worry about caching these
46                 // plans (possibly including FFTW wisdom).
47                 fftw_complex *in = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * fft_width * fft_height);
48                 fftw_complex *out = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * fft_width * fft_height);
49                 fftw_plan p = fftw_plan_dft_2d(fft_height, fft_width, in, out, FFTW_FORWARD, FFTW_ESTIMATE);
50
51                 // Zero pad.
52                 for (int i = 0; i < fft_height * fft_width; ++i) {
53                         in[i][0] = 0.0;
54                         in[i][1] = 0.0;
55                 }
56                 for (unsigned y = 0; y < convolve_height; ++y) {
57                         for (unsigned x = 0; x < convolve_width; ++x) {
58                                 int i = y * fft_width + x;
59                                 in[i][0] = pixel_data[y * convolve_width + x];
60                                 in[i][1] = 0.0;
61                         }
62                 }
63
64                 fftw_execute(p);
65
66                 // Convert to fp16.
67                 fp16_int_t *kernel = new fp16_int_t[fft_width * fft_height * 2];
68                 for (int i = 0; i < fft_width * fft_height; ++i) {
69                         kernel[i * 2 + 0] = fp64_to_fp16(out[i][0]);
70                         kernel[i * 2 + 1] = fp64_to_fp16(out[i][1]);
71                 }
72
73                 // (Re-)upload the texture.
74                 texture_num = resource_pool->create_2d_texture(GL_RG16F, fft_width, fft_height);
75                 glBindTexture(GL_TEXTURE_2D, texture_num);
76                 check_error();
77                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
78                 check_error();
79                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
80                 check_error();
81                 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
82                 check_error();
83                 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, fft_width, fft_height, GL_RG, GL_HALF_FLOAT, kernel);
84                 check_error();
85                 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
86                 check_error();
87                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
88                 check_error();
89                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
90                 check_error();
91
92                 fftw_free(in);
93                 fftw_free(out);
94                 delete[] kernel;
95         } else {
96                 glBindTexture(GL_TEXTURE_2D, texture_num);
97                 check_error();
98         }
99
100         // Bind it to a sampler.
101         set_uniform_int(glsl_program_num, prefix, "tex", *sampler_num);
102         ++*sampler_num;
103 }
104
105 string FFTInput::output_fragment_shader()
106 {
107         return read_file("flat_input.frag");
108 }
109
110 void FFTInput::invalidate_pixel_data()
111 {
112         if (texture_num != 0) {
113                 resource_pool->release_2d_texture(texture_num);
114                 texture_num = 0;
115         }
116 }
117
118 bool FFTInput::set_int(const std::string& key, int value)
119 {
120         if (key == "needs_mipmaps") {
121                 // We cannot supply mipmaps; it would not make any sense for FFT data.
122                 return (value == 0);
123         }
124         if (key == "fft_width") {
125                 if (value < int(convolve_width)) {
126                         return false;
127                 }
128                 invalidate_pixel_data();
129         }
130         if (key == "fft_height") {
131                 if (value < int(convolve_height)) {
132                         return false;
133                 }
134                 invalidate_pixel_data();
135         }
136         return Effect::set_int(key, value);
137 }
138
139 }  // namespace movit