blob: cec38b96e875ab847b6dd2464da0e7f06769037c [file] [log] [blame]
// 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] {
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),
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_->set_error_handler([this] {
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