This kills a lot of the assumptions that have been going around,
and should allow us to deal much better with the situation when
we have two or more inputs to an effect (where you basically can't
predict the sampler number used reliably); there's still an edge
case that's documented with a TODO, but this is generally much better.
assert(middle->incoming_links.size() == middle->effect->num_inputs());
}
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 &&
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];
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) {
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 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();
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;
+ }
// phases as inputs, instead of Node.
Phase *phase;
// 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;
// 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);
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.
// 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; }
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 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)
{
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();
}
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) {
};
TEST(EffectChainTest, MipmapGenerationWorks) {
#include <GL/glew.h>
#include <math.h>
#include <GL/glew.h>
#include <math.h>
+#include "effect_chain.h"
#include "effect_util.h"
#include "fp16.h"
#include "fft_pass_effect.h"
#include "effect_util.h"
#include "fp16.h"
#include "fft_pass_effect.h"
int input_size = (direction == VERTICAL) ? input_height : input_width;
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();
check_error();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
check_error();
*width = *virtual_width = input_width;
*height = *virtual_height = input_height;
}
*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:
enum Direction { HORIZONTAL = 0, VERTICAL = 1 };
private:
int input_width, input_height;
GLuint tex;
int fft_size;
int input_width, input_height;
GLuint tex;
int fft_size;
// We specifically do not want mipmaps on the input texture;
// they break minification.
// 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();
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 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);
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;
void update_texture(GLuint glsl_program_num, const std::string &prefix, unsigned *sampler_num);
ResampleEffect *parent;
Direction direction;
GLuint texnum;
int input_width, input_height, output_width, output_height;
Direction direction;
GLuint texnum;
int input_width, input_height, output_width, output_height;
+#include "effect_chain.h"
#include "slice_effect.h"
#include "effect_util.h"
#include "util.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));
}
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();
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);
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:
enum Direction { HORIZONTAL = 0, VERTICAL = 1 };
private:
int input_width, input_height;
int input_slice_size, output_slice_size;
Direction direction;
int input_width, input_height;
int input_slice_size, output_slice_size;
Direction direction;