OSDN Git Service

Revert "Remove vr_wm service"
authorDaniel Nicoara <dnicoara@google.com>
Fri, 5 May 2017 15:08:12 +0000 (15:08 +0000)
committerDaniel Nicoara <dnicoara@google.com>
Fri, 5 May 2017 15:08:12 +0000 (15:08 +0000)
This reverts commit efd3e0873e24f25e855bb64fae8485fc04690d56.

Reason for revert: IVrWindowManager.aidl is still used in frameworks/base

Change-Id: Id52fb8cf482ea5ab141ebf6b304c12b18a9d8070

29 files changed:
services/vr/vr_window_manager/Android.bp [new file with mode: 0644]
services/vr/vr_window_manager/aidl/android/service/vr/IVrWindowManager.aidl [new file with mode: 0644]
services/vr/vr_window_manager/application.cpp [new file with mode: 0644]
services/vr/vr_window_manager/application.h [new file with mode: 0644]
services/vr/vr_window_manager/controller_data_provider.h [new file with mode: 0644]
services/vr/vr_window_manager/controller_mesh.cpp [new file with mode: 0644]
services/vr/vr_window_manager/controller_mesh.h [new file with mode: 0644]
services/vr/vr_window_manager/display_view.cpp [new file with mode: 0644]
services/vr/vr_window_manager/display_view.h [new file with mode: 0644]
services/vr/vr_window_manager/elbow_model.cpp [new file with mode: 0644]
services/vr/vr_window_manager/elbow_model.h [new file with mode: 0644]
services/vr/vr_window_manager/hwc_callback.cpp [new file with mode: 0644]
services/vr/vr_window_manager/hwc_callback.h [new file with mode: 0644]
services/vr/vr_window_manager/proguard.flags [new file with mode: 0644]
services/vr/vr_window_manager/reticle.cpp [new file with mode: 0644]
services/vr/vr_window_manager/reticle.h [new file with mode: 0644]
services/vr/vr_window_manager/shell_view.cpp [new file with mode: 0644]
services/vr/vr_window_manager/shell_view.h [new file with mode: 0644]
services/vr/vr_window_manager/shell_view_binder_interface.h [new file with mode: 0644]
services/vr/vr_window_manager/surface_flinger_view.cpp [new file with mode: 0644]
services/vr/vr_window_manager/surface_flinger_view.h [new file with mode: 0644]
services/vr/vr_window_manager/texture.cpp [new file with mode: 0644]
services/vr/vr_window_manager/texture.h [new file with mode: 0644]
services/vr/vr_window_manager/vr_window_manager.cpp [new file with mode: 0644]
services/vr/vr_window_manager/vr_window_manager_binder.cpp [new file with mode: 0644]
services/vr/vr_window_manager/vr_window_manager_binder.h [new file with mode: 0644]
services/vr/vr_window_manager/vr_window_manager_binder_test.cpp [new file with mode: 0644]
services/vr/vr_window_manager/vr_wm.rc [new file with mode: 0644]
services/vr/vr_window_manager/vr_wm_ctl.cpp [new file with mode: 0644]

