aca85b9d404f152b8971230612c696dbc747a11e
[movit] / init.cpp
1 #include <GL/glew.h>
2 #include <assert.h>
3 #include <stddef.h>
4 #include <algorithm>
5 #include <string>
6
7 #include "init.h"
8 #include "util.h"
9
10 bool movit_initialized = false;
11 MovitDebugLevel movit_debug_level = MOVIT_DEBUG_ON;
12 float movit_texel_subpixel_precision;
13 bool movit_srgb_textures_supported;
14 int movit_num_wrongly_rounded;
15
16 // The rules for objects with nontrivial constructors in static scope
17 // are somewhat convoluted, and easy to mess up. We simply have a
18 // pointer instead (and never care to clean it up).
19 std::string *movit_data_directory = NULL;
20
21 namespace {
22
23 void measure_texel_subpixel_precision()
24 {
25         static const unsigned width = 4096;
26
27         // Generate a destination texture to render to, and an FBO.
28         GLuint dst_texnum, fbo;
29
30         glGenTextures(1, &dst_texnum);
31         check_error();
32         glBindTexture(GL_TEXTURE_2D, dst_texnum);
33         check_error();
34         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, width, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
35         check_error();
36
37         glGenFramebuffers(1, &fbo);
38         check_error();
39         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
40         check_error();
41         glFramebufferTexture2D(
42                 GL_FRAMEBUFFER,
43                 GL_COLOR_ATTACHMENT0,
44                 GL_TEXTURE_2D,
45                 dst_texnum,
46                 0);
47         check_error();
48
49         // Now generate a simple texture that's just [0,1].
50         GLuint src_texnum;
51         float texdata[] = { 0, 1 };
52         glGenTextures(1, &src_texnum);
53         check_error();
54         glBindTexture(GL_TEXTURE_1D, src_texnum);
55         check_error();
56         glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
57         check_error();
58         glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
59         check_error();
60         glTexImage1D(GL_TEXTURE_1D, 0, GL_LUMINANCE16F_ARB, 2, 0, GL_LUMINANCE, GL_FLOAT, texdata);
61         check_error();
62         glEnable(GL_TEXTURE_1D);
63         check_error();
64
65         // Basic state.
66         glDisable(GL_BLEND);
67         check_error();
68         glDisable(GL_DEPTH_TEST);
69         check_error();
70         glDepthMask(GL_FALSE);
71         check_error();
72
73         glViewport(0, 0, width, 1);
74
75         glMatrixMode(GL_PROJECTION);
76         glLoadIdentity();
77         glOrtho(0.0, 1.0, 0.0, 1.0, 0.0, 1.0);
78
79         glMatrixMode(GL_MODELVIEW);
80         glLoadIdentity();
81         check_error();
82
83         // Draw the texture stretched over a long quad, interpolating it out.
84         // Note that since the texel center is in (0.5), we need to adjust the
85         // texture coordinates in order not to get long stretches of (1,1,1,...)
86         // at the start and (...,0,0,0) at the end.
87         glBegin(GL_QUADS);
88
89         glTexCoord1f(0.25f);
90         glVertex2f(0.0f, 0.0f);
91
92         glTexCoord1f(0.75f);
93         glVertex2f(1.0f, 0.0f);
94
95         glTexCoord1f(0.75f);
96         glVertex2f(1.0f, 1.0f);
97
98         glTexCoord1f(0.25f);
99         glVertex2f(0.0f, 1.0f);
100
101         glEnd();
102         check_error();
103
104         glDisable(GL_TEXTURE_1D);
105         check_error();
106
107         // Now read the data back and see what the card did.
108         // (We only look at the red channel; the others will surely be the same.)
109         // We assume a linear ramp; anything else will give sort of odd results here.
110         float out_data[width];
111         glReadPixels(0, 0, width, 1, GL_RED, GL_FLOAT, out_data);
112         check_error();
113
114         float biggest_jump = 0.0f;
115         for (unsigned i = 1; i < width; ++i) {
116                 assert(out_data[i] >= out_data[i - 1]);
117                 biggest_jump = std::max(biggest_jump, out_data[i] - out_data[i - 1]);
118         }
119
120         movit_texel_subpixel_precision = biggest_jump;
121
122         // Clean up.
123         glBindTexture(GL_TEXTURE_1D, 0);
124         check_error();
125         glBindFramebuffer(GL_FRAMEBUFFER, 0);
126         check_error();
127         glDeleteFramebuffers(1, &fbo);
128         check_error();
129         glDeleteTextures(1, &dst_texnum);
130         check_error();
131         glDeleteTextures(1, &src_texnum);
132         check_error();
133 }
134
135 void measure_roundoff_problems()
136 {
137         // Generate a destination texture to render to, and an FBO.
138         GLuint dst_texnum, fbo;
139
140         glGenTextures(1, &dst_texnum);
141         check_error();
142         glBindTexture(GL_TEXTURE_2D, dst_texnum);
143         check_error();
144         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
145         check_error();
146
147         glGenFramebuffers(1, &fbo);
148         check_error();
149         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
150         check_error();
151         glFramebufferTexture2D(
152                 GL_FRAMEBUFFER,
153                 GL_COLOR_ATTACHMENT0,
154                 GL_TEXTURE_2D,
155                 dst_texnum,
156                 0);
157         check_error();
158
159         // Now generate a texture where every value except the last should be
160         // rounded up to the next one. However, there are cards (in highly
161         // common use) that can't do this right, for unknown reasons.
162         GLuint src_texnum;
163         float texdata[512];
164         for (int i = 0; i < 256; ++i) {
165                 texdata[i * 2 + 0] = (i + 0.48) / 255.0;
166                 texdata[i * 2 + 1] = (i + 0.52) / 255.0;
167         }
168         glGenTextures(1, &src_texnum);
169         check_error();
170         glBindTexture(GL_TEXTURE_1D, src_texnum);
171         check_error();
172         glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
173         check_error();
174         glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
175         check_error();
176         glTexImage1D(GL_TEXTURE_1D, 0, GL_LUMINANCE32F_ARB, 512, 0, GL_LUMINANCE, GL_FLOAT, texdata);
177         check_error();
178         glEnable(GL_TEXTURE_1D);
179         check_error();
180
181         // Basic state.
182         glDisable(GL_BLEND);
183         check_error();
184         glDisable(GL_DEPTH_TEST);
185         check_error();
186         glDepthMask(GL_FALSE);
187         check_error();
188
189         glViewport(0, 0, 512, 1);
190
191         glMatrixMode(GL_PROJECTION);
192         glLoadIdentity();
193         glOrtho(0.0, 1.0, 0.0, 1.0, 0.0, 1.0);
194
195         glMatrixMode(GL_MODELVIEW);
196         glLoadIdentity();
197         check_error();
198
199         // Draw the texture stretched over a long quad, interpolating it out.
200         glBegin(GL_QUADS);
201
202         glTexCoord1f(0.0f);
203         glVertex2f(0.0f, 0.0f);
204
205         glTexCoord1f(1.0f);
206         glVertex2f(1.0f, 0.0f);
207
208         glTexCoord1f(1.0f);
209         glVertex2f(1.0f, 1.0f);
210
211         glTexCoord1f(0.0f);
212         glVertex2f(0.0f, 1.0f);
213
214         glEnd();
215         check_error();
216
217         glDisable(GL_TEXTURE_1D);
218         check_error();
219
220         // Now read the data back and see what the card did. (Ignore the last value.)
221         // (We only look at the red channel; the others will surely be the same.)
222         unsigned char out_data[512];
223         glReadPixels(0, 0, 512, 1, GL_RED, GL_UNSIGNED_BYTE, out_data);
224         check_error();
225
226         int wrongly_rounded = 0;
227         for (unsigned i = 0; i < 255; ++i) {
228                 if (out_data[i * 2 + 0] != i) {
229                         ++wrongly_rounded;
230                 }
231                 if (out_data[i * 2 + 1] != i + 1) {
232                         ++wrongly_rounded;
233                 }
234         }
235
236         movit_num_wrongly_rounded = wrongly_rounded;
237
238         // Clean up.
239         glBindTexture(GL_TEXTURE_1D, 0);
240         check_error();
241         glBindFramebuffer(GL_FRAMEBUFFER, 0);
242         check_error();
243         glDeleteFramebuffers(1, &fbo);
244         check_error();
245         glDeleteTextures(1, &dst_texnum);
246         check_error();
247         glDeleteTextures(1, &src_texnum);
248         check_error();
249 }
250
251 void check_extensions()
252 {
253         // We fundamentally need FBOs and floating-point textures.
254         assert(glewIsSupported("GL_ARB_framebuffer_object") != 0);
255         assert(glewIsSupported("GL_ARB_texture_float") != 0);
256
257         // We assume that we can use non-power-of-two textures without restrictions.
258         assert(glewIsSupported("GL_ARB_texture_non_power_of_two") != 0);
259
260         // We also need GLSL fragment shaders.
261         assert(glewIsSupported("GL_ARB_fragment_shader") != 0);
262         assert(glewIsSupported("GL_ARB_shading_language_100") != 0);
263
264         // FlatInput and YCbCrInput uses PBOs. (They could in theory do without,
265         // but no modern card would really not provide it.)
266         assert(glewIsSupported("GL_ARB_pixel_buffer_object") != 0);
267
268         // ResampleEffect uses RG textures to encode a two-component LUT.
269         assert(glewIsSupported("GL_ARB_texture_rg") != 0);
270
271         // sRGB texture decode would be nice, but are not mandatory
272         // (GammaExpansionEffect can do the same thing if needed).
273         movit_srgb_textures_supported = glewIsSupported("GL_EXT_texture_sRGB");
274 }
275
276 }  // namespace
277
278 void init_movit(const std::string& data_directory, MovitDebugLevel debug_level)
279 {
280         if (movit_initialized) {
281                 return;
282         }
283
284         movit_data_directory = new std::string(data_directory);
285         movit_debug_level = debug_level;
286
287         glewInit();
288
289         // geez 
290         glPixelStorei(GL_PACK_ALIGNMENT, 1);
291         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
292         glDisable(GL_DITHER);
293
294         measure_texel_subpixel_precision();
295         measure_roundoff_problems();
296         check_extensions();
297
298         movit_initialized = true;
299 }