Force vrhwc/wm to report the native resolution
Due to some Yak-shaving, this also involved a partial conversion of our
libraries to soong.
Notes:
* I also changed the name of libsensor to libvrsensor to avoid a naming
clash with an existing library
* The remaining libraries will be soongified in a separate CL
Bug: 36139334
Test: Build and run vr apps.
Change-Id: Ib39687bd01e3d2e1c30adc54e18f362a85954ab9
diff --git a/libs/vr/libvrsensor/Android.bp b/libs/vr/libvrsensor/Android.bp
new file mode 100644
index 0000000..376630e
--- /dev/null
+++ b/libs/vr/libvrsensor/Android.bp
@@ -0,0 +1,71 @@
+// Copyright (C) 2015 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.
+
+sourceFiles = [
+ "pose_client.cpp",
+ "sensor_client.cpp",
+]
+
+includeFiles = [
+ "include",
+]
+
+staticLibraries = [
+ "libbufferhub",
+ "libdvrcommon",
+ "libpdx_default_transport",
+]
+
+sharedLibraries = [
+ "libbase",
+ "libcutils",
+ "libhardware",
+ "liblog",
+ "libutils",
+]
+
+cc_library {
+ srcs: sourceFiles,
+ export_include_dirs: includeFiles,
+ static_libs: staticLibraries,
+ shared_libs: sharedLibraries,
+ name: "libvrsensor",
+}
+
+testFiles = ["tests/sensor_app_tests.cpp"]
+
+cc_test {
+ name: "sensor_app_tests",
+ tags: ["optional"],
+
+ srcs: testFiles,
+
+ shared_libs: [
+ "libEGL",
+ "libGLESv1_CM",
+ "libGLESv2",
+ "libvulkan",
+ "libsync",
+ ] + sharedLibraries,
+
+ static_libs: [
+ "libgmock_main",
+ "libgmock",
+ "libdisplay",
+ "libeds",
+ "libvrsensor",
+ "libdvrgraphics",
+ ] + staticLibraries,
+
+}
diff --git a/libs/vr/libvrsensor/include/CPPLINT.cfg b/libs/vr/libvrsensor/include/CPPLINT.cfg
new file mode 100644
index 0000000..2f8a3c0
--- /dev/null
+++ b/libs/vr/libvrsensor/include/CPPLINT.cfg
@@ -0,0 +1 @@
+filter=-build/header_guard
diff --git a/libs/vr/libvrsensor/include/dvr/pose_client.h b/libs/vr/libvrsensor/include/dvr/pose_client.h
new file mode 100644
index 0000000..ed75f84
--- /dev/null
+++ b/libs/vr/libvrsensor/include/dvr/pose_client.h
@@ -0,0 +1,205 @@
+#ifndef ANDROID_DVR_POSE_CLIENT_H_
+#define ANDROID_DVR_POSE_CLIENT_H_
+
+#ifdef __ARM_NEON
+#include <arm_neon.h>
+#else
+#ifndef __FLOAT32X4T_86
+#define __FLOAT32X4T_86
+typedef float float32x4_t __attribute__ ((__vector_size__ (16)));
+typedef struct float32x4x4_t { float32x4_t val[4]; };
+#endif
+#endif
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct DvrPose DvrPose;
+
+// Represents the current state provided by the pose service, containing a
+// rotation and translation.
+typedef struct __attribute__((packed, aligned(8))) DvrPoseState {
+ // A quaternion representing the rotation of the HMD in Start Space.
+ struct __attribute__((packed)) {
+ float x, y, z, w;
+ } head_from_start_rotation;
+ // The position of the HMD in Start Space.
+ struct __attribute__((packed)) {
+ float x, y, z;
+ } head_from_start_translation;
+ // Time in nanoseconds for the current pose.
+ uint64_t timestamp_ns;
+ // The rotational velocity of the HMD.
+ struct __attribute__((packed)) {
+ float x, y, z;
+ } sensor_from_start_rotation_velocity;
+} DvrPoseState;
+
+enum {
+ DVR_POSE_FLAG_VALID = (1UL << 0), // This pose is valid.
+ DVR_POSE_FLAG_HEAD = (1UL << 1), // This pose is the head.
+ DVR_POSE_FLAG_CONTROLLER = (1UL << 2), // This pose is a controller.
+};
+
+// Represents an estimated pose, accessed asynchronously through a shared ring
+// buffer. No assumptions should be made about the data in padding space.
+// The size of this struct is 128 bytes.
+typedef struct __attribute__((packed, aligned(16))) DvrPoseAsync {
+ // Left eye head-from-start orientation quaternion x,y,z,w.
+ float32x4_t orientation;
+ // Left eye head-from-start translation x,y,z,pad in meters.
+ float32x4_t translation;
+ // Right eye head-from-start orientation quaternion x,y,z,w.
+ float32x4_t right_orientation;
+ // Right eye head-from-start translation x,y,z,pad in meters.
+ float32x4_t right_translation;
+ // Start-space angular velocity x,y,z,pad in radians per second.
+ float32x4_t angular_velocity;
+ // Start-space positional velocity x,y,z,pad in meters per second.
+ float32x4_t velocity;
+ // Timestamp of when this pose is predicted for, typically halfway through
+ // scanout.
+ int64_t timestamp_ns;
+ // Bitmask of DVR_POSE_FLAG_* constants that apply to this pose.
+ //
+ // If DVR_POSE_FLAG_VALID is not set, the pose is indeterminate.
+ uint64_t flags;
+ // Reserved padding to 128 bytes.
+ uint8_t pad[16];
+} DvrPoseAsync;
+
+// Returned by the async pose ring buffer access API.
+typedef struct DvrPoseRingBufferInfo {
+ // Read-only pointer to the pose ring buffer. The current pose is in this
+ // buffer at element buffer[current_frame & (buffer_size - 1)]. The next
+ // frame's forecasted pose is at element
+ // ((current_frame + 1) & (buffer_size - 1)). And so on. The poses are
+ // predicted for when 50% of the corresponding frame's pixel data is visible
+ // to the user.
+ // The last value returned by dvrPresent is the count for the next frame,
+ // which is the earliest that the application could display something if they
+ // were to render promptly. (TODO(jbates) move this comment to dvrPresent).
+ volatile const DvrPoseAsync* buffer;
+ // Minimum number of accurate forecasted poses including the current frame's
+ // pose. This is the number of poses that are udpated by the pose service.
+ // If the application reads past this count, they will get a stale prediction
+ // from a previous frame. Guaranteed to be at least 2.
+ uint32_t min_future_count;
+ // Number of elements in buffer. At least 8 and greater than min_future_count.
+ // Guaranteed to be a power of two. The total size of the buffer in bytes is:
+ // total_count * sizeof(DvrPoseAsync)
+ uint32_t total_count;
+} DvrPoseRingBufferInfo;
+
+typedef enum DvrPoseMode {
+ DVR_POSE_MODE_6DOF = 0,
+ DVR_POSE_MODE_3DOF,
+ DVR_POSE_MODE_MOCK_FROZEN,
+ DVR_POSE_MODE_MOCK_HEAD_TURN_SLOW,
+ DVR_POSE_MODE_MOCK_HEAD_TURN_FAST,
+ DVR_POSE_MODE_MOCK_ROTATE_SLOW,
+ DVR_POSE_MODE_MOCK_ROTATE_MEDIUM,
+ DVR_POSE_MODE_MOCK_ROTATE_FAST,
+ DVR_POSE_MODE_MOCK_CIRCLE_STRAFE,
+
+ // Always last.
+ DVR_POSE_MODE_COUNT,
+} DvrPoseMode;
+
+typedef enum DvrControllerId {
+ DVR_CONTROLLER_0 = 0,
+ DVR_CONTROLLER_1 = 1,
+} DvrControllerId;
+
+// Creates a new pose client.
+//
+// @return Pointer to the created pose client, nullptr on failure.
+DvrPose* dvrPoseCreate();
+
+// Destroys a pose client.
+//
+// @param client Pointer to the pose client to be destroyed.
+void dvrPoseDestroy(DvrPose* client);
+
+// Gets the pose for the given vsync count.
+//
+// @param client Pointer to the pose client.
+// @param vsync_count Vsync that this pose should be forward-predicted to.
+// Typically this is the count returned by dvrGetNextVsyncCount.
+// @param out_pose Struct to store pose state.
+// @return Zero on success, negative error code on failure.
+int dvrPoseGet(DvrPose* client, uint32_t vsync_count, DvrPoseAsync* out_pose);
+
+// Gets the current vsync count.
+uint32_t dvrPoseGetVsyncCount(DvrPose* client);
+
+// Gets the pose for the given controller at the given vsync count.
+//
+// @param client Pointer to the pose client.
+// @param controller_id The controller id.
+// @param vsync_count Vsync that this pose should be forward-predicted to.
+// Typically this is the count returned by dvrGetNextVsyncCount.
+// @param out_pose Struct to store pose state.
+// @return Zero on success, negative error code on failure.
+int dvrPoseGetController(DvrPose* client, int32_t controller_id,
+ uint32_t vsync_count, DvrPoseAsync* out_pose);
+
+// Enables/disables logging for the controller fusion.
+//
+// @param client Pointer to the pose client.
+// @param enable True starts logging, False stops.
+// @return Zero on success, negative error code on failure.
+int dvrPoseLogController(DvrPose* client, bool enable);
+
+// DEPRECATED
+// Polls current pose state.
+//
+// @param client Pointer to the pose client.
+// @param state Struct to store polled state.
+// @return Zero on success, negative error code on failure.
+int dvrPosePoll(DvrPose* client, DvrPoseState* state);
+
+// Freezes the pose to the provided state.
+//
+// Future poll operations will return this state until a different state is
+// frozen or dvrPoseSetMode() is called with a different mode. The timestamp is
+// not frozen.
+//
+// @param client Pointer to the pose client.
+// @param frozen_state State pose to be frozen to.
+// @return Zero on success, negative error code on failure.
+int dvrPoseFreeze(DvrPose* client, const DvrPoseState* frozen_state);
+
+// Sets the pose service mode.
+//
+// @param mode The requested pose mode.
+// @return Zero on success, negative error code on failure.
+int dvrPoseSetMode(DvrPose* client, DvrPoseMode mode);
+
+// Gets the pose service mode.
+//
+// @param mode Return value for the current pose mode.
+// @return Zero on success, negative error code on failure.
+int dvrPoseGetMode(DvrPose* client, DvrPoseMode* mode);
+
+// Get access to the shared memory pose ring buffer.
+// A future pose at vsync <current> + <offset> is accessed at index:
+// index = (<current> + <offset>) % out_buffer_size
+// Where <current> was the last value returned by dvrPresent and
+// <offset> is less than or equal to |out_min_future_count|.
+// |out_buffer| will be set to a pointer to the buffer.
+// |out_fd| will be set to the gralloc buffer file descriptor, which is
+// required for binding this buffer for GPU use.
+// Returns 0 on success.
+int dvrPoseGetRingBuffer(DvrPose* client, DvrPoseRingBufferInfo* out_info);
+
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // ANDROID_DVR_POSE_CLIENT_H_
diff --git a/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h b/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h
new file mode 100644
index 0000000..0616d46
--- /dev/null
+++ b/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h
@@ -0,0 +1,28 @@
+#ifndef ANDROID_DVR_POSE_IPC_H_
+#define ANDROID_DVR_POSE_IPC_H_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DVR_POSE_SERVICE_BASE "system/vr/pose"
+#define DVR_POSE_SERVICE_CLIENT (DVR_POSE_SERVICE_BASE "/client")
+
+enum {
+ DVR_POSE_POLL = 0,
+ DVR_POSE_FREEZE,
+ DVR_POSE_SET_MODE,
+ DVR_POSE_GET_RING_BUFFER,
+ DVR_POSE_NOTIFY_VSYNC,
+ DVR_POSE_GET_MODE,
+ DVR_POSE_GET_CONTROLLER_RING_BUFFER,
+ DVR_POSE_LOG_CONTROLLER,
+};
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // ANDROID_DVR_POSE_IPC_H_
diff --git a/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h b/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h
new file mode 100644
index 0000000..66c4c7c
--- /dev/null
+++ b/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h
@@ -0,0 +1,43 @@
+#ifndef ANDROID_DVR_POSE_CLIENT_INTERNAL_H_
+#define ANDROID_DVR_POSE_CLIENT_INTERNAL_H_
+
+#include <stdint.h>
+
+#include <dvr/pose_client.h>
+#include <pdx/file_handle.h>
+#include <private/dvr/sensor_constants.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Sensord head pose ring buffer.
+typedef struct __attribute__((packed, aligned(16))) DvrPoseRingBuffer {
+ // Ring buffer always at the beginning of the structure, as consumers may
+ // not have access to this parent structure definition.
+ DvrPoseAsync ring[kPoseAsyncBufferTotalCount];
+ // Current vsync_count (where sensord is writing poses from).
+ uint32_t vsync_count;
+} DvrPoseMetadata;
+
+// Called by displayd to give vsync count info to the pose service.
+// |display_timestamp| Display timestamp is in the middle of scanout.
+// |display_period_ns| Nanos between vsyncs.
+// |right_eye_photon_offset_ns| Nanos to shift the prediction timestamp for
+// the right eye head pose (relative to the left eye prediction).
+int privateDvrPoseNotifyVsync(DvrPose* client, uint32_t vsync_count,
+ int64_t display_timestamp,
+ int64_t display_period_ns,
+ int64_t right_eye_photon_offset_ns);
+
+// Get file descriptor for access to the shared memory pose buffer. This can be
+// used with GL extensions that support shared memory buffer objects. The caller
+// takes ownership of the returned fd and must close it or pass on ownership.
+int privateDvrPoseGetRingBufferFd(DvrPose* client,
+ android::pdx::LocalHandle* fd);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // ANDROID_DVR_POSE_CLIENT_INTERNAL_H_
diff --git a/libs/vr/libvrsensor/include/private/dvr/sensor-ipc.h b/libs/vr/libvrsensor/include/private/dvr/sensor-ipc.h
new file mode 100644
index 0000000..b2ebd95
--- /dev/null
+++ b/libs/vr/libvrsensor/include/private/dvr/sensor-ipc.h
@@ -0,0 +1,17 @@
+#ifndef ANDROID_DVR_SENSOR_IPC_H_
+#define ANDROID_DVR_SENSOR_IPC_H_
+
+#define DVR_SENSOR_SERVICE_BASE "system/vr/sensors"
+
+#define DVR_SENSOR_SERVICE_CLIENT (DVR_SENSOR_SERVICE_BASE "/client")
+
+/*
+ * Endpoint ops
+ */
+enum {
+ DVR_SENSOR_START = 0,
+ DVR_SENSOR_STOP,
+ DVR_SENSOR_POLL,
+};
+
+#endif // ANDROID_DVR_SENSOR_IPC_H_
diff --git a/libs/vr/libvrsensor/include/private/dvr/sensor_client.h b/libs/vr/libvrsensor/include/private/dvr/sensor_client.h
new file mode 100644
index 0000000..15a9b8f
--- /dev/null
+++ b/libs/vr/libvrsensor/include/private/dvr/sensor_client.h
@@ -0,0 +1,37 @@
+#ifndef ANDROID_DVR_SENSOR_CLIENT_H_
+#define ANDROID_DVR_SENSOR_CLIENT_H_
+
+#include <hardware/sensors.h>
+#include <pdx/client.h>
+#include <poll.h>
+
+namespace android {
+namespace dvr {
+
+// SensorClient is a remote interface to the sensor service in sensord.
+class SensorClient : public pdx::ClientBase<SensorClient> {
+ public:
+ ~SensorClient();
+
+ int StartSensor();
+ int StopSensor();
+ int Poll(sensors_event_t* events, int max_count);
+
+ private:
+ friend BASE;
+
+ // Set up a channel associated with the sensor of the indicated type.
+ // NOTE(segal): If our hardware ends up with multiple sensors of the same
+ // type, we'll have to change this.
+ explicit SensorClient(int sensor_type);
+
+ int sensor_type_;
+
+ SensorClient(const SensorClient&);
+ SensorClient& operator=(const SensorClient&);
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_SENSOR_CLIENT_H_
diff --git a/libs/vr/libvrsensor/include/private/dvr/sensor_constants.h b/libs/vr/libvrsensor/include/private/dvr/sensor_constants.h
new file mode 100644
index 0000000..8fa87b3
--- /dev/null
+++ b/libs/vr/libvrsensor/include/private/dvr/sensor_constants.h
@@ -0,0 +1,23 @@
+#ifndef ANDROID_DVR_SENSOR_CONSTANTS_H_
+#define ANDROID_DVR_SENSOR_CONSTANTS_H_
+
+namespace android {
+namespace dvr {
+
+// Number of elements in the async pose buffer.
+// Must be power of two.
+// Macro so that shader code can easily include this value.
+#define kPoseAsyncBufferTotalCount 8
+
+// Mask for accessing the current ring buffer array element:
+// index = vsync_count & kPoseAsyncBufferIndexMask
+constexpr uint32_t kPoseAsyncBufferIndexMask = kPoseAsyncBufferTotalCount - 1;
+
+// Number of pose frames including the current frame that are kept updated with
+// pose forecast data. The other poses are left their last known estimates.
+constexpr uint32_t kPoseAsyncBufferMinFutureCount = 4;
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_SENSOR_CONSTANTS_H_
diff --git a/libs/vr/libvrsensor/pose_client.cpp b/libs/vr/libvrsensor/pose_client.cpp
new file mode 100644
index 0000000..9eae3aa
--- /dev/null
+++ b/libs/vr/libvrsensor/pose_client.cpp
@@ -0,0 +1,338 @@
+#define LOG_TAG "PoseClient"
+#include <dvr/pose_client.h>
+
+#include <stdint.h>
+
+#include <log/log.h>
+#include <pdx/client.h>
+#include <pdx/default_transport/client_channel_factory.h>
+#include <pdx/file_handle.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/pose-ipc.h>
+#include <private/dvr/pose_client_internal.h>
+#include <private/dvr/sensor_constants.h>
+
+using android::pdx::LocalHandle;
+using android::pdx::LocalChannelHandle;
+using android::pdx::Status;
+using android::pdx::Transaction;
+
+#define arraysize(x) (static_cast<int32_t>(std::extent<decltype(x)>::value))
+
+namespace android {
+namespace dvr {
+
+// PoseClient is a remote interface to the pose service in sensord.
+class PoseClient : public pdx::ClientBase<PoseClient> {
+ public:
+ ~PoseClient() override {}
+
+ // Casts C handle into an instance of this class.
+ static PoseClient* FromC(DvrPose* client) {
+ return reinterpret_cast<PoseClient*>(client);
+ }
+
+ // Polls the pose service for the current state and stores it in *state.
+ // Returns zero on success, a negative error code otherwise.
+ int Poll(DvrPoseState* state) {
+ Transaction trans{*this};
+ Status<int> status =
+ trans.Send<int>(DVR_POSE_POLL, nullptr, 0, state, sizeof(*state));
+ ALOGE_IF(!status, "Pose poll() failed because: %s\n",
+ status.GetErrorMessage().c_str());
+ return ReturnStatusOrError(status);
+ }
+
+ int GetPose(uint32_t vsync_count, DvrPoseAsync* out_pose) {
+ if (!mapped_pose_buffer_) {
+ int ret = GetRingBuffer(nullptr);
+ if (ret < 0)
+ return ret;
+ }
+ *out_pose =
+ mapped_pose_buffer_->ring[vsync_count & kPoseAsyncBufferIndexMask];
+ return 0;
+ }
+
+ uint32_t GetVsyncCount() {
+ if (!mapped_pose_buffer_) {
+ int ret = GetRingBuffer(nullptr);
+ if (ret < 0)
+ return 0;
+ }
+ return mapped_pose_buffer_->vsync_count;
+ }
+
+ int GetControllerPose(int32_t controller_id, uint32_t vsync_count,
+ DvrPoseAsync* out_pose) {
+ if (controller_id < 0 || controller_id >= arraysize(controllers_)) {
+ return -EINVAL;
+ }
+ if (!controllers_[controller_id].mapped_pose_buffer) {
+ int ret = GetControllerRingBuffer(controller_id);
+ if (ret < 0)
+ return ret;
+ }
+ *out_pose =
+ controllers_[controller_id]
+ .mapped_pose_buffer[vsync_count & kPoseAsyncBufferIndexMask];
+ return 0;
+ }
+
+ int LogController(bool enable) {
+ Transaction trans{*this};
+ Status<int> status = trans.Send<int>(DVR_POSE_LOG_CONTROLLER, &enable,
+ sizeof(enable), nullptr, 0);
+ ALOGE_IF(!status, "Pose LogController() failed because: %s",
+ status.GetErrorMessage().c_str());
+ return ReturnStatusOrError(status);
+ }
+
+ // Freezes the pose to the provided state. Future poll operations will return
+ // this state until a different state is frozen or SetMode() is called with a
+ // different mode.
+ // Returns zero on success, a negative error code otherwise.
+ int Freeze(const DvrPoseState& frozen_state) {
+ Transaction trans{*this};
+ Status<int> status = trans.Send<int>(DVR_POSE_FREEZE, &frozen_state,
+ sizeof(frozen_state), nullptr, 0);
+ ALOGE_IF(!status, "Pose Freeze() failed because: %s\n",
+ status.GetErrorMessage().c_str());
+ return ReturnStatusOrError(status);
+ }
+
+ // Sets the data mode for the pose service.
+ int SetMode(DvrPoseMode mode) {
+ Transaction trans{*this};
+ Status<int> status =
+ trans.Send<int>(DVR_POSE_SET_MODE, &mode, sizeof(mode), nullptr, 0);
+ ALOGE_IF(!status, "Pose SetPoseMode() failed because: %s",
+ status.GetErrorMessage().c_str());
+ return ReturnStatusOrError(status);
+ }
+
+ // Gets the data mode for the pose service.
+ int GetMode(DvrPoseMode* out_mode) {
+ int mode;
+ Transaction trans{*this};
+ Status<int> status =
+ trans.Send<int>(DVR_POSE_GET_MODE, nullptr, 0, &mode, sizeof(mode));
+ ALOGE_IF(!status, "Pose GetPoseMode() failed because: %s",
+ status.GetErrorMessage().c_str());
+ if (status)
+ *out_mode = DvrPoseMode(mode);
+ return ReturnStatusOrError(status);
+ }
+
+ int GetRingBuffer(DvrPoseRingBufferInfo* out_info) {
+ if (pose_buffer_.get()) {
+ if (out_info) {
+ GetPoseRingBufferInfo(out_info);
+ }
+ return 0;
+ }
+
+ Transaction trans{*this};
+ Status<LocalChannelHandle> status =
+ trans.Send<LocalChannelHandle>(DVR_POSE_GET_RING_BUFFER);
+ if (!status) {
+ ALOGE("Pose GetRingBuffer() failed because: %s",
+ status.GetErrorMessage().c_str());
+ return -status.error();
+ }
+
+ auto buffer = BufferConsumer::Import(status.take());
+ if (!buffer) {
+ ALOGE("Pose failed to import ring buffer");
+ return -EIO;
+ }
+ void* addr = nullptr;
+ int ret = buffer->GetBlobReadOnlyPointer(sizeof(DvrPoseRingBuffer), &addr);
+ if (ret < 0 || !addr) {
+ ALOGE("Pose failed to map ring buffer: ret:%d, addr:%p", ret, addr);
+ return -EIO;
+ }
+ pose_buffer_.swap(buffer);
+ mapped_pose_buffer_ = static_cast<const DvrPoseRingBuffer*>(addr);
+ ALOGI("Mapped pose data translation %f,%f,%f quat %f,%f,%f,%f",
+ mapped_pose_buffer_->ring[0].translation[0],
+ mapped_pose_buffer_->ring[0].translation[1],
+ mapped_pose_buffer_->ring[0].translation[2],
+ mapped_pose_buffer_->ring[0].orientation[0],
+ mapped_pose_buffer_->ring[0].orientation[1],
+ mapped_pose_buffer_->ring[0].orientation[2],
+ mapped_pose_buffer_->ring[0].orientation[3]);
+ if (out_info) {
+ GetPoseRingBufferInfo(out_info);
+ }
+ return 0;
+ }
+
+ int GetControllerRingBuffer(int32_t controller_id) {
+ if (controller_id < 0 || controller_id >= arraysize(controllers_)) {
+ return -EINVAL;
+ }
+ ControllerClientState& client_state = controllers_[controller_id];
+ if (client_state.pose_buffer.get()) {
+ return 0;
+ }
+
+ Transaction trans{*this};
+ Status<LocalChannelHandle> status = trans.Send<LocalChannelHandle>(
+ DVR_POSE_GET_CONTROLLER_RING_BUFFER, &controller_id,
+ sizeof(controller_id), nullptr, 0);
+ if (!status) {
+ return -status.error();
+ }
+
+ auto buffer = BufferConsumer::Import(status.take());
+ if (!buffer) {
+ ALOGE("Pose failed to import ring buffer");
+ return -EIO;
+ }
+ constexpr size_t size = kPoseAsyncBufferTotalCount * sizeof(DvrPoseAsync);
+ void* addr = nullptr;
+ int ret = buffer->GetBlobReadOnlyPointer(size, &addr);
+ if (ret < 0 || !addr) {
+ ALOGE("Pose failed to map ring buffer: ret:%d, addr:%p", ret, addr);
+ return -EIO;
+ }
+ client_state.pose_buffer.swap(buffer);
+ client_state.mapped_pose_buffer = static_cast<const DvrPoseAsync*>(addr);
+ ALOGI(
+ "Mapped controller %d pose data translation %f,%f,%f quat %f,%f,%f,%f",
+ controller_id, client_state.mapped_pose_buffer[0].translation[0],
+ client_state.mapped_pose_buffer[0].translation[1],
+ client_state.mapped_pose_buffer[0].translation[2],
+ client_state.mapped_pose_buffer[0].orientation[0],
+ client_state.mapped_pose_buffer[0].orientation[1],
+ client_state.mapped_pose_buffer[0].orientation[2],
+ client_state.mapped_pose_buffer[0].orientation[3]);
+ return 0;
+ }
+
+ int NotifyVsync(uint32_t vsync_count, int64_t display_timestamp,
+ int64_t display_period_ns,
+ int64_t right_eye_photon_offset_ns) {
+ const struct iovec data[] = {
+ {.iov_base = &vsync_count, .iov_len = sizeof(vsync_count)},
+ {.iov_base = &display_timestamp, .iov_len = sizeof(display_timestamp)},
+ {.iov_base = &display_period_ns, .iov_len = sizeof(display_period_ns)},
+ {.iov_base = &right_eye_photon_offset_ns,
+ .iov_len = sizeof(right_eye_photon_offset_ns)},
+ };
+ Transaction trans{*this};
+ Status<int> status =
+ trans.SendVector<int>(DVR_POSE_NOTIFY_VSYNC, data, nullptr);
+ ALOGE_IF(!status, "Pose NotifyVsync() failed because: %s\n",
+ status.GetErrorMessage().c_str());
+ return ReturnStatusOrError(status);
+ }
+
+ int GetRingBufferFd(LocalHandle* fd) {
+ int ret = GetRingBuffer(nullptr);
+ if (ret < 0)
+ return ret;
+ *fd = pose_buffer_->GetBlobFd();
+ return 0;
+ }
+
+ private:
+ friend BASE;
+
+ // Set up a channel to the pose service.
+ PoseClient()
+ : BASE(pdx::default_transport::ClientChannelFactory::Create(
+ DVR_POSE_SERVICE_CLIENT)) {
+ // TODO(eieio): Cache the pose and make timeout 0 so that the API doesn't
+ // block while waiting for the pose service to come back up.
+ EnableAutoReconnect(kInfiniteTimeout);
+ }
+
+ PoseClient(const PoseClient&) = delete;
+ PoseClient& operator=(const PoseClient&) = delete;
+
+ void GetPoseRingBufferInfo(DvrPoseRingBufferInfo* out_info) const {
+ out_info->min_future_count = kPoseAsyncBufferMinFutureCount;
+ out_info->total_count = kPoseAsyncBufferTotalCount;
+ out_info->buffer = mapped_pose_buffer_->ring;
+ }
+
+ std::unique_ptr<BufferConsumer> pose_buffer_;
+ const DvrPoseRingBuffer* mapped_pose_buffer_ = nullptr;
+
+ struct ControllerClientState {
+ std::unique_ptr<BufferConsumer> pose_buffer;
+ const DvrPoseAsync* mapped_pose_buffer = nullptr;
+ };
+ ControllerClientState controllers_[2];
+};
+
+} // namespace dvr
+} // namespace android
+
+using android::dvr::PoseClient;
+
+struct DvrPose {};
+
+extern "C" {
+
+DvrPose* dvrPoseCreate() {
+ PoseClient* client = PoseClient::Create().release();
+ return reinterpret_cast<DvrPose*>(client);
+}
+
+void dvrPoseDestroy(DvrPose* client) { delete PoseClient::FromC(client); }
+
+int dvrPoseGet(DvrPose* client, uint32_t vsync_count, DvrPoseAsync* out_pose) {
+ return PoseClient::FromC(client)->GetPose(vsync_count, out_pose);
+}
+
+uint32_t dvrPoseGetVsyncCount(DvrPose* client) {
+ return PoseClient::FromC(client)->GetVsyncCount();
+}
+
+int dvrPoseGetController(DvrPose* client, int32_t controller_id,
+ uint32_t vsync_count, DvrPoseAsync* out_pose) {
+ return PoseClient::FromC(client)->GetControllerPose(controller_id,
+ vsync_count, out_pose);
+}
+
+int dvrPoseLogController(DvrPose* client, bool enable) {
+ return PoseClient::FromC(client)->LogController(enable);
+}
+
+int dvrPosePoll(DvrPose* client, DvrPoseState* state) {
+ return PoseClient::FromC(client)->Poll(state);
+}
+
+int dvrPoseFreeze(DvrPose* client, const DvrPoseState* frozen_state) {
+ return PoseClient::FromC(client)->Freeze(*frozen_state);
+}
+
+int dvrPoseSetMode(DvrPose* client, DvrPoseMode mode) {
+ return PoseClient::FromC(client)->SetMode(mode);
+}
+
+int dvrPoseGetMode(DvrPose* client, DvrPoseMode* mode) {
+ return PoseClient::FromC(client)->GetMode(mode);
+}
+
+int dvrPoseGetRingBuffer(DvrPose* client, DvrPoseRingBufferInfo* out_info) {
+ return PoseClient::FromC(client)->GetRingBuffer(out_info);
+}
+
+int privateDvrPoseNotifyVsync(DvrPose* client, uint32_t vsync_count,
+ int64_t display_timestamp,
+ int64_t display_period_ns,
+ int64_t right_eye_photon_offset_ns) {
+ return PoseClient::FromC(client)->NotifyVsync(vsync_count, display_timestamp,
+ display_period_ns,
+ right_eye_photon_offset_ns);
+}
+
+int privateDvrPoseGetRingBufferFd(DvrPose* client, LocalHandle* fd) {
+ return PoseClient::FromC(client)->GetRingBufferFd(fd);
+}
+
+} // extern "C"
diff --git a/libs/vr/libvrsensor/sensor_client.cpp b/libs/vr/libvrsensor/sensor_client.cpp
new file mode 100644
index 0000000..04e88cc
--- /dev/null
+++ b/libs/vr/libvrsensor/sensor_client.cpp
@@ -0,0 +1,79 @@
+#define LOG_TAG "SensorClient"
+#include <private/dvr/sensor_client.h>
+
+#include <log/log.h>
+#include <poll.h>
+
+#include <pdx/default_transport/client_channel_factory.h>
+#include <private/dvr/sensor-ipc.h>
+
+using android::pdx::Transaction;
+
+namespace android {
+namespace dvr {
+
+SensorClient::SensorClient(int sensor_type)
+ : BASE(pdx::default_transport::ClientChannelFactory::Create(
+ DVR_SENSOR_SERVICE_CLIENT)),
+ sensor_type_(sensor_type) {}
+
+SensorClient::~SensorClient() {}
+
+int SensorClient::StartSensor() {
+ Transaction trans{*this};
+ auto status = trans.Send<int>(DVR_SENSOR_START, &sensor_type_,
+ sizeof(sensor_type_), nullptr, 0);
+ ALOGE_IF(!status, "startSensor() failed because: %s\n",
+ status.GetErrorMessage().c_str());
+ return ReturnStatusOrError(status);
+}
+
+int SensorClient::StopSensor() {
+ Transaction trans{*this};
+ auto status = trans.Send<int>(DVR_SENSOR_STOP);
+ ALOGE_IF(!status, "stopSensor() failed because: %s\n",
+ status.GetErrorMessage().c_str());
+ return ReturnStatusOrError(status);
+}
+
+int SensorClient::Poll(sensors_event_t* events, int max_events) {
+ int num_events = 0;
+ struct iovec rvec[] = {
+ {.iov_base = &num_events, .iov_len = sizeof(int)},
+ {.iov_base = events, .iov_len = max_events * sizeof(sensors_event_t)},
+ };
+ Transaction trans{*this};
+ auto status = trans.SendVector<int>(DVR_SENSOR_POLL, nullptr, rvec);
+ ALOGE_IF(!status, "Sensor poll() failed because: %s\n",
+ status.GetErrorMessage().c_str());
+ return !status ? -status.error() : num_events;
+}
+
+} // namespace dvr
+} // namespace android
+
+// Entrypoints to simplify using the library when programmatically dynamicly
+// loading it.
+// Allows us to call this library without linking it, as, for instance,
+// when compiling GVR in Google3.
+// NOTE(segal): It's kind of a hack.
+
+extern "C" uint64_t dvrStartSensor(int type) {
+ android::dvr::SensorClient* service =
+ android::dvr::SensorClient::Create(type).release();
+ service->StartSensor();
+ return (uint64_t)service;
+}
+
+extern "C" void dvrStopSensor(uint64_t service) {
+ android::dvr::SensorClient* iss =
+ reinterpret_cast<android::dvr::SensorClient*>(service);
+ iss->StopSensor();
+ delete iss;
+}
+
+extern "C" int dvrPollSensor(uint64_t service, int max_count,
+ sensors_event_t* events) {
+ return reinterpret_cast<android::dvr::SensorClient*>(service)->Poll(
+ events, max_count);
+}
diff --git a/libs/vr/libvrsensor/tests/sensor_app_tests.cpp b/libs/vr/libvrsensor/tests/sensor_app_tests.cpp
new file mode 100644
index 0000000..64c9864
--- /dev/null
+++ b/libs/vr/libvrsensor/tests/sensor_app_tests.cpp
@@ -0,0 +1,132 @@
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+#include <math.h>
+#include <inttypes.h>
+
+#include <dvr/graphics.h>
+#include <dvr/pose_client.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <private/dvr/types.h>
+
+using android::dvr::vec4;
+
+namespace {
+
+vec4 ToVec4(float32x4_t rhs) { return vec4(rhs[0], rhs[1], rhs[2], rhs[3]); }
+
+}
+
+DvrGraphicsContext* CreateContext() {
+ DvrGraphicsContext* context = nullptr;
+ int display_width = 0, display_height = 0;
+ int surface_width = 0, surface_height = 0;
+ float inter_lens_meters = 0.0f;
+ float left_fov[4] = {0.0f};
+ float right_fov[4] = {0.0f};
+ int disable_warp = 0;
+ DvrSurfaceParameter surface_params[] = {
+ DVR_SURFACE_PARAMETER_IN(DISABLE_DISTORTION, disable_warp),
+ DVR_SURFACE_PARAMETER_OUT(DISPLAY_WIDTH, &display_width),
+ DVR_SURFACE_PARAMETER_OUT(DISPLAY_HEIGHT, &display_height),
+ DVR_SURFACE_PARAMETER_OUT(SURFACE_WIDTH, &surface_width),
+ DVR_SURFACE_PARAMETER_OUT(SURFACE_HEIGHT, &surface_height),
+ DVR_SURFACE_PARAMETER_OUT(INTER_LENS_METERS, &inter_lens_meters),
+ DVR_SURFACE_PARAMETER_OUT(LEFT_FOV_LRBT, left_fov),
+ DVR_SURFACE_PARAMETER_OUT(RIGHT_FOV_LRBT, right_fov),
+ DVR_SURFACE_PARAMETER_LIST_END,
+ };
+ dvrGraphicsContextCreate(surface_params, &context);
+ return context;
+}
+
+TEST(SensorAppTests, GetPose) {
+ DvrGraphicsContext* context = CreateContext();
+ ASSERT_NE(nullptr, context);
+ DvrPose* client = dvrPoseCreate();
+ ASSERT_NE(nullptr, client);
+
+ DvrPoseAsync last_pose;
+ uint32_t last_vsync_count = 0;
+ for (int i = 0; i < 10; ++i) {
+ DvrFrameSchedule schedule;
+ dvrGraphicsWaitNextFrame(context, 0, &schedule);
+ DvrPoseAsync pose;
+ int ret = dvrPoseGet(client, schedule.vsync_count, &pose);
+ ASSERT_EQ(0, ret);
+
+ // Check for unit-length quaternion to verify valid pose.
+ vec4 quaternion = ToVec4(pose.orientation);
+ float length = quaternion.norm();
+ EXPECT_GT(0.001, fabs(1.0f - length));
+
+ // Check for different data each frame, but skip first few to allow
+ // startup anomalies.
+ if (i > 0) {
+ if (last_vsync_count == schedule.vsync_count)
+ ALOGE("vsync did not increment: %u", schedule.vsync_count);
+ if (pose.timestamp_ns == last_pose.timestamp_ns)
+ ALOGE("timestamp did not change: %" PRIu64, pose.timestamp_ns);
+ // TODO(jbates) figure out why the bots are not passing this check.
+ // EXPECT_NE(last_vsync_count, schedule.vsync_count);
+ // EXPECT_NE(pose.timestamp_ns, last_pose.timestamp_ns);
+ }
+ last_pose = pose;
+ last_vsync_count = schedule.vsync_count;
+ dvrBeginRenderFrame(context);
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+ dvrPresent(context);
+ }
+
+ dvrPoseDestroy(client);
+ dvrGraphicsContextDestroy(context);
+}
+
+TEST(SensorAppTests, PoseRingBuffer) {
+ DvrGraphicsContext* context = CreateContext();
+ ASSERT_NE(nullptr, context);
+ DvrPose* client = dvrPoseCreate();
+ ASSERT_NE(nullptr, client);
+
+ DvrPoseRingBufferInfo info;
+ int ret = dvrPoseGetRingBuffer(client, &info);
+ ASSERT_EQ(0, ret);
+ ASSERT_NE(nullptr, info.buffer);
+ EXPECT_LE(2u, info.min_future_count);
+ EXPECT_LE(8u, info.total_count);
+
+ DvrPoseAsync last_pose;
+ uint32_t last_vsync_count = 0;
+ for (int i = 0; i < 10; ++i) {
+ DvrFrameSchedule schedule;
+ dvrGraphicsWaitNextFrame(context, 0, &schedule);
+ DvrPoseAsync pose;
+ ret = dvrPoseGet(client, schedule.vsync_count, &pose);
+ ASSERT_EQ(0, ret);
+
+ // Check for unit-length quaternion to verify valid pose.
+ vec4 quaternion = ToVec4(pose.orientation);
+ float length = quaternion.norm();
+ EXPECT_GT(0.001, fabs(1.0f - length));
+
+ // Check for different data each frame, but skip first few to allow
+ // startup anomalies.
+ if (i > 0) {
+ if (last_vsync_count == schedule.vsync_count)
+ ALOGE("vsync did not increment: %u", schedule.vsync_count);
+ if (pose.timestamp_ns == last_pose.timestamp_ns)
+ ALOGE("timestamp did not change: %" PRIu64, pose.timestamp_ns);
+ // TODO(jbates) figure out why the bots are not passing this check.
+ // EXPECT_NE(last_vsync_count, schedule.vsync_count);
+ // EXPECT_NE(pose.timestamp_ns, last_pose.timestamp_ns);
+ }
+ last_pose = pose;
+ last_vsync_count = schedule.vsync_count;
+ dvrBeginRenderFrame(context);
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+ dvrPresent(context);
+ }
+
+ dvrPoseDestroy(client);
+ dvrGraphicsContextDestroy(context);
+}