diff --git a/services/vr/vr_window_manager/Android.bp b/services/vr/vr_window_manager/Android.bp
new file mode 100644 (file)
index 0000000..d7ddba1
--- /dev/null
@@ -0,0 +1,102 @@
+// 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"],
+}
diff --git a/services/vr/vr_window_manager/aidl/android/service/vr/IVrWindowManager.aidl b/services/vr/vr_window_manager/aidl/android/service/vr/IVrWindowManager.aidl
new file mode 100644 (file)
index 0000000..b16049f
--- /dev/null
@@ -0,0 +1,30 @@
+/**
+ * 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;
+}
+
diff --git a/services/vr/vr_window_manager/application.cpp b/services/vr/vr_window_manager/application.cpp
new file mode 100644 (file)
index 0000000..8b4460a
--- /dev/null
@@ -0,0 +1,330 @@
+#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
diff --git a/services/vr/vr_window_manager/application.h b/services/vr/vr_window_manager/application.h
new file mode 100644 (file)
index 0000000..ed99157
--- /dev/null
@@ -0,0 +1,111 @@
+#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_
diff --git a/services/vr/vr_window_manager/controller_data_provider.h b/services/vr/vr_window_manager/controller_data_provider.h
new file mode 100644 (file)
index 0000000..bc1450c
--- /dev/null
@@ -0,0 +1,19 @@
+#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
diff --git a/services/vr/vr_window_manager/controller_mesh.cpp b/services/vr/vr_window_manager/controller_mesh.cpp
new file mode 100644 (file)
index 0000000..c6095b1
--- /dev/null
@@ -0,0 +1,75 @@
+#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
diff --git a/services/vr/vr_window_manager/controller_mesh.h b/services/vr/vr_window_manager/controller_mesh.h
new file mode 100644 (file)
index 0000000..88872c7
--- /dev/null
@@ -0,0 +1,13 @@
+#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_
diff --git a/services/vr/vr_window_manager/display_view.cpp b/services/vr/vr_window_manager/display_view.cpp
new file mode 100644 (file)
index 0000000..88768a0
--- /dev/null
@@ -0,0 +1,458 @@
+#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
diff --git a/services/vr/vr_window_manager/display_view.h b/services/vr/vr_window_manager/display_view.h
new file mode 100644 (file)
index 0000000..ad624c6
--- /dev/null
@@ -0,0 +1,119 @@
+#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_
diff --git a/services/vr/vr_window_manager/elbow_model.cpp b/services/vr/vr_window_manager/elbow_model.cpp
new file mode 100644 (file)
index 0000000..9543f17
--- /dev/null
@@ -0,0 +1,134 @@
+#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
diff --git a/services/vr/vr_window_manager/elbow_model.h b/services/vr/vr_window_manager/elbow_model.h
new file mode 100644 (file)
index 0000000..a6d5ca9
--- /dev/null
@@ -0,0 +1,45 @@
+#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_
diff --git a/services/vr/vr_window_manager/hwc_callback.cpp b/services/vr/vr_window_manager/hwc_callback.cpp
new file mode 100644 (file)
index 0000000..28e97ff
--- /dev/null
@@ -0,0 +1,96 @@
+#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
diff --git a/services/vr/vr_window_manager/hwc_callback.h b/services/vr/vr_window_manager/hwc_callback.h
new file mode 100644 (file)
index 0000000..259c4ac
--- /dev/null
@@ -0,0 +1,132 @@
+#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_
diff --git a/services/vr/vr_window_manager/proguard.flags b/services/vr/vr_window_manager/proguard.flags
new file mode 100644 (file)
index 0000000..7683d6e
--- /dev/null
@@ -0,0 +1,22 @@
+# 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 *;
+}
diff --git a/services/vr/vr_window_manager/reticle.cpp b/services/vr/vr_window_manager/reticle.cpp
new file mode 100644 (file)
index 0000000..cbd0caf
--- /dev/null
@@ -0,0 +1,100 @@
+#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
diff --git a/services/vr/vr_window_manager/reticle.h b/services/vr/vr_window_manager/reticle.h
new file mode 100644 (file)
index 0000000..d8522aa
--- /dev/null
@@ -0,0 +1,35 @@
+#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_
diff --git a/services/vr/vr_window_manager/shell_view.cpp b/services/vr/vr_window_manager/shell_view.cpp
new file mode 100644 (file)
index 0000000..2b53cd6
--- /dev/null
@@ -0,0 +1,529 @@
+#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
diff --git a/services/vr/vr_window_manager/shell_view.h b/services/vr/vr_window_manager/shell_view.h
new file mode 100644 (file)
index 0000000..d90e833
--- /dev/null
@@ -0,0 +1,103 @@
+#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_
diff --git a/services/vr/vr_window_manager/shell_view_binder_interface.h b/services/vr/vr_window_manager/shell_view_binder_interface.h
new file mode 100644 (file)
index 0000000..c66e4a1
--- /dev/null
@@ -0,0 +1,22 @@
+#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_
diff --git a/services/vr/vr_window_manager/surface_flinger_view.cpp b/services/vr/vr_window_manager/surface_flinger_view.cpp
new file mode 100644 (file)
index 0000000..b41de03
--- /dev/null
@@ -0,0 +1,77 @@
+#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
diff --git a/services/vr/vr_window_manager/surface_flinger_view.h b/services/vr/vr_window_manager/surface_flinger_view.h
new file mode 100644 (file)
index 0000000..1bea38d
--- /dev/null
@@ -0,0 +1,46 @@
+#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_
diff --git a/services/vr/vr_window_manager/texture.cpp b/services/vr/vr_window_manager/texture.cpp
new file mode 100644 (file)
index 0000000..2229efa
--- /dev/null
@@ -0,0 +1,41 @@
+#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
diff --git a/services/vr/vr_window_manager/texture.h b/services/vr/vr_window_manager/texture.h
new file mode 100644 (file)
index 0000000..9840f19
--- /dev/null
@@ -0,0 +1,37 @@
+#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_
diff --git a/services/vr/vr_window_manager/vr_window_manager.cpp b/services/vr/vr_window_manager/vr_window_manager.cpp
new file mode 100644 (file)
index 0000000..dd2cba7
--- /dev/null
@@ -0,0 +1,47 @@
+#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;
+}
diff --git a/services/vr/vr_window_manager/vr_window_manager_binder.cpp b/services/vr/vr_window_manager/vr_window_manager_binder.cpp
new file mode 100644 (file)
index 0000000..fdcb8b2
--- /dev/null
@@ -0,0 +1,167 @@
+#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
diff --git a/services/vr/vr_window_manager/vr_window_manager_binder.h b/services/vr/vr_window_manager/vr_window_manager_binder.h
new file mode 100644 (file)
index 0000000..9d0f0b2
--- /dev/null
@@ -0,0 +1,79 @@
+#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_
diff --git a/services/vr/vr_window_manager/vr_window_manager_binder_test.cpp b/services/vr/vr_window_manager/vr_window_manager_binder_test.cpp
new file mode 100644 (file)
index 0000000..f43e803
--- /dev/null
@@ -0,0 +1,29 @@
+#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;
+}
diff --git a/services/vr/vr_window_manager/vr_wm.rc b/services/vr/vr_window_manager/vr_wm.rc
new file mode 100644 (file)
index 0000000..e515bb7
--- /dev/null
@@ -0,0 +1,5 @@
+service vr_wm /system/bin/vr_wm
+  class core
+  user system
+  group system graphics input
+  writepid /dev/cpuset/system/tasks
diff --git a/services/vr/vr_window_manager/vr_wm_ctl.cpp b/services/vr/vr_window_manager/vr_wm_ctl.cpp
new file mode 100644 (file)
index 0000000..758e02b
--- /dev/null
@@ -0,0 +1,52 @@
+#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;
+}