Add DaydreamVR native libraries and services
Upstreaming the main VR system components from master-dreamos-dev
into goog/master.
Bug: None
Test: `m -j32` succeeds. Sailfish boots and basic_vr sample app works
Change-Id: I853015872afc443aecee10411ef2d6b79184d051
diff --git a/libs/vr/libdvrcommon/Android.mk b/libs/vr/libdvrcommon/Android.mk
new file mode 100644
index 0000000..72afab2
--- /dev/null
+++ b/libs/vr/libdvrcommon/Android.mk
@@ -0,0 +1,81 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+sourceFiles := \
+ frame_time_history.cpp \
+ revision.cpp \
+ revision_path.cpp \
+ sync_util.cpp \
+
+includeFiles := \
+ $(LOCAL_PATH)/include \
+ external/eigen \
+
+sharedLibraries := \
+ libbase \
+ libcutils \
+ liblog \
+ libutils \
+ libEGL \
+ libGLESv2 \
+ libui \
+ libgui \
+ libhardware
+
+staticLibraries := \
+ libchrome \
+ libpdx_default_transport \
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_C_INCLUDES := \
+ $(includeFiles) \
+
+LOCAL_CFLAGS += -DLOG_TAG=\"libdvrcommon\"
+LOCAL_CFLAGS += -DTRACE=0
+LOCAL_EXPORT_C_INCLUDE_DIRS := \
+ $(includeFiles) \
+
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_MODULE := libdvrcommon
+include $(BUILD_STATIC_LIBRARY)
+
+testFiles := \
+ tests/numeric_test.cpp \
+ tests/pose_test.cpp \
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libdvrcommon_test
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+ $(testFiles) \
+
+LOCAL_C_INCLUDES := \
+ $(includeFiles) \
+
+LOCAL_SHARED_LIBRARIES := \
+ $(sharedLibraries) \
+
+LOCAL_STATIC_LIBRARIES := \
+ libgmock_main \
+ libgmock \
+ libgtest \
+ $(staticLibraries) \
+
+include $(BUILD_NATIVE_TEST)
+
diff --git a/libs/vr/libdvrcommon/frame_time_history.cpp b/libs/vr/libdvrcommon/frame_time_history.cpp
new file mode 100644
index 0000000..796498c
--- /dev/null
+++ b/libs/vr/libdvrcommon/frame_time_history.cpp
@@ -0,0 +1,37 @@
+#include <private/dvr/frame_time_history.h>
+
+#include <cutils/log.h>
+
+namespace android {
+namespace dvr {
+
+void FrameTimeHistory::AddSample(int64_t frame_time) {
+ if (size_ == frame_times_.size()) {
+ int64_t expired_frame_time = frame_times_[start_];
+ frame_times_[start_] = frame_time;
+ start_ = (start_ + 1) % frame_times_.size();
+ total_frame_time_ -= expired_frame_time;
+ } else {
+ frame_times_[(start_ + size_) % frame_times_.size()] = frame_time;
+ size_++;
+ }
+ total_frame_time_ += frame_time;
+}
+
+int FrameTimeHistory::GetSampleCount() const { return size_; }
+
+int64_t FrameTimeHistory::GetAverage() const {
+ LOG_ALWAYS_FATAL_IF(size_ == 0);
+ return total_frame_time_ / size_;
+}
+
+void FrameTimeHistory::ResetWithSeed(int64_t frame_time_seed) {
+ start_ = 0;
+ size_ = frame_times_.size();
+ for (size_t i = 0; i < size_; ++i)
+ frame_times_[i] = frame_time_seed;
+ total_frame_time_ = frame_time_seed * size_;
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libdvrcommon/include/private/dvr/benchmark.h b/libs/vr/libdvrcommon/include/private/dvr/benchmark.h
new file mode 100644
index 0000000..2dbb5f2
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/benchmark.h
@@ -0,0 +1,86 @@
+#ifndef ANDROID_DVR_BENCHMARK_H_
+#define ANDROID_DVR_BENCHMARK_H_
+
+#include <stdio.h>
+#include <time.h>
+
+#include <cutils/trace.h>
+
+#include <private/dvr/clock_ns.h>
+
+// Set benchmark traces, using Android systrace.
+//
+// The simplest one-parameter version of btrace automatically sets the
+// timestamp with the system clock. The other versions can optionally set the
+// timestamp manually, or pass additional data to be written to the log line.
+//
+// Example:
+// Btrace("Start execution");
+// ... code to benchmark ...
+// Btrace("End execution");
+//
+// Use compute_benchmarks.py (currently in dreamos/system/core/applications),
+// with the trace path "Start execution,End execution",
+// to report the elapsed time between the two calls.
+//
+// Btrace will either output to standard atrace, or to a file if specified.
+// The versions BtraceData also allow an int64_t to be included in the trace.
+
+// Btrace without data payload.
+static inline void Btrace(const char* name, int64_t nanoseconds_monotonic);
+static inline void Btrace(const char* name);
+static inline void Btrace(FILE* file, const char* name,
+ int64_t nanoseconds_monotonic);
+static inline void Btrace(FILE* file, const char* name);
+
+// Btrace with data payload.
+static inline void BtraceData(const char* name, int64_t nanoseconds_monotonic,
+ int64_t data);
+static inline void BtraceData(const char* name, int64_t data);
+static inline void BtraceData(FILE* file, const char* name,
+ int64_t nanoseconds_monotonic, int64_t data);
+static inline void BtraceData(FILE* file, const char* name, int64_t data);
+
+static inline void Btrace(const char* name, int64_t nanoseconds_monotonic) {
+ const int kLogMessageLength = 256;
+ char log_message[kLogMessageLength];
+ snprintf(log_message, kLogMessageLength, "#btrace#%s", name);
+ atrace_int64(ATRACE_TAG_WEBVIEW, log_message, nanoseconds_monotonic);
+}
+
+static inline void Btrace(const char* name) {
+ Btrace(name, android::dvr::GetSystemClockNs());
+}
+
+static inline void Btrace(FILE* file, const char* name,
+ int64_t nanoseconds_monotonic) {
+ fprintf(file, "#btrace#%s|%" PRId64 "\n", name, nanoseconds_monotonic);
+}
+
+static inline void Btrace(FILE* file, const char* name) {
+ Btrace(file, name, android::dvr::GetSystemClockNs());
+}
+
+static inline void BtraceData(const char* name, int64_t nanoseconds_monotonic,
+ int64_t data) {
+ const int kLogMessageLength = 256;
+ char log_message[kLogMessageLength];
+ snprintf(log_message, kLogMessageLength, "#btrace#%s|%" PRId64, name, data);
+ atrace_int64(ATRACE_TAG_WEBVIEW, log_message, nanoseconds_monotonic);
+}
+
+static inline void BtraceData(const char* name, int64_t data) {
+ BtraceData(name, android::dvr::GetSystemClockNs(), data);
+}
+
+static inline void BtraceData(FILE* file, const char* name,
+ int64_t nanoseconds_monotonic, int64_t data) {
+ fprintf(file, "#btrace#%s|%" PRId64 "|%" PRId64 "\n", name, data,
+ nanoseconds_monotonic);
+}
+
+static inline void BtraceData(FILE* file, const char* name, int64_t data) {
+ BtraceData(file, name, android::dvr::GetSystemClockNs(), data);
+}
+
+#endif // ANDROID_DVR_BENCHMARK_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/clock_ns.h b/libs/vr/libdvrcommon/include/private/dvr/clock_ns.h
new file mode 100644
index 0000000..8e777ed
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/clock_ns.h
@@ -0,0 +1,84 @@
+#ifndef ANDROID_DVR_CLOCK_NS_H_
+#define ANDROID_DVR_CLOCK_NS_H_
+
+#include <stdint.h>
+#include <time.h>
+
+namespace android {
+namespace dvr {
+
+constexpr int64_t kNanosPerSecond = 1000000000ll;
+
+// Returns the standard Dream OS monotonic system time that corresponds with all
+// timestamps found in Dream OS APIs.
+static inline timespec GetSystemClock() {
+ timespec t;
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ return t;
+}
+
+static inline timespec GetSystemClockRaw() {
+ timespec t;
+ clock_gettime(CLOCK_MONOTONIC_RAW, &t);
+ return t;
+}
+
+static inline int64_t GetSystemClockNs() {
+ timespec t = GetSystemClock();
+ int64_t ns = kNanosPerSecond * (int64_t)t.tv_sec + (int64_t)t.tv_nsec;
+ return ns;
+}
+
+static inline int64_t GetSystemClockRawNs() {
+ timespec t = GetSystemClockRaw();
+ int64_t ns = kNanosPerSecond * (int64_t)t.tv_sec + (int64_t)t.tv_nsec;
+ return ns;
+}
+
+static inline double NsToSec(int64_t nanoseconds) {
+ return nanoseconds / static_cast<double>(kNanosPerSecond);
+}
+
+static inline double GetSystemClockSec() { return NsToSec(GetSystemClockNs()); }
+
+static inline double GetSystemClockMs() { return GetSystemClockSec() * 1000.0; }
+
+// Converts a nanosecond timestamp to a timespec. Based on the kernel function
+// of the same name.
+static inline timespec NsToTimespec(int64_t ns) {
+ timespec t;
+ int32_t remainder;
+
+ t.tv_sec = ns / kNanosPerSecond;
+ remainder = ns % kNanosPerSecond;
+ if (remainder < 0) {
+ t.tv_nsec--;
+ remainder += kNanosPerSecond;
+ }
+ t.tv_nsec = remainder;
+
+ return t;
+}
+
+// Timestamp comparison functions that handle wrapping values correctly.
+static inline bool TimestampLT(int64_t a, int64_t b) {
+ return static_cast<int64_t>(static_cast<uint64_t>(a) -
+ static_cast<uint64_t>(b)) < 0;
+}
+static inline bool TimestampLE(int64_t a, int64_t b) {
+ return static_cast<int64_t>(static_cast<uint64_t>(a) -
+ static_cast<uint64_t>(b)) <= 0;
+}
+static inline bool TimestampGT(int64_t a, int64_t b) {
+ return static_cast<int64_t>(static_cast<uint64_t>(a) -
+ static_cast<uint64_t>(b)) > 0;
+}
+static inline bool TimestampGE(int64_t a, int64_t b) {
+ return static_cast<int64_t>(static_cast<uint64_t>(a) -
+ static_cast<uint64_t>(b)) >= 0;
+}
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_CLOCK_NS_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/debug.h b/libs/vr/libdvrcommon/include/private/dvr/debug.h
new file mode 100644
index 0000000..7db681a
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/debug.h
@@ -0,0 +1,37 @@
+#ifndef ANDROID_DVR_DEBUG_H_
+#define ANDROID_DVR_DEBUG_H_
+
+#include <GLES3/gl3.h>
+#include <math.h>
+
+#include <base/logging.h>
+
+#ifndef NDEBUG
+#define CHECK_GL() \
+ do { \
+ const GLenum err = glGetError(); \
+ if (err != GL_NO_ERROR) { \
+ LOG(ERROR) << "OpenGL error " << err; \
+ } \
+ } while (0)
+
+#define CHECK_GL_FBO() \
+ do { \
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); \
+ switch (status) { \
+ case GL_FRAMEBUFFER_COMPLETE: \
+ break; \
+ case GL_FRAMEBUFFER_UNSUPPORTED: \
+ LOG(ERROR) << "GL_FRAMEBUFFER_UNSUPPORTED"; \
+ break; \
+ default: \
+ LOG(ERROR) << "FBO user error: " << status; \
+ break; \
+ } \
+ } while (0)
+#else
+#define CHECK_GL()
+#define CHECK_GL_FBO()
+#endif
+
+#endif // ANDROID_DVR_DEBUG_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/eigen.h b/libs/vr/libdvrcommon/include/private/dvr/eigen.h
new file mode 100644
index 0000000..defaf58
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/eigen.h
@@ -0,0 +1,57 @@
+#ifndef ANDROID_DVR_EIGEN_H_
+#define ANDROID_DVR_EIGEN_H_
+
+#include <Eigen/Core>
+#include <Eigen/Geometry>
+
+namespace Eigen {
+
+// Eigen doesn't take advantage of C++ template typedefs, but we can
+template <class T, int N>
+using Vector = Matrix<T, N, 1>;
+
+template <class T>
+using Vector2 = Vector<T, 2>;
+
+template <class T>
+using Vector3 = Vector<T, 3>;
+
+template <class T>
+using Vector4 = Vector<T, 4>;
+
+template <class T, int N>
+using RowVector = Matrix<T, 1, N>;
+
+template <class T>
+using RowVector2 = RowVector<T, 2>;
+
+template <class T>
+using RowVector3 = RowVector<T, 3>;
+
+template <class T>
+using RowVector4 = RowVector<T, 4>;
+
+// In Eigen, the type you should be using for transformation matrices is the
+// `Transform` class, instead of a raw `Matrix`.
+// The `Projective` option means this will not make any assumptions about the
+// last row of the object, making this suitable for use as general OpenGL
+// projection matrices (which is the most common use-case). The one caveat
+// is that in order to apply this transformation to non-homogeneous vectors
+// (e.g., vec3), you must use the `.linear()` method to get the affine part of
+// the matrix.
+//
+// Example:
+// mat4 transform;
+// vec3 position;
+// vec3 transformed = transform.linear() * position;
+//
+// Note, the use of N-1 is because the parameter passed to Eigen is the ambient
+// dimension of the transformation, not the size of the matrix iself.
+// However graphics programmers sometimes get upset when they see a 3 next
+// to a matrix when they expect a 4, so I'm hoping this will avoid that.
+template <class T, int N>
+using AffineMatrix = Transform<T, N-1, Projective>;
+
+} // namespace Eigen
+
+#endif // ANDROID_DVR_EIGEN_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/epoll_file_descriptor.h b/libs/vr/libdvrcommon/include/private/dvr/epoll_file_descriptor.h
new file mode 100644
index 0000000..8df741c
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/epoll_file_descriptor.h
@@ -0,0 +1,62 @@
+#ifndef LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_EPOLL_FILE_DESCRIPTOR_H_
+#define LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_EPOLL_FILE_DESCRIPTOR_H_
+
+#include <android-base/unique_fd.h>
+#include <base/logging.h>
+#include <sys/epoll.h>
+
+namespace android {
+namespace dvr {
+
+class EpollFileDescriptor {
+ public:
+ static const int CTL_ADD = EPOLL_CTL_ADD;
+ static const int CTL_MOD = EPOLL_CTL_MOD;
+ static const int CTL_DEL = EPOLL_CTL_DEL;
+
+ EpollFileDescriptor() : fd_(-1) {}
+
+ // Constructs an EpollFileDescriptor from an integer file descriptor and
+ // takes ownership.
+ explicit EpollFileDescriptor(int fd) : fd_(fd) {}
+
+ bool IsValid() const { return fd_.get() >= 0; }
+
+ int Create() {
+ if (IsValid()) {
+ LOG(WARNING) << "epoll fd has already been created.";
+ return -EALREADY;
+ }
+
+ fd_.reset(epoll_create(64));
+
+ if (fd_.get() < 0)
+ return -errno;
+ else
+ return 0;
+ }
+
+ int Control(int op, int target_fd, epoll_event* ev) {
+ if (epoll_ctl(fd_.get(), op, target_fd, ev) < 0)
+ return -errno;
+ else
+ return 0;
+ }
+
+ int Wait(epoll_event* events, int maxevents, int timeout) {
+ int ret = epoll_wait(fd_.get(), events, maxevents, timeout);
+
+ if (ret < 0)
+ return -errno;
+ else
+ return ret;
+ }
+
+ private:
+ base::unique_fd fd_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_EPOLL_FILE_DESCRIPTOR_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/field_of_view.h b/libs/vr/libdvrcommon/include/private/dvr/field_of_view.h
new file mode 100644
index 0000000..d0ee69c
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/field_of_view.h
@@ -0,0 +1,95 @@
+#ifndef ANDROID_DVR_FIELD_OF_VIEW_H_
+#define ANDROID_DVR_FIELD_OF_VIEW_H_
+
+#include <cmath>
+
+#include <private/dvr/eigen.h>
+
+namespace android {
+namespace dvr {
+
+// Encapsulates a generalized, asymmetric field of view with four half angles.
+// Each half angle denotes the angle between the corresponding frustum plane.
+// Together with a near and far plane, a FieldOfView forms the frustum of an
+// off-axis perspective projection.
+class FieldOfView {
+ public:
+ // The default constructor sets an angle of 0 (in any unit) for all four
+ // half-angles.
+ FieldOfView() : left_(0.0f), right_(0.0f), bottom_(0.0f), top_(0.0f) {}
+
+ // Constructs a FieldOfView from four angles.
+ FieldOfView(float left, float right, float bottom, float top)
+ : left_(left), right_(right), bottom_(bottom), top_(top) {}
+
+ explicit FieldOfView(const float* fov)
+ : FieldOfView(fov[0], fov[1], fov[2], fov[3]) {}
+
+ // Accessors for all four half-angles.
+ float GetLeft() const { return left_; }
+ float GetRight() const { return right_; }
+ float GetBottom() const { return bottom_; }
+ float GetTop() const { return top_; }
+
+ // Setters for all four half-angles.
+ void SetLeft(float left) { left_ = left; }
+ void SetRight(float right) { right_ = right; }
+ void SetBottom(float bottom) { bottom_ = bottom; }
+ void SetTop(float top) { top_ = top; }
+
+ Eigen::AffineMatrix<float, 4> GetProjectionMatrix(float z_near,
+ float z_far) const {
+ float x_left = -std::tan(left_) * z_near;
+ float x_right = std::tan(right_) * z_near;
+ float y_bottom = -std::tan(bottom_) * z_near;
+ float y_top = std::tan(top_) * z_near;
+
+ float zero = 0.0f;
+ if (x_left == x_right || y_bottom == y_top || z_near == z_far ||
+ z_near <= zero || z_far <= zero) {
+ return Eigen::AffineMatrix<float, 4>::Identity();
+ }
+
+ float x = (2 * z_near) / (x_right - x_left);
+ float y = (2 * z_near) / (y_top - y_bottom);
+ float a = (x_right + x_left) / (x_right - x_left);
+ float b = (y_top + y_bottom) / (y_top - y_bottom);
+ float c = (z_near + z_far) / (z_near - z_far);
+ float d = (2 * z_near * z_far) / (z_near - z_far);
+
+ // Note: Eigen matrix initialization syntax is always 'column-major'
+ // even if the storage is row-major. Or in other words, just write the
+ // matrix like you'd see in a math textbook.
+ Eigen::AffineMatrix<float, 4> result;
+ result.matrix() << x, 0, a, 0,
+ 0, y, b, 0,
+ 0, 0, c, d,
+ 0, 0, -1, 0;
+ return result;
+ }
+
+ static FieldOfView FromProjectionMatrix(
+ const Eigen::AffineMatrix<float, 4>& m) {
+ // Compute tangents.
+ float tan_vert_fov = 1.0f / m(1, 1);
+ float tan_horz_fov = 1.0f / m(0, 0);
+ float t = (m(1, 2) + 1.0f) * tan_vert_fov;
+ float b = (m(1, 2) - 1.0f) * tan_vert_fov;
+ float l = (m(0, 2) - 1.0f) * tan_horz_fov;
+ float r = (m(0, 2) + 1.0f) * tan_horz_fov;
+
+ return FieldOfView(std::atan(-l), std::atan(r), std::atan(-b),
+ std::atan(t));
+ }
+
+ private:
+ float left_;
+ float right_;
+ float bottom_;
+ float top_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_FIELD_OF_VIEW_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/frame_time_history.h b/libs/vr/libdvrcommon/include/private/dvr/frame_time_history.h
new file mode 100644
index 0000000..008c636
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/frame_time_history.h
@@ -0,0 +1,33 @@
+#ifndef ANDROID_DVR_FRAME_TIME_HISTORY_H_
+#define ANDROID_DVR_FRAME_TIME_HISTORY_H_
+
+#include <stdint.h>
+
+#include <array>
+
+namespace android {
+namespace dvr {
+
+// Maintains frame time history and provides averaging utility methods.
+class FrameTimeHistory {
+ public:
+ void AddSample(int64_t frame_time);
+ int GetSampleCount() const;
+ int64_t GetAverage() const;
+ float GetAverageFps() const {
+ return 1000000000.0f / static_cast<float>(GetAverage());
+ }
+ void ResetWithSeed(int64_t frame_time_seed);
+
+ private:
+ static constexpr int kFrameTimeHistoryNumSamples = 30;
+ std::array<int64_t, kFrameTimeHistoryNumSamples> frame_times_;
+ int start_ = 0;
+ size_t size_ = 0;
+ int64_t total_frame_time_ = 0;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_FRAME_TIME_HISTORY_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/log_helpers.h b/libs/vr/libdvrcommon/include/private/dvr/log_helpers.h
new file mode 100644
index 0000000..c9f7f8e
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/log_helpers.h
@@ -0,0 +1,63 @@
+#ifndef ANDROID_DVR_LOG_HELPERS_H_
+#define ANDROID_DVR_LOG_HELPERS_H_
+
+#include <iomanip>
+
+#include <base/logging.h>
+#include <private/dvr/eigen.h>
+#include <private/dvr/field_of_view.h>
+
+namespace android {
+namespace dvr {
+
+template <typename T>
+inline std::ostream& operator<<(std::ostream& out,
+ const Eigen::Vector<T, 2>& vec) {
+ return out << "vec2(" << vec.x() << ',' << vec.y() << ')';
+}
+
+template <typename T>
+inline std::ostream& operator<<(std::ostream& out,
+ const Eigen::Vector<T, 3>& vec) {
+ return out << "vec3(" << vec.x() << ',' << vec.y() << ',' << vec.z() << ')';
+}
+
+template <typename T>
+inline std::ostream& operator<<(std::ostream& out,
+ const Eigen::Vector<T, 4>& vec) {
+ return out << "vec4(" << vec.x() << ',' << vec.y() << ',' << vec.z() << ','
+ << vec.w() << ')';
+}
+
+template <typename T>
+inline std::ostream& operator<<(std::ostream& out,
+ const Eigen::AffineMatrix<T, 4>& mat) {
+ out << std::setfill(' ') << std::setprecision(4) << std::fixed << std::showpos;
+ out << "\nmat4[";
+ out << std::setw(10) << mat(0, 0) << " " << std::setw(10) << mat(0, 1) << " "
+ << std::setw(10) << mat(0, 2) << " " << std::setw(10) << mat(0, 3);
+ out << "]\n [";
+ out << std::setw(10) << mat(1, 0) << " " << std::setw(10) << mat(1, 1) << " "
+ << std::setw(10) << mat(1, 2) << " " << std::setw(10) << mat(1, 3);
+ out << "]\n [";
+ out << std::setw(10) << mat(2, 0) << " " << std::setw(10) << mat(2, 1) << " "
+ << std::setw(10) << mat(2, 2) << " " << std::setw(10) << mat(2, 3);
+ out << "]\n [";
+ out << std::setw(10) << mat(3, 0) << " " << std::setw(10) << mat(3, 1) << " "
+ << std::setw(10) << mat(3, 2) << " " << std::setw(10) << mat(3, 3);
+ out << "]\n";
+
+ return out;
+}
+
+inline std::ostream& operator<<(std::ostream& out, const FieldOfView& fov) {
+ return out << "fov(" << (fov.GetLeft() * 180.0f / M_PI) << ','
+ << (fov.GetRight() * 180.0f / M_PI) << ','
+ << (fov.GetBottom() * 180.0f / M_PI) << ','
+ << (fov.GetTop() * 180.0f / M_PI) << ')';
+}
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_LOG_HELPERS_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/numeric.h b/libs/vr/libdvrcommon/include/private/dvr/numeric.h
new file mode 100644
index 0000000..584236a
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/numeric.h
@@ -0,0 +1,178 @@
+#ifndef ANDROID_DVR_NUMERIC_H_
+#define ANDROID_DVR_NUMERIC_H_
+
+#include <cmath>
+#include <limits>
+#include <random>
+#include <type_traits>
+
+#include <private/dvr/eigen.h>
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+template <typename FT>
+static inline FT ToDeg(FT f) {
+ return f * static_cast<FT>(180.0 / M_PI);
+}
+
+template <typename FT>
+static inline FT ToRad(FT f) {
+ return f * static_cast<FT>(M_PI / 180.0);
+}
+
+// Adjusts `x` to the periodic range `[lo, hi]` (to normalize angle values
+// for example).
+template <typename T>
+T NormalizePeriodicRange(T x, T lo, T hi) {
+ T range_size = hi - lo;
+
+ while (x < lo) {
+ x += range_size;
+ }
+
+ while (x > hi) {
+ x -= range_size;
+ }
+
+ return x;
+}
+
+// Normalizes a measurement in radians.
+// @param x the angle to be normalized
+// @param centre the point around which to normalize the range
+// @return the value of x, normalized to the range [centre - 180, centre + 180]
+template <typename T>
+T NormalizeDegrees(T x, T centre = static_cast<T>(180.0)) {
+ return NormalizePeriodicRange(x, centre - static_cast<T>(180.0),
+ centre + static_cast<T>(180.0));
+}
+
+// Normalizes a measurement in radians.
+// @param x the angle to be normalized
+// @param centre the point around which to normalize the range
+// @return the value of x, normalized to the range
+// [centre - M_PI, centre + M_PI]
+// @remark the centre parameter is to make it possible to specify a different
+// periodic range. This is useful if you are planning on comparing two
+// angles close to 0 or M_PI, so that one might not accidentally end
+// up on the other side of the range
+template <typename T>
+T NormalizeRadians(T x, T centre = static_cast<T>(M_PI)) {
+ return NormalizePeriodicRange(x, centre - static_cast<T>(M_PI),
+ centre + static_cast<T>(M_PI));
+}
+
+static inline vec2i Round(const vec2& v) {
+ return vec2i(roundf(v.x()), roundf(v.y()));
+}
+
+static inline vec2i Scale(const vec2i& v, float scale) {
+ return vec2i(roundf(static_cast<float>(v.x()) * scale),
+ roundf(static_cast<float>(v.y()) * scale));
+}
+
+// Re-maps `x` from `[lba,uba]` to `[lbb,ubb]`.
+template <typename T>
+T ConvertRange(T x, T lba, T uba, T lbb, T ubb) {
+ return (((x - lba) * (ubb - lbb)) / (uba - lba)) + lbb;
+}
+
+template <typename R1, typename R2>
+static inline vec2 MapPoint(const vec2& pt, const R1& from, const R2& to) {
+ vec2 normalized((pt - vec2(from.p1)).array() / vec2(from.GetSize()).array());
+ return (normalized * vec2(to.GetSize())) + vec2(to.p1);
+}
+
+template <typename T>
+inline bool IsZero(const T& v,
+ const T& tol = std::numeric_limits<T>::epsilon()) {
+ return std::abs(v) <= tol;
+}
+
+template <typename T>
+inline bool IsEqual(const T& a, const T& b,
+ const T& tol = std::numeric_limits<T>::epsilon()) {
+ return std::abs(b - a) <= tol;
+}
+
+template <typename T>
+T Square(const T& x) {
+ return x * x;
+}
+
+template <typename T>
+T RandomInRange(T lo, T hi,
+ typename
+ std::enable_if<std::is_floating_point<T>::value>::type* = 0) {
+ std::random_device rd;
+ std::mt19937 gen(rd());
+ std::uniform_real_distribution<T> distro(lo, hi);
+ return distro(gen);
+}
+
+template <typename T>
+T RandomInRange(T lo, T hi,
+ typename
+ std::enable_if<std::is_integral<T>::value>::type* = 0) {
+ std::random_device rd;
+ std::mt19937 gen(rd());
+ std::uniform_int_distribution<T> distro(lo, hi);
+ return distro(gen);
+}
+
+template <typename Derived1, typename Derived2>
+Derived1 RandomInRange(
+ const Eigen::MatrixBase<Derived1>& lo,
+ const Eigen::MatrixBase<Derived2>& hi) {
+ using Matrix1_t = Eigen::MatrixBase<Derived1>;
+ using Matrix2_t = Eigen::MatrixBase<Derived2>;
+
+ EIGEN_STATIC_ASSERT_SAME_MATRIX_SIZE(Matrix1_t, Matrix2_t);
+
+ Derived1 result = Matrix1_t::Zero();
+
+ for (int row = 0; row < result.rows(); ++row) {
+ for (int col = 0; col < result.cols(); ++col) {
+ result(row, col) = RandomInRange(lo(row, col), hi(row, col));
+ }
+ }
+
+ return result;
+}
+
+template <typename T>
+T RandomRange(T x) {
+ return RandomInRange(-x, x);
+}
+
+template <typename T>
+T Clamp(T x, T lo, T hi) {
+ return std::min(std::max(x, lo), hi);
+}
+
+inline mat3 ScaleMatrix(const vec2& scale_xy) {
+ return mat3(Eigen::Scaling(scale_xy[0], scale_xy[1], 1.0f));
+}
+
+inline mat3 TranslationMatrix(const vec2& translation) {
+ return mat3(Eigen::Translation2f(translation));
+}
+
+inline mat4 TranslationMatrix(const vec3& translation) {
+ return mat4(Eigen::Translation3f(translation));
+}
+
+inline vec2 TransformPoint(const mat3& m, const vec2& p) {
+ return m.linear() * p + m.translation();
+}
+
+inline vec2 TransformVector(const mat3& m, const vec2& p) {
+ return m.linear() * p;
+}
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_NUMERIC_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/ortho.h b/libs/vr/libdvrcommon/include/private/dvr/ortho.h
new file mode 100644
index 0000000..fc0bce3
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/ortho.h
@@ -0,0 +1,31 @@
+#ifndef ANDROID_DVR_ORTHO_H_
+#define ANDROID_DVR_ORTHO_H_
+
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+template <class T>
+Eigen::AffineMatrix<T, 4> OrthoMatrix(T left, T right, T bottom, T top,
+ T znear, T zfar) {
+ Eigen::AffineMatrix<T, 4> result;
+ const T t2 = static_cast<T>(2);
+ const T a = t2 / (right - left);
+ const T b = t2 / (top - bottom);
+ const T c = t2 / (zfar - znear);
+ const T xoff = -(right + left) / (right - left);
+ const T yoff = -(top + bottom) / (top - bottom);
+ const T zoff = -(zfar + znear) / (zfar - znear);
+ const T t1 = static_cast<T>(1);
+ result.matrix() << a, 0, 0, xoff,
+ 0, b, 0, yoff,
+ 0, 0, c, zoff,
+ 0, 0, 0, t1;
+ return result;
+}
+
+} // namespace android
+} // namespace dvr
+
+#endif // ANDROID_DVR_ORTHO_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/platform_defines.h b/libs/vr/libdvrcommon/include/private/dvr/platform_defines.h
new file mode 100644
index 0000000..71d4c8c
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/platform_defines.h
@@ -0,0 +1,16 @@
+#ifndef ANDROID_DVR_PLATFORM_DEFINES_H_
+#define ANDROID_DVR_PLATFORM_DEFINES_H_
+
+// Platform-specific macros and defines.
+
+// QCOM's GRALLOC_USAGE_PRIVATE_ALLOC_UBWC usage bit.
+#define GRALLOC_USAGE_QCOM_FRAMEBUFFER_COMPRESSION GRALLOC_USAGE_PRIVATE_1
+
+// QCOM bit to use the ADSP heap. This carveout heap is accessible to Linux,
+// Hexagon DSPs, and the GPU.
+#define GRALLOC_USAGE_PRIVATE_ADSP_HEAP 0x01000000
+
+// Force a gralloc buffer to get the uncached ION option set.
+#define GRALLOC_USAGE_PRIVATE_UNCACHED 0x02000000
+
+#endif // ANDROID_DVR_PLATFORM_DEFINES_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/pose.h b/libs/vr/libdvrcommon/include/private/dvr/pose.h
new file mode 100644
index 0000000..97944e8
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/pose.h
@@ -0,0 +1,118 @@
+#ifndef ANDROID_DVR_POSE_H_
+#define ANDROID_DVR_POSE_H_
+
+#include <private/dvr/eigen.h>
+
+namespace android {
+namespace dvr {
+
+// Encapsulates a 3D pose (rotation and position).
+//
+// @tparam T Data type for storing the position coordinate and rotation
+// quaternion.
+template <typename T>
+class Pose {
+ public:
+ // Creates identity pose.
+ Pose()
+ : rotation_(Eigen::Quaternion<T>::Identity()),
+ position_(Eigen::Vector3<T>::Zero()) {}
+
+ // Initializes a pose with given rotation and position.
+ //
+ // rotation Initial rotation.
+ // position Initial position.
+ Pose(Eigen::Quaternion<T> rotation, Eigen::Vector3<T> position)
+ : rotation_(rotation), position_(position) {}
+
+ void Invert() {
+ rotation_ = rotation_.inverse();
+ position_ = rotation_ * -position_;
+ }
+
+ Pose Inverse() const {
+ Pose result(*this);
+ result.Invert();
+ return result;
+ }
+
+ // Compute the composition of this pose with another, storing the result
+ // in the current object
+ void ComposeInPlace(const Pose& other) {
+ position_ = position_ + rotation_ * other.position_;
+ rotation_ = rotation_ * other.rotation_;
+ }
+
+ // Computes the composition of this pose with another, and returns the result
+ Pose Compose(const Pose& other) const {
+ Pose result(*this);
+ result.ComposeInPlace(other);
+ return result;
+ }
+
+ Eigen::Vector3<T> TransformPoint(const Eigen::Vector3<T>& v) const {
+ return rotation_ * v + position_;
+ }
+
+ Eigen::Vector3<T> Transform(const Eigen::Vector3<T>& v) const {
+ return rotation_ * v;
+ }
+
+ Pose& operator*=(const Pose& other) {
+ ComposeInPlace(other);
+ return *this;
+ }
+
+ Pose operator*(const Pose& other) const { return Compose(other); }
+
+ // Gets the rotation of the 3D pose.
+ Eigen::Quaternion<T> GetRotation() const { return rotation_; }
+
+ // Gets the position of the 3D pose.
+ Eigen::Vector3<T> GetPosition() const { return position_; }
+
+ // Sets the rotation of the 3D pose.
+ void SetRotation(Eigen::Quaternion<T> rotation) { rotation_ = rotation; }
+
+ // Sets the position of the 3D pose.
+ void SetPosition(Eigen::Vector3<T> position) { position_ = position; }
+
+ // Gets a 4x4 matrix representing a transform from the reference space (that
+ // the rotation and position of the pose are relative to) to the object space.
+ Eigen::AffineMatrix<T, 4> GetObjectFromReferenceMatrix() const;
+
+ // Gets a 4x4 matrix representing a transform from the object space to the
+ // reference space (that the rotation and position of the pose are relative
+ // to).
+ Eigen::AffineMatrix<T, 4> GetReferenceFromObjectMatrix() const;
+
+ private:
+ Eigen::Quaternion<T> rotation_;
+ Eigen::Vector3<T> position_;
+};
+
+template <typename T>
+Eigen::AffineMatrix<T, 4> Pose<T>::GetObjectFromReferenceMatrix() const {
+ // The transfrom from the reference is the inverse of the pose.
+ Eigen::AffineMatrix<T, 4> matrix(rotation_.inverse().toRotationMatrix());
+ return matrix.translate(-position_);
+}
+
+template <typename T>
+Eigen::AffineMatrix<T, 4> Pose<T>::GetReferenceFromObjectMatrix() const {
+ // The transfrom to the reference.
+ Eigen::AffineMatrix<T, 4> matrix(rotation_.toRotationMatrix());
+ return matrix.pretranslate(position_);
+}
+
+//------------------------------------------------------------------------------
+// Type-specific typedefs.
+//------------------------------------------------------------------------------
+
+using Posef = Pose<float>;
+using Posed = Pose<double>;
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_POSE_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/range.h b/libs/vr/libdvrcommon/include/private/dvr/range.h
new file mode 100644
index 0000000..1d06c96
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/range.h
@@ -0,0 +1,43 @@
+#ifndef ANDROID_DVR_RANGE_H_
+#define ANDROID_DVR_RANGE_H_
+
+#include <private/dvr/eigen.h>
+
+namespace android {
+namespace dvr {
+
+// TODO(skiazyk): Replace all instances of this with Eigen::AlignedBox
+
+// Container of two points that define a 2D range.
+template <class T, int d>
+struct Range {
+ // Construct an uninitialized Range.
+ Range() {}
+ Range(Eigen::Vector<T, d> p1, Eigen::Vector<T, d> p2) : p1(p1), p2(p2) {}
+
+ static Range<T, d> FromSize(Eigen::Vector<T, d> p1,
+ Eigen::Vector<T, d> size) {
+ return Range<T, d>(p1, p1 + size);
+ }
+
+ bool operator==(const Range<T, d>& rhs) const {
+ return p1 == rhs.p1 && p2 == rhs.p2;
+ }
+
+ Eigen::Vector<T, d> GetMinPoint() const { return p1; }
+
+ Eigen::Vector<T, d> GetMaxPoint() const { return p2; }
+
+ Eigen::Vector<T, d> GetSize() const { return p2 - p1; }
+
+ Eigen::Vector<T, d> p1;
+ Eigen::Vector<T, d> p2;
+};
+
+typedef Range<int, 2> Range2i;
+typedef Range<float, 2> Range2f;
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_RANGE_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/revision.h b/libs/vr/libdvrcommon/include/private/dvr/revision.h
new file mode 100644
index 0000000..dda0fce
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/revision.h
@@ -0,0 +1,47 @@
+#ifndef LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_REVISION_H_
+#define LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_REVISION_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// List of DreamOS products
+typedef enum DvrProduct {
+ DVR_PRODUCT_UNKNOWN,
+ DVR_PRODUCT_A00,
+ DVR_PRODUCT_A65R,
+ DVR_PRODUCT_TWILIGHT = DVR_PRODUCT_A65R
+} DvrProduct;
+
+// List of possible revisions.
+typedef enum DvrRevision {
+ DVR_REVISION_UNKNOWN,
+ DVR_REVISION_P1,
+ DVR_REVISION_P2,
+ DVR_REVISION_P3,
+} DvrRevision;
+
+// Query the device's product.
+//
+// @return DvrProduct value, or DvrProductUnknown on error.
+DvrProduct dvr_get_product();
+
+// Query the device's revision.
+//
+// @return DvrRevision value, or DvrRevisionUnknown on error.
+DvrRevision dvr_get_revision();
+
+// Returns the device's board revision string.
+//
+// @return NULL-terminated string such as 'a00-p1'.
+const char* dvr_get_product_revision_str();
+
+// Returns the device's serial number.
+//
+// @return Returns NULL on error, or a NULL-terminated string.
+const char* dvr_get_serial_number();
+
+#ifdef __cplusplus
+}
+#endif // extern "C"
+#endif // LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_REVISION_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/ring_buffer.h b/libs/vr/libdvrcommon/include/private/dvr/ring_buffer.h
new file mode 100644
index 0000000..44485a7
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/ring_buffer.h
@@ -0,0 +1,99 @@
+#ifndef ANDROID_DVR_RING_BUFFER_H_
+#define ANDROID_DVR_RING_BUFFER_H_
+
+#include <utility>
+#include <vector>
+
+namespace android {
+namespace dvr {
+
+// A simple ring buffer implementation.
+//
+// A vector works but you either have to keep track of start_ and size_ yourself
+// or erase() from the front which is inefficient.
+//
+// A deque works but the common usage pattern of Append() PopFront() Append()
+// PopFront() looks like it allocates each time size goes from 0 --> 1, which we
+// don't want. This class allocates only once.
+template <typename T>
+class RingBuffer {
+ public:
+ RingBuffer() { Reset(0); }
+
+ explicit RingBuffer(size_t capacity) { Reset(capacity); }
+
+ RingBuffer(const RingBuffer& other) = default;
+ RingBuffer(RingBuffer&& other) = default;
+ RingBuffer& operator=(const RingBuffer& other) = default;
+ RingBuffer& operator=(RingBuffer&& other) = default;
+
+ void Append(const T& val) {
+ if (IsFull())
+ PopFront();
+ Get(size_) = val;
+ size_++;
+ }
+
+ void Append(T&& val) {
+ if (IsFull())
+ PopFront();
+ Get(size_) = std::move(val);
+ size_++;
+ }
+
+ bool IsEmpty() const { return size_ == 0; }
+
+ bool IsFull() const { return size_ == buffer_.size(); }
+
+ size_t GetSize() const { return size_; }
+
+ size_t GetCapacity() const { return buffer_.size(); }
+
+ T& Get(size_t i) { return buffer_[(start_ + i) % buffer_.size()]; }
+
+ const T& Get(size_t i) const {
+ return buffer_[(start_ + i) % buffer_.size()];
+ }
+
+ const T& Back() const { return Get(size_ - 1); }
+
+ T& Back() { return Get(size_ - 1); }
+
+ const T& Front() const { return Get(0); }
+
+ T& Front() { return Get(0); }
+
+ void PopBack() {
+ if (size_ != 0) {
+ Get(size_ - 1) = T();
+ size_--;
+ }
+ }
+
+ void PopFront() {
+ if (size_ != 0) {
+ Get(0) = T();
+ start_ = (start_ + 1) % buffer_.size();
+ size_--;
+ }
+ }
+
+ void Clear() { Reset(GetCapacity()); }
+
+ void Reset(size_t capacity) {
+ start_ = size_ = 0;
+ buffer_.clear();
+ buffer_.resize(capacity);
+ }
+
+ private:
+ // Ideally we'd allocate our own memory and use placement new to instantiate
+ // instances of T instead of using a vector, but the vector is simpler.
+ std::vector<T> buffer_;
+ size_t start_, size_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_RING_BUFFER_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/sync_util.h b/libs/vr/libdvrcommon/include/private/dvr/sync_util.h
new file mode 100644
index 0000000..c6911bc
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/sync_util.h
@@ -0,0 +1,31 @@
+#ifndef ANDROID_DVR_SYNC_UTIL_H_
+#define ANDROID_DVR_SYNC_UTIL_H_
+
+#include <cstdint>
+#include <type_traits>
+
+namespace android {
+namespace dvr {
+
+constexpr size_t kFenceInfoBufferSize = 4096;
+
+// This buffer is eventually mapped to a sync_fence_info_data struct (from
+// sync.h), whose largest member is a uint32_t. We align to 8 bytes to be extra
+// cautious.
+using FenceInfoBuffer = std::aligned_storage<kFenceInfoBufferSize, 8>::type;
+
+// Get fence info. Internally this works just like sync_fence_info(), except the
+// caller supplies a memory buffer instead of allocating memory.
+// On success, returns 0. On error, -1 is returned, and errno is set.
+int GetSyncFenceInfo(int fence_fd, FenceInfoBuffer* buffer);
+
+// Returns the timestamp when the fence was first signaled. buffer is used as
+// described in GetSyncFenceInfo().
+// On success, returns 0. On error, -1 is returned, and errno is set.
+int GetFenceSignaledTimestamp(int fence_fd, FenceInfoBuffer* buffer,
+ int64_t* timestamp);
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_SYNC_UTIL_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/test/test_macros.h b/libs/vr/libdvrcommon/include/private/dvr/test/test_macros.h
new file mode 100644
index 0000000..6048652
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/test/test_macros.h
@@ -0,0 +1,124 @@
+#ifndef LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_TEST_TEST_MACROS_H_
+#define LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_TEST_TEST_MACROS_H_
+
+#include <gtest/gtest.h>
+
+#include <cmath>
+
+#include <private/dvr/numeric.h>
+
+namespace android {
+namespace dvr {
+
+template <int N, typename A, typename B, typename T>
+::testing::AssertionResult CmpArrayLikeFloatEq(
+ const char* expectedStr, const char* actualStr, const char* toleranceStr,
+ const A& expected, const B& actual, const T& tolerance) {
+ for (int i = 0; i < N; ++i) {
+ if (!IsEqual(expected[i], actual[i], tolerance)) {
+ return ::testing::AssertionFailure()
+ << "\"" << expectedStr << "\" and \"" << actualStr
+ << "\" differ at element " << i << " by at least " << tolerance
+ << " : "
+ << " Expected \"" << expected[i] << "\", was \"" << actual[i]
+ << "\".";
+ }
+ }
+
+ return ::testing::AssertionSuccess();
+}
+
+template <int N, typename A, typename B, typename T>
+::testing::AssertionResult CmpMatrixLikeFloatEq(
+ const char* expectedStr, const char* actualStr, const char* toleranceStr,
+ const A& expected, const B& actual, const T& tolerance) {
+ for (int r = 0; r < N; ++r) {
+ for (int c = 0; c < N; ++c) {
+ if (!IsEqual(expected(r, c), actual(r, c), tolerance)) {
+ return ::testing::AssertionFailure()
+ << "\"" << expectedStr << "\" and \"" << actualStr
+ << "\" differ at (" << r << "," << c << ")"
+ << " by at least " << tolerance << " : "
+ << " Expected \"" << expected(r, c) << "\", was \""
+ << actual(r, c) << "\".";
+ }
+ }
+ }
+
+ return ::testing::AssertionSuccess();
+}
+
+template <int N, typename A, typename B, typename T>
+::testing::AssertionResult CmpArrayLikeFloatNe(
+ const char* expectedStr, const char* actualStr, const char* toleranceStr,
+ const A& expected, const B& actual, const T& tolerance) {
+ for (int i = 0; i < N; ++i) {
+ if (!IsEqual(expected[i], actual[i], tolerance)) {
+ return ::testing::AssertionSuccess();
+ }
+ }
+
+ ::testing::Message message;
+ message << "Expected \"" << expectedStr
+ << "\" to differ from provided value \"" << actualStr
+ << "\" by at least " << tolerance << ".";
+
+ return ::testing::AssertionFailure(message);
+}
+
+template <int N, typename A, typename B, typename T>
+::testing::AssertionResult CmpMatrixLikeFloatNe(
+ const char* expectedStr, const char* actualStr, const char* toleranceStr,
+ const A& expected, const B& actual, const T& tolerance) {
+ for (int r = 0; r < N; ++r) {
+ for (int c = 0; c < N; ++c) {
+ if (!IsEqual(expected(r, c), actual(r, c), tolerance)) {
+ return ::testing::AssertionSuccess();
+ }
+ }
+ }
+
+ ::testing::Message message;
+ message << "Expected \"" << expectedStr
+ << "\" to differ from provided value \"" << actualStr
+ << "\" by at least " << tolerance << ".";
+
+ return ::testing::AssertionFailure(message);
+}
+
+} // namespace dvr
+} // namespace android
+
+#define EXPECT_VEC3_NEAR(expected, actual, tol) \
+ EXPECT_PRED_FORMAT3(android::dvr::CmpArrayLikeFloatEq<3>, expected, actual, \
+ tol)
+
+#define EXPECT_VEC3_NOT_NEAR(expected, actual, tol) \
+ EXPECT_PRED_FORMAT3(android::dvr::CmpArrayLikeFloatNe<3>, expected, actual, \
+ tol)
+
+#define EXPECT_QUAT_NEAR(expected, actual, tol) \
+ EXPECT_PRED_FORMAT3(android::dvr::CmpArrayLikeFloatEq<3>, expected.coeffs(), \
+ actual.coeffs(), tol)
+
+#define EXPECT_QUAT_NOT_NEAR(expected, actual, tol) \
+ EXPECT_PRED_FORMAT3(android::dvr::CmpArrayLikeFloatNe<3>, expected.coeffs(), \
+ actual.coeffs(), tol)
+
+#define EXPECT_MAT4_NEAR(expected, actual, tol) \
+ EXPECT_PRED_FORMAT3(android::dvr::CmpMatrixLikeFloatEq<4>, expected, actual, \
+ tol)
+
+#define EXPECT_MAT4_NOT_NEAR(expected, actual, tol) \
+ EXPECT_PRED_FORMAT3(android::dvr::CmpMatrixLikeFloatNe<4>, expected, actual, \
+ tol)
+
+#define EXPECT_MAT3_NEAR(expected, actual, tol) \
+ EXPECT_PRED_FORMAT3(android::dvr \
+ : CmpMatrixLikeFloatEq<3>, expected, actual, tol)
+
+#define EXPECT_MAT3_NOT_NEAR(expected, actual, tol) \
+ EXPECT_PRED_FORMAT3(android::dvr::CmpMatrixLikeFloatNe<3>, expected, actual, \
+ tol)
+
+#endif // LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_TEST_TEST_MACROS_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/types.h b/libs/vr/libdvrcommon/include/private/dvr/types.h
new file mode 100644
index 0000000..1fa54af
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/types.h
@@ -0,0 +1,51 @@
+#ifndef ANDROID_DVR_TYPES_H_
+#define ANDROID_DVR_TYPES_H_
+
+// All basic types used by VR code.
+
+#include <private/dvr/eigen.h>
+#include <private/dvr/field_of_view.h>
+#include <private/dvr/pose.h>
+#include <private/dvr/range.h>
+
+namespace android {
+namespace dvr {
+
+enum RgbColorChannel { kRed, kGreen, kBlue };
+
+// EyeType: 0 for left, 1 for right.
+enum EyeType { kLeftEye = 0, kRightEye = 1 };
+
+// In the context of VR, vector types are used as much as base types.
+
+using vec2f = Eigen::Vector2f;
+using vec2d = Eigen::Vector2d;
+using vec2i = Eigen::Vector2i;
+using vec2 = vec2f;
+
+using vec3f = Eigen::Vector3f;
+using vec3d = Eigen::Vector3d;
+using vec3i = Eigen::Vector3i;
+using vec3 = vec3f;
+
+using vec4f = Eigen::Vector4f;
+using vec4d = Eigen::Vector4d;
+using vec4i = Eigen::Vector4i;
+using vec4 = vec4f;
+
+using mat3f = Eigen::AffineMatrix<float, 3>;
+using mat3d = Eigen::AffineMatrix<double, 3>;
+using mat3 = mat3f;
+
+using mat4f = Eigen::AffineMatrix<float, 4>;
+using mat4d = Eigen::AffineMatrix<double, 4>;
+using mat4 = mat4f;
+
+using quatf = Eigen::Quaternionf;
+using quatd = Eigen::Quaterniond;
+using quat = quatf;
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_TYPES_H_
diff --git a/libs/vr/libdvrcommon/revision.cpp b/libs/vr/libdvrcommon/revision.cpp
new file mode 100644
index 0000000..ae8603f
--- /dev/null
+++ b/libs/vr/libdvrcommon/revision.cpp
@@ -0,0 +1,175 @@
+#include "private/dvr/revision.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <base/logging.h>
+
+#include "revision_path.h"
+
+namespace {
+
+// Allows quicker access to the product revision. If non-zero, then
+// the product revision file has already been processed.
+static bool global_product_revision_processed = false;
+
+static bool global_serial_number_processed = false;
+
+// The product.
+static DvrProduct global_product = DVR_PRODUCT_UNKNOWN;
+
+// The revision.
+static DvrRevision global_revision = DVR_REVISION_UNKNOWN;
+
+// Maximum size of the product revision string.
+constexpr int kProductRevisionStringSize = 32;
+
+// Maximum size of the serial number.
+constexpr int kSerialNumberStringSize = 32;
+
+// The product revision string.
+static char global_product_revision_str[kProductRevisionStringSize + 1] = "";
+
+// The serial number string
+static char global_serial_number[kSerialNumberStringSize + 1] = "";
+
+// Product and revision combinations.
+struct DvrProductRevision {
+ const char* str;
+ DvrProduct product;
+ DvrRevision revision;
+};
+
+// Null-terminated list of all product and revision combinations.
+static constexpr DvrProductRevision kProductRevisions[] = {
+ {"a00-p1", DVR_PRODUCT_A00, DVR_REVISION_P1},
+ {"a00-p2", DVR_PRODUCT_A00, DVR_REVISION_P2},
+ {"a00-p3", DVR_PRODUCT_A00, DVR_REVISION_P3},
+ {"twilight-p1", DVR_PRODUCT_A65R, DVR_REVISION_P1},
+ {"twilight-p2", DVR_PRODUCT_A65R, DVR_REVISION_P2},
+ {NULL, DVR_PRODUCT_UNKNOWN, DVR_REVISION_UNKNOWN}};
+
+// Read the product revision string, and store the global data.
+static void process_product_revision() {
+ int fd;
+ ssize_t read_rc;
+ const DvrProductRevision* product_revision = kProductRevisions;
+
+ // Of course in a multi-threaded environment, for a few microseconds
+ // during process startup, it is possible that this function will be
+ // called and execute fully multiple times. That is why the product
+ // revision string is statically allocated.
+
+ if (global_product_revision_processed)
+ return;
+
+ // Whether there was a failure or not, we don't want to do this again.
+ // Upon failure it's most likely to fail again anyway.
+
+ fd = open(dvr_product_revision_file_path(), O_RDONLY);
+ if (fd < 0) {
+ PLOG(ERROR) << "Could not open '" << dvr_product_revision_file_path()
+ << "' to get product revision";
+ global_product_revision_processed = true;
+ return;
+ }
+
+ read_rc = read(fd, global_product_revision_str, kProductRevisionStringSize);
+ if (read_rc <= 0) {
+ PLOG(ERROR) << "Could not read from '" << dvr_product_revision_file_path()
+ << "'";
+ global_product_revision_processed = true;
+ return;
+ }
+
+ close(fd);
+
+ global_product_revision_str[read_rc] = '\0';
+
+ while (product_revision->str) {
+ if (!strcmp(product_revision->str, global_product_revision_str))
+ break;
+ product_revision++;
+ }
+
+ if (product_revision->str) {
+ global_product = product_revision->product;
+ global_revision = product_revision->revision;
+ } else {
+ LOG(ERROR) << "Unable to match '" << global_product_revision_str
+ << "' to a product/revision.";
+ }
+
+ global_product_revision_processed = true;
+}
+
+} // anonymous namespace
+
+extern "C" DvrProduct dvr_get_product() {
+ process_product_revision();
+ return global_product;
+}
+
+extern "C" DvrRevision dvr_get_revision() {
+ process_product_revision();
+ return global_revision;
+}
+
+extern "C" const char* dvr_get_product_revision_str() {
+ process_product_revision();
+ return global_product_revision_str;
+}
+
+extern "C" const char* dvr_get_serial_number() {
+ process_product_revision();
+ if (global_product == DVR_PRODUCT_A00) {
+ if (!global_serial_number_processed) {
+#ifdef DVR_HOST
+ global_serial_number_processed = true;
+#else
+ int width = 4;
+ uintptr_t addr = 0x00074138;
+ uintptr_t endaddr = addr + width - 1;
+
+ int fd = open("/dev/mem", O_RDWR | O_SYNC);
+ if (fd < 0) {
+ if (errno == EPERM)
+ global_serial_number_processed = true;
+ fprintf(stderr, "cannot open /dev/mem\n");
+ return global_serial_number;
+ }
+
+ off64_t mmap_start = addr & ~(PAGE_SIZE - 1);
+ size_t mmap_size = endaddr - mmap_start + 1;
+ mmap_size = (mmap_size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
+
+ void* page = mmap64(0, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
+ mmap_start);
+
+ if (page == MAP_FAILED) {
+ global_serial_number_processed = true;
+ fprintf(stderr, "cannot mmap region\n");
+ close(fd);
+ return global_serial_number;
+ }
+
+ uint32_t* x =
+ reinterpret_cast<uint32_t*>((((uintptr_t)page) + (addr & 4095)));
+ snprintf(global_serial_number, kSerialNumberStringSize, "%08x", *x);
+ global_serial_number_processed = true;
+
+ munmap(page, mmap_size);
+ close(fd);
+#endif
+ }
+ return global_serial_number;
+ } else {
+ return nullptr;
+ }
+}
diff --git a/libs/vr/libdvrcommon/revision_path.cpp b/libs/vr/libdvrcommon/revision_path.cpp
new file mode 100644
index 0000000..c49f9aa
--- /dev/null
+++ b/libs/vr/libdvrcommon/revision_path.cpp
@@ -0,0 +1,15 @@
+#include "revision_path.h"
+
+namespace {
+
+// The path to the product revision file.
+static const char* kProductRevisionFilePath =
+ "/sys/firmware/devicetree/base/goog,board-revision";
+
+} // anonymous namespace
+
+// This exists in a separate file so that it can be replaced for
+// testing.
+const char* dvr_product_revision_file_path() {
+ return kProductRevisionFilePath;
+}
diff --git a/libs/vr/libdvrcommon/revision_path.h b/libs/vr/libdvrcommon/revision_path.h
new file mode 100644
index 0000000..afcea46
--- /dev/null
+++ b/libs/vr/libdvrcommon/revision_path.h
@@ -0,0 +1,9 @@
+#ifndef ANDROID_DVR_LIBDVRCOMMON_REVISION_PATH_H_
+#define ANDROID_DVR_LIBDVRCOMMON_REVISION_PATH_H_
+
+// Returns the revision file path.
+// This exists in a separate file so that it can be replaced for
+// testing.
+const char* dvr_product_revision_file_path();
+
+#endif // ANDROID_DVR_LIBDVRCOMMON_REVISION_PATH_H_
diff --git a/libs/vr/libdvrcommon/sync_util.cpp b/libs/vr/libdvrcommon/sync_util.cpp
new file mode 100644
index 0000000..3637936
--- /dev/null
+++ b/libs/vr/libdvrcommon/sync_util.cpp
@@ -0,0 +1,87 @@
+#include "include/private/dvr/sync_util.h"
+
+#include <errno.h>
+#include <sys/ioctl.h>
+
+// TODO: http://b/33239638 Move GetSyncFenceInfo() into upstream libsync instead
+// of duplicating functionality and structure definitions from it.
+struct sync_fence_info_data {
+ uint32_t len;
+ char name[32];
+ int32_t status;
+ uint8_t pt_info[0];
+};
+
+struct sync_pt_info {
+ uint32_t len;
+ char obj_name[32];
+ char driver_name[32];
+ int32_t status;
+ uint64_t timestamp_ns;
+ uint8_t driver_data[0];
+};
+
+#define SYNC_IOC_MAGIC '>'
+#define SYNC_IOC_WAIT _IOW(SYNC_IOC_MAGIC, 0, __s32)
+#define SYNC_IOC_MERGE _IOWR(SYNC_IOC_MAGIC, 1, struct sync_merge_data)
+#define SYNC_IOC_FENCE_INFO _IOWR(SYNC_IOC_MAGIC, 2, struct sync_fence_info_data)
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+// This is copied from sync_pt_info() in libsync/sync.c. It's been cleaned up to
+// remove lint warnings.
+sync_pt_info* GetSyncPtInfo(sync_fence_info_data* info, sync_pt_info* itr) {
+ if (itr == nullptr)
+ itr = reinterpret_cast<sync_pt_info*>(info->pt_info);
+ else
+ itr = reinterpret_cast<sync_pt_info*>(reinterpret_cast<uint8_t*>(itr) +
+ itr->len);
+
+ if (reinterpret_cast<uint8_t*>(itr) - reinterpret_cast<uint8_t*>(info) >=
+ static_cast<int>(info->len))
+ return nullptr;
+
+ return itr;
+}
+
+} // namespace
+
+int GetSyncFenceInfo(int fence_fd, FenceInfoBuffer* buffer) {
+ // If the implementation of sync_fence_info() in libsync/sync.c changes, this
+ // function should be changed to match.
+ if (buffer == nullptr) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ sync_fence_info_data* fence_info =
+ reinterpret_cast<sync_fence_info_data*>(buffer);
+ fence_info->len = kFenceInfoBufferSize;
+ return ioctl(fence_fd, SYNC_IOC_FENCE_INFO, fence_info);
+}
+
+int GetFenceSignaledTimestamp(int fence_fd, FenceInfoBuffer* buffer,
+ int64_t* timestamp) {
+ int result = GetSyncFenceInfo(fence_fd, buffer);
+ if (result < 0)
+ return result;
+
+ sync_fence_info_data* fence_info =
+ reinterpret_cast<sync_fence_info_data*>(buffer);
+ struct sync_pt_info* pt_info = nullptr;
+ while ((pt_info = GetSyncPtInfo(fence_info, pt_info)) != nullptr) {
+ if (pt_info->status == 1) {
+ *timestamp = pt_info->timestamp_ns;
+ return 0;
+ }
+ }
+
+ errno = EAGAIN;
+ return -1;
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libdvrcommon/tests/numeric_test.cpp b/libs/vr/libdvrcommon/tests/numeric_test.cpp
new file mode 100644
index 0000000..1ee1447
--- /dev/null
+++ b/libs/vr/libdvrcommon/tests/numeric_test.cpp
@@ -0,0 +1,67 @@
+#include <gtest/gtest.h>
+
+#include <private/dvr/numeric.h>
+
+using TestTypes = ::testing::Types<float, double, int>;
+
+using android::dvr::RandomInRange;
+
+template <typename T>
+class NumericTest : public ::testing::TestWithParam<T> {
+ public:
+ using FT = T;
+};
+
+TYPED_TEST_CASE(NumericTest, TestTypes);
+
+TYPED_TEST(NumericTest, RandomInRange) {
+ using FT = typename TestFixture::FT;
+
+ const int kNumTrials = 50;
+ const FT kLowRange = static_cast<FT>(-100);
+ const FT kHighRange = static_cast<FT>(100);
+
+ for (int i = 0; i < kNumTrials; ++i) {
+ FT value = RandomInRange(kLowRange, kHighRange);
+
+ EXPECT_LE(kLowRange, value);
+ EXPECT_GE(kHighRange, value);
+ }
+}
+
+TEST(RandomInRange, TestIntVersion) {
+ // This checks specifically that the function does not always give the lo
+ // value (this was previously a bug)
+
+ const int kNumTrials = 50;
+ const int kLowRange = -100;
+ const int kHighRange = 100;
+
+ for (int i = 0; i < kNumTrials; ++i) {
+ int value = RandomInRange(kLowRange, kHighRange);
+
+ if (value != kLowRange) {
+ SUCCEED();
+ return;
+ }
+ }
+
+ FAIL() << "Did not produce a value other than the range minimum for "
+ << "integers.";
+}
+
+TEST(RandomInRange, TestVectorVersion) {
+ Eigen::Vector3d lo(-3.0, -4.0, -5.0);
+ Eigen::Vector3d hi(5.0, 4.0, 3.0);
+
+ const int kNumTrials = 50;
+
+ for (int i = 0; i < kNumTrials; ++i) {
+ Eigen::Vector3d result = RandomInRange(lo, hi);
+
+ for (int j = 0; j < 3; ++j) {
+ EXPECT_LE(lo[j], result[j]);
+ EXPECT_GE(hi[j], result[j]);
+ }
+ }
+}
diff --git a/libs/vr/libdvrcommon/tests/pose_test.cpp b/libs/vr/libdvrcommon/tests/pose_test.cpp
new file mode 100644
index 0000000..aa1896d
--- /dev/null
+++ b/libs/vr/libdvrcommon/tests/pose_test.cpp
@@ -0,0 +1,154 @@
+#include <gtest/gtest.h>
+
+#include <private/dvr/eigen.h>
+#include <private/dvr/pose.h>
+#include <private/dvr/test/test_macros.h>
+
+using PoseTypes = ::testing::Types<float, double>;
+
+template <class T>
+class PoseTest : public ::testing::TestWithParam<T> {
+ public:
+ using FT = T;
+ using Pose_t = android::dvr::Pose<FT>;
+ using quat_t = Eigen::Quaternion<FT>;
+ using vec3_t = Eigen::Vector3<FT>;
+ using mat4_t = Eigen::AffineMatrix<FT, 4>;
+};
+
+TYPED_TEST_CASE(PoseTest, PoseTypes);
+
+// Check that the two matrix methods are inverses of each other
+TYPED_TEST(PoseTest, SelfInverse) {
+ using quat_t = typename TestFixture::quat_t;
+ using vec3_t = typename TestFixture::vec3_t;
+ using Pose_t = typename TestFixture::Pose_t;
+ using mat4_t = typename TestFixture::mat4_t;
+ using FT = typename TestFixture::FT;
+
+ const auto tolerance = FT(0.0001);
+
+ const quat_t initial_rotation(Eigen::AngleAxis<FT>(
+ FT(M_PI / 3.0), vec3_t(FT(3.0), FT(4.0), FT(5.0)).normalized()));
+ const vec3_t initial_position = vec3_t(FT(2.0), FT(10.0), FT(-4.0));
+ const Pose_t initial_pose(initial_rotation, initial_position);
+
+ auto result_pose = initial_pose.GetReferenceFromObjectMatrix() *
+ initial_pose.GetObjectFromReferenceMatrix();
+
+ EXPECT_MAT4_NEAR(result_pose, mat4_t::Identity(), tolerance);
+}
+
+TYPED_TEST(PoseTest, TransformPoint) {
+ using quat_t = typename TestFixture::quat_t;
+ using vec3_t = typename TestFixture::vec3_t;
+ using Pose_t = typename TestFixture::Pose_t;
+ using FT = typename TestFixture::FT;
+
+ const auto tolerance = FT(0.0001);
+
+ const quat_t pose_rotation(
+ Eigen::AngleAxis<FT>(FT(M_PI / 2.0), vec3_t(FT(0.0), FT(0.0), FT(1.0))));
+ const auto pose_position = vec3_t(FT(1.0), FT(1.0), FT(2.0));
+
+ const Pose_t test_pose(pose_rotation, pose_position);
+
+ for (int axis = 0; axis < 3; ++axis) {
+ vec3_t start_position = vec3_t::Zero();
+ start_position[axis] = FT(1.0);
+ const vec3_t expected_transformed =
+ (pose_rotation * start_position) + pose_position;
+ const vec3_t actual_transformed = test_pose.TransformPoint(start_position);
+ EXPECT_VEC3_NEAR(expected_transformed, actual_transformed, tolerance);
+ }
+}
+
+TYPED_TEST(PoseTest, TransformVector) {
+ using quat_t = typename TestFixture::quat_t;
+ using vec3_t = typename TestFixture::vec3_t;
+ using Pose_t = typename TestFixture::Pose_t;
+ using FT = typename TestFixture::FT;
+
+ const auto tolerance = FT(0.0001);
+
+ const quat_t pose_rotation(Eigen::AngleAxis<FT>(
+ FT(M_PI / 6.0), vec3_t(FT(3.0), FT(4.0), FT(5.0)).normalized()));
+
+ const auto pose_position = vec3_t(FT(500.0), FT(-500.0), FT(300.0));
+
+ const Pose_t test_pose(pose_rotation, pose_position);
+
+ for (int axis = 0; axis < 3; ++axis) {
+ vec3_t start_position = vec3_t::Zero();
+ start_position[axis] = FT(1.0);
+ const vec3_t expected_rotated = pose_rotation * start_position;
+ const vec3_t actual_rotated = test_pose.Transform(start_position);
+ EXPECT_VEC3_NEAR(expected_rotated, actual_rotated, tolerance);
+ }
+}
+
+TYPED_TEST(PoseTest, Composition) {
+ using quat_t = typename TestFixture::quat_t;
+ using Pose_t = typename TestFixture::Pose_t;
+ using vec3_t = typename TestFixture::vec3_t;
+ using FT = typename TestFixture::FT;
+
+ const auto tolerance = FT(0.0001);
+
+ const quat_t first_rotation(
+ Eigen::AngleAxis<FT>(FT(M_PI / 2.0), vec3_t(FT(0.0), FT(0.0), FT(1.0))));
+ const auto first_offset = vec3_t(FT(-3.0), FT(2.0), FT(-1.0));
+ const quat_t second_rotation(Eigen::AngleAxis<FT>(
+ FT(M_PI / 3.0), vec3_t(FT(1.0), FT(-1.0), FT(0.0)).normalized()));
+ const auto second_offset = vec3_t(FT(6.0), FT(-7.0), FT(-8.0));
+
+ const Pose_t first_pose(first_rotation, first_offset);
+ const Pose_t second_pose(second_rotation, second_offset);
+
+ const auto combined_pose(second_pose.Compose(first_pose));
+
+ for (int axis = 0; axis < 3; ++axis) {
+ vec3_t start_position = vec3_t::Zero();
+ start_position[axis] = FT(1.0);
+ const vec3_t expected_transformed =
+ second_pose.TransformPoint(first_pose.TransformPoint(start_position));
+ const vec3_t actual_transformed =
+ combined_pose.TransformPoint(start_position);
+ EXPECT_VEC3_NEAR(expected_transformed, actual_transformed, tolerance);
+ }
+}
+
+TYPED_TEST(PoseTest, Inverse) {
+ using quat_t = typename TestFixture::quat_t;
+ using vec3_t = typename TestFixture::vec3_t;
+ using Pose_t = typename TestFixture::Pose_t;
+ using FT = typename TestFixture::FT;
+
+ const auto tolerance = FT(0.0001);
+
+ const quat_t pose_rotation(Eigen::AngleAxis<FT>(
+ FT(M_PI / 2.0), vec3_t(FT(4.0), FT(-2.0), FT(-1.0)).normalized()));
+ const auto pose_position = vec3_t(FT(-1.0), FT(2.0), FT(-4.0));
+
+ Pose_t pose(pose_rotation, pose_position);
+ const Pose_t pose_inverse = pose.Inverse();
+
+ for (int axis = 0; axis < 3; ++axis) {
+ vec3_t start_position = vec3_t::Zero();
+ start_position[axis] = FT(1.0);
+ const vec3_t transformed = pose.Transform(start_position);
+ const vec3_t inverted = pose_inverse.Transform(transformed);
+ EXPECT_VEC3_NEAR(start_position, inverted, tolerance);
+ }
+
+ Pose_t nullified_pose[2] = {
+ pose.Compose(pose_inverse), pose_inverse.Compose(pose),
+ };
+
+ for (int i = 0; i < 2; ++i) {
+ EXPECT_QUAT_NEAR(quat_t::Identity(), nullified_pose[i].GetRotation(),
+ tolerance);
+ EXPECT_VEC3_NEAR(vec3_t::Zero(), nullified_pose[i].GetPosition(),
+ tolerance);
+ }
+}
diff --git a/libs/vr/libdvrcommon/tests/revision_app_tests.cpp b/libs/vr/libdvrcommon/tests/revision_app_tests.cpp
new file mode 100644
index 0000000..772481b
--- /dev/null
+++ b/libs/vr/libdvrcommon/tests/revision_app_tests.cpp
@@ -0,0 +1,34 @@
+#include <dvr/test/app_test.h>
+#include <gtest/gtest.h>
+#include <private/dvr/revision.h>
+
+// Making sure this information is not available
+// inside the sandbox
+
+namespace {
+
+TEST(RevisionTests, GetProduct) {
+ ASSERT_EQ(DVR_PRODUCT_UNKNOWN, dvr_get_product());
+}
+
+TEST(RevisionTests, GetRevision) {
+ ASSERT_EQ(DVR_REVISION_UNKNOWN, dvr_get_revision());
+}
+
+TEST(RevisionTests, GetRevisionStr) {
+ ASSERT_STREQ("", dvr_get_product_revision_str());
+}
+
+TEST(RevisionTests, GetSerialNo) {
+ ASSERT_EQ(nullptr, dvr_get_serial_number());
+}
+
+} // namespace
+
+int main(int argc, char* argv[]) {
+ dreamos::test::AppTestBegin();
+ ::testing::InitGoogleTest(&argc, argv);
+ int result = RUN_ALL_TESTS();
+ dreamos::test::AppTestEnd(result);
+ return result;
+}
diff --git a/libs/vr/libdvrcommon/tests/revision_tests.cpp b/libs/vr/libdvrcommon/tests/revision_tests.cpp
new file mode 100644
index 0000000..9abf480
--- /dev/null
+++ b/libs/vr/libdvrcommon/tests/revision_tests.cpp
@@ -0,0 +1,27 @@
+#include <gtest/gtest.h>
+#include <private/dvr/revision.h>
+
+namespace {
+
+TEST(RevisionTests, GetProduct) {
+ ASSERT_NE(DVR_PRODUCT_UNKNOWN, dvr_get_product());
+}
+
+TEST(RevisionTests, GetRevision) {
+ ASSERT_NE(DVR_REVISION_UNKNOWN, dvr_get_revision());
+}
+
+TEST(RevisionTests, GetRevisionStr) {
+ ASSERT_NE(nullptr, dvr_get_product_revision_str());
+}
+
+TEST(RevisionTests, GetSerialNo) {
+ ASSERT_NE(nullptr, dvr_get_serial_number());
+}
+
+} // namespace
+
+int main(int argc, char* argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}