Add a new framework for 1D-LUTs via fp16 textures. Make the gamma compression and...
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Tue, 2 Oct 2012 17:58:09 +0000 (19:58 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Tue, 2 Oct 2012 17:58:09 +0000 (19:58 +0200)
18 files changed:
effect.cpp
effect.h
effect_chain.cpp
gamma_compression_effect.cpp
gamma_compression_effect.frag [new file with mode: 0644]
gamma_compression_effect.h
gamma_compression_effect_rec709.frag [deleted file]
gamma_compression_effect_srgb.frag [deleted file]
gamma_expansion_effect.cpp
gamma_expansion_effect.frag [new file with mode: 0644]
gamma_expansion_effect.h
gamma_expansion_effect_rec709.frag [deleted file]
gamma_expansion_effect_srgb.frag [deleted file]
lift_gamma_gain_effect.cpp
lift_gamma_gain_effect.h
main.cpp
vignette_effect.cpp
vignette_effect.h

index 3a4f8c8..02fc1f7 100644 (file)
@@ -9,6 +9,18 @@
 #include <GL/gl.h>
 #include <GL/glext.h>
 
+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<std::string, Texture1D>::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<std::string, float*>::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<std::string, Texture1D>::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;
+       }
 }
index 1478c6e..af2f516 100644 (file)
--- 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<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, Texture1D> params_tex_1d;
 };
 
 #endif // !defined(_EFFECT_H)
index a6ff90e..9ff5fd8 100644 (file)
@@ -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);
index 69d2493..945267c 100644 (file)
@@ -1,3 +1,4 @@
+#include <math.h>
 #include <assert.h>
 
 #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 (file)
index 0000000..8944982
--- /dev/null
@@ -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;
+}
index 3f32f5c..7016341 100644 (file)
@@ -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 (file)
index 807cd49..0000000
+++ /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 (file)
index e65d6bb..0000000
+++ /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
index 61f0330..d4e532f 100644 (file)
@@ -1,3 +1,4 @@
+#include <math.h>
 #include <assert.h>
 
 #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 (file)
index 0000000..4a5db9e
--- /dev/null
@@ -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;
+}
index 1125d11..f079e70 100644 (file)
@@ -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 (file)
index 00259cc..0000000
+++ /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 (file)
index e90bb62..0000000
+++ /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
index 993525d..19cef18 100644 (file)
@@ -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),
index 271b2a1..6364eeb 100644 (file)
@@ -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;
index 1386a0f..666a1ed 100644 (file)
--- 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);
index f1449ae..e6e7e7b 100644 (file)
@@ -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);
 
index b8e0f68..82c648c 100644 (file)
@@ -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;