blob: f84a0d1060727d5dcfa14a1950abe6f1d3c13215 [file] [log] [blame]
#include "application.h"
#include <binder/IServiceManager.h>
#include <cutils/log.h>
#include <dvr/graphics.h>
#include <dvr/performance_client_api.h>
#include <dvr/pose_client.h>
#include <EGL/egl.h>
#include <GLES3/gl3.h>
#include <gui/ISurfaceComposer.h>
#include <hardware/hwcomposer_defs.h>
#include <private/dvr/graphics/vr_gl_extensions.h>
#include <vector>
namespace android {
namespace dvr {
Application::Application()
: controller_api_status_logged_(false),
controller_connection_state_logged_(false) {}
Application::~Application() {
}
int Application::Initialize(JNIEnv* env, jobject app_context,
jobject class_loader) {
dvrSetCpuPartition(0, "/application/performance");
bool is_right_handed = true; // TODO: retrieve setting from system
elbow_model_.Enable(ElbowModel::kDefaultNeckPosition, is_right_handed);
last_frame_time_ = std::chrono::system_clock::now();
java_env_ = env;
app_context_ = app_context;
class_loader_ = class_loader;
return 0;
}
int Application::AllocateResources() {
int surface_width = 0, surface_height = 0;
DvrLensInfo lens_info = {};
GLuint texture_id = 0;
GLenum texture_target = 0;
std::vector<DvrSurfaceParameter> surface_params = {
DVR_SURFACE_PARAMETER_OUT(SURFACE_WIDTH, &surface_width),
DVR_SURFACE_PARAMETER_OUT(SURFACE_HEIGHT, &surface_height),
DVR_SURFACE_PARAMETER_OUT(INTER_LENS_METERS, &lens_info.inter_lens_meters),
DVR_SURFACE_PARAMETER_OUT(LEFT_FOV_LRBT, &lens_info.left_fov),
DVR_SURFACE_PARAMETER_OUT(RIGHT_FOV_LRBT, &lens_info.right_fov),
DVR_SURFACE_PARAMETER_OUT(SURFACE_TEXTURE_TARGET_TYPE, &texture_target),
DVR_SURFACE_PARAMETER_OUT(SURFACE_TEXTURE_TARGET_ID, &texture_id),
DVR_SURFACE_PARAMETER_IN(VISIBLE, 0),
DVR_SURFACE_PARAMETER_IN(Z_ORDER, 1),
DVR_SURFACE_PARAMETER_IN(GEOMETRY, DVR_SURFACE_GEOMETRY_SINGLE),
DVR_SURFACE_PARAMETER_IN(ENABLE_LATE_LATCH, 0),
DVR_SURFACE_PARAMETER_IN(DISABLE_DISTORTION, 0),
DVR_SURFACE_PARAMETER_LIST_END,
};
int ret = dvrGraphicsContextCreate(surface_params.data(), &graphics_context_);
if (ret)
return ret;
GLuint fbo = 0;
GLuint depth_stencil_buffer = 0;
GLuint samples = 1;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
texture_target, texture_id, 0, samples);
glGenRenderbuffers(1, &depth_stencil_buffer);
glBindRenderbuffer(GL_RENDERBUFFER, depth_stencil_buffer);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
GL_DEPTH_COMPONENT24, surface_width,
surface_height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER, depth_stencil_buffer);
ALOGI("Surface size=%dx%d", surface_width, surface_height);
pose_client_ = dvrPoseCreate();
if (!pose_client_)
return 1;
vec2i eye_size(surface_width / 2, surface_height);
eye_viewport_[0] = Range2i::FromSize(vec2i(0, 0), eye_size);
eye_viewport_[1] = Range2i::FromSize(vec2i(surface_width / 2, 0), eye_size);
eye_from_head_[0] = Eigen::Translation3f(
vec3(lens_info.inter_lens_meters * 0.5f, 0.0f, 0.0f));
eye_from_head_[1] = Eigen::Translation3f(
vec3(-lens_info.inter_lens_meters * 0.5f, 0.0f, 0.0f));
fov_[0] = FieldOfView(lens_info.left_fov[0], lens_info.left_fov[1],
lens_info.left_fov[2], lens_info.left_fov[3]);
fov_[1] = FieldOfView(lens_info.right_fov[0], lens_info.right_fov[1],
lens_info.right_fov[2], lens_info.right_fov[3]);
gvr_context_ = gvr::GvrApi::Create(java_env_, app_context_, class_loader_);
if (gvr_context_ == nullptr) {
ALOGE("Gvr context creation failed");
return 1;
}
int32_t options = gvr_controller_get_default_options();
options |= GVR_CONTROLLER_ENABLE_GYRO | GVR_CONTROLLER_ENABLE_ACCEL;
controller_.reset(new gvr::ControllerApi);
if (!controller_->Init(java_env_, app_context_, class_loader_, options,
gvr_context_->cobj())) {
ALOGE("Gvr controller init failed");
return 1;
}
controller_state_.reset(new gvr::ControllerState);
return 0;
}
void Application::DeallocateResources() {
gvr_context_.reset();
controller_.reset();
controller_state_.reset();
if (graphics_context_)
dvrGraphicsContextDestroy(graphics_context_);
if (pose_client_)
dvrPoseDestroy(pose_client_);
initialized_ = false;
}
void Application::ProcessTasks(const std::vector<MainThreadTask>& tasks) {
for (auto task : tasks) {
switch (task) {
case MainThreadTask::EnableDebugMode:
if (!debug_mode_) {
debug_mode_ = true;
SetVisibility(debug_mode_);
}
break;
case MainThreadTask::DisableDebugMode:
if (debug_mode_) {
debug_mode_ = false;
SetVisibility(debug_mode_);
}
break;
case MainThreadTask::EnteringVrMode:
if (!initialized_)
AllocateResources();
break;
case MainThreadTask::ExitingVrMode:
if (initialized_)
DeallocateResources();
break;
case MainThreadTask::Show:
if (!is_visible_)
SetVisibility(true);
break;
}
}
}
void Application::DrawFrame() {
// Thread should block if we are invisible or not fully initialized.
std::unique_lock<std::mutex> lock(mutex_);
wake_up_init_and_render_.wait(lock, [this]() {
return is_visible_ && initialized_ || !main_thread_tasks_.empty();
});
// Process main thread tasks if there are any.
std::vector<MainThreadTask> tasks;
tasks.swap(main_thread_tasks_);
lock.unlock();
if (!tasks.empty())
ProcessTasks(tasks);
if (!initialized_)
return;
// TODO(steventhomas): If we're not visible, block until we are. For now we
// throttle by calling dvrGraphicsWaitNextFrame.
DvrFrameSchedule schedule;
dvrGraphicsWaitNextFrame(graphics_context_, 0, &schedule);
OnDrawFrame();
if (is_visible_) {
ProcessControllerInput();
DvrPoseAsync pose;
dvrPoseGet(pose_client_, schedule.vsync_count, &pose);
last_pose_ = Posef(
quat(pose.orientation[3], pose.orientation[0], pose.orientation[1],
pose.orientation[2]),
vec3(pose.translation[0], pose.translation[1], pose.translation[2]));
std::chrono::time_point<std::chrono::system_clock> now =
std::chrono::system_clock::now();
double delta =
std::chrono::duration<double>(now - last_frame_time_).count();
last_frame_time_ = now;
if (delta > 1.0f)
delta = 0.05f;
fade_value_ += delta / 0.25f;
if (fade_value_ > 1.0f)
fade_value_ = 1.0f;
quat controller_quat(controller_orientation_.qw, controller_orientation_.qx,
controller_orientation_.qy, controller_orientation_.qz);
controller_position_ = elbow_model_.Update(
delta, last_pose_.GetRotation(), controller_quat, false);
dvrBeginRenderFrameEds(graphics_context_, pose.orientation,
pose.translation);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
mat4 head_matrix = last_pose_.GetObjectFromReferenceMatrix();
glViewport(eye_viewport_[kLeftEye].GetMinPoint()[0],
eye_viewport_[kLeftEye].GetMinPoint()[1],
eye_viewport_[kLeftEye].GetSize()[0],
eye_viewport_[kLeftEye].GetSize()[1]);
DrawEye(kLeftEye, fov_[kLeftEye].GetProjectionMatrix(0.1f, 500.0f),
eye_from_head_[kLeftEye], head_matrix);
glViewport(eye_viewport_[kRightEye].GetMinPoint()[0],
eye_viewport_[kRightEye].GetMinPoint()[1],
eye_viewport_[kRightEye].GetSize()[0],
eye_viewport_[kRightEye].GetSize()[1]);
DrawEye(kRightEye, fov_[kRightEye].GetProjectionMatrix(0.1f, 500.0f),
eye_from_head_[kRightEye], head_matrix);
dvrPresent(graphics_context_);
}
}
void Application::ProcessControllerInput() {
controller_state_->Update(*controller_);
gvr::ControllerApiStatus new_api_status = controller_state_->GetApiStatus();
gvr::ControllerConnectionState new_connection_state =
controller_state_->GetConnectionState();
if (!controller_api_status_logged_) {
controller_api_status_logged_ = true;
ALOGI("Controller api status: %s",
gvr::ControllerApi::ToString(new_api_status));
} else if (new_api_status != controller_api_status_) {
ALOGI("Controller api status changed: %s --> %s",
gvr::ControllerApi::ToString(controller_api_status_),
gvr::ControllerApi::ToString(new_api_status));
}
if (new_api_status == gvr::kControllerApiOk) {
if (!controller_connection_state_logged_) {
controller_connection_state_logged_ = true;
ALOGI("Controller connection state: %s",
gvr::ControllerApi::ToString(new_connection_state));
} else if (new_connection_state != controller_connection_state_) {
ALOGI("Controller connection state changed: %s --> %s",
gvr::ControllerApi::ToString(controller_connection_state_),
gvr::ControllerApi::ToString(new_connection_state));
}
} else {
controller_connection_state_logged_ = false;
}
if (new_api_status == gvr::kControllerApiOk)
controller_orientation_ = controller_state_->GetOrientation();
controller_api_status_ = new_api_status;
controller_connection_state_ = new_connection_state;
}
void Application::SetVisibility(bool visible) {
bool changed = is_visible_ != visible;
if (changed) {
is_visible_ = visible;
dvrGraphicsSurfaceSetVisible(graphics_context_, is_visible_);
if (is_visible_)
controller_->Resume();
else
controller_->Pause();
OnVisibilityChanged(is_visible_);
}
}
void Application::OnVisibilityChanged(bool visible) {
if (visible) {
fade_value_ = 0;
// We have been sleeping so to ensure correct deltas, reset the time.
last_frame_time_ = std::chrono::system_clock::now();
}
}
void Application::QueueTask(MainThreadTask task) {
std::unique_lock<std::mutex> lock(mutex_);
main_thread_tasks_.push_back(task);
wake_up_init_and_render_.notify_one();
}
} // namespace dvr
} // namespace android