blob: 784a8329b541766b0d810ac48a1f6fc92a97fa68 [file] [log] [blame]
// Copyright 2019 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/paper/paper_renderer.h"
#include "src/ui/lib/escher/debug/debug_rects.h"
#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/paper/paper_renderer_static_config.h"
#include "src/ui/lib/escher/paper/paper_scene.h"
#include "src/ui/lib/escher/paper/paper_timestamp_graph.h"
#include "src/ui/lib/escher/renderer/batch_gpu_uploader.h"
#include "src/ui/lib/escher/scene/viewing_volume.h"
#include "src/ui/lib/escher/test/fixtures/readback_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 {
// Extends ReadbackTest by providing a ready-to-use DebugFont instance.
class PaperRendererTest : public ReadbackTest {
protected:
// |ReadbackTest|
void SetUp() override {
ReadbackTest::SetUp();
escher()->shader_program_factory()->filesystem()->InitializeWithRealFiles(
kPaperRendererShaderPaths);
PaperRendererConfig config;
auto depth_stencil_format = escher()->device()->caps().GetMatchingDepthStencilFormat();
if (depth_stencil_format.result == vk::Result::eSuccess) {
config.depth_stencil_format = depth_stencil_format.value;
FXL_LOG(INFO) << "Depth stencil format set to " << vk::to_string(config.depth_stencil_format);
} else {
GTEST_SKIP() << "Cannot find a valid depth stencil format, test skipped";
}
ren = PaperRenderer::New(escher(), config);
}
// |ReadbackTest|
void TearDown() override {
ren.reset();
ReadbackTest::TearDown();
}
// Sets up the environment including the frame, scene, and cameras.
void frame_setup() {
fd = NewFrame(vk::ImageLayout::eColorAttachmentOptimal);
scene = fxl::MakeRefCounted<PaperScene>();
scene->point_lights.resize(1);
scene->bounding_box = BoundingBox(vec3(0), vec3(kFramebufferHeight));
const escher::ViewingVolume& volume = ViewingVolume(scene->bounding_box);
escher::Camera cam = escher::Camera::NewOrtho(volume);
cameras = {cam};
};
// Used by tests that call DebugRects::Color when drawing.
int32_t get_colored_data(DebugRects::Color kColor) {
auto bytes = ReadbackFromColorAttachment(fd.frame, vk::ImageLayout::eColorAttachmentOptimal,
vk::ImageLayout::eColorAttachmentOptimal);
const ColorHistogram<ColorBgra> histogram(bytes.data(), kFramebufferWidth * kFramebufferHeight);
EXPECT_EQ(2U, histogram.size());
ColorRgba c = DebugRects::colorData[kColor];
ColorBgra b = ColorBgra(c.r, c.g, c.b, c.a);
return histogram[b];
};
public:
TexturePtr depth_buffer() { return ren->depth_buffers_[0]; }
escher::PaperRendererPtr ren;
// Frame environment variables.
ReadbackTest::FrameData fd;
escher::PaperScenePtr scene;
std::vector<Camera> cameras;
};
VK_TEST_F(PaperRendererTest, Text) {
constexpr uint32_t kNumPixelsPerGlyph = 7 * 7;
constexpr ColorBgra kWhite(255, 255, 255, 255);
constexpr ColorBgra kBlack(0, 0, 0, 255);
constexpr ColorBgra kTransparentBlack(0, 0, 0, 0);
// Expects PaperRenderer's background color to be transparent.
for (int32_t scale = 1; scale <= 4; ++scale) {
frame_setup();
// |expected_black| is the total number of black pixels *within* the glyphs
// *before* scaling. In other words, black background pixels outside of the
// glyph bounds are not counted. Also, consider the glyph "!" which has 4
// black pixels all in one vertical column (3 black, 1 white, 1 black)...
// if the scale is 2 then both the width and height are doubled so the
// number of black pixels in the glyph after scaling is 16.
std::function<void(std::string, size_t)> draw_and_check_histogram = [&](std::string glyphs,
size_t expected_black) {
auto gpu_uploader =
std::make_shared<escher::BatchGpuUploader>(escher(), fd.frame->frame_number());
ren->BeginFrame(fd.frame, gpu_uploader, scene, cameras, fd.color_attachment);
ren->DrawDebugText(glyphs, {0, 10 * scale}, scale);
ren->FinalizeFrame();
auto upload_semaphore = escher::Semaphore::New(escher()->vk_device());
gpu_uploader->AddSignalSemaphore(upload_semaphore);
gpu_uploader->Submit();
ren->EndFrame(upload_semaphore);
const int32_t scale_squared = scale * scale;
size_t expected_white =
(glyphs.length() * kNumPixelsPerGlyph - expected_black) * scale_squared;
auto bytes = ReadbackFromColorAttachment(fd.frame, fd.color_attachment->swapchain_layout(),
vk::ImageLayout::eColorAttachmentOptimal);
const ColorHistogram<ColorBgra> histogram(bytes.data(),
kFramebufferWidth * kFramebufferHeight);
EXPECT_EQ(3U, histogram.size());
EXPECT_EQ(histogram[kWhite], expected_white)
<< "FAILED WHILE DRAWING \"" << glyphs << "\" AT SCALE: " << scale;
EXPECT_EQ(histogram[kBlack], expected_black * scale_squared)
<< "FAILED WHILE DRAWING \"" << glyphs << "\" AT SCALE: " << scale;
EXPECT_EQ(histogram[kTransparentBlack],
kNumFramebufferPixels - (scale_squared * kNumPixelsPerGlyph * glyphs.length()));
};
// Each time, we draw on top of the previous glyph.
draw_and_check_histogram("1", 5);
draw_and_check_histogram("A", 12);
draw_and_check_histogram("!", 4);
// Draw a glyph that has not been defined, it should draw a black square.
draw_and_check_histogram("Z", 25);
// Draw several glyphs next to each other.
draw_and_check_histogram(" 1A!", 0 + 5 + 12 + 4);
fd.frame->EndFrame(SemaphorePtr(), []() {});
}
escher()->vk_device().waitIdle();
ASSERT_TRUE(escher()->Cleanup());
}
// Tests that vertical and horizontal lines of a specific color are drawn correctly when called.
// It does this by checking the number of pixels drawn of the specified color against what is
// expected. Colors are created in debug_rects.h and take the format |kColor|.
VK_TEST_F(PaperRendererTest, Lines) {
for (int32_t thickness = 1; thickness <= 4; ++thickness) {
frame_setup();
// Draws verticle and horizontal lines of |KColor| starting at (0, 0) and going to |endCoord|.
std::function<void(DebugRects::Color, uint8_t)> draw_and_check_histogram =
[&](DebugRects::Color kColor, uint8_t endCoord) {
auto expected_colored = endCoord * thickness;
{
auto gpu_uploader =
std::make_shared<escher::BatchGpuUploader>(escher(), fd.frame->frame_number());
ren->BeginFrame(fd.frame, gpu_uploader, scene, cameras, fd.color_attachment);
ren->DrawVLine(kColor, 0, 0, endCoord, thickness);
ren->FinalizeFrame();
auto upload_semaphore = escher::Semaphore::New(escher()->vk_device());
gpu_uploader->AddSignalSemaphore(upload_semaphore);
gpu_uploader->Submit();
ren->EndFrame(std::move(upload_semaphore));
}
EXPECT_EQ(expected_colored, get_colored_data(kColor))
<< "FAILED WHILE DRAWING VERTICAL LINE OF COLOR \"" << kColor
<< "\" AT THICKNESS: " << thickness;
{
auto gpu_uploader =
std::make_shared<escher::BatchGpuUploader>(escher(), fd.frame->frame_number());
ren->BeginFrame(fd.frame, gpu_uploader, scene, cameras, fd.color_attachment);
ren->DrawHLine(kColor, 0, 0, endCoord, thickness);
ren->FinalizeFrame();
auto upload_semaphore = escher::Semaphore::New(escher()->vk_device());
gpu_uploader->AddSignalSemaphore(upload_semaphore);
gpu_uploader->Submit();
ren->EndFrame(std::move(upload_semaphore));
}
EXPECT_EQ(expected_colored, get_colored_data(kColor))
<< "FAILED WHILE DRAWING HORIZONTAL LINE OF COLOR \"" << kColor
<< "\" AT THICKNESS: " << thickness;
};
draw_and_check_histogram(escher::DebugRects::kPurple, (uint8_t)500);
draw_and_check_histogram(escher::DebugRects::kRed, (uint8_t)800);
draw_and_check_histogram(escher::DebugRects::kYellow, (uint8_t)200);
fd.frame->EndFrame(SemaphorePtr(), []() {});
}
escher()->vk_device().waitIdle();
ASSERT_TRUE(escher()->Cleanup());
}
// Tests drawing fake data used by the Debug Graph.
VK_TEST_F(PaperRendererTest, PaperTimestampGraph) {
int16_t expected_colored = 0;
PaperTimestampGraph graph;
for (int32_t i = 1; i <= 10; ++i) {
frame_setup();
// Creates an escher Timestamp where |done_time| > |start_time| so that the values are
// not negative. All other values are 0 to simplify the test.
std::function<void(int8_t, int8_t)> draw_and_check_histogram = [&](uint8_t start_time,
uint8_t done_time) {
auto gpu_uploader =
std::make_shared<escher::BatchGpuUploader>(escher(), fd.frame->frame_number());
EXPECT_TRUE(depth_buffer() || i == 1);
ren->BeginFrame(fd.frame, gpu_uploader, scene, cameras, fd.color_attachment);
EXPECT_TRUE(depth_buffer());
PaperRenderer::Timestamp ts;
ts.latch_point = 0;
ts.update_done = 0;
ts.render_start = start_time;
ts.render_done = done_time;
ts.target_present = 0;
ts.actual_present = 0;
graph.AddTimestamp(ts);
constexpr uint32_t kGraphHeight = 500;
constexpr uint32_t kGraphWidth = 500;
graph.DrawGraphContentOn(ren.get(), {{0, 0}, {kGraphWidth, kGraphHeight}});
ren->FinalizeFrame();
auto upload_semaphore = escher::Semaphore::New(escher()->vk_device());
gpu_uploader->AddSignalSemaphore(upload_semaphore);
gpu_uploader->Submit();
ren->EndFrame(std::move(upload_semaphore));
int16_t render_time = done_time - start_time;
const int16_t h_interval = kGraphHeight / 35;
const int16_t w_interval = PaperTimestampGraph::kSampleLineThickness;
expected_colored += (render_time * h_interval) * w_interval;
auto returned_colored = get_colored_data(escher::DebugRects::kRed);
EXPECT_EQ(expected_colored, returned_colored)
<< "FAILED WHILE DRAWING DEBUG DATA FOR RENDER TIME " << render_time;
};
int8_t end = i * 2;
draw_and_check_histogram(1, end);
fd.frame->EndFrame(SemaphorePtr(), []() {});
}
escher()->vk_device().waitIdle();
ASSERT_TRUE(escher()->Cleanup());
}
} // namespace test
} // namespace escher