+// Construct GLSL programs, starting at the given effect and following
+// the chain from there. We end a program every time we come to an effect
+// marked as "needs texture bounce", one that is used by multiple other
+// effects, and of course at the end.
+void EffectChain::construct_glsl_programs(Effect *start, std::set<Effect *> *completed_effects)
+{
+ assert(start != NULL);
+ if (completed_effects->count(start) != 0) {
+ // This has already been done for us.
+ return;
+ }
+
+ std::vector<Effect *> this_phase_inputs; // Also includes all intermediates; these will be filtered away later.
+ std::vector<Effect *> this_phase_effects;
+ Effect *node = start;
+ for ( ;; ) { // Termination condition within loop.
+ assert(node != NULL);
+
+ // Check that we have all the inputs we need for this effect.
+ // If not, we end the phase here right away; the other side
+ // of the input chain will eventually come and pick the effect up.
+ assert(incoming_links.count(node) == 1);
+ std::vector<Effect *> deps = incoming_links[node];
+ assert(node->num_inputs() == deps.size());
+ if (!deps.empty()) {
+ bool have_all_deps = true;
+ for (unsigned i = 0; i < deps.size(); ++i) {
+ if (completed_effects->count(deps[i]) == 0) {
+ have_all_deps = false;
+ break;
+ }
+ }
+
+ if (!have_all_deps) {
+ if (!this_phase_effects.empty()) {
+ phases.push_back(compile_glsl_program(this_phase_inputs, this_phase_effects));
+ }
+ return;
+ }
+ this_phase_inputs.insert(this_phase_inputs.end(), deps.begin(), deps.end());
+ }
+ this_phase_effects.push_back(node);
+ completed_effects->insert(node);
+
+ // Find all the effects that use this one as a direct input.
+ if (outgoing_links.count(node) == 0) {
+ // End of the line; output.
+ phases.push_back(compile_glsl_program(this_phase_inputs, this_phase_effects));
+ return;
+ }
+
+ std::vector<Effect *> next = outgoing_links[node];
+ assert(!next.empty());
+ if (next.size() > 1) {
+ if (node->num_inputs() != 0) {
+ // 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
+ // performance-wise in most cases) is to bounce it to a texture
+ // and then let the next passes read from that.
+ phases.push_back(compile_glsl_program(this_phase_inputs, this_phase_effects));
+ }
+
+ // Start phases for all the effects that need us (in arbitrary order).
+ for (unsigned i = 0; i < next.size(); ++i) {
+ construct_glsl_programs(next[i], completed_effects);
+ }
+ return;
+ }
+
+ // OK, only one effect uses this as the input. Keep iterating,
+ // but first see if it requires a texture bounce; if so, give it
+ // one by starting a new phase.
+ node = next[0];
+ if (node->needs_texture_bounce()) {
+ phases.push_back(compile_glsl_program(this_phase_inputs, this_phase_effects));
+ this_phase_inputs.clear();
+ this_phase_effects.clear();
+ }
+ }
+}
+