]> git.sesse.net Git - movit/blob - util.cpp
Release Movit 1.7.1.
[movit] / util.cpp
1 #include <epoxy/gl.h>
2 #include <assert.h>
3 #include <math.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <locale>
8 #include <sstream>
9 #include <string>
10 #include <Eigen/Core>
11
12 #include "bundled_shaders.h"
13 #include "fp16.h"
14 #include "init.h"
15 #include "util.h"
16
17 #if defined(__APPLE__)
18 #include <OpenGL/OpenGL.h>
19 #elif defined(WIN32)
20 #include <epoxy/wgl.h>
21 #else
22 #include <epoxy/glx.h>
23 #include <epoxy/egl.h>
24 #endif
25
26 using namespace std;
27
28 namespace movit {
29
30 extern string *movit_data_directory;
31
32 void hsv2rgb(float h, float s, float v, float *r, float *g, float *b)
33 {
34         if (h < 0.0f) {
35                 h += 2.0f * M_PI;
36         }
37         float c = v * s;
38         float hp = (h * 180.0 / M_PI) / 60.0;
39         float x = c * (1 - fabs(fmod(hp, 2.0f) - 1.0f));
40
41         if (hp >= 0 && hp < 1) {
42                 *r = c;
43                 *g = x;
44                 *b = 0.0f;
45         } else if (hp >= 1 && hp < 2) {
46                 *r = x;
47                 *g = c;
48                 *b = 0.0f;
49         } else if (hp >= 2 && hp < 3) {
50                 *r = 0.0f;
51                 *g = c;
52                 *b = x;
53         } else if (hp >= 3 && hp < 4) {
54                 *r = 0.0f;
55                 *g = x;
56                 *b = c;
57         } else if (hp >= 4 && hp < 5) {
58                 *r = x;
59                 *g = 0.0f;
60                 *b = c;
61         } else {
62                 *r = c;
63                 *g = 0.0f;
64                 *b = x;
65         }
66
67         float m = v - c;
68         *r += m;
69         *g += m;
70         *b += m;
71 }
72
73 void hsv2rgb_normalized(float h, float s, float v, float *r, float *g, float *b)
74 {
75         float ref_r, ref_g, ref_b;
76         hsv2rgb(h, s, v, r, g, b);
77         hsv2rgb(h, 0.0f, v, &ref_r, &ref_g, &ref_b);
78         float lum = 0.2126 * *r + 0.7152 * *g + 0.0722 * *b;
79         float ref_lum = 0.2126 * ref_r + 0.7152 * ref_g + 0.0722 * ref_b;
80         if (lum > 1e-3) {
81                 float fac = ref_lum / lum;
82                 *r *= fac;
83                 *g *= fac;
84                 *b *= fac;
85         }
86 }
87
88 string read_file_from_bundle(const string &filename)
89 {
90         for (const BundledShader *shader = bundled_shaders; shader->filename != nullptr; ++shader) {
91                 if (shader->filename == filename) {
92                         return string(shader_bundle + shader->offset, shader->length);
93                 }
94         }
95         return "";  // Not found.
96 }
97
98 string read_file(const string &filename)
99 {
100         string contents_from_bundle = read_file_from_bundle(filename);
101
102         // If no data directory has been given, we read only from the bundle.
103         if (*movit_data_directory == "") {
104                 if (contents_from_bundle.empty()) {
105                         fprintf(stderr, "%s: Shader not compiled in, and no data directory has been given.\n", filename.c_str());
106                         exit(1);
107                 } else {
108                         return contents_from_bundle;
109                 }
110         }
111
112         // If we're given a data directory, we still support reading from the bundle,
113         // but a successful read from the file system takes priority.
114         const string full_pathname = *movit_data_directory + "/" + filename;
115
116         FILE *fp = fopen(full_pathname.c_str(), "r");
117         if (fp == nullptr) {
118                 if (!contents_from_bundle.empty()) {
119                         return contents_from_bundle;
120                 }
121                 perror(full_pathname.c_str());
122                 exit(1);
123         }
124
125         int ret = fseek(fp, 0, SEEK_END);
126         if (ret == -1) {
127                 if (!contents_from_bundle.empty()) {
128                         fclose(fp);
129                         return contents_from_bundle;
130                 }
131                 perror("fseek(SEEK_END)");
132                 exit(1);
133         }
134
135         int size = ftell(fp);
136
137         ret = fseek(fp, 0, SEEK_SET);
138         if (ret == -1) {
139                 if (!contents_from_bundle.empty()) {
140                         fclose(fp);
141                         return contents_from_bundle;
142                 }
143                 perror("fseek(SEEK_SET)");
144                 exit(1);
145         }
146
147         string str;
148         str.resize(size);
149         ret = fread(&str[0], size, 1, fp);
150         if (ret == -1) {
151                 if (!contents_from_bundle.empty()) {
152                         fclose(fp);
153                         return contents_from_bundle;
154                 }
155                 perror("fread");
156                 exit(1);
157         }
158         if (ret == 0) {
159                 if (!contents_from_bundle.empty()) {
160                         fclose(fp);
161                         return contents_from_bundle;
162                 }
163                 fprintf(stderr, "Short read when trying to read %d bytes from %s\n",
164                         size, full_pathname.c_str());
165                 exit(1);
166         }
167         fclose(fp);
168
169         return str;
170 }
171
172 string read_version_dependent_file(const string &base, const string &extension)
173 {
174         if (movit_shader_model == MOVIT_GLSL_130) {
175                 return read_file(base + ".130." + extension);
176         } else if (movit_shader_model == MOVIT_GLSL_150) {
177                 return read_file(base + ".150." + extension);
178         } else if (movit_shader_model == MOVIT_ESSL_300) {
179                 return read_file(base + ".300es." + extension);
180         } else {
181                 assert(false);
182         }
183 }
184
185 GLuint compile_shader(const string &shader_src, GLenum type)
186 {
187         GLuint obj = glCreateShader(type);
188         const GLchar* source[] = { shader_src.data() };
189         const GLint length[] = { (GLint)shader_src.size() };
190         glShaderSource(obj, 1, source, length);
191         glCompileShader(obj);
192
193         GLchar info_log[4096];
194         GLsizei log_length = sizeof(info_log) - 1;
195         glGetShaderInfoLog(obj, log_length, &log_length, info_log);
196         info_log[log_length] = 0; 
197         if (strlen(info_log) > 0) {
198                 fprintf(stderr, "Shader compile log: %s\n", info_log);
199         }
200
201         GLint status;
202         glGetShaderiv(obj, GL_COMPILE_STATUS, &status);
203         if (status == GL_FALSE) {
204                 // Add some line numbers to easier identify compile errors.
205                 string src_with_lines = "/*   1 */ ";
206                 size_t lineno = 1;
207                 for (char ch : shader_src) {
208                         src_with_lines.push_back(ch);
209                         if (ch == '\n') {
210                                 char buf[32];
211                                 snprintf(buf, sizeof(buf), "/* %3zu */ ", ++lineno);
212                                 src_with_lines += buf;
213                         }
214                 }
215
216                 fprintf(stderr, "Failed to compile shader:\n%s\n", src_with_lines.c_str());
217                 exit(1);
218         }
219
220         return obj;
221 }
222
223 void print_3x3_matrix(const Eigen::Matrix3d& m)
224 {
225         printf("%6.4f %6.4f %6.4f\n", m(0,0), m(0,1), m(0,2));
226         printf("%6.4f %6.4f %6.4f\n", m(1,0), m(1,1), m(1,2));
227         printf("%6.4f %6.4f %6.4f\n", m(2,0), m(2,1), m(2,2));
228         printf("\n");
229 }
230
231 string output_glsl_mat3(const string &name, const Eigen::Matrix3d &m)
232 {
233         // Use stringstream to be independent of the current locale in a thread-safe manner.
234         stringstream ss;
235         ss.imbue(locale("C"));
236         ss.precision(8);
237         ss << scientific;
238         ss << "const mat3 " << name << " = mat3(\n";
239         ss << "    " << m(0,0) << ", " << m(1,0) << ", " << m(2,0) << ",\n";
240         ss << "    " << m(0,1) << ", " << m(1,1) << ", " << m(2,1) << ",\n";
241         ss << "    " << m(0,2) << ", " << m(1,2) << ", " << m(2,2) << ");\n\n";
242         return ss.str();
243 }
244
245 string output_glsl_float(const string &name, float x)
246 {
247         // Use stringstream to be independent of the current locale in a thread-safe manner.
248         stringstream ss;
249         ss.imbue(locale("C"));
250         ss.precision(8);
251         ss << scientific;
252         ss << "const float " << name << " = " << x << ";\n";
253         return ss.str();
254 }
255
256 string output_glsl_vec2(const string &name, float x, float y)
257 {
258         // Use stringstream to be independent of the current locale in a thread-safe manner.
259         stringstream ss;
260         ss.imbue(locale("C"));
261         ss.precision(8);
262         ss << scientific;
263         ss << "const vec2 " << name << " = vec2(" << x << ", " << y << ");\n";
264         return ss.str();
265 }
266
267 string output_glsl_vec3(const string &name, float x, float y, float z)
268 {
269         // Use stringstream to be independent of the current locale in a thread-safe manner.
270         stringstream ss;
271         ss.imbue(locale("C"));
272         ss.precision(8);
273         ss << scientific;
274         ss << "const vec3 " << name << " = vec3(" << x << ", " << y << ", " << z << ");\n";
275         return ss.str();
276 }
277
278 GLuint generate_vbo(GLint size, GLenum type, GLsizeiptr data_size, const GLvoid *data)
279 {
280         GLuint vbo;
281         glGenBuffers(1, &vbo);
282         check_error();
283         glBindBuffer(GL_ARRAY_BUFFER, vbo);
284         check_error();
285         glBufferData(GL_ARRAY_BUFFER, data_size, data, GL_STATIC_DRAW);
286         check_error();
287         glBindBuffer(GL_ARRAY_BUFFER, 0);
288         check_error();
289
290         return vbo;
291 }
292
293 GLuint fill_vertex_attribute(GLuint glsl_program_num, const string &attribute_name, GLint size, GLenum type, GLsizeiptr data_size, const GLvoid *data)
294 {
295         int attrib = glGetAttribLocation(glsl_program_num, attribute_name.c_str());
296         if (attrib == -1) {
297                 return -1;
298         }
299
300         GLuint vbo = generate_vbo(size, type, data_size, data);
301
302         glBindBuffer(GL_ARRAY_BUFFER, vbo);
303         check_error();
304         glEnableVertexAttribArray(attrib);
305         check_error();
306         glVertexAttribPointer(attrib, size, type, GL_FALSE, 0, BUFFER_OFFSET(0));
307         check_error();
308         glBindBuffer(GL_ARRAY_BUFFER, 0);
309         check_error();
310
311         return vbo;
312 }
313
314 void cleanup_vertex_attribute(GLuint glsl_program_num, const string &attribute_name, GLuint vbo)
315 {
316         int attrib = glGetAttribLocation(glsl_program_num, attribute_name.c_str());
317         if (attrib == -1) {
318                 return;
319         }
320
321         glDisableVertexAttribArray(attrib);
322         check_error();
323         glDeleteBuffers(1, &vbo);
324         check_error();
325 }
326
327 unsigned div_round_up(unsigned a, unsigned b)
328 {
329         return (a + b - 1) / b;
330 }
331
332 // Algorithm from http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2.
333 unsigned next_power_of_two(unsigned v)
334 {
335         v--;
336         v |= v >> 1;
337         v |= v >> 2;
338         v |= v >> 4;
339         v |= v >> 8;
340         v |= v >> 16;
341         v++;
342         return v;
343 }
344
345 void *get_gl_context_identifier()
346 {
347 #if defined(__APPLE__)
348         return (void *)CGLGetCurrentContext();
349 #elif defined(WIN32)
350         return (void *)wglGetCurrentContext();
351 #else
352         void *ret = (void *)eglGetCurrentContext();
353         if (ret != nullptr) {
354                 return ret;
355         }
356         return (void *)glXGetCurrentContext();
357 #endif
358 }
359
360 void abort_gl_error(GLenum err, const char *filename, int line)
361 {
362         const char *err_text = "unknown";
363
364         // All errors listed in the glGetError(3G) man page.
365         switch (err) {
366         case GL_NO_ERROR:
367                 err_text = "GL_NO_ERROR";  // Should not happen.
368                 break;
369         case GL_INVALID_ENUM:
370                 err_text = "GL_INVALID_ENUM";
371                 break;
372         case GL_INVALID_VALUE:
373                 err_text = "GL_INVALID_VALUE";
374                 break;
375         case GL_INVALID_OPERATION:
376                 err_text = "GL_INVALID_OPERATION";
377                 break;
378         case GL_INVALID_FRAMEBUFFER_OPERATION:
379                 err_text = "GL_INVALID_FRAMEBUFFER_OPERATION";
380                 break;
381         case GL_OUT_OF_MEMORY:
382                 err_text = "GL_OUT_OF_MEMORY";
383                 break;
384         case GL_STACK_UNDERFLOW:
385                 err_text = "GL_STACK_UNDERFLOW";
386                 break;
387         case GL_STACK_OVERFLOW:
388                 err_text = "GL_STACK_OVERFLOW";
389                 break;
390         }
391         fprintf(stderr, "GL error 0x%x (%s) at %s:%d\n", err, err_text, filename, line);
392         abort();
393 }
394
395 }  // namespace movit