| // Copyright 2017 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 "garnet/examples/ui/hello_pose_buffer/app.h" |
| |
| #if defined(countof) |
| // Workaround for compiler error due to Zircon defining countof() as a macro. |
| // Redefines countof() using GLM_COUNTOF(), which currently provides a more |
| // sophisticated implementation anyway. |
| #undef countof |
| #include <glm/glm.hpp> |
| #define countof(X) GLM_COUNTOF(X) |
| #else |
| // No workaround required. |
| #include <glm/glm.hpp> |
| #endif |
| |
| #include <glm/gtc/type_ptr.hpp> |
| #include <glm/gtx/quaternion.hpp> |
| |
| #include <fuchsia/ui/gfx/cpp/fidl.h> |
| #include <lib/async-loop/cpp/loop.h> |
| #include <lib/async/cpp/task.h> |
| |
| #include "lib/component/cpp/connect.h" |
| #include "lib/escher/util/image_utils.h" |
| |
| #include "lib/fxl/functional/make_copyable.h" |
| #include "lib/fxl/logging.h" |
| |
| #include "lib/escher/hmd/pose_buffer.h" |
| #include "lib/ui/scenic/cpp/commands.h" |
| #include "lib/ui/scenic/cpp/host_memory.h" |
| |
| using namespace scenic; |
| |
| namespace hello_pose_buffer { |
| |
| static constexpr float kEdgeLength = 900; |
| ; |
| |
| static constexpr uint64_t kBillion = 1000000000; |
| |
| App::App(async::Loop* loop) |
| : startup_context_(component::StartupContext::CreateFromStartupInfo()), |
| loop_(loop) { |
| // Connect to the Mozart service. |
| scenic_ = startup_context_ |
| ->ConnectToEnvironmentService<fuchsia::ui::scenic::Scenic>(); |
| scenic_.set_error_handler([this](zx_status_t status) { |
| FXL_LOG(INFO) << "Lost connection to Mozart service."; |
| loop_->Quit(); |
| }); |
| scenic_->GetDisplayInfo([this](fuchsia::ui::gfx::DisplayInfo display_info) { |
| Init(std::move(display_info)); |
| }); |
| } |
| |
| void App::CreateExampleScene(float display_width, float display_height) { |
| auto session = session_.get(); |
| |
| // The top-level nesting for drawing anything is compositor -> layer-stack |
| // -> layer. Layer content can come from an image, or by rendering a scene. |
| // In this case, we do the latter, so we nest layer -> renderer -> camera -> |
| // scene. |
| compositor_ = std::make_unique<DisplayCompositor>(session); |
| LayerStack layer_stack(session); |
| Layer layer(session); |
| Renderer renderer(session); |
| Scene scene(session); |
| camera_ = std::make_unique<Camera>(scene); |
| |
| float camera_offset = kEdgeLength * 4.f; |
| float eye_position[3] = {0, 0, camera_offset}; |
| float look_at[3] = {0, 0, 0}; |
| float up[3] = {0, 1, 0}; |
| float fovy = glm::radians(30.f); |
| |
| camera_->SetTransform(eye_position, look_at, up); |
| camera_->SetProjection(fovy); |
| |
| compositor_->SetLayerStack(layer_stack); |
| layer_stack.AddLayer(layer); |
| layer.SetSize(display_width, display_height); |
| layer.SetRenderer(renderer); |
| renderer.SetCamera(camera_->id()); |
| |
| // Set up lights. |
| AmbientLight ambient_light(session); |
| DirectionalLight directional_light(session); |
| scene.AddLight(ambient_light); |
| scene.AddLight(directional_light); |
| ambient_light.SetColor(0.3f, 0.3f, 0.3f); |
| directional_light.SetColor(0.7f, 0.7f, 0.7f); |
| directional_light.SetDirection(1.f, 1.f, -2.f); |
| |
| // Create an EntityNode to serve as the scene root. |
| EntityNode root_node(session); |
| scene.AddChild(root_node.id()); |
| |
| static const int num_checkers = 3; |
| static const float checker_length = kEdgeLength / num_checkers; |
| Rectangle checker_shape(session, kEdgeLength / num_checkers, |
| kEdgeLength / num_checkers); |
| |
| Material light_material(session); |
| light_material.SetColor(120, 120, 120, 255); |
| |
| Material dark_material(session); |
| dark_material.SetColor(20, 20, 20, 255); |
| |
| Material materials[2] = {std::move(light_material), std::move(dark_material)}; |
| |
| for (int i = 0; i < num_checkers; i++) { |
| for (int j = 0; j < num_checkers; j++) { |
| int material_index = (i + (j % 2)) % 2; |
| glm::vec3 translation( |
| kEdgeLength * -0.5 + checker_length * i + checker_length / 2, |
| kEdgeLength * -0.5 + checker_length * j + checker_length / 2, |
| kEdgeLength); |
| |
| // EntityNode checker_node(session); |
| ShapeNode checker_shape_node(session); |
| checker_shape_node.SetShape(checker_shape); |
| checker_shape_node.SetMaterial(materials[material_index]); |
| // checker_node.AddPart(checker_shape_node); |
| checker_shape_node.SetTranslation(translation.x, translation.y, |
| translation.z); |
| root_node.AddChild(checker_shape_node); |
| } |
| } |
| |
| uint64_t vmo_size = PAGE_SIZE; |
| zx::vmo vmo; |
| zx_status_t status; |
| status = zx::vmo::create(vmo_size, 0u, &pose_buffer_vmo_); |
| FXL_DCHECK(status == ZX_OK); |
| status = pose_buffer_vmo_.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo); |
| FXL_DCHECK(status == ZX_OK); |
| |
| uint64_t base_time = zx::clock::get_monotonic().get(); |
| uint64_t time_interval = 1024 * 1024 * 60 / 3.0; // 16.67 ms |
| uint32_t num_entries = 1; |
| |
| Memory mem(session, std::move(vmo), vmo_size, |
| fuchsia::images::MemoryType::VK_DEVICE_MEMORY); |
| Buffer pose_buffer(mem, 0, vmo_size); |
| |
| camera_->SetPoseBuffer(pose_buffer, num_entries, base_time, time_interval); |
| } |
| |
| void App::Init(fuchsia::ui::gfx::DisplayInfo display_info) { |
| FXL_LOG(INFO) << "Creating new Session"; |
| |
| // TODO: set up SessionListener. |
| session_ = std::make_unique<scenic::Session>(scenic_.get()); |
| session_->SetDebugName("Hello Pose Buffer"); |
| session_->set_error_handler([this](zx_status_t status) { |
| FXL_LOG(INFO) << "Session terminated."; |
| loop_->Quit(); |
| }); |
| |
| // Wait kSessionDuration seconds, and close the session. |
| constexpr zx::duration kSessionDuration = zx::sec(40); |
| async::PostDelayedTask(loop_->dispatcher(), |
| [this] { ReleaseSessionResources(); }, |
| kSessionDuration); |
| |
| // Set up initial scene. |
| const float display_width = static_cast<float>(display_info.width_in_px); |
| const float display_height = static_cast<float>(display_info.height_in_px); |
| CreateExampleScene(display_width, display_height); |
| |
| start_time_ = zx_clock_get(ZX_CLOCK_MONOTONIC); |
| Update(start_time_); |
| } |
| |
| void App::Update(uint64_t next_presentation_time) { |
| double secs = |
| static_cast<double>(next_presentation_time - start_time_) / kBillion; |
| |
| glm::vec3 pos(0, kEdgeLength / 2, 0); |
| glm::quat quat = |
| glm::angleAxis(static_cast<float>(secs / 2.0), glm::vec3(0, 0, 1)); |
| escher::hmd::Pose pose(quat, pos); |
| |
| // Use vmo::write here for test simplicity. In a real case the vmo should be |
| // mapped into a vmar so we dont need a syscall per write |
| zx_status_t status = |
| pose_buffer_vmo_.write(&pose, 0, sizeof(escher::hmd::Pose)); |
| FXL_DCHECK(status == ZX_OK); |
| |
| // Present |
| session_->Present( |
| next_presentation_time, [this](fuchsia::images::PresentationInfo info) { |
| Update(info.presentation_time + info.presentation_interval); |
| }); |
| } |
| |
| void App::ReleaseSessionResources() { |
| FXL_LOG(INFO) << "Closing session."; |
| |
| compositor_.reset(); |
| camera_.reset(); |
| |
| session_.reset(); |
| } |
| |
| } // namespace hello_pose_buffer |