blob: 13b19a8a7a6fc90926523b1a9bbccd8ba0dda548 [file] [log] [blame]
// Copyright 2018 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 <lib/sys/cpp/testing/component_context_provider.h>
#include <lib/ui/scenic/cpp/commands.h>
#include <zircon/syscalls.h>
#include "gtest/gtest.h"
#include "sdk/lib/ui/scenic/cpp/view_token_pair.h"
#include "src/ui/lib/escher/paper/paper_renderer.h"
#include "src/ui/lib/escher/paper/paper_scene.h"
#include "src/ui/lib/escher/test/gtest_escher.h"
#include "src/ui/lib/escher/vk/image_layout_updater.h"
#include "src/ui/scenic/lib/gfx/engine/engine_renderer_visitor.h"
#include "src/ui/scenic/lib/gfx/resources/camera.h"
#include "src/ui/scenic/lib/gfx/resources/compositor/layer.h"
#include "src/ui/scenic/lib/gfx/resources/nodes/shape_node.h"
#include "src/ui/scenic/lib/gfx/resources/renderers/renderer.h"
#include "src/ui/scenic/lib/gfx/resources/shapes/rounded_rectangle_shape.h"
#include "src/ui/scenic/lib/gfx/resources/view_holder.h"
#include "src/ui/scenic/lib/gfx/tests/vk_session_test.h"
#include "src/ui/scenic/lib/gfx/util/time.h"
namespace scenic_impl {
namespace gfx {
namespace test {
using namespace escher;
class ViewClippingTest : public VkSessionTest {
public:
// We need a rounded rect factory and a view linker which
// the base VkSessionTest doesn't have.
void TearDown() override {
VkSessionTest::TearDown();
view_linker_.reset();
}
// We need a view linker which the base VkSessionTest doesn't have.
SessionContext CreateSessionContext() override {
auto session_context = VkSessionTest::CreateSessionContext();
FXL_DCHECK(!view_linker_);
view_linker_ = std::make_unique<ViewLinker>();
session_context.view_linker = view_linker_.get();
return session_context;
}
private:
std::unique_ptr<ViewLinker> view_linker_;
};
static constexpr float kNear = 1.f;
static constexpr float kFar = -200.f;
static constexpr float kWidth = 1024;
static constexpr float kHeight = 768;
// Simple unit test to check that view bound colors
// for debug wireframe rendering are being set properly.
VK_TEST_F(ViewClippingTest, SetBoundsRenderingTest) {
uint32_t scene_id = 5;
uint32_t view_id = 15;
uint32_t view_holder_id = 30;
auto [view_token, view_holder_token] = scenic::ViewTokenPair::New();
Apply(scenic::NewCreateSceneCmd(scene_id));
Apply(scenic::NewCreateViewHolderCmd(view_holder_id, std::move(view_holder_token), "ViewHolder"));
Apply(scenic::NewCreateViewCmd(view_id, std::move(view_token), "View"));
Apply(scenic::NewSetViewHolderBoundsColorCmd(view_holder_id, 255, 0, 255));
ViewHolder* view_holder = FindResource<ViewHolder>(view_holder_id).get();
EXPECT_TRUE(view_holder);
glm::vec4 color = view_holder->bounds_color() * 255.f;
EXPECT_EQ(color, glm::vec4(255, 0, 255, 255));
}
// This first unit test checks to see if a view holder
// is properly having its bounds set by the
// "SetViewPropertiesCmd" and if the correct clipping
// planes are being generated as a result.
VK_TEST_F(ViewClippingTest, ClipSettingTest) {
uint32_t scene_id = 5;
uint32_t view_id = 15;
uint32_t view_holder_id = 30;
auto [view_token, view_holder_token] = scenic::ViewTokenPair::New();
Apply(scenic::NewCreateSceneCmd(scene_id));
Apply(
scenic::NewCreateViewHolderCmd(view_holder_id, std::move(view_holder_token), "MyViewHolder"));
Apply(scenic::NewCreateViewCmd(view_id, std::move(view_token), "MyView"));
ViewHolder* view_holder = FindResource<ViewHolder>(view_holder_id).get();
EXPECT_TRUE(view_holder);
// Try a bunch of different bounding box configurations to make sure that
// they all work.
for (int32_t i = -10; i < 10; i++) {
for (int32_t j = -10; j < 10; j++) {
for (int32_t k = -10; k < 10; k++) {
for (int32_t m = 1; m < 10; m++) {
const std::array<float, 3> bbox_min = {float(i), float(j), float(k)};
const std::array<float, 3> bbox_max = {float(i + m), float(j + m), float(k + m)};
const std::array<float, 3> inset_min = {0, 0, 0};
const std::array<float, 3> inset_max = {0, 0, 0};
BoundingBox bbox(vec3(i, j, k), vec3(i + m, j + m, k + m));
Apply(scenic::NewSetViewPropertiesCmd(view_holder_id, bbox_min, bbox_max, inset_min,
inset_max));
const std::vector<plane3>& clip_planes = view_holder->clip_planes();
std::vector<plane3> test_planes = bbox.CreatePlanes();
EXPECT_EQ(clip_planes.size(), test_planes.size());
for (uint32_t p = 0; p < clip_planes.size(); p++) {
plane3 test_plane = test_planes[p];
plane3 view_plane = clip_planes[p];
EXPECT_EQ(test_plane.dir(), view_plane.dir());
EXPECT_EQ(test_plane.dist(), view_plane.dist());
}
}
}
}
}
}
// This first unit test checks to see if a view holder
// is properly having its bounds set by the
// "SetViewPropertiesCmd" and if the correct clipping
// planes are being generated as a result.
VK_TEST_F(ViewClippingTest, InsetsTest) {
uint32_t scene_id = 5;
uint32_t view_id = 15;
uint32_t view_holder_id = 30;
auto [view_token, view_holder_token] = scenic::ViewTokenPair::New();
Apply(scenic::NewCreateSceneCmd(scene_id));
Apply(scenic::NewCreateViewHolderCmd(view_holder_id, std::move(view_holder_token), "ViewHolder"));
Apply(scenic::NewCreateViewCmd(view_id, std::move(view_token), "View"));
ViewHolder* view_holder = FindResource<ViewHolder>(view_holder_id).get();
EXPECT_TRUE(view_holder);
// Set view bounding box properties.
const std::array<float, 3> bbox_min = {0, 0, -100};
const std::array<float, 3> bbox_max = {500, 500, 0};
const std::array<float, 3> inset_min = {10, 20, 30};
const std::array<float, 3> inset_max = {40, 50, 60};
Apply(scenic::NewSetViewPropertiesCmd(view_holder_id, bbox_min, bbox_max, inset_min, inset_max));
// Test to make sure the bounding boxes are the same.
BoundingBox test_bbox(vec3(10, 20, -70), vec3(460, 450, -60));
const BoundingBox view_bounding_box = view_holder->GetLocalBoundingBox();
EXPECT_EQ(test_bbox, view_bounding_box);
// Test to make sure the view planes are the same.
const std::vector<plane3>& view_planes = view_holder->clip_planes();
std::vector<plane3> test_planes = test_bbox.CreatePlanes();
EXPECT_EQ(view_planes.size(), test_planes.size());
for (uint32_t p = 0; p < view_planes.size(); p++) {
plane3 test_plane = test_planes[p];
plane3 view_plane = view_planes[p];
EXPECT_EQ(test_plane.dir(), view_plane.dir());
EXPECT_EQ(test_plane.dist(), view_plane.dist());
}
}
// Run a single test case on a view that's added to a ViewHolder after its
// properties are set to make sure that it still clips.
VK_TEST_F(ViewClippingTest, ClipSettingBeforeViewCreationTest) {
uint32_t scene_id = 5;
uint32_t view_id = 15;
uint32_t view_holder_id = 30;
auto [view_token, view_holder_token] = scenic::ViewTokenPair::New();
Apply(scenic::NewCreateSceneCmd(scene_id));
Apply(
scenic::NewCreateViewHolderCmd(view_holder_id, std::move(view_holder_token), "MyViewHolder"));
ViewHolder* view_holder = FindResource<ViewHolder>(view_holder_id).get();
EXPECT_TRUE(view_holder);
const std::array<float, 3> bbox_min = {-5, -10, -15};
const std::array<float, 3> bbox_max = {5, 10, 15};
const std::array<float, 3> inset = {0, 0, 0};
BoundingBox bbox(vec3(bbox_min[0], bbox_min[1], bbox_min[2]),
vec3(bbox_max[0], bbox_max[1], bbox_max[2]));
Apply(scenic::NewSetViewPropertiesCmd(view_holder_id, bbox_min, bbox_max, inset, inset));
const std::vector<plane3>& clip_planes = view_holder->clip_planes();
Apply(scenic::NewCreateViewCmd(view_id, std::move(view_token), "MyView"));
std::vector<plane3> test_planes = bbox.CreatePlanes();
EXPECT_EQ(clip_planes.size(), test_planes.size());
for (uint32_t p = 0; p < clip_planes.size(); p++) {
plane3 test_plane = test_planes[p];
plane3 view_plane = clip_planes[p];
EXPECT_EQ(test_plane.dir(), view_plane.dir());
EXPECT_EQ(test_plane.dist(), view_plane.dist());
}
}
// This test is used to check that meshes get clipped properly
// by their view holder's clip planes when the EngineRendererVisitor
// traverses the scene.
VK_TEST_F(ViewClippingTest, SceneTraversal) {
auto escher = escher::test::GetEscher()->GetWeakPtr();
uint32_t scene_id = 5;
uint32_t view_id = 15;
uint32_t view_holder_id = 30;
uint32_t shape_node_id = 50;
uint32_t material_id = 60;
uint32_t rect_id = 70;
auto [view_token, view_holder_token] = scenic::ViewTokenPair::New();
const std::array<float, 3> bbox_min = {0, 0, kFar};
const std::array<float, 3> bbox_max = {kWidth, kHeight, kNear};
const std::array<float, 3> inset_min = {0, 0, 0};
const std::array<float, 3> inset_max = {0, 0, 0};
Apply(scenic::NewCreateSceneCmd(scene_id));
Apply(
scenic::NewCreateViewHolderCmd(view_holder_id, std::move(view_holder_token), "MyViewHolder"));
Apply(scenic::NewCreateViewCmd(view_id, std::move(view_token), "MyView"));
Apply(scenic::NewSetViewPropertiesCmd(view_holder_id, bbox_min, bbox_max, inset_min, inset_max));
Apply(scenic::NewCreateShapeNodeCmd(shape_node_id));
// Set shape to shape node.
EXPECT_TRUE(Apply(scenic::NewCreateRoundedRectangleCmd(rect_id, 30.f, 40.f, 2.f, 4.f, 6.f, 8.f)));
Apply(scenic::NewSetShapeCmd(shape_node_id, rect_id));
// Set material to shape node.
Apply(scenic::NewCreateMaterialCmd(material_id));
Apply(scenic::NewSetColorCmd(material_id, 255, 255, 255, 255));
Apply(scenic::NewSetMaterialCmd(shape_node_id, material_id));
Apply(scenic::NewAddChildCmd(scene_id, view_holder_id));
Apply(scenic::NewAddChildCmd(view_id, shape_node_id));
ScenePtr scene = FindResource<Scene>(scene_id);
// Make default paper scene.
PaperScenePtr paper_scene = fxl::MakeRefCounted<PaperScene>();
paper_scene->bounding_box = BoundingBox(vec3(0.f, 0.f, kFar), vec3(kWidth, kHeight, kNear));
ViewingVolume volume(paper_scene->bounding_box);
// Make escher camera.
escher::Camera camera = escher::Camera::NewOrtho(volume);
// Make paper renderer.
PaperRendererPtr paper_renderer = PaperRenderer::New(escher);
// Make frame.
FramePtr frame = escher->NewFrame("ViewClippingFrame", 0);
// Make output image.
ImageInfo info;
info.format = vk::Format::eB8G8R8A8Srgb;
info.width = kWidth;
info.height = kHeight;
info.usage = vk::ImageUsageFlagBits::eColorAttachment;
auto image_cache = escher->image_cache();
auto output_image = image_cache->NewImage(info);
PaperDrawCallFactory* draw_call_factory = paper_renderer->draw_call_factory();
draw_call_factory->set_track_cache_entries(true);
auto gpu_uploader = std::make_shared<BatchGpuUploader>(escher, frame->frame_number());
auto layout_updater = std::make_unique<ImageLayoutUpdater>(escher);
// Set up output image layout.
frame->cmds()->ImageBarrier(
output_image, vk::ImageLayout::eUndefined, vk::ImageLayout::eColorAttachmentOptimal,
vk::PipelineStageFlagBits::eAllCommands, vk::AccessFlagBits::eColorAttachmentWrite,
vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::AccessFlagBits::eColorAttachmentWrite);
paper_renderer->BeginFrame(frame, gpu_uploader, paper_scene, {camera}, output_image);
EngineRendererVisitor visitor(paper_renderer.get(), gpu_uploader.get(), layout_updater.get(),
/*hide_protected_memory=*/false, nullptr);
visitor.Visit(scene.get());
// Get the cache entries from the PaperDrawcallFactory.
const std::vector<PaperShapeCacheEntry>& cache_entries =
draw_call_factory->tracked_cache_entries();
EXPECT_TRUE(cache_entries.size() == 1);
const PaperShapeCacheEntry& entry = cache_entries[0];
// Now we're going to manually create a cache entry using the same
// rounded rectangle and we're going to see if it matches the entry
// we got from going through the whole pipeline with the view
// properties set.
ShapeNode* shape_node = FindResource<ShapeNode>(shape_node_id).get();
EXPECT_TRUE(shape_node);
const ShapePtr& shape = shape_node->shape();
EXPECT_TRUE(shape);
EXPECT_TRUE(shape->IsKindOf<RoundedRectangleShape>());
auto rect = static_cast<RoundedRectangleShape*>(shape.get());
auto spec = rect->spec();
// These are the planes that the above view holder properties should generate.
std::vector<plane3> planes;
planes.push_back(plane3(vec3(1, 0, 0), 0));
planes.push_back(plane3(vec3(0, 1, 0), 0));
planes.push_back(plane3(vec3(0, 0, 1), -200));
planes.push_back(plane3(vec3(-1, 0, 0), -1024));
planes.push_back(plane3(vec3(0, -1, 0), -768));
planes.push_back(plane3(vec3(0, 0, -1), -1));
PaperShapeCache cache(escher, PaperRendererConfig());
cache.BeginFrame(gpu_uploader.get(), 0);
const PaperShapeCacheEntry& entry2 = cache.GetRoundedRectMesh(spec, planes.data(), 6);
// Cache entries should be identitcal.
EXPECT_EQ(entry.mesh->num_vertices(), entry2.mesh->num_vertices());
EXPECT_EQ(entry.num_indices, entry2.num_indices);
EXPECT_EQ(entry.num_shadow_volume_indices, entry2.num_shadow_volume_indices);
// End frame
paper_renderer->FinalizeFrame();
auto upload_semaphore = escher::Semaphore::New(escher->vk_device());
auto layout_update_semaphore = escher::Semaphore::New(escher->vk_device());
gpu_uploader->AddSignalSemaphore(upload_semaphore);
gpu_uploader->Submit();
layout_updater->AddSignalSemaphore(layout_update_semaphore);
layout_updater->Submit();
paper_renderer->EndFrame({std::move(upload_semaphore), std::move(layout_update_semaphore)});
cache.EndFrame();
auto frame_done_semaphore = Semaphore::New(escher->vk_device());
frame->EndFrame(frame_done_semaphore, nullptr);
output_image = nullptr;
escher->vk_device().waitIdle();
escher->Cleanup();
}
} // namespace test
} // namespace gfx
} // namespace scenic_impl