]> git.sesse.net Git - movit/commitdiff
Formalize the notion of messing with sampler state.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Mon, 10 Mar 2014 22:59:28 +0000 (23:59 +0100)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Mon, 10 Mar 2014 23:15:31 +0000 (00:15 +0100)
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.

effect_chain.cpp
effect_chain.h
effect_chain_test.cpp
fft_pass_effect.cpp
fft_pass_effect.h
resample_effect.cpp
resample_effect.h
slice_effect.cpp
slice_effect.h

index 22ba189013d0961dcd00f10212a3abca6309875e..c886155a19dc96324fbbca76ec1cc7d5e571b53d 100644 (file)
@@ -149,6 +149,15 @@ void EffectChain::insert_node_between(Node *sender, Node *middle, Node *receiver
        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 &&
@@ -1480,6 +1489,7 @@ void EffectChain::render_to_fbo(GLuint dest_fbo, unsigned width, unsigned height
                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) {
@@ -1533,8 +1543,16 @@ void EffectChain::render_to_fbo(GLuint dest_fbo, unsigned width, unsigned height
                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;
+                       }
                }
 
                // Now draw!
                }
 
                // Now draw!
index 642f079201f037219b1ead3c02aebf3e85bfb28d..4417de912f9f979a8308f96e6291246c3c73841c 100644 (file)
@@ -71,6 +71,15 @@ private:
        // 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;
@@ -173,6 +182,16 @@ public:
        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.
index cf725f1131bb1eae8a1722c1e2989893d8c178be..b1f0815cc62d80b604ca5a4fc0f1618d941ca913 100644 (file)
@@ -522,17 +522,27 @@ class MipmapNeedingEffect : public Effect {
 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) {
index c2b47f047c0747459b92682895be7b9a147f5de2..9c8aeb058e71fa33435901d4f5a2588a38ccb4f3 100644 (file)
@@ -1,6 +1,7 @@
 #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"
@@ -40,15 +41,13 @@ void FFTPassEffect::set_gl_state(GLuint glsl_program_num, const string &prefix,
 
        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();
index 1fd4ea2c1886ed0bd6fb1f358954989770e0e5da..460a946be4dcbdb24f8bddfcc011cb3cdb6af6a0 100644 (file)
@@ -98,10 +98,13 @@ public:
                *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:
+       EffectChain *chain;
        int input_width, input_height;
        GLuint tex;
        int fft_size;
        int input_width, input_height;
        GLuint tex;
        int fft_size;
index 4af60de7d3c47c6089a560f0d23c2ce62594cd81..a6774873daa3b5b58be975300f97839cc5562a56 100644 (file)
@@ -413,7 +413,8 @@ void SingleResamplePassEffect::set_gl_state(GLuint glsl_program_num, const strin
 
        // 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();
index 628c61a348ea3ba22904370a4a572f753dfed8aa..44587bf22f9200a1f79147c1ced2b9bf40e82dbb 100644 (file)
@@ -73,6 +73,7 @@ public:
        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);
@@ -93,6 +94,7 @@ private:
        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;
+       EffectChain *chain;
        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;
index 09b91b90a9a4b03c291875948f844ea63590194e..15f539263af7be2da36a5fb9ba8da5b0351e5d1c 100644 (file)
@@ -1,5 +1,6 @@
 #include <GL/glew.h>
 
 #include <GL/glew.h>
 
+#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"
@@ -60,12 +61,10 @@ void SliceEffect::set_gl_state(GLuint glsl_program_num, const string &prefix, un
                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();
index 6380c1665279078623a802c9ccf2f7db4f17574d..17202d77c97577e720cf023426c8c28f095ab8c5 100644 (file)
@@ -29,10 +29,12 @@ public:
                                     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:
+       EffectChain *chain;
        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;