#include <stdio.h>
#include <math.h>
#include <string.h>
+#include <locale.h>
#include <assert.h>
#include <GL/glew.h>
frag_shader += "\n";
}
- for (unsigned i = 0; i < effects.size(); ++i) {
- Node *node = effects[i];
+ std::vector<Node *> sorted_effects = topological_sort(effects);
+
+ for (unsigned i = 0; i < sorted_effects.size(); ++i) {
+ Node *node = sorted_effects[i];
if (node->incoming_links.size() == 1) {
frag_shader += std::string("#define INPUT ") + node->incoming_links[0]->effect_id + "\n";
input_needs_mipmaps |= node->effect->needs_mipmaps();
}
- for (unsigned i = 0; i < effects.size(); ++i) {
- Node *node = effects[i];
+ for (unsigned i = 0; i < sorted_effects.size(); ++i) {
+ Node *node = sorted_effects[i];
if (node->effect->num_inputs() == 0) {
CHECK(node->effect->set_int("needs_mipmaps", input_needs_mipmaps));
}
}
- frag_shader += std::string("#define INPUT ") + effects.back()->effect_id + "\n";
+ frag_shader += std::string("#define INPUT ") + sorted_effects.back()->effect_id + "\n";
frag_shader.append(read_file("footer.frag"));
if (movit_debug_level == MOVIT_DEBUG_ON) {
phase->fragment_shader = fs_obj;
phase->input_needs_mipmaps = input_needs_mipmaps;
phase->inputs = true_inputs;
- phase->effects = effects;
+ phase->effects = sorted_effects;
return phase;
}
// without any explicit recursion.
void EffectChain::construct_glsl_programs(Node *output)
{
- // Which effects have already been completed in this phase?
+ // Which effects have already been completed?
// We need to keep track of it, as an effect with multiple outputs
- // could otherwise be calculate multiple times.
+ // could otherwise be calculated multiple times.
std::set<Node *> completed_effects;
// Effects in the current phase, as well as inputs (outputs from other phases
Node *node = effects_todo_this_phase.top();
effects_todo_this_phase.pop();
- // This should currently only happen for effects that are phase outputs,
- // and we throw those out separately below.
- assert(completed_effects.count(node) == 0);
+ // This should currently only happen for effects that are inputs
+ // (either true inputs or phase outputs). We special-case inputs,
+ // and then deduplicate phase outputs in compile_glsl_program().
+ if (node->effect->num_inputs() == 0) {
+ if (find(this_phase_effects.begin(), this_phase_effects.end(), node) != this_phase_effects.end()) {
+ continue;
+ }
+ } else {
+ assert(completed_effects.count(node) == 0);
+ }
this_phase_effects.push_back(node);
completed_effects.insert(node);
}
fprintf(fp, "digraph G {\n");
+ fprintf(fp, " output [shape=box label=\"(output)\"];\n");
for (unsigned i = 0; i < nodes.size(); ++i) {
// Find out which phase this event belongs to.
- int in_phase = -1;
+ std::vector<int> in_phases;
for (unsigned j = 0; j < phases.size(); ++j) {
const Phase* p = phases[j];
if (std::find(p->effects.begin(), p->effects.end(), nodes[i]) != p->effects.end()) {
- assert(in_phase == -1);
- in_phase = j;
+ in_phases.push_back(j);
}
}
- if (in_phase == -1) {
+ if (in_phases.empty()) {
fprintf(fp, " n%ld [label=\"%s\"];\n", (long)nodes[i], nodes[i]->effect->effect_type_id().c_str());
- } else {
+ } else if (in_phases.size() == 1) {
fprintf(fp, " n%ld [label=\"%s\" style=\"filled\" fillcolor=\"/accent8/%d\"];\n",
(long)nodes[i], nodes[i]->effect->effect_type_id().c_str(),
- (in_phase % 8) + 1);
+ (in_phases[0] % 8) + 1);
+ } else {
+ // If we had new enough Graphviz, style="wedged" would probably be ideal here.
+ // But alas.
+ fprintf(fp, " n%ld [label=\"%s [in multiple phases]\" style=\"filled\" fillcolor=\"/accent8/%d\"];\n",
+ (long)nodes[i], nodes[i]->effect->effect_type_id().c_str(),
+ (in_phases[0] % 8) + 1);
}
- for (unsigned j = 0; j < nodes[i]->outgoing_links.size(); ++j) {
- std::vector<std::string> labels;
-
- if (nodes[i]->outgoing_links[j]->effect->needs_texture_bounce()) {
- labels.push_back("needs_bounce");
- }
- if (nodes[i]->effect->changes_output_size()) {
- labels.push_back("resize");
- }
- switch (nodes[i]->output_color_space) {
- case COLORSPACE_INVALID:
- labels.push_back("spc[invalid]");
- break;
- case COLORSPACE_REC_601_525:
- labels.push_back("spc[rec601-525]");
- break;
- case COLORSPACE_REC_601_625:
- labels.push_back("spc[rec601-625]");
- break;
- default:
- break;
- }
+ char from_node_id[256];
+ snprintf(from_node_id, 256, "n%ld", (long)nodes[i]);
- switch (nodes[i]->output_gamma_curve) {
- case GAMMA_INVALID:
- labels.push_back("gamma[invalid]");
- break;
- case GAMMA_sRGB:
- labels.push_back("gamma[sRGB]");
- break;
- case GAMMA_REC_601: // and GAMMA_REC_709
- labels.push_back("gamma[rec601/709]");
- break;
- default:
- break;
- }
+ for (unsigned j = 0; j < nodes[i]->outgoing_links.size(); ++j) {
+ char to_node_id[256];
+ snprintf(to_node_id, 256, "n%ld", (long)nodes[i]->outgoing_links[j]);
- switch (nodes[i]->output_alpha_type) {
- case ALPHA_INVALID:
- labels.push_back("alpha[invalid]");
- break;
- case ALPHA_BLANK:
- labels.push_back("alpha[blank]");
- break;
- case ALPHA_POSTMULTIPLIED:
- labels.push_back("alpha[postmult]");
- break;
- default:
- break;
- }
+ std::vector<std::string> labels = get_labels_for_edge(nodes[i], nodes[i]->outgoing_links[j]);
+ output_dot_edge(fp, from_node_id, to_node_id, labels);
+ }
- if (labels.empty()) {
- fprintf(fp, " n%ld -> n%ld;\n", (long)nodes[i], (long)nodes[i]->outgoing_links[j]);
- } else {
- std::string label = labels[0];
- for (unsigned k = 1; k < labels.size(); ++k) {
- label += ", " + labels[k];
- }
- fprintf(fp, " n%ld -> n%ld [label=\"%s\"];\n", (long)nodes[i], (long)nodes[i]->outgoing_links[j], label.c_str());
- }
+ if (nodes[i]->outgoing_links.empty() && !nodes[i]->disabled) {
+ // Output node.
+ std::vector<std::string> labels = get_labels_for_edge(nodes[i], NULL);
+ output_dot_edge(fp, from_node_id, "output", labels);
}
}
fprintf(fp, "}\n");
fclose(fp);
}
+std::vector<std::string> EffectChain::get_labels_for_edge(const Node *from, const Node *to)
+{
+ std::vector<std::string> labels;
+
+ if (to != NULL && to->effect->needs_texture_bounce()) {
+ labels.push_back("needs_bounce");
+ }
+ if (from->effect->changes_output_size()) {
+ labels.push_back("resize");
+ }
+
+ switch (from->output_color_space) {
+ case COLORSPACE_INVALID:
+ labels.push_back("spc[invalid]");
+ break;
+ case COLORSPACE_REC_601_525:
+ labels.push_back("spc[rec601-525]");
+ break;
+ case COLORSPACE_REC_601_625:
+ labels.push_back("spc[rec601-625]");
+ break;
+ default:
+ break;
+ }
+
+ switch (from->output_gamma_curve) {
+ case GAMMA_INVALID:
+ labels.push_back("gamma[invalid]");
+ break;
+ case GAMMA_sRGB:
+ labels.push_back("gamma[sRGB]");
+ break;
+ case GAMMA_REC_601: // and GAMMA_REC_709
+ labels.push_back("gamma[rec601/709]");
+ break;
+ default:
+ break;
+ }
+
+ switch (from->output_alpha_type) {
+ case ALPHA_INVALID:
+ labels.push_back("alpha[invalid]");
+ break;
+ case ALPHA_BLANK:
+ labels.push_back("alpha[blank]");
+ break;
+ case ALPHA_POSTMULTIPLIED:
+ labels.push_back("alpha[postmult]");
+ break;
+ default:
+ break;
+ }
+
+ return labels;
+}
+
+void EffectChain::output_dot_edge(FILE *fp,
+ const std::string &from_node_id,
+ const std::string &to_node_id,
+ const std::vector<std::string> &labels)
+{
+ if (labels.empty()) {
+ fprintf(fp, " %s -> %s;\n", from_node_id.c_str(), to_node_id.c_str());
+ } else {
+ std::string label = labels[0];
+ for (unsigned k = 1; k < labels.size(); ++k) {
+ label += ", " + labels[k];
+ }
+ fprintf(fp, " %s -> %s [label=\"%s\"];\n", from_node_id.c_str(), to_node_id.c_str(), label.c_str());
+ }
+}
+
unsigned EffectChain::fit_rectangle_to_aspect(unsigned width, unsigned height)
{
if (float(width) * aspect_denom >= float(height) * aspect_nom) {
phase->output_height = best_width * aspect_denom / aspect_nom;
}
-void EffectChain::sort_nodes_topologically()
+void EffectChain::sort_all_nodes_topologically()
+{
+ nodes = topological_sort(nodes);
+}
+
+std::vector<Node *> EffectChain::topological_sort(const std::vector<Node *> &nodes)
{
- std::set<Node *> visited_nodes;
+ std::set<Node *> nodes_left_to_visit(nodes.begin(), nodes.end());
std::vector<Node *> sorted_list;
for (unsigned i = 0; i < nodes.size(); ++i) {
- if (nodes[i]->incoming_links.size() == 0) {
- topological_sort_visit_node(nodes[i], &visited_nodes, &sorted_list);
- }
+ topological_sort_visit_node(nodes[i], &nodes_left_to_visit, &sorted_list);
}
reverse(sorted_list.begin(), sorted_list.end());
- nodes = sorted_list;
+ return sorted_list;
}
-void EffectChain::topological_sort_visit_node(Node *node, std::set<Node *> *visited_nodes, std::vector<Node *> *sorted_list)
+void EffectChain::topological_sort_visit_node(Node *node, std::set<Node *> *nodes_left_to_visit, std::vector<Node *> *sorted_list)
{
- if (visited_nodes->count(node) != 0) {
+ if (nodes_left_to_visit->count(node) == 0) {
return;
}
- visited_nodes->insert(node);
+ nodes_left_to_visit->erase(node);
for (unsigned i = 0; i < node->outgoing_links.size(); ++i) {
- topological_sort_visit_node(node->outgoing_links[i], visited_nodes, sorted_list);
+ topological_sort_visit_node(node->outgoing_links[i], nodes_left_to_visit, sorted_list);
}
sorted_list->push_back(node);
}
default:
assert(false);
}
+
+ if (node->output_alpha_type == ALPHA_PREMULTIPLIED) {
+ assert(node->output_gamma_curve == GAMMA_LINEAR);
+ }
}
}
}
void EffectChain::propagate_gamma_and_color_space()
{
// We depend on going through the nodes in order.
- sort_nodes_topologically();
+ sort_all_nodes_topologically();
for (unsigned i = 0; i < nodes.size(); ++i) {
Node *node = nodes[i];
void EffectChain::propagate_alpha()
{
// We depend on going through the nodes in order.
- sort_nodes_topologically();
+ sort_all_nodes_topologically();
for (unsigned i = 0; i < nodes.size(); ++i) {
Node *node = nodes[i];
void EffectChain::finalize()
{
+ // Save the current locale, and set it to C, so that we can output decimal
+ // numbers with printf and be sure to get them in the format mandated by GLSL.
+ char *saved_locale = setlocale(LC_NUMERIC, "C");
+
// Output the graph as it is before we do any conversions on it.
output_dot("step0-start.dot");
assert(phases[0]->inputs.empty());
finalized = true;
+ setlocale(LC_NUMERIC, saved_locale);
}
void EffectChain::render_to_fbo(GLuint dest_fbo, unsigned width, unsigned height)