1 #define GL_GLEXT_PROTOTYPES 1
16 #include "alpha_division_effect.h"
17 #include "alpha_multiplication_effect.h"
18 #include "colorspace_conversion_effect.h"
19 #include "dither_effect.h"
21 #include "effect_chain.h"
22 #include "gamma_compression_effect.h"
23 #include "gamma_expansion_effect.h"
26 #include "resource_pool.h"
29 EffectChain::EffectChain(float aspect_nom, float aspect_denom, ResourcePool *resource_pool)
30 : aspect_nom(aspect_nom),
31 aspect_denom(aspect_denom),
35 resource_pool(resource_pool) {
36 if (resource_pool == NULL) {
37 this->resource_pool = new ResourcePool();
38 owns_resource_pool = true;
40 owns_resource_pool = false;
44 EffectChain::~EffectChain()
46 for (unsigned i = 0; i < nodes.size(); ++i) {
47 if (nodes[i]->output_texture != 0) {
48 glDeleteTextures(1, &nodes[i]->output_texture);
50 delete nodes[i]->effect;
53 for (unsigned i = 0; i < phases.size(); ++i) {
54 resource_pool->release_glsl_program(phases[i]->glsl_program_num);
57 if (owns_resource_pool) {
62 Input *EffectChain::add_input(Input *input)
65 inputs.push_back(input);
70 void EffectChain::add_output(const ImageFormat &format, OutputAlphaFormat alpha_format)
73 output_format = format;
74 output_alpha_format = alpha_format;
77 Node *EffectChain::add_node(Effect *effect)
79 for (unsigned i = 0; i < nodes.size(); ++i) {
80 assert(nodes[i]->effect != effect);
84 sprintf(effect_id, "eff%u", (unsigned)nodes.size());
86 Node *node = new Node;
87 node->effect = effect;
88 node->disabled = false;
89 node->effect_id = effect_id;
90 node->output_color_space = COLORSPACE_INVALID;
91 node->output_gamma_curve = GAMMA_INVALID;
92 node->output_alpha_type = ALPHA_INVALID;
93 node->output_texture = 0;
95 nodes.push_back(node);
96 node_map[effect] = node;
100 void EffectChain::connect_nodes(Node *sender, Node *receiver)
102 sender->outgoing_links.push_back(receiver);
103 receiver->incoming_links.push_back(sender);
106 void EffectChain::replace_receiver(Node *old_receiver, Node *new_receiver)
108 new_receiver->incoming_links = old_receiver->incoming_links;
109 old_receiver->incoming_links.clear();
111 for (unsigned i = 0; i < new_receiver->incoming_links.size(); ++i) {
112 Node *sender = new_receiver->incoming_links[i];
113 for (unsigned j = 0; j < sender->outgoing_links.size(); ++j) {
114 if (sender->outgoing_links[j] == old_receiver) {
115 sender->outgoing_links[j] = new_receiver;
121 void EffectChain::replace_sender(Node *old_sender, Node *new_sender)
123 new_sender->outgoing_links = old_sender->outgoing_links;
124 old_sender->outgoing_links.clear();
126 for (unsigned i = 0; i < new_sender->outgoing_links.size(); ++i) {
127 Node *receiver = new_sender->outgoing_links[i];
128 for (unsigned j = 0; j < receiver->incoming_links.size(); ++j) {
129 if (receiver->incoming_links[j] == old_sender) {
130 receiver->incoming_links[j] = new_sender;
136 void EffectChain::insert_node_between(Node *sender, Node *middle, Node *receiver)
138 for (unsigned i = 0; i < sender->outgoing_links.size(); ++i) {
139 if (sender->outgoing_links[i] == receiver) {
140 sender->outgoing_links[i] = middle;
141 middle->incoming_links.push_back(sender);
144 for (unsigned i = 0; i < receiver->incoming_links.size(); ++i) {
145 if (receiver->incoming_links[i] == sender) {
146 receiver->incoming_links[i] = middle;
147 middle->outgoing_links.push_back(receiver);
151 assert(middle->incoming_links.size() == middle->effect->num_inputs());
154 void EffectChain::find_all_nonlinear_inputs(Node *node, std::vector<Node *> *nonlinear_inputs)
156 if (node->output_gamma_curve == GAMMA_LINEAR &&
157 node->effect->effect_type_id() != "GammaCompressionEffect") {
160 if (node->effect->num_inputs() == 0) {
161 nonlinear_inputs->push_back(node);
163 assert(node->effect->num_inputs() == node->incoming_links.size());
164 for (unsigned i = 0; i < node->incoming_links.size(); ++i) {
165 find_all_nonlinear_inputs(node->incoming_links[i], nonlinear_inputs);
170 Effect *EffectChain::add_effect(Effect *effect, const std::vector<Effect *> &inputs)
173 assert(inputs.size() == effect->num_inputs());
174 Node *node = add_node(effect);
175 for (unsigned i = 0; i < inputs.size(); ++i) {
176 assert(node_map.count(inputs[i]) != 0);
177 connect_nodes(node_map[inputs[i]], node);
182 // GLSL pre-1.30 doesn't support token pasting. Replace PREFIX(x) with <effect_id>_x.
183 std::string replace_prefix(const std::string &text, const std::string &prefix)
188 while (start < text.size()) {
189 size_t pos = text.find("PREFIX(", start);
190 if (pos == std::string::npos) {
191 output.append(text.substr(start, std::string::npos));
195 output.append(text.substr(start, pos - start));
196 output.append(prefix);
199 pos += strlen("PREFIX(");
201 // Output stuff until we find the matching ), which we then eat.
203 size_t end_arg_pos = pos;
204 while (end_arg_pos < text.size()) {
205 if (text[end_arg_pos] == '(') {
207 } else if (text[end_arg_pos] == ')') {
215 output.append(text.substr(pos, end_arg_pos - pos));
223 Phase *EffectChain::compile_glsl_program(
224 const std::vector<Node *> &inputs,
225 const std::vector<Node *> &effects)
227 assert(!effects.empty());
229 // Deduplicate the inputs.
230 std::vector<Node *> true_inputs = inputs;
231 std::sort(true_inputs.begin(), true_inputs.end());
232 true_inputs.erase(std::unique(true_inputs.begin(), true_inputs.end()), true_inputs.end());
234 bool input_needs_mipmaps = false;
235 std::string frag_shader = read_file("header.frag");
237 // Create functions for all the texture inputs that we need.
238 for (unsigned i = 0; i < true_inputs.size(); ++i) {
239 Node *input = true_inputs[i];
241 frag_shader += std::string("uniform sampler2D tex_") + input->effect_id + ";\n";
242 frag_shader += std::string("vec4 ") + input->effect_id + "(vec2 tc) {\n";
243 frag_shader += "\treturn texture2D(tex_" + input->effect_id + ", tc);\n";
244 frag_shader += "}\n";
248 std::vector<Node *> sorted_effects = topological_sort(effects);
250 for (unsigned i = 0; i < sorted_effects.size(); ++i) {
251 Node *node = sorted_effects[i];
253 if (node->incoming_links.size() == 1) {
254 frag_shader += std::string("#define INPUT ") + node->incoming_links[0]->effect_id + "\n";
256 for (unsigned j = 0; j < node->incoming_links.size(); ++j) {
258 sprintf(buf, "#define INPUT%d %s\n", j + 1, node->incoming_links[j]->effect_id.c_str());
264 frag_shader += std::string("#define FUNCNAME ") + node->effect_id + "\n";
265 frag_shader += replace_prefix(node->effect->output_convenience_uniforms(), node->effect_id);
266 frag_shader += replace_prefix(node->effect->output_fragment_shader(), node->effect_id);
267 frag_shader += "#undef PREFIX\n";
268 frag_shader += "#undef FUNCNAME\n";
269 if (node->incoming_links.size() == 1) {
270 frag_shader += "#undef INPUT\n";
272 for (unsigned j = 0; j < node->incoming_links.size(); ++j) {
274 sprintf(buf, "#undef INPUT%d\n", j + 1);
280 input_needs_mipmaps |= node->effect->needs_mipmaps();
282 for (unsigned i = 0; i < sorted_effects.size(); ++i) {
283 Node *node = sorted_effects[i];
284 if (node->effect->num_inputs() == 0) {
285 CHECK(node->effect->set_int("needs_mipmaps", input_needs_mipmaps));
288 frag_shader += std::string("#define INPUT ") + sorted_effects.back()->effect_id + "\n";
289 frag_shader.append(read_file("footer.frag"));
291 Phase *phase = new Phase;
292 phase->glsl_program_num = resource_pool->compile_glsl_program(read_file("vs.vert"), frag_shader);
293 phase->input_needs_mipmaps = input_needs_mipmaps;
294 phase->inputs = true_inputs;
295 phase->effects = sorted_effects;
300 // Construct GLSL programs, starting at the given effect and following
301 // the chain from there. We end a program every time we come to an effect
302 // marked as "needs texture bounce", one that is used by multiple other
303 // effects, every time an effect wants to change the output size,
304 // and of course at the end.
306 // We follow a quite simple depth-first search from the output, although
307 // without any explicit recursion.
308 void EffectChain::construct_glsl_programs(Node *output)
310 // Which effects have already been completed?
311 // We need to keep track of it, as an effect with multiple outputs
312 // could otherwise be calculated multiple times.
313 std::set<Node *> completed_effects;
315 // Effects in the current phase, as well as inputs (outputs from other phases
316 // that we depend on). Note that since we start iterating from the end,
317 // the effect list will be in the reverse order.
318 std::vector<Node *> this_phase_inputs;
319 std::vector<Node *> this_phase_effects;
321 // Effects that we have yet to calculate, but that we know should
322 // be in the current phase.
323 std::stack<Node *> effects_todo_this_phase;
325 // Effects that we have yet to calculate, but that come from other phases.
326 // We delay these until we have this phase done in its entirety,
327 // at which point we pick any of them and start a new phase from that.
328 std::stack<Node *> effects_todo_other_phases;
330 effects_todo_this_phase.push(output);
332 for ( ;; ) { // Termination condition within loop.
333 if (!effects_todo_this_phase.empty()) {
334 // OK, we have more to do this phase.
335 Node *node = effects_todo_this_phase.top();
336 effects_todo_this_phase.pop();
338 // This should currently only happen for effects that are inputs
339 // (either true inputs or phase outputs). We special-case inputs,
340 // and then deduplicate phase outputs in compile_glsl_program().
341 if (node->effect->num_inputs() == 0) {
342 if (find(this_phase_effects.begin(), this_phase_effects.end(), node) != this_phase_effects.end()) {
346 assert(completed_effects.count(node) == 0);
349 this_phase_effects.push_back(node);
350 completed_effects.insert(node);
352 // Find all the dependencies of this effect, and add them to the stack.
353 std::vector<Node *> deps = node->incoming_links;
354 assert(node->effect->num_inputs() == deps.size());
355 for (unsigned i = 0; i < deps.size(); ++i) {
356 bool start_new_phase = false;
358 // FIXME: If we sample directly from a texture, we won't need this.
359 if (node->effect->needs_texture_bounce()) {
360 start_new_phase = true;
363 if (deps[i]->outgoing_links.size() > 1) {
364 if (deps[i]->effect->num_inputs() > 0) {
365 // More than one effect uses this as the input,
366 // and it is not a texture itself.
367 // The easiest thing to do (and probably also the safest
368 // performance-wise in most cases) is to bounce it to a texture
369 // and then let the next passes read from that.
370 start_new_phase = true;
372 // For textures, we try to be slightly more clever;
373 // if none of our outputs need a bounce, we don't bounce
374 // but instead simply use the effect many times.
376 // Strictly speaking, we could bounce it for some outputs
377 // and use it directly for others, but the processing becomes
378 // somewhat simpler if the effect is only used in one such way.
379 for (unsigned j = 0; j < deps[i]->outgoing_links.size(); ++j) {
380 Node *rdep = deps[i]->outgoing_links[j];
381 start_new_phase |= rdep->effect->needs_texture_bounce();
386 if (deps[i]->effect->changes_output_size()) {
387 start_new_phase = true;
390 if (start_new_phase) {
391 effects_todo_other_phases.push(deps[i]);
392 this_phase_inputs.push_back(deps[i]);
394 effects_todo_this_phase.push(deps[i]);
400 // No more effects to do this phase. Take all the ones we have,
401 // and create a GLSL program for it.
402 if (!this_phase_effects.empty()) {
403 reverse(this_phase_effects.begin(), this_phase_effects.end());
404 phases.push_back(compile_glsl_program(this_phase_inputs, this_phase_effects));
405 this_phase_effects.back()->phase = phases.back();
406 this_phase_inputs.clear();
407 this_phase_effects.clear();
409 assert(this_phase_inputs.empty());
410 assert(this_phase_effects.empty());
412 // If we have no effects left, exit.
413 if (effects_todo_other_phases.empty()) {
417 Node *node = effects_todo_other_phases.top();
418 effects_todo_other_phases.pop();
420 if (completed_effects.count(node) == 0) {
421 // Start a new phase, calculating from this effect.
422 effects_todo_this_phase.push(node);
426 // Finally, since the phases are found from the output but must be executed
427 // from the input(s), reverse them, too.
428 std::reverse(phases.begin(), phases.end());
431 void EffectChain::output_dot(const char *filename)
433 if (movit_debug_level != MOVIT_DEBUG_ON) {
437 FILE *fp = fopen(filename, "w");
443 fprintf(fp, "digraph G {\n");
444 fprintf(fp, " output [shape=box label=\"(output)\"];\n");
445 for (unsigned i = 0; i < nodes.size(); ++i) {
446 // Find out which phase this event belongs to.
447 std::vector<int> in_phases;
448 for (unsigned j = 0; j < phases.size(); ++j) {
449 const Phase* p = phases[j];
450 if (std::find(p->effects.begin(), p->effects.end(), nodes[i]) != p->effects.end()) {
451 in_phases.push_back(j);
455 if (in_phases.empty()) {
456 fprintf(fp, " n%ld [label=\"%s\"];\n", (long)nodes[i], nodes[i]->effect->effect_type_id().c_str());
457 } else if (in_phases.size() == 1) {
458 fprintf(fp, " n%ld [label=\"%s\" style=\"filled\" fillcolor=\"/accent8/%d\"];\n",
459 (long)nodes[i], nodes[i]->effect->effect_type_id().c_str(),
460 (in_phases[0] % 8) + 1);
462 // If we had new enough Graphviz, style="wedged" would probably be ideal here.
464 fprintf(fp, " n%ld [label=\"%s [in multiple phases]\" style=\"filled\" fillcolor=\"/accent8/%d\"];\n",
465 (long)nodes[i], nodes[i]->effect->effect_type_id().c_str(),
466 (in_phases[0] % 8) + 1);
469 char from_node_id[256];
470 snprintf(from_node_id, 256, "n%ld", (long)nodes[i]);
472 for (unsigned j = 0; j < nodes[i]->outgoing_links.size(); ++j) {
473 char to_node_id[256];
474 snprintf(to_node_id, 256, "n%ld", (long)nodes[i]->outgoing_links[j]);
476 std::vector<std::string> labels = get_labels_for_edge(nodes[i], nodes[i]->outgoing_links[j]);
477 output_dot_edge(fp, from_node_id, to_node_id, labels);
480 if (nodes[i]->outgoing_links.empty() && !nodes[i]->disabled) {
482 std::vector<std::string> labels = get_labels_for_edge(nodes[i], NULL);
483 output_dot_edge(fp, from_node_id, "output", labels);
491 std::vector<std::string> EffectChain::get_labels_for_edge(const Node *from, const Node *to)
493 std::vector<std::string> labels;
495 if (to != NULL && to->effect->needs_texture_bounce()) {
496 labels.push_back("needs_bounce");
498 if (from->effect->changes_output_size()) {
499 labels.push_back("resize");
502 switch (from->output_color_space) {
503 case COLORSPACE_INVALID:
504 labels.push_back("spc[invalid]");
506 case COLORSPACE_REC_601_525:
507 labels.push_back("spc[rec601-525]");
509 case COLORSPACE_REC_601_625:
510 labels.push_back("spc[rec601-625]");
516 switch (from->output_gamma_curve) {
518 labels.push_back("gamma[invalid]");
521 labels.push_back("gamma[sRGB]");
523 case GAMMA_REC_601: // and GAMMA_REC_709
524 labels.push_back("gamma[rec601/709]");
530 switch (from->output_alpha_type) {
532 labels.push_back("alpha[invalid]");
535 labels.push_back("alpha[blank]");
537 case ALPHA_POSTMULTIPLIED:
538 labels.push_back("alpha[postmult]");
547 void EffectChain::output_dot_edge(FILE *fp,
548 const std::string &from_node_id,
549 const std::string &to_node_id,
550 const std::vector<std::string> &labels)
552 if (labels.empty()) {
553 fprintf(fp, " %s -> %s;\n", from_node_id.c_str(), to_node_id.c_str());
555 std::string label = labels[0];
556 for (unsigned k = 1; k < labels.size(); ++k) {
557 label += ", " + labels[k];
559 fprintf(fp, " %s -> %s [label=\"%s\"];\n", from_node_id.c_str(), to_node_id.c_str(), label.c_str());
563 void EffectChain::size_rectangle_to_fit(unsigned width, unsigned height, unsigned *output_width, unsigned *output_height)
565 unsigned scaled_width, scaled_height;
567 if (float(width) * aspect_denom >= float(height) * aspect_nom) {
568 // Same aspect, or W/H > aspect (image is wider than the frame).
569 // In either case, keep width, and adjust height.
570 scaled_width = width;
571 scaled_height = lrintf(width * aspect_denom / aspect_nom);
573 // W/H < aspect (image is taller than the frame), so keep height,
575 scaled_width = lrintf(height * aspect_nom / aspect_denom);
576 scaled_height = height;
579 // We should be consistently larger or smaller then the existing choice,
580 // since we have the same aspect.
581 assert(!(scaled_width < *output_width && scaled_height > *output_height));
582 assert(!(scaled_height < *output_height && scaled_width > *output_width));
584 if (scaled_width >= *output_width && scaled_height >= *output_height) {
585 *output_width = scaled_width;
586 *output_height = scaled_height;
590 // Propagate input texture sizes throughout, and inform effects downstream.
591 // (Like a lot of other code, we depend on effects being in topological order.)
592 void EffectChain::inform_input_sizes(Phase *phase)
594 // All effects that have a defined size (inputs and RTT inputs)
595 // get that. Reset all others.
596 for (unsigned i = 0; i < phase->effects.size(); ++i) {
597 Node *node = phase->effects[i];
598 if (node->effect->num_inputs() == 0) {
599 Input *input = static_cast<Input *>(node->effect);
600 node->output_width = input->get_width();
601 node->output_height = input->get_height();
602 assert(node->output_width != 0);
603 assert(node->output_height != 0);
605 node->output_width = node->output_height = 0;
608 for (unsigned i = 0; i < phase->inputs.size(); ++i) {
609 Node *input = phase->inputs[i];
610 input->output_width = input->phase->virtual_output_width;
611 input->output_height = input->phase->virtual_output_height;
612 assert(input->output_width != 0);
613 assert(input->output_height != 0);
616 // Now propagate from the inputs towards the end, and inform as we go.
617 // The rules are simple:
619 // 1. Don't touch effects that already have given sizes (ie., inputs).
620 // 2. If all of your inputs have the same size, that will be your output size.
621 // 3. Otherwise, your output size is 0x0.
622 for (unsigned i = 0; i < phase->effects.size(); ++i) {
623 Node *node = phase->effects[i];
624 if (node->effect->num_inputs() == 0) {
627 unsigned this_output_width = 0;
628 unsigned this_output_height = 0;
629 for (unsigned j = 0; j < node->incoming_links.size(); ++j) {
630 Node *input = node->incoming_links[j];
631 node->effect->inform_input_size(j, input->output_width, input->output_height);
633 this_output_width = input->output_width;
634 this_output_height = input->output_height;
635 } else if (input->output_width != this_output_width || input->output_height != this_output_height) {
637 this_output_width = 0;
638 this_output_height = 0;
641 node->output_width = this_output_width;
642 node->output_height = this_output_height;
646 // Note: You should call inform_input_sizes() before this, as the last effect's
647 // desired output size might change based on the inputs.
648 void EffectChain::find_output_size(Phase *phase)
650 Node *output_node = phase->effects.back();
652 // If the last effect explicitly sets an output size, use that.
653 if (output_node->effect->changes_output_size()) {
654 output_node->effect->get_output_size(&phase->output_width, &phase->output_height,
655 &phase->virtual_output_width, &phase->virtual_output_height);
659 // If all effects have the same size, use that.
660 unsigned output_width = 0, output_height = 0;
661 bool all_inputs_same_size = true;
663 for (unsigned i = 0; i < phase->inputs.size(); ++i) {
664 Node *input = phase->inputs[i];
665 assert(input->phase->output_width != 0);
666 assert(input->phase->output_height != 0);
667 if (output_width == 0 && output_height == 0) {
668 output_width = input->phase->virtual_output_width;
669 output_height = input->phase->virtual_output_height;
670 } else if (output_width != input->phase->virtual_output_width ||
671 output_height != input->phase->virtual_output_height) {
672 all_inputs_same_size = false;
675 for (unsigned i = 0; i < phase->effects.size(); ++i) {
676 Effect *effect = phase->effects[i]->effect;
677 if (effect->num_inputs() != 0) {
681 Input *input = static_cast<Input *>(effect);
682 if (output_width == 0 && output_height == 0) {
683 output_width = input->get_width();
684 output_height = input->get_height();
685 } else if (output_width != input->get_width() ||
686 output_height != input->get_height()) {
687 all_inputs_same_size = false;
691 if (all_inputs_same_size) {
692 assert(output_width != 0);
693 assert(output_height != 0);
694 phase->virtual_output_width = phase->output_width = output_width;
695 phase->virtual_output_height = phase->output_height = output_height;
699 // If not, fit all the inputs into the current aspect, and select the largest one.
702 for (unsigned i = 0; i < phase->inputs.size(); ++i) {
703 Node *input = phase->inputs[i];
704 assert(input->phase->output_width != 0);
705 assert(input->phase->output_height != 0);
706 size_rectangle_to_fit(input->phase->output_width, input->phase->output_height, &output_width, &output_height);
708 for (unsigned i = 0; i < phase->effects.size(); ++i) {
709 Effect *effect = phase->effects[i]->effect;
710 if (effect->num_inputs() != 0) {
714 Input *input = static_cast<Input *>(effect);
715 size_rectangle_to_fit(input->get_width(), input->get_height(), &output_width, &output_height);
717 assert(output_width != 0);
718 assert(output_height != 0);
719 phase->virtual_output_width = phase->output_width = output_width;
720 phase->virtual_output_height = phase->output_height = output_height;
723 void EffectChain::sort_all_nodes_topologically()
725 nodes = topological_sort(nodes);
728 std::vector<Node *> EffectChain::topological_sort(const std::vector<Node *> &nodes)
730 std::set<Node *> nodes_left_to_visit(nodes.begin(), nodes.end());
731 std::vector<Node *> sorted_list;
732 for (unsigned i = 0; i < nodes.size(); ++i) {
733 topological_sort_visit_node(nodes[i], &nodes_left_to_visit, &sorted_list);
735 reverse(sorted_list.begin(), sorted_list.end());
739 void EffectChain::topological_sort_visit_node(Node *node, std::set<Node *> *nodes_left_to_visit, std::vector<Node *> *sorted_list)
741 if (nodes_left_to_visit->count(node) == 0) {
744 nodes_left_to_visit->erase(node);
745 for (unsigned i = 0; i < node->outgoing_links.size(); ++i) {
746 topological_sort_visit_node(node->outgoing_links[i], nodes_left_to_visit, sorted_list);
748 sorted_list->push_back(node);
751 void EffectChain::find_color_spaces_for_inputs()
753 for (unsigned i = 0; i < nodes.size(); ++i) {
754 Node *node = nodes[i];
755 if (node->disabled) {
758 if (node->incoming_links.size() == 0) {
759 Input *input = static_cast<Input *>(node->effect);
760 node->output_color_space = input->get_color_space();
761 node->output_gamma_curve = input->get_gamma_curve();
763 Effect::AlphaHandling alpha_handling = input->alpha_handling();
764 switch (alpha_handling) {
765 case Effect::OUTPUT_BLANK_ALPHA:
766 node->output_alpha_type = ALPHA_BLANK;
768 case Effect::INPUT_AND_OUTPUT_PREMULTIPLIED_ALPHA:
769 node->output_alpha_type = ALPHA_PREMULTIPLIED;
771 case Effect::OUTPUT_POSTMULTIPLIED_ALPHA:
772 node->output_alpha_type = ALPHA_POSTMULTIPLIED;
774 case Effect::INPUT_PREMULTIPLIED_ALPHA_KEEP_BLANK:
775 case Effect::DONT_CARE_ALPHA_TYPE:
780 if (node->output_alpha_type == ALPHA_PREMULTIPLIED) {
781 assert(node->output_gamma_curve == GAMMA_LINEAR);
787 // Propagate gamma and color space information as far as we can in the graph.
788 // The rules are simple: Anything where all the inputs agree, get that as
789 // output as well. Anything else keeps having *_INVALID.
790 void EffectChain::propagate_gamma_and_color_space()
792 // We depend on going through the nodes in order.
793 sort_all_nodes_topologically();
795 for (unsigned i = 0; i < nodes.size(); ++i) {
796 Node *node = nodes[i];
797 if (node->disabled) {
800 assert(node->incoming_links.size() == node->effect->num_inputs());
801 if (node->incoming_links.size() == 0) {
802 assert(node->output_color_space != COLORSPACE_INVALID);
803 assert(node->output_gamma_curve != GAMMA_INVALID);
807 Colorspace color_space = node->incoming_links[0]->output_color_space;
808 GammaCurve gamma_curve = node->incoming_links[0]->output_gamma_curve;
809 for (unsigned j = 1; j < node->incoming_links.size(); ++j) {
810 if (node->incoming_links[j]->output_color_space != color_space) {
811 color_space = COLORSPACE_INVALID;
813 if (node->incoming_links[j]->output_gamma_curve != gamma_curve) {
814 gamma_curve = GAMMA_INVALID;
818 // The conversion effects already have their outputs set correctly,
819 // so leave them alone.
820 if (node->effect->effect_type_id() != "ColorspaceConversionEffect") {
821 node->output_color_space = color_space;
823 if (node->effect->effect_type_id() != "GammaCompressionEffect" &&
824 node->effect->effect_type_id() != "GammaExpansionEffect") {
825 node->output_gamma_curve = gamma_curve;
830 // Propagate alpha information as far as we can in the graph.
831 // Similar to propagate_gamma_and_color_space().
832 void EffectChain::propagate_alpha()
834 // We depend on going through the nodes in order.
835 sort_all_nodes_topologically();
837 for (unsigned i = 0; i < nodes.size(); ++i) {
838 Node *node = nodes[i];
839 if (node->disabled) {
842 assert(node->incoming_links.size() == node->effect->num_inputs());
843 if (node->incoming_links.size() == 0) {
844 assert(node->output_alpha_type != ALPHA_INVALID);
848 // The alpha multiplication/division effects are special cases.
849 if (node->effect->effect_type_id() == "AlphaMultiplicationEffect") {
850 assert(node->incoming_links.size() == 1);
851 assert(node->incoming_links[0]->output_alpha_type == ALPHA_POSTMULTIPLIED);
852 node->output_alpha_type = ALPHA_PREMULTIPLIED;
855 if (node->effect->effect_type_id() == "AlphaDivisionEffect") {
856 assert(node->incoming_links.size() == 1);
857 assert(node->incoming_links[0]->output_alpha_type == ALPHA_PREMULTIPLIED);
858 node->output_alpha_type = ALPHA_POSTMULTIPLIED;
862 // GammaCompressionEffect and GammaExpansionEffect are also a special case,
863 // because they are the only one that _need_ postmultiplied alpha.
864 if (node->effect->effect_type_id() == "GammaCompressionEffect" ||
865 node->effect->effect_type_id() == "GammaExpansionEffect") {
866 assert(node->incoming_links.size() == 1);
867 if (node->incoming_links[0]->output_alpha_type == ALPHA_BLANK) {
868 node->output_alpha_type = ALPHA_BLANK;
869 } else if (node->incoming_links[0]->output_alpha_type == ALPHA_POSTMULTIPLIED) {
870 node->output_alpha_type = ALPHA_POSTMULTIPLIED;
872 node->output_alpha_type = ALPHA_INVALID;
877 // Only inputs can have unconditional alpha output (OUTPUT_BLANK_ALPHA
878 // or OUTPUT_POSTMULTIPLIED_ALPHA), and they have already been
879 // taken care of above. Rationale: Even if you could imagine
880 // e.g. an effect that took in an image and set alpha=1.0
881 // unconditionally, it wouldn't make any sense to have it as
882 // e.g. OUTPUT_BLANK_ALPHA, since it wouldn't know whether it
883 // got its input pre- or postmultiplied, so it wouldn't know
884 // whether to divide away the old alpha or not.
885 Effect::AlphaHandling alpha_handling = node->effect->alpha_handling();
886 assert(alpha_handling == Effect::INPUT_AND_OUTPUT_PREMULTIPLIED_ALPHA ||
887 alpha_handling == Effect::INPUT_PREMULTIPLIED_ALPHA_KEEP_BLANK ||
888 alpha_handling == Effect::DONT_CARE_ALPHA_TYPE);
890 // If the node has multiple inputs, check that they are all valid and
892 bool any_invalid = false;
893 bool any_premultiplied = false;
894 bool any_postmultiplied = false;
896 for (unsigned j = 0; j < node->incoming_links.size(); ++j) {
897 switch (node->incoming_links[j]->output_alpha_type) {
902 // Blank is good as both pre- and postmultiplied alpha,
903 // so just ignore it.
905 case ALPHA_PREMULTIPLIED:
906 any_premultiplied = true;
908 case ALPHA_POSTMULTIPLIED:
909 any_postmultiplied = true;
917 node->output_alpha_type = ALPHA_INVALID;
921 // Inputs must be of the same type.
922 if (any_premultiplied && any_postmultiplied) {
923 node->output_alpha_type = ALPHA_INVALID;
927 if (alpha_handling == Effect::INPUT_AND_OUTPUT_PREMULTIPLIED_ALPHA ||
928 alpha_handling == Effect::INPUT_PREMULTIPLIED_ALPHA_KEEP_BLANK) {
929 // If the effect has asked for premultiplied alpha, check that it has got it.
930 if (any_postmultiplied) {
931 node->output_alpha_type = ALPHA_INVALID;
932 } else if (!any_premultiplied &&
933 alpha_handling == Effect::INPUT_PREMULTIPLIED_ALPHA_KEEP_BLANK) {
934 // Blank input alpha, and the effect preserves blank alpha.
935 node->output_alpha_type = ALPHA_BLANK;
937 node->output_alpha_type = ALPHA_PREMULTIPLIED;
940 // OK, all inputs are the same, and this effect is not going
942 assert(alpha_handling == Effect::DONT_CARE_ALPHA_TYPE);
943 if (any_premultiplied) {
944 node->output_alpha_type = ALPHA_PREMULTIPLIED;
945 } else if (any_postmultiplied) {
946 node->output_alpha_type = ALPHA_POSTMULTIPLIED;
948 node->output_alpha_type = ALPHA_BLANK;
954 bool EffectChain::node_needs_colorspace_fix(Node *node)
956 if (node->disabled) {
959 if (node->effect->num_inputs() == 0) {
963 // propagate_gamma_and_color_space() has already set our output
964 // to COLORSPACE_INVALID if the inputs differ, so we can rely on that.
965 if (node->output_color_space == COLORSPACE_INVALID) {
968 return (node->effect->needs_srgb_primaries() && node->output_color_space != COLORSPACE_sRGB);
971 // Fix up color spaces so that there are no COLORSPACE_INVALID nodes left in
972 // the graph. Our strategy is not always optimal, but quite simple:
973 // Find an effect that's as early as possible where the inputs are of
974 // unacceptable colorspaces (that is, either different, or, if the effect only
975 // wants sRGB, not sRGB.) Add appropriate conversions on all its inputs,
976 // propagate the information anew, and repeat until there are no more such
978 void EffectChain::fix_internal_color_spaces()
980 unsigned colorspace_propagation_pass = 0;
984 for (unsigned i = 0; i < nodes.size(); ++i) {
985 Node *node = nodes[i];
986 if (!node_needs_colorspace_fix(node)) {
990 // Go through each input that is not sRGB, and insert
991 // a colorspace conversion after it.
992 for (unsigned j = 0; j < node->incoming_links.size(); ++j) {
993 Node *input = node->incoming_links[j];
994 assert(input->output_color_space != COLORSPACE_INVALID);
995 if (input->output_color_space == COLORSPACE_sRGB) {
998 Node *conversion = add_node(new ColorspaceConversionEffect());
999 CHECK(conversion->effect->set_int("source_space", input->output_color_space));
1000 CHECK(conversion->effect->set_int("destination_space", COLORSPACE_sRGB));
1001 conversion->output_color_space = COLORSPACE_sRGB;
1002 replace_sender(input, conversion);
1003 connect_nodes(input, conversion);
1006 // Re-sort topologically, and propagate the new information.
1007 propagate_gamma_and_color_space();
1014 sprintf(filename, "step5-colorspacefix-iter%u.dot", ++colorspace_propagation_pass);
1015 output_dot(filename);
1016 assert(colorspace_propagation_pass < 100);
1017 } while (found_any);
1019 for (unsigned i = 0; i < nodes.size(); ++i) {
1020 Node *node = nodes[i];
1021 if (node->disabled) {
1024 assert(node->output_color_space != COLORSPACE_INVALID);
1028 bool EffectChain::node_needs_alpha_fix(Node *node)
1030 if (node->disabled) {
1034 // propagate_alpha() has already set our output to ALPHA_INVALID if the
1035 // inputs differ or we are otherwise in mismatch, so we can rely on that.
1036 return (node->output_alpha_type == ALPHA_INVALID);
1039 // Fix up alpha so that there are no ALPHA_INVALID nodes left in
1040 // the graph. Similar to fix_internal_color_spaces().
1041 void EffectChain::fix_internal_alpha(unsigned step)
1043 unsigned alpha_propagation_pass = 0;
1047 for (unsigned i = 0; i < nodes.size(); ++i) {
1048 Node *node = nodes[i];
1049 if (!node_needs_alpha_fix(node)) {
1053 // If we need to fix up GammaExpansionEffect, then clearly something
1054 // is wrong, since the combination of premultiplied alpha and nonlinear inputs
1056 assert(node->effect->effect_type_id() != "GammaExpansionEffect");
1058 AlphaType desired_type = ALPHA_PREMULTIPLIED;
1060 // GammaCompressionEffect is special; it needs postmultiplied alpha.
1061 if (node->effect->effect_type_id() == "GammaCompressionEffect") {
1062 assert(node->incoming_links.size() == 1);
1063 assert(node->incoming_links[0]->output_alpha_type == ALPHA_PREMULTIPLIED);
1064 desired_type = ALPHA_POSTMULTIPLIED;
1067 // Go through each input that is not premultiplied alpha, and insert
1068 // a conversion before it.
1069 for (unsigned j = 0; j < node->incoming_links.size(); ++j) {
1070 Node *input = node->incoming_links[j];
1071 assert(input->output_alpha_type != ALPHA_INVALID);
1072 if (input->output_alpha_type == desired_type ||
1073 input->output_alpha_type == ALPHA_BLANK) {
1077 if (desired_type == ALPHA_PREMULTIPLIED) {
1078 conversion = add_node(new AlphaMultiplicationEffect());
1080 conversion = add_node(new AlphaDivisionEffect());
1082 conversion->output_alpha_type = desired_type;
1083 replace_sender(input, conversion);
1084 connect_nodes(input, conversion);
1087 // Re-sort topologically, and propagate the new information.
1088 propagate_gamma_and_color_space();
1096 sprintf(filename, "step%u-alphafix-iter%u.dot", step, ++alpha_propagation_pass);
1097 output_dot(filename);
1098 assert(alpha_propagation_pass < 100);
1099 } while (found_any);
1101 for (unsigned i = 0; i < nodes.size(); ++i) {
1102 Node *node = nodes[i];
1103 if (node->disabled) {
1106 assert(node->output_alpha_type != ALPHA_INVALID);
1110 // Make so that the output is in the desired color space.
1111 void EffectChain::fix_output_color_space()
1113 Node *output = find_output_node();
1114 if (output->output_color_space != output_format.color_space) {
1115 Node *conversion = add_node(new ColorspaceConversionEffect());
1116 CHECK(conversion->effect->set_int("source_space", output->output_color_space));
1117 CHECK(conversion->effect->set_int("destination_space", output_format.color_space));
1118 conversion->output_color_space = output_format.color_space;
1119 connect_nodes(output, conversion);
1121 propagate_gamma_and_color_space();
1125 // Make so that the output is in the desired pre-/postmultiplication alpha state.
1126 void EffectChain::fix_output_alpha()
1128 Node *output = find_output_node();
1129 assert(output->output_alpha_type != ALPHA_INVALID);
1130 if (output->output_alpha_type == ALPHA_BLANK) {
1131 // No alpha output, so we don't care.
1134 if (output->output_alpha_type == ALPHA_PREMULTIPLIED &&
1135 output_alpha_format == OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED) {
1136 Node *conversion = add_node(new AlphaDivisionEffect());
1137 connect_nodes(output, conversion);
1139 propagate_gamma_and_color_space();
1141 if (output->output_alpha_type == ALPHA_POSTMULTIPLIED &&
1142 output_alpha_format == OUTPUT_ALPHA_FORMAT_PREMULTIPLIED) {
1143 Node *conversion = add_node(new AlphaMultiplicationEffect());
1144 connect_nodes(output, conversion);
1146 propagate_gamma_and_color_space();
1150 bool EffectChain::node_needs_gamma_fix(Node *node)
1152 if (node->disabled) {
1156 // Small hack since the output is not an explicit node:
1157 // If we are the last node and our output is in the wrong
1158 // space compared to EffectChain's output, we need to fix it.
1159 // This will only take us to linear, but fix_output_gamma()
1160 // will come and take us to the desired output gamma
1163 // This needs to be before everything else, since it could
1164 // even apply to inputs (if they are the only effect).
1165 if (node->outgoing_links.empty() &&
1166 node->output_gamma_curve != output_format.gamma_curve &&
1167 node->output_gamma_curve != GAMMA_LINEAR) {
1171 if (node->effect->num_inputs() == 0) {
1175 // propagate_gamma_and_color_space() has already set our output
1176 // to GAMMA_INVALID if the inputs differ, so we can rely on that,
1177 // except for GammaCompressionEffect.
1178 if (node->output_gamma_curve == GAMMA_INVALID) {
1181 if (node->effect->effect_type_id() == "GammaCompressionEffect") {
1182 assert(node->incoming_links.size() == 1);
1183 return node->incoming_links[0]->output_gamma_curve != GAMMA_LINEAR;
1186 return (node->effect->needs_linear_light() && node->output_gamma_curve != GAMMA_LINEAR);
1189 // Very similar to fix_internal_color_spaces(), but for gamma.
1190 // There is one difference, though; before we start adding conversion nodes,
1191 // we see if we can get anything out of asking the sources to deliver
1192 // linear gamma directly. fix_internal_gamma_by_asking_inputs()
1193 // does that part, while fix_internal_gamma_by_inserting_nodes()
1194 // inserts nodes as needed afterwards.
1195 void EffectChain::fix_internal_gamma_by_asking_inputs(unsigned step)
1197 unsigned gamma_propagation_pass = 0;
1201 for (unsigned i = 0; i < nodes.size(); ++i) {
1202 Node *node = nodes[i];
1203 if (!node_needs_gamma_fix(node)) {
1207 // See if all inputs can give us linear gamma. If not, leave it.
1208 std::vector<Node *> nonlinear_inputs;
1209 find_all_nonlinear_inputs(node, &nonlinear_inputs);
1210 assert(!nonlinear_inputs.empty());
1213 for (unsigned i = 0; i < nonlinear_inputs.size(); ++i) {
1214 Input *input = static_cast<Input *>(nonlinear_inputs[i]->effect);
1215 all_ok &= input->can_output_linear_gamma();
1222 for (unsigned i = 0; i < nonlinear_inputs.size(); ++i) {
1223 CHECK(nonlinear_inputs[i]->effect->set_int("output_linear_gamma", 1));
1224 nonlinear_inputs[i]->output_gamma_curve = GAMMA_LINEAR;
1227 // Re-sort topologically, and propagate the new information.
1228 propagate_gamma_and_color_space();
1235 sprintf(filename, "step%u-gammafix-iter%u.dot", step, ++gamma_propagation_pass);
1236 output_dot(filename);
1237 assert(gamma_propagation_pass < 100);
1238 } while (found_any);
1241 void EffectChain::fix_internal_gamma_by_inserting_nodes(unsigned step)
1243 unsigned gamma_propagation_pass = 0;
1247 for (unsigned i = 0; i < nodes.size(); ++i) {
1248 Node *node = nodes[i];
1249 if (!node_needs_gamma_fix(node)) {
1253 // Special case: We could be an input and still be asked to
1254 // fix our gamma; if so, we should be the only node
1255 // (as node_needs_gamma_fix() would only return true in
1256 // for an input in that case). That means we should insert
1257 // a conversion node _after_ ourselves.
1258 if (node->incoming_links.empty()) {
1259 assert(node->outgoing_links.empty());
1260 Node *conversion = add_node(new GammaExpansionEffect());
1261 CHECK(conversion->effect->set_int("source_curve", node->output_gamma_curve));
1262 conversion->output_gamma_curve = GAMMA_LINEAR;
1263 connect_nodes(node, conversion);
1266 // If not, go through each input that is not linear gamma,
1267 // and insert a gamma conversion after it.
1268 for (unsigned j = 0; j < node->incoming_links.size(); ++j) {
1269 Node *input = node->incoming_links[j];
1270 assert(input->output_gamma_curve != GAMMA_INVALID);
1271 if (input->output_gamma_curve == GAMMA_LINEAR) {
1274 Node *conversion = add_node(new GammaExpansionEffect());
1275 CHECK(conversion->effect->set_int("source_curve", input->output_gamma_curve));
1276 conversion->output_gamma_curve = GAMMA_LINEAR;
1277 replace_sender(input, conversion);
1278 connect_nodes(input, conversion);
1281 // Re-sort topologically, and propagate the new information.
1283 propagate_gamma_and_color_space();
1290 sprintf(filename, "step%u-gammafix-iter%u.dot", step, ++gamma_propagation_pass);
1291 output_dot(filename);
1292 assert(gamma_propagation_pass < 100);
1293 } while (found_any);
1295 for (unsigned i = 0; i < nodes.size(); ++i) {
1296 Node *node = nodes[i];
1297 if (node->disabled) {
1300 assert(node->output_gamma_curve != GAMMA_INVALID);
1304 // Make so that the output is in the desired gamma.
1305 // Note that this assumes linear input gamma, so it might create the need
1306 // for another pass of fix_internal_gamma().
1307 void EffectChain::fix_output_gamma()
1309 Node *output = find_output_node();
1310 if (output->output_gamma_curve != output_format.gamma_curve) {
1311 Node *conversion = add_node(new GammaCompressionEffect());
1312 CHECK(conversion->effect->set_int("destination_curve", output_format.gamma_curve));
1313 conversion->output_gamma_curve = output_format.gamma_curve;
1314 connect_nodes(output, conversion);
1318 // If the user has requested dither, add a DitherEffect right at the end
1319 // (after GammaCompressionEffect etc.). This needs to be done after everything else,
1320 // since dither is about the only effect that can _not_ be done in linear space.
1321 void EffectChain::add_dither_if_needed()
1323 if (num_dither_bits == 0) {
1326 Node *output = find_output_node();
1327 Node *dither = add_node(new DitherEffect());
1328 CHECK(dither->effect->set_int("num_bits", num_dither_bits));
1329 connect_nodes(output, dither);
1331 dither_effect = dither->effect;
1334 // Find the output node. This is, simply, one that has no outgoing links.
1335 // If there are multiple ones, the graph is malformed (we do not support
1336 // multiple outputs right now).
1337 Node *EffectChain::find_output_node()
1339 std::vector<Node *> output_nodes;
1340 for (unsigned i = 0; i < nodes.size(); ++i) {
1341 Node *node = nodes[i];
1342 if (node->disabled) {
1345 if (node->outgoing_links.empty()) {
1346 output_nodes.push_back(node);
1349 assert(output_nodes.size() == 1);
1350 return output_nodes[0];
1353 void EffectChain::finalize()
1355 // Save the current locale, and set it to C, so that we can output decimal
1356 // numbers with printf and be sure to get them in the format mandated by GLSL.
1357 char *saved_locale = setlocale(LC_NUMERIC, "C");
1359 // Output the graph as it is before we do any conversions on it.
1360 output_dot("step0-start.dot");
1362 // Give each effect in turn a chance to rewrite its own part of the graph.
1363 // Note that if more effects are added as part of this, they will be
1364 // picked up as part of the same for loop, since they are added at the end.
1365 for (unsigned i = 0; i < nodes.size(); ++i) {
1366 nodes[i]->effect->rewrite_graph(this, nodes[i]);
1368 output_dot("step1-rewritten.dot");
1370 find_color_spaces_for_inputs();
1371 output_dot("step2-input-colorspace.dot");
1374 output_dot("step3-propagated-alpha.dot");
1376 propagate_gamma_and_color_space();
1377 output_dot("step4-propagated-all.dot");
1379 fix_internal_color_spaces();
1380 fix_internal_alpha(6);
1381 fix_output_color_space();
1382 output_dot("step7-output-colorspacefix.dot");
1384 output_dot("step8-output-alphafix.dot");
1386 // Note that we need to fix gamma after colorspace conversion,
1387 // because colorspace conversions might create needs for gamma conversions.
1388 // Also, we need to run an extra pass of fix_internal_gamma() after
1389 // fixing the output gamma, as we only have conversions to/from linear,
1390 // and fix_internal_alpha() since GammaCompressionEffect needs
1391 // postmultiplied input.
1392 fix_internal_gamma_by_asking_inputs(9);
1393 fix_internal_gamma_by_inserting_nodes(10);
1395 output_dot("step11-output-gammafix.dot");
1397 output_dot("step12-output-alpha-propagated.dot");
1398 fix_internal_alpha(13);
1399 output_dot("step14-output-alpha-fixed.dot");
1400 fix_internal_gamma_by_asking_inputs(15);
1401 fix_internal_gamma_by_inserting_nodes(16);
1403 output_dot("step17-before-dither.dot");
1405 add_dither_if_needed();
1407 output_dot("step18-final.dot");
1409 // Construct all needed GLSL programs, starting at the output.
1410 construct_glsl_programs(find_output_node());
1412 output_dot("step19-split-to-phases.dot");
1414 // If we have more than one phase, we need intermediate render-to-texture.
1415 // Construct an FBO, and then as many textures as we need.
1416 // We choose the simplest option of having one texture per output,
1417 // since otherwise this turns into an (albeit simple)
1418 // register allocation problem.
1419 if (phases.size() > 1) {
1420 for (unsigned i = 0; i < phases.size() - 1; ++i) {
1421 inform_input_sizes(phases[i]);
1422 find_output_size(phases[i]);
1424 Node *output_node = phases[i]->effects.back();
1425 glGenTextures(1, &output_node->output_texture);
1427 glBindTexture(GL_TEXTURE_2D, output_node->output_texture);
1429 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1431 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1433 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, phases[i]->output_width, phases[i]->output_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
1436 output_node->output_texture_width = phases[i]->output_width;
1437 output_node->output_texture_height = phases[i]->output_height;
1439 inform_input_sizes(phases.back());
1442 for (unsigned i = 0; i < inputs.size(); ++i) {
1443 inputs[i]->finalize();
1446 assert(phases[0]->inputs.empty());
1449 setlocale(LC_NUMERIC, saved_locale);
1452 void EffectChain::render_to_fbo(GLuint dest_fbo, unsigned width, unsigned height)
1456 // Save original viewport.
1457 GLuint x = 0, y = 0;
1460 if (width == 0 && height == 0) {
1462 glGetIntegerv(GL_VIEWPORT, viewport);
1465 width = viewport[2];
1466 height = viewport[3];
1470 glDisable(GL_BLEND);
1472 glDisable(GL_DEPTH_TEST);
1474 glDepthMask(GL_FALSE);
1477 glMatrixMode(GL_PROJECTION);
1479 glOrtho(0.0, 1.0, 0.0, 1.0, 0.0, 1.0);
1481 glMatrixMode(GL_MODELVIEW);
1484 if (phases.size() > 1) {
1485 glGenFramebuffers(1, &fbo);
1487 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
1491 std::set<Node *> generated_mipmaps;
1493 for (unsigned phase = 0; phase < phases.size(); ++phase) {
1494 // See if the requested output size has changed. If so, we need to recreate
1495 // the texture (and before we start setting up inputs).
1496 inform_input_sizes(phases[phase]);
1497 if (phase != phases.size() - 1) {
1498 find_output_size(phases[phase]);
1500 Node *output_node = phases[phase]->effects.back();
1502 if (output_node->output_texture_width != phases[phase]->output_width ||
1503 output_node->output_texture_height != phases[phase]->output_height) {
1504 glActiveTexture(GL_TEXTURE0);
1506 glBindTexture(GL_TEXTURE_2D, output_node->output_texture);
1508 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, phases[phase]->output_width, phases[phase]->output_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
1510 glBindTexture(GL_TEXTURE_2D, 0);
1513 output_node->output_texture_width = phases[phase]->output_width;
1514 output_node->output_texture_height = phases[phase]->output_height;
1518 glUseProgram(phases[phase]->glsl_program_num);
1521 // Set up RTT inputs for this phase.
1522 for (unsigned sampler = 0; sampler < phases[phase]->inputs.size(); ++sampler) {
1523 glActiveTexture(GL_TEXTURE0 + sampler);
1524 Node *input = phases[phase]->inputs[sampler];
1525 glBindTexture(GL_TEXTURE_2D, input->output_texture);
1527 if (phases[phase]->input_needs_mipmaps) {
1528 if (generated_mipmaps.count(input) == 0) {
1529 glGenerateMipmap(GL_TEXTURE_2D);
1531 generated_mipmaps.insert(input);
1533 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
1536 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1540 std::string texture_name = std::string("tex_") + input->effect_id;
1541 glUniform1i(glGetUniformLocation(phases[phase]->glsl_program_num, texture_name.c_str()), sampler);
1545 // And now the output.
1546 if (phase == phases.size() - 1) {
1547 // Last phase goes to the output the user specified.
1548 glBindFramebuffer(GL_FRAMEBUFFER, dest_fbo);
1550 GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
1551 assert(status == GL_FRAMEBUFFER_COMPLETE);
1552 glViewport(x, y, width, height);
1553 if (dither_effect != NULL) {
1554 CHECK(dither_effect->set_int("output_width", width));
1555 CHECK(dither_effect->set_int("output_height", height));
1558 Node *output_node = phases[phase]->effects.back();
1559 glFramebufferTexture2D(
1561 GL_COLOR_ATTACHMENT0,
1563 output_node->output_texture,
1566 GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
1567 assert(status == GL_FRAMEBUFFER_COMPLETE);
1568 glViewport(0, 0, phases[phase]->output_width, phases[phase]->output_height);
1571 // Give the required parameters to all the effects.
1572 unsigned sampler_num = phases[phase]->inputs.size();
1573 for (unsigned i = 0; i < phases[phase]->effects.size(); ++i) {
1574 Node *node = phases[phase]->effects[i];
1575 node->effect->set_gl_state(phases[phase]->glsl_program_num, node->effect_id, &sampler_num);
1582 glTexCoord2f(0.0f, 0.0f);
1583 glVertex2f(0.0f, 0.0f);
1585 glTexCoord2f(1.0f, 0.0f);
1586 glVertex2f(1.0f, 0.0f);
1588 glTexCoord2f(1.0f, 1.0f);
1589 glVertex2f(1.0f, 1.0f);
1591 glTexCoord2f(0.0f, 1.0f);
1592 glVertex2f(0.0f, 1.0f);
1597 for (unsigned i = 0; i < phases[phase]->effects.size(); ++i) {
1598 Node *node = phases[phase]->effects[i];
1599 node->effect->clear_gl_state();
1603 glBindFramebuffer(GL_FRAMEBUFFER, 0);
1607 glDeleteFramebuffers(1, &fbo);