X-Git-Url: https://git.sesse.net/?p=movit;a=blobdiff_plain;f=effect_chain.cpp;h=cb807720b09de0620a09cac6a75fdfc95f507d6b;hp=e68895d7b8f40c8c30475b8f51e0e49c7f39a473;hb=1514db1d0f3963dbc18177d2fd7eaae16a069c3b;hpb=599841e1a667c8009dd6f1b3001f2c9f341a361f diff --git a/effect_chain.cpp b/effect_chain.cpp index e68895d..cb80772 100644 --- a/effect_chain.cpp +++ b/effect_chain.cpp @@ -23,33 +23,36 @@ #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() { for (unsigned i = 0; i < nodes.size(); ++i) { - if (nodes[i]->output_texture != 0) { - glDeleteTextures(1, &nodes[i]->output_texture); - } delete nodes[i]->effect; 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; } } @@ -70,20 +73,20 @@ void EffectChain::add_output(const ImageFormat &format, OutputAlphaFormat alpha_ Node *EffectChain::add_node(Effect *effect) { - char effect_id[256]; - sprintf(effect_id, "eff%u", (unsigned)nodes.size()); + for (unsigned i = 0; i < nodes.size(); ++i) { + assert(nodes[i]->effect != effect); + } Node *node = new Node; node->effect = effect; node->disabled = false; - node->effect_id = effect_id; node->output_color_space = COLORSPACE_INVALID; node->output_gamma_curve = GAMMA_INVALID; node->output_alpha_type = ALPHA_INVALID; - node->output_texture = 0; nodes.push_back(node); node_map[effect] = node; + effect->inform_added(this); return node; } @@ -214,6 +217,7 @@ Phase *EffectChain::compile_glsl_program( const std::vector &inputs, const std::vector &effects) { + Phase *phase = new Phase; assert(!effects.empty()); // Deduplicate the inputs. @@ -227,10 +231,13 @@ Phase *EffectChain::compile_glsl_program( // Create functions for all the texture inputs that we need. for (unsigned i = 0; i < true_inputs.size(); ++i) { Node *input = true_inputs[i]; + char effect_id[256]; + sprintf(effect_id, "in%u", i); + phase->effect_ids.insert(std::make_pair(input, effect_id)); - frag_shader += std::string("uniform sampler2D tex_") + input->effect_id + ";\n"; - frag_shader += std::string("vec4 ") + input->effect_id + "(vec2 tc) {\n"; - frag_shader += "\treturn texture2D(tex_" + input->effect_id + ", tc);\n"; + frag_shader += std::string("uniform sampler2D tex_") + effect_id + ";\n"; + frag_shader += std::string("vec4 ") + effect_id + "(vec2 tc) {\n"; + frag_shader += "\treturn texture2D(tex_" + std::string(effect_id) + ", tc);\n"; frag_shader += "}\n"; frag_shader += "\n"; } @@ -239,21 +246,24 @@ Phase *EffectChain::compile_glsl_program( for (unsigned i = 0; i < sorted_effects.size(); ++i) { Node *node = sorted_effects[i]; + char effect_id[256]; + sprintf(effect_id, "eff%u", i); + phase->effect_ids.insert(std::make_pair(node, effect_id)); if (node->incoming_links.size() == 1) { - frag_shader += std::string("#define INPUT ") + node->incoming_links[0]->effect_id + "\n"; + frag_shader += std::string("#define INPUT ") + phase->effect_ids[node->incoming_links[0]] + "\n"; } else { for (unsigned j = 0; j < node->incoming_links.size(); ++j) { char buf[256]; - sprintf(buf, "#define INPUT%d %s\n", j + 1, node->incoming_links[j]->effect_id.c_str()); + sprintf(buf, "#define INPUT%d %s\n", j + 1, phase->effect_ids[node->incoming_links[j]].c_str()); frag_shader += buf; } } frag_shader += "\n"; - frag_shader += std::string("#define FUNCNAME ") + node->effect_id + "\n"; - frag_shader += replace_prefix(node->effect->output_convenience_uniforms(), node->effect_id); - frag_shader += replace_prefix(node->effect->output_fragment_shader(), node->effect_id); + frag_shader += std::string("#define FUNCNAME ") + effect_id + "\n"; + frag_shader += replace_prefix(node->effect->output_convenience_uniforms(), effect_id); + frag_shader += replace_prefix(node->effect->output_fragment_shader(), effect_id); frag_shader += "#undef PREFIX\n"; frag_shader += "#undef FUNCNAME\n"; if (node->incoming_links.size() == 1) { @@ -275,37 +285,10 @@ Phase *EffectChain::compile_glsl_program( CHECK(node->effect->set_int("needs_mipmaps", input_needs_mipmaps)); } } - frag_shader += std::string("#define INPUT ") + sorted_effects.back()->effect_id + "\n"; + frag_shader += std::string("#define INPUT ") + phase->effect_ids[sorted_effects.back()] + "\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; @@ -1427,36 +1410,6 @@ void EffectChain::finalize() output_dot("step19-split-to-phases.dot"); - // If we have more than one phase, we need intermediate render-to-texture. - // Construct an FBO, and then as many textures as we need. - // We choose the simplest option of having one texture per output, - // 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]); - - Node *output_node = phases[i]->effects.back(); - glGenTextures(1, &output_node->output_texture); - check_error(); - glBindTexture(GL_TEXTURE_2D, output_node->output_texture); - check_error(); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - check_error(); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - check_error(); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, phases[i]->output_width, phases[i]->output_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - check_error(); - - output_node->output_texture_width = phases[i]->output_width; - output_node->output_texture_height = phases[i]->output_height; - } - inform_input_sizes(phases.back()); - } - for (unsigned i = 0; i < inputs.size(); ++i) { inputs[i]->finalize(); } @@ -1473,6 +1426,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]; @@ -1499,35 +1453,26 @@ 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(); } std::set generated_mipmaps; + // We choose the simplest option of having one texture per output, + // since otherwise this turns into an (albeit simple) register allocation problem. + std::map output_textures; + for (unsigned phase = 0; phase < phases.size(); ++phase) { - // See if the requested output size has changed. If so, we need to recreate - // the texture (and before we start setting up inputs). + // Find a texture for this phase. inform_input_sizes(phases[phase]); if (phase != phases.size() - 1) { find_output_size(phases[phase]); - Node *output_node = phases[phase]->effects.back(); - - if (output_node->output_texture_width != phases[phase]->output_width || - output_node->output_texture_height != phases[phase]->output_height) { - glActiveTexture(GL_TEXTURE0); - check_error(); - glBindTexture(GL_TEXTURE_2D, output_node->output_texture); - check_error(); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, phases[phase]->output_width, phases[phase]->output_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - check_error(); - glBindTexture(GL_TEXTURE_2D, 0); - check_error(); - - output_node->output_texture_width = phases[phase]->output_width; - output_node->output_texture_height = phases[phase]->output_height; - } + GLuint tex_num = resource_pool->create_2d_texture(GL_RGBA16F_ARB, phases[phase]->output_width, phases[phase]->output_height); + output_textures.insert(std::make_pair(phases[phase], tex_num)); } glUseProgram(phases[phase]->glsl_program_num); @@ -1537,7 +1482,7 @@ void EffectChain::render_to_fbo(GLuint dest_fbo, unsigned width, unsigned height for (unsigned sampler = 0; sampler < phases[phase]->inputs.size(); ++sampler) { glActiveTexture(GL_TEXTURE0 + sampler); Node *input = phases[phase]->inputs[sampler]; - glBindTexture(GL_TEXTURE_2D, input->output_texture); + glBindTexture(GL_TEXTURE_2D, output_textures[input->phase]); check_error(); if (phases[phase]->input_needs_mipmaps) { if (generated_mipmaps.count(input) == 0) { @@ -1551,8 +1496,12 @@ void EffectChain::render_to_fbo(GLuint dest_fbo, unsigned width, unsigned height glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); check_error(); } + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + check_error(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + check_error(); - std::string texture_name = std::string("tex_") + input->effect_id; + std::string texture_name = std::string("tex_") + phases[phase]->effect_ids[input]; glUniform1i(glGetUniformLocation(phases[phase]->glsl_program_num, texture_name.c_str()), sampler); check_error(); } @@ -1570,12 +1519,11 @@ void EffectChain::render_to_fbo(GLuint dest_fbo, unsigned width, unsigned height CHECK(dither_effect->set_int("output_height", height)); } } else { - Node *output_node = phases[phase]->effects.back(); glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - output_node->output_texture, + output_textures[phases[phase]], 0); check_error(); GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); @@ -1587,7 +1535,7 @@ void EffectChain::render_to_fbo(GLuint dest_fbo, unsigned width, unsigned height unsigned sampler_num = phases[phase]->inputs.size(); for (unsigned i = 0; i < phases[phase]->effects.size(); ++i) { Node *node = phases[phase]->effects[i]; - node->effect->set_gl_state(phases[phase]->glsl_program_num, node->effect_id, &sampler_num); + node->effect->set_gl_state(phases[phase]->glsl_program_num, phases[phase]->effect_ids[node], &sampler_num); check_error(); } @@ -1614,4 +1562,18 @@ void EffectChain::render_to_fbo(GLuint dest_fbo, unsigned width, unsigned height node->effect->clear_gl_state(); } } + + for (std::map::const_iterator texture_it = output_textures.begin(); + texture_it != output_textures.end(); + ++texture_it) { + resource_pool->release_2d_texture(texture_it->second); + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + check_error(); + + if (fbo != 0) { + glDeleteFramebuffers(1, &fbo); + check_error(); + } }