From: Steinar H. Gunderson Date: Tue, 2 Oct 2012 17:58:09 +0000 (+0200) Subject: Add a new framework for 1D-LUTs via fp16 textures. Make the gamma compression and... X-Git-Tag: 1.0~421 X-Git-Url: https://git.sesse.net/?p=movit;a=commitdiff_plain;h=2ced784c6599cb0b21427481ee17f4c8f6afdada Add a new framework for 1D-LUTs via fp16 textures. Make the gamma compression and expansion effects use it. --- diff --git a/effect.cpp b/effect.cpp index 3a4f8c8..02fc1f7 100644 --- a/effect.cpp +++ b/effect.cpp @@ -9,6 +9,18 @@ #include #include +void set_uniform_int(GLuint glsl_program_num, const std::string &prefix, const std::string &key, int value) +{ + std::string name = prefix + "_" + key; + GLint l = glGetUniformLocation(glsl_program_num, name.c_str()); + if (l == -1) { + return; + } + check_error(); + glUniform1i(l, value); + check_error(); +} + void set_uniform_float(GLuint glsl_program_num, const std::string &prefix, const std::string &key, float value) { std::string name = prefix + "_" + key; @@ -105,6 +117,34 @@ void Effect::register_vec3(const std::string &key, float *values) params_vec3[key] = values; } +void Effect::register_1d_texture(const std::string &key, float *values, size_t size) +{ + assert(params_tex_1d.count(key) == 0); + + Texture1D tex; + tex.values = values; + tex.size = size; + tex.needs_update = false; + glGenTextures(1, &tex.texture_num); + + glBindTexture(GL_TEXTURE_1D, tex.texture_num); + check_error(); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + check_error(); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + check_error(); + glTexImage1D(GL_TEXTURE_1D, 0, GL_LUMINANCE16F_ARB, size, 0, GL_LUMINANCE, GL_FLOAT, values); + check_error(); + + params_tex_1d[key] = tex; +} + +void Effect::invalidate_1d_texture(const std::string &key) +{ + assert(params_tex_1d.count(key) != 0); + params_tex_1d[key].needs_update = true; +} + // Output convenience uniforms for each parameter. // These will be filled in per-frame. std::string Effect::output_convenience_uniforms() @@ -131,10 +171,17 @@ std::string Effect::output_convenience_uniforms() sprintf(buf, "uniform vec3 PREFIX(%s);\n", it->first.c_str()); output.append(buf); } + for (std::map::const_iterator it = params_tex_1d.begin(); + it != params_tex_1d.end(); + ++it) { + char buf[256]; + sprintf(buf, "uniform sampler1D PREFIX(%s);\n", it->first.c_str()); + output.append(buf); + } return output; } -void Effect::set_uniforms(GLuint glsl_program_num, const std::string& prefix) +void Effect::set_uniforms(GLuint glsl_program_num, const std::string& prefix, unsigned *sampler_num) { for (std::map::const_iterator it = params_float.begin(); it != params_float.end(); @@ -151,4 +198,21 @@ void Effect::set_uniforms(GLuint glsl_program_num, const std::string& prefix) ++it) { set_uniform_vec3(glsl_program_num, prefix, it->first, it->second); } + + for (std::map::const_iterator it = params_tex_1d.begin(); + it != params_tex_1d.end(); + ++it) { + glActiveTexture(GL_TEXTURE0 + *sampler_num); + check_error(); + glBindTexture(GL_TEXTURE_1D, it->second.texture_num); + check_error(); + + if (it->second.needs_update) { + glTexImage1D(GL_TEXTURE_1D, 0, GL_LUMINANCE16F_ARB, it->second.size, 0, GL_LUMINANCE, GL_FLOAT, it->second.values); + check_error(); + } + + set_uniform_int(glsl_program_num, prefix, it->first, *sampler_num); + ++*sampler_num; + } } diff --git a/effect.h b/effect.h index 1478c6e..af2f516 100644 --- a/effect.h +++ b/effect.h @@ -23,6 +23,7 @@ struct RGBTriplet { }; // Convenience functions that deal with prepending the prefix. +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); void set_uniform_vec2(GLuint glsl_program_num, const std::string &prefix, const std::string &key, const float *values); void set_uniform_vec3(GLuint glsl_program_num, const std::string &prefix, const std::string &key, const float *values); @@ -37,7 +38,7 @@ public: virtual std::string output_convenience_uniforms(); virtual std::string output_fragment_shader() = 0; - virtual void set_uniforms(GLuint glsl_program_num, const std::string& prefix); + virtual void set_uniforms(GLuint glsl_program_num, const std::string& prefix, unsigned *sampler_num); // Neither of these take ownership. bool set_int(const std::string&, int value); @@ -51,12 +52,22 @@ protected: 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); + void register_1d_texture(const std::string &key, float *values, size_t size); + void invalidate_1d_texture(const std::string &key); private: + struct Texture1D { + float *values; + size_t size; + bool needs_update; + GLuint texture_num; + }; + std::map params_int; std::map params_float; std::map params_vec2; std::map params_vec3; + std::map params_tex_1d; }; #endif // !defined(_EFFECT_H) diff --git a/effect_chain.cpp b/effect_chain.cpp index a6ff90e..9ff5fd8 100644 --- a/effect_chain.cpp +++ b/effect_chain.cpp @@ -261,10 +261,11 @@ void EffectChain::render_to_screen(unsigned char *src) check_error(); glUniform1i(glGetUniformLocation(glsl_program_num, "input_tex"), 0); + unsigned sampler_num = 1; for (unsigned i = 0; i < effects.size(); ++i) { char effect_id[256]; sprintf(effect_id, "eff%d", i); - effects[i]->set_uniforms(glsl_program_num, effect_id); + effects[i]->set_uniforms(glsl_program_num, effect_id, &sampler_num); } glDisable(GL_BLEND); diff --git a/gamma_compression_effect.cpp b/gamma_compression_effect.cpp index 69d2493..945267c 100644 --- a/gamma_compression_effect.cpp +++ b/gamma_compression_effect.cpp @@ -1,3 +1,4 @@ +#include #include #include "gamma_compression_effect.h" @@ -7,18 +8,37 @@ GammaCompressionEffect::GammaCompressionEffect() : destination_curve(GAMMA_LINEAR) { register_int("destination_curve", (int *)&destination_curve); + register_1d_texture("compression_curve_tex", compression_curve, COMPRESSION_CURVE_SIZE); } std::string GammaCompressionEffect::output_fragment_shader() { - switch (destination_curve) { - case GAMMA_LINEAR: + if (destination_curve == GAMMA_LINEAR) { return read_file("identity.frag"); - case GAMMA_sRGB: - return read_file("gamma_compression_effect_srgb.frag"); - case GAMMA_REC_709: // and GAMMA_REC_601 - return read_file("gamma_compression_effect_rec709.frag"); - default: - assert(false); } + if (destination_curve == GAMMA_sRGB) { + for (unsigned i = 0; i < COMPRESSION_CURVE_SIZE; ++i) { + float x = i / (float)(COMPRESSION_CURVE_SIZE - 1); + if (x < 0.0031308f) { + compression_curve[i] = 12.92f * x; + } else { + compression_curve[i] = 1.055f * pow(x, 1.0f / 2.4f) - 0.055f; + } + } + invalidate_1d_texture("compression_curve_tex"); + return read_file("gamma_compression_effect.frag"); + } + if (destination_curve == GAMMA_REC_709) { // And Rec. 601. + for (unsigned i = 0; i < COMPRESSION_CURVE_SIZE; ++i) { + float x = i / (float)(COMPRESSION_CURVE_SIZE - 1); + if (x < 0.018f) { + compression_curve[i] = 4.5f * x; + } else { + compression_curve[i] = 1.099f * pow(x, 0.45f) - 0.099; + } + } + invalidate_1d_texture("compression_curve_tex"); + return read_file("gamma_compression_effect.frag"); + } + assert(false); } diff --git a/gamma_compression_effect.frag b/gamma_compression_effect.frag new file mode 100644 index 0000000..8944982 --- /dev/null +++ b/gamma_compression_effect.frag @@ -0,0 +1,11 @@ +// Compress to sRGB gamma curve. + +vec4 FUNCNAME(vec2 tc) { + vec4 x = LAST_INPUT(tc); + + x.r = texture1D(PREFIX(compression_curve_tex), x.r).x; + x.g = texture1D(PREFIX(compression_curve_tex), x.g).x; + x.b = texture1D(PREFIX(compression_curve_tex), x.b).x; + + return x; +} diff --git a/gamma_compression_effect.h b/gamma_compression_effect.h index 3f32f5c..7016341 100644 --- a/gamma_compression_effect.h +++ b/gamma_compression_effect.h @@ -4,6 +4,8 @@ #include "effect.h" #include "effect_chain.h" +#define COMPRESSION_CURVE_SIZE 4096 + class GammaCompressionEffect : public Effect { public: GammaCompressionEffect(); @@ -13,6 +15,7 @@ public: private: GammaCurve destination_curve; + float compression_curve[COMPRESSION_CURVE_SIZE]; }; #endif // !defined(_GAMMA_COMPRESSION_EFFECT_H) diff --git a/gamma_compression_effect_rec709.frag b/gamma_compression_effect_rec709.frag deleted file mode 100644 index 807cd49..0000000 --- a/gamma_compression_effect_rec709.frag +++ /dev/null @@ -1,31 +0,0 @@ -// Compress to Rec. 601/Rec. 709 gamma curve. - -#if 0 - -// if we have the lut -uniform sampler1D PREFIX(rec709_inverse_tex); - -vec4 FUNCNAME(vec2 tc) { - vec4 x = LAST_INPUT(tc); - - x.r = texture1D(PREFIX(rec709_inverse_tex), x.r).x; - x.g = texture1D(PREFIX(rec709_inverse_tex), x.g).x; - x.b = texture1D(PREFIX(rec709_inverse_tex), x.b).x; - - return x; -} - -#else - -// use arithmetic (slow) -vec4 FUNCNAME(vec2 tc) { - vec4 x = LAST_INPUT(tc); - - vec3 a = vec3(4.5) * x.rgb; - vec3 b = vec3(1.099) * pow(x.rgb, vec3(0.45)) - vec3(0.099); - vec3 f = vec3(greaterThan(x.rgb, vec3(0.018))); - - return vec4(mix(a, b, f), x.a); -} - -#endif diff --git a/gamma_compression_effect_srgb.frag b/gamma_compression_effect_srgb.frag deleted file mode 100644 index e65d6bb..0000000 --- a/gamma_compression_effect_srgb.frag +++ /dev/null @@ -1,31 +0,0 @@ -// Compress to sRGB gamma curve. - -#if 0 - -// if we have the lut -uniform sampler1D PREFIX(srgb_inverse_tex); - -vec4 FUNCNAME(vec2 tc) { - vec4 x = LAST_INPUT(tc); - - x.r = texture1D(PREFIX(srgb_inverse_tex), x.r).x; - x.g = texture1D(PREFIX(srgb_inverse_tex), x.g).x; - x.b = texture1D(PREFIX(srgb_inverse_tex), x.b).x; - - return x; -} - -#else - -// use arithmetic (slow) -vec4 FUNCNAME(vec2 tc) { - vec4 x = LAST_INPUT(tc); - - vec3 a = vec3(12.92) * x.rgb; - vec3 b = vec3(1.055) * pow(x.rgb, vec3(1.0/2.4)) - vec3(0.055); - vec3 f = vec3(greaterThan(x.rgb, vec3(0.0031308))); - - return vec4(mix(a, b, f), x.a); -} - -#endif diff --git a/gamma_expansion_effect.cpp b/gamma_expansion_effect.cpp index 61f0330..d4e532f 100644 --- a/gamma_expansion_effect.cpp +++ b/gamma_expansion_effect.cpp @@ -1,3 +1,4 @@ +#include #include #include "gamma_expansion_effect.h" @@ -7,18 +8,37 @@ GammaExpansionEffect::GammaExpansionEffect() : source_curve(GAMMA_LINEAR) { register_int("source_curve", (int *)&source_curve); + register_1d_texture("expansion_curve_tex", expansion_curve, EXPANSION_CURVE_SIZE); } std::string GammaExpansionEffect::output_fragment_shader() { - switch (source_curve) { - case GAMMA_LINEAR: + if (source_curve == GAMMA_LINEAR) { return read_file("identity.frag"); - case GAMMA_sRGB: - return read_file("gamma_expansion_effect_srgb.frag"); - case GAMMA_REC_709: // and GAMMA_REC_601 - return read_file("gamma_expansion_effect_rec709.frag"); - default: - assert(false); } + if (source_curve == GAMMA_sRGB) { + for (unsigned i = 0; i < EXPANSION_CURVE_SIZE; ++i) { + float x = i / (float)(EXPANSION_CURVE_SIZE - 1); + if (x < 0.04045f) { + expansion_curve[i] = (1.0/12.92f) * x; + } else { + expansion_curve[i] = pow((x + 0.055) * (1.0/1.055f), 2.4); + } + } + invalidate_1d_texture("expansion_curve_tex"); + return read_file("gamma_expansion_effect.frag"); + } + if (source_curve == GAMMA_REC_709) { // And Rec. 601. + for (unsigned i = 0; i < EXPANSION_CURVE_SIZE; ++i) { + float x = i / (float)(EXPANSION_CURVE_SIZE - 1); + if (x < 0.081f) { + expansion_curve[i] = (1.0/4.5f) * x; + } else { + expansion_curve[i] = pow((x + 0.099) * (1.0/1.099f), 1.0f/0.45f); + } + } + invalidate_1d_texture("expansion_curve_tex"); + return read_file("gamma_expansion_effect.frag"); + } + assert(false); } diff --git a/gamma_expansion_effect.frag b/gamma_expansion_effect.frag new file mode 100644 index 0000000..4a5db9e --- /dev/null +++ b/gamma_expansion_effect.frag @@ -0,0 +1,11 @@ +// Expand sRGB gamma curve. + +vec4 FUNCNAME(vec2 tc) { + vec4 x = LAST_INPUT(tc); + + x.r = texture1D(PREFIX(expansion_curve_tex), x.r).x; + x.g = texture1D(PREFIX(expansion_curve_tex), x.g).x; + x.b = texture1D(PREFIX(expansion_curve_tex), x.b).x; + + return x; +} diff --git a/gamma_expansion_effect.h b/gamma_expansion_effect.h index 1125d11..f079e70 100644 --- a/gamma_expansion_effect.h +++ b/gamma_expansion_effect.h @@ -4,6 +4,8 @@ #include "effect.h" #include "effect_chain.h" +#define EXPANSION_CURVE_SIZE 256 + class GammaExpansionEffect : public Effect { public: GammaExpansionEffect(); @@ -14,6 +16,7 @@ public: private: GammaCurve source_curve; + float expansion_curve[EXPANSION_CURVE_SIZE]; }; #endif // !defined(_GAMMA_EXPANSION_EFFECT_H) diff --git a/gamma_expansion_effect_rec709.frag b/gamma_expansion_effect_rec709.frag deleted file mode 100644 index 00259cc..0000000 --- a/gamma_expansion_effect_rec709.frag +++ /dev/null @@ -1,31 +0,0 @@ -// Expand Rec. 601/Rec. 709 gamma curve. - -#if 0 - -// if we have the lut -uniform sampler1D PREFIX(rec709_tex); - -vec4 FUNCNAME(vec2 tc) { - vec4 x = LAST_INPUT(tc); - - x.r = texture1D(PREFIX(rec709_tex), x.r).x; - x.g = texture1D(PREFIX(rec709_tex), x.g).x; - x.b = texture1D(PREFIX(rec709_tex), x.b).x; - - return x; -} - -#else - -// use arithmetic (slow) -vec4 FUNCNAME(vec2 tc) { - vec4 x = LAST_INPUT(tc); - - vec3 a = x.rgb * vec3(1.0/4.500); - vec3 b = pow((x.rgb + vec3(0.099)) * vec3(1.0/1.099), vec3(1.0/0.45)); - vec3 f = vec3(greaterThan(x.rgb, vec3(0.081))); - - return vec4(mix(a, b, f), x.a); -} - -#endif diff --git a/gamma_expansion_effect_srgb.frag b/gamma_expansion_effect_srgb.frag deleted file mode 100644 index e90bb62..0000000 --- a/gamma_expansion_effect_srgb.frag +++ /dev/null @@ -1,31 +0,0 @@ -// Expand sRGB gamma curve. - -#if 0 - -// if we have the lut -uniform sampler1D PREFIX(srgb_tex); - -vec4 FUNCNAME(vec2 tc) { - vec4 x = LAST_INPUT(tc); - - x.r = texture1D(PREFIX(srgb_tex), x.r).x; - x.g = texture1D(PREFIX(srgb_tex), x.g).x; - x.b = texture1D(PREFIX(srgb_tex), x.b).x; - - return x; -} - -#else - -// use arithmetic (slow) -vec4 FUNCNAME(vec2 tc) { - vec4 x = LAST_INPUT(tc); - - vec3 a = x.rgb * vec3(1.0/12.92); - vec3 b = pow((x.rgb + vec3(0.055)) * vec3(1.0/1.055), vec3(2.4)); - vec3 f = vec3(greaterThan(x.rgb, vec3(0.04045))); - - return vec4(mix(a, b, f), x.a); -} - -#endif diff --git a/lift_gamma_gain_effect.cpp b/lift_gamma_gain_effect.cpp index 993525d..19cef18 100644 --- a/lift_gamma_gain_effect.cpp +++ b/lift_gamma_gain_effect.cpp @@ -22,9 +22,9 @@ std::string LiftGammaGainEffect::output_fragment_shader() return read_file("lift_gamma_gain_effect.frag"); } -void LiftGammaGainEffect::set_uniforms(GLuint glsl_program_num, const std::string &prefix) +void LiftGammaGainEffect::set_uniforms(GLuint glsl_program_num, const std::string &prefix, unsigned *sampler_num) { - Effect::set_uniforms(glsl_program_num, prefix); + Effect::set_uniforms(glsl_program_num, prefix, sampler_num); RGBTriplet gain_pow_inv_gamma( pow(gain.r, 1.0f / gamma.r), diff --git a/lift_gamma_gain_effect.h b/lift_gamma_gain_effect.h index 271b2a1..6364eeb 100644 --- a/lift_gamma_gain_effect.h +++ b/lift_gamma_gain_effect.h @@ -8,7 +8,7 @@ public: LiftGammaGainEffect(); std::string output_fragment_shader(); - void set_uniforms(GLuint glsl_program_num, const std::string &prefix); + void set_uniforms(GLuint glsl_program_num, const std::string &prefix, unsigned *sampler_num); private: RGBTriplet lift, gamma, gain; diff --git a/main.cpp b/main.cpp index 1386a0f..666a1ed 100644 --- a/main.cpp +++ b/main.cpp @@ -178,44 +178,6 @@ int main(int argc, char **argv) //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 4); //check_error(); -#if 0 - // sRGB reverse LUT - glBindTexture(GL_TEXTURE_1D, SRGB_REVERSE_LUT); - check_error(); - glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - check_error(); - float srgb_reverse_tex[4096]; - for (unsigned i = 0; i < 4096; ++i) { - float x = i / 4095.0; - if (x < 0.0031308f) { - srgb_reverse_tex[i] = 12.92f * x; - } else { - srgb_reverse_tex[i] = 1.055f * pow(x, 1.0f / 2.4f) - 0.055f; - } - } - glTexImage1D(GL_TEXTURE_1D, 0, GL_LUMINANCE16F_ARB, 4096, 0, GL_LUMINANCE, GL_FLOAT, srgb_reverse_tex); - check_error(); - - // sRGB LUT - glBindTexture(GL_TEXTURE_1D, SRGB_LUT); - check_error(); - glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - check_error(); - float srgb_tex[256]; - for (unsigned i = 0; i < 256; ++i) { - float x = i / 255.0; - if (x < 0.04045f) { - srgb_tex[i] = x * (1.0f / 12.92f); - } else { - srgb_tex[i] = pow((x + 0.055) * (1.0 / 1.055f), 2.4); - } - } - glTexImage1D(GL_TEXTURE_1D, 0, GL_LUMINANCE16F_ARB, 256, 0, GL_LUMINANCE, GL_FLOAT, srgb_tex); - check_error(); -#endif - // generate a PDO to hold the data we read back with glReadPixels() // (Intel/DRI goes into a slow path if we don't read to PDO) glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 1); diff --git a/vignette_effect.cpp b/vignette_effect.cpp index f1449ae..e6e7e7b 100644 --- a/vignette_effect.cpp +++ b/vignette_effect.cpp @@ -22,9 +22,9 @@ std::string VignetteEffect::output_fragment_shader() return read_file("vignette_effect.frag"); } -void VignetteEffect::set_uniforms(GLuint glsl_program_num, const std::string &prefix) +void VignetteEffect::set_uniforms(GLuint glsl_program_num, const std::string &prefix, unsigned *sampler_num) { - Effect::set_uniforms(glsl_program_num, prefix); + Effect::set_uniforms(glsl_program_num, prefix, sampler_num); set_uniform_float(glsl_program_num, prefix, "inv_radius", 1.0f / radius); diff --git a/vignette_effect.h b/vignette_effect.h index b8e0f68..82c648c 100644 --- a/vignette_effect.h +++ b/vignette_effect.h @@ -8,7 +8,7 @@ public: VignetteEffect(); std::string output_fragment_shader(); - void set_uniforms(GLuint glsl_program_num, const std::string &prefix); + void set_uniforms(GLuint glsl_program_num, const std::string &prefix, unsigned *sampler_num); private: Point2D center;