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
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.
}
}
+// 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<Input *>(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;
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();
// 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();
// 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.
// (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);