]> git.sesse.net Git - movit/blob - resource_pool.cpp
Release Movit 1.6.3.
[movit] / resource_pool.cpp
1 #include <assert.h>
2 #include <pthread.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <algorithm>
6 #include <map>
7 #include <string>
8 #include <utility>
9 #include <epoxy/gl.h>
10
11 #include "init.h"
12 #include "resource_pool.h"
13 #include "util.h"
14
15 using namespace std;
16
17 namespace movit {
18
19 ResourcePool::ResourcePool(size_t program_freelist_max_length,
20                            size_t texture_freelist_max_bytes,
21                            size_t fbo_freelist_max_length,
22                            size_t vao_freelist_max_length)
23         : program_freelist_max_length(program_freelist_max_length),
24           texture_freelist_max_bytes(texture_freelist_max_bytes),
25           fbo_freelist_max_length(fbo_freelist_max_length),
26           vao_freelist_max_length(vao_freelist_max_length),
27           texture_freelist_bytes(0)
28 {
29         pthread_mutex_init(&lock, nullptr);
30 }
31
32 ResourcePool::~ResourcePool()
33 {
34         assert(program_refcount.empty());
35
36         for (GLuint program : program_freelist) {
37                 delete_program(program);
38         }
39         assert(programs.empty());
40         assert(program_shaders.empty());
41
42         for (GLuint free_texture_num : texture_freelist) {
43                 assert(texture_formats.count(free_texture_num) != 0);
44                 texture_freelist_bytes -= estimate_texture_size(texture_formats[free_texture_num]);
45                 glDeleteSync(texture_formats[free_texture_num].no_reuse_before);
46                 texture_formats.erase(free_texture_num);
47                 glDeleteTextures(1, &free_texture_num);
48                 check_error();
49         }
50         assert(texture_formats.empty());
51         assert(texture_freelist_bytes == 0);
52
53         void *context = get_gl_context_identifier();
54         cleanup_unlinked_fbos(context);
55
56         for (const auto &context_and_fbos : fbo_freelist) {
57                 if (context_and_fbos.first != context) {
58                         // If this does not hold, the client should have called clean_context() earlier.
59                         assert(context_and_fbos.second.empty());
60                         continue;
61                 }
62                 for (FBOFormatIterator fbo_it : context_and_fbos.second) {
63                         glDeleteFramebuffers(1, &fbo_it->second.fbo_num);
64                         check_error();
65                         fbo_formats.erase(fbo_it);
66                 }
67         }
68
69         assert(fbo_formats.empty());
70 }
71
72 void ResourcePool::delete_program(GLuint glsl_program_num)
73 {
74         bool found_program = false;
75         for (map<pair<string, string>, GLuint>::iterator program_it = programs.begin();
76              program_it != programs.end();
77              ++program_it) {
78                 if (program_it->second == glsl_program_num) {
79                         programs.erase(program_it);
80                         found_program = true;
81                         break;
82                 }
83         }
84         for (map<string, GLuint>::iterator program_it = compute_programs.begin();
85              program_it != compute_programs.end();
86              ++program_it) {
87                 if (program_it->second == glsl_program_num) {
88                         compute_programs.erase(program_it);
89                         found_program = true;
90                         break;
91                 }
92         }
93         assert(found_program);
94
95         map<GLuint, stack<GLuint>>::iterator instance_list_it = program_instances.find(glsl_program_num);
96         assert(instance_list_it != program_instances.end());
97
98         while (!instance_list_it->second.empty()) {
99                 GLuint instance_program_num = instance_list_it->second.top();
100                 instance_list_it->second.pop();
101                 glDeleteProgram(instance_program_num);
102                 program_masters.erase(instance_program_num);
103         }
104         program_instances.erase(instance_list_it);
105
106         map<GLuint, ShaderSpec>::iterator shader_it =
107                 program_shaders.find(glsl_program_num);
108         if (shader_it == program_shaders.end()) {
109                 // Should be a compute shader.
110                 map<GLuint, ComputeShaderSpec>::iterator compute_shader_it =
111                         compute_program_shaders.find(glsl_program_num);
112                 assert(compute_shader_it != compute_program_shaders.end());
113
114                 glDeleteShader(compute_shader_it->second.cs_obj);
115                 compute_program_shaders.erase(compute_shader_it);
116         } else {
117                 glDeleteShader(shader_it->second.vs_obj);
118                 glDeleteShader(shader_it->second.fs_obj);
119                 program_shaders.erase(shader_it);
120         }
121 }
122
123 GLuint ResourcePool::compile_glsl_program(const string& vertex_shader,
124                                           const string& fragment_shader,
125                                           const vector<string>& fragment_shader_outputs)
126 {
127         GLuint glsl_program_num;
128         pthread_mutex_lock(&lock);
129
130         // Augment the fragment shader program text with the outputs, so that they become
131         // part of the key. Also potentially useful for debugging.
132         string fragment_shader_processed = fragment_shader;
133         for (unsigned output_index = 0; output_index < fragment_shader_outputs.size(); ++output_index) {
134                 char buf[256];
135                 snprintf(buf, sizeof(buf), "// Bound output: %s\n", fragment_shader_outputs[output_index].c_str());
136                 fragment_shader_processed += buf;
137         }
138
139         const pair<string, string> key(vertex_shader, fragment_shader_processed);
140         if (programs.count(key)) {
141                 // Already in the cache.
142                 glsl_program_num = programs[key];
143                 increment_program_refcount(glsl_program_num);
144         } else {
145                 // Not in the cache. Compile the shaders.
146                 GLuint vs_obj = compile_shader(vertex_shader, GL_VERTEX_SHADER);
147                 check_error();
148                 GLuint fs_obj = compile_shader(fragment_shader_processed, GL_FRAGMENT_SHADER);
149                 check_error();
150                 glsl_program_num = link_program(vs_obj, fs_obj, fragment_shader_outputs);
151
152                 output_debug_shader(fragment_shader_processed, "frag");
153
154                 programs.insert(make_pair(key, glsl_program_num));
155                 add_master_program(glsl_program_num);
156
157                 ShaderSpec spec;
158                 spec.vs_obj = vs_obj;
159                 spec.fs_obj = fs_obj;
160                 spec.fragment_shader_outputs = fragment_shader_outputs;
161                 program_shaders.insert(make_pair(glsl_program_num, spec));
162         }
163         pthread_mutex_unlock(&lock);
164         return glsl_program_num;
165 }
166
167 GLuint ResourcePool::link_program(GLuint vs_obj,
168                                   GLuint fs_obj,
169                                   const vector<string>& fragment_shader_outputs)
170 {
171         GLuint glsl_program_num = glCreateProgram();
172         check_error();
173         glAttachShader(glsl_program_num, vs_obj);
174         check_error();
175         glAttachShader(glsl_program_num, fs_obj);
176         check_error();
177
178         // Bind the outputs, if we have multiple ones.
179         if (fragment_shader_outputs.size() > 1) {
180                 for (unsigned output_index = 0; output_index < fragment_shader_outputs.size(); ++output_index) {
181                         glBindFragDataLocation(glsl_program_num, output_index,
182                                                fragment_shader_outputs[output_index].c_str());
183                 }
184         }
185
186         glLinkProgram(glsl_program_num);
187         check_error();
188
189         GLint success;
190         glGetProgramiv(glsl_program_num, GL_LINK_STATUS, &success);
191         if (success == GL_FALSE) {
192                 GLchar error_log[1024] = {0};
193                 glGetProgramInfoLog(glsl_program_num, 1024, nullptr, error_log);
194                 fprintf(stderr, "Error linking program: %s\n", error_log);
195                 exit(1);
196         }
197
198         return glsl_program_num;
199 }
200
201 void ResourcePool::release_glsl_program(GLuint glsl_program_num)
202 {
203         pthread_mutex_lock(&lock);
204         map<GLuint, int>::iterator refcount_it = program_refcount.find(glsl_program_num);
205         assert(refcount_it != program_refcount.end());
206
207         if (--refcount_it->second == 0) {
208                 program_refcount.erase(refcount_it);
209                 assert(find(program_freelist.begin(), program_freelist.end(), glsl_program_num)
210                         == program_freelist.end());
211                 program_freelist.push_front(glsl_program_num);
212                 if (program_freelist.size() > program_freelist_max_length) {
213                         delete_program(program_freelist.back());
214                         program_freelist.pop_back();
215                 }
216         }
217
218         pthread_mutex_unlock(&lock);
219 }
220
221 GLuint ResourcePool::compile_glsl_compute_program(const string& compute_shader)
222 {
223         GLuint glsl_program_num;
224         pthread_mutex_lock(&lock);
225
226         const string &key = compute_shader;
227         if (compute_programs.count(key)) {
228                 // Already in the cache.
229                 glsl_program_num = compute_programs[key];
230                 increment_program_refcount(glsl_program_num);
231         } else {
232                 // Not in the cache. Compile the shader.
233                 GLuint cs_obj = compile_shader(compute_shader, GL_COMPUTE_SHADER);
234                 check_error();
235                 glsl_program_num = link_compute_program(cs_obj);
236
237                 output_debug_shader(compute_shader, "comp");
238
239                 compute_programs.insert(make_pair(key, glsl_program_num));
240                 add_master_program(glsl_program_num);
241
242                 ComputeShaderSpec spec;
243                 spec.cs_obj = cs_obj;
244                 compute_program_shaders.insert(make_pair(glsl_program_num, spec));
245         }
246         pthread_mutex_unlock(&lock);
247         return glsl_program_num;
248 }
249
250 GLuint ResourcePool::link_compute_program(GLuint cs_obj)
251 {
252         GLuint glsl_program_num = glCreateProgram();
253         check_error();
254         glAttachShader(glsl_program_num, cs_obj);
255         check_error();
256         glLinkProgram(glsl_program_num);
257         check_error();
258
259         GLint success;
260         glGetProgramiv(glsl_program_num, GL_LINK_STATUS, &success);
261         if (success == GL_FALSE) {
262                 GLchar error_log[1024] = {0};
263                 glGetProgramInfoLog(glsl_program_num, 1024, nullptr, error_log);
264                 fprintf(stderr, "Error linking program: %s\n", error_log);
265                 exit(1);
266         }
267
268         return glsl_program_num;
269 }
270
271 GLuint ResourcePool::use_glsl_program(GLuint glsl_program_num)
272 {
273         pthread_mutex_lock(&lock);
274         assert(program_instances.count(glsl_program_num));
275         stack<GLuint> &instances = program_instances[glsl_program_num];
276
277         GLuint instance_program_num;
278         if (!instances.empty()) {
279                 // There's an unused instance of this program; just return it.
280                 instance_program_num = instances.top();
281                 instances.pop();
282         } else {
283                 // We need to clone this program. (unuse_glsl_program()
284                 // will later put it onto the list.)
285                 map<GLuint, ShaderSpec>::iterator shader_it =
286                         program_shaders.find(glsl_program_num);
287                 if (shader_it == program_shaders.end()) {
288                         // Should be a compute shader.
289                         map<GLuint, ComputeShaderSpec>::iterator compute_shader_it =
290                                 compute_program_shaders.find(glsl_program_num);
291                         instance_program_num = link_compute_program(
292                                 compute_shader_it->second.cs_obj);
293                 } else {
294                         // A regular fragment shader.
295                         instance_program_num = link_program(
296                                 shader_it->second.vs_obj,
297                                 shader_it->second.fs_obj,
298                                 shader_it->second.fragment_shader_outputs);
299                 }
300                 program_masters.insert(make_pair(instance_program_num, glsl_program_num));
301         }
302         pthread_mutex_unlock(&lock);
303
304         glUseProgram(instance_program_num);
305         return instance_program_num;
306 }
307
308 void ResourcePool::unuse_glsl_program(GLuint instance_program_num)
309 {
310         pthread_mutex_lock(&lock);
311
312         auto master_it = program_masters.find(instance_program_num);
313         assert(master_it != program_masters.end());
314
315         assert(program_instances.count(master_it->second));
316         stack<GLuint> &instances = program_instances[master_it->second];
317
318         instances.push(instance_program_num);
319
320         pthread_mutex_unlock(&lock);
321 }
322
323 GLuint ResourcePool::create_2d_texture(GLint internal_format, GLsizei width, GLsizei height)
324 {
325         assert(width > 0);
326         assert(height > 0);
327
328         pthread_mutex_lock(&lock);
329         // See if there's a texture on the freelist we can use.
330         for (auto freelist_it = texture_freelist.begin();
331              freelist_it != texture_freelist.end();
332              ++freelist_it) {
333                 GLuint texture_num = *freelist_it;
334                 map<GLuint, Texture2D>::const_iterator format_it = texture_formats.find(texture_num);
335                 assert(format_it != texture_formats.end());
336                 if (format_it->second.internal_format == internal_format &&
337                     format_it->second.width == width &&
338                     format_it->second.height == height) {
339                         texture_freelist_bytes -= estimate_texture_size(format_it->second);
340                         texture_freelist.erase(freelist_it);
341                         GLsync sync = format_it->second.no_reuse_before;
342                         pthread_mutex_unlock(&lock);
343                         glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
344                         glDeleteSync(sync);
345                         return texture_num;
346                 }
347         }
348
349         // Find any reasonable format given the internal format; OpenGL validates it
350         // even though we give nullptr as pointer.
351         GLenum format;
352         switch (internal_format) {
353         case GL_RGBA32F_ARB:
354         case GL_RGBA16F_ARB:
355         case GL_RGBA16:
356         case GL_RGBA8:
357         case GL_RGB10_A2:
358         case GL_SRGB8_ALPHA8:
359                 format = GL_RGBA;
360                 break;
361         case GL_RGB32F:
362         case GL_RGB16F:
363         case GL_RGB16:
364         case GL_R11F_G11F_B10F:
365         case GL_RGB8:
366         case GL_RGB10:
367         case GL_SRGB8:
368         case GL_RGB565:
369         case GL_RGB9_E5:
370                 format = GL_RGB;
371                 break;
372         case GL_RG32F:
373         case GL_RG16F:
374         case GL_RG16:
375         case GL_RG8:
376                 format = GL_RG;
377                 break;
378         case GL_R32F:
379         case GL_R16F:
380         case GL_R16:
381         case GL_R8:
382                 format = GL_RED;
383                 break;
384         default:
385                 // TODO: Add more here as needed.
386                 assert(false);
387         }
388
389         // Same with type; GLES is stricter than desktop OpenGL here.
390         GLenum type;
391         switch (internal_format) {
392         case GL_RGBA32F_ARB:
393         case GL_RGBA16F_ARB:
394         case GL_RGB32F:
395         case GL_RGB16F:
396         case GL_R11F_G11F_B10F:
397         case GL_RGB9_E5:
398         case GL_RG32F:
399         case GL_RG16F:
400         case GL_R32F:
401         case GL_R16F:
402                 type = GL_FLOAT;
403                 break;
404         case GL_RGBA16:
405         case GL_RGB16:
406         case GL_RG16:
407         case GL_R16:
408                 type = GL_UNSIGNED_SHORT;
409                 break;
410         case GL_SRGB8_ALPHA8:
411         case GL_SRGB8:
412         case GL_RGBA8:
413         case GL_RGB8:
414         case GL_RGB10_A2:
415         case GL_RGB10:
416         case GL_RG8:
417         case GL_R8:
418                 type = GL_UNSIGNED_BYTE;
419                 break;
420         case GL_RGB565:
421                 type = GL_UNSIGNED_SHORT_5_6_5;
422                 break;
423         default:
424                 // TODO: Add more here as needed.
425                 assert(false);
426         }
427
428
429         GLuint texture_num;
430         glGenTextures(1, &texture_num);
431         check_error();
432         glBindTexture(GL_TEXTURE_2D, texture_num);
433         check_error();
434         glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, format, type, nullptr);
435         check_error();
436         glBindTexture(GL_TEXTURE_2D, 0);
437         check_error();
438
439         Texture2D texture_format;
440         texture_format.internal_format = internal_format;
441         texture_format.width = width;
442         texture_format.height = height;
443         assert(texture_formats.count(texture_num) == 0);
444         texture_formats.insert(make_pair(texture_num, texture_format));
445
446         pthread_mutex_unlock(&lock);
447         return texture_num;
448 }
449
450 void ResourcePool::release_2d_texture(GLuint texture_num)
451 {
452         pthread_mutex_lock(&lock);
453         texture_freelist.push_front(texture_num);
454         assert(texture_formats.count(texture_num) != 0);
455         texture_freelist_bytes += estimate_texture_size(texture_formats[texture_num]);
456         texture_formats[texture_num].no_reuse_before = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
457
458         while (texture_freelist_bytes > texture_freelist_max_bytes) {
459                 GLuint free_texture_num = texture_freelist.back();
460                 texture_freelist.pop_back();
461                 assert(texture_formats.count(free_texture_num) != 0);
462                 texture_freelist_bytes -= estimate_texture_size(texture_formats[free_texture_num]);
463                 glDeleteSync(texture_formats[free_texture_num].no_reuse_before);
464                 texture_formats.erase(free_texture_num);
465                 glDeleteTextures(1, &free_texture_num);
466                 check_error();
467
468                 // Unlink any lingering FBO related to this texture. We might
469                 // not be in the right context, so don't delete it right away;
470                 // the cleanup in release_fbo() (which calls cleanup_unlinked_fbos())
471                 // will take care of actually doing that later.
472                 for (auto &key_and_fbo : fbo_formats) {
473                         for (unsigned i = 0; i < num_fbo_attachments; ++i) {
474                                 if (key_and_fbo.second.texture_num[i] == free_texture_num) {
475                                         key_and_fbo.second.texture_num[i] = GL_INVALID_INDEX;
476                                 }
477                         }
478                 }
479         }
480         pthread_mutex_unlock(&lock);
481 }
482
483 GLuint ResourcePool::create_fbo(GLuint texture0_num, GLuint texture1_num, GLuint texture2_num, GLuint texture3_num)
484 {
485         void *context = get_gl_context_identifier();
486
487         // Make sure we are filled from the bottom.
488         assert(texture0_num != 0);
489         if (texture1_num == 0) {
490                 assert(texture2_num == 0);
491         }
492         if (texture2_num == 0) {
493                 assert(texture3_num == 0);
494         }
495
496         pthread_mutex_lock(&lock);
497         if (fbo_freelist.count(context) != 0) {
498                 // See if there's an FBO on the freelist we can use.
499                 auto end = fbo_freelist[context].end();
500                 for (auto freelist_it = fbo_freelist[context].begin(); freelist_it != end; ++freelist_it) {
501                         FBOFormatIterator fbo_it = *freelist_it;
502                         if (fbo_it->second.texture_num[0] == texture0_num &&
503                             fbo_it->second.texture_num[1] == texture1_num &&
504                             fbo_it->second.texture_num[2] == texture2_num &&
505                             fbo_it->second.texture_num[3] == texture3_num) {
506                                 fbo_freelist[context].erase(freelist_it);
507                                 pthread_mutex_unlock(&lock);
508                                 return fbo_it->second.fbo_num;
509                         }
510                 }
511         }
512
513         // Create a new one.
514         FBO fbo_format;
515         fbo_format.texture_num[0] = texture0_num;
516         fbo_format.texture_num[1] = texture1_num;
517         fbo_format.texture_num[2] = texture2_num;
518         fbo_format.texture_num[3] = texture3_num;
519
520         glGenFramebuffers(1, &fbo_format.fbo_num);
521         check_error();
522         glBindFramebuffer(GL_FRAMEBUFFER, fbo_format.fbo_num);
523         check_error();
524
525         GLenum bufs[num_fbo_attachments];
526         unsigned num_active_attachments = 0;
527         for (unsigned i = 0; i < num_fbo_attachments; ++i, ++num_active_attachments) {
528                 if (fbo_format.texture_num[i] == 0) {
529                         break;
530                 }
531                 glFramebufferTexture2D(
532                         GL_FRAMEBUFFER,
533                         GL_COLOR_ATTACHMENT0 + i,
534                         GL_TEXTURE_2D,
535                         fbo_format.texture_num[i],
536                         0);
537                 check_error();
538                 bufs[i] = GL_COLOR_ATTACHMENT0 + i;
539         }
540
541         glDrawBuffers(num_active_attachments, bufs);
542         check_error();
543
544         GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
545         assert(status == GL_FRAMEBUFFER_COMPLETE);
546         glBindFramebuffer(GL_FRAMEBUFFER, 0);
547         check_error();
548
549         pair<void *, GLuint> key(context, fbo_format.fbo_num);
550         assert(fbo_formats.count(key) == 0);
551         fbo_formats.insert(make_pair(key, fbo_format));
552
553         pthread_mutex_unlock(&lock);
554         return fbo_format.fbo_num;
555 }
556
557 void ResourcePool::release_fbo(GLuint fbo_num)
558 {
559         void *context = get_gl_context_identifier();
560
561         pthread_mutex_lock(&lock);
562         FBOFormatIterator fbo_it = fbo_formats.find(make_pair(context, fbo_num));
563         assert(fbo_it != fbo_formats.end());
564         fbo_freelist[context].push_front(fbo_it);
565
566         // Now that we're in this context, free up any FBOs that are connected
567         // to deleted textures (in release_2d_texture).
568         cleanup_unlinked_fbos(context);
569
570         shrink_fbo_freelist(context, fbo_freelist_max_length);
571         pthread_mutex_unlock(&lock);
572 }
573
574 GLuint ResourcePool::create_vec2_vao(const set<GLint> &attribute_indices, GLuint vbo_num)
575 {
576         void *context = get_gl_context_identifier();
577
578         pthread_mutex_lock(&lock);
579         if (vao_freelist.count(context) != 0) {
580                 // See if there's a VAO the freelist we can use.
581                 auto end = vao_freelist[context].end();
582                 for (auto freelist_it = vao_freelist[context].begin(); freelist_it != end; ++freelist_it) {
583                         VAOFormatIterator vao_it = *freelist_it;
584                         if (vao_it->second.vbo_num == vbo_num &&
585                             vao_it->second.attribute_indices == attribute_indices) {
586                                 vao_freelist[context].erase(freelist_it);
587                                 pthread_mutex_unlock(&lock);
588                                 return vao_it->second.vao_num;
589                         }
590                 }
591         }
592
593         // Create a new one.
594         VAO vao_format;
595         vao_format.attribute_indices = attribute_indices;
596         vao_format.vbo_num = vbo_num;
597
598         glGenVertexArrays(1, &vao_format.vao_num);
599         check_error();
600         glBindVertexArray(vao_format.vao_num);
601         check_error();
602         glBindBuffer(GL_ARRAY_BUFFER, vbo_num);
603         check_error();
604
605         for (GLint attr : attribute_indices) {
606                 glEnableVertexAttribArray(attr);
607                 check_error();
608                 glVertexAttribPointer(attr, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
609                 check_error();
610         }
611
612         glBindVertexArray(0);
613         check_error();
614         glBindBuffer(GL_ARRAY_BUFFER, 0);
615         check_error();
616
617         pair<void *, GLuint> key(context, vao_format.vao_num);
618         assert(vao_formats.count(key) == 0);
619         vao_formats.insert(make_pair(key, vao_format));
620
621         pthread_mutex_unlock(&lock);
622         return vao_format.vao_num;
623 }
624
625 void ResourcePool::release_vec2_vao(GLuint vao_num)
626 {
627         void *context = get_gl_context_identifier();
628
629         pthread_mutex_lock(&lock);
630         VAOFormatIterator vao_it = vao_formats.find(make_pair(context, vao_num));
631         assert(vao_it != vao_formats.end());
632         vao_freelist[context].push_front(vao_it);
633
634         shrink_vao_freelist(context, vao_freelist_max_length);
635         pthread_mutex_unlock(&lock);
636 }
637
638 void ResourcePool::clean_context()
639 {
640         void *context = get_gl_context_identifier();
641
642         // Currently, we only need to worry about FBOs and VAOs, as they are the only
643         // non-shareable resources we hold.
644         shrink_fbo_freelist(context, 0);
645         fbo_freelist.erase(context);
646
647         shrink_vao_freelist(context, 0);
648         vao_freelist.erase(context);
649 }
650
651 void ResourcePool::cleanup_unlinked_fbos(void *context)
652 {
653         auto end = fbo_freelist[context].end();
654         for (auto freelist_it = fbo_freelist[context].begin(); freelist_it != end; ) {
655                 FBOFormatIterator fbo_it = *freelist_it;
656
657                 bool all_unlinked = true;
658                 for (unsigned i = 0; i < num_fbo_attachments; ++i) {
659                         if (fbo_it->second.texture_num[i] != 0 &&
660                             fbo_it->second.texture_num[i] != GL_INVALID_INDEX) {
661                                 all_unlinked = false;
662                                 break;
663                         }
664                 }
665                 if (all_unlinked) {
666                         glDeleteFramebuffers(1, &fbo_it->second.fbo_num);
667                         check_error();
668                         fbo_formats.erase(fbo_it);
669                         fbo_freelist[context].erase(freelist_it++);
670                 } else {
671                         freelist_it++;
672                 }
673         }
674 }
675
676 void ResourcePool::shrink_fbo_freelist(void *context, size_t max_length)
677 {
678         list<FBOFormatIterator> &freelist = fbo_freelist[context];
679         while (freelist.size() > max_length) {
680                 FBOFormatIterator free_fbo_it = freelist.back();
681                 glDeleteFramebuffers(1, &free_fbo_it->second.fbo_num);
682                 check_error();
683                 fbo_formats.erase(free_fbo_it);
684                 freelist.pop_back();
685         }
686 }
687
688 void ResourcePool::shrink_vao_freelist(void *context, size_t max_length)
689 {
690         list<VAOFormatIterator> &freelist = vao_freelist[context];
691         while (freelist.size() > max_length) {
692                 VAOFormatIterator free_vao_it = freelist.back();
693                 glDeleteVertexArrays(1, &free_vao_it->second.vao_num);
694                 check_error();
695                 vao_formats.erase(free_vao_it);
696                 freelist.pop_back();
697         }
698 }
699
700 void ResourcePool::increment_program_refcount(GLuint program_num)
701 {
702         map<GLuint, int>::iterator refcount_it = program_refcount.find(program_num);
703         if (refcount_it != program_refcount.end()) {
704                 ++refcount_it->second;
705         } else {
706                 list<GLuint>::iterator freelist_it =
707                         find(program_freelist.begin(), program_freelist.end(), program_num);
708                 assert(freelist_it != program_freelist.end());
709                 program_freelist.erase(freelist_it);
710                 program_refcount.insert(make_pair(program_num, 1));
711         }
712 }
713
714 void ResourcePool::output_debug_shader(const string &shader_src, const string &suffix)
715 {
716         if (movit_debug_level == MOVIT_DEBUG_ON) {
717                 // Output shader to a temporary file, for easier debugging.
718                 static int compiled_shader_num = 0;
719                 char filename[256];
720                 sprintf(filename, "chain-%03d.%s", compiled_shader_num++, suffix.c_str());
721                 FILE *fp = fopen(filename, "w");
722                 if (fp == nullptr) {
723                         perror(filename);
724                         exit(1);
725                 }
726                 fprintf(fp, "%s\n", shader_src.c_str());
727                 fclose(fp);
728         }
729 }
730
731 void ResourcePool::add_master_program(GLuint program_num)
732 {
733         program_refcount.insert(make_pair(program_num, 1));
734         stack<GLuint> instances;
735         instances.push(program_num);
736         program_instances.insert(make_pair(program_num, instances));
737         program_masters.insert(make_pair(program_num, program_num));
738 }
739
740 size_t ResourcePool::estimate_texture_size(const Texture2D &texture_format)
741 {
742         size_t bytes_per_pixel;
743
744         switch (texture_format.internal_format) {
745         case GL_RGBA32F_ARB:
746                 bytes_per_pixel = 16;
747                 break;
748         case GL_RGBA16F_ARB:
749                 bytes_per_pixel = 8;
750                 break;
751         case GL_RGB32F_ARB:
752                 bytes_per_pixel = 12;
753                 break;
754         case GL_RGB16F_ARB:
755                 bytes_per_pixel = 6;
756                 break;
757         case GL_R11F_G11F_B10F:
758                 bytes_per_pixel = 4;
759                 break;
760         case GL_RGB9_E5:
761                 bytes_per_pixel = 4;
762                 break;
763         case GL_RGBA8:
764         case GL_SRGB8_ALPHA8:
765         case GL_RGB10_A2:
766         case GL_RGB10:
767                 bytes_per_pixel = 4;
768                 break;
769         case GL_RGB8:
770         case GL_SRGB8:
771                 bytes_per_pixel = 3;
772                 break;
773         case GL_RG32F:
774                 bytes_per_pixel = 8;
775                 break;
776         case GL_RG16F:
777                 bytes_per_pixel = 4;
778                 break;
779         case GL_R32F:
780                 bytes_per_pixel = 4;
781                 break;
782         case GL_R16F:
783                 bytes_per_pixel = 2;
784                 break;
785         case GL_RG8:
786                 bytes_per_pixel = 2;
787                 break;
788         case GL_R8:
789                 bytes_per_pixel = 1;
790                 break;
791         case GL_RGB565:
792                 bytes_per_pixel = 2;
793                 break;
794         case GL_RGBA16:
795                 bytes_per_pixel = 8;
796                 break;
797         case GL_RGB16:
798                 bytes_per_pixel = 6;
799                 break;
800         case GL_RG16:
801                 bytes_per_pixel = 4;
802                 break;
803         case GL_R16:
804                 bytes_per_pixel = 2;
805                 break;
806         default:
807                 // TODO: Add more here as needed.
808                 assert(false);
809         }
810
811         return texture_format.width * texture_format.height * bytes_per_pixel;
812 }
813
814 }  // namespace movit