]> git.sesse.net Git - movit/blobdiff - effect.h
Release Movit 1.6.3.
[movit] / effect.h
index e10fab80759fd2359571fb6ff48d71a56d894aca..f404d3994adbfc6aa7c57647a0e7316ad1b37604 100644 (file)
--- a/effect.h
+++ b/effect.h
@@ -169,12 +169,37 @@ public:
        // either will be fine.
        virtual bool needs_texture_bounce() const { return false; }
 
-       // Whether this effect expects mipmaps or not. If you set this to
-       // true, you will be sampling with bilinear filtering; if not,
-       // you could be sampling with simple linear filtering and no mipmaps
-       // (although there is no guarantee; if a different effect in the chain
-       // needs mipmaps, you will also get them).
-       virtual bool needs_mipmaps() const { return false; }
+       // Whether this effect expects mipmaps or not.
+       enum MipmapRequirements {
+               // If chosen, you will be sampling with bilinear filtering,
+               // ie. the closest mipmap will be chosen, and then there will be
+               // bilinear interpolation inside it (GL_LINEAR_MIPMAP_NEAREST).
+               NEEDS_MIPMAPS,
+
+               // Whether the effect doesn't really care whether input textures
+               // are with or without mipmaps. You could get the same effect
+               // as NEEDS_MIPMAPS or CANNOT_ACCEPT_MIPMAPS; normally, you won't
+               // get them, but if a different effect in the same phase needs mipmaps,
+               // you will also get them.
+               DOES_NOT_NEED_MIPMAPS,
+
+               // The opposite of NEEDS_MIPMAPS; you will always be sampling from
+               // the most detailed mip level (GL_LINEAR). Effects with NEEDS_MIPMAPS
+               // and CANNOT_ACCEPT_MIPMAPS can not coexist within the same phase;
+               // such phases will be split.
+               //
+               // This is the only choice that makes sense for a compute shader,
+               // given that it doesn't have screen-space derivatives and thus
+               // always will sample the most detailed mip level.
+               CANNOT_ACCEPT_MIPMAPS,
+       };
+       virtual MipmapRequirements needs_mipmaps() const {
+               if (is_compute_shader()) {
+                       return CANNOT_ACCEPT_MIPMAPS;
+               } else {
+                       return DOES_NOT_NEED_MIPMAPS;
+               }
+       }
 
        // Whether there is a direct correspondence between input and output
        // texels. Specifically, the effect must not:
@@ -196,7 +221,16 @@ public:
        // set sets_virtual_output_size(), though.
        //
        // Does not make a lot of sense together with needs_texture_bounce().
-       virtual bool one_to_one_sampling() const { return false; }
+       // Cannot be set for compute shaders.
+       virtual bool one_to_one_sampling() const { return strong_one_to_one_sampling(); }
+
+       // Similar in use to one_to_one_sampling(), but even stricter:
+       // The effect must not modify texture coordinate in any way when
+       // calling its input(s). This allows it to also be used after
+       // a compute shader, in the same phase.
+       //
+       // An effect that it strong one-to-one must also be one-to-one.
+       virtual bool strong_one_to_one_sampling() const { return false; }
 
        // Whether this effect wants to output to a different size than
        // its input(s) (see inform_input_size(), below). See also
@@ -245,6 +279,45 @@ public:
                assert(false);
        }
 
+       // Whether this effect uses a compute shader instead of a regular fragment shader.
+       // Compute shaders are more flexible in that they can have multiple outputs
+       // for each invocation and also communicate between instances (by using shared
+       // memory within each group), but are not universally supported. The typical
+       // pattern would be to check movit_compute_shaders_supported and rewrite the
+       // graph to use a compute shader effect instead of a regular effect if it is
+       // available, in order to get better performance. Since compute shaders can reuse
+       // loads (again typically through shared memory), using needs_texture_bounce()
+       // is usually not needed, although it is allowed; the best candidates for compute
+       // shaders are typically those that sample many times from their input
+       // but can reuse those loads across neighboring instances.
+       //
+       // Compute shaders commonly work with unnormalized texture coordinates
+       // (where coordinates are integers [0..W) and [0..H)), whereas the rest
+       // of Movit, including any inputs you may want to sample from, works
+       // with normalized coordinates ([0..1)). Movit gives you uniforms
+       // PREFIX(inv_output_size) and PREFIX(output_texcoord_adjust) that you
+       // can use to transform unnormalized to normalized, as well as a macro
+       // NORMALIZE_TEXTURE_COORDS(vec2) that does it for you.
+       //
+       // Since compute shaders have flexible output, it is difficult to chain other
+       // effects after them in the same phase, and thus, they will always be last.
+       // (This limitation may be lifted for the special case of one-to-one effects
+       // in the future.) Furthermore, they cannot write to the framebuffer, just to
+       // textures, so Movit may have to insert an extra phase just to do the output
+       // from a texture to the screen in some cases. However, this is transparent
+       // to both the effect and the user.
+       virtual bool is_compute_shader() const { return false; }
+
+       // For a compute shader (see the previous member function), what dimensions
+       // it should be invoked over. Called every frame, before uniforms are set
+       // (so you are allowed to update uniforms based from this call).
+       virtual void get_compute_dimensions(unsigned output_width, unsigned output_height,
+                                           unsigned *x, unsigned *y, unsigned *z) const {
+               *x = output_width;
+               *y = output_height;
+               *z = 1;
+       }
+
        // 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.
