Check required extensions at start.
[movit] / init.cpp
1 #include "init.h"
2 #include "opengl.h"
3 #include "util.h"
4
5 #include <set>
6 #include <string>
7
8 bool movit_initialized = false;
9 float movit_texel_subpixel_precision;
10 bool movit_srgb_textures_supported;
11
12 namespace {
13
14 void measure_texel_subpixel_precision()
15 {
16         static const unsigned width = 1024;
17
18         // Generate a destination texture to render to, and an FBO.
19         GLuint dst_texnum, fbo;
20
21         glGenTextures(1, &dst_texnum);
22         check_error();
23         glBindTexture(GL_TEXTURE_2D, dst_texnum);
24         check_error();
25         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, width, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
26         check_error();
27
28         glGenFramebuffers(1, &fbo);
29         check_error();
30         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
31         check_error();
32         glFramebufferTexture2D(
33                 GL_FRAMEBUFFER,
34                 GL_COLOR_ATTACHMENT0,
35                 GL_TEXTURE_2D,
36                 dst_texnum,
37                 0);
38         check_error();
39
40         // Now generate a simple texture that's just [0,1].
41         GLuint src_texnum;
42         float texdata[] = { 0, 1 };
43         glGenTextures(1, &dst_texnum);
44         check_error();
45         glBindTexture(GL_TEXTURE_1D, dst_texnum);
46         check_error();
47         glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
48         check_error();
49         glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
50         check_error();
51         glTexImage1D(GL_TEXTURE_1D, 0, GL_LUMINANCE16F_ARB, 2, 0, GL_LUMINANCE, GL_FLOAT, texdata);
52         check_error();
53         glEnable(GL_TEXTURE_1D);
54         check_error();
55
56         // Basic state.
57         glDisable(GL_BLEND);
58         check_error();
59         glDisable(GL_DEPTH_TEST);
60         check_error();
61         glDepthMask(GL_FALSE);
62         check_error();
63
64         glViewport(0, 0, width, 1);
65
66         glMatrixMode(GL_PROJECTION);
67         glLoadIdentity();
68         glOrtho(0.0, 1.0, 0.0, 1.0, 0.0, 1.0);
69
70         glMatrixMode(GL_MODELVIEW);
71         glLoadIdentity();
72         check_error();
73
74         // Draw the texture stretched over a long quad, interpolating it out.
75         // Note that since the texel center is in (0.5), we need to adjust the
76         // texture coordinates in order not to get long stretches of (1,1,1,...)
77         // at the start and (...,0,0,0) at the end.
78         glBegin(GL_QUADS);
79
80         glTexCoord1f(0.25f);
81         glVertex2f(0.0f, 0.0f);
82
83         glTexCoord1f(0.75f);
84         glVertex2f(1.0f, 0.0f);
85
86         glTexCoord1f(0.75f);
87         glVertex2f(1.0f, 1.0f);
88
89         glTexCoord1f(0.25f);
90         glVertex2f(0.0f, 1.0f);
91
92         glEnd();
93         check_error();
94
95         glDisable(GL_TEXTURE_1D);
96         check_error();
97
98         // Now read the data back and see what the card did.
99         // (We only look at the red channel; the others will surely be the same.)
100         // We assume a linear ramp; anything else will give sort of odd results here.
101         float out_data[width];
102         glReadPixels(0, 0, width, 1, GL_RED, GL_FLOAT, out_data);
103         check_error();
104
105         float biggest_jump = 0.0f;
106         for (unsigned i = 1; i < width; ++i) {
107                 assert(out_data[i] >= out_data[i - 1]);
108                 biggest_jump = std::max(biggest_jump, out_data[i] - out_data[i - 1]);
109         }
110
111         movit_texel_subpixel_precision = biggest_jump;
112
113         // Clean up.
114         glBindTexture(GL_TEXTURE_1D, 0);
115         check_error();
116         glBindFramebuffer(GL_FRAMEBUFFER, 0);
117         check_error();
118         glDeleteFramebuffers(1, &fbo);
119         check_error();
120         glDeleteTextures(1, &dst_texnum);
121         check_error();
122         glDeleteTextures(1, &src_texnum);
123         check_error();
124 }
125
126 void get_extensions(std::set<std::string> *extensions)
127 {
128         char *str = strdup(reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)));
129         for (char *ptr = strtok(str, " "); ptr != NULL; ptr = strtok(NULL, " ")) {
130                 extensions->insert(ptr);
131         }
132         free(str);
133 }
134
135 void check_extensions()
136 {
137         std::set<std::string> extensions;
138         get_extensions(&extensions);
139
140         // We fundamentally need FBOs and floating-point textures.
141         assert(extensions.count("GL_ARB_framebuffer_object") != 0);
142         assert(extensions.count("GL_ARB_texture_float") != 0);
143
144         // We assume that we can use non-power-of-two textures without restrictions.
145         assert(extensions.count("GL_ARB_texture_non_power_of_two") != 0);
146
147         // We also need GLSL fragment shaders.
148         assert(extensions.count("GL_ARB_fragment_shader") != 0);
149         assert(extensions.count("GL_ARB_shading_language_100") != 0);
150
151         // FlatInput and YCbCrInput uses PBOs. (They could in theory do without,
152         // but no modern card would really not provide it.)
153         assert(extensions.count("GL_ARB_pixel_buffer_object") != 0);
154
155         // ResampleEffect uses RG textures to encode a two-component LUT.
156         assert(extensions.count("GL_ARB_texture_rg") != 0);
157
158         // sRGB texture decode would be nice, but are not mandatory
159         // (GammaExpansionEffect can do the same thing if needed).
160         movit_srgb_textures_supported = extensions.count("GL_EXT_texture_sRGB");
161 }
162
163 }  // namespace
164
165 void init_movit()
166 {
167         if (movit_initialized) {
168                 return;
169         }
170
171         // geez 
172         glPixelStorei(GL_PACK_ALIGNMENT, 1);
173         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
174
175         measure_texel_subpixel_precision();
176         check_extensions();
177
178         movit_initialized = true;
179 }