]> git.sesse.net Git - movit/blobdiff - effect_chain.cpp
Add a shared ResourcePool to share resources between EffectChains.
[movit] / effect_chain.cpp
index 0f8876d8f70c68ccb6b4d2a41a538414eddf9efe..e6ba841ad6bd4737bc3a506384963233311c0678 100644 (file)
@@ -1,35 +1,45 @@
 #define GL_GLEXT_PROTOTYPES 1
 
-#include <stdio.h>
+#include <GL/glew.h>
+#include <assert.h>
+#include <locale.h>
 #include <math.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
-#include <locale.h>
-#include <assert.h>
-#include <GL/glew.h>
-
 #include <algorithm>
 #include <set>
 #include <stack>
 #include <vector>
 
-#include "util.h"
-#include "effect_chain.h"
-#include "gamma_expansion_effect.h"
-#include "gamma_compression_effect.h"
-#include "colorspace_conversion_effect.h"
-#include "alpha_multiplication_effect.h"
 #include "alpha_division_effect.h"
+#include "alpha_multiplication_effect.h"
+#include "colorspace_conversion_effect.h"
 #include "dither_effect.h"
-#include "input.h"
+#include "effect.h"
+#include "effect_chain.h"
+#include "gamma_compression_effect.h"
+#include "gamma_expansion_effect.h"
 #include "init.h"
+#include "input.h"
+#include "resource_pool.h"
+#include "util.h"
 
-EffectChain::EffectChain(float aspect_nom, float aspect_denom)
+EffectChain::EffectChain(float aspect_nom, float aspect_denom, ResourcePool *resource_pool)
        : aspect_nom(aspect_nom),
          aspect_denom(aspect_denom),
          dither_effect(NULL),
-         fbo(0),
          num_dither_bits(0),
-         finalized(false) {}
+         finalized(false),
+         resource_pool(resource_pool) {
+       if (resource_pool == NULL) {
+               this->resource_pool = new ResourcePool();
+               owns_resource_pool = true;
+       } else {
+               owns_resource_pool = false;
+       }
+}
 
 EffectChain::~EffectChain()
 {
@@ -41,18 +51,17 @@ EffectChain::~EffectChain()
                delete nodes[i];
        }
        for (unsigned i = 0; i < phases.size(); ++i) {
-               glDeleteProgram(phases[i]->glsl_program_num);
-               glDeleteShader(phases[i]->vertex_shader);
-               glDeleteShader(phases[i]->fragment_shader);
+               resource_pool->release_glsl_program(phases[i]->glsl_program_num);
                delete phases[i];
        }
-       if (fbo != 0) {
-               glDeleteFramebuffers(1, &fbo);
+       if (owns_resource_pool) {
+               delete resource_pool;
        }
 }
 
 Input *EffectChain::add_input(Input *input)
 {
+       assert(!finalized);
        inputs.push_back(input);
        add_node(input);
        return input;
@@ -60,12 +69,17 @@ Input *EffectChain::add_input(Input *input)
 
 void EffectChain::add_output(const ImageFormat &format, OutputAlphaFormat alpha_format)
 {
+       assert(!finalized);
        output_format = format;
        output_alpha_format = alpha_format;
 }
 
 Node *EffectChain::add_node(Effect *effect)
 {
+       for (unsigned i = 0; i < nodes.size(); ++i) {
+               assert(nodes[i]->effect != effect);
+       }
+
        char effect_id[256];
        sprintf(effect_id, "eff%u", (unsigned)nodes.size());
 
@@ -155,6 +169,7 @@ void EffectChain::find_all_nonlinear_inputs(Node *node, std::vector<Node *> *non
 
 Effect *EffectChain::add_effect(Effect *effect, const std::vector<Effect *> &inputs)
 {
+       assert(!finalized);
        assert(inputs.size() == effect->num_inputs());
        Node *node = add_node(effect);
        for (unsigned i = 0; i < inputs.size(); ++i) {
@@ -273,34 +288,8 @@ Phase *EffectChain::compile_glsl_program(
        frag_shader += std::string("#define INPUT ") + sorted_effects.back()->effect_id + "\n";
        frag_shader.append(read_file("footer.frag"));
 
-       if (movit_debug_level == MOVIT_DEBUG_ON) {
-               // Output shader to a temporary file, for easier debugging.
-               static int compiled_shader_num = 0;
-               char filename[256];
-               sprintf(filename, "chain-%03d.frag", compiled_shader_num++);
-               FILE *fp = fopen(filename, "w");
-               if (fp == NULL) {
-                       perror(filename);
-                       exit(1);
-               }
-               fprintf(fp, "%s\n", frag_shader.c_str());
-               fclose(fp);
-       }
-       
-       GLuint glsl_program_num = glCreateProgram();
-       GLuint vs_obj = compile_shader(read_file("vs.vert"), GL_VERTEX_SHADER);
-       GLuint fs_obj = compile_shader(frag_shader, GL_FRAGMENT_SHADER);
-       glAttachShader(glsl_program_num, vs_obj);
-       check_error();
-       glAttachShader(glsl_program_num, fs_obj);
-       check_error();
-       glLinkProgram(glsl_program_num);
-       check_error();
-
        Phase *phase = new Phase;
-       phase->glsl_program_num = glsl_program_num;
-       phase->vertex_shader = vs_obj;
-       phase->fragment_shader = fs_obj;
+       phase->glsl_program_num = resource_pool->compile_glsl_program(read_file("vs.vert"), frag_shader);
        phase->input_needs_mipmaps = input_needs_mipmaps;
        phase->inputs = true_inputs;
        phase->effects = sorted_effects;
@@ -1428,8 +1417,6 @@ void EffectChain::finalize()
        // since otherwise this turns into an (albeit simple)
        // register allocation problem.
        if (phases.size() > 1) {
-               glGenFramebuffers(1, &fbo);
-
                for (unsigned i = 0; i < phases.size() - 1; ++i) {
                        inform_input_sizes(phases[i]);
                        find_output_size(phases[i]);
@@ -1468,6 +1455,7 @@ void EffectChain::render_to_fbo(GLuint dest_fbo, unsigned width, unsigned height
 
        // Save original viewport.
        GLuint x = 0, y = 0;
+       GLuint fbo = 0;
 
        if (width == 0 && height == 0) {
                GLint viewport[4];
@@ -1494,6 +1482,8 @@ void EffectChain::render_to_fbo(GLuint dest_fbo, unsigned width, unsigned height
        glLoadIdentity();
 
        if (phases.size() > 1) {
+               glGenFramebuffers(1, &fbo);
+               check_error();
                glBindFramebuffer(GL_FRAMEBUFFER, fbo);
                check_error();
        }
@@ -1557,6 +1547,8 @@ void EffectChain::render_to_fbo(GLuint dest_fbo, unsigned width, unsigned height
                        // Last phase goes to the output the user specified.
                        glBindFramebuffer(GL_FRAMEBUFFER, dest_fbo);
                        check_error();
+                       GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
+                       assert(status == GL_FRAMEBUFFER_COMPLETE);
                        glViewport(x, y, width, height);
                        if (dither_effect != NULL) {
                                CHECK(dither_effect->set_int("output_width", width));
@@ -1571,6 +1563,8 @@ void EffectChain::render_to_fbo(GLuint dest_fbo, unsigned width, unsigned height
                                output_node->output_texture,
                                0);
                        check_error();
+                       GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
+                       assert(status == GL_FRAMEBUFFER_COMPLETE);
                        glViewport(0, 0, phases[phase]->output_width, phases[phase]->output_height);
                }
 
@@ -1605,4 +1599,12 @@ void EffectChain::render_to_fbo(GLuint dest_fbo, unsigned width, unsigned height
                        node->effect->clear_gl_state();
                }
        }
+
+       glBindFramebuffer(GL_FRAMEBUFFER, 0);
+       check_error();
+
+       if (fbo != 0) {
+               glDeleteFramebuffers(1, &fbo);
+               check_error();
+       }
 }