num_taps(16),
radius(3.0f),
direction(HORIZONTAL),
- width(1280),
- height(720)
+ width(1280),
+ height(720),
+ uniform_samples(NULL)
{
register_float("radius", &radius);
register_int("direction", (int *)&direction);
register_int("num_taps", &num_taps);
}
+SingleBlurPassEffect::~SingleBlurPassEffect()
+{
+ delete[] uniform_samples;
+}
+
string SingleBlurPassEffect::output_fragment_shader()
{
char buf[256];
sprintf(buf, "#define DIRECTION_VERTICAL %d\n#define NUM_TAPS %d\n",
(direction == VERTICAL), num_taps);
+ uniform_samples = new float[2 * (num_taps / 2 + 1)];
+ register_uniform_vec2_array("samples", uniform_samples, num_taps / 2 + 1);
return buf + read_file("blur_effect.frag");
}
//
// We pack the parameters into a float4: The relative sample coordinates
// in (x,y), and the weight in z. w is unused.
- float* samples = new float[2 * (num_taps / 2 + 1)];
// Center sample.
- samples[2 * 0 + 0] = 0.0f;
- samples[2 * 0 + 1] = weight[0];
+ uniform_samples[2 * 0 + 0] = 0.0f;
+ uniform_samples[2 * 0 + 1] = weight[0];
// All other samples.
for (int i = 1; i < num_taps / 2 + 1; ++i) {
float pos, total_weight;
combine_two_samples(w1, w2, pos1, pos2, size, &pos, &total_weight, NULL);
- samples[2 * i + 0] = pos;
- samples[2 * i + 1] = total_weight;
+ uniform_samples[2 * i + 0] = pos;
+ uniform_samples[2 * i + 1] = total_weight;
}
- set_uniform_vec2_array(glsl_program_num, prefix, "samples", samples, num_taps / 2 + 1);
-
delete[] weight;
- delete[] samples;
}
void SingleBlurPassEffect::clear_gl_state()
// DIRECTION_VERTICAL will be #defined to 1 if we are doing a vertical blur,
// 0 otherwise.
-uniform vec2 PREFIX(samples)[NUM_TAPS / 2 + 1];
+// Implicit uniforms:
+// uniform vec2 PREFIX(samples)[NUM_TAPS / 2 + 1];
vec4 FUNCNAME(vec2 tc) {
vec4 sum = vec4(PREFIX(samples)[0].y) * INPUT(tc);
// If parent is non-NULL, calls to inform_input_size will be forwarded
// so that it can make reasonable decisions for both blur passes.
SingleBlurPassEffect(BlurEffect *parent);
+ virtual ~SingleBlurPassEffect();
virtual std::string effect_type_id() const { return "SingleBlurPassEffect"; }
std::string output_fragment_shader();
float radius;
Direction direction;
int width, height, virtual_width, virtual_height;
+ float *uniform_samples;
};
} // namespace movit
{
register_int("num_repeats_x", &num_repeats_x);
register_int("num_repeats_y", &num_repeats_y);
+ register_vec2("num_repeats", uniform_num_repeats);
}
string ComplexModulateEffect::output_fragment_shader()
{
Effect::set_gl_state(glsl_program_num, prefix, sampler_num);
- float num_repeats[] = { float(num_repeats_x), float(num_repeats_y) };
- set_uniform_vec2(glsl_program_num, prefix, "num_repeats", num_repeats);
+ uniform_num_repeats[0] = float(num_repeats_x);
+ uniform_num_repeats[1] = float(num_repeats_y);
// Set the secondary input to repeat (and nearest while we're at it).
Node *self = chain->find_node_for_effect(this);
-uniform vec2 PREFIX(num_repeats);
+// Implicit uniforms:
+// uniform vec2 PREFIX(num_repeats);
vec4 FUNCNAME(vec2 tc) {
vec4 pixel = INPUT1(tc);
EffectChain *chain;
int primary_input_width, primary_input_height;
int num_repeats_x, num_repeats_y;
+ float uniform_num_repeats[2];
};
} // namespace movit
last_circle_radius(-1.0f),
last_gaussian_radius(-1.0f),
last_correlation(-1.0f),
- last_noise(-1.0f)
+ last_noise(-1.0f),
+ uniform_samples(NULL)
{
register_int("matrix_size", &R);
register_float("circle_radius", &circle_radius);
register_float("noise", &noise);
}
+DeconvolutionSharpenEffect::~DeconvolutionSharpenEffect()
+{
+ delete[] uniform_samples;
+}
+
string DeconvolutionSharpenEffect::output_fragment_shader()
{
char buf[256];
assert(R >= 1);
assert(R <= 25); // Same limit as Refocus.
+ uniform_samples = new float[4 * (R + 1) * (R + 1)];
+ register_uniform_vec4_array("samples", uniform_samples, (R + 1) * (R + 1));
+
last_R = R;
return buf + read_file("deconvolution_sharpen_effect.frag");
}
update_deconvolution_kernel();
}
// Now encode it as uniforms, and pass it on to the shader.
- float samples[4 * (R + 1) * (R + 1)];
for (int y = 0; y <= R; ++y) {
for (int x = 0; x <= R; ++x) {
int i = y * (R + 1) + x;
- samples[i * 4 + 0] = x / float(width);
- samples[i * 4 + 1] = y / float(height);
- samples[i * 4 + 2] = g(y, x);
- samples[i * 4 + 3] = 0.0f;
+ uniform_samples[i * 4 + 0] = x / float(width);
+ uniform_samples[i * 4 + 1] = y / float(height);
+ uniform_samples[i * 4 + 2] = g(y, x);
+ uniform_samples[i * 4 + 3] = 0.0f;
}
}
-
- set_uniform_vec4_array(glsl_program_num, prefix, "samples", samples, (R + 1) * (R + 1));
}
} // namespace movit
-uniform vec4 PREFIX(samples)[(R + 1) * (R + 1)];
+// Implicit uniforms:
+// uniform vec4 PREFIX(samples)[(R + 1) * (R + 1)];
vec4 FUNCNAME(vec2 tc) {
// The full matrix has five different symmetry cases, that look like this:
class DeconvolutionSharpenEffect : public Effect {
public:
DeconvolutionSharpenEffect();
+ virtual ~DeconvolutionSharpenEffect();
virtual std::string effect_type_id() const { return "DeconvolutionSharpenEffect"; }
std::string output_fragment_shader();
Eigen::MatrixXf g;
int last_R;
float last_circle_radius, last_gaussian_radius, last_correlation, last_noise;
+
+ float *uniform_samples;
void update_deconvolution_kernel();
};
register_int("output_width", &width);
register_int("output_height", &height);
register_int("num_bits", &num_bits);
+ register_uniform_float("round_fac", &uniform_round_fac);
+ register_uniform_float("inv_round_fac", &uniform_inv_round_fac);
+ register_uniform_vec2("tc_scale", uniform_tc_scale);
+ register_uniform_sampler2d("dither_tex", &uniform_dither_tex);
glGenTextures(1, &texnum);
}
glBindTexture(GL_TEXTURE_2D, texnum);
check_error();
- set_uniform_int(glsl_program_num, prefix, "dither_tex", *sampler_num);
+ uniform_dither_tex = *sampler_num;
++*sampler_num;
// In theory, we should adjust for the texel centers that have moved here as well,
// but since we use GL_NEAREST and we don't really care a lot what texel we sample,
// we don't have to worry about it.
- float tc_scale[] = { float(width) / float(texture_width), float(height) / float(texture_height) };
- set_uniform_vec2(glsl_program_num, prefix, "tc_scale", tc_scale);
+ uniform_tc_scale[0] = float(width) / float(texture_width);
+ uniform_tc_scale[1] = float(height) / float(texture_height);
// Used if the shader needs to do explicit rounding.
int round_fac = (1 << num_bits) - 1;
- set_uniform_float(glsl_program_num, prefix, "round_fac", round_fac);
- set_uniform_float(glsl_program_num, prefix, "inv_round_fac", 1.0f / round_fac);
+ uniform_round_fac = round_fac;
+ uniform_inv_round_fac = 1.0f / round_fac;
}
} // namespace movit
-uniform sampler2D PREFIX(dither_tex);
-uniform vec2 PREFIX(tc_scale);
-uniform float PREFIX(round_fac), PREFIX(inv_round_fac);
+// Implicit uniforms:
+// uniform sampler2D PREFIX(dither_tex);
+// uniform vec2 PREFIX(tc_scale);
+// uniform float PREFIX(round_fac), PREFIX(inv_round_fac);
vec4 FUNCNAME(vec2 tc) {
vec4 result = INPUT(tc);
int texture_width, texture_height;
GLuint texnum;
+ float uniform_round_fac, uniform_inv_round_fac;
+ float uniform_tc_scale[2];
+ GLint uniform_dither_tex;
};
} // namespace movit
#include "effect.h"
#include "effect_util.h"
+using namespace Eigen;
using namespace std;
namespace movit {
{
assert(params_float.count(key) == 0);
params_float[key] = value;
+ register_uniform_float(key, value);
}
void Effect::register_vec2(const string &key, float *values)
{
assert(params_vec2.count(key) == 0);
params_vec2[key] = values;
+ register_uniform_vec2(key, values);
}
void Effect::register_vec3(const string &key, float *values)
{
assert(params_vec3.count(key) == 0);
params_vec3[key] = values;
+ register_uniform_vec3(key, values);
}
void Effect::register_vec4(const string &key, float *values)
{
assert(params_vec4.count(key) == 0);
params_vec4[key] = values;
+ register_uniform_vec4(key, values);
}
-// Output convenience uniforms for each parameter.
-// These will be filled in per-frame.
-string Effect::output_convenience_uniforms() const
+void Effect::set_gl_state(GLuint glsl_program_num, const string& prefix, unsigned *sampler_num) {}
+
+void Effect::clear_gl_state() {}
+
+void Effect::register_uniform_bool(const std::string &key, const bool *value)
{
- string output = "";
- for (map<string, float*>::const_iterator it = params_float.begin();
- it != params_float.end();
- ++it) {
- char buf[256];
- sprintf(buf, "uniform float PREFIX(%s);\n", it->first.c_str());
- output.append(buf);
- }
- for (map<string, float*>::const_iterator it = params_vec2.begin();
- it != params_vec2.end();
- ++it) {
- char buf[256];
- sprintf(buf, "uniform vec2 PREFIX(%s);\n", it->first.c_str());
- output.append(buf);
- }
- for (map<string, float*>::const_iterator it = params_vec3.begin();
- it != params_vec3.end();
- ++it) {
- char buf[256];
- sprintf(buf, "uniform vec3 PREFIX(%s);\n", it->first.c_str());
- output.append(buf);
- }
- for (map<string, float*>::const_iterator it = params_vec4.begin();
- it != params_vec4.end();
- ++it) {
- char buf[256];
- sprintf(buf, "uniform vec4 PREFIX(%s);\n", it->first.c_str());
- output.append(buf);
- }
- return output;
+ Uniform<bool> uniform;
+ uniform.name = key;
+ uniform.value = value;
+ uniform.num_values = 1;
+ uniform.location = -1;
+ uniforms_bool.push_back(uniform);
}
-void Effect::set_gl_state(GLuint glsl_program_num, const string& prefix, unsigned *sampler_num)
+void Effect::register_uniform_int(const std::string &key, const int *value)
{
- for (map<string, float*>::const_iterator it = params_float.begin();
- it != params_float.end();
- ++it) {
- set_uniform_float(glsl_program_num, prefix, it->first, *it->second);
- }
- for (map<string, float*>::const_iterator it = params_vec2.begin();
- it != params_vec2.end();
- ++it) {
- set_uniform_vec2(glsl_program_num, prefix, it->first, it->second);
- }
- for (map<string, float*>::const_iterator it = params_vec3.begin();
- it != params_vec3.end();
- ++it) {
- set_uniform_vec3(glsl_program_num, prefix, it->first, it->second);
- }
- for (map<string, float*>::const_iterator it = params_vec4.begin();
- it != params_vec4.end();
- ++it) {
- set_uniform_vec4(glsl_program_num, prefix, it->first, it->second);
- }
+ Uniform<int> uniform;
+ uniform.name = key;
+ uniform.value = value;
+ uniform.num_values = 1;
+ uniform.location = -1;
+ uniforms_int.push_back(uniform);
}
-void Effect::clear_gl_state() {}
+void Effect::register_uniform_sampler2d(const std::string &key, const GLint *value)
+{
+ Uniform<int> uniform;
+ uniform.name = key;
+ uniform.value = value;
+ uniform.num_values = 1;
+ uniform.location = -1;
+ uniforms_sampler2d.push_back(uniform);
+}
+
+void Effect::register_uniform_float(const std::string &key, const float *value)
+{
+ Uniform<float> uniform;
+ uniform.name = key;
+ uniform.value = value;
+ uniform.num_values = 1;
+ uniform.location = -1;
+ uniforms_float.push_back(uniform);
+}
+
+void Effect::register_uniform_vec2(const std::string &key, const float *values)
+{
+ Uniform<float> uniform;
+ uniform.name = key;
+ uniform.value = values;
+ uniform.num_values = 1;
+ uniform.location = -1;
+ uniforms_vec2.push_back(uniform);
+}
+
+void Effect::register_uniform_vec3(const std::string &key, const float *values)
+{
+ Uniform<float> uniform;
+ uniform.name = key;
+ uniform.value = values;
+ uniform.num_values = 1;
+ uniform.location = -1;
+ uniforms_vec3.push_back(uniform);
+}
+
+void Effect::register_uniform_vec4(const std::string &key, const float *values)
+{
+ Uniform<float> uniform;
+ uniform.name = key;
+ uniform.value = values;
+ uniform.num_values = 1;
+ uniform.location = -1;
+ uniforms_vec4.push_back(uniform);
+}
+
+void Effect::register_uniform_vec2_array(const std::string &key, const float *values, size_t num_values)
+{
+ Uniform<float> uniform;
+ uniform.name = key;
+ uniform.value = values;
+ uniform.num_values = num_values;
+ uniform.location = -1;
+ uniforms_vec2_array.push_back(uniform);
+}
+
+void Effect::register_uniform_vec4_array(const std::string &key, const float *values, size_t num_values)
+{
+ Uniform<float> uniform;
+ uniform.name = key;
+ uniform.value = values;
+ uniform.num_values = num_values;
+ uniform.location = -1;
+ uniforms_vec4_array.push_back(uniform);
+}
+
+void Effect::register_uniform_mat3(const std::string &key, const Matrix3d *matrix)
+{
+ Uniform<Matrix3d> uniform;
+ uniform.name = key;
+ uniform.value = matrix;
+ uniform.num_values = 1;
+ uniform.location = -1;
+ uniforms_mat3.push_back(uniform);
+}
} // namespace movit
#include <stddef.h>
#include <map>
#include <string>
+#include <vector>
+#include <Eigen/Core>
#include "defs.h"
float r, g, b, a;
};
+// Represents a registered uniform.
+template<class T>
+struct Uniform {
+ std::string name; // Without prefix.
+ const T *value; // Owner by the effect.
+ size_t num_values; // Number of elements; for arrays only. _Not_ the vector length.
+ std::string prefix; // Filled in only after phases have been constructed.
+ GLint location; // Filled in only after phases have been constructed. -1 if no location.
+};
+
class Effect {
public:
virtual ~Effect() {}
// itself from all other effects.
virtual void rewrite_graph(EffectChain *graph, Node *self) {}
- // Outputs one GLSL uniform declaration for each registered parameter
- // (see below), with the right prefix prepended to each uniform name.
- // If you do not want this behavior, you can override this function.
- virtual std::string output_convenience_uniforms() const;
-
// Returns the GLSL fragment shader string for this effect.
virtual std::string output_fragment_shader() = 0;
protected:
// Register a parameter. Whenever set_*() is called with the same key,
// it will update the value in the given pointer (typically a pointer
- // to some private member variable in your effect).
+ // to some private member variable in your effect). It will also
+ // register a uniform of the same name (plus an arbitrary prefix
+ // which you can access using the PREFIX macro) that you can access.
//
// Neither of these take ownership of the pointer.
void register_vec3(const std::string &key, float *values);
void register_vec4(const std::string &key, float *values);
+ // Register uniforms, such that they will automatically be set
+ // before the shader runs. This is more efficient than set_uniform_*
+ // in effect_util.h, because it doesn't need to do name lookups
+ // every time. Also, in the future, it will use uniform buffer objects
+ // (UBOs) if available to reduce the number of calls into the driver.
+ //
+ // May not be called after output_fragment_shader() has returned.
+ // The pointer must be valid for the entire lifetime of the Effect,
+ // since the value is pulled from it each execution. The value is
+ // guaranteed to be read after set_gl_state() for the effect has
+ // returned, so you can safely update its value from there.
+ //
+ // Note that this will also declare the uniform in the shader for you,
+ // so you should not do that yourself. (This is so it can be part of
+ // the right uniform block.) However, it is probably a good idea to
+ // have a commented-out declaration so that it is easier to see the
+ // type and thus understand the shader on its own.
+ //
+ // 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_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_vec2_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, 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;
+ friend class EffectChain;
};
} // namespace movit
#include <stack>
#include <utility>
#include <vector>
+#include <Eigen/Core>
#include "alpha_division_effect.h"
#include "alpha_multiplication_effect.h"
#include "dither_effect.h"
#include "effect.h"
#include "effect_chain.h"
+#include "effect_util.h"
#include "gamma_compression_effect.h"
#include "gamma_expansion_effect.h"
#include "init.h"
#include "util.h"
#include "ycbcr_conversion_effect.h"
+using namespace Eigen;
using namespace std;
namespace movit {
void EffectChain::compile_glsl_program(Phase *phase)
{
- string frag_shader = read_version_dependent_file("header", "frag");
+ string frag_shader_header = read_version_dependent_file("header", "frag");
+ string frag_shader = "";
// Create functions for all the texture inputs that we need.
for (unsigned i = 0; i < phase->inputs.size(); ++i) {
frag_shader += "\n";
}
+ // Give each effect in the phase its own ID.
for (unsigned i = 0; i < phase->effects.size(); ++i) {
Node *node = phase->effects[i];
char effect_id[256];
sprintf(effect_id, "eff%u", i);
phase->effect_ids.insert(make_pair(node, effect_id));
+ }
+ for (unsigned i = 0; i < phase->effects.size(); ++i) {
+ Node *node = phase->effects[i];
+ const string effect_id = phase->effect_ids[node];
if (node->incoming_links.size() == 1) {
frag_shader += string("#define INPUT ") + phase->effect_ids[node->incoming_links[0]] + "\n";
} else {
frag_shader += "\n";
frag_shader += string("#define FUNCNAME ") + effect_id + "\n";
- frag_shader += replace_prefix(node->effect->output_convenience_uniforms(), effect_id);
frag_shader += replace_prefix(node->effect->output_fragment_shader(), effect_id);
frag_shader += "#undef PREFIX\n";
frag_shader += "#undef FUNCNAME\n";
frag_shader += string("#define INPUT ") + phase->effect_ids[phase->effects.back()] + "\n";
frag_shader.append(read_version_dependent_file("footer", "frag"));
+ // Collect uniforms from all effects and output them. Note that this needs
+ // to happen after output_fragment_shader(), even though the uniforms come
+ // before in the output source, since output_fragment_shader() is allowed
+ // to register new uniforms (e.g. arrays that are of unknown length until
+ // finalization time).
+ // TODO: Make a uniform block for platforms that support it.
+ string frag_shader_uniforms = "";
+ for (unsigned i = 0; i < phase->effects.size(); ++i) {
+ Node *node = phase->effects[i];
+ Effect *effect = node->effect;
+ const string effect_id = phase->effect_ids[node];
+ for (unsigned j = 0; j < effect->uniforms_bool.size(); ++j) {
+ phase->uniforms_bool.push_back(effect->uniforms_bool[j]);
+ phase->uniforms_bool.back().prefix = effect_id;
+ frag_shader_uniforms += string("uniform bool ") + effect_id
+ + "_" + effect->uniforms_bool[j].name + ";\n";
+ }
+ for (unsigned j = 0; j < effect->uniforms_int.size(); ++j) {
+ phase->uniforms_int.push_back(effect->uniforms_int[j]);
+ phase->uniforms_int.back().prefix = effect_id;
+ frag_shader_uniforms += string("uniform int ") + effect_id
+ + "_" + effect->uniforms_int[j].name + ";\n";
+ }
+ for (unsigned j = 0; j < effect->uniforms_sampler2d.size(); ++j) {
+ phase->uniforms_int.push_back(effect->uniforms_sampler2d[j]);
+ phase->uniforms_int.back().prefix = effect_id;
+ frag_shader_uniforms += string("uniform sampler2D ") + effect_id
+ + "_" + effect->uniforms_sampler2d[j].name + ";\n";
+ }
+ for (unsigned j = 0; j < effect->uniforms_float.size(); ++j) {
+ phase->uniforms_float.push_back(effect->uniforms_float[j]);
+ phase->uniforms_float.back().prefix = effect_id;
+ frag_shader_uniforms += string("uniform float ") + effect_id
+ + "_" + effect->uniforms_float[j].name + ";\n";
+ }
+ for (unsigned j = 0; j < effect->uniforms_vec2.size(); ++j) {
+ phase->uniforms_vec2.push_back(effect->uniforms_vec2[j]);
+ phase->uniforms_vec2.back().prefix = effect_id;
+ frag_shader_uniforms += string("uniform vec2 ") + effect_id
+ + "_" + effect->uniforms_vec2[j].name + ";\n";
+ }
+ for (unsigned j = 0; j < effect->uniforms_vec3.size(); ++j) {
+ phase->uniforms_vec3.push_back(effect->uniforms_vec3[j]);
+ phase->uniforms_vec3.back().prefix = effect_id;
+ frag_shader_uniforms += string("uniform vec3 ") + effect_id
+ + "_" + effect->uniforms_vec3[j].name + ";\n";
+ }
+ for (unsigned j = 0; j < effect->uniforms_vec4.size(); ++j) {
+ phase->uniforms_vec4.push_back(effect->uniforms_vec4[j]);
+ phase->uniforms_vec4.back().prefix = effect_id;
+ frag_shader_uniforms += string("uniform vec4 ") + effect_id
+ + "_" + effect->uniforms_vec4[j].name + ";\n";
+ }
+ for (unsigned j = 0; j < effect->uniforms_vec2_array.size(); ++j) {
+ char buf[256];
+ phase->uniforms_vec2.push_back(effect->uniforms_vec2_array[j]);
+ phase->uniforms_vec2.back().prefix = effect_id;
+ snprintf(buf, sizeof(buf), "uniform vec2 %s_%s[%d];\n",
+ effect_id.c_str(), effect->uniforms_vec2_array[j].name.c_str(),
+ int(effect->uniforms_vec2_array[j].num_values));
+ frag_shader_uniforms += buf;
+ }
+ for (unsigned j = 0; j < effect->uniforms_vec4_array.size(); ++j) {
+ char buf[256];
+ phase->uniforms_vec4.push_back(effect->uniforms_vec4_array[j]);
+ phase->uniforms_vec4.back().prefix = effect_id;
+ snprintf(buf, sizeof(buf), "uniform vec4 %s_%s[%d];\n",
+ effect_id.c_str(), effect->uniforms_vec4_array[j].name.c_str(),
+ int(effect->uniforms_vec4_array[j].num_values));
+ frag_shader_uniforms += buf;
+ }
+ for (unsigned j = 0; j < effect->uniforms_mat3.size(); ++j) {
+ phase->uniforms_mat3.push_back(effect->uniforms_mat3[j]);
+ phase->uniforms_mat3.back().prefix = effect_id;
+ frag_shader_uniforms += string("uniform mat3 ") + effect_id
+ + "_" + effect->uniforms_mat3[j].name + ";\n";
+ }
+ }
+
+ frag_shader = frag_shader_header + frag_shader_uniforms + frag_shader;
+
string vert_shader = read_version_dependent_file("vs", "vert");
phase->glsl_program_num = resource_pool->compile_glsl_program(vert_shader, frag_shader);
+
+ // Collect the resulting program numbers for each uniform.
+ for (unsigned i = 0; i < phase->uniforms_bool.size(); ++i) {
+ Uniform<bool> &uniform = phase->uniforms_bool[i];
+ uniform.location = get_uniform_location(phase->glsl_program_num, uniform.prefix, uniform.name);
+ }
+ for (unsigned i = 0; i < phase->uniforms_int.size(); ++i) {
+ Uniform<int> &uniform = phase->uniforms_int[i];
+ uniform.location = get_uniform_location(phase->glsl_program_num, uniform.prefix, uniform.name);
+ }
+ for (unsigned i = 0; i < phase->uniforms_float.size(); ++i) {
+ Uniform<float> &uniform = phase->uniforms_float[i];
+ uniform.location = get_uniform_location(phase->glsl_program_num, uniform.prefix, uniform.name);
+ }
+ for (unsigned i = 0; i < phase->uniforms_vec2.size(); ++i) {
+ Uniform<float> &uniform = phase->uniforms_vec2[i];
+ uniform.location = get_uniform_location(phase->glsl_program_num, uniform.prefix, uniform.name);
+ }
+ for (unsigned i = 0; i < phase->uniforms_vec3.size(); ++i) {
+ Uniform<float> &uniform = phase->uniforms_vec3[i];
+ uniform.location = get_uniform_location(phase->glsl_program_num, uniform.prefix, uniform.name);
+ }
+ for (unsigned i = 0; i < phase->uniforms_vec4.size(); ++i) {
+ Uniform<float> &uniform = phase->uniforms_vec4[i];
+ uniform.location = get_uniform_location(phase->glsl_program_num, uniform.prefix, uniform.name);
+ }
+ for (unsigned i = 0; i < phase->uniforms_mat3.size(); ++i) {
+ Uniform<Matrix3d> &uniform = phase->uniforms_mat3[i];
+ uniform.location = get_uniform_location(phase->glsl_program_num, uniform.prefix, uniform.name);
+ }
}
// Construct GLSL programs, starting at the given effect and following
}
}
+ // Uniforms need to come after set_gl_state(), since they can be updated
+ // from there.
+ setup_uniforms(phase);
+
// Now draw!
float vertices[] = {
0.0f, 2.0f,
check_error();
}
+void EffectChain::setup_uniforms(Phase *phase)
+{
+ // TODO: Use UBO blocks.
+ for (size_t i = 0; i < phase->uniforms_bool.size(); ++i) {
+ const Uniform<bool> &uniform = phase->uniforms_bool[i];
+ assert(uniform.num_values == 1);
+ if (uniform.location != -1) {
+ glUniform1i(uniform.location, *uniform.value);
+ }
+ }
+ for (size_t i = 0; i < phase->uniforms_int.size(); ++i) {
+ const Uniform<int> &uniform = phase->uniforms_int[i];
+ if (uniform.location != -1) {
+ glUniform1iv(uniform.location, uniform.num_values, uniform.value);
+ }
+ }
+ for (size_t i = 0; i < phase->uniforms_float.size(); ++i) {
+ const Uniform<float> &uniform = phase->uniforms_float[i];
+ if (uniform.location != -1) {
+ glUniform1fv(uniform.location, uniform.num_values, uniform.value);
+ }
+ }
+ for (size_t i = 0; i < phase->uniforms_vec2.size(); ++i) {
+ const Uniform<float> &uniform = phase->uniforms_vec2[i];
+ if (uniform.location != -1) {
+ glUniform2fv(uniform.location, uniform.num_values, uniform.value);
+ }
+ }
+ for (size_t i = 0; i < phase->uniforms_vec3.size(); ++i) {
+ const Uniform<float> &uniform = phase->uniforms_vec3[i];
+ if (uniform.location != -1) {
+ glUniform3fv(uniform.location, uniform.num_values, uniform.value);
+ }
+ }
+ for (size_t i = 0; i < phase->uniforms_vec4.size(); ++i) {
+ const Uniform<float> &uniform = phase->uniforms_vec4[i];
+ if (uniform.location != -1) {
+ glUniform4fv(uniform.location, uniform.num_values, uniform.value);
+ }
+ }
+ for (size_t i = 0; i < phase->uniforms_mat3.size(); ++i) {
+ const Uniform<Matrix3d> &uniform = phase->uniforms_mat3[i];
+ assert(uniform.num_values == 1);
+ if (uniform.location != -1) {
+ // Convert to float (GLSL has no double matrices).
+ float matrixf[9];
+ for (unsigned y = 0; y < 3; ++y) {
+ for (unsigned x = 0; x < 3; ++x) {
+ matrixf[y + x * 3] = (*uniform.value)(y, x);
+ }
+ }
+ glUniformMatrix3fv(uniform.location, 1, GL_FALSE, matrixf);
+ }
+ }
+}
+
void EffectChain::setup_rtt_sampler(GLuint glsl_program_num, int sampler_num, const string &effect_id, bool use_mipmaps)
{
glActiveTexture(GL_TEXTURE0 + sampler_num);
#include <set>
#include <string>
#include <vector>
+#include <Eigen/Core>
+#include "effect.h"
#include "image_format.h"
#include "ycbcr.h"
// Unique per-phase to increase cacheability of compiled shaders.
std::map<Node *, std::string> effect_ids;
+ // Uniforms for this phase; combined from all the effects.
+ 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<Eigen::Matrix3d> > uniforms_mat3;
+
// For measurement of GPU time used.
GLuint timer_query_object;
uint64_t time_elapsed_ns;
// Execute one phase, ie. set up all inputs, effects and outputs, and render the quad.
void execute_phase(Phase *phase, bool last_phase, std::map<Phase *, GLuint> *output_textures, std::set<Phase *> *generated_mipmaps);
+ // Set up uniforms for one phase. The program must already be bound.
+ void setup_uniforms(Phase *phase);
+
// Set up the given sampler number for sampling from an RTT texture,
// and bind it to "tex_" plus the given GLSL variable.
void setup_rtt_sampler(GLuint glsl_program_num, int sampler_num, const std::string &effect_id, bool use_mipmaps);
class Node;
// Convenience functions that deal with prepending the prefix.
+// Note that using EffectChain::register_uniform_*() is more efficient
+// than calling these from set_gl_state().
GLint get_uniform_location(GLuint glsl_program_num, const std::string &prefix, const std::string &key);
void set_uniform_int(GLuint glsl_program_num, const std::string &prefix, const std::string &key, int value);
void set_uniform_float(GLuint glsl_program_num, const std::string &prefix, const std::string &key, float value);
{
register_int("fft_width", &fft_width);
register_int("fft_height", &fft_height);
+ register_uniform_sampler2d("tex", &uniform_tex);
}
FFTInput::~FFTInput()
}
// Bind it to a sampler.
- set_uniform_int(glsl_program_num, prefix, "tex", *sampler_num);
+ uniform_tex = *sampler_num;
++*sampler_num;
}
unsigned convolve_width, convolve_height;
const float *pixel_data;
ResourcePool *resource_pool;
+ GLint uniform_tex;
};
} // namespace movit
register_int("direction", (int *)&direction);
register_int("pass_number", &pass_number);
register_int("inverse", &inverse);
+ register_uniform_float("num_repeats", &uniform_num_repeats);
+ register_uniform_sampler2d("support_tex", &uniform_support_tex);
glGenTextures(1, &tex);
}
generate_support_texture();
}
- set_uniform_int(glsl_program_num, prefix, "support_tex", *sampler_num);
+ uniform_support_tex = *sampler_num;
++*sampler_num;
assert(input_size % fft_size == 0);
- set_uniform_float(glsl_program_num, prefix, "num_repeats", input_size / fft_size);
+ uniform_num_repeats = input_size / fft_size;
}
void FFTPassEffect::generate_support_texture()
// DIRECTION_VERTICAL will be #defined to 1 if we are doing a vertical FFT,
// and 0 otherwise.
-uniform float PREFIX(num_repeats);
-uniform sampler2D PREFIX(support_tex);
+// Implicit uniforms:
+// uniform float PREFIX(num_repeats);
+// uniform sampler2D PREFIX(support_tex);
vec4 FUNCNAME(vec2 tc) {
#if DIRECTION_VERTICAL
EffectChain *chain;
int input_width, input_height;
GLuint tex;
+ float uniform_num_repeats;
+ GLint uniform_support_tex;
int fft_size;
Direction direction;
assert(type == GL_FLOAT || type == GL_HALF_FLOAT || type == GL_UNSIGNED_SHORT || type == GL_UNSIGNED_BYTE);
register_int("output_linear_gamma", &output_linear_gamma);
register_int("needs_mipmaps", &needs_mipmaps);
+ register_uniform_sampler2d("tex", &uniform_tex);
// Some types are not supported in all GL versions (e.g. GLES),
// and will corrected into the right format in the shader.
}
// Bind it to a sampler.
- set_uniform_int(glsl_program_num, prefix, "tex", *sampler_num);
+ uniform_tex = *sampler_num;
++*sampler_num;
}
-uniform sampler2D PREFIX(tex);
+// Implicit uniforms:
+// uniform sampler2D PREFIX(tex);
vec4 FUNCNAME(vec2 tc) {
// OpenGL's origin is bottom-left, but most graphics software assumes
const void *pixel_data;
ResourcePool *resource_pool;
bool fixup_swap_rb, fixup_red_to_grayscale;
+ GLint uniform_tex;
};
} // namespace movit
: destination_curve(GAMMA_LINEAR)
{
register_int("destination_curve", (int *)&destination_curve);
+ register_uniform_float("linear_scale", &uniform_linear_scale);
+ register_uniform_float("c0", &uniform_c0);
+ register_uniform_float("c1", &uniform_c1);
+ register_uniform_float("c2", &uniform_c2);
+ register_uniform_float("c3", &uniform_c3);
+ register_uniform_float("c4", &uniform_c4);
+ register_uniform_float("beta", &uniform_beta);
}
string GammaCompressionEffect::output_fragment_shader()
// β = 0.0031308, ɣ = 1/2.4.
// maxerror = 0.000785 = 0.200 * 255
// error at 1.0 = 0.000078 = 0.020 * 255
- set_uniform_float(glsl_program_num, prefix, "linear_scale", 12.92);
- set_uniform_float(glsl_program_num, prefix, "c0", -0.03679675939);
- set_uniform_float(glsl_program_num, prefix, "c1", 1.443803073);
- set_uniform_float(glsl_program_num, prefix, "c2", -0.9239780987);
- set_uniform_float(glsl_program_num, prefix, "c3", 0.8060491596);
- set_uniform_float(glsl_program_num, prefix, "c4", -0.2891558568);
- set_uniform_float(glsl_program_num, prefix, "beta", 0.0031308);
+ uniform_linear_scale = 12.92;
+ uniform_c0 = -0.03679675939;
+ uniform_c1 = 1.443803073;
+ uniform_c2 = -0.9239780987;
+ uniform_c3 = 0.8060491596;
+ uniform_c4 = -0.2891558568;
+ uniform_beta = 0.0031308;
}
if (destination_curve == GAMMA_REC_709) { // Also includes Rec. 601, and 10-bit Rec. 2020.
// Rec. 2020, page 3; ɑ = 1.099, β = 0.018, ɣ = 0.45.
// maxerror = 0.000131 = 0.033 * 255 = 0.134 * 1023
// error at 1.0 = 0.000013 = 0.003 * 255 = 0.013 * 1023
- set_uniform_float(glsl_program_num, prefix, "linear_scale", 4.5);
- set_uniform_float(glsl_program_num, prefix, "c0", -0.08541688528);
- set_uniform_float(glsl_program_num, prefix, "c1", 1.292793370);
- set_uniform_float(glsl_program_num, prefix, "c2", -0.4070417645);
- set_uniform_float(glsl_program_num, prefix, "c3", 0.2923891828);
- set_uniform_float(glsl_program_num, prefix, "c4", -0.09273699351);
- set_uniform_float(glsl_program_num, prefix, "beta", 0.018);
+ uniform_linear_scale = 4.5;
+ uniform_c0 = -0.08541688528;
+ uniform_c1 = 1.292793370;
+ uniform_c2 = -0.4070417645;
+ uniform_c3 = 0.2923891828;
+ uniform_c4 = -0.09273699351;
+ uniform_beta = 0.018;
}
if (destination_curve == GAMMA_REC_2020_12_BIT) {
// Rec. 2020, page 3; ɑ = 1.0993, β = 0.0181, ɣ = 0.45.
// bit. (Removing the constraint for x=1 will only take this down
// from 0.553 to 0.501; adding a fifth order can get it down to
// 0.167, although this assumes working in fp64 and not fp32.)
- set_uniform_float(glsl_program_num, prefix, "linear_scale", 4.5);
- set_uniform_float(glsl_program_num, prefix, "c0", -0.08569685663);
- set_uniform_float(glsl_program_num, prefix, "c1", 1.293000900);
- set_uniform_float(glsl_program_num, prefix, "c2", -0.4067291321);
- set_uniform_float(glsl_program_num, prefix, "c3", 0.2919741179);
- set_uniform_float(glsl_program_num, prefix, "c4", -0.09256205770);
- set_uniform_float(glsl_program_num, prefix, "beta", 0.0181);
+ uniform_linear_scale = 4.5;
+ uniform_c0 = -0.08569685663;
+ uniform_c1 = 1.293000900;
+ uniform_c2 = -0.4067291321;
+ uniform_c3 = 0.2919741179;
+ uniform_c4 = -0.09256205770;
+ uniform_beta = 0.0181;
}
}
// Compress gamma curve.
-uniform float PREFIX(linear_scale);
-uniform float PREFIX(c0), PREFIX(c1), PREFIX(c2), PREFIX(c3), PREFIX(c4);
-uniform float PREFIX(beta);
+// Implicit uniforms:
+// uniform float PREFIX(linear_scale);
+// uniform float PREFIX(c0), PREFIX(c1), PREFIX(c2), PREFIX(c3), PREFIX(c4);
+// uniform float PREFIX(beta);
vec4 FUNCNAME(vec2 tc) {
vec4 x = INPUT(tc);
private:
GammaCurve destination_curve;
+ float uniform_linear_scale, uniform_c0, uniform_c1, uniform_c2, uniform_c3, uniform_c4, uniform_beta;
};
} // namespace movit
: source_curve(GAMMA_LINEAR)
{
register_int("source_curve", (int *)&source_curve);
+ register_uniform_float("linear_scale", &uniform_linear_scale);
+ register_uniform_float("c0", &uniform_c0);
+ register_uniform_float("c1", &uniform_c1);
+ register_uniform_float("c2", &uniform_c2);
+ register_uniform_float("c3", &uniform_c3);
+ register_uniform_float("c4", &uniform_c4);
+ register_uniform_float("beta", &uniform_beta);
}
string GammaExpansionEffect::output_fragment_shader()
//
// Note that the worst _relative_ error by far is just at the beginning
// of the exponential curve, ie., just around β.
- set_uniform_float(glsl_program_num, prefix, "linear_scale", 1.0 / 12.92);
- set_uniform_float(glsl_program_num, prefix, "c0", 0.001324469581);
- set_uniform_float(glsl_program_num, prefix, "c1", 0.02227416690);
- set_uniform_float(glsl_program_num, prefix, "c2", 0.5917615253);
- set_uniform_float(glsl_program_num, prefix, "c3", 0.4733532353);
- set_uniform_float(glsl_program_num, prefix, "c4", -0.08880738120);
- set_uniform_float(glsl_program_num, prefix, "beta", 0.04045);
+ uniform_linear_scale = 1.0 / 12.92;
+ uniform_c0 = 0.001324469581;
+ uniform_c1 = 0.02227416690;
+ uniform_c2 = 0.5917615253;
+ uniform_c3 = 0.4733532353;
+ uniform_c4 = -0.08880738120;
+ uniform_beta = 0.04045;
}
if (source_curve == GAMMA_REC_709) { // Also includes Rec. 601, and 10-bit Rec. 2020.
// Rec. 2020, page 3; ɑ = 1.099, β = 0.018 * 4.5, ɣ = 1/0.45.
// Note that Rec. 2020 only gives the other direction, which is why
// our beta and gamma are different from the numbers mentioned
// (we've inverted the formula).
- set_uniform_float(glsl_program_num, prefix, "linear_scale", 1.0 / 4.5);
- set_uniform_float(glsl_program_num, prefix, "c0", 0.005137028744);
- set_uniform_float(glsl_program_num, prefix, "c1", 0.09802596889);
- set_uniform_float(glsl_program_num, prefix, "c2", 0.7255768864);
- set_uniform_float(glsl_program_num, prefix, "c3", 0.2135067966);
- set_uniform_float(glsl_program_num, prefix, "c4", -0.04225094667);
- set_uniform_float(glsl_program_num, prefix, "beta", 0.018 * 4.5);
+ uniform_linear_scale = 1.0 / 4.5;
+ uniform_c0 = 0.005137028744;
+ uniform_c1 = 0.09802596889;
+ uniform_c2 = 0.7255768864;
+ uniform_c3 = 0.2135067966;
+ uniform_c4 = -0.04225094667;
+ uniform_beta = 0.018 * 4.5;
}
if (source_curve == GAMMA_REC_2020_12_BIT) {
// Rec. 2020, page 3; ɑ = 1.0993, β = 0.0181 * 4.5, ɣ = 1/0.45.
// Note that Rec. 2020 only gives the other direction, which is why
// our beta and gamma are different from the numbers mentioned
// (we've inverted the formula).
- set_uniform_float(glsl_program_num, prefix, "linear_scale", 1.0 / 4.5);
- set_uniform_float(glsl_program_num, prefix, "c0", 0.005167545928);
- set_uniform_float(glsl_program_num, prefix, "c1", 0.09835585809);
- set_uniform_float(glsl_program_num, prefix, "c2", 0.7254820139);
- set_uniform_float(glsl_program_num, prefix, "c3", 0.2131291155);
- set_uniform_float(glsl_program_num, prefix, "c4", -0.04213877222);
- set_uniform_float(glsl_program_num, prefix, "beta", 0.0181 * 4.5);
+ uniform_linear_scale = 1.0 / 4.5;
+ uniform_c0 = 0.005167545928;
+ uniform_c1 = 0.09835585809;
+ uniform_c2 = 0.7254820139;
+ uniform_c3 = 0.2131291155;
+ uniform_c4 = -0.04213877222;
+ uniform_beta = 0.0181 * 4.5;
}
}
// Expand gamma curve.
-uniform float PREFIX(linear_scale);
-uniform float PREFIX(c0), PREFIX(c1), PREFIX(c2), PREFIX(c3), PREFIX(c4);
-uniform float PREFIX(beta);
+// Implicit uniforms:
+// uniform float PREFIX(linear_scale);
+// uniform float PREFIX(c0), PREFIX(c1), PREFIX(c2), PREFIX(c3), PREFIX(c4);
+// uniform float PREFIX(beta);
vec4 FUNCNAME(vec2 tc) {
vec4 x = INPUT(tc);
private:
GammaCurve source_curve;
+ float uniform_linear_scale, uniform_c0, uniform_c1, uniform_c2, uniform_c3, uniform_c4, uniform_beta;
};
} // namespace movit
register_vec3("lift", (float *)&lift);
register_vec3("gamma", (float *)&gamma);
register_vec3("gain", (float *)&gain);
+ register_uniform_vec3("gain_pow_inv_gamma", (float *)&uniform_gain_pow_inv_gamma);
+ register_uniform_vec3("inv_gamma_22", (float *)&uniform_inv_gamma22);
}
string LiftGammaGainEffect::output_fragment_shader()
{
Effect::set_gl_state(glsl_program_num, prefix, sampler_num);
- RGBTriplet gain_pow_inv_gamma(
+ uniform_gain_pow_inv_gamma = RGBTriplet(
pow(gain.r, 1.0f / gamma.r),
pow(gain.g, 1.0f / gamma.g),
pow(gain.b, 1.0f / gamma.b));
- set_uniform_vec3(glsl_program_num, prefix, "gain_pow_inv_gamma", (float *)&gain_pow_inv_gamma);
- RGBTriplet inv_gamma_22(
+ uniform_inv_gamma22 = RGBTriplet(
2.2f / gamma.r,
2.2f / gamma.g,
2.2f / gamma.b);
- set_uniform_vec3(glsl_program_num, prefix, "inv_gamma_22", (float *)&inv_gamma_22);
}
} // namespace movit
+// Implicit uniforms:
+//
// These are calculated in the host code to save some arithmetic.
-uniform vec3 PREFIX(gain_pow_inv_gamma); // gain^(1/gamma).
-uniform vec3 PREFIX(inv_gamma_22); // 2.2 / gamma.
+// uniform vec3 PREFIX(gain_pow_inv_gamma); // gain^(1/gamma).
+// uniform vec3 PREFIX(inv_gamma_22); // 2.2 / gamma.
vec4 FUNCNAME(vec2 tc) {
vec4 x = INPUT(tc);
private:
RGBTriplet lift, gamma, gain;
+ RGBTriplet uniform_gain_pow_inv_gamma, uniform_inv_gamma22;
};
} // namespace movit
register_float("transition_width", &transition_width);
register_float("progress", &progress);
register_int("inverse", &inverse);
+ register_uniform_bool("inverse", &uniform_inverse);
+ register_uniform_float("progress_mul_w_plus_one", &uniform_progress_mul_w_plus_one);
}
string LumaMixEffect::output_fragment_shader()
void LumaMixEffect::set_gl_state(GLuint glsl_program_num, const string &prefix, unsigned *sampler_num)
{
Effect::set_gl_state(glsl_program_num, prefix, sampler_num);
- set_uniform_float(glsl_program_num, prefix, "progress_mul_w_plus_one", progress * (transition_width + 1.0));
- set_uniform_int(glsl_program_num, prefix, "inverse", inverse);
+ uniform_progress_mul_w_plus_one = progress * (transition_width + 1.0);
+ uniform_inverse = inverse;
}
} // namespace movit
-uniform float PREFIX(progress_mul_w_plus_one);
-uniform bool PREFIX(inverse);
+// Implicit uniforms:
+// uniform float PREFIX(progress_mul_w_plus_one);
+// uniform bool PREFIX(inverse);
vec4 FUNCNAME(vec2 tc) {
vec4 first = INPUT1(tc);
private:
float transition_width, progress;
int inverse; // 0 or 1.
+ bool uniform_inverse;
+ float uniform_progress_mul_w_plus_one;
};
} // namespace movit
register_float("border_offset_left", &border_offset_left);
register_float("border_offset_bottom", &border_offset_bottom);
register_float("border_offset_right", &border_offset_right);
+ register_uniform_vec2("offset", uniform_offset);
+ register_uniform_vec2("scale", uniform_scale);
+ register_uniform_vec2("normalized_coords_to_texels", uniform_normalized_coords_to_texels);
+ register_uniform_vec2("offset_bottomleft", uniform_offset_bottomleft);
+ register_uniform_vec2("offset_topright", uniform_offset_topright);
}
string PaddingEffect::output_fragment_shader()
{
Effect::set_gl_state(glsl_program_num, prefix, sampler_num);
- float offset[2] = {
- left / output_width,
- (output_height - input_height - top) / output_height
- };
- set_uniform_vec2(glsl_program_num, prefix, "offset", offset);
+ uniform_offset[0] = left / output_width;
+ uniform_offset[1] = (output_height - input_height - top) / output_height;
- float scale[2] = {
- float(output_width) / input_width,
- float(output_height) / input_height
- };
- set_uniform_vec2(glsl_program_num, prefix, "scale", scale);
+ uniform_scale[0] = float(output_width) / input_width;
+ uniform_scale[1] = float(output_height) / input_height;
- float normalized_coords_to_texels[2] = {
- float(input_width), float(input_height)
- };
- set_uniform_vec2(glsl_program_num, prefix, "normalized_coords_to_texels", normalized_coords_to_texels);
+ uniform_normalized_coords_to_texels[0] = float(input_width);
+ uniform_normalized_coords_to_texels[1] = float(input_height);
// Texels -0.5..0.5 should map to light level 0..1 (and then we
// clamp the rest).
- float offset_bottomleft[2] = {
- 0.5f - border_offset_left, 0.5f + border_offset_bottom,
- };
+ uniform_offset_bottomleft[0] = 0.5f - border_offset_left;
+ uniform_offset_bottomleft[1] = 0.5f + border_offset_bottom;
// Texels size-0.5..size+0.5 should map to light level 1..0 (and then clamp).
- float offset_topright[2] = {
- input_width + 0.5f + border_offset_right, input_height + 0.5f - border_offset_top,
- };
-
- set_uniform_vec2(glsl_program_num, prefix, "offset_bottomleft", offset_bottomleft);
- set_uniform_vec2(glsl_program_num, prefix, "offset_topright", offset_topright);
+ uniform_offset_topright[0] = input_width + 0.5f + border_offset_right;
+ uniform_offset_topright[1] = input_height + 0.5f - border_offset_top;
}
// We don't change the pixels of the image itself, so the only thing that
-uniform vec2 PREFIX(offset);
-uniform vec2 PREFIX(scale);
-
-uniform vec2 PREFIX(normalized_coords_to_texels);
-uniform vec2 PREFIX(offset_bottomleft);
-uniform vec2 PREFIX(offset_topright);
+// Implicit uniforms:
+// uniform vec2 PREFIX(offset);
+// uniform vec2 PREFIX(scale);
+//
+// uniform vec2 PREFIX(normalized_coords_to_texels);
+// uniform vec2 PREFIX(offset_bottomleft);
+// uniform vec2 PREFIX(offset_topright);
vec4 FUNCNAME(vec2 tc) {
tc -= PREFIX(offset);
float top, left;
float border_offset_top, border_offset_left;
float border_offset_bottom, border_offset_right;
+ float uniform_offset[2], uniform_scale[2];
+ float uniform_normalized_coords_to_texels[2];
+ float uniform_offset_bottomleft[2], uniform_offset_topright[2];
};
class IntegralPaddingEffect : public PaddingEffect {
register_int("output_height", &output_height);
register_float("offset", &offset);
register_float("zoom", &zoom);
+ register_uniform_sampler2d("sample_tex", &uniform_sample_tex);
+ register_uniform_int("num_samples", &uniform_num_samples); // FIXME: What about GLSL pre-1.30?
+ register_uniform_float("num_loops", &uniform_num_loops);
+ register_uniform_float("slice_height", &uniform_slice_height);
+ register_uniform_float("sample_x_scale", &uniform_sample_x_scale);
+ register_uniform_float("sample_x_offset", &uniform_sample_x_offset);
+ register_uniform_float("whole_pixel_offset", &uniform_whole_pixel_offset);
glGenTextures(1, &texnum);
}
glBindTexture(GL_TEXTURE_2D, texnum);
check_error();
- set_uniform_int(glsl_program_num, prefix, "sample_tex", *sampler_num);
+ uniform_sample_tex = *sampler_num;
++*sampler_num;
- set_uniform_int(glsl_program_num, prefix, "num_samples", src_bilinear_samples);
- set_uniform_float(glsl_program_num, prefix, "num_loops", num_loops);
- set_uniform_float(glsl_program_num, prefix, "slice_height", slice_height);
+ uniform_num_samples = src_bilinear_samples;
+ uniform_num_loops = num_loops;
+ uniform_slice_height = slice_height;
// Instructions for how to convert integer sample numbers to positions in the weight texture.
- set_uniform_float(glsl_program_num, prefix, "sample_x_scale", 1.0f / src_bilinear_samples);
- set_uniform_float(glsl_program_num, prefix, "sample_x_offset", 0.5f / src_bilinear_samples);
+ uniform_sample_x_scale = 1.0f / src_bilinear_samples;
+ uniform_sample_x_offset = 0.5f / src_bilinear_samples;
- float whole_pixel_offset;
if (direction == SingleResamplePassEffect::VERTICAL) {
- whole_pixel_offset = lrintf(offset) / float(input_height);
+ uniform_whole_pixel_offset = lrintf(offset) / float(input_height);
} else {
- whole_pixel_offset = lrintf(offset) / float(input_width);
+ uniform_whole_pixel_offset = lrintf(offset) / float(input_width);
}
- set_uniform_float(glsl_program_num, prefix, "whole_pixel_offset", whole_pixel_offset);
// We specifically do not want mipmaps on the input texture;
// they break minification.
// DIRECTION_VERTICAL will be #defined to 1 if we are scaling vertically,
// and 0 otherwise.
-uniform sampler2D PREFIX(sample_tex);
-uniform int PREFIX(num_samples);
-uniform float PREFIX(num_loops);
-uniform float PREFIX(sample_x_scale);
-uniform float PREFIX(sample_x_offset);
-uniform float PREFIX(slice_height);
+// Implicit uniforms:
+// uniform sampler2D PREFIX(sample_tex);
+// uniform int PREFIX(num_samples);
+// uniform float PREFIX(num_loops);
+// uniform float PREFIX(sample_x_scale);
+// uniform float PREFIX(sample_x_offset);
+// uniform float PREFIX(slice_height);
// We put the fractional part of the offset (-0.5 to 0.5 pixels) in the weights
// because we have to (otherwise they'd do nothing). However, the support texture
// measured in _input_ pixels and tc is in _output_ pixels, although we could
// compensate for that.) However, the shader should be mostly bandwidth bound
// and not ALU bound, so an extra add per sample shouldn't be too hopeless.
-uniform float PREFIX(whole_pixel_offset);
+//
+// Implicitly declared:
+// uniform float PREFIX(whole_pixel_offset);
// Sample a single weight. First fetch information about where to sample
// and the weight from sample_tex, and then read the pixel itself.
EffectChain *chain;
Direction direction;
GLuint texnum;
+ GLint uniform_sample_tex;
+ float uniform_num_loops, uniform_slice_height, uniform_sample_x_scale, uniform_sample_x_offset;
+ float uniform_whole_pixel_offset;
+ int uniform_num_samples;
+
int input_width, input_height, output_width, output_height;
float offset, zoom;
int last_input_width, last_input_height, last_output_width, last_output_height;
register_int("output_slice_size", &output_slice_size);
register_int("offset", &offset);
register_int("direction", (int *)&direction);
+ register_uniform_float("output_coord_to_slice_num", &uniform_output_coord_to_slice_num);
+ register_uniform_float("slice_num_to_input_coord", &uniform_slice_num_to_input_coord);
+ register_uniform_float("slice_offset_to_input_coord", &uniform_slice_offset_to_input_coord);
+ register_uniform_float("offset", &uniform_offset);
}
string SliceEffect::output_fragment_shader()
get_output_size(&output_width, &output_height, &output_width, &output_height);
if (direction == HORIZONTAL) {
- set_uniform_float(glsl_program_num, prefix, "output_coord_to_slice_num", float(output_width) / float(output_slice_size));
- set_uniform_float(glsl_program_num, prefix, "slice_num_to_input_coord", float(input_slice_size) / float(input_width));
- set_uniform_float(glsl_program_num, prefix, "slice_offset_to_input_coord", float(output_slice_size) / float(input_width));
- set_uniform_float(glsl_program_num, prefix, "offset", float(offset) / float(input_width));
+ uniform_output_coord_to_slice_num = float(output_width) / float(output_slice_size);
+ uniform_slice_num_to_input_coord = float(input_slice_size) / float(input_width);
+ uniform_slice_offset_to_input_coord = float(output_slice_size) / float(input_width);
+ uniform_offset = float(offset) / float(input_width);
} else {
- set_uniform_float(glsl_program_num, prefix, "output_coord_to_slice_num", float(output_height) / float(output_slice_size));
- set_uniform_float(glsl_program_num, prefix, "slice_num_to_input_coord", float(input_slice_size) / float(input_height));
- 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, "offset", float(offset) / float(input_height));
+ uniform_output_coord_to_slice_num = float(output_height) / float(output_slice_size);
+ uniform_slice_num_to_input_coord = float(input_slice_size) / float(input_height);
+ uniform_slice_offset_to_input_coord = float(output_slice_size) / float(input_height);
+ uniform_offset = float(offset) / float(input_height);
}
// Normalized coordinates could potentially cause blurring of the image.
-uniform float PREFIX(output_coord_to_slice_num);
-uniform float PREFIX(slice_num_to_input_coord);
-uniform float PREFIX(slice_offset_to_input_coord);
-uniform float PREFIX(offset);
+// Implicit uniforms:
+// uniform float PREFIX(output_coord_to_slice_num);
+// uniform float PREFIX(slice_num_to_input_coord);
+// uniform float PREFIX(slice_offset_to_input_coord);
+// uniform float PREFIX(offset);
vec4 FUNCNAME(vec2 tc) {
// DIRECTION_VERTICAL will be #defined to 1 if we are expanding vertically,
int input_slice_size, output_slice_size;
int offset;
Direction direction;
+
+ float uniform_output_coord_to_slice_num, uniform_slice_num_to_input_coord;
+ float uniform_slice_offset_to_input_coord, uniform_offset;
};
} // namespace movit
// changes, even within git versions. There is no specific version
// documentation outside the regular changelogs, though.
-#define MOVIT_VERSION 1
+#define MOVIT_VERSION 2
#endif // !defined(_MOVIT_VERSION_H)
VignetteEffect::VignetteEffect()
: center(0.5f, 0.5f),
- aspect_correction(1.0f, 1.0f),
+ uniform_aspect_correction(1.0f, 1.0f),
+ uniform_flipped_center(0.5f, 0.5f),
radius(0.3f),
inner_radius(0.3f)
{
register_vec2("center", (float *)¢er);
register_float("radius", (float *)&radius);
register_float("inner_radius", (float *)&inner_radius);
+ register_uniform_float("pihalf_div_radius", &uniform_pihalf_div_radius);
+ register_uniform_vec2("aspect_correction", (float *)&uniform_aspect_correction);
+ register_uniform_vec2("flipped_center", (float *)&uniform_flipped_center);
}
string VignetteEffect::output_fragment_shader()
void VignetteEffect::inform_input_size(unsigned input_num, unsigned width, unsigned height) {
assert(input_num == 0);
if (width >= height) {
- aspect_correction = Point2D(float(width) / float(height), 1.0f);
+ uniform_aspect_correction = Point2D(float(width) / float(height), 1.0f);
} else {
- aspect_correction = Point2D(1.0f, float(height) / float(width));
+ uniform_aspect_correction = Point2D(1.0f, float(height) / float(width));
}
}
{
Effect::set_gl_state(glsl_program_num, prefix, sampler_num);
- set_uniform_float(glsl_program_num, prefix, "pihalf_div_radius", 0.5 * M_PI / radius);
- set_uniform_vec2(glsl_program_num, prefix, "aspect_correction", (float *)&aspect_correction);
-
- Point2D flipped_center(center.x, 1.0f - center.y);
- set_uniform_vec2(glsl_program_num, prefix, "flipped_center", (float *)&flipped_center);
+ uniform_pihalf_div_radius = 0.5 * M_PI / radius;
+ uniform_flipped_center = Point2D(center.x, 1.0f - center.y);
}
} // namespace movit
// A simple, circular vignette, with a cos² falloff.
-
-uniform float PREFIX(pihalf_div_radius);
-uniform vec2 PREFIX(aspect_correction);
-uniform vec2 PREFIX(flipped_center);
+
+// Implicit uniforms:
+// uniform float PREFIX(pihalf_div_radius);
+//
+// uniform vec2 PREFIX(aspect_correction);
+// uniform vec2 PREFIX(flipped_center);
vec4 FUNCNAME(vec2 tc) {
vec4 x = INPUT(tc);
void set_gl_state(GLuint glsl_program_num, const std::string &prefix, unsigned *sampler_num);
private:
- Point2D center, aspect_correction;
+ Point2D center;
+ Point2D uniform_aspect_correction, uniform_flipped_center;
float radius, inner_radius;
+ float uniform_pihalf_div_radius;
};
} // namespace movit
{
register_vec3("neutral_color", (float *)&neutral_color);
register_float("output_color_temperature", &output_color_temperature);
+ register_uniform_mat3("correction_matrix", &uniform_correction_matrix);
}
string WhiteBalanceEffect::output_fragment_shader()
* Note that since we postmultiply our vectors, the order of the matrices
* has to be the opposite of the execution order.
*/
- Matrix3d corr_matrix =
+ uniform_correction_matrix =
rgb_to_xyz_matrix.inverse() *
Map<const Matrix3d>(xyz_to_lms_matrix).inverse() *
lms_scale.asDiagonal() *
Map<const Matrix3d>(xyz_to_lms_matrix) *
rgb_to_xyz_matrix;
- set_uniform_mat3(glsl_program_num, prefix, "correction_matrix", corr_matrix);
}
} // namespace movit
-uniform mat3 PREFIX(correction_matrix);
+// Implicit uniforms:
+// uniform mat3 PREFIX(correction_matrix);
vec4 FUNCNAME(vec2 tc) {
vec4 ret = INPUT(tc);
#include <epoxy/gl.h>
#include <string>
+#include <Eigen/Core>
#include "effect.h"
// Output color temperature (in Kelvins).
// Choosing 6500 will lead to no color cast (ie., the neutral color becomes perfectly gray).
float output_color_temperature;
+
+ Eigen::Matrix3d uniform_correction_matrix;
};
} // namespace movit
pitches[CHANNEL_CHROMA] = width / ycbcr_format.chroma_subsampling_x;
pixel_data = NULL;
+
+ register_uniform_sampler2d("tex_y", &uniform_tex_y);
+ register_uniform_sampler2d("tex_cbcr", &uniform_tex_cbcr);
}
YCbCr422InterleavedInput::~YCbCr422InterleavedInput()
check_error();
// Bind samplers.
- set_uniform_int(glsl_program_num, prefix, "tex_y", *sampler_num + 0);
- set_uniform_int(glsl_program_num, prefix, "tex_cbcr", *sampler_num + 1);
+ uniform_tex_y = *sampler_num + 0;
+ uniform_tex_cbcr = *sampler_num + 1;
*sampler_num += 2;
}
-uniform sampler2D PREFIX(tex_y);
-uniform sampler2D PREFIX(tex_cbcr);
+// Implicit uniforms:
+// uniform sampler2D PREFIX(tex_y);
+// uniform sampler2D PREFIX(tex_cbcr);
vec4 FUNCNAME(vec2 tc) {
// OpenGL's origin is bottom-left, but most graphics software assumes
unsigned width, height;
const unsigned char *pixel_data;
ResourcePool *resource_pool;
+
+ GLint uniform_tex_y, uniform_tex_cbcr;
};
} // namespace movit
heights[2] = height / ycbcr_format.chroma_subsampling_y;
pixel_data[0] = pixel_data[1] = pixel_data[2] = NULL;
+
+ register_uniform_sampler2d("tex_y", &uniform_tex_y);
+ register_uniform_sampler2d("tex_cb", &uniform_tex_cb);
+ register_uniform_sampler2d("tex_cr", &uniform_tex_cr);
}
YCbCrInput::~YCbCrInput()
check_error();
// Bind samplers.
- set_uniform_int(glsl_program_num, prefix, "tex_y", *sampler_num + 0);
- set_uniform_int(glsl_program_num, prefix, "tex_cb", *sampler_num + 1);
- set_uniform_int(glsl_program_num, prefix, "tex_cr", *sampler_num + 2);
+ uniform_tex_y = *sampler_num + 0;
+ uniform_tex_cb = *sampler_num + 1;
+ uniform_tex_cr = *sampler_num + 2;
*sampler_num += 3;
}
-uniform sampler2D PREFIX(tex_y);
-uniform sampler2D PREFIX(tex_cb);
-uniform sampler2D PREFIX(tex_cr);
+// Implicit uniforms:
+// uniform sampler2D PREFIX(tex_y);
+// uniform sampler2D PREFIX(tex_cb);
+// uniform sampler2D PREFIX(tex_cr);
vec4 FUNCNAME(vec2 tc) {
// OpenGL's origin is bottom-left, but most graphics software assumes
ImageFormat image_format;
YCbCrFormat ycbcr_format;
GLuint pbos[3], texture_num[3];
+ GLint uniform_tex_y, uniform_tex_cb, uniform_tex_cr;
unsigned width, height, widths[3], heights[3];
const unsigned char *pixel_data[3];