@@ -301,7 +374,8 @@ public:
 
        // Set a parameter; intended to be called from user code.
        // Neither of these take ownership of the pointer.
-       virtual bool set_int(const std::string&, int value) MUST_CHECK_RESULT;
+       virtual bool set_int(const std::string &key, int value) MUST_CHECK_RESULT;
+       virtual bool set_ivec2(const std::string &key, const int *values) MUST_CHECK_RESULT;
        virtual bool set_float(const std::string &key, float value) MUST_CHECK_RESULT;
        virtual bool set_vec2(const std::string &key, const float *values) MUST_CHECK_RESULT;
        virtual bool set_vec3(const std::string &key, const float *values) MUST_CHECK_RESULT;
@@ -318,6 +392,7 @@ protected:
 
        // These correspond directly to int/float/vec2/vec3/vec4 in GLSL.
        void register_int(const std::string &key, int *value);
+       void register_ivec2(const std::string &key, int *values);
        void register_float(const std::string &key, float *value);
        void register_vec2(const std::string &key, float *values);
        void register_vec3(const std::string &key, float *values);
@@ -345,7 +420,8 @@ protected:
        // except for register_int as noted above.
        void register_uniform_sampler2d(const std::string &key, const int *value);
        void register_uniform_bool(const std::string &key, const bool *value);
-       void register_uniform_int(const std::string &key, const int *value);  // Note: Requires GLSL 1.30 or newer.
+       void register_uniform_int(const std::string &key, const int *value);
+       void register_uniform_ivec2(const std::string &key, const int *values);
        void register_uniform_float(const std::string &key, const float *value);
        void register_uniform_vec2(const std::string &key, const float *values);
        void register_uniform_vec3(const std::string &key, const float *values);
@@ -358,24 +434,27 @@ protected:
 
 private:
        std::map<std::string, int *> params_int;
+       std::map<std::string, int *> params_ivec2;
        std::map<std::string, float *> params_float;
        std::map<std::string, float *> params_vec2;
        std::map<std::string, float *> params_vec3;
        std::map<std::string, float *> params_vec4;
 
        // Picked out by EffectChain during finalization.
-       std::vector<Uniform<int> > uniforms_sampler2d;
-       std::vector<Uniform<bool> > uniforms_bool;
-       std::vector<Uniform<int> > uniforms_int;
-       std::vector<Uniform<float> > uniforms_float;
-       std::vector<Uniform<float> > uniforms_vec2;
-       std::vector<Uniform<float> > uniforms_vec3;
-       std::vector<Uniform<float> > uniforms_vec4;
-       std::vector<Uniform<float> > uniforms_float_array;
-       std::vector<Uniform<float> > uniforms_vec2_array;
-       std::vector<Uniform<float> > uniforms_vec3_array;
-       std::vector<Uniform<float> > uniforms_vec4_array;
-       std::vector<Uniform<Eigen::Matrix3d> > uniforms_mat3;
+       std::vector<Uniform<int>> uniforms_image2d;
+       std::vector<Uniform<int>> uniforms_sampler2d;
+       std::vector<Uniform<bool>> uniforms_bool;
+       std::vector<Uniform<int>> uniforms_int;
+       std::vector<Uniform<int>> uniforms_ivec2;
+       std::vector<Uniform<float>> uniforms_float;
+       std::vector<Uniform<float>> uniforms_vec2;
+       std::vector<Uniform<float>> uniforms_vec3;
+       std::vector<Uniform<float>> uniforms_vec4;
+       std::vector<Uniform<float>> uniforms_float_array;
+       std::vector<Uniform<float>> uniforms_vec2_array;
+       std::vector<Uniform<float>> uniforms_vec3_array;
+       std::vector<Uniform<float>> uniforms_vec4_array;
+       std::vector<Uniform<Eigen::Matrix3d>> uniforms_mat3;
        friend class EffectChain;
 };