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