]> git.sesse.net Git - movit/blob - effect_chain.cpp
8d3c61d27f4293eed70e9b5dfd4b02e6af9e9bbf
[movit] / effect_chain.cpp
1 #define GL_GLEXT_PROTOTYPES 1
2
3 #include <GL/glew.h>
4 #include <assert.h>
5 #include <locale.h>
6 #include <math.h>
7 #include <stddef.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <algorithm>
12 #include <set>
13 #include <stack>
14 #include <utility>
15 #include <vector>
16
17 #include "alpha_division_effect.h"
18 #include "alpha_multiplication_effect.h"
19 #include "colorspace_conversion_effect.h"
20 #include "dither_effect.h"
21 #include "effect.h"
22 #include "effect_chain.h"
23 #include "gamma_compression_effect.h"
24 #include "gamma_expansion_effect.h"
25 #include "init.h"
26 #include "input.h"
27 #include "resource_pool.h"
28 #include "util.h"
29
30 using namespace std;
31
32 namespace movit {
33
34 EffectChain::EffectChain(float aspect_nom, float aspect_denom, ResourcePool *resource_pool)
35         : aspect_nom(aspect_nom),
36           aspect_denom(aspect_denom),
37           dither_effect(NULL),
38           num_dither_bits(0),
39           finalized(false),
40           resource_pool(resource_pool) {
41         if (resource_pool == NULL) {
42                 this->resource_pool = new ResourcePool();
43                 owns_resource_pool = true;
44         } else {
45                 owns_resource_pool = false;
46         }
47 }
48
49 EffectChain::~EffectChain()
50 {
51         for (unsigned i = 0; i < nodes.size(); ++i) {
52                 delete nodes[i]->effect;
53                 delete nodes[i];
54         }
55         for (unsigned i = 0; i < phases.size(); ++i) {
56                 resource_pool->release_glsl_program(phases[i]->glsl_program_num);
57                 delete phases[i];
58         }
59         if (owns_resource_pool) {
60                 delete resource_pool;
61         }
62         for (map<void *, GLuint>::const_iterator fbo_it = fbos.begin();
63              fbo_it != fbos.end(); ++fbo_it) {
64                 glDeleteFramebuffers(1, &fbo_it->second);
65                 check_error();
66         }
67 }
68
69 Input *EffectChain::add_input(Input *input)
70 {
71         assert(!finalized);
72         inputs.push_back(input);
73         add_node(input);
74         return input;
75 }
76
77 void EffectChain::add_output(const ImageFormat &format, OutputAlphaFormat alpha_format)
78 {
79         assert(!finalized);
80         output_format = format;
81         output_alpha_format = alpha_format;
82 }
83
84 Node *EffectChain::add_node(Effect *effect)
85 {
86         for (unsigned i = 0; i < nodes.size(); ++i) {
87                 assert(nodes[i]->effect != effect);
88         }
89
90         Node *node = new Node;
91         node->effect = effect;
92         node->disabled = false;
93         node->output_color_space = COLORSPACE_INVALID;
94         node->output_gamma_curve = GAMMA_INVALID;
95         node->output_alpha_type = ALPHA_INVALID;
96
97         nodes.push_back(node);
98         node_map[effect] = node;
99         effect->inform_added(this);
100         return node;
101 }
102
103 void EffectChain::connect_nodes(Node *sender, Node *receiver)
104 {
105         sender->outgoing_links.push_back(receiver);
106         receiver->incoming_links.push_back(sender);
107 }
108
109 void EffectChain::replace_receiver(Node *old_receiver, Node *new_receiver)
110 {
111         new_receiver->incoming_links = old_receiver->incoming_links;
112         old_receiver->incoming_links.clear();
113         
114         for (unsigned i = 0; i < new_receiver->incoming_links.size(); ++i) {
115                 Node *sender = new_receiver->incoming_links[i];
116                 for (unsigned j = 0; j < sender->outgoing_links.size(); ++j) {
117                         if (sender->outgoing_links[j] == old_receiver) {
118                                 sender->outgoing_links[j] = new_receiver;
119                         }
120                 }
121         }       
122 }
123
124 void EffectChain::replace_sender(Node *old_sender, Node *new_sender)
125 {
126         new_sender->outgoing_links = old_sender->outgoing_links;
127         old_sender->outgoing_links.clear();
128         
129         for (unsigned i = 0; i < new_sender->outgoing_links.size(); ++i) {
130                 Node *receiver = new_sender->outgoing_links[i];
131                 for (unsigned j = 0; j < receiver->incoming_links.size(); ++j) {
132                         if (receiver->incoming_links[j] == old_sender) {
133                                 receiver->incoming_links[j] = new_sender;
134                         }
135                 }
136         }       
137 }
138
139 void EffectChain::insert_node_between(Node *sender, Node *middle, Node *receiver)
140 {
141         for (unsigned i = 0; i < sender->outgoing_links.size(); ++i) {
142                 if (sender->outgoing_links[i] == receiver) {
143                         sender->outgoing_links[i] = middle;
144                         middle->incoming_links.push_back(sender);
145                 }
146         }
147         for (unsigned i = 0; i < receiver->incoming_links.size(); ++i) {
148                 if (receiver->incoming_links[i] == sender) {
149                         receiver->incoming_links[i] = middle;
150                         middle->outgoing_links.push_back(receiver);
151                 }
152         }
153
154         assert(middle->incoming_links.size() == middle->effect->num_inputs());
155 }
156
157 GLenum EffectChain::get_input_sampler(Node *node, unsigned input_num) const
158 {
159         assert(node->effect->needs_texture_bounce());
160         assert(input_num < node->incoming_links.size());
161         assert(node->incoming_links[input_num]->bound_sampler_num >= 0);
162         assert(node->incoming_links[input_num]->bound_sampler_num < 8);
163         return GL_TEXTURE0 + node->incoming_links[input_num]->bound_sampler_num;
164 }
165
166 void EffectChain::find_all_nonlinear_inputs(Node *node, vector<Node *> *nonlinear_inputs)
167 {
168         if (node->output_gamma_curve == GAMMA_LINEAR &&
169             node->effect->effect_type_id() != "GammaCompressionEffect") {
170                 return;
171         }
172         if (node->effect->num_inputs() == 0) {
173                 nonlinear_inputs->push_back(node);
174         } else {
175                 assert(node->effect->num_inputs() == node->incoming_links.size());
176                 for (unsigned i = 0; i < node->incoming_links.size(); ++i) {
177                         find_all_nonlinear_inputs(node->incoming_links[i], nonlinear_inputs);
178                 }
179         }
180 }
181
182 Effect *EffectChain::add_effect(Effect *effect, const vector<Effect *> &inputs)
183 {
184         assert(!finalized);
185         assert(inputs.size() == effect->num_inputs());
186         Node *node = add_node(effect);
187         for (unsigned i = 0; i < inputs.size(); ++i) {
188                 assert(node_map.count(inputs[i]) != 0);
189                 connect_nodes(node_map[inputs[i]], node);
190         }
191         return effect;
192 }
193
194 // GLSL pre-1.30 doesn't support token pasting. Replace PREFIX(x) with <effect_id>_x.
195 string replace_prefix(const string &text, const string &prefix)
196 {
197         string output;
198         size_t start = 0;
199
200         while (start < text.size()) {
201                 size_t pos = text.find("PREFIX(", start);
202                 if (pos == string::npos) {
203                         output.append(text.substr(start, string::npos));
204                         break;
205                 }
206
207                 output.append(text.substr(start, pos - start));
208                 output.append(prefix);
209                 output.append("_");
210
211                 pos += strlen("PREFIX(");
212         
213                 // Output stuff until we find the matching ), which we then eat.
214                 int depth = 1;
215                 size_t end_arg_pos = pos;
216                 while (end_arg_pos < text.size()) {
217                         if (text[end_arg_pos] == '(') {
218                                 ++depth;
219                         } else if (text[end_arg_pos] == ')') {
220                                 --depth;
221                                 if (depth == 0) {
222                                         break;
223                                 }
224                         }
225                         ++end_arg_pos;
226                 }
227                 output.append(text.substr(pos, end_arg_pos - pos));
228                 ++end_arg_pos;
229                 assert(depth == 0);
230                 start = end_arg_pos;
231         }
232         return output;
233 }
234
235 void EffectChain::compile_glsl_program(Phase *phase)
236 {
237         string frag_shader = read_file("header.frag");
238
239         // Create functions for all the texture inputs that we need.
240         for (unsigned i = 0; i < phase->inputs.size(); ++i) {
241                 Node *input = phase->inputs[i]->output_node;
242                 char effect_id[256];
243                 sprintf(effect_id, "in%u", i);
244                 phase->effect_ids.insert(make_pair(input, effect_id));
245         
246                 frag_shader += string("uniform sampler2D tex_") + effect_id + ";\n";
247                 frag_shader += string("vec4 ") + effect_id + "(vec2 tc) {\n";
248                 frag_shader += "\treturn texture2D(tex_" + string(effect_id) + ", tc);\n";
249                 frag_shader += "}\n";
250                 frag_shader += "\n";
251         }
252
253         for (unsigned i = 0; i < phase->effects.size(); ++i) {
254                 Node *node = phase->effects[i];
255                 char effect_id[256];
256                 sprintf(effect_id, "eff%u", i);
257                 phase->effect_ids.insert(make_pair(node, effect_id));
258
259                 if (node->incoming_links.size() == 1) {
260                         frag_shader += string("#define INPUT ") + phase->effect_ids[node->incoming_links[0]] + "\n";
261                 } else {
262                         for (unsigned j = 0; j < node->incoming_links.size(); ++j) {
263                                 char buf[256];
264                                 sprintf(buf, "#define INPUT%d %s\n", j + 1, phase->effect_ids[node->incoming_links[j]].c_str());
265                                 frag_shader += buf;
266                         }
267                 }
268         
269                 frag_shader += "\n";
270                 frag_shader += string("#define FUNCNAME ") + effect_id + "\n";
271                 frag_shader += replace_prefix(node->effect->output_convenience_uniforms(), effect_id);
272                 frag_shader += replace_prefix(node->effect->output_fragment_shader(), effect_id);
273                 frag_shader += "#undef PREFIX\n";
274                 frag_shader += "#undef FUNCNAME\n";
275                 if (node->incoming_links.size() == 1) {
276                         frag_shader += "#undef INPUT\n";
277                 } else {
278                         for (unsigned j = 0; j < node->incoming_links.size(); ++j) {
279                                 char buf[256];
280                                 sprintf(buf, "#undef INPUT%d\n", j + 1);
281                                 frag_shader += buf;
282                         }
283                 }
284                 frag_shader += "\n";
285         }
286         frag_shader += string("#define INPUT ") + phase->effect_ids[phase->effects.back()] + "\n";
287         frag_shader.append(read_file("footer.frag"));
288
289         phase->glsl_program_num = resource_pool->compile_glsl_program(read_file("vs.vert"), frag_shader);
290 }
291
292 // Construct GLSL programs, starting at the given effect and following
293 // the chain from there. We end a program every time we come to an effect
294 // marked as "needs texture bounce", one that is used by multiple other
295 // effects, every time an effect wants to change the output size,
296 // and of course at the end.
297 //
298 // We follow a quite simple depth-first search from the output, although
299 // without recursing explicitly within each phase.
300 Phase *EffectChain::construct_phase(Node *output, map<Node *, Phase *> *completed_effects)
301 {
302         if (completed_effects->count(output)) {
303                 return (*completed_effects)[output];
304         }
305
306         Phase *phase = new Phase;
307         phase->output_node = output;
308
309         // Effects that we have yet to calculate, but that we know should
310         // be in the current phase.
311         stack<Node *> effects_todo_this_phase;
312         effects_todo_this_phase.push(output);
313
314         while (!effects_todo_this_phase.empty()) {
315                 Node *node = effects_todo_this_phase.top();
316                 effects_todo_this_phase.pop();
317
318                 // This should currently only happen for effects that are inputs
319                 // (either true inputs or phase outputs). We special-case inputs,
320                 // and then deduplicate phase outputs below.
321                 if (node->effect->num_inputs() == 0) {
322                         if (find(phase->effects.begin(), phase->effects.end(), node) != phase->effects.end()) {
323                                 continue;
324                         }
325                 } else {
326                         assert(completed_effects->count(node) == 0);
327                 }
328
329                 phase->effects.push_back(node);
330
331                 // Find all the dependencies of this effect, and add them to the stack.
332                 vector<Node *> deps = node->incoming_links;
333                 assert(node->effect->num_inputs() == deps.size());
334                 for (unsigned i = 0; i < deps.size(); ++i) {
335                         bool start_new_phase = false;
336
337                         if (node->effect->needs_texture_bounce() &&
338                             !deps[i]->effect->is_single_texture()) {
339                                 start_new_phase = true;
340                         }
341
342                         if (deps[i]->outgoing_links.size() > 1) {
343                                 if (!deps[i]->effect->is_single_texture()) {
344                                         // More than one effect uses this as the input,
345                                         // and it is not a texture itself.
346                                         // The easiest thing to do (and probably also the safest
347                                         // performance-wise in most cases) is to bounce it to a texture
348                                         // and then let the next passes read from that.
349                                         start_new_phase = true;
350                                 } else {
351                                         assert(deps[i]->effect->num_inputs() == 0);
352
353                                         // For textures, we try to be slightly more clever;
354                                         // if none of our outputs need a bounce, we don't bounce
355                                         // but instead simply use the effect many times.
356                                         //
357                                         // Strictly speaking, we could bounce it for some outputs
358                                         // and use it directly for others, but the processing becomes
359                                         // somewhat simpler if the effect is only used in one such way.
360                                         for (unsigned j = 0; j < deps[i]->outgoing_links.size(); ++j) {
361                                                 Node *rdep = deps[i]->outgoing_links[j];
362                                                 start_new_phase |= rdep->effect->needs_texture_bounce();
363                                         }
364                                 }
365                         }
366
367                         if (deps[i]->effect->changes_output_size()) {
368                                 start_new_phase = true;
369                         }
370
371                         if (start_new_phase) {
372                                 phase->inputs.push_back(construct_phase(deps[i], completed_effects));
373                         } else {
374                                 effects_todo_this_phase.push(deps[i]);
375                         }
376                 }
377         }
378
379         // No more effects to do this phase. Take all the ones we have,
380         // and create a GLSL program for it.
381         assert(!phase->effects.empty());
382
383         // Deduplicate the inputs.
384         sort(phase->inputs.begin(), phase->inputs.end());
385         phase->inputs.erase(unique(phase->inputs.begin(), phase->inputs.end()), phase->inputs.end());
386
387         // We added the effects from the output and back, but we need to output
388         // them in topological sort order in the shader.
389         phase->effects = topological_sort(phase->effects);
390
391         // Figure out if we need mipmaps or not, and if so, tell the inputs that.
392         phase->input_needs_mipmaps = false;
393         for (unsigned i = 0; i < phase->effects.size(); ++i) {
394                 Node *node = phase->effects[i];
395                 phase->input_needs_mipmaps |= node->effect->needs_mipmaps();
396         }
397         for (unsigned i = 0; i < phase->effects.size(); ++i) {
398                 Node *node = phase->effects[i];
399                 if (node->effect->num_inputs() == 0) {
400                         CHECK(node->effect->set_int("needs_mipmaps", phase->input_needs_mipmaps));
401                 }
402         }
403
404         // Actually make the shader for this phase.
405         compile_glsl_program(phase);
406
407         assert(completed_effects->count(output) == 0);
408         completed_effects->insert(make_pair(output, phase));
409         phases.push_back(phase);
410         return phase;
411 }
412
413 void EffectChain::output_dot(const char *filename)
414 {
415         if (movit_debug_level != MOVIT_DEBUG_ON) {
416                 return;
417         }
418
419         FILE *fp = fopen(filename, "w");
420         if (fp == NULL) {
421                 perror(filename);
422                 exit(1);
423         }
424
425         fprintf(fp, "digraph G {\n");
426         fprintf(fp, "  output [shape=box label=\"(output)\"];\n");
427         for (unsigned i = 0; i < nodes.size(); ++i) {
428                 // Find out which phase this event belongs to.
429                 vector<int> in_phases;
430                 for (unsigned j = 0; j < phases.size(); ++j) {
431                         const Phase* p = phases[j];
432                         if (find(p->effects.begin(), p->effects.end(), nodes[i]) != p->effects.end()) {
433                                 in_phases.push_back(j);
434                         }
435                 }
436
437                 if (in_phases.empty()) {
438                         fprintf(fp, "  n%ld [label=\"%s\"];\n", (long)nodes[i], nodes[i]->effect->effect_type_id().c_str());
439                 } else if (in_phases.size() == 1) {
440                         fprintf(fp, "  n%ld [label=\"%s\" style=\"filled\" fillcolor=\"/accent8/%d\"];\n",
441                                 (long)nodes[i], nodes[i]->effect->effect_type_id().c_str(),
442                                 (in_phases[0] % 8) + 1);
443                 } else {
444                         // If we had new enough Graphviz, style="wedged" would probably be ideal here.
445                         // But alas.
446                         fprintf(fp, "  n%ld [label=\"%s [in multiple phases]\" style=\"filled\" fillcolor=\"/accent8/%d\"];\n",
447                                 (long)nodes[i], nodes[i]->effect->effect_type_id().c_str(),
448                                 (in_phases[0] % 8) + 1);
449                 }
450
451                 char from_node_id[256];
452                 snprintf(from_node_id, 256, "n%ld", (long)nodes[i]);
453
454                 for (unsigned j = 0; j < nodes[i]->outgoing_links.size(); ++j) {
455                         char to_node_id[256];
456                         snprintf(to_node_id, 256, "n%ld", (long)nodes[i]->outgoing_links[j]);
457
458                         vector<string> labels = get_labels_for_edge(nodes[i], nodes[i]->outgoing_links[j]);
459                         output_dot_edge(fp, from_node_id, to_node_id, labels);
460                 }
461
462                 if (nodes[i]->outgoing_links.empty() && !nodes[i]->disabled) {
463                         // Output node.
464                         vector<string> labels = get_labels_for_edge(nodes[i], NULL);
465                         output_dot_edge(fp, from_node_id, "output", labels);
466                 }
467         }
468         fprintf(fp, "}\n");
469
470         fclose(fp);
471 }
472
473 vector<string> EffectChain::get_labels_for_edge(const Node *from, const Node *to)
474 {
475         vector<string> labels;
476
477         if (to != NULL && to->effect->needs_texture_bounce()) {
478                 labels.push_back("needs_bounce");
479         }
480         if (from->effect->changes_output_size()) {
481                 labels.push_back("resize");
482         }
483
484         switch (from->output_color_space) {
485         case COLORSPACE_INVALID:
486                 labels.push_back("spc[invalid]");
487                 break;
488         case COLORSPACE_REC_601_525:
489                 labels.push_back("spc[rec601-525]");
490                 break;
491         case COLORSPACE_REC_601_625:
492                 labels.push_back("spc[rec601-625]");
493                 break;
494         default:
495                 break;
496         }
497
498         switch (from->output_gamma_curve) {
499         case GAMMA_INVALID:
500                 labels.push_back("gamma[invalid]");
501                 break;
502         case GAMMA_sRGB:
503                 labels.push_back("gamma[sRGB]");
504                 break;
505         case GAMMA_REC_601:  // and GAMMA_REC_709
506                 labels.push_back("gamma[rec601/709]");
507                 break;
508         default:
509                 break;
510         }
511
512         switch (from->output_alpha_type) {
513         case ALPHA_INVALID:
514                 labels.push_back("alpha[invalid]");
515                 break;
516         case ALPHA_BLANK:
517                 labels.push_back("alpha[blank]");
518                 break;
519         case ALPHA_POSTMULTIPLIED:
520                 labels.push_back("alpha[postmult]");
521                 break;
522         default:
523                 break;
524         }
525
526         return labels;
527 }
528
529 void EffectChain::output_dot_edge(FILE *fp,
530                                   const string &from_node_id,
531                                   const string &to_node_id,
532                                   const vector<string> &labels)
533 {
534         if (labels.empty()) {
535                 fprintf(fp, "  %s -> %s;\n", from_node_id.c_str(), to_node_id.c_str());
536         } else {
537                 string label = labels[0];
538                 for (unsigned k = 1; k < labels.size(); ++k) {
539                         label += ", " + labels[k];
540                 }
541                 fprintf(fp, "  %s -> %s [label=\"%s\"];\n", from_node_id.c_str(), to_node_id.c_str(), label.c_str());
542         }
543 }
544
545 void EffectChain::size_rectangle_to_fit(unsigned width, unsigned height, unsigned *output_width, unsigned *output_height)
546 {
547         unsigned scaled_width, scaled_height;
548
549         if (float(width) * aspect_denom >= float(height) * aspect_nom) {
550                 // Same aspect, or W/H > aspect (image is wider than the frame).
551                 // In either case, keep width, and adjust height.
552                 scaled_width = width;
553                 scaled_height = lrintf(width * aspect_denom / aspect_nom);
554         } else {
555                 // W/H < aspect (image is taller than the frame), so keep height,
556                 // and adjust width.
557                 scaled_width = lrintf(height * aspect_nom / aspect_denom);
558                 scaled_height = height;
559         }
560
561         // We should be consistently larger or smaller then the existing choice,
562         // since we have the same aspect.
563         assert(!(scaled_width < *output_width && scaled_height > *output_height));
564         assert(!(scaled_height < *output_height && scaled_width > *output_width));
565
566         if (scaled_width >= *output_width && scaled_height >= *output_height) {
567                 *output_width = scaled_width;
568                 *output_height = scaled_height;
569         }
570 }
571
572 // Propagate input texture sizes throughout, and inform effects downstream.
573 // (Like a lot of other code, we depend on effects being in topological order.)
574 void EffectChain::inform_input_sizes(Phase *phase)
575 {
576         // All effects that have a defined size (inputs and RTT inputs)
577         // get that. Reset all others.
578         for (unsigned i = 0; i < phase->effects.size(); ++i) {
579                 Node *node = phase->effects[i];
580                 if (node->effect->num_inputs() == 0) {
581                         Input *input = static_cast<Input *>(node->effect);
582                         node->output_width = input->get_width();
583                         node->output_height = input->get_height();
584                         assert(node->output_width != 0);
585                         assert(node->output_height != 0);
586                 } else {
587                         node->output_width = node->output_height = 0;
588                 }
589         }
590         for (unsigned i = 0; i < phase->inputs.size(); ++i) {
591                 Phase *input = phase->inputs[i];
592                 input->output_node->output_width = input->virtual_output_width;
593                 input->output_node->output_height = input->virtual_output_height;
594                 assert(input->output_node->output_width != 0);
595                 assert(input->output_node->output_height != 0);
596         }
597
598         // Now propagate from the inputs towards the end, and inform as we go.
599         // The rules are simple:
600         //
601         //   1. Don't touch effects that already have given sizes (ie., inputs).
602         //   2. If all of your inputs have the same size, that will be your output size.
603         //   3. Otherwise, your output size is 0x0.
604         for (unsigned i = 0; i < phase->effects.size(); ++i) {
605                 Node *node = phase->effects[i];
606                 if (node->effect->num_inputs() == 0) {
607                         continue;
608                 }
609                 unsigned this_output_width = 0;
610                 unsigned this_output_height = 0;
611                 for (unsigned j = 0; j < node->incoming_links.size(); ++j) {
612                         Node *input = node->incoming_links[j];
613                         node->effect->inform_input_size(j, input->output_width, input->output_height);
614                         if (j == 0) {
615                                 this_output_width = input->output_width;
616                                 this_output_height = input->output_height;
617                         } else if (input->output_width != this_output_width || input->output_height != this_output_height) {
618                                 // Inputs disagree.
619                                 this_output_width = 0;
620                                 this_output_height = 0;
621                         }
622                 }
623                 node->output_width = this_output_width;
624                 node->output_height = this_output_height;
625         }
626 }
627
628 // Note: You should call inform_input_sizes() before this, as the last effect's
629 // desired output size might change based on the inputs.
630 void EffectChain::find_output_size(Phase *phase)
631 {
632         Node *output_node = phase->effects.back();
633
634         // If the last effect explicitly sets an output size, use that.
635         if (output_node->effect->changes_output_size()) {
636                 output_node->effect->get_output_size(&phase->output_width, &phase->output_height,
637                                                      &phase->virtual_output_width, &phase->virtual_output_height);
638                 return;
639         }
640
641         // If all effects have the same size, use that.
642         unsigned output_width = 0, output_height = 0;
643         bool all_inputs_same_size = true;
644
645         for (unsigned i = 0; i < phase->inputs.size(); ++i) {
646                 Phase *input = phase->inputs[i];
647                 assert(input->output_width != 0);
648                 assert(input->output_height != 0);
649                 if (output_width == 0 && output_height == 0) {
650                         output_width = input->virtual_output_width;
651                         output_height = input->virtual_output_height;
652                 } else if (output_width != input->virtual_output_width ||
653                            output_height != input->virtual_output_height) {
654                         all_inputs_same_size = false;
655                 }
656         }
657         for (unsigned i = 0; i < phase->effects.size(); ++i) {
658                 Effect *effect = phase->effects[i]->effect;
659                 if (effect->num_inputs() != 0) {
660                         continue;
661                 }
662
663                 Input *input = static_cast<Input *>(effect);
664                 if (output_width == 0 && output_height == 0) {
665                         output_width = input->get_width();
666                         output_height = input->get_height();
667                 } else if (output_width != input->get_width() ||
668                            output_height != input->get_height()) {
669                         all_inputs_same_size = false;
670                 }
671         }
672
673         if (all_inputs_same_size) {
674                 assert(output_width != 0);
675                 assert(output_height != 0);
676                 phase->virtual_output_width = phase->output_width = output_width;
677                 phase->virtual_output_height = phase->output_height = output_height;
678                 return;
679         }
680
681         // If not, fit all the inputs into the current aspect, and select the largest one. 
682         output_width = 0;
683         output_height = 0;
684         for (unsigned i = 0; i < phase->inputs.size(); ++i) {
685                 Phase *input = phase->inputs[i];
686                 assert(input->output_width != 0);
687                 assert(input->output_height != 0);
688                 size_rectangle_to_fit(input->output_width, input->output_height, &output_width, &output_height);
689         }
690         for (unsigned i = 0; i < phase->effects.size(); ++i) {
691                 Effect *effect = phase->effects[i]->effect;
692                 if (effect->num_inputs() != 0) {
693                         continue;
694                 }
695
696                 Input *input = static_cast<Input *>(effect);
697                 size_rectangle_to_fit(input->get_width(), input->get_height(), &output_width, &output_height);
698         }
699         assert(output_width != 0);
700         assert(output_height != 0);
701         phase->virtual_output_width = phase->output_width = output_width;
702         phase->virtual_output_height = phase->output_height = output_height;
703 }
704
705 void EffectChain::sort_all_nodes_topologically()
706 {
707         nodes = topological_sort(nodes);
708 }
709
710 vector<Node *> EffectChain::topological_sort(const vector<Node *> &nodes)
711 {
712         set<Node *> nodes_left_to_visit(nodes.begin(), nodes.end());
713         vector<Node *> sorted_list;
714         for (unsigned i = 0; i < nodes.size(); ++i) {
715                 topological_sort_visit_node(nodes[i], &nodes_left_to_visit, &sorted_list);
716         }
717         reverse(sorted_list.begin(), sorted_list.end());
718         return sorted_list;
719 }
720
721 void EffectChain::topological_sort_visit_node(Node *node, set<Node *> *nodes_left_to_visit, vector<Node *> *sorted_list)
722 {
723         if (nodes_left_to_visit->count(node) == 0) {
724                 return;
725         }
726         nodes_left_to_visit->erase(node);
727         for (unsigned i = 0; i < node->outgoing_links.size(); ++i) {
728                 topological_sort_visit_node(node->outgoing_links[i], nodes_left_to_visit, sorted_list);
729         }
730         sorted_list->push_back(node);
731 }
732
733 void EffectChain::find_color_spaces_for_inputs()
734 {
735         for (unsigned i = 0; i < nodes.size(); ++i) {
736                 Node *node = nodes[i];
737                 if (node->disabled) {
738                         continue;
739                 }
740                 if (node->incoming_links.size() == 0) {
741                         Input *input = static_cast<Input *>(node->effect);
742                         node->output_color_space = input->get_color_space();
743                         node->output_gamma_curve = input->get_gamma_curve();
744
745                         Effect::AlphaHandling alpha_handling = input->alpha_handling();
746                         switch (alpha_handling) {
747                         case Effect::OUTPUT_BLANK_ALPHA:
748                                 node->output_alpha_type = ALPHA_BLANK;
749                                 break;
750                         case Effect::INPUT_AND_OUTPUT_PREMULTIPLIED_ALPHA:
751                                 node->output_alpha_type = ALPHA_PREMULTIPLIED;
752                                 break;
753                         case Effect::OUTPUT_POSTMULTIPLIED_ALPHA:
754                                 node->output_alpha_type = ALPHA_POSTMULTIPLIED;
755                                 break;
756                         case Effect::INPUT_PREMULTIPLIED_ALPHA_KEEP_BLANK:
757                         case Effect::DONT_CARE_ALPHA_TYPE:
758                         default:
759                                 assert(false);
760                         }
761
762                         if (node->output_alpha_type == ALPHA_PREMULTIPLIED) {
763                                 assert(node->output_gamma_curve == GAMMA_LINEAR);
764                         }
765                 }
766         }
767 }
768
769 // Propagate gamma and color space information as far as we can in the graph.
770 // The rules are simple: Anything where all the inputs agree, get that as
771 // output as well. Anything else keeps having *_INVALID.
772 void EffectChain::propagate_gamma_and_color_space()
773 {
774         // We depend on going through the nodes in order.
775         sort_all_nodes_topologically();
776
777         for (unsigned i = 0; i < nodes.size(); ++i) {
778                 Node *node = nodes[i];
779                 if (node->disabled) {
780                         continue;
781                 }
782                 assert(node->incoming_links.size() == node->effect->num_inputs());
783                 if (node->incoming_links.size() == 0) {
784                         assert(node->output_color_space != COLORSPACE_INVALID);
785                         assert(node->output_gamma_curve != GAMMA_INVALID);
786                         continue;
787                 }
788
789                 Colorspace color_space = node->incoming_links[0]->output_color_space;
790                 GammaCurve gamma_curve = node->incoming_links[0]->output_gamma_curve;
791                 for (unsigned j = 1; j < node->incoming_links.size(); ++j) {
792                         if (node->incoming_links[j]->output_color_space != color_space) {
793                                 color_space = COLORSPACE_INVALID;
794                         }
795                         if (node->incoming_links[j]->output_gamma_curve != gamma_curve) {
796                                 gamma_curve = GAMMA_INVALID;
797                         }
798                 }
799
800                 // The conversion effects already have their outputs set correctly,
801                 // so leave them alone.
802                 if (node->effect->effect_type_id() != "ColorspaceConversionEffect") {
803                         node->output_color_space = color_space;
804                 }               
805                 if (node->effect->effect_type_id() != "GammaCompressionEffect" &&
806                     node->effect->effect_type_id() != "GammaExpansionEffect") {
807                         node->output_gamma_curve = gamma_curve;
808                 }               
809         }
810 }
811
812 // Propagate alpha information as far as we can in the graph.
813 // Similar to propagate_gamma_and_color_space().
814 void EffectChain::propagate_alpha()
815 {
816         // We depend on going through the nodes in order.
817         sort_all_nodes_topologically();
818
819         for (unsigned i = 0; i < nodes.size(); ++i) {
820                 Node *node = nodes[i];
821                 if (node->disabled) {
822                         continue;
823                 }
824                 assert(node->incoming_links.size() == node->effect->num_inputs());
825                 if (node->incoming_links.size() == 0) {
826                         assert(node->output_alpha_type != ALPHA_INVALID);
827                         continue;
828                 }
829
830                 // The alpha multiplication/division effects are special cases.
831                 if (node->effect->effect_type_id() == "AlphaMultiplicationEffect") {
832                         assert(node->incoming_links.size() == 1);
833                         assert(node->incoming_links[0]->output_alpha_type == ALPHA_POSTMULTIPLIED);
834                         node->output_alpha_type = ALPHA_PREMULTIPLIED;
835                         continue;
836                 }
837                 if (node->effect->effect_type_id() == "AlphaDivisionEffect") {
838                         assert(node->incoming_links.size() == 1);
839                         assert(node->incoming_links[0]->output_alpha_type == ALPHA_PREMULTIPLIED);
840                         node->output_alpha_type = ALPHA_POSTMULTIPLIED;
841                         continue;
842                 }
843
844                 // GammaCompressionEffect and GammaExpansionEffect are also a special case,
845                 // because they are the only one that _need_ postmultiplied alpha.
846                 if (node->effect->effect_type_id() == "GammaCompressionEffect" ||
847                     node->effect->effect_type_id() == "GammaExpansionEffect") {
848                         assert(node->incoming_links.size() == 1);
849                         if (node->incoming_links[0]->output_alpha_type == ALPHA_BLANK) {
850                                 node->output_alpha_type = ALPHA_BLANK;
851                         } else if (node->incoming_links[0]->output_alpha_type == ALPHA_POSTMULTIPLIED) {
852                                 node->output_alpha_type = ALPHA_POSTMULTIPLIED;
853                         } else {
854                                 node->output_alpha_type = ALPHA_INVALID;
855                         }
856                         continue;
857                 }
858
859                 // Only inputs can have unconditional alpha output (OUTPUT_BLANK_ALPHA
860                 // or OUTPUT_POSTMULTIPLIED_ALPHA), and they have already been
861                 // taken care of above. Rationale: Even if you could imagine
862                 // e.g. an effect that took in an image and set alpha=1.0
863                 // unconditionally, it wouldn't make any sense to have it as
864                 // e.g. OUTPUT_BLANK_ALPHA, since it wouldn't know whether it
865                 // got its input pre- or postmultiplied, so it wouldn't know
866                 // whether to divide away the old alpha or not.
867                 Effect::AlphaHandling alpha_handling = node->effect->alpha_handling();
868                 assert(alpha_handling == Effect::INPUT_AND_OUTPUT_PREMULTIPLIED_ALPHA ||
869                        alpha_handling == Effect::INPUT_PREMULTIPLIED_ALPHA_KEEP_BLANK ||
870                        alpha_handling == Effect::DONT_CARE_ALPHA_TYPE);
871
872                 // If the node has multiple inputs, check that they are all valid and
873                 // the same.
874                 bool any_invalid = false;
875                 bool any_premultiplied = false;
876                 bool any_postmultiplied = false;
877
878                 for (unsigned j = 0; j < node->incoming_links.size(); ++j) {
879                         switch (node->incoming_links[j]->output_alpha_type) {
880                         case ALPHA_INVALID:
881                                 any_invalid = true;
882                                 break;
883                         case ALPHA_BLANK:
884                                 // Blank is good as both pre- and postmultiplied alpha,
885                                 // so just ignore it.
886                                 break;
887                         case ALPHA_PREMULTIPLIED:
888                                 any_premultiplied = true;
889                                 break;
890                         case ALPHA_POSTMULTIPLIED:
891                                 any_postmultiplied = true;
892                                 break;
893                         default:
894                                 assert(false);
895                         }
896                 }
897
898                 if (any_invalid) {
899                         node->output_alpha_type = ALPHA_INVALID;
900                         continue;
901                 }
902
903                 // Inputs must be of the same type.
904                 if (any_premultiplied && any_postmultiplied) {
905                         node->output_alpha_type = ALPHA_INVALID;
906                         continue;
907                 }
908
909                 if (alpha_handling == Effect::INPUT_AND_OUTPUT_PREMULTIPLIED_ALPHA ||
910                     alpha_handling == Effect::INPUT_PREMULTIPLIED_ALPHA_KEEP_BLANK) {
911                         // If the effect has asked for premultiplied alpha, check that it has got it.
912                         if (any_postmultiplied) {
913                                 node->output_alpha_type = ALPHA_INVALID;
914                         } else if (!any_premultiplied &&
915                                    alpha_handling == Effect::INPUT_PREMULTIPLIED_ALPHA_KEEP_BLANK) {
916                                 // Blank input alpha, and the effect preserves blank alpha.
917                                 node->output_alpha_type = ALPHA_BLANK;
918                         } else {
919                                 node->output_alpha_type = ALPHA_PREMULTIPLIED;
920                         }
921                 } else {
922                         // OK, all inputs are the same, and this effect is not going
923                         // to change it.
924                         assert(alpha_handling == Effect::DONT_CARE_ALPHA_TYPE);
925                         if (any_premultiplied) {
926                                 node->output_alpha_type = ALPHA_PREMULTIPLIED;
927                         } else if (any_postmultiplied) {
928                                 node->output_alpha_type = ALPHA_POSTMULTIPLIED;
929                         } else {
930                                 node->output_alpha_type = ALPHA_BLANK;
931                         }
932                 }
933         }
934 }
935
936 bool EffectChain::node_needs_colorspace_fix(Node *node)
937 {
938         if (node->disabled) {
939                 return false;
940         }
941         if (node->effect->num_inputs() == 0) {
942                 return false;
943         }
944
945         // propagate_gamma_and_color_space() has already set our output
946         // to COLORSPACE_INVALID if the inputs differ, so we can rely on that.
947         if (node->output_color_space == COLORSPACE_INVALID) {
948                 return true;
949         }
950         return (node->effect->needs_srgb_primaries() && node->output_color_space != COLORSPACE_sRGB);
951 }
952
953 // Fix up color spaces so that there are no COLORSPACE_INVALID nodes left in
954 // the graph. Our strategy is not always optimal, but quite simple:
955 // Find an effect that's as early as possible where the inputs are of
956 // unacceptable colorspaces (that is, either different, or, if the effect only
957 // wants sRGB, not sRGB.) Add appropriate conversions on all its inputs,
958 // propagate the information anew, and repeat until there are no more such
959 // effects.
960 void EffectChain::fix_internal_color_spaces()
961 {
962         unsigned colorspace_propagation_pass = 0;
963         bool found_any;
964         do {
965                 found_any = false;
966                 for (unsigned i = 0; i < nodes.size(); ++i) {
967                         Node *node = nodes[i];
968                         if (!node_needs_colorspace_fix(node)) {
969                                 continue;
970                         }
971
972                         // Go through each input that is not sRGB, and insert
973                         // a colorspace conversion after it.
974                         for (unsigned j = 0; j < node->incoming_links.size(); ++j) {
975                                 Node *input = node->incoming_links[j];
976                                 assert(input->output_color_space != COLORSPACE_INVALID);
977                                 if (input->output_color_space == COLORSPACE_sRGB) {
978                                         continue;
979                                 }
980                                 Node *conversion = add_node(new ColorspaceConversionEffect());
981                                 CHECK(conversion->effect->set_int("source_space", input->output_color_space));
982                                 CHECK(conversion->effect->set_int("destination_space", COLORSPACE_sRGB));
983                                 conversion->output_color_space = COLORSPACE_sRGB;
984                                 replace_sender(input, conversion);
985                                 connect_nodes(input, conversion);
986                         }
987
988                         // Re-sort topologically, and propagate the new information.
989                         propagate_gamma_and_color_space();
990                         
991                         found_any = true;
992                         break;
993                 }
994         
995                 char filename[256];
996                 sprintf(filename, "step5-colorspacefix-iter%u.dot", ++colorspace_propagation_pass);
997                 output_dot(filename);
998                 assert(colorspace_propagation_pass < 100);
999         } while (found_any);
1000
1001         for (unsigned i = 0; i < nodes.size(); ++i) {
1002                 Node *node = nodes[i];
1003                 if (node->disabled) {
1004                         continue;
1005                 }
1006                 assert(node->output_color_space != COLORSPACE_INVALID);
1007         }
1008 }
1009
1010 bool EffectChain::node_needs_alpha_fix(Node *node)
1011 {
1012         if (node->disabled) {
1013                 return false;
1014         }
1015
1016         // propagate_alpha() has already set our output to ALPHA_INVALID if the
1017         // inputs differ or we are otherwise in mismatch, so we can rely on that.
1018         return (node->output_alpha_type == ALPHA_INVALID);
1019 }
1020
1021 // Fix up alpha so that there are no ALPHA_INVALID nodes left in
1022 // the graph. Similar to fix_internal_color_spaces().
1023 void EffectChain::fix_internal_alpha(unsigned step)
1024 {
1025         unsigned alpha_propagation_pass = 0;
1026         bool found_any;
1027         do {
1028                 found_any = false;
1029                 for (unsigned i = 0; i < nodes.size(); ++i) {
1030                         Node *node = nodes[i];
1031                         if (!node_needs_alpha_fix(node)) {
1032                                 continue;
1033                         }
1034
1035                         // If we need to fix up GammaExpansionEffect, then clearly something
1036                         // is wrong, since the combination of premultiplied alpha and nonlinear inputs
1037                         // is meaningless.
1038                         assert(node->effect->effect_type_id() != "GammaExpansionEffect");
1039
1040                         AlphaType desired_type = ALPHA_PREMULTIPLIED;
1041
1042                         // GammaCompressionEffect is special; it needs postmultiplied alpha.
1043                         if (node->effect->effect_type_id() == "GammaCompressionEffect") {
1044                                 assert(node->incoming_links.size() == 1);
1045                                 assert(node->incoming_links[0]->output_alpha_type == ALPHA_PREMULTIPLIED);
1046                                 desired_type = ALPHA_POSTMULTIPLIED;
1047                         }
1048
1049                         // Go through each input that is not premultiplied alpha, and insert
1050                         // a conversion before it.
1051                         for (unsigned j = 0; j < node->incoming_links.size(); ++j) {
1052                                 Node *input = node->incoming_links[j];
1053                                 assert(input->output_alpha_type != ALPHA_INVALID);
1054                                 if (input->output_alpha_type == desired_type ||
1055                                     input->output_alpha_type == ALPHA_BLANK) {
1056                                         continue;
1057                                 }
1058                                 Node *conversion;
1059                                 if (desired_type == ALPHA_PREMULTIPLIED) {
1060                                         conversion = add_node(new AlphaMultiplicationEffect());
1061                                 } else {
1062                                         conversion = add_node(new AlphaDivisionEffect());
1063                                 }
1064                                 conversion->output_alpha_type = desired_type;
1065                                 replace_sender(input, conversion);
1066                                 connect_nodes(input, conversion);
1067                         }
1068
1069                         // Re-sort topologically, and propagate the new information.
1070                         propagate_gamma_and_color_space();
1071                         propagate_alpha();
1072                         
1073                         found_any = true;
1074                         break;
1075                 }
1076         
1077                 char filename[256];
1078                 sprintf(filename, "step%u-alphafix-iter%u.dot", step, ++alpha_propagation_pass);
1079                 output_dot(filename);
1080                 assert(alpha_propagation_pass < 100);
1081         } while (found_any);
1082
1083         for (unsigned i = 0; i < nodes.size(); ++i) {
1084                 Node *node = nodes[i];
1085                 if (node->disabled) {
1086                         continue;
1087                 }
1088                 assert(node->output_alpha_type != ALPHA_INVALID);
1089         }
1090 }
1091
1092 // Make so that the output is in the desired color space.
1093 void EffectChain::fix_output_color_space()
1094 {
1095         Node *output = find_output_node();
1096         if (output->output_color_space != output_format.color_space) {
1097                 Node *conversion = add_node(new ColorspaceConversionEffect());
1098                 CHECK(conversion->effect->set_int("source_space", output->output_color_space));
1099                 CHECK(conversion->effect->set_int("destination_space", output_format.color_space));
1100                 conversion->output_color_space = output_format.color_space;
1101                 connect_nodes(output, conversion);
1102                 propagate_alpha();
1103                 propagate_gamma_and_color_space();
1104         }
1105 }
1106
1107 // Make so that the output is in the desired pre-/postmultiplication alpha state.
1108 void EffectChain::fix_output_alpha()
1109 {
1110         Node *output = find_output_node();
1111         assert(output->output_alpha_type != ALPHA_INVALID);
1112         if (output->output_alpha_type == ALPHA_BLANK) {
1113                 // No alpha output, so we don't care.
1114                 return;
1115         }
1116         if (output->output_alpha_type == ALPHA_PREMULTIPLIED &&
1117             output_alpha_format == OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED) {
1118                 Node *conversion = add_node(new AlphaDivisionEffect());
1119                 connect_nodes(output, conversion);
1120                 propagate_alpha();
1121                 propagate_gamma_and_color_space();
1122         }
1123         if (output->output_alpha_type == ALPHA_POSTMULTIPLIED &&
1124             output_alpha_format == OUTPUT_ALPHA_FORMAT_PREMULTIPLIED) {
1125                 Node *conversion = add_node(new AlphaMultiplicationEffect());
1126                 connect_nodes(output, conversion);
1127                 propagate_alpha();
1128                 propagate_gamma_and_color_space();
1129         }
1130 }
1131
1132 bool EffectChain::node_needs_gamma_fix(Node *node)
1133 {
1134         if (node->disabled) {
1135                 return false;
1136         }
1137
1138         // Small hack since the output is not an explicit node:
1139         // If we are the last node and our output is in the wrong
1140         // space compared to EffectChain's output, we need to fix it.
1141         // This will only take us to linear, but fix_output_gamma()
1142         // will come and take us to the desired output gamma
1143         // if it is needed.
1144         //
1145         // This needs to be before everything else, since it could
1146         // even apply to inputs (if they are the only effect).
1147         if (node->outgoing_links.empty() &&
1148             node->output_gamma_curve != output_format.gamma_curve &&
1149             node->output_gamma_curve != GAMMA_LINEAR) {
1150                 return true;
1151         }
1152
1153         if (node->effect->num_inputs() == 0) {
1154                 return false;
1155         }
1156
1157         // propagate_gamma_and_color_space() has already set our output
1158         // to GAMMA_INVALID if the inputs differ, so we can rely on that,
1159         // except for GammaCompressionEffect.
1160         if (node->output_gamma_curve == GAMMA_INVALID) {
1161                 return true;
1162         }
1163         if (node->effect->effect_type_id() == "GammaCompressionEffect") {
1164                 assert(node->incoming_links.size() == 1);
1165                 return node->incoming_links[0]->output_gamma_curve != GAMMA_LINEAR;
1166         }
1167
1168         return (node->effect->needs_linear_light() && node->output_gamma_curve != GAMMA_LINEAR);
1169 }
1170
1171 // Very similar to fix_internal_color_spaces(), but for gamma.
1172 // There is one difference, though; before we start adding conversion nodes,
1173 // we see if we can get anything out of asking the sources to deliver
1174 // linear gamma directly. fix_internal_gamma_by_asking_inputs()
1175 // does that part, while fix_internal_gamma_by_inserting_nodes()
1176 // inserts nodes as needed afterwards.
1177 void EffectChain::fix_internal_gamma_by_asking_inputs(unsigned step)
1178 {
1179         unsigned gamma_propagation_pass = 0;
1180         bool found_any;
1181         do {
1182                 found_any = false;
1183                 for (unsigned i = 0; i < nodes.size(); ++i) {
1184                         Node *node = nodes[i];
1185                         if (!node_needs_gamma_fix(node)) {
1186                                 continue;
1187                         }
1188
1189                         // See if all inputs can give us linear gamma. If not, leave it.
1190                         vector<Node *> nonlinear_inputs;
1191                         find_all_nonlinear_inputs(node, &nonlinear_inputs);
1192                         assert(!nonlinear_inputs.empty());
1193
1194                         bool all_ok = true;
1195                         for (unsigned i = 0; i < nonlinear_inputs.size(); ++i) {
1196                                 Input *input = static_cast<Input *>(nonlinear_inputs[i]->effect);
1197                                 all_ok &= input->can_output_linear_gamma();
1198                         }
1199
1200                         if (!all_ok) {
1201                                 continue;
1202                         }
1203
1204                         for (unsigned i = 0; i < nonlinear_inputs.size(); ++i) {
1205                                 CHECK(nonlinear_inputs[i]->effect->set_int("output_linear_gamma", 1));
1206                                 nonlinear_inputs[i]->output_gamma_curve = GAMMA_LINEAR;
1207                         }
1208
1209                         // Re-sort topologically, and propagate the new information.
1210                         propagate_gamma_and_color_space();
1211                         
1212                         found_any = true;
1213                         break;
1214                 }
1215         
1216                 char filename[256];
1217                 sprintf(filename, "step%u-gammafix-iter%u.dot", step, ++gamma_propagation_pass);
1218                 output_dot(filename);
1219                 assert(gamma_propagation_pass < 100);
1220         } while (found_any);
1221 }
1222
1223 void EffectChain::fix_internal_gamma_by_inserting_nodes(unsigned step)
1224 {
1225         unsigned gamma_propagation_pass = 0;
1226         bool found_any;
1227         do {
1228                 found_any = false;
1229                 for (unsigned i = 0; i < nodes.size(); ++i) {
1230                         Node *node = nodes[i];
1231                         if (!node_needs_gamma_fix(node)) {
1232                                 continue;
1233                         }
1234
1235                         // Special case: We could be an input and still be asked to
1236                         // fix our gamma; if so, we should be the only node
1237                         // (as node_needs_gamma_fix() would only return true in
1238                         // for an input in that case). That means we should insert
1239                         // a conversion node _after_ ourselves.
1240                         if (node->incoming_links.empty()) {
1241                                 assert(node->outgoing_links.empty());
1242                                 Node *conversion = add_node(new GammaExpansionEffect());
1243                                 CHECK(conversion->effect->set_int("source_curve", node->output_gamma_curve));
1244                                 conversion->output_gamma_curve = GAMMA_LINEAR;
1245                                 connect_nodes(node, conversion);
1246                         }
1247
1248                         // If not, go through each input that is not linear gamma,
1249                         // and insert a gamma conversion after it.
1250                         for (unsigned j = 0; j < node->incoming_links.size(); ++j) {
1251                                 Node *input = node->incoming_links[j];
1252                                 assert(input->output_gamma_curve != GAMMA_INVALID);
1253                                 if (input->output_gamma_curve == GAMMA_LINEAR) {
1254                                         continue;
1255                                 }
1256                                 Node *conversion = add_node(new GammaExpansionEffect());
1257                                 CHECK(conversion->effect->set_int("source_curve", input->output_gamma_curve));
1258                                 conversion->output_gamma_curve = GAMMA_LINEAR;
1259                                 replace_sender(input, conversion);
1260                                 connect_nodes(input, conversion);
1261                         }
1262
1263                         // Re-sort topologically, and propagate the new information.
1264                         propagate_alpha();
1265                         propagate_gamma_and_color_space();
1266                         
1267                         found_any = true;
1268                         break;
1269                 }
1270         
1271                 char filename[256];
1272                 sprintf(filename, "step%u-gammafix-iter%u.dot", step, ++gamma_propagation_pass);
1273                 output_dot(filename);
1274                 assert(gamma_propagation_pass < 100);
1275         } while (found_any);
1276
1277         for (unsigned i = 0; i < nodes.size(); ++i) {
1278                 Node *node = nodes[i];
1279                 if (node->disabled) {
1280                         continue;
1281                 }
1282                 assert(node->output_gamma_curve != GAMMA_INVALID);
1283         }
1284 }
1285
1286 // Make so that the output is in the desired gamma.
1287 // Note that this assumes linear input gamma, so it might create the need
1288 // for another pass of fix_internal_gamma().
1289 void EffectChain::fix_output_gamma()
1290 {
1291         Node *output = find_output_node();
1292         if (output->output_gamma_curve != output_format.gamma_curve) {
1293                 Node *conversion = add_node(new GammaCompressionEffect());
1294                 CHECK(conversion->effect->set_int("destination_curve", output_format.gamma_curve));
1295                 conversion->output_gamma_curve = output_format.gamma_curve;
1296                 connect_nodes(output, conversion);
1297         }
1298 }
1299         
1300 // If the user has requested dither, add a DitherEffect right at the end
1301 // (after GammaCompressionEffect etc.). This needs to be done after everything else,
1302 // since dither is about the only effect that can _not_ be done in linear space.
1303 void EffectChain::add_dither_if_needed()
1304 {
1305         if (num_dither_bits == 0) {
1306                 return;
1307         }
1308         Node *output = find_output_node();
1309         Node *dither = add_node(new DitherEffect());
1310         CHECK(dither->effect->set_int("num_bits", num_dither_bits));
1311         connect_nodes(output, dither);
1312
1313         dither_effect = dither->effect;
1314 }
1315
1316 // Find the output node. This is, simply, one that has no outgoing links.
1317 // If there are multiple ones, the graph is malformed (we do not support
1318 // multiple outputs right now).
1319 Node *EffectChain::find_output_node()
1320 {
1321         vector<Node *> output_nodes;
1322         for (unsigned i = 0; i < nodes.size(); ++i) {
1323                 Node *node = nodes[i];
1324                 if (node->disabled) {
1325                         continue;
1326                 }
1327                 if (node->outgoing_links.empty()) {
1328                         output_nodes.push_back(node);
1329                 }
1330         }
1331         assert(output_nodes.size() == 1);
1332         return output_nodes[0];
1333 }
1334
1335 void EffectChain::finalize()
1336 {
1337         // Save the current locale, and set it to C, so that we can output decimal
1338         // numbers with printf and be sure to get them in the format mandated by GLSL.
1339         char *saved_locale = setlocale(LC_NUMERIC, "C");
1340
1341         // Output the graph as it is before we do any conversions on it.
1342         output_dot("step0-start.dot");
1343
1344         // Give each effect in turn a chance to rewrite its own part of the graph.
1345         // Note that if more effects are added as part of this, they will be
1346         // picked up as part of the same for loop, since they are added at the end.
1347         for (unsigned i = 0; i < nodes.size(); ++i) {
1348                 nodes[i]->effect->rewrite_graph(this, nodes[i]);
1349         }
1350         output_dot("step1-rewritten.dot");
1351
1352         find_color_spaces_for_inputs();
1353         output_dot("step2-input-colorspace.dot");
1354
1355         propagate_alpha();
1356         output_dot("step3-propagated-alpha.dot");
1357
1358         propagate_gamma_and_color_space();
1359         output_dot("step4-propagated-all.dot");
1360
1361         fix_internal_color_spaces();
1362         fix_internal_alpha(6);
1363         fix_output_color_space();
1364         output_dot("step7-output-colorspacefix.dot");
1365         fix_output_alpha();
1366         output_dot("step8-output-alphafix.dot");
1367
1368         // Note that we need to fix gamma after colorspace conversion,
1369         // because colorspace conversions might create needs for gamma conversions.
1370         // Also, we need to run an extra pass of fix_internal_gamma() after 
1371         // fixing the output gamma, as we only have conversions to/from linear,
1372         // and fix_internal_alpha() since GammaCompressionEffect needs
1373         // postmultiplied input.
1374         fix_internal_gamma_by_asking_inputs(9);
1375         fix_internal_gamma_by_inserting_nodes(10);
1376         fix_output_gamma();
1377         output_dot("step11-output-gammafix.dot");
1378         propagate_alpha();
1379         output_dot("step12-output-alpha-propagated.dot");
1380         fix_internal_alpha(13);
1381         output_dot("step14-output-alpha-fixed.dot");
1382         fix_internal_gamma_by_asking_inputs(15);
1383         fix_internal_gamma_by_inserting_nodes(16);
1384
1385         output_dot("step17-before-dither.dot");
1386
1387         add_dither_if_needed();
1388
1389         output_dot("step18-final.dot");
1390         
1391         // Construct all needed GLSL programs, starting at the output.
1392         // We need to keep track of which effects have already been computed,
1393         // as an effect with multiple users could otherwise be calculated
1394         // multiple times.
1395         map<Node *, Phase *> completed_effects;
1396         construct_phase(find_output_node(), &completed_effects);
1397
1398         output_dot("step19-split-to-phases.dot");
1399
1400         assert(phases[0]->inputs.empty());
1401         
1402         finalized = true;
1403         setlocale(LC_NUMERIC, saved_locale);
1404 }
1405
1406 void EffectChain::render_to_fbo(GLuint dest_fbo, unsigned width, unsigned height)
1407 {
1408         assert(finalized);
1409
1410         // Save original viewport.
1411         GLuint x = 0, y = 0;
1412         GLuint fbo = 0;
1413         void *context = get_gl_context_identifier();
1414
1415         if (width == 0 && height == 0) {
1416                 GLint viewport[4];
1417                 glGetIntegerv(GL_VIEWPORT, viewport);
1418                 x = viewport[0];
1419                 y = viewport[1];
1420                 width = viewport[2];
1421                 height = viewport[3];
1422         }
1423
1424         // Basic state.
1425         glDisable(GL_BLEND);
1426         check_error();
1427         glDisable(GL_DEPTH_TEST);
1428         check_error();
1429         glDepthMask(GL_FALSE);
1430         check_error();
1431
1432         if (phases.size() > 1) {
1433                 if (fbos.count(context) == 0) {
1434                         glGenFramebuffers(1, &fbo);
1435                         check_error();
1436                         fbos.insert(make_pair(context, fbo));
1437                 } else {
1438                         fbo = fbos[context];
1439                 }
1440                 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
1441                 check_error();
1442         }
1443
1444         set<Phase *> generated_mipmaps;
1445
1446         // We choose the simplest option of having one texture per output,
1447         // since otherwise this turns into an (albeit simple) register allocation problem.
1448         map<Phase *, GLuint> output_textures;
1449
1450         for (unsigned phase = 0; phase < phases.size(); ++phase) {
1451                 // Find a texture for this phase.
1452                 inform_input_sizes(phases[phase]);
1453                 if (phase != phases.size() - 1) {
1454                         find_output_size(phases[phase]);
1455
1456                         GLuint tex_num = resource_pool->create_2d_texture(GL_RGBA16F_ARB, phases[phase]->output_width, phases[phase]->output_height);
1457                         output_textures.insert(make_pair(phases[phase], tex_num));
1458                 }
1459
1460                 const GLuint glsl_program_num = phases[phase]->glsl_program_num;
1461                 check_error();
1462                 glUseProgram(glsl_program_num);
1463                 check_error();
1464
1465                 // Set up RTT inputs for this phase.
1466                 for (unsigned sampler = 0; sampler < phases[phase]->inputs.size(); ++sampler) {
1467                         glActiveTexture(GL_TEXTURE0 + sampler);
1468                         Phase *input = phases[phase]->inputs[sampler];
1469                         input->output_node->bound_sampler_num = sampler;
1470                         glBindTexture(GL_TEXTURE_2D, output_textures[input]);
1471                         check_error();
1472                         if (phases[phase]->input_needs_mipmaps) {
1473                                 if (generated_mipmaps.count(input) == 0) {
1474                                         glGenerateMipmap(GL_TEXTURE_2D);
1475                                         check_error();
1476                                         generated_mipmaps.insert(input);
1477                                 }
1478                                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
1479                                 check_error();
1480                         } else {
1481                                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1482                                 check_error();
1483                         }
1484                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1485                         check_error();
1486                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1487                         check_error();
1488
1489                         string texture_name = string("tex_") + phases[phase]->effect_ids[input->output_node];
1490                         glUniform1i(glGetUniformLocation(glsl_program_num, texture_name.c_str()), sampler);
1491                         check_error();
1492                 }
1493
1494                 // And now the output.
1495                 if (phase == phases.size() - 1) {
1496                         // Last phase goes to the output the user specified.
1497                         glBindFramebuffer(GL_FRAMEBUFFER, dest_fbo);
1498                         check_error();
1499                         GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
1500                         assert(status == GL_FRAMEBUFFER_COMPLETE);
1501                         glViewport(x, y, width, height);
1502                         if (dither_effect != NULL) {
1503                                 CHECK(dither_effect->set_int("output_width", width));
1504                                 CHECK(dither_effect->set_int("output_height", height));
1505                         }
1506                 } else {
1507                         glFramebufferTexture2D(
1508                                 GL_FRAMEBUFFER,
1509                                 GL_COLOR_ATTACHMENT0,
1510                                 GL_TEXTURE_2D,
1511                                 output_textures[phases[phase]],
1512                                 0);
1513                         check_error();
1514                         GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
1515                         assert(status == GL_FRAMEBUFFER_COMPLETE);
1516                         glViewport(0, 0, phases[phase]->output_width, phases[phase]->output_height);
1517                 }
1518
1519                 // Give the required parameters to all the effects.
1520                 unsigned sampler_num = phases[phase]->inputs.size();
1521                 for (unsigned i = 0; i < phases[phase]->effects.size(); ++i) {
1522                         Node *node = phases[phase]->effects[i];
1523                         unsigned old_sampler_num = sampler_num;
1524                         node->effect->set_gl_state(glsl_program_num, phases[phase]->effect_ids[node], &sampler_num);
1525                         check_error();
1526
1527                         if (node->effect->is_single_texture()) {
1528                                 assert(sampler_num - old_sampler_num == 1);
1529                                 node->bound_sampler_num = old_sampler_num;
1530                         } else {
1531                                 node->bound_sampler_num = -1;
1532                         }
1533                 }
1534
1535                 // Now draw!
1536                 float vertices[] = {
1537                         0.0f, 1.0f,
1538                         0.0f, 0.0f,
1539                         1.0f, 1.0f,
1540                         1.0f, 0.0f
1541                 };
1542
1543                 GLuint vao;
1544                 glGenVertexArrays(1, &vao);
1545                 check_error();
1546                 glBindVertexArray(vao);
1547                 check_error();
1548
1549                 GLuint position_vbo = fill_vertex_attribute(glsl_program_num, "position", 2, GL_FLOAT, sizeof(vertices), vertices);
1550                 GLuint texcoord_vbo = fill_vertex_attribute(glsl_program_num, "texcoord", 2, GL_FLOAT, sizeof(vertices), vertices);  // Same as vertices.
1551
1552                 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1553                 check_error();
1554
1555                 cleanup_vertex_attribute(glsl_program_num, "position", position_vbo);
1556                 cleanup_vertex_attribute(glsl_program_num, "texcoord", texcoord_vbo);
1557
1558                 glUseProgram(0);
1559                 check_error();
1560
1561                 for (unsigned i = 0; i < phases[phase]->effects.size(); ++i) {
1562                         Node *node = phases[phase]->effects[i];
1563                         node->effect->clear_gl_state();
1564                 }
1565
1566                 glDeleteVertexArrays(1, &vao);
1567                 check_error();
1568         }
1569
1570         for (map<Phase *, GLuint>::const_iterator texture_it = output_textures.begin();
1571              texture_it != output_textures.end();
1572              ++texture_it) {
1573                 resource_pool->release_2d_texture(texture_it->second);
1574         }
1575
1576         glBindFramebuffer(GL_FRAMEBUFFER, 0);
1577         check_error();
1578 }
1579
1580 }  // namespace movit