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