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