// Keeps the type of alpha (premultiplied, postmultiplied, blank)
// unchanged from input to output. Usually appropriate if you
// process all color channels in a linear fashion, do not change
- // alpha, and do not produce any new pixels thare have alpha != 1.0.
+ // alpha, and do not produce any new pixels that have alpha != 1.0.
//
// Does not make sense for inputs.
DONT_CARE_ALPHA_TYPE,
// 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:
// 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
// and allow dependent effects to change that sampler state.
virtual bool is_single_texture() const { return false; }
+ // If set, this effect should never be bounced to an output, even if a
+ // dependent effect demands texture bounce.
+ //
+ // Note that setting this can invoke undefined behavior, up to and including crashing,
+ // so you should only use it if you have deep understanding of your entire chain
+ // and Movit's processing of it. The most likely use case is if you have an input
+ // that's cheap to compute but not a single texture (e.g. YCbCrInput), and want
+ // to run a ResampleEffect directly from it. Normally, this would require a bounce,
+ // but it's faster not to. (However, also note that in this case, effective texel
+ // subpixel precision will be too optimistic, since chroma is already subsampled.)
+ //
+ // Has no effect if is_single_texture() is set.
+ virtual bool override_disable_bounce() const { return false; }
+
// If changes_output_size() is true, you must implement this to tell
// the framework what output size you want. Also, you can set a
// virtual width/height, which is the size the next effect (if any)
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.
// 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;
//
// Neither of these take ownership of the pointer.
- // int is special since GLSL pre-1.30 doesn't have integer uniforms.
- // Thus, ints that you register will _not_ be converted to GLSL uniforms.
+ // These correspond directly to int/float/vec2/vec3/vec4 in GLSL.
void register_int(const std::string &key, int *value);
-
- // These correspond directly to float/vec2/vec3/vec4 in GLSL.
+ 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);
//
// Calling register_* will automatically imply register_uniform_*,
// except for register_int as noted above.
- 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_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);
+ 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);
void register_uniform_vec4(const std::string &key, const float *values);
+ void register_uniform_float_array(const std::string &key, const float *values, size_t num_values);
void register_uniform_vec2_array(const std::string &key, const float *values, size_t num_values);
+ void register_uniform_vec3_array(const std::string &key, const float *values, size_t num_values);
void register_uniform_vec4_array(const std::string &key, const float *values, size_t num_values);
void register_uniform_mat3(const std::string &key, const Eigen::Matrix3d *matrix);
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<bool> > uniforms_bool;
- std::vector<Uniform<int> > uniforms_int;
- std::vector<Uniform<int> > uniforms_sampler2d;
- 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_vec2_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;
};