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