X-Git-Url: https://git.sesse.net/?p=movit;a=blobdiff_plain;f=effect_chain.cpp;h=f11a5e45a3ada2ccbbaf38a5b76630ceb2f41915;hp=6d6886cc0d881b95a4ac9972e2b4629d406a385c;hb=eff011224abc5dc81f801f3ea44572287a55bcac;hpb=10bcc7948c3911f1e4459c98205726334998229e diff --git a/effect_chain.cpp b/effect_chain.cpp index 6d6886c..f11a5e4 100644 --- a/effect_chain.cpp +++ b/effect_chain.cpp @@ -432,6 +432,7 @@ void EffectChain::compile_glsl_program(Phase *phase) // strong one-to-one). frag_shader += "(tc) CS_OUTPUT_VAL\n"; } else { + assert(phase->effect_ids.count(make_pair(input, link_type))); frag_shader += string(" ") + phase->effect_ids[make_pair(input, link_type)] + "\n"; } } @@ -455,6 +456,7 @@ void EffectChain::compile_glsl_program(Phase *phase) frag_shader += "\n"; } if (phase->is_compute_shader) { + assert(phase->effect_ids.count(make_pair(phase->compute_shader_node, IN_SAME_PHASE))); frag_shader += string("#define INPUT ") + phase->effect_ids[make_pair(phase->compute_shader_node, IN_SAME_PHASE)] + "\n"; if (phase->compute_shader_node == phase->effects.back()) { // No postprocessing. @@ -463,6 +465,7 @@ void EffectChain::compile_glsl_program(Phase *phase) frag_shader += string("#define CS_POSTPROC ") + phase->effect_ids[make_pair(phase->effects.back(), IN_SAME_PHASE)] + "\n"; } } else { + assert(phase->effect_ids.count(make_pair(phase->effects.back(), IN_SAME_PHASE))); frag_shader += string("#define INPUT ") + phase->effect_ids[make_pair(phase->effects.back(), IN_SAME_PHASE)] + "\n"; } @@ -690,14 +693,15 @@ Phase *EffectChain::construct_phase(Node *output, map *complete } // Find all the dependencies of this effect, and add them to the stack. - vector deps = node->incoming_links; - assert(node->effect->num_inputs() == deps.size()); - for (unsigned i = 0; i < deps.size(); ++i) { + assert(node->effect->num_inputs() == node->incoming_links.size()); + for (Node *dep : node->incoming_links) { bool start_new_phase = false; + Effect::MipmapRequirements save_needs_mipmaps = dep->needs_mipmaps; + if (node->effect->needs_texture_bounce() && - !deps[i]->effect->is_single_texture() && - !deps[i]->effect->override_disable_bounce()) { + !dep->effect->is_single_texture() && + !dep->effect->override_disable_bounce()) { start_new_phase = true; } @@ -712,17 +716,17 @@ Phase *EffectChain::construct_phase(Node *output, map *complete // if we have diamonds in the graph; if so, choose that. // If not, the effect on the node can also decide (this is the // more common case). - Effect::MipmapRequirements dep_mipmaps = deps[i]->needs_mipmaps; + Effect::MipmapRequirements dep_mipmaps = dep->needs_mipmaps; if (dep_mipmaps == Effect::DOES_NOT_NEED_MIPMAPS) { - if (deps[i]->effect->num_inputs() == 0) { - Input *input = static_cast(deps[i]->effect); + if (dep->effect->num_inputs() == 0) { + Input *input = static_cast(dep->effect); dep_mipmaps = input->can_supply_mipmaps() ? Effect::DOES_NOT_NEED_MIPMAPS : Effect::CANNOT_ACCEPT_MIPMAPS; } else { - dep_mipmaps = deps[i]->effect->needs_mipmaps(); + dep_mipmaps = dep->effect->needs_mipmaps(); } } if (dep_mipmaps == Effect::DOES_NOT_NEED_MIPMAPS) { - deps[i]->needs_mipmaps = node->needs_mipmaps; + dep->needs_mipmaps = node->needs_mipmaps; } else if (dep_mipmaps != node->needs_mipmaps) { // The dependency cannot supply our mipmap demands // (either because it's an input that can't do mipmaps, @@ -733,8 +737,8 @@ Phase *EffectChain::construct_phase(Node *output, map *complete } } - if (deps[i]->outgoing_links.size() > 1) { - if (!deps[i]->effect->is_single_texture()) { + if (dep->outgoing_links.size() > 1) { + if (!dep->effect->is_single_texture()) { // More than one effect uses this as the input, // and it is not a texture itself. // The easiest thing to do (and probably also the safest @@ -742,7 +746,7 @@ Phase *EffectChain::construct_phase(Node *output, map *complete // and then let the next passes read from that. start_new_phase = true; } else { - assert(deps[i]->effect->num_inputs() == 0); + assert(dep->effect->num_inputs() == 0); // For textures, we try to be slightly more clever; // if none of our outputs need a bounce, we don't bounce @@ -751,46 +755,51 @@ Phase *EffectChain::construct_phase(Node *output, map *complete // Strictly speaking, we could bounce it for some outputs // and use it directly for others, but the processing becomes // somewhat simpler if the effect is only used in one such way. - for (unsigned j = 0; j < deps[i]->outgoing_links.size(); ++j) { - Node *rdep = deps[i]->outgoing_links[j]; + for (unsigned j = 0; j < dep->outgoing_links.size(); ++j) { + Node *rdep = dep->outgoing_links[j]; start_new_phase |= rdep->effect->needs_texture_bounce(); } } } - if (deps[i]->effect->is_compute_shader()) { + if (dep->effect->is_compute_shader()) { if (phase->is_compute_shader) { // Only one compute shader per phase. start_new_phase = true; } else if (!node->strong_one_to_one_sampling) { // If all nodes so far are strong one-to-one, we can put them after // the compute shader (ie., process them on the output). - start_new_phase = !node->strong_one_to_one_sampling; - } else { + start_new_phase = true; + } else if (!start_new_phase) { phase->is_compute_shader = true; - phase->compute_shader_node = deps[i]; + phase->compute_shader_node = dep; } - } else if (deps[i]->effect->sets_virtual_output_size()) { - assert(deps[i]->effect->changes_output_size()); + } else if (dep->effect->sets_virtual_output_size()) { + assert(dep->effect->changes_output_size()); // If the next effect sets a virtual size to rely on OpenGL's // bilinear sampling, we'll really need to break the phase here. start_new_phase = true; - } else if (deps[i]->effect->changes_output_size() && !node->one_to_one_sampling) { + } else if (dep->effect->changes_output_size() && !node->one_to_one_sampling) { // If the next effect changes size and we don't have one-to-one sampling, // we also need to break here. start_new_phase = true; } if (start_new_phase) { - phase->inputs.push_back(construct_phase(deps[i], completed_effects)); + // Since we're starting a new phase here, we don't need to impose any + // new demands on this effect. Restore the status we had before we + // started looking at it. + dep->needs_mipmaps = save_needs_mipmaps; + + phase->inputs.push_back(construct_phase(dep, completed_effects)); } else { - effects_todo_this_phase.push(deps[i]); + effects_todo_this_phase.push(dep); // Propagate the one-to-one status down through the dependency. - deps[i]->one_to_one_sampling = node->one_to_one_sampling && - deps[i]->effect->one_to_one_sampling(); - deps[i]->strong_one_to_one_sampling = node->strong_one_to_one_sampling && - deps[i]->effect->strong_one_to_one_sampling(); + dep->one_to_one_sampling = node->one_to_one_sampling && + dep->effect->one_to_one_sampling(); + dep->strong_one_to_one_sampling = node->strong_one_to_one_sampling && + dep->effect->strong_one_to_one_sampling(); } node->incoming_link_type.push_back(start_new_phase ? IN_ANOTHER_PHASE : IN_SAME_PHASE); @@ -1916,6 +1925,22 @@ void EffectChain::finalize() output_dot("step21-split-to-phases.dot"); + // There are some corner cases where we thought we needed to add a dummy + // effect, but then it turned out later we didn't (e.g. induces_compute_shader() + // didn't see a mipmap conflict coming, which would cause the compute shader + // to be split off from the inal phase); if so, remove the extra phase + // at the end, since it will give us some trouble during execution. + // + // TODO: Remove induces_compute_shader() and replace it with precise tracking. + if (has_dummy_effect && !phases[phases.size() - 2]->is_compute_shader) { + resource_pool->release_glsl_program(phases.back()->glsl_program_num); + delete phases.back(); + phases.pop_back(); + has_dummy_effect = false; + } + + output_dot("step22-dummy-phase-removal.dot"); + assert(phases[0]->inputs.empty()); finalized = true; @@ -2006,6 +2031,7 @@ void EffectChain::render(GLuint dest_fbo, const vector &dest assert(y == 0); assert(num_phases >= 2); assert(!phases.back()->is_compute_shader); + assert(phases[phases.size() - 2]->is_compute_shader); assert(phases.back()->effects.size() == 1); assert(phases.back()->effects[0]->effect->effect_type_id() == "ComputeShaderOutputDisplayEffect"); @@ -2029,9 +2055,10 @@ void EffectChain::render(GLuint dest_fbo, const vector &dest phase->timer_query_objects_running.push_back(timer_query_object); } bool last_phase = (phase_num == num_phases - 1); - if (phase_num == num_phases - 1) { + if (last_phase) { // Last phase goes to the output the user specified. if (!phase->is_compute_shader) { + assert(dest_fbo != (GLuint)-1); glBindFramebuffer(GL_FRAMEBUFFER, dest_fbo); check_error(); GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);