1 // Unit tests for EffectChain.
3 // Note that this also contains the tests for some of the simpler effects.
5 #include "effect_chain.h"
6 #include "flat_input.h"
7 #include "gtest/gtest.h"
8 #include "mirror_effect.h"
10 #include "test_util.h"
12 TEST(EffectChainTest, EmptyChain) {
18 EffectChainTester tester(data, 3, 2, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
19 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
21 expect_equal(data, out_data, 3, 2);
24 // An effect that does nothing.
25 class IdentityEffect : public Effect {
28 virtual std::string effect_type_id() const { return "IdentityEffect"; }
29 std::string output_fragment_shader() { return read_file("identity.frag"); }
32 TEST(EffectChainTest, Identity) {
38 EffectChainTester tester(data, 3, 2, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
39 tester.get_chain()->add_effect(new IdentityEffect());
40 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
42 expect_equal(data, out_data, 3, 2);
45 // An effect that does nothing, but requests texture bounce.
46 class BouncingIdentityEffect : public Effect {
48 BouncingIdentityEffect() {}
49 virtual std::string effect_type_id() const { return "IdentityEffect"; }
50 std::string output_fragment_shader() { return read_file("identity.frag"); }
51 bool needs_texture_bounce() const { return true; }
54 TEST(EffectChainTest, TextureBouncePreservesIdentity) {
60 EffectChainTester tester(data, 3, 2, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
61 tester.get_chain()->add_effect(new BouncingIdentityEffect());
62 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
64 expect_equal(data, out_data, 3, 2);
67 TEST(MirrorTest, BasicTest) {
72 float expected_data[6] = {
77 EffectChainTester tester(data, 3, 2, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
78 tester.get_chain()->add_effect(new MirrorEffect());
79 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
81 expect_equal(expected_data, out_data, 3, 2);
84 // A dummy effect that inverts its input.
85 class InvertEffect : public Effect {
88 virtual std::string effect_type_id() const { return "InvertEffect"; }
89 std::string output_fragment_shader() { return read_file("invert_effect.frag"); }
92 // Like IdentityEffect, but rewrites itself out of the loop,
93 // splicing in a InvertEffect instead. Also stores the new node,
94 // so we later can check that there are gamma conversion effects
96 class RewritingToInvertEffect : public Effect {
98 RewritingToInvertEffect() {}
99 virtual std::string effect_type_id() const { return "RewritingToInvertEffect"; }
100 std::string output_fragment_shader() { EXPECT_TRUE(false); return read_file("identity.frag"); }
101 virtual void rewrite_graph(EffectChain *graph, Node *self) {
102 Node *invert_node = graph->add_node(new InvertEffect());
103 graph->replace_receiver(self, invert_node);
104 graph->replace_sender(self, invert_node);
106 self->disabled = true;
107 this->invert_node = invert_node;
113 TEST(EffectChainTest, RewritingWorksAndGammaConversionsAreInserted) {
118 float expected_data[6] = {
119 1.0f, 0.9771f, 0.9673f,
123 EffectChainTester tester(data, 3, 2, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_sRGB);
124 RewritingToInvertEffect *effect = new RewritingToInvertEffect();
125 tester.get_chain()->add_effect(effect);
126 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_sRGB);
128 Node *node = effect->invert_node;
129 ASSERT_EQ(1, node->incoming_links.size());
130 ASSERT_EQ(1, node->outgoing_links.size());
131 EXPECT_EQ("GammaExpansionEffect", node->incoming_links[0]->effect->effect_type_id());
132 EXPECT_EQ("GammaCompressionEffect", node->outgoing_links[0]->effect->effect_type_id());
134 expect_equal(expected_data, out_data, 3, 2);
137 // Like RewritingToInvertEffect, but splicing in a MirrorEffect instead,
138 // which does not need linear light.
139 class RewritingToMirrorEffect : public Effect {
141 RewritingToMirrorEffect() {}
142 virtual std::string effect_type_id() const { return "RewritingToMirrorEffect"; }
143 std::string output_fragment_shader() { EXPECT_TRUE(false); return read_file("identity.frag"); }
144 virtual void rewrite_graph(EffectChain *graph, Node *self) {
145 Node *mirror_node = graph->add_node(new MirrorEffect());
146 graph->replace_receiver(self, mirror_node);
147 graph->replace_sender(self, mirror_node);
149 self->disabled = true;
150 this->mirror_node = mirror_node;
156 TEST(EffectChainTest, NoGammaConversionsWhenLinearLightNotNeeded) {
161 float expected_data[6] = {
166 EffectChainTester tester(data, 3, 2, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_sRGB);
167 RewritingToMirrorEffect *effect = new RewritingToMirrorEffect();
168 tester.get_chain()->add_effect(effect);
169 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_sRGB);
171 Node *node = effect->mirror_node;
172 ASSERT_EQ(1, node->incoming_links.size());
173 EXPECT_EQ(0, node->outgoing_links.size());
174 EXPECT_EQ("FlatInput", node->incoming_links[0]->effect->effect_type_id());
176 expect_equal(expected_data, out_data, 3, 2);
179 // The identity effect needs linear light, and thus will get conversions on both sides.
180 // Verify that sRGB data is properly converted to and from linear light for the entire ramp.
181 TEST(EffectChainTest, IdentityThroughsRGBConversions) {
183 for (unsigned i = 0; i < 256; ++i) {
187 EffectChainTester tester(data, 256, 1, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_sRGB);
188 tester.get_chain()->add_effect(new IdentityEffect());
189 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_sRGB);
191 expect_equal(data, out_data, 256, 1);
194 // Same, for the Rec. 601/709 gamma curve.
195 TEST(EffectChainTest, IdentityThroughRec709) {
197 for (unsigned i = 0; i < 256; ++i) {
201 EffectChainTester tester(data, 256, 1, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_REC_709);
202 tester.get_chain()->add_effect(new IdentityEffect());
203 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_REC_709);
205 expect_equal(data, out_data, 256, 1);