blob: 10142627a14300d44a8a448fe8b69c84c9bd81e3 [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 "gtest/gtest.h"
#include "gtest_escher.h"
#include "src/ui/lib/escher/defaults/default_shader_program_factory.h"
#include "src/ui/lib/escher/escher.h"
#include "src/ui/lib/escher/hmd/pose_buffer.h"
#include "src/ui/lib/escher/hmd/pose_buffer_latching_shader.h"
#include "src/ui/lib/escher/renderer/frame.h"
#include "src/ui/lib/escher/resources/resource_recycler.h"
#include "src/ui/lib/escher/scene/camera.h"
#include "src/ui/lib/escher/test/gtest_vulkan.h"
#include "src/ui/lib/escher/util/epsilon_compare.h"
#include "src/ui/lib/escher/vk/buffer.h"
#include "src/ui/lib/escher/vk/gpu_allocator.h"
#include <glm/gtc/type_ptr.hpp>
namespace escher {
namespace test {
// Returns true iff |a| and |b| are the same within optional |epsilon|.
bool ComparePose(escher::hmd::Pose* p0, escher::hmd::Pose* p1, float epsilon = 0.0) {
bool compare = true;
EXPECT_TRUE(CompareFloat(p0->a, p1->a, epsilon));
compare = compare && CompareFloat(p0->a, p1->a, epsilon);
EXPECT_TRUE(CompareFloat(p0->b, p1->b, epsilon));
compare = compare && CompareFloat(p0->b, p1->b, epsilon);
EXPECT_TRUE(CompareFloat(p0->c, p1->c, epsilon));
compare = compare && CompareFloat(p0->c, p1->c, epsilon);
EXPECT_TRUE(CompareFloat(p0->d, p1->d, epsilon));
compare = compare && CompareFloat(p0->d, p1->d, epsilon);
EXPECT_TRUE(CompareFloat(p0->x, p1->x, epsilon));
compare = compare && CompareFloat(p0->x, p1->x, epsilon);
EXPECT_TRUE(CompareFloat(p0->y, p1->y, epsilon));
compare = compare && CompareFloat(p0->y, p1->y, epsilon);
EXPECT_TRUE(CompareFloat(p0->z, p1->z, epsilon));
compare = compare && CompareFloat(p0->z, p1->z, epsilon);
return true;
}
glm::mat4 MatrixFromPose(const hmd::Pose& pose) {
return glm::toMat4(glm::quat(pose.d, pose.a, pose.b, pose.c)) *
glm::translate(mat4(), glm::vec3(pose.x, pose.y, pose.z));
}
using PoseBufferTest = escher::test::TestWithVkValidationLayer;
// TODO(36692): This test now causes Vulkan validation errors on AEMU.
VK_TEST_F(PoseBufferTest, ComputeShaderLatching) {
auto escher = escher::test::EscherEnvironment::GetGlobalTestEnvironment()->GetEscher();
escher->shader_program_factory()->filesystem()->InitializeWithRealFiles({});
escher::FramePtr frame = escher->NewFrame("PoseBufferLatchingTest", 0);
uint32_t num_entries = 8;
uint64_t base_time = 42L; // Choose an arbitrary, non-zero time.
uint64_t time_interval = 1024 * 1024; // 1 ms
vk::DeviceSize pose_buffer_size = num_entries * sizeof(escher::hmd::Pose);
vk::MemoryPropertyFlags memory_property_flags =
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent;
vk::BufferUsageFlags buffer_usage_flags =
vk::BufferUsageFlagBits::eUniformBuffer | vk::BufferUsageFlagBits::eStorageBuffer;
// Create the shader.
hmd::PoseBuffer pose_buffer(
escher->gpu_allocator()->AllocateBuffer(escher->resource_recycler(), pose_buffer_size,
buffer_usage_flags, memory_property_flags),
num_entries, base_time, time_interval);
hmd::PoseBufferLatchingShader test_shader(escher->GetWeakPtr());
// Fill the pose buffer.
ASSERT_NE(nullptr, pose_buffer.buffer->host_ptr());
hmd::Pose* poses = reinterpret_cast<hmd::Pose*>(pose_buffer.buffer->host_ptr());
float pi = glm::pi<float>();
for (uint32_t i = 0; i < num_entries; i++) {
// Change pose each interation. The goal is to have unique poses in each
// slot of the buffer where the first pose is the identity pose.
glm::vec3 pos = vec3(i * 3.f, i * 5.f, i * 7.f);
vec3 euler_angles(2 * pi / num_entries * i);
glm::quat quat(euler_angles);
new (&poses[i]) escher::hmd::Pose(quat, pos);
}
// Dispatch shaders.
std::vector<BufferPtr> output_buffers;
std::vector<Camera> cameras;
// Dispatch a few extra to test modulo rollover.
uint32_t num_dispatches = num_entries * 2;
for (uint32_t i = 0; i < num_dispatches; i++) {
// Identity Camera.
Camera camera(glm::mat4(1), glm::mat4(1));
// Use identity camera for first iteration only, change for all others.
if (i != 0) {
camera = Camera(glm::rotate(glm::mat4(), 2 * pi / num_dispatches * i, vec3(1, 1, 1)),
glm::perspective(45.0f, 1.0f, 0.1f, 100.0f));
}
uint64_t latch_time = base_time + (time_interval * (0.5 + i));
output_buffers.push_back(test_shader.LatchPose(frame, camera, pose_buffer, latch_time, true));
cameras.push_back(camera);
}
// Dispatch the shader once to test the stereo flow.
// This is kept simple as most of the functionality is tested above.
// Identity Camera.
Camera left_camera(glm::mat4(1), glm::mat4(2));
Camera right_camera(glm::mat4(1), glm::mat4(3));
BufferPtr stereo_output_buffer =
test_shader.LatchStereoPose(frame, left_camera, right_camera, pose_buffer, base_time, true);
// Execute shaders.
frame->EndFrame(nullptr, []() {});
auto result = escher->vk_device().waitIdle();
ASSERT_EQ(vk::Result::eSuccess, result);
// Verify results.
for (uint32_t i = 0; i < num_dispatches; i++) {
auto output_buffer = output_buffers[i];
ASSERT_NE(nullptr, output_buffer->host_ptr());
uint32_t index = i % num_entries;
auto pose_in = &poses[index];
auto pose_out = reinterpret_cast<hmd::Pose*>(output_buffer->host_ptr());
EXPECT_TRUE(ComparePose(pose_in, pose_out, 0.0));
glm::mat4 vp_matrix_in =
cameras[i].projection() * MatrixFromPose(*pose_in) * cameras[i].transform();
glm::mat4 vp_matrix_out =
glm::make_mat4(reinterpret_cast<float*>(output_buffer->host_ptr() + sizeof(hmd::Pose)));
EXPECT_TRUE(CompareMatrix(vp_matrix_in, vp_matrix_out, 0.00001));
// Pose zero uses all identity params so VP result should be identity.
if (i == 0) {
EXPECT_TRUE(CompareMatrix(mat4(), vp_matrix_out, 0.0));
}
}
// Stereo flow:
glm::mat4 left_vp_matrix_in =
left_camera.projection() * MatrixFromPose(poses[0]) * left_camera.transform();
glm::mat4 left_vp_matrix_out = glm::make_mat4(
reinterpret_cast<float*>(stereo_output_buffer->host_ptr() + sizeof(hmd::Pose)));
EXPECT_TRUE(CompareMatrix(left_vp_matrix_in, left_vp_matrix_out, 0.00001));
glm::mat4 right_vp_matrix_in =
right_camera.projection() * MatrixFromPose(poses[0]) * right_camera.transform();
glm::mat4 right_vp_matrix_out = glm::make_mat4(reinterpret_cast<float*>(
stereo_output_buffer->host_ptr() + sizeof(hmd::Pose) + 16 * sizeof(float)));
EXPECT_TRUE(CompareMatrix(right_vp_matrix_in, right_vp_matrix_out, 0.00001));
escher->Cleanup();
}
} // namespace test
} // namespace escher