blob: c535c033c90a0771145dbd6a7aa64cb987377816 [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/flatland/rectangle_compositor.h"
#include <gtest/gtest.h>
#include "src/ui/lib/escher/defaults/default_shader_program_factory.h"
#include "src/ui/lib/escher/flatland/flatland_static_config.h"
#include "src/ui/lib/escher/renderer/batch_gpu_uploader.h"
#include "src/ui/lib/escher/renderer/frame.h"
#include "src/ui/lib/escher/renderer/render_funcs.h"
#include "src/ui/lib/escher/resources/resource.h"
#include "src/ui/lib/escher/resources/resource_manager.h"
#include "src/ui/lib/escher/test/common/gtest_escher.h"
#include "src/ui/lib/escher/test/common/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/vk/texture.h"
namespace escher {
// Default 1x1 texture for Renderables with no texture.
TexturePtr CreateWhiteTexture(EscherWeakPtr escher, BatchGpuUploader* gpu_uploader) {
FX_DCHECK(escher);
uint8_t channels[4];
channels[0] = channels[1] = channels[2] = channels[3] = 255;
auto image = escher->NewRgbaImage(gpu_uploader, 1, 1, channels);
return escher->NewTexture(std::move(image), vk::Filter::eNearest);
}
// 2x2 texture with white, red, green and blue pixels.
TexturePtr CreateFourColorTexture(EscherWeakPtr escher, BatchGpuUploader* gpu_uploader) {
FX_DCHECK(escher);
uint8_t channels[16] = {/*white*/ 255, 255, 255, 255, /*red*/ 255, 0, 0, 255,
/*green*/ 0, 255, 0, 255, /*blue*/ 0, 0, 255, 255};
auto image = escher->NewRgbaImage(gpu_uploader, 2, 2, channels);
return escher->NewTexture(std::move(image), vk::Filter::eNearest);
}
// WxH texture with red(top) and green(bottom) pixels.
TexturePtr CreateTwoColorTexture(EscherWeakPtr escher, BatchGpuUploader* gpu_uploader,
uint32_t width, uint32_t height) {
FX_DCHECK(escher);
std::vector<uint8_t> channels;
uint8_t red[4] = {255, 0, 0, 255};
uint8_t green[4] = {0, 255, 0, 255};
for (size_t y = 0; y < height; ++y) {
for (size_t x = 0; x < width; ++x) {
if (y < height / 2) {
channels.insert(channels.end(), red, red + 4);
} else {
channels.insert(channels.end(), green, green + 4);
}
}
}
auto image = escher->NewRgbaImage(gpu_uploader, width, height, channels.data());
return escher->NewTexture(std::move(image), vk::Filter::eLinear);
}
TexturePtr CreateDepthBuffer(Escher* escher, const ImagePtr& output_image) {
TexturePtr depth_buffer;
RenderFuncs::ObtainDepthTexture(
escher, output_image->use_protected_memory(), output_image->info(),
escher->device()->caps().GetMatchingDepthStencilFormat().value, depth_buffer);
return depth_buffer;
}
// Extends ReadbackTest to allow for quick testing of RectangleCompositor.
class RectangleCompositorTest : public ReadbackTest {
protected:
// |ReadbackTest|
void SetUp() override {
ReadbackTest::SetUp();
escher()->shader_program_factory()->filesystem()->InitializeWithRealFiles(kFlatlandShaderPaths);
ren_ = std::make_unique<RectangleCompositor>(escher()->GetWeakPtr());
frame_setup();
auto gpu_uploader =
std::make_shared<escher::BatchGpuUploader>(escher(), frame_data_.frame->frame_number());
auto cmd_buf = frame_data_.frame->cmds();
auto upload_semaphore = escher::Semaphore::New(escher()->vk_device());
gpu_uploader->AddSignalSemaphore(upload_semaphore);
default_texture_ = CreateWhiteTexture(escher(), gpu_uploader.get());
cmd_buf->AddWaitSemaphore(std::move(upload_semaphore),
vk::PipelineStageFlagBits::eVertexInput |
vk::PipelineStageFlagBits::eFragmentShader |
vk::PipelineStageFlagBits::eColorAttachmentOutput |
vk::PipelineStageFlagBits::eTransfer);
gpu_uploader->Submit();
frame_data_.frame->EndFrame(SemaphorePtr(), []() {});
}
// |ReadbackTest|
void TearDown() override {
frame_data_.frame->EndFrame(SemaphorePtr(), []() {});
EXPECT_VK_SUCCESS(escher()->vk_device().waitIdle());
ASSERT_TRUE(escher()->Cleanup());
ren_.reset();
ReadbackTest::TearDown();
}
// Sets up the environment.
void frame_setup() { frame_data_ = NewFrame(vk::ImageLayout::eColorAttachmentOptimal); }
escher::RectangleCompositor* renderer() const { return ren_.get(); }
public:
std::unique_ptr<escher::RectangleCompositor> ren_;
// Frame environment variables.
ReadbackTest::FrameData frame_data_;
// Default texture;
TexturePtr default_texture_;
// Common colors used between tests.
static constexpr ColorBgra kWhite = ColorBgra(255, 255, 255, 255);
static constexpr ColorBgra kRed = ColorBgra(255, 0, 0, 255);
static constexpr ColorBgra kGreen = ColorBgra(0, 255, 0, 255);
static constexpr ColorBgra kBlue = ColorBgra(0, 0, 255, 255);
static constexpr ColorBgra kBlack = ColorBgra(0, 0, 0, 0);
};
// Render a single renderable using the RectangleCompositor. It should
// render as a single white rectangle.
VK_TEST_F(RectangleCompositorTest, SingleRenderableTest) {
frame_setup();
EXPECT_TRUE(ren_);
Rectangle2D rectangle(vec2(150, 200), vec2(100, 300));
RectangleCompositor::ColorData color_data(vec4(1), /*is_opaque*/ true);
auto cmd_buf = frame_data_.frame->cmds();
auto depth_texture = CreateDepthBuffer(escher().get(), frame_data_.color_attachment);
ren_->DrawBatch(cmd_buf, {rectangle}, {default_texture_}, {color_data},
frame_data_.color_attachment, depth_texture);
auto bytes = ReadbackFromColorAttachment(frame_data_.frame,
frame_data_.color_attachment->swapchain_layout(),
vk::ImageLayout::eColorAttachmentOptimal);
const ColorHistogram<ColorBgra> histogram(bytes.data(), kFramebufferWidth * kFramebufferHeight);
constexpr ColorBgra kWhite(255, 255, 255, 255);
constexpr ColorBgra kBlack(0, 0, 0, 0);
EXPECT_EQ(2U, histogram.size());
EXPECT_EQ(histogram[kWhite], 30000U); // 100x300.
EXPECT_EQ(histogram[kBlack], (512U * 512U - 30000U));
}
// Render a single full-screen renderable with a texture that has 4 colors.
VK_TEST_F(RectangleCompositorTest, SimpleTextureTest) {
frame_setup();
auto gpu_uploader =
std::make_shared<escher::BatchGpuUploader>(escher(), frame_data_.frame->frame_number());
EXPECT_TRUE(gpu_uploader);
EXPECT_TRUE(ren_);
auto texture = CreateFourColorTexture(escher(), gpu_uploader.get());
gpu_uploader->Submit();
Rectangle2D rectangle(vec2(0, 0), vec2(512, 512));
RectangleCompositor::ColorData color_data(vec4(1), /*is_opaque*/ true);
auto cmd_buf = frame_data_.frame->cmds();
auto depth_texture = CreateDepthBuffer(escher().get(), frame_data_.color_attachment);
ren_->DrawBatch(cmd_buf, {rectangle}, {texture}, {color_data}, frame_data_.color_attachment,
depth_texture);
auto bytes = ReadbackFromColorAttachment(frame_data_.frame,
frame_data_.color_attachment->swapchain_layout(),
vk::ImageLayout::eColorAttachmentOptimal);
const ColorHistogram<ColorBgra> histogram(bytes.data(), kFramebufferWidth * kFramebufferHeight);
constexpr uint32_t num_pixels = 512 * 512 / 4;
EXPECT_EQ(4U, histogram.size());
EXPECT_EQ(histogram[kWhite], num_pixels);
EXPECT_EQ(histogram[kRed], num_pixels);
EXPECT_EQ(histogram[kGreen], num_pixels);
EXPECT_EQ(histogram[kBlue], num_pixels);
}
// Render a single full-screen renderable with a texture that has 2 colors.
VK_TEST_F(RectangleCompositorTest, TwoColorTextureTest) {
frame_setup();
auto gpu_uploader =
std::make_shared<escher::BatchGpuUploader>(escher(), frame_data_.frame->frame_number());
EXPECT_TRUE(gpu_uploader);
EXPECT_TRUE(ren_);
auto texture =
CreateTwoColorTexture(escher(), gpu_uploader.get(), kFramebufferWidth, kFramebufferHeight);
gpu_uploader->Submit();
Rectangle2D rectangle(vec2(0, 0), vec2(kFramebufferWidth, kFramebufferHeight));
RectangleCompositor::ColorData color_data(vec4(1), /*is_opaque*/ true);
auto cmd_buf = frame_data_.frame->cmds();
auto depth_texture = CreateDepthBuffer(escher().get(), frame_data_.color_attachment);
ren_->DrawBatch(cmd_buf, {rectangle}, {texture}, {color_data}, frame_data_.color_attachment,
depth_texture);
auto bytes = ReadbackFromColorAttachment(frame_data_.frame,
frame_data_.color_attachment->swapchain_layout(),
vk::ImageLayout::eColorAttachmentOptimal);
const ColorHistogram<ColorBgra> histogram(bytes.data(), kFramebufferWidth * kFramebufferHeight);
constexpr uint32_t num_pixels = kFramebufferWidth * kFramebufferHeight / 2;
EXPECT_EQ(histogram[kRed], num_pixels);
EXPECT_EQ(histogram[kGreen], num_pixels);
EXPECT_EQ(2U, histogram.size());
}
// Render with color conversion applied.
VK_TEST_F(RectangleCompositorTest, ColorConversionTest) {
frame_setup();
auto gpu_uploader =
std::make_shared<escher::BatchGpuUploader>(escher(), frame_data_.frame->frame_number());
EXPECT_TRUE(gpu_uploader);
EXPECT_TRUE(ren_);
auto texture = CreateWhiteTexture(escher(), gpu_uploader.get());
gpu_uploader->Submit();
// Set the color conversion parameters.
const glm::mat4 conversion_matrix(.288299, 0.052709, -0.257912, 0.00000, 0.711701, 0.947291,
0.257912, 0.00000, 0.000000, -0.000000, 1.000000, 0.00000,
0.000000, 0.000000, 0.00000, 1.00000);
const glm::vec4 offsets(0);
ren_->SetColorConversionParams({conversion_matrix, offsets, offsets});
Rectangle2D rectangle(vec2(0, 0), vec2(512, 512));
RectangleCompositor::ColorData color_data(vec4(1), /*is_opaque*/ true);
auto cmd_buf = frame_data_.frame->cmds();
auto depth_texture = CreateDepthBuffer(escher().get(), frame_data_.color_attachment);
ren_->DrawBatch(cmd_buf, {rectangle}, {texture}, {color_data}, frame_data_.color_attachment,
depth_texture, /*apply_color_conversion*/ true);
auto bytes = ReadbackFromColorAttachment(frame_data_.frame,
frame_data_.color_attachment->swapchain_layout(),
vk::ImageLayout::eColorAttachmentOptimal);
const ColorHistogram<ColorBgra> histogram(bytes.data(), kFramebufferWidth * kFramebufferHeight);
// Calculate expected color.
auto expected_color_float = conversion_matrix * glm::vec4(1, 1, 1, 1);
ColorBgra expected_color = {static_cast<uint8_t>(std::max(expected_color_float.x * 255, 0.f)),
static_cast<uint8_t>(std::max(expected_color_float.y * 255, 0.f)),
static_cast<uint8_t>(std::max(expected_color_float.z * 255, 0.f)),
static_cast<uint8_t>(std::max(expected_color_float.w * 255, 0.f))};
constexpr uint32_t num_pixels = 512 * 512;
EXPECT_EQ(1U, histogram.size());
EXPECT_EQ(histogram[expected_color], num_pixels);
// Reset compositor.
ren_->SetColorConversionParams({glm::mat4(1), glm::vec4(0), glm::vec4(0)});
}
// Render a single full-screen renderable with a texture that has 4 colors but
// with uv coordinates that make it so only one pixel is covering the renderable
// at a time, and test each one.
VK_TEST_F(RectangleCompositorTest, SimpleTextureNonStandardUVsTest) {
frame_setup();
auto gpu_uploader =
std::make_shared<escher::BatchGpuUploader>(escher(), frame_data_.frame->frame_number());
EXPECT_TRUE(gpu_uploader);
EXPECT_TRUE(ren_);
auto texture = CreateFourColorTexture(escher(), gpu_uploader.get());
gpu_uploader->Submit();
constexpr uint32_t num_pixels = 512 * 512;
const uint32_t kNumColors = 4;
ColorBgra colors[] = {kWhite, kRed, kGreen, kBlue};
std::array<vec2, 4> uvs[] = {
/*white*/ {vec2(0, 0), vec2(0.5, 0), vec2(0.5, 0.5), vec2(0, 0.5)},
/*red*/ {vec2(0.5, 0), vec2(1.0, 0), vec2(1.0, 0.5), vec2(0.5, 0.5)},
/*green*/ {vec2(0., 0.5), vec2(0.5, 0.5), vec2(0.5, 1.0), vec2(0, 1.0)},
/*blue*/ {vec2(0.5, 0.5), vec2(1.0, 0.5), vec2(1.0, 1.0), vec2(0.5, 1.0)}};
RectangleCompositor::ColorData color_data(vec4(1), /*is_opaque*/ true);
for (uint32_t i = 0; i < kNumColors; i++) {
auto cmd_buf = frame_data_.frame->cmds();
auto depth_texture = CreateDepthBuffer(escher().get(), frame_data_.color_attachment);
Rectangle2D rectangle(vec2(0, 0), vec2(512, 512), uvs[i]);
ren_->DrawBatch(cmd_buf, {rectangle}, {texture}, {color_data}, frame_data_.color_attachment,
depth_texture);
auto bytes = ReadbackFromColorAttachment(frame_data_.frame,
frame_data_.color_attachment->swapchain_layout(),
vk::ImageLayout::eColorAttachmentOptimal);
const ColorHistogram<ColorBgra> histogram(bytes.data(), kFramebufferWidth * kFramebufferHeight);
EXPECT_EQ(1U, histogram.size());
EXPECT_EQ(histogram[colors[i]], num_pixels);
}
}
// Render a single full-screen renderable that is rotated by 90 degrees
// and shifted so that it is half off the screen to the right. This should
// make it so that only 2 out of the 4 texture colors display, and those 2
// colors should be the proper colors post-rotation.
// Prerotation:
// | W R |
// | G B |
///
// Post rotation:
// | G W |
// | B R |
//
// When this post-rotation renderable is shifted to the right hand of the screen,
// only the green and blue colors should show.
VK_TEST_F(RectangleCompositorTest, RotatedTextureTest) {
frame_setup();
auto gpu_uploader =
std::make_shared<escher::BatchGpuUploader>(escher(), frame_data_.frame->frame_number());
EXPECT_TRUE(gpu_uploader);
EXPECT_TRUE(ren_);
auto texture = CreateFourColorTexture(escher(), gpu_uploader.get());
gpu_uploader->Submit();
Rectangle2D rectangle(vec2(kFramebufferWidth / 2, 0), vec2(kFramebufferWidth, kFramebufferHeight),
{vec2(0, 1), vec2(0, 0), vec2(1, 0), vec2(1, 1)});
RectangleCompositor::ColorData color_data(vec4(1), /*is_opaque*/ true);
auto cmd_buf = frame_data_.frame->cmds();
auto depth_texture = CreateDepthBuffer(escher().get(), frame_data_.color_attachment);
ren_->DrawBatch(cmd_buf, {rectangle}, {texture}, {color_data}, frame_data_.color_attachment,
depth_texture);
auto bytes = ReadbackFromColorAttachment(frame_data_.frame,
frame_data_.color_attachment->swapchain_layout(),
vk::ImageLayout::eColorAttachmentOptimal);
const ColorHistogram<ColorBgra> histogram(bytes.data(), kFramebufferWidth * kFramebufferHeight);
constexpr uint32_t num_pixels_per_color = kFramebufferWidth * kFramebufferHeight / 4;
// The three colors that should show are black (background), green and blue.
EXPECT_EQ(3U, histogram.size());
EXPECT_EQ(histogram[kWhite], 0U);
EXPECT_EQ(histogram[kRed], 0U);
EXPECT_EQ(histogram[kGreen], num_pixels_per_color);
EXPECT_EQ(histogram[kBlue], num_pixels_per_color);
}
// Render 4 rectangles side by side, each one taking up
// 1/4 of the entire frame. There should be no black pixels
// and each rectangle should have the same exact number of
// pixels covered.
VK_TEST_F(RectangleCompositorTest, MultiRenderableTest) {
frame_setup();
EXPECT_TRUE(ren_);
std::vector<Rectangle2D> rectangles;
std::vector<RectangleCompositor::ColorData> color_datas;
std::vector<const TexturePtr> textures;
vec4 colors[4] = {vec4{1, 0, 0, 1}, vec4(0, 1, 0, 1), vec4(0, 0, 1, 1), vec4(1, 1, 1, 1)};
for (uint32_t i = 0; i < 4; i++) {
Rectangle2D rectangle(vec2(128 * i, 0), vec2(128, 512));
RectangleCompositor::ColorData color_data(colors[i], /*is_opaque*/ true);
rectangles.emplace_back(rectangle);
color_datas.emplace_back(color_data);
textures.emplace_back(default_texture_);
}
auto cmd_buf = frame_data_.frame->cmds();
auto depth_texture = CreateDepthBuffer(escher().get(), frame_data_.color_attachment);
ren_->DrawBatch(cmd_buf, rectangles, textures, color_datas, frame_data_.color_attachment,
depth_texture);
auto bytes = ReadbackFromColorAttachment(frame_data_.frame,
frame_data_.color_attachment->swapchain_layout(),
vk::ImageLayout::eColorAttachmentOptimal);
const ColorHistogram<ColorBgra> histogram(bytes.data(), kFramebufferWidth * kFramebufferHeight);
size_t pixels_per_color = 128 * 512;
EXPECT_EQ(4U, histogram.size());
EXPECT_EQ(histogram[kRed], pixels_per_color);
EXPECT_EQ(histogram[kGreen], pixels_per_color);
EXPECT_EQ(histogram[kBlue], pixels_per_color);
EXPECT_EQ(histogram[kWhite], pixels_per_color);
EXPECT_EQ(histogram[kBlack], 0U);
}
// This test makes sure that depth is taken into account when
// rendering rectangles. Rectangle depth is implicit, with later
// rectangles being higher up than earlier rectangles. So this test
// renders two renderables, directly on top of eachother, red then
// green. Since the green one is inserted second, it should cover the
// red one, which should not have any pixels rendered.
VK_TEST_F(RectangleCompositorTest, OverlapTest) {
frame_setup();
EXPECT_TRUE(ren_);
std::vector<Rectangle2D> rectangles;
std::vector<RectangleCompositor::ColorData> color_datas;
std::vector<const TexturePtr> textures;
vec4 colors[2] = {vec4{1, 0, 0, 1}, vec4(0, 1, 0, 1)};
for (uint32_t i = 0; i < 2; i++) {
Rectangle2D rectangle(vec2(200, 200), vec2(100, 100));
RectangleCompositor::ColorData color_data(colors[i], /*is_opaque*/ true);
rectangles.emplace_back(rectangle);
color_datas.emplace_back(color_data);
textures.emplace_back(default_texture_);
}
auto cmd_buf = frame_data_.frame->cmds();
auto depth_texture = CreateDepthBuffer(escher().get(), frame_data_.color_attachment);
ren_->DrawBatch(cmd_buf, rectangles, textures, color_datas, frame_data_.color_attachment,
depth_texture);
auto bytes = ReadbackFromColorAttachment(frame_data_.frame,
frame_data_.color_attachment->swapchain_layout(),
vk::ImageLayout::eColorAttachmentOptimal);
const ColorHistogram<ColorBgra> histogram(bytes.data(), kFramebufferWidth * kFramebufferHeight);
size_t pixels_per_color = 100 * 100;
EXPECT_EQ(2U, histogram.size());
EXPECT_EQ(histogram[kRed], 0U);
EXPECT_EQ(histogram[kGreen], pixels_per_color);
EXPECT_EQ(histogram[kBlack], 512U * 512U - pixels_per_color);
}
// This test makes sure that alpha-blending transparency works.
// It renders a blue rectangle with 0.6 alpha on top of an
// opaque red rectangle.
// It does this test *twice*, once with is_opaque turned off
// and one with it on. Transparency should only be applied when
// the flag is on, even if the RectangleRenderable color has an
// alpha that is < 1.0.
// TODO (43394): Add testing for multiple interleaved opaque and
// transparent rectangles.
VK_TEST_F(RectangleCompositorTest, TransparencyTest) {
frame_setup();
EXPECT_TRUE(ren_);
std::vector<Rectangle2D> rectangles;
std::vector<RectangleCompositor::ColorData> color_datas;
std::vector<const TexturePtr> textures;
vec4 colors[2] = {vec4{1, 0, 0, 1}, vec4(0, 0, 1, 0.6)};
for (uint32_t i = 0; i < 2; i++) {
Rectangle2D rectangle(vec2(200, 200), vec2(100, 100));
RectangleCompositor::ColorData color_data(colors[i], /*is_opaque*/ false);
rectangles.emplace_back(rectangle);
color_datas.emplace_back(color_data);
textures.emplace_back(default_texture_);
}
auto cmd_buf = frame_data_.frame->cmds();
auto depth_texture = CreateDepthBuffer(escher().get(), frame_data_.color_attachment);
ren_->DrawBatch(cmd_buf, rectangles, textures, color_datas, frame_data_.color_attachment,
depth_texture);
auto bytes = ReadbackFromColorAttachment(frame_data_.frame,
frame_data_.color_attachment->swapchain_layout(),
vk::ImageLayout::eColorAttachmentOptimal);
const ColorHistogram<ColorBgra> histogram(bytes.data(), kFramebufferWidth * kFramebufferHeight);
// On Fuchsia the above transparency operation results in kBlend,
// but on LinuxHost it results in kBlend2. We check equality against
// both so that the test is robust regardless of platform.
constexpr ColorBgra kBlend(102, 0, 153, 255);
constexpr ColorBgra kBlend2(102, 0, 152, 255);
size_t pixels_per_color = 100 * 100;
EXPECT_EQ(2U, histogram.size());
EXPECT_EQ(histogram[kRed], 0U);
EXPECT_EQ(histogram[kBlue], 0U);
EXPECT_TRUE(histogram[kBlend] == pixels_per_color || histogram[kBlend2] == pixels_per_color);
EXPECT_EQ(histogram[kBlack], 512U * 512U - pixels_per_color);
}
// Turn the transparency flag off and try rendering with transparency again. Now
// even though the color has transparency, it should still render as opaque.
VK_TEST_F(RectangleCompositorTest, TransparencyFlagOffTest) {
frame_setup();
EXPECT_TRUE(ren_);
std::vector<Rectangle2D> rectangles;
std::vector<RectangleCompositor::ColorData> color_datas;
std::vector<const TexturePtr> textures;
vec4 colors[2] = {vec4{1, 0, 0, 1}, vec4(0, 0, 1, 0.6)};
for (uint32_t i = 0; i < 2; i++) {
Rectangle2D rectangle(vec2(200, 200), vec2(100, 100));
RectangleCompositor::ColorData color_data(colors[i], /*is_opaque*/ true);
rectangles.emplace_back(rectangle);
color_datas.emplace_back(color_data);
textures.emplace_back(default_texture_);
}
auto cmd_buf = frame_data_.frame->cmds();
auto depth_texture = CreateDepthBuffer(escher().get(), frame_data_.color_attachment);
ren_->DrawBatch(cmd_buf, rectangles, textures, color_datas, frame_data_.color_attachment,
depth_texture);
auto bytes = ReadbackFromColorAttachment(frame_data_.frame,
frame_data_.color_attachment->swapchain_layout(),
vk::ImageLayout::eColorAttachmentOptimal);
const ColorHistogram<ColorBgra> histogram2(bytes.data(), kFramebufferWidth * kFramebufferHeight);
constexpr ColorBgra kBlue2(0, 0, 153, 153); // Premultiplied alpha.
constexpr ColorBgra kBlend(102, 0, 153, 255);
constexpr ColorBgra kBlend2(102, 0, 152, 255);
size_t pixels_per_color = 100 * 100;
EXPECT_EQ(2U, histogram2.size());
EXPECT_EQ(histogram2[kRed], 0U);
EXPECT_EQ(histogram2[kBlue2], pixels_per_color);
EXPECT_TRUE(histogram2[kBlend] == pixels_per_color || histogram2[kBlend2] == 0);
EXPECT_EQ(histogram2[kBlack], 512U * 512U - pixels_per_color);
}
// Render 100 renderables.
VK_TEST_F(RectangleCompositorTest, StressTest) {
frame_setup();
EXPECT_TRUE(ren_);
std::vector<Rectangle2D> rectangles;
std::vector<RectangleCompositor::ColorData> color_datas;
std::vector<const TexturePtr> textures;
uint32_t max_renderables = 100;
for (uint32_t i = 0; i < max_renderables; i++) {
Rectangle2D rectangle(vec2(i, 0), vec2(1, 1));
RectangleCompositor::ColorData color_data(vec4(1, 0, 0, 1), /*is_opaque*/ true);
rectangles.emplace_back(rectangle);
color_datas.emplace_back(color_data);
textures.emplace_back(default_texture_);
}
auto cmd_buf = frame_data_.frame->cmds();
auto depth_texture = CreateDepthBuffer(escher().get(), frame_data_.color_attachment);
ren_->DrawBatch(cmd_buf, rectangles, textures, color_datas, frame_data_.color_attachment,
depth_texture);
auto bytes = ReadbackFromColorAttachment(frame_data_.frame,
frame_data_.color_attachment->swapchain_layout(),
vk::ImageLayout::eColorAttachmentOptimal);
const ColorHistogram<ColorBgra> histogram(bytes.data(), kFramebufferWidth * kFramebufferHeight);
EXPECT_EQ(2U, histogram.size());
EXPECT_EQ(histogram[kRed], 100U);
EXPECT_EQ(histogram[kBlack], 512U * 512U - 100U);
}
// Test to make sure that the image create info returned by the RectangleCompositor is
// AFBC compliant.
VK_TEST_F(RectangleCompositorTest, SetsAFBCCompatibleConstraints) {
frame_setup();
auto image_usage =
RectangleCompositor::kRenderTargetUsageFlags | vk::ImageUsageFlagBits::eTransferSrc;
auto vk_format = vk::Format::eB8G8R8A8Srgb;
auto constraints = RectangleCompositor::GetDefaultImageConstraints(vk_format, image_usage);
EXPECT_EQ(constraints.format, vk_format);
EXPECT_EQ(constraints.mipLevels, 1U);
EXPECT_EQ(constraints.arrayLayers, 1U);
// https://developer.arm.com/documentation/101897/0300/Buffers-and-textures/AFBC-textures-for-Vulkan
// In order to maintain ARM Framebuffer Compression (AFBC):
// - VkSampleCountFlagBits must be VK_SAMPLE_COUNT_1_BIT.
EXPECT_EQ(constraints.samples, vk::SampleCountFlagBits::e1);
// - VkImageType must be VK_IMAGE_TYPE_2D.
EXPECT_EQ(constraints.imageType, vk::ImageType::e2D);
// - VkImageTiling must be VK_IMAGE_TILING_OPTIMAL.
EXPECT_EQ(constraints.tiling, vk::ImageTiling::eOptimal);
// VkImageUsageFlags must not contain:
// - VK_IMAGE_USAGE_STORAGE_BIT
EXPECT_FALSE(constraints.usage & vk::ImageUsageFlagBits::eStorage);
// - VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT
EXPECT_FALSE(constraints.usage & vk::ImageUsageFlagBits::eTransientAttachment);
}
} // namespace escher