assert(middle->incoming_links.size() == middle->effect->num_inputs());
}
+GLenum EffectChain::get_input_sampler(Node *node, unsigned input_num) const
+{
+ assert(node->effect->needs_texture_bounce());
+ assert(input_num < node->incoming_links.size());
+ assert(node->incoming_links[input_num]->bound_sampler_num >= 0);
+ assert(node->incoming_links[input_num]->bound_sampler_num < 8);
+ return GL_TEXTURE0 + node->incoming_links[input_num]->bound_sampler_num;
+}
+
void EffectChain::find_all_nonlinear_inputs(Node *node, vector<Node *> *nonlinear_inputs)
{
if (node->output_gamma_curve == GAMMA_LINEAR &&
for (unsigned sampler = 0; sampler < phases[phase]->inputs.size(); ++sampler) {
glActiveTexture(GL_TEXTURE0 + sampler);
Node *input = phases[phase]->inputs[sampler];
+ input->bound_sampler_num = sampler;
glBindTexture(GL_TEXTURE_2D, output_textures[input->phase]);
check_error();
if (phases[phase]->input_needs_mipmaps) {
unsigned sampler_num = phases[phase]->inputs.size();
for (unsigned i = 0; i < phases[phase]->effects.size(); ++i) {
Node *node = phases[phase]->effects[i];
+ unsigned old_sampler_num = sampler_num;
node->effect->set_gl_state(glsl_program_num, phases[phase]->effect_ids[node], &sampler_num);
check_error();
+
+ if (node->effect->is_single_texture()) {
+ assert(sampler_num - old_sampler_num == 1);
+ node->bound_sampler_num = old_sampler_num;
+ } else {
+ node->bound_sampler_num = -1;
+ }
}
// Now draw!
// phases as inputs, instead of Node.
Phase *phase;
+ // If the effect has is_single_texture(), or if the output went to RTT
+ // and that texture has been bound to a sampler, the sampler number
+ // will be stored here.
+ //
+ // TODO: Can an RTT texture be used as inputs to multiple effects
+ // within the same phase? If so, we have a problem with modifying
+ // sampler state here.
+ int bound_sampler_num;
+
// Used during the building of the effect chain.
Colorspace output_color_space;
GammaCurve output_gamma_curve;
void replace_receiver(Node *old_receiver, Node *new_receiver);
void replace_sender(Node *new_sender, Node *receiver);
void insert_node_between(Node *sender, Node *middle, Node *receiver);
+ Node *find_node_for_effect(Effect *effect) { return node_map[effect]; }
+
+ // Get the OpenGL sampler (GL_TEXTURE0, GL_TEXTURE1, etc.) for the
+ // input of the given node, so that one can modify the sampler state
+ // directly. Only valid to call during set_gl_state().
+ //
+ // Also, for this to be allowed, <node>'s effect must have
+ // needs_texture_bounce() set, so that it samples directly from a
+ // single-sampler input, or from an RTT texture.
+ GLenum get_input_sampler(Node *node, unsigned input_num) const;
// Get the current resource pool assigned to this EffectChain.
// Primarily to let effects allocate textures as needed.
public:
MipmapNeedingEffect() {}
virtual bool needs_mipmaps() const { return true; }
+
+ // To be allowed to mess with the sampler state.
+ virtual bool needs_texture_bounce() const { return true; }
+
virtual string effect_type_id() const { return "MipmapNeedingEffect"; }
string output_fragment_shader() { return read_file("mipmap_needing_effect.frag"); }
+ virtual void inform_added(EffectChain *chain) { this->chain = chain; }
+
void set_gl_state(GLuint glsl_program_num, const string& prefix, unsigned *sampler_num)
{
- glActiveTexture(GL_TEXTURE0);
+ Node *self = chain->find_node_for_effect(this);
+ glActiveTexture(chain->get_input_sampler(self, 0));
check_error();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
check_error();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
check_error();
}
+
+private:
+ EffectChain *chain;
};
TEST(EffectChainTest, MipmapGenerationWorks) {
#include <GL/glew.h>
#include <math.h>
+#include "effect_chain.h"
#include "effect_util.h"
#include "fp16.h"
#include "fft_pass_effect.h"
int input_size = (direction == VERTICAL) ? input_height : input_width;
- // See the comments on changes_output_size() in the .h file to see
- // why this is legal. It is _needed_ because it counteracts the
- // precision issues we get because we sample the input texture with
- // normalized coordinates (especially when the repeat count along
- // the axis is not a power of two); we very rapidly end up in narrowly
- // missing a texel center, which causes precision loss to propagate
- // throughout the FFT.
- assert(*sampler_num == 1);
- glActiveTexture(GL_TEXTURE0);
+ // This is needed because it counteracts the precision issues we get
+ // because we sample the input texture with normalized coordinates
+ // (especially when the repeat count along the axis is not a power of
+ // two); we very rapidly end up in narrowly missing a texel center,
+ // which causes precision loss to propagate throughout the FFT.
+ Node *self = chain->find_node_for_effect(this);
+ glActiveTexture(chain->get_input_sampler(self, 0));
check_error();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
check_error();
*width = *virtual_width = input_width;
*height = *virtual_height = input_height;
}
+
+ virtual void inform_added(EffectChain *chain) { this->chain = chain; }
enum Direction { HORIZONTAL = 0, VERTICAL = 1 };
private:
+ EffectChain *chain;
int input_width, input_height;
GLuint tex;
int fft_size;
// We specifically do not want mipmaps on the input texture;
// they break minification.
- glActiveTexture(GL_TEXTURE0);
+ Node *self = chain->find_node_for_effect(this);
+ glActiveTexture(chain->get_input_sampler(self, 0));
check_error();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
check_error();
virtual bool needs_srgb_primaries() const { return false; }
virtual AlphaHandling alpha_handling() const { return INPUT_PREMULTIPLIED_ALPHA_KEEP_BLANK; }
+ virtual void inform_added(EffectChain *chain) { this->chain = chain; }
virtual void inform_input_size(unsigned input_num, unsigned width, unsigned height) {
if (parent != NULL) {
parent->inform_input_size(input_num, width, height);
void update_texture(GLuint glsl_program_num, const std::string &prefix, unsigned *sampler_num);
ResampleEffect *parent;
+ EffectChain *chain;
Direction direction;
GLuint texnum;
int input_width, input_height, output_width, output_height;
#include <GL/glew.h>
+#include "effect_chain.h"
#include "slice_effect.h"
#include "effect_util.h"
#include "util.h"
set_uniform_float(glsl_program_num, prefix, "slice_offset_to_input_coord", float(output_slice_size) / float(input_height));
}
- // Normalized coordinates could potentially cause blurring of the
- // image; it's not critical, but we have set changes_output_size()
- // and needs_texture_bounce(), so simply turning off the interpolation
- // is allowed.
- assert(*sampler_num == 1);
- glActiveTexture(GL_TEXTURE0);
+ // Normalized coordinates could potentially cause blurring of the image.
+ // It isn't critical, but still good practice.
+ Node *self = chain->find_node_for_effect(this);
+ glActiveTexture(chain->get_input_sampler(self, 0));
check_error();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
check_error();
unsigned *virtual_width, unsigned *virtual_height) const;
void set_gl_state(GLuint glsl_program_num, const std::string &prefix, unsigned *sampler_num);
+ virtual void inform_added(EffectChain *chain) { this->chain = chain; }
enum Direction { HORIZONTAL = 0, VERTICAL = 1 };
private:
+ EffectChain *chain;
int input_width, input_height;
int input_slice_size, output_slice_size;
Direction direction;