]> git.sesse.net Git - movit/blob - effect_chain.cpp
Do not output .dot and .frag debug files if NDEBUG is set.
[movit] / effect_chain.cpp
1 #define GL_GLEXT_PROTOTYPES 1
2
3 #include <stdio.h>
4 #include <math.h>
5 #include <string.h>
6 #include <assert.h>
7 #include <GL/glew.h>
8
9 #include <algorithm>
10 #include <set>
11 #include <stack>
12 #include <vector>
13
14 #include "util.h"
15 #include "effect_chain.h"
16 #include "gamma_expansion_effect.h"
17 #include "gamma_compression_effect.h"
18 #include "colorspace_conversion_effect.h"
19 #include "dither_effect.h"
20 #include "input.h"
21
22 EffectChain::EffectChain(float aspect_nom, float aspect_denom)
23         : aspect_nom(aspect_nom),
24           aspect_denom(aspect_denom),
25           dither_effect(NULL),
26           fbo(0),
27           num_dither_bits(0),
28           finalized(false) {}
29
30 EffectChain::~EffectChain()
31 {
32         for (unsigned i = 0; i < nodes.size(); ++i) {
33                 if (nodes[i]->output_texture != 0) {
34                         glDeleteTextures(1, &nodes[i]->output_texture);
35                 }
36                 delete nodes[i]->effect;
37                 delete nodes[i];
38         }
39         for (unsigned i = 0; i < phases.size(); ++i) {
40                 glDeleteProgram(phases[i]->glsl_program_num);
41                 glDeleteShader(phases[i]->vertex_shader);
42                 glDeleteShader(phases[i]->fragment_shader);
43                 delete phases[i];
44         }
45         if (fbo != 0) {
46                 glDeleteFramebuffers(1, &fbo);
47         }
48 }
49
50 Input *EffectChain::add_input(Input *input)
51 {
52         inputs.push_back(input);
53
54         Node *node = add_node(input);
55         node->output_color_space = input->get_color_space();
56         node->output_gamma_curve = input->get_gamma_curve();
57         return input;
58 }
59
60 void EffectChain::add_output(const ImageFormat &format)
61 {
62         output_format = format;
63 }
64
65 Node *EffectChain::add_node(Effect *effect)
66 {
67         char effect_id[256];
68         sprintf(effect_id, "eff%u", (unsigned)nodes.size());
69
70         Node *node = new Node;
71         node->effect = effect;
72         node->disabled = false;
73         node->effect_id = effect_id;
74         node->output_color_space = COLORSPACE_INVALID;
75         node->output_gamma_curve = GAMMA_INVALID;
76         node->output_texture = 0;
77
78         nodes.push_back(node);
79         node_map[effect] = node;
80         return node;
81 }
82
83 void EffectChain::connect_nodes(Node *sender, Node *receiver)
84 {
85         sender->outgoing_links.push_back(receiver);
86         receiver->incoming_links.push_back(sender);
87 }
88
89 void EffectChain::replace_receiver(Node *old_receiver, Node *new_receiver)
90 {
91         new_receiver->incoming_links = old_receiver->incoming_links;
92         old_receiver->incoming_links.clear();
93         
94         for (unsigned i = 0; i < new_receiver->incoming_links.size(); ++i) {
95                 Node *sender = new_receiver->incoming_links[i];
96                 for (unsigned j = 0; j < sender->outgoing_links.size(); ++j) {
97                         if (sender->outgoing_links[j] == old_receiver) {
98                                 sender->outgoing_links[j] = new_receiver;
99                         }
100                 }
101         }       
102 }
103
104 void EffectChain::replace_sender(Node *old_sender, Node *new_sender)
105 {
106         new_sender->outgoing_links = old_sender->outgoing_links;
107         old_sender->outgoing_links.clear();
108         
109         for (unsigned i = 0; i < new_sender->outgoing_links.size(); ++i) {
110                 Node *receiver = new_sender->outgoing_links[i];
111                 for (unsigned j = 0; j < receiver->incoming_links.size(); ++j) {
112                         if (receiver->incoming_links[j] == old_sender) {
113                                 receiver->incoming_links[j] = new_sender;
114                         }
115                 }
116         }       
117 }
118
119 void EffectChain::insert_node_between(Node *sender, Node *middle, Node *receiver)
120 {
121         for (unsigned i = 0; i < sender->outgoing_links.size(); ++i) {
122                 if (sender->outgoing_links[i] == receiver) {
123                         sender->outgoing_links[i] = middle;
124                         middle->incoming_links.push_back(sender);
125                 }
126         }
127         for (unsigned i = 0; i < receiver->incoming_links.size(); ++i) {
128                 if (receiver->incoming_links[i] == sender) {
129                         receiver->incoming_links[i] = middle;
130                         middle->outgoing_links.push_back(receiver);
131                 }
132         }
133
134         assert(middle->incoming_links.size() == middle->effect->num_inputs());
135 }
136
137 void EffectChain::find_all_nonlinear_inputs(Node *node, std::vector<Node *> *nonlinear_inputs)
138 {
139         if (node->output_gamma_curve == GAMMA_LINEAR &&
140             node->effect->effect_type_id() != "GammaCompressionEffect") {
141                 return;
142         }
143         if (node->effect->num_inputs() == 0) {
144                 nonlinear_inputs->push_back(node);
145         } else {
146                 assert(node->effect->num_inputs() == node->incoming_links.size());
147                 for (unsigned i = 0; i < node->incoming_links.size(); ++i) {
148                         find_all_nonlinear_inputs(node->incoming_links[i], nonlinear_inputs);
149                 }
150         }
151 }
152
153 Effect *EffectChain::add_effect(Effect *effect, const std::vector<Effect *> &inputs)
154 {
155         assert(inputs.size() == effect->num_inputs());
156         Node *node = add_node(effect);
157         for (unsigned i = 0; i < inputs.size(); ++i) {
158                 assert(node_map.count(inputs[i]) != 0);
159                 connect_nodes(node_map[inputs[i]], node);
160         }
161         return effect;
162 }
163
164 // GLSL pre-1.30 doesn't support token pasting. Replace PREFIX(x) with <effect_id>_x.
165 std::string replace_prefix(const std::string &text, const std::string &prefix)
166 {
167         std::string output;
168         size_t start = 0;
169
170         while (start < text.size()) {
171                 size_t pos = text.find("PREFIX(", start);
172                 if (pos == std::string::npos) {
173                         output.append(text.substr(start, std::string::npos));
174                         break;
175                 }
176
177                 output.append(text.substr(start, pos - start));
178                 output.append(prefix);
179                 output.append("_");
180
181                 pos += strlen("PREFIX(");
182         
183                 // Output stuff until we find the matching ), which we then eat.
184                 int depth = 1;
185                 size_t end_arg_pos = pos;
186                 while (end_arg_pos < text.size()) {
187                         if (text[end_arg_pos] == '(') {
188                                 ++depth;
189                         } else if (text[end_arg_pos] == ')') {
190                                 --depth;
191                                 if (depth == 0) {
192                                         break;
193                                 }
194                         }
195                         ++end_arg_pos;
196                 }
197                 output.append(text.substr(pos, end_arg_pos - pos));
198                 ++end_arg_pos;
199                 assert(depth == 0);
200                 start = end_arg_pos;
201         }
202         return output;
203 }
204
205 Phase *EffectChain::compile_glsl_program(
206         const std::vector<Node *> &inputs,
207         const std::vector<Node *> &effects)
208 {
209         assert(!effects.empty());
210
211         // Deduplicate the inputs.
212         std::vector<Node *> true_inputs = inputs;
213         std::sort(true_inputs.begin(), true_inputs.end());
214         true_inputs.erase(std::unique(true_inputs.begin(), true_inputs.end()), true_inputs.end());
215
216         bool input_needs_mipmaps = false;
217         std::string frag_shader = read_file("header.frag");
218
219         // Create functions for all the texture inputs that we need.
220         for (unsigned i = 0; i < true_inputs.size(); ++i) {
221                 Node *input = true_inputs[i];
222         
223                 frag_shader += std::string("uniform sampler2D tex_") + input->effect_id + ";\n";
224                 frag_shader += std::string("vec4 ") + input->effect_id + "(vec2 tc) {\n";
225                 frag_shader += "\treturn texture2D(tex_" + input->effect_id + ", tc);\n";
226                 frag_shader += "}\n";
227                 frag_shader += "\n";
228         }
229
230         for (unsigned i = 0; i < effects.size(); ++i) {
231                 Node *node = effects[i];
232
233                 if (node->incoming_links.size() == 1) {
234                         frag_shader += std::string("#define INPUT ") + node->incoming_links[0]->effect_id + "\n";
235                 } else {
236                         for (unsigned j = 0; j < node->incoming_links.size(); ++j) {
237                                 char buf[256];
238                                 sprintf(buf, "#define INPUT%d %s\n", j + 1, node->incoming_links[j]->effect_id.c_str());
239                                 frag_shader += buf;
240                         }
241                 }
242         
243                 frag_shader += "\n";
244                 frag_shader += std::string("#define FUNCNAME ") + node->effect_id + "\n";
245                 frag_shader += replace_prefix(node->effect->output_convenience_uniforms(), node->effect_id);
246                 frag_shader += replace_prefix(node->effect->output_fragment_shader(), node->effect_id);
247                 frag_shader += "#undef PREFIX\n";
248                 frag_shader += "#undef FUNCNAME\n";
249                 if (node->incoming_links.size() == 1) {
250                         frag_shader += "#undef INPUT\n";
251                 } else {
252                         for (unsigned j = 0; j < node->incoming_links.size(); ++j) {
253                                 char buf[256];
254                                 sprintf(buf, "#undef INPUT%d\n", j + 1);
255                                 frag_shader += buf;
256                         }
257                 }
258                 frag_shader += "\n";
259
260                 input_needs_mipmaps |= node->effect->needs_mipmaps();
261         }
262         for (unsigned i = 0; i < effects.size(); ++i) {
263                 Node *node = effects[i];
264                 if (node->effect->num_inputs() == 0) {
265                         CHECK(node->effect->set_int("needs_mipmaps", input_needs_mipmaps));
266                 }
267         }
268         frag_shader += std::string("#define INPUT ") + effects.back()->effect_id + "\n";
269         frag_shader.append(read_file("footer.frag"));
270
271 #ifndef NDEBUG
272         // Output shader to a temporary file, for easier debugging.
273         static int compiled_shader_num = 0;
274         char filename[256];
275         sprintf(filename, "chain-%03d.frag", compiled_shader_num++);
276         FILE *fp = fopen(filename, "w");
277         if (fp == NULL) {
278                 perror(filename);
279                 exit(1);
280         }
281         fprintf(fp, "%s\n", frag_shader.c_str());
282         fclose(fp);
283 #endif
284         
285         GLuint glsl_program_num = glCreateProgram();
286         GLuint vs_obj = compile_shader(read_file("vs.vert"), GL_VERTEX_SHADER);
287         GLuint fs_obj = compile_shader(frag_shader, GL_FRAGMENT_SHADER);
288         glAttachShader(glsl_program_num, vs_obj);
289         check_error();
290         glAttachShader(glsl_program_num, fs_obj);
291         check_error();
292         glLinkProgram(glsl_program_num);
293         check_error();
294
295         Phase *phase = new Phase;
296         phase->glsl_program_num = glsl_program_num;
297         phase->vertex_shader = vs_obj;
298         phase->fragment_shader = fs_obj;
299         phase->input_needs_mipmaps = input_needs_mipmaps;
300         phase->inputs = true_inputs;
301         phase->effects = effects;
302
303         return phase;
304 }
305
306 // Construct GLSL programs, starting at the given effect and following
307 // the chain from there. We end a program every time we come to an effect
308 // marked as "needs texture bounce", one that is used by multiple other
309 // effects, every time an effect wants to change the output size,
310 // and of course at the end.
311 //
312 // We follow a quite simple depth-first search from the output, although
313 // without any explicit recursion.
314 void EffectChain::construct_glsl_programs(Node *output)
315 {
316         // Which effects have already been completed in this phase?
317         // We need to keep track of it, as an effect with multiple outputs
318         // could otherwise be calculate multiple times.
319         std::set<Node *> completed_effects;
320
321         // Effects in the current phase, as well as inputs (outputs from other phases
322         // that we depend on). Note that since we start iterating from the end,
323         // the effect list will be in the reverse order.
324         std::vector<Node *> this_phase_inputs;
325         std::vector<Node *> this_phase_effects;
326
327         // Effects that we have yet to calculate, but that we know should
328         // be in the current phase.
329         std::stack<Node *> effects_todo_this_phase;
330
331         // Effects that we have yet to calculate, but that come from other phases.
332         // We delay these until we have this phase done in its entirety,
333         // at which point we pick any of them and start a new phase from that.
334         std::stack<Node *> effects_todo_other_phases;
335
336         effects_todo_this_phase.push(output);
337
338         for ( ;; ) {  // Termination condition within loop.
339                 if (!effects_todo_this_phase.empty()) {
340                         // OK, we have more to do this phase.
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 phase outputs,
345                         // and we throw those out separately below.
346                         assert(completed_effects.count(node) == 0);
347
348                         this_phase_effects.push_back(node);
349                         completed_effects.insert(node);
350
351                         // Find all the dependencies of this effect, and add them to the stack.
352                         std::vector<Node *> deps = node->incoming_links;
353                         assert(node->effect->num_inputs() == deps.size());
354                         for (unsigned i = 0; i < deps.size(); ++i) {
355                                 bool start_new_phase = false;
356
357                                 // FIXME: If we sample directly from a texture, we won't need this.
358                                 if (node->effect->needs_texture_bounce()) {
359                                         start_new_phase = true;
360                                 }
361
362                                 if (deps[i]->outgoing_links.size() > 1) {
363                                         if (deps[i]->effect->num_inputs() > 0) {
364                                                 // More than one effect uses this as the input,
365                                                 // and it is not a texture itself.
366                                                 // The easiest thing to do (and probably also the safest
367                                                 // performance-wise in most cases) is to bounce it to a texture
368                                                 // and then let the next passes read from that.
369                                                 start_new_phase = true;
370                                         } else {
371                                                 // For textures, we try to be slightly more clever;
372                                                 // if none of our outputs need a bounce, we don't bounce
373                                                 // but instead simply use the effect many times.
374                                                 //
375                                                 // Strictly speaking, we could bounce it for some outputs
376                                                 // and use it directly for others, but the processing becomes
377                                                 // somewhat simpler if the effect is only used in one such way.
378                                                 for (unsigned j = 0; j < deps[i]->outgoing_links.size(); ++j) {
379                                                         Node *rdep = deps[i]->outgoing_links[j];
380                                                         start_new_phase |= rdep->effect->needs_texture_bounce();
381                                                 }
382                                         }
383                                 }
384
385                                 if (deps[i]->effect->changes_output_size()) {
386                                         start_new_phase = true;
387                                 }
388
389                                 if (start_new_phase) {
390                                         effects_todo_other_phases.push(deps[i]);
391                                         this_phase_inputs.push_back(deps[i]);
392                                 } else {
393                                         effects_todo_this_phase.push(deps[i]);
394                                 }
395                         }
396                         continue;
397                 }
398
399                 // No more effects to do this phase. Take all the ones we have,
400                 // and create a GLSL program for it.
401                 if (!this_phase_effects.empty()) {
402                         reverse(this_phase_effects.begin(), this_phase_effects.end());
403                         phases.push_back(compile_glsl_program(this_phase_inputs, this_phase_effects));
404                         this_phase_effects.back()->phase = phases.back();
405                         this_phase_inputs.clear();
406                         this_phase_effects.clear();
407                 }
408                 assert(this_phase_inputs.empty());
409                 assert(this_phase_effects.empty());
410
411                 // If we have no effects left, exit.
412                 if (effects_todo_other_phases.empty()) {
413                         break;
414                 }
415
416                 Node *node = effects_todo_other_phases.top();
417                 effects_todo_other_phases.pop();
418
419                 if (completed_effects.count(node) == 0) {
420                         // Start a new phase, calculating from this effect.
421                         effects_todo_this_phase.push(node);
422                 }
423         }
424
425         // Finally, since the phases are found from the output but must be executed
426         // from the input(s), reverse them, too.
427         std::reverse(phases.begin(), phases.end());
428 }
429
430 void EffectChain::output_dot(const char *filename)
431 {
432 #ifdef NDEBUG
433         return;
434 #endif
435
436         FILE *fp = fopen(filename, "w");
437         if (fp == NULL) {
438                 perror(filename);
439                 exit(1);
440         }
441
442         fprintf(fp, "digraph G {\n");
443         for (unsigned i = 0; i < nodes.size(); ++i) {
444                 // Find out which phase this event belongs to.
445                 int in_phase = -1;
446                 for (unsigned j = 0; j < phases.size(); ++j) {
447                         const Phase* p = phases[j];
448                         if (std::find(p->effects.begin(), p->effects.end(), nodes[i]) != p->effects.end()) {
449                                 assert(in_phase == -1);
450                                 in_phase = j;
451                         }
452                 }
453
454                 if (in_phase == -1) {
455                         fprintf(fp, "  n%ld [label=\"%s\"];\n", (long)nodes[i], nodes[i]->effect->effect_type_id().c_str());
456                 } else {
457                         fprintf(fp, "  n%ld [label=\"%s\" style=\"filled\" fillcolor=\"/accent8/%d\"];\n",
458                                 (long)nodes[i], nodes[i]->effect->effect_type_id().c_str(),
459                                 (in_phase % 8) + 1);
460                 }
461                 for (unsigned j = 0; j < nodes[i]->outgoing_links.size(); ++j) {
462                         std::vector<std::string> labels;
463
464                         if (nodes[i]->outgoing_links[j]->effect->needs_texture_bounce()) {
465                                 labels.push_back("needs_bounce");
466                         }
467                         if (nodes[i]->effect->changes_output_size()) {
468                                 labels.push_back("resize");
469                         }
470
471                         switch (nodes[i]->output_color_space) {
472                         case COLORSPACE_INVALID:
473                                 labels.push_back("spc[invalid]");
474                                 break;
475                         case COLORSPACE_REC_601_525:
476                                 labels.push_back("spc[rec601-525]");
477                                 break;
478                         case COLORSPACE_REC_601_625:
479                                 labels.push_back("spc[rec601-625]");
480                                 break;
481                         default:
482                                 break;
483                         }
484
485                         switch (nodes[i]->output_gamma_curve) {
486                         case GAMMA_INVALID:
487                                 labels.push_back("gamma[invalid]");
488                                 break;
489                         case GAMMA_sRGB:
490                                 labels.push_back("gamma[sRGB]");
491                                 break;
492                         case GAMMA_REC_601:  // and GAMMA_REC_709
493                                 labels.push_back("gamma[rec601/709]");
494                                 break;
495                         default:
496                                 break;
497                         }
498
499                         if (labels.empty()) {
500                                 fprintf(fp, "  n%ld -> n%ld;\n", (long)nodes[i], (long)nodes[i]->outgoing_links[j]);
501                         } else {
502                                 std::string label = labels[0];
503                                 for (unsigned k = 1; k < labels.size(); ++k) {
504                                         label += ", " + labels[k];
505                                 }
506                                 fprintf(fp, "  n%ld -> n%ld [label=\"%s\"];\n", (long)nodes[i], (long)nodes[i]->outgoing_links[j], label.c_str());
507                         }
508                 }
509         }
510         fprintf(fp, "}\n");
511
512         fclose(fp);
513 }
514
515 unsigned EffectChain::fit_rectangle_to_aspect(unsigned width, unsigned height)
516 {
517         if (float(width) * aspect_denom >= float(height) * aspect_nom) {
518                 // Same aspect, or W/H > aspect (image is wider than the frame).
519                 // In either case, keep width.
520                 return width;
521         } else {
522                 // W/H < aspect (image is taller than the frame), so keep height,
523                 // and adjust width correspondingly.
524                 return lrintf(height * aspect_nom / aspect_denom);
525         }
526 }
527
528 // Propagate input texture sizes throughout, and inform effects downstream.
529 // (Like a lot of other code, we depend on effects being in topological order.)
530 void EffectChain::inform_input_sizes(Phase *phase)
531 {
532         // All effects that have a defined size (inputs and RTT inputs)
533         // get that. Reset all others.
534         for (unsigned i = 0; i < phase->effects.size(); ++i) {
535                 Node *node = phase->effects[i];
536                 if (node->effect->num_inputs() == 0) {
537                         Input *input = static_cast<Input *>(node->effect);
538                         node->output_width = input->get_width();
539                         node->output_height = input->get_height();
540                         assert(node->output_width != 0);
541                         assert(node->output_height != 0);
542                 } else {
543                         node->output_width = node->output_height = 0;
544                 }
545         }
546         for (unsigned i = 0; i < phase->inputs.size(); ++i) {
547                 Node *input = phase->inputs[i];
548                 input->output_width = input->phase->output_width;
549                 input->output_height = input->phase->output_height;
550                 assert(input->output_width != 0);
551                 assert(input->output_height != 0);
552         }
553
554         // Now propagate from the inputs towards the end, and inform as we go.
555         // The rules are simple:
556         //
557         //   1. Don't touch effects that already have given sizes (ie., inputs).
558         //   2. If all of your inputs have the same size, that will be your output size.
559         //   3. Otherwise, your output size is 0x0.
560         for (unsigned i = 0; i < phase->effects.size(); ++i) {
561                 Node *node = phase->effects[i];
562                 if (node->effect->num_inputs() == 0) {
563                         continue;
564                 }
565                 unsigned this_output_width = 0;
566                 unsigned this_output_height = 0;
567                 for (unsigned j = 0; j < node->incoming_links.size(); ++j) {
568                         Node *input = node->incoming_links[j];
569                         node->effect->inform_input_size(j, input->output_width, input->output_height);
570                         if (j == 0) {
571                                 this_output_width = input->output_width;
572                                 this_output_height = input->output_height;
573                         } else if (input->output_width != this_output_width || input->output_height != this_output_height) {
574                                 // Inputs disagree.
575                                 this_output_width = 0;
576                                 this_output_height = 0;
577                         }
578                 }
579                 node->output_width = this_output_width;
580                 node->output_height = this_output_height;
581         }
582 }
583
584 // Note: You should call inform_input_sizes() before this, as the last effect's
585 // desired output size might change based on the inputs.
586 void EffectChain::find_output_size(Phase *phase)
587 {
588         Node *output_node = phase->effects.back();
589
590         // If the last effect explicitly sets an output size, use that.
591         if (output_node->effect->changes_output_size()) {
592                 output_node->effect->get_output_size(&phase->output_width, &phase->output_height);
593                 return;
594         }
595
596         // If not, look at the input phases and textures.
597         // We select the largest one (by fit into the current aspect).
598         unsigned best_width = 0;
599         for (unsigned i = 0; i < phase->inputs.size(); ++i) {
600                 Node *input = phase->inputs[i];
601                 assert(input->phase->output_width != 0);
602                 assert(input->phase->output_height != 0);
603                 unsigned width = fit_rectangle_to_aspect(input->phase->output_width, input->phase->output_height);
604                 if (width > best_width) {
605                         best_width = width;
606                 }
607         }
608         for (unsigned i = 0; i < phase->effects.size(); ++i) {
609                 Effect *effect = phase->effects[i]->effect;
610                 if (effect->num_inputs() != 0) {
611                         continue;
612                 }
613
614                 Input *input = static_cast<Input *>(effect);
615                 unsigned width = fit_rectangle_to_aspect(input->get_width(), input->get_height());
616                 if (width > best_width) {
617                         best_width = width;
618                 }
619         }
620         assert(best_width != 0);
621         phase->output_width = best_width;
622         phase->output_height = best_width * aspect_denom / aspect_nom;
623 }
624
625 void EffectChain::sort_nodes_topologically()
626 {
627         std::set<Node *> visited_nodes;
628         std::vector<Node *> sorted_list;
629         for (unsigned i = 0; i < nodes.size(); ++i) {
630                 if (nodes[i]->incoming_links.size() == 0) {
631                         topological_sort_visit_node(nodes[i], &visited_nodes, &sorted_list);
632                 }
633         }
634         reverse(sorted_list.begin(), sorted_list.end());
635         nodes = sorted_list;
636 }
637
638 void EffectChain::topological_sort_visit_node(Node *node, std::set<Node *> *visited_nodes, std::vector<Node *> *sorted_list)
639 {
640         if (visited_nodes->count(node) != 0) {
641                 return;
642         }
643         visited_nodes->insert(node);
644         for (unsigned i = 0; i < node->outgoing_links.size(); ++i) {
645                 topological_sort_visit_node(node->outgoing_links[i], visited_nodes, sorted_list);
646         }
647         sorted_list->push_back(node);
648 }
649
650 // Propagate gamma and color space information as far as we can in the graph.
651 // The rules are simple: Anything where all the inputs agree, get that as
652 // output as well. Anything else keeps having *_INVALID.
653 void EffectChain::propagate_gamma_and_color_space()
654 {
655         // We depend on going through the nodes in order.
656         sort_nodes_topologically();
657
658         for (unsigned i = 0; i < nodes.size(); ++i) {
659                 Node *node = nodes[i];
660                 if (node->disabled) {
661                         continue;
662                 }
663                 assert(node->incoming_links.size() == node->effect->num_inputs());
664                 if (node->incoming_links.size() == 0) {
665                         assert(node->output_color_space != COLORSPACE_INVALID);
666                         assert(node->output_gamma_curve != GAMMA_INVALID);
667                         continue;
668                 }
669
670                 Colorspace color_space = node->incoming_links[0]->output_color_space;
671                 GammaCurve gamma_curve = node->incoming_links[0]->output_gamma_curve;
672                 for (unsigned j = 1; j < node->incoming_links.size(); ++j) {
673                         if (node->incoming_links[j]->output_color_space != color_space) {
674                                 color_space = COLORSPACE_INVALID;
675                         }
676                         if (node->incoming_links[j]->output_gamma_curve != gamma_curve) {
677                                 gamma_curve = GAMMA_INVALID;
678                         }
679                 }
680
681                 // The conversion effects already have their outputs set correctly,
682                 // so leave them alone.
683                 if (node->effect->effect_type_id() != "ColorspaceConversionEffect") {
684                         node->output_color_space = color_space;
685                 }               
686                 if (node->effect->effect_type_id() != "GammaCompressionEffect" &&
687                     node->effect->effect_type_id() != "GammaExpansionEffect") {
688                         node->output_gamma_curve = gamma_curve;
689                 }               
690         }
691 }
692
693 bool EffectChain::node_needs_colorspace_fix(Node *node)
694 {
695         if (node->disabled) {
696                 return false;
697         }
698         if (node->effect->num_inputs() == 0) {
699                 return false;
700         }
701
702         // propagate_gamma_and_color_space() has already set our output
703         // to COLORSPACE_INVALID if the inputs differ, so we can rely on that.
704         if (node->output_color_space == COLORSPACE_INVALID) {
705                 return true;
706         }
707         return (node->effect->needs_srgb_primaries() && node->output_color_space != COLORSPACE_sRGB);
708 }
709
710 // Fix up color spaces so that there are no COLORSPACE_INVALID nodes left in
711 // the graph. Our strategy is not always optimal, but quite simple:
712 // Find an effect that's as early as possible where the inputs are of
713 // unacceptable colorspaces (that is, either different, or, if the effect only
714 // wants sRGB, not sRGB.) Add appropriate conversions on all its inputs,
715 // propagate the information anew, and repeat until there are no more such
716 // effects.
717 void EffectChain::fix_internal_color_spaces()
718 {
719         unsigned colorspace_propagation_pass = 0;
720         bool found_any;
721         do {
722                 found_any = false;
723                 for (unsigned i = 0; i < nodes.size(); ++i) {
724                         Node *node = nodes[i];
725                         if (!node_needs_colorspace_fix(node)) {
726                                 continue;
727                         }
728
729                         // Go through each input that is not sRGB, and insert
730                         // a colorspace conversion before it.
731                         for (unsigned j = 0; j < node->incoming_links.size(); ++j) {
732                                 Node *input = node->incoming_links[j];
733                                 assert(input->output_color_space != COLORSPACE_INVALID);
734                                 if (input->output_color_space == COLORSPACE_sRGB) {
735                                         continue;
736                                 }
737                                 Node *conversion = add_node(new ColorspaceConversionEffect());
738                                 CHECK(conversion->effect->set_int("source_space", input->output_color_space));
739                                 CHECK(conversion->effect->set_int("destination_space", COLORSPACE_sRGB));
740                                 conversion->output_color_space = COLORSPACE_sRGB;
741                                 insert_node_between(input, conversion, node);
742                         }
743
744                         // Re-sort topologically, and propagate the new information.
745                         propagate_gamma_and_color_space();
746                         
747                         found_any = true;
748                         break;
749                 }
750         
751                 char filename[256];
752                 sprintf(filename, "step3-colorspacefix-iter%u.dot", ++colorspace_propagation_pass);
753                 output_dot(filename);
754                 assert(colorspace_propagation_pass < 100);
755         } while (found_any);
756
757         for (unsigned i = 0; i < nodes.size(); ++i) {
758                 Node *node = nodes[i];
759                 if (node->disabled) {
760                         continue;
761                 }
762                 assert(node->output_color_space != COLORSPACE_INVALID);
763         }
764 }
765
766 // Make so that the output is in the desired color space.
767 void EffectChain::fix_output_color_space()
768 {
769         Node *output = find_output_node();
770         if (output->output_color_space != output_format.color_space) {
771                 Node *conversion = add_node(new ColorspaceConversionEffect());
772                 CHECK(conversion->effect->set_int("source_space", output->output_color_space));
773                 CHECK(conversion->effect->set_int("destination_space", output_format.color_space));
774                 conversion->output_color_space = output_format.color_space;
775                 connect_nodes(output, conversion);
776                 propagate_gamma_and_color_space();
777         }
778 }
779
780 bool EffectChain::node_needs_gamma_fix(Node *node)
781 {
782         if (node->disabled) {
783                 return false;
784         }
785
786         // Small hack since the output is not an explicit node:
787         // If we are the last node and our output is in the wrong
788         // space compared to EffectChain's output, we need to fix it.
789         // This will only take us to linear, but fix_output_gamma()
790         // will come and take us to the desired output gamma
791         // if it is needed.
792         //
793         // This needs to be before everything else, since it could
794         // even apply to inputs (if they are the only effect).
795         if (node->outgoing_links.empty() &&
796             node->output_gamma_curve != output_format.gamma_curve &&
797             node->output_gamma_curve != GAMMA_LINEAR) {
798                 return true;
799         }
800
801         if (node->effect->num_inputs() == 0) {
802                 return false;
803         }
804
805         // propagate_gamma_and_color_space() has already set our output
806         // to GAMMA_INVALID if the inputs differ, so we can rely on that,
807         // except for GammaCompressionEffect.
808         if (node->output_gamma_curve == GAMMA_INVALID) {
809                 return true;
810         }
811         if (node->effect->effect_type_id() == "GammaCompressionEffect") {
812                 assert(node->incoming_links.size() == 1);
813                 return node->incoming_links[0]->output_gamma_curve != GAMMA_LINEAR;
814         }
815
816         return (node->effect->needs_linear_light() && node->output_gamma_curve != GAMMA_LINEAR);
817 }
818
819 // Very similar to fix_internal_color_spaces(), but for gamma.
820 // There is one difference, though; before we start adding conversion nodes,
821 // we see if we can get anything out of asking the sources to deliver
822 // linear gamma directly. fix_internal_gamma_by_asking_inputs()
823 // does that part, while fix_internal_gamma_by_inserting_nodes()
824 // inserts nodes as needed afterwards.
825 void EffectChain::fix_internal_gamma_by_asking_inputs(unsigned step)
826 {
827         unsigned gamma_propagation_pass = 0;
828         bool found_any;
829         do {
830                 found_any = false;
831                 for (unsigned i = 0; i < nodes.size(); ++i) {
832                         Node *node = nodes[i];
833                         if (!node_needs_gamma_fix(node)) {
834                                 continue;
835                         }
836
837                         // See if all inputs can give us linear gamma. If not, leave it.
838                         std::vector<Node *> nonlinear_inputs;
839                         find_all_nonlinear_inputs(node, &nonlinear_inputs);
840                         assert(!nonlinear_inputs.empty());
841
842                         bool all_ok = true;
843                         for (unsigned i = 0; i < nonlinear_inputs.size(); ++i) {
844                                 Input *input = static_cast<Input *>(nonlinear_inputs[i]->effect);
845                                 all_ok &= input->can_output_linear_gamma();
846                         }
847
848                         if (!all_ok) {
849                                 continue;
850                         }
851
852                         for (unsigned i = 0; i < nonlinear_inputs.size(); ++i) {
853                                 CHECK(nonlinear_inputs[i]->effect->set_int("output_linear_gamma", 1));
854                                 nonlinear_inputs[i]->output_gamma_curve = GAMMA_LINEAR;
855                         }
856
857                         // Re-sort topologically, and propagate the new information.
858                         propagate_gamma_and_color_space();
859                         
860                         found_any = true;
861                         break;
862                 }
863         
864                 char filename[256];
865                 sprintf(filename, "step%u-gammafix-iter%u.dot", step, ++gamma_propagation_pass);
866                 output_dot(filename);
867                 assert(gamma_propagation_pass < 100);
868         } while (found_any);
869 }
870
871 void EffectChain::fix_internal_gamma_by_inserting_nodes(unsigned step)
872 {
873         unsigned gamma_propagation_pass = 0;
874         bool found_any;
875         do {
876                 found_any = false;
877                 for (unsigned i = 0; i < nodes.size(); ++i) {
878                         Node *node = nodes[i];
879                         if (!node_needs_gamma_fix(node)) {
880                                 continue;
881                         }
882
883                         // Special case: We could be an input and still be asked to
884                         // fix our gamma; if so, we should be the only node
885                         // (as node_needs_gamma_fix() would only return true in
886                         // for an input in that case). That means we should insert
887                         // a conversion node _after_ ourselves.
888                         if (node->incoming_links.empty()) {
889                                 assert(node->outgoing_links.empty());
890                                 Node *conversion = add_node(new GammaExpansionEffect());
891                                 CHECK(conversion->effect->set_int("source_curve", node->output_gamma_curve));
892                                 conversion->output_gamma_curve = GAMMA_LINEAR;
893                                 connect_nodes(node, conversion);
894                         }
895
896                         // If not, go through each input that is not linear gamma,
897                         // and insert a gamma conversion before it.
898                         for (unsigned j = 0; j < node->incoming_links.size(); ++j) {
899                                 Node *input = node->incoming_links[j];
900                                 assert(input->output_gamma_curve != GAMMA_INVALID);
901                                 if (input->output_gamma_curve == GAMMA_LINEAR) {
902                                         continue;
903                                 }
904                                 Node *conversion = add_node(new GammaExpansionEffect());
905                                 CHECK(conversion->effect->set_int("source_curve", input->output_gamma_curve));
906                                 conversion->output_gamma_curve = GAMMA_LINEAR;
907                                 insert_node_between(input, conversion, node);
908                         }
909
910                         // Re-sort topologically, and propagate the new information.
911                         propagate_gamma_and_color_space();
912                         
913                         found_any = true;
914                         break;
915                 }
916         
917                 char filename[256];
918                 sprintf(filename, "step%u-gammafix-iter%u.dot", step, ++gamma_propagation_pass);
919                 output_dot(filename);
920                 assert(gamma_propagation_pass < 100);
921         } while (found_any);
922
923         for (unsigned i = 0; i < nodes.size(); ++i) {
924                 Node *node = nodes[i];
925                 if (node->disabled) {
926                         continue;
927                 }
928                 assert(node->output_gamma_curve != GAMMA_INVALID);
929         }
930 }
931
932 // Make so that the output is in the desired gamma.
933 // Note that this assumes linear input gamma, so it might create the need
934 // for another pass of fix_internal_gamma().
935 void EffectChain::fix_output_gamma()
936 {
937         Node *output = find_output_node();
938         if (output->output_gamma_curve != output_format.gamma_curve) {
939                 Node *conversion = add_node(new GammaCompressionEffect());
940                 CHECK(conversion->effect->set_int("destination_curve", output_format.gamma_curve));
941                 conversion->output_gamma_curve = output_format.gamma_curve;
942                 connect_nodes(output, conversion);
943         }
944 }
945         
946 // If the user has requested dither, add a DitherEffect right at the end
947 // (after GammaCompressionEffect etc.). This needs to be done after everything else,
948 // since dither is about the only effect that can _not_ be done in linear space.
949 void EffectChain::add_dither_if_needed()
950 {
951         if (num_dither_bits == 0) {
952                 return;
953         }
954         Node *output = find_output_node();
955         Node *dither = add_node(new DitherEffect());
956         CHECK(dither->effect->set_int("num_bits", num_dither_bits));
957         connect_nodes(output, dither);
958
959         dither_effect = dither->effect;
960 }
961
962 // Find the output node. This is, simply, one that has no outgoing links.
963 // If there are multiple ones, the graph is malformed (we do not support
964 // multiple outputs right now).
965 Node *EffectChain::find_output_node()
966 {
967         std::vector<Node *> output_nodes;
968         for (unsigned i = 0; i < nodes.size(); ++i) {
969                 Node *node = nodes[i];
970                 if (node->disabled) {
971                         continue;
972                 }
973                 if (node->outgoing_links.empty()) {
974                         output_nodes.push_back(node);
975                 }
976         }
977         assert(output_nodes.size() == 1);
978         return output_nodes[0];
979 }
980
981 void EffectChain::finalize()
982 {
983         // Output the graph as it is before we do any conversions on it.
984         output_dot("step0-start.dot");
985
986         // Give each effect in turn a chance to rewrite its own part of the graph.
987         // Note that if more effects are added as part of this, they will be
988         // picked up as part of the same for loop, since they are added at the end.
989         for (unsigned i = 0; i < nodes.size(); ++i) {
990                 nodes[i]->effect->rewrite_graph(this, nodes[i]);
991         }
992         output_dot("step1-rewritten.dot");
993
994         propagate_gamma_and_color_space();
995         output_dot("step2-propagated.dot");
996
997         fix_internal_color_spaces();
998         fix_output_color_space();
999         output_dot("step4-output-colorspacefix.dot");
1000
1001         // Note that we need to fix gamma after colorspace conversion,
1002         // because colorspace conversions might create needs for gamma conversions.
1003         // Also, we need to run an extra pass of fix_internal_gamma() after 
1004         // fixing the output gamma, as we only have conversions to/from linear.
1005         fix_internal_gamma_by_asking_inputs(5);
1006         fix_internal_gamma_by_inserting_nodes(6);
1007         fix_output_gamma();
1008         output_dot("step7-output-gammafix.dot");
1009         fix_internal_gamma_by_asking_inputs(8);
1010         fix_internal_gamma_by_inserting_nodes(9);
1011
1012         output_dot("step10-before-dither.dot");
1013
1014         add_dither_if_needed();
1015
1016         output_dot("step11-final.dot");
1017         
1018         // Construct all needed GLSL programs, starting at the output.
1019         construct_glsl_programs(find_output_node());
1020
1021         output_dot("step12-split-to-phases.dot");
1022
1023         // If we have more than one phase, we need intermediate render-to-texture.
1024         // Construct an FBO, and then as many textures as we need.
1025         // We choose the simplest option of having one texture per output,
1026         // since otherwise this turns into an (albeit simple)
1027         // register allocation problem.
1028         if (phases.size() > 1) {
1029                 glGenFramebuffers(1, &fbo);
1030
1031                 for (unsigned i = 0; i < phases.size() - 1; ++i) {
1032                         inform_input_sizes(phases[i]);
1033                         find_output_size(phases[i]);
1034
1035                         Node *output_node = phases[i]->effects.back();
1036                         glGenTextures(1, &output_node->output_texture);
1037                         check_error();
1038                         glBindTexture(GL_TEXTURE_2D, output_node->output_texture);
1039                         check_error();
1040                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1041                         check_error();
1042                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1043                         check_error();
1044                         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, phases[i]->output_width, phases[i]->output_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
1045                         check_error();
1046
1047                         output_node->output_texture_width = phases[i]->output_width;
1048                         output_node->output_texture_height = phases[i]->output_height;
1049                 }
1050                 inform_input_sizes(phases.back());
1051         }
1052                 
1053         for (unsigned i = 0; i < inputs.size(); ++i) {
1054                 inputs[i]->finalize();
1055         }
1056
1057         assert(phases[0]->inputs.empty());
1058         
1059         finalized = true;
1060 }
1061
1062 void EffectChain::render_to_fbo(GLuint dest_fbo, unsigned width, unsigned height)
1063 {
1064         assert(finalized);
1065
1066         // Save original viewport.
1067         GLuint x = 0, y = 0;
1068
1069         if (width == 0 && height == 0) {
1070                 GLint viewport[4];
1071                 glGetIntegerv(GL_VIEWPORT, viewport);
1072                 x = viewport[0];
1073                 y = viewport[1];
1074                 width = viewport[2];
1075                 height = viewport[3];
1076         }
1077
1078         // Basic state.
1079         glDisable(GL_BLEND);
1080         check_error();
1081         glDisable(GL_DEPTH_TEST);
1082         check_error();
1083         glDepthMask(GL_FALSE);
1084         check_error();
1085
1086         glMatrixMode(GL_PROJECTION);
1087         glLoadIdentity();
1088         glOrtho(0.0, 1.0, 0.0, 1.0, 0.0, 1.0);
1089
1090         glMatrixMode(GL_MODELVIEW);
1091         glLoadIdentity();
1092
1093         if (phases.size() > 1) {
1094                 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
1095                 check_error();
1096         }
1097
1098         std::set<Node *> generated_mipmaps;
1099
1100         for (unsigned phase = 0; phase < phases.size(); ++phase) {
1101                 // See if the requested output size has changed. If so, we need to recreate
1102                 // the texture (and before we start setting up inputs).
1103                 inform_input_sizes(phases[phase]);
1104                 if (phase != phases.size() - 1) {
1105                         find_output_size(phases[phase]);
1106
1107                         Node *output_node = phases[phase]->effects.back();
1108
1109                         if (output_node->output_texture_width != phases[phase]->output_width ||
1110                             output_node->output_texture_height != phases[phase]->output_height) {
1111                                 glActiveTexture(GL_TEXTURE0);
1112                                 check_error();
1113                                 glBindTexture(GL_TEXTURE_2D, output_node->output_texture);
1114                                 check_error();
1115                                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, phases[phase]->output_width, phases[phase]->output_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
1116                                 check_error();
1117                                 glBindTexture(GL_TEXTURE_2D, 0);
1118                                 check_error();
1119
1120                                 output_node->output_texture_width = phases[phase]->output_width;
1121                                 output_node->output_texture_height = phases[phase]->output_height;
1122                         }
1123                 }
1124
1125                 glUseProgram(phases[phase]->glsl_program_num);
1126                 check_error();
1127
1128                 // Set up RTT inputs for this phase.
1129                 for (unsigned sampler = 0; sampler < phases[phase]->inputs.size(); ++sampler) {
1130                         glActiveTexture(GL_TEXTURE0 + sampler);
1131                         Node *input = phases[phase]->inputs[sampler];
1132                         glBindTexture(GL_TEXTURE_2D, input->output_texture);
1133                         check_error();
1134                         if (phases[phase]->input_needs_mipmaps) {
1135                                 if (generated_mipmaps.count(input) == 0) {
1136                                         glGenerateMipmap(GL_TEXTURE_2D);
1137                                         check_error();
1138                                         generated_mipmaps.insert(input);
1139                                 }
1140                                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
1141                                 check_error();
1142                         } else {
1143                                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1144                                 check_error();
1145                         }
1146
1147                         std::string texture_name = std::string("tex_") + input->effect_id;
1148                         glUniform1i(glGetUniformLocation(phases[phase]->glsl_program_num, texture_name.c_str()), sampler);
1149                         check_error();
1150                 }
1151
1152                 // And now the output.
1153                 if (phase == phases.size() - 1) {
1154                         // Last phase goes to the output the user specified.
1155                         glBindFramebuffer(GL_FRAMEBUFFER, dest_fbo);
1156                         check_error();
1157                         glViewport(x, y, width, height);
1158                         if (dither_effect != NULL) {
1159                                 CHECK(dither_effect->set_int("output_width", width));
1160                                 CHECK(dither_effect->set_int("output_height", height));
1161                         }
1162                 } else {
1163                         Node *output_node = phases[phase]->effects.back();
1164                         glFramebufferTexture2D(
1165                                 GL_FRAMEBUFFER,
1166                                 GL_COLOR_ATTACHMENT0,
1167                                 GL_TEXTURE_2D,
1168                                 output_node->output_texture,
1169                                 0);
1170                         check_error();
1171                         glViewport(0, 0, phases[phase]->output_width, phases[phase]->output_height);
1172                 }
1173
1174                 // Give the required parameters to all the effects.
1175                 unsigned sampler_num = phases[phase]->inputs.size();
1176                 for (unsigned i = 0; i < phases[phase]->effects.size(); ++i) {
1177                         Node *node = phases[phase]->effects[i];
1178                         node->effect->set_gl_state(phases[phase]->glsl_program_num, node->effect_id, &sampler_num);
1179                         check_error();
1180                 }
1181
1182                 // Now draw!
1183                 glBegin(GL_QUADS);
1184
1185                 glTexCoord2f(0.0f, 0.0f);
1186                 glVertex2f(0.0f, 0.0f);
1187
1188                 glTexCoord2f(1.0f, 0.0f);
1189                 glVertex2f(1.0f, 0.0f);
1190
1191                 glTexCoord2f(1.0f, 1.0f);
1192                 glVertex2f(1.0f, 1.0f);
1193
1194                 glTexCoord2f(0.0f, 1.0f);
1195                 glVertex2f(0.0f, 1.0f);
1196
1197                 glEnd();
1198                 check_error();
1199
1200                 for (unsigned i = 0; i < phases[phase]->effects.size(); ++i) {
1201                         Node *node = phases[phase]->effects[i];
1202                         node->effect->clear_gl_state();
1203                 }
1204         }
1205 }