template<class T>
class RewritingEffect : public Effect {
public:
- RewritingEffect() : effect(new T()), replaced_node(NULL) {}
+ RewritingEffect() : effect(new T()), replaced_node(nullptr) {}
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) {
0.0000f, 0.0000f, 0.0000f, 1.0000f
};
float out_data[4 * 4];
- EffectChainTester tester(NULL, 1, 4);
+ EffectChainTester tester(nullptr, 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);
};
float out_data[size];
- EffectChainTester tester(NULL, 4, 1, FORMAT_GRAYSCALE);
+ EffectChainTester tester(nullptr, 4, 1, FORMAT_GRAYSCALE);
// First say that we have sRGB, linear input.
ImageFormat format;
expected_data[i] = i / 255.0;
};
float out_data[256];
- EffectChainTester tester(NULL, 256, 1);
+ EffectChainTester tester(nullptr, 256, 1);
tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_sRGB);
tester.get_chain()->add_effect(new IdentityEffect());
tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_sRGB);
// which outputs blank alpha.
class RewritingToBlueInput : public Input {
public:
- RewritingToBlueInput() : blue_node(NULL) { register_int("needs_mipmaps", &needs_mipmaps); }
+ RewritingToBlueInput() : blue_node(nullptr) { register_int("needs_mipmaps", &needs_mipmaps); }
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) {
0.0f, 0.0f, 1.0f, 1.0f,
};
float out_data[4 * size];
- EffectChainTester tester(NULL, size, 1);
+ EffectChainTester tester(nullptr, size, 1);
RewritingToBlueInput *input = new RewritingToBlueInput();
tester.get_chain()->add_input(input);
tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_LINEAR, OUTPUT_ALPHA_FORMAT_PREMULTIPLIED);
0.0f, 0.0f, 1.0f, 1.0f,
};
float out_data[4 * size];
- EffectChainTester tester(NULL, size, 1);
+ EffectChainTester tester(nullptr, size, 1);
tester.get_chain()->add_input(new BlueInput());
tester.get_chain()->add_effect(new BlankAlphaPreservingEffect());
RewritingEffect<MirrorEffect> *effect = new RewritingEffect<MirrorEffect>();
0.0f, 0.0f, 1.0f, 1.0f,
};
float out_data[4 * size];
- EffectChainTester tester(NULL, size, 1);
+ EffectChainTester tester(nullptr, size, 1);
tester.get_chain()->add_input(new BlueInput());
tester.get_chain()->add_effect(new IdentityEffect()); // Not BlankAlphaPreservingEffect.
RewritingEffect<MirrorEffect> *effect = new RewritingEffect<MirrorEffect>();
0.25f, 0.25f, 0.25f, 0.25f,
};
float out_data[4 * 16];
- EffectChainTester tester(NULL, 4, 16, FORMAT_GRAYSCALE);
+ EffectChainTester tester(nullptr, 4, 16, FORMAT_GRAYSCALE);
ImageFormat format;
format.color_space = COLORSPACE_sRGB;
MultiplyEffect *mul_two = new MultiplyEffect();
ASSERT_TRUE(mul_two->set_vec4("factor", two));
- EffectChainTester tester(NULL, 2, 2);
+ EffectChainTester tester(nullptr, 2, 2);
ImageFormat format;
format.color_space = COLORSPACE_sRGB;
BouncingIdentityEffect *bounce = new BouncingIdentityEffect();
- EffectChainTester tester(NULL, 2, 2);
+ EffectChainTester tester(nullptr, 2, 2);
ImageFormat format;
format.color_space = COLORSPACE_sRGB;
};
float out_data[2 * 2];
- EffectChainTester tester(NULL, 2, 2);
+ EffectChainTester tester(nullptr, 2, 2);
tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_sRGB);
// MirrorEffect does not get linear light, so the conversions will be
};
float out_data[2 * 2];
- EffectChainTester tester(NULL, 2, 2);
+ EffectChainTester tester(nullptr, 2, 2);
tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_REC_601_625, GAMMA_LINEAR);
// MirrorEffect does not get linear light, so the conversions will be
};
float out_data[4 * 3];
- EffectChainTester tester(NULL, 4, 3); // Note non-square aspect.
+ EffectChainTester tester(nullptr, 4, 3); // Note non-square aspect.
ImageFormat format;
format.color_space = COLORSPACE_sRGB;
// (keep the height, round the width 9.333 to 9).
float out_data[9 * 7];
- EffectChainTester tester(NULL, 4, 3);
+ EffectChainTester tester(nullptr, 4, 3);
ImageFormat format;
format.color_space = COLORSPACE_sRGB;
FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, 1, size);
input->set_pixel_data(data);
- EffectChainTester tester(NULL, 1, size);
+ EffectChainTester tester(nullptr, 1, size);
tester.get_chain()->add_input(new BlueInput());
Effect *phase1_end = tester.get_chain()->add_effect(new BouncingIdentityEffect());
tester.get_chain()->add_input(input);
check_error();
glBindTexture(GL_TEXTURE_2D, texnum);
check_error();
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_RGBA, GL_FLOAT, NULL);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_RGBA, GL_FLOAT, nullptr);
check_error();
glGenFramebuffers(1, &fbo);
// 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 = setlocale(LC_ALL, "nb_NO.UTF_8");
- if (saved_locale == NULL) {
+ if (saved_locale == nullptr) {
// The locale wasn't available.
return;
}
free(saved_locale);
}
+// An effect that does nothing, but as a compute shader.
+class IdentityComputeEffect : public Effect {
+public:
+ IdentityComputeEffect() {}
+ virtual string effect_type_id() const { return "IdentityComputeEffect"; }
+ virtual bool is_compute_shader() const { return true; }
+ string output_fragment_shader() { return read_file("identity.comp"); }
+};
+
+class WithAndWithoutComputeShaderTest : public testing::TestWithParam<string> {
+};
+INSTANTIATE_TEST_CASE_P(WithAndWithoutComputeShaderTest,
+ WithAndWithoutComputeShaderTest,
+ testing::Values("fragment", "compute"));
+
TEST(EffectChainTest, sRGBIntermediate) {
float data[] = {
0.0f, 0.5f, 0.0f, 1.0f,
<< "Expected sRGB not to be able to represent 0.5 exactly (got " << out_data[1] << ")";
EXPECT_LT(fabs(out_data[1] - data[1]), 0.1f)
<< "Expected sRGB to be able to represent 0.5 approximately (got " << out_data[1] << ")";
+
+ // This state should have been preserved.
+ EXPECT_FALSE(glIsEnabled(GL_FRAMEBUFFER_SRGB));
}
// An effect that is like IdentityEffect, but also does not require linear light.
}
// This maximum error is pretty bad; about 6.5 levels of a 10-bit sRGB
- // framebuffer.
- expect_equal(linear_data, out_data, size, 1, 7e-3, 2e-5);
+ // framebuffer. (Slightly more on NVIDIA cards.)
+ expect_equal(linear_data, out_data, size, 1, 7.5e-3, 2e-5);
}
-TEST(EffectChainTest, SquareRoot10bitIntermediateAccuracy) {
+TEST_P(WithAndWithoutComputeShaderTest, SquareRoot10bitIntermediateAccuracy) {
// Note that we do the comparison in sRGB space, which is what we
// typically would want; however, we do the sRGB conversion ourself
// to avoid compounding errors from shader conversions into the
EffectChainTester tester(data, size, 1, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, GL_RGBA32F);
tester.get_chain()->set_intermediate_format(GL_RGB10_A2, SQUARE_ROOT_FRAMEBUFFER_TRANSFORMATION);
- tester.get_chain()->add_effect(new IdentityEffect());
+ if (GetParam() == "compute") {
+ tester.get_chain()->add_effect(new IdentityComputeEffect());
+ } else {
+ tester.get_chain()->add_effect(new IdentityEffect());
+ }
tester.get_chain()->add_effect(new BouncingIdentityEffect());
tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
// This maximum error is much better; about 0.7 levels of a 10-bit sRGB
// framebuffer (ideal would be 0.5). That is an order of magnitude better
// than in the linear test above. The RMS error is much better, too.
- expect_equal(linear_data, out_data, size, 1, 7e-4, 5e-6);
+ expect_equal(linear_data, out_data, size, 1, 7.5e-4, 5e-6);
}
TEST(EffectChainTest, SquareRootIntermediateIsTurnedOffForNonLinearData) {
expect_equal(data, out_data, 3, 2);
}
+TEST(ComputeShaderTest, Identity) {
+ float data[] = {
+ 0.0f, 0.25f, 0.3f,
+ 0.75f, 1.0f, 1.0f,
+ };
+ float out_data[6];
+ EffectChainTester tester(data, 3, 2, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
+ if (!movit_compute_shaders_supported) {
+ fprintf(stderr, "Skipping test; no support for compile shaders.\n");
+ return;
+ }
+ tester.get_chain()->add_effect(new IdentityComputeEffect());
+ tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
+
+ expect_equal(data, out_data, 3, 2);
+}
+
+// Like IdentityComputeEffect, but due to the alpha handling, this will be
+// the very last effect in the chain, which means we can't output it directly
+// to the screen.
+class IdentityAlphaComputeEffect : public IdentityComputeEffect {
+ AlphaHandling alpha_handling() const { return DONT_CARE_ALPHA_TYPE; }
+};
+
+TEST(ComputeShaderTest, LastEffectInChain) {
+ float data[] = {
+ 0.0f, 0.25f, 0.3f,
+ 0.75f, 1.0f, 1.0f,
+ };
+ float out_data[6];
+ EffectChainTester tester(data, 3, 2, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
+ if (!movit_compute_shaders_supported) {
+ fprintf(stderr, "Skipping test; no support for compile shaders.\n");
+ return;
+ }
+ tester.get_chain()->add_effect(new IdentityAlphaComputeEffect());
+ tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
+
+ expect_equal(data, out_data, 3, 2);
+}
+
+TEST(ComputeShaderTest, Render8BitTo8Bit) {
+ uint8_t data[] = {
+ 14, 200, 80,
+ 90, 100, 110,
+ };
+ uint8_t out_data[6];
+ EffectChainTester tester(nullptr, 3, 2, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, GL_RGBA8);
+ if (!movit_compute_shaders_supported) {
+ fprintf(stderr, "Skipping test; no support for compile shaders.\n");
+ return;
+ }
+ tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, 3, 2);
+ tester.get_chain()->add_effect(new IdentityAlphaComputeEffect());
+ tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
+
+ expect_equal(data, out_data, 3, 2);
+}
+
} // namespace movit