From 048e90293b5f5ed4b922d60c4cb47c4b5d24b85e Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Tue, 9 Oct 2012 12:04:18 +0200 Subject: [PATCH] Spread resolution information throughout the effect chain. --- effect.h | 18 +++++++++++--- effect_chain.cpp | 63 ++++++++++++++++++++++++++++++++++++++++++++++-- effect_chain.h | 11 +++++++++ 3 files changed, 87 insertions(+), 5 deletions(-) diff --git a/effect.h b/effect.h index cbf6f3a..da4f74d 100644 --- a/effect.h +++ b/effect.h @@ -107,9 +107,9 @@ public: virtual bool needs_mipmaps() const { return false; } // Whether this effect wants to output to a different size than - // its input(s). If you set this to true, the output will be - // bounced to a texture (similarly to if the next effect set - // needs_texture_bounce()). + // its input(s) (see inform_input_size(), below). If you set this to + // true, the output will be bounced to a texture (similarly to if the + // next effect set needs_texture_bounce()). virtual bool changes_output_size() const { return false; } // If changes_output_size() is true, you must implement this to tell @@ -121,6 +121,18 @@ public: assert(false); } + // Tells the effect the resolution of each of its input. + // This will be called every frame, and always before get_output_size(), + // so you can change your output size based on the input if so desired. + // + // Note that in some cases, an input might not have a single well-defined + // resolution (for instance if you fade between two inputs with + // different resolutions). In this case, you will get width=0 and height=0 + // for that input. If you cannot handle that, you will need to set + // needs_texture_bounce() to true, which will force a render to a single + // given resolution before you get the input. + virtual void inform_input_size(unsigned input_num, unsigned width, unsigned height) {} + // How many inputs this effect will take (a fixed number). // If you have only one input, it will be called INPUT() in GLSL; // if you have several, they will be INPUT1(), INPUT2(), and so on. diff --git a/effect_chain.cpp b/effect_chain.cpp index 87d0663..4eda18d 100644 --- a/effect_chain.cpp +++ b/effect_chain.cpp @@ -450,12 +450,69 @@ unsigned EffectChain::fit_rectangle_to_aspect(unsigned width, unsigned height) } } +// Propagate input texture sizes throughout, and inform effects downstream. +// (Like a lot of other code, we depend on effects being in topological order.) +void EffectChain::inform_input_sizes(Phase *phase) +{ + // All effects that have a defined size (inputs and RTT inputs) + // get that. Reset all others. + for (unsigned i = 0; i < phase->effects.size(); ++i) { + Node *node = phase->effects[i]; + if (node->effect->num_inputs() == 0) { + Input *input = static_cast(node->effect); + node->output_width = input->get_width(); + node->output_height = input->get_height(); + assert(node->output_width != 0); + assert(node->output_height != 0); + } else { + node->output_width = node->output_height = 0; + } + } + for (unsigned i = 0; i < phase->inputs.size(); ++i) { + Node *input = phase->inputs[i]; + input->output_width = input->phase->output_width; + input->output_height = input->phase->output_height; + assert(input->output_width != 0); + assert(input->output_height != 0); + } + + // Now propagate from the inputs towards the end, and inform as we go. + // The rules are simple: + // + // 1. Don't touch effects that already have given sizes (ie., inputs). + // 2. If all of your inputs have the same size, that will be your output size. + // 3. Otherwise, your output size is 0x0. + for (unsigned i = 0; i < phase->effects.size(); ++i) { + Node *node = phase->effects[i]; + if (node->effect->num_inputs() == 0) { + continue; + } + unsigned this_output_width = 0; + unsigned this_output_height = 0; + for (unsigned j = 0; j < node->incoming_links.size(); ++j) { + Node *input = node->incoming_links[j]; + node->effect->inform_input_size(j, input->output_width, input->output_height); + if (j == 0) { + this_output_width = input->output_width; + this_output_height = input->output_height; + } else if (input->output_width != this_output_width || input->output_height != this_output_height) { + // Inputs disagree. + this_output_width = 0; + this_output_height = 0; + } + } + node->output_width = this_output_width; + node->output_height = this_output_height; + } +} + +// Note: You should call inform_input_sizes() before this, as the last effect's +// desired output size might change based on the inputs. void EffectChain::find_output_size(Phase *phase) { Node *output_node = phase->effects.back(); - // If the last effect explicitly sets an output size, - // use that. + // If the last effect explicitly sets an output size, use that. if (output_node->effect->changes_output_size()) { output_node->effect->get_output_size(&phase->output_width, &phase->output_height); return; @@ -843,6 +900,7 @@ void EffectChain::finalize() glGenFramebuffers(1, &fbo); for (unsigned i = 0; i < phases.size() - 1; ++i) { + inform_input_sizes(phases[i]); find_output_size(phases[i]); Node *output_node = phases[i]->effects.back(); @@ -905,6 +963,7 @@ void EffectChain::render_to_screen() // See if the requested output size has changed. If so, we need to recreate // the texture (and before we start setting up inputs). if (phase != phases.size() - 1) { + inform_input_sizes(phases[phase]); find_output_size(phases[phase]); Node *output_node = phases[phase]->effects.back(); diff --git a/effect_chain.h b/effect_chain.h index 48b6087..1c50b16 100644 --- a/effect_chain.h +++ b/effect_chain.h @@ -25,6 +25,13 @@ private: // Identifier used to create unique variables in GLSL. std::string effect_id; + // Logical size of the output of this effect, ie. the resolution + // you would get if you sampled it as a texture. If it is undefined + // (since the inputs differ in resolution), it will be 0x0. + // If both this and output_texture_{width,height} are set, + // they will be equal. + unsigned output_width, output_height; + // If output goes to RTT (otherwise, none of these are set). // The Phase pointer is a but ugly; we should probably fix so // that Phase takes other phases as inputs, instead of Node. @@ -111,6 +118,10 @@ private: // (aspect_nom/aspect_denom) and returns the new width and height. unsigned fit_rectangle_to_aspect(unsigned width, unsigned height); + // Compute the input sizes for all inputs for all effects in a given phase, + // and inform the effects about the results. + void inform_input_sizes(Phase *phase); + // Determine the preferred output size of a given phase. // Requires that all input phases (if any) already have output sizes set. void find_output_size(Phase *phase); -- 2.39.2