--- /dev/null
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+native_src = [
+ "application.cpp",
+ "controller_mesh.cpp",
+ "elbow_model.cpp",
+ "hwc_callback.cpp",
+ "reticle.cpp",
+ "shell_view.cpp",
+ "surface_flinger_view.cpp",
+ "texture.cpp",
+ "vr_window_manager.cpp",
+ "vr_window_manager_binder.cpp",
+ "aidl/android/service/vr/IVrWindowManager.aidl",
+ "display_view.cpp",
+]
+
+static_libs = [
+ "libdisplay",
+ "libbufferhub",
+ "libbufferhubqueue",
+ "libeds",
+ "libdvrgraphics",
+ "libdvrcommon",
+ "libhwcomposer-client",
+ "libvrsensor",
+ "libperformance",
+ "libpdx_default_transport",
+ "libcutils",
+ "libvr_hwc-binder",
+ "libvr_manager",
+ "libvirtualtouchpadclient",
+]
+
+shared_libs = [
+ "android.frameworks.vr.composer@1.0",
+ "android.hardware.graphics.composer@2.1",
+ "libbase",
+ "libbinder",
+ "libinput",
+ "libhardware",
+ "libhwbinder",
+ "libsync",
+ "libutils",
+ "libgui",
+ "libEGL",
+ "libGLESv2",
+ "libvulkan",
+ "libsync",
+ "libui",
+ "libhidlbase",
+ "libhidltransport",
+ "liblog",
+ "libvr_hwc-hal",
+]
+
+cc_binary {
+ srcs: native_src,
+ static_libs: static_libs,
+ shared_libs: shared_libs,
+ cflags: ["-DGL_GLEXT_PROTOTYPES", "-DEGL_EGLEXT_PROTOTYPES", "-DLOG_TAG=\"VrWindowManager\""],
+ host_ldlibs: ["-llog"],
+ name: "vr_wm",
+ tags: ["optional"],
+ init_rc: ["vr_wm.rc"],
+}
+
+cmd_src = [
+ "vr_wm_ctl.cpp",
+ "aidl/android/service/vr/IVrWindowManager.aidl",
+]
+
+staticLibs = ["libcutils"]
+
+sharedLibs = [
+ "libbase",
+ "libbinder",
+ "libutils",
+]
+
+cc_binary {
+ srcs: cmd_src,
+ static_libs: staticLibs,
+ shared_libs: sharedLibs,
+ cppflags: ["-std=c++11"],
+ cflags: ["-DLOG_TAG=\"vrwmctl\""],
+ host_ldlibs: ["-llog"],
+ name: "vr_wm_ctl",
+ tags: ["optional"],
+}
--- /dev/null
+/**
+ * Copyright (c) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.vr;
+
+/** @hide */
+interface IVrWindowManager {
+ const String SERVICE_NAME = "vr_window_manager";
+ void connectController(in FileDescriptor fd) = 0;
+ void disconnectController() = 1;
+ void enterVrMode() = 2;
+ void exitVrMode() = 3;
+ void setDebugMode(int mode) = 4;
+ void set2DMode(int mode) = 5;
+ void setRotation(int angle) = 6;
+}
+
--- /dev/null
+#include "application.h"
+
+#include <inttypes.h>
+#include <EGL/egl.h>
+#include <GLES3/gl3.h>
+#include <binder/IServiceManager.h>
+#include <dvr/graphics.h>
+#include <dvr/performance_client_api.h>
+#include <dvr/pose_client.h>
+#include <gui/ISurfaceComposer.h>
+#include <hardware/hwcomposer_defs.h>
+#include <log/log.h>
+#include <private/dvr/graphics/vr_gl_extensions.h>
+
+#include <vector>
+
+namespace android {
+namespace dvr {
+
+Application::Application() {
+ vr_mode_listener_ = new VrModeListener(this);
+}
+
+Application::~Application() {
+ sp<IVrManager> vrManagerService = interface_cast<IVrManager>(
+ defaultServiceManager()->getService(String16("vrmanager")));
+ if (vrManagerService.get()) {
+ vrManagerService->unregisterPersistentVrStateListener(vr_mode_listener_);
+ }
+}
+
+int Application::Initialize() {
+ 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();
+
+ sp<IVrManager> vrManagerService = interface_cast<IVrManager>(
+ defaultServiceManager()->getService(String16("vrmanager")));
+ if (vrManagerService.get()) {
+ vrManagerService->registerPersistentVrStateListener(vr_mode_listener_);
+ }
+ 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]);
+
+ return 0;
+}
+
+void Application::DeallocateResources() {
+ if (graphics_context_)
+ dvrGraphicsContextDestroy(graphics_context_);
+ graphics_context_ = nullptr;
+
+ 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_) {
+ LOG_ALWAYS_FATAL_IF(AllocateResources(),
+ "Failed to allocate resources");
+ }
+ 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;
+ int status = dvrGraphicsWaitNextFrame(graphics_context_, 0, &schedule);
+ if (status < 0) {
+ ALOGE("Context lost, deallocating graphics resources");
+ SetVisibility(false);
+ DeallocateResources();
+ }
+
+ 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;
+
+ controller_position_ =
+ elbow_model_.Update(delta, last_pose_.GetRotation(),
+ controller_orientation_, should_recenter_);
+
+ 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);
+
+ OnEndFrame();
+
+ dvrPresent(graphics_context_);
+ should_recenter_ = false;
+ }
+}
+
+void Application::ProcessControllerInput() {
+ if (controller_data_provider_) {
+ shmem_controller_active_ = false;
+ const void* data = controller_data_provider_->LockControllerData();
+ // TODO(kpschoedel): define wire format.
+ if (data) {
+ struct wire_format {
+ uint32_t version;
+ uint32_t timestamph;
+ uint32_t timestampl;
+ uint32_t quat_count;
+ float q[4];
+ uint32_t buttonsh;
+ uint32_t buttonsl;
+ } __attribute__((__aligned__(32)));
+ const wire_format* wire_data = static_cast<const wire_format*>(data);
+ static uint64_t last_timestamp = 0;
+ if (wire_data->version == 1) {
+ shmem_controller_active_ = true;
+ uint64_t timestamp =
+ (((uint64_t)wire_data->timestamph) << 32) | wire_data->timestampl;
+ if (timestamp == last_timestamp) {
+ static uint64_t last_logged_timestamp = 0;
+ if (last_logged_timestamp != last_timestamp) {
+ last_logged_timestamp = last_timestamp;
+ ALOGI("Controller shmem stale T=0x%" PRIX64, last_timestamp);
+ }
+ } else {
+ last_timestamp = timestamp;
+ controller_orientation_ = quat(wire_data->q[3], wire_data->q[0],
+ wire_data->q[1], wire_data->q[2]);
+ shmem_controller_buttons_ =
+ (((uint64_t)wire_data->buttonsh) << 32) | wire_data->buttonsl;
+ }
+ } else if (wire_data->version == 0xFEEDFACE) {
+ static bool logged_init = false;
+ if (!logged_init) {
+ logged_init = true;
+ ALOGI("Controller shmem waiting for data");
+ }
+ }
+ }
+ controller_data_provider_->UnlockControllerData();
+ if (shmem_controller_active_) {
+ ALOGV("Controller shmem orientation: %f %f %f %f",
+ controller_orientation_.x(), controller_orientation_.y(),
+ controller_orientation_.z(), controller_orientation_.w());
+ if (shmem_controller_buttons_) {
+ ALOGV("Controller shmem buttons: %017" PRIX64,
+ shmem_controller_buttons_);
+ }
+ }
+ }
+}
+
+void Application::SetVisibility(bool visible) {
+ if (visible && !initialized_) {
+ if (AllocateResources())
+ ALOGE("Failed to allocate resources");
+ }
+
+ bool changed = is_visible_ != visible;
+ if (changed) {
+ is_visible_ = visible;
+ dvrGraphicsSurfaceSetVisible(graphics_context_, is_visible_);
+ 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();
+}
+
+void Application::VrModeListener::onPersistentVrStateChanged(bool enabled) {
+ if (!enabled)
+ app_->QueueTask(MainThreadTask::ExitingVrMode);
+}
+
+} // namespace dvr
+} // namespace android
--- /dev/null
+#ifndef VR_WINDOW_MANAGER_APPLICATION_H_
+#define VR_WINDOW_MANAGER_APPLICATION_H_
+
+#include <memory>
+#include <private/dvr/types.h>
+#include <stdint.h>
+#include <vr/vr_manager/vr_manager.h>
+
+#include <chrono>
+#include <mutex>
+#include <vector>
+
+#include "controller_data_provider.h"
+#include "elbow_model.h"
+
+struct DvrGraphicsContext;
+struct DvrPose;
+
+namespace android {
+namespace dvr {
+
+class Application {
+ public:
+ Application();
+ virtual ~Application();
+
+ virtual int Initialize();
+
+ virtual int AllocateResources();
+ virtual void DeallocateResources();
+
+ void DrawFrame();
+
+ void SetControllerDataProvider(ControllerDataProvider* provider) {
+ controller_data_provider_ = provider;
+ }
+
+ protected:
+ enum class MainThreadTask {
+ EnteringVrMode,
+ ExitingVrMode,
+ EnableDebugMode,
+ DisableDebugMode,
+ Show,
+ };
+
+ class VrModeListener : public BnPersistentVrStateCallbacks {
+ public:
+ VrModeListener(Application *app) : app_(app) {}
+ void onPersistentVrStateChanged(bool enabled) override;
+
+ private:
+ Application *app_;
+ };
+
+ sp<VrModeListener> vr_mode_listener_;
+ virtual void OnDrawFrame() = 0;
+ virtual void DrawEye(EyeType eye, const mat4& perspective,
+ const mat4& eye_matrix, const mat4& head_matrix) = 0;
+ virtual void OnEndFrame() = 0;
+
+ void SetVisibility(bool visible);
+ virtual void OnVisibilityChanged(bool visible);
+
+ void ProcessControllerInput();
+
+ void ProcessTasks(const std::vector<MainThreadTask>& tasks);
+
+ void QueueTask(MainThreadTask task);
+
+ DvrGraphicsContext* graphics_context_ = nullptr;
+ DvrPose* pose_client_ = nullptr;
+
+ Range2i eye_viewport_[2];
+ mat4 eye_from_head_[2];
+ FieldOfView fov_[2];
+ Posef last_pose_;
+
+ quat controller_orientation_;
+ bool shmem_controller_active_ = false;
+ uint64_t shmem_controller_buttons_;
+
+ // Used to center the scene when the shell becomes visible.
+ bool should_recenter_ = true;
+
+ bool is_visible_ = false;
+ std::chrono::time_point<std::chrono::system_clock> visibility_button_press_;
+ bool debug_mode_ = false;
+
+ std::chrono::time_point<std::chrono::system_clock> last_frame_time_;
+ vec3 controller_position_;
+ ElbowModel elbow_model_;
+
+ float fade_value_ = 0;
+
+ std::mutex mutex_;
+ std::condition_variable wake_up_init_and_render_;
+ bool initialized_ = false;
+ std::vector<MainThreadTask> main_thread_tasks_;
+
+ // Controller data provider from shared memory buffer.
+ ControllerDataProvider* controller_data_provider_ = nullptr;
+
+ Application(const Application&) = delete;
+ void operator=(const Application&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // VR_WINDOW_MANAGER_APPLICATION_H_
--- /dev/null
+#ifndef VR_WINDOW_MANAGER_CONTROLLER_DATA_PROVIDER_H_
+#define VR_WINDOW_MANAGER_CONTROLLER_DATA_PROVIDER_H_
+
+namespace android {
+namespace dvr {
+
+class ControllerDataProvider {
+ public:
+ virtual ~ControllerDataProvider() {}
+ // Returns data pointer or nullptr. If pointer is valid, call to
+ // UnlockControllerData is required.
+ virtual const void* LockControllerData() = 0;
+ virtual void UnlockControllerData() = 0;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // VR_WINDOW_MANAGER_CONTROLLER_DATA_PROVIDER_H_
\ No newline at end of file
--- /dev/null
+#include "controller_mesh.h"
+
+namespace android {
+namespace dvr {
+
+const int kNumControllerMeshVertices = 60;
+
+// Vertices in position.xyz, normal.xyz, uv.xy oder.
+// Generated from an .obj mesh.
+const float kControllerMeshVertices[] = {
+ 0.002023, 0.001469, -0.5, 0.809016, 0.587787, 0, 0, 0,
+ 0.000773, 0.002378, -0.5, 0.309004, 0.951061, 0, 0.1, 0,
+ 0.000773, 0.002378, 0, 0.309004, 0.951061, 0, 0.1, 1,
+ 0.002023, 0.001469, -0.5, 0.809016, 0.587787, 0, 0, 0,
+ 0.000773, 0.002378, 0, 0.309004, 0.951061, 0, 0.1, 1,
+ 0.002023, 0.001469, 0, 0.809016, 0.587787, 0, 0, 1,
+ 0.000773, 0.002378, -0.5, 0.309004, 0.951061, 0, 0.1, 0,
+ -0.000773, 0.002378, -0.5, -0.309004, 0.951061, 0, 0.2, 0,
+ -0.000773, 0.002378, 0, -0.309004, 0.951061, 0, 0.2, 1,
+ 0.000773, 0.002378, -0.5, 0.309004, 0.951061, 0, 0.1, 0,
+ -0.000773, 0.002378, 0, -0.309004, 0.951061, 0, 0.2, 1,
+ 0.000773, 0.002378, 0, 0.309004, 0.951061, 0, 0.1, 1,
+ -0.000773, 0.002378, -0.5, -0.309004, 0.951061, 0, 0.2, 0,
+ -0.002023, 0.001469, -0.5, -0.809016, 0.587787, 0, 0.3, 0,
+ -0.002023, 0.001469, 0, -0.809016, 0.587787, 0, 0.3, 1,
+ -0.000773, 0.002378, -0.5, -0.309004, 0.951061, 0, 0.2, 0,
+ -0.002023, 0.001469, 0, -0.809016, 0.587787, 0, 0.3, 1,
+ -0.000773, 0.002378, 0, -0.309004, 0.951061, 0, 0.2, 1,
+ -0.002023, 0.001469, -0.5, -0.809016, 0.587787, 0, 0.3, 0,
+ -0.0025, 0, -0.5, -1, -0, 0, 0.4, 0,
+ -0.0025, 0, 0, -1, -0, 0, 0.4, 1,
+ -0.002023, 0.001469, -0.5, -0.809016, 0.587787, 0, 0.3, 0,
+ -0.0025, 0, 0, -1, -0, 0, 0.4, 1,
+ -0.002023, 0.001469, 0, -0.809016, 0.587787, 0, 0.3, 1,
+ -0.0025, 0, -0.5, -1, -0, 0, 0.4, 0,
+ -0.002023, -0.001469, -0.5, -0.809016, -0.587787, 0, 0.5, 0,
+ -0.002023, -0.001469, 0, -0.809016, -0.587787, 0, 0.5, 1,
+ -0.0025, 0, -0.5, -1, -0, 0, 0.4, 0,
+ -0.002023, -0.001469, 0, -0.809016, -0.587787, 0, 0.5, 1,
+ -0.0025, 0, 0, -1, -0, 0, 0.4, 1,
+ -0.002023, -0.001469, -0.5, -0.809016, -0.587787, 0, 0.5, 0,
+ -0.000773, -0.002378, -0.5, -0.309004, -0.951061, 0, 0.6, 0,
+ -0.000773, -0.002378, 0, -0.309004, -0.951061, 0, 0.6, 1,
+ -0.002023, -0.001469, -0.5, -0.809016, -0.587787, 0, 0.5, 0,
+ -0.000773, -0.002378, 0, -0.309004, -0.951061, 0, 0.6, 1,
+ -0.002023, -0.001469, 0, -0.809016, -0.587787, 0, 0.5, 1,
+ -0.000773, -0.002378, -0.5, -0.309004, -0.951061, 0, 0.6, 0,
+ 0.000773, -0.002378, -0.5, 0.309004, -0.951061, 0, 0.7, 0,
+ 0.000773, -0.002378, 0, 0.309004, -0.951061, 0, 0.7, 1,
+ -0.000773, -0.002378, -0.5, -0.309004, -0.951061, 0, 0.6, 0,
+ 0.000773, -0.002378, 0, 0.309004, -0.951061, 0, 0.7, 1,
+ -0.000773, -0.002378, 0, -0.309004, -0.951061, 0, 0.6, 1,
+ 0.000773, -0.002378, -0.5, 0.309004, -0.951061, 0, 0.7, 0,
+ 0.002023, -0.001469, -0.5, 0.809016, -0.587787, 0, 0.8, 0,
+ 0.002023, -0.001469, 0, 0.809016, -0.587787, 0, 0.8, 1,
+ 0.000773, -0.002378, -0.5, 0.309004, -0.951061, 0, 0.7, 0,
+ 0.002023, -0.001469, 0, 0.809016, -0.587787, 0, 0.8, 1,
+ 0.000773, -0.002378, 0, 0.309004, -0.951061, 0, 0.7, 1,
+ 0.002023, -0.001469, -0.5, 0.809016, -0.587787, 0, 0.8, 0,
+ 0.0025, 0, -0.5, 1, 0, 0, 0.9, 0,
+ 0.0025, 0, 0, 1, 0, 0, 0.9, 1,
+ 0.002023, -0.001469, -0.5, 0.809016, -0.587787, 0, 0.8, 0,
+ 0.0025, 0, 0, 1, 0, 0, 0.9, 1,
+ 0.002023, -0.001469, 0, 0.809016, -0.587787, 0, 0.8, 1,
+ 0.0025, 0, -0.5, 1, 0, 0, 0.9, 0,
+ 0.002023, 0.001469, -0.5, 0.809016, 0.587787, 0, 1, 0,
+ 0.002023, 0.001469, 0, 0.809016, 0.587787, 0, 1, 1,
+ 0.0025, 0, -0.5, 1, 0, 0, 0.9, 0,
+ 0.002023, 0.001469, 0, 0.809016, 0.587787, 0, 1, 1,
+ 0.0025, 0, 0, 1, 0, 0, 0.9, 1,
+
+};
+
+} // namespace dvr
+} // namespace android
--- /dev/null
+#ifndef VR_WINDOW_MANAGER_CONTROLLER_MESH_H_
+#define VR_WINDOW_MANAGER_CONTROLLER_MESH_H_
+
+namespace android {
+namespace dvr {
+
+extern const int kNumControllerMeshVertices;
+extern const float kControllerMeshVertices[];
+
+} // namespace dvr
+} // namespace android
+
+#endif // VR_WINDOW_MANAGER_CONTROLLER_MESH_H_
--- /dev/null
+#include "display_view.h"
+
+#include "texture.h"
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+constexpr float kLayerScaleFactor = 3.0f;
+constexpr unsigned int kMaximumPendingFrames = 8;
+constexpr uint32_t kSystemId = 1000;
+
+// clang-format off
+const GLfloat kVertices[] = {
+ -1, -1, 0,
+ 1, -1, 0,
+ -1, 1, 0,
+ 1, 1, 0,
+};
+
+const GLfloat kTextureVertices[] = {
+ 0, 1,
+ 1, 1,
+ 0, 0,
+ 1, 0,
+};
+// clang-format on
+
+// Returns true if the given point is inside the given rect.
+bool IsInside(const vec2& pt, const vec2& tl, const vec2& br) {
+ return pt.x() >= tl.x() && pt.x() <= br.x() && pt.y() >= tl.y() &&
+ pt.y() <= br.y();
+}
+
+mat4 GetScalingMatrix(float width, float height) {
+ float xscale = 1, yscale = 1;
+ float ar = width / height;
+ if (ar > 1)
+ yscale = 1.0 / ar;
+ else
+ xscale = ar;
+
+ xscale *= kLayerScaleFactor;
+ yscale *= kLayerScaleFactor;
+
+ return mat4(Eigen::Scaling<float>(xscale, yscale, 1.0));
+}
+
+// Helper function that applies the crop transform to the texture layer and
+// positions (and scales) the texture layer in the appropriate location in the
+// display space.
+mat4 GetLayerTransform(const TextureLayer& texture_layer, float display_width,
+ float display_height) {
+ // Map from vertex coordinates to [0, 1] coordinates:
+ // 1) Flip y since in vertex coordinates (-1, -1) is at the bottom left and
+ // in texture coordinates (0, 0) is at the top left.
+ // 2) Translate by (1, 1) to map vertex coordinates to [0, 2] on x and y.
+ // 3) Scale by 1 / 2 to map coordinates to [0, 1] on x and y.
+ mat4 unit_space(Eigen::AlignedScaling3f(0.5f, 0.5f, 1.0f) *
+ Eigen::Translation3f(1.0f, 1.0f, 0.0f) *
+ Eigen::AlignedScaling3f(1.0f, -1.0f, 1.0f));
+
+ mat4 texture_space(Eigen::AlignedScaling3f(
+ texture_layer.texture->width(), texture_layer.texture->height(), 1.0f));
+
+ // 1) Translate the layer to crop the left and top edge.
+ // 2) Scale the layer such that the cropped right and bottom edges map outside
+ // the exture region.
+ float crop_width = texture_layer.crop.right - texture_layer.crop.left;
+ float crop_height = texture_layer.crop.bottom - texture_layer.crop.top;
+ mat4 texture_crop(Eigen::AlignedScaling3f(
+ texture_layer.texture->width() / crop_width,
+ texture_layer.texture->height() / crop_height, 1.0f) *
+ Eigen::Translation3f(-texture_layer.crop.left,
+ -texture_layer.crop.top, 0.0f));
+
+ mat4 display_space(
+ Eigen::AlignedScaling3f(display_width, display_height, 1.0f));
+
+ // 1) Scale the texture to fit the display frame.
+ // 2) Translate the texture in the display frame location.
+ float display_frame_width =
+ texture_layer.display_frame.right - texture_layer.display_frame.left;
+ float display_frame_height =
+ texture_layer.display_frame.bottom - texture_layer.display_frame.top;
+ mat4 display_frame(
+ Eigen::Translation3f(texture_layer.display_frame.left,
+ texture_layer.display_frame.top, 0.0f) *
+ Eigen::AlignedScaling3f(display_frame_width / display_width,
+ display_frame_height / display_height, 1.0f));
+
+ mat4 layer_transform = unit_space.inverse() * display_space.inverse() *
+ display_frame * display_space *
+ texture_space.inverse() * texture_crop *
+ texture_space * unit_space;
+ return layer_transform;
+}
+
+// Determine if ths frame should be shown or hidden.
+ViewMode CalculateVisibilityFromLayerConfig(const HwcCallback::Frame& frame,
+ uint32_t* appid) {
+ auto& layers = frame.layers();
+
+ size_t index;
+ // Skip all layers that we don't know about.
+ for (index = 0; index < layers.size(); index++) {
+ if (layers[index].type != 0xFFFFFFFF && layers[index].type != 0)
+ break;
+ }
+
+ if (index == layers.size())
+ return ViewMode::Hidden;
+
+ if (layers[index].type != 1) {
+ // We don't have a VR app layer? Abort.
+ return ViewMode::Hidden;
+ }
+
+ if (layers[index].appid != *appid) {
+ *appid = layers[index].appid;
+ return ViewMode::App;
+ }
+
+ // This is the VR app, ignore it.
+ index++;
+
+ // Now, find a dim layer if it exists.
+ // If it does, ignore any layers behind it for visibility determination.
+ for (size_t i = index; i < layers.size(); i++) {
+ if (layers[i].appid == HwcCallback::HwcLayer::kSurfaceFlingerLayer) {
+ index = i + 1;
+ }
+ }
+
+ // If any non-skipped layers exist now then we show, otherwise hide.
+ for (size_t i = index; i < layers.size(); i++) {
+ if (!layers[i].should_skip_layer())
+ return ViewMode::VR;
+ }
+
+ return ViewMode::Hidden;
+}
+
+} // namespace
+
+DisplayView::DisplayView(uint32_t id, int touchpad_id)
+ : id_(id), touchpad_id_(touchpad_id) {
+ translate_ = Eigen::Translation3f(0, 0, -5.0f);
+ ime_translate_ = mat4(Eigen::Translation3f(0.0f, -0.5f, 0.25f));
+ ime_top_left_ = vec2(0, 0);
+ ime_size_ = vec2(0, 0);
+ rotation_ = mat4::Identity();
+}
+
+DisplayView::~DisplayView() {}
+
+void DisplayView::Recenter(const mat4& initial) {
+ initial_head_matrix_ =
+ initial * Eigen::AngleAxisf(M_PI * 0.5f, vec3::UnitZ());
+}
+
+void DisplayView::SetPrograms(ShaderProgram* program,
+ ShaderProgram* overlay_program) {
+ program_ = program;
+ overlay_program_ = overlay_program;
+}
+
+void DisplayView::DrawEye(EyeType /* eye */, const mat4& perspective,
+ const mat4& eye_matrix, const mat4& head_matrix,
+ float fade_value) {
+ scale_ = GetScalingMatrix(size_.x(), size_.y());
+
+ DrawOverlays(perspective, eye_matrix, head_matrix, fade_value);
+}
+
+void DisplayView::AdvanceFrame() {
+ if (!pending_frames_.empty()) {
+ // Check if we should advance the frame.
+ auto& frame = pending_frames_.front();
+ if (frame.visibility == ViewMode::Hidden ||
+ frame.frame->Finish() == HwcCallback::FrameStatus::kFinished) {
+ current_frame_ = std::move(frame);
+ pending_frames_.pop_front();
+ }
+ }
+}
+
+void DisplayView::OnDrawFrame(SurfaceFlingerView* surface_flinger_view,
+ bool debug_mode) {
+ textures_.clear();
+ has_ime_ = false;
+
+ if (!visible())
+ return;
+
+ surface_flinger_view->GetTextures(*current_frame_.frame.get(), &textures_,
+ &ime_texture_, debug_mode,
+ current_frame_.visibility == ViewMode::VR);
+ has_ime_ = ime_texture_.texture != nullptr;
+}
+
+base::unique_fd DisplayView::OnFrame(std::unique_ptr<HwcCallback::Frame> frame,
+ bool debug_mode, bool is_vr_active,
+ bool* showing) {
+ size_ = vec2(frame->display_width(), frame->display_height());
+ uint32_t app = current_vr_app_;
+ ViewMode visibility = CalculateVisibilityFromLayerConfig(*frame.get(), &app);
+
+ if (visibility == ViewMode::Hidden && debug_mode)
+ visibility = ViewMode::VR;
+
+ if (frame->layers().empty()) {
+ current_vr_app_ = 0;
+ } else if (visibility == ViewMode::App) {
+ // This is either a VR app switch or a 2D app launching.
+ // If we can have VR apps, update if it's 0.
+ if (!always_2d_ && is_vr_active && !use_2dmode_ && app != kSystemId) {
+ visibility = ViewMode::Hidden;
+ current_vr_app_ = app;
+ }
+ } else if ((use_2dmode_ || !is_vr_active) && app != 0 &&
+ visibility == ViewMode::Hidden) {
+ // This is the case for the VR app launching a 2D intent of itself on some
+ // display.
+ visibility = ViewMode::App;
+ } else if (!current_vr_app_) {
+ // The VR app is running.
+ current_vr_app_ = app;
+ }
+
+ pending_frames_.emplace_back(std::move(frame), visibility);
+
+ if (pending_frames_.size() > kMaximumPendingFrames) {
+ pending_frames_.pop_front();
+ }
+
+ if (visibility == ViewMode::Hidden &&
+ current_frame_.visibility == ViewMode::Hidden) {
+ // Consume all frames while hidden.
+ while (!pending_frames_.empty())
+ AdvanceFrame();
+ }
+
+ // If we are showing ourselves the main thread is not processing anything,
+ // so give it a kick.
+ if (visibility != ViewMode::Hidden &&
+ current_frame_.visibility == ViewMode::Hidden) {
+ *showing = true;
+ }
+
+ return base::unique_fd(dup(release_fence_.get()));
+}
+
+bool DisplayView::IsHit(const vec3& view_location, const vec3& view_direction,
+ vec3* hit_location, vec2* hit_location_in_window_coord,
+ bool test_ime) {
+ mat4 m = GetStandardTransform();
+ if (test_ime)
+ m = m * ime_translate_;
+ mat4 inverse = (m * scale_).inverse();
+ vec4 transformed_loc =
+ inverse * vec4(view_location[0], view_location[1], view_location[2], 1);
+ vec4 transformed_dir = inverse * vec4(view_direction[0], view_direction[1],
+ view_direction[2], 0);
+
+ if (transformed_dir.z() >= 0 || transformed_loc.z() <= 0)
+ return false;
+
+ float distance = -transformed_loc.z() / transformed_dir.z();
+ vec4 transformed_hit_loc = transformed_loc + transformed_dir * distance;
+ if (transformed_hit_loc.x() < -1 || transformed_hit_loc.x() > 1)
+ return false;
+ if (transformed_hit_loc.y() < -1 || transformed_hit_loc.y() > 1)
+ return false;
+
+ hit_location_in_window_coord->x() =
+ (1 + transformed_hit_loc.x()) / 2 * size_.x();
+ hit_location_in_window_coord->y() =
+ (1 - transformed_hit_loc.y()) / 2 * size_.y();
+
+ *hit_location = view_location + view_direction * distance;
+ return true;
+}
+
+void DisplayView::DrawOverlays(const mat4& perspective, const mat4& eye_matrix,
+ const mat4& head_matrix, float fade_value) {
+ if (textures_.empty())
+ return;
+
+ program_->Use();
+ mat4 mvp = perspective * eye_matrix * head_matrix;
+ GLint view_projection_location =
+ glGetUniformLocation(program_->GetProgram(), "uViewProjection");
+ glUniformMatrix4fv(view_projection_location, 1, 0, mvp.data());
+
+ GLint alpha_location = glGetUniformLocation(program_->GetProgram(), "uAlpha");
+
+ GLint tex_location = glGetUniformLocation(program_->GetProgram(), "tex");
+ glUniform1i(tex_location, 0);
+ glActiveTexture(GL_TEXTURE0);
+
+ for (const auto& texture_layer : textures_) {
+ switch (texture_layer.blending) {
+ case HWC2_BLEND_MODE_PREMULTIPLIED:
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ break;
+ case HWC2_BLEND_MODE_COVERAGE:
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ break;
+ default:
+ break;
+ }
+
+ glUniform1f(alpha_location, fade_value * texture_layer.alpha);
+
+ glBindTexture(GL_TEXTURE_2D, texture_layer.texture->id());
+
+ mat4 layer_transform =
+ GetLayerTransform(texture_layer, size_.x(), size_.y());
+
+ mat4 transform = GetStandardTransform() * scale_ * layer_transform;
+ DrawWithTransform(transform, *program_);
+
+ glDisable(GL_BLEND);
+ }
+
+ if (has_ime_) {
+ ime_top_left_ = vec2(static_cast<float>(ime_texture_.display_frame.left),
+ static_cast<float>(ime_texture_.display_frame.top));
+ ime_size_ = vec2(static_cast<float>(ime_texture_.display_frame.right -
+ ime_texture_.display_frame.left),
+ static_cast<float>(ime_texture_.display_frame.bottom -
+ ime_texture_.display_frame.top));
+
+ DrawDimOverlay(mvp, textures_[0], ime_top_left_, ime_top_left_ + ime_size_);
+
+ DrawIme();
+ }
+}
+
+void DisplayView::UpdateReleaseFence() {
+ EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ EGLSyncKHR sync =
+ eglCreateSyncKHR(display, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
+ if (sync != EGL_NO_SYNC_KHR) {
+ // Need to flush in order to get the fence FD.
+ glFlush();
+ base::unique_fd fence(eglDupNativeFenceFDANDROID(display, sync));
+ eglDestroySyncKHR(display, sync);
+ release_fence_ = std::move(fence);
+ } else {
+ ALOGE("Failed to create sync fence");
+ release_fence_ = base::unique_fd();
+ }
+}
+
+mat4 DisplayView::GetStandardTransform() {
+ mat4 m = initial_head_matrix_ * rotation_ * translate_;
+ if (current_frame_.visibility == ViewMode::App)
+ m *= Eigen::AngleAxisf(M_PI * -0.5f, vec3::UnitZ());
+ return m;
+}
+
+void DisplayView::DrawIme() {
+ program_->Use();
+ glBindTexture(GL_TEXTURE_2D, ime_texture_.texture->id());
+
+ mat4 layer_transform = GetLayerTransform(ime_texture_, size_.x(), size_.y());
+
+ mat4 transform =
+ GetStandardTransform() * ime_translate_ * scale_ * layer_transform;
+
+ DrawWithTransform(transform, *program_);
+}
+
+void DisplayView::DrawDimOverlay(const mat4& mvp, const TextureLayer& layer,
+ const vec2& top_left,
+ const vec2& bottom_right) {
+ overlay_program_->Use();
+ glUniformMatrix4fv(
+ glGetUniformLocation(overlay_program_->GetProgram(), "uViewProjection"),
+ 1, 0, mvp.data());
+ glUniform4f(glGetUniformLocation(overlay_program_->GetProgram(), "uCoords"),
+ top_left.x() / size_.x(), top_left.y() / size_.y(),
+ bottom_right.x() / size_.x(), bottom_right.y() / size_.y());
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ mat4 layer_transform = GetLayerTransform(layer, size_.x(), size_.y());
+
+ mat4 transform = GetStandardTransform() * scale_ * layer_transform;
+ DrawWithTransform(transform, *overlay_program_);
+ glDisable(GL_BLEND);
+}
+
+void DisplayView::DrawWithTransform(const mat4& transform,
+ const ShaderProgram& program) {
+ GLint transform_location =
+ glGetUniformLocation(program.GetProgram(), "uTransform");
+ glUniformMatrix4fv(transform_location, 1, 0, transform.data());
+
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, kVertices);
+ glEnableVertexAttribArray(1);
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, kTextureVertices);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+}
+
+bool DisplayView::UpdateHitInfo(const vec3& view_location,
+ const vec3& view_direction,
+ vec3* hit_location) {
+ bool is_hit = false;
+ if (has_ime_) {
+ // This will set allow_input_ and hit_location_in_window_coord_.
+ is_hit = IsImeHit(view_location, view_direction, hit_location);
+ } else {
+ is_hit = IsHit(view_location, view_direction, hit_location,
+ &hit_location_in_window_coord_, false);
+ allow_input_ = is_hit;
+ }
+ return is_hit;
+}
+
+bool DisplayView::IsImeHit(const vec3& view_location,
+ const vec3& view_direction, vec3* hit_location) {
+ // First, check if the IME window is hit.
+ bool is_hit = IsHit(view_location, view_direction, hit_location,
+ &hit_location_in_window_coord_, true);
+ if (is_hit) {
+ // If it is, check if the window coordinate is in the IME region;
+ // if so then we are done.
+ if (IsInside(hit_location_in_window_coord_, ime_top_left_,
+ ime_top_left_ + ime_size_)) {
+ allow_input_ = true;
+ return true;
+ }
+ }
+
+ allow_input_ = false;
+ // Check if we have hit the main window.
+ is_hit = IsHit(view_location, view_direction, hit_location,
+ &hit_location_in_window_coord_, false);
+ if (is_hit) {
+ // Only allow input if we are not hitting the region hidden by the IME.
+ // Allowing input here would cause clicks on the main window to actually
+ // be clicks on the IME.
+ if (!IsInside(hit_location_in_window_coord_, ime_top_left_,
+ ime_top_left_ + ime_size_)) {
+ allow_input_ = true;
+ }
+ }
+ return is_hit;
+}
+
+} // namespace dvr
+} // namespace android
--- /dev/null
+#ifndef VR_WINDOW_MANAGER_DISPLAY_VIEW_H_
+#define VR_WINDOW_MANAGER_DISPLAY_VIEW_H_
+
+#include <private/dvr/graphics/mesh.h>
+#include <private/dvr/graphics/shader_program.h>
+
+#include "hwc_callback.h"
+#include "surface_flinger_view.h"
+
+namespace android {
+namespace dvr {
+
+enum class ViewMode {
+ Hidden,
+ VR,
+ App,
+};
+
+class DisplayView {
+ public:
+ DisplayView(uint32_t id, int touchpad_id);
+ ~DisplayView();
+
+ // Calls to these 3 functions must be synchronized.
+ base::unique_fd OnFrame(std::unique_ptr<HwcCallback::Frame> frame,
+ bool debug_mode, bool is_vr_active, bool* showing);
+ void AdvanceFrame();
+ void UpdateReleaseFence();
+
+ void OnDrawFrame(SurfaceFlingerView* surface_flinger_view, bool debug_mode);
+ void DrawEye(EyeType eye, const mat4& perspective, const mat4& eye_matrix,
+ const mat4& head_matrix, float fade_value);
+
+ void Recenter(const mat4& initial);
+
+ bool UpdateHitInfo(const vec3& view_location, const vec3& view_direction,
+ vec3* hit_location);
+
+ void SetPrograms(ShaderProgram* program, ShaderProgram* overlay_program);
+
+ bool visible() const { return current_frame_.visibility != ViewMode::Hidden; }
+ bool allow_input() const { return allow_input_; }
+ const vec2& hit_location() const { return hit_location_in_window_coord_; }
+ uint32_t id() const { return id_; }
+ int touchpad_id() const { return touchpad_id_; }
+ vec2 size() const { return size_; }
+
+ void set_2dmode(bool mode) { use_2dmode_ = mode; }
+ void set_always_2d(bool mode) { always_2d_ = mode; }
+
+ void set_rotation(const mat4& rotation) { rotation_ = rotation; }
+
+ private:
+ bool IsHit(const vec3& view_location, const vec3& view_direction,
+ vec3* hit_location, vec2* hit_location_in_window_coord,
+ bool test_ime);
+ bool IsImeHit(const vec3& view_location, const vec3& view_direction,
+ vec3* hit_location);
+ void DrawOverlays(const mat4& perspective, const mat4& eye_matrix,
+ const mat4& head_matrix, float fade_value);
+ void DrawIme();
+ void DrawDimOverlay(const mat4& mvp, const TextureLayer& layer,
+ const vec2& top_left, const vec2& bottom_right);
+ void DrawWithTransform(const mat4& transform, const ShaderProgram& program);
+
+ // This is the rotated, translated but unscaled transform to apply everywhere.
+ mat4 GetStandardTransform();
+
+ uint32_t id_;
+ int touchpad_id_;
+
+ uint32_t current_vr_app_;
+
+ ShaderProgram* program_;
+ ShaderProgram* overlay_program_;
+
+ mat4 initial_head_matrix_;
+ mat4 scale_;
+ mat4 translate_;
+ mat4 ime_translate_;
+ mat4 rotation_;
+ vec2 size_;
+
+ std::vector<TextureLayer> textures_;
+ TextureLayer ime_texture_;
+
+ bool allow_input_ = false;
+ vec2 hit_location_in_window_coord_;
+ vec2 ime_top_left_;
+ vec2 ime_size_;
+ bool has_ime_ = false;
+ bool use_2dmode_ = false;
+ bool always_2d_ = false;
+
+ struct PendingFrame {
+ PendingFrame() = default;
+ PendingFrame(std::unique_ptr<HwcCallback::Frame>&& frame,
+ ViewMode visibility)
+ : frame(std::move(frame)), visibility(visibility) {}
+ PendingFrame(PendingFrame&& r)
+ : frame(std::move(r.frame)), visibility(r.visibility) {}
+
+ void operator=(PendingFrame&& r) {
+ frame.reset(r.frame.release());
+ visibility = r.visibility;
+ }
+
+ std::unique_ptr<HwcCallback::Frame> frame;
+ ViewMode visibility = ViewMode::Hidden;
+ };
+ std::deque<PendingFrame> pending_frames_;
+ PendingFrame current_frame_;
+ base::unique_fd release_fence_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // VR_WINDOW_MANAGER_DISPLAY_VIEW_H_
--- /dev/null
+#include "elbow_model.h"
+
+#include <log/log.h>
+
+namespace android {
+namespace dvr {
+namespace {
+
+const vec3 kControllerForearm(0.0f, 0.0f, -0.25f);
+const vec3 kControllerPosition(0.0f, 0.0f, -0.05f);
+const vec3 kLeftElbowPosition(-0.195f, -0.5f, 0.075f);
+const vec3 kLeftArmExtension(0.13f, 0.14f, -0.08f);
+const vec3 kRightElbowPosition(0.195f, -0.5f, 0.075f);
+const vec3 kRightArmExtension(-0.13f, 0.14f, -0.08f);
+constexpr float kElbowBendRatio = 0.4f;
+constexpr float kCosMaxExtensionAngle =
+ 0.87f; // Cos of 30 degrees (90-30 = 60)
+constexpr float kCosMinExtensionAngle = 0.12f; // Cos of 83 degrees (90-83 = 7)
+constexpr float kYAxisExtensionFraction = 0.4f;
+constexpr float kMinRotationSpeed = 0.61f; // 35 degrees in radians
+constexpr float kMinAngleDelta = 0.175f; // 10 degrees in radians
+
+float clamp(float v, float min, float max) {
+ if (v < min)
+ return min;
+ if (v > max)
+ return max;
+ return v;
+}
+
+float NormalizeAngle(float angle) {
+ if (angle > M_PI)
+ angle = 2.0f * M_PI - angle;
+ return angle;
+}
+
+} // namespace
+
+const vec3 ElbowModel::kDefaultNeckPosition = vec3(0, -0.075f, -0.080f);
+
+ElbowModel::ElbowModel() {}
+ElbowModel::~ElbowModel() {}
+
+void ElbowModel::Enable(const vec3& neck_position, bool right_handed) {
+ enabled_ = true;
+ neck_position_ = neck_position;
+
+ if (right_handed) {
+ elbow_position_ = kRightElbowPosition;
+ arm_extension_ = kRightArmExtension;
+ } else {
+ elbow_position_ = kLeftElbowPosition;
+ arm_extension_ = kLeftArmExtension;
+ }
+
+ ResetRoot();
+}
+
+void ElbowModel::Disable() { enabled_ = false; }
+
+vec3 ElbowModel::Update(float delta_t, const quat& hmd_orientation,
+ const quat& controller_orientation, bool recenter) {
+ if (!enabled_)
+ return vec3::Zero();
+
+ float heading_rad = GetHeading(hmd_orientation);
+
+ quat y_rotation;
+ y_rotation = Eigen::AngleAxis<float>(heading_rad, vec3::UnitY());
+
+ // If the controller's angular velocity is above a certain amount, we can
+ // assume torso rotation and move the elbow joint relative to the
+ // camera orientation.
+ float angle_delta = last_controller_.angularDistance(controller_orientation);
+ float rot_speed = angle_delta / delta_t;
+
+ if (recenter) {
+ root_rot_ = y_rotation;
+ } else if (rot_speed > kMinRotationSpeed) {
+ root_rot_.slerp(angle_delta / kMinAngleDelta, y_rotation);
+ }
+
+ // Calculate angle (or really, cos thereof) between controller forward vector
+ // and Y axis to determine extension amount.
+ vec3 controller_forward_rotated = controller_orientation * -vec3::UnitZ();
+ float dot_y = controller_forward_rotated.y();
+ float amt_extension = clamp(dot_y - kCosMinExtensionAngle, 0, 1);
+
+ // Remove the root rotation from the orientation reading--we'll add it back in
+ // later.
+ quat controller_rot = root_rot_.inverse() * controller_orientation;
+ controller_forward_rotated = controller_rot * -vec3::UnitZ();
+ quat rot_xy;
+ rot_xy.setFromTwoVectors(-vec3::UnitZ(), controller_forward_rotated);
+
+ // Fixing polar singularity
+ float total_angle = NormalizeAngle(atan2f(rot_xy.norm(), rot_xy.w()) * 2.0f);
+ float lerp_amount = (1.0f - powf(total_angle / M_PI, 6.0f)) *
+ (1.0f - (kElbowBendRatio +
+ (1.0f - kElbowBendRatio) *
+ (amt_extension + kYAxisExtensionFraction)));
+
+ // Calculate the relative rotations of the elbow and wrist joints.
+ quat wrist_rot = quat::Identity();
+ wrist_rot.slerp(lerp_amount, rot_xy);
+ quat elbow_rot = wrist_rot.inverse() * rot_xy;
+
+ last_controller_ = controller_orientation;
+
+ vec3 position =
+ root_rot_ *
+ ((controller_root_offset_ + arm_extension_ * amt_extension) +
+ elbow_rot * (kControllerForearm + wrist_rot * kControllerPosition));
+
+ return position;
+}
+
+float ElbowModel::GetHeading(const quat& orientation) {
+ vec3 gaze = orientation * -vec3::UnitZ();
+
+ if (gaze.y() > 0.99)
+ gaze = orientation * -vec3::UnitY();
+ else if (gaze.y() < -0.99)
+ gaze = orientation * vec3::UnitY();
+
+ return atan2f(-gaze.x(), -gaze.z());
+}
+
+void ElbowModel::ResetRoot() {
+ controller_root_offset_ = elbow_position_ + neck_position_;
+}
+
+} // namespace dvr
+} // namespace android
--- /dev/null
+#ifndef VR_WINDOW_MANAGER_ELBOW_MODEL_H_
+#define VR_WINDOW_MANAGER_ELBOW_MODEL_H_
+
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+class ElbowModel {
+ public:
+ ElbowModel();
+ ~ElbowModel();
+
+ void Enable(const vec3& neck_position, bool right_handed);
+ void Disable();
+
+ vec3 Update(float delta_t, const quat& hmd_orientation,
+ const quat& controller_orientation, bool recenter);
+
+ static const vec3 kDefaultNeckPosition;
+
+ private:
+ ElbowModel(const ElbowModel&) = delete;
+ void operator=(const ElbowModel&) = delete;
+
+ void ResetRoot();
+
+ float GetHeading(const quat& orientation);
+
+ bool enabled_ = false;
+
+ quat last_controller_ = quat::Identity();
+
+ quat root_rot_ = quat::Identity();
+
+ vec3 controller_root_offset_ = vec3::Zero();
+ vec3 elbow_position_ = vec3::Zero();
+ vec3 arm_extension_ = vec3::Zero();
+ vec3 neck_position_ = vec3::Zero();
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // VR_WINDOW_MANAGER_ELBOW_MODEL_H_
--- /dev/null
+#include "hwc_callback.h"
+
+#include <android-base/unique_fd.h>
+#include <log/log.h>
+#include <private/dvr/native_buffer.h>
+#include <sync/sync.h>
+#include <ui/GraphicBufferMapper.h>
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+HwcCallback::FrameStatus GetFrameStatus(const HwcCallback::Frame& frame) {
+ for (const auto& layer : frame.layers()) {
+ // If there is no fence it means the buffer is already finished.
+ if (layer.fence->isValid()) {
+ status_t result = layer.fence->wait(0);
+ if (result != OK) {
+ if (result != -ETIME) {
+ ALOGE("fence wait on buffer fence failed. status=%d (%s).",
+ result, strerror(-result));
+ return HwcCallback::FrameStatus::kError;
+ }
+ return HwcCallback::FrameStatus::kUnfinished;
+ }
+ }
+ }
+
+ return HwcCallback::FrameStatus::kFinished;
+}
+
+} // namespace
+
+void HwcCallback::HwcLayer::PrintLayer() {
+ ALOGI("appid=%d, type=%d, alpha=%.2f, cursor=%dx%d, color=%02X%02X%02X%02X, "
+ "crop=%.1f,%.1f,%.1f,%.1f, display=%d,%d,%d,%d, dataspace=%d, "
+ "transform=%d", appid, type, alpha, cursor_x, cursor_y, color.r, color.g,
+ color.b, color.a, crop.left, crop.top, crop.right, crop.bottom,
+ display_frame.left, display_frame.right, display_frame.top,
+ display_frame.bottom, dataspace, transform);
+}
+
+HwcCallback::HwcCallback(Client* client) : client_(client) {
+}
+
+HwcCallback::~HwcCallback() {
+}
+
+binder::Status HwcCallback::onNewFrame(
+ const ParcelableComposerFrame& parcelable_frame,
+ ParcelableUniqueFd* fence) {
+ ComposerView::Frame frame = parcelable_frame.frame();
+ std::vector<HwcLayer> hwc_frame(frame.layers.size());
+ for (size_t i = 0; i < frame.layers.size(); ++i) {
+ const ComposerView::ComposerLayer& layer = frame.layers[i];
+ hwc_frame[i] = HwcLayer{
+ .fence = layer.fence,
+ .buffer = layer.buffer,
+ .crop = layer.crop,
+ .display_frame = layer.display_frame,
+ .blending = static_cast<int32_t>(layer.blend_mode),
+ .appid = layer.app_id,
+ .type = static_cast<HwcLayer::LayerType>(layer.type),
+ .alpha = layer.alpha,
+ .cursor_x = layer.cursor_x,
+ .cursor_y = layer.cursor_y,
+ .color = layer.color,
+ .dataspace = layer.dataspace,
+ .transform = layer.transform,
+ };
+ }
+
+ fence->set_fence(client_->OnFrame(std::make_unique<Frame>(
+ std::move(hwc_frame), frame.display_id, frame.removed,
+ frame.display_width, frame.display_height)));
+ return binder::Status::ok();
+}
+
+HwcCallback::Frame::Frame(std::vector<HwcLayer>&& layers, uint32_t display_id,
+ bool removed, int32_t display_width,
+ int32_t display_height)
+ : display_id_(display_id),
+ removed_(removed),
+ display_width_(display_width),
+ display_height_(display_height),
+ layers_(std::move(layers)) {}
+
+HwcCallback::FrameStatus HwcCallback::Frame::Finish() {
+ if (status_ == FrameStatus::kUnfinished)
+ status_ = GetFrameStatus(*this);
+ return status_;
+}
+
+} // namespace dvr
+} // namespace android
--- /dev/null
+#ifndef VR_WINDOW_MANAGER_HWC_CALLBACK_H_
+#define VR_WINDOW_MANAGER_HWC_CALLBACK_H_
+
+#include <android/dvr/BnVrComposerCallback.h>
+#include <android-base/unique_fd.h>
+#include <impl/vr_hwc.h>
+
+#include <deque>
+#include <functional>
+#include <mutex>
+#include <vector>
+
+namespace android {
+
+class Fence;
+class GraphicBuffer;
+
+namespace dvr {
+
+using Recti = ComposerView::ComposerLayer::Recti;
+using Rectf = ComposerView::ComposerLayer::Rectf;
+
+class HwcCallback : public BnVrComposerCallback {
+ public:
+ struct HwcLayer {
+ enum LayerType : uint32_t {
+ // These are from frameworks/base/core/java/android/view/WindowManager.java
+ kSurfaceFlingerLayer = 0,
+ kUndefinedWindow = ~0U,
+ kFirstApplicationWindow = 1,
+ kLastApplicationWindow = 99,
+ kFirstSubWindow = 1000,
+ kLastSubWindow = 1999,
+ kFirstSystemWindow = 2000,
+ kStatusBar = kFirstSystemWindow,
+ kInputMethod = kFirstSystemWindow + 11,
+ kNavigationBar = kFirstSystemWindow + 19,
+ kLastSystemWindow = 2999,
+ };
+
+ bool should_skip_layer() const {
+ switch (type) {
+ // Always skip the following layer types
+ case kNavigationBar:
+ case kStatusBar:
+ case kSurfaceFlingerLayer:
+ case kUndefinedWindow:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ // This is a layer that provides some other functionality, eg dim layer.
+ // We use this to determine the point at which layers are "on top".
+ bool is_extra_layer() const {
+ switch(type) {
+ case kSurfaceFlingerLayer:
+ case kUndefinedWindow:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ void PrintLayer();
+
+ sp<Fence> fence;
+ sp<GraphicBuffer> buffer;
+ Rectf crop;
+ Recti display_frame;
+ int32_t blending;
+ uint32_t appid;
+ LayerType type;
+ float alpha;
+ int32_t cursor_x;
+ int32_t cursor_y;
+ IComposerClient::Color color;
+ int32_t dataspace;
+ int32_t transform;
+ };
+
+ enum class FrameStatus {
+ kUnfinished,
+ kFinished,
+ kError
+ };
+
+ class Frame {
+ public:
+ Frame(std::vector<HwcLayer>&& layers, uint32_t display_id, bool removed,
+ int32_t display_width, int32_t display_height);
+
+ FrameStatus Finish();
+ const std::vector<HwcLayer>& layers() const { return layers_; }
+ uint32_t display_id() const { return display_id_; }
+ bool removed() const { return removed_; }
+ int32_t display_width() const { return display_width_; }
+ int32_t display_height() const { return display_height_; }
+
+ private:
+ uint32_t display_id_;
+ bool removed_;
+ int32_t display_width_;
+ int32_t display_height_;
+ std::vector<HwcLayer> layers_;
+ FrameStatus status_ = FrameStatus::kUnfinished;
+ };
+
+ class Client {
+ public:
+ virtual ~Client() {}
+ virtual base::unique_fd OnFrame(std::unique_ptr<Frame>) = 0;
+ };
+
+ explicit HwcCallback(Client* client);
+ ~HwcCallback() override;
+
+ private:
+ binder::Status onNewFrame(const ParcelableComposerFrame& frame,
+ ParcelableUniqueFd* fence) override;
+
+ Client *client_;
+
+ HwcCallback(const HwcCallback&) = delete;
+ void operator=(const HwcCallback&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // VR_WINDOW_MANAGER_HWC_CALLBACK_H_
--- /dev/null
+# Don't obfuscate any NDK/SDK code. This makes the debugging of stack traces in
+# in release builds easier.
+-keepnames class com.google.vr.ndk.** { *; }
+-keepnames class com.google.vr.sdk.** { *; }
+
+# These are part of the SDK <-> VrCore interfaces for GVR.
+-keepnames class com.google.vr.vrcore.library.api.** { *; }
+
+# These are part of the Java <-> native interfaces for GVR.
+-keep class com.google.vr.** { native <methods>; }
+
+-keep class com.google.vr.cardboard.annotations.UsedByNative
+-keep @com.google.vr.cardboard.annotations.UsedByNative class *
+-keepclassmembers class * {
+ @com.google.vr.cardboard.annotations.UsedByNative *;
+}
+
+-keep class com.google.vr.cardboard.UsedByNative
+-keep @com.google.vr.cardboard.UsedByNative class *
+-keepclassmembers class * {
+ @com.google.vr.cardboard.UsedByNative *;
+}
--- /dev/null
+#include "reticle.h"
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+const std::string kVertexShader = SHADER0([]() {
+ layout(location = 0) in vec4 aPosition;
+ layout(location = 1) in vec4 aTexCoord;
+ uniform mat4 uViewProjection;
+ uniform mat4 uTransform;
+
+ out vec2 vTexCoord;
+ void main() {
+ gl_Position = uViewProjection * uTransform * aPosition;
+ vTexCoord = aTexCoord.xy;
+ }
+});
+
+const std::string kFragmentShader = SHADER0([]() {
+ precision mediump float;
+
+ in vec2 vTexCoord;
+ uniform vec3 uColor;
+
+ out vec4 fragColor;
+ void main() {
+ float alpha = smoothstep(1.0, 0.0, length(vTexCoord));
+ fragColor = vec4(uColor, alpha);
+ }
+});
+
+} // namespace
+
+Reticle::Reticle() {}
+
+Reticle::~Reticle() {}
+
+bool Reticle::Initialize() {
+ program_.Link(kVertexShader, kFragmentShader);
+ if (!program_)
+ return false;
+
+ return true;
+}
+
+void Reticle::ShowAt(const mat4& hit_transform, const vec3& color) {
+ transform_ = hit_transform;
+ shown_ = true;
+
+ GLint view_projection_location =
+ glGetUniformLocation(program_.GetProgram(), "uColor");
+ glProgramUniform3f(program_.GetProgram(), view_projection_location, color.x(),
+ color.y(), color.z());
+}
+
+void Reticle::Draw(const mat4& perspective, const mat4& eye_matrix,
+ const mat4& head_matrix) {
+ if (!shown_)
+ return;
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ program_.Use();
+
+ const float kRadius = 0.015;
+ GLfloat vertices[] = {
+ -kRadius, -kRadius, 0, kRadius, -kRadius, 0,
+ -kRadius, kRadius, 0, kRadius, kRadius, 0,
+ };
+ GLfloat texture_vertices[] = {
+ -1, 1, 1, 1, -1, -1, 1, -1,
+ };
+
+ mat4 mvp = perspective * eye_matrix * head_matrix;
+ GLint view_projection_location =
+ glGetUniformLocation(program_.GetProgram(), "uViewProjection");
+ glUniformMatrix4fv(view_projection_location, 1, 0, mvp.data());
+
+ GLint transform_location =
+ glGetUniformLocation(program_.GetProgram(), "uTransform");
+ glUniformMatrix4fv(transform_location, 1, 0, transform_.data());
+
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertices);
+ glEnableVertexAttribArray(1);
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, texture_vertices);
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ glDisable(GL_BLEND);
+}
+
+} // namespace dvr
+} // namespace android
--- /dev/null
+#ifndef VR_WINDOW_MANAGER_SHELL_RETICLE_H_
+#define VR_WINDOW_MANAGER_SHELL_RETICLE_H_
+
+#include <private/dvr/graphics/shader_program.h>
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+class Reticle {
+ public:
+ Reticle();
+ ~Reticle();
+
+ bool Initialize();
+
+ void ShowAt(const mat4& hit_transform, const vec3& color);
+ void Hide() { shown_ = false; }
+
+ void Draw(const mat4& perspective, const mat4& eye_matrix,
+ const mat4& head_matrix);
+
+ private:
+ bool shown_ = false;
+ ShaderProgram program_;
+ mat4 transform_;
+
+ Reticle(const Reticle&) = delete;
+ void operator=(const Reticle&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // VR_WINDOW_MANAGER_SHELL_RETICLE_H_
--- /dev/null
+#include "shell_view.h"
+
+#include <EGL/eglext.h>
+#include <GLES3/gl3.h>
+#include <android/input.h>
+#include <binder/IServiceManager.h>
+#include <dvr/graphics.h>
+#include <hardware/hwcomposer2.h>
+#include <inttypes.h>
+#include <log/log.h>
+
+#include "controller_mesh.h"
+#include "texture.h"
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+constexpr uint32_t kPrimaryDisplayId = 1;
+
+const std::string kVertexShader = SHADER0([]() {
+ layout(location = 0) in vec4 aPosition;
+ layout(location = 1) in vec4 aTexCoord;
+ uniform mat4 uViewProjection;
+ uniform mat4 uTransform;
+
+ out vec2 vTexCoord;
+ void main() {
+ gl_Position = uViewProjection * uTransform * aPosition;
+ vTexCoord = aTexCoord.xy;
+ }
+});
+
+const std::string kFragmentShader = SHADER0([]() {
+ precision mediump float;
+
+ in vec2 vTexCoord;
+ uniform sampler2D tex;
+ uniform float uAlpha;
+
+ out vec4 fragColor;
+ void main() {
+ fragColor = texture(tex, vTexCoord);
+ fragColor.a *= uAlpha;
+ }
+});
+
+// This shader provides a dim layer in a given rect. This is intended
+// to indicate the non-interactive region.
+// Texture coordinates between [uCoords.xy, uCoords.zw] are dim, otherwise
+// transparent.
+const std::string kOverlayFragmentShader = SHADER0([]() {
+ precision highp float;
+
+ in vec2 vTexCoord;
+ uniform sampler2D tex;
+ uniform vec4 uCoords;
+
+ out vec4 fragColor;
+ void main() {
+ vec4 color = vec4(0, 0, 0, 0);
+ if (all(greaterThan(vTexCoord, uCoords.xy)) &&
+ all(lessThan(vTexCoord, uCoords.zw))) {
+ color = vec4(0, 0, 0, 0.5);
+ }
+ fragColor = color;
+ }
+});
+
+const std::string kControllerFragmentShader = SHADER0([]() {
+ precision mediump float;
+
+ in vec2 vTexCoord;
+
+ out vec4 fragColor;
+ void main() { fragColor = vec4(0.8, 0.2, 0.2, 1.0); }
+});
+
+mat4 GetHorizontallyAlignedMatrixFromPose(const Posef& pose) {
+ vec3 position = pose.GetPosition();
+ quat view_quaternion = pose.GetRotation();
+
+ vec3 z = vec3(view_quaternion * vec3(0.0f, 0.0f, 1.0f));
+ vec3 y(0.0f, 1.0f, 0.0f);
+ vec3 x = y.cross(z);
+ x.normalize();
+ y = z.cross(x);
+
+ mat4 m;
+ // clang-format off
+ m(0, 0) = x[0]; m(0, 1) = y[0]; m(0, 2) = z[0]; m(0, 3) = position[0];
+ m(1, 0) = x[1]; m(1, 1) = y[1]; m(1, 2) = z[1]; m(1, 3) = position[1];
+ m(2, 0) = x[2]; m(2, 1) = y[2]; m(2, 2) = z[2]; m(2, 3) = position[2];
+ m(3, 0) = 0.0f; m(3, 1) = 0.0f; m(3, 2) = 0.0f; m(3, 3) = 1.0f;
+ // clang-format on
+
+ return m;
+}
+
+int GetTouchIdForDisplay(uint32_t display) {
+ return display == kPrimaryDisplayId ? DVR_VIRTUAL_TOUCHPAD_PRIMARY
+ : DVR_VIRTUAL_TOUCHPAD_VIRTUAL;
+}
+
+} // namespace
+
+ShellView::ShellView() {}
+
+ShellView::~ShellView() {}
+
+int ShellView::Initialize() {
+ int ret = Application::Initialize();
+ if (ret)
+ return ret;
+
+ virtual_touchpad_.reset(dvrVirtualTouchpadCreate());
+ const status_t touchpad_status =
+ dvrVirtualTouchpadAttach(virtual_touchpad_.get());
+ if (touchpad_status != OK) {
+ ALOGE("Failed to connect to virtual touchpad");
+ return touchpad_status;
+ }
+
+ surface_flinger_view_.reset(new SurfaceFlingerView);
+ if (!surface_flinger_view_->Initialize(this))
+ return 1;
+
+ return 0;
+}
+
+int ShellView::AllocateResources() {
+ int ret = Application::AllocateResources();
+ if (ret)
+ return ret;
+
+ program_.reset(new ShaderProgram);
+ program_->Link(kVertexShader, kFragmentShader);
+ overlay_program_.reset(new ShaderProgram);
+ overlay_program_->Link(kVertexShader, kOverlayFragmentShader);
+ controller_program_.reset(new ShaderProgram);
+ controller_program_->Link(kVertexShader, kControllerFragmentShader);
+ if (!program_ || !overlay_program_ || !controller_program_)
+ return 1;
+
+ reticle_.reset(new Reticle());
+ if (!reticle_->Initialize())
+ return 1;
+
+ controller_mesh_.reset(new Mesh<vec3, vec3, vec2>());
+ controller_mesh_->SetVertices(kNumControllerMeshVertices,
+ kControllerMeshVertices);
+
+ for (auto& display : displays_)
+ display->SetPrograms(program_.get(), overlay_program_.get());
+
+ initialized_ = true;
+
+ return 0;
+}
+
+void ShellView::DeallocateResources() {
+ {
+ std::unique_lock<std::mutex> l(display_frame_mutex_);
+ removed_displays_.clear();
+ new_displays_.clear();
+ displays_.clear();
+ }
+
+ display_client_.reset();
+ reticle_.reset();
+ controller_mesh_.reset();
+ program_.reset(new ShaderProgram);
+ overlay_program_.reset(new ShaderProgram);
+ controller_program_.reset(new ShaderProgram);
+ Application::DeallocateResources();
+}
+
+void ShellView::EnableDebug(bool debug) {
+ QueueTask(debug ? MainThreadTask::EnableDebugMode
+ : MainThreadTask::DisableDebugMode);
+}
+
+void ShellView::VrMode(bool mode) {
+ QueueTask(mode ? MainThreadTask::EnteringVrMode
+ : MainThreadTask::ExitingVrMode);
+}
+
+void ShellView::dumpInternal(String8& result) {
+ result.append("[shell]\n");
+ result.appendFormat("initialized = %s\n", initialized_ ? "true" : "false");
+ result.appendFormat("is_visible = %s\n", is_visible_ ? "true" : "false");
+ result.appendFormat("debug_mode = %s\n\n", debug_mode_ ? "true" : "false");
+
+ result.append("[displays]\n");
+ result.appendFormat("count = %zu\n", displays_.size());
+ for (size_t i = 0; i < displays_.size(); ++i) {
+ result.appendFormat(" display_id = %" PRId32 "\n", displays_[i]->id());
+ result.appendFormat(" size=%fx%f\n",
+ displays_[i]->size().x(), displays_[i]->size().y());
+ }
+
+ result.append("\n");
+}
+
+void ShellView::Set2DMode(bool mode) {
+ if (!displays_.empty())
+ displays_[0]->set_2dmode(mode);
+}
+
+void ShellView::SetRotation(int angle) {
+ mat4 m(Eigen::AngleAxisf(M_PI * -0.5f * angle, vec3::UnitZ()));
+ for (auto& d: displays_)
+ d->set_rotation(m);
+}
+
+void ShellView::OnDrawFrame() {
+ bool visible = false;
+
+ {
+ std::unique_lock<std::mutex> l(display_frame_mutex_);
+
+ // Move any new displays into the list.
+ if (!new_displays_.empty()) {
+ for (auto& display : new_displays_) {
+ display->Recenter(GetHorizontallyAlignedMatrixFromPose(last_pose_));
+ display->SetPrograms(program_.get(), overlay_program_.get());
+ displays_.emplace_back(display.release());
+ }
+ new_displays_.clear();
+ }
+
+ // Remove any old displays from the list now.
+ if (!removed_displays_.empty()) {
+ for (auto& display : removed_displays_) {
+ displays_.erase(std::find_if(
+ displays_.begin(), displays_.end(),
+ [display](auto& ptr) { return display == ptr.get(); }));
+ }
+ removed_displays_.clear();
+ }
+
+ for (auto& display : displays_) {
+ display->AdvanceFrame();
+ visible = visible || display->visible();
+ }
+ }
+
+ if (!debug_mode_ && visible != is_visible_) {
+ SetVisibility(visible);
+ }
+
+ for (auto& display : displays_) {
+ display->OnDrawFrame(surface_flinger_view_.get(), debug_mode_);
+ }
+}
+
+void ShellView::OnEndFrame() {
+ std::unique_lock<std::mutex> l(display_frame_mutex_);
+ for (auto& display : displays_) {
+ display->UpdateReleaseFence();
+ }
+}
+
+DisplayView* ShellView::FindOrCreateDisplay(uint32_t id) {
+ for (auto& display : displays_) {
+ if (display->id() == id) {
+ return display.get();
+ }
+ }
+
+ // It might be pending addition.
+ for (auto& display : new_displays_) {
+ if (display->id() == id) {
+ return display.get();
+ }
+ }
+
+ auto display = new DisplayView(id, GetTouchIdForDisplay(id));
+ // Virtual displays only ever have 2D apps so force it.
+ if (id != kPrimaryDisplayId)
+ display->set_always_2d(true);
+ new_displays_.emplace_back(display);
+ return display;
+}
+
+base::unique_fd ShellView::OnFrame(std::unique_ptr<HwcCallback::Frame> frame) {
+ std::unique_lock<std::mutex> l(display_frame_mutex_);
+ DisplayView* display = FindOrCreateDisplay(frame->display_id());
+
+ if (frame->removed()) {
+ removed_displays_.push_back(display);
+ return base::unique_fd();
+ }
+
+ bool showing = false;
+
+ // This is a temporary fix for now. These APIs will be changed when everything
+ // is moved into vrcore.
+ // Do this on demand in case vr_flinger crashed and we are reconnecting.
+ if (!display_client_.get()) {
+ int error = 0;
+ display_client_ = DisplayClient::Create(&error);
+
+ if (error) {
+ ALOGE("Could not connect to display service : %s(%d)", strerror(error),
+ error);
+ return base::unique_fd();
+ }
+ }
+
+ // TODO(achaulk): change when moved into vrcore.
+ bool vr_running = display_client_->IsVrAppRunning();
+
+ base::unique_fd fd(
+ display->OnFrame(std::move(frame), debug_mode_, vr_running, &showing));
+
+ if (showing)
+ QueueTask(MainThreadTask::Show);
+
+ return fd;
+}
+
+void ShellView::DrawEye(EyeType eye, const mat4& perspective,
+ const mat4& eye_matrix, const mat4& head_matrix) {
+ if (should_recenter_ && !displays_.empty()) {
+ // Position the quad horizontally aligned in the direction the user
+ // is facing, effectively taking out head roll.
+ displays_[0]->Recenter(GetHorizontallyAlignedMatrixFromPose(last_pose_));
+ }
+
+ for (auto& display : displays_) {
+ if (display->visible()) {
+ display->DrawEye(eye, perspective, eye_matrix, head_matrix, fade_value_);
+ }
+ }
+
+ // TODO(alexst): Replicate controller rendering from VR Home.
+ // Current approach in the function below is a quick visualization.
+ DrawController(perspective, eye_matrix, head_matrix);
+
+ DrawReticle(perspective, eye_matrix, head_matrix);
+}
+
+void ShellView::OnVisibilityChanged(bool visible) {
+ should_recenter_ = visible;
+ Application::OnVisibilityChanged(visible);
+}
+
+bool ShellView::OnClick(bool down) {
+ if (down) {
+ if (!is_touching_ && active_display_ && active_display_->allow_input()) {
+ is_touching_ = true;
+ }
+ } else {
+ is_touching_ = false;
+ }
+ Touch();
+ return true;
+}
+
+void ShellView::DrawReticle(const mat4& perspective, const mat4& eye_matrix,
+ const mat4& head_matrix) {
+ reticle_->Hide();
+
+ vec3 pointer_location = last_pose_.GetPosition();
+ quat view_quaternion = last_pose_.GetRotation();
+
+ if (shmem_controller_active_) {
+ view_quaternion = controller_orientation_;
+ vec4 controller_location = controller_translate_ * vec4(0, 0, 0, 1);
+ pointer_location = vec3(controller_location.x(), controller_location.y(),
+ controller_location.z());
+
+ if (shmem_controller_active_) {
+ uint64_t buttons = shmem_controller_buttons_;
+ shmem_controller_buttons_ = 0;
+ while (buttons) {
+ switch (buttons & 0xF) {
+ case 0x1:
+ OnClick(false);
+ break;
+ case 0x3:
+ OnTouchpadButton(false, AMOTION_EVENT_BUTTON_BACK);
+ break;
+ case 0x4:
+ should_recenter_ = true;
+ break;
+ case 0x9:
+ OnClick(true);
+ break;
+ case 0xB:
+ OnTouchpadButton(true, AMOTION_EVENT_BUTTON_BACK);
+ break;
+ default:
+ break;
+ }
+ buttons >>= 4;
+ }
+ }
+ }
+
+ vec3 hit_location;
+ active_display_ =
+ FindActiveDisplay(pointer_location, view_quaternion, &hit_location);
+
+ if (active_display_) {
+ reticle_->ShowAt(
+ Eigen::Translation3f(hit_location) * view_quaternion.matrix(),
+ active_display_->allow_input() ? vec3(1, 0, 0) : vec3(0, 0, 0));
+ Touch();
+ }
+
+ reticle_->Draw(perspective, eye_matrix, head_matrix);
+}
+
+DisplayView* ShellView::FindActiveDisplay(const vec3& position,
+ const quat& quaternion,
+ vec3* hit_location) {
+ vec3 direction = vec3(quaternion * vec3(0, 0, -1));
+ vec3 temp_hit;
+
+ DisplayView* best_display = nullptr;
+ vec3 best_hit;
+
+ auto is_better = [&best_hit, &position](DisplayView*, const vec3& hit) {
+ return (hit - position).squaredNorm() < (best_hit - position).squaredNorm();
+ };
+
+ for (auto& display : displays_) {
+ if (display->UpdateHitInfo(position, direction, &temp_hit)) {
+ if (!best_display || is_better(display.get(), temp_hit)) {
+ best_display = display.get();
+ best_hit = temp_hit;
+ }
+ }
+ }
+
+ if (best_display)
+ *hit_location = best_hit;
+ return best_display;
+}
+
+void ShellView::DrawController(const mat4& perspective, const mat4& eye_matrix,
+ const mat4& head_matrix) {
+ if (!shmem_controller_active_)
+ return;
+
+ controller_program_->Use();
+ mat4 mvp = perspective * eye_matrix * head_matrix;
+
+ GLint view_projection_location = glGetUniformLocation(
+ controller_program_->GetProgram(), "uViewProjection");
+ glUniformMatrix4fv(view_projection_location, 1, 0, mvp.data());
+
+ quat view_quaternion = controller_orientation_;
+ view_quaternion.toRotationMatrix();
+
+ vec3 world_pos = last_pose_.GetPosition() + controller_position_;
+
+ controller_translate_ =
+ Eigen::Translation3f(world_pos.x(), world_pos.y(), world_pos.z());
+
+ mat4 transform = controller_translate_ * view_quaternion *
+ mat4(Eigen::Scaling<float>(1, 1, 3.0));
+ GLint transform_location =
+ glGetUniformLocation(controller_program_->GetProgram(), "uTransform");
+ glUniformMatrix4fv(transform_location, 1, 0, transform.data());
+
+ controller_mesh_->Draw();
+}
+
+void ShellView::Touch() {
+ if (!virtual_touchpad_) {
+ ALOGE("missing virtual touchpad");
+ return;
+ }
+
+ if (!active_display_)
+ return;
+
+ const vec2& hit_location = active_display_->hit_location();
+ const vec2 size = active_display_->size();
+
+ float x = hit_location.x() / size.x();
+ float y = hit_location.y() / size.y();
+
+ // Device is portrait, but in landscape when in VR.
+ // Rotate touch input appropriately.
+ const android::status_t status = dvrVirtualTouchpadTouch(
+ virtual_touchpad_.get(), active_display_->touchpad_id(),
+ x, y, is_touching_ ? 1.0f : 0.0f);
+ if (status != OK) {
+ ALOGE("touch failed: %d", status);
+ }
+}
+
+bool ShellView::OnTouchpadButton(bool down, int button) {
+ int buttons = touchpad_buttons_;
+ if (down) {
+ if (active_display_ && active_display_->allow_input()) {
+ buttons |= button;
+ }
+ } else {
+ buttons &= ~button;
+ }
+ if (buttons == touchpad_buttons_) {
+ return true;
+ }
+ touchpad_buttons_ = buttons;
+ if (!virtual_touchpad_) {
+ ALOGE("missing virtual touchpad");
+ return false;
+ }
+
+ if (!active_display_)
+ return true;
+
+ const android::status_t status = dvrVirtualTouchpadButtonState(
+ virtual_touchpad_.get(), active_display_->touchpad_id(),
+ touchpad_buttons_);
+ if (status != OK) {
+ ALOGE("touchpad button failed: %d %d", touchpad_buttons_, status);
+ }
+ return true;
+}
+
+} // namespace dvr
+} // namespace android
--- /dev/null
+#ifndef VR_WINDOW_MANAGER_SHELL_VIEW_H_
+#define VR_WINDOW_MANAGER_SHELL_VIEW_H_
+
+#include <dvr/virtual_touchpad_client.h>
+#include <private/dvr/display_client.h>
+#include <private/dvr/graphics/mesh.h>
+#include <private/dvr/graphics/shader_program.h>
+
+#include <deque>
+
+#include "application.h"
+#include "display_view.h"
+#include "reticle.h"
+#include "shell_view_binder_interface.h"
+#include "surface_flinger_view.h"
+
+namespace android {
+namespace dvr {
+
+class ShellView : public Application,
+ public android::dvr::ShellViewBinderInterface,
+ public HwcCallback::Client {
+ public:
+ ShellView();
+ virtual ~ShellView();
+
+ int Initialize() override;
+
+ int AllocateResources() override;
+ void DeallocateResources() override;
+
+ // ShellViewBinderInterface:
+ void EnableDebug(bool debug) override;
+ void VrMode(bool mode) override;
+ void dumpInternal(String8& result) override;
+ void Set2DMode(bool mode) override;
+ void SetRotation(int angle) override;
+
+
+ protected:
+ void DrawEye(EyeType eye, const mat4& perspective, const mat4& eye_matrix,
+ const mat4& head_matrix) override;
+ void OnDrawFrame() override;
+ void OnEndFrame() override;
+ void OnVisibilityChanged(bool visible) override;
+
+ void DrawReticle(const mat4& perspective, const mat4& eye_matrix,
+ const mat4& head_matrix);
+ void DrawController(const mat4& perspective, const mat4& eye_matrix,
+ const mat4& head_matrix);
+
+ void Touch();
+ bool OnTouchpadButton(bool down, int button);
+
+ bool OnClick(bool down);
+
+ DisplayView* FindActiveDisplay(const vec3& position, const quat& quaternion,
+ vec3* hit_location);
+
+ // HwcCallback::Client:
+ base::unique_fd OnFrame(std::unique_ptr<HwcCallback::Frame> frame) override;
+ DisplayView* FindOrCreateDisplay(uint32_t id);
+
+ std::unique_ptr<ShaderProgram> program_;
+ std::unique_ptr<ShaderProgram> overlay_program_;
+ std::unique_ptr<ShaderProgram> controller_program_;
+
+ std::unique_ptr<SurfaceFlingerView> surface_flinger_view_;
+ std::unique_ptr<Reticle> reticle_;
+
+ std::unique_ptr<DisplayClient> display_client_;
+
+ struct DvrVirtualTouchpadDeleter {
+ void operator()(DvrVirtualTouchpad* p) {
+ dvrVirtualTouchpadDetach(p);
+ dvrVirtualTouchpadDestroy(p);
+ }
+ };
+ std::unique_ptr<DvrVirtualTouchpad, DvrVirtualTouchpadDeleter>
+ virtual_touchpad_;
+
+ std::unique_ptr<Mesh<vec3, vec3, vec2>> controller_mesh_;
+
+ bool is_touching_ = false;
+ int touchpad_buttons_ = 0;
+
+ std::mutex display_frame_mutex_;
+
+ std::vector<std::unique_ptr<DisplayView>> displays_;
+ std::vector<std::unique_ptr<DisplayView>> new_displays_;
+ std::vector<DisplayView*> removed_displays_;
+ DisplayView* active_display_ = nullptr;
+
+ mat4 controller_translate_;
+
+ ShellView(const ShellView&) = delete;
+ void operator=(const ShellView&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // VR_WINDOW_MANAGER_SHELL_VIEW_H_
--- /dev/null
+#ifndef VR_WINDOW_MANAGER_SHELL_VIEWBINDER_INTERFACE_H_
+#define VR_WINDOW_MANAGER_SHELL_VIEWBINDER_INTERFACE_H_
+
+namespace android {
+namespace dvr {
+
+class ShellViewBinderInterface {
+ public:
+ ShellViewBinderInterface() {};
+ virtual ~ShellViewBinderInterface() {};
+
+ virtual void EnableDebug(bool debug) = 0;
+ virtual void VrMode(bool mode) = 0;
+ virtual void dumpInternal(String8& result) = 0;
+ virtual void Set2DMode(bool mode) = 0;
+ virtual void SetRotation(int angle) = 0;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // VR_WINDOW_MANAGER_SHELL_VIEWBINDER_INTERFACE_H_
--- /dev/null
+#include "surface_flinger_view.h"
+
+#include <android/dvr/IVrComposer.h>
+#include <binder/IServiceManager.h>
+#include <private/dvr/native_buffer.h>
+
+#include "hwc_callback.h"
+#include "texture.h"
+
+namespace android {
+namespace dvr {
+
+SurfaceFlingerView::SurfaceFlingerView() {}
+
+SurfaceFlingerView::~SurfaceFlingerView() {}
+
+bool SurfaceFlingerView::Initialize(HwcCallback::Client *client) {
+ sp<IServiceManager> sm(defaultServiceManager());
+ vr_composer_ = interface_cast<IVrComposer>(
+ sm->getService(IVrComposer::SERVICE_NAME()));
+
+ String8 service_name(IVrComposer::SERVICE_NAME().string());
+ if (!vr_composer_.get()) {
+ ALOGE("Faild to connect to %s", service_name.c_str());
+ return false;
+ }
+
+ composer_callback_ = new HwcCallback(client);
+ binder::Status status = vr_composer_->registerObserver(composer_callback_);
+ if (!status.isOk()) {
+ ALOGE("Failed to register observer with %s", service_name.c_str());
+ return false;
+ }
+
+ return true;
+}
+
+bool SurfaceFlingerView::GetTextures(const HwcCallback::Frame& frame,
+ std::vector<TextureLayer>* texture_layers,
+ TextureLayer* ime_layer,
+ bool debug, bool skip_first_layer) const {
+ auto& layers = frame.layers();
+ texture_layers->clear();
+
+ size_t start = 0;
+ // Skip the second layer if it is from the VR app.
+ if (!debug && skip_first_layer) {
+ start = 2;
+ }
+
+ for (size_t i = start; i < layers.size(); ++i) {
+ if (!debug && layers[i].should_skip_layer())
+ continue;
+
+ std::unique_ptr<Texture> texture(new Texture());
+ if (!texture->Initialize(layers[i].buffer->getNativeBuffer())) {
+ ALOGE("Failed to create texture");
+ texture_layers->clear();
+ return false;
+ }
+
+ TextureLayer texture_layer = {
+ std::move(texture), layers[i].crop, layers[i].display_frame,
+ layers[i].blending, layers[i].alpha,
+ };
+ if (debug && layers[i].type == HwcCallback::HwcLayer::kInputMethod) {
+ *ime_layer = std::move(texture_layer);
+ } else {
+ texture_layers->emplace_back(std::move(texture_layer));
+ }
+ }
+
+ return true;
+}
+
+} // namespace dvr
+} // namespace android
--- /dev/null
+#ifndef APPLICATIONS_EXPERIMENTS_SURFACE_FLINGER_DEMO_SURFACE_FLINGER_VIEW_H_
+#define APPLICATIONS_EXPERIMENTS_SURFACE_FLINGER_DEMO_SURFACE_FLINGER_VIEW_H_
+
+#include <memory>
+
+#include "hwc_callback.h"
+
+namespace android {
+namespace dvr {
+
+class IDisplay;
+class IVrComposer;
+class Texture;
+
+struct TextureLayer {
+ std::unique_ptr<Texture> texture;
+ Rectf crop;
+ Recti display_frame;
+ int32_t blending;
+ float alpha;
+};
+
+class SurfaceFlingerView {
+ public:
+ SurfaceFlingerView();
+ ~SurfaceFlingerView();
+
+ bool Initialize(HwcCallback::Client *client);
+
+ bool GetTextures(const HwcCallback::Frame& layers,
+ std::vector<TextureLayer>* texture_layers,
+ TextureLayer* ime_layer, bool debug,
+ bool skip_first_layer) const;
+
+ private:
+ sp<IVrComposer> vr_composer_;
+ sp<HwcCallback> composer_callback_;
+
+ SurfaceFlingerView(const SurfaceFlingerView&) = delete;
+ void operator=(const SurfaceFlingerView&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // APPLICATIONS_EXPERIMENTS_SURFACE_FLINGER_DEMO_SURFACE_FLINGER_VIEW_H_
--- /dev/null
+#include "texture.h"
+
+#include <GLES/glext.h>
+#include <log/log.h>
+#include <system/window.h>
+
+namespace android {
+namespace dvr {
+
+Texture::Texture() {}
+
+Texture::~Texture() {
+ EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (id_)
+ glDeleteTextures(1, &id_);
+ if (image_)
+ eglDestroyImageKHR(display, image_);
+}
+
+bool Texture::Initialize(ANativeWindowBuffer* buffer) {
+ width_ = buffer->width;
+ height_ = buffer->height;
+
+ EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ image_ = eglCreateImageKHR(display, EGL_NO_CONTEXT,
+ EGL_NATIVE_BUFFER_ANDROID, buffer, nullptr);
+ if (!image_) {
+ ALOGE("Failed to create eglImage");
+ return false;
+ }
+
+ glGenTextures(1, &id_);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, id_);
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image_);
+
+ return true;
+}
+
+} // namespace android
+} // namespace dvr
--- /dev/null
+#ifndef VR_WINDOW_MANAGER_TEXTURE_H_
+#define VR_WINDOW_MANAGER_TEXTURE_H_
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES/gl.h>
+
+struct ANativeWindowBuffer;
+
+namespace android {
+namespace dvr {
+
+class Texture {
+ public:
+ explicit Texture();
+ ~Texture();
+
+ bool Initialize(ANativeWindowBuffer* buffer);
+
+ GLuint id() const { return id_; }
+ int width() const { return width_; }
+ int height() const { return height_; }
+
+ private:
+ EGLImageKHR image_ = nullptr;
+ GLuint id_ = 0;
+ int width_ = 0;
+ int height_ = 0;
+
+ Texture(const Texture&) = delete;
+ void operator=(const Texture&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // VR_WINDOW_MANAGER_TEXTURE_H_
--- /dev/null
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <hwbinder/IPCThreadState.h>
+#include <impl/vr_hwc.h>
+
+#include "shell_view.h"
+#include "vr_window_manager_binder.h"
+
+using namespace android;
+using namespace android::dvr;
+
+int main(int /* argc */, char** /* argv */) {
+ android::ProcessState::self()->startThreadPool();
+
+ // ShellView needs to be created after vr_hwcomposer.
+ android::dvr::ShellView app;
+ const int app_status = app.Initialize();
+ LOG_ALWAYS_FATAL_IF(app_status != 0, "failed to initialize: %d", app_status);
+
+ // Create vr_wm_binder.
+ android::sp<android::service::vr::VrWindowManagerBinder> vr_wm_binder =
+ new android::service::vr::VrWindowManagerBinder(app);
+ const int status = vr_wm_binder->Initialize();
+ LOG_ALWAYS_FATAL_IF(status != 0, "initialization failed: %d", status);
+
+ android::sp<android::IServiceManager> sm(android::defaultServiceManager());
+ const android::status_t vr_wm_binder_status = sm->addService(
+ android::service::vr::VrWindowManagerBinder::SERVICE_NAME(),
+ vr_wm_binder, false /*allowIsolated*/);
+ LOG_ALWAYS_FATAL_IF(vr_wm_binder_status != android::OK,
+ "vr_wm_binder service not added: %d",
+ static_cast<int>(vr_wm_binder_status));
+
+ app.SetControllerDataProvider(vr_wm_binder.get());
+
+ android::hardware::ProcessState::self()->startThreadPool();
+
+ while (true) {
+ app.DrawFrame();
+ }
+
+ android::hardware::IPCThreadState::self()->joinThreadPool();
+ android::IPCThreadState::self()->joinThreadPool();
+
+ return 0;
+}
--- /dev/null
+#include "vr_window_manager_binder.h"
+
+#include <inttypes.h>
+#include <sys/mman.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/PermissionCache.h>
+#include <binder/Status.h>
+#include <cutils/log.h>
+#include <private/android_filesystem_config.h>
+#include <utils/Errors.h>
+
+namespace android {
+namespace service {
+namespace vr {
+
+namespace {
+const String16 kDumpPermission("android.permission.DUMP");
+const String16 kSendMeControllerInputPermission(
+ "android.permission.RESTRICTED_VR_ACCESS");
+} // anonymous namespace
+
+constexpr size_t AshmemControllerDataProvider::kRegionLength;
+
+status_t AshmemControllerDataProvider::Connect(const int in_fd) {
+ if (in_fd < 0) {
+ return BAD_VALUE;
+ }
+ if (fd_.get() >= 0) {
+ // The VrCore is dead. Long live the VrCore.
+ Disconnect();
+ }
+ void* const shared_region =
+ ::mmap(nullptr, kRegionLength, PROT_READ, MAP_SHARED, in_fd, 0);
+ if (shared_region == MAP_FAILED) {
+ shared_region_ = nullptr;
+ return NO_MEMORY;
+ }
+
+ errno = 0;
+ const int fd = ::fcntl(in_fd, F_DUPFD_CLOEXEC, 0);
+ if (fd < 0) {
+ ::munmap(shared_region, kRegionLength);
+ return -errno;
+ }
+ fd_.reset(fd);
+ ALOGI("controller connected %d -> %d @ %p", in_fd, fd, shared_region);
+
+ std::lock_guard<std::mutex> guard(mutex_);
+ shared_region_ = shared_region;
+ return OK;
+}
+
+status_t AshmemControllerDataProvider::Disconnect() {
+ if (shared_region_ == nullptr || fd_.get() < 0) {
+ return INVALID_OPERATION;
+ }
+ std::lock_guard<std::mutex> guard(mutex_);
+ ::munmap(shared_region_, kRegionLength);
+ shared_region_ = nullptr;
+ fd_.reset();
+ ALOGI("controller disconnected");
+ return OK;
+}
+
+const void* AshmemControllerDataProvider::LockControllerData() {
+ mutex_.lock();
+ if (!shared_region_) {
+ mutex_.unlock();
+ return nullptr;
+ }
+ return shared_region_;
+}
+
+void AshmemControllerDataProvider::UnlockControllerData() { mutex_.unlock(); }
+
+void AshmemControllerDataProvider::dumpInternal(String8& result) {
+ result.appendFormat("[controller]\nfd = %d\n", fd_.get());
+ if (shared_region_) {
+ int32_t* p = reinterpret_cast<int32_t*>(shared_region_);
+ result.appendFormat("header = ");
+ for (int i = 0; i < 8; ++i) {
+ result.appendFormat("%c 0x%08" PRIX32, i ? ',' : '[', p[i]);
+ }
+ result.appendFormat(" ]\n\n");
+ }
+}
+
+int VrWindowManagerBinder::Initialize() { return 0; }
+
+binder::Status VrWindowManagerBinder::connectController(
+ const ::android::base::unique_fd& in_fd) {
+ // TODO(kpschoedel): check permission
+#if 0
+ int32_t pid, uid;
+ if (!PermissionCache::checkCallingPermission(kSendMeControllerInputPermission,
+ &pid, &uid)) {
+ ALOGE("permission denied to pid=%" PRId32 " uid=%" PRId32, pid, uid);
+ return binder::Status::fromStatusT(PERMISSION_DENIED);
+ }
+#endif
+ return binder::Status::fromStatusT(Connect(in_fd.get()));
+}
+
+binder::Status VrWindowManagerBinder::disconnectController() {
+ // TODO(kpschoedel): check permission
+#if 0
+ int32_t pid, uid;
+ if (!PermissionCache::checkCallingPermission(kSendMeControllerInputPermission,
+ &pid, &uid)) {
+ ALOGE("permission denied to pid=%" PRId32 " uid=%" PRId32, pid, uid);
+ return binder::Status::fromStatusT(PERMISSION_DENIED);
+ }
+#endif
+ return binder::Status::fromStatusT(Disconnect());
+}
+
+binder::Status VrWindowManagerBinder::enterVrMode() {
+ // TODO(kpschoedel): check permission
+ app_.VrMode(true);
+ return binder::Status::ok();
+}
+
+binder::Status VrWindowManagerBinder::exitVrMode() {
+ // TODO(kpschoedel): check permission
+ app_.VrMode(false);
+ return binder::Status::ok();
+}
+
+binder::Status VrWindowManagerBinder::setDebugMode(int32_t mode) {
+ // TODO(kpschoedel): check permission
+ app_.EnableDebug(static_cast<bool>(mode));
+ return binder::Status::ok();
+}
+
+binder::Status VrWindowManagerBinder::set2DMode(int32_t mode) {
+ app_.Set2DMode(static_cast<bool>(mode));
+ return binder::Status::ok();
+}
+
+binder::Status VrWindowManagerBinder::setRotation(int32_t angle) {
+ app_.SetRotation(angle);
+ return binder::Status::ok();
+}
+
+status_t VrWindowManagerBinder::dump(
+ int fd, const Vector<String16>& args [[gnu::unused]]) {
+ String8 result;
+ const android::IPCThreadState* ipc = android::IPCThreadState::self();
+ const pid_t pid = ipc->getCallingPid();
+ const uid_t uid = ipc->getCallingUid();
+ if ((uid != AID_SHELL) &&
+ !PermissionCache::checkPermission(kDumpPermission, pid, uid)) {
+ result.appendFormat("Permission denial: can't dump " LOG_TAG
+ " from pid=%d, uid=%d\n",
+ pid, uid);
+ } else {
+ app_.dumpInternal(result);
+ AshmemControllerDataProvider::dumpInternal(result);
+ }
+ write(fd, result.string(), result.size());
+ return OK;
+}
+
+} // namespace vr
+} // namespace service
+} // namespace android
--- /dev/null
+#ifndef VR_WINDOW_MANAGER_VR_WINDOW_MANAGER_BINDER_H_
+#define VR_WINDOW_MANAGER_VR_WINDOW_MANAGER_BINDER_H_
+
+#include <android/service/vr/BnVrWindowManager.h>
+
+#include <mutex>
+
+#include "controller_data_provider.h"
+#include "shell_view_binder_interface.h"
+
+namespace android {
+namespace service {
+namespace vr {
+
+class AshmemControllerDataProvider : public dvr::ControllerDataProvider {
+ public:
+ AshmemControllerDataProvider() {}
+ virtual ~AshmemControllerDataProvider() {}
+
+ status_t Connect(int fd);
+ status_t Disconnect();
+
+ // ControllerDataProvider:
+ const void* LockControllerData() override;
+ void UnlockControllerData() override;
+
+ protected:
+ void dumpInternal(String8& result);
+
+ private:
+ static constexpr size_t kRegionLength = 8192; // TODO(kpschoedel)
+ ::android::base::unique_fd fd_;
+
+ // Mutex for guarding shared_region_.
+ std::mutex mutex_;
+ void* shared_region_ = nullptr;
+
+ AshmemControllerDataProvider(const AshmemControllerDataProvider&) = delete;
+ void operator=(const AshmemControllerDataProvider&) = delete;
+};
+
+class VrWindowManagerBinder : public BnVrWindowManager,
+ public AshmemControllerDataProvider {
+ public:
+ VrWindowManagerBinder(android::dvr::ShellViewBinderInterface& app)
+ : app_(app) {}
+ virtual ~VrWindowManagerBinder() {}
+
+ // Must be called before clients can connect.
+ // Returns 0 if initialization is successful.
+ int Initialize();
+ static char const* getServiceName() { return "vr_window_manager"; }
+
+ protected:
+ // Implements IVrWindowManagerBinder.
+ ::android::binder::Status connectController(
+ const ::android::base::unique_fd& fd) override;
+ ::android::binder::Status disconnectController() override;
+ ::android::binder::Status enterVrMode() override;
+ ::android::binder::Status exitVrMode() override;
+ ::android::binder::Status setDebugMode(int32_t mode) override;
+ ::android::binder::Status set2DMode(int32_t mode) override;
+ ::android::binder::Status setRotation(int32_t angle) override;
+
+ // Implements BBinder::dump().
+ status_t dump(int fd, const Vector<String16>& args) override;
+
+ private:
+ android::dvr::ShellViewBinderInterface& app_;
+
+ VrWindowManagerBinder(const VrWindowManagerBinder&) = delete;
+ void operator=(const VrWindowManagerBinder&) = delete;
+};
+
+} // namespace vr
+} // namespace service
+} // namespace android
+
+#endif // VR_WINDOW_MANAGER_VR_WINDOW_MANAGER_BINDER_H_
--- /dev/null
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <cutils/log.h>
+
+#include "vr_window_manager_binder.h"
+
+int main() {
+ ALOGI("Starting");
+ android::service::vr::VrWindowManagerBinder service;
+ const int status = service.Initialize();
+ LOG_ALWAYS_FATAL_IF(status != 0, "initialization failed: %d", status);
+
+ signal(SIGPIPE, SIG_IGN);
+ android::sp<android::ProcessState> ps(android::ProcessState::self());
+ ps->setThreadPoolMaxThreadCount(4);
+ ps->startThreadPool();
+ ps->giveThreadPoolName();
+
+ android::sp<android::IServiceManager> sm(android::defaultServiceManager());
+ const android::status_t service_status = sm->addService(
+ android::service::vr::VrWindowManagerBinder::SERVICE_NAME(), &service,
+ false /*allowIsolated*/);
+ LOG_ALWAYS_FATAL_IF(service_status != android::OK, "service not added: %d",
+ static_cast<int>(service_status));
+
+ android::IPCThreadState::self()->joinThreadPool();
+ return 0;
+}
--- /dev/null
+service vr_wm /system/bin/vr_wm
+ class core
+ user system
+ group system graphics input
+ writepid /dev/cpuset/system/tasks
--- /dev/null
+#include <android/service/vr/BpVrWindowManager.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <inttypes.h>
+
+void usage() { fprintf(stderr, "usage: vr_wm_ctl [enter|exit|debug N]\n"); }
+
+int report(const android::binder::Status& status) {
+ if (status.isOk()) {
+ fprintf(stderr, "ok\n");
+ return 0;
+ }
+ fprintf(stderr, "failed (%" PRId32 ") %s\n", status.exceptionCode(),
+ status.exceptionMessage().string());
+ return (int)status.exceptionCode();
+}
+
+int main(int argc, char* argv[]) {
+ android::sp<android::IServiceManager> sm(android::defaultServiceManager());
+ if (sm == nullptr) {
+ fprintf(stderr, "service manager not found\n");
+ exit(1);
+ }
+
+ android::sp<android::service::vr::IVrWindowManager> vrwm =
+ android::interface_cast<android::service::vr::IVrWindowManager>(
+ sm->getService(
+ android::service::vr::IVrWindowManager::SERVICE_NAME()));
+ if (vrwm == nullptr) {
+ fprintf(stderr, "service not found\n");
+ exit(1);
+ }
+
+ android::binder::Status status;
+ if ((argc == 2) && (strcmp(argv[1], "enter") == 0)) {
+ exit(report(vrwm->enterVrMode()));
+ } else if ((argc == 2) && (strcmp(argv[1], "exit") == 0)) {
+ exit(report(vrwm->exitVrMode()));
+ } else if ((argc == 3) && (strcmp(argv[1], "debug") == 0)) {
+ exit(report(vrwm->setDebugMode(atoi(argv[2]))));
+ } else if ((argc == 3) && (strcmp(argv[1], "2d") == 0)) {
+ exit(report(vrwm->set2DMode(atoi(argv[2]))));
+ } else if ((argc == 3) && (strcmp(argv[1], "rotate") == 0)) {
+ exit(report(vrwm->setRotation(atoi(argv[2]))));
+ } else {
+ usage();
+ exit(2);
+ }
+
+ return 0;
+}