]> git.sesse.net Git - movit/blobdiff - effect_chain_test.cpp
Drop setting the locale altogether.
[movit] / effect_chain_test.cpp
index 442c2fb889fed5f82d2b6abff95269cfa76abce8..dd4ffa2254835978c77b7243a563867fd07a9411 100644 (file)
@@ -2,19 +2,29 @@
 //
 // Note that this also contains the tests for some of the simpler effects.
 
-#include <GL/glew.h>
+#include <locale>
+#include <sstream>
+#include <string>
+
+#include <epoxy/gl.h>
 #include <assert.h>
 
 #include "effect.h"
 #include "effect_chain.h"
 #include "flat_input.h"
 #include "gtest/gtest.h"
+#include "init.h"
 #include "input.h"
 #include "mirror_effect.h"
+#include "multiply_effect.h"
 #include "resize_effect.h"
 #include "test_util.h"
 #include "util.h"
 
+using namespace std;
+
+namespace movit {
+
 TEST(EffectChainTest, EmptyChain) {
        float data[] = {
                0.0f, 0.25f, 0.3f,
@@ -31,8 +41,8 @@ TEST(EffectChainTest, EmptyChain) {
 class IdentityEffect : public Effect {
 public:
        IdentityEffect() {}
-       virtual std::string effect_type_id() const { return "IdentityEffect"; }
-       std::string output_fragment_shader() { return read_file("identity.frag"); }
+       virtual string effect_type_id() const { return "IdentityEffect"; }
+       string output_fragment_shader() { return read_file("identity.frag"); }
 };
 
 TEST(EffectChainTest, Identity) {
@@ -52,8 +62,8 @@ TEST(EffectChainTest, Identity) {
 class BouncingIdentityEffect : public Effect {
 public:
        BouncingIdentityEffect() {}
-       virtual std::string effect_type_id() const { return "IdentityEffect"; }
-       std::string output_fragment_shader() { return read_file("identity.frag"); }
+       virtual string effect_type_id() const { return "IdentityEffect"; }
+       string output_fragment_shader() { return read_file("identity.frag"); }
        bool needs_texture_bounce() const { return true; }
        AlphaHandling alpha_handling() const { return DONT_CARE_ALPHA_TYPE; }
 };
@@ -92,8 +102,8 @@ TEST(MirrorTest, BasicTest) {
 class InvertEffect : public Effect {
 public:
        InvertEffect() {}
-       virtual std::string effect_type_id() const { return "InvertEffect"; }
-       std::string output_fragment_shader() { return read_file("invert_effect.frag"); }
+       virtual string effect_type_id() const { return "InvertEffect"; }
+       string output_fragment_shader() { return read_file("invert_effect.frag"); }
 
        // A real invert would actually care about its alpha,
        // but in this unit test, it only complicates things.
@@ -107,8 +117,8 @@ template<class T>
 class RewritingEffect : public Effect {
 public:
        RewritingEffect() : effect(new T()), replaced_node(NULL) {}
-       virtual std::string effect_type_id() const { return "RewritingEffect[" + effect->effect_type_id() + "]"; }
-       std::string output_fragment_shader() { EXPECT_TRUE(false); return read_file("identity.frag"); }
+       virtual string effect_type_id() const { return "RewritingEffect[" + effect->effect_type_id() + "]"; }
+       string output_fragment_shader() { EXPECT_TRUE(false); return read_file("identity.frag"); }
        virtual void rewrite_graph(EffectChain *graph, Node *self) {
                replaced_node = graph->add_node(effect);
                graph->replace_receiver(self, replaced_node);
@@ -146,19 +156,23 @@ TEST(EffectChainTest, RewritingWorksAndGammaConversionsAreInserted) {
 
 TEST(EffectChainTest, RewritingWorksAndTexturesAreAskedForsRGB) {
        unsigned char data[] = {
-               0, 64,
-               128, 255,
+                 0,   0,   0, 255,
+                64,  64,  64, 255,
+               128, 128, 128, 255,
+               255, 255, 255, 255,
        };
-       float expected_data[4] = {
-               1.0f, 0.9771f,
-               0.8983f, 0.0f,
+       float expected_data[] = {
+               1.0000f, 1.0000f, 1.0000f, 1.0000f,
+               0.9771f, 0.9771f, 0.9771f, 1.0000f,
+               0.8983f, 0.8983f, 0.8983f, 1.0000f,
+               0.0000f, 0.0000f, 0.0000f, 1.0000f
        };
-       float out_data[4];
-       EffectChainTester tester(NULL, 2, 2);
-       tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_sRGB);
+       float out_data[4 * 4];
+       EffectChainTester tester(NULL, 1, 4);
+       tester.add_input(data, FORMAT_RGBA_POSTMULTIPLIED_ALPHA, COLORSPACE_sRGB, GAMMA_sRGB);
        RewritingEffect<InvertEffect> *effect = new RewritingEffect<InvertEffect>();
        tester.get_chain()->add_effect(effect);
-       tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_sRGB);
+       tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
 
        Node *node = effect->replaced_node;
        ASSERT_EQ(1, node->incoming_links.size());
@@ -166,7 +180,7 @@ TEST(EffectChainTest, RewritingWorksAndTexturesAreAskedForsRGB) {
        EXPECT_EQ("FlatInput", node->incoming_links[0]->effect->effect_type_id());
        EXPECT_EQ("GammaCompressionEffect", node->outgoing_links[0]->effect->effect_type_id());
 
-       expect_equal(expected_data, out_data, 2, 2);
+       expect_equal(expected_data, out_data, 4, 4);
 }
 
 TEST(EffectChainTest, RewritingWorksAndColorspaceConversionsAreInserted) {
@@ -201,7 +215,7 @@ public:
            : FlatInput(format, pixel_format, type, width, height),
              overridden_color_space(format.color_space),
              overridden_gamma_curve(format.gamma_curve) {}
-       virtual std::string effect_type_id() const { return "UnknownColorspaceInput"; }
+       virtual string effect_type_id() const { return "UnknownColorspaceInput"; }
 
        void set_color_space(Colorspace colorspace) {
                overridden_color_space = colorspace;
@@ -217,7 +231,7 @@ private:
        GammaCurve overridden_gamma_curve;
 };
 
-TEST(EffectChainTester, HandlesInputChangingColorspace) {
+TEST(EffectChainTest, HandlesInputChangingColorspace) {
        const int size = 4;
 
        float data[size] = {
@@ -386,8 +400,8 @@ TEST(EffectChainTest, NoAlphaConversionsWhenPremultipliedAlphaNotNeeded) {
 class BlueInput : public Input {
 public:
        BlueInput() { register_int("needs_mipmaps", &needs_mipmaps); }
-       virtual std::string effect_type_id() const { return "IdentityEffect"; }
-       std::string output_fragment_shader() { return read_file("blue.frag"); }
+       virtual string effect_type_id() const { return "IdentityEffect"; }
+       string output_fragment_shader() { return read_file("blue.frag"); }
        virtual AlphaHandling alpha_handling() const { return OUTPUT_BLANK_ALPHA; }
        virtual void finalize() {}
        virtual bool can_output_linear_gamma() const { return true; }
@@ -405,8 +419,8 @@ private:
 class RewritingToBlueInput : public Input {
 public:
        RewritingToBlueInput() : blue_node(NULL) { register_int("needs_mipmaps", &needs_mipmaps); }
-       virtual std::string effect_type_id() const { return "RewritingToBlueInput"; }
-       std::string output_fragment_shader() { EXPECT_TRUE(false); return read_file("identity.frag"); }
+       virtual string effect_type_id() const { return "RewritingToBlueInput"; }
+       string output_fragment_shader() { EXPECT_TRUE(false); return read_file("identity.frag"); }
        virtual void rewrite_graph(EffectChain *graph, Node *self) {
                Node *blue_node = graph->add_node(new BlueInput());
                graph->replace_receiver(self, blue_node);
@@ -456,8 +470,8 @@ TEST(EffectChainTest, NoAlphaConversionsWithBlankAlpha) {
 class BlankAlphaPreservingEffect : public Effect {
 public:
        BlankAlphaPreservingEffect() {}
-       virtual std::string effect_type_id() const { return "BlankAlphaPreservingEffect"; }
-       std::string output_fragment_shader() { return read_file("identity.frag"); }
+       virtual string effect_type_id() const { return "BlankAlphaPreservingEffect"; }
+       string output_fragment_shader() { return read_file("identity.frag"); }
        virtual AlphaHandling alpha_handling() const { return INPUT_PREMULTIPLIED_ALPHA_KEEP_BLANK; }
 };
 
@@ -516,17 +530,27 @@ class MipmapNeedingEffect : public Effect {
 public:
        MipmapNeedingEffect() {}
        virtual bool needs_mipmaps() const { return true; }
-       virtual std::string effect_type_id() const { return "MipmapNeedingEffect"; }
-       std::string output_fragment_shader() { return read_file("mipmap_needing_effect.frag"); }
-       void set_gl_state(GLuint glsl_program_num, const std::string& prefix, unsigned *sampler_num)
+
+       // To be allowed to mess with the sampler state.
+       virtual bool needs_texture_bounce() const { return true; }
+
+       virtual string effect_type_id() const { return "MipmapNeedingEffect"; }
+       string output_fragment_shader() { return read_file("mipmap_needing_effect.frag"); }
+       virtual void inform_added(EffectChain *chain) { this->chain = chain; }
+
+       void set_gl_state(GLuint glsl_program_num, const string& prefix, unsigned *sampler_num)
        {
-               glActiveTexture(GL_TEXTURE0);
+               Node *self = chain->find_node_for_effect(this);
+               glActiveTexture(chain->get_input_sampler(self, 0));
                check_error();
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
                check_error();
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
                check_error();
        }
+
+private:
+       EffectChain *chain;
 };
 
 TEST(EffectChainTest, MipmapGenerationWorks) {
@@ -638,24 +662,12 @@ TEST(EffectChainTest, ResizeDownByFourThenUpByFour) {
        expect_equal(expected_data, out_data, 4, 16);
 }
 
-// An effect that multiplies with a constant. Used below.
-class MultiplyEffect : public Effect {
-public:
-       MultiplyEffect() { register_float("factor", &factor); }
-       virtual std::string effect_type_id() const { return "MultiplyEffect"; }
-       std::string output_fragment_shader() { return read_file("multiply.frag"); }
-       virtual AlphaHandling alpha_handling() const { return DONT_CARE_ALPHA_TYPE; }
-
-private:
-       float factor;
-};
-
 // An effect that adds its two inputs together. Used below.
 class AddEffect : public Effect {
 public:
        AddEffect() {}
-       virtual std::string effect_type_id() const { return "AddEffect"; }
-       std::string output_fragment_shader() { return read_file("add.frag"); }
+       virtual string effect_type_id() const { return "AddEffect"; }
+       string output_fragment_shader() { return read_file("add.frag"); }
        virtual unsigned num_inputs() const { return 2; }
        virtual AlphaHandling alpha_handling() const { return DONT_CARE_ALPHA_TYPE; }
 };
@@ -680,11 +692,14 @@ TEST(EffectChainTest, DiamondGraph) {
        };
        float out_data[2 * 2];
 
+       const float half[] = { 0.5f, 0.5f, 0.5f, 0.5f };
+       const float two[] = { 2.0f, 2.0f, 2.0f, 0.5f };
+
        MultiplyEffect *mul_half = new MultiplyEffect();
-       ASSERT_TRUE(mul_half->set_float("factor", 0.5f));
+       ASSERT_TRUE(mul_half->set_vec4("factor", half));
        
        MultiplyEffect *mul_two = new MultiplyEffect();
-       ASSERT_TRUE(mul_two->set_float("factor", 2.0f));
+       ASSERT_TRUE(mul_two->set_vec4("factor", two));
 
        EffectChainTester tester(NULL, 2, 2);
 
@@ -726,11 +741,14 @@ TEST(EffectChainTest, DiamondGraphWithOneInputUsedInTwoPhases) {
        };
        float out_data[2 * 2];
 
+       const float half[] = { 0.5f, 0.5f, 0.5f, 0.5f };
+       const float two[] = { 2.0f, 2.0f, 2.0f, 0.5f };
+
        MultiplyEffect *mul_half = new MultiplyEffect();
-       ASSERT_TRUE(mul_half->set_float("factor", 0.5f));
+       ASSERT_TRUE(mul_half->set_vec4("factor", half));
        
        MultiplyEffect *mul_two = new MultiplyEffect();
-       ASSERT_TRUE(mul_two->set_float("factor", 2.0f));
+       ASSERT_TRUE(mul_two->set_vec4("factor", two));
        
        BouncingIdentityEffect *bounce = new BouncingIdentityEffect();
 
@@ -829,7 +847,7 @@ public:
                input_width = width;
                input_height = height;
        }
-       virtual std::string effect_type_id() const { return "SizeStoringEffect"; }
+       virtual string effect_type_id() const { return "SizeStoringEffect"; }
 
        int input_width, input_height;
 };
@@ -918,8 +936,8 @@ public:
                  height(height),
                  virtual_width(virtual_width),
                  virtual_height(virtual_height) {}
-       virtual std::string effect_type_id() const { return "VirtualResizeEffect"; }
-       std::string output_fragment_shader() { return read_file("identity.frag"); }
+       virtual string effect_type_id() const { return "VirtualResizeEffect"; }
+       string output_fragment_shader() { return read_file("identity.frag"); }
 
        virtual bool changes_output_size() const { return true; }
 
@@ -959,3 +977,113 @@ TEST(EffectChainTest, VirtualSizeIsSentOnToInputs) {
        // since bilinear scaling from 2x2 → 3x3 → 2x2 is not very exact.
        expect_equal(data, out_data, size, size);
 }
+
+// Does not use EffectChainTest, so that it can construct an EffectChain without
+// a shared ResourcePool (which is also properly destroyed afterwards).
+// Also turns on debugging to test that code path.
+TEST(EffectChainTest, IdentityWithOwnPool) {
+       const int width = 3, height = 2;
+       float data[] = {
+               0.0f, 0.25f, 0.3f,
+               0.75f, 1.0f, 1.0f,
+       };
+       const float expected_data[] = {
+               0.75f, 1.0f, 1.0f,
+               0.0f, 0.25f, 0.3f,
+       };
+       float out_data[6], temp[6 * 4];
+
+       EffectChain chain(width, height);
+       movit_debug_level = MOVIT_DEBUG_ON;
+
+       ImageFormat format;
+       format.color_space = COLORSPACE_sRGB;
+       format.gamma_curve = GAMMA_LINEAR;
+
+       FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, width, height);
+       input->set_pixel_data(data);
+       chain.add_input(input);
+       chain.add_output(format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED);
+
+       GLuint texnum, fbo;
+       glGenTextures(1, &texnum);
+       check_error();
+       glBindTexture(GL_TEXTURE_2D, texnum);
+       check_error();
+       glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_RGBA, GL_FLOAT, NULL);
+       check_error();
+
+       glGenFramebuffers(1, &fbo);
+       check_error();
+       glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+       check_error();
+       glFramebufferTexture2D(
+               GL_FRAMEBUFFER,
+               GL_COLOR_ATTACHMENT0,
+               GL_TEXTURE_2D,
+               texnum,
+               0);
+       check_error();
+       glBindFramebuffer(GL_FRAMEBUFFER, 0);
+       check_error();
+
+       chain.finalize();
+
+       chain.render_to_fbo(fbo, width, height);
+
+       glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+       check_error();
+       glReadPixels(0, 0, width, height, GL_RGBA, GL_FLOAT, temp);
+       check_error();
+       for (unsigned i = 0; i < 6; ++i) {
+               out_data[i] = temp[i * 4];
+       }
+
+       expect_equal(expected_data, out_data, width, height);
+
+       // Reset the debug status again.
+       movit_debug_level = MOVIT_DEBUG_OFF;
+}
+
+// A dummy effect whose only purpose is to test sprintf decimal behavior.
+class PrintfingBlueEffect : public Effect {
+public:
+       PrintfingBlueEffect() {}
+       virtual string effect_type_id() const { return "PrintfingBlueEffect"; }
+       string output_fragment_shader() {
+               stringstream ss;
+               ss.imbue(locale("C"));
+               ss.precision(8);
+               ss << "vec4 FUNCNAME(vec2 tc) { return vec4("
+                  << 0.0f << ", " << 0.0f << ", "
+                  << 0.5f << ", " << 1.0f << "); }\n";
+               return ss.str();
+       }
+};
+
+TEST(EffectChainTest, StringStreamLocalesWork) {
+       // An example of a locale with comma instead of period as decimal separator.
+       // Obviously, if you run on a machine without this locale available,
+       // the test will always succeed. Note that the OpenGL driver might call
+       // setlocale() behind-the-scenes, and that might corrupt the returned
+       // pointer, so we need to take our own copy of it here.
+       char *saved_locale = strdup(setlocale(LC_ALL, "nb_NO.UTF_8"));
+       float data[] = {
+               0.0f, 0.0f, 0.0f, 0.0f,
+       };
+       float expected_data[] = {
+               0.0f, 0.0f, 0.5f, 1.0f,
+       };
+       float out_data[4];
+       EffectChainTester tester(data, 1, 1, FORMAT_RGBA_PREMULTIPLIED_ALPHA, COLORSPACE_sRGB, GAMMA_LINEAR);
+       tester.get_chain()->add_effect(new PrintfingBlueEffect());
+       tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_LINEAR);
+
+       expect_equal(expected_data, out_data, 4, 1);
+
+       setlocale(LC_ALL, saved_locale);
+       free(saved_locale);
+}
+
+
+}  // namespace movit