blob: f12affa8a1b165a1b2d48095f2998d891fdb3abb [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/ui/lib/escher/defaults/default_shader_program_factory.h"
#include "src/ui/lib/escher/escher.h"
#include "src/ui/lib/escher/geometry/bounding_box.h"
#include "src/ui/lib/escher/material/material.h"
#include "src/ui/lib/escher/paper/paper_renderer.h"
#include "src/ui/lib/escher/paper/paper_renderer_static_config.h"
#include "src/ui/lib/escher/paper/paper_scene.h"
#include "src/ui/lib/escher/renderer/batch_gpu_uploader.h"
#include "src/ui/lib/escher/test/common/paper_renderer_test.h"
#include "src/ui/lib/escher/types/color.h"
#include "src/ui/lib/escher/types/color_histogram.h"
#include "src/ui/lib/escher/util/image_utils.h"
#include <vulkan/vulkan.hpp>
namespace escher {
namespace test {
namespace {
struct Result {
static const Result Ok() { return Result(); }
static const Result Err(std::string err) { return Result(err); }
Result() : err(std::nullopt) {}
Result(std::string err_) : err(err_) {}
bool IsOk() { return !err.has_value(); }
bool IsErr() { return err.has_value(); }
std::optional<std::string> err;
};
template <class ColorT>
bool ColorMatch(const ColorT x, const ColorT y, double eps = 0.05) {
static_assert(ColorT::color_depth() == 8);
return sqrt(((x.r - y.r) / 255.0 * (x.r - y.r) / 255.0) +
((x.g - y.g) / 255.0 * (x.g - y.g) / 255.0) +
((x.b - y.b) / 255.0 * (x.b - y.b) / 255.0) +
((x.a - y.a) / 255.0 * (x.a - y.a) / 255.0)) < eps;
}
template <class ColorT>
Result ExpectHistogramMatch(const ColorHistogram<ColorT>& hist_real,
const ColorHistogram<ColorT>& hist_expected, double eps = 1e-4) {
std::vector<ColorT> colors_real;
std::vector<ColorT> colors_expected;
size_t total_pixels_real = 0;
size_t total_pixels_expected = 0;
for (const auto kv : hist_real.values) {
colors_real.push_back(kv.first);
total_pixels_real += kv.second;
}
for (const auto kv : hist_expected.values) {
colors_expected.push_back(kv.first);
total_pixels_expected += kv.second;
}
for (const auto kv : hist_real.values) {
auto expected_key =
std::find_if(colors_expected.begin(), colors_expected.end(),
[color_x = kv.first](ColorT color_y) { return ColorMatch(color_x, color_y); });
if (expected_key == colors_expected.end()) {
std::ostringstream err;
err << "Color " << kv.first << " not found. \n"
<< "Histogram: " << hist_real << ";\n"
<< "Expected: " << hist_expected;
return Result::Err(err.str());
}
double ratio_expected = hist_expected[*expected_key] / double(total_pixels_expected);
double ratio_real = kv.second / double(total_pixels_real);
if (fabs(ratio_real - ratio_expected) > eps) {
std::ostringstream err;
err << "Ratio of color " << kv.first << " doesn't match."
<< " Expected ratio: " << hist_expected[*expected_key] << "/"
<< double(total_pixels_expected) << ", real ratio: " << kv.second << "/"
<< double(total_pixels_real) << "\n"
<< "Histogram: " << hist_real << ";\n"
<< "Expected: " << hist_expected;
return Result::Err(err.str());
}
}
return Result::Ok();
}
#define EXPECT_RESULT_OK(expr) \
do { \
Result result = (expr); \
EXPECT_TRUE(result.IsOk()) << *result.err; \
} while (0);
} // namespace
using GammaCorrectionTest = PaperRendererTest;
// We draw the following scene:
// +--------+---------+---------+---------+
// | ǁ |
// | ǁ |
// | ǁ |
// + ǁ |
// | ǁ |
// | ǁ |
// | not ǁ |
// + gamma ǁ gamma |
// | corrected ǁ corrected |
// | ǁ |
// | ǁ |
// + ǁ |
// | ǁ |
// | ǁ |
// | ǁ |
// +--------+---------+---------+---------+
//
// A rectangle and a square a drawn, both with the same color texture, with gamma
// correction applied only to the rectangle. The rectangle is drawn above the square,
// on the right half of the output image.
//
// 1/2 of the area should be kColor (1, .8, .4, 1);
// 1/2 of the area should be gamma-corrected kColor (1, .64, .16, 1);
// (i.e. the RGB components are squared)
VK_TEST_F(GammaCorrectionTest, SomeCorrectedSomeNot) {
const glm::vec4 kColor(1.f, 0.8f, 0.4f, 1);
// Gamma-corrected color is (1, .64, .16, 1).
// 0-255 integer representations of these colors are:
const ColorBgra kExpectedNonGammaColor = ColorBgra::FromFloats(kColor);
const ColorBgra kExpectedGammaColor = ColorBgra::FromFloats(kColor * kColor);
MaterialPtr material;
{
auto gpu_uploader = std::make_unique<escher::BatchGpuUploader>(escher(), 0);
ImagePtr image =
escher()->NewRgbaImage(gpu_uploader.get(), 1, 1, ColorRgba::FromFloats(kColor).bytes());
gpu_uploader->Submit();
EXPECT_VK_SUCCESS(escher()->vk_device().waitIdle());
TexturePtr tex = Texture::New(escher()->resource_recycler(), image, vk::Filter::eNearest);
material = Material::New(glm::vec4(1, 1, 1, 1), tex);
}
SetupFrame();
BeginRenderingFrame();
escher::PaperTransformStack* transform_stack = renderer()->transform_stack();
transform_stack->PushTranslation(vec2(0, 0));
{
transform_stack->PushElevation(0);
vec2 top_left(0, 0);
vec2 bottom_right(kFramebufferWidth, kFramebufferHeight);
renderer()->DrawRect(top_left, bottom_right, material);
transform_stack->Pop();
}
{
transform_stack->PushElevation(-1);
vec2 top_left(kFramebufferWidth / 2, 0);
vec2 bottom_right(kFramebufferWidth, kFramebufferHeight);
renderer()->DrawRect(top_left, bottom_right, material, PaperDrawableFlagBits::kBt709Oetf);
transform_stack->Pop();
}
EndRenderingFrame();
EXPECT_VK_SUCCESS(escher()->vk_device().waitIdle());
auto bytes = GetPixelData();
const ColorHistogram<ColorBgra> histogram(bytes.data(), kFramebufferWidth * kFramebufferHeight);
auto expected_histogram = ColorHistogram<ColorBgra>(
{{kExpectedNonGammaColor, kFramebufferWidth * kFramebufferHeight / 2},
{kExpectedGammaColor, kFramebufferWidth * kFramebufferHeight / 2}});
EXPECT_RESULT_OK(ExpectHistogramMatch(histogram, expected_histogram));
frame_data().frame->EndFrame(SemaphorePtr(), []() {});
}
} // namespace test
} // namespace